diff --git a/Makefile.core.mk b/Makefile.core.mk deleted file mode 100644 index d9c7aaeec..000000000 --- a/Makefile.core.mk +++ /dev/null @@ -1,523 +0,0 @@ -## Copyright Istio Authors -## -## Licensed under the Apache License, Version 2.0 (the "License"); -## you may not use this file except in compliance with the License. -## You may obtain a copy of the License at -## -## http://www.apache.org/licenses/LICENSE-2.0 -## -## Unless required by applicable law or agreed to in writing, software -## distributed under the License is distributed on an "AS IS" BASIS, -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -## See the License for the specific language governing permissions and -## limitations under the License. - -#----------------------------------------------------------------------------- -# Global Variables -#----------------------------------------------------------------------------- -ISTIO_GO := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) -export ISTIO_GO -SHELL := /bin/bash -o pipefail - -export VERSION ?= 1.14-dev - -# Base version of Istio image to use -BASE_VERSION ?= latest - -export GO111MODULE ?= on -export GOPROXY ?= https://proxy.golang.org -export GOSUMDB ?= sum.golang.org - -# If GOPATH is not set by the env, set it to a sane value -GOPATH ?= $(shell cd ${ISTIO_GO}/../../..; pwd) -export GOPATH - -# If GOPATH is made up of several paths, use the first one for our targets in this Makefile -GO_TOP := $(shell echo ${GOPATH} | cut -d ':' -f1) -export GO_TOP - -GO ?= go - -GOARCH_LOCAL := $(TARGET_ARCH) -GOOS_LOCAL := $(TARGET_OS) - -#----------------------------------------------------------------------------- -# Output control -#----------------------------------------------------------------------------- -# Invoke make VERBOSE=1 to enable echoing of the command being executed -export VERBOSE ?= 0 -# Place the variable Q in front of a command to control echoing of the command being executed. -Q = $(if $(filter 1,$VERBOSE),,@) -# Use the variable H to add a header (equivalent to =>) to informational output -H = $(shell printf "\033[34;1m=>\033[0m") - -ifeq ($(origin DEBUG), undefined) - BUILDTYPE_DIR:=release -else ifeq ($(DEBUG),0) - BUILDTYPE_DIR:=release -else - BUILDTYPE_DIR:=debug - export GCFLAGS:=all=-N -l - $(info $(H) Build with debugger information) -endif - -# Optional file including user-specific settings (HUB, TAG, etc) --include .istiorc.mk - -# Environment for tests, the directory containing istio and deps binaries. -# Typically same as GOPATH/bin, so tests work seemlessly with IDEs. - -export ISTIO_BIN=$(GOBIN) -# Using same package structure as pkg/ - -# If we are running in the Linux build container on non Linux hosts, we add the -# linux binaries to the build dependencies, BUILD_DEPS, which can be added to other targets -# that would need the Linux binaries (ex. tests). -BUILD_DEPS:= -ifeq ($(IN_BUILD_CONTAINER),1) - ifneq ($(GOOS_LOCAL),"linux") - BUILD_DEPS += build-linux - endif -endif - -export ARTIFACTS ?= $(TARGET_OUT) -export JUNIT_OUT ?= $(ARTIFACTS)/junit.xml -export REPO_ROOT := $(shell git rev-parse --show-toplevel) - -# Make directories needed by the build system -$(shell mkdir -p $(TARGET_OUT_LINUX)) -$(shell mkdir -p $(TARGET_OUT_LINUX)/logs) -$(shell mkdir -p $(dir $(JUNIT_OUT))) - -# Need seperate target for init: -$(TARGET_OUT): - @mkdir -p $@ - -# scratch dir: this shouldn't be simply 'docker' since that's used for docker.save to store tar.gz files -ISTIO_DOCKER:=${TARGET_OUT_LINUX}/docker_temp - -# scratch dir for building isolated images. Please don't remove it again - using -# ISTIO_DOCKER results in slowdown, all files (including multiple copies of envoy) will be -# copied to the docker temp container - even if you add only a tiny file, >1G of data will -# be copied, for each docker image. -DOCKER_BUILD_TOP:=${TARGET_OUT_LINUX}/docker_build -DOCKERX_BUILD_TOP:=${TARGET_OUT_LINUX}/dockerx_build - -# dir where tar.gz files from docker.save are stored -ISTIO_DOCKER_TAR:=${TARGET_OUT_LINUX}/release/docker - -# Populate the git version for istio/proxy (i.e. Envoy) -#ifeq ($(PROXY_REPO_SHA),) -# export PROXY_REPO_SHA:=$(shell grep PROXY_REPO_SHA istio.deps -A 4 | grep lastStableSHA | cut -f 4 -d '"') -#endif - -# Envoy binary variables Keep the default URLs up-to-date with the latest push from istio/proxy. - -export ISTIO_ENVOY_BASE_URL ?= https://storage.googleapis.com/istio-build/proxy - -# Use envoy as the sidecar by default -export SIDECAR ?= envoy - -# OS-neutral vars. These currently only work for linux. -export ISTIO_ENVOY_VERSION ?= ${PROXY_REPO_SHA} -export ISTIO_ENVOY_DEBUG_URL ?= $(ISTIO_ENVOY_BASE_URL)/envoy-debug-$(ISTIO_ENVOY_VERSION).tar.gz -export ISTIO_ENVOY_CENTOS_DEBUG_URL ?= $(ISTIO_ENVOY_BASE_URL)/envoy-centos-debug-$(ISTIO_ENVOY_VERSION).tar.gz -export ISTIO_ENVOY_RELEASE_URL ?= $(ISTIO_ENVOY_BASE_URL)/envoy-alpha-$(ISTIO_ENVOY_VERSION).tar.gz -export ISTIO_ENVOY_CENTOS_RELEASE_URL ?= $(ISTIO_ENVOY_BASE_URL)/envoy-centos-alpha-$(ISTIO_ENVOY_VERSION).tar.gz - -# Envoy Linux vars. -export ISTIO_ENVOY_LINUX_VERSION ?= ${ISTIO_ENVOY_VERSION} -export ISTIO_ENVOY_LINUX_DEBUG_URL ?= ${ISTIO_ENVOY_DEBUG_URL} -export ISTIO_ENVOY_LINUX_RELEASE_URL ?= ${ISTIO_ENVOY_RELEASE_URL} -# Variables for the extracted debug/release Envoy artifacts. -export ISTIO_ENVOY_LINUX_DEBUG_DIR ?= ${TARGET_OUT_LINUX}/debug -export ISTIO_ENVOY_LINUX_DEBUG_NAME ?= envoy-debug-${ISTIO_ENVOY_LINUX_VERSION} -export ISTIO_ENVOY_LINUX_DEBUG_PATH ?= ${ISTIO_ENVOY_LINUX_DEBUG_DIR}/${ISTIO_ENVOY_LINUX_DEBUG_NAME} -export ISTIO_ENVOY_CENTOS_LINUX_DEBUG_NAME ?= envoy-centos-debug-${ISTIO_ENVOY_LINUX_VERSION} -export ISTIO_ENVOY_CENTOS_LINUX_DEBUG_PATH ?= ${ISTIO_ENVOY_LINUX_DEBUG_DIR}/${ISTIO_ENVOY_CENTOS_LINUX_DEBUG_NAME} - -export ISTIO_ENVOY_LINUX_RELEASE_DIR ?= ${TARGET_OUT_LINUX}/release -export ISTIO_ENVOY_LINUX_RELEASE_NAME ?= ${SIDECAR}-${ISTIO_ENVOY_VERSION} -export ISTIO_ENVOY_LINUX_RELEASE_PATH ?= ${ISTIO_ENVOY_LINUX_RELEASE_DIR}/${ISTIO_ENVOY_LINUX_RELEASE_NAME} -export ISTIO_ENVOY_CENTOS_LINUX_RELEASE_NAME ?= envoy-centos-${ISTIO_ENVOY_LINUX_VERSION} -export ISTIO_ENVOY_CENTOS_LINUX_RELEASE_PATH ?= ${ISTIO_ENVOY_LINUX_RELEASE_DIR}/${ISTIO_ENVOY_CENTOS_LINUX_RELEASE_NAME} - -# Envoy macOS vars. -# TODO Change url when official envoy release for macOS is available -export ISTIO_ENVOY_MACOS_VERSION ?= 1.0.2 -export ISTIO_ENVOY_MACOS_RELEASE_URL ?= https://github.com/istio/proxy/releases/download/${ISTIO_ENVOY_MACOS_VERSION}/istio-proxy-${ISTIO_ENVOY_MACOS_VERSION}-macos.tar.gz -# Variables for the extracted debug/release Envoy artifacts. -export ISTIO_ENVOY_MACOS_RELEASE_DIR ?= ${TARGET_OUT}/release -export ISTIO_ENVOY_MACOS_RELEASE_NAME ?= envoy-${ISTIO_ENVOY_MACOS_VERSION} -export ISTIO_ENVOY_MACOS_RELEASE_PATH ?= ${ISTIO_ENVOY_MACOS_RELEASE_DIR}/${ISTIO_ENVOY_MACOS_RELEASE_NAME} - -# Allow user-override envoy bootstrap config path. -export ISTIO_ENVOY_BOOTSTRAP_CONFIG_PATH ?= ${ISTIO_GO}/tools/packaging/common/envoy_bootstrap.json -export ISTIO_ENVOY_BOOTSTRAP_CONFIG_DIR = $(dir ${ISTIO_ENVOY_BOOTSTRAP_CONFIG_PATH}) - -# If the hub is not explicitly set, use default to istio. -HUB ?=istio -ifeq ($(HUB),) - $(error "HUB cannot be empty") -endif - -# For dockerx builds, allow HUBS which is a space seperated list of hubs. Default to HUB. -HUBS ?= $(HUB) - -# If tag not explicitly set in users' .istiorc.mk or command line, default to the git sha. -TAG ?= $(shell git rev-parse --verify HEAD) -ifeq ($(TAG),) - $(error "TAG cannot be empty") -endif - -VARIANT := -ifeq ($(VARIANT),) - TAG_VARIANT:=${TAG} -else - TAG_VARIANT:=${TAG}-${VARIANT} -endif - -PULL_POLICY ?= IfNotPresent -ifeq ($(TAG),latest) - PULL_POLICY = Always -endif -ifeq ($(PULL_POLICY),) - $(error "PULL_POLICY cannot be empty") -endif - -include tools/proto/proto.mk - -.PHONY: default -default: init build test - -.PHONY: init -# Downloads envoy, based on the SHA defined in the base pilot Dockerfile -#init: $(TARGET_OUT)/istio_is_init -# @mkdir -p ${TARGET_OUT}/logs -# @mkdir -p ${TARGET_OUT}/release - -# I tried to make this dependent on what I thought was the appropriate -# lock file, but it caused the rule for that file to get run (which -# seems to be about obtaining a new version of the 3rd party libraries). -#$(TARGET_OUT)/istio_is_init: bin/init.sh istio.deps | $(TARGET_OUT) -# @# Add a retry, as occasionally we see transient connection failures to GCS -# @# Like `curl: (56) OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 104` -# TARGET_OUT=$(TARGET_OUT) ISTIO_BIN=$(ISTIO_BIN) GOOS_LOCAL=$(GOOS_LOCAL) bin/retry.sh SSL_ERROR_SYSCALL bin/init.sh -# touch $(TARGET_OUT)/istio_is_init - -# init.sh downloads envoy and webassembly plugins -${TARGET_OUT}/${SIDECAR}: init -${ISTIO_ENVOY_LINUX_DEBUG_PATH}: init -${ISTIO_ENVOY_LINUX_RELEASE_PATH}: init -${ISTIO_ENVOY_MACOS_RELEASE_PATH}: init - -# Pull dependencies, based on the checked in Gopkg.lock file. -# Developers must manually run `dep ensure` if adding new deps -depend: init | $(TARGET_OUT) - -DIRS_TO_CLEAN := $(TARGET_OUT) -DIRS_TO_CLEAN += $(TARGET_OUT_LINUX) - -$(OUTPUT_DIRS): - @mkdir -p $@ - -.PHONY: ${GEN_CERT} -GEN_CERT := ${ISTIO_BIN}/generate_cert -${GEN_CERT}: - GOOS=$(GOOS_LOCAL) && GOARCH=$(GOARCH_LOCAL) && common/scripts/gobuild.sh $@ ./security/tools/generate_cert - -#----------------------------------------------------------------------------- -# Target: precommit -#----------------------------------------------------------------------------- -.PHONY: precommit format lint - -# Target run by the pre-commit script, to automate formatting and lint -# If pre-commit script is not used, please run this manually. -precommit: format lint - -format: fmt ## Auto formats all code. This should be run before sending a PR. - -fmt: format-go format-python tidy-go - -ifeq ($(DEBUG),1) -# gobuild script uses custom linker flag to set the variables. -RELEASE_LDFLAGS='' -else -RELEASE_LDFLAGS='-extldflags -static -s -w' -endif - -# List of all binaries to build -# We split the binaries into "agent" binaries and standard ones. This corresponds to build "agent". -# This allows conditional compilation to avoid pulling in costly dependencies to the agent, such as XDS and k8s. -AGENT_BINARIES:=./pilot/cmd/pilot-agent -STANDARD_BINARIES:=./istioctl/cmd/istioctl \ - ./pilot/cmd/pilot-discovery -# ./pkg/test/echo/cmd/client \ -# ./pkg/test/echo/cmd/server \ -# ./samples/extauthz/cmd/extauthz \ -# ./operator/cmd/operator \ -# ./cni/cmd/istio-cni \ -# ./cni/cmd/istio-cni-taint \ -# ./cni/cmd/install-cni \ -# ./tools/istio-iptables \ -# ./tools/bug-report -BINARIES:=$(STANDARD_BINARIES) $(AGENT_BINARIES) - -# List of binaries included in releases -RELEASE_BINARIES:=pilot-discovery pilot-agent istioctl bug-report - -.PHONY: build -build: depend ## Builds all go binaries. - GOPROXY=https://goproxy.cn GOOS=$(GOOS_LOCAL) GOARCH=$(GOARCH_LOCAL) LDFLAGS=$(RELEASE_LDFLAGS) common/scripts/gobuild.sh $(TARGET_OUT)/ $(STANDARD_BINARIES) - GOPROXY=https://goproxy.cn GOOS=$(GOOS_LOCAL) GOARCH=$(GOARCH_LOCAL) LDFLAGS=$(RELEASE_LDFLAGS) common/scripts/gobuild.sh $(TARGET_OUT)/ -tags=agent $(AGENT_BINARIES) - -# The build-linux target is responsible for building binaries used within containers. -# This target should be expanded upon as we add more Linux architectures: i.e. build-arm64. -# Then a new build target can be created such as build-container-bin that builds these -# various platform images. -.PHONY: build-linux -build-linux: depend - GOOS=linux GOARCH=$(GOARCH_LOCAL) LDFLAGS=$(RELEASE_LDFLAGS) common/scripts/gobuild.sh $(TARGET_OUT_LINUX)/ $(STANDARD_BINARIES) - GOOS=linux GOARCH=$(GOARCH_LOCAL) LDFLAGS=$(RELEASE_LDFLAGS) common/scripts/gobuild.sh $(TARGET_OUT_LINUX)/ -tags=agent $(AGENT_BINARIES) - -# Create targets for TARGET_OUT_LINUX/binary -# There are two use cases here: -# * Building all docker images (generally in CI). In this case we want to build everything at once, so they share work -# * Building a single docker image (generally during dev). In this case we just want to build the single binary alone -BUILD_ALL ?= true -define build-linux -.PHONY: $(TARGET_OUT_LINUX)/$(shell basename $(1)) -ifeq ($(BUILD_ALL),true) -$(TARGET_OUT_LINUX)/$(shell basename $(1)): build-linux - @: -else -$(TARGET_OUT_LINUX)/$(shell basename $(1)): $(TARGET_OUT_LINUX) - GOOS=linux GOARCH=$(GOARCH_LOCAL) LDFLAGS=$(RELEASE_LDFLAGS) common/scripts/gobuild.sh $(TARGET_OUT_LINUX)/ -tags=$(2) $(1) -endif -endef - -$(foreach bin,$(STANDARD_BINARIES),$(eval $(call build-linux,$(bin),""))) -$(foreach bin,$(AGENT_BINARIES),$(eval $(call build-linux,$(bin),"agent"))) - -# Create helper targets for each binary, like "pilot-discovery" -# As an optimization, these still build everything -$(foreach bin,$(BINARIES),$(shell basename $(bin))): build -ifneq ($(TARGET_OUT_LINUX),$(LOCAL_OUT)) -# if we are on linux already, then this rule is handled by build-linux above, which handles BUILD_ALL variable -$(foreach bin,$(BINARIES),${LOCAL_OUT}/$(shell basename $(bin))): build -endif - -MARKDOWN_LINT_ALLOWLIST=localhost:8080,storage.googleapis.com/istio-artifacts/pilot/,http://ratings.default.svc.cluster.local:9080/ratings - -lint-helm-global: - find manifests -name 'Chart.yaml' -print0 | ${XARGS} -L 1 dirname | xargs -r helm lint - -lint: lint-python lint-copyright-banner lint-scripts lint-go lint-dockerfiles lint-markdown lint-yaml lint-licenses lint-helm-global ## Runs all linters. - @bin/check_samples.sh - @testlinter - @envvarlinter istioctl pilot security - -go-gen: - @mkdir -p /tmp/bin - @PATH="${PATH}":/tmp/bin go generate ./... - -refresh-goldens: - @REFRESH_GOLDEN=true go test ${GOBUILDFLAGS} ./operator/... - @REFRESH_GOLDEN=true go test ${GOBUILDFLAGS} ./pkg/kube/inject/... - @REFRESH_GOLDEN=true go test ${GOBUILDFLAGS} ./pilot/pkg/security/authz/builder/... - # we needn't cni here - @#REFRESH_GOLDEN=true go test ${GOBUILDFLAGS} ./cni/pkg/plugin/... - -update-golden: refresh-goldens - -# Keep dummy target since some build pipelines depend on this -gen-charts: - @echo "This target is no longer required and will be removed in the future" - -gen: \ - mod-download-go \ - go-gen \ - mirror-licenses \ - format \ - update-crds \ - proto \ - copy-templates \ - gen-kustomize \ - update-golden ## Update all generated code. - -gen-check: gen check-clean-repo - -copy-templates: - rm manifests/charts/istiod-remote/templates/* - rm manifests/charts/gateways/istio-egress/templates/* - - # gateway charts - cp -r manifests/charts/gateways/istio-ingress/templates/* manifests/charts/gateways/istio-egress/templates - find ./manifests/charts/gateways/istio-egress/templates -type f -exec sed -i -e 's/ingress/egress/g' {} \; - find ./manifests/charts/gateways/istio-egress/templates -type f -exec sed -i -e 's/Ingress/Egress/g' {} \; - - # external istiod remote cluster charts - cp manifests/charts/base/templates/services.yaml manifests/charts/istiod-remote/templates - cp manifests/charts/base/templates/endpoints.yaml manifests/charts/istiod-remote/templates - cp manifests/charts/base/templates/reader-serviceaccount.yaml manifests/charts/istiod-remote/templates - cp manifests/charts/istio-control/istio-discovery/templates/mutatingwebhook.yaml manifests/charts/istiod-remote/templates - cp manifests/charts/istio-control/istio-discovery/templates/reader-clusterrole.yaml manifests/charts/istiod-remote/templates - cp manifests/charts/istio-control/istio-discovery/templates/reader-clusterrolebinding.yaml manifests/charts/istiod-remote/templates - - # external istiod config cluster charts - cp manifests/charts/istio-control/istio-discovery/files/injection-template.yaml manifests/charts/istiod-remote/files - cp manifests/charts/istio-control/istio-discovery/files/gateway-injection-template.yaml manifests/charts/istiod-remote/files - cp manifests/charts/istio-control/istio-discovery/templates/istiod-injector-configmap.yaml manifests/charts/istiod-remote/templates - cp manifests/charts/istio-control/istio-discovery/templates/configmap.yaml manifests/charts/istiod-remote/templates - cp manifests/charts/istio-control/istio-discovery/templates/telemetryv2_*.yaml manifests/charts/istiod-remote/templates - sed -e '1 i {{- if .Values.global.configCluster }}' -e '$$ a {{- end }}' manifests/charts/base/crds/crd-all.gen.yaml > manifests/charts/istiod-remote/templates/crd-all.gen.yaml - sed -e '1 i {{- if .Values.global.configCluster }}' -e '$$ a {{- end }}' manifests/charts/base/crds/crd-operator.yaml > manifests/charts/istiod-remote/templates/crd-operator.yaml - sed -e '1 i {{- if .Values.global.configCluster }}' -e '$$ a {{- end }}' manifests/charts/istio-control/istio-discovery/templates/validatingwebhookconfiguration.yaml > manifests/charts/istiod-remote/templates/validatingwebhookconfiguration.yaml - sed -e '1 i {{- if .Values.global.configCluster }}' -e '$$ a {{- end }}' manifests/charts/istio-control/istio-discovery/templates/serviceaccount.yaml > manifests/charts/istiod-remote/templates/serviceaccount.yaml - sed -e '1 i {{- if .Values.global.configCluster }}' -e '$$ a {{- end }}' manifests/charts/istio-control/istio-discovery/templates/role.yaml > manifests/charts/istiod-remote/templates/role.yaml - sed -e '1 i {{- if .Values.global.configCluster }}' -e '$$ a {{- end }}' manifests/charts/istio-control/istio-discovery/templates/rolebinding.yaml > manifests/charts/istiod-remote/templates/rolebinding.yaml - sed -e '1 i {{- if .Values.global.configCluster }}' -e '$$ a {{- end }}' manifests/charts/istio-control/istio-discovery/templates/clusterrole.yaml > manifests/charts/istiod-remote/templates/clusterrole.yaml - sed -e '1 i {{- if .Values.global.configCluster }}' -e '$$ a {{- end }}' manifests/charts/istio-control/istio-discovery/templates/clusterrolebinding.yaml > manifests/charts/istiod-remote/templates/clusterrolebinding.yaml - - # copy istio-discovery values, but apply some local customizations - cp manifests/charts/istio-control/istio-discovery/values.yaml manifests/charts/istiod-remote/ - yq -i '.telemetry.enabled=false | .global.externalIstiod=true | .global.omitSidecarInjectorConfigMap=true | .pilot.configMap=false' manifests/charts/istiod-remote/values.yaml -# Generate kustomize templates. -gen-kustomize: - helm3 template istio --namespace dubbo-system --include-crds manifests/charts/base > manifests/charts/base/files/gen-istio-cluster.yaml - helm3 template istio --namespace dubbo-system manifests/charts/istio-control/istio-discovery \ - > manifests/charts/istio-control/istio-discovery/files/gen-istio.yaml - helm3 template operator --namespace istio-operator manifests/charts/istio-operator \ - --set hub=gcr.io/istio-testing --set tag=${VERSION} > manifests/charts/istio-operator/files/gen-operator.yaml - -#----------------------------------------------------------------------------- -# Target: go build -#----------------------------------------------------------------------------- - -# Non-static istioctl targets. These are typically a build artifact. -${TARGET_OUT}/release/istioctl-linux-amd64: depend - GOOS=linux GOARCH=amd64 LDFLAGS=$(RELEASE_LDFLAGS) common/scripts/gobuild.sh $@ ./istioctl/cmd/istioctl -${TARGET_OUT}/release/istioctl-linux-armv7: depend - GOOS=linux GOARCH=arm GOARM=7 LDFLAGS=$(RELEASE_LDFLAGS) common/scripts/gobuild.sh $@ ./istioctl/cmd/istioctl -${TARGET_OUT}/release/istioctl-linux-arm64: depend - GOOS=linux GOARCH=arm64 LDFLAGS=$(RELEASE_LDFLAGS) common/scripts/gobuild.sh $@ ./istioctl/cmd/istioctl -${TARGET_OUT}/release/istioctl-osx: depend - GOOS=darwin GOARCH=amd64 LDFLAGS=$(RELEASE_LDFLAGS) common/scripts/gobuild.sh $@ ./istioctl/cmd/istioctl -${TARGET_OUT}/release/istioctl-osx-arm64: depend - GOOS=darwin GOARCH=arm64 LDFLAGS=$(RELEASE_LDFLAGS) common/scripts/gobuild.sh $@ ./istioctl/cmd/istioctl -${TARGET_OUT}/release/istioctl-win.exe: depend - GOOS=windows LDFLAGS=$(RELEASE_LDFLAGS) common/scripts/gobuild.sh $@ ./istioctl/cmd/istioctl - -# generate the istioctl completion files -${TARGET_OUT}/release/istioctl.bash: ${LOCAL_OUT}/istioctl - ${LOCAL_OUT}/istioctl completion bash > ${TARGET_OUT}/release/istioctl.bash - -${TARGET_OUT}/release/_istioctl: ${LOCAL_OUT}/istioctl - ${LOCAL_OUT}/istioctl completion zsh > ${TARGET_OUT}/release/_istioctl - -.PHONY: binaries-test -binaries-test: - go test ${GOBUILDFLAGS} ./tests/binary/... -v --base-dir ${TARGET_OUT} --binaries="$(RELEASE_BINARIES)" - -# istioctl-all makes all of the non-static istioctl executables for each supported OS -.PHONY: istioctl-all -istioctl-all: ${TARGET_OUT}/release/istioctl-linux-amd64 ${TARGET_OUT}/release/istioctl-linux-armv7 ${TARGET_OUT}/release/istioctl-linux-arm64 \ - ${TARGET_OUT}/release/istioctl-osx \ - ${TARGET_OUT}/release/istioctl-osx-arm64 \ - ${TARGET_OUT}/release/istioctl-win.exe - -.PHONY: istioctl.completion -istioctl.completion: ${TARGET_OUT}/release/istioctl.bash ${TARGET_OUT}/release/_istioctl - -# istioctl-install builds then installs istioctl into $GOPATH/BIN -# Used for debugging istioctl during dev work -.PHONY: istioctl-install-container -istioctl-install-container: istioctl - -#----------------------------------------------------------------------------- -# Target: test -#----------------------------------------------------------------------------- - -.PHONY: test - -# This target sets JUNIT_REPORT to the location of the go-junit-report binary. -# This binary is provided in the build container. If it is not found, the build -# container is not being used, so ask the user to install go-junit-report. -JUNIT_REPORT := $(shell which go-junit-report 2> /dev/null || echo "${ISTIO_BIN}/go-junit-report") - -${ISTIO_BIN}/go-junit-report: - @echo "go-junit-report was not found in the build environment." - @echo "Please install go-junit-report (ex. go install github.com/jstemmer/go-junit-report@latest)" - @exit 1 - -# This is just an alias for racetest now -test: racetest ## Runs all unit tests - -# For now, keep a minimal subset. This can be expanded in the future. -BENCH_TARGETS ?= ./pilot/... - -.PHONY: racetest -racetest: $(JUNIT_REPORT) - go test ${GOBUILDFLAGS} ${T} -race ./... 2>&1 | tee >($(JUNIT_REPORT) > $(JUNIT_OUT)) - -.PHONY: benchtest -benchtest: $(JUNIT_REPORT) ## Runs all benchmarks - prow/benchtest.sh run $(BENCH_TARGETS) - prow/benchtest.sh compare - -report-benchtest: - prow/benchtest.sh report - -#----------------------------------------------------------------------------- -# Target: clean -#----------------------------------------------------------------------------- -.PHONY: clean - -clean: ## Cleans all the intermediate files and folders previously generated. - rm -rf $(DIRS_TO_CLEAN) - -#----------------------------------------------------------------------------- -# Target: docker -#----------------------------------------------------------------------------- -.PHONY: push - -# for now docker is limited to Linux compiles - why ? -include tools/istio-docker.mk - -push: docker.push ## Build and push docker images to registry defined by $HUB and $TAG - -#----------------------------------------------------------------------------- -# Target: environment and tools -#----------------------------------------------------------------------------- -.PHONY: show.env show.goenv - -show.env: ; $(info $(H) environment variables...) - $(Q) printenv - -show.goenv: ; $(info $(H) go environment...) - $(Q) $(GO) version - $(Q) $(GO) env - -# show makefile variables. Usage: make show. -show.%: ; $(info $* $(H) $($*)) - $(Q) true - -#----------------------------------------------------------------------------- -# Target: custom resource definitions -#----------------------------------------------------------------------------- - -update-crds: - bin/update_crds.sh - -#----------------------------------------------------------------------------- -# Target: artifacts and distribution -#----------------------------------------------------------------------------- -# deb, rpm, etc packages -include tools/packaging/packaging.mk - -#----------------------------------------------------------------------------- -# Target: integration tests -#----------------------------------------------------------------------------- -include tests/integration/tests.mk - -include common/Makefile.common.mk diff --git a/bin/.spelling_failures b/bin/.spelling_failures deleted file mode 100755 index e41a424b4..000000000 --- a/bin/.spelling_failures +++ /dev/null @@ -1,2 +0,0 @@ -OWNERS -vendor/ diff --git a/bin/check_samples.sh b/bin/check_samples.sh deleted file mode 100755 index 6ba1fda47..000000000 --- a/bin/check_samples.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -# Copyright 2019 Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -shopt -s globstar -set -e - -SCRIPTPATH=$( cd "$(dirname "$0")" && pwd -P ) -ROOTDIR=$SCRIPTPATH/.. -cd "$ROOTDIR" || exit - -# rely on go build cache -ISTIOCTL=bin/istioctl -go build -o $ISTIOCTL ./istioctl/cmd/istioctl - -for f in samples/**/*.yaml; do - echo "Validating $f..." - $ISTIOCTL validate -x \ - -f "$f" -done diff --git a/bin/diff_yaml.py b/bin/diff_yaml.py deleted file mode 100755 index 4c5182e5e..000000000 --- a/bin/diff_yaml.py +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2018 Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Compare 2 multi document kubernetes yaml files -# It ensures that order does not matter -# -from __future__ import print_function -import argparse -import datadiff -import sys -import yaml # pyyaml - -# returns fully qualified resource name of the k8s resource - - -def by_resource_name(res): - if res is None: - return "" - - return "{}::{}::{}".format(res['apiVersion'], - res['kind'], - res['metadata']['name']) - - -def keydiff(k0, k1): - k0s = set(k0) - k1s = set(k1) - added = k1s - k0s - removed = k0s - k1s - common = k0s.intersection(k1s) - - return added, removed, common - - -def drop_keys(res, k1, k2): - if k2 in res[k1]: - del res[k1][k2] - - -def normalize_configmap(res): - try: - if res['kind'] != "ConfigMap": - return res - - data = res['data'] - - # some times keys are yamls... - # so parse them - for k in data: - try: - op = yaml.safe_load_all(data[k]) - data[k] = list(op) - except yaml.YAMLError as ex: - print(ex) - - return res - except KeyError as ke: - if 'kind' in str(ke) or 'data' in str(ke): - return res - - raise - - -def normalize_ports(res): - try: - spec = res["spec"] - if spec is None: - return res - ports = sorted(spec['ports'], key=lambda x: x["port"]) - spec['ports'] = ports - - return res - except KeyError as ke: - if 'spec' in str(ke) or 'ports' in str(ke) or 'port' in str(ke): - return res - - raise - - -def normalize_res(res, args): - if not res: - return res - - if args.ignore_labels: - drop_keys(res, "metadata", "labels") - - if args.ignore_namespace: - drop_keys(res, "metadata", "namespace") - - res = normalize_ports(res) - - res = normalize_configmap(res) - - return res - - -def normalize(rl, args): - for i in range(len(rl)): - rl[i] = normalize_res(rl[i], args) - - return rl - - -def compare(args): - j0 = normalize(list(yaml.safe_load_all(open(args.orig))), args) - j1 = normalize(list(yaml.safe_load_all(open(args.new))), args) - - q0 = {by_resource_name(res): res for res in j0 if res is not None} - q1 = {by_resource_name(res): res for res in j1 if res is not None} - - added, removed, common = keydiff(q0.keys(), q1.keys()) - - changed = 0 - for k in sorted(common): - if q0[k] != q1[k]: - changed += 1 - - print("## +++ ", args.new) - print("## --- ", args.orig) - print("## Added:", len(added)) - print("## Removed:", len(removed)) - print("## Updated:", changed) - print("## Unchanged:", len(common) - changed) - - for k in sorted(added): - print("+", k) - - for k in sorted(removed): - print("-", k) - - print("##", "*" * 25) - - for k in sorted(common): - if q0[k] != q1[k]: - print("## ", k) - s0 = yaml.safe_dump(q0[k], default_flow_style=False, indent=2) - s1 = yaml.safe_dump(q1[k], default_flow_style=False, indent=2) - - print(datadiff.diff(s0, s1, fromfile=args.orig, tofile=args.new)) - - return changed + len(added) + len(removed) - - -def main(args): - return compare(args) - - -def get_parser(): - parser = argparse.ArgumentParser( - description="Compare kubernetes yaml files") - - parser.add_argument("orig") - parser.add_argument("new") - parser.add_argument("--ignore-namespace", action="store_true", default=False, - help="Ignore namespace during comparison") - parser.add_argument("--ignore-labels", action="store_true", default=False, - help="Ignore resource labels during comparison") - parser.add_argument("--ignore-annotations", action="store_true", default=False, - help="Ignore annotations during comparison") - - return parser - - -if __name__ == "__main__": - parser = get_parser() - args = parser.parse_args() - sys.exit(main(args)) diff --git a/bin/init.sh b/bin/init.sh deleted file mode 100755 index d03f77d6b..000000000 --- a/bin/init.sh +++ /dev/null @@ -1,159 +0,0 @@ -#!/bin/bash - -# Copyright 2018 Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Init script downloads or updates envoy and the go dependencies. Called from Makefile, which sets -# the needed environment variables. - -set -o errexit -set -o nounset -set -o pipefail - -if [[ "${ISTIO_ENVOY_LINUX_RELEASE_URL:-}" == "" ]]; then - echo "Envoy variables no set. Make sure you run through the makefile (\`make init\`) rather than directly." - exit 1 -fi - -# Download Envoy debug and release binaries for Linux x86_64. They will be included in the -# docker images created by Dockerfile.proxyv2. - -# Gets the download command supported by the system (currently either curl or wget) -DOWNLOAD_COMMAND="" -function set_download_command () { - # Try curl. - if command -v curl > /dev/null; then - if curl --version | grep Protocols | grep https > /dev/null; then - DOWNLOAD_COMMAND="curl -fLSs --retry 5 --retry-delay 1 --retry-connrefused" - return - fi - echo curl does not support https, will try wget for downloading files. - else - echo curl is not installed, will try wget for downloading files. - fi - - # Try wget. - if command -v wget > /dev/null; then - DOWNLOAD_COMMAND="wget -qO -" - return - fi - echo wget is not installed. - - echo Error: curl is not installed or does not support https, wget is not installed. \ - Cannot download envoy. Please install wget or add support of https to curl. - exit 1 -} - -# Downloads and extract an Envoy binary if the artifact doesn't already exist. -# Params: -# $1: The URL of the Envoy tar.gz to be downloaded. -# $2: The full path of the output binary. -# $3: Non-versioned name to use -function download_envoy_if_necessary () { - if [[ ! -f "$2" ]] ; then - # Enter the output directory. - mkdir -p "$(dirname "$2")" - pushd "$(dirname "$2")" - - # Download and extract the binary to the output directory. - echo "Downloading ${SIDECAR}: $1 to $2" - time ${DOWNLOAD_COMMAND} --header "${AUTH_HEADER:-}" "$1" | tar xz - - # Copy the extracted binary to the output location - cp usr/local/bin/"${SIDECAR}" "$2" - - # Remove the extracted binary. - rm -rf usr - - # Make a copy named just "envoy" in the same directory (overwrite if necessary). - echo "Copying $2 to $(dirname "$2")/${3}" - cp -f "$2" "$(dirname "$2")/${3}" - popd - fi -} - -# Downloads WebAssembly based plugin if it doesn't already exist. -# Params: -# $1: The URL of the WebAssembly file to be downloaded. -# $2: The full path of the output file. -function download_wasm_if_necessary () { - download_file_dir="$(dirname "$2")" - download_file_name="$(basename "$1")" - download_file_path="${download_file_dir}/${download_file_name}" - if [[ ! -f "${download_file_path}" ]] ; then - # Enter the output directory. - mkdir -p "${download_file_dir}" - pushd "${download_file_dir}" - - # Download the WebAssembly plugin files to the output directory. - echo "Downloading WebAssembly file: $1 to ${download_file_path}" - if [[ ${DOWNLOAD_COMMAND} == curl* ]]; then - time ${DOWNLOAD_COMMAND} --header "${AUTH_HEADER:-}" "$1" -o "${download_file_name}" - elif [[ ${DOWNLOAD_COMMAND} == wget* ]]; then - time ${DOWNLOAD_COMMAND} --header "${AUTH_HEADER:-}" "$1" -O "${download_file_name}" - fi - - # Copy the webassembly file to the output location - cp "${download_file_path}" "$2" - popd - fi -} - -mkdir -p "${TARGET_OUT}" - -# Set the value of DOWNLOAD_COMMAND (either curl or wget) -set_download_command - -if [[ -n "${DEBUG_IMAGE:-}" ]]; then - # Download and extract the Envoy linux debug binary. - download_envoy_if_necessary "${ISTIO_ENVOY_LINUX_DEBUG_URL}" "$ISTIO_ENVOY_LINUX_DEBUG_PATH" "${SIDECAR}" -else - echo "Skipping envoy debug. Set DEBUG_IMAGE to download." -fi - -# Download and extract the Envoy linux release binary. -download_envoy_if_necessary "${ISTIO_ENVOY_LINUX_RELEASE_URL}" "$ISTIO_ENVOY_LINUX_RELEASE_PATH" "${SIDECAR}" -download_envoy_if_necessary "${ISTIO_ENVOY_CENTOS_RELEASE_URL}" "$ISTIO_ENVOY_CENTOS_LINUX_RELEASE_PATH" "${SIDECAR}-centos" - -if [[ "$GOOS_LOCAL" == "darwin" ]]; then - # Download and extract the Envoy macOS release binary - download_envoy_if_necessary "${ISTIO_ENVOY_MACOS_RELEASE_URL}" "$ISTIO_ENVOY_MACOS_RELEASE_PATH" "${SIDECAR}" - ISTIO_ENVOY_NATIVE_PATH=${ISTIO_ENVOY_MACOS_RELEASE_PATH} -else - ISTIO_ENVOY_NATIVE_PATH=${ISTIO_ENVOY_LINUX_RELEASE_PATH} -fi - -# Download WebAssembly plugin files -WASM_RELEASE_DIR=${ISTIO_ENVOY_LINUX_RELEASE_DIR} -for plugin in stats metadata_exchange -do - FILTER_WASM_URL="${ISTIO_ENVOY_BASE_URL}/${plugin}-${ISTIO_ENVOY_VERSION}.wasm" - download_wasm_if_necessary "${FILTER_WASM_URL}" "${WASM_RELEASE_DIR}"/"${plugin//_/-}"-filter.wasm - FILTER_WASM_URL="${ISTIO_ENVOY_BASE_URL}/${plugin}-${ISTIO_ENVOY_VERSION}.compiled.wasm" - download_wasm_if_necessary "${FILTER_WASM_URL}" "${WASM_RELEASE_DIR}"/"${plugin//_/-}"-filter.compiled.wasm -done - -# Copy native envoy binary to TARGET_OUT -echo "Copying ${ISTIO_ENVOY_NATIVE_PATH} to ${TARGET_OUT}/${SIDECAR}" -cp -f "${ISTIO_ENVOY_NATIVE_PATH}" "${TARGET_OUT}/${SIDECAR}" - -# Copy CentOS binary -echo "Copying ${ISTIO_ENVOY_CENTOS_LINUX_RELEASE_PATH} to ${TARGET_OUT_LINUX}/${SIDECAR}-centos" -cp -f "${ISTIO_ENVOY_CENTOS_LINUX_RELEASE_PATH}" "${TARGET_OUT_LINUX}/${SIDECAR}-centos" - -# Copy the envoy binary to TARGET_OUT_LINUX if the local OS is not Linux -if [[ "$GOOS_LOCAL" != "linux" ]]; then - echo "Copying ${ISTIO_ENVOY_LINUX_RELEASE_PATH} to ${TARGET_OUT_LINUX}/${SIDECAR}" - cp -f "${ISTIO_ENVOY_LINUX_RELEASE_PATH}" "${TARGET_OUT_LINUX}/${SIDECAR}" -fi diff --git a/bin/retry.sh b/bin/retry.sh deleted file mode 100755 index 98b26a91c..000000000 --- a/bin/retry.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash - -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# retry.sh retries a command until it succeeds. It accepts a regex pattern to match failures on to -# determine if a retry should be attempted. -# Example: retry.sh "connection timed out" ./my-flaky-script.sh some args -# This will run "my-flaky-script.sh", retrying any failed runs that output "connection timed out" up -# to 5 times. - -function fail { - echo "${1}" >&2 - exit 1 -} - -function isatty() { - if [ -t 1 ] ; then - return 0 - else - return 1 - fi -} - -function retry { - local tmpFile - tmpFile=$(mktemp) - trap 'rm -f "${tmpFile}"' EXIT - - local failureRegex="$1" - shift - local n=1 - local max=5 - while true; do - unset SHELL # Don't let environment control which shell to use - if isatty; then - if [ "$(uname)" == "Darwin" ]; then - script -q -r "${tmpFile}" "${*}" - else - script --flush --quiet --return "${tmpFile}" --command "${*}" - fi - else - # if we aren't a TTY, run directly as script will always run with a tty, which may output content that - # we cannot display - set -o pipefail; "$@" 2>&1 | tee "${tmpFile}" - fi - # shellcheck disable=SC2181 - if [[ $? == 0 ]]; then - break - fi - if ! grep -Eq "${failureRegex}" "${tmpFile}"; then - fail "Unexpected failure" - fi - if [[ $n -lt $max ]]; then - ((n++)) - echo "Command failed. Attempt $n/$max:" - else - fail "The command has failed after $n attempts." - fi - done -} - -retry "$@" diff --git a/bin/update_crds.sh b/bin/update_crds.sh deleted file mode 100755 index 10d98d84d..000000000 --- a/bin/update_crds.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash - -# Copyright 2019 Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -e - -fail() { - echo "$@" 1>&2 - exit 1 -} - -API_TMP="$(mktemp -d -u)" - -trap 'rm -rf "${API_TMP}"' EXIT - -SCRIPTPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -ROOTDIR=$(dirname "${SCRIPTPATH}") -cd "${ROOTDIR}" - -REPO="github.com/istio/api" -# using the pseudo version we have in go.mod file. e.g. v.0.0.0-- -# first check if there's a replace: e.g. replace istio.io/api => github.com/USER/istioapi v0.0.0-- -SHA=$(grep "istio.io/api" go.mod | grep "^replace" | awk -F "-" '{print $NF}') -if [ -n "${SHA}" ]; then - REPO=$(grep "istio.io/api" go.mod | grep "^replace" | awk '{print $4}') -else - SHA=$(grep "istio.io/api" go.mod | head -n1 | awk -F "-" '{print $NF}') -fi - -if [ -z "${SHA}" ]; then - fail "Unable to retrieve the commit SHA of istio/api from go.mod file. Not updating the CRD file. Please make sure istio/api exists in the Go module."; -fi - -git clone "https://${REPO}" "${API_TMP}" && cd "${API_TMP}" -git checkout "${SHA}" -if [ ! -f "${API_TMP}/kubernetes/customresourcedefinitions.gen.yaml" ]; then - echo "Generated Custom Resource Definitions file does not exist in the commit SHA ${SHA}. Not updating the CRD file." - exit -fi -rm -f "${ROOTDIR}/manifests/charts/base/crds/crd-all.gen.yaml" -cp "${API_TMP}/kubernetes/customresourcedefinitions.gen.yaml" "${ROOTDIR}/manifests/charts/base/crds/crd-all.gen.yaml" diff --git a/bin/update_deps.sh b/bin/update_deps.sh deleted file mode 100755 index 4fc14da76..000000000 --- a/bin/update_deps.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash - -# Copyright 2019 Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -e - -UPDATE_BRANCH=${UPDATE_BRANCH:-"release-1.14"} - -SCRIPTPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -ROOTDIR=$(dirname "${SCRIPTPATH}") -cd "${ROOTDIR}" - -# Get the sha of top commit -# $1 = repo -function getSha() { - local dir result - dir=$(mktemp -d) - git clone --depth=1 "https://github.com/istio/${1}.git" -b "${UPDATE_BRANCH}" "${dir}" - - result="$(cd "${dir}" && git rev-parse HEAD)" - rm -rf "${dir}" - - echo "${result}" -} - -make update-common - -export GO111MODULE=on -go get -u "istio.io/api@${UPDATE_BRANCH}" -go get -u "istio.io/client-go@${UPDATE_BRANCH}" -go get -u "istio.io/pkg@${UPDATE_BRANCH}" -go mod tidy - -sed -i "s/^BUILDER_SHA=.*\$/BUILDER_SHA=$(getSha release-builder)/" prow/release-commit.sh -sed -i '/PROXY_REPO_SHA/,/lastStableSHA/ { s/"lastStableSHA":.*/"lastStableSHA": "'"$(getSha proxy)"'"/ }' istio.deps - -# shellcheck disable=SC1001 -LATEST_DEB11_DISTROLESS_SHA256=$(crane digest gcr.io/distroless/static-debian11 | awk -F\: '{print $2}') -sed -i -E "s/sha256:[a-z0-9]+/sha256:${LATEST_DEB11_DISTROLESS_SHA256}/g" docker/Dockerfile.distroless - -# shellcheck disable=SC1001 -LATEST_IPTABLES_DISTROLESS_SHA256=$(crane digest gcr.io/istio-release/iptables | awk -F\: '{print $2}') -sed -i -E "s/sha256:[a-z0-9]+/sha256:${LATEST_IPTABLES_DISTROLESS_SHA256}/g" pilot/docker/Dockerfile.proxyv2 diff --git a/bin/update_proxy.sh b/bin/update_proxy.sh deleted file mode 100755 index 7aac5fc0b..000000000 --- a/bin/update_proxy.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash - -# Copyright 2020 Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Update the Proxy SHA in istio.deps with the first argument -# Exit immediately for non zero status -set -e -# Check unset variables -set -u -# Print commands -set -x - -SCRIPTPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -ROOTDIR=$(dirname "${SCRIPTPATH}") -cd "${ROOTDIR}" - -# Wait for the proxy to become available -ISTIO_ENVOY_VERSION=${ISTIO_ENVOY_VERSION:-$1} -ISTIO_ENVOY_LINUX_VERSION=${ISTIO_ENVOY_LINUX_VERSION:-${ISTIO_ENVOY_VERSION}} -ISTIO_ENVOY_BASE_URL=${ISTIO_ENVOY_BASE_URL:-https://storage.googleapis.com/istio-build/proxy} -ISTIO_ENVOY_RELEASE_URL=${ISTIO_ENVOY_RELEASE_URL:-${ISTIO_ENVOY_BASE_URL}/envoy-alpha-${ISTIO_ENVOY_LINUX_VERSION}.tar.gz} -SLEEP_TIME=60 - -printf "Verifying %s is available\n" "$ISTIO_ENVOY_RELEASE_URL" -until curl --output /dev/null --silent --head --fail "$ISTIO_ENVOY_RELEASE_URL"; do - printf '.' - sleep $SLEEP_TIME -done -printf '\n' - -plugin=metadata_exchange -WASM_URL=${ISTIO_ENVOY_BASE_URL}/${plugin}-${ISTIO_ENVOY_VERSION}.wasm -printf "Verifying %s is available\n" "$WASM_URL" -until curl --output /dev/null --silent --head --fail "$WASM_URL"; do - printf '.' - sleep $SLEEP_TIME -done -printf '\n' - -# Update the dependency in istio.deps -sed -i '/PROXY_REPO_SHA/,/lastStableSHA/ { s/"lastStableSHA":.*/"lastStableSHA": "'"$1"'"/ }' istio.deps diff --git a/cmd/pixiu/pixiu.go b/cmd/pixiu/pixiu.go index 0bb58aede..d07c7a025 100644 --- a/cmd/pixiu/pixiu.go +++ b/cmd/pixiu/pixiu.go @@ -28,8 +28,8 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/cmd" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/pluginregistry" + "github.com/apache/dubbo-go-pixiu/pkg/cmd" + _ "github.com/apache/dubbo-go-pixiu/pkg/pluginregistry" ) const ( diff --git a/common/.commonfiles.sha b/common/.commonfiles.sha deleted file mode 100755 index bd9a2ee9c..000000000 --- a/common/.commonfiles.sha +++ /dev/null @@ -1 +0,0 @@ -cf9705b2ac1420c68d0747d0d4bbcc7f99933fb4 diff --git a/common/Makefile.common.mk b/common/Makefile.common.mk deleted file mode 100755 index b3a7528e3..000000000 --- a/common/Makefile.common.mk +++ /dev/null @@ -1,128 +0,0 @@ -# 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". - -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -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: - @${FINDFILES} -name 'Dockerfile*' -print0 | ${XARGS} hadolint -c ./common/config/.hadolint.yml - -lint-scripts: - @${FINDFILES} -name '*.sh' -print0 | ${XARGS} shellcheck - -lint-yaml: - @${FINDFILES} \( -name '*.yml' -o -name '*.yaml' \) -not -exec grep -q -e "{{" {} \; -print0 | ${XARGS} yamllint -c ./common/config/.yamllint.yml - -lint-helm: - @${FINDFILES} -name 'Chart.yaml' -print0 | ${XARGS} -L 1 dirname | xargs -r helm lint --strict - -lint-copyright-banner: - @${FINDFILES} \( -name '*.go' -o -name '*.cc' -o -name '*.h' -o -name '*.proto' -o -name '*.py' -o -name '*.sh' \) \( ! \( -name '*.gen.go' -o -name '*.pb.go' -o -name '*_pb2.py' \) \) -print0 |\ - ${XARGS} common/scripts/lint_copyright_banner.sh - -fix-copyright-banner: - @${FINDFILES} \( -name '*.go' -o -name '*.cc' -o -name '*.h' -o -name '*.proto' -o -name '*.py' -o -name '*.sh' \) \( ! \( -name '*.gen.go' -o -name '*.pb.go' -o -name '*_pb2.py' \) \) -print0 |\ - ${XARGS} common/scripts/fix_copyright_banner.sh - -lint-go: - @${FINDFILES} -name '*.go' \( ! \( -name '*.gen.go' -o -name '*.pb.go' \) \) -print0 | ${XARGS} common/scripts/lint_go.sh - -lint-python: - @${FINDFILES} -name '*.py' \( ! \( -name '*_pb2.py' \) \) -print0 | ${XARGS} autopep8 --max-line-length 160 --exit-code -d - -lint-markdown: - @${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} - -lint-sass: - @${FINDFILES} -name '*.scss' -print0 | ${XARGS} sass-lint -c common/config/sass-lint.yml --verbose - -lint-typescript: - @${FINDFILES} -name '*.ts' -print0 | ${XARGS} tslint -c common/config/tslint.json - -lint-protos: - @if test -d common-protos; then $(FINDFILES) -name '*.proto' -print0 | $(XARGS) -L 1 prototool lint --protoc-bin-path=/usr/bin/protoc --protoc-wkt-path=common-protos; fi - -lint-licenses: - @if test -d licenses; then license-lint --config common/config/license-lint.yml; fi - -lint-all: lint-dockerfiles lint-scripts lint-yaml lint-helm lint-copyright-banner lint-go lint-python lint-markdown lint-sass lint-typescript lint-protos lint-licenses - -tidy-go: - @find -name go.mod -execdir go mod tidy \; - -mod-download-go: - @-GOFLAGS="-mod=readonly" find -name go.mod -execdir go mod download \; -# go mod tidy is needed with Golang 1.16+ as go mod download affects go.sum -# https://github.com/golang/go/issues/43994 - @find -name go.mod -execdir go mod tidy \; - -format-go: tidy-go - @${FINDFILES} -name '*.go' \( ! \( -name '*.gen.go' -o -name '*.pb.go' \) \) -print0 | ${XARGS} common/scripts/format_go.sh - -format-python: - @${FINDFILES} -name '*.py' -print0 | ${XARGS} autopep8 --max-line-length 160 --aggressive --aggressive -i - -format-protos: - @$(FINDFILES) -name '*.proto' -print0 | $(XARGS) -L 1 prototool format -w - -dump-licenses: mod-download-go - @license-lint --config common/config/license-lint.yml --report - -dump-licenses-csv: mod-download-go - @license-lint --config common/config/license-lint.yml --csv - -mirror-licenses: mod-download-go - @rm -fr licenses - @license-lint --mirror - -TMP := $(shell mktemp -d -u) -UPDATE_BRANCH ?= "release-1.14" - -update-common: - @mkdir -p $(TMP) - @git clone -q --depth 1 --single-branch --branch $(UPDATE_BRANCH) https://github.com/istio/common-files $(TMP)/common-files - @cd $(TMP)/common-files ; git rev-parse HEAD >files/common/.commonfiles.sha - @rm -fr common - @cp -a $(TMP)/common-files/files/* $(shell pwd) - @rm -fr $(TMP)/common-files - -update-common-protos: - @mkdir -p $(TMP) - @git clone -q --depth 1 --single-branch --branch $(UPDATE_BRANCH) https://github.com/istio/common-files $(TMP)/common-files - @cd $(TMP)/common-files ; git rev-parse HEAD > common-protos/.commonfiles.sha - @rm -fr common-protos - @cp -a $(TMP)/common-files/common-protos $(shell pwd) - @rm -fr $(TMP)/common-files - -check-clean-repo: - @common/scripts/check_clean_repo.sh - -tidy-docker: - @docker image prune --all --force --filter="label=io.istio.repo=https://github.com/istio/tools" --filter="label!=io.istio.version=$(IMAGE_VERSION)" - -# help works by looking over all Makefile includes matching `target: ## comment` regex and outputting them -help: ## Show this help - @egrep -h '^[a-zA-Z_\.-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' - -.PHONY: lint-dockerfiles lint-scripts lint-yaml lint-copyright-banner lint-go lint-python lint-helm lint-markdown lint-sass lint-typescript lint-protos lint-all format-go format-python format-protos update-common update-common-protos lint-licenses dump-licenses dump-licenses-csv check-clean-repo tidy-docker help tidy-go mod-download-go diff --git a/common/config/.golangci-format.yml b/common/config/.golangci-format.yml deleted file mode 100755 index 15da25a30..000000000 --- a/common/config/.golangci-format.yml +++ /dev/null @@ -1,54 +0,0 @@ -# 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.38.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 - - gci - - gofumpt - fast: false - -linters-settings: - 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 diff --git a/common/config/.golangci.yml b/common/config/.golangci.yml deleted file mode 100755 index 6867b5ce5..000000000 --- a/common/config/.golangci.yml +++ /dev/null @@ -1,263 +0,0 @@ -# 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.38.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: - - deadcode - - errcheck - - exportloopref - - gocritic - - gofumpt - - goimports - - revive - - gosimple - - govet - - ineffassign - - lll - - misspell - - staticcheck - - structcheck - - stylecheck - - typecheck - - unconvert - - unparam - - varcheck - fast: false - -linters-settings: - errcheck: - # report about not checking of errors in type assetions: `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 - 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 - -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 - - # TODO(https://github.com/dominikh/go-tools/issues/732) remove this once we update - - linters: - - staticcheck - text: "SA1019: package github.com/golang/protobuf" - - # 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 diff --git a/common/config/.hadolint.yml b/common/config/.hadolint.yml deleted file mode 100755 index 3e4e1cbab..000000000 --- a/common/config/.hadolint.yml +++ /dev/null @@ -1,15 +0,0 @@ -# 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". - -ignored: - - DL3008 - - DL3059 - -trustedRegistries: - - gcr.io - - docker.io - - quay.io diff --git a/common/config/.yamllint.yml b/common/config/.yamllint.yml deleted file mode 100755 index c2f21b581..000000000 --- a/common/config/.yamllint.yml +++ /dev/null @@ -1,29 +0,0 @@ -# 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". - -rules: - braces: disable - brackets: disable - colons: enable - commas: disable - comments: disable - comments-indentation: disable - document-end: disable - document-start: disable - empty-lines: disable - empty-values: disable - hyphens: enable - indentation: disable - key-duplicates: enable - key-ordering: disable - line-length: disable - new-line-at-end-of-file: disable - new-lines: enable - octal-values: disable - quoted-strings: disable - trailing-spaces: disable - truthy: disable diff --git a/common/config/license-lint.yml b/common/config/license-lint.yml deleted file mode 100755 index e50811a2c..000000000 --- a/common/config/license-lint.yml +++ /dev/null @@ -1,121 +0,0 @@ -unrestricted_licenses: - - Apache-2.0 - - CC-BY-3.0 - - ISC - - AFL-2.1 - - AFL-3.0 - - Artistic-1.0 - - Artistic-2.0 - - Apache-1.1 - - BSD-1-Clause - - BSD-2-Clause - - BSD-3-Clause - - 0BSD - - FTL - - LPL-1.02 - - MS-PL - - MIT - - NCSA - - OpenSSL - - PHP-3.0 - - TCP-wrappers - - W3C - - Xnet - - Zlib - -reciprocal_licenses: - - CC0-1.0 - - APSL-2.0 - - CDDL-1.0 - - CDDL-1.1 - - CPL-1.0 - - EPL-1.0 - - IPL-1.0 - - MPL-1.0 - - MPL-1.1 - - MPL-2.0 - - MPL-2.0-no-copyleft-exception - - Ruby - -restricted_licenses: - - GPL-1.0-only - - GPL-1.0-or-later - - GPL-2.0-only - - GPL-2.0-or-later - - GPL-3.0-only - - GPL-3.0-or-later - - LGPL-2.0-only - - LGPL-2.0-or-later - - LGPL-2.1-only - - LGPL-2.1-or-later - - LGPL-3.0-only - - LGPL-3.0-or-later - - NPL-1.0 - - NPL-1.1 - - OSL-1.0 - - OSL-1.1 - - OSL-2.0 - - OSL-2.1 - - OSL-3.0 - - QPL-1.0 - - Sleepycat - -allowlisted_modules: -# MIT: https://github.com/ghodss/yaml/blob/master/LICENSE -- github.com/ghodss/yaml - -# BSD: https://github.com/gogo/protobuf/blob/master/LICENSE -- github.com/gogo/protobuf - -# BSD: https://github.com/magiconair/properties/blob/master/LICENSE.md -- github.com/magiconair/properties - -# Apache 2.0 -- github.com/spf13/cobra -- github.com/spf13/afero - -# Public domain: https://github.com/xi2/xz/blob/master/LICENSE -- github.com/xi2/xz - -# Helm is Apache 2.0: https://github.com/helm/helm/blob/master/LICENSE -# However, it has a bunch of LICENSE test files that our linter fails to understand -- helm.sh/helm/v3 - -# https://github.com/pelletier/go-toml/blob/master/LICENSE -# Uses MIT for everything, except a few files copied from -# google's civil library that uses Apache 2 -- github.com/pelletier/go-toml - -# https://github.com/xeipuuv/gojsonpointer/blob/master/LICENSE-APACHE-2.0.txt -- github.com/xeipuuv/gojsonpointer -# https://github.com/xeipuuv/gojsonreference/blob/master/LICENSE-APACHE-2.0.txt -- github.com/xeipuuv/gojsonreference -# Apache 2.0: https://github.com/xeipuuv/gojsonschema/blob/master/LICENSE-APACHE-2.0.txt -- github.com/xeipuuv/gojsonschema - -# Apache 2.0 (but missing appendix): https://github.com/garyburd/redigo/blob/master/LICENSE -- github.com/garyburd/redigo -- github.com/gomodule/redigo - -# Apache 2.0 -# github.com/ghodss/yaml: MIT / BSD-3 -# github.com/gogo/protobuf: BSD-3 -# github.com/jmespath/go-jmespath: Apache 2.0 -# sigs.k8s.io/yaml: MIT / BSD-3 -- github.com/tektoncd/pipeline - -# MIT: https://github.com/kubernetes-sigs/yaml/blob/master/LICENSE -- sigs.k8s.io/yaml - -# https://github.com/go-errors/errors/blob/master/LICENSE.MIT -- github.com/go-errors/errors - -# runc is Apache 2.0: https://github.com/opencontainers/runc/blob/master/LICENSE -# but it contains BSD dep which our linter fails to understand: https://github.com/opencontainers/runc/blob/v0.1.1/Godeps/_workspace/src/github.com/golang/protobuf/LICENSE -- github.com/opencontainers/runc - -# MIT: https://github.com/felixge/fgprof/blob/master/LICENSE.txt -- github.com/felixge/fgprof - -# Apache 2.0 -- github.com/google/pprof diff --git a/common/config/mdl.rb b/common/config/mdl.rb deleted file mode 100755 index 8764f94d7..000000000 --- a/common/config/mdl.rb +++ /dev/null @@ -1,12 +0,0 @@ -all -rule 'MD002', :level => 1 -rule 'MD007', :indent => 4 -rule 'MD013', :line_length => 160, :code_blocks => false, :tables => false -rule 'MD026', :punctuation => ".,;:!" -exclude_rule 'MD013' -exclude_rule 'MD014' -exclude_rule 'MD030' -exclude_rule 'MD032' -exclude_rule 'MD033' -exclude_rule 'MD041' -exclude_rule 'MD046' diff --git a/common/config/sass-lint.yml b/common/config/sass-lint.yml deleted file mode 100755 index da43ee79c..000000000 --- a/common/config/sass-lint.yml +++ /dev/null @@ -1,98 +0,0 @@ -######################### -## Config for sass-lint -######################### -# Linter Options -options: - # Don't merge default rules - merge-default-rules: false - # Raise an error if more than 50 warnings are generated - max-warnings: 500 -# Rule Configuration -rules: - attribute-quotes: - - 2 - - - include: false - bem-depth: 2 - border-zero: 2 - brace-style: 2 - class-name-format: 2 - clean-import-paths: 2 - declarations-before-nesting: 2 - empty-args: 2 - empty-line-between-blocks: 2 - extends-before-declarations: 2 - extends-before-mixins: 2 - final-newline: 2 - force-attribute-nesting: 0 - force-element-nesting: 0 - force-pseudo-nesting: 0 - function-name-format: 2 - hex-length: 0 - hex-notation: 2 - id-name-format: 2 - indentation: - - 2 - - - size: 4 - leading-zero: - - 2 - - - include: false - max-file-line-count: 0 - max-file-length: 0 - mixins-before-declarations: 2 - no-attribute-selectors: 0 - no-color-hex: 0 - no-color-keywords: 0 - no-color-literals: 0 - no-combinators: 0 - no-css-comments: 2 - no-debug: 2 - no-disallowed-properties: 2 - no-duplicate-properties: 2 - no-empty-rulesets: 2 - no-extends: 2 - no-ids: 0 - no-invalid-hex: 2 - no-important: 0 - no-mergeable-selectors: 2 - no-misspelled-properties: 2 - no-qualifying-elements: 0 - no-trailing-whitespace: 2 - no-trailing-zero: 2 - no-transition-all: 0 - no-url-domains: 2 - no-url-protocols: 2 - no-warn: 2 - one-declaration-per-line: 2 - placeholder-in-extend: 2 - placeholder-name-format: 2 - property-sort-order: 0 - property-units: 2 - pseudo-element: 2 - quotes: - - 2 - - - style: double - shorthand-values: 2 - single-line-per-selector: 0 - space-after-bang: 2 - space-after-colon: 2 - space-after-comma: 2 - space-around-operator: 2 - space-before-bang: 2 - space-before-brace: 2 - space-before-colon: 2 - space-between-parens: 2 - trailing-semicolon: 2 - url-quotes: 2 - variable-for-property: - - 0 - - - properties: - - color - - background-color - - fill - variable-name-format: 0 - zero-unit: 2 diff --git a/common/config/tslint.json b/common/config/tslint.json deleted file mode 100755 index 6db4bb19d..000000000 --- a/common/config/tslint.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "defaultSeverity": "error", - "extends": [ - "tslint:recommended" - ], - "rules": { - "max-line-length": { - "options": [160] - }, - "arrow-parens": false, - "new-parens": true, - "no-arg": true, - "no-bitwise": true, - "no-conditional-assignment": true, - "no-consecutive-blank-lines": true, - "no-console": { - "severity": "warning", - "options": ["debug", "info", "log", "time", "timeEnd", "trace"] - }, - "no-shadowed-variable": false, - "eofline": false - }, - "jsRules": {}, - "rulesDirectory": [] -} \ No newline at end of file diff --git a/common/scripts/check_clean_repo.sh b/common/scripts/check_clean_repo.sh deleted file mode 100755 index 075c9fa67..000000000 --- a/common/scripts/check_clean_repo.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -# Copyright Istio Authors - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -function write_patch_file() { - if [ -z "${ARTIFACTS}" ]; then - return 0 - fi - - PATCH_NAME="check-clean-repo-diff.patch" - PATCH_OUT="${ARTIFACTS}/${PATCH_NAME}" - git diff > "${PATCH_OUT}" - - [ -n "${JOB_NAME}" ] && [ -n "${BUILD_ID}" ] - IN_PROW="$?" - - # Don't persist large diffs (30M+) on CI - LARGE_FILE="$(find "${ARTIFACTS}" -name "${PATCH_NAME}" -type 'f' -size +30M)" - if [ "${IN_PROW}" -eq 0 ] && [ -n "${LARGE_FILE}" ]; then - rm "${PATCH_OUT}" - echo "WARNING: patch file was too large to persist ($(du -h "${PATCH_OUT}"))" - return 0 - fi - echo "You can also try applying the patch file from the build artifacts." -} - -if [[ -n $(git status --porcelain) ]]; then - git status - git diff - echo "ERROR: Some files need to be updated, please run 'make gen' and include any changed files in your PR" - write_patch_file - exit 1 -fi diff --git a/common/scripts/copyright-banner-go.txt b/common/scripts/copyright-banner-go.txt deleted file mode 100755 index ffac692d1..000000000 --- a/common/scripts/copyright-banner-go.txt +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - diff --git a/common/scripts/fix_copyright_banner.sh b/common/scripts/fix_copyright_banner.sh deleted file mode 100755 index e4945be1e..000000000 --- a/common/scripts/fix_copyright_banner.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -# 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". - -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -e - -WD=$(dirname "$0") -WD=$(cd "$WD"; pwd) - -for fn in "$@"; do - if ! grep -L -q -e "Apache License, Version 2" -e "Copyright" "${fn}"; then - if [[ "${fn}" == *.go ]]; then - newfile=$(cat "${WD}/copyright-banner-go.txt" "${fn}") - echo "${newfile}" > "${fn}" - echo "Fixing license: ${fn}" - else - echo "Cannot fix license: ${fn}. Unknown file type" - fi - fi -done diff --git a/common/scripts/format_go.sh b/common/scripts/format_go.sh deleted file mode 100755 index 8730e2a0f..000000000 --- a/common/scripts/format_go.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -# 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". - -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -#golangci-lint run --fix -c ./common/config/.golangci-format.yml diff --git a/common/scripts/gobuild.sh b/common/scripts/gobuild.sh deleted file mode 100755 index a89cb8f81..000000000 --- a/common/scripts/gobuild.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/bin/bash - -# 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". - -# Copyright Istio Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This script builds and version stamps the output - -VERBOSE=${VERBOSE:-"0"} -V="" -if [[ "${VERBOSE}" == "1" ]];then - V="-x" - set -x -fi - -SCRIPTPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -OUT=${1:?"output path"} -shift - -set -e - -BUILD_GOOS=${GOOS:-linux} -BUILD_GOARCH=${GOARCH:-amd64} -GOBINARY=${GOBINARY:-go} -GOPKG="$GOPATH/pkg" -BUILDINFO=${BUILDINFO:-""} -STATIC=${STATIC:-1} -LDFLAGS=${LDFLAGS:--extldflags -static} -GOBUILDFLAGS=${GOBUILDFLAGS:-""} -# Split GOBUILDFLAGS by spaces into an array called GOBUILDFLAGS_ARRAY. -IFS=' ' read -r -a GOBUILDFLAGS_ARRAY <<< "$GOBUILDFLAGS" - -GCFLAGS=${GCFLAGS:-} -export CGO_ENABLED=${CGO_ENABLED:-0} - -if [[ "${STATIC}" != "1" ]];then - LDFLAGS="" -fi - -# gather buildinfo if not already provided -# For a release build BUILDINFO should be produced -# at the beginning of the build and used throughout -if [[ -z ${BUILDINFO} ]];then - BUILDINFO=$(mktemp) - "${SCRIPTPATH}/report_build_info.sh" > "${BUILDINFO}" -fi - -# BUILD LD_EXTRAFLAGS -LD_EXTRAFLAGS="" - -while read -r line; do - LD_EXTRAFLAGS="${LD_EXTRAFLAGS} -X ${line}" -done < "${BUILDINFO}" - -OPTIMIZATION_FLAGS=(-trimpath) -if [ "${DEBUG}" == "1" ]; then - OPTIMIZATION_FLAGS=() -fi - -time GOOS=${BUILD_GOOS} GOARCH=${BUILD_GOARCH} GOCACHE=/tmp ${GOBINARY} build \ - ${V} "${GOBUILDFLAGS_ARRAY[@]}" ${GCFLAGS:+-gcflags "${GCFLAGS}"} \ - -o "${OUT}" \ - "${OPTIMIZATION_FLAGS[@]}" \ - -pkgdir="${GOPKG}/${BUILD_GOOS}_${BUILD_GOARCH}" \ - -ldflags "${LDFLAGS} ${LD_EXTRAFLAGS}" "${@}" \ No newline at end of file diff --git a/common/scripts/kind_provisioner.sh b/common/scripts/kind_provisioner.sh deleted file mode 100755 index 0839df8d4..000000000 --- a/common/scripts/kind_provisioner.sh +++ /dev/null @@ -1,431 +0,0 @@ -#!/bin/bash - -# 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". - -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -e -set -x - -# The purpose of this file is to unify prow/lib.sh in both istio and istio.io -# repos to avoid code duplication. - -#################################################################### -################# COMMON SECTION ############################### -#################################################################### - -# 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.21.1" - -# COMMON_SCRIPTS contains the directory this file is in. -COMMON_SCRIPTS=$(dirname "${BASH_SOURCE:-$0}") - -function log() { - echo -e "$(date -u '+%Y-%m-%dT%H:%M:%S.%NZ')\t$*" -} - -function retry() { - local n=1 - local max=5 - local delay=5 - while true; do - "$@" && break - if [[ $n -lt $max ]]; then - ((n++)) - log "Command failed. Attempt $n/$max:" - sleep $delay; - else - log "The command has failed after $n attempts." >&2 - return 2 - fi - done -} - -# load_cluster_topology function reads cluster configuration topology file and -# sets up environment variables used by other functions. So this should be called -# before anything else. -# -# Note: Cluster configuration topology file specifies basic configuration of each -# KinD cluster like its name, pod and service subnets and network_id. If two cluster -# have the same network_id then they belong to the same network and their pods can -# talk to each other directly. -# -# [{ "cluster_name": "cluster1","pod_subnet": "10.10.0.0/16","svc_subnet": "10.255.10.0/24","network_id": "0" }, -# { "cluster_name": "cluster2","pod_subnet": "10.20.0.0/16","svc_subnet": "10.255.20.0/24","network_id": "0" }, -# { "cluster_name": "cluster3","pod_subnet": "10.30.0.0/16","svc_subnet": "10.255.30.0/24","network_id": "1" }] -function load_cluster_topology() { - CLUSTER_TOPOLOGY_CONFIG_FILE="${1}" - - if [[ ! -f "${CLUSTER_TOPOLOGY_CONFIG_FILE}" ]]; then - log 'cluster topology configuration file is not specified' - exit 1 - fi - - export CLUSTER_NAMES - export CLUSTER_POD_SUBNETS - export CLUSTER_SVC_SUBNETS - export CLUSTER_NETWORK_ID - - KUBE_CLUSTERS=$(jq '.[] | select(.kind == "Kubernetes" or .kind == null)' "${CLUSTER_TOPOLOGY_CONFIG_FILE}") - - while read -r value; do - CLUSTER_NAMES+=("$value") - done < <(echo "${KUBE_CLUSTERS}" | jq -r '.cluster_name // .clusterName') - - while read -r value; do - CLUSTER_POD_SUBNETS+=("$value") - done < <(echo "${KUBE_CLUSTERS}" | jq -r '.pod_subnet // .podSubnet') - - while read -r value; do - CLUSTER_SVC_SUBNETS+=("$value") - done < <(echo "${KUBE_CLUSTERS}" | jq -r '.svc_subnet // .svcSubnet') - - while read -r value; do - CLUSTER_NETWORK_ID+=("$value") - done < <(echo "${KUBE_CLUSTERS}" | jq -r '.network_id // .network') - - export NUM_CLUSTERS - NUM_CLUSTERS=$(echo "${KUBE_CLUSTERS}" | jq -s 'length') - - echo "${CLUSTER_NAMES[@]}" - echo "${CLUSTER_POD_SUBNETS[@]}" - echo "${CLUSTER_SVC_SUBNETS[@]}" - echo "${CLUSTER_NETWORK_ID[@]}" - echo "${NUM_CLUSTERS}" -} - -##################################################################### -################### SINGLE-CLUSTER SECTION ###################### -##################################################################### - -# cleanup_kind_cluster takes a single parameter NAME -# and deletes the KinD cluster with that name -function cleanup_kind_cluster() { - echo "Test exited with exit code $?." - NAME="${1}" - kind export logs --name "${NAME}" "${ARTIFACTS}/kind" -v9 || true - if [[ -z "${SKIP_CLEANUP:-}" ]]; then - echo "Cleaning up kind cluster" - kind delete cluster --name "${NAME}" -v9 || true - fi -} - -# 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 - echo 'DEFAULT_CLUSTER_YAML file must be specified. Exiting...' - return 1 - fi -} - -function setup_kind_cluster_retry() { - retry setup_kind_cluster "$@" -} - -# setup_kind_cluster creates new KinD cluster with given name, image and configuration -# 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 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() { - NAME="${1:-istio-testing}" - IMAGE="${2:-"${DEFAULT_KIND_IMAGE}"}" - CONFIG="${3:-}" - NOMETALBINSTALL="${4:-}" - - check_default_cluster_yaml - - # Delete any previous KinD cluster - echo "Deleting previous KinD cluster with name=${NAME}" - if ! (kind delete cluster --name="${NAME}" -v9) > /dev/null; then - echo "No existing kind cluster with name ${NAME}. Continue..." - fi - - # explicitly disable shellcheck since we actually want $NAME to expand now - # shellcheck disable=SC2064 - trap "cleanup_kind_cluster ${NAME}" EXIT - - # If config not explicitly set, then use defaults - if [[ -z "${CONFIG}" ]]; then - # Kubernetes 1.15+ - CONFIG=${DEFAULT_CLUSTER_YAML} - # Configure the cluster IP Family only for default configs - if [ "${IP_FAMILY}" != "ipv4" ]; then - grep "ipFamily: ${IP_FAMILY}" "${CONFIG}" || \ - cat <> "${CONFIG}" -networking: - ipFamily: ${IP_FAMILY} -EOF - fi - fi - - # Create KinD cluster - 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 - 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- || true - - # If metrics server configuration directory is specified then deploy in - # the cluster just created - if [[ -n ${METRICS_SERVER_CONFIG_DIR} ]]; then - retry kubectl apply -f "${METRICS_SERVER_CONFIG_DIR}" - fi - - # Install Metallb if not set to install explicitly - if [[ -z "${NOMETALBINSTALL}" ]]; then - retry install_metallb "" - fi - - # IPv6 clusters need some CoreDNS changes in order to work in CI: - # Istio CI doesn't offer IPv6 connectivity, so CoreDNS should be configured - # to work in an offline environment: - # 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 [ "${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:" - echo "${original_coredns}" - # Patch it - fixed_coredns=$( - printf '%s' "${original_coredns}" | sed \ - -e 's/^.*kubernetes cluster\.local/& internal/' \ - -e '/^.*upstream$/d' \ - -e '/^.*fallthrough.*$/d' \ - -e '/^.*forward . \/etc\/resolv.conf$/d' \ - -e '/^.*loop$/d' \ - ) - echo "Patched CoreDNS config:" - echo "${fixed_coredns}" - printf '%s' "${fixed_coredns}" | kubectl apply -f - - fi - - # On Ubuntu Jammy, the trap runs when this function exits. Remove trap to prevent - # cluster shutdown here. - trap EXIT -} - -############################################################################### -#################### MULTICLUSTER SECTION ############################### -############################################################################### - -# Cleans up the clusters created by setup_kind_clusters -# It expects CLUSTER_NAMES to be present which means that -# load_cluster_topology must be called before invoking it -function cleanup_kind_clusters() { - echo "Test exited with exit code $?." - for c in "${CLUSTER_NAMES[@]}"; do - cleanup_kind_cluster "${c}" - done -} - -# 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. 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" - IP_FAMILY="${2:-ipv4}" - - check_default_cluster_yaml - - # Trap replaces any previous trap's, so we need to explicitly cleanup clusters here - trap cleanup_kind_clusters EXIT - - function deploy_kind() { - IDX="${1}" - CLUSTER_NAME="${CLUSTER_NAMES[$IDX]}" - CLUSTER_POD_SUBNET="${CLUSTER_POD_SUBNETS[$IDX]}" - CLUSTER_SVC_SUBNET="${CLUSTER_SVC_SUBNETS[$IDX]}" - CLUSTER_YAML="${ARTIFACTS}/config-${CLUSTER_NAME}.yaml" - if [ ! -f "${CLUSTER_YAML}" ]; then - cp "${DEFAULT_CLUSTER_YAML}" "${CLUSTER_YAML}" - cat <> "${CLUSTER_YAML}" -networking: - podSubnet: ${CLUSTER_POD_SUBNET} - serviceSubnet: ${CLUSTER_SVC_SUBNET} -EOF - fi - - CLUSTER_KUBECONFIG="${KUBECONFIG_DIR}/${CLUSTER_NAME}" - - # Create the clusters. - KUBECONFIG="${CLUSTER_KUBECONFIG}" setup_kind_cluster "${CLUSTER_NAME}" "${IMAGE}" "${CLUSTER_YAML}" "true" - - # Kind currently supports getting a kubeconfig for internal or external usage. To simplify our tests, - # its much simpler if we have a single kubeconfig that can be used internally and externally. - # To do this, we can replace the server with the IP address of the docker container - # https://github.com/kubernetes-sigs/kind/issues/1558 tracks this upstream - CONTAINER_IP=$(docker inspect "${CLUSTER_NAME}-control-plane" --format "{{ .NetworkSettings.Networks.kind.IPAddress }}") - n=0 - until [ $n -ge 10 ]; do - n=$((n+1)) - kind get kubeconfig --name "${CLUSTER_NAME}" --internal | \ - sed "s/${CLUSTER_NAME}-control-plane/${CONTAINER_IP}/g" > "${CLUSTER_KUBECONFIG}" - [ -s "${CLUSTER_KUBECONFIG}" ] && break - sleep 3 - done - - # Enable core dumps - retry docker exec "${CLUSTER_NAME}"-control-plane bash -c "sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited" - } - - # Now deploy the specified number of KinD clusters and - # wait till they are provisioned successfully. - declare -a DEPLOY_KIND_JOBS - for i in "${!CLUSTER_NAMES[@]}"; do - deploy_kind "${i}" & DEPLOY_KIND_JOBS+=("${!}") - done - - for pid in "${DEPLOY_KIND_JOBS[@]}"; do - wait "${pid}" || exit 1 - done - - # Install MetalLB for LoadBalancer support. Must be done synchronously since METALLB_IPS is shared. - # and keep track of the list of Kubeconfig files that will be exported later - export KUBECONFIGS - for CLUSTER_NAME in "${CLUSTER_NAMES[@]}"; do - KUBECONFIG_FILE="${KUBECONFIG_DIR}/${CLUSTER_NAME}" - if [[ ${NUM_CLUSTERS} -gt 1 ]]; then - retry install_metallb "${KUBECONFIG_FILE}" - fi - KUBECONFIGS+=("${KUBECONFIG_FILE}") - done - - ITER_END=$((NUM_CLUSTERS-1)) - for i in $(seq 0 "$ITER_END"); do - for j in $(seq 0 "$ITER_END"); do - if [[ "${j}" -gt "${i}" ]]; then - NETWORK_ID_I="${CLUSTER_NETWORK_ID[i]}" - NETWORK_ID_J="${CLUSTER_NETWORK_ID[j]}" - if [[ "$NETWORK_ID_I" == "$NETWORK_ID_J" ]]; then - POD_TO_POD_AND_SERVICE_CONNECTIVITY=1 - else - POD_TO_POD_AND_SERVICE_CONNECTIVITY=0 - fi - connect_kind_clusters \ - "${CLUSTER_NAMES[i]}" "${KUBECONFIGS[i]}" \ - "${CLUSTER_NAMES[j]}" "${KUBECONFIGS[j]}" \ - "${POD_TO_POD_AND_SERVICE_CONNECTIVITY}" - fi - done - done -} - -function connect_kind_clusters() { - C1="${1}" - C1_KUBECONFIG="${2}" - C2="${3}" - C2_KUBECONFIG="${4}" - POD_TO_POD_AND_SERVICE_CONNECTIVITY="${5}" - - C1_NODE="${C1}-control-plane" - C2_NODE="${C2}-control-plane" - C1_DOCKER_IP=$(docker inspect -f "{{ .NetworkSettings.Networks.kind.IPAddress }}" "${C1_NODE}") - C2_DOCKER_IP=$(docker inspect -f "{{ .NetworkSettings.Networks.kind.IPAddress }}" "${C2_NODE}") - if [ "${POD_TO_POD_AND_SERVICE_CONNECTIVITY}" -eq 1 ]; then - # Set up routing rules for inter-cluster direct pod to pod & service communication - C1_POD_CIDR=$(KUBECONFIG="${C1_KUBECONFIG}" kubectl get node -ojsonpath='{.items[0].spec.podCIDR}') - C2_POD_CIDR=$(KUBECONFIG="${C2_KUBECONFIG}" kubectl get node -ojsonpath='{.items[0].spec.podCIDR}') - C1_SVC_CIDR=$(KUBECONFIG="${C1_KUBECONFIG}" kubectl cluster-info dump | sed -n 's/^.*--service-cluster-ip-range=\([^"]*\).*$/\1/p' | head -n 1) - C2_SVC_CIDR=$(KUBECONFIG="${C2_KUBECONFIG}" kubectl cluster-info dump | sed -n 's/^.*--service-cluster-ip-range=\([^"]*\).*$/\1/p' | head -n 1) - docker exec "${C1_NODE}" ip route add "${C2_POD_CIDR}" via "${C2_DOCKER_IP}" - docker exec "${C1_NODE}" ip route add "${C2_SVC_CIDR}" via "${C2_DOCKER_IP}" - docker exec "${C2_NODE}" ip route add "${C1_POD_CIDR}" via "${C1_DOCKER_IP}" - docker exec "${C2_NODE}" ip route add "${C1_SVC_CIDR}" via "${C1_DOCKER_IP}" - fi -} - -function install_metallb() { - KUBECONFIG="${1}" - 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[*]}" ]; then - # Take IPs from the end of the docker kind network subnet to use for MetalLB IPs - DOCKER_KIND_SUBNET="$(docker inspect kind | jq '.[0].IPAM.Config[0].Subnet' -r)" - METALLB_IPS4=() - while read -r ip; do - METALLB_IPS4+=("$ip") - done < <(cidr_to_ips "$DOCKER_KIND_SUBNET" | tail -n 100) - METALLB_IPS6=() - if [[ "$(docker inspect kind | jq '.[0].IPAM.Config | length' -r)" == 2 ]]; then - # Two configs? Must be dual stack. - DOCKER_KIND_SUBNET="$(docker inspect kind | jq '.[0].IPAM.Config[1].Subnet' -r)" - while read -r ip; do - METALLB_IPS6+=("$ip") - done < <(cidr_to_ips "$DOCKER_KIND_SUBNET" | tail -n 100) - fi - fi - - # Give this cluster of those IPs - RANGE="[" - for i in {0..9}; do - RANGE+="${METALLB_IPS4[1]}," - METALLB_IPS4=("${METALLB_IPS4[@]:1}") - if [[ "${#METALLB_IPS6[@]}" != 0 ]]; then - RANGE+="${METALLB_IPS6[1]}," - METALLB_IPS6=("${METALLB_IPS6[@]:1}") - fi - done - RANGE="${RANGE%?}]" - - echo 'apiVersion: v1 -kind: ConfigMap -metadata: - namespace: metallb-system - name: config -data: - config: | - address-pools: - - name: default - protocol: layer2 - addresses: '"$RANGE" | kubectl apply --kubeconfig="$KUBECONFIG" -f - -} - -function cidr_to_ips() { - CIDR="$1" - # cidr_to_ips returns a list of single IPs from a CIDR. We skip 1000 (since they are likely to be allocated - # already to other services), then pick the next 100. - python3 - < /dev/null); then - if [[ -z "${IGNORE_DIRTY_TREE}" ]] && [[ -n "$(git status --porcelain 2>/dev/null)" ]]; then - BUILD_GIT_REVISION=${BUILD_GIT_REVISION}"-dirty" - fi -else - BUILD_GIT_REVISION=unknown -fi - -# Check for local changes -tree_status="Clean" -if [[ -z "${IGNORE_DIRTY_TREE}" ]] && ! git diff-index --quiet HEAD --; then - tree_status="Modified" -fi - -GIT_DESCRIBE_TAG=$(git describe --tags) -HUB=${HUB:-"docker.io/istio"} - -# used by common/scripts/gobuild.sh -echo "istio.io/pkg/version.buildVersion=${VERSION:-$BUILD_GIT_REVISION}" -echo "istio.io/pkg/version.buildGitRevision=${BUILD_GIT_REVISION}" -echo "istio.io/pkg/version.buildStatus=${tree_status}" -echo "istio.io/pkg/version.buildTag=${GIT_DESCRIBE_TAG}" -echo "istio.io/pkg/version.buildHub=${HUB}" diff --git a/common/scripts/run.sh b/common/scripts/run.sh deleted file mode 100755 index 4e8e97013..000000000 --- a/common/scripts/run.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/bash - -# 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". - -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -e - -WD=$(dirname "$0") -WD=$(cd "$WD"; pwd) - -export FOR_BUILD_CONTAINER=1 -# shellcheck disable=SC1090,SC1091 -source "${WD}/setup_env.sh" - - -MOUNT_SOURCE="${MOUNT_SOURCE:-${PWD}}" -MOUNT_DEST="${MOUNT_DEST:-/work}" - -read -ra DOCKER_RUN_OPTIONS <<< "${DOCKER_RUN_OPTIONS:-}" - -[[ -t 1 ]] && DOCKER_RUN_OPTIONS+=("-i") - -# $CONTAINER_OPTIONS becomes an empty arg when quoted, so SC2086 is disabled for the -# following command only -# shellcheck disable=SC2086 - -for arg in "$@" ; do - if [[ $arg =~ "pilot-discovery" || $arg =~ "istioctl" || $arg =~ "init" ]] - then - IMG="docker.io/golang:1.18" - fi - done - -"${CONTAINER_CLI}" run \ - --rm \ - "${DOCKER_RUN_OPTIONS[@]}" \ - -u "${UID}:${DOCKER_GID}" \ - --init \ - --sig-proxy=true \ - ${DOCKER_SOCKET_MOUNT:--v /var/run/docker.sock:/var/run/docker.sock} \ - $CONTAINER_OPTIONS \ - --env-file <(env | grep -v ${ENV_BLOCKLIST}) \ - -e IN_BUILD_CONTAINER=1 \ - -e TZ="${TIMEZONE:-$TZ}" \ - --mount "type=bind,source=${MOUNT_SOURCE},destination=/work" \ - --mount "type=volume,source=go,destination=/go" \ - --mount "type=volume,source=gocache,destination=/gocache" \ - --mount "type=volume,source=cache,destination=/home/.cache" \ - ${CONDITIONAL_HOST_MOUNTS} \ - -w "${MOUNT_DEST}" "${IMG}" "$@" diff --git a/common/scripts/setup_env.sh b/common/scripts/setup_env.sh deleted file mode 100755 index ad37e53a1..000000000 --- a/common/scripts/setup_env.sh +++ /dev/null @@ -1,234 +0,0 @@ -#!/bin/bash -# shellcheck disable=SC2034 - -# 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". - -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -e - -# https://stackoverflow.com/questions/59895/how-can-i-get-the-source-directory-of-a-bash-script-from-within-the-script-itsel -# Note: the normal way we use in other scripts in Istio do not work when `source`d, which is why we use this approach -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -REPO_ROOT="$(dirname "$(dirname "${SCRIPT_DIR}")")" - -LOCAL_ARCH=$(uname -m) - -# Pass environment set target architecture to build system -if [[ ${TARGET_ARCH} ]]; then - # Target explicitly set - : -elif [[ ${LOCAL_ARCH} == x86_64 ]]; then - TARGET_ARCH=amd64 -elif [[ ${LOCAL_ARCH} == armv8* ]]; then - TARGET_ARCH=arm64 -elif [[ ${LOCAL_ARCH} == arm64* ]]; then - TARGET_ARCH=arm64 -elif [[ ${LOCAL_ARCH} == aarch64* ]]; then - TARGET_ARCH=arm64 -elif [[ ${LOCAL_ARCH} == armv* ]]; then - TARGET_ARCH=arm -elif [[ ${LOCAL_ARCH} == s390x ]]; then - TARGET_ARCH=s390x -elif [[ ${LOCAL_ARCH} == ppc64le ]]; then - TARGET_ARCH=ppc64le -else - echo "This system's architecture, ${LOCAL_ARCH}, isn't supported" - exit 1 -fi - -LOCAL_OS=$(uname) - -# Pass environment set target operating-system to build system -if [[ ${TARGET_OS} ]]; then - # Target explicitly set - : -elif [[ $LOCAL_OS == Linux ]]; then - TARGET_OS=linux - readlink_flags="-f" -elif [[ $LOCAL_OS == Darwin ]]; then - TARGET_OS=darwin - readlink_flags="" -else - echo "This system's OS, $LOCAL_OS, isn't supported" - exit 1 -fi - -# Build image to use -if [[ "${IMAGE_VERSION:-}" == "" ]]; then - IMAGE_VERSION=release-1.14-4ccccb9c73fd9fe0d3b32d31738c8bb16c6b849d -fi -if [[ "${IMAGE_NAME:-}" == "" ]]; then - IMAGE_NAME=build-tools -fi - -DOCKER_GID="${DOCKER_GID:-$(grep '^docker:' /etc/group | cut -f3 -d:)}" - -TIMEZONE=$(readlink "$readlink_flags" /etc/localtime | sed -e 's/^.*zoneinfo\///') - -TARGET_OUT="${TARGET_OUT:-$(pwd)/out/${TARGET_OS}_${TARGET_ARCH}}" -TARGET_OUT_LINUX="${TARGET_OUT_LINUX:-$(pwd)/out/linux_${TARGET_ARCH}}" - -CONTAINER_TARGET_OUT="${CONTAINER_TARGET_OUT:-/work/out/${TARGET_OS}_${TARGET_ARCH}}" -CONTAINER_TARGET_OUT_LINUX="${CONTAINER_TARGET_OUT_LINUX:-/work/out/linux_${TARGET_ARCH}}" - -IMG="${IMG:-gcr.io/istio-testing/${IMAGE_NAME}:${IMAGE_VERSION}}" - -CONTAINER_CLI="${CONTAINER_CLI:-docker}" - -ENV_BLOCKLIST="${ENV_BLOCKLIST:-^_\|^PATH=\|^GOPATH=\|^GOROOT=\|^SHELL=\|^EDITOR=\|^TMUX=\|^USER=\|^HOME=\|^PWD=\|^TERM=\|^rvm=\|^SSH=\|^TMPDIR=\|^CC=\|^CXX=\|^MAKEFILE_LIST=}" - -# Remove functions from the list of exported variables, they mess up with the `env` command. -for f in $(declare -F -x | cut -d ' ' -f 3); -do - unset -f "${f}" -done - -# Set conditional host mounts -CONDITIONAL_HOST_MOUNTS="${CONDITIONAL_HOST_MOUNTS:-} " -container_kubeconfig='' - -# docker conditional host mount (needed for make docker push) -if [[ -d "${HOME}/.docker" ]]; then - CONDITIONAL_HOST_MOUNTS+="--mount type=bind,source=${HOME}/.docker,destination=/config/.docker,readonly " -fi - -# gcloud conditional host mount (needed for docker push with the gcloud auth configure-docker) -if [[ -d "${HOME}/.config/gcloud" ]]; then - CONDITIONAL_HOST_MOUNTS+="--mount type=bind,source=${HOME}/.config/gcloud,destination=/config/.config/gcloud,readonly " -fi - -# gitconfig conditional host mount (needed for git commands inside container) -if [[ -f "${HOME}/.gitconfig" ]]; then - CONDITIONAL_HOST_MOUNTS+="--mount type=bind,source=${HOME}/.gitconfig,destination=/home/.gitconfig,readonly " -fi - -# .netrc conditional host mount (needed for git commands inside container) -if [[ -f "${HOME}/.netrc" ]]; then - CONDITIONAL_HOST_MOUNTS+="--mount type=bind,source=${HOME}/.netrc,destination=/home/.netrc,readonly " -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. -add_KUBECONFIG_if_exists () { - if [[ -f "$1" ]]; then - 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=${1},destination=/config/${kubeconfig_random},readonly " - fi -} - -# This function is designed for maximum compatibility with various platforms. This runs on -# any Mac or Linux platform with bash 4.2+. Please take care not to modify this function -# without testing properly. -# -# This function will properly handle any type of path including those with spaces using the -# loading pattern specified by *kubectl config*. -# -# testcase: "a:b c:d" -# testcase: "a b:c d:e f" -# testcase: "a b:c:d e" -parse_KUBECONFIG () { -TMPDIR="" -if [[ "$1" =~ ([^:]*):(.*) ]]; then - while true; do - rematch=${BASH_REMATCH[1]} - add_KUBECONFIG_if_exists "$rematch" - remainder="${BASH_REMATCH[2]}" - if [[ ! "$remainder" =~ ([^:]*):(.*) ]]; then - if [[ -n "$remainder" ]]; then - add_KUBECONFIG_if_exists "$remainder" - break - fi - fi - done -else - add_KUBECONFIG_if_exists "$1" -fi -} - -KUBECONFIG=${KUBECONFIG:="$HOME/.kube/config"} -parse_KUBECONFIG "${KUBECONFIG}" -if [[ "${FOR_BUILD_CONTAINER:-0}" -eq "1" ]]; then - KUBECONFIG="${container_kubeconfig%?}" -fi - -# LOCAL_OUT should point to architecture where we are currently running versus the desired. -# This is used when we need to run a build artifact during tests or later as part of another -# target. -if [[ "${FOR_BUILD_CONTAINER:-0}" -eq "1" ]]; then - LOCAL_OUT="${TARGET_OUT_LINUX}" -else - LOCAL_OUT="${TARGET_OUT}" -fi - -if [[ "${FOR_BUILD_CONTAINER:-0}" -eq "1" ]]; then - # Override variables with container specific - TARGET_OUT=${CONTAINER_TARGET_OUT} - TARGET_OUT_LINUX=${CONTAINER_TARGET_OUT_LINUX} - REPO_ROOT=/work -fi - -go_os_arch=${LOCAL_OUT##*/} -# Golang OS/Arch format -LOCAL_GO_OS=${go_os_arch%_*} -LOCAL_GO_ARCH=${go_os_arch##*_} - -BUILD_WITH_CONTAINER=0 - -VARS=( - CONTAINER_TARGET_OUT - CONTAINER_TARGET_OUT_LINUX - TARGET_OUT - TARGET_OUT_LINUX - LOCAL_GO_OS - LOCAL_GO_ARCH - LOCAL_OUT - LOCAL_OS - TARGET_OS - LOCAL_ARCH - TARGET_ARCH - TIMEZONE - KUBECONFIG - CONDITIONAL_HOST_MOUNTS - ENV_BLOCKLIST - CONTAINER_CLI - DOCKER_GID - IMG - IMAGE_NAME - IMAGE_VERSION - REPO_ROOT - BUILD_WITH_CONTAINER -) - -# For non container build, we need to write env to file -if [[ "${1}" == "envfile" ]]; then - # ! does a variable-variable https://stackoverflow.com/a/10757531/374797 - for var in "${VARS[@]}"; do - echo "${var}"="${!var}" - done -else - for var in "${VARS[@]}"; do - # shellcheck disable=SC2163 - export "${var}" - done -fi diff --git a/configcenter/load.go b/configcenter/load.go index e8e582bf0..ab579adc9 100644 --- a/configcenter/load.go +++ b/configcenter/load.go @@ -26,8 +26,8 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) const ( diff --git a/configcenter/load_test.go b/configcenter/load_test.go index 1932dd4aa..292c1a8e7 100644 --- a/configcenter/load_test.go +++ b/configcenter/load_test.go @@ -23,8 +23,8 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) func getBootstrap() *model.Bootstrap { diff --git a/configcenter/nacos_load.go b/configcenter/nacos_load.go index fb30da882..8ec969015 100644 --- a/configcenter/nacos_load.go +++ b/configcenter/nacos_load.go @@ -26,8 +26,8 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) const ( diff --git a/go.mod b/go.mod index 5e88e8e34..1454714b1 100644 --- a/go.mod +++ b/go.mod @@ -2,48 +2,21 @@ module github.com/apache/dubbo-go-pixiu go 1.18 -// https://github.com/containerd/containerd/issues/5781 -exclude k8s.io/kubernetes v1.13.0 - -// Client-go does not handle different versions of mergo due to some breaking changes - use the matching version -replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.5 - -replace istio.io/api => github.com/dubbo-go-pixiu/operator-api v0.0.0-20230521024122-de7669e54430 - -replace istio.io/client-go => github.com/dubbo-go-pixiu/operator-client-go v1.14.6-0.20230521064746-0907a7fb8042 - require ( - cloud.google.com/go/compute v1.6.0 - cloud.google.com/go/security v1.3.0 - contrib.go.opencensus.io/exporter/prometheus v0.4.1 dubbo.apache.org/dubbo-go/v3 v3.0.2-0.20220519062747-f6405fa79d5c - github.com/AdaLogics/go-fuzz-headers v0.0.0-20220408101031-f1761e18c0c6 - github.com/Masterminds/sprig/v3 v3.2.2 github.com/MicahParks/keyfunc v1.0.0 github.com/Shopify/sarama v1.19.0 github.com/alibaba/sentinel-golang v1.0.4 github.com/apache/dubbo-getty v1.4.8 github.com/apache/dubbo-go-hessian2 v1.11.3 github.com/cch123/supermonkey v1.0.1 - github.com/cenkalti/backoff/v4 v4.1.3 - github.com/census-instrumentation/opencensus-proto v0.3.0 - github.com/cheggaaa/pb/v3 v3.0.8 - github.com/cncf/xds/go v0.0.0-20220330162227-eded343319d0 - github.com/coreos/go-oidc/v3 v3.1.0 github.com/creasty/defaults v1.5.2 - github.com/davecgh/go-spew v1.1.1 - github.com/docker/cli v23.0.0-rc.1+incompatible github.com/dubbo-go-pixiu/pixiu-api v0.1.6-0.20220612115254-d9a176b25b99 github.com/dubbogo/go-zookeeper v1.0.4-0.20211212162352-f9d2183d89d5 github.com/dubbogo/gost v1.13.3-0.20221216065235-4c45e166b5a0 github.com/dubbogo/grpc-go v1.42.9 github.com/dubbogo/triple v1.1.8 github.com/envoyproxy/go-control-plane v0.10.2-0.20220428052930-ec95b9f870a8 - github.com/evanphx/json-patch/v5 v5.6.0 - github.com/fatih/color v1.13.0 - github.com/felixge/fgprof v0.9.2 - github.com/florianl/go-nflog/v2 v2.0.1 - github.com/fsnotify/fsnotify v1.5.4 github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 github.com/go-errors/errors v1.0.1 github.com/go-playground/assert/v2 v2.0.1 @@ -53,51 +26,18 @@ require ( github.com/golang-jwt/jwt/v4 v4.4.2 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.2 - github.com/google/cel-go v0.12.5 - github.com/google/go-cmp v0.5.9 - github.com/google/go-containerregistry v0.8.0 - github.com/google/gofuzz v1.2.0 - github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 - github.com/google/uuid v1.3.0 - github.com/gorilla/mux v1.8.0 - github.com/gorilla/websocket v1.5.0 - github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 - github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 - github.com/hashicorp/go-multierror v1.1.1 - github.com/hashicorp/go-version v1.4.0 - github.com/hashicorp/golang-lru v0.5.4 github.com/imdario/mergo v0.3.12 github.com/jhump/protoreflect v1.9.0 - github.com/kr/pretty v0.3.0 - github.com/kylelemons/godebug v1.1.0 - github.com/lestrrat-go/jwx v1.2.29 - github.com/lucas-clemente/quic-go v0.27.0 - github.com/mattn/go-isatty v0.0.14 - github.com/miekg/dns v1.1.48 - github.com/mitchellh/copystructure v1.2.0 - github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/mapstructure v1.5.0 - github.com/moby/buildkit v0.11.4 github.com/nacos-group/nacos-sdk-go v1.1.3 - github.com/onsi/gomega v1.20.1 - github.com/openshift/api v0.0.0-20200713203337-b2494ecb17dd github.com/opentrx/seata-golang/v2 v2.0.5 github.com/pkg/errors v0.9.1 - github.com/pmezard/go-difflib v1.0.0 github.com/prometheus/client_golang v1.14.0 - github.com/prometheus/client_model v0.3.0 github.com/prometheus/common v0.37.0 - github.com/prometheus/prometheus v2.5.0+incompatible - github.com/ryanuber/go-glob v1.0.0 github.com/spf13/cast v1.5.0 github.com/spf13/cobra v1.5.0 - github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.11.0 - github.com/stretchr/testify v1.9.0 - github.com/vishvananda/netlink v1.2.0-beta - github.com/yl2chen/cidranger v1.0.2 + github.com/stretchr/testify v1.8.4 go.etcd.io/etcd/api/v3 v3.5.5 - go.opencensus.io v0.23.0 go.opentelemetry.io/otel v1.10.0 go.opentelemetry.io/otel/exporters/jaeger v1.10.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.10.0 @@ -107,224 +47,103 @@ require ( go.opentelemetry.io/otel/sdk v1.10.0 go.opentelemetry.io/otel/sdk/metric v0.32.1 go.opentelemetry.io/otel/trace v1.10.0 - go.opentelemetry.io/proto/otlp v0.19.0 - go.uber.org/atomic v1.9.0 go.uber.org/zap v1.21.0 - golang.org/x/crypto v0.21.0 - golang.org/x/net v0.21.0 - golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 - golang.org/x/sync v0.1.0 - golang.org/x/sys v0.18.0 - golang.org/x/time v0.1.0 - gomodules.xyz/jsonpatch/v3 v3.0.1 - google.golang.org/api v0.74.0 - google.golang.org/genproto v0.0.0-20220706185917-7780775163c4 + golang.org/x/crypto v0.14.0 + golang.org/x/net v0.17.0 google.golang.org/grpc v1.50.1 google.golang.org/protobuf v1.28.1 - gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/yaml.v2 v2.4.0 - gopkg.in/yaml.v3 v3.0.1 - helm.sh/helm/v3 v3.10.3 - istio.io/api v0.0.0-20221004225839-607aeaab2827 - istio.io/client-go v1.14.4-0.20220906213432-736be6f83263 - istio.io/pkg v0.0.0-20220906212832-f98e656e3df0 - k8s.io/api v0.25.2 - k8s.io/apiextensions-apiserver v0.25.2 - k8s.io/apimachinery v0.25.2 - k8s.io/cli-runtime v0.25.2 - k8s.io/client-go v0.25.2 - k8s.io/klog/v2 v2.70.1 - k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 - k8s.io/kubectl v0.25.2 - k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed mosn.io/proxy-wasm-go-host v0.1.0 - sigs.k8s.io/controller-runtime v0.11.2 - sigs.k8s.io/gateway-api v0.4.1-0.20220419214231-03f50b47814e - sigs.k8s.io/mcs-api v0.1.0 - sigs.k8s.io/yaml v1.3.0 vimagination.zapto.org/byteio v0.0.0-20200222190125-d27cba0f0b10 ) require ( - cloud.google.com/go v0.101.0 // indirect - cloud.google.com/go/logging v1.5.0-jsonlog-preview // indirect - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.28 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.21 // indirect - github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/logger v0.2.1 // indirect - github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/BurntSushi/toml v1.1.0 // indirect - github.com/MakeNowJust/heredoc v1.0.0 // indirect - github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.1.1 // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + cloud.google.com/go v0.65.0 // indirect + contrib.go.opencensus.io/exporter/prometheus v0.4.1 // indirect github.com/RoaringBitmap/roaring v0.7.1 // indirect - github.com/VividCortex/ewma v1.1.1 // indirect github.com/Workiva/go-datastructures v1.0.52 // indirect github.com/aliyun/alibaba-cloud-sdk-go v1.61.18 // indirect - github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220418222510-f25a4f6275ed // indirect - github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.2.0 // indirect - github.com/blang/semver/v4 v4.0.0 // indirect github.com/buger/jsonparser v1.1.1 // indirect - github.com/cespare/xxhash v1.1.0 // indirect + github.com/cenkalti/backoff/v4 v4.1.3 // indirect + github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/chai2010/gettext-go v1.0.2 // indirect - github.com/cheekybits/genny v1.0.0 // indirect github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect - github.com/containerd/stargz-snapshotter/estargz v0.13.0 // indirect - github.com/containerd/typeurl v1.0.2 // indirect + github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc // indirect github.com/coreos/go-semver v0.3.0 // indirect - github.com/coreos/go-systemd/v22 v22.4.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/cyphar/filepath-securejoin v0.2.3 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect - github.com/docker/distribution v2.8.2+incompatible // indirect - github.com/docker/docker v24.0.9+incompatible // indirect - github.com/docker/docker-credential-helpers v0.7.0 // indirect + github.com/coreos/go-systemd/v22 v22.3.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/eapache/go-resiliency v1.1.0 // indirect github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect github.com/eapache/queue v1.1.0 // indirect - github.com/emicklei/go-restful/v3 v3.8.0 // indirect github.com/envoyproxy/protoc-gen-validate v0.6.7 // indirect - github.com/evanphx/json-patch v5.6.0+incompatible // indirect - github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect - github.com/fatih/camelcase v1.0.0 // indirect - github.com/fvbommel/sortorder v1.0.1 // indirect - github.com/go-kit/log v0.2.1 // indirect + github.com/go-kit/log v0.2.0 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.6 // indirect - github.com/go-openapi/swag v0.21.1 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-playground/validator/v10 v10.11.0 // indirect - github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect - github.com/gobwas/glob v0.2.3 // indirect - github.com/goccy/go-json v0.10.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/btree v1.0.1 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1 // indirect - github.com/googleapis/gax-go/v2 v2.3.0 // indirect - github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.10.0 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/huandu/xstrings v1.3.2 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/gorilla/websocket v1.4.2 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jinzhu/copier v0.3.5 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/jonboulle/clockwork v0.2.2 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/k0kubun/pp v3.0.1+incompatible // indirect - github.com/klauspost/compress v1.15.12 // indirect github.com/knadh/koanf v1.4.1 // indirect - github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.2.1 // indirect - github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect - github.com/lestrrat-go/blackmagic v1.0.2 // indirect - github.com/lestrrat-go/httpcc v1.0.1 // indirect - github.com/lestrrat-go/iter v1.0.2 // indirect - github.com/lestrrat-go/option v1.0.1 // indirect - github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.6 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/marten-seemann/qpack v0.2.1 // indirect - github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect - github.com/marten-seemann/qtls-go1-17 v0.1.2 // indirect - github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-runewidth v0.0.13 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mdlayher/netlink v1.4.1 // indirect - github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00 // indirect - github.com/mitchellh/go-wordwrap v1.0.0 // indirect + github.com/mattn/go-colorable v0.1.7 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/moby/spdystream v0.2.0 // indirect - github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/mschoch/smat v0.2.0 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/natefinch/lumberjack v2.0.0+incompatible // indirect - github.com/nxadm/tail v1.4.8 // indirect - github.com/onsi/ginkgo v1.16.5 // indirect - github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.3-0.20220303224323-02efb9a75ee1 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect - github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.1 // indirect - github.com/peterbourgon/diskv v2.0.1+incompatible // indirect + github.com/pelletier/go-toml v1.7.0 // indirect github.com/pierrec/lz4 v2.5.2+incompatible // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect - github.com/prometheus/prom2json v1.3.1 // indirect github.com/prometheus/statsd_exporter v0.21.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a // indirect - github.com/rivo/uniseg v0.2.0 // indirect - github.com/rogpeppe/go-internal v1.8.0 // indirect - github.com/russross/blackfriday v1.6.0 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b // indirect github.com/shirou/gopsutil/v3 v3.22.2 // indirect - github.com/shopspring/decimal v1.2.0 // indirect - github.com/sirupsen/logrus v1.9.0 // indirect - github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/spf13/afero v1.8.2 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/stoewer/go-strcase v1.2.0 // indirect - github.com/subosito/gotenv v1.3.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect github.com/tklauser/go-sysconf v0.3.10 // indirect github.com/tklauser/numcpus v0.4.0 // indirect github.com/uber/jaeger-client-go v2.29.1+incompatible // indirect github.com/uber/jaeger-lib v2.4.1+incompatible // indirect github.com/ugorji/go/codec v1.2.6 // indirect - github.com/vbatts/tar-split v0.11.2 // indirect - github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect - github.com/wasmerio/wasmer-go v1.0.4 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect - github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - github.com/xeipuuv/gojsonschema v1.2.0 // indirect - github.com/xlab/treeprint v1.1.0 // indirect + github.com/wasmerio/wasmer-go v1.0.3 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.5.5 // indirect - go.etcd.io/etcd/client/v3 v3.5.5 // indirect - go.etcd.io/etcd/server/v3 v3.5.5 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.4 // indirect + go.etcd.io/etcd/client/v3 v3.5.4 // indirect + go.opencensus.io v0.23.0 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.10.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0 // indirect - go.starlark.net v0.0.0-20211013185944-b0039bd2cfe3 // indirect - go.uber.org/multierr v1.8.0 // indirect + go.opentelemetry.io/proto/otlp v0.19.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.6.0 // indirect golang.org/x/arch v0.0.0-20200826200359-b19915210f00 // indirect - golang.org/x/mod v0.8.0 // indirect - golang.org/x/term v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.6.0 // indirect - gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect - gomodules.xyz/orderedmap v0.1.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/ini.v1 v1.66.4 // indirect + golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect + golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/appengine v1.6.6 // indirect + google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7 // indirect + gopkg.in/ini.v1 v1.51.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect - gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect - k8s.io/component-base v0.25.2 // indirect - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect - sigs.k8s.io/kustomize/api v0.12.1 // indirect - sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect - vimagination.zapto.org/memio v0.0.0-20221021163155-6d79b4ee428f // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + vimagination.zapto.org/memio v1.0.0 // indirect ) - -replace google.golang.org/protobuf v1.28.1 => google.golang.org/protobuf v1.28.0 diff --git a/go.sum b/go.sum index 122b30187..3e1adf783 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,9 @@ -bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -17,185 +13,67 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.101.0 h1:g+LL+JvpvdyGtcaD2xw2mSByE/6F9s471eJSoaysM84= -cloud.google.com/go v0.101.0/go.mod h1:hEiddgDb77jDQ+I80tURYNJEnuwPzFU8awCFFRLKjW0= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0 h1:XdQIN5mdPTSBVwSIVDuY5e8ZzVAccsHvD3qTEz4zIps= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= -cloud.google.com/go/logging v1.5.0-jsonlog-preview h1:ytQ2SSy+rh+fGUDdRo8zHeHTi+fhcZMyUddSgYhbugc= -cloud.google.com/go/logging v1.5.0-jsonlog-preview/go.mod h1:A26OFzlArW55bhS1qMGlgsctgae2DbXoFE7JAtzGVco= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/security v1.3.0 h1:BhCl33x+KQI4qiZnFrfr2gAGhb2aZ0ZvKB3Y4QlEfgo= -cloud.google.com/go/security v1.3.0/go.mod h1:pQsnLAXfMzuWVJdctBs8BV3tGd3Jr0SMYu6KK3QXYAs= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.22.0/go.mod h1:GbaLEoMqbVm6sx3Z0R++gSiBlgMv6yUi2q1DeGFKQgE= contrib.go.opencensus.io/exporter/prometheus v0.4.1 h1:oObVeKo2NxpdF/fIfrPsNj6K0Prg0R0mHM+uANlYMiM= contrib.go.opencensus.io/exporter/prometheus v0.4.1/go.mod h1:t9wvfitlUjGXG2IXAZsuFq26mDGid/JwCEXp+gTG/9U= -dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= -dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= -dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= dubbo.apache.org/dubbo-go/v3 v3.0.2-0.20220519062747-f6405fa79d5c h1:2pkHGglVaHLrviaIbv4F4MdDZ/+Ta87tY73TX7EP6i8= dubbo.apache.org/dubbo-go/v3 v3.0.2-0.20220519062747-f6405fa79d5c/go.mod h1:5Ba+I+RykqCJiT9Ys66GezlfZZk8eIJxNnowcCgboYs= -git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20220408101031-f1761e18c0c6 h1:l+ujeiNp0cAxa7aU5Cw1ufjaxu4FfpuyeW1DQhV21Cc= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20220408101031-f1761e18c0c6/go.mod h1:mHEIrBMD2IgTJphw1R5sfgwN/Xg/q8IPOUlb90leKyk= -github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM= -github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= -github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk= -github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= -github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= -github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= -github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= -github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= -github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= -github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= github.com/MicahParks/keyfunc v1.0.0 h1:O9VAkG6q/LqX4eS+HuIsW9KfC/Luh2NBQr9v4NiwHU0= github.com/MicahParks/keyfunc v1.0.0/go.mod h1:R8RZa27qn+5cHTfYLJ9/+7aSb5JIdz7cl0XFo0o4muo= -github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= -github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= -github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= -github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= -github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= -github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= -github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= -github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/RoaringBitmap/roaring v0.7.1 h1:HkcLv8q/kwGJnhEWe+vinu+04DGDdQ7nVivMhNhxP2g= github.com/RoaringBitmap/roaring v0.7.1/go.mod h1:jdT9ykXwHFNdJbEtxePexlFYH9LXucApeS0/+/g+p1I= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/Shopify/sarama v1.19.0 h1:9oksLxC6uxVPHPVYUmq6xhr1BOF/hHobWH2UzO67z1s= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= -github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= -github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/Workiva/go-datastructures v1.0.52 h1:PLSK6pwn8mYdaoaCZEMsXBpBotr4HHn9abU0yMQt0NI= github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw= -github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alessio/shellescape v1.2.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= -github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= github.com/alibaba/sentinel-golang v1.0.4 h1:i0wtMvNVdy7vM4DdzYrlC4r/Mpk1OKUUBurKKkWhEo8= github.com/alibaba/sentinel-golang v1.0.4/go.mod h1:Lag5rIYyJiPOylK8Kku2P+a23gdKMMqzQS7wTnjWEpk= github.com/aliyun/alibaba-cloud-sdk-go v1.61.18 h1:zOVTBdCKFd9JbCKz9/nt+FovbjPFmb7mUnp8nH9fQBA= github.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220418222510-f25a4f6275ed h1:ue9pVfIcP+QMEjfgo/Ez4ZjNZfonGgR6NgjMaJMu1Cg= -github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220418222510-f25a4f6275ed/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/apache/dubbo-getty v1.4.8 h1:Q9WKXmVu4Dm16cMJHamegRbxpDiYaGIU+MnPGhJhNyk= github.com/apache/dubbo-getty v1.4.8/go.mod h1:cPJlbcHUTNTpiboMQjMHhE9XBni11LiBiG8FdrDuVzk= github.com/apache/dubbo-go-hessian2 v1.9.1/go.mod h1:xQUjE7F8PX49nm80kChFvepA/AvqAZ0oh/UaB6+6pBE= @@ -206,20 +84,12 @@ github.com/apache/dubbo-go-hessian2 v1.11.3/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHs github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY= -github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= @@ -234,72 +104,39 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21 github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= -github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= -github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= -github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cch123/supermonkey v1.0.1 h1:sPNQhaqMpfpERGb1oNoPcYV5tGln72SLlG2q2ozpzqg= github.com/cch123/supermonkey v1.0.1/go.mod h1:d5jXTCyG6nu/pu0vYmoC0P/l0eBGesv3oQQ315uNBOA= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0 h1:t/LhUZLVitR1Ow2YOnduCsavhwFUklBMoGVYUCqmCqk= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= -github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= -github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= -github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= -github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= -github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= -github.com/cheggaaa/pb/v3 v3.0.8 h1:bC8oemdChbke2FHIIGy9mn4DPJ2caZYQnfbRqwmdCoA= -github.com/cheggaaa/pb/v3 v3.0.8/go.mod h1:UICbiLec/XO6Hw6k+BHEtHeQFzzBH4i2/qk/ow1EJTA= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= -github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= -github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= -github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 h1:hzAQntlaYRkVSFEfj9OTWlVV1H155FMD8BTKktLv0QI= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= @@ -308,193 +145,40 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc h1:PYXxkRUBGUMa5xgMVMDl62vEklZvKpVaxQeN9ie7Hfk= github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20220330162227-eded343319d0 h1:MK/+hUKWd1o46LiZ/PK0GHUEYDmHVbxqW6WSJBh61c8= -github.com/cncf/xds/go v0.0.0-20220330162227-eded343319d0/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= -github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= -github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= -github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= -github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= -github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= -github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= -github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= -github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= -github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= -github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= -github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= -github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= -github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= -github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= -github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= -github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s= -github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= -github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= -github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= -github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= -github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= -github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= -github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= -github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= -github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= -github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= -github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= -github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= -github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/stargz-snapshotter/estargz v0.10.0/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= -github.com/containerd/stargz-snapshotter/estargz v0.10.1/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= -github.com/containerd/stargz-snapshotter/estargz v0.13.0 h1:fD7AwuVV+B40p0d9qVkH/Au1qhp8hn/HWJHIYjpEcfw= -github.com/containerd/stargz-snapshotter/estargz v0.13.0/go.mod h1:m+9VaGJGlhCnrcEUod8mYumTmRgblwd3rC5UCEh2Yp0= -github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= -github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= -github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= -github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= -github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= -github.com/containerd/typeurl v1.0.2 h1:Chlt8zIieDbzQFzXzAeBEF92KhExuE4p9p92/QmY7aY= -github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= -github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= -github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= -github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= -github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= -github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= -github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= -github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-oidc/v3 v3.1.0 h1:6avEvcdvTa1qYsOZ6I5PRkSYHzpTNWgKYmaJfaYbrRw= -github.com/coreos/go-oidc/v3 v3.1.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/go-systemd/v22 v22.4.0 h1:y9YHcjnjynCd/DVbg5j9L/33jQM3MxJlbj/zWskzfGU= -github.com/coreos/go-systemd/v22 v22.4.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creasty/defaults v1.5.2 h1:/VfB6uxpyp6h0fr7SPp7n8WJBoV8jfxQXPCnkVSjyls= github.com/creasty/defaults v1.5.2/go.mod h1:FPZ+Y0WNrbqOVw+c6av63eyHUAl6pMHZwqLPvXUZGfY= -github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= -github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= -github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= -github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= -github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= -github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= -github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= -github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/docker/cli v20.10.10+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/cli v20.10.12+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/cli v23.0.0-rc.1+incompatible h1:Vl3pcUK4/LFAD56Ys3BrqgAtuwpWd/IO3amuSL0ZbP0= -github.com/docker/cli v23.0.0-rc.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= -github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= -github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.10+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.12+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0= -github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= -github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= -github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= -github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dubbo-go-pixiu/operator-api v0.0.0-20230521024122-de7669e54430 h1:2Qjn7n3kCSJ60VSNnpA/RbmeXcel6ftjhrN6opQ4SJQ= -github.com/dubbo-go-pixiu/operator-api v0.0.0-20230521024122-de7669e54430/go.mod h1:SWsRqLbdc3vFk5cIRylKHC28nx58hfikw2rjlpwR0Qs= -github.com/dubbo-go-pixiu/operator-client-go v1.14.6-0.20230521064746-0907a7fb8042 h1:L/NmoQfKNhNObrfqVpit3q1DxN6hXn8N0EG9fCtDRaI= -github.com/dubbo-go-pixiu/operator-client-go v1.14.6-0.20230521064746-0907a7fb8042/go.mod h1:PvtYHGhoKNXvyX9hPuScG/bQ2CLD47OSNvltcWmE8H4= github.com/dubbo-go-pixiu/pixiu-api v0.1.6-0.20220612115254-d9a176b25b99 h1:UjDxgIEu6DbJVJTrxm5mwC0j54jNao1pkYVlT8X+KgY= github.com/dubbo-go-pixiu/pixiu-api v0.1.6-0.20220612115254-d9a176b25b99/go.mod h1:1l+6pDTdEHwCyyyJmfckOAdGp6f5PZ33ZVMgxso9q/U= github.com/dubbogo/go-zookeeper v1.0.3/go.mod h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c= @@ -526,18 +210,11 @@ github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful/v3 v3.7.4/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= -github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= @@ -548,62 +225,28 @@ github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go. github.com/envoyproxy/go-control-plane v0.10.2-0.20220428052930-ec95b9f870a8 h1:ZqKqxZ7mtvDKhP9H9eqQpbX2VwSiN7HKCU4SHqOPjM0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220428052930-ec95b9f870a8/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= github.com/envoyproxy/protoc-gen-validate v0.6.7 h1:qcZcULcd/abmQg6dwigimCNEyi4gg31M/xaciQlDml8= github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= -github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= -github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= -github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.0.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/evanphx/json-patch/v5 v5.5.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= -github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= -github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= -github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= -github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw= -github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/felixge/fgprof v0.9.2 h1:tAMHtWMyl6E0BimjVbFt7fieU6FpjttsZN7j0wT5blc= -github.com/felixge/fgprof v0.9.2/go.mod h1:+VNi+ZXtHIQ6wIw6bUT8nXQRefQflWECoFyRealT5sg= -github.com/florianl/go-nflog/v2 v2.0.1 h1:8csWIqFQ2vDaZJkxezY3dXDB7bEFg0VRFsYd2Bzj3II= -github.com/florianl/go-nflog/v2 v2.0.1/go.mod h1:g+SOgM/SuePn9bvS/eo3Ild7J71nSb29OzbxR+7cln0= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= -github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE= -github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= -github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= -github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= @@ -614,14 +257,12 @@ github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0 h1:7i2K3eKTos3Vc0enKCfnVcgHh2olr/MyfboYq7cAcFw= github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= -github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= github.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -629,68 +270,14 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 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/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= -github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= -github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= -github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= -github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= -github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= -github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= -github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= -github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= -github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= -github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= -github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= -github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= -github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= -github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= @@ -705,40 +292,24 @@ github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM= github.com/go-xorm/xorm v0.7.9/go.mod h1:XiVxrMMIhFkwSkh96BW7PACl7UhLtx2iJIHMdmjh5sQ= -github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= -github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= -github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= -github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/goinggo/mapstructure v0.0.0-20140717182941-194205d9b4a9 h1:wqckanyE9qc/XnvnybC6SHOb8Nyd62QXAZOzA8twFig= github.com/goinggo/mapstructure v0.0.0-20140717182941-194205d9b4a9/go.mod h1:64ikIrMv84B+raz7akXOqbF7cK3/OQQ/6cClY10oy7A= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs= github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= @@ -752,7 +323,6 @@ github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -763,7 +333,6 @@ github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71 github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -779,12 +348,10 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc= @@ -795,13 +362,8 @@ github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2 github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw= github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b/go.mod h1:Z4GIJBJO3Wa4gD4vbwQxXXZ+WHmW6E9ixmNrwvs0iZs= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/cel-go v0.12.5 h1:DmzaiSgoaqGCjtpPQWl26/gND+yRpim56H1jCVev6d8= -github.com/google/cel-go v0.12.5/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw= -github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -809,29 +371,16 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -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-containerregistry v0.7.0/go.mod h1:2zaoelrL0d08gGbpdP3LqyUuBmhWbpD6IOe2s9nLS2k= -github.com/google/go-containerregistry v0.8.0 h1:mtR24eN6rapCN+shds82qFEIWWmg64NPMuyCNT7/Ogc= -github.com/google/go-containerregistry v0.8.0/go.mod h1:wW5v71NHGnQyb4k+gSshjxidrC7lN33MdWEn+Mz9TsI= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -839,105 +388,55 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= -github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1 h1:8pyqKJvrJqUYaKS851Ule26pwWvey6IDMiczaBLDKLQ= -github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1/go.mod h1:gSuNB+gJaOiQKLEZ+q+PK9Mq3SOzhRcw2GsGS/FhYDk= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= -github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0 h1:nRJtk3y8Fm770D42QV6T90ZnvFZyk7agSo3Q+Z9p3WI= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= -github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= -github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.10.0 h1:ESEyqQqXXFIcImj/BE8oKEX37Zsuceb2cZI+EL/zNCY= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.10.0/go.mod h1:XnLCLFp3tjoZJszVKjfpyAK6J8sYIcQXWQxmqLWF21I= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= -github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= @@ -945,7 +444,6 @@ github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= github.com/hashicorp/go-secure-stdlib/mlock v0.1.1/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= @@ -960,57 +458,39 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4= -github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= -github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= -github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= github.com/hashicorp/vault/sdk v0.3.0/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= -github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= -github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= github.com/jackc/pgx v3.6.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag= -github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= github.com/jhump/protoreflect v1.9.0 h1:npqHz788dryJiR/l6K/RUQAyh2SwV91+d1dnh4RjO9w= github.com/jhump/protoreflect v1.9.0/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= -github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -1021,21 +501,7 @@ github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqx github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA= -github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= -github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= -github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= -github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= -github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw= -github.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs= -github.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA= -github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U= -github.com/jsimonetti/rtnetlink v0.0.0-20210525051524-4cc836578190 h1:iycCSDo8EKVueI9sfVBBJmtNn9DnXV/K1YWwEJO+uOs= -github.com/jsimonetti/rtnetlink v0.0.0-20210525051524-4cc836578190/go.mod h1:NmKSdU4VGSiv1bMsdqNALI4RSvvjtz65tTMCnD05qLo= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -1060,16 +526,10 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM= -github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/knadh/koanf v1.4.1 h1:Z0VGW/uo8NJmjd+L1Dc3S5frq6c62w5xQ9Yf4Mg3wFQ= github.com/knadh/koanf v1.4.1/go.mod h1:1cfH5223ZeZUOs8FU2UdTmaNfHpqgtjV0+NHjRO43gs= github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7/go.mod h1:Y2SaZf2Rzd0pXkLVhLlCiAXFCLSXAIbTKDivVgff/AM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -1079,132 +539,49 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A= -github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= -github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= -github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= -github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= -github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= -github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= -github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= -github.com/lestrrat-go/jwx v1.2.29 h1:QT0utmUJ4/12rmsVQrJ3u55bycPkKqGYuGT4tyRhxSQ= -github.com/lestrrat-go/jwx v1.2.29/go.mod h1:hU8k2l6WF0ncx20uQdOmik/Gjg6E3/wIRtXSNFeZuB8= -github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= -github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= -github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570/go.mod h1:BLt8L9ld7wVsvEWQbuLrUZnCMnUmLZ+CGDzKtclrTlE= github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f/go.mod h1:UGmTpUd3rjbtfIpwAPrcfmGf/Z1HS95TATB+m57TPB8= github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042/go.mod h1:TPpsiPUEh0zFL1Snz4crhMlBe60PYxRHr5oFF3rRYg0= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= -github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/lucas-clemente/quic-go v0.27.0 h1:v6WY87q9zD4dKASbG8hy/LpzAVNzEQzw8sEIeloJsc4= -github.com/lucas-clemente/quic-go v0.27.0/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= -github.com/marten-seemann/qpack v0.2.1 h1:jvTsT/HpCn2UZJdP+UUB53FfUUgeOyG5K1ns0OJOGVs= -github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= -github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ= -github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= -github.com/marten-seemann/qtls-go1-17 v0.1.1/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= -github.com/marten-seemann/qtls-go1-17 v0.1.2 h1:JADBlm0LYiVbuSySCHeY863dNkcpMmDR7s0bLKJeYlQ= -github.com/marten-seemann/qtls-go1-17 v0.1.2/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= -github.com/marten-seemann/qtls-go1-18 v0.1.1/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= -github.com/marten-seemann/qtls-go1-18 v0.1.2 h1:JH6jmzbduz0ITVQ7ShevK10Av5+jBEKAHMntXmIV7kM= -github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43 h1:WgyLFv10Ov49JAQI/ZLUkCZ7VJS3r74hwFIGXJsgZlY= -github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo= -github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0= -github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc= -github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= -github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= -github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= -github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= -github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8= -github.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= -github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= -github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys= -github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8= -github.com/mdlayher/netlink v1.4.1 h1:I154BCU+mKlIf7BgcAJB2r7QjveNPty6uNY1g9ChVfI= -github.com/mdlayher/netlink v1.4.1/go.mod h1:e4/KuJ+s8UhfUpO9z00/fDZZmhSrs+oxyqAS9cNgn6Q= -github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00 h1:qEtkL8n1DAHpi5/AOgAckwGQUlMe4+jhL/GMt+GKIks= -github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00/go.mod h1:GAFlyu4/XV68LkQKYzKhIo/WW7j3Zi0YRAz/BOoanUc= -github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.48 h1:Ucfr7IIVyMBz4lRE8qmGUuZ4Wt3/ZGu9hmcMT3Uu4tQ= -github.com/miekg/dns v1.1.48/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= @@ -1212,25 +589,11 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/buildkit v0.11.4 h1:mleVHr+n7HUD65QNUkgkT3d8muTzhYUoHE9FM3Ej05s= -github.com/moby/buildkit v0.11.4/go.mod h1:P5Qi041LvCfhkfYBHry+Rwoo3Wi6H971J2ggE+PcIoo= -github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= -github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= -github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -1238,20 +601,11 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nacos-group/nacos-sdk-go v1.0.8/go.mod h1:hlAPn3UdzlxIlSILAyOXKxjFSvDJ9oLzTJ9hLAK1KzA= github.com/nacos-group/nacos-sdk-go v1.1.1/go.mod h1:UHOtQNQY/qpk2dhg6gDq8u5+/CEIc3+lWmrmxEzX0/g= github.com/nacos-group/nacos-sdk-go v1.1.3 h1:xNlSC9li2A11ifTA8HCqgM6NRImGUJA4X+gGK5muJuQ= @@ -1265,77 +619,17 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= -github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= -github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso= github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= -github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= -github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= -github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2-0.20210730191737-8e42a01fb1b7/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2-0.20210819154149-5ad6f50d6283/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.3-0.20220303224323-02efb9a75ee1 h1:9iFHD5Kt9hkOfeawBNiEeEaV7bmC4/Z5wJp8E9BptMs= -github.com/opencontainers/image-spec v1.0.3-0.20220303224323-02efb9a75ee1/go.mod h1:K/JAU0m27RFhDRX4PcFdIKntROP6y5Ed6O91aZYDQfs= -github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= -github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= -github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= -github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= -github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= -github.com/openshift/api v0.0.0-20200713203337-b2494ecb17dd h1:MV2FH/cm1wqoVCIL98GT46CMnXZw9faUoIzdZ4nfZw0= -github.com/openshift/api v0.0.0-20200713203337-b2494ecb17dd/go.mod h1:vWmWTm4y7XR3wkLR+bDDjRbvkBfx2yP7yve6kfb7+Ts= -github.com/openshift/build-machinery-go v0.0.0-20200713135615-1f43d26dccc7/go.mod h1:b1BuldmJlbA/xYtdZvKi+7j5YGB44qJUJDZ9zwiNCfE= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -1345,7 +639,6 @@ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYr github.com/opentrx/seata-golang/v2 v2.0.5 h1:WG5mIMBNL8tcV9k3VuEj1ivekTEfxLwKKYBO8wtlw94= github.com/opentrx/seata-golang/v2 v2.0.5/go.mod h1:gVGvCWHjTS+QVsq0A2iBLbQPW9PkTUheJ5Nnlyg6Dog= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= -github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= @@ -1354,17 +647,9 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= -github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -1372,28 +657,21 @@ github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUM github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 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/polarismesh/polaris-go v1.1.0/go.mod h1:tquawfjEKp1W3ffNJQSzhfditjjoZ7tvhOCElN7Efzs= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= @@ -1404,7 +682,6 @@ github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqr github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -1413,13 +690,10 @@ github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= @@ -1427,39 +701,25 @@ github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16 github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.28.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.33.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE= github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/prometheus/prom2json v1.3.1 h1:OogL5hsrJpLPz3jZ4LPz4sJRTtADzViCNRQoqrzUQvk= -github.com/prometheus/prom2json v1.3.1/go.mod h1:A8Oy9aiQx4wrJY9ya1i4nHOySGmkVp5EO0aU1iSJR+g= -github.com/prometheus/prometheus v2.5.0+incompatible h1:7QPitgO2kOFG8ecuRn9O/4L9+10He72rVRJvMXrE9Hg= -github.com/prometheus/prometheus v2.5.0+incompatible/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s= github.com/prometheus/statsd_exporter v0.21.0 h1:hA05Q5RFeIjgwKIYEdFd59xu5Wwaznf33yKI+pyX6T8= github.com/prometheus/statsd_exporter v0.21.0/go.mod h1:rbT83sZq2V+p73lHhPZfMc3MLCHmSHelCh9hSGYNLTQ= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= @@ -1469,135 +729,66 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= -github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= -github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b h1:gQZ0qzfKHQIybLANtM3mBXNUtOfsCFXeTsnBqCsx1KM= github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/shirou/gopsutil v3.20.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil/v3 v3.21.6/go.mod h1:JfVbDpIBLVzT8oKbvMg9P3wEIMDDpVn+LwHTKj0ST88= github.com/shirou/gopsutil/v3 v3.22.2 h1:wCrArWFkHYIdDxx/FSfF5RB4dpJYW6t7rcp3+zL8uks= github.com/shirou/gopsutil/v3 v3.22.2/go.mod h1:WapW1AOOPlHyXr+yOyw3uYx36enocrtSoSBy0L5vUHY= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= -github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= -github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= -github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= -github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= -github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= -github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= -github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= -github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= -github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= -github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= -github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= -github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= -github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= -github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= -github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= -github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= -github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/soheilhy/cmux v0.1.5-0.20210205191134-5ec6847320e5 h1:GJTW+uNMIV1RKwox+T4aN0/sQlYRg78uHZf2H0aBcDw= github.com/soheilhy/cmux v0.1.5-0.20210205191134-5ec6847320e5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= -github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= -github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= -github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= -github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= -github.com/spf13/viper v1.11.0 h1:7OX/1FS6n7jHD1zGrZTM7WtY13ZELRyosK4k93oPr44= -github.com/spf13/viper v1.11.0/go.mod h1:djo0X/bA5+tYVoCn+C7cAYJGcVn/qYLFTG8gdUsX7Zk= -github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc= -github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= -github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -1605,21 +796,12 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= -github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= -github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ= github.com/tevid/gohamcrest v1.1.1/go.mod h1:3UvtWlqm8j5JbwYZh80D/PVBt0mJ1eJiYgZMibh0H/k= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tklauser/go-sysconf v0.3.6/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= @@ -1639,108 +821,49 @@ github.com/uber/jaeger-client-go v2.29.1+incompatible h1:R9ec3zO3sGpzs0abd43Y+fB github.com/uber/jaeger-client-go v2.29.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= -github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= -github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI= -github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= -github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= -github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= -github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netlink v1.2.0-beta h1:CTNzkunO9iTkRaupF540+w47mexyQgNkA/ibnuKc39w= -github.com/vishvananda/netlink v1.2.0-beta/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns= -github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/wasmerio/wasmer-go v1.0.3 h1:9pWIlIqUKxALvFlWK8+Zy90qyqxd+8wlyVG91txh1TU= github.com/wasmerio/wasmer-go v1.0.3/go.mod h1:0gzVdSfg6pysA6QVp6iVRPTagC6Wq9pOE8J86WKb2Fk= -github.com/wasmerio/wasmer-go v1.0.4 h1:MnqHoOGfiQ8MMq2RF6wyCeebKOe84G88h5yv+vmxJgs= -github.com/wasmerio/wasmer-go v1.0.4/go.mod h1:0gzVdSfg6pysA6QVp6iVRPTagC6Wq9pOE8J86WKb2Fk= -github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= -github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= -github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= -github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU= -github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= github.com/zouyx/agollo/v3 v3.4.5 h1:7YCxzY9ZYaH9TuVUBvmI6Tk0mwMggikah+cfbYogcHQ= github.com/zouyx/agollo/v3 v3.4.5/go.mod h1:LJr3kDmm23QSW+F1Ol4TMHDa7HvJvscMdVxJ2IpUTVc= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 h1:VcrIfasaLFkyjk6KNlXQSzO+B0fZcnECiDrKJsfxka0= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489 h1:1JFLBqwIgdyHN1ZtgjTBwO+blA6gVOmZurpiMEsETKo= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.etcd.io/etcd/api/v3 v3.5.0-alpha.0/go.mod h1:mPcW6aZJukV6Aa81LSKpBjQXTWlXB5r74ymPoSWa3Sw= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/api/v3 v3.5.5 h1:BX4JIbQ7hl7+jL+g+2j5UAr0o1bctCm6/Ct+ArBGkf0= go.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/pkg/v3 v3.5.4 h1:lrneYvz923dvC14R54XcA7FXoZ3mlGZAgmwhfm7HqOg= go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/pkg/v3 v3.5.5 h1:9S0JUVvmrVl7wCF39iTQthdaaNIiAaQbmK75ogO6GU8= -go.etcd.io/etcd/client/pkg/v3 v3.5.5/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ= +go.etcd.io/etcd/client/v2 v2.305.0-alpha.0 h1:jZepGpOeJATxsbMNBZczDS2jHdK/QVHM1iPe9jURJ8o= go.etcd.io/etcd/client/v2 v2.305.0-alpha.0/go.mod h1:kdV+xzCJ3luEBSIeQyB/OEKkWKd8Zkux4sbDeANrosU= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= -go.etcd.io/etcd/client/v2 v2.305.5 h1:DktRP60//JJpnPC0VBymAN/7V71GHMdjDCBt4ZPXDjI= -go.etcd.io/etcd/client/v2 v2.305.5/go.mod h1:zQjKllfqfBVyVStbt4FaosoX2iYd8fV/GRy/PbowgP4= go.etcd.io/etcd/client/v3 v3.5.0-alpha.0/go.mod h1:wKt7jgDgf/OfKiYmCq5WFGxOFAkVMLxiiXgLDFhECr8= +go.etcd.io/etcd/client/v3 v3.5.4 h1:p83BUL3tAYS0OT/r0qglgc3M1JjhM0diV8DSWAhVXv4= go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= -go.etcd.io/etcd/client/v3 v3.5.5 h1:q++2WTJbUgpQu4B6hCuT7VkdwaTP7Qz6Daak3WzbrlI= -go.etcd.io/etcd/client/v3 v3.5.5/go.mod h1:aApjR4WGlSumpnJ2kloS75h6aHUmAyaPLjHMxpc7E7c= +go.etcd.io/etcd/pkg/v3 v3.5.0-alpha.0 h1:3yLUEC0nFCxw/RArImOyRUI4OAFbg4PFpBbAhSNzKNY= go.etcd.io/etcd/pkg/v3 v3.5.0-alpha.0/go.mod h1:tV31atvwzcybuqejDoY3oaNRTtlD2l/Ot78Pc9w7DMY= -go.etcd.io/etcd/pkg/v3 v3.5.5 h1:Ablg7T7OkR+AeeeU32kdVhw/AGDsitkKPl7aW73ssjU= -go.etcd.io/etcd/pkg/v3 v3.5.5/go.mod h1:6ksYFxttiUGzC2uxyqiyOEvhAiD0tuIqSZkX3TyPdaE= +go.etcd.io/etcd/raft/v3 v3.5.0-alpha.0 h1:DvYJotxV9q1Lkn7pknzAbFO/CLtCVidCr2K9qRLJ8pA= go.etcd.io/etcd/raft/v3 v3.5.0-alpha.0/go.mod h1:FAwse6Zlm5v4tEWZaTjmNhe17Int4Oxbu7+2r0DiD3w= -go.etcd.io/etcd/raft/v3 v3.5.5 h1:Ibz6XyZ60OYyRopu73lLM/P+qco3YtlZMOhnXNS051I= -go.etcd.io/etcd/raft/v3 v3.5.5/go.mod h1:76TA48q03g1y1VpTue92jZLr9lIHKUNcYdZOOGyx8rI= +go.etcd.io/etcd/server/v3 v3.5.0-alpha.0 h1:fYv7CmmdyuIu27UmKQjS9K/1GtcCa+XnPKqiKBbQkrk= go.etcd.io/etcd/server/v3 v3.5.0-alpha.0/go.mod h1:tsKetYpt980ZTpzl/gb+UOJj9RkIyCb1u4wjzMg90BQ= -go.etcd.io/etcd/server/v3 v3.5.5 h1:jNjYm/9s+f9A9r6+SC4RvNaz6AqixpOvhrFdT0PvIj0= -go.etcd.io/etcd/server/v3 v3.5.5/go.mod h1:rZ95vDw/jrvsbj9XpTqPrTAB9/kzchVdhRirySPkUBc= -go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= -go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -1748,52 +871,34 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/contrib v0.20.0 h1:ubFQUn0VCZ0gPwIoJfBJVpeBlyRMxu8Mm/huKWYd9p0= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.25.0/go.mod h1:E5NNboN0UqSAki0Atn9kVwaN7I+l25gGxDqBueo/74E= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.29.0 h1:n9b7AAdbQtQ0k9dm0Dm2/KUcUqtG8i2O15KzNaDze8c= -go.opentelemetry.io/otel v1.0.1/go.mod h1:OPEOD4jIT2SlZPMmwT6FqZz2C0ZNdQqiWcoK6M0SNFU= go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= go.opentelemetry.io/otel v1.10.0 h1:Y7DTJMR6zs1xkS/upamJYk0SxxN4C9AqRd77jmZnyY4= go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ= go.opentelemetry.io/otel/exporters/jaeger v1.10.0 h1:7W3aVVjEYayu/GOqOVF4mbTvnCuxF1wWu3eRxFGQXvw= go.opentelemetry.io/otel/exporters/jaeger v1.10.0/go.mod h1:n9IGyx0fgyXXZ/i0foLHNxtET9CzXHzZeKCucvRBFgA= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.7.0/go.mod h1:M1hVZHNxcbkAlcvrOMlpQ4YOO3Awf+4N2dxkZL3xm04= go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.10.0 h1:TaB+1rQhddO1sF71MpZOZAuSPW1klK2M8XxfrBMfK7Y= go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.10.0/go.mod h1:78XhIg8Ht9vR4tbLNUhXsiOnE2HOuSeKAiAcoVQEpOY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.1/go.mod h1:Kv8liBeVNFkkkbilbgWRpV+wWuu+H5xdOT6HAgd30iw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0/go.mod h1:ceUgdyfNv4h4gLxHR0WNfDiiVmZFodZhZSbOLhpxqXE= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.10.0 h1:pDDYmo0QadUPal5fwXoY1pmMpFcdyhXOmL5drCrI3vU= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.10.0/go.mod h1:Krqnjl22jUJ0HgMzw5eveuCvFDXY4nSYb4F8t5gdrag= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.1/go.mod h1:xOvWoTOrQjxjW61xtOmD/WKGRYb/P4NzRo3bs65U6Rk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0 h1:MFAyzUPrTwLOwCi+cltN0ZVyy4phU41lwH+lyMyQTS4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0/go.mod h1:E+/KKhwOSw8yoPxSSuUHG6vKppkvhN+S1Jc7Nib3k3o= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.10.0 h1:S8DedULB3gp93Rh+9Z+7NTEv+6Id/KYS7LDyipZ9iCE= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.10.0/go.mod h1:5WV40MLWwvWlGP7Xm8g3pMcg0pKOUY609qxJn8y7LmM= go.opentelemetry.io/otel/exporters/prometheus v0.32.1 h1:1+iSNGGCYoDAMuFDN2M+sYTwa5/wApb7yO/GpW5Vtzg= go.opentelemetry.io/otel/exporters/prometheus v0.32.1/go.mod h1:t1ZclNSxaC2ztzbHxGU71mg3pkkaHyHcMUIK2Yvft0E= go.opentelemetry.io/otel/metric v0.32.1 h1:ftff5LSBCIDwL0UkhBuDg8j9NNxx2IusvJ18q9h6RC4= go.opentelemetry.io/otel/metric v0.32.1/go.mod h1:iLPP7FaKMAD5BIxJ2VX7f2KTuz//0QK2hEUyti5psqQ= -go.opentelemetry.io/otel/sdk v1.0.1/go.mod h1:HrdXne+BiwsOHYYkBE5ysIcv2bvdZstxzmCQhxTcZkI= -go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU= go.opentelemetry.io/otel/sdk v1.10.0 h1:jZ6K7sVn04kk/3DNUdJ4mqRlGDiXAVuIG+MMENpTNdY= go.opentelemetry.io/otel/sdk v1.10.0/go.mod h1:vO06iKzD5baltJz1zarxMCNHFpUlUiOy4s65ECtn6kE= go.opentelemetry.io/otel/sdk/metric v0.32.1 h1:S6AqzulzGQl+sTpYeAoVLw1SJbc2LYuKCMUmfEKG+zM= go.opentelemetry.io/otel/sdk/metric v0.32.1/go.mod h1:Nn+Nt/7cKzm5ISmvLzNO5RLf0Xuv8/Qo8fkpr0JDOzs= -go.opentelemetry.io/otel/trace v1.0.1/go.mod h1:5g4i4fKLaX2BQpSBsxw8YYcgKpMMSW3x7ZTuYBr3sUk= go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= go.opentelemetry.io/otel/trace v1.10.0 h1:npQMbR8o7mum8uF95yFbOEJffhs1sbCOfDh8zAJiH5E= go.opentelemetry.io/otel/trace v1.10.0/go.mod h1:Sij3YYczqAdz+EhmGhE6TpTxUO5/F/AzrK+kxfGqySM= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.opentelemetry.io/proto/otlp v0.16.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.starlark.net v0.0.0-20211013185944-b0039bd2cfe3 h1:oBcONsksxvpeodDrLjiMDaKHXKAVVfAydhe/792CE/o= -go.starlark.net v0.0.0-20211013185944-b0039bd2cfe3/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -1802,15 +907,13 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= @@ -1820,47 +923,23 @@ go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= -go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= golang.org/x/arch v0.0.0-20200826200359-b19915210f00 h1:cfd5G6xu8iZTFmjBYVemyBmE/sTf0A3vpE3BmoOuLCI= golang.org/x/arch v0.0.0-20200826200359-b19915210f00/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= -golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= -golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1878,7 +957,6 @@ golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8H golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 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-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1889,7 +967,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= @@ -1899,24 +976,13 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1924,24 +990,16 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/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.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1951,74 +1009,36 @@ golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= -golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211105192438-b53810dc28af/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211111160137-58aab5ef257a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= 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= @@ -2030,68 +1050,43 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503/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.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2100,131 +1095,64 @@ golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211106132015-ebca88c72f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= -golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/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= @@ -2235,15 +1163,10 @@ golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -2256,7 +1179,6 @@ golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200115044656-831fdb1e1868/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -2278,48 +1200,20 @@ golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20200928182047-19e03678916f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201014170642-d1624618ad65/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= -gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= -gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= -gomodules.xyz/jsonpatch/v3 v3.0.1 h1:Te7hKxV52TKCbNYq3t84tzKav3xhThdvSsSp/W89IyI= -gomodules.xyz/jsonpatch/v3 v3.0.1/go.mod h1:CBhndykehEwTOlEfnsfJwvkFQbSN8YZFr9M+cIHAJto= -gomodules.xyz/orderedmap v0.1.0 h1:fM/+TGh/O1KkqGR5xjTKg6bU8OKBkg7p0Y+x/J9m8Os= -gomodules.xyz/orderedmap v0.1.0/go.mod h1:g9/TPUCm1t2gwD3j3zfV8uylyYhVdCNSi+xCEIu7yTU= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -2337,53 +1231,22 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0 h1:ExR2D+5TYIrMphWgs5JCgwRhEDlPDXXrLwHHMgPHTXE= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= -google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= @@ -2393,7 +1256,6 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -2413,67 +1275,14 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210106152847-07624b53cd92/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210607140030-00d4fb20b1ae/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211104193956-4c6863e31247/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211111162719-482062a4217b/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7 h1:HOL66YCI20JvN2hVk6o2YIp9i/3RvzVUz82PqNr7fXw= google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220405205423-9d709892a2bf/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220706185917-7780775163c4 h1:7YDGQC/0sigNGzsEWyb9s72jTxlFdwVEYNJHbfQ+Dtg= -google.golang.org/genproto v0.0.0-20220706185917-7780775163c4/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -2484,7 +1293,6 @@ google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -2493,32 +1301,18 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -2533,13 +1327,12 @@ google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX7 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.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -2549,24 +1342,13 @@ gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= -gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= -gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= @@ -2579,21 +1361,11 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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.0-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= -grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= -helm.sh/helm/v3 v3.10.3 h1:wL7IUZ7Zyukm5Kz0OUmIFZgKHuAgByCrUcJBtY0kDyw= -helm.sh/helm/v3 v3.10.3/go.mod h1:CXOcs02AYvrlPMWARNYNRgf2rNP7gLJQsi/Ubd4EDrI= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -2602,119 +1374,20 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -istio.io/pkg v0.0.0-20220906212832-f98e656e3df0 h1:OxnCRBQq2VIS/tqVIaNkTqSMGiI9TEsW28nBwmHdB5s= -istio.io/pkg v0.0.0-20220906212832-f98e656e3df0/go.mod h1:kcBYN5TiyGFM2bs4b7K81j+YeDZ4JrINP+brV9ehZe0= -k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= -k8s.io/api v0.18.3/go.mod h1:UOaMwERbqJMfeeeHc8XJKawj4P9TgDRnViIqqBeH2QA= -k8s.io/api v0.18.4/go.mod h1:lOIQAKYgai1+vz9J7YcDZwC26Z0zQewYOGWdyIPUUQ4= -k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= -k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= -k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= -k8s.io/api v0.25.2 h1:v6G8RyFcwf0HR5jQGIAYlvtRNrxMJQG1xJzaSeVnIS8= -k8s.io/api v0.25.2/go.mod h1:qP1Rn4sCVFwx/xIhe+we2cwBLTXNcheRyYXwajonhy0= -k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY= -k8s.io/apiextensions-apiserver v0.18.4/go.mod h1:NYeyeYq4SIpFlPxSAB6jHPIdvu3hL0pc36wuRChybio= -k8s.io/apiextensions-apiserver v0.25.2 h1:8uOQX17RE7XL02ngtnh3TgifY7EhekpK+/piwzQNnBo= -k8s.io/apiextensions-apiserver v0.25.2/go.mod h1:iRwwRDlWPfaHhuBfQ0WMa5skdQfrE18QXJaJvIDLvE8= -k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= -k8s.io/apimachinery v0.18.3/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= -k8s.io/apimachinery v0.18.4/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= -k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= -k8s.io/apimachinery v0.25.2 h1:WbxfAjCx+AeN8Ilp9joWnyJ6xu9OMeS/fsfjK/5zaQs= -k8s.io/apimachinery v0.25.2/go.mod h1:hqqA1X0bsgsxI6dXsJ4HnNTBOmJNxyPp8dw3u2fSHwA= -k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw= -k8s.io/apiserver v0.18.4/go.mod h1:q+zoFct5ABNnYkGIaGQ3bcbUNdmPyOCoEBcg51LChY8= -k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= -k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= -k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= -k8s.io/cli-runtime v0.25.2 h1:XOx+SKRjBpYMLY/J292BHTkmyDffl/qOx3YSuFZkTuc= -k8s.io/cli-runtime v0.25.2/go.mod h1:OQx3+/0st6x5YpkkJQlEWLC73V0wHsOFMC1/roxV8Oc= -k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU= -k8s.io/client-go v0.18.4/go.mod h1:f5sXwL4yAZRkAtzOxRWUhA/N8XzGCb+nPZI8PfobZ9g= -k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= -k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= -k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= -k8s.io/client-go v0.25.2 h1:SUPp9p5CwM0yXGQrwYurw9LWz+YtMwhWd0GqOsSiefo= -k8s.io/client-go v0.25.2/go.mod h1:i7cNU7N+yGQmJkewcRD2+Vuj4iz7b30kI8OcL3horQ4= -k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= -k8s.io/code-generator v0.18.3/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= -k8s.io/code-generator v0.18.4/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= -k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM= -k8s.io/component-base v0.18.4/go.mod h1:7jr/Ef5PGmKwQhyAz/pjByxJbC58mhKAhiaDu0vXfPk= -k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= -k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= -k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= -k8s.io/component-base v0.25.2 h1:Nve/ZyHLUBHz1rqwkjXm/Re6IniNa5k7KgzxZpTfSQY= -k8s.io/component-base v0.25.2/go.mod h1:90W21YMr+Yjg7MX+DohmZLzjsBtaxQDDwaX4YxDkl60= -k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= -k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= -k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= -k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= -k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= -k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= -k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= -k8s.io/kubectl v0.25.2 h1:2993lTeVimxKSWx/7z2PiJxUILygRa3tmC4QhFaeioA= -k8s.io/kubectl v0.25.2/go.mod h1:eoBGJtKUj7x38KXelz+dqVtbtbKwCqyKzJWmBHU0prg= -k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20200603063816-c1c6865ac451/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= -k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= mosn.io/proxy-wasm-go-host v0.1.0 h1:WtXMiOpuDyx3Vruf/BUAiu4bo30rSPx68E5vxEBTT6g= mosn.io/proxy-wasm-go-host v0.1.0/go.mod h1:wBRY9Xjf77GZhvAuEc6nQXZ6dQK39kNU2ok/+Z8mSU8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/controller-runtime v0.6.1/go.mod h1:XRYBPdbf5XJu9kpS84VJiZ7h/u1hF3gEORz0efEja7A= -sigs.k8s.io/controller-runtime v0.11.2 h1:H5GTxQl0Mc9UjRJhORusqfJCIjBO8UtUxGggCwL1rLA= -sigs.k8s.io/controller-runtime v0.11.2/go.mod h1:P6QCzrEjLaZGqHsfd+os7JQ+WFZhvB8MRFsn4dWF7O4= -sigs.k8s.io/controller-tools v0.3.0/go.mod h1:enhtKGfxZD1GFEoMgP8Fdbu+uKQ/cq1/WGJhdVChfvI= -sigs.k8s.io/gateway-api v0.4.1-0.20220419214231-03f50b47814e h1:PQOYjMWqURIPBmIEHw9OQAbtNb1DnwAVwAj/TC9l+24= -sigs.k8s.io/gateway-api v0.4.1-0.20220419214231-03f50b47814e/go.mod h1:Gj2je/oOS/22fEU/U4xJ/nRH0wuQ3/kcfJUmLqtqXV4= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/kind v0.8.1/go.mod h1:oNKTxUVPYkV9lWzY6CVMNluVq8cBsyq+UgPJdvA3uu4= -sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= -sigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCYnkH6S1s= -sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk= -sigs.k8s.io/kustomize/kyaml v0.13.9/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4= -sigs.k8s.io/mcs-api v0.1.0 h1:edDbg0oRGfXw8TmZjKYep06LcJLv/qcYLidejnUp0PM= -sigs.k8s.io/mcs-api v0.1.0/go.mod h1:gGiAryeFNB4GBsq2LBmVqSgKoobLxt+p7ii/WG5QYYw= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= -sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= vimagination.zapto.org/byteio v0.0.0-20200222190125-d27cba0f0b10 h1:pxt6fVJP67Hxo1qk8JalUghLlk3abYByl+3e0JYfUlE= vimagination.zapto.org/byteio v0.0.0-20200222190125-d27cba0f0b10/go.mod h1:fl9OF22g6MTKgvHA1hqMXe/L7+ULWofVTwbC9loGu7A= -vimagination.zapto.org/memio v0.0.0-20221021163155-6d79b4ee428f h1:hMTuz4fLYI1JBfUghAQLAljjgnufLgy4WykP2DMuHOA= -vimagination.zapto.org/memio v0.0.0-20221021163155-6d79b4ee428f/go.mod h1:RdXC+0ctULK7DykQQu6QeRJhQZVbAfYu+vOys0FNk4c= +vimagination.zapto.org/memio v1.0.0 h1:r0GDf430aNuGpOAV57UTvbUzAf82UclRyGG/pBp1uvU= +vimagination.zapto.org/memio v1.0.0/go.mod h1:zHGDKp2tyvF4IAfLti4pKYqCJucXYmmKMb3UMrCHK/4= xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU= xorm.io/builder v0.3.9/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM= diff --git a/istioctl/cmd/add-to-mesh.go b/istioctl/cmd/add-to-mesh.go deleted file mode 100644 index 7288f1457..000000000 --- a/istioctl/cmd/add-to-mesh.go +++ /dev/null @@ -1,688 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "fmt" - "io" - "os" - "strconv" - "strings" -) - -import ( - "github.com/hashicorp/go-multierror" - "github.com/spf13/cobra" - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/api/networking/v1alpha3" - "istio.io/pkg/log" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - k8s_labels "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/kubernetes" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/clioptions" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/handlers" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - istioProtocol "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/inject" - "github.com/apache/dubbo-go-pixiu/pkg/url" -) - -var crdFactory = createDynamicInterface - -// For most common ports allow the protocol to be guessed, this isn't meant -// to replace /etc/services. Fully qualified proto[-extra]:port is the -// recommended usage. -var portsToName = map[int32]string{ - 80: "http", - 443: "https", - 3306: "mysql", - 8080: "http", -} - -// vmServiceOpts contains the options of a mesh expansion service running on VM. -type vmServiceOpts struct { - Name string - Namespace string - ServiceAccount string - IP []string - PortList model.PortList - Labels map[string]string - Annotations map[string]string -} - -func addToMeshCmd() *cobra.Command { - addToMeshCmd := &cobra.Command{ - Use: "add-to-mesh", - Aliases: []string{"add"}, - Short: "Add workloads into Istio service mesh", - Long: `'istioctl experimental add-to-mesh' restarts pods with an Istio sidecar or configures meshed pod access to external services. -Use 'add-to-mesh' as an alternate to namespace-wide auto injection for troubleshooting compatibility. - -The 'remove-from-mesh' command can be used to restart with the sidecar removed.`, - Example: ` # Restart all productpage pods with an Istio sidecar - istioctl experimental add-to-mesh service productpage - - # Restart just pods from the productpage-v1 deployment - istioctl experimental add-to-mesh deployment productpage-v1 - - # Restart just pods from the details-v1 deployment - istioctl x add deployment details-v1 - - # Control how meshed pods see an external service - istioctl experimental add-to-mesh external-service vmhttp 172.12.23.125,172.12.23.126 \ - http:9080 tcp:8888 --labels app=test,version=v1 --annotations env=stage --serviceaccount stageAdmin`, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) != 0 { - return fmt.Errorf("unknown resource type %q", args[0]) - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - cmd.HelpFunc()(cmd, args) - return nil - }, - } - addToMeshCmd.AddCommand(svcMeshifyCmd()) - addToMeshCmd.AddCommand(deploymentMeshifyCmd()) - externalSvcMeshifyCmd := externalSvcMeshifyCmd() - hideInheritedFlags(externalSvcMeshifyCmd, "meshConfigFile", "meshConfigMapName", "injectConfigFile", - "injectConfigMapName", "valuesFile") - addToMeshCmd.AddCommand(externalSvcMeshifyCmd) - addToMeshCmd.PersistentFlags().StringVar(&meshConfigFile, "meshConfigFile", "", - "Mesh configuration filename. Takes precedence over --meshConfigMapName if set") - addToMeshCmd.PersistentFlags().StringVar(&injectConfigFile, "injectConfigFile", "", - "Injection configuration filename. Cannot be used with --injectConfigMapName") - addToMeshCmd.PersistentFlags().StringVar(&valuesFile, "valuesFile", "", - "Injection values configuration filename.") - - addToMeshCmd.PersistentFlags().StringVar(&meshConfigMapName, "meshConfigMapName", defaultMeshConfigMapName, - fmt.Sprintf("ConfigMap name for Istio mesh configuration, key should be %q", configMapKey)) - addToMeshCmd.PersistentFlags().StringVar(&injectConfigMapName, "injectConfigMapName", defaultInjectConfigMapName, - fmt.Sprintf("ConfigMap name for Istio sidecar injection, key should be %q.", injectConfigMapKey)) - - addToMeshCmd.Long += "\n\n" + ExperimentalMsg - return addToMeshCmd -} - -func deploymentMeshifyCmd() *cobra.Command { - var opts clioptions.ControlPlaneOptions - - cmd := &cobra.Command{ - Use: "deployment ", - Aliases: []string{"deploy", "dep"}, - Short: "Add deployment to Istio service mesh", - // nolint: lll - Long: `'istioctl experimental add-to-mesh deployment' restarts pods with the Istio sidecar. Use 'add-to-mesh' -to test deployments for compatibility with Istio. It can be used instead of namespace-wide auto-injection of sidecars and is especially helpful for compatibility testing. - -If your deployment does not function after using 'add-to-mesh' you must re-deploy it and troubleshoot it for Istio compatibility. -See ` + url.DeploymentRequirements + ` - -See also 'istioctl experimental remove-from-mesh deployment' which does the reverse.`, - Example: ` # Restart pods from the productpage-v1 deployment with Istio sidecar - istioctl experimental add-to-mesh deployment productpage-v1 - - # Restart pods from the details-v1 deployment with Istio sidecar - istioctl x add-to-mesh deploy details-v1 - - # Restart pods from the ratings-v1 deployment with Istio sidecar - istioctl x add dep ratings-v1`, - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) != 1 { - return fmt.Errorf("expecting deployment name") - } - client, err := interfaceFactory(kubeconfig) - if err != nil { - return err - } - ns := handlers.HandleNamespace(namespace, defaultNamespace) - writer := cmd.OutOrStdout() - - var valuesConfig string - var sidecarTemplate inject.RawTemplates - meshConfig, err := setupParameters(&sidecarTemplate, &valuesConfig, opts.Revision) - if err != nil { - return err - } - dep, err := client.AppsV1().Deployments(ns).Get(context.TODO(), args[0], metav1.GetOptions{}) - if err != nil { - return fmt.Errorf("deployment %q does not exist", args[0]) - } - return injectSideCarIntoDeployment(client, dep, sidecarTemplate, valuesConfig, - args[0], ns, opts.Revision, meshConfig, writer, func(warning string) { - fmt.Fprintln(cmd.ErrOrStderr(), warning) - }) - }, - } - cmd.Long += "\n\n" + ExperimentalMsg - opts.AttachControlPlaneFlags(cmd) - return cmd -} - -func svcMeshifyCmd() *cobra.Command { - var opts clioptions.ControlPlaneOptions - - cmd := &cobra.Command{ - Use: "service ", - Aliases: []string{"svc"}, - Short: "Add Service to Istio service mesh", - // nolint: lll - Long: `istioctl experimental add-to-mesh service restarts pods with the Istio sidecar. Use 'add-to-mesh' -to test deployments for compatibility with Istio. It can be used instead of namespace-wide auto-injection of sidecars and is especially helpful for compatibility testing. - -If your service does not function after using 'add-to-mesh' you must re-deploy it and troubleshoot it for Istio compatibility. -See ` + url.DeploymentRequirements + ` - -See also 'istioctl experimental remove-from-mesh service' which does the reverse.`, - Example: ` # Restart all productpage pods with an Istio sidecar - istioctl experimental add-to-mesh service productpage - - # Restart all details-v1 pods with an Istio sidecar - istioctl x add-to-mesh svc details-v1 - - # Restart all ratings-v1 pods with an Istio sidecar - istioctl x add svc ratings-v1`, - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) != 1 { - return fmt.Errorf("expecting service name") - } - client, err := interfaceFactory(kubeconfig) - if err != nil { - return err - } - ns := handlers.HandleNamespace(namespace, defaultNamespace) - writer := cmd.OutOrStdout() - - var valuesConfig string - var sidecarTemplate inject.RawTemplates - meshConfig, err := setupParameters(&sidecarTemplate, &valuesConfig, opts.Revision) - if err != nil { - return err - } - matchingDeployments, err := findDeploymentsForSvc(client, ns, args[0]) - if err != nil { - return err - } - if len(matchingDeployments) == 0 { - _, _ = fmt.Fprintf(writer, "No deployments found for service %s.%s\n", args[0], ns) - return nil - } - return injectSideCarIntoDeployments(client, matchingDeployments, sidecarTemplate, valuesConfig, - args[0], ns, opts.Revision, meshConfig, writer, func(warning string) { - fmt.Fprintln(cmd.ErrOrStderr(), warning) - }) - }, - } - cmd.Long += "\n\n" + ExperimentalMsg - opts.AttachControlPlaneFlags(cmd) - return cmd -} - -func injectSideCarIntoDeployments(client kubernetes.Interface, deps []appsv1.Deployment, sidecarTemplate inject.RawTemplates, valuesConfig, - name, namespace string, revision string, meshConfig *meshconfig.MeshConfig, writer io.Writer, warningHandler func(string)) error { - var errs error - for _, dep := range deps { - err := injectSideCarIntoDeployment(client, &dep, sidecarTemplate, valuesConfig, - name, namespace, revision, meshConfig, writer, warningHandler) - if err != nil { - errs = multierror.Append(errs, err) - } - } - return errs -} - -func externalSvcMeshifyCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "external-service [name1:]port1 [[name2:]port2] ...", - Aliases: []string{"es"}, - Short: "Add external service (e.g. services running on a VM) to Istio service mesh", - Long: `istioctl experimental add-to-mesh external-service create a ServiceEntry and -a Service without selector for the specified external service in Istio service mesh. -The typical usage scenario is Mesh Expansion on VMs. - -See also 'istioctl experimental remove-from-mesh external-service' which does the reverse.`, - Example: ` # Control how meshed pods contact 172.12.23.125 and .126 - istioctl experimental add-to-mesh external-service vmhttp 172.12.23.125,172.12.23.126 \ - http:9080 tcp:8888 --labels app=test,version=v1 --annotations env=stage --serviceaccount stageAdmin`, - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) < 3 { - return fmt.Errorf("provide service name, IP and Port List") - } - client, err := interfaceFactory(kubeconfig) - if err != nil { - return err - } - seClient, err := crdFactory(kubeconfig) - if err != nil { - return err - } - writer := cmd.OutOrStdout() - ns := handlers.HandleNamespace(namespace, defaultNamespace) - _, err = client.CoreV1().Services(ns).Get(context.TODO(), args[0], metav1.GetOptions{}) - if err != nil { - return addServiceOnVMToMesh(seClient, client, ns, args, resourceLabels, annotations, svcAcctAnn, writer) - } - return fmt.Errorf("service %q already exists, skip", args[0]) - }, - } - cmd.PersistentFlags().StringSliceVarP(&resourceLabels, "labels", "l", - nil, "List of labels to apply if creating a service/endpoint; e.g. -l env=prod,vers=2") - cmd.PersistentFlags().StringSliceVarP(&annotations, "annotations", "a", - nil, "List of string annotations to apply if creating a service/endpoint; e.g. -a foo=bar,x=y") - cmd.PersistentFlags().StringVarP(&svcAcctAnn, "serviceaccount", "s", - "default", "Service account to link to the service") - - cmd.Long += "\n\n" + ExperimentalMsg - return cmd -} - -func setupParameters(sidecarTemplate *inject.RawTemplates, valuesConfig *string, revision string) (*meshconfig.MeshConfig, error) { - var meshConfig *meshconfig.MeshConfig - var err error - injectConfigMapName = defaultInjectWebhookConfigName - if meshConfigFile != "" { - if meshConfig, err = mesh.ReadMeshConfig(meshConfigFile); err != nil { - return nil, err - } - } else { - if meshConfig, err = getMeshConfigFromConfigMap(kubeconfig, "add-to-mesh", revision); err != nil { - return nil, err - } - } - if injectConfigFile != "" { - injectionConfig, err := os.ReadFile(injectConfigFile) // nolint: vetshadow - if err != nil { - return nil, err - } - injectConfig, err := readInjectConfigFile(injectionConfig) - if err != nil { - return nil, multierror.Append(err, fmt.Errorf("loading --injectConfigFile")) - } - *sidecarTemplate = injectConfig - } else if *sidecarTemplate, err = getInjectConfigFromConfigMap(kubeconfig, revision); err != nil { - return nil, err - } - if valuesFile != "" { - valuesConfigBytes, err := os.ReadFile(valuesFile) // nolint: vetshadow - if err != nil { - return nil, err - } - *valuesConfig = string(valuesConfigBytes) - } else if *valuesConfig, err = getValuesFromConfigMap(kubeconfig, revision); err != nil { - return nil, err - } - return meshConfig, err -} - -func injectSideCarIntoDeployment(client kubernetes.Interface, dep *appsv1.Deployment, sidecarTemplate inject.RawTemplates, valuesConfig, - svcName, svcNamespace string, revision string, meshConfig *meshconfig.MeshConfig, writer io.Writer, warningHandler func(string)) error { - var errs error - log.Debugf("updating deployment %s.%s with Istio sidecar injected", - dep.Name, dep.Namespace) - templs, err := inject.ParseTemplates(sidecarTemplate) - if err != nil { - return err - } - vc, err := inject.NewValuesConfig(valuesConfig) - if err != nil { - return err - } - newDep, err := inject.IntoObject(nil, templs, vc, revision, meshConfig, dep, warningHandler) - if err != nil { - errs = multierror.Append(errs, fmt.Errorf("failed to inject sidecar to deployment resource %s.%s for service %s.%s due to %v", - dep.Name, dep.Namespace, svcName, svcNamespace, err)) - return errs - } - res, b := newDep.(*appsv1.Deployment) - if !b { - errs = multierror.Append(errs, fmt.Errorf("failed to create new deployment resource %s.%s for service %s.%s due to %v", - dep.Name, dep.Namespace, svcName, svcNamespace, err)) - return errs - } - if _, err = client.AppsV1().Deployments(svcNamespace).Update(context.TODO(), res, metav1.UpdateOptions{}); err != nil { - errs = multierror.Append(errs, fmt.Errorf("failed to update deployment %s.%s for service %s.%s due to %v", - dep.Name, dep.Namespace, svcName, svcNamespace, err)) - return errs - } - d := &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: dep.Name, - Namespace: dep.Namespace, - UID: dep.UID, - OwnerReferences: dep.OwnerReferences, - }, - } - if _, err = client.AppsV1().Deployments(svcNamespace).UpdateStatus(context.TODO(), d, metav1.UpdateOptions{}); err != nil { - errs = multierror.Append(errs, fmt.Errorf("failed to update deployment status %s.%s for service %s.%s due to %v", - dep.Name, dep.Namespace, svcName, svcNamespace, err)) - return errs - } - _, _ = fmt.Fprintf(writer, "deployment %s.%s updated successfully with Istio sidecar injected.\n"+ - "Next Step: Add related labels to the deployment to align with Istio's requirement: %s\n", - dep.Name, dep.Namespace, url.DeploymentRequirements) - return errs -} - -func findDeploymentsForSvc(client kubernetes.Interface, ns, name string) ([]appsv1.Deployment, error) { - svc, err := client.CoreV1().Services(ns).Get(context.TODO(), name, metav1.GetOptions{}) - if err != nil { - return nil, err - } - svcSelector := k8s_labels.SelectorFromSet(svc.Spec.Selector) - if svcSelector.Empty() { - return nil, nil - } - deployments, err := client.AppsV1().Deployments(ns).List(context.TODO(), metav1.ListOptions{}) - if err != nil { - return nil, err - } - deps := make([]appsv1.Deployment, 0, len(deployments.Items)) - for _, dep := range deployments.Items { - depLabels := k8s_labels.Set(dep.Spec.Selector.MatchLabels) - if svcSelector.Matches(depLabels) { - deps = append(deps, dep) - } - } - return deps, nil -} - -func createDynamicInterface(kubeconfig string) (dynamic.Interface, error) { - restConfig, err := kube.BuildClientConfig(kubeconfig, configContext) - if err != nil { - return nil, err - } - dynamicClient, err := dynamic.NewForConfig(restConfig) - if err != nil { - return nil, err - } - return dynamicClient, nil -} - -func convertPortList(ports []string) (model.PortList, error) { - portList := model.PortList{} - for _, p := range ports { - np, err := str2NamedPort(p) - if err != nil { - return nil, fmt.Errorf("invalid port format %v", p) - } - protocol := istioProtocol.Parse(np.name) - if protocol == istioProtocol.Unsupported { - return nil, fmt.Errorf("protocol %s is not supported by Istio", np.name) - } - portList = append(portList, &model.Port{ - Port: int(np.port), - Protocol: protocol, - Name: np.name + "-" + strconv.Itoa(int(np.port)), - }) - } - return portList, nil -} - -// namedPort defines the Port and Name tuple needed for services and endpoints. -type namedPort struct { - port int32 - name string -} - -// str2NamedPort parses a proto:port string into a namePort struct. -func str2NamedPort(str string) (namedPort, error) { - var r namedPort - idx := strings.Index(str, ":") - if idx >= 0 { - r.name = str[:idx] - str = str[idx+1:] - } - p, err := strconv.Atoi(str) - if err != nil { - return r, err - } - r.port = int32(p) - if len(r.name) == 0 { - name, found := portsToName[r.port] - r.name = name - if !found { - r.name = str - } - } - return r, nil -} - -// addServiceOnVMToMesh adds a service running on VM into Istio service mesh -func addServiceOnVMToMesh(dynamicClient dynamic.Interface, client kubernetes.Interface, ns string, - args, l, a []string, svcAcctAnn string, writer io.Writer) error { - svcName := args[0] - ips := strings.Split(args[1], ",") - portsListStr := args[2:] - ports, err := convertPortList(portsListStr) - if err != nil { - return err - } - labels := convertToStringMap(l) - annotations := convertToStringMap(a) - opts := &vmServiceOpts{ - Name: svcName, - Namespace: ns, - PortList: ports, - IP: ips, - ServiceAccount: svcAcctAnn, - Labels: labels, - Annotations: annotations, - } - - u := &unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": collections.IstioNetworkingV1Alpha3Serviceentries.Resource().APIVersion(), - "kind": collections.IstioNetworkingV1Alpha3Serviceentries.Resource().Kind(), - "metadata": map[string]interface{}{ - "namespace": opts.Namespace, - "name": resourceName(opts.Name), - }, - }, - } - annotations[corev1.ServiceAccountNameKey] = opts.ServiceAccount - s := &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: opts.Name, - Namespace: opts.Namespace, - Annotations: annotations, - Labels: labels, - }, - } - - // Pre-check Kubernetes service and service entry does not exist. - _, err = client.CoreV1().Services(ns).Get(context.TODO(), opts.Name, metav1.GetOptions{}) - if err == nil { - return fmt.Errorf("service %q already exists, skip", opts.Name) - } - serviceEntryGVR := schema.GroupVersionResource{ - Group: collections.IstioNetworkingV1Alpha3Serviceentries.Resource().Group(), - Version: collections.IstioNetworkingV1Alpha3Serviceentries.Resource().Version(), - Resource: collections.IstioNetworkingV1Alpha3Serviceentries.Resource().Plural(), - } - _, err = dynamicClient.Resource(serviceEntryGVR).Namespace(ns).Get(context.TODO(), resourceName(opts.Name), metav1.GetOptions{}) - if err == nil { - return fmt.Errorf("service entry %q already exists, skip", resourceName(opts.Name)) - } - - if err = generateServiceEntry(u, opts); err != nil { - return err - } - generateK8sService(s, opts) - if err = createServiceEntry(dynamicClient, ns, u, opts.Name, writer); err != nil { - return err - } - return createK8sService(client, ns, s, writer) -} - -func generateServiceEntry(u *unstructured.Unstructured, o *vmServiceOpts) error { - if o == nil { - return fmt.Errorf("empty vm service options") - } - ports := make([]*v1alpha3.Port, 0, len(o.PortList)) - for _, p := range o.PortList { - ports = append(ports, &v1alpha3.Port{ - Number: uint32(p.Port), - Protocol: string(p.Protocol), - Name: p.Name, - }) - } - eps := make([]*v1alpha3.WorkloadEntry, 0, len(o.IP)) - for _, ip := range o.IP { - eps = append(eps, &v1alpha3.WorkloadEntry{ - Address: ip, - Labels: o.Labels, - }) - } - host := fmt.Sprintf("%v.%v.svc.%s", o.Name, o.Namespace, constants.DefaultKubernetesDomain) - spec := &v1alpha3.ServiceEntry{ - Hosts: []string{host}, - Ports: ports, - Endpoints: eps, - Resolution: v1alpha3.ServiceEntry_STATIC, - Location: v1alpha3.ServiceEntry_MESH_INTERNAL, - } - - iSpec, err := unstructureIstioType(spec) - if err != nil { - return err - } - u.Object["spec"] = iSpec - - return nil -} - -// Because we are placing into an Unstructured, place as a map instead -// of structured Istio types. (The go-client can handle the structured data, but the -// fake go-client used for mocking cannot.) -func unstructureIstioType(spec interface{}) (map[string]interface{}, error) { - b, err := yaml.Marshal(spec) - if err != nil { - return nil, err - } - iSpec := map[string]interface{}{} - err = yaml.Unmarshal(b, &iSpec) - if err != nil { - return nil, err - } - return iSpec, nil -} - -func resourceName(hostShortName string) string { - return fmt.Sprintf("mesh-expansion-%v", hostShortName) -} - -func generateK8sService(s *corev1.Service, o *vmServiceOpts) { - ports := make([]corev1.ServicePort, 0, len(o.PortList)) - for _, p := range o.PortList { - ports = append(ports, corev1.ServicePort{ - Name: strings.ToLower(p.Name), - Port: int32(p.Port), - }) - } - - spec := corev1.ServiceSpec{ - Ports: ports, - } - s.Spec = spec -} - -func convertToUnsignedInt32Map(s []string) map[string]uint32 { - out := make(map[string]uint32, len(s)) - for _, l := range s { - k, v := splitEqual(l) - u64, err := strconv.ParseUint(v, 10, 32) - if err != nil { - log.Errorf("failed to convert to uint32: %v", err) - } - out[k] = uint32(u64) - } - return out -} - -func convertToStringMap(s []string) map[string]string { - out := make(map[string]string, len(s)) - for _, l := range s { - k, v := splitEqual(l) - out[k] = v - } - return out -} - -// splitEqual splits key=value string into key,value. if no = is found -// the whole string is the key and value is empty. -func splitEqual(str string) (string, string) { - idx := strings.Index(str, "=") - var k string - var v string - if idx >= 0 { - k = str[:idx] - v = str[idx+1:] - } else { - k = str - } - return k, v -} - -// createK8sService creates k8s service object for external services in order for DNS query and cluster VIP. -func createK8sService(client kubernetes.Interface, ns string, svc *corev1.Service, writer io.Writer) error { - if svc == nil { - return fmt.Errorf("failed to create vm service") - } - if _, err := client.CoreV1().Services(ns).Create(context.TODO(), svc, metav1.CreateOptions{}); err != nil { - return fmt.Errorf("failed to create kubernetes service %v", err) - } - if _, err := client.CoreV1().Services(ns).UpdateStatus(context.TODO(), svc, metav1.UpdateOptions{}); err != nil { - return fmt.Errorf("failed to create kubernetes service %v", err) - } - sName := strings.Join([]string{svc.Name, svc.Namespace}, ".") - _, _ = fmt.Fprintf(writer, "Kubernetes Service %q has been created in the Istio service mesh"+ - " for the external service %q\n", sName, svc.Name) - return nil -} - -// createServiceEntry creates an Istio ServiceEntry object in order to register vm service. -func createServiceEntry(dynamicClient dynamic.Interface, ns string, - u *unstructured.Unstructured, name string, writer io.Writer) error { - if u == nil { - return fmt.Errorf("failed to create vm service") - } - serviceEntryGVR := schema.GroupVersionResource{ - Group: collections.IstioNetworkingV1Alpha3Serviceentries.Resource().Group(), - Version: collections.IstioNetworkingV1Alpha3Serviceentries.Resource().Version(), - Resource: collections.IstioNetworkingV1Alpha3Serviceentries.Resource().Plural(), - } - _, err := dynamicClient.Resource(serviceEntryGVR).Namespace(ns).Create(context.TODO(), u, metav1.CreateOptions{}) - if err != nil { - return fmt.Errorf("failed to create service entry %v", err) - } - seName := strings.Join([]string{u.GetName(), u.GetNamespace()}, ".") - _, _ = fmt.Fprintf(writer, "ServiceEntry %q has been created in the Istio service mesh"+ - " for the external service %q\n", seName, name) - return nil -} diff --git a/istioctl/cmd/add-to-mesh_test.go b/istioctl/cmd/add-to-mesh_test.go deleted file mode 100644 index c04f86e80..000000000 --- a/istioctl/cmd/add-to-mesh_test.go +++ /dev/null @@ -1,399 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "bytes" - "fmt" - "reflect" - "strings" - "testing" -) - -import ( - appsv1 "k8s.io/api/apps/v1" - coreV1 "k8s.io/api/core/v1" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/dynamic/fake" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" - "github.com/apache/dubbo-go-pixiu/pkg/url" -) - -type testcase struct { - description string - expectedException bool - args []string - k8sConfigs []runtime.Object - dynamicConfigs []runtime.Object - expectedOutput string - namespace string -} - -var ( - one = int32(1) - cannedK8sConfigs = []runtime.Object{ - &coreV1.ConfigMapList{Items: []coreV1.ConfigMap{}}, - - &appsv1.DeploymentList{Items: []appsv1.Deployment{ - { - ObjectMeta: metaV1.ObjectMeta{ - Name: "details-v1", - Namespace: "default", - Labels: map[string]string{ - "app": "details", - }, - }, - Spec: appsv1.DeploymentSpec{ - Replicas: &one, - Selector: &metaV1.LabelSelector{ - MatchLabels: map[string]string{"app": "details"}, - }, - Template: coreV1.PodTemplateSpec{ - ObjectMeta: metaV1.ObjectMeta{ - Labels: map[string]string{"app": "details"}, - }, - Spec: coreV1.PodSpec{ - Containers: []coreV1.Container{ - {Name: "details", Image: "docker.io/istio/examples-bookinfo-details-v1:1.15.0"}, - }, - }, - }, - }, - }, - }}, - &coreV1.ServiceList{Items: []coreV1.Service{ - { - ObjectMeta: metaV1.ObjectMeta{ - Name: "details", - Namespace: "default", - }, - Spec: coreV1.ServiceSpec{ - Ports: []coreV1.ServicePort{ - { - Port: 9080, - Name: "http", - }, - }, - Selector: map[string]string{"app": "details"}, - }, - }, - { - ObjectMeta: metaV1.ObjectMeta{ - Name: "dummyservice", - Namespace: "default", - }, - Spec: coreV1.ServiceSpec{ - Ports: []coreV1.ServicePort{ - { - Port: 9080, - Name: "http", - }, - }, - Selector: map[string]string{"app": "dummy"}, - }, - }, - }}, - } - cannedDynamicConfigs = []runtime.Object{ - &unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": collections.IstioNetworkingV1Alpha3Serviceentries.Resource().APIVersion(), - "kind": collections.IstioNetworkingV1Alpha3Serviceentries.Resource().Kind(), - "metadata": map[string]interface{}{ - "namespace": "default", - "name": "mesh-expansion-vmtest", - }, - }, - }, - } -) - -func TestAddToMesh(t *testing.T) { - cases := []testcase{ - { - description: "Invalid command args - missing service name", - args: strings.Split("experimental add-to-mesh service", " "), - expectedException: true, - expectedOutput: "Error: expecting service name\n", - }, - { - description: "Invalid command args - missing deployment name", - args: strings.Split("experimental add-to-mesh deployment", " "), - expectedException: true, - expectedOutput: "Error: expecting deployment name\n", - }, - { - description: "valid case - add service into mesh", - args: strings.Split("experimental add-to-mesh service details --meshConfigFile testdata/mesh-config.yaml"+ - " --injectConfigFile testdata/inject-config.yaml"+ - " --valuesFile testdata/inject-values.yaml", " "), - expectedException: false, - k8sConfigs: cannedK8sConfigs, - expectedOutput: "deployment details-v1.default updated successfully with Istio sidecar injected.\n" + - "Next Step: Add related labels to the deployment to align with Istio's requirement: " + url.DeploymentRequirements + "\n", - namespace: "default", - }, - { - description: "valid case - add deployment into mesh", - args: strings.Split("experimental add-to-mesh deployment details-v1 --meshConfigFile testdata/mesh-config.yaml"+ - " --injectConfigFile testdata/inject-config.yaml"+ - " --valuesFile testdata/inject-values.yaml", " "), - expectedException: false, - k8sConfigs: cannedK8sConfigs, - expectedOutput: "deployment details-v1.default updated successfully with Istio sidecar injected.\n" + - "Next Step: Add related labels to the deployment to align with Istio's requirement: " + url.DeploymentRequirements + "\n", - namespace: "default", - }, - { - description: "service does not exist", - args: strings.Split("experimental add-to-mesh service test --meshConfigFile testdata/mesh-config.yaml"+ - " --injectConfigFile testdata/inject-config.yaml"+ - " --valuesFile testdata/inject-values.yaml", " "), - expectedException: true, - k8sConfigs: cannedK8sConfigs, - expectedOutput: "Error: services \"test\" not found\n", - }, - { - description: "deployment does not exist", - args: strings.Split("experimental add-to-mesh deployment test --meshConfigFile testdata/mesh-config.yaml"+ - " --injectConfigFile testdata/inject-config.yaml"+ - " --valuesFile testdata/inject-values.yaml", " "), - expectedException: true, - k8sConfigs: cannedK8sConfigs, - expectedOutput: "Error: deployment \"test\" does not exist\n", - }, - { - description: "service does not exist (with short syntax)", - args: strings.Split("x add svc test --meshConfigFile testdata/mesh-config.yaml"+ - " --injectConfigFile testdata/inject-config.yaml"+ - " --valuesFile testdata/inject-values.yaml", " "), - expectedException: true, - k8sConfigs: cannedK8sConfigs, - expectedOutput: "Error: services \"test\" not found\n", - }, - { - description: "deployment does not exist (with short syntax)", - args: strings.Split("x add deploy test --meshConfigFile testdata/mesh-config.yaml"+ - " --injectConfigFile testdata/inject-config.yaml"+ - " --valuesFile testdata/inject-values.yaml", " "), - expectedException: true, - k8sConfigs: cannedK8sConfigs, - expectedOutput: "Error: deployment \"test\" does not exist\n", - }, - { - description: "service without deployment", - args: strings.Split("experimental add-to-mesh service dummyservice --meshConfigFile testdata/mesh-config.yaml"+ - " --injectConfigFile testdata/inject-config.yaml"+ - " --valuesFile testdata/inject-values.yaml", " "), - namespace: "default", - expectedException: false, - k8sConfigs: cannedK8sConfigs, - expectedOutput: "No deployments found for service dummyservice.default\n", - }, - { - description: "Invalid command args - missing service name", - args: strings.Split("experimental add-to-mesh service", " "), - expectedException: true, - expectedOutput: "Error: expecting service name\n", - }, - { - description: "Invalid command args - missing service IP", - args: strings.Split("experimental add-to-mesh external-service test tcp:12345", " "), - expectedException: true, - expectedOutput: "Error: provide service name, IP and Port List\n", - }, - { - description: "Invalid command args - missing service Ports", - args: strings.Split("experimental add-to-mesh external-service test 172.186.15.123", " "), - expectedException: true, - expectedOutput: "Error: provide service name, IP and Port List\n", - }, - { - description: "Invalid command args - invalid port protocol", - args: strings.Split("experimental add-to-mesh external-service test 172.186.15.123 tcp1:12345", " "), - expectedException: true, - expectedOutput: "Error: protocol tcp1 is not supported by Istio\n", - }, - { - description: "service already exists", - args: strings.Split("experimental add-to-mesh external-service dummyservice 11.11.11.11 tcp:12345", " "), - expectedException: true, - k8sConfigs: cannedK8sConfigs, - dynamicConfigs: cannedDynamicConfigs, - namespace: "default", - expectedOutput: "Error: service \"dummyservice\" already exists, skip\n", - }, - { - description: "service already exists (with short syntax)", - args: strings.Split("x add es dummyservice 11.11.11.11 tcp:12345", " "), - expectedException: true, - k8sConfigs: cannedK8sConfigs, - dynamicConfigs: cannedDynamicConfigs, - namespace: "default", - expectedOutput: "Error: service \"dummyservice\" already exists, skip\n", - }, - { - description: "ServiceEntry already exists", - args: strings.Split("experimental add-to-mesh external-service vmtest 11.11.11.11 tcp:12345", " "), - expectedException: true, - k8sConfigs: cannedK8sConfigs, - dynamicConfigs: cannedDynamicConfigs, - namespace: "default", - expectedOutput: "Error: service entry \"mesh-expansion-vmtest\" already exists, skip\n", - }, - { - description: "external service banana namespace", - args: strings.Split("experimental add-to-mesh external-service vmtest 11.11.11.11 tcp:12345 tcp:12346", " "), - k8sConfigs: cannedK8sConfigs, - dynamicConfigs: cannedDynamicConfigs, - namespace: "banana", - expectedOutput: `ServiceEntry "mesh-expansion-vmtest.banana" has been created in the Istio service mesh for the external service "vmtest" -Kubernetes Service "vmtest.banana" has been created in the Istio service mesh for the external service "vmtest" -`, - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("case %d %s", i, c.description), func(t *testing.T) { - verifyAddToMeshOutput(t, c) - }) - } -} - -func verifyAddToMeshOutput(t *testing.T, c testcase) { - t.Helper() - - interfaceFactory = mockInterfaceFactoryGenerator(c.k8sConfigs) - crdFactory = mockDynamicClientGenerator(c.dynamicConfigs) - var out bytes.Buffer - rootCmd := GetRootCmd(c.args) - rootCmd.SetOut(&out) - rootCmd.SetErr(&out) - if c.namespace != "" { - namespace = c.namespace - } - - fErr := rootCmd.Execute() - output := out.String() - - if c.expectedException { - if fErr == nil { - t.Fatalf("Wanted an exception, "+ - "didn't get one, output was %q", output) - } - } else { - if fErr != nil { - t.Fatalf("Unwanted exception: %v", fErr) - } - } - - if c.expectedOutput != "" && c.expectedOutput != output { - assert.Equal(t, c.expectedOutput, output) - t.Fatalf("Unexpected output for 'istioctl %s'\n got: %q\nwant: %q", strings.Join(c.args, " "), output, c.expectedOutput) - } -} - -func mockDynamicClientGenerator(dynamicConfigs []runtime.Object) func(kubeconfig string) (dynamic.Interface, error) { - outFactory := func(_ string) (dynamic.Interface, error) { - types := runtime.NewScheme() - client := fake.NewSimpleDynamicClient(types, dynamicConfigs...) - return client, nil - } - return outFactory -} - -func TestSplitEqual(t *testing.T) { - tests := []struct { - arg string - wantKey string - wantValue string - }{ - {arg: "key=value", wantKey: "key", wantValue: "value"}, - {arg: "key==value", wantKey: "key", wantValue: "=value"}, - {arg: "key=", wantKey: "key", wantValue: ""}, - {arg: "key", wantKey: "key", wantValue: ""}, - {arg: "", wantKey: "", wantValue: ""}, - } - for _, tt := range tests { - t.Run(tt.arg, func(t *testing.T) { - gotKey, gotValue := splitEqual(tt.arg) - if gotKey != tt.wantKey { - t.Errorf("splitEqual(%v) got = %v, want %v", tt.arg, gotKey, tt.wantKey) - } - if gotValue != tt.wantValue { - t.Errorf("splitEqual(%v) got1 = %v, want %v", tt.arg, gotValue, tt.wantValue) - } - }) - } -} - -func TestConvertToMap(t *testing.T) { - tests := []struct { - name string - arg []string - want map[string]string - }{ - {name: "empty", arg: []string{""}, want: map[string]string{"": ""}}, - {name: "one-valid", arg: []string{"key=value"}, want: map[string]string{"key": "value"}}, - {name: "one-valid-double-equals", arg: []string{"key==value"}, want: map[string]string{"key": "=value"}}, - {name: "one-key-only", arg: []string{"key"}, want: map[string]string{"key": ""}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := convertToStringMap(tt.arg); !reflect.DeepEqual(got, tt.want) { - t.Errorf("convertToStringMap() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestStr2NamedPort(t *testing.T) { - tests := []struct { - input string // input - expVal namedPort // output - expErr bool // error - }{ - // Good cases: - {"http:5555", namedPort{5555, "http"}, false}, - {"80", namedPort{80, "http"}, false}, - {"443", namedPort{443, "https"}, false}, - {"1234", namedPort{1234, "1234"}, false}, - // Error cases: - {"", namedPort{0, ""}, true}, - {"foo:bar", namedPort{0, "foo"}, true}, - } - for _, tst := range tests { - actVal, actErr := str2NamedPort(tst.input) - if tst.expVal != actVal { - t.Errorf("Got '%+v', expecting '%+v' for Str2NamedPort('%s')", actVal, tst.expVal, tst.input) - } - if tst.expErr { - if actErr == nil { - t.Errorf("Got no error when expecting an error for Str2NamedPort('%s')", tst.input) - } - } else { - if actErr != nil { - t.Errorf("Got unexpected error '%+v' when expecting none for Str2NamedPort('%s')", actErr, tst.input) - } - } - } -} diff --git a/istioctl/cmd/admin.go b/istioctl/cmd/admin.go deleted file mode 100644 index 4d219398c..000000000 --- a/istioctl/cmd/admin.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "fmt" -) - -import ( - "github.com/spf13/cobra" -) - -func adminCmd() *cobra.Command { - adminCmd := &cobra.Command{ - Use: "admin", - Short: "Manage control plane (istiod) configuration", - Long: "A group of commands used to manage istiod configuration", - Example: ` # Retrieve information about istiod configuration. - istioctl admin log`, - Aliases: []string{"istiod"}, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) != 0 { - return fmt.Errorf("unknown subcommand %q", args[0]) - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - cmd.HelpFunc()(cmd, args) - return nil - }, - } - - istiodLog := istiodLogCmd() - adminCmd.AddCommand(istiodLog) - adminCmd.PersistentFlags().StringVarP(&istiodLabelSelector, "selector", "l", "app=istiod", "label selector") - - return adminCmd -} diff --git a/istioctl/cmd/analyze.go b/istioctl/cmd/analyze.go deleted file mode 100644 index 01aba741b..000000000 --- a/istioctl/cmd/analyze.go +++ /dev/null @@ -1,463 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "fmt" - "os" - "path/filepath" - "runtime" - "sort" - "strings" - "time" -) - -import ( - "github.com/mattn/go-isatty" - "github.com/spf13/cobra" - "k8s.io/apimachinery/pkg/api/errors" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/formatting" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/handlers" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/diag" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/local" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/url" -) - -// AnalyzerFoundIssuesError indicates that at least one analyzer found problems. -type AnalyzerFoundIssuesError struct{} - -// FileParseError indicates a provided file was unable to be parsed. -type FileParseError struct{} - -const FileParseString = "Some files couldn't be parsed." - -func (f AnalyzerFoundIssuesError) Error() string { - var sb strings.Builder - sb.WriteString(fmt.Sprintf("Analyzers found issues when analyzing %s.\n", analyzeTargetAsString())) - sb.WriteString(fmt.Sprintf("See %s for more information about causes and resolutions.", url.ConfigAnalysis)) - return sb.String() -} - -func (f FileParseError) Error() string { - return FileParseString -} - -var ( - listAnalyzers bool - useKube bool - failureThreshold = formatting.MessageThreshold{Level: diag.Error} // messages at least this level will generate an error exit code - outputThreshold = formatting.MessageThreshold{Level: diag.Info} // messages at least this level will be included in the output - colorize bool - msgOutputFormat string - meshCfgFile string - selectedNamespace string - allNamespaces bool - suppress []string - analysisTimeout time.Duration - recursive bool - ignoreUnknown bool - - fileExtensions = []string{".json", ".yaml", ".yml"} -) - -// Analyze command -func Analyze() *cobra.Command { - analysisCmd := &cobra.Command{ - Use: "analyze ...", - Short: "Analyze Istio configuration and print validation messages", - Example: ` # Analyze the current live cluster - istioctl analyze - - # Analyze the current live cluster, simulating the effect of applying additional yaml files - istioctl analyze a.yaml b.yaml my-app-config/ - - # Analyze the current live cluster, simulating the effect of applying a directory of config recursively - istioctl analyze --recursive my-istio-config/ - - # Analyze yaml files without connecting to a live cluster - istioctl analyze --use-kube=false a.yaml b.yaml my-app-config/ - - # Analyze the current live cluster and suppress PodMissingProxy for pod mypod in namespace 'testing'. - istioctl analyze -S "IST0103=Pod mypod.testing" - - # Analyze the current live cluster and suppress PodMissingProxy for all pods in namespace 'testing', - # and suppress MisplacedAnnotation on deployment foobar in namespace default. - istioctl analyze -S "IST0103=Pod *.testing" -S "IST0107=Deployment foobar.default" - - # List available analyzers - istioctl analyze -L`, - RunE: func(cmd *cobra.Command, args []string) error { - msgOutputFormat = strings.ToLower(msgOutputFormat) - _, ok := formatting.MsgOutputFormats[msgOutputFormat] - if !ok { - return CommandParseError{ - fmt.Errorf("%s not a valid option for format. See istioctl analyze --help", msgOutputFormat), - } - } - - if listAnalyzers { - fmt.Print(AnalyzersAsString(analyzers.All())) - return nil - } - - readers, err := gatherFiles(cmd, args) - if err != nil { - return err - } - cancel := make(chan struct{}) - - // We use the "namespace" arg that's provided as part of root istioctl as a flag for specifying what namespace to use - // for file resources that don't have one specified. - selectedNamespace = handlers.HandleNamespace(namespace, defaultNamespace) - - // check whether selected namespace exists. - if namespace != "" && useKube { - client, err := kube.NewExtendedClient(kube.BuildClientCmd(kubeconfig, configContext), "") - if err != nil { - return err - } - _, err = client.CoreV1().Namespaces().Get(context.TODO(), namespace, v1.GetOptions{}) - if errors.IsNotFound(err) { - fmt.Fprintf(cmd.ErrOrStderr(), "namespace %q not found\n", namespace) - return nil - } - } - - // If we've explicitly asked for all namespaces, blank the selectedNamespace var out - if allNamespaces { - selectedNamespace = "" - } - - sa := local.NewIstiodAnalyzer(analyzers.AllCombined(), - resource.Namespace(selectedNamespace), - resource.Namespace(istioNamespace), nil, true) - - // Check for suppressions and add them to our SourceAnalyzer - suppressions := make([]local.AnalysisSuppression, 0, len(suppress)) - for _, s := range suppress { - parts := strings.Split(s, "=") - if len(parts) != 2 { - return fmt.Errorf("%s is not a valid suppression value. See istioctl analyze --help", s) - } - // Check to see if the supplied code is valid. If not, emit a - // warning but continue. - codeIsValid := false - for _, at := range msg.All() { - if at.Code() == parts[0] { - codeIsValid = true - break - } - } - - if !codeIsValid { - fmt.Fprintf(cmd.ErrOrStderr(), "Warning: Supplied message code '%s' is an unknown message code and will not have any effect.\n", parts[0]) - } - suppressions = append(suppressions, local.AnalysisSuppression{ - Code: parts[0], - ResourceName: parts[1], - }) - } - sa.SetSuppressions(suppressions) - - // If we're using kube, use that as a base source. - if useKube { - // Set up the kube client - restConfig, err := kube.DefaultRestConfig(kubeconfig, configContext) - if err != nil { - return err - } - k, err := kube.NewClient(kube.NewClientConfigForRestConfig(restConfig)) - if err != nil { - return err - } - sa.AddRunningKubeSource(k) - } - - // If we explicitly specify mesh config, use it. - // This takes precedence over default mesh config or mesh config from a running Kube instance. - if meshCfgFile != "" { - _ = sa.AddFileKubeMeshConfig(meshCfgFile) - } - - // If we're not using kube (files only), add defaults for some resources we expect to be provided by Istio - if !useKube { - err := sa.AddDefaultResources() - if err != nil { - return err - } - } - - // If files are provided, treat them (collectively) as a source. - parseErrors := 0 - if len(readers) > 0 { - if err = sa.AddReaderKubeSource(readers); err != nil { - fmt.Fprintf(cmd.ErrOrStderr(), "Error(s) adding files: %v", err) - parseErrors++ - } - } - - // Do the analysis - result, err := sa.Analyze(cancel) - if err != nil { - return err - } - - // Maybe output details about which analyzers ran - if verbose { - fmt.Fprintf(cmd.ErrOrStderr(), "Analyzed resources in %s\n", analyzeTargetAsString()) - - if len(result.SkippedAnalyzers) > 0 { - fmt.Fprintln(cmd.ErrOrStderr(), "Skipped analyzers:") - for _, a := range result.SkippedAnalyzers { - fmt.Fprintln(cmd.ErrOrStderr(), "\t", a) - } - } - if len(result.ExecutedAnalyzers) > 0 { - fmt.Fprintln(cmd.ErrOrStderr(), "Executed analyzers:") - for _, a := range result.ExecutedAnalyzers { - fmt.Fprintln(cmd.ErrOrStderr(), "\t", a) - } - } - fmt.Fprintln(cmd.ErrOrStderr()) - } - - // Get messages for output - outputMessages := result.Messages.SetDocRef("istioctl-analyze").FilterOutLowerThan(outputThreshold.Level) - - // Print all the messages to stdout in the specified format - output, err := formatting.Print(outputMessages, msgOutputFormat, colorize) - if err != nil { - return err - } - fmt.Fprintln(cmd.OutOrStdout(), output) - - // An extra message on success - if len(outputMessages) == 0 { - if parseErrors == 0 { - if len(readers) > 0 { - var files []string - for _, r := range readers { - files = append(files, r.Name) - } - fmt.Fprintf(cmd.ErrOrStderr(), "\u2714 No validation issues found when analyzing %s.\n", strings.Join(files, "\n")) - } else { - fmt.Fprintf(cmd.ErrOrStderr(), "\u2714 No validation issues found when analyzing %s.\n", analyzeTargetAsString()) - } - } else { - fileOrFiles := "files" - if parseErrors == 1 { - fileOrFiles = "file" - } - fmt.Fprintf(cmd.ErrOrStderr(), - "No validation issues found when analyzing %s (but %d %s could not be parsed).\n", - analyzeTargetAsString(), - parseErrors, - fileOrFiles, - ) - } - } - - // Return code is based on the unfiltered validation message list/parse errors - // We're intentionally keeping failure threshold and output threshold decoupled for now - var returnError error - if msgOutputFormat == formatting.LogFormat { - returnError = errorIfMessagesExceedThreshold(result.Messages) - if returnError == nil && parseErrors > 0 && !ignoreUnknown { - returnError = FileParseError{} - } - } - return returnError - }, - } - - analysisCmd.PersistentFlags().BoolVarP(&listAnalyzers, "list-analyzers", "L", false, - "List the analyzers available to run. Suppresses normal execution.") - analysisCmd.PersistentFlags().BoolVarP(&useKube, "use-kube", "k", true, - "Use live Kubernetes cluster for analysis. Set --use-kube=false to analyze files only.") - analysisCmd.PersistentFlags().BoolVar(&colorize, "color", formatting.IstioctlColorDefault(analysisCmd.OutOrStdout()), - "Default true. Disable with '=false' or set $TERM to dumb") - analysisCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, - "Enable verbose output") - analysisCmd.PersistentFlags().Var(&failureThreshold, "failure-threshold", - fmt.Sprintf("The severity level of analysis at which to set a non-zero exit code. Valid values: %v", diag.GetAllLevelStrings())) - analysisCmd.PersistentFlags().Var(&outputThreshold, "output-threshold", - fmt.Sprintf("The severity level of analysis at which to display messages. Valid values: %v", diag.GetAllLevelStrings())) - analysisCmd.PersistentFlags().StringVarP(&msgOutputFormat, "output", "o", formatting.LogFormat, - fmt.Sprintf("Output format: one of %v", formatting.MsgOutputFormatKeys)) - analysisCmd.PersistentFlags().StringVar(&meshCfgFile, "meshConfigFile", "", - "Overrides the mesh config values to use for analysis.") - analysisCmd.PersistentFlags().BoolVarP(&allNamespaces, "all-namespaces", "A", false, - "Analyze all namespaces") - analysisCmd.PersistentFlags().StringArrayVarP(&suppress, "suppress", "S", []string{}, - "Suppress reporting a message code on a specific resource. Values are supplied in the form "+ - `= (e.g. '--suppress "IST0102=DestinationRule primary-dr.default"'). Can be repeated. `+ - `You can include the wildcard character '*' to support a partial match (e.g. '--suppress "IST0102=DestinationRule *.default" ).`) - analysisCmd.PersistentFlags().DurationVar(&analysisTimeout, "timeout", 30*time.Second, - "The duration to wait before failing") - analysisCmd.PersistentFlags().BoolVarP(&recursive, "recursive", "R", false, - "Process directory arguments recursively. Useful when you want to analyze related manifests organized within the same directory.") - analysisCmd.PersistentFlags().BoolVar(&ignoreUnknown, "ignore-unknown", false, - "Don't complain about un-parseable input documents, for cases where analyze should run only on k8s compliant inputs.") - return analysisCmd -} - -func gatherFiles(cmd *cobra.Command, args []string) ([]local.ReaderSource, error) { - var readers []local.ReaderSource - for _, f := range args { - var r *os.File - - // Handle "-" as stdin as a special case. - if f == "-" { - if isatty.IsTerminal(os.Stdin.Fd()) && !isJSONorYAMLOutputFormat() { - fmt.Fprint(cmd.OutOrStdout(), "Reading from stdin:\n") - } - r = os.Stdin - readers = append(readers, local.ReaderSource{Name: f, Reader: r}) - continue - } - - fi, err := os.Stat(f) - if err != nil { - return nil, err - } - - if fi.IsDir() { - dirReaders, err := gatherFilesInDirectory(cmd, f) - if err != nil { - return nil, err - } - readers = append(readers, dirReaders...) - } else { - if !isValidFile(f) { - fmt.Fprintf(cmd.ErrOrStderr(), "Skipping file %v, recognized file extensions are: %v\n", f, fileExtensions) - continue - } - rs, err := gatherFile(f) - if err != nil { - return nil, err - } - readers = append(readers, rs) - } - } - return readers, nil -} - -func gatherFile(f string) (local.ReaderSource, error) { - r, err := os.Open(f) - if err != nil { - return local.ReaderSource{}, err - } - runtime.SetFinalizer(r, func(x *os.File) { x.Close() }) - return local.ReaderSource{Name: f, Reader: r}, nil -} - -func gatherFilesInDirectory(cmd *cobra.Command, dir string) ([]local.ReaderSource, error) { - var readers []local.ReaderSource - - err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - // If we encounter a directory, recurse only if the --recursve option - // was provided and the directory is not the same as dir. - if info.IsDir() { - if !recursive && dir != path { - return filepath.SkipDir - } - return nil - } - - if !isValidFile(path) { - fmt.Fprintf(cmd.ErrOrStderr(), "Skipping file %v, recognized file extensions are: %v\n", path, fileExtensions) - return nil - } - - r, err := os.Open(path) - if err != nil { - return err - } - runtime.SetFinalizer(r, func(x *os.File) { x.Close() }) - readers = append(readers, local.ReaderSource{Name: path, Reader: r}) - return nil - }) - return readers, err -} - -func errorIfMessagesExceedThreshold(messages []diag.Message) error { - foundIssues := false - for _, m := range messages { - if m.Type.Level().IsWorseThanOrEqualTo(failureThreshold.Level) { - foundIssues = true - } - } - - if foundIssues { - return AnalyzerFoundIssuesError{} - } - - return nil -} - -func isValidFile(f string) bool { - ext := filepath.Ext(f) - for _, e := range fileExtensions { - if e == ext { - return true - } - } - return false -} - -func AnalyzersAsString(analyzers []analysis.Analyzer) string { - nameToAnalyzer := make(map[string]analysis.Analyzer) - analyzerNames := make([]string, len(analyzers)) - for i, a := range analyzers { - analyzerNames[i] = a.Metadata().Name - nameToAnalyzer[a.Metadata().Name] = a - } - sort.Strings(analyzerNames) - - var b strings.Builder - for _, aName := range analyzerNames { - b.WriteString(fmt.Sprintf("* %s:\n", aName)) - a := nameToAnalyzer[aName] - if a.Metadata().Description != "" { - b.WriteString(fmt.Sprintf(" %s\n", a.Metadata().Description)) - } - } - return b.String() -} - -func analyzeTargetAsString() string { - if allNamespaces { - return "all namespaces" - } - return fmt.Sprintf("namespace: %s", selectedNamespace) -} - -// TODO: Refactor output writer so that it is smart enough to know when to output what. -func isJSONorYAMLOutputFormat() bool { - return msgOutputFormat == formatting.JSONFormat || msgOutputFormat == formatting.YAMLFormat -} diff --git a/istioctl/cmd/analyze_test.go b/istioctl/cmd/analyze_test.go deleted file mode 100644 index 5b724d0bf..000000000 --- a/istioctl/cmd/analyze_test.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/diag" -) - -func TestErrorOnIssuesFound(t *testing.T) { - g := NewWithT(t) - - msgs := []diag.Message{ - diag.NewMessage( - diag.NewMessageType(diag.Error, "B1", "Template: %q"), - nil, - "", - ), - diag.NewMessage( - diag.NewMessageType(diag.Warning, "A1", "Template: %q"), - nil, - "", - ), - } - - err := errorIfMessagesExceedThreshold(msgs) - - g.Expect(err).To(BeIdenticalTo(AnalyzerFoundIssuesError{})) -} - -func TestNoErrorIfMessageLevelsBelowThreshold(t *testing.T) { - g := NewWithT(t) - - msgs := []diag.Message{ - diag.NewMessage( - diag.NewMessageType(diag.Info, "B1", "Template: %q"), - nil, - "", - ), - diag.NewMessage( - diag.NewMessageType(diag.Warning, "A1", "Template: %q"), - nil, - "", - ), - } - - err := errorIfMessagesExceedThreshold(msgs) - - g.Expect(err).To(BeNil()) -} diff --git a/istioctl/cmd/authz.go b/istioctl/cmd/authz.go deleted file mode 100644 index bb3818410..000000000 --- a/istioctl/cmd/authz.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "fmt" - "io" - "os" -) - -import ( - "github.com/spf13/cobra" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/authz" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/configdump" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/handlers" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -var configDumpFile string - -var checkCmd = &cobra.Command{ - Use: "check [/][.]", - Short: "Check AuthorizationPolicy applied in the pod.", - Long: `Check prints the AuthorizationPolicy applied to a pod by directly checking -the Envoy configuration of the pod. The command is especially useful for inspecting -the policy propagation from Istiod to Envoy and the final AuthorizationPolicy list merged -from multiple sources (mesh-level, namespace-level and workload-level). - -The command also supports reading from a standalone config dump file with flag -f.`, - Example: ` # Check AuthorizationPolicy applied to pod httpbin-88ddbcfdd-nt5jb: - istioctl x authz check httpbin-88ddbcfdd-nt5jb - - # Check AuthorizationPolicy applied to one pod under a deployment - istioctl x authz check deployment/productpage-v1 - - # Check AuthorizationPolicy from Envoy config dump file: - istioctl x authz check -f httpbin_config_dump.json`, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) > 1 { - cmd.Println(cmd.UsageString()) - return fmt.Errorf("check requires only [.]") - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - var configDump *configdump.Wrapper - var err error - if configDumpFile != "" { - configDump, err = getConfigDumpFromFile(configDumpFile) - if err != nil { - return fmt.Errorf("failed to get config dump from file %s: %s", configDumpFile, err) - } - } else if len(args) == 1 { - kubeClient, err := kubeClient(kubeconfig, configContext) - if err != nil { - return fmt.Errorf("failed to create k8s client: %w", err) - } - podName, podNamespace, err := handlers.InferPodInfoFromTypedResource(args[0], - handlers.HandleNamespace(namespace, defaultNamespace), - kubeClient.UtilFactory()) - if err != nil { - return err - } - configDump, err = getConfigDumpFromPod(podName, podNamespace) - if err != nil { - return fmt.Errorf("failed to get config dump from pod %s in %s", podName, podNamespace) - } - } else { - return fmt.Errorf("expecting pod name or config dump, found: %d", len(args)) - } - - analyzer, err := authz.NewAnalyzer(configDump) - if err != nil { - return err - } - analyzer.Print(cmd.OutOrStdout()) - return nil - }, -} - -func getConfigDumpFromFile(filename string) (*configdump.Wrapper, error) { - file, err := os.Open(filename) - if err != nil { - return nil, err - } - defer func() { - if err := file.Close(); err != nil { - log.Errorf("failed to close %s: %s", filename, err) - } - }() - data, err := io.ReadAll(file) - if err != nil { - return nil, err - } - - envoyConfig := &configdump.Wrapper{} - if err := envoyConfig.UnmarshalJSON(data); err != nil { - return nil, fmt.Errorf("failed to unmarshal proxy config: %s", err) - } - return envoyConfig, nil -} - -func getConfigDumpFromPod(podName, podNamespace string) (*configdump.Wrapper, error) { - kubeClient, err := kube.NewExtendedClient(kube.BuildClientCmd(kubeconfig, configContext), "") - if err != nil { - return nil, err - } - - pods, err := kubeClient.GetIstioPods(context.TODO(), podNamespace, map[string]string{ - "fieldSelector": "metadata.name=" + podName, - }) - if err != nil { - return nil, fmt.Errorf("failed to get pod: %s", err) - } - if len(pods) != 1 { - return nil, fmt.Errorf("expecting only 1 pod for %s.%s, found: %d", podName, podNamespace, len(pods)) - } - - data, err := kubeClient.EnvoyDo(context.TODO(), podName, podNamespace, "GET", "config_dump") - if err != nil { - return nil, fmt.Errorf("failed to get proxy config for %s.%s: %s", podName, podNamespace, err) - } - envoyConfig := &configdump.Wrapper{} - if err := envoyConfig.UnmarshalJSON(data); err != nil { - return nil, fmt.Errorf("failed to unmarshal proxy config: %s", err) - } - return envoyConfig, nil -} - -// AuthZ groups commands used for inspecting and interacting the authorization policy. -// Note: this is still under active development and is not ready for real use. -func AuthZ() *cobra.Command { - cmd := &cobra.Command{ - Use: "authz", - Short: "Inspect Istio AuthorizationPolicy", - } - - cmd.AddCommand(checkCmd) - cmd.Long += "\n\n" + ExperimentalMsg - return cmd -} - -func init() { - checkCmd.PersistentFlags().StringVarP(&configDumpFile, "file", "f", "", - "The json file with Envoy config dump to be checked") -} diff --git a/istioctl/cmd/authz_test.go b/istioctl/cmd/authz_test.go deleted file mode 100644 index 14aa17a25..000000000 --- a/istioctl/cmd/authz_test.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd diff --git a/istioctl/cmd/clusters.go b/istioctl/cmd/clusters.go deleted file mode 100644 index d555181c1..000000000 --- a/istioctl/cmd/clusters.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "encoding/json" - "fmt" - "io" - "text/tabwriter" -) - -import ( - "github.com/spf13/cobra" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/clioptions" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" -) - -// TODO move to multicluster package; requires exposing some private funcs/vars in this package -func clustersCommand() *cobra.Command { - var opts clioptions.ControlPlaneOptions - cmd := &cobra.Command{ - Use: "remote-clusters", - Short: "Lists the remote clusters each istiod instance is connected to.", - RunE: func(cmd *cobra.Command, args []string) error { - kubeClient, err := kubeClientWithRevision(kubeconfig, configContext, opts.Revision) - if err != nil { - return err - } - res, err := kubeClient.AllDiscoveryDo(context.Background(), istioNamespace, "/debug/clusterz") - if err != nil { - return err - } - return writeMulticlusterStatus(cmd.OutOrStdout(), res) - }, - } - opts.AttachControlPlaneFlags(cmd) - return cmd -} - -func writeMulticlusterStatus(out io.Writer, input map[string][]byte) error { - statuses, err := parseClusterStatuses(input) - if err != nil { - return err - } - w := new(tabwriter.Writer).Init(out, 0, 8, 5, ' ', 0) - _, _ = fmt.Fprintln(w, "NAME\tSECRET\tSTATUS\tISTIOD") - for istiod, clusters := range statuses { - for _, c := range clusters { - _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", c.ID, c.SecretName, c.SyncStatus, istiod) - } - } - _ = w.Flush() - return nil -} - -func parseClusterStatuses(input map[string][]byte) (map[string][]cluster.DebugInfo, error) { - statuses := make(map[string][]cluster.DebugInfo, len(input)) - for istiodKey, bytes := range input { - var parsed []cluster.DebugInfo - if err := json.Unmarshal(bytes, &parsed); err != nil { - return nil, err - } - statuses[istiodKey] = parsed - } - return statuses, nil -} diff --git a/istioctl/cmd/completion.go b/istioctl/cmd/completion.go deleted file mode 100644 index ba58c9d2b..000000000 --- a/istioctl/cmd/completion.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "strings" -) - -import ( - "github.com/spf13/cobra" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/handlers" -) - -func getPodsNameInDefaultNamespace(toComplete string) ([]string, error) { - kubeClient, err := kubeClient(kubeconfig, configContext) - if err != nil { - return nil, err - } - - ctx := context.Background() - ns := handlers.HandleNamespace(namespace, defaultNamespace) - podList, err := kubeClient.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{}) - if err != nil { - return nil, err - } - - var podsName []string - for _, pod := range podList.Items { - if toComplete == "" || strings.HasPrefix(pod.Name, toComplete) { - podsName = append(podsName, pod.Name) - } - } - - return podsName, nil -} - -func validPodsNameArgs(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) != 0 { - return nil, cobra.ShellCompDirectiveNoFileComp - } - - podsName, err := getPodsNameInDefaultNamespace(toComplete) - if err != nil { - return nil, cobra.ShellCompDirectiveNoFileComp - } - return podsName, cobra.ShellCompDirectiveNoFileComp -} - -func getServicesName(toComplete string) ([]string, error) { - kubeClient, err := kubeClient(kubeconfig, configContext) - if err != nil { - return nil, err - } - - ctx := context.Background() - ns := handlers.HandleNamespace(namespace, defaultNamespace) - serviceList, err := kubeClient.CoreV1().Services(ns).List(ctx, metav1.ListOptions{}) - if err != nil { - return nil, err - } - - var serviceNameList []string - for _, service := range serviceList.Items { - if toComplete == "" || strings.HasPrefix(service.Name, toComplete) { - serviceNameList = append(serviceNameList, service.Name) - } - } - - return serviceNameList, nil -} - -func validServiceArgs(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) != 0 { - return nil, cobra.ShellCompDirectiveNoFileComp - } - - servicesName, err := getServicesName(toComplete) - if err != nil { - return nil, cobra.ShellCompDirectiveNoFileComp - } - return servicesName, cobra.ShellCompDirectiveNoFileComp -} - -func getNamespacesName(toComplete string) ([]string, error) { - kubeClient, err := kubeClient(kubeconfig, configContext) - if err != nil { - return nil, err - } - - ctx := context.Background() - nsList, err := getNamespaces(ctx, kubeClient) - if err != nil { - return nil, err - } - - var nsNameList []string - for _, ns := range nsList { - if toComplete == "" || strings.HasPrefix(ns.Name, toComplete) { - nsNameList = append(nsNameList, ns.Name) - } - } - - return nsNameList, nil -} - -func validNamespaceArgs(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) != 0 { - return nil, cobra.ShellCompDirectiveNoFileComp - } - - nsName, err := getNamespacesName(toComplete) - if err != nil { - return nil, cobra.ShellCompDirectiveNoFileComp - } - return nsName, cobra.ShellCompDirectiveNoFileComp -} diff --git a/istioctl/cmd/config.go b/istioctl/cmd/config.go deleted file mode 100644 index a8d8dd31f..000000000 --- a/istioctl/cmd/config.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "fmt" - "io" - "sort" - "text/tabwriter" -) - -import ( - "github.com/spf13/cobra" - "github.com/spf13/viper" - "istio.io/pkg/env" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" -) - -// settableFlags are the flags used to istioctl -var settableFlags = map[string]interface{}{ - "istioNamespace": env.RegisterStringVar("ISTIOCTL_ISTIONAMESPACE", constants.IstioSystemNamespace, "The istioctl --istioNamespace override"), - "xds-address": env.RegisterStringVar("ISTIOCTL_XDS_ADDRESS", "", "The istioctl --xds-address override"), - "xds-port": env.RegisterIntVar("ISTIOCTL_XDS_PORT", 15012, "The istioctl --xds-port override"), - "authority": env.RegisterStringVar("ISTIOCTL_AUTHORITY", "", "The istioctl --authority override"), - "cert-dir": env.RegisterStringVar("ISTIOCTL_CERT_DIR", "", "The istioctl --cert-dir override"), - "insecure": env.RegisterBoolVar("ISTIOCTL_INSECURE", false, "The istioctl --insecure override"), - "prefer-experimental": env.RegisterBoolVar("ISTIOCTL_PREFER_EXPERIMENTAL", false, "The istioctl should use experimental subcommand variants"), - "plaintext": env.RegisterBoolVar("ISTIOCTL_PLAINTEXT", false, "The istioctl --plaintext override"), -} - -// configCmd represents the config subcommand command -func configCmd() *cobra.Command { - configCmd := &cobra.Command{ - Use: "config SUBCOMMAND", - Short: "Configure istioctl defaults", - Args: cobra.NoArgs, - Example: ` # list configuration parameters - istioctl config list`, - } - configCmd.AddCommand(listCommand()) - return configCmd -} - -func listCommand() *cobra.Command { - listCmd := &cobra.Command{ - Use: "list", - Short: "List istio configurable defaults", - Args: cobra.ExactArgs(0), - RunE: func(c *cobra.Command, _ []string) error { - scope.Debugf("Config file %q", IstioConfig) - return runList(c.OutOrStdout()) - }, - } - return listCmd -} - -func runList(writer io.Writer) error { - // Sort flag names - keys := make([]string, len(settableFlags)) - i := 0 - for key := range settableFlags { - keys[i] = key - i++ - } - sort.Strings(keys) - - w := new(tabwriter.Writer).Init(writer, 0, 8, 5, ' ', 0) - fmt.Fprintf(w, "FLAG\tVALUE\tFROM\n") - for _, flag := range keys { - v := settableFlags[flag] - fmt.Fprintf(w, "%s\t%s\t%v\n", flag, viper.GetString(flag), configSource(flag, v)) - } - return w.Flush() -} - -func configSource(flag string, v interface{}) string { - // Environment variables have high precedence in Viper - if isVarSet(v) { - return "$" + getVarVar(v).Name - } - - if viper.InConfig(flag) { - return IstioConfig - } - - return "default" -} - -func getVarVar(v interface{}) env.Var { - switch ev := v.(type) { - case env.StringVar: - return ev.Var - case env.BoolVar: - return ev.Var - case env.IntVar: - return ev.Var - case env.DurationVar: - return ev.Var - case env.FloatVar: - return ev.Var - default: - panic(fmt.Sprintf("Unexpected environment var type %v", v)) - } -} - -func isVarSet(v interface{}) bool { - switch ev := v.(type) { - case env.StringVar: - _, ok := ev.Lookup() - return ok - case env.BoolVar: - _, ok := ev.Lookup() - return ok - case env.IntVar: - _, ok := ev.Lookup() - return ok - case env.DurationVar: - _, ok := ev.Lookup() - return ok - case env.FloatVar: - _, ok := ev.Lookup() - return ok - default: - panic(fmt.Sprintf("Unexpected environment var type %v", v)) - } -} diff --git a/istioctl/cmd/config_test.go b/istioctl/cmd/config_test.go deleted file mode 100644 index 46ee63ec6..000000000 --- a/istioctl/cmd/config_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "fmt" - "regexp" - "strings" - "testing" -) - -func TestConfigList(t *testing.T) { - cases := []testCase{ - { // case 0 - args: strings.Split("experimental config get istioNamespace", " "), - expectedRegexp: regexp.MustCompile("Usage:\n istioctl experimental config"), - wantException: false, - }, - { // case 1 - args: strings.Split("experimental config list", " "), - expectedOutput: `FLAG VALUE FROM -authority default -cert-dir default -insecure default -istioNamespace dubbo-system default -plaintext default -prefer-experimental default -xds-address default -xds-port 15012 default -`, - wantException: false, - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("case %d %s", i, strings.Join(c.args, " ")), func(t *testing.T) { - verifyOutput(t, c) - }) - } -} diff --git a/istioctl/cmd/dashboard.go b/istioctl/cmd/dashboard.go deleted file mode 100644 index 7106651ca..000000000 --- a/istioctl/cmd/dashboard.go +++ /dev/null @@ -1,537 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "errors" - "fmt" - "io" - "os" - "os/exec" - "os/signal" - "runtime" -) - -import ( - "github.com/spf13/cobra" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/clioptions" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/handlers" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -var ( - listenPort = 0 - controlZport = 0 - - bindAddress = "" - - // open browser or not, default is true - browser = true - - // label selector - labelSelector = "" - - addonNamespace = "" - - envoyDashNs = "" -) - -// port-forward to Istio System Prometheus; open browser -func promDashCmd() *cobra.Command { - var opts clioptions.ControlPlaneOptions - cmd := &cobra.Command{ - Use: "prometheus", - Short: "Open Prometheus web UI", - Long: `Open Istio's Prometheus dashboard`, - Example: ` istioctl dashboard prometheus - - # with short syntax - istioctl dash prometheus - istioctl d prometheus`, - RunE: func(cmd *cobra.Command, args []string) error { - client, err := kubeClientWithRevision(kubeconfig, configContext, opts.Revision) - if err != nil { - return fmt.Errorf("failed to create k8s client: %v", err) - } - - pl, err := client.PodsForSelector(context.TODO(), addonNamespace, "app=prometheus") - if err != nil { - return fmt.Errorf("not able to locate Prometheus pod: %v", err) - } - - if len(pl.Items) < 1 { - return errors.New("no Prometheus pods found") - } - - // only use the first pod in the list - return portForward(pl.Items[0].Name, addonNamespace, "Prometheus", - "http://%s", bindAddress, 9090, client, cmd.OutOrStdout(), browser) - }, - } - - return cmd -} - -// port-forward to Istio System Grafana; open browser -func grafanaDashCmd() *cobra.Command { - var opts clioptions.ControlPlaneOptions - cmd := &cobra.Command{ - Use: "grafana", - Short: "Open Grafana web UI", - Long: `Open Istio's Grafana dashboard`, - Example: ` istioctl dashboard grafana - - # with short syntax - istioctl dash grafana - istioctl d grafana`, - RunE: func(cmd *cobra.Command, args []string) error { - client, err := kubeClientWithRevision(kubeconfig, configContext, opts.Revision) - if err != nil { - return fmt.Errorf("failed to create k8s client: %v", err) - } - - pl, err := client.PodsForSelector(context.TODO(), addonNamespace, "app=grafana") - if err != nil { - return fmt.Errorf("not able to locate Grafana pod: %v", err) - } - - if len(pl.Items) < 1 { - return errors.New("no Grafana pods found") - } - - // only use the first pod in the list - return portForward(pl.Items[0].Name, addonNamespace, "Grafana", - "http://%s", bindAddress, 3000, client, cmd.OutOrStdout(), browser) - }, - } - - return cmd -} - -// port-forward to Istio System Kiali; open browser -func kialiDashCmd() *cobra.Command { - var opts clioptions.ControlPlaneOptions - cmd := &cobra.Command{ - Use: "kiali", - Short: "Open Kiali web UI", - Long: `Open Istio's Kiali dashboard`, - Example: ` istioctl dashboard kiali - - # with short syntax - istioctl dash kiali - istioctl d kiali`, - RunE: func(cmd *cobra.Command, args []string) error { - client, err := kubeClientWithRevision(kubeconfig, configContext, opts.Revision) - if err != nil { - return fmt.Errorf("failed to create k8s client: %v", err) - } - - pl, err := client.PodsForSelector(context.TODO(), addonNamespace, "app=kiali") - if err != nil { - return fmt.Errorf("not able to locate Kiali pod: %v", err) - } - - if len(pl.Items) < 1 { - return errors.New("no Kiali pods found") - } - - // only use the first pod in the list - return portForward(pl.Items[0].Name, addonNamespace, "Kiali", - "http://%s/kiali", bindAddress, 20001, client, cmd.OutOrStdout(), browser) - }, - } - - return cmd -} - -// port-forward to Istio System Jaeger; open browser -func jaegerDashCmd() *cobra.Command { - var opts clioptions.ControlPlaneOptions - cmd := &cobra.Command{ - Use: "jaeger", - Short: "Open Jaeger web UI", - Long: `Open Istio's Jaeger dashboard`, - Example: ` istioctl dashboard jaeger - - # with short syntax - istioctl dash jaeger - istioctl d jaeger`, - RunE: func(cmd *cobra.Command, args []string) error { - client, err := kubeClientWithRevision(kubeconfig, configContext, opts.Revision) - if err != nil { - return fmt.Errorf("failed to create k8s client: %v", err) - } - - pl, err := client.PodsForSelector(context.TODO(), addonNamespace, "app=jaeger") - if err != nil { - return fmt.Errorf("not able to locate Jaeger pod: %v", err) - } - - if len(pl.Items) < 1 { - return errors.New("no Jaeger pods found") - } - - // only use the first pod in the list - return portForward(pl.Items[0].Name, addonNamespace, "Jaeger", - "http://%s", bindAddress, 16686, client, cmd.OutOrStdout(), browser) - }, - } - - return cmd -} - -// port-forward to Istio System Zipkin; open browser -func zipkinDashCmd() *cobra.Command { - var opts clioptions.ControlPlaneOptions - cmd := &cobra.Command{ - Use: "zipkin", - Short: "Open Zipkin web UI", - Long: `Open Istio's Zipkin dashboard`, - Example: ` istioctl dashboard zipkin - - # with short syntax - istioctl dash zipkin - istioctl d zipkin`, - RunE: func(cmd *cobra.Command, args []string) error { - client, err := kubeClientWithRevision(kubeconfig, configContext, opts.Revision) - if err != nil { - return fmt.Errorf("failed to create k8s client: %v", err) - } - - pl, err := client.PodsForSelector(context.TODO(), addonNamespace, "app=zipkin") - if err != nil { - return fmt.Errorf("not able to locate Zipkin pod: %v", err) - } - - if len(pl.Items) < 1 { - return errors.New("no Zipkin pods found") - } - - // only use the first pod in the list - return portForward(pl.Items[0].Name, addonNamespace, "Zipkin", - "http://%s", bindAddress, 9411, client, cmd.OutOrStdout(), browser) - }, - } - - return cmd -} - -// port-forward to sidecar Envoy admin port; open browser -func envoyDashCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "envoy [/][.]", - Short: "Open Envoy admin web UI", - Long: `Open the Envoy admin dashboard for a sidecar`, - Example: ` # Open Envoy dashboard for the productpage-123-456.default pod - istioctl dashboard envoy productpage-123-456.default - - # Open Envoy dashboard for one pod under a deployment - istioctl dashboard envoy deployment/productpage-v1 - - # with short syntax - istioctl dash envoy productpage-123-456.default - istioctl d envoy productpage-123-456.default -`, - RunE: func(c *cobra.Command, args []string) error { - if labelSelector == "" && len(args) < 1 { - c.Println(c.UsageString()) - return fmt.Errorf("specify a pod or --selector") - } - - if labelSelector != "" && len(args) > 0 { - c.Println(c.UsageString()) - return fmt.Errorf("name cannot be provided when a selector is specified") - } - - client, err := kubeClient(kubeconfig, configContext) - if err != nil { - return fmt.Errorf("failed to create k8s client: %v", err) - } - - var podName, ns string - if labelSelector != "" { - pl, err := client.PodsForSelector(context.TODO(), handlers.HandleNamespace(envoyDashNs, defaultNamespace), labelSelector) - if err != nil { - return fmt.Errorf("not able to locate pod with selector %s: %v", labelSelector, err) - } - - if len(pl.Items) < 1 { - return errors.New("no pods found") - } - - if len(pl.Items) > 1 { - log.Warnf("more than 1 pods fits selector: %s; will use pod: %s", labelSelector, pl.Items[0].Name) - } - - // only use the first pod in the list - podName = pl.Items[0].Name - ns = pl.Items[0].Namespace - } else { - podName, ns, err = handlers.InferPodInfoFromTypedResource(args[0], - handlers.HandleNamespace(envoyDashNs, defaultNamespace), - client.UtilFactory()) - if err != nil { - return err - } - } - - return portForward(podName, ns, fmt.Sprintf("Envoy sidecar %s", podName), - "http://%s", bindAddress, 15000, client, c.OutOrStdout(), browser) - }, - } - - return cmd -} - -// port-forward to sidecar ControlZ port; open browser -func controlZDashCmd() *cobra.Command { - var opts clioptions.ControlPlaneOptions - cmd := &cobra.Command{ - Use: "controlz [/][.]", - Short: "Open ControlZ web UI", - Long: `Open the ControlZ web UI for a pod in the Istio control plane`, - Example: ` # Open ControlZ web UI for the istiod-123-456.dubbo-system pod - istioctl dashboard controlz istiod-123-456.dubbo-system - - # Open ControlZ web UI for the istiod-56dd66799-jfdvs pod in a custom namespace - istioctl dashboard controlz istiod-123-456 -n custom-ns - - # Open ControlZ web UI for any Istiod pod - istioctl dashboard controlz deployment/istiod.dubbo-system - - # with short syntax - istioctl dash controlz pilot-123-456.dubbo-system - istioctl d controlz pilot-123-456.dubbo-system -`, - RunE: func(c *cobra.Command, args []string) error { - if labelSelector == "" && len(args) < 1 { - c.Println(c.UsageString()) - return fmt.Errorf("specify a pod or --selector") - } - - if labelSelector != "" && len(args) > 0 { - c.Println(c.UsageString()) - return fmt.Errorf("name cannot be provided when a selector is specified") - } - - client, err := kubeClientWithRevision(kubeconfig, configContext, opts.Revision) - if err != nil { - return fmt.Errorf("failed to create k8s client: %v", err) - } - - var podName, ns string - if labelSelector != "" { - pl, err := client.PodsForSelector(context.TODO(), handlers.HandleNamespace(addonNamespace, defaultNamespace), labelSelector) - if err != nil { - return fmt.Errorf("not able to locate pod with selector %s: %v", labelSelector, err) - } - - if len(pl.Items) < 1 { - return errors.New("no pods found") - } - - if len(pl.Items) > 1 { - log.Warnf("more than 1 pods fits selector: %s; will use pod: %s", labelSelector, pl.Items[0].Name) - } - - // only use the first pod in the list - podName = pl.Items[0].Name - ns = pl.Items[0].Namespace - } else { - podName, ns, err = handlers.InferPodInfoFromTypedResource(args[0], - handlers.HandleNamespace(addonNamespace, defaultNamespace), - client.UtilFactory()) - if err != nil { - return err - } - } - - return portForward(podName, ns, fmt.Sprintf("ControlZ %s", podName), - "http://%s", bindAddress, controlZport, client, c.OutOrStdout(), browser) - }, - } - - return cmd -} - -// port-forward to SkyWalking UI on dubbo-system -func skywalkingDashCmd() *cobra.Command { - var opts clioptions.ControlPlaneOptions - cmd := &cobra.Command{ - Use: "skywalking", - Short: "Open SkyWalking UI", - Long: "Open the Istio dashboard in the SkyWalking UI", - Example: ` istioctl dashboard skywalking - - # with short syntax - istioctl dash skywalking - istioctl d skywalking`, - RunE: func(cmd *cobra.Command, args []string) error { - client, err := kubeClientWithRevision(kubeconfig, configContext, opts.Revision) - if err != nil { - return fmt.Errorf("failed to create k8s client: %v", err) - } - - pl, err := client.PodsForSelector(context.TODO(), addonNamespace, "app=skywalking-ui") - if err != nil { - return fmt.Errorf("not able to locate SkyWalking UI pod: %v", err) - } - - if len(pl.Items) < 1 { - return errors.New("no SkyWalking UI pods found") - } - - // only use the first pod in the list - return portForward(pl.Items[0].Name, addonNamespace, "SkyWalking", - "http://%s", bindAddress, 8080, client, cmd.OutOrStdout(), browser) - }, - } - - return cmd -} - -// portForward first tries to forward localhost:remotePort to podName:remotePort, falls back to dynamic local port -func portForward(podName, namespace, flavor, urlFormat, localAddress string, remotePort int, - client kube.ExtendedClient, writer io.Writer, browser bool) error { - // port preference: - // - If --listenPort is specified, use it - // - without --listenPort, prefer the remotePort but fall back to a random port - var portPrefs []int - if listenPort != 0 { - portPrefs = []int{listenPort} - } else { - portPrefs = []int{remotePort, 0} - } - - var err error - for _, localPort := range portPrefs { - var fw kube.PortForwarder - fw, err = client.NewPortForwarder(podName, namespace, localAddress, localPort, remotePort) - if err != nil { - return fmt.Errorf("could not build port forwarder for %s: %v", flavor, err) - } - - if err = fw.Start(); err != nil { - fw.Close() - // Try the next port - continue - } - - // Close the port forwarder when the command is terminated. - closePortForwarderOnInterrupt(fw) - - log.Debugf(fmt.Sprintf("port-forward to %s pod ready", flavor)) - openBrowser(fmt.Sprintf(urlFormat, fw.Address()), writer, browser) - - // Wait for stop - fw.WaitForStop() - - return nil - } - - return fmt.Errorf("failure running port forward process: %v", err) -} - -func closePortForwarderOnInterrupt(fw kube.PortForwarder) { - go func() { - signals := make(chan os.Signal, 1) - signal.Notify(signals, os.Interrupt) - defer signal.Stop(signals) - <-signals - fw.Close() - }() -} - -func openBrowser(url string, writer io.Writer, browser bool) { - var err error - - fmt.Fprintf(writer, "%s\n", url) - - if !browser { - fmt.Fprint(writer, "skipping opening a browser") - return - } - - switch runtime.GOOS { - case "linux": - err = exec.Command("xdg-open", url).Start() - case "windows": - err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() - case "darwin": - err = exec.Command("open", url).Start() - default: - fmt.Fprintf(writer, "Unsupported platform %q; open %s in your browser.\n", runtime.GOOS, url) - } - - if err != nil { - fmt.Fprintf(writer, "Failed to open browser; open %s in your browser.\n", url) - } -} - -func dashboard() *cobra.Command { - dashboardCmd := &cobra.Command{ - Use: "dashboard", - Aliases: []string{"dash", "d"}, - Short: "Access to Istio web UIs", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) != 0 { - return fmt.Errorf("unknown dashboard %q", args[0]) - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - cmd.HelpFunc()(cmd, args) - return nil - }, - } - - dashboardCmd.PersistentFlags().IntVarP(&listenPort, "port", "p", 0, "Local port to listen to") - dashboardCmd.PersistentFlags().StringVar(&bindAddress, "address", "localhost", - "Address to listen on. Only accepts IP address or localhost as a value. "+ - "When localhost is supplied, istioctl will try to bind on both 127.0.0.1 and ::1 "+ - "and will fail if neither of these address are available to bind.") - dashboardCmd.PersistentFlags().BoolVar(&browser, "browser", true, - "When --browser is supplied as false, istioctl dashboard will not open the browser. "+ - "Default is true which means istioctl dashboard will always open a browser to view the dashboard.") - dashboardCmd.PersistentFlags().StringVarP(&addonNamespace, "namespace", "n", istioNamespace, - "Namespace where the addon is running, if not specified, dubbo-system would be used") - - dashboardCmd.AddCommand(kialiDashCmd()) - dashboardCmd.AddCommand(promDashCmd()) - dashboardCmd.AddCommand(grafanaDashCmd()) - dashboardCmd.AddCommand(jaegerDashCmd()) - dashboardCmd.AddCommand(zipkinDashCmd()) - dashboardCmd.AddCommand(skywalkingDashCmd()) - - envoy := envoyDashCmd() - envoy.PersistentFlags().StringVarP(&labelSelector, "selector", "l", "", "Label selector") - envoy.PersistentFlags().StringVarP(&envoyDashNs, "namespace", "n", defaultNamespace, - "Namespace where the addon is running, if not specified, dubbo-system would be used") - dashboardCmd.AddCommand(envoy) - - controlz := controlZDashCmd() - controlz.PersistentFlags().IntVar(&controlZport, "ctrlz_port", 9876, "ControlZ port") - controlz.PersistentFlags().StringVarP(&labelSelector, "selector", "l", "", "Label selector") - controlz.PersistentFlags().StringVarP(&addonNamespace, "namespace", "n", istioNamespace, - "Namespace where the addon is running, if not specified, dubbo-system would be used") - dashboardCmd.AddCommand(controlz) - - return dashboardCmd -} diff --git a/istioctl/cmd/dashboard_test.go b/istioctl/cmd/dashboard_test.go deleted file mode 100644 index 83531f993..000000000 --- a/istioctl/cmd/dashboard_test.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "fmt" - "regexp" - "strings" - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -func TestDashboard(t *testing.T) { - kubeClientWithRevision = mockExecClientDashboard - kubeClient = mockEnvoyClientDashboard - - cases := []testCase{ - { // case 0 - args: strings.Split("dashboard --browser=false", " "), - expectedRegexp: regexp.MustCompile("Access to Istio web UIs"), - }, - { // case 1 - args: strings.Split("dashboard invalid --browser=false", " "), - expectedRegexp: regexp.MustCompile(`unknown dashboard "invalid"`), - wantException: true, - }, - { // case 2 - args: strings.Split("dashboard controlz --browser=false", " "), - expectedRegexp: regexp.MustCompile(".*Error: specify a pod or --selector"), - wantException: true, - }, - { // case 3 - args: strings.Split("dashboard controlz --browser=false pod-123456-7890", " "), - expectedRegexp: regexp.MustCompile(".*http://localhost:3456"), - wantException: false, - }, - { // case 4 - args: strings.Split("dashboard envoy --browser=false", " "), - expectedRegexp: regexp.MustCompile(".*Error: specify a pod or --selector"), - wantException: true, - }, - { // case 5 - args: strings.Split("dashboard envoy --browser=false pod-123456-7890", " "), - expectedRegexp: regexp.MustCompile("http://localhost:3456"), - wantException: false, - }, - { // case 6 - args: strings.Split("dashboard grafana --browser=false", " "), - expectedOutput: "Error: no Grafana pods found\n", - wantException: true, - }, - { // case 7 - args: strings.Split("dashboard jaeger --browser=false", " "), - expectedOutput: "Error: no Jaeger pods found\n", - wantException: true, - }, - { // case 8 - args: strings.Split("dashboard kiali --browser=false", " "), - expectedOutput: "Error: no Kiali pods found\n", - wantException: true, - }, - { // case 9 - args: strings.Split("dashboard prometheus --browser=false", " "), - expectedOutput: "Error: no Prometheus pods found\n", - wantException: true, - }, - { // case 10 - args: strings.Split("dashboard zipkin --browser=false", " "), - expectedOutput: "Error: no Zipkin pods found\n", - wantException: true, - }, - { // case 11 - args: strings.Split("dashboard envoy --selector app=example --browser=false", " "), - expectedRegexp: regexp.MustCompile(".*no pods found"), - wantException: true, - }, - { // case 12 - args: strings.Split("dashboard envoy --browser=false --selector app=example pod-123456-7890", " "), - expectedRegexp: regexp.MustCompile(".*Error: name cannot be provided when a selector is specified"), - wantException: true, - }, - { // case 13 - args: strings.Split("dashboard --browser=false controlz --selector app=example", " "), - expectedRegexp: regexp.MustCompile(".*no pods found"), - wantException: true, - }, - { // case 14 - args: strings.Split("dashboard --browser=false controlz --selector app=example pod-123456-7890", " "), - expectedRegexp: regexp.MustCompile(".*Error: name cannot be provided when a selector is specified"), - wantException: true, - }, - { // case 15 - args: strings.Split("-n test dashboard", " "), - expectedRegexp: regexp.MustCompile("Access to Istio web UIs"), - }, - { // case 16 - args: strings.Split("dashboard controlz --browser=false pod-123456-7890 -n dubbo-system", " "), - expectedRegexp: regexp.MustCompile(".*http://localhost:3456"), - wantException: false, - }, - { // case 17 - args: strings.Split("dashboard envoy --browser=false pod-123456-7890 -n dubbo-system", " "), - expectedRegexp: regexp.MustCompile("http://localhost:3456"), - wantException: false, - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("case %d %s", i, strings.Join(c.args, " ")), func(t *testing.T) { - verifyOutput(t, c) - }) - } -} - -func mockExecClientDashboard(_, _, _ string) (kube.ExtendedClient, error) { - return kube.MockClient{}, nil -} - -func mockEnvoyClientDashboard(_, _ string) (kube.ExtendedClient, error) { - return kube.MockClient{}, nil -} diff --git a/istioctl/cmd/deprecated_cmd.go b/istioctl/cmd/deprecated_cmd.go deleted file mode 100644 index 95fd6e123..000000000 --- a/istioctl/cmd/deprecated_cmd.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// DEPRECATED - These commands are deprecated and will be removed in future releases. - -package cmd - -import ( - istioclient "istio.io/client-go/pkg/clientset/versioned" -) - -import ( - kubecfg "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -// Create a model.ConfigStore (or sortedConfigStore) -var configStoreFactory = newConfigStore - -func newConfigStore() (istioclient.Interface, error) { - cfg, err := kubecfg.BuildClientConfig(kubeconfig, configContext) - if err != nil { - return nil, err - } - kclient, err := kubecfg.NewClient(kubecfg.NewClientConfigForRestConfig(cfg)) - if err != nil { - return nil, err - } - return kclient.Istio(), nil -} diff --git a/istioctl/cmd/describe.go b/istioctl/cmd/describe.go deleted file mode 100644 index 89022de72..000000000 --- a/istioctl/cmd/describe.go +++ /dev/null @@ -1,1337 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "encoding/json" - "fmt" - "io" - "regexp" - "strconv" - "strings" -) - -import ( - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - envoy_api_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - rbac_http_filter "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/rbac/v3" - http_conn "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "github.com/hashicorp/go-multierror" - "github.com/spf13/cobra" - "google.golang.org/protobuf/types/known/structpb" - apiannotation "istio.io/api/annotation" - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/api/networking/v1alpha3" - "istio.io/api/security/v1beta1" - typev1beta1 "istio.io/api/type/v1beta1" - clientnetworking "istio.io/client-go/pkg/apis/networking/v1alpha3" - istioclient "istio.io/client-go/pkg/clientset/versioned" - "istio.io/pkg/log" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - k8s_labels "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/kubernetes" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/clioptions" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/tag" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/configdump" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/handlers" - istio_envoy_configdump "github.com/apache/dubbo-go-pixiu/istioctl/pkg/writer/envoy/configdump" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/crdclient" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - authnv1beta1 "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/authn/v1beta1" - pilotcontroller "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube/controller" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - configKube "github.com/apache/dubbo-go-pixiu/pkg/config/kube" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/inject" -) - -type myProtoValue struct { - *structpb.Value -} - -const ( - k8sSuffix = ".svc." + constants.DefaultKubernetesDomain -) - -var ( - // Function that creates Kubernetes client-go; making it a variable lets us mock client-go - interfaceFactory = createInterface - - // Ignore unmeshed pods. This makes it easy to suppress warnings about kube-system etc - ignoreUnmeshed = false -) - -func podDescribeCmd() *cobra.Command { - var opts clioptions.ControlPlaneOptions - cmd := &cobra.Command{ - Use: "pod ", - Aliases: []string{"po"}, - Short: "Describe pods and their Istio configuration [kube-only]", - Long: `Analyzes pod, its Services, DestinationRules, and VirtualServices and reports -the configuration objects that affect that pod.`, - Example: ` istioctl experimental describe pod productpage-v1-c7765c886-7zzd4`, - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) != 1 { - return fmt.Errorf("expecting pod name") - } - - podName, ns := handlers.InferPodInfo(args[0], handlers.HandleNamespace(namespace, defaultNamespace)) - - client, err := interfaceFactory(kubeconfig) - if err != nil { - return err - } - pod, err := client.CoreV1().Pods(ns).Get(context.TODO(), podName, metav1.GetOptions{}) - if err != nil { - return err - } - - writer := cmd.OutOrStdout() - - podLabels := k8s_labels.Set(pod.ObjectMeta.Labels) - annotations := k8s_labels.Set(pod.ObjectMeta.Annotations) - opts.Revision = getRevisionFromPodAnnotation(annotations) - - printPod(writer, pod, opts.Revision) - - svcs, err := client.CoreV1().Services(ns).List(context.TODO(), metav1.ListOptions{}) - if err != nil { - return err - } - - matchingServices := make([]v1.Service, 0, len(svcs.Items)) - for _, svc := range svcs.Items { - if len(svc.Spec.Selector) > 0 { - svcSelector := k8s_labels.SelectorFromSet(svc.Spec.Selector) - if svcSelector.Matches(podLabels) { - matchingServices = append(matchingServices, svc) - } - } - } - // Validate Istio's "Service association" requirement - if len(matchingServices) == 0 && !ignoreUnmeshed { - fmt.Fprintf(cmd.OutOrStdout(), - "Warning: No Kubernetes Services select pod %s (see https://istio.io/docs/setup/kubernetes/additional-setup/requirements/ )\n", // nolint: lll - kname(pod.ObjectMeta)) - } - // TODO look for port collisions between services targeting this pod - - kubeClient, err := kubeClientWithRevision(kubeconfig, configContext, opts.Revision) - if err != nil { - return err - } - - var configClient istioclient.Interface - if configClient, err = configStoreFactory(); err != nil { - return err - } - - podsLabels := []k8s_labels.Set{k8s_labels.Set(pod.ObjectMeta.Labels)} - fmt.Fprintf(writer, "--------------------\n") - err = describePodServices(writer, kubeClient, configClient, pod, matchingServices, podsLabels) - if err != nil { - return err - } - - // render PeerAuthentication info - fmt.Fprintf(writer, "--------------------\n") - err = describePeerAuthentication(writer, kubeClient, configClient, ns, k8s_labels.Set(pod.ObjectMeta.Labels)) - if err != nil { - return err - } - - // TODO find sidecar configs that select this workload and render them - - // Now look for ingress gateways - return printIngressInfo(writer, matchingServices, podsLabels, client, configClient, kubeClient) - }, - ValidArgsFunction: validPodsNameArgs, - } - - cmd.PersistentFlags().BoolVar(&ignoreUnmeshed, "ignoreUnmeshed", false, - "Suppress warnings for unmeshed pods") - cmd.Long += "\n\n" + ExperimentalMsg - return cmd -} - -func getRevisionFromPodAnnotation(anno k8s_labels.Set) string { - statusString := anno.Get(apiannotation.SidecarStatus.Name) - var injectionStatus inject.SidecarInjectionStatus - if err := json.Unmarshal([]byte(statusString), &injectionStatus); err != nil { - return "" - } - - return injectionStatus.Revision -} - -func describe() *cobra.Command { - describeCmd := &cobra.Command{ - Use: "describe", - Aliases: []string{"des"}, - Short: "Describe resource and related Istio configuration", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) != 0 { - return fmt.Errorf("unknown resource type %q", args[0]) - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - cmd.HelpFunc()(cmd, args) - return nil - }, - } - - describeCmd.AddCommand(podDescribeCmd()) - describeCmd.AddCommand(svcDescribeCmd()) - return describeCmd -} - -// Append ".svc.cluster.local" if it isn't already present -func extendFQDN(host string) string { - if host[0] == '*' { - return host - } - if strings.HasSuffix(host, k8sSuffix) { - return host - } - return host + k8sSuffix -} - -// getDestRuleSubsets gets names of subsets that match any pod labels (also, ones that don't match). -func getDestRuleSubsets(subsets []*v1alpha3.Subset, podsLabels []k8s_labels.Set) ([]string, []string) { - matchingSubsets := make([]string, 0, len(subsets)) - nonmatchingSubsets := make([]string, 0, len(subsets)) - for _, subset := range subsets { - subsetSelector := k8s_labels.SelectorFromSet(subset.Labels) - if matchesAnyPod(subsetSelector, podsLabels) { - matchingSubsets = append(matchingSubsets, subset.Name) - } else { - nonmatchingSubsets = append(nonmatchingSubsets, subset.Name) - } - } - - return matchingSubsets, nonmatchingSubsets -} - -func matchesAnyPod(subsetSelector k8s_labels.Selector, podsLabels []k8s_labels.Set) bool { - for _, podLabels := range podsLabels { - if subsetSelector.Matches(podLabels) { - return true - } - } - return false -} - -func printDestinationRule(writer io.Writer, dr *clientnetworking.DestinationRule, podsLabels []k8s_labels.Set) { - fmt.Fprintf(writer, "DestinationRule: %s for %q\n", kname(dr.ObjectMeta), dr.Spec.Host) - - matchingSubsets, nonmatchingSubsets := getDestRuleSubsets(dr.Spec.Subsets, podsLabels) - - if len(matchingSubsets) != 0 || len(nonmatchingSubsets) != 0 { - if len(matchingSubsets) == 0 { - fmt.Fprintf(writer, " WARNING POD DOES NOT MATCH ANY SUBSETS. (Non matching subsets %s)\n", - strings.Join(nonmatchingSubsets, ",")) - } - fmt.Fprintf(writer, " Matching subsets: %s\n", strings.Join(matchingSubsets, ",")) - if len(nonmatchingSubsets) > 0 { - fmt.Fprintf(writer, " (Non-matching subsets %s)\n", strings.Join(nonmatchingSubsets, ",")) - } - } - - // Ignore LoadBalancer, ConnectionPool, OutlierDetection, and PortLevelSettings - trafficPolicy := dr.Spec.TrafficPolicy - if trafficPolicy == nil { - fmt.Fprintf(writer, " No Traffic Policy\n") - } else { - if trafficPolicy.Tls != nil { - fmt.Fprintf(writer, " Traffic Policy TLS Mode: %s\n", dr.Spec.TrafficPolicy.Tls.Mode.String()) - } - extra := []string{} - if trafficPolicy.LoadBalancer != nil { - extra = append(extra, "load balancer") - } - if trafficPolicy.ConnectionPool != nil { - extra = append(extra, "connection pool") - } - if trafficPolicy.OutlierDetection != nil { - extra = append(extra, "outlier detection") - } - if trafficPolicy.PortLevelSettings != nil { - extra = append(extra, "port level settings") - } - if len(extra) > 0 { - fmt.Fprintf(writer, " %s\n", strings.Join(extra, "/")) - } - } -} - -// httpRouteMatchSvc returns true if it matches and a slice of facts about the match -func httpRouteMatchSvc(vs *clientnetworking.VirtualService, route *v1alpha3.HTTPRoute, svc v1.Service, matchingSubsets []string, nonmatchingSubsets []string, dr *clientnetworking.DestinationRule) (bool, []string) { // nolint: lll - svcHost := extendFQDN(fmt.Sprintf("%s.%s", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace)) - facts := []string{} - mismatchNotes := []string{} - match := false - for _, dest := range route.Route { - fqdn := string(model.ResolveShortnameToFQDN(dest.Destination.Host, config.Meta{Namespace: vs.Namespace})) - if extendFQDN(fqdn) == svcHost { - if dest.Destination.Subset != "" { - if contains(nonmatchingSubsets, dest.Destination.Subset) { - mismatchNotes = append(mismatchNotes, fmt.Sprintf("Route to non-matching subset %s for (%s)", - dest.Destination.Subset, - renderMatches(route.Match))) - continue - } - if !contains(matchingSubsets, dest.Destination.Subset) { - if dr == nil { - // Don't bother giving the match conditions, the problem is that there are unknowns in the VirtualService - mismatchNotes = append(mismatchNotes, fmt.Sprintf("Warning: Route to subset %s but NO DESTINATION RULE defining subsets!", dest.Destination.Subset)) - } else { - // Don't bother giving the match conditions, the problem is that there are unknowns in the VirtualService - mismatchNotes = append(mismatchNotes, - fmt.Sprintf("Warning: Route to UNKNOWN subset %s; check DestinationRule %s", dest.Destination.Subset, kname(dr.ObjectMeta))) - } - continue - } - } - - match = true - if dest.Weight > 0 { - facts = append(facts, fmt.Sprintf("Weight %d%%", dest.Weight)) - } - // Consider adding RemoveResponseHeaders, AppendResponseHeaders, RemoveRequestHeaders, AppendRequestHeaders - } else { - mismatchNotes = append(mismatchNotes, fmt.Sprintf("Route to %s", dest.Destination.Host)) - } - } - - if match { - reqMatchFacts := []string{} - - if route.Fault != nil { - reqMatchFacts = append(reqMatchFacts, fmt.Sprintf("Fault injection %s", route.Fault.String())) - } - - // TODO Consider adding Headers, SourceLabels - - for _, trafficMatch := range route.Match { - reqMatchFacts = append(reqMatchFacts, renderMatch(trafficMatch)) - } - - if len(reqMatchFacts) > 0 { - facts = append(facts, strings.Join(reqMatchFacts, ", ")) - } - } - - if !match && len(mismatchNotes) > 0 { - facts = append(facts, mismatchNotes...) - } - return match, facts -} - -func tcpRouteMatchSvc(vs *clientnetworking.VirtualService, route *v1alpha3.TCPRoute, svc v1.Service) (bool, []string) { - match := false - facts := []string{} - svcHost := extendFQDN(fmt.Sprintf("%s.%s", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace)) - for _, dest := range route.Route { - fqdn := string(model.ResolveShortnameToFQDN(dest.Destination.Host, config.Meta{Namespace: vs.Namespace})) - if extendFQDN(fqdn) == svcHost { - match = true - } - } - - if match { - for _, trafficMatch := range route.Match { - facts = append(facts, trafficMatch.String()) - } - } - - return match, facts -} - -func renderStringMatch(sm *v1alpha3.StringMatch) string { - if sm == nil { - return "" - } - - switch x := sm.MatchType.(type) { - case *v1alpha3.StringMatch_Exact: - return x.Exact - case *v1alpha3.StringMatch_Prefix: - return x.Prefix + "*" - } - - return sm.String() -} - -func renderMatches(trafficMatches []*v1alpha3.HTTPMatchRequest) string { - if len(trafficMatches) == 0 { - return "everything" - } - - matches := []string{} - for _, trafficMatch := range trafficMatches { - matches = append(matches, renderMatch(trafficMatch)) - } - return strings.Join(matches, ", ") -} - -func renderMatch(match *v1alpha3.HTTPMatchRequest) string { - retval := "" - // TODO Are users interested in seeing Scheme, Method, Authority? - if match.Uri != nil { - retval += renderStringMatch(match.Uri) - - if match.IgnoreUriCase { - retval += " uncased" - } - } - - if len(match.Headers) > 0 { - headerConds := []string{} - for key, val := range match.Headers { - headerConds = append(headerConds, fmt.Sprintf("%s=%s", key, renderStringMatch(val))) - } - retval += " when headers are " + strings.Join(headerConds, "; ") - } - - // TODO QueryParams, maybe Gateways - return strings.TrimSpace(retval) -} - -func printPod(writer io.Writer, pod *v1.Pod, revision string) { - ports := []string{} - UserID := int64(1337) - for _, container := range pod.Spec.Containers { - for _, port := range container.Ports { - var protocol string - // Suppress / for TCP, print it for everything else - if port.Protocol != "TCP" { - protocol = fmt.Sprintf("/%s", port.Protocol) - } - ports = append(ports, fmt.Sprintf("%d%s (%s)", port.ContainerPort, protocol, container.Name)) - } - // Ref: https://istio.io/latest/docs/ops/deployment/requirements/#pod-requirements - if container.Name != "istio-proxy" && container.Name != "istio-operator" { - if container.SecurityContext != nil && container.SecurityContext.RunAsUser != nil { - if *container.SecurityContext.RunAsUser == UserID { - fmt.Fprintf(writer, "WARNING: User ID (UID) 1337 is reserved for the sidecar proxy.\n") - } - } - } - } - - fmt.Fprintf(writer, "Pod: %s\n", kname(pod.ObjectMeta)) - fmt.Fprintf(writer, " Pod Revision: %s\n", revision) - if len(ports) > 0 { - fmt.Fprintf(writer, " Pod Ports: %s\n", strings.Join(ports, ", ")) - } else { - fmt.Fprintf(writer, " Pod does not expose ports\n") - } - - if pod.Status.Phase != v1.PodRunning { - fmt.Printf(" Pod is not %s (%s)\n", v1.PodRunning, pod.Status.Phase) - return - } - - for _, containerStatus := range pod.Status.ContainerStatuses { - if !containerStatus.Ready { - fmt.Fprintf(writer, "WARNING: Pod %s Container %s NOT READY\n", kname(pod.ObjectMeta), containerStatus.Name) - } - } - - if ignoreUnmeshed { - return - } - - if !isMeshed(pod) { - fmt.Fprintf(writer, "WARNING: %s is not part of mesh; no Istio sidecar\n", kname(pod.ObjectMeta)) - return - } - - // Ref: https://istio.io/latest/docs/ops/deployment/requirements/#pod-requirements - if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.RunAsUser != nil { - if *pod.Spec.SecurityContext.RunAsUser == UserID { - fmt.Fprintf(writer, " WARNING: User ID (UID) 1337 is reserved for the sidecar proxy.\n") - } - } - - // https://istio.io/docs/setup/kubernetes/additional-setup/requirements/ - // says "We recommend adding an explicit app label and version label to deployments." - app, ok := pod.ObjectMeta.Labels["app"] - if !ok || app == "" { - fmt.Fprintf(writer, "Suggestion: add 'app' label to pod for Istio telemetry.\n") - } - version, ok := pod.ObjectMeta.Labels["version"] - if !ok || version == "" { - fmt.Fprintf(writer, "Suggestion: add 'version' label to pod for Istio telemetry.\n") - } -} - -func kname(meta metav1.ObjectMeta) string { - ns := handlers.HandleNamespace(namespace, defaultNamespace) - if meta.Namespace == ns { - return meta.Name - } - - // Use the Istio convention pod-name[.namespace] - return fmt.Sprintf("%s.%s", meta.Name, meta.Namespace) -} - -func printService(writer io.Writer, svc v1.Service, pod *v1.Pod) { - fmt.Fprintf(writer, "Service: %s\n", kname(svc.ObjectMeta)) - for _, port := range svc.Spec.Ports { - if port.Protocol != "TCP" { - // Ignore UDP ports, which are not supported by Istio - continue - } - // Get port number - nport, err := pilotcontroller.FindPort(pod, &port) - if err == nil { - protocol := findProtocolForPort(&port) - fmt.Fprintf(writer, " Port: %s %d/%s targets pod port %d\n", port.Name, port.Port, protocol, nport) - } else { - fmt.Fprintf(writer, " %s\n", err.Error()) - } - } -} - -func findProtocolForPort(port *v1.ServicePort) string { - var protocol string - if port.Name == "" && port.AppProtocol == nil && port.Protocol != v1.ProtocolUDP { - protocol = "auto-detect" - } else { - protocol = string(configKube.ConvertProtocol(port.Port, port.Name, port.Protocol, port.AppProtocol)) - } - return protocol -} - -func contains(slice []string, s string) bool { - for _, candidate := range slice { - if candidate == s { - return true - } - } - - return false -} - -func isMeshed(pod *v1.Pod) bool { - var sidecar bool - - for _, container := range pod.Spec.Containers { - sidecar = sidecar || (container.Name == inject.ProxyContainerName) - } - - return sidecar -} - -// Extract value of key out of Struct, but always return a Struct, even if the value isn't one -func (v *myProtoValue) keyAsStruct(key string) *myProtoValue { - if v == nil || v.GetStructValue() == nil { - return asMyProtoValue(&structpb.Struct{Fields: make(map[string]*structpb.Value)}) - } - - return &myProtoValue{v.GetStructValue().Fields[key]} -} - -// asMyProtoValue wraps a protobuf Struct so we may use it with keyAsStruct and keyAsString -func asMyProtoValue(s *structpb.Struct) *myProtoValue { - return &myProtoValue{ - &structpb.Value{ - Kind: &structpb.Value_StructValue{ - StructValue: s, - }, - }, - } -} - -func (v *myProtoValue) keyAsString(key string) string { - s := v.keyAsStruct(key) - return s.GetStringValue() -} - -func getIstioRBACPolicies(cd *configdump.Wrapper, port int32) ([]string, error) { - hcm, err := getInboundHTTPConnectionManager(cd, port) - if err != nil || hcm == nil { - return []string{}, err - } - - // Identify RBAC policies. Currently there are no "breadcrumbs" so we only return the policy names. - for _, httpFilter := range hcm.HttpFilters { - if httpFilter.Name == wellknown.HTTPRoleBasedAccessControl { - rbac := &rbac_http_filter.RBAC{} - if err := httpFilter.GetTypedConfig().UnmarshalTo(rbac); err == nil { - policies := []string{} - for polName := range rbac.Rules.Policies { - policies = append(policies, polName) - } - return policies, nil - } - } - } - - return []string{}, nil -} - -// Return the first HTTP Connection Manager config for the inbound port -func getInboundHTTPConnectionManager(cd *configdump.Wrapper, port int32) (*http_conn.HttpConnectionManager, error) { - filter := istio_envoy_configdump.ListenerFilter{ - Port: uint32(port), - } - listeners, err := cd.GetListenerConfigDump() - if err != nil { - return nil, err - } - - for _, l := range listeners.DynamicListeners { - if l.ActiveState == nil { - continue - } - // Support v2 or v3 in config dump. See ads.go:RequestedTypes for more info. - l.ActiveState.Listener.TypeUrl = v3.ListenerType - listenerTyped := &listener.Listener{} - err = l.ActiveState.Listener.UnmarshalTo(listenerTyped) - if err != nil { - return nil, err - } - if listenerTyped.Name == model.VirtualInboundListenerName { - for _, filterChain := range listenerTyped.FilterChains { - for _, filter := range filterChain.Filters { - hcm := &http_conn.HttpConnectionManager{} - if err := filter.GetTypedConfig().UnmarshalTo(hcm); err == nil { - return hcm, nil - } - } - } - } - // This next check is deprecated in 1.6 and can be removed when we remove - // the old config_dumps in support of https://github.com/istio/istio/issues/23042 - if filter.Verify(listenerTyped) { - sockAddr := listenerTyped.Address.GetSocketAddress() - if sockAddr != nil { - // Skip outbound listeners - if sockAddr.Address == "0.0.0.0" { - continue - } - } - - for _, filterChain := range listenerTyped.FilterChains { - for _, filter := range filterChain.Filters { - hcm := &http_conn.HttpConnectionManager{} - if err := filter.GetTypedConfig().UnmarshalTo(hcm); err == nil { - return hcm, nil - } - } - } - } - } - - return nil, nil -} - -// getIstioConfigNameForSvc returns name, namespace -func getIstioVirtualServiceNameForSvc(cd *configdump.Wrapper, svc v1.Service, port int32) (string, string, error) { - path, err := getIstioVirtualServicePathForSvcFromRoute(cd, svc, port) - if err != nil { - return "", "", err - } - - // Starting with recent 1.5.0 builds, the path will include .istio.io. Handle both. - // nolint: gosimple - re := regexp.MustCompile("/apis/networking(\\.istio\\.io)?/v1alpha3/namespaces/(?P[^/]+)/virtual-service/(?P[^/]+)") - ss := re.FindStringSubmatch(path) - if ss == nil { - return "", "", fmt.Errorf("not a VS path: %s", path) - } - return ss[3], ss[2], nil -} - -// getIstioVirtualServicePathForSvcFromRoute returns something like "/apis/networking/v1alpha3/namespaces/default/virtual-service/reviews" -func getIstioVirtualServicePathForSvcFromRoute(cd *configdump.Wrapper, svc v1.Service, port int32) (string, error) { - sPort := strconv.Itoa(int(port)) - - // Routes know their destination Service name, namespace, and port, and the DR that configures them - rcd, err := cd.GetDynamicRouteDump(false) - if err != nil { - return "", err - } - for _, rcd := range rcd.DynamicRouteConfigs { - routeTyped := &route.RouteConfiguration{} - err = rcd.RouteConfig.UnmarshalTo(routeTyped) - if err != nil { - return "", err - } - if routeTyped.Name != sPort && !strings.HasPrefix(routeTyped.Name, "http.") && - !strings.HasPrefix(routeTyped.Name, "https.") { - continue - } - - for _, vh := range routeTyped.VirtualHosts { - for _, route := range vh.Routes { - if routeDestinationMatchesSvc(route, svc, vh, port) { - return getIstioConfig(route.Metadata) - } - } - } - } - return "", nil -} - -// routeDestinationMatchesSvc determines whether or not to use this service as a destination -func routeDestinationMatchesSvc(vhRoute *route.Route, svc v1.Service, vh *route.VirtualHost, port int32) bool { - if vhRoute == nil { - return false - } - - // Infer from VirtualHost domains matching ..svc.cluster.local - re := regexp.MustCompile(`(?P[^\.]+)\.(?P[^\.]+)\.svc\.cluster\.local$`) - for _, domain := range vh.Domains { - ss := re.FindStringSubmatch(domain) - if ss != nil { - if ss[1] == svc.ObjectMeta.Name && ss[2] == svc.ObjectMeta.Namespace { - return true - } - } - } - - clusterName := "" - switch cs := vhRoute.GetRoute().GetClusterSpecifier().(type) { - case *route.RouteAction_Cluster: - clusterName = cs.Cluster - case *route.RouteAction_WeightedClusters: - clusterName = cs.WeightedClusters.Clusters[0].GetName() - } - - // If this is an ingress gateway, the Domains will be something like *:80, so check routes - // which will look like "outbound|9080||productpage.default.svc.cluster.local" - res := fmt.Sprintf(`outbound\|%d\|[^\|]*\|(?P[^\.]+)\.(?P[^\.]+)\.svc\.cluster\.local$`, port) - re = regexp.MustCompile(res) - - ss := re.FindStringSubmatch(clusterName) - if ss != nil { - if ss[1] == svc.ObjectMeta.Name && ss[2] == svc.ObjectMeta.Namespace { - return true - } - } - - return false -} - -// getIstioConfig returns .metadata.filter_metadata.istio.config, err -func getIstioConfig(metadata *envoy_api_core.Metadata) (string, error) { - if metadata != nil { - istioConfig := asMyProtoValue(metadata.FilterMetadata[util.IstioMetadataKey]). - keyAsString("config") - return istioConfig, nil - } - return "", fmt.Errorf("no istio config") -} - -// getIstioConfigNameForSvc returns name, namespace -func getIstioDestinationRuleNameForSvc(cd *configdump.Wrapper, svc v1.Service, port int32) (string, string, error) { - path, err := getIstioDestinationRulePathForSvc(cd, svc, port) - if err != nil || path == "" { - return "", "", err - } - - // Starting with recent 1.5.0 builds, the path will include .istio.io. Handle both. - // nolint: gosimple - re := regexp.MustCompile("/apis/networking(\\.istio\\.io)?/v1alpha3/namespaces/(?P[^/]+)/destination-rule/(?P[^/]+)") - ss := re.FindStringSubmatch(path) - if ss == nil { - return "", "", fmt.Errorf("not a DR path: %s", path) - } - return ss[3], ss[2], nil -} - -// getIstioDestinationRulePathForSvc returns something like "/apis/networking/v1alpha3/namespaces/default/destination-rule/reviews" -func getIstioDestinationRulePathForSvc(cd *configdump.Wrapper, svc v1.Service, port int32) (string, error) { - svcHost := extendFQDN(fmt.Sprintf("%s.%s", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace)) - filter := istio_envoy_configdump.ClusterFilter{ - FQDN: host.Name(svcHost), - Port: int(port), - // Although we want inbound traffic, ask for outbound traffic, as the DR is - // not associated with the inbound traffic. - Direction: model.TrafficDirectionOutbound, - } - - dump, err := cd.GetClusterConfigDump() - if err != nil { - return "", err - } - - for _, dac := range dump.DynamicActiveClusters { - clusterTyped := &cluster.Cluster{} - // Support v2 or v3 in config dump. See ads.go:RequestedTypes for more info. - dac.Cluster.TypeUrl = v3.ClusterType - err = dac.Cluster.UnmarshalTo(clusterTyped) - if err != nil { - return "", err - } - if filter.Verify(clusterTyped) { - metadata := clusterTyped.Metadata - if metadata != nil { - istioConfig := asMyProtoValue(metadata.FilterMetadata[util.IstioMetadataKey]). - keyAsString("config") - return istioConfig, nil - } - } - } - - return "", nil -} - -// TODO simplify this by showing for each matching Destination the negation of the previous HttpMatchRequest -// and showing the non-matching Destinations. (The current code is ad-hoc, and usually shows most of that information.) -func printVirtualService(writer io.Writer, vs *clientnetworking.VirtualService, svc v1.Service, matchingSubsets []string, nonmatchingSubsets []string, dr *clientnetworking.DestinationRule) { // nolint: lll - fmt.Fprintf(writer, "VirtualService: %s\n", kname(vs.ObjectMeta)) - - // There is no point in checking that 'port' uses HTTP (for HTTP route matches) - // or uses TCP (for TCP route matches) because if the port has the wrong name - // the VirtualService metadata will not appear. - - matches := 0 - facts := 0 - mismatchNotes := []string{} - for _, httpRoute := range vs.Spec.Http { - routeMatch, newfacts := httpRouteMatchSvc(vs, httpRoute, svc, matchingSubsets, nonmatchingSubsets, dr) - if routeMatch { - matches++ - for _, newfact := range newfacts { - fmt.Fprintf(writer, " %s\n", newfact) - facts++ - } - } else { - mismatchNotes = append(mismatchNotes, newfacts...) - } - } - - // TODO vsSpec.Tls if I can find examples in the wild - - for _, tcpRoute := range vs.Spec.Tcp { - routeMatch, newfacts := tcpRouteMatchSvc(vs, tcpRoute, svc) - if routeMatch { - matches++ - for _, newfact := range newfacts { - fmt.Fprintf(writer, " %s\n", newfact) - facts++ - } - } else { - mismatchNotes = append(mismatchNotes, newfacts...) - } - } - - if matches == 0 { - if len(vs.Spec.Http) > 0 { - fmt.Fprintf(writer, " WARNING: No destinations match pod subsets (checked %d HTTP routes)\n", len(vs.Spec.Http)) - } - if len(vs.Spec.Tcp) > 0 { - fmt.Fprintf(writer, " WARNING: No destinations match pod subsets (checked %d TCP routes)\n", len(vs.Spec.Tcp)) - } - for _, mismatch := range mismatchNotes { - fmt.Fprintf(writer, " %s\n", mismatch) - } - return - } - - possibleDests := len(vs.Spec.Http) + len(vs.Spec.Tls) + len(vs.Spec.Tcp) - if matches < possibleDests { - // We've printed the match conditions. We can't say for sure that matching - // traffic will reach this pod, because an earlier match condition could have captured it. - fmt.Fprintf(writer, " %d additional destination(s) that will not reach this pod\n", possibleDests-matches) - // If we matched, but printed nothing, treat this as the catch-all - if facts == 0 { - for _, mismatch := range mismatchNotes { - fmt.Fprintf(writer, " %s\n", mismatch) - } - } - - return - } - - if facts == 0 { - // We printed nothing other than the name. Print something. - if len(vs.Spec.Http) > 0 { - fmt.Fprintf(writer, " %d HTTP route(s)\n", len(vs.Spec.Http)) - } - if len(vs.Spec.Tcp) > 0 { - fmt.Fprintf(writer, " %d TCP route(s)\n", len(vs.Spec.Tcp)) - } - } -} - -func printIngressInfo(writer io.Writer, matchingServices []v1.Service, podsLabels []k8s_labels.Set, kubeClient kubernetes.Interface, configClient istioclient.Interface, client kube.ExtendedClient) error { // nolint: lll - - pods, err := kubeClient.CoreV1().Pods(istioNamespace).List(context.TODO(), metav1.ListOptions{ - LabelSelector: "istio=ingressgateway", - FieldSelector: "status.phase=Running", - }) - if err != nil { - return multierror.Prefix(err, "Could not find ingress gateway pods") - } - if len(pods.Items) == 0 { - fmt.Fprintf(writer, "Skipping Gateway information (no ingress gateway pods)\n") - return nil - } - pod := pods.Items[0] - - // Currently no support for non-standard gateways selecting non ingressgateway pods - ingressSvcs, err := kubeClient.CoreV1().Services(istioNamespace).List(context.TODO(), metav1.ListOptions{ - LabelSelector: "istio=ingressgateway", - }) - if err != nil { - return multierror.Prefix(err, "Could not find ingress gateway service") - } - if len(ingressSvcs.Items) == 0 { - return fmt.Errorf("no ingress gateway service") - } - byConfigDump, err := client.EnvoyDo(context.TODO(), pod.Name, pod.Namespace, "GET", "config_dump") - if err != nil { - return fmt.Errorf("failed to execute command on ingress gateway sidecar: %v", err) - } - - cd := configdump.Wrapper{} - err = cd.UnmarshalJSON(byConfigDump) - if err != nil { - return fmt.Errorf("can't parse ingress gateway sidecar config_dump: %v", err) - } - - ipIngress := getIngressIP(ingressSvcs.Items[0], pod) - - for row, svc := range matchingServices { - for _, port := range svc.Spec.Ports { - matchingSubsets := []string{} - nonmatchingSubsets := []string{} - drName, drNamespace, err := getIstioDestinationRuleNameForSvc(&cd, svc, port.Port) - var dr *clientnetworking.DestinationRule - if err == nil && drName != "" && drNamespace != "" { - dr, _ = configClient.NetworkingV1alpha3().DestinationRules(drNamespace).Get(context.Background(), drName, metav1.GetOptions{}) - if dr != nil { - matchingSubsets, nonmatchingSubsets = getDestRuleSubsets(dr.Spec.Subsets, podsLabels) - } else { - fmt.Fprintf(writer, - "WARNING: Proxy is stale; it references to non-existent destination rule %s.%s\n", - drName, drNamespace) - } - } - - vsName, vsNamespace, err := getIstioVirtualServiceNameForSvc(&cd, svc, port.Port) - if err == nil && vsName != "" && vsNamespace != "" { - vs, _ := configClient.NetworkingV1alpha3().VirtualServices(vsNamespace).Get(context.Background(), vsName, metav1.GetOptions{}) - if vs != nil { - if row == 0 { - fmt.Fprintf(writer, "\n") - } else { - fmt.Fprintf(writer, "--------------------\n") - } - - printIngressService(writer, &ingressSvcs.Items[0], &pod, ipIngress) - printVirtualService(writer, vs, svc, matchingSubsets, nonmatchingSubsets, dr) - } else { - fmt.Fprintf(writer, - "WARNING: Proxy is stale; it references to non-existent virtual service %s.%s\n", - vsName, vsNamespace) - } - } - } - } - - return nil -} - -func printIngressService(writer io.Writer, ingressSvc *v1.Service, ingressPod *v1.Pod, ip string) { - // The ingressgateway service offers a lot of ports but the pod doesn't listen to all - // of them. For example, it doesn't listen on 443 without additional setup. This prints - // the most basic output. - portsToShow := map[string]bool{ - "http2": true, - } - protocolToScheme := map[string]string{ - "HTTP2": "http", - } - schemePortDefault := map[string]int{ - "http": 80, - } - - for _, port := range ingressSvc.Spec.Ports { - if port.Protocol != "TCP" || !portsToShow[port.Name] { - continue - } - - // Get port number - _, err := pilotcontroller.FindPort(ingressPod, &port) - if err == nil { - nport := int(port.Port) - protocol := string(configKube.ConvertProtocol(port.Port, port.Name, port.Protocol, port.AppProtocol)) - - scheme := protocolToScheme[protocol] - portSuffix := "" - if schemePortDefault[scheme] != nport { - portSuffix = fmt.Sprintf(":%d", nport) - } - fmt.Fprintf(writer, "\nExposed on Ingress Gateway %s://%s%s\n", scheme, ip, portSuffix) - } - } -} - -func getIngressIP(service v1.Service, pod v1.Pod) string { - if len(service.Status.LoadBalancer.Ingress) > 0 { - return service.Status.LoadBalancer.Ingress[0].IP - } - - if pod.Status.HostIP != "" { - return pod.Status.HostIP - } - - // The scope of this function is to get the IP from Kubernetes, we do not - // ask Docker or minikube for an IP. - // See https://istio.io/docs/tasks/traffic-management/ingress/ingress-control/#determining-the-ingress-ip-and-ports - - return "unknown" -} - -func svcDescribeCmd() *cobra.Command { - var opts clioptions.ControlPlaneOptions - cmd := &cobra.Command{ - Use: "service ", - Aliases: []string{"svc"}, - Short: "Describe services and their Istio configuration [kube-only]", - Long: `Analyzes service, pods, DestinationRules, and VirtualServices and reports -the configuration objects that affect that service.`, - Example: ` istioctl experimental describe service productpage`, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) != 1 { - cmd.Println(cmd.UsageString()) - return fmt.Errorf("expecting service name") - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - svcName, ns := handlers.InferPodInfo(args[0], handlers.HandleNamespace(namespace, defaultNamespace)) - - client, err := interfaceFactory(kubeconfig) - if err != nil { - return err - } - svc, err := client.CoreV1().Services(ns).Get(context.TODO(), svcName, metav1.GetOptions{}) - if err != nil { - return err - } - - writer := cmd.OutOrStdout() - - pods, err := client.CoreV1().Pods(ns).List(context.TODO(), metav1.ListOptions{}) - if err != nil { - return err - } - - matchingPods := []v1.Pod{} - selectedPodCount := 0 - if len(svc.Spec.Selector) > 0 { - svcSelector := k8s_labels.SelectorFromSet(svc.Spec.Selector) - for _, pod := range pods.Items { - if svcSelector.Matches(k8s_labels.Set(pod.ObjectMeta.Labels)) { - selectedPodCount++ - - if pod.Status.Phase != v1.PodRunning { - fmt.Printf(" Pod is not %s (%s)\n", v1.PodRunning, pod.Status.Phase) - continue - } - - ready, err := containerReady(&pod, proxyContainerName) - if err != nil { - fmt.Fprintf(writer, "Pod %s: %s\n", kname(pod.ObjectMeta), err) - continue - } - if !ready { - fmt.Fprintf(writer, "WARNING: Pod %s Container %s NOT READY\n", kname(pod.ObjectMeta), proxyContainerName) - continue - } - - matchingPods = append(matchingPods, pod) - } - } - } - - if len(matchingPods) == 0 { - if selectedPodCount == 0 { - fmt.Fprintf(writer, "Service %q has no pods.\n", kname(svc.ObjectMeta)) - return nil - } - fmt.Fprintf(writer, "Service %q has no Istio pods. (%d pods in service).\n", kname(svc.ObjectMeta), selectedPodCount) - fmt.Fprintf(writer, "Use `istioctl experimental add-to-mesh`, `istioctl kube-inject`, or redeploy with Istio automatic sidecar injection.\n") - return nil - } - - kubeClient, err := kubeClientWithRevision(kubeconfig, configContext, opts.Revision) - if err != nil { - return err - } - - var configClient istioclient.Interface - if configClient, err = configStoreFactory(); err != nil { - return err - } - - // Get all the labels for all the matching pods. We will used this to complain - // if NONE of the pods match a VirtualService - podsLabels := make([]k8s_labels.Set, len(matchingPods)) - for i, pod := range matchingPods { - podsLabels[i] = k8s_labels.Set(pod.ObjectMeta.Labels) - } - - // Describe based on the Envoy config for this first pod only - pod := matchingPods[0] - - // Only consider the service invoked with this command, not other services that might select the pod - svcs := []v1.Service{*svc} - - err = describePodServices(writer, kubeClient, configClient, &pod, svcs, podsLabels) - if err != nil { - return err - } - - // Now look for ingress gateways - return printIngressInfo(writer, svcs, podsLabels, client, configClient, kubeClient) - }, - ValidArgsFunction: validServiceArgs, - } - - cmd.PersistentFlags().BoolVar(&ignoreUnmeshed, "ignoreUnmeshed", false, - "Suppress warnings for unmeshed pods") - cmd.Long += "\n\n" + ExperimentalMsg - return cmd -} - -func describePodServices(writer io.Writer, kubeClient kube.ExtendedClient, configClient istioclient.Interface, pod *v1.Pod, matchingServices []v1.Service, podsLabels []k8s_labels.Set) error { // nolint: lll - byConfigDump, err := kubeClient.EnvoyDo(context.TODO(), pod.ObjectMeta.Name, pod.ObjectMeta.Namespace, "GET", "config_dump") - if err != nil { - if ignoreUnmeshed { - return nil - } - - return fmt.Errorf("failed to execute command on sidecar: %v", err) - } - - cd := configdump.Wrapper{} - err = cd.UnmarshalJSON(byConfigDump) - if err != nil { - return fmt.Errorf("can't parse sidecar config_dump for %v: %v", err, pod.ObjectMeta.Name) - } - - for row, svc := range matchingServices { - if row != 0 { - fmt.Fprintf(writer, "--------------------\n") - } - printService(writer, svc, pod) - - for _, port := range svc.Spec.Ports { - matchingSubsets := []string{} - nonmatchingSubsets := []string{} - drName, drNamespace, err := getIstioDestinationRuleNameForSvc(&cd, svc, port.Port) - if err != nil { - log.Errorf("fetch destination rule for %v: %v", svc.Name, err) - } - var dr *clientnetworking.DestinationRule - if err == nil && drName != "" && drNamespace != "" { - dr, _ = configClient.NetworkingV1alpha3().DestinationRules(drNamespace).Get(context.Background(), drName, metav1.GetOptions{}) - if dr != nil { - if len(svc.Spec.Ports) > 1 { - // If there is more than one port, prefix each DR by the port it applies to - fmt.Fprintf(writer, "%d ", port.Port) - } - printDestinationRule(writer, dr, podsLabels) - matchingSubsets, nonmatchingSubsets = getDestRuleSubsets(dr.Spec.Subsets, podsLabels) - } else { - fmt.Fprintf(writer, - "WARNING: Proxy is stale; it references to non-existent destination rule %s.%s\n", - drName, drNamespace) - } - } - - vsName, vsNamespace, err := getIstioVirtualServiceNameForSvc(&cd, svc, port.Port) - if err == nil && vsName != "" && vsNamespace != "" { - vs, _ := configClient.NetworkingV1alpha3().VirtualServices(vsNamespace).Get(context.Background(), vsName, metav1.GetOptions{}) - if vs != nil { - if len(svc.Spec.Ports) > 1 { - // If there is more than one port, prefix each DR by the port it applies to - fmt.Fprintf(writer, "%d ", port.Port) - } - printVirtualService(writer, vs, svc, matchingSubsets, nonmatchingSubsets, dr) - } else { - fmt.Fprintf(writer, - "WARNING: Proxy is stale; it references to non-existent virtual service %s.%s\n", - vsName, vsNamespace) - } - } - - policies, err := getIstioRBACPolicies(&cd, port.Port) - if err != nil { - log.Errorf("error getting rbac policies: %v", err) - } - if len(policies) > 0 { - if len(svc.Spec.Ports) > 1 { - // If there is more than one port, prefix each DR by the port it applies to - fmt.Fprintf(writer, "%d ", port.Port) - } - - fmt.Fprintf(writer, "RBAC policies: %s\n", strings.Join(policies, ", ")) - } - } - } - - return nil -} - -func containerReady(pod *v1.Pod, containerName string) (bool, error) { - for _, containerStatus := range pod.Status.ContainerStatuses { - if containerStatus.Name == containerName { - return containerStatus.Ready, nil - } - } - return false, fmt.Errorf("no container %q in pod", containerName) -} - -// describePeerAuthentication fetches all PeerAuthentication in workload and root namespace. -// It lists the ones applied to the pod, and the current active mTLS mode. -// When the client doesn't have access to root namespace, it will only show workload namespace Peerauthentications. -func describePeerAuthentication(writer io.Writer, kubeClient kube.ExtendedClient, configClient istioclient.Interface, workloadNamespace string, podsLabels k8s_labels.Set) error { // nolint: lll - meshCfg, err := getMeshConfig(kubeClient) - if err != nil { - return fmt.Errorf("failed to fetch mesh config: %v", err) - } - - workloadPAList, err := configClient.SecurityV1beta1().PeerAuthentications(workloadNamespace).List(context.Background(), metav1.ListOptions{}) - if err != nil { - return fmt.Errorf("failed to fetch workload namespace PeerAuthentication: %v", err) - } - - rootPAList, err := configClient.SecurityV1beta1().PeerAuthentications(meshCfg.RootNamespace).List(context.Background(), metav1.ListOptions{}) - if err != nil { - return fmt.Errorf("failed to fetch root namespace PeerAuthentication: %v", err) - } - - allPAs := append(rootPAList.Items, workloadPAList.Items...) - - var cfgs []*config.Config - for _, pa := range allPAs { - pa := pa - cfg := crdclient.TranslateObject(pa, config.GroupVersionKind(pa.GroupVersionKind()), "") - cfgs = append(cfgs, &cfg) - } - - matchedPA := findMatchedConfigs(podsLabels, cfgs) - effectivePA := authnv1beta1.ComposePeerAuthentication(meshCfg.RootNamespace, matchedPA) - printPeerAuthentication(writer, effectivePA) - if len(matchedPA) != 0 { - printConfigs(writer, matchedPA) - } - - return nil -} - -// Workloader is used for matching all configs -type Workloader interface { - GetSelector() *typev1beta1.WorkloadSelector -} - -// findMatchedConfigs should filter out unrelated configs that are not matched given podsLabels. -// When the config has no selector labels, this method will treat it as qualified namespace level -// config. So configs passed into this method should only contains workload's namespaces configs -// and rootNamespaces configs, caller should be responsible for controlling configs passed -// in. -func findMatchedConfigs(podsLabels k8s_labels.Set, configs []*config.Config) []*config.Config { - var cfgs []*config.Config - - for _, cfg := range configs { - cfg := cfg - labels := cfg.Spec.(Workloader).GetSelector().GetMatchLabels() - selector := k8s_labels.SelectorFromSet(labels) - if selector.Matches(podsLabels) { - cfgs = append(cfgs, cfg) - } - } - - return cfgs -} - -// printConfig prints the applied configs based on the member's type. -// When there is the array is empty, caller should make sure the intended -// log is handled in their methods. -func printConfigs(writer io.Writer, configs []*config.Config) { - if len(configs) == 0 { - return - } - fmt.Fprintf(writer, "Applied %s:\n", configs[0].Meta.GroupVersionKind.Kind) - var cfgNames string - for i, cfg := range configs { - cfgNames += cfg.Meta.Name + "." + cfg.Meta.Namespace - if i < len(configs)-1 { - cfgNames += ", " - } - } - fmt.Fprintf(writer, " %s\n", cfgNames) -} - -func printPeerAuthentication(writer io.Writer, pa *v1beta1.PeerAuthentication) { - fmt.Fprintf(writer, "Effective PeerAuthentication:\n") - fmt.Fprintf(writer, " Workload mTLS mode: %s\n", pa.Mtls.Mode.String()) - if len(pa.PortLevelMtls) != 0 { - fmt.Fprintf(writer, " Port Level mTLS mode:\n") - for port, mode := range pa.PortLevelMtls { - fmt.Fprintf(writer, " %d: %s\n", port, mode.Mode.String()) - } - } -} - -func getMeshConfig(kubeClient kube.ExtendedClient) (*meshconfig.MeshConfig, error) { - rev := kubeClient.Revision() - meshConfigMapName := defaultMeshConfigMapName - - // if the revision is not "default", render mesh config map name with revision - if rev != tag.DefaultRevisionName && rev != "" { - meshConfigMapName = fmt.Sprintf("%s-%s", defaultMeshConfigMapName, rev) - } - - meshConfigMap, err := kubeClient.CoreV1().ConfigMaps(istioNamespace).Get(context.TODO(), meshConfigMapName, metav1.GetOptions{}) - if err != nil { - return nil, fmt.Errorf("could not read configmap %q from namespace %q: %v", meshConfigMapName, istioNamespace, err) - } - - configYaml, ok := meshConfigMap.Data[defaultMeshConfigMapKey] - if !ok { - return nil, fmt.Errorf("missing config map key %q", defaultMeshConfigMapKey) - } - - cfg, err := mesh.ApplyMeshConfigDefaults(configYaml) - if err != nil { - return nil, fmt.Errorf("error parsing mesh config: %v", err) - } - - return cfg, nil -} diff --git a/istioctl/cmd/describe_test.go b/istioctl/cmd/describe_test.go deleted file mode 100644 index b7942985a..000000000 --- a/istioctl/cmd/describe_test.go +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "bytes" - "fmt" - "strings" - "testing" -) - -import ( - "github.com/google/go-cmp/cmp" - apiannotation "istio.io/api/annotation" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - k8s_labels "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/fake" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/configdump" - "github.com/apache/dubbo-go-pixiu/pilot/test/util" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -// execAndK8sConfigTestCase lets a test case hold some Envoy, Istio, and Kubernetes configuration -type execAndK8sConfigTestCase struct { - k8sConfigs []runtime.Object // Canned K8s configuration - namespace string - - args []string - - // Typically use one of the three - expectedOutput string // Expected constant output - expectedString string // String output is expected to contain - goldenFilename string // Expected output stored in golden file - - wantException bool -} - -// Tests Pilot /debug -func TestDescribe(t *testing.T) { - cases := []execAndK8sConfigTestCase{ - { // case 0 - args: strings.Split("experimental describe", " "), - expectedString: "Describe resource and related Istio configuration", - }, - { // case 1 short name 'i' - args: strings.Split("x des", " "), - expectedString: "Describe resource and related Istio configuration", - }, - { // case 2 no pod - args: strings.Split("experimental describe pod", " "), - expectedString: "Error: expecting pod name", - wantException: true, // "istioctl experimental inspect pod" should fail - }, - { // case 3 unknown pod - args: strings.Split("experimental describe po not-a-pod", " "), - expectedString: "pods \"not-a-pod\" not found", - wantException: true, // "istioctl experimental describe pod not-a-pod" should fail - }, - { // case 8 unknown service - args: strings.Split("experimental describe service not-a-service", " "), - expectedString: "services \"not-a-service\" not found", - wantException: true, // "istioctl experimental describe service not-a-service" should fail - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("case %d %s", i, strings.Join(c.args, " ")), func(t *testing.T) { - verifyExecAndK8sConfigTestCaseTestOutput(t, c) - }) - } -} - -func TestGetRevisionFromPodAnnotation(t *testing.T) { - cases := []struct { - anno k8s_labels.Set - - expected string - }{ - { - anno: k8s_labels.Set{ - apiannotation.SidecarStatus.Name: "", - }, - expected: "", - }, - { - anno: k8s_labels.Set{}, - expected: "", - }, - { - anno: k8s_labels.Set{ - apiannotation.SidecarStatus.Name: ` - { - "initContainers": [ - "istio-init" - ], - "containers": [ - "istio-proxy" - ], - "volumes": [ - "istio-envoy", - "istio-data", - "istio-podinfo", - "istio-token", - "istiod-ca-cert" - ], - "imagePullSecrets": null, - "revision": "1-13-2" - }`, - }, - expected: "1-13-2", - }, - } - - for _, tc := range cases { - t.Run("", func(t *testing.T) { - got := getRevisionFromPodAnnotation(tc.anno) - assert.Equal(t, tc.expected, got) - }) - } -} - -func TestFindProtocolForPort(t *testing.T) { - http := "HTTP" - cases := []struct { - port v1.ServicePort - expectedProtocol string - }{ - { - port: v1.ServicePort{ - Name: "http", - Protocol: v1.ProtocolTCP, - }, - expectedProtocol: "HTTP", - }, - { - port: v1.ServicePort{ - Name: "GRPC-port", - Protocol: v1.ProtocolTCP, - }, - expectedProtocol: "GRPC", - }, - { - port: v1.ServicePort{ - AppProtocol: &http, - Protocol: v1.ProtocolTCP, - }, - expectedProtocol: "HTTP", - }, - { - port: v1.ServicePort{ - Protocol: v1.ProtocolTCP, - Port: 80, - }, - expectedProtocol: "auto-detect", - }, - { - port: v1.ServicePort{ - Protocol: v1.ProtocolUDP, - Port: 80, - }, - expectedProtocol: "UDP", - }, - } - - for _, tc := range cases { - protocol := findProtocolForPort(&tc.port) - if protocol != tc.expectedProtocol { - t.Fatalf("Output didn't match for the port protocol: got %s want %s", protocol, tc.expectedProtocol) - } - } -} - -func verifyExecAndK8sConfigTestCaseTestOutput(t *testing.T, c execAndK8sConfigTestCase) { - t.Helper() - - // Override the Istio config factory - configStoreFactory = mockClientFactoryGenerator() - - // Override the K8s config factory - interfaceFactory = mockInterfaceFactoryGenerator(c.k8sConfigs) - - var out bytes.Buffer - rootCmd := GetRootCmd(c.args) - rootCmd.SetOut(&out) - rootCmd.SetErr(&out) - if c.namespace != "" { - namespace = c.namespace - } - - fErr := rootCmd.Execute() - output := out.String() - - if c.expectedOutput != "" && c.expectedOutput != output { - t.Fatalf("Unexpected output for 'istioctl %s'\n got: %q\nwant: %q", strings.Join(c.args, " "), output, c.expectedOutput) - } - - if c.expectedString != "" && !strings.Contains(output, c.expectedString) { - t.Fatalf("Output didn't match for 'istioctl %s'\n got %v\nwant: %v", strings.Join(c.args, " "), output, c.expectedString) - } - - if c.goldenFilename != "" { - util.CompareContent(t, []byte(output), c.goldenFilename) - } - - if c.wantException { - if fErr == nil { - t.Fatalf("Wanted an exception for 'istioctl %s', didn't get one, output was %q", - strings.Join(c.args, " "), output) - } - } else { - if fErr != nil { - t.Fatalf("Unwanted exception for 'istioctl %s': %v", strings.Join(c.args, " "), fErr) - } - } -} - -func mockInterfaceFactoryGenerator(k8sConfigs []runtime.Object) func(kubeconfig string) (kubernetes.Interface, error) { - outFactory := func(_ string) (kubernetes.Interface, error) { - client := fake.NewSimpleClientset(k8sConfigs...) - return client, nil - } - - return outFactory -} - -func TestGetIstioVirtualServicePathForSvcFromRoute(t *testing.T) { - tests := []struct { - name string - inputConfig string - inputService v1.Service - inputPort int32 - expected string - }{ - { - name: "test tls config", - inputConfig: "testdata/describe/tls_config.json", - inputService: v1.Service{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "productpage", - Namespace: "default", - }, - Spec: v1.ServiceSpec{}, - Status: v1.ServiceStatus{}, - }, - inputPort: int32(9080), - expected: "/apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/bookinfo", - }, - { - name: "test http config", - inputConfig: "testdata/describe/http_config.json", - inputService: v1.Service{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "productpage", - Namespace: "default", - }, - Spec: v1.ServiceSpec{}, - Status: v1.ServiceStatus{}, - }, - inputPort: int32(9080), - expected: "/apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/bookinfo", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - ic, err := readFile(test.inputConfig) - if err != nil { - t.Fatalf("unable to open file in directory: %s", test.inputConfig) - } - cd := configdump.Wrapper{} - err = cd.UnmarshalJSON(ic) - if err != nil { - t.Fatal(err) - } - out, err := getIstioVirtualServicePathForSvcFromRoute(&cd, test.inputService, test.inputPort) - if err != nil { - t.Fatal(err) - } - if diff := cmp.Diff(test.expected, out); diff != "" { - t.Fatalf("Diff:\n%s", diff) - } - }) - } -} diff --git a/istioctl/cmd/google.go b/istioctl/cmd/google.go deleted file mode 100644 index a43fbef4b..000000000 --- a/istioctl/cmd/google.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "fmt" - "net/http" - "strings" -) - -import ( - "golang.org/x/oauth2" - "golang.org/x/oauth2/google" -) - -func isMCPAddr(addr string) bool { - // A bit inexact but should be good enough. - return strings.Contains(addr, ".googleapis.com/") || strings.Contains(addr, ".googleapis.com:443/") -} - -func mcpTransport(ctx context.Context, tr http.RoundTripper) (http.RoundTripper, error) { - creds, err := google.FindDefaultCredentials(ctx, "https://www.googleapis.com/auth/cloud-platform") - if err != nil { - return nil, fmt.Errorf("finding default GCP credentials: %w", err) - } - return &oauth2.Transport{ - Base: tr, - Source: creds.TokenSource, - }, nil -} diff --git a/istioctl/cmd/injector-list.go b/istioctl/cmd/injector-list.go deleted file mode 100644 index 8fa0097f5..000000000 --- a/istioctl/cmd/injector-list.go +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "fmt" - "io" - "sort" - "strconv" - "strings" - "text/tabwriter" -) - -import ( - "github.com/spf13/cobra" - "istio.io/api/annotation" - "istio.io/api/label" - admit_v1 "k8s.io/api/admissionregistration/v1" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - api_pkg_labels "k8s.io/apimachinery/pkg/labels" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/clioptions" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/injection" - analyzer_util "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -type revisionCount struct { - // pods in a revision - pods int - // pods that are disabled from injection - disabled int - // pods that are enabled for injection, but whose revision doesn't match their namespace's revision - needsRestart int -} - -func injectorCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "injector", - Short: "List sidecar injector and sidecar versions", - Long: `List sidecar injector and sidecar versions`, - Example: ` istioctl experimental injector list`, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) != 0 { - return fmt.Errorf("unknown subcommand %q", args[0]) - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - cmd.HelpFunc()(cmd, args) - return nil - }, - } - - cmd.AddCommand(injectorListCommand()) - return cmd -} - -func injectorListCommand() *cobra.Command { - var opts clioptions.ControlPlaneOptions - cmd := &cobra.Command{ - Use: "list", - Short: "List sidecar injector and sidecar versions", - Long: `List sidecar injector and sidecar versions`, - Example: ` istioctl experimental injector list`, - RunE: func(cmd *cobra.Command, args []string) error { - client, err := kubeClientWithRevision(kubeconfig, configContext, opts.Revision) - if err != nil { - return fmt.Errorf("failed to create k8s client: %v", err) - } - ctx := context.Background() - - nslist, err := getNamespaces(ctx, client) - if err != nil { - return err - } - sort.Slice(nslist, func(i, j int) bool { - return nslist[i].Name < nslist[j].Name - }) - - hooks, err := getWebhooks(ctx, client) - if err != nil { - return err - } - pods, err := getPods(ctx, client) - if err != nil { - return err - } - err = printNS(cmd.OutOrStdout(), nslist, hooks, pods) - if err != nil { - return err - } - cmd.Println() - injectedImages, err := getInjectedImages(ctx, client) - if err != nil { - return err - } - - sort.Slice(hooks, func(i, j int) bool { - return hooks[i].Name < hooks[j].Name - }) - return printHooks(cmd.OutOrStdout(), nslist, hooks, injectedImages) - }, - } - - return cmd -} - -func getNamespaces(ctx context.Context, client kube.ExtendedClient) ([]v1.Namespace, error) { - nslist, err := client.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) - if err != nil { - return []v1.Namespace{}, err - } - return nslist.Items, nil -} - -func printNS(writer io.Writer, namespaces []v1.Namespace, hooks []admit_v1.MutatingWebhookConfiguration, - allPods map[resource.Namespace][]v1.Pod) error { - outputCount := 0 - - w := new(tabwriter.Writer).Init(writer, 0, 8, 1, ' ', 0) - for _, namespace := range namespaces { - if hideFromOutput(resource.Namespace(namespace.Name)) { - continue - } - - revision := getInjectedRevision(&namespace, hooks) - podCount := podCountByRevision(allPods[resource.Namespace(namespace.Name)], revision) - if len(podCount) == 0 { - // This namespace has no pods, but we wish to display it if new pods will be auto-injected - if revision != "" { - podCount[revision] = revisionCount{} - } - } - for injectedRevision, count := range podCount { - if outputCount == 0 { - fmt.Fprintln(w, "NAMESPACE\tISTIO-REVISION\tPOD-REVISIONS") - } - outputCount++ - - fmt.Fprintf(w, "%s\t%s\t%s\n", namespace.Name, revision, renderCounts(injectedRevision, count)) - } - } - if outputCount == 0 { - fmt.Fprintf(writer, "No Istio injected namespaces present.\n") - } - - return w.Flush() -} - -func getWebhooks(ctx context.Context, client kube.ExtendedClient) ([]admit_v1.MutatingWebhookConfiguration, error) { - hooks, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().List(ctx, metav1.ListOptions{}) - if err != nil { - return []admit_v1.MutatingWebhookConfiguration{}, err - } - return hooks.Items, nil -} - -func printHooks(writer io.Writer, namespaces []v1.Namespace, hooks []admit_v1.MutatingWebhookConfiguration, injectedImages map[string]string) error { - if len(hooks) == 0 { - fmt.Fprintf(writer, "No Istio injection hooks present.\n") - return nil - } - - w := new(tabwriter.Writer).Init(writer, 0, 8, 1, ' ', 0) - fmt.Fprintln(w, "NAMESPACES\tINJECTOR-HOOK\tISTIO-REVISION\tSIDECAR-IMAGE") - for _, hook := range hooks { - revision := hook.ObjectMeta.GetLabels()[label.IoIstioRev.Name] - namespaces := getMatchingNamespaces(&hook, namespaces) - if len(namespaces) == 0 { - fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", "DOES NOT AUTOINJECT", hook.Name, revision, injectedImages[revision]) - continue - } - for _, namespace := range namespaces { - fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", namespace.Name, hook.Name, revision, injectedImages[revision]) - } - } - return w.Flush() -} - -func getInjector(namespace *v1.Namespace, hooks []admit_v1.MutatingWebhookConfiguration) *admit_v1.MutatingWebhookConfiguration { - // find matching hook - for _, hook := range hooks { - for _, webhook := range hook.Webhooks { - nsSelector, err := metav1.LabelSelectorAsSelector(webhook.NamespaceSelector) - if err != nil { - continue - } - if nsSelector.Matches(api_pkg_labels.Set(namespace.ObjectMeta.Labels)) { - return &hook - } - } - } - return nil -} - -func getInjectedRevision(namespace *v1.Namespace, hooks []admit_v1.MutatingWebhookConfiguration) string { - injector := getInjector(namespace, hooks) - if injector != nil { - return injector.ObjectMeta.GetLabels()[label.IoIstioRev.Name] - } - newRev := namespace.ObjectMeta.GetLabels()[label.IoIstioRev.Name] - oldLabel, ok := namespace.ObjectMeta.GetLabels()[analyzer_util.InjectionLabelName] - // If there is no istio-injection=disabled and no istio.io/rev, the namespace isn't injected - if newRev == "" && (ok && oldLabel == "disabled" || !ok) { - return "" - } - if newRev != "" { - return fmt.Sprintf("MISSING/%s", newRev) - } - return fmt.Sprintf("MISSING/%s", analyzer_util.InjectionLabelName) -} - -func getMatchingNamespaces(hook *admit_v1.MutatingWebhookConfiguration, namespaces []v1.Namespace) []v1.Namespace { - retval := make([]v1.Namespace, 0) - for _, webhook := range hook.Webhooks { - nsSelector, err := metav1.LabelSelectorAsSelector(webhook.NamespaceSelector) - if err != nil { - return retval - } - - for _, namespace := range namespaces { - if nsSelector.Matches(api_pkg_labels.Set(namespace.Labels)) { - retval = append(retval, namespace) - } - } - } - return retval -} - -func getPods(ctx context.Context, client kube.ExtendedClient) (map[resource.Namespace][]v1.Pod, error) { - retval := map[resource.Namespace][]v1.Pod{} - // All pods in all namespaces - pods, err := client.CoreV1().Pods("").List(ctx, metav1.ListOptions{}) - if err != nil { - return retval, err - } - for _, pod := range pods.Items { - podList, ok := retval[resource.Namespace(pod.GetNamespace())] - if !ok { - retval[resource.Namespace(pod.GetNamespace())] = []v1.Pod{pod} - } else { - retval[resource.Namespace(pod.GetNamespace())] = append(podList, pod) - } - } - return retval, nil -} - -// getInjectedImages() returns a map of revision->dockerimage -func getInjectedImages(ctx context.Context, client kube.ExtendedClient) (map[string]string, error) { - retval := map[string]string{} - - // All configs in all namespaces that are Istio revisioned - configMaps, err := client.CoreV1().ConfigMaps("").List(ctx, metav1.ListOptions{LabelSelector: label.IoIstioRev.Name}) - if err != nil { - return retval, err - } - - for _, configMap := range configMaps.Items { - image := injection.GetIstioProxyImage(&configMap) - if image != "" { - retval[configMap.ObjectMeta.GetLabels()[label.IoIstioRev.Name]] = image - } - } - - return retval, nil -} - -// podCountByRevision() returns a map of revision->pods, with "" as the dummy "revision" for uninjected pods -func podCountByRevision(pods []v1.Pod, expectedRevision string) map[string]revisionCount { - retval := map[string]revisionCount{} - for _, pod := range pods { - revision := pod.ObjectMeta.GetLabels()[label.IoIstioRev.Name] - revisionLabel := revision - if revision == "" { - revisionLabel = "" - } - counts := retval[revisionLabel] - counts.pods++ - if injectionDisabled(&pod) { - counts.disabled++ - } else if revision != expectedRevision { - counts.needsRestart++ - } - retval[revisionLabel] = counts - } - return retval -} - -func hideFromOutput(ns resource.Namespace) bool { - return (analyzer_util.IsSystemNamespace(ns) || ns == resource.Namespace(istioNamespace)) -} - -func injectionDisabled(pod *v1.Pod) bool { - inject := pod.ObjectMeta.GetAnnotations()[annotation.SidecarInject.Name] - return strings.EqualFold(inject, "false") -} - -func renderCounts(injectedRevision string, counts revisionCount) string { - if counts.pods == 0 { - return "" - } - - podText := strconv.Itoa(counts.pods) - if counts.disabled > 0 { - podText += fmt.Sprintf(" (injection disabled: %d)", counts.disabled) - } - if counts.needsRestart > 0 { - podText += fmt.Sprintf(" NEEDS RESTART: %d", counts.needsRestart) - } - return fmt.Sprintf("%s: %s", injectedRevision, podText) -} diff --git a/istioctl/cmd/internal-debug.go b/istioctl/cmd/internal-debug.go deleted file mode 100644 index 4639de425..000000000 --- a/istioctl/cmd/internal-debug.go +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "fmt" - "io" - "strings" -) - -import ( - envoy_corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - xdsapi "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "github.com/spf13/cobra" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/clioptions" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/multixds" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/writer/pilot" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -func HandlerForRetrieveDebugList(kubeClient kube.ExtendedClient, - centralOpts clioptions.CentralControlPlaneOptions, - writer io.Writer) (map[string]*xdsapi.DiscoveryResponse, error) { - var namespace, serviceAccount string - xdsRequest := xdsapi.DiscoveryRequest{ - ResourceNames: []string{"list"}, - Node: &envoy_corev3.Node{ - Id: "debug~0.0.0.0~istioctl~cluster.local", - }, - TypeUrl: v3.DebugType, - } - xdsResponses, respErr := multixds.AllRequestAndProcessXds(&xdsRequest, centralOpts, istioNamespace, - namespace, serviceAccount, kubeClient) - if respErr != nil { - return xdsResponses, respErr - } - _, _ = fmt.Fprint(writer, "error: according to below command list, please check all supported internal debug commands\n") - return xdsResponses, nil -} - -func HandlerForDebugErrors(kubeClient kube.ExtendedClient, - centralOpts *clioptions.CentralControlPlaneOptions, - writer io.Writer, - xdsResponses map[string]*xdsapi.DiscoveryResponse) (map[string]*xdsapi.DiscoveryResponse, error) { - for _, response := range xdsResponses { - for _, resource := range response.Resources { - eString := string(resource.Value) - switch { - case strings.Contains(eString, "You must provide a proxyID in the query string"): - return nil, fmt.Errorf(" You must provide a proxyID in the query string, e.g. [%s]", - "edsz?proxyID=istio-ingressgateway") - - case strings.Contains(eString, "404 page not found"): - return HandlerForRetrieveDebugList(kubeClient, *centralOpts, writer) - - case strings.Contains(eString, "querystring parameter 'resource' is required"): - return nil, fmt.Errorf("querystring parameter 'resource' is required, e.g. [%s]", - "config_distribution?resource=VirtualService/default/bookinfo") - } - } - } - return nil, nil -} - -func debugCommand() *cobra.Command { - var opts clioptions.ControlPlaneOptions - var centralOpts clioptions.CentralControlPlaneOptions - - debugCommand := &cobra.Command{ - Use: "internal-debug [/][.]", - Short: "Retrieves the debug information of istio", - Long: ` -Retrieves the debug information from Istiod or Pods in the mesh using the service account from the pod if --cert-dir is empty. -By default it will use the default serviceAccount from (dubbo-system) namespace if the pod is not specified. -`, - Example: ` # Retrieve sync status for all Envoys in a mesh - istioctl x internal-debug syncz - - # Retrieve sync diff for a single Envoy and Istiod - istioctl x internal-debug syncz istio-egressgateway-59585c5b9c-ndc59.dubbo-system - - # SECURITY OPTIONS - - # Retrieve syncz debug information directly from the control plane, using token security - # (This is the usual way to get the debug information with an out-of-cluster control plane.) - istioctl x internal-debug syncz --xds-address istio.cloudprovider.example.com:15012 - - # Retrieve syncz debug information via Kubernetes config, using token security - # (This is the usual way to get the debug information with an in-cluster control plane.) - istioctl x internal-debug syncz - - # Retrieve syncz debug information directly from the control plane, using RSA certificate security - # (Certificates must be obtained before this step. The --cert-dir flag lets istioctl bypass the Kubernetes API server.) - istioctl x internal-debug syncz --xds-address istio.example.com:15012 --cert-dir ~/.istio-certs - - # Retrieve syncz information via XDS from specific control plane in multi-control plane in-cluster configuration - # (Select a specific control plane in an in-cluster canary Istio configuration.) - istioctl x internal-debug syncz --xds-label istio.io/rev=default -`, - RunE: func(c *cobra.Command, args []string) error { - kubeClient, err := kubeClientWithRevision(kubeconfig, configContext, opts.Revision) - if err != nil { - return err - } - if len(args) == 0 { - return CommandParseError{ - e: fmt.Errorf("debug type is required"), - } - } - var xdsRequest xdsapi.DiscoveryRequest - var namespace, serviceAccount string - - xdsRequest = xdsapi.DiscoveryRequest{ - ResourceNames: []string{args[0]}, - Node: &envoy_corev3.Node{ - Id: "debug~0.0.0.0~istioctl~cluster.local", - }, - TypeUrl: v3.DebugType, - } - - xdsResponses, err := multixds.MultiRequestAndProcessXds(internalDebugAllIstiod, &xdsRequest, centralOpts, istioNamespace, - namespace, serviceAccount, kubeClient) - if err != nil { - return err - } - sw := pilot.XdsStatusWriter{ - Writer: c.OutOrStdout(), - InternalDebugAllIstiod: internalDebugAllIstiod, - } - newResponse, err := HandlerForDebugErrors(kubeClient, ¢ralOpts, c.OutOrStdout(), xdsResponses) - if err != nil { - return err - } - if newResponse != nil { - return sw.PrintAll(newResponse) - } - - return sw.PrintAll(xdsResponses) - }, - } - - opts.AttachControlPlaneFlags(debugCommand) - centralOpts.AttachControlPlaneFlags(debugCommand) - debugCommand.Long += "\n\n" + ExperimentalMsg - debugCommand.PersistentFlags().BoolVar(&internalDebugAllIstiod, "all", false, - "Send the same request to all instances of Istiod. Only applicable for in-cluster deployment.") - return debugCommand -} - -var internalDebugAllIstiod bool diff --git a/istioctl/cmd/istioctl/doc.go b/istioctl/cmd/istioctl/doc.go deleted file mode 100644 index 996f9ba7c..000000000 --- a/istioctl/cmd/istioctl/doc.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Command istioctl is a Istio configuration command line utility. -package main // import "github.com/apache/dubbo-go-pixiu/istioctl/cmd/istioctl" diff --git a/istioctl/cmd/istioctl/istioctl_test.go b/istioctl/cmd/istioctl/istioctl_test.go deleted file mode 100644 index 349f1930b..000000000 --- a/istioctl/cmd/istioctl/istioctl_test.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "os" - "testing" -) - -func TestIstioctlMain(_ *testing.T) { - os.Args = []string{"istioctl", "version", "--remote=false"} - main() -} diff --git a/istioctl/cmd/istioctl/main.go b/istioctl/cmd/istioctl/main.go deleted file mode 100644 index 0cd24606b..000000000 --- a/istioctl/cmd/istioctl/main.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "fmt" - "os" -) - -import ( - "istio.io/pkg/log" - _ "k8s.io/client-go/plugin/pkg/client/auth" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/cmd" -) - -func main() { - if err := cmd.ConfigAndEnvProcessing(); err != nil { - fmt.Fprintf(os.Stderr, "Could not initialize: %v\n", err) - exitCode := cmd.GetExitCode(err) - os.Exit(exitCode) - } - - rootCmd := cmd.GetRootCmd(os.Args[1:]) - - log.EnableKlogWithCobra() - - if err := rootCmd.Execute(); err != nil { - exitCode := cmd.GetExitCode(err) - os.Exit(exitCode) - } -} diff --git a/istioctl/cmd/istioctl_test.go b/istioctl/cmd/istioctl_test.go deleted file mode 100644 index 7a702e028..000000000 --- a/istioctl/cmd/istioctl_test.go +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "bytes" - "regexp" - "strings" - "testing" -) - -import ( - istioclient "istio.io/client-go/pkg/clientset/versioned" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/test/util" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -type testCase struct { - args []string - - // Typically use one of the three - expectedOutput string // Expected constant output - expectedRegexp *regexp.Regexp // Expected regexp output - goldenFilename string // Expected output stored in golden file - - wantException bool -} - -func TestBadParse(t *testing.T) { - // unknown flags should be a command parse - rootCmd := GetRootCmd([]string{"--unknown-flag"}) - fErr := rootCmd.Execute() - - switch fErr.(type) { - case CommandParseError: - // do nothing - default: - t.Errorf("Expected a CommandParseError, but got %q.", fErr) - } - - // we should propagate to subcommands - rootCmd = GetRootCmd([]string{"x", "analyze", "--unknown-flag"}) - fErr = rootCmd.Execute() - - switch fErr.(type) { - case CommandParseError: - // do nothing - default: - t.Errorf("Expected a CommandParseError, but got %q.", fErr) - } - - // all of the subcommands - rootCmd = GetRootCmd([]string{"authz", "tls-check", "--unknown-flag"}) - fErr = rootCmd.Execute() - - switch fErr.(type) { - case CommandParseError: - // do nothing - default: - t.Errorf("Expected a CommandParseError, but got %q.", fErr) - } -} - -// mockClientFactoryGenerator creates a factory for model.ConfigStore preloaded with data -func mockClientFactoryGenerator(setupFn ...func(c istioclient.Interface)) func() (istioclient.Interface, error) { - outFactory := func() (istioclient.Interface, error) { - c := kube.NewFakeClient().Istio() - for _, f := range setupFn { - f(c) - } - return c, nil - } - - return outFactory -} - -func verifyOutput(t *testing.T, c testCase) { - t.Helper() - - // Override the client factory used by main.go - configStoreFactory = mockClientFactoryGenerator() - - var out bytes.Buffer - rootCmd := GetRootCmd(c.args) - rootCmd.SetOut(&out) - rootCmd.SetErr(&out) - - fErr := rootCmd.Execute() - output := out.String() - - if c.expectedOutput != "" && c.expectedOutput != output { - t.Fatalf("Unexpected output for 'istioctl %s'\n got: %q\nwant: %q", - strings.Join(c.args, " "), output, c.expectedOutput) - } - - if c.expectedRegexp != nil && !c.expectedRegexp.MatchString(output) { - t.Fatalf("Output didn't match for 'istioctl %s'\n got %v\nwant: %v", - strings.Join(c.args, " "), output, c.expectedRegexp) - } - - if c.goldenFilename != "" { - util.CompareContent(t, []byte(output), c.goldenFilename) - } - - if c.wantException { - if fErr == nil { - t.Fatalf("Wanted an exception for 'istioctl %s', didn't get one, output was %q", - strings.Join(c.args, " "), output) - } - } else { - if fErr != nil { - t.Fatalf("Unwanted exception for 'istioctl %s': %v", strings.Join(c.args, " "), fErr) - } - } -} diff --git a/istioctl/cmd/istiodconfig.go b/istioctl/cmd/istiodconfig.go deleted file mode 100644 index 2ac20d7d6..000000000 --- a/istioctl/cmd/istiodconfig.go +++ /dev/null @@ -1,472 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "net/http" - "net/url" - "regexp" - "sort" - "strings" - "sync" -) - -import ( - "github.com/spf13/cobra" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/clioptions" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/handlers" -) - -type flagState interface { - run() (string, error) -} - -var ( - _ flagState = (*resetState)(nil) - _ flagState = (*logLevelState)(nil) - _ flagState = (*stackTraceLevelState)(nil) - _ flagState = (*getAllLogLevelsState)(nil) -) - -type resetState struct { - client *ControlzClient -} - -func (rs *resetState) run() (string, error) { - const ( - defaultOutputLevel = "info" - defaultStackTraceLevel = "none" - ) - allScopes, err := rs.client.GetScopes() - if err != nil { - return "", fmt.Errorf("could not get all scopes: %v", err) - } - var defaultScopes []*ScopeInfo - for _, scope := range allScopes { - defaultScopes = append(defaultScopes, &ScopeInfo{ - Name: scope.Name, - OutputLevel: defaultOutputLevel, - StackTraceLevel: defaultStackTraceLevel, - }) - } - err = rs.client.PutScopes(defaultScopes) - if err != nil { - return "", err - } - - return "", nil -} - -type logLevelState struct { - client *ControlzClient - outputLogLevel string -} - -func (ll *logLevelState) run() (string, error) { - scopeInfos, err := newScopeInfosFromScopeLevelPairs(ll.outputLogLevel) - if err != nil { - return "", err - } - err = ll.client.PutScopes(scopeInfos) - if err != nil { - return "", err - } - return "", nil -} - -type stackTraceLevelState struct { - client *ControlzClient - stackTraceLevel string -} - -func (stl *stackTraceLevelState) run() (string, error) { - scopeInfos, err := newScopeInfosFromScopeStackTraceLevelPairs(stl.stackTraceLevel) - if err != nil { - return "", err - } - err = stl.client.PutScopes(scopeInfos) - if err != nil { - return "", err - } - return "", nil -} - -type getAllLogLevelsState struct { - client *ControlzClient - outputFormat string -} - -func (ga *getAllLogLevelsState) run() (string, error) { - type scopeLogLevel struct { - ScopeName string `json:"scope_name"` - LogLevel string `json:"log_level"` - } - allScopes, err := ga.client.GetScopes() - sort.Slice(allScopes, func(i, j int) bool { - return allScopes[i].Name < allScopes[j].Name - }) - if err != nil { - return "", fmt.Errorf("could not get scopes information: %v", err) - } - var resultScopeLogLevel []*scopeLogLevel - for _, scope := range allScopes { - resultScopeLogLevel = append(resultScopeLogLevel, &scopeLogLevel{ScopeName: scope.Name, LogLevel: scope.OutputLevel}) - } - var output strings.Builder - switch ga.outputFormat { - case "short": - output.Write([]byte("Active scopes:\n")) - for _, sll := range resultScopeLogLevel { - _, _ = fmt.Fprintf(&output, " %s:%s\n", sll.ScopeName, sll.LogLevel) - } - case "json": - outputBytes, err := json.MarshalIndent(&resultScopeLogLevel, "", " ") - outputBytes = append(outputBytes, []byte("\n")...) - if err != nil { - return "", err - } - output.Write(outputBytes) - default: - return "", fmt.Errorf("output format %q not supported", ga.outputFormat) - } - return output.String(), nil -} - -type istiodConfigLog struct { - state flagState -} - -func (id *istiodConfigLog) execute() (string, error) { - output, err := id.state.run() - return output, err -} - -func chooseClientFlag(ctrzClient *ControlzClient, reset bool, outputLogLevel, stackTraceLevel, outputFormat string) *istiodConfigLog { - if reset { - return &istiodConfigLog{state: &resetState{ctrzClient}} - } else if outputLogLevel != "" { - return &istiodConfigLog{state: &logLevelState{ - client: ctrzClient, - outputLogLevel: outputLogLevel, - }} - } else if stackTraceLevel != "" { - return &istiodConfigLog{state: &stackTraceLevelState{ - client: ctrzClient, - stackTraceLevel: stackTraceLevel, - }} - } else { - return &istiodConfigLog{state: &getAllLogLevelsState{ - client: ctrzClient, - outputFormat: outputFormat, - }} - } -} - -type ScopeInfo struct { - Name string `json:"name"` - Description string `json:"description,omitempty"` - OutputLevel string `json:"output_level,omitempty"` - StackTraceLevel string `json:"stack_trace_level,omitempty"` - LogCallers bool `json:"log_callers,omitempty"` -} - -type ScopeLevelPair struct { - scope string - logLevel string -} - -type scopeStackTraceLevelPair ScopeLevelPair - -func newScopeLevelPair(slp, validationPattern string) (*ScopeLevelPair, error) { - matched, err := regexp.MatchString(validationPattern, slp) - if err != nil { - return nil, err - } - if !matched { - return nil, fmt.Errorf("pattern %s did not match", slp) - } - scopeLogLevel := strings.Split(slp, ":") - s := &ScopeLevelPair{ - scope: scopeLogLevel[0], - logLevel: scopeLogLevel[1], - } - return s, nil -} - -func newScopeInfosFromScopeLevelPairs(scopeLevelPairs string) ([]*ScopeInfo, error) { - slParis := strings.Split(scopeLevelPairs, ",") - var scopeInfos []*ScopeInfo - for _, slp := range slParis { - sl, err := newScopeLevelPair(slp, validationPattern) - if err != nil { - return nil, err - } - si := &ScopeInfo{ - Name: sl.scope, - OutputLevel: sl.logLevel, - } - scopeInfos = append(scopeInfos, si) - } - return scopeInfos, nil -} - -func newScopeStackTraceLevelPair(sslp, validationPattern string) (*scopeStackTraceLevelPair, error) { - matched, err := regexp.MatchString(validationPattern, sslp) - if err != nil { - return nil, err - } - if !matched { - return nil, fmt.Errorf("pattern %s did not match", sslp) - } - scopeStackTraceLevel := strings.Split(sslp, ":") - ss := &scopeStackTraceLevelPair{ - scope: scopeStackTraceLevel[0], - logLevel: scopeStackTraceLevel[1], - } - return ss, nil -} - -func newScopeInfosFromScopeStackTraceLevelPairs(scopeStackTraceLevelPairs string) ([]*ScopeInfo, error) { - sslPairs := strings.Split(scopeStackTraceLevelPairs, ",") - var scopeInfos []*ScopeInfo - for _, sslp := range sslPairs { - slp, err := newScopeStackTraceLevelPair(sslp, validationPattern) - if err != nil { - return nil, err - } - si := &ScopeInfo{ - Name: slp.scope, - StackTraceLevel: slp.logLevel, - } - scopeInfos = append(scopeInfos, si) - } - return scopeInfos, nil -} - -type ControlzClient struct { - baseURL *url.URL - httpClient *http.Client -} - -func (c *ControlzClient) GetScopes() ([]*ScopeInfo, error) { - var scopeInfos []*ScopeInfo - resp, err := c.httpClient.Get(c.baseURL.String()) - if err != nil { - return nil, err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("request not successful %s", resp.Status) - } - - err = json.NewDecoder(resp.Body).Decode(&scopeInfos) - if err != nil { - return nil, fmt.Errorf("cannot deserialize response %s", err) - } - return scopeInfos, nil -} - -func (c *ControlzClient) PutScope(scope *ScopeInfo) error { - var jsonScopeInfo bytes.Buffer - err := json.NewEncoder(&jsonScopeInfo).Encode(scope) - if err != nil { - return fmt.Errorf("cannot serialize scope %+v", *scope) - } - req, err := http.NewRequest(http.MethodPut, c.baseURL.String()+"/"+scope.Name, &jsonScopeInfo) - if err != nil { - return err - } - defer req.Body.Close() - - resp, err := c.httpClient.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusAccepted { - return fmt.Errorf("cannot update resource %s, got status %s", scope.Name, resp.Status) - } - return nil -} - -func (c *ControlzClient) PutScopes(scopes []*ScopeInfo) error { - ch := make(chan struct { - err error - scopeName string - }, len(scopes)) - var wg sync.WaitGroup - for _, scope := range scopes { - wg.Add(1) - go func(si *ScopeInfo) { - defer wg.Done() - err := c.PutScope(si) - ch <- struct { - err error - scopeName string - }{err: err, scopeName: si.Name} - }(scope) - } - wg.Wait() - close(ch) - for result := range ch { - if result.err != nil { - return fmt.Errorf("failed updating Scope %s: %v", result.scopeName, result.err) - } - } - return nil -} - -func (c *ControlzClient) GetScope(scope string) (*ScopeInfo, error) { - var s ScopeInfo - resp, err := http.Get(c.baseURL.String() + "/" + scope) - if err != nil { - return &s, err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return &s, fmt.Errorf("request not successful %s: ", resp.Status) - } - - err = json.NewDecoder(resp.Body).Decode(&s) - if err != nil { - return &s, fmt.Errorf("cannot deserialize response: %s", err) - } - return &s, nil -} - -var ( - istiodLabelSelector = "" - istiodReset = false - validationPattern = `^\w+:(debug|error|warn|info|debug)` -) - -func istiodLogCmd() *cobra.Command { - var opts clioptions.ControlPlaneOptions - outputLogLevel := "" - stackTraceLevel := "" - - // output format (yaml or short) - outputFormat := "short" - - logCmd := &cobra.Command{ - Use: "log [] [--level :][--stack-trace-level :]|[-r|--reset]|[--output|-o short|yaml]", - Short: "Manage istiod logging.", - Long: "Retrieve or update logging levels of istiod components.", - Example: ` # Retrieve information about istiod logging levels. - istioctl admin log - - # Retrieve information about istiod logging levels on a specific control plane pod. - istioctl admin l istiod-5c868d8bdd-pmvgg - - # Update levels of the specified loggers. - istioctl admin log --level ads:debug,authorization:debug - - # Reset levels of all the loggers to default value (info). - istioctl admin log -r -`, - Aliases: []string{"l"}, - Args: func(logCmd *cobra.Command, args []string) error { - if istiodReset && outputLogLevel != "" { - logCmd.Println(logCmd.UsageString()) - return fmt.Errorf("--level cannot be combined with --reset") - } - if istiodReset && stackTraceLevel != "" { - logCmd.Println(logCmd.UsageString()) - return fmt.Errorf("--stack-trace-level cannot be combined with --reset") - } - return nil - }, - RunE: func(logCmd *cobra.Command, args []string) error { - client, err := kubeClientWithRevision(kubeconfig, configContext, opts.Revision) - if err != nil { - return fmt.Errorf("failed to create k8s client: %v", err) - } - - var podName, ns string - if len(args) == 0 { - pl, err := client.PodsForSelector(context.TODO(), handlers.HandleNamespace(istioNamespace, defaultNamespace), istiodLabelSelector) - if err != nil { - return fmt.Errorf("not able to locate pod with selector %s: %v", istiodLabelSelector, err) - } - - if len(pl.Items) < 1 { - return errors.New("no pods found") - } - - if len(pl.Items) > 1 { - log.Warnf("more than 1 pods fits selector: %s; will use pod: %s", istiodLabelSelector, pl.Items[0].Name) - } - - // only use the first pod in the list - podName = pl.Items[0].Name - ns = pl.Items[0].Namespace - } else if len(args) == 1 { - podName, ns = args[0], istioNamespace - } - - portForwarder, err := client.NewPortForwarder(podName, ns, bindAddress, 0, controlZport) - if err != nil { - return fmt.Errorf("could not build port forwarder for ControlZ %s: %v", podName, err) - } - defer portForwarder.Close() - err = portForwarder.Start() - if err != nil { - return fmt.Errorf("could not start port forwarder for ControlZ %s: %v", podName, err) - } - - ctrlzClient := &ControlzClient{ - baseURL: &url.URL{ - Scheme: "http", - Host: portForwarder.Address(), - Path: "scopej", - }, - httpClient: &http.Client{}, - } - istiodConfigCmd := chooseClientFlag(ctrlzClient, istiodReset, outputLogLevel, stackTraceLevel, outputFormat) - output, err := istiodConfigCmd.execute() - if output != "" { - _, err := logCmd.OutOrStdout().Write([]byte(output)) - if err != nil { - return err - } - } - if err != nil { - return err - } - return nil - }, - } - logCmd.PersistentFlags().BoolVarP(&istiodReset, "reset", "r", istiodReset, "Reset levels to default value. (info)") - logCmd.PersistentFlags().IntVar(&controlZport, "ctrlz_port", 9876, "ControlZ port") - logCmd.PersistentFlags().StringVar(&outputLogLevel, "level", outputLogLevel, - "Comma-separated list of output logging level for scopes in format :[,:,...]"+ - "Possible values for : none, error, warn, info, debug") - logCmd.PersistentFlags().StringVar(&stackTraceLevel, "stack-trace-level", stackTraceLevel, - "Comma-separated list of stack trace level for scopes in format :[,:,...] "+ - "Possible values for : none, error, warn, info, debug") - logCmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", - outputFormat, "Output format: one of json|short") - return logCmd -} diff --git a/istioctl/cmd/istiodconfig_test.go b/istioctl/cmd/istiodconfig_test.go deleted file mode 100644 index 719366c43..000000000 --- a/istioctl/cmd/istiodconfig_test.go +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "fmt" - "net/http" - "net/http/httptest" - "net/url" - "reflect" - "strings" - "testing" -) - -func TestCtlPlaneConfig(t *testing.T) { - istiodConfigMap := map[string][]byte{ - "istiod-7b69ff6f8c-fvjvw": []byte("Active scopes:\n ads:info"), - } - - cases := []execTestCase{ - { - args: strings.Split("admin", " "), - expectedString: "Manage istiod logging", - }, - { - args: strings.Split("admin log -l app=invalid", " "), - expectedString: "no pods found", - wantException: true, - }, - { - args: strings.Split("admin log", " "), - expectedString: "no pods found", - wantException: true, - }, - { - execClientConfig: istiodConfigMap, - args: strings.Split("admin log istiod-7b69ff6f8c-fvjvw --level invalid", " "), - expectedString: "pattern invalid did not match", - wantException: true, - }, - { - execClientConfig: istiodConfigMap, - args: strings.Split("admin log istiod-7b69ff6f8c-fvjvw --stack-trace-level invalid", " "), - expectedString: "pattern invalid did not match", - wantException: true, - }, - { - args: strings.Split("admin log --reset --level invalid", " "), - expectedString: "--level cannot be combined with --reset", - wantException: true, - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("case %d %s", i, strings.Join(c.args, " ")), func(t *testing.T) { - verifyExecTestOutput(t, c) - }) - } -} - -func Test_newScopeLevelPair(t *testing.T) { - validationPattern := `^\w+:(debug|error|warn|info|debug)` - type args struct { - slp string - validationPattern string - } - tests := []struct { - name string - args args - want *ScopeLevelPair - wantErr bool - }{ - { - name: "Fail when logs scope-level pair don't match pattern", - args: args{validationPattern: validationPattern, slp: "invalid:pattern"}, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := newScopeLevelPair(tt.args.slp, tt.args.validationPattern) - if (err != nil) != tt.wantErr { - t.Errorf("newScopeLevelPair() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("newScopeLevelPair() got = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_newScopeStackTraceLevelPair(t *testing.T) { - validationPattern := `^\w+:(debug|error|warn|info|debug)` - type args struct { - sslp string - validationPattern string - } - tests := []struct { - name string - args args - want *scopeStackTraceLevelPair - wantErr bool - }{ - { - name: "Fail when logs scope-level pair don't match pattern", - args: args{validationPattern: validationPattern, sslp: "invalid:pattern"}, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := newScopeStackTraceLevelPair(tt.args.sslp, tt.args.validationPattern) - if (err != nil) != tt.wantErr { - t.Errorf("newScopeStackTraceLevelPair() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("newScopeStackTraceLevelPair() got = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_chooseClientFlag(t *testing.T) { - url, _ := url.Parse("http://localhost/scopej/resource") - - ctrzClient := &ControlzClient{ - baseURL: url, - httpClient: &http.Client{}, - } - - type args struct { - ctrzClient *ControlzClient - reset bool - outputLogLevel string - stackTraceLevel string - outputFormat string - } - tests := []struct { - name string - args args - want *istiodConfigLog - }{ - { - name: "given --reset flag return reset command", - args: args{ - ctrzClient: ctrzClient, - reset: true, - outputLogLevel: "", - stackTraceLevel: "", - outputFormat: "", - }, - want: &istiodConfigLog{state: &resetState{ - client: ctrzClient, - }}, - }, - { - name: "given --level flag return outputLogLevel command", - args: args{ - ctrzClient: ctrzClient, - reset: false, - outputLogLevel: "resource:info", - stackTraceLevel: "", - outputFormat: "", - }, - want: &istiodConfigLog{state: &logLevelState{ - client: ctrzClient, - outputLogLevel: "resource:info", - }}, - }, - { - name: "given --stack-trace-level flag return stackTraceLevelState", - args: args{ - ctrzClient: ctrzClient, - reset: false, - outputLogLevel: "", - stackTraceLevel: "resource:info", - outputFormat: "", - }, - want: &istiodConfigLog{ - state: &stackTraceLevelState{ - client: ctrzClient, - stackTraceLevel: "resource:info", - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := chooseClientFlag(tt.args.ctrzClient, tt.args.reset, tt.args.outputLogLevel, - tt.args.stackTraceLevel, tt.args.outputFormat); !reflect.DeepEqual(got, tt.want) { - t.Errorf("chooseClientFlag() = %v, want %v", got, tt.want) - } - }) - } -} - -func resourceHandler(writer http.ResponseWriter, request *http.Request) { - const getResponse = `{"name":"resource","description":"Core resource model scope","output_level":"info","stack_trace_level":"none","log_callers":false}` - - switch request.Method { - case http.MethodGet: - _, _ = writer.Write([]byte(getResponse)) - } -} - -func adsHandler(writer http.ResponseWriter, request *http.Request) { - const getResponse = `{"name":"ads","description":"ads debugging","output_level":"info","stack_trace_level":"none","log_callers":false}` - - switch request.Method { - case http.MethodGet: - _, _ = writer.Write([]byte(getResponse)) - } -} - -func setupHTTPServer() (*httptest.Server, *url.URL) { - handler := http.NewServeMux() - handler.HandleFunc("/scopej/ads", adsHandler) - handler.HandleFunc("/scopej/resource", resourceHandler) - server := httptest.NewServer(handler) - url, _ := url.Parse(server.URL) - return server, url -} - -func Test_flagState_run(t *testing.T) { - server, url := setupHTTPServer() - defer server.Close() - - ctrzClientNoScopejHandler := &ControlzClient{ - baseURL: url, - httpClient: &http.Client{}, - } - tests := []struct { - name string - state flagState - want string - wantErr bool - }{ - { - name: "resetState.run() should throw an error if the /scopej endpoint is missing", - state: &resetState{client: ctrzClientNoScopejHandler}, - want: "", - wantErr: true, - }, - { - name: "logLevelState.run() should throw an error if the /scopej endpoint is missing", - state: &logLevelState{ - client: ctrzClientNoScopejHandler, - outputLogLevel: "test:debug", - }, - want: "", - wantErr: true, - }, - { - name: "stackTraceLevelState.run() should throw an error if the /scopej endpoint is missing", - state: &stackTraceLevelState{ - client: ctrzClientNoScopejHandler, - stackTraceLevel: "test:debug", - }, - want: "", - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.state.run() - if (err != nil) != tt.wantErr { - t.Errorf("run() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.want { - t.Errorf("run() got = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/istioctl/cmd/kubeinject.go b/istioctl/cmd/kubeinject.go deleted file mode 100644 index 7c55140ef..000000000 --- a/istioctl/cmd/kubeinject.go +++ /dev/null @@ -1,668 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "bytes" - "context" - "crypto/tls" - "crypto/x509" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "os" - "sort" - "strings" - "time" -) - -import ( - "github.com/hashicorp/go-multierror" - "github.com/spf13/cobra" - "istio.io/api/label" - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/log" - "istio.io/pkg/version" - admission "k8s.io/api/admission/v1" - admissionv1beta1 "k8s.io/api/admission/v1beta1" - admissionregistration "k8s.io/api/admissionregistration/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/serializer" - "k8s.io/client-go/kubernetes" - v1 "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/kubectl/pkg/polymorphichelpers" - "k8s.io/kubectl/pkg/util/podutils" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/clioptions" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/tag" - iopv1alpha1 "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/manifest" - "github.com/apache/dubbo-go-pixiu/operator/pkg/validate" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/inject" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -const ( - configMapKey = "mesh" - injectConfigMapKey = "config" - valuesConfigMapKey = "values" -) - -type ExternalInjector struct { - client kube.ExtendedClient - clientConfig *admissionregistration.WebhookClientConfig - injectorAddress string -} - -func (e ExternalInjector) Inject(pod *corev1.Pod, deploymentNS string) ([]byte, error) { - cc := e.clientConfig - if cc == nil { - return nil, nil - } - var address string - if cc.URL != nil { - address = *cc.URL - } - var certPool *x509.CertPool - if len(cc.CABundle) > 0 { - certPool = x509.NewCertPool() - certPool.AppendCertsFromPEM(cc.CABundle) - } else { - var err error - certPool, err = x509.SystemCertPool() - if err != nil { - return nil, err - } - } - tlsClientConfig := &tls.Config{RootCAs: certPool} - client := http.Client{ - Timeout: time.Second * 5, - Transport: &http.Transport{ - TLSClientConfig: tlsClientConfig, - }, - } - if cc.Service != nil { - svc, err := e.client.CoreV1().Services(cc.Service.Namespace).Get(context.Background(), cc.Service.Name, metav1.GetOptions{}) - if err != nil { - return nil, err - } - namespace, selector, err := polymorphichelpers.SelectorsForObject(svc) - if err != nil { - if e.injectorAddress == "" { - return nil, fmt.Errorf("cannot attach to %T: %v", svc, err) - } - address = fmt.Sprintf("https://%s:%d%s", e.injectorAddress, *cc.Service.Port, *cc.Service.Path) - } else { - pod, err := GetFirstPod(e.client.CoreV1(), namespace, selector.String()) - if err != nil { - return nil, err - } - webhookPort := cc.Service.Port - podPort := 15017 - for _, v := range svc.Spec.Ports { - if v.Port == *webhookPort { - podPort = v.TargetPort.IntValue() - break - } - } - f, err := e.client.NewPortForwarder(pod.Name, pod.Namespace, "", 0, podPort) - if err != nil { - return nil, err - } - if err := f.Start(); err != nil { - return nil, err - } - address = fmt.Sprintf("https://%s%s", f.Address(), *cc.Service.Path) - defer func() { - f.Close() - f.WaitForStop() - }() - } - tlsClientConfig.ServerName = fmt.Sprintf("%s.%s.%s", cc.Service.Name, cc.Service.Namespace, "svc") - } else if isMCPAddr(address) { - var err error - client.Transport, err = mcpTransport(context.TODO(), client.Transport) - if err != nil { - return nil, err - } - } - podBytes, err := json.Marshal(pod) - if pod.Namespace != "" { - deploymentNS = pod.Namespace - } - if err != nil { - return nil, err - } - rev := &admission.AdmissionReview{ - TypeMeta: metav1.TypeMeta{ - APIVersion: admission.SchemeGroupVersion.String(), - Kind: "AdmissionReview", - }, - Request: &admission.AdmissionRequest{ - Object: runtime.RawExtension{Raw: podBytes}, - Kind: metav1.GroupVersionKind{ - Group: admission.GroupName, - Version: admission.SchemeGroupVersion.Version, - Kind: "AdmissionRequest", - }, - Resource: metav1.GroupVersionResource{}, - SubResource: "", - RequestKind: nil, - RequestResource: nil, - RequestSubResource: "", - Name: pod.Name, - Namespace: deploymentNS, - }, - Response: nil, - } - revBytes, err := json.Marshal(rev) - if err != nil { - return nil, err - } - resp, err := client.Post(address, "application/json", bytes.NewBuffer(revBytes)) - if err != nil { - return nil, err - } - defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - var obj runtime.Object - var ar *kube.AdmissionReview - out, _, err := deserializer.Decode(body, nil, obj) - if err != nil { - return nil, fmt.Errorf("could not decode body: %v", err) - } - ar, err = kube.AdmissionReviewKubeToAdapter(out) - if err != nil { - return nil, fmt.Errorf("could not decode object: %v", err) - } - - return ar.Response.Patch, nil -} - -var ( - runtimeScheme = func() *runtime.Scheme { - r := runtime.NewScheme() - r.AddKnownTypes(admissionv1beta1.SchemeGroupVersion, &admissionv1beta1.AdmissionReview{}) - r.AddKnownTypes(admission.SchemeGroupVersion, &admission.AdmissionReview{}) - return r - }() - codecs = serializer.NewCodecFactory(runtimeScheme) - deserializer = codecs.UniversalDeserializer() -) - -// GetFirstPod returns a pod matching the namespace and label selector -// and the number of all pods that match the label selector. -// This is forked from polymorphichelpers.GetFirstPod to not watch and instead return an error if no pods are found -func GetFirstPod(client v1.CoreV1Interface, namespace string, selector string) (*corev1.Pod, error) { - options := metav1.ListOptions{LabelSelector: selector} - - sortBy := func(pods []*corev1.Pod) sort.Interface { return sort.Reverse(podutils.ActivePods(pods)) } - podList, err := client.Pods(namespace).List(context.TODO(), options) - if err != nil { - return nil, err - } - pods := make([]*corev1.Pod, 0, len(podList.Items)) - for i := range podList.Items { - pod := podList.Items[i] - pods = append(pods, &pod) - } - if len(pods) > 0 { - sort.Sort(sortBy(pods)) - return pods[0], nil - } - return nil, fmt.Errorf("no pods matching selector %q found in namespace %q", selector, namespace) -} - -func createInterface(kubeconfig string) (kubernetes.Interface, error) { - restConfig, err := kube.BuildClientConfig(kubeconfig, configContext) - if err != nil { - return nil, err - } - return kubernetes.NewForConfig(restConfig) -} - -func getMeshConfigFromConfigMap(kubeconfig, command, revision string) (*meshconfig.MeshConfig, error) { - client, err := createInterface(kubeconfig) - if err != nil { - return nil, err - } - - if meshConfigMapName == defaultMeshConfigMapName && revision != "" { - meshConfigMapName = fmt.Sprintf("%s-%s", defaultMeshConfigMapName, revision) - } - meshConfigMap, err := client.CoreV1().ConfigMaps(istioNamespace).Get(context.TODO(), meshConfigMapName, metav1.GetOptions{}) - if err != nil { - return nil, fmt.Errorf("could not read valid configmap %q from namespace %q: %v - "+ - "Use --meshConfigFile or re-run "+command+" with `-i and ensure valid MeshConfig exists", - meshConfigMapName, istioNamespace, err) - } - // values in the data are strings, while proto might use a - // different data type. therefore, we have to get a value by a - // key - configYaml, exists := meshConfigMap.Data[configMapKey] - if !exists { - return nil, fmt.Errorf("missing configuration map key %q", configMapKey) - } - cfg, err := mesh.ApplyMeshConfigDefaults(configYaml) - if err != nil { - err = multierror.Append(err, fmt.Errorf("istioctl version %s cannot parse mesh config. Install istioctl from the latest Istio release", - version.Info.Version)) - } - return cfg, err -} - -// grabs the raw values from the ConfigMap. These are encoded as JSON. -func getValuesFromConfigMap(kubeconfig, revision string) (string, error) { - client, err := createInterface(kubeconfig) - if err != nil { - return "", err - } - - if revision != "" { - injectConfigMapName = fmt.Sprintf("%s-%s", defaultInjectConfigMapName, revision) - } - meshConfigMap, err := client.CoreV1().ConfigMaps(istioNamespace).Get(context.TODO(), injectConfigMapName, metav1.GetOptions{}) - if err != nil { - return "", fmt.Errorf("could not find valid configmap %q from namespace %q: %v - "+ - "Use --valuesFile or re-run kube-inject with `-i and ensure istio-sidecar-injector configmap exists", - injectConfigMapName, istioNamespace, err) - } - - valuesData, exists := meshConfigMap.Data[valuesConfigMapKey] - if !exists { - return "", fmt.Errorf("missing configuration map key %q in %q", - valuesConfigMapKey, injectConfigMapName) - } - - return valuesData, nil -} - -func readInjectConfigFile(f []byte) (inject.RawTemplates, error) { - var injectConfig inject.Config - err := yaml.Unmarshal(f, &injectConfig) - if err != nil || len(injectConfig.RawTemplates) == 0 { - // This must be a direct template, instead of an inject.Config. We support both formats - return map[string]string{inject.SidecarTemplateName: string(f)}, nil - } - cfg, err := inject.UnmarshalConfig(f) - if err != nil { - return nil, err - } - return cfg.RawTemplates, err -} - -func getInjectConfigFromConfigMap(kubeconfig, revision string) (inject.RawTemplates, error) { - client, err := createInterface(kubeconfig) - if err != nil { - return nil, err - } - - if injectConfigMapName == defaultInjectConfigMapName && revision != "" { - injectConfigMapName = fmt.Sprintf("%s-%s", defaultInjectConfigMapName, revision) - } - meshConfigMap, err := client.CoreV1().ConfigMaps(istioNamespace).Get(context.TODO(), injectConfigMapName, metav1.GetOptions{}) - if err != nil { - return nil, fmt.Errorf("could not find valid configmap %q from namespace %q: %v - "+ - "Use --injectConfigFile or re-run kube-inject with `-i ` and ensure istio-sidecar-injector configmap exists", - injectConfigMapName, istioNamespace, err) - } - // values in the data are strings, while proto might use a - // different data type. therefore, we have to get a value by a - // key - injectData, exists := meshConfigMap.Data[injectConfigMapKey] - if !exists { - return nil, fmt.Errorf("missing configuration map key %q in %q", - injectConfigMapKey, injectConfigMapName) - } - injectConfig, err := inject.UnmarshalConfig([]byte(injectData)) - if err != nil { - return nil, fmt.Errorf("unable to convert data from configmap %q: %v", - injectConfigMapName, err) - } - log.Debugf("using inject template from configmap %q", injectConfigMapName) - return injectConfig.RawTemplates, nil -} - -func setUpExternalInjector(kubeconfig, revision, injectorAddress string) (*ExternalInjector, error) { - e := &ExternalInjector{} - client, err := kube.NewExtendedClient(kube.BuildClientCmd(kubeconfig, configContext), "") - if err != nil { - return e, err - } - if revision == "" { - revision = "default" - } - whcList, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().List(context.TODO(), - metav1.ListOptions{LabelSelector: fmt.Sprintf("%s=%s", label.IoIstioRev.Name, revision)}) - if err != nil { - return e, fmt.Errorf("could not find valid mutatingWebhookConfiguration %q from cluster %v", - whcName, err) - } - if whcList != nil && len(whcList.Items) != 0 { - for _, wh := range whcList.Items[0].Webhooks { - if strings.HasSuffix(wh.Name, defaultWebhookName) { - return &ExternalInjector{client, &wh.ClientConfig, injectorAddress}, nil - } - } - } - return e, fmt.Errorf("could not find valid mutatingWebhookConfiguration %q from cluster", defaultWebhookName) -} - -func validateFlags() error { - var err error - if inFilename == "" { - err = multierror.Append(err, errors.New("filename not specified (see --filename or -f)")) - } - if meshConfigFile == "" && meshConfigMapName == "" && iopFilename == "" { - err = multierror.Append(err, - errors.New("--meshConfigFile or --meshConfigMapName or --operatorFileName must be set")) - } - return err -} - -func setupKubeInjectParameters(sidecarTemplate *inject.RawTemplates, valuesConfig *string, - revision, injectorAddress string) (*ExternalInjector, *meshconfig.MeshConfig, error) { - var err error - injector := &ExternalInjector{} - if injectConfigFile != "" { - injectionConfig, err := os.ReadFile(injectConfigFile) // nolint: vetshadow - if err != nil { - return nil, nil, err - } - injectConfig, err := readInjectConfigFile(injectionConfig) - if err != nil { - return nil, nil, multierror.Append(err, fmt.Errorf("loading --injectConfigFile")) - } - *sidecarTemplate = injectConfig - } else { - injector, err = setUpExternalInjector(kubeconfig, revision, injectorAddress) - if err != nil || injector.clientConfig == nil { - log.Warnf("failed to get injection config from mutatingWebhookConfigurations %q, will fall back to "+ - "get injection from the injection configmap %q : %v", whcName, defaultInjectWebhookConfigName, err) - if *sidecarTemplate, err = getInjectConfigFromConfigMap(kubeconfig, revision); err != nil { - return nil, nil, err - } - } - return injector, nil, nil - } - - // Get configs from IOP files firstly, and if not exists, get configs from files and configmaps. - values, meshConfig, err := getIOPConfigs() - if err != nil { - return nil, nil, err - } - if meshConfig == nil { - if meshConfigFile != "" { - if meshConfig, err = mesh.ReadMeshConfig(meshConfigFile); err != nil { - return nil, nil, err - } - } else { - if meshConfig, err = getMeshConfigFromConfigMap(kubeconfig, "kube-inject", revision); err != nil { - return nil, nil, err - } - } - } - - if values != "" { - *valuesConfig = values - } - if valuesConfig == nil || *valuesConfig == "" { - if valuesFile != "" { - valuesConfigBytes, err := os.ReadFile(valuesFile) // nolint: vetshadow - if err != nil { - return nil, nil, err - } - *valuesConfig = string(valuesConfigBytes) - } else if *valuesConfig, err = getValuesFromConfigMap(kubeconfig, revision); err != nil { - return nil, nil, err - } - } - return injector, meshConfig, err -} - -// getIOPConfigs gets the configs in IOPs. -func getIOPConfigs() (string, *meshconfig.MeshConfig, error) { - var meshConfig *meshconfig.MeshConfig - var valuesConfig string - if iopFilename != "" { - var iop *iopv1alpha1.IstioOperator - y, err := manifest.ReadLayeredYAMLs([]string{iopFilename}) - if err != nil { - return "", nil, err - } - iop, err = validate.UnmarshalIOP(y) - if err != nil { - return "", nil, err - } - if err := validate.ValidIOP(iop); err != nil { - return "", nil, fmt.Errorf("validation errors: \n%s", err) - } - if err != nil { - return "", nil, err - } - if iop.Spec.Values != nil { - values, err := protomarshal.ToJSON(iop.Spec.Values) - if err != nil { - return "", nil, err - } - valuesConfig = values - } - if iop.Spec.MeshConfig != nil { - meshConfigYaml, err := protomarshal.ToYAML(iop.Spec.MeshConfig) - if err != nil { - return "", nil, err - } - meshConfig, err = mesh.ApplyMeshConfigDefaults(meshConfigYaml) - if err != nil { - return "", nil, err - } - } - } - return valuesConfig, meshConfig, nil -} - -var ( - inFilename string - outFilename string - meshConfigFile string - meshConfigMapName string - valuesFile string - injectConfigFile string - injectConfigMapName string - whcName string - iopFilename string -) - -const ( - defaultMeshConfigMapName = "istio" - defaultMeshConfigMapKey = "mesh" - defaultInjectConfigMapName = "istio-sidecar-injector" - defaultInjectWebhookConfigName = "istio-sidecar-injector" - defaultWebhookName = "sidecar-injector.istio.io" -) - -func injectCommand() *cobra.Command { - var opts clioptions.ControlPlaneOptions - var centralOpts clioptions.CentralControlPlaneOptions - - injectCmd := &cobra.Command{ - Use: "kube-inject", - Short: "Inject Istio sidecar into Kubernetes pod resources", - Long: ` -kube-inject manually injects the Istio sidecar into Kubernetes -workloads. Unsupported resources are left unmodified so it is safe to -run kube-inject over a single file that contains multiple Service, -ConfigMap, Deployment, etc. definitions for a complex application. When in -doubt re-run istioctl kube-inject on deployments to get the most up-to-date changes. - -It's best to do kube-inject when the resource is initially created. -`, - Example: ` # Update resources on the fly before applying. - kubectl apply -f <(istioctl kube-inject -f ) - - # Create a persistent version of the deployment with Istio sidecar injected. - istioctl kube-inject -f deployment.yaml -o deployment-injected.yaml - - # Update an existing deployment. - kubectl get deployment -o yaml | istioctl kube-inject -f - | kubectl apply -f - - - # Capture cluster configuration for later use with kube-inject - kubectl -n dubbo-system get cm istio-sidecar-injector -o jsonpath="{.data.config}" > /tmp/inj-template.tmpl - kubectl -n dubbo-system get cm istio -o jsonpath="{.data.mesh}" > /tmp/mesh.yaml - kubectl -n dubbo-system get cm istio-sidecar-injector -o jsonpath="{.data.values}" > /tmp/values.json - - # Use kube-inject based on captured configuration - istioctl kube-inject -f samples/bookinfo/platform/kube/bookinfo.yaml \ - --injectConfigFile /tmp/inj-template.tmpl \ - --meshConfigFile /tmp/mesh.yaml \ - --valuesFile /tmp/values.json -`, - RunE: func(c *cobra.Command, _ []string) (err error) { - if err = validateFlags(); err != nil { - return err - } - var reader io.Reader - - if inFilename == "-" { - reader = os.Stdin - } else { - var in *os.File - if in, err = os.Open(inFilename); err != nil { - return err - } - reader = in - defer func() { - if errClose := in.Close(); errClose != nil { - log.Errorf("Error: close file from %s, %s", inFilename, errClose) - - // don't overwrite the previous error - if err == nil { - err = errClose - } - } - }() - } - - var writer io.Writer - if outFilename == "" { - writer = c.OutOrStdout() - } else { - var out *os.File - if out, err = os.Create(outFilename); err != nil { - return err - } - writer = out - defer func() { - if errClose := out.Close(); errClose != nil { - log.Errorf("Error: close file from %s, %s", outFilename, errClose) - - // don't overwrite the previous error - if err == nil { - err = errClose - } - } - }() - } - var valuesConfig string - var sidecarTemplate inject.RawTemplates - var meshConfig *meshconfig.MeshConfig - rev := opts.Revision - // if the revision is "default", render templates with an empty revision - if rev == tag.DefaultRevisionName { - rev = "" - } - injectorAddress := centralOpts.Xds - index := strings.IndexByte(injectorAddress, ':') - if index != -1 { - injectorAddress = injectorAddress[:index] - } - injector, meshConfig, err := setupKubeInjectParameters(&sidecarTemplate, &valuesConfig, rev, injectorAddress) - if err != nil { - return err - } - if injector.client == nil && meshConfig == nil { - return fmt.Errorf( - "failed to get injection config from mutatingWebhookConfigurations and injection configmap - " + - "check injection configmap or pass --revision flag", - ) - } - var warnings []string - templs, err := inject.ParseTemplates(sidecarTemplate) - if err != nil { - return err - } - vc, err := inject.NewValuesConfig(valuesConfig) - if err != nil { - return err - } - retval := inject.IntoResourceFile(injector, templs, vc, rev, meshConfig, - reader, writer, func(warning string) { - warnings = append(warnings, warning) - }) - if len(warnings) > 0 { - fmt.Fprintln(c.ErrOrStderr()) - } - for _, warning := range warnings { - fmt.Fprintln(c.ErrOrStderr(), warning) - } - return retval - }, - PersistentPreRunE: func(c *cobra.Command, args []string) error { - // istioctl kube-inject is typically redirected to a .yaml file; - // the default for log messages should be stderr, not stdout - _ = c.Root().PersistentFlags().Set("log_target", "stderr") - - return c.Parent().PersistentPreRunE(c, args) - }, - } - - injectCmd.PersistentFlags().StringVar(&meshConfigFile, "meshConfigFile", "", - "Mesh configuration filename. Takes precedence over --meshConfigMapName if set") - injectCmd.PersistentFlags().StringVar(&injectConfigFile, "injectConfigFile", "", - "Injection configuration filename. Cannot be used with --injectConfigMapName") - injectCmd.PersistentFlags().StringVar(&valuesFile, "valuesFile", "", - "Injection values configuration filename.") - - injectCmd.PersistentFlags().StringVarP(&inFilename, "filename", "f", - "", "Input Kubernetes resource filename") - injectCmd.PersistentFlags().StringVarP(&outFilename, "output", "o", - "", "Modified output Kubernetes resource filename") - injectCmd.PersistentFlags().StringVar(&iopFilename, "operatorFileName", "", - "Path to file containing IstioOperator custom resources. If configs from files like "+ - "meshConfigFile, valuesFile are provided, they will be overridden by iop config values.") - - injectCmd.PersistentFlags().StringVar(&meshConfigMapName, "meshConfigMapName", defaultMeshConfigMapName, - fmt.Sprintf("ConfigMap name for Istio mesh configuration, key should be %q", configMapKey)) - injectCmd.PersistentFlags().StringVar(&injectConfigMapName, "injectConfigMapName", defaultInjectConfigMapName, - fmt.Sprintf("ConfigMap name for Istio sidecar injection, key should be %q.", injectConfigMapKey)) - _ = injectCmd.PersistentFlags().MarkHidden("injectConfigMapName") - injectCmd.PersistentFlags().StringVar(&whcName, "webhookConfig", defaultInjectWebhookConfigName, - "MutatingWebhookConfiguration name for Istio") - opts.AttachControlPlaneFlags(injectCmd) - centralOpts.AttachControlPlaneFlags(injectCmd) - return injectCmd -} diff --git a/istioctl/cmd/kubeinject_test.go b/istioctl/cmd/kubeinject_test.go deleted file mode 100644 index 94709f8a1..000000000 --- a/istioctl/cmd/kubeinject_test.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package cmd - -import ( - "fmt" - "regexp" - "strings" - "testing" -) - -func TestKubeInject(t *testing.T) { - cases := []testCase{ - { // case 0 - args: strings.Split("kube-inject", " "), - expectedRegexp: regexp.MustCompile(`filename not specified \(see --filename or -f\)`), - wantException: true, - }, - { // case 1 - args: strings.Split("kube-inject -f missing.yaml", " "), - expectedRegexp: regexp.MustCompile(`open missing.yaml: no such file or directory`), - wantException: true, - }, - { // case 2 - args: strings.Split( - "kube-inject --meshConfigFile testdata/mesh-config.yaml"+ - " --injectConfigFile testdata/inject-config.yaml -f testdata/deployment/hello.yaml"+ - " --valuesFile testdata/inject-values.yaml", - " "), - goldenFilename: "testdata/deployment/hello.yaml.injected", - }, - { // case 3 - args: strings.Split( - "kube-inject --meshConfigFile testdata/mesh-config.yaml"+ - " --injectConfigFile testdata/inject-config-inline.yaml -f testdata/deployment/hello.yaml"+ - " --valuesFile testdata/inject-values.yaml", - " "), - goldenFilename: "testdata/deployment/hello.yaml.injected", - }, - { // case 4 with only iop files - args: strings.Split( - "kube-inject --operatorFileName testdata/istio-operator.yaml"+ - " --injectConfigFile testdata/inject-config-iop.yaml -f testdata/deployment/hello.yaml", - " "), - goldenFilename: "testdata/deployment/hello.yaml.iop.injected", - }, - { // case 5 with only iop files - args: strings.Split( - "kube-inject --operatorFileName testdata/istio-operator.yaml"+ - " --injectConfigFile testdata/inject-config-inline-iop.yaml -f testdata/deployment/hello.yaml", - " "), - goldenFilename: "testdata/deployment/hello.yaml.iop.injected", - }, - { // case 6 with iops and values override - args: strings.Split( - "kube-inject --operatorFileName testdata/istio-operator.yaml"+ - " --injectConfigFile testdata/inject-config-iop.yaml -f testdata/deployment/hello.yaml"+ - " -f testdata/deployment/hello.yaml"+ - " --valuesFile testdata/inject-values.yaml", - " "), - goldenFilename: "testdata/deployment/hello.yaml.iop.injected", - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("case %d %s", i, strings.Join(c.args, " ")), func(t *testing.T) { - verifyOutput(t, c) - }) - } -} diff --git a/istioctl/cmd/kubeuninject.go b/istioctl/cmd/kubeuninject.go deleted file mode 100644 index 1cccbe7ef..000000000 --- a/istioctl/cmd/kubeuninject.go +++ /dev/null @@ -1,420 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "bufio" - "encoding/json" - "errors" - "fmt" - "io" - "os" - "reflect" - "regexp" - "strings" -) - -import ( - "github.com/hashicorp/go-multierror" - "github.com/spf13/cobra" - "istio.io/pkg/log" - batch "k8s.io/api/batch/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - yamlDecoder "k8s.io/apimachinery/pkg/util/yaml" - "sigs.k8s.io/yaml" -) - -import ( - istioStatus "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/status" - "github.com/apache/dubbo-go-pixiu/pkg/kube/inject" -) - -const ( - annotationPolicy = "sidecar.istio.io/inject" - certVolumeName = "istio-certs" - dataVolumeName = "istio-data" - enableCoreDumpContainerName = "enable-core-dump" - envoyVolumeName = "istio-envoy" - initContainerName = "istio-init" - initValidationContainerName = "istio-validation" - jwtTokenVolumeName = "istio-token" - pilotCertVolumeName = "istiod-ca-cert" - podInfoVolumeName = "istio-podinfo" - proxyContainerName = "istio-proxy" - sidecarAnnotationPrefix = "sidecar.istio.io" -) - -func validateUninjectFlags() error { - var err error - - if uninjectInFilename == "" { - err = multierror.Append(err, errors.New("filename not specified (see --filename or -f)")) - } - return err -} - -// extractResourceFile uninjects the istio proxy from the specified -// kubernetes YAML file. -func extractResourceFile(in io.Reader, out io.Writer) error { - reader := yamlDecoder.NewYAMLReader(bufio.NewReaderSize(in, 4096)) - for { - raw, err := reader.Read() - if err == io.EOF { - break - } - if err != nil { - return err - } - - obj, err := inject.FromRawToObject(raw) - if err != nil && !runtime.IsNotRegisteredError(err) { - return multierror.Append(err, fmt.Errorf("cannot parse YAML input")) - } - - var updated []byte - if err == nil { - outObject, err := extractObject(obj) - if err != nil { - return err - } - if updated, err = yaml.Marshal(outObject); err != nil { - return err - } - } else { - updated = raw // unchanged - } - - if _, err = out.Write(updated); err != nil { - return err - } - if _, err = fmt.Fprint(out, "---\n"); err != nil { - return err - } - } - return nil -} - -// removeInjectedContainers removes the injected container name - istio-proxy and istio-init -func removeInjectedContainers(containers []corev1.Container, injectedContainerName string) []corev1.Container { - for index, c := range containers { - if c.Name == injectedContainerName { - if index < len(containers)-1 { - containers = append(containers[:index], containers[index+1:]...) - } else { - containers = containers[:index] - } - break - } - } - return containers -} - -func restoreAppProbes(containers []corev1.Container, probers map[string]*inject.Prober) []corev1.Container { - re := regexp.MustCompile("/app-health/([a-z]+)/(readyz|livez|startupz)") - for name, prober := range probers { - matches := re.FindStringSubmatch(name) - if len(matches) == 0 { - continue - } - containerName := matches[1] - probeType := matches[2] - for i, c := range containers { - if c.Name == containerName { - container := c.DeepCopy() - switch probeType { - case "readyz": - container.ReadinessProbe = &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - HTTPGet: prober.HTTPGet, - }, - TimeoutSeconds: prober.TimeoutSeconds, - } - case "livez": - container.LivenessProbe = &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - HTTPGet: prober.HTTPGet, - }, - TimeoutSeconds: prober.TimeoutSeconds, - } - case "startupz": - container.StartupProbe = &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - HTTPGet: prober.HTTPGet, - }, - TimeoutSeconds: prober.TimeoutSeconds, - } - } - containers[i] = *container - } - } - } - return containers -} - -func retrieveAppProbe(containers []corev1.Container) string { - for _, c := range containers { - if c.Name != proxyContainerName { - continue - } - - for _, env := range c.Env { - if env.Name == istioStatus.KubeAppProberEnvName { - return env.Value - } - } - } - - return "" -} - -// removeInjectedVolumes removes the injected volumes if exists. -// for example, istio-envoy, istio-certs, and istio-token -func removeInjectedVolumes(volumes []corev1.Volume, injectedVolume string) []corev1.Volume { - for index, v := range volumes { - if v.Name == injectedVolume { - if index < len(volumes)-1 { - volumes = append(volumes[:index], volumes[index+1:]...) - } else { - volumes = volumes[:index] - } - break - } - } - return volumes -} - -func removeDNSConfig(podDNSConfig *corev1.PodDNSConfig) { - if podDNSConfig == nil { - return - } - - l := len(podDNSConfig.Searches) - index := 0 - for index < l { - s := podDNSConfig.Searches[index] - if strings.Contains(s, "global") { - if index < len(podDNSConfig.Searches)-1 { - podDNSConfig.Searches = append(podDNSConfig.Searches[:index], - podDNSConfig.Searches[index+1:]...) - } else { - podDNSConfig.Searches = podDNSConfig.Searches[:index] - } - // reset to 0 - index = 0 - l = len(podDNSConfig.Searches) - } else { - index++ - } - } -} - -// handleAnnotations removes the injected annotations which contains sidecar.istio.io -// it adds sidecar.istio.io/inject: false -func handleAnnotations(annotations map[string]string) map[string]string { - if annotations == nil { - annotations = make(map[string]string) - } - - for key := range annotations { - if strings.Contains(key, sidecarAnnotationPrefix) { - delete(annotations, key) - } - } - // sidecar.istio.io/inject: false to default the auto-injector in case it is present. - annotations[annotationPolicy] = "false" - return annotations -} - -// extractObject extras the sidecar injection and return the uninjected object. -func extractObject(in runtime.Object) (interface{}, error) { - out := in.DeepCopyObject() - - var metadata *metav1.ObjectMeta - var podSpec *corev1.PodSpec - - // Handle Lists - if list, ok := out.(*corev1.List); ok { - result := list - - for i, item := range list.Items { - obj, err := inject.FromRawToObject(item.Raw) - if runtime.IsNotRegisteredError(err) { - continue - } - if err != nil { - return nil, err - } - - r, err := extractObject(obj) - if err != nil { - return nil, err - } - - re := runtime.RawExtension{} - re.Object = r.(runtime.Object) - result.Items[i] = re - } - return result, nil - } - - // CronJobs have JobTemplates in them, instead of Templates, so we - // special case them. - switch v := out.(type) { - case *batch.CronJob: - job := v - metadata = &job.Spec.JobTemplate.ObjectMeta - podSpec = &job.Spec.JobTemplate.Spec.Template.Spec - case *corev1.Pod: - pod := v - metadata = &pod.ObjectMeta - podSpec = &pod.Spec - default: - // `in` is a pointer to an Object. Dereference it. - outValue := reflect.ValueOf(out).Elem() - - templateValue := outValue.FieldByName("Spec").FieldByName("Template") - - // `Template` is defined as a pointer in some older API - // definitions, e.g. ReplicationController - if templateValue.Kind() == reflect.Ptr { - if templateValue.IsNil() { - return out, fmt.Errorf("spec.template is required value") - } - templateValue = templateValue.Elem() - } - metadata = templateValue.FieldByName("ObjectMeta").Addr().Interface().(*metav1.ObjectMeta) - podSpec = templateValue.FieldByName("Spec").Addr().Interface().(*corev1.PodSpec) - } - - metadata.Annotations = handleAnnotations(metadata.Annotations) - // skip uninjection for pods - sidecarInjected := false - for _, c := range podSpec.Containers { - if c.Name == proxyContainerName { - sidecarInjected = true - } - } - if !sidecarInjected { - return out, nil - } - - var appProbe inject.KubeAppProbers - appProbeStr := retrieveAppProbe(podSpec.Containers) - if appProbeStr != "" { - if err := json.Unmarshal([]byte(appProbeStr), &appProbe); err != nil { - return nil, err - } - } - if appProbe != nil { - podSpec.Containers = restoreAppProbes(podSpec.Containers, appProbe) - } - - podSpec.InitContainers = removeInjectedContainers(podSpec.InitContainers, initContainerName) - podSpec.InitContainers = removeInjectedContainers(podSpec.InitContainers, initValidationContainerName) - podSpec.InitContainers = removeInjectedContainers(podSpec.InitContainers, enableCoreDumpContainerName) - podSpec.Containers = removeInjectedContainers(podSpec.Containers, proxyContainerName) - podSpec.Volumes = removeInjectedVolumes(podSpec.Volumes, certVolumeName) - podSpec.Volumes = removeInjectedVolumes(podSpec.Volumes, dataVolumeName) - podSpec.Volumes = removeInjectedVolumes(podSpec.Volumes, envoyVolumeName) - podSpec.Volumes = removeInjectedVolumes(podSpec.Volumes, jwtTokenVolumeName) - podSpec.Volumes = removeInjectedVolumes(podSpec.Volumes, pilotCertVolumeName) - podSpec.Volumes = removeInjectedVolumes(podSpec.Volumes, podInfoVolumeName) - removeDNSConfig(podSpec.DNSConfig) - - return out, nil -} - -var ( - uninjectInFilename string - uninjectOutFilename string -) - -func uninjectCommand() *cobra.Command { - uninjectCmd := &cobra.Command{ - Use: "kube-uninject", - Short: "Uninject Envoy sidecar from Kubernetes pod resources", - Long: ` -kube-uninject is used to prevent Istio from adding a sidecar and -also provides the inverse of "istioctl kube-inject -f". -`, - Example: ` # Update resources before applying. - kubectl apply -f <(istioctl experimental kube-uninject -f ) - - # Create a persistent version of the deployment by removing Envoy sidecar. - istioctl experimental kube-uninject -f deployment.yaml -o deployment-uninjected.yaml - - # Update an existing deployment. - kubectl get deployment -o yaml | istioctl experimental kube-uninject -f - | kubectl apply -f -`, - RunE: func(c *cobra.Command, _ []string) (err error) { - if err = validateUninjectFlags(); err != nil { - return err - } - // get the resource content - var reader io.Reader - if uninjectInFilename == "-" { - reader = os.Stdin - } else { - var in *os.File - if in, err = os.Open(uninjectInFilename); err != nil { - log.Errorf("Error: close file from %s, %s", uninjectInFilename, err) - return err - } - reader = in - defer func() { - if errClose := in.Close(); errClose != nil { - log.Errorf("Error: close file from %s, %s", uninjectInFilename, errClose) - - // don't overwrite the previous error - if err == nil { - err = errClose - } - } - }() - } - - var writer io.Writer - if uninjectOutFilename == "" { - writer = c.OutOrStdout() - } else { - var out *os.File - if out, err = os.Create(uninjectOutFilename); err != nil { - return err - } - writer = out - defer func() { - if errClose := out.Close(); errClose != nil { - log.Errorf("Error: close file from %s, %s", uninjectOutFilename, errClose) - - // don't overwrite the previous error - if err == nil { - err = errClose - } - } - }() - } - - return extractResourceFile(reader, writer) - }, - } - - uninjectCmd.PersistentFlags().StringVarP(&uninjectInFilename, "filename", "f", - "", "Input Kubernetes resource filename") - uninjectCmd.PersistentFlags().StringVarP(&uninjectOutFilename, "output", "o", - "", "Modified output Kubernetes resource filename") - - return uninjectCmd -} diff --git a/istioctl/cmd/kubeuninject_test.go b/istioctl/cmd/kubeuninject_test.go deleted file mode 100644 index 2bad56f58..000000000 --- a/istioctl/cmd/kubeuninject_test.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "fmt" - "regexp" - "strings" - "testing" -) - -func TestKubeUninject(t *testing.T) { - cases := []testCase{ - { // case 0 - args: strings.Split("experimental kube-uninject", " "), - expectedRegexp: regexp.MustCompile(`filename not specified \(see --filename or -f\)`), - wantException: true, - }, - { // case 1 - args: strings.Split("experimental kube-uninject -f missing.yaml", " "), - expectedRegexp: regexp.MustCompile(`open missing.yaml: no such file or directory`), - wantException: true, - }, - { // case 2 - args: strings.Split( - "experimental kube-uninject -f testdata/uninject/cronjob.yaml.injected", " "), - goldenFilename: "testdata/uninject/cronjob.yaml", - }, - { // case 3 - args: strings.Split( - "experimental kube-uninject -f testdata/uninject/cronjob-with-app.yaml.injected", " "), - goldenFilename: "testdata/uninject/cronjob-with-app.yaml", - }, - { // case 4 - args: strings.Split( - "experimental kube-uninject -f testdata/uninject/daemonset.yaml.injected", " "), - goldenFilename: "testdata/uninject/daemonset.yaml", - }, - { // case 5 - args: strings.Split( - "experimental kube-uninject -f testdata/uninject/deploymentconfig.yaml.injected", " "), - goldenFilename: "testdata/uninject/deploymentconfig.yaml", - }, - { // case 6 - args: strings.Split( - "experimental kube-uninject -f testdata/uninject/deploymentconfig-multi.yaml.injected", " "), - goldenFilename: "testdata/uninject/deploymentconfig-multi.yaml", - }, - { // case 7 - args: strings.Split( - "experimental kube-uninject -f testdata/uninject/job.yaml.injected", " "), - goldenFilename: "testdata/uninject/job.yaml", - }, - { // case 8 - args: strings.Split( - "experimental kube-uninject -f testdata/uninject/list.yaml.injected", " "), - goldenFilename: "testdata/uninject/list.yaml", - }, - { // case 9 - args: strings.Split( - "experimental kube-uninject -f testdata/uninject/pod.yaml.injected", " "), - goldenFilename: "testdata/uninject/pod.yaml", - }, - { // case 10 - args: strings.Split( - "experimental kube-uninject -f testdata/uninject/replicaset.yaml.injected", " "), - goldenFilename: "testdata/uninject/replicaset.yaml", - }, - { // case 11 - args: strings.Split( - "experimental kube-uninject -f testdata/uninject/replicationcontroller.yaml.injected", " "), - goldenFilename: "testdata/uninject/replicationcontroller.yaml", - }, - { // case 12 - args: strings.Split( - "experimental kube-uninject -f testdata/uninject/statefulset.yaml.injected", " "), - goldenFilename: "testdata/uninject/statefulset.yaml", - }, - { // case 13: verify the uninjected file - args: strings.Split( - "experimental kube-uninject -f testdata/uninject/hello.yaml", " "), - goldenFilename: "testdata/uninject/hello.yaml", - }, - { // case 14: enable-core-dump - args: strings.Split( - "experimental kube-uninject -f testdata/uninject/enable-core-dump.yaml.injected", " "), - goldenFilename: "testdata/uninject/enable-core-dump.yaml", - }, - { // case 15: restore rewritten app probes - args: strings.Split( - "experimental kube-uninject -f testdata/uninject/deploymentconfig-app-probe.yaml.injected", " "), - goldenFilename: "testdata/uninject/deploymentconfig-app-probe.yaml", - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("case %d %s", i, strings.Join(c.args, " ")), func(t *testing.T) { - verifyOutput(t, c) - }) - } -} diff --git a/istioctl/cmd/metrics.go b/istioctl/cmd/metrics.go deleted file mode 100644 index 73d040c76..000000000 --- a/istioctl/cmd/metrics.go +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "errors" - "fmt" - "io" - "strings" - "text/tabwriter" - "time" -) - -import ( - "github.com/hashicorp/go-multierror" - "github.com/prometheus/client_golang/api" - promv1 "github.com/prometheus/client_golang/api/prometheus/v1" - "github.com/prometheus/common/model" - "github.com/spf13/cobra" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/clioptions" -) - -var ( - metricsOpts clioptions.ControlPlaneOptions - metricsDuration time.Duration -) - -const ( - destWorkloadLabel = "destination_workload" - destWorkloadNamespaceLabel = "destination_workload_namespace" - reqTot = "istio_requests_total" - reqDur = "istio_request_duration_milliseconds" -) - -func metricsCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "metrics ...", - Short: "Prints the metrics for the specified workload(s) when running in Kubernetes.", - Long: ` -Prints the metrics for the specified service(s) when running in Kubernetes. - -This command finds a Prometheus pod running in the specified istio system -namespace. It then executes a series of queries per requested workload to -find the following top-level workload metrics: total requests per second, -error rate, and request latency at p50, p90, and p99 percentiles. The -query results are printed to the console, organized by workload name. - -All metrics returned are from server-side reports. This means that latencies -and error rates are from the perspective of the service itself and not of an -individual client (or aggregate set of clients). Rates and latencies are -calculated over a time interval of 1 minute. -`, - Example: ` # Retrieve workload metrics for productpage-v1 workload - istioctl experimental metrics productpage-v1 - - # Retrieve workload metrics for various services with custom duration - istioctl experimental metrics productpage-v1 -d 2m - - # Retrieve workload metrics for various services in the different namespaces - istioctl experimental metrics productpage-v1.foo reviews-v1.bar ratings-v1.baz`, - // nolint: goimports - Aliases: []string{"m"}, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - cmd.Println(cmd.UsageString()) - return fmt.Errorf("metrics requires workload name") - } - return nil - }, - RunE: run, - DisableFlagsInUseLine: true, - } - - cmd.PersistentFlags().DurationVarP(&metricsDuration, "duration", "d", time.Minute, "Duration of query metrics, default value is 1m.") - - return cmd -} - -type workloadMetrics struct { - workload string - totalRPS, errorRPS float64 - p50Latency, p90Latency, p99Latency time.Duration -} - -func run(c *cobra.Command, args []string) error { - log.Debugf("metrics command invoked for workload(s): %v", args) - - client, err := kubeClientWithRevision(kubeconfig, configContext, metricsOpts.Revision) - if err != nil { - return fmt.Errorf("failed to create k8s client: %v", err) - } - - pl, err := client.PodsForSelector(context.TODO(), istioNamespace, "app=prometheus") - if err != nil { - return fmt.Errorf("not able to locate Prometheus pod: %v", err) - } - - if len(pl.Items) < 1 { - return errors.New("no Prometheus pods found") - } - - // only use the first pod in the list - promPod := pl.Items[0] - fw, err := client.NewPortForwarder(promPod.Name, istioNamespace, "", 0, 9090) - if err != nil { - return fmt.Errorf("could not build port forwarder for prometheus: %v", err) - } - - if err = fw.Start(); err != nil { - return fmt.Errorf("failure running port forward process: %v", err) - } - - // Close the forwarder either when we exit or when an this processes is interrupted. - defer fw.Close() - closePortForwarderOnInterrupt(fw) - - log.Debugf("port-forward to prometheus pod ready") - - promAPI, err := prometheusAPI(fmt.Sprintf("http://%s", fw.Address())) - if err != nil { - return fmt.Errorf("failure running port forward process: %v", err) - } - - printHeader(c.OutOrStdout()) - - workloads := args - for _, workload := range workloads { - sm, err := metrics(promAPI, workload, metricsDuration) - if err != nil { - return fmt.Errorf("could not build metrics for workload '%s': %v", workload, err) - } - - printMetrics(c.OutOrStdout(), sm) - } - return nil -} - -func prometheusAPI(address string) (promv1.API, error) { - promClient, err := api.NewClient(api.Config{Address: address}) - if err != nil { - return nil, fmt.Errorf("could not build prometheus client: %v", err) - } - return promv1.NewAPI(promClient), nil -} - -func metrics(promAPI promv1.API, workload string, duration time.Duration) (workloadMetrics, error) { - parts := strings.Split(workload, ".") - wname := parts[0] - wns := "" - if len(parts) > 1 { - wns = parts[1] - } - - rpsQuery := fmt.Sprintf(`sum(rate(%s{%s=~"%s.*", %s=~"%s.*",reporter="destination"}[%s]))`, - reqTot, destWorkloadLabel, wname, destWorkloadNamespaceLabel, wns, duration) - errRPSQuery := fmt.Sprintf(`sum(rate(%s{%s=~"%s.*", %s=~"%s.*",reporter="destination",response_code=~"[45][0-9]{2}"}[%s]))`, - reqTot, destWorkloadLabel, wname, destWorkloadNamespaceLabel, wns, duration) - - var me *multierror.Error - var err error - sm := workloadMetrics{workload: workload} - sm.totalRPS, err = vectorValue(promAPI, rpsQuery) - if err != nil { - me = multierror.Append(me, err) - } - - sm.errorRPS, err = vectorValue(promAPI, errRPSQuery) - if err != nil { - me = multierror.Append(me, err) - } - - p50Latency, err := getLatency(promAPI, wname, wns, duration, 0.5) - if err != nil { - me = multierror.Append(me, err) - } - sm.p50Latency = p50Latency - - p90Latency, err := getLatency(promAPI, wname, wns, duration, 0.9) - if err != nil { - me = multierror.Append(me, err) - } - sm.p90Latency = p90Latency - - p99Latency, err := getLatency(promAPI, wname, wns, duration, 0.99) - if err != nil { - me = multierror.Append(me, err) - } - sm.p99Latency = p99Latency - - if me.ErrorOrNil() != nil { - return sm, fmt.Errorf("error retrieving some metrics: %v", me.Error()) - } - - return sm, nil -} - -func getLatency(promAPI promv1.API, workloadName, workloadNamespace string, duration time.Duration, quantile float64) (time.Duration, error) { - latencyQuery := fmt.Sprintf(`histogram_quantile(%f, sum(rate(%s_bucket{%s=~"%s.*", %s=~"%s.*",reporter="destination"}[%s])) by (le))`, - quantile, reqDur, destWorkloadLabel, workloadName, destWorkloadNamespaceLabel, workloadNamespace, duration) - - letency, err := vectorValue(promAPI, latencyQuery) - if err != nil { - return time.Duration(0), err - } - - return convertLatencyToDuration(letency), nil -} - -func vectorValue(promAPI promv1.API, query string) (float64, error) { - val, _, err := promAPI.Query(context.Background(), query, time.Now()) - if err != nil { - return 0, fmt.Errorf("query() failure for '%s': %v", query, err) - } - - log.Debugf("executing query: %s result:%s", query, val) - - switch v := val.(type) { - case model.Vector: - if v.Len() < 1 { - log.Debugf("no values for query: %s", query) - return 0, nil - } - - return float64(v[0].Value), nil - default: - return 0, errors.New("bad metric value type returned for query") - } -} - -func convertLatencyToDuration(val float64) time.Duration { - return time.Duration(val) * time.Millisecond -} - -func printHeader(writer io.Writer) { - w := tabwriter.NewWriter(writer, 13, 1, 2, ' ', tabwriter.AlignRight) - _, _ = fmt.Fprintf(w, "%40s\tTOTAL RPS\tERROR RPS\tP50 LATENCY\tP90 LATENCY\tP99 LATENCY\t\n", "WORKLOAD") - _ = w.Flush() -} - -func printMetrics(writer io.Writer, wm workloadMetrics) { - w := tabwriter.NewWriter(writer, 13, 1, 2, ' ', tabwriter.AlignRight) - _, _ = fmt.Fprintf(w, "%40s\t", wm.workload) - _, _ = fmt.Fprintf(w, "%.3f\t", wm.totalRPS) - _, _ = fmt.Fprintf(w, "%.3f\t", wm.errorRPS) - _, _ = fmt.Fprintf(w, "%s\t", wm.p50Latency) - _, _ = fmt.Fprintf(w, "%s\t", wm.p90Latency) - _, _ = fmt.Fprintf(w, "%s\t\n", wm.p99Latency) - _ = w.Flush() -} diff --git a/istioctl/cmd/metrics_test.go b/istioctl/cmd/metrics_test.go deleted file mode 100644 index e2d368c34..000000000 --- a/istioctl/cmd/metrics_test.go +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "bytes" - "context" - "fmt" - "regexp" - "strings" - "testing" - "time" -) - -import ( - promv1 "github.com/prometheus/client_golang/api/prometheus/v1" - prometheus_model "github.com/prometheus/common/model" - v1 "k8s.io/api/core/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -// mockPromAPI lets us mock calls to Prometheus API -type mockPromAPI struct { - cannedResponse map[string]prometheus_model.Value -} - -func mockExecClientAuthNoPilot(_, _, _ string) (kube.ExtendedClient, error) { - return &kube.MockClient{}, nil -} - -func TestMetricsNoPrometheus(t *testing.T) { - kubeClientWithRevision = mockExecClientAuthNoPilot - - cases := []testCase{ - { // case 0 - args: strings.Split("experimental metrics", " "), - expectedRegexp: regexp.MustCompile("Error: metrics requires workload name\n"), - wantException: true, - }, - { // case 1 - args: strings.Split("experimental metrics details", " "), - expectedOutput: "Error: no Prometheus pods found\n", - wantException: true, - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("case %d %s", i, strings.Join(c.args, " ")), func(t *testing.T) { - verifyOutput(t, c) - }) - } -} - -func TestMetrics(t *testing.T) { - kubeClientWithRevision = mockPortForwardClientAuthPrometheus - - cases := []testCase{ - { // case 0 - args: strings.Split("experimental metrics details", " "), - expectedRegexp: regexp.MustCompile("could not build metrics for workload"), - wantException: true, - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("case %d %s", i, strings.Join(c.args, " ")), func(t *testing.T) { - verifyOutput(t, c) - }) - } -} - -func mockPortForwardClientAuthPrometheus(_, _, _ string) (kube.ExtendedClient, error) { - return &kube.MockClient{ - DiscoverablePods: map[string]map[string]*v1.PodList{ - "dubbo-system": { - "app=prometheus": { - Items: []v1.Pod{ - { - TypeMeta: meta_v1.TypeMeta{ - Kind: "MockPod", - }, - }, - }, - }, - }, - }, - }, nil -} - -func TestAPI(t *testing.T) { - _, _ = prometheusAPI(fmt.Sprintf("http://localhost:%d", 1234)) -} - -var _ promv1.API = mockPromAPI{} - -func TestPrintMetrics(t *testing.T) { - mockProm := mockPromAPI{ - cannedResponse: map[string]prometheus_model.Value{ - "sum(rate(istio_requests_total{destination_workload=~\"details.*\", destination_workload_namespace=~\".*\",reporter=\"destination\"}[1m0s]))": prometheus_model.Vector{ // nolint: lll - &prometheus_model.Sample{Value: 0.04}, - }, - "sum(rate(istio_requests_total{destination_workload=~\"details.*\", destination_workload_namespace=~\".*\",reporter=\"destination\",response_code=~\"[45][0-9]{2}\"}[1m0s]))": prometheus_model.Vector{}, // nolint: lll - "histogram_quantile(0.500000, sum(rate(istio_request_duration_milliseconds_bucket{destination_workload=~\"details.*\", destination_workload_namespace=~\".*\",reporter=\"destination\"}[1m0s])) by (le))": prometheus_model.Vector{ // nolint: lll - &prometheus_model.Sample{Value: 2.5}, - }, - "histogram_quantile(0.900000, sum(rate(istio_request_duration_milliseconds_bucket{destination_workload=~\"details.*\", destination_workload_namespace=~\".*\",reporter=\"destination\"}[1m0s])) by (le))": prometheus_model.Vector{ // nolint: lll - &prometheus_model.Sample{Value: 4.5}, - }, - "histogram_quantile(0.990000, sum(rate(istio_request_duration_milliseconds_bucket{destination_workload=~\"details.*\", destination_workload_namespace=~\".*\",reporter=\"destination\"}[1m0s])) by (le))": prometheus_model.Vector{ // nolint: lll - &prometheus_model.Sample{Value: 4.95}, - }, - }, - } - workload := "details" - - sm, err := metrics(mockProm, workload, time.Minute) - if err != nil { - t.Fatalf("Unwanted exception %v", err) - } - - var out bytes.Buffer - printHeader(&out) - printMetrics(&out, sm) - output := out.String() - - expectedOutput := ` WORKLOAD TOTAL RPS ERROR RPS P50 LATENCY P90 LATENCY P99 LATENCY - details 0.040 0.000 2ms 4ms 4ms -` - if output != expectedOutput { - t.Fatalf("Unexpected output; got:\n %q\nwant:\n %q", output, expectedOutput) - } -} - -func (client mockPromAPI) Alerts(ctx context.Context) (promv1.AlertsResult, error) { - return promv1.AlertsResult{}, fmt.Errorf("TODO mockPromAPI doesn't mock Alerts") -} - -func (client mockPromAPI) AlertManagers(ctx context.Context) (promv1.AlertManagersResult, error) { - return promv1.AlertManagersResult{}, fmt.Errorf("TODO mockPromAPI doesn't mock AlertManagers") -} - -func (client mockPromAPI) CleanTombstones(ctx context.Context) error { - return nil -} - -func (client mockPromAPI) Config(ctx context.Context) (promv1.ConfigResult, error) { - return promv1.ConfigResult{}, nil -} - -func (client mockPromAPI) DeleteSeries(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) error { - return nil -} - -func (client mockPromAPI) Flags(ctx context.Context) (promv1.FlagsResult, error) { - return nil, nil -} - -func (client mockPromAPI) Query(ctx context.Context, query string, ts time.Time, opts ...promv1.Option) (prometheus_model.Value, promv1.Warnings, error) { - canned, ok := client.cannedResponse[query] - if !ok { - return prometheus_model.Vector{}, nil, nil - } - return canned, nil, nil -} - -func (client mockPromAPI) TSDB(ctx context.Context) (promv1.TSDBResult, error) { - return promv1.TSDBResult{}, nil -} - -func (client mockPromAPI) QueryRange(ctx context.Context, query string, r promv1.Range, opts ...promv1.Option) (prometheus_model.Value, promv1.Warnings, error) { - canned, ok := client.cannedResponse[query] - if !ok { - return prometheus_model.Vector{}, nil, nil - } - return canned, nil, nil -} - -func (client mockPromAPI) WalReplay(ctx context.Context) (promv1.WalReplayStatus, error) { - // TODO implement me - panic("implement me") -} - -func (client mockPromAPI) Series(ctx context.Context, matches []string, - startTime time.Time, endTime time.Time) ([]prometheus_model.LabelSet, promv1.Warnings, error) { - return nil, nil, nil -} - -func (client mockPromAPI) Snapshot(ctx context.Context, skipHead bool) (promv1.SnapshotResult, error) { - return promv1.SnapshotResult{}, nil -} - -func (client mockPromAPI) Rules(ctx context.Context) (promv1.RulesResult, error) { - return promv1.RulesResult{}, nil -} - -func (client mockPromAPI) Targets(ctx context.Context) (promv1.TargetsResult, error) { - return promv1.TargetsResult{}, nil -} - -func (client mockPromAPI) TargetsMetadata(ctx context.Context, matchTarget string, metric string, limit string) ([]promv1.MetricMetadata, error) { - return nil, nil -} - -func (client mockPromAPI) Runtimeinfo(ctx context.Context) (promv1.RuntimeinfoResult, error) { - return promv1.RuntimeinfoResult{}, nil -} - -func (client mockPromAPI) Metadata(ctx context.Context, metric string, limit string) (map[string][]promv1.Metadata, error) { - return nil, nil -} - -func (client mockPromAPI) LabelNames(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]string, promv1.Warnings, error) { - return nil, nil, nil -} - -func (client mockPromAPI) LabelValues(context.Context, string, []string, time.Time, time.Time) (prometheus_model.LabelValues, promv1.Warnings, error) { - return nil, nil, nil -} - -func (client mockPromAPI) Buildinfo(ctx context.Context) (promv1.BuildinfoResult, error) { - return promv1.BuildinfoResult{}, nil -} - -func (client mockPromAPI) QueryExemplars(ctx context.Context, query string, startTime time.Time, endTime time.Time) ([]promv1.ExemplarQueryResult, error) { - return nil, nil -} diff --git a/istioctl/cmd/options.go b/istioctl/cmd/options.go deleted file mode 100644 index 78a3efb49..000000000 --- a/istioctl/cmd/options.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "github.com/spf13/cobra" - "github.com/spf13/pflag" -) - -var helpFlags = map[string]bool{ - "log_as_json": true, - "log_stacktrace_level": true, - "log_target": true, - "log_caller": true, - "log_output_level": true, - // istioctl also inherits support for log_rotate, log_rotate_max_age, log_rotate_max_backups, - // log_rotate_max_size, but these are rarely appropriate for a user-facing CLI so we ignore them -} - -func optionsCommand(rootCmd *cobra.Command) *cobra.Command { - retval := &cobra.Command{ - Use: "options", - Short: "Displays istioctl global options", - Args: cobra.ExactArgs(0), - } - - retval.SetHelpFunc(func(c *cobra.Command, args []string) { - c.Printf("The following options can be passed to any command:\n") - // (Currently the only global options we show are help options) - rootCmd.PersistentFlags().VisitAll(func(flag *pflag.Flag) { - if _, ok := helpFlags[flag.Name]; ok { - // Currently every flag.Shorthand is "", so there is no point in showing shorthands - shorthand := " " - if flag.Shorthand != "" { - shorthand = "-" + flag.Shorthand + "," - } - c.Printf(" %s --%s: %s\n", shorthand, flag.Name, flag.Usage) - } - }) - }) - - return retval -} - -// validateFlagIsSetManuallyOrNot can validate that a persistent flag is set manually or not by user for given command -func validateFlagIsSetManuallyOrNot(istioCmd *cobra.Command, flagName string) bool { - if istioCmd != nil { - allPersistentFlagSet := istioCmd.PersistentFlags() - if flagName != "" { - return allPersistentFlagSet.Changed(flagName) - } - } - return false -} diff --git a/istioctl/cmd/options_test.go b/istioctl/cmd/options_test.go deleted file mode 100644 index a9ad8066a..000000000 --- a/istioctl/cmd/options_test.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "bytes" - "regexp" - "testing" -) - -// nolint: lll -var expectedOutput = `The following options can be passed to any command: - --log_as_json: Whether to format output as JSON or in plain console-friendly format - --log_caller: Comma-separated list of scopes for which to include caller information, scopes can be any of \[.*\] - --log_output_level: Comma-separated minimum per-scope logging level of messages to output, in the form of :,:,... where scope can be one of \[.*\] and level can be one of \[.*\] - --log_stacktrace_level: Comma-separated minimum per-scope logging level at which stack traces are captured, in the form of :,,... where scope can be one of \[.*\] and level can be one of \[.*\] - --log_target: The set of paths where to output the log. This can be any path as well as the special values stdout and stderr -` - -func TestLogHelp(t *testing.T) { - var out bytes.Buffer - rootCmd := GetRootCmd([]string{"options"}) - rootCmd.SetOut(&out) - rootCmd.SetErr(&out) - - fErr := rootCmd.Execute() - if fErr != nil { - t.Fatalf("options failed with %v and %q\n", fErr, out.String()) - } - if !regexp.MustCompile(expectedOutput).Match(out.Bytes()) { - t.Fatalf("'istioctl options' expected output\n%s\n got\n%s", - expectedOutput, out.String()) - } -} diff --git a/istioctl/cmd/precheck.go b/istioctl/cmd/precheck.go deleted file mode 100644 index 166ff6a7f..000000000 --- a/istioctl/cmd/precheck.go +++ /dev/null @@ -1,477 +0,0 @@ -// Copyright © 2021 NAME HERE -// -// 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 cmd - -import ( - "context" - "errors" - "fmt" - "net" - "strconv" - "strings" -) - -import ( - adminapi "github.com/envoyproxy/go-control-plane/envoy/admin/v3" - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - "github.com/fatih/color" - "github.com/spf13/cobra" - "golang.org/x/sync/errgroup" - "golang.org/x/sync/semaphore" - authorizationapi "k8s.io/api/authorization/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/clioptions" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/install/k8sversion" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/formatting" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/maturity" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/diag" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/local" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - kube3 "github.com/apache/dubbo-go-pixiu/pkg/config/legacy/source/kube" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/url" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -func preCheck() *cobra.Command { - var opts clioptions.ControlPlaneOptions - var skipControlPlane bool - // cmd represents the upgradeCheck command - cmd := &cobra.Command{ - Use: "precheck", - Short: "check whether Istio can safely be installed or upgrade", - Long: `precheck inspects a Kubernetes cluster for Istio install and upgrade requirements.`, - Example: ` # Verify that Istio can be installed or upgraded - istioctl x precheck - - # Check only a single namespace - istioctl x precheck --namespace default`, - RunE: func(cmd *cobra.Command, args []string) (err error) { - cli, err := kube.NewExtendedClient(kube.BuildClientCmd(kubeconfig, configContext), revision) - if err != nil { - return err - } - - msgs := diag.Messages{} - if !skipControlPlane { - msgs, err = checkControlPlane(cli) - if err != nil { - return err - } - } - nsmsgs, err := checkDataPlane(cli, namespace) - if err != nil { - return err - } - msgs.Add(nsmsgs...) - // Print all the messages to stdout in the specified format - msgs = msgs.SortedDedupedCopy() - output, err := formatting.Print(msgs, msgOutputFormat, colorize) - if err != nil { - return err - } - if len(msgs) == 0 { - fmt.Fprintf(cmd.ErrOrStderr(), color.New(color.FgGreen).Sprint("✔")+" No issues found when checking the cluster. Istio is safe to install or upgrade!\n"+ - " To get started, check out https://istio.io/latest/docs/setup/getting-started/\n") - } else { - fmt.Fprintln(cmd.OutOrStdout(), output) - } - for _, m := range msgs { - if m.Type.Level().IsWorseThanOrEqualTo(diag.Warning) { - e := fmt.Sprintf(`Issues found when checking the cluster. Istio may not be safe to install or upgrade. -See %s for more information about causes and resolutions.`, url.ConfigAnalysis) - return errors.New(e) - } - } - return nil - }, - } - cmd.PersistentFlags().BoolVar(&skipControlPlane, "skip-controlplane", false, "skip checking the control plane") - opts.AttachControlPlaneFlags(cmd) - return cmd -} - -func checkControlPlane(cli kube.ExtendedClient) (diag.Messages, error) { - msgs := diag.Messages{} - - m, err := checkServerVersion(cli) - if err != nil { - return nil, err - } - msgs = append(msgs, m...) - - msgs = append(msgs, checkInstallPermissions(cli)...) - - // TODO: add more checks - - sa := local.NewSourceAnalyzer(analysis.Combine("upgrade precheck", &maturity.AlphaAnalyzer{}), - resource.Namespace(selectedNamespace), resource.Namespace(istioNamespace), nil, true, analysisTimeout) - // Set up the kube client - config := kube.BuildClientCmd(kubeconfig, configContext) - restConfig, err := config.ClientConfig() - if err != nil { - return nil, err - } - - k, err := kube.NewClient(kube.NewClientConfigForRestConfig(restConfig)) - if err != nil { - return nil, err - } - sa.AddRunningKubeSource(k) - cancel := make(chan struct{}) - result, err := sa.Analyze(cancel) - if err != nil { - return nil, err - } - if result.Messages != nil { - msgs = append(msgs, result.Messages...) - } - - return msgs, nil -} - -func checkInstallPermissions(cli kube.ExtendedClient) diag.Messages { - Resources := []struct { - namespace string - group string - version string - name string - }{ - { - version: "v1", - name: "Namespace", - }, - { - namespace: istioNamespace, - group: "rbac.authorization.k8s.io", - version: "v1", - name: "ClusterRole", - }, - { - namespace: istioNamespace, - group: "rbac.authorization.k8s.io", - version: "v1", - name: "ClusterRoleBinding", - }, - { - namespace: istioNamespace, - group: "apiextensions.k8s.io", - version: "v1", - name: "CustomResourceDefinition", - }, - { - namespace: istioNamespace, - group: "rbac.authorization.k8s.io", - version: "v1", - name: "Role", - }, - { - namespace: istioNamespace, - version: "v1", - name: "ServiceAccount", - }, - { - namespace: istioNamespace, - version: "v1", - name: "Service", - }, - { - namespace: istioNamespace, - group: "apps", - version: "v1", - name: "Deployments", - }, - { - namespace: istioNamespace, - version: "v1", - name: "ConfigMap", - }, - { - group: "admissionregistration.k8s.io", - version: "v1", - name: "MutatingWebhookConfiguration", - }, - { - group: "admissionregistration.k8s.io", - version: "v1", - name: "ValidatingWebhookConfiguration", - }, - } - msgs := diag.Messages{} - for _, r := range Resources { - err := checkCanCreateResources(cli, r.namespace, r.group, r.version, r.name) - if err != nil { - msgs.Add(msg.NewInsufficientPermissions(&resource.Instance{Origin: clusterOrigin{}}, r.name, err.Error())) - } - } - return msgs -} - -func checkCanCreateResources(c kube.ExtendedClient, namespace, group, version, name string) error { - s := &authorizationapi.SelfSubjectAccessReview{ - Spec: authorizationapi.SelfSubjectAccessReviewSpec{ - ResourceAttributes: &authorizationapi.ResourceAttributes{ - Namespace: namespace, - Verb: "create", - Group: group, - Version: version, - Resource: name, - }, - }, - } - - response, err := c.AuthorizationV1().SelfSubjectAccessReviews().Create(context.Background(), s, metav1.CreateOptions{}) - if err != nil { - return err - } - - if !response.Status.Allowed { - if len(response.Status.Reason) > 0 { - return errors.New(response.Status.Reason) - } - return errors.New("permission denied") - } - return nil -} - -func checkServerVersion(cli kube.ExtendedClient) (diag.Messages, error) { - v, err := cli.GetKubernetesVersion() - if err != nil { - return nil, fmt.Errorf("failed to get the Kubernetes version: %v", err) - } - compatible, err := k8sversion.CheckKubernetesVersion(v) - if err != nil { - return nil, err - } - if !compatible { - return []diag.Message{ - msg.NewUnsupportedKubernetesVersion(&resource.Instance{Origin: clusterOrigin{}}, v.String(), fmt.Sprintf("1.%d", k8sversion.MinK8SVersion)), - }, nil - } - return nil, nil -} - -func checkDataPlane(cli kube.ExtendedClient, namespace string) (diag.Messages, error) { - msgs := diag.Messages{} - - m, err := checkListeners(cli, namespace) - if err != nil { - return nil, err - } - msgs = append(msgs, m...) - - // TODO: add more checks - - return msgs, nil -} - -func checkListeners(cli kube.ExtendedClient, namespace string) (diag.Messages, error) { - pods, err := cli.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{ - // Find all running pods - FieldSelector: "status.phase=Running", - // Find all injected pods - LabelSelector: "security.istio.io/tlsMode=istio", - }) - if err != nil { - return nil, err - } - - var messages diag.Messages = make([]diag.Message, 0) - g := errgroup.Group{} - - sem := semaphore.NewWeighted(25) - for _, pod := range pods.Items { - pod := pod - g.Go(func() error { - _ = sem.Acquire(context.Background(), 1) - defer sem.Release(1) - // Fetch list of all clusters to get which ports we care about - resp, err := cli.EnvoyDo(context.Background(), pod.Name, pod.Namespace, "GET", "config_dump?resource=dynamic_active_clusters&mask=cluster.name") - if err != nil { - fmt.Println("failed to get config dump: ", err) - return nil - } - ports, err := extractInboundPorts(resp) - if err != nil { - fmt.Println("failed to get ports: ", err) - return nil - } - - // Next, look at what ports the pod is actually listening on - // This requires parsing the output from ss; the version we use doesn't support JSON - out, _, err := cli.PodExec(pod.Name, pod.Namespace, "istio-proxy", "ss -ltnH") - if err != nil { - if strings.Contains(err.Error(), "executable file not found") { - // Likely distroless or other custom build without ss. Nothing we can do here... - return nil - } - fmt.Println("failed to get listener state: ", err) - return nil - } - for _, ss := range strings.Split(out, "\n") { - if len(ss) == 0 { - continue - } - bind, port, err := net.SplitHostPort(getColumn(ss, 3)) - if err != nil { - fmt.Println("failed to get parse state: ", err) - continue - } - ip := net.ParseIP(bind) - portn, _ := strconv.Atoi(port) - if _, f := ports[portn]; f { - c := ports[portn] - if bind == "" { - continue - } else if bind == "*" || ip.IsUnspecified() { - c.Wildcard = true - } else if ip.IsLoopback() { - c.Lo = true - } else { - c.Explicit = true - } - ports[portn] = c - } - } - - origin := &kube3.Origin{ - Collection: collections.K8SCoreV1Pods.Name(), - Kind: collections.K8SCoreV1Pods.Resource().Kind(), - FullName: resource.FullName{ - Namespace: resource.Namespace(pod.Namespace), - Name: resource.LocalName(pod.Name), - }, - Version: resource.Version(pod.ResourceVersion), - } - for port, status := range ports { - // Binding to localhost no longer works out of the box on Istio 1.10+, give them a warning. - if status.Lo { - messages.Add(msg.NewLocalhostListener(&resource.Instance{Origin: origin}, fmt.Sprint(port))) - } - } - return nil - }) - } - if err := g.Wait(); err != nil { - return nil, err - } - return messages, nil -} - -func getColumn(line string, col int) string { - res := []byte{} - prevSpace := false - for _, c := range line { - if col < 0 { - return string(res) - } - if c == ' ' { - if !prevSpace { - col-- - } - prevSpace = true - continue - } - prevSpace = false - if col == 0 { - res = append(res, byte(c)) - } - } - return string(res) -} - -func extractInboundPorts(configdump []byte) (map[int]bindStatus, error) { - ports := map[int]bindStatus{} - cd := &adminapi.ConfigDump{} - if err := protomarshal.Unmarshal(configdump, cd); err != nil { - return nil, err - } - for _, cdump := range cd.Configs { - clw := &adminapi.ClustersConfigDump_DynamicCluster{} - if err := cdump.UnmarshalTo(clw); err != nil { - return nil, err - } - cl := &cluster.Cluster{} - if err := clw.Cluster.UnmarshalTo(cl); err != nil { - return nil, err - } - dir, _, _, port := model.ParseSubsetKey(cl.Name) - if dir == model.TrafficDirectionInbound { - ports[port] = bindStatus{} - } - } - return ports, nil -} - -type bindStatus struct { - Lo bool - Wildcard bool - Explicit bool -} - -func (b bindStatus) Any() bool { - return b.Lo || b.Wildcard || b.Explicit -} - -func (b bindStatus) String() string { - res := []string{} - if b.Lo { - res = append(res, "Localhost") - } - if b.Wildcard { - res = append(res, "Wildcard") - } - if b.Explicit { - res = append(res, "Explicit") - } - if len(res) == 0 { - return "Unknown" - } - return strings.Join(res, ", ") -} - -// clusterOrigin defines an Origin that refers to the cluster -type clusterOrigin struct{} - -func (o clusterOrigin) String() string { - return "" -} - -func (o clusterOrigin) FriendlyName() string { - return "Cluster" -} - -func (o clusterOrigin) Comparator() string { - return o.FriendlyName() -} - -func (o clusterOrigin) Namespace() resource.Namespace { - return "" -} - -func (o clusterOrigin) Reference() resource.Reference { - return nil -} - -func (o clusterOrigin) FieldMap() map[string]int { - return make(map[string]int) -} diff --git a/istioctl/cmd/proxyconfig.go b/istioctl/cmd/proxyconfig.go deleted file mode 100644 index f73a03374..000000000 --- a/istioctl/cmd/proxyconfig.go +++ /dev/null @@ -1,1199 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "errors" - "fmt" - "io" - "os" - "regexp" - "strings" -) - -import ( - "github.com/spf13/cobra" - "istio.io/pkg/log" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/handlers" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/writer/envoy/clusters" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/writer/envoy/configdump" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" -) - -const ( - jsonOutput = "json" - yamlOutput = "yaml" - summaryOutput = "short" - prometheusOutput = "prom" - prometheusMergedOutput = "prom-merged" -) - -var ( - fqdn, direction, subset string - port int - verboseProxyConfig bool - - address, listenerType, statsType string - - routeName string - - clusterName, status string - - // output format (yaml or short) - outputFormat string -) - -// Level is an enumeration of all supported log levels. -type Level int - -const ( - defaultLoggerName = "level" - defaultOutputLevel = WarningLevel -) - -const ( - // OffLevel disables logging - OffLevel Level = iota - // CriticalLevel enables critical level logging - CriticalLevel - // ErrorLevel enables error level logging - ErrorLevel - // WarningLevel enables warning level logging - WarningLevel - // InfoLevel enables info level logging - InfoLevel - // DebugLevel enables debug level logging - DebugLevel - // TraceLevel enables trace level logging - TraceLevel -) - -// existing sorted active loggers -var activeLoggers = []string{ - "admin", - "aws", - "assert", - "backtrace", - "client", - "config", - "connection", - "conn_handler", // Added through https://github.com/envoyproxy/envoy/pull/8263 - "dubbo", - "file", - "filter", - "forward_proxy", - "grpc", - "hc", - "health_checker", - "http", - "http2", - "hystrix", - "init", - "io", - "jwt", - "kafka", - "lua", - "main", - "misc", - "mongo", - "quic", - "pool", - "rbac", - "redis", - "router", - "runtime", - "stats", - "secret", - "tap", - "testing", - "thrift", - "tracing", - "upstream", - "udp", - "wasm", -} - -var levelToString = map[Level]string{ - TraceLevel: "trace", - DebugLevel: "debug", - InfoLevel: "info", - WarningLevel: "warning", - ErrorLevel: "error", - CriticalLevel: "critical", - OffLevel: "off", -} - -var stringToLevel = map[string]Level{ - "trace": TraceLevel, - "debug": DebugLevel, - "info": InfoLevel, - "warning": WarningLevel, - "error": ErrorLevel, - "critical": CriticalLevel, - "off": OffLevel, -} - -var ( - loggerLevelString = "" - reset = false -) - -func extractConfigDump(podName, podNamespace string) ([]byte, error) { - kubeClient, err := kubeClient(kubeconfig, configContext) - if err != nil { - return nil, fmt.Errorf("failed to create k8s client: %v", err) - } - path := "config_dump" - debug, err := kubeClient.EnvoyDo(context.TODO(), podName, podNamespace, "GET", path) - if err != nil { - return nil, fmt.Errorf("failed to execute command on %s.%s sidecar: %v", podName, podNamespace, err) - } - return debug, err -} - -func setupPodConfigdumpWriter(podName, podNamespace string, out io.Writer) (*configdump.ConfigWriter, error) { - debug, err := extractConfigDump(podName, podNamespace) - if err != nil { - return nil, err - } - return setupConfigdumpEnvoyConfigWriter(debug, out) -} - -func readFile(filename string) ([]byte, error) { - file := os.Stdin - if filename != "-" { - var err error - file, err = os.Open(filename) - if err != nil { - return nil, err - } - } - defer func() { - if err := file.Close(); err != nil { - log.Errorf("failed to close %s: %s", filename, err) - } - }() - return io.ReadAll(file) -} - -func setupFileConfigdumpWriter(filename string, out io.Writer) (*configdump.ConfigWriter, error) { - data, err := readFile(filename) - if err != nil { - return nil, err - } - return setupConfigdumpEnvoyConfigWriter(data, out) -} - -func setupConfigdumpEnvoyConfigWriter(debug []byte, out io.Writer) (*configdump.ConfigWriter, error) { - cw := &configdump.ConfigWriter{Stdout: out} - err := cw.Prime(debug) - if err != nil { - return nil, err - } - return cw, nil -} - -func setupEnvoyClusterStatsConfig(podName, podNamespace string, outputFormat string) (string, error) { - kubeClient, err := kubeClient(kubeconfig, configContext) - if err != nil { - return "", fmt.Errorf("failed to create Kubernetes client: %v", err) - } - path := "clusters" - if outputFormat == jsonOutput || outputFormat == yamlOutput { - // for yaml output we will convert the json to yaml when printed - path += "?format=json" - } - result, err := kubeClient.EnvoyDo(context.TODO(), podName, podNamespace, "GET", path) - if err != nil { - return "", fmt.Errorf("failed to execute command on Envoy: %v", err) - } - return string(result), nil -} - -func setupEnvoyServerStatsConfig(podName, podNamespace string, outputFormat string) (string, error) { - kubeClient, err := kubeClient(kubeconfig, configContext) - if err != nil { - return "", fmt.Errorf("failed to create Kubernetes client: %v", err) - } - path := "stats" - port := 15000 - if outputFormat == jsonOutput || outputFormat == yamlOutput { - // for yaml output we will convert the json to yaml when printed - path += "?format=json" - } else if outputFormat == prometheusOutput { - path += "/prometheus" - } else if outputFormat == prometheusMergedOutput { - path += "/prometheus" - port = 15020 - } - - result, err := kubeClient.EnvoyDoWithPort(context.Background(), podName, podNamespace, "GET", path, port) - if err != nil { - return "", fmt.Errorf("failed to execute command on Envoy: %v", err) - } - return string(result), nil -} - -func setupEnvoyLogConfig(param, podName, podNamespace string) (string, error) { - kubeClient, err := kubeClient(kubeconfig, configContext) - if err != nil { - return "", fmt.Errorf("failed to create Kubernetes client: %v", err) - } - path := "logging" - if param != "" { - path = path + "?" + param - } - result, err := kubeClient.EnvoyDo(context.TODO(), podName, podNamespace, "POST", path) - if err != nil { - return "", fmt.Errorf("failed to execute command on Envoy: %v", err) - } - return string(result), nil -} - -func getLogLevelFromConfigMap() (string, error) { - valuesConfig, err := getValuesFromConfigMap(kubeconfig, "") - if err != nil { - return "", err - } - var values struct { - SidecarInjectorWebhook struct { - Global struct { - Proxy struct { - LogLevel string `json:"logLevel"` - } `json:"proxy"` - } `json:"global"` - } `json:"sidecarInjectorWebhook"` - } - if err := yaml.Unmarshal([]byte(valuesConfig), &values); err != nil { - return "", fmt.Errorf("failed to parse values config: %v [%v]", err, valuesConfig) - } - return values.SidecarInjectorWebhook.Global.Proxy.LogLevel, nil -} - -func setupPodClustersWriter(podName, podNamespace string, out io.Writer) (*clusters.ConfigWriter, error) { - kubeClient, err := kubeClient(kubeconfig, configContext) - if err != nil { - return nil, fmt.Errorf("failed to create k8s client: %v", err) - } - path := "clusters?format=json" - debug, err := kubeClient.EnvoyDo(context.TODO(), podName, podNamespace, "GET", path) - if err != nil { - return nil, fmt.Errorf("failed to execute command on Envoy: %v", err) - } - return setupClustersEnvoyConfigWriter(debug, out) -} - -func setupFileClustersWriter(filename string, out io.Writer) (*clusters.ConfigWriter, error) { - file, err := os.Open(filename) - if err != nil { - return nil, err - } - defer func() { - if err := file.Close(); err != nil { - log.Errorf("failed to close %s: %s", filename, err) - } - }() - data, err := io.ReadAll(file) - if err != nil { - return nil, err - } - return setupClustersEnvoyConfigWriter(data, out) -} - -// TODO(fisherxu): migrate this to config dump when implemented in Envoy -// Issue to track -> https://github.com/envoyproxy/envoy/issues/3362 -func setupClustersEnvoyConfigWriter(debug []byte, out io.Writer) (*clusters.ConfigWriter, error) { - cw := &clusters.ConfigWriter{Stdout: out} - err := cw.Prime(debug) - if err != nil { - return nil, err - } - return cw, nil -} - -func clusterConfigCmd() *cobra.Command { - var podName, podNamespace string - - clusterConfigCmd := &cobra.Command{ - Use: "cluster [/][.]", - Short: "Retrieves cluster configuration for the Envoy in the specified pod", - Long: `Retrieve information about cluster configuration for the Envoy instance in the specified pod.`, - Example: ` # Retrieve summary about cluster configuration for a given pod from Envoy. - istioctl proxy-config clusters - - # Retrieve cluster summary for clusters with port 9080. - istioctl proxy-config clusters --port 9080 - - # Retrieve full cluster dump for clusters that are inbound with a FQDN of details.default.svc.cluster.local. - istioctl proxy-config clusters --fqdn details.default.svc.cluster.local --direction inbound -o json - - # Retrieve cluster summary without using Kubernetes API - ssh 'curl localhost:15000/config_dump' > envoy-config.json - istioctl proxy-config clusters --file envoy-config.json -`, - Aliases: []string{"clusters", "c"}, - Args: func(cmd *cobra.Command, args []string) error { - if (len(args) == 1) != (configDumpFile == "") { - cmd.Println(cmd.UsageString()) - return fmt.Errorf("cluster requires pod name or --file parameter") - } - return nil - }, - RunE: func(c *cobra.Command, args []string) error { - var configWriter *configdump.ConfigWriter - var err error - if len(args) == 1 { - if podName, podNamespace, err = getPodName(args[0]); err != nil { - return err - } - configWriter, err = setupPodConfigdumpWriter(podName, podNamespace, c.OutOrStdout()) - } else { - configWriter, err = setupFileConfigdumpWriter(configDumpFile, c.OutOrStdout()) - } - if err != nil { - return err - } - filter := configdump.ClusterFilter{ - FQDN: host.Name(fqdn), - Port: port, - Subset: subset, - Direction: model.TrafficDirection(direction), - } - switch outputFormat { - case summaryOutput: - return configWriter.PrintClusterSummary(filter) - case jsonOutput, yamlOutput: - return configWriter.PrintClusterDump(filter, outputFormat) - default: - return fmt.Errorf("output format %q not supported", outputFormat) - } - }, - ValidArgsFunction: validPodsNameArgs, - } - - clusterConfigCmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", summaryOutput, "Output format: one of json|yaml|short") - clusterConfigCmd.PersistentFlags().StringVar(&fqdn, "fqdn", "", "Filter clusters by substring of Service FQDN field") - clusterConfigCmd.PersistentFlags().StringVar(&direction, "direction", "", "Filter clusters by Direction field") - clusterConfigCmd.PersistentFlags().StringVar(&subset, "subset", "", "Filter clusters by substring of Subset field") - clusterConfigCmd.PersistentFlags().IntVar(&port, "port", 0, "Filter clusters by Port field") - clusterConfigCmd.PersistentFlags().StringVarP(&configDumpFile, "file", "f", "", - "Envoy config dump JSON file") - - return clusterConfigCmd -} - -func allConfigCmd() *cobra.Command { - allConfigCmd := &cobra.Command{ - Use: "all [/][.]", - Short: "Retrieves all configuration for the Envoy in the specified pod", - Long: `Retrieve information about all configuration for the Envoy instance in the specified pod.`, - Example: ` # Retrieve summary about all configuration for a given pod from Envoy. - istioctl proxy-config all - - # Retrieve full cluster dump as JSON - istioctl proxy-config all -o json - - # Retrieve full cluster dump with short syntax - istioctl pc a - - # Retrieve cluster summary without using Kubernetes API - ssh 'curl localhost:15000/config_dump' > envoy-config.json - istioctl proxy-config all --file envoy-config.json -`, - Aliases: []string{"a"}, - Args: func(cmd *cobra.Command, args []string) error { - if (len(args) == 1) != (configDumpFile == "") { - cmd.Println(cmd.UsageString()) - return fmt.Errorf("all requires pod name or --file parameter") - } - return nil - }, - RunE: func(c *cobra.Command, args []string) error { - switch outputFormat { - case jsonOutput, yamlOutput: - var dump []byte - var err error - if len(args) == 1 { - podName, podNamespace, err := getPodName(args[0]) - if err != nil { - return err - } - dump, err = extractConfigDump(podName, podNamespace) - if err != nil { - return err - } - } else { - dump, err = readFile(configDumpFile) - if err != nil { - return err - } - } - if outputFormat == yamlOutput { - if dump, err = yaml.JSONToYAML(dump); err != nil { - return err - } - } - fmt.Fprintln(c.OutOrStdout(), string(dump)) - - case summaryOutput: - var configWriter *configdump.ConfigWriter - if len(args) == 1 { - podName, podNamespace, err := getPodName(args[0]) - if err != nil { - return err - } - configWriter, err = setupPodConfigdumpWriter(podName, podNamespace, c.OutOrStdout()) - if err != nil { - return err - } - } else { - var err error - configWriter, err = setupFileConfigdumpWriter(configDumpFile, c.OutOrStdout()) - if err != nil { - return err - } - } - return configWriter.PrintFullSummary( - configdump.ClusterFilter{ - FQDN: host.Name(fqdn), - Port: port, - Subset: subset, - Direction: model.TrafficDirection(direction), - }, - configdump.ListenerFilter{ - Address: address, - Port: uint32(port), - Type: listenerType, - Verbose: verboseProxyConfig, - }, - configdump.RouteFilter{ - Name: routeName, - Verbose: verboseProxyConfig, - }, - ) - default: - return fmt.Errorf("output format %q not supported", outputFormat) - } - return nil - }, - ValidArgsFunction: validPodsNameArgs, - } - - allConfigCmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", summaryOutput, "Output format: one of json|yaml|short") - allConfigCmd.PersistentFlags().StringVarP(&configDumpFile, "file", "f", "", - "Envoy config dump file") - allConfigCmd.PersistentFlags().BoolVar(&verboseProxyConfig, "verbose", true, "Output more information") - - // cluster - allConfigCmd.PersistentFlags().StringVar(&fqdn, "fqdn", "", "Filter clusters by substring of Service FQDN field") - allConfigCmd.PersistentFlags().StringVar(&direction, "direction", "", "Filter clusters by Direction field") - allConfigCmd.PersistentFlags().StringVar(&subset, "subset", "", "Filter clusters by substring of Subset field") - - // applies to cluster and route - allConfigCmd.PersistentFlags().IntVar(&port, "port", 0, "Filter clusters and listeners by Port field") - - // Listener - allConfigCmd.PersistentFlags().StringVar(&address, "address", "", "Filter listeners by address field") - allConfigCmd.PersistentFlags().StringVar(&listenerType, "type", "", "Filter listeners by type field") - - // route - allConfigCmd.PersistentFlags().StringVar(&routeName, "name", "", "Filter listeners by route name field") - - return allConfigCmd -} - -func listenerConfigCmd() *cobra.Command { - var podName, podNamespace string - - listenerConfigCmd := &cobra.Command{ - Use: "listener [/][.]", - Short: "Retrieves listener configuration for the Envoy in the specified pod", - Long: `Retrieve information about listener configuration for the Envoy instance in the specified pod.`, - Example: ` # Retrieve summary about listener configuration for a given pod from Envoy. - istioctl proxy-config listeners - - # Retrieve listener summary for listeners with port 9080. - istioctl proxy-config listeners --port 9080 - - # Retrieve full listener dump for HTTP listeners with a wildcard address (0.0.0.0). - istioctl proxy-config listeners --type HTTP --address 0.0.0.0 -o json - - # Retrieve listener summary without using Kubernetes API - ssh 'curl localhost:15000/config_dump' > envoy-config.json - istioctl proxy-config listeners --file envoy-config.json -`, - Aliases: []string{"listeners", "l"}, - Args: func(cmd *cobra.Command, args []string) error { - if (len(args) == 1) != (configDumpFile == "") { - cmd.Println(cmd.UsageString()) - return fmt.Errorf("listener requires pod name or --file parameter") - } - return nil - }, - RunE: func(c *cobra.Command, args []string) error { - var configWriter *configdump.ConfigWriter - var err error - if len(args) == 1 { - if podName, podNamespace, err = getPodName(args[0]); err != nil { - return err - } - configWriter, err = setupPodConfigdumpWriter(podName, podNamespace, c.OutOrStdout()) - } else { - configWriter, err = setupFileConfigdumpWriter(configDumpFile, c.OutOrStdout()) - } - if err != nil { - return err - } - filter := configdump.ListenerFilter{ - Address: address, - Port: uint32(port), - Type: listenerType, - Verbose: verboseProxyConfig, - } - - switch outputFormat { - case summaryOutput: - return configWriter.PrintListenerSummary(filter) - case jsonOutput, yamlOutput: - return configWriter.PrintListenerDump(filter, outputFormat) - default: - return fmt.Errorf("output format %q not supported", outputFormat) - } - }, - ValidArgsFunction: validPodsNameArgs, - } - - listenerConfigCmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", summaryOutput, "Output format: one of json|yaml|short") - listenerConfigCmd.PersistentFlags().StringVar(&address, "address", "", "Filter listeners by address field") - listenerConfigCmd.PersistentFlags().StringVar(&listenerType, "type", "", "Filter listeners by type field") - listenerConfigCmd.PersistentFlags().IntVar(&port, "port", 0, "Filter listeners by Port field") - listenerConfigCmd.PersistentFlags().BoolVar(&verboseProxyConfig, "verbose", true, "Output more information") - listenerConfigCmd.PersistentFlags().StringVarP(&configDumpFile, "file", "f", "", - "Envoy config dump JSON file") - - return listenerConfigCmd -} - -func statsConfigCmd() *cobra.Command { - var podName, podNamespace string - - statsConfigCmd := &cobra.Command{ - Use: "envoy-stats [/][.]", - Short: "Retrieves Envoy metrics in the specified pod", - Long: `Retrieve Envoy emitted metrics for the specified pod.`, - Example: ` # Retrieve Envoy emitted metrics for the specified pod. - istioctl experimental envoy-stats - - # Retrieve Envoy server metrics in prometheus format - istioctl experimental envoy-stats --output prom - - # Retrieve Envoy server metrics in prometheus format with merged application metrics - istioctl experimental envoy-stats --output prom-merged - - # Retrieve Envoy cluster metrics - istioctl experimental envoy-stats --type clusters -`, - Aliases: []string{"es"}, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) != 1 && (labelSelector == "") { - cmd.Println(cmd.UsageString()) - return fmt.Errorf("stats requires pod name or label selector") - } - return nil - }, - RunE: func(c *cobra.Command, args []string) error { - var stats string - var err error - - if podName, podNamespace, err = getPodName(args[0]); err != nil { - return err - } - if statsType == "" || statsType == "server" { - stats, err = setupEnvoyServerStatsConfig(podName, podNamespace, outputFormat) - if err != nil { - return err - } - } else if statsType == "cluster" || statsType == "clusters" { - stats, err = setupEnvoyClusterStatsConfig(podName, podNamespace, outputFormat) - if err != nil { - return err - } - } else { - return fmt.Errorf("unknown stats type %s", statsType) - } - - switch outputFormat { - // convert the json output to yaml - case yamlOutput: - var out []byte - if out, err = yaml.JSONToYAML([]byte(stats)); err != nil { - return err - } - _, _ = fmt.Fprint(c.OutOrStdout(), string(out)) - default: - _, _ = fmt.Fprint(c.OutOrStdout(), stats) - } - - return nil - }, - ValidArgsFunction: validPodsNameArgs, - } - statsConfigCmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", summaryOutput, "Output format: one of json|yaml|prom") - statsConfigCmd.PersistentFlags().StringVarP(&statsType, "type", "t", "server", "Where to grab the stats: one of server|clusters") - - return statsConfigCmd -} - -func logCmd() *cobra.Command { - var podName, podNamespace string - var podNames []string - - logCmd := &cobra.Command{ - Use: "log [/][.]", - Short: "(experimental) Retrieves logging levels of the Envoy in the specified pod", - Long: "(experimental) Retrieve information about logging levels of the Envoy instance in the specified pod, and update optionally", - Example: ` # Retrieve information about logging levels for a given pod from Envoy. - istioctl proxy-config log - - # Update levels of the all loggers - istioctl proxy-config log --level none - - # Update levels of the specified loggers. - istioctl proxy-config log --level http:debug,redis:debug - - # Reset levels of all the loggers to default value (warning). - istioctl proxy-config log -r -`, - Aliases: []string{"o"}, - Args: func(cmd *cobra.Command, args []string) error { - if labelSelector == "" && len(args) < 1 { - cmd.Println(cmd.UsageString()) - return fmt.Errorf("log requires pod name or --selector") - } - if reset && loggerLevelString != "" { - cmd.Println(cmd.UsageString()) - return fmt.Errorf("--level cannot be combined with --reset") - } - return nil - }, - RunE: func(c *cobra.Command, args []string) error { - var err error - var loggerNames []string - if labelSelector != "" { - if podNames, podNamespace, err = getPodNameBySelector(labelSelector); err != nil { - return err - } - for _, pod := range podNames { - name, err = setupEnvoyLogConfig("", pod, podNamespace) - loggerNames = append(loggerNames, name) - } - if err != nil { - return err - } - if len(podNames) > 0 { - podName = podNames[0] - } - } else { - if podName, podNamespace, err = getPodName(args[0]); err != nil { - return err - } - name, err := setupEnvoyLogConfig("", podName, podNamespace) - loggerNames = append(loggerNames, name) - if err != nil { - return err - } - } - - destLoggerLevels := map[string]Level{} - if reset { - // reset logging level to `defaultOutputLevel`, and ignore the `level` option - levelString, _ := getLogLevelFromConfigMap() - level, ok := stringToLevel[levelString] - if ok { - destLoggerLevels[defaultLoggerName] = level - } else { - log.Warnf("unable to get logLevel from ConfigMap istio-sidecar-injector, using default value: %v", - levelToString[defaultOutputLevel]) - destLoggerLevels[defaultLoggerName] = defaultOutputLevel - } - } else if loggerLevelString != "" { - levels := strings.Split(loggerLevelString, ",") - for _, ol := range levels { - if !strings.Contains(ol, ":") && !strings.Contains(ol, "=") { - level, ok := stringToLevel[ol] - if ok { - destLoggerLevels = map[string]Level{ - defaultLoggerName: level, - } - } else { - return fmt.Errorf("unrecognized logging level: %v", ol) - } - } else { - loggerLevel := regexp.MustCompile(`[:=]`).Split(ol, 2) - for _, logName := range loggerNames { - if !strings.Contains(logName, loggerLevel[0]) { - return fmt.Errorf("unrecognized logger name: %v", loggerLevel[0]) - } - } - level, ok := stringToLevel[loggerLevel[1]] - if !ok { - return fmt.Errorf("unrecognized logging level: %v", loggerLevel[1]) - } - destLoggerLevels[loggerLevel[0]] = level - } - } - } - - var resp string - if len(destLoggerLevels) == 0 { - resp, err = setupEnvoyLogConfig("", podName, podNamespace) - } else { - if ll, ok := destLoggerLevels[defaultLoggerName]; ok { - // update levels of all loggers first - resp, err = setupEnvoyLogConfig(defaultLoggerName+"="+levelToString[ll], podName, podNamespace) - delete(destLoggerLevels, defaultLoggerName) - } - for lg, ll := range destLoggerLevels { - resp, err = setupEnvoyLogConfig(lg+"="+levelToString[ll], podName, podNamespace) - } - } - if err != nil { - return err - } - _, _ = fmt.Fprint(c.OutOrStdout(), resp) - return nil - }, - ValidArgsFunction: validPodsNameArgs, - } - - levelListString := fmt.Sprintf("[%s, %s, %s, %s, %s, %s, %s]", - levelToString[TraceLevel], - levelToString[DebugLevel], - levelToString[InfoLevel], - levelToString[WarningLevel], - levelToString[ErrorLevel], - levelToString[CriticalLevel], - levelToString[OffLevel]) - s := strings.Join(activeLoggers, ", ") - - logCmd.PersistentFlags().BoolVarP(&reset, "reset", "r", reset, "Reset levels to default value (warning).") - logCmd.PersistentFlags().StringVarP(&labelSelector, "selector", "l", "", "Label selector") - logCmd.PersistentFlags().StringVar(&loggerLevelString, "level", loggerLevelString, - fmt.Sprintf("Comma-separated minimum per-logger level of messages to output, in the form of"+ - " [:],[:],... where logger can be one of %s and level can be one of %s", - s, levelListString)) - - return logCmd -} - -func routeConfigCmd() *cobra.Command { - var podName, podNamespace string - - routeConfigCmd := &cobra.Command{ - Use: "route [/][.]", - Short: "Retrieves route configuration for the Envoy in the specified pod", - Long: `Retrieve information about route configuration for the Envoy instance in the specified pod.`, - Example: ` # Retrieve summary about route configuration for a given pod from Envoy. - istioctl proxy-config routes - - # Retrieve route summary for route 9080. - istioctl proxy-config route --name 9080 - - # Retrieve full route dump for route 9080 - istioctl proxy-config route --name 9080 -o json - - # Retrieve route summary without using Kubernetes API - ssh 'curl localhost:15000/config_dump' > envoy-config.json - istioctl proxy-config routes --file envoy-config.json -`, - Aliases: []string{"routes", "r"}, - Args: func(cmd *cobra.Command, args []string) error { - if (len(args) == 1) != (configDumpFile == "") { - cmd.Println(cmd.UsageString()) - return fmt.Errorf("route requires pod name or --file parameter") - } - return nil - }, - RunE: func(c *cobra.Command, args []string) error { - var configWriter *configdump.ConfigWriter - var err error - if len(args) == 1 { - if podName, podNamespace, err = getPodName(args[0]); err != nil { - return err - } - configWriter, err = setupPodConfigdumpWriter(podName, podNamespace, c.OutOrStdout()) - } else { - configWriter, err = setupFileConfigdumpWriter(configDumpFile, c.OutOrStdout()) - } - if err != nil { - return err - } - filter := configdump.RouteFilter{ - Name: routeName, - Verbose: verboseProxyConfig, - } - switch outputFormat { - case summaryOutput: - return configWriter.PrintRouteSummary(filter) - case jsonOutput, yamlOutput: - return configWriter.PrintRouteDump(filter, outputFormat) - default: - return fmt.Errorf("output format %q not supported", outputFormat) - } - }, - ValidArgsFunction: validPodsNameArgs, - } - - routeConfigCmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", summaryOutput, "Output format: one of json|yaml|short") - routeConfigCmd.PersistentFlags().StringVar(&routeName, "name", "", "Filter listeners by route name field") - routeConfigCmd.PersistentFlags().BoolVar(&verboseProxyConfig, "verbose", true, "Output more information") - routeConfigCmd.PersistentFlags().StringVarP(&configDumpFile, "file", "f", "", - "Envoy config dump JSON file") - - return routeConfigCmd -} - -func endpointConfigCmd() *cobra.Command { - var podName, podNamespace string - - endpointConfigCmd := &cobra.Command{ - Use: "endpoint [/][.]", - Short: "Retrieves endpoint configuration for the Envoy in the specified pod", - Long: `Retrieve information about endpoint configuration for the Envoy instance in the specified pod.`, - Example: ` # Retrieve full endpoint configuration for a given pod from Envoy. - istioctl proxy-config endpoint - - # Retrieve endpoint summary for endpoint with port 9080. - istioctl proxy-config endpoint --port 9080 - - # Retrieve full endpoint with a address (172.17.0.2). - istioctl proxy-config endpoint --address 172.17.0.2 -o json - - # Retrieve full endpoint with a cluster name (outbound|9411||zipkin.dubbo-system.svc.cluster.local). - istioctl proxy-config endpoint --cluster "outbound|9411||zipkin.dubbo-system.svc.cluster.local" -o json - # Retrieve full endpoint with the status (healthy). - istioctl proxy-config endpoint --status healthy -ojson - - # Retrieve endpoint summary without using Kubernetes API - ssh 'curl localhost:15000/clusters?format=json' > envoy-clusters.json - istioctl proxy-config endpoints --file envoy-clusters.json -`, - Aliases: []string{"endpoints", "ep"}, - Args: func(cmd *cobra.Command, args []string) error { - if (len(args) == 1) != (configDumpFile == "") { - cmd.Println(cmd.UsageString()) - return fmt.Errorf("endpoints requires pod name or --file parameter") - } - return nil - }, - RunE: func(c *cobra.Command, args []string) error { - var configWriter *clusters.ConfigWriter - var err error - if len(args) == 1 { - if podName, podNamespace, err = getPodName(args[0]); err != nil { - return err - } - configWriter, err = setupPodClustersWriter(podName, podNamespace, c.OutOrStdout()) - } else { - configWriter, err = setupFileClustersWriter(configDumpFile, c.OutOrStdout()) - } - if err != nil { - return err - } - - filter := clusters.EndpointFilter{ - Address: address, - Port: uint32(port), - Cluster: clusterName, - Status: status, - } - - switch outputFormat { - case summaryOutput: - return configWriter.PrintEndpointsSummary(filter) - case jsonOutput, yamlOutput: - return configWriter.PrintEndpoints(filter, outputFormat) - default: - return fmt.Errorf("output format %q not supported", outputFormat) - } - }, - ValidArgsFunction: validPodsNameArgs, - } - - endpointConfigCmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", summaryOutput, "Output format: one of json|yaml|short") - endpointConfigCmd.PersistentFlags().StringVar(&address, "address", "", "Filter endpoints by address field") - endpointConfigCmd.PersistentFlags().IntVar(&port, "port", 0, "Filter endpoints by Port field") - endpointConfigCmd.PersistentFlags().StringVar(&clusterName, "cluster", "", "Filter endpoints by cluster name field") - endpointConfigCmd.PersistentFlags().StringVar(&status, "status", "", "Filter endpoints by status field") - endpointConfigCmd.PersistentFlags().StringVarP(&configDumpFile, "file", "f", "", - "Envoy config dump JSON file") - - return endpointConfigCmd -} - -func bootstrapConfigCmd() *cobra.Command { - var podName, podNamespace string - - // Shadow outputVariable since this command uses a different default value - var outputFormat string - - bootstrapConfigCmd := &cobra.Command{ - Use: "bootstrap [/][.]", - Short: "Retrieves bootstrap configuration for the Envoy in the specified pod", - Long: `Retrieve information about bootstrap configuration for the Envoy instance in the specified pod.`, - Example: ` # Retrieve full bootstrap configuration for a given pod from Envoy. - istioctl proxy-config bootstrap - - # Retrieve full bootstrap without using Kubernetes API - ssh 'curl localhost:15000/config_dump' > envoy-config.json - istioctl proxy-config bootstrap --file envoy-config.json - - # Show a human-readable Istio and Envoy version summary - istioctl proxy-config bootstrap -o short -`, - Aliases: []string{"b"}, - Args: func(cmd *cobra.Command, args []string) error { - if (len(args) == 1) != (configDumpFile == "") { - cmd.Println(cmd.UsageString()) - return fmt.Errorf("bootstrap requires pod name or --file parameter") - } - return nil - }, - RunE: func(c *cobra.Command, args []string) error { - var configWriter *configdump.ConfigWriter - var err error - if len(args) == 1 { - if podName, podNamespace, err = getPodName(args[0]); err != nil { - return err - } - configWriter, err = setupPodConfigdumpWriter(podName, podNamespace, c.OutOrStdout()) - } else { - configWriter, err = setupFileConfigdumpWriter(configDumpFile, c.OutOrStdout()) - } - if err != nil { - return err - } - - switch outputFormat { - case summaryOutput: - return configWriter.PrintVersionSummary() - case jsonOutput, yamlOutput: - return configWriter.PrintBootstrapDump(outputFormat) - default: - return fmt.Errorf("output format %q not supported", outputFormat) - } - }, - ValidArgsFunction: validPodsNameArgs, - } - - bootstrapConfigCmd.Flags().StringVarP(&outputFormat, "output", "o", jsonOutput, "Output format: one of json|yaml|short") - bootstrapConfigCmd.PersistentFlags().StringVarP(&configDumpFile, "file", "f", "", - "Envoy config dump JSON file") - - return bootstrapConfigCmd -} - -func secretConfigCmd() *cobra.Command { - var podName, podNamespace string - - secretConfigCmd := &cobra.Command{ - Use: "secret [/][.]", - Short: "Retrieves secret configuration for the Envoy in the specified pod", - Long: `Retrieve information about secret configuration for the Envoy instance in the specified pod.`, - Example: ` # Retrieve full secret configuration for a given pod from Envoy. - istioctl proxy-config secret - - # Retrieve full bootstrap without using Kubernetes API - ssh 'curl localhost:15000/config_dump' > envoy-config.json - istioctl proxy-config secret --file envoy-config.json`, - Aliases: []string{"secrets", "s"}, - Args: func(cmd *cobra.Command, args []string) error { - if (len(args) == 1) != (configDumpFile == "") { - cmd.Println(cmd.UsageString()) - return fmt.Errorf("secret requires pod name or --file parameter") - } - return nil - }, - RunE: func(c *cobra.Command, args []string) error { - var configWriter *configdump.ConfigWriter - var err error - if len(args) == 1 { - if podName, podNamespace, err = getPodName(args[0]); err != nil { - return err - } - configWriter, err = setupPodConfigdumpWriter(podName, podNamespace, c.OutOrStdout()) - } else { - configWriter, err = setupFileConfigdumpWriter(configDumpFile, c.OutOrStdout()) - } - if err != nil { - return err - } - switch outputFormat { - case summaryOutput: - return configWriter.PrintSecretSummary() - case jsonOutput, yamlOutput: - return configWriter.PrintSecretDump(outputFormat) - default: - return fmt.Errorf("output format %q not supported", outputFormat) - } - }, - ValidArgsFunction: validPodsNameArgs, - } - - secretConfigCmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", summaryOutput, "Output format: one of json|yaml|short") - secretConfigCmd.PersistentFlags().StringVarP(&configDumpFile, "file", "f", "", - "Envoy config dump JSON file") - secretConfigCmd.Long += "\n\n" + ExperimentalMsg - return secretConfigCmd -} - -func rootCACompareConfigCmd() *cobra.Command { - var podName1, podName2, podNamespace1, podNamespace2 string - - rootCACompareConfigCmd := &cobra.Command{ - Use: "rootca-compare [pod/][.] [pod/][.]", - Short: "Compare ROOTCA values for the two given pods", - Long: `Compare ROOTCA values for given 2 pods to check the connectivity between them.`, - Example: ` # Compare ROOTCA values for given 2 pods to check the connectivity between them. - istioctl proxy-config rootca-compare `, - Aliases: []string{"rc"}, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) != 2 { - cmd.Println(cmd.UsageString()) - return fmt.Errorf("rootca-compare requires 2 pods as an argument") - } - return nil - }, - RunE: func(c *cobra.Command, args []string) error { - var configWriter1, configWriter2 *configdump.ConfigWriter - var err error - if len(args) == 2 { - if podName1, podNamespace1, err = getPodName(args[0]); err != nil { - return err - } - configWriter1, err = setupPodConfigdumpWriter(podName1, podNamespace1, c.OutOrStdout()) - if err != nil { - return err - } - - if podName2, podNamespace2, err = getPodName(args[1]); err != nil { - return err - } - configWriter2, err = setupPodConfigdumpWriter(podName2, podNamespace2, c.OutOrStdout()) - if err != nil { - return err - } - } else { - c.Println(c.UsageString()) - return fmt.Errorf("rootca-compare requires 2 pods as an argument") - } - - rootCA1, err1 := configWriter1.PrintPodRootCAFromDynamicSecretDump() - if err1 != nil { - return fmt.Errorf("error when retrieving ROOTCA of [%s]: %v", args[0], err1) - } - rootCA2, err2 := configWriter2.PrintPodRootCAFromDynamicSecretDump() - if err2 != nil { - return fmt.Errorf("error when retrieving ROOTCA of [%s]: %v", args[1], err2) - } - - var returnErr error - if rootCA1 == rootCA2 { - report := fmt.Sprintf("Both [%s.%s] and [%s.%s] have the identical ROOTCA, theoretically the connectivity between them is available", - podName1, podNamespace1, podName2, podNamespace2) - c.Println(report) - returnErr = nil - } else { - report := fmt.Sprintf("Both [%s.%s] and [%s.%s] have the non identical ROOTCA, theoretically the connectivity between them is unavailable", - podName1, podNamespace1, podName2, podNamespace2) - returnErr = fmt.Errorf(report) - } - return returnErr - }, - ValidArgsFunction: validPodsNameArgs, - } - - rootCACompareConfigCmd.Long += "\n\n" + ExperimentalMsg - return rootCACompareConfigCmd -} - -func proxyConfig() *cobra.Command { - configCmd := &cobra.Command{ - Use: "proxy-config", - Short: "Retrieve information about proxy configuration from Envoy [kube only]", - Long: `A group of commands used to retrieve information about proxy configuration from the Envoy config dump`, - Example: ` # Retrieve information about proxy configuration from an Envoy instance. - istioctl proxy-config `, - Aliases: []string{"pc"}, - } - - configCmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", summaryOutput, "Output format: one of json|yaml|short") - - configCmd.AddCommand(clusterConfigCmd()) - configCmd.AddCommand(allConfigCmd()) - configCmd.AddCommand(listenerConfigCmd()) - configCmd.AddCommand(logCmd()) - configCmd.AddCommand(routeConfigCmd()) - configCmd.AddCommand(bootstrapConfigCmd()) - configCmd.AddCommand(endpointConfigCmd()) - configCmd.AddCommand(secretConfigCmd()) - configCmd.AddCommand(rootCACompareConfigCmd()) - - return configCmd -} - -func getPodName(podflag string) (string, string, error) { - kubeClient, err := kubeClient(kubeconfig, configContext) - if err != nil { - return "", "", fmt.Errorf("failed to create k8s client: %w", err) - } - var podName, ns string - podName, ns, err = handlers.InferPodInfoFromTypedResource(podflag, - handlers.HandleNamespace(namespace, defaultNamespace), - kubeClient.UtilFactory()) - if err != nil { - return "", "", err - } - return podName, ns, nil -} - -func getPodNameBySelector(labelSelector string) ([]string, string, error) { - var ( - podNames []string - ns string - ) - client, err := kubeClient(kubeconfig, configContext) - if err != nil { - return nil, "", fmt.Errorf("failed to create k8s client: %w", err) - } - pl, err := client.PodsForSelector(context.TODO(), handlers.HandleNamespace(namespace, defaultNamespace), labelSelector) - if err != nil { - return nil, "", fmt.Errorf("not able to locate pod with selector %s: %v", labelSelector, err) - } - if len(pl.Items) < 1 { - return nil, "", errors.New("no pods found") - } - for _, pod := range pl.Items { - podNames = append(podNames, pod.Name) - } - ns = pl.Items[0].Namespace - return podNames, ns, nil -} diff --git a/istioctl/cmd/proxyconfig_test.go b/istioctl/cmd/proxyconfig_test.go deleted file mode 100644 index 704cd2b48..000000000 --- a/istioctl/cmd/proxyconfig_test.go +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "bytes" - "fmt" - "strings" - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/test/util" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -type execTestCase struct { - execClientConfig map[string][]byte - args []string - - // Typically use one of the three - expectedOutput string // Expected constant output - expectedString string // String output is expected to contain - goldenFilename string // Expected output stored in golden file - - wantException bool -} - -func TestProxyConfig(t *testing.T) { - loggingConfig := map[string][]byte{ - "details-v1-5b7f94f9bc-wp5tb": util.ReadFile(t, "../pkg/writer/envoy/logging/testdata/logging.txt"), - "httpbin-794b576b6c-qx6pf": []byte("{}"), - } - cases := []execTestCase{ - { - args: strings.Split("proxy-config", " "), - expectedString: "A group of commands used to retrieve information about", - }, - { // short name 'pc' - args: strings.Split("pc", " "), - expectedString: "A group of commands used to retrieve information about", - }, - { // clusters invalid - args: strings.Split("proxy-config clusters invalid", " "), - expectedString: "unable to retrieve Pod: pods \"invalid\" not found", - wantException: true, // "istioctl proxy-config clusters invalid" should fail - }, - { // listeners invalid - args: strings.Split("proxy-config listeners invalid", " "), - expectedString: "unable to retrieve Pod: pods \"invalid\" not found", - wantException: true, // "istioctl proxy-config listeners invalid" should fail - }, - { // logging empty - args: strings.Split("proxy-config log", " "), - expectedString: "Error: log requires pod name or --selector", - wantException: true, // "istioctl proxy-config logging empty" should fail - }, - { // logging invalid - args: strings.Split("proxy-config log invalid", " "), - expectedString: "unable to retrieve Pod: pods \"invalid\" not found", - wantException: true, // "istioctl proxy-config logging invalid" should fail - }, - { // logging level invalid - execClientConfig: loggingConfig, - args: strings.Split("proxy-config log details-v1-5b7f94f9bc-wp5tb --level xxx", " "), - expectedString: "unrecognized logging level: xxx", - wantException: true, - }, - { // logger name invalid - execClientConfig: loggingConfig, - args: strings.Split("proxy-config log details-v1-5b7f94f9bc-wp5tb --level xxx:debug", " "), - expectedString: "unrecognized logger name: xxx", - wantException: true, - }, - { // logger name valid, but logging level invalid - execClientConfig: loggingConfig, - args: strings.Split("proxy-config log details-v1-5b7f94f9bc-wp5tb --level http:yyy", " "), - expectedString: "unrecognized logging level: yyy", - wantException: true, - }, - { // both logger name and logging level invalid - execClientConfig: loggingConfig, - args: strings.Split("proxy-config log details-v1-5b7f94f9bc-wp5tb --level xxx:yyy", " "), - expectedString: "unrecognized logger name: xxx", - wantException: true, - }, - { // routes invalid - args: strings.Split("proxy-config routes invalid", " "), - expectedString: "unable to retrieve Pod: pods \"invalid\" not found", - wantException: true, // "istioctl proxy-config routes invalid" should fail - }, - { // bootstrap invalid - args: strings.Split("proxy-config bootstrap invalid", " "), - expectedString: "unable to retrieve Pod: pods \"invalid\" not found", - wantException: true, // "istioctl proxy-config bootstrap invalid" should fail - }, - { // secret invalid - args: strings.Split("proxy-config secret invalid", " "), - expectedString: "unable to retrieve Pod: pods \"invalid\" not found", - wantException: true, // "istioctl proxy-config secret invalid" should fail - }, - { // endpoint invalid - args: strings.Split("proxy-config endpoint invalid", " "), - expectedString: "unable to retrieve Pod: pods \"invalid\" not found", - wantException: true, // "istioctl proxy-config endpoint invalid" should fail - }, - { // supplying nonexistent deployment name should result in error - args: strings.Split("proxy-config clusters deployment/random-gibberish", " "), - expectedString: `"deployment/random-gibberish" does not refer to a pod`, - wantException: true, - }, - { // supplying nonexistent deployment name in nonexistent namespace - args: strings.Split("proxy-config endpoint deployment/random-gibberish.bogus", " "), - expectedString: `"deployment/random-gibberish" does not refer to a pod`, - wantException: true, - }, - { // supplying type that doesn't select pods should fail - args: strings.Split("proxy-config listeners serviceaccount/sleep", " "), - expectedString: `"serviceaccount/sleep" does not refer to a pod`, - wantException: true, - }, - { // supplying valid pod name retrieves Envoy config (fails because we don't check in Envoy config unit tests) - execClientConfig: loggingConfig, - args: strings.Split("pc clusters httpbin-794b576b6c-qx6pf", " "), - expectedString: `config dump has no configuration type`, - wantException: true, - }, - { // supplying valid pod name retrieves Envoy config (fails because we don't check in Envoy config unit tests) - execClientConfig: loggingConfig, - args: strings.Split("pc bootstrap httpbin-794b576b6c-qx6pf", " "), - expectedString: `config dump has no configuration type`, - wantException: true, - }, - { // supplying valid pod name retrieves Envoy config (fails because we don't check in Envoy config unit tests) - execClientConfig: loggingConfig, - args: strings.Split("pc endpoint httpbin-794b576b6c-qx6pf", " "), - expectedString: `ENDPOINT STATUS OUTLIER CHECK CLUSTER`, - wantException: false, - }, - { // supplying valid pod name retrieves Envoy config (fails because we don't check in Envoy config unit tests) - execClientConfig: loggingConfig, - args: strings.Split("pc listener httpbin-794b576b6c-qx6pf", " "), - expectedString: `config dump has no configuration type`, - wantException: true, - }, - { // supplying valid pod name retrieves Envoy config (fails because we don't check in Envoy config unit tests) - execClientConfig: loggingConfig, - args: strings.Split("pc route httpbin-794b576b6c-qx6pf", " "), - expectedString: `config dump has no configuration type`, - wantException: true, - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("case %d %s", i, strings.Join(c.args, " ")), func(t *testing.T) { - verifyExecTestOutput(t, c) - }) - } -} - -func verifyExecTestOutput(t *testing.T, c execTestCase) { - t.Helper() - - // Override the exec client factory used by proxyconfig.go and proxystatus.go - kubeClientWithRevision = mockClientExecFactoryGenerator(c.execClientConfig) - kubeClient = mockEnvoyClientFactoryGenerator(c.execClientConfig) - - var out bytes.Buffer - rootCmd := GetRootCmd(c.args) - rootCmd.SetOut(&out) - rootCmd.SetErr(&out) - - fErr := rootCmd.Execute() - output := out.String() - - if c.expectedOutput != "" && c.expectedOutput != output { - t.Fatalf("Unexpected output for 'istioctl %s'\n got: %q\nwant: %q", strings.Join(c.args, " "), output, c.expectedOutput) - } - - if c.expectedString != "" && !strings.Contains(output, c.expectedString) { - t.Fatalf("Output didn't match for 'istioctl %s'\n got %v\nwant: %v", strings.Join(c.args, " "), output, c.expectedString) - } - - if c.goldenFilename != "" { - util.CompareContent(t, []byte(output), c.goldenFilename) - } - - if c.wantException { - if fErr == nil { - t.Fatalf("Wanted an exception for 'istioctl %s', didn't get one, output was %q", - strings.Join(c.args, " "), output) - } - } else { - if fErr != nil { - t.Fatalf("Unwanted exception for 'istioctl %s': %v", strings.Join(c.args, " "), fErr) - } - } -} - -// mockClientExecFactoryGenerator generates a function with the same signature as -// kubernetes.NewExecClient() that returns a mock client. -// nolint: lll -func mockClientExecFactoryGenerator(testResults map[string][]byte) func(kubeconfig, configContext string, _ string) (kube.ExtendedClient, error) { - outFactory := func(_, _ string, _ string) (kube.ExtendedClient, error) { - return kube.MockClient{ - Results: testResults, - }, nil - } - - return outFactory -} - -func mockEnvoyClientFactoryGenerator(testResults map[string][]byte) func(kubeconfig, configContext string) (kube.ExtendedClient, error) { - outFactory := func(_, _ string) (kube.ExtendedClient, error) { - return kube.MockClient{ - Results: testResults, - }, nil - } - - return outFactory -} diff --git a/istioctl/cmd/proxystatus.go b/istioctl/cmd/proxystatus.go deleted file mode 100644 index bd2be4896..000000000 --- a/istioctl/cmd/proxystatus.go +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "fmt" - "io" - "os" -) - -import ( - xdsapi "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "github.com/spf13/cobra" - "istio.io/pkg/log" - "k8s.io/client-go/rest" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/clioptions" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/multixds" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/handlers" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/writer/compare" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/writer/pilot" - pilotxds "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -func statusCommand() *cobra.Command { - var opts clioptions.ControlPlaneOptions - - statusCmd := &cobra.Command{ - Use: "proxy-status [/][.]", - Short: "Retrieves the synchronization status of each Envoy in the mesh [kube only]", - Long: ` -Retrieves last sent and last acknowledged xDS sync from Istiod to each Envoy in the mesh - -`, - Example: ` # Retrieve sync status for all Envoys in a mesh - istioctl proxy-status - - # Retrieve sync diff for a single Envoy and Istiod - istioctl proxy-status istio-egressgateway-59585c5b9c-ndc59.dubbo-system - - # Retrieve sync diff between Istiod and one pod under a deployment - istioctl proxy-status deployment/productpage-v1 - - # Write proxy config-dump to file, and compare to Istio control plane - kubectl port-forward -n dubbo-system istio-egressgateway-59585c5b9c-ndc59 15000 & - curl localhost:15000/config_dump > cd.json - istioctl proxy-status istio-egressgateway-59585c5b9c-ndc59.dubbo-system --file cd.json -`, - Aliases: []string{"ps"}, - Args: func(cmd *cobra.Command, args []string) error { - if (len(args) == 0) && (configDumpFile != "") { - cmd.Println(cmd.UsageString()) - return fmt.Errorf("--file can only be used when pod-name is specified") - } - return nil - }, - RunE: func(c *cobra.Command, args []string) error { - kubeClient, err := kubeClientWithRevision(kubeconfig, configContext, opts.Revision) - if err != nil { - return err - } - if len(args) > 0 { - podName, ns, err := handlers.InferPodInfoFromTypedResource(args[0], - handlers.HandleNamespace(namespace, defaultNamespace), - kubeClient.UtilFactory()) - if err != nil { - return err - } - var envoyDump []byte - if configDumpFile != "" { - envoyDump, err = readConfigFile(configDumpFile) - } else { - path := "config_dump" - envoyDump, err = kubeClient.EnvoyDo(context.TODO(), podName, ns, "GET", path) - } - if err != nil { - return err - } - - path := fmt.Sprintf("/debug/config_dump?proxyID=%s.%s", podName, ns) - istiodDumps, err := kubeClient.AllDiscoveryDo(context.TODO(), istioNamespace, path) - if err != nil { - return err - } - c, err := compare.NewComparator(c.OutOrStdout(), istiodDumps, envoyDump) - if err != nil { - return err - } - return c.Diff() - } - statuses, err := kubeClient.AllDiscoveryDo(context.TODO(), istioNamespace, "/debug/syncz") - if err != nil { - return err - } - sw := pilot.StatusWriter{Writer: c.OutOrStdout()} - return sw.PrintAll(statuses) - }, - } - - opts.AttachControlPlaneFlags(statusCmd) - statusCmd.PersistentFlags().StringVarP(&configDumpFile, "file", "f", "", - "Envoy config dump JSON file") - - return statusCmd -} - -func readConfigFile(filename string) ([]byte, error) { - file := os.Stdin - if filename != "-" { - var err error - file, err = os.Open(filename) - if err != nil { - return nil, err - } - } - defer func() { - if err := file.Close(); err != nil { - log.Errorf("failed to close %s: %s", filename, err) - } - }() - data, err := io.ReadAll(file) - if err != nil { - return nil, err - } - return data, nil -} - -func newKubeClientWithRevision(kubeconfig, configContext string, revision string) (kube.ExtendedClient, error) { - rc, err := kube.DefaultRestConfig(kubeconfig, configContext, func(config *rest.Config) { - // We are running a one-off command locally, so we don't need to worry too much about rate limitting - // Bumping this up greatly decreases install time - config.QPS = 50 - config.Burst = 100 - }) - if err != nil { - return nil, err - } - return kube.NewExtendedClient(kube.NewClientConfigForRestConfig(rc), revision) -} - -func newKubeClient(kubeconfig, configContext string) (kube.ExtendedClient, error) { - return newKubeClientWithRevision(kubeconfig, configContext, "") -} - -func xdsStatusCommand() *cobra.Command { - var opts clioptions.ControlPlaneOptions - var centralOpts clioptions.CentralControlPlaneOptions - - statusCmd := &cobra.Command{ - Use: "proxy-status [/][.]", - Short: "Retrieves the synchronization status of each Envoy in the mesh", - Long: ` -Retrieves last sent and last acknowledged xDS sync from Istiod to each Envoy in the mesh -`, - Example: ` # Retrieve sync status for all Envoys in a mesh - istioctl x proxy-status - - # Retrieve sync diff for a single Envoy and Istiod - istioctl x proxy-status istio-egressgateway-59585c5b9c-ndc59.dubbo-system - - # SECURITY OPTIONS - - # Retrieve proxy status information directly from the control plane, using token security - # (This is the usual way to get the proxy-status with an out-of-cluster control plane.) - istioctl x ps --xds-address istio.cloudprovider.example.com:15012 - - # Retrieve proxy status information via Kubernetes config, using token security - # (This is the usual way to get the proxy-status with an in-cluster control plane.) - istioctl x proxy-status - - # Retrieve proxy status information directly from the control plane, using RSA certificate security - # (Certificates must be obtained before this step. The --cert-dir flag lets istioctl bypass the Kubernetes API server.) - istioctl x ps --xds-address istio.example.com:15012 --cert-dir ~/.istio-certs - - # Retrieve proxy status information via XDS from specific control plane in multi-control plane in-cluster configuration - # (Select a specific control plane in an in-cluster canary Istio configuration.) - istioctl x ps --xds-label istio.io/rev=default -`, - Aliases: []string{"ps"}, - RunE: func(c *cobra.Command, args []string) error { - kubeClient, err := kubeClientWithRevision(kubeconfig, configContext, opts.Revision) - if err != nil { - return err - } - - if len(args) > 0 { - podName, ns, err := handlers.InferPodInfoFromTypedResource(args[0], - handlers.HandleNamespace(namespace, defaultNamespace), - kubeClient.UtilFactory()) - if err != nil { - return err - } - var envoyDump []byte - if configDumpFile != "" { - envoyDump, err = readConfigFile(configDumpFile) - } else { - path := "config_dump" - envoyDump, err = kubeClient.EnvoyDo(context.TODO(), podName, ns, "GET", path) - } - if err != nil { - return fmt.Errorf("could not contact sidecar: %w", err) - } - - xdsRequest := xdsapi.DiscoveryRequest{ - ResourceNames: []string{fmt.Sprintf("%s.%s", podName, ns)}, - TypeUrl: pilotxds.TypeDebugConfigDump, - } - xdsResponses, err := multixds.FirstRequestAndProcessXds(&xdsRequest, centralOpts, istioNamespace, "", "", kubeClient) - if err != nil { - return err - } - c, err := compare.NewXdsComparator(c.OutOrStdout(), xdsResponses, envoyDump) - if err != nil { - return err - } - return c.Diff() - } - - xdsRequest := xdsapi.DiscoveryRequest{ - TypeUrl: pilotxds.TypeDebugSyncronization, - } - xdsResponses, err := multixds.AllRequestAndProcessXds(&xdsRequest, centralOpts, istioNamespace, "", "", kubeClient) - if err != nil { - return err - } - sw := pilot.XdsStatusWriter{Writer: c.OutOrStdout()} - return sw.PrintAll(xdsResponses) - }, - } - - opts.AttachControlPlaneFlags(statusCmd) - centralOpts.AttachControlPlaneFlags(statusCmd) - statusCmd.PersistentFlags().StringVarP(&configDumpFile, "file", "f", "", - "Envoy config dump JSON file") - - return statusCmd -} diff --git a/istioctl/cmd/proxystatus_test.go b/istioctl/cmd/proxystatus_test.go deleted file mode 100644 index 7cd651a4a..000000000 --- a/istioctl/cmd/proxystatus_test.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "fmt" - "strings" - "testing" -) - -func TestProxyStatus(t *testing.T) { - cases := []execTestCase{ - { // case 0 - args: strings.Split("proxy-status", " "), - expectedString: "NAME CLUSTER CDS LDS EDS RDS ECDS ISTIOD", - }, - { // case 1 short name "ps" - args: strings.Split("ps", " "), - expectedString: "NAME CLUSTER CDS LDS EDS RDS ECDS ISTIOD", - }, - { // case 2: supplying nonexistent pod name should result in error with flag - args: strings.Split("proxy-status deployment/random-gibberish", " "), - wantException: true, - }, - { // case 3: supplying nonexistent deployment name - args: strings.Split("proxy-status deployment/random-gibberish.default", " "), - wantException: true, - }, - { // case 4: supplying nonexistent deployment name in nonexistent namespace - args: strings.Split("proxy-status deployment/random-gibberish.bogus", " "), - wantException: true, - }, - { // case 5: supplying nonexistent pod name should result in error - args: strings.Split("proxy-status random-gibberish-podname-61789237418234", " "), - wantException: true, - }, - { // case 6: new --revision argument - args: strings.Split("proxy-status --revision canary", " "), - expectedString: "NAME CLUSTER CDS LDS EDS RDS ECDS ISTIOD", - }, - { // case 7: supplying type that doesn't select pods should fail - args: strings.Split("proxy-status serviceaccount/sleep", " "), - wantException: true, - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("case %d %s", i, strings.Join(c.args, " ")), func(t *testing.T) { - verifyExecTestOutput(t, c) - }) - } -} diff --git a/istioctl/cmd/remove-from-mesh.go b/istioctl/cmd/remove-from-mesh.go deleted file mode 100644 index 2bd153bda..000000000 --- a/istioctl/cmd/remove-from-mesh.go +++ /dev/null @@ -1,321 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "encoding/json" - "fmt" - "io" - "strings" -) - -import ( - "github.com/hashicorp/go-multierror" - "github.com/spf13/cobra" - "istio.io/api/annotation" - "istio.io/pkg/log" - appsv1 "k8s.io/api/apps/v1" - apierror "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/kubernetes" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/handlers" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/kube/inject" -) - -func removeFromMeshCmd() *cobra.Command { - removeFromMeshCmd := &cobra.Command{ - Use: "remove-from-mesh", - Aliases: []string{"rm"}, - Short: "Remove workloads from Istio service mesh", - Long: `'istioctl experimental remove-from-mesh' restarts pods without an Istio sidecar or removes external service access configuration. -Use 'remove-from-mesh' to quickly test uninjected behavior as part of compatibility troubleshooting. -The 'add-to-mesh' command can be used to add or restore the sidecar.`, - Example: ` # Restart all productpage pods without an Istio sidecar - istioctl experimental remove-from-mesh service productpage - - # Restart all details-v1 pods without an Istio sidecar - istioctl x rm service details-v1 - - # Restart all ratings-v1 pods without an Istio sidecar - istioctl x rm deploy ratings-v1`, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) != 0 { - return fmt.Errorf("unknown resource type %q", args[0]) - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - cmd.HelpFunc()(cmd, args) - return nil - }, - } - removeFromMeshCmd.AddCommand(svcUnMeshifyCmd()) - removeFromMeshCmd.AddCommand(deploymentUnMeshifyCmd()) - removeFromMeshCmd.AddCommand(externalSvcUnMeshifyCmd()) - removeFromMeshCmd.Long += "\n\n" + ExperimentalMsg - return removeFromMeshCmd -} - -func deploymentUnMeshifyCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "deployment ", - Aliases: []string{"deploy", "dep"}, - Short: "Remove deployment from Istio service mesh", - Long: `'istioctl experimental remove-from-mesh deployment' restarts pods with the Istio sidecar un-injected. -'remove-from-mesh' is a compatibility troubleshooting tool.`, - Example: ` # Restart all productpage-v1 pods without an Istio sidecar - istioctl experimental remove-from-mesh deployment productpage-v1 - - # Restart all details-v1 pods without an Istio sidecar - istioctl x remove-from-mesh deploy details-v1 - - # Restart all ratings-v1 pods without an Istio sidecar - istioctl x rm dep ratings-v1`, - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) != 1 { - return fmt.Errorf("expecting deployment name") - } - ns := handlers.HandleNamespace(namespace, defaultNamespace) - if util.IsSystemNamespace(resource.Namespace(ns)) || ns == istioNamespace { - return fmt.Errorf("namespace %s is a system namespace and has no Istio sidecar injected", ns) - } - client, err := interfaceFactory(kubeconfig) - if err != nil { - return err - } - dep, err := client.AppsV1().Deployments(ns).Get(context.TODO(), args[0], metav1.GetOptions{}) - if err != nil { - if apierror.IsNotFound(err) { - return fmt.Errorf("deployment %q does not exist", args[0]) - } - return err - } - writer := cmd.OutOrStdout() - deps := []appsv1.Deployment{} - deps = append(deps, *dep) - return unInjectSideCarFromDeployment(client, deps, args[0], ns, writer) - }, - } - cmd.Long += "\n\n" + ExperimentalMsg - return cmd -} - -func svcUnMeshifyCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "service ", - Aliases: []string{"svc"}, - Short: "Remove Service from Istio service mesh", - Long: `'istioctl experimental remove-from-mesh service' restarts pods with the Istio sidecar un-injected. -'remove-from-mesh' is a compatibility troubleshooting tool.`, - Example: ` # Restart all productpage pods without an Istio sidecar - istioctl experimental remove-from-mesh service productpage - - # Restart all details-v1 pods without an Istio sidecar - istioctl x remove-from-mesh svc details-v1 - - # Restart all ratings-v1 pods without an Istio sidecar - istioctl x rm svc ratings-v1`, - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) != 1 { - return fmt.Errorf("expecting service name") - } - ns := handlers.HandleNamespace(namespace, defaultNamespace) - if util.IsSystemNamespace(resource.Namespace(ns)) || ns == istioNamespace { - return fmt.Errorf("namespace %s is a system namespace and has no Istio sidecar injected", ns) - } - client, err := interfaceFactory(kubeconfig) - if err != nil { - return err - } - _, err = client.CoreV1().Services(ns).Get(context.TODO(), args[0], metav1.GetOptions{}) - if err != nil { - if apierror.IsNotFound(err) { - return fmt.Errorf("service %q does not exist, skip", args[0]) - } - return err - } - matchingDeployments, err := findDeploymentsForSvc(client, ns, args[0]) - if err != nil { - return err - } - writer := cmd.OutOrStdout() - if len(matchingDeployments) == 0 { - fmt.Fprintf(writer, "No deployments found for service %s.%s\n", args[0], ns) - return nil - } - return unInjectSideCarFromDeployment(client, matchingDeployments, args[0], ns, writer) - }, - } - cmd.Long += "\n\n" + ExperimentalMsg - return cmd -} - -func externalSvcUnMeshifyCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "external-service ", - Aliases: []string{"es"}, - Short: "Remove Service Entry and Kubernetes Service for the external service from Istio service mesh", - Long: `'istioctl experimental remove-from-mesh external-service' removes the ServiceEntry and -the Kubernetes Service for the specified external service (e.g. services running on a VM) from Istio service mesh. -The typical usage scenario is Mesh Expansion on VMs.`, - Example: ` # Remove "vmhttp" service entry rules - istioctl experimental remove-from-mesh external-service vmhttp - - # Remove "vmhttp" service entry rules - istioctl x remove-from-mesh es vmhttp - - # Remove "vmhttp" service entry rules - istioctl x rm es vmhttp`, - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) != 1 { - return fmt.Errorf("expecting external service name") - } - client, err := interfaceFactory(kubeconfig) - if err != nil { - return err - } - seClient, err := crdFactory(kubeconfig) - if err != nil { - return err - } - writer := cmd.OutOrStdout() - ns := handlers.HandleNamespace(namespace, defaultNamespace) - _, err = client.CoreV1().Services(ns).Get(context.TODO(), args[0], metav1.GetOptions{}) - if err == nil { - return removeServiceOnVMFromMesh(seClient, client, ns, args[0], writer) - } - if apierror.IsNotFound(err) { - return fmt.Errorf("service %q does not exist, skip", args[0]) - } - return err - }, - } - cmd.Long += "\n\n" + ExperimentalMsg - return cmd -} - -func unInjectSideCarFromDeployment(client kubernetes.Interface, deps []appsv1.Deployment, - svcName, svcNamespace string, writer io.Writer) error { - var errs error - name := strings.Join([]string{svcName, svcNamespace}, ".") - for _, dep := range deps { - log.Debugf("updating deployment %s.%s with Istio sidecar un-injected", - dep.Name, dep.Namespace) - res := dep.DeepCopy() - depName := strings.Join([]string{dep.Name, dep.Namespace}, ".") - sidecarInjected := false - podSpec := dep.Spec.Template.Spec.DeepCopy() - for _, c := range podSpec.Containers { - if c.Name == proxyContainerName { - sidecarInjected = true - break - } - } - if !sidecarInjected { - // The sidecar wasn't explicitly injected. (Unless there is annotation it may have been auto injected) - if val := dep.Spec.Template.Annotations[annotation.SidecarInject.Name]; strings.EqualFold(val, "false") { - fmt.Fprintf(writer, "deployment %q has no Istio sidecar injected. Skipping.\n", depName) - continue - } - } - - var appProbe inject.KubeAppProbers - appProbeStr := retrieveAppProbe(podSpec.Containers) - if appProbeStr != "" { - err := json.Unmarshal([]byte(appProbeStr), &appProbe) - errs = multierror.Append(errs, err) - } - if appProbe != nil { - podSpec.Containers = restoreAppProbes(podSpec.Containers, appProbe) - } - - podSpec.InitContainers = removeInjectedContainers(podSpec.InitContainers, initContainerName) - podSpec.InitContainers = removeInjectedContainers(podSpec.InitContainers, initValidationContainerName) - podSpec.InitContainers = removeInjectedContainers(podSpec.InitContainers, enableCoreDumpContainerName) - podSpec.Containers = removeInjectedContainers(podSpec.Containers, proxyContainerName) - podSpec.Volumes = removeInjectedVolumes(podSpec.Volumes, certVolumeName) - podSpec.Volumes = removeInjectedVolumes(podSpec.Volumes, dataVolumeName) - podSpec.Volumes = removeInjectedVolumes(podSpec.Volumes, envoyVolumeName) - podSpec.Volumes = removeInjectedVolumes(podSpec.Volumes, jwtTokenVolumeName) - podSpec.Volumes = removeInjectedVolumes(podSpec.Volumes, pilotCertVolumeName) - podSpec.Volumes = removeInjectedVolumes(podSpec.Volumes, podInfoVolumeName) - removeDNSConfig(podSpec.DNSConfig) - res.Spec.Template.Spec = *podSpec - // If we are in an auto-inject namespace, removing the sidecar isn't enough, we - // must prevent injection - if res.Spec.Template.Annotations == nil { - res.Spec.Template.Annotations = make(map[string]string) - } - res.Spec.Template.Annotations[annotation.SidecarInject.Name] = "false" - if _, err := client.AppsV1().Deployments(svcNamespace).Update(context.TODO(), res, metav1.UpdateOptions{}); err != nil { - errs = multierror.Append(errs, fmt.Errorf("failed to update deployment %q for service %q due to %v", depName, name, err)) - continue - } - d := &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: dep.Name, - Namespace: dep.Namespace, - UID: dep.UID, - OwnerReferences: dep.OwnerReferences, - }, - } - if _, err := client.AppsV1().Deployments(svcNamespace).UpdateStatus(context.TODO(), d, metav1.UpdateOptions{}); err != nil { - errs = multierror.Append(errs, fmt.Errorf("failed to update deployment status %q for service %q due to %v", depName, name, err)) - continue - } - fmt.Fprintf(writer, "deployment %q updated successfully with Istio sidecar un-injected.\n", depName) - } - return errs -} - -// removeServiceOnVMFromMesh removes the Service Entry and K8s service for the specified external service -func removeServiceOnVMFromMesh(dynamicClient dynamic.Interface, client kubernetes.Interface, ns string, - svcName string, writer io.Writer) error { - // Pre-check Kubernetes service and service entry does not exist. - _, err := client.CoreV1().Services(ns).Get(context.TODO(), svcName, metav1.GetOptions{}) - if err != nil { - return fmt.Errorf("service %q does not exist, skip", svcName) - } - serviceEntryGVR := schema.GroupVersionResource{ - Group: "networking.istio.io", - Version: collections.IstioNetworkingV1Alpha3Serviceentries.Resource().Version(), - Resource: "serviceentries", - } - _, err = dynamicClient.Resource(serviceEntryGVR).Namespace(ns).Get(context.TODO(), resourceName(svcName), metav1.GetOptions{}) - if err != nil { - return fmt.Errorf("service entry %q does not exist, skip", resourceName(svcName)) - } - err = client.CoreV1().Services(ns).Delete(context.TODO(), svcName, metav1.DeleteOptions{}) - if err != nil { - return fmt.Errorf("failed to delete Kubernetes service %q due to %v", svcName, err) - } - name := strings.Join([]string{svcName, ns}, ".") - fmt.Fprintf(writer, "Kubernetes Service %q has been deleted for external service %q\n", name, svcName) - err = dynamicClient.Resource(serviceEntryGVR).Namespace(ns).Delete(context.TODO(), resourceName(svcName), metav1.DeleteOptions{}) - if err != nil { - return fmt.Errorf("failed to delete service entry %q due to %v", resourceName(svcName), err) - } - fmt.Fprintf(writer, "Service Entry %q has been deleted for external service %q\n", resourceName(svcName), svcName) - return nil -} diff --git a/istioctl/cmd/remove-from-mesh_test.go b/istioctl/cmd/remove-from-mesh_test.go deleted file mode 100644 index c77dbc426..000000000 --- a/istioctl/cmd/remove-from-mesh_test.go +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "fmt" - "strings" - "testing" -) - -import ( - appsv1 "k8s.io/api/apps/v1" - coreV1 "k8s.io/api/core/v1" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -var ( - cannedK8sConfig = []runtime.Object{ - &coreV1.ConfigMapList{Items: []coreV1.ConfigMap{}}, - - &appsv1.DeploymentList{Items: []appsv1.Deployment{ - { - ObjectMeta: metaV1.ObjectMeta{ - Name: "details-v1", - Namespace: "default", - Labels: map[string]string{ - "app": "details", - }, - }, - Spec: appsv1.DeploymentSpec{ - Replicas: &one, - Selector: &metaV1.LabelSelector{ - MatchLabels: map[string]string{"app": "details"}, - }, - Template: coreV1.PodTemplateSpec{ - ObjectMeta: metaV1.ObjectMeta{ - Labels: map[string]string{"app": "details"}, - }, - Spec: coreV1.PodSpec{ - Containers: []coreV1.Container{ - {Name: "details", Image: "docker.io/istio/examples-bookinfo-details-v1:1.15.0"}, - {Name: "istio-proxy", Image: "docker.io/istio/proxyv2:1.2.2"}, - }, - InitContainers: []coreV1.Container{ - {Name: "istio-init", Image: "docker.io/istio/proxy_init:1.2.2"}, - }, - }, - }, - }, - }, - }}, - &coreV1.ServiceList{Items: []coreV1.Service{ - { - ObjectMeta: metaV1.ObjectMeta{ - Name: "details", - Namespace: "default", - }, - Spec: coreV1.ServiceSpec{ - Ports: []coreV1.ServicePort{ - { - Port: 9080, - Name: "http", - }, - }, - Selector: map[string]string{"app": "details"}, - }, - }, - { - ObjectMeta: metaV1.ObjectMeta{ - Name: "dummyservice", - Namespace: "default", - }, - Spec: coreV1.ServiceSpec{ - Ports: []coreV1.ServicePort{ - { - Port: 9080, - Name: "http", - }, - }, - Selector: map[string]string{"app": "dummy"}, - }, - }, - { - ObjectMeta: metaV1.ObjectMeta{ - Name: "vmtest", - Namespace: "default", - }, - Spec: coreV1.ServiceSpec{ - Ports: []coreV1.ServicePort{ - { - Port: 9999, - Name: "http", - }, - }, - Selector: map[string]string{"app": "vmtest"}, - }, - }, - }}, - } - cannedDynamicConfig = []runtime.Object{ - &unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": "networking.istio.io/" + collections.IstioNetworkingV1Alpha3Serviceentries.Resource().Version(), - "kind": collections.IstioNetworkingV1Alpha3Serviceentries.Resource().Kind(), - "metadata": map[string]interface{}{ - "namespace": "default", - "name": "mesh-expansion-vmtest", - }, - }, - }, - } -) - -func TestRemoveFromMesh(t *testing.T) { - cases := []testcase{ - { - description: "Invalid command args - missing service name", - args: strings.Split("experimental remove-from-mesh service", " "), - expectedException: true, - expectedOutput: "Error: expecting service name\n", - }, - { - description: "Invalid command args - missing deployment name", - args: strings.Split("experimental remove-from-mesh deployment", " "), - expectedException: true, - expectedOutput: "Error: expecting deployment name\n", - }, - { - description: "Invalid namespace - system namespace not allowed", - args: strings.Split("experimental remove-from-mesh deployment local-path-storage", " "), - expectedException: true, - namespace: "local-path-storage", - expectedOutput: "Error: namespace local-path-storage is a system namespace and has no Istio sidecar injected\n", - }, - { - description: "Invalid namespace - system namespace not allowed", - args: strings.Split("experimental remove-from-mesh service istio-ingressgateway", " "), - expectedException: true, - namespace: "dubbo-system", - expectedOutput: "Error: namespace dubbo-system is a system namespace and has no Istio sidecar injected\n", - }, - { - description: "valid case - remove service from mesh", - args: strings.Split("experimental remove-from-mesh service details", " "), - expectedException: false, - k8sConfigs: cannedK8sConfig, - namespace: "default", - expectedOutput: "deployment \"details-v1.default\" updated successfully with Istio sidecar un-injected.\n", - }, - { - description: "valid case - remove deployment from mesh", - args: strings.Split("experimental remove-from-mesh deployment details-v1", " "), - expectedException: false, - k8sConfigs: cannedK8sConfig, - namespace: "default", - expectedOutput: "deployment \"details-v1.default\" updated successfully with Istio sidecar un-injected.\n", - }, - { - description: "service does not exist", - args: strings.Split("experimental remove-from-mesh service test", " "), - expectedException: true, - k8sConfigs: cannedK8sConfig, - expectedOutput: "Error: service \"test\" does not exist, skip\n", - }, - { - description: "deployment does not exist", - args: strings.Split("experimental remove-from-mesh deployment test", " "), - expectedException: true, - k8sConfigs: cannedK8sConfig, - expectedOutput: "Error: deployment \"test\" does not exist\n", - }, - { - description: "service does not exist (with short syntax)", - args: strings.Split("x rm svc test", " "), - expectedException: true, - k8sConfigs: cannedK8sConfig, - expectedOutput: "Error: service \"test\" does not exist, skip\n", - }, - { - description: "deployment does not exist (with short syntax)", - args: strings.Split("x rm deploy test", " "), - expectedException: true, - k8sConfigs: cannedK8sConfig, - expectedOutput: "Error: deployment \"test\" does not exist\n", - }, - { - description: "service without deployment", - args: strings.Split("experimental remove-from-mesh service dummyservice", " "), - expectedException: false, - k8sConfigs: cannedK8sConfig, - namespace: "default", - expectedOutput: "No deployments found for service dummyservice.default\n", - }, - { - description: "Invalid command args - missing external service name", - args: strings.Split("experimental remove-from-mesh external-service", " "), - expectedException: true, - expectedOutput: "Error: expecting external service name\n", - }, - { - description: "external-service does not exist", - args: strings.Split("experimental remove-from-mesh external-service test", " "), - expectedException: true, - k8sConfigs: cannedK8sConfig, - dynamicConfigs: cannedDynamicConfig, - expectedOutput: "Error: service \"test\" does not exist, skip\n", - }, - { - description: "ServiceEntry does not exist", - args: strings.Split("experimental remove-from-mesh external-service dummyservice", " "), - expectedException: true, - k8sConfigs: cannedK8sConfig, - dynamicConfigs: cannedDynamicConfig, - namespace: "default", - expectedOutput: "Error: service entry \"mesh-expansion-dummyservice\" does not exist, skip\n", - }, - { - description: "ServiceEntry does not exist (with short syntax)", - args: strings.Split("x rm es dummyservice", " "), - expectedException: true, - k8sConfigs: cannedK8sConfig, - dynamicConfigs: cannedDynamicConfig, - namespace: "default", - expectedOutput: "Error: service entry \"mesh-expansion-dummyservice\" does not exist, skip\n", - }, - { - description: "valid case - external service", - args: strings.Split("experimental remove-from-mesh external-service vmtest", " "), - expectedException: false, - k8sConfigs: cannedK8sConfig, - dynamicConfigs: cannedDynamicConfig, - namespace: "default", - expectedOutput: "Kubernetes Service \"vmtest.default\" has been deleted for external service \"vmtest\"\n" + - "Service Entry \"mesh-expansion-vmtest\" has been deleted for external service \"vmtest\"\n", - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("case %d %s", i, c.description), func(t *testing.T) { - verifyAddToMeshOutput(t, c) - }) - } -} diff --git a/istioctl/cmd/revision.go b/istioctl/cmd/revision.go deleted file mode 100644 index b599a6826..000000000 --- a/istioctl/cmd/revision.go +++ /dev/null @@ -1,1112 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "encoding/json" - "fmt" - "io" - "strings" - "text/tabwriter" - "time" -) - -import ( - "github.com/hashicorp/go-multierror" - "github.com/spf13/cobra" - "istio.io/api/label" - "istio.io/api/operator/v1alpha1" - admit_v1 "k8s.io/api/admissionregistration/v1" - v1 "k8s.io/api/core/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - apimachinery_schema "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/duration" - "k8s.io/apimachinery/pkg/util/validation" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/tag" - "github.com/apache/dubbo-go-pixiu/operator/cmd/mesh" - operator_istio "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio" - iopv1alpha1 "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/manifest" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -type revisionArgs struct { - manifestsPath string - name string - verbose bool - output string -} - -const ( - istioOperatorCRSection = "ISTIO-OPERATOR-CR" - controlPlaneSection = "CONTROL-PLANE" - gatewaysSection = "GATEWAYS" - webhooksSection = "MUTATING-WEBHOOKS" - podsSection = "PODS" - - jsonFormat = "json" - tableFormat = "table" -) - -var ( - validFormats = map[string]bool{ - tableFormat: true, - jsonFormat: true, - } - - defaultSections = []string{ - istioOperatorCRSection, - webhooksSection, - controlPlaneSection, - gatewaysSection, - } - - verboseSections = []string{ - istioOperatorCRSection, - webhooksSection, - controlPlaneSection, - gatewaysSection, - podsSection, - } -) - -var ( - istioOperatorGVR = apimachinery_schema.GroupVersionResource{ - Group: iopv1alpha1.SchemeGroupVersion.Group, - Version: iopv1alpha1.SchemeGroupVersion.Version, - Resource: "istiooperators", - } - - revArgs = revisionArgs{} -) - -func revisionCommand() *cobra.Command { - revisionCmd := &cobra.Command{ - Use: "revision", - Long: "The revision command provides a revision centric view of istio deployments. " + - "It provides insight into IstioOperator CRs defining the revision, istiod and gateway pods " + - "which are part of deployment of a particular revision.", - Short: "Provide insight into various revisions (istiod, gateways) installed in the cluster", - Aliases: []string{"rev"}, - } - revisionCmd.PersistentFlags().StringVarP(&revArgs.manifestsPath, "manifests", "d", "", mesh.ManifestsFlagHelpStr) - revisionCmd.PersistentFlags().BoolVarP(&revArgs.verbose, "verbose", "v", false, "Enable verbose output") - revisionCmd.PersistentFlags().StringVarP(&revArgs.output, "output", "o", tableFormat, "Output format for revision description "+ - "(available formats: table,json)") - - revisionCmd.AddCommand(revisionListCommand()) - revisionCmd.AddCommand(revisionDescribeCommand()) - revisionCmd.AddCommand(tagCommand()) - return revisionCmd -} - -func revisionDescribeCommand() *cobra.Command { - describeCmd := &cobra.Command{ - Use: "describe", - Example: ` # View the details of a revision named 'canary' - istioctl x revision describe canary - - # View the details of a revision named 'canary' and also the pods - # under that particular revision - istioctl x revision describe canary -v - - # Get details about a revision in json format (default format is human-friendly table format) - istioctl x revision describe canary -v -o json -`, - Short: "Show information about a revision, including customizations, " + - "istiod version and which pods/gateways are using it.", - PreRunE: func(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - return fmt.Errorf("revision must be specified") - } - if len(args) != 1 { - return fmt.Errorf("exactly 1 revision should be specified") - } - revArgs.name = args[0] - if !validFormats[revArgs.output] { - return fmt.Errorf("unknown format %s. It should be %#v", revArgs.output, validFormats) - } - if errs := validation.IsDNS1123Label(revArgs.name); len(errs) > 0 { - return fmt.Errorf("%s - invalid revision format: %v", revArgs.name, errs) - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - logger := clog.NewConsoleLogger(cmd.OutOrStdout(), cmd.ErrOrStderr(), scope) - return printRevisionDescription(cmd.OutOrStdout(), &revArgs, logger) - }, - } - describeCmd.Flags().BoolVarP(&revArgs.verbose, "verbose", "v", false, "Enable verbose output") - return describeCmd -} - -func revisionListCommand() *cobra.Command { - listCmd := &cobra.Command{ - Use: "list", - Short: "Show list of control plane and gateway revisions that are currently installed in cluster", - Example: ` # View summary of revisions installed in the current cluster - # which can be overridden with --context parameter. - istioctl x revision list - - # View list of revisions including customizations, istiod and gateway pods - istioctl x revision list -v -`, - PreRunE: func(cmd *cobra.Command, args []string) error { - if !revArgs.verbose && revArgs.manifestsPath != "" { - return fmt.Errorf("manifest path should only be specified with -v") - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - logger := clog.NewConsoleLogger(cmd.OutOrStdout(), cmd.ErrOrStderr(), scope) - return revisionList(cmd.OutOrStdout(), &revArgs, logger) - }, - } - return listCmd -} - -// PodFilteredInfo represents a small subset of fields from -// Pod object in Kubernetes. Exposed for integration test -type PodFilteredInfo struct { - Namespace string `json:"namespace"` - Name string `json:"name"` - Address string `json:"address"` - Status v1.PodPhase `json:"status"` - Age string `json:"age"` -} - -// IstioOperatorCRInfo represents a tiny subset of fields from -// IstioOperator CR. This structure is used for displaying data. -// Exposed for integration test -type IstioOperatorCRInfo struct { - IOP *iopv1alpha1.IstioOperator `json:"-"` - Namespace string `json:"namespace"` - Name string `json:"name"` - Profile string `json:"profile"` - Components []string `json:"components,omitempty"` - Customizations []iopDiff `json:"customizations,omitempty"` -} - -// MutatingWebhookConfigInfo represents a tiny subset of fields from -// MutatingWebhookConfiguration kubernetes object. This is exposed for -// integration tests only -type MutatingWebhookConfigInfo struct { - Name string `json:"name"` - Revision string `json:"revision"` - Tag string `json:"tag,omitempty"` -} - -// NsInfo represents namespace related information like pods running there. -// It is used to display data and is exposed for integration tests. -type NsInfo struct { - Name string `json:"name,omitempty"` - Pods []*PodFilteredInfo `json:"pods,omitempty"` -} - -// RevisionDescription is used to display revision related information. -// This is exposed for integration tests. -type RevisionDescription struct { - IstioOperatorCRs []*IstioOperatorCRInfo `json:"istio_operator_crs,omitempty"` - Webhooks []*MutatingWebhookConfigInfo `json:"webhooks,omitempty"` - ControlPlanePods []*PodFilteredInfo `json:"control_plane_pods,omitempty"` - IngressGatewayPods []*PodFilteredInfo `json:"ingess_gateways,omitempty"` - EgressGatewayPods []*PodFilteredInfo `json:"egress_gateways,omitempty"` - NamespaceSummary map[string]*NsInfo `json:"namespace_summary,omitempty"` -} - -func revisionList(writer io.Writer, args *revisionArgs, logger clog.Logger) error { - client, err := newKubeClient(kubeconfig, configContext) - if err != nil { - return fmt.Errorf("cannot create kubeclient for kubeconfig=%s, context=%s: %v", - kubeconfig, configContext, err) - } - - revisions := map[string]*RevisionDescription{} - - // Get a list of control planes which are installed in remote clusters - // In this case, it is possible that they only have webhooks installed. - webhooks, err := getWebhooks(context.Background(), client) - if err != nil { - return fmt.Errorf("error while listing mutating webhooks: %v", err) - } - for _, hook := range webhooks { - rev := renderWithDefault(hook.GetLabels()[label.IoIstioRev.Name], "default") - tag := hook.GetLabels()[tag.IstioTagLabel] - ri, revPresent := revisions[rev] - if revPresent { - if tag != "" { - ri.Webhooks = append(ri.Webhooks, &MutatingWebhookConfigInfo{ - Name: hook.Name, - Revision: rev, - Tag: tag, - }) - } - } else { - revisions[rev] = &RevisionDescription{ - IstioOperatorCRs: []*IstioOperatorCRInfo{}, - Webhooks: []*MutatingWebhookConfigInfo{{Name: hook.Name, Revision: rev, Tag: tag}}, - } - } - } - - // Get a list of all CRs which are installed in this cluster - iopcrs, err := getAllIstioOperatorCRs(client) - if err != nil { - return fmt.Errorf("error while listing IstioOperator CRs: %v", err) - } - for _, iop := range iopcrs { - rev := renderWithDefault(iop.Spec.GetRevision(), "default") - if ri := revisions[rev]; ri == nil { - revisions[rev] = &RevisionDescription{} - } - iopInfo := &IstioOperatorCRInfo{ - IOP: iop, - Namespace: iop.GetNamespace(), - Name: iop.GetName(), - Profile: iop.Spec.GetProfile(), - Components: getEnabledComponents(iop.Spec), - Customizations: nil, - } - if args.verbose { - iopInfo.Customizations, err = getDiffs(iop, revArgs.manifestsPath, - profileWithDefault(iop.Spec.GetProfile()), logger) - if err != nil { - return fmt.Errorf("error while finding customizations: %v", err) - } - } - revisions[rev].IstioOperatorCRs = append(revisions[rev].IstioOperatorCRs, iopInfo) - } - - if args.verbose { - for rev, desc := range revisions { - revClient, err := newKubeClientWithRevision(kubeconfig, configContext, rev) - if err != nil { - return fmt.Errorf("failed to get revision based kubeclient for revision: %s", rev) - } - if err = annotateWithControlPlanePodInfo(desc, revClient); err != nil { - return fmt.Errorf("failed to get control plane pods for revision: %s", rev) - } - if err = annotateWithGatewayInfo(desc, revClient); err != nil { - return fmt.Errorf("failed to get gateway pods for revision: %s", rev) - } - } - } - - switch revArgs.output { - case jsonFormat: - return printJSON(writer, revisions) - case tableFormat: - if len(revisions) == 0 { - _, err = fmt.Fprintln(writer, "No Istio installation found.\n"+ - "No IstioOperator CR or sidecar injectors found") - return err - } - return printRevisionInfoTable(writer, args.verbose, revisions) - default: - return fmt.Errorf("unknown format: %s", revArgs.output) - } -} - -func printRevisionInfoTable(writer io.Writer, verbose bool, revisions map[string]*RevisionDescription) error { - if err := printSummaryTable(writer, verbose, revisions); err != nil { - return fmt.Errorf("failed to print summary table: %v", err) - } - if verbose { - if err := printControlPlaneSummaryTable(writer, revisions); err != nil { - return fmt.Errorf("failed to print control plane table: %v", err) - } - if err := printGatewaySummaryTable(writer, revisions); err != nil { - return fmt.Errorf("failed to print gateway summary table: %v", err) - } - } - return nil -} - -func printControlPlaneSummaryTable(w io.Writer, revisions map[string]*RevisionDescription) error { - fmt.Fprintf(w, "\nCONTROL PLANE:\n") - tw := new(tabwriter.Writer).Init(w, 0, 8, 1, ' ', 0) - fmt.Fprintf(tw, "REVISION\tISTIOD-ENABLED\tCONTROL-PLANE-PODS\n") - for rev, rd := range revisions { - isIstiodEnabled := false - outer: - for _, iop := range rd.IstioOperatorCRs { - for _, c := range iop.Components { - if c == "istiod" { - isIstiodEnabled = true - break outer - } - } - } - maxRows := max(1, len(rd.ControlPlanePods)) - for i := 0; i < maxRows; i++ { - rowRev, rowEnabled, rowPod := "", "NO", "" - if i == 0 { - rowRev = rev - if isIstiodEnabled { - rowEnabled = "YES" - } - } - switch { - case i < len(rd.ControlPlanePods): - rowPod = fmt.Sprintf("%s/%s", - rd.ControlPlanePods[i].Namespace, - rd.ControlPlanePods[i].Name) - case i == 0 && len(rd.ControlPlanePods) == 0: - rowPod = "" - } - fmt.Fprintf(tw, "%s\t%s\t%s\n", rowRev, rowEnabled, rowPod) - } - } - return tw.Flush() -} - -func printGatewaySummaryTable(w io.Writer, revisions map[string]*RevisionDescription) error { - if err := printIngressGatewaySummaryTable(w, revisions); err != nil { - return fmt.Errorf("error while printing ingress gateway summary: %v", err) - } - if err := printEgressGatewaySummaryTable(w, revisions); err != nil { - return fmt.Errorf("error while printing egress gateway summary: %v", err) - } - return nil -} - -func printIngressGatewaySummaryTable(w io.Writer, revisions map[string]*RevisionDescription) error { - fmt.Fprintf(w, "\nINGRESS GATEWAYS:\n") - tw := new(tabwriter.Writer).Init(w, 0, 8, 1, ' ', 0) - fmt.Fprintf(tw, "REVISION\tDECLARED-GATEWAYS\tGATEWAY-POD\n") - for rev, rd := range revisions { - enabledIngressGateways := []string{} - for _, iop := range rd.IstioOperatorCRs { - for _, c := range iop.Components { - if strings.HasPrefix(c, "ingress") { - enabledIngressGateways = append(enabledIngressGateways, c) - } - } - } - maxRows := max(max(1, len(enabledIngressGateways)), len(rd.IngressGatewayPods)) - for i := 0; i < maxRows; i++ { - var rowRev, rowEnabled, rowPod string - if i == 0 { - rowRev = rev - } - if i == 0 && len(enabledIngressGateways) == 0 { - rowEnabled = "" - } else if i < len(enabledIngressGateways) { - rowEnabled = enabledIngressGateways[i] - } - if i == 0 && len(rd.IngressGatewayPods) == 0 { - rowPod = "" - } else if i < len(rd.IngressGatewayPods) { - rowPod = fmt.Sprintf("%s/%s", - rd.IngressGatewayPods[i].Namespace, - rd.IngressGatewayPods[i].Name) - } - fmt.Fprintf(tw, "%s\t%s\t%s\n", rowRev, rowEnabled, rowPod) - } - } - return tw.Flush() -} - -// TODO(su225): This is a copy paste of corresponding function of ingress. Refactor these parts! -func printEgressGatewaySummaryTable(w io.Writer, revisions map[string]*RevisionDescription) error { - fmt.Fprintf(w, "\nEGRESS GATEWAYS:\n") - tw := new(tabwriter.Writer).Init(w, 0, 8, 1, ' ', 0) - fmt.Fprintf(tw, "REVISION\tDECLARED-GATEWAYS\tGATEWAY-POD\n") - for rev, rd := range revisions { - enabledEgressGateways := []string{} - for _, iop := range rd.IstioOperatorCRs { - for _, c := range iop.Components { - if strings.HasPrefix(c, "egress") { - enabledEgressGateways = append(enabledEgressGateways, c) - } - } - } - maxRows := max(max(1, len(enabledEgressGateways)), len(rd.EgressGatewayPods)) - for i := 0; i < maxRows; i++ { - var rowRev, rowEnabled, rowPod string - if i == 0 { - rowRev = rev - } - if i == 0 && len(enabledEgressGateways) == 0 { - rowEnabled = "" - } else if i < len(enabledEgressGateways) { - rowEnabled = enabledEgressGateways[i] - } - if i == 0 && len(rd.EgressGatewayPods) == 0 { - rowPod = "" - } else if i < len(rd.EgressGatewayPods) { - rowPod = fmt.Sprintf("%s/%s", - rd.EgressGatewayPods[i].Namespace, - rd.EgressGatewayPods[i].Name) - } - fmt.Fprintf(tw, "%s\t%s\t%s\n", rowRev, rowEnabled, rowPod) - } - } - return tw.Flush() -} - -//nolint:errcheck -func printSummaryTable(writer io.Writer, verbose bool, revisions map[string]*RevisionDescription) error { - tw := new(tabwriter.Writer).Init(writer, 0, 8, 1, ' ', 0) - if verbose { - tw.Write([]byte("REVISION\tTAG\tISTIO-OPERATOR-CR\tPROFILE\tREQD-COMPONENTS\tCUSTOMIZATIONS\n")) - } else { - tw.Write([]byte("REVISION\tTAG\tISTIO-OPERATOR-CR\tPROFILE\tREQD-COMPONENTS\n")) - } - for r, ri := range revisions { - rowID, tags := 0, []string{} - for _, wh := range ri.Webhooks { - if wh.Tag != "" { - tags = append(tags, wh.Tag) - } - } - if len(tags) == 0 { - tags = append(tags, "") - } - for _, iop := range ri.IstioOperatorCRs { - profile := profileWithDefault(iop.Profile) - components := iop.Components - qualifiedName := fmt.Sprintf("%s/%s", iop.Namespace, iop.Name) - - customizations := []string{} - for _, c := range iop.Customizations { - customizations = append(customizations, fmt.Sprintf("%s=%s", c.Path, c.Value)) - } - if len(customizations) == 0 { - customizations = append(customizations, "") - } - maxIopRows := max(max(1, len(components)), len(customizations)) - for i := 0; i < maxIopRows; i++ { - var rowTag, rowRev string - var rowIopName, rowProfile, rowComp, rowCust string - if i == 0 { - rowIopName = qualifiedName - rowProfile = profile - } - if i < len(components) { - rowComp = components[i] - } - if i < len(customizations) { - rowCust = customizations[i] - } - if rowID < len(tags) { - rowTag = tags[rowID] - } - if rowID == 0 { - rowRev = r - } - if verbose { - fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\t%s\n", - rowRev, rowTag, rowIopName, rowProfile, rowComp, rowCust) - } else { - fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\n", - rowRev, rowTag, rowIopName, rowProfile, rowComp) - } - rowID++ - } - } - for rowID < len(tags) { - var rowRev, rowTag, rowIopName string - if rowID == 0 { - rowRev = r - } - if rowID == 0 { - rowIopName = "" - } - rowTag = tags[rowID] - if verbose { - fmt.Fprintf(tw, "%s\t%s\t%s\t \t \t \n", rowRev, rowTag, rowIopName) - } else { - fmt.Fprintf(tw, "%s\t%s\t%s\t \t \n", rowRev, rowTag, rowIopName) - } - rowID++ - } - } - return tw.Flush() -} - -func getAllIstioOperatorCRs(client kube.ExtendedClient) ([]*iopv1alpha1.IstioOperator, error) { - ucrs, err := client.Dynamic().Resource(istioOperatorGVR). - List(context.Background(), meta_v1.ListOptions{}) - if err != nil { - return []*iopv1alpha1.IstioOperator{}, fmt.Errorf("cannot retrieve IstioOperator CRs: %v", err) - } - iopCRs := []*iopv1alpha1.IstioOperator{} - for _, u := range ucrs.Items { - u.SetCreationTimestamp(meta_v1.Time{}) - u.SetManagedFields([]meta_v1.ManagedFieldsEntry{}) - iop, err := operator_istio.UnmarshalIstioOperator(util.ToYAML(u.Object), true) - if err != nil { - return []*iopv1alpha1.IstioOperator{}, - fmt.Errorf("error while converting to IstioOperator CR - %s/%s: %v", - u.GetNamespace(), u.GetName(), err) - } - iopCRs = append(iopCRs, iop) - } - return iopCRs, nil -} - -func printRevisionDescription(w io.Writer, args *revisionArgs, logger clog.Logger) error { - revision := args.name - client, err := newKubeClientWithRevision(kubeconfig, configContext, revision) - if err != nil { - return fmt.Errorf("cannot create kubeclient for kubeconfig=%s, context=%s: %v", - kubeconfig, configContext, err) - } - allIops, err := getAllIstioOperatorCRs(client) - if err != nil { - return fmt.Errorf("error while fetching IstioOperator CR: %v", err) - } - iopsInCluster := getIOPWithRevision(allIops, revision) - allWebhooks, err := getWebhooks(context.Background(), client) - if err != nil { - return fmt.Errorf("error while fetching mutating webhook configurations: %v", err) - } - webhooks := filterWebhooksWithRevision(allWebhooks, revision) - revDescription := getBasicRevisionDescription(iopsInCluster, webhooks) - if err = annotateWithIOPCustomization(revDescription, args.manifestsPath, logger); err != nil { - return err - } - if err = annotateWithControlPlanePodInfo(revDescription, client); err != nil { - return err - } - if err = annotateWithGatewayInfo(revDescription, client); err != nil { - return err - } - if !revisionExists(revDescription) { - return fmt.Errorf("revision %s is not present", revision) - } - if args.verbose { - revAliases := []string{revision} - for _, wh := range revDescription.Webhooks { - revAliases = append(revAliases, wh.Tag) - } - if err = annotateWithNamespaceAndPodInfo(revDescription, revAliases); err != nil { - return err - } - } - switch revArgs.output { - case jsonFormat: - return printJSON(w, revDescription) - case tableFormat: - sections := defaultSections - if args.verbose { - sections = verboseSections - } - return printTable(w, sections, revDescription) - default: - return fmt.Errorf("unknown format %s", revArgs.output) - } -} - -func revisionExists(revDescription *RevisionDescription) bool { - return len(revDescription.IstioOperatorCRs) != 0 || - len(revDescription.Webhooks) != 0 || - len(revDescription.ControlPlanePods) != 0 || - len(revDescription.IngressGatewayPods) != 0 || - len(revDescription.EgressGatewayPods) != 0 -} - -func annotateWithNamespaceAndPodInfo(revDescription *RevisionDescription, revisionAliases []string) error { - client, err := newKubeClient(kubeconfig, configContext) - if err != nil { - return fmt.Errorf("failed to create kubeclient: %v", err) - } - nsMap := make(map[string]*NsInfo) - for _, ra := range revisionAliases { - pods, err := getPodsWithSelector(client, "", &meta_v1.LabelSelector{ - MatchLabels: map[string]string{ - label.IoIstioRev.Name: ra, - }, - }) - if err != nil { - return fmt.Errorf("failed to fetch pods for rev/tag: %s: %v", ra, err) - } - for _, po := range pods { - fpo := getFilteredPodInfo(&po) - _, ok := nsMap[po.Namespace] - if !ok { - nsMap[po.Namespace] = &NsInfo{ - Name: po.Namespace, - Pods: []*PodFilteredInfo{fpo}, - } - } else { - nsMap[po.Namespace].Pods = append(nsMap[po.Namespace].Pods, fpo) - } - } - } - revDescription.NamespaceSummary = nsMap - return nil -} - -func annotateWithGatewayInfo(revDescription *RevisionDescription, client kube.ExtendedClient) error { - ingressPods, err := getPodsForComponent(client, "IngressGateways") - if err != nil { - return fmt.Errorf("error while fetching ingress gateway pods: %v", err) - } - revDescription.IngressGatewayPods = transformToFilteredPodInfo(ingressPods) - egressPods, err := getPodsForComponent(client, "EgressGateways") - if err != nil { - return fmt.Errorf("error while fetching egress gateway pods: %v", err) - } - revDescription.EgressGatewayPods = transformToFilteredPodInfo(egressPods) - return nil -} - -func annotateWithControlPlanePodInfo(revDescription *RevisionDescription, client kube.ExtendedClient) error { - controlPlanePods, err := getPodsForComponent(client, "Pilot") - if err != nil { - return fmt.Errorf("error while fetching control plane pods: %v", err) - } - revDescription.ControlPlanePods = transformToFilteredPodInfo(controlPlanePods) - return nil -} - -func annotateWithIOPCustomization(revDesc *RevisionDescription, manifestsPath string, logger clog.Logger) error { - for _, cr := range revDesc.IstioOperatorCRs { - cust, err := getDiffs(cr.IOP, manifestsPath, profileWithDefault(cr.IOP.Spec.GetProfile()), logger) - if err != nil { - return fmt.Errorf("error while computing customization for %s/%s: %v", - cr.Name, cr.Namespace, err) - } - cr.Customizations = cust - } - return nil -} - -func getBasicRevisionDescription(iopCRs []*iopv1alpha1.IstioOperator, - mutatingWebhooks []admit_v1.MutatingWebhookConfiguration) *RevisionDescription { - revDescription := &RevisionDescription{ - IstioOperatorCRs: []*IstioOperatorCRInfo{}, - Webhooks: []*MutatingWebhookConfigInfo{}, - } - for _, iop := range iopCRs { - revDescription.IstioOperatorCRs = append(revDescription.IstioOperatorCRs, &IstioOperatorCRInfo{ - IOP: iop, - Namespace: iop.Namespace, - Name: iop.Name, - Profile: renderWithDefault(iop.Spec.Profile, "default"), - Components: getEnabledComponents(iop.Spec), - Customizations: nil, - }) - } - for _, mwh := range mutatingWebhooks { - revDescription.Webhooks = append(revDescription.Webhooks, &MutatingWebhookConfigInfo{ - Name: mwh.Name, - Revision: renderWithDefault(mwh.Labels[label.IoIstioRev.Name], "default"), - Tag: mwh.Labels[tag.IstioTagLabel], - }) - } - return revDescription -} - -func printJSON(w io.Writer, res interface{}) error { - out, err := json.MarshalIndent(res, "", "\t") - if err != nil { - return fmt.Errorf("error while marshaling to JSON: %v", err) - } - fmt.Fprintln(w, string(out)) - return nil -} - -func filterWebhooksWithRevision(webhooks []admit_v1.MutatingWebhookConfiguration, revision string) []admit_v1.MutatingWebhookConfiguration { - whFiltered := []admit_v1.MutatingWebhookConfiguration{} - for _, wh := range webhooks { - if wh.GetLabels()[label.IoIstioRev.Name] == revision { - whFiltered = append(whFiltered, wh) - } - } - return whFiltered -} - -func transformToFilteredPodInfo(pods []v1.Pod) []*PodFilteredInfo { - pfilInfo := []*PodFilteredInfo{} - for _, p := range pods { - pfilInfo = append(pfilInfo, getFilteredPodInfo(&p)) - } - return pfilInfo -} - -func getFilteredPodInfo(pod *v1.Pod) *PodFilteredInfo { - return &PodFilteredInfo{ - Namespace: pod.Namespace, - Name: pod.Name, - Address: pod.Status.PodIP, - Status: pod.Status.Phase, - Age: translateTimestampSince(pod.CreationTimestamp), - } -} - -func printTable(w io.Writer, sections []string, desc *RevisionDescription) error { - errs := &multierror.Error{} - tablePrintFuncs := map[string]func(io.Writer, *RevisionDescription) error{ - istioOperatorCRSection: printIstioOperatorCRInfo, - webhooksSection: printWebhookInfo, - controlPlaneSection: printControlPlane, - gatewaysSection: printGateways, - podsSection: printPodsInfo, - } - for _, s := range sections { - f := tablePrintFuncs[s] - if f == nil { - errs = multierror.Append(fmt.Errorf("unknown section: %s", s), errs.Errors...) - continue - } - err := f(w, desc) - if err != nil { - errs = multierror.Append(fmt.Errorf("error in section %s: %v", s, err)) - } - } - return errs.ErrorOrNil() -} - -func printPodsInfo(w io.Writer, desc *RevisionDescription) error { - fmt.Fprintf(w, "\nPODS:\n") - if len(desc.NamespaceSummary) == 0 { - fmt.Fprintln(w, "No pods for this revision") - return nil - } - for ns, nsi := range desc.NamespaceSummary { - fmt.Fprintf(w, "NAMESPACE %s: (%d)\n", ns, len(nsi.Pods)) - if err := printPodTable(w, nsi.Pods); err != nil { - return fmt.Errorf("error while printing pod table: %v", err) - } - fmt.Fprintln(w) - } - return nil -} - -//nolint:unparam -func printIstioOperatorCRInfo(w io.Writer, desc *RevisionDescription) error { - fmt.Fprintf(w, "\nISTIO-OPERATOR CUSTOM RESOURCE: (%d)", len(desc.IstioOperatorCRs)) - if len(desc.IstioOperatorCRs) == 0 { - if len(desc.Webhooks) > 0 { - fmt.Fprintln(w, "\nThere are webhooks and Istiod could be external to the cluster") - } else { - // Ideally, it should not come here - fmt.Fprintln(w, "\nNo CRs found.") - } - return nil - } - for i, iop := range desc.IstioOperatorCRs { - fmt.Fprintf(w, "\n%d. %s/%s\n", i+1, iop.Namespace, iop.Name) - fmt.Fprintf(w, " COMPONENTS:\n") - if len(iop.Components) > 0 { - for _, c := range iop.Components { - fmt.Fprintf(w, " - %s\n", c) - } - } else { - fmt.Fprintf(w, " no enabled components\n") - } - - // For each IOP, print all customizations for it - fmt.Fprintf(w, " CUSTOMIZATIONS:\n") - if len(iop.Customizations) > 0 { - for _, customization := range iop.Customizations { - fmt.Fprintf(w, " - %s=%s\n", customization.Path, customization.Value) - } - } else { - fmt.Fprintf(w, " \n") - } - } - return nil -} - -//nolint:errcheck -func printWebhookInfo(w io.Writer, desc *RevisionDescription) error { - fmt.Fprintf(w, "\nMUTATING WEBHOOK CONFIGURATIONS: (%d)\n", len(desc.Webhooks)) - if len(desc.Webhooks) == 0 { - fmt.Fprintln(w, "No mutating webhook found for this revision. Something could be wrong with installation") - return nil - } - tw := new(tabwriter.Writer).Init(w, 0, 0, 1, ' ', 0) - tw.Write([]byte("WEBHOOK\tTAG\n")) - for _, wh := range desc.Webhooks { - tw.Write([]byte(fmt.Sprintf("%s\t%s\n", wh.Name, renderWithDefault(wh.Tag, "")))) - } - return tw.Flush() -} - -func printControlPlane(w io.Writer, desc *RevisionDescription) error { - fmt.Fprintf(w, "\nCONTROL PLANE PODS (ISTIOD): (%d)\n", len(desc.ControlPlanePods)) - if len(desc.ControlPlanePods) == 0 { - if len(desc.Webhooks) > 0 { - fmt.Fprintln(w, "No Istiod found in this cluster for the revision. "+ - "However there are webhooks. It is possible that Istiod is external to this cluster or "+ - "perhaps it is not uninstalled properly") - } else { - fmt.Fprintln(w, "No Istiod or the webhook found in this cluster for the revision. Something could be wrong") - } - return nil - } - return printPodTable(w, desc.ControlPlanePods) -} - -func printGateways(w io.Writer, desc *RevisionDescription) error { - if err := printIngressGateways(w, desc); err != nil { - return fmt.Errorf("error while printing ingress-gateway info: %v", err) - } - if err := printEgressGateways(w, desc); err != nil { - return fmt.Errorf("error while printing egress-gateway info: %v", err) - } - return nil -} - -func printIngressGateways(w io.Writer, desc *RevisionDescription) error { - fmt.Fprintf(w, "\nINGRESS GATEWAYS: (%d)\n", len(desc.IngressGatewayPods)) - if len(desc.IngressGatewayPods) == 0 { - if ingressGatewayEnabled(desc) { - fmt.Fprintln(w, "Ingress gateway is enabled for this revision. However there are no such pods. "+ - "It could be that it is replaced by ingress-gateway from another revision (as it is still upgraded in-place) "+ - "or it could be some issue with installation") - } else { - fmt.Fprintln(w, "Ingress gateway is disabled for this revision") - } - return nil - } - if !ingressGatewayEnabled(desc) { - fmt.Fprintln(w, "WARNING: Ingress gateway is not enabled for this revision.") - } - return printPodTable(w, desc.IngressGatewayPods) -} - -func printEgressGateways(w io.Writer, desc *RevisionDescription) error { - fmt.Fprintf(w, "\nEGRESS GATEWAYS: (%d)\n", len(desc.IngressGatewayPods)) - if len(desc.EgressGatewayPods) == 0 { - if egressGatewayEnabled(desc) { - fmt.Fprintln(w, "Egress gateway is enabled for this revision. However there are no such pods. "+ - "It could be that it is replaced by egress-gateway from another revision (as it is still upgraded in-place) "+ - "or it could be some issue with installation") - } else { - fmt.Fprintln(w, "Egress gateway is disabled for this revision") - } - return nil - } - if !egressGatewayEnabled(desc) { - fmt.Fprintln(w, "WARNING: Egress gateway is not enabled for this revision.") - } - return printPodTable(w, desc.EgressGatewayPods) -} - -type istioGatewayType = string - -const ( - ingress istioGatewayType = "ingress" - egress istioGatewayType = "egress" -) - -func ingressGatewayEnabled(desc *RevisionDescription) bool { - return gatewayTypeEnabled(desc, ingress) -} - -func egressGatewayEnabled(desc *RevisionDescription) bool { - return gatewayTypeEnabled(desc, egress) -} - -func gatewayTypeEnabled(desc *RevisionDescription, gwType istioGatewayType) bool { - for _, iopdesc := range desc.IstioOperatorCRs { - for _, comp := range iopdesc.Components { - if strings.HasPrefix(comp, gwType) { - return true - } - } - } - return false -} - -func getIOPWithRevision(iops []*iopv1alpha1.IstioOperator, revision string) []*iopv1alpha1.IstioOperator { - filteredIOPs := []*iopv1alpha1.IstioOperator{} - for _, iop := range iops { - if iop.Spec == nil { - continue - } - if iop.Spec.Revision == revision || (revision == "default" && iop.Spec.Revision == "") { - filteredIOPs = append(filteredIOPs, iop) - } - } - return filteredIOPs -} - -func printPodTable(w io.Writer, pods []*PodFilteredInfo) error { - podTableW := new(tabwriter.Writer).Init(w, 0, 0, 1, ' ', 0) - fmt.Fprintln(podTableW, "NAMESPACE\tNAME\tADDRESS\tSTATUS\tAGE") - for _, pod := range pods { - fmt.Fprintf(podTableW, "%s\t%s\t%s\t%s\t%s\n", - pod.Namespace, pod.Name, pod.Address, pod.Status, pod.Age) - } - return podTableW.Flush() -} - -func getEnabledComponents(iops *v1alpha1.IstioOperatorSpec) []string { - if iops == nil || iops.Components == nil { - return []string{} - } - enabledComponents := []string{} - if iops.Components.Base != nil && iops.Components.Base.Enabled.GetValue() { - enabledComponents = append(enabledComponents, "base") - } - if iops.Components.Cni != nil && iops.Components.Cni.Enabled.GetValue() { - enabledComponents = append(enabledComponents, "cni") - } - if iops.Components.Pilot != nil && iops.Components.Pilot.Enabled.GetValue() { - enabledComponents = append(enabledComponents, "istiod") - } - for _, gw := range iops.Components.IngressGateways { - if gw.Enabled.GetValue() { - enabledComponents = append(enabledComponents, fmt.Sprintf("ingress:%s", gw.GetName())) - } - } - for _, gw := range iops.Components.EgressGateways { - if gw.Enabled.GetValue() { - enabledComponents = append(enabledComponents, fmt.Sprintf("egress:%s", gw.GetName())) - } - } - return enabledComponents -} - -func getPodsForComponent(client kube.ExtendedClient, component string) ([]v1.Pod, error) { - return getPodsWithSelector(client, istioNamespace, &meta_v1.LabelSelector{ - MatchLabels: map[string]string{ - label.IoIstioRev.Name: client.Revision(), - label.OperatorComponent.Name: component, - }, - }) -} - -func getPodsWithSelector(client kube.ExtendedClient, ns string, selector *meta_v1.LabelSelector) ([]v1.Pod, error) { - labelSelector, err := meta_v1.LabelSelectorAsSelector(selector) - if err != nil { - return []v1.Pod{}, err - } - podList, err := client.CoreV1().Pods(ns).List(context.TODO(), - meta_v1.ListOptions{LabelSelector: labelSelector.String()}) - if err != nil { - return []v1.Pod{}, err - } - return podList.Items, nil -} - -type iopDiff struct { - Path string `json:"path"` - Value string `json:"value"` -} - -func getDiffs(installed *iopv1alpha1.IstioOperator, manifestsPath, profile string, l clog.Logger) ([]iopDiff, error) { - setFlags := []string{"profile=" + profile} - if manifestsPath != "" { - setFlags = append(setFlags, fmt.Sprintf("installPackagePath=%s", manifestsPath)) - } - _, base, err := manifest.GenerateConfig([]string{}, setFlags, true, nil, l) - if err != nil { - return []iopDiff{}, err - } - mapInstalled, err := config.ToMap(installed.Spec) - if err != nil { - return []iopDiff{}, err - } - mapBase, err := config.ToMap(base.Spec) - if err != nil { - return []iopDiff{}, err - } - return diffWalk("", "", mapInstalled, mapBase) -} - -// TODO(su225): Improve this and write tests for it. -func diffWalk(path, separator string, installed interface{}, base interface{}) ([]iopDiff, error) { - switch v := installed.(type) { - case map[string]interface{}: - accum := make([]iopDiff, 0) - typedOrig, ok := base.(map[string]interface{}) - if ok { - for key, vv := range v { - childwalk, err := diffWalk(fmt.Sprintf("%s%s%s", path, separator, pathComponent(key)), ".", vv, typedOrig[key]) - if err != nil { - return accum, err - } - accum = append(accum, childwalk...) - } - } - return accum, nil - case []interface{}: - accum := make([]iopDiff, 0) - typedOrig, ok := base.([]interface{}) - if ok { - for idx, vv := range v { - var baseMap interface{} - if idx < len(typedOrig) { - baseMap = typedOrig[idx] - } - indexwalk, err := diffWalk(fmt.Sprintf("%s[%d]", path, idx), ".", vv, baseMap) - if err != nil { - return accum, err - } - accum = append(accum, indexwalk...) - } - } - return accum, nil - case string: - if v != base && base != nil { - return []iopDiff{{Path: path, Value: fmt.Sprintf("%v", v)}}, nil - } - default: - if v != base && base != nil { - return []iopDiff{{Path: path, Value: fmt.Sprintf("%v", v)}}, nil - } - } - return []iopDiff{}, nil -} - -func renderWithDefault(s, def string) string { - if s != "" { - return s - } - return def -} - -func profileWithDefault(profile string) string { - if profile != "" { - return profile - } - return "default" -} - -func pathComponent(component string) string { - if !strings.Contains(component, util.PathSeparator) { - return component - } - return strings.ReplaceAll(component, util.PathSeparator, util.EscapedPathSeparator) -} - -// Human-readable age. (This is from kubectl pkg/describe/describe.go) -func translateTimestampSince(timestamp meta_v1.Time) string { - if timestamp.IsZero() { - return "" - } - return duration.HumanDuration(time.Since(timestamp.Time)) -} - -func max(x, y int) int { - if x > y { - return x - } - return y -} diff --git a/istioctl/cmd/revision_test.go b/istioctl/cmd/revision_test.go deleted file mode 100644 index fdefc843c..000000000 --- a/istioctl/cmd/revision_test.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "sort" - "testing" -) - -import ( - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - "istio.io/api/operator/v1alpha1" -) - -func TestGetEnabledComponentsFromIOPSpec(t *testing.T) { - enabledPbVal := &wrappers.BoolValue{Value: true} - disabledPbVal := &wrappers.BoolValue{Value: false} - - for _, test := range []struct { - name string - iops *v1alpha1.IstioOperatorSpec - expected []string - }{ - { - name: "iop spec is nil", - iops: nil, - expected: []string{}, - }, - { - name: "all components enabled", - iops: &v1alpha1.IstioOperatorSpec{ - Components: &v1alpha1.IstioComponentSetSpec{ - Base: &v1alpha1.BaseComponentSpec{Enabled: enabledPbVal}, - Pilot: &v1alpha1.ComponentSpec{Enabled: enabledPbVal}, - Cni: &v1alpha1.ComponentSpec{Enabled: enabledPbVal}, - IngressGateways: []*v1alpha1.GatewaySpec{ - {Name: "ingressgateway", Enabled: enabledPbVal}, - {Name: "eastwestgateway", Enabled: enabledPbVal}, - }, - EgressGateways: []*v1alpha1.GatewaySpec{ - {Name: "egressgateway", Enabled: enabledPbVal}, - }, - }, - }, - expected: []string{ - "base", "istiod", "cni", - "ingress:ingressgateway", "ingress:eastwestgateway", - "egress:egressgateway", - }, - }, - { - name: "cni and gateways are disabled", - iops: &v1alpha1.IstioOperatorSpec{ - Components: &v1alpha1.IstioComponentSetSpec{ - Base: &v1alpha1.BaseComponentSpec{Enabled: enabledPbVal}, - Pilot: &v1alpha1.ComponentSpec{Enabled: enabledPbVal}, - Cni: &v1alpha1.ComponentSpec{Enabled: disabledPbVal}, - IngressGateways: []*v1alpha1.GatewaySpec{ - {Name: "ingressgateway", Enabled: disabledPbVal}, - }, - EgressGateways: []*v1alpha1.GatewaySpec{ - {Name: "egressgateway", Enabled: disabledPbVal}, - }, - }, - }, - expected: []string{"base", "istiod"}, - }, - { - name: "all components are disabled", - iops: &v1alpha1.IstioOperatorSpec{ - Components: &v1alpha1.IstioComponentSetSpec{ - Base: &v1alpha1.BaseComponentSpec{Enabled: disabledPbVal}, - Pilot: &v1alpha1.ComponentSpec{Enabled: disabledPbVal}, - Cni: &v1alpha1.ComponentSpec{Enabled: disabledPbVal}, - IngressGateways: []*v1alpha1.GatewaySpec{ - {Name: "ingressgateway", Enabled: disabledPbVal}, - }, - EgressGateways: []*v1alpha1.GatewaySpec{ - {Name: "egressgateway", Enabled: disabledPbVal}, - }, - }, - }, - expected: []string{}, - }, - { - name: "component-spec has nil", - iops: &v1alpha1.IstioOperatorSpec{ - Components: &v1alpha1.IstioComponentSetSpec{ - Base: &v1alpha1.BaseComponentSpec{Enabled: enabledPbVal}, - Pilot: &v1alpha1.ComponentSpec{Enabled: enabledPbVal}, - }, - }, - expected: []string{"base", "istiod"}, - }, - } { - t.Run(test.name, func(st *testing.T) { - actual := getEnabledComponents(test.iops) - sort.Strings(actual) - sort.Strings(test.expected) - if len(actual) != len(test.expected) { - st.Fatalf("length of actual(%d) and expected(%d) don't match. "+ - "actual=%v, expected=%v", len(actual), len(test.expected), actual, test.expected) - } - for i := 0; i < len(actual); i++ { - if actual[i] != test.expected[i] { - st.Fatalf("actual %s does not match expected %s", actual[i], test.expected[i]) - } - } - }) - } -} diff --git a/istioctl/cmd/root.go b/istioctl/cmd/root.go deleted file mode 100644 index 2e1852cb5..000000000 --- a/istioctl/cmd/root.go +++ /dev/null @@ -1,388 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "errors" - "fmt" - "path/filepath" - "strings" -) - -import ( - "github.com/spf13/cobra" - "github.com/spf13/cobra/doc" - "github.com/spf13/viper" - "istio.io/pkg/collateral" - "istio.io/pkg/env" - "istio.io/pkg/log" - v1 "k8s.io/api/core/v1" - "k8s.io/client-go/tools/clientcmd" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/install" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/multicluster" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/validate" - "github.com/apache/dubbo-go-pixiu/operator/cmd/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/cmd" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/tools/bug-report/pkg/bugreport" -) - -// CommandParseError distinguishes an error parsing istioctl CLI arguments from an error processing -type CommandParseError struct { - e error -} - -func (c CommandParseError) Error() string { - return c.e.Error() -} - -const ( - // Location to read istioctl defaults from - defaultIstioctlConfig = "$HOME/.istioctl/config.yaml" - - // ExperimentalMsg indicate active development and not for production use warning. - ExperimentalMsg = `THIS COMMAND IS UNDER ACTIVE DEVELOPMENT AND NOT READY FOR PRODUCTION USE.` -) - -const ( - FlagNamespace = "namespace" - FlagIstioNamespace = "istioNamespace" - FlagCharts = "charts" -) - -var ( - // IstioConfig is the name of the istioctl config file (if any) - IstioConfig = env.RegisterStringVar("ISTIOCONFIG", defaultIstioctlConfig, - "Default values for istioctl flags").Get() - - kubeconfig string - configContext string - namespace string - istioNamespace string - defaultNamespace string - - // Create a kubernetes client (or mockClient) for talking to control plane components - kubeClientWithRevision = newKubeClientWithRevision - - // Create a kubernetes.ExecClient (or mock) for talking to data plane components - kubeClient = newKubeClient - - loggingOptions = defaultLogOptions() - - // scope is for dev logging. Warning: log levels are not set by --log_output_level until command is Run(). - scope = log.RegisterScope("cli", "istioctl", 0) -) - -func defaultLogOptions() *log.Options { - o := log.DefaultOptions() - - // These scopes are, at the default "INFO" level, too chatty for command line use - o.SetOutputLevel("validation", log.ErrorLevel) - o.SetOutputLevel("processing", log.ErrorLevel) - o.SetOutputLevel("analysis", log.WarnLevel) - o.SetOutputLevel("installer", log.WarnLevel) - o.SetOutputLevel("translator", log.WarnLevel) - o.SetOutputLevel("adsc", log.WarnLevel) - o.SetOutputLevel("default", log.WarnLevel) - o.SetOutputLevel("klog", log.WarnLevel) - o.SetOutputLevel("kube", log.ErrorLevel) - - return o -} - -// ConfigAndEnvProcessing uses spf13/viper for overriding CLI parameters -func ConfigAndEnvProcessing() error { - configPath := filepath.Dir(IstioConfig) - baseName := filepath.Base(IstioConfig) - configType := filepath.Ext(IstioConfig) - configName := baseName[0 : len(baseName)-len(configType)] - if configType != "" { - configType = configType[1:] - } - - // Allow users to override some variables through $HOME/.istioctl/config.yaml - // and environment variables. - viper.SetEnvPrefix("ISTIOCTL") - viper.AutomaticEnv() - viper.AllowEmptyEnv(true) // So we can say ISTIOCTL_CERT_DIR="" to suppress certs - viper.SetConfigName(configName) - viper.SetConfigType(configType) - viper.AddConfigPath(configPath) - viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) - err := viper.ReadInConfig() - // Ignore errors reading the configuration unless the file is explicitly customized - if IstioConfig != defaultIstioctlConfig { - return err - } - - return nil -} - -func init() { - viper.SetDefault("istioNamespace", constants.IstioSystemNamespace) - viper.SetDefault("xds-port", 15012) -} - -// GetRootCmd returns the root of the cobra command-tree. -func GetRootCmd(args []string) *cobra.Command { - rootCmd := &cobra.Command{ - Use: "istioctl", - Short: "Istio control interface.", - SilenceUsage: true, - DisableAutoGenTag: true, - Long: `Istio configuration command line utility for service operators to -debug and diagnose their Istio mesh. -`, - PersistentPreRunE: configureLogging, - } - - rootCmd.SetArgs(args) - - rootCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "c", "", - "Kubernetes configuration file") - - rootCmd.PersistentFlags().StringVar(&configContext, "context", "", - "The name of the kubeconfig context to use") - - rootCmd.PersistentFlags().StringVarP(&istioNamespace, FlagIstioNamespace, "i", viper.GetString(FlagIstioNamespace), - "Istio system namespace") - - rootCmd.PersistentFlags().StringVarP(&namespace, FlagNamespace, "n", v1.NamespaceAll, - "Config namespace") - - _ = rootCmd.RegisterFlagCompletionFunc(FlagIstioNamespace, validNamespaceArgs) - _ = rootCmd.RegisterFlagCompletionFunc(FlagNamespace, validNamespaceArgs) - - // Attach the Istio logging options to the command. - loggingOptions.AttachCobraFlags(rootCmd) - hiddenFlags := []string{ - "log_as_json", "log_rotate", "log_rotate_max_age", "log_rotate_max_backups", - "log_rotate_max_size", "log_stacktrace_level", "log_target", "log_caller", "log_output_level", - } - for _, opt := range hiddenFlags { - _ = rootCmd.PersistentFlags().MarkHidden(opt) - } - - cmd.AddFlags(rootCmd) - - kubeInjectCmd := injectCommand() - hideInheritedFlags(kubeInjectCmd, FlagNamespace) - rootCmd.AddCommand(kubeInjectCmd) - - experimentalCmd := &cobra.Command{ - Use: "experimental", - Aliases: []string{"x", "exp"}, - Short: "Experimental commands that may be modified or deprecated", - } - - xdsBasedTroubleshooting := []*cobra.Command{ - xdsVersionCommand(), - xdsStatusCommand(), - } - debugBasedTroubleshooting := []*cobra.Command{ - newVersionCommand(), - statusCommand(), - } - var debugCmdAttachmentPoint *cobra.Command - if viper.GetBool("PREFER-EXPERIMENTAL") { - legacyCmd := &cobra.Command{ - Use: "legacy", - Short: "Legacy command variants", - } - rootCmd.AddCommand(legacyCmd) - for _, c := range xdsBasedTroubleshooting { - rootCmd.AddCommand(c) - } - debugCmdAttachmentPoint = legacyCmd - } else { - debugCmdAttachmentPoint = rootCmd - } - for _, c := range xdsBasedTroubleshooting { - experimentalCmd.AddCommand(c) - } - for _, c := range debugBasedTroubleshooting { - debugCmdAttachmentPoint.AddCommand(c) - } - - rootCmd.AddCommand(experimentalCmd) - rootCmd.AddCommand(proxyConfig()) - rootCmd.AddCommand(adminCmd()) - experimentalCmd.AddCommand(injectorCommand()) - - rootCmd.AddCommand(install.NewVerifyCommand()) - experimentalCmd.AddCommand(AuthZ()) - rootCmd.AddCommand(seeExperimentalCmd("authz")) - experimentalCmd.AddCommand(uninjectCommand()) - experimentalCmd.AddCommand(metricsCmd()) - experimentalCmd.AddCommand(describe()) - experimentalCmd.AddCommand(addToMeshCmd()) - experimentalCmd.AddCommand(removeFromMeshCmd()) - experimentalCmd.AddCommand(waitCmd()) - experimentalCmd.AddCommand(mesh.UninstallCmd(loggingOptions)) - experimentalCmd.AddCommand(configCmd()) - experimentalCmd.AddCommand(workloadCommands()) - experimentalCmd.AddCommand(revisionCommand()) - experimentalCmd.AddCommand(debugCommand()) - experimentalCmd.AddCommand(preCheck()) - experimentalCmd.AddCommand(statsConfigCmd()) - - analyzeCmd := Analyze() - hideInheritedFlags(analyzeCmd, FlagIstioNamespace) - rootCmd.AddCommand(analyzeCmd) - - dashboardCmd := dashboard() - hideInheritedFlags(dashboardCmd, FlagNamespace, FlagIstioNamespace) - rootCmd.AddCommand(dashboardCmd) - - manifestCmd := mesh.ManifestCmd(loggingOptions) - hideInheritedFlags(manifestCmd, FlagNamespace, FlagIstioNamespace, FlagCharts) - rootCmd.AddCommand(manifestCmd) - - operatorCmd := mesh.OperatorCmd() - hideInheritedFlags(operatorCmd, FlagNamespace, FlagIstioNamespace, FlagCharts) - rootCmd.AddCommand(operatorCmd) - - installCmd := mesh.InstallCmd(loggingOptions) - hideInheritedFlags(installCmd, FlagNamespace, FlagIstioNamespace, FlagCharts) - rootCmd.AddCommand(installCmd) - - profileCmd := mesh.ProfileCmd(loggingOptions) - hideInheritedFlags(profileCmd, FlagNamespace, FlagIstioNamespace, FlagCharts) - rootCmd.AddCommand(profileCmd) - - upgradeCmd := mesh.UpgradeCmd(loggingOptions) - hideInheritedFlags(upgradeCmd, FlagNamespace, FlagIstioNamespace, FlagCharts) - rootCmd.AddCommand(upgradeCmd) - - bugReportCmd := bugreport.Cmd(loggingOptions) - hideInheritedFlags(bugReportCmd, FlagNamespace, FlagIstioNamespace) - rootCmd.AddCommand(bugReportCmd) - - tagCmd := tagCommand() - hideInheritedFlags(tagCommand(), FlagNamespace, FlagIstioNamespace, FlagCharts) - rootCmd.AddCommand(tagCmd) - - remoteSecretCmd := multicluster.NewCreateRemoteSecretCommand() - remoteClustersCmd := clustersCommand() - // leave the multicluster commands in x for backwards compat - rootCmd.AddCommand(remoteSecretCmd) - rootCmd.AddCommand(remoteClustersCmd) - experimentalCmd.AddCommand(remoteSecretCmd) - experimentalCmd.AddCommand(remoteClustersCmd) - - rootCmd.AddCommand(collateral.CobraCommand(rootCmd, &doc.GenManHeader{ - Title: "Istio Control", - Section: "istioctl CLI", - Manual: "Istio Control", - })) - - validateCmd := validate.NewValidateCommand(&istioNamespace, &namespace) - hideInheritedFlags(validateCmd, "kubeconfig") - rootCmd.AddCommand(validateCmd) - - rootCmd.AddCommand(optionsCommand(rootCmd)) - - // BFS apply the flag error function to all subcommands - seenCommands := make(map[*cobra.Command]bool) - var commandStack []*cobra.Command - - commandStack = append(commandStack, rootCmd) - - for len(commandStack) > 0 { - n := len(commandStack) - 1 - curCmd := commandStack[n] - commandStack = commandStack[:n] - seenCommands[curCmd] = true - for _, command := range curCmd.Commands() { - if !seenCommands[command] { - commandStack = append(commandStack, command) - } - } - curCmd.SetFlagErrorFunc(func(_ *cobra.Command, e error) error { - return CommandParseError{e} - }) - } - - return rootCmd -} - -func hideInheritedFlags(orig *cobra.Command, hidden ...string) { - orig.SetHelpFunc(func(cmd *cobra.Command, args []string) { - for _, hidden := range hidden { - _ = cmd.Flags().MarkHidden(hidden) // nolint: errcheck - } - - orig.SetHelpFunc(nil) - orig.HelpFunc()(cmd, args) - }) -} - -func configureLogging(_ *cobra.Command, _ []string) error { - if err := log.Configure(loggingOptions); err != nil { - return err - } - defaultNamespace = getDefaultNamespace(kubeconfig) - return nil -} - -func getDefaultNamespace(kubeconfig string) string { - configAccess := clientcmd.NewDefaultPathOptions() - - if kubeconfig != "" { - // use specified kubeconfig file for the location of the - // config to read - configAccess.GlobalFile = kubeconfig - } - - // gets existing kubeconfig or returns new empty config - config, err := configAccess.GetStartingConfig() - if err != nil { - return v1.NamespaceDefault - } - - // If a specific context was specified, use that. Otherwise, just use the current context from the kube config. - selectedContext := config.CurrentContext - if configContext != "" { - selectedContext = configContext - } - - // Use the namespace associated with the selected context as default, if the context has one - context, ok := config.Contexts[selectedContext] - if !ok { - return v1.NamespaceDefault - } - if context.Namespace == "" { - return v1.NamespaceDefault - } - return context.Namespace -} - -// seeExperimentalCmd is used for commands that have been around for a release but not graduated -// Other alternative -// for graduatedCmd see https://github.com/istio/istio/pull/26408 -// for softGraduatedCmd see https://github.com/istio/istio/pull/26563 -func seeExperimentalCmd(name string) *cobra.Command { - msg := fmt.Sprintf("(%s is experimental. Use `istioctl experimental %s`)", name, name) - return &cobra.Command{ - Use: name, - Short: msg, - RunE: func(_ *cobra.Command, _ []string) error { - return errors.New(msg) - }, - } -} diff --git a/istioctl/cmd/root_test.go b/istioctl/cmd/root_test.go deleted file mode 100644 index 6b93ff05f..000000000 --- a/istioctl/cmd/root_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "bytes" - "strings" - "testing" -) - -import ( - "github.com/spf13/cobra" -) - -func checkHelpForFlag(t *testing.T, gotHelpText, flag string, wantExists bool) { - t.Helper() - - if strings.Contains(gotHelpText, flag) != wantExists { - if wantExists { - t.Errorf("%q flag was expected but not found in help text", flag) - } else { - t.Errorf("%q flag was found in help text but not expected", flag) - } - } -} - -func TestHideInheritedFlags(t *testing.T) { - const ( - parentFlag0 = "parent-flag0" - parentFlag1 = "parent-flag1" - parentFlag2 = "parent-flag2" - childFlag2 = "child-flag2" - ) - parent := &cobra.Command{Use: "parent"} - _ = parent.PersistentFlags().String(parentFlag0, "", parentFlag0) - _ = parent.PersistentFlags().String(parentFlag1, "", parentFlag1) - _ = parent.PersistentFlags().String(parentFlag2, "", parentFlag2) - var out bytes.Buffer - parent.SetOut(&out) - parent.SetErr(&out) - - child := &cobra.Command{ - Use: "child", - Run: func(c *cobra.Command, args []string) {}, - } - _ = parent.PersistentFlags().String(childFlag2, "", childFlag2) - parent.AddCommand(child) - - // verify both parent flags and the child flag are visible by default - parent.SetArgs([]string{"child", "--help"}) - if err := parent.Execute(); err != nil { - t.Fatal(err) - } - got := out.String() - out.Reset() - checkHelpForFlag(t, got, parentFlag0, true) - checkHelpForFlag(t, got, parentFlag1, true) - checkHelpForFlag(t, got, parentFlag2, true) - checkHelpForFlag(t, got, childFlag2, true) - - // verify the hidden parent flag is not visible in help text - hideInheritedFlags(child, parentFlag1, parentFlag2) - if err := parent.Execute(); err != nil { - t.Fatal(err) - } - got = out.String() - out.Reset() - checkHelpForFlag(t, got, parentFlag0, true) - checkHelpForFlag(t, got, parentFlag1, false) - checkHelpForFlag(t, got, parentFlag1, false) - checkHelpForFlag(t, got, childFlag2, true) -} diff --git a/istioctl/cmd/sysexits.go b/istioctl/cmd/sysexits.go deleted file mode 100644 index 600ea295e..000000000 --- a/istioctl/cmd/sysexits.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "strings" -) - -// Values should try to use sendmail-style values as in -// See e.g. https://man.openbsd.org/sysexits.3 -// or `less /usr/includes/sysexits.h` if you're on Linux -// -// Picking the right range is tricky--there are a lot of reserved ones (see -// https://www.tldp.org/LDP/abs/html/exitcodes.html#EXITCODESREF) and then some -// used by convention (see sysexits). -// -// The intention here is to use 64-78 in a way that matches the attempt in -// sysexits to signify some error running istioctl, and use 79-125 as custom -// error codes for other info that we'd like to use to pass info on. -const ( - ExitUnknownError = 1 // for compatibility with existing exit code - ExitIncorrectUsage = 64 - ExitDataError = 65 // some format error with input data - - // below here are non-zero exit codes that don't indicate an error with istioctl itself - ExitAnalyzerFoundIssues = 79 // istioctl analyze found issues, for CI/CD -) - -func GetExitCode(e error) int { - if strings.Contains(e.Error(), "unknown command") { - e = CommandParseError{e} - } - - switch e.(type) { - case CommandParseError: - return ExitIncorrectUsage - case FileParseError: - return ExitDataError - case AnalyzerFoundIssuesError: - return ExitAnalyzerFoundIssues - default: - return ExitUnknownError - } -} diff --git a/istioctl/cmd/sysexits_test.go b/istioctl/cmd/sysexits_test.go deleted file mode 100644 index 3d364a575..000000000 --- a/istioctl/cmd/sysexits_test.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "errors" - "testing" -) - -var KnownErrorCode = map[error]int{ - errors.New("unknown command"): ExitIncorrectUsage, - errors.New("unexpected error"): ExitUnknownError, - CommandParseError{e: errors.New("command parse error")}: ExitIncorrectUsage, - FileParseError{}: ExitDataError, - AnalyzerFoundIssuesError{}: ExitAnalyzerFoundIssues, -} - -func TestKnownExitStrings(t *testing.T) { - for err, wantCode := range KnownErrorCode { - if code := GetExitCode(err); code != wantCode { - t.Errorf("For %v want %v, but is %v", err, wantCode, code) - } - } -} diff --git a/istioctl/cmd/tag.go b/istioctl/cmd/tag.go deleted file mode 100644 index 26077f862..000000000 --- a/istioctl/cmd/tag.go +++ /dev/null @@ -1,457 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "fmt" - "io" - "strings" - "text/tabwriter" -) - -import ( - "github.com/spf13/cobra" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/tag" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/formatting" - "github.com/apache/dubbo-go-pixiu/operator/cmd/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/webhook" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/diag" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/local" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -const ( - - // help strings and long formatted user outputs - skipConfirmationFlagHelpStr = `The skipConfirmation determines whether the user is prompted for confirmation. -If set to true, the user is not prompted and a Yes response is assumed in all cases.` - overrideHelpStr = `If true, allow revision tags to be overwritten, otherwise reject revision tag updates that -overwrite existing revision tags.` - revisionHelpStr = "Control plane revision to reference from a given revision tag" - tagCreatedStr = `Revision tag %q created, referencing control plane revision %q. To enable injection using this -revision tag, use 'kubectl label namespace istio.io/rev=%s' -` - webhookNameHelpStr = "Name to use for a revision tag's mutating webhook configuration." - autoInjectNamespacesHelpStr = "If set to true, the sidecars should be automatically injected into all namespaces by default" -) - -// options for CLI -var ( - // revision to point tag webhook at - revision = "" - manifestsPath = "" - overwrite = false - skipConfirmation = false - webhookName = "" - autoInjectNamespaces = false -) - -type tagDescription struct { - Tag string `json:"tag"` - Revision string `json:"revision"` - Namespaces []string `json:"namespaces"` -} - -func tagCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "tag", - Short: "Command group used to interact with revision tags", - Long: `Command group used to interact with revision tags. Revision tags allow for the creation of mutable aliases -referring to control plane revisions for sidecar injection. - -With revision tags, rather than relabeling a namespace from "istio.io/rev=revision-a" to "istio.io/rev=revision-b" to -change which control plane revision handles injection, it's possible to create a revision tag "prod" and label our -namespace "istio.io/rev=prod". The "prod" revision tag could point to "1-7-6" initially and then be changed to point to "1-8-1" -at some later point. - -This allows operators to change which Istio control plane revision should handle injection for a namespace or set of namespaces -without manual relabeling of the "istio.io/rev" tag. -`, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) != 0 { - return fmt.Errorf("unknown subcommand %q", args[0]) - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - cmd.HelpFunc()(cmd, args) - return nil - }, - } - - cmd.AddCommand(tagSetCommand()) - cmd.AddCommand(tagGenerateCommand()) - cmd.AddCommand(tagListCommand()) - cmd.AddCommand(tagRemoveCommand()) - - return cmd -} - -func tagSetCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "set ", - Short: "Create or modify revision tags", - Long: `Create or modify revision tags. Tag an Istio control plane revision for use with namespace istio.io/rev -injection labels.`, - Example: ` # Create a revision tag from the "1-8-0" revision - istioctl tag set prod --revision 1-8-0 - - # Point namespace "test-ns" at the revision pointed to by the "prod" revision tag - kubectl label ns test-ns istio.io/rev=prod - - # Change the revision tag to reference the "1-8-1" revision - istioctl tag set prod --revision 1-8-1 --overwrite - - # Make revision "1-8-1" the default revision, both resulting in that revision handling injection for "istio-injection=enabled" - # and validating resources cluster-wide - istioctl tag set default --revision 1-8-1 - - # Rollout namespace "test-ns" to update workloads to the "1-8-1" revision - kubectl rollout restart deployments -n test-ns -`, - SuggestFor: []string{"create"}, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - return fmt.Errorf("must provide a tag for modification") - } - if len(args) > 1 { - return fmt.Errorf("must provide a single tag for creation") - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - client, err := kubeClient(kubeconfig, configContext) - if err != nil { - return fmt.Errorf("failed to create Kubernetes client: %v", err) - } - - return setTag(context.Background(), client, args[0], revision, istioNamespace, false, cmd.OutOrStdout(), cmd.OutOrStderr()) - }, - } - - cmd.PersistentFlags().BoolVar(&overwrite, "overwrite", false, overrideHelpStr) - cmd.PersistentFlags().StringVarP(&manifestsPath, "manifests", "d", "", mesh.ManifestsFlagHelpStr) - cmd.PersistentFlags().BoolVarP(&skipConfirmation, "skip-confirmation", "y", false, skipConfirmationFlagHelpStr) - cmd.PersistentFlags().StringVarP(&revision, "revision", "r", "", revisionHelpStr) - cmd.PersistentFlags().StringVarP(&webhookName, "webhook-name", "", "", webhookNameHelpStr) - cmd.PersistentFlags().BoolVar(&autoInjectNamespaces, "auto-inject-namespaces", false, autoInjectNamespacesHelpStr) - _ = cmd.MarkPersistentFlagRequired("revision") - - return cmd -} - -func tagGenerateCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "generate ", - Short: "Generate configuration for a revision tag to stdout", - Long: `Create a revision tag and output to the command's stdout. Tag an Istio control plane revision for use with namespace istio.io/rev -injection labels.`, - Example: ` # Create a revision tag from the "1-8-0" revision - istioctl tag generate prod --revision 1-8-0 > tag.yaml - - # Apply the tag to cluster - kubectl apply -f tag.yaml - - # Point namespace "test-ns" at the revision pointed to by the "prod" revision tag - kubectl label ns test-ns istio.io/rev=prod - - # Rollout namespace "test-ns" to update workloads to the "1-8-0" revision - kubectl rollout restart deployments -n test-ns -`, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - return fmt.Errorf("must provide a tag for modification") - } - if len(args) > 1 { - return fmt.Errorf("must provide a single tag for creation") - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - client, err := kubeClient(kubeconfig, configContext) - if err != nil { - return fmt.Errorf("failed to create Kubernetes client: %v", err) - } - - return setTag(context.Background(), client, args[0], revision, istioNamespace, true, cmd.OutOrStdout(), cmd.OutOrStderr()) - }, - } - - cmd.PersistentFlags().BoolVar(&overwrite, "overwrite", false, overrideHelpStr) - cmd.PersistentFlags().StringVarP(&manifestsPath, "manifests", "d", "", mesh.ManifestsFlagHelpStr) - cmd.PersistentFlags().BoolVarP(&skipConfirmation, "skip-confirmation", "y", false, skipConfirmationFlagHelpStr) - cmd.PersistentFlags().StringVarP(&revision, "revision", "r", "", revisionHelpStr) - cmd.PersistentFlags().StringVarP(&webhookName, "webhook-name", "", "", webhookNameHelpStr) - cmd.PersistentFlags().BoolVar(&autoInjectNamespaces, "auto-inject-namespaces", false, autoInjectNamespacesHelpStr) - _ = cmd.MarkPersistentFlagRequired("revision") - - return cmd -} - -func tagListCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "list", - Short: "List existing revision tags", - Example: "istioctl tag list", - Aliases: []string{"show"}, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) != 0 { - return fmt.Errorf("tag list command does not accept arguments") - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - client, err := kubeClient(kubeconfig, configContext) - if err != nil { - return fmt.Errorf("failed to create Kubernetes client: %v", err) - } - return listTags(context.Background(), client.Kube(), cmd.OutOrStdout()) - }, - } - - return cmd -} - -func tagRemoveCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "remove ", - Short: "Remove Istio control plane revision tag", - Long: `Remove Istio control plane revision tag. - -Removing a revision tag should be done with care. Removing a revision tag will disrupt sidecar injection in namespaces -that reference the tag in an "istio.io/rev" label. Verify that there are no remaining namespaces referencing a -revision tag before removing using the "istioctl tag list" command. -`, - Example: ` # Remove the revision tag "prod" - istioctl tag remove prod -`, - Aliases: []string{"delete"}, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - return fmt.Errorf("must provide a tag for removal") - } - if len(args) > 1 { - return fmt.Errorf("must provide a single tag for removal") - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - client, err := kubeClient(kubeconfig, configContext) - if err != nil { - return fmt.Errorf("failed to create Kubernetes client: %v", err) - } - - return removeTag(context.Background(), client.Kube(), args[0], skipConfirmation, cmd.OutOrStdout()) - }, - } - - cmd.PersistentFlags().BoolVarP(&skipConfirmation, "skip-confirmation", "y", false, skipConfirmationFlagHelpStr) - return cmd -} - -// setTag creates or modifies a revision tag. -func setTag(ctx context.Context, kubeClient kube.ExtendedClient, tagName, revision, istioNS string, generate bool, w, stderr io.Writer) error { - opts := &tag.GenerateOptions{ - Tag: tagName, - Revision: revision, - WebhookName: webhookName, - ManifestsPath: manifestsPath, - Generate: generate, - Overwrite: overwrite, - AutoInjectNamespaces: autoInjectNamespaces, - } - tagWhYAML, err := tag.Generate(ctx, kubeClient, opts, istioNS) - if err != nil { - return err - } - // Check the newly generated webhook does not conflict with existing ones. - resName := webhookName - if resName == "" { - resName = fmt.Sprintf("%s-%s", "istio-revision-tag", tagName) - } - if err := analyzeWebhook(resName, tagWhYAML, revision, kubeClient.RESTConfig()); err != nil { - // if we have a conflict, we will fail. If --skip-confirmation is set, we will continue with a - // warning; when actually applying we will also confirm to ensure the user does not see the - // warning *after* it has applied - if !skipConfirmation { - _, _ = stderr.Write([]byte(err.Error())) - if !generate { - if !confirm("Apply anyways? [y/N]", w) { - return nil - } - } - } - } - - if generate { - _, err := w.Write([]byte(tagWhYAML)) - if err != nil { - return err - } - return nil - } - - if err := tag.Create(kubeClient, tagWhYAML); err != nil { - return fmt.Errorf("failed to apply tag webhook MutatingWebhookConfiguration to cluster: %v", err) - } - fmt.Fprintf(w, tagCreatedStr, tagName, revision, tagName) - return nil -} - -func analyzeWebhook(name, wh, revision string, config *rest.Config) error { - sa := local.NewSourceAnalyzer(analysis.Combine("webhook", &webhook.Analyzer{}), - resource.Namespace(selectedNamespace), resource.Namespace(istioNamespace), nil, true, analysisTimeout) - if err := sa.AddReaderKubeSource([]local.ReaderSource{{Name: "", Reader: strings.NewReader(wh)}}); err != nil { - return err - } - k, err := kube.NewClient(kube.NewClientConfigForRestConfig(config)) - if err != nil { - return err - } - sa.AddRunningKubeSourceWithRevision(k, revision) - res, err := sa.Analyze(make(chan struct{})) - if err != nil { - return err - } - relevantMessages := diag.Messages{} - for _, msg := range res.Messages.FilterOutLowerThan(diag.Error) { - if msg.Resource.Metadata.FullName.Name == resource.LocalName(name) { - relevantMessages = append(relevantMessages, msg) - } - } - if len(relevantMessages) > 0 { - o, err := formatting.Print(relevantMessages, formatting.LogFormat, colorize) - if err != nil { - return err - } - // nolint - return fmt.Errorf("creating tag would conflict, pass --skip-confirmation to proceed:\n%v\n", o) - } - return nil -} - -// removeTag removes an existing revision tag. -func removeTag(ctx context.Context, kubeClient kubernetes.Interface, tagName string, skipConfirmation bool, w io.Writer) error { - webhooks, err := tag.GetWebhooksWithTag(ctx, kubeClient, tagName) - if err != nil { - return fmt.Errorf("failed to retrieve tag with name %s: %v", tagName, err) - } - if len(webhooks) == 0 { - return fmt.Errorf("cannot remove tag %q: cannot find MutatingWebhookConfiguration for tag", tagName) - } - - taggedNamespaces, err := tag.GetNamespacesWithTag(ctx, kubeClient, tagName) - if err != nil { - return fmt.Errorf("failed to retrieve namespaces dependent on tag %q", tagName) - } - // warn user if deleting a tag that still has namespaces pointed to it - if len(taggedNamespaces) > 0 && !skipConfirmation { - if !confirm(buildDeleteTagConfirmation(tagName, taggedNamespaces), w) { - fmt.Fprintf(w, "Aborting operation.\n") - return nil - } - } - - // proceed with webhook deletion - err = tag.DeleteTagWebhooks(ctx, kubeClient, tagName) - if err != nil { - return fmt.Errorf("failed to delete Istio revision tag MutatingConfigurationWebhook: %v", err) - } - - fmt.Fprintf(w, "Revision tag %s removed\n", tagName) - return nil -} - -// listTags lists existing revision. -func listTags(ctx context.Context, kubeClient kubernetes.Interface, writer io.Writer) error { - tagWebhooks, err := tag.GetTagWebhooks(ctx, kubeClient) - if err != nil { - return fmt.Errorf("failed to retrieve revision tags: %v", err) - } - if len(tagWebhooks) == 0 { - fmt.Fprintf(writer, "No Istio revision tag MutatingWebhookConfigurations to list\n") - return nil - } - tags := make([]tagDescription, 0) - for _, wh := range tagWebhooks { - tagName, err := tag.GetWebhookTagName(wh) - if err != nil { - return fmt.Errorf("error parsing tag name from webhook %q: %v", wh.Name, err) - } - tagRevision, err := tag.GetWebhookRevision(wh) - if err != nil { - return fmt.Errorf("error parsing revision from webhook %q: %v", wh.Name, err) - } - tagNamespaces, err := tag.GetNamespacesWithTag(ctx, kubeClient, tagName) - if err != nil { - return fmt.Errorf("error retrieving namespaces for tag %q: %v", tagName, err) - } - tagDesc := tagDescription{ - Tag: tagName, - Revision: tagRevision, - Namespaces: tagNamespaces, - } - tags = append(tags, tagDesc) - } - - switch revArgs.output { - case jsonFormat: - return printJSON(writer, tags) - case tableFormat: - default: - return fmt.Errorf("unknown format: %s", revArgs.output) - } - w := new(tabwriter.Writer).Init(writer, 0, 8, 1, ' ', 0) - fmt.Fprintln(w, "TAG\tREVISION\tNAMESPACES") - for _, t := range tags { - fmt.Fprintf(w, "%s\t%s\t%s\n", t.Tag, t.Revision, strings.Join(t.Namespaces, ",")) - } - - return w.Flush() -} - -// buildDeleteTagConfirmation takes a list of webhooks and creates a message prompting confirmation for their deletion. -func buildDeleteTagConfirmation(tag string, taggedNamespaces []string) string { - var sb strings.Builder - base := fmt.Sprintf("Caution, found %d namespace(s) still injected by tag %q:", len(taggedNamespaces), tag) - sb.WriteString(base) - for _, ns := range taggedNamespaces { - sb.WriteString(" " + ns) - } - sb.WriteString("\nProceed with operation? [y/N]") - - return sb.String() -} - -// confirm waits for a user to confirm with the supplied message. -func confirm(msg string, w io.Writer) bool { - fmt.Fprintf(w, "%s ", msg) - - var response string - _, err := fmt.Scanln(&response) - if err != nil { - return false - } - response = strings.ToUpper(response) - return response == "Y" || response == "YES" -} diff --git a/istioctl/cmd/tag_test.go b/istioctl/cmd/tag_test.go deleted file mode 100644 index fe195b554..000000000 --- a/istioctl/cmd/tag_test.go +++ /dev/null @@ -1,357 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "bytes" - "context" - "fmt" - "strings" - "testing" -) - -import ( - "istio.io/api/label" - admit_v1 "k8s.io/api/admissionregistration/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes/fake" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/tag" - "github.com/apache/dubbo-go-pixiu/operator/pkg/helmreconciler" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -const istioInjectionWebhookSuffix = "sidecar-injector.istio.io" - -var revisionCanonicalWebhook = admit_v1.MutatingWebhookConfiguration{ - ObjectMeta: metav1.ObjectMeta{ - Name: "istio-sidecar-injector-revision", - Labels: map[string]string{label.IoIstioRev.Name: "revision"}, - }, - Webhooks: []admit_v1.MutatingWebhook{ - { - Name: fmt.Sprintf("namespace.%s", istioInjectionWebhookSuffix), - ClientConfig: admit_v1.WebhookClientConfig{ - Service: &admit_v1.ServiceReference{ - Namespace: "default", - Name: "istiod-revision", - }, - CABundle: []byte("ca"), - }, - }, - { - Name: fmt.Sprintf("object.%s", istioInjectionWebhookSuffix), - ClientConfig: admit_v1.WebhookClientConfig{ - Service: &admit_v1.ServiceReference{ - Namespace: "default", - Name: "istiod-revision", - }, - CABundle: []byte("ca"), - }, - }, - }, -} - -func TestTagList(t *testing.T) { - tcs := []struct { - name string - webhooks admit_v1.MutatingWebhookConfigurationList - namespaces corev1.NamespaceList - outputMatches []string - outputExcludes []string - error string - }{ - { - name: "TestBasicTag", - webhooks: admit_v1.MutatingWebhookConfigurationList{ - Items: []admit_v1.MutatingWebhookConfiguration{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "istio-revision-tag-sample", - Labels: map[string]string{ - tag.IstioTagLabel: "sample", - label.IoIstioRev.Name: "sample-revision", - helmreconciler.IstioComponentLabelStr: "Pilot", - }, - }, - }, - }, - }, - namespaces: corev1.NamespaceList{}, - outputMatches: []string{"sample", "sample-revision"}, - outputExcludes: []string{}, - error: "", - }, - { - name: "TestNonTagWebhooksExcluded", - webhooks: admit_v1.MutatingWebhookConfigurationList{ - Items: []admit_v1.MutatingWebhookConfiguration{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "istio-revision-test", - Labels: map[string]string{label.IoIstioRev.Name: "test"}, - }, - }, - }, - }, - namespaces: corev1.NamespaceList{}, - outputMatches: []string{}, - outputExcludes: []string{"test"}, - error: "", - }, - { - name: "TestNamespacesIncluded", - webhooks: admit_v1.MutatingWebhookConfigurationList{ - Items: []admit_v1.MutatingWebhookConfiguration{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "istio-revision-test", - Labels: map[string]string{ - label.IoIstioRev.Name: "revision", - tag.IstioTagLabel: "test", - }, - }, - }, - }, - }, - namespaces: corev1.NamespaceList{ - Items: []corev1.Namespace{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "dependent", - Labels: map[string]string{label.IoIstioRev.Name: "test"}, - }, - }, - }, - }, - outputMatches: []string{"test", "revision", "dependent"}, - outputExcludes: []string{}, - error: "", - }, - } - - for _, tc := range tcs { - t.Run(tc.name, func(t *testing.T) { - var out bytes.Buffer - client := fake.NewSimpleClientset(tc.webhooks.DeepCopyObject(), tc.namespaces.DeepCopyObject()) - revArgs.output = jsonFormat - err := listTags(context.Background(), client, &out) - if tc.error == "" && err != nil { - t.Fatalf("expected no error, got %v", err) - } - if tc.error != "" { - if err == nil { - t.Fatalf("expected error to include \"%s\" but got none", tc.error) - } - if !strings.Contains(err.Error(), tc.error) { - t.Fatalf("expected \"%s\" in error, got %v", tc.error, err) - } - } - - commandOutput := out.String() - for _, s := range tc.outputMatches { - if !strings.Contains(commandOutput, s) { - t.Fatalf("expected \"%s\" in command output, got %s", s, commandOutput) - } - } - for _, s := range tc.outputExcludes { - if strings.Contains(commandOutput, s) { - t.Fatalf("expected \"%s\" in command output, got %s", s, commandOutput) - } - } - }) - } -} - -func TestRemoveTag(t *testing.T) { - tcs := []struct { - name string - tag string - webhooksBefore admit_v1.MutatingWebhookConfigurationList - webhooksAfter admit_v1.MutatingWebhookConfigurationList - namespaces corev1.NamespaceList - outputMatches []string - skipConfirmation bool - error string - }{ - { - name: "TestSimpleRemove", - tag: "sample", - webhooksBefore: admit_v1.MutatingWebhookConfigurationList{ - Items: []admit_v1.MutatingWebhookConfiguration{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "istio-revision-tag-sample", - Labels: map[string]string{tag.IstioTagLabel: "sample"}, - }, - }, - }, - }, - webhooksAfter: admit_v1.MutatingWebhookConfigurationList{}, - namespaces: corev1.NamespaceList{}, - outputMatches: []string{}, - skipConfirmation: true, - error: "", - }, - { - name: "TestWrongTagLabelNotRemoved", - tag: "sample", - webhooksBefore: admit_v1.MutatingWebhookConfigurationList{ - Items: []admit_v1.MutatingWebhookConfiguration{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "istio-revision-tag-wrong", - Labels: map[string]string{tag.IstioTagLabel: "wrong"}, - }, - }, - }, - }, - webhooksAfter: admit_v1.MutatingWebhookConfigurationList{ - Items: []admit_v1.MutatingWebhookConfiguration{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "istio-revision-tag-wrong", - Labels: map[string]string{tag.IstioTagLabel: "wrong"}, - }, - }, - }, - }, - namespaces: corev1.NamespaceList{}, - outputMatches: []string{}, - skipConfirmation: true, - error: "cannot remove tag \"sample\"", - }, - { - name: "TestDeleteTagWithDependentNamespace", - tag: "match", - webhooksBefore: admit_v1.MutatingWebhookConfigurationList{ - Items: []admit_v1.MutatingWebhookConfiguration{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "istio-revision-tag-match", - Labels: map[string]string{tag.IstioTagLabel: "match"}, - }, - }, - }, - }, - webhooksAfter: admit_v1.MutatingWebhookConfigurationList{ - Items: []admit_v1.MutatingWebhookConfiguration{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "istio-revision-tag-match", - Labels: map[string]string{tag.IstioTagLabel: "match"}, - }, - }, - }, - }, - namespaces: corev1.NamespaceList{ - Items: []corev1.Namespace{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "dependent", - Labels: map[string]string{label.IoIstioRev.Name: "match"}, - }, - }, - }, - }, - outputMatches: []string{"Caution, found 1 namespace(s) still injected by tag \"match\": dependent"}, - skipConfirmation: false, - error: "", - }, - } - - for _, tc := range tcs { - t.Run(tc.name, func(t *testing.T) { - var out bytes.Buffer - client := fake.NewSimpleClientset(tc.webhooksBefore.DeepCopyObject(), tc.namespaces.DeepCopyObject()) - err := removeTag(context.Background(), client, tc.tag, tc.skipConfirmation, &out) - if tc.error == "" && err != nil { - t.Fatalf("expected no error, got %v", err) - } - if tc.error != "" { - if err == nil { - t.Fatalf("expected error to include \"%s\" but got none", tc.error) - } - if !strings.Contains(err.Error(), tc.error) { - t.Fatalf("expected \"%s\" in error, got %v", tc.error, err) - } - } - - commandOutput := out.String() - for _, s := range tc.outputMatches { - if !strings.Contains(commandOutput, s) { - t.Fatalf("expected \"%s\" in command output, got %s", s, commandOutput) - } - } - - // check mutating webhooks after run - webhooksAfter, _ := client.AdmissionregistrationV1().MutatingWebhookConfigurations().List(context.Background(), metav1.ListOptions{}) - if len(webhooksAfter.Items) != len(tc.webhooksAfter.Items) { - t.Fatalf("expected %d after running, got %d", len(tc.webhooksAfter.Items), len(webhooksAfter.Items)) - } - }) - } -} - -func TestSetTagErrors(t *testing.T) { - tcs := []struct { - name string - tag string - revision string - webhooksBefore admit_v1.MutatingWebhookConfigurationList - namespaces corev1.NamespaceList - outputMatches []string - error string - }{ - { - name: "TestErrorWhenRevisionWithNameCollision", - tag: "revision", - revision: "revision", - webhooksBefore: admit_v1.MutatingWebhookConfigurationList{ - Items: []admit_v1.MutatingWebhookConfiguration{revisionCanonicalWebhook}, - }, - namespaces: corev1.NamespaceList{}, - outputMatches: []string{}, - error: "cannot create revision tag \"revision\"", - }, - } - - for _, tc := range tcs { - t.Run(tc.name, func(t *testing.T) { - var out bytes.Buffer - - client := fake.NewSimpleClientset(tc.webhooksBefore.DeepCopyObject(), tc.namespaces.DeepCopyObject()) - mockClient := kube.MockClient{ - Interface: client, - } - skipConfirmation = true - err := setTag(context.Background(), mockClient, tc.tag, tc.revision, "dubbo-system", false, &out, nil) - if tc.error == "" && err != nil { - t.Fatalf("expected no error, got %v", err) - } - if tc.error != "" { - if err == nil { - t.Fatalf("expected error to include \"%s\" but got none", tc.error) - } - if !strings.Contains(err.Error(), tc.error) { - t.Fatalf("expected \"%s\" in error, got %v", tc.error, err) - } - } - }) - } -} diff --git a/istioctl/cmd/testdata/deployment/hello.yaml b/istioctl/cmd/testdata/deployment/hello.yaml deleted file mode 100644 index a7cff42d8..000000000 --- a/istioctl/cmd/testdata/deployment/hello.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/istioctl/cmd/testdata/deployment/hello.yaml.injected b/istioctl/cmd/testdata/deployment/hello.yaml.injected deleted file mode 100644 index 7e3ac6c3b..000000000 --- a/istioctl/cmd/testdata/deployment/hello.yaml.injected +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":null,"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - image: docker.io/istio/proxy_debug:unittest - name: istio-proxy - resources: {} - initContainers: - - image: docker.io/istio/proxy_init:unittest-test - name: istio-init - resources: {} -status: {} ---- diff --git a/istioctl/cmd/testdata/deployment/hello.yaml.iop.injected b/istioctl/cmd/testdata/deployment/hello.yaml.iop.injected deleted file mode 100644 index 0026b986e..000000000 --- a/istioctl/cmd/testdata/deployment/hello.yaml.iop.injected +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":null,"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - image: docker.io/istio/proxy_debug:unittest - name: istio-proxy - resources: {} - initContainers: - - image: docker.io/istio/proxy_init:unittest-testiop - name: istio-init - resources: {} -status: {} ---- diff --git a/istioctl/cmd/testdata/describe/http_config.json b/istioctl/cmd/testdata/describe/http_config.json deleted file mode 100644 index b245b4727..000000000 --- a/istioctl/cmd/testdata/describe/http_config.json +++ /dev/null @@ -1,199 +0,0 @@ -{ - "configs": [ - { - "@type": "type.googleapis.com/envoy.admin.v3.RoutesConfigDump", - "dynamic_route_configs": [ - { - "version_info": "2022-03-04T10:22:24Z/54", - "route_config": { - "@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", - "name": "http.8080", - "virtual_hosts": [ - { - "name": "*:80", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "path": "/productpage", - "case_sensitive": true - }, - "route": { - "cluster": "outbound|9080||productpage.default.svc.cluster.local", - "timeout": "0s", - "retry_policy": { - "retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes", - "num_retries": 2, - "retry_host_predicate": [ - { - "name": "envoy.retry_host_predicates.previous_hosts" - } - ], - "host_selection_retry_max_attempts": "5", - "retriable_status_codes": [ - 503 - ] - }, - "max_grpc_timeout": "0s" - }, - "metadata": { - "filter_metadata": { - "istio": { - "config": "/apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/bookinfo" - } - } - }, - "decorator": { - "operation": "productpage.default.svc.cluster.local:9080/productpage" - } - }, - { - "match": { - "prefix": "/static", - "case_sensitive": true - }, - "route": { - "cluster": "outbound|9080||productpage.default.svc.cluster.local", - "timeout": "0s", - "retry_policy": { - "retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes", - "num_retries": 2, - "retry_host_predicate": [ - { - "name": "envoy.retry_host_predicates.previous_hosts" - } - ], - "host_selection_retry_max_attempts": "5", - "retriable_status_codes": [ - 503 - ] - }, - "max_grpc_timeout": "0s" - }, - "metadata": { - "filter_metadata": { - "istio": { - "config": "/apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/bookinfo" - } - } - }, - "decorator": { - "operation": "productpage.default.svc.cluster.local:9080/static*" - } - }, - { - "match": { - "path": "/login", - "case_sensitive": true - }, - "route": { - "cluster": "outbound|9080||productpage.default.svc.cluster.local", - "timeout": "0s", - "retry_policy": { - "retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes", - "num_retries": 2, - "retry_host_predicate": [ - { - "name": "envoy.retry_host_predicates.previous_hosts" - } - ], - "host_selection_retry_max_attempts": "5", - "retriable_status_codes": [ - 503 - ] - }, - "max_grpc_timeout": "0s" - }, - "metadata": { - "filter_metadata": { - "istio": { - "config": "/apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/bookinfo" - } - } - }, - "decorator": { - "operation": "productpage.default.svc.cluster.local:9080/login" - } - }, - { - "match": { - "path": "/logout", - "case_sensitive": true - }, - "route": { - "cluster": "outbound|9080||productpage.default.svc.cluster.local", - "timeout": "0s", - "retry_policy": { - "retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes", - "num_retries": 2, - "retry_host_predicate": [ - { - "name": "envoy.retry_host_predicates.previous_hosts" - } - ], - "host_selection_retry_max_attempts": "5", - "retriable_status_codes": [ - 503 - ] - }, - "max_grpc_timeout": "0s" - }, - "metadata": { - "filter_metadata": { - "istio": { - "config": "/apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/bookinfo" - } - } - }, - "decorator": { - "operation": "productpage.default.svc.cluster.local:9080/logout" - } - }, - { - "match": { - "prefix": "/api/v1/products", - "case_sensitive": true - }, - "route": { - "cluster": "outbound|9080||productpage.default.svc.cluster.local", - "timeout": "0s", - "retry_policy": { - "retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes", - "num_retries": 2, - "retry_host_predicate": [ - { - "name": "envoy.retry_host_predicates.previous_hosts" - } - ], - "host_selection_retry_max_attempts": "5", - "retriable_status_codes": [ - 503 - ] - }, - "max_grpc_timeout": "0s" - }, - "metadata": { - "filter_metadata": { - "istio": { - "config": "/apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/bookinfo" - } - } - }, - "decorator": { - "operation": "productpage.default.svc.cluster.local:9080/api/v1/products*" - } - } - ], - "include_request_attempt_count": true - } - ], - "validate_clusters": false - }, - "last_updated": "2022-03-04T10:22:24.737Z" - } - ] - } - ] -} diff --git a/istioctl/cmd/testdata/describe/tls_config.json b/istioctl/cmd/testdata/describe/tls_config.json deleted file mode 100644 index a31fc3085..000000000 --- a/istioctl/cmd/testdata/describe/tls_config.json +++ /dev/null @@ -1,199 +0,0 @@ -{ - "configs": [ - { - "@type": "type.googleapis.com/envoy.admin.v3.RoutesConfigDump", - "dynamic_route_configs": [ - { - "version_info": "2022-03-04T07:18:48Z/22", - "route_config": { - "@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", - "name": "https.443.https.bookinfo-gateway.default", - "virtual_hosts": [ - { - "name": "*:443", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "path": "/productpage", - "case_sensitive": true - }, - "route": { - "cluster": "outbound|9080||productpage.default.svc.cluster.local", - "timeout": "0s", - "retry_policy": { - "retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes", - "num_retries": 2, - "retry_host_predicate": [ - { - "name": "envoy.retry_host_predicates.previous_hosts" - } - ], - "host_selection_retry_max_attempts": "5", - "retriable_status_codes": [ - 503 - ] - }, - "max_grpc_timeout": "0s" - }, - "metadata": { - "filter_metadata": { - "istio": { - "config": "/apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/bookinfo" - } - } - }, - "decorator": { - "operation": "productpage.default.svc.cluster.local:9080/productpage" - } - }, - { - "match": { - "prefix": "/static", - "case_sensitive": true - }, - "route": { - "cluster": "outbound|9080||productpage.default.svc.cluster.local", - "timeout": "0s", - "retry_policy": { - "retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes", - "num_retries": 2, - "retry_host_predicate": [ - { - "name": "envoy.retry_host_predicates.previous_hosts" - } - ], - "host_selection_retry_max_attempts": "5", - "retriable_status_codes": [ - 503 - ] - }, - "max_grpc_timeout": "0s" - }, - "metadata": { - "filter_metadata": { - "istio": { - "config": "/apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/bookinfo" - } - } - }, - "decorator": { - "operation": "productpage.default.svc.cluster.local:9080/static*" - } - }, - { - "match": { - "path": "/login", - "case_sensitive": true - }, - "route": { - "cluster": "outbound|9080||productpage.default.svc.cluster.local", - "timeout": "0s", - "retry_policy": { - "retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes", - "num_retries": 2, - "retry_host_predicate": [ - { - "name": "envoy.retry_host_predicates.previous_hosts" - } - ], - "host_selection_retry_max_attempts": "5", - "retriable_status_codes": [ - 503 - ] - }, - "max_grpc_timeout": "0s" - }, - "metadata": { - "filter_metadata": { - "istio": { - "config": "/apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/bookinfo" - } - } - }, - "decorator": { - "operation": "productpage.default.svc.cluster.local:9080/login" - } - }, - { - "match": { - "path": "/logout", - "case_sensitive": true - }, - "route": { - "cluster": "outbound|9080||productpage.default.svc.cluster.local", - "timeout": "0s", - "retry_policy": { - "retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes", - "num_retries": 2, - "retry_host_predicate": [ - { - "name": "envoy.retry_host_predicates.previous_hosts" - } - ], - "host_selection_retry_max_attempts": "5", - "retriable_status_codes": [ - 503 - ] - }, - "max_grpc_timeout": "0s" - }, - "metadata": { - "filter_metadata": { - "istio": { - "config": "/apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/bookinfo" - } - } - }, - "decorator": { - "operation": "productpage.default.svc.cluster.local:9080/logout" - } - }, - { - "match": { - "prefix": "/api/v1/products", - "case_sensitive": true - }, - "route": { - "cluster": "outbound|9080||productpage.default.svc.cluster.local", - "timeout": "0s", - "retry_policy": { - "retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes", - "num_retries": 2, - "retry_host_predicate": [ - { - "name": "envoy.retry_host_predicates.previous_hosts" - } - ], - "host_selection_retry_max_attempts": "5", - "retriable_status_codes": [ - 503 - ] - }, - "max_grpc_timeout": "0s" - }, - "metadata": { - "filter_metadata": { - "istio": { - "config": "/apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/bookinfo" - } - } - }, - "decorator": { - "operation": "productpage.default.svc.cluster.local:9080/api/v1/products*" - } - } - ], - "include_request_attempt_count": true - } - ], - "validate_clusters": false - }, - "last_updated": "2022-03-04T07:18:49.007Z" - } - ] - } - ] -} diff --git a/istioctl/cmd/testdata/inject-config-inline-iop.yaml b/istioctl/cmd/testdata/inject-config-inline-iop.yaml deleted file mode 100644 index 7c93251dc..000000000 --- a/istioctl/cmd/testdata/inject-config-inline-iop.yaml +++ /dev/null @@ -1,7 +0,0 @@ -spec: - initContainers: - - name: istio-init - image: docker.io/istio/proxy_init:unittest-{{.Values.global.tag}} - containers: - - name: istio-proxy - image: docker.io/istio/proxy_debug:unittest diff --git a/istioctl/cmd/testdata/inject-config-inline.yaml b/istioctl/cmd/testdata/inject-config-inline.yaml deleted file mode 100644 index 903458624..000000000 --- a/istioctl/cmd/testdata/inject-config-inline.yaml +++ /dev/null @@ -1,7 +0,0 @@ -spec: - initContainers: - - name: istio-init - image: docker.io/istio/proxy_init:unittest-{{.Values.global.suffix}} - containers: - - name: istio-proxy - image: docker.io/istio/proxy_debug:unittest diff --git a/istioctl/cmd/testdata/inject-config-iop.yaml b/istioctl/cmd/testdata/inject-config-iop.yaml deleted file mode 100644 index 32442cb90..000000000 --- a/istioctl/cmd/testdata/inject-config-iop.yaml +++ /dev/null @@ -1,9 +0,0 @@ -templates: - sidecar: |- - spec: - initContainers: - - name: istio-init - image: docker.io/istio/proxy_init:unittest-{{.Values.global.tag}} - containers: - - name: istio-proxy - image: docker.io/istio/proxy_debug:unittest diff --git a/istioctl/cmd/testdata/inject-config.yaml b/istioctl/cmd/testdata/inject-config.yaml deleted file mode 100644 index 9533e664f..000000000 --- a/istioctl/cmd/testdata/inject-config.yaml +++ /dev/null @@ -1,9 +0,0 @@ -templates: - sidecar: |- - spec: - initContainers: - - name: istio-init - image: docker.io/istio/proxy_init:unittest-{{.Values.global.suffix}} - containers: - - name: istio-proxy - image: docker.io/istio/proxy_debug:unittest diff --git a/istioctl/cmd/testdata/inject-values.yaml b/istioctl/cmd/testdata/inject-values.yaml deleted file mode 100644 index 2b9efda26..000000000 --- a/istioctl/cmd/testdata/inject-values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -global: - suffix: test diff --git a/istioctl/cmd/testdata/istio-operator.yaml b/istioctl/cmd/testdata/istio-operator.yaml deleted file mode 100644 index efbfc5516..000000000 --- a/istioctl/cmd/testdata/istio-operator.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -metadata: - name: test - namespace: dubbo-system -spec: - meshConfig: - # Set enableTracing to false to disable request tracing. - enableTracing: true - # This is the ingress service name, update if you used a different name - ingressService: istio-ingress - connectTimeout: 1s - defaultConfig: - ### ADVANCED SETTINGS ############# - # Where should envoy's configuration be stored in the istio-proxy container - configPath: "/etc/istio/proxy" - binaryPath: "/usr/local/bin/envoy" - # The pseudo service name used for Envoy. - serviceCluster: istio-proxy - values: - global: - tag: testiop diff --git a/istioctl/cmd/testdata/mesh-config.yaml b/istioctl/cmd/testdata/mesh-config.yaml deleted file mode 100644 index bf95b8dd8..000000000 --- a/istioctl/cmd/testdata/mesh-config.yaml +++ /dev/null @@ -1,55 +0,0 @@ -# Set enableTracing to false to disable request tracing. -enableTracing: true -# This is the ingress service name, update if you used a different name -ingressService: istio-ingress -# -defaultConfig: - # NOTE: If you change any values in this section, make sure to make - # the same changes in start up args in istio-ingress pods. - # - # TCP connection timeout between Envoy & the application, and between Envoys. - connectTimeout: 1s - # - ### ADVANCED SETTINGS ############# - # Where should envoy's configuration be stored in the istio-proxy container - configPath: "/etc/istio/proxy" - binaryPath: "/usr/local/bin/envoy" - # The pseudo service name used for Envoy. - serviceCluster: istio-proxy - # These settings that determine how long an old Envoy - # process should be kept alive after an occasional reload. - drainDuration: 2s - parentShutdownDuration: 3s - # - # The mode used to redirect inbound connections to Envoy. This setting - # has no effect on outbound traffic: iptables REDIRECT is always used for - # outbound connections. - # If "REDIRECT", use iptables REDIRECT to NAT and redirect to Envoy. - # The "REDIRECT" mode loses source addresses during redirection. - # If "TPROXY", use iptables TPROXY to redirect to Envoy. - # The "TPROXY" mode preserves both the source and destination IP - # addresses and ports, so that they can be used for advanced filtering - # and manipulation. - # The "TPROXY" mode also configures the sidecar to run with the - # CAP_NET_ADMIN capability, which is required to use TPROXY. - #interceptionMode: REDIRECT - # - # Port where Envoy listens (on local host) for admin commands - # You can exec into the istio-proxy container in a pod and - # curl the admin port (curl http://localhost:15000/) to obtain - # diagnostic information from Envoy. See - # https://lyft.github.io/envoy/docs/operations/admin.html - # for more details - proxyAdminPort: 15000 - # - # Zipkin trace collector - zipkinAddress: "" - # - # Statsd metrics collector converts statsd metrics into Prometheus metrics. - statsdUdpAddress: "" - # - # Mutual TLS authentication between sidecars and istio control plane. - controlPlaneAuthPolicy: NONE - # - # Address where istio Pilot service is running - discoveryAddress: istio-pilot:15007 diff --git a/istioctl/cmd/testdata/uninject/cronjob-with-app.yaml b/istioctl/cmd/testdata/uninject/cronjob-with-app.yaml deleted file mode 100644 index ba228e19a..000000000 --- a/istioctl/cmd/testdata/uninject/cronjob-with-app.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: batch/v2alpha1 -kind: CronJob -metadata: - creationTimestamp: null - name: hello -spec: - jobTemplate: - metadata: - annotations: - sidecar.istio.io/inject: "false" - creationTimestamp: null - labels: - app: hello - spec: - template: - metadata: - creationTimestamp: null - spec: - containers: - - args: - - /bin/sh - - -c - - date; echo Hello from the Kubernetes cluster - image: busybox - name: hello - resources: {} - restartPolicy: OnFailure - schedule: '*/1 * * * *' -status: {} ---- diff --git a/istioctl/cmd/testdata/uninject/cronjob-with-app.yaml.injected b/istioctl/cmd/testdata/uninject/cronjob-with-app.yaml.injected deleted file mode 100644 index beace2fe1..000000000 --- a/istioctl/cmd/testdata/uninject/cronjob-with-app.yaml.injected +++ /dev/null @@ -1,142 +0,0 @@ -apiVersion: batch/v2alpha1 -kind: CronJob -metadata: - creationTimestamp: null - name: hello -spec: - jobTemplate: - metadata: - annotations: - sidecar.istio.io/status: '{"version":"","initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-certs"],"imagePullSecrets":null}' - creationTimestamp: null - labels: - app: hello - spec: - template: - metadata: - creationTimestamp: null - spec: - containers: - - args: - - /bin/sh - - -c - - date; echo Hello from the Kubernetes cluster - image: busybox - name: hello - resources: {} - - args: - - proxy - - sidecar - - --configPath - - /etc/istio/proxy - - --binaryPath - - /usr/local/bin/envoy - - --serviceCluster - - hello.default - - --drainDuration - - 45s - - --parentShutdownDuration - - 1m0s - - --discoveryAddress - - istio-pilot:15010 - - --connectTimeout - - 1s - - --statsdUdpAddress - - "" - - --proxyAdminPort - - "15000" - - --controlPlaneAuthPolicy - - NONE - - --concurrency - - "1" - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: ISTIO_META_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - image: docker.io/istio/proxyv2:unittest - imagePullPolicy: IfNotPresent - name: istio-proxy - resources: - requests: - cpu: 10m - memory: 30Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - initContainers: - - args: - - -p - - "15001" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - "" - - -d - - "" - image: docker.io/istio/proxy_init:unittest - imagePullPolicy: IfNotPresent - name: istio-init - resources: {} - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - restartPolicy: OnFailure - volumes: - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-certs - secret: - optional: true - secretName: istio.default - schedule: '*/1 * * * *' -status: {} ---- diff --git a/istioctl/cmd/testdata/uninject/cronjob.yaml b/istioctl/cmd/testdata/uninject/cronjob.yaml deleted file mode 100644 index cc1cf3d69..000000000 --- a/istioctl/cmd/testdata/uninject/cronjob.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: batch/v2alpha1 -kind: CronJob -metadata: - creationTimestamp: null - name: hellocron -spec: - jobTemplate: - metadata: - annotations: - sidecar.istio.io/inject: "false" - creationTimestamp: null - spec: - template: - metadata: - creationTimestamp: null - spec: - containers: - - args: - - /bin/sh - - -c - - date; echo Hello from the Kubernetes cluster - image: busybox - name: hello - resources: {} - restartPolicy: OnFailure - schedule: '*/1 * * * *' -status: {} ---- diff --git a/istioctl/cmd/testdata/uninject/cronjob.yaml.injected b/istioctl/cmd/testdata/uninject/cronjob.yaml.injected deleted file mode 100644 index d22cf6817..000000000 --- a/istioctl/cmd/testdata/uninject/cronjob.yaml.injected +++ /dev/null @@ -1,170 +0,0 @@ -apiVersion: batch/v2alpha1 -kind: CronJob -metadata: - creationTimestamp: null - name: hellocron -spec: - jobTemplate: - metadata: - annotations: - sidecar.istio.io/interceptionMode: REDIRECT - sidecar.istio.io/status: '{"version":"","initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-certs"],"imagePullSecrets":null}' - traffic.sidecar.istio.io/excludeInboundPorts: "15020" - traffic.sidecar.istio.io/includeOutboundIPRanges: '*' - creationTimestamp: null - spec: - template: - metadata: - creationTimestamp: null - spec: - containers: - - args: - - /bin/sh - - -c - - date; echo Hello from the Kubernetes cluster - image: busybox - name: hello - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --configPath - - /etc/istio/proxy - - --binaryPath - - /usr/local/bin/envoy - - --serviceCluster - - hellocron.default - - --drainDuration - - 45s - - --parentShutdownDuration - - 1m0s - - --discoveryAddress - - istio-pilot:15010 - - --dnsRefreshRate - - 300s - - --connectTimeout - - 1s - - --proxyAdminPort - - "15000" - - --controlPlaneAuthPolicy - - NONE - - --statusPort - - "15020" - - --applicationPorts - - "" - - --concurrency - - "2" - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: ISTIO_META_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: ISTIO_META_CONFIG_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_INCLUDE_INBOUND_PORTS - image: docker.io/istio/proxyv2:unittest - imagePullPolicy: IfNotPresent - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15020 - initialDelaySeconds: 1 - periodSeconds: 2 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - initContainers: - - args: - - -p - - "15001" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - "" - - -d - - "15090,15020" - image: docker.io/istio/proxy_init:unittest - imagePullPolicy: IfNotPresent - name: istio-init - resources: - limits: - cpu: 100m - memory: 50Mi - requests: - cpu: 10m - memory: 10Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - restartPolicy: OnFailure - volumes: - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-certs - secret: - optional: true - secretName: istio.default - schedule: '*/1 * * * *' -status: {} ---- diff --git a/istioctl/cmd/testdata/uninject/daemonset.yaml b/istioctl/cmd/testdata/uninject/daemonset.yaml deleted file mode 100644 index 3fdea63e3..000000000 --- a/istioctl/cmd/testdata/uninject/daemonset.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: apps/v1 -kind: DaemonSet -metadata: - creationTimestamp: null - name: hello -spec: - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - annotations: - sidecar.istio.io/inject: "false" - creationTimestamp: null - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - updateStrategy: {} -status: - currentNumberScheduled: 0 - desiredNumberScheduled: 0 - numberMisscheduled: 0 - numberReady: 0 ---- diff --git a/istioctl/cmd/testdata/uninject/daemonset.yaml.injected b/istioctl/cmd/testdata/uninject/daemonset.yaml.injected deleted file mode 100644 index 0832efb38..000000000 --- a/istioctl/cmd/testdata/uninject/daemonset.yaml.injected +++ /dev/null @@ -1,178 +0,0 @@ -apiVersion: apps/v1 -kind: DaemonSet -metadata: - creationTimestamp: null - name: hello -spec: - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - annotations: - sidecar.istio.io/status: '{"version":"","initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-certs"],"imagePullSecrets":null}' - creationTimestamp: null - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --configPath - - /etc/istio/proxy - - --binaryPath - - /usr/local/bin/envoy - - --serviceCluster - - hello.$(POD_NAMESPACE) - - --drainDuration - - 45s - - --parentShutdownDuration - - 1m0s - - --discoveryAddress - - istio-pilot:15010 - - --dnsRefreshRate - - 300s - - --connectTimeout - - 1s - - --proxyAdminPort - - "15000" - - --controlPlaneAuthPolicy - - NONE - - --statusPort - - "15020" - - --applicationPorts - - "80" - - --concurrency - - "2" - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: ISTIO_META_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: ISTIO_META_CONFIG_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_INCLUDE_INBOUND_PORTS - value: "80" - - name: ISTIO_METAJSON_LABELS - value: | - {"app":"hello","tier":"backend","track":"stable"} - image: docker.io/istio/proxyv2:unittest - imagePullPolicy: IfNotPresent - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15020 - initialDelaySeconds: 1 - periodSeconds: 2 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - initContainers: - - args: - - -p - - "15001" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - "80" - - -d - - "15090,15020" - image: docker.io/istio/proxy_init:unittest - imagePullPolicy: IfNotPresent - name: istio-init - resources: - limits: - cpu: 100m - memory: 50Mi - requests: - cpu: 10m - memory: 10Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - volumes: - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-certs - secret: - optional: true - secretName: istio.default - updateStrategy: {} -status: - currentNumberScheduled: 0 - desiredNumberScheduled: 0 - numberMisscheduled: 0 - numberReady: 0 ---- diff --git a/istioctl/cmd/testdata/uninject/deploymentconfig-app-probe.yaml b/istioctl/cmd/testdata/uninject/deploymentconfig-app-probe.yaml deleted file mode 100644 index f2b2bbba9..000000000 --- a/istioctl/cmd/testdata/uninject/deploymentconfig-app-probe.yaml +++ /dev/null @@ -1,59 +0,0 @@ -apiVersion: apps.openshift.io/v1 -kind: DeploymentConfig -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - revisionHistoryLimit: 2 - strategy: - resources: {} - type: Rolling - template: - metadata: - annotations: - sidecar.istio.io/inject: "false" - creationTimestamp: null - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - timeoutSeconds: 3 - name: hello - ports: - - containerPort: 80 - name: http - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - timeoutSeconds: 3 - resources: {} - test: false - triggers: - - type: ConfigChange - - imageChangeParams: - automatic: true - containerNames: - - helloworld - from: - kind: ImageStreamTag - name: hello-go-gke:1.0 - type: ImageChange -status: - availableReplicas: 0 - latestVersion: 0 - observedGeneration: 0 - replicas: 0 - unavailableReplicas: 0 - updatedReplicas: 0 ---- diff --git a/istioctl/cmd/testdata/uninject/deploymentconfig-app-probe.yaml.injected b/istioctl/cmd/testdata/uninject/deploymentconfig-app-probe.yaml.injected deleted file mode 100644 index a1378ea77..000000000 --- a/istioctl/cmd/testdata/uninject/deploymentconfig-app-probe.yaml.injected +++ /dev/null @@ -1,196 +0,0 @@ -apiVersion: apps.openshift.io/v1 -kind: DeploymentConfig -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - revisionHistoryLimit: 2 - strategy: - resources: {} - type: Rolling - template: - metadata: - annotations: - sidecar.istio.io/interceptionMode: REDIRECT - sidecar.istio.io/status: '{"version":"","initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-certs"],"imagePullSecrets":null}' - traffic.sidecar.istio.io/excludeInboundPorts: "15020" - traffic.sidecar.istio.io/includeInboundPorts: "80" - traffic.sidecar.istio.io/includeOutboundIPRanges: '*' - creationTimestamp: null - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --configPath - - /etc/istio/proxy - - --binaryPath - - /usr/local/bin/envoy - - --serviceCluster - - hello.$(POD_NAMESPACE) - - --drainDuration - - 45s - - --parentShutdownDuration - - 1m0s - - --discoveryAddress - - istio-pilot:15010 - - --dnsRefreshRate - - 300s - - --connectTimeout - - 1s - - --proxyAdminPort - - "15000" - - --controlPlaneAuthPolicy - - NONE - - --statusPort - - "15020" - - --applicationPorts - - "80" - - --concurrency - - "2" - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: ISTIO_META_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: ISTIO_META_CONFIG_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_INCLUDE_INBOUND_PORTS - value: "80" - - name: ISTIO_METAJSON_LABELS - value: | - {"app":"hello","tier":"backend","track":"stable"} - - name: ISTIO_KUBE_APP_PROBERS - value: '{"/app-health/hello/livez":{"httpGet":{"path":"/healthz","port":8080,"scheme":"HTTP"},"timeoutSeconds":3},"/app-health/hello/readyz":{"httpGet":{"path":"/healthz","port":8080,"scheme":"HTTP"},"timeoutSeconds":3}}' - image: docker.io/istio/proxyv2:unittest - imagePullPolicy: IfNotPresent - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15020 - initialDelaySeconds: 1 - periodSeconds: 2 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - initContainers: - - args: - - -p - - "15001" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - "80" - - -d - - "15090,15020" - image: docker.io/istio/proxy_init:unittest - imagePullPolicy: IfNotPresent - name: istio-init - resources: - limits: - cpu: 100m - memory: 50Mi - requests: - cpu: 10m - memory: 10Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - volumes: - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-certs - secret: - optional: true - secretName: istio.default - test: false - triggers: - - type: ConfigChange - - imageChangeParams: - automatic: true - containerNames: - - helloworld - from: - kind: ImageStreamTag - name: hello-go-gke:1.0 - type: ImageChange -status: - availableReplicas: 0 - latestVersion: 0 - observedGeneration: 0 - replicas: 0 - unavailableReplicas: 0 - updatedReplicas: 0 ---- diff --git a/istioctl/cmd/testdata/uninject/deploymentconfig-multi.yaml b/istioctl/cmd/testdata/uninject/deploymentconfig-multi.yaml deleted file mode 100644 index ed701cb9c..000000000 --- a/istioctl/cmd/testdata/uninject/deploymentconfig-multi.yaml +++ /dev/null @@ -1,64 +0,0 @@ -apiVersion: v1 -items: -- apiVersion: v1 - kind: Service - metadata: - name: frontend - spec: - ports: - - port: 80 - protocol: TCP - targetPort: 80 - selector: - app: hello - tier: frontend - type: LoadBalancer -- apiVersion: apps.openshift.io/v1 - kind: DeploymentConfig - metadata: - creationTimestamp: null - name: hello - spec: - replicas: 7 - revisionHistoryLimit: 2 - strategy: - resources: {} - type: Rolling - template: - metadata: - annotations: - sidecar.istio.io/inject: "false" - creationTimestamp: null - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - test: false - triggers: - - type: ConfigChange - - imageChangeParams: - automatic: true - containerNames: - - helloworld - from: - kind: ImageStreamTag - name: hello-go-gke:1.0 - type: ImageChange - status: - availableReplicas: 0 - latestVersion: 0 - observedGeneration: 0 - replicas: 0 - unavailableReplicas: 0 - updatedReplicas: 0 -kind: List -metadata: {} ---- diff --git a/istioctl/cmd/testdata/uninject/deploymentconfig-multi.yaml.injected b/istioctl/cmd/testdata/uninject/deploymentconfig-multi.yaml.injected deleted file mode 100644 index 356f76364..000000000 --- a/istioctl/cmd/testdata/uninject/deploymentconfig-multi.yaml.injected +++ /dev/null @@ -1,211 +0,0 @@ -apiVersion: v1 -items: -- apiVersion: v1 - kind: Service - metadata: - name: frontend - spec: - ports: - - port: 80 - protocol: TCP - targetPort: 80 - selector: - app: hello - tier: frontend - type: LoadBalancer -- apiVersion: apps.openshift.io/v1 - kind: DeploymentConfig - metadata: - creationTimestamp: null - name: hello - spec: - replicas: 7 - revisionHistoryLimit: 2 - strategy: - resources: {} - type: Rolling - template: - metadata: - annotations: - sidecar.istio.io/interceptionMode: REDIRECT - sidecar.istio.io/status: '{"version":"","initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-certs"],"imagePullSecrets":null}' - traffic.sidecar.istio.io/excludeInboundPorts: "15020" - traffic.sidecar.istio.io/includeInboundPorts: "80" - traffic.sidecar.istio.io/includeOutboundIPRanges: '*' - creationTimestamp: null - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --configPath - - /etc/istio/proxy - - --binaryPath - - /usr/local/bin/envoy - - --serviceCluster - - hello.$(POD_NAMESPACE) - - --drainDuration - - 45s - - --parentShutdownDuration - - 1m0s - - --discoveryAddress - - istio-pilot:15010 - - --dnsRefreshRate - - 300s - - --connectTimeout - - 1s - - --proxyAdminPort - - "15000" - - --controlPlaneAuthPolicy - - NONE - - --statusPort - - "15020" - - --applicationPorts - - "80" - - --concurrency - - "2" - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: ISTIO_META_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: ISTIO_META_CONFIG_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_INCLUDE_INBOUND_PORTS - value: "80" - - name: ISTIO_METAJSON_LABELS - value: | - {"app":"hello","tier":"backend","track":"stable"} - image: docker.io/istio/proxyv2:unittest - imagePullPolicy: IfNotPresent - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15020 - initialDelaySeconds: 1 - periodSeconds: 2 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - initContainers: - - args: - - -p - - "15001" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - "80" - - -d - - "15090,15020" - image: docker.io/istio/proxy_init:unittest - imagePullPolicy: IfNotPresent - name: istio-init - resources: - limits: - cpu: 100m - memory: 50Mi - requests: - cpu: 10m - memory: 10Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - volumes: - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-certs - secret: - optional: true - secretName: istio.default - test: false - triggers: - - type: ConfigChange - - imageChangeParams: - automatic: true - containerNames: - - helloworld - from: - kind: ImageStreamTag - name: hello-go-gke:1.0 - type: ImageChange - status: - availableReplicas: 0 - latestVersion: 0 - observedGeneration: 0 - replicas: 0 - unavailableReplicas: 0 - updatedReplicas: 0 -kind: List -metadata: {} ---- diff --git a/istioctl/cmd/testdata/uninject/deploymentconfig.yaml b/istioctl/cmd/testdata/uninject/deploymentconfig.yaml deleted file mode 100644 index 390b69981..000000000 --- a/istioctl/cmd/testdata/uninject/deploymentconfig.yaml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: apps.openshift.io/v1 -kind: DeploymentConfig -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - revisionHistoryLimit: 2 - strategy: - resources: {} - type: Rolling - template: - metadata: - annotations: - sidecar.istio.io/inject: "false" - creationTimestamp: null - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - test: false - triggers: - - type: ConfigChange - - imageChangeParams: - automatic: true - containerNames: - - helloworld - from: - kind: ImageStreamTag - name: hello-go-gke:1.0 - type: ImageChange -status: - availableReplicas: 0 - latestVersion: 0 - observedGeneration: 0 - replicas: 0 - unavailableReplicas: 0 - updatedReplicas: 0 ---- diff --git a/istioctl/cmd/testdata/uninject/deploymentconfig.yaml.injected b/istioctl/cmd/testdata/uninject/deploymentconfig.yaml.injected deleted file mode 100644 index 66194aeb4..000000000 --- a/istioctl/cmd/testdata/uninject/deploymentconfig.yaml.injected +++ /dev/null @@ -1,194 +0,0 @@ -apiVersion: apps.openshift.io/v1 -kind: DeploymentConfig -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - revisionHistoryLimit: 2 - strategy: - resources: {} - type: Rolling - template: - metadata: - annotations: - sidecar.istio.io/interceptionMode: REDIRECT - sidecar.istio.io/status: '{"version":"","initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-certs"],"imagePullSecrets":null}' - traffic.sidecar.istio.io/excludeInboundPorts: "15020" - traffic.sidecar.istio.io/includeInboundPorts: "80" - traffic.sidecar.istio.io/includeOutboundIPRanges: '*' - creationTimestamp: null - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --configPath - - /etc/istio/proxy - - --binaryPath - - /usr/local/bin/envoy - - --serviceCluster - - hello.$(POD_NAMESPACE) - - --drainDuration - - 45s - - --parentShutdownDuration - - 1m0s - - --discoveryAddress - - istio-pilot:15010 - - --dnsRefreshRate - - 300s - - --connectTimeout - - 1s - - --proxyAdminPort - - "15000" - - --controlPlaneAuthPolicy - - NONE - - --statusPort - - "15020" - - --applicationPorts - - "80" - - --concurrency - - "2" - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: ISTIO_META_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: ISTIO_META_CONFIG_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_INCLUDE_INBOUND_PORTS - value: "80" - - name: ISTIO_METAJSON_LABELS - value: | - {"app":"hello","tier":"backend","track":"stable"} - image: docker.io/istio/proxyv2:unittest - imagePullPolicy: IfNotPresent - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15020 - initialDelaySeconds: 1 - periodSeconds: 2 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - initContainers: - - args: - - -p - - "15001" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - "80" - - -d - - "15090,15020" - image: docker.io/istio/proxy_init:unittest - imagePullPolicy: IfNotPresent - name: istio-init - resources: - limits: - cpu: 100m - memory: 50Mi - requests: - cpu: 10m - memory: 10Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - volumes: - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-certs - secret: - optional: true - secretName: istio.default - test: false - triggers: - - type: ConfigChange - - imageChangeParams: - automatic: true - containerNames: - - helloworld - from: - kind: ImageStreamTag - name: hello-go-gke:1.0 - type: ImageChange -status: - availableReplicas: 0 - latestVersion: 0 - observedGeneration: 0 - replicas: 0 - unavailableReplicas: 0 - updatedReplicas: 0 ---- diff --git a/istioctl/cmd/testdata/uninject/enable-core-dump.yaml b/istioctl/cmd/testdata/uninject/enable-core-dump.yaml deleted file mode 100644 index d2ec77b90..000000000 --- a/istioctl/cmd/testdata/uninject/enable-core-dump.yaml +++ /dev/null @@ -1,32 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - sidecar.istio.io/inject: "false" - creationTimestamp: null - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} -status: {} ---- diff --git a/istioctl/cmd/testdata/uninject/enable-core-dump.yaml.injected b/istioctl/cmd/testdata/uninject/enable-core-dump.yaml.injected deleted file mode 100644 index 80fb9262a..000000000 --- a/istioctl/cmd/testdata/uninject/enable-core-dump.yaml.injected +++ /dev/null @@ -1,199 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - sidecar.istio.io/interceptionMode: REDIRECT - traffic.sidecar.istio.io/excludeInboundPorts: "15020" - traffic.sidecar.istio.io/includeInboundPorts: "80" - traffic.sidecar.istio.io/includeOutboundIPRanges: '*' - creationTimestamp: null - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --configPath - - /etc/istio/proxy - - --binaryPath - - /usr/local/bin/envoy - - --serviceCluster - - hello.$(POD_NAMESPACE) - - --drainDuration - - 45s - - --parentShutdownDuration - - 1m0s - - --discoveryAddress - - istio-pilot:15010 - - --dnsRefreshRate - - 300s - - --connectTimeout - - 1s - - --proxyAdminPort - - "15000" - - --controlPlaneAuthPolicy - - NONE - - --statusPort - - "15020" - - --applicationPorts - - "80" - - --concurrency - - "2" - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: ISTIO_META_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: ISTIO_META_CONFIG_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_INCLUDE_INBOUND_PORTS - value: "80" - - name: ISTIO_METAJSON_LABELS - value: | - {"app":"hello","tier":"backend","track":"stable"} - image: docker.io/istio/proxyv2:unittest - imagePullPolicy: IfNotPresent - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15020 - initialDelaySeconds: 1 - periodSeconds: 2 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - initContainers: - - args: - - -p - - "15001" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - "80" - - -d - - "15090,15020" - image: docker.io/istio/proxy_init:unittest - imagePullPolicy: IfNotPresent - name: istio-init - resources: - limits: - cpu: 100m - memory: 50Mi - requests: - cpu: 10m - memory: 10Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/core.proxy && ulimit -c unlimited - command: - - /bin/sh - image: docker.io/istio/proxy_init:unittest - imagePullPolicy: IfNotPresent - name: enable-core-dump - resources: {} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - volumes: - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-certs - secret: - optional: true - secretName: istio.default -status: {} ---- diff --git a/istioctl/cmd/testdata/uninject/hello.yaml b/istioctl/cmd/testdata/uninject/hello.yaml deleted file mode 100644 index 12f4c98db..000000000 --- a/istioctl/cmd/testdata/uninject/hello.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: {} - strategy: {} - template: - metadata: - annotations: - sidecar.istio.io/inject: "false" - creationTimestamp: null - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} -status: {} ---- diff --git a/istioctl/cmd/testdata/uninject/job.yaml b/istioctl/cmd/testdata/uninject/job.yaml deleted file mode 100644 index c52b59fb6..000000000 --- a/istioctl/cmd/testdata/uninject/job.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - creationTimestamp: null - name: pi -spec: - template: - metadata: - annotations: - sidecar.istio.io/inject: "false" - creationTimestamp: null - name: pi - spec: - containers: - - command: - - perl - - -Mbignum=bpi - - -wle - - print bpi(2000) - image: perl - name: pi - resources: {} - restartPolicy: Never -status: {} ---- diff --git a/istioctl/cmd/testdata/uninject/job.yaml.injected b/istioctl/cmd/testdata/uninject/job.yaml.injected deleted file mode 100644 index 85d611707..000000000 --- a/istioctl/cmd/testdata/uninject/job.yaml.injected +++ /dev/null @@ -1,167 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - creationTimestamp: null - name: pi -spec: - template: - metadata: - annotations: - sidecar.istio.io/interceptionMode: REDIRECT - sidecar.istio.io/status: '{"version":"","initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-certs"],"imagePullSecrets":null}' - traffic.sidecar.istio.io/excludeInboundPorts: "15020" - traffic.sidecar.istio.io/includeOutboundIPRanges: '*' - creationTimestamp: null - name: pi - spec: - containers: - - command: - - perl - - -Mbignum=bpi - - -wle - - print bpi(2000) - image: perl - name: pi - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --configPath - - /etc/istio/proxy - - --binaryPath - - /usr/local/bin/envoy - - --serviceCluster - - pi.default - - --drainDuration - - 45s - - --parentShutdownDuration - - 1m0s - - --discoveryAddress - - istio-pilot:15010 - - --dnsRefreshRate - - 300s - - --connectTimeout - - 1s - - --proxyAdminPort - - "15000" - - --controlPlaneAuthPolicy - - NONE - - --statusPort - - "15020" - - --applicationPorts - - "" - - --concurrency - - "2" - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: ISTIO_META_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: ISTIO_META_CONFIG_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_INCLUDE_INBOUND_PORTS - image: docker.io/istio/proxyv2:unittest - imagePullPolicy: IfNotPresent - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15020 - initialDelaySeconds: 1 - periodSeconds: 2 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - initContainers: - - args: - - -p - - "15001" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - "" - - -d - - "15090,15020" - image: docker.io/istio/proxy_init:unittest - imagePullPolicy: IfNotPresent - name: istio-init - resources: - limits: - cpu: 100m - memory: 50Mi - requests: - cpu: 10m - memory: 10Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - restartPolicy: Never - volumes: - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-certs - secret: - optional: true - secretName: istio.default -status: {} ---- diff --git a/istioctl/cmd/testdata/uninject/list.yaml b/istioctl/cmd/testdata/uninject/list.yaml deleted file mode 100644 index ad4e82d65..000000000 --- a/istioctl/cmd/testdata/uninject/list.yaml +++ /dev/null @@ -1,77 +0,0 @@ -apiVersion: v1 -items: -- apiVersion: apps/v1 - kind: Deployment - metadata: - creationTimestamp: null - name: hello-v1 - spec: - replicas: 3 - selector: - matchLabels: - app: hello - tier: backend - track: stable - version: v1 - strategy: {} - template: - metadata: - annotations: - sidecar.istio.io/inject: "false" - creationTimestamp: null - labels: - app: hello - tier: backend - track: stable - version: v1 - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - dnsConfig: {} - volumes: - - name: test - secret: - optional: true - secretName: test - status: {} -- apiVersion: apps/v1 - kind: Deployment - metadata: - creationTimestamp: null - name: hello-v2 - spec: - replicas: 3 - selector: - matchLabels: - app: hello - tier: backend - track: stable - version: v2 - strategy: {} - template: - metadata: - annotations: - sidecar.istio.io/inject: "false" - creationTimestamp: null - labels: - app: hello - tier: backend - track: stable - version: v2 - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 81 - name: http - resources: {} - status: {} -kind: List -metadata: {} ---- diff --git a/istioctl/cmd/testdata/uninject/list.yaml.injected b/istioctl/cmd/testdata/uninject/list.yaml.injected deleted file mode 100644 index dd523a384..000000000 --- a/istioctl/cmd/testdata/uninject/list.yaml.injected +++ /dev/null @@ -1,372 +0,0 @@ -apiVersion: v1 -items: -- apiVersion: apps/v1 - kind: Deployment - metadata: - creationTimestamp: null - name: hello-v1 - spec: - replicas: 3 - selector: - matchLabels: - app: hello - tier: backend - track: stable - version: v1 - strategy: {} - template: - metadata: - annotations: - sidecar.istio.io/interceptionMode: REDIRECT - sidecar.istio.io/status: '{"version":"","initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-certs"],"imagePullSecrets":null}' - traffic.sidecar.istio.io/excludeInboundPorts: "15020" - traffic.sidecar.istio.io/includeInboundPorts: "80" - traffic.sidecar.istio.io/includeOutboundIPRanges: '*' - creationTimestamp: null - labels: - app: hello - tier: backend - track: stable - version: v1 - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --configPath - - /etc/istio/proxy - - --binaryPath - - /usr/local/bin/envoy - - --serviceCluster - - hello.$(POD_NAMESPACE) - - --drainDuration - - 45s - - --parentShutdownDuration - - 1m0s - - --discoveryAddress - - istio-pilot:15010 - - --dnsRefreshRate - - 300s - - --connectTimeout - - 1s - - --proxyAdminPort - - "15000" - - --controlPlaneAuthPolicy - - NONE - - --statusPort - - "15020" - - --applicationPorts - - "80" - - --concurrency - - "2" - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: ISTIO_META_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: ISTIO_META_CONFIG_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_INCLUDE_INBOUND_PORTS - value: "80" - - name: ISTIO_METAJSON_LABELS - value: | - {"app":"hello","tier":"backend","track":"stable","version":"v1"} - image: docker.io/istio/proxyv2:unittest - imagePullPolicy: IfNotPresent - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15020 - initialDelaySeconds: 1 - periodSeconds: 2 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - dnsConfig: - searches: - - global - - default.global - initContainers: - - args: - - -p - - "15001" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - "80" - - -d - - "15090,15020" - image: docker.io/istio/proxy_init:unittest - imagePullPolicy: IfNotPresent - name: istio-init - resources: - limits: - cpu: 100m - memory: 50Mi - requests: - cpu: 10m - memory: 10Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - volumes: - - name: test - secret: - optional: true - secretName: test - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-certs - secret: - optional: true - secretName: istio.default - status: {} -- apiVersion: apps/v1 - kind: Deployment - metadata: - creationTimestamp: null - name: hello-v2 - spec: - replicas: 3 - selector: - matchLabels: - app: hello - tier: backend - track: stable - version: v2 - strategy: {} - template: - metadata: - annotations: - sidecar.istio.io/interceptionMode: REDIRECT - sidecar.istio.io/status: '{"version":"","initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-certs"],"imagePullSecrets":null}' - traffic.sidecar.istio.io/excludeInboundPorts: "15020" - traffic.sidecar.istio.io/includeInboundPorts: "81" - traffic.sidecar.istio.io/includeOutboundIPRanges: '*' - creationTimestamp: null - labels: - app: hello - tier: backend - track: stable - version: v2 - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 81 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --configPath - - /etc/istio/proxy - - --binaryPath - - /usr/local/bin/envoy - - --serviceCluster - - hello.$(POD_NAMESPACE) - - --drainDuration - - 45s - - --parentShutdownDuration - - 1m0s - - --discoveryAddress - - istio-pilot:15010 - - --dnsRefreshRate - - 300s - - --connectTimeout - - 1s - - --proxyAdminPort - - "15000" - - --controlPlaneAuthPolicy - - NONE - - --statusPort - - "15020" - - --applicationPorts - - "81" - - --concurrency - - "2" - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: ISTIO_META_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: ISTIO_META_CONFIG_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_INCLUDE_INBOUND_PORTS - value: "81" - - name: ISTIO_METAJSON_LABELS - value: | - {"app":"hello","tier":"backend","track":"stable","version":"v2"} - image: docker.io/istio/proxyv2:unittest - imagePullPolicy: IfNotPresent - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15020 - initialDelaySeconds: 1 - periodSeconds: 2 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - initContainers: - - args: - - -p - - "15001" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - "81" - - -d - - "15090,15020" - image: docker.io/istio/proxy_init:unittest - imagePullPolicy: IfNotPresent - name: istio-init - resources: - limits: - cpu: 100m - memory: 50Mi - requests: - cpu: 10m - memory: 10Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - volumes: - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-certs - secret: - optional: true - secretName: istio.default - status: {} -kind: List -metadata: {} ---- diff --git a/istioctl/cmd/testdata/uninject/pod.yaml b/istioctl/cmd/testdata/uninject/pod.yaml deleted file mode 100644 index 7fa3558c2..000000000 --- a/istioctl/cmd/testdata/uninject/pod.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - annotations: - sidecar.istio.io/inject: "false" - creationTimestamp: null - name: hellopod -spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - dnsConfig: - searches: - - test -status: {} ---- diff --git a/istioctl/cmd/testdata/uninject/pod.yaml.injected b/istioctl/cmd/testdata/uninject/pod.yaml.injected deleted file mode 100644 index 5b94a084c..000000000 --- a/istioctl/cmd/testdata/uninject/pod.yaml.injected +++ /dev/null @@ -1,166 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - annotations: - sidecar.istio.io/interceptionMode: REDIRECT - sidecar.istio.io/status: '{"version":"","initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-certs"],"imagePullSecrets":null}' - traffic.sidecar.istio.io/excludeInboundPorts: "15020" - traffic.sidecar.istio.io/includeInboundPorts: "80" - traffic.sidecar.istio.io/includeOutboundIPRanges: '*' - creationTimestamp: null - name: hellopod -spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --configPath - - /etc/istio/proxy - - --binaryPath - - /usr/local/bin/envoy - - --serviceCluster - - hellopod.default - - --drainDuration - - 45s - - --parentShutdownDuration - - 1m0s - - --discoveryAddress - - istio-pilot:15010 - - --dnsRefreshRate - - 300s - - --connectTimeout - - 1s - - --proxyAdminPort - - "15000" - - --controlPlaneAuthPolicy - - NONE - - --statusPort - - "15020" - - --applicationPorts - - "80" - - --concurrency - - "2" - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: ISTIO_META_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: ISTIO_META_CONFIG_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_INCLUDE_INBOUND_PORTS - value: "80" - image: docker.io/istio/proxyv2:unittest - imagePullPolicy: IfNotPresent - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15020 - initialDelaySeconds: 1 - periodSeconds: 2 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - dnsConfig: - searches: - - global - - default.global - - test - initContainers: - - args: - - -p - - "15001" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - "80" - - -d - - "15090,15020" - image: docker.io/istio/proxy_init:unittest - imagePullPolicy: IfNotPresent - name: istio-init - resources: - limits: - cpu: 100m - memory: 50Mi - requests: - cpu: 10m - memory: 10Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - volumes: - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-certs - secret: - optional: true - secretName: istio.default -status: {} ---- diff --git a/istioctl/cmd/testdata/uninject/replicaset.yaml b/istioctl/cmd/testdata/uninject/replicaset.yaml deleted file mode 100644 index f55892aac..000000000 --- a/istioctl/cmd/testdata/uninject/replicaset.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: apps/v1 -kind: ReplicaSet -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - template: - metadata: - annotations: - sidecar.istio.io/inject: "false" - creationTimestamp: null - labels: - app: hello - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} -status: - replicas: 0 ---- diff --git a/istioctl/cmd/testdata/uninject/replicaset.yaml.injected b/istioctl/cmd/testdata/uninject/replicaset.yaml.injected deleted file mode 100644 index 6e5ec4e69..000000000 --- a/istioctl/cmd/testdata/uninject/replicaset.yaml.injected +++ /dev/null @@ -1,175 +0,0 @@ -apiVersion: apps/v1 -kind: ReplicaSet -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - template: - metadata: - annotations: - sidecar.istio.io/interceptionMode: REDIRECT - sidecar.istio.io/status: '{"version":"","initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-certs"],"imagePullSecrets":null}' - traffic.sidecar.istio.io/excludeInboundPorts: "15020" - traffic.sidecar.istio.io/includeInboundPorts: "80" - traffic.sidecar.istio.io/includeOutboundIPRanges: '*' - creationTimestamp: null - labels: - app: hello - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --configPath - - /etc/istio/proxy - - --binaryPath - - /usr/local/bin/envoy - - --serviceCluster - - hello.$(POD_NAMESPACE) - - --drainDuration - - 45s - - --parentShutdownDuration - - 1m0s - - --discoveryAddress - - istio-pilot:15010 - - --dnsRefreshRate - - 300s - - --connectTimeout - - 1s - - --proxyAdminPort - - "15000" - - --controlPlaneAuthPolicy - - NONE - - --statusPort - - "15020" - - --applicationPorts - - "80" - - --concurrency - - "2" - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: ISTIO_META_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: ISTIO_META_CONFIG_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_INCLUDE_INBOUND_PORTS - value: "80" - - name: ISTIO_METAJSON_LABELS - value: | - {"app":"hello"} - image: docker.io/istio/proxyv2:unittest - imagePullPolicy: IfNotPresent - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15020 - initialDelaySeconds: 1 - periodSeconds: 2 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - initContainers: - - args: - - -p - - "15001" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - "80" - - -d - - "15090,15020" - image: docker.io/istio/proxy_init:unittest - imagePullPolicy: IfNotPresent - name: istio-init - resources: - limits: - cpu: 100m - memory: 50Mi - requests: - cpu: 10m - memory: 10Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - volumes: - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-certs - secret: - optional: true - secretName: istio.default -status: - replicas: 0 ---- diff --git a/istioctl/cmd/testdata/uninject/replicationcontroller.yaml b/istioctl/cmd/testdata/uninject/replicationcontroller.yaml deleted file mode 100644 index 33aedbe81..000000000 --- a/istioctl/cmd/testdata/uninject/replicationcontroller.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: v1 -kind: ReplicationController -metadata: - creationTimestamp: null - name: nginx -spec: - replicas: 3 - selector: - app: nginx - template: - metadata: - annotations: - sidecar.istio.io/inject: "false" - creationTimestamp: null - labels: - app: nginx - name: nginx - spec: - containers: - - image: nginx - name: nginx - ports: - - containerPort: 80 - resources: {} -status: - replicas: 0 ---- diff --git a/istioctl/cmd/testdata/uninject/replicationcontroller.yaml.injected b/istioctl/cmd/testdata/uninject/replicationcontroller.yaml.injected deleted file mode 100644 index 4e209e820..000000000 --- a/istioctl/cmd/testdata/uninject/replicationcontroller.yaml.injected +++ /dev/null @@ -1,174 +0,0 @@ -apiVersion: v1 -kind: ReplicationController -metadata: - creationTimestamp: null - name: nginx -spec: - replicas: 3 - selector: - app: nginx - template: - metadata: - annotations: - sidecar.istio.io/interceptionMode: REDIRECT - sidecar.istio.io/status: '{"version":"","initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-certs"],"imagePullSecrets":null}' - traffic.sidecar.istio.io/excludeInboundPorts: "15020" - traffic.sidecar.istio.io/includeInboundPorts: "80" - traffic.sidecar.istio.io/includeOutboundIPRanges: '*' - creationTimestamp: null - labels: - app: nginx - name: nginx - spec: - containers: - - image: nginx - name: nginx - ports: - - containerPort: 80 - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --configPath - - /etc/istio/proxy - - --binaryPath - - /usr/local/bin/envoy - - --serviceCluster - - nginx.$(POD_NAMESPACE) - - --drainDuration - - 45s - - --parentShutdownDuration - - 1m0s - - --discoveryAddress - - istio-pilot:15010 - - --dnsRefreshRate - - 300s - - --connectTimeout - - 1s - - --proxyAdminPort - - "15000" - - --controlPlaneAuthPolicy - - NONE - - --statusPort - - "15020" - - --applicationPorts - - "80" - - --concurrency - - "2" - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: ISTIO_META_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: ISTIO_META_CONFIG_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_INCLUDE_INBOUND_PORTS - value: "80" - - name: ISTIO_METAJSON_LABELS - value: | - {"app":"nginx"} - image: docker.io/istio/proxyv2:unittest - imagePullPolicy: IfNotPresent - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15020 - initialDelaySeconds: 1 - periodSeconds: 2 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - initContainers: - - args: - - -p - - "15001" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - "80" - - -d - - "15090,15020" - image: docker.io/istio/proxy_init:unittest - imagePullPolicy: IfNotPresent - name: istio-init - resources: - limits: - cpu: 100m - memory: 50Mi - requests: - cpu: 10m - memory: 10Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - volumes: - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-certs - secret: - optional: true - secretName: istio.default -status: - replicas: 0 ---- diff --git a/istioctl/cmd/testdata/uninject/statefulset.yaml b/istioctl/cmd/testdata/uninject/statefulset.yaml deleted file mode 100644 index ec78d402b..000000000 --- a/istioctl/cmd/testdata/uninject/statefulset.yaml +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: apps/v1 -kind: StatefulSet -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 3 - selector: - matchLabels: - app: hello - tier: backend - track: stable - serviceName: hello - template: - metadata: - annotations: - sidecar.istio.io/inject: "false" - creationTimestamp: null - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - volumeMounts: - - mountPath: /var/lib/data - name: data - volumes: - - hostPath: - path: /mnt/disks/ssd0 - name: data - updateStrategy: {} -status: - availableReplicas: 0 - replicas: 0 ---- diff --git a/istioctl/cmd/testdata/uninject/statefulset.yaml.injected b/istioctl/cmd/testdata/uninject/statefulset.yaml.injected deleted file mode 100644 index 9ceaeb056..000000000 --- a/istioctl/cmd/testdata/uninject/statefulset.yaml.injected +++ /dev/null @@ -1,187 +0,0 @@ -apiVersion: apps/v1 -kind: StatefulSet -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 3 - selector: - matchLabels: - app: hello - tier: backend - track: stable - serviceName: hello - template: - metadata: - annotations: - sidecar.istio.io/interceptionMode: REDIRECT - sidecar.istio.io/status: '{"version":"","initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-certs"],"imagePullSecrets":null}' - traffic.sidecar.istio.io/excludeInboundPorts: "15020" - traffic.sidecar.istio.io/includeInboundPorts: "80" - traffic.sidecar.istio.io/includeOutboundIPRanges: '*' - creationTimestamp: null - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - volumeMounts: - - mountPath: /var/lib/data - name: data - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --configPath - - /etc/istio/proxy - - --binaryPath - - /usr/local/bin/envoy - - --serviceCluster - - hello.$(POD_NAMESPACE) - - --drainDuration - - 45s - - --parentShutdownDuration - - 1m0s - - --discoveryAddress - - istio-pilot:15010 - - --dnsRefreshRate - - 300s - - --connectTimeout - - 1s - - --proxyAdminPort - - "15000" - - --controlPlaneAuthPolicy - - NONE - - --statusPort - - "15020" - - --applicationPorts - - "80" - - --concurrency - - "2" - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: ISTIO_META_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: ISTIO_META_CONFIG_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_INCLUDE_INBOUND_PORTS - value: "80" - - name: ISTIO_METAJSON_LABELS - value: | - {"app":"hello","tier":"backend","track":"stable"} - image: docker.io/istio/proxyv2:unittest - imagePullPolicy: IfNotPresent - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15020 - initialDelaySeconds: 1 - periodSeconds: 2 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - initContainers: - - args: - - -p - - "15001" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - "80" - - -d - - "15090,15020" - image: docker.io/istio/proxy_init:unittest - imagePullPolicy: IfNotPresent - name: istio-init - resources: - limits: - cpu: 100m - memory: 50Mi - requests: - cpu: 10m - memory: 10Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - volumes: - - hostPath: - path: /mnt/disks/ssd0 - name: data - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-certs - secret: - optional: true - secretName: istio.default - updateStrategy: {} -status: - replicas: 0 ---- diff --git a/istioctl/cmd/testdata/v1alpha3/.gitignore b/istioctl/cmd/testdata/v1alpha3/.gitignore deleted file mode 100644 index 27e4998f6..000000000 --- a/istioctl/cmd/testdata/v1alpha3/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Only keep .golden files -*.yaml diff --git a/istioctl/cmd/testdata/v1alpha3/merged-gateway.yaml.golden b/istioctl/cmd/testdata/v1alpha3/merged-gateway.yaml.golden deleted file mode 100644 index 90827e004..000000000 --- a/istioctl/cmd/testdata/v1alpha3/merged-gateway.yaml.golden +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - creationTimestamp: null - name: wild-simple-ingress-istio-autogenerated-k8s-ingress - namespace: default -spec: - gateways: - - dubbo-system/simple-ingress-istio-autogenerated-k8s-ingress - hosts: - - '*' - http: - - match: - - uri: - exact: /foo/bar/ - route: - - destination: - host: anotherservice-service.another-namespace.svc.cluster.local - port: - number: 7080 - weight: 100 - - match: - - uri: - prefix: /foo/ - route: - - destination: - host: myservice-service.default.svc.cluster.local - port: - number: 9080 - weight: 100 - - match: - - uri: - prefix: / - route: - - destination: - host: my-ui.default.svc.cluster.local - port: - number: 80 - weight: 100 diff --git a/istioctl/cmd/testdata/v1alpha3/myservice-gateway.yaml.golden b/istioctl/cmd/testdata/v1alpha3/myservice-gateway.yaml.golden deleted file mode 100644 index 4529313ab..000000000 --- a/istioctl/cmd/testdata/v1alpha3/myservice-gateway.yaml.golden +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - creationTimestamp: null - name: wild-simple-ingress-istio-autogenerated-k8s-ingress - namespace: default -spec: - gateways: - - dubbo-system/simple-ingress-istio-autogenerated-k8s-ingress - hosts: - - '*' - http: - - match: - - uri: - prefix: /foo/ - route: - - destination: - host: myservice-service.default.svc.cluster.local - port: - number: 9080 - weight: 100 - - match: - - uri: - prefix: / - route: - - destination: - host: my-ui.default.svc.cluster.local - port: - number: 80 - weight: 100 diff --git a/istioctl/cmd/testdata/v1alpha3/named-port-gateway.yaml.golden b/istioctl/cmd/testdata/v1alpha3/named-port-gateway.yaml.golden deleted file mode 100644 index 6377eacb8..000000000 --- a/istioctl/cmd/testdata/v1alpha3/named-port-gateway.yaml.golden +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - creationTimestamp: null - name: wild-simple-ingress-istio-autogenerated-k8s-ingress - namespace: mock-ns -spec: - gateways: - - dubbo-system/simple-ingress-istio-autogenerated-k8s-ingress - hosts: - - '*' - http: - - match: - - uri: - prefix: "" - route: - - destination: - host: my-service.mock-ns.svc.cluster.local - port: - number: 1234 - weight: 100 diff --git a/istioctl/cmd/testdata/vmconfig-nil-proxy-metadata/.gitignore b/istioctl/cmd/testdata/vmconfig-nil-proxy-metadata/.gitignore deleted file mode 100644 index 2caba9a0d..000000000 --- a/istioctl/cmd/testdata/vmconfig-nil-proxy-metadata/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# Only keep golden and input files -hosts -istio-token -mesh.yaml -root-cert.pem -cluster.env \ No newline at end of file diff --git a/istioctl/cmd/testdata/vmconfig-nil-proxy-metadata/cluster.env.golden b/istioctl/cmd/testdata/vmconfig-nil-proxy-metadata/cluster.env.golden deleted file mode 100644 index dfec258cb..000000000 --- a/istioctl/cmd/testdata/vmconfig-nil-proxy-metadata/cluster.env.golden +++ /dev/null @@ -1,17 +0,0 @@ -CANONICAL_REVISION='latest' -CANONICAL_SERVICE='foo' -ISTIO_INBOUND_PORTS='*' -ISTIO_LOCAL_EXCLUDE_PORTS='22,15090,15021,15020' -ISTIO_METAJSON_LABELS='{"service.istio.io/canonical-name":"foo","service.istio.io/canonical-revision":"latest"}' -ISTIO_META_CLUSTER_ID='Kubernetes' -ISTIO_META_DNS_CAPTURE='true' -ISTIO_META_MESH_ID='' -ISTIO_META_NETWORK='' -ISTIO_META_WORKLOAD_NAME='foo' -ISTIO_NAMESPACE='bar' -ISTIO_SERVICE='foo.bar' -ISTIO_SERVICE_CIDR='*' -ISTIO_SVC_IP='10.10.10.10' -POD_NAMESPACE='bar' -SERVICE_ACCOUNT='vm-serviceaccount' -TRUST_DOMAIN='' diff --git a/istioctl/cmd/testdata/vmconfig-nil-proxy-metadata/hosts.golden b/istioctl/cmd/testdata/vmconfig-nil-proxy-metadata/hosts.golden deleted file mode 100644 index e69de29bb..000000000 diff --git a/istioctl/cmd/testdata/vmconfig-nil-proxy-metadata/istio-token.golden b/istioctl/cmd/testdata/vmconfig-nil-proxy-metadata/istio-token.golden deleted file mode 100644 index e69de29bb..000000000 diff --git a/istioctl/cmd/testdata/vmconfig-nil-proxy-metadata/mesh.yaml.golden b/istioctl/cmd/testdata/vmconfig-nil-proxy-metadata/mesh.yaml.golden deleted file mode 100644 index b0b90b1d3..000000000 --- a/istioctl/cmd/testdata/vmconfig-nil-proxy-metadata/mesh.yaml.golden +++ /dev/null @@ -1,16 +0,0 @@ -defaultConfig: - proxyMetadata: - CANONICAL_REVISION: latest - CANONICAL_SERVICE: foo - ISTIO_META_CLUSTER_ID: Kubernetes - ISTIO_META_DNS_CAPTURE: "true" - ISTIO_META_MESH_ID: "" - ISTIO_META_NETWORK: "" - ISTIO_META_WORKLOAD_NAME: foo - ISTIO_METAJSON_LABELS: '{"service.istio.io/canonical-name":"foo","service.istio.io/canonical-revision":"latest"}' - POD_NAMESPACE: bar - SERVICE_ACCOUNT: vm-serviceaccount - TRUST_DOMAIN: "" - readinessProbe: - httpGet: - port: 8080 diff --git a/istioctl/cmd/testdata/vmconfig-nil-proxy-metadata/root-cert.pem.golden b/istioctl/cmd/testdata/vmconfig-nil-proxy-metadata/root-cert.pem.golden deleted file mode 100644 index 581ddb2c2..000000000 --- a/istioctl/cmd/testdata/vmconfig-nil-proxy-metadata/root-cert.pem.golden +++ /dev/null @@ -1 +0,0 @@ -fake-CA-cert \ No newline at end of file diff --git a/istioctl/cmd/testdata/vmconfig-nil-proxy-metadata/workloadgroup.yaml b/istioctl/cmd/testdata/vmconfig-nil-proxy-metadata/workloadgroup.yaml deleted file mode 100644 index 5b6105095..000000000 --- a/istioctl/cmd/testdata/vmconfig-nil-proxy-metadata/workloadgroup.yaml +++ /dev/null @@ -1,14 +0,0 @@ -kind: WorkloadGroup -metadata: - name: foo - namespace: bar -spec: - metadata: - annotations: {} - labels: {} - template: - ports: {} - serviceAccount: vm-serviceaccount - probe: - httpGet: - port: 8080 diff --git a/istioctl/cmd/testdata/vmconfig/.gitignore b/istioctl/cmd/testdata/vmconfig/.gitignore deleted file mode 100644 index 7a5b68f58..000000000 --- a/istioctl/cmd/testdata/vmconfig/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# Only keep golden and input files -../vmconfig-nil-proxy-metadata/hosts -istio-token -mesh.yaml -root-cert.pem -cluster.env \ No newline at end of file diff --git a/istioctl/cmd/testdata/vmconfig/simple/.gitignore b/istioctl/cmd/testdata/vmconfig/simple/.gitignore deleted file mode 100644 index 2caba9a0d..000000000 --- a/istioctl/cmd/testdata/vmconfig/simple/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# Only keep golden and input files -hosts -istio-token -mesh.yaml -root-cert.pem -cluster.env \ No newline at end of file diff --git a/istioctl/cmd/testdata/vmconfig/simple/cluster.env.golden b/istioctl/cmd/testdata/vmconfig/simple/cluster.env.golden deleted file mode 100644 index c5c8fb336..000000000 --- a/istioctl/cmd/testdata/vmconfig/simple/cluster.env.golden +++ /dev/null @@ -1,20 +0,0 @@ -CANONICAL_REVISION='latest' -CANONICAL_SERVICE='foo' -CA_ADDR='istiod-rev-1.dubbo-system.svc:15012' -CLUSTER_MESH_CONFIG_VALUE='foo' -ISTIO_INBOUND_PORTS='*' -ISTIO_LOCAL_EXCLUDE_PORTS='22,15090,15021,15020' -ISTIO_METAJSON_LABELS='{"service.istio.io/canonical-name":"foo","service.istio.io/canonical-revision":"latest"}' -ISTIO_META_CLUSTER_ID='Kubernetes' -ISTIO_META_DNS_CAPTURE='true' -ISTIO_META_MESH_ID='' -ISTIO_META_NETWORK='' -ISTIO_META_WORKLOAD_NAME='foo' -ISTIO_NAMESPACE='bar' -ISTIO_SERVICE='foo.bar' -ISTIO_SERVICE_CIDR='*' -ISTIO_SVC_IP='10.10.10.10' -POD_NAMESPACE='bar' -PROXY_CONFIG_ANNOT_VALUE='bar' -SERVICE_ACCOUNT='vm-serviceaccount' -TRUST_DOMAIN='' diff --git a/istioctl/cmd/testdata/vmconfig/simple/hosts.golden b/istioctl/cmd/testdata/vmconfig/simple/hosts.golden deleted file mode 100644 index e69de29bb..000000000 diff --git a/istioctl/cmd/testdata/vmconfig/simple/istio-token.golden b/istioctl/cmd/testdata/vmconfig/simple/istio-token.golden deleted file mode 100644 index e69de29bb..000000000 diff --git a/istioctl/cmd/testdata/vmconfig/simple/mesh.yaml.golden b/istioctl/cmd/testdata/vmconfig/simple/mesh.yaml.golden deleted file mode 100644 index c28b3d6c9..000000000 --- a/istioctl/cmd/testdata/vmconfig/simple/mesh.yaml.golden +++ /dev/null @@ -1,19 +0,0 @@ -defaultConfig: - discoveryAddress: istiod-rev-1.dubbo-system.svc:15012 - proxyMetadata: - CANONICAL_REVISION: latest - CANONICAL_SERVICE: foo - CLUSTER_MESH_CONFIG_VALUE: foo - ISTIO_META_CLUSTER_ID: Kubernetes - ISTIO_META_DNS_CAPTURE: "true" - ISTIO_META_MESH_ID: "" - ISTIO_META_NETWORK: "" - ISTIO_META_WORKLOAD_NAME: foo - ISTIO_METAJSON_LABELS: '{"service.istio.io/canonical-name":"foo","service.istio.io/canonical-revision":"latest"}' - POD_NAMESPACE: bar - PROXY_CONFIG_ANNOT_VALUE: bar - SERVICE_ACCOUNT: vm-serviceaccount - TRUST_DOMAIN: "" - readinessProbe: - httpGet: - port: 8080 diff --git a/istioctl/cmd/testdata/vmconfig/simple/meshconfig.yaml b/istioctl/cmd/testdata/vmconfig/simple/meshconfig.yaml deleted file mode 100644 index b187e872c..000000000 --- a/istioctl/cmd/testdata/vmconfig/simple/meshconfig.yaml +++ /dev/null @@ -1,8 +0,0 @@ -defaultConfig: - proxyMetadata: - # should be overridden by the command - ISTIO_META_DNS_CAPTURE: "false" - # should be overridden by the annotation on the WorkloadGroup - PROXY_CONFIG_ANNOT_VALUE: "foo" - # should be in the final cluster.env/mesh.yaml - CLUSTER_MESH_CONFIG_VALUE: "foo" diff --git a/istioctl/cmd/testdata/vmconfig/simple/root-cert.pem.golden b/istioctl/cmd/testdata/vmconfig/simple/root-cert.pem.golden deleted file mode 100644 index 581ddb2c2..000000000 --- a/istioctl/cmd/testdata/vmconfig/simple/root-cert.pem.golden +++ /dev/null @@ -1 +0,0 @@ -fake-CA-cert \ No newline at end of file diff --git a/istioctl/cmd/testdata/vmconfig/simple/workloadgroup.yaml b/istioctl/cmd/testdata/vmconfig/simple/workloadgroup.yaml deleted file mode 100644 index ccb8b1ef1..000000000 --- a/istioctl/cmd/testdata/vmconfig/simple/workloadgroup.yaml +++ /dev/null @@ -1,18 +0,0 @@ -kind: WorkloadGroup -metadata: - name: foo - namespace: bar -spec: - metadata: - annotations: - proxy.istio.io/config: |- - proxyMetadata: - # this should override the value from the global meshconfig - PROXY_CONFIG_ANNOT_VALUE: bar - labels: {} - template: - ports: {} - serviceAccount: vm-serviceaccount - probe: - httpGet: - port: 8080 diff --git a/istioctl/cmd/version.go b/istioctl/cmd/version.go deleted file mode 100644 index 553cf4257..000000000 --- a/istioctl/cmd/version.go +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "encoding/json" - "fmt" - "os" -) - -import ( - envoy_corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - xdsapi "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "github.com/spf13/cobra" - "github.com/spf13/pflag" - structpb "google.golang.org/protobuf/types/known/structpb" - "istio.io/pkg/log" - istioVersion "istio.io/pkg/version" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/clioptions" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/multixds" - "github.com/apache/dubbo-go-pixiu/operator/cmd/mesh" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - "github.com/apache/dubbo-go-pixiu/pkg/proxy" -) - -func newVersionCommand() *cobra.Command { - profileCmd := mesh.ProfileCmd(log.DefaultOptions()) - var opts clioptions.ControlPlaneOptions - versionCmd := istioVersion.CobraCommandWithOptions(istioVersion.CobraOptions{ - GetRemoteVersion: getRemoteInfoWrapper(&profileCmd, &opts), - GetProxyVersions: getProxyInfoWrapper(&opts), - }) - opts.AttachControlPlaneFlags(versionCmd) - - versionCmd.Flags().VisitAll(func(flag *pflag.Flag) { - if flag.Name == "short" { - err := flag.Value.Set("true") - if err != nil { - fmt.Fprintf(os.Stdout, "set flag %q as true failed due to error %v", flag.Name, err) - } - } - if flag.Name == "remote" { - err := flag.Value.Set("true") - if err != nil { - fmt.Fprintf(os.Stdout, "set flag %q as true failed due to error %v", flag.Name, err) - } - } - }) - return versionCmd -} - -func getRemoteInfo(opts clioptions.ControlPlaneOptions) (*istioVersion.MeshInfo, error) { - kubeClient, err := kubeClientWithRevision(kubeconfig, configContext, opts.Revision) - if err != nil { - return nil, err - } - - return kubeClient.GetIstioVersions(context.TODO(), istioNamespace) -} - -func getRemoteInfoWrapper(pc **cobra.Command, opts *clioptions.ControlPlaneOptions) func() (*istioVersion.MeshInfo, error) { - return func() (*istioVersion.MeshInfo, error) { - remInfo, err := getRemoteInfo(*opts) - if err != nil { - fmt.Fprintf((*pc).OutOrStderr(), "%v\n", err) - // Return nil so that the client version is printed - return nil, nil - } - if remInfo == nil { - fmt.Fprintf((*pc).OutOrStderr(), "Istio is not present in the cluster with namespace %q\n", istioNamespace) - } - return remInfo, err - } -} - -func getProxyInfoWrapper(opts *clioptions.ControlPlaneOptions) func() (*[]istioVersion.ProxyInfo, error) { - return func() (*[]istioVersion.ProxyInfo, error) { - return proxy.GetProxyInfo(kubeconfig, configContext, opts.Revision, istioNamespace) - } -} - -// xdsVersionCommand gets the Control Plane and Sidecar versions via XDS -func xdsVersionCommand() *cobra.Command { - var opts clioptions.ControlPlaneOptions - var centralOpts clioptions.CentralControlPlaneOptions - var xdsResponses *xdsapi.DiscoveryResponse - versionCmd := istioVersion.CobraCommandWithOptions(istioVersion.CobraOptions{ - GetRemoteVersion: xdsRemoteVersionWrapper(&opts, ¢ralOpts, &xdsResponses), - GetProxyVersions: xdsProxyVersionWrapper(&xdsResponses), - }) - opts.AttachControlPlaneFlags(versionCmd) - centralOpts.AttachControlPlaneFlags(versionCmd) - versionCmd.Args = func(c *cobra.Command, args []string) error { - if err := cobra.NoArgs(c, args); err != nil { - return err - } - if err := centralOpts.ValidateControlPlaneFlags(); err != nil { - return err - } - return nil - } - versionCmd.Example = `# Retrieve version information directly from the control plane, using token security -# (This is the usual way to get the control plane version with an out-of-cluster control plane.) -istioctl x version --xds-address istio.cloudprovider.example.com:15012 - -# Retrieve version information via Kubernetes config, using token security -# (This is the usual way to get the control plane version with an in-cluster control plane.) -istioctl x version - -# Retrieve version information directly from the control plane, using RSA certificate security -# (Certificates must be obtained before this step. The --cert-dir flag lets istioctl bypass the Kubernetes API server.) -istioctl x version --xds-address istio.example.com:15012 --cert-dir ~/.istio-certs - -# Retrieve version information via XDS from specific control plane in multi-control plane in-cluster configuration -# (Select a specific control plane in an in-cluster canary Istio configuration.) -istioctl x version --xds-label istio.io/rev=default -` - - versionCmd.Flags().VisitAll(func(flag *pflag.Flag) { - if flag.Name == "short" { - err := flag.Value.Set("true") - if err != nil { - fmt.Fprintf(os.Stdout, "set flag %q as true failed due to error %v", flag.Name, err) - } - } - if flag.Name == "remote" { - err := flag.Value.Set("true") - if err != nil { - fmt.Fprintf(os.Stdout, "set flag %q as true failed due to error %v", flag.Name, err) - } - } - }) - return versionCmd -} - -// xdsRemoteVersionWrapper uses outXDS to share the XDS response with xdsProxyVersionWrapper. -// (Screwy API on istioVersion.CobraCommandWithOptions) -// nolint: lll -func xdsRemoteVersionWrapper(opts *clioptions.ControlPlaneOptions, centralOpts *clioptions.CentralControlPlaneOptions, outXDS **xdsapi.DiscoveryResponse) func() (*istioVersion.MeshInfo, error) { - return func() (*istioVersion.MeshInfo, error) { - xdsRequest := xdsapi.DiscoveryRequest{ - TypeUrl: "istio.io/connections", - } - kubeClient, err := kubeClientWithRevision(kubeconfig, configContext, opts.Revision) - if err != nil { - return nil, err - } - xdsResponse, err := multixds.RequestAndProcessXds(&xdsRequest, *centralOpts, istioNamespace, kubeClient) - if err != nil { - return nil, err - } - *outXDS = xdsResponse - if xdsResponse.ControlPlane == nil { - return &istioVersion.MeshInfo{ - istioVersion.ServerInfo{ - Component: "MISSING CP ID", - Info: istioVersion.BuildInfo{ - Version: "MISSING CP ID", - }, - }, - }, nil - } - cpID := xds.IstioControlPlaneInstance{} - err = json.Unmarshal([]byte(xdsResponse.ControlPlane.Identifier), &cpID) - if err != nil { - return nil, fmt.Errorf("could not parse CP Identifier: %w", err) - } - return &istioVersion.MeshInfo{ - istioVersion.ServerInfo{ - Component: cpID.Component, - Info: cpID.Info, - }, - }, nil - } -} - -func xdsProxyVersionWrapper(xdsResponse **xdsapi.DiscoveryResponse) func() (*[]istioVersion.ProxyInfo, error) { - return func() (*[]istioVersion.ProxyInfo, error) { - pi := []istioVersion.ProxyInfo{} - for _, resource := range (*xdsResponse).Resources { - switch resource.TypeUrl { - case "type.googleapis.com/envoy.config.core.v3.Node": - node := envoy_corev3.Node{} - err := resource.UnmarshalTo(&node) - if err != nil { - return nil, fmt.Errorf("could not unmarshal Node: %w", err) - } - meta, err := model.ParseMetadata(node.Metadata) - if err != nil || meta.ProxyConfig == nil { - // Skip non-sidecars (e.g. istioctl queries) - continue - } - pi = append(pi, istioVersion.ProxyInfo{ - ID: node.Id, - IstioVersion: getIstioVersionFromXdsMetadata(node.Metadata), - }) - default: - return nil, fmt.Errorf("unexpected resource type %q", resource.TypeUrl) - } - } - return &pi, nil - } -} - -func getIstioVersionFromXdsMetadata(metadata *structpb.Struct) string { - meta, err := model.ParseMetadata(metadata) - if err != nil { - return "unknown sidecar version" - } - return meta.IstioVersion -} diff --git a/istioctl/cmd/version_test.go b/istioctl/cmd/version_test.go deleted file mode 100644 index c67fac6ee..000000000 --- a/istioctl/cmd/version_test.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "fmt" - "strings" - "testing" -) - -import ( - "istio.io/pkg/version" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -var meshInfo = version.MeshInfo{ - {Component: "Pilot", Info: version.BuildInfo{Version: "1.0.0", GitRevision: "gitSHA123", GolangVersion: "go1.10", BuildStatus: "Clean", GitTag: "Tag"}}, - {Component: "Injector", Info: version.BuildInfo{Version: "1.0.1", GitRevision: "gitSHAabc", GolangVersion: "go1.10.1", BuildStatus: "Modified", GitTag: "OtherTag"}}, - {Component: "Citadel", Info: version.BuildInfo{Version: "1.2", GitRevision: "gitSHA321", GolangVersion: "go1.11.0", BuildStatus: "Clean", GitTag: "Tag"}}, -} - -func TestVersion(t *testing.T) { - kubeClientWithRevision = mockExecClientVersionTest - - cases := []testCase{ - { // case 0 client-side only, normal output - args: strings.Split("version --remote=false --short=false", " "), - // ignore the output, all output checks are now in istio/pkg - }, - { // case 1 remote, normal output - args: strings.Split("version --remote=true --short=false --output=", " "), - // ignore the output, all output checks are now in istio/pkg - }, - { // case 2 bogus arg - args: strings.Split("version --typo", " "), - expectedOutput: "Error: unknown flag: --typo\n", - wantException: true, - }, - { // case 3 bogus output arg - args: strings.Split("version --output xyz", " "), - expectedOutput: "Error: --output must be 'yaml' or 'json'\n", - wantException: true, - }, - { // case 4 remote, --revision flag - args: strings.Split("version --remote=true --short=false --revision canary", " "), - // ignore the output, all output checks are now in istio/pkg - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("case %d %s", i, strings.Join(c.args, " ")), func(t *testing.T) { - verifyOutput(t, c) - }) - } -} - -func mockExecClientVersionTest(_, _ string, _ string) (kube.ExtendedClient, error) { - return kube.MockClient{ - IstioVersions: &meshInfo, - }, nil -} diff --git a/istioctl/cmd/wait.go b/istioctl/cmd/wait.go deleted file mode 100644 index 2340c87cb..000000000 --- a/istioctl/cmd/wait.go +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "strconv" - "strings" - "time" -) - -import ( - "github.com/spf13/cobra" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/dynamic" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/clioptions" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/handlers" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -var ( - forFlag string - nameflag string - threshold float32 - timeout time.Duration - generation string - verbose bool - targetSchema collection.Schema - clientGetter func(string, string) (dynamic.Interface, error) -) - -const pollInterval = time.Second - -// waitCmd represents the wait command -func waitCmd() *cobra.Command { - var opts clioptions.ControlPlaneOptions - cmd := &cobra.Command{ - Use: "wait [flags] [.]", - Short: "Wait for an Istio resource", - Long: `Waits for the specified condition to be true of an Istio resource.`, - Example: ` # Wait until the bookinfo virtual service has been distributed to all proxies in the mesh - istioctl experimental wait --for=distribution virtualservice bookinfo.default - - # Wait until 99% of the proxies receive the distribution, timing out after 5 minutes - istioctl experimental wait --for=distribution --threshold=.99 --timeout=300 virtualservice bookinfo.default -`, - RunE: func(cmd *cobra.Command, args []string) error { - printVerbosef(cmd, "kubeconfig %s", kubeconfig) - printVerbosef(cmd, "ctx %s", configContext) - if forFlag == "delete" { - return errors.New("wait for delete is not yet implemented") - } else if forFlag != "distribution" { - return fmt.Errorf("--for must be 'delete' or 'distribution', got: %s", forFlag) - } - var w *watcher - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - if generation == "" { - w = getAndWatchResource(ctx) // setup version getter from kubernetes - } else { - w = withContext(ctx) - w.Go(func(result chan string) error { - result <- generation - return nil - }) - } - // wait for all deployed versions to be contained in generations - t := time.NewTicker(pollInterval) - printVerbosef(cmd, "getting first version from chan") - firstVersion, err := w.BlockingRead() - if err != nil { - return fmt.Errorf("unable to retrieve Kubernetes resource %s: %v", "", err) - } - generations := []string{firstVersion} - targetResource := config.Key( - targetSchema.Resource().Group(), targetSchema.Resource().Version(), targetSchema.Resource().Kind(), - nameflag, namespace) - for { - // run the check here as soon as we start - // because tickers won't run immediately - present, notpresent, sdcnum, err := poll(cmd, generations, targetResource, opts) - printVerbosef(cmd, "Received poll result: %d/%d", present, present+notpresent) - if err != nil { - return err - } else if float32(present)/float32(present+notpresent) >= threshold { - _, _ = fmt.Fprintf(cmd.OutOrStdout(), "Resource %s present on %d out of %d configurations for totally %d sidecars\n", - targetResource, present, present+notpresent, sdcnum) - return nil - } - select { - case newVersion := <-w.resultsChan: - printVerbosef(cmd, "received new target version: %s", newVersion) - generations = append(generations, newVersion) - case <-t.C: - printVerbosef(cmd, "tick") - continue - case err = <-w.errorChan: - return fmt.Errorf("unable to retrieve Kubernetes resource2 %s: %v", "", err) - case <-ctx.Done(): - printVerbosef(cmd, "timeout") - // I think this means the timeout has happened: - t.Stop() - return fmt.Errorf("timeout expired before resource %s became effective on all sidecars", - targetResource) - } - } - }, - Args: func(cmd *cobra.Command, args []string) error { - if err := cobra.ExactArgs(2)(cmd, args); err != nil { - return err - } - nameflag, namespace = handlers.InferPodInfo(args[1], handlers.HandleNamespace(namespace, defaultNamespace)) - return validateType(args[0]) - }, - } - cmd.PersistentFlags().StringVar(&forFlag, "for", "distribution", - "Wait condition, must be 'distribution' or 'delete'") - cmd.PersistentFlags().DurationVar(&timeout, "timeout", time.Second*30, - "The duration to wait before failing") - cmd.PersistentFlags().Float32Var(&threshold, "threshold", 1, - "The ratio of distribution required for success") - cmd.PersistentFlags().StringVar(&generation, "generation", "", - "Wait for a specific generation of config to become current, rather than using whatever is latest in "+ - "Kubernetes") - cmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "enables verbose output") - _ = cmd.PersistentFlags().MarkHidden("verbose") - opts.AttachControlPlaneFlags(cmd) - return cmd -} - -func printVerbosef(cmd *cobra.Command, template string, args ...interface{}) { - if verbose { - _, _ = fmt.Fprintf(cmd.OutOrStdout(), template+"\n", args...) - } -} - -func validateType(kind string) error { - originalKind := kind - - // Remove any dashes. - kind = strings.ReplaceAll(kind, "-", "") - - for _, s := range collections.Pilot.All() { - if strings.EqualFold(kind, s.Resource().Kind()) { - targetSchema = s - return nil - } - } - return fmt.Errorf("type %s is not recognized", originalKind) -} - -func countVersions(versionCount map[string]int, configVersion string) { - if count, ok := versionCount[configVersion]; ok { - versionCount[configVersion] = count + 1 - } else { - versionCount[configVersion] = 1 - } -} - -func poll(cmd *cobra.Command, - acceptedVersions []string, - targetResource string, - opts clioptions.ControlPlaneOptions) (present, notpresent, sdcnum int, err error) { - kubeClient, err := kubeClientWithRevision(kubeconfig, configContext, opts.Revision) - if err != nil { - return 0, 0, 0, err - } - path := fmt.Sprintf("/debug/config_distribution?resource=%s", targetResource) - pilotResponses, err := kubeClient.AllDiscoveryDo(context.TODO(), istioNamespace, path) - if err != nil { - return 0, 0, 0, fmt.Errorf("unable to query pilot for distribution "+ - "(are you using pilot version >= 1.4 with config distribution tracking on): %s", err) - } - sdcnum = 0 - versionCount := make(map[string]int) - for _, response := range pilotResponses { - var configVersions []xds.SyncedVersions - err = json.Unmarshal(response, &configVersions) - if err != nil { - return 0, 0, 0, err - } - printVerbosef(cmd, "sync status: %+v", configVersions) - sdcnum += len(configVersions) - for _, configVersion := range configVersions { - countVersions(versionCount, configVersion.ClusterVersion) - countVersions(versionCount, configVersion.RouteVersion) - countVersions(versionCount, configVersion.ListenerVersion) - } - } - - for version, count := range versionCount { - if contains(acceptedVersions, version) { - present += count - } else { - notpresent += count - } - } - return present, notpresent, sdcnum, nil -} - -func init() { - clientGetter = func(kubeconfig, context string) (dynamic.Interface, error) { - config, err := kube.DefaultRestConfig(kubeconfig, context) - if err != nil { - return nil, err - } - cfg := dynamic.ConfigFor(config) - dclient, err := dynamic.NewForConfig(cfg) - if err != nil { - return nil, err - } - return dclient, nil - } -} - -// getAndWatchResource ensures that Generations always contains -// the current generation of the targetResource, adding new versions -// as they are created. -func getAndWatchResource(ictx context.Context) *watcher { - g := withContext(ictx) - // copy nameflag to avoid race - nf := nameflag - g.Go(func(result chan string) error { - // retrieve latest generation from Kubernetes - dclient, err := clientGetter(kubeconfig, configContext) - if err != nil { - return err - } - collectionParts := strings.Split(targetSchema.Name().String(), "/") - group := targetSchema.Resource().Group() - version := targetSchema.Resource().Version() - resource := collectionParts[3] - r := dclient.Resource(schema.GroupVersionResource{Group: group, Version: version, Resource: resource}).Namespace(namespace) - watch, err := r.Watch(context.TODO(), metav1.ListOptions{FieldSelector: "metadata.name=" + nf}) - if err != nil { - return err - } - for w := range watch.ResultChan() { - o, ok := w.Object.(metav1.Object) - if !ok { - continue - } - if o.GetName() == nf { - result <- strconv.FormatInt(o.GetGeneration(), 10) - } - select { - case <-ictx.Done(): - return ictx.Err() - default: - continue - } - } - - return nil - }) - return g -} - -type watcher struct { - resultsChan chan string - errorChan chan error - ctx context.Context -} - -func withContext(ctx context.Context) *watcher { - return &watcher{ - resultsChan: make(chan string, 1), - errorChan: make(chan error, 1), - ctx: ctx, - } -} - -func (w *watcher) Go(f func(chan string) error) { - go func() { - if err := f(w.resultsChan); err != nil { - w.errorChan <- err - } - }() -} - -func (w *watcher) BlockingRead() (string, error) { - select { - case err := <-w.errorChan: - return "", err - case res := <-w.resultsChan: - return res, nil - case <-w.ctx.Done(): - return "", w.ctx.Err() - } -} diff --git a/istioctl/cmd/wait_test.go b/istioctl/cmd/wait_test.go deleted file mode 100644 index aeefe7e37..000000000 --- a/istioctl/cmd/wait_test.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "encoding/json" - "fmt" - "strings" - "sync" - "testing" -) - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/dynamic/fake" - ktesting "k8s.io/client-go/testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" -) - -func TestWaitCmd(t *testing.T) { - cannedResponseObj := []xds.SyncedVersions{ - { - ProxyID: "foo", - ClusterVersion: "1", - ListenerVersion: "1", - RouteVersion: "1", - }, - } - cannedResponse, _ := json.Marshal(cannedResponseObj) - cannedResponseMap := map[string][]byte{"onlyonepilot": cannedResponse} - - cases := []execTestCase{ - { - execClientConfig: cannedResponseMap, - args: strings.Split("x wait --generation=2 --timeout=2s virtual-service foo.default", " "), - wantException: true, - }, - { - execClientConfig: cannedResponseMap, - args: strings.Split("x wait --generation=1 virtual-service foo.default", " "), - wantException: false, - }, - { - execClientConfig: cannedResponseMap, - args: strings.Split("x wait --generation=1 VirtualService foo.default", " "), - wantException: false, - }, - { - execClientConfig: cannedResponseMap, - args: strings.Split("x wait --generation=1 not-service foo.default", " "), - wantException: true, - }, - { - execClientConfig: cannedResponseMap, - args: strings.Split("x wait --timeout 2s virtual-service bar.default", " "), - wantException: true, - expectedOutput: "Error: timeout expired before resource networking.istio.io/v1alpha3/VirtualService/default/bar became effective on all sidecars\n", - }, - { - execClientConfig: cannedResponseMap, - args: strings.Split("x wait --timeout 2s virtualservice foo.default", " "), - wantException: false, - }, - { - execClientConfig: cannedResponseMap, - args: strings.Split("x wait --timeout 2s --revision canary virtualservice foo.default", " "), - wantException: false, - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("case %d %s", i, strings.Join(c.args, " ")), func(t *testing.T) { - _ = setupK8Sfake() - verifyExecTestOutput(t, c) - }) - } -} - -func setupK8Sfake() *fake.FakeDynamicClient { - client := fake.NewSimpleDynamicClient(runtime.NewScheme()) - clientGetter = func(_, _ string) (dynamic.Interface, error) { - return client, nil - } - l := sync.Mutex{} - l.Lock() - client.PrependWatchReactor("*", func(action ktesting.Action) (handled bool, ret watch.Interface, err error) { - l.Unlock() - return false, nil, nil - }) - go func() { - // wait till watch created, then send create events. - // by default, k8s sends all existing objects at the beginning of a watch, but the test mock does not. This - // function forces the test to behave like kubernetes does, but creates a race condition on watch creation. - l.Lock() - gvr := schema.GroupVersionResource{Group: "networking.istio.io", Version: "v1alpha3", Resource: "virtualservices"} - x := client.Resource(gvr).Namespace("default") - - x.Create(context.TODO(), - newUnstructured("networking.istio.io/v1alpha3", "virtualservice", "default", "foo", int64(1)), - metav1.CreateOptions{}) - x.Create(context.TODO(), - newUnstructured("networking.istio.io/v1alpha3", "virtualservice", "default", "bar", int64(3)), - metav1.CreateOptions{}) - }() - return client -} - -func newUnstructured(apiVersion, kind, namespace, name string, generation int64) *unstructured.Unstructured { - return &unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": apiVersion, - "kind": kind, - "metadata": map[string]interface{}{ - "namespace": namespace, - "name": name, - "generation": generation, - }, - }, - } -} diff --git a/istioctl/cmd/workload.go b/istioctl/cmd/workload.go deleted file mode 100644 index 52c742e5d..000000000 --- a/istioctl/cmd/workload.go +++ /dev/null @@ -1,644 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "encoding/json" - "fmt" - "io" - "net" - "os" - "path/filepath" - "sort" - "strconv" - "strings" -) - -import ( - "github.com/spf13/cobra" - "istio.io/api/annotation" - "istio.io/api/label" - meshconfig "istio.io/api/mesh/v1alpha1" - networkingv1alpha3 "istio.io/api/networking/v1alpha3" - clientv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3" - "istio.io/pkg/log" - authenticationv1 "k8s.io/api/authentication/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/clioptions" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/multicluster" - "github.com/apache/dubbo-go-pixiu/operator/pkg/tpath" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube/controller" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/validation" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/labels" - "github.com/apache/dubbo-go-pixiu/pkg/url" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" - "github.com/apache/dubbo-go-pixiu/pkg/util/shellescape" -) - -var ( - // TODO refactor away from package vars and add more UTs - tokenDuration int64 - name string - serviceAccount string - filename string - outputDir string - clusterID string - ingressIP string - internalIP string - externalIP string - ingressSvc string - autoRegister bool - dnsCapture bool - ports []string - resourceLabels []string - annotations []string - svcAcctAnn string -) - -const ( - filePerms = os.FileMode(0o744) -) - -func workloadCommands() *cobra.Command { - workloadCmd := &cobra.Command{ - Use: "workload", - Short: "Commands to assist in configuring and deploying workloads running on VMs and other non-Kubernetes environments", - Example: ` # workload group yaml generation - workload group create - - # workload entry configuration generation - workload entry configure`, - } - workloadCmd.AddCommand(groupCommand()) - workloadCmd.AddCommand(entryCommand()) - return workloadCmd -} - -func groupCommand() *cobra.Command { - groupCmd := &cobra.Command{ - Use: "group", - Short: "Commands dealing with WorkloadGroup resources", - Example: "group create --name foo --namespace bar --labels app=foobar", - } - groupCmd.AddCommand(createCommand()) - return groupCmd -} - -func entryCommand() *cobra.Command { - entryCmd := &cobra.Command{ - Use: "entry", - Short: "Commands dealing with WorkloadEntry resources", - Example: "entry configure -f workloadgroup.yaml -o outputDir", - } - entryCmd.AddCommand(configureCommand()) - return entryCmd -} - -func createCommand() *cobra.Command { - createCmd := &cobra.Command{ - Use: "create", - Short: "Creates a WorkloadGroup resource that provides a template for associated WorkloadEntries", - Long: `Creates a WorkloadGroup resource that provides a template for associated WorkloadEntries. -The default output is serialized YAML, which can be piped into 'kubectl apply -f -' to send the artifact to the API Server.`, - Example: "create --name foo --namespace bar --labels app=foo,bar=baz --ports grpc=3550,http=8080 --annotations annotation=foobar --serviceAccount sa", - Args: func(cmd *cobra.Command, args []string) error { - if name == "" { - return fmt.Errorf("expecting a workload name") - } - if namespace == "" { - return fmt.Errorf("expecting a workload namespace") - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - u := &unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": collections.IstioNetworkingV1Alpha3Workloadgroups.Resource().APIVersion(), - "kind": collections.IstioNetworkingV1Alpha3Workloadgroups.Resource().Kind(), - "metadata": map[string]interface{}{ - "name": name, - "namespace": namespace, - }, - }, - } - spec := &networkingv1alpha3.WorkloadGroup{ - Metadata: &networkingv1alpha3.WorkloadGroup_ObjectMeta{ - Labels: convertToStringMap(resourceLabels), - Annotations: convertToStringMap(annotations), - }, - Template: &networkingv1alpha3.WorkloadEntry{ - Ports: convertToUnsignedInt32Map(ports), - ServiceAccount: serviceAccount, - }, - } - wgYAML, err := generateWorkloadGroupYAML(u, spec) - if err != nil { - return err - } - _, err = cmd.OutOrStdout().Write(wgYAML) - return err - }, - } - createCmd.PersistentFlags().StringVar(&name, "name", "", "The name of the workload group") - createCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", "", "The namespace that the workload instances will belong to") - createCmd.PersistentFlags().StringSliceVarP(&resourceLabels, "labels", "l", nil, "The labels to apply to the workload instances; e.g. -l env=prod,vers=2") - createCmd.PersistentFlags().StringSliceVarP(&annotations, "annotations", "a", nil, "The annotations to apply to the workload instances") - createCmd.PersistentFlags().StringSliceVarP(&ports, "ports", "p", nil, "The incoming ports exposed by the workload instance") - createCmd.PersistentFlags().StringVarP(&serviceAccount, "serviceAccount", "s", "default", "The service identity to associate with the workload instances") - return createCmd -} - -func generateWorkloadGroupYAML(u *unstructured.Unstructured, spec *networkingv1alpha3.WorkloadGroup) ([]byte, error) { - iSpec, err := unstructureIstioType(spec) - if err != nil { - return nil, err - } - u.Object["spec"] = iSpec - - wgYAML, err := yaml.Marshal(u.Object) - if err != nil { - return nil, err - } - return wgYAML, nil -} - -func configureCommand() *cobra.Command { - var opts clioptions.ControlPlaneOptions - - configureCmd := &cobra.Command{ - Use: "configure", - Short: "Generates all the required configuration files for a workload instance running on a VM or non-Kubernetes environment", - Long: `Generates all the required configuration files for workload instance on a VM or non-Kubernetes environment from a WorkloadGroup artifact. -This includes a MeshConfig resource, the cluster.env file, and necessary certificates and security tokens. -Configure requires either the WorkloadGroup artifact path or its location on the API server.`, - Example: ` # configure example using a local WorkloadGroup artifact - configure -f workloadgroup.yaml -o config - - # configure example using the API server - configure --name foo --namespace bar -o config`, - Args: func(cmd *cobra.Command, args []string) error { - if filename == "" && (name == "" || namespace == "") { - return fmt.Errorf("expecting a WorkloadGroup artifact file or the name and namespace of an existing WorkloadGroup") - } - if outputDir == "" { - return fmt.Errorf("expecting an output directory") - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - kubeClient, err := kubeClientWithRevision(kubeconfig, configContext, opts.Revision) - if err != nil { - return err - } - - wg := &clientv1alpha3.WorkloadGroup{} - if filename != "" { - if err := readWorkloadGroup(filename, wg); err != nil { - return err - } - } else { - wg, err = kubeClient.Istio().NetworkingV1alpha3().WorkloadGroups(namespace).Get(context.Background(), name, metav1.GetOptions{}) - // errors if the requested workload group does not exist in the given namespace - if err != nil { - return fmt.Errorf("workloadgroup %s not found in namespace %s: %v", name, namespace, err) - } - } - - // extract the cluster ID from the injector config (.Values.global.multiCluster.clusterName) - if !validateFlagIsSetManuallyOrNot(cmd, "clusterID") { - // extract the cluster ID from the injector config if it is not set by user - clusterName, err := extractClusterIDFromInjectionConfig(kubeClient) - if err != nil { - return fmt.Errorf("failed to automatically determine the --clusterID: %v", err) - } - if clusterName != "" { - clusterID = clusterName - } - } - - if err = createConfig(kubeClient, wg, clusterID, ingressIP, internalIP, externalIP, outputDir, cmd.OutOrStderr()); err != nil { - return err - } - fmt.Printf("Configuration generation into directory %s was successful\n", outputDir) - return nil - }, - PreRunE: func(cmd *cobra.Command, args []string) error { - if len(internalIP) > 0 && len(externalIP) > 0 { - return fmt.Errorf("the flags --internalIP and --externalIP are mutually exclusive") - } - return nil - }, - } - configureCmd.PersistentFlags().StringVarP(&filename, "file", "f", "", "filename of the WorkloadGroup artifact. Leave this field empty if using the API server") - configureCmd.PersistentFlags().StringVar(&name, "name", "", "The name of the workload group") - configureCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", "", "The namespace that the workload instances belongs to") - configureCmd.PersistentFlags().StringVarP(&outputDir, "output", "o", "", "Output directory for generated files") - configureCmd.PersistentFlags().StringVar(&clusterID, "clusterID", "", "The ID used to identify the cluster") - configureCmd.PersistentFlags().Int64Var(&tokenDuration, "tokenDuration", 3600, "The token duration in seconds (default: 1 hour)") - configureCmd.PersistentFlags().StringVar(&ingressSvc, "ingressService", multicluster.IstioEastWestGatewayServiceName, "Name of the Service to be"+ - " used as the ingress gateway, in the format .. If no namespace is provided, the default "+istioNamespace+" namespace will be used.") - configureCmd.PersistentFlags().StringVar(&ingressIP, "ingressIP", "", "IP address of the ingress gateway") - configureCmd.PersistentFlags().BoolVar(&autoRegister, "autoregister", false, "Creates a WorkloadEntry upon connection to istiod (if enabled in pilot).") - configureCmd.PersistentFlags().BoolVar(&dnsCapture, "capture-dns", true, "Enables the capture of outgoing DNS packets on port 53, redirecting to istio-agent") - configureCmd.PersistentFlags().StringVar(&internalIP, "internalIP", "", "Internal IP address of the workload") - configureCmd.PersistentFlags().StringVar(&externalIP, "externalIP", "", "External IP address of the workload") - opts.AttachControlPlaneFlags(configureCmd) - return configureCmd -} - -// Reads a WorkloadGroup yaml. Additionally populates default values if unset -// TODO: add WorkloadGroup validation in pkg/config/validation -func readWorkloadGroup(filename string, wg *clientv1alpha3.WorkloadGroup) error { - f, err := os.ReadFile(filename) - if err != nil { - return err - } - if err = yaml.Unmarshal(f, wg); err != nil { - return err - } - // fill empty structs - if wg.Spec.Metadata == nil { - wg.Spec.Metadata = &networkingv1alpha3.WorkloadGroup_ObjectMeta{} - } - if wg.Spec.Template == nil { - wg.Spec.Template = &networkingv1alpha3.WorkloadEntry{} - } - // default service account for an empty field is "default" - if wg.Spec.Template.ServiceAccount == "" { - wg.Spec.Template.ServiceAccount = "default" - } - return nil -} - -// Creates all the relevant config for the given workload group and cluster -func createConfig(kubeClient kube.ExtendedClient, wg *clientv1alpha3.WorkloadGroup, clusterID, ingressIP, internalIP, - externalIP string, outputDir string, out io.Writer) error { - if err := os.MkdirAll(outputDir, filePerms); err != nil { - return err - } - var ( - err error - proxyConfig *meshconfig.ProxyConfig - ) - revision := kubeClient.Revision() - if proxyConfig, err = createMeshConfig(kubeClient, wg, clusterID, outputDir, revision); err != nil { - return err - } - if err := createClusterEnv(wg, proxyConfig, revision, internalIP, externalIP, outputDir); err != nil { - return err - } - if err := createCertsTokens(kubeClient, wg, outputDir, out); err != nil { - return err - } - if err := createHosts(kubeClient, ingressIP, outputDir, revision); err != nil { - return err - } - return nil -} - -// Write cluster.env into the given directory -func createClusterEnv(wg *clientv1alpha3.WorkloadGroup, config *meshconfig.ProxyConfig, revision, internalIP, externalIP, dir string) error { - we := wg.Spec.Template - ports := []string{} - for _, v := range we.Ports { - ports = append(ports, fmt.Sprint(v)) - } - // respect the inbound port annotation and capture all traffic if no inbound ports are set - portBehavior := "*" - if len(ports) > 0 { - portBehavior = strings.Join(ports, ",") - } - - // 22: ssh is extremely common for VMs, and we do not want to make VM unaccessible if there is an issue - // 15090: prometheus - // 15021/15020: agent - excludePorts := "22,15090,15021" - if config.StatusPort != 15090 && config.StatusPort != 15021 { - if config.StatusPort != 0 { - // Explicit status port set, use that - excludePorts += fmt.Sprintf(",%d", config.StatusPort) - } else { - // use default status port - excludePorts += ",15020" - } - } - // default attributes and service name, namespace, ports, service account, service CIDR - overrides := map[string]string{ - "ISTIO_INBOUND_PORTS": portBehavior, - "ISTIO_NAMESPACE": wg.Namespace, - "ISTIO_SERVICE": fmt.Sprintf("%s.%s", wg.Name, wg.Namespace), - "ISTIO_SERVICE_CIDR": "*", - "ISTIO_LOCAL_EXCLUDE_PORTS": excludePorts, - "SERVICE_ACCOUNT": we.ServiceAccount, - } - - if isRevisioned(revision) { - overrides["CA_ADDR"] = istiodAddr(revision) - } - if len(internalIP) > 0 { - overrides["ISTIO_SVC_IP"] = internalIP - } else if len(externalIP) > 0 { - overrides["ISTIO_SVC_IP"] = externalIP - overrides["REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION"] = "true" - } - - // clusterEnv will use proxyMetadata from the proxyConfig + overrides specific to the WorkloadGroup and cmd args - // this is similar to the way the injector sets all values proxyConfig.proxyMetadata to the Pod's env - clusterEnv := map[string]string{} - for _, metaMap := range []map[string]string{config.ProxyMetadata, overrides} { - for k, v := range metaMap { - clusterEnv[k] = v - } - } - - return os.WriteFile(filepath.Join(dir, "cluster.env"), []byte(mapToString(clusterEnv)), filePerms) -} - -// Get and store the needed certificate and token. The certificate comes from the CA root cert, and -// the token is generated by kubectl under the workload group's namespace and service account -// TODO: Make the following accurate when using the Kubernetes certificate signer -func createCertsTokens(kubeClient kube.ExtendedClient, wg *clientv1alpha3.WorkloadGroup, dir string, out io.Writer) error { - rootCert, err := kubeClient.CoreV1().ConfigMaps(wg.Namespace).Get(context.Background(), controller.CACertNamespaceConfigMap, metav1.GetOptions{}) - // errors if the requested configmap does not exist in the given namespace - if err != nil { - return fmt.Errorf("configmap %s was not found in namespace %s: %v", controller.CACertNamespaceConfigMap, wg.Namespace, err) - } - if err = os.WriteFile(filepath.Join(dir, "root-cert.pem"), []byte(rootCert.Data[constants.CACertNamespaceConfigMapDataName]), filePerms); err != nil { - return err - } - - serviceAccount := wg.Spec.Template.ServiceAccount - tokenPath := filepath.Join(dir, "istio-token") - jwtPolicy, err := util.DetectSupportedJWTPolicy(kubeClient) - if err != nil { - fmt.Fprintf(out, "Failed to determine JWT policy support: %v", err) - } - if jwtPolicy == util.FirstPartyJWT { - fmt.Fprintf(out, "Warning: cluster does not support third party JWT authentication. "+ - "Falling back to less secure first party JWT. "+ - "See "+url.ConfigureSAToken+" for details."+"\n") - sa, err := kubeClient.CoreV1().ServiceAccounts(wg.Namespace).Get(context.TODO(), serviceAccount, metav1.GetOptions{}) - if err != nil { - return err - } - secret, err := kubeClient.CoreV1().Secrets(wg.Namespace).Get(context.TODO(), sa.Secrets[0].Name, metav1.GetOptions{}) - if err != nil { - return err - } - if err := os.WriteFile(tokenPath, secret.Data["token"], filePerms); err != nil { - return err - } - fmt.Fprintf(out, "Warning: a security token for namespace %q and service account %q has been generated "+ - "and stored at %q\n", wg.Namespace, serviceAccount, tokenPath) - return nil - } - token := &authenticationv1.TokenRequest{ - // ObjectMeta isn't required in real k8s, but needed for tests - ObjectMeta: metav1.ObjectMeta{ - Name: serviceAccount, - Namespace: wg.Namespace, - }, - Spec: authenticationv1.TokenRequestSpec{ - Audiences: []string{"istio-ca"}, - ExpirationSeconds: &tokenDuration, - }, - } - tokenReq, err := kubeClient.CoreV1().ServiceAccounts(wg.Namespace).CreateToken(context.Background(), serviceAccount, token, metav1.CreateOptions{}) - // errors if the token could not be created with the given service account in the given namespace - if err != nil { - return fmt.Errorf("could not create a token under service account %s in namespace %s: %v", serviceAccount, wg.Namespace, err) - } - if err := os.WriteFile(tokenPath, []byte(tokenReq.Status.Token), filePerms); err != nil { - return err - } - fmt.Fprintf(out, "Warning: a security token for namespace %q and service account %q has been generated and "+ - "stored at %q\n", wg.Namespace, serviceAccount, tokenPath) - return nil -} - -func createMeshConfig(kubeClient kube.ExtendedClient, wg *clientv1alpha3.WorkloadGroup, clusterID, dir, revision string) (*meshconfig.ProxyConfig, error) { - istioCM := "istio" - // Case with multiple control planes - if isRevisioned(revision) { - istioCM = fmt.Sprintf("%s-%s", istioCM, revision) - } - istio, err := kubeClient.CoreV1().ConfigMaps(istioNamespace).Get(context.Background(), istioCM, metav1.GetOptions{}) - // errors if the requested configmap does not exist in the given namespace - if err != nil { - return nil, fmt.Errorf("configmap %s was not found in namespace %s: %v", istioCM, istioNamespace, err) - } - // fill some fields before applying the yaml to prevent errors later - meshConfig := &meshconfig.MeshConfig{ - DefaultConfig: &meshconfig.ProxyConfig{ - ProxyMetadata: map[string]string{}, - }, - } - if err := protomarshal.ApplyYAML(istio.Data[configMapKey], meshConfig); err != nil { - return nil, err - } - if isRevisioned(revision) && meshConfig.DefaultConfig.DiscoveryAddress == "" { - meshConfig.DefaultConfig.DiscoveryAddress = istiodAddr(revision) - } - - // performing separate map-merge, apply seems to completely overwrite all metadata - proxyMetadata := meshConfig.DefaultConfig.ProxyMetadata - - // support proxy.istio.io/config on the WorkloadGroup, in the WorkloadGroup spec - for _, annotations := range []map[string]string{wg.Annotations, wg.Spec.Metadata.Annotations} { - if pcYaml, ok := annotations[annotation.ProxyConfig.Name]; ok { - if err := protomarshal.ApplyYAML(pcYaml, meshConfig.DefaultConfig); err != nil { - return nil, err - } - for k, v := range meshConfig.DefaultConfig.ProxyMetadata { - proxyMetadata[k] = v - } - } - } - - meshConfig.DefaultConfig.ProxyMetadata = proxyMetadata - - lbls := map[string]string{} - for k, v := range wg.Spec.Metadata.Labels { - lbls[k] = v - } - // case where a user provided custom workload group has labels in the workload entry template field - we := wg.Spec.Template - if len(we.Labels) > 0 { - fmt.Printf("Labels should be set in the metadata. The following WorkloadEntry labels will override metadata labels: %s\n", we.Labels) - for k, v := range we.Labels { - lbls[k] = v - } - } - - meshConfig.DefaultConfig.ReadinessProbe = wg.Spec.Probe - - md := meshConfig.DefaultConfig.ProxyMetadata - if md == nil { - md = map[string]string{} - meshConfig.DefaultConfig.ProxyMetadata = md - } - md["CANONICAL_SERVICE"], md["CANONICAL_REVISION"] = labels.CanonicalService(lbls, wg.Name) - md["POD_NAMESPACE"] = wg.Namespace - md["SERVICE_ACCOUNT"] = we.ServiceAccount - md["TRUST_DOMAIN"] = meshConfig.TrustDomain - - md["ISTIO_META_CLUSTER_ID"] = clusterID - md["ISTIO_META_MESH_ID"] = meshConfig.DefaultConfig.MeshId - md["ISTIO_META_NETWORK"] = we.Network - if portsStr := marshalWorkloadEntryPodPorts(we.Ports); portsStr != "" { - md["ISTIO_META_POD_PORTS"] = portsStr - } - md["ISTIO_META_WORKLOAD_NAME"] = wg.Name - lbls[label.ServiceCanonicalName.Name] = md["CANONICAL_SERVICE"] - lbls[label.ServiceCanonicalRevision.Name] = md["CANONICAL_REVISION"] - if labelsJSON, err := json.Marshal(lbls); err == nil { - md["ISTIO_METAJSON_LABELS"] = string(labelsJSON) - } - - // TODO the defaults should be controlled by meshConfig/proxyConfig; if flags not given to the command proxyCOnfig takes precedence - if dnsCapture { - md["ISTIO_META_DNS_CAPTURE"] = strconv.FormatBool(dnsCapture) - } - if autoRegister { - md["ISTIO_META_AUTO_REGISTER_GROUP"] = wg.Name - } - - proxyConfig, err := protomarshal.ToJSONMap(meshConfig.DefaultConfig) - if err != nil { - return nil, err - } - - proxyYAML, err := yaml.Marshal(map[string]interface{}{"defaultConfig": proxyConfig}) - if err != nil { - return nil, err - } - - return meshConfig.DefaultConfig, os.WriteFile(filepath.Join(dir, "mesh.yaml"), proxyYAML, filePerms) -} - -func marshalWorkloadEntryPodPorts(p map[string]uint32) string { - var out []model.PodPort - for name, port := range p { - out = append(out, model.PodPort{Name: name, ContainerPort: int(port)}) - } - if len(out) == 0 { - return "" - } - sort.Slice(out, func(i, j int) bool { - return out[i].Name < out[j].Name - }) - str, err := json.Marshal(out) - if err != nil { - return "" - } - return string(str) -} - -// Retrieves the external IP of the ingress-gateway for the hosts file additions -func createHosts(kubeClient kube.ExtendedClient, ingressIP, dir string, revision string) error { - // try to infer the ingress IP if the provided one is invalid - if validation.ValidateIPAddress(ingressIP) != nil { - p := strings.Split(ingressSvc, ".") - ingressNs := istioNamespace - if len(p) == 2 { - ingressSvc = p[0] - ingressNs = p[1] - } - ingress, err := kubeClient.CoreV1().Services(ingressNs).Get(context.Background(), ingressSvc, metav1.GetOptions{}) - if err == nil { - if ingress.Status.LoadBalancer.Ingress != nil && len(ingress.Status.LoadBalancer.Ingress) > 0 { - ingressIP = ingress.Status.LoadBalancer.Ingress[0].IP - } else if len(ingress.Spec.ExternalIPs) > 0 { - ingressIP = ingress.Spec.ExternalIPs[0] - } - // TODO: add case where the load balancer is a DNS name - } - } - - var hosts string - if net.ParseIP(ingressIP) != nil { - hosts = fmt.Sprintf("%s %s\n", ingressIP, istiodHost(revision)) - } else { - log.Warnf("Could not auto-detect IP for. Use --ingressIP to manually specify the Gateway address to reach istiod from the VM.", - istiodHost(revision), istioNamespace) - } - return os.WriteFile(filepath.Join(dir, "hosts"), []byte(hosts), filePerms) -} - -func isRevisioned(revision string) bool { - return revision != "" && revision != "default" -} - -func istiodHost(revision string) string { - istiod := "istiod" - if isRevisioned(revision) { - istiod = fmt.Sprintf("%s-%s", istiod, revision) - } - return fmt.Sprintf("%s.%s.svc", istiod, istioNamespace) -} - -func istiodAddr(revision string) string { - // TODO make port configurable - return fmt.Sprintf("%s:%d", istiodHost(revision), 15012) -} - -// Returns a map with each k,v entry on a new line -func mapToString(m map[string]string) string { - lines := []string{} - for k, v := range m { - lines = append(lines, fmt.Sprintf("%s=%s", k, shellescape.Quote(v))) - } - sort.Strings(lines) - return strings.Join(lines, "\n") + "\n" -} - -// extractClusterIDFromInjectionConfig can extract clusterID from injection configmap -func extractClusterIDFromInjectionConfig(kubeClient kube.ExtendedClient) (string, error) { - injectionConfigMap := "istio-sidecar-injector" - // Case with multiple control planes - revision := kubeClient.Revision() - if isRevisioned(revision) { - injectionConfigMap = fmt.Sprintf("%s-%s", injectionConfigMap, revision) - } - istioInjectionCM, err := kubeClient.CoreV1().ConfigMaps(istioNamespace).Get(context.Background(), injectionConfigMap, metav1.GetOptions{}) - if err != nil { - return "", fmt.Errorf("fetch injection template: %v", err) - } - - var injectedCMValues map[string]interface{} - if err := json.Unmarshal([]byte(istioInjectionCM.Data[valuesConfigMapKey]), &injectedCMValues); err != nil { - return "", err - } - v, f, err := tpath.GetFromStructPath(injectedCMValues, "global.multiCluster.clusterName") - if err != nil { - return "", err - } - vs, ok := v.(string) - if !f || !ok { - return "", fmt.Errorf("could not retrieve global.multiCluster.clusterName from injection config") - } - return vs, nil -} diff --git a/istioctl/cmd/workload_test.go b/istioctl/cmd/workload_test.go deleted file mode 100644 index b1e473bba..000000000 --- a/istioctl/cmd/workload_test.go +++ /dev/null @@ -1,364 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "bytes" - "fmt" - "os" - "path" - "strings" - "testing" -) - -import ( - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes/fake" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/test/util" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -var fakeCACert = []byte("fake-CA-cert") - -var ( - defaultYAML = `apiVersion: networking.istio.io/v1alpha3 -kind: WorkloadGroup -metadata: - name: foo - namespace: bar -spec: - metadata: {} - template: - serviceAccount: default -` - - customYAML = `apiVersion: networking.istio.io/v1alpha3 -kind: WorkloadGroup -metadata: - name: foo - namespace: bar -spec: - metadata: - annotations: - annotation: foobar - labels: - app: foo - bar: baz - template: - ports: - grpc: 3550 - http: 8080 - serviceAccount: test -` -) - -func TestWorkloadGroupCreate(t *testing.T) { - cases := []testcase{ - { - description: "Invalid command args - missing service name and namespace", - args: strings.Split("experimental workload group create", " "), - expectedException: true, - expectedOutput: "Error: expecting a workload name\n", - }, - { - description: "Invalid command args - missing service name", - args: strings.Split("experimental workload group create --namespace bar", " "), - expectedException: true, - expectedOutput: "Error: expecting a workload name\n", - }, - { - description: "Invalid command args - missing service namespace", - args: strings.Split("experimental workload group create --name foo", " "), - expectedException: true, - expectedOutput: "Error: expecting a workload namespace\n", - }, - { - description: "valid case - minimal flags, infer defaults", - args: strings.Split("experimental workload group create --name foo --namespace bar", " "), - expectedException: false, - expectedOutput: defaultYAML, - }, - { - description: "valid case - create full workload group", - args: strings.Split("experimental workload group create --name foo --namespace bar --labels app=foo,bar=baz "+ - " --annotations annotation=foobar --ports grpc=3550,http=8080 --serviceAccount test", " "), - expectedException: false, - expectedOutput: customYAML, - }, - { - description: "valid case - create full workload group with shortnames", - args: strings.Split("experimental workload group create --name foo -n bar -l app=foo,bar=baz -p grpc=3550,http=8080"+ - " -a annotation=foobar --serviceAccount test", " "), - expectedException: false, - expectedOutput: customYAML, - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("case %d %s", i, c.description), func(t *testing.T) { - verifyAddToMeshOutput(t, c) - }) - } -} - -func TestWorkloadEntryConfigureInvalidArgs(t *testing.T) { - cases := []testcase{ - { - description: "Invalid command args - missing valid input spec", - args: strings.Split("experimental workload entry configure --name foo -o temp --clusterID cid", " "), - expectedException: true, - expectedOutput: "Error: expecting a WorkloadGroup artifact file or the name and namespace of an existing WorkloadGroup\n", - }, - { - description: "Invalid command args - missing valid input spec", - args: strings.Split("experimental workload entry configure -n bar -o temp --clusterID cid", " "), - expectedException: true, - expectedOutput: "Error: expecting a WorkloadGroup artifact file or the name and namespace of an existing WorkloadGroup\n", - }, - { - description: "Invalid command args - valid filename input but missing output filename", - args: strings.Split("experimental workload entry configure -f file --clusterID cid", " "), - expectedException: true, - expectedOutput: "Error: expecting an output directory\n", - }, - { - description: "Invalid command args - valid kubectl input but missing output filename", - args: strings.Split("experimental workload entry configure --name foo -n bar --clusterID cid", " "), - expectedException: true, - expectedOutput: "Error: expecting an output directory\n", - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("case %d %s", i, c.description), func(t *testing.T) { - verifyAddToMeshOutput(t, c) - }) - } -} - -const goldenSuffix = ".golden" - -// TestWorkloadEntryConfigure enumerates test cases based on subdirectories of testdata/vmconfig. -// Each subdirectory contains two input files: workloadgroup.yaml and meshconfig.yaml that are used -// to generate golden outputs from the VM command. -func TestWorkloadEntryConfigure(t *testing.T) { - noClusterID := "failed to automatically determine the --clusterID" - files, err := os.ReadDir("testdata/vmconfig") - if err != nil { - t.Fatal(err) - } - for _, dir := range files { - if !dir.IsDir() { - continue - } - t.Run(dir.Name(), func(t *testing.T) { - testdir := path.Join("testdata/vmconfig", dir.Name()) - kubeClientWithRevision = func(_, _, _ string) (kube.ExtendedClient, error) { - return &kube.MockClient{ - RevisionValue: "rev-1", - Interface: fake.NewSimpleClientset( - &v1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{Namespace: "bar", Name: "vm-serviceaccount"}, - Secrets: []v1.ObjectReference{{Name: "test"}}, - }, - &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Namespace: "bar", Name: "istio-ca-root-cert"}, - Data: map[string]string{"root-cert.pem": string(fakeCACert)}, - }, - &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Namespace: "dubbo-system", Name: "istio-rev-1"}, - Data: map[string]string{ - "mesh": string(util.ReadFile(t, path.Join(testdir, "meshconfig.yaml"))), - }, - }, - &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{Namespace: "bar", Name: "test"}, - Data: map[string][]byte{ - "token": {}, - }, - }, - ), - }, nil - } - - cmdWithClusterID := []string{ - "x", "workload", "entry", "configure", - "-f", path.Join("testdata/vmconfig", dir.Name(), "workloadgroup.yaml"), - "--internalIP", "10.10.10.10", - "--clusterID", "Kubernetes", - "-o", testdir, - } - if _, err := runTestCmd(t, cmdWithClusterID); err != nil { - t.Fatal(err) - } - - cmdNoClusterID := []string{ - "x", "workload", "entry", "configure", - "-f", path.Join("testdata/vmconfig", dir.Name(), "workloadgroup.yaml"), - "--internalIP", "10.10.10.10", - "-o", testdir, - } - if output, err := runTestCmd(t, cmdNoClusterID); err != nil { - if !strings.Contains(output, noClusterID) { - t.Fatal(err) - } - } - - checkFiles := map[string]bool{ - // outputs to check - "mesh.yaml": true, "istio-token": true, "hosts": true, "root-cert.pem": true, "cluster.env": true, - // inputs that we allow to exist, if other files seep in unexpectedly we fail the test - ".gitignore": false, "meshconfig.yaml": false, "workloadgroup.yaml": false, - } - - checkOutputFiles(t, testdir, checkFiles) - }) - } -} - -func TestWorkloadEntryToPodPortsMeta(t *testing.T) { - cases := []struct { - description string - ports map[string]uint32 - want string - }{ - { - description: "test json marshal", - ports: map[string]uint32{ - "HTTP": 80, - "HTTPS": 443, - }, - want: `[{"name":"HTTP","containerPort":80,"protocol":""},{"name":"HTTPS","containerPort":443,"protocol":""}]`, - }, - } - for i, c := range cases { - t.Run(fmt.Sprintf("case %d %s", i, c.description), func(t *testing.T) { - str := marshalWorkloadEntryPodPorts(c.ports) - if c.want != str { - t.Errorf("want %s, got %s", c.want, str) - } - }) - } -} - -// TestWorkloadEntryConfigureNilProxyMetadata tests a particular use case when the -// proxyMetadata is nil, no metadata would be generated at all. -func TestWorkloadEntryConfigureNilProxyMetadata(t *testing.T) { - testdir := "testdata/vmconfig-nil-proxy-metadata" - noClusterID := "failed to automatically determine the --clusterID" - - kubeClientWithRevision = func(_, _, _ string) (kube.ExtendedClient, error) { - return &kube.MockClient{ - Interface: fake.NewSimpleClientset( - &v1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{Namespace: "bar", Name: "vm-serviceaccount"}, - Secrets: []v1.ObjectReference{{Name: "test"}}, - }, - &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Namespace: "bar", Name: "istio-ca-root-cert"}, - Data: map[string]string{"root-cert.pem": string(fakeCACert)}, - }, - &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Namespace: "dubbo-system", Name: "istio"}, - Data: map[string]string{ - "mesh": "defaultConfig: {}", - }, - }, - &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{Namespace: "bar", Name: "test"}, - Data: map[string][]byte{ - "token": {}, - }, - }, - ), - }, nil - } - - cmdWithClusterID := []string{ - "x", "workload", "entry", "configure", - "-f", path.Join(testdir, "workloadgroup.yaml"), - "--internalIP", "10.10.10.10", - "--clusterID", "Kubernetes", - "-o", testdir, - } - if output, err := runTestCmd(t, cmdWithClusterID); err != nil { - t.Logf("output: %v", output) - t.Fatal(err) - } - - cmdNoClusterID := []string{ - "x", "workload", "entry", "configure", - "-f", path.Join(testdir, "workloadgroup.yaml"), - "--internalIP", "10.10.10.10", - "-o", testdir, - } - if output, err := runTestCmd(t, cmdNoClusterID); err != nil { - if !strings.Contains(output, noClusterID) { - t.Fatal(err) - } - } - - checkFiles := map[string]bool{ - // outputs to check - "mesh.yaml": true, "istio-token": true, "hosts": true, "root-cert.pem": true, "cluster.env": true, - // inputs that we allow to exist, if other files seep in unexpectedly we fail the test - ".gitignore": false, "workloadgroup.yaml": false, - } - - checkOutputFiles(t, testdir, checkFiles) -} - -func runTestCmd(t *testing.T, args []string) (string, error) { - t.Helper() - // TODO there is already probably something else that does this - var out bytes.Buffer - rootCmd := GetRootCmd(args) - rootCmd.SetOut(&out) - rootCmd.SetErr(&out) - err := rootCmd.Execute() - output := out.String() - return output, err -} - -func checkOutputFiles(t *testing.T, testdir string, checkFiles map[string]bool) { - t.Helper() - - outputFiles, err := os.ReadDir(testdir) - if err != nil { - t.Fatal(err) - } - - for _, f := range outputFiles { - checkGolden, ok := checkFiles[f.Name()] - if !ok { - if checkGolden, ok := checkFiles[f.Name()[:len(f.Name())-len(goldenSuffix)]]; !(checkGolden && ok) { - t.Errorf("unexpected file in output dir: %s", f.Name()) - } - continue - } - if checkGolden { - t.Run(f.Name(), func(t *testing.T) { - contents := util.ReadFile(t, path.Join(testdir, f.Name())) - goldenFile := path.Join(testdir, f.Name()+goldenSuffix) - util.RefreshGoldenFile(t, contents, goldenFile) - util.CompareContent(t, contents, goldenFile) - }) - } - } -} diff --git a/istioctl/docker/Dockerfile.istioctl b/istioctl/docker/Dockerfile.istioctl deleted file mode 100644 index 88ba15bdf..000000000 --- a/istioctl/docker/Dockerfile.istioctl +++ /dev/null @@ -1,40 +0,0 @@ -FROM ubuntu:jammy - -ENV DEBIAN_FRONTEND=noninteractive - -# Do not add more stuff to this list that isn't small or critically useful. -# If you occasionally need something on the container do -# sudo apt-get update && apt-get whichever - -# hadolint ignore=DL3005,DL3008 -RUN apt-get update && \ - apt-get install --no-install-recommends -y \ - ca-certificates \ - curl \ - iptables \ - iproute2 \ - iputils-ping \ - knot-dnsutils \ - netcat \ - tcpdump \ - conntrack \ - bsdmainutils \ - net-tools \ - lsof \ - sudo \ - && update-ca-certificates \ - && apt-get upgrade -y \ - && apt-get clean \ - && rm -rf /var/log/*log /var/lib/apt/lists/* /var/log/apt/* /var/lib/dpkg/*-old /var/cache/debconf/*-old \ - && update-alternatives --set iptables /usr/sbin/iptables-legacy \ - && update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy - -# Sudoers used to allow tcpdump and other debug utilities. -RUN useradd -m --uid 1337 istio-proxy && \ - echo "istio-proxy ALL=NOPASSWD: ALL" >> /etc/sudoers - -# Version is the base image version from the TLD Makefile -USER 1000:1000 -ARG TARGETARCH -COPY ${TARGETARCH:-amd64}/istioctl /usr/local/bin/istioctl -ENTRYPOINT ["/usr/local/bin/istioctl"] diff --git a/istioctl/pkg/authz/analyzer.go b/istioctl/pkg/authz/analyzer.go deleted file mode 100644 index f3c46af44..000000000 --- a/istioctl/pkg/authz/analyzer.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// The auth package provides support for checking the authentication and authorization policy applied -// in the mesh. It aims to increase the debuggability and observability of auth policies. -// Note: this is still under active development and is not ready for real use. -package authz - -import ( - "fmt" - "io" -) - -import ( - envoy_admin "github.com/envoyproxy/go-control-plane/envoy/admin/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/configdump" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" -) - -// Analyzer that can be used to check authorization policy. -type Analyzer struct { - listenerDump *envoy_admin.ListenersConfigDump -} - -// NewAnalyzer creates a new analyzer for a given pod based on its envoy config. -func NewAnalyzer(envoyConfig *configdump.Wrapper) (*Analyzer, error) { - listeners, err := envoyConfig.GetDynamicListenerDump(true) - if err != nil { - return nil, fmt.Errorf("failed to get dynamic listener dump: %s", err) - } - - return &Analyzer{listenerDump: listeners}, nil -} - -// Print print sthe analyze results. -func (a *Analyzer) Print(writer io.Writer) { - var listeners []*listener.Listener - for _, l := range a.listenerDump.DynamicListeners { - listenerTyped := &listener.Listener{} - // Support v2 or v3 in config dump. See ads.go:RequestedTypes for more info. - l.ActiveState.Listener.TypeUrl = v3.ListenerType - err := l.ActiveState.Listener.UnmarshalTo(listenerTyped) - if err != nil { - return - } - listeners = append(listeners, listenerTyped) - } - Print(writer, listeners) -} diff --git a/istioctl/pkg/authz/listener.go b/istioctl/pkg/authz/listener.go deleted file mode 100644 index 1280b5b49..000000000 --- a/istioctl/pkg/authz/listener.go +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package authz - -import ( - "fmt" - "io" - "regexp" - "strings" - "text/tabwriter" -) - -import ( - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - rbacpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3" - rbac_http_filter "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/rbac/v3" - hcm_filter "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - rbac_tcp_filter "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/rbac/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "google.golang.org/protobuf/proto" - "istio.io/pkg/log" -) - -const ( - anonymousName = "_anonymous_match_nothing_" -) - -// Matches the policy name in RBAC filter config with format like ns[default]-policy[some-policy]-rule[1]. -var re = regexp.MustCompile(`ns\[(.+)\]-policy\[(.+)\]-rule\[(.+)\]`) - -type filterChain struct { - rbacHTTP []*rbac_http_filter.RBAC - rbacTCP []*rbac_tcp_filter.RBAC -} - -type parsedListener struct { - filterChains []*filterChain -} - -func getFilterConfig(filter *listener.Filter, out proto.Message) error { - switch c := filter.ConfigType.(type) { - case *listener.Filter_TypedConfig: - if err := c.TypedConfig.UnmarshalTo(out); err != nil { - return err - } - } - return nil -} - -func getHTTPConnectionManager(filter *listener.Filter) *hcm_filter.HttpConnectionManager { - cm := &hcm_filter.HttpConnectionManager{} - if err := getFilterConfig(filter, cm); err != nil { - log.Errorf("failed to get HTTP connection manager config: %s", err) - return nil - } - return cm -} - -func getHTTPFilterConfig(filter *hcm_filter.HttpFilter, out proto.Message) error { - switch c := filter.ConfigType.(type) { - case *hcm_filter.HttpFilter_TypedConfig: - if err := c.TypedConfig.UnmarshalTo(out); err != nil { - return err - } - } - return nil -} - -func parse(listeners []*listener.Listener) []*parsedListener { - var parsedListeners []*parsedListener - for _, l := range listeners { - parsed := &parsedListener{} - for _, fc := range l.FilterChains { - parsedFC := &filterChain{} - for _, filter := range fc.Filters { - switch filter.Name { - case wellknown.HTTPConnectionManager, "envoy.http_connection_manager": - if cm := getHTTPConnectionManager(filter); cm != nil { - for _, httpFilter := range cm.GetHttpFilters() { - switch httpFilter.GetName() { - case wellknown.HTTPRoleBasedAccessControl: - rbacHTTP := &rbac_http_filter.RBAC{} - if err := getHTTPFilterConfig(httpFilter, rbacHTTP); err != nil { - log.Errorf("found RBAC HTTP filter but failed to parse: %s", err) - } else { - parsedFC.rbacHTTP = append(parsedFC.rbacHTTP, rbacHTTP) - } - } - } - } - case wellknown.RoleBasedAccessControl: - rbacTCP := &rbac_tcp_filter.RBAC{} - if err := getFilterConfig(filter, rbacTCP); err != nil { - log.Errorf("found RBAC network filter but failed to parse: %s", err) - } else { - parsedFC.rbacTCP = append(parsedFC.rbacTCP, rbacTCP) - } - } - } - - parsed.filterChains = append(parsed.filterChains, parsedFC) - } - parsedListeners = append(parsedListeners, parsed) - } - return parsedListeners -} - -func extractName(name string) (string, string) { - // parts[1] is the namespace, parts[2] is the policy name, parts[3] is the rule index. - parts := re.FindStringSubmatch(name) - if len(parts) != 4 { - log.Errorf("failed to parse policy name: %s", name) - return "", "" - } - return fmt.Sprintf("%s.%s", parts[2], parts[1]), parts[3] -} - -// Print prints the AuthorizationPolicy in the listener. -func Print(writer io.Writer, listeners []*listener.Listener) { - parsedListeners := parse(listeners) - if parsedListeners == nil { - return - } - - actionToPolicy := map[rbacpb.RBAC_Action]map[string]struct{}{} - policyToRule := map[string]map[string]struct{}{} - - addPolicy := func(action rbacpb.RBAC_Action, name string, rule string) { - if actionToPolicy[action] == nil { - actionToPolicy[action] = map[string]struct{}{} - } - if policyToRule[name] == nil { - policyToRule[name] = map[string]struct{}{} - } - actionToPolicy[action][name] = struct{}{} - policyToRule[name][rule] = struct{}{} - } - - for _, parsed := range parsedListeners { - for _, fc := range parsed.filterChains { - for _, rbacHTTP := range fc.rbacHTTP { - action := rbacHTTP.GetRules().GetAction() - for name := range rbacHTTP.GetRules().GetPolicies() { - nameOfPolicy, indexOfRule := extractName(name) - addPolicy(action, nameOfPolicy, indexOfRule) - } - if len(rbacHTTP.GetRules().GetPolicies()) == 0 { - addPolicy(action, anonymousName, "0") - } - } - for _, rbacTCP := range fc.rbacTCP { - action := rbacTCP.GetRules().GetAction() - for name := range rbacTCP.GetRules().GetPolicies() { - nameOfPolicy, indexOfRule := extractName(name) - addPolicy(action, nameOfPolicy, indexOfRule) - } - if len(rbacTCP.GetRules().GetPolicies()) == 0 { - addPolicy(action, anonymousName, "0") - } - } - } - } - - buf := strings.Builder{} - buf.WriteString("ACTION\tAuthorizationPolicy\tRULES\n") - for _, action := range []rbacpb.RBAC_Action{rbacpb.RBAC_DENY, rbacpb.RBAC_ALLOW, rbacpb.RBAC_LOG} { - if names, ok := actionToPolicy[action]; ok { - for name := range names { - buf.WriteString(fmt.Sprintf("%s\t%s\t%d\n", action, name, len(policyToRule[name]))) - } - } - } - - w := new(tabwriter.Writer).Init(writer, 0, 8, 3, ' ', 0) - if _, err := fmt.Fprint(w, buf.String()); err != nil { - log.Errorf("failed to print output: %s", err) - } - _ = w.Flush() -} diff --git a/istioctl/pkg/clioptions/central.go b/istioctl/pkg/clioptions/central.go deleted file mode 100644 index 07ac28b22..000000000 --- a/istioctl/pkg/clioptions/central.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package clioptions - -import ( - "fmt" - "time" -) - -import ( - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -// CentralControlPlaneOptions holds options common to all subcommands -// that invoke Istiod via xDS REST endpoint -type CentralControlPlaneOptions struct { - // Xds is XDS endpoint, e.g. localhost:15010. - Xds string - - // XdsPodLabel is a Kubernetes label on the Istiod pods - XdsPodLabel string - - // XdsPodPort is a port exposing XDS (typically 15010 or 15012) - XdsPodPort int - - // CertDir is the local directory containing certificates - CertDir string - - // Timeout is how long to wait before giving up on XDS - Timeout time.Duration - - // InsecureSkipVerify skips client verification the server's certificate chain and host name. - InsecureSkipVerify bool - - // XDSSAN is the expected Subject Alternative Name of the XDS server - XDSSAN string - - // Plaintext forces plain text communication (for talking to port 15010) - Plaintext bool - - // GCP project number or ID to use for XDS calls, if any. - GCPProject string - - // Istiod address. For MCP may be different than Xds. - IstiodAddr string -} - -// AttachControlPlaneFlags attaches control-plane flags to a Cobra command. -// (Currently just --endpoint) -func (o *CentralControlPlaneOptions) AttachControlPlaneFlags(cmd *cobra.Command) { - cmd.PersistentFlags().StringVar(&o.Xds, "xds-address", viper.GetString("XDS-ADDRESS"), - "XDS Endpoint") - cmd.PersistentFlags().StringVar(&o.CertDir, "cert-dir", viper.GetString("CERT-DIR"), - "XDS Endpoint certificate directory") - cmd.PersistentFlags().StringVar(&o.XdsPodLabel, "xds-label", "", - "Istiod pod label selector") - cmd.PersistentFlags().IntVar(&o.XdsPodPort, "xds-port", viper.GetInt("XDS-PORT"), - "Istiod pod port") - cmd.PersistentFlags().DurationVar(&o.Timeout, "timeout", time.Second*30, - "The duration to wait before failing") - cmd.PersistentFlags().StringVar(&o.XDSSAN, "authority", viper.GetString("AUTHORITY"), - "XDS Subject Alternative Name (for example istiod.dubbo-system.svc)") - cmd.PersistentFlags().BoolVar(&o.InsecureSkipVerify, "insecure", viper.GetBool("INSECURE"), - "Skip server certificate and domain verification. (NOT SECURE!)") - cmd.PersistentFlags().BoolVar(&o.Plaintext, "plaintext", viper.GetBool("PLAINTEXT"), - "Use plain-text HTTP/2 when connecting to server (no TLS).") -} - -// ValidateControlPlaneFlags checks arguments for valid values and combinations -func (o *CentralControlPlaneOptions) ValidateControlPlaneFlags() error { - if o.Xds != "" && o.XdsPodLabel != "" { - return fmt.Errorf("either --xds-address or --xds-label, not both") - } - if o.Plaintext && o.CertDir != "" { - return fmt.Errorf("either --plaintext or --cert-dir, not both") - } - return nil -} diff --git a/istioctl/pkg/clioptions/control_plane.go b/istioctl/pkg/clioptions/control_plane.go deleted file mode 100644 index 7dc05c122..000000000 --- a/istioctl/pkg/clioptions/control_plane.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package clioptions - -import ( - "github.com/spf13/cobra" -) - -// ControlPlaneOptions defines common options used by istioctl. -type ControlPlaneOptions struct { - // Revision is the istio.io/rev control plane revision - Revision string -} - -// AttachControlPlaneFlags attaches control-plane flags to a Cobra command. -// (Currently just --revision) -func (o *ControlPlaneOptions) AttachControlPlaneFlags(cmd *cobra.Command) { - cmd.PersistentFlags().StringVarP(&o.Revision, "revision", "r", "", - "Control plane revision") -} diff --git a/istioctl/pkg/clioptions/doc.go b/istioctl/pkg/clioptions/doc.go deleted file mode 100644 index 30d37b86d..000000000 --- a/istioctl/pkg/clioptions/doc.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package clioptions contains flags which can be added to istiocl commands. -package clioptions diff --git a/istioctl/pkg/install/k8sversion/version.go b/istioctl/pkg/install/k8sversion/version.go deleted file mode 100644 index 8dc044809..000000000 --- a/istioctl/pkg/install/k8sversion/version.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package k8sversion - -import ( - "fmt" -) - -import ( - goversion "github.com/hashicorp/go-version" - pkgVersion "istio.io/pkg/version" - "k8s.io/apimachinery/pkg/version" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -const ( - // MinK8SVersion is the minimum k8s version required to run this version of Istio - // https://istio.io/docs/setup/platform-setup/ - MinK8SVersion = 19 - UnSupportedK8SVersionLogMsg = "\nThe Kubernetes version %s is not supported by Istio %s. The minimum supported Kubernetes version is 1.%d.\n" + - "Proceeding with the installation, but you might experience problems. " + - "See https://istio.io/latest/docs/setup/platform-setup/ for a list of supported versions.\n" -) - -// CheckKubernetesVersion checks if this Istio version is supported in the k8s version -func CheckKubernetesVersion(versionInfo *version.Info) (bool, error) { - v, err := extractKubernetesVersion(versionInfo) - if err != nil { - return false, err - } - return MinK8SVersion <= v, nil -} - -// extractKubernetesVersion returns the Kubernetes minor version. For example, `v1.19.1` will return `19` -func extractKubernetesVersion(versionInfo *version.Info) (int, error) { - ver, err := goversion.NewVersion(versionInfo.String()) - if err != nil { - return 0, fmt.Errorf("could not parse %v", err) - } - // Segments provide slice of int eg: v1.19.1 => [1, 19, 1] - num := ver.Segments()[1] - return num, nil -} - -// IsK8VersionSupported checks minimum supported Kubernetes version for Istio. -// If the K8s version is not atleast the `MinK8SVersion`, it logs a message warning the user that they -// may experience problems if they proceed with the install. -func IsK8VersionSupported(c kube.Client, l clog.Logger) error { - serverVersion, err := c.GetKubernetesVersion() - if err != nil { - return fmt.Errorf("error getting Kubernetes version: %w", err) - } - if !kube.IsAtLeastVersion(c, MinK8SVersion) { - l.LogAndPrintf(UnSupportedK8SVersionLogMsg, serverVersion.GitVersion, pkgVersion.Info.Version, MinK8SVersion) - } - return nil -} diff --git a/istioctl/pkg/install/k8sversion/version_test.go b/istioctl/pkg/install/k8sversion/version_test.go deleted file mode 100644 index 555192be4..000000000 --- a/istioctl/pkg/install/k8sversion/version_test.go +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package k8sversion - -import ( - "bytes" - "fmt" - "strings" - "testing" -) - -import ( - pkgVersion "istio.io/pkg/version" - "k8s.io/apimachinery/pkg/version" - fakediscovery "k8s.io/client-go/discovery/fake" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -var ( - version1_17 = &version.Info{ - Major: "1", - Minor: "17", - GitVersion: "1.17", - } - version1_8 = &version.Info{ - Major: "1", - Minor: "8", - GitVersion: "v1.8", - } - version1_18 = &version.Info{ - Major: "1", - Minor: "18", - GitVersion: "v1.18.5", - } - version1_19 = &version.Info{ - Major: "1", - Minor: "19", - GitVersion: "v1.19.4", - } - version1_20 = &version.Info{ - Major: "1", - Minor: "20", - GitVersion: "v1.20.2", - } - version1_19RC = &version.Info{ - Major: "1", - Minor: "19", - GitVersion: "v1.19.5-rc.0", - } - version1_17GKE = &version.Info{ - Major: "1", - Minor: "17+", - GitVersion: "v1.17.7-gke.10", - } - version1_8GKE = &version.Info{ - Major: "1", - Minor: "8", - GitVersion: "v1.8.7-gke.8", - } - versionInvalid1 = &version.Info{ - Major: "1", - Minor: "7", - GitVersion: "v1.invalid.7", - } - versionInvalid2 = &version.Info{ - Major: "one", - Minor: "seven", - GitVersion: "one.seven", - } -) - -func TestExtractKubernetesVersion(t *testing.T) { - cases := []struct { - version *version.Info - expected int - errMsg error - isValid bool - }{ - { - version: version1_17, - expected: 17, - errMsg: nil, - isValid: true, - }, - { - version: version1_8, - expected: 8, - errMsg: nil, - isValid: true, - }, - { - version: version1_18, - expected: 18, - errMsg: nil, - isValid: true, - }, - { - version: version1_19, - expected: 19, - errMsg: nil, - isValid: true, - }, - { - version: version1_20, - expected: 20, - errMsg: nil, - isValid: true, - }, - { - version: version1_19RC, - expected: 19, - errMsg: nil, - isValid: true, - }, - { - version: version1_17GKE, - expected: 17, - errMsg: nil, - isValid: true, - }, - { - version: version1_8GKE, - expected: 8, - errMsg: nil, - isValid: true, - }, - { - version: versionInvalid1, - errMsg: fmt.Errorf("could not parse Malformed version: %v", versionInvalid1.GitVersion), - isValid: false, - }, - { - version: versionInvalid2, - errMsg: fmt.Errorf("could not parse Malformed version: %v", versionInvalid2.GitVersion), - isValid: false, - }, - } - for i, c := range cases { - t.Run(fmt.Sprintf("case %d %s", i, c.version), func(t *testing.T) { - got, err := extractKubernetesVersion(c.version) - if c.errMsg != err && c.isValid { - t.Fatalf("\nwanted: %v \nbut found: %v", c.errMsg, err) - } - if got != c.expected { - t.Fatalf("wanted %v got %v", c.expected, got) - } - }) - } -} - -func TestIsK8VersionSupported(t *testing.T) { - cases := []struct { - version *version.Info - logMsg string - isValid bool - }{ - { - version: version1_18, - logMsg: fmt.Sprintf(UnSupportedK8SVersionLogMsg, version1_18.GitVersion, pkgVersion.Info.Version, MinK8SVersion), - isValid: false, - }, - { - version: version1_8, - logMsg: fmt.Sprintf(UnSupportedK8SVersionLogMsg, version1_8.GitVersion, pkgVersion.Info.Version, MinK8SVersion), - isValid: false, - }, - { - version: version1_17GKE, - logMsg: fmt.Sprintf(UnSupportedK8SVersionLogMsg, version1_17GKE.GitVersion, pkgVersion.Info.Version, MinK8SVersion), - isValid: false, - }, - { - version: versionInvalid1, - logMsg: fmt.Sprintf(UnSupportedK8SVersionLogMsg, versionInvalid1.GitVersion, pkgVersion.Info.Version, MinK8SVersion), - isValid: false, - }, - { - version: version1_20, - isValid: true, - }, - } - - var outBuf bytes.Buffer - var errBuf bytes.Buffer - - for i, c := range cases { - t.Run(fmt.Sprintf("case %d %s", i, c.version), func(t *testing.T) { - k8sClient := kube.NewFakeClient() - k8sClient.Discovery().(*fakediscovery.FakeDiscovery).FakedServerVersion = c.version - - logger := clog.NewConsoleLogger(&outBuf, &errBuf, nil) - IsK8VersionSupported(k8sClient, logger) - - errMsgTrim := strings.TrimSpace(c.logMsg) - outBufTrim := strings.TrimSpace(outBuf.String()) - - if !c.isValid && strings.Compare(errMsgTrim, outBufTrim) != 0 { - t.Fatalf("\nwanted: %v \nbut found: %v", errMsgTrim, outBufTrim) - } - - if c.isValid && outBuf.Len() > 0 { - t.Fatalf("\nwanted: %v \nbut found: %v", errMsgTrim, outBufTrim) - } - outBuf.Reset() - }) - } -} diff --git a/istioctl/pkg/install/verify.go b/istioctl/pkg/install/verify.go deleted file mode 100644 index ea4335f2f..000000000 --- a/istioctl/pkg/install/verify.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package install - -import ( - "fmt" -) - -import ( - "github.com/spf13/cobra" - "k8s.io/cli-runtime/pkg/genericclioptions" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/clioptions" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/formatting" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/verifier" - "github.com/apache/dubbo-go-pixiu/operator/cmd/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" -) - -// NewVerifyCommand creates a new command for verifying Istio Installation Status -func NewVerifyCommand() *cobra.Command { - var ( - kubeConfigFlags = &genericclioptions.ConfigFlags{ - Context: strPtr(""), - Namespace: strPtr(""), - KubeConfig: strPtr(""), - } - - filenames = []string{} - istioNamespace string - opts clioptions.ControlPlaneOptions - manifestsPath string - ) - verifyInstallCmd := &cobra.Command{ - Use: "verify-install [-f ] [--revision ]", - Short: "Verifies Istio Installation Status", - Long: ` -verify-install verifies Istio installation status against the installation file -you specified when you installed Istio. It loops through all the installation -resources defined in your installation file and reports whether all of them are -in ready status. It will report failure when any of them are not ready. - -If you do not specify an installation it will check for an IstioOperator resource -and will verify if pods and services defined in it are present. - -Note: For verifying whether your cluster is ready for Istio installation, see -istioctl experimental precheck. -`, - Example: ` # Verify that Istio is installed correctly via Istio Operator - istioctl verify-install - - # Verify the deployment matches a custom Istio deployment configuration - istioctl verify-install -f $HOME/istio.yaml - - # Verify the deployment matches the Istio Operator deployment definition - istioctl verify-install --revision - - # Verify the installation of specific revision - istioctl verify-install -r 1-9-0`, - Args: func(cmd *cobra.Command, args []string) error { - if len(filenames) > 0 && opts.Revision != "" { - cmd.Println(cmd.UsageString()) - return fmt.Errorf("supply either a file or revision, but not both") - } - return nil - }, - RunE: func(c *cobra.Command, args []string) error { - installationVerifier, err := verifier.NewStatusVerifier(istioNamespace, manifestsPath, - *kubeConfigFlags.KubeConfig, *kubeConfigFlags.Context, filenames, opts) - if err != nil { - return err - } - if formatting.IstioctlColorDefault(c.OutOrStdout()) { - installationVerifier.Colorize() - } - return installationVerifier.Verify() - }, - } - - flags := verifyInstallCmd.PersistentFlags() - flags.StringVarP(&istioNamespace, "istioNamespace", "i", constants.IstioSystemNamespace, - "Istio system namespace") - kubeConfigFlags.AddFlags(flags) - flags.StringSliceVarP(&filenames, "filename", "f", filenames, "Istio YAML installation file.") - verifyInstallCmd.PersistentFlags().StringVarP(&manifestsPath, "manifests", "d", "", mesh.ManifestsFlagHelpStr) - opts.AttachControlPlaneFlags(verifyInstallCmd) - return verifyInstallCmd -} - -func strPtr(val string) *string { - return &val -} diff --git a/istioctl/pkg/multicluster/cluster.go b/istioctl/pkg/multicluster/cluster.go deleted file mode 100644 index c4edc73c5..000000000 --- a/istioctl/pkg/multicluster/cluster.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package multicluster - -import ( - "context" -) - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/kubernetes" -) - -const ( - defaultIstioNamespace = "dubbo-system" -) - -// Use UUID of kube-system Namespace as unique identifier for cluster. -// (see https://docs.google.com/document/d/1F__vEKeI41P7PPUCMM9PVPYY34pyrvQI5rbTJVnS5c4) -func clusterUID(client kubernetes.Interface) (types.UID, error) { - kubeSystem, err := client.CoreV1().Namespaces().Get(context.TODO(), "kube-system", metav1.GetOptions{}) - if err != nil { - return "", err - } - return kubeSystem.UID, nil -} - -const ( - IstioEastWestGatewayServiceName = "istio-eastwestgateway" -) diff --git a/istioctl/pkg/multicluster/env.go b/istioctl/pkg/multicluster/env.go deleted file mode 100644 index ea20d2de6..000000000 --- a/istioctl/pkg/multicluster/env.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package multicluster - -import ( - "fmt" - "io" - "os" - "time" -) - -import ( - "github.com/spf13/cobra" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/tools/clientcmd/api" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -type ConditionFunc func() (done bool, err error) - -type Environment interface { - GetConfig() *api.Config - CreateClient(context string) (kube.ExtendedClient, error) - Stdout() io.Writer - Stderr() io.Writer - ReadFile(filename string) ([]byte, error) - Printf(format string, a ...interface{}) - Errorf(format string, a ...interface{}) - Poll(interval, timeout time.Duration, condition ConditionFunc) error -} - -type KubeEnvironment struct { - config *api.Config - stdout io.Writer - stderr io.Writer - kubeconfig string -} - -func (e *KubeEnvironment) CreateClient(context string) (kube.ExtendedClient, error) { - cfg, err := kube.BuildClientConfig(e.kubeconfig, context) - if err != nil { - return nil, err - } - return kube.NewExtendedClient(kube.NewClientConfigForRestConfig(cfg), "") -} - -func (e *KubeEnvironment) Printf(format string, a ...interface{}) { - _, _ = fmt.Fprintf(e.stdout, format, a...) -} - -func (e *KubeEnvironment) Errorf(format string, a ...interface{}) { - _, _ = fmt.Fprintf(e.stderr, format, a...) -} - -func (e *KubeEnvironment) GetConfig() *api.Config { return e.config } -func (e *KubeEnvironment) Stdout() io.Writer { return e.stdout } -func (e *KubeEnvironment) Stderr() io.Writer { return e.stderr } -func (e *KubeEnvironment) ReadFile(filename string) ([]byte, error) { return os.ReadFile(filename) } -func (e *KubeEnvironment) Poll(interval, timeout time.Duration, condition ConditionFunc) error { - return wait.Poll(interval, timeout, func() (bool, error) { - return condition() - }) -} - -var _ Environment = (*KubeEnvironment)(nil) - -func NewEnvironment(kubeconfig, context string, stdout, stderr io.Writer) (*KubeEnvironment, error) { - config, err := kube.BuildClientCmd(kubeconfig, context).ConfigAccess().GetStartingConfig() - if err != nil { - return nil, err - } - - return &KubeEnvironment{ - config: config, - stdout: stdout, - stderr: stderr, - kubeconfig: kubeconfig, - }, nil -} - -func NewEnvironmentFromCobra(kubeconfig, context string, cmd *cobra.Command) (Environment, error) { - return NewEnvironment(kubeconfig, context, cmd.OutOrStdout(), cmd.OutOrStderr()) -} diff --git a/istioctl/pkg/multicluster/env_test.go b/istioctl/pkg/multicluster/env_test.go deleted file mode 100644 index c20b323ec..000000000 --- a/istioctl/pkg/multicluster/env_test.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package multicluster - -import ( - "bytes" - "os" - "reflect" - "testing" - "time" -) - -import ( - "github.com/google/go-cmp/cmp" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/tools/clientcmd/api" - "k8s.io/client-go/tools/clientcmd/api/latest" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -var fakeKubeconfigData = `apiVersion: v1 -kind: Config -clusters: -- cluster: - certificate-authority-data: UEhPTlkK - server: https://192.168.1.1 - name: prod0 -contexts: -- context: - cluster: prod0 - user: prod0 - name: prod0 -current-context: prod0 -users: -- name: prod0 - user: - auth-provider: - name: gcp` // nolint:lll - -func createFakeKubeconfigFileOrDie(t *testing.T) (string, *api.Config) { - t.Helper() - - f, err := os.CreateTemp("", "fakeKubeconfigForEnvironment") - if err != nil { - t.Fatalf("could not create fake kubeconfig file: %v", err) - } - kubeconfigPath := f.Name() - defer func() { - if err := f.Close(); err != nil { - t.Errorf("couldn't close temp file %v: %v", kubeconfigPath, err) - } - }() - - if _, err = f.WriteString(fakeKubeconfigData); err != nil { - t.Fatalf("could not write fake kubeconfig data to %v: %v", kubeconfigPath, err) - } - - // Temporary workaround until https://github.com/kubernetes/kubernetes/pull/86414 merges - into := &api.Config{ - Clusters: map[string]*api.Cluster{}, - AuthInfos: map[string]*api.AuthInfo{}, - Contexts: map[string]*api.Context{}, - Extensions: map[string]runtime.Object{}, - } - out, _, err := latest.Codec.Decode([]byte(fakeKubeconfigData), nil, into) - if err != nil { - t.Fatalf("could not decode fake kubeconfig: %v", err) - } - config, ok := out.(*api.Config) - if !ok { - t.Fatalf("decoded kubeconfig is not a valid api.Config (%v)", reflect.TypeOf(out)) - } - - // fill in missing defaults - config.Contexts[config.CurrentContext].LocationOfOrigin = kubeconfigPath - config.Clusters[config.CurrentContext].LocationOfOrigin = kubeconfigPath - config.AuthInfos[config.CurrentContext].LocationOfOrigin = kubeconfigPath - config.Extensions = make(map[string]runtime.Object) - config.Preferences.Extensions = make(map[string]runtime.Object) - - return kubeconfigPath, config -} - -type fakeEnvironment struct { - KubeEnvironment - - client kube.ExtendedClient - injectClientCreateError error - kubeconfig string - wOut bytes.Buffer - wErr bytes.Buffer -} - -func newFakeEnvironmentOrDie(t *testing.T, minor string, config *api.Config, objs ...runtime.Object) *fakeEnvironment { - t.Helper() - - var wOut, wErr bytes.Buffer - - f := &fakeEnvironment{ - KubeEnvironment: KubeEnvironment{ - config: config, - stdout: &wOut, - stderr: &wErr, - kubeconfig: "unused", - }, - client: kube.NewFakeClientWithVersion(minor, objs...), - kubeconfig: "unused", - wOut: wOut, - wErr: wErr, - } - - return f -} - -func (f *fakeEnvironment) CreateClient(_ string) (kube.ExtendedClient, error) { - if f.injectClientCreateError != nil { - return nil, f.injectClientCreateError - } - return f.client, nil -} - -func (f *fakeEnvironment) Poll(_, _ time.Duration, condition ConditionFunc) error { - // TODO - add hooks to inject fake timeouts - _, _ = condition() - return nil -} - -func TestNewEnvironment(t *testing.T) { - context := "" // empty, use current-Context - kubeconfig, wantConfig := createFakeKubeconfigFileOrDie(t) - - var wOut, wErr bytes.Buffer - - wantEnv := &KubeEnvironment{ - config: wantConfig, - stdout: &wOut, - stderr: &wErr, - kubeconfig: kubeconfig, - } - - gotEnv, err := NewEnvironment(kubeconfig, context, &wOut, &wErr) - if err != nil { - t.Fatal(err) - } - - if diff := cmp.Diff(*wantEnv.GetConfig(), *gotEnv.GetConfig()); diff != "" { - t.Errorf("bad config: \n got %v \nwant %v\ndiff %v", gotEnv, wantEnv, diff) - } - - wantEnv.config = nil - gotEnv.config = nil - if !reflect.DeepEqual(wantEnv, gotEnv) { - t.Errorf("bad environment: \n got %v \nwant %v", *gotEnv, *wantEnv) - } - - // verify interface - if gotEnv.Stderr() != &wErr { - t.Errorf("Stderr() returned wrong io.writer") - } - if gotEnv.Stdout() != &wOut { - t.Errorf("Stdout() returned wrong io.writer") - } - gotEnv.Printf("stdout %v", "test") - wantOut := "stdout test" - if gotOut := wOut.String(); gotOut != wantOut { - t.Errorf("Printf() printed wrong string: got %v want %v", gotOut, wantOut) - } - gotEnv.Errorf("stderr %v", "test") - wantErr := "stderr test" - if gotErr := wErr.String(); gotErr != wantErr { - t.Errorf("Errorf() printed wrong string: got %v want %v", gotErr, wantErr) - } -} diff --git a/istioctl/pkg/multicluster/options.go b/istioctl/pkg/multicluster/options.go deleted file mode 100644 index 81521d6bc..000000000 --- a/istioctl/pkg/multicluster/options.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package multicluster - -import ( - "errors" -) - -import ( - "github.com/spf13/pflag" - "k8s.io/client-go/tools/clientcmd" -) - -// TODO(ayj) - add to istio.io/api/annotations -const clusterNameAnnotationKey = "networking.istio.io/cluster" - -// KubeOptions contains kubernetes options common to all commands. -type KubeOptions struct { - Kubeconfig string - Context string - Namespace string -} - -// Inherit the common kubernetes flags defined in the root package. This is a bit of a hack, -// but it allows us to directly get the final values for each of these flags without needing -// to pass pointers-to-flags through all of the (sub)commands. -func (o *KubeOptions) prepare(flags *pflag.FlagSet) { - if f := flags.Lookup("kubeconfig"); f != nil { - o.Kubeconfig = f.Value.String() - } - if f := flags.Lookup("context"); f != nil { - o.Context = f.Value.String() - } - if f := flags.Lookup("namespace"); f != nil { - o.Namespace = f.Value.String() - } - - if o.Namespace == "" { - o.Namespace = defaultIstioNamespace - - configAccess := clientcmd.NewDefaultPathOptions() - configAccess.GlobalFile = o.Kubeconfig - if config, err := configAccess.GetStartingConfig(); err == nil { - if context, ok := config.Contexts[config.CurrentContext]; ok && context.Namespace != "" { - o.Namespace = context.Namespace - } - } - } -} - -type filenameOption struct { - filename string -} - -func (f *filenameOption) addFlags(flagset *pflag.FlagSet) { - if flagset.Lookup("filename") == nil { - flagset.StringVarP(&f.filename, "filename", "f", "", - "Filename of the multicluster mesh description") - } -} - -func (f *filenameOption) prepare() error { - if len(f.filename) == 0 { - return errors.New("must specify -f") - } - return nil -} diff --git a/istioctl/pkg/multicluster/remote_secret.go b/istioctl/pkg/multicluster/remote_secret.go deleted file mode 100644 index 32088653c..000000000 --- a/istioctl/pkg/multicluster/remote_secret.go +++ /dev/null @@ -1,784 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package multicluster - -import ( - "bytes" - "context" - "fmt" - "io" - "os" - "strings" - "time" -) - -import ( - "github.com/cenkalti/backoff/v4" - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "istio.io/pkg/log" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/serializer/json" - "k8s.io/apimachinery/pkg/runtime/serializer/versioning" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - _ "k8s.io/client-go/plugin/pkg/client/auth" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/tools/clientcmd/api" - "k8s.io/client-go/tools/clientcmd/api/latest" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/cmd/mesh" - "github.com/apache/dubbo-go-pixiu/operator/pkg/helm" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/multicluster" -) - -var ( - codec runtime.Codec - scheme *runtime.Scheme - - tokenWaitBackoff = time.Second -) - -func init() { - scheme = runtime.NewScheme() - utilruntime.Must(v1.AddToScheme(scheme)) - opt := json.SerializerOptions{ - Yaml: true, - Pretty: false, - Strict: false, - } - yamlSerializer := json.NewSerializerWithOptions(json.DefaultMetaFactory, scheme, scheme, opt) - codec = versioning.NewDefaultingCodecForScheme( - scheme, - yamlSerializer, - yamlSerializer, - v1.SchemeGroupVersion, - runtime.InternalGroupVersioner, - ) -} - -const ( - remoteSecretPrefix = "istio-remote-secret-" - configSecretName = "istio-kubeconfig" - configSecretKey = "config" -) - -func remoteSecretNameFromClusterName(clusterName string) string { - return remoteSecretPrefix + clusterName -} - -// NewCreateRemoteSecretCommand creates a new command for joining two contexts -// together in a multi-cluster mesh. -func NewCreateRemoteSecretCommand() *cobra.Command { - opts := RemoteSecretOptions{ - AuthType: RemoteSecretAuthTypeBearerToken, - AuthPluginConfig: make(map[string]string), - Type: SecretTypeRemote, - } - c := &cobra.Command{ - Use: "create-remote-secret", - Short: "Create a secret with credentials to allow Istio to access remote Kubernetes apiservers", - Example: ` # Create a secret to access cluster c0's apiserver and install it in cluster c1. - istioctl --kubeconfig=c0.yaml x create-remote-secret --name c0 \ - | kubectl --kubeconfig=c1.yaml apply -f - - - # Delete a secret that was previously installed in c1 - istioctl --kubeconfig=c0.yaml x create-remote-secret --name c0 \ - | kubectl --kubeconfig=c1.yaml delete -f - - - # Create a secret access a remote cluster with an auth plugin - istioctl --kubeconfig=c0.yaml x create-remote-secret --name c0 --auth-type=plugin --auth-plugin-name=gcp \ - | kubectl --kubeconfig=c1.yaml apply -f -`, - Args: cobra.NoArgs, - RunE: func(c *cobra.Command, args []string) error { - if err := opts.prepare(c.Flags()); err != nil { - return err - } - env, err := NewEnvironmentFromCobra(opts.Kubeconfig, opts.Context, c) - if err != nil { - return err - } - out, warn, err := CreateRemoteSecret(opts, env) - if err != nil { - _, _ = fmt.Fprintf(c.OutOrStderr(), "error: %v\n", err) - return err - } - if warn != nil { - _, _ = fmt.Fprintf(c.OutOrStderr(), "warn: %v\n", warn) - } - _, _ = fmt.Fprint(c.OutOrStdout(), out) - return nil - }, - } - opts.addFlags(c.PersistentFlags()) - return c -} - -func createRemoteServiceAccountSecret(kubeconfig *api.Config, clusterName, secName string) (*v1.Secret, error) { // nolint:interfacer - var data bytes.Buffer - if err := latest.Codec.Encode(kubeconfig, &data); err != nil { - return nil, err - } - key := clusterName - if secName == configSecretName { - key = configSecretKey - } - out := &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: secName, - Annotations: map[string]string{ - clusterNameAnnotationKey: clusterName, - }, - Labels: map[string]string{ - multicluster.MultiClusterSecretLabel: "true", - }, - }, - Data: map[string][]byte{ - key: data.Bytes(), - }, - } - return out, nil -} - -func createBaseKubeconfig(caData []byte, clusterName, server string) *api.Config { - return &api.Config{ - Clusters: map[string]*api.Cluster{ - clusterName: { - CertificateAuthorityData: caData, - Server: server, - }, - }, - AuthInfos: map[string]*api.AuthInfo{}, - Contexts: map[string]*api.Context{ - clusterName: { - Cluster: clusterName, - AuthInfo: clusterName, - }, - }, - CurrentContext: clusterName, - } -} - -func createBearerTokenKubeconfig(caData, token []byte, clusterName, server string) *api.Config { - c := createBaseKubeconfig(caData, clusterName, server) - c.AuthInfos[c.CurrentContext] = &api.AuthInfo{ - Token: string(token), - } - return c -} - -func createPluginKubeconfig(caData []byte, clusterName, server string, authProviderConfig *api.AuthProviderConfig) *api.Config { - c := createBaseKubeconfig(caData, clusterName, server) - c.AuthInfos[c.CurrentContext] = &api.AuthInfo{ - AuthProvider: authProviderConfig, - } - return c -} - -func createRemoteSecretFromPlugin( - tokenSecret *v1.Secret, - server, clusterName, secName string, - authProviderConfig *api.AuthProviderConfig, -) (*v1.Secret, error) { - caData, ok := tokenSecret.Data[v1.ServiceAccountRootCAKey] - if !ok { - return nil, errMissingRootCAKey - } - - // Create a Kubeconfig to access the remote cluster using the auth provider plugin. - kubeconfig := createPluginKubeconfig(caData, clusterName, server, authProviderConfig) - if err := clientcmd.Validate(*kubeconfig); err != nil { - return nil, fmt.Errorf("invalid kubeconfig: %v", err) - } - - // Encode the Kubeconfig in a secret that can be loaded by Istio to dynamically discover and access the remote cluster. - return createRemoteServiceAccountSecret(kubeconfig, clusterName, secName) -} - -var ( - errMissingRootCAKey = fmt.Errorf("no %q data found", v1.ServiceAccountRootCAKey) - errMissingTokenKey = fmt.Errorf("no %q data found", v1.ServiceAccountTokenKey) -) - -func createRemoteSecretFromTokenAndServer(client kube.ExtendedClient, tokenSecret *v1.Secret, clusterName, server, secName string) (*v1.Secret, error) { - caData, token, err := waitForTokenData(client, tokenSecret) - if err != nil { - return nil, err - } - - // Create a Kubeconfig to access the remote cluster using the remote service account credentials. - kubeconfig := createBearerTokenKubeconfig(caData, token, clusterName, server) - if err := clientcmd.Validate(*kubeconfig); err != nil { - return nil, fmt.Errorf("invalid kubeconfig: %v", err) - } - - // Encode the Kubeconfig in a secret that can be loaded by Istio to dynamically discover and access the remote cluster. - return createRemoteServiceAccountSecret(kubeconfig, clusterName, secName) -} - -func waitForTokenData(client kube.ExtendedClient, secret *v1.Secret) (ca, token []byte, err error) { - ca, token, err = tokenDataFromSecret(secret) - if err == nil { - return - } - - log.Infof("Waiting for data to be populated in %s", secret.Name) - err = backoff.Retry( - func() error { - secret, err = client.Kube().CoreV1().Secrets(secret.Namespace).Get(context.TODO(), secret.Name, metav1.GetOptions{}) - if err != nil { - return err - } - ca, token, err = tokenDataFromSecret(secret) - return err - }, - backoff.WithMaxRetries(backoff.NewConstantBackOff(tokenWaitBackoff), 5)) - return -} - -func tokenDataFromSecret(tokenSecret *v1.Secret) (ca, token []byte, err error) { - var ok bool - ca, ok = tokenSecret.Data[v1.ServiceAccountRootCAKey] - if !ok { - err = errMissingRootCAKey - return - } - token, ok = tokenSecret.Data[v1.ServiceAccountTokenKey] - if !ok { - err = errMissingTokenKey - return - } - return -} - -func getServiceAccountSecret(client kube.ExtendedClient, opt RemoteSecretOptions) (*v1.Secret, error) { - // Create the service account if it doesn't exist. - serviceAccount, err := getOrCreateServiceAccount(client, opt) - if err != nil { - return nil, err - } - - if !kube.IsAtLeastVersion(client, 24) { - return legacyGetServiceAccountSecret(serviceAccount, client, opt) - } - return getOrCreateServiceAccountSecret(serviceAccount, client, opt) -} - -// In Kubernetes 1.24+ we can't assume the secrets will be referenced in the ServiceAccount or be created automatically. -// See https://github.com/istio/istio/issues/38246 -func getOrCreateServiceAccountSecret( - serviceAccount *v1.ServiceAccount, - client kube.ExtendedClient, - opt RemoteSecretOptions, -) (*v1.Secret, error) { - ctx := context.TODO() - - // manually specified secret, make sure it references the ServiceAccount - if opt.SecretName != "" { - secret, err := client.Kube().CoreV1().Secrets(opt.Namespace).Get(ctx, opt.SecretName, metav1.GetOptions{}) - if err != nil { - return nil, fmt.Errorf("could not get specified secret %s/%s: %v", - opt.Namespace, opt.SecretName, err) - } - if err := secretReferencesServiceAccount(serviceAccount, secret); err != nil { - return nil, err - } - return secret, nil - } - - // first try to find an existing secret that references the SA - // TODO will the SA have any reference to secrets anymore, can we avoid this list? - allSecrets, err := client.Kube().CoreV1().Secrets(opt.Namespace).List(ctx, metav1.ListOptions{}) - if err != nil { - return nil, fmt.Errorf("failed listing secrets in %s: %v", opt.Namespace, err) - } - for _, item := range allSecrets.Items { - secret := item - if secretReferencesServiceAccount(serviceAccount, &secret) == nil { - return &secret, nil - } - } - - // finally, create the sa token secret manually - // https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#manually-create-a-service-account-api-token - // TODO ephemeral time-based tokens are preferred; we should re-think this - log.Infof("Creating token secret for service account %q", serviceAccount.Name) - secretName := tokenSecretName(serviceAccount.Name) - return client.Kube().CoreV1().Secrets(opt.Namespace).Create(ctx, &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: secretName, - Annotations: map[string]string{v1.ServiceAccountNameKey: serviceAccount.Name}, - }, - Type: v1.SecretTypeServiceAccountToken, - }, metav1.CreateOptions{}) -} - -func tokenSecretName(saName string) string { - return saName + "-istio-remote-secret-token" -} - -func secretReferencesServiceAccount(serviceAccount *v1.ServiceAccount, secret *v1.Secret) error { - if secret.Type != v1.SecretTypeServiceAccountToken || - secret.Annotations[v1.ServiceAccountNameKey] != serviceAccount.Name { - return fmt.Errorf("secret %s/%s does not reference ServiceAccount %s", - secret.Namespace, secret.Name, serviceAccount.Name) - } - return nil -} - -func legacyGetServiceAccountSecret( - serviceAccount *v1.ServiceAccount, - client kube.ExtendedClient, - opt RemoteSecretOptions, -) (*v1.Secret, error) { - if len(serviceAccount.Secrets) == 0 { - return nil, fmt.Errorf("no secret found in the service account: %s", serviceAccount) - } - - secretName := "" - secretNamespace := "" - if opt.SecretName != "" { - found := false - for _, secret := range serviceAccount.Secrets { - if secret.Name == opt.SecretName { - found = true - secretName = secret.Name - secretNamespace = secret.Namespace - break - } - } - if !found { - return nil, fmt.Errorf("provided secret does not exist: %s", opt.SecretName) - } - } else { - if len(serviceAccount.Secrets) == 1 { - secretName = serviceAccount.Secrets[0].Name - secretNamespace = serviceAccount.Secrets[0].Namespace - } else { - return nil, fmt.Errorf("wrong number of secrets (%v) in serviceaccount %s/%s, please use --secret-name to specify one", - len(serviceAccount.Secrets), opt.Namespace, opt.ServiceAccountName) - } - } - - if secretNamespace == "" { - secretNamespace = opt.Namespace - } - return client.Kube().CoreV1().Secrets(secretNamespace).Get(context.TODO(), secretName, metav1.GetOptions{}) -} - -func getOrCreateServiceAccount(client kube.ExtendedClient, opt RemoteSecretOptions) (*v1.ServiceAccount, error) { - if sa, err := client.Kube().CoreV1().ServiceAccounts(opt.Namespace).Get( - context.TODO(), opt.ServiceAccountName, metav1.GetOptions{}); err == nil { - return sa, nil - } else if !opt.CreateServiceAccount { - // User chose not to automatically create the service account. - return nil, fmt.Errorf("failed retrieving service account %s.%s required for creating "+ - "the remote secret (hint: try installing a minimal Istio profile on the cluster first, "+ - "or run with '--create-service-account=true'): %v", - opt.ServiceAccountName, - opt.Namespace, - err) - } - - if err := createServiceAccount(client, opt); err != nil { - return nil, err - } - - // Return the newly created service account. - sa, err := client.Kube().CoreV1().ServiceAccounts(opt.Namespace).Get( - context.TODO(), opt.ServiceAccountName, metav1.GetOptions{}) - if err != nil { - return nil, fmt.Errorf("failed retrieving service account %s.%s after creating it: %v", - opt.ServiceAccountName, opt.Namespace, err) - } - return sa, nil -} - -func createServiceAccount(client kube.ExtendedClient, opt RemoteSecretOptions) error { - yaml, err := generateServiceAccountYAML(opt) - if err != nil { - return err - } - - // Before we can apply the yaml, we have to ensure the system namespace exists. - if err := createNamespaceIfNotExist(client, opt.Namespace); err != nil { - return err - } - - // Apply the YAML to the cluster. - return applyYAML(client, yaml, opt.Namespace) -} - -func generateServiceAccountYAML(opt RemoteSecretOptions) (string, error) { - // Create a renderer for the base installation. - baseRenderer := helm.NewHelmRenderer(opt.ManifestsPath, "base", "Base", opt.Namespace, nil) - discoveryRenderer := helm.NewHelmRenderer(opt.ManifestsPath, "istio-control/istio-discovery", "Pilot", opt.Namespace, nil) - - baseTemplates := []string{"reader-serviceaccount.yaml"} - discoveryTemplates := []string{"clusterrole.yaml", "clusterrolebinding.yaml"} - - if err := baseRenderer.Run(); err != nil { - return "", fmt.Errorf("failed running base Helm renderer: %w", err) - } - if err := discoveryRenderer.Run(); err != nil { - return "", fmt.Errorf("failed running base discovery Helm renderer: %w", err) - } - - values := fmt.Sprintf(` -global: - istioNamespace: %s -`, opt.Namespace) - - // Render the templates required for the service account and role bindings. - baseContent, err := baseRenderer.RenderManifestFiltered(values, func(template string) bool { - for _, t := range baseTemplates { - if strings.Contains(template, t) { - return true - } - } - return false - }) - if err != nil { - return "", fmt.Errorf("failed rendering base manifest: %w", err) - } - discoveryContent, err := discoveryRenderer.RenderManifestFiltered(values, func(template string) bool { - for _, t := range discoveryTemplates { - if strings.Contains(template, t) { - return true - } - } - return false - }) - if err != nil { - return "", fmt.Errorf("failed rendering discovery manifest: %w", err) - } - - aggregateContent := fmt.Sprintf(` -%s ---- -%s -`, baseContent, discoveryContent) - return aggregateContent, nil -} - -func applyYAML(client kube.ExtendedClient, yamlContent, ns string) error { - yamlFile, err := writeToTempFile(yamlContent) - if err != nil { - return fmt.Errorf("failed creating manifest file: %v", err) - } - - // Apply the YAML to the cluster. - if err := client.ApplyYAMLFiles(ns, yamlFile); err != nil { - return fmt.Errorf("failed applying manifest %s: %v", yamlFile, err) - } - return nil -} - -func createNamespaceIfNotExist(client kube.Client, ns string) error { - if _, err := client.Kube().CoreV1().Namespaces().Get(context.TODO(), ns, metav1.GetOptions{}); err != nil { - if _, err := client.Kube().CoreV1().Namespaces().Create(context.TODO(), &v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: ns, - }, - }, metav1.CreateOptions{}); err != nil { - return fmt.Errorf("failed creating namespace %s: %v", ns, err) - } - } - return nil -} - -func writeToTempFile(content string) (string, error) { - outFile, err := os.CreateTemp("", "remote-secret-manifest-*") - if err != nil { - return "", fmt.Errorf("failed creating temp file for manifest: %v", err) - } - defer func() { _ = outFile.Close() }() - - if _, err := outFile.Write([]byte(content)); err != nil { - return "", fmt.Errorf("failed writing manifest file: %v", err) - } - return outFile.Name(), nil -} - -func getServerFromKubeconfig(context string, config *api.Config) (string, Warning, error) { - if context == "" { - context = config.CurrentContext - } - - configContext, ok := config.Contexts[context] - if !ok { - return "", nil, fmt.Errorf("could not find cluster for context %q", context) - } - cluster, ok := config.Clusters[configContext.Cluster] - if !ok { - return "", nil, fmt.Errorf("could not find server for context %q", context) - } - if strings.Contains(cluster.Server, "127.0.0.1") || strings.Contains(cluster.Server, "localhost") { - return cluster.Server, fmt.Errorf( - "server in Kubeconfig is %s. This is likely not reachable from inside the cluster, "+ - "if you're using Kubernetes in Docker, pass --server with the container IP for the API Server", - cluster.Server), nil - } - return cluster.Server, nil, nil -} - -const ( - outputHeader = "# This file is autogenerated, do not edit.\n" - outputTrailer = "---\n" -) - -func writeEncodedObject(out io.Writer, in runtime.Object) error { - if _, err := fmt.Fprint(out, outputHeader); err != nil { - return err - } - if err := codec.Encode(in, out); err != nil { - return err - } - if _, err := fmt.Fprint(out, outputTrailer); err != nil { - return err - } - return nil -} - -type writer interface { - io.Writer - String() string -} - -func makeOutputWriter() writer { - return &bytes.Buffer{} -} - -var makeOutputWriterTestHook = makeOutputWriter - -// RemoteSecretAuthType is a strongly typed authentication type suitable for use with pflags.Var(). -type ( - RemoteSecretAuthType string - SecretType string -) - -var _ pflag.Value = (*RemoteSecretAuthType)(nil) - -func (at *RemoteSecretAuthType) String() string { return string(*at) } -func (at *RemoteSecretAuthType) Type() string { return "RemoteSecretAuthType" } -func (at *RemoteSecretAuthType) Set(in string) error { - *at = RemoteSecretAuthType(in) - return nil -} - -func (at *SecretType) String() string { return string(*at) } -func (at *SecretType) Type() string { return "SecretType" } -func (at *SecretType) Set(in string) error { - *at = SecretType(in) - return nil -} - -const ( - // Use a bearer token for authentication to the remote kubernetes cluster. - RemoteSecretAuthTypeBearerToken RemoteSecretAuthType = "bearer-token" - - // Use a custom authentication plugin for the remote kubernetes cluster. - RemoteSecretAuthTypePlugin RemoteSecretAuthType = "plugin" - - // Secret generated from remote cluster - SecretTypeRemote SecretType = "remote" - - // Secret generated from config cluster - SecretTypeConfig SecretType = "config" -) - -// RemoteSecretOptions contains the options for creating a remote secret. -type RemoteSecretOptions struct { - KubeOptions - - // Name of the local cluster whose credentials are stored in the secret. Must be - // DNS1123 label as it will be used for the k8s secret name. - ClusterName string - - // Create a secret with this service account's credentials. - ServiceAccountName string - - // CreateServiceAccount if true, the service account specified by ServiceAccountName - // will be created if it doesn't exist. - CreateServiceAccount bool - - // Authentication method for the remote Kubernetes cluster. - AuthType RemoteSecretAuthType - // Authenticator plugin configuration - AuthPluginName string - AuthPluginConfig map[string]string - - // Type of the generated secret - Type SecretType - - // ManifestsPath is a path to a manifestsPath and profiles directory in the local filesystem, - // or URL with a release tgz. This is only used when no reader service account exists and has - // to be created. - ManifestsPath string - - // ServerOverride overrides the server IP/hostname field from the Kubeconfig - ServerOverride string - - // SecretName selects a specific secret from the remote service account, if there are multiple - SecretName string -} - -func (o *RemoteSecretOptions) addFlags(flagset *pflag.FlagSet) { - flagset.StringVar(&o.ServiceAccountName, "service-account", "", - "Create a secret with this service account's credentials. Default value is \""+ - constants.DefaultServiceAccountName+"\" if --type is \"remote\", \""+ - constants.DefaultConfigServiceAccountName+"\" if --type is \"config\".") - flagset.BoolVar(&o.CreateServiceAccount, "create-service-account", true, - "If true, the service account needed for creating the remote secret will be created "+ - "if it doesn't exist.") - flagset.StringVar(&o.ClusterName, "name", "", - "Name of the local cluster whose credentials are stored "+ - "in the secret. If a name is not specified the kube-system namespace's UUID of "+ - "the local cluster will be used.") - flagset.StringVar(&o.ServerOverride, "server", "", - "The address and port of the Kubernetes API server.") - flagset.StringVar(&o.SecretName, "secret-name", "", - "The name of the specific secret to use from the service-account. Needed when there are multiple secrets in the service account.") - var supportedAuthType []string - for _, at := range []RemoteSecretAuthType{RemoteSecretAuthTypeBearerToken, RemoteSecretAuthTypePlugin} { - supportedAuthType = append(supportedAuthType, string(at)) - } - var supportedSecretType []string - for _, at := range []SecretType{SecretTypeRemote, SecretTypeConfig} { - supportedSecretType = append(supportedSecretType, string(at)) - } - - flagset.Var(&o.AuthType, "auth-type", - fmt.Sprintf("Type of authentication to use. supported values = %v", supportedAuthType)) - flagset.StringVar(&o.AuthPluginName, "auth-plugin-name", o.AuthPluginName, - fmt.Sprintf("Authenticator plug-in name. --auth-type=%v must be set with this option", - RemoteSecretAuthTypePlugin)) - flagset.StringToString("auth-plugin-config", o.AuthPluginConfig, - fmt.Sprintf("Authenticator plug-in configuration. --auth-type=%v must be set with this option", - RemoteSecretAuthTypePlugin)) - flagset.Var(&o.Type, "type", - fmt.Sprintf("Type of the generated secret. supported values = %v", supportedSecretType)) - flagset.StringVarP(&o.ManifestsPath, "manifests", "d", "", mesh.ManifestsFlagHelpStr) -} - -func (o *RemoteSecretOptions) prepare(flags *pflag.FlagSet) error { - o.KubeOptions.prepare(flags) - - if o.ClusterName != "" { - if !labels.IsDNS1123Label(o.ClusterName) { - return fmt.Errorf("%v is not a valid DNS 1123 label", o.ClusterName) - } - } - return nil -} - -type Warning error - -func createRemoteSecret(opt RemoteSecretOptions, client kube.ExtendedClient, env Environment) (*v1.Secret, Warning, error) { - // generate the clusterName if not specified - if opt.ClusterName == "" { - uid, err := clusterUID(client) - if err != nil { - return nil, nil, err - } - opt.ClusterName = string(uid) - } - - var secretName string - switch opt.Type { - case SecretTypeRemote: - secretName = remoteSecretNameFromClusterName(opt.ClusterName) - if opt.ServiceAccountName == "" { - opt.ServiceAccountName = constants.DefaultServiceAccountName - } - case SecretTypeConfig: - secretName = configSecretName - if opt.ServiceAccountName == "" { - opt.ServiceAccountName = constants.DefaultConfigServiceAccountName - } - default: - return nil, nil, fmt.Errorf("unsupported type: %v", opt.Type) - } - tokenSecret, err := getServiceAccountSecret(client, opt) - if err != nil { - return nil, nil, fmt.Errorf("could not get access token to read resources from local kube-apiserver: %v", err) - } - - var server string - var warn Warning - if opt.ServerOverride != "" { - server = opt.ServerOverride - } else { - server, warn, err = getServerFromKubeconfig(opt.Context, env.GetConfig()) - if err != nil { - return nil, warn, err - } - } - - var remoteSecret *v1.Secret - switch opt.AuthType { - case RemoteSecretAuthTypeBearerToken: - remoteSecret, err = createRemoteSecretFromTokenAndServer(client, tokenSecret, opt.ClusterName, server, secretName) - case RemoteSecretAuthTypePlugin: - authProviderConfig := &api.AuthProviderConfig{ - Name: opt.AuthPluginName, - Config: opt.AuthPluginConfig, - } - remoteSecret, err = createRemoteSecretFromPlugin(tokenSecret, server, opt.ClusterName, secretName, - authProviderConfig) - default: - err = fmt.Errorf("unsupported authentication type: %v", opt.AuthType) - } - if err != nil { - return nil, warn, err - } - - remoteSecret.Namespace = opt.Namespace - return remoteSecret, warn, nil -} - -// CreateRemoteSecret creates a remote secret with credentials of the specified service account. -// This is useful for providing a cluster access to a remote apiserver. -func CreateRemoteSecret(opt RemoteSecretOptions, env Environment) (string, Warning, error) { - client, err := env.CreateClient(opt.Context) - if err != nil { - return "", nil, err - } - - remoteSecret, warn, err := createRemoteSecret(opt, client, env) - if err != nil { - return "", warn, err - } - - // convert any binary data to the string equivalent for easier review. The - // kube-apiserver will convert this to binary before it persists it to storage. - remoteSecret.StringData = make(map[string]string, len(remoteSecret.Data)) - for k, v := range remoteSecret.Data { - remoteSecret.StringData[k] = string(v) - } - remoteSecret.Data = nil - - w := makeOutputWriterTestHook() - if err := writeEncodedObject(w, remoteSecret); err != nil { - return "", warn, err - } - return w.String(), warn, nil -} diff --git a/istioctl/pkg/multicluster/remote_secret_test.go b/istioctl/pkg/multicluster/remote_secret_test.go deleted file mode 100644 index 3fcb6b47a..000000000 --- a/istioctl/pkg/multicluster/remote_secret_test.go +++ /dev/null @@ -1,948 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package multicluster - -import ( - "bytes" - "errors" - "fmt" - "path/filepath" - "strings" - "testing" - "time" -) - -import ( - "github.com/google/go-cmp/cmp" - . "github.com/onsi/gomega" - "github.com/spf13/pflag" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/tools/clientcmd/api" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/multicluster" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" -) - -var ( - kubeSystemNamespaceUID = types.UID("54643f96-eca0-11e9-bb97-42010a80000a") - kubeSystemNamespace = &v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "kube-system", - UID: kubeSystemNamespaceUID, - }, - } -) - -const ( - testNamespace = "dubbo-system-test" - testServiceAccountName = "test-service-account" - testKubeconfig = "test-kubeconfig" - testContext = "test-context" -) - -func makeServiceAccount(secrets ...string) *v1.ServiceAccount { - sa := &v1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: testServiceAccountName, - Namespace: testNamespace, - }, - } - - for _, secret := range secrets { - sa.Secrets = append(sa.Secrets, v1.ObjectReference{ - Name: secret, - Namespace: testNamespace, - }) - } - - return sa -} - -func makeSecret(name, caData, token string) *v1.Secret { - out := &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: testNamespace, - Annotations: map[string]string{v1.ServiceAccountNameKey: testServiceAccountName}, - }, - Data: map[string][]byte{}, - Type: v1.SecretTypeServiceAccountToken, - } - if len(caData) > 0 { - out.Data[v1.ServiceAccountRootCAKey] = []byte(caData) - } - if len(token) > 0 { - out.Data[v1.ServiceAccountTokenKey] = []byte(token) - } - return out -} - -type fakeOutputWriter struct { - b bytes.Buffer - injectError error - failAfter int -} - -func (w *fakeOutputWriter) Write(p []byte) (n int, err error) { - w.failAfter-- - if w.failAfter <= 0 && w.injectError != nil { - return 0, w.injectError - } - return w.b.Write(p) -} -func (w *fakeOutputWriter) String() string { return w.b.String() } - -func TestCreateRemoteSecrets(t *testing.T) { - prevOutputWriterStub := makeOutputWriterTestHook - defer func() { makeOutputWriterTestHook = prevOutputWriterStub }() - - sa := makeServiceAccount("saSecret") - sa2 := makeServiceAccount("saSecret", "saSecret2") - saSecret := makeSecret("saSecret", "caData", "token") - saSecret2 := makeSecret("saSecret2", "caData", "token") - saSecretMissingToken := makeSecret("saSecret", "caData", "") - badStartingConfigErrStr := "could not find cluster for context" - - cases := []struct { - testName string - - // test input - config *api.Config - objs []runtime.Object - name string - secType SecretType - secretName string - - // inject errors - badStartingConfig bool - outputWriterError error - - want string - wantErrStr string - k8sMinorVersion string - }{ - //{ - // testName: "fail to get service account secret token", - // objs: []runtime.Object{kubeSystemNamespace, sa}, - // wantErrStr: "no \"ca.crt\" data found", - //}, - { - testName: "fail to create starting config", - objs: []runtime.Object{kubeSystemNamespace, sa, saSecret}, - config: api.NewConfig(), - badStartingConfig: true, - wantErrStr: badStartingConfigErrStr, - }, - { - testName: "fail to find cluster in local Kubeconfig", - objs: []runtime.Object{kubeSystemNamespace, sa, saSecret}, - config: &api.Config{ - CurrentContext: testContext, - Clusters: map[string]*api.Cluster{ /* missing cluster */ }, - }, - wantErrStr: fmt.Sprintf(`could not find cluster for context %q`, testContext), - }, - { - testName: "fail to create remote secret token", - objs: []runtime.Object{kubeSystemNamespace, sa, saSecretMissingToken}, - config: &api.Config{ - CurrentContext: testContext, - Contexts: map[string]*api.Context{ - testContext: {Cluster: "cluster"}, - }, - Clusters: map[string]*api.Cluster{ - "cluster": {Server: "server"}, - }, - }, - wantErrStr: `no "token" data found`, - }, - { - testName: "fail to encode secret", - objs: []runtime.Object{kubeSystemNamespace, sa, saSecret}, - config: &api.Config{ - CurrentContext: testContext, - Contexts: map[string]*api.Context{ - testContext: {Cluster: "cluster"}, - }, - Clusters: map[string]*api.Cluster{ - "cluster": {Server: "server"}, - }, - }, - outputWriterError: errors.New("injected encode error"), - wantErrStr: "injected encode error", - }, - { - testName: "success", - objs: []runtime.Object{kubeSystemNamespace, sa, saSecret}, - config: &api.Config{ - CurrentContext: testContext, - Contexts: map[string]*api.Context{ - testContext: {Cluster: "cluster"}, - }, - Clusters: map[string]*api.Cluster{ - "cluster": {Server: "server"}, - }, - }, - name: "cluster-foo", - want: "cal-want", - }, - { - testName: "success with type defined", - objs: []runtime.Object{kubeSystemNamespace, sa, saSecret}, - config: &api.Config{ - CurrentContext: testContext, - Contexts: map[string]*api.Context{ - testContext: {Cluster: "cluster"}, - }, - Clusters: map[string]*api.Cluster{ - "cluster": {Server: "server"}, - }, - }, - name: "cluster-foo", - secType: "config", - want: "cal-want", - }, - { - testName: "failure due to multiple secrets", - objs: []runtime.Object{kubeSystemNamespace, sa2, saSecret, saSecret2}, - config: &api.Config{ - CurrentContext: testContext, - Contexts: map[string]*api.Context{ - testContext: {Cluster: "cluster"}, - }, - Clusters: map[string]*api.Cluster{ - "cluster": {Server: "server"}, - }, - }, - name: "cluster-foo", - want: "cal-want", - wantErrStr: "wrong number of secrets (2) in serviceaccount", - // for k8s 1.24+ we auto-create a secret instead of relying on a reference in service account - k8sMinorVersion: "23", - }, - { - testName: "success when specific secret name provided", - objs: []runtime.Object{kubeSystemNamespace, sa2, saSecret, saSecret2}, - config: &api.Config{ - CurrentContext: testContext, - Contexts: map[string]*api.Context{ - testContext: {Cluster: "cluster"}, - }, - Clusters: map[string]*api.Cluster{ - "cluster": {Server: "server"}, - }, - }, - secretName: saSecret.Name, - name: "cluster-foo", - want: "cal-want", - }, - { - testName: "fail when non-existing secret name provided", - objs: []runtime.Object{kubeSystemNamespace, sa2, saSecret, saSecret2}, - config: &api.Config{ - CurrentContext: testContext, - Contexts: map[string]*api.Context{ - testContext: {Cluster: "cluster"}, - }, - Clusters: map[string]*api.Cluster{ - "cluster": {Server: "server"}, - }, - }, - secretName: "nonexistingSecret", - name: "cluster-foo", - want: "cal-want", - wantErrStr: "provided secret does not exist", - // for k8s 1.24+ we auto-create a secret instead of relying on a reference in service account - k8sMinorVersion: "23", - }, - } - - for i := range cases { - c := &cases[i] - t.Run(fmt.Sprintf("[%v] %v", i, c.testName), func(tt *testing.T) { - makeOutputWriterTestHook = func() writer { - return &fakeOutputWriter{injectError: c.outputWriterError} - } - if c.secType != SecretTypeConfig { - c.secType = SecretTypeRemote - } - opts := RemoteSecretOptions{ - ServiceAccountName: testServiceAccountName, - AuthType: RemoteSecretAuthTypeBearerToken, - // ClusterName: testCluster, - KubeOptions: KubeOptions{ - Namespace: testNamespace, - Context: testContext, - Kubeconfig: testKubeconfig, - }, - Type: c.secType, - SecretName: c.secretName, - } - - env := newFakeEnvironmentOrDie(t, c.k8sMinorVersion, c.config, c.objs...) - - got, _, err := CreateRemoteSecret(opts, env) // TODO - if c.wantErrStr != "" { - if err == nil { - tt.Fatalf("wanted error including %q but got none", c.wantErrStr) - } else if !strings.Contains(err.Error(), c.wantErrStr) { - tt.Fatalf("wanted error including %q but got %v", c.wantErrStr, err) - } - } else if c.wantErrStr == "" && err != nil { - tt.Fatalf("wanted non-error but got %q", err) - } else if c.want != "" { - var secretName, key string - switch c.secType { - case SecretTypeConfig: - secretName = configSecretName - key = configSecretKey - default: - secretName = remoteSecretPrefix + string(kubeSystemNamespaceUID) - key = "54643f96-eca0-11e9-bb97-42010a80000a" - } - wantOutput := fmt.Sprintf(`# This file is autogenerated, do not edit. -apiVersion: v1 -kind: Secret -metadata: - annotations: - %s: 54643f96-eca0-11e9-bb97-42010a80000a - creationTimestamp: null - labels: - istio/multiCluster: "true" - name: %s - namespace: dubbo-system-test -stringData: - %s: | - apiVersion: v1 - clusters: - - cluster: - certificate-authority-data: Y2FEYXRh - server: server - name: 54643f96-eca0-11e9-bb97-42010a80000a - contexts: - - context: - cluster: 54643f96-eca0-11e9-bb97-42010a80000a - user: 54643f96-eca0-11e9-bb97-42010a80000a - name: 54643f96-eca0-11e9-bb97-42010a80000a - current-context: 54643f96-eca0-11e9-bb97-42010a80000a - kind: Config - preferences: {} - users: - - name: 54643f96-eca0-11e9-bb97-42010a80000a - user: - token: token ---- -`, clusterNameAnnotationKey, secretName, key) - - if diff := cmp.Diff(got, wantOutput); diff != "" { - tt.Errorf("got\n%v\nwant\n%vdiff %v", got, c.want, diff) - } - } - }) - } -} - -func TestGetServiceAccountSecretToken(t *testing.T) { - secret := makeSecret("secret", "caData", "token") - - type tc struct { - name string - opts RemoteSecretOptions - objs []runtime.Object - - want *v1.Secret - wantErrStr string - } - - commonCases := []tc{ - { - name: "missing service account", - opts: RemoteSecretOptions{ - ServiceAccountName: testServiceAccountName, - KubeOptions: KubeOptions{ - Namespace: testNamespace, - }, - ManifestsPath: filepath.Join(env.IstioSrc, "manifests"), - }, - wantErrStr: fmt.Sprintf("serviceaccounts %q not found", testServiceAccountName), - }, - } - - legacyCases := append([]tc{ - { - name: "wrong number of secrets", - opts: RemoteSecretOptions{ - ServiceAccountName: testServiceAccountName, - CreateServiceAccount: false, - KubeOptions: KubeOptions{ - Namespace: testNamespace, - }, - ManifestsPath: filepath.Join(env.IstioSrc, "manifests"), - }, - objs: []runtime.Object{ - makeServiceAccount("secret", "extra-secret"), - }, - wantErrStr: "wrong number of secrets", - }, - { - name: "missing service account token secret", - opts: RemoteSecretOptions{ - ServiceAccountName: testServiceAccountName, - KubeOptions: KubeOptions{ - Namespace: testNamespace, - }, - ManifestsPath: filepath.Join(env.IstioSrc, "manifests"), - }, - objs: []runtime.Object{ - makeServiceAccount("wrong-secret"), - secret, - }, - wantErrStr: `secrets "wrong-secret" not found`, - }, - { - name: "success", - opts: RemoteSecretOptions{ - ServiceAccountName: testServiceAccountName, - KubeOptions: KubeOptions{ - Namespace: testNamespace, - }, - ManifestsPath: filepath.Join(env.IstioSrc, "manifests"), - }, - objs: []runtime.Object{ - makeServiceAccount("secret"), - secret, - }, - want: secret, - }, - }, commonCases...) - - cases := append([]tc{ - { - name: "success", - opts: RemoteSecretOptions{ - ServiceAccountName: testServiceAccountName, - KubeOptions: KubeOptions{ - Namespace: testNamespace, - }, - ManifestsPath: filepath.Join(env.IstioSrc, "manifests"), - }, - objs: []runtime.Object{ - makeServiceAccount(tokenSecretName(testServiceAccountName)), - }, - want: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: tokenSecretName(testServiceAccountName), - Namespace: testNamespace, - Annotations: map[string]string{v1.ServiceAccountNameKey: testServiceAccountName}, - }, - Type: v1.SecretTypeServiceAccountToken, - }, - }, - }, commonCases...) - - doCase := func(t *testing.T, c tc, k8sMinorVer string) { - t.Run(fmt.Sprintf("%v", c.name), func(tt *testing.T) { - client := kube.NewFakeClientWithVersion(k8sMinorVer, c.objs...) - got, err := getServiceAccountSecret(client, c.opts) - if c.wantErrStr != "" { - if err == nil { - tt.Fatalf("wanted error including %q but got none", c.wantErrStr) - } else if !strings.Contains(err.Error(), c.wantErrStr) { - tt.Fatalf("wanted error including %q but got %v", c.wantErrStr, err) - } - } else if c.wantErrStr == "" && err != nil { - tt.Fatalf("wanted non-error but got %q", err) - } else if diff := cmp.Diff(got, c.want); diff != "" { - tt.Errorf("got\n%v\nwant\n%vdiff %v", got, c.want, diff) - } - }) - } - - t.Run("kubernetes created secret (legacy)", func(t *testing.T) { - for _, c := range legacyCases { - doCase(t, c, "23") - } - }) - t.Run("istioctl created secret", func(t *testing.T) { - for _, c := range cases { - doCase(t, c, "") - } - }) -} - -func TestGenerateServiceAccount(t *testing.T) { - opts := RemoteSecretOptions{ - CreateServiceAccount: true, - ManifestsPath: filepath.Join(env.IstioSrc, "manifests"), - KubeOptions: KubeOptions{ - Namespace: "dubbo-system", - }, - } - yaml, err := generateServiceAccountYAML(opts) - if err != nil { - t.Fatalf("failed to generate service account YAML: %v", err) - } - objs, err := object.ParseK8sObjectsFromYAMLManifest(yaml) - if err != nil { - t.Fatalf("could not parse k8s objects from generated YAML: %v", err) - } - - mustFindObject(t, objs, "istio-reader-service-account", "ServiceAccount") - mustFindObject(t, objs, "istio-reader-clusterrole-dubbo-system", "ClusterRole") - mustFindObject(t, objs, "istio-reader-clusterrole-dubbo-system", "ClusterRoleBinding") -} - -func mustFindObject(t test.Failer, objs object.K8sObjects, name, kind string) { - t.Helper() - var obj *object.K8sObject - for _, o := range objs { - if o.Kind == kind && o.Name == name { - obj = o - break - } - } - if obj == nil { - t.Fatalf("expected %v/%v", name, kind) - } -} - -func TestGetClusterServerFromKubeconfig(t *testing.T) { - server := "server0" - context := "context0" - cluster := "cluster0" - - cases := []struct { - name string - config *api.Config - context string - wantServer string - wantErrStr string - wantWarning bool - }{ - { - name: "bad starting config", - context: context, - config: api.NewConfig(), - wantErrStr: "could not find cluster for context", - }, - { - name: "missing cluster", - context: context, - config: &api.Config{ - CurrentContext: context, - Contexts: map[string]*api.Context{}, - Clusters: map[string]*api.Cluster{}, - }, - wantErrStr: "could not find cluster for context", - }, - { - name: "missing server", - context: context, - config: &api.Config{ - CurrentContext: context, - Contexts: map[string]*api.Context{ - context: {Cluster: cluster}, - }, - Clusters: map[string]*api.Cluster{}, - }, - wantErrStr: "could not find server for context", - }, - { - name: "success", - context: context, - config: &api.Config{ - CurrentContext: context, - Contexts: map[string]*api.Context{ - context: {Cluster: cluster}, - }, - Clusters: map[string]*api.Cluster{ - cluster: {Server: server}, - }, - }, - wantServer: server, - }, - { - name: "warning", - context: context, - config: &api.Config{ - CurrentContext: context, - Contexts: map[string]*api.Context{ - context: {Cluster: cluster}, - }, - Clusters: map[string]*api.Cluster{ - cluster: {Server: "http://127.0.0.1:12345"}, - }, - }, - wantWarning: true, - wantServer: "http://127.0.0.1:12345", - }, - { - name: "use explicit Context different from current-context", - context: context, - config: &api.Config{ - CurrentContext: "ignored-context", // verify context override is used - Contexts: map[string]*api.Context{ - context: {Cluster: cluster}, - }, - Clusters: map[string]*api.Cluster{ - cluster: {Server: server}, - }, - }, - wantServer: server, - }, - } - - for i := range cases { - c := &cases[i] - t.Run(fmt.Sprintf("[%v] %v", i, c.name), func(t *testing.T) { - gotServer, warn, err := getServerFromKubeconfig(c.context, c.config) - if c.wantWarning && warn == nil { - t.Fatalf("wanted warning but got nil") - } else if !c.wantWarning && warn != nil { - t.Fatalf("wanted non-warning but got: %v", warn) - } - if c.wantErrStr != "" { - if err == nil { - t.Fatalf("wanted error including %q but got none", c.wantErrStr) - } else if !strings.Contains(err.Error(), c.wantErrStr) { - t.Fatalf("wanted error including %q but got %v", c.wantErrStr, err) - } - } else if c.wantErrStr == "" && err != nil { - t.Fatalf("wanted non-error but got %q", err) - } else if gotServer != c.wantServer { - t.Errorf("got server %v want %v", gotServer, server) - } - }) - } -} - -func TestCreateRemoteKubeconfig(t *testing.T) { - fakeClusterName := "fake-clusterName-0" - kubeconfig := strings.ReplaceAll(`apiVersion: v1 -clusters: -- cluster: - certificate-authority-data: Y2FEYXRh - server: https://1.2.3.4 - name: {cluster} -contexts: -- context: - cluster: {cluster} - user: {cluster} - name: {cluster} -current-context: {cluster} -kind: Config -preferences: {} -users: -- name: {cluster} - user: - token: token -`, "{cluster}", fakeClusterName) - - cases := []struct { - name string - clusterName string - context string - server string - haveTokenSecret *v1.Secret - updatedTokenSecret *v1.Secret - wantRemoteSecret *v1.Secret - wantErrStr string - }{ - { - name: "missing caData", - haveTokenSecret: makeSecret("", "", "token"), - context: "c0", - clusterName: fakeClusterName, - wantErrStr: errMissingRootCAKey.Error(), - }, - { - name: "missing token", - haveTokenSecret: makeSecret("", "caData", ""), - context: "c0", - clusterName: fakeClusterName, - wantErrStr: errMissingTokenKey.Error(), - }, - { - name: "bad server name", - haveTokenSecret: makeSecret("", "caData", "token"), - context: "c0", - clusterName: fakeClusterName, - server: "", - wantErrStr: "invalid kubeconfig:", - }, - { - name: "success after wait", - haveTokenSecret: makeSecret("", "caData", ""), - updatedTokenSecret: makeSecret("", "caData", "token"), // token is populated later - context: "c0", - clusterName: fakeClusterName, - server: "https://1.2.3.4", - wantRemoteSecret: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: remoteSecretNameFromClusterName(fakeClusterName), - Annotations: map[string]string{ - clusterNameAnnotationKey: fakeClusterName, - }, - Labels: map[string]string{ - multicluster.MultiClusterSecretLabel: "true", - }, - }, - Data: map[string][]byte{ - fakeClusterName: []byte(kubeconfig), - }, - }, - }, - { - name: "success", - haveTokenSecret: makeSecret("", "caData", "token"), - context: "c0", - clusterName: fakeClusterName, - server: "https://1.2.3.4", - wantRemoteSecret: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: remoteSecretNameFromClusterName(fakeClusterName), - Annotations: map[string]string{ - clusterNameAnnotationKey: fakeClusterName, - }, - Labels: map[string]string{ - multicluster.MultiClusterSecretLabel: "true", - }, - }, - Data: map[string][]byte{ - fakeClusterName: []byte(kubeconfig), - }, - }, - }, - } - oldBackoff := tokenWaitBackoff - tokenWaitBackoff = time.Millisecond - t.Cleanup(func() { - tokenWaitBackoff = oldBackoff - }) - for i := range cases { - c := &cases[i] - secName := remoteSecretNameFromClusterName(c.clusterName) - t.Run(fmt.Sprintf("[%v] %v", i, c.name), func(tt *testing.T) { - // no updateTokenSecret means re-fetching yields the same result - obj := []runtime.Object{c.haveTokenSecret} - if c.updatedTokenSecret != nil { - // fetching should give a different result than the token secret we pass in - obj = []runtime.Object{c.updatedTokenSecret} - } - client := kube.NewFakeClient(obj...) - - got, err := createRemoteSecretFromTokenAndServer(client, c.haveTokenSecret, c.clusterName, c.server, secName) - if c.wantErrStr != "" { - if err == nil { - tt.Fatalf("wanted error including %q but none", c.wantErrStr) - } else if !strings.Contains(err.Error(), c.wantErrStr) { - tt.Fatalf("wanted error including %q but %v", c.wantErrStr, err) - } - } else if c.wantErrStr == "" && err != nil { - tt.Fatalf("wanted non-error but got %q", err) - } else if diff := cmp.Diff(got, c.wantRemoteSecret); diff != "" { - tt.Fatalf(" got %v\nwant %v\ndiff %v", got, c.wantRemoteSecret, diff) - } - }) - } -} - -func TestWriteEncodedSecret(t *testing.T) { - s := &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - }, - } - - w := &fakeOutputWriter{failAfter: 0, injectError: errors.New("error")} - if err := writeEncodedObject(w, s); err == nil { - t.Error("want error on local write failure") - } - - w = &fakeOutputWriter{failAfter: 1, injectError: errors.New("error")} - if err := writeEncodedObject(w, s); err == nil { - t.Error("want error on remote write failure") - } - - w = &fakeOutputWriter{failAfter: 2, injectError: errors.New("error")} - if err := writeEncodedObject(w, s); err == nil { - t.Error("want error on third write failure") - } - - w = &fakeOutputWriter{} - if err := writeEncodedObject(w, s); err != nil { - t.Errorf("unexpected error: %v", err) - } - - want := `# This file is autogenerated, do not edit. -apiVersion: v1 -kind: Secret -metadata: - creationTimestamp: null - name: foo ---- -` - if w.String() != want { - t.Errorf("got\n%q\nwant\n%q", w.String(), want) - } -} - -func TestCreateRemoteSecretFromPlugin(t *testing.T) { - fakeClusterName := "fake-clusterName-0" - kubeconfig := strings.ReplaceAll(`apiVersion: v1 -clusters: -- cluster: - certificate-authority-data: Y2FEYXRh - server: https://1.2.3.4 - name: {cluster} -contexts: -- context: - cluster: {cluster} - user: {cluster} - name: {cluster} -current-context: {cluster} -kind: Config -preferences: {} -users: -- name: {cluster} - user: - auth-provider: - config: - k1: v1 - name: foobar -`, "{cluster}", fakeClusterName) - - cases := []struct { - name string - in *v1.Secret - context string - clusterName string - server string - authProviderConfig *api.AuthProviderConfig - want *v1.Secret - wantErrStr string - }{ - { - name: "error on missing caData", - in: makeSecret("", "", "token"), - context: "c0", - clusterName: fakeClusterName, - wantErrStr: errMissingRootCAKey.Error(), - }, - { - name: "success on missing token", - in: makeSecret("", "caData", ""), - context: "c0", - clusterName: fakeClusterName, - server: "https://1.2.3.4", - authProviderConfig: &api.AuthProviderConfig{ - Name: "foobar", - Config: map[string]string{ - "k1": "v1", - }, - }, - want: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: remoteSecretNameFromClusterName(fakeClusterName), - Annotations: map[string]string{ - clusterNameAnnotationKey: fakeClusterName, - }, - Labels: map[string]string{ - multicluster.MultiClusterSecretLabel: "true", - }, - }, - Data: map[string][]byte{ - fakeClusterName: []byte(kubeconfig), - }, - }, - }, - { - name: "success", - in: makeSecret("", "caData", "token"), - context: "c0", - clusterName: fakeClusterName, - server: "https://1.2.3.4", - authProviderConfig: &api.AuthProviderConfig{ - Name: "foobar", - Config: map[string]string{ - "k1": "v1", - }, - }, - want: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: remoteSecretNameFromClusterName(fakeClusterName), - Annotations: map[string]string{ - clusterNameAnnotationKey: fakeClusterName, - }, - Labels: map[string]string{ - multicluster.MultiClusterSecretLabel: "true", - }, - }, - Data: map[string][]byte{ - fakeClusterName: []byte(kubeconfig), - }, - }, - }, - } - - for i := range cases { - c := &cases[i] - secName := remoteSecretNameFromClusterName(c.clusterName) - t.Run(fmt.Sprintf("[%v] %v", i, c.name), func(tt *testing.T) { - got, err := createRemoteSecretFromPlugin(c.in, c.server, c.clusterName, secName, c.authProviderConfig) - if c.wantErrStr != "" { - if err == nil { - tt.Fatalf("wanted error including %q but none", c.wantErrStr) - } else if !strings.Contains(err.Error(), c.wantErrStr) { - tt.Fatalf("wanted error including %q but %v", c.wantErrStr, err) - } - } else if c.wantErrStr == "" && err != nil { - tt.Fatalf("wanted non-error but got %q", err) - } else if diff := cmp.Diff(got, c.want); diff != "" { - tt.Fatalf(" got %v\nwant %v\ndiff %v", got, c.want, diff) - } - }) - } -} - -func TestRemoteSecretOptions(t *testing.T) { - g := NewWithT(t) - - o := RemoteSecretOptions{} - flags := pflag.NewFlagSet("test", pflag.ContinueOnError) - o.addFlags(flags) - g.Expect(flags.Parse([]string{ - "--name", - "valid-name", - })).Should(Succeed()) - g.Expect(o.prepare(flags)).Should(Succeed()) - - o = RemoteSecretOptions{} - flags = pflag.NewFlagSet("test", pflag.ContinueOnError) - o.addFlags(flags) - g.Expect(flags.Parse([]string{ - "--name", - "?-invalid-name", - })).Should(Succeed()) - g.Expect(o.prepare(flags)).Should(Not(Succeed())) -} diff --git a/istioctl/pkg/multixds/gather.go b/istioctl/pkg/multixds/gather.go deleted file mode 100644 index a7b8c0acc..000000000 --- a/istioctl/pkg/multixds/gather.go +++ /dev/null @@ -1,276 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package multixds - -// multixds knows how to target either central Istiod or all the Istiod pods on a cluster. - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "net" - "net/url" -) - -import ( - xdsapi "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "istio.io/api/label" - istioversion "istio.io/pkg/version" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/clioptions" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/xds" - pilotxds "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -const ( - // Service account to create tokens in - tokenServiceAccount = "default" -) - -type ControlPlaneNotFoundError struct { - Namespace string -} - -func (c ControlPlaneNotFoundError) Error() string { - return fmt.Sprintf("no running Istio pods in %q", c.Namespace) -} - -var _ error = ControlPlaneNotFoundError{} - -// RequestAndProcessXds merges XDS responses from 1 central or 1..N K8s cluster-based XDS servers -// Deprecated This method makes multiple responses appear to come from a single control plane; -// consider using AllRequestAndProcessXds or FirstRequestAndProcessXds -// nolint: lll -func RequestAndProcessXds(dr *xdsapi.DiscoveryRequest, centralOpts clioptions.CentralControlPlaneOptions, istioNamespace string, kubeClient kube.ExtendedClient) (*xdsapi.DiscoveryResponse, error) { - responses, err := MultiRequestAndProcessXds(true, dr, centralOpts, istioNamespace, - istioNamespace, tokenServiceAccount, kubeClient) - if err != nil { - return nil, err - } - return mergeShards(responses) -} - -// nolint: lll -func queryEachShard(all bool, dr *xdsapi.DiscoveryRequest, istioNamespace string, kubeClient kube.ExtendedClient, centralOpts clioptions.CentralControlPlaneOptions) ([]*xdsapi.DiscoveryResponse, error) { - labelSelector := centralOpts.XdsPodLabel - if labelSelector == "" { - labelSelector = "app=istiod" - } - pods, err := kubeClient.GetIstioPods(context.TODO(), istioNamespace, map[string]string{ - "labelSelector": labelSelector, - "fieldSelector": "status.phase=Running", - }) - if err != nil { - return nil, err - } - if len(pods) == 0 { - return nil, ControlPlaneNotFoundError{istioNamespace} - } - - responses := []*xdsapi.DiscoveryResponse{} - xdsOpts := clioptions.CentralControlPlaneOptions{ - XDSSAN: makeSan(istioNamespace, kubeClient.Revision()), - CertDir: centralOpts.CertDir, - Timeout: centralOpts.Timeout, - } - dialOpts, err := xds.DialOptions(xdsOpts, istioNamespace, tokenServiceAccount, kubeClient) - if err != nil { - return nil, err - } - for _, pod := range pods { - fw, err := kubeClient.NewPortForwarder(pod.Name, pod.Namespace, "localhost", 0, centralOpts.XdsPodPort) - if err != nil { - return nil, err - } - err = fw.Start() - if err != nil { - return nil, err - } - defer fw.Close() - xdsOpts.Xds = fw.Address() - response, err := xds.GetXdsResponse(dr, istioNamespace, tokenServiceAccount, xdsOpts, dialOpts) - if err != nil { - return nil, fmt.Errorf("could not get XDS from discovery pod %q: %v", pod.Name, err) - } - responses = append(responses, response) - if !all && len(responses) > 0 { - break - } - } - return responses, nil -} - -func mergeShards(responses map[string]*xdsapi.DiscoveryResponse) (*xdsapi.DiscoveryResponse, error) { - retval := xdsapi.DiscoveryResponse{} - if len(responses) == 0 { - return &retval, nil - } - - for _, response := range responses { - // Combine all the shards as one, even if that means losing information about - // the control plane version from each shard. - retval.ControlPlane = response.ControlPlane - retval.Resources = append(retval.Resources, response.Resources...) - } - - return &retval, nil -} - -func makeSan(istioNamespace, revision string) string { - if revision == "" { - return fmt.Sprintf("istiod.%s.svc", istioNamespace) - } - return fmt.Sprintf("istiod-%s.%s.svc", revision, istioNamespace) -} - -// AllRequestAndProcessXds returns all XDS responses from 1 central or 1..N K8s cluster-based XDS servers -// nolint: lll -func AllRequestAndProcessXds(dr *xdsapi.DiscoveryRequest, centralOpts clioptions.CentralControlPlaneOptions, istioNamespace string, - ns string, serviceAccount string, kubeClient kube.ExtendedClient) (map[string]*xdsapi.DiscoveryResponse, error) { - return MultiRequestAndProcessXds(true, dr, centralOpts, istioNamespace, ns, serviceAccount, kubeClient) -} - -// FirstRequestAndProcessXds returns all XDS responses from 1 central or 1..N K8s cluster-based XDS servers, -// stopping after the first response that returns any resources. -// nolint: lll -func FirstRequestAndProcessXds(dr *xdsapi.DiscoveryRequest, centralOpts clioptions.CentralControlPlaneOptions, istioNamespace string, - ns string, serviceAccount string, kubeClient kube.ExtendedClient) (map[string]*xdsapi.DiscoveryResponse, error) { - return MultiRequestAndProcessXds(false, dr, centralOpts, istioNamespace, ns, serviceAccount, kubeClient) -} - -type xdsAddr struct { - gcpProject, host, istiod string -} - -func getXdsAddressFromWebhooks(client kube.ExtendedClient) (*xdsAddr, error) { - webhooks, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().List(context.Background(), metav1.ListOptions{ - LabelSelector: fmt.Sprintf("%s=%s,!istio.io/tag", label.IoIstioRev.Name, client.Revision()), - }) - if err != nil { - return nil, err - } - for _, whc := range webhooks.Items { - for _, wh := range whc.Webhooks { - if wh.ClientConfig.URL != nil { - u, err := url.Parse(*wh.ClientConfig.URL) - if err != nil { - return nil, fmt.Errorf("parsing webhook URL: %w", err) - } - if isMCPAddr(u) { - return parseMCPAddr(u) - } - port := u.Port() - if port == "" { - port = "443" // default from Kubernetes - } - return &xdsAddr{host: net.JoinHostPort(u.Hostname(), port)}, nil - } - } - } - return nil, errors.New("xds address not found") -} - -// nolint: lll -func MultiRequestAndProcessXds(all bool, dr *xdsapi.DiscoveryRequest, centralOpts clioptions.CentralControlPlaneOptions, istioNamespace string, - ns string, serviceAccount string, kubeClient kube.ExtendedClient) (map[string]*xdsapi.DiscoveryResponse, error) { - // If Central Istiod case, just call it - if ns == "" { - ns = istioNamespace - } - if ns == istioNamespace { - serviceAccount = tokenServiceAccount - } - if centralOpts.Xds != "" { - dialOpts, err := xds.DialOptions(centralOpts, ns, serviceAccount, kubeClient) - if err != nil { - return nil, err - } - response, err := xds.GetXdsResponse(dr, ns, serviceAccount, centralOpts, dialOpts) - if err != nil { - return nil, err - } - return map[string]*xdsapi.DiscoveryResponse{ - CpInfo(response).ID: response, - }, nil - } - - // Self-administered case. Find all Istiods in revision using K8s, port-forward and call each in turn - responses, err := queryEachShard(all, dr, istioNamespace, kubeClient, centralOpts) - if err != nil { - if _, ok := err.(ControlPlaneNotFoundError); ok { - // Attempt to get the XDS address from the webhook and try again - addr, err := getXdsAddressFromWebhooks(kubeClient) - if err == nil { - centralOpts.Xds = addr.host - centralOpts.GCPProject = addr.gcpProject - centralOpts.IstiodAddr = addr.istiod - dialOpts, err := xds.DialOptions(centralOpts, istioNamespace, tokenServiceAccount, kubeClient) - if err != nil { - return nil, err - } - response, err := xds.GetXdsResponse(dr, istioNamespace, tokenServiceAccount, centralOpts, dialOpts) - if err != nil { - return nil, err - } - return map[string]*xdsapi.DiscoveryResponse{ - CpInfo(response).ID: response, - }, nil - } - } - return nil, err - } - return mapShards(responses) -} - -func mapShards(responses []*xdsapi.DiscoveryResponse) (map[string]*xdsapi.DiscoveryResponse, error) { - retval := map[string]*xdsapi.DiscoveryResponse{} - - for _, response := range responses { - retval[CpInfo(response).ID] = response - } - - return retval, nil -} - -// CpInfo returns the Istio control plane info from JSON-encoded XDS ControlPlane Identifier -func CpInfo(xdsResponse *xdsapi.DiscoveryResponse) pilotxds.IstioControlPlaneInstance { - if xdsResponse.ControlPlane == nil { - return pilotxds.IstioControlPlaneInstance{ - Component: "MISSING", - ID: "MISSING", - Info: istioversion.BuildInfo{ - Version: "MISSING CP ID", - }, - } - } - - cpID := pilotxds.IstioControlPlaneInstance{} - err := json.Unmarshal([]byte(xdsResponse.ControlPlane.Identifier), &cpID) - if err != nil { - return pilotxds.IstioControlPlaneInstance{ - Component: "INVALID", - ID: "INVALID", - Info: istioversion.BuildInfo{ - Version: "INVALID CP ID", - }, - } - } - return cpID -} diff --git a/istioctl/pkg/multixds/google.go b/istioctl/pkg/multixds/google.go deleted file mode 100644 index c3a2d2307..000000000 --- a/istioctl/pkg/multixds/google.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package multixds - -import ( - "fmt" - "net/url" - "strings" -) - -func isMCPAddr(u *url.URL) bool { - return strings.HasSuffix(u.Host, ".googleapis.com") || strings.HasSuffix(u.Host, ".googleapis.com:443") -} - -func parseMCPAddr(u *url.URL) (*xdsAddr, error) { - ret := &xdsAddr{host: u.Host} - if !strings.HasSuffix(ret.host, ":443") { - ret.host += ":443" - } - const projSeg = "/projects/" - i := strings.Index(u.Path, projSeg) - if i == -1 { - return nil, fmt.Errorf("webhook URL %s doesn't contain the projects segment", u) - } - i += len(projSeg) - j := strings.IndexByte(u.Path[i:], '/') - if j == -1 { - return nil, fmt.Errorf("webhook URL %s is malformed", u) - } - ret.gcpProject = u.Path[i : i+j] - - const crSeg = "/ISTIO_META_CLOUDRUN_ADDR/" - i += j - j = strings.Index(u.Path[i:], crSeg) - if j == -1 { - return nil, fmt.Errorf("webhook URL %s is missing %s", u, crSeg) - } - ret.istiod = u.Path[i+j+len(crSeg):] - - return ret, nil -} diff --git a/istioctl/pkg/tag/generate.go b/istioctl/pkg/tag/generate.go deleted file mode 100644 index a958b41ec..000000000 --- a/istioctl/pkg/tag/generate.go +++ /dev/null @@ -1,350 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package tag - -import ( - "bytes" - "context" - "fmt" - "net/url" - "os" - "strings" -) - -import ( - admit_v1 "k8s.io/api/admissionregistration/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/serializer" - "k8s.io/apimachinery/pkg/runtime/serializer/json" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/helm" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -const ( - IstioTagLabel = "istio.io/tag" - DefaultRevisionName = "dubbo" - - defaultChart = "default" - pilotDiscoveryChart = "istio-control/istio-discovery" - revisionTagTemplateName = "revision-tags.yaml" - vwhTemplateName = "validatingwebhook.yaml" - - istioInjectionWebhookSuffix = "sidecar-injector.istio.io" -) - -// tagWebhookConfig holds config needed to render a tag webhook. -type tagWebhookConfig struct { - Tag string - Revision string - URL string - Path string - CABundle string - IstioNamespace string -} - -// GenerateOptions is the group of options needed to generate a tag webhook. -type GenerateOptions struct { - // Tag is the name of the revision tag to generate. - Tag string - // Revision is the revision to associate the revision tag with. - Revision string - // WebhookName is an override for the mutating webhook name. - WebhookName string - // ManifestsPath specifies where the manifests to render the mutatingwebhook can be found. - // TODO(Monkeyanator) once we stop using Helm templating remove this. - ManifestsPath string - // Generate determines whether we should just generate the webhooks without applying. This - // applying is not done here but we are looser with checks when doing generate. - Generate bool - // Overwrite removes analysis checks around existing webhooks. - Overwrite bool - // AutoInjectNamespaces controls, if the sidecars should be injected into all namespaces by default. - AutoInjectNamespaces bool -} - -// Generate generates the manifests for a revision tag pointed the given revision. -func Generate(ctx context.Context, client kube.ExtendedClient, opts *GenerateOptions, istioNS string) (string, error) { - // abort if there exists a revision with the target tag name - revWebhookCollisions, err := GetWebhooksWithRevision(ctx, client, opts.Tag) - if err != nil { - return "", err - } - if !opts.Generate && !opts.Overwrite && - len(revWebhookCollisions) > 0 && opts.Tag != DefaultRevisionName { - return "", fmt.Errorf("cannot create revision tag %q: found existing control plane revision with same name", opts.Tag) - } - - // find canonical revision webhook to base our tag webhook off of - revWebhooks, err := GetWebhooksWithRevision(ctx, client, opts.Revision) - if err != nil { - return "", err - } - if len(revWebhooks) == 0 { - return "", fmt.Errorf("cannot modify tag: cannot find MutatingWebhookConfiguration with revision %q", opts.Revision) - } - if len(revWebhooks) > 1 { - return "", fmt.Errorf("cannot modify tag: found multiple canonical webhooks with revision %q", opts.Revision) - } - - whs, err := GetWebhooksWithTag(ctx, client, opts.Tag) - if err != nil { - return "", err - } - if len(whs) > 0 && !opts.Overwrite { - return "", fmt.Errorf("revision tag %q already exists, and --overwrite is false", opts.Tag) - } - - tagWhConfig, err := tagWebhookConfigFromCanonicalWebhook(revWebhooks[0], opts.Tag, istioNS) - if err != nil { - return "", fmt.Errorf("failed to create tag webhook config: %w", err) - } - tagWhYAML, err := generateMutatingWebhook(tagWhConfig, opts.WebhookName, opts.ManifestsPath, opts.AutoInjectNamespaces) - if err != nil { - return "", fmt.Errorf("failed to create tag webhook: %w", err) - } - - if opts.Tag == DefaultRevisionName { - if !opts.Generate { - // deactivate other istio-injection=enabled injectors if using default revisions. - err := DeactivateIstioInjectionWebhook(ctx, client) - if err != nil { - return "", fmt.Errorf("failed deactivating existing default revision: %w", err) - } - // delete deprecated validating webhook configuration if it exists. - err = DeleteDeprecatedValidator(ctx, client) - if err != nil { - return "", fmt.Errorf("failed removing deprecated validating webhook: %w", err) - } - } - - // TODO(Monkeyanator) should extract the validationURL from revision's validating webhook here. However, - // to ease complexity when pointing default to revision without per-revision validating webhook, - // instead grab the endpoint information from the mutating webhook. This is not strictly correct. - validationWhConfig := fixWhConfig(tagWhConfig) - - vwhYAML, err := generateValidatingWebhook(validationWhConfig, opts.ManifestsPath) - if err != nil { - return "", fmt.Errorf("failed to create validating webhook: %w", err) - } - tagWhYAML = fmt.Sprintf(`%s ---- -%s`, tagWhYAML, vwhYAML) - } - - return tagWhYAML, nil -} - -func fixWhConfig(whConfig *tagWebhookConfig) *tagWebhookConfig { - if whConfig.URL != "" { - webhookURL, err := url.Parse(whConfig.URL) - if err == nil { - webhookURL.Path = "/validate" - whConfig.URL = webhookURL.String() - } - } - return whConfig -} - -// Create applies the given tag manifests. -func Create(client kube.ExtendedClient, manifests string) error { - if err := applyYAML(client, manifests, "dubbo-system"); err != nil { - return fmt.Errorf("failed to apply tag manifests to cluster: %v", err) - } - return nil -} - -// generateValidatingWebhook renders a validating webhook configuration from the given tagWebhookConfig. -func generateValidatingWebhook(config *tagWebhookConfig, chartPath string) (string, error) { - r := helm.NewHelmRenderer(chartPath, defaultChart, "Pilot", config.IstioNamespace, nil) - - if err := r.Run(); err != nil { - return "", fmt.Errorf("failed running Helm renderer: %v", err) - } - - values := fmt.Sprintf(` -revision: %q -base: - validationURL: %s -`, config.Revision, config.URL) - - validatingWebhookYAML, err := r.RenderManifestFiltered(values, func(tmplName string) bool { - return strings.Contains(tmplName, vwhTemplateName) - }) - if err != nil { - return "", fmt.Errorf("failed rendering istio-control manifest: %v", err) - } - - scheme := runtime.NewScheme() - codecFactory := serializer.NewCodecFactory(scheme) - deserializer := codecFactory.UniversalDeserializer() - serializer := json.NewSerializerWithOptions( - json.DefaultMetaFactory, nil, nil, json.SerializerOptions{ - Yaml: true, - Pretty: true, - Strict: true, - }) - - whObject, _, err := deserializer.Decode([]byte(validatingWebhookYAML), nil, &admit_v1.ValidatingWebhookConfiguration{}) - if err != nil { - return "", fmt.Errorf("could not decode generated webhook: %w", err) - } - decodedWh := whObject.(*admit_v1.ValidatingWebhookConfiguration) - for i := range decodedWh.Webhooks { - decodedWh.Webhooks[i].ClientConfig.CABundle = []byte(config.CABundle) - } - - whBuf := new(bytes.Buffer) - if err = serializer.Encode(decodedWh, whBuf); err != nil { - return "", err - } - - return whBuf.String(), nil -} - -// generateMutatingWebhook renders a mutating webhook configuration from the given tagWebhookConfig. -func generateMutatingWebhook(config *tagWebhookConfig, webhookName, chartPath string, autoInjectNamespaces bool) (string, error) { - r := helm.NewHelmRenderer(chartPath, pilotDiscoveryChart, "Pilot", config.IstioNamespace, nil) - - if err := r.Run(); err != nil { - return "", fmt.Errorf("failed running Helm renderer: %v", err) - } - - values := fmt.Sprintf(` -revision: %q -revisionTags: - - %s - -sidecarInjectorWebhook: - enableNamespacesByDefault: %t - objectSelector: - enabled: true - autoInject: true - -istiodRemote: - injectionURL: %s -`, config.Revision, config.Tag, autoInjectNamespaces, config.URL) - - tagWebhookYaml, err := r.RenderManifestFiltered(values, func(tmplName string) bool { - return strings.Contains(tmplName, revisionTagTemplateName) - }) - if err != nil { - return "", fmt.Errorf("failed rendering istio-control manifest: %v", err) - } - - scheme := runtime.NewScheme() - codecFactory := serializer.NewCodecFactory(scheme) - deserializer := codecFactory.UniversalDeserializer() - serializer := json.NewSerializerWithOptions( - json.DefaultMetaFactory, nil, nil, json.SerializerOptions{ - Yaml: true, - Pretty: true, - Strict: true, - }) - - whObject, _, err := deserializer.Decode([]byte(tagWebhookYaml), nil, &admit_v1.MutatingWebhookConfiguration{}) - if err != nil { - return "", fmt.Errorf("could not decode generated webhook: %w", err) - } - decodedWh := whObject.(*admit_v1.MutatingWebhookConfiguration) - for i := range decodedWh.Webhooks { - decodedWh.Webhooks[i].ClientConfig.CABundle = []byte(config.CABundle) - if decodedWh.Webhooks[i].ClientConfig.Service != nil { - decodedWh.Webhooks[i].ClientConfig.Service.Path = &config.Path - } - } - if webhookName != "" { - decodedWh.Name = webhookName - } - - whBuf := new(bytes.Buffer) - if err = serializer.Encode(decodedWh, whBuf); err != nil { - return "", err - } - - return whBuf.String(), nil -} - -// tagWebhookConfigFromCanonicalWebhook parses configuration needed to create tag webhook from existing revision webhook. -func tagWebhookConfigFromCanonicalWebhook(wh admit_v1.MutatingWebhookConfiguration, tagName, istioNS string) (*tagWebhookConfig, error) { - rev, err := GetWebhookRevision(wh) - if err != nil { - return nil, err - } - // if the revision is "default", render templates with an empty revision - if rev == DefaultRevisionName { - rev = "" - } - - var injectionURL, caBundle, path string - found := false - for _, w := range wh.Webhooks { - if strings.HasSuffix(w.Name, istioInjectionWebhookSuffix) { - found = true - caBundle = string(w.ClientConfig.CABundle) - if w.ClientConfig.URL != nil { - injectionURL = *w.ClientConfig.URL - } - if w.ClientConfig.Service != nil { - if w.ClientConfig.Service.Path != nil { - path = *w.ClientConfig.Service.Path - } - } - break - } - } - if !found { - return nil, fmt.Errorf("could not find sidecar-injector webhook in canonical webhook %q", wh.Name) - } - - return &tagWebhookConfig{ - Tag: tagName, - Revision: rev, - URL: injectionURL, - CABundle: caBundle, - IstioNamespace: istioNS, - Path: path, - }, nil -} - -// applyYAML taken from remote_secret.go -func applyYAML(client kube.ExtendedClient, yamlContent, ns string) error { - yamlFile, err := writeToTempFile(yamlContent) - if err != nil { - return fmt.Errorf("failed creating manifest file: %w", err) - } - - // Apply the YAML to the cluster. - if err := client.ApplyYAMLFiles(ns, yamlFile); err != nil { - return fmt.Errorf("failed applying manifest %s: %v", yamlFile, err) - } - return nil -} - -// writeToTempFile taken from remote_secret.go -func writeToTempFile(content string) (string, error) { - outFile, err := os.CreateTemp("", "revision-tag-manifest-*") - if err != nil { - return "", fmt.Errorf("failed creating temp file for manifest: %w", err) - } - defer func() { _ = outFile.Close() }() - - if _, err := outFile.Write([]byte(content)); err != nil { - return "", fmt.Errorf("failed writing manifest file: %w", err) - } - return outFile.Name(), nil -} diff --git a/istioctl/pkg/tag/generate_test.go b/istioctl/pkg/tag/generate_test.go deleted file mode 100644 index 97da22b84..000000000 --- a/istioctl/pkg/tag/generate_test.go +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package tag - -import ( - "fmt" - "path/filepath" - "testing" -) - -import ( - "istio.io/api/label" - admit_v1 "k8s.io/api/admissionregistration/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/serializer" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/env" -) - -var ( - defaultRevisionCanonicalWebhook = admit_v1.MutatingWebhookConfiguration{ - ObjectMeta: metav1.ObjectMeta{ - Name: "istio-sidecar-injector", - Labels: map[string]string{label.IoIstioRev.Name: "dubbo"}, - }, - Webhooks: []admit_v1.MutatingWebhook{ - { - Name: fmt.Sprintf("namespace.%s", istioInjectionWebhookSuffix), - ClientConfig: admit_v1.WebhookClientConfig{ - Service: &admit_v1.ServiceReference{ - Namespace: "dubbo-system", - Name: "istiod", - }, - CABundle: []byte("ca"), - }, - }, - { - Name: fmt.Sprintf("object.%s", istioInjectionWebhookSuffix), - ClientConfig: admit_v1.WebhookClientConfig{ - Service: &admit_v1.ServiceReference{ - Namespace: "dubbo-system", - Name: "istiod", - }, - CABundle: []byte("ca"), - }, - }, - }, - } - samplePath = "/sample/path" - revisionCanonicalWebhook = admit_v1.MutatingWebhookConfiguration{ - ObjectMeta: metav1.ObjectMeta{ - Name: "istio-sidecar-injector-revision", - Labels: map[string]string{label.IoIstioRev.Name: "revision"}, - }, - Webhooks: []admit_v1.MutatingWebhook{ - { - Name: fmt.Sprintf("namespace.%s", istioInjectionWebhookSuffix), - ClientConfig: admit_v1.WebhookClientConfig{ - Service: &admit_v1.ServiceReference{ - Namespace: "dubbo-system", - Name: "istiod-revision", - Path: &samplePath, - }, - CABundle: []byte("ca"), - }, - }, - { - Name: fmt.Sprintf("object.%s", istioInjectionWebhookSuffix), - ClientConfig: admit_v1.WebhookClientConfig{ - Service: &admit_v1.ServiceReference{ - Namespace: "dubbo-system", - Name: "istiod-revision", - }, - CABundle: []byte("ca"), - }, - }, - }, - } - remoteInjectionURL = "https://random.host.com/inject/cluster/cluster1/net/net1" - revisionCanonicalWebhookRemote = admit_v1.MutatingWebhookConfiguration{ - ObjectMeta: metav1.ObjectMeta{ - Name: "istio-sidecar-injector-revision", - Labels: map[string]string{label.IoIstioRev.Name: "revision"}, - }, - Webhooks: []admit_v1.MutatingWebhook{ - { - Name: fmt.Sprintf("namespace.%s", istioInjectionWebhookSuffix), - ClientConfig: admit_v1.WebhookClientConfig{ - URL: &remoteInjectionURL, - CABundle: []byte("ca"), - }, - }, - { - Name: fmt.Sprintf("object.%s", istioInjectionWebhookSuffix), - ClientConfig: admit_v1.WebhookClientConfig{ - URL: &remoteInjectionURL, - CABundle: []byte("ca"), - }, - }, - }, - } - remoteValidationURL = "https://random.host.com/validate" -) - -func TestGenerateValidatingWebhook(t *testing.T) { - tcs := []struct { - name string - webhook admit_v1.MutatingWebhookConfiguration - whURL string - whSVC string - whCA string - }{ - { - name: "webhook-pointing-to-service", - webhook: revisionCanonicalWebhook, - whURL: "", - whSVC: "istiod-revision", - whCA: "ca", - }, - { - name: "webhook-pointing-to-url", - webhook: revisionCanonicalWebhookRemote, - whURL: remoteValidationURL, - whSVC: "", - whCA: "ca", - }, - } - scheme := runtime.NewScheme() - codecFactory := serializer.NewCodecFactory(scheme) - deserializer := codecFactory.UniversalDeserializer() - - for _, tc := range tcs { - webhookConfig, err := tagWebhookConfigFromCanonicalWebhook(tc.webhook, "default", "dubbo-system") - if err != nil { - t.Fatalf("webhook parsing failed with error: %v", err) - } - webhookYAML, err := generateValidatingWebhook(fixWhConfig(webhookConfig), filepath.Join(env.IstioSrc, "manifests")) - if err != nil { - t.Fatalf("tag webhook YAML generation failed with error: %v", err) - } - - vwhObject, _, err := deserializer.Decode([]byte(webhookYAML), nil, &admit_v1.ValidatingWebhookConfiguration{}) - if err != nil { - t.Fatalf("could not parse webhook from generated YAML: %s", vwhObject) - } - wh := vwhObject.(*admit_v1.ValidatingWebhookConfiguration) - - for _, webhook := range wh.Webhooks { - validationWhConf := webhook.ClientConfig - if tc.whSVC != "" { - if validationWhConf.Service == nil { - t.Fatalf("expected validation service %s, got nil", tc.whSVC) - } - if validationWhConf.Service.Name != tc.whSVC { - t.Fatalf("expected validation service %s, got %s", tc.whSVC, validationWhConf.Service.Name) - } - } - if tc.whURL != "" { - if validationWhConf.URL == nil { - t.Fatalf("expected validation URL %s, got nil", tc.whURL) - } - if *validationWhConf.URL != tc.whURL { - t.Fatalf("expected validation URL %s, got %s", tc.whURL, *validationWhConf.URL) - } - } - if tc.whCA != "" { - if string(validationWhConf.CABundle) != tc.whCA { - t.Fatalf("expected CA bundle %q, got %q", tc.whCA, validationWhConf.CABundle) - } - } - } - } -} - -func TestGenerateMutatingWebhook(t *testing.T) { - tcs := []struct { - name string - webhook admit_v1.MutatingWebhookConfiguration - tagName string - whURL string - whSVC string - whCA string - numWebhooks int - }{ - { - name: "webhook-pointing-to-service", - webhook: revisionCanonicalWebhook, - tagName: "canary", - whURL: "", - whSVC: "istiod-revision", - whCA: "ca", - numWebhooks: 2, - }, - { - name: "webhook-pointing-to-url", - webhook: revisionCanonicalWebhookRemote, - tagName: "canary", - whURL: remoteInjectionURL, - whSVC: "", - whCA: "ca", - numWebhooks: 2, - }, - { - name: "webhook-pointing-to-default-revision", - webhook: defaultRevisionCanonicalWebhook, - tagName: "canary", - whURL: "", - whSVC: "istiod", - whCA: "ca", - numWebhooks: 2, - }, - { - name: "webhook-pointing-to-default-revision", - webhook: defaultRevisionCanonicalWebhook, - tagName: "default", - whURL: "", - whSVC: "istiod", - whCA: "ca", - numWebhooks: 4, - }, - } - scheme := runtime.NewScheme() - codecFactory := serializer.NewCodecFactory(scheme) - deserializer := codecFactory.UniversalDeserializer() - - for _, tc := range tcs { - webhookConfig, err := tagWebhookConfigFromCanonicalWebhook(tc.webhook, tc.tagName, "dubbo-system") - if err != nil { - t.Fatalf("webhook parsing failed with error: %v", err) - } - webhookYAML, err := generateMutatingWebhook(webhookConfig, "", filepath.Join(env.IstioSrc, "manifests"), false) - if err != nil { - t.Fatalf("tag webhook YAML generation failed with error: %v", err) - } - - whObject, _, err := deserializer.Decode([]byte(webhookYAML), nil, &admit_v1.MutatingWebhookConfiguration{}) - if err != nil { - t.Fatalf("could not parse webhook from generated YAML: %s", webhookYAML) - } - wh := whObject.(*admit_v1.MutatingWebhookConfiguration) - - // expect both namespace.sidecar-injector.istio.io and object.sidecar-injector.istio.io webhooks - if len(wh.Webhooks) != tc.numWebhooks { - t.Errorf("expected %d webhook(s) in MutatingWebhookConfiguration, found %d", - tc.numWebhooks, len(wh.Webhooks)) - } - tag, exists := wh.ObjectMeta.Labels[IstioTagLabel] - if !exists { - t.Errorf("expected tag webhook to have %s label, did not find", IstioTagLabel) - } - if tag != tc.tagName { - t.Errorf("expected tag webhook to have istio.io/tag=%s, found %s instead", tc.tagName, tag) - } - - // ensure all webhooks have the correct client config - for _, webhook := range wh.Webhooks { - injectionWhConf := webhook.ClientConfig - if tc.whSVC != "" { - if injectionWhConf.Service == nil { - t.Fatalf("expected injection service %s, got nil", tc.whSVC) - } - if injectionWhConf.Service.Name != tc.whSVC { - t.Fatalf("expected injection service %s, got %s", tc.whSVC, injectionWhConf.Service.Name) - } - } - if tc.whURL != "" { - if injectionWhConf.URL == nil { - t.Fatalf("expected injection URL %s, got nil", tc.whURL) - } - if *injectionWhConf.URL != tc.whURL { - t.Fatalf("expected injection URL %s, got %s", tc.whURL, *injectionWhConf.URL) - } - } - if tc.whCA != "" { - if string(injectionWhConf.CABundle) != tc.whCA { - t.Fatalf("expected CA bundle %q, got %q", tc.whCA, injectionWhConf.CABundle) - } - } - } - } -} diff --git a/istioctl/pkg/tag/util.go b/istioctl/pkg/tag/util.go deleted file mode 100644 index a403b1bc5..000000000 --- a/istioctl/pkg/tag/util.go +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package tag - -import ( - "context" - "fmt" - "strings" -) - -import ( - "github.com/hashicorp/go-multierror" - "istio.io/api/label" - admit_v1 "k8s.io/api/admissionregistration/v1" - kerrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" -) - -func GetTagWebhooks(ctx context.Context, client kubernetes.Interface) ([]admit_v1.MutatingWebhookConfiguration, error) { - webhooks, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().List(ctx, metav1.ListOptions{ - LabelSelector: IstioTagLabel, - }) - if err != nil { - return nil, err - } - return webhooks.Items, nil -} - -// GetWebhooksWithTag returns webhooks tagged with istio.io/tag=. -func GetWebhooksWithTag(ctx context.Context, client kubernetes.Interface, tag string) ([]admit_v1.MutatingWebhookConfiguration, error) { - webhooks, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().List(ctx, metav1.ListOptions{ - LabelSelector: fmt.Sprintf("%s=%s", IstioTagLabel, tag), - }) - if err != nil { - return nil, err - } - return webhooks.Items, nil -} - -// GetWebhooksWithRevision returns webhooks tagged with istio.io/rev= and NOT TAGGED with istio.io/tag. -// this retrieves the webhook created at revision installation rather than tag webhooks -func GetWebhooksWithRevision(ctx context.Context, client kubernetes.Interface, rev string) ([]admit_v1.MutatingWebhookConfiguration, error) { - webhooks, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().List(ctx, metav1.ListOptions{ - LabelSelector: fmt.Sprintf("%s=%s,!%s", label.IoIstioRev.Name, rev, IstioTagLabel), - }) - if err != nil { - return nil, err - } - return webhooks.Items, nil -} - -// GetNamespacesWithTag retrieves all namespaces pointed at the given tag. -func GetNamespacesWithTag(ctx context.Context, client kubernetes.Interface, tag string) ([]string, error) { - namespaces, err := client.CoreV1().Namespaces().List(ctx, metav1.ListOptions{ - LabelSelector: fmt.Sprintf("%s=%s", label.IoIstioRev.Name, tag), - }) - if err != nil { - return nil, err - } - - nsNames := make([]string, len(namespaces.Items)) - for i, ns := range namespaces.Items { - nsNames[i] = ns.Name - } - return nsNames, nil -} - -// GetWebhookTagName extracts tag name from webhook object. -func GetWebhookTagName(wh admit_v1.MutatingWebhookConfiguration) (string, error) { - if tagName, ok := wh.ObjectMeta.Labels[IstioTagLabel]; ok { - return tagName, nil - } - return "", fmt.Errorf("could not extract tag name from webhook") -} - -// GetWebhookRevision extracts tag target revision from webhook object. -func GetWebhookRevision(wh admit_v1.MutatingWebhookConfiguration) (string, error) { - if tagName, ok := wh.ObjectMeta.Labels[label.IoIstioRev.Name]; ok { - return tagName, nil - } - return "", fmt.Errorf("could not extract tag revision from webhook") -} - -// DeleteTagWebhooks deletes the given webhooks. -func DeleteTagWebhooks(ctx context.Context, client kubernetes.Interface, tag string) error { - webhooks, err := GetWebhooksWithTag(ctx, client, tag) - if err != nil { - return err - } - var result error - for _, wh := range webhooks { - result = multierror.Append(client.AdmissionregistrationV1().MutatingWebhookConfigurations().Delete(ctx, wh.Name, metav1.DeleteOptions{})).ErrorOrNil() - } - return result -} - -// DeleteDeprecatedValidator deletes the deprecated validating webhook configuration. This is used after a user explicitly -// sets a new default revision. -func DeleteDeprecatedValidator(ctx context.Context, client kubernetes.Interface) error { - vwhs, err := client.AdmissionregistrationV1().ValidatingWebhookConfigurations().List(ctx, metav1.ListOptions{ - LabelSelector: "app=istiod", - }) - if err != nil { - return err - } - var errs *multierror.Error - for _, vwh := range vwhs.Items { - // hacky but we want to remove the validators that used to be in base, not the per-revision webhooks. - if !strings.Contains(vwh.Name, "validator") { - errs = multierror.Append(errs, - client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Delete(ctx, vwh.Name, metav1.DeleteOptions{})) - } - } - if kerrors.IsNotFound(err) { - return nil - } - return errs.ErrorOrNil() -} - -var neverMatch = &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "istio.io/deactivated": "never-match", - }, -} - -// PreviousInstallExists checks whether there is an existing Istio installation. Should be used in installer when deciding -// whether to make an installation the default. -func PreviousInstallExists(ctx context.Context, client kubernetes.Interface) bool { - mwhs, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().List(ctx, metav1.ListOptions{ - LabelSelector: "app=sidecar-injector", - }) - if err != nil { - return false - } - return len(mwhs.Items) > 0 -} - -// DeactivateIstioInjectionWebhook deactivates the istio-injection webhook from the given MutatingWebhookConfiguration if exists. -// used rather than just deleting the webhook since we want to keep it around after changing the default so user can later -// switch back to it. This is a hack but it is meant to cover a corner case where a user wants to migrate from a non-revisioned -// old version and then later decides to switch back to the old revision again. -func DeactivateIstioInjectionWebhook(ctx context.Context, client kubernetes.Interface) error { - whs, err := GetWebhooksWithRevision(ctx, client, DefaultRevisionName) - if err != nil { - return err - } - if len(whs) == 0 { - // no revision with default, no action required. - return nil - } - if len(whs) > 1 { - return fmt.Errorf("expected a single webhook for default revision") - } - webhook := whs[0] - for i := range webhook.Webhooks { - wh := webhook.Webhooks[i] - // this is an abomination, but if this isn't a per-revision webhook, we want to make it ineffectual - // without deleting it. Add a nonsense match. - wh.NamespaceSelector = neverMatch - wh.ObjectSelector = neverMatch - webhook.Webhooks[i] = wh - } - admit := client.AdmissionregistrationV1().MutatingWebhookConfigurations() - _, err = admit.Update(ctx, &webhook, metav1.UpdateOptions{}) - if err != nil { - return err - } - - return nil -} diff --git a/istioctl/pkg/util/clusters/wrapper.go b/istioctl/pkg/util/clusters/wrapper.go deleted file mode 100644 index c4199f3f6..000000000 --- a/istioctl/pkg/util/clusters/wrapper.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package clusters - -import ( - adminapi "github.com/envoyproxy/go-control-plane/envoy/admin/v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -// Wrapper is a wrapper around the Envoy Clusters -// It has extra helper functions for handling any/struct/marshal protobuf pain -type Wrapper struct { - *adminapi.Clusters -} - -// MarshalJSON is a custom marshaller to handle protobuf pain -func (w *Wrapper) MarshalJSON() ([]byte, error) { - return protomarshal.Marshal(w) -} - -// UnmarshalJSON is a custom unmarshaller to handle protobuf pain -func (w *Wrapper) UnmarshalJSON(b []byte) error { - cd := &adminapi.Clusters{} - err := protomarshal.UnmarshalAllowUnknown(b, cd) - *w = Wrapper{cd} - return err -} diff --git a/istioctl/pkg/util/configdump/bootstrap.go b/istioctl/pkg/util/configdump/bootstrap.go deleted file mode 100644 index 45b2c784c..000000000 --- a/istioctl/pkg/util/configdump/bootstrap.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configdump - -import ( - adminapi "github.com/envoyproxy/go-control-plane/envoy/admin/v3" -) - -// GetBootstrapConfigDump retrieves the bootstrap config dump from the ConfigDump -func (w *Wrapper) GetBootstrapConfigDump() (*adminapi.BootstrapConfigDump, error) { - bootstrapDumpAny, err := w.getSection(bootstrap) - if err != nil { - return nil, err - } - bootstrapDump := &adminapi.BootstrapConfigDump{} - err = bootstrapDumpAny.UnmarshalTo(bootstrapDump) - if err != nil { - return nil, err - } - return bootstrapDump, nil -} diff --git a/istioctl/pkg/util/configdump/cluster.go b/istioctl/pkg/util/configdump/cluster.go deleted file mode 100644 index 6ad10a8ca..000000000 --- a/istioctl/pkg/util/configdump/cluster.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configdump - -import ( - "sort" -) - -import ( - adminapi "github.com/envoyproxy/go-control-plane/envoy/admin/v3" - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" -) - -import ( - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" -) - -// GetDynamicClusterDump retrieves a cluster dump with just dynamic active clusters in it -func (w *Wrapper) GetDynamicClusterDump(stripVersions bool) (*adminapi.ClustersConfigDump, error) { - clusterDump, err := w.GetClusterConfigDump() - if err != nil { - return nil, err - } - dac := clusterDump.GetDynamicActiveClusters() - // Allow sorting to work even if we don't have the exact same type - for i := range dac { - dac[i].Cluster.TypeUrl = v3.ClusterType - } - sort.Slice(dac, func(i, j int) bool { - cluster := &cluster.Cluster{} - err = dac[i].Cluster.UnmarshalTo(cluster) - if err != nil { - return false - } - name := cluster.Name - err = dac[j].Cluster.UnmarshalTo(cluster) - if err != nil { - return false - } - return name < cluster.Name - }) - if stripVersions { - for i := range dac { - dac[i].VersionInfo = "" - dac[i].LastUpdated = nil - } - } - return &adminapi.ClustersConfigDump{DynamicActiveClusters: dac}, nil -} - -// GetClusterConfigDump retrieves the cluster config dump from the ConfigDump -func (w *Wrapper) GetClusterConfigDump() (*adminapi.ClustersConfigDump, error) { - clusterDumpAny, err := w.getSection(clusters) - if err != nil { - return nil, err - } - clusterDump := &adminapi.ClustersConfigDump{} - err = clusterDumpAny.UnmarshalTo(clusterDump) - if err != nil { - return nil, err - } - return clusterDump, nil -} diff --git a/istioctl/pkg/util/configdump/listener.go b/istioctl/pkg/util/configdump/listener.go deleted file mode 100644 index b53699ae6..000000000 --- a/istioctl/pkg/util/configdump/listener.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configdump - -import ( - "sort" -) - -import ( - adminapi "github.com/envoyproxy/go-control-plane/envoy/admin/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" -) - -import ( - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" -) - -// GetDynamicListenerDump retrieves a listener dump with just dynamic active listeners in it -func (w *Wrapper) GetDynamicListenerDump(stripVersions bool) (*adminapi.ListenersConfigDump, error) { - listenerDump, err := w.GetListenerConfigDump() - if err != nil { - return nil, err - } - - dal := make([]*adminapi.ListenersConfigDump_DynamicListener, 0) - for _, l := range listenerDump.DynamicListeners { - // If a listener was reloaded, it would contain both the active and draining state - // delete the draining state for proper comparison - l.DrainingState = nil - if l.ActiveState != nil { - dal = append(dal, l) - } - } - - // Support v2 or v3 in config dump. See ads.go:RequestedTypes for more info. - for i := range dal { - dal[i].ActiveState.Listener.TypeUrl = v3.ListenerType - } - sort.Slice(dal, func(i, j int) bool { - l := &listener.Listener{} - err = dal[i].ActiveState.Listener.UnmarshalTo(l) - if err != nil { - return false - } - name := l.Name - err = dal[j].ActiveState.Listener.UnmarshalTo(l) - if err != nil { - return false - } - return name < l.Name - }) - if stripVersions { - for i := range dal { - dal[i].ActiveState.VersionInfo = "" - dal[i].ActiveState.LastUpdated = nil - dal[i].Name = "" // In Istio 1.5, Envoy creates this; suppress it - } - } - return &adminapi.ListenersConfigDump{DynamicListeners: dal}, nil -} - -// GetListenerConfigDump retrieves the listener config dump from the ConfigDump -func (w *Wrapper) GetListenerConfigDump() (*adminapi.ListenersConfigDump, error) { - listenerDumpAny, err := w.getSection(listeners) - if err != nil { - return nil, err - } - listenerDump := &adminapi.ListenersConfigDump{} - err = listenerDumpAny.UnmarshalTo(listenerDump) - if err != nil { - return nil, err - } - return listenerDump, nil -} diff --git a/istioctl/pkg/util/configdump/route.go b/istioctl/pkg/util/configdump/route.go deleted file mode 100644 index 20e78cdf9..000000000 --- a/istioctl/pkg/util/configdump/route.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configdump - -import ( - "sort" - "time" -) - -import ( - adminapi "github.com/envoyproxy/go-control-plane/envoy/admin/v3" - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - "google.golang.org/protobuf/types/known/anypb" -) - -import ( - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" -) - -// GetLastUpdatedDynamicRouteTime retrieves the LastUpdated timestamp of the -// most recently updated DynamicRouteConfig -func (w *Wrapper) GetLastUpdatedDynamicRouteTime() (*time.Time, error) { - routeDump, err := w.GetRouteConfigDump() - if err != nil { - return nil, err - } - drc := routeDump.GetDynamicRouteConfigs() - - lastUpdated := time.Unix(0, 0) // get the oldest possible timestamp - for i := range drc { - if drc[i].LastUpdated != nil { - drLastUpdated := drc[i].LastUpdated.AsTime() - if drLastUpdated.After(lastUpdated) { - lastUpdated = drLastUpdated - } - } - } - if lastUpdated.After(time.Unix(0, 0)) { // if a timestamp was obtained from a drc - return &lastUpdated, nil - } - return nil, nil -} - -// GetDynamicRouteDump retrieves a route dump with just dynamic active routes in it -func (w *Wrapper) GetDynamicRouteDump(stripVersions bool) (*adminapi.RoutesConfigDump, error) { - routeDump, err := w.GetRouteConfigDump() - if err != nil { - return nil, err - } - drc := routeDump.GetDynamicRouteConfigs() - // Support v2 or v3 in config dump. See ads.go:RequestedTypes for more info. - for i := range drc { - drc[i].RouteConfig.TypeUrl = v3.RouteType - } - sort.Slice(drc, func(i, j int) bool { - r := &route.RouteConfiguration{} - err = drc[i].RouteConfig.UnmarshalTo(r) - if err != nil { - return false - } - name := r.Name - err = drc[j].RouteConfig.UnmarshalTo(r) - if err != nil { - return false - } - return name < r.Name - }) - - // In Istio 1.5, it is not enough just to sort the routes. The virtual hosts - // within a route might have a different order. Sort those too. - for i := range drc { - route := &route.RouteConfiguration{} - err = drc[i].RouteConfig.UnmarshalTo(route) - if err != nil { - return nil, err - } - sort.Slice(route.VirtualHosts, func(i, j int) bool { - return route.VirtualHosts[i].Name < route.VirtualHosts[j].Name - }) - drc[i].RouteConfig, err = anypb.New(route) - if err != nil { - return nil, err - } - } - - if stripVersions { - for i := range drc { - drc[i].VersionInfo = "" - drc[i].LastUpdated = nil - } - } - return &adminapi.RoutesConfigDump{DynamicRouteConfigs: drc}, nil -} - -// GetRouteConfigDump retrieves the route config dump from the ConfigDump -func (w *Wrapper) GetRouteConfigDump() (*adminapi.RoutesConfigDump, error) { - routeDumpAny, err := w.getSection(routes) - if err != nil { - return nil, err - } - routeDump := &adminapi.RoutesConfigDump{} - err = routeDumpAny.UnmarshalTo(routeDump) - if err != nil { - return nil, err - } - return routeDump, nil -} diff --git a/istioctl/pkg/util/configdump/secret.go b/istioctl/pkg/util/configdump/secret.go deleted file mode 100644 index 325613a96..000000000 --- a/istioctl/pkg/util/configdump/secret.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configdump - -import ( - "encoding/base64" - "fmt" -) - -import ( - adminapi "github.com/envoyproxy/go-control-plane/envoy/admin/v3" - extapi "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - any "google.golang.org/protobuf/types/known/anypb" -) - -// GetSecretsConfigDump retrieves a secret dump from a config dump wrapper -func (w *Wrapper) GetSecretConfigDump() (*adminapi.SecretsConfigDump, error) { - secretDumpAny, err := w.getSection(secrets) - if err != nil { - return nil, err - } - secretDump := &adminapi.SecretsConfigDump{} - err = secretDumpAny.UnmarshalTo(secretDump) - if err != nil { - return nil, err - } - return secretDump, nil -} - -// GetRootCAFromSecretConfigDump retrieves root CA from a secret config dump wrapper -func (w *Wrapper) GetRootCAFromSecretConfigDump(anySec *any.Any) (string, error) { - var secret extapi.Secret - if err := anySec.UnmarshalTo(&secret); err != nil { - return "", fmt.Errorf("failed to unmarshall ROOTCA secret: %v", err) - } - var returnStr string - var returnErr error - rCASecret := secret.GetValidationContext() - if rCASecret != nil { - trustCA := rCASecret.GetTrustedCa() - if trustCA != nil { - inlineBytes := trustCA.GetInlineBytes() - if inlineBytes != nil { - returnStr = base64.StdEncoding.EncodeToString(inlineBytes) - returnErr = nil - } else { - returnStr = "" - returnErr = fmt.Errorf("can not retrieve inlineBytes from trustCA section") - } - } else { - returnStr = "" - returnErr = fmt.Errorf("can not retrieve trustedCa from secret ROOTCA") - } - } else { - returnStr = "" - returnErr = fmt.Errorf("can not find ROOTCA from secret config dump") - } - return returnStr, returnErr -} diff --git a/istioctl/pkg/util/configdump/util.go b/istioctl/pkg/util/configdump/util.go deleted file mode 100644 index b41edbe3e..000000000 --- a/istioctl/pkg/util/configdump/util.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configdump - -import ( - "fmt" -) - -import ( - any "google.golang.org/protobuf/types/known/anypb" -) - -type configTypeURL string - -// See https://www.envoyproxy.io/docs/envoy/latest/api-v3/admin/v3/config_dump.proto -const ( - bootstrap configTypeURL = "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump" - listeners configTypeURL = "type.googleapis.com/envoy.admin.v3.ListenersConfigDump" - clusters configTypeURL = "type.googleapis.com/envoy.admin.v3.ClustersConfigDump" - routes configTypeURL = "type.googleapis.com/envoy.admin.v3.RoutesConfigDump" - secrets configTypeURL = "type.googleapis.com/envoy.admin.v3.SecretsConfigDump" -) - -// getSection takes a TypeURL and returns the types.Any from the config dump corresponding to that URL -func (w *Wrapper) getSection(sectionTypeURL configTypeURL) (*any.Any, error) { - var dumpAny *any.Any - for _, conf := range w.Configs { - if conf.TypeUrl == string(sectionTypeURL) { - dumpAny = conf - } - } - if dumpAny == nil { - return nil, fmt.Errorf("config dump has no configuration type %s", sectionTypeURL) - } - - return dumpAny, nil -} diff --git a/istioctl/pkg/util/configdump/wrapper.go b/istioctl/pkg/util/configdump/wrapper.go deleted file mode 100644 index 6f50fa813..000000000 --- a/istioctl/pkg/util/configdump/wrapper.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configdump - -import ( - "bytes" - "reflect" - "strings" -) - -import ( - adminapi "github.com/envoyproxy/go-control-plane/envoy/admin/v3" - "github.com/golang/protobuf/jsonpb" // nolint: staticcheck - "github.com/golang/protobuf/proto" // nolint: staticcheck - emptypb "github.com/golang/protobuf/ptypes/empty" - exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" -) - -// nonstrictResolver is an AnyResolver that ignores unknown proto messages -type nonstrictResolver struct{} - -var envoyResolver nonstrictResolver - -func (m *nonstrictResolver) Resolve(typeURL string) (proto.Message, error) { - // See https://github.com/golang/protobuf/issues/747#issuecomment-437463120 - mname := typeURL - if slash := strings.LastIndex(typeURL, "/"); slash >= 0 { - mname = mname[slash+1:] - } - // nolint: staticcheck - mt := proto.MessageType(mname) - if mt == nil { - // istioctl should keep going if it encounters new Envoy versions; ignore unknown types - return &exprpb.Type{TypeKind: &exprpb.Type_Dyn{Dyn: &emptypb.Empty{}}}, nil - } - return reflect.New(mt.Elem()).Interface().(proto.Message), nil -} - -// Wrapper is a wrapper around the Envoy ConfigDump -// It has extra helper functions for handling any/struct/marshal protobuf pain -type Wrapper struct { - *adminapi.ConfigDump -} - -// MarshalJSON is a custom marshaller to handle protobuf pain -func (w *Wrapper) MarshalJSON() ([]byte, error) { - buffer := &bytes.Buffer{} - err := (&jsonpb.Marshaler{}).Marshal(buffer, w) - if err != nil { - return nil, err - } - return buffer.Bytes(), nil -} - -// UnmarshalJSON is a custom unmarshaller to handle protobuf pain -func (w *Wrapper) UnmarshalJSON(b []byte) error { - cd := &adminapi.ConfigDump{} - err := (&jsonpb.Unmarshaler{ - AllowUnknownFields: true, - AnyResolver: &envoyResolver, - }).Unmarshal(bytes.NewReader(b), cd) - *w = Wrapper{cd} - return err -} diff --git a/istioctl/pkg/util/formatting/formatter.go b/istioctl/pkg/util/formatting/formatter.go deleted file mode 100644 index 7cb1a8685..000000000 --- a/istioctl/pkg/util/formatting/formatter.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package formatting - -import ( - "encoding/json" - "fmt" - "io" - "os" - "strings" -) - -import ( - "github.com/mattn/go-isatty" - "istio.io/pkg/env" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/diag" -) - -// Formatting options for Messages -const ( - LogFormat = "log" - JSONFormat = "json" - YAMLFormat = "yaml" -) - -var ( - MsgOutputFormatKeys = []string{LogFormat, JSONFormat, YAMLFormat} - MsgOutputFormats = make(map[string]bool) - termEnvVar = env.RegisterStringVar("TERM", "", "Specifies terminal type. Use 'dumb' to suppress color output") -) - -func init() { - for _, key := range MsgOutputFormatKeys { - MsgOutputFormats[key] = true - } -} - -// Print output messages in the specified format with color options -func Print(ms diag.Messages, format string, colorize bool) (string, error) { - switch format { - case LogFormat: - return printLog(ms, colorize), nil - case JSONFormat: - return printJSON(ms) - case YAMLFormat: - return printYAML(ms) - default: - return "", fmt.Errorf("invalid format, expected one of %v but got %q", MsgOutputFormatKeys, format) - } -} - -func printLog(ms diag.Messages, colorize bool) string { - var logOutput []string - for _, m := range ms { - logOutput = append(logOutput, render(m, colorize)) - } - return strings.Join(logOutput, "\n") -} - -func printJSON(ms diag.Messages) (string, error) { - jsonOutput, err := json.MarshalIndent(ms, "", "\t") - return string(jsonOutput), err -} - -func printYAML(ms diag.Messages) (string, error) { - yamlOutput, err := yaml.Marshal(ms) - return string(yamlOutput), err -} - -// Formatting options for Message -var ( - colorPrefixes = map[diag.Level]string{ - diag.Info: "", // no special color for info messages - diag.Warning: "\033[33m", // yellow - diag.Error: "\033[1;31m", // bold red - } -) - -// render turns a Message instance into a string with an option of colored bash output -func render(m diag.Message, colorize bool) string { - return fmt.Sprintf("%s%v%s [%v]%s %s", - colorPrefix(m, colorize), m.Type.Level(), colorSuffix(colorize), - m.Type.Code(), m.Origin(), fmt.Sprintf(m.Type.Template(), m.Parameters...), - ) -} - -func colorPrefix(m diag.Message, colorize bool) string { - if !colorize { - return "" - } - - prefix, ok := colorPrefixes[m.Type.Level()] - if !ok { - return "" - } - return prefix -} - -func colorSuffix(colorize bool) string { - if !colorize { - return "" - } - return "\033[0m" -} - -func IstioctlColorDefault(writer io.Writer) bool { - if strings.EqualFold(termEnvVar.Get(), "dumb") { - return false - } - - file, ok := writer.(*os.File) - if ok { - if !isatty.IsTerminal(file.Fd()) { - return false - } - } - - return true -} diff --git a/istioctl/pkg/util/formatting/formatter_test.go b/istioctl/pkg/util/formatting/formatter_test.go deleted file mode 100644 index a86d040bd..000000000 --- a/istioctl/pkg/util/formatting/formatter_test.go +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package formatting - -import ( - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/diag" - "github.com/apache/dubbo-go-pixiu/pkg/url" -) - -func TestFormatter_PrintLog(t *testing.T) { - g := NewWithT(t) - - firstMsg := diag.NewMessage( - diag.NewMessageType(diag.Error, "B1", "Explosion accident: %v"), - diag.MockResource("SoapBubble"), - "the bubble is too big", - ) - secondMsg := diag.NewMessage( - diag.NewMessageType(diag.Warning, "C1", "Collapse danger: %v"), - diag.MockResource("GrandCastle"), - "the castle is too old", - ) - - msgs := diag.Messages{firstMsg, secondMsg} - output, _ := Print(msgs, LogFormat, false) - - g.Expect(output).To(Equal( - "Error [B1] (SoapBubble) Explosion accident: the bubble is too big\n" + - "Warning [C1] (GrandCastle) Collapse danger: the castle is too old", - )) -} - -func TestFormatter_PrintLogWithColor(t *testing.T) { - g := NewWithT(t) - - firstMsg := diag.NewMessage( - diag.NewMessageType(diag.Error, "B1", "Explosion accident: %v"), - diag.MockResource("SoapBubble"), - "the bubble is too big", - ) - secondMsg := diag.NewMessage( - diag.NewMessageType(diag.Warning, "C1", "Collapse danger: %v"), - diag.MockResource("GrandCastle"), - "the castle is too old", - ) - - msgs := diag.Messages{firstMsg, secondMsg} - output, _ := Print(msgs, LogFormat, true) - - g.Expect(output).To(Equal( - "\033[1;31mError\033[0m [B1] (SoapBubble) Explosion accident: the bubble is too big\n" + - "\033[33mWarning\033[0m [C1] (GrandCastle) Collapse danger: the castle is too old", - )) -} - -func TestFormatter_PrintJSON(t *testing.T) { - g := NewWithT(t) - - firstMsg := diag.NewMessage( - diag.NewMessageType(diag.Error, "B1", "Explosion accident: %v"), - diag.MockResource("SoapBubble"), - "the bubble is too big", - ) - secondMsg := diag.NewMessage( - diag.NewMessageType(diag.Warning, "C1", "Collapse danger: %v"), - diag.MockResource("GrandCastle"), - "the castle is too old", - ) - - msgs := diag.Messages{firstMsg, secondMsg} - output, _ := Print(msgs, JSONFormat, false) - - expectedOutput := `[ - { - "code": "B1", - "documentationUrl": "` + url.ConfigAnalysis + `/b1/", - "level": "Error", - "message": "Explosion accident: the bubble is too big", - "origin": "SoapBubble" - }, - { - "code": "C1", - "documentationUrl": "` + url.ConfigAnalysis + `/c1/", - "level": "Warning", - "message": "Collapse danger: the castle is too old", - "origin": "GrandCastle" - } -]` - - g.Expect(output).To(Equal(expectedOutput)) -} - -func TestFormatter_PrintYAML(t *testing.T) { - g := NewWithT(t) - - firstMsg := diag.NewMessage( - diag.NewMessageType(diag.Error, "B1", "Explosion accident: %v"), - diag.MockResource("SoapBubble"), - "the bubble is too big", - ) - secondMsg := diag.NewMessage( - diag.NewMessageType(diag.Warning, "C1", "Collapse danger: %v"), - diag.MockResource("GrandCastle"), - "the castle is too old", - ) - - msgs := diag.Messages{firstMsg, secondMsg} - output, _ := Print(msgs, YAMLFormat, false) - - expectedOutput := `- code: B1 - documentationUrl: ` + url.ConfigAnalysis + `/b1/ - level: Error - message: 'Explosion accident: the bubble is too big' - origin: SoapBubble -- code: C1 - documentationUrl: ` + url.ConfigAnalysis + `/c1/ - level: Warning - message: 'Collapse danger: the castle is too old' - origin: GrandCastle -` - - g.Expect(output).To(Equal(expectedOutput)) -} - -func TestFormatter_PrintEmpty(t *testing.T) { - g := NewWithT(t) - - msgs := diag.Messages{} - - logOutput, _ := Print(msgs, LogFormat, false) - g.Expect(logOutput).To(Equal("")) - - jsonOutput, _ := Print(msgs, JSONFormat, false) - g.Expect(jsonOutput).To(Equal("[]")) - - yamlOutput, _ := Print(msgs, YAMLFormat, false) - g.Expect(yamlOutput).To(Equal("[]\n")) -} diff --git a/istioctl/pkg/util/formatting/msg_threshold.go b/istioctl/pkg/util/formatting/msg_threshold.go deleted file mode 100644 index 95a9f54f5..000000000 --- a/istioctl/pkg/util/formatting/msg_threshold.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package formatting - -import ( - "errors" - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/diag" -) - -// MessageThreshold is a wrapper around Level to be used as a cobra command line argument. -// It should satisfy the pflag.Value interface. -type MessageThreshold struct { - diag.Level -} - -// String is a function declared in the pflag.Value interface -func (m *MessageThreshold) String() string { - return m.Level.String() -} - -// Type is a function declared in the pflag.Value interface -func (m *MessageThreshold) Type() string { - return "Level" -} - -// Set is a function declared in the pflag.Value interface -func (m *MessageThreshold) Set(s string) error { - levelMap := diag.GetUppercaseStringToLevelMap() - level, ok := levelMap[strings.ToUpper(s)] - if !ok { - return errors.New("invalid level option") - } - m.Level = level - return nil -} diff --git a/istioctl/pkg/util/handlers/handlers.go b/istioctl/pkg/util/handlers/handlers.go deleted file mode 100644 index 457d053d3..000000000 --- a/istioctl/pkg/util/handlers/handlers.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package handlers - -import ( - "errors" - "fmt" - "sort" - "strings" - "time" -) - -import ( - v1 "k8s.io/api/core/v1" - corev1client "k8s.io/client-go/kubernetes/typed/core/v1" - cmdutil "k8s.io/kubectl/pkg/cmd/util" - "k8s.io/kubectl/pkg/polymorphichelpers" - "k8s.io/kubectl/pkg/scheme" - "k8s.io/kubectl/pkg/util/podutils" -) - -// InferPodInfo Uses name to infer namespace if the passed name contains namespace information. -// Otherwise uses the namespace value passed into the function -func InferPodInfo(name, defaultNS string) (string, string) { - return inferNsInfo(name, defaultNS) -} - -// inferNsInfo Uses name to infer namespace if the passed name contains namespace information. -// Otherwise uses the namespace value passed into the function -func inferNsInfo(name, namespace string) (string, string) { - separator := strings.LastIndex(name, ".") - if separator < 0 { - return name, namespace - } - - return name[0:separator], name[separator+1:] -} - -// HandleNamespace returns the defaultNamespace if the namespace is empty -func HandleNamespace(ns, defaultNamespace string) string { - if ns == v1.NamespaceAll { - ns = defaultNamespace - } - return ns -} - -// InferPodInfoFromTypedResource gets a pod name, from an expression like Deployment/httpbin, or Deployment/productpage-v1.bookinfo -func InferPodInfoFromTypedResource(name, defaultNS string, factory cmdutil.Factory) (string, string, error) { - resname, ns := inferNsInfo(name, defaultNS) - if !strings.Contains(resname, "/") { - return resname, ns, nil - } - - // Pod is referred to using something like "deployment/httpbin". Use the kubectl - // libraries to look up the the resource name, find the pods it selects, and return - // one of those pods. - builder := factory.NewBuilder(). - WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...). - NamespaceParam(ns).DefaultNamespace(). - SingleResourceType() - builder.ResourceNames("pods", resname) - infos, err := builder.Do().Infos() - if err != nil { - return "", "", fmt.Errorf("failed retrieving: %v in the %q namespace", err, ns) - } - if len(infos) != 1 { - return "", "", errors.New("expected a resource") - } - _, ok := infos[0].Object.(*v1.Pod) - if ok { - // If we got a pod, just use its name - return infos[0].Name, infos[0].Namespace, nil - } - namespace, selector, err := polymorphichelpers.SelectorsForObject(infos[0].Object) - if err != nil { - return "", "", fmt.Errorf("%q does not refer to a pod", resname) - } - clientConfig, err := factory.ToRESTConfig() - if err != nil { - return "", "", err - } - clientset, err := corev1client.NewForConfig(clientConfig) - if err != nil { - return "", "", err - } - // We need to pass in a sorter, and the one used by `kubectl logs` is good enough. - sortBy := func(pods []*v1.Pod) sort.Interface { return podutils.ByLogging(pods) } - timeout := 2 * time.Second - pod, _, err := polymorphichelpers.GetFirstPod(clientset, namespace, selector.String(), timeout, sortBy) - if err != nil { - return "", "", fmt.Errorf("no pods match %q", resname) - } - return pod.Name, ns, nil -} diff --git a/istioctl/pkg/util/handlers/handlers_test.go b/istioctl/pkg/util/handlers/handlers_test.go deleted file mode 100644 index d8381a0e3..000000000 --- a/istioctl/pkg/util/handlers/handlers_test.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package handlers - -import ( - "strings" - "testing" -) - -func TestInferPodInfo(t *testing.T) { - tests := []struct { - proxyName string - namespace string - wantPodName string - wantNamespace string - }{ - { - proxyName: "istio-ingressgateway-8d9697654-qdzgh.dubbo-system", - namespace: "kube-system", - wantPodName: "istio-ingressgateway-8d9697654-qdzgh", - wantNamespace: "dubbo-system", - }, - { - proxyName: "istio-ingressgateway-8d9697654-qdzgh.dubbo-system", - namespace: "", - wantPodName: "istio-ingressgateway-8d9697654-qdzgh", - wantNamespace: "dubbo-system", - }, - { - proxyName: "istio-ingressgateway-8d9697654-qdzgh", - namespace: "kube-system", - wantPodName: "istio-ingressgateway-8d9697654-qdzgh", - wantNamespace: "kube-system", - }, - { - proxyName: "istio-security-post-install-1.2.2-bm9w2.dubbo-system", - namespace: "dubbo-system", - wantPodName: "istio-security-post-install-1.2.2-bm9w2", - wantNamespace: "dubbo-system", - }, - { - proxyName: "istio-security-post-install-1.2.2-bm9w2.dubbo-system", - namespace: "", - wantPodName: "istio-security-post-install-1.2.2-bm9w2", - wantNamespace: "dubbo-system", - }, - } - for _, tt := range tests { - t.Run(strings.Split(tt.proxyName, ".")[0], func(t *testing.T) { - gotPodName, gotNamespace := InferPodInfo(tt.proxyName, tt.namespace) - if gotPodName != tt.wantPodName || gotNamespace != tt.wantNamespace { - t.Errorf("unexpected podName and namespace: wanted %v %v got %v %v", tt.wantPodName, tt.wantNamespace, gotPodName, gotNamespace) - } - }) - } -} - -func TestHandleNamespace(t *testing.T) { - ns := HandleNamespace("test", "default") - if ns != "test" { - t.Fatalf("Get the incorrect namespace: %q back", ns) - } - - tests := []struct { - description string - namespace string - defaultNamespace string - wantNamespace string - }{ - { - description: "return test namespace", - namespace: "test", - defaultNamespace: "default", - wantNamespace: "test", - }, - { - description: "return default namespace", - namespace: "", - defaultNamespace: "default", - wantNamespace: "default", - }, - } - for _, tt := range tests { - t.Run(tt.description, func(t *testing.T) { - gotNs := HandleNamespace(tt.namespace, tt.defaultNamespace) - if gotNs != tt.wantNamespace { - t.Fatalf("unexpected namespace: wanted %v got %v", tt.wantNamespace, gotNs) - } - }) - } -} diff --git a/istioctl/pkg/util/proto/messageslice.go b/istioctl/pkg/util/proto/messageslice.go deleted file mode 100644 index 93f75a8ea..000000000 --- a/istioctl/pkg/util/proto/messageslice.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package proto - -import ( - "bytes" -) - -import ( - "google.golang.org/protobuf/proto" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -// MessageSlice allows us to marshal slices of protobuf messages like clusters/listeners/routes/endpoints correctly -type MessageSlice []proto.Message - -// MarshalJSON handles marshaling of slices of proto messages -func (pSlice MessageSlice) MarshalJSON() ([]byte, error) { - buffer := bytes.NewBufferString("[") - sliceLength := len(pSlice) - for index, msg := range pSlice { - b, err := protomarshal.Marshal(msg) - if err != nil { - return nil, err - } - buffer.Write(b) - if index < sliceLength-1 { - buffer.WriteString(",") - } - } - buffer.WriteString("]") - return buffer.Bytes(), nil -} diff --git a/istioctl/pkg/validate/validate.go b/istioctl/pkg/validate/validate.go deleted file mode 100644 index 4b6f4c2c2..000000000 --- a/istioctl/pkg/validate/validate.go +++ /dev/null @@ -1,465 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validate - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "os" - "strings" -) - -import ( - "github.com/hashicorp/go-multierror" - "github.com/spf13/cobra" - "gopkg.in/yaml.v2" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" -) - -import ( - operator_istio "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - operator_validate "github.com/apache/dubbo-go-pixiu/operator/pkg/validate" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/validation" - "github.com/apache/dubbo-go-pixiu/pkg/url" -) - -var ( - errMissingFilename = errors.New(`error: you must specify resources by --filename. -Example resource specifications include: - '-f rsrc.yaml' - '--filename=rsrc.json'`) - - validFields = map[string]struct{}{ - "apiVersion": {}, - "kind": {}, - "metadata": {}, - "spec": {}, - "status": {}, - } - - istioDeploymentLabel = []string{ - "app", - "version", - } - serviceProtocolUDP = "UDP" -) - -type validator struct{} - -func checkFields(un *unstructured.Unstructured) error { - var errs error - for key := range un.Object { - if _, ok := validFields[key]; !ok { - errs = multierror.Append(errs, fmt.Errorf("unknown field %q", key)) - } - } - return errs -} - -func (v *validator) validateResource(istioNamespace, defaultNamespace string, un *unstructured.Unstructured, writer io.Writer) (validation.Warning, error) { - gvk := config.GroupVersionKind{ - Group: un.GroupVersionKind().Group, - Version: un.GroupVersionKind().Version, - Kind: un.GroupVersionKind().Kind, - } - // TODO(jasonwzm) remove this when multi-version is supported. v1beta1 shares the same - // schema as v1lalpha3. Fake conversion and validate against v1alpha3. - if gvk.Group == name.NetworkingAPIGroupName && gvk.Version == "v1beta1" { - gvk.Version = "v1alpha3" - } - schema, exists := collections.Pilot.FindByGroupVersionKind(gvk) - if exists { - obj, err := convertObjectFromUnstructured(schema, un, "") - if err != nil { - return nil, fmt.Errorf("cannot parse proto message: %v", err) - } - if err = checkFields(un); err != nil { - return nil, err - } - - // If object to validate has no namespace, set it (the validity of a CR - // may depend on its namespace; for example a VirtualService with exportTo=".") - if obj.Namespace == "" { - // If the user didn't specify --namespace, and is validating a CR with no namespace, use "default" - if defaultNamespace == "" { - defaultNamespace = metav1.NamespaceDefault - } - obj.Namespace = defaultNamespace - } - - warnings, err := schema.Resource().ValidateConfig(*obj) - return warnings, err - } - - var errs error - if un.IsList() { - _ = un.EachListItem(func(item runtime.Object) error { - castItem := item.(*unstructured.Unstructured) - if castItem.GetKind() == name.ServiceStr { - err := v.validateServicePortPrefix(istioNamespace, castItem) - if err != nil { - errs = multierror.Append(errs, err) - } - } - if castItem.GetKind() == name.DeploymentStr { - err := v.validateDeploymentLabel(istioNamespace, castItem, writer) - if err != nil { - errs = multierror.Append(errs, err) - } - } - return nil - }) - } - - if errs != nil { - return nil, errs - } - if un.GetKind() == name.ServiceStr { - return nil, v.validateServicePortPrefix(istioNamespace, un) - } - - if un.GetKind() == name.DeploymentStr { - if err := v.validateDeploymentLabel(istioNamespace, un, writer); err != nil { - return nil, err - } - return nil, nil - } - - if un.GetAPIVersion() == "install.istio.io/v1alpha1" { - if un.GetKind() == "IstioOperator" { - if err := checkFields(un); err != nil { - return nil, err - } - // IstioOperator isn't part of pkg/config/schema/collections, - // usual conversion not available. Convert unstructured to string - // and ask operator code to check. - un.SetCreationTimestamp(metav1.Time{}) // UnmarshalIstioOperator chokes on these - by := util.ToYAML(un) - iop, err := operator_istio.UnmarshalIstioOperator(by, false) - if err != nil { - return nil, err - } - return nil, operator_validate.CheckIstioOperator(iop, true) - } - } - - // Didn't really validate. This is OK, as we often get non-Istio Kubernetes YAML - // we can't complain about. - - return nil, nil -} - -func (v *validator) validateServicePortPrefix(istioNamespace string, un *unstructured.Unstructured) error { - var errs error - if un.GetNamespace() == handleNamespace(istioNamespace) { - return nil - } - spec := un.Object["spec"].(map[string]interface{}) - if _, ok := spec["ports"]; ok { - ports := spec["ports"].([]interface{}) - for _, port := range ports { - p := port.(map[string]interface{}) - if p["protocol"] != nil && strings.EqualFold(p["protocol"].(string), serviceProtocolUDP) { - continue - } - if p["name"] == nil { - errs = multierror.Append(errs, fmt.Errorf("service %q has an unnamed port. This is not recommended,"+ - " See "+url.DeploymentRequirements, fmt.Sprintf("%s/%s/:", un.GetName(), un.GetNamespace()))) - continue - } - if servicePortPrefixed(p["name"].(string)) { - errs = multierror.Append(errs, fmt.Errorf("service %q port %q does not follow the Istio naming convention."+ - " See "+url.DeploymentRequirements, fmt.Sprintf("%s/%s/:", un.GetName(), un.GetNamespace()), p["name"].(string))) - } - } - } - if errs != nil { - return errs - } - return nil -} - -func (v *validator) validateDeploymentLabel(istioNamespace string, un *unstructured.Unstructured, writer io.Writer) error { - if un.GetNamespace() == handleNamespace(istioNamespace) { - return nil - } - labels, err := GetTemplateLabels(un) - if err != nil { - return err - } - url := fmt.Sprintf("See %s\n", url.DeploymentRequirements) - for _, l := range istioDeploymentLabel { - if _, ok := labels[l]; !ok { - fmt.Fprintf(writer, "deployment %q may not provide Istio metrics and telemetry without label %q. "+url, - fmt.Sprintf("%s/%s:", un.GetName(), un.GetNamespace()), l) - } - } - return nil -} - -// GetTemplateLabels returns spec.template.metadata.labels from Deployment -func GetTemplateLabels(u *unstructured.Unstructured) (map[string]string, error) { - if spec, ok := u.Object["spec"].(map[string]interface{}); ok { - if template, ok := spec["template"].(map[string]interface{}); ok { - m, _, err := unstructured.NestedStringMap(template, "metadata", "labels") - if err != nil { - return nil, err - } - return m, nil - } - } - return nil, nil -} - -func (v *validator) validateFile(istioNamespace *string, defaultNamespace string, reader io.Reader, writer io.Writer) (validation.Warning, error) { - decoder := yaml.NewDecoder(reader) - decoder.SetStrict(true) - var errs error - var warnings validation.Warning - for { - // YAML allows non-string keys and the produces generic keys for nested fields - raw := make(map[interface{}]interface{}) - err := decoder.Decode(&raw) - if err == io.EOF { - return warnings, errs - } - if err != nil { - errs = multierror.Append(errs, err) - return warnings, errs - } - if len(raw) == 0 { - continue - } - out := transformInterfaceMap(raw) - un := unstructured.Unstructured{Object: out} - warning, err := v.validateResource(*istioNamespace, defaultNamespace, &un, writer) - if err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, fmt.Sprintf("%s/%s/%s:", - un.GetKind(), un.GetNamespace(), un.GetName()))) - } - if warning != nil { - warnings = multierror.Append(warnings, multierror.Prefix(warning, fmt.Sprintf("%s/%s/%s:", - un.GetKind(), un.GetNamespace(), un.GetName()))) - } - } -} - -func validateFiles(istioNamespace *string, defaultNamespace string, filenames []string, writer io.Writer) error { - if len(filenames) == 0 { - return errMissingFilename - } - - v := &validator{} - - var errs, err error - var reader io.ReadCloser - warningsByFilename := map[string]validation.Warning{} - for _, filename := range filenames { - if filename == "-" { - reader = io.NopCloser(os.Stdin) - } else { - reader, err = os.Open(filename) - } - if err != nil { - errs = multierror.Append(errs, fmt.Errorf("cannot read file %q: %v", filename, err)) - continue - } - warning, err := v.validateFile(istioNamespace, defaultNamespace, reader, writer) - if err != nil { - errs = multierror.Append(errs, err) - } - reader.Close() - warningsByFilename[filename] = warning - } - - if errs != nil { - // Display warnings we encountered as well - for _, fname := range filenames { - if w := warningsByFilename[fname]; w != nil { - if fname == "-" { - _, _ = fmt.Fprint(writer, warningToString(w)) - break - } - _, _ = fmt.Fprintf(writer, "%q has warnings: %v\n", fname, warningToString(w)) - } - } - return errs - } - for _, fname := range filenames { - if fname == "-" { - if w := warningsByFilename[fname]; w != nil { - _, _ = fmt.Fprint(writer, warningToString(w)) - } else { - _, _ = fmt.Fprintf(writer, "validation succeed\n") - } - break - } - - if w := warningsByFilename[fname]; w != nil { - _, _ = fmt.Fprintf(writer, "%q has warnings: %v\n", fname, warningToString(w)) - } else { - _, _ = fmt.Fprintf(writer, "%q is valid\n", fname) - } - } - - return nil -} - -// NewValidateCommand creates a new command for validating Istio k8s resources. -func NewValidateCommand(istioNamespace *string, defaultNamespace *string) *cobra.Command { - var filenames []string - var referential bool - - c := &cobra.Command{ - Use: "validate -f FILENAME [options]", - Aliases: []string{"v"}, - Short: "Validate Istio policy and rules files", - Example: ` # Validate bookinfo-gateway.yaml - istioctl validate -f samples/bookinfo/networking/bookinfo-gateway.yaml - - # Validate bookinfo-gateway.yaml with shorthand syntax - istioctl v -f samples/bookinfo/networking/bookinfo-gateway.yaml - - # Validate current deployments under 'default' namespace within the cluster - kubectl get deployments -o yaml | istioctl validate -f - - - # Validate current services under 'default' namespace within the cluster - kubectl get services -o yaml | istioctl validate -f - - - # Also see the related command 'istioctl analyze' - istioctl analyze samples/bookinfo/networking/bookinfo-gateway.yaml -`, - Args: cobra.NoArgs, - RunE: func(c *cobra.Command, _ []string) error { - return validateFiles(istioNamespace, *defaultNamespace, filenames, c.OutOrStderr()) - }, - } - - flags := c.PersistentFlags() - flags.StringSliceVarP(&filenames, "filename", "f", nil, "Names of files to validate") - flags.BoolVarP(&referential, "referential", "x", true, "Enable structural validation for policy and telemetry") - - return c -} - -func warningToString(w validation.Warning) string { - we, ok := w.(*multierror.Error) - if ok { - we.ErrorFormat = func(i []error) string { - points := make([]string, len(i)) - for i, err := range i { - points[i] = fmt.Sprintf("* %s", err) - } - - return fmt.Sprintf( - "\n\t%s\n", - strings.Join(points, "\n\t")) - } - } - return w.Error() -} - -func transformInterfaceArray(in []interface{}) []interface{} { - out := make([]interface{}, len(in)) - for i, v := range in { - out[i] = transformMapValue(v) - } - return out -} - -func transformInterfaceMap(in map[interface{}]interface{}) map[string]interface{} { - out := make(map[string]interface{}, len(in)) - for k, v := range in { - out[fmt.Sprintf("%v", k)] = transformMapValue(v) - } - return out -} - -func transformMapValue(in interface{}) interface{} { - switch v := in.(type) { - case []interface{}: - return transformInterfaceArray(v) - case map[interface{}]interface{}: - return transformInterfaceMap(v) - default: - return v - } -} - -func servicePortPrefixed(n string) bool { - i := strings.IndexByte(n, '-') - if i >= 0 { - n = n[:i] - } - p := protocol.Parse(n) - return p == protocol.Unsupported -} - -func handleNamespace(istioNamespace string) string { - if istioNamespace == "" { - istioNamespace = constants.IstioSystemNamespace - } - return istioNamespace -} - -// TODO(nmittler): Remove this once Pilot migrates to galley schema. -func convertObjectFromUnstructured(schema collection.Schema, un *unstructured.Unstructured, domain string) (*config.Config, error) { - data, err := fromSchemaAndJSONMap(schema, un.Object["spec"]) - if err != nil { - return nil, err - } - - return &config.Config{ - Meta: config.Meta{ - GroupVersionKind: schema.Resource().GroupVersionKind(), - Name: un.GetName(), - Namespace: un.GetNamespace(), - Domain: domain, - Labels: un.GetLabels(), - Annotations: un.GetAnnotations(), - ResourceVersion: un.GetResourceVersion(), - CreationTimestamp: un.GetCreationTimestamp().Time, - }, - Spec: data, - }, nil -} - -// TODO(nmittler): Remove this once Pilot migrates to galley schema. -func fromSchemaAndJSONMap(schema collection.Schema, data interface{}) (config.Spec, error) { - // Marshal to json bytes - str, err := json.Marshal(data) - if err != nil { - return nil, err - } - out, err := schema.Resource().NewInstance() - if err != nil { - return nil, err - } - if err = config.ApplyJSONStrict(out, string(str)); err != nil { - return nil, err - } - return out, nil -} diff --git a/istioctl/pkg/validate/validate_test.go b/istioctl/pkg/validate/validate_test.go deleted file mode 100644 index a6d85b723..000000000 --- a/istioctl/pkg/validate/validate_test.go +++ /dev/null @@ -1,657 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validate - -import ( - "bytes" - "fmt" - "io" - "os" - "regexp" - "strings" - "testing" -) - -import ( - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -const ( - validDeploymentList = ` -apiVersion: v1 -items: -- apiVersion: apps/v1 - kind: Deployment - metadata: - labels: - app: hello - version: v1 - name: hello-v1 - spec: - replicas: 1 - template: - metadata: - labels: - app: hello - version: v1 - spec: - containers: - - name: hello - image: istio/examples-hello - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9080 -- apiVersion: apps/v1 - kind: Deployment - metadata: - labels: - app: details - version: v2 - name: details - spec: - replicas: 1 - template: - metadata: - labels: - app: details - version: v1 - spec: - containers: - - name: details - image: istio/examples-bookinfo-details-v1:1.10.1 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9080 -kind: List -metadata: - resourceVersion: "" - selfLink: ""` - invalidSvcList = ` -apiVersion: v1 -items: - - - apiVersion: v1 - kind: Service - metadata: - name: details - spec: - ports: - - - name: details - port: 9080 - - - apiVersion: v1 - kind: Service - metadata: - name: hello - spec: - ports: - - - port: 80 - protocol: TCP -kind: List -metadata: - resourceVersion: ""` - udpService = ` -kind: Service -metadata: - name: hello -spec: - ports: - - - protocol: udp` - skippedService = ` -kind: Service -metadata: - name: hello - namespace: dubbo-system -spec: - ports: - - - name: http - port: 9080` - validPortNamingSvc = ` -apiVersion: v1 -kind: Service -metadata: - name: hello -spec: - ports: - - name: http - port: 9080` - validPortNamingWithSuffixSvc = ` -apiVersion: v1 -kind: Service -metadata: - name: hello -spec: - ports: - - name: http-hello - port: 9080` - invalidPortNamingSvc = ` -apiVersion: v1 -kind: Service -metadata: - name: hello -spec: - ports: - - name: hello - port: 9080` - portNameMissingSvc = ` -apiVersion: v1 -kind: Service -metadata: - name: hello -spec: - ports: - - protocol: TCP` - validVirtualService = ` -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: valid-virtual-service -spec: - hosts: - - c - http: - - route: - - destination: - host: c - subset: v1 - weight: 75 - - destination: - host: c - subset: v2 - weight: 25` - validVirtualService1 = ` -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: valid-virtual-service1 -spec: - hosts: - - d - http: - - route: - - destination: - host: c - subset: v1 - weight: 75 - - destination: - host: c - subset: v2 - weight: 25` - validVirtualService2 = ` -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: valid-virtual-service2 -spec: - exportTo: - - '.' - hosts: - - d - http: - - route: - - destination: - host: c - subset: v1` - invalidVirtualService = ` -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: invalid-virtual-service -spec: - http: - - route: - - destination: - host: c - subset: v1 - weight: 75 - - destination: - host: c - subset: v2 - weight: 15` - invalidVirtualServiceV1Beta1 = ` -apiVersion: networking.istio.io/v1beta1 -kind: VirtualService -metadata: - name: invalid-virtual-service -spec: - http: -` - warnDestinationRule = `apiVersion: networking.istio.io/v1beta1 -kind: DestinationRule -metadata: - name: reviews-cb-policy -spec: - host: reviews.prod.svc.cluster.local - trafficPolicy: - outlierDetection: - consecutiveErrors: 7 -` - invalidYAML = ` -(...!)` - validKubernetesYAML = ` -apiVersion: v1 -kind: Namespace -metadata: - name: dubbo-system` - invalidUnsupportedKey = ` -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: productpage -unexpected_junk: - still_more_junk: -spec: - host: productpage` - versionLabelMissingDeployment = ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec:` - skippedDeployment = ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello - namespace: dubbo-system -spec: ~` - invalidIstioConfig = ` -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -metadata: - namespace: dubbo-system - name: example-istiocontrolplane -spec: - dummy: - traffic_management: - components: - namespace: istio-traffic-management -` - validIstioConfig = ` -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -metadata: - namespace: dubbo-system - name: example-istiocontrolplane -spec: - addonComponents: - grafana: - enabled: true -` - invalidDuplicateKey = ` -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: productpage -spec: -trafficPolicy: {} -trafficPolicy: - tls: - mode: ISTIO_MUTUAL -` - validDeployment = ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: helloworld-v1 - labels: - app: helloworld - version: v1 -spec: - replicas: 1 - selector: - matchLabels: - app: helloworld - version: v1 - template: - metadata: - annotations: - sidecar.istio.io/bootstrapOverride: "istio-custom-bootstrap-config" - labels: - app: helloworld - version: v1 - spec: - containers: - - name: helloworld - image: docker.io/istio/examples-helloworld-v1 - resources: - requests: - cpu: "100m" - imagePullPolicy: IfNotPresent - ports: - - containerPort: 5000 -` -) - -func fromYAML(in string) *unstructured.Unstructured { - var un unstructured.Unstructured - if err := yaml.Unmarshal([]byte(in), &un); err != nil { - panic(err) - } - return &un -} - -func TestValidateResource(t *testing.T) { - cases := []struct { - name string - in string - valid bool - warn bool - }{ - { - name: "valid pilot configuration", - in: validVirtualService, - valid: true, - }, - { - name: "invalid pilot configuration", - in: invalidVirtualService, - valid: false, - }, - { - name: "invalid pilot configuration v1beta1", - in: invalidVirtualServiceV1Beta1, - valid: false, - }, - { - name: "port name missing service", - in: portNameMissingSvc, - valid: false, - }, - { - name: "version label missing deployment", - in: versionLabelMissingDeployment, - valid: true, - }, - { - name: "valid port naming service", - in: validPortNamingSvc, - valid: true, - }, - { - name: "valid port naming with suffix service", - in: validPortNamingWithSuffixSvc, - valid: true, - }, - { - name: "invalid port naming service", - in: invalidPortNamingSvc, - valid: false, - }, - { - name: "invalid service list", - in: invalidSvcList, - valid: false, - }, - { - name: "valid deployment list", - in: validDeploymentList, - valid: true, - }, - { - name: "skip validating deployment", - in: skippedDeployment, - valid: true, - }, - { - name: "skip validating service", - in: skippedService, - valid: true, - }, - { - name: "service with udp port", - in: udpService, - valid: true, - }, - { - name: "invalid Istio Operator config", - in: invalidIstioConfig, - valid: false, - }, - { - name: "valid Istio Operator config", - in: validIstioConfig, - valid: true, - }, - { - name: "warning", - in: warnDestinationRule, - valid: true, - warn: true, - }, - { - name: "exportTo=.", - in: validVirtualService2, - valid: true, - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("[%v] %v ", i, c.name), func(tt *testing.T) { - defer func() { recover() }() - v := &validator{} - var writer io.Writer - warn, err := v.validateResource("dubbo-system", "", fromYAML(c.in), writer) - if (err == nil) != c.valid { - tt.Fatalf("unexpected validation result: got %v want %v: err=%v", err == nil, c.valid, err) - } - if (warn != nil) != c.warn { - tt.Fatalf("unexpected validation warning result: got %v want %v: warn=%v", warn != nil, c.warn, warn) - } - }) - } -} - -func buildMultiDocYAML(docs []string) string { - var b strings.Builder - for _, r := range docs { - if r != "" { - b.WriteString(strings.Trim(r, " \t\n")) - } - b.WriteString("\n---\n") - } - return b.String() -} - -func createTestFile(t *testing.T, data string) (string, io.Closer) { - t.Helper() - validFile, err := os.CreateTemp("", "TestValidateCommand") - if err != nil { - t.Fatal(err) - } - if _, err := validFile.WriteString(data); err != nil { - t.Fatal(err) - } - return validFile.Name(), validFile -} - -func TestValidateCommand(t *testing.T) { - valid := buildMultiDocYAML([]string{validVirtualService, validVirtualService1}) - invalid := buildMultiDocYAML([]string{invalidVirtualService, validVirtualService1}) - warnings := buildMultiDocYAML([]string{invalidVirtualService, validVirtualService1, warnDestinationRule}) - - validFilename, closeValidFile := createTestFile(t, valid) - defer closeValidFile.Close() - - invalidFilename, closeInvalidFile := createTestFile(t, invalid) - defer closeInvalidFile.Close() - - warningFilename, closeWarningFile := createTestFile(t, warnings) - defer closeWarningFile.Close() - - invalidYAMLFile, closeInvalidYAMLFile := createTestFile(t, invalidYAML) - defer closeInvalidYAMLFile.Close() - - validKubernetesYAMLFile, closeKubernetesYAMLFile := createTestFile(t, validKubernetesYAML) - defer closeKubernetesYAMLFile.Close() - - versionLabelMissingDeploymentFile, closeVersionLabelMissingDeploymentFile := createTestFile(t, versionLabelMissingDeployment) - defer closeVersionLabelMissingDeploymentFile.Close() - - portNameMissingSvcFile, closePortNameMissingSvcFile := createTestFile(t, portNameMissingSvc) - defer closePortNameMissingSvcFile.Close() - - unsupportedKeyFilename, closeUnsupportedKeyFile := createTestFile(t, invalidUnsupportedKey) - defer closeUnsupportedKeyFile.Close() - - duplicateKeyFilename, closeUnsupportedKeyFile := createTestFile(t, invalidDuplicateKey) - defer closeUnsupportedKeyFile.Close() - - validPortNamingSvcFile, closeValidPortNamingSvcFile := createTestFile(t, validPortNamingSvc) - defer closeValidPortNamingSvcFile.Close() - - validPortNamingWithSuffixSvcFile, closeValidPortNamingWithSuffixSvcFile := createTestFile(t, validPortNamingWithSuffixSvc) - defer closeValidPortNamingWithSuffixSvcFile.Close() - - invalidPortNamingSvcFile, closeInvalidPortNamingSvcFile := createTestFile(t, invalidPortNamingSvc) - defer closeInvalidPortNamingSvcFile.Close() - - cases := []struct { - name string - args []string - wantError bool - expectedRegexp *regexp.Regexp // Expected regexp output - }{ - { - name: "valid port naming service", - args: []string{"--filename", validPortNamingSvcFile}, - wantError: false, - }, - { - name: "valid port naming with suffix service", - args: []string{"--filename", validPortNamingWithSuffixSvcFile}, - wantError: false, - }, - { - name: "invalid port naming service", - args: []string{"--filename", invalidPortNamingSvcFile}, - wantError: true, - }, - { - name: "filename missing", - wantError: true, - }, - { - name: "valid resources from file", - args: []string{"--filename", validFilename}, - }, - { - name: "extra args", - args: []string{"--filename", validFilename, "extra-arg"}, - wantError: true, - }, - { - name: "invalid resources from file", - args: []string{"--filename", invalidFilename}, - wantError: true, - }, - { - name: "invalid filename", - args: []string{"--filename", "INVALID_FILE_NAME"}, - wantError: true, - }, - { - name: "invalid YAML", - args: []string{"--filename", invalidYAMLFile}, - wantError: true, - }, - { - name: "valid Kubernetes YAML", - args: []string{"--filename", validKubernetesYAMLFile}, - expectedRegexp: regexp.MustCompile(`^".*" is valid -$`), - wantError: false, - }, - { - name: "invalid top-level key", - args: []string{"--filename", unsupportedKeyFilename}, - expectedRegexp: regexp.MustCompile(`.*unknown field "unexpected_junk"`), - wantError: true, - }, - { - name: "version label missing deployment", - args: []string{"--filename", versionLabelMissingDeploymentFile}, - wantError: false, - }, - { - name: "port name missing service", - args: []string{"--filename", portNameMissingSvcFile}, - wantError: true, - }, - { - name: "duplicate key", - args: []string{"--filename", duplicateKeyFilename}, - expectedRegexp: regexp.MustCompile(`.*key ".*" already set`), - wantError: true, - }, - { - name: "warning", - args: []string{"--filename", warningFilename}, - expectedRegexp: regexp.MustCompile(`(?m)".*" has warnings: - \* DestinationRule//reviews-cb-policy: outlier detection consecutive errors is deprecated, use consecutiveGatewayErrors or consecutive5xxErrors instead - -Error: 1 error occurred: - \* VirtualService//invalid-virtual-service: total destination weight 90 != 100`), - wantError: true, - }, - } - istioNamespace := "dubbo-system" - defaultNamespace := "" - for i, c := range cases { - t.Run(fmt.Sprintf("[%v] %v", i, c.name), func(t *testing.T) { - validateCmd := NewValidateCommand(&istioNamespace, &defaultNamespace) - validateCmd.SilenceUsage = true - validateCmd.SetArgs(c.args) - - // capture output to keep test logs clean - var out bytes.Buffer - validateCmd.SetOut(&out) - validateCmd.SetErr(&out) - - err := validateCmd.Execute() - if (err != nil) != c.wantError { - t.Errorf("unexpected validate return status: got %v want %v: \nerr=%v", - err != nil, c.wantError, err) - } - output := out.String() - if c.expectedRegexp != nil && !c.expectedRegexp.MatchString(output) { - t.Errorf("Output didn't match for 'istioctl %s'\n got %v\nwant: %v", - strings.Join(c.args, " "), output, c.expectedRegexp) - } - }) - } -} - -func TestGetTemplateLabels(t *testing.T) { - un := fromYAML(validDeployment) - - labels, err := GetTemplateLabels(un) - if err != nil { - t.Fatal(err) - } - assert.Equal(t, labels, map[string]string{ - "app": "helloworld", - "version": "v1", - }) -} diff --git a/istioctl/pkg/verifier/k8s_util.go b/istioctl/pkg/verifier/k8s_util.go deleted file mode 100644 index f1760693d..000000000 --- a/istioctl/pkg/verifier/k8s_util.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package verifier - -import ( - "fmt" -) - -import ( - appsv1 "k8s.io/api/apps/v1" - v1batch "k8s.io/api/batch/v1" - apimachinery_schema "k8s.io/apimachinery/pkg/runtime/schema" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -func verifyDeploymentStatus(deployment *appsv1.Deployment) error { - cond := getDeploymentCondition(deployment.Status, appsv1.DeploymentProgressing) - if cond != nil && cond.Reason == "ProgressDeadlineExceeded" { - return fmt.Errorf("deployment %q exceeded its progress deadline", deployment.Name) - } - if deployment.Spec.Replicas != nil && deployment.Status.UpdatedReplicas < *deployment.Spec.Replicas { - return fmt.Errorf("waiting for deployment %q rollout to finish: %d out of %d new replicas have been updated", - deployment.Name, deployment.Status.UpdatedReplicas, *deployment.Spec.Replicas) - } - if deployment.Status.Replicas > deployment.Status.UpdatedReplicas { - return fmt.Errorf("waiting for deployment %q rollout to finish: %d old replicas are pending termination", - deployment.Name, deployment.Status.Replicas-deployment.Status.UpdatedReplicas) - } - if deployment.Status.AvailableReplicas < deployment.Status.UpdatedReplicas { - return fmt.Errorf("waiting for deployment %q rollout to finish: %d of %d updated replicas are available", - deployment.Name, deployment.Status.AvailableReplicas, deployment.Status.UpdatedReplicas) - } - return nil -} - -func getDeploymentCondition(status appsv1.DeploymentStatus, condType appsv1.DeploymentConditionType) *appsv1.DeploymentCondition { - for i := range status.Conditions { - c := status.Conditions[i] - if c.Type == condType { - return &c - } - } - return nil -} - -func verifyJobPostInstall(job *v1batch.Job) error { - for _, c := range job.Status.Conditions { - if c.Type == v1batch.JobFailed { - return fmt.Errorf("the required Job %s/%s failed", job.Namespace, job.Name) - } - } - return nil -} - -func findResourceInSpec(gvk apimachinery_schema.GroupVersionKind) string { - s, f := collections.All.FindByGroupVersionKind(config.GroupVersionKind{ - Group: gvk.Group, - Version: gvk.Version, - Kind: gvk.Kind, - }) - if !f { - return "" - } - return s.Resource().Plural() -} diff --git a/istioctl/pkg/verifier/verifier.go b/istioctl/pkg/verifier/verifier.go deleted file mode 100644 index f3df01fe2..000000000 --- a/istioctl/pkg/verifier/verifier.go +++ /dev/null @@ -1,490 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package verifier - -import ( - "context" - "fmt" - "strings" -) - -import ( - "github.com/fatih/color" - "istio.io/api/label" - admit_v1 "k8s.io/api/admissionregistration/v1" - appsv1 "k8s.io/api/apps/v1" - v1batch "k8s.io/api/batch/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - apimachinery_schema "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/cli-runtime/pkg/genericclioptions" - "k8s.io/cli-runtime/pkg/resource" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/clioptions" - operator_istio "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio" - "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/controlplane" - "github.com/apache/dubbo-go-pixiu/operator/pkg/manifest" - "github.com/apache/dubbo-go-pixiu/operator/pkg/translate" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -var istioOperatorGVR = apimachinery_schema.GroupVersionResource{ - Group: v1alpha1.SchemeGroupVersion.Group, - Version: v1alpha1.SchemeGroupVersion.Version, - Resource: "istiooperators", -} - -// StatusVerifier checks status of certain resources like deployment, -// jobs and also verifies count of certain resource types. -type StatusVerifier struct { - istioNamespace string - manifestsPath string - filenames []string - controlPlaneOpts clioptions.ControlPlaneOptions - logger clog.Logger - iop *v1alpha1.IstioOperator - successMarker string - failureMarker string - client kube.ExtendedClient -} - -type StatusVerifierOptions func(*StatusVerifier) - -func WithLogger(l clog.Logger) StatusVerifierOptions { - return func(s *StatusVerifier) { - s.logger = l - } -} - -func WithIOP(iop *v1alpha1.IstioOperator) StatusVerifierOptions { - return func(s *StatusVerifier) { - s.iop = iop - } -} - -// NewStatusVerifier creates a new instance of post-install verifier -// which checks the status of various resources from the manifest. -func NewStatusVerifier(istioNamespace, manifestsPath, kubeconfig, context string, - filenames []string, controlPlaneOpts clioptions.ControlPlaneOptions, - options ...StatusVerifierOptions) (*StatusVerifier, error) { - client, err := kube.NewExtendedClient(kube.BuildClientCmd(kubeconfig, context), "") - if err != nil { - return nil, fmt.Errorf("failed to connect Kubernetes API server, error: %v", err) - } - - verifier := StatusVerifier{ - logger: clog.NewDefaultLogger(), - successMarker: "✔", - failureMarker: "✘", - istioNamespace: istioNamespace, - manifestsPath: manifestsPath, - filenames: filenames, - controlPlaneOpts: controlPlaneOpts, - client: client, - } - - for _, opt := range options { - opt(&verifier) - } - - return &verifier, nil -} - -func (v *StatusVerifier) Colorize() { - v.successMarker = color.New(color.FgGreen).Sprint(v.successMarker) - v.failureMarker = color.New(color.FgRed).Sprint(v.failureMarker) -} - -// Verify implements Verifier interface. Here we check status of deployment -// and jobs, count various resources for verification. -func (v *StatusVerifier) Verify() error { - if v.iop != nil { - return v.verifyFinalIOP() - } - if len(v.filenames) == 0 { - return v.verifyInstallIOPRevision() - } - return v.verifyInstall() -} - -func (v *StatusVerifier) verifyInstallIOPRevision() error { - var err error - if v.controlPlaneOpts.Revision == "" { - v.controlPlaneOpts.Revision, err = v.getRevision() - if err != nil { - return err - } - } - iop, err := v.operatorFromCluster(v.controlPlaneOpts.Revision) - if err != nil { - // At this point we know there is no IstioOperator defining a control plane. This may - // be the case in a Istio cluster with external control plane. - v.logger.LogAndErrorf("error while fetching revision %s: %v", v.controlPlaneOpts.Revision, err.Error()) - injector, err2 := v.injectorFromCluster(v.controlPlaneOpts.Revision) - if err2 == nil && injector != nil { - // The cluster *is* configured for Istio, but no IOP is present. This could mean - // - the user followed our remote control plane instructions - // - helm was used - // - user did `istioctl manifest generate | kubectl apply ...` - return fmt.Errorf("Istio present but verify-install needs an IstioOperator or manifest for comparison. Supply flag --filename ") // nolint: stylecheck - } - return fmt.Errorf("could not load IstioOperator from cluster: %v. Use --filename", err) - } - if v.manifestsPath != "" { - iop.Spec.InstallPackagePath = v.manifestsPath - } - profile := manifest.GetProfile(iop) - by, err := yaml.Marshal(iop) - if err != nil { - return err - } - mergedIOP, err := manifest.GetMergedIOP(string(by), profile, v.manifestsPath, v.controlPlaneOpts.Revision, v.client, v.logger) - if err != nil { - return err - } - crdCount, istioDeploymentCount, err := v.verifyPostInstallIstioOperator( - mergedIOP, fmt.Sprintf("in cluster operator %s", mergedIOP.GetName())) - return v.reportStatus(crdCount, istioDeploymentCount, err) -} - -func (v *StatusVerifier) getRevision() (string, error) { - var revision string - var revs string - revCount := 0 - pods, err := v.client.PodsForSelector(context.TODO(), v.istioNamespace, "app=istiod") - if err != nil { - return "", fmt.Errorf("failed to fetch istiod pod, error: %v", err) - } - for _, pod := range pods.Items { - rev := pod.ObjectMeta.GetLabels()[label.IoIstioRev.Name] - revCount++ - if rev == "default" { - continue - } - revision = rev - } - if revision == "" { - revs = "default" - } else { - revs = revision - } - v.logger.LogAndPrintf("%d Istio control planes detected, checking --revision %q only", revCount, revs) - return revision, nil -} - -func (v *StatusVerifier) verifyFinalIOP() error { - crdCount, istioDeploymentCount, err := v.verifyPostInstallIstioOperator( - v.iop, fmt.Sprintf("IOP:%s", v.iop.GetName())) - return v.reportStatus(crdCount, istioDeploymentCount, err) -} - -func (v *StatusVerifier) verifyInstall() error { - // This is not a pre-check. Check that the supplied resources exist in the cluster - r := resource.NewBuilder(v.client.UtilFactory()). - Unstructured(). - FilenameParam(false, &resource.FilenameOptions{Filenames: v.filenames}). - Flatten(). - Do() - if r.Err() != nil { - return r.Err() - } - visitor := genericclioptions.ResourceFinderForResult(r).Do() - crdCount, istioDeploymentCount, err := v.verifyPostInstall( - visitor, strings.Join(v.filenames, ",")) - return v.reportStatus(crdCount, istioDeploymentCount, err) -} - -func (v *StatusVerifier) verifyPostInstallIstioOperator(iop *v1alpha1.IstioOperator, filename string) (int, int, error) { - t := translate.NewTranslator() - ver, err := v.client.GetKubernetesVersion() - if err != nil { - return 0, 0, err - } - cp, err := controlplane.NewIstioControlPlane(iop.Spec, t, nil, ver) - if err != nil { - return 0, 0, err - } - if err := cp.Run(); err != nil { - return 0, 0, err - } - - manifests, errs := cp.RenderManifest() - if errs != nil && len(errs) > 0 { - return 0, 0, errs.ToError() - } - - builder := resource.NewBuilder(v.client.UtilFactory()).ContinueOnError().Unstructured() - for cat, manifest := range manifests { - for i, manitem := range manifest { - reader := strings.NewReader(manitem) - pseudoFilename := fmt.Sprintf("%s:%d generated from %s", cat, i, filename) - builder = builder.Stream(reader, pseudoFilename) - } - } - r := builder.Flatten().Do() - if r.Err() != nil { - return 0, 0, r.Err() - } - visitor := genericclioptions.ResourceFinderForResult(r).Do() - // Indirectly RECURSE back into verifyPostInstall with the manifest we just generated - generatedCrds, generatedDeployments, err := v.verifyPostInstall( - visitor, - fmt.Sprintf("generated from %s", filename)) - if err != nil { - return generatedCrds, generatedDeployments, err - } - - return generatedCrds, generatedDeployments, nil -} - -func (v *StatusVerifier) verifyPostInstall(visitor resource.Visitor, filename string) (int, int, error) { - crdCount := 0 - istioDeploymentCount := 0 - err := visitor.Visit(func(info *resource.Info, err error) error { - if err != nil { - return err - } - content, err := runtime.DefaultUnstructuredConverter.ToUnstructured(info.Object) - if err != nil { - return err - } - un := &unstructured.Unstructured{Object: content} - kind := un.GetKind() - name := un.GetName() - namespace := un.GetNamespace() - kinds := findResourceInSpec(un.GetObjectKind().GroupVersionKind()) - if kinds == "" { - kinds = strings.ToLower(kind) + "s" - } - if namespace == "" { - namespace = v.istioNamespace - } - switch kind { - case "Deployment": - deployment := &appsv1.Deployment{} - err = info.Client. - Get(). - Resource(kinds). - Namespace(namespace). - Name(name). - VersionedParams(&meta_v1.GetOptions{}, scheme.ParameterCodec). - Do(context.TODO()). - Into(deployment) - if err != nil { - v.reportFailure(kind, name, namespace, err) - return err - } - if err = verifyDeploymentStatus(deployment); err != nil { - ivf := istioVerificationFailureError(filename, err) - v.reportFailure(kind, name, namespace, ivf) - return ivf - } - if namespace == v.istioNamespace && strings.HasPrefix(name, "istio") { - istioDeploymentCount++ - } - case "Job": - job := &v1batch.Job{} - err = info.Client. - Get(). - Resource(kinds). - Namespace(namespace). - Name(name). - VersionedParams(&meta_v1.GetOptions{}, scheme.ParameterCodec). - Do(context.TODO()). - Into(job) - if err != nil { - v.reportFailure(kind, name, namespace, err) - return err - } - if err := verifyJobPostInstall(job); err != nil { - ivf := istioVerificationFailureError(filename, err) - v.reportFailure(kind, name, namespace, ivf) - return ivf - } - case "IstioOperator": - // It is not a problem if the cluster does not include the IstioOperator - // we are checking. Instead, verify the cluster has the things the - // IstioOperator specifies it should have. - - // IstioOperator isn't part of pkg/config/schema/collections, - // usual conversion not available. Convert unstructured to string - // and ask operator code to unmarshal. - fixTimestampRelatedUnmarshalIssues(un) - - by := util.ToYAML(un) - unmergedIOP, err := operator_istio.UnmarshalIstioOperator(by, true) - if err != nil { - v.reportFailure(kind, name, namespace, err) - return err - } - profile := manifest.GetProfile(unmergedIOP) - iop, err := manifest.GetMergedIOP(by, profile, v.manifestsPath, v.controlPlaneOpts.Revision, - v.client, v.logger) - if err != nil { - v.reportFailure(kind, name, namespace, err) - return err - } - if v.manifestsPath != "" { - iop.Spec.InstallPackagePath = v.manifestsPath - } - if v1alpha1.Namespace(iop.Spec) == "" { - v1alpha1.SetNamespace(iop.Spec, v.istioNamespace) - } - generatedCrds, generatedDeployments, err := v.verifyPostInstallIstioOperator(iop, filename) - crdCount += generatedCrds - istioDeploymentCount += generatedDeployments - if err != nil { - return err - } - default: - result := info.Client. - Get(). - Resource(kinds). - Name(name). - Do(context.TODO()) - if result.Error() != nil { - result = info.Client. - Get(). - Resource(kinds). - Namespace(namespace). - Name(name). - Do(context.TODO()) - if result.Error() != nil { - v.reportFailure(kind, name, namespace, result.Error()) - return istioVerificationFailureError(filename, - fmt.Errorf("the required %s:%s is not ready due to: %v", - kind, name, result.Error())) - } - } - if kind == "CustomResourceDefinition" { - crdCount++ - } - } - v.logger.LogAndPrintf("%s %s: %s.%s checked successfully", v.successMarker, kind, name, namespace) - return nil - }) - return crdCount, istioDeploymentCount, err -} - -// Find Istio injector matching revision. ("" matches any revision.) -func (v *StatusVerifier) injectorFromCluster(revision string) (*admit_v1.MutatingWebhookConfiguration, error) { - hooks, err := v.client.AdmissionregistrationV1().MutatingWebhookConfigurations().List(context.Background(), meta_v1.ListOptions{}) - if err != nil { - return nil, err - } - - revCount := 0 - var hookmatch *admit_v1.MutatingWebhookConfiguration - for i, hook := range hooks.Items { - rev := hook.ObjectMeta.GetLabels()[label.IoIstioRev.Name] - if rev != "" { - revCount++ - revision = rev - if revision == "" || revision == rev { - hookmatch = &hooks.Items[i] - } - } - } - - v.logger.LogAndPrintf("%d Istio injectors detected", revCount) - if hookmatch != nil { - return hookmatch, nil - } - - return nil, fmt.Errorf("Istio injector revision %q not found", revision) // nolint: stylecheck -} - -// Find an IstioOperator matching revision in the cluster. The IstioOperators -// don't have a label for their revision, so we parse them and check .Spec.Revision -func (v *StatusVerifier) operatorFromCluster(revision string) (*v1alpha1.IstioOperator, error) { - iops, err := AllOperatorsInCluster(v.client.Dynamic()) - if err != nil { - return nil, err - } - for _, iop := range iops { - if iop.Spec.Revision == revision || - (iop.Spec.Revision == "default" && revision == "") || - (iop.Spec.Revision == "" && revision == "default") { - return iop, nil - } - } - return nil, fmt.Errorf("control plane revision %q not found", revision) -} - -func (v *StatusVerifier) reportStatus(crdCount, istioDeploymentCount int, err error) error { - v.logger.LogAndPrintf("Checked %v custom resource definitions", crdCount) - v.logger.LogAndPrintf("Checked %v Istio Deployments", istioDeploymentCount) - if istioDeploymentCount == 0 { - if err != nil { - v.logger.LogAndPrintf("! No Istio installation found: %v", err) - } else { - v.logger.LogAndPrintf("! No Istio installation found") - } - return fmt.Errorf("no Istio installation found") - } - if err != nil { - // Don't return full error; it is usually an unwieldy aggregate - return fmt.Errorf("Istio installation failed") // nolint - } - v.logger.LogAndPrintf("%s Istio is installed and verified successfully", v.successMarker) - return nil -} - -func fixTimestampRelatedUnmarshalIssues(un *unstructured.Unstructured) { - un.SetCreationTimestamp(meta_v1.Time{}) // UnmarshalIstioOperator chokes on these - - // UnmarshalIstioOperator fails because managedFields could contain time - // and gogo/protobuf/jsonpb(v1.3.1) tries to unmarshal it as struct (the type - // meta_v1.Time is really a struct) and fails. - un.SetManagedFields([]meta_v1.ManagedFieldsEntry{}) -} - -// Find all IstioOperator in the cluster. -func AllOperatorsInCluster(client dynamic.Interface) ([]*v1alpha1.IstioOperator, error) { - ul, err := client. - Resource(istioOperatorGVR). - List(context.TODO(), meta_v1.ListOptions{}) - if err != nil { - return nil, err - } - retval := make([]*v1alpha1.IstioOperator, 0) - for _, un := range ul.Items { - fixTimestampRelatedUnmarshalIssues(&un) - by := util.ToYAML(un.Object) - iop, err := operator_istio.UnmarshalIstioOperator(by, true) - if err != nil { - return nil, err - } - retval = append(retval, iop) - } - return retval, nil -} - -func istioVerificationFailureError(filename string, reason error) error { - return fmt.Errorf("Istio installation failed, incomplete or does not match \"%s\": %v", filename, reason) // nolint -} - -func (v *StatusVerifier) reportFailure(kind, name, namespace string, err error) { - v.logger.LogAndPrintf("%s %s: %s.%s: %v", v.failureMarker, kind, name, namespace, err) -} diff --git a/istioctl/pkg/verifier/verify_test.go b/istioctl/pkg/verifier/verify_test.go deleted file mode 100644 index a18807c43..000000000 --- a/istioctl/pkg/verifier/verify_test.go +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package verifier - -import ( - "fmt" - "testing" -) - -import ( - appsv1 "k8s.io/api/apps/v1" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -var ( - availableDeployment = appsv1.Deployment{ - Status: appsv1.DeploymentStatus{ - Conditions: []appsv1.DeploymentCondition{ - { - Type: appsv1.DeploymentAvailable, - }, - }, - }, - } - - scaleUpRollingDeployment = appsv1.Deployment{ - Spec: appsv1.DeploymentSpec{ - Replicas: &[]int32{3}[0], - }, - Status: appsv1.DeploymentStatus{ - Conditions: []appsv1.DeploymentCondition{ - { - Type: appsv1.DeploymentProgressing, - }, - { - Type: appsv1.DeploymentAvailable, - }, - }, - UpdatedReplicas: 2, - }, - } - - deletingOldRollingDeployment = appsv1.Deployment{ - Spec: appsv1.DeploymentSpec{ - Replicas: &[]int32{2}[0], - }, - Status: appsv1.DeploymentStatus{ - Conditions: []appsv1.DeploymentCondition{ - { - Type: appsv1.DeploymentProgressing, - }, - { - Type: appsv1.DeploymentAvailable, - }, - }, - UpdatedReplicas: 2, - AvailableReplicas: 2, - Replicas: 3, - }, - } - - failedDeployment = appsv1.Deployment{ - Spec: appsv1.DeploymentSpec{ - Replicas: &[]int32{2}[0], - }, - Status: appsv1.DeploymentStatus{ - Conditions: []appsv1.DeploymentCondition{ - { - Type: appsv1.DeploymentReplicaFailure, - }, - }, - UpdatedReplicas: 2, - AvailableReplicas: 0, - Replicas: 3, - }, - } - - deadlineExceededDeployment = appsv1.Deployment{ - Status: appsv1.DeploymentStatus{ - Conditions: []appsv1.DeploymentCondition{ - { - Type: appsv1.DeploymentProgressing, - Reason: "ProgressDeadlineExceeded", - }, - }, - }, - } -) - -func TestGetDeploymentStatus(t *testing.T) { - errCases := []*appsv1.Deployment{ - &scaleUpRollingDeployment, - &deletingOldRollingDeployment, - &failedDeployment, - &deadlineExceededDeployment, - } - for i, c := range errCases { - t.Run(fmt.Sprintf("[err-%v] ", i), func(tt *testing.T) { - if err := verifyDeploymentStatus(c); err == nil { - tt.Fatalf("unexpected nil error") - } - }) - } - - okCases := []*appsv1.Deployment{ - &availableDeployment, - } - for i, c := range okCases { - t.Run(fmt.Sprintf("[ok-%v] ", i), func(tt *testing.T) { - if err := verifyDeploymentStatus(c); err != nil { - tt.Fatalf("unexpected error: %v", err) - } - }) - } -} - -func TestGetDeploymentCondition(t *testing.T) { - cases := []struct { - status appsv1.DeploymentStatus - condType appsv1.DeploymentConditionType - shouldFind bool - }{ - { - // Simple "find Available in Available" - status: availableDeployment.Status, - condType: appsv1.DeploymentAvailable, - shouldFind: true, - }, - { - // find Available in Progressing,Available - // valid in e.g. RollingUpdate - status: scaleUpRollingDeployment.Status, - condType: appsv1.DeploymentAvailable, - shouldFind: true, - }, - { - // find Available in ReplicaFailure - status: failedDeployment.Status, - condType: appsv1.DeploymentAvailable, - shouldFind: false, - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("[%v] ", i), func(tt *testing.T) { - dc := getDeploymentCondition(c.status, c.condType) - if !c.shouldFind { - if dc != nil { - tt.Fatalf("unexpected condition: got %v want nil", dc) - } - } else { - if dc.Type != c.condType { - tt.Fatalf("unexpected condition: got %v want %v", dc, c.condType) - } - } - }) - } -} - -func TestFindResourceInSpec(t *testing.T) { - cases := []struct { - kind schema.GroupVersionKind - plural string - }{ - { - // Should find Kubernetes resourcespecs - kind: gvk.Service.Kubernetes(), - plural: "services", - }, - { - // Should be empty for not-found - kind: schema.GroupVersionKind{Kind: "fake"}, - plural: "", - }, - { - // Should be empty for empty input - kind: schema.GroupVersionKind{}, - plural: "", - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("[%v] %v ", i, c.kind), func(tt *testing.T) { - plural := findResourceInSpec(c.kind) - if plural != c.plural { - tt.Fatalf("unexpected plural from kind: got %v want %v", plural, c.plural) - } - }) - } -} diff --git a/istioctl/pkg/writer/compare/cluster.go b/istioctl/pkg/writer/compare/cluster.go deleted file mode 100644 index 6a919ba15..000000000 --- a/istioctl/pkg/writer/compare/cluster.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package compare - -import ( - "bytes" - "fmt" -) - -import ( - "github.com/pmezard/go-difflib/difflib" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -// ClusterDiff prints a diff between Istiod and Envoy clusters to the passed writer -func (c *Comparator) ClusterDiff() error { - envoyBytes, istiodBytes := &bytes.Buffer{}, &bytes.Buffer{} - envoyClusterDump, err := c.envoy.GetDynamicClusterDump(true) - if err != nil { - envoyBytes.WriteString(err.Error()) - } else { - envoy, err := protomarshal.ToJSONWithIndent(envoyClusterDump, " ") - if err != nil { - return err - } - envoyBytes.WriteString(envoy) - } - istiodClusterDump, err := c.istiod.GetDynamicClusterDump(true) - if err != nil { - istiodBytes.WriteString(err.Error()) - } else { - istiod, err := protomarshal.ToJSONWithIndent(istiodClusterDump, " ") - if err != nil { - return err - } - istiodBytes.WriteString(istiod) - } - diff := difflib.UnifiedDiff{ - FromFile: "Istiod Clusters", - A: difflib.SplitLines(istiodBytes.String()), - ToFile: "Envoy Clusters", - B: difflib.SplitLines(envoyBytes.String()), - Context: c.context, - } - text, err := difflib.GetUnifiedDiffString(diff) - if err != nil { - return err - } - if text != "" { - fmt.Fprintln(c.w, text) - } else { - fmt.Fprintln(c.w, "Clusters Match") - } - return nil -} diff --git a/istioctl/pkg/writer/compare/cluster_test.go b/istioctl/pkg/writer/compare/cluster_test.go deleted file mode 100644 index e63de0a48..000000000 --- a/istioctl/pkg/writer/compare/cluster_test.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package compare diff --git a/istioctl/pkg/writer/compare/comparator.go b/istioctl/pkg/writer/compare/comparator.go deleted file mode 100644 index 7c364da1b..000000000 --- a/istioctl/pkg/writer/compare/comparator.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package compare - -import ( - "encoding/json" - "fmt" - "io" -) - -import ( - adminapi "github.com/envoyproxy/go-control-plane/envoy/admin/v3" - xdsapi "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/configdump" -) - -// Comparator diffs between a config dump from Istiod and one from Envoy -type Comparator struct { - envoy, istiod *configdump.Wrapper - w io.Writer - context int - location string -} - -// NewComparator is a comparator constructor -func NewComparator(w io.Writer, istiodResponses map[string][]byte, envoyResponse []byte) (*Comparator, error) { - c := &Comparator{} - for _, resp := range istiodResponses { - istiodDump := &configdump.Wrapper{} - err := json.Unmarshal(resp, istiodDump) - if err != nil { - continue - } - c.istiod = istiodDump - break - } - if c.istiod == nil { - return nil, fmt.Errorf("unable to find config dump in Istiod responses") - } - envoyDump := &configdump.Wrapper{} - err := json.Unmarshal(envoyResponse, envoyDump) - if err != nil { - return nil, err - } - c.envoy = envoyDump - c.w = w - c.context = 7 - c.location = "Local" // the time.Location for formatting time.Time instances - return c, nil -} - -// NewXdsComparator is a comparator constructor -func NewXdsComparator(w io.Writer, istiodResponses map[string]*xdsapi.DiscoveryResponse, envoyResponse []byte) (*Comparator, error) { - c := &Comparator{} - for _, resp := range istiodResponses { - if len(resp.Resources) > 0 { - c.istiod = &configdump.Wrapper{ - ConfigDump: &adminapi.ConfigDump{ - Configs: resp.Resources, - }, - } - break - } - } - if c.istiod == nil { - return nil, fmt.Errorf("unable to find config dump in Istiod responses") - } - envoyDump := &configdump.Wrapper{} - err := json.Unmarshal(envoyResponse, envoyDump) - if err != nil { - return nil, err - } - c.envoy = envoyDump - c.w = w - c.context = 7 - c.location = "Local" // the time.Location for formatting time.Time instances - return c, nil -} - -// Diff prints a diff between Istiod and Envoy to the passed writer -func (c *Comparator) Diff() error { - if err := c.ClusterDiff(); err != nil { - return err - } - if err := c.ListenerDiff(); err != nil { - return err - } - return c.RouteDiff() -} diff --git a/istioctl/pkg/writer/compare/comparator_test.go b/istioctl/pkg/writer/compare/comparator_test.go deleted file mode 100644 index e63de0a48..000000000 --- a/istioctl/pkg/writer/compare/comparator_test.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package compare diff --git a/istioctl/pkg/writer/compare/listener.go b/istioctl/pkg/writer/compare/listener.go deleted file mode 100644 index 65bc23458..000000000 --- a/istioctl/pkg/writer/compare/listener.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package compare - -import ( - "bytes" - "fmt" -) - -import ( - "github.com/pmezard/go-difflib/difflib" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -// ListenerDiff prints a diff between Istiod and Envoy listeners to the passed writer -func (c *Comparator) ListenerDiff() error { - envoyBytes, istiodBytes := &bytes.Buffer{}, &bytes.Buffer{} - envoyListenerDump, err := c.envoy.GetDynamicListenerDump(true) - if err != nil { - envoyBytes.WriteString(err.Error()) - } else { - envoy, err := protomarshal.ToJSONWithIndent(envoyListenerDump, " ") - if err != nil { - return err - } - envoyBytes.WriteString(envoy) - } - istiodListenerDump, err := c.istiod.GetDynamicListenerDump(true) - if err != nil { - istiodBytes.WriteString(err.Error()) - } else { - istiod, err := protomarshal.ToJSONWithIndent(istiodListenerDump, " ") - if err != nil { - return err - } - istiodBytes.WriteString(istiod) - } - diff := difflib.UnifiedDiff{ - FromFile: "Istiod Listeners", - A: difflib.SplitLines(istiodBytes.String()), - ToFile: "Envoy Listeners", - B: difflib.SplitLines(envoyBytes.String()), - Context: c.context, - } - text, err := difflib.GetUnifiedDiffString(diff) - if err != nil { - return err - } - if text != "" { - fmt.Fprintln(c.w, text) - } else { - fmt.Fprintln(c.w, "Listeners Match") - } - return nil -} diff --git a/istioctl/pkg/writer/compare/route.go b/istioctl/pkg/writer/compare/route.go deleted file mode 100644 index 0014dd67e..000000000 --- a/istioctl/pkg/writer/compare/route.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package compare - -import ( - "bytes" - "fmt" - "time" -) - -import ( - "github.com/pmezard/go-difflib/difflib" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -// RouteDiff prints a diff between Istiod and Envoy routes to the passed writer -func (c *Comparator) RouteDiff() error { - envoyBytes, istiodBytes := &bytes.Buffer{}, &bytes.Buffer{} - envoyRouteDump, err := c.envoy.GetDynamicRouteDump(true) - if err != nil { - envoyBytes.WriteString(err.Error()) - } else { - envoy, err := protomarshal.ToJSONWithIndent(envoyRouteDump, " ") - if err != nil { - return err - } - envoyBytes.WriteString(envoy) - } - istiodRouteDump, err := c.istiod.GetDynamicRouteDump(true) - if err != nil { - istiodBytes.WriteString(err.Error()) - } else { - istiod, err := protomarshal.ToJSONWithIndent(istiodRouteDump, " ") - if err != nil { - return err - } - istiodBytes.WriteString(istiod) - } - diff := difflib.UnifiedDiff{ - FromFile: "Istiod Routes", - A: difflib.SplitLines(istiodBytes.String()), - ToFile: "Envoy Routes", - B: difflib.SplitLines(envoyBytes.String()), - Context: c.context, - } - text, err := difflib.GetUnifiedDiffString(diff) - if err != nil { - return err - } - lastUpdatedStr := "" - if lastUpdated, err := c.envoy.GetLastUpdatedDynamicRouteTime(); err != nil { - return err - } else if lastUpdated != nil { - loc, err := time.LoadLocation(c.location) - if err != nil { - loc, _ = time.LoadLocation("UTC") - } - lastUpdatedStr = fmt.Sprintf(" (RDS last loaded at %s)", lastUpdated.In(loc).Format(time.RFC1123)) - } - if text != "" { - fmt.Fprintf(c.w, "Routes Don't Match%s\n", lastUpdatedStr) - fmt.Fprintln(c.w, text) - } else { - fmt.Fprintf(c.w, "Routes Match%s\n", lastUpdatedStr) - } - return nil -} diff --git a/istioctl/pkg/writer/compare/sds/util.go b/istioctl/pkg/writer/compare/sds/util.go deleted file mode 100644 index 1fb086a6d..000000000 --- a/istioctl/pkg/writer/compare/sds/util.go +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sdscompare - -import ( - "crypto/x509" - "encoding/pem" - "fmt" - "time" -) - -import ( - envoy_admin "github.com/envoyproxy/go-control-plane/envoy/admin/v3" - auth "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/configdump" -) - -// SecretItemDiff represents a secret that has been diffed between nodeagent and proxy -type SecretItemDiff struct { - Agent string `json:"agent"` - Proxy string `json:"proxy"` - SecretItem -} - -// SecretItem is an intermediate representation of secrets, used to provide a common -// format between the envoy proxy secrets and node agent output which can be diffed -type SecretItem struct { - Name string `json:"resource_name"` - Data string `json:"cert"` - Source string `json:"source"` - Destination string `json:"destination"` - State string `json:"state"` - SecretMeta -} - -// SecretMeta holds selected fields which can be extracted from parsed x509 cert -type SecretMeta struct { - Valid bool `json:"cert_valid"` - SerialNumber string `json:"serial_number"` - NotAfter string `json:"not_after"` - NotBefore string `json:"not_before"` - Type string `json:"type"` -} - -// NewSecretItemBuilder returns a new builder to create a secret item -func NewSecretItemBuilder() SecretItemBuilder { - return &secretItemBuilder{} -} - -// SecretItemBuilder wraps the process of setting fields for the SecretItem -// and builds the Metadata fields from the cert contents behind the scenes -type SecretItemBuilder interface { - Name(string) SecretItemBuilder - Data(string) SecretItemBuilder - Source(string) SecretItemBuilder - Destination(string) SecretItemBuilder - State(string) SecretItemBuilder - Build() (SecretItem, error) -} - -// secretItemBuilder implements SecretItemBuilder, and acts as an intermediate before SecretItem generation -type secretItemBuilder struct { - name string - data string - source string - dest string - state string - SecretMeta -} - -// Name sets the name field on a secretItemBuilder -func (s *secretItemBuilder) Name(name string) SecretItemBuilder { - s.name = name - return s -} - -// Data sets the data field on a secretItemBuilder -func (s *secretItemBuilder) Data(data string) SecretItemBuilder { - s.data = data - return s -} - -// Source sets the source field on a secretItemBuilder -func (s *secretItemBuilder) Source(source string) SecretItemBuilder { - s.source = source - return s -} - -// Destination sets the destination field on a secretItemBuilder -func (s *secretItemBuilder) Destination(dest string) SecretItemBuilder { - s.dest = dest - return s -} - -// State sets the state of the secret on the agent or sidecar -func (s *secretItemBuilder) State(state string) SecretItemBuilder { - s.state = state - return s -} - -// Build takes the set fields from the builder and constructs the actual SecretItem -// including generating the SecretMeta from the supplied cert data, if present -func (s *secretItemBuilder) Build() (SecretItem, error) { - result := SecretItem{ - Name: s.name, - Data: s.data, - Source: s.source, - Destination: s.dest, - State: s.state, - } - - var meta SecretMeta - var err error - if s.data != "" { - meta, err = secretMetaFromCert([]byte(s.data)) - if err != nil { - log.Debugf("failed to parse secret resource %s from source %s: %v", - s.name, s.source, err) - result.Valid = false - return result, nil - } - result.SecretMeta = meta - result.Valid = true - return result, nil - } - result.Valid = false - return result, nil -} - -// GetEnvoySecrets parses the secrets section of the config dump into []SecretItem -func GetEnvoySecrets( - wrapper *configdump.Wrapper) ([]SecretItem, error) { - secretConfigDump, err := wrapper.GetSecretConfigDump() - if err != nil { - return nil, err - } - - proxySecretItems := make([]SecretItem, 0) - for _, warmingSecret := range secretConfigDump.DynamicWarmingSecrets { - secret, err := parseDynamicSecret(warmingSecret, "WARMING") - if err != nil { - return nil, fmt.Errorf("failed building warming secret %s: %v", - warmingSecret.Name, err) - } - proxySecretItems = append(proxySecretItems, secret) - } - for _, activeSecret := range secretConfigDump.DynamicActiveSecrets { - secret, err := parseDynamicSecret(activeSecret, "ACTIVE") - if err != nil { - return nil, fmt.Errorf("failed building warming secret %s: %v", - activeSecret.Name, err) - } - if activeSecret.VersionInfo == "uninitialized" { - secret.State = "UNINITIALIZED" - } - proxySecretItems = append(proxySecretItems, secret) - } - return proxySecretItems, nil -} - -func parseDynamicSecret(s *envoy_admin.SecretsConfigDump_DynamicSecret, state string) (SecretItem, error) { - builder := NewSecretItemBuilder() - builder.Name(s.Name).State(state) - - secretTyped := &auth.Secret{} - err := s.GetSecret().UnmarshalTo(secretTyped) - if err != nil { - return SecretItem{}, err - } - - certChainSecret := secretTyped. - GetTlsCertificate(). - GetCertificateChain(). - GetInlineBytes() - caDataSecret := secretTyped. - GetValidationContext(). - GetTrustedCa(). - GetInlineBytes() - - // seems as though the most straightforward way to tell whether this is a root ca or not - // is to check whether the inline bytes of the cert chain or the trusted ca field is zero length - if len(certChainSecret) > 0 { - builder.Data(string(certChainSecret)) - } else if len(caDataSecret) > 0 { - builder.Data(string(caDataSecret)) - } - - secret, err := builder.Build() - if err != nil { - return SecretItem{}, fmt.Errorf("error building secret: %v", err) - } - - return secret, nil -} - -func secretMetaFromCert(rawCert []byte) (SecretMeta, error) { - block, _ := pem.Decode(rawCert) - if block == nil { - return SecretMeta{}, fmt.Errorf("failed to parse certificate PEM") - } - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return SecretMeta{}, err - } - var certType string - if cert.IsCA { - certType = "CA" - } else { - certType = "Cert Chain" - } - - return SecretMeta{ - SerialNumber: fmt.Sprintf("%d", cert.SerialNumber), - NotAfter: cert.NotAfter.Format(time.RFC3339), - NotBefore: cert.NotBefore.Format(time.RFC3339), - Type: certType, - }, nil -} diff --git a/istioctl/pkg/writer/compare/sds/writer.go b/istioctl/pkg/writer/compare/sds/writer.go deleted file mode 100644 index 0ad067226..000000000 --- a/istioctl/pkg/writer/compare/sds/writer.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sdscompare - -import ( - "encoding/json" - "fmt" - "io" - "strings" - "text/tabwriter" -) - -// SDSWriter takes lists of SecretItem or SecretItemDiff and prints them through supplied output writer -type SDSWriter interface { - PrintSecretItems([]SecretItem) error - PrintDiffs([]SecretItemDiff) error -} - -type Format int - -const ( - JSON Format = iota - TABULAR -) - -// NewSDSWriter generates a new instance which conforms to SDSWriter interface -func NewSDSWriter(w io.Writer, format Format) SDSWriter { - return &sdsWriter{ - w: w, - output: format, - } -} - -// sdsWriter is provided concrete implementation of SDSWriter -type sdsWriter struct { - w io.Writer - output Format -} - -// PrintSecretItems uses the user supplied output format to determine how to display the diffed secrets -func (w *sdsWriter) PrintSecretItems(secrets []SecretItem) error { - var err error - switch w.output { - case JSON: - err = w.printSecretItemsJSON(secrets) - case TABULAR: - err = w.printSecretItemsTabular(secrets) - } - return err -} - -var ( - secretItemColumns = []string{"RESOURCE NAME", "TYPE", "STATUS", "VALID CERT", "SERIAL NUMBER", "NOT AFTER", "NOT BEFORE"} - secretDiffColumns = []string{"RESOURCE NAME", "TYPE", "VALID CERT", "NODE AGENT", "PROXY", "SERIAL NUMBER", "NOT AFTER", "NOT BEFORE"} -) - -// printSecretItemsTabular prints the secret in table format -func (w *sdsWriter) printSecretItemsTabular(secrets []SecretItem) error { - if len(secrets) == 0 { - fmt.Fprintln(w.w, "No secret items to show.") - return nil - } - tw := new(tabwriter.Writer).Init(w.w, 0, 5, 5, ' ', 0) - fmt.Fprintln(tw, strings.Join(secretItemColumns, "\t")) - for _, s := range secrets { - fmt.Fprintf(tw, "%s\t%s\t%s\t%t\t%s\t%s\t%s\n", - s.Name, s.Type, s.State, s.Valid, s.SerialNumber, s.NotAfter, s.NotBefore) - } - return tw.Flush() -} - -// printSecretItemsJSON prints secret in JSON format, and dumps the raw certificate data with the output -func (w *sdsWriter) printSecretItemsJSON(secrets []SecretItem) error { - out, err := json.MarshalIndent(secrets, "", " ") - if err != nil { - return err - } - - _, err = w.w.Write(out) - if err != nil { - return err - } - - return nil -} - -// PrintDiffs uses the user supplied output format to determine how to display the diffed secrets -func (w *sdsWriter) PrintDiffs(statuses []SecretItemDiff) error { - var err error - switch w.output { - case JSON: - err = w.printDiffsJSON(statuses) - case TABULAR: - err = w.printDiffsTabular(statuses) - } - return err -} - -// printsDiffsTabular prints the secret in table format -func (w *sdsWriter) printDiffsTabular(statuses []SecretItemDiff) error { - if len(statuses) == 0 { - fmt.Fprintln(w.w, "No secrets found to diff.") - return nil - } - tw := new(tabwriter.Writer).Init(w.w, 0, 5, 5, ' ', 0) - fmt.Fprintln(tw, strings.Join(secretDiffColumns, "\t")) - for _, status := range statuses { - fmt.Fprintf(tw, "%s\t%s\t%t\t%s\t%s\t%s\t%s\t%s\n", - status.Name, status.Type, status.Valid, status.Source, status.Proxy, status.SerialNumber, status.NotAfter, status.NotBefore) - } - return tw.Flush() -} - -// printDiffsJSON prints secret in JSON format, and dumps the raw certificate data with the output -func (w *sdsWriter) printDiffsJSON(statuses []SecretItemDiff) error { - out, err := json.MarshalIndent(statuses, "", " ") - if err != nil { - return err - } - - _, err = w.w.Write(out) - if err != nil { - return err - } - - return nil -} diff --git a/istioctl/pkg/writer/compare/sds/writer_test.go b/istioctl/pkg/writer/compare/sds/writer_test.go deleted file mode 100644 index b94440fc2..000000000 --- a/istioctl/pkg/writer/compare/sds/writer_test.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sdscompare - -import ( - "bytes" - "strings" - "testing" -) - -func TestSDSWriterSecretItems(t *testing.T) { - tests := []struct { - name string - format Format - items []SecretItem - expected []string - unexpected []string - }{ - { - name: "test tabular output with no secret items is equivalent to the header", - format: TABULAR, - items: []SecretItem{}, - expected: []string{}, - unexpected: secretItemColumns, - }, - { - name: "test tabular output with a single secret item", - format: TABULAR, - items: []SecretItem{ - { - Name: "olinger", - Data: "certdata", - Source: "source", - Destination: "destination", - SecretMeta: SecretMeta{ - Valid: true, - SerialNumber: "serial_number", - NotAfter: "expires", - NotBefore: "valid", - Type: "type", - }, - }, - }, - expected: append( - []string{"olinger", "serial_number", "expires", "valid", "type"}, - secretItemColumns...), - unexpected: []string{"source", "destination", "certdata"}, - }, - { - name: "test JSON output with a single secret item", - format: JSON, - items: []SecretItem{ - { - Name: "olinger", - Data: "certdata", - Source: "source", - Destination: "destination", - SecretMeta: SecretMeta{ - Valid: true, - SerialNumber: "serial_number", - NotAfter: "expires", - NotBefore: "valid", - Type: "type", - }, - }, - }, - expected: []string{"olinger", "source", "destination", "serial_number", "expires", "valid", "type", "certdata"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - w := &bytes.Buffer{} - mockWriter := NewSDSWriter(w, tt.format) - err := mockWriter.PrintSecretItems(tt.items) - if err != nil { - t.Errorf("error printing secret items: %v", err) - } - checkOutput(t, w.String(), tt.expected, tt.unexpected) - }) - } -} - -func TestSDSWriterSecretDiff(t *testing.T) { - tests := []struct { - name string - format Format - diffs []SecretItemDiff - expected []string - unexpected []string - }{ - { - name: "test tabular output with no secret items is equivalent to the header", - format: TABULAR, - diffs: []SecretItemDiff{}, - expected: []string{}, - unexpected: secretDiffColumns, - }, - { - name: "test tabular output with a single secret diff", - format: TABULAR, - diffs: []SecretItemDiff{ - { - Agent: "alligator", - Proxy: "proxy", - SecretItem: SecretItem{ - Name: "fields", - Data: "certdata", - Source: "should", - Destination: "destination", - SecretMeta: SecretMeta{ - Valid: true, - SerialNumber: "serial_number", - NotAfter: "expires", - NotBefore: "valid", - Type: "type", - }, - }, - }, - }, - expected: append( - []string{"fields", "should", "serial_number", "expires", "valid", "type", "proxy"}, - secretDiffColumns...), - unexpected: []string{"alligator", "certdata"}, - }, - { - name: "test JSON output with a single secret diff", - format: JSON, - diffs: []SecretItemDiff{ - { - Agent: "alligator", - Proxy: "proxy", - SecretItem: SecretItem{ - Name: "fields", - Data: "certdata", - Source: "should", - Destination: "destination", - SecretMeta: SecretMeta{ - Valid: true, - SerialNumber: "serial_number", - NotAfter: "expires", - NotBefore: "valid", - Type: "type", - }, - }, - }, - }, - expected: []string{"fields", "should", "serial_number", "expires", "valid", "type", "proxy", "certdata"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - w := &bytes.Buffer{} - mockWriter := NewSDSWriter(w, tt.format) - err := mockWriter.PrintDiffs(tt.diffs) - if err != nil { - t.Errorf("error printing secret items: %v", err) - } - checkOutput(t, w.String(), tt.expected, tt.unexpected) - }) - } -} - -func checkOutput(t *testing.T, output string, expected, unexpected []string) { - t.Helper() - for _, expected := range expected { - if !strings.Contains(output, expected) { - t.Errorf("expected %s included in writer output, did not find", expected) - } - } - for _, unexpected := range unexpected { - if strings.Contains(output, unexpected) { - t.Errorf("unexpected string %s included in writer output", unexpected) - } - } -} diff --git a/istioctl/pkg/writer/envoy/clusters/clusters.go b/istioctl/pkg/writer/envoy/clusters/clusters.go deleted file mode 100644 index e4e3056a0..000000000 --- a/istioctl/pkg/writer/envoy/clusters/clusters.go +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package clusters - -import ( - "encoding/json" - "fmt" - "io" - "sort" - "strconv" - "strings" - "text/tabwriter" -) - -import ( - adminapi "github.com/envoyproxy/go-control-plane/envoy/admin/v3" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/clusters" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/proto" -) - -// EndpointFilter is used to pass filter information into route based config writer print functions -type EndpointFilter struct { - Address string - Port uint32 - Cluster string - Status string -} - -// ConfigWriter is a writer for processing responses from the Envoy Admin config_dump endpoint -type ConfigWriter struct { - Stdout io.Writer - clusters *clusters.Wrapper -} - -// EndpointCluster is used to store the endpoint and cluster -type EndpointCluster struct { - address string - port int - cluster string - status core.HealthStatus - failedOutlierCheck bool -} - -// Prime loads the clusters output into the writer ready for printing -func (c *ConfigWriter) Prime(b []byte) error { - cd := clusters.Wrapper{} - err := json.Unmarshal(b, &cd) - if err != nil { - return fmt.Errorf("error unmarshalling config dump response from Envoy: %v", err) - } - c.clusters = &cd - return nil -} - -func retrieveEndpointAddress(host *adminapi.HostStatus) string { - addr := host.Address.GetSocketAddress() - if addr != nil { - return addr.Address - } - return "unix://" + host.Address.GetPipe().Path -} - -func retrieveEndpointPort(l *adminapi.HostStatus) uint32 { - addr := l.Address.GetSocketAddress() - if addr != nil { - return addr.GetPortValue() - } - return 0 -} - -func retrieveEndpointStatus(l *adminapi.HostStatus) core.HealthStatus { - return l.HealthStatus.GetEdsHealthStatus() -} - -func retrieveFailedOutlierCheck(l *adminapi.HostStatus) bool { - return l.HealthStatus.GetFailedOutlierCheck() -} - -// Verify returns true if the passed host matches the filter fields -func (e *EndpointFilter) Verify(host *adminapi.HostStatus, cluster string) bool { - if e.Address == "" && e.Port == 0 && e.Cluster == "" && e.Status == "" { - return true - } - if e.Address != "" && !strings.EqualFold(retrieveEndpointAddress(host), e.Address) { - return false - } - if e.Port != 0 && retrieveEndpointPort(host) != e.Port { - return false - } - if e.Cluster != "" && !strings.EqualFold(cluster, e.Cluster) { - return false - } - status := retrieveEndpointStatus(host) - if e.Status != "" && !strings.EqualFold(core.HealthStatus_name[int32(status)], e.Status) { - return false - } - return true -} - -// PrintEndpointsSummary prints just the endpoints config summary to the ConfigWriter stdout -func (c *ConfigWriter) PrintEndpointsSummary(filter EndpointFilter) error { - if c.clusters == nil { - return fmt.Errorf("config writer has not been primed") - } - - w := new(tabwriter.Writer).Init(c.Stdout, 0, 8, 5, ' ', 0) - - clusterEndpoint := make([]EndpointCluster, 0) - for _, cluster := range c.clusters.ClusterStatuses { - for _, host := range cluster.HostStatuses { - if filter.Verify(host, cluster.Name) { - addr := retrieveEndpointAddress(host) - port := retrieveEndpointPort(host) - status := retrieveEndpointStatus(host) - outlierCheck := retrieveFailedOutlierCheck(host) - clusterEndpoint = append(clusterEndpoint, EndpointCluster{addr, int(port), cluster.Name, status, outlierCheck}) - } - } - } - - clusterEndpoint = retrieveSortedEndpointClusterSlice(clusterEndpoint) - fmt.Fprintln(w, "ENDPOINT\tSTATUS\tOUTLIER CHECK\tCLUSTER") - for _, ce := range clusterEndpoint { - var endpoint string - if ce.port != 0 { - endpoint = ce.address + ":" + strconv.Itoa(ce.port) - } else { - endpoint = ce.address - } - fmt.Fprintf(w, "%v\t%v\t%v\t%v\n", endpoint, core.HealthStatus_name[int32(ce.status)], printFailedOutlierCheck(ce.failedOutlierCheck), ce.cluster) - } - - return w.Flush() -} - -// PrintEndpoints prints the endpoints config to the ConfigWriter stdout -func (c *ConfigWriter) PrintEndpoints(filter EndpointFilter, outputFormat string) error { - if c.clusters == nil { - return fmt.Errorf("config writer has not been primed") - } - - filteredClusters := proto.MessageSlice{} - for _, cluster := range c.clusters.ClusterStatuses { - for _, host := range cluster.HostStatuses { - if filter.Verify(host, cluster.Name) { - filteredClusters = append(filteredClusters, cluster) - break - } - } - } - out, err := json.MarshalIndent(filteredClusters, "", " ") - if err != nil { - return err - } - if outputFormat == "yaml" { - if out, err = yaml.JSONToYAML(out); err != nil { - return err - } - } - fmt.Fprintln(c.Stdout, string(out)) - return nil -} - -func retrieveSortedEndpointClusterSlice(ec []EndpointCluster) []EndpointCluster { - sort.Slice(ec, func(i, j int) bool { - if ec[i].address == ec[j].address { - if ec[i].port == ec[j].port { - return ec[i].cluster < ec[j].cluster - } - return ec[i].port < ec[j].port - } - return ec[i].address < ec[j].address - }) - return ec -} - -func printFailedOutlierCheck(b bool) string { - if b { - return "FAILED" - } - return "OK" -} diff --git a/istioctl/pkg/writer/envoy/configdump/cluster.go b/istioctl/pkg/writer/envoy/configdump/cluster.go deleted file mode 100644 index 2da5fc23b..000000000 --- a/istioctl/pkg/writer/envoy/configdump/cluster.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configdump - -import ( - "encoding/json" - "fmt" - "sort" - "strings" - "text/tabwriter" -) - -import ( - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - "sigs.k8s.io/yaml" -) - -import ( - protio "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/proto" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" -) - -// ClusterFilter is used to pass filter information into cluster based config writer print functions -type ClusterFilter struct { - FQDN host.Name - Port int - Subset string - Direction model.TrafficDirection -} - -// Verify returns true if the passed cluster matches the filter fields -func (c *ClusterFilter) Verify(cluster *cluster.Cluster) bool { - name := cluster.Name - if c.FQDN == "" && c.Port == 0 && c.Subset == "" && c.Direction == "" { - return true - } - if c.FQDN != "" && !strings.Contains(name, string(c.FQDN)) { - return false - } - if c.Direction != "" && !strings.Contains(name, string(c.Direction)) { - return false - } - if c.Subset != "" && !strings.Contains(name, c.Subset) { - return false - } - if c.Port != 0 { - p := fmt.Sprintf("|%v|", c.Port) - if !strings.Contains(name, p) { - return false - } - } - return true -} - -// PrintClusterSummary prints a summary of the relevant clusters in the config dump to the ConfigWriter stdout -func (c *ConfigWriter) PrintClusterSummary(filter ClusterFilter) error { - w, clusters, err := c.setupClusterConfigWriter() - if err != nil { - return err - } - _, _ = fmt.Fprintln(w, "SERVICE FQDN\tPORT\tSUBSET\tDIRECTION\tTYPE\tDESTINATION RULE") - for _, c := range clusters { - if filter.Verify(c) { - if len(strings.Split(c.Name, "|")) > 3 { - direction, subset, fqdn, port := model.ParseSubsetKey(c.Name) - if subset == "" { - subset = "-" - } - _, _ = fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%s\t%s\n", fqdn, port, subset, direction, c.GetType(), - describeManagement(c.GetMetadata())) - } else { - _, _ = fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%s\t%s\n", c.Name, "-", "-", "-", c.GetType(), - describeManagement(c.GetMetadata())) - } - } - } - return w.Flush() -} - -// PrintClusterDump prints the relevant clusters in the config dump to the ConfigWriter stdout -func (c *ConfigWriter) PrintClusterDump(filter ClusterFilter, outputFormat string) error { - _, clusters, err := c.setupClusterConfigWriter() - if err != nil { - return err - } - filteredClusters := make(protio.MessageSlice, 0, len(clusters)) - for _, cluster := range clusters { - if filter.Verify(cluster) { - filteredClusters = append(filteredClusters, cluster) - } - } - out, err := json.MarshalIndent(filteredClusters, "", " ") - if err != nil { - return err - } - if outputFormat == "yaml" { - if out, err = yaml.JSONToYAML(out); err != nil { - return err - } - } - _, _ = fmt.Fprintln(c.Stdout, string(out)) - return nil -} - -func (c *ConfigWriter) setupClusterConfigWriter() (*tabwriter.Writer, []*cluster.Cluster, error) { - clusters, err := c.retrieveSortedClusterSlice() - if err != nil { - return nil, nil, err - } - w := new(tabwriter.Writer).Init(c.Stdout, 0, 8, 5, ' ', 0) - return w, clusters, nil -} - -func (c *ConfigWriter) retrieveSortedClusterSlice() ([]*cluster.Cluster, error) { - if c.configDump == nil { - return nil, fmt.Errorf("config writer has not been primed") - } - clusterDump, err := c.configDump.GetClusterConfigDump() - if err != nil { - return nil, err - } - clusters := make([]*cluster.Cluster, 0) - for _, c := range clusterDump.DynamicActiveClusters { - if c.Cluster != nil { - clusterTyped := &cluster.Cluster{} - // Support v2 or v3 in config dump. See ads.go:RequestedTypes for more info. - c.Cluster.TypeUrl = v3.ClusterType - err = c.Cluster.UnmarshalTo(clusterTyped) - if err != nil { - return nil, err - } - clusters = append(clusters, clusterTyped) - } - } - for _, c := range clusterDump.StaticClusters { - if c.Cluster != nil { - clusterTyped := &cluster.Cluster{} - // Support v2 or v3 in config dump. See ads.go:RequestedTypes for more info. - c.Cluster.TypeUrl = v3.ClusterType - err = c.Cluster.UnmarshalTo(clusterTyped) - if err != nil { - return nil, err - } - clusters = append(clusters, clusterTyped) - } - } - if len(clusters) == 0 { - return nil, fmt.Errorf("no clusters found") - } - sort.Slice(clusters, func(i, j int) bool { - iDirection, iSubset, iName, iPort := safelyParseSubsetKey(clusters[i].Name) - jDirection, jSubset, jName, jPort := safelyParseSubsetKey(clusters[j].Name) - if iName == jName { - if iSubset == jSubset { - if iPort == jPort { - return iDirection < jDirection - } - return iPort < jPort - } - return iSubset < jSubset - } - return iName < jName - }) - return clusters, nil -} - -func safelyParseSubsetKey(key string) (model.TrafficDirection, string, host.Name, int) { - if len(strings.Split(key, "|")) > 3 { - return model.ParseSubsetKey(key) - } - name := host.Name(key) - return "", "", name, 0 -} diff --git a/istioctl/pkg/writer/envoy/configdump/cluster_test.go b/istioctl/pkg/writer/envoy/configdump/cluster_test.go deleted file mode 100644 index bf7e92006..000000000 --- a/istioctl/pkg/writer/envoy/configdump/cluster_test.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configdump diff --git a/istioctl/pkg/writer/envoy/configdump/configdump.go b/istioctl/pkg/writer/envoy/configdump/configdump.go deleted file mode 100644 index 8e9dd84f6..000000000 --- a/istioctl/pkg/writer/envoy/configdump/configdump.go +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configdump - -import ( - "encoding/json" - "fmt" - "io" - "strings" - "text/tabwriter" -) - -import ( - envoy_admin_v3 "github.com/envoyproxy/go-control-plane/envoy/admin/v3" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/configdump" - sdscompare "github.com/apache/dubbo-go-pixiu/istioctl/pkg/writer/compare/sds" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -// ConfigWriter is a writer for processing responses from the Envoy Admin config_dump endpoint -type ConfigWriter struct { - Stdout io.Writer - configDump *configdump.Wrapper -} - -// Prime loads the config dump into the writer ready for printing -func (c *ConfigWriter) Prime(b []byte) error { - cd := configdump.Wrapper{} - // TODO(fisherxu): migrate this to jsonpb when issue fixed in golang - // Issue to track -> https://github.com/golang/protobuf/issues/632 - err := json.Unmarshal(b, &cd) - if err != nil { - return fmt.Errorf("error unmarshalling config dump response from Envoy: %v", err) - } - c.configDump = &cd - return nil -} - -// PrintBootstrapDump prints just the bootstrap config dump to the ConfigWriter stdout -func (c *ConfigWriter) PrintBootstrapDump(outputFormat string) error { - if c.configDump == nil { - return fmt.Errorf("config writer has not been primed") - } - bootstrapDump, err := c.configDump.GetBootstrapConfigDump() - if err != nil { - return err - } - out, err := protomarshal.ToJSONWithIndent(bootstrapDump, " ") - if err != nil { - return fmt.Errorf("unable to marshal bootstrap in Envoy config dump") - } - if outputFormat == "yaml" { - outbyte, err := yaml.JSONToYAML([]byte(out)) - if err != nil { - return err - } - out = string(outbyte) - } - fmt.Fprintln(c.Stdout, out) - return nil -} - -// PrintSecretDump prints just the secret config dump to the ConfigWriter stdout -func (c *ConfigWriter) PrintSecretDump(outputFormat string) error { - if c.configDump == nil { - return fmt.Errorf("config writer has not been primed") - } - secretDump, err := c.configDump.GetSecretConfigDump() - if err != nil { - return fmt.Errorf("sidecar doesn't support secrets: %v", err) - } - out, err := protomarshal.ToJSONWithIndent(secretDump, " ") - if err != nil { - return fmt.Errorf("unable to marshal secrets in Envoy config dump") - } - if outputFormat == "yaml" { - outbyte, err := yaml.JSONToYAML([]byte(out)) - if err != nil { - return err - } - out = string(outbyte) - } - fmt.Fprintln(c.Stdout, out) - return nil -} - -// PrintSecretSummary prints a summary of dynamic active secrets from the config dump -func (c *ConfigWriter) PrintSecretSummary() error { - secretDump, err := c.configDump.GetSecretConfigDump() - if err != nil { - return err - } - if len(secretDump.DynamicActiveSecrets) == 0 && - len(secretDump.DynamicWarmingSecrets) == 0 { - fmt.Fprintln(c.Stdout, "No active or warming secrets found.") - return nil - } - secretItems, err := sdscompare.GetEnvoySecrets(c.configDump) - if err != nil { - return err - } - - secretWriter := sdscompare.NewSDSWriter(c.Stdout, sdscompare.TABULAR) - return secretWriter.PrintSecretItems(secretItems) -} - -func (c *ConfigWriter) PrintFullSummary(cf ClusterFilter, lf ListenerFilter, rf RouteFilter) error { - if err := c.PrintClusterSummary(cf); err != nil { - return err - } - _, _ = c.Stdout.Write([]byte("\n")) - if err := c.PrintListenerSummary(lf); err != nil { - return err - } - _, _ = c.Stdout.Write([]byte("\n")) - if err := c.PrintRouteSummary(rf); err != nil { - return err - } - _, _ = c.Stdout.Write([]byte("\n")) - if err := c.PrintSecretSummary(); err != nil { - return err - } - return nil -} - -// PrintVersionSummary prints version information for Istio and Envoy from the config dump -func (c *ConfigWriter) PrintVersionSummary() error { - if c.configDump == nil { - return fmt.Errorf("config writer has not been primed") - } - - bootstrapDump, err := c.configDump.GetBootstrapConfigDump() - if err != nil { - return err - } - - var ( - istioVersion, istioProxySha = c.getIstioVersionInfo(bootstrapDump) - envoyVersion = c.getUserAgentVersionInfo(bootstrapDump) - - tw = tabwriter.NewWriter(c.Stdout, 0, 8, 1, ' ', 0) - ) - - if len(istioVersion) > 0 { - fmt.Fprintf(tw, "Istio Version:\t%s\n", istioVersion) - } - if len(istioProxySha) > 0 { - fmt.Fprintf(tw, "Istio Proxy Version:\t%s\n", istioProxySha) - } - if len(envoyVersion) > 0 { - fmt.Fprintf(tw, "Envoy Version:\t%s\n", envoyVersion) - } - - return tw.Flush() -} - -// PrintPodRootCAFromDynamicSecretDump prints just pod's root ca from dynamic secret config dump to the ConfigWriter stdout -func (c *ConfigWriter) PrintPodRootCAFromDynamicSecretDump() (string, error) { - if c.configDump == nil { - return "", fmt.Errorf("config writer has not been primed") - } - secretDump, err := c.configDump.GetSecretConfigDump() - if err != nil { - return "", fmt.Errorf("sidecar doesn't support secrets: %v", err) - } - for _, secret := range secretDump.DynamicActiveSecrets { - // check the ROOTCA from secret dump - if secret.Name == "ROOTCA" { - var returnStr string - var returnErr error - strCA, err := c.configDump.GetRootCAFromSecretConfigDump(secret.GetSecret()) - if err != nil { - returnStr = "" - returnErr = fmt.Errorf("can not dump ROOTCA from secret: %v", err) - } else { - returnStr = strCA - returnErr = nil - } - return returnStr, returnErr - } - } - return "", fmt.Errorf("can not find ROOTCA from secret") -} - -func (c *ConfigWriter) getIstioVersionInfo(bootstrapDump *envoy_admin_v3.BootstrapConfigDump) (version, sha string) { - const ( - istioVersionKey = "ISTIO_VERSION" - istioProxyShaKey = "ISTIO_PROXY_SHA" - ) - - md := bootstrapDump.GetBootstrap().GetNode().GetMetadata().GetFields() - - if versionPB, ok := md[istioVersionKey]; ok { - version = versionPB.GetStringValue() - } - if shaPB, ok := md[istioProxyShaKey]; ok { - sha = shaPB.GetStringValue() - if shaParts := strings.Split(sha, ":"); len(shaParts) > 1 { - sha = shaParts[1] - } - } - - return -} - -func (c *ConfigWriter) getUserAgentVersionInfo(bootstrapDump *envoy_admin_v3.BootstrapConfigDump) string { - const ( - buildLabelKey = "build.label" - buildTypeKey = "build.type" - statusKey = "revision.status" - sslVersionKey = "ssl.version" - ) - - var ( - buildVersion = bootstrapDump.GetBootstrap().GetNode().GetUserAgentBuildVersion() - version = buildVersion.GetVersion() - md = buildVersion.GetMetadata().GetFields() - - sb strings.Builder - ) - - fmt.Fprintf(&sb, "%d.%d.%d", version.GetMajorNumber(), version.GetMinorNumber(), version.GetPatch()) - if label, ok := md[buildLabelKey]; ok { - fmt.Fprintf(&sb, "-%s", label.GetStringValue()) - } - if status, ok := md[statusKey]; ok { - fmt.Fprintf(&sb, "/%s", status.GetStringValue()) - } - if typ, ok := md[buildTypeKey]; ok { - fmt.Fprintf(&sb, "/%s", typ.GetStringValue()) - } - if sslVersion, ok := md[sslVersionKey]; ok { - fmt.Fprintf(&sb, "/%s", sslVersion.GetStringValue()) - } - - return sb.String() -} diff --git a/istioctl/pkg/writer/envoy/configdump/configdump_test.go b/istioctl/pkg/writer/envoy/configdump/configdump_test.go deleted file mode 100644 index d6539fb21..000000000 --- a/istioctl/pkg/writer/envoy/configdump/configdump_test.go +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configdump - -import ( - "bytes" - "os" - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/test/util" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -func TestConfigWriter_Prime(t *testing.T) { - tests := []struct { - name string - wantConfigs int - inputFile string - wantErr bool - }{ - { - name: "errors if unable to unmarshal bytes", - inputFile: "", - wantConfigs: 0, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - cw := &ConfigWriter{} - cd, _ := os.ReadFile(tt.inputFile) - err := cw.Prime(cd) - if cw.configDump == nil { - if tt.wantConfigs != 0 { - t.Errorf("wanted some configs loaded but config dump was nil") - } - } else if len(cw.configDump.Configs) != tt.wantConfigs { - t.Errorf("wanted %v configs loaded in got %v", tt.wantConfigs, len(cw.configDump.Configs)) - } - if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - }) - } -} - -func TestConfigWriter_PrintBootstrapDump(t *testing.T) { - tests := []struct { - name string - wantOutputFile string - callPrime bool - wantErr bool - }{ - // TODO: Turn on when protobuf bug is resolved - https://github.com/golang/protobuf/issues/632 - // { - // name: "returns expected bootstrap dump from Envoy onto Stdout", - // callPrime: true, - // wantOutputFile: "testdata/bootstrapdump.json", - // }, - { - name: "errors if config dump is not primed", - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotOut := &bytes.Buffer{} - cw := &ConfigWriter{Stdout: gotOut} - cd, _ := os.ReadFile("testdata/configdump.json") - if tt.callPrime { - cw.Prime(cd) - } - err := cw.PrintBootstrapDump("json") - if tt.wantOutputFile != "" { - util.CompareContent(t, gotOut.Bytes(), tt.wantOutputFile) - } - if err == nil && tt.wantErr { - t.Errorf("PrintBootstrapDump (%v) did not produce expected err", tt.name) - } else if err != nil && !tt.wantErr { - t.Errorf("PrintBootstrapDump (%v) produced unexpected err: %v", tt.name, err) - } - }) - } -} - -func TestConfigWriter_PrintVersionSummary(t *testing.T) { - tests := []struct { - name string - wantOutputFile string - callPrime bool - wantErr bool - }{ - { - name: "returns expected version summary onto Stdout", - callPrime: true, - wantOutputFile: "testdata/versionsummary.txt", - }, - { - name: "errors if config dump is not primed", - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotOut := &bytes.Buffer{} - cw := &ConfigWriter{Stdout: gotOut} - cd, _ := os.ReadFile("testdata/configdump.json") - if tt.callPrime { - cw.Prime(cd) - } - err := cw.PrintVersionSummary() - if tt.wantOutputFile != "" { - util.CompareContent(t, gotOut.Bytes(), tt.wantOutputFile) - } - if err == nil && tt.wantErr { - t.Errorf("PrintVersionSummary (%v) did not produce expected err", tt.name) - } else if err != nil && !tt.wantErr { - t.Errorf("PrintVersionSummary (%v) produced unexpected err: %v", tt.name, err) - } - }) - } -} diff --git a/istioctl/pkg/writer/envoy/configdump/listener.go b/istioctl/pkg/writer/envoy/configdump/listener.go deleted file mode 100644 index 2c61fc974..000000000 --- a/istioctl/pkg/writer/envoy/configdump/listener.go +++ /dev/null @@ -1,443 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configdump - -import ( - "encoding/json" - "fmt" - "reflect" - "sort" - "strings" - "text/tabwriter" -) - -import ( - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - httpConn "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - tcp "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "sigs.k8s.io/yaml" -) - -import ( - protio "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/proto" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" -) - -const ( - // HTTPListener identifies a listener as being of HTTP type by the presence of an HTTP connection manager filter - HTTPListener = wellknown.HTTPConnectionManager - - // TCPListener identifies a listener as being of TCP type by the presence of TCP proxy filter - TCPListener = wellknown.TCPProxy -) - -// ListenerFilter is used to pass filter information into listener based config writer print functions -type ListenerFilter struct { - Address string - Port uint32 - Type string - Verbose bool -} - -// Verify returns true if the passed listener matches the filter fields -func (l *ListenerFilter) Verify(listener *listener.Listener) bool { - if l.Address == "" && l.Port == 0 && l.Type == "" { - return true - } - if l.Address != "" && !strings.EqualFold(retrieveListenerAddress(listener), l.Address) { - return false - } - if l.Port != 0 && retrieveListenerPort(listener) != l.Port { - return false - } - if l.Type != "" && !strings.EqualFold(retrieveListenerType(listener), l.Type) { - return false - } - return true -} - -func getFilterChains(l *listener.Listener) []*listener.FilterChain { - res := l.FilterChains - if l.DefaultFilterChain != nil { - res = append(res, l.DefaultFilterChain) - } - return res -} - -// retrieveListenerType classifies a Listener as HTTP|TCP|HTTP+TCP|UNKNOWN -func retrieveListenerType(l *listener.Listener) string { - nHTTP := 0 - nTCP := 0 - for _, filterChain := range getFilterChains(l) { - for _, filter := range filterChain.GetFilters() { - if filter.Name == HTTPListener { - nHTTP++ - } else if filter.Name == TCPListener { - if !strings.Contains(string(filter.GetTypedConfig().GetValue()), util.BlackHoleCluster) { - nTCP++ - } - } - } - } - - if nHTTP > 0 { - if nTCP == 0 { - return "HTTP" - } - return "HTTP+TCP" - } else if nTCP > 0 { - return "TCP" - } - - return "UNKNOWN" -} - -func retrieveListenerAddress(l *listener.Listener) string { - sockAddr := l.Address.GetSocketAddress() - if sockAddr != nil { - return sockAddr.Address - } - - pipe := l.Address.GetPipe() - if pipe != nil { - return pipe.Path - } - - return "" -} - -func retrieveListenerPort(l *listener.Listener) uint32 { - return l.Address.GetSocketAddress().GetPortValue() -} - -// PrintListenerSummary prints a summary of the relevant listeners in the config dump to the ConfigWriter stdout -func (c *ConfigWriter) PrintListenerSummary(filter ListenerFilter) error { - w, listeners, err := c.setupListenerConfigWriter() - if err != nil { - return err - } - - verifiedListeners := make([]*listener.Listener, 0, len(listeners)) - for _, l := range listeners { - if filter.Verify(l) { - verifiedListeners = append(verifiedListeners, l) - } - } - - // Sort by port, addr, type - sort.Slice(verifiedListeners, func(i, j int) bool { - iPort := retrieveListenerPort(verifiedListeners[i]) - jPort := retrieveListenerPort(verifiedListeners[j]) - if iPort != jPort { - return iPort < jPort - } - iAddr := retrieveListenerAddress(verifiedListeners[i]) - jAddr := retrieveListenerAddress(verifiedListeners[j]) - if iAddr != jAddr { - return iAddr < jAddr - } - iType := retrieveListenerType(verifiedListeners[i]) - jType := retrieveListenerType(verifiedListeners[j]) - return iType < jType - }) - - if filter.Verbose { - fmt.Fprintln(w, "ADDRESS\tPORT\tMATCH\tDESTINATION") - } else { - fmt.Fprintln(w, "ADDRESS\tPORT\tTYPE") - } - for _, l := range verifiedListeners { - address := retrieveListenerAddress(l) - port := retrieveListenerPort(l) - if filter.Verbose { - - matches := retrieveListenerMatches(l) - sort.Slice(matches, func(i, j int) bool { - return matches[i].destination > matches[j].destination - }) - for _, match := range matches { - fmt.Fprintf(w, "%v\t%v\t%v\t%v\n", address, port, match.match, match.destination) - } - } else { - listenerType := retrieveListenerType(l) - fmt.Fprintf(w, "%v\t%v\t%v\n", address, port, listenerType) - } - } - return w.Flush() -} - -type filterchain struct { - match string - destination string -} - -var ( - plaintextHTTPALPNs = []string{"http/1.0", "http/1.1", "h2c"} - istioHTTPPlaintext = []string{"istio", "istio-http/1.0", "istio-http/1.1", "istio-h2"} - httpTLS = []string{"http/1.0", "http/1.1", "h2c", "istio-http/1.0", "istio-http/1.1", "istio-h2"} - tcpTLS = []string{"istio-peer-exchange", "istio"} - - protDescrs = map[string][]string{ - "App: HTTP TLS": httpTLS, - "App: Istio HTTP Plain": istioHTTPPlaintext, - "App: TCP TLS": tcpTLS, - "App: HTTP": plaintextHTTPALPNs, - } -) - -func retrieveListenerMatches(l *listener.Listener) []filterchain { - fChains := getFilterChains(l) - resp := make([]filterchain, 0, len(fChains)) - for _, filterChain := range fChains { - match := filterChain.FilterChainMatch - if match == nil { - match = &listener.FilterChainMatch{} - } - // filterChaince also has SuffixLen, SourceType, SourcePrefixRanges which are not rendered. - - descrs := []string{} - if len(match.ServerNames) > 0 { - descrs = append(descrs, fmt.Sprintf("SNI: %s", strings.Join(match.ServerNames, ","))) - } - if len(match.TransportProtocol) > 0 { - descrs = append(descrs, fmt.Sprintf("Trans: %s", match.TransportProtocol)) - } - - if len(match.ApplicationProtocols) > 0 { - found := false - for protDescr, protocols := range protDescrs { - if reflect.DeepEqual(match.ApplicationProtocols, protocols) { - found = true - descrs = append(descrs, protDescr) - break - } - } - if !found { - descrs = append(descrs, fmt.Sprintf("App: %s", strings.Join(match.ApplicationProtocols, ","))) - } - } - - port := "" - if match.DestinationPort != nil { - port = fmt.Sprintf(":%d", match.DestinationPort.GetValue()) - } - if len(match.PrefixRanges) > 0 { - pf := []string{} - for _, p := range match.PrefixRanges { - pf = append(pf, fmt.Sprintf("%s/%d", p.AddressPrefix, p.GetPrefixLen().GetValue())) - } - descrs = append(descrs, fmt.Sprintf("Addr: %s%s", strings.Join(pf, ","), port)) - } else if port != "" { - descrs = append(descrs, fmt.Sprintf("Addr: *%s", port)) - } - if len(descrs) == 0 { - descrs = []string{"ALL"} - } - fc := filterchain{ - destination: getFilterType(filterChain.GetFilters()), - match: strings.Join(descrs, "; "), - } - resp = append(resp, fc) - } - return resp -} - -func getFilterType(filters []*listener.Filter) string { - for _, filter := range filters { - if filter.Name == HTTPListener { - httpProxy := &httpConn.HttpConnectionManager{} - // Allow Unmarshal to work even if Envoy and istioctl are different - filter.GetTypedConfig().TypeUrl = "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager" - err := filter.GetTypedConfig().UnmarshalTo(httpProxy) - if err != nil { - return err.Error() - } - if httpProxy.GetRouteConfig() != nil { - return describeRouteConfig(httpProxy.GetRouteConfig()) - } - if httpProxy.GetRds().GetRouteConfigName() != "" { - return fmt.Sprintf("Route: %s", httpProxy.GetRds().GetRouteConfigName()) - } - return "HTTP" - } else if filter.Name == TCPListener { - if !strings.Contains(string(filter.GetTypedConfig().GetValue()), util.BlackHoleCluster) { - tcpProxy := &tcp.TcpProxy{} - // Allow Unmarshal to work even if Envoy and istioctl are different - filter.GetTypedConfig().TypeUrl = "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy" - err := filter.GetTypedConfig().UnmarshalTo(tcpProxy) - if err != nil { - return err.Error() - } - if strings.Contains(tcpProxy.GetCluster(), "Cluster") { - return tcpProxy.GetCluster() - } - return fmt.Sprintf("Cluster: %s", tcpProxy.GetCluster()) - } - } - } - return "Non-HTTP/Non-TCP" -} - -func describeRouteConfig(route *route.RouteConfiguration) string { - if cluster := getMatchAllCluster(route); cluster != "" { - return cluster - } - vhosts := []string{} - for _, vh := range route.GetVirtualHosts() { - if describeDomains(vh) == "" { - vhosts = append(vhosts, describeRoutes(vh)) - } else { - vhosts = append(vhosts, fmt.Sprintf("%s %s", describeDomains(vh), describeRoutes(vh))) - } - } - return fmt.Sprintf("Inline Route: %s", strings.Join(vhosts, "; ")) -} - -// If this is a route that matches everything and forwards to a cluster, just report the cluster. -func getMatchAllCluster(er *route.RouteConfiguration) string { - if len(er.GetVirtualHosts()) != 1 { - return "" - } - vh := er.GetVirtualHosts()[0] - if !reflect.DeepEqual(vh.Domains, []string{"*"}) { - return "" - } - if len(vh.GetRoutes()) != 1 { - return "" - } - r := vh.GetRoutes()[0] - if r.GetMatch().GetPrefix() != "/" { - return "" - } - a, ok := r.GetAction().(*route.Route_Route) - if !ok { - return "" - } - cl, ok := a.Route.ClusterSpecifier.(*route.RouteAction_Cluster) - if !ok { - return "" - } - if strings.Contains(cl.Cluster, "Cluster") { - return cl.Cluster - } - return fmt.Sprintf("Cluster: %s", cl.Cluster) -} - -func describeDomains(vh *route.VirtualHost) string { - if len(vh.GetDomains()) == 1 && vh.GetDomains()[0] == "*" { - return "" - } - return strings.Join(vh.GetDomains(), "/") -} - -func describeRoutes(vh *route.VirtualHost) string { - routes := make([]string, 0, len(vh.GetRoutes())) - for _, route := range vh.GetRoutes() { - routes = append(routes, describeMatch(route.GetMatch())) - } - return strings.Join(routes, ", ") -} - -func describeMatch(match *route.RouteMatch) string { - conds := []string{} - if match.GetPrefix() != "" { - conds = append(conds, fmt.Sprintf("%s*", match.GetPrefix())) - } - if match.GetPath() != "" { - conds = append(conds, match.GetPath()) - } - if match.GetSafeRegex() != nil { - conds = append(conds, fmt.Sprintf("regex %s", match.GetSafeRegex().Regex)) - } - // Ignore headers - return strings.Join(conds, " ") -} - -// PrintListenerDump prints the relevant listeners in the config dump to the ConfigWriter stdout -func (c *ConfigWriter) PrintListenerDump(filter ListenerFilter, outputFormat string) error { - _, listeners, err := c.setupListenerConfigWriter() - if err != nil { - return err - } - filteredListeners := protio.MessageSlice{} - for _, listener := range listeners { - if filter.Verify(listener) { - filteredListeners = append(filteredListeners, listener) - } - } - out, err := json.MarshalIndent(filteredListeners, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal listeners: %v", err) - } - if outputFormat == "yaml" { - if out, err = yaml.JSONToYAML(out); err != nil { - return err - } - } - fmt.Fprintln(c.Stdout, string(out)) - return nil -} - -func (c *ConfigWriter) setupListenerConfigWriter() (*tabwriter.Writer, []*listener.Listener, error) { - listeners, err := c.retrieveSortedListenerSlice() - if err != nil { - return nil, nil, err - } - w := new(tabwriter.Writer).Init(c.Stdout, 0, 8, 1, ' ', 0) - return w, listeners, nil -} - -func (c *ConfigWriter) retrieveSortedListenerSlice() ([]*listener.Listener, error) { - if c.configDump == nil { - return nil, fmt.Errorf("config writer has not been primed") - } - listenerDump, err := c.configDump.GetListenerConfigDump() - if err != nil { - return nil, fmt.Errorf("listener dump: %v", err) - } - listeners := make([]*listener.Listener, 0) - for _, l := range listenerDump.DynamicListeners { - if l.ActiveState != nil && l.ActiveState.Listener != nil { - listenerTyped := &listener.Listener{} - // Support v2 or v3 in config dump. See ads.go:RequestedTypes for more info. - l.ActiveState.Listener.TypeUrl = v3.ListenerType - err = l.ActiveState.Listener.UnmarshalTo(listenerTyped) - if err != nil { - return nil, fmt.Errorf("unmarshal listener: %v", err) - } - listeners = append(listeners, listenerTyped) - } - } - - for _, l := range listenerDump.StaticListeners { - if l.Listener != nil { - listenerTyped := &listener.Listener{} - // Support v2 or v3 in config dump. See ads.go:RequestedTypes for more info. - l.Listener.TypeUrl = v3.ListenerType - err = l.Listener.UnmarshalTo(listenerTyped) - if err != nil { - return nil, fmt.Errorf("unmarshal listener: %v", err) - } - listeners = append(listeners, listenerTyped) - } - } - if len(listeners) == 0 { - return nil, fmt.Errorf("no listeners found") - } - return listeners, nil -} diff --git a/istioctl/pkg/writer/envoy/configdump/listener_test.go b/istioctl/pkg/writer/envoy/configdump/listener_test.go deleted file mode 100644 index 88277890f..000000000 --- a/istioctl/pkg/writer/envoy/configdump/listener_test.go +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2020 Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configdump - -import ( - "testing" -) - -import ( - v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" -) - -func TestListenerFilter_Verify(t *testing.T) { - tests := []struct { - desc string - inFilter *ListenerFilter - inListener *listener.Listener - expect bool - }{ - { - desc: "filter-fields-empty", - inFilter: &ListenerFilter{ - Address: "", - Port: 0, - Type: "", - }, - inListener: &listener.Listener{}, - expect: true, - }, - { - desc: "addrs-dont-match", - inFilter: &ListenerFilter{ - Address: "0.0.0.0", - }, - inListener: &listener.Listener{ - Address: &v3.Address{ - Address: &v3.Address_SocketAddress{ - SocketAddress: &v3.SocketAddress{Address: "1.1.1.1"}, - }, - }, - }, - expect: false, - }, - { - desc: "ports-dont-match", - inFilter: &ListenerFilter{ - Port: 10, - }, - inListener: &listener.Listener{ - Address: &v3.Address{ - Address: &v3.Address_SocketAddress{ - SocketAddress: &v3.SocketAddress{ - PortSpecifier: &v3.SocketAddress_PortValue{ - PortValue: 11, - }, - }, - }, - }, - }, - expect: false, - }, - { - desc: "http-type-match", - inFilter: &ListenerFilter{ - Type: "HTTP", - }, - inListener: &listener.Listener{ - FilterChains: []*listener.FilterChain{ - { - Filters: []*listener.Filter{ - { - Name: wellknown.HTTPConnectionManager, - }, - }, - }, - }, - }, - expect: true, - }, - { - desc: "http-tcp-type-match", - inFilter: &ListenerFilter{ - Type: "HTTP+TCP", - }, - inListener: &listener.Listener{ - FilterChains: []*listener.FilterChain{{ - Filters: []*listener.Filter{ - { - Name: wellknown.TCPProxy, - }, - { - Name: wellknown.TCPProxy, - }, - { - Name: wellknown.HTTPConnectionManager, - }, - }, - }}, - }, - expect: true, - }, - { - desc: "tcp-type-match", - inFilter: &ListenerFilter{ - Type: "TCP", - }, - inListener: &listener.Listener{ - FilterChains: []*listener.FilterChain{{ - Filters: []*listener.Filter{{ - Name: wellknown.TCPProxy, - }}, - }}, - }, - expect: true, - }, - { - desc: "unknown-type", - inFilter: &ListenerFilter{ - Type: "UNKNOWN", - }, - inListener: &listener.Listener{ - FilterChains: []*listener.FilterChain{{ - Filters: []*listener.Filter{}, - }}, - }, - expect: true, - }, - { - desc: "listener-pipe", - inFilter: &ListenerFilter{ - Address: "", - Port: 0, - Type: "", - }, - inListener: &listener.Listener{ - Address: &v3.Address{ - Address: &v3.Address_Pipe{ - Pipe: &v3.Pipe{Path: "unix:///dev/shm/uds.socket"}, - }, - }, - }, - expect: true, - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got := tt.inFilter.Verify(tt.inListener); got != tt.expect { - t.Errorf("%s: expect %v got %v", tt.desc, tt.expect, got) - } - }) - } -} diff --git a/istioctl/pkg/writer/envoy/configdump/route.go b/istioctl/pkg/writer/envoy/configdump/route.go deleted file mode 100644 index adc891da9..000000000 --- a/istioctl/pkg/writer/envoy/configdump/route.go +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configdump - -import ( - "encoding/json" - "fmt" - "sort" - "strconv" - "strings" - "text/tabwriter" -) - -import ( - envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - "sigs.k8s.io/yaml" -) - -import ( - protio "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/proto" - pilot_util "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// RouteFilter is used to pass filter information into route based config writer print functions -type RouteFilter struct { - Name string - Verbose bool -} - -// Verify returns true if the passed route matches the filter fields -func (r *RouteFilter) Verify(route *route.RouteConfiguration) bool { - if r.Name != "" && r.Name != route.Name { - return false - } - return true -} - -// PrintRouteSummary prints a summary of the relevant routes in the config dump to the ConfigWriter stdout -func (c *ConfigWriter) PrintRouteSummary(filter RouteFilter) error { - w, routes, err := c.setupRouteConfigWriter() - if err != nil { - return err - } - if filter.Verbose { - fmt.Fprintln(w, "NAME\tDOMAINS\tMATCH\tVIRTUAL SERVICE") - } else { - fmt.Fprintln(w, "NAME\tVIRTUAL HOSTS") - } - for _, route := range routes { - if filter.Verify(route) { - if filter.Verbose { - for _, vhosts := range route.GetVirtualHosts() { - for _, r := range vhosts.Routes { - if !isPassthrough(r.GetAction()) { - fmt.Fprintf(w, "%v\t%s\t%s\t%s\n", - route.Name, - describeRouteDomains(vhosts.GetDomains()), - describeMatch(r.GetMatch()), - describeManagement(r.GetMetadata())) - } - } - if len(vhosts.Routes) == 0 { - fmt.Fprintf(w, "%v\t%s\t%s\t%s\n", - route.Name, - describeRouteDomains(vhosts.GetDomains()), - "/*", - "404") - } - } - } else { - fmt.Fprintf(w, "%v\t%v\n", route.Name, len(route.GetVirtualHosts())) - } - } - } - return w.Flush() -} - -func describeRouteDomains(domains []string) string { - if len(domains) == 0 { - return "" - } - if len(domains) == 1 { - return domains[0] - } - - // Return the shortest non-numeric domain. Count of domains seems uninteresting. - max := 2 - withoutPort := make([]string, 0, len(domains)) - for _, d := range domains { - if !strings.Contains(d, ":") { - withoutPort = append(withoutPort, d) - // if the domain contains IPv6, such as [fd00:10:96::7fc7] and [fd00:10:96::7fc7]:8090 - } else if strings.Count(d, ":") > 2 { - // if the domain is only a IPv6 address, such as [fd00:10:96::7fc7], append it - if strings.HasSuffix(d, "]") { - withoutPort = append(withoutPort, d) - } - } - } - withoutPort = unexpandDomains(withoutPort) - if len(withoutPort) > max { - ret := strings.Join(withoutPort[:max], ", ") - return fmt.Sprintf("%s + %d more...", ret, len(withoutPort)-max) - } - return strings.Join(withoutPort, ", ") -} - -func unexpandDomains(domains []string) []string { - unique := sets.New(domains...) - shouldDelete := sets.New() - for _, h := range domains { - stripFull := strings.TrimSuffix(h, ".svc.cluster.local") - if _, f := unique[stripFull]; f && stripFull != h { - shouldDelete.Insert(h) - } - stripPartial := strings.TrimSuffix(h, ".svc") - if _, f := unique[stripPartial]; f && stripPartial != h { - shouldDelete.Insert(h) - } - } - // Filter from original list to keep original order - ret := make([]string, 0, len(domains)) - for _, h := range domains { - if _, f := shouldDelete[h]; !f { - ret = append(ret, h) - } - } - return ret -} - -func describeManagement(metadata *envoy_config_core_v3.Metadata) string { - if metadata == nil { - return "" - } - istioMetadata, ok := metadata.FilterMetadata[pilot_util.IstioMetadataKey] - if !ok { - return "" - } - config, ok := istioMetadata.Fields["config"] - if !ok { - return "" - } - return renderConfig(config.GetStringValue()) -} - -func renderConfig(configPath string) string { - if strings.HasPrefix(configPath, "/apis/networking.istio.io/v1alpha3/namespaces/") { - pieces := strings.Split(configPath, "/") - if len(pieces) != 8 { - return "" - } - return fmt.Sprintf("%s.%s", pieces[7], pieces[5]) - } - return "" -} - -// PrintRouteDump prints the relevant routes in the config dump to the ConfigWriter stdout -func (c *ConfigWriter) PrintRouteDump(filter RouteFilter, outputFormat string) error { - _, routes, err := c.setupRouteConfigWriter() - if err != nil { - return err - } - filteredRoutes := make(protio.MessageSlice, 0, len(routes)) - for _, route := range routes { - if filter.Verify(route) { - filteredRoutes = append(filteredRoutes, route) - } - } - out, err := json.MarshalIndent(filteredRoutes, "", " ") - if err != nil { - return err - } - if outputFormat == "yaml" { - if out, err = yaml.JSONToYAML(out); err != nil { - return err - } - } - fmt.Fprintln(c.Stdout, string(out)) - return nil -} - -func (c *ConfigWriter) setupRouteConfigWriter() (*tabwriter.Writer, []*route.RouteConfiguration, error) { - routes, err := c.retrieveSortedRouteSlice() - if err != nil { - return nil, nil, err - } - w := new(tabwriter.Writer).Init(c.Stdout, 0, 8, 5, ' ', 0) - return w, routes, nil -} - -func (c *ConfigWriter) retrieveSortedRouteSlice() ([]*route.RouteConfiguration, error) { - if c.configDump == nil { - return nil, fmt.Errorf("config writer has not been primed") - } - routeDump, err := c.configDump.GetRouteConfigDump() - if err != nil { - return nil, err - } - routes := make([]*route.RouteConfiguration, 0) - for _, r := range routeDump.DynamicRouteConfigs { - if r.RouteConfig != nil { - routeTyped := &route.RouteConfiguration{} - // Support v2 or v3 in config dump. See ads.go:RequestedTypes for more info. - r.RouteConfig.TypeUrl = v3.RouteType - err = r.RouteConfig.UnmarshalTo(routeTyped) - if err != nil { - return nil, err - } - routes = append(routes, routeTyped) - } - } - for _, r := range routeDump.StaticRouteConfigs { - if r.RouteConfig != nil { - routeTyped := &route.RouteConfiguration{} - // Support v2 or v3 in config dump. See ads.go:RequestedTypes for more info. - r.RouteConfig.TypeUrl = v3.RouteType - err = r.RouteConfig.UnmarshalTo(routeTyped) - if err != nil { - return nil, err - } - routes = append(routes, routeTyped) - } - } - if len(routes) == 0 { - return nil, fmt.Errorf("no routes found") - } - sort.Slice(routes, func(i, j int) bool { - iName, err := strconv.Atoi(routes[i].Name) - if err != nil { - return false - } - jName, err := strconv.Atoi(routes[j].Name) - if err != nil { - return false - } - return iName < jName - }) - return routes, nil -} - -func isPassthrough(action interface{}) bool { - a, ok := action.(*route.Route_Route) - if !ok { - return false - } - cl, ok := a.Route.ClusterSpecifier.(*route.RouteAction_Cluster) - if !ok { - return false - } - return cl.Cluster == "PassthroughCluster" -} diff --git a/istioctl/pkg/writer/envoy/configdump/route_test.go b/istioctl/pkg/writer/envoy/configdump/route_test.go deleted file mode 100644 index 67e0b08cb..000000000 --- a/istioctl/pkg/writer/envoy/configdump/route_test.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configdump - -import ( - "testing" -) - -func TestDescribeRouteDomains(t *testing.T) { - tests := []struct { - desc string - domains []string - expected string - }{ - { - desc: "test zero domain", - domains: []string{}, - expected: "", - }, - { - desc: "test only one domain", - domains: []string{"example.com"}, - expected: "example.com", - }, - { - desc: "test domains with port", - domains: []string{"example.com", "example.com:8080"}, - expected: "example.com", - }, - { - desc: "test domains with ipv4 addresses", - domains: []string{"example.com", "example.com:8080", "1.2.3.4", "1.2.3.4:8080"}, - expected: "example.com, 1.2.3.4", - }, - { - desc: "test domains with ipv6 addresses", - domains: []string{"example.com", "example.com:8080", "[fd00:10:96::7fc7]", "[fd00:10:96::7fc7]:8080"}, - expected: "example.com, [fd00:10:96::7fc7]", - }, - { - desc: "test with more domains", - domains: []string{"example.com", "example.com:8080", "www.example.com", "www.example.com:8080", "[fd00:10:96::7fc7]", "[fd00:10:96::7fc7]:8080"}, - expected: "example.com, www.example.com + 1 more...", - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got := describeRouteDomains(tt.domains); got != tt.expected { - t.Errorf("%s: expect %v got %v", tt.desc, tt.expected, got) - } - }) - } -} diff --git a/istioctl/pkg/writer/envoy/configdump/testdata/configdump.json b/istioctl/pkg/writer/envoy/configdump/testdata/configdump.json deleted file mode 100644 index 4732296e1..000000000 --- a/istioctl/pkg/writer/envoy/configdump/testdata/configdump.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "configs": [ - { - "@type": "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump", - "bootstrap": { - "node": { - "metadata": { - "ISTIO_PROXY_SHA": "istio-proxy:436f365a8007cd8a13a9f1321e7cce94bcc8883e", - "ISTIO_VERSION": "1.10.0" - }, - "userAgentBuildVersion": { - "version": { - "majorNumber": 1, - "minorNumber": 18, - "patch": 3 - }, - "metadata": { - "build.type": "RELEASE", - "revision.sha": "436f365a8007cd8a13a9f1321e7cce94bcc8883e", - "revision.status": "Clean", - "ssl.version": "BoringSSL" - } - } - } - } - } - ] -} diff --git a/istioctl/pkg/writer/envoy/configdump/testdata/versionsummary.txt b/istioctl/pkg/writer/envoy/configdump/testdata/versionsummary.txt deleted file mode 100644 index 56212c2fb..000000000 --- a/istioctl/pkg/writer/envoy/configdump/testdata/versionsummary.txt +++ /dev/null @@ -1,3 +0,0 @@ -Istio Version: 1.10.0 -Istio Proxy Version: 436f365a8007cd8a13a9f1321e7cce94bcc8883e -Envoy Version: 1.18.3/Clean/RELEASE/BoringSSL diff --git a/istioctl/pkg/writer/envoy/logging/testdata/logging.txt b/istioctl/pkg/writer/envoy/logging/testdata/logging.txt deleted file mode 100644 index a8acc160a..000000000 --- a/istioctl/pkg/writer/envoy/logging/testdata/logging.txt +++ /dev/null @@ -1,42 +0,0 @@ -active loggers: - admin: warning - aws: warning - assert: warning - backtrace: warning - client: warning - config: warning - connection: warning - dubbo: warning - file: warning - filter: warning - forward_proxy: warning - grpc: warning - hc: warning - health_checker: warning - http: warning - http2: warning - hystrix: warning - init: warning - io: warning - jwt: warning - kafka: warning - lua: warning - main: warning - misc: warning - mongo: warning - quic: warning - pool: warning - rbac: warning - redis: warning - router: warning - runtime: warning - stats: warning - secret: warning - tap: warning - testing: warning - thrift: warning - tracing: warning - upstream: warning - udp: warning - wasm: warning - diff --git a/istioctl/pkg/writer/pilot/status.go b/istioctl/pkg/writer/pilot/status.go deleted file mode 100644 index 7c0558b30..000000000 --- a/istioctl/pkg/writer/pilot/status.go +++ /dev/null @@ -1,302 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package pilot - -import ( - "encoding/json" - "fmt" - "io" - "sort" - "strings" - "text/tabwriter" -) - -import ( - xdsapi "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - xdsstatus "github.com/envoyproxy/go-control-plane/envoy/service/status/v3" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/multixds" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - xdsresource "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" -) - -// StatusWriter enables printing of sync status using multiple []byte Istiod responses -type StatusWriter struct { - Writer io.Writer -} - -type writerStatus struct { - pilot string - xds.SyncStatus -} - -// XdsStatusWriter enables printing of sync status using multiple xdsapi.DiscoveryResponse Istiod responses -type XdsStatusWriter struct { - Writer io.Writer - InternalDebugAllIstiod bool -} - -type xdsWriterStatus struct { - proxyID string - clusterID string - istiodID string - istiodVersion string - clusterStatus string - listenerStatus string - routeStatus string - endpointStatus string - extensionconfigStaus string -} - -// PrintAll takes a slice of Pilot syncz responses and outputs them using a tabwriter -func (s *StatusWriter) PrintAll(statuses map[string][]byte) error { - w, fullStatus, err := s.setupStatusPrint(statuses) - if err != nil { - return err - } - for _, status := range fullStatus { - if err := statusPrintln(w, status); err != nil { - return err - } - } - return w.Flush() -} - -// PrintSingle takes a slice of Pilot syncz responses and outputs them using a tabwriter filtering for a specific pod -func (s *StatusWriter) PrintSingle(statuses map[string][]byte, proxyName string) error { - w, fullStatus, err := s.setupStatusPrint(statuses) - if err != nil { - return err - } - for _, status := range fullStatus { - if strings.Contains(status.ProxyID, proxyName) { - if err := statusPrintln(w, status); err != nil { - return err - } - } - } - return w.Flush() -} - -func (s *StatusWriter) setupStatusPrint(statuses map[string][]byte) (*tabwriter.Writer, []*writerStatus, error) { - w := new(tabwriter.Writer).Init(s.Writer, 0, 9, 5, ' ', 0) - _, _ = fmt.Fprintln(w, "NAME\tCLUSTER\tCDS\tLDS\tEDS\tRDS\tECDS\tISTIOD\tVERSION") - fullStatus := make([]*writerStatus, 0, len(statuses)) - for pilot, status := range statuses { - var ss []*writerStatus - err := json.Unmarshal(status, &ss) - if err != nil { - return nil, nil, err - } - for _, s := range ss { - s.pilot = pilot - } - fullStatus = append(fullStatus, ss...) - } - sort.Slice(fullStatus, func(i, j int) bool { - if fullStatus[i].ClusterID != fullStatus[j].ClusterID { - return fullStatus[i].ClusterID < fullStatus[j].ClusterID - } - return fullStatus[i].ProxyID < fullStatus[j].ProxyID - }) - return w, fullStatus, nil -} - -func statusPrintln(w io.Writer, status *writerStatus) error { - clusterSynced := xdsStatus(status.ClusterSent, status.ClusterAcked) - listenerSynced := xdsStatus(status.ListenerSent, status.ListenerAcked) - routeSynced := xdsStatus(status.RouteSent, status.RouteAcked) - endpointSynced := xdsStatus(status.EndpointSent, status.EndpointAcked) - extensionconfigSynced := xdsStatus(status.ExtensionConfigSent, status.ExtensionConfigAcked) - version := status.IstioVersion - if version == "" { - // If we can't find an Istio version (talking to a 1.1 pilot), fallback to the proxy version - // This is misleading, as the proxy version isn't always the same as the Istio version, - // but it is better than not providing any information. - version = status.ProxyVersion + "*" - } - _, _ = fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\n", - status.ProxyID, status.ClusterID, - clusterSynced, listenerSynced, endpointSynced, routeSynced, extensionconfigSynced, - status.pilot, version) - return nil -} - -func xdsStatus(sent, acked string) string { - if sent == "" { - return "NOT SENT" - } - if sent == acked { - return "SYNCED" - } - // acked will be empty string when there is never Acknowledged - if acked == "" { - return "STALE (Never Acknowledged)" - } - // Since the Nonce changes to uuid, so there is no more any time diff info - return "STALE" -} - -// PrintAll takes a slice of Istiod syncz responses and outputs them using a tabwriter -func (s *XdsStatusWriter) PrintAll(statuses map[string]*xdsapi.DiscoveryResponse) error { - w, fullStatus, err := s.setupStatusPrint(statuses) - if err != nil { - return err - } - for _, status := range fullStatus { - if err := xdsStatusPrintln(w, status); err != nil { - return err - } - } - if w != nil { - return w.Flush() - } - return nil -} - -func (s *XdsStatusWriter) setupStatusPrint(drs map[string]*xdsapi.DiscoveryResponse) (*tabwriter.Writer, []*xdsWriterStatus, error) { - // Gather the statuses before printing so they may be sorted - var fullStatus []*xdsWriterStatus - mappedResp := map[string]string{} - var w *tabwriter.Writer - for id, dr := range drs { - for _, resource := range dr.Resources { - switch resource.TypeUrl { - case "type.googleapis.com/envoy.service.status.v3.ClientConfig": - clientConfig := xdsstatus.ClientConfig{} - err := resource.UnmarshalTo(&clientConfig) - if err != nil { - return nil, nil, fmt.Errorf("could not unmarshal ClientConfig: %w", err) - } - cds, lds, eds, rds, ecds := getSyncStatus(&clientConfig) - cp := multixds.CpInfo(dr) - meta, err := model.ParseMetadata(clientConfig.GetNode().GetMetadata()) - if err != nil { - return nil, nil, fmt.Errorf("could not parse node metadata: %w", err) - } - fullStatus = append(fullStatus, &xdsWriterStatus{ - proxyID: clientConfig.GetNode().GetId(), - clusterID: meta.ClusterID.String(), - istiodID: cp.ID, - istiodVersion: cp.Info.Version, - clusterStatus: cds, - listenerStatus: lds, - routeStatus: rds, - endpointStatus: eds, - extensionconfigStaus: ecds, - }) - if len(fullStatus) == 0 { - return nil, nil, fmt.Errorf("no proxies found (checked %d istiods)", len(drs)) - } - - w = new(tabwriter.Writer).Init(s.Writer, 0, 8, 5, ' ', 0) - _, _ = fmt.Fprintln(w, "NAME\tCLUSTER\tCDS\tLDS\tEDS\tRDS\tECDS\tISTIOD\tVERSION") - - sort.Slice(fullStatus, func(i, j int) bool { - return fullStatus[i].proxyID < fullStatus[j].proxyID - }) - default: - for _, resource := range dr.Resources { - if s.InternalDebugAllIstiod { - mappedResp[id] = string(resource.Value) + "\n" - } else { - _, _ = s.Writer.Write(resource.Value) - _, _ = s.Writer.Write([]byte("\n")) - } - } - fullStatus = nil - } - } - } - if len(mappedResp) > 0 { - mresp, err := json.MarshalIndent(mappedResp, "", " ") - if err != nil { - return nil, nil, err - } - _, _ = s.Writer.Write(mresp) - _, _ = s.Writer.Write([]byte("\n")) - } - - return w, fullStatus, nil -} - -func xdsStatusPrintln(w io.Writer, status *xdsWriterStatus) error { - _, err := fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\n", - status.proxyID, status.clusterID, - status.clusterStatus, status.listenerStatus, status.endpointStatus, status.routeStatus, - status.extensionconfigStaus, - status.istiodID, status.istiodVersion) - return err -} - -func getSyncStatus(clientConfig *xdsstatus.ClientConfig) (cds, lds, eds, rds, ecds string) { - configs := handleAndGetXdsConfigs(clientConfig) - for _, config := range configs { - cfgType := config.GetTypeUrl() - switch cfgType { - case xdsresource.ListenerType: - lds = config.GetConfigStatus().String() - case xdsresource.ClusterType: - cds = config.GetConfigStatus().String() - case xdsresource.RouteType: - rds = config.GetConfigStatus().String() - case xdsresource.EndpointType: - eds = config.GetConfigStatus().String() - case xdsresource.ExtensionConfigurationType: - ecds = config.GetConfigStatus().String() - default: - log.Infof("GenericXdsConfig unexpected type %s\n", xdsresource.GetShortType(cfgType)) - } - } - return -} - -func handleAndGetXdsConfigs(clientConfig *xdsstatus.ClientConfig) []*xdsstatus.ClientConfig_GenericXdsConfig { - configs := make([]*xdsstatus.ClientConfig_GenericXdsConfig, 0) - if clientConfig.GetGenericXdsConfigs() != nil { - configs = clientConfig.GetGenericXdsConfigs() - return configs - } - - // FIXME: currently removing the deprecated code below may result in functions not working - // if there is a mismatch of versions between istiod and istioctl - // nolint: staticcheck - for _, config := range clientConfig.GetXdsConfig() { - var typeURL string - switch config.PerXdsConfig.(type) { - case *xdsstatus.PerXdsConfig_ListenerConfig: - typeURL = xdsresource.ListenerType - case *xdsstatus.PerXdsConfig_ClusterConfig: - typeURL = xdsresource.ClusterType - case *xdsstatus.PerXdsConfig_RouteConfig: - typeURL = xdsresource.RouteType - case *xdsstatus.PerXdsConfig_EndpointConfig: - typeURL = xdsresource.EndpointType - } - - if typeURL != "" { - configs = append(configs, &xdsstatus.ClientConfig_GenericXdsConfig{ - TypeUrl: typeURL, - ConfigStatus: config.Status, - }) - } - } - - return configs -} diff --git a/istioctl/pkg/writer/pilot/status_test.go b/istioctl/pkg/writer/pilot/status_test.go deleted file mode 100644 index 99582cda4..000000000 --- a/istioctl/pkg/writer/pilot/status_test.go +++ /dev/null @@ -1,412 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package pilot - -import ( - "bytes" - "encoding/json" - "os" - "testing" -) - -import ( - envoycorev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - xdsapi "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - status "github.com/envoyproxy/go-control-plane/envoy/service/status/v3" - "github.com/google/uuid" - any "google.golang.org/protobuf/types/known/anypb" - istioversion "istio.io/pkg/version" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - networkingutil "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" - "github.com/apache/dubbo-go-pixiu/tests/util" -) - -var preDefinedNonce = newNonce() - -func newNonce() string { - return uuid.New().String() -} - -func TestStatusWriter_PrintAll(t *testing.T) { - tests := []struct { - name string - input map[string][]xds.SyncStatus - want string - wantErr bool - }{ - { - name: "prints multiple istiod inputs to buffer in alphabetical order by pod name", - input: map[string][]xds.SyncStatus{ - "istiod1": statusInput1(), - "istiod2": statusInput2(), - "istiod3": statusInput3(), - }, - want: "testdata/multiStatusMultiPilot.txt", - }, - { - name: "prints single istiod input to buffer in alphabetical order by pod name", - input: map[string][]xds.SyncStatus{ - "istiod1": append(statusInput1(), statusInput2()...), - }, - want: "testdata/multiStatusSinglePilot.txt", - }, - { - name: "error if given non-syncstatus info", - input: map[string][]xds.SyncStatus{ - "istiod1": {}, - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := &bytes.Buffer{} - sw := StatusWriter{Writer: got} - input := map[string][]byte{} - for key, ss := range tt.input { - b, _ := json.Marshal(ss) - input[key] = b - } - if len(tt.input["istiod1"]) == 0 { - input = map[string][]byte{ - "istiod1": []byte(`gobbledygook`), - } - } - err := sw.PrintAll(input) - if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - want, _ := os.ReadFile(tt.want) - if err := util.Compare(got.Bytes(), want); err != nil { - t.Errorf(err.Error()) - } - }) - } -} - -func TestStatusWriter_PrintSingle(t *testing.T) { - tests := []struct { - name string - input map[string][]xds.SyncStatus - filterPod string - want string - wantErr bool - }{ - { - name: "prints multiple istiod inputs to buffer filtering for pod", - input: map[string][]xds.SyncStatus{ - "istiod1": statusInput1(), - "istiod2": statusInput2(), - }, - filterPod: "proxy2", - want: "testdata/singleStatus.txt", - }, - { - name: "single istiod input to buffer filtering for pod", - input: map[string][]xds.SyncStatus{ - "istiod2": append(statusInput1(), statusInput2()...), - }, - filterPod: "proxy2", - want: "testdata/singleStatus.txt", - }, - { - name: "fallback to proxy version", - input: map[string][]xds.SyncStatus{ - "istiod2": statusInputProxyVersion(), - }, - filterPod: "proxy2", - want: "testdata/singleStatusFallback.txt", - }, - { - name: "error if given non-syncstatus info", - input: map[string][]xds.SyncStatus{ - "istiod1": {}, - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := &bytes.Buffer{} - sw := StatusWriter{Writer: got} - input := map[string][]byte{} - for key, ss := range tt.input { - b, _ := json.Marshal(ss) - input[key] = b - } - if len(tt.input["istiod1"]) == 0 && len(tt.input["istiod2"]) == 0 { - input = map[string][]byte{ - "istiod1": []byte(`gobbledygook`), - } - } - err := sw.PrintSingle(input, tt.filterPod) - if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - want, _ := os.ReadFile(tt.want) - if err := util.Compare(got.Bytes(), want); err != nil { - t.Errorf(err.Error()) - } - }) - } -} - -func statusInput1() []xds.SyncStatus { - return []xds.SyncStatus{ - { - ClusterID: "cluster1", - ProxyID: "proxy1", - IstioVersion: "1.1", - ClusterSent: preDefinedNonce, - ClusterAcked: newNonce(), - ListenerSent: preDefinedNonce, - ListenerAcked: preDefinedNonce, - EndpointSent: preDefinedNonce, - EndpointAcked: preDefinedNonce, - }, - } -} - -func statusInput2() []xds.SyncStatus { - return []xds.SyncStatus{ - { - ClusterID: "cluster2", - ProxyID: "proxy2", - IstioVersion: "1.1", - ClusterSent: preDefinedNonce, - ClusterAcked: newNonce(), - ListenerSent: preDefinedNonce, - ListenerAcked: preDefinedNonce, - EndpointSent: preDefinedNonce, - EndpointAcked: newNonce(), - RouteSent: preDefinedNonce, - RouteAcked: preDefinedNonce, - }, - } -} - -func statusInput3() []xds.SyncStatus { - return []xds.SyncStatus{ - { - ClusterID: "cluster3", - ProxyID: "proxy3", - IstioVersion: "1.1", - ClusterSent: preDefinedNonce, - ClusterAcked: "", - ListenerAcked: preDefinedNonce, - EndpointSent: preDefinedNonce, - EndpointAcked: "", - RouteSent: preDefinedNonce, - RouteAcked: preDefinedNonce, - }, - } -} - -func statusInputProxyVersion() []xds.SyncStatus { - return []xds.SyncStatus{ - { - ClusterID: "cluster2", - ProxyID: "proxy2", - ProxyVersion: "1.1", - ClusterSent: preDefinedNonce, - ClusterAcked: newNonce(), - ListenerSent: preDefinedNonce, - ListenerAcked: preDefinedNonce, - EndpointSent: preDefinedNonce, - EndpointAcked: newNonce(), - RouteSent: preDefinedNonce, - RouteAcked: preDefinedNonce, - }, - } -} - -func TestXdsStatusWriter_PrintAll(t *testing.T) { - tests := []struct { - name string - input map[string]*xdsapi.DiscoveryResponse - want string - wantErr bool - }{ - { - name: "prints multiple istiod inputs to buffer in alphabetical order by pod name", - input: map[string]*xdsapi.DiscoveryResponse{ - "istiod1": xdsResponseInput("istiod1", []clientConfigInput{ - { - proxyID: "proxy1", - clusterID: "cluster1", - cdsSyncStatus: status.ConfigStatus_STALE, - ldsSyncStatus: status.ConfigStatus_SYNCED, - rdsSyncStatus: status.ConfigStatus_NOT_SENT, - edsSyncStatus: status.ConfigStatus_SYNCED, - ecdsSyncStatus: status.ConfigStatus_SYNCED, - }, - }), - "istiod2": xdsResponseInput("istiod2", []clientConfigInput{ - { - proxyID: "proxy2", - clusterID: "cluster2", - cdsSyncStatus: status.ConfigStatus_STALE, - ldsSyncStatus: status.ConfigStatus_SYNCED, - rdsSyncStatus: status.ConfigStatus_SYNCED, - edsSyncStatus: status.ConfigStatus_STALE, - ecdsSyncStatus: status.ConfigStatus_STALE, - }, - }), - "istiod3": xdsResponseInput("istiod3", []clientConfigInput{ - { - proxyID: "proxy3", - clusterID: "cluster3", - cdsSyncStatus: status.ConfigStatus_UNKNOWN, - ldsSyncStatus: status.ConfigStatus_ERROR, - rdsSyncStatus: status.ConfigStatus_NOT_SENT, - edsSyncStatus: status.ConfigStatus_STALE, - ecdsSyncStatus: status.ConfigStatus_NOT_SENT, - }, - }), - }, - want: "testdata/multiXdsStatusMultiPilot.txt", - }, - { - name: "prints single istiod input to buffer in alphabetical order by pod name", - input: map[string]*xdsapi.DiscoveryResponse{ - "istiod1": xdsResponseInput("istiod1", []clientConfigInput{ - { - proxyID: "proxy1", - clusterID: "cluster1", - cdsSyncStatus: status.ConfigStatus_STALE, - ldsSyncStatus: status.ConfigStatus_SYNCED, - rdsSyncStatus: status.ConfigStatus_NOT_SENT, - edsSyncStatus: status.ConfigStatus_SYNCED, - ecdsSyncStatus: status.ConfigStatus_NOT_SENT, - }, - { - proxyID: "proxy2", - clusterID: "cluster2", - cdsSyncStatus: status.ConfigStatus_STALE, - ldsSyncStatus: status.ConfigStatus_SYNCED, - rdsSyncStatus: status.ConfigStatus_SYNCED, - edsSyncStatus: status.ConfigStatus_STALE, - ecdsSyncStatus: status.ConfigStatus_NOT_SENT, - }, - }), - }, - want: "testdata/multiXdsStatusSinglePilot.txt", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := &bytes.Buffer{} - sw := XdsStatusWriter{Writer: got} - input := map[string]*xdsapi.DiscoveryResponse{} - for key, ss := range tt.input { - input[key] = ss - } - - err := sw.PrintAll(input) - if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - want, _ := os.ReadFile(tt.want) - if err := util.Compare(got.Bytes(), want); err != nil { - t.Errorf(err.Error()) - } - }) - } -} - -const clientConfigType = "type.googleapis.com/envoy.service.status.v3.ClientConfig" - -type clientConfigInput struct { - proxyID string - clusterID string - - cdsSyncStatus status.ConfigStatus - ldsSyncStatus status.ConfigStatus - rdsSyncStatus status.ConfigStatus - edsSyncStatus status.ConfigStatus - ecdsSyncStatus status.ConfigStatus -} - -func newXdsClientConfig(config clientConfigInput) *status.ClientConfig { - meta := model.NodeMetadata{ - ClusterID: cluster.ID(config.clusterID), - } - return &status.ClientConfig{ - Node: &envoycorev3.Node{ - Id: config.proxyID, - Metadata: meta.ToStruct(), - }, - GenericXdsConfigs: []*status.ClientConfig_GenericXdsConfig{ - { - TypeUrl: v3.ClusterType, - ConfigStatus: config.cdsSyncStatus, - }, - { - TypeUrl: v3.ListenerType, - ConfigStatus: config.ldsSyncStatus, - }, - { - TypeUrl: v3.RouteType, - ConfigStatus: config.rdsSyncStatus, - }, - { - TypeUrl: v3.EndpointType, - ConfigStatus: config.edsSyncStatus, - }, - { - TypeUrl: v3.ExtensionConfigurationType, - ConfigStatus: config.ecdsSyncStatus, - }, - }, - } -} - -func xdsResponseInput(istiodID string, configInputs []clientConfigInput) *xdsapi.DiscoveryResponse { - icp := &xds.IstioControlPlaneInstance{ - Component: "istiod", - ID: istiodID, - Info: istioversion.BuildInfo{ - Version: "1.1", - }, - } - identifier, _ := json.Marshal(icp) - - resources := make([]*any.Any, 0) - for _, input := range configInputs { - resources = append(resources, networkingutil.MessageToAny(newXdsClientConfig(input))) - } - - return &xdsapi.DiscoveryResponse{ - VersionInfo: "1.1", - TypeUrl: clientConfigType, - Resources: resources, - ControlPlane: &envoycorev3.ControlPlane{ - Identifier: string(identifier), - }, - } -} diff --git a/istioctl/pkg/writer/pilot/testdata/multiStatusMultiPilot.txt b/istioctl/pkg/writer/pilot/testdata/multiStatusMultiPilot.txt deleted file mode 100644 index 4472b9c10..000000000 --- a/istioctl/pkg/writer/pilot/testdata/multiStatusMultiPilot.txt +++ /dev/null @@ -1,5 +0,0 @@ -NAME CLUSTER CDS LDS EDS RDS ECDS ISTIOD VERSION -proxy1 cluster1 STALE SYNCED SYNCED NOT SENT NOT SENT istiod1 1.1 -proxy2 cluster2 STALE SYNCED STALE SYNCED NOT SENT istiod2 1.1 -proxy3 cluster3 STALE (Never Acknowledged) NOT SENT STALE (Never Acknowledged) SYNCED NOT SENT istiod3 1.1 - diff --git a/istioctl/pkg/writer/pilot/testdata/multiStatusSinglePilot.txt b/istioctl/pkg/writer/pilot/testdata/multiStatusSinglePilot.txt deleted file mode 100644 index 218a8a7d0..000000000 --- a/istioctl/pkg/writer/pilot/testdata/multiStatusSinglePilot.txt +++ /dev/null @@ -1,3 +0,0 @@ -NAME CLUSTER CDS LDS EDS RDS ECDS ISTIOD VERSION -proxy1 cluster1 STALE SYNCED SYNCED NOT SENT NOT SENT istiod1 1.1 -proxy2 cluster2 STALE SYNCED STALE SYNCED NOT SENT istiod1 1.1 diff --git a/istioctl/pkg/writer/pilot/testdata/multiXdsStatusMultiPilot.txt b/istioctl/pkg/writer/pilot/testdata/multiXdsStatusMultiPilot.txt deleted file mode 100644 index 8dea2b9a3..000000000 --- a/istioctl/pkg/writer/pilot/testdata/multiXdsStatusMultiPilot.txt +++ /dev/null @@ -1,4 +0,0 @@ -NAME CLUSTER CDS LDS EDS RDS ECDS ISTIOD VERSION -proxy1 cluster1 STALE SYNCED SYNCED NOT_SENT SYNCED istiod1 1.1 -proxy2 cluster2 STALE SYNCED STALE SYNCED STALE istiod2 1.1 -proxy3 cluster3 UNKNOWN ERROR STALE NOT_SENT NOT_SENT istiod3 1.1 diff --git a/istioctl/pkg/writer/pilot/testdata/multiXdsStatusSinglePilot.txt b/istioctl/pkg/writer/pilot/testdata/multiXdsStatusSinglePilot.txt deleted file mode 100644 index c358a342b..000000000 --- a/istioctl/pkg/writer/pilot/testdata/multiXdsStatusSinglePilot.txt +++ /dev/null @@ -1,3 +0,0 @@ -NAME CLUSTER CDS LDS EDS RDS ECDS ISTIOD VERSION -proxy1 cluster1 STALE SYNCED SYNCED NOT_SENT NOT_SENT istiod1 1.1 -proxy2 cluster2 STALE SYNCED STALE SYNCED NOT_SENT istiod1 1.1 diff --git a/istioctl/pkg/writer/pilot/testdata/singleStatus.txt b/istioctl/pkg/writer/pilot/testdata/singleStatus.txt deleted file mode 100644 index 6f099c3fe..000000000 --- a/istioctl/pkg/writer/pilot/testdata/singleStatus.txt +++ /dev/null @@ -1,2 +0,0 @@ -NAME CLUSTER CDS LDS EDS RDS ECDS ISTIOD VERSION -proxy2 cluster2 STALE SYNCED STALE SYNCED NOT SENT istiod2 1.1 diff --git a/istioctl/pkg/writer/pilot/testdata/singleStatusFallback.txt b/istioctl/pkg/writer/pilot/testdata/singleStatusFallback.txt deleted file mode 100644 index 551440139..000000000 --- a/istioctl/pkg/writer/pilot/testdata/singleStatusFallback.txt +++ /dev/null @@ -1,2 +0,0 @@ -NAME CLUSTER CDS LDS EDS RDS ECDS ISTIOD VERSION -proxy2 cluster2 STALE SYNCED STALE SYNCED NOT SENT istiod2 1.1* diff --git a/istioctl/pkg/xds/client.go b/istioctl/pkg/xds/client.go deleted file mode 100644 index 23690f97e..000000000 --- a/istioctl/pkg/xds/client.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -// xds uses ADSC to call XDS - -import ( - "context" - "crypto/tls" - "fmt" - "strings" -) - -import ( - xdsapi "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/clioptions" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/adsc" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -const ( - // defaultExpirationSeconds is how long-lived a token to request (an hour) - defaultExpirationSeconds = 60 * 60 -) - -// Audience to create tokens for -var tokenAudiences = []string{"istio-ca"} - -// GetXdsResponse opens a gRPC connection to opts.xds and waits for a single response -func GetXdsResponse(dr *xdsapi.DiscoveryRequest, ns string, serviceAccount string, opts clioptions.CentralControlPlaneOptions, - grpcOpts []grpc.DialOption) (*xdsapi.DiscoveryResponse, error) { - adscConn, err := adsc.NewWithBackoffPolicy(opts.Xds, &adsc.Config{ - Meta: model.NodeMetadata{ - Generator: "event", - ServiceAccount: serviceAccount, - Namespace: ns, - CloudrunAddr: opts.IstiodAddr, - }.ToStruct(), - CertDir: opts.CertDir, - InsecureSkipVerify: opts.InsecureSkipVerify, - XDSSAN: opts.XDSSAN, - GrpcOpts: grpcOpts, - }, nil) - if err != nil { - return nil, fmt.Errorf("could not dial: %w", err) - } - err = adscConn.Run() - if err != nil { - return nil, fmt.Errorf("ADSC: failed running %v", err) - } - - err = adscConn.Send(dr) - if err != nil { - return nil, err - } - response, err := adscConn.WaitVersion(opts.Timeout, dr.TypeUrl, "") - return response, err -} - -// DialOptions constructs gRPC dial options from command line configuration -func DialOptions(opts clioptions.CentralControlPlaneOptions, - ns string, serviceAccount string, kubeClient kube.ExtendedClient) ([]grpc.DialOption, error) { - ctx := context.TODO() - // If we are using the insecure 15010 don't bother getting a token - if opts.Plaintext || opts.CertDir != "" { - return make([]grpc.DialOption, 0), nil - } - // Use bearer token - aud := tokenAudiences - isMCP := strings.HasSuffix(opts.Xds, ".googleapis.com") || strings.HasSuffix(opts.Xds, ".googleapis.com:443") - if isMCP { - // Special credentials handling when using ASM Managed Control Plane. - mem, err := getHubMembership(ctx, kubeClient) - if err != nil { - return nil, fmt.Errorf("failed to query Hub membership: %w", err) - } - aud = []string{mem.WorkloadIdentityPool} - } - k8sCreds, err := kubeClient.CreatePerRPCCredentials(ctx, ns, serviceAccount, aud, defaultExpirationSeconds) - if err != nil { - return nil, fmt.Errorf("failed to create RPC credentials for \"%s.%s\": %w", serviceAccount, ns, err) - } - if isMCP { - return mcpDialOptions(ctx, opts.GCPProject, k8sCreds) - } - return []grpc.DialOption{ - grpc.WithTransportCredentials(credentials.NewTLS( - &tls.Config{ - // Always skip verifying, because without it we always get "certificate signed by unknown authority". - // We don't set the XDSSAN for the same reason. - InsecureSkipVerify: true, - })), - grpc.WithPerRPCCredentials(k8sCreds), - }, nil -} diff --git a/istioctl/pkg/xds/google.go b/istioctl/pkg/xds/google.go deleted file mode 100644 index ca9f0b3c7..000000000 --- a/istioctl/pkg/xds/google.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "context" - "crypto/tls" - "crypto/x509" - "errors" - "fmt" - "strings" -) - -import ( - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/oauth" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -type meshAuthCredentials struct { - k8sCreds credentials.PerRPCCredentials - gcpCreds credentials.PerRPCCredentials - project string -} - -func (c *meshAuthCredentials) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { - ret := map[string]string{ - "x-goog-user-project": c.project, - } - if err := updateAuthHdrs(ctx, uri, "k8s", c.k8sCreds, ret, "x-mesh-authorization"); err != nil { - return nil, err - } - if err := updateAuthHdrs(ctx, uri, "gcp", c.gcpCreds, ret, "authorization"); err != nil { - return nil, err - } - return ret, nil -} - -func (*meshAuthCredentials) RequireTransportSecurity() bool { - return true -} - -func updateAuthHdrs(ctx context.Context, uri []string, kind string, creds credentials.PerRPCCredentials, dst map[string]string, dstHdr string) error { - ret, err := creds.GetRequestMetadata(ctx, uri...) - if err != nil { - return err - } - for k, v := range ret { - if !strings.EqualFold(k, "authorization") { - if _, ok := dst[k]; ok { - return fmt.Errorf("underlying %s credentials contain a %s header which is already present in the combined credentials", kind, k) - } - dst[k] = v - } else { - dst[dstHdr] = v - } - } - return nil -} - -type hubMembership struct { - WorkloadIdentityPool string -} - -func getHubMembership(ctx context.Context, exClient kube.ExtendedClient) (*hubMembership, error) { - client := exClient.Dynamic() - gvr := schema.GroupVersionResource{ - Group: "hub.gke.io", - Version: "v1", - Resource: "memberships", - } - u, err := client.Resource(gvr).Get(ctx, "membership", metav1.GetOptions{}) - if err != nil { - return nil, err - } - spec, ok := u.Object["spec"].(map[string]interface{}) - if !ok { - return nil, errors.New(`field "spec" is not a map`) - } - var mem hubMembership - mem.WorkloadIdentityPool, ok = spec["workload_identity_pool"].(string) - if !ok { - return nil, errors.New(`field "spec.workload_identity_pool" is not a string`) - } - return &mem, nil -} - -func mcpDialOptions(ctx context.Context, gcpProject string, k8sCreds credentials.PerRPCCredentials) ([]grpc.DialOption, error) { - systemRoots, err := x509.SystemCertPool() - if err != nil { - return nil, fmt.Errorf("failed to get system cert pool: %w", err) - } - gcpCreds, err := oauth.NewApplicationDefault(ctx) - if err != nil { - return nil, fmt.Errorf("failed to get application default credentials: %w", err) - } - - return []grpc.DialOption{ - grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ - RootCAs: systemRoots, - })), - grpc.WithPerRPCCredentials(&meshAuthCredentials{ - k8sCreds: k8sCreds, - gcpCreds: gcpCreds, - project: gcpProject, - }), - }, nil -} diff --git a/licenses/cloud.google.com/go/LICENSE b/licenses/cloud.google.com/go/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/cloud.google.com/go/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/cloud.google.com/go/compute/LICENSE b/licenses/cloud.google.com/go/compute/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/cloud.google.com/go/compute/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/cloud.google.com/go/logging/LICENSE b/licenses/cloud.google.com/go/logging/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/cloud.google.com/go/logging/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/cloud.google.com/go/security/LICENSE b/licenses/cloud.google.com/go/security/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/cloud.google.com/go/security/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/cloud.google.com/go/third_party/pkgsite/LICENSE b/licenses/cloud.google.com/go/third_party/pkgsite/LICENSE deleted file mode 100644 index 244127300..000000000 --- a/licenses/cloud.google.com/go/third_party/pkgsite/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2020 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/contrib.go.opencensus.io/exporter/prometheus/LICENSE b/licenses/contrib.go.opencensus.io/exporter/prometheus/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/contrib.go.opencensus.io/exporter/prometheus/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/dubbo.apache.org/dubbo-go/v3/LICENSE b/licenses/dubbo.apache.org/dubbo-go/v3/LICENSE deleted file mode 100644 index 75b52484e..000000000 --- a/licenses/dubbo.apache.org/dubbo-go/v3/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/AdaLogics/go-fuzz-headers/LICENSE b/licenses/github.com/AdaLogics/go-fuzz-headers/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/github.com/AdaLogics/go-fuzz-headers/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/Azure/go-autorest/autorest/LICENSE b/licenses/github.com/Azure/go-autorest/autorest/LICENSE deleted file mode 100644 index b9d6a27ea..000000000 --- a/licenses/github.com/Azure/go-autorest/autorest/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2015 Microsoft Corporation - - 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. diff --git a/licenses/github.com/Azure/go-autorest/autorest/adal/LICENSE b/licenses/github.com/Azure/go-autorest/autorest/adal/LICENSE deleted file mode 100644 index b9d6a27ea..000000000 --- a/licenses/github.com/Azure/go-autorest/autorest/adal/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2015 Microsoft Corporation - - 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. diff --git a/licenses/github.com/Azure/go-autorest/autorest/date/LICENSE b/licenses/github.com/Azure/go-autorest/autorest/date/LICENSE deleted file mode 100644 index b9d6a27ea..000000000 --- a/licenses/github.com/Azure/go-autorest/autorest/date/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2015 Microsoft Corporation - - 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. diff --git a/licenses/github.com/Azure/go-autorest/logger/LICENSE b/licenses/github.com/Azure/go-autorest/logger/LICENSE deleted file mode 100644 index b9d6a27ea..000000000 --- a/licenses/github.com/Azure/go-autorest/logger/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2015 Microsoft Corporation - - 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. diff --git a/licenses/github.com/Azure/go-autorest/tracing/LICENSE b/licenses/github.com/Azure/go-autorest/tracing/LICENSE deleted file mode 100644 index b9d6a27ea..000000000 --- a/licenses/github.com/Azure/go-autorest/tracing/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2015 Microsoft Corporation - - 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. diff --git a/licenses/github.com/BurntSushi/toml/COPYING b/licenses/github.com/BurntSushi/toml/COPYING deleted file mode 100644 index 01b574320..000000000 --- a/licenses/github.com/BurntSushi/toml/COPYING +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 TOML authors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING b/licenses/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING deleted file mode 100644 index 01b574320..000000000 --- a/licenses/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 TOML authors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING b/licenses/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING deleted file mode 100644 index 01b574320..000000000 --- a/licenses/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 TOML authors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/BurntSushi/toml/cmd/tomlv/COPYING b/licenses/github.com/BurntSushi/toml/cmd/tomlv/COPYING deleted file mode 100644 index 01b574320..000000000 --- a/licenses/github.com/BurntSushi/toml/cmd/tomlv/COPYING +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 TOML authors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/BurntSushi/toml/internal/toml-test/COPYING b/licenses/github.com/BurntSushi/toml/internal/toml-test/COPYING deleted file mode 100644 index 93b22020a..000000000 --- a/licenses/github.com/BurntSushi/toml/internal/toml-test/COPYING +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2018 TOML authors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/MakeNowJust/heredoc/LICENSE b/licenses/github.com/MakeNowJust/heredoc/LICENSE deleted file mode 100644 index 6d0eb9d5d..000000000 --- a/licenses/github.com/MakeNowJust/heredoc/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014-2019 TSUYUSATO Kitsune - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/Masterminds/goutils/LICENSE.txt b/licenses/github.com/Masterminds/goutils/LICENSE.txt deleted file mode 100644 index d64569567..000000000 --- a/licenses/github.com/Masterminds/goutils/LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/Masterminds/semver/v3/LICENSE.txt b/licenses/github.com/Masterminds/semver/v3/LICENSE.txt deleted file mode 100644 index 9ff7da9c4..000000000 --- a/licenses/github.com/Masterminds/semver/v3/LICENSE.txt +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (C) 2014-2019, Matt Butcher and Matt Farina - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/Masterminds/sprig/v3/LICENSE.txt b/licenses/github.com/Masterminds/sprig/v3/LICENSE.txt deleted file mode 100644 index f311b1eaa..000000000 --- a/licenses/github.com/Masterminds/sprig/v3/LICENSE.txt +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (C) 2013-2020 Masterminds - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/MicahParks/keyfunc/LICENSE b/licenses/github.com/MicahParks/keyfunc/LICENSE deleted file mode 100644 index 06dd4f210..000000000 --- a/licenses/github.com/MicahParks/keyfunc/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2021 Micah Parks - - 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. diff --git a/licenses/github.com/PuerkitoBio/purell/LICENSE b/licenses/github.com/PuerkitoBio/purell/LICENSE deleted file mode 100644 index 4b9986dea..000000000 --- a/licenses/github.com/PuerkitoBio/purell/LICENSE +++ /dev/null @@ -1,12 +0,0 @@ -Copyright (c) 2012, Martin Angers -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/PuerkitoBio/urlesc/LICENSE b/licenses/github.com/PuerkitoBio/urlesc/LICENSE deleted file mode 100644 index 744875676..000000000 --- a/licenses/github.com/PuerkitoBio/urlesc/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2012 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/RoaringBitmap/roaring/LICENSE b/licenses/github.com/RoaringBitmap/roaring/LICENSE deleted file mode 100644 index 3ccdd0008..000000000 --- a/licenses/github.com/RoaringBitmap/roaring/LICENSE +++ /dev/null @@ -1,235 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2016 by the authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -================================================================================ - -Portions of runcontainer.go are from the Go standard library, which is licensed -under: - -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/Shopify/sarama/LICENSE b/licenses/github.com/Shopify/sarama/LICENSE deleted file mode 100644 index d2bf4352f..000000000 --- a/licenses/github.com/Shopify/sarama/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2013 Shopify - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/VividCortex/ewma/LICENSE b/licenses/github.com/VividCortex/ewma/LICENSE deleted file mode 100644 index a78d643ed..000000000 --- a/licenses/github.com/VividCortex/ewma/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License - -Copyright (c) 2013 VividCortex - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/Workiva/go-datastructures/LICENSE b/licenses/github.com/Workiva/go-datastructures/LICENSE deleted file mode 100644 index 7a4a3ea24..000000000 --- a/licenses/github.com/Workiva/go-datastructures/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. \ No newline at end of file diff --git a/licenses/github.com/alibaba/sentinel-golang/LICENSE b/licenses/github.com/alibaba/sentinel-golang/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/github.com/alibaba/sentinel-golang/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/aliyun/alibaba-cloud-sdk-go/LICENSE b/licenses/github.com/aliyun/alibaba-cloud-sdk-go/LICENSE deleted file mode 100644 index 0c44dcefe..000000000 --- a/licenses/github.com/aliyun/alibaba-cloud-sdk-go/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright (c) 2009-present, Alibaba Cloud 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. diff --git a/licenses/github.com/antlr/antlr4/runtime/Go/antlr/LICENSE b/licenses/github.com/antlr/antlr4/runtime/Go/antlr/LICENSE deleted file mode 100644 index 52cf18e42..000000000 --- a/licenses/github.com/antlr/antlr4/runtime/Go/antlr/LICENSE +++ /dev/null @@ -1,26 +0,0 @@ -Copyright 2021 The ANTLR Project - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - 3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/apache/dubbo-getty/LICENSE b/licenses/github.com/apache/dubbo-getty/LICENSE deleted file mode 100644 index fc9119800..000000000 --- a/licenses/github.com/apache/dubbo-getty/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2016 ~ 2018 Alex Stocks. - - 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. diff --git a/licenses/github.com/apache/dubbo-getty/examples/LICENSE b/licenses/github.com/apache/dubbo-getty/examples/LICENSE deleted file mode 100644 index 0c1183182..000000000 --- a/licenses/github.com/apache/dubbo-getty/examples/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2016 ~ 2019 Alex Stocks. - - 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. diff --git a/licenses/github.com/apache/dubbo-go-hessian2/LICENSE b/licenses/github.com/apache/dubbo-go-hessian2/LICENSE deleted file mode 100644 index 7dd1e2bee..000000000 --- a/licenses/github.com/apache/dubbo-go-hessian2/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/asaskevich/govalidator/LICENSE b/licenses/github.com/asaskevich/govalidator/LICENSE deleted file mode 100644 index 2f9a31fad..000000000 --- a/licenses/github.com/asaskevich/govalidator/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Alex Saskevich - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/licenses/github.com/beorn7/perks/LICENSE b/licenses/github.com/beorn7/perks/LICENSE deleted file mode 100644 index 339177be6..000000000 --- a/licenses/github.com/beorn7/perks/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (C) 2013 Blake Mizerany - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/blang/semver/v4/LICENSE b/licenses/github.com/blang/semver/v4/LICENSE deleted file mode 100644 index 5ba5c86fc..000000000 --- a/licenses/github.com/blang/semver/v4/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License - -Copyright (c) 2014 Benedikt Lang - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - diff --git a/licenses/github.com/buger/jsonparser/LICENSE b/licenses/github.com/buger/jsonparser/LICENSE deleted file mode 100644 index ac25aeb7d..000000000 --- a/licenses/github.com/buger/jsonparser/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016 Leonid Bugaev - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/cch123/supermonkey/LICENSE b/licenses/github.com/cch123/supermonkey/LICENSE deleted file mode 100644 index 96e9dafe0..000000000 --- a/licenses/github.com/cch123/supermonkey/LICENSE +++ /dev/null @@ -1,8 +0,0 @@ -The MIT License (MIT) -Copyright © 2020 Xargin - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Softwareâ€), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS ISâ€, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/cenkalti/backoff/v4/LICENSE b/licenses/github.com/cenkalti/backoff/v4/LICENSE deleted file mode 100644 index 89b817996..000000000 --- a/licenses/github.com/cenkalti/backoff/v4/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Cenk Altı - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/census-instrumentation/opencensus-proto/LICENSE b/licenses/github.com/census-instrumentation/opencensus-proto/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/github.com/census-instrumentation/opencensus-proto/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/cespare/xxhash/LICENSE.txt b/licenses/github.com/cespare/xxhash/LICENSE.txt deleted file mode 100644 index 24b53065f..000000000 --- a/licenses/github.com/cespare/xxhash/LICENSE.txt +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2016 Caleb Spare - -MIT License - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/cespare/xxhash/v2/LICENSE.txt b/licenses/github.com/cespare/xxhash/v2/LICENSE.txt deleted file mode 100644 index 24b53065f..000000000 --- a/licenses/github.com/cespare/xxhash/v2/LICENSE.txt +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2016 Caleb Spare - -MIT License - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/chai2010/gettext-go/LICENSE b/licenses/github.com/chai2010/gettext-go/LICENSE deleted file mode 100644 index 8f3940825..000000000 --- a/licenses/github.com/chai2010/gettext-go/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright 2013 ChaiShushan . All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/cheekybits/genny/LICENSE b/licenses/github.com/cheekybits/genny/LICENSE deleted file mode 100644 index 519d7f227..000000000 --- a/licenses/github.com/cheekybits/genny/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 cheekybits - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/licenses/github.com/cheggaaa/pb/v3/LICENSE b/licenses/github.com/cheggaaa/pb/v3/LICENSE deleted file mode 100644 index 511970333..000000000 --- a/licenses/github.com/cheggaaa/pb/v3/LICENSE +++ /dev/null @@ -1,12 +0,0 @@ -Copyright (c) 2012-2015, Sergey Cherepanov -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/licenses/github.com/cncf/udpa/go/LICENSE b/licenses/github.com/cncf/udpa/go/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/github.com/cncf/udpa/go/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/cncf/xds/go/LICENSE b/licenses/github.com/cncf/xds/go/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/github.com/cncf/xds/go/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/containerd/stargz-snapshotter/estargz/LICENSE b/licenses/github.com/containerd/stargz-snapshotter/estargz/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/github.com/containerd/stargz-snapshotter/estargz/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/containerd/typeurl/LICENSE b/licenses/github.com/containerd/typeurl/LICENSE deleted file mode 100644 index 584149b6e..000000000 --- a/licenses/github.com/containerd/typeurl/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright The containerd Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://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. diff --git a/licenses/github.com/coreos/go-oidc/v3/LICENSE b/licenses/github.com/coreos/go-oidc/v3/LICENSE deleted file mode 100644 index e06d20818..000000000 --- a/licenses/github.com/coreos/go-oidc/v3/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. - diff --git a/licenses/github.com/coreos/go-semver/LICENSE b/licenses/github.com/coreos/go-semver/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/github.com/coreos/go-semver/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/coreos/go-systemd/v22/LICENSE b/licenses/github.com/coreos/go-systemd/v22/LICENSE deleted file mode 100644 index 37ec93a14..000000000 --- a/licenses/github.com/coreos/go-systemd/v22/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/cpuguy83/go-md2man/v2/LICENSE.md b/licenses/github.com/cpuguy83/go-md2man/v2/LICENSE.md deleted file mode 100644 index 1cade6cef..000000000 --- a/licenses/github.com/cpuguy83/go-md2man/v2/LICENSE.md +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Brian Goff - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/creasty/defaults/LICENSE b/licenses/github.com/creasty/defaults/LICENSE deleted file mode 100644 index 1483dd2d8..000000000 --- a/licenses/github.com/creasty/defaults/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2017-present Yuki Iwanaga - -MIT License - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/cyphar/filepath-securejoin/LICENSE b/licenses/github.com/cyphar/filepath-securejoin/LICENSE deleted file mode 100644 index bec842f29..000000000 --- a/licenses/github.com/cyphar/filepath-securejoin/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved. -Copyright (C) 2017 SUSE LLC. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/davecgh/go-spew/LICENSE b/licenses/github.com/davecgh/go-spew/LICENSE deleted file mode 100644 index bc52e96f2..000000000 --- a/licenses/github.com/davecgh/go-spew/LICENSE +++ /dev/null @@ -1,15 +0,0 @@ -ISC License - -Copyright (c) 2012-2016 Dave Collins - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/licenses/github.com/docker/cli/LICENSE b/licenses/github.com/docker/cli/LICENSE deleted file mode 100644 index 9c8e20ab8..000000000 --- a/licenses/github.com/docker/cli/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2013-2017 Docker, Inc. - - 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 - - https://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. diff --git a/licenses/github.com/docker/distribution/LICENSE b/licenses/github.com/docker/distribution/LICENSE deleted file mode 100644 index e06d20818..000000000 --- a/licenses/github.com/docker/distribution/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. - diff --git a/licenses/github.com/docker/docker-credential-helpers/LICENSE b/licenses/github.com/docker/docker-credential-helpers/LICENSE deleted file mode 100644 index 1ea555e2a..000000000 --- a/licenses/github.com/docker/docker-credential-helpers/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2016 David Calavera - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/docker/docker/LICENSE b/licenses/github.com/docker/docker/LICENSE deleted file mode 100644 index 6d8d58fb6..000000000 --- a/licenses/github.com/docker/docker/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2013-2018 Docker, Inc. - - 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 - - https://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. diff --git a/licenses/github.com/docker/docker/contrib/busybox/LICENSE b/licenses/github.com/docker/docker/contrib/busybox/LICENSE deleted file mode 100644 index 178d79909..000000000 --- a/licenses/github.com/docker/docker/contrib/busybox/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 John Howard (Microsoft) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/dubbogo/go-zookeeper/LICENSE b/licenses/github.com/dubbogo/go-zookeeper/LICENSE deleted file mode 100644 index bc00498c5..000000000 --- a/licenses/github.com/dubbogo/go-zookeeper/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2013, Samuel Stauffer -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -* Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. -* Neither the name of the author nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/dubbogo/gost/LICENSE b/licenses/github.com/dubbogo/gost/LICENSE deleted file mode 100644 index 2c4b8470f..000000000 --- a/licenses/github.com/dubbogo/gost/LICENSE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS diff --git a/licenses/github.com/dubbogo/grpc-go/LICENSE b/licenses/github.com/dubbogo/grpc-go/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/github.com/dubbogo/grpc-go/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/dubbogo/triple/NONE b/licenses/github.com/dubbogo/triple/NONE deleted file mode 100644 index 10e0e777e..000000000 --- a/licenses/github.com/dubbogo/triple/NONE +++ /dev/null @@ -1 +0,0 @@ -NO LICENSE FOUND diff --git a/licenses/github.com/eapache/go-resiliency/LICENSE b/licenses/github.com/eapache/go-resiliency/LICENSE deleted file mode 100644 index 698a3f513..000000000 --- a/licenses/github.com/eapache/go-resiliency/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Evan Huus - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/licenses/github.com/eapache/go-xerial-snappy/LICENSE b/licenses/github.com/eapache/go-xerial-snappy/LICENSE deleted file mode 100644 index 5bf3688d9..000000000 --- a/licenses/github.com/eapache/go-xerial-snappy/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Evan Huus - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/eapache/queue/LICENSE b/licenses/github.com/eapache/queue/LICENSE deleted file mode 100644 index d5f36dbca..000000000 --- a/licenses/github.com/eapache/queue/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Evan Huus - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/licenses/github.com/emicklei/go-restful/v3/LICENSE b/licenses/github.com/emicklei/go-restful/v3/LICENSE deleted file mode 100644 index ece7ec61e..000000000 --- a/licenses/github.com/emicklei/go-restful/v3/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2012,2013 Ernest Micklei - -MIT License - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/licenses/github.com/envoyproxy/go-control-plane/LICENSE b/licenses/github.com/envoyproxy/go-control-plane/LICENSE deleted file mode 100644 index 8dada3eda..000000000 --- a/licenses/github.com/envoyproxy/go-control-plane/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. diff --git a/licenses/github.com/envoyproxy/protoc-gen-validate/LICENSE b/licenses/github.com/envoyproxy/protoc-gen-validate/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/github.com/envoyproxy/protoc-gen-validate/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/evanphx/json-patch/LICENSE b/licenses/github.com/evanphx/json-patch/LICENSE deleted file mode 100644 index df76d7d77..000000000 --- a/licenses/github.com/evanphx/json-patch/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2014, Evan Phoenix -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. -* Neither the name of the Evan Phoenix nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/evanphx/json-patch/v5/LICENSE b/licenses/github.com/evanphx/json-patch/v5/LICENSE deleted file mode 100644 index df76d7d77..000000000 --- a/licenses/github.com/evanphx/json-patch/v5/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2014, Evan Phoenix -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. -* Neither the name of the Evan Phoenix nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/exponent-io/jsonpath/LICENSE b/licenses/github.com/exponent-io/jsonpath/LICENSE deleted file mode 100644 index 541977250..000000000 --- a/licenses/github.com/exponent-io/jsonpath/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Exponent Labs LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/fatih/camelcase/LICENSE.md b/licenses/github.com/fatih/camelcase/LICENSE.md deleted file mode 100644 index aa4a536ca..000000000 --- a/licenses/github.com/fatih/camelcase/LICENSE.md +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Fatih Arslan - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/fatih/color/LICENSE.md b/licenses/github.com/fatih/color/LICENSE.md deleted file mode 100644 index 25fdaf639..000000000 --- a/licenses/github.com/fatih/color/LICENSE.md +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 Fatih Arslan - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/felixge/fgprof/LICENSE.txt b/licenses/github.com/felixge/fgprof/LICENSE.txt deleted file mode 100644 index 3e424911b..000000000 --- a/licenses/github.com/felixge/fgprof/LICENSE.txt +++ /dev/null @@ -1,8 +0,0 @@ -The MIT License (MIT) -Copyright © 2020 Felix Geisendörfer - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Softwareâ€), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS ISâ€, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/florianl/go-nflog/v2/LICENSE b/licenses/github.com/florianl/go-nflog/v2/LICENSE deleted file mode 100644 index ce183b02d..000000000 --- a/licenses/github.com/florianl/go-nflog/v2/LICENSE +++ /dev/null @@ -1,10 +0,0 @@ -MIT License -=========== - -Copyright (C) 2018-2020 Florian Lehner - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/fsnotify/fsnotify/LICENSE b/licenses/github.com/fsnotify/fsnotify/LICENSE deleted file mode 100644 index e180c8fb0..000000000 --- a/licenses/github.com/fsnotify/fsnotify/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (c) 2012 The Go Authors. All rights reserved. -Copyright (c) 2012-2019 fsnotify Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/fvbommel/sortorder/LICENSE b/licenses/github.com/fvbommel/sortorder/LICENSE deleted file mode 100644 index 5c695fb59..000000000 --- a/licenses/github.com/fvbommel/sortorder/LICENSE +++ /dev/null @@ -1,17 +0,0 @@ -The MIT License (MIT) -Copyright (c) 2015 Frits van Bommel -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/ghodss/yaml/LICENSE b/licenses/github.com/ghodss/yaml/LICENSE deleted file mode 100644 index 7805d36de..000000000 --- a/licenses/github.com/ghodss/yaml/LICENSE +++ /dev/null @@ -1,50 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Sam Ghods - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - -Copyright (c) 2012 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/go-errors/errors/NONE b/licenses/github.com/go-errors/errors/NONE deleted file mode 100644 index 10e0e777e..000000000 --- a/licenses/github.com/go-errors/errors/NONE +++ /dev/null @@ -1 +0,0 @@ -NO LICENSE FOUND diff --git a/licenses/github.com/go-kit/log/LICENSE b/licenses/github.com/go-kit/log/LICENSE deleted file mode 100644 index bb5bdb9cb..000000000 --- a/licenses/github.com/go-kit/log/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021 Go kit - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/go-kit/log/term/LICENSE b/licenses/github.com/go-kit/log/term/LICENSE deleted file mode 100644 index f090cb42f..000000000 --- a/licenses/github.com/go-kit/log/term/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Simon Eskildsen - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/go-logfmt/logfmt/LICENSE b/licenses/github.com/go-logfmt/logfmt/LICENSE deleted file mode 100644 index c02650896..000000000 --- a/licenses/github.com/go-logfmt/logfmt/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 go-logfmt - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/licenses/github.com/go-logr/logr/LICENSE b/licenses/github.com/go-logr/logr/LICENSE deleted file mode 100644 index 8dada3eda..000000000 --- a/licenses/github.com/go-logr/logr/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. diff --git a/licenses/github.com/go-logr/stdr/LICENSE b/licenses/github.com/go-logr/stdr/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/github.com/go-logr/stdr/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/go-openapi/jsonpointer/LICENSE b/licenses/github.com/go-openapi/jsonpointer/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/github.com/go-openapi/jsonpointer/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/go-openapi/jsonreference/LICENSE b/licenses/github.com/go-openapi/jsonreference/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/github.com/go-openapi/jsonreference/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/go-openapi/swag/LICENSE b/licenses/github.com/go-openapi/swag/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/github.com/go-openapi/swag/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/go-playground/assert/v2/LICENSE b/licenses/github.com/go-playground/assert/v2/LICENSE deleted file mode 100644 index 6a2ae9aa4..000000000 --- a/licenses/github.com/go-playground/assert/v2/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Dean Karn - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/licenses/github.com/go-playground/locales/LICENSE b/licenses/github.com/go-playground/locales/LICENSE deleted file mode 100644 index 75854ac4f..000000000 --- a/licenses/github.com/go-playground/locales/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Go Playground - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/licenses/github.com/go-playground/universal-translator/LICENSE b/licenses/github.com/go-playground/universal-translator/LICENSE deleted file mode 100644 index 8d8aba15b..000000000 --- a/licenses/github.com/go-playground/universal-translator/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Go Playground - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/go-playground/validator/v10/LICENSE b/licenses/github.com/go-playground/validator/v10/LICENSE deleted file mode 100644 index 6a2ae9aa4..000000000 --- a/licenses/github.com/go-playground/validator/v10/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Dean Karn - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/licenses/github.com/go-resty/resty/v2/LICENSE b/licenses/github.com/go-resty/resty/v2/LICENSE deleted file mode 100644 index 27326a653..000000000 --- a/licenses/github.com/go-resty/resty/v2/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015-2021 Jeevanandam M., https://myjeeva.com - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/gobwas/glob/LICENSE b/licenses/github.com/gobwas/glob/LICENSE deleted file mode 100644 index 9d4735cad..000000000 --- a/licenses/github.com/gobwas/glob/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Sergey Kamardin - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/licenses/github.com/gogo/protobuf/LICENSE b/licenses/github.com/gogo/protobuf/LICENSE deleted file mode 100644 index f57de90da..000000000 --- a/licenses/github.com/gogo/protobuf/LICENSE +++ /dev/null @@ -1,35 +0,0 @@ -Copyright (c) 2013, The GoGo Authors. All rights reserved. - -Protocol Buffers for Go with Gadgets - -Go support for Protocol Buffers - Google's data interchange format - -Copyright 2010 The Go Authors. All rights reserved. -https://github.com/golang/protobuf - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/licenses/github.com/goinggo/mapstructure/LICENSE b/licenses/github.com/goinggo/mapstructure/LICENSE deleted file mode 100644 index 1622abe69..000000000 --- a/licenses/github.com/goinggo/mapstructure/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 Mitchell Hashimoto, William Kennedy - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/golang-jwt/jwt/v4/LICENSE b/licenses/github.com/golang-jwt/jwt/v4/LICENSE deleted file mode 100644 index 35dbc2520..000000000 --- a/licenses/github.com/golang-jwt/jwt/v4/LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -Copyright (c) 2012 Dave Grijalva -Copyright (c) 2021 golang-jwt maintainers - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/licenses/github.com/golang/groupcache/LICENSE b/licenses/github.com/golang/groupcache/LICENSE deleted file mode 100644 index 37ec93a14..000000000 --- a/licenses/github.com/golang/groupcache/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/golang/mock/LICENSE b/licenses/github.com/golang/mock/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/github.com/golang/mock/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/golang/protobuf/LICENSE b/licenses/github.com/golang/protobuf/LICENSE deleted file mode 100644 index 0f646931a..000000000 --- a/licenses/github.com/golang/protobuf/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ -Copyright 2010 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/licenses/github.com/golang/snappy/LICENSE b/licenses/github.com/golang/snappy/LICENSE deleted file mode 100644 index 6050c10f4..000000000 --- a/licenses/github.com/golang/snappy/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/google/btree/LICENSE b/licenses/github.com/google/btree/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/github.com/google/btree/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/google/cel-go/LICENSE b/licenses/github.com/google/cel-go/LICENSE deleted file mode 100644 index 2493ed2eb..000000000 --- a/licenses/github.com/google/cel-go/LICENSE +++ /dev/null @@ -1,233 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. - -=========================================================================== -The common/types/pb/equal.go modification of proto.Equal logic -=========================================================================== -Copyright (c) 2018 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/google/gnostic/LICENSE b/licenses/github.com/google/gnostic/LICENSE deleted file mode 100644 index 6b0b1270f..000000000 --- a/licenses/github.com/google/gnostic/LICENSE +++ /dev/null @@ -1,203 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. - diff --git a/licenses/github.com/google/go-cmp/LICENSE b/licenses/github.com/google/go-cmp/LICENSE deleted file mode 100644 index 32017f8fa..000000000 --- a/licenses/github.com/google/go-cmp/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2017 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/google/go-containerregistry/LICENSE b/licenses/github.com/google/go-containerregistry/LICENSE deleted file mode 100644 index 7a4a3ea24..000000000 --- a/licenses/github.com/google/go-containerregistry/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. \ No newline at end of file diff --git a/licenses/github.com/google/gofuzz/LICENSE b/licenses/github.com/google/gofuzz/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/github.com/google/gofuzz/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/google/pprof/LICENSE b/licenses/github.com/google/pprof/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/github.com/google/pprof/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/google/pprof/third_party/svgpan/LICENSE b/licenses/github.com/google/pprof/third_party/svgpan/LICENSE deleted file mode 100644 index 35bc17455..000000000 --- a/licenses/github.com/google/pprof/third_party/svgpan/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright 2009-2017 Andrea Leofreddi . All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are -permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS -OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -The views and conclusions contained in the software and documentation are those of the -authors and should not be interpreted as representing official policies, either expressed -or implied, of Andrea Leofreddi. diff --git a/licenses/github.com/google/shlex/COPYING b/licenses/github.com/google/shlex/COPYING deleted file mode 100644 index d64569567..000000000 --- a/licenses/github.com/google/shlex/COPYING +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/google/uuid/LICENSE b/licenses/github.com/google/uuid/LICENSE deleted file mode 100644 index 5dc68268d..000000000 --- a/licenses/github.com/google/uuid/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009,2014 Google Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/googleapis/gax-go/v2/LICENSE b/licenses/github.com/googleapis/gax-go/v2/LICENSE deleted file mode 100644 index 6d16b6578..000000000 --- a/licenses/github.com/googleapis/gax-go/v2/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright 2016, Google Inc. -All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/gorilla/mux/LICENSE b/licenses/github.com/gorilla/mux/LICENSE deleted file mode 100644 index 6903df638..000000000 --- a/licenses/github.com/gorilla/mux/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2012-2018 The Gorilla Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/gorilla/websocket/LICENSE b/licenses/github.com/gorilla/websocket/LICENSE deleted file mode 100644 index 9171c9722..000000000 --- a/licenses/github.com/gorilla/websocket/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/gregjones/httpcache/LICENSE.txt b/licenses/github.com/gregjones/httpcache/LICENSE.txt deleted file mode 100644 index 81316beb0..000000000 --- a/licenses/github.com/gregjones/httpcache/LICENSE.txt +++ /dev/null @@ -1,7 +0,0 @@ -Copyright © 2012 Greg Jones (greg.jones@gmail.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Softwareâ€), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS ISâ€, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/licenses/github.com/grpc-ecosystem/go-grpc-middleware/LICENSE b/licenses/github.com/grpc-ecosystem/go-grpc-middleware/LICENSE deleted file mode 100644 index b2b065037..000000000 --- a/licenses/github.com/grpc-ecosystem/go-grpc-middleware/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. \ No newline at end of file diff --git a/licenses/github.com/grpc-ecosystem/go-grpc-prometheus/LICENSE b/licenses/github.com/grpc-ecosystem/go-grpc-prometheus/LICENSE deleted file mode 100644 index b2b065037..000000000 --- a/licenses/github.com/grpc-ecosystem/go-grpc-prometheus/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. \ No newline at end of file diff --git a/licenses/github.com/grpc-ecosystem/grpc-gateway/v2/LICENSE.txt b/licenses/github.com/grpc-ecosystem/grpc-gateway/v2/LICENSE.txt deleted file mode 100644 index 364516251..000000000 --- a/licenses/github.com/grpc-ecosystem/grpc-gateway/v2/LICENSE.txt +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2015, Gengo, Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither the name of Gengo, Inc. nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/grpc-ecosystem/grpc-gateway/v2/internal/casing/LICENSE.md b/licenses/github.com/grpc-ecosystem/grpc-gateway/v2/internal/casing/LICENSE.md deleted file mode 100644 index 0f646931a..000000000 --- a/licenses/github.com/grpc-ecosystem/grpc-gateway/v2/internal/casing/LICENSE.md +++ /dev/null @@ -1,28 +0,0 @@ -Copyright 2010 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/licenses/github.com/hashicorp/errwrap/LICENSE b/licenses/github.com/hashicorp/errwrap/LICENSE deleted file mode 100644 index c33dcc7c9..000000000 --- a/licenses/github.com/hashicorp/errwrap/LICENSE +++ /dev/null @@ -1,354 +0,0 @@ -Mozilla Public License, version 2.0 - -1. Definitions - -1.1. “Contributor†- - means each individual or legal entity that creates, contributes to the - creation of, or owns Covered Software. - -1.2. “Contributor Version†- - means the combination of the Contributions of others (if any) used by a - Contributor and that particular Contributor’s Contribution. - -1.3. “Contribution†- - means Covered Software of a particular Contributor. - -1.4. “Covered Software†- - means Source Code Form to which the initial Contributor has attached the - notice in Exhibit A, the Executable Form of such Source Code Form, and - Modifications of such Source Code Form, in each case including portions - thereof. - -1.5. “Incompatible With Secondary Licenses†- means - - a. that the initial Contributor has attached the notice described in - Exhibit B to the Covered Software; or - - b. that the Covered Software was made available under the terms of version - 1.1 or earlier of the License, but not also under the terms of a - Secondary License. - -1.6. “Executable Form†- - means any form of the work other than Source Code Form. - -1.7. “Larger Work†- - means a work that combines Covered Software with other material, in a separate - file or files, that is not Covered Software. - -1.8. “License†- - means this document. - -1.9. “Licensable†- - means having the right to grant, to the maximum extent possible, whether at the - time of the initial grant or subsequently, any and all of the rights conveyed by - this License. - -1.10. “Modifications†- - means any of the following: - - a. any file in Source Code Form that results from an addition to, deletion - from, or modification of the contents of Covered Software; or - - b. any new file in Source Code Form that contains any Covered Software. - -1.11. “Patent Claims†of a Contributor - - means any patent claim(s), including without limitation, method, process, - and apparatus claims, in any patent Licensable by such Contributor that - would be infringed, but for the grant of the License, by the making, - using, selling, offering for sale, having made, import, or transfer of - either its Contributions or its Contributor Version. - -1.12. “Secondary License†- - means either the GNU General Public License, Version 2.0, the GNU Lesser - General Public License, Version 2.1, the GNU Affero General Public - License, Version 3.0, or any later versions of those licenses. - -1.13. “Source Code Form†- - means the form of the work preferred for making modifications. - -1.14. “You†(or “Yourâ€) - - means an individual or a legal entity exercising rights under this - License. For legal entities, “You†includes any entity that controls, is - controlled by, or is under common control with You. For purposes of this - definition, “control†means (a) the power, direct or indirect, to cause - the direction or management of such entity, whether by contract or - otherwise, or (b) ownership of more than fifty percent (50%) of the - outstanding shares or beneficial ownership of such entity. - - -2. License Grants and Conditions - -2.1. Grants - - Each Contributor hereby grants You a world-wide, royalty-free, - non-exclusive license: - - a. under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or as - part of a Larger Work; and - - b. under Patent Claims of such Contributor to make, use, sell, offer for - sale, have made, import, and otherwise transfer either its Contributions - or its Contributor Version. - -2.2. Effective Date - - The licenses granted in Section 2.1 with respect to any Contribution become - effective for each Contribution on the date the Contributor first distributes - such Contribution. - -2.3. Limitations on Grant Scope - - The licenses granted in this Section 2 are the only rights granted under this - License. No additional rights or licenses will be implied from the distribution - or licensing of Covered Software under this License. Notwithstanding Section - 2.1(b) above, no patent license is granted by a Contributor: - - a. for any code that a Contributor has removed from Covered Software; or - - b. for infringements caused by: (i) Your and any other third party’s - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - - c. under Patent Claims infringed by Covered Software in the absence of its - Contributions. - - This License does not grant any rights in the trademarks, service marks, or - logos of any Contributor (except as may be necessary to comply with the - notice requirements in Section 3.4). - -2.4. Subsequent Licenses - - No Contributor makes additional grants as a result of Your choice to - distribute the Covered Software under a subsequent version of this License - (see Section 10.2) or under the terms of a Secondary License (if permitted - under the terms of Section 3.3). - -2.5. Representation - - Each Contributor represents that the Contributor believes its Contributions - are its original creation(s) or it has sufficient rights to grant the - rights to its Contributions conveyed by this License. - -2.6. Fair Use - - This License is not intended to limit any rights You have under applicable - copyright doctrines of fair use, fair dealing, or other equivalents. - -2.7. Conditions - - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in - Section 2.1. - - -3. Responsibilities - -3.1. Distribution of Source Form - - All distribution of Covered Software in Source Code Form, including any - Modifications that You create or to which You contribute, must be under the - terms of this License. You must inform recipients that the Source Code Form - of the Covered Software is governed by the terms of this License, and how - they can obtain a copy of this License. You may not attempt to alter or - restrict the recipients’ rights in the Source Code Form. - -3.2. Distribution of Executable Form - - If You distribute Covered Software in Executable Form then: - - a. such Covered Software must also be made available in Source Code Form, - as described in Section 3.1, and You must inform recipients of the - Executable Form how they can obtain a copy of such Source Code Form by - reasonable means in a timely manner, at a charge no more than the cost - of distribution to the recipient; and - - b. You may distribute such Executable Form under the terms of this License, - or sublicense it under different terms, provided that the license for - the Executable Form does not attempt to limit or alter the recipients’ - rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - - You may create and distribute a Larger Work under terms of Your choice, - provided that You also comply with the requirements of this License for the - Covered Software. If the Larger Work is a combination of Covered Software - with a work governed by one or more Secondary Licenses, and the Covered - Software is not Incompatible With Secondary Licenses, this License permits - You to additionally distribute such Covered Software under the terms of - such Secondary License(s), so that the recipient of the Larger Work may, at - their option, further distribute the Covered Software under the terms of - either this License or such Secondary License(s). - -3.4. Notices - - You may not remove or alter the substance of any license notices (including - copyright notices, patent notices, disclaimers of warranty, or limitations - of liability) contained within the Source Code Form of the Covered - Software, except that You may alter any license notices to the extent - required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - - You may choose to offer, and to charge a fee for, warranty, support, - indemnity or liability obligations to one or more recipients of Covered - Software. However, You may do so only on Your own behalf, and not on behalf - of any Contributor. You must make it absolutely clear that any such - warranty, support, indemnity, or liability obligation is offered by You - alone, and You hereby agree to indemnify every Contributor for any - liability incurred by such Contributor as a result of warranty, support, - indemnity or liability terms You offer. You may include additional - disclaimers of warranty and limitations of liability specific to any - jurisdiction. - -4. Inability to Comply Due to Statute or Regulation - - If it is impossible for You to comply with any of the terms of this License - with respect to some or all of the Covered Software due to statute, judicial - order, or regulation then You must: (a) comply with the terms of this License - to the maximum extent possible; and (b) describe the limitations and the code - they affect. Such description must be placed in a text file included with all - distributions of the Covered Software under this License. Except to the - extent prohibited by statute or regulation, such description must be - sufficiently detailed for a recipient of ordinary skill to be able to - understand it. - -5. Termination - -5.1. The rights granted under this License will terminate automatically if You - fail to comply with any of its terms. However, if You become compliant, - then the rights granted under this License from a particular Contributor - are reinstated (a) provisionally, unless and until such Contributor - explicitly and finally terminates Your grants, and (b) on an ongoing basis, - if such Contributor fails to notify You of the non-compliance by some - reasonable means prior to 60 days after You have come back into compliance. - Moreover, Your grants from a particular Contributor are reinstated on an - ongoing basis if such Contributor notifies You of the non-compliance by - some reasonable means, this is the first time You have received notice of - non-compliance with this License from such Contributor, and You become - compliant prior to 30 days after Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent - infringement claim (excluding declaratory judgment actions, counter-claims, - and cross-claims) alleging that a Contributor Version directly or - indirectly infringes any patent, then the rights granted to You by any and - all Contributors for the Covered Software under Section 2.1 of this License - shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user - license agreements (excluding distributors and resellers) which have been - validly granted by You or Your distributors under this License prior to - termination shall survive termination. - -6. Disclaimer of Warranty - - Covered Software is provided under this License on an “as is†basis, without - warranty of any kind, either expressed, implied, or statutory, including, - without limitation, warranties that the Covered Software is free of defects, - merchantable, fit for a particular purpose or non-infringing. The entire - risk as to the quality and performance of the Covered Software is with You. - Should any Covered Software prove defective in any respect, You (not any - Contributor) assume the cost of any necessary servicing, repair, or - correction. This disclaimer of warranty constitutes an essential part of this - License. No use of any Covered Software is authorized under this License - except under this disclaimer. - -7. Limitation of Liability - - Under no circumstances and under no legal theory, whether tort (including - negligence), contract, or otherwise, shall any Contributor, or anyone who - distributes Covered Software as permitted above, be liable to You for any - direct, indirect, special, incidental, or consequential damages of any - character including, without limitation, damages for lost profits, loss of - goodwill, work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses, even if such party shall have been - informed of the possibility of such damages. This limitation of liability - shall not apply to liability for death or personal injury resulting from such - party’s negligence to the extent applicable law prohibits such limitation. - Some jurisdictions do not allow the exclusion or limitation of incidental or - consequential damages, so this exclusion and limitation may not apply to You. - -8. Litigation - - Any litigation relating to this License may be brought only in the courts of - a jurisdiction where the defendant maintains its principal place of business - and such litigation shall be governed by laws of that jurisdiction, without - reference to its conflict-of-law provisions. Nothing in this Section shall - prevent a party’s ability to bring cross-claims or counter-claims. - -9. Miscellaneous - - This License represents the complete agreement concerning the subject matter - hereof. If any provision of this License is held to be unenforceable, such - provision shall be reformed only to the extent necessary to make it - enforceable. Any law or regulation which provides that the language of a - contract shall be construed against the drafter shall not be used to construe - this License against a Contributor. - - -10. Versions of the License - -10.1. New Versions - - Mozilla Foundation is the license steward. Except as provided in Section - 10.3, no one other than the license steward has the right to modify or - publish new versions of this License. Each version will be given a - distinguishing version number. - -10.2. Effect of New Versions - - You may distribute the Covered Software under the terms of the version of - the License under which You originally received the Covered Software, or - under the terms of any subsequent version published by the license - steward. - -10.3. Modified Versions - - If you create software not governed by this License, and you want to - create a new license for such software, you may create and use a modified - version of this License if you rename the license and remove any - references to the name of the license steward (except to note that such - modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses - If You choose to distribute Source Code Form that is Incompatible With - Secondary Licenses under the terms of this version of the License, the - notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice - - This Source Code Form is subject to the - terms of the Mozilla Public License, v. - 2.0. If a copy of the MPL was not - distributed with this file, You can - obtain one at - http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular file, then -You may include the notice in a location (such as a LICENSE file in a relevant -directory) where a recipient would be likely to look for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - “Incompatible With Secondary Licenses†Notice - - This Source Code Form is “Incompatible - With Secondary Licensesâ€, as defined by - the Mozilla Public License, v. 2.0. - diff --git a/licenses/github.com/hashicorp/go-multierror/LICENSE b/licenses/github.com/hashicorp/go-multierror/LICENSE deleted file mode 100644 index 82b4de97c..000000000 --- a/licenses/github.com/hashicorp/go-multierror/LICENSE +++ /dev/null @@ -1,353 +0,0 @@ -Mozilla Public License, version 2.0 - -1. Definitions - -1.1. “Contributor†- - means each individual or legal entity that creates, contributes to the - creation of, or owns Covered Software. - -1.2. “Contributor Version†- - means the combination of the Contributions of others (if any) used by a - Contributor and that particular Contributor’s Contribution. - -1.3. “Contribution†- - means Covered Software of a particular Contributor. - -1.4. “Covered Software†- - means Source Code Form to which the initial Contributor has attached the - notice in Exhibit A, the Executable Form of such Source Code Form, and - Modifications of such Source Code Form, in each case including portions - thereof. - -1.5. “Incompatible With Secondary Licenses†- means - - a. that the initial Contributor has attached the notice described in - Exhibit B to the Covered Software; or - - b. that the Covered Software was made available under the terms of version - 1.1 or earlier of the License, but not also under the terms of a - Secondary License. - -1.6. “Executable Form†- - means any form of the work other than Source Code Form. - -1.7. “Larger Work†- - means a work that combines Covered Software with other material, in a separate - file or files, that is not Covered Software. - -1.8. “License†- - means this document. - -1.9. “Licensable†- - means having the right to grant, to the maximum extent possible, whether at the - time of the initial grant or subsequently, any and all of the rights conveyed by - this License. - -1.10. “Modifications†- - means any of the following: - - a. any file in Source Code Form that results from an addition to, deletion - from, or modification of the contents of Covered Software; or - - b. any new file in Source Code Form that contains any Covered Software. - -1.11. “Patent Claims†of a Contributor - - means any patent claim(s), including without limitation, method, process, - and apparatus claims, in any patent Licensable by such Contributor that - would be infringed, but for the grant of the License, by the making, - using, selling, offering for sale, having made, import, or transfer of - either its Contributions or its Contributor Version. - -1.12. “Secondary License†- - means either the GNU General Public License, Version 2.0, the GNU Lesser - General Public License, Version 2.1, the GNU Affero General Public - License, Version 3.0, or any later versions of those licenses. - -1.13. “Source Code Form†- - means the form of the work preferred for making modifications. - -1.14. “You†(or “Yourâ€) - - means an individual or a legal entity exercising rights under this - License. For legal entities, “You†includes any entity that controls, is - controlled by, or is under common control with You. For purposes of this - definition, “control†means (a) the power, direct or indirect, to cause - the direction or management of such entity, whether by contract or - otherwise, or (b) ownership of more than fifty percent (50%) of the - outstanding shares or beneficial ownership of such entity. - - -2. License Grants and Conditions - -2.1. Grants - - Each Contributor hereby grants You a world-wide, royalty-free, - non-exclusive license: - - a. under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or as - part of a Larger Work; and - - b. under Patent Claims of such Contributor to make, use, sell, offer for - sale, have made, import, and otherwise transfer either its Contributions - or its Contributor Version. - -2.2. Effective Date - - The licenses granted in Section 2.1 with respect to any Contribution become - effective for each Contribution on the date the Contributor first distributes - such Contribution. - -2.3. Limitations on Grant Scope - - The licenses granted in this Section 2 are the only rights granted under this - License. No additional rights or licenses will be implied from the distribution - or licensing of Covered Software under this License. Notwithstanding Section - 2.1(b) above, no patent license is granted by a Contributor: - - a. for any code that a Contributor has removed from Covered Software; or - - b. for infringements caused by: (i) Your and any other third party’s - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - - c. under Patent Claims infringed by Covered Software in the absence of its - Contributions. - - This License does not grant any rights in the trademarks, service marks, or - logos of any Contributor (except as may be necessary to comply with the - notice requirements in Section 3.4). - -2.4. Subsequent Licenses - - No Contributor makes additional grants as a result of Your choice to - distribute the Covered Software under a subsequent version of this License - (see Section 10.2) or under the terms of a Secondary License (if permitted - under the terms of Section 3.3). - -2.5. Representation - - Each Contributor represents that the Contributor believes its Contributions - are its original creation(s) or it has sufficient rights to grant the - rights to its Contributions conveyed by this License. - -2.6. Fair Use - - This License is not intended to limit any rights You have under applicable - copyright doctrines of fair use, fair dealing, or other equivalents. - -2.7. Conditions - - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in - Section 2.1. - - -3. Responsibilities - -3.1. Distribution of Source Form - - All distribution of Covered Software in Source Code Form, including any - Modifications that You create or to which You contribute, must be under the - terms of this License. You must inform recipients that the Source Code Form - of the Covered Software is governed by the terms of this License, and how - they can obtain a copy of this License. You may not attempt to alter or - restrict the recipients’ rights in the Source Code Form. - -3.2. Distribution of Executable Form - - If You distribute Covered Software in Executable Form then: - - a. such Covered Software must also be made available in Source Code Form, - as described in Section 3.1, and You must inform recipients of the - Executable Form how they can obtain a copy of such Source Code Form by - reasonable means in a timely manner, at a charge no more than the cost - of distribution to the recipient; and - - b. You may distribute such Executable Form under the terms of this License, - or sublicense it under different terms, provided that the license for - the Executable Form does not attempt to limit or alter the recipients’ - rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - - You may create and distribute a Larger Work under terms of Your choice, - provided that You also comply with the requirements of this License for the - Covered Software. If the Larger Work is a combination of Covered Software - with a work governed by one or more Secondary Licenses, and the Covered - Software is not Incompatible With Secondary Licenses, this License permits - You to additionally distribute such Covered Software under the terms of - such Secondary License(s), so that the recipient of the Larger Work may, at - their option, further distribute the Covered Software under the terms of - either this License or such Secondary License(s). - -3.4. Notices - - You may not remove or alter the substance of any license notices (including - copyright notices, patent notices, disclaimers of warranty, or limitations - of liability) contained within the Source Code Form of the Covered - Software, except that You may alter any license notices to the extent - required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - - You may choose to offer, and to charge a fee for, warranty, support, - indemnity or liability obligations to one or more recipients of Covered - Software. However, You may do so only on Your own behalf, and not on behalf - of any Contributor. You must make it absolutely clear that any such - warranty, support, indemnity, or liability obligation is offered by You - alone, and You hereby agree to indemnify every Contributor for any - liability incurred by such Contributor as a result of warranty, support, - indemnity or liability terms You offer. You may include additional - disclaimers of warranty and limitations of liability specific to any - jurisdiction. - -4. Inability to Comply Due to Statute or Regulation - - If it is impossible for You to comply with any of the terms of this License - with respect to some or all of the Covered Software due to statute, judicial - order, or regulation then You must: (a) comply with the terms of this License - to the maximum extent possible; and (b) describe the limitations and the code - they affect. Such description must be placed in a text file included with all - distributions of the Covered Software under this License. Except to the - extent prohibited by statute or regulation, such description must be - sufficiently detailed for a recipient of ordinary skill to be able to - understand it. - -5. Termination - -5.1. The rights granted under this License will terminate automatically if You - fail to comply with any of its terms. However, if You become compliant, - then the rights granted under this License from a particular Contributor - are reinstated (a) provisionally, unless and until such Contributor - explicitly and finally terminates Your grants, and (b) on an ongoing basis, - if such Contributor fails to notify You of the non-compliance by some - reasonable means prior to 60 days after You have come back into compliance. - Moreover, Your grants from a particular Contributor are reinstated on an - ongoing basis if such Contributor notifies You of the non-compliance by - some reasonable means, this is the first time You have received notice of - non-compliance with this License from such Contributor, and You become - compliant prior to 30 days after Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent - infringement claim (excluding declaratory judgment actions, counter-claims, - and cross-claims) alleging that a Contributor Version directly or - indirectly infringes any patent, then the rights granted to You by any and - all Contributors for the Covered Software under Section 2.1 of this License - shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user - license agreements (excluding distributors and resellers) which have been - validly granted by You or Your distributors under this License prior to - termination shall survive termination. - -6. Disclaimer of Warranty - - Covered Software is provided under this License on an “as is†basis, without - warranty of any kind, either expressed, implied, or statutory, including, - without limitation, warranties that the Covered Software is free of defects, - merchantable, fit for a particular purpose or non-infringing. The entire - risk as to the quality and performance of the Covered Software is with You. - Should any Covered Software prove defective in any respect, You (not any - Contributor) assume the cost of any necessary servicing, repair, or - correction. This disclaimer of warranty constitutes an essential part of this - License. No use of any Covered Software is authorized under this License - except under this disclaimer. - -7. Limitation of Liability - - Under no circumstances and under no legal theory, whether tort (including - negligence), contract, or otherwise, shall any Contributor, or anyone who - distributes Covered Software as permitted above, be liable to You for any - direct, indirect, special, incidental, or consequential damages of any - character including, without limitation, damages for lost profits, loss of - goodwill, work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses, even if such party shall have been - informed of the possibility of such damages. This limitation of liability - shall not apply to liability for death or personal injury resulting from such - party’s negligence to the extent applicable law prohibits such limitation. - Some jurisdictions do not allow the exclusion or limitation of incidental or - consequential damages, so this exclusion and limitation may not apply to You. - -8. Litigation - - Any litigation relating to this License may be brought only in the courts of - a jurisdiction where the defendant maintains its principal place of business - and such litigation shall be governed by laws of that jurisdiction, without - reference to its conflict-of-law provisions. Nothing in this Section shall - prevent a party’s ability to bring cross-claims or counter-claims. - -9. Miscellaneous - - This License represents the complete agreement concerning the subject matter - hereof. If any provision of this License is held to be unenforceable, such - provision shall be reformed only to the extent necessary to make it - enforceable. Any law or regulation which provides that the language of a - contract shall be construed against the drafter shall not be used to construe - this License against a Contributor. - - -10. Versions of the License - -10.1. New Versions - - Mozilla Foundation is the license steward. Except as provided in Section - 10.3, no one other than the license steward has the right to modify or - publish new versions of this License. Each version will be given a - distinguishing version number. - -10.2. Effect of New Versions - - You may distribute the Covered Software under the terms of the version of - the License under which You originally received the Covered Software, or - under the terms of any subsequent version published by the license - steward. - -10.3. Modified Versions - - If you create software not governed by this License, and you want to - create a new license for such software, you may create and use a modified - version of this License if you rename the license and remove any - references to the name of the license steward (except to note that such - modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses - If You choose to distribute Source Code Form that is Incompatible With - Secondary Licenses under the terms of this version of the License, the - notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice - - This Source Code Form is subject to the - terms of the Mozilla Public License, v. - 2.0. If a copy of the MPL was not - distributed with this file, You can - obtain one at - http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular file, then -You may include the notice in a location (such as a LICENSE file in a relevant -directory) where a recipient would be likely to look for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - “Incompatible With Secondary Licenses†Notice - - This Source Code Form is “Incompatible - With Secondary Licensesâ€, as defined by - the Mozilla Public License, v. 2.0. diff --git a/licenses/github.com/hashicorp/go-version/LICENSE b/licenses/github.com/hashicorp/go-version/LICENSE deleted file mode 100644 index c33dcc7c9..000000000 --- a/licenses/github.com/hashicorp/go-version/LICENSE +++ /dev/null @@ -1,354 +0,0 @@ -Mozilla Public License, version 2.0 - -1. Definitions - -1.1. “Contributor†- - means each individual or legal entity that creates, contributes to the - creation of, or owns Covered Software. - -1.2. “Contributor Version†- - means the combination of the Contributions of others (if any) used by a - Contributor and that particular Contributor’s Contribution. - -1.3. “Contribution†- - means Covered Software of a particular Contributor. - -1.4. “Covered Software†- - means Source Code Form to which the initial Contributor has attached the - notice in Exhibit A, the Executable Form of such Source Code Form, and - Modifications of such Source Code Form, in each case including portions - thereof. - -1.5. “Incompatible With Secondary Licenses†- means - - a. that the initial Contributor has attached the notice described in - Exhibit B to the Covered Software; or - - b. that the Covered Software was made available under the terms of version - 1.1 or earlier of the License, but not also under the terms of a - Secondary License. - -1.6. “Executable Form†- - means any form of the work other than Source Code Form. - -1.7. “Larger Work†- - means a work that combines Covered Software with other material, in a separate - file or files, that is not Covered Software. - -1.8. “License†- - means this document. - -1.9. “Licensable†- - means having the right to grant, to the maximum extent possible, whether at the - time of the initial grant or subsequently, any and all of the rights conveyed by - this License. - -1.10. “Modifications†- - means any of the following: - - a. any file in Source Code Form that results from an addition to, deletion - from, or modification of the contents of Covered Software; or - - b. any new file in Source Code Form that contains any Covered Software. - -1.11. “Patent Claims†of a Contributor - - means any patent claim(s), including without limitation, method, process, - and apparatus claims, in any patent Licensable by such Contributor that - would be infringed, but for the grant of the License, by the making, - using, selling, offering for sale, having made, import, or transfer of - either its Contributions or its Contributor Version. - -1.12. “Secondary License†- - means either the GNU General Public License, Version 2.0, the GNU Lesser - General Public License, Version 2.1, the GNU Affero General Public - License, Version 3.0, or any later versions of those licenses. - -1.13. “Source Code Form†- - means the form of the work preferred for making modifications. - -1.14. “You†(or “Yourâ€) - - means an individual or a legal entity exercising rights under this - License. For legal entities, “You†includes any entity that controls, is - controlled by, or is under common control with You. For purposes of this - definition, “control†means (a) the power, direct or indirect, to cause - the direction or management of such entity, whether by contract or - otherwise, or (b) ownership of more than fifty percent (50%) of the - outstanding shares or beneficial ownership of such entity. - - -2. License Grants and Conditions - -2.1. Grants - - Each Contributor hereby grants You a world-wide, royalty-free, - non-exclusive license: - - a. under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or as - part of a Larger Work; and - - b. under Patent Claims of such Contributor to make, use, sell, offer for - sale, have made, import, and otherwise transfer either its Contributions - or its Contributor Version. - -2.2. Effective Date - - The licenses granted in Section 2.1 with respect to any Contribution become - effective for each Contribution on the date the Contributor first distributes - such Contribution. - -2.3. Limitations on Grant Scope - - The licenses granted in this Section 2 are the only rights granted under this - License. No additional rights or licenses will be implied from the distribution - or licensing of Covered Software under this License. Notwithstanding Section - 2.1(b) above, no patent license is granted by a Contributor: - - a. for any code that a Contributor has removed from Covered Software; or - - b. for infringements caused by: (i) Your and any other third party’s - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - - c. under Patent Claims infringed by Covered Software in the absence of its - Contributions. - - This License does not grant any rights in the trademarks, service marks, or - logos of any Contributor (except as may be necessary to comply with the - notice requirements in Section 3.4). - -2.4. Subsequent Licenses - - No Contributor makes additional grants as a result of Your choice to - distribute the Covered Software under a subsequent version of this License - (see Section 10.2) or under the terms of a Secondary License (if permitted - under the terms of Section 3.3). - -2.5. Representation - - Each Contributor represents that the Contributor believes its Contributions - are its original creation(s) or it has sufficient rights to grant the - rights to its Contributions conveyed by this License. - -2.6. Fair Use - - This License is not intended to limit any rights You have under applicable - copyright doctrines of fair use, fair dealing, or other equivalents. - -2.7. Conditions - - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in - Section 2.1. - - -3. Responsibilities - -3.1. Distribution of Source Form - - All distribution of Covered Software in Source Code Form, including any - Modifications that You create or to which You contribute, must be under the - terms of this License. You must inform recipients that the Source Code Form - of the Covered Software is governed by the terms of this License, and how - they can obtain a copy of this License. You may not attempt to alter or - restrict the recipients’ rights in the Source Code Form. - -3.2. Distribution of Executable Form - - If You distribute Covered Software in Executable Form then: - - a. such Covered Software must also be made available in Source Code Form, - as described in Section 3.1, and You must inform recipients of the - Executable Form how they can obtain a copy of such Source Code Form by - reasonable means in a timely manner, at a charge no more than the cost - of distribution to the recipient; and - - b. You may distribute such Executable Form under the terms of this License, - or sublicense it under different terms, provided that the license for - the Executable Form does not attempt to limit or alter the recipients’ - rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - - You may create and distribute a Larger Work under terms of Your choice, - provided that You also comply with the requirements of this License for the - Covered Software. If the Larger Work is a combination of Covered Software - with a work governed by one or more Secondary Licenses, and the Covered - Software is not Incompatible With Secondary Licenses, this License permits - You to additionally distribute such Covered Software under the terms of - such Secondary License(s), so that the recipient of the Larger Work may, at - their option, further distribute the Covered Software under the terms of - either this License or such Secondary License(s). - -3.4. Notices - - You may not remove or alter the substance of any license notices (including - copyright notices, patent notices, disclaimers of warranty, or limitations - of liability) contained within the Source Code Form of the Covered - Software, except that You may alter any license notices to the extent - required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - - You may choose to offer, and to charge a fee for, warranty, support, - indemnity or liability obligations to one or more recipients of Covered - Software. However, You may do so only on Your own behalf, and not on behalf - of any Contributor. You must make it absolutely clear that any such - warranty, support, indemnity, or liability obligation is offered by You - alone, and You hereby agree to indemnify every Contributor for any - liability incurred by such Contributor as a result of warranty, support, - indemnity or liability terms You offer. You may include additional - disclaimers of warranty and limitations of liability specific to any - jurisdiction. - -4. Inability to Comply Due to Statute or Regulation - - If it is impossible for You to comply with any of the terms of this License - with respect to some or all of the Covered Software due to statute, judicial - order, or regulation then You must: (a) comply with the terms of this License - to the maximum extent possible; and (b) describe the limitations and the code - they affect. Such description must be placed in a text file included with all - distributions of the Covered Software under this License. Except to the - extent prohibited by statute or regulation, such description must be - sufficiently detailed for a recipient of ordinary skill to be able to - understand it. - -5. Termination - -5.1. The rights granted under this License will terminate automatically if You - fail to comply with any of its terms. However, if You become compliant, - then the rights granted under this License from a particular Contributor - are reinstated (a) provisionally, unless and until such Contributor - explicitly and finally terminates Your grants, and (b) on an ongoing basis, - if such Contributor fails to notify You of the non-compliance by some - reasonable means prior to 60 days after You have come back into compliance. - Moreover, Your grants from a particular Contributor are reinstated on an - ongoing basis if such Contributor notifies You of the non-compliance by - some reasonable means, this is the first time You have received notice of - non-compliance with this License from such Contributor, and You become - compliant prior to 30 days after Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent - infringement claim (excluding declaratory judgment actions, counter-claims, - and cross-claims) alleging that a Contributor Version directly or - indirectly infringes any patent, then the rights granted to You by any and - all Contributors for the Covered Software under Section 2.1 of this License - shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user - license agreements (excluding distributors and resellers) which have been - validly granted by You or Your distributors under this License prior to - termination shall survive termination. - -6. Disclaimer of Warranty - - Covered Software is provided under this License on an “as is†basis, without - warranty of any kind, either expressed, implied, or statutory, including, - without limitation, warranties that the Covered Software is free of defects, - merchantable, fit for a particular purpose or non-infringing. The entire - risk as to the quality and performance of the Covered Software is with You. - Should any Covered Software prove defective in any respect, You (not any - Contributor) assume the cost of any necessary servicing, repair, or - correction. This disclaimer of warranty constitutes an essential part of this - License. No use of any Covered Software is authorized under this License - except under this disclaimer. - -7. Limitation of Liability - - Under no circumstances and under no legal theory, whether tort (including - negligence), contract, or otherwise, shall any Contributor, or anyone who - distributes Covered Software as permitted above, be liable to You for any - direct, indirect, special, incidental, or consequential damages of any - character including, without limitation, damages for lost profits, loss of - goodwill, work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses, even if such party shall have been - informed of the possibility of such damages. This limitation of liability - shall not apply to liability for death or personal injury resulting from such - party’s negligence to the extent applicable law prohibits such limitation. - Some jurisdictions do not allow the exclusion or limitation of incidental or - consequential damages, so this exclusion and limitation may not apply to You. - -8. Litigation - - Any litigation relating to this License may be brought only in the courts of - a jurisdiction where the defendant maintains its principal place of business - and such litigation shall be governed by laws of that jurisdiction, without - reference to its conflict-of-law provisions. Nothing in this Section shall - prevent a party’s ability to bring cross-claims or counter-claims. - -9. Miscellaneous - - This License represents the complete agreement concerning the subject matter - hereof. If any provision of this License is held to be unenforceable, such - provision shall be reformed only to the extent necessary to make it - enforceable. Any law or regulation which provides that the language of a - contract shall be construed against the drafter shall not be used to construe - this License against a Contributor. - - -10. Versions of the License - -10.1. New Versions - - Mozilla Foundation is the license steward. Except as provided in Section - 10.3, no one other than the license steward has the right to modify or - publish new versions of this License. Each version will be given a - distinguishing version number. - -10.2. Effect of New Versions - - You may distribute the Covered Software under the terms of the version of - the License under which You originally received the Covered Software, or - under the terms of any subsequent version published by the license - steward. - -10.3. Modified Versions - - If you create software not governed by this License, and you want to - create a new license for such software, you may create and use a modified - version of this License if you rename the license and remove any - references to the name of the license steward (except to note that such - modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses - If You choose to distribute Source Code Form that is Incompatible With - Secondary Licenses under the terms of this version of the License, the - notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice - - This Source Code Form is subject to the - terms of the Mozilla Public License, v. - 2.0. If a copy of the MPL was not - distributed with this file, You can - obtain one at - http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular file, then -You may include the notice in a location (such as a LICENSE file in a relevant -directory) where a recipient would be likely to look for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - “Incompatible With Secondary Licenses†Notice - - This Source Code Form is “Incompatible - With Secondary Licensesâ€, as defined by - the Mozilla Public License, v. 2.0. - diff --git a/licenses/github.com/hashicorp/golang-lru/LICENSE b/licenses/github.com/hashicorp/golang-lru/LICENSE deleted file mode 100644 index be2cc4dfb..000000000 --- a/licenses/github.com/hashicorp/golang-lru/LICENSE +++ /dev/null @@ -1,362 +0,0 @@ -Mozilla Public License, version 2.0 - -1. Definitions - -1.1. "Contributor" - - means each individual or legal entity that creates, contributes to the - creation of, or owns Covered Software. - -1.2. "Contributor Version" - - means the combination of the Contributions of others (if any) used by a - Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - - means Source Code Form to which the initial Contributor has attached the - notice in Exhibit A, the Executable Form of such Source Code Form, and - Modifications of such Source Code Form, in each case including portions - thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - a. that the initial Contributor has attached the notice described in - Exhibit B to the Covered Software; or - - b. that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the terms of - a Secondary License. - -1.6. "Executable Form" - - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - - means a work that combines Covered Software with other material, in a - separate file or files, that is not Covered Software. - -1.8. "License" - - means this document. - -1.9. "Licensable" - - means having the right to grant, to the maximum extent possible, whether - at the time of the initial grant or subsequently, any and all of the - rights conveyed by this License. - -1.10. "Modifications" - - means any of the following: - - a. any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered Software; or - - b. any new file in Source Code Form that contains any Covered Software. - -1.11. "Patent Claims" of a Contributor - - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the License, - by the making, using, selling, offering for sale, having made, import, - or transfer of either its Contributions or its Contributor Version. - -1.12. "Secondary License" - - means either the GNU General Public License, Version 2.0, the GNU Lesser - General Public License, Version 2.1, the GNU Affero General Public - License, Version 3.0, or any later versions of those licenses. - -1.13. "Source Code Form" - - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that controls, is - controlled by, or is under common control with You. For purposes of this - definition, "control" means (a) the power, direct or indirect, to cause - the direction or management of such entity, whether by contract or - otherwise, or (b) ownership of more than fifty percent (50%) of the - outstanding shares or beneficial ownership of such entity. - - -2. License Grants and Conditions - -2.1. Grants - - Each Contributor hereby grants You a world-wide, royalty-free, - non-exclusive license: - - a. under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - - b. under Patent Claims of such Contributor to make, use, sell, offer for - sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - - The licenses granted in Section 2.1 with respect to any Contribution - become effective for each Contribution on the date the Contributor first - distributes such Contribution. - -2.3. Limitations on Grant Scope - - The licenses granted in this Section 2 are the only rights granted under - this License. No additional rights or licenses will be implied from the - distribution or licensing of Covered Software under this License. - Notwithstanding Section 2.1(b) above, no patent license is granted by a - Contributor: - - a. for any code that a Contributor has removed from Covered Software; or - - b. for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - - c. under Patent Claims infringed by Covered Software in the absence of - its Contributions. - - This License does not grant any rights in the trademarks, service marks, - or logos of any Contributor (except as may be necessary to comply with - the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - - No Contributor makes additional grants as a result of Your choice to - distribute the Covered Software under a subsequent version of this - License (see Section 10.2) or under the terms of a Secondary License (if - permitted under the terms of Section 3.3). - -2.5. Representation - - Each Contributor represents that the Contributor believes its - Contributions are its original creation(s) or it has sufficient rights to - grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - - This License is not intended to limit any rights You have under - applicable copyright doctrines of fair use, fair dealing, or other - equivalents. - -2.7. Conditions - - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in - Section 2.1. - - -3. Responsibilities - -3.1. Distribution of Source Form - - All distribution of Covered Software in Source Code Form, including any - Modifications that You create or to which You contribute, must be under - the terms of this License. You must inform recipients that the Source - Code Form of the Covered Software is governed by the terms of this - License, and how they can obtain a copy of this License. You may not - attempt to alter or restrict the recipients' rights in the Source Code - Form. - -3.2. Distribution of Executable Form - - If You distribute Covered Software in Executable Form then: - - a. such Covered Software must also be made available in Source Code Form, - as described in Section 3.1, and You must inform recipients of the - Executable Form how they can obtain a copy of such Source Code Form by - reasonable means in a timely manner, at a charge no more than the cost - of distribution to the recipient; and - - b. You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter the - recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - - You may create and distribute a Larger Work under terms of Your choice, - provided that You also comply with the requirements of this License for - the Covered Software. If the Larger Work is a combination of Covered - Software with a work governed by one or more Secondary Licenses, and the - Covered Software is not Incompatible With Secondary Licenses, this - License permits You to additionally distribute such Covered Software - under the terms of such Secondary License(s), so that the recipient of - the Larger Work may, at their option, further distribute the Covered - Software under the terms of either this License or such Secondary - License(s). - -3.4. Notices - - You may not remove or alter the substance of any license notices - (including copyright notices, patent notices, disclaimers of warranty, or - limitations of liability) contained within the Source Code Form of the - Covered Software, except that You may alter any license notices to the - extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - - You may choose to offer, and to charge a fee for, warranty, support, - indemnity or liability obligations to one or more recipients of Covered - Software. However, You may do so only on Your own behalf, and not on - behalf of any Contributor. You must make it absolutely clear that any - such warranty, support, indemnity, or liability obligation is offered by - You alone, and You hereby agree to indemnify every Contributor for any - liability incurred by such Contributor as a result of warranty, support, - indemnity or liability terms You offer. You may include additional - disclaimers of warranty and limitations of liability specific to any - jurisdiction. - -4. Inability to Comply Due to Statute or Regulation - - If it is impossible for You to comply with any of the terms of this License - with respect to some or all of the Covered Software due to statute, - judicial order, or regulation then You must: (a) comply with the terms of - this License to the maximum extent possible; and (b) describe the - limitations and the code they affect. Such description must be placed in a - text file included with all distributions of the Covered Software under - this License. Except to the extent prohibited by statute or regulation, - such description must be sufficiently detailed for a recipient of ordinary - skill to be able to understand it. - -5. Termination - -5.1. The rights granted under this License will terminate automatically if You - fail to comply with any of its terms. However, if You become compliant, - then the rights granted under this License from a particular Contributor - are reinstated (a) provisionally, unless and until such Contributor - explicitly and finally terminates Your grants, and (b) on an ongoing - basis, if such Contributor fails to notify You of the non-compliance by - some reasonable means prior to 60 days after You have come back into - compliance. Moreover, Your grants from a particular Contributor are - reinstated on an ongoing basis if such Contributor notifies You of the - non-compliance by some reasonable means, this is the first time You have - received notice of non-compliance with this License from such - Contributor, and You become compliant prior to 30 days after Your receipt - of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent - infringement claim (excluding declaratory judgment actions, - counter-claims, and cross-claims) alleging that a Contributor Version - directly or indirectly infringes any patent, then the rights granted to - You by any and all Contributors for the Covered Software under Section - 2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user - license agreements (excluding distributors and resellers) which have been - validly granted by You or Your distributors under this License prior to - termination shall survive termination. - -6. Disclaimer of Warranty - - Covered Software is provided under this License on an "as is" basis, - without warranty of any kind, either expressed, implied, or statutory, - including, without limitation, warranties that the Covered Software is free - of defects, merchantable, fit for a particular purpose or non-infringing. - The entire risk as to the quality and performance of the Covered Software - is with You. Should any Covered Software prove defective in any respect, - You (not any Contributor) assume the cost of any necessary servicing, - repair, or correction. This disclaimer of warranty constitutes an essential - part of this License. No use of any Covered Software is authorized under - this License except under this disclaimer. - -7. Limitation of Liability - - Under no circumstances and under no legal theory, whether tort (including - negligence), contract, or otherwise, shall any Contributor, or anyone who - distributes Covered Software as permitted above, be liable to You for any - direct, indirect, special, incidental, or consequential damages of any - character including, without limitation, damages for lost profits, loss of - goodwill, work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses, even if such party shall have been - informed of the possibility of such damages. This limitation of liability - shall not apply to liability for death or personal injury resulting from - such party's negligence to the extent applicable law prohibits such - limitation. Some jurisdictions do not allow the exclusion or limitation of - incidental or consequential damages, so this exclusion and limitation may - not apply to You. - -8. Litigation - - Any litigation relating to this License may be brought only in the courts - of a jurisdiction where the defendant maintains its principal place of - business and such litigation shall be governed by laws of that - jurisdiction, without reference to its conflict-of-law provisions. Nothing - in this Section shall prevent a party's ability to bring cross-claims or - counter-claims. - -9. Miscellaneous - - This License represents the complete agreement concerning the subject - matter hereof. If any provision of this License is held to be - unenforceable, such provision shall be reformed only to the extent - necessary to make it enforceable. Any law or regulation which provides that - the language of a contract shall be construed against the drafter shall not - be used to construe this License against a Contributor. - - -10. Versions of the License - -10.1. New Versions - - Mozilla Foundation is the license steward. Except as provided in Section - 10.3, no one other than the license steward has the right to modify or - publish new versions of this License. Each version will be given a - distinguishing version number. - -10.2. Effect of New Versions - - You may distribute the Covered Software under the terms of the version - of the License under which You originally received the Covered Software, - or under the terms of any subsequent version published by the license - steward. - -10.3. Modified Versions - - If you create software not governed by this License, and you want to - create a new license for such software, you may create and use a - modified version of this License if you rename the license and remove - any references to the name of the license steward (except to note that - such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary - Licenses If You choose to distribute Source Code Form that is - Incompatible With Secondary Licenses under the terms of this version of - the License, the notice described in Exhibit B of this License must be - attached. - -Exhibit A - Source Code Form License Notice - - This Source Code Form is subject to the - terms of the Mozilla Public License, v. - 2.0. If a copy of the MPL was not - distributed with this file, You can - obtain one at - http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular file, -then You may include the notice in a location (such as a LICENSE file in a -relevant directory) where a recipient would be likely to look for such a -notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice - - This Source Code Form is "Incompatible - With Secondary Licenses", as defined by - the Mozilla Public License, v. 2.0. diff --git a/licenses/github.com/hashicorp/hcl/LICENSE b/licenses/github.com/hashicorp/hcl/LICENSE deleted file mode 100644 index c33dcc7c9..000000000 --- a/licenses/github.com/hashicorp/hcl/LICENSE +++ /dev/null @@ -1,354 +0,0 @@ -Mozilla Public License, version 2.0 - -1. Definitions - -1.1. “Contributor†- - means each individual or legal entity that creates, contributes to the - creation of, or owns Covered Software. - -1.2. “Contributor Version†- - means the combination of the Contributions of others (if any) used by a - Contributor and that particular Contributor’s Contribution. - -1.3. “Contribution†- - means Covered Software of a particular Contributor. - -1.4. “Covered Software†- - means Source Code Form to which the initial Contributor has attached the - notice in Exhibit A, the Executable Form of such Source Code Form, and - Modifications of such Source Code Form, in each case including portions - thereof. - -1.5. “Incompatible With Secondary Licenses†- means - - a. that the initial Contributor has attached the notice described in - Exhibit B to the Covered Software; or - - b. that the Covered Software was made available under the terms of version - 1.1 or earlier of the License, but not also under the terms of a - Secondary License. - -1.6. “Executable Form†- - means any form of the work other than Source Code Form. - -1.7. “Larger Work†- - means a work that combines Covered Software with other material, in a separate - file or files, that is not Covered Software. - -1.8. “License†- - means this document. - -1.9. “Licensable†- - means having the right to grant, to the maximum extent possible, whether at the - time of the initial grant or subsequently, any and all of the rights conveyed by - this License. - -1.10. “Modifications†- - means any of the following: - - a. any file in Source Code Form that results from an addition to, deletion - from, or modification of the contents of Covered Software; or - - b. any new file in Source Code Form that contains any Covered Software. - -1.11. “Patent Claims†of a Contributor - - means any patent claim(s), including without limitation, method, process, - and apparatus claims, in any patent Licensable by such Contributor that - would be infringed, but for the grant of the License, by the making, - using, selling, offering for sale, having made, import, or transfer of - either its Contributions or its Contributor Version. - -1.12. “Secondary License†- - means either the GNU General Public License, Version 2.0, the GNU Lesser - General Public License, Version 2.1, the GNU Affero General Public - License, Version 3.0, or any later versions of those licenses. - -1.13. “Source Code Form†- - means the form of the work preferred for making modifications. - -1.14. “You†(or “Yourâ€) - - means an individual or a legal entity exercising rights under this - License. For legal entities, “You†includes any entity that controls, is - controlled by, or is under common control with You. For purposes of this - definition, “control†means (a) the power, direct or indirect, to cause - the direction or management of such entity, whether by contract or - otherwise, or (b) ownership of more than fifty percent (50%) of the - outstanding shares or beneficial ownership of such entity. - - -2. License Grants and Conditions - -2.1. Grants - - Each Contributor hereby grants You a world-wide, royalty-free, - non-exclusive license: - - a. under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or as - part of a Larger Work; and - - b. under Patent Claims of such Contributor to make, use, sell, offer for - sale, have made, import, and otherwise transfer either its Contributions - or its Contributor Version. - -2.2. Effective Date - - The licenses granted in Section 2.1 with respect to any Contribution become - effective for each Contribution on the date the Contributor first distributes - such Contribution. - -2.3. Limitations on Grant Scope - - The licenses granted in this Section 2 are the only rights granted under this - License. No additional rights or licenses will be implied from the distribution - or licensing of Covered Software under this License. Notwithstanding Section - 2.1(b) above, no patent license is granted by a Contributor: - - a. for any code that a Contributor has removed from Covered Software; or - - b. for infringements caused by: (i) Your and any other third party’s - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - - c. under Patent Claims infringed by Covered Software in the absence of its - Contributions. - - This License does not grant any rights in the trademarks, service marks, or - logos of any Contributor (except as may be necessary to comply with the - notice requirements in Section 3.4). - -2.4. Subsequent Licenses - - No Contributor makes additional grants as a result of Your choice to - distribute the Covered Software under a subsequent version of this License - (see Section 10.2) or under the terms of a Secondary License (if permitted - under the terms of Section 3.3). - -2.5. Representation - - Each Contributor represents that the Contributor believes its Contributions - are its original creation(s) or it has sufficient rights to grant the - rights to its Contributions conveyed by this License. - -2.6. Fair Use - - This License is not intended to limit any rights You have under applicable - copyright doctrines of fair use, fair dealing, or other equivalents. - -2.7. Conditions - - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in - Section 2.1. - - -3. Responsibilities - -3.1. Distribution of Source Form - - All distribution of Covered Software in Source Code Form, including any - Modifications that You create or to which You contribute, must be under the - terms of this License. You must inform recipients that the Source Code Form - of the Covered Software is governed by the terms of this License, and how - they can obtain a copy of this License. You may not attempt to alter or - restrict the recipients’ rights in the Source Code Form. - -3.2. Distribution of Executable Form - - If You distribute Covered Software in Executable Form then: - - a. such Covered Software must also be made available in Source Code Form, - as described in Section 3.1, and You must inform recipients of the - Executable Form how they can obtain a copy of such Source Code Form by - reasonable means in a timely manner, at a charge no more than the cost - of distribution to the recipient; and - - b. You may distribute such Executable Form under the terms of this License, - or sublicense it under different terms, provided that the license for - the Executable Form does not attempt to limit or alter the recipients’ - rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - - You may create and distribute a Larger Work under terms of Your choice, - provided that You also comply with the requirements of this License for the - Covered Software. If the Larger Work is a combination of Covered Software - with a work governed by one or more Secondary Licenses, and the Covered - Software is not Incompatible With Secondary Licenses, this License permits - You to additionally distribute such Covered Software under the terms of - such Secondary License(s), so that the recipient of the Larger Work may, at - their option, further distribute the Covered Software under the terms of - either this License or such Secondary License(s). - -3.4. Notices - - You may not remove or alter the substance of any license notices (including - copyright notices, patent notices, disclaimers of warranty, or limitations - of liability) contained within the Source Code Form of the Covered - Software, except that You may alter any license notices to the extent - required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - - You may choose to offer, and to charge a fee for, warranty, support, - indemnity or liability obligations to one or more recipients of Covered - Software. However, You may do so only on Your own behalf, and not on behalf - of any Contributor. You must make it absolutely clear that any such - warranty, support, indemnity, or liability obligation is offered by You - alone, and You hereby agree to indemnify every Contributor for any - liability incurred by such Contributor as a result of warranty, support, - indemnity or liability terms You offer. You may include additional - disclaimers of warranty and limitations of liability specific to any - jurisdiction. - -4. Inability to Comply Due to Statute or Regulation - - If it is impossible for You to comply with any of the terms of this License - with respect to some or all of the Covered Software due to statute, judicial - order, or regulation then You must: (a) comply with the terms of this License - to the maximum extent possible; and (b) describe the limitations and the code - they affect. Such description must be placed in a text file included with all - distributions of the Covered Software under this License. Except to the - extent prohibited by statute or regulation, such description must be - sufficiently detailed for a recipient of ordinary skill to be able to - understand it. - -5. Termination - -5.1. The rights granted under this License will terminate automatically if You - fail to comply with any of its terms. However, if You become compliant, - then the rights granted under this License from a particular Contributor - are reinstated (a) provisionally, unless and until such Contributor - explicitly and finally terminates Your grants, and (b) on an ongoing basis, - if such Contributor fails to notify You of the non-compliance by some - reasonable means prior to 60 days after You have come back into compliance. - Moreover, Your grants from a particular Contributor are reinstated on an - ongoing basis if such Contributor notifies You of the non-compliance by - some reasonable means, this is the first time You have received notice of - non-compliance with this License from such Contributor, and You become - compliant prior to 30 days after Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent - infringement claim (excluding declaratory judgment actions, counter-claims, - and cross-claims) alleging that a Contributor Version directly or - indirectly infringes any patent, then the rights granted to You by any and - all Contributors for the Covered Software under Section 2.1 of this License - shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user - license agreements (excluding distributors and resellers) which have been - validly granted by You or Your distributors under this License prior to - termination shall survive termination. - -6. Disclaimer of Warranty - - Covered Software is provided under this License on an “as is†basis, without - warranty of any kind, either expressed, implied, or statutory, including, - without limitation, warranties that the Covered Software is free of defects, - merchantable, fit for a particular purpose or non-infringing. The entire - risk as to the quality and performance of the Covered Software is with You. - Should any Covered Software prove defective in any respect, You (not any - Contributor) assume the cost of any necessary servicing, repair, or - correction. This disclaimer of warranty constitutes an essential part of this - License. No use of any Covered Software is authorized under this License - except under this disclaimer. - -7. Limitation of Liability - - Under no circumstances and under no legal theory, whether tort (including - negligence), contract, or otherwise, shall any Contributor, or anyone who - distributes Covered Software as permitted above, be liable to You for any - direct, indirect, special, incidental, or consequential damages of any - character including, without limitation, damages for lost profits, loss of - goodwill, work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses, even if such party shall have been - informed of the possibility of such damages. This limitation of liability - shall not apply to liability for death or personal injury resulting from such - party’s negligence to the extent applicable law prohibits such limitation. - Some jurisdictions do not allow the exclusion or limitation of incidental or - consequential damages, so this exclusion and limitation may not apply to You. - -8. Litigation - - Any litigation relating to this License may be brought only in the courts of - a jurisdiction where the defendant maintains its principal place of business - and such litigation shall be governed by laws of that jurisdiction, without - reference to its conflict-of-law provisions. Nothing in this Section shall - prevent a party’s ability to bring cross-claims or counter-claims. - -9. Miscellaneous - - This License represents the complete agreement concerning the subject matter - hereof. If any provision of this License is held to be unenforceable, such - provision shall be reformed only to the extent necessary to make it - enforceable. Any law or regulation which provides that the language of a - contract shall be construed against the drafter shall not be used to construe - this License against a Contributor. - - -10. Versions of the License - -10.1. New Versions - - Mozilla Foundation is the license steward. Except as provided in Section - 10.3, no one other than the license steward has the right to modify or - publish new versions of this License. Each version will be given a - distinguishing version number. - -10.2. Effect of New Versions - - You may distribute the Covered Software under the terms of the version of - the License under which You originally received the Covered Software, or - under the terms of any subsequent version published by the license - steward. - -10.3. Modified Versions - - If you create software not governed by this License, and you want to - create a new license for such software, you may create and use a modified - version of this License if you rename the license and remove any - references to the name of the license steward (except to note that such - modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses - If You choose to distribute Source Code Form that is Incompatible With - Secondary Licenses under the terms of this version of the License, the - notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice - - This Source Code Form is subject to the - terms of the Mozilla Public License, v. - 2.0. If a copy of the MPL was not - distributed with this file, You can - obtain one at - http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular file, then -You may include the notice in a location (such as a LICENSE file in a relevant -directory) where a recipient would be likely to look for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - “Incompatible With Secondary Licenses†Notice - - This Source Code Form is “Incompatible - With Secondary Licensesâ€, as defined by - the Mozilla Public License, v. 2.0. - diff --git a/licenses/github.com/huandu/xstrings/LICENSE b/licenses/github.com/huandu/xstrings/LICENSE deleted file mode 100644 index 270177259..000000000 --- a/licenses/github.com/huandu/xstrings/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Huan Du - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/licenses/github.com/imdario/mergo/LICENSE b/licenses/github.com/imdario/mergo/LICENSE deleted file mode 100644 index 686680298..000000000 --- a/licenses/github.com/imdario/mergo/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (c) 2013 Dario Castañé. All rights reserved. -Copyright (c) 2012 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/jhump/protoreflect/LICENSE b/licenses/github.com/jhump/protoreflect/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/github.com/jhump/protoreflect/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/jinzhu/copier/License b/licenses/github.com/jinzhu/copier/License deleted file mode 100644 index e2dc5381e..000000000 --- a/licenses/github.com/jinzhu/copier/License +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Jinzhu - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/jmespath/go-jmespath/LICENSE b/licenses/github.com/jmespath/go-jmespath/LICENSE deleted file mode 100644 index b03310a91..000000000 --- a/licenses/github.com/jmespath/go-jmespath/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright 2015 James Saryerwinnie - -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. diff --git a/licenses/github.com/jonboulle/clockwork/LICENSE b/licenses/github.com/jonboulle/clockwork/LICENSE deleted file mode 100644 index 5c304d1a4..000000000 --- a/licenses/github.com/jonboulle/clockwork/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. diff --git a/licenses/github.com/josharian/intern/license.md b/licenses/github.com/josharian/intern/license.md deleted file mode 100644 index 353d3055f..000000000 --- a/licenses/github.com/josharian/intern/license.md +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 Josh Bleecher Snyder - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/josharian/native/license b/licenses/github.com/josharian/native/license deleted file mode 100644 index 6e617a9c7..000000000 --- a/licenses/github.com/josharian/native/license +++ /dev/null @@ -1,7 +0,0 @@ -Copyright 2020 Josh Bleecher Snyder - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/json-iterator/go/LICENSE b/licenses/github.com/json-iterator/go/LICENSE deleted file mode 100644 index 2cf4f5ab2..000000000 --- a/licenses/github.com/json-iterator/go/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016 json-iterator - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/k0kubun/pp/LICENSE.txt b/licenses/github.com/k0kubun/pp/LICENSE.txt deleted file mode 100644 index c2e6a4b7d..000000000 --- a/licenses/github.com/k0kubun/pp/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Takashi Kokubun - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/klauspost/compress/LICENSE b/licenses/github.com/klauspost/compress/LICENSE deleted file mode 100644 index 87d557477..000000000 --- a/licenses/github.com/klauspost/compress/LICENSE +++ /dev/null @@ -1,304 +0,0 @@ -Copyright (c) 2012 The Go Authors. All rights reserved. -Copyright (c) 2019 Klaus Post. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ------------------- - -Files: gzhttp/* - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2016-2017 The New York Times Company - - 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. - ------------------- - -Files: s2/cmd/internal/readahead/* - -The MIT License (MIT) - -Copyright (c) 2015 Klaus Post - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - ---------------------- -Files: snappy/* -Files: internal/snapref/* - -Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ------------------ - -Files: s2/cmd/internal/filepathx/* - -Copyright 2016 The filepathx Authors - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/klauspost/compress/gzhttp/LICENSE b/licenses/github.com/klauspost/compress/gzhttp/LICENSE deleted file mode 100644 index df6192d36..000000000 --- a/licenses/github.com/klauspost/compress/gzhttp/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2016-2017 The New York Times Company - - 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. diff --git a/licenses/github.com/klauspost/compress/internal/snapref/LICENSE b/licenses/github.com/klauspost/compress/internal/snapref/LICENSE deleted file mode 100644 index 6050c10f4..000000000 --- a/licenses/github.com/klauspost/compress/internal/snapref/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/klauspost/compress/s2/LICENSE b/licenses/github.com/klauspost/compress/s2/LICENSE deleted file mode 100644 index 1d2d645bd..000000000 --- a/licenses/github.com/klauspost/compress/s2/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. -Copyright (c) 2019 Klaus Post. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/klauspost/compress/s2/cmd/internal/filepathx/LICENSE b/licenses/github.com/klauspost/compress/s2/cmd/internal/filepathx/LICENSE deleted file mode 100644 index 29019f0f3..000000000 --- a/licenses/github.com/klauspost/compress/s2/cmd/internal/filepathx/LICENSE +++ /dev/null @@ -1,7 +0,0 @@ -Copyright 2016 The filepathx Authors - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/klauspost/compress/s2/cmd/internal/readahead/LICENSE b/licenses/github.com/klauspost/compress/s2/cmd/internal/readahead/LICENSE deleted file mode 100644 index eaeb61a87..000000000 --- a/licenses/github.com/klauspost/compress/s2/cmd/internal/readahead/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Klaus Post - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/licenses/github.com/klauspost/compress/snappy/LICENSE b/licenses/github.com/klauspost/compress/snappy/LICENSE deleted file mode 100644 index 6050c10f4..000000000 --- a/licenses/github.com/klauspost/compress/snappy/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/klauspost/compress/zstd/internal/xxhash/LICENSE.txt b/licenses/github.com/klauspost/compress/zstd/internal/xxhash/LICENSE.txt deleted file mode 100644 index 24b53065f..000000000 --- a/licenses/github.com/klauspost/compress/zstd/internal/xxhash/LICENSE.txt +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2016 Caleb Spare - -MIT License - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/knadh/koanf/LICENSE b/licenses/github.com/knadh/koanf/LICENSE deleted file mode 100644 index c78ef52fb..000000000 --- a/licenses/github.com/knadh/koanf/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License - -Copyright (c) 2019, Kailash Nadh. https://github.com/knadh - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/kr/pretty/License b/licenses/github.com/kr/pretty/License deleted file mode 100644 index 480a32805..000000000 --- a/licenses/github.com/kr/pretty/License +++ /dev/null @@ -1,19 +0,0 @@ -Copyright 2012 Keith Rarick - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/kr/text/License b/licenses/github.com/kr/text/License deleted file mode 100644 index 480a32805..000000000 --- a/licenses/github.com/kr/text/License +++ /dev/null @@ -1,19 +0,0 @@ -Copyright 2012 Keith Rarick - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/kylelemons/godebug/LICENSE b/licenses/github.com/kylelemons/godebug/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/github.com/kylelemons/godebug/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/leodido/go-urn/LICENSE b/licenses/github.com/leodido/go-urn/LICENSE deleted file mode 100644 index 8c3504a5a..000000000 --- a/licenses/github.com/leodido/go-urn/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 Leonardo Di Donato - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/lestrrat-go/backoff/v2/LICENSE b/licenses/github.com/lestrrat-go/backoff/v2/LICENSE deleted file mode 100644 index 3c0d13224..000000000 --- a/licenses/github.com/lestrrat-go/backoff/v2/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 lestrrat - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/lestrrat-go/blackmagic/LICENSE b/licenses/github.com/lestrrat-go/blackmagic/LICENSE deleted file mode 100644 index 188ea7685..000000000 --- a/licenses/github.com/lestrrat-go/blackmagic/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021 lestrrat-go - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/lestrrat-go/httpcc/LICENSE b/licenses/github.com/lestrrat-go/httpcc/LICENSE deleted file mode 100644 index 963209bfb..000000000 --- a/licenses/github.com/lestrrat-go/httpcc/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020 lestrrat-go - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/lestrrat-go/iter/LICENSE b/licenses/github.com/lestrrat-go/iter/LICENSE deleted file mode 100644 index 963209bfb..000000000 --- a/licenses/github.com/lestrrat-go/iter/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020 lestrrat-go - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/lestrrat-go/jwx/LICENSE b/licenses/github.com/lestrrat-go/jwx/LICENSE deleted file mode 100644 index 205e33a7f..000000000 --- a/licenses/github.com/lestrrat-go/jwx/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 lestrrat - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/licenses/github.com/lestrrat-go/option/LICENSE b/licenses/github.com/lestrrat-go/option/LICENSE deleted file mode 100644 index 188ea7685..000000000 --- a/licenses/github.com/lestrrat-go/option/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021 lestrrat-go - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/liggitt/tabwriter/LICENSE b/licenses/github.com/liggitt/tabwriter/LICENSE deleted file mode 100644 index 6a66aea5e..000000000 --- a/licenses/github.com/liggitt/tabwriter/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/lucas-clemente/quic-go/LICENSE b/licenses/github.com/lucas-clemente/quic-go/LICENSE deleted file mode 100644 index 51378befb..000000000 --- a/licenses/github.com/lucas-clemente/quic-go/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016 the quic-go authors & Google, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/magiconair/properties/LICENSE.md b/licenses/github.com/magiconair/properties/LICENSE.md deleted file mode 100644 index 79c87e3e6..000000000 --- a/licenses/github.com/magiconair/properties/LICENSE.md +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2013-2020, Frank Schroeder - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/mailru/easyjson/LICENSE b/licenses/github.com/mailru/easyjson/LICENSE deleted file mode 100644 index fbff658f7..000000000 --- a/licenses/github.com/mailru/easyjson/LICENSE +++ /dev/null @@ -1,7 +0,0 @@ -Copyright (c) 2016 Mail.Ru Group - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/marten-seemann/qpack/LICENSE.md b/licenses/github.com/marten-seemann/qpack/LICENSE.md deleted file mode 100644 index 1ac5a2d9a..000000000 --- a/licenses/github.com/marten-seemann/qpack/LICENSE.md +++ /dev/null @@ -1,7 +0,0 @@ -Copyright 2019 Marten Seemann - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/marten-seemann/qtls-go1-18/LICENSE b/licenses/github.com/marten-seemann/qtls-go1-18/LICENSE deleted file mode 100644 index 6a66aea5e..000000000 --- a/licenses/github.com/marten-seemann/qtls-go1-18/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/mattn/go-colorable/LICENSE b/licenses/github.com/mattn/go-colorable/LICENSE deleted file mode 100644 index 91b5cef30..000000000 --- a/licenses/github.com/mattn/go-colorable/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Yasuhiro Matsumoto - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/mattn/go-isatty/LICENSE b/licenses/github.com/mattn/go-isatty/LICENSE deleted file mode 100644 index 65dc692b6..000000000 --- a/licenses/github.com/mattn/go-isatty/LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -Copyright (c) Yasuhiro MATSUMOTO - -MIT License (Expat) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/mattn/go-runewidth/LICENSE b/licenses/github.com/mattn/go-runewidth/LICENSE deleted file mode 100644 index 91b5cef30..000000000 --- a/licenses/github.com/mattn/go-runewidth/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Yasuhiro Matsumoto - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/matttproud/golang_protobuf_extensions/LICENSE b/licenses/github.com/matttproud/golang_protobuf_extensions/LICENSE deleted file mode 100644 index 8dada3eda..000000000 --- a/licenses/github.com/matttproud/golang_protobuf_extensions/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. diff --git a/licenses/github.com/mdlayher/netlink/LICENSE.md b/licenses/github.com/mdlayher/netlink/LICENSE.md deleted file mode 100644 index 77123e81b..000000000 --- a/licenses/github.com/mdlayher/netlink/LICENSE.md +++ /dev/null @@ -1,9 +0,0 @@ -# MIT License - -Copyright (C) 2016-2021 Matt Layher - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/mdlayher/socket/LICENSE.md b/licenses/github.com/mdlayher/socket/LICENSE.md deleted file mode 100644 index 3ccdb75b2..000000000 --- a/licenses/github.com/mdlayher/socket/LICENSE.md +++ /dev/null @@ -1,9 +0,0 @@ -# MIT License - -Copyright (C) 2021 Matt Layher - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/miekg/dns/LICENSE b/licenses/github.com/miekg/dns/LICENSE deleted file mode 100644 index 55f12ab77..000000000 --- a/licenses/github.com/miekg/dns/LICENSE +++ /dev/null @@ -1,30 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -As this is fork of the official Go code the same license applies. -Extensions of the original work are copyright (c) 2011 Miek Gieben diff --git a/licenses/github.com/mitchellh/copystructure/LICENSE b/licenses/github.com/mitchellh/copystructure/LICENSE deleted file mode 100644 index 229851590..000000000 --- a/licenses/github.com/mitchellh/copystructure/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Mitchell Hashimoto - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/mitchellh/go-homedir/LICENSE b/licenses/github.com/mitchellh/go-homedir/LICENSE deleted file mode 100644 index f9c841a51..000000000 --- a/licenses/github.com/mitchellh/go-homedir/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 Mitchell Hashimoto - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/mitchellh/go-wordwrap/LICENSE.md b/licenses/github.com/mitchellh/go-wordwrap/LICENSE.md deleted file mode 100644 index 229851590..000000000 --- a/licenses/github.com/mitchellh/go-wordwrap/LICENSE.md +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Mitchell Hashimoto - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/mitchellh/mapstructure/LICENSE b/licenses/github.com/mitchellh/mapstructure/LICENSE deleted file mode 100644 index f9c841a51..000000000 --- a/licenses/github.com/mitchellh/mapstructure/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 Mitchell Hashimoto - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/mitchellh/reflectwalk/LICENSE b/licenses/github.com/mitchellh/reflectwalk/LICENSE deleted file mode 100644 index f9c841a51..000000000 --- a/licenses/github.com/mitchellh/reflectwalk/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 Mitchell Hashimoto - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/moby/buildkit/LICENSE b/licenses/github.com/moby/buildkit/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/github.com/moby/buildkit/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/moby/spdystream/LICENSE b/licenses/github.com/moby/spdystream/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/github.com/moby/spdystream/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/moby/term/LICENSE b/licenses/github.com/moby/term/LICENSE deleted file mode 100644 index 6d8d58fb6..000000000 --- a/licenses/github.com/moby/term/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2013-2018 Docker, Inc. - - 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 - - https://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. diff --git a/licenses/github.com/modern-go/concurrent/LICENSE b/licenses/github.com/modern-go/concurrent/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/github.com/modern-go/concurrent/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/modern-go/reflect2/LICENSE b/licenses/github.com/modern-go/reflect2/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/github.com/modern-go/reflect2/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/monochromegane/go-gitignore/LICENSE b/licenses/github.com/monochromegane/go-gitignore/LICENSE deleted file mode 100644 index 91b84e927..000000000 --- a/licenses/github.com/monochromegane/go-gitignore/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) [2015] [go-gitignore] - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/munnerz/goautoneg/LICENSE b/licenses/github.com/munnerz/goautoneg/LICENSE deleted file mode 100644 index bbc7b897c..000000000 --- a/licenses/github.com/munnerz/goautoneg/LICENSE +++ /dev/null @@ -1,31 +0,0 @@ -Copyright (c) 2011, Open Knowledge Foundation Ltd. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - Neither the name of the Open Knowledge Foundation Ltd. nor the - names of its contributors may be used to endorse or promote - products derived from this software without specific prior written - permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/nacos-group/nacos-sdk-go/LICENSE b/licenses/github.com/nacos-group/nacos-sdk-go/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/github.com/nacos-group/nacos-sdk-go/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/natefinch/lumberjack/LICENSE b/licenses/github.com/natefinch/lumberjack/LICENSE deleted file mode 100644 index c3d4cc307..000000000 --- a/licenses/github.com/natefinch/lumberjack/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Nate Finch - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/licenses/github.com/onsi/gomega/LICENSE b/licenses/github.com/onsi/gomega/LICENSE deleted file mode 100644 index 9415ee72c..000000000 --- a/licenses/github.com/onsi/gomega/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2013-2014 Onsi Fakhouri - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/opencontainers/go-digest/LICENSE b/licenses/github.com/opencontainers/go-digest/LICENSE deleted file mode 100644 index 3ac8ab648..000000000 --- a/licenses/github.com/opencontainers/go-digest/LICENSE +++ /dev/null @@ -1,192 +0,0 @@ - - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2019, 2020 OCI Contributors - Copyright 2016 Docker, Inc. - - 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 - - https://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. diff --git a/licenses/github.com/opencontainers/image-spec/LICENSE b/licenses/github.com/opencontainers/image-spec/LICENSE deleted file mode 100644 index 9fdc20fdb..000000000 --- a/licenses/github.com/opencontainers/image-spec/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2016 The Linux Foundation. - - 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. diff --git a/licenses/github.com/openshift/api/LICENSE b/licenses/github.com/openshift/api/LICENSE deleted file mode 100644 index 5c389317e..000000000 --- a/licenses/github.com/openshift/api/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2020 Red Hat, Inc. - - 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. diff --git a/licenses/github.com/opentracing/opentracing-go/LICENSE b/licenses/github.com/opentracing/opentracing-go/LICENSE deleted file mode 100644 index f0027349e..000000000 --- a/licenses/github.com/opentracing/opentracing-go/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2016 The OpenTracing Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/licenses/github.com/opentrx/seata-golang/v2/LICENSE b/licenses/github.com/opentrx/seata-golang/v2/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/github.com/opentrx/seata-golang/v2/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/pelletier/go-toml/LICENSE b/licenses/github.com/pelletier/go-toml/LICENSE deleted file mode 100644 index f414553c2..000000000 --- a/licenses/github.com/pelletier/go-toml/LICENSE +++ /dev/null @@ -1,247 +0,0 @@ -The bulk of github.com/pelletier/go-toml is distributed under the MIT license -(see below), with the exception of localtime.go and localtime.test.go. -Those two files have been copied over from Google's civil library at revision -ed46f5086358513cf8c25f8e3f022cb838a49d66, and are distributed under the Apache -2.0 license (see below). - - -github.com/pelletier/go-toml: - - -The MIT License (MIT) - -Copyright (c) 2013 - 2021 Thomas Pelletier, Eric Anderton - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - -localtime.go, localtime_test.go: - -Originals: - https://raw.githubusercontent.com/googleapis/google-cloud-go/ed46f5086358513cf8c25f8e3f022cb838a49d66/civil/civil.go - https://raw.githubusercontent.com/googleapis/google-cloud-go/ed46f5086358513cf8c25f8e3f022cb838a49d66/civil/civil_test.go -Changes: - * Renamed files from civil* to localtime*. - * Package changed from civil to toml. - * 'Local' prefix added to all structs. -License: - https://raw.githubusercontent.com/googleapis/google-cloud-go/ed46f5086358513cf8c25f8e3f022cb838a49d66/LICENSE - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/peterbourgon/diskv/LICENSE b/licenses/github.com/peterbourgon/diskv/LICENSE deleted file mode 100644 index 41ce7f16e..000000000 --- a/licenses/github.com/peterbourgon/diskv/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2011-2012 Peter Bourgon - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/pierrec/lz4/LICENSE b/licenses/github.com/pierrec/lz4/LICENSE deleted file mode 100644 index bd899d835..000000000 --- a/licenses/github.com/pierrec/lz4/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (c) 2015, Pierre Curto -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of xxHash nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/licenses/github.com/pkg/errors/LICENSE b/licenses/github.com/pkg/errors/LICENSE deleted file mode 100644 index 835ba3e75..000000000 --- a/licenses/github.com/pkg/errors/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) 2015, Dave Cheney -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/pmezard/go-difflib/LICENSE b/licenses/github.com/pmezard/go-difflib/LICENSE deleted file mode 100644 index c67dad612..000000000 --- a/licenses/github.com/pmezard/go-difflib/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2013, Patrick Mezard -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - The names of its contributors may not be used to endorse or promote -products derived from this software without specific prior written -permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/prometheus/client_golang/LICENSE b/licenses/github.com/prometheus/client_golang/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/github.com/prometheus/client_golang/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/prometheus/client_model/LICENSE b/licenses/github.com/prometheus/client_model/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/github.com/prometheus/client_model/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/prometheus/common/LICENSE b/licenses/github.com/prometheus/common/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/github.com/prometheus/common/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/prometheus/procfs/LICENSE b/licenses/github.com/prometheus/procfs/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/github.com/prometheus/procfs/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/prometheus/prom2json/LICENSE b/licenses/github.com/prometheus/prom2json/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/github.com/prometheus/prom2json/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/prometheus/prometheus/LICENSE b/licenses/github.com/prometheus/prometheus/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/github.com/prometheus/prometheus/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/prometheus/statsd_exporter/LICENSE b/licenses/github.com/prometheus/statsd_exporter/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/github.com/prometheus/statsd_exporter/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/rcrowley/go-metrics/LICENSE b/licenses/github.com/rcrowley/go-metrics/LICENSE deleted file mode 100644 index 363fa9ee7..000000000 --- a/licenses/github.com/rcrowley/go-metrics/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -Copyright 2012 Richard Crowley. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - -THIS SOFTWARE IS PROVIDED BY RICHARD CROWLEY ``AS IS'' AND ANY EXPRESS -OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL RICHARD CROWLEY OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -THE POSSIBILITY OF SUCH DAMAGE. - -The views and conclusions contained in the software and documentation -are those of the authors and should not be interpreted as representing -official policies, either expressed or implied, of Richard Crowley. diff --git a/licenses/github.com/rivo/uniseg/LICENSE.txt b/licenses/github.com/rivo/uniseg/LICENSE.txt deleted file mode 100644 index 5040f1ef8..000000000 --- a/licenses/github.com/rivo/uniseg/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 Oliver Kuederle - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/rogpeppe/go-internal/LICENSE b/licenses/github.com/rogpeppe/go-internal/LICENSE deleted file mode 100644 index 49ea0f928..000000000 --- a/licenses/github.com/rogpeppe/go-internal/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2018 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/russross/blackfriday/LICENSE.txt b/licenses/github.com/russross/blackfriday/LICENSE.txt deleted file mode 100644 index 7fbb253a8..000000000 --- a/licenses/github.com/russross/blackfriday/LICENSE.txt +++ /dev/null @@ -1,28 +0,0 @@ -Blackfriday is distributed under the Simplified BSD License: - -Copyright © 2011 Russ Ross -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided with - the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/russross/blackfriday/v2/LICENSE.txt b/licenses/github.com/russross/blackfriday/v2/LICENSE.txt deleted file mode 100644 index 2885af360..000000000 --- a/licenses/github.com/russross/blackfriday/v2/LICENSE.txt +++ /dev/null @@ -1,29 +0,0 @@ -Blackfriday is distributed under the Simplified BSD License: - -> Copyright © 2011 Russ Ross -> All rights reserved. -> -> Redistribution and use in source and binary forms, with or without -> modification, are permitted provided that the following conditions -> are met: -> -> 1. Redistributions of source code must retain the above copyright -> notice, this list of conditions and the following disclaimer. -> -> 2. Redistributions in binary form must reproduce the above -> copyright notice, this list of conditions and the following -> disclaimer in the documentation and/or other materials provided with -> the distribution. -> -> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -> "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -> LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -> FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -> COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -> INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -> BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -> LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -> CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -> LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -> ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -> POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/ryanuber/go-glob/LICENSE b/licenses/github.com/ryanuber/go-glob/LICENSE deleted file mode 100644 index bdfbd9514..000000000 --- a/licenses/github.com/ryanuber/go-glob/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Ryan Uber - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/satori/go.uuid/LICENSE b/licenses/github.com/satori/go.uuid/LICENSE deleted file mode 100644 index 926d54987..000000000 --- a/licenses/github.com/satori/go.uuid/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (C) 2013-2018 by Maxim Bublis - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/github.com/shirou/gopsutil/v3/LICENSE b/licenses/github.com/shirou/gopsutil/v3/LICENSE deleted file mode 100644 index 6f06adcbf..000000000 --- a/licenses/github.com/shirou/gopsutil/v3/LICENSE +++ /dev/null @@ -1,61 +0,0 @@ -gopsutil is distributed under BSD license reproduced below. - -Copyright (c) 2014, WAKAYAMA Shirou -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of the gopsutil authors nor the names of its contributors - may be used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -------- -internal/common/binary.go in the gopsutil is copied and modified from golang/encoding/binary.go. - - - -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/licenses/github.com/shopspring/decimal/LICENSE b/licenses/github.com/shopspring/decimal/LICENSE deleted file mode 100644 index ad2148aaf..000000000 --- a/licenses/github.com/shopspring/decimal/LICENSE +++ /dev/null @@ -1,45 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Spring, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -- Based on https://github.com/oguzbilgic/fpd, which has the following license: -""" -The MIT License (MIT) - -Copyright (c) 2013 Oguz Bilgic - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -""" diff --git a/licenses/github.com/sirupsen/logrus/LICENSE b/licenses/github.com/sirupsen/logrus/LICENSE deleted file mode 100644 index f090cb42f..000000000 --- a/licenses/github.com/sirupsen/logrus/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Simon Eskildsen - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/spaolacci/murmur3/LICENSE b/licenses/github.com/spaolacci/murmur3/LICENSE deleted file mode 100644 index 2a46fd750..000000000 --- a/licenses/github.com/spaolacci/murmur3/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -Copyright 2013, Sébastien Paolacci. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the library nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/spf13/afero/LICENSE.txt b/licenses/github.com/spf13/afero/LICENSE.txt deleted file mode 100644 index 298f0e266..000000000 --- a/licenses/github.com/spf13/afero/LICENSE.txt +++ /dev/null @@ -1,174 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. diff --git a/licenses/github.com/spf13/cast/LICENSE b/licenses/github.com/spf13/cast/LICENSE deleted file mode 100644 index 4527efb9c..000000000 --- a/licenses/github.com/spf13/cast/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Steve Francia - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/licenses/github.com/spf13/cobra/LICENSE.txt b/licenses/github.com/spf13/cobra/LICENSE.txt deleted file mode 100644 index 298f0e266..000000000 --- a/licenses/github.com/spf13/cobra/LICENSE.txt +++ /dev/null @@ -1,174 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. diff --git a/licenses/github.com/spf13/jwalterweatherman/LICENSE b/licenses/github.com/spf13/jwalterweatherman/LICENSE deleted file mode 100644 index 4527efb9c..000000000 --- a/licenses/github.com/spf13/jwalterweatherman/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Steve Francia - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/licenses/github.com/spf13/pflag/LICENSE b/licenses/github.com/spf13/pflag/LICENSE deleted file mode 100644 index 63ed1cfea..000000000 --- a/licenses/github.com/spf13/pflag/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (c) 2012 Alex Ogier. All rights reserved. -Copyright (c) 2012 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/spf13/viper/LICENSE b/licenses/github.com/spf13/viper/LICENSE deleted file mode 100644 index 4527efb9c..000000000 --- a/licenses/github.com/spf13/viper/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Steve Francia - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/licenses/github.com/stoewer/go-strcase/LICENSE b/licenses/github.com/stoewer/go-strcase/LICENSE deleted file mode 100644 index a105a3819..000000000 --- a/licenses/github.com/stoewer/go-strcase/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2017, Adrian Stoewer - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/stretchr/testify/LICENSE b/licenses/github.com/stretchr/testify/LICENSE deleted file mode 100644 index 4b0421cf9..000000000 --- a/licenses/github.com/stretchr/testify/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/subosito/gotenv/LICENSE b/licenses/github.com/subosito/gotenv/LICENSE deleted file mode 100644 index f64ccaedc..000000000 --- a/licenses/github.com/subosito/gotenv/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 Alif Rachmawadi - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/tklauser/go-sysconf/LICENSE b/licenses/github.com/tklauser/go-sysconf/LICENSE deleted file mode 100644 index cf198debc..000000000 --- a/licenses/github.com/tklauser/go-sysconf/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -BSD 3-Clause License - -Copyright (c) 2018-2021, Tobias Klauser -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/tklauser/numcpus/LICENSE b/licenses/github.com/tklauser/numcpus/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/github.com/tklauser/numcpus/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/uber/jaeger-client-go/LICENSE b/licenses/github.com/uber/jaeger-client-go/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/github.com/uber/jaeger-client-go/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/uber/jaeger-lib/LICENSE b/licenses/github.com/uber/jaeger-lib/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/github.com/uber/jaeger-lib/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/github.com/ugorji/go/codec/LICENSE b/licenses/github.com/ugorji/go/codec/LICENSE deleted file mode 100644 index 36a8bcf10..000000000 --- a/licenses/github.com/ugorji/go/codec/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2012-2020 Ugorji Nwoke. -All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/vbatts/tar-split/LICENSE b/licenses/github.com/vbatts/tar-split/LICENSE deleted file mode 100644 index ca03685b1..000000000 --- a/licenses/github.com/vbatts/tar-split/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (c) 2015 Vincent Batts, Raleigh, NC, USA - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its contributors -may be used to endorse or promote products derived from this software without -specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/github.com/vishvananda/netlink/LICENSE b/licenses/github.com/vishvananda/netlink/LICENSE deleted file mode 100644 index 9f64db858..000000000 --- a/licenses/github.com/vishvananda/netlink/LICENSE +++ /dev/null @@ -1,192 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2014 Vishvananda Ishaya. - Copyright 2014 Docker, Inc. - - 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. diff --git a/licenses/github.com/vishvananda/netns/LICENSE b/licenses/github.com/vishvananda/netns/LICENSE deleted file mode 100644 index 9f64db858..000000000 --- a/licenses/github.com/vishvananda/netns/LICENSE +++ /dev/null @@ -1,192 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2014 Vishvananda Ishaya. - Copyright 2014 Docker, Inc. - - 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. diff --git a/licenses/github.com/wasmerio/wasmer-go/LICENSE b/licenses/github.com/wasmerio/wasmer-go/LICENSE deleted file mode 100644 index 62bb543eb..000000000 --- a/licenses/github.com/wasmerio/wasmer-go/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019-present Wasmer, Inc. and its affiliates. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/github.com/xeipuuv/gojsonpointer/NONE b/licenses/github.com/xeipuuv/gojsonpointer/NONE deleted file mode 100644 index 10e0e777e..000000000 --- a/licenses/github.com/xeipuuv/gojsonpointer/NONE +++ /dev/null @@ -1 +0,0 @@ -NO LICENSE FOUND diff --git a/licenses/github.com/xeipuuv/gojsonreference/NONE b/licenses/github.com/xeipuuv/gojsonreference/NONE deleted file mode 100644 index 10e0e777e..000000000 --- a/licenses/github.com/xeipuuv/gojsonreference/NONE +++ /dev/null @@ -1 +0,0 @@ -NO LICENSE FOUND diff --git a/licenses/github.com/xeipuuv/gojsonschema/NONE b/licenses/github.com/xeipuuv/gojsonschema/NONE deleted file mode 100644 index 10e0e777e..000000000 --- a/licenses/github.com/xeipuuv/gojsonschema/NONE +++ /dev/null @@ -1 +0,0 @@ -NO LICENSE FOUND diff --git a/licenses/github.com/xlab/treeprint/LICENSE b/licenses/github.com/xlab/treeprint/LICENSE deleted file mode 100644 index 5ab533ad2..000000000 --- a/licenses/github.com/xlab/treeprint/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) -Copyright © 2016 Maxim Kupriianov - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the “Softwareâ€), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS ISâ€, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/github.com/yl2chen/cidranger/LICENSE b/licenses/github.com/yl2chen/cidranger/LICENSE deleted file mode 100644 index c41c62239..000000000 --- a/licenses/github.com/yl2chen/cidranger/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2017 Yulin - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/go.etcd.io/etcd/api/v3/LICENSE b/licenses/go.etcd.io/etcd/api/v3/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/go.etcd.io/etcd/api/v3/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/go.etcd.io/etcd/client/pkg/v3/LICENSE b/licenses/go.etcd.io/etcd/client/pkg/v3/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/go.etcd.io/etcd/client/pkg/v3/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/go.etcd.io/etcd/client/v3/LICENSE b/licenses/go.etcd.io/etcd/client/v3/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/go.etcd.io/etcd/client/v3/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/go.opencensus.io/LICENSE b/licenses/go.opencensus.io/LICENSE deleted file mode 100644 index 7a4a3ea24..000000000 --- a/licenses/go.opencensus.io/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. \ No newline at end of file diff --git a/licenses/go.opentelemetry.io/otel/LICENSE b/licenses/go.opentelemetry.io/otel/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/go.opentelemetry.io/otel/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/go.opentelemetry.io/otel/exporters/jaeger/LICENSE b/licenses/go.opentelemetry.io/otel/exporters/jaeger/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/go.opentelemetry.io/otel/exporters/jaeger/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/LICENSE b/licenses/go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/LICENSE deleted file mode 100644 index 2bc6fbbf6..000000000 --- a/licenses/go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/LICENSE +++ /dev/null @@ -1,306 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. - --------------------------------------------------- -SOFTWARE DISTRIBUTED WITH THRIFT: - -The Apache Thrift software includes a number of subcomponents with -separate copyright notices and license terms. Your use of the source -code for the these subcomponents is subject to the terms and -conditions of the following licenses. - --------------------------------------------------- -Portions of the following files are licensed under the MIT License: - - lib/erl/src/Makefile.am - -Please see doc/otp-base-license.txt for the full terms of this license. - --------------------------------------------------- -For the aclocal/ax_boost_base.m4 and contrib/fb303/aclocal/ax_boost_base.m4 components: - -# Copyright (c) 2007 Thomas Porschberg -# -# Copying and distribution of this file, with or without -# modification, are permitted in any medium without royalty provided -# the copyright notice and this notice are preserved. - --------------------------------------------------- -For the lib/nodejs/lib/thrift/json_parse.js: - -/* - json_parse.js - 2015-05-02 - Public Domain. - NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. - -*/ -(By Douglas Crockford ) - --------------------------------------------------- -For lib/cpp/src/thrift/windows/SocketPair.cpp - -/* socketpair.c - * Copyright 2007 by Nathan C. Myers ; some rights reserved. - * This code is Free Software. It may be copied freely, in original or - * modified form, subject only to the restrictions that (1) the author is - * relieved from all responsibilities for any use for any purpose, and (2) - * this copyright notice must be retained, unchanged, in its entirety. If - * for any reason the author might be held responsible for any consequences - * of copying or use, license is withheld. - */ - - --------------------------------------------------- -For lib/py/compat/win32/stdint.h - -// ISO C9x compliant stdint.h for Microsoft Visual Studio -// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 -// -// Copyright (c) 2006-2008 Alexander Chemeris -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// 3. The name of the author may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -/////////////////////////////////////////////////////////////////////////////// - - --------------------------------------------------- -Codegen template in t_html_generator.h - -* Bootstrap v2.0.3 -* -* Copyright 2012 Twitter, Inc -* Licensed under the Apache License v2.0 -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Designed and built with all the love in the world @twitter by @mdo and @fat. - ---------------------------------------------------- -For t_cl_generator.cc - - * Copyright (c) 2008- Patrick Collison - * Copyright (c) 2006- Facebook - ---------------------------------------------------- diff --git a/licenses/go.opentelemetry.io/otel/exporters/otlp/internal/retry/LICENSE b/licenses/go.opentelemetry.io/otel/exporters/otlp/internal/retry/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/go.opentelemetry.io/otel/exporters/otlp/internal/retry/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/go.opentelemetry.io/otel/exporters/otlp/otlptrace/LICENSE b/licenses/go.opentelemetry.io/otel/exporters/otlp/otlptrace/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/go.opentelemetry.io/otel/exporters/otlp/otlptrace/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/LICENSE b/licenses/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/go.opentelemetry.io/otel/exporters/prometheus/LICENSE b/licenses/go.opentelemetry.io/otel/exporters/prometheus/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/go.opentelemetry.io/otel/exporters/prometheus/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/go.opentelemetry.io/otel/metric/LICENSE b/licenses/go.opentelemetry.io/otel/metric/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/go.opentelemetry.io/otel/metric/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/go.opentelemetry.io/otel/sdk/LICENSE b/licenses/go.opentelemetry.io/otel/sdk/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/go.opentelemetry.io/otel/sdk/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/go.opentelemetry.io/otel/sdk/metric/LICENSE b/licenses/go.opentelemetry.io/otel/sdk/metric/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/go.opentelemetry.io/otel/sdk/metric/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/go.opentelemetry.io/otel/trace/LICENSE b/licenses/go.opentelemetry.io/otel/trace/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/go.opentelemetry.io/otel/trace/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/go.opentelemetry.io/proto/otlp/LICENSE b/licenses/go.opentelemetry.io/proto/otlp/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/licenses/go.opentelemetry.io/proto/otlp/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/go.starlark.net/LICENSE b/licenses/go.starlark.net/LICENSE deleted file mode 100644 index a6609a143..000000000 --- a/licenses/go.starlark.net/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -Copyright (c) 2017 The Bazel Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the - distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/go.uber.org/atomic/LICENSE.txt b/licenses/go.uber.org/atomic/LICENSE.txt deleted file mode 100644 index 8765c9fbc..000000000 --- a/licenses/go.uber.org/atomic/LICENSE.txt +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2016 Uber Technologies, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/go.uber.org/multierr/LICENSE.txt b/licenses/go.uber.org/multierr/LICENSE.txt deleted file mode 100644 index 413e30f7c..000000000 --- a/licenses/go.uber.org/multierr/LICENSE.txt +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2017-2021 Uber Technologies, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/go.uber.org/zap/LICENSE.txt b/licenses/go.uber.org/zap/LICENSE.txt deleted file mode 100644 index 6652bed45..000000000 --- a/licenses/go.uber.org/zap/LICENSE.txt +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2016-2017 Uber Technologies, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/golang.org/x/arch/LICENSE b/licenses/golang.org/x/arch/LICENSE deleted file mode 100644 index d29b37261..000000000 --- a/licenses/golang.org/x/arch/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2015 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/golang.org/x/crypto/LICENSE b/licenses/golang.org/x/crypto/LICENSE deleted file mode 100644 index 6a66aea5e..000000000 --- a/licenses/golang.org/x/crypto/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/golang.org/x/net/LICENSE b/licenses/golang.org/x/net/LICENSE deleted file mode 100644 index 6a66aea5e..000000000 --- a/licenses/golang.org/x/net/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/golang.org/x/oauth2/LICENSE b/licenses/golang.org/x/oauth2/LICENSE deleted file mode 100644 index 6a66aea5e..000000000 --- a/licenses/golang.org/x/oauth2/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/golang.org/x/sync/LICENSE b/licenses/golang.org/x/sync/LICENSE deleted file mode 100644 index 6a66aea5e..000000000 --- a/licenses/golang.org/x/sync/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/golang.org/x/sys/LICENSE b/licenses/golang.org/x/sys/LICENSE deleted file mode 100644 index 6a66aea5e..000000000 --- a/licenses/golang.org/x/sys/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/golang.org/x/term/LICENSE b/licenses/golang.org/x/term/LICENSE deleted file mode 100644 index 6a66aea5e..000000000 --- a/licenses/golang.org/x/term/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/golang.org/x/text/LICENSE b/licenses/golang.org/x/text/LICENSE deleted file mode 100644 index 6a66aea5e..000000000 --- a/licenses/golang.org/x/text/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/golang.org/x/time/LICENSE b/licenses/golang.org/x/time/LICENSE deleted file mode 100644 index 6a66aea5e..000000000 --- a/licenses/golang.org/x/time/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/gomodules.xyz/jsonpatch/v2/LICENSE b/licenses/gomodules.xyz/jsonpatch/v2/LICENSE deleted file mode 100644 index 8f71f43fe..000000000 --- a/licenses/gomodules.xyz/jsonpatch/v2/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. - diff --git a/licenses/gomodules.xyz/jsonpatch/v3/LICENSE b/licenses/gomodules.xyz/jsonpatch/v3/LICENSE deleted file mode 100644 index 8f71f43fe..000000000 --- a/licenses/gomodules.xyz/jsonpatch/v3/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. - diff --git a/licenses/gomodules.xyz/orderedmap/LICENSE b/licenses/gomodules.xyz/orderedmap/LICENSE deleted file mode 100644 index 2732e3795..000000000 --- a/licenses/gomodules.xyz/orderedmap/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2017 Ian Coleman - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, Subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or Substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/google.golang.org/api/LICENSE b/licenses/google.golang.org/api/LICENSE deleted file mode 100644 index 263aa7a0c..000000000 --- a/licenses/google.golang.org/api/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2011 Google Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/google.golang.org/api/internal/third_party/uritemplates/LICENSE b/licenses/google.golang.org/api/internal/third_party/uritemplates/LICENSE deleted file mode 100644 index 7109c6ef9..000000000 --- a/licenses/google.golang.org/api/internal/third_party/uritemplates/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2013 Joshua Tacoma. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/google.golang.org/genproto/LICENSE b/licenses/google.golang.org/genproto/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/google.golang.org/genproto/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/google.golang.org/grpc/LICENSE b/licenses/google.golang.org/grpc/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/google.golang.org/grpc/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/google.golang.org/protobuf/LICENSE b/licenses/google.golang.org/protobuf/LICENSE deleted file mode 100644 index 49ea0f928..000000000 --- a/licenses/google.golang.org/protobuf/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2018 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/gopkg.in/inf.v0/LICENSE b/licenses/gopkg.in/inf.v0/LICENSE deleted file mode 100644 index 87a5cede3..000000000 --- a/licenses/gopkg.in/inf.v0/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (c) 2012 Péter Surányi. Portions Copyright (c) 2009 The Go -Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/gopkg.in/ini.v1/LICENSE b/licenses/gopkg.in/ini.v1/LICENSE deleted file mode 100644 index d361bbcdf..000000000 --- a/licenses/gopkg.in/ini.v1/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright 2014 Unknwon - - 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. diff --git a/licenses/gopkg.in/natefinch/lumberjack.v2/LICENSE b/licenses/gopkg.in/natefinch/lumberjack.v2/LICENSE deleted file mode 100644 index c3d4cc307..000000000 --- a/licenses/gopkg.in/natefinch/lumberjack.v2/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Nate Finch - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/licenses/gopkg.in/square/go-jose.v2/LICENSE b/licenses/gopkg.in/square/go-jose.v2/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/gopkg.in/square/go-jose.v2/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/gopkg.in/square/go-jose.v2/json/LICENSE b/licenses/gopkg.in/square/go-jose.v2/json/LICENSE deleted file mode 100644 index 744875676..000000000 --- a/licenses/gopkg.in/square/go-jose.v2/json/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2012 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/gopkg.in/yaml.v2/LICENSE b/licenses/gopkg.in/yaml.v2/LICENSE deleted file mode 100644 index 8dada3eda..000000000 --- a/licenses/gopkg.in/yaml.v2/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. diff --git a/licenses/gopkg.in/yaml.v3/LICENSE b/licenses/gopkg.in/yaml.v3/LICENSE deleted file mode 100644 index 2683e4bb1..000000000 --- a/licenses/gopkg.in/yaml.v3/LICENSE +++ /dev/null @@ -1,50 +0,0 @@ - -This project is covered by two different licenses: MIT and Apache. - -#### MIT License #### - -The following files were ported to Go from C files of libyaml, and thus -are still covered by their original MIT license, with the additional -copyright staring in 2011 when the project was ported over: - - apic.go emitterc.go parserc.go readerc.go scannerc.go - writerc.go yamlh.go yamlprivateh.go - -Copyright (c) 2006-2010 Kirill Simonov -Copyright (c) 2006-2011 Kirill Simonov - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -### Apache License ### - -All the remaining project files are covered by the Apache license: - -Copyright (c) 2011-2019 Canonical Ltd - -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. diff --git a/licenses/helm.sh/helm/v3/LICENSE b/licenses/helm.sh/helm/v3/LICENSE deleted file mode 100644 index 21c57fae2..000000000 --- a/licenses/helm.sh/helm/v3/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2016 The Kubernetes 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. diff --git a/licenses/helm.sh/helm/v3/pkg/chart/loader/testdata/LICENSE b/licenses/helm.sh/helm/v3/pkg/chart/loader/testdata/LICENSE deleted file mode 100644 index 6121943b1..000000000 --- a/licenses/helm.sh/helm/v3/pkg/chart/loader/testdata/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/licenses/helm.sh/helm/v3/pkg/chart/loader/testdata/frobnitz.v1/LICENSE b/licenses/helm.sh/helm/v3/pkg/chart/loader/testdata/frobnitz.v1/LICENSE deleted file mode 100644 index 6121943b1..000000000 --- a/licenses/helm.sh/helm/v3/pkg/chart/loader/testdata/frobnitz.v1/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/licenses/helm.sh/helm/v3/pkg/chart/loader/testdata/frobnitz.v2.reqs/LICENSE b/licenses/helm.sh/helm/v3/pkg/chart/loader/testdata/frobnitz.v2.reqs/LICENSE deleted file mode 100644 index 6121943b1..000000000 --- a/licenses/helm.sh/helm/v3/pkg/chart/loader/testdata/frobnitz.v2.reqs/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/licenses/helm.sh/helm/v3/pkg/chart/loader/testdata/frobnitz/LICENSE b/licenses/helm.sh/helm/v3/pkg/chart/loader/testdata/frobnitz/LICENSE deleted file mode 100644 index 6121943b1..000000000 --- a/licenses/helm.sh/helm/v3/pkg/chart/loader/testdata/frobnitz/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/licenses/helm.sh/helm/v3/pkg/chart/loader/testdata/frobnitz_backslash/LICENSE b/licenses/helm.sh/helm/v3/pkg/chart/loader/testdata/frobnitz_backslash/LICENSE deleted file mode 100644 index 6121943b1..000000000 --- a/licenses/helm.sh/helm/v3/pkg/chart/loader/testdata/frobnitz_backslash/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/licenses/helm.sh/helm/v3/pkg/chart/loader/testdata/frobnitz_with_bom/LICENSE b/licenses/helm.sh/helm/v3/pkg/chart/loader/testdata/frobnitz_with_bom/LICENSE deleted file mode 100644 index c27b00bf2..000000000 --- a/licenses/helm.sh/helm/v3/pkg/chart/loader/testdata/frobnitz_with_bom/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/licenses/helm.sh/helm/v3/pkg/chart/loader/testdata/frobnitz_with_dev_null/LICENSE b/licenses/helm.sh/helm/v3/pkg/chart/loader/testdata/frobnitz_with_dev_null/LICENSE deleted file mode 100644 index 6121943b1..000000000 --- a/licenses/helm.sh/helm/v3/pkg/chart/loader/testdata/frobnitz_with_dev_null/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/licenses/helm.sh/helm/v3/pkg/chartutil/testdata/dependent-chart-alias/LICENSE b/licenses/helm.sh/helm/v3/pkg/chartutil/testdata/dependent-chart-alias/LICENSE deleted file mode 100644 index 6121943b1..000000000 --- a/licenses/helm.sh/helm/v3/pkg/chartutil/testdata/dependent-chart-alias/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/licenses/helm.sh/helm/v3/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/LICENSE b/licenses/helm.sh/helm/v3/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/LICENSE deleted file mode 100644 index 6121943b1..000000000 --- a/licenses/helm.sh/helm/v3/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/licenses/helm.sh/helm/v3/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/LICENSE b/licenses/helm.sh/helm/v3/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/LICENSE deleted file mode 100644 index 6121943b1..000000000 --- a/licenses/helm.sh/helm/v3/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/licenses/helm.sh/helm/v3/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/LICENSE b/licenses/helm.sh/helm/v3/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/LICENSE deleted file mode 100644 index 6121943b1..000000000 --- a/licenses/helm.sh/helm/v3/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/licenses/helm.sh/helm/v3/pkg/chartutil/testdata/frobnitz/LICENSE b/licenses/helm.sh/helm/v3/pkg/chartutil/testdata/frobnitz/LICENSE deleted file mode 100644 index 6121943b1..000000000 --- a/licenses/helm.sh/helm/v3/pkg/chartutil/testdata/frobnitz/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/licenses/istio.io/api/LICENSE b/licenses/istio.io/api/LICENSE deleted file mode 100644 index 56e48aa37..000000000 --- a/licenses/istio.io/api/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2016-2020 Istio Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/licenses/istio.io/client-go/LICENSE b/licenses/istio.io/client-go/LICENSE deleted file mode 100644 index 56e48aa37..000000000 --- a/licenses/istio.io/client-go/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2016-2020 Istio Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/licenses/istio.io/pkg/LICENSE b/licenses/istio.io/pkg/LICENSE deleted file mode 100644 index 56e48aa37..000000000 --- a/licenses/istio.io/pkg/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2016-2020 Istio Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/licenses/k8s.io/api/LICENSE b/licenses/k8s.io/api/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/k8s.io/api/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/k8s.io/apiextensions-apiserver/LICENSE b/licenses/k8s.io/apiextensions-apiserver/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/k8s.io/apiextensions-apiserver/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/LICENSE b/licenses/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/LICENSE deleted file mode 100644 index 7a4a3ea24..000000000 --- a/licenses/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. \ No newline at end of file diff --git a/licenses/k8s.io/apimachinery/LICENSE b/licenses/k8s.io/apimachinery/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/k8s.io/apimachinery/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/k8s.io/apimachinery/third_party/forked/golang/LICENSE b/licenses/k8s.io/apimachinery/third_party/forked/golang/LICENSE deleted file mode 100644 index 6a66aea5e..000000000 --- a/licenses/k8s.io/apimachinery/third_party/forked/golang/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/k8s.io/cli-runtime/LICENSE b/licenses/k8s.io/cli-runtime/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/k8s.io/cli-runtime/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/k8s.io/client-go/LICENSE b/licenses/k8s.io/client-go/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/k8s.io/client-go/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/k8s.io/client-go/third_party/forked/golang/LICENSE b/licenses/k8s.io/client-go/third_party/forked/golang/LICENSE deleted file mode 100644 index 6a66aea5e..000000000 --- a/licenses/k8s.io/client-go/third_party/forked/golang/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/k8s.io/component-base/LICENSE b/licenses/k8s.io/component-base/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/k8s.io/component-base/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/k8s.io/klog/v2/LICENSE b/licenses/k8s.io/klog/v2/LICENSE deleted file mode 100644 index 37ec93a14..000000000 --- a/licenses/k8s.io/klog/v2/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/k8s.io/kube-openapi/LICENSE b/licenses/k8s.io/kube-openapi/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/k8s.io/kube-openapi/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/k8s.io/kube-openapi/pkg/validation/errors/LICENSE b/licenses/k8s.io/kube-openapi/pkg/validation/errors/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/k8s.io/kube-openapi/pkg/validation/errors/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/k8s.io/kube-openapi/pkg/validation/spec/LICENSE b/licenses/k8s.io/kube-openapi/pkg/validation/spec/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/k8s.io/kube-openapi/pkg/validation/spec/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/k8s.io/kube-openapi/pkg/validation/strfmt/LICENSE b/licenses/k8s.io/kube-openapi/pkg/validation/strfmt/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/k8s.io/kube-openapi/pkg/validation/strfmt/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/k8s.io/kube-openapi/pkg/validation/validate/LICENSE b/licenses/k8s.io/kube-openapi/pkg/validation/validate/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/k8s.io/kube-openapi/pkg/validation/validate/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/k8s.io/kubectl/LICENSE b/licenses/k8s.io/kubectl/LICENSE deleted file mode 100644 index 8dada3eda..000000000 --- a/licenses/k8s.io/kubectl/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. diff --git a/licenses/k8s.io/utils/LICENSE b/licenses/k8s.io/utils/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/licenses/k8s.io/utils/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/licenses/k8s.io/utils/inotify/LICENSE b/licenses/k8s.io/utils/inotify/LICENSE deleted file mode 100644 index 6a66aea5e..000000000 --- a/licenses/k8s.io/utils/inotify/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/k8s.io/utils/internal/third_party/forked/golang/LICENSE b/licenses/k8s.io/utils/internal/third_party/forked/golang/LICENSE deleted file mode 100644 index 744875676..000000000 --- a/licenses/k8s.io/utils/internal/third_party/forked/golang/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2012 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/k8s.io/utils/third_party/forked/golang/LICENSE b/licenses/k8s.io/utils/third_party/forked/golang/LICENSE deleted file mode 100644 index 744875676..000000000 --- a/licenses/k8s.io/utils/third_party/forked/golang/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2012 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/mosn.io/proxy-wasm-go-host/LICENSE b/licenses/mosn.io/proxy-wasm-go-host/LICENSE deleted file mode 100644 index f49a4e16e..000000000 --- a/licenses/mosn.io/proxy-wasm-go-host/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. \ No newline at end of file diff --git a/licenses/sigs.k8s.io/controller-runtime/LICENSE b/licenses/sigs.k8s.io/controller-runtime/LICENSE deleted file mode 100644 index 8dada3eda..000000000 --- a/licenses/sigs.k8s.io/controller-runtime/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. diff --git a/licenses/sigs.k8s.io/gateway-api/LICENSE b/licenses/sigs.k8s.io/gateway-api/LICENSE deleted file mode 100644 index a5949bd7a..000000000 --- a/licenses/sigs.k8s.io/gateway-api/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2020 The Kubernetes Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/licenses/sigs.k8s.io/json/LICENSE b/licenses/sigs.k8s.io/json/LICENSE deleted file mode 100644 index e5adf7f0c..000000000 --- a/licenses/sigs.k8s.io/json/LICENSE +++ /dev/null @@ -1,238 +0,0 @@ -Files other than internal/golang/* licensed under: - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. - - ------------------- - -internal/golang/* files licensed under: - - -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/sigs.k8s.io/kustomize/api/LICENSE b/licenses/sigs.k8s.io/kustomize/api/LICENSE deleted file mode 100644 index 8dada3eda..000000000 --- a/licenses/sigs.k8s.io/kustomize/api/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. diff --git a/licenses/sigs.k8s.io/kustomize/kyaml/LICENSE b/licenses/sigs.k8s.io/kustomize/kyaml/LICENSE deleted file mode 100644 index 8dada3eda..000000000 --- a/licenses/sigs.k8s.io/kustomize/kyaml/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. diff --git a/licenses/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml/LICENSE b/licenses/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml/LICENSE deleted file mode 100644 index 2683e4bb1..000000000 --- a/licenses/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml/LICENSE +++ /dev/null @@ -1,50 +0,0 @@ - -This project is covered by two different licenses: MIT and Apache. - -#### MIT License #### - -The following files were ported to Go from C files of libyaml, and thus -are still covered by their original MIT license, with the additional -copyright staring in 2011 when the project was ported over: - - apic.go emitterc.go parserc.go readerc.go scannerc.go - writerc.go yamlh.go yamlprivateh.go - -Copyright (c) 2006-2010 Kirill Simonov -Copyright (c) 2006-2011 Kirill Simonov - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -### Apache License ### - -All the remaining project files are covered by the Apache license: - -Copyright (c) 2011-2019 Canonical Ltd - -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. diff --git a/licenses/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/qri-io/starlib/util/LICENSE b/licenses/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/qri-io/starlib/util/LICENSE deleted file mode 100644 index 31f292dce..000000000 --- a/licenses/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/qri-io/starlib/util/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2018 QRI, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/licenses/sigs.k8s.io/mcs-api/LICENSE b/licenses/sigs.k8s.io/mcs-api/LICENSE deleted file mode 100644 index 8dada3eda..000000000 --- a/licenses/sigs.k8s.io/mcs-api/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. diff --git a/licenses/sigs.k8s.io/structured-merge-diff/v4/LICENSE b/licenses/sigs.k8s.io/structured-merge-diff/v4/LICENSE deleted file mode 100644 index 8dada3eda..000000000 --- a/licenses/sigs.k8s.io/structured-merge-diff/v4/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. diff --git a/licenses/sigs.k8s.io/yaml/LICENSE b/licenses/sigs.k8s.io/yaml/LICENSE deleted file mode 100644 index 7805d36de..000000000 --- a/licenses/sigs.k8s.io/yaml/LICENSE +++ /dev/null @@ -1,50 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Sam Ghods - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - -Copyright (c) 2012 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/vimagination.zapto.org/byteio/LICENSE b/licenses/vimagination.zapto.org/byteio/LICENSE deleted file mode 100644 index df1fc3dfb..000000000 --- a/licenses/vimagination.zapto.org/byteio/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright (c) 2015, Michael Woolnough - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. diff --git a/manifests/.gitattributes b/manifests/.gitattributes deleted file mode 100644 index 4ccc0f5d8..000000000 --- a/manifests/.gitattributes +++ /dev/null @@ -1,9 +0,0 @@ -*.descriptor linguist-generated=true -*.descriptor -diff -merge -*.descriptor_set linguist-generated=true -*.descriptor_set -diff -merge -*.pb.html linguist-generated=true -*.pb.go linguist-generated=true -*.gen.go linguist-generated=true -*.gen.yaml linguist-generated=true -*_pb2.py linguist-generated=true diff --git a/manifests/addons/dashboards/istio-extension-dashboard.json b/manifests/addons/dashboards/istio-extension-dashboard.json deleted file mode 100644 index ae6606add..000000000 --- a/manifests/addons/dashboards/istio-extension-dashboard.json +++ /dev/null @@ -1,830 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": false, - "gnetId": null, - "graphTooltip": 0, - "links": [], - "panels": [ - { - "collapsed": false, - "datasource": "Prometheus", - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 3, - "panels": [], - "title": "Wasm VMs", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "description": "", - "fieldConfig": { - "defaults": { - "custom": { - "align": null - }, - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 1 - }, - "hiddenSeries": false, - "id": 2, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "avg(envoy_wasm_envoy_wasm_runtime_null_active)", - "interval": "", - "legendFormat": "native", - "refId": "A" - }, - { - "expr": "avg(envoy_wasm_envoy_wasm_runtime_v8_active)", - "interval": "", - "legendFormat": "v8", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Active", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:123", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:124", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {}, - "links": [] - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 1 - }, - "hiddenSeries": false, - "id": 6, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "avg(envoy_wasm_envoy_wasm_runtime_null_created)", - "interval": "", - "legendFormat": "native", - "refId": "A" - }, - { - "expr": "avg(envoy_wasm_envoy_wasm_runtime_v8_created)", - "interval": "", - "legendFormat": "v8", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Created", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:68", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:69", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "datasource": "Prometheus", - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 9 - }, - "id": 7, - "panels": [], - "title": "Wasm Module Remote Load", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {}, - "links": [] - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 8, - "x": 0, - "y": 10 - }, - "hiddenSeries": false, - "id": 11, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "avg(envoy_wasm_remote_load_cache_entries)", - "interval": "", - "legendFormat": "entries", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Cache Entry", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:178", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:179", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {}, - "links": [] - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 8, - "x": 8, - "y": 10 - }, - "hiddenSeries": false, - "id": 8, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "avg(envoy_wasm_remote_load_cache_hits)", - "interval": "", - "legendFormat": "hits", - "refId": "A" - }, - { - "expr": "avg(envoy_wasm_remote_load_cache_misses)", - "interval": "", - "legendFormat": "misses", - "refId": "B" - }, - { - "expr": "avg(envoy_wasm_remote_load_cache_negative_hits)", - "interval": "", - "legendFormat": "negative hits", - "refId": "C" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Cache Visit", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:233", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:234", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {}, - "links": [] - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 8, - "x": 16, - "y": 10 - }, - "hiddenSeries": false, - "id": 10, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "avg(envoy_wasm_remote_load_fetch_failures)", - "interval": "", - "legendFormat": "failures", - "refId": "A" - }, - { - "expr": "avg(envoy_wasm_remote_load_fetch_successes)", - "interval": "", - "legendFormat": "successes", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Remote Fetch", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:288", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:289", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "datasource": "Prometheus", - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 18 - }, - "id": 71, - "panels": [], - "title": "Proxy Resource Usage", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 19 - }, - "hiddenSeries": false, - "id": 72, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(container_memory_working_set_bytes{container=\"istio-proxy\"})", - "format": "time_series", - "hide": false, - "intervalFactor": 2, - "legendFormat": "Total (k8s)", - "refId": "A", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Memory", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:396", - "format": "bytes", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:397", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 19 - }, - "hiddenSeries": false, - "id": 73, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(container_cpu_usage_seconds_total{container=\"istio-proxy\"}[1m]))", - "format": "time_series", - "hide": false, - "intervalFactor": 2, - "legendFormat": "Total (k8s)", - "refId": "A", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "vCPU", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:447", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:448", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "refresh": false, - "schemaVersion": 26, - "style": "dark", - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "2020-10-22T23:11:45.783Z", - "to": "2020-10-23T00:04:19.481Z" - }, - "timepicker": { - "refresh_intervals": [ - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] - }, - "timezone": "", - "title": "Istio Wasm Extension Dashboard", - "uid": "7PAV7ctGz", - "version": 17 -} diff --git a/manifests/addons/dashboards/istio-mesh-dashboard.json b/manifests/addons/dashboards/istio-mesh-dashboard.json deleted file mode 100644 index 90ef9bfe4..000000000 --- a/manifests/addons/dashboards/istio-mesh-dashboard.json +++ /dev/null @@ -1,1744 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": false, - "gnetId": null, - "graphTooltip": 0, - "id": null, - "links": [], - "panels": [ - { - "content": "
\n
\n Istio\n
\n
\n Istio is an open platform that provides a uniform way to secure,\n connect, and \n monitor microservices.\n
\n Need help? Join the Istio community.\n
\n
", - "gridPos": { - "h": 3, - "w": 24, - "x": 0, - "y": 0 - }, - "height": "50px", - "id": 13, - "links": [], - "mode": "html", - "style": { - "font-size": "18pt" - }, - "title": "", - "transparent": true, - "type": "text" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "datasource": "Prometheus", - "format": "ops", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 6, - "x": 0, - "y": 3 - }, - "id": 20, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": true, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "round(sum(irate(istio_requests_total{reporter=\"source\"}[1m])), 0.001)", - "intervalFactor": 1, - "refId": "A", - "step": 4 - } - ], - "thresholds": "", - "title": "Global Request Volume", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "avg" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "datasource": "Prometheus", - "format": "percentunit", - "gauge": { - "maxValue": 100, - "minValue": 80, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": false - }, - "gridPos": { - "h": 3, - "w": 6, - "x": 6, - "y": 3 - }, - "id": 21, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": true, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "sum(rate(istio_requests_total{reporter=\"source\", response_code!~\"5.*\"}[1m])) / sum(rate(istio_requests_total{reporter=\"source\"}[1m]))", - "format": "time_series", - "intervalFactor": 1, - "refId": "A", - "step": 4 - } - ], - "thresholds": "95, 99, 99.5", - "title": "Global Success Rate (non-5xx responses)", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "avg" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "datasource": "Prometheus", - "format": "ops", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 6, - "x": 12, - "y": 3 - }, - "id": 22, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": true, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "sum(irate(istio_requests_total{reporter=\"source\", response_code=~\"4.*\"}[1m]))", - "format": "time_series", - "intervalFactor": 1, - "refId": "A", - "step": 4 - } - ], - "thresholds": "", - "title": "4xxs", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "avg" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "datasource": "Prometheus", - "format": "ops", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 6, - "x": 18, - "y": 3 - }, - "id": 23, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": true, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "sum(irate(istio_requests_total{reporter=\"source\", response_code=~\"5.*\"}[1m]))", - "format": "time_series", - "intervalFactor": 1, - "refId": "A", - "step": 4 - } - ], - "thresholds": "", - "title": "5xxs", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "avg" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": "Prometheus", - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 6, - "x": 0, - "y": 6 - }, - "id": 113, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "max(pilot_k8s_cfg_events{type=\"VirtualService\", event=\"add\"}) - (max(pilot_k8s_cfg_events{type=\"VirtualService\", event=\"delete\"}) or max(up * 0))", - "format": "time_series", - "intervalFactor": 1, - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "Virtual Services", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": "Prometheus", - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 6, - "x": 6, - "y": 6 - }, - "id": 114, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "max(pilot_k8s_cfg_events{type=\"DestinationRule\", event=\"add\"}) - (max(pilot_k8s_cfg_events{type=\"DestinationRule\", event=\"delete\"}) or max(up * 0))", - "format": "time_series", - "intervalFactor": 1, - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "Destination Rules", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": "Prometheus", - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 6, - "x": 12, - "y": 6 - }, - "id": 115, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "max(pilot_k8s_cfg_events{type=\"Gateway\", event=\"add\"}) - (max(pilot_k8s_cfg_events{type=\"Gateway\", event=\"delete\"}) or max(up * 0))", - "format": "time_series", - "intervalFactor": 1, - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "Gateways", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": "Prometheus", - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 6, - "x": 18, - "y": 6 - }, - "id": 116, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "max(pilot_k8s_cfg_events{type=\"WorkloadEntry\", event=\"add\"}) - (max(pilot_k8s_cfg_events{type=\"WorkloadEntry\", event=\"delete\"}) or max(up * 0))", - "format": "time_series", - "intervalFactor": 1, - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "Workload Entries", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": "Prometheus", - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 6, - "x": 0, - "y": 6 - }, - "id": 117, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "max(pilot_k8s_cfg_events{type=\"ServiceEntry\", event=\"add\"}) - (max(pilot_k8s_cfg_events{type=\"ServiceEntry\", event=\"delete\"}) or max(up * 0))", - "format": "time_series", - "intervalFactor": 1, - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "Service Entries", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": "Prometheus", - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 6, - "x": 6, - "y": 6 - }, - "id": 90, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "max(pilot_k8s_cfg_events{type=\"PeerAuthentication\", event=\"add\"}) - (max(pilot_k8s_cfg_events{type=\"PeerAuthentication\", event=\"delete\"}) or max(up * 0))", - "format": "time_series", - "intervalFactor": 1, - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "PeerAuthentication Policies", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": "Prometheus", - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 6, - "x": 12, - "y": 6 - }, - "id": 91, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "max(pilot_k8s_cfg_events{type=\"RequestAuthentication\", event=\"add\"}) - (max(pilot_k8s_cfg_events{type=\"RequestAuthentication\", event=\"delete\"}) or max(up * 0))", - "format": "time_series", - "intervalFactor": 1, - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "RequestAuthentication Policies", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": "Prometheus", - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 6, - "x": 18, - "y": 6 - }, - "id": 92, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "max(pilot_k8s_cfg_events{type=\"AuthorizationPolicy\", event=\"add\"}) - (max(pilot_k8s_cfg_events{type=\"AuthorizationPolicy\", event=\"delete\"}) or max(up * 0))", - "format": "time_series", - "intervalFactor": 1, - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "Authorization Policies", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "columns": [], - "datasource": "Prometheus", - "fontSize": "100%", - "gridPos": { - "h": 21, - "w": 24, - "x": 0, - "y": 9 - }, - "hideTimeOverride": false, - "id": 73, - "links": [], - "pageSize": null, - "repeat": null, - "repeatDirection": "v", - "scroll": true, - "showHeader": true, - "sort": { - "col": 5, - "desc": true - }, - "styles": [ - { - "alias": "Workload", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "link": false, - "linkTargetBlank": false, - "linkTooltip": "Workload dashboard", - "linkUrl": "/dashboard/db/istio-workload-dashboard?var-namespace=${__cell_3:raw}&var-workload=${__cell_2:raw}", - "pattern": "destination_workload", - "preserveFormat": false, - "sanitize": false, - "thresholds": [], - "type": "hidden", - "unit": "short" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "pattern": "Time", - "thresholds": [], - "type": "hidden", - "unit": "short" - }, - { - "alias": "Requests", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "pattern": "Value #A", - "thresholds": [], - "type": "number", - "unit": "ops" - }, - { - "alias": "P50 Latency", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "pattern": "Value #B", - "thresholds": [], - "type": "number", - "unit": "s" - }, - { - "alias": "P90 Latency", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "pattern": "Value #C", - "thresholds": [], - "type": "number", - "unit": "s" - }, - { - "alias": "P99 Latency", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "pattern": "Value #D", - "thresholds": [], - "type": "number", - "unit": "s" - }, - { - "alias": "Success Rate", - "colorMode": "cell", - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "pattern": "Value #E", - "thresholds": [ - ".95", - " 1.00" - ], - "type": "number", - "unit": "percentunit" - }, - { - "alias": "Workload", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "link": true, - "linkTooltip": "$__cell dashboard", - "linkUrl": "/dashboard/db/istio-workload-dashboard?var-workload=${__cell_2:raw}&var-namespace=${__cell_3:raw}", - "pattern": "destination_workload_var", - "thresholds": [], - "type": "number", - "unit": "short" - }, - { - "alias": "Service", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "link": true, - "linkTooltip": "$__cell dashboard", - "linkUrl": "/dashboard/db/istio-service-dashboard?var-service=${__cell_1:raw}", - "pattern": "destination_service", - "thresholds": [], - "type": "string", - "unit": "short" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "pattern": "destination_workload_namespace", - "thresholds": [], - "type": "hidden", - "unit": "short" - } - ], - "targets": [ - { - "expr": "label_join(sum(rate(istio_requests_total{reporter=\"source\", response_code=\"200\"}[1m])) by (destination_workload, destination_workload_namespace, destination_service), \"destination_workload_var\", \".\", \"destination_workload\", \"destination_workload_namespace\")", - "format": "table", - "hide": false, - "instant": true, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload}}.{{ destination_workload_namespace }}", - "refId": "A" - }, - { - "expr": "label_join((histogram_quantile(0.50, sum(rate(istio_request_duration_milliseconds_bucket{reporter=\"source\"}[1m])) by (le, destination_workload, destination_workload_namespace)) / 1000) or histogram_quantile(0.50, sum(rate(istio_request_duration_seconds_bucket{reporter=\"source\"}[1m])) by (le, destination_workload, destination_workload_namespace)), \"destination_workload_var\", \".\", \"destination_workload\", \"destination_workload_namespace\")", - "format": "table", - "hide": false, - "instant": true, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload}}.{{ destination_workload_namespace }}", - "refId": "B" - }, - { - "expr": "label_join((histogram_quantile(0.90, sum(rate(istio_request_duration_milliseconds_bucket{reporter=\"source\"}[1m])) by (le, destination_workload, destination_workload_namespace)) / 1000) or histogram_quantile(0.90, sum(rate(istio_request_duration_seconds_bucket{reporter=\"source\"}[1m])) by (le, destination_workload, destination_workload_namespace)), \"destination_workload_var\", \".\", \"destination_workload\", \"destination_workload_namespace\")", - "format": "table", - "hide": false, - "instant": true, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }}", - "refId": "C" - }, - { - "expr": "label_join((histogram_quantile(0.99, sum(rate(istio_request_duration_milliseconds_bucket{reporter=\"source\"}[1m])) by (le, destination_workload, destination_workload_namespace)) / 1000) or histogram_quantile(0.99, sum(rate(istio_request_duration_seconds_bucket{reporter=\"source\"}[1m])) by (le, destination_workload, destination_workload_namespace)), \"destination_workload_var\", \".\", \"destination_workload\", \"destination_workload_namespace\")", - "format": "table", - "hide": false, - "instant": true, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }}", - "refId": "D" - }, - { - "expr": "label_join((sum(rate(istio_requests_total{reporter=\"source\", response_code!~\"5.*\"}[1m])) by (destination_workload, destination_workload_namespace) / sum(rate(istio_requests_total{reporter=\"source\"}[1m])) by (destination_workload, destination_workload_namespace)), \"destination_workload_var\", \".\", \"destination_workload\", \"destination_workload_namespace\")", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }}", - "refId": "E" - } - ], - "timeFrom": null, - "title": "HTTP/GRPC Workloads", - "transform": "table", - "type": "table" - }, - { - "columns": [], - "datasource": "Prometheus", - "fontSize": "100%", - "gridPos": { - "h": 18, - "w": 24, - "x": 0, - "y": 30 - }, - "hideTimeOverride": false, - "id": 109, - "links": [], - "pageSize": null, - "repeatDirection": "v", - "scroll": true, - "showHeader": true, - "sort": { - "col": 5, - "desc": true - }, - "styles": [ - { - "alias": "Workload", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "link": false, - "linkTargetBlank": false, - "linkTooltip": "$__cell dashboard", - "linkUrl": "/dashboard/db/istio-workload-dashboard?var-namespace=${__cell_3:raw}&var-workload=${__cell_2:raw}", - "pattern": "destination_workload", - "preserveFormat": false, - "sanitize": false, - "thresholds": [], - "type": "hidden", - "unit": "short" - }, - { - "alias": "Bytes Sent", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "pattern": "Value #A", - "thresholds": [ - "" - ], - "type": "number", - "unit": "Bps" - }, - { - "alias": "Bytes Received", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "pattern": "Value #B", - "thresholds": [], - "type": "number", - "unit": "Bps" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "pattern": "Time", - "thresholds": [], - "type": "hidden", - "unit": "short" - }, - { - "alias": "Workload", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "link": true, - "linkTooltip": "$__cell dashboard", - "linkUrl": "/dashboard/db/istio-workload-dashboard?var-namespace=${__cell_3:raw}&var-workload=${__cell_2:raw}", - "pattern": "destination_workload_var", - "thresholds": [], - "type": "string", - "unit": "short" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "pattern": "destination_workload_namespace", - "thresholds": [], - "type": "hidden", - "unit": "short" - }, - { - "alias": "Service", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "link": true, - "linkTooltip": "$__cell dashboard", - "linkUrl": "/dashboard/db/istio-service-dashboard?var-service=${__cell_1:raw}", - "pattern": "destination_service", - "thresholds": [], - "type": "number", - "unit": "short" - } - ], - "targets": [ - { - "expr": "label_join(sum(rate(istio_tcp_received_bytes_total{reporter=\"source\"}[1m])) by (destination_workload, destination_workload_namespace, destination_service), \"destination_workload_var\", \".\", \"destination_workload\", \"destination_workload_namespace\")", - "format": "table", - "hide": false, - "instant": true, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}", - "refId": "A" - }, - { - "expr": "label_join(sum(rate(istio_tcp_sent_bytes_total{reporter=\"source\"}[1m])) by (destination_workload, destination_workload_namespace, destination_service), \"destination_workload_var\", \".\", \"destination_workload\", \"destination_workload_namespace\")", - "format": "table", - "hide": false, - "instant": true, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}", - "refId": "B" - } - ], - "timeFrom": null, - "title": "TCP Workloads", - "transform": "table", - "type": "table" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 9, - "w": 24, - "x": 0, - "y": 48 - }, - "id": 111, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(istio_build) by (component, tag)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ component }}: {{ tag }}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Istio Components by Version", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "refresh": "5s", - "schemaVersion": 18, - "style": "dark", - "tags": [], - "templating": { - "list": [ - { - "current": { - "selected": true, - "text": "default", - "value": "default" - }, - "hide": 0, - "includeAll": false, - "label": null, - "multi": false, - "name": "datasource", - "options": [], - "query": "prometheus", - "queryValue": "", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "type": "datasource" - } - ] - }, - "time": { - "from": "now-5m", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ] - }, - "timezone": "browser", - "title": "Istio Mesh Dashboard", - "uid": "G8wLrJIZk", - "version": 5 -} diff --git a/manifests/addons/dashboards/istio-performance-dashboard.json b/manifests/addons/dashboards/istio-performance-dashboard.json deleted file mode 100644 index c01f59a27..000000000 --- a/manifests/addons/dashboards/istio-performance-dashboard.json +++ /dev/null @@ -1,1314 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": false, - "gnetId": null, - "graphTooltip": 0, - "links": [], - "panels": [ - { - "collapsed": true, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 21, - "panels": [ - { - "content": "The charts on this dashboard are intended to show Istio main components cost in terms of resources utilization under steady load.\n\n- **vCPU / 1k rps:** shows vCPU utilization by the main Istio components normalized by 1000 requests/second. When idle or low traffic, this chart will be blank. The curve for istio-proxy refers to the services sidecars only.\n- **vCPU:** vCPU utilization by Istio components, not normalized.\n- **Memory:** memory footprint for the components. Telemetry and policy are normalized by 1k rps, and no data is shown when there is no traffic. For ingress and istio-proxy, the data is per instance.\n- **Bytes transferred / sec:** shows the number of bytes flowing through each Istio component.\n\n\n", - "gridPos": { - "h": 6, - "w": 24, - "x": 0, - "y": 1 - }, - "id": 19, - "links": [], - "mode": "markdown", - "timeFrom": null, - "timeShift": null, - "title": "Performance Dashboard README", - "transparent": true, - "type": "text" - } - ], - "title": "Performance Dashboard Notes", - "type": "row" - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 1 - }, - "id": 6, - "panels": [], - "title": "vCPU Usage", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 2 - }, - "id": 4, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "(sum(irate(container_cpu_usage_seconds_total{pod=~\"istio-ingressgateway-.*\",container=\"istio-proxy\"}[1m])) / (round(sum(irate(istio_requests_total{source_workload=\"istio-ingressgateway\", reporter=\"source\"}[1m])), 0.001)/1000))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "istio-ingressgateway", - "refId": "A" - }, - { - "expr": "(sum(irate(container_cpu_usage_seconds_total{namespace!=\"dubbo-system\",container=\"istio-proxy\"}[1m]))/ (round(sum(irate(istio_requests_total[1m])), 0.001)/1000))/ (sum(irate(istio_requests_total{source_workload=\"istio-ingressgateway\"}[1m])) >bool 10)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "istio-proxy", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "vCPU / 1k rps", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 2 - }, - "id": 7, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(container_cpu_usage_seconds_total{pod=~\"istio-ingressgateway-.*\",container=\"istio-proxy\"}[1m]))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "istio-ingressgateway", - "refId": "A" - }, - { - "expr": "sum(rate(container_cpu_usage_seconds_total{namespace!=\"dubbo-system\",container=\"istio-proxy\"}[1m]))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "istio-proxy", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "vCPU", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 10 - }, - "id": 13, - "panels": [], - "title": "Memory and Data Rates", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 11 - }, - "id": 902, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(container_memory_working_set_bytes{pod=~\"istio-ingressgateway-.*\"}) / count(container_memory_working_set_bytes{pod=~\"istio-ingressgateway-.*\",container!=\"POD\"})", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "per istio-ingressgateway", - "refId": "A" - }, - { - "expr": "sum(container_memory_working_set_bytes{namespace!=\"dubbo-system\",container=\"istio-proxy\"}) / count(container_memory_working_set_bytes{namespace!=\"dubbo-system\",container=\"istio-proxy\"})", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "per istio proxy", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Memory Usage", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "bytes", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 11 - }, - "id": 11, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(irate(istio_response_bytes_sum{source_workload=\"istio-ingressgateway\", reporter=\"source\"}[1m]))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "istio-ingressgateway", - "refId": "A" - }, - { - "expr": "sum(irate(istio_response_bytes_sum{source_workload_namespace!=\"dubbo-system\", reporter=\"source\"}[1m])) + sum(irate(istio_request_bytes_sum{source_workload_namespace!=\"dubbo-system\", reporter=\"source\"}[1m]))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "istio-proxy", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Bytes transferred / sec", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "Bps", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 19 - }, - "id": 17, - "panels": [], - "title": "Istio Component Versions", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 20 - }, - "id": 15, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(istio_build) by (component, tag)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ component }}: {{ tag }}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Istio Components by Version", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 31 - }, - "id": 71, - "panels": [], - "title": "Proxy Resource Usage", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 7, - "w": 6, - "x": 0, - "y": 32 - }, - "id": 72, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(container_memory_working_set_bytes{container=\"istio-proxy\"})", - "format": "time_series", - "hide": false, - "intervalFactor": 2, - "legendFormat": "Total (k8s)", - "refId": "A", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Memory", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "bytes", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 7, - "w": 6, - "x": 6, - "y": 32 - }, - "id": 73, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(container_cpu_usage_seconds_total{container=\"istio-proxy\"}[1m]))", - "format": "time_series", - "hide": false, - "intervalFactor": 2, - "legendFormat": "Total (k8s)", - "refId": "A", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "vCPU", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 7, - "w": 6, - "x": 12, - "y": 32 - }, - "id": 702, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(container_fs_usage_bytes{container=\"istio-proxy\"})", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Total (k8s)", - "refId": "A", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Disk", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "bytes", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "decimals": null, - "format": "none", - "label": "", - "logBase": 1024, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 39 - }, - "id": 69, - "panels": [], - "title": "Istiod Resource Usage", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 7, - "w": 6, - "x": 0, - "y": 40 - }, - "id": 5, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "process_virtual_memory_bytes{app=\"istiod\"}", - "format": "time_series", - "instant": false, - "intervalFactor": 2, - "legendFormat": "Virtual Memory", - "refId": "I", - "step": 2 - }, - { - "expr": "process_resident_memory_bytes{app=\"istiod\"}", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Resident Memory", - "refId": "H", - "step": 2 - }, - { - "expr": "go_memstats_heap_sys_bytes{app=\"istiod\"}", - "format": "time_series", - "hide": true, - "intervalFactor": 2, - "legendFormat": "heap sys", - "refId": "A" - }, - { - "expr": "go_memstats_heap_alloc_bytes{app=\"istiod\"}", - "format": "time_series", - "hide": true, - "intervalFactor": 2, - "legendFormat": "heap alloc", - "refId": "D" - }, - { - "expr": "go_memstats_alloc_bytes{app=\"istiod\"}", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Alloc", - "refId": "F", - "step": 2 - }, - { - "expr": "go_memstats_heap_inuse_bytes{app=\"istiod\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 2, - "legendFormat": "Heap in-use", - "refId": "E", - "step": 2 - }, - { - "expr": "go_memstats_stack_inuse_bytes{app=\"istiod\"}", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Stack in-use", - "refId": "G", - "step": 2 - }, - { - "expr": "sum(container_memory_working_set_bytes{container=~\"discovery|istio-proxy\", pod=~\"istiod-.*\"})", - "format": "time_series", - "hide": false, - "intervalFactor": 2, - "legendFormat": "Total (k8s)", - "refId": "C", - "step": 2 - }, - { - "expr": "container_memory_working_set_bytes{container=~\"discovery|istio-proxy\", pod=~\"istiod-.*\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 2, - "legendFormat": "{{ container }} (k8s)", - "refId": "B", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Memory", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "bytes", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 7, - "w": 6, - "x": 6, - "y": 40 - }, - "id": 602, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(container_cpu_usage_seconds_total{container=~\"discovery|istio-proxy\", pod=~\"istiod-.*\"}[1m]))", - "format": "time_series", - "hide": false, - "intervalFactor": 2, - "legendFormat": "Total (k8s)", - "refId": "A", - "step": 2 - }, - { - "expr": "sum(rate(container_cpu_usage_seconds_total{container=~\"discovery|istio-proxy\", pod=~\"istiod-.*\"}[1m])) by (container)", - "format": "time_series", - "hide": false, - "intervalFactor": 2, - "legendFormat": "{{ container }} (k8s)", - "refId": "B", - "step": 2 - }, - { - "expr": "irate(process_cpu_seconds_total{app=\"istiod\"}[1m])", - "format": "time_series", - "hide": false, - "intervalFactor": 2, - "legendFormat": "pilot (self-reported)", - "refId": "C", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "vCPU", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 7, - "w": 6, - "x": 12, - "y": 40 - }, - "id": 74, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "process_open_fds{app=\"istiod\"}", - "format": "time_series", - "hide": true, - "instant": false, - "interval": "", - "intervalFactor": 2, - "legendFormat": "Open FDs (pilot)", - "refId": "A" - }, - { - "expr": "container_fs_usage_bytes{ container=~\"discovery|istio-proxy\", pod=~\"istiod-.*\"}", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "{{ container }}", - "refId": "B", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Disk", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "bytes", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "decimals": null, - "format": "none", - "label": "", - "logBase": 1024, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 7, - "w": 6, - "x": 18, - "y": 40 - }, - "id": 402, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": false, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "go_goroutines{app=\"istiod\"}", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Number of Goroutines", - "refId": "A", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Goroutines", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "refresh": "10s", - "schemaVersion": 18, - "style": "dark", - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-5m", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ] - }, - "timezone": "", - "title": "Istio Performance Dashboard", - "uid": "vu8e0VWZk", - "version": 22 -} diff --git a/manifests/addons/dashboards/istio-service-dashboard.json b/manifests/addons/dashboards/istio-service-dashboard.json deleted file mode 100644 index 833c0d36d..000000000 --- a/manifests/addons/dashboards/istio-service-dashboard.json +++ /dev/null @@ -1,2990 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": false, - "gnetId": null, - "graphTooltip": 0, - "iteration": 1595591291797, - "links": [], - "panels": [ - { - "collapsed": true, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 106, - "panels": [ - { - "content": "
\nSERVICE: $service\n
", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 24, - "x": 0, - "y": 1 - }, - "id": 89, - "links": [], - "mode": "html", - "options": { - "content": "
\nSERVICE: $service\n
", - "mode": "html" - }, - "pluginVersion": "7.1.0", - "title": "", - "transparent": true, - "type": "text" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "format": "ops", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 0, - "y": 4 - }, - "id": 12, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": true, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "round(sum(irate(istio_requests_total{reporter=~\"$qrep\",destination_service=~\"$service\"}[5m])), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "refId": "A", - "step": 4 - } - ], - "thresholds": "", - "title": "Client Request Volume", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "rgba(50, 172, 45, 0.97)", - "rgba(237, 129, 40, 0.89)", - "rgba(245, 54, 54, 0.9)" - ], - "datasource": "Prometheus", - "decimals": null, - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "format": "percentunit", - "gauge": { - "maxValue": 100, - "minValue": 80, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": false - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 6, - "y": 4 - }, - "id": 14, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": true, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "sum(irate(istio_requests_total{reporter=~\"$qrep\",destination_service=~\"$service\",response_code!~\"5.*\"}[5m])) / sum(irate(istio_requests_total{reporter=~\"$qrep\",destination_service=~\"$service\"}[5m]))", - "format": "time_series", - "intervalFactor": 1, - "refId": "A" - } - ], - "thresholds": "95, 99, 99.5", - "title": "Client Success Rate (non-5xx responses)", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "avg" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 4, - "w": 6, - "x": 12, - "y": 4 - }, - "hiddenSeries": false, - "id": 87, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "rightSide": true, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "(histogram_quantile(0.50, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\",destination_service=~\"$service\"}[1m])) by (le)) / 1000) or histogram_quantile(0.50, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\",destination_service=~\"$service\"}[1m])) by (le))", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "P50", - "refId": "A" - }, - { - "expr": "(histogram_quantile(0.90, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\",destination_service=~\"$service\"}[1m])) by (le)) / 1000) or histogram_quantile(0.90, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\",destination_service=~\"$service\"}[1m])) by (le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "P90", - "refId": "B" - }, - { - "expr": "(histogram_quantile(0.99, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\",destination_service=~\"$service\"}[1m])) by (le)) / 1000) or histogram_quantile(0.99, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\",destination_service=~\"$service\"}[1m])) by (le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "P99", - "refId": "C" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Client Request Duration", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "format": "Bps", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 18, - "y": 4 - }, - "id": 84, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": true, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "sum(irate(istio_tcp_received_bytes_total{reporter=~\"$qrep\", destination_service=~\"$service\"}[1m]))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "", - "refId": "A" - } - ], - "thresholds": "", - "title": "TCP Received Bytes", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "avg" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "format": "ops", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 0, - "y": 8 - }, - "id": 97, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": true, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "round(sum(irate(istio_requests_total{reporter=\"destination\",destination_service=~\"$service\"}[5m])), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "refId": "A", - "step": 4 - } - ], - "thresholds": "", - "title": "Server Request Volume", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "rgba(50, 172, 45, 0.97)", - "rgba(237, 129, 40, 0.89)", - "rgba(245, 54, 54, 0.9)" - ], - "datasource": "Prometheus", - "decimals": null, - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "format": "percentunit", - "gauge": { - "maxValue": 100, - "minValue": 80, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": false - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 6, - "y": 8 - }, - "id": 98, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": true, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "sum(irate(istio_requests_total{reporter=\"destination\",destination_service=~\"$service\",response_code!~\"5.*\"}[5m])) / sum(irate(istio_requests_total{reporter=\"destination\",destination_service=~\"$service\"}[5m]))", - "format": "time_series", - "intervalFactor": 1, - "refId": "A" - } - ], - "thresholds": "95, 99, 99.5", - "title": "Server Success Rate (non-5xx responses)", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "avg" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 4, - "w": 6, - "x": 12, - "y": 8 - }, - "hiddenSeries": false, - "id": 99, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "rightSide": true, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "(histogram_quantile(0.50, sum(irate(istio_request_duration_milliseconds_bucket{reporter=\"destination\",destination_service=~\"$service\"}[1m])) by (le)) / 1000) or histogram_quantile(0.50, sum(irate(istio_request_duration_seconds_bucket{reporter=\"destination\",destination_service=~\"$service\"}[1m])) by (le))", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "P50", - "refId": "A" - }, - { - "expr": "(histogram_quantile(0.90, sum(irate(istio_request_duration_milliseconds_bucket{reporter=\"destination\",destination_service=~\"$service\"}[1m])) by (le)) / 1000) or histogram_quantile(0.90, sum(irate(istio_request_duration_seconds_bucket{reporter=\"destination\",destination_service=~\"$service\"}[1m])) by (le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "P90", - "refId": "B" - }, - { - "expr": "(histogram_quantile(0.99, sum(irate(istio_request_duration_milliseconds_bucket{reporter=\"destination\",destination_service=~\"$service\"}[1m])) by (le)) / 1000) or histogram_quantile(0.99, sum(irate(istio_request_duration_seconds_bucket{reporter=\"destination\",destination_service=~\"$service\"}[1m])) by (le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "P99", - "refId": "C" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Server Request Duration", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "format": "Bps", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 18, - "y": 8 - }, - "id": 100, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": true, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "sum(irate(istio_tcp_sent_bytes_total{reporter=~\"$qrep\", destination_service=~\"$service\"}[1m]))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "", - "refId": "A" - } - ], - "thresholds": "", - "title": "TCP Sent Bytes", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "avg" - } - ], - "title": "General", - "type": "row" - }, - { - "collapsed": true, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 1 - }, - "id": 104, - "panels": [ - { - "content": "
\nCLIENT WORKLOADS\n
", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 24, - "x": 0, - "y": 2 - }, - "id": 45, - "links": [], - "mode": "html", - "options": { - "content": "
\nCLIENT WORKLOADS\n
", - "mode": "html" - }, - "pluginVersion": "7.1.0", - "title": "", - "transparent": true, - "type": "text" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 0, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 5 - }, - "hiddenSeries": false, - "id": 25, - "legend": { - "avg": false, - "current": false, - "hideEmpty": true, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null as zero", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "round(sum(irate(istio_requests_total{connection_security_policy=\"mutual_tls\",destination_service=~\"$service\",reporter=~\"$qrep\",source_workload=~\"$srcwl\",source_workload_namespace=~\"$srcns\"}[5m])) by (source_workload, source_workload_namespace, response_code), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ source_workload }}.{{ source_workload_namespace }} : {{ response_code }} (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "round(sum(irate(istio_requests_total{connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", reporter=~\"$qrep\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[5m])) by (source_workload, source_workload_namespace, response_code), 0.001)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ source_workload }}.{{ source_workload_namespace }} : {{ response_code }}", - "refId": "B", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Incoming Requests By Source And Response Code", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [ - "total" - ] - }, - "yaxes": [ - { - "format": "ops", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 5 - }, - "hiddenSeries": false, - "id": 26, - "legend": { - "avg": false, - "current": false, - "hideEmpty": true, - "hideZero": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(irate(istio_requests_total{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\",response_code!~\"5.*\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[5m])) by (source_workload, source_workload_namespace) / sum(irate(istio_requests_total{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[5m])) by (source_workload, source_workload_namespace)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ source_workload }}.{{ source_workload_namespace }} (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "sum(irate(istio_requests_total{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\",response_code!~\"5.*\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[5m])) by (source_workload, source_workload_namespace) / sum(irate(istio_requests_total{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[5m])) by (source_workload, source_workload_namespace)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ source_workload }}.{{ source_workload_namespace }}", - "refId": "B", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Incoming Success Rate (non-5xx responses) By Source", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "percentunit", - "label": null, - "logBase": 1, - "max": "1.01", - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "description": "", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 8, - "x": 0, - "y": 11 - }, - "hiddenSeries": false, - "id": 27, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": true, - "hideZero": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "(histogram_quantile(0.50, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.50, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P50 (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.90, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.90, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P90 (ðŸ”mTLS)", - "refId": "B", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.95, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.95, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P95 (ðŸ”mTLS)", - "refId": "C", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.99, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.99, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P99 (ðŸ”mTLS)", - "refId": "D", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.50, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.50, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P50", - "refId": "E", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.90, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.90, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P90", - "refId": "F", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.95, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.95, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P95", - "refId": "G", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.99, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.99, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P99", - "refId": "H", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Incoming Request Duration By Source", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 8, - "x": 8, - "y": 11 - }, - "hiddenSeries": false, - "id": 28, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": true, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.50, sum(irate(istio_request_bytes_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P50 (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "histogram_quantile(0.90, sum(irate(istio_request_bytes_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P90 (ðŸ”mTLS)", - "refId": "B", - "step": 2 - }, - { - "expr": "histogram_quantile(0.95, sum(irate(istio_request_bytes_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P95 (ðŸ”mTLS)", - "refId": "C", - "step": 2 - }, - { - "expr": "histogram_quantile(0.99, sum(irate(istio_request_bytes_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P99 (ðŸ”mTLS)", - "refId": "D", - "step": 2 - }, - { - "expr": "histogram_quantile(0.50, sum(irate(istio_request_bytes_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P50", - "refId": "E", - "step": 2 - }, - { - "expr": "histogram_quantile(0.90, sum(irate(istio_request_bytes_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P90", - "refId": "F", - "step": 2 - }, - { - "expr": "histogram_quantile(0.95, sum(irate(istio_request_bytes_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P95", - "refId": "G", - "step": 2 - }, - { - "expr": "histogram_quantile(0.99, sum(irate(istio_request_bytes_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P99", - "refId": "H", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Incoming Request Size By Source", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "decbytes", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 8, - "x": 16, - "y": 11 - }, - "hiddenSeries": false, - "id": 68, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": true, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.50, sum(irate(istio_response_bytes_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P50 (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "histogram_quantile(0.90, sum(irate(istio_response_bytes_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P90 (ðŸ”mTLS)", - "refId": "B", - "step": 2 - }, - { - "expr": "histogram_quantile(0.95, sum(irate(istio_response_bytes_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P95 (ðŸ”mTLS)", - "refId": "C", - "step": 2 - }, - { - "expr": "histogram_quantile(0.99, sum(irate(istio_response_bytes_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P99 (ðŸ”mTLS)", - "refId": "D", - "step": 2 - }, - { - "expr": "histogram_quantile(0.50, sum(irate(istio_response_bytes_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P50", - "refId": "E", - "step": 2 - }, - { - "expr": "histogram_quantile(0.90, sum(irate(istio_response_bytes_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P90", - "refId": "F", - "step": 2 - }, - { - "expr": "histogram_quantile(0.95, sum(irate(istio_response_bytes_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P95", - "refId": "G", - "step": 2 - }, - { - "expr": "histogram_quantile(0.99, sum(irate(istio_response_bytes_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P99", - "refId": "H", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Response Size By Source", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "decbytes", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 17 - }, - "hiddenSeries": false, - "id": 80, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "round(sum(irate(istio_tcp_received_bytes_total{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace), 0.001)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ source_workload }}.{{ source_workload_namespace}} (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "round(sum(irate(istio_tcp_received_bytes_total{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ source_workload }}.{{ source_workload_namespace}}", - "refId": "B", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Bytes Received from Incoming TCP Connection", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "Bps", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 17 - }, - "hiddenSeries": false, - "id": 82, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "round(sum(irate(istio_tcp_sent_bytes_total{connection_security_policy=\"mutual_tls\", reporter=~\"$qrep\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ source_workload }}.{{ source_workload_namespace}} (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "round(sum(irate(istio_tcp_sent_bytes_total{connection_security_policy!=\"mutual_tls\", reporter=~\"$qrep\", destination_service=~\"$service\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ source_workload }}.{{ source_workload_namespace}}", - "refId": "B", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Bytes Sent to Incoming TCP Connection", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "Bps", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "title": "Client Workloads", - "type": "row" - }, - { - "collapsed": true, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 2 - }, - "id": 102, - "panels": [ - { - "content": "
\nSERVICE WORKLOADS\n
", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 24, - "x": 0, - "y": 3 - }, - "id": 69, - "links": [], - "mode": "html", - "options": { - "content": "
\nSERVICE WORKLOADS\n
", - "mode": "html" - }, - "pluginVersion": "7.1.0", - "title": "", - "transparent": true, - "type": "text" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 0, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 6 - }, - "hiddenSeries": false, - "id": 90, - "legend": { - "avg": false, - "current": false, - "hideEmpty": true, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null as zero", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "round(sum(irate(istio_requests_total{connection_security_policy=\"mutual_tls\",destination_service=~\"$service\",reporter=\"destination\",destination_workload=~\"$dstwl\",destination_workload_namespace=~\"$dstns\"}[5m])) by (destination_workload, destination_workload_namespace, response_code), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} : {{ response_code }} (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "round(sum(irate(istio_requests_total{connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", reporter=\"destination\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[5m])) by (destination_workload, destination_workload_namespace, response_code), 0.001)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} : {{ response_code }}", - "refId": "B", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Incoming Requests By Destination Workload And Response Code", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [ - "total" - ] - }, - "yaxes": [ - { - "format": "ops", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 6 - }, - "hiddenSeries": false, - "id": 91, - "legend": { - "avg": false, - "current": false, - "hideEmpty": true, - "hideZero": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(irate(istio_requests_total{reporter=\"destination\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\",response_code!~\"5.*\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[5m])) by (destination_workload, destination_workload_namespace) / sum(irate(istio_requests_total{reporter=\"destination\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[5m])) by (destination_workload, destination_workload_namespace)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "sum(irate(istio_requests_total{reporter=\"destination\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\",response_code!~\"5.*\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[5m])) by (destination_workload, destination_workload_namespace) / sum(irate(istio_requests_total{reporter=\"destination\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[5m])) by (destination_workload, destination_workload_namespace)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }}", - "refId": "B", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Incoming Success Rate (non-5xx responses) By Destination Workload", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "percentunit", - "label": null, - "logBase": 1, - "max": "1.01", - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "description": "", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 8, - "x": 0, - "y": 12 - }, - "hiddenSeries": false, - "id": 94, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": true, - "hideZero": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "(histogram_quantile(0.50, sum(irate(istio_request_duration_milliseconds_bucket{reporter=\"destination\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le)) / 1000) or histogram_quantile(0.50, sum(irate(istio_request_duration_seconds_bucket{reporter=\"destination\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P50 (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.90, sum(irate(istio_request_duration_milliseconds_bucket{reporter=\"destination\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le)) / 1000) or histogram_quantile(0.90, sum(irate(istio_request_duration_seconds_bucket{reporter=\"destination\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P90 (ðŸ”mTLS)", - "refId": "B", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.95, sum(irate(istio_request_duration_milliseconds_bucket{reporter=\"destination\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le)) / 1000) or histogram_quantile(0.95, sum(irate(istio_request_duration_seconds_bucket{reporter=\"destination\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P95 (ðŸ”mTLS)", - "refId": "C", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.99, sum(irate(istio_request_duration_milliseconds_bucket{reporter=\"destination\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le)) / 1000) or histogram_quantile(0.99, sum(irate(istio_request_duration_seconds_bucket{reporter=\"destination\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P99 (ðŸ”mTLS)", - "refId": "D", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.50, sum(irate(istio_request_duration_milliseconds_bucket{reporter=\"destination\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le)) / 1000) or histogram_quantile(0.50, sum(irate(istio_request_duration_seconds_bucket{reporter=\"destination\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P50", - "refId": "E", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.90, sum(irate(istio_request_duration_milliseconds_bucket{reporter=\"destination\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le)) / 1000) or histogram_quantile(0.90, sum(irate(istio_request_duration_seconds_bucket{reporter=\"destination\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P90", - "refId": "F", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.95, sum(irate(istio_request_duration_milliseconds_bucket{reporter=\"destination\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le)) / 1000) or histogram_quantile(0.95, sum(irate(istio_request_duration_seconds_bucket{reporter=\"destination\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P95", - "refId": "G", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.99, sum(irate(istio_request_duration_milliseconds_bucket{reporter=\"destination\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le)) / 1000) or histogram_quantile(0.99, sum(irate(istio_request_duration_seconds_bucket{reporter=\"destination\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P99", - "refId": "H", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Incoming Request Duration By Service Workload", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 8, - "x": 8, - "y": 12 - }, - "hiddenSeries": false, - "id": 95, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": true, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.50, sum(irate(istio_request_bytes_bucket{reporter=\"destination\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P50 (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "histogram_quantile(0.90, sum(irate(istio_request_bytes_bucket{reporter=\"destination\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P90 (ðŸ”mTLS)", - "refId": "B", - "step": 2 - }, - { - "expr": "histogram_quantile(0.95, sum(irate(istio_request_bytes_bucket{reporter=\"destination\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P95 (ðŸ”mTLS)", - "refId": "C", - "step": 2 - }, - { - "expr": "histogram_quantile(0.99, sum(irate(istio_request_bytes_bucket{reporter=\"destination\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P99 (ðŸ”mTLS)", - "refId": "D", - "step": 2 - }, - { - "expr": "histogram_quantile(0.50, sum(irate(istio_request_bytes_bucket{reporter=\"destination\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P50", - "refId": "E", - "step": 2 - }, - { - "expr": "histogram_quantile(0.90, sum(irate(istio_request_bytes_bucket{reporter=\"destination\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P90", - "refId": "F", - "step": 2 - }, - { - "expr": "histogram_quantile(0.95, sum(irate(istio_request_bytes_bucket{reporter=\"destination\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P95", - "refId": "G", - "step": 2 - }, - { - "expr": "histogram_quantile(0.99, sum(irate(istio_request_bytes_bucket{reporter=\"destination\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P99", - "refId": "H", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Incoming Request Size By Service Workload", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "decbytes", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 8, - "x": 16, - "y": 12 - }, - "hiddenSeries": false, - "id": 96, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": true, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.50, sum(irate(istio_response_bytes_bucket{reporter=\"destination\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P50 (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "histogram_quantile(0.90, sum(irate(istio_response_bytes_bucket{reporter=\"destination\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P90 (ðŸ”mTLS)", - "refId": "B", - "step": 2 - }, - { - "expr": "histogram_quantile(0.95, sum(irate(istio_response_bytes_bucket{reporter=\"destination\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P95 (ðŸ”mTLS)", - "refId": "C", - "step": 2 - }, - { - "expr": "histogram_quantile(0.99, sum(irate(istio_response_bytes_bucket{reporter=\"destination\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P99 (ðŸ”mTLS)", - "refId": "D", - "step": 2 - }, - { - "expr": "histogram_quantile(0.50, sum(irate(istio_response_bytes_bucket{reporter=\"destination\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P50", - "refId": "E", - "step": 2 - }, - { - "expr": "histogram_quantile(0.90, sum(irate(istio_response_bytes_bucket{reporter=\"destination\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P90", - "refId": "F", - "step": 2 - }, - { - "expr": "histogram_quantile(0.95, sum(irate(istio_response_bytes_bucket{reporter=\"destination\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P95", - "refId": "G", - "step": 2 - }, - { - "expr": "histogram_quantile(0.99, sum(irate(istio_response_bytes_bucket{reporter=\"destination\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace }} P99", - "refId": "H", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Response Size By Service Workload", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "decbytes", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 18 - }, - "hiddenSeries": false, - "id": 92, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "round(sum(irate(istio_tcp_received_bytes_total{reporter=\"destination\", connection_security_policy=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace), 0.001)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace}} (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "round(sum(irate(istio_tcp_received_bytes_total{reporter=\"destination\", connection_security_policy!=\"mutual_tls\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{ destination_workload_namespace}}", - "refId": "B", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Bytes Received from Incoming TCP Connection", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "Bps", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 18 - }, - "hiddenSeries": false, - "id": 93, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "round(sum(irate(istio_tcp_sent_bytes_total{connection_security_policy=\"mutual_tls\", reporter=\"destination\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{destination_workload_namespace }} (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "round(sum(irate(istio_tcp_sent_bytes_total{connection_security_policy!=\"mutual_tls\", reporter=\"destination\", destination_service=~\"$service\", destination_workload=~\"$dstwl\", destination_workload_namespace=~\"$dstns\"}[1m])) by (destination_workload, destination_workload_namespace), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ destination_workload }}.{{destination_workload_namespace }}", - "refId": "B", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Bytes Sent to Incoming TCP Connection", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "Bps", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "title": "Service Workloads", - "type": "row" - } - ], - "refresh": "1m", - "schemaVersion": 26, - "style": "dark", - "tags": [], - "templating": { - "list": [ - { - "current": { - "selected": true, - "text": "default", - "value": "default" - }, - "hide": 0, - "includeAll": false, - "label": null, - "multi": false, - "name": "datasource", - "options": [], - "query": "prometheus", - "queryValue": "", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "type": "datasource" - }, - { - "allValue": null, - "current": {}, - "datasource": "Prometheus", - "definition": "", - "hide": 0, - "includeAll": false, - "label": "Service", - "multi": false, - "name": "service", - "options": [], - "query": "label_values(destination_service)", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": { - "selected": true, - "text": "destination", - "value": "destination" - }, - "datasource": "Prometheus", - "definition": "", - "hide": 0, - "includeAll": false, - "label": "Reporter", - "multi": true, - "name": "qrep", - "options": [], - "query": "label_values(reporter)", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 2, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": {}, - "datasource": "Prometheus", - "definition": "", - "hide": 0, - "includeAll": true, - "label": "Client Workload Namespace", - "multi": true, - "name": "srcns", - "options": [], - "query": "query_result(sum(istio_requests_total{reporter=~\"$qrep\", destination_service=\"$service\"}) by (source_workload_namespace) or sum(istio_tcp_sent_bytes_total{reporter=~\"$qrep\", destination_service=~\"$service\"}) by (source_workload_namespace))", - "refresh": 1, - "regex": "/.*namespace=\"([^\"]*).*/", - "skipUrlSync": false, - "sort": 2, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": {}, - "datasource": "Prometheus", - "definition": "", - "hide": 0, - "includeAll": true, - "label": "Client Workload", - "multi": true, - "name": "srcwl", - "options": [], - "query": "query_result(sum(istio_requests_total{reporter=~\"$qrep\", destination_service=~\"$service\", source_workload_namespace=~\"$srcns\"}) by (source_workload) or sum(istio_tcp_sent_bytes_total{reporter=~\"$qrep\", destination_service=~\"$service\", source_workload_namespace=~\"$srcns\"}) by (source_workload))", - "refresh": 1, - "regex": "/.*workload=\"([^\"]*).*/", - "skipUrlSync": false, - "sort": 3, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": {}, - "datasource": "Prometheus", - "definition": "", - "hide": 0, - "includeAll": true, - "label": "Service Workload Namespace", - "multi": true, - "name": "dstns", - "options": [], - "query": "query_result(sum(istio_requests_total{reporter=\"destination\", destination_service=\"$service\"}) by (destination_workload_namespace) or sum(istio_tcp_sent_bytes_total{reporter=\"destination\", destination_service=~\"$service\"}) by (destination_workload_namespace))", - "refresh": 1, - "regex": "/.*namespace=\"([^\"]*).*/", - "skipUrlSync": false, - "sort": 2, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": {}, - "datasource": "Prometheus", - "definition": "", - "hide": 0, - "includeAll": true, - "label": "Service Workload", - "multi": true, - "name": "dstwl", - "options": [], - "query": "query_result( sum(istio_requests_total{reporter=\"destination\", destination_service=~\"$service\", destination_workload_namespace=~\"$dstns\"}) by (destination_workload) or sum(istio_tcp_sent_bytes_total{reporter=\"destination\", destination_service=~\"$service\", destination_workload_namespace=~\"$dstns\"}) by (destination_workload))", - "refresh": 1, - "regex": "/.*workload=\"([^\"]*).*/", - "skipUrlSync": false, - "sort": 3, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - } - ] - }, - "time": { - "from": "now-5m", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ] - }, - "timezone": "", - "title": "Istio Service Dashboard", - "uid": "LJ_uJAvmk", - "version": 1 -} diff --git a/manifests/addons/dashboards/istio-workload-dashboard.json b/manifests/addons/dashboards/istio-workload-dashboard.json deleted file mode 100644 index 8224a397a..000000000 --- a/manifests/addons/dashboards/istio-workload-dashboard.json +++ /dev/null @@ -1,2672 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": false, - "gnetId": null, - "graphTooltip": 0, - "iteration": 1531345461465, - "links": [], - "panels": [ - { - "collapsed": true, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 95, - "panels": [ - { - "content": "
\nWORKLOAD: $workload.$namespace\n
", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 24, - "x": 0, - "y": 1 - }, - "id": 89, - "links": [], - "mode": "html", - "options": { - "content": "
\nWORKLOAD: $workload.$namespace\n
", - "mode": "html" - }, - "pluginVersion": "7.1.0", - "title": "", - "transparent": true, - "type": "text" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "format": "ops", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 8, - "x": 0, - "y": 4 - }, - "id": 12, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": true, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "round(sum(irate(istio_requests_total{reporter=~\"$qrep\",destination_workload_namespace=~\"$namespace\",destination_workload=~\"$workload\"}[5m])), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "refId": "A", - "step": 4 - } - ], - "thresholds": "", - "title": "Incoming Request Volume", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "rgba(50, 172, 45, 0.97)", - "rgba(237, 129, 40, 0.89)", - "rgba(245, 54, 54, 0.9)" - ], - "datasource": "Prometheus", - "decimals": null, - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "format": "percentunit", - "gauge": { - "maxValue": 100, - "minValue": 80, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": false - }, - "gridPos": { - "h": 4, - "w": 8, - "x": 8, - "y": 4 - }, - "id": 14, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": true, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "sum(irate(istio_requests_total{reporter=~\"$qrep\",destination_workload_namespace=~\"$namespace\",destination_workload=~\"$workload\",response_code!~\"5.*\"}[5m])) / sum(irate(istio_requests_total{reporter=~\"$qrep\",destination_workload_namespace=~\"$namespace\",destination_workload=~\"$workload\"}[5m]))", - "format": "time_series", - "intervalFactor": 1, - "refId": "A" - } - ], - "thresholds": "95, 99, 99.5", - "title": "Incoming Success Rate (non-5xx responses)", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "avg" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 4, - "w": 8, - "x": 16, - "y": 4 - }, - "hiddenSeries": false, - "id": 87, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "rightSide": true, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "(histogram_quantile(0.50, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\",destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\"}[1m])) by (le)) / 1000) or histogram_quantile(0.50, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\",destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\"}[1m])) by (le))", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "P50", - "refId": "A" - }, - { - "expr": "(histogram_quantile(0.90, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\",destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\"}[1m])) by (le)) / 1000) or histogram_quantile(0.90, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\",destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\"}[1m])) by (le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "P90", - "refId": "B" - }, - { - "expr": "(histogram_quantile(0.99, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\",destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\"}[1m])) by (le)) / 1000) or histogram_quantile(0.99, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\",destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\"}[1m])) by (le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "P99", - "refId": "C" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Request Duration", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "format": "Bps", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 12, - "x": 0, - "y": 8 - }, - "id": 84, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": true, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "sum(irate(istio_tcp_sent_bytes_total{reporter=~\"$qrep\", destination_workload_namespace=~\"$namespace\", destination_workload=~\"$workload\"}[1m])) + sum(irate(istio_tcp_received_bytes_total{reporter=~\"$qrep\", destination_workload_namespace=~\"$namespace\", destination_workload=~\"$workload\"}[1m]))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "", - "refId": "A" - } - ], - "thresholds": "", - "title": "TCP Server Traffic", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "avg" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "format": "Bps", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 12, - "x": 12, - "y": 8 - }, - "id": 85, - "interval": null, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": true, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "sum(irate(istio_tcp_sent_bytes_total{reporter=~\"$qrep\", source_workload_namespace=~\"$namespace\", source_workload=~\"$workload\"}[1m])) + sum(irate(istio_tcp_received_bytes_total{reporter=~\"$qrep\", source_workload_namespace=~\"$namespace\", source_workload=~\"$workload\"}[1m]))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "", - "refId": "A" - } - ], - "thresholds": "", - "title": "TCP Client Traffic", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "avg" - } - ], - "title": "General", - "type": "row" - }, - { - "collapsed": true, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 1 - }, - "id": 93, - "panels": [ - { - "content": "
\nINBOUND WORKLOADS\n
", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 24, - "x": 0, - "y": 13 - }, - "id": 45, - "links": [], - "mode": "html", - "options": { - "content": "
\nINBOUND WORKLOADS\n
", - "mode": "html" - }, - "pluginVersion": "7.1.0", - "title": "", - "transparent": true, - "type": "text" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 0, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 16 - }, - "hiddenSeries": false, - "id": 25, - "legend": { - "avg": false, - "current": false, - "hideEmpty": true, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null as zero", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "round(sum(irate(istio_requests_total{connection_security_policy=\"mutual_tls\", destination_workload_namespace=~\"$namespace\", destination_workload=~\"$workload\", reporter=~\"$qrep\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[5m])) by (source_workload, source_workload_namespace, response_code), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ source_workload }}.{{ source_workload_namespace }} : {{ response_code }} (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "round(sum(irate(istio_requests_total{connection_security_policy!=\"mutual_tls\", destination_workload_namespace=~\"$namespace\", destination_workload=~\"$workload\", reporter=~\"$qrep\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[5m])) by (source_workload, source_workload_namespace, response_code), 0.001)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ source_workload }}.{{ source_workload_namespace }} : {{ response_code }}", - "refId": "B", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Incoming Requests By Source And Response Code", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [ - "total" - ] - }, - "yaxes": [ - { - "format": "ops", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 16 - }, - "hiddenSeries": false, - "id": 26, - "legend": { - "avg": false, - "current": false, - "hideEmpty": true, - "hideZero": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(irate(istio_requests_total{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_workload_namespace=~\"$namespace\", destination_workload=~\"$workload\",response_code!~\"5.*\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[5m])) by (source_workload, source_workload_namespace) / sum(irate(istio_requests_total{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_workload_namespace=~\"$namespace\", destination_workload=~\"$workload\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[5m])) by (source_workload, source_workload_namespace)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ source_workload }}.{{ source_workload_namespace }} (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "sum(irate(istio_requests_total{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_workload_namespace=~\"$namespace\", destination_workload=~\"$workload\",response_code!~\"5.*\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[5m])) by (source_workload, source_workload_namespace) / sum(irate(istio_requests_total{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_workload_namespace=~\"$namespace\", destination_workload=~\"$workload\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[5m])) by (source_workload, source_workload_namespace)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ source_workload }}.{{ source_workload_namespace }}", - "refId": "B", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Incoming Success Rate (non-5xx responses) By Source", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "percentunit", - "label": null, - "logBase": 1, - "max": "1.01", - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "description": "", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 8, - "x": 0, - "y": 22 - }, - "hiddenSeries": false, - "id": 27, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": true, - "hideZero": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "(histogram_quantile(0.50, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.50, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P50 (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.90, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.90, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P90 (ðŸ”mTLS)", - "refId": "B", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.95, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.95, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P95 (ðŸ”mTLS)", - "refId": "C", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.99, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.99, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P99 (ðŸ”mTLS)", - "refId": "D", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.50, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.50, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P50", - "refId": "E", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.90, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.90, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P90", - "refId": "F", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.95, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.95, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P95", - "refId": "G", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.99, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.99, sum(irate(istio_request_duration_seconds_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P99", - "refId": "H", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Incoming Request Duration By Source", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 8, - "x": 8, - "y": 22 - }, - "hiddenSeries": false, - "id": 28, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": true, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.50, sum(irate(istio_request_bytes_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P50 (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "histogram_quantile(0.90, sum(irate(istio_request_bytes_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P90 (ðŸ”mTLS)", - "refId": "B", - "step": 2 - }, - { - "expr": "histogram_quantile(0.95, sum(irate(istio_request_bytes_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P95 (ðŸ”mTLS)", - "refId": "C", - "step": 2 - }, - { - "expr": "histogram_quantile(0.99, sum(irate(istio_request_bytes_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P99 (ðŸ”mTLS)", - "refId": "D", - "step": 2 - }, - { - "expr": "histogram_quantile(0.50, sum(irate(istio_request_bytes_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P50", - "refId": "E", - "step": 2 - }, - { - "expr": "histogram_quantile(0.90, sum(irate(istio_request_bytes_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P90", - "refId": "F", - "step": 2 - }, - { - "expr": "histogram_quantile(0.95, sum(irate(istio_request_bytes_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P95", - "refId": "G", - "step": 2 - }, - { - "expr": "histogram_quantile(0.99, sum(irate(istio_request_bytes_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P99", - "refId": "H", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Incoming Request Size By Source", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "decbytes", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 8, - "x": 16, - "y": 22 - }, - "hiddenSeries": false, - "id": 68, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": true, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.50, sum(irate(istio_response_bytes_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P50 (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "histogram_quantile(0.90, sum(irate(istio_response_bytes_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P90 (ðŸ”mTLS)", - "refId": "B", - "step": 2 - }, - { - "expr": "histogram_quantile(0.95, sum(irate(istio_response_bytes_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P95 (ðŸ”mTLS)", - "refId": "C", - "step": 2 - }, - { - "expr": "histogram_quantile(0.99, sum(irate(istio_response_bytes_bucket{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P99 (ðŸ”mTLS)", - "refId": "D", - "step": 2 - }, - { - "expr": "histogram_quantile(0.50, sum(irate(istio_response_bytes_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P50", - "refId": "E", - "step": 2 - }, - { - "expr": "histogram_quantile(0.90, sum(irate(istio_response_bytes_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P90", - "refId": "F", - "step": 2 - }, - { - "expr": "histogram_quantile(0.95, sum(irate(istio_response_bytes_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P95", - "refId": "G", - "step": 2 - }, - { - "expr": "histogram_quantile(0.99, sum(irate(istio_response_bytes_bucket{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_workload=~\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{source_workload}}.{{source_workload_namespace}} P99", - "refId": "H", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Response Size By Source", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "decbytes", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 28 - }, - "hiddenSeries": false, - "id": 80, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "round(sum(irate(istio_tcp_received_bytes_total{reporter=~\"$qrep\", connection_security_policy=\"mutual_tls\", destination_workload_namespace=~\"$namespace\", destination_workload=~\"$workload\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace), 0.001)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ source_workload }}.{{ source_workload_namespace}} (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "round(sum(irate(istio_tcp_received_bytes_total{reporter=~\"$qrep\", connection_security_policy!=\"mutual_tls\", destination_workload_namespace=~\"$namespace\", destination_workload=~\"$workload\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ source_workload }}.{{ source_workload_namespace}}", - "refId": "B", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Bytes Received from Incoming TCP Connection", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "Bps", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 28 - }, - "hiddenSeries": false, - "id": 82, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "round(sum(irate(istio_tcp_sent_bytes_total{connection_security_policy=\"mutual_tls\", reporter=~\"$qrep\", destination_workload_namespace=~\"$namespace\", destination_workload=~\"$workload\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ source_workload }}.{{ source_workload_namespace}} (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "round(sum(irate(istio_tcp_sent_bytes_total{connection_security_policy!=\"mutual_tls\", reporter=~\"$qrep\", destination_workload_namespace=~\"$namespace\", destination_workload=~\"$workload\", source_workload=~\"$srcwl\", source_workload_namespace=~\"$srcns\"}[1m])) by (source_workload, source_workload_namespace), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ source_workload }}.{{ source_workload_namespace}}", - "refId": "B", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Bytes Sent to Incoming TCP Connection", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "Bps", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "title": "Inbound Workloads", - "type": "row" - }, - { - "collapsed": true, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 2 - }, - "id": 91, - "panels": [ - { - "content": "
\nOUTBOUND SERVICES\n
", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 24, - "x": 0, - "y": 14 - }, - "id": 69, - "links": [], - "mode": "html", - "options": { - "content": "
\nOUTBOUND SERVICES\n
", - "mode": "html" - }, - "pluginVersion": "7.1.0", - "title": "", - "transparent": true, - "type": "text" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 0, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 17 - }, - "hiddenSeries": false, - "id": 70, - "legend": { - "avg": false, - "current": false, - "hideEmpty": true, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null as zero", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "round(sum(irate(istio_requests_total{destination_principal=~\"spiffe.*\", source_workload_namespace=~\"$namespace\", source_workload=~\"$workload\", reporter=\"source\", destination_service=~\"$dstsvc\"}[5m])) by (destination_service, response_code), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} : {{ response_code }} (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "round(sum(irate(istio_requests_total{destination_principal!~\"spiffe.*\", source_workload_namespace=~\"$namespace\", source_workload=~\"$workload\", reporter=\"source\", destination_service=~\"$dstsvc\"}[5m])) by (destination_service, response_code), 0.001)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} : {{ response_code }}", - "refId": "B", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Outgoing Requests By Destination And Response Code", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [ - "total" - ] - }, - "yaxes": [ - { - "format": "ops", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 17 - }, - "hiddenSeries": false, - "id": 71, - "legend": { - "avg": false, - "current": false, - "hideEmpty": true, - "hideZero": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(irate(istio_requests_total{reporter=\"source\", connection_security_policy=\"mutual_tls\", source_workload_namespace=~\"$namespace\", source_workload=~\"$workload\",response_code!~\"5.*\", destination_service=~\"$dstsvc\"}[5m])) by (destination_service) / sum(irate(istio_requests_total{reporter=\"source\", connection_security_policy=\"mutual_tls\", source_workload_namespace=~\"$namespace\", source_workload=~\"$workload\", destination_service=~\"$dstsvc\"}[5m])) by (destination_service)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "sum(irate(istio_requests_total{reporter=\"source\", connection_security_policy!=\"mutual_tls\", source_workload_namespace=~\"$namespace\", source_workload=~\"$workload\",response_code!~\"5.*\", destination_service=~\"$dstsvc\"}[5m])) by (destination_service) / sum(irate(istio_requests_total{reporter=\"source\", connection_security_policy!=\"mutual_tls\", source_workload_namespace=~\"$namespace\", source_workload=~\"$workload\", destination_service=~\"$dstsvc\"}[5m])) by (destination_service)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }}", - "refId": "B", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Outgoing Success Rate (non-5xx responses) By Destination", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "percentunit", - "label": null, - "logBase": 1, - "max": "1.01", - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "description": "", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 8, - "x": 0, - "y": 23 - }, - "hiddenSeries": false, - "id": 72, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": true, - "hideZero": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "(histogram_quantile(0.50, sum(irate(istio_request_duration_milliseconds_bucket{reporter=\"source\", connection_security_policy=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le)) / 1000) or histogram_quantile(0.50, sum(irate(istio_request_duration_seconds_bucket{reporter=\"source\", connection_security_policy=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P50 (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.90, sum(irate(istio_request_duration_milliseconds_bucket{reporter=\"source\", connection_security_policy=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le)) / 1000) or histogram_quantile(0.90, sum(irate(istio_request_duration_seconds_bucket{reporter=\"source\", connection_security_policy=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P90 (ðŸ”mTLS)", - "refId": "B", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.95, sum(irate(istio_request_duration_milliseconds_bucket{reporter=\"source\", connection_security_policy=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le)) / 1000) or histogram_quantile(0.95, sum(irate(istio_request_duration_seconds_bucket{reporter=\"source\", connection_security_policy=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P95 (ðŸ”mTLS)", - "refId": "C", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.99, sum(irate(istio_request_duration_milliseconds_bucket{reporter=\"source\", connection_security_policy=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le)) / 1000) or histogram_quantile(0.99, sum(irate(istio_request_duration_seconds_bucket{reporter=\"source\", connection_security_policy=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P99 (ðŸ”mTLS)", - "refId": "D", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.50, sum(irate(istio_request_duration_milliseconds_bucket{reporter=\"source\", connection_security_policy!=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le)) / 1000) or histogram_quantile(0.50, sum(irate(istio_request_duration_seconds_bucket{reporter=\"source\", connection_security_policy!=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P50", - "refId": "E", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.90, sum(irate(istio_request_duration_milliseconds_bucket{reporter=\"source\", connection_security_policy!=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le)) / 1000) or histogram_quantile(0.90, sum(irate(istio_request_duration_seconds_bucket{reporter=\"source\", connection_security_policy!=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P90", - "refId": "F", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.95, sum(irate(istio_request_duration_milliseconds_bucket{reporter=\"source\", connection_security_policy!=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le)) / 1000) or histogram_quantile(0.95, sum(irate(istio_request_duration_seconds_bucket{reporter=\"source\", connection_security_policy!=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P95", - "refId": "G", - "step": 2 - }, - { - "expr": "(histogram_quantile(0.99, sum(irate(istio_request_duration_milliseconds_bucket{reporter=\"source\", connection_security_policy!=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le)) / 1000) or histogram_quantile(0.99, sum(irate(istio_request_duration_seconds_bucket{reporter=\"source\", connection_security_policy!=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P99", - "refId": "H", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Outgoing Request Duration By Destination", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 8, - "x": 8, - "y": 23 - }, - "hiddenSeries": false, - "id": 73, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": true, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.50, sum(irate(istio_request_bytes_bucket{reporter=\"source\", connection_security_policy=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P50 (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "histogram_quantile(0.90, sum(irate(istio_request_bytes_bucket{reporter=\"source\", connection_security_policy=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P90 (ðŸ”mTLS)", - "refId": "B", - "step": 2 - }, - { - "expr": "histogram_quantile(0.95, sum(irate(istio_request_bytes_bucket{reporter=\"source\", connection_security_policy=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P95 (ðŸ”mTLS)", - "refId": "C", - "step": 2 - }, - { - "expr": "histogram_quantile(0.99, sum(irate(istio_request_bytes_bucket{reporter=\"source\", connection_security_policy=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P99 (ðŸ”mTLS)", - "refId": "D", - "step": 2 - }, - { - "expr": "histogram_quantile(0.50, sum(irate(istio_request_bytes_bucket{reporter=\"source\", connection_security_policy!=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P50", - "refId": "E", - "step": 2 - }, - { - "expr": "histogram_quantile(0.90, sum(irate(istio_request_bytes_bucket{reporter=\"source\", connection_security_policy!=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P90", - "refId": "F", - "step": 2 - }, - { - "expr": "histogram_quantile(0.95, sum(irate(istio_request_bytes_bucket{reporter=\"source\", connection_security_policy!=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P95", - "refId": "G", - "step": 2 - }, - { - "expr": "histogram_quantile(0.99, sum(irate(istio_request_bytes_bucket{reporter=\"source\", connection_security_policy!=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P99", - "refId": "H", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Outgoing Request Size By Destination", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "decbytes", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 8, - "x": 16, - "y": 23 - }, - "hiddenSeries": false, - "id": 74, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": true, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.50, sum(irate(istio_response_bytes_bucket{reporter=\"source\", connection_security_policy=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P50 (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "histogram_quantile(0.90, sum(irate(istio_response_bytes_bucket{reporter=\"source\", connection_security_policy=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P90 (ðŸ”mTLS)", - "refId": "B", - "step": 2 - }, - { - "expr": "histogram_quantile(0.95, sum(irate(istio_response_bytes_bucket{reporter=\"source\", connection_security_policy=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P95 (ðŸ”mTLS)", - "refId": "C", - "step": 2 - }, - { - "expr": "histogram_quantile(0.99, sum(irate(istio_response_bytes_bucket{reporter=\"source\", connection_security_policy=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P99 (ðŸ”mTLS)", - "refId": "D", - "step": 2 - }, - { - "expr": "histogram_quantile(0.50, sum(irate(istio_response_bytes_bucket{reporter=\"source\", connection_security_policy!=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P50", - "refId": "E", - "step": 2 - }, - { - "expr": "histogram_quantile(0.90, sum(irate(istio_response_bytes_bucket{reporter=\"source\", connection_security_policy!=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P90", - "refId": "F", - "step": 2 - }, - { - "expr": "histogram_quantile(0.95, sum(irate(istio_response_bytes_bucket{reporter=\"source\", connection_security_policy!=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P95", - "refId": "G", - "step": 2 - }, - { - "expr": "histogram_quantile(0.99, sum(irate(istio_response_bytes_bucket{reporter=\"source\", connection_security_policy!=\"mutual_tls\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service, le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} P99", - "refId": "H", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Response Size By Destination", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "decbytes", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 29 - }, - "hiddenSeries": false, - "id": 76, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "round(sum(irate(istio_tcp_received_bytes_total{connection_security_policy=\"mutual_tls\", reporter=\"source\", source_workload_namespace=~\"$namespace\", source_workload=~\"$workload\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "round(sum(irate(istio_tcp_received_bytes_total{connection_security_policy!=\"mutual_tls\", reporter=\"source\", source_workload_namespace=~\"$namespace\", source_workload=~\"$workload\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ destination_service }}", - "refId": "B", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Bytes Sent on Outgoing TCP Connection", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "Bps", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 29 - }, - "hiddenSeries": false, - "id": 78, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.0", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "round(sum(irate(istio_tcp_sent_bytes_total{reporter=\"source\", connection_security_policy=\"mutual_tls\", source_workload_namespace=~\"$namespace\", source_workload=~\"$workload\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} (ðŸ”mTLS)", - "refId": "A", - "step": 2 - }, - { - "expr": "round(sum(irate(istio_tcp_sent_bytes_total{reporter=\"source\", connection_security_policy!=\"mutual_tls\", source_workload_namespace=~\"$namespace\", source_workload=~\"$workload\", destination_service=~\"$dstsvc\"}[1m])) by (destination_service), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ destination_service }}", - "refId": "B", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Bytes Received from Outgoing TCP Connection", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "Bps", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "title": "Outbound Services", - "type": "row" - } - ], - "refresh": "1m", - "schemaVersion": 26, - "style": "dark", - "tags": [], - "templating": { - "list": [ - { - "current": { - "selected": true, - "text": "default", - "value": "default" - }, - "hide": 0, - "includeAll": false, - "label": null, - "multi": false, - "name": "datasource", - "options": [], - "query": "prometheus", - "queryValue": "", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "type": "datasource" - }, - { - "allValue": null, - "current": {}, - "datasource": "Prometheus", - "definition": "", - "hide": 0, - "includeAll": false, - "label": "Namespace", - "multi": false, - "name": "namespace", - "options": [], - "query": "query_result(sum(istio_requests_total) by (destination_workload_namespace) or sum(istio_tcp_sent_bytes_total) by (destination_workload_namespace))", - "refresh": 1, - "regex": "/.*_namespace=\"([^\"]*).*/", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": {}, - "datasource": "Prometheus", - "definition": "", - "hide": 0, - "includeAll": false, - "label": "Workload", - "multi": false, - "name": "workload", - "options": [], - "query": "query_result((sum(istio_requests_total{destination_workload_namespace=~\"$namespace\"}) by (destination_workload) or sum(istio_requests_total{source_workload_namespace=~\"$namespace\"}) by (source_workload)) or (sum(istio_tcp_sent_bytes_total{destination_workload_namespace=~\"$namespace\"}) by (destination_workload) or sum(istio_tcp_sent_bytes_total{source_workload_namespace=~\"$namespace\"}) by (source_workload)))", - "refresh": 1, - "regex": "/.*workload=\"([^\"]*).*/", - "skipUrlSync": false, - "sort": 1, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": { - "selected": true, - "text": "destination", - "value": "destination" - }, - "datasource": "Prometheus", - "definition": "", - "hide": 0, - "includeAll": false, - "label": "Reporter", - "multi": true, - "name": "qrep", - "options": [], - "query": "label_values(reporter)", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 2, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": {}, - "datasource": "Prometheus", - "definition": "", - "hide": 0, - "includeAll": true, - "label": "Inbound Workload Namespace", - "multi": true, - "name": "srcns", - "options": [], - "query": "query_result(sum(istio_requests_total{reporter=~\"$qrep\", destination_workload=\"$workload\", destination_workload_namespace=~\"$namespace\"}) by (source_workload_namespace) or sum(istio_tcp_sent_bytes_total{reporter=~\"$qrep\", destination_workload=\"$workload\", destination_workload_namespace=~\"$namespace\"}) by (source_workload_namespace))", - "refresh": 1, - "regex": "/.*namespace=\"([^\"]*).*/", - "skipUrlSync": false, - "sort": 2, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": {}, - "datasource": "Prometheus", - "definition": "", - "hide": 0, - "includeAll": true, - "label": "Inbound Workload", - "multi": true, - "name": "srcwl", - "options": [], - "query": "query_result(sum(istio_requests_total{reporter=~\"$qrep\", destination_workload=\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload_namespace=~\"$srcns\"}) by (source_workload) or sum(istio_tcp_sent_bytes_total{reporter=~\"$qrep\", destination_workload=\"$workload\", destination_workload_namespace=~\"$namespace\", source_workload_namespace=~\"$srcns\"}) by (source_workload))", - "refresh": 1, - "regex": "/.*workload=\"([^\"]*).*/", - "skipUrlSync": false, - "sort": 3, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": {}, - "datasource": "Prometheus", - "definition": "", - "hide": 0, - "includeAll": true, - "label": "Destination Service", - "multi": true, - "name": "dstsvc", - "options": [], - "query": "query_result(sum(istio_requests_total{reporter=\"source\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\"}) by (destination_service) or sum(istio_tcp_sent_bytes_total{reporter=\"source\", source_workload=~\"$workload\", source_workload_namespace=~\"$namespace\"}) by (destination_service))", - "refresh": 1, - "regex": "/.*destination_service=\"([^\"]*).*/", - "skipUrlSync": false, - "sort": 4, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - } - ] - }, - "time": { - "from": "now-5m", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ] - }, - "timezone": "", - "title": "Istio Workload Dashboard", - "uid": "UbsSZTDik", - "version": 1 -} diff --git a/manifests/addons/dashboards/pilot-dashboard.json b/manifests/addons/dashboards/pilot-dashboard.json deleted file mode 100644 index 82f730639..000000000 --- a/manifests/addons/dashboards/pilot-dashboard.json +++ /dev/null @@ -1,1749 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": false, - "gnetId": null, - "graphTooltip": 1, - "links": [], - "panels": [ - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 60, - "panels": [], - "title": "Deployed Versions", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 5, - "w": 24, - "x": 0, - "y": 1 - }, - "id": 56, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(istio_build{component=\"pilot\"}) by (tag)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ tag }}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Pilot Versions", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 6 - }, - "id": 62, - "panels": [], - "title": "Resource Usage", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 7, - "w": 6, - "x": 0, - "y": 7 - }, - "id": 5, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "process_virtual_memory_bytes{app=\"istiod\"}", - "format": "time_series", - "instant": false, - "intervalFactor": 2, - "legendFormat": "Virtual Memory", - "refId": "I", - "step": 2 - }, - { - "expr": "process_resident_memory_bytes{app=\"istiod\"}", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Resident Memory", - "refId": "H", - "step": 2 - }, - { - "expr": "go_memstats_heap_sys_bytes{app=\"istiod\"}", - "format": "time_series", - "hide": true, - "intervalFactor": 2, - "legendFormat": "heap sys", - "refId": "A" - }, - { - "expr": "go_memstats_heap_alloc_bytes{app=\"istiod\"}", - "format": "time_series", - "hide": true, - "intervalFactor": 2, - "legendFormat": "heap alloc", - "refId": "D" - }, - { - "expr": "go_memstats_alloc_bytes{app=\"istiod\"}", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Alloc", - "refId": "F", - "step": 2 - }, - { - "expr": "go_memstats_heap_inuse_bytes{app=\"istiod\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 2, - "legendFormat": "Heap in-use", - "refId": "E", - "step": 2 - }, - { - "expr": "go_memstats_stack_inuse_bytes{app=\"istiod\"}", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Stack in-use", - "refId": "G", - "step": 2 - }, - { - "expr": "container_memory_working_set_bytes{container=~\"discovery\", pod=~\"istiod-.*|istio-pilot-.*\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 2, - "legendFormat": "Discovery (container)", - "refId": "B", - "step": 2 - }, - { - "expr": "container_memory_working_set_bytes{container=~\"istio-proxy\", pod=~\"istiod-.*|istio-pilot-.*\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Sidecar (container)", - "refId": "C" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Memory", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "bytes", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 7, - "w": 6, - "x": 6, - "y": 7 - }, - "id": 6, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(irate(container_cpu_usage_seconds_total{container=\"discovery\", pod=~\"istiod-.*|istio-pilot-.*\"}[1m]))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Discovery (container)", - "refId": "A" - }, - { - "expr": "irate(process_cpu_seconds_total{app=\"istiod\"}[1m])", - "format": "time_series", - "hide": false, - "intervalFactor": 2, - "legendFormat": "Discovery (process)", - "refId": "C", - "step": 2 - }, - { - "expr": "sum(irate(container_cpu_usage_seconds_total{container=\"istio-proxy\", pod=~\"istiod-.*|istio-pilot-.*\"}[1m]))", - "format": "time_series", - "hide": false, - "intervalFactor": 2, - "legendFormat": "Sidecar (container)", - "refId": "B", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "CPU", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 7, - "w": 6, - "x": 12, - "y": 7 - }, - "id": 7, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "container_fs_usage_bytes{container=\"discovery\", pod=~\"istiod-.*|istio-pilot-.*\"}", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Discovery", - "refId": "B", - "step": 2 - }, - { - "expr": "container_fs_usage_bytes{container=\"istio-proxy\", pod=~\"istiod-.*|istio-pilot-.*\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Sidecar", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Disk", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "bytes", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "decimals": null, - "format": "none", - "label": "", - "logBase": 1024, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 7, - "w": 6, - "x": 18, - "y": 7 - }, - "id": 4, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": false, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "go_goroutines{app=\"istiod\"}", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Number of Goroutines", - "refId": "A", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Goroutines", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 14 - }, - "id": 58, - "panels": [], - "title": "Pilot Push Information", - "type": "row" - }, - { - "aliasColors": {}, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "description": "Shows the rate of pilot pushes", - "fill": 1, - "gridPos": { - "h": 8, - "w": 8, - "x": 0, - "y": 15 - }, - "id": 622, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": false, - "linewidth": 1, - "links": [], - "nullPointMode": "null as zero", - "paceLength": 10, - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "sum(irate(pilot_xds_pushes{type=\"cds\"}[1m]))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Cluster", - "refId": "C" - }, - { - "expr": "sum(irate(pilot_xds_pushes{type=\"eds\"}[1m]))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Endpoints", - "refId": "D" - }, - { - "expr": "sum(irate(pilot_xds_pushes{type=\"lds\"}[1m]))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Listeners", - "refId": "A" - }, - { - "expr": "sum(irate(pilot_xds_pushes{type=\"rds\"}[1m]))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Routes", - "refId": "E" - }, - { - "expr": "sum(irate(pilot_xds_pushes{type=\"sds\"}[1m]))", - "interval": "", - "legendFormat": "Secrets", - "refId": "B" - }, - { - "expr": "sum(irate(pilot_xds_pushes{type=\"nds\"}[1m]))", - "interval": "", - "legendFormat": "Nametables", - "refId": "F" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Pilot Pushes", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [ - "total" - ] - }, - "yaxes": [ - { - "format": "ops", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "description": "Captures a variety of pilot errors", - "fill": 1, - "gridPos": { - "h": 8, - "w": 8, - "x": 8, - "y": 15 - }, - "id": 67, - "legend": { - "avg": false, - "current": false, - "hideEmpty": true, - "hideZero": true, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(pilot_xds_cds_reject{app=\"istiod\"}) or (absent(pilot_xds_cds_reject{app=\"istiod\"}) - 1)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Rejected CDS Configs", - "refId": "C" - }, - { - "expr": "sum(pilot_xds_eds_reject{app=\"istiod\"}) or (absent(pilot_xds_eds_reject{app=\"istiod\"}) - 1)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Rejected EDS Configs", - "refId": "D" - }, - { - "expr": "sum(pilot_xds_rds_reject{app=\"istiod\"}) or (absent(pilot_xds_rds_reject{app=\"istiod\"}) - 1)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Rejected RDS Configs", - "refId": "A" - }, - { - "expr": "sum(pilot_xds_lds_reject{app=\"istiod\"}) or (absent(pilot_xds_lds_reject{app=\"istiod\"}) - 1)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Rejected LDS Configs", - "refId": "B" - }, - { - "expr": "sum(rate(pilot_xds_write_timeout{app=\"istiod\"}[1m]))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Write Timeouts", - "refId": "F" - }, - { - "expr": "sum(rate(pilot_total_xds_internal_errors{app=\"istiod\"}[1m]))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Internal Errors", - "refId": "H" - }, - { - "expr": "sum(rate(pilot_total_xds_rejects{app=\"istiod\"}[1m]))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Config Rejection Rate", - "refId": "E" - }, - { - "expr": "sum(rate(pilot_xds_push_context_errors{app=\"istiod\"}[1m]))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Push Context Errors", - "refId": "K" - }, - { - "expr": "sum(rate(pilot_xds_write_timeout{app=\"istiod\"}[1m]))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Push Timeouts", - "refId": "G" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Pilot Errors", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "description": "Shows the total time it takes to push a config update to a proxy", - "fill": 1, - "gridPos": { - "h": 8, - "w": 8, - "x": 16, - "y": 15 - }, - "id": 624, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.5, sum(rate(pilot_proxy_convergence_time_bucket[1m])) by (le))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "p50 ", - "refId": "A" - }, - { - "expr": "histogram_quantile(0.9, sum(rate(pilot_proxy_convergence_time_bucket[1m])) by (le))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "p90", - "refId": "B" - }, - { - "expr": "histogram_quantile(0.99, sum(rate(pilot_proxy_convergence_time_bucket[1m])) by (le))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "p99", - "refId": "C" - }, - { - "expr": "histogram_quantile(0.999, sum(rate(pilot_proxy_convergence_time_bucket[1m])) by (le))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "p99.9", - "refId": "D" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Proxy Push Time", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 23 - }, - "id": 45, - "legend": { - "avg": false, - "current": false, - "hideEmpty": true, - "hideZero": true, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null as zero", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "pilot_conflict_inbound_listener{app=\"istiod\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Inbound Listeners", - "refId": "B" - }, - { - "expr": "pilot_conflict_outbound_listener_http_over_current_tcp{app=\"istiod\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Outbound Listeners (http over current tcp)", - "refId": "A" - }, - { - "expr": "pilot_conflict_outbound_listener_tcp_over_current_tcp{app=\"istiod\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Outbound Listeners (tcp over current tcp)", - "refId": "C" - }, - { - "expr": "pilot_conflict_outbound_listener_tcp_over_current_http{app=\"istiod\"}", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "Outbound Listeners (tcp over current http)", - "refId": "D" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Conflicts", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 23 - }, - "id": 47, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "avg(pilot_virt_services{app=\"istiod\"})", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Virtual Services", - "refId": "A" - }, - { - "expr": "avg(pilot_services{app=\"istiod\"})", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Services", - "refId": "B" - }, - { - "expr": "sum(pilot_xds{app=\"istiod\"}) by (pod)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Connected Endpoints {{pod}}", - "refId": "E" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "ADS Monitoring", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 31 - }, - "id": 64, - "panels": [], - "title": "Envoy Information", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "description": "Shows details about Envoy proxies in the mesh", - "fill": 1, - "gridPos": { - "h": 8, - "w": 8, - "x": 0, - "y": 32 - }, - "id": 40, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(irate(envoy_cluster_upstream_cx_total{cluster_name=\"xds-grpc\"}[1m]))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "XDS Connections", - "refId": "C" - }, - { - "expr": "sum(irate(envoy_cluster_upstream_cx_connect_fail{cluster_name=\"xds-grpc\"}[1m]))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "XDS Connection Failures", - "refId": "A" - }, - { - "expr": "sum(increase(envoy_server_hot_restart_epoch[1m]))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Envoy Restarts", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Envoy Details", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "ops", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "ops", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 8, - "w": 8, - "x": 8, - "y": 32 - }, - "id": 41, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(envoy_cluster_upstream_cx_active{cluster_name=\"xds-grpc\"})", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "XDS Active Connections", - "refId": "C", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "XDS Active Connections", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "description": "Shows the size of XDS requests and responses", - "fill": 1, - "gridPos": { - "h": 8, - "w": 8, - "x": 16, - "y": 32 - }, - "id": 42, - "legend": { - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "max(rate(envoy_cluster_upstream_cx_rx_bytes_total{cluster_name=\"xds-grpc\"}[1m]))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "XDS Response Bytes Max", - "refId": "D" - }, - { - "expr": "quantile(0.5, rate(envoy_cluster_upstream_cx_rx_bytes_total{cluster_name=\"xds-grpc\"}[1m]))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "XDS Response Bytes Average", - "refId": "B" - }, - { - "expr": "max(rate(envoy_cluster_upstream_cx_tx_bytes_total{cluster_name=\"xds-grpc\"}[1m]))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "XDS Request Bytes Max", - "refId": "A" - }, - { - "expr": "quantile(.5, rate(envoy_cluster_upstream_cx_tx_bytes_total{cluster_name=\"xds-grpc\"}[1m]))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "XDS Request Bytes Average", - "refId": "C" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "XDS Requests Size", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "Bps", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "ops", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "datasource": null, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 40 - }, - "id": 626, - "panels": [], - "title": "Webhooks", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 41 - }, - "hiddenSeries": false, - "id": 629, - "legend": { - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(galley_validation_passed[1m]))", - "interval": "", - "legendFormat": "Validations (Success)", - "refId": "A" - }, - { - "expr": "sum(rate(galley_validation_failed[1m]))", - "interval": "", - "legendFormat": "Validation (Failure)", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Configuration Validation", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "description": "", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 41 - }, - "hiddenSeries": false, - "id": 630, - "legend": { - "avg": false, - "current": false, - "hideZero": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(sidecar_injection_success_total[1m]))", - "interval": "", - "legendFormat": "Injections (Success)", - "refId": "A" - }, - { - "expr": "sum(rate(sidecar_injection_failure_total[1m]))", - "interval": "", - "legendFormat": "Injections (Failure)", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Sidecar Injection", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "refresh": "5s", - "schemaVersion": 18, - "style": "dark", - "tags": [], - "templating": { - "list": [ - { - "current": { - "selected": true, - "text": "default", - "value": "default" - }, - "hide": 0, - "includeAll": false, - "label": null, - "multi": false, - "name": "datasource", - "options": [], - "query": "prometheus", - "queryValue": "", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "type": "datasource" - } - ] - }, - "time": { - "from": "now-5m", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ] - }, - "timezone": "browser", - "title": "Istio Control Plane Dashboard", - "uid": "3--MLVZZk", - "version": 11 -} diff --git a/manifests/addons/gen.sh b/manifests/addons/gen.sh deleted file mode 100755 index 4a9ecff42..000000000 --- a/manifests/addons/gen.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env bash - -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -WD=$(dirname "$0") -WD=$(cd "$WD"; pwd) - -set -eux - -# This script sets up the plain text rendered deployments for addons -# See samples/addons/README.md for more information - -ADDONS="${WD}/../../samples/addons" -DASHBOARDS="${WD}/dashboards" -mkdir -p "${ADDONS}" -TMP=$(mktemp -d) -# Set up kiali -{ -helm3 template kiali-server \ - --namespace dubbo-system \ - --version 1.50.0 \ - --set deployment.image_version=v1.50 \ - --include-crds \ - --set nameOverride=kiali \ - --set fullnameOverride=kiali \ - kiali-server \ - --repo https://kiali.org/helm-charts \ - -f "${WD}/values-kiali.yaml" -} > "${ADDONS}/kiali.yaml" - -# Set up prometheus -helm3 template prometheus prometheus \ - --namespace dubbo-system \ - --version 15.0.1 \ - --repo https://prometheus-community.github.io/helm-charts \ - -f "${WD}/values-prometheus.yaml" \ - > "${ADDONS}/prometheus.yaml" - -function compressDashboard() { - < "${DASHBOARDS}/$1" jq -c > "${TMP}/$1" -} - -# Set up grafana -{ - helm3 template grafana grafana \ - --namespace dubbo-system \ - --version 6.18.2 \ - --repo https://grafana.github.io/helm-charts \ - -f "${WD}/values-grafana.yaml" - - # Set up grafana dashboards. Split into 2 and compress to single line json to avoid Kubernetes size limits - compressDashboard "pilot-dashboard.json" - compressDashboard "istio-performance-dashboard.json" - compressDashboard "istio-workload-dashboard.json" - compressDashboard "istio-service-dashboard.json" - compressDashboard "istio-mesh-dashboard.json" - compressDashboard "istio-extension-dashboard.json" - echo -e "\n---\n" - kubectl create configmap -n dubbo-system istio-grafana-dashboards \ - --dry-run=client -oyaml \ - --from-file=pilot-dashboard.json="${TMP}/pilot-dashboard.json" \ - --from-file=istio-performance-dashboard.json="${TMP}/istio-performance-dashboard.json" - - echo -e "\n---\n" - kubectl create configmap -n dubbo-system istio-services-grafana-dashboards \ - --dry-run=client -oyaml \ - --from-file=istio-workload-dashboard.json="${TMP}/istio-workload-dashboard.json" \ - --from-file=istio-service-dashboard.json="${TMP}/istio-service-dashboard.json" \ - --from-file=istio-mesh-dashboard.json="${TMP}/istio-mesh-dashboard.json" \ - --from-file=istio-extension-dashboard.json="${TMP}/istio-extension-dashboard.json" -} > "${ADDONS}/grafana.yaml" diff --git a/manifests/addons/values-grafana.yaml b/manifests/addons/values-grafana.yaml deleted file mode 100644 index c8af4b79c..000000000 --- a/manifests/addons/values-grafana.yaml +++ /dev/null @@ -1,68 +0,0 @@ -# Avoid creating a bunch of RBAC rules for features we are not enabling -rbac: - create: false - pspEnabled: false - -# Disable test pods -testFramework: - enabled: false - -# For istioctl dashboard, we will look for this label -podLabels: - app: grafana - sidecar.istio.io/inject: "false" - -# Demo only, so we will have no authentication -admin: - existingSecret: true -ldap: - existingSecret: true -env: - GF_SECURITY_ADMIN_USER: "-" - GF_SECURITY_ADMIN_PASSWORD: "-" - GF_AUTH_BASIC_ENABLED: "false" - GF_AUTH_ANONYMOUS_ENABLED: "true" - GF_AUTH_ANONYMOUS_ORG_ROLE: Admin - -# Expose on port 3000 to match the Istio docs -service: - port: 3000 - -# Set up out dashboards -dashboardProviders: - dashboardproviders.yaml: - apiVersion: 1 - providers: - - name: 'istio' - orgId: 1 - folder: 'istio' - type: file - disableDeletion: false - options: - path: /var/lib/grafana/dashboards/istio - - name: 'istio-services' - orgId: 1 - folder: 'istio' - type: file - disableDeletion: false - options: - path: /var/lib/grafana/dashboards/istio-services - -dashboardsConfigMaps: - istio: "istio-grafana-dashboards" - istio-services: "istio-services-grafana-dashboards" - -# Configure the prometheus data source. We expect prometheus:9090 in the same namespace -datasources: - datasources.yaml: - apiVersion: 1 - datasources: - - name: Prometheus - type: prometheus - orgId: 1 - url: http://prometheus:9090 - access: proxy - isDefault: true - jsonData: - timeInterval: 5s - editable: true diff --git a/manifests/addons/values-kiali.yaml b/manifests/addons/values-kiali.yaml deleted file mode 100644 index 37c73d71d..000000000 --- a/manifests/addons/values-kiali.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# We set up anonymous authentication as this is for demos. -auth: - strategy: anonymous -deployment: - pod_labels: - sidecar.istio.io/inject: "false" - accessible_namespaces: - - '**' - ingress_enabled: false - hpa: - api_version: autoscaling/v2beta2 -login_token: - signing_key: CHANGEME00000000 diff --git a/manifests/addons/values-prometheus.yaml b/manifests/addons/values-prometheus.yaml deleted file mode 100644 index faaddbd9d..000000000 --- a/manifests/addons/values-prometheus.yaml +++ /dev/null @@ -1,28 +0,0 @@ -# To simplify the deployment, disable non-essential components -alertmanager: - enabled: false -pushgateway: - enabled: false -kubeStateMetrics: - enabled: false -nodeExporter: - enabled: false -server: - podLabels: - sidecar.istio.io/inject: "false" - persistentVolume: - enabled: false - # Use port 9090 to match Istio documentation - service: - servicePort: 9090 - readinessProbeInitialDelay: 0 - # Speed up scraping a bit from the default - global: - scrape_interval: 15s - - # Match legacy addon deployment - fullnameOverride: prometheus - - # use dockerhub - image: - repository: prom/prometheus diff --git a/manifests/charts/README.md b/manifests/charts/README.md deleted file mode 100644 index 82451c5cf..000000000 --- a/manifests/charts/README.md +++ /dev/null @@ -1,136 +0,0 @@ -# Istio Installer - -Note: If making any changes to the charts or values.yaml in this dir, first read [UPDATING-CHARTS.md](UPDATING-CHARTS.md) - -Istio installer is a modular, 'a-la-carte' installer for Istio. It is based on a -fork of the Istio helm templates, refactored to increase modularity and isolation. - -Goals: -- Improve upgrade experience: users should be able to gradually roll upgrades, with proper -canary deployments for Istio components. It should be possible to deploy a new version while keeping the -stable version in place and gradually migrate apps to the new version. - -- More flexibility: the new installer allows multiple 'environments', allowing applications to select -a set of control plane settings and components. While the entire mesh respects the same APIs and config, -apps may target different 'environments' which contain different instances and variants of Istio. - -- Better security: separate Istio components reside in different namespaces, allowing different teams or -roles to manage different parts of Istio. For example, a security team would maintain the -root CA and policy, a telemetry team may only have access to Prometheus, -and a different team may maintain the control plane components (which are highly security sensitive). - -The install is organized in 'environments' - each environment consists of a set of components -in different namespaces that are configured to work together. Regardless of 'environment', -workloads can talk with each other and obey the Istio configuration resources, but each environment -can use different Istio versions and different configuration defaults. - -`istioctl kube-inject` or the automatic sidecar injector are used to select the environment. -In the case of the sidecar injector, the namespace label `istio-env: ` is used instead -of the conventional `istio-injected: true`. The name of the environment is defined as the namespace -where the corresponding control plane components (config, discovery, auto-injection) are running. -In the examples below, by default this is the `istio-control` namespace. Pod annotations can also -be used to select a different 'environment'. - -## Installing - -The new installer is intended to be modular and very explicit about what is installed. It has -far more steps than the Istio installer - but each step is smaller and focused on a specific -feature, and can be performed by different people/teams at different times. - -It is strongly recommended that different namespaces are used, with different service accounts. -In particular access to the security-critical production components (root CA, policy, control) -should be locked down and restricted. The new installer allows multiple instances of -policy/control/telemetry - so testing/staging of new settings and versions can be performed -by a different role than the prod version. - -The intended users of this repo are users running Istio in production who want to select, tune -and understand each binary that gets deployed, and select which combination to use. - -Note: each component can be installed in parallel with an existing Istio 1.0 or 1.1 install in -`dubbo-system`. The new components will not interfere with existing apps, but can interoperate -and it is possible to gradually move apps from Istio 1.0/1.1 to the new environments and -across environments ( for example canary -> prod ) - -Note: there are still some cluster roles that may need to be fixed, most likely cluster permissions -will need to move to the security component. - -## Everything is Optional - -Each component in the new installer is optional. Users can install the component defined in the new installer, -use the equivalent component in `dubbo-system`, configured with the official installer, or use a different -version or implementation. - -For example you may use your own Prometheus and Grafana installs, or you may use a specialized/custom -certificate provisioning tool, or use components that are centrally managed and running in a different cluster. - -This is a work in progress - building on top of the multi-cluster installer. - -As an extreme, the goal is to be possible to run Istio workloads in a cluster without installing any Istio component -in that cluster. Currently the minimum we require is the security provider (node agent or citadel). - -### Install Istio CRDs - -This is the first step of the install. Please do not remove or edit any CRD - config currently requires -all CRDs to be present. On each upgrade it is recommended to reapply the file, to make sure -you get all CRDs. CRDs are separated by release and by component type in the CRD directory. - -Istio has strong integration with certmanager. Some operators may want to keep their current certmanager -CRDs in place and not have Istio modify them. In this case, it is necessary to apply CRD files individually. - -```bash -kubectl apply -k github.com/istio/installer/base -``` - -or - -```bash -kubectl apply -f base/files -``` - -### Install Istio-CNI - -This is an optional step - CNI must run in a dedicated namespace, it is a 'singleton' and extremely -security sensitive. Access to the CNI namespace must be highly restricted. - -**NOTE:** The environment variable `ISTIO_CLUSTER_ISGKE` is assumed to be set to `true` if the cluster -is a GKE cluster. - -```bash -ISTIO_CNI_ARGS= -# TODO: What k8s data can we use for this check for whether GKE? -if [[ "${ISTIO_CLUSTER_ISGKE}" == "true" ]]; then - ISTIO_CNI_ARGS="--set cni.cniBinDir=/home/kubernetes/bin" -fi -iop kube-system istio-cni $IBASE/istio-cni/ ${ISTIO_CNI_ARGS} -``` - -TODO. It is possible to add Istio-CNI later, and gradually migrate. - -### Install Control plane - -This can run in any cluster. A mesh should have at least one cluster should run Pilot or equivalent XDS server, -and it is recommended to have Pilot running in each region and in multiple availability zones for multi cluster. - -```bash -iop istio-control istio-discovery $IBASE/istio-control/istio-discovery \ - --set global.istioNamespace=dubbo-system - -# Second istio-discovery, using master version of istio -TAG=latest HUB=gcr.io/istio-testing iop istio-master istio-discovery-master $IBASE/istio-control/istio-discovery \ - --set policy.enable=false \ - --set global.istioNamespace=istio-master -``` - -### Gateways - -A cluster may use multiple Gateways, each with a different load balancer IP, domains and certificates. - -Since the domain certificates are stored in the gateway namespace, it is recommended to keep each -gateway in a dedicated namespace and restrict access. - -For large-scale gateways it is optionally possible to use a dedicated pilot in the gateway namespace. - -### Additional test templates - -A number of helm test setups are general-purpose and should be installable in any cluster, to confirm -Istio works properly and allow testing the specific install. diff --git a/manifests/charts/UPDATING-CHARTS.md b/manifests/charts/UPDATING-CHARTS.md deleted file mode 100644 index 33f7e5559..000000000 --- a/manifests/charts/UPDATING-CHARTS.md +++ /dev/null @@ -1,60 +0,0 @@ -# Upating charts and values.yaml - -The charts in the `manifests` directory are used in istioctl to generate an installation manifest. The configuration -settings contained in values.yaml files and passed through the CLI are validated against a -[schema](../../operator/pkg/apis/istio/v1alpha1/values_types.proto). -Whenever making changes in the charts, it's important to follow the below steps. - -## Step 0. Check that any schema change really belongs in values.yaml - -Is this a new parameter being added? If not, go to the next step. -Dynamic, runtime config that is used to configure Istio components should go into the -[MeshConfig API](https://github.com/istio/api/blob/master/mesh/v1alpha1/config.proto). Values.yaml is being deprecated and adding -to it is discouraged. MeshConfig is the official API which follows API management practices and is dynamic -(does not require component restarts). -Exceptions to this rule are configuration items that affect K8s level settings (resources, mounts etc.) - -## Step 1. Make changes in charts and values.yaml in `manifests` directory - -## Step 2. Make corresponding values changes in [../profiles/default.yaml](../profiles/default.yaml) - -The values.yaml in `manifests` are only used for direct Helm based installations, which is being deprecated. -If any values.yaml changes are being made, the same changes must be made in the `manifests/profiles/default.yaml` -file, which must be in sync with the Helm values in `manifests`. - -## Step 3. Update the validation schema - -Istioctl uses a [schema](../../operator/pkg/apis/istio/v1alpha1/values_types.proto) to validate the values. Any changes to -the schema must be added here, otherwise istioctl users will see errors. -Once the schema file is updated, run: - -```bash -$ make operator-proto -``` - -This will regenerate the Go structs used for schema validation. - -## Step 4. Update the generated manifests - -Tests of istioctl use the auto-generated manifests to ensure that the istioctl binary has the correct version of the charts. -These manifests can be found in [gen-istio.yaml](../charts/istio-control/istio-discovery/files/gen-istio.yaml). -To regenerate the manifests, run: - -```bash -$ make gen -``` - -## Step 5. Update golden files - -The new charts/values will likely produce different installation manifests. Unit tests that expect a certain command -output will fail for this reason. To update the golden output files, run: - -```bash -$ make refresh-goldens -``` - -This will generate git diffs in the golden output files. Check that the changes are what you expect. - -## Step 6. Create a PR using outputs from Steps 1 to 5 - -Your PR should pass all the checks if you followed these steps. diff --git a/manifests/charts/base/Chart.yaml b/manifests/charts/base/Chart.yaml deleted file mode 100644 index 11934811f..000000000 --- a/manifests/charts/base/Chart.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -name: base -# This version is never actually shipped. istio/release-builder will replace it at build-time -# with the appropriate version -version: 1.0.0 -appVersion: 1.0.0 -tillerVersion: ">=2.7.2" -description: Helm chart for deploying Istio cluster resources and CRDs -keywords: - - istio -sources: - - http://github.com/istio/istio -engine: gotpl -icon: https://istio.io/latest/favicons/android-192x192.png diff --git a/manifests/charts/base/README.md b/manifests/charts/base/README.md deleted file mode 100644 index 6a93c189c..000000000 --- a/manifests/charts/base/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Istio base Helm Chart - -This chart installs resources shared by all Istio revisions. This includes Istio CRDs. - -## Setup Repo Info - -```console -helm repo add istio https://istio-release.storage.googleapis.com/charts -helm repo update -``` - -_See [helm repo](https://helm.sh/docs/helm/helm_repo/) for command documentation._ - -## Installing the Chart - -To install the chart with the release name `istio-base`: - -```console -kubectl create namespace dubbo-system -helm install istio-base istio/base -n dubbo-system -``` diff --git a/manifests/charts/base/crds/crd-all.gen.yaml b/manifests/charts/base/crds/crd-all.gen.yaml deleted file mode 100644 index cde5582b4..000000000 --- a/manifests/charts/base/crds/crd-all.gen.yaml +++ /dev/null @@ -1,6571 +0,0 @@ -# DO NOT EDIT - Generated by Cue OpenAPI generator based on Istio APIs. -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: servicemetadatas.extensions.istio.io -spec: - group: extensions.istio.io - names: - categories: - - istio-io - - extensions-istio-io - kind: ServiceMetadata - listKind: ServiceMetadataList - plural: servicemetadatas - shortNames: - - sm - singular: servicemetadata - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'it''s the spec of Service Metadata See more details at:' - properties: - applicationName: - type: string - metadataInfo: - type: string - revision: - type: string - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: servicenamemappings.extensions.istio.io -spec: - group: extensions.istio.io - names: - categories: - - istio-io - - extensions-istio-io - kind: ServiceNameMapping - listKind: ServiceNameMappingList - plural: servicenamemappings - shortNames: - - snp - singular: servicenamemapping - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'it''s the spec of Service Name Mapping, which is used to - map the interface name See more details at:' - properties: - applicationNames: - items: - type: string - type: array - interfaceName: - description: InterfaceName. - type: string - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: wasmplugins.extensions.istio.io -spec: - group: extensions.istio.io - names: - categories: - - istio-io - - extensions-istio-io - kind: WasmPlugin - listKind: WasmPluginList - plural: wasmplugins - singular: wasmplugin - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Extend the functionality provided by the Istio proxy through - WebAssembly filters. See more details at: https://istio.io/docs/reference/config/proxy_extensions/wasm-plugin.html' - properties: - imagePullPolicy: - description: The pull behaviour to be applied when fetching an OCI - image. - enum: - - UNSPECIFIED_POLICY - - IfNotPresent - - Always - type: string - imagePullSecret: - description: Credentials to use for OCI image pulling. - type: string - phase: - description: Determines where in the filter chain this `WasmPlugin` - is to be injected. - enum: - - UNSPECIFIED_PHASE - - AUTHN - - AUTHZ - - STATS - type: string - pluginConfig: - description: The configuration that will be passed on to the plugin. - type: object - x-kubernetes-preserve-unknown-fields: true - pluginName: - type: string - priority: - description: Determines ordering of `WasmPlugins` in the same `phase`. - nullable: true - type: integer - selector: - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - sha256: - description: SHA256 checksum that will be used to verify Wasm module - or OCI container. - type: string - url: - description: URL of a Wasm module or OCI container. - type: string - verificationKey: - type: string - vmConfig: - description: Configuration for a Wasm VM. - properties: - env: - description: Specifies environment variables to be injected to - this VM. - items: - properties: - name: - type: string - value: - description: Value for the environment variable. - type: string - valueFrom: - enum: - - INLINE - - HOST - type: string - type: object - type: array - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: destinationrules.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: DestinationRule - listKind: DestinationRuleList - plural: destinationrules - shortNames: - - dr - singular: destinationrule - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The name of a service from the service registry - jsonPath: .spec.host - name: Host - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting load balancing, outlier detection, - etc. See more details at: https://istio.io/docs/reference/config/networking/destination-rule.html' - properties: - exportTo: - description: A list of namespaces to which this destination rule is - exported. - items: - type: string - type: array - host: - description: The name of a service from the service registry. - type: string - subsets: - items: - properties: - labels: - additionalProperties: - type: string - type: object - name: - description: Name of the subset. - type: string - trafficPolicy: - description: Traffic policies that apply to this subset. - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should - be upgraded to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests - to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will - be preserved while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the - socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query - parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to - traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this - is DestinationRule-level and will override mesh - wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list - of labels used to sort endpoints to do priority - based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local - origin failures from external errors. - type: boolean - type: object - portLevelSettings: - description: Traffic policies specific to individual ports. - items: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection - should be upgraded to http2 for the associated - destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP - requests to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to - a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream - connection pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per - connection to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol - will be preserved while initiating connection - to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and - TCP upstream connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP - connections to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE - on the socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between - keep-alive probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer - algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP - header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP - query parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' - separated, e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities - to traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, - this is DestinationRule-level and will override - mesh wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered - list of labels used to sort endpoints to - do priority based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of - Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a - host is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep - analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish - local origin failures from external errors. - type: boolean - type: object - port: - properties: - number: - type: integer - type: object - tls: - description: TLS related settings for connections - to the upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server - during TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - type: object - type: array - tls: - description: TLS related settings for connections to the - upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during - TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - tunnel: - properties: - protocol: - description: Specifies which protocol to use for tunneling - the downstream connection. - type: string - targetHost: - description: Specifies a host to which the downstream - connection is tunneled. - type: string - targetPort: - description: Specifies a port to which the downstream - connection is tunneled. - type: integer - type: object - type: object - type: object - type: array - trafficPolicy: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should be upgraded - to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests to - a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will be preserved - while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the socket - to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, failover - or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to traffic - distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this is DestinationRule-level - and will override mesh wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, failover - or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list of labels - used to sort endpoints to do priority based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local origin - failures from external errors. - type: boolean - type: object - portLevelSettings: - description: Traffic policies specific to individual ports. - items: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should - be upgraded to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests - to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will - be preserved while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the - socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query - parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to - traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this - is DestinationRule-level and will override mesh - wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list - of labels used to sort endpoints to do priority - based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local - origin failures from external errors. - type: boolean - type: object - port: - properties: - number: - type: integer - type: object - tls: - description: TLS related settings for connections to the - upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during - TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - type: object - type: array - tls: - description: TLS related settings for connections to the upstream - service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during TLS - handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - tunnel: - properties: - protocol: - description: Specifies which protocol to use for tunneling - the downstream connection. - type: string - targetHost: - description: Specifies a host to which the downstream connection - is tunneled. - type: string - targetPort: - description: Specifies a port to which the downstream connection - is tunneled. - type: integer - type: object - type: object - workloadSelector: - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - description: The name of a service from the service registry - jsonPath: .spec.host - name: Host - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting load balancing, outlier detection, - etc. See more details at: https://istio.io/docs/reference/config/networking/destination-rule.html' - properties: - exportTo: - description: A list of namespaces to which this destination rule is - exported. - items: - type: string - type: array - host: - description: The name of a service from the service registry. - type: string - subsets: - items: - properties: - labels: - additionalProperties: - type: string - type: object - name: - description: Name of the subset. - type: string - trafficPolicy: - description: Traffic policies that apply to this subset. - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should - be upgraded to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests - to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will - be preserved while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the - socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query - parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to - traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this - is DestinationRule-level and will override mesh - wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list - of labels used to sort endpoints to do priority - based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local - origin failures from external errors. - type: boolean - type: object - portLevelSettings: - description: Traffic policies specific to individual ports. - items: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection - should be upgraded to http2 for the associated - destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP - requests to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to - a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream - connection pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per - connection to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol - will be preserved while initiating connection - to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and - TCP upstream connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP - connections to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE - on the socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between - keep-alive probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer - algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP - header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP - query parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' - separated, e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities - to traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, - this is DestinationRule-level and will override - mesh wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered - list of labels used to sort endpoints to - do priority based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of - Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a - host is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep - analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish - local origin failures from external errors. - type: boolean - type: object - port: - properties: - number: - type: integer - type: object - tls: - description: TLS related settings for connections - to the upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server - during TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - type: object - type: array - tls: - description: TLS related settings for connections to the - upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during - TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - tunnel: - properties: - protocol: - description: Specifies which protocol to use for tunneling - the downstream connection. - type: string - targetHost: - description: Specifies a host to which the downstream - connection is tunneled. - type: string - targetPort: - description: Specifies a port to which the downstream - connection is tunneled. - type: integer - type: object - type: object - type: object - type: array - trafficPolicy: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should be upgraded - to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests to - a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will be preserved - while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the socket - to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, failover - or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to traffic - distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this is DestinationRule-level - and will override mesh wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, failover - or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list of labels - used to sort endpoints to do priority based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local origin - failures from external errors. - type: boolean - type: object - portLevelSettings: - description: Traffic policies specific to individual ports. - items: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should - be upgraded to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests - to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will - be preserved while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the - socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query - parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to - traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this - is DestinationRule-level and will override mesh - wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list - of labels used to sort endpoints to do priority - based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local - origin failures from external errors. - type: boolean - type: object - port: - properties: - number: - type: integer - type: object - tls: - description: TLS related settings for connections to the - upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during - TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - type: object - type: array - tls: - description: TLS related settings for connections to the upstream - service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during TLS - handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - tunnel: - properties: - protocol: - description: Specifies which protocol to use for tunneling - the downstream connection. - type: string - targetHost: - description: Specifies a host to which the downstream connection - is tunneled. - type: string - targetPort: - description: Specifies a port to which the downstream connection - is tunneled. - type: integer - type: object - type: object - workloadSelector: - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: envoyfilters.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: EnvoyFilter - listKind: EnvoyFilterList - plural: envoyfilters - singular: envoyfilter - scope: Namespaced - versions: - - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Customizing Envoy configuration generated by Istio. See - more details at: https://istio.io/docs/reference/config/networking/envoy-filter.html' - properties: - configPatches: - description: One or more patches with match conditions. - items: - properties: - applyTo: - enum: - - INVALID - - LISTENER - - FILTER_CHAIN - - NETWORK_FILTER - - HTTP_FILTER - - ROUTE_CONFIGURATION - - VIRTUAL_HOST - - HTTP_ROUTE - - CLUSTER - - EXTENSION_CONFIG - - BOOTSTRAP - type: string - match: - description: Match on listener/route configuration/cluster. - oneOf: - - not: - anyOf: - - required: - - listener - - required: - - routeConfiguration - - required: - - cluster - - required: - - listener - - required: - - routeConfiguration - - required: - - cluster - properties: - cluster: - description: Match on envoy cluster attributes. - properties: - name: - description: The exact name of the cluster to match. - type: string - portNumber: - description: The service port for which this cluster - was generated. - type: integer - service: - description: The fully qualified service name for this - cluster. - type: string - subset: - description: The subset associated with the service. - type: string - type: object - context: - description: The specific config generation context to match - on. - enum: - - ANY - - SIDECAR_INBOUND - - SIDECAR_OUTBOUND - - GATEWAY - type: string - listener: - description: Match on envoy listener attributes. - properties: - filterChain: - description: Match a specific filter chain in a listener. - properties: - applicationProtocols: - description: Applies only to sidecars. - type: string - destinationPort: - description: The destination_port value used by - a filter chain's match condition. - type: integer - filter: - description: The name of a specific filter to apply - the patch to. - properties: - name: - description: The filter name to match on. - type: string - subFilter: - properties: - name: - description: The filter name to match on. - type: string - type: object - type: object - name: - description: The name assigned to the filter chain. - type: string - sni: - description: The SNI value used by a filter chain's - match condition. - type: string - transportProtocol: - description: Applies only to `SIDECAR_INBOUND` context. - type: string - type: object - name: - description: Match a specific listener by its name. - type: string - portName: - type: string - portNumber: - type: integer - type: object - proxy: - description: Match on properties associated with a proxy. - properties: - metadata: - additionalProperties: - type: string - type: object - proxyVersion: - type: string - type: object - routeConfiguration: - description: Match on envoy HTTP route configuration attributes. - properties: - gateway: - type: string - name: - description: Route configuration name to match on. - type: string - portName: - description: Applicable only for GATEWAY context. - type: string - portNumber: - type: integer - vhost: - properties: - name: - type: string - route: - description: Match a specific route within the virtual - host. - properties: - action: - description: Match a route with specific action - type. - enum: - - ANY - - ROUTE - - REDIRECT - - DIRECT_RESPONSE - type: string - name: - type: string - type: object - type: object - type: object - type: object - patch: - description: The patch to apply along with the operation. - properties: - filterClass: - description: Determines the filter insertion order. - enum: - - UNSPECIFIED - - AUTHN - - AUTHZ - - STATS - type: string - operation: - description: Determines how the patch should be applied. - enum: - - INVALID - - MERGE - - ADD - - REMOVE - - INSERT_BEFORE - - INSERT_AFTER - - INSERT_FIRST - - REPLACE - type: string - value: - description: The JSON config of the object being patched. - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - type: object - type: array - priority: - description: Priority defines the order in which patch sets are applied - within a context. - format: int32 - type: integer - workloadSelector: - properties: - labels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: gateways.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: Gateway - listKind: GatewayList - plural: gateways - shortNames: - - gw - singular: gateway - scope: Namespaced - versions: - - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting edge load balancer. See more details - at: https://istio.io/docs/reference/config/networking/gateway.html' - properties: - selector: - additionalProperties: - type: string - type: object - servers: - description: A list of server specifications. - items: - properties: - bind: - type: string - defaultEndpoint: - type: string - hosts: - description: One or more hosts exposed by this gateway. - items: - type: string - type: array - name: - description: An optional name of the server, when set must be - unique across all servers. - type: string - port: - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - tls: - description: Set of TLS related options that govern the server's - behavior. - properties: - caCertificates: - description: REQUIRED if mode is `MUTUAL`. - type: string - cipherSuites: - description: 'Optional: If specified, only support the specified - cipher list.' - items: - type: string - type: array - credentialName: - type: string - httpsRedirect: - type: boolean - maxProtocolVersion: - description: 'Optional: Maximum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - minProtocolVersion: - description: 'Optional: Minimum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - mode: - enum: - - PASSTHROUGH - - SIMPLE - - MUTUAL - - AUTO_PASSTHROUGH - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - serverCertificate: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - subjectAltNames: - items: - type: string - type: array - verifyCertificateHash: - items: - type: string - type: array - verifyCertificateSpki: - items: - type: string - type: array - type: object - type: object - type: array - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting edge load balancer. See more details - at: https://istio.io/docs/reference/config/networking/gateway.html' - properties: - selector: - additionalProperties: - type: string - type: object - servers: - description: A list of server specifications. - items: - properties: - bind: - type: string - defaultEndpoint: - type: string - hosts: - description: One or more hosts exposed by this gateway. - items: - type: string - type: array - name: - description: An optional name of the server, when set must be - unique across all servers. - type: string - port: - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - tls: - description: Set of TLS related options that govern the server's - behavior. - properties: - caCertificates: - description: REQUIRED if mode is `MUTUAL`. - type: string - cipherSuites: - description: 'Optional: If specified, only support the specified - cipher list.' - items: - type: string - type: array - credentialName: - type: string - httpsRedirect: - type: boolean - maxProtocolVersion: - description: 'Optional: Maximum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - minProtocolVersion: - description: 'Optional: Minimum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - mode: - enum: - - PASSTHROUGH - - SIMPLE - - MUTUAL - - AUTO_PASSTHROUGH - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - serverCertificate: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - subjectAltNames: - items: - type: string - type: array - verifyCertificateHash: - items: - type: string - type: array - verifyCertificateSpki: - items: - type: string - type: array - type: object - type: object - type: array - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: proxyconfigs.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: ProxyConfig - listKind: ProxyConfigList - plural: proxyconfigs - singular: proxyconfig - scope: Namespaced - versions: - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Provides configuration for individual workloads. See more - details at: https://istio.io/docs/reference/config/networking/proxy-config.html' - properties: - concurrency: - description: The number of worker threads to run. - nullable: true - type: integer - environmentVariables: - additionalProperties: - type: string - description: Additional environment variables for the proxy. - type: object - image: - description: Specifies the details of the proxy image. - properties: - imageType: - description: The image type of the image. - type: string - type: object - selector: - description: Optional. - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: serviceentries.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: ServiceEntry - listKind: ServiceEntryList - plural: serviceentries - shortNames: - - se - singular: serviceentry - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The hosts associated with the ServiceEntry - jsonPath: .spec.hosts - name: Hosts - type: string - - description: Whether the service is external to the mesh or part of the mesh - (MESH_EXTERNAL or MESH_INTERNAL) - jsonPath: .spec.location - name: Location - type: string - - description: Service discovery mode for the hosts (NONE, STATIC, or DNS) - jsonPath: .spec.resolution - name: Resolution - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting service registry. See more details - at: https://istio.io/docs/reference/config/networking/service-entry.html' - properties: - addresses: - description: The virtual IP addresses associated with the service. - items: - type: string - type: array - endpoints: - description: One or more endpoints associated with the service. - items: - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - type: array - exportTo: - description: A list of namespaces to which this service is exported. - items: - type: string - type: array - hosts: - description: The hosts associated with the ServiceEntry. - items: - type: string - type: array - location: - enum: - - MESH_EXTERNAL - - MESH_INTERNAL - type: string - ports: - description: The ports associated with the external service. - items: - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - type: array - resolution: - description: Service discovery mode for the hosts. - enum: - - NONE - - STATIC - - DNS - - DNS_ROUND_ROBIN - type: string - subjectAltNames: - items: - type: string - type: array - workloadSelector: - description: Applicable only for MESH_INTERNAL services. - properties: - labels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - description: The hosts associated with the ServiceEntry - jsonPath: .spec.hosts - name: Hosts - type: string - - description: Whether the service is external to the mesh or part of the mesh - (MESH_EXTERNAL or MESH_INTERNAL) - jsonPath: .spec.location - name: Location - type: string - - description: Service discovery mode for the hosts (NONE, STATIC, or DNS) - jsonPath: .spec.resolution - name: Resolution - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting service registry. See more details - at: https://istio.io/docs/reference/config/networking/service-entry.html' - properties: - addresses: - description: The virtual IP addresses associated with the service. - items: - type: string - type: array - endpoints: - description: One or more endpoints associated with the service. - items: - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - type: array - exportTo: - description: A list of namespaces to which this service is exported. - items: - type: string - type: array - hosts: - description: The hosts associated with the ServiceEntry. - items: - type: string - type: array - location: - enum: - - MESH_EXTERNAL - - MESH_INTERNAL - type: string - ports: - description: The ports associated with the external service. - items: - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - type: array - resolution: - description: Service discovery mode for the hosts. - enum: - - NONE - - STATIC - - DNS - - DNS_ROUND_ROBIN - type: string - subjectAltNames: - items: - type: string - type: array - workloadSelector: - description: Applicable only for MESH_INTERNAL services. - properties: - labels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: sidecars.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: Sidecar - listKind: SidecarList - plural: sidecars - singular: sidecar - scope: Namespaced - versions: - - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting network reachability of a sidecar. - See more details at: https://istio.io/docs/reference/config/networking/sidecar.html' - properties: - egress: - items: - properties: - bind: - type: string - captureMode: - enum: - - DEFAULT - - IPTABLES - - NONE - type: string - hosts: - items: - type: string - type: array - port: - description: The port associated with the listener. - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - type: object - type: array - ingress: - items: - properties: - bind: - description: The IP to which the listener should be bound. - type: string - captureMode: - enum: - - DEFAULT - - IPTABLES - - NONE - type: string - defaultEndpoint: - type: string - port: - description: The port associated with the listener. - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - tls: - properties: - caCertificates: - description: REQUIRED if mode is `MUTUAL`. - type: string - cipherSuites: - description: 'Optional: If specified, only support the specified - cipher list.' - items: - type: string - type: array - credentialName: - type: string - httpsRedirect: - type: boolean - maxProtocolVersion: - description: 'Optional: Maximum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - minProtocolVersion: - description: 'Optional: Minimum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - mode: - enum: - - PASSTHROUGH - - SIMPLE - - MUTUAL - - AUTO_PASSTHROUGH - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - serverCertificate: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - subjectAltNames: - items: - type: string - type: array - verifyCertificateHash: - items: - type: string - type: array - verifyCertificateSpki: - items: - type: string - type: array - type: object - type: object - type: array - outboundTrafficPolicy: - description: Configuration for the outbound traffic policy. - properties: - egressProxy: - properties: - host: - description: The name of a service from the service registry. - type: string - port: - description: Specifies the port on the host that is being - addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - mode: - enum: - - REGISTRY_ONLY - - ALLOW_ANY - type: string - type: object - workloadSelector: - properties: - labels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting network reachability of a sidecar. - See more details at: https://istio.io/docs/reference/config/networking/sidecar.html' - properties: - egress: - items: - properties: - bind: - type: string - captureMode: - enum: - - DEFAULT - - IPTABLES - - NONE - type: string - hosts: - items: - type: string - type: array - port: - description: The port associated with the listener. - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - type: object - type: array - ingress: - items: - properties: - bind: - description: The IP to which the listener should be bound. - type: string - captureMode: - enum: - - DEFAULT - - IPTABLES - - NONE - type: string - defaultEndpoint: - type: string - port: - description: The port associated with the listener. - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - tls: - properties: - caCertificates: - description: REQUIRED if mode is `MUTUAL`. - type: string - cipherSuites: - description: 'Optional: If specified, only support the specified - cipher list.' - items: - type: string - type: array - credentialName: - type: string - httpsRedirect: - type: boolean - maxProtocolVersion: - description: 'Optional: Maximum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - minProtocolVersion: - description: 'Optional: Minimum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - mode: - enum: - - PASSTHROUGH - - SIMPLE - - MUTUAL - - AUTO_PASSTHROUGH - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - serverCertificate: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - subjectAltNames: - items: - type: string - type: array - verifyCertificateHash: - items: - type: string - type: array - verifyCertificateSpki: - items: - type: string - type: array - type: object - type: object - type: array - outboundTrafficPolicy: - description: Configuration for the outbound traffic policy. - properties: - egressProxy: - properties: - host: - description: The name of a service from the service registry. - type: string - port: - description: Specifies the port on the host that is being - addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - mode: - enum: - - REGISTRY_ONLY - - ALLOW_ANY - type: string - type: object - workloadSelector: - properties: - labels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: virtualservices.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: VirtualService - listKind: VirtualServiceList - plural: virtualservices - shortNames: - - vs - singular: virtualservice - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The names of gateways and sidecars that should apply these routes - jsonPath: .spec.gateways - name: Gateways - type: string - - description: The destination hosts to which traffic is being sent - jsonPath: .spec.hosts - name: Hosts - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting label/content routing, sni routing, - etc. See more details at: https://istio.io/docs/reference/config/networking/virtual-service.html' - properties: - exportTo: - description: A list of namespaces to which this virtual service is - exported. - items: - type: string - type: array - gateways: - description: The names of gateways and sidecars that should apply - these routes. - items: - type: string - type: array - hosts: - description: The destination hosts to which traffic is being sent. - items: - type: string - type: array - http: - description: An ordered list of route rules for HTTP traffic. - items: - properties: - corsPolicy: - description: Cross-Origin Resource Sharing policy (CORS). - properties: - allowCredentials: - nullable: true - type: boolean - allowHeaders: - items: - type: string - type: array - allowMethods: - description: List of HTTP methods allowed to access the - resource. - items: - type: string - type: array - allowOrigin: - description: The list of origins that are allowed to perform - CORS requests. - items: - type: string - type: array - allowOrigins: - description: String patterns that match allowed origins. - items: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - type: array - exposeHeaders: - items: - type: string - type: array - maxAge: - type: string - type: object - delegate: - properties: - name: - description: Name specifies the name of the delegate VirtualService. - type: string - namespace: - description: Namespace specifies the namespace where the - delegate VirtualService resides. - type: string - type: object - fault: - description: Fault injection policy to apply on HTTP traffic - at the client side. - properties: - abort: - oneOf: - - not: - anyOf: - - required: - - httpStatus - - required: - - grpcStatus - - required: - - http2Error - - required: - - httpStatus - - required: - - grpcStatus - - required: - - http2Error - properties: - grpcStatus: - type: string - http2Error: - type: string - httpStatus: - description: HTTP status code to use to abort the Http - request. - format: int32 - type: integer - percentage: - description: Percentage of requests to be aborted with - the error code provided. - properties: - value: - format: double - type: number - type: object - type: object - delay: - oneOf: - - not: - anyOf: - - required: - - fixedDelay - - required: - - exponentialDelay - - required: - - fixedDelay - - required: - - exponentialDelay - properties: - exponentialDelay: - type: string - fixedDelay: - description: Add a fixed delay before forwarding the - request. - type: string - percent: - description: Percentage of requests on which the delay - will be injected (0-100). - format: int32 - type: integer - percentage: - description: Percentage of requests on which the delay - will be injected. - properties: - value: - format: double - type: number - type: object - type: object - type: object - headers: - properties: - request: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - response: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - type: object - match: - items: - properties: - authority: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - headers: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - type: object - ignoreUriCase: - description: Flag to specify whether the URI matching - should be case-insensitive. - type: boolean - method: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - name: - description: The name assigned to a match. - type: string - port: - description: Specifies the ports on the host that is being - addressed. - type: integer - queryParams: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - description: Query parameters for matching. - type: object - scheme: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - uri: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - withoutHeaders: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - description: withoutHeader has the same syntax with the - header, but has opposite meaning. - type: object - type: object - type: array - mirror: - properties: - host: - description: The name of a service from the service registry. - type: string - port: - description: Specifies the port on the host that is being - addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - mirror_percent: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - nullable: true - type: integer - mirrorPercent: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - nullable: true - type: integer - mirrorPercentage: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - properties: - value: - format: double - type: number - type: object - name: - description: The name assigned to the route for debugging purposes. - type: string - redirect: - description: A HTTP rule can either redirect or forward (default) - traffic. - oneOf: - - not: - anyOf: - - required: - - port - - required: - - derivePort - - required: - - port - - required: - - derivePort - properties: - authority: - type: string - derivePort: - enum: - - FROM_PROTOCOL_DEFAULT - - FROM_REQUEST_PORT - type: string - port: - description: On a redirect, overwrite the port portion of - the URL with this value. - type: integer - redirectCode: - type: integer - scheme: - description: On a redirect, overwrite the scheme portion - of the URL with this value. - type: string - uri: - type: string - type: object - retries: - description: Retry policy for HTTP requests. - properties: - attempts: - description: Number of retries to be allowed for a given - request. - format: int32 - type: integer - perTryTimeout: - description: Timeout per attempt for a given request, including - the initial call and any retries. - type: string - retryOn: - description: Specifies the conditions under which retry - takes place. - type: string - retryRemoteLocalities: - description: Flag to specify whether the retries should - retry to other localities. - nullable: true - type: boolean - type: object - rewrite: - description: Rewrite HTTP URIs and Authority headers. - properties: - authority: - description: rewrite the Authority/Host header with this - value. - type: string - uri: - type: string - type: object - route: - description: A HTTP rule can either redirect or forward (default) - traffic. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - headers: - properties: - request: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - response: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - timeout: - description: Timeout for HTTP requests, default is disabled. - type: string - type: object - type: array - tcp: - description: An ordered list of route rules for opaque TCP traffic. - items: - properties: - match: - items: - properties: - destinationSubnets: - description: IPv4 or IPv6 ip addresses of destination - with optional subnet. - items: - type: string - type: array - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - port: - description: Specifies the port on the host that is being - addressed. - type: integer - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - sourceSubnet: - description: IPv4 or IPv6 ip address of source with optional - subnet. - type: string - type: object - type: array - route: - description: The destination to which the connection should - be forwarded to. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - type: object - type: array - tls: - items: - properties: - match: - items: - properties: - destinationSubnets: - description: IPv4 or IPv6 ip addresses of destination - with optional subnet. - items: - type: string - type: array - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - port: - description: Specifies the port on the host that is being - addressed. - type: integer - sniHosts: - description: SNI (server name indicator) to match on. - items: - type: string - type: array - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - type: object - type: array - route: - description: The destination to which the connection should - be forwarded to. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - type: object - type: array - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - description: The names of gateways and sidecars that should apply these routes - jsonPath: .spec.gateways - name: Gateways - type: string - - description: The destination hosts to which traffic is being sent - jsonPath: .spec.hosts - name: Hosts - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting label/content routing, sni routing, - etc. See more details at: https://istio.io/docs/reference/config/networking/virtual-service.html' - properties: - exportTo: - description: A list of namespaces to which this virtual service is - exported. - items: - type: string - type: array - gateways: - description: The names of gateways and sidecars that should apply - these routes. - items: - type: string - type: array - hosts: - description: The destination hosts to which traffic is being sent. - items: - type: string - type: array - http: - description: An ordered list of route rules for HTTP traffic. - items: - properties: - corsPolicy: - description: Cross-Origin Resource Sharing policy (CORS). - properties: - allowCredentials: - nullable: true - type: boolean - allowHeaders: - items: - type: string - type: array - allowMethods: - description: List of HTTP methods allowed to access the - resource. - items: - type: string - type: array - allowOrigin: - description: The list of origins that are allowed to perform - CORS requests. - items: - type: string - type: array - allowOrigins: - description: String patterns that match allowed origins. - items: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - type: array - exposeHeaders: - items: - type: string - type: array - maxAge: - type: string - type: object - delegate: - properties: - name: - description: Name specifies the name of the delegate VirtualService. - type: string - namespace: - description: Namespace specifies the namespace where the - delegate VirtualService resides. - type: string - type: object - fault: - description: Fault injection policy to apply on HTTP traffic - at the client side. - properties: - abort: - oneOf: - - not: - anyOf: - - required: - - httpStatus - - required: - - grpcStatus - - required: - - http2Error - - required: - - httpStatus - - required: - - grpcStatus - - required: - - http2Error - properties: - grpcStatus: - type: string - http2Error: - type: string - httpStatus: - description: HTTP status code to use to abort the Http - request. - format: int32 - type: integer - percentage: - description: Percentage of requests to be aborted with - the error code provided. - properties: - value: - format: double - type: number - type: object - type: object - delay: - oneOf: - - not: - anyOf: - - required: - - fixedDelay - - required: - - exponentialDelay - - required: - - fixedDelay - - required: - - exponentialDelay - properties: - exponentialDelay: - type: string - fixedDelay: - description: Add a fixed delay before forwarding the - request. - type: string - percent: - description: Percentage of requests on which the delay - will be injected (0-100). - format: int32 - type: integer - percentage: - description: Percentage of requests on which the delay - will be injected. - properties: - value: - format: double - type: number - type: object - type: object - type: object - headers: - properties: - request: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - response: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - type: object - match: - items: - properties: - authority: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - headers: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - type: object - ignoreUriCase: - description: Flag to specify whether the URI matching - should be case-insensitive. - type: boolean - method: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - name: - description: The name assigned to a match. - type: string - port: - description: Specifies the ports on the host that is being - addressed. - type: integer - queryParams: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - description: Query parameters for matching. - type: object - scheme: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - uri: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - withoutHeaders: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - description: withoutHeader has the same syntax with the - header, but has opposite meaning. - type: object - type: object - type: array - mirror: - properties: - host: - description: The name of a service from the service registry. - type: string - port: - description: Specifies the port on the host that is being - addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - mirror_percent: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - nullable: true - type: integer - mirrorPercent: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - nullable: true - type: integer - mirrorPercentage: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - properties: - value: - format: double - type: number - type: object - name: - description: The name assigned to the route for debugging purposes. - type: string - redirect: - description: A HTTP rule can either redirect or forward (default) - traffic. - oneOf: - - not: - anyOf: - - required: - - port - - required: - - derivePort - - required: - - port - - required: - - derivePort - properties: - authority: - type: string - derivePort: - enum: - - FROM_PROTOCOL_DEFAULT - - FROM_REQUEST_PORT - type: string - port: - description: On a redirect, overwrite the port portion of - the URL with this value. - type: integer - redirectCode: - type: integer - scheme: - description: On a redirect, overwrite the scheme portion - of the URL with this value. - type: string - uri: - type: string - type: object - retries: - description: Retry policy for HTTP requests. - properties: - attempts: - description: Number of retries to be allowed for a given - request. - format: int32 - type: integer - perTryTimeout: - description: Timeout per attempt for a given request, including - the initial call and any retries. - type: string - retryOn: - description: Specifies the conditions under which retry - takes place. - type: string - retryRemoteLocalities: - description: Flag to specify whether the retries should - retry to other localities. - nullable: true - type: boolean - type: object - rewrite: - description: Rewrite HTTP URIs and Authority headers. - properties: - authority: - description: rewrite the Authority/Host header with this - value. - type: string - uri: - type: string - type: object - route: - description: A HTTP rule can either redirect or forward (default) - traffic. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - headers: - properties: - request: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - response: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - timeout: - description: Timeout for HTTP requests, default is disabled. - type: string - type: object - type: array - tcp: - description: An ordered list of route rules for opaque TCP traffic. - items: - properties: - match: - items: - properties: - destinationSubnets: - description: IPv4 or IPv6 ip addresses of destination - with optional subnet. - items: - type: string - type: array - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - port: - description: Specifies the port on the host that is being - addressed. - type: integer - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - sourceSubnet: - description: IPv4 or IPv6 ip address of source with optional - subnet. - type: string - type: object - type: array - route: - description: The destination to which the connection should - be forwarded to. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - type: object - type: array - tls: - items: - properties: - match: - items: - properties: - destinationSubnets: - description: IPv4 or IPv6 ip addresses of destination - with optional subnet. - items: - type: string - type: array - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - port: - description: Specifies the port on the host that is being - addressed. - type: integer - sniHosts: - description: SNI (server name indicator) to match on. - items: - type: string - type: array - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - type: object - type: array - route: - description: The destination to which the connection should - be forwarded to. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - type: object - type: array - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: workloadentries.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: WorkloadEntry - listKind: WorkloadEntryList - plural: workloadentries - shortNames: - - we - singular: workloadentry - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - description: Address associated with the network endpoint. - jsonPath: .spec.address - name: Address - type: string - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting VMs onboarded into the mesh. See - more details at: https://istio.io/docs/reference/config/networking/workload-entry.html' - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - description: Address associated with the network endpoint. - jsonPath: .spec.address - name: Address - type: string - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting VMs onboarded into the mesh. See - more details at: https://istio.io/docs/reference/config/networking/workload-entry.html' - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: workloadgroups.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: WorkloadGroup - listKind: WorkloadGroupList - plural: workloadgroups - shortNames: - - wg - singular: workloadgroup - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Describes a collection of workload instances. See more details - at: https://istio.io/docs/reference/config/networking/workload-group.html' - properties: - metadata: - description: Metadata that will be used for all corresponding `WorkloadEntries`. - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - probe: - description: '`ReadinessProbe` describes the configuration the user - must provide for healthchecking on their workload.' - oneOf: - - not: - anyOf: - - required: - - httpGet - - required: - - tcpSocket - - required: - - exec - - required: - - httpGet - - required: - - tcpSocket - - required: - - exec - properties: - exec: - description: Health is determined by how the command that is executed - exited. - properties: - command: - description: Command to run. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for the probe to be - considered failed after having succeeded. - format: int32 - type: integer - httpGet: - properties: - host: - description: Host name to connect to, defaults to the pod - IP. - type: string - httpHeaders: - description: Headers the proxy will pass on to make the request. - items: - properties: - name: - type: string - value: - type: string - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - description: Port on which the endpoint lives. - type: integer - scheme: - type: string - type: object - initialDelaySeconds: - description: Number of seconds after the container has started - before readiness probes are initiated. - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform the probe. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for the probe to be - considered successful after having failed. - format: int32 - type: integer - tcpSocket: - description: Health is determined by if the proxy is able to connect. - properties: - host: - type: string - port: - type: integer - type: object - timeoutSeconds: - description: Number of seconds after which the probe times out. - format: int32 - type: integer - type: object - template: - description: Template to be used for the generation of `WorkloadEntry` - resources that belong to this `WorkloadGroup`. - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - properties: - metadata: - description: Metadata that will be used for all corresponding `WorkloadEntries`. - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - probe: - description: '`ReadinessProbe` describes the configuration the user - must provide for healthchecking on their workload.' - oneOf: - - not: - anyOf: - - required: - - httpGet - - required: - - tcpSocket - - required: - - exec - - required: - - httpGet - - required: - - tcpSocket - - required: - - exec - properties: - exec: - description: Health is determined by how the command that is executed - exited. - properties: - command: - description: Command to run. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for the probe to be - considered failed after having succeeded. - format: int32 - type: integer - httpGet: - properties: - host: - description: Host name to connect to, defaults to the pod - IP. - type: string - httpHeaders: - description: Headers the proxy will pass on to make the request. - items: - properties: - name: - type: string - value: - type: string - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - description: Port on which the endpoint lives. - type: integer - scheme: - type: string - type: object - initialDelaySeconds: - description: Number of seconds after the container has started - before readiness probes are initiated. - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform the probe. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for the probe to be - considered successful after having failed. - format: int32 - type: integer - tcpSocket: - description: Health is determined by if the proxy is able to connect. - properties: - host: - type: string - port: - type: integer - type: object - timeoutSeconds: - description: Number of seconds after which the probe times out. - format: int32 - type: integer - type: object - template: - description: Template to be used for the generation of `WorkloadEntry` - resources that belong to this `WorkloadGroup`. - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - istio: security - release: istio - name: authorizationpolicies.security.istio.io -spec: - group: security.istio.io - names: - categories: - - istio-io - - security-istio-io - kind: AuthorizationPolicy - listKind: AuthorizationPolicyList - plural: authorizationpolicies - singular: authorizationpolicy - scope: Namespaced - versions: - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration for access control on workloads. See more - details at: https://istio.io/docs/reference/config/security/authorization-policy.html' - oneOf: - - not: - anyOf: - - required: - - provider - - required: - - provider - properties: - action: - description: Optional. - enum: - - ALLOW - - DENY - - AUDIT - - CUSTOM - type: string - provider: - description: Specifies detailed configuration of the CUSTOM action. - properties: - name: - description: Specifies the name of the extension provider. - type: string - type: object - rules: - description: Optional. - items: - properties: - from: - description: Optional. - items: - properties: - source: - description: Source specifies the source of a request. - properties: - ipBlocks: - description: Optional. - items: - type: string - type: array - namespaces: - description: Optional. - items: - type: string - type: array - notIpBlocks: - description: Optional. - items: - type: string - type: array - notNamespaces: - description: Optional. - items: - type: string - type: array - notPrincipals: - description: Optional. - items: - type: string - type: array - notRemoteIpBlocks: - description: Optional. - items: - type: string - type: array - notRequestPrincipals: - description: Optional. - items: - type: string - type: array - principals: - description: Optional. - items: - type: string - type: array - remoteIpBlocks: - description: Optional. - items: - type: string - type: array - requestPrincipals: - description: Optional. - items: - type: string - type: array - type: object - type: object - type: array - to: - description: Optional. - items: - properties: - operation: - description: Operation specifies the operation of a request. - properties: - hosts: - description: Optional. - items: - type: string - type: array - methods: - description: Optional. - items: - type: string - type: array - notHosts: - description: Optional. - items: - type: string - type: array - notMethods: - description: Optional. - items: - type: string - type: array - notPaths: - description: Optional. - items: - type: string - type: array - notPorts: - description: Optional. - items: - type: string - type: array - paths: - description: Optional. - items: - type: string - type: array - ports: - description: Optional. - items: - type: string - type: array - type: object - type: object - type: array - when: - description: Optional. - items: - properties: - key: - description: The name of an Istio attribute. - type: string - notValues: - description: Optional. - items: - type: string - type: array - values: - description: Optional. - items: - type: string - type: array - type: object - type: array - type: object - type: array - selector: - description: Optional. - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - istio: security - release: istio - name: peerauthentications.security.istio.io -spec: - group: security.istio.io - names: - categories: - - istio-io - - security-istio-io - kind: PeerAuthentication - listKind: PeerAuthenticationList - plural: peerauthentications - shortNames: - - pa - singular: peerauthentication - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Defines the mTLS mode used for peer authentication. - jsonPath: .spec.mtls.mode - name: Mode - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: PeerAuthentication defines how traffic will be tunneled (or - not) to the sidecar. - properties: - mtls: - description: Mutual TLS settings for workload. - properties: - mode: - description: Defines the mTLS mode used for peer authentication. - enum: - - UNSET - - DISABLE - - PERMISSIVE - - STRICT - type: string - type: object - portLevelMtls: - additionalProperties: - properties: - mode: - description: Defines the mTLS mode used for peer authentication. - enum: - - UNSET - - DISABLE - - PERMISSIVE - - STRICT - type: string - type: object - description: Port specific mutual TLS settings. - type: object - selector: - description: The selector determines the workloads to apply the ChannelAuthentication - on. - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - istio: security - release: istio - name: requestauthentications.security.istio.io -spec: - group: security.istio.io - names: - categories: - - istio-io - - security-istio-io - kind: RequestAuthentication - listKind: RequestAuthenticationList - plural: requestauthentications - shortNames: - - ra - singular: requestauthentication - scope: Namespaced - versions: - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: RequestAuthentication defines what request authentication - methods are supported by a workload. - properties: - jwtRules: - description: Define the list of JWTs that can be validated at the - selected workloads' proxy. - items: - properties: - audiences: - items: - type: string - type: array - forwardOriginalToken: - description: If set to true, the original token will be kept - for the upstream request. - type: boolean - fromHeaders: - description: List of header locations from which JWT is expected. - items: - properties: - name: - description: The HTTP header name. - type: string - prefix: - description: The prefix that should be stripped before - decoding the token. - type: string - type: object - type: array - fromParams: - description: List of query parameters from which JWT is expected. - items: - type: string - type: array - issuer: - description: Identifies the issuer that issued the JWT. - type: string - jwks: - description: JSON Web Key Set of public keys to validate signature - of the JWT. - type: string - jwks_uri: - type: string - jwksUri: - type: string - outputPayloadToHeader: - type: string - type: object - type: array - selector: - description: Optional. - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - istio: telemetry - release: istio - name: telemetries.telemetry.istio.io -spec: - group: telemetry.istio.io - names: - categories: - - istio-io - - telemetry-istio-io - kind: Telemetry - listKind: TelemetryList - plural: telemetries - shortNames: - - telemetry - singular: telemetry - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Telemetry configuration for workloads. See more details - at: https://istio.io/docs/reference/config/telemetry.html' - properties: - accessLogging: - description: Optional. - items: - properties: - disabled: - description: Controls logging. - nullable: true - type: boolean - filter: - description: Optional. - properties: - expression: - description: CEL expression for selecting when requests/connections - should be logged. - type: string - type: object - match: - description: Allows tailoring of logging behavior to specific - conditions. - properties: - mode: - enum: - - CLIENT_AND_SERVER - - CLIENT - - SERVER - type: string - type: object - providers: - description: Optional. - items: - properties: - name: - description: Required. - type: string - type: object - type: array - type: object - type: array - metrics: - description: Optional. - items: - properties: - overrides: - description: Optional. - items: - properties: - disabled: - description: Optional. - nullable: true - type: boolean - match: - description: Match allows provides the scope of the override. - oneOf: - - not: - anyOf: - - required: - - metric - - required: - - customMetric - - required: - - metric - - required: - - customMetric - properties: - customMetric: - description: Allows free-form specification of a metric. - type: string - metric: - description: One of the well-known Istio Standard - Metrics. - enum: - - ALL_METRICS - - REQUEST_COUNT - - REQUEST_DURATION - - REQUEST_SIZE - - RESPONSE_SIZE - - TCP_OPENED_CONNECTIONS - - TCP_CLOSED_CONNECTIONS - - TCP_SENT_BYTES - - TCP_RECEIVED_BYTES - - GRPC_REQUEST_MESSAGES - - GRPC_RESPONSE_MESSAGES - type: string - mode: - enum: - - CLIENT_AND_SERVER - - CLIENT - - SERVER - type: string - type: object - tagOverrides: - additionalProperties: - properties: - operation: - description: Operation controls whether or not to - update/add a tag, or to remove it. - enum: - - UPSERT - - REMOVE - type: string - value: - description: Value is only considered if the operation - is `UPSERT`. - type: string - type: object - description: Optional. - type: object - type: object - type: array - providers: - description: Optional. - items: - properties: - name: - description: Required. - type: string - type: object - type: array - type: object - type: array - selector: - description: Optional. - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - tracing: - description: Optional. - items: - properties: - customTags: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - literal - - required: - - environment - - required: - - header - - required: - - literal - - required: - - environment - - required: - - header - properties: - environment: - description: Environment adds the value of an environment - variable to each span. - properties: - defaultValue: - description: Optional. - type: string - name: - description: Name of the environment variable from - which to extract the tag value. - type: string - type: object - header: - properties: - defaultValue: - description: Optional. - type: string - name: - description: Name of the header from which to extract - the tag value. - type: string - type: object - literal: - description: Literal adds the same, hard-coded value to - each span. - properties: - value: - description: The tag value to use. - type: string - type: object - type: object - description: Optional. - type: object - disableSpanReporting: - description: Controls span reporting. - nullable: true - type: boolean - match: - description: Allows tailoring of behavior to specific conditions. - properties: - mode: - enum: - - CLIENT_AND_SERVER - - CLIENT - - SERVER - type: string - type: object - providers: - description: Optional. - items: - properties: - name: - description: Required. - type: string - type: object - type: array - randomSamplingPercentage: - nullable: true - type: number - useRequestIdForTraceSampling: - nullable: true - type: boolean - type: object - type: array - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- diff --git a/manifests/charts/base/crds/crd-operator.yaml b/manifests/charts/base/crds/crd-operator.yaml deleted file mode 100644 index 2a80f4186..000000000 --- a/manifests/charts/base/crds/crd-operator.yaml +++ /dev/null @@ -1,48 +0,0 @@ -# SYNC WITH manifests/charts/istio-operator/templates -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: istiooperators.install.istio.io - labels: - release: istio -spec: - conversion: - strategy: None - group: install.istio.io - names: - kind: IstioOperator - listKind: IstioOperatorList - plural: istiooperators - singular: istiooperator - shortNames: - - iop - - io - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Istio control plane revision - jsonPath: .spec.revision - name: Revision - type: string - - description: IOP current state - jsonPath: .status.status - name: Status - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - subresources: - status: {} - name: v1alpha1 - schema: - openAPIV3Schema: - type: object - x-kubernetes-preserve-unknown-fields: true - served: true - storage: true ---- diff --git a/manifests/charts/base/files/gen-istio-cluster.yaml b/manifests/charts/base/files/gen-istio-cluster.yaml deleted file mode 100644 index 33ae88a15..000000000 --- a/manifests/charts/base/files/gen-istio-cluster.yaml +++ /dev/null @@ -1,6934 +0,0 @@ ---- -# Source: crds/crd-all.gen.yaml -# DO NOT EDIT - Generated by Cue OpenAPI generator based on Istio APIs. -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: servicemetadatas.extensions.istio.io -spec: - group: extensions.istio.io - names: - categories: - - istio-io - - extensions-istio-io - kind: ServiceMetadata - listKind: ServiceMetadataList - plural: servicemetadatas - shortNames: - - sm - singular: servicemetadata - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'it''s the spec of Service Metadata See more details at:' - properties: - applicationName: - type: string - metadataInfo: - type: string - revision: - type: string - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: servicenamemappings.extensions.istio.io -spec: - group: extensions.istio.io - names: - categories: - - istio-io - - extensions-istio-io - kind: ServiceNameMapping - listKind: ServiceNameMappingList - plural: servicenamemappings - shortNames: - - snp - singular: servicenamemapping - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'it''s the spec of Service Name Mapping, which is used to - map the interface name See more details at:' - properties: - applicationNames: - items: - type: string - type: array - interfaceName: - description: InterfaceName. - type: string - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: wasmplugins.extensions.istio.io -spec: - group: extensions.istio.io - names: - categories: - - istio-io - - extensions-istio-io - kind: WasmPlugin - listKind: WasmPluginList - plural: wasmplugins - singular: wasmplugin - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Extend the functionality provided by the Istio proxy through - WebAssembly filters. See more details at: https://istio.io/docs/reference/config/proxy_extensions/wasm-plugin.html' - properties: - imagePullPolicy: - description: The pull behaviour to be applied when fetching an OCI - image. - enum: - - UNSPECIFIED_POLICY - - IfNotPresent - - Always - type: string - imagePullSecret: - description: Credentials to use for OCI image pulling. - type: string - phase: - description: Determines where in the filter chain this `WasmPlugin` - is to be injected. - enum: - - UNSPECIFIED_PHASE - - AUTHN - - AUTHZ - - STATS - type: string - pluginConfig: - description: The configuration that will be passed on to the plugin. - type: object - x-kubernetes-preserve-unknown-fields: true - pluginName: - type: string - priority: - description: Determines ordering of `WasmPlugins` in the same `phase`. - nullable: true - type: integer - selector: - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - sha256: - description: SHA256 checksum that will be used to verify Wasm module - or OCI container. - type: string - url: - description: URL of a Wasm module or OCI container. - type: string - verificationKey: - type: string - vmConfig: - description: Configuration for a Wasm VM. - properties: - env: - description: Specifies environment variables to be injected to - this VM. - items: - properties: - name: - type: string - value: - description: Value for the environment variable. - type: string - valueFrom: - enum: - - INLINE - - HOST - type: string - type: object - type: array - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: destinationrules.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: DestinationRule - listKind: DestinationRuleList - plural: destinationrules - shortNames: - - dr - singular: destinationrule - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The name of a service from the service registry - jsonPath: .spec.host - name: Host - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting load balancing, outlier detection, - etc. See more details at: https://istio.io/docs/reference/config/networking/destination-rule.html' - properties: - exportTo: - description: A list of namespaces to which this destination rule is - exported. - items: - type: string - type: array - host: - description: The name of a service from the service registry. - type: string - subsets: - items: - properties: - labels: - additionalProperties: - type: string - type: object - name: - description: Name of the subset. - type: string - trafficPolicy: - description: Traffic policies that apply to this subset. - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should - be upgraded to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests - to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will - be preserved while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the - socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query - parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to - traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this - is DestinationRule-level and will override mesh - wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list - of labels used to sort endpoints to do priority - based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local - origin failures from external errors. - type: boolean - type: object - portLevelSettings: - description: Traffic policies specific to individual ports. - items: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection - should be upgraded to http2 for the associated - destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP - requests to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to - a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream - connection pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per - connection to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol - will be preserved while initiating connection - to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and - TCP upstream connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP - connections to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE - on the socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between - keep-alive probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer - algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP - header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP - query parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' - separated, e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities - to traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, - this is DestinationRule-level and will override - mesh wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered - list of labels used to sort endpoints to - do priority based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of - Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a - host is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep - analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish - local origin failures from external errors. - type: boolean - type: object - port: - properties: - number: - type: integer - type: object - tls: - description: TLS related settings for connections - to the upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server - during TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - type: object - type: array - tls: - description: TLS related settings for connections to the - upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during - TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - tunnel: - properties: - protocol: - description: Specifies which protocol to use for tunneling - the downstream connection. - type: string - targetHost: - description: Specifies a host to which the downstream - connection is tunneled. - type: string - targetPort: - description: Specifies a port to which the downstream - connection is tunneled. - type: integer - type: object - type: object - type: object - type: array - trafficPolicy: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should be upgraded - to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests to - a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will be preserved - while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the socket - to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, failover - or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to traffic - distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this is DestinationRule-level - and will override mesh wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, failover - or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list of labels - used to sort endpoints to do priority based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local origin - failures from external errors. - type: boolean - type: object - portLevelSettings: - description: Traffic policies specific to individual ports. - items: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should - be upgraded to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests - to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will - be preserved while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the - socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query - parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to - traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this - is DestinationRule-level and will override mesh - wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list - of labels used to sort endpoints to do priority - based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local - origin failures from external errors. - type: boolean - type: object - port: - properties: - number: - type: integer - type: object - tls: - description: TLS related settings for connections to the - upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during - TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - type: object - type: array - tls: - description: TLS related settings for connections to the upstream - service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during TLS - handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - tunnel: - properties: - protocol: - description: Specifies which protocol to use for tunneling - the downstream connection. - type: string - targetHost: - description: Specifies a host to which the downstream connection - is tunneled. - type: string - targetPort: - description: Specifies a port to which the downstream connection - is tunneled. - type: integer - type: object - type: object - workloadSelector: - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - description: The name of a service from the service registry - jsonPath: .spec.host - name: Host - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting load balancing, outlier detection, - etc. See more details at: https://istio.io/docs/reference/config/networking/destination-rule.html' - properties: - exportTo: - description: A list of namespaces to which this destination rule is - exported. - items: - type: string - type: array - host: - description: The name of a service from the service registry. - type: string - subsets: - items: - properties: - labels: - additionalProperties: - type: string - type: object - name: - description: Name of the subset. - type: string - trafficPolicy: - description: Traffic policies that apply to this subset. - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should - be upgraded to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests - to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will - be preserved while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the - socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query - parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to - traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this - is DestinationRule-level and will override mesh - wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list - of labels used to sort endpoints to do priority - based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local - origin failures from external errors. - type: boolean - type: object - portLevelSettings: - description: Traffic policies specific to individual ports. - items: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection - should be upgraded to http2 for the associated - destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP - requests to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to - a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream - connection pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per - connection to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol - will be preserved while initiating connection - to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and - TCP upstream connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP - connections to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE - on the socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between - keep-alive probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer - algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP - header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP - query parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' - separated, e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities - to traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, - this is DestinationRule-level and will override - mesh wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered - list of labels used to sort endpoints to - do priority based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of - Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a - host is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep - analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish - local origin failures from external errors. - type: boolean - type: object - port: - properties: - number: - type: integer - type: object - tls: - description: TLS related settings for connections - to the upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server - during TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - type: object - type: array - tls: - description: TLS related settings for connections to the - upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during - TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - tunnel: - properties: - protocol: - description: Specifies which protocol to use for tunneling - the downstream connection. - type: string - targetHost: - description: Specifies a host to which the downstream - connection is tunneled. - type: string - targetPort: - description: Specifies a port to which the downstream - connection is tunneled. - type: integer - type: object - type: object - type: object - type: array - trafficPolicy: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should be upgraded - to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests to - a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will be preserved - while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the socket - to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, failover - or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to traffic - distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this is DestinationRule-level - and will override mesh wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, failover - or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list of labels - used to sort endpoints to do priority based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local origin - failures from external errors. - type: boolean - type: object - portLevelSettings: - description: Traffic policies specific to individual ports. - items: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should - be upgraded to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests - to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will - be preserved while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the - socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query - parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to - traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this - is DestinationRule-level and will override mesh - wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list - of labels used to sort endpoints to do priority - based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local - origin failures from external errors. - type: boolean - type: object - port: - properties: - number: - type: integer - type: object - tls: - description: TLS related settings for connections to the - upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during - TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - type: object - type: array - tls: - description: TLS related settings for connections to the upstream - service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during TLS - handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - tunnel: - properties: - protocol: - description: Specifies which protocol to use for tunneling - the downstream connection. - type: string - targetHost: - description: Specifies a host to which the downstream connection - is tunneled. - type: string - targetPort: - description: Specifies a port to which the downstream connection - is tunneled. - type: integer - type: object - type: object - workloadSelector: - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: envoyfilters.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: EnvoyFilter - listKind: EnvoyFilterList - plural: envoyfilters - singular: envoyfilter - scope: Namespaced - versions: - - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Customizing Envoy configuration generated by Istio. See - more details at: https://istio.io/docs/reference/config/networking/envoy-filter.html' - properties: - configPatches: - description: One or more patches with match conditions. - items: - properties: - applyTo: - enum: - - INVALID - - LISTENER - - FILTER_CHAIN - - NETWORK_FILTER - - HTTP_FILTER - - ROUTE_CONFIGURATION - - VIRTUAL_HOST - - HTTP_ROUTE - - CLUSTER - - EXTENSION_CONFIG - - BOOTSTRAP - type: string - match: - description: Match on listener/route configuration/cluster. - oneOf: - - not: - anyOf: - - required: - - listener - - required: - - routeConfiguration - - required: - - cluster - - required: - - listener - - required: - - routeConfiguration - - required: - - cluster - properties: - cluster: - description: Match on envoy cluster attributes. - properties: - name: - description: The exact name of the cluster to match. - type: string - portNumber: - description: The service port for which this cluster - was generated. - type: integer - service: - description: The fully qualified service name for this - cluster. - type: string - subset: - description: The subset associated with the service. - type: string - type: object - context: - description: The specific config generation context to match - on. - enum: - - ANY - - SIDECAR_INBOUND - - SIDECAR_OUTBOUND - - GATEWAY - type: string - listener: - description: Match on envoy listener attributes. - properties: - filterChain: - description: Match a specific filter chain in a listener. - properties: - applicationProtocols: - description: Applies only to sidecars. - type: string - destinationPort: - description: The destination_port value used by - a filter chain's match condition. - type: integer - filter: - description: The name of a specific filter to apply - the patch to. - properties: - name: - description: The filter name to match on. - type: string - subFilter: - properties: - name: - description: The filter name to match on. - type: string - type: object - type: object - name: - description: The name assigned to the filter chain. - type: string - sni: - description: The SNI value used by a filter chain's - match condition. - type: string - transportProtocol: - description: Applies only to `SIDECAR_INBOUND` context. - type: string - type: object - name: - description: Match a specific listener by its name. - type: string - portName: - type: string - portNumber: - type: integer - type: object - proxy: - description: Match on properties associated with a proxy. - properties: - metadata: - additionalProperties: - type: string - type: object - proxyVersion: - type: string - type: object - routeConfiguration: - description: Match on envoy HTTP route configuration attributes. - properties: - gateway: - type: string - name: - description: Route configuration name to match on. - type: string - portName: - description: Applicable only for GATEWAY context. - type: string - portNumber: - type: integer - vhost: - properties: - name: - type: string - route: - description: Match a specific route within the virtual - host. - properties: - action: - description: Match a route with specific action - type. - enum: - - ANY - - ROUTE - - REDIRECT - - DIRECT_RESPONSE - type: string - name: - type: string - type: object - type: object - type: object - type: object - patch: - description: The patch to apply along with the operation. - properties: - filterClass: - description: Determines the filter insertion order. - enum: - - UNSPECIFIED - - AUTHN - - AUTHZ - - STATS - type: string - operation: - description: Determines how the patch should be applied. - enum: - - INVALID - - MERGE - - ADD - - REMOVE - - INSERT_BEFORE - - INSERT_AFTER - - INSERT_FIRST - - REPLACE - type: string - value: - description: The JSON config of the object being patched. - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - type: object - type: array - priority: - description: Priority defines the order in which patch sets are applied - within a context. - format: int32 - type: integer - workloadSelector: - properties: - labels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: gateways.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: Gateway - listKind: GatewayList - plural: gateways - shortNames: - - gw - singular: gateway - scope: Namespaced - versions: - - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting edge load balancer. See more details - at: https://istio.io/docs/reference/config/networking/gateway.html' - properties: - selector: - additionalProperties: - type: string - type: object - servers: - description: A list of server specifications. - items: - properties: - bind: - type: string - defaultEndpoint: - type: string - hosts: - description: One or more hosts exposed by this gateway. - items: - type: string - type: array - name: - description: An optional name of the server, when set must be - unique across all servers. - type: string - port: - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - tls: - description: Set of TLS related options that govern the server's - behavior. - properties: - caCertificates: - description: REQUIRED if mode is `MUTUAL`. - type: string - cipherSuites: - description: 'Optional: If specified, only support the specified - cipher list.' - items: - type: string - type: array - credentialName: - type: string - httpsRedirect: - type: boolean - maxProtocolVersion: - description: 'Optional: Maximum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - minProtocolVersion: - description: 'Optional: Minimum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - mode: - enum: - - PASSTHROUGH - - SIMPLE - - MUTUAL - - AUTO_PASSTHROUGH - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - serverCertificate: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - subjectAltNames: - items: - type: string - type: array - verifyCertificateHash: - items: - type: string - type: array - verifyCertificateSpki: - items: - type: string - type: array - type: object - type: object - type: array - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting edge load balancer. See more details - at: https://istio.io/docs/reference/config/networking/gateway.html' - properties: - selector: - additionalProperties: - type: string - type: object - servers: - description: A list of server specifications. - items: - properties: - bind: - type: string - defaultEndpoint: - type: string - hosts: - description: One or more hosts exposed by this gateway. - items: - type: string - type: array - name: - description: An optional name of the server, when set must be - unique across all servers. - type: string - port: - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - tls: - description: Set of TLS related options that govern the server's - behavior. - properties: - caCertificates: - description: REQUIRED if mode is `MUTUAL`. - type: string - cipherSuites: - description: 'Optional: If specified, only support the specified - cipher list.' - items: - type: string - type: array - credentialName: - type: string - httpsRedirect: - type: boolean - maxProtocolVersion: - description: 'Optional: Maximum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - minProtocolVersion: - description: 'Optional: Minimum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - mode: - enum: - - PASSTHROUGH - - SIMPLE - - MUTUAL - - AUTO_PASSTHROUGH - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - serverCertificate: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - subjectAltNames: - items: - type: string - type: array - verifyCertificateHash: - items: - type: string - type: array - verifyCertificateSpki: - items: - type: string - type: array - type: object - type: object - type: array - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: proxyconfigs.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: ProxyConfig - listKind: ProxyConfigList - plural: proxyconfigs - singular: proxyconfig - scope: Namespaced - versions: - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Provides configuration for individual workloads. See more - details at: https://istio.io/docs/reference/config/networking/proxy-config.html' - properties: - concurrency: - description: The number of worker threads to run. - nullable: true - type: integer - environmentVariables: - additionalProperties: - type: string - description: Additional environment variables for the proxy. - type: object - image: - description: Specifies the details of the proxy image. - properties: - imageType: - description: The image type of the image. - type: string - type: object - selector: - description: Optional. - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: serviceentries.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: ServiceEntry - listKind: ServiceEntryList - plural: serviceentries - shortNames: - - se - singular: serviceentry - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The hosts associated with the ServiceEntry - jsonPath: .spec.hosts - name: Hosts - type: string - - description: Whether the service is external to the mesh or part of the mesh - (MESH_EXTERNAL or MESH_INTERNAL) - jsonPath: .spec.location - name: Location - type: string - - description: Service discovery mode for the hosts (NONE, STATIC, or DNS) - jsonPath: .spec.resolution - name: Resolution - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting service registry. See more details - at: https://istio.io/docs/reference/config/networking/service-entry.html' - properties: - addresses: - description: The virtual IP addresses associated with the service. - items: - type: string - type: array - endpoints: - description: One or more endpoints associated with the service. - items: - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - type: array - exportTo: - description: A list of namespaces to which this service is exported. - items: - type: string - type: array - hosts: - description: The hosts associated with the ServiceEntry. - items: - type: string - type: array - location: - enum: - - MESH_EXTERNAL - - MESH_INTERNAL - type: string - ports: - description: The ports associated with the external service. - items: - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - type: array - resolution: - description: Service discovery mode for the hosts. - enum: - - NONE - - STATIC - - DNS - - DNS_ROUND_ROBIN - type: string - subjectAltNames: - items: - type: string - type: array - workloadSelector: - description: Applicable only for MESH_INTERNAL services. - properties: - labels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - description: The hosts associated with the ServiceEntry - jsonPath: .spec.hosts - name: Hosts - type: string - - description: Whether the service is external to the mesh or part of the mesh - (MESH_EXTERNAL or MESH_INTERNAL) - jsonPath: .spec.location - name: Location - type: string - - description: Service discovery mode for the hosts (NONE, STATIC, or DNS) - jsonPath: .spec.resolution - name: Resolution - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting service registry. See more details - at: https://istio.io/docs/reference/config/networking/service-entry.html' - properties: - addresses: - description: The virtual IP addresses associated with the service. - items: - type: string - type: array - endpoints: - description: One or more endpoints associated with the service. - items: - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - type: array - exportTo: - description: A list of namespaces to which this service is exported. - items: - type: string - type: array - hosts: - description: The hosts associated with the ServiceEntry. - items: - type: string - type: array - location: - enum: - - MESH_EXTERNAL - - MESH_INTERNAL - type: string - ports: - description: The ports associated with the external service. - items: - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - type: array - resolution: - description: Service discovery mode for the hosts. - enum: - - NONE - - STATIC - - DNS - - DNS_ROUND_ROBIN - type: string - subjectAltNames: - items: - type: string - type: array - workloadSelector: - description: Applicable only for MESH_INTERNAL services. - properties: - labels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: sidecars.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: Sidecar - listKind: SidecarList - plural: sidecars - singular: sidecar - scope: Namespaced - versions: - - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting network reachability of a sidecar. - See more details at: https://istio.io/docs/reference/config/networking/sidecar.html' - properties: - egress: - items: - properties: - bind: - type: string - captureMode: - enum: - - DEFAULT - - IPTABLES - - NONE - type: string - hosts: - items: - type: string - type: array - port: - description: The port associated with the listener. - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - type: object - type: array - ingress: - items: - properties: - bind: - description: The IP to which the listener should be bound. - type: string - captureMode: - enum: - - DEFAULT - - IPTABLES - - NONE - type: string - defaultEndpoint: - type: string - port: - description: The port associated with the listener. - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - tls: - properties: - caCertificates: - description: REQUIRED if mode is `MUTUAL`. - type: string - cipherSuites: - description: 'Optional: If specified, only support the specified - cipher list.' - items: - type: string - type: array - credentialName: - type: string - httpsRedirect: - type: boolean - maxProtocolVersion: - description: 'Optional: Maximum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - minProtocolVersion: - description: 'Optional: Minimum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - mode: - enum: - - PASSTHROUGH - - SIMPLE - - MUTUAL - - AUTO_PASSTHROUGH - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - serverCertificate: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - subjectAltNames: - items: - type: string - type: array - verifyCertificateHash: - items: - type: string - type: array - verifyCertificateSpki: - items: - type: string - type: array - type: object - type: object - type: array - outboundTrafficPolicy: - description: Configuration for the outbound traffic policy. - properties: - egressProxy: - properties: - host: - description: The name of a service from the service registry. - type: string - port: - description: Specifies the port on the host that is being - addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - mode: - enum: - - REGISTRY_ONLY - - ALLOW_ANY - type: string - type: object - workloadSelector: - properties: - labels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting network reachability of a sidecar. - See more details at: https://istio.io/docs/reference/config/networking/sidecar.html' - properties: - egress: - items: - properties: - bind: - type: string - captureMode: - enum: - - DEFAULT - - IPTABLES - - NONE - type: string - hosts: - items: - type: string - type: array - port: - description: The port associated with the listener. - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - type: object - type: array - ingress: - items: - properties: - bind: - description: The IP to which the listener should be bound. - type: string - captureMode: - enum: - - DEFAULT - - IPTABLES - - NONE - type: string - defaultEndpoint: - type: string - port: - description: The port associated with the listener. - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - tls: - properties: - caCertificates: - description: REQUIRED if mode is `MUTUAL`. - type: string - cipherSuites: - description: 'Optional: If specified, only support the specified - cipher list.' - items: - type: string - type: array - credentialName: - type: string - httpsRedirect: - type: boolean - maxProtocolVersion: - description: 'Optional: Maximum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - minProtocolVersion: - description: 'Optional: Minimum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - mode: - enum: - - PASSTHROUGH - - SIMPLE - - MUTUAL - - AUTO_PASSTHROUGH - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - serverCertificate: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - subjectAltNames: - items: - type: string - type: array - verifyCertificateHash: - items: - type: string - type: array - verifyCertificateSpki: - items: - type: string - type: array - type: object - type: object - type: array - outboundTrafficPolicy: - description: Configuration for the outbound traffic policy. - properties: - egressProxy: - properties: - host: - description: The name of a service from the service registry. - type: string - port: - description: Specifies the port on the host that is being - addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - mode: - enum: - - REGISTRY_ONLY - - ALLOW_ANY - type: string - type: object - workloadSelector: - properties: - labels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: virtualservices.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: VirtualService - listKind: VirtualServiceList - plural: virtualservices - shortNames: - - vs - singular: virtualservice - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The names of gateways and sidecars that should apply these routes - jsonPath: .spec.gateways - name: Gateways - type: string - - description: The destination hosts to which traffic is being sent - jsonPath: .spec.hosts - name: Hosts - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting label/content routing, sni routing, - etc. See more details at: https://istio.io/docs/reference/config/networking/virtual-service.html' - properties: - exportTo: - description: A list of namespaces to which this virtual service is - exported. - items: - type: string - type: array - gateways: - description: The names of gateways and sidecars that should apply - these routes. - items: - type: string - type: array - hosts: - description: The destination hosts to which traffic is being sent. - items: - type: string - type: array - http: - description: An ordered list of route rules for HTTP traffic. - items: - properties: - corsPolicy: - description: Cross-Origin Resource Sharing policy (CORS). - properties: - allowCredentials: - nullable: true - type: boolean - allowHeaders: - items: - type: string - type: array - allowMethods: - description: List of HTTP methods allowed to access the - resource. - items: - type: string - type: array - allowOrigin: - description: The list of origins that are allowed to perform - CORS requests. - items: - type: string - type: array - allowOrigins: - description: String patterns that match allowed origins. - items: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - type: array - exposeHeaders: - items: - type: string - type: array - maxAge: - type: string - type: object - delegate: - properties: - name: - description: Name specifies the name of the delegate VirtualService. - type: string - namespace: - description: Namespace specifies the namespace where the - delegate VirtualService resides. - type: string - type: object - fault: - description: Fault injection policy to apply on HTTP traffic - at the client side. - properties: - abort: - oneOf: - - not: - anyOf: - - required: - - httpStatus - - required: - - grpcStatus - - required: - - http2Error - - required: - - httpStatus - - required: - - grpcStatus - - required: - - http2Error - properties: - grpcStatus: - type: string - http2Error: - type: string - httpStatus: - description: HTTP status code to use to abort the Http - request. - format: int32 - type: integer - percentage: - description: Percentage of requests to be aborted with - the error code provided. - properties: - value: - format: double - type: number - type: object - type: object - delay: - oneOf: - - not: - anyOf: - - required: - - fixedDelay - - required: - - exponentialDelay - - required: - - fixedDelay - - required: - - exponentialDelay - properties: - exponentialDelay: - type: string - fixedDelay: - description: Add a fixed delay before forwarding the - request. - type: string - percent: - description: Percentage of requests on which the delay - will be injected (0-100). - format: int32 - type: integer - percentage: - description: Percentage of requests on which the delay - will be injected. - properties: - value: - format: double - type: number - type: object - type: object - type: object - headers: - properties: - request: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - response: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - type: object - match: - items: - properties: - authority: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - headers: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - type: object - ignoreUriCase: - description: Flag to specify whether the URI matching - should be case-insensitive. - type: boolean - method: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - name: - description: The name assigned to a match. - type: string - port: - description: Specifies the ports on the host that is being - addressed. - type: integer - queryParams: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - description: Query parameters for matching. - type: object - scheme: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - uri: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - withoutHeaders: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - description: withoutHeader has the same syntax with the - header, but has opposite meaning. - type: object - type: object - type: array - mirror: - properties: - host: - description: The name of a service from the service registry. - type: string - port: - description: Specifies the port on the host that is being - addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - mirror_percent: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - nullable: true - type: integer - mirrorPercent: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - nullable: true - type: integer - mirrorPercentage: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - properties: - value: - format: double - type: number - type: object - name: - description: The name assigned to the route for debugging purposes. - type: string - redirect: - description: A HTTP rule can either redirect or forward (default) - traffic. - oneOf: - - not: - anyOf: - - required: - - port - - required: - - derivePort - - required: - - port - - required: - - derivePort - properties: - authority: - type: string - derivePort: - enum: - - FROM_PROTOCOL_DEFAULT - - FROM_REQUEST_PORT - type: string - port: - description: On a redirect, overwrite the port portion of - the URL with this value. - type: integer - redirectCode: - type: integer - scheme: - description: On a redirect, overwrite the scheme portion - of the URL with this value. - type: string - uri: - type: string - type: object - retries: - description: Retry policy for HTTP requests. - properties: - attempts: - description: Number of retries to be allowed for a given - request. - format: int32 - type: integer - perTryTimeout: - description: Timeout per attempt for a given request, including - the initial call and any retries. - type: string - retryOn: - description: Specifies the conditions under which retry - takes place. - type: string - retryRemoteLocalities: - description: Flag to specify whether the retries should - retry to other localities. - nullable: true - type: boolean - type: object - rewrite: - description: Rewrite HTTP URIs and Authority headers. - properties: - authority: - description: rewrite the Authority/Host header with this - value. - type: string - uri: - type: string - type: object - route: - description: A HTTP rule can either redirect or forward (default) - traffic. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - headers: - properties: - request: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - response: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - timeout: - description: Timeout for HTTP requests, default is disabled. - type: string - type: object - type: array - tcp: - description: An ordered list of route rules for opaque TCP traffic. - items: - properties: - match: - items: - properties: - destinationSubnets: - description: IPv4 or IPv6 ip addresses of destination - with optional subnet. - items: - type: string - type: array - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - port: - description: Specifies the port on the host that is being - addressed. - type: integer - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - sourceSubnet: - description: IPv4 or IPv6 ip address of source with optional - subnet. - type: string - type: object - type: array - route: - description: The destination to which the connection should - be forwarded to. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - type: object - type: array - tls: - items: - properties: - match: - items: - properties: - destinationSubnets: - description: IPv4 or IPv6 ip addresses of destination - with optional subnet. - items: - type: string - type: array - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - port: - description: Specifies the port on the host that is being - addressed. - type: integer - sniHosts: - description: SNI (server name indicator) to match on. - items: - type: string - type: array - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - type: object - type: array - route: - description: The destination to which the connection should - be forwarded to. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - type: object - type: array - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - description: The names of gateways and sidecars that should apply these routes - jsonPath: .spec.gateways - name: Gateways - type: string - - description: The destination hosts to which traffic is being sent - jsonPath: .spec.hosts - name: Hosts - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting label/content routing, sni routing, - etc. See more details at: https://istio.io/docs/reference/config/networking/virtual-service.html' - properties: - exportTo: - description: A list of namespaces to which this virtual service is - exported. - items: - type: string - type: array - gateways: - description: The names of gateways and sidecars that should apply - these routes. - items: - type: string - type: array - hosts: - description: The destination hosts to which traffic is being sent. - items: - type: string - type: array - http: - description: An ordered list of route rules for HTTP traffic. - items: - properties: - corsPolicy: - description: Cross-Origin Resource Sharing policy (CORS). - properties: - allowCredentials: - nullable: true - type: boolean - allowHeaders: - items: - type: string - type: array - allowMethods: - description: List of HTTP methods allowed to access the - resource. - items: - type: string - type: array - allowOrigin: - description: The list of origins that are allowed to perform - CORS requests. - items: - type: string - type: array - allowOrigins: - description: String patterns that match allowed origins. - items: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - type: array - exposeHeaders: - items: - type: string - type: array - maxAge: - type: string - type: object - delegate: - properties: - name: - description: Name specifies the name of the delegate VirtualService. - type: string - namespace: - description: Namespace specifies the namespace where the - delegate VirtualService resides. - type: string - type: object - fault: - description: Fault injection policy to apply on HTTP traffic - at the client side. - properties: - abort: - oneOf: - - not: - anyOf: - - required: - - httpStatus - - required: - - grpcStatus - - required: - - http2Error - - required: - - httpStatus - - required: - - grpcStatus - - required: - - http2Error - properties: - grpcStatus: - type: string - http2Error: - type: string - httpStatus: - description: HTTP status code to use to abort the Http - request. - format: int32 - type: integer - percentage: - description: Percentage of requests to be aborted with - the error code provided. - properties: - value: - format: double - type: number - type: object - type: object - delay: - oneOf: - - not: - anyOf: - - required: - - fixedDelay - - required: - - exponentialDelay - - required: - - fixedDelay - - required: - - exponentialDelay - properties: - exponentialDelay: - type: string - fixedDelay: - description: Add a fixed delay before forwarding the - request. - type: string - percent: - description: Percentage of requests on which the delay - will be injected (0-100). - format: int32 - type: integer - percentage: - description: Percentage of requests on which the delay - will be injected. - properties: - value: - format: double - type: number - type: object - type: object - type: object - headers: - properties: - request: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - response: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - type: object - match: - items: - properties: - authority: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - headers: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - type: object - ignoreUriCase: - description: Flag to specify whether the URI matching - should be case-insensitive. - type: boolean - method: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - name: - description: The name assigned to a match. - type: string - port: - description: Specifies the ports on the host that is being - addressed. - type: integer - queryParams: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - description: Query parameters for matching. - type: object - scheme: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - uri: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - withoutHeaders: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - description: withoutHeader has the same syntax with the - header, but has opposite meaning. - type: object - type: object - type: array - mirror: - properties: - host: - description: The name of a service from the service registry. - type: string - port: - description: Specifies the port on the host that is being - addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - mirror_percent: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - nullable: true - type: integer - mirrorPercent: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - nullable: true - type: integer - mirrorPercentage: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - properties: - value: - format: double - type: number - type: object - name: - description: The name assigned to the route for debugging purposes. - type: string - redirect: - description: A HTTP rule can either redirect or forward (default) - traffic. - oneOf: - - not: - anyOf: - - required: - - port - - required: - - derivePort - - required: - - port - - required: - - derivePort - properties: - authority: - type: string - derivePort: - enum: - - FROM_PROTOCOL_DEFAULT - - FROM_REQUEST_PORT - type: string - port: - description: On a redirect, overwrite the port portion of - the URL with this value. - type: integer - redirectCode: - type: integer - scheme: - description: On a redirect, overwrite the scheme portion - of the URL with this value. - type: string - uri: - type: string - type: object - retries: - description: Retry policy for HTTP requests. - properties: - attempts: - description: Number of retries to be allowed for a given - request. - format: int32 - type: integer - perTryTimeout: - description: Timeout per attempt for a given request, including - the initial call and any retries. - type: string - retryOn: - description: Specifies the conditions under which retry - takes place. - type: string - retryRemoteLocalities: - description: Flag to specify whether the retries should - retry to other localities. - nullable: true - type: boolean - type: object - rewrite: - description: Rewrite HTTP URIs and Authority headers. - properties: - authority: - description: rewrite the Authority/Host header with this - value. - type: string - uri: - type: string - type: object - route: - description: A HTTP rule can either redirect or forward (default) - traffic. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - headers: - properties: - request: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - response: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - timeout: - description: Timeout for HTTP requests, default is disabled. - type: string - type: object - type: array - tcp: - description: An ordered list of route rules for opaque TCP traffic. - items: - properties: - match: - items: - properties: - destinationSubnets: - description: IPv4 or IPv6 ip addresses of destination - with optional subnet. - items: - type: string - type: array - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - port: - description: Specifies the port on the host that is being - addressed. - type: integer - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - sourceSubnet: - description: IPv4 or IPv6 ip address of source with optional - subnet. - type: string - type: object - type: array - route: - description: The destination to which the connection should - be forwarded to. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - type: object - type: array - tls: - items: - properties: - match: - items: - properties: - destinationSubnets: - description: IPv4 or IPv6 ip addresses of destination - with optional subnet. - items: - type: string - type: array - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - port: - description: Specifies the port on the host that is being - addressed. - type: integer - sniHosts: - description: SNI (server name indicator) to match on. - items: - type: string - type: array - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - type: object - type: array - route: - description: The destination to which the connection should - be forwarded to. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - type: object - type: array - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: workloadentries.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: WorkloadEntry - listKind: WorkloadEntryList - plural: workloadentries - shortNames: - - we - singular: workloadentry - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - description: Address associated with the network endpoint. - jsonPath: .spec.address - name: Address - type: string - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting VMs onboarded into the mesh. See - more details at: https://istio.io/docs/reference/config/networking/workload-entry.html' - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - description: Address associated with the network endpoint. - jsonPath: .spec.address - name: Address - type: string - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting VMs onboarded into the mesh. See - more details at: https://istio.io/docs/reference/config/networking/workload-entry.html' - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: workloadgroups.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: WorkloadGroup - listKind: WorkloadGroupList - plural: workloadgroups - shortNames: - - wg - singular: workloadgroup - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Describes a collection of workload instances. See more details - at: https://istio.io/docs/reference/config/networking/workload-group.html' - properties: - metadata: - description: Metadata that will be used for all corresponding `WorkloadEntries`. - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - probe: - description: '`ReadinessProbe` describes the configuration the user - must provide for healthchecking on their workload.' - oneOf: - - not: - anyOf: - - required: - - httpGet - - required: - - tcpSocket - - required: - - exec - - required: - - httpGet - - required: - - tcpSocket - - required: - - exec - properties: - exec: - description: Health is determined by how the command that is executed - exited. - properties: - command: - description: Command to run. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for the probe to be - considered failed after having succeeded. - format: int32 - type: integer - httpGet: - properties: - host: - description: Host name to connect to, defaults to the pod - IP. - type: string - httpHeaders: - description: Headers the proxy will pass on to make the request. - items: - properties: - name: - type: string - value: - type: string - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - description: Port on which the endpoint lives. - type: integer - scheme: - type: string - type: object - initialDelaySeconds: - description: Number of seconds after the container has started - before readiness probes are initiated. - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform the probe. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for the probe to be - considered successful after having failed. - format: int32 - type: integer - tcpSocket: - description: Health is determined by if the proxy is able to connect. - properties: - host: - type: string - port: - type: integer - type: object - timeoutSeconds: - description: Number of seconds after which the probe times out. - format: int32 - type: integer - type: object - template: - description: Template to be used for the generation of `WorkloadEntry` - resources that belong to this `WorkloadGroup`. - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - properties: - metadata: - description: Metadata that will be used for all corresponding `WorkloadEntries`. - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - probe: - description: '`ReadinessProbe` describes the configuration the user - must provide for healthchecking on their workload.' - oneOf: - - not: - anyOf: - - required: - - httpGet - - required: - - tcpSocket - - required: - - exec - - required: - - httpGet - - required: - - tcpSocket - - required: - - exec - properties: - exec: - description: Health is determined by how the command that is executed - exited. - properties: - command: - description: Command to run. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for the probe to be - considered failed after having succeeded. - format: int32 - type: integer - httpGet: - properties: - host: - description: Host name to connect to, defaults to the pod - IP. - type: string - httpHeaders: - description: Headers the proxy will pass on to make the request. - items: - properties: - name: - type: string - value: - type: string - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - description: Port on which the endpoint lives. - type: integer - scheme: - type: string - type: object - initialDelaySeconds: - description: Number of seconds after the container has started - before readiness probes are initiated. - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform the probe. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for the probe to be - considered successful after having failed. - format: int32 - type: integer - tcpSocket: - description: Health is determined by if the proxy is able to connect. - properties: - host: - type: string - port: - type: integer - type: object - timeoutSeconds: - description: Number of seconds after which the probe times out. - format: int32 - type: integer - type: object - template: - description: Template to be used for the generation of `WorkloadEntry` - resources that belong to this `WorkloadGroup`. - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - istio: security - release: istio - name: authorizationpolicies.security.istio.io -spec: - group: security.istio.io - names: - categories: - - istio-io - - security-istio-io - kind: AuthorizationPolicy - listKind: AuthorizationPolicyList - plural: authorizationpolicies - singular: authorizationpolicy - scope: Namespaced - versions: - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration for access control on workloads. See more - details at: https://istio.io/docs/reference/config/security/authorization-policy.html' - oneOf: - - not: - anyOf: - - required: - - provider - - required: - - provider - properties: - action: - description: Optional. - enum: - - ALLOW - - DENY - - AUDIT - - CUSTOM - type: string - provider: - description: Specifies detailed configuration of the CUSTOM action. - properties: - name: - description: Specifies the name of the extension provider. - type: string - type: object - rules: - description: Optional. - items: - properties: - from: - description: Optional. - items: - properties: - source: - description: Source specifies the source of a request. - properties: - ipBlocks: - description: Optional. - items: - type: string - type: array - namespaces: - description: Optional. - items: - type: string - type: array - notIpBlocks: - description: Optional. - items: - type: string - type: array - notNamespaces: - description: Optional. - items: - type: string - type: array - notPrincipals: - description: Optional. - items: - type: string - type: array - notRemoteIpBlocks: - description: Optional. - items: - type: string - type: array - notRequestPrincipals: - description: Optional. - items: - type: string - type: array - principals: - description: Optional. - items: - type: string - type: array - remoteIpBlocks: - description: Optional. - items: - type: string - type: array - requestPrincipals: - description: Optional. - items: - type: string - type: array - type: object - type: object - type: array - to: - description: Optional. - items: - properties: - operation: - description: Operation specifies the operation of a request. - properties: - hosts: - description: Optional. - items: - type: string - type: array - methods: - description: Optional. - items: - type: string - type: array - notHosts: - description: Optional. - items: - type: string - type: array - notMethods: - description: Optional. - items: - type: string - type: array - notPaths: - description: Optional. - items: - type: string - type: array - notPorts: - description: Optional. - items: - type: string - type: array - paths: - description: Optional. - items: - type: string - type: array - ports: - description: Optional. - items: - type: string - type: array - type: object - type: object - type: array - when: - description: Optional. - items: - properties: - key: - description: The name of an Istio attribute. - type: string - notValues: - description: Optional. - items: - type: string - type: array - values: - description: Optional. - items: - type: string - type: array - type: object - type: array - type: object - type: array - selector: - description: Optional. - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - istio: security - release: istio - name: peerauthentications.security.istio.io -spec: - group: security.istio.io - names: - categories: - - istio-io - - security-istio-io - kind: PeerAuthentication - listKind: PeerAuthenticationList - plural: peerauthentications - shortNames: - - pa - singular: peerauthentication - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Defines the mTLS mode used for peer authentication. - jsonPath: .spec.mtls.mode - name: Mode - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: PeerAuthentication defines how traffic will be tunneled (or - not) to the sidecar. - properties: - mtls: - description: Mutual TLS settings for workload. - properties: - mode: - description: Defines the mTLS mode used for peer authentication. - enum: - - UNSET - - DISABLE - - PERMISSIVE - - STRICT - type: string - type: object - portLevelMtls: - additionalProperties: - properties: - mode: - description: Defines the mTLS mode used for peer authentication. - enum: - - UNSET - - DISABLE - - PERMISSIVE - - STRICT - type: string - type: object - description: Port specific mutual TLS settings. - type: object - selector: - description: The selector determines the workloads to apply the ChannelAuthentication - on. - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - istio: security - release: istio - name: requestauthentications.security.istio.io -spec: - group: security.istio.io - names: - categories: - - istio-io - - security-istio-io - kind: RequestAuthentication - listKind: RequestAuthenticationList - plural: requestauthentications - shortNames: - - ra - singular: requestauthentication - scope: Namespaced - versions: - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: RequestAuthentication defines what request authentication - methods are supported by a workload. - properties: - jwtRules: - description: Define the list of JWTs that can be validated at the - selected workloads' proxy. - items: - properties: - audiences: - items: - type: string - type: array - forwardOriginalToken: - description: If set to true, the original token will be kept - for the upstream request. - type: boolean - fromHeaders: - description: List of header locations from which JWT is expected. - items: - properties: - name: - description: The HTTP header name. - type: string - prefix: - description: The prefix that should be stripped before - decoding the token. - type: string - type: object - type: array - fromParams: - description: List of query parameters from which JWT is expected. - items: - type: string - type: array - issuer: - description: Identifies the issuer that issued the JWT. - type: string - jwks: - description: JSON Web Key Set of public keys to validate signature - of the JWT. - type: string - jwks_uri: - type: string - jwksUri: - type: string - outputPayloadToHeader: - type: string - type: object - type: array - selector: - description: Optional. - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - istio: telemetry - release: istio - name: telemetries.telemetry.istio.io -spec: - group: telemetry.istio.io - names: - categories: - - istio-io - - telemetry-istio-io - kind: Telemetry - listKind: TelemetryList - plural: telemetries - shortNames: - - telemetry - singular: telemetry - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Telemetry configuration for workloads. See more details - at: https://istio.io/docs/reference/config/telemetry.html' - properties: - accessLogging: - description: Optional. - items: - properties: - disabled: - description: Controls logging. - nullable: true - type: boolean - filter: - description: Optional. - properties: - expression: - description: CEL expression for selecting when requests/connections - should be logged. - type: string - type: object - match: - description: Allows tailoring of logging behavior to specific - conditions. - properties: - mode: - enum: - - CLIENT_AND_SERVER - - CLIENT - - SERVER - type: string - type: object - providers: - description: Optional. - items: - properties: - name: - description: Required. - type: string - type: object - type: array - type: object - type: array - metrics: - description: Optional. - items: - properties: - overrides: - description: Optional. - items: - properties: - disabled: - description: Optional. - nullable: true - type: boolean - match: - description: Match allows provides the scope of the override. - oneOf: - - not: - anyOf: - - required: - - metric - - required: - - customMetric - - required: - - metric - - required: - - customMetric - properties: - customMetric: - description: Allows free-form specification of a metric. - type: string - metric: - description: One of the well-known Istio Standard - Metrics. - enum: - - ALL_METRICS - - REQUEST_COUNT - - REQUEST_DURATION - - REQUEST_SIZE - - RESPONSE_SIZE - - TCP_OPENED_CONNECTIONS - - TCP_CLOSED_CONNECTIONS - - TCP_SENT_BYTES - - TCP_RECEIVED_BYTES - - GRPC_REQUEST_MESSAGES - - GRPC_RESPONSE_MESSAGES - type: string - mode: - enum: - - CLIENT_AND_SERVER - - CLIENT - - SERVER - type: string - type: object - tagOverrides: - additionalProperties: - properties: - operation: - description: Operation controls whether or not to - update/add a tag, or to remove it. - enum: - - UPSERT - - REMOVE - type: string - value: - description: Value is only considered if the operation - is `UPSERT`. - type: string - type: object - description: Optional. - type: object - type: object - type: array - providers: - description: Optional. - items: - properties: - name: - description: Required. - type: string - type: object - type: array - type: object - type: array - selector: - description: Optional. - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - tracing: - description: Optional. - items: - properties: - customTags: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - literal - - required: - - environment - - required: - - header - - required: - - literal - - required: - - environment - - required: - - header - properties: - environment: - description: Environment adds the value of an environment - variable to each span. - properties: - defaultValue: - description: Optional. - type: string - name: - description: Name of the environment variable from - which to extract the tag value. - type: string - type: object - header: - properties: - defaultValue: - description: Optional. - type: string - name: - description: Name of the header from which to extract - the tag value. - type: string - type: object - literal: - description: Literal adds the same, hard-coded value to - each span. - properties: - value: - description: The tag value to use. - type: string - type: object - type: object - description: Optional. - type: object - disableSpanReporting: - description: Controls span reporting. - nullable: true - type: boolean - match: - description: Allows tailoring of behavior to specific conditions. - properties: - mode: - enum: - - CLIENT_AND_SERVER - - CLIENT - - SERVER - type: string - type: object - providers: - description: Optional. - items: - properties: - name: - description: Required. - type: string - type: object - type: array - randomSamplingPercentage: - nullable: true - type: number - useRequestIdForTraceSampling: - nullable: true - type: boolean - type: object - type: array - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- - ---- -# Source: crds/crd-operator.yaml -# SYNC WITH manifests/charts/istio-operator/templates -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: istiooperators.install.istio.io - labels: - release: istio -spec: - conversion: - strategy: None - group: install.istio.io - names: - kind: IstioOperator - listKind: IstioOperatorList - plural: istiooperators - singular: istiooperator - shortNames: - - iop - - io - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Istio control plane revision - jsonPath: .spec.revision - name: Revision - type: string - - description: IOP current state - jsonPath: .status.status - name: Status - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - subresources: - status: {} - name: v1alpha1 - schema: - openAPIV3Schema: - type: object - x-kubernetes-preserve-unknown-fields: true - served: true - storage: true ---- - ---- -# Source: base/templates/reader-serviceaccount.yaml -# This service account aggregates reader permissions for the revisions in a given cluster -# Should be used for remote secret creation. -apiVersion: v1 -kind: ServiceAccount -metadata: - name: istio-reader-service-account - namespace: dubbo-system - labels: - app: istio-reader - release: istio ---- -# Source: base/templates/serviceaccount.yaml -# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# DO NOT EDIT! -# THIS IS A LEGACY CHART HERE FOR BACKCOMPAT -# UPDATED CHART AT manifests/charts/istio-control/istio-discovery -# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: istiod-service-account - namespace: dubbo-system - labels: - app: istiod - release: istio ---- -# Source: base/templates/clusterrole.yaml -# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# DO NOT EDIT! -# THIS IS A LEGACY CHART HERE FOR BACKCOMPAT -# UPDATED CHART AT manifests/charts/istio-control/istio-discovery -# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istiod-dubbo-system - labels: - app: istiod - release: istio -rules: - # sidecar injection controller - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["mutatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update", "patch"] - - # configuration validation webhook controller - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["validatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update"] - - # istio configuration - # removing CRD permissions can break older versions of Istio running alongside this control plane (https://github.com/istio/istio/issues/29382) - # please proceed with caution - - apiGroups: ["config.istio.io", "security.istio.io", "networking.istio.io", "authentication.istio.io", "rbac.istio.io", "telemetry.istio.io"] - verbs: ["get", "watch", "list"] - resources: ["*"] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "workloadentries" ] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "workloadentries/status" ] - - # auto-detect installed CRD definitions - - apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "list", "watch"] - - # discovery and routing - - apiGroups: [""] - resources: ["pods", "nodes", "services", "namespaces", "endpoints"] - verbs: ["get", "list", "watch"] - - apiGroups: ["discovery.k8s.io"] - resources: ["endpointslices"] - verbs: ["get", "list", "watch"] - - # ingress controller - - apiGroups: ["networking.k8s.io"] - resources: ["ingresses", "ingressclasses"] - verbs: ["get", "list", "watch"] - - apiGroups: ["networking.k8s.io"] - resources: ["ingresses/status"] - verbs: ["*"] - - # required for CA's namespace controller - - apiGroups: [""] - resources: ["configmaps"] - verbs: ["create", "get", "list", "watch", "update"] - - # Istiod and bootstrap. - - apiGroups: ["certificates.k8s.io"] - resources: - - "certificatesigningrequests" - - "certificatesigningrequests/approval" - - "certificatesigningrequests/status" - verbs: ["update", "create", "get", "delete", "watch"] - - apiGroups: ["certificates.k8s.io"] - resources: - - "signers" - resourceNames: - - "kubernetes.io/legacy-unknown" - verbs: ["approve"] - - # Used by Istiod to verify the JWT tokens - - apiGroups: ["authentication.k8s.io"] - resources: ["tokenreviews"] - verbs: ["create"] - - # Used by Istiod to verify gateway SDS - - apiGroups: ["authorization.k8s.io"] - resources: ["subjectaccessreviews"] - verbs: ["create"] - - # Use for Kubernetes Service APIs - - apiGroups: ["networking.x-k8s.io", "gateway.networking.k8s.io"] - resources: ["*"] - verbs: ["get", "watch", "list"] - - apiGroups: ["networking.x-k8s.io", "gateway.networking.k8s.io"] - resources: ["*"] # TODO: should be on just */status but wildcard is not supported - verbs: ["update"] - - apiGroups: ["gateway.networking.k8s.io"] - resources: ["gatewayclasses"] - verbs: ["create", "update", "patch", "delete"] - - # Needed for multicluster secret reading, possibly ingress certs in the future - - apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "watch", "list"] - - # Used for MCS serviceexport management - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceexports"] - verbs: ["get", "watch", "list", "create", "delete"] - - # Used for MCS serviceimport management - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceimports"] - verbs: ["get", "watch", "list"] ---- -# Source: base/templates/clusterrole.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istio-reader-dubbo-system - labels: - app: istio-reader - release: istio -rules: - - apiGroups: - - "config.istio.io" - - "security.istio.io" - - "networking.istio.io" - - "authentication.istio.io" - - "rbac.istio.io" - resources: ["*"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["endpoints", "pods", "services", "nodes", "replicationcontrollers", "namespaces", "secrets"] - verbs: ["get", "list", "watch"] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list" ] - resources: [ "workloadentries" ] - - apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "list", "watch"] - - apiGroups: ["discovery.k8s.io"] - resources: ["endpointslices"] - verbs: ["get", "list", "watch"] - - apiGroups: ["apps"] - resources: ["replicasets"] - verbs: ["get", "list", "watch"] - - apiGroups: ["authentication.k8s.io"] - resources: ["tokenreviews"] - verbs: ["create"] - - apiGroups: ["authorization.k8s.io"] - resources: ["subjectaccessreviews"] - verbs: ["create"] - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceexports"] - verbs: ["get", "watch", "list"] - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceimports"] - verbs: ["get", "watch", "list"] ---- -# Source: base/templates/clusterrolebinding.yaml -# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# DO NOT EDIT! -# THIS IS A LEGACY CHART HERE FOR BACKCOMPAT -# UPDATED CHART AT manifests/charts/istio-control/istio-discovery -# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istio-reader-dubbo-system - labels: - app: istio-reader - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istio-reader-dubbo-system -subjects: - - kind: ServiceAccount - name: istio-reader-service-account - namespace: dubbo-system ---- -# Source: base/templates/clusterrolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istiod-dubbo-system - labels: - app: istiod - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istiod-dubbo-system -subjects: - - kind: ServiceAccount - name: istiod-service-account - namespace: dubbo-system ---- -# Source: base/templates/role.yaml -# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# DO NOT EDIT! -# THIS IS A LEGACY CHART HERE FOR BACKCOMPAT -# UPDATED CHART AT manifests/charts/istio-control/istio-discovery -# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: istiod-dubbo-system - namespace: dubbo-system - labels: - app: istiod - release: istio -rules: -# permissions to verify the webhook is ready and rejecting -# invalid config. We use --server-dry-run so no config is persisted. -- apiGroups: ["networking.istio.io"] - verbs: ["create"] - resources: ["gateways"] - -# For storing CA secret -- apiGroups: [""] - resources: ["secrets"] - # TODO lock this down to istio-ca-cert if not using the DNS cert mesh config - verbs: ["create", "get", "watch", "list", "update", "delete"] ---- -# Source: base/templates/rolebinding.yaml -# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# DO NOT EDIT! -# THIS IS A LEGACY CHART HERE FOR BACKCOMPAT -# UPDATED CHART AT manifests/charts/istio-control/istio-discovery -# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: istiod-dubbo-system - namespace: dubbo-system - labels: - app: istiod - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: istiod-dubbo-system -subjects: - - kind: ServiceAccount - name: istiod-service-account - namespace: dubbo-system ---- -# Source: base/templates/default.yaml -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - name: istiod-default-validator - labels: - app: istiod - release: istio - istio: istiod - istio.io/rev: dubbo -webhooks: - - name: validation.istio.io - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: "/validate" - rules: - - operations: - - CREATE - - UPDATE - apiGroups: - - security.istio.io - - networking.istio.io - apiVersions: - - "*" - resources: - - "*" - # Fail open until the validation webhook is ready. The webhook controller - # will update this to `Fail` and patch in the `caBundle` when the webhook - # endpoint is ready. - failurePolicy: Ignore - sideEffects: None - admissionReviewVersions: ["v1beta1", "v1"] diff --git a/manifests/charts/base/kustomization.yaml b/manifests/charts/base/kustomization.yaml deleted file mode 100644 index dbde62f0a..000000000 --- a/manifests/charts/base/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -resources: - - files/gen-istio-cluster.yaml diff --git a/manifests/charts/base/templates/NOTES.txt b/manifests/charts/base/templates/NOTES.txt deleted file mode 100644 index 006450167..000000000 --- a/manifests/charts/base/templates/NOTES.txt +++ /dev/null @@ -1,5 +0,0 @@ -Istio base successfully installed! - -To learn more about the release, try: - $ helm status {{ .Release.Name }} - $ helm get all {{ .Release.Name }} diff --git a/manifests/charts/base/templates/clusterrole.yaml b/manifests/charts/base/templates/clusterrole.yaml deleted file mode 100644 index e0cbea8fe..000000000 --- a/manifests/charts/base/templates/clusterrole.yaml +++ /dev/null @@ -1,181 +0,0 @@ -# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# DO NOT EDIT! -# THIS IS A LEGACY CHART HERE FOR BACKCOMPAT -# UPDATED CHART AT manifests/charts/istio-control/istio-discovery -# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istiod-{{ .Values.global.istioNamespace }} - labels: - app: istiod - release: {{ .Release.Name }} -rules: - # sidecar injection controller - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["mutatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update", "patch"] - - # configuration validation webhook controller - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["validatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update"] - - # istio configuration - # removing CRD permissions can break older versions of Istio running alongside this control plane (https://github.com/istio/istio/issues/29382) - # please proceed with caution - - apiGroups: ["config.istio.io", "security.istio.io", "networking.istio.io", "authentication.istio.io", "rbac.istio.io", "telemetry.istio.io"] - verbs: ["get", "watch", "list"] - resources: ["*"] -{{- if .Values.global.istiod.enableAnalysis }} - - apiGroups: ["config.istio.io", "security.istio.io", "networking.istio.io", "authentication.istio.io", "rbac.istio.io", "telemetry.istio.io"] - verbs: ["update"] - # TODO: should be on just */status but wildcard is not supported - resources: ["*"] -{{- end }} - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "workloadentries" ] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "workloadentries/status" ] - - # auto-detect installed CRD definitions - - apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "list", "watch"] - - # discovery and routing - - apiGroups: [""] - resources: ["pods", "nodes", "services", "namespaces", "endpoints"] - verbs: ["get", "list", "watch"] - - apiGroups: ["discovery.k8s.io"] - resources: ["endpointslices"] - verbs: ["get", "list", "watch"] - - # ingress controller -{{- if .Values.global.istiod.enableAnalysis }} - - apiGroups: ["extensions", "networking.k8s.io"] - resources: ["ingresses"] - verbs: ["get", "list", "watch"] - - apiGroups: ["extensions", "networking.k8s.io"] - resources: ["ingresses/status"] - verbs: ["*"] -{{- end}} - - apiGroups: ["networking.k8s.io"] - resources: ["ingresses", "ingressclasses"] - verbs: ["get", "list", "watch"] - - apiGroups: ["networking.k8s.io"] - resources: ["ingresses/status"] - verbs: ["*"] - - # required for CA's namespace controller - - apiGroups: [""] - resources: ["configmaps"] - verbs: ["create", "get", "list", "watch", "update"] - - # Istiod and bootstrap. - - apiGroups: ["certificates.k8s.io"] - resources: - - "certificatesigningrequests" - - "certificatesigningrequests/approval" - - "certificatesigningrequests/status" - verbs: ["update", "create", "get", "delete", "watch"] - - apiGroups: ["certificates.k8s.io"] - resources: - - "signers" - resourceNames: - - "kubernetes.io/legacy-unknown" - verbs: ["approve"] - - # Used by Istiod to verify the JWT tokens - - apiGroups: ["authentication.k8s.io"] - resources: ["tokenreviews"] - verbs: ["create"] - - # Used by Istiod to verify gateway SDS - - apiGroups: ["authorization.k8s.io"] - resources: ["subjectaccessreviews"] - verbs: ["create"] - - # Use for Kubernetes Service APIs - - apiGroups: ["networking.x-k8s.io", "gateway.networking.k8s.io"] - resources: ["*"] - verbs: ["get", "watch", "list"] - - apiGroups: ["networking.x-k8s.io", "gateway.networking.k8s.io"] - resources: ["*"] # TODO: should be on just */status but wildcard is not supported - verbs: ["update"] - - apiGroups: ["gateway.networking.k8s.io"] - resources: ["gatewayclasses"] - verbs: ["create", "update", "patch", "delete"] - - # Needed for multicluster secret reading, possibly ingress certs in the future - - apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "watch", "list"] - - # Used for MCS serviceexport management - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceexports"] - verbs: ["get", "watch", "list", "create", "delete"] - - # Used for MCS serviceimport management - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceimports"] - verbs: ["get", "watch", "list"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istio-reader-{{ .Values.global.istioNamespace }} - labels: - app: istio-reader - release: {{ .Release.Name }} -rules: - - apiGroups: - - "config.istio.io" - - "security.istio.io" - - "networking.istio.io" - - "authentication.istio.io" - - "rbac.istio.io" - resources: ["*"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["endpoints", "pods", "services", "nodes", "replicationcontrollers", "namespaces", "secrets"] - verbs: ["get", "list", "watch"] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list" ] - resources: [ "workloadentries" ] - - apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "list", "watch"] - - apiGroups: ["discovery.k8s.io"] - resources: ["endpointslices"] - verbs: ["get", "list", "watch"] - - apiGroups: ["apps"] - resources: ["replicasets"] - verbs: ["get", "list", "watch"] - - apiGroups: ["authentication.k8s.io"] - resources: ["tokenreviews"] - verbs: ["create"] - - apiGroups: ["authorization.k8s.io"] - resources: ["subjectaccessreviews"] - verbs: ["create"] - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceexports"] - verbs: ["get", "watch", "list"] - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceimports"] - verbs: ["get", "watch", "list"] -{{- if or .Values.global.externalIstiod }} - - apiGroups: [""] - resources: ["configmaps"] - verbs: ["create", "get", "list", "watch", "update"] - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["mutatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update", "patch"] - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["validatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update"] -{{- end}} ---- diff --git a/manifests/charts/base/templates/clusterrolebinding.yaml b/manifests/charts/base/templates/clusterrolebinding.yaml deleted file mode 100644 index d61729b29..000000000 --- a/manifests/charts/base/templates/clusterrolebinding.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# DO NOT EDIT! -# THIS IS A LEGACY CHART HERE FOR BACKCOMPAT -# UPDATED CHART AT manifests/charts/istio-control/istio-discovery -# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istio-reader-{{ .Values.global.istioNamespace }} - labels: - app: istio-reader - release: {{ .Release.Name }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istio-reader-{{ .Values.global.istioNamespace }} -subjects: - - kind: ServiceAccount - name: istio-reader-service-account - namespace: {{ .Values.global.istioNamespace }} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istiod-{{ .Values.global.istioNamespace }} - labels: - app: istiod - release: {{ .Release.Name }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istiod-{{ .Values.global.istioNamespace }} -subjects: - - kind: ServiceAccount - name: istiod-service-account - namespace: {{ .Values.global.istioNamespace }} ---- diff --git a/manifests/charts/base/templates/crds.yaml b/manifests/charts/base/templates/crds.yaml deleted file mode 100644 index 871ee2a6b..000000000 --- a/manifests/charts/base/templates/crds.yaml +++ /dev/null @@ -1,4 +0,0 @@ -{{- if .Values.base.enableCRDTemplates }} -{{ .Files.Get "crds/crd-all.gen.yaml" }} -{{ .Files.Get "crds/crd-operator.yaml" }} -{{- end }} diff --git a/manifests/charts/base/templates/default.yaml b/manifests/charts/base/templates/default.yaml deleted file mode 100644 index 62444abaa..000000000 --- a/manifests/charts/base/templates/default.yaml +++ /dev/null @@ -1,43 +0,0 @@ -{{- if not (eq .Values.defaultRevision "") }} -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - name: istiod-default-validator - labels: - app: istiod - release: {{ .Release.Name }} - istio: istiod - istio.io/rev: {{ .Values.defaultRevision }} -webhooks: - - name: validation.istio.io - clientConfig: - {{- if .Values.base.validationURL }} - url: {{ .Values.base.validationURL }} - {{- else }} - service: - {{- if (eq .Values.defaultRevision "dubbo") }} - name: istiod - {{- else }} - name: istiod-{{ .Values.defaultRevision }} - {{- end }} - namespace: {{ .Values.global.istioNamespace }} - path: "/validate" - {{- end }} - rules: - - operations: - - CREATE - - UPDATE - apiGroups: - - security.istio.io - - networking.istio.io - apiVersions: - - "*" - resources: - - "*" - # Fail open until the validation webhook is ready. The webhook controller - # will update this to `Fail` and patch in the `caBundle` when the webhook - # endpoint is ready. - failurePolicy: Ignore - sideEffects: None - admissionReviewVersions: ["v1beta1", "v1"] -{{- end }} diff --git a/manifests/charts/base/templates/endpoints.yaml b/manifests/charts/base/templates/endpoints.yaml deleted file mode 100644 index fb4dd5efd..000000000 --- a/manifests/charts/base/templates/endpoints.yaml +++ /dev/null @@ -1,30 +0,0 @@ -{{- if .Values.global.remotePilotAddress }} - {{- if .Values.pilot.enabled }} -apiVersion: v1 -kind: Endpoints -metadata: - name: istiod-remote - namespace: {{ .Release.Namespace }} -subsets: -- addresses: - - ip: {{ .Values.global.remotePilotAddress }} - ports: - - port: 15012 - name: tcp-istiod - protocol: TCP - {{- else if regexMatch "^([0-9]*\\.){3}[0-9]*$" .Values.global.remotePilotAddress }} -apiVersion: v1 -kind: Endpoints -metadata: - name: istiod - namespace: {{ .Release.Namespace }} -subsets: -- addresses: - - ip: {{ .Values.global.remotePilotAddress }} - ports: - - port: 15012 - name: tcp-istiod - protocol: TCP - {{- end }} ---- -{{- end }} diff --git a/manifests/charts/base/templates/reader-serviceaccount.yaml b/manifests/charts/base/templates/reader-serviceaccount.yaml deleted file mode 100644 index d9ce18c27..000000000 --- a/manifests/charts/base/templates/reader-serviceaccount.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# This service account aggregates reader permissions for the revisions in a given cluster -# Should be used for remote secret creation. -apiVersion: v1 -kind: ServiceAccount - {{- if .Values.global.imagePullSecrets }} -imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} -metadata: - name: istio-reader-service-account - namespace: {{ .Values.global.istioNamespace }} - labels: - app: istio-reader - release: {{ .Release.Name }} diff --git a/manifests/charts/base/templates/role.yaml b/manifests/charts/base/templates/role.yaml deleted file mode 100644 index ca1a4243f..000000000 --- a/manifests/charts/base/templates/role.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# DO NOT EDIT! -# THIS IS A LEGACY CHART HERE FOR BACKCOMPAT -# UPDATED CHART AT manifests/charts/istio-control/istio-discovery -# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: istiod-{{ .Values.global.istioNamespace }} - namespace: {{ .Values.global.istioNamespace }} - labels: - app: istiod - release: {{ .Release.Name }} -rules: -# permissions to verify the webhook is ready and rejecting -# invalid config. We use --server-dry-run so no config is persisted. -- apiGroups: ["networking.istio.io"] - verbs: ["create"] - resources: ["gateways"] - -# For storing CA secret -- apiGroups: [""] - resources: ["secrets"] - # TODO lock this down to istio-ca-cert if not using the DNS cert mesh config - verbs: ["create", "get", "watch", "list", "update", "delete"] diff --git a/manifests/charts/base/templates/rolebinding.yaml b/manifests/charts/base/templates/rolebinding.yaml deleted file mode 100644 index 2b591fb89..000000000 --- a/manifests/charts/base/templates/rolebinding.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# DO NOT EDIT! -# THIS IS A LEGACY CHART HERE FOR BACKCOMPAT -# UPDATED CHART AT manifests/charts/istio-control/istio-discovery -# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: istiod-{{ .Values.global.istioNamespace }} - namespace: {{ .Values.global.istioNamespace }} - labels: - app: istiod - release: {{ .Release.Name }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: istiod-{{ .Values.global.istioNamespace }} -subjects: - - kind: ServiceAccount - name: istiod-service-account - namespace: {{ .Values.global.istioNamespace }} diff --git a/manifests/charts/base/templates/serviceaccount.yaml b/manifests/charts/base/templates/serviceaccount.yaml deleted file mode 100644 index ec25fd250..000000000 --- a/manifests/charts/base/templates/serviceaccount.yaml +++ /dev/null @@ -1,19 +0,0 @@ -# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# DO NOT EDIT! -# THIS IS A LEGACY CHART HERE FOR BACKCOMPAT -# UPDATED CHART AT manifests/charts/istio-control/istio-discovery -# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -apiVersion: v1 -kind: ServiceAccount - {{- if .Values.global.imagePullSecrets }} -imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} -metadata: - name: istiod-service-account - namespace: {{ .Values.global.istioNamespace }} - labels: - app: istiod - release: {{ .Release.Name }} diff --git a/manifests/charts/base/templates/services.yaml b/manifests/charts/base/templates/services.yaml deleted file mode 100644 index 705c36001..000000000 --- a/manifests/charts/base/templates/services.yaml +++ /dev/null @@ -1,37 +0,0 @@ -{{- if .Values.global.remotePilotAddress }} - {{- if .Values.pilot.enabled }} -# when local istiod is enabled, we can't use istiod service name to reach the remote control plane -apiVersion: v1 -kind: Service -metadata: - name: istiod-remote - namespace: {{ .Release.Namespace }} -spec: - ports: - - port: 15012 - name: tcp-istiod - protocol: TCP - clusterIP: None - {{- else }} -# when local istiod isn't enabled, we can use istiod service name to reach the remote control plane -apiVersion: v1 -kind: Service -metadata: - name: istiod - namespace: {{ .Release.Namespace }} -spec: - ports: - - port: 15012 - name: tcp-istiod - protocol: TCP - # if the remotePilotAddress is IP addr, we use clusterIP: None. - # else, we use externalName - {{- if regexMatch "^([0-9]*\\.){3}[0-9]*$" .Values.global.remotePilotAddress }} - clusterIP: None - {{- else }} - type: ExternalName - externalName: {{ .Values.global.remotePilotAddress }} - {{- end }} - {{- end }} ---- -{{- end }} diff --git a/manifests/charts/base/values.yaml b/manifests/charts/base/values.yaml deleted file mode 100644 index 92c5b65e9..000000000 --- a/manifests/charts/base/values.yaml +++ /dev/null @@ -1,29 +0,0 @@ -global: - - # ImagePullSecrets for control plane ServiceAccount, list of secrets in the same namespace - # to use for pulling any images in pods that reference this ServiceAccount. - # Must be set for any cluster configured with private docker registry. - imagePullSecrets: [] - - # Used to locate istiod. - istioNamespace: dubbo-system - - istiod: - enableAnalysis: false - - configValidation: true - externalIstiod: false - remotePilotAddress: "" - -base: - # Used for helm2 to add the CRDs to templates. - enableCRDTemplates: false - - # Validation webhook configuration url - # For example: https://$remotePilotAddress:15017/validate - validationURL: "" - - # For istioctl usage to disable istio config crds in base - enableIstioConfigCRDs: true - -defaultRevision: "dubbo" diff --git a/manifests/charts/default/Chart.yaml b/manifests/charts/default/Chart.yaml deleted file mode 100644 index 3d23a1b3a..000000000 --- a/manifests/charts/default/Chart.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -name: istio-default -# This version is never actually shipped. istio/release-builder will replace it at build-time -# with the appropriate version -version: 1.0.0 -appVersion: 1.0.0 -description: Helm chart for istio default revision components. -keywords: - - istio -sources: - - http://github.com/istio/istio -engine: gotpl -icon: https://istio.io/latest/favicons/android-192x192.png diff --git a/manifests/charts/default/templates/mutatingwebhook.yaml b/manifests/charts/default/templates/mutatingwebhook.yaml deleted file mode 100644 index 11c029bd0..000000000 --- a/manifests/charts/default/templates/mutatingwebhook.yaml +++ /dev/null @@ -1,122 +0,0 @@ -# Adapted from istio-discovery/templates/mutatingwebhook.yaml -# Removed paths for legacy and default selectors since a revision tag -# is inherently created from a specific revision -{{/* Copy just what we need to avoid expensive deepCopy */}} -{{- $whv := dict - "revision" .Values.revision - "injectionURL" .Values.istiodRemote.injectionURL - "namespace" .Release.Namespace }} -{{- define "core" }} -- name: {{.Prefix}}sidecar-injector.istio.io - clientConfig: - {{- if .injectionURL }} - url: {{ .injectionURL }} - {{- else }} - service: - name: istiod{{- if not (eq .revision "") }}-{{ .revision }}{{- end }} - namespace: {{ .namespace }} - path: "/inject" - {{- end }} - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] -{{- end }} - -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - name: istio-revision-tag-default - labels: - istio.io/tag: "default" - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Pilot" - app: sidecar-injector - release: {{ .Release.Name }} -webhooks: -{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "rev.namespace.") ) }} - namespaceSelector: - matchExpressions: - - key: istio.io/rev - operator: In - values: - - "default" - - key: istio-injection - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" -{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "rev.object.") ) }} - namespaceSelector: - matchExpressions: - - key: istio.io/rev - operator: DoesNotExist - - key: istio-injection - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" - - key: istio.io/rev - operator: In - values: - - "default" - -{{- /* Case 1: Namespace selector enabled, and object selector is not injected */}} -{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "namespace.") ) }} - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: In - values: - - enabled - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" - -{{- /* Case 2: no namespace label, but object selector is enabled (and revision label is not, which has priority) */}} -{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "object.") ) }} - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: DoesNotExist - - key: istio.io/rev - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: In - values: - - "true" - - key: istio.io/rev - operator: DoesNotExist - -{{- if .Values.sidecarInjectorWebhook.enableNamespacesByDefault }} -{{- /* Special case 3: no labels at all */}} -{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "auto.") ) }} - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: DoesNotExist - - key: istio.io/rev - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: DoesNotExist - - key: istio.io/rev - operator: DoesNotExist -{{- end }} diff --git a/manifests/charts/default/templates/validatingwebhook.yaml b/manifests/charts/default/templates/validatingwebhook.yaml deleted file mode 100644 index 94c4416b5..000000000 --- a/manifests/charts/default/templates/validatingwebhook.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - name: istiod-default-validator - labels: - app: istiod - istio: istiod - istio.io/rev: {{ .Values.revision | default "default" }} - istio.io/tag: "default" - # Required to make sure this resource is removed - # when purging Istio resources - operator.istio.io/component: Pilot -webhooks: - - name: validation.istio.io - clientConfig: - {{- if .Values.base.validationURL }} - url: {{ .Values.base.validationURL }} - {{- else }} - service: - name: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - namespace: {{ .Values.global.istioNamespace }} - path: "/validate" - {{- end }} - rules: - - operations: - - CREATE - - UPDATE - apiGroups: - - security.istio.io - - networking.istio.io - - telemetry.istio.io - - extensions.istio.io - apiVersions: - - "*" - resources: - - "*" - failurePolicy: Ignore - sideEffects: None - admissionReviewVersions: ["v1beta1", "v1"] - objectSelector: - matchExpressions: - - key: istio.io/rev - operator: DoesNotExist ---- diff --git a/manifests/charts/default/values.yaml b/manifests/charts/default/values.yaml deleted file mode 100644 index 5afd0b559..000000000 --- a/manifests/charts/default/values.yaml +++ /dev/null @@ -1,21 +0,0 @@ -global: - # Used to locate istiod. - istioNamespace: "dubbo-system" - -base: - # Validation webhook configuration url - # For example: https://$remotePilotAddress:15017/validate - validationURL: "" - -istiodRemote: - # Sidecar injector mutating webhook configuration url - # For example: https://$remotePilotAddress:15017/inject - injectionURL: "" - -# Revision is set as 'version' label and part of the resource names when installing multiple control planes. -revision: "" - -sidecarInjectorWebhook: - # This enables injection of sidecar in all namespaces, - enableNamespacesByDefault: false - diff --git a/manifests/charts/gateway/Chart.yaml b/manifests/charts/gateway/Chart.yaml deleted file mode 100644 index 1a9164028..000000000 --- a/manifests/charts/gateway/Chart.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: v2 -name: gateway -description: Helm chart for deploying Istio gateways -type: application - -# This version is never actually shipped. istio/release-builder will replace it at build-time -# with the appropriate version -version: 1.0.0 -appVersion: 1.0.0 - -sources: -- http://github.com/istio/istio -icon: https://istio.io/latest/favicons/android-192x192.png -keywords: -- istio -- gateways diff --git a/manifests/charts/gateway/README.md b/manifests/charts/gateway/README.md deleted file mode 100644 index 99e6e09d0..000000000 --- a/manifests/charts/gateway/README.md +++ /dev/null @@ -1,148 +0,0 @@ -# Istio Gateway Helm Chart - -This chart installs an Istio gateway deployment. - -## Setup Repo Info - -```console -helm repo add istio https://istio-release.storage.googleapis.com/charts -helm repo update -``` - -_See [helm repo](https://helm.sh/docs/helm/helm_repo/) for command documentation._ - -## Installing the Chart - -To install the chart with the release name `istio-ingressgateway`: - -```console -helm install istio-ingressgateway istio/gateway -``` - -## Uninstalling the Chart - -To uninstall/delete the `istio-ingressgateway` deployment: - -```console -helm delete istio-ingressgateway -``` - -## Configuration - -To view support configuration options and documentation, run: - -```console -helm show values istio/gateway -``` - -### `image: auto` Information - -The image used by the chart, `auto`, may be unintuitive. -This exists because the pod spec will be automatically populated at runtime, using the same mechanism as [Sidecar Injection](istio.io/latest/docs/setup/additional-setup/sidecar-injection). -This allows the same configurations and lifecycle to apply to gateways as sidecars. - -Note: this does mean that the namespace the gateway is deployed in must not have the `istio-injection=disabled` label. -See [Controlling the injection policy](https://istio.io/latest/docs/setup/additional-setup/sidecar-injection/#controlling-the-injection-policy) for more info. - -### Examples - -#### Egress Gateway - -Deploying a Gateway to be used as an [Egress Gateway](https://istio.io/latest/docs/tasks/traffic-management/egress/egress-gateway/): - -```yaml -service: - # Egress gateways do not need an external LoadBalancer IP - type: ClusterIP -``` - -#### Multi-network/VM Gateway - -Deploying a Gateway to be used as a [Multi-network Gateway](https://istio.io/latest/docs/setup/install/multicluster/) for network `network-1`: - -```yaml -networkGateway: network-1 -``` - -### Migrating from other installation methods - -Installations from other installation methods (such as istioctl, Istio Operator, other helm charts, etc) can be migrated to use the new Helm charts -following the guidance below. -If you are able to, a clean installation is simpler. However, this often requires an external IP migration which can be challenging. - -WARNING: when installing over an existing deployment, the two deployments will be merged together by Helm, which may lead to unexpected results. - -#### Legacy Gateway Helm charts - -Istio historically offered two different charts - `manifests/charts/gateways/istio-ingress` and `manifests/charts/gateways/istio-egress`. -These are replaced by this chart. -While not required, it is recommended all new users use this chart, and existing users migrate when possible. - -This chart has the following benefits and differences: -* Designed with Helm best practices in mind (standardized values options, values schema, values are not all nested under `gateways.istio-ingressgateway.*`, release name and namespace taken into account, etc). -* Utilizes Gateway injection, simplifying upgrades, allowing gateways to run in any namespace, and avoiding repeating config for sidecars and gateways. -* Published to official Istio Helm repository. -* Single chart for all gateways (Ingress, Egress, East West). - -#### General concerns - -For a smooth migration, the resource names and `Deployment.spec.selector` labels must match. - -If you install with `helm install istio-gateway istio/gateway`, resources will be named `istio-gateway` and the `selector` labels set to: - -```yaml -app: istio-gateway -istio: gateway # the release name with leading istio- prefix stripped -``` - -If your existing installation doesn't follow these names, you can override them. For example, if you have resources named `my-custom-gateway` with `selector` labels -`foo=bar,istio=ingressgateway`: - -```yaml -name: my-custom-gateway # Override the name to match existing resources -labels: - app: "" # Unset default app selector label - istio: ingressgateway # override default istio selector label - foo: bar # Add the existing custom selector label -``` - -#### Migrating an existing Helm release - -An existing helm release can be `helm upgrade`d to this chart by using the same release name. For example, if a previous -installation was done like: - -```console -helm install istio-ingress manifests/charts/gateways/istio-ingress -n dubbo-system -``` - -It could be upgraded with - -```console -helm upgrade istio-ingress manifests/charts/gateway -n dubbo-system --set name=istio-ingressgateway --set labels.app=istio-ingressgateway --set labels.istio=ingressgateway -``` - -Note the name and labels are overridden to match the names of the existing installation. - -Warning: the helm charts here default to using port 80 and 443, while the old charts used 8080 and 8443. -If you have AuthorizationPolicies that reference port these ports, you should update them during this process, -or customize the ports to match the old defaults. -See the [security advisory](https://istio.io/latest/news/security/istio-security-2021-002/) for more information. - -#### Other migrations - -If you see errors like `rendered manifests contain a resource that already exists` during installation, you may need to forcibly take ownership. - -The script below can handle this for you. Replace `RELEASE` and `NAMESPACE` with the name and namespace of the release: - -```console -KINDS=(service deployment) -RELEASE=istio-ingressgateway -NAMESPACE=dubbo-system -for KIND in "${KINDS[@]}"; do - kubectl --namespace $NAMESPACE --overwrite=true annotate $KIND $RELEASE meta.helm.sh/release-name=$RELEASE - kubectl --namespace $NAMESPACE --overwrite=true annotate $KIND $RELEASE meta.helm.sh/release-namespace=$NAMESPACE - kubectl --namespace $NAMESPACE --overwrite=true label $KIND $RELEASE app.kubernetes.io/managed-by=Helm -done -``` - -You may ignore errors about resources not being found. diff --git a/manifests/charts/gateway/templates/NOTES.txt b/manifests/charts/gateway/templates/NOTES.txt deleted file mode 100644 index 78451d33e..000000000 --- a/manifests/charts/gateway/templates/NOTES.txt +++ /dev/null @@ -1,9 +0,0 @@ -"{{ include "gateway.name" . }}" successfully installed! - -To learn more about the release, try: - $ helm status {{ .Release.Name }} - $ helm get all {{ .Release.Name }} - -Next steps: - * Deploy an HTTP Gateway: https://istio.io/latest/docs/tasks/traffic-management/ingress/ingress-control/ - * Deploy an HTTPS Gateway: https://istio.io/latest/docs/tasks/traffic-management/ingress/secure-ingress/ diff --git a/manifests/charts/gateway/templates/_helpers.tpl b/manifests/charts/gateway/templates/_helpers.tpl deleted file mode 100644 index e75d27345..000000000 --- a/manifests/charts/gateway/templates/_helpers.tpl +++ /dev/null @@ -1,52 +0,0 @@ -{{- define "gateway.name" -}} -{{- if eq .Release.Name "RELEASE-NAME" -}} - {{- .Values.name | default "istio-ingressgateway" -}} -{{- else -}} - {{- .Values.name | default .Release.Name | default "istio-ingressgateway" -}} -{{- end -}} -{{- end }} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "gateway.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{- define "gateway.labels" -}} -helm.sh/chart: {{ include "gateway.chart" . }} -{{ include "gateway.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -app.kubernetes.io/name: {{ include "gateway.name" . }} -{{- range $key, $val := .Values.labels }} -{{- if not (or (eq $key "app") (eq $key "istio")) }} -{{ $key | quote }}: {{ $val | quote }} -{{- end }} -{{- end }} -{{- end }} - -{{- define "gateway.selectorLabels" -}} -{{- if hasKey .Values.labels "app" }} -{{- with .Values.labels.app }}app: {{.|quote}} -{{- end}} -{{- else }}app: {{ include "gateway.name" . }} -{{- end }} -{{- if hasKey .Values.labels "istio" }} -{{- with .Values.labels.istio }} -istio: {{.|quote}} -{{- end}} -{{- else }} -istio: {{ include "gateway.name" . | trimPrefix "istio-" }} -{{- end }} -{{- end }} - -{{- define "gateway.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- .Values.serviceAccount.name | default (include "gateway.name" .) }} -{{- else }} -{{- .Values.serviceAccount.name | default "default" }} -{{- end }} -{{- end }} diff --git a/manifests/charts/gateway/templates/deployment.yaml b/manifests/charts/gateway/templates/deployment.yaml deleted file mode 100644 index 2f2d529f8..000000000 --- a/manifests/charts/gateway/templates/deployment.yaml +++ /dev/null @@ -1,100 +0,0 @@ -apiVersion: apps/v1 -kind: {{ .Values.kind | default "Deployment" }} -metadata: - name: {{ include "gateway.name" . }} - namespace: {{ .Release.Namespace }} - labels: - {{- include "gateway.labels" . | nindent 4}} - annotations: - {{- .Values.annotations | toYaml | nindent 4 }} -spec: - {{- if not .Values.autoscaling.enabled }} - replicas: {{ .Values.replicaCount }} - {{- end }} - selector: - matchLabels: - {{- include "gateway.selectorLabels" . | nindent 6 }} - template: - metadata: - {{- with .Values.podAnnotations }} - annotations: - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - sidecar.istio.io/inject: "true" - {{- with .Values.revision }} - istio.io/rev: {{ . }} - {{- end }} - {{- include "gateway.selectorLabels" . | nindent 8 }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include "gateway.serviceAccountName" . }} - securityContext: - {{- if .Values.securityContext }} - {{- toYaml .Values.securityContext | nindent 8 }} - {{- else if (semverCompare ">=1.22-0" .Capabilities.KubeVersion.GitVersion) }} - # Safe since 1.22: https://github.com/kubernetes/kubernetes/pull/103326 - sysctls: - - name: net.ipv4.ip_unprivileged_port_start - value: "0" - {{- end }} - containers: - - name: istio-proxy - # "auto" will be populated at runtime by the mutating webhook. See https://istio.io/latest/docs/setup/additional-setup/sidecar-injection/#customizing-injection - image: auto - securityContext: - {{- if .Values.containerSecurityContext }} - {{- toYaml .Values.containerSecurityContext | nindent 12 }} - {{- else if (semverCompare ">=1.22-0" .Capabilities.KubeVersion.GitVersion) }} - # Safe since 1.22: https://github.com/kubernetes/kubernetes/pull/103326 - capabilities: - drop: - - ALL - allowPrivilegeEscalation: false - privileged: false - readOnlyRootFilesystem: true - runAsUser: 1337 - runAsGroup: 1337 - runAsNonRoot: true - {{- else }} - capabilities: - drop: - - ALL - add: - - NET_BIND_SERVICE - runAsUser: 0 - runAsGroup: 1337 - runAsNonRoot: false - allowPrivilegeEscalation: true - readOnlyRootFilesystem: true - {{- end }} - env: - {{- with .Values.networkGateway }} - - name: ISTIO_META_REQUESTED_NETWORK_VIEW - value: "{{.}}" - {{- end }} - {{- range $key, $val := .Values.env }} - - name: {{ $key }} - value: {{ $val | quote }} - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - resources: - {{- toYaml .Values.resources | nindent 12 }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} diff --git a/manifests/charts/gateway/templates/hpa.yaml b/manifests/charts/gateway/templates/hpa.yaml deleted file mode 100644 index e287406c4..000000000 --- a/manifests/charts/gateway/templates/hpa.yaml +++ /dev/null @@ -1,31 +0,0 @@ -{{- if and (.Values.autoscaling.enabled) (eq .Values.kind "Deployment") }} -{{- if (semverCompare ">=1.23-0" .Capabilities.KubeVersion.GitVersion)}} -apiVersion: autoscaling/v2 -{{- else }} -apiVersion: autoscaling/v2beta2 -{{- end }} -kind: HorizontalPodAutoscaler -metadata: - name: {{ include "gateway.name" . }} - namespace: {{ .Release.Namespace }} - labels: - {{- include "gateway.labels" . | nindent 4 }} - annotations: - {{- .Values.annotations | toYaml | nindent 4 }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: {{ .Values.kind | default "Deployment" }} - name: {{ include "gateway.name" . }} - minReplicas: {{ .Values.autoscaling.minReplicas }} - maxReplicas: {{ .Values.autoscaling.maxReplicas }} - metrics: - {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - target: - averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} - type: Utilization - {{- end }} -{{- end }} diff --git a/manifests/charts/gateway/templates/role.yaml b/manifests/charts/gateway/templates/role.yaml deleted file mode 100644 index 3febf79bc..000000000 --- a/manifests/charts/gateway/templates/role.yaml +++ /dev/null @@ -1,25 +0,0 @@ -{{/*Set up roles for Istio Gateway. Not required for gateway-api*/}} -{{- if .Values.rbac.enabled }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: {{ include "gateway.serviceAccountName" . }} - namespace: {{ .Release.Namespace }} -rules: -- apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "watch", "list"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: {{ include "gateway.serviceAccountName" . }} - namespace: {{ .Release.Namespace }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: {{ include "gateway.serviceAccountName" . }} -subjects: -- kind: ServiceAccount - name: {{ include "gateway.serviceAccountName" . }} -{{- end }} diff --git a/manifests/charts/gateway/templates/service.yaml b/manifests/charts/gateway/templates/service.yaml deleted file mode 100644 index b96a52b90..000000000 --- a/manifests/charts/gateway/templates/service.yaml +++ /dev/null @@ -1,50 +0,0 @@ -{{- if not (eq .Values.service.type "None") }} -apiVersion: v1 -kind: Service -metadata: - name: {{ include "gateway.name" . }} - namespace: {{ .Release.Namespace }} - labels: - {{- include "gateway.labels" . | nindent 4 }} - {{- with .Values.networkGateway }} - topology.istio.io/network: "{{.}}" - {{- end }} - annotations: - {{- merge (deepCopy .Values.service.annotations) .Values.annotations | toYaml | nindent 4 }} -spec: -{{- with .Values.service.loadBalancerIP }} - loadBalancerIP: "{{ . }}" -{{- end }} -{{- with .Values.service.loadBalancerSourceRanges }} - loadBalancerSourceRanges: -{{ toYaml . | indent 4 }} -{{- end }} -{{- with .Values.service.externalTrafficPolicy }} - externalTrafficPolicy: "{{ . }}" -{{- end }} - type: {{ .Values.service.type }} - ports: -{{- if .Values.networkGateway }} - - name: status-port - port: 15021 - targetPort: 15021 - - name: tls - port: 15443 - targetPort: 15443 - - name: tls-istiod - port: 15012 - targetPort: 15012 - - name: tls-webhook - port: 15017 - targetPort: 15017 -{{- else }} -{{ .Values.service.ports | toYaml | indent 4 }} -{{- end }} -{{- if .Values.service.externalIPs }} - externalIPs: {{- range .Values.service.externalIPs }} - - {{.}} - {{- end }} -{{- end }} - selector: - {{- include "gateway.selectorLabels" . | nindent 4 }} -{{- end }} diff --git a/manifests/charts/gateway/templates/serviceaccount.yaml b/manifests/charts/gateway/templates/serviceaccount.yaml deleted file mode 100644 index e5b2304d6..000000000 --- a/manifests/charts/gateway/templates/serviceaccount.yaml +++ /dev/null @@ -1,13 +0,0 @@ -{{- if .Values.serviceAccount.create }} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "gateway.serviceAccountName" . }} - namespace: {{ .Release.Namespace }} - labels: - {{- include "gateway.labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -{{- end }} diff --git a/manifests/charts/gateway/values.schema.json b/manifests/charts/gateway/values.schema.json deleted file mode 100644 index 9feb760cf..000000000 --- a/manifests/charts/gateway/values.schema.json +++ /dev/null @@ -1,197 +0,0 @@ -{ - "$schema": "http://json-schema.org/schema#", - "type": "object", - "additionalProperties": false, - "properties": { - "global": { - "type": "object" - }, - "affinity": { - "type": "object" - }, - "securityContext": { - "type": ["object", "null"] - }, - "containerSecurityContext": { - "type": ["object", "null"] - }, - "kind":{ - "type": "string", - "enum": ["Deployment", "DaemonSet"] - }, - "annotations": { - "additionalProperties": { - "type": [ - "string", - "integer" - ] - }, - "type": "object" - }, - "autoscaling": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "maxReplicas": { - "type": "integer" - }, - "minReplicas": { - "type": "integer" - }, - "targetCPUUtilizationPercentage": { - "type": "integer" - } - } - }, - "env": { - "type": "object" - }, - "labels": { - "type": "object" - }, - "name": { - "type": "string" - }, - "nodeSelector": { - "type": "object" - }, - "podAnnotations": { - "type": "object", - "properties": { - "inject.istio.io/templates": { - "type": "string" - }, - "prometheus.io/path": { - "type": "string" - }, - "prometheus.io/port": { - "type": "string" - }, - "prometheus.io/scrape": { - "type": "string" - } - } - }, - "replicaCount": { - "type": "integer" - }, - "resources": { - "type": "object", - "properties": { - "limits": { - "type": "object", - "properties": { - "cpu": { - "type": "string" - }, - "memory": { - "type": "string" - } - } - }, - "requests": { - "type": "object", - "properties": { - "cpu": { - "type": "string" - }, - "memory": { - "type": "string" - } - } - } - } - }, - "revision": { - "type": "string" - }, - "runAsRoot": { - "type": "boolean" - }, - "unprivilegedPort": { - "type": ["string", "boolean"], - "enum": [true, false, "auto"] - }, - "service": { - "type": "object", - "properties": { - "annotations": { - "type": "object" - }, - "externalTrafficPolicy": { - "type": "string" - }, - "loadBalancerIP": { - "type": "string" - }, - "loadBalancerSourceRanges": { - "type": "array" - }, - "ports": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "port": { - "type": "integer" - }, - "protocol": { - "type": "string" - }, - "targetPort": { - "type": "integer" - } - } - } - }, - "type": { - "type": "string" - } - } - }, - "serviceAccount": { - "type": "object", - "properties": { - "annotations": { - "type": "object" - }, - "name": { - "type": "string" - }, - "create": { - "type": "boolean" - } - } - }, - "rbac": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - } - } - }, - "tolerations": { - "type": "array" - }, - "networkGateway": { - "type": "string" - }, - "imagePullSecrets": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - } - } - } - } - } -} diff --git a/manifests/charts/gateway/values.yaml b/manifests/charts/gateway/values.yaml deleted file mode 100644 index 9967b5320..000000000 --- a/manifests/charts/gateway/values.yaml +++ /dev/null @@ -1,91 +0,0 @@ -# Name allows overriding the release name. Generally this should not be set -name: "" -# revision declares which revision this gateway is a part of -revision: "" - -replicaCount: 1 - -kind: Deployment - -rbac: - # If enabled, roles will be created to enable accessing certificates from Gateways. This is not needed - # when using http://gateway-api.org/. - enabled: true - -serviceAccount: - # If set, a service account will be created. Otherwise, the default is used - create: true - # Annotations to add to the service account - annotations: {} - # The name of the service account to use. - # If not set, the release name is used - name: "" - -podAnnotations: - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - prometheus.io/path: "/stats/prometheus" - inject.istio.io/templates: "gateway" - sidecar.istio.io/inject: "true" - -# Define the security context for the pod. -# If unset, this will be automatically set to the minimum privileges required to bind to port 80 and 443. -# On Kubernetes 1.22+, this only requires the `net.ipv4.ip_unprivileged_port_start` sysctl. -securityContext: ~ -containerSecurityContext: ~ - -service: - # Type of service. Set to "None" to disable the service entirely - type: LoadBalancer - ports: - - name: status-port - port: 15021 - protocol: TCP - targetPort: 15021 - - name: http2 - port: 80 - protocol: TCP - targetPort: 80 - - name: https - port: 443 - protocol: TCP - targetPort: 443 - annotations: {} - loadBalancerIP: "" - loadBalancerSourceRanges: [] - externalTrafficPolicy: "" - externalIPs: [] - -resources: - requests: - cpu: 100m - memory: 128Mi - limits: - cpu: 2000m - memory: 1024Mi - -autoscaling: - enabled: true - minReplicas: 1 - maxReplicas: 5 - targetCPUUtilizationPercentage: 80 - -# Pod environment variables -env: {} - -# Labels to apply to all resources -labels: {} - -# Annotations to apply to all resources -annotations: {} - -nodeSelector: {} - -tolerations: [] - -affinity: {} - -# If specified, the gateway will act as a network gateway for the given network. -networkGateway: "" - -imagePullSecrets: [] diff --git a/manifests/charts/gateways/istio-egress/Chart.yaml b/manifests/charts/gateways/istio-egress/Chart.yaml deleted file mode 100644 index 2e24125f8..000000000 --- a/manifests/charts/gateways/istio-egress/Chart.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: v1 -name: istio-egress -# This version is never actually shipped. istio/release-builder will replace it at build-time -# with the appropriate version -version: 1.0.0 -appVersion: 1.0.0 -tillerVersion: ">=2.7.2" -description: Helm chart for deploying Istio gateways -keywords: - - istio - - egressgateway - - gateways -sources: - - http://github.com/istio/istio -engine: gotpl -icon: https://istio.io/latest/favicons/android-192x192.png diff --git a/manifests/charts/gateways/istio-egress/NOTES.txt b/manifests/charts/gateways/istio-egress/NOTES.txt deleted file mode 100644 index 941869eb3..000000000 --- a/manifests/charts/gateways/istio-egress/NOTES.txt +++ /dev/null @@ -1,45 +0,0 @@ - -Changes: -- separate namespace allows: --- easier reconfig of just the gateway --- TLS secrets and domain name management is isolated, for better security --- simplified configuration --- multiple versions of the ingress can be used, to minize upgrade risks - -- the new chart uses the default namespace service account, and doesn't require -additional RBAC permissions. - -- simplified label structure. Label change is not supported on upgrade. - -- for 'internal load balancer' you should deploy a separate gateway, in a different -namespace. - -All ingress gateway have a "app:ingressgateway" label, used to identify it as an -ingress, and an "istio: ingressgateway$SUFFIX" label of Gateway selection. - -The Gateways use "istio: ingressgateway$SUFFIX" selectors. - - -# Multiple gateway versions - - - -# Using different pilot versions - - - -# Migration from dubbo-system - -Istio 1.0 includes the gateways in dubbo-system. Since the external IP is associated -with the Service and bound to the namespace, it is recommended to: - -1. Install the new gateway in a new namespace. -2. Copy any TLS certificate to the new namespace, and configure the domains. -3. Checking the new gateway work - for example by overriding the IP in /etc/hosts -4. Modify the DNS server to add the A record of the new namespace -5. Check traffic -6. Delete the A record corresponding to the gateway in dubbo-system -7. Upgrade dubbo-system, disabling the ingressgateway -8. Delete the domain TLS certs from dubbo-system. - -If using certmanager, all Certificate and associated configs must be moved as well. diff --git a/manifests/charts/gateways/istio-egress/templates/_affinity.tpl b/manifests/charts/gateways/istio-egress/templates/_affinity.tpl deleted file mode 100644 index 3d6c896a6..000000000 --- a/manifests/charts/gateways/istio-egress/templates/_affinity.tpl +++ /dev/null @@ -1,102 +0,0 @@ -{{/* affinity - https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ */}} - -{{ define "nodeaffinity" }} -nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - {{- include "nodeAffinityRequiredDuringScheduling" . }} - preferredDuringSchedulingIgnoredDuringExecution: - {{- include "nodeAffinityPreferredDuringScheduling" . }} -{{- end }} - -{{- define "nodeAffinityRequiredDuringScheduling" }} - {{- $nodeSelector := default .global.defaultNodeSelector .nodeSelector -}} - {{- if or .global.arch $nodeSelector }} - nodeSelectorTerms: - - matchExpressions: - {{- range $key, $val := .global.arch }} - - key: kubernetes.io/arch - operator: In - values: - {{- if gt ($val | int) 0 }} - - {{ $key | quote }} - {{- end }} - {{- end }} - {{- range $key, $val := $nodeSelector }} - - key: {{ $key }} - operator: In - values: - - {{ $val | quote }} - {{- end }} - {{- end }} -{{- end }} - -{{- define "nodeAffinityPreferredDuringScheduling" }} - {{- range $key, $val := .global.arch }} - {{- if gt ($val | int) 0 }} - - weight: {{ $val | int }} - preference: - matchExpressions: - - key: kubernetes.io/arch - operator: In - values: - - {{ $key | quote }} - {{- end }} - {{- end }} -{{- end }} - -{{- define "podAntiAffinity" }} -{{- if or .podAntiAffinityLabelSelector .podAntiAffinityTermLabelSelector}} - podAntiAffinity: - {{- if .podAntiAffinityLabelSelector }} - requiredDuringSchedulingIgnoredDuringExecution: - {{- include "podAntiAffinityRequiredDuringScheduling" . }} - {{- end }} - {{- if .podAntiAffinityTermLabelSelector }} - preferredDuringSchedulingIgnoredDuringExecution: - {{- include "podAntiAffinityPreferredDuringScheduling" . }} - {{- end }} -{{- end }} -{{- end }} - -{{- define "podAntiAffinityRequiredDuringScheduling" }} - {{- range $index, $item := .podAntiAffinityLabelSelector }} - - labelSelector: - matchExpressions: - - key: {{ $item.key }} - operator: {{ $item.operator }} - {{- if $item.values }} - values: - {{- $vals := split "," $item.values }} - {{- range $i, $v := $vals }} - - {{ $v | quote }} - {{- end }} - {{- end }} - topologyKey: {{ $item.topologyKey }} - {{- if $item.namespaces }} - namespaces: - {{- $ns := split "," $item.namespaces }} - {{- range $i, $n := $ns }} - - {{ $n | quote }} - {{- end }} - {{- end }} - {{- end }} -{{- end }} - -{{- define "podAntiAffinityPreferredDuringScheduling" }} - {{- range $index, $item := .podAntiAffinityTermLabelSelector }} - - podAffinityTerm: - labelSelector: - matchExpressions: - - key: {{ $item.key }} - operator: {{ $item.operator }} - {{- if $item.values }} - values: - {{- $vals := split "," $item.values }} - {{- range $i, $v := $vals }} - - {{ $v | quote }} - {{- end }} - {{- end }} - topologyKey: {{ $item.topologyKey }} - weight: 100 - {{- end }} -{{- end }} diff --git a/manifests/charts/gateways/istio-egress/templates/autoscale.yaml b/manifests/charts/gateways/istio-egress/templates/autoscale.yaml deleted file mode 100644 index aa901685a..000000000 --- a/manifests/charts/gateways/istio-egress/templates/autoscale.yaml +++ /dev/null @@ -1,60 +0,0 @@ -{{ $gateway := index .Values "gateways" "istio-egressgateway" }} -{{- if and $gateway.autoscaleEnabled $gateway.autoscaleMin $gateway.autoscaleMax }} -{{- if not .Values.global.autoscalingv2API }} -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: {{ $gateway.name }} - namespace: {{ .Release.Namespace }} - labels: -{{ $gateway.labels | toYaml | indent 4 }} - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "EgressGateways" -spec: - maxReplicas: {{ $gateway.autoscaleMax }} - minReplicas: {{ $gateway.autoscaleMin }} - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ $gateway.name }} - metrics: - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ $gateway.cpu.targetAverageUtilization }} ---- -{{- else }} -{{- if (semverCompare ">=1.23-0" .Capabilities.KubeVersion.GitVersion)}} -apiVersion: autoscaling/v2 -{{- else }} -apiVersion: autoscaling/v2beta2 -{{- end }} -kind: HorizontalPodAutoscaler -metadata: - name: {{ $gateway.name }} - namespace: {{ .Release.Namespace }} - labels: -{{ $gateway.labels | toYaml | indent 4 }} - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "EgressGateways" -spec: - maxReplicas: {{ $gateway.autoscaleMax }} - minReplicas: {{ $gateway.autoscaleMin }} - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ $gateway.name }} - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: {{ $gateway.cpu.targetAverageUtilization }} ---- -{{- end }} -{{- end }} diff --git a/manifests/charts/gateways/istio-egress/templates/deployment.yaml b/manifests/charts/gateways/istio-egress/templates/deployment.yaml deleted file mode 100644 index 3334fefa1..000000000 --- a/manifests/charts/gateways/istio-egress/templates/deployment.yaml +++ /dev/null @@ -1,335 +0,0 @@ -{{- $gateway := index .Values "gateways" "istio-egressgateway" }} -{{- if eq $gateway.injectionTemplate "" }} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ $gateway.name }} - namespace: {{ .Release.Namespace }} - labels: -{{ $gateway.labels | toYaml | indent 4 }} - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "EgressGateways" -spec: -{{- if not $gateway.autoscaleEnabled }} -{{- if $gateway.replicaCount }} - replicas: {{ $gateway.replicaCount }} -{{- end }} -{{- end }} - selector: - matchLabels: -{{ $gateway.labels | toYaml | indent 6 }} - strategy: - rollingUpdate: - maxSurge: {{ $gateway.rollingMaxSurge }} - maxUnavailable: {{ $gateway.rollingMaxUnavailable }} - template: - metadata: - labels: -{{ $gateway.labels | toYaml | indent 8 }} -{{- if eq .Release.Namespace "dubbo-system"}} - heritage: Tiller - release: istio - chart: gateways -{{- end }} - service.istio.io/canonical-name: {{ $gateway.name }} - {{- if not (eq .Values.revision "") }} - service.istio.io/canonical-revision: {{ .Values.revision }} - {{- else}} - service.istio.io/canonical-revision: latest - {{- end }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "EgressGateways" - sidecar.istio.io/inject: "false" - annotations: - {{- if .Values.meshConfig.enablePrometheusMerge }} - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - prometheus.io/path: "/stats/prometheus" - {{- end }} - sidecar.istio.io/inject: "false" -{{- if $gateway.podAnnotations }} -{{ toYaml $gateway.podAnnotations | indent 8 }} -{{ end }} - spec: -{{- if not $gateway.runAsRoot }} - securityContext: - runAsUser: 1337 - runAsGroup: 1337 - runAsNonRoot: true - fsGroup: 1337 -{{- end }} - serviceAccountName: {{ $gateway.name }}-service-account -{{- if .Values.global.priorityClassName }} - priorityClassName: "{{ .Values.global.priorityClassName }}" -{{- end }} -{{- if .Values.global.proxy.enableCoreDump }} - initContainers: - - name: enable-core-dump -{{- if contains "/" .Values.global.proxy.image }} - image: "{{ .Values.global.proxy.image }}" -{{- else }} - image: "{{ .Values.global.hub }}/{{ .Values.global.proxy.image | default "dubbo-agent" }}:{{ .Values.global.tag }}" -{{- end }} -{{- if .Values.global.imagePullPolicy }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} -{{- end }} - command: - - /bin/sh - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - securityContext: - runAsUser: 0 - runAsGroup: 0 - runAsNonRoot: false - privileged: true -{{- end }} - containers: - - name: istio-proxy -{{- if contains "/" .Values.global.proxy.image }} - image: "{{ .Values.global.proxy.image }}" -{{- else }} - image: "{{ .Values.global.hub }}/{{ .Values.global.proxy.image | default "dubbo-agent" }}:{{ .Values.global.tag }}" -{{- end }} -{{- if .Values.global.imagePullPolicy }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} -{{- end }} - ports: - {{- range $key, $val := $gateway.ports }} - - containerPort: {{ $val.targetPort | default $val.port }} - protocol: {{ $val.protocol | default "TCP" }} - {{- end }} - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - {{- if .Values.global.proxy.logLevel }} - - --proxyLogLevel={{ .Values.global.proxy.logLevel }} - {{- end}} - {{- if .Values.global.proxy.componentLogLevel }} - - --proxyComponentLogLevel={{ .Values.global.proxy.componentLogLevel }} - {{- end}} - {{- if .Values.global.logging.level }} - - --log_output_level={{ .Values.global.logging.level }} - {{- end}} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if not $gateway.runAsRoot }} - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - {{- end }} - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - scheme: HTTP - initialDelaySeconds: 1 - periodSeconds: 2 - successThreshold: 1 - timeoutSeconds: 1 - resources: -{{- if $gateway.resources }} -{{ toYaml $gateway.resources | indent 12 }} -{{- else }} -{{ toYaml .Values.global.defaultResources | indent 12 }} -{{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: NODE_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.nodeName - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: status.podIP - - name: HOST_IP - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: status.hostIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: ISTIO_META_WORKLOAD_NAME - value: {{ $gateway.name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/{{ .Release.Namespace }}/deployments/{{ $gateway.name }} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if .Values.meshConfig.trustDomain }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.meshConfig.trustDomain }}" - {{- end }} - {{- if .Values.meshConfig.trustDomain }} - - name: TRUST_DOMAIN - value: "{{ .Values.meshConfig.trustDomain }}" - {{- end }} - {{- if not $gateway.runAsRoot }} - - name: ISTIO_META_UNPRIVILEGED_POD - value: "true" - {{- end }} - {{- range $key, $val := $gateway.env }} - - name: {{ $key }} - value: "{{ $val }}" - {{- end }} - {{- range $key, $value := .Values.meshConfig.defaultConfig.proxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- $network_set := index $gateway.env "ISTIO_META_NETWORK" }} - {{- if and (not $network_set) .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ $.Values.global.multiCluster.clusterName | default `Kubernetes` }}" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - - name: istio-envoy - mountPath: /etc/istio/proxy - - name: config-volume - mountPath: /etc/istio/config -{{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert -{{- end }} -{{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - mountPath: /var/run/secrets/tokens - readOnly: true -{{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - mountPath: /etc/certs - readOnly: true - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - - name: podinfo - mountPath: /etc/istio/pod - {{- range $gateway.secretVolumes }} - - name: {{ .name }} - mountPath: {{ .mountPath | quote }} - readOnly: true - {{- end }} - {{- range $gateway.configVolumes }} - {{- if .mountPath }} - - name: {{ .name }} - mountPath: {{ .mountPath | quote }} - readOnly: true - {{- end }} - {{- end }} -{{- if $gateway.additionalContainers }} -{{ toYaml $gateway.additionalContainers | indent 8 }} -{{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs -{{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert -{{- end }} - - name: podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - - name: istio-envoy - emptyDir: {} - - name: istio-data - emptyDir: {} -{{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} -{{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - secretName: istio.istio-egressgateway-service-account - optional: true - {{- end }} - - name: config-volume - configMap: - name: istio{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - optional: true - {{- range $gateway.secretVolumes }} - - name: {{ .name }} - secret: - secretName: {{ .secretName | quote }} - optional: true - {{- end }} - {{- range $gateway.configVolumes }} - - name: {{ .name }} - configMap: - name: {{ .configMapName | quote }} - optional: true - {{- end }} - affinity: -{{ include "nodeaffinity" (dict "global" .Values.global "nodeSelector" $gateway.nodeSelector) | trim | indent 8 }} - {{- include "podAntiAffinity" $gateway | indent 6 }} -{{- if $gateway.tolerations }} - tolerations: -{{ toYaml $gateway.tolerations | indent 6 }} -{{- else if .Values.global.defaultTolerations }} - tolerations: -{{ toYaml .Values.global.defaultTolerations | indent 6 }} -{{- end }} -{{- end }} diff --git a/manifests/charts/gateways/istio-egress/templates/injected-deployment.yaml b/manifests/charts/gateways/istio-egress/templates/injected-deployment.yaml deleted file mode 100644 index 03984e981..000000000 --- a/manifests/charts/gateways/istio-egress/templates/injected-deployment.yaml +++ /dev/null @@ -1,143 +0,0 @@ -{{- $gateway := index .Values "gateways" "istio-egressgateway" }} -{{- if ne $gateway.injectionTemplate "" }} -{{/* This provides a minimal gateway, ready to be injected. - Any settings from values.gateways should be here - these are options specific to the gateway. - Global settings, like the image, various env vars and volumes, etc will be injected. - The normal Deployment is not suitable for this, as the original pod spec will override the injection template. */}} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ $gateway.name | default "istio-egressgateway" }} - namespace: {{ .Release.Namespace }} - labels: -{{ $gateway.labels | toYaml | indent 4 }} - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "EgressGateways" -spec: -{{- if not $gateway.autoscaleEnabled }} -{{- if $gateway.replicaCount }} - replicas: {{ $gateway.replicaCount }} -{{- end }} -{{- end }} - selector: - matchLabels: -{{ $gateway.labels | toYaml | indent 6 }} - strategy: - rollingUpdate: - maxSurge: {{ $gateway.rollingMaxSurge }} - maxUnavailable: {{ $gateway.rollingMaxUnavailable }} - template: - metadata: - labels: -{{ $gateway.labels | toYaml | indent 8 }} -{{- if eq .Release.Namespace "dubbo-system"}} - heritage: Tiller - release: istio - chart: gateways -{{- end }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "EgressGateways" - sidecar.istio.io/inject: "true" - {{- with .Values.revision }} - istio.io/rev: {{ . }} - {{- end }} - annotations: - {{- if .Values.meshConfig.enablePrometheusMerge }} - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - prometheus.io/path: "/stats/prometheus" - {{- end }} - sidecar.istio.io/inject: "true" - inject.istio.io/templates: "{{ $gateway.injectionTemplate }}" -{{- if $gateway.podAnnotations }} -{{ toYaml $gateway.podAnnotations | indent 8 }} -{{ end }} - spec: -{{- if not $gateway.runAsRoot }} - securityContext: - runAsUser: 1337 - runAsGroup: 1337 - runAsNonRoot: true - fsGroup: 1337 -{{- end }} - serviceAccountName: {{ $gateway.name | default "istio-egressgateway" }}-service-account -{{- if .Values.global.priorityClassName }} - priorityClassName: "{{ .Values.global.priorityClassName }}" -{{- end }} - containers: - - name: istio-proxy - image: auto - ports: - {{- range $key, $val := $gateway.ports }} - - containerPort: {{ $val.targetPort | default $val.port }} - protocol: {{ $val.protocol | default "TCP" }} - {{- end }} - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - {{- if not $gateway.runAsRoot }} - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - {{- end }} - resources: -{{- if $gateway.resources }} -{{ toYaml $gateway.resources | indent 12 }} -{{- else }} -{{ toYaml .Values.global.defaultResources | indent 12 }} -{{- end }} - env: - {{- if not $gateway.runAsRoot }} - - name: ISTIO_META_UNPRIVILEGED_POD - value: "true" - {{- end }} - {{- range $key, $val := $gateway.env }} - - name: {{ $key }} - value: {{ $val | quote }} - {{- end }} - volumeMounts: - {{- range $gateway.secretVolumes }} - - name: {{ .name }} - mountPath: {{ .mountPath | quote }} - readOnly: true - {{- end }} - {{- range $gateway.configVolumes }} - {{- if .mountPath }} - - name: {{ .name }} - mountPath: {{ .mountPath | quote }} - readOnly: true - {{- end }} - {{- end }} -{{- if $gateway.additionalContainers }} -{{ toYaml $gateway.additionalContainers | indent 8 }} -{{- end }} - volumes: - {{- range $gateway.secretVolumes }} - - name: {{ .name }} - secret: - secretName: {{ .secretName | quote }} - optional: true - {{- end }} - {{- range $gateway.configVolumes }} - - name: {{ .name }} - configMap: - name: {{ .configMapName | quote }} - optional: true - {{- end }} - affinity: -{{ include "nodeaffinity" (dict "global" .Values.global "nodeSelector" $gateway.nodeSelector) | trim | indent 8 }} - {{- include "podAntiAffinity" $gateway | indent 6 }} -{{- if $gateway.tolerations }} - tolerations: -{{ toYaml $gateway.tolerations | indent 6 }} -{{- else if .Values.global.defaultTolerations }} - tolerations: -{{ toYaml .Values.global.defaultTolerations | indent 6 }} -{{- end }} -{{- end }} diff --git a/manifests/charts/gateways/istio-egress/templates/poddisruptionbudget.yaml b/manifests/charts/gateways/istio-egress/templates/poddisruptionbudget.yaml deleted file mode 100644 index ba3c6d24b..000000000 --- a/manifests/charts/gateways/istio-egress/templates/poddisruptionbudget.yaml +++ /dev/null @@ -1,23 +0,0 @@ -{{- if .Values.global.defaultPodDisruptionBudget.enabled }} -{{ $gateway := index .Values "gateways" "istio-egressgateway" }} -{{- if (semverCompare ">=1.21-0" .Capabilities.KubeVersion.GitVersion) }} -apiVersion: policy/v1 -{{- else }} -apiVersion: policy/v1beta1 -{{- end }} -kind: PodDisruptionBudget -metadata: - name: {{ $gateway.name }} - namespace: {{ .Release.Namespace }} - labels: -{{ $gateway.labels | toYaml | trim | indent 4 }} - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "EgressGateways" -spec: - minAvailable: 1 - selector: - matchLabels: -{{ $gateway.labels | toYaml | trim | indent 6 }} -{{- end }} diff --git a/manifests/charts/gateways/istio-egress/templates/role.yaml b/manifests/charts/gateways/istio-egress/templates/role.yaml deleted file mode 100644 index c472fcef2..000000000 --- a/manifests/charts/gateways/istio-egress/templates/role.yaml +++ /dev/null @@ -1,16 +0,0 @@ -{{ $gateway := index .Values "gateways" "istio-egressgateway" }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: {{ $gateway.name }}-sds - namespace: {{ .Release.Namespace }} - labels: - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "EgressGateways" -rules: -- apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "watch", "list"] ---- diff --git a/manifests/charts/gateways/istio-egress/templates/rolebindings.yaml b/manifests/charts/gateways/istio-egress/templates/rolebindings.yaml deleted file mode 100644 index fd1ffcd70..000000000 --- a/manifests/charts/gateways/istio-egress/templates/rolebindings.yaml +++ /dev/null @@ -1,19 +0,0 @@ -{{ $gateway := index .Values "gateways" "istio-egressgateway" }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: {{ $gateway.name }}-sds - namespace: {{ .Release.Namespace }} - labels: - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "EgressGateways" -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: {{ $gateway.name }}-sds -subjects: -- kind: ServiceAccount - name: {{ $gateway.name }}-service-account ---- diff --git a/manifests/charts/gateways/istio-egress/templates/service.yaml b/manifests/charts/gateways/istio-egress/templates/service.yaml deleted file mode 100644 index 2f8ce959e..000000000 --- a/manifests/charts/gateways/istio-egress/templates/service.yaml +++ /dev/null @@ -1,47 +0,0 @@ -{{ $gateway := index .Values "gateways" "istio-egressgateway" }} -{{- if not $gateway.customService }} -apiVersion: v1 -kind: Service -metadata: - name: {{ $gateway.name }} - namespace: {{ .Release.Namespace }} - annotations: - {{- range $key, $val := $gateway.serviceAnnotations }} - {{ $key }}: {{ $val | quote }} - {{- end }} - labels: -{{ $gateway.labels | toYaml | indent 4 }} - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "EgressGateways" -spec: -{{- if $gateway.loadBalancerIP }} - loadBalancerIP: "{{ $gateway.loadBalancerIP }}" -{{- end }} -{{- if $gateway.loadBalancerSourceRanges }} - loadBalancerSourceRanges: -{{ toYaml $gateway.loadBalancerSourceRanges | indent 4 }} -{{- end }} -{{- if $gateway.externalTrafficPolicy }} - externalTrafficPolicy: {{$gateway.externalTrafficPolicy }} -{{- end }} - type: {{ $gateway.type }} - selector: -{{ $gateway.labels | toYaml | indent 4 }} - ports: - - {{- range $key, $val := $gateway.ports }} - - - {{- range $pkey, $pval := $val }} - {{ $pkey}}: {{ $pval }} - {{- end }} - {{- end }} - - {{ range $app := $gateway.egressPorts }} - - - port: {{ $app.port }} - name: {{ $app.name }} - {{- end }} ---- -{{ end }} diff --git a/manifests/charts/gateways/istio-egress/templates/serviceaccount.yaml b/manifests/charts/gateways/istio-egress/templates/serviceaccount.yaml deleted file mode 100644 index b6a3eb40c..000000000 --- a/manifests/charts/gateways/istio-egress/templates/serviceaccount.yaml +++ /dev/null @@ -1,22 +0,0 @@ -{{ $gateway := index .Values "gateways" "istio-egressgateway" }} -apiVersion: v1 -kind: ServiceAccount -{{- if .Values.global.imagePullSecrets }} -imagePullSecrets: -{{- range .Values.global.imagePullSecrets }} - - name: {{ . }} -{{- end }} -{{- end }} -metadata: - name: {{ $gateway.name }}-service-account - namespace: {{ .Release.Namespace }} - labels: -{{ $gateway.labels | toYaml | trim | indent 4 }} - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "EgressGateways" - {{- with $gateway.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} diff --git a/manifests/charts/gateways/istio-egress/values.yaml b/manifests/charts/gateways/istio-egress/values.yaml deleted file mode 100644 index 1abcb8e25..000000000 --- a/manifests/charts/gateways/istio-egress/values.yaml +++ /dev/null @@ -1,299 +0,0 @@ -# Standalone istio egress gateway. -# Should be installed in a separate namespace, to minimize access to config -gateways: - istio-egressgateway: - name: istio-egressgateway - ports: - - port: 80 - targetPort: 8080 - name: http2 - protocol: TCP - - port: 443 - name: https - targetPort: 8443 - protocol: TCP - - labels: - app: istio-egressgateway - istio: egressgateway - - # Scalability tuning - # replicaCount: 1 - rollingMaxSurge: 100% - rollingMaxUnavailable: 25% - autoscaleEnabled: true - autoscaleMin: 1 - autoscaleMax: 5 - resources: - requests: - cpu: 100m - memory: 128Mi - limits: - cpu: 2000m - memory: 1024Mi - cpu: - targetAverageUtilization: 80 - - serviceAnnotations: {} - podAnnotations: {} - type: ClusterIP # change to NodePort or LoadBalancer if need be - - secretVolumes: - - name: egressgateway-certs - secretName: istio-egressgateway-certs - mountPath: /etc/istio/egressgateway-certs - - name: egressgateway-ca-certs - secretName: istio-egressgateway-ca-certs - mountPath: /etc/istio/egressgateway-ca-certs - - configVolumes: [] - additionalContainers: [] - - serviceAccount: - # Annotations to add to the service account - annotations: {} - - ### Advanced options ############ - # TODO: convert to real options, env should not be exposed - env: {} - # Set this to "external" if and only if you want the egress gateway to - # act as a transparent SNI gateway that routes mTLS/TLS traffic to - # external services defined using service entries, where the service - # entry has resolution set to DNS, has one or more endpoints with - # network field set to "external". By default its set to "" so that - # the egress gateway sees the same set of endpoints as the sidecars - # preserving backward compatibility - # ISTIO_META_REQUESTED_NETWORK_VIEW: "" - - nodeSelector: {} - tolerations: [] - - # Specify the pod anti-affinity that allows you to constrain which nodes - # your pod is eligible to be scheduled based on labels on pods that are - # already running on the node rather than based on labels on nodes. - # There are currently two types of anti-affinity: - # "requiredDuringSchedulingIgnoredDuringExecution" - # "preferredDuringSchedulingIgnoredDuringExecution" - # which denote "hard" vs. "soft" requirements, you can define your values - # in "podAntiAffinityLabelSelector" and "podAntiAffinityTermLabelSelector" - # correspondingly. - # For example: - # podAntiAffinityLabelSelector: - # - key: security - # operator: In - # values: S1,S2 - # topologyKey: "kubernetes.io/hostname" - # This pod anti-affinity rule says that the pod requires not to be scheduled - # onto a node if that node is already running a pod with label having key - # "security" and value "S1". - podAntiAffinityLabelSelector: [] - podAntiAffinityTermLabelSelector: [] - - # whether to run the gateway in a privileged container - runAsRoot: false - - # The injection template to use for the gateway. If not set, no injection will be performed. - injectionTemplate: "" - -# Revision is set as 'version' label and part of the resource names when installing multiple control planes. -revision: "" - -# For Helm compatibility. -ownerName: "" - -global: - # set the default set of namespaces to which services, service entries, virtual services, destination - # rules should be exported to. Currently only one value can be provided in this list. This value - # should be one of the following two options: - # * implies these objects are visible to all namespaces, enabling any sidecar to talk to any other sidecar. - # . implies these objects are visible to only to sidecars in the same namespace, or if imported as a Sidecar.egress.host - defaultConfigVisibilitySettings: [] - - # Default node selector to be applied to all deployments so that all pods can be - # constrained to run a particular nodes. Each component can overwrite these default - # values by adding its node selector block in the relevant section below and setting - # the desired values. - defaultNodeSelector: {} - - # enable pod disruption budget for the control plane, which is used to - # ensure Istio control plane components are gradually upgraded or recovered. - defaultPodDisruptionBudget: - enabled: true - - # A minimal set of requested resources to applied to all deployments so that - # Horizontal Pod Autoscaler will be able to function (if set). - # Each component can overwrite these default values by adding its own resources - # block in the relevant section below and setting the desired resources values. - defaultResources: - requests: - cpu: 10m - # memory: 128Mi - # limits: - # cpu: 100m - # memory: 128Mi - - # Default node tolerations to be applied to all deployments so that all pods can be - # scheduled to a particular nodes with matching taints. Each component can overwrite - # these default values by adding its tolerations block in the relevant section below - # and setting the desired values. - # Configure this field in case that all pods of Istio control plane are expected to - # be scheduled to particular nodes with specified taints. - defaultTolerations: [] - - # Default hub for Istio images. - # Releases are published to docker hub under 'istio' project. - # Dev builds from prow are on gcr.io - hub: apache - - # Default tag for Istio images. - tag: latest - - # Specify image pull policy if default behavior isn't desired. - # Default behavior: latest images will be Always else IfNotPresent. - imagePullPolicy: "" - - # ImagePullSecrets for all ServiceAccount, list of secrets in the same namespace - # to use for pulling any images in pods that reference this ServiceAccount. - # For components that don't use ServiceAccounts (i.e. grafana, servicegraph, tracing) - # ImagePullSecrets will be added to the corresponding Deployment(StatefulSet) objects. - # Must be set for any cluster configured with private docker registry. - imagePullSecrets: [] - # - private-registry-key - - # To output all istio components logs in json format by adding --log_as_json argument to each container argument - logAsJson: false - - # Specify pod scheduling arch(amd64, ppc64le, s390x, arm64) and weight as follows: - # 0 - Never scheduled - # 1 - Least preferred - # 2 - No preference - # 3 - Most preferred - arch: {} - - # Comma-separated minimum per-scope logging level of messages to output, in the form of :,: - # The control plane has different scopes depending on component, but can configure default log level across all components - # If empty, default scope and level will be used as configured in code - logging: - level: "default:info" - - # Kubernetes >=v1.11.0 will create two PriorityClass, including system-cluster-critical and - # system-node-critical, it is better to configure this in order to make sure your Istio pods - # will not be killed because of low priority class. - # Refer to https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass - # for more detail. - priorityClassName: "" - - proxy: - image: dubbo-agent - - # CAUTION: It is important to ensure that all Istio helm charts specify the same clusterDomain value - # cluster domain. Default value is "cluster.local". - clusterDomain: "cluster.local" - - # Per Component log level for proxy, applies to gateways and sidecars. If a component level is - # not set, then the global "logLevel" will be used. - componentLogLevel: "misc:error" - - # If set, newly injected sidecars will have core dumps enabled. - enableCoreDump: false - - # Log level for proxy, applies to gateways and sidecars. - # Expected values are: trace|debug|info|warning|error|critical|off - logLevel: warning - - ############################################################################################## - # The following values are found in other charts. To effectively modify these values, make # - # make sure they are consistent across your Istio helm charts # - ############################################################################################## - - # The customized CA address to retrieve certificates for the pods in the cluster. - # CSR clients such as the Istio Agent and ingress gateways can use this to specify the CA endpoint. - caAddress: "" - - # Used to locate istiod. - istioNamespace: dubbo-system - - # Configure the policy for validating JWT. - # Currently, two options are supported: "third-party-jwt" and "first-party-jwt". - jwtPolicy: "third-party-jwt" - - # Mesh ID means Mesh Identifier. It should be unique within the scope where - # meshes will interact with each other, but it is not required to be - # globally/universally unique. For example, if any of the following are true, - # then two meshes must have different Mesh IDs: - # - Meshes will have their telemetry aggregated in one place - # - Meshes will be federated together - # - Policy will be written referencing one mesh from the other - # - # If an administrator expects that any of these conditions may become true in - # the future, they should ensure their meshes have different Mesh IDs - # assigned. - # - # Within a multicluster mesh, each cluster must be (manually or auto) - # configured to have the same Mesh ID value. If an existing cluster 'joins' a - # multicluster mesh, it will need to be migrated to the new mesh ID. Details - # of migration TBD, and it may be a disruptive operation to change the Mesh - # ID post-install. - # - # If the mesh admin does not specify a value, Istio will use the value of the - # mesh's Trust Domain. The best practice is to select a proper Trust Domain - # value. - meshID: "" - - # Use the user-specified, secret volume mounted key and certs for Pilot and workloads. - mountMtlsCerts: false - - multiCluster: - # Set to true to connect two kubernetes clusters via their respective - # ingressgateway services when pods in each cluster cannot directly - # talk to one another. All clusters should be using Istio mTLS and must - # have a shared root CA for this model to work. - enabled: false - # Should be set to the name of the cluster this installation will run in. This is required for sidecar injection - # to properly label proxies - clusterName: "" - - # Network defines the network this cluster belong to. This name - # corresponds to the networks in the map of mesh networks. - network: "" - - # Configure the certificate provider for control plane communication. - # Currently, two providers are supported: "kubernetes" and "istiod". - # As some platforms may not have kubernetes signing APIs, - # Istiod is the default - pilotCertProvider: istiod - - sds: - # The JWT token for SDS and the aud field of such JWT. See RFC 7519, section 4.1.3. - # When a CSR is sent from Citadel Agent to the CA (e.g. Citadel), this aud is to make sure the - # JWT is intended for the CA. - token: - aud: istio-ca - - sts: - # The service port used by Security Token Service (STS) server to handle token exchange requests. - # Setting this port to a non-zero value enables STS server. - servicePort: 0 - - # whether to use autoscaling/v2 template for HPA settings - # for internal usage only, not to be configured by users. - autoscalingv2API: true - -meshConfig: - enablePrometheusMerge: true - - # The trust domain corresponds to the trust root of a system - # Refer to https://github.com/spiffe/spiffe/blob/master/standards/SPIFFE-ID.md#21-trust-domain - trustDomain: "cluster.local" - - defaultConfig: - proxyMetadata: {} - tracing: - # tlsSettings: - # mode: DISABLE # DISABLE, SIMPLE, MUTUAL, ISTIO_MUTUAL - # clientCertificate: # example: /etc/istio/tracer/cert-chain.pem - # privateKey: # example: /etc/istio/tracer/key.pem - # caCertificates: # example: /etc/istio/tracer/root-cert.pem - # sni: # example: tracer.somedomain - # subjectAltNames: [] - # - tracer.somedomain diff --git a/manifests/charts/gateways/istio-ingress/Chart.yaml b/manifests/charts/gateways/istio-ingress/Chart.yaml deleted file mode 100644 index 0314ba11a..000000000 --- a/manifests/charts/gateways/istio-ingress/Chart.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: v1 -name: istio-ingress -# This version is never actually shipped. istio/release-builder will replace it at build-time -# with the appropriate version -version: 1.0.0 -appVersion: 1.0.0 -tillerVersion: ">=2.7.2" -description: Helm chart for deploying Istio gateways -keywords: - - istio - - ingressgateway - - gateways -sources: - - http://github.com/istio/istio -engine: gotpl -icon: https://istio.io/latest/favicons/android-192x192.png diff --git a/manifests/charts/gateways/istio-ingress/NOTES.txt b/manifests/charts/gateways/istio-ingress/NOTES.txt deleted file mode 100644 index 03a7436d5..000000000 --- a/manifests/charts/gateways/istio-ingress/NOTES.txt +++ /dev/null @@ -1,43 +0,0 @@ - -Changes: -- separate namespace allows: --- easier reconfig of just the gateway --- TLS secrets and domain name management is isolated, for better security --- simplified configuration --- multiple versions of the ingress can be used, to minimize upgrade risks - -- the new chart uses the default namespace service account, and doesn't require -additional RBAC permissions. - -- simplified label and chart structure. -- ability to run a pilot dedicated for the gateway, isolated from the main pilot. This is more robust, safer on upgrades -and allows a bit more flexibility. -- the dedicated pilot-per-ingress is required if the gateway needs to support k8s-style ingress. - -# Port and basic host configuration - -In order to configure the Service object, the install/upgrade needs to provide a list of all ports. -In the past, this was done when installing/upgrading full istio, and involved some duplication - ports configured -both in upgrade, Gateway and VirtualService. - -The new Ingress chart uses a 'values.yaml' (see user-example-ingress), which auto-generates Service ports, -Gateways and basic VirtualService. It is still possible to only configure the ports in Service, and do manual -config for the rest. - -All internal services ( telemetry, pilot debug ports, mesh expansion ) can now be configured via the new mechanism. - -# Migration from dubbo-system - -Istio 1.0 includes the gateways in dubbo-system. Since the external IP is associated -with the Service and bound to the namespace, it is recommended to: - -1. Install the new gateway in a new namespace. -2. Copy any TLS certificate to the new namespace, and configure the domains. -3. Checking the new gateway work - for example by overriding the IP in /etc/hosts -4. Modify the DNS server to add the A record of the new namespace -5. Check traffic -6. Delete the A record corresponding to the gateway in dubbo-system -7. Upgrade dubbo-system, disabling the ingressgateway -8. Delete the domain TLS certs from dubbo-system. - -If using certmanager, all Certificate and associated configs must be moved as well. diff --git a/manifests/charts/gateways/istio-ingress/templates/_affinity.tpl b/manifests/charts/gateways/istio-ingress/templates/_affinity.tpl deleted file mode 100644 index 3d6c896a6..000000000 --- a/manifests/charts/gateways/istio-ingress/templates/_affinity.tpl +++ /dev/null @@ -1,102 +0,0 @@ -{{/* affinity - https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ */}} - -{{ define "nodeaffinity" }} -nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - {{- include "nodeAffinityRequiredDuringScheduling" . }} - preferredDuringSchedulingIgnoredDuringExecution: - {{- include "nodeAffinityPreferredDuringScheduling" . }} -{{- end }} - -{{- define "nodeAffinityRequiredDuringScheduling" }} - {{- $nodeSelector := default .global.defaultNodeSelector .nodeSelector -}} - {{- if or .global.arch $nodeSelector }} - nodeSelectorTerms: - - matchExpressions: - {{- range $key, $val := .global.arch }} - - key: kubernetes.io/arch - operator: In - values: - {{- if gt ($val | int) 0 }} - - {{ $key | quote }} - {{- end }} - {{- end }} - {{- range $key, $val := $nodeSelector }} - - key: {{ $key }} - operator: In - values: - - {{ $val | quote }} - {{- end }} - {{- end }} -{{- end }} - -{{- define "nodeAffinityPreferredDuringScheduling" }} - {{- range $key, $val := .global.arch }} - {{- if gt ($val | int) 0 }} - - weight: {{ $val | int }} - preference: - matchExpressions: - - key: kubernetes.io/arch - operator: In - values: - - {{ $key | quote }} - {{- end }} - {{- end }} -{{- end }} - -{{- define "podAntiAffinity" }} -{{- if or .podAntiAffinityLabelSelector .podAntiAffinityTermLabelSelector}} - podAntiAffinity: - {{- if .podAntiAffinityLabelSelector }} - requiredDuringSchedulingIgnoredDuringExecution: - {{- include "podAntiAffinityRequiredDuringScheduling" . }} - {{- end }} - {{- if .podAntiAffinityTermLabelSelector }} - preferredDuringSchedulingIgnoredDuringExecution: - {{- include "podAntiAffinityPreferredDuringScheduling" . }} - {{- end }} -{{- end }} -{{- end }} - -{{- define "podAntiAffinityRequiredDuringScheduling" }} - {{- range $index, $item := .podAntiAffinityLabelSelector }} - - labelSelector: - matchExpressions: - - key: {{ $item.key }} - operator: {{ $item.operator }} - {{- if $item.values }} - values: - {{- $vals := split "," $item.values }} - {{- range $i, $v := $vals }} - - {{ $v | quote }} - {{- end }} - {{- end }} - topologyKey: {{ $item.topologyKey }} - {{- if $item.namespaces }} - namespaces: - {{- $ns := split "," $item.namespaces }} - {{- range $i, $n := $ns }} - - {{ $n | quote }} - {{- end }} - {{- end }} - {{- end }} -{{- end }} - -{{- define "podAntiAffinityPreferredDuringScheduling" }} - {{- range $index, $item := .podAntiAffinityTermLabelSelector }} - - podAffinityTerm: - labelSelector: - matchExpressions: - - key: {{ $item.key }} - operator: {{ $item.operator }} - {{- if $item.values }} - values: - {{- $vals := split "," $item.values }} - {{- range $i, $v := $vals }} - - {{ $v | quote }} - {{- end }} - {{- end }} - topologyKey: {{ $item.topologyKey }} - weight: 100 - {{- end }} -{{- end }} diff --git a/manifests/charts/gateways/istio-ingress/templates/autoscale.yaml b/manifests/charts/gateways/istio-ingress/templates/autoscale.yaml deleted file mode 100644 index 0a6299bf0..000000000 --- a/manifests/charts/gateways/istio-ingress/templates/autoscale.yaml +++ /dev/null @@ -1,60 +0,0 @@ -{{ $gateway := index .Values "gateways" "istio-ingressgateway" }} -{{- if and $gateway.autoscaleEnabled $gateway.autoscaleMin $gateway.autoscaleMax }} -{{- if not .Values.global.autoscalingv2API }} -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: {{ $gateway.name }} - namespace: {{ .Release.Namespace }} - labels: -{{ $gateway.labels | toYaml | indent 4 }} - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "IngressGateways" -spec: - maxReplicas: {{ $gateway.autoscaleMax }} - minReplicas: {{ $gateway.autoscaleMin }} - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ $gateway.name }} - metrics: - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ $gateway.cpu.targetAverageUtilization }} ---- -{{- else }} -{{- if (semverCompare ">=1.23-0" .Capabilities.KubeVersion.GitVersion)}} -apiVersion: autoscaling/v2 -{{- else }} -apiVersion: autoscaling/v2beta2 -{{- end }} -kind: HorizontalPodAutoscaler -metadata: - name: {{ $gateway.name }} - namespace: {{ .Release.Namespace }} - labels: -{{ $gateway.labels | toYaml | indent 4 }} - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "IngressGateways" -spec: - maxReplicas: {{ $gateway.autoscaleMax }} - minReplicas: {{ $gateway.autoscaleMin }} - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ $gateway.name }} - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: {{ $gateway.cpu.targetAverageUtilization }} ---- -{{- end }} -{{- end }} diff --git a/manifests/charts/gateways/istio-ingress/templates/deployment.yaml b/manifests/charts/gateways/istio-ingress/templates/deployment.yaml deleted file mode 100644 index ef5a54b9d..000000000 --- a/manifests/charts/gateways/istio-ingress/templates/deployment.yaml +++ /dev/null @@ -1,335 +0,0 @@ -{{- $gateway := index .Values "gateways" "istio-ingressgateway" }} -{{- if eq $gateway.injectionTemplate "" }} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ $gateway.name }} - namespace: {{ .Release.Namespace }} - labels: -{{ $gateway.labels | toYaml | indent 4 }} - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "IngressGateways" -spec: -{{- if not $gateway.autoscaleEnabled }} -{{- if $gateway.replicaCount }} - replicas: {{ $gateway.replicaCount }} -{{- end }} -{{- end }} - selector: - matchLabels: -{{ $gateway.labels | toYaml | indent 6 }} - strategy: - rollingUpdate: - maxSurge: {{ $gateway.rollingMaxSurge }} - maxUnavailable: {{ $gateway.rollingMaxUnavailable }} - template: - metadata: - labels: -{{ $gateway.labels | toYaml | indent 8 }} -{{- if eq .Release.Namespace "dubbo-system"}} - heritage: Tiller - release: istio - chart: gateways -{{- end }} - service.istio.io/canonical-name: {{ $gateway.name }} - {{- if not (eq .Values.revision "") }} - service.istio.io/canonical-revision: {{ .Values.revision }} - {{- else}} - service.istio.io/canonical-revision: latest - {{- end }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "IngressGateways" - sidecar.istio.io/inject: "false" - annotations: - {{- if .Values.meshConfig.enablePrometheusMerge }} - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - prometheus.io/path: "/stats/prometheus" - {{- end }} - sidecar.istio.io/inject: "false" -{{- if $gateway.podAnnotations }} -{{ toYaml $gateway.podAnnotations | indent 8 }} -{{ end }} - spec: -{{- if not $gateway.runAsRoot }} - securityContext: - runAsUser: 1337 - runAsGroup: 1337 - runAsNonRoot: true - fsGroup: 1337 -{{- end }} - serviceAccountName: {{ $gateway.name }}-service-account -{{- if .Values.global.priorityClassName }} - priorityClassName: "{{ .Values.global.priorityClassName }}" -{{- end }} -{{- if .Values.global.proxy.enableCoreDump }} - initContainers: - - name: enable-core-dump -{{- if contains "/" .Values.global.proxy.image }} - image: "{{ .Values.global.proxy.image }}" -{{- else }} - image: "{{ .Values.global.hub }}/{{ .Values.global.proxy.image | default "dubbo-agent" }}:{{ .Values.global.tag }}" -{{- end }} -{{- if .Values.global.imagePullPolicy }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} -{{- end }} - command: - - /bin/sh - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - securityContext: - runAsUser: 0 - runAsGroup: 0 - runAsNonRoot: false - privileged: true -{{- end }} - containers: - - name: istio-proxy -{{- if contains "/" .Values.global.proxy.image }} - image: "{{ .Values.global.proxy.image }}" -{{- else }} - image: "{{ .Values.global.hub }}/{{ .Values.global.proxy.image | default "dubbo-agent" }}:{{ .Values.global.tag }}" -{{- end }} -{{- if .Values.global.imagePullPolicy }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} -{{- end }} - ports: - {{- range $key, $val := $gateway.ports }} - - containerPort: {{ $val.targetPort | default $val.port }} - protocol: {{ $val.protocol | default "TCP" }} - {{- end }} - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - {{- if .Values.global.proxy.logLevel }} - - --proxyLogLevel={{ .Values.global.proxy.logLevel }} - {{- end}} - {{- if .Values.global.proxy.componentLogLevel }} - - --proxyComponentLogLevel={{ .Values.global.proxy.componentLogLevel }} - {{- end}} - {{- if .Values.global.logging.level }} - - --log_output_level={{ .Values.global.logging.level }} - {{- end}} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if not $gateway.runAsRoot }} - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - {{- end }} - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - scheme: HTTP - initialDelaySeconds: 1 - periodSeconds: 2 - successThreshold: 1 - timeoutSeconds: 1 - resources: -{{- if $gateway.resources }} -{{ toYaml $gateway.resources | indent 12 }} -{{- else }} -{{ toYaml .Values.global.defaultResources | indent 12 }} -{{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: NODE_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.nodeName - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: status.podIP - - name: HOST_IP - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: status.hostIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: ISTIO_META_WORKLOAD_NAME - value: {{ $gateway.name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/{{ .Release.Namespace }}/deployments/{{ $gateway.name }} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if .Values.meshConfig.trustDomain }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.meshConfig.trustDomain }}" - {{- end }} - {{- if .Values.meshConfig.trustDomain }} - - name: TRUST_DOMAIN - value: "{{ .Values.meshConfig.trustDomain }}" - {{- end }} - {{- if not $gateway.runAsRoot }} - - name: ISTIO_META_UNPRIVILEGED_POD - value: "true" - {{- end }} - {{- range $key, $val := $gateway.env }} - - name: {{ $key }} - value: "{{ $val }}" - {{- end }} - {{- range $key, $value := .Values.meshConfig.defaultConfig.proxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- $network_set := index $gateway.env "ISTIO_META_NETWORK" }} - {{- if and (not $network_set) .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ $.Values.global.multiCluster.clusterName | default `Kubernetes` }}" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - - name: istio-envoy - mountPath: /etc/istio/proxy - - name: config-volume - mountPath: /etc/istio/config -{{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert -{{- end }} -{{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - mountPath: /var/run/secrets/tokens - readOnly: true -{{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - mountPath: /etc/certs - readOnly: true - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - - name: podinfo - mountPath: /etc/istio/pod - {{- range $gateway.secretVolumes }} - - name: {{ .name }} - mountPath: {{ .mountPath | quote }} - readOnly: true - {{- end }} - {{- range $gateway.configVolumes }} - {{- if .mountPath }} - - name: {{ .name }} - mountPath: {{ .mountPath | quote }} - readOnly: true - {{- end }} - {{- end }} -{{- if $gateway.additionalContainers }} -{{ toYaml $gateway.additionalContainers | indent 8 }} -{{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs -{{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert -{{- end }} - - name: podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - - name: istio-envoy - emptyDir: {} - - name: istio-data - emptyDir: {} -{{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} -{{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - secretName: istio.istio-ingressgateway-service-account - optional: true - {{- end }} - - name: config-volume - configMap: - name: istio{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - optional: true - {{- range $gateway.secretVolumes }} - - name: {{ .name }} - secret: - secretName: {{ .secretName | quote }} - optional: true - {{- end }} - {{- range $gateway.configVolumes }} - - name: {{ .name }} - configMap: - name: {{ .configMapName | quote }} - optional: true - {{- end }} - affinity: -{{ include "nodeaffinity" (dict "global" .Values.global "nodeSelector" $gateway.nodeSelector) | trim | indent 8 }} - {{- include "podAntiAffinity" $gateway | indent 6 }} -{{- if $gateway.tolerations }} - tolerations: -{{ toYaml $gateway.tolerations | indent 6 }} -{{- else if .Values.global.defaultTolerations }} - tolerations: -{{ toYaml .Values.global.defaultTolerations | indent 6 }} -{{- end }} -{{- end }} diff --git a/manifests/charts/gateways/istio-ingress/templates/injected-deployment.yaml b/manifests/charts/gateways/istio-ingress/templates/injected-deployment.yaml deleted file mode 100644 index f392b4c72..000000000 --- a/manifests/charts/gateways/istio-ingress/templates/injected-deployment.yaml +++ /dev/null @@ -1,143 +0,0 @@ -{{- $gateway := index .Values "gateways" "istio-ingressgateway" }} -{{- if ne $gateway.injectionTemplate "" }} -{{/* This provides a minimal gateway, ready to be injected. - Any settings from values.gateways should be here - these are options specific to the gateway. - Global settings, like the image, various env vars and volumes, etc will be injected. - The normal Deployment is not suitable for this, as the original pod spec will override the injection template. */}} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ $gateway.name | default "istio-ingressgateway" }} - namespace: {{ .Release.Namespace }} - labels: -{{ $gateway.labels | toYaml | indent 4 }} - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "IngressGateways" -spec: -{{- if not $gateway.autoscaleEnabled }} -{{- if $gateway.replicaCount }} - replicas: {{ $gateway.replicaCount }} -{{- end }} -{{- end }} - selector: - matchLabels: -{{ $gateway.labels | toYaml | indent 6 }} - strategy: - rollingUpdate: - maxSurge: {{ $gateway.rollingMaxSurge }} - maxUnavailable: {{ $gateway.rollingMaxUnavailable }} - template: - metadata: - labels: -{{ $gateway.labels | toYaml | indent 8 }} -{{- if eq .Release.Namespace "dubbo-system"}} - heritage: Tiller - release: istio - chart: gateways -{{- end }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "IngressGateways" - sidecar.istio.io/inject: "true" - {{- with .Values.revision }} - istio.io/rev: {{ . }} - {{- end }} - annotations: - {{- if .Values.meshConfig.enablePrometheusMerge }} - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - prometheus.io/path: "/stats/prometheus" - {{- end }} - sidecar.istio.io/inject: "true" - inject.istio.io/templates: "{{ $gateway.injectionTemplate }}" -{{- if $gateway.podAnnotations }} -{{ toYaml $gateway.podAnnotations | indent 8 }} -{{ end }} - spec: -{{- if not $gateway.runAsRoot }} - securityContext: - runAsUser: 1337 - runAsGroup: 1337 - runAsNonRoot: true - fsGroup: 1337 -{{- end }} - serviceAccountName: {{ $gateway.name | default "istio-ingressgateway" }}-service-account -{{- if .Values.global.priorityClassName }} - priorityClassName: "{{ .Values.global.priorityClassName }}" -{{- end }} - containers: - - name: istio-proxy - image: auto - ports: - {{- range $key, $val := $gateway.ports }} - - containerPort: {{ $val.targetPort | default $val.port }} - protocol: {{ $val.protocol | default "TCP" }} - {{- end }} - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - {{- if not $gateway.runAsRoot }} - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - {{- end }} - resources: -{{- if $gateway.resources }} -{{ toYaml $gateway.resources | indent 12 }} -{{- else }} -{{ toYaml .Values.global.defaultResources | indent 12 }} -{{- end }} - env: - {{- if not $gateway.runAsRoot }} - - name: ISTIO_META_UNPRIVILEGED_POD - value: "true" - {{- end }} - {{- range $key, $val := $gateway.env }} - - name: {{ $key }} - value: {{ $val | quote }} - {{- end }} - volumeMounts: - {{- range $gateway.secretVolumes }} - - name: {{ .name }} - mountPath: {{ .mountPath | quote }} - readOnly: true - {{- end }} - {{- range $gateway.configVolumes }} - {{- if .mountPath }} - - name: {{ .name }} - mountPath: {{ .mountPath | quote }} - readOnly: true - {{- end }} - {{- end }} -{{- if $gateway.additionalContainers }} -{{ toYaml $gateway.additionalContainers | indent 8 }} -{{- end }} - volumes: - {{- range $gateway.secretVolumes }} - - name: {{ .name }} - secret: - secretName: {{ .secretName | quote }} - optional: true - {{- end }} - {{- range $gateway.configVolumes }} - - name: {{ .name }} - configMap: - name: {{ .configMapName | quote }} - optional: true - {{- end }} - affinity: -{{ include "nodeaffinity" (dict "global" .Values.global "nodeSelector" $gateway.nodeSelector) | trim | indent 8 }} - {{- include "podAntiAffinity" $gateway | indent 6 }} -{{- if $gateway.tolerations }} - tolerations: -{{ toYaml $gateway.tolerations | indent 6 }} -{{- else if .Values.global.defaultTolerations }} - tolerations: -{{ toYaml .Values.global.defaultTolerations | indent 6 }} -{{- end }} -{{- end }} diff --git a/manifests/charts/gateways/istio-ingress/templates/poddisruptionbudget.yaml b/manifests/charts/gateways/istio-ingress/templates/poddisruptionbudget.yaml deleted file mode 100644 index 8b3688959..000000000 --- a/manifests/charts/gateways/istio-ingress/templates/poddisruptionbudget.yaml +++ /dev/null @@ -1,23 +0,0 @@ -{{- if .Values.global.defaultPodDisruptionBudget.enabled }} -{{ $gateway := index .Values "gateways" "istio-ingressgateway" }} -{{- if (semverCompare ">=1.21-0" .Capabilities.KubeVersion.GitVersion) }} -apiVersion: policy/v1 -{{- else }} -apiVersion: policy/v1beta1 -{{- end }} -kind: PodDisruptionBudget -metadata: - name: {{ $gateway.name }} - namespace: {{ .Release.Namespace }} - labels: -{{ $gateway.labels | toYaml | trim | indent 4 }} - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "IngressGateways" -spec: - minAvailable: 1 - selector: - matchLabels: -{{ $gateway.labels | toYaml | trim | indent 6 }} -{{- end }} diff --git a/manifests/charts/gateways/istio-ingress/templates/role.yaml b/manifests/charts/gateways/istio-ingress/templates/role.yaml deleted file mode 100644 index 3e21bca5b..000000000 --- a/manifests/charts/gateways/istio-ingress/templates/role.yaml +++ /dev/null @@ -1,16 +0,0 @@ -{{ $gateway := index .Values "gateways" "istio-ingressgateway" }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: {{ $gateway.name }}-sds - namespace: {{ .Release.Namespace }} - labels: - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "IngressGateways" -rules: -- apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "watch", "list"] ---- diff --git a/manifests/charts/gateways/istio-ingress/templates/rolebindings.yaml b/manifests/charts/gateways/istio-ingress/templates/rolebindings.yaml deleted file mode 100644 index d45255792..000000000 --- a/manifests/charts/gateways/istio-ingress/templates/rolebindings.yaml +++ /dev/null @@ -1,19 +0,0 @@ -{{ $gateway := index .Values "gateways" "istio-ingressgateway" }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: {{ $gateway.name }}-sds - namespace: {{ .Release.Namespace }} - labels: - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "IngressGateways" -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: {{ $gateway.name }}-sds -subjects: -- kind: ServiceAccount - name: {{ $gateway.name }}-service-account ---- diff --git a/manifests/charts/gateways/istio-ingress/templates/service.yaml b/manifests/charts/gateways/istio-ingress/templates/service.yaml deleted file mode 100644 index a3b97be16..000000000 --- a/manifests/charts/gateways/istio-ingress/templates/service.yaml +++ /dev/null @@ -1,47 +0,0 @@ -{{ $gateway := index .Values "gateways" "istio-ingressgateway" }} -{{- if not $gateway.customService }} -apiVersion: v1 -kind: Service -metadata: - name: {{ $gateway.name }} - namespace: {{ .Release.Namespace }} - annotations: - {{- range $key, $val := $gateway.serviceAnnotations }} - {{ $key }}: {{ $val | quote }} - {{- end }} - labels: -{{ $gateway.labels | toYaml | indent 4 }} - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "IngressGateways" -spec: -{{- if $gateway.loadBalancerIP }} - loadBalancerIP: "{{ $gateway.loadBalancerIP }}" -{{- end }} -{{- if $gateway.loadBalancerSourceRanges }} - loadBalancerSourceRanges: -{{ toYaml $gateway.loadBalancerSourceRanges | indent 4 }} -{{- end }} -{{- if $gateway.externalTrafficPolicy }} - externalTrafficPolicy: {{$gateway.externalTrafficPolicy }} -{{- end }} - type: {{ $gateway.type }} - selector: -{{ $gateway.labels | toYaml | indent 4 }} - ports: - - {{- range $key, $val := $gateway.ports }} - - - {{- range $pkey, $pval := $val }} - {{ $pkey}}: {{ $pval }} - {{- end }} - {{- end }} - - {{ range $app := $gateway.ingressPorts }} - - - port: {{ $app.port }} - name: {{ $app.name }} - {{- end }} ---- -{{ end }} diff --git a/manifests/charts/gateways/istio-ingress/templates/serviceaccount.yaml b/manifests/charts/gateways/istio-ingress/templates/serviceaccount.yaml deleted file mode 100644 index 9cf3034cd..000000000 --- a/manifests/charts/gateways/istio-ingress/templates/serviceaccount.yaml +++ /dev/null @@ -1,22 +0,0 @@ -{{ $gateway := index .Values "gateways" "istio-ingressgateway" }} -apiVersion: v1 -kind: ServiceAccount -{{- if .Values.global.imagePullSecrets }} -imagePullSecrets: -{{- range .Values.global.imagePullSecrets }} - - name: {{ . }} -{{- end }} -{{- end }} -metadata: - name: {{ $gateway.name }}-service-account - namespace: {{ .Release.Namespace }} - labels: -{{ $gateway.labels | toYaml | trim | indent 4 }} - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "IngressGateways" - {{- with $gateway.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} diff --git a/manifests/charts/gateways/istio-ingress/values.yaml b/manifests/charts/gateways/istio-ingress/values.yaml deleted file mode 100644 index f70d811d5..000000000 --- a/manifests/charts/gateways/istio-ingress/values.yaml +++ /dev/null @@ -1,315 +0,0 @@ -# A-la-carte istio ingress gateway. -# Must be installed in a separate namespace, to minimize access to secrets. - -gateways: - istio-ingressgateway: - name: istio-ingressgateway - labels: - app: istio-ingressgateway - istio: ingressgateway - ports: - ## You can add custom gateway ports in user values overrides, but it must include those ports since helm replaces. - # Note that AWS ELB will by default perform health checks on the first port - # on this list. Setting this to the health check port will ensure that health - # checks always work. https://github.com/istio/istio/issues/12503 - - port: 15021 - targetPort: 15021 - name: status-port - protocol: TCP - - port: 80 - targetPort: 8080 - name: http2 - protocol: TCP - - port: 443 - targetPort: 8443 - name: https - protocol: TCP - - # Scalability tuning - # replicaCount: 1 - rollingMaxSurge: 100% - rollingMaxUnavailable: 25% - autoscaleEnabled: true - autoscaleMin: 1 - autoscaleMax: 5 - - cpu: - targetAverageUtilization: 80 - - resources: - requests: - cpu: 100m - memory: 128Mi - limits: - cpu: 2000m - memory: 1024Mi - - loadBalancerIP: "" - loadBalancerSourceRanges: [] - serviceAnnotations: {} - - # To generate an internal load balancer: - # --set serviceAnnotations.cloud.google.com/load-balancer-type=internal - #serviceAnnotations: - # cloud.google.com/load-balancer-type: "internal" - - podAnnotations: {} - type: LoadBalancer #change to NodePort, ClusterIP or LoadBalancer if need be - - ############## - secretVolumes: - - name: ingressgateway-certs - secretName: istio-ingressgateway-certs - mountPath: /etc/istio/ingressgateway-certs - - name: ingressgateway-ca-certs - secretName: istio-ingressgateway-ca-certs - mountPath: /etc/istio/ingressgateway-ca-certs - - customService: false - externalTrafficPolicy: "" - - ingressPorts: [] - additionalContainers: [] - configVolumes: [] - - serviceAccount: - # Annotations to add to the service account - annotations: {} - - ### Advanced options ############ - env: {} - nodeSelector: {} - tolerations: [] - - # Specify the pod anti-affinity that allows you to constrain which nodes - # your pod is eligible to be scheduled based on labels on pods that are - # already running on the node rather than based on labels on nodes. - # There are currently two types of anti-affinity: - # "requiredDuringSchedulingIgnoredDuringExecution" - # "preferredDuringSchedulingIgnoredDuringExecution" - # which denote "hard" vs. "soft" requirements, you can define your values - # in "podAntiAffinityLabelSelector" and "podAntiAffinityTermLabelSelector" - # correspondingly. - # For example: - # podAntiAffinityLabelSelector: - # - key: security - # operator: In - # values: S1,S2 - # topologyKey: "kubernetes.io/hostname" - # This pod anti-affinity rule says that the pod requires not to be scheduled - # onto a node if that node is already running a pod with label having key - # "security" and value "S1". - podAntiAffinityLabelSelector: [] - podAntiAffinityTermLabelSelector: [] - - # whether to run the gateway in a privileged container - runAsRoot: false - - # The injection template to use for the gateway. If not set, no injection will be performed. - injectionTemplate: "" - -# Revision is set as 'version' label and part of the resource names when installing multiple control planes. -revision: "" - -# For Helm compatibility. -ownerName: "" - -global: - # set the default set of namespaces to which services, service entries, virtual services, destination - # rules should be exported to. Currently only one value can be provided in this list. This value - # should be one of the following two options: - # * implies these objects are visible to all namespaces, enabling any sidecar to talk to any other sidecar. - # . implies these objects are visible to only to sidecars in the same namespace, or if imported as a Sidecar.egress.host - defaultConfigVisibilitySettings: [] - - # Default node selector to be applied to all deployments so that all pods can be - # constrained to run a particular nodes. Each component can overwrite these default - # values by adding its node selector block in the relevant section below and setting - # the desired values. - defaultNodeSelector: {} - - # enable pod disruption budget for the control plane, which is used to - # ensure Istio control plane components are gradually upgraded or recovered. - defaultPodDisruptionBudget: - enabled: true - - # A minimal set of requested resources to applied to all deployments so that - # Horizontal Pod Autoscaler will be able to function (if set). - # Each component can overwrite these default values by adding its own resources - # block in the relevant section below and setting the desired resources values. - defaultResources: - requests: - cpu: 10m - # memory: 128Mi - # limits: - # cpu: 100m - # memory: 128Mi - - # Default node tolerations to be applied to all deployments so that all pods can be - # scheduled to a particular nodes with matching taints. Each component can overwrite - # these default values by adding its tolerations block in the relevant section below - # and setting the desired values. - # Configure this field in case that all pods of Istio control plane are expected to - # be scheduled to particular nodes with specified taints. - defaultTolerations: [] - - # Default hub for Istio images. - # Releases are published to docker hub under 'istio' project. - # Dev builds from prow are on gcr.io - hub: apache - - # Default tag for Istio images. - tag: latest - - # Specify image pull policy if default behavior isn't desired. - # Default behavior: latest images will be Always else IfNotPresent. - imagePullPolicy: "" - - # ImagePullSecrets for all ServiceAccount, list of secrets in the same namespace - # to use for pulling any images in pods that reference this ServiceAccount. - # For components that don't use ServiceAccounts (i.e. grafana, servicegraph, tracing) - # ImagePullSecrets will be added to the corresponding Deployment(StatefulSet) objects. - # Must be set for any cluster configured with private docker registry. - imagePullSecrets: [] - # - private-registry-key - - # To output all istio components logs in json format by adding --log_as_json argument to each container argument - logAsJson: false - - # Specify pod scheduling arch(amd64, ppc64le, s390x, arm64) and weight as follows: - # 0 - Never scheduled - # 1 - Least preferred - # 2 - No preference - # 3 - Most preferred - arch: {} - - # Comma-separated minimum per-scope logging level of messages to output, in the form of :,: - # The control plane has different scopes depending on component, but can configure default log level across all components - # If empty, default scope and level will be used as configured in code - logging: - level: "default:info" - - # Kubernetes >=v1.11.0 will create two PriorityClass, including system-cluster-critical and - # system-node-critical, it is better to configure this in order to make sure your Istio pods - # will not be killed because of low priority class. - # Refer to https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass - # for more detail. - priorityClassName: "" - - proxy: - image: dubbo-agent - - # CAUTION: It is important to ensure that all Istio helm charts specify the same clusterDomain value - # cluster domain. Default value is "cluster.local". - clusterDomain: "cluster.local" - - # Per Component log level for proxy, applies to gateways and sidecars. If a component level is - # not set, then the global "logLevel" will be used. - componentLogLevel: "misc:error" - - # If set, newly injected sidecars will have core dumps enabled. - enableCoreDump: false - - # Log level for proxy, applies to gateways and sidecars. - # Expected values are: trace|debug|info|warning|error|critical|off - logLevel: warning - - ############################################################################################## - # The following values are found in other charts. To effectively modify these values, make # - # make sure they are consistent across your Istio helm charts # - ############################################################################################## - - # The customized CA address to retrieve certificates for the pods in the cluster. - # CSR clients such as the Istio Agent and ingress gateways can use this to specify the CA endpoint. - caAddress: "" - - # Used to locate istiod. - istioNamespace: dubbo-system - - # Configure the policy for validating JWT. - # Currently, two options are supported: "third-party-jwt" and "first-party-jwt". - jwtPolicy: "third-party-jwt" - - # Mesh ID means Mesh Identifier. It should be unique within the scope where - # meshes will interact with each other, but it is not required to be - # globally/universally unique. For example, if any of the following are true, - # then two meshes must have different Mesh IDs: - # - Meshes will have their telemetry aggregated in one place - # - Meshes will be federated together - # - Policy will be written referencing one mesh from the other - # - # If an administrator expects that any of these conditions may become true in - # the future, they should ensure their meshes have different Mesh IDs - # assigned. - # - # Within a multicluster mesh, each cluster must be (manually or auto) - # configured to have the same Mesh ID value. If an existing cluster 'joins' a - # multicluster mesh, it will need to be migrated to the new mesh ID. Details - # of migration TBD, and it may be a disruptive operation to change the Mesh - # ID post-install. - # - # If the mesh admin does not specify a value, Istio will use the value of the - # mesh's Trust Domain. The best practice is to select a proper Trust Domain - # value. - meshID: "" - - # Use the user-specified, secret volume mounted key and certs for Pilot and workloads. - mountMtlsCerts: false - - multiCluster: - # Set to true to connect two kubernetes clusters via their respective - # ingressgateway services when pods in each cluster cannot directly - # talk to one another. All clusters should be using Istio mTLS and must - # have a shared root CA for this model to work. - enabled: false - # Should be set to the name of the cluster this installation will run in. This is required for sidecar injection - # to properly label proxies - clusterName: "" - # The suffix for global service names - globalDomainSuffix: "global" - # Enable envoy filter to translate `globalDomainSuffix` to cluster local suffix for cross cluster communication - includeEnvoyFilter: true - - # Network defines the network this cluster belong to. This name - # corresponds to the networks in the map of mesh networks. - network: "" - - # Configure the certificate provider for control plane communication. - # Currently, two providers are supported: "kubernetes" and "istiod". - # As some platforms may not have kubernetes signing APIs, - # Istiod is the default - pilotCertProvider: istiod - - sds: - # The JWT token for SDS and the aud field of such JWT. See RFC 7519, section 4.1.3. - # When a CSR is sent from Citadel Agent to the CA (e.g. Citadel), this aud is to make sure the - # JWT is intended for the CA. - token: - aud: istio-ca - - sts: - # The service port used by Security Token Service (STS) server to handle token exchange requests. - # Setting this port to a non-zero value enables STS server. - servicePort: 0 - # whether to use autoscaling/v2 template for HPA settings - # for internal usage only, not to be configured by users. - autoscalingv2API: true - -meshConfig: - enablePrometheusMerge: true - - # The trust domain corresponds to the trust root of a system - # Refer to https://github.com/spiffe/spiffe/blob/master/standards/SPIFFE-ID.md#21-trust-domain - trustDomain: "cluster.local" - - defaultConfig: - proxyMetadata: {} - tracing: - # tlsSettings: - # mode: DISABLE # DISABLE, SIMPLE, MUTUAL, ISTIO_MUTUAL - # clientCertificate: # example: /etc/istio/tracer/cert-chain.pem - # privateKey: # example: /etc/istio/tracer/key.pem - # caCertificates: # example: /etc/istio/tracer/root-cert.pem - # sni: # example: tracer.somedomain - # subjectAltNames: [] - # - tracer.somedomain diff --git a/manifests/charts/install-OpenShift.md b/manifests/charts/install-OpenShift.md deleted file mode 100644 index 932c02d32..000000000 --- a/manifests/charts/install-OpenShift.md +++ /dev/null @@ -1,43 +0,0 @@ -# Installing Istio on OpenShift using Helm - -> Note: Be aware of the [platform setup required for OpenShift](https://istio.io/latest/docs/setup/platform-setup/openshift/) when installing Istio. - -To install with Helm, you must first create the namespace that you wish to install in if the namespace does not exist already. The default namespace used is `dubbo-system` and can be created as follows: - -```console -kubectl create namespace dubbo-system -``` - -The installation process using the Helm charts is as follows: - -1) `base` chart creates cluster-wide CRDs, cluster bindings and cluster resources. It is possible to change the namespace from `dubbo-system` but it is not recommended. - -```console -helm install istio-base -n dubbo-system manifests/charts/base -``` - -2) `istio-cni` chart installs the CNI plugin. This should be installed after the `base` chart and prior to `istiod` chart. Need to add `--set istio_cni.enabled=true` to the `istiod` install to enable its usage. - -```console -helm install istio-cni -n kube-system manifests/charts/istio-cni --set cni.cniBinDir="/var/lib/cni/bin" --set cni.cniConfDir="/etc/cni/multus/net.d" --set cni.chained=false --set cni.cniConfFileName="istio-cni.conf" --set cni.excludeNamespaces[0]="dubbo-system" --set cni.excludeNamespaces[1]="kube-system" --set cni.repair.enabled=false --set cni.logLevel=info -``` - -3) `istio-control/istio-discovery` chart installs a revision of istiod. - -```console - helm install -n dubbo-system istio-17 manifests/charts/istio-control/istio-discovery --set istio_cni.enabled=true --set global.jwtPolicy=first-party-jwt --set sidecarInjectorWebhook.injectedAnnotations."k8s\.v1\.cni\.cncf\.io/networks"="istio-cni" -``` - -4) `gateways` charts install a load balancer with `ingress` and `egress`. - -Ingress secrets and access should be separated from the control plane. - -```console -helm install -n dubbo-system istio-ingress manifests/charts/gateways/istio-ingress --set global.jwtPolicy=first-party-jwt -``` - -Egress secrets and access should be separated from the control plane. - -```console -helm install -n dubbo-system istio-egress manifests/charts/gateways/istio-egress --set global.jwtPolicy=first-party-jwt -``` diff --git a/manifests/charts/istio-cni/Chart.yaml b/manifests/charts/istio-cni/Chart.yaml deleted file mode 100644 index 9256ac09c..000000000 --- a/manifests/charts/istio-cni/Chart.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -name: cni -# This version is never actually shipped. istio/release-builder will replace it at build-time -# with the appropriate version -version: 1.0.0 -appVersion: 1.0.0 -description: Helm chart for istio-cni components -keywords: - - istio-cni - - istio -sources: - - http://github.com/istio/cni -engine: gotpl -icon: https://istio.io/latest/favicons/android-192x192.png diff --git a/manifests/charts/istio-cni/README.md b/manifests/charts/istio-cni/README.md deleted file mode 100644 index b7fbc5d52..000000000 --- a/manifests/charts/istio-cni/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Istio CNI Helm Chart - -This chart installs the Istio CNI Plugin. See the [CNI installation guide](https://istio.io/latest/docs/setup/additional-setup/cni/) -for more information. - -## Setup Repo Info - -```console -helm repo add istio https://istio-release.storage.googleapis.com/charts -helm repo update -``` - -_See [helm repo](https://helm.sh/docs/helm/helm_repo/) for command documentation._ - -## Installing the Chart - -To install the chart with the release name `istio-cni`: - -```console -helm install istio-cni istio/cni -n kube-system -``` - -Installation in `kube-system` is recommended to ensure the [`system-node-critical`](https://kubernetes.io/docs/tasks/administer-cluster/guaranteed-scheduling-critical-addon-pods/) -`priorityClassName` can be used. diff --git a/manifests/charts/istio-cni/templates/NOTES.txt b/manifests/charts/istio-cni/templates/NOTES.txt deleted file mode 100644 index 994628240..000000000 --- a/manifests/charts/istio-cni/templates/NOTES.txt +++ /dev/null @@ -1,5 +0,0 @@ -"{{ .Release.Name }}" successfully installed! - -To learn more about the release, try: - $ helm status {{ .Release.Name }} - $ helm get all {{ .Release.Name }} diff --git a/manifests/charts/istio-cni/templates/clusterrole.yaml b/manifests/charts/istio-cni/templates/clusterrole.yaml deleted file mode 100644 index 7f7030de3..000000000 --- a/manifests/charts/istio-cni/templates/clusterrole.yaml +++ /dev/null @@ -1,63 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istio-cni - labels: - app: istio-cni - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Cni" -rules: -- apiGroups: [""] - resources: - - pods - - nodes - verbs: - - get ---- -{{- if .Values.cni.repair.enabled }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istio-cni-repair-role - labels: - app: istio-cni - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Cni" -rules: -- apiGroups: [""] - resources: ["pods"] - verbs: ["get", "list", "watch", "delete", "patch", "update" ] -- apiGroups: [""] - resources: ["events"] - verbs: ["get", "list", "watch", "delete", "patch", "update", "create" ] -{{- end }} ---- - {{- if .Values.cni.taint.enabled }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istio-cni-taint-role - labels: - app: istio-cni - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Cni" -rules: - - apiGroups: [""] - resources: ["pods"] - verbs: ["get", "list", "watch", "patch"] - - apiGroups: [""] - resources: ["nodes"] - verbs: ["get", "list", "watch", "update", "patch"] - - apiGroups: [""] - resources: ["configmaps"] - verbs: ["get", "list"] - - apiGroups: ["coordination.k8s.io"] - resources: ["leases"] - verbs: ["get", "list", "create", "update"] - {{- end }} diff --git a/manifests/charts/istio-cni/templates/clusterrolebinding.yaml b/manifests/charts/istio-cni/templates/clusterrolebinding.yaml deleted file mode 100644 index deabd5238..000000000 --- a/manifests/charts/istio-cni/templates/clusterrolebinding.yaml +++ /dev/null @@ -1,78 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istio-cni - labels: - app: istio-cni - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Cni" -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istio-cni -subjects: -- kind: ServiceAccount - name: istio-cni - namespace: {{ .Release.Namespace }} ---- -{{- if .Values.cni.repair.enabled }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istio-cni-repair-rolebinding - labels: - k8s-app: istio-cni-repair - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Cni" -subjects: -- kind: ServiceAccount - name: istio-cni - namespace: {{ .Release.Namespace}} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istio-cni-repair-role -{{- end }} ---- -{{- if ne .Values.cni.psp_cluster_role "" }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: istio-cni-psp - namespace: {{ .Release.Namespace }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Cni" -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ .Values.cni.psp_cluster_role }} -subjects: -- kind: ServiceAccount - name: istio-cni - namespace: {{ .Release.Namespace }} -{{- end }} ---- -{{- if .Values.cni.taint.enabled }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istio-cni-taint-rolebinding - labels: - k8s-app: istio-cni-taint - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Cni" -subjects: - - kind: ServiceAccount - name: istio-cni - namespace: {{ .Release.Namespace}} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istio-cni-taint-role -{{- end }} diff --git a/manifests/charts/istio-cni/templates/configmap-cni.yaml b/manifests/charts/istio-cni/templates/configmap-cni.yaml deleted file mode 100644 index 9c51b257e..000000000 --- a/manifests/charts/istio-cni/templates/configmap-cni.yaml +++ /dev/null @@ -1,46 +0,0 @@ -kind: ConfigMap -apiVersion: v1 -metadata: - name: istio-cni-config - namespace: {{ .Release.Namespace }} - labels: - app: istio-cni - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Cni" -data: - # The CNI network configuration to add to the plugin chain on each node. The special - # values in this config will be automatically populated. - cni_network_config: |- - { - "cniVersion": "0.3.1", - "name": "istio-cni", - "type": "istio-cni", - "log_level": {{ quote .Values.cni.logLevel }}, - "log_uds_address": "__LOG_UDS_ADDRESS__", - "kubernetes": { - "kubeconfig": "__KUBECONFIG_FILEPATH__", - "cni_bin_dir": {{ quote .Values.cni.cniBinDir }}, - "exclude_namespaces": [ {{ range $idx, $ns := .Values.cni.excludeNamespaces }}{{ if $idx }}, {{ end }}{{ quote $ns }}{{ end }} ] - } - } ---- - {{- if .Values.cni.taint.enabled }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: "istio-cni-taint-configmap" - namespace: {{ .Release.Namespace }} - labels: - app: istio-cni - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Cni" -data: - config: | - - name: istio-cni - selector: k8s-app=istio-cni-node - namespace: {{ .Release.Namespace }} - {{- end }} diff --git a/manifests/charts/istio-cni/templates/daemonset.yaml b/manifests/charts/istio-cni/templates/daemonset.yaml deleted file mode 100644 index 5fbc02219..000000000 --- a/manifests/charts/istio-cni/templates/daemonset.yaml +++ /dev/null @@ -1,162 +0,0 @@ -# This manifest installs the Istio install-cni container, as well -# as the Istio CNI plugin and config on -# each master and worker node in a Kubernetes cluster. -kind: DaemonSet -apiVersion: apps/v1 -metadata: - name: istio-cni-node - namespace: {{ .Release.Namespace }} - labels: - k8s-app: istio-cni-node - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Cni" -spec: - selector: - matchLabels: - k8s-app: istio-cni-node - updateStrategy: - type: RollingUpdate - rollingUpdate: - maxUnavailable: 1 - template: - metadata: - labels: - k8s-app: istio-cni-node - sidecar.istio.io/inject: "false" - annotations: - sidecar.istio.io/inject: "false" - # Add Prometheus Scrape annotations - prometheus.io/scrape: 'true' - prometheus.io/port: "15014" - prometheus.io/path: '/metrics' - # Custom annotations - {{- if .Values.cni.podAnnotations }} -{{ toYaml .Values.cni.podAnnotations | indent 8 }} - {{- end }} - spec: - nodeSelector: - kubernetes.io/os: linux - tolerations: - # Make sure istio-cni-node gets scheduled on all nodes. - - effect: NoSchedule - operator: Exists - # Mark the pod as a critical add-on for rescheduling. - - key: CriticalAddonsOnly - operator: Exists - - effect: NoExecute - operator: Exists - priorityClassName: system-node-critical - serviceAccountName: istio-cni - # Minimize downtime during a rolling upgrade or deletion; tell Kubernetes to do a "force - # deletion": https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods. - terminationGracePeriodSeconds: 5 - containers: - # This container installs the Istio CNI binaries - # and CNI network config file on each node. - - name: install-cni -{{- if contains "/" .Values.cni.image }} - image: "{{ .Values.cni.image }}" -{{- else }} - image: "{{ .Values.cni.hub | default .Values.global.hub }}/{{ .Values.cni.image | default "install-cni" }}:{{ .Values.cni.tag | default .Values.global.tag }}" -{{- end }} -{{- if or .Values.cni.pullPolicy .Values.global.imagePullPolicy }} - imagePullPolicy: {{ .Values.cni.pullPolicy | default .Values.global.imagePullPolicy }} -{{- end }} - readinessProbe: - httpGet: - path: /readyz - port: 8000 - securityContext: - runAsGroup: 0 - runAsUser: 0 - runAsNonRoot: false - privileged: {{ .Values.cni.privileged }} - command: ["install-cni"] - env: -{{- if .Values.cni.cniConfFileName }} - # Name of the CNI config file to create. - - name: CNI_CONF_NAME - value: "{{ .Values.cni.cniConfFileName }}" -{{- end }} - # The CNI network config to install on each node. - - name: CNI_NETWORK_CONFIG - valueFrom: - configMapKeyRef: - name: istio-cni-config - key: cni_network_config - - name: CNI_NET_DIR - value: {{ default "/etc/cni/net.d" .Values.cni.cniConfDir }} - # Deploy as a standalone CNI plugin or as chained? - - name: CHAINED_CNI_PLUGIN - value: "{{ .Values.cni.chained }}" - - name: REPAIR_ENABLED - value: "{{ .Values.cni.repair.enabled }}" - - name: REPAIR_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - - name: REPAIR_LABEL_PODS - value: "{{.Values.cni.repair.labelPods}}" - # Set to true to enable pod deletion - - name: REPAIR_DELETE_PODS - value: "{{.Values.cni.repair.deletePods}}" - - name: REPAIR_RUN_AS_DAEMON - value: "true" - - name: REPAIR_SIDECAR_ANNOTATION - value: "sidecar.istio.io/status" - - name: REPAIR_INIT_CONTAINER_NAME - value: "{{ .Values.cni.repair.initContainerName }}" - - name: REPAIR_BROKEN_POD_LABEL_KEY - value: "{{.Values.cni.repair.brokenPodLabelKey}}" - - name: REPAIR_BROKEN_POD_LABEL_VALUE - value: "{{.Values.cni.repair.brokenPodLabelValue}}" - volumeMounts: - - mountPath: /host/opt/cni/bin - name: cni-bin-dir - - mountPath: /host/etc/cni/net.d - name: cni-net-dir - - mountPath: /var/run/istio-cni - name: cni-log-dir - resources: -{{- if .Values.cni.resources }} -{{ toYaml .Values.cni.resources | trim | indent 12 }} -{{- else }} -{{ toYaml .Values.global.defaultResources | trim | indent 12 }} -{{- end }} -{{- if .Values.cni.taint.enabled }} - - name: taint-controller -{{- if contains "/" .Values.cni.image }} - image: "{{ .Values.cni.image }}" -{{- else }} - image: "{{ .Values.cni.hub | default .Values.global.hub }}/{{ .Values.cni.image | default "install-cni" }}:{{ .Values.cni.tag | default .Values.global.tag }}" -{{- end }} -{{- if or .Values.cni.pullPolicy .Values.global.imagePullPolicy }} - imagePullPolicy: {{ .Values.cni.pullPolicy | default .Values.global.imagePullPolicy }} -{{- end }} - command: ["/opt/local/bin/istio-cni-taint"] - securityContext: - runAsUser: 1337 - runAsGroup: 1337 - runAsNonRoot: true - env: - - name: "TAINT_RUN-AS-DAEMON" - value: "true" - - name: "TAINT_CONFIGMAP-NAME" - value: "istio-cni-taint-configmap" - - name: "TAINT_CONFIGMAP-NAMESPACE" - value: {{ .Release.Namespace | quote }} -{{- end }} - volumes: - # Used to install CNI. - - name: cni-bin-dir - hostPath: - path: {{ default "/opt/cni/bin" .Values.cni.cniBinDir }} - - name: cni-net-dir - hostPath: - path: {{ default "/etc/cni/net.d" .Values.cni.cniConfDir }} - # Used for UDS log - - name: cni-log-dir - hostPath: - path: /var/run/istio-cni diff --git a/manifests/charts/istio-cni/templates/resourcequota.yaml b/manifests/charts/istio-cni/templates/resourcequota.yaml deleted file mode 100644 index 15946ae72..000000000 --- a/manifests/charts/istio-cni/templates/resourcequota.yaml +++ /dev/null @@ -1,16 +0,0 @@ -{{- if .Values.cni.resourceQuotas.enabled }} -apiVersion: v1 -kind: ResourceQuota -metadata: - name: istio-cni-resource-quota - namespace: {{ .Release.Namespace }} -spec: - hard: - pods: {{ .Values.cni.resourceQuotas.pods | quote }} - scopeSelector: - matchExpressions: - - operator: In - scopeName: PriorityClass - values: - - system-node-critical -{{- end }} diff --git a/manifests/charts/istio-cni/templates/serviceaccount.yaml b/manifests/charts/istio-cni/templates/serviceaccount.yaml deleted file mode 100644 index 4645db63a..000000000 --- a/manifests/charts/istio-cni/templates/serviceaccount.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -{{- if .Values.global.imagePullSecrets }} -imagePullSecrets: -{{- range .Values.global.imagePullSecrets }} - - name: {{ . }} -{{- end }} -{{- end }} -metadata: - name: istio-cni - namespace: {{ .Release.Namespace }} - labels: - app: istio-cni - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Cni" diff --git a/manifests/charts/istio-cni/values.yaml b/manifests/charts/istio-cni/values.yaml deleted file mode 100644 index 9946081db..000000000 --- a/manifests/charts/istio-cni/values.yaml +++ /dev/null @@ -1,97 +0,0 @@ -cni: - hub: "" - tag: "" - image: install-cni - pullPolicy: "" - - logLevel: info - - # Configuration file to insert istio-cni plugin configuration - # by default this will be the first file found in the cni-conf-dir - # Example - # cniConfFileName: 10-calico.conflist - - # CNI bin and conf dir override settings - # defaults: - cniBinDir: /opt/cni/bin - cniConfDir: /etc/cni/net.d - cniConfFileName: "" - - excludeNamespaces: - - dubbo-system - - kube-system - - # Custom annotations on pod level, if you need them - podAnnotations: {} - - # If this value is set a RoleBinding will be created - # in the same namespace as the istio-cni DaemonSet is created. - # This can be used to bind a preexisting ClusterRole to the istio/cni ServiceAccount - # e.g. if you use PodSecurityPolicies - psp_cluster_role: "" - - # Deploy the config files as plugin chain (value "true") or as standalone files in the conf dir (value "false")? - # Some k8s flavors (e.g. OpenShift) do not support the chain approach, set to false if this is the case - chained: true - - # Allow the istio-cni container to run in privileged mode, needed for some platforms (e.g. OpenShift) - privileged: false - - repair: - enabled: true - hub: "" - tag: "" - - labelPods: true - deletePods: true - - initContainerName: "istio-validation" - - brokenPodLabelKey: "cni.istio.io/uninitialized" - brokenPodLabelValue: "true" - - resources: - requests: - cpu: 100m - memory: 100Mi - - # Experimental taint controller for further race condition mitigation - taint: - enabled: false - - resourceQuotas: - enabled: false - pods: 5000 - -# Revision is set as 'version' label and part of the resource names when installing multiple control planes. -revision: "" - -# For Helm compatibility. -ownerName: "" - -global: - # Default hub for Istio images. - # Releases are published to docker hub under 'istio' project. - # Dev builds from prow are on gcr.io - hub: apache - - # Default tag for Istio images. - tag: latest - - # Specify image pull policy if default behavior isn't desired. - # Default behavior: latest images will be Always else IfNotPresent. - imagePullPolicy: "" - - # ImagePullSecrets for all ServiceAccount, list of secrets in the same namespace - # to use for pulling any images in pods that reference this ServiceAccount. - # For components that don't use ServiceAccounts (i.e. grafana, servicegraph, tracing) - # ImagePullSecrets will be added to the corresponding Deployment(StatefulSet) objects. - # Must be set for any cluster configured with private docker registry. - imagePullSecrets: [] - # - private-registry-key - - # Default resources allocated - defaultResources: - requests: - cpu: 100m - memory: 100Mi diff --git a/manifests/charts/istio-control/istio-discovery/Chart.yaml b/manifests/charts/istio-control/istio-discovery/Chart.yaml deleted file mode 100644 index e47028a86..000000000 --- a/manifests/charts/istio-control/istio-discovery/Chart.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: v1 -name: istiod -# This version is never actually shipped. istio/release-builder will replace it at build-time -# with the appropriate version -version: 1.0.0 -appVersion: 1.0.0 -tillerVersion: ">=2.7.2" -description: Helm chart for istio control plane -keywords: - - istio - - istiod - - istio-discovery -sources: - - http://github.com/istio/istio -engine: gotpl -icon: https://istio.io/latest/favicons/android-192x192.png diff --git a/manifests/charts/istio-control/istio-discovery/README.md b/manifests/charts/istio-control/istio-discovery/README.md deleted file mode 100644 index c0855d2f9..000000000 --- a/manifests/charts/istio-control/istio-discovery/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# Istiod Helm Chart - -This chart installs an Istiod deployment. - -## Setup Repo Info - -```console -helm repo add istio https://istio-release.storage.googleapis.com/charts -helm repo update -``` - -_See [helm repo](https://helm.sh/docs/helm/helm_repo/) for command documentation._ - -## Installing the Chart - -Before installing, ensure CRDs are installed in the cluster (from the `istio/base` chart). - -To install the chart with the release name `istiod`: - -```console -kubectl create namespace dubbo-system -helm install istiod istio/istiod --namespace dubbo-system -``` - -## Uninstalling the Chart - -To uninstall/delete the `istiod` deployment: - -```console -helm delete istiod --namespace dubbo-system -``` - -## Configuration - -To view support configuration options and documentation, run: - -```console -helm show values istio/istiod -``` - -### Examples - -#### Configuring mesh configuration settings - -Any [Mesh Config](https://istio.io/latest/docs/reference/config/istio.mesh.v1alpha1/) options can be configured like below: - -```yaml -meshConfig: - accessLogFile: /dev/stdout -``` - -#### Revisions - -Control plane revisions allow deploying multiple versions of the control plane in the same cluster. -This allows safe [canary upgrades](https://istio.io/latest/docs/setup/upgrade/canary/) - -```yaml -revision: my-revision-name -``` diff --git a/manifests/charts/istio-control/istio-discovery/files/dubbo.yaml b/manifests/charts/istio-control/istio-discovery/files/dubbo.yaml deleted file mode 100644 index 71a442b17..000000000 --- a/manifests/charts/istio-control/istio-discovery/files/dubbo.yaml +++ /dev/null @@ -1,166 +0,0 @@ -{{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} -metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } -spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} diff --git a/manifests/charts/istio-control/istio-discovery/files/gateway-injection-template.yaml b/manifests/charts/istio-control/istio-discovery/files/gateway-injection-template.yaml deleted file mode 100644 index 3d86c5e00..000000000 --- a/manifests/charts/istio-control/istio-discovery/files/gateway-injection-template.yaml +++ /dev/null @@ -1,225 +0,0 @@ -{{- $containers := list }} -{{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} -metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } -spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} diff --git a/manifests/charts/istio-control/istio-discovery/files/gen-istio.yaml b/manifests/charts/istio-control/istio-discovery/files/gen-istio.yaml deleted file mode 100644 index 5eefd3879..000000000 --- a/manifests/charts/istio-control/istio-discovery/files/gen-istio.yaml +++ /dev/null @@ -1,3225 +0,0 @@ ---- -# Source: istiod/templates/poddisruptionbudget.yaml -apiVersion: policy/v1 -kind: PodDisruptionBudget -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - release: istio - istio: pilot -spec: - minAvailable: 1 - selector: - matchLabels: - app: istiod - istio: pilot ---- -# Source: istiod/templates/serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - release: istio ---- -# Source: istiod/templates/configmap.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: istio - namespace: dubbo-system - labels: - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - release: istio -data: - - # Configuration file for the mesh networks to be used by the Split Horizon EDS. - meshNetworks: |- - networks: {} - - mesh: |- - defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - tracing: - zipkin: - address: zipkin.dubbo-system:9411 - enablePrometheusMerge: true - rootNamespace: null - trustDomain: cluster.local ---- -# Source: istiod/templates/istiod-injector-configmap.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: istio-sidecar-injector - namespace: dubbo-system - labels: - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - release: istio -data: - - values: |- - { - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } - } - - # To disable injection: use omitSidecarInjectorConfigMap, which disables the webhook patching - # and istiod webhook functionality. - # - # New fields should not use Values - it is a 'primary' config object, users should be able - # to fine tune it or use it with kube-inject. - config: |- - # defaultTemplates defines the default template to use for pods that do not explicitly specify a template - defaultTemplates: [sidecar] - policy: enabled - alwaysInjectSelector: - [] - neverInjectSelector: - [] - injectedAnnotations: - template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" - templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - dubbo: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} ---- -# Source: istiod/templates/clusterrole.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istiod-clusterrole-dubbo-system - labels: - app: istiod - release: istio -rules: - # sidecar injection controller - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["mutatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update", "patch"] - - # configuration validation webhook controller - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["validatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update"] - - # istio configuration - # removing CRD permissions can break older versions of Istio running alongside this control plane (https://github.com/istio/istio/issues/29382) - # please proceed with caution - - apiGroups: ["config.istio.io", "security.istio.io", "networking.istio.io", "authentication.istio.io", "rbac.istio.io", "telemetry.istio.io", "extensions.istio.io"] - verbs: ["get", "watch", "list"] - resources: ["*"] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "workloadentries" ] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "workloadentries/status" ] - - # auto-detect installed CRD definitions - - apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "list", "watch"] - - # discovery and routing - - apiGroups: [""] - resources: ["pods", "nodes", "services", "namespaces", "endpoints"] - verbs: ["get", "list", "watch"] - - apiGroups: ["discovery.k8s.io"] - resources: ["endpointslices"] - verbs: ["get", "list", "watch"] - - # ingress controller - - apiGroups: ["networking.k8s.io"] - resources: ["ingresses", "ingressclasses"] - verbs: ["get", "list", "watch"] - - apiGroups: ["networking.k8s.io"] - resources: ["ingresses/status"] - verbs: ["*"] - - # required for CA's namespace controller - - apiGroups: [""] - resources: ["configmaps"] - verbs: ["create", "get", "list", "watch", "update"] - - # Istiod and bootstrap. - - apiGroups: ["certificates.k8s.io"] - resources: - - "certificatesigningrequests" - - "certificatesigningrequests/approval" - - "certificatesigningrequests/status" - verbs: ["update", "create", "get", "delete", "watch"] - - apiGroups: ["certificates.k8s.io"] - resources: - - "signers" - resourceNames: - - "kubernetes.io/legacy-unknown" - verbs: ["approve"] - - # Used by Istiod to verify the JWT tokens - - apiGroups: ["authentication.k8s.io"] - resources: ["tokenreviews"] - verbs: ["create"] - - # Used by Istiod to verify gateway SDS - - apiGroups: ["authorization.k8s.io"] - resources: ["subjectaccessreviews"] - verbs: ["create"] - - # Use for Kubernetes Service APIs - - apiGroups: ["networking.x-k8s.io", "gateway.networking.k8s.io"] - resources: ["*"] - verbs: ["get", "watch", "list"] - - apiGroups: ["networking.x-k8s.io", "gateway.networking.k8s.io"] - resources: ["*"] # TODO: should be on just */status but wildcard is not supported - verbs: ["update", "patch"] - - apiGroups: ["gateway.networking.k8s.io"] - resources: ["gatewayclasses"] - verbs: ["create", "update", "patch", "delete"] - - # Needed for multicluster secret reading, possibly ingress certs in the future - - apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "watch", "list"] - - # Used for MCS serviceexport management - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceexports"] - verbs: [ "get", "watch", "list", "create", "delete"] - - # Used for MCS serviceimport management - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceimports"] - verbs: ["get", "watch", "list"] - - # Used for Service Name Mapping - - apiGroups: ["extensions.istio.io"] - resources: ["servicenamemappings"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - # Used for Service Metadata - - apiGroups: ["extensions.istio.io"] - resources: ["servicemetadatas"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] ---- -# Source: istiod/templates/clusterrole.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istiod-gateway-controller-dubbo-system - labels: - app: istiod - release: istio -rules: - - apiGroups: ["apps"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "deployments" ] - - apiGroups: [""] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "services" ] ---- -# Source: istiod/templates/reader-clusterrole.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istio-reader-clusterrole-dubbo-system - labels: - app: istio-reader - release: istio -rules: - - apiGroups: - - "config.istio.io" - - "security.istio.io" - - "networking.istio.io" - - "authentication.istio.io" - - "rbac.istio.io" - resources: ["*"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["endpoints", "pods", "services", "nodes", "replicationcontrollers", "namespaces", "secrets"] - verbs: ["get", "list", "watch"] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list" ] - resources: [ "workloadentries" ] - - apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "list", "watch"] - - apiGroups: ["discovery.k8s.io"] - resources: ["endpointslices"] - verbs: ["get", "list", "watch"] - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceexports"] - verbs: ["get", "list", "watch", "create", "delete"] - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceimports"] - verbs: ["get", "list", "watch"] - - apiGroups: ["apps"] - resources: ["replicasets"] - verbs: ["get", "list", "watch"] - - apiGroups: ["authentication.k8s.io"] - resources: ["tokenreviews"] - verbs: ["create"] - - apiGroups: ["authorization.k8s.io"] - resources: ["subjectaccessreviews"] - verbs: ["create"] ---- -# Source: istiod/templates/clusterrolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istiod-clusterrole-dubbo-system - labels: - app: istiod - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istiod-clusterrole-dubbo-system -subjects: - - kind: ServiceAccount - name: istiod - namespace: dubbo-system ---- -# Source: istiod/templates/clusterrolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istiod-gateway-controller-dubbo-system - labels: - app: istiod - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istiod-gateway-controller-dubbo-system -subjects: -- kind: ServiceAccount - name: istiod - namespace: dubbo-system ---- -# Source: istiod/templates/reader-clusterrolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istio-reader-clusterrole-dubbo-system - labels: - app: istio-reader - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istio-reader-clusterrole-dubbo-system -subjects: - - kind: ServiceAccount - name: istio-reader-service-account - namespace: dubbo-system ---- -# Source: istiod/templates/role.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - release: istio -rules: -# permissions to verify the webhook is ready and rejecting -# invalid config. We use --server-dry-run so no config is persisted. -- apiGroups: ["networking.istio.io"] - verbs: ["create"] - resources: ["gateways"] - -# For storing CA secret -- apiGroups: [""] - resources: ["secrets"] - # TODO lock this down to istio-ca-cert if not using the DNS cert mesh config - verbs: ["create", "get", "watch", "list", "update", "delete"] ---- -# Source: istiod/templates/rolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: istiod -subjects: - - kind: ServiceAccount - name: istiod - namespace: dubbo-system ---- -# Source: istiod/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: istiod - namespace: dubbo-system - labels: - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - app: istiod - istio: pilot - release: istio -spec: - ports: - - port: 15010 - name: grpc-xds # plaintext - protocol: TCP - - port: 15012 - name: https-dns # mTLS with k8s-signed cert - protocol: TCP - - port: 443 - name: https-webhook # validation and injection - targetPort: 15017 - protocol: TCP - - port: 15014 - name: http-monitoring # prometheus stats - protocol: TCP - selector: - app: istiod - # Label used by the 'default' service. For versioned deployments we match with app and version. - # This avoids default deployment picking the canary - istio: pilot ---- -# Source: istiod/templates/deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - istio: pilot - release: istio -spec: - strategy: - rollingUpdate: - maxSurge: 100% - maxUnavailable: 25% - selector: - matchLabels: - istio: pilot - template: - metadata: - labels: - app: istiod - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - sidecar.istio.io/inject: "false" - operator.istio.io/component: "Pilot" - istio: pilot - annotations: - prometheus.io/port: "15014" - prometheus.io/scrape: "true" - sidecar.istio.io/inject: "false" - spec: - serviceAccountName: istiod - securityContext: - fsGroup: 1337 - containers: - - name: discovery - image: "apache/dubbo-pilot:latest" - args: - - "discovery" - - --monitoringAddr=:15014 - - --log_output_level=default:info - - --domain - - cluster.local - - --keepaliveMaxServerConnectionAge - - "30m" - ports: - - containerPort: 8080 - protocol: TCP - - containerPort: 15010 - protocol: TCP - - containerPort: 15017 - protocol: TCP - readinessProbe: - httpGet: - path: /ready - port: 8080 - initialDelaySeconds: 1 - periodSeconds: 3 - timeoutSeconds: 5 - env: - - name: REVISION - value: "default" - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.serviceAccountName - - name: KUBECONFIG - value: /var/run/secrets/remote/config - - name: PILOT_TRACE_SAMPLING - value: "1" - - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_OUTBOUND - value: "true" - - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_INBOUND - value: "true" - - name: ISTIOD_ADDR - value: istiod.dubbo-system.svc:15012 - - name: PILOT_ENABLE_ANALYSIS - value: "false" - - name: CLUSTER_ID - value: "Kubernetes" - resources: - requests: - cpu: 500m - memory: 2048Mi - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsUser: 1337 - runAsGroup: 1337 - runAsNonRoot: true - capabilities: - drop: - - ALL - volumeMounts: - - name: istio-token - mountPath: /var/run/secrets/tokens - readOnly: true - - name: local-certs - mountPath: /var/run/secrets/istio-dns - - name: cacerts - mountPath: /etc/cacerts - readOnly: true - - name: istio-kubeconfig - mountPath: /var/run/secrets/remote - readOnly: true - volumes: - # Technically not needed on this pod - but it helps debugging/testing SDS - # Should be removed after everything works. - - emptyDir: - medium: Memory - name: local-certs - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - # Optional: user-generated root - - name: cacerts - secret: - secretName: cacerts - optional: true - - name: istio-kubeconfig - secret: - secretName: istio-kubeconfig - optional: true ---- -# Source: istiod/templates/autoscale.yaml -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" -spec: - maxReplicas: 5 - minReplicas: 1 - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: istiod - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: 80 ---- -# Source: istiod/templates/revision-tags.yaml -# Adapted from istio-discovery/templates/mutatingwebhook.yaml -# Removed paths for legacy and default selectors since a revision tag -# is inherently created from a specific revision ---- -# Source: istiod/templates/telemetryv2_1.11.yaml -# Note: http stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.11 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats ---- -# Source: istiod/templates/telemetryv2_1.11.yaml -# Note: tcp stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.11 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: tcp_stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" ---- -# Source: istiod/templates/telemetryv2_1.12.yaml -# Note: http stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.12 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats ---- -# Source: istiod/templates/telemetryv2_1.12.yaml -# Note: tcp stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.12 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: tcp_stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" ---- -# Source: istiod/templates/telemetryv2_1.13.yaml -# Note: http stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.13 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats ---- -# Source: istiod/templates/telemetryv2_1.13.yaml -# Note: tcp stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.13 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: tcp_stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" ---- -# Source: istiod/templates/telemetryv2_1.14.yaml -# Note: http stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.14 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats ---- -# Source: istiod/templates/telemetryv2_1.14.yaml -# Note: tcp stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.14 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: tcp_stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" ---- -# Source: istiod/templates/telemetryv2_1.15.yaml -# Note: http stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.15 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats ---- -# Source: istiod/templates/telemetryv2_1.15.yaml -# Note: tcp stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.15 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: tcp_stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" ---- -# Source: istiod/templates/mutatingwebhook.yaml -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - name: istio-sidecar-injector - labels: - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - app: sidecar-injector - release: istio -webhooks: -- name: rev.namespace.sidecar-injector.istio.io - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: "/inject" - port: 443 - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] - namespaceSelector: - matchExpressions: - - key: istio.io/rev - operator: In - values: - - "default" - - key: istio-injection - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" -- name: rev.object.sidecar-injector.istio.io - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: "/inject" - port: 443 - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] - namespaceSelector: - matchExpressions: - - key: istio.io/rev - operator: DoesNotExist - - key: istio-injection - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" - - key: istio.io/rev - operator: In - values: - - "default" -- name: namespace.sidecar-injector.istio.io - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: "/inject" - port: 443 - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: In - values: - - enabled - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" -- name: object.sidecar-injector.istio.io - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: "/inject" - port: 443 - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: DoesNotExist - - key: istio.io/rev - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: In - values: - - "true" - - key: istio.io/rev - operator: DoesNotExist diff --git a/manifests/charts/istio-control/istio-discovery/files/grpc-agent.yaml b/manifests/charts/istio-control/istio-discovery/files/grpc-agent.yaml deleted file mode 100644 index 5816a46ee..000000000 --- a/manifests/charts/istio-control/istio-discovery/files/grpc-agent.yaml +++ /dev/null @@ -1,244 +0,0 @@ -{{- $containers := list }} -{{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} -metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } -spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations -{{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} -{{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} diff --git a/manifests/charts/istio-control/istio-discovery/files/grpc-simple.yaml b/manifests/charts/istio-control/istio-discovery/files/grpc-simple.yaml deleted file mode 100644 index 4346a41c6..000000000 --- a/manifests/charts/istio-control/istio-discovery/files/grpc-simple.yaml +++ /dev/null @@ -1,64 +0,0 @@ -metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" -spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} diff --git a/manifests/charts/istio-control/istio-discovery/files/injection-template.yaml b/manifests/charts/istio-control/istio-discovery/files/injection-template.yaml deleted file mode 100644 index 9a94e3b26..000000000 --- a/manifests/charts/istio-control/istio-discovery/files/injection-template.yaml +++ /dev/null @@ -1,486 +0,0 @@ -{{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} -{{- end }} -{{- $containers := list }} -{{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} -metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} -{{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} -{{- end }} - } -spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} diff --git a/manifests/charts/istio-control/istio-discovery/kustomization.yaml b/manifests/charts/istio-control/istio-discovery/kustomization.yaml deleted file mode 100644 index 7f9bbc394..000000000 --- a/manifests/charts/istio-control/istio-discovery/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -resources: - - files/gen-istio.yaml diff --git a/manifests/charts/istio-control/istio-discovery/templates/NOTES.txt b/manifests/charts/istio-control/istio-discovery/templates/NOTES.txt deleted file mode 100644 index 3f1397b85..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/NOTES.txt +++ /dev/null @@ -1,21 +0,0 @@ -"istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}" successfully installed! - -To learn more about the release, try: - $ helm status {{ .Release.Name }} - $ helm get all {{ .Release.Name }} - -Next steps: - * Deploy a Gateway: https://istio.io/latest/docs/setup/additional-setup/gateway/ - * Try out our tasks to get started on common configurations: - * https://istio.io/latest/docs/tasks/traffic-management - * https://istio.io/latest/docs/tasks/security/ - * https://istio.io/latest/docs/tasks/policy-enforcement/ - * https://istio.io/latest/docs/tasks/policy-enforcement/ - * Review the list of actively supported releases, CVE publications and our hardening guide: - * https://istio.io/latest/docs/releases/supported-releases/ - * https://istio.io/latest/news/security/ - * https://istio.io/latest/docs/ops/best-practices/security/ - -For further documentation see https://istio.io website - -Tell us how your install/upgrade experience went at https://forms.gle/yEtCbt45FZ3VoDT5A diff --git a/manifests/charts/istio-control/istio-discovery/templates/autoscale.yaml b/manifests/charts/istio-control/istio-discovery/templates/autoscale.yaml deleted file mode 100644 index 2edcc745e..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/autoscale.yaml +++ /dev/null @@ -1,59 +0,0 @@ -{{- if and .Values.pilot.autoscaleEnabled .Values.pilot.autoscaleMin .Values.pilot.autoscaleMax }} -{{- if not .Values.global.autoscalingv2API }} -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - namespace: {{ .Release.Namespace }} - labels: - app: istiod - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Pilot" -spec: - maxReplicas: {{ .Values.pilot.autoscaleMax }} - minReplicas: {{ .Values.pilot.autoscaleMin }} - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - metrics: - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ .Values.pilot.cpu.targetAverageUtilization }} ---- -{{- else }} -{{- if (semverCompare ">=1.23-0" .Capabilities.KubeVersion.GitVersion)}} -apiVersion: autoscaling/v2 -{{- else }} -apiVersion: autoscaling/v2beta2 -{{- end }} -kind: HorizontalPodAutoscaler -metadata: - name: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - namespace: {{ .Release.Namespace }} - labels: - app: istiod - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Pilot" -spec: - maxReplicas: {{ .Values.pilot.autoscaleMax }} - minReplicas: {{ .Values.pilot.autoscaleMin }} - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: {{ .Values.pilot.cpu.targetAverageUtilization }} ---- -{{- end }} -{{- end }} diff --git a/manifests/charts/istio-control/istio-discovery/templates/clusterrole.yaml b/manifests/charts/istio-control/istio-discovery/templates/clusterrole.yaml deleted file mode 100644 index 1671f9dcc..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/clusterrole.yaml +++ /dev/null @@ -1,147 +0,0 @@ -{{ $mcsAPIGroup := or .Values.pilot.env.MCS_API_GROUP "multicluster.x-k8s.io" }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istiod-clusterrole{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }}-{{ .Release.Namespace }} - labels: - app: istiod - release: {{ .Release.Name }} -rules: - # sidecar injection controller - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["mutatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update", "patch"] - - # configuration validation webhook controller - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["validatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update"] - - # istio configuration - # removing CRD permissions can break older versions of Istio running alongside this control plane (https://github.com/istio/istio/issues/29382) - # please proceed with caution - - apiGroups: ["config.istio.io", "security.istio.io", "networking.istio.io", "authentication.istio.io", "rbac.istio.io", "telemetry.istio.io", "extensions.istio.io"] - verbs: ["get", "watch", "list"] - resources: ["*"] -{{- if .Values.global.istiod.enableAnalysis }} - - apiGroups: ["config.istio.io", "security.istio.io", "networking.istio.io", "authentication.istio.io", "rbac.istio.io", "telemetry.istio.io", "extensions.istio.io"] - verbs: ["update"] - # TODO: should be on just */status but wildcard is not supported - resources: ["*"] -{{- end }} - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "workloadentries" ] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "workloadentries/status" ] - - # auto-detect installed CRD definitions - - apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "list", "watch"] - - # discovery and routing - - apiGroups: [""] - resources: ["pods", "nodes", "services", "namespaces", "endpoints"] - verbs: ["get", "list", "watch"] - - apiGroups: ["discovery.k8s.io"] - resources: ["endpointslices"] - verbs: ["get", "list", "watch"] - - # ingress controller -{{- if .Values.global.istiod.enableAnalysis }} - - apiGroups: ["extensions", "networking.k8s.io"] - resources: ["ingresses"] - verbs: ["get", "list", "watch"] - - apiGroups: ["extensions", "networking.k8s.io"] - resources: ["ingresses/status"] - verbs: ["*"] -{{- end}} - - apiGroups: ["networking.k8s.io"] - resources: ["ingresses", "ingressclasses"] - verbs: ["get", "list", "watch"] - - apiGroups: ["networking.k8s.io"] - resources: ["ingresses/status"] - verbs: ["*"] - - # required for CA's namespace controller - - apiGroups: [""] - resources: ["configmaps"] - verbs: ["create", "get", "list", "watch", "update"] - - # Istiod and bootstrap. - - apiGroups: ["certificates.k8s.io"] - resources: - - "certificatesigningrequests" - - "certificatesigningrequests/approval" - - "certificatesigningrequests/status" - verbs: ["update", "create", "get", "delete", "watch"] - - apiGroups: ["certificates.k8s.io"] - resources: - - "signers" - resourceNames: - - "kubernetes.io/legacy-unknown" - verbs: ["approve"] - - # Used by Istiod to verify the JWT tokens - - apiGroups: ["authentication.k8s.io"] - resources: ["tokenreviews"] - verbs: ["create"] - - # Used by Istiod to verify gateway SDS - - apiGroups: ["authorization.k8s.io"] - resources: ["subjectaccessreviews"] - verbs: ["create"] - - # Use for Kubernetes Service APIs - - apiGroups: ["networking.x-k8s.io", "gateway.networking.k8s.io"] - resources: ["*"] - verbs: ["get", "watch", "list"] - - apiGroups: ["networking.x-k8s.io", "gateway.networking.k8s.io"] - resources: ["*"] # TODO: should be on just */status but wildcard is not supported - verbs: ["update", "patch"] - - apiGroups: ["gateway.networking.k8s.io"] - resources: ["gatewayclasses"] - verbs: ["create", "update", "patch", "delete"] - - # Needed for multicluster secret reading, possibly ingress certs in the future - - apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "watch", "list"] - - # Used for MCS serviceexport management - - apiGroups: ["{{ $mcsAPIGroup }}"] - resources: ["serviceexports"] - verbs: [ "get", "watch", "list", "create", "delete"] - - # Used for MCS serviceimport management - - apiGroups: ["{{ $mcsAPIGroup }}"] - resources: ["serviceimports"] - verbs: ["get", "watch", "list"] - - # Used for Service Name Mapping - - apiGroups: ["extensions.istio.io"] - resources: ["servicenamemappings"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - # Used for Service Metadata - - apiGroups: ["extensions.istio.io"] - resources: ["servicemetadatas"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] ---- -{{- if not (eq (toString .Values.pilot.env.PILOT_ENABLE_GATEWAY_API_DEPLOYMENT_CONTROLLER) "false") }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istiod-gateway-controller{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }}-{{ .Release.Namespace }} - labels: - app: istiod - release: {{ .Release.Name }} -rules: - - apiGroups: ["apps"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "deployments" ] - - apiGroups: [""] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "services" ] -{{- end }} diff --git a/manifests/charts/istio-control/istio-discovery/templates/clusterrolebinding.yaml b/manifests/charts/istio-control/istio-discovery/templates/clusterrolebinding.yaml deleted file mode 100644 index e7235dbbc..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/clusterrolebinding.yaml +++ /dev/null @@ -1,33 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istiod-clusterrole{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }}-{{ .Release.Namespace }} - labels: - app: istiod - release: {{ .Release.Name }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istiod-clusterrole{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }}-{{ .Release.Namespace }} -subjects: - - kind: ServiceAccount - name: istiod{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }} - namespace: {{ .Values.global.istioNamespace }} ---- -{{- if not (eq (toString .Values.pilot.env.PILOT_ENABLE_GATEWAY_API_DEPLOYMENT_CONTROLLER) "false") }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istiod-gateway-controller{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }}-{{ .Release.Namespace }} - labels: - app: istiod - release: {{ .Release.Name }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istiod-gateway-controller{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }}-{{ .Release.Namespace }} -subjects: -- kind: ServiceAccount - name: istiod{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }} - namespace: {{ .Values.global.istioNamespace }} -{{- end }} diff --git a/manifests/charts/istio-control/istio-discovery/templates/configmap-jwks.yaml b/manifests/charts/istio-control/istio-discovery/templates/configmap-jwks.yaml deleted file mode 100644 index 7b719ac7e..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/configmap-jwks.yaml +++ /dev/null @@ -1,14 +0,0 @@ -{{- if .Values.pilot.jwksResolverExtraRootCA }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: pilot-jwks-extra-cacerts{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - namespace: {{ .Release.Namespace }} - labels: - release: {{ .Release.Name }} - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Pilot" -data: - extra.pem: {{ .Values.pilot.jwksResolverExtraRootCA | quote }} -{{- end }} diff --git a/manifests/charts/istio-control/istio-discovery/templates/configmap.yaml b/manifests/charts/istio-control/istio-discovery/templates/configmap.yaml deleted file mode 100644 index 8e57c8186..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/configmap.yaml +++ /dev/null @@ -1,100 +0,0 @@ -{{- define "mesh" }} - # The trust domain corresponds to the trust root of a system. - # Refer to https://github.com/spiffe/spiffe/blob/master/standards/SPIFFE-ID.md#21-trust-domain - trustDomain: "cluster.local" - - # The namespace to treat as the administrative root namespace for Istio configuration. - # When processing a leaf namespace Istio will search for declarations in that namespace first - # and if none are found it will search in the root namespace. Any matching declaration found in the root namespace - # is processed as if it were declared in the leaf namespace. - rootNamespace: {{ .Values.meshConfig.rootNamespace | default .Values.global.istioNamespace }} - - defaultConfig: - {{- if .Values.global.meshID }} - meshId: {{ .Values.global.meshID }} - {{- end }} - tracing: - {{- if eq .Values.global.proxy.tracer "lightstep" }} - lightstep: - # Address of the LightStep Satellite pool - address: {{ .Values.global.tracer.lightstep.address }} - # Access Token used to communicate with the Satellite pool - accessToken: {{ .Values.global.tracer.lightstep.accessToken }} - {{- else if eq .Values.global.proxy.tracer "zipkin" }} - zipkin: - # Address of the Zipkin collector - address: {{ .Values.global.tracer.zipkin.address | default (print "zipkin." .Values.global.istioNamespace ":9411") }} - {{- else if eq .Values.global.proxy.tracer "datadog" }} - datadog: - # Address of the Datadog Agent - address: {{ .Values.global.tracer.datadog.address | default "$(HOST_IP):8126" }} - {{- else if eq .Values.global.proxy.tracer "stackdriver" }} - stackdriver: - # enables trace output to stdout. - {{- if $.Values.global.tracer.stackdriver.debug }} - debug: {{ $.Values.global.tracer.stackdriver.debug }} - {{- end }} - {{- if $.Values.global.tracer.stackdriver.maxNumberOfAttributes }} - # The global default max number of attributes per span. - maxNumberOfAttributes: {{ $.Values.global.tracer.stackdriver.maxNumberOfAttributes | default "200" }} - {{- end }} - {{- if $.Values.global.tracer.stackdriver.maxNumberOfAnnotations }} - # The global default max number of annotation events per span. - maxNumberOfAnnotations: {{ $.Values.global.tracer.stackdriver.maxNumberOfAnnotations | default "200" }} - {{- end }} - {{- if $.Values.global.tracer.stackdriver.maxNumberOfMessageEvents }} - # The global default max number of message events per span. - maxNumberOfMessageEvents: {{ $.Values.global.tracer.stackdriver.maxNumberOfMessageEvents | default "200" }} - {{- end }} - {{- else if eq .Values.global.proxy.tracer "openCensusAgent" }} - {{/* Fill in openCensusAgent configuration from meshConfig so it isn't overwritten below */}} -{{ toYaml $.Values.meshConfig.defaultConfig.tracing | indent 8 }} - {{- else }} - {} - {{- end }} - {{- if .Values.global.remotePilotAddress }} - {{- if .Values.pilot.enabled }} - discoveryAddress: {{ printf "istiod-remote.%s.svc" .Release.Namespace }}:15012 - {{- else }} - discoveryAddress: {{ printf "istiod.%s.svc" .Release.Namespace }}:15012 - {{- end }} - {{- else }} - discoveryAddress: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{.Release.Namespace}}.svc:15012 - {{- end }} -{{- end }} - -{{/* We take the mesh config above, defined with individual values.yaml, and merge with .Values.meshConfig */}} -{{/* The intent here is that meshConfig.foo becomes the API, rather than re-inventing the API in values.yaml */}} -{{- $originalMesh := include "mesh" . | fromYaml }} -{{- $mesh := mergeOverwrite $originalMesh .Values.meshConfig }} - -{{- if .Values.pilot.configMap }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: istio{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - namespace: {{ .Release.Namespace }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Pilot" - release: {{ .Release.Name }} -data: - - # Configuration file for the mesh networks to be used by the Split Horizon EDS. - meshNetworks: |- - {{- if .Values.global.meshNetworks }} - networks: -{{ toYaml .Values.global.meshNetworks | trim | indent 6 }} - {{- else }} - networks: {} - {{- end }} - - mesh: |- -{{- if .Values.meshConfig }} -{{ $mesh | toYaml | indent 4 }} -{{- else }} -{{- include "mesh" . }} -{{- end }} ---- -{{- end }} diff --git a/manifests/charts/istio-control/istio-discovery/templates/deployment.yaml b/manifests/charts/istio-control/istio-discovery/templates/deployment.yaml deleted file mode 100644 index 4c1bc3527..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/deployment.yaml +++ /dev/null @@ -1,228 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - namespace: {{ .Release.Namespace }} - labels: - app: istiod - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Pilot" - istio: pilot - release: {{ .Release.Name }} -{{- range $key, $val := .Values.pilot.deploymentLabels }} - {{ $key }}: "{{ $val }}" -{{- end }} -spec: -{{- if not .Values.pilot.autoscaleEnabled }} -{{- if .Values.pilot.replicaCount }} - replicas: {{ .Values.pilot.replicaCount }} -{{- end }} -{{- end }} - strategy: - rollingUpdate: - maxSurge: {{ .Values.pilot.rollingMaxSurge }} - maxUnavailable: {{ .Values.pilot.rollingMaxUnavailable }} - selector: - matchLabels: - {{- if ne .Values.revision "" }} - app: istiod - istio.io/rev: {{ .Values.revision | default "default" }} - {{- else }} - istio: pilot - {{- end }} - template: - metadata: - labels: - app: istiod - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - sidecar.istio.io/inject: "false" - operator.istio.io/component: "Pilot" - {{- if ne .Values.revision "" }} - istio: istiod - {{- else }} - istio: pilot - {{- end }} - {{- range $key, $val := .Values.pilot.podLabels }} - {{ $key }}: "{{ $val }}" - {{- end }} - annotations: - {{- if .Values.meshConfig.enablePrometheusMerge }} - prometheus.io/port: "15014" - prometheus.io/scrape: "true" - {{- end }} - sidecar.istio.io/inject: "false" - {{- if .Values.pilot.podAnnotations }} -{{ toYaml .Values.pilot.podAnnotations | indent 8 }} - {{- end }} - spec: -{{- if .Values.pilot.nodeSelector }} - nodeSelector: -{{ toYaml .Values.pilot.nodeSelector | indent 8 }} -{{- end }} -{{- with .Values.pilot.affinity }} - affinity: -{{- toYaml . | nindent 8 }} -{{- end }} -{{- with .Values.pilot.tolerations }} - tolerations: -{{- toYaml . | nindent 8 }} -{{- end }} - serviceAccountName: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} -{{- if .Values.global.priorityClassName }} - priorityClassName: "{{ .Values.global.priorityClassName }}" -{{- end }} - securityContext: - fsGroup: 1337 - containers: - - name: discovery -{{- if contains "/" .Values.pilot.image }} - image: "{{ .Values.pilot.image }}" -{{- else }} - image: "{{ .Values.pilot.hub | default .Values.global.hub }}/{{ .Values.pilot.image | default "dubbo-pilot" }}:{{ .Values.pilot.tag | default .Values.global.tag }}" -{{- end }} -{{- if .Values.global.imagePullPolicy }} - imagePullPolicy: {{ .Values.global.imagePullPolicy }} -{{- end }} - args: - - "discovery" - - --monitoringAddr=:15014 -{{- if .Values.global.logging.level }} - - --log_output_level={{ .Values.global.logging.level }} -{{- end}} -{{- if .Values.global.logAsJson }} - - --log_as_json -{{- end }} - - --domain - - {{ .Values.global.proxy.clusterDomain }} -{{- if .Values.global.oneNamespace }} - - "-a" - - {{ .Release.Namespace }} -{{- end }} -{{- if .Values.pilot.plugins }} - - --plugins={{ .Values.pilot.plugins }} -{{- end }} - - --keepaliveMaxServerConnectionAge - - "{{ .Values.pilot.keepaliveMaxServerConnectionAge }}" - ports: - - containerPort: 8080 - protocol: TCP - - containerPort: 15010 - protocol: TCP - - containerPort: 15017 - protocol: TCP - readinessProbe: - httpGet: - path: /ready - port: 8080 - initialDelaySeconds: 1 - periodSeconds: 3 - timeoutSeconds: 5 - env: - - name: REVISION - value: "{{ .Values.revision | default `default` }}" - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.serviceAccountName - - name: KUBECONFIG - value: /var/run/secrets/remote/config - {{- if .Values.pilot.env }} - {{- range $key, $val := .Values.pilot.env }} - - name: {{ $key }} - value: "{{ $val }}" - {{- end }} - {{- end }} -{{- if .Values.pilot.traceSampling }} - - name: PILOT_TRACE_SAMPLING - value: "{{ .Values.pilot.traceSampling }}" -{{- end }} - - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_OUTBOUND - value: "{{ .Values.pilot.enableProtocolSniffingForOutbound }}" - - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_INBOUND - value: "{{ .Values.pilot.enableProtocolSniffingForInbound }}" - - name: ISTIOD_ADDR - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Release.Namespace }}.svc:15012 - - name: PILOT_ENABLE_ANALYSIS - value: "{{ .Values.global.istiod.enableAnalysis }}" - - name: CLUSTER_ID - value: "{{ $.Values.global.multiCluster.clusterName | default `Kubernetes` }}" - resources: -{{- if .Values.pilot.resources }} -{{ toYaml .Values.pilot.resources | trim | indent 12 }} -{{- else }} -{{ toYaml .Values.global.defaultResources | trim | indent 12 }} -{{- end }} - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsUser: 1337 - runAsGroup: 1337 - runAsNonRoot: true - capabilities: - drop: - - ALL - volumeMounts: - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - mountPath: /var/run/secrets/tokens - readOnly: true - {{- end }} - - name: local-certs - mountPath: /var/run/secrets/istio-dns - - name: cacerts - mountPath: /etc/cacerts - readOnly: true - - name: istio-kubeconfig - mountPath: /var/run/secrets/remote - readOnly: true - {{- if .Values.pilot.jwksResolverExtraRootCA }} - - name: extracacerts - mountPath: /cacerts - {{- end }} - volumes: - # Technically not needed on this pod - but it helps debugging/testing SDS - # Should be removed after everything works. - - emptyDir: - medium: Memory - name: local-certs - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: {{ .Values.global.sds.token.aud }} - expirationSeconds: 43200 - path: istio-token - {{- end }} - # Optional: user-generated root - - name: cacerts - secret: - secretName: cacerts - optional: true - - name: istio-kubeconfig - secret: - secretName: istio-kubeconfig - optional: true - {{- if .Values.pilot.jwksResolverExtraRootCA }} - - name: extracacerts - configMap: - name: pilot-jwks-extra-cacerts{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- end }} ---- diff --git a/manifests/charts/istio-control/istio-discovery/templates/istiod-injector-configmap.yaml b/manifests/charts/istio-control/istio-discovery/templates/istiod-injector-configmap.yaml deleted file mode 100644 index ac4679ef6..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/istiod-injector-configmap.yaml +++ /dev/null @@ -1,71 +0,0 @@ -{{- if not .Values.global.omitSidecarInjectorConfigMap }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: istio-sidecar-injector{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - namespace: {{ .Release.Namespace }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Pilot" - release: {{ .Release.Name }} -data: -{{/* Scope the values to just top level fields used in the template, to reduce the size. */}} - values: |- -{{ pick .Values "global" "istio_cni" "sidecarInjectorWebhook" "revision" | toPrettyJson | indent 4 }} - - # To disable injection: use omitSidecarInjectorConfigMap, which disables the webhook patching - # and istiod webhook functionality. - # - # New fields should not use Values - it is a 'primary' config object, users should be able - # to fine tune it or use it with kube-inject. - config: |- - # defaultTemplates defines the default template to use for pods that do not explicitly specify a template - {{- if .Values.sidecarInjectorWebhook.defaultTemplates }} - defaultTemplates: -{{- range .Values.sidecarInjectorWebhook.defaultTemplates}} - - {{ . }} -{{- end }} - {{- else }} - defaultTemplates: [sidecar] - {{- end }} - policy: {{ .Values.global.proxy.autoInject }} - alwaysInjectSelector: -{{ toYaml .Values.sidecarInjectorWebhook.alwaysInjectSelector | trim | indent 6 }} - neverInjectSelector: -{{ toYaml .Values.sidecarInjectorWebhook.neverInjectSelector | trim | indent 6 }} - injectedAnnotations: - {{- range $key, $val := .Values.sidecarInjectorWebhook.injectedAnnotations }} - "{{ $key }}": {{ $val | quote }} - {{- end }} - {{- /* If someone ends up with this new template, but an older Istiod image, they will attempt to render this template - which will fail with "Pod injection failed: template: inject:1: function "Istio_1_9_Required_Template_And_Version_Mismatched" not defined". - This should make it obvious that their installation is broken. - */}} - template: {{ `{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}` | quote }} - templates: -{{- if not (hasKey .Values.sidecarInjectorWebhook.templates "sidecar") }} - sidecar: | -{{ .Files.Get "files/injection-template.yaml" | trim | indent 8 }} -{{- end }} -{{- if not (hasKey .Values.sidecarInjectorWebhook.templates "gateway") }} - gateway: | -{{ .Files.Get "files/gateway-injection-template.yaml" | trim | indent 8 }} -{{- end }} -{{- if not (hasKey .Values.sidecarInjectorWebhook.templates "grpc-simple") }} - grpc-simple: | -{{ .Files.Get "files/grpc-simple.yaml" | trim | indent 8 }} -{{- end }} -{{- if not (hasKey .Values.sidecarInjectorWebhook.templates "grpc-agent") }} - grpc-agent: | -{{ .Files.Get "files/grpc-agent.yaml" | trim | indent 8 }} -{{- end }} -{{- if not (hasKey .Values.sidecarInjectorWebhook.templates "dubbo") }} - dubbo: | -{{ .Files.Get "files/dubbo.yaml" | trim | indent 8 }} -{{- end }} -{{- with .Values.sidecarInjectorWebhook.templates }} -{{ toYaml . | trim | indent 6 }} -{{- end }} - -{{- end }} diff --git a/manifests/charts/istio-control/istio-discovery/templates/mutatingwebhook.yaml b/manifests/charts/istio-control/istio-discovery/templates/mutatingwebhook.yaml deleted file mode 100644 index c8f304924..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/mutatingwebhook.yaml +++ /dev/null @@ -1,149 +0,0 @@ -{{- /* Core defines the common configuration used by all webhook segments */}} -{{/* Copy just what we need to avoid expensive deepCopy */}} -{{- $whv := dict - "revision" .Values.revision - "injectionPath" .Values.istiodRemote.injectionPath - "injectionURL" .Values.istiodRemote.injectionURL - "namespace" .Release.Namespace }} -{{- define "core" }} -{{- /* Kubernetes unfortunately requires a unique name for the webhook in some newer versions, so we assign -a unique prefix to each. */}} -- name: {{.Prefix}}sidecar-injector.istio.io - clientConfig: - {{- if .injectionURL }} - url: "{{ .injectionURL }}" - {{- else }} - service: - name: istiod{{- if not (eq .revision "") }}-{{ .revision }}{{- end }} - namespace: {{ .namespace }} - path: "{{ .injectionPath }}" - port: 443 - {{- end }} - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] -{{- end }} -{{- /* Installed for each revision - not installed for cluster resources ( cluster roles, bindings, crds) */}} -{{- if not .Values.global.operatorManageWebhooks }} -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: -{{- if eq .Release.Namespace "dubbo-system"}} - name: istio-sidecar-injector{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} -{{- else }} - name: istio-sidecar-injector{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}-{{ .Release.Namespace }} -{{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Pilot" - app: sidecar-injector - release: {{ .Release.Name }} -webhooks: -{{- /* Set up the selectors. First section is for revision, rest is for "default" revision */}} - -{{- /* Case 1: namespace selector matches, and object doesn't disable */}} -{{- /* Note: if both revision and legacy selector, we give precedence to the legacy one */}} -{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "rev.namespace.") ) }} - namespaceSelector: - matchExpressions: - - key: istio.io/rev - operator: In - values: - {{- if (eq .Values.revision "") }} - - "default" - {{- else }} - - "{{ .Values.revision }}" - {{- end }} - - key: istio-injection - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" - -{{- /* Case 2: No namespace selector, but object selects our revision (and doesn't disable) */}} -{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "rev.object.") ) }} - namespaceSelector: - matchExpressions: - - key: istio.io/rev - operator: DoesNotExist - - key: istio-injection - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" - - key: istio.io/rev - operator: In - values: - {{- if (eq .Values.revision "") }} - - "default" - {{- else }} - - "{{ .Values.revision }}" - {{- end }} - - -{{- /* Webhooks for default revision */}} -{{- if (eq .Values.revision "") }} - -{{- /* Case 1: Namespace selector enabled, and object selector is not injected */}} -{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "namespace.") ) }} - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: In - values: - - enabled - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" - -{{- /* Case 2: no namespace label, but object selector is enabled (and revision label is not, which has priority) */}} -{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "object.") ) }} - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: DoesNotExist - - key: istio.io/rev - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: In - values: - - "true" - - key: istio.io/rev - operator: DoesNotExist - -{{- if .Values.sidecarInjectorWebhook.enableNamespacesByDefault }} -{{- /* Special case 3: no labels at all */}} -{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "auto.") ) }} - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: DoesNotExist - - key: istio.io/rev - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: DoesNotExist - - key: istio.io/rev - operator: DoesNotExist -{{- end }} - -{{- end }} -{{- end }} diff --git a/manifests/charts/istio-control/istio-discovery/templates/poddisruptionbudget.yaml b/manifests/charts/istio-control/istio-discovery/templates/poddisruptionbudget.yaml deleted file mode 100644 index 454737a69..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/poddisruptionbudget.yaml +++ /dev/null @@ -1,29 +0,0 @@ -{{- if .Values.global.defaultPodDisruptionBudget.enabled }} -{{- if (semverCompare ">=1.21-0" .Capabilities.KubeVersion.GitVersion) }} -apiVersion: policy/v1 -{{- else }} -apiVersion: policy/v1beta1 -{{- end }} -kind: PodDisruptionBudget -metadata: - name: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - namespace: {{ .Release.Namespace }} - labels: - app: istiod - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Pilot" - release: {{ .Release.Name }} - istio: pilot -spec: - minAvailable: 1 - selector: - matchLabels: - app: istiod - {{- if ne .Values.revision "" }} - istio.io/rev: {{ .Values.revision }} - {{- else }} - istio: pilot - {{- end }} ---- -{{- end }} diff --git a/manifests/charts/istio-control/istio-discovery/templates/reader-clusterrole.yaml b/manifests/charts/istio-control/istio-discovery/templates/reader-clusterrole.yaml deleted file mode 100644 index 793b75e66..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/reader-clusterrole.yaml +++ /dev/null @@ -1,55 +0,0 @@ -{{ $mcsAPIGroup := or .Values.pilot.env.MCS_API_GROUP "multicluster.x-k8s.io" }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istio-reader-clusterrole{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }}-{{ .Release.Namespace }} - labels: - app: istio-reader - release: {{ .Release.Name }} -rules: - - apiGroups: - - "config.istio.io" - - "security.istio.io" - - "networking.istio.io" - - "authentication.istio.io" - - "rbac.istio.io" - resources: ["*"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["endpoints", "pods", "services", "nodes", "replicationcontrollers", "namespaces", "secrets"] - verbs: ["get", "list", "watch"] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list" ] - resources: [ "workloadentries" ] - - apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "list", "watch"] - - apiGroups: ["discovery.k8s.io"] - resources: ["endpointslices"] - verbs: ["get", "list", "watch"] - - apiGroups: ["{{ $mcsAPIGroup }}"] - resources: ["serviceexports"] - verbs: ["get", "list", "watch", "create", "delete"] - - apiGroups: ["{{ $mcsAPIGroup }}"] - resources: ["serviceimports"] - verbs: ["get", "list", "watch"] - - apiGroups: ["apps"] - resources: ["replicasets"] - verbs: ["get", "list", "watch"] - - apiGroups: ["authentication.k8s.io"] - resources: ["tokenreviews"] - verbs: ["create"] - - apiGroups: ["authorization.k8s.io"] - resources: ["subjectaccessreviews"] - verbs: ["create"] -{{- if .Values.global.externalIstiod }} - - apiGroups: [""] - resources: ["configmaps"] - verbs: ["create", "get", "list", "watch", "update"] - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["mutatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update", "patch"] - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["validatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update"] -{{- end}} diff --git a/manifests/charts/istio-control/istio-discovery/templates/reader-clusterrolebinding.yaml b/manifests/charts/istio-control/istio-discovery/templates/reader-clusterrolebinding.yaml deleted file mode 100644 index 4f9925c9d..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/reader-clusterrolebinding.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istio-reader-clusterrole{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }}-{{ .Release.Namespace }} - labels: - app: istio-reader - release: {{ .Release.Name }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istio-reader-clusterrole{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }}-{{ .Release.Namespace }} -subjects: - - kind: ServiceAccount - name: istio-reader-service-account - namespace: {{ .Values.global.istioNamespace }} diff --git a/manifests/charts/istio-control/istio-discovery/templates/revision-tags.yaml b/manifests/charts/istio-control/istio-discovery/templates/revision-tags.yaml deleted file mode 100644 index 90976a9c3..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/revision-tags.yaml +++ /dev/null @@ -1,136 +0,0 @@ -# Adapted from istio-discovery/templates/mutatingwebhook.yaml -# Removed paths for legacy and default selectors since a revision tag -# is inherently created from a specific revision -{{- $whv := dict - "revision" .Values.revision - "injectionPath" .Values.istiodRemote.injectionPath - "injectionURL" .Values.istiodRemote.injectionURL - "namespace" .Release.Namespace }} -{{- define "core" }} -{{- /* Kubernetes unfortunately requires a unique name for the webhook in some newer versions, so we assign -a unique prefix to each. */}} -- name: {{.Prefix}}sidecar-injector.istio.io - clientConfig: - {{- if .injectionURL }} - url: "{{ .injectionURL }}" - {{- else }} - service: - name: istiod{{- if not (eq .revision "") }}-{{ .revision }}{{- end }} - namespace: {{ .namespace }} - path: "{{ .injectionPath }}" - port: 443 - {{- end }} - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] -{{- end }} -{{- range $tagName := $.Values.revisionTags }} -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: -{{- if eq $.Release.Namespace "dubbo-system"}} - name: istio-revision-tag-{{ $tagName }} -{{- else }} - name: istio-revision-tag-{{ $tagName }}-{{ $.Release.Namespace }} -{{- end }} - labels: - istio.io/tag: {{ $tagName }} - istio.io/rev: {{ $.Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ $.Values.ownerName | default "unknown" }} - operator.istio.io/component: "Pilot" - app: sidecar-injector - release: {{ $.Release.Name }} -webhooks: -{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "rev.namespace.") ) }} - namespaceSelector: - matchExpressions: - - key: istio.io/rev - operator: In - values: - - "{{ $tagName }}" - - key: istio-injection - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" -{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "rev.object.") ) }} - namespaceSelector: - matchExpressions: - - key: istio.io/rev - operator: DoesNotExist - - key: istio-injection - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" - - key: istio.io/rev - operator: In - values: - - "{{ $tagName }}" - -{{- /* When the tag is "default" we want to create webhooks for the default revision */}} -{{- /* These webhooks should be kept in sync with istio-discovery/templates/mutatingwebhook.yaml */}} -{{- if (eq $tagName "default") }} - -{{- /* Case 1: Namespace selector enabled, and object selector is not injected */}} -{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "namespace.") ) }} - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: In - values: - - enabled - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" - -{{- /* Case 2: no namespace label, but object selector is enabled (and revision label is not, which has priority) */}} -{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "object.") ) }} - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: DoesNotExist - - key: istio.io/rev - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: In - values: - - "true" - - key: istio.io/rev - operator: DoesNotExist - -{{- if $.Values.sidecarInjectorWebhook.enableNamespacesByDefault }} -{{- /* Special case 3: no labels at all */}} -{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "auto.") ) }} - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: DoesNotExist - - key: istio.io/rev - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: DoesNotExist - - key: istio.io/rev - operator: DoesNotExist -{{- end }} - -{{- end }} -{{- end }} diff --git a/manifests/charts/istio-control/istio-discovery/templates/role.yaml b/manifests/charts/istio-control/istio-discovery/templates/role.yaml deleted file mode 100644 index 25c4f5c3b..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/role.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: istiod{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }} - namespace: {{ .Values.global.istioNamespace }} - labels: - app: istiod - release: {{ .Release.Name }} -rules: -# permissions to verify the webhook is ready and rejecting -# invalid config. We use --server-dry-run so no config is persisted. -- apiGroups: ["networking.istio.io"] - verbs: ["create"] - resources: ["gateways"] - -# For storing CA secret -- apiGroups: [""] - resources: ["secrets"] - # TODO lock this down to istio-ca-cert if not using the DNS cert mesh config - verbs: ["create", "get", "watch", "list", "update", "delete"] diff --git a/manifests/charts/istio-control/istio-discovery/templates/rolebinding.yaml b/manifests/charts/istio-control/istio-discovery/templates/rolebinding.yaml deleted file mode 100644 index 0d700f008..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/rolebinding.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: istiod{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }} - namespace: {{ .Values.global.istioNamespace }} - labels: - app: istiod - release: {{ .Release.Name }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: istiod{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }} -subjects: - - kind: ServiceAccount - name: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - namespace: {{ .Values.global.istioNamespace }} diff --git a/manifests/charts/istio-control/istio-discovery/templates/service.yaml b/manifests/charts/istio-control/istio-discovery/templates/service.yaml deleted file mode 100644 index b5ddf5b6e..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/service.yaml +++ /dev/null @@ -1,41 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - namespace: {{ .Release.Namespace }} - {{- if .Values.pilot.serviceAnnotations }} - annotations: -{{ toYaml .Values.pilot.serviceAnnotations | indent 4 }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Pilot" - app: istiod - istio: pilot - release: {{ .Release.Name }} -spec: - ports: - - port: 15010 - name: grpc-xds # plaintext - protocol: TCP - - port: 15012 - name: https-dns # mTLS with k8s-signed cert - protocol: TCP - - port: 443 - name: https-webhook # validation and injection - targetPort: 15017 - protocol: TCP - - port: 15014 - name: http-monitoring # prometheus stats - protocol: TCP - selector: - app: istiod - {{- if ne .Values.revision "" }} - istio.io/rev: {{ .Values.revision }} - {{- else }} - # Label used by the 'default' service. For versioned deployments we match with app and version. - # This avoids default deployment picking the canary - istio: pilot - {{- end }} ---- diff --git a/manifests/charts/istio-control/istio-discovery/templates/serviceaccount.yaml b/manifests/charts/istio-control/istio-discovery/templates/serviceaccount.yaml deleted file mode 100644 index ee6cbc326..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/serviceaccount.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount - {{- if .Values.global.imagePullSecrets }} -imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} -metadata: - name: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - namespace: {{ .Values.global.istioNamespace }} - labels: - app: istiod - release: {{ .Release.Name }} ---- diff --git a/manifests/charts/istio-control/istio-discovery/templates/telemetryv2_1.11.yaml b/manifests/charts/istio-control/istio-discovery/templates/telemetryv2_1.11.yaml deleted file mode 100644 index da7acd7d7..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/telemetryv2_1.11.yaml +++ /dev/null @@ -1,606 +0,0 @@ -{{- if and .Values.telemetry.enabled .Values.telemetry.v2.enabled }} ---- -# Note: http stats filter is wasm enabled only in sidecars. -{{- if .Values.telemetry.v2.prometheus.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.11{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.outboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.outboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.inboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.inboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_inbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.gateway }} - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.gateway | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} ---- -# Note: tcp stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.11{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.inboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.inboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_inbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.outboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.outboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.gateway }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.gateway | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} ---- -{{- end }} -{{- if .Values.telemetry.v2.stackdriver.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stackdriver-filter-1.11{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: -{{- if not .Values.telemetry.v2.stackdriver.disableOutbound }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}"} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } -{{- end }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"disable_server_access_logging": {{ not .Values.telemetry.v2.stackdriver.logging }}, "access_logging": "{{ .Values.telemetry.v2.stackdriver.inboundAccessLogging }}", "disable_host_header_fallback": true} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - 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 - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", "disable_host_header_fallback": true} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stackdriver-filter-1.11{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - {{- if not .Values.telemetry.v2.stackdriver.disableOutbound }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}"} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"disable_server_access_logging": {{ not .Values.telemetry.v2.stackdriver.logging }}, "access_logging": "{{ .Values.telemetry.v2.stackdriver.inboundAccessLogging }}"} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_inbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}"} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } ---- -{{- if .Values.telemetry.v2.accessLogPolicy.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stackdriver-sampling-accesslog-filter-1.11{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "istio.stackdriver" - patch: - operation: INSERT_BEFORE - value: - name: istio.access_log - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "log_window_duration": "{{ .Values.telemetry.v2.accessLogPolicy.logWindowDuration }}" - } - vm_config: - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: "envoy.wasm.access_log_policy" } ---- -{{- end }} -{{- end }} -{{- end }} diff --git a/manifests/charts/istio-control/istio-discovery/templates/telemetryv2_1.12.yaml b/manifests/charts/istio-control/istio-discovery/templates/telemetryv2_1.12.yaml deleted file mode 100644 index c2fe6eeb0..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/telemetryv2_1.12.yaml +++ /dev/null @@ -1,606 +0,0 @@ -{{- if and .Values.telemetry.enabled .Values.telemetry.v2.enabled }} ---- -# Note: http stats filter is wasm enabled only in sidecars. -{{- if .Values.telemetry.v2.prometheus.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.12{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.outboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.outboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.inboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.inboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_inbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.gateway }} - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.gateway | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} ---- -# Note: tcp stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.12{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.inboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.inboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_inbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.outboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.outboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.gateway }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.gateway | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} ---- -{{- end }} -{{- if .Values.telemetry.v2.stackdriver.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stackdriver-filter-1.12{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: -{{- if not .Values.telemetry.v2.stackdriver.disableOutbound }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}"} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } -{{- end }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"disable_server_access_logging": {{ not .Values.telemetry.v2.stackdriver.logging }}, "access_logging": "{{ .Values.telemetry.v2.stackdriver.inboundAccessLogging }}", "disable_host_header_fallback": true} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - 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 - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", "disable_host_header_fallback": true} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stackdriver-filter-1.12{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - {{- if not .Values.telemetry.v2.stackdriver.disableOutbound }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}"} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"disable_server_access_logging": {{ not .Values.telemetry.v2.stackdriver.logging }}, "access_logging": "{{ .Values.telemetry.v2.stackdriver.inboundAccessLogging }}"} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_inbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}"} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } ---- -{{- if .Values.telemetry.v2.accessLogPolicy.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stackdriver-sampling-accesslog-filter-1.12{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "istio.stackdriver" - patch: - operation: INSERT_BEFORE - value: - name: istio.access_log - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "log_window_duration": "{{ .Values.telemetry.v2.accessLogPolicy.logWindowDuration }}" - } - vm_config: - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: "envoy.wasm.access_log_policy" } ---- -{{- end }} -{{- end }} -{{- end }} diff --git a/manifests/charts/istio-control/istio-discovery/templates/telemetryv2_1.13.yaml b/manifests/charts/istio-control/istio-discovery/templates/telemetryv2_1.13.yaml deleted file mode 100644 index c4269fa04..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/telemetryv2_1.13.yaml +++ /dev/null @@ -1,628 +0,0 @@ -{{- if and .Values.telemetry.enabled .Values.telemetry.v2.enabled }} ---- -# Note: http stats filter is wasm enabled only in sidecars. -{{- if .Values.telemetry.v2.prometheus.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.13{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.outboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.outboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.inboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.inboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_inbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.gateway }} - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.gateway | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} ---- -# Note: tcp stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.13{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.inboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.inboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_inbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.outboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.outboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.gateway }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.gateway | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} ---- -{{- end }} -{{- if .Values.telemetry.v2.stackdriver.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stackdriver-filter-1.13{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: -{{- if not .Values.telemetry.v2.stackdriver.disableOutbound }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } -{{- end }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "disable_server_access_logging": {{ not .Values.telemetry.v2.stackdriver.logging }}, - "access_logging": "{{ .Values.telemetry.v2.stackdriver.inboundAccessLogging }}", - "disable_host_header_fallback": true, - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - 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 - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "disable_host_header_fallback": true, - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stackdriver-filter-1.13{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - {{- if not .Values.telemetry.v2.stackdriver.disableOutbound }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "disable_server_access_logging": {{ not .Values.telemetry.v2.stackdriver.logging }}, - "access_logging": "{{ .Values.telemetry.v2.stackdriver.inboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_inbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } ---- -{{- if .Values.telemetry.v2.accessLogPolicy.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stackdriver-sampling-accesslog-filter-1.13{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "istio.stackdriver" - patch: - operation: INSERT_BEFORE - value: - name: istio.access_log - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "log_window_duration": "{{ .Values.telemetry.v2.accessLogPolicy.logWindowDuration }}" - } - vm_config: - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: "envoy.wasm.access_log_policy" } ---- -{{- end }} -{{- end }} -{{- end }} diff --git a/manifests/charts/istio-control/istio-discovery/templates/telemetryv2_1.14.yaml b/manifests/charts/istio-control/istio-discovery/templates/telemetryv2_1.14.yaml deleted file mode 100644 index d5d4f1bfd..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/telemetryv2_1.14.yaml +++ /dev/null @@ -1,628 +0,0 @@ -{{- if and .Values.telemetry.enabled .Values.telemetry.v2.enabled }} ---- -# Note: http stats filter is wasm enabled only in sidecars. -{{- if .Values.telemetry.v2.prometheus.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.14{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.outboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.outboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.inboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.inboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_inbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.gateway }} - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.gateway | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} ---- -# Note: tcp stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.14{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.inboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.inboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_inbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.outboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.outboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.gateway }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.gateway | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} ---- -{{- end }} -{{- if .Values.telemetry.v2.stackdriver.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stackdriver-filter-1.14{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: -{{- if not .Values.telemetry.v2.stackdriver.disableOutbound }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } -{{- end }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "disable_server_access_logging": {{ not .Values.telemetry.v2.stackdriver.logging }}, - "access_logging": "{{ .Values.telemetry.v2.stackdriver.inboundAccessLogging }}", - "disable_host_header_fallback": true, - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - 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 - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "disable_host_header_fallback": true, - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stackdriver-filter-1.14{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - {{- if not .Values.telemetry.v2.stackdriver.disableOutbound }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "disable_server_access_logging": {{ not .Values.telemetry.v2.stackdriver.logging }}, - "access_logging": "{{ .Values.telemetry.v2.stackdriver.inboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_inbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } ---- -{{- if .Values.telemetry.v2.accessLogPolicy.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stackdriver-sampling-accesslog-filter-1.14{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "istio.stackdriver" - patch: - operation: INSERT_BEFORE - value: - name: istio.access_log - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "log_window_duration": "{{ .Values.telemetry.v2.accessLogPolicy.logWindowDuration }}" - } - vm_config: - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: "envoy.wasm.access_log_policy" } ---- -{{- end }} -{{- end }} -{{- end }} diff --git a/manifests/charts/istio-control/istio-discovery/templates/telemetryv2_1.15.yaml b/manifests/charts/istio-control/istio-discovery/templates/telemetryv2_1.15.yaml deleted file mode 100644 index bd9d32818..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/telemetryv2_1.15.yaml +++ /dev/null @@ -1,628 +0,0 @@ -{{- if and .Values.telemetry.enabled .Values.telemetry.v2.enabled }} ---- -# Note: http stats filter is wasm enabled only in sidecars. -{{- if .Values.telemetry.v2.prometheus.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.15{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.outboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.outboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.inboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.inboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_inbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.gateway }} - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.gateway | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} ---- -# Note: tcp stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.15{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.inboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.inboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_inbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.outboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.outboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.gateway }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.gateway | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} ---- -{{- end }} -{{- if .Values.telemetry.v2.stackdriver.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stackdriver-filter-1.15{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: -{{- if not .Values.telemetry.v2.stackdriver.disableOutbound }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } -{{- end }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "disable_server_access_logging": {{ not .Values.telemetry.v2.stackdriver.logging }}, - "access_logging": "{{ .Values.telemetry.v2.stackdriver.inboundAccessLogging }}", - "disable_host_header_fallback": true, - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - 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 - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "disable_host_header_fallback": true, - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stackdriver-filter-1.15{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - {{- if not .Values.telemetry.v2.stackdriver.disableOutbound }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "disable_server_access_logging": {{ not .Values.telemetry.v2.stackdriver.logging }}, - "access_logging": "{{ .Values.telemetry.v2.stackdriver.inboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_inbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } ---- -{{- if .Values.telemetry.v2.accessLogPolicy.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stackdriver-sampling-accesslog-filter-1.15{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "istio.stackdriver" - patch: - operation: INSERT_BEFORE - value: - name: istio.access_log - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "log_window_duration": "{{ .Values.telemetry.v2.accessLogPolicy.logWindowDuration }}" - } - vm_config: - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: "envoy.wasm.access_log_policy" } ---- -{{- end }} -{{- end }} -{{- end }} diff --git a/manifests/charts/istio-control/istio-discovery/templates/validatingwebhookconfiguration.yaml b/manifests/charts/istio-control/istio-discovery/templates/validatingwebhookconfiguration.yaml deleted file mode 100644 index 72d3adf6d..000000000 --- a/manifests/charts/istio-control/istio-discovery/templates/validatingwebhookconfiguration.yaml +++ /dev/null @@ -1,55 +0,0 @@ -{{- if .Values.global.configValidation }} -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - name: istio-validator{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}-{{ .Values.global.istioNamespace }} - labels: - app: istiod - release: {{ .Release.Name }} - istio: istiod - istio.io/rev: {{ .Values.revision | default "default" }} -webhooks: - # Webhook handling per-revision validation. Mostly here so we can determine whether webhooks - # are rejecting invalid configs on a per-revision basis. - - name: rev.validation.istio.io - clientConfig: - # Should change from base but cannot for API compat - {{- if .Values.base.validationURL }} - url: {{ .Values.base.validationURL }} - {{- else }} - service: - name: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - namespace: {{ .Values.global.istioNamespace }} - path: "/validate" - {{- end }} - rules: - - operations: - - CREATE - - UPDATE - apiGroups: - - security.istio.io - - networking.istio.io - - telemetry.istio.io - - extensions.istio.io - apiVersions: - - "*" - resources: - - "*" - # Fail open until the validation webhook is ready. The webhook controller - # will update this to `Fail` and patch in the `caBundle` when the webhook - # endpoint is ready. - failurePolicy: Ignore - sideEffects: None - admissionReviewVersions: ["v1beta1", "v1"] - objectSelector: - matchExpressions: - - key: istio.io/rev - operator: In - values: - {{- if (eq .Values.revision "") }} - - "default" - {{- else }} - - "{{ .Values.revision }}" - {{- end }} ---- -{{- end }} diff --git a/manifests/charts/istio-control/istio-discovery/values.yaml b/manifests/charts/istio-control/istio-discovery/values.yaml deleted file mode 100644 index 6df5c248d..000000000 --- a/manifests/charts/istio-control/istio-discovery/values.yaml +++ /dev/null @@ -1,534 +0,0 @@ -#.Values.pilot for discovery and mesh wide config - -## Discovery Settings -pilot: - autoscaleEnabled: true - autoscaleMin: 1 - autoscaleMax: 5 - replicaCount: 1 - rollingMaxSurge: 100% - rollingMaxUnavailable: 25% - - hub: "" - tag: "" - - # Can be a full hub/image:tag - image: dubbo-pilot - traceSampling: 1.0 - - # Resources for a small pilot install - resources: - requests: - cpu: 500m - memory: 2048Mi - - env: {} - - cpu: - targetAverageUtilization: 80 - - # if protocol sniffing is enabled for outbound - enableProtocolSniffingForOutbound: true - # if protocol sniffing is enabled for inbound - enableProtocolSniffingForInbound: true - - nodeSelector: {} - podAnnotations: {} - serviceAnnotations: {} - - # You can use jwksResolverExtraRootCA to provide a root certificate - # in PEM format. This will then be trusted by pilot when resolving - # JWKS URIs. - jwksResolverExtraRootCA: "" - - # This is used to set the source of configuration for - # the associated address in configSource, if nothing is specificed - # the default MCP is assumed. - configSource: - subscribedResources: [] - - plugins: [] - - # The following is used to limit how long a sidecar can be connected - # to a pilot. It balances out load across pilot instances at the cost of - # increasing system churn. - keepaliveMaxServerConnectionAge: 30m - - # Additional labels to apply to the deployment. - deploymentLabels: {} - - - ## Mesh config settings - - # Install the mesh config map, generated from values.yaml. - # If false, pilot wil use default values (by default) or user-supplied values. - configMap: true - - # Additional labels to apply on the pod level for monitoring and logging configuration. - podLabels: {} - - -sidecarInjectorWebhook: - # You can use the field called alwaysInjectSelector and neverInjectSelector which will always inject the sidecar or - # always skip the injection on pods that match that label selector, regardless of the global policy. - # See https://istio.io/docs/setup/kubernetes/additional-setup/sidecar-injection/#more-control-adding-exceptions - neverInjectSelector: [] - alwaysInjectSelector: [] - - # injectedAnnotations are additional annotations that will be added to the pod spec after injection - # This is primarily to support PSP annotations. For example, if you defined a PSP with the annotations: - # - # annotations: - # apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default - # apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default - # - # The PSP controller would add corresponding annotations to the pod spec for each container. However, this happens before - # the inject adds additional containers, so we must specify them explicitly here. With the above example, we could specify: - # injectedAnnotations: - # container.apparmor.security.beta.kubernetes.io/istio-init: runtime/default - # container.apparmor.security.beta.kubernetes.io/istio-proxy: runtime/default - injectedAnnotations: {} - - # This enables injection of sidecar in all namespaces, - # with the exception of namespaces with "istio-injection:disabled" annotation - # Only one environment should have this enabled. - enableNamespacesByDefault: false - - rewriteAppHTTPProbe: true - - # Templates defines a set of custom injection templates that can be used. For example, defining: - # - # templates: - # hello: | - # metadata: - # labels: - # hello: world - # - # Then starting a pod with the `inject.istio.io/templates: hello` annotation, will result in the pod - # being injected with the hello=world labels. - # This is intended for advanced configuration only; most users should use the built in template - templates: {} - - # Default templates specifies a set of default templates that are used in sidecar injection. - # By default, a template `sidecar` is always provided, which contains the template of default sidecar. - # To inject other additional templates, define it using the `templates` option, and add it to - # the default templates list. - # For example: - # - # templates: - # hello: | - # metadata: - # labels: - # hello: world - # - # defaultTemplates: ["sidecar", "hello"] - defaultTemplates: [] -istiodRemote: - # Sidecar injector mutating webhook configuration clientConfig.url value. - # For example: https://$remotePilotAddress:15017/inject - # The host should not refer to a service running in the cluster; use a service reference by specifying - # the clientConfig.service field instead. - injectionURL: "" - - # Sidecar injector mutating webhook configuration path value for the clientConfig.service field. - # Override to pass env variables, for example: /inject/cluster/remote/net/network2 - injectionPath: "/inject" -telemetry: - enabled: true - v2: - # For Null VM case now. - # This also enables metadata exchange. - enabled: true - metadataExchange: - # Indicates whether to enable WebAssembly runtime for metadata exchange filter. - wasmEnabled: false - # Indicate if prometheus stats filter is enabled or not - prometheus: - enabled: true - # Indicates whether to enable WebAssembly runtime for stats filter. - wasmEnabled: false - # overrides stats EnvoyFilter configuration. - configOverride: - gateway: {} - inboundSidecar: {} - outboundSidecar: {} - # stackdriver filter settings. - stackdriver: - enabled: false - logging: false - monitoring: false - topology: false # deprecated. setting this to true will have no effect, as this option is no longer supported. - disableOutbound: false - # configOverride parts give you the ability to override the low level configuration params passed to envoy filter. - - configOverride: {} - # e.g. - # disable_server_access_logging: false - # disable_host_header_fallback: true - # Access Log Policy Filter Settings. This enables filtering of access logs from stackdriver. - accessLogPolicy: - enabled: false - # To reduce the number of successful logs, default log window duration is - # set to 12 hours. - logWindowDuration: "43200s" -# Revision is set as 'version' label and part of the resource names when installing multiple control planes. -revision: "" - -# Revision tags are aliases to Istio control plane revisions -revisionTags: [] - -# For Helm compatibility. -ownerName: "" - -# meshConfig defines runtime configuration of components, including Istiod and istio-agent behavior -# See https://istio.io/docs/reference/config/istio.mesh.v1alpha1/ for all available options -meshConfig: - enablePrometheusMerge: true - # Config for the default ProxyConfig. - # Initially using directly the proxy metadata - can also be activated using annotations - # on the pod. This is an unsupported low-level API, pending review and decisions on - # enabling the feature. Enabling the DNS listener is safe - and allows further testing - # and gradual adoption by setting capture only on specific workloads. It also allows - # VMs to use other DNS options, like dnsmasq or unbound. - - # The namespace to treat as the administrative root namespace for Istio configuration. - # When processing a leaf namespace Istio will search for declarations in that namespace first - # and if none are found it will search in the root namespace. Any matching declaration found in the root namespace - # is processed as if it were declared in the leaf namespace. - - rootNamespace: - - # The trust domain corresponds to the trust root of a system - # Refer to https://github.com/spiffe/spiffe/blob/master/standards/SPIFFE-ID.md#21-trust-domain - trustDomain: "cluster.local" - - # TODO: the intent is to eventually have this enabled by default when security is used. - # It is not clear if user should normally need to configure - the metadata is typically - # used as an escape and to control testing and rollout, but it is not intended as a long-term - # stable API. - - # What we may configure in mesh config is the ".global" - and use of other suffixes. - # No hurry to do this in 1.6, we're trying to prove the code. - -global: - # Used to locate istiod. - istioNamespace: dubbo-system - # enable pod disruption budget for the control plane, which is used to - # ensure Istio control plane components are gradually upgraded or recovered. - defaultPodDisruptionBudget: - enabled: true - # The values aren't mutable due to a current PodDisruptionBudget limitation - # minAvailable: 1 - - # A minimal set of requested resources to applied to all deployments so that - # Horizontal Pod Autoscaler will be able to function (if set). - # Each component can overwrite these default values by adding its own resources - # block in the relevant section below and setting the desired resources values. - defaultResources: - requests: - cpu: 10m - # memory: 128Mi - # limits: - # cpu: 100m - # memory: 128Mi - - # Default hub for Istio images. - # Releases are published to docker hub under 'istio' project. - # Dev builds from prow are on gcr.io - hub: apache - # Default tag for Istio images. - tag: latest - - # Specify image pull policy if default behavior isn't desired. - # Default behavior: latest images will be Always else IfNotPresent. - imagePullPolicy: "" - - # ImagePullSecrets for all ServiceAccount, list of secrets in the same namespace - # to use for pulling any images in pods that reference this ServiceAccount. - # For components that don't use ServiceAccounts (i.e. grafana, servicegraph, tracing) - # ImagePullSecrets will be added to the corresponding Deployment(StatefulSet) objects. - # Must be set for any cluster configured with private docker registry. - imagePullSecrets: [] - # - private-registry-key - - # Enabled by default in master for maximising testing. - istiod: - enableAnalysis: false - - # To output all istio components logs in json format by adding --log_as_json argument to each container argument - logAsJson: false - - # Comma-separated minimum per-scope logging level of messages to output, in the form of :,: - # The control plane has different scopes depending on component, but can configure default log level across all components - # If empty, default scope and level will be used as configured in code - logging: - level: "default:info" - - omitSidecarInjectorConfigMap: false - - # Whether to restrict the applications namespace the controller manages; - # If not set, controller watches all namespaces - oneNamespace: false - - # Configure whether Operator manages webhook configurations. The current behavior - # of Istiod is to manage its own webhook configurations. - # When this option is set as true, Istio Operator, instead of webhooks, manages the - # webhook configurations. When this option is set as false, webhooks manage their - # own webhook configurations. - operatorManageWebhooks: false - - # Custom DNS config for the pod to resolve names of services in other - # clusters. Use this to add additional search domains, and other settings. - # see - # https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#dns-config - # This does not apply to gateway pods as they typically need a different - # set of DNS settings than the normal application pods (e.g., in - # multicluster scenarios). - # NOTE: If using templates, follow the pattern in the commented example below. - #podDNSSearchNamespaces: - #- global - #- "{{ valueOrDefault .DeploymentMeta.Namespace \"default\" }}.global" - - # Kubernetes >=v1.11.0 will create two PriorityClass, including system-cluster-critical and - # system-node-critical, it is better to configure this in order to make sure your Istio pods - # will not be killed because of low priority class. - # Refer to https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass - # for more detail. - priorityClassName: "" - - proxy: - image: dubbo-agent - - # This controls the 'policy' in the sidecar injector. - autoInject: enabled - - # CAUTION: It is important to ensure that all Istio helm charts specify the same clusterDomain value - # cluster domain. Default value is "cluster.local". - clusterDomain: "cluster.local" - - # Per Component log level for proxy, applies to gateways and sidecars. If a component level is - # not set, then the global "logLevel" will be used. - componentLogLevel: "misc:error" - - # If set, newly injected sidecars will have core dumps enabled. - enableCoreDump: false - - # istio ingress capture allowlist - # examples: - # Redirect only selected ports: --includeInboundPorts="80,8080" - excludeInboundPorts: "" - includeInboundPorts: "*" - - - # istio egress capture allowlist - # https://istio.io/docs/tasks/traffic-management/egress.html#calling-external-services-directly - # example: includeIPRanges: "172.30.0.0/16,172.20.0.0/16" - # would only capture egress traffic on those two IP Ranges, all other outbound traffic would - # be allowed by the sidecar - includeIPRanges: "*" - excludeIPRanges: "" - includeOutboundPorts: "" - excludeOutboundPorts: "" - - # Log level for proxy, applies to gateways and sidecars. - # Expected values are: trace|debug|info|warning|error|critical|off - logLevel: warning - - #If set to true, istio-proxy container will have privileged securityContext - privileged: false - - # The number of successive failed probes before indicating readiness failure. - readinessFailureThreshold: 30 - - # The initial delay for readiness probes in seconds. - readinessInitialDelaySeconds: 1 - - # The period between readiness probes. - readinessPeriodSeconds: 2 - - # Resources for the sidecar. - resources: - requests: - cpu: 100m - memory: 128Mi - limits: - cpu: 2000m - memory: 1024Mi - - # Default port for Pilot agent health checks. A value of 0 will disable health checking. - statusPort: 15020 - - # Specify which tracer to use. One of: zipkin, lightstep, datadog, stackdriver. - # If using stackdriver tracer outside GCP, set env GOOGLE_APPLICATION_CREDENTIALS to the GCP credential file. - tracer: "zipkin" - - # Controls if sidecar is injected at the front of the container list and blocks the start of the other containers until the proxy is ready - holdApplicationUntilProxyStarts: false - - proxy_init: - # Base name for the proxy_init container, used to configure iptables. - image: dubbo-agent - resources: - limits: - cpu: 2000m - memory: 1024Mi - requests: - cpu: 10m - memory: 10Mi - - # configure remote pilot and istiod service and endpoint - remotePilotAddress: "" - - ############################################################################################## - # The following values are found in other charts. To effectively modify these values, make # - # make sure they are consistent across your Istio helm charts # - ############################################################################################## - - # The customized CA address to retrieve certificates for the pods in the cluster. - # CSR clients such as the Istio Agent and ingress gateways can use this to specify the CA endpoint. - # If not set explicitly, default to the Istio discovery address. - caAddress: "" - - # Configure a remote cluster data plane controlled by an external istiod. - # When set to true, istiod is not deployed locally and only a subset of the other - # discovery charts are enabled. - externalIstiod: false - - # Configure a remote cluster as the config cluster for an external istiod. - configCluster: false - - # Configure the policy for validating JWT. - # Currently, two options are supported: "third-party-jwt" and "first-party-jwt". - jwtPolicy: "third-party-jwt" - - # Mesh ID means Mesh Identifier. It should be unique within the scope where - # meshes will interact with each other, but it is not required to be - # globally/universally unique. For example, if any of the following are true, - # then two meshes must have different Mesh IDs: - # - Meshes will have their telemetry aggregated in one place - # - Meshes will be federated together - # - Policy will be written referencing one mesh from the other - # - # If an administrator expects that any of these conditions may become true in - # the future, they should ensure their meshes have different Mesh IDs - # assigned. - # - # Within a multicluster mesh, each cluster must be (manually or auto) - # configured to have the same Mesh ID value. If an existing cluster 'joins' a - # multicluster mesh, it will need to be migrated to the new mesh ID. Details - # of migration TBD, and it may be a disruptive operation to change the Mesh - # ID post-install. - # - # If the mesh admin does not specify a value, Istio will use the value of the - # mesh's Trust Domain. The best practice is to select a proper Trust Domain - # value. - meshID: "" - - # Configure the mesh networks to be used by the Split Horizon EDS. - # - # The following example defines two networks with different endpoints association methods. - # For `network1` all endpoints that their IP belongs to the provided CIDR range will be - # mapped to network1. The gateway for this network example is specified by its public IP - # address and port. - # The second network, `network2`, in this example is defined differently with all endpoints - # retrieved through the specified Multi-Cluster registry being mapped to network2. The - # gateway is also defined differently with the name of the gateway service on the remote - # cluster. The public IP for the gateway will be determined from that remote service (only - # LoadBalancer gateway service type is currently supported, for a NodePort type gateway service, - # it still need to be configured manually). - # - # meshNetworks: - # network1: - # endpoints: - # - fromCidr: "192.168.0.1/24" - # gateways: - # - address: 1.1.1.1 - # port: 80 - # network2: - # endpoints: - # - fromRegistry: reg1 - # gateways: - # - registryServiceName: istio-ingressgateway.dubbo-system.svc.cluster.local - # port: 443 - # - meshNetworks: {} - - # Use the user-specified, secret volume mounted key and certs for Pilot and workloads. - mountMtlsCerts: false - - multiCluster: - # Set to true to connect two kubernetes clusters via their respective - # ingressgateway services when pods in each cluster cannot directly - # talk to one another. All clusters should be using Istio mTLS and must - # have a shared root CA for this model to work. - enabled: false - # Should be set to the name of the cluster this installation will run in. This is required for sidecar injection - # to properly label proxies - clusterName: "" - - # Network defines the network this cluster belong to. This name - # corresponds to the networks in the map of mesh networks. - network: "" - - # Configure the certificate provider for control plane communication. - # Currently, two providers are supported: "kubernetes" and "istiod". - # As some platforms may not have kubernetes signing APIs, - # Istiod is the default - pilotCertProvider: istiod - - sds: - # The JWT token for SDS and the aud field of such JWT. See RFC 7519, section 4.1.3. - # When a CSR is sent from Istio Agent to the CA (e.g. Istiod), this aud is to make sure the - # JWT is intended for the CA. - token: - aud: istio-ca - - sts: - # The service port used by Security Token Service (STS) server to handle token exchange requests. - # Setting this port to a non-zero value enables STS server. - servicePort: 0 - - # Configuration for each of the supported tracers - tracer: - # Configuration for envoy to send trace data to LightStep. - # Disabled by default. - # address: the : of the satellite pool - # accessToken: required for sending data to the pool - # - datadog: - # Host:Port for submitting traces to the Datadog agent. - address: "$(HOST_IP):8126" - lightstep: - address: "" # example: lightstep-satellite:443 - accessToken: "" # example: abcdefg1234567 - stackdriver: - # enables trace output to stdout. - debug: false - # The global default max number of message events per span. - maxNumberOfMessageEvents: 200 - # The global default max number of annotation events per span. - maxNumberOfAnnotations: 200 - # The global default max number of attributes per span. - maxNumberOfAttributes: 200 - zipkin: - # Host:Port for reporting trace data in zipkin format. If not specified, will default to - # zipkin service (port 9411) in the same namespace as the other istio components. - address: "" - - # Use the Mesh Control Protocol (MCP) for configuring Istiod. Requires an MCP source. - useMCP: false - - # The name of the CA for workload certificates. - # For example, when caName=GkeWorkloadCertificate, GKE workload certificates - # will be used as the certificates for workloads. - # The default value is "" and when caName="", the CA will be configured by other - # mechanisms (e.g., environmental variable CA_PROVIDER). - caName: "" - - # whether to use autoscaling/v2 template for HPA settings - # for internal usage only, not to be configured by users. - autoscalingv2API: true - -base: - # For istioctl usage to disable istio config crds in base - enableIstioConfigCRDs: true diff --git a/manifests/charts/istio-operator/Chart.yaml b/manifests/charts/istio-operator/Chart.yaml deleted file mode 100644 index cb1613b6d..000000000 --- a/manifests/charts/istio-operator/Chart.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -name: istio-operator -# This version is never actually shipped. istio/release-builder will replace it at build-time -# with the appropriate version -version: 1.0.0 -appVersion: 1.0.0 -tillerVersion: ">=2.7.2" -description: Helm chart for deploying Istio operator -keywords: - - istio - - operator -sources: - - https://github.com/istio/istio/tree/master/operator -engine: gotpl -icon: https://istio.io/latest/favicons/android-192x192.png diff --git a/manifests/charts/istio-operator/crds/crd-operator.yaml b/manifests/charts/istio-operator/crds/crd-operator.yaml deleted file mode 100644 index 93ac1de07..000000000 --- a/manifests/charts/istio-operator/crds/crd-operator.yaml +++ /dev/null @@ -1,48 +0,0 @@ -# SYNC WITH manifests/charts/base/files -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: istiooperators.install.istio.io - labels: - release: istio -spec: - conversion: - strategy: None - group: install.istio.io - names: - kind: IstioOperator - listKind: IstioOperatorList - plural: istiooperators - singular: istiooperator - shortNames: - - iop - - io - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Istio control plane revision - jsonPath: .spec.revision - name: Revision - type: string - - description: IOP current state - jsonPath: .status.status - name: Status - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - subresources: - status: {} - schema: - openAPIV3Schema: - type: object - x-kubernetes-preserve-unknown-fields: true - served: true - storage: true ---- diff --git a/manifests/charts/istio-operator/files/gen-operator.yaml b/manifests/charts/istio-operator/files/gen-operator.yaml deleted file mode 100644 index 24b9156ad..000000000 --- a/manifests/charts/istio-operator/files/gen-operator.yaml +++ /dev/null @@ -1,213 +0,0 @@ ---- -# Source: istio-operator/templates/service_account.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - namespace: istio-operator - name: istio-operator ---- -# Source: istio-operator/templates/clusterrole.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - creationTimestamp: null - name: istio-operator -rules: -# istio groups -- apiGroups: - - authentication.istio.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - config.istio.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - install.istio.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - networking.istio.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - security.istio.io - resources: - - '*' - verbs: - - '*' -# k8s groups -- apiGroups: - - admissionregistration.k8s.io - resources: - - mutatingwebhookconfigurations - - validatingwebhookconfigurations - verbs: - - '*' -- apiGroups: - - apiextensions.k8s.io - resources: - - customresourcedefinitions.apiextensions.k8s.io - - customresourcedefinitions - verbs: - - '*' -- apiGroups: - - apps - - extensions - resources: - - daemonsets - - deployments - - deployments/finalizers - - replicasets - verbs: - - '*' -- apiGroups: - - autoscaling - resources: - - horizontalpodautoscalers - verbs: - - '*' -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - get - - create - - update -- apiGroups: - - policy - resources: - - poddisruptionbudgets - verbs: - - '*' -- apiGroups: - - rbac.authorization.k8s.io - resources: - - clusterrolebindings - - clusterroles - - roles - - rolebindings - verbs: - - '*' -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - create - - update -- apiGroups: - - "" - resources: - - configmaps - - endpoints - - events - - namespaces - - pods - - pods/proxy - - pods/portforward - - persistentvolumeclaims - - secrets - - services - - serviceaccounts - verbs: - - '*' ---- -# Source: istio-operator/templates/clusterrole_binding.yaml -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: istio-operator -subjects: -- kind: ServiceAccount - name: istio-operator - namespace: istio-operator -roleRef: - kind: ClusterRole - name: istio-operator - apiGroup: rbac.authorization.k8s.io ---- -# Source: istio-operator/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - namespace: istio-operator - labels: - name: istio-operator - name: istio-operator -spec: - ports: - - name: http-metrics - port: 8383 - targetPort: 8383 - protocol: TCP - selector: - name: istio-operator ---- -# Source: istio-operator/templates/deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - namespace: istio-operator - name: istio-operator -spec: - replicas: 1 - revisionHistoryLimit: 10 - selector: - matchLabels: - name: istio-operator - template: - metadata: - labels: - name: istio-operator - spec: - serviceAccountName: istio-operator - containers: - - name: istio-operator - image: gcr.io/istio-testing/dubbo-operator:v0.5.0-rc01 - command: - - operator - - server - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - imagePullPolicy: IfNotPresent - resources: - limits: - cpu: 200m - memory: 256Mi - requests: - cpu: 50m - memory: 128Mi - env: - - name: WATCH_NAMESPACE - value: "dubbo-system" - - name: LEADER_ELECTION_NAMESPACE - value: "istio-operator" - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: OPERATOR_NAME - value: "istio-operator" - - name: WAIT_FOR_RESOURCES_TIMEOUT - value: "300s" - - name: REVISION - value: "" diff --git a/manifests/charts/istio-operator/templates/clusterrole.yaml b/manifests/charts/istio-operator/templates/clusterrole.yaml deleted file mode 100644 index 56dec904e..000000000 --- a/manifests/charts/istio-operator/templates/clusterrole.yaml +++ /dev/null @@ -1,116 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - creationTimestamp: null - name: istio-operator{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} -rules: -# istio groups -- apiGroups: - - authentication.istio.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - config.istio.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - install.istio.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - networking.istio.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - security.istio.io - resources: - - '*' - verbs: - - '*' -# k8s groups -- apiGroups: - - admissionregistration.k8s.io - resources: - - mutatingwebhookconfigurations - - validatingwebhookconfigurations - verbs: - - '*' -- apiGroups: - - apiextensions.k8s.io - resources: - - customresourcedefinitions.apiextensions.k8s.io - - customresourcedefinitions - verbs: - - '*' -- apiGroups: - - apps - - extensions - resources: - - daemonsets - - deployments - - deployments/finalizers - - replicasets - verbs: - - '*' -- apiGroups: - - autoscaling - resources: - - horizontalpodautoscalers - verbs: - - '*' -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - get - - create - - update -- apiGroups: - - policy - resources: - - poddisruptionbudgets - verbs: - - '*' -- apiGroups: - - rbac.authorization.k8s.io - resources: - - clusterrolebindings - - clusterroles - - roles - - rolebindings - verbs: - - '*' -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - create - - update -- apiGroups: - - "" - resources: - - configmaps - - endpoints - - events - - namespaces - - pods - - pods/proxy - - pods/portforward - - persistentvolumeclaims - - secrets - - services - - serviceaccounts - verbs: - - '*' ---- diff --git a/manifests/charts/istio-operator/templates/clusterrole_binding.yaml b/manifests/charts/istio-operator/templates/clusterrole_binding.yaml deleted file mode 100644 index a3df073ab..000000000 --- a/manifests/charts/istio-operator/templates/clusterrole_binding.yaml +++ /dev/null @@ -1,13 +0,0 @@ -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: istio-operator{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} -subjects: -- kind: ServiceAccount - name: istio-operator{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - namespace: {{.Release.Namespace}} -roleRef: - kind: ClusterRole - name: istio-operator{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - apiGroup: rbac.authorization.k8s.io ---- diff --git a/manifests/charts/istio-operator/templates/crds.yaml b/manifests/charts/istio-operator/templates/crds.yaml deleted file mode 100644 index a37036508..000000000 --- a/manifests/charts/istio-operator/templates/crds.yaml +++ /dev/null @@ -1,6 +0,0 @@ -{{- if .Values.enableCRDTemplates -}} -{{- range $path, $bytes := .Files.Glob "crds/*.yaml" -}} ---- -{{ $.Files.Get $path }} -{{- end -}} -{{- end -}} diff --git a/manifests/charts/istio-operator/templates/deployment.yaml b/manifests/charts/istio-operator/templates/deployment.yaml deleted file mode 100644 index 223815940..000000000 --- a/manifests/charts/istio-operator/templates/deployment.yaml +++ /dev/null @@ -1,71 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - namespace: {{.Release.Namespace}} - name: istio-operator{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} -spec: - replicas: 1 - revisionHistoryLimit: {{ .Values.deploymentHistory }} - selector: - matchLabels: - name: istio-operator - template: - metadata: - labels: - name: istio-operator - {{- range $key, $val := .Values.podLabels }} - {{ $key }}: "{{ $val }}" - {{- end }} - {{- if .Values.podAnnotations }} - annotations: -{{ toYaml .Values.podAnnotations | indent 8 }} - {{- end }} - spec: - serviceAccountName: istio-operator{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - containers: - - name: istio-operator - image: {{.Values.hub}}/dubbo-operator:{{.Values.tag}} - command: - - operator - - server - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - imagePullPolicy: IfNotPresent - resources: -{{ toYaml .Values.operator.resources | trim | indent 12 }} - env: - - name: WATCH_NAMESPACE - value: {{.Values.watchedNamespaces | quote}} - - name: LEADER_ELECTION_NAMESPACE - value: {{.Release.Namespace | quote}} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: OPERATOR_NAME - value: {{.Release.Namespace | quote}} - - name: WAIT_FOR_RESOURCES_TIMEOUT - value: {{.Values.waitForResourcesTimeout | quote}} - - name: REVISION - value: {{.Values.revision | quote}} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} ---- diff --git a/manifests/charts/istio-operator/templates/service.yaml b/manifests/charts/istio-operator/templates/service.yaml deleted file mode 100644 index e32e8ea0a..000000000 --- a/manifests/charts/istio-operator/templates/service.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - namespace: {{.Release.Namespace}} - labels: - name: istio-operator - name: istio-operator{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} -spec: - ports: - - name: http-metrics - port: 8383 - targetPort: 8383 - protocol: TCP - selector: - name: istio-operator ---- diff --git a/manifests/charts/istio-operator/templates/service_account.yaml b/manifests/charts/istio-operator/templates/service_account.yaml deleted file mode 100644 index fe9d4cf2b..000000000 --- a/manifests/charts/istio-operator/templates/service_account.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - namespace: {{.Release.Namespace}} - name: istio-operator{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} -{{- if .Values.imagePullSecrets }} -imagePullSecrets: -{{- range .Values.imagePullSecrets }} -- name: {{ . }} -{{- end }} -{{- end }} ---- diff --git a/manifests/charts/istio-operator/values.yaml b/manifests/charts/istio-operator/values.yaml deleted file mode 100644 index 20315949d..000000000 --- a/manifests/charts/istio-operator/values.yaml +++ /dev/null @@ -1,42 +0,0 @@ -hub: apache -tag: latest - -# ImagePullSecrets for operator ServiceAccount, list of secrets in the same namespace -# used to pull operator image. Must be set for any cluster configured with private docker registry. -imagePullSecrets: [] - -# Used to replace istioNamespace to support operator watch multiple namespaces. -watchedNamespaces: dubbo-system -waitForResourcesTimeout: 300s - -# Used for helm2 to add the CRDs to templates. -enableCRDTemplates: false - -# revision for the operator resources -revision: "" - -# The number of old ReplicaSets to retain in operator deployment -deploymentHistory: 10 - -# Operator resource defaults -operator: - resources: - limits: - cpu: 200m - memory: 256Mi - requests: - cpu: 50m - memory: 128Mi - -# Node labels for pod assignment -nodeSelector: {} - -# Tolerations for pod assignment -tolerations: [] - -# Affinity for pod assignment -affinity: {} - -# Additional labels and annotations to apply on the pod level for monitoring and logging configuration. -podLabels: {} -podAnnotations: {} diff --git a/manifests/charts/istiod-remote/Chart.yaml b/manifests/charts/istiod-remote/Chart.yaml deleted file mode 100644 index e4669c05c..000000000 --- a/manifests/charts/istiod-remote/Chart.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -name: istiod-remote -# This version is never actually shipped. istio/release-builder will replace it at build-time -# with the appropriate version -version: 1.0.0 -appVersion: 1.0.0 -tillerVersion: ">=2.7.2" -description: Helm chart for a remote cluster using an external istio control plane -keywords: - - istio - - external-istiod -sources: - - http://github.com/istio/istio -engine: gotpl -icon: https://istio.io/latest/favicons/android-192x192.png diff --git a/manifests/charts/istiod-remote/NOTES.txt b/manifests/charts/istiod-remote/NOTES.txt deleted file mode 100644 index 0230b6f86..000000000 --- a/manifests/charts/istiod-remote/NOTES.txt +++ /dev/null @@ -1,4 +0,0 @@ -Install for a remote cluster using an external control plane. - -The templates in this directory are copies of base and istio-discovery templates. -DO NOT EDIT! Make changes in the corresponding files in base or istio-discovery and they will be copied here by make gen. diff --git a/manifests/charts/istiod-remote/files/gateway-injection-template.yaml b/manifests/charts/istiod-remote/files/gateway-injection-template.yaml deleted file mode 100644 index 3d86c5e00..000000000 --- a/manifests/charts/istiod-remote/files/gateway-injection-template.yaml +++ /dev/null @@ -1,225 +0,0 @@ -{{- $containers := list }} -{{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} -metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } -spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} diff --git a/manifests/charts/istiod-remote/files/injection-template.yaml b/manifests/charts/istiod-remote/files/injection-template.yaml deleted file mode 100644 index 9a94e3b26..000000000 --- a/manifests/charts/istiod-remote/files/injection-template.yaml +++ /dev/null @@ -1,486 +0,0 @@ -{{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} -{{- end }} -{{- $containers := list }} -{{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} -metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} -{{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} -{{- end }} - } -spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} diff --git a/manifests/charts/istiod-remote/templates/clusterrole.yaml b/manifests/charts/istiod-remote/templates/clusterrole.yaml deleted file mode 100644 index daacd2a9c..000000000 --- a/manifests/charts/istiod-remote/templates/clusterrole.yaml +++ /dev/null @@ -1,149 +0,0 @@ -{{- if .Values.global.configCluster }} -{{ $mcsAPIGroup := or .Values.pilot.env.MCS_API_GROUP "multicluster.x-k8s.io" }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istiod-clusterrole{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }}-{{ .Release.Namespace }} - labels: - app: istiod - release: {{ .Release.Name }} -rules: - # sidecar injection controller - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["mutatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update", "patch"] - - # configuration validation webhook controller - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["validatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update"] - - # istio configuration - # removing CRD permissions can break older versions of Istio running alongside this control plane (https://github.com/istio/istio/issues/29382) - # please proceed with caution - - apiGroups: ["config.istio.io", "security.istio.io", "networking.istio.io", "authentication.istio.io", "rbac.istio.io", "telemetry.istio.io", "extensions.istio.io"] - verbs: ["get", "watch", "list"] - resources: ["*"] -{{- if .Values.global.istiod.enableAnalysis }} - - apiGroups: ["config.istio.io", "security.istio.io", "networking.istio.io", "authentication.istio.io", "rbac.istio.io", "telemetry.istio.io", "extensions.istio.io"] - verbs: ["update"] - # TODO: should be on just */status but wildcard is not supported - resources: ["*"] -{{- end }} - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "workloadentries" ] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "workloadentries/status" ] - - # auto-detect installed CRD definitions - - apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "list", "watch"] - - # discovery and routing - - apiGroups: [""] - resources: ["pods", "nodes", "services", "namespaces", "endpoints"] - verbs: ["get", "list", "watch"] - - apiGroups: ["discovery.k8s.io"] - resources: ["endpointslices"] - verbs: ["get", "list", "watch"] - - # ingress controller -{{- if .Values.global.istiod.enableAnalysis }} - - apiGroups: ["extensions", "networking.k8s.io"] - resources: ["ingresses"] - verbs: ["get", "list", "watch"] - - apiGroups: ["extensions", "networking.k8s.io"] - resources: ["ingresses/status"] - verbs: ["*"] -{{- end}} - - apiGroups: ["networking.k8s.io"] - resources: ["ingresses", "ingressclasses"] - verbs: ["get", "list", "watch"] - - apiGroups: ["networking.k8s.io"] - resources: ["ingresses/status"] - verbs: ["*"] - - # required for CA's namespace controller - - apiGroups: [""] - resources: ["configmaps"] - verbs: ["create", "get", "list", "watch", "update"] - - # Istiod and bootstrap. - - apiGroups: ["certificates.k8s.io"] - resources: - - "certificatesigningrequests" - - "certificatesigningrequests/approval" - - "certificatesigningrequests/status" - verbs: ["update", "create", "get", "delete", "watch"] - - apiGroups: ["certificates.k8s.io"] - resources: - - "signers" - resourceNames: - - "kubernetes.io/legacy-unknown" - verbs: ["approve"] - - # Used by Istiod to verify the JWT tokens - - apiGroups: ["authentication.k8s.io"] - resources: ["tokenreviews"] - verbs: ["create"] - - # Used by Istiod to verify gateway SDS - - apiGroups: ["authorization.k8s.io"] - resources: ["subjectaccessreviews"] - verbs: ["create"] - - # Use for Kubernetes Service APIs - - apiGroups: ["networking.x-k8s.io", "gateway.networking.k8s.io"] - resources: ["*"] - verbs: ["get", "watch", "list"] - - apiGroups: ["networking.x-k8s.io", "gateway.networking.k8s.io"] - resources: ["*"] # TODO: should be on just */status but wildcard is not supported - verbs: ["update", "patch"] - - apiGroups: ["gateway.networking.k8s.io"] - resources: ["gatewayclasses"] - verbs: ["create", "update", "patch", "delete"] - - # Needed for multicluster secret reading, possibly ingress certs in the future - - apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "watch", "list"] - - # Used for MCS serviceexport management - - apiGroups: ["{{ $mcsAPIGroup }}"] - resources: ["serviceexports"] - verbs: [ "get", "watch", "list", "create", "delete"] - - # Used for MCS serviceimport management - - apiGroups: ["{{ $mcsAPIGroup }}"] - resources: ["serviceimports"] - verbs: ["get", "watch", "list"] - - # Used for Service Name Mapping - - apiGroups: ["extensions.istio.io"] - resources: ["servicenamemappings"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - # Used for Service Metadata - - apiGroups: ["extensions.istio.io"] - resources: ["servicemetadatas"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] ---- -{{- if not (eq (toString .Values.pilot.env.PILOT_ENABLE_GATEWAY_API_DEPLOYMENT_CONTROLLER) "false") }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istiod-gateway-controller{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }}-{{ .Release.Namespace }} - labels: - app: istiod - release: {{ .Release.Name }} -rules: - - apiGroups: ["apps"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "deployments" ] - - apiGroups: [""] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "services" ] -{{- end }} -{{- end }} diff --git a/manifests/charts/istiod-remote/templates/clusterrolebinding.yaml b/manifests/charts/istiod-remote/templates/clusterrolebinding.yaml deleted file mode 100644 index 932cdce02..000000000 --- a/manifests/charts/istiod-remote/templates/clusterrolebinding.yaml +++ /dev/null @@ -1,35 +0,0 @@ -{{- if .Values.global.configCluster }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istiod-clusterrole{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }}-{{ .Release.Namespace }} - labels: - app: istiod - release: {{ .Release.Name }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istiod-clusterrole{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }}-{{ .Release.Namespace }} -subjects: - - kind: ServiceAccount - name: istiod{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }} - namespace: {{ .Values.global.istioNamespace }} ---- -{{- if not (eq (toString .Values.pilot.env.PILOT_ENABLE_GATEWAY_API_DEPLOYMENT_CONTROLLER) "false") }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istiod-gateway-controller{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }}-{{ .Release.Namespace }} - labels: - app: istiod - release: {{ .Release.Name }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istiod-gateway-controller{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }}-{{ .Release.Namespace }} -subjects: -- kind: ServiceAccount - name: istiod{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }} - namespace: {{ .Values.global.istioNamespace }} -{{- end }} -{{- end }} diff --git a/manifests/charts/istiod-remote/templates/configmap.yaml b/manifests/charts/istiod-remote/templates/configmap.yaml deleted file mode 100644 index 8e57c8186..000000000 --- a/manifests/charts/istiod-remote/templates/configmap.yaml +++ /dev/null @@ -1,100 +0,0 @@ -{{- define "mesh" }} - # The trust domain corresponds to the trust root of a system. - # Refer to https://github.com/spiffe/spiffe/blob/master/standards/SPIFFE-ID.md#21-trust-domain - trustDomain: "cluster.local" - - # The namespace to treat as the administrative root namespace for Istio configuration. - # When processing a leaf namespace Istio will search for declarations in that namespace first - # and if none are found it will search in the root namespace. Any matching declaration found in the root namespace - # is processed as if it were declared in the leaf namespace. - rootNamespace: {{ .Values.meshConfig.rootNamespace | default .Values.global.istioNamespace }} - - defaultConfig: - {{- if .Values.global.meshID }} - meshId: {{ .Values.global.meshID }} - {{- end }} - tracing: - {{- if eq .Values.global.proxy.tracer "lightstep" }} - lightstep: - # Address of the LightStep Satellite pool - address: {{ .Values.global.tracer.lightstep.address }} - # Access Token used to communicate with the Satellite pool - accessToken: {{ .Values.global.tracer.lightstep.accessToken }} - {{- else if eq .Values.global.proxy.tracer "zipkin" }} - zipkin: - # Address of the Zipkin collector - address: {{ .Values.global.tracer.zipkin.address | default (print "zipkin." .Values.global.istioNamespace ":9411") }} - {{- else if eq .Values.global.proxy.tracer "datadog" }} - datadog: - # Address of the Datadog Agent - address: {{ .Values.global.tracer.datadog.address | default "$(HOST_IP):8126" }} - {{- else if eq .Values.global.proxy.tracer "stackdriver" }} - stackdriver: - # enables trace output to stdout. - {{- if $.Values.global.tracer.stackdriver.debug }} - debug: {{ $.Values.global.tracer.stackdriver.debug }} - {{- end }} - {{- if $.Values.global.tracer.stackdriver.maxNumberOfAttributes }} - # The global default max number of attributes per span. - maxNumberOfAttributes: {{ $.Values.global.tracer.stackdriver.maxNumberOfAttributes | default "200" }} - {{- end }} - {{- if $.Values.global.tracer.stackdriver.maxNumberOfAnnotations }} - # The global default max number of annotation events per span. - maxNumberOfAnnotations: {{ $.Values.global.tracer.stackdriver.maxNumberOfAnnotations | default "200" }} - {{- end }} - {{- if $.Values.global.tracer.stackdriver.maxNumberOfMessageEvents }} - # The global default max number of message events per span. - maxNumberOfMessageEvents: {{ $.Values.global.tracer.stackdriver.maxNumberOfMessageEvents | default "200" }} - {{- end }} - {{- else if eq .Values.global.proxy.tracer "openCensusAgent" }} - {{/* Fill in openCensusAgent configuration from meshConfig so it isn't overwritten below */}} -{{ toYaml $.Values.meshConfig.defaultConfig.tracing | indent 8 }} - {{- else }} - {} - {{- end }} - {{- if .Values.global.remotePilotAddress }} - {{- if .Values.pilot.enabled }} - discoveryAddress: {{ printf "istiod-remote.%s.svc" .Release.Namespace }}:15012 - {{- else }} - discoveryAddress: {{ printf "istiod.%s.svc" .Release.Namespace }}:15012 - {{- end }} - {{- else }} - discoveryAddress: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{.Release.Namespace}}.svc:15012 - {{- end }} -{{- end }} - -{{/* We take the mesh config above, defined with individual values.yaml, and merge with .Values.meshConfig */}} -{{/* The intent here is that meshConfig.foo becomes the API, rather than re-inventing the API in values.yaml */}} -{{- $originalMesh := include "mesh" . | fromYaml }} -{{- $mesh := mergeOverwrite $originalMesh .Values.meshConfig }} - -{{- if .Values.pilot.configMap }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: istio{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - namespace: {{ .Release.Namespace }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Pilot" - release: {{ .Release.Name }} -data: - - # Configuration file for the mesh networks to be used by the Split Horizon EDS. - meshNetworks: |- - {{- if .Values.global.meshNetworks }} - networks: -{{ toYaml .Values.global.meshNetworks | trim | indent 6 }} - {{- else }} - networks: {} - {{- end }} - - mesh: |- -{{- if .Values.meshConfig }} -{{ $mesh | toYaml | indent 4 }} -{{- else }} -{{- include "mesh" . }} -{{- end }} ---- -{{- end }} diff --git a/manifests/charts/istiod-remote/templates/crd-all.gen.yaml b/manifests/charts/istiod-remote/templates/crd-all.gen.yaml deleted file mode 100644 index 6cd20ba3f..000000000 --- a/manifests/charts/istiod-remote/templates/crd-all.gen.yaml +++ /dev/null @@ -1,6573 +0,0 @@ -{{- if .Values.global.configCluster }} -# DO NOT EDIT - Generated by Cue OpenAPI generator based on Istio APIs. -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: servicemetadatas.extensions.istio.io -spec: - group: extensions.istio.io - names: - categories: - - istio-io - - extensions-istio-io - kind: ServiceMetadata - listKind: ServiceMetadataList - plural: servicemetadatas - shortNames: - - sm - singular: servicemetadata - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'it''s the spec of Service Metadata See more details at:' - properties: - applicationName: - type: string - metadataInfo: - type: string - revision: - type: string - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: servicenamemappings.extensions.istio.io -spec: - group: extensions.istio.io - names: - categories: - - istio-io - - extensions-istio-io - kind: ServiceNameMapping - listKind: ServiceNameMappingList - plural: servicenamemappings - shortNames: - - snp - singular: servicenamemapping - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'it''s the spec of Service Name Mapping, which is used to - map the interface name See more details at:' - properties: - applicationNames: - items: - type: string - type: array - interfaceName: - description: InterfaceName. - type: string - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: wasmplugins.extensions.istio.io -spec: - group: extensions.istio.io - names: - categories: - - istio-io - - extensions-istio-io - kind: WasmPlugin - listKind: WasmPluginList - plural: wasmplugins - singular: wasmplugin - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Extend the functionality provided by the Istio proxy through - WebAssembly filters. See more details at: https://istio.io/docs/reference/config/proxy_extensions/wasm-plugin.html' - properties: - imagePullPolicy: - description: The pull behaviour to be applied when fetching an OCI - image. - enum: - - UNSPECIFIED_POLICY - - IfNotPresent - - Always - type: string - imagePullSecret: - description: Credentials to use for OCI image pulling. - type: string - phase: - description: Determines where in the filter chain this `WasmPlugin` - is to be injected. - enum: - - UNSPECIFIED_PHASE - - AUTHN - - AUTHZ - - STATS - type: string - pluginConfig: - description: The configuration that will be passed on to the plugin. - type: object - x-kubernetes-preserve-unknown-fields: true - pluginName: - type: string - priority: - description: Determines ordering of `WasmPlugins` in the same `phase`. - nullable: true - type: integer - selector: - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - sha256: - description: SHA256 checksum that will be used to verify Wasm module - or OCI container. - type: string - url: - description: URL of a Wasm module or OCI container. - type: string - verificationKey: - type: string - vmConfig: - description: Configuration for a Wasm VM. - properties: - env: - description: Specifies environment variables to be injected to - this VM. - items: - properties: - name: - type: string - value: - description: Value for the environment variable. - type: string - valueFrom: - enum: - - INLINE - - HOST - type: string - type: object - type: array - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: destinationrules.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: DestinationRule - listKind: DestinationRuleList - plural: destinationrules - shortNames: - - dr - singular: destinationrule - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The name of a service from the service registry - jsonPath: .spec.host - name: Host - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting load balancing, outlier detection, - etc. See more details at: https://istio.io/docs/reference/config/networking/destination-rule.html' - properties: - exportTo: - description: A list of namespaces to which this destination rule is - exported. - items: - type: string - type: array - host: - description: The name of a service from the service registry. - type: string - subsets: - items: - properties: - labels: - additionalProperties: - type: string - type: object - name: - description: Name of the subset. - type: string - trafficPolicy: - description: Traffic policies that apply to this subset. - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should - be upgraded to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests - to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will - be preserved while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the - socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query - parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to - traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this - is DestinationRule-level and will override mesh - wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list - of labels used to sort endpoints to do priority - based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local - origin failures from external errors. - type: boolean - type: object - portLevelSettings: - description: Traffic policies specific to individual ports. - items: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection - should be upgraded to http2 for the associated - destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP - requests to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to - a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream - connection pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per - connection to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol - will be preserved while initiating connection - to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and - TCP upstream connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP - connections to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE - on the socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between - keep-alive probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer - algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP - header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP - query parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' - separated, e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities - to traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, - this is DestinationRule-level and will override - mesh wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered - list of labels used to sort endpoints to - do priority based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of - Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a - host is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep - analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish - local origin failures from external errors. - type: boolean - type: object - port: - properties: - number: - type: integer - type: object - tls: - description: TLS related settings for connections - to the upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server - during TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - type: object - type: array - tls: - description: TLS related settings for connections to the - upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during - TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - tunnel: - properties: - protocol: - description: Specifies which protocol to use for tunneling - the downstream connection. - type: string - targetHost: - description: Specifies a host to which the downstream - connection is tunneled. - type: string - targetPort: - description: Specifies a port to which the downstream - connection is tunneled. - type: integer - type: object - type: object - type: object - type: array - trafficPolicy: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should be upgraded - to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests to - a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will be preserved - while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the socket - to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, failover - or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to traffic - distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this is DestinationRule-level - and will override mesh wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, failover - or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list of labels - used to sort endpoints to do priority based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local origin - failures from external errors. - type: boolean - type: object - portLevelSettings: - description: Traffic policies specific to individual ports. - items: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should - be upgraded to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests - to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will - be preserved while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the - socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query - parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to - traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this - is DestinationRule-level and will override mesh - wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list - of labels used to sort endpoints to do priority - based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local - origin failures from external errors. - type: boolean - type: object - port: - properties: - number: - type: integer - type: object - tls: - description: TLS related settings for connections to the - upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during - TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - type: object - type: array - tls: - description: TLS related settings for connections to the upstream - service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during TLS - handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - tunnel: - properties: - protocol: - description: Specifies which protocol to use for tunneling - the downstream connection. - type: string - targetHost: - description: Specifies a host to which the downstream connection - is tunneled. - type: string - targetPort: - description: Specifies a port to which the downstream connection - is tunneled. - type: integer - type: object - type: object - workloadSelector: - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - description: The name of a service from the service registry - jsonPath: .spec.host - name: Host - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting load balancing, outlier detection, - etc. See more details at: https://istio.io/docs/reference/config/networking/destination-rule.html' - properties: - exportTo: - description: A list of namespaces to which this destination rule is - exported. - items: - type: string - type: array - host: - description: The name of a service from the service registry. - type: string - subsets: - items: - properties: - labels: - additionalProperties: - type: string - type: object - name: - description: Name of the subset. - type: string - trafficPolicy: - description: Traffic policies that apply to this subset. - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should - be upgraded to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests - to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will - be preserved while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the - socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query - parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to - traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this - is DestinationRule-level and will override mesh - wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list - of labels used to sort endpoints to do priority - based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local - origin failures from external errors. - type: boolean - type: object - portLevelSettings: - description: Traffic policies specific to individual ports. - items: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection - should be upgraded to http2 for the associated - destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP - requests to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to - a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream - connection pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per - connection to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol - will be preserved while initiating connection - to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and - TCP upstream connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP - connections to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE - on the socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between - keep-alive probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer - algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP - header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP - query parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' - separated, e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities - to traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, - this is DestinationRule-level and will override - mesh wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered - list of labels used to sort endpoints to - do priority based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of - Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a - host is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep - analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish - local origin failures from external errors. - type: boolean - type: object - port: - properties: - number: - type: integer - type: object - tls: - description: TLS related settings for connections - to the upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server - during TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - type: object - type: array - tls: - description: TLS related settings for connections to the - upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during - TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - tunnel: - properties: - protocol: - description: Specifies which protocol to use for tunneling - the downstream connection. - type: string - targetHost: - description: Specifies a host to which the downstream - connection is tunneled. - type: string - targetPort: - description: Specifies a port to which the downstream - connection is tunneled. - type: integer - type: object - type: object - type: object - type: array - trafficPolicy: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should be upgraded - to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests to - a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will be preserved - while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the socket - to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, failover - or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to traffic - distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this is DestinationRule-level - and will override mesh wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, failover - or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list of labels - used to sort endpoints to do priority based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local origin - failures from external errors. - type: boolean - type: object - portLevelSettings: - description: Traffic policies specific to individual ports. - items: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should - be upgraded to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests - to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will - be preserved while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the - socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query - parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to - traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this - is DestinationRule-level and will override mesh - wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list - of labels used to sort endpoints to do priority - based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local - origin failures from external errors. - type: boolean - type: object - port: - properties: - number: - type: integer - type: object - tls: - description: TLS related settings for connections to the - upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during - TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - type: object - type: array - tls: - description: TLS related settings for connections to the upstream - service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during TLS - handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - tunnel: - properties: - protocol: - description: Specifies which protocol to use for tunneling - the downstream connection. - type: string - targetHost: - description: Specifies a host to which the downstream connection - is tunneled. - type: string - targetPort: - description: Specifies a port to which the downstream connection - is tunneled. - type: integer - type: object - type: object - workloadSelector: - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: envoyfilters.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: EnvoyFilter - listKind: EnvoyFilterList - plural: envoyfilters - singular: envoyfilter - scope: Namespaced - versions: - - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Customizing Envoy configuration generated by Istio. See - more details at: https://istio.io/docs/reference/config/networking/envoy-filter.html' - properties: - configPatches: - description: One or more patches with match conditions. - items: - properties: - applyTo: - enum: - - INVALID - - LISTENER - - FILTER_CHAIN - - NETWORK_FILTER - - HTTP_FILTER - - ROUTE_CONFIGURATION - - VIRTUAL_HOST - - HTTP_ROUTE - - CLUSTER - - EXTENSION_CONFIG - - BOOTSTRAP - type: string - match: - description: Match on listener/route configuration/cluster. - oneOf: - - not: - anyOf: - - required: - - listener - - required: - - routeConfiguration - - required: - - cluster - - required: - - listener - - required: - - routeConfiguration - - required: - - cluster - properties: - cluster: - description: Match on envoy cluster attributes. - properties: - name: - description: The exact name of the cluster to match. - type: string - portNumber: - description: The service port for which this cluster - was generated. - type: integer - service: - description: The fully qualified service name for this - cluster. - type: string - subset: - description: The subset associated with the service. - type: string - type: object - context: - description: The specific config generation context to match - on. - enum: - - ANY - - SIDECAR_INBOUND - - SIDECAR_OUTBOUND - - GATEWAY - type: string - listener: - description: Match on envoy listener attributes. - properties: - filterChain: - description: Match a specific filter chain in a listener. - properties: - applicationProtocols: - description: Applies only to sidecars. - type: string - destinationPort: - description: The destination_port value used by - a filter chain's match condition. - type: integer - filter: - description: The name of a specific filter to apply - the patch to. - properties: - name: - description: The filter name to match on. - type: string - subFilter: - properties: - name: - description: The filter name to match on. - type: string - type: object - type: object - name: - description: The name assigned to the filter chain. - type: string - sni: - description: The SNI value used by a filter chain's - match condition. - type: string - transportProtocol: - description: Applies only to `SIDECAR_INBOUND` context. - type: string - type: object - name: - description: Match a specific listener by its name. - type: string - portName: - type: string - portNumber: - type: integer - type: object - proxy: - description: Match on properties associated with a proxy. - properties: - metadata: - additionalProperties: - type: string - type: object - proxyVersion: - type: string - type: object - routeConfiguration: - description: Match on envoy HTTP route configuration attributes. - properties: - gateway: - type: string - name: - description: Route configuration name to match on. - type: string - portName: - description: Applicable only for GATEWAY context. - type: string - portNumber: - type: integer - vhost: - properties: - name: - type: string - route: - description: Match a specific route within the virtual - host. - properties: - action: - description: Match a route with specific action - type. - enum: - - ANY - - ROUTE - - REDIRECT - - DIRECT_RESPONSE - type: string - name: - type: string - type: object - type: object - type: object - type: object - patch: - description: The patch to apply along with the operation. - properties: - filterClass: - description: Determines the filter insertion order. - enum: - - UNSPECIFIED - - AUTHN - - AUTHZ - - STATS - type: string - operation: - description: Determines how the patch should be applied. - enum: - - INVALID - - MERGE - - ADD - - REMOVE - - INSERT_BEFORE - - INSERT_AFTER - - INSERT_FIRST - - REPLACE - type: string - value: - description: The JSON config of the object being patched. - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - type: object - type: array - priority: - description: Priority defines the order in which patch sets are applied - within a context. - format: int32 - type: integer - workloadSelector: - properties: - labels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: gateways.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: Gateway - listKind: GatewayList - plural: gateways - shortNames: - - gw - singular: gateway - scope: Namespaced - versions: - - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting edge load balancer. See more details - at: https://istio.io/docs/reference/config/networking/gateway.html' - properties: - selector: - additionalProperties: - type: string - type: object - servers: - description: A list of server specifications. - items: - properties: - bind: - type: string - defaultEndpoint: - type: string - hosts: - description: One or more hosts exposed by this gateway. - items: - type: string - type: array - name: - description: An optional name of the server, when set must be - unique across all servers. - type: string - port: - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - tls: - description: Set of TLS related options that govern the server's - behavior. - properties: - caCertificates: - description: REQUIRED if mode is `MUTUAL`. - type: string - cipherSuites: - description: 'Optional: If specified, only support the specified - cipher list.' - items: - type: string - type: array - credentialName: - type: string - httpsRedirect: - type: boolean - maxProtocolVersion: - description: 'Optional: Maximum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - minProtocolVersion: - description: 'Optional: Minimum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - mode: - enum: - - PASSTHROUGH - - SIMPLE - - MUTUAL - - AUTO_PASSTHROUGH - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - serverCertificate: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - subjectAltNames: - items: - type: string - type: array - verifyCertificateHash: - items: - type: string - type: array - verifyCertificateSpki: - items: - type: string - type: array - type: object - type: object - type: array - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting edge load balancer. See more details - at: https://istio.io/docs/reference/config/networking/gateway.html' - properties: - selector: - additionalProperties: - type: string - type: object - servers: - description: A list of server specifications. - items: - properties: - bind: - type: string - defaultEndpoint: - type: string - hosts: - description: One or more hosts exposed by this gateway. - items: - type: string - type: array - name: - description: An optional name of the server, when set must be - unique across all servers. - type: string - port: - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - tls: - description: Set of TLS related options that govern the server's - behavior. - properties: - caCertificates: - description: REQUIRED if mode is `MUTUAL`. - type: string - cipherSuites: - description: 'Optional: If specified, only support the specified - cipher list.' - items: - type: string - type: array - credentialName: - type: string - httpsRedirect: - type: boolean - maxProtocolVersion: - description: 'Optional: Maximum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - minProtocolVersion: - description: 'Optional: Minimum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - mode: - enum: - - PASSTHROUGH - - SIMPLE - - MUTUAL - - AUTO_PASSTHROUGH - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - serverCertificate: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - subjectAltNames: - items: - type: string - type: array - verifyCertificateHash: - items: - type: string - type: array - verifyCertificateSpki: - items: - type: string - type: array - type: object - type: object - type: array - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: proxyconfigs.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: ProxyConfig - listKind: ProxyConfigList - plural: proxyconfigs - singular: proxyconfig - scope: Namespaced - versions: - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Provides configuration for individual workloads. See more - details at: https://istio.io/docs/reference/config/networking/proxy-config.html' - properties: - concurrency: - description: The number of worker threads to run. - nullable: true - type: integer - environmentVariables: - additionalProperties: - type: string - description: Additional environment variables for the proxy. - type: object - image: - description: Specifies the details of the proxy image. - properties: - imageType: - description: The image type of the image. - type: string - type: object - selector: - description: Optional. - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: serviceentries.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: ServiceEntry - listKind: ServiceEntryList - plural: serviceentries - shortNames: - - se - singular: serviceentry - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The hosts associated with the ServiceEntry - jsonPath: .spec.hosts - name: Hosts - type: string - - description: Whether the service is external to the mesh or part of the mesh - (MESH_EXTERNAL or MESH_INTERNAL) - jsonPath: .spec.location - name: Location - type: string - - description: Service discovery mode for the hosts (NONE, STATIC, or DNS) - jsonPath: .spec.resolution - name: Resolution - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting service registry. See more details - at: https://istio.io/docs/reference/config/networking/service-entry.html' - properties: - addresses: - description: The virtual IP addresses associated with the service. - items: - type: string - type: array - endpoints: - description: One or more endpoints associated with the service. - items: - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - type: array - exportTo: - description: A list of namespaces to which this service is exported. - items: - type: string - type: array - hosts: - description: The hosts associated with the ServiceEntry. - items: - type: string - type: array - location: - enum: - - MESH_EXTERNAL - - MESH_INTERNAL - type: string - ports: - description: The ports associated with the external service. - items: - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - type: array - resolution: - description: Service discovery mode for the hosts. - enum: - - NONE - - STATIC - - DNS - - DNS_ROUND_ROBIN - type: string - subjectAltNames: - items: - type: string - type: array - workloadSelector: - description: Applicable only for MESH_INTERNAL services. - properties: - labels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - description: The hosts associated with the ServiceEntry - jsonPath: .spec.hosts - name: Hosts - type: string - - description: Whether the service is external to the mesh or part of the mesh - (MESH_EXTERNAL or MESH_INTERNAL) - jsonPath: .spec.location - name: Location - type: string - - description: Service discovery mode for the hosts (NONE, STATIC, or DNS) - jsonPath: .spec.resolution - name: Resolution - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting service registry. See more details - at: https://istio.io/docs/reference/config/networking/service-entry.html' - properties: - addresses: - description: The virtual IP addresses associated with the service. - items: - type: string - type: array - endpoints: - description: One or more endpoints associated with the service. - items: - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - type: array - exportTo: - description: A list of namespaces to which this service is exported. - items: - type: string - type: array - hosts: - description: The hosts associated with the ServiceEntry. - items: - type: string - type: array - location: - enum: - - MESH_EXTERNAL - - MESH_INTERNAL - type: string - ports: - description: The ports associated with the external service. - items: - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - type: array - resolution: - description: Service discovery mode for the hosts. - enum: - - NONE - - STATIC - - DNS - - DNS_ROUND_ROBIN - type: string - subjectAltNames: - items: - type: string - type: array - workloadSelector: - description: Applicable only for MESH_INTERNAL services. - properties: - labels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: sidecars.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: Sidecar - listKind: SidecarList - plural: sidecars - singular: sidecar - scope: Namespaced - versions: - - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting network reachability of a sidecar. - See more details at: https://istio.io/docs/reference/config/networking/sidecar.html' - properties: - egress: - items: - properties: - bind: - type: string - captureMode: - enum: - - DEFAULT - - IPTABLES - - NONE - type: string - hosts: - items: - type: string - type: array - port: - description: The port associated with the listener. - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - type: object - type: array - ingress: - items: - properties: - bind: - description: The IP to which the listener should be bound. - type: string - captureMode: - enum: - - DEFAULT - - IPTABLES - - NONE - type: string - defaultEndpoint: - type: string - port: - description: The port associated with the listener. - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - tls: - properties: - caCertificates: - description: REQUIRED if mode is `MUTUAL`. - type: string - cipherSuites: - description: 'Optional: If specified, only support the specified - cipher list.' - items: - type: string - type: array - credentialName: - type: string - httpsRedirect: - type: boolean - maxProtocolVersion: - description: 'Optional: Maximum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - minProtocolVersion: - description: 'Optional: Minimum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - mode: - enum: - - PASSTHROUGH - - SIMPLE - - MUTUAL - - AUTO_PASSTHROUGH - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - serverCertificate: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - subjectAltNames: - items: - type: string - type: array - verifyCertificateHash: - items: - type: string - type: array - verifyCertificateSpki: - items: - type: string - type: array - type: object - type: object - type: array - outboundTrafficPolicy: - description: Configuration for the outbound traffic policy. - properties: - egressProxy: - properties: - host: - description: The name of a service from the service registry. - type: string - port: - description: Specifies the port on the host that is being - addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - mode: - enum: - - REGISTRY_ONLY - - ALLOW_ANY - type: string - type: object - workloadSelector: - properties: - labels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting network reachability of a sidecar. - See more details at: https://istio.io/docs/reference/config/networking/sidecar.html' - properties: - egress: - items: - properties: - bind: - type: string - captureMode: - enum: - - DEFAULT - - IPTABLES - - NONE - type: string - hosts: - items: - type: string - type: array - port: - description: The port associated with the listener. - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - type: object - type: array - ingress: - items: - properties: - bind: - description: The IP to which the listener should be bound. - type: string - captureMode: - enum: - - DEFAULT - - IPTABLES - - NONE - type: string - defaultEndpoint: - type: string - port: - description: The port associated with the listener. - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - tls: - properties: - caCertificates: - description: REQUIRED if mode is `MUTUAL`. - type: string - cipherSuites: - description: 'Optional: If specified, only support the specified - cipher list.' - items: - type: string - type: array - credentialName: - type: string - httpsRedirect: - type: boolean - maxProtocolVersion: - description: 'Optional: Maximum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - minProtocolVersion: - description: 'Optional: Minimum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - mode: - enum: - - PASSTHROUGH - - SIMPLE - - MUTUAL - - AUTO_PASSTHROUGH - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - serverCertificate: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - subjectAltNames: - items: - type: string - type: array - verifyCertificateHash: - items: - type: string - type: array - verifyCertificateSpki: - items: - type: string - type: array - type: object - type: object - type: array - outboundTrafficPolicy: - description: Configuration for the outbound traffic policy. - properties: - egressProxy: - properties: - host: - description: The name of a service from the service registry. - type: string - port: - description: Specifies the port on the host that is being - addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - mode: - enum: - - REGISTRY_ONLY - - ALLOW_ANY - type: string - type: object - workloadSelector: - properties: - labels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: virtualservices.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: VirtualService - listKind: VirtualServiceList - plural: virtualservices - shortNames: - - vs - singular: virtualservice - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The names of gateways and sidecars that should apply these routes - jsonPath: .spec.gateways - name: Gateways - type: string - - description: The destination hosts to which traffic is being sent - jsonPath: .spec.hosts - name: Hosts - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting label/content routing, sni routing, - etc. See more details at: https://istio.io/docs/reference/config/networking/virtual-service.html' - properties: - exportTo: - description: A list of namespaces to which this virtual service is - exported. - items: - type: string - type: array - gateways: - description: The names of gateways and sidecars that should apply - these routes. - items: - type: string - type: array - hosts: - description: The destination hosts to which traffic is being sent. - items: - type: string - type: array - http: - description: An ordered list of route rules for HTTP traffic. - items: - properties: - corsPolicy: - description: Cross-Origin Resource Sharing policy (CORS). - properties: - allowCredentials: - nullable: true - type: boolean - allowHeaders: - items: - type: string - type: array - allowMethods: - description: List of HTTP methods allowed to access the - resource. - items: - type: string - type: array - allowOrigin: - description: The list of origins that are allowed to perform - CORS requests. - items: - type: string - type: array - allowOrigins: - description: String patterns that match allowed origins. - items: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - type: array - exposeHeaders: - items: - type: string - type: array - maxAge: - type: string - type: object - delegate: - properties: - name: - description: Name specifies the name of the delegate VirtualService. - type: string - namespace: - description: Namespace specifies the namespace where the - delegate VirtualService resides. - type: string - type: object - fault: - description: Fault injection policy to apply on HTTP traffic - at the client side. - properties: - abort: - oneOf: - - not: - anyOf: - - required: - - httpStatus - - required: - - grpcStatus - - required: - - http2Error - - required: - - httpStatus - - required: - - grpcStatus - - required: - - http2Error - properties: - grpcStatus: - type: string - http2Error: - type: string - httpStatus: - description: HTTP status code to use to abort the Http - request. - format: int32 - type: integer - percentage: - description: Percentage of requests to be aborted with - the error code provided. - properties: - value: - format: double - type: number - type: object - type: object - delay: - oneOf: - - not: - anyOf: - - required: - - fixedDelay - - required: - - exponentialDelay - - required: - - fixedDelay - - required: - - exponentialDelay - properties: - exponentialDelay: - type: string - fixedDelay: - description: Add a fixed delay before forwarding the - request. - type: string - percent: - description: Percentage of requests on which the delay - will be injected (0-100). - format: int32 - type: integer - percentage: - description: Percentage of requests on which the delay - will be injected. - properties: - value: - format: double - type: number - type: object - type: object - type: object - headers: - properties: - request: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - response: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - type: object - match: - items: - properties: - authority: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - headers: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - type: object - ignoreUriCase: - description: Flag to specify whether the URI matching - should be case-insensitive. - type: boolean - method: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - name: - description: The name assigned to a match. - type: string - port: - description: Specifies the ports on the host that is being - addressed. - type: integer - queryParams: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - description: Query parameters for matching. - type: object - scheme: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - uri: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - withoutHeaders: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - description: withoutHeader has the same syntax with the - header, but has opposite meaning. - type: object - type: object - type: array - mirror: - properties: - host: - description: The name of a service from the service registry. - type: string - port: - description: Specifies the port on the host that is being - addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - mirror_percent: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - nullable: true - type: integer - mirrorPercent: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - nullable: true - type: integer - mirrorPercentage: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - properties: - value: - format: double - type: number - type: object - name: - description: The name assigned to the route for debugging purposes. - type: string - redirect: - description: A HTTP rule can either redirect or forward (default) - traffic. - oneOf: - - not: - anyOf: - - required: - - port - - required: - - derivePort - - required: - - port - - required: - - derivePort - properties: - authority: - type: string - derivePort: - enum: - - FROM_PROTOCOL_DEFAULT - - FROM_REQUEST_PORT - type: string - port: - description: On a redirect, overwrite the port portion of - the URL with this value. - type: integer - redirectCode: - type: integer - scheme: - description: On a redirect, overwrite the scheme portion - of the URL with this value. - type: string - uri: - type: string - type: object - retries: - description: Retry policy for HTTP requests. - properties: - attempts: - description: Number of retries to be allowed for a given - request. - format: int32 - type: integer - perTryTimeout: - description: Timeout per attempt for a given request, including - the initial call and any retries. - type: string - retryOn: - description: Specifies the conditions under which retry - takes place. - type: string - retryRemoteLocalities: - description: Flag to specify whether the retries should - retry to other localities. - nullable: true - type: boolean - type: object - rewrite: - description: Rewrite HTTP URIs and Authority headers. - properties: - authority: - description: rewrite the Authority/Host header with this - value. - type: string - uri: - type: string - type: object - route: - description: A HTTP rule can either redirect or forward (default) - traffic. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - headers: - properties: - request: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - response: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - timeout: - description: Timeout for HTTP requests, default is disabled. - type: string - type: object - type: array - tcp: - description: An ordered list of route rules for opaque TCP traffic. - items: - properties: - match: - items: - properties: - destinationSubnets: - description: IPv4 or IPv6 ip addresses of destination - with optional subnet. - items: - type: string - type: array - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - port: - description: Specifies the port on the host that is being - addressed. - type: integer - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - sourceSubnet: - description: IPv4 or IPv6 ip address of source with optional - subnet. - type: string - type: object - type: array - route: - description: The destination to which the connection should - be forwarded to. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - type: object - type: array - tls: - items: - properties: - match: - items: - properties: - destinationSubnets: - description: IPv4 or IPv6 ip addresses of destination - with optional subnet. - items: - type: string - type: array - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - port: - description: Specifies the port on the host that is being - addressed. - type: integer - sniHosts: - description: SNI (server name indicator) to match on. - items: - type: string - type: array - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - type: object - type: array - route: - description: The destination to which the connection should - be forwarded to. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - type: object - type: array - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - description: The names of gateways and sidecars that should apply these routes - jsonPath: .spec.gateways - name: Gateways - type: string - - description: The destination hosts to which traffic is being sent - jsonPath: .spec.hosts - name: Hosts - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting label/content routing, sni routing, - etc. See more details at: https://istio.io/docs/reference/config/networking/virtual-service.html' - properties: - exportTo: - description: A list of namespaces to which this virtual service is - exported. - items: - type: string - type: array - gateways: - description: The names of gateways and sidecars that should apply - these routes. - items: - type: string - type: array - hosts: - description: The destination hosts to which traffic is being sent. - items: - type: string - type: array - http: - description: An ordered list of route rules for HTTP traffic. - items: - properties: - corsPolicy: - description: Cross-Origin Resource Sharing policy (CORS). - properties: - allowCredentials: - nullable: true - type: boolean - allowHeaders: - items: - type: string - type: array - allowMethods: - description: List of HTTP methods allowed to access the - resource. - items: - type: string - type: array - allowOrigin: - description: The list of origins that are allowed to perform - CORS requests. - items: - type: string - type: array - allowOrigins: - description: String patterns that match allowed origins. - items: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - type: array - exposeHeaders: - items: - type: string - type: array - maxAge: - type: string - type: object - delegate: - properties: - name: - description: Name specifies the name of the delegate VirtualService. - type: string - namespace: - description: Namespace specifies the namespace where the - delegate VirtualService resides. - type: string - type: object - fault: - description: Fault injection policy to apply on HTTP traffic - at the client side. - properties: - abort: - oneOf: - - not: - anyOf: - - required: - - httpStatus - - required: - - grpcStatus - - required: - - http2Error - - required: - - httpStatus - - required: - - grpcStatus - - required: - - http2Error - properties: - grpcStatus: - type: string - http2Error: - type: string - httpStatus: - description: HTTP status code to use to abort the Http - request. - format: int32 - type: integer - percentage: - description: Percentage of requests to be aborted with - the error code provided. - properties: - value: - format: double - type: number - type: object - type: object - delay: - oneOf: - - not: - anyOf: - - required: - - fixedDelay - - required: - - exponentialDelay - - required: - - fixedDelay - - required: - - exponentialDelay - properties: - exponentialDelay: - type: string - fixedDelay: - description: Add a fixed delay before forwarding the - request. - type: string - percent: - description: Percentage of requests on which the delay - will be injected (0-100). - format: int32 - type: integer - percentage: - description: Percentage of requests on which the delay - will be injected. - properties: - value: - format: double - type: number - type: object - type: object - type: object - headers: - properties: - request: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - response: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - type: object - match: - items: - properties: - authority: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - headers: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - type: object - ignoreUriCase: - description: Flag to specify whether the URI matching - should be case-insensitive. - type: boolean - method: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - name: - description: The name assigned to a match. - type: string - port: - description: Specifies the ports on the host that is being - addressed. - type: integer - queryParams: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - description: Query parameters for matching. - type: object - scheme: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - uri: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - withoutHeaders: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - description: withoutHeader has the same syntax with the - header, but has opposite meaning. - type: object - type: object - type: array - mirror: - properties: - host: - description: The name of a service from the service registry. - type: string - port: - description: Specifies the port on the host that is being - addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - mirror_percent: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - nullable: true - type: integer - mirrorPercent: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - nullable: true - type: integer - mirrorPercentage: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - properties: - value: - format: double - type: number - type: object - name: - description: The name assigned to the route for debugging purposes. - type: string - redirect: - description: A HTTP rule can either redirect or forward (default) - traffic. - oneOf: - - not: - anyOf: - - required: - - port - - required: - - derivePort - - required: - - port - - required: - - derivePort - properties: - authority: - type: string - derivePort: - enum: - - FROM_PROTOCOL_DEFAULT - - FROM_REQUEST_PORT - type: string - port: - description: On a redirect, overwrite the port portion of - the URL with this value. - type: integer - redirectCode: - type: integer - scheme: - description: On a redirect, overwrite the scheme portion - of the URL with this value. - type: string - uri: - type: string - type: object - retries: - description: Retry policy for HTTP requests. - properties: - attempts: - description: Number of retries to be allowed for a given - request. - format: int32 - type: integer - perTryTimeout: - description: Timeout per attempt for a given request, including - the initial call and any retries. - type: string - retryOn: - description: Specifies the conditions under which retry - takes place. - type: string - retryRemoteLocalities: - description: Flag to specify whether the retries should - retry to other localities. - nullable: true - type: boolean - type: object - rewrite: - description: Rewrite HTTP URIs and Authority headers. - properties: - authority: - description: rewrite the Authority/Host header with this - value. - type: string - uri: - type: string - type: object - route: - description: A HTTP rule can either redirect or forward (default) - traffic. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - headers: - properties: - request: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - response: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - timeout: - description: Timeout for HTTP requests, default is disabled. - type: string - type: object - type: array - tcp: - description: An ordered list of route rules for opaque TCP traffic. - items: - properties: - match: - items: - properties: - destinationSubnets: - description: IPv4 or IPv6 ip addresses of destination - with optional subnet. - items: - type: string - type: array - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - port: - description: Specifies the port on the host that is being - addressed. - type: integer - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - sourceSubnet: - description: IPv4 or IPv6 ip address of source with optional - subnet. - type: string - type: object - type: array - route: - description: The destination to which the connection should - be forwarded to. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - type: object - type: array - tls: - items: - properties: - match: - items: - properties: - destinationSubnets: - description: IPv4 or IPv6 ip addresses of destination - with optional subnet. - items: - type: string - type: array - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - port: - description: Specifies the port on the host that is being - addressed. - type: integer - sniHosts: - description: SNI (server name indicator) to match on. - items: - type: string - type: array - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - type: object - type: array - route: - description: The destination to which the connection should - be forwarded to. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - type: object - type: array - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: workloadentries.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: WorkloadEntry - listKind: WorkloadEntryList - plural: workloadentries - shortNames: - - we - singular: workloadentry - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - description: Address associated with the network endpoint. - jsonPath: .spec.address - name: Address - type: string - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting VMs onboarded into the mesh. See - more details at: https://istio.io/docs/reference/config/networking/workload-entry.html' - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - description: Address associated with the network endpoint. - jsonPath: .spec.address - name: Address - type: string - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting VMs onboarded into the mesh. See - more details at: https://istio.io/docs/reference/config/networking/workload-entry.html' - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: workloadgroups.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: WorkloadGroup - listKind: WorkloadGroupList - plural: workloadgroups - shortNames: - - wg - singular: workloadgroup - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Describes a collection of workload instances. See more details - at: https://istio.io/docs/reference/config/networking/workload-group.html' - properties: - metadata: - description: Metadata that will be used for all corresponding `WorkloadEntries`. - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - probe: - description: '`ReadinessProbe` describes the configuration the user - must provide for healthchecking on their workload.' - oneOf: - - not: - anyOf: - - required: - - httpGet - - required: - - tcpSocket - - required: - - exec - - required: - - httpGet - - required: - - tcpSocket - - required: - - exec - properties: - exec: - description: Health is determined by how the command that is executed - exited. - properties: - command: - description: Command to run. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for the probe to be - considered failed after having succeeded. - format: int32 - type: integer - httpGet: - properties: - host: - description: Host name to connect to, defaults to the pod - IP. - type: string - httpHeaders: - description: Headers the proxy will pass on to make the request. - items: - properties: - name: - type: string - value: - type: string - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - description: Port on which the endpoint lives. - type: integer - scheme: - type: string - type: object - initialDelaySeconds: - description: Number of seconds after the container has started - before readiness probes are initiated. - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform the probe. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for the probe to be - considered successful after having failed. - format: int32 - type: integer - tcpSocket: - description: Health is determined by if the proxy is able to connect. - properties: - host: - type: string - port: - type: integer - type: object - timeoutSeconds: - description: Number of seconds after which the probe times out. - format: int32 - type: integer - type: object - template: - description: Template to be used for the generation of `WorkloadEntry` - resources that belong to this `WorkloadGroup`. - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - properties: - metadata: - description: Metadata that will be used for all corresponding `WorkloadEntries`. - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - probe: - description: '`ReadinessProbe` describes the configuration the user - must provide for healthchecking on their workload.' - oneOf: - - not: - anyOf: - - required: - - httpGet - - required: - - tcpSocket - - required: - - exec - - required: - - httpGet - - required: - - tcpSocket - - required: - - exec - properties: - exec: - description: Health is determined by how the command that is executed - exited. - properties: - command: - description: Command to run. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for the probe to be - considered failed after having succeeded. - format: int32 - type: integer - httpGet: - properties: - host: - description: Host name to connect to, defaults to the pod - IP. - type: string - httpHeaders: - description: Headers the proxy will pass on to make the request. - items: - properties: - name: - type: string - value: - type: string - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - description: Port on which the endpoint lives. - type: integer - scheme: - type: string - type: object - initialDelaySeconds: - description: Number of seconds after the container has started - before readiness probes are initiated. - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform the probe. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for the probe to be - considered successful after having failed. - format: int32 - type: integer - tcpSocket: - description: Health is determined by if the proxy is able to connect. - properties: - host: - type: string - port: - type: integer - type: object - timeoutSeconds: - description: Number of seconds after which the probe times out. - format: int32 - type: integer - type: object - template: - description: Template to be used for the generation of `WorkloadEntry` - resources that belong to this `WorkloadGroup`. - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - istio: security - release: istio - name: authorizationpolicies.security.istio.io -spec: - group: security.istio.io - names: - categories: - - istio-io - - security-istio-io - kind: AuthorizationPolicy - listKind: AuthorizationPolicyList - plural: authorizationpolicies - singular: authorizationpolicy - scope: Namespaced - versions: - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration for access control on workloads. See more - details at: https://istio.io/docs/reference/config/security/authorization-policy.html' - oneOf: - - not: - anyOf: - - required: - - provider - - required: - - provider - properties: - action: - description: Optional. - enum: - - ALLOW - - DENY - - AUDIT - - CUSTOM - type: string - provider: - description: Specifies detailed configuration of the CUSTOM action. - properties: - name: - description: Specifies the name of the extension provider. - type: string - type: object - rules: - description: Optional. - items: - properties: - from: - description: Optional. - items: - properties: - source: - description: Source specifies the source of a request. - properties: - ipBlocks: - description: Optional. - items: - type: string - type: array - namespaces: - description: Optional. - items: - type: string - type: array - notIpBlocks: - description: Optional. - items: - type: string - type: array - notNamespaces: - description: Optional. - items: - type: string - type: array - notPrincipals: - description: Optional. - items: - type: string - type: array - notRemoteIpBlocks: - description: Optional. - items: - type: string - type: array - notRequestPrincipals: - description: Optional. - items: - type: string - type: array - principals: - description: Optional. - items: - type: string - type: array - remoteIpBlocks: - description: Optional. - items: - type: string - type: array - requestPrincipals: - description: Optional. - items: - type: string - type: array - type: object - type: object - type: array - to: - description: Optional. - items: - properties: - operation: - description: Operation specifies the operation of a request. - properties: - hosts: - description: Optional. - items: - type: string - type: array - methods: - description: Optional. - items: - type: string - type: array - notHosts: - description: Optional. - items: - type: string - type: array - notMethods: - description: Optional. - items: - type: string - type: array - notPaths: - description: Optional. - items: - type: string - type: array - notPorts: - description: Optional. - items: - type: string - type: array - paths: - description: Optional. - items: - type: string - type: array - ports: - description: Optional. - items: - type: string - type: array - type: object - type: object - type: array - when: - description: Optional. - items: - properties: - key: - description: The name of an Istio attribute. - type: string - notValues: - description: Optional. - items: - type: string - type: array - values: - description: Optional. - items: - type: string - type: array - type: object - type: array - type: object - type: array - selector: - description: Optional. - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - istio: security - release: istio - name: peerauthentications.security.istio.io -spec: - group: security.istio.io - names: - categories: - - istio-io - - security-istio-io - kind: PeerAuthentication - listKind: PeerAuthenticationList - plural: peerauthentications - shortNames: - - pa - singular: peerauthentication - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Defines the mTLS mode used for peer authentication. - jsonPath: .spec.mtls.mode - name: Mode - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: PeerAuthentication defines how traffic will be tunneled (or - not) to the sidecar. - properties: - mtls: - description: Mutual TLS settings for workload. - properties: - mode: - description: Defines the mTLS mode used for peer authentication. - enum: - - UNSET - - DISABLE - - PERMISSIVE - - STRICT - type: string - type: object - portLevelMtls: - additionalProperties: - properties: - mode: - description: Defines the mTLS mode used for peer authentication. - enum: - - UNSET - - DISABLE - - PERMISSIVE - - STRICT - type: string - type: object - description: Port specific mutual TLS settings. - type: object - selector: - description: The selector determines the workloads to apply the ChannelAuthentication - on. - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - istio: security - release: istio - name: requestauthentications.security.istio.io -spec: - group: security.istio.io - names: - categories: - - istio-io - - security-istio-io - kind: RequestAuthentication - listKind: RequestAuthenticationList - plural: requestauthentications - shortNames: - - ra - singular: requestauthentication - scope: Namespaced - versions: - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: RequestAuthentication defines what request authentication - methods are supported by a workload. - properties: - jwtRules: - description: Define the list of JWTs that can be validated at the - selected workloads' proxy. - items: - properties: - audiences: - items: - type: string - type: array - forwardOriginalToken: - description: If set to true, the original token will be kept - for the upstream request. - type: boolean - fromHeaders: - description: List of header locations from which JWT is expected. - items: - properties: - name: - description: The HTTP header name. - type: string - prefix: - description: The prefix that should be stripped before - decoding the token. - type: string - type: object - type: array - fromParams: - description: List of query parameters from which JWT is expected. - items: - type: string - type: array - issuer: - description: Identifies the issuer that issued the JWT. - type: string - jwks: - description: JSON Web Key Set of public keys to validate signature - of the JWT. - type: string - jwks_uri: - type: string - jwksUri: - type: string - outputPayloadToHeader: - type: string - type: object - type: array - selector: - description: Optional. - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - istio: telemetry - release: istio - name: telemetries.telemetry.istio.io -spec: - group: telemetry.istio.io - names: - categories: - - istio-io - - telemetry-istio-io - kind: Telemetry - listKind: TelemetryList - plural: telemetries - shortNames: - - telemetry - singular: telemetry - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Telemetry configuration for workloads. See more details - at: https://istio.io/docs/reference/config/telemetry.html' - properties: - accessLogging: - description: Optional. - items: - properties: - disabled: - description: Controls logging. - nullable: true - type: boolean - filter: - description: Optional. - properties: - expression: - description: CEL expression for selecting when requests/connections - should be logged. - type: string - type: object - match: - description: Allows tailoring of logging behavior to specific - conditions. - properties: - mode: - enum: - - CLIENT_AND_SERVER - - CLIENT - - SERVER - type: string - type: object - providers: - description: Optional. - items: - properties: - name: - description: Required. - type: string - type: object - type: array - type: object - type: array - metrics: - description: Optional. - items: - properties: - overrides: - description: Optional. - items: - properties: - disabled: - description: Optional. - nullable: true - type: boolean - match: - description: Match allows provides the scope of the override. - oneOf: - - not: - anyOf: - - required: - - metric - - required: - - customMetric - - required: - - metric - - required: - - customMetric - properties: - customMetric: - description: Allows free-form specification of a metric. - type: string - metric: - description: One of the well-known Istio Standard - Metrics. - enum: - - ALL_METRICS - - REQUEST_COUNT - - REQUEST_DURATION - - REQUEST_SIZE - - RESPONSE_SIZE - - TCP_OPENED_CONNECTIONS - - TCP_CLOSED_CONNECTIONS - - TCP_SENT_BYTES - - TCP_RECEIVED_BYTES - - GRPC_REQUEST_MESSAGES - - GRPC_RESPONSE_MESSAGES - type: string - mode: - enum: - - CLIENT_AND_SERVER - - CLIENT - - SERVER - type: string - type: object - tagOverrides: - additionalProperties: - properties: - operation: - description: Operation controls whether or not to - update/add a tag, or to remove it. - enum: - - UPSERT - - REMOVE - type: string - value: - description: Value is only considered if the operation - is `UPSERT`. - type: string - type: object - description: Optional. - type: object - type: object - type: array - providers: - description: Optional. - items: - properties: - name: - description: Required. - type: string - type: object - type: array - type: object - type: array - selector: - description: Optional. - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - tracing: - description: Optional. - items: - properties: - customTags: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - literal - - required: - - environment - - required: - - header - - required: - - literal - - required: - - environment - - required: - - header - properties: - environment: - description: Environment adds the value of an environment - variable to each span. - properties: - defaultValue: - description: Optional. - type: string - name: - description: Name of the environment variable from - which to extract the tag value. - type: string - type: object - header: - properties: - defaultValue: - description: Optional. - type: string - name: - description: Name of the header from which to extract - the tag value. - type: string - type: object - literal: - description: Literal adds the same, hard-coded value to - each span. - properties: - value: - description: The tag value to use. - type: string - type: object - type: object - description: Optional. - type: object - disableSpanReporting: - description: Controls span reporting. - nullable: true - type: boolean - match: - description: Allows tailoring of behavior to specific conditions. - properties: - mode: - enum: - - CLIENT_AND_SERVER - - CLIENT - - SERVER - type: string - type: object - providers: - description: Optional. - items: - properties: - name: - description: Required. - type: string - type: object - type: array - randomSamplingPercentage: - nullable: true - type: number - useRequestIdForTraceSampling: - nullable: true - type: boolean - type: object - type: array - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - ---- -{{- end }} diff --git a/manifests/charts/istiod-remote/templates/crd-operator.yaml b/manifests/charts/istiod-remote/templates/crd-operator.yaml deleted file mode 100644 index 42e95ee8e..000000000 --- a/manifests/charts/istiod-remote/templates/crd-operator.yaml +++ /dev/null @@ -1,50 +0,0 @@ -{{- if .Values.global.configCluster }} -# SYNC WITH manifests/charts/istio-operator/templates -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: istiooperators.install.istio.io - labels: - release: istio -spec: - conversion: - strategy: None - group: install.istio.io - names: - kind: IstioOperator - listKind: IstioOperatorList - plural: istiooperators - singular: istiooperator - shortNames: - - iop - - io - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Istio control plane revision - jsonPath: .spec.revision - name: Revision - type: string - - description: IOP current state - jsonPath: .status.status - name: Status - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - subresources: - status: {} - name: v1alpha1 - schema: - openAPIV3Schema: - type: object - x-kubernetes-preserve-unknown-fields: true - served: true - storage: true ---- -{{- end }} diff --git a/manifests/charts/istiod-remote/templates/endpoints.yaml b/manifests/charts/istiod-remote/templates/endpoints.yaml deleted file mode 100644 index fb4dd5efd..000000000 --- a/manifests/charts/istiod-remote/templates/endpoints.yaml +++ /dev/null @@ -1,30 +0,0 @@ -{{- if .Values.global.remotePilotAddress }} - {{- if .Values.pilot.enabled }} -apiVersion: v1 -kind: Endpoints -metadata: - name: istiod-remote - namespace: {{ .Release.Namespace }} -subsets: -- addresses: - - ip: {{ .Values.global.remotePilotAddress }} - ports: - - port: 15012 - name: tcp-istiod - protocol: TCP - {{- else if regexMatch "^([0-9]*\\.){3}[0-9]*$" .Values.global.remotePilotAddress }} -apiVersion: v1 -kind: Endpoints -metadata: - name: istiod - namespace: {{ .Release.Namespace }} -subsets: -- addresses: - - ip: {{ .Values.global.remotePilotAddress }} - ports: - - port: 15012 - name: tcp-istiod - protocol: TCP - {{- end }} ---- -{{- end }} diff --git a/manifests/charts/istiod-remote/templates/istiod-injector-configmap.yaml b/manifests/charts/istiod-remote/templates/istiod-injector-configmap.yaml deleted file mode 100644 index ac4679ef6..000000000 --- a/manifests/charts/istiod-remote/templates/istiod-injector-configmap.yaml +++ /dev/null @@ -1,71 +0,0 @@ -{{- if not .Values.global.omitSidecarInjectorConfigMap }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: istio-sidecar-injector{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - namespace: {{ .Release.Namespace }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Pilot" - release: {{ .Release.Name }} -data: -{{/* Scope the values to just top level fields used in the template, to reduce the size. */}} - values: |- -{{ pick .Values "global" "istio_cni" "sidecarInjectorWebhook" "revision" | toPrettyJson | indent 4 }} - - # To disable injection: use omitSidecarInjectorConfigMap, which disables the webhook patching - # and istiod webhook functionality. - # - # New fields should not use Values - it is a 'primary' config object, users should be able - # to fine tune it or use it with kube-inject. - config: |- - # defaultTemplates defines the default template to use for pods that do not explicitly specify a template - {{- if .Values.sidecarInjectorWebhook.defaultTemplates }} - defaultTemplates: -{{- range .Values.sidecarInjectorWebhook.defaultTemplates}} - - {{ . }} -{{- end }} - {{- else }} - defaultTemplates: [sidecar] - {{- end }} - policy: {{ .Values.global.proxy.autoInject }} - alwaysInjectSelector: -{{ toYaml .Values.sidecarInjectorWebhook.alwaysInjectSelector | trim | indent 6 }} - neverInjectSelector: -{{ toYaml .Values.sidecarInjectorWebhook.neverInjectSelector | trim | indent 6 }} - injectedAnnotations: - {{- range $key, $val := .Values.sidecarInjectorWebhook.injectedAnnotations }} - "{{ $key }}": {{ $val | quote }} - {{- end }} - {{- /* If someone ends up with this new template, but an older Istiod image, they will attempt to render this template - which will fail with "Pod injection failed: template: inject:1: function "Istio_1_9_Required_Template_And_Version_Mismatched" not defined". - This should make it obvious that their installation is broken. - */}} - template: {{ `{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}` | quote }} - templates: -{{- if not (hasKey .Values.sidecarInjectorWebhook.templates "sidecar") }} - sidecar: | -{{ .Files.Get "files/injection-template.yaml" | trim | indent 8 }} -{{- end }} -{{- if not (hasKey .Values.sidecarInjectorWebhook.templates "gateway") }} - gateway: | -{{ .Files.Get "files/gateway-injection-template.yaml" | trim | indent 8 }} -{{- end }} -{{- if not (hasKey .Values.sidecarInjectorWebhook.templates "grpc-simple") }} - grpc-simple: | -{{ .Files.Get "files/grpc-simple.yaml" | trim | indent 8 }} -{{- end }} -{{- if not (hasKey .Values.sidecarInjectorWebhook.templates "grpc-agent") }} - grpc-agent: | -{{ .Files.Get "files/grpc-agent.yaml" | trim | indent 8 }} -{{- end }} -{{- if not (hasKey .Values.sidecarInjectorWebhook.templates "dubbo") }} - dubbo: | -{{ .Files.Get "files/dubbo.yaml" | trim | indent 8 }} -{{- end }} -{{- with .Values.sidecarInjectorWebhook.templates }} -{{ toYaml . | trim | indent 6 }} -{{- end }} - -{{- end }} diff --git a/manifests/charts/istiod-remote/templates/mutatingwebhook.yaml b/manifests/charts/istiod-remote/templates/mutatingwebhook.yaml deleted file mode 100644 index c8f304924..000000000 --- a/manifests/charts/istiod-remote/templates/mutatingwebhook.yaml +++ /dev/null @@ -1,149 +0,0 @@ -{{- /* Core defines the common configuration used by all webhook segments */}} -{{/* Copy just what we need to avoid expensive deepCopy */}} -{{- $whv := dict - "revision" .Values.revision - "injectionPath" .Values.istiodRemote.injectionPath - "injectionURL" .Values.istiodRemote.injectionURL - "namespace" .Release.Namespace }} -{{- define "core" }} -{{- /* Kubernetes unfortunately requires a unique name for the webhook in some newer versions, so we assign -a unique prefix to each. */}} -- name: {{.Prefix}}sidecar-injector.istio.io - clientConfig: - {{- if .injectionURL }} - url: "{{ .injectionURL }}" - {{- else }} - service: - name: istiod{{- if not (eq .revision "") }}-{{ .revision }}{{- end }} - namespace: {{ .namespace }} - path: "{{ .injectionPath }}" - port: 443 - {{- end }} - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] -{{- end }} -{{- /* Installed for each revision - not installed for cluster resources ( cluster roles, bindings, crds) */}} -{{- if not .Values.global.operatorManageWebhooks }} -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: -{{- if eq .Release.Namespace "dubbo-system"}} - name: istio-sidecar-injector{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} -{{- else }} - name: istio-sidecar-injector{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}-{{ .Release.Namespace }} -{{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} - install.operator.istio.io/owning-resource: {{ .Values.ownerName | default "unknown" }} - operator.istio.io/component: "Pilot" - app: sidecar-injector - release: {{ .Release.Name }} -webhooks: -{{- /* Set up the selectors. First section is for revision, rest is for "default" revision */}} - -{{- /* Case 1: namespace selector matches, and object doesn't disable */}} -{{- /* Note: if both revision and legacy selector, we give precedence to the legacy one */}} -{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "rev.namespace.") ) }} - namespaceSelector: - matchExpressions: - - key: istio.io/rev - operator: In - values: - {{- if (eq .Values.revision "") }} - - "default" - {{- else }} - - "{{ .Values.revision }}" - {{- end }} - - key: istio-injection - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" - -{{- /* Case 2: No namespace selector, but object selects our revision (and doesn't disable) */}} -{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "rev.object.") ) }} - namespaceSelector: - matchExpressions: - - key: istio.io/rev - operator: DoesNotExist - - key: istio-injection - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" - - key: istio.io/rev - operator: In - values: - {{- if (eq .Values.revision "") }} - - "default" - {{- else }} - - "{{ .Values.revision }}" - {{- end }} - - -{{- /* Webhooks for default revision */}} -{{- if (eq .Values.revision "") }} - -{{- /* Case 1: Namespace selector enabled, and object selector is not injected */}} -{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "namespace.") ) }} - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: In - values: - - enabled - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" - -{{- /* Case 2: no namespace label, but object selector is enabled (and revision label is not, which has priority) */}} -{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "object.") ) }} - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: DoesNotExist - - key: istio.io/rev - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: In - values: - - "true" - - key: istio.io/rev - operator: DoesNotExist - -{{- if .Values.sidecarInjectorWebhook.enableNamespacesByDefault }} -{{- /* Special case 3: no labels at all */}} -{{- include "core" (mergeOverwrite (deepCopy $whv) (dict "Prefix" "auto.") ) }} - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: DoesNotExist - - key: istio.io/rev - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: DoesNotExist - - key: istio.io/rev - operator: DoesNotExist -{{- end }} - -{{- end }} -{{- end }} diff --git a/manifests/charts/istiod-remote/templates/reader-clusterrole.yaml b/manifests/charts/istiod-remote/templates/reader-clusterrole.yaml deleted file mode 100644 index 793b75e66..000000000 --- a/manifests/charts/istiod-remote/templates/reader-clusterrole.yaml +++ /dev/null @@ -1,55 +0,0 @@ -{{ $mcsAPIGroup := or .Values.pilot.env.MCS_API_GROUP "multicluster.x-k8s.io" }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istio-reader-clusterrole{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }}-{{ .Release.Namespace }} - labels: - app: istio-reader - release: {{ .Release.Name }} -rules: - - apiGroups: - - "config.istio.io" - - "security.istio.io" - - "networking.istio.io" - - "authentication.istio.io" - - "rbac.istio.io" - resources: ["*"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["endpoints", "pods", "services", "nodes", "replicationcontrollers", "namespaces", "secrets"] - verbs: ["get", "list", "watch"] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list" ] - resources: [ "workloadentries" ] - - apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "list", "watch"] - - apiGroups: ["discovery.k8s.io"] - resources: ["endpointslices"] - verbs: ["get", "list", "watch"] - - apiGroups: ["{{ $mcsAPIGroup }}"] - resources: ["serviceexports"] - verbs: ["get", "list", "watch", "create", "delete"] - - apiGroups: ["{{ $mcsAPIGroup }}"] - resources: ["serviceimports"] - verbs: ["get", "list", "watch"] - - apiGroups: ["apps"] - resources: ["replicasets"] - verbs: ["get", "list", "watch"] - - apiGroups: ["authentication.k8s.io"] - resources: ["tokenreviews"] - verbs: ["create"] - - apiGroups: ["authorization.k8s.io"] - resources: ["subjectaccessreviews"] - verbs: ["create"] -{{- if .Values.global.externalIstiod }} - - apiGroups: [""] - resources: ["configmaps"] - verbs: ["create", "get", "list", "watch", "update"] - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["mutatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update", "patch"] - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["validatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update"] -{{- end}} diff --git a/manifests/charts/istiod-remote/templates/reader-clusterrolebinding.yaml b/manifests/charts/istiod-remote/templates/reader-clusterrolebinding.yaml deleted file mode 100644 index 4f9925c9d..000000000 --- a/manifests/charts/istiod-remote/templates/reader-clusterrolebinding.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istio-reader-clusterrole{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }}-{{ .Release.Namespace }} - labels: - app: istio-reader - release: {{ .Release.Name }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istio-reader-clusterrole{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }}-{{ .Release.Namespace }} -subjects: - - kind: ServiceAccount - name: istio-reader-service-account - namespace: {{ .Values.global.istioNamespace }} diff --git a/manifests/charts/istiod-remote/templates/reader-serviceaccount.yaml b/manifests/charts/istiod-remote/templates/reader-serviceaccount.yaml deleted file mode 100644 index d9ce18c27..000000000 --- a/manifests/charts/istiod-remote/templates/reader-serviceaccount.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# This service account aggregates reader permissions for the revisions in a given cluster -# Should be used for remote secret creation. -apiVersion: v1 -kind: ServiceAccount - {{- if .Values.global.imagePullSecrets }} -imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} -metadata: - name: istio-reader-service-account - namespace: {{ .Values.global.istioNamespace }} - labels: - app: istio-reader - release: {{ .Release.Name }} diff --git a/manifests/charts/istiod-remote/templates/role.yaml b/manifests/charts/istiod-remote/templates/role.yaml deleted file mode 100644 index 699491275..000000000 --- a/manifests/charts/istiod-remote/templates/role.yaml +++ /dev/null @@ -1,22 +0,0 @@ -{{- if .Values.global.configCluster }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: istiod{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }} - namespace: {{ .Values.global.istioNamespace }} - labels: - app: istiod - release: {{ .Release.Name }} -rules: -# permissions to verify the webhook is ready and rejecting -# invalid config. We use --server-dry-run so no config is persisted. -- apiGroups: ["networking.istio.io"] - verbs: ["create"] - resources: ["gateways"] - -# For storing CA secret -- apiGroups: [""] - resources: ["secrets"] - # TODO lock this down to istio-ca-cert if not using the DNS cert mesh config - verbs: ["create", "get", "watch", "list", "update", "delete"] -{{- end }} diff --git a/manifests/charts/istiod-remote/templates/rolebinding.yaml b/manifests/charts/istiod-remote/templates/rolebinding.yaml deleted file mode 100644 index f65b3b122..000000000 --- a/manifests/charts/istiod-remote/templates/rolebinding.yaml +++ /dev/null @@ -1,18 +0,0 @@ -{{- if .Values.global.configCluster }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: istiod{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }} - namespace: {{ .Values.global.istioNamespace }} - labels: - app: istiod - release: {{ .Release.Name }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: istiod{{- if not (eq .Values.revision "")}}-{{ .Values.revision }}{{- end }} -subjects: - - kind: ServiceAccount - name: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - namespace: {{ .Values.global.istioNamespace }} -{{- end }} diff --git a/manifests/charts/istiod-remote/templates/serviceaccount.yaml b/manifests/charts/istiod-remote/templates/serviceaccount.yaml deleted file mode 100644 index 4f8d20f6a..000000000 --- a/manifests/charts/istiod-remote/templates/serviceaccount.yaml +++ /dev/null @@ -1,17 +0,0 @@ -{{- if .Values.global.configCluster }} -apiVersion: v1 -kind: ServiceAccount - {{- if .Values.global.imagePullSecrets }} -imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} -metadata: - name: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - namespace: {{ .Values.global.istioNamespace }} - labels: - app: istiod - release: {{ .Release.Name }} ---- -{{- end }} diff --git a/manifests/charts/istiod-remote/templates/services.yaml b/manifests/charts/istiod-remote/templates/services.yaml deleted file mode 100644 index 705c36001..000000000 --- a/manifests/charts/istiod-remote/templates/services.yaml +++ /dev/null @@ -1,37 +0,0 @@ -{{- if .Values.global.remotePilotAddress }} - {{- if .Values.pilot.enabled }} -# when local istiod is enabled, we can't use istiod service name to reach the remote control plane -apiVersion: v1 -kind: Service -metadata: - name: istiod-remote - namespace: {{ .Release.Namespace }} -spec: - ports: - - port: 15012 - name: tcp-istiod - protocol: TCP - clusterIP: None - {{- else }} -# when local istiod isn't enabled, we can use istiod service name to reach the remote control plane -apiVersion: v1 -kind: Service -metadata: - name: istiod - namespace: {{ .Release.Namespace }} -spec: - ports: - - port: 15012 - name: tcp-istiod - protocol: TCP - # if the remotePilotAddress is IP addr, we use clusterIP: None. - # else, we use externalName - {{- if regexMatch "^([0-9]*\\.){3}[0-9]*$" .Values.global.remotePilotAddress }} - clusterIP: None - {{- else }} - type: ExternalName - externalName: {{ .Values.global.remotePilotAddress }} - {{- end }} - {{- end }} ---- -{{- end }} diff --git a/manifests/charts/istiod-remote/templates/telemetryv2_1.11.yaml b/manifests/charts/istiod-remote/templates/telemetryv2_1.11.yaml deleted file mode 100644 index da7acd7d7..000000000 --- a/manifests/charts/istiod-remote/templates/telemetryv2_1.11.yaml +++ /dev/null @@ -1,606 +0,0 @@ -{{- if and .Values.telemetry.enabled .Values.telemetry.v2.enabled }} ---- -# Note: http stats filter is wasm enabled only in sidecars. -{{- if .Values.telemetry.v2.prometheus.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.11{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.outboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.outboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.inboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.inboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_inbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.gateway }} - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.gateway | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} ---- -# Note: tcp stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.11{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.inboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.inboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_inbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.outboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.outboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.gateway }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.gateway | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} ---- -{{- end }} -{{- if .Values.telemetry.v2.stackdriver.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stackdriver-filter-1.11{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: -{{- if not .Values.telemetry.v2.stackdriver.disableOutbound }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}"} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } -{{- end }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"disable_server_access_logging": {{ not .Values.telemetry.v2.stackdriver.logging }}, "access_logging": "{{ .Values.telemetry.v2.stackdriver.inboundAccessLogging }}", "disable_host_header_fallback": true} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - 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 - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", "disable_host_header_fallback": true} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stackdriver-filter-1.11{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - {{- if not .Values.telemetry.v2.stackdriver.disableOutbound }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}"} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"disable_server_access_logging": {{ not .Values.telemetry.v2.stackdriver.logging }}, "access_logging": "{{ .Values.telemetry.v2.stackdriver.inboundAccessLogging }}"} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_inbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}"} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } ---- -{{- if .Values.telemetry.v2.accessLogPolicy.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stackdriver-sampling-accesslog-filter-1.11{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "istio.stackdriver" - patch: - operation: INSERT_BEFORE - value: - name: istio.access_log - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "log_window_duration": "{{ .Values.telemetry.v2.accessLogPolicy.logWindowDuration }}" - } - vm_config: - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: "envoy.wasm.access_log_policy" } ---- -{{- end }} -{{- end }} -{{- end }} diff --git a/manifests/charts/istiod-remote/templates/telemetryv2_1.12.yaml b/manifests/charts/istiod-remote/templates/telemetryv2_1.12.yaml deleted file mode 100644 index c2fe6eeb0..000000000 --- a/manifests/charts/istiod-remote/templates/telemetryv2_1.12.yaml +++ /dev/null @@ -1,606 +0,0 @@ -{{- if and .Values.telemetry.enabled .Values.telemetry.v2.enabled }} ---- -# Note: http stats filter is wasm enabled only in sidecars. -{{- if .Values.telemetry.v2.prometheus.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.12{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.outboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.outboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.inboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.inboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_inbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.gateway }} - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.gateway | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} ---- -# Note: tcp stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.12{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.inboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.inboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_inbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.outboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.outboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.gateway }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.gateway | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} ---- -{{- end }} -{{- if .Values.telemetry.v2.stackdriver.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stackdriver-filter-1.12{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: -{{- if not .Values.telemetry.v2.stackdriver.disableOutbound }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}"} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } -{{- end }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"disable_server_access_logging": {{ not .Values.telemetry.v2.stackdriver.logging }}, "access_logging": "{{ .Values.telemetry.v2.stackdriver.inboundAccessLogging }}", "disable_host_header_fallback": true} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - 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 - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", "disable_host_header_fallback": true} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stackdriver-filter-1.12{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - {{- if not .Values.telemetry.v2.stackdriver.disableOutbound }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}"} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"disable_server_access_logging": {{ not .Values.telemetry.v2.stackdriver.logging }}, "access_logging": "{{ .Values.telemetry.v2.stackdriver.inboundAccessLogging }}"} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_inbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - {"access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}"} - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } ---- -{{- if .Values.telemetry.v2.accessLogPolicy.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stackdriver-sampling-accesslog-filter-1.12{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "istio.stackdriver" - patch: - operation: INSERT_BEFORE - value: - name: istio.access_log - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "log_window_duration": "{{ .Values.telemetry.v2.accessLogPolicy.logWindowDuration }}" - } - vm_config: - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: "envoy.wasm.access_log_policy" } ---- -{{- end }} -{{- end }} -{{- end }} diff --git a/manifests/charts/istiod-remote/templates/telemetryv2_1.13.yaml b/manifests/charts/istiod-remote/templates/telemetryv2_1.13.yaml deleted file mode 100644 index c4269fa04..000000000 --- a/manifests/charts/istiod-remote/templates/telemetryv2_1.13.yaml +++ /dev/null @@ -1,628 +0,0 @@ -{{- if and .Values.telemetry.enabled .Values.telemetry.v2.enabled }} ---- -# Note: http stats filter is wasm enabled only in sidecars. -{{- if .Values.telemetry.v2.prometheus.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.13{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.outboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.outboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.inboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.inboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_inbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.gateway }} - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.gateway | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} ---- -# Note: tcp stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.13{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.inboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.inboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_inbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.outboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.outboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.gateway }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.gateway | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} ---- -{{- end }} -{{- if .Values.telemetry.v2.stackdriver.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stackdriver-filter-1.13{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: -{{- if not .Values.telemetry.v2.stackdriver.disableOutbound }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } -{{- end }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "disable_server_access_logging": {{ not .Values.telemetry.v2.stackdriver.logging }}, - "access_logging": "{{ .Values.telemetry.v2.stackdriver.inboundAccessLogging }}", - "disable_host_header_fallback": true, - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - 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 - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "disable_host_header_fallback": true, - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stackdriver-filter-1.13{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - {{- if not .Values.telemetry.v2.stackdriver.disableOutbound }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "disable_server_access_logging": {{ not .Values.telemetry.v2.stackdriver.logging }}, - "access_logging": "{{ .Values.telemetry.v2.stackdriver.inboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_inbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } ---- -{{- if .Values.telemetry.v2.accessLogPolicy.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stackdriver-sampling-accesslog-filter-1.13{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "istio.stackdriver" - patch: - operation: INSERT_BEFORE - value: - name: istio.access_log - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "log_window_duration": "{{ .Values.telemetry.v2.accessLogPolicy.logWindowDuration }}" - } - vm_config: - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: "envoy.wasm.access_log_policy" } ---- -{{- end }} -{{- end }} -{{- end }} diff --git a/manifests/charts/istiod-remote/templates/telemetryv2_1.14.yaml b/manifests/charts/istiod-remote/templates/telemetryv2_1.14.yaml deleted file mode 100644 index d5d4f1bfd..000000000 --- a/manifests/charts/istiod-remote/templates/telemetryv2_1.14.yaml +++ /dev/null @@ -1,628 +0,0 @@ -{{- if and .Values.telemetry.enabled .Values.telemetry.v2.enabled }} ---- -# Note: http stats filter is wasm enabled only in sidecars. -{{- if .Values.telemetry.v2.prometheus.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.14{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.outboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.outboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.inboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.inboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_inbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.gateway }} - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.gateway | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} ---- -# Note: tcp stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.14{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.inboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.inboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_inbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.outboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.outboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.gateway }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.gateway | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} ---- -{{- end }} -{{- if .Values.telemetry.v2.stackdriver.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stackdriver-filter-1.14{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: -{{- if not .Values.telemetry.v2.stackdriver.disableOutbound }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } -{{- end }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "disable_server_access_logging": {{ not .Values.telemetry.v2.stackdriver.logging }}, - "access_logging": "{{ .Values.telemetry.v2.stackdriver.inboundAccessLogging }}", - "disable_host_header_fallback": true, - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - 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 - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "disable_host_header_fallback": true, - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stackdriver-filter-1.14{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - {{- if not .Values.telemetry.v2.stackdriver.disableOutbound }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "disable_server_access_logging": {{ not .Values.telemetry.v2.stackdriver.logging }}, - "access_logging": "{{ .Values.telemetry.v2.stackdriver.inboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_inbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } ---- -{{- if .Values.telemetry.v2.accessLogPolicy.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stackdriver-sampling-accesslog-filter-1.14{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "istio.stackdriver" - patch: - operation: INSERT_BEFORE - value: - name: istio.access_log - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "log_window_duration": "{{ .Values.telemetry.v2.accessLogPolicy.logWindowDuration }}" - } - vm_config: - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: "envoy.wasm.access_log_policy" } ---- -{{- end }} -{{- end }} -{{- end }} diff --git a/manifests/charts/istiod-remote/templates/telemetryv2_1.15.yaml b/manifests/charts/istiod-remote/templates/telemetryv2_1.15.yaml deleted file mode 100644 index bd9d32818..000000000 --- a/manifests/charts/istiod-remote/templates/telemetryv2_1.15.yaml +++ /dev/null @@ -1,628 +0,0 @@ -{{- if and .Values.telemetry.enabled .Values.telemetry.v2.enabled }} ---- -# Note: http stats filter is wasm enabled only in sidecars. -{{- if .Values.telemetry.v2.prometheus.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.15{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.outboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.outboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.inboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.inboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_inbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.gateway }} - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.gateway | indent 18 }} - {{- end }} - vm_config: - vm_id: stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - {{- end }} ---- -# Note: tcp stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.15{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.inboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.inboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_inbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.outboundSidecar }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.outboundSidecar | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.prometheus.configOverride.gateway }} - { - "debug": "false", - "stat_prefix": "istio" - } - {{- else }} - {{ toJson .Values.telemetry.v2.prometheus.configOverride.gateway | indent 18 }} - {{- end }} - vm_config: - vm_id: tcp_stats_outbound - {{- if .Values.telemetry.v2.prometheus.wasmEnabled }} - runtime: envoy.wasm.runtime.v8 - allow_precompiled: true - code: - local: - filename: /etc/istio/extensions/stats-filter.compiled.wasm - {{- else }} - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - {{- end }} ---- -{{- end }} -{{- if .Values.telemetry.v2.stackdriver.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stackdriver-filter-1.15{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: -{{- if not .Values.telemetry.v2.stackdriver.disableOutbound }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } -{{- end }} - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "disable_server_access_logging": {{ not .Values.telemetry.v2.stackdriver.logging }}, - "access_logging": "{{ .Values.telemetry.v2.stackdriver.inboundAccessLogging }}", - "disable_host_header_fallback": true, - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - 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 - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "disable_host_header_fallback": true, - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stackdriver-filter-1.15{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - {{- if not .Values.telemetry.v2.stackdriver.disableOutbound }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } - {{- end }} - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "disable_server_access_logging": {{ not .Values.telemetry.v2.stackdriver.logging }}, - "access_logging": "{{ .Values.telemetry.v2.stackdriver.inboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_inbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stackdriver - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stackdriver_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {{- if not .Values.telemetry.v2.stackdriver.configOverride }} - { - "access_logging": "{{ .Values.telemetry.v2.stackdriver.outboundAccessLogging }}", - "metric_expiry_duration": "3600s" - } - {{- else }} - {{ toJson .Values.telemetry.v2.stackdriver.configOverride | indent 18 }} - {{- end }} - vm_config: - vm_id: stackdriver_outbound - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: envoy.wasm.null.stackdriver } ---- -{{- if .Values.telemetry.v2.accessLogPolicy.enabled }} -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stackdriver-sampling-accesslog-filter-1.15{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - {{- if .Values.meshConfig.rootNamespace }} - namespace: {{ .Values.meshConfig.rootNamespace }} - {{- else }} - namespace: {{ .Release.Namespace }} - {{- end }} - labels: - istio.io/rev: {{ .Values.revision | default "default" }} -spec: - - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "istio.stackdriver" - patch: - operation: INSERT_BEFORE - value: - name: istio.access_log - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "log_window_duration": "{{ .Values.telemetry.v2.accessLogPolicy.logWindowDuration }}" - } - vm_config: - runtime: envoy.wasm.runtime.null - code: - local: { inline_string: "envoy.wasm.access_log_policy" } ---- -{{- end }} -{{- end }} -{{- end }} diff --git a/manifests/charts/istiod-remote/templates/validatingwebhookconfiguration.yaml b/manifests/charts/istiod-remote/templates/validatingwebhookconfiguration.yaml deleted file mode 100644 index 637aba905..000000000 --- a/manifests/charts/istiod-remote/templates/validatingwebhookconfiguration.yaml +++ /dev/null @@ -1,57 +0,0 @@ -{{- if .Values.global.configCluster }} -{{- if .Values.global.configValidation }} -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - name: istio-validator{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}-{{ .Values.global.istioNamespace }} - labels: - app: istiod - release: {{ .Release.Name }} - istio: istiod - istio.io/rev: {{ .Values.revision | default "default" }} -webhooks: - # Webhook handling per-revision validation. Mostly here so we can determine whether webhooks - # are rejecting invalid configs on a per-revision basis. - - name: rev.validation.istio.io - clientConfig: - # Should change from base but cannot for API compat - {{- if .Values.base.validationURL }} - url: {{ .Values.base.validationURL }} - {{- else }} - service: - name: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }} - namespace: {{ .Values.global.istioNamespace }} - path: "/validate" - {{- end }} - rules: - - operations: - - CREATE - - UPDATE - apiGroups: - - security.istio.io - - networking.istio.io - - telemetry.istio.io - - extensions.istio.io - apiVersions: - - "*" - resources: - - "*" - # Fail open until the validation webhook is ready. The webhook controller - # will update this to `Fail` and patch in the `caBundle` when the webhook - # endpoint is ready. - failurePolicy: Ignore - sideEffects: None - admissionReviewVersions: ["v1beta1", "v1"] - objectSelector: - matchExpressions: - - key: istio.io/rev - operator: In - values: - {{- if (eq .Values.revision "") }} - - "default" - {{- else }} - - "{{ .Values.revision }}" - {{- end }} ---- -{{- end }} -{{- end }} diff --git a/manifests/charts/istiod-remote/values.yaml b/manifests/charts/istiod-remote/values.yaml deleted file mode 100644 index a1d24a05a..000000000 --- a/manifests/charts/istiod-remote/values.yaml +++ /dev/null @@ -1,458 +0,0 @@ -#.Values.pilot for discovery and mesh wide config - -## Discovery Settings -pilot: - autoscaleEnabled: true - autoscaleMin: 1 - autoscaleMax: 5 - replicaCount: 1 - rollingMaxSurge: 100% - rollingMaxUnavailable: 25% - hub: "" - tag: "" - # Can be a full hub/image:tag - image: dubbo-pilot - traceSampling: 1.0 - # Resources for a small pilot install - resources: - requests: - cpu: 500m - memory: 2048Mi - env: {} - cpu: - targetAverageUtilization: 80 - # if protocol sniffing is enabled for outbound - enableProtocolSniffingForOutbound: true - # if protocol sniffing is enabled for inbound - enableProtocolSniffingForInbound: true - nodeSelector: {} - podAnnotations: {} - serviceAnnotations: {} - # You can use jwksResolverExtraRootCA to provide a root certificate - # in PEM format. This will then be trusted by pilot when resolving - # JWKS URIs. - jwksResolverExtraRootCA: "" - # This is used to set the source of configuration for - # the associated address in configSource, if nothing is specificed - # the default MCP is assumed. - configSource: - subscribedResources: [] - plugins: [] - # The following is used to limit how long a sidecar can be connected - # to a pilot. It balances out load across pilot instances at the cost of - # increasing system churn. - keepaliveMaxServerConnectionAge: 30m - # Additional labels to apply to the deployment. - deploymentLabels: {} - ## Mesh config settings - - # Install the mesh config map, generated from values.yaml. - # If false, pilot wil use default values (by default) or user-supplied values. - configMap: false - # Additional labels to apply on the pod level for monitoring and logging configuration. - podLabels: {} -sidecarInjectorWebhook: - # You can use the field called alwaysInjectSelector and neverInjectSelector which will always inject the sidecar or - # always skip the injection on pods that match that label selector, regardless of the global policy. - # See https://istio.io/docs/setup/kubernetes/additional-setup/sidecar-injection/#more-control-adding-exceptions - neverInjectSelector: [] - alwaysInjectSelector: [] - # injectedAnnotations are additional annotations that will be added to the pod spec after injection - # This is primarily to support PSP annotations. For example, if you defined a PSP with the annotations: - # - # annotations: - # apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default - # apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default - # - # The PSP controller would add corresponding annotations to the pod spec for each container. However, this happens before - # the inject adds additional containers, so we must specify them explicitly here. With the above example, we could specify: - # injectedAnnotations: - # container.apparmor.security.beta.kubernetes.io/istio-init: runtime/default - # container.apparmor.security.beta.kubernetes.io/istio-proxy: runtime/default - injectedAnnotations: {} - # This enables injection of sidecar in all namespaces, - # with the exception of namespaces with "istio-injection:disabled" annotation - # Only one environment should have this enabled. - enableNamespacesByDefault: false - rewriteAppHTTPProbe: true - # Templates defines a set of custom injection templates that can be used. For example, defining: - # - # templates: - # hello: | - # metadata: - # labels: - # hello: world - # - # Then starting a pod with the `inject.istio.io/templates: hello` annotation, will result in the pod - # being injected with the hello=world labels. - # This is intended for advanced configuration only; most users should use the built in template - templates: {} - # Default templates specifies a set of default templates that are used in sidecar injection. - # By default, a template `sidecar` is always provided, which contains the template of default sidecar. - # To inject other additional templates, define it using the `templates` option, and add it to - # the default templates list. - # For example: - # - # templates: - # hello: | - # metadata: - # labels: - # hello: world - # - # defaultTemplates: ["sidecar", "hello"] - defaultTemplates: [] -istiodRemote: - # Sidecar injector mutating webhook configuration clientConfig.url value. - # For example: https://$remotePilotAddress:15017/inject - # The host should not refer to a service running in the cluster; use a service reference by specifying - # the clientConfig.service field instead. - injectionURL: "" - # Sidecar injector mutating webhook configuration path value for the clientConfig.service field. - # Override to pass env variables, for example: /inject/cluster/remote/net/network2 - injectionPath: "/inject" -telemetry: - enabled: false - v2: - # For Null VM case now. - # This also enables metadata exchange. - enabled: true - metadataExchange: - # Indicates whether to enable WebAssembly runtime for metadata exchange filter. - wasmEnabled: false - # Indicate if prometheus stats filter is enabled or not - prometheus: - enabled: true - # Indicates whether to enable WebAssembly runtime for stats filter. - wasmEnabled: false - # overrides stats EnvoyFilter configuration. - configOverride: - gateway: {} - inboundSidecar: {} - outboundSidecar: {} - # stackdriver filter settings. - stackdriver: - enabled: false - logging: false - monitoring: false - topology: false # deprecated. setting this to true will have no effect, as this option is no longer supported. - disableOutbound: false - # configOverride parts give you the ability to override the low level configuration params passed to envoy filter. - - configOverride: {} - # e.g. - # disable_server_access_logging: false - # disable_host_header_fallback: true - # Access Log Policy Filter Settings. This enables filtering of access logs from stackdriver. - accessLogPolicy: - enabled: false - # To reduce the number of successful logs, default log window duration is - # set to 12 hours. - logWindowDuration: "43200s" -# Revision is set as 'version' label and part of the resource names when installing multiple control planes. -revision: "" -# Revision tags are aliases to Istio control plane revisions -revisionTags: [] -# For Helm compatibility. -ownerName: "" -# meshConfig defines runtime configuration of components, including Istiod and istio-agent behavior -# See https://istio.io/docs/reference/config/istio.mesh.v1alpha1/ for all available options -meshConfig: - enablePrometheusMerge: true - # Config for the default ProxyConfig. - # Initially using directly the proxy metadata - can also be activated using annotations - # on the pod. This is an unsupported low-level API, pending review and decisions on - # enabling the feature. Enabling the DNS listener is safe - and allows further testing - # and gradual adoption by setting capture only on specific workloads. It also allows - # VMs to use other DNS options, like dnsmasq or unbound. - - # The namespace to treat as the administrative root namespace for Istio configuration. - # When processing a leaf namespace Istio will search for declarations in that namespace first - # and if none are found it will search in the root namespace. Any matching declaration found in the root namespace - # is processed as if it were declared in the leaf namespace. - rootNamespace: - # The trust domain corresponds to the trust root of a system - # Refer to https://github.com/spiffe/spiffe/blob/master/standards/SPIFFE-ID.md#21-trust-domain - trustDomain: "cluster.local" - # TODO: the intent is to eventually have this enabled by default when security is used. - # It is not clear if user should normally need to configure - the metadata is typically - # used as an escape and to control testing and rollout, but it is not intended as a long-term - # stable API. - - # What we may configure in mesh config is the ".global" - and use of other suffixes. - # No hurry to do this in 1.6, we're trying to prove the code. -global: - # Used to locate istiod. - istioNamespace: dubbo-system - # enable pod disruption budget for the control plane, which is used to - # ensure Istio control plane components are gradually upgraded or recovered. - defaultPodDisruptionBudget: - enabled: true - # The values aren't mutable due to a current PodDisruptionBudget limitation - # minAvailable: 1 - # A minimal set of requested resources to applied to all deployments so that - # Horizontal Pod Autoscaler will be able to function (if set). - # Each component can overwrite these default values by adding its own resources - # block in the relevant section below and setting the desired resources values. - defaultResources: - requests: - cpu: 10m - # memory: 128Mi - # limits: - # cpu: 100m - # memory: 128Mi - # Default hub for Istio images. - # Releases are published to docker hub under 'istio' project. - # Dev builds from prow are on gcr.io - hub: apache - # Default tag for Istio images. - tag: latest - # Specify image pull policy if default behavior isn't desired. - # Default behavior: latest images will be Always else IfNotPresent. - imagePullPolicy: "" - # ImagePullSecrets for all ServiceAccount, list of secrets in the same namespace - # to use for pulling any images in pods that reference this ServiceAccount. - # For components that don't use ServiceAccounts (i.e. grafana, servicegraph, tracing) - # ImagePullSecrets will be added to the corresponding Deployment(StatefulSet) objects. - # Must be set for any cluster configured with private docker registry. - imagePullSecrets: [] - # - private-registry-key - - # Enabled by default in master for maximising testing. - istiod: - enableAnalysis: false - # To output all istio components logs in json format by adding --log_as_json argument to each container argument - logAsJson: false - # Comma-separated minimum per-scope logging level of messages to output, in the form of :,: - # The control plane has different scopes depending on component, but can configure default log level across all components - # If empty, default scope and level will be used as configured in code - logging: - level: "default:info" - omitSidecarInjectorConfigMap: true - # Whether to restrict the applications namespace the controller manages; - # If not set, controller watches all namespaces - oneNamespace: false - # Configure whether Operator manages webhook configurations. The current behavior - # of Istiod is to manage its own webhook configurations. - # When this option is set as true, Istio Operator, instead of webhooks, manages the - # webhook configurations. When this option is set as false, webhooks manage their - # own webhook configurations. - operatorManageWebhooks: false - # Custom DNS config for the pod to resolve names of services in other - # clusters. Use this to add additional search domains, and other settings. - # see - # https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#dns-config - # This does not apply to gateway pods as they typically need a different - # set of DNS settings than the normal application pods (e.g., in - # multicluster scenarios). - # NOTE: If using templates, follow the pattern in the commented example below. - #podDNSSearchNamespaces: - #- global - #- "{{ valueOrDefault .DeploymentMeta.Namespace \"default\" }}.global" - - # Kubernetes >=v1.11.0 will create two PriorityClass, including system-cluster-critical and - # system-node-critical, it is better to configure this in order to make sure your Istio pods - # will not be killed because of low priority class. - # Refer to https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass - # for more detail. - priorityClassName: "" - proxy: - image: dubbo-agent - # This controls the 'policy' in the sidecar injector. - autoInject: enabled - # CAUTION: It is important to ensure that all Istio helm charts specify the same clusterDomain value - # cluster domain. Default value is "cluster.local". - clusterDomain: "cluster.local" - # Per Component log level for proxy, applies to gateways and sidecars. If a component level is - # not set, then the global "logLevel" will be used. - componentLogLevel: "misc:error" - # If set, newly injected sidecars will have core dumps enabled. - enableCoreDump: false - # istio ingress capture allowlist - # examples: - # Redirect only selected ports: --includeInboundPorts="80,8080" - excludeInboundPorts: "" - includeInboundPorts: "*" - # istio egress capture allowlist - # https://istio.io/docs/tasks/traffic-management/egress.html#calling-external-services-directly - # example: includeIPRanges: "172.30.0.0/16,172.20.0.0/16" - # would only capture egress traffic on those two IP Ranges, all other outbound traffic would - # be allowed by the sidecar - includeIPRanges: "*" - excludeIPRanges: "" - includeOutboundPorts: "" - excludeOutboundPorts: "" - # Log level for proxy, applies to gateways and sidecars. - # Expected values are: trace|debug|info|warning|error|critical|off - logLevel: warning - #If set to true, istio-proxy container will have privileged securityContext - privileged: false - # The number of successive failed probes before indicating readiness failure. - readinessFailureThreshold: 30 - # The initial delay for readiness probes in seconds. - readinessInitialDelaySeconds: 1 - # The period between readiness probes. - readinessPeriodSeconds: 2 - # Resources for the sidecar. - resources: - requests: - cpu: 100m - memory: 128Mi - limits: - cpu: 2000m - memory: 1024Mi - # Default port for Pilot agent health checks. A value of 0 will disable health checking. - statusPort: 15020 - # Specify which tracer to use. One of: zipkin, lightstep, datadog, stackdriver. - # If using stackdriver tracer outside GCP, set env GOOGLE_APPLICATION_CREDENTIALS to the GCP credential file. - tracer: "zipkin" - # Controls if sidecar is injected at the front of the container list and blocks the start of the other containers until the proxy is ready - holdApplicationUntilProxyStarts: false - proxy_init: - # Base name for the proxy_init container, used to configure iptables. - image: dubbo-agent - resources: - limits: - cpu: 2000m - memory: 1024Mi - requests: - cpu: 10m - memory: 10Mi - # configure remote pilot and istiod service and endpoint - remotePilotAddress: "" - ############################################################################################## - # The following values are found in other charts. To effectively modify these values, make # - # make sure they are consistent across your Istio helm charts # - ############################################################################################## - - # The customized CA address to retrieve certificates for the pods in the cluster. - # CSR clients such as the Istio Agent and ingress gateways can use this to specify the CA endpoint. - # If not set explicitly, default to the Istio discovery address. - caAddress: "" - # Configure a remote cluster data plane controlled by an external istiod. - # When set to true, istiod is not deployed locally and only a subset of the other - # discovery charts are enabled. - externalIstiod: true - # Configure a remote cluster as the config cluster for an external istiod. - configCluster: false - # Configure the policy for validating JWT. - # Currently, two options are supported: "third-party-jwt" and "first-party-jwt". - jwtPolicy: "third-party-jwt" - # Mesh ID means Mesh Identifier. It should be unique within the scope where - # meshes will interact with each other, but it is not required to be - # globally/universally unique. For example, if any of the following are true, - # then two meshes must have different Mesh IDs: - # - Meshes will have their telemetry aggregated in one place - # - Meshes will be federated together - # - Policy will be written referencing one mesh from the other - # - # If an administrator expects that any of these conditions may become true in - # the future, they should ensure their meshes have different Mesh IDs - # assigned. - # - # Within a multicluster mesh, each cluster must be (manually or auto) - # configured to have the same Mesh ID value. If an existing cluster 'joins' a - # multicluster mesh, it will need to be migrated to the new mesh ID. Details - # of migration TBD, and it may be a disruptive operation to change the Mesh - # ID post-install. - # - # If the mesh admin does not specify a value, Istio will use the value of the - # mesh's Trust Domain. The best practice is to select a proper Trust Domain - # value. - meshID: "" - # Configure the mesh networks to be used by the Split Horizon EDS. - # - # The following example defines two networks with different endpoints association methods. - # For `network1` all endpoints that their IP belongs to the provided CIDR range will be - # mapped to network1. The gateway for this network example is specified by its public IP - # address and port. - # The second network, `network2`, in this example is defined differently with all endpoints - # retrieved through the specified Multi-Cluster registry being mapped to network2. The - # gateway is also defined differently with the name of the gateway service on the remote - # cluster. The public IP for the gateway will be determined from that remote service (only - # LoadBalancer gateway service type is currently supported, for a NodePort type gateway service, - # it still need to be configured manually). - # - # meshNetworks: - # network1: - # endpoints: - # - fromCidr: "192.168.0.1/24" - # gateways: - # - address: 1.1.1.1 - # port: 80 - # network2: - # endpoints: - # - fromRegistry: reg1 - # gateways: - # - registryServiceName: istio-ingressgateway.dubbo-system.svc.cluster.local - # port: 443 - # - meshNetworks: {} - # Use the user-specified, secret volume mounted key and certs for Pilot and workloads. - mountMtlsCerts: false - multiCluster: - # Set to true to connect two kubernetes clusters via their respective - # ingressgateway services when pods in each cluster cannot directly - # talk to one another. All clusters should be using Istio mTLS and must - # have a shared root CA for this model to work. - enabled: false - # Should be set to the name of the cluster this installation will run in. This is required for sidecar injection - # to properly label proxies - clusterName: "" - # Network defines the network this cluster belong to. This name - # corresponds to the networks in the map of mesh networks. - network: "" - # Configure the certificate provider for control plane communication. - # Currently, two providers are supported: "kubernetes" and "istiod". - # As some platforms may not have kubernetes signing APIs, - # Istiod is the default - pilotCertProvider: istiod - sds: - # The JWT token for SDS and the aud field of such JWT. See RFC 7519, section 4.1.3. - # When a CSR is sent from Istio Agent to the CA (e.g. Istiod), this aud is to make sure the - # JWT is intended for the CA. - token: - aud: istio-ca - sts: - # The service port used by Security Token Service (STS) server to handle token exchange requests. - # Setting this port to a non-zero value enables STS server. - servicePort: 0 - # Configuration for each of the supported tracers - tracer: - # Configuration for envoy to send trace data to LightStep. - # Disabled by default. - # address: the : of the satellite pool - # accessToken: required for sending data to the pool - # - datadog: - # Host:Port for submitting traces to the Datadog agent. - address: "$(HOST_IP):8126" - lightstep: - address: "" # example: lightstep-satellite:443 - accessToken: "" # example: abcdefg1234567 - stackdriver: - # enables trace output to stdout. - debug: false - # The global default max number of message events per span. - maxNumberOfMessageEvents: 200 - # The global default max number of annotation events per span. - maxNumberOfAnnotations: 200 - # The global default max number of attributes per span. - maxNumberOfAttributes: 200 - zipkin: - # Host:Port for reporting trace data in zipkin format. If not specified, will default to - # zipkin service (port 9411) in the same namespace as the other istio components. - address: "" - # Use the Mesh Control Protocol (MCP) for configuring Istiod. Requires an MCP source. - useMCP: false - # The name of the CA for workload certificates. - # For example, when caName=GkeWorkloadCertificate, GKE workload certificates - # will be used as the certificates for workloads. - # The default value is "" and when caName="", the CA will be configured by other - # mechanisms (e.g., environmental variable CA_PROVIDER). - caName: "" - # whether to use autoscaling/v2 template for HPA settings - # for internal usage only, not to be configured by users. - autoscalingv2API: true -base: - # For istioctl usage to disable istio config crds in base - enableIstioConfigCRDs: true diff --git a/manifests/examples/customresource/istio_v1alpha1_istiooperator_cr.yaml b/manifests/examples/customresource/istio_v1alpha1_istiooperator_cr.yaml deleted file mode 100644 index 124d19d43..000000000 --- a/manifests/examples/customresource/istio_v1alpha1_istiooperator_cr.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -metadata: - namespace: dubbo-system - name: example-istiocontrolplane -spec: - profile: demo -... diff --git a/manifests/examples/user-gateway/ingress-gateway-only.yaml b/manifests/examples/user-gateway/ingress-gateway-only.yaml deleted file mode 100644 index c37e85b01..000000000 --- a/manifests/examples/user-gateway/ingress-gateway-only.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: empty - components: - ingressGateways: - - enabled: true - namespace: my-namespace diff --git a/manifests/manifest.go b/manifests/manifest.go deleted file mode 100644 index e225839f1..000000000 --- a/manifests/manifest.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package manifests - -import ( - "embed" - "io/fs" - "os" -) - -// FS embeds the manifests -// -//go:embed charts/* profiles/* -//go:embed charts/gateways/istio-egress/templates/_affinity.tpl -//go:embed charts/gateways/istio-ingress/templates/_affinity.tpl -var FS embed.FS - -// BuiltinOrDir returns a FS for the provided directory. If no directory is passed, the compiled in -// FS will be used -func BuiltinOrDir(dir string) fs.FS { - if dir == "" { - return FS - } - return os.DirFS(dir) -} diff --git a/manifests/profiles/default.yaml b/manifests/profiles/default.yaml deleted file mode 100644 index 63a691639..000000000 --- a/manifests/profiles/default.yaml +++ /dev/null @@ -1,184 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -metadata: - namespace: dubbo-system -spec: - hub: apache - tag: latest - - # You may override parts of meshconfig by uncommenting the following lines. - meshConfig: - defaultConfig: - proxyMetadata: {} - enablePrometheusMerge: true - # Opt-out of global http2 upgrades. - # Destination rule is used to opt-in. - # h2_upgrade_policy: DO_NOT_UPGRADE - - # Traffic management feature - components: - base: - enabled: true - pilot: - enabled: true - - # Istio Gateway feature - ingressGateways: - - name: istio-ingressgateway - enabled: false - egressGateways: - - name: istio-egressgateway - enabled: false - - # Istio CNI feature - cni: - enabled: false - - # Remote and config cluster configuration for an external istiod - istiodRemote: - enabled: false - - # Global values passed through to helm global.yaml. - # Please keep this in sync with manifests/charts/global.yaml - values: - defaultRevision: "" - global: - istioNamespace: dubbo-system - istiod: - enableAnalysis: false - logging: - level: "default:info" - logAsJson: false - pilotCertProvider: istiod - jwtPolicy: third-party-jwt - proxy: - image: dubbo-agent - clusterDomain: "cluster.local" - resources: - requests: - cpu: 100m - memory: 128Mi - limits: - cpu: 2000m - memory: 1024Mi - logLevel: warning - componentLogLevel: "misc:error" - privileged: false - enableCoreDump: false - statusPort: 15020 - readinessInitialDelaySeconds: 1 - readinessPeriodSeconds: 2 - readinessFailureThreshold: 30 - includeIPRanges: "*" - excludeIPRanges: "" - excludeOutboundPorts: "" - excludeInboundPorts: "" - autoInject: enabled - tracer: "zipkin" - proxy_init: - image: dubbo-agent - resources: - limits: - cpu: 2000m - memory: 1024Mi - requests: - cpu: 10m - memory: 10Mi - # Specify image pull policy if default behavior isn't desired. - # Default behavior: latest images will be Always else IfNotPresent. - imagePullPolicy: "" - operatorManageWebhooks: false - tracer: - lightstep: {} - zipkin: {} - datadog: {} - stackdriver: {} - imagePullSecrets: [] - oneNamespace: false - defaultNodeSelector: {} - configValidation: true - multiCluster: - enabled: false - clusterName: "" - omitSidecarInjectorConfigMap: false - network: "" - defaultResources: - requests: - cpu: 10m - defaultPodDisruptionBudget: - enabled: true - priorityClassName: "" - useMCP: false - sds: - token: - aud: istio-ca - sts: - servicePort: 0 - meshNetworks: {} - mountMtlsCerts: false - base: - enableCRDTemplates: false - validationURL: "" - pilot: - autoscaleEnabled: true - autoscaleMin: 1 - autoscaleMax: 5 - replicaCount: 1 - image: dubbo-pilot - traceSampling: 1.0 - env: {} - cpu: - targetAverageUtilization: 80 - nodeSelector: {} - keepaliveMaxServerConnectionAge: 30m - enableProtocolSniffingForOutbound: true - enableProtocolSniffingForInbound: true - deploymentLabels: - podLabels: {} - configMap: true - - telemetry: - enabled: true - v2: - enabled: true - metadataExchange: - wasmEnabled: false - prometheus: - wasmEnabled: false - enabled: true - stackdriver: - enabled: false - logging: false - monitoring: false - topology: false - configOverride: {} - - istiodRemote: - injectionURL: "" - - gateways: - istio-egressgateway: - env: {} - autoscaleEnabled: true - type: ClusterIP - name: istio-egressgateway - secretVolumes: - - name: egressgateway-certs - secretName: istio-egressgateway-certs - mountPath: /etc/istio/egressgateway-certs - - name: egressgateway-ca-certs - secretName: istio-egressgateway-ca-certs - mountPath: /etc/istio/egressgateway-ca-certs - - istio-ingressgateway: - autoscaleEnabled: true - type: LoadBalancer - name: istio-ingressgateway - env: {} - secretVolumes: - - name: ingressgateway-certs - secretName: istio-ingressgateway-certs - mountPath: /etc/istio/ingressgateway-certs - - name: ingressgateway-ca-certs - secretName: istio-ingressgateway-ca-certs - mountPath: /etc/istio/ingressgateway-ca-certs diff --git a/manifests/profiles/demo.yaml b/manifests/profiles/demo.yaml deleted file mode 100644 index ae47ad255..000000000 --- a/manifests/profiles/demo.yaml +++ /dev/null @@ -1,77 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - meshConfig: - accessLogFile: /dev/stdout - extensionProviders: - - name: otel - envoyOtelAls: - service: otel-collector.dubbo-system.svc.cluster.local - port: 4317 - components: - egressGateways: - - name: istio-egressgateway - enabled: true - k8s: - resources: - requests: - cpu: 10m - memory: 40Mi - - ingressGateways: - - name: istio-ingressgateway - enabled: true - k8s: - resources: - requests: - cpu: 10m - memory: 40Mi - service: - ports: - ## You can add custom gateway ports in user values overrides, but it must include those ports since helm replaces. - # Note that AWS ELB will by default perform health checks on the first port - # on this list. Setting this to the health check port will ensure that health - # checks always work. https://github.com/istio/istio/issues/12503 - - port: 15021 - targetPort: 15021 - name: status-port - - port: 80 - targetPort: 8080 - name: http2 - - port: 443 - targetPort: 8443 - name: https - - port: 31400 - targetPort: 31400 - name: tcp - # This is the port where sni routing happens - - port: 15443 - targetPort: 15443 - name: tls - - pilot: - k8s: - env: - - name: PILOT_TRACE_SAMPLING - value: "100" - resources: - requests: - cpu: 10m - memory: 100Mi - - values: - global: - proxy: - resources: - requests: - cpu: 10m - memory: 40Mi - - pilot: - autoscaleEnabled: false - - gateways: - istio-egressgateway: - autoscaleEnabled: false - istio-ingressgateway: - autoscaleEnabled: false diff --git a/manifests/profiles/empty.yaml b/manifests/profiles/empty.yaml deleted file mode 100644 index 07de5b1e0..000000000 --- a/manifests/profiles/empty.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# The empty profile has everything disabled -# This is useful as a base for custom user configuration -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - components: - base: - enabled: false - pilot: - enabled: false - ingressGateways: - - name: istio-ingressgateway - enabled: false diff --git a/manifests/profiles/external.yaml b/manifests/profiles/external.yaml deleted file mode 100644 index 00c951680..000000000 --- a/manifests/profiles/external.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# The external profile is used to configure a mesh using an external control plane. -# Only the injector mutating webhook configuration is installed. -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - components: - base: - enabled: false - pilot: - enabled: false - ingressGateways: - - name: istio-ingressgateway - enabled: false - istiodRemote: - enabled: true - values: - global: - externalIstiod: true - omitSidecarInjectorConfigMap: true - configCluster: false - pilot: - configMap: false - telemetry: - enabled: false diff --git a/manifests/profiles/minimal.yaml b/manifests/profiles/minimal.yaml deleted file mode 100644 index 075881ee0..000000000 --- a/manifests/profiles/minimal.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# The minimal profile will install just the core control plane -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - components: - ingressGateways: - - name: istio-ingressgateway - enabled: false diff --git a/manifests/profiles/openshift.yaml b/manifests/profiles/openshift.yaml deleted file mode 100644 index f5665e787..000000000 --- a/manifests/profiles/openshift.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - components: - cni: - enabled: true - namespace: kube-system - values: - cni: - cniBinDir: /var/lib/cni/bin - cniConfDir: /etc/cni/multus/net.d - chained: false - cniConfFileName: "istio-cni.conf" - excludeNamespaces: - - dubbo-system - - kube-system - logLevel: info - privileged: true - sidecarInjectorWebhook: - injectedAnnotations: - k8s.v1.cni.cncf.io/networks: istio-cni diff --git a/manifests/profiles/preview.yaml b/manifests/profiles/preview.yaml deleted file mode 100644 index e0d9b636a..000000000 --- a/manifests/profiles/preview.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# The preview profile contains features that are experimental. -# This is intended to explore new features coming to Istio. -# Stability, security, and performance are not guaranteed - use at your own risk. -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - meshConfig: - defaultConfig: - proxyMetadata: - # Enable Istio agent to handle DNS requests for known hosts - # Unknown hosts will automatically be resolved using upstream dns servers in resolv.conf - ISTIO_META_DNS_CAPTURE: "true" - # Enable dynamic bootstrap generation. - BOOTSTRAP_XDS_AGENT: "true" - values: - telemetry: - v2: - metadataExchange: - wasmEnabled: true - prometheus: - wasmEnabled: true diff --git a/manifests/profiles/remote.yaml b/manifests/profiles/remote.yaml deleted file mode 100644 index dbbc49aea..000000000 --- a/manifests/profiles/remote.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Deprecated. Use the `default` profile instead. -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: {} diff --git a/operator/ARCHITECTURE.md b/operator/ARCHITECTURE.md deleted file mode 100644 index 372237155..000000000 --- a/operator/ARCHITECTURE.md +++ /dev/null @@ -1,249 +0,0 @@ -# Istio operator code overview - -## Introduction - -This document covers primarily the code, with some background on how the design maps to it. -See the -[design doc](https://docs.google.com/document/d/11j9ZtYWNWnxQYnZy8ayZav1FMwTH6F6z6fkDYZ7V298/edit#heading=h.qex63c29z2to) -for a more complete design description. The operator code is divided roughly into five areas: - -1. [IstioOperatorSpec API](#istiooperatorspec-api) and related infrastructure, which is expressed as a -[proto](https://github.com/istio/api/blob/master/operator/v1alpha1/operator.proto) and -compiled to [Go -structs](https://github.com/istio/api/blob/master/operator/v1alpha1/operator.pb.go). -`IstioOperatorSpec` has pass-through fields to the Helm values.yaml API, but these are additionally validated through -a [schema](pkg/apis/istio/v1alpha1/values_types.proto). -1. [Controller](#k8s-controller) code. The code comprises the K8s listener, webhook and logic for reconciling the cluster -to an `IstioOperatorSpec` CR. -1. [Manifest creation](#manifest-creation) code. User settings are overlaid on top of the -selected profile values and passed to a renderer in the Helm library to create manifests. Further customization on the -created manifests can be done through overlays. -1. [CLI](#cli) code. CLI code shares the `IstioOperatorSpec` API with -the controller, but allows manifests to be generated and optionally applied from the command line without the need to -run a privileged controller in the cluster. -1. [Migration tools](#migration-tools). The migration tools are intended to -automate configuration migration from Helm to the operator. - -The operator code uses the new Helm charts in the [istio/manifests/charts](../manifests/charts/istio-operator). It is not -compatible with the older charts in [istio/istio](https://github.com/istio/istio/tree/1.4.7/install/kubernetes/helm). -See `istio/manifests/charts` for details about the new charts and why they were created. Briefly, the new charts -are intended to support production ready deployments of Istio that follow best practices like canarying for upgrade. - -## Terminology - -Throughout the document, the following terms are used: - -- `IstioOperatorSpec`: The API directly defined in the -[IstioOperatorSpec proto](https://github.com/istio/api/blob/master/operator/v1alpha1/operator.proto), -including feature and component groupings, namespaces and enablement, and per-component K8s settings. -- Helm values.yaml API, implicitly defined through the various values.yaml files in the -[istio/manifests/charts](../manifests/charts/istio-operator) and schematized in the operator through -[values_types.proto](pkg/apis/istio/v1alpha1/values_types.proto). - -## IstioOperatorSpec API - -The `IstioOperatorSpec` API is intended to replace the installation and K8s parts of Helm values.yaml. - -### Features and components - -The operator has a very similar structure to istio/installer: components are grouped into features. -`IstioOperatorSpec` defines functional settings at the feature level. Functional settings are those that performs some -function in the Istio control plane without necessarily being tied to any one component that runs in a Deployment. -Component settings are those that necessarily refer to a particular Deployment or Service. For example, the number -of Pilot replicas is a component setting, because it refers to a component which is a Deployment in the -cluster. Most K8s platform settings are necessarily component settings. -The available features and the components that comprise each feature are as follows: - -| Feature | Components | -|---------|------------| -CRDs, and other cluster wide configs | Base -Traffic Management | Pilot -Security | Pilot -Configuration management | Pilot -AutoInjection | Pilot -Gateways | Ingress gateway -Gateways | Egress gateway -Policy | Policy (deprecated) -Telemetry | Telemetry (deprecated) - -Features and components are defined in the -[name](https://github.com/istio/operator/blob/e9097258cb4fbe59648e7da663cdad6f16927b8f/pkg/name/name.go#L44) package. - -Note: Besides the features and the components listed in the table above, some addon features and components are as follows: - -| Feature | Components | -|---------|------------| -Telemetry | Prometheus -Telemetry | Prometheus Operator -Telemetry | Grafana -Telemetry | Kiali -Telemetry | Tracing -ThirdParty | CNI - -### Namespaces - -The `IstioOperatorSpec` API and underlying new Helm charts offer a lot of flexibility in which namespaces features and -components are installed into. Namespace definitions can be defined and specialized at the global, feature and component -level, with each lower level overriding the setting of the higher parent level. For example, if the global default -namespace is defined as: - -```yaml -defaultNamespace: dubbo-system -``` - -and namespaces are specialized for the gateway feature and its components: - -```yaml -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -metadata: - namespace: istio-operator -spec: - components: - ingressGateways: - - name: istio-ingressgateway - enabled: true - namespace: istio-gateways -``` - -the resulting namespaces will be: - -| Component | Namespace | -| --------- | :-------- | -ingressGateways | istio-gateways - -These rules are expressed in code in the -[name](https://github.com/istio/operator/blob/e9097258cb4fbe59648e7da663cdad6f16927b8f/pkg/name/name.go#L246) package. - -### Enablement - -Features and components can be individually or collectively enabled or disabled. If a feature is disabled, all of its -components are disabled, regardless of their component-level enablement. If a feature is enabled, all of its components -are enabled, unless they are individually disabled. For example: - -```yaml - telemetry: - enabled: true - v2: - enabled: false -``` - -These rules are expressed in code in the -[name](https://github.com/istio/operator/blob/e9097258cb4fbe59648e7da663cdad6f16927b8f/pkg/name/name.go#L131) package. - -### K8s settings - -Rather than defining selective mappings from parameters to fields in K8s resources, the `IstioOperatorSpec` API -contains a consistent K8s block for each Istio component. The available K8s settings are defined in -[KubernetesResourcesSpec](https://github.com/istio/api/blob/7791470ecc4c5e123589ff2b781f47b1bcae6ddd/mesh/v1alpha1/component.proto#L103): - -| Field name | K8s API reference | -| :--------- | :---------------- | -resources | [resources](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container) -readinessProbe | [readiness probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/) -replicaCount | [replica count](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) -hpaSpec | [HorizontalPodAutoscaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) -podDisruptionBudget | [PodDisruptionBudget](https://kubernetes.io/docs/concepts/workloads/pods/disruptions/#how-disruption-budgets-work) -podAnnotations | [pod annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) -env | [container environment variables](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) -imagePullPolicy| [ImagePullPolicy](https://kubernetes.io/docs/concepts/containers/images/) -priorityClassName | [priority class name](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass) -nodeSelector| [node selector](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) -affinity | [affinity and anti-affinity](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) -serviceAnnotations | [service annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) -securityContext | [security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) - -These K8s setting are available for each component under the `k8s` field, for example: - -```yaml -trafficManagement: - components: - pilot: - k8s: - hpaSpec: - # HPA spec, as defined in K8s API -``` - -### Translations - -API translations are version specific and are expressed as a -[table of Translators](https://github.com/istio/operator/blob/e9097258cb4fbe59648e7da663cdad6f16927b8f/pkg/translate/translate.go#L110) -indexed by minor [version](pkg/version/version.go). This is because -mapping rules are only allowed to change between minor (not patch) versions. - -The `IstioOperatorSpec` API fields are translated to the output manifest in two ways: - -1. The `IstioOperatorSpec` API fields are mapped to the Helm values.yaml schema using the -[APIMapping](https://github.com/istio/operator/blob/e9097258cb4fbe59648e7da663cdad6f16927b8f/pkg/translate/translate.go#L112) -field of the [Translator](https://github.com/istio/operator/blob/e9097258cb4fbe59648e7da663cdad6f16927b8f/pkg/translate/translate.go#L52) -struct. -1. The K8s settings are applied to resources in the output manifest using the -[KubernetesMapping](https://github.com/istio/operator/blob/e9097258cb4fbe59648e7da663cdad6f16927b8f/pkg/translate/translate.go#L132) -field in the [Translator](https://github.com/istio/operator/blob/e9097258cb4fbe59648e7da663cdad6f16927b8f/pkg/translate/translate.go#L52) -struct. - -Other per-component mappings to Helm values.yaml are expressed in the -[ComponentMaps](https://github.com/istio/operator/blob/e9097258cb4fbe59648e7da663cdad6f16927b8f/pkg/translate/translate.go#L83) -struct. - -### Validations - -Both the `IstioOperatorSpec` and Helm APIs are validated. The `IstioOperatorSpec` API is validated through a -table of validation rules in -[pkg/validate/validate.go](pkg/validate/validate.go). These rules -refer to the Go struct path schema and hence have names with a capitalized first letter. -The Helm values.yaml API is validated in -[validate_values.go](pkg/validate/validate_values.go) -and refer to the values.yaml data paths. Hence, these rules have names with a lower case first letter. -Apart from validating the correctness of individual fields, the operator ensure that relationships between values in -different parts of the configuration tree are correct. For example, it's an error to enable a component while its -parent feature is disabled. - -## K8s controller - -TODO(rcernich). - -## Manifest creation - -Manifest rendering is a multi-step process, shown in the figure below. ![rendering -process](images/operator_render_flow.svg) The example in the figure shows the rendering being triggered by a CLI `mesh` -command with a `IstioOperatorSpec` CR passed to it from a file; however, the same rendering steps would occur when an -in-cluster CR is updated and the controller acts upon it to generate a new manifest to apply to the cluster. Note that -both the charts and configuration profiles can come from three different sources: compiled-in, local filesystem, or URL -(TODO(mostrowski): describe the remote URL functionality). -The source may be selected independently for the charts and profiles. The different steps in creating the manifest are -as follows: - -1. The user CR (my_custom.yaml) selects a configuration profile. If no profile is selected, the -[default profile](../manifests/profiles/default.yaml) is used. Each profile is defined as a -set of defaults for `IstioOperatorSpec`, for both the restructured fields (K8s settings, namespaces and enablement) -and the Helm values (Istio behavior configuration). - -1. The fields defined in the user CR override any values defined in the configuration profile CR. The -resulting CR is converted to Helm values.yaml format and passed to the next step. -1. Part of the configuration profile contains settings in the Helm values.yaml schema format. User overrides of -these fields are applied and merged with the output of this step. The result of this step is a merge of configuration -profile defaults and user overlays, all expressed in Helm values.yaml format. This final values.yaml configuration -is passed to the Helm rendering library and used to render the charts. The rendered manifests are passed to the next -step. -1. Overlays in the user CR are applied to the rendered manifests. No values are ever defined in configuration profile -CRs at this layer, so no merge is performed in this step. - -## CLI - -The CLI `mesh` command is implemented in the [cmd/mesh](cmd/mesh/) -subdirectory as a Cobra command with the following subcommands: - -- [manifest](cmd/mesh/manifest.go): the manifest subcommand is used to generate, install, diff or migrate Istio manifests, it has the following subcommands: - - [install](cmd/mesh/install.go): the install subcommand is used to generate an Istio install manifest and apply it to a cluster. - - [diff](cmd/mesh/manifest-diff.go): the diff subcommand is used to compare manifest from two files or directories. - - [generate](cmd/mesh/manifest-generate.go): the generate subcommand is used to generate an Istio install manifest. -- [profile](cmd/mesh/profile.go): dumps the default values for a selected profile, it has the following subcommands: - - [diff](cmd/mesh/profile-diff.go): the diff subcommand is used to display the difference between two Istio configuration profiles. - - [dump](cmd/mesh/profile-dump.go): the dump subcommand is used to dump the values in an Istio configuration profile. - - [list](cmd/mesh/profile-list.go): the list subcommand is used to list available Istio configuration profiles. -- [upgrade](cmd/mesh/upgrade.go): performs an in-place upgrade of the Istio control plane with eligibility checks. - -## Migration tools - -TODO(richardwxn). diff --git a/operator/README.md b/operator/README.md deleted file mode 100644 index 2e9bfdeb8..000000000 --- a/operator/README.md +++ /dev/null @@ -1,424 +0,0 @@ -[![Go Report Card](https://goreportcard.com/badge/github.com/istio/operator)](https://goreportcard.com/report/github.com/istio/operator) - -# Istio Operator - -The istio/operator repo is part of istio/istio from 1.5 onwards. -You can [contribute](../CONTRIBUTING.md) by picking an -[unassigned open issue](https://github.com/istio/istio/issues?q=is%3Aissue+is%3Aopen+label%3Aarea%2Fenvironments%2Foperator+no%3Aassignee), -creating a [bug or feature request](../BUGS-AND-FEATURE-REQUESTS.md), -or just coming to the weekly [Environments Working Group](https://github.com/istio/community/blob/master/WORKING-GROUPS.md) -meeting to share your ideas. - -This document is an overview of how the operator works from a user perspective. For more details about the design and -architecture and a code overview, see [ARCHITECTURE.md](./ARCHITECTURE.md). - -## Introduction - -The operator uses the [IstioOperator API](https://github.com/istio/api/blob/master/operator/v1alpha1/operator.proto), which has -three main components: - -- [MeshConfig](https://github.com/istio/api/blob/master/mesh/v1alpha1/config.proto) for runtime config consumed directly by Istio -control plane components. -- [Component configuration API](https://github.com/istio/api/blob/00671adacbea20f941cb20cce021bc63cbad1840/operator/v1alpha1/operator.proto#L42-L93), for managing -K8s settings like resources, auto scaling, pod disruption budgets and others defined in the -[KubernetesResourceSpec](https://github.com/istio/api/blob/00671adacbea20f941cb20cce021bc63cbad1840/operator/v1alpha1/operator.proto#L217-L271) -for Istio core and addon components. -- The legacy -[Helm installation API](https://github.com/istio/istio/blob/master/operator/pkg/apis/istio/v1alpha1/values_types.proto) for backwards -compatibility. - -Some parameters will temporarily exist both the component configuration and legacy Helm APIs - for example, K8s -resources. However, the Istio community recommends using the first API as it is more consistent, is validated, -and will naturally follow the graduation process for APIs while the same parameters in the configuration API are planned -for deprecation. - -[Profiles](https://istio.io/docs/setup/kubernetes/additional-setup/config-profiles/), are provided as a starting point for -an Istio install and can be customized by creating customization overlay files or passing parameters through the ---set flag. For example, to select the minimal profile: - -```yaml -# minimal.yaml - -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: minimal -``` - -If you don't specify a configuration profile, Istio is installed using the `default` configuration profile. All -profiles listed in istio.io are available by default, or `profile:` can point to a local file path to reference a custom -profile base to use as a starting point for customization. See the [API reference](https://github.com/istio/api/blob/master/operator/v1alpha1/operator.proto) -for details. - -## Developer quick start - -The quick start describes how to install and use the operator `mesh` CLI command and/or controller. - -### CLI - -To build the operator CLI, simply: - -```bash -make build -``` - -Ensure the created binary is in your PATH to run the examples below. - -### Controller (in cluster) - -Building a custom controller requires a Dockerhub (or similar) account. To build using the container based build: - -```bash -HUB=docker.io/ TAG=latest make docker.operator -``` - -This builds the controller binary and docker file, and pushes the image to the specified hub with the `latest` tag. -Once the images are pushed, configure kubectl to point to your cluster and install the controller. - -Install the controller manifest: - -```bash -istioctl operator init --hub docker.io/ --tag latest -kubectl create ns dubbo-system -kubectl apply -f operator/samples/default-install.yaml -``` - -This installs the controller into the cluster in the istio-operator namespace. The controller in turns installs -the Istio control plane into the dubbo-system namespace by default. - -### Controller (running locally) - -1. Set env $WATCH_NAMESPACE (default value is "dubbo-system") and $LEADER_ELECTION_NAMESPACE (default value is "istio-operator") - -1. Create the `WATCH_NAMESPACE` and `LEADER_ELECTION_NAMESPACE` if they are not created yet. - -```bash -kubectl create ns $WATCH_NAMESPACE --dry-run -o yaml | kubectl apply -f - -kubectl create ns $LEADER_ELECTION_NAMESPACE --dry-run -o yaml | kubectl apply -f - -``` - -1. From the istio repo root directory, run `go run ./operator/cmd/operator/*.go server` - -To use Remote debugging with IntelliJ, replace above step 2 with following: - -1. From `./operator/cmd/operator` path run -` -dlv debug --headless --listen=:2345 --api-version=2 -- server -`. - -1. In IntelliJ, create a new Go Remote debug configuration with default settings. - -1. Start debugging process and verify it is working. For example, try adding a breakpoint at Reconcile logic and apply a new CR. - -### Relationship between the CLI and controller - -The CLI and controller share the same API and codebase for generating manifests from the API. You can think of the -controller as the CLI command `istioctl install` running in a loop in a pod in the cluster and using the config -from the in-cluster IstioOperator custom resource (CR). -There are two major differences: - -1. The controller does not accept any dynamic user config through flags. All user interaction is through the -IstioOperator CR. -1. The controller has additional logic that mirrors istioctl commands like upgrade, but is driven from the declarative -API rather than command line. - -### Quick tour of CLI commands - -#### Flags - -The `istioctl` command supports the following flags: - -- `dry-run`: console output only, nothing applied to cluster or written to files. -- `verbose`: display entire manifest contents and other debug info (default is false). -- `set`: select profile or override profile defaults - -#### Basic default manifest - -The following command generates a manifest with the compiled-in `default` profile and charts: - -```bash -istioctl manifest generate -``` - -You can see these sources for the compiled-in profiles and charts in the repo under `manifests/`. These profiles and charts are also included in the Istio release tar. - -#### Output to dirs - -The output of the manifest is concatenated into a single file. To generate a directory hierarchy with subdirectory -levels representing a child dependency, use the following command: - -```bash -istioctl manifest generate -o istio_manifests -``` - -Use depth first search to traverse the created directory hierarchy when applying your YAML files. This is needed for -correct sequencing of dependencies. Child manifest directories must wait for their parent directory to be fully applied, -but not their sibling manifest directories. - -#### Just apply it for me - -The following command generates the manifests and applies them in the correct dependency order, waiting for the -dependencies to have the needed CRDs available: - -```bash -istioctl install -``` - -#### Review the values of a configuration profile - -The following commands show the values of a configuration profile: - -```bash -# show available profiles -istioctl profile list - -# show the values in demo profile -istioctl profile dump demo - -# show the values after a customization file is applied -istioctl profile dump -f samples/pilot-k8s.yaml - -# show differences between the default and demo profiles -istioctl profile dump default > 1.yaml -istioctl profile dump demo > 2.yaml -istioctl profile diff 1.yaml 2.yaml - -# show the differences in the generated manifests between the default profile and a customized install -istioctl manifest generate > 1.yaml -istioctl manifest generate -f samples/pilot-k8s.yaml > 2.yaml -istioctl manifest diff 1.yam1 2.yaml -``` - -The profile dump sub-command supports a couple of useful flags: - -- `config-path`: select the root for the configuration subtree you want to see e.g. just show Pilot: - -```bash -istioctl profile dump --config-path components.pilot -``` - -- `filename`: set parameters in the configuration file before dumping the resulting profile e.g. show the pilot k8s overlay settings: - -```bash -istioctl profile dump --filename samples/pilot-k8s.yaml -``` - -#### Select a specific configuration profile - -The simplest customization is to select a profile different to `default` e.g. `minimal`. See [manifests/profiles/minimal.yaml](../manifests/profiles/minimal.yaml): - -```yaml -# minimal-install.yaml -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: minimal -``` - -Use `istioctl` to generate the manifests for the new configuration profile: - -```bash -istioctl manifest generate -f manifests/profiles/minimal.yaml -``` - -After running the command, the Helm charts are rendered using `manifests/profiles/minimal.yaml`. - -##### --set syntax - -The CLI `--set` option can be used to override settings within the profile. - -For example, to enable auto mTLS, use `istioctl manifest generate --set values.global.mtls.auto=true --set values.global.controlPlaneSecurityEnabled=true` - -To override a setting that includes dots, escape them with a backslash (\). Your shell may require enclosing quotes. - -``` bash -istioctl manifest generate --set "values.sidecarInjectorWebhook.injectedAnnotations.container\.apparmor\.security\.beta\.kubernetes\.io/istio-proxy=runtime/default" -``` - -To override a setting that is part of a list, use brackets. - -``` bash -istioctl manifest generate --set values.gateways.istio-ingressgateway.enabled=false \ ---set values.gateways.istio-egressgateway.enabled=true \ ---set 'values.gateways.istio-egressgateway.secretVolumes[0].name'=egressgateway-certs \ ---set 'values.gateways.istio-egressgateway.secretVolumes[0].secretName'=istio-egressgateway-certs \ ---set 'values.gateways.istio-egressgateway.secretVolumes[0].mountPath'=/etc/istio/egressgateway-certs -``` - -#### Install from file path - -The compiled in charts and profiles are used by default, but you can specify a file path, for example: - -```yaml -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: /usr/home/bob/go/src/github.com/ostromart/istio-installer/data/profiles/default.yaml - installPackagePath: /usr/home/bob/go/src/github.com/ostromart/istio-installer/data/charts/ -``` - -You can mix and match these approaches. For example, you can use a compiled-in configuration profile with charts in your -local file system. - -#### Check diffs of manifests - -The following command takes two manifests and output the differences in a readable way. It can be used to compare between the manifests generated by operator API and helm directly: - -```bash -istioctl manifest diff ./out/helm-template/manifest.yaml ./out/mesh-manifest/manifest.yaml -``` - -### New API customization - -The [new platform level installation API](https://github.com/istio/api/blob/master/operator/v1alpha1/operator.proto) -defines install time parameters like component and enablement and namespace, and K8s settings like resources, HPA spec etc. in a structured way. -The simplest customization is to turn components on and off. For example, to turn on cni ([samples/cni-on.yaml](samples/cni-on.yaml): - -```yaml -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - components: - cni: - enabled: true -``` - -The operator validates the configuration and automatically detects syntax errors. If you are -using Helm values that are incompatible, the schema validation used in the operator may reject input that is valid for -Helm. -Each Istio component has K8s settings, and these can be overridden from the defaults using official K8s APIs rather than -Istio defined schemas ([samples/pilot-k8s.yaml](samples/pilot-k8s.yaml)): - -```yaml -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - components: - pilot: - k8s: - resources: - requests: - cpu: 1000m # override from default 500m - memory: 4096Mi # ... default 2048Mi - hpaSpec: - maxReplicas: 10 # ... default 5 - minReplicas: 2 # ... default 1 - nodeSelector: # ... default empty - master: "true" - tolerations: # ... default empty - - key: dedicated - operator: Exists - effect: NoSchedule - - key: CriticalAddonsOnly - operator: Exists -``` - -The K8s settings are defined in detail in the -[operator API](https://github.com/istio/api/blob/master/operator/v1alpha1/operator.proto). -The settings are the same for all components, so a user can configure pilot K8s settings in exactly the same, consistent -way as galley settings. Supported K8s settings currently include: - -- [resources](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container) -- [readiness probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/) -- [replica count](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) -- [HorizontalPodAutoscaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) -- [PodDisruptionBudget](https://kubernetes.io/docs/concepts/workloads/pods/disruptions/#how-disruption-budgets-work) -- [pod annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) -- [container environment variables](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) -- [ImagePullPolicy](https://kubernetes.io/docs/concepts/containers/images/) -- [priority class name](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass) -- [node selector](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) -- [toleration](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) -- [affinity and anti-affinity](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) -- [deployment strategy](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) -- [service annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) -- [service spec](https://kubernetes.io/docs/concepts/services-networking/service/) -- [pod securityContext](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) - -All of these K8s settings use the K8s API definitions, so [K8s documentation](https://kubernetes.io/docs/concepts/) can -be used for reference. All K8s overlay values are also validated in the operator. - -### Customizing the old values.yaml API - -The new platform install API above deals with K8s level settings. The remaining values.yaml parameters deal with Istio -control plane operation rather than installation. For the time being, the operator just passes these through to the Helm -charts unmodified (but validated through a -[schema](pkg/apis/istio/v1alpha1/values_types.proto)). Values.yaml settings -are overridden the same way as the new API, though a customized CR overlaid over default values for the selected -profile. Here's an example of overriding some global level default values ([samples/values-global.yaml](samples/values-global.yaml)): - -```yaml -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: sds - values: - global: - logging: - level: "default:warning" # override from info -``` - -Values overrides can also be specified for a particular component - ([samples/values-pilot.yaml](samples/values-pilot.yaml)): - -```yaml -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - values: - pilot: - traceSampling: 0.1 # override from 1.0 -``` - -### Advanced K8s resource overlays - -Advanced users may occasionally have the need to customize parameters (like container command line flags) which are not -exposed through either of the installation or configuration APIs described in this document. For such cases, it's -possible to overlay the generated K8s resources before they are applied with user-defined overlays. For example, to -override some container level values in the Pilot container ([samples/pilot-advanced-override.yaml](samples/pilot-advanced-override.yaml)): - -```yaml -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - components: - pilot: - k8s: - overlays: - - kind: Deployment - name: istio-pilot - patches: - - path: spec.template.spec.containers.[name:discovery].args.[30m] - value: "60m" # OVERRIDDEN - - path: spec.template.spec.containers.[name:discovery].ports.[containerPort:8080].containerPort - value: 8090 # OVERRIDDEN - - path: 'spec.template.spec.volumes[100]' #push to the list - value: - configMap: - name: my-config-map - name: my-volume-name - - path: 'spec.template.spec.containers[0].volumeMounts[100]' - value: - mountPath: /mnt/path1 - name: my-volume-name - - kind: Service - name: istio-pilot - patches: - - path: spec.ports.[name:grpc-xds].port - value: 15099 # OVERRIDDEN -``` - -The user-defined overlay uses a path spec that includes the ability to select list items by key. In the example above, -the container with the key-value "name: discovery" is selected from the list of containers, and the command line -parameter with value "30m" is selected to be modified. The advanced overlay capability is described in more detail in -the spec. - -## Interaction with controller - -The controller shares the same API as the operator CLI, so it's possible to install any of the above examples as a CR -in the cluster in the dubbo-system namespace and the controller will react to it with the same outcome as running -`istioctl install -f `. - -## Architecture - -See [ARCHITECTURE.md](ARCHITECTURE.md) diff --git a/operator/cmd/mesh.go b/operator/cmd/mesh.go deleted file mode 100644 index d9435c744..000000000 --- a/operator/cmd/mesh.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "os" -) - -import ( - "istio.io/pkg/version" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/cmd/mesh" - binversion "github.com/apache/dubbo-go-pixiu/operator/version" -) - -func main() { - version.Info.Version = binversion.OperatorVersionString - rootCmd := mesh.GetRootCmd(os.Args[1:]) - if err := rootCmd.Execute(); err != nil { - os.Exit(1) - } -} diff --git a/operator/cmd/mesh/install.go b/operator/cmd/mesh/install.go deleted file mode 100644 index dd0c43713..000000000 --- a/operator/cmd/mesh/install.go +++ /dev/null @@ -1,431 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "context" - "fmt" - "io" - "os" - "sort" - "strings" - "time" -) - -import ( - "github.com/fatih/color" - "github.com/spf13/cobra" - "istio.io/api/operator/v1alpha1" - "istio.io/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/clioptions" - revtag "github.com/apache/dubbo-go-pixiu/istioctl/pkg/tag" - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/verifier" - v1alpha12 "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/cache" - "github.com/apache/dubbo-go-pixiu/operator/pkg/controller/istiocontrolplane" - "github.com/apache/dubbo-go-pixiu/operator/pkg/helmreconciler" - "github.com/apache/dubbo-go-pixiu/operator/pkg/manifest" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/translate" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/progress" - pkgversion "github.com/apache/dubbo-go-pixiu/operator/pkg/version" - operatorVer "github.com/apache/dubbo-go-pixiu/operator/version" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -type InstallArgs struct { - // InFilenames is an array of paths to the input IstioOperator CR files. - InFilenames []string - // KubeConfigPath is the path to kube config file. - KubeConfigPath string - // Context is the cluster context in the kube config - Context string - // ReadinessTimeout is maximum time to wait for all Istio resources to be ready. wait must be true for this setting - // to take effect. - ReadinessTimeout time.Duration - // SkipConfirmation determines whether the user is prompted for confirmation. - // If set to true, the user is not prompted and a Yes response is assumed in all cases. - SkipConfirmation bool - // Force proceeds even if there are validation errors - Force bool - // Verify after installation - Verify bool - // Set is a string with element format "path=value" where path is an IstioOperator path and the value is a - // value to set the node at that path to. - Set []string - // ManifestsPath is a path to a ManifestsPath and profiles directory in the local filesystem, or URL with a release tgz. - ManifestsPath string - // Revision is the Istio control plane revision the command targets. - Revision string -} - -func (a *InstallArgs) String() string { - var b strings.Builder - b.WriteString("InFilenames: " + fmt.Sprint(a.InFilenames) + "\n") - b.WriteString("KubeConfigPath: " + a.KubeConfigPath + "\n") - b.WriteString("Context: " + a.Context + "\n") - b.WriteString("ReadinessTimeout: " + fmt.Sprint(a.ReadinessTimeout) + "\n") - b.WriteString("SkipConfirmation: " + fmt.Sprint(a.SkipConfirmation) + "\n") - b.WriteString("Force: " + fmt.Sprint(a.Force) + "\n") - b.WriteString("Verify: " + fmt.Sprint(a.Verify) + "\n") - b.WriteString("Set: " + fmt.Sprint(a.Set) + "\n") - b.WriteString("ManifestsPath: " + a.ManifestsPath + "\n") - b.WriteString("Revision: " + a.Revision + "\n") - return b.String() -} - -func addInstallFlags(cmd *cobra.Command, args *InstallArgs) { - cmd.PersistentFlags().StringSliceVarP(&args.InFilenames, "filename", "f", nil, filenameFlagHelpStr) - cmd.PersistentFlags().StringVarP(&args.KubeConfigPath, "kubeconfig", "c", "", KubeConfigFlagHelpStr) - cmd.PersistentFlags().StringVar(&args.Context, "context", "", ContextFlagHelpStr) - cmd.PersistentFlags().DurationVar(&args.ReadinessTimeout, "readiness-timeout", 300*time.Second, - "Maximum time to wait for Istio resources in each component to be ready.") - cmd.PersistentFlags().BoolVarP(&args.SkipConfirmation, "skip-confirmation", "y", false, skipConfirmationFlagHelpStr) - cmd.PersistentFlags().BoolVar(&args.Force, "force", false, ForceFlagHelpStr) - cmd.PersistentFlags().BoolVar(&args.Verify, "verify", false, VerifyCRInstallHelpStr) - cmd.PersistentFlags().StringArrayVarP(&args.Set, "set", "s", nil, setFlagHelpStr) - cmd.PersistentFlags().StringVarP(&args.ManifestsPath, "charts", "", "", ChartsDeprecatedStr) - cmd.PersistentFlags().StringVarP(&args.ManifestsPath, "manifests", "d", "", ManifestsFlagHelpStr) - cmd.PersistentFlags().StringVarP(&args.Revision, "revision", "r", "", revisionFlagHelpStr) -} - -// InstallCmdWithArgs generates an Istio install manifest and applies it to a cluster -func InstallCmdWithArgs(rootArgs *RootArgs, iArgs *InstallArgs, logOpts *log.Options) *cobra.Command { - ic := &cobra.Command{ - Use: "install", - Short: "Applies an Istio manifest, installing or reconfiguring Istio on a cluster.", - Long: "The install command generates an Istio install manifest and applies it to a cluster.", - Aliases: []string{"apply"}, - // nolint: lll - Example: ` # Apply a default Istio installation - istioctl install - - # Enable Tracing - istioctl install --set meshConfig.enableTracing=true - - # Generate the demo profile and don't wait for confirmation - istioctl install --set profile=demo --skip-confirmation - - # To override a setting that includes dots, escape them with a backslash (\). Your shell may require enclosing quotes. - istioctl install --set "values.sidecarInjectorWebhook.injectedAnnotations.container\.apparmor\.security\.beta\.kubernetes\.io/istio-proxy=runtime/default" - - # For setting boolean-string option, it should be enclosed quotes and escaped with a backslash (\). - istioctl install --set meshConfig.defaultConfig.proxyMetadata.PROXY_XDS_VIA_AGENT=\"false\" -`, - Args: cobra.ExactArgs(0), - PreRunE: func(cmd *cobra.Command, args []string) error { - if !labels.IsDNS1123Label(iArgs.Revision) && cmd.PersistentFlags().Changed("revision") { - return fmt.Errorf("invalid revision specified: %v", iArgs.Revision) - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - l := clog.NewConsoleLogger(cmd.OutOrStdout(), cmd.ErrOrStderr(), installerScope) - p := NewPrinterForWriter(cmd.OutOrStderr()) - return Install(rootArgs, iArgs, logOpts, cmd.OutOrStdout(), l, p) - }, - } - - addFlags(ic, rootArgs) - addInstallFlags(ic, iArgs) - return ic -} - -// InstallCmd generates an Istio install manifest and applies it to a cluster -func InstallCmd(logOpts *log.Options) *cobra.Command { - return InstallCmdWithArgs(&RootArgs{}, &InstallArgs{}, logOpts) -} - -func Install(rootArgs *RootArgs, iArgs *InstallArgs, logOpts *log.Options, stdOut io.Writer, l clog.Logger, p Printer) error { - kubeClient, client, err := KubernetesClients(iArgs.KubeConfigPath, iArgs.Context, l) - if err != nil { - return err - } - - tag, err := GetTagVersion(operatorVer.OperatorVersionString) - if err != nil { - return fmt.Errorf("fetch Istio version: %v", err) - } - setFlags := applyFlagAliases(iArgs.Set, iArgs.ManifestsPath, iArgs.Revision) - - _, iop, err := manifest.GenerateConfig(iArgs.InFilenames, setFlags, iArgs.Force, kubeClient, l) - if err != nil { - return fmt.Errorf("generate config: %v", err) - } - - profile, ns, enabledComponents, err := getProfileNSAndEnabledComponents(iop) - if err != nil { - return fmt.Errorf("failed to get profile, namespace or enabled components: %v", err) - } - - // Ignore the err because we don't want to show - // "no running Istio pods in dubbo-system" for the first time - _ = detectIstioVersionDiff(p, tag, ns, kubeClient, setFlags) - - // Warn users if they use `istioctl install` without any config args. - if !rootArgs.DryRun && !iArgs.SkipConfirmation { - prompt := fmt.Sprintf("This will install the Istio %s %s profile with %q components into the cluster. Proceed? (y/N)", tag, profile, enabledComponents) - if profile == "empty" { - prompt = fmt.Sprintf("This will install the Istio %s %s profile into the cluster. Proceed? (y/N)", tag, profile) - } - if !confirm(prompt, stdOut) { - p.Println("Cancelled.") - os.Exit(1) - } - } - if err := configLogs(logOpts); err != nil { - return fmt.Errorf("could not configure logs: %s", err) - } - - // Detect whether previous installation exists prior to performing the installation. - exists := revtag.PreviousInstallExists(context.Background(), kubeClient) - pilotEnabled := iop.Spec.Components.Pilot != nil && iop.Spec.Components.Pilot.Enabled.Value - rev := iop.Spec.Revision - if rev == "" && pilotEnabled { - _ = revtag.DeleteTagWebhooks(context.Background(), kubeClient, revtag.DefaultRevisionName) - } - iop, err = InstallManifests(iop, iArgs.Force, rootArgs.DryRun, kubeClient, client, iArgs.ReadinessTimeout, l) - if err != nil { - return fmt.Errorf("failed to install manifests: %v", err) - } - - if !exists || rev == "" && pilotEnabled { - p.Println("Making this installation the default for injection and validation.") - if rev == "" { - rev = revtag.DefaultRevisionName - } - autoInjectNamespaces := validateEnableNamespacesByDefault(iop) - - o := &revtag.GenerateOptions{ - Tag: revtag.DefaultRevisionName, - Revision: rev, - Overwrite: true, - AutoInjectNamespaces: autoInjectNamespaces, - } - // If tag cannot be created could be remote cluster install, don't fail out. - tagManifests, err := revtag.Generate(context.Background(), kubeClient, o, ns) - if err == nil { - err = revtag.Create(kubeClient, tagManifests) - if err != nil { - return err - } - } - } - - if iArgs.Verify { - if rootArgs.DryRun { - l.LogAndPrint("Control plane health check is not applicable in dry-run mode") - return nil - } - l.LogAndPrint("\n\nVerifying installation:") - installationVerifier, err := verifier.NewStatusVerifier(iop.Namespace, iArgs.ManifestsPath, iArgs.KubeConfigPath, - iArgs.Context, iArgs.InFilenames, clioptions.ControlPlaneOptions{Revision: iop.Spec.Revision}, - verifier.WithLogger(l), - verifier.WithIOP(iop), - ) - if err != nil { - return fmt.Errorf("failed to setup verifier: %v", err) - } - if err := installationVerifier.Verify(); err != nil { - return fmt.Errorf("verification failed with the following error: %v", err) - } - } - - if !rootArgs.DryRun { - _, _ = fmt.Fprintln(stdOut, "\nThank you for installing Istio 1.14. Please take a few minutes to "+ - "tell us about your install/upgrade experience! https://forms.gle/yEtCbt45FZ3VoDT5A") - } - - return nil -} - -// InstallManifests generates manifests from the given istiooperator instance and applies them to the -// cluster. See GenManifests for more description of the manifest generation process. -// -// force validation warnings are written to logger but command is not aborted -// DryRun all operations are done but nothing is written -// -// Returns final IstioOperator after installation if successful. -func InstallManifests(iop *v1alpha12.IstioOperator, force bool, dryRun bool, kubeClient kube.Client, client client.Client, - waitTimeout time.Duration, l clog.Logger) (*v1alpha12.IstioOperator, error) { - // Needed in case we are running a test through this path that doesn't start a new process. - cache.FlushObjectCaches() - opts := &helmreconciler.Options{ - DryRun: dryRun, Log: l, WaitTimeout: waitTimeout, ProgressLog: progress.NewLog(), - Force: force, - } - reconciler, err := helmreconciler.NewHelmReconciler(client, kubeClient, iop, opts) - if err != nil { - return iop, err - } - status, err := reconciler.Reconcile() - if err != nil { - return iop, fmt.Errorf("errors occurred during operation: %v", err) - } - if status.Status != v1alpha1.InstallStatus_HEALTHY { - return iop, fmt.Errorf("errors occurred during operation") - } - - opts.ProgressLog.SetState(progress.StateComplete) - - // Save a copy of what was installed as a CR in the cluster under an internal name. - iop.Name = savedIOPName(iop) - if iop.Annotations == nil { - iop.Annotations = make(map[string]string) - } - iop.Annotations[istiocontrolplane.IgnoreReconcileAnnotation] = "true" - iopStr, err := yaml.Marshal(iop) - if err != nil { - return iop, err - } - - return iop, saveIOPToCluster(reconciler, string(iopStr)) -} - -func savedIOPName(iop *v1alpha12.IstioOperator) string { - ret := name.InstalledSpecCRPrefix - if iop.Name != "" { - ret += "-" + iop.Name - } - if iop.Spec.Revision != "" { - ret += "-" + iop.Spec.Revision - } - return ret -} - -// detectIstioVersionDiff will show warning if istioctl version and control plane version are different -// nolint: interfacer -func detectIstioVersionDiff(p Printer, tag string, ns string, kubeClient kube.ExtendedClient, setFlags []string) error { - warnMarker := color.New(color.FgYellow).Add(color.Italic).Sprint("WARNING:") - icps, err := kubeClient.GetIstioVersions(context.TODO(), ns) - if err != nil { - return err - } - if len(*icps) != 0 { - var icpTags []string - var icpTag string - // create normalized tags for multiple control plane revisions - for _, icp := range *icps { - tagVer, err := GetTagVersion(icp.Info.GitTag) - if err != nil { - return err - } - icpTags = append(icpTags, tagVer) - } - // sort different versions of control plane revsions - sort.Strings(icpTags) - // capture latest revision installed for comparison - for _, val := range icpTags { - if val != "" { - icpTag = val - } - } - revision := manifest.GetValueForSetFlag(setFlags, "revision") - msg := "" - // when the revision is not passed and if the ns has a prior istio - if revision == "" && tag != icpTag { - if icpTag < tag { - msg = "A newer" - } else { - msg = "An older" - } - p.Printf("%s Istio control planes installed: %s.\n"+ - "%s "+msg+" installed version of Istio has been detected. Running this command will overwrite it.\n", warnMarker, strings.Join(icpTags, ", "), warnMarker) - } - // when the revision is passed - if icpTag != "" && tag != icpTag && revision != "" { - if icpTag < tag { - p.Printf("%s Istio is being upgraded from %s -> %s.\n"+ - "%s Before upgrading, you may wish to use 'istioctl analyze' to check for "+ - "IST0002 and IST0135 deprecation warnings.\n", warnMarker, icpTag, tag, warnMarker) - } else { - p.Printf("%s Istio is being downgraded from %s -> %s.\n", warnMarker, icpTag, tag) - } - } - } - return nil -} - -// GetTagVersion returns istio tag version -func GetTagVersion(tagInfo string) (string, error) { - if pkgversion.IsVersionString(tagInfo) { - tagInfo = pkgversion.TagToVersionStringGrace(tagInfo) - } - tag, err := pkgversion.NewVersionFromString(tagInfo) - if err != nil { - return "", err - } - return tag.String(), nil -} - -// getProfileNSAndEnabledComponents get the profile and all the enabled components -// from the given input files and --set flag overlays. -func getProfileNSAndEnabledComponents(iop *v1alpha12.IstioOperator) (string, string, []string, error) { - var enabledComponents []string - if iop.Spec.Components != nil { - for _, c := range name.AllCoreComponentNames { - enabled, err := translate.IsComponentEnabledInSpec(c, iop.Spec) - if err != nil { - return "", "", nil, fmt.Errorf("failed to check if component: %s is enabled or not: %v", string(c), err) - } - if enabled { - enabledComponents = append(enabledComponents, name.UserFacingComponentName(c)) - } - } - for _, c := range iop.Spec.Components.IngressGateways { - if c.Enabled.GetValue() { - enabledComponents = append(enabledComponents, name.UserFacingComponentName(name.IngressComponentName)) - break - } - } - for _, c := range iop.Spec.Components.EgressGateways { - if c.Enabled.GetValue() { - enabledComponents = append(enabledComponents, name.UserFacingComponentName(name.EgressComponentName)) - break - } - } - } - - if configuredNamespace := v1alpha12.Namespace(iop.Spec); configuredNamespace != "" { - return iop.Spec.Profile, configuredNamespace, enabledComponents, nil - } - return iop.Spec.Profile, name.IstioDefaultNamespace, enabledComponents, nil -} - -// validateEnableNamespacesByDefault checks whether there is .Values.sidecarInjectorWebhook.enableNamespacesByDefault set in the Istio Operator. -// Should be used in installer when deciding whether to enable an automatic sidecar injection in all namespaces. -func validateEnableNamespacesByDefault(iop *v1alpha12.IstioOperator) bool { - if iop == nil || iop.Spec == nil || iop.Spec.Values == nil { - return false - } - sidecarValues := iop.Spec.Values.AsMap()["sidecarInjectorWebhook"] - sidecarMap, ok := sidecarValues.(map[string]interface{}) - if !ok { - return false - } - autoInjectNamespaces, ok := sidecarMap["enableNamespacesByDefault"].(bool) - if !ok { - return false - } - - return autoInjectNamespaces -} diff --git a/operator/cmd/mesh/manifest-diff.go b/operator/cmd/mesh/manifest-diff.go deleted file mode 100644 index 41b28aa90..000000000 --- a/operator/cmd/mesh/manifest-diff.go +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "fmt" - "os" - "path/filepath" -) - -import ( - "github.com/spf13/cobra" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/compare" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -// YAMLSuffix is the suffix of a YAML file. -const YAMLSuffix = ".yaml" - -type manifestDiffArgs struct { - // compareDir indicates comparison between directory. - compareDir bool - // verbose generates verbose output. - verbose bool - // selectResources constrains the list of resources to compare to only the ones in this list, ignoring all others. - // The format of each list item is :: and the items are comma separated. The * character represents wildcard selection. - // e.g. - // Deployment:dubbo-system:* - compare all deployments in dubbo-system namespace - // Service:*:istio-pilot - compare Services called "istio-pilot" in all namespaces. - selectResources string - // ignoreResources ignores all listed items during comparison. It uses the same list format as selectResources. - ignoreResources string - // renameResources identifies renamed resources before comparison. - // The format of each renaming pair is A->B, all renaming pairs are comma separated. - // e.g. Service:*:istio-pilot->Service:*:istio-control - rename istio-pilot service into istio-control - renameResources string -} - -func addManifestDiffFlags(cmd *cobra.Command, diffArgs *manifestDiffArgs) { - cmd.PersistentFlags().BoolVarP(&diffArgs.compareDir, "directory", "r", - false, "Compare directory.") - cmd.PersistentFlags().BoolVarP(&diffArgs.verbose, "verbose", "v", - false, "Verbose output.") - cmd.PersistentFlags().StringVar(&diffArgs.selectResources, "select", "::", - "Constrain the list of resources to compare to only the ones in this list, ignoring all others.\n"+ - "The format of each list item is \"::\" and the items are comma separated. The \"*\" character represents wildcard selection.\n"+ - "e.g.\n"+ - " Deployment:dubbo-system:* - compare all deployments in dubbo-system namespace\n"+ - " Service:*:istiod - compare Services called \"istiod\" in all namespaces") - cmd.PersistentFlags().StringVar(&diffArgs.ignoreResources, "ignore", "", - "Ignore all listed items during comparison, using the same list format as selectResources.") - cmd.PersistentFlags().StringVar(&diffArgs.renameResources, "rename", "", - "Rename resources before comparison.\n"+ - "The format of each renaming pair is A->B, all renaming pairs are comma separated.\n"+ - "e.g. Service:*:istiod->Service:*:istio-control - rename istiod service into istio-control") -} - -func manifestDiffCmd(rootArgs *RootArgs, diffArgs *manifestDiffArgs) *cobra.Command { - cmd := &cobra.Command{ - Use: "diff ", - Short: "Compare manifests and generate diff", - Long: "The diff subcommand compares manifests from two files or directories. The output is a list of\n" + - "changed paths with the value changes shown as OLD-VALUE -> NEW-VALUE.\n" + - "List order changes are shown as [OLD-INDEX->NEW-INDEX], with ? used where a list item is added or\n" + - "removed.", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) != 2 { - return fmt.Errorf("diff requires two files or directories") - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - var err error - var equal bool - if diffArgs.compareDir { - equal, err = compareManifestsFromDirs(rootArgs, diffArgs.verbose, args[0], args[1], - diffArgs.renameResources, diffArgs.selectResources, diffArgs.ignoreResources) - if err != nil { - return err - } - if !equal { - os.Exit(1) - } - return nil - } - - equal, err = compareManifestsFromFiles(rootArgs, args, diffArgs.verbose, - diffArgs.renameResources, diffArgs.selectResources, diffArgs.ignoreResources) - if err != nil { - return err - } - if !equal { - os.Exit(1) - } - return nil - }, - } - return cmd -} - -// compareManifestsFromFiles compares two manifest files -func compareManifestsFromFiles(rootArgs *RootArgs, args []string, verbose bool, - renameResources, selectResources, ignoreResources string) (bool, error) { - initLogsOrExit(rootArgs) - - a, err := os.ReadFile(args[0]) - if err != nil { - return false, fmt.Errorf("could not read %q: %v", args[0], err) - } - b, err := os.ReadFile(args[1]) - if err != nil { - return false, fmt.Errorf("could not read %q: %v", args[1], err) - } - - diff, err := compare.ManifestDiffWithRenameSelectIgnore(string(a), string(b), renameResources, selectResources, - ignoreResources, verbose) - if err != nil { - return false, err - } - if diff != "" { - fmt.Printf("Differences in manifests are:\n%s\n", diff) - return false, nil - } - - fmt.Println("Manifests are identical") - return true, nil -} - -func yamlFileFilter(path string) bool { - return filepath.Ext(path) == YAMLSuffix -} - -// compareManifestsFromDirs compares manifests from two directories -func compareManifestsFromDirs(rootArgs *RootArgs, verbose bool, dirName1, dirName2, - renameResources, selectResources, ignoreResources string) (bool, error) { - initLogsOrExit(rootArgs) - - mf1, err := util.ReadFilesWithFilter(dirName1, yamlFileFilter) - if err != nil { - return false, err - } - mf2, err := util.ReadFilesWithFilter(dirName2, yamlFileFilter) - if err != nil { - return false, err - } - - diff, err := compare.ManifestDiffWithRenameSelectIgnore(mf1, mf2, renameResources, selectResources, - ignoreResources, verbose) - if err != nil { - return false, err - } - if diff != "" { - fmt.Printf("Differences in manifests are:\n%s\n", diff) - return false, nil - } - - fmt.Println("Manifests are identical") - return true, nil -} diff --git a/operator/cmd/mesh/manifest-generate.go b/operator/cmd/mesh/manifest-generate.go deleted file mode 100644 index 6d7ae55d3..000000000 --- a/operator/cmd/mesh/manifest-generate.go +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "fmt" - "os" - "path/filepath" - "strings" -) - -import ( - "github.com/spf13/cobra" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/helm" - "github.com/apache/dubbo-go-pixiu/operator/pkg/helmreconciler" - "github.com/apache/dubbo-go-pixiu/operator/pkg/manifest" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" -) - -type ManifestGenerateArgs struct { - // InFilenames is an array of paths to the input IstioOperator CR files. - InFilenames []string - // OutFilename is the path to the generated output directory. - OutFilename string - // Set is a string with element format "path=value" where path is an IstioOperator path and the value is a - // value to set the node at that path to. - Set []string - // Force proceeds even if there are validation errors - Force bool - // ManifestsPath is a path to a charts and profiles directory in the local filesystem, or URL with a release tgz. - ManifestsPath string - // Revision is the Istio control plane revision the command targets. - Revision string - // Components is a list of strings specifying which component's manifests to be generated. - Components []string - // Filter is the list of components to render - Filter []string -} - -func (a *ManifestGenerateArgs) String() string { - var b strings.Builder - b.WriteString("InFilenames: " + fmt.Sprint(a.InFilenames) + "\n") - b.WriteString("OutFilename: " + a.OutFilename + "\n") - b.WriteString("Set: " + fmt.Sprint(a.Set) + "\n") - b.WriteString("Force: " + fmt.Sprint(a.Force) + "\n") - b.WriteString("ManifestsPath: " + a.ManifestsPath + "\n") - b.WriteString("Revision: " + a.Revision + "\n") - b.WriteString("Components: " + fmt.Sprint(a.Components) + "\n") - return b.String() -} - -func addManifestGenerateFlags(cmd *cobra.Command, args *ManifestGenerateArgs) { - cmd.PersistentFlags().StringSliceVarP(&args.InFilenames, "filename", "f", nil, filenameFlagHelpStr) - cmd.PersistentFlags().StringVarP(&args.OutFilename, "output", "o", "", "Manifest output directory path.") - cmd.PersistentFlags().StringArrayVarP(&args.Set, "set", "s", nil, setFlagHelpStr) - cmd.PersistentFlags().BoolVar(&args.Force, "force", false, ForceFlagHelpStr) - cmd.PersistentFlags().StringVarP(&args.ManifestsPath, "charts", "", "", ChartsDeprecatedStr) - cmd.PersistentFlags().StringVarP(&args.ManifestsPath, "manifests", "d", "", ManifestsFlagHelpStr) - cmd.PersistentFlags().StringVarP(&args.Revision, "revision", "r", "", revisionFlagHelpStr) - cmd.PersistentFlags().StringSliceVar(&args.Components, "component", nil, ComponentFlagHelpStr) - cmd.PersistentFlags().StringSliceVar(&args.Filter, "filter", nil, "") - _ = cmd.PersistentFlags().MarkHidden("filter") -} - -func ManifestGenerateCmd(rootArgs *RootArgs, mgArgs *ManifestGenerateArgs, logOpts *log.Options) *cobra.Command { - return &cobra.Command{ - Use: "generate", - Short: "Generates an Istio install manifest", - Long: "The generate subcommand generates an Istio install manifest and outputs to the console by default.", - // nolint: lll - Example: ` # Generate a default Istio installation - istioctl manifest generate - - # Enable Tracing - istioctl manifest generate --set meshConfig.enableTracing=true - - # Generate the demo profile - istioctl manifest generate --set profile=demo - - # To override a setting that includes dots, escape them with a backslash (\). Your shell may require enclosing quotes. - istioctl manifest generate --set "values.sidecarInjectorWebhook.injectedAnnotations.container\.apparmor\.security\.beta\.kubernetes\.io/istio-proxy=runtime/default" - - # For setting boolean-string option, it should be enclosed quotes and escaped with a backslash (\). - istioctl manifest generate --set meshConfig.defaultConfig.proxyMetadata.PROXY_XDS_VIA_AGENT=\"false\" -`, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) != 0 { - return fmt.Errorf("generate accepts no positional arguments, got %#v", args) - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - l := clog.NewConsoleLogger(cmd.OutOrStdout(), cmd.ErrOrStderr(), installerScope) - return ManifestGenerate(rootArgs, mgArgs, logOpts, l) - }, - } -} - -func ManifestGenerate(args *RootArgs, mgArgs *ManifestGenerateArgs, logopts *log.Options, l clog.Logger) error { - if err := configLogs(logopts); err != nil { - return fmt.Errorf("could not configure logs: %s", err) - } - - manifests, _, err := manifest.GenManifests(mgArgs.InFilenames, applyFlagAliases(mgArgs.Set, mgArgs.ManifestsPath, mgArgs.Revision), - mgArgs.Force, mgArgs.Filter, nil, l) - if err != nil { - return err - } - - if len(mgArgs.Components) != 0 { - filteredManifests := name.ManifestMap{} - for _, cArg := range mgArgs.Components { - componentName := name.ComponentName(cArg) - if cManifests, ok := manifests[componentName]; ok { - filteredManifests[componentName] = cManifests - } else { - return fmt.Errorf("incorrect component name: %s. Valid options: %v", cArg, name.AllComponentNames) - } - } - manifests = filteredManifests - } - - if mgArgs.OutFilename == "" { - ordered, err := orderedManifests(manifests) - if err != nil { - return fmt.Errorf("failed to order manifests: %v", err) - } - for _, m := range ordered { - l.Print(m + object.YAMLSeparator) - } - } else { - if err := os.MkdirAll(mgArgs.OutFilename, os.ModePerm); err != nil { - return err - } - if err := RenderToDir(manifests, mgArgs.OutFilename, args.DryRun, l); err != nil { - return err - } - } - - return nil -} - -// orderedManifests generates a list of manifests from the given map sorted by the default object order -// This allows -func orderedManifests(mm name.ManifestMap) ([]string, error) { - var rawOutput []string - var output []string - for _, mfs := range mm { - rawOutput = append(rawOutput, mfs...) - } - objects, err := object.ParseK8sObjectsFromYAMLManifest(strings.Join(rawOutput, helm.YAMLSeparator)) - if err != nil { - return nil, err - } - // For a given group of objects, sort in order to avoid missing dependencies, such as creating CRDs first - objects.Sort(object.DefaultObjectOrder()) - for _, obj := range objects { - yml, err := obj.YAML() - if err != nil { - return nil, err - } - output = append(output, string(yml)) - } - - return output, nil -} - -// RenderToDir writes manifests to a local filesystem directory tree. -func RenderToDir(manifests name.ManifestMap, outputDir string, dryRun bool, l clog.Logger) error { - l.LogAndPrintf("Component dependencies tree: \n%s", helmreconciler.InstallTreeString()) - l.LogAndPrintf("Rendering manifests to output dir %s", outputDir) - return renderRecursive(manifests, helmreconciler.InstallTree, outputDir, dryRun, l) -} - -func renderRecursive(manifests name.ManifestMap, installTree helmreconciler.ComponentTree, outputDir string, dryRun bool, l clog.Logger) error { - for k, v := range installTree { - componentName := string(k) - // In cases (like gateways) where multiple instances can exist, concatenate the manifests and apply as one. - ym := strings.Join(manifests[k], helm.YAMLSeparator) - l.LogAndPrintf("Rendering: %s", componentName) - dirName := filepath.Join(outputDir, componentName) - if !dryRun { - if err := os.MkdirAll(dirName, os.ModePerm); err != nil { - return fmt.Errorf("could not create directory %s; %s", outputDir, err) - } - } - fname := filepath.Join(dirName, componentName) + ".yaml" - l.LogAndPrintf("Writing manifest to %s", fname) - if !dryRun { - if err := os.WriteFile(fname, []byte(ym), 0o644); err != nil { - return fmt.Errorf("could not write manifest config; %s", err) - } - } - - kt, ok := v.(helmreconciler.ComponentTree) - if !ok { - // Leaf - return nil - } - if err := renderRecursive(manifests, kt, dirName, dryRun, l); err != nil { - return err - } - } - return nil -} diff --git a/operator/cmd/mesh/manifest-generate_test.go b/operator/cmd/mesh/manifest-generate_test.go deleted file mode 100644 index 0c169f7ed..000000000 --- a/operator/cmd/mesh/manifest-generate_test.go +++ /dev/null @@ -1,1118 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - "reflect" - "strings" - "testing" -) - -import ( - "github.com/google/go-cmp/cmp" - . "github.com/onsi/gomega" - "istio.io/pkg/version" - v1 "k8s.io/api/admissionregistration/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - klabels "k8s.io/apimachinery/pkg/labels" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/compare" - "github.com/apache/dubbo-go-pixiu/operator/pkg/helm" - "github.com/apache/dubbo-go-pixiu/operator/pkg/helmreconciler" - "github.com/apache/dubbo-go-pixiu/operator/pkg/manifest" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/httpserver" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/tgz" - tutil "github.com/apache/dubbo-go-pixiu/pilot/test/util" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" -) - -const ( - istioTestVersion = "istio-1.7.0" - testTGZFilename = istioTestVersion + "-linux.tar.gz" - testIstioDiscoveryChartPath = "charts/istio-control/istio-discovery/templates" -) - -// chartSourceType defines where charts used in the test come from. -type chartSourceType string - -var ( - operatorRootDir = filepath.Join(env.IstioSrc, "operator") - - // testDataDir contains the directory for manifest-generate test data - testDataDir = filepath.Join(operatorRootDir, "cmd/mesh/testdata/manifest-generate") - - // Snapshot charts are in testdata/manifest-generate/data-snapshot - snapshotCharts = func() chartSourceType { - d, err := os.MkdirTemp("", "data-snapshot-*") - if err != nil { - panic(fmt.Errorf("failed to make temp dir: %v", err)) - } - f, err := os.Open("testdata/manifest-generate/data-snapshot.tar.gz") - if err != nil { - panic(fmt.Errorf("failed to read data snapshot: %v", err)) - } - if err := tgz.Extract(f, d); err != nil { - panic(fmt.Errorf("failed to extract data snapshot: %v", err)) - } - return chartSourceType(filepath.Join(d, "manifests")) - }() - // Compiled in charts come from assets.gen.go - compiledInCharts chartSourceType = "COMPILED" - _ = compiledInCharts - // Live charts come from manifests/ - liveCharts = chartSourceType(filepath.Join(env.IstioSrc, helm.OperatorSubdirFilePath)) -) - -type testGroup []struct { - desc string - // Small changes to the input profile produce large changes to the golden output - // files. This makes it difficult to spot meaningful changes in pull requests. - // By default we hide these changes to make developers life's a bit easier. However, - // it is still useful to sometimes override this behavior and show the full diff. - // When this flag is true, use an alternative file suffix that is not hidden by - // default github in pull requests. - showOutputFileInPullRequest bool - flags string - noInput bool - outputDir string - fileSelect []string - diffSelect string - diffIgnore string - chartSource chartSourceType -} - -func TestMain(m *testing.M) { - code := m.Run() - // Cleanup uncompress snapshot charts - os.RemoveAll(string(snapshotCharts)) - os.Exit(code) -} - -func TestManifestGenerateComponentHubTag(t *testing.T) { - g := NewWithT(t) - - objs, err := runManifestCommands("component_hub_tag", "--set values.gateways.istio-ingressgateway.enabled=true", liveCharts, []string{"templates/deployment.yaml"}) - if err != nil { - t.Fatal(err) - } - - tests := []struct { - deploymentName string - containerName string - want string - }{ - { - deploymentName: "istio-ingressgateway", - containerName: "istio-proxy", - want: "istio-spec.hub/dubbo-agent:istio-spec.tag", - }, - { - deploymentName: "istiod", - containerName: "discovery", - want: "component.pilot.hub/dubbo-pilot:2", - }, - } - - for _, tt := range tests { - for _, os := range objs { - containerName := tt.deploymentName - if tt.containerName != "" { - containerName = tt.containerName - } - container := mustGetContainer(g, os, tt.deploymentName, containerName) - g.Expect(container).Should(HavePathValueEqual(PathValue{"image", tt.want})) - } - } -} - -func TestManifestGenerateGateways(t *testing.T) { - g := NewWithT(t) - - flags := "-s components.ingressGateways.[0].k8s.resources.requests.memory=999Mi " + - "-s components.ingressGateways.[name:user-ingressgateway].k8s.resources.requests.cpu=555m" - - objss, err := runManifestCommands("gateways", flags, liveCharts, nil) - if err != nil { - t.Fatal(err) - } - - for _, objs := range objss { - g.Expect(objs.kind(name.HPAStr).size()).Should(Equal(3)) - g.Expect(objs.kind(name.PDBStr).size()).Should(Equal(3)) - g.Expect(objs.kind(name.ServiceStr).labels("istio=ingressgateway").size()).Should(Equal(3)) - g.Expect(objs.kind(name.RoleStr).nameMatches(".*gateway.*").size()).Should(Equal(3)) - g.Expect(objs.kind(name.RoleBindingStr).nameMatches(".*gateway.*").size()).Should(Equal(3)) - g.Expect(objs.kind(name.SAStr).nameMatches(".*gateway.*").size()).Should(Equal(3)) - - dobj := mustGetDeployment(g, objs, "istio-ingressgateway") - d := dobj.Unstructured() - c := dobj.Container("istio-proxy") - g.Expect(d).Should(HavePathValueContain(PathValue{"metadata.labels", toMap("aaa:aaa-val,bbb:bbb-val")})) - g.Expect(c).Should(HavePathValueEqual(PathValue{"resources.requests.cpu", "111m"})) - g.Expect(c).Should(HavePathValueEqual(PathValue{"resources.requests.memory", "999Mi"})) - - dobj = mustGetDeployment(g, objs, "user-ingressgateway") - d = dobj.Unstructured() - c = dobj.Container("istio-proxy") - g.Expect(d).Should(HavePathValueContain(PathValue{"metadata.labels", toMap("ccc:ccc-val,ddd:ddd-val")})) - g.Expect(c).Should(HavePathValueEqual(PathValue{"resources.requests.cpu", "555m"})) - g.Expect(c).Should(HavePathValueEqual(PathValue{"resources.requests.memory", "888Mi"})) - - dobj = mustGetDeployment(g, objs, "ilb-gateway") - d = dobj.Unstructured() - c = dobj.Container("istio-proxy") - s := mustGetService(g, objs, "ilb-gateway").Unstructured() - g.Expect(d).Should(HavePathValueContain(PathValue{"metadata.labels", toMap("app:istio-ingressgateway,istio:ingressgateway,release: istio")})) - g.Expect(c).Should(HavePathValueEqual(PathValue{"resources.requests.cpu", "333m"})) - g.Expect(c).Should(HavePathValueEqual(PathValue{"env.[name:PILOT_CERT_PROVIDER].value", "foobar"})) - g.Expect(s).Should(HavePathValueContain(PathValue{"metadata.annotations", toMap("cloud.google.com/load-balancer-type: internal")})) - g.Expect(s).Should(HavePathValueContain(PathValue{"spec.ports.[0]", portVal("grpc-pilot-mtls", 15011, -1)})) - g.Expect(s).Should(HavePathValueContain(PathValue{"spec.ports.[1]", portVal("tcp-citadel-grpc-tls", 8060, 8060)})) - g.Expect(s).Should(HavePathValueContain(PathValue{"spec.ports.[2]", portVal("tcp-dns", 5353, -1)})) - - for _, o := range objs.kind(name.HPAStr).objSlice { - ou := o.Unstructured() - g.Expect(ou).Should(HavePathValueEqual(PathValue{"spec.minReplicas", int64(1)})) - g.Expect(ou).Should(HavePathValueEqual(PathValue{"spec.maxReplicas", int64(5)})) - } - - checkRoleBindingsReferenceRoles(g, objs) - } -} - -func TestManifestGenerateWithDuplicateMutatingWebhookConfig(t *testing.T) { - testResourceFile := "duplicate_mwc" - - testCases := []struct { - name string - force bool - assertFunc func(g *WithT, objs *ObjectSet, err error) - }{ - { - name: "Duplicate MutatingWebhookConfiguration should be allowed when --force is enabled", - force: true, - assertFunc: func(g *WithT, objs *ObjectSet, err error) { - g.Expect(err).Should(BeNil()) - g.Expect(objs.kind(name.MutatingWebhookConfigurationStr).size()).Should(Equal(2)) - }, - }, - { - name: "Duplicate MutatingWebhookConfiguration should not be allowed when --force is disabled", - force: false, - assertFunc: func(g *WithT, objs *ObjectSet, err error) { - g.Expect(err.Error()).To(ContainSubstring("Webhook overlaps with others")) - g.Expect(objs).Should(BeNil()) - }, - }, - } - - recreateSimpleTestEnv() - - rs, err := readFile(filepath.Join(testDataDir, "input-extra-resources", testResourceFile+".yaml")) - if err != nil { - t.Fatal(err) - } - - err = writeFile(filepath.Join(env.IstioSrc, helm.OperatorSubdirFilePath+"/"+testIstioDiscoveryChartPath+"/"+testResourceFile+".yaml"), []byte(rs)) - if err != nil { - t.Fatal(err) - } - - t.Cleanup(func() { - removeFile(filepath.Join(env.IstioSrc, helm.OperatorSubdirFilePath+"/"+testIstioDiscoveryChartPath+"/"+testResourceFile+".yaml")) - }) - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - g := NewWithT(t) - objs, err := fakeControllerReconcile(testResourceFile, liveCharts, &helmreconciler.Options{Force: tc.force, SkipPrune: true}) - tc.assertFunc(g, objs, err) - }) - } -} - -func TestManifestGenerateIstiodRemote(t *testing.T) { - g := NewWithT(t) - - objss, err := runManifestCommands("istiod_remote", "", liveCharts, nil) - if err != nil { - t.Fatal(err) - } - - for _, objs := range objss { - // check core CRDs exists - g.Expect(objs.kind(name.CRDStr).nameEquals("destinationrules.networking.istio.io")).Should(Not(BeNil())) - g.Expect(objs.kind(name.CRDStr).nameEquals("gateways.networking.istio.io")).Should(Not(BeNil())) - g.Expect(objs.kind(name.CRDStr).nameEquals("sidecars.networking.istio.io")).Should(Not(BeNil())) - g.Expect(objs.kind(name.CRDStr).nameEquals("virtualservices.networking.istio.io")).Should(Not(BeNil())) - g.Expect(objs.kind(name.CRDStr).nameEquals("adapters.config.istio.io")).Should(BeNil()) - g.Expect(objs.kind(name.CRDStr).nameEquals("authorizationpolicies.security.istio.io")).Should(Not(BeNil())) - - g.Expect(objs.kind(name.ClusterRoleStr).nameEquals("istiod-dubbo-system")).Should(Not(BeNil())) - g.Expect(objs.kind(name.ClusterRoleStr).nameEquals("istio-reader-dubbo-system")).Should(Not(BeNil())) - g.Expect(objs.kind(name.ClusterRoleBindingStr).nameEquals("istiod-dubbo-system")).Should(Not(BeNil())) - g.Expect(objs.kind(name.ClusterRoleBindingStr).nameEquals("istio-reader-dubbo-system")).Should(Not(BeNil())) - g.Expect(objs.kind(name.CMStr).nameEquals("istio-sidecar-injector")).Should(Not(BeNil())) - g.Expect(objs.kind(name.ServiceStr).nameEquals("istiod")).Should(Not(BeNil())) - g.Expect(objs.kind(name.SAStr).nameEquals("istio-reader-service-account")).Should(Not(BeNil())) - g.Expect(objs.kind(name.SAStr).nameEquals("istiod-service-account")).Should(Not(BeNil())) - - mwc := mustGetMutatingWebhookConfiguration(g, objs, "istio-sidecar-injector").Unstructured() - g.Expect(mwc).Should(HavePathValueEqual(PathValue{"webhooks.[0].clientConfig.url", "https://xxx:15017/inject"})) - - ep := mustGetEndpoint(g, objs, "istiod-remote").Unstructured() - g.Expect(ep).Should(HavePathValueEqual(PathValue{"subsets.[0].addresses.[0]", endpointSubsetAddressVal("", "169.10.112.88", "")})) - g.Expect(ep).Should(HavePathValueContain(PathValue{"subsets.[0].ports.[0]", portVal("tcp-istiod", 15012, -1)})) - - checkClusterRoleBindingsReferenceRoles(g, objs) - } -} - -func TestManifestGenerateAllOff(t *testing.T) { - g := NewWithT(t) - m, _, err := generateManifest("all_off", "", liveCharts, nil) - if err != nil { - t.Fatal(err) - } - objs, err := parseObjectSetFromManifest(m) - if err != nil { - t.Fatal(err) - } - g.Expect(objs.size()).Should(Equal(0)) -} - -func TestManifestGenerateFlagsMinimalProfile(t *testing.T) { - g := NewWithT(t) - // Change profile from empty to minimal using flag. - m, _, err := generateManifest("empty", "-s profile=minimal", liveCharts, []string{"templates/deployment.yaml"}) - if err != nil { - t.Fatal(err) - } - objs, err := parseObjectSetFromManifest(m) - if err != nil { - t.Fatal(err) - } - // minimal profile always has istiod, empty does not. - mustGetDeployment(g, objs, "istiod") -} - -func TestManifestGenerateFlagsSetHubTag(t *testing.T) { - g := NewWithT(t) - m, _, err := generateManifest("minimal", "-s hub=foo -s tag=bar", liveCharts, []string{"templates/deployment.yaml"}) - if err != nil { - t.Fatal(err) - } - objs, err := parseObjectSetFromManifest(m) - if err != nil { - t.Fatal(err) - } - - dobj := mustGetDeployment(g, objs, "istiod") - - c := dobj.Container("discovery") - g.Expect(c).Should(HavePathValueEqual(PathValue{"image", "foo/dubbo-pilot:bar"})) -} - -func TestManifestGenerateFlagsSetValues(t *testing.T) { - g := NewWithT(t) - m, _, err := generateManifest("default", "-s values.gateways.istio-ingressgateway.enabled=true -s values.global.proxy.image=myproxy -s values.global.proxy.includeIPRanges=172.30.0.0/16,172.21.0.0/16", liveCharts, - []string{"templates/deployment.yaml", "templates/istiod-injector-configmap.yaml"}) - if err != nil { - t.Fatal(err) - } - objs, err := parseObjectSetFromManifest(m) - if err != nil { - t.Fatal(err) - } - dobj := mustGetDeployment(g, objs, "istio-ingressgateway") - - c := dobj.Container("istio-proxy") - g.Expect(c).Should(HavePathValueEqual(PathValue{"image", "apache/myproxy:latest"})) - - cm := objs.kind("ConfigMap").nameEquals("istio-sidecar-injector").Unstructured() - // TODO: change values to some nicer format rather than text block. - g.Expect(cm).Should(HavePathValueMatchRegex(PathValue{"data.values", `.*"includeIPRanges"\: "172\.30\.0\.0/16,172\.21\.0\.0/16".*`})) -} - -func TestManifestGenerateFlags(t *testing.T) { - flagOutputDir := createTempDirOrFail(t, "flag-output") - flagOutputValuesDir := createTempDirOrFail(t, "flag-output-values") - runTestGroup(t, testGroup{ - //{ - // desc: "all_on", - // diffIgnore: "ConfigMap:*:istio, ClusterRole::dubbo-system$, ClusterRoleBinding::", - // showOutputFileInPullRequest: true, - //}, - { - desc: "flag_values_enable_egressgateway", - diffSelect: "Service:*:istio-egressgateway", - fileSelect: []string{"templates/service.yaml"}, - flags: "--set values.gateways.istio-egressgateway.enabled=true", - noInput: true, - }, - { - desc: "flag_output", - flags: "-o " + flagOutputDir, - diffSelect: "Deployment:*:istiod", - fileSelect: []string{"templates/deployment.yaml"}, - outputDir: flagOutputDir, - }, - //{ - // desc: "flag_output_set_values", - // diffSelect: "Deployment:*:istio-ingressgateway", - // flags: "-s values.global.proxy.image=mynewproxy -o " + flagOutputValuesDir, - // fileSelect: []string{"templates/deployment.yaml"}, - // outputDir: flagOutputValuesDir, - // noInput: true, - //}, - { - desc: "flag_force", - diffSelect: "no:resources:selected", - fileSelect: []string{""}, - flags: "--force", - }, - }) - removeDirOrFail(t, flagOutputDir) - removeDirOrFail(t, flagOutputValuesDir) -} - -func TestManifestGeneratePilot(t *testing.T) { - runTestGroup(t, testGroup{ - //{ - // desc: "pilot_default", - // diffIgnore: "CustomResourceDefinition:*:*,ConfigMap:*:istio", - //}, - { - desc: "pilot_k8s_settings", - diffSelect: "Deployment:*:istiod,HorizontalPodAutoscaler:*:istiod", - fileSelect: []string{"templates/deployment.yaml", "templates/autoscale.yaml"}, - }, - { - desc: "pilot_override_values", - diffSelect: "Deployment:*:istiod,HorizontalPodAutoscaler:*:istiod", - fileSelect: []string{"templates/deployment.yaml", "templates/autoscale.yaml"}, - }, - { - desc: "pilot_override_kubernetes", - diffSelect: "Deployment:*:istiod, Service:*:istiod,MutatingWebhookConfiguration:*:istio-sidecar-injector,ClusterRoleBinding::istio-reader-dubbo-system", - fileSelect: []string{ - "templates/deployment.yaml", "templates/mutatingwebhook.yaml", - "templates/service.yaml", "templates/reader-clusterrolebinding.yaml", "templates/clusterrolebinding.yaml", - }, - }, - // TODO https://github.com/istio/istio/issues/22347 this is broken for overriding things to default value - // This can be seen from REGISTRY_ONLY not applying - { - desc: "pilot_merge_meshconfig", - diffSelect: "ConfigMap:*:istio$", - fileSelect: []string{"templates/configmap.yaml"}, - }, - { - desc: "pilot_disable_tracing", - diffSelect: "ConfigMap:*:istio$", - }, - { - desc: "deprecated_autoscaling_k8s_spec", - diffSelect: "HorizontalPodAutoscaler:*:istiod,HorizontalPodAutoscaler:*:istio-ingressgateway", - fileSelect: []string{"templates/autoscale.yaml"}, - }, - }) -} - -func TestManifestGenerateGateway(t *testing.T) { - runTestGroup(t, testGroup{ - //{ - // desc: "ingressgateway_k8s_settings", - // diffSelect: "Deployment:*:istio-ingressgateway, Service:*:istio-ingressgateway", - //}, - }) -} - -// TestManifestGenerateHelmValues tests whether enabling components through the values passthrough interface works as -// expected i.e. without requiring enablement also in IstioOperator API. -func TestManifestGenerateHelmValues(t *testing.T) { - runTestGroup(t, testGroup{ - { - desc: "helm_values_enablement", - diffSelect: "Deployment:*:istio-egressgateway, Service:*:istio-egressgateway", - }, - }) -} - -func TestManifestGenerateOrdered(t *testing.T) { - // Since this is testing the special case of stable YAML output order, it - // does not use the established test group pattern - inPath := filepath.Join(testDataDir, "input/all_on.yaml") - got1, err := runManifestGenerate([]string{inPath}, "", snapshotCharts, nil) - if err != nil { - t.Fatal(err) - } - got2, err := runManifestGenerate([]string{inPath}, "", snapshotCharts, nil) - if err != nil { - t.Fatal(err) - } - - if got1 != got2 { - fmt.Printf("%s", util.YAMLDiff(got1, got2)) - t.Errorf("stable_manifest: Manifest generation is not producing stable text output.") - } -} - -func TestManifestGenerateFlagAliases(t *testing.T) { - inPath := filepath.Join(testDataDir, "input/all_on.yaml") - gotSet, err := runManifestGenerate([]string{inPath}, "--set revision=foo", snapshotCharts, []string{"templates/deployment.yaml"}) - if err != nil { - t.Fatal(err) - } - gotAlias, err := runManifestGenerate([]string{inPath}, "--revision=foo", snapshotCharts, []string{"templates/deployment.yaml"}) - if err != nil { - t.Fatal(err) - } - - if gotAlias != gotSet { - t.Errorf("Flag aliases not producing same output: with --set: \n\n%s\n\nWith alias:\n\n%s\nDiff:\n\n%s\n", - gotSet, gotAlias, util.YAMLDiff(gotSet, gotAlias)) - } -} - -func TestMultiICPSFiles(t *testing.T) { - inPathBase := filepath.Join(testDataDir, "input/all_off.yaml") - inPathOverride := filepath.Join(testDataDir, "input/helm_values_enablement.yaml") - got, err := runManifestGenerate([]string{inPathBase, inPathOverride}, "", snapshotCharts, []string{"templates/deployment.yaml", "templates/service.yaml"}) - if err != nil { - t.Fatal(err) - } - outPath := filepath.Join(testDataDir, "output/helm_values_enablement"+goldenFileSuffixHideChangesInReview) - - want, err := readFile(outPath) - if err != nil { - t.Fatal(err) - } - diffSelect := "Deployment:*:istio-egressgateway, Service:*:istio-egressgateway" - got, err = compare.FilterManifest(got, diffSelect, "") - if err != nil { - t.Errorf("error selecting from output manifest: %v", err) - } - diff := compare.YAMLCmp(got, want) - if diff != "" { - t.Errorf("`manifest generate` diff = %s", diff) - } -} - -func TestBareSpec(t *testing.T) { - inPathBase := filepath.Join(testDataDir, "input/bare_spec.yaml") - _, err := runManifestGenerate([]string{inPathBase}, "", liveCharts, []string{"templates/deployment.yaml"}) - if err != nil { - t.Fatal(err) - } -} - -func TestMultipleSpecOneFile(t *testing.T) { - inPathBase := filepath.Join(testDataDir, "input/multiple_iops.yaml") - _, err := runManifestGenerate([]string{inPathBase}, "", liveCharts, []string{"templates/deployment.yaml"}) - if !strings.Contains(err.Error(), "contains multiple IstioOperator CRs, only one per file is supported") { - t.Fatalf("got %v, expected error for file with multiple IOPs", err) - } -} - -func TestBareValues(t *testing.T) { - inPathBase := filepath.Join(testDataDir, "input/bare_values.yaml") - // As long as the generate doesn't panic, we pass it. bare_values.yaml doesn't - // overlay well because JSON doesn't handle null values, and our charts - // don't expect values to be blown away. - _, _ = runManifestGenerate([]string{inPathBase}, "", liveCharts, []string{"templates/deployment.yaml"}) -} - -func TestBogusControlPlaneSec(t *testing.T) { - inPathBase := filepath.Join(testDataDir, "input/bogus_cps.yaml") - _, err := runManifestGenerate([]string{inPathBase}, "", liveCharts, []string{"templates/deployment.yaml"}) - if err != nil { - t.Fatal(err) - } -} - -func TestInstallPackagePath(t *testing.T) { - serverDir := t.TempDir() - if err := tgz.Create(string(liveCharts), filepath.Join(serverDir, testTGZFilename)); err != nil { - t.Fatal(err) - } - srv := httpserver.NewServer(serverDir) - runTestGroup(t, testGroup{ - { - // Use some arbitrary small test input (pilot only) since we are testing the local filesystem code here, not - // manifest generation. - desc: "install_package_path", - diffSelect: "Deployment:*:istiod", - flags: "--set installPackagePath=" + string(liveCharts), - }, - { - // Specify both charts and profile from local filesystem. - desc: "install_package_path", - diffSelect: "Deployment:*:istiod", - flags: fmt.Sprintf("--set installPackagePath=%s --set profile=%s/profiles/default.yaml", string(liveCharts), string(liveCharts)), - }, - { - // --force is needed for version mismatch. - desc: "install_package_path", - diffSelect: "Deployment:*:istiod", - flags: "--force --set installPackagePath=" + srv.URL() + "/" + testTGZFilename, - }, - }) -} - -// TestTrailingWhitespace ensures there are no trailing spaces in the manifests -// This is important because `kubectl edit` and other commands will get escaped if they are present -// making it hard to read/edit -func TestTrailingWhitespace(t *testing.T) { - got, err := runManifestGenerate([]string{}, "--set values.gateways.istio-egressgateway.enabled=true", liveCharts, nil) - if err != nil { - t.Fatal(err) - } - lines := strings.Split(got, "\n") - for i, l := range lines { - if strings.HasSuffix(l, " ") { - t.Errorf("Line %v has a trailing space: [%v]. Context: %v", i, l, strings.Join(lines[i-25:i+25], "\n")) - } - } -} - -func validateReferentialIntegrity(t *testing.T, objs object.K8sObjects, cname string, deploymentSelector map[string]string) { - t.Run(cname, func(t *testing.T) { - deployment := mustFindObject(t, objs, cname, name.DeploymentStr) - service := mustFindObject(t, objs, cname, name.ServiceStr) - pdb := mustFindObject(t, objs, cname, name.PDBStr) - hpa := mustFindObject(t, objs, cname, name.HPAStr) - podLabels := mustGetLabels(t, deployment, "spec.template.metadata.labels") - // Check all selectors align - mustSelect(t, mustGetLabels(t, pdb, "spec.selector.matchLabels"), podLabels) - mustSelect(t, mustGetLabels(t, service, "spec.selector"), podLabels) - mustSelect(t, mustGetLabels(t, deployment, "spec.selector.matchLabels"), podLabels) - if hpaName := mustGetPath(t, hpa, "spec.scaleTargetRef.name"); cname != hpaName { - t.Fatalf("HPA does not match deployment: %v != %v", cname, hpaName) - } - - serviceAccountName := mustGetPath(t, deployment, "spec.template.spec.serviceAccountName").(string) - mustFindObject(t, objs, serviceAccountName, name.SAStr) - - // Check we aren't changing immutable fields. This only matters for in place upgrade (non revision) - // This one is not a selector, it must be an exact match - if sel := mustGetLabels(t, deployment, "spec.selector.matchLabels"); !reflect.DeepEqual(deploymentSelector, sel) { - t.Fatalf("Depployment selectors are immutable, but changed since 1.5. Was %v, now is %v", deploymentSelector, sel) - } - }) -} - -// This test enforces that objects that reference other objects do so properly, such as Service selecting deployment -func TestConfigSelectors(t *testing.T) { - selectors := []string{ - "templates/deployment.yaml", - "templates/service.yaml", - "templates/poddisruptionbudget.yaml", - "templates/autoscale.yaml", - "templates/serviceaccount.yaml", - } - got, err := runManifestGenerate([]string{}, "--set values.gateways.istio-ingressgateway.enabled=true --set values.gateways.istio-egressgateway.enabled=true", liveCharts, selectors) - if err != nil { - t.Fatal(err) - } - objs, err := object.ParseK8sObjectsFromYAMLManifest(got) - if err != nil { - t.Fatal(err) - } - gotRev, e := runManifestGenerate([]string{}, "--set revision=canary", liveCharts, selectors) - if e != nil { - t.Fatal(e) - } - objsRev, err := object.ParseK8sObjectsFromYAMLManifest(gotRev) - if err != nil { - t.Fatal(err) - } - - istiod15Selector := map[string]string{ - "istio": "pilot", - } - istiodCanary16Selector := map[string]string{ - "app": "istiod", - "istio.io/rev": "canary", - } - ingress15Selector := map[string]string{ - "app": "istio-ingressgateway", - "istio": "ingressgateway", - } - egress15Selector := map[string]string{ - "app": "istio-egressgateway", - "istio": "egressgateway", - } - - // Validate references within the same deployment - validateReferentialIntegrity(t, objs, "istiod", istiod15Selector) - validateReferentialIntegrity(t, objs, "istio-ingressgateway", ingress15Selector) - validateReferentialIntegrity(t, objs, "istio-egressgateway", egress15Selector) - validateReferentialIntegrity(t, objsRev, "istiod-canary", istiodCanary16Selector) - - t.Run("cross revision", func(t *testing.T) { - // Istiod revisions have complicated cross revision implications. We should assert these are correct - // First we fetch all the objects for our default install - cname := "istiod" - deployment := mustFindObject(t, objs, cname, name.DeploymentStr) - service := mustFindObject(t, objs, cname, name.ServiceStr) - pdb := mustFindObject(t, objs, cname, name.PDBStr) - podLabels := mustGetLabels(t, deployment, "spec.template.metadata.labels") - - // Next we fetch all the objects for a revision install - nameRev := "istiod-canary" - deploymentRev := mustFindObject(t, objsRev, nameRev, name.DeploymentStr) - hpaRev := mustFindObject(t, objsRev, nameRev, name.HPAStr) - serviceRev := mustFindObject(t, objsRev, nameRev, name.ServiceStr) - pdbRev := mustFindObject(t, objsRev, nameRev, name.PDBStr) - podLabelsRev := mustGetLabels(t, deploymentRev, "spec.template.metadata.labels") - - // Make sure default and revisions do not cross - mustNotSelect(t, mustGetLabels(t, serviceRev, "spec.selector"), podLabels) - mustNotSelect(t, mustGetLabels(t, service, "spec.selector"), podLabelsRev) - mustNotSelect(t, mustGetLabels(t, pdbRev, "spec.selector.matchLabels"), podLabels) - mustNotSelect(t, mustGetLabels(t, pdb, "spec.selector.matchLabels"), podLabelsRev) - - // Make sure the scaleTargetRef points to the correct Deployment - if hpaName := mustGetPath(t, hpaRev, "spec.scaleTargetRef.name"); nameRev != hpaName { - t.Fatalf("HPA does not match deployment: %v != %v", nameRev, hpaName) - } - - // Check selection of previous versions . This only matters for in place upgrade (non revision) - podLabels15 := map[string]string{ - "app": "istiod", - "istio": "pilot", - } - mustSelect(t, mustGetLabels(t, service, "spec.selector"), podLabels15) - mustNotSelect(t, mustGetLabels(t, serviceRev, "spec.selector"), podLabels15) - mustSelect(t, mustGetLabels(t, pdb, "spec.selector.matchLabels"), podLabels15) - mustNotSelect(t, mustGetLabels(t, pdbRev, "spec.selector.matchLabels"), podLabels15) - }) -} - -// TestLDFlags checks whether building mesh command with -// -ldflags "-X istio.io/pkg/version.buildHub=myhub -X istio.io/pkg/version.buildVersion=mytag" -// results in these values showing up in a generated manifest. -func TestLDFlags(t *testing.T) { - tmpHub, tmpTag := version.DockerInfo.Hub, version.DockerInfo.Tag - defer func() { - version.DockerInfo.Hub, version.DockerInfo.Tag = tmpHub, tmpTag - }() - version.DockerInfo.Hub = "testHub" - version.DockerInfo.Tag = "testTag" - l := clog.NewConsoleLogger(os.Stdout, os.Stderr, installerScope) - _, iop, err := manifest.GenerateConfig(nil, []string{"installPackagePath=" + string(liveCharts)}, true, nil, l) - if err != nil { - t.Fatal(err) - } - if iop.Spec.Hub != version.DockerInfo.Hub || iop.Spec.Tag.GetStringValue() != version.DockerInfo.Tag { - t.Fatalf("DockerInfoHub, DockerInfoTag got: %s,%s, want: %s, %s", iop.Spec.Hub, iop.Spec.Tag, version.DockerInfo.Hub, version.DockerInfo.Tag) - } -} - -func runTestGroup(t *testing.T, tests testGroup) { - for _, tt := range tests { - tt := tt - t.Run(tt.desc, func(t *testing.T) { - t.Parallel() - inPath := filepath.Join(testDataDir, "input", tt.desc+".yaml") - outputSuffix := goldenFileSuffixHideChangesInReview - if tt.showOutputFileInPullRequest { - outputSuffix = goldenFileSuffixShowChangesInReview - } - outPath := filepath.Join(testDataDir, "output", tt.desc+outputSuffix) - - var filenames []string - if !tt.noInput { - filenames = []string{inPath} - } - - csource := snapshotCharts - if tt.chartSource != "" { - csource = tt.chartSource - } - got, err := runManifestGenerate(filenames, tt.flags, csource, tt.fileSelect) - if err != nil { - t.Fatal(err) - } - - if tt.outputDir != "" { - got, err = util.ReadFilesWithFilter(tt.outputDir, func(fileName string) bool { - return strings.HasSuffix(fileName, ".yaml") - }) - if err != nil { - t.Fatal(err) - } - } - - diffSelect := "*:*:*" - if tt.diffSelect != "" { - diffSelect = tt.diffSelect - got, err = compare.FilterManifest(got, diffSelect, "") - if err != nil { - t.Errorf("error selecting from output manifest: %v", err) - } - } - - tutil.RefreshGoldenFile(t, []byte(got), outPath) - - want, err := readFile(outPath) - if err != nil { - t.Fatal(err) - } - - if got != want { - diff, err := compare.ManifestDiffWithRenameSelectIgnore(got, want, - "", diffSelect, tt.diffIgnore, false) - if err != nil { - t.Fatal(err) - } - if diff != "" { - t.Fatalf("%s: got:\n%s\nwant:\n%s\n(-got, +want)\n%s\n", tt.desc, "", "", diff) - } - t.Fatalf(cmp.Diff(got, want)) - } - }) - } -} - -// nolint: unparam -func generateManifest(inFile, flags string, chartSource chartSourceType, fileSelect []string) (string, object.K8sObjects, error) { - inPath := filepath.Join(testDataDir, "input", inFile+".yaml") - manifest, err := runManifestGenerate([]string{inPath}, flags, chartSource, fileSelect) - if err != nil { - return "", nil, fmt.Errorf("error %s: %s", err, manifest) - } - objs, err := object.ParseK8sObjectsFromYAMLManifest(manifest) - return manifest, objs, err -} - -// runManifestGenerate runs the manifest generate command. If filenames is set, passes the given filenames as -f flag, -// flags is passed to the command verbatim. If you set both flags and path, make sure to not use -f in flags. -func runManifestGenerate(filenames []string, flags string, chartSource chartSourceType, fileSelect []string) (string, error) { - return runManifestCommand("generate", filenames, flags, chartSource, fileSelect) -} - -func mustGetWebhook(t test.Failer, obj object.K8sObject) []v1.MutatingWebhook { - t.Helper() - path := mustGetPath(t, obj, "webhooks") - by, err := json.Marshal(path) - if err != nil { - t.Fatal(err) - } - var mwh []v1.MutatingWebhook - if err := json.Unmarshal(by, &mwh); err != nil { - t.Fatal(err) - } - return mwh -} - -func getWebhooks(t *testing.T, setFlags string, webhookName string) []v1.MutatingWebhook { - t.Helper() - got, err := runManifestGenerate([]string{}, setFlags, liveCharts, []string{"templates/mutatingwebhook.yaml"}) - if err != nil { - t.Fatal(err) - } - objs, err := object.ParseK8sObjectsFromYAMLManifest(got) - if err != nil { - t.Fatal(err) - } - return mustGetWebhook(t, mustFindObject(t, objs, webhookName, name.MutatingWebhookConfigurationStr)) -} - -func getWebhooksFromYaml(t *testing.T, yml string) []v1.MutatingWebhook { - t.Helper() - objs, err := object.ParseK8sObjectsFromYAMLManifest(yml) - if err != nil { - t.Fatal(err) - } - if len(objs) != 1 { - t.Fatal("expected one webhook") - } - return mustGetWebhook(t, *objs[0]) -} - -type LabelSet struct { - namespace, pod klabels.Set -} - -func mergeWebhooks(whs ...[]v1.MutatingWebhook) []v1.MutatingWebhook { - res := []v1.MutatingWebhook{} - for _, wh := range whs { - res = append(res, wh...) - } - return res -} - -const ( - // istioctl manifest generate --set values.sidecarInjectorWebhook.useLegacySelectors=true - legacyDefaultInjector = ` -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - name: istio-sidecar-injector -webhooks: -- name: sidecar-injector.istio.io - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: "/inject" - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] - namespaceSelector: - matchLabels: - istio-injection: enabled - objectSelector: - matchExpressions: - - key: "sidecar.istio.io/inject" - operator: NotIn - values: - - "false" -` - - // istioctl manifest generate --set values.sidecarInjectorWebhook.useLegacySelectors=true --set revision=canary - legacyRevisionInjector = ` -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - name: istio-sidecar-injector-canary -webhooks: -- name: sidecar-injector.istio.io - clientConfig: - service: - name: istiod-canary - namespace: dubbo-system - path: "/inject" - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: DoesNotExist - - key: istio.io/rev - operator: In - values: - - canary - objectSelector: - matchExpressions: - - key: "sidecar.istio.io/inject" - operator: NotIn - values: - - "false" -` -) - -// This test checks the mutating webhook selectors behavior, especially with interaction with revisions -func TestWebhookSelector(t *testing.T) { - // Setup various labels to be tested - empty := klabels.Set{} - revLabel := klabels.Set{"istio.io/rev": "canary"} - legacyAndRevLabel := klabels.Set{"istio-injection": "enabled", "istio.io/rev": "canary"} - legacyDisabledAndRevLabel := klabels.Set{"istio-injection": "disabled", "istio.io/rev": "canary"} - legacyLabel := klabels.Set{"istio-injection": "enabled"} - legacyLabelDisabled := klabels.Set{"istio-injection": "disabled"} - - objEnabled := klabels.Set{"sidecar.istio.io/inject": "true"} - objDisable := klabels.Set{"sidecar.istio.io/inject": "false"} - objEnabledAndRev := klabels.Set{"sidecar.istio.io/inject": "true", "istio.io/rev": "canary"} - objDisableAndRev := klabels.Set{"sidecar.istio.io/inject": "false", "istio.io/rev": "canary"} - - defaultWebhook := getWebhooks(t, "", "istio-sidecar-injector") - revWebhook := getWebhooks(t, "--set revision=canary", "istio-sidecar-injector-canary") - autoWebhook := getWebhooks(t, "--set values.sidecarInjectorWebhook.enableNamespacesByDefault=true", "istio-sidecar-injector") - legacyWebhook := getWebhooksFromYaml(t, legacyDefaultInjector) - legacyRevWebhook := getWebhooksFromYaml(t, legacyRevisionInjector) - - // predicate is used to filter out "obvious" test cases, to avoid enumerating all cases - // nolint: unparam - predicate := func(ls LabelSet) (string, bool) { - if ls.namespace.Get("istio-injection") == "disabled" { - return "", true - } - if ls.pod.Get("sidecar.istio.io/inject") == "false" { - return "", true - } - return "", false - } - - // We test the cross product namespace and pod labels: - // 1. revision label (istio.io/rev) - // 2. inject label true (istio-injection on namespace, sidecar.istio.io/inject on pod) - // 3. inject label false - // 4. inject label true and revision label - // 5. inject label false and revision label - // 6. no label - // However, we filter out all the disable cases, leaving us with a reasonable number of cases - testLabels := []LabelSet{} - for _, namespaceLabel := range []klabels.Set{empty, revLabel, legacyLabel, legacyLabelDisabled, legacyAndRevLabel, legacyDisabledAndRevLabel} { - for _, podLabel := range []klabels.Set{empty, revLabel, objEnabled, objDisable, objEnabledAndRev, objDisableAndRev} { - testLabels = append(testLabels, LabelSet{namespaceLabel, podLabel}) - } - } - type assertion struct { - namespaceLabel klabels.Set - objectLabel klabels.Set - match string - } - baseAssertions := []assertion{ - {empty, empty, ""}, - {empty, revLabel, "istiod-canary"}, - {empty, objEnabled, "istiod"}, - {empty, objEnabledAndRev, "istiod-canary"}, - {revLabel, empty, "istiod-canary"}, - {revLabel, revLabel, "istiod-canary"}, - {revLabel, objEnabled, "istiod-canary"}, - {revLabel, objEnabledAndRev, "istiod-canary"}, - {legacyLabel, empty, "istiod"}, - {legacyLabel, objEnabled, "istiod"}, - {legacyAndRevLabel, empty, "istiod"}, - {legacyAndRevLabel, objEnabled, "istiod"}, - - // The behavior of these is a bit odd; they are explicitly selecting a revision but getting - // the default Unfortunately, the legacy webhook selectors would select these, cause - // duplicate injection, so we defer to the namespace label. - {legacyLabel, revLabel, "istiod"}, - {legacyAndRevLabel, revLabel, "istiod"}, - {legacyLabel, objEnabledAndRev, "istiod"}, - {legacyAndRevLabel, objEnabledAndRev, "istiod"}, - } - cases := []struct { - name string - webhooks []v1.MutatingWebhook - checks []assertion - }{ - { - name: "base", - webhooks: mergeWebhooks(defaultWebhook, revWebhook), - checks: baseAssertions, - }, - { - // This is exactly the same as above, but empty/empty matches - name: "auto injection", - webhooks: mergeWebhooks(autoWebhook, revWebhook), - checks: append([]assertion{{empty, empty, "istiod"}}, baseAssertions...), - }, - { - // Upgrade from a legacy webhook to a new revision based - // Note: we don't need non revision legacy -> non revision, since it will overwrite the webhook - name: "revision upgrade", - webhooks: mergeWebhooks(legacyWebhook, revWebhook), - checks: append([]assertion{ - {empty, objEnabled, ""}, // Legacy one requires namespace label - }, baseAssertions...), - }, - { - // Use new default webhook, while we still have a legacy revision one around. - name: "inplace upgrade", - webhooks: mergeWebhooks(defaultWebhook, legacyRevWebhook), - checks: append([]assertion{ - {empty, revLabel, ""}, // Legacy one requires namespace label - {empty, objEnabledAndRev, ""}, // Legacy one requires namespace label - }, baseAssertions...), - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - whs := tt.webhooks - for _, s := range testLabels { - t.Run(fmt.Sprintf("ns:%v pod:%v", s.namespace, s.pod), func(t *testing.T) { - found := "" - match := 0 - for i, wh := range whs { - sn := wh.ClientConfig.Service.Name - matches := selectorMatches(t, wh.NamespaceSelector, s.namespace) && selectorMatches(t, wh.ObjectSelector, s.pod) - if matches && found != "" { - // There must be exactly one match, or we will double inject. - t.Fatalf("matched multiple webhooks. Had %v, matched %v", found, sn) - } - if matches { - found = sn - match = i - } - } - // If our predicate can tell us the expected match, use that - if want, ok := predicate(s); ok { - if want != found { - t.Fatalf("expected webhook to go to service %q, found %q", want, found) - } - return - } - // Otherwise, look through our assertions for a matching one, and check that - for _, w := range tt.checks { - if w.namespaceLabel.String() == s.namespace.String() && w.objectLabel.String() == s.pod.String() { - if found != w.match { - if found != "" { - t.Fatalf("expected webhook to go to service %q, found %q (from match %d)\nNamespace selector: %v\nObject selector: %v)", - w.match, found, match, whs[match].NamespaceSelector.MatchExpressions, whs[match].ObjectSelector.MatchExpressions) - } else { - t.Fatalf("expected webhook to go to service %q, found %q", w.match, found) - } - } - return - } - } - // If none match, a test case is missing for the label set. - t.Fatalf("no assertion for namespace=%v pod=%v", s.namespace, s.pod) - }) - } - }) - } -} - -func selectorMatches(t *testing.T, selector *metav1.LabelSelector, labels klabels.Set) bool { - t.Helper() - // From webhook spec: "Default to the empty LabelSelector, which matches everything." - if selector == nil { - return true - } - s, err := metav1.LabelSelectorAsSelector(selector) - if err != nil { - t.Fatal(err) - } - return s.Matches(labels) -} - -func TestSidecarTemplate(t *testing.T) { - runTestGroup(t, testGroup{ - //{ - // desc: "sidecar_template", - // diffSelect: "ConfigMap:*:istio-sidecar-injector", - //}, - }) -} diff --git a/operator/cmd/mesh/manifest.go b/operator/cmd/mesh/manifest.go deleted file mode 100644 index 917e30b07..000000000 --- a/operator/cmd/mesh/manifest.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "github.com/spf13/cobra" - "istio.io/pkg/log" -) - -// ManifestCmd is a group of commands related to manifest generation, installation, diffing and migration. -func ManifestCmd(logOpts *log.Options) *cobra.Command { - mc := &cobra.Command{ - Use: "manifest", - Short: "Commands related to Istio manifests", - Long: "The manifest command generates and diffs Istio manifests.", - } - - mgcArgs := &ManifestGenerateArgs{} - mdcArgs := &manifestDiffArgs{} - - args := &RootArgs{} - - mgc := ManifestGenerateCmd(args, mgcArgs, logOpts) - mdc := manifestDiffCmd(args, mdcArgs) - ic := InstallCmd(logOpts) - - addFlags(mc, args) - addFlags(mgc, args) - addFlags(mdc, args) - - addManifestGenerateFlags(mgc, mgcArgs) - addManifestDiffFlags(mdc, mdcArgs) - - mc.AddCommand(mgc) - mc.AddCommand(mdc) - mc.AddCommand(ic) - - return mc -} diff --git a/operator/cmd/mesh/manifest_shared_test.go b/operator/cmd/mesh/manifest_shared_test.go deleted file mode 100644 index 204865f41..000000000 --- a/operator/cmd/mesh/manifest_shared_test.go +++ /dev/null @@ -1,366 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "bytes" - "context" - "fmt" - "os" - "path/filepath" - "strings" -) - -import ( - "istio.io/pkg/log" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/version" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - "sigs.k8s.io/controller-runtime/pkg/envtest" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/cache" - "github.com/apache/dubbo-go-pixiu/operator/pkg/controller/istiocontrolplane" - "github.com/apache/dubbo-go-pixiu/operator/pkg/helmreconciler" - "github.com/apache/dubbo-go-pixiu/operator/pkg/manifest" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -// cmdType is one of the commands used to generate and optionally apply a manifest. -type cmdType string - -const ( - // istioctl manifest generate - cmdGenerate cmdType = "istioctl manifest generate" - // istioctl install - cmdApply cmdType = "istioctl install" - // in-cluster controller - cmdController cmdType = "operator controller" -) - -// Golden output files add a lot of noise to pull requests. Use a unique suffix so -// we can hide them by default. This should match one of the `linuguist-generated=true` -// lines in istio.io/istio/.gitattributes. -const ( - goldenFileSuffixHideChangesInReview = ".golden.yaml" - goldenFileSuffixShowChangesInReview = ".golden-show-in-gh-pull-request.yaml" -) - -var ( - // By default, tests only run with manifest generate, since it doesn't require any external fake test environment. - testedManifestCmds = []cmdType{cmdGenerate} - // Only used if kubebuilder is installed. - testenv *envtest.Environment - testClient client.Client - testReconcileOperator *istiocontrolplane.ReconcileIstioOperator - - allNamespacedGVKs = append(helmreconciler.NamespacedResources(&version.Info{Major: "1", Minor: "25"}), - schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Endpoints"}) - // CRDs are not in the prune list, but must be considered for tests. - allClusterGVKs = append(helmreconciler.ClusterResources, - schema.GroupVersionKind{Group: "apiextensions.k8s.io", Version: "v1beta1", Kind: "CustomResourceDefinition"}) -) - -func init() { - if kubeBuilderInstalled() { - // TestMode is required to not wait in the go client for resources that will never be created in the test server. - helmreconciler.TestMode = true - // Add install and controller to the list of commands to run tests against. - testedManifestCmds = append(testedManifestCmds, cmdApply, cmdController) - } -} - -// recreateTestEnv (re)creates a kubebuilder fake API server environment. This is required for testing of the -// controller runtime, which is used in the operator. -func recreateTestEnv() error { - // If kubebuilder is installed, use that test env for apply and controller testing. - log.Infof("Recreating kubebuilder test environment\n") - - if testenv != nil { - testenv.Stop() - } - - var err error - testenv = &envtest.Environment{} - testRestConfig, err = testenv.Start() - if err != nil { - return err - } - - testK8Interface, err = kubernetes.NewForConfig(testRestConfig) - testRestConfig.QPS = 50 - testRestConfig.Burst = 100 - if err != nil { - return err - } - - s := scheme.Scheme - s.AddKnownTypes(v1alpha1.SchemeGroupVersion, &v1alpha1.IstioOperator{}) - - testClient, err = client.New(testRestConfig, client.Options{Scheme: s}) - if err != nil { - return err - } - - testReconcileOperator = istiocontrolplane.NewReconcileIstioOperator(testClient, nil, s) - return nil -} - -// recreateSimpleTestEnv mocks fake kube api server which relies on a simple object tracker -func recreateSimpleTestEnv() { - log.Infof("Creating simple test environment\n") - helmreconciler.TestMode = true - s := scheme.Scheme - s.AddKnownTypes(v1alpha1.SchemeGroupVersion, &v1alpha1.IstioOperator{}) - - testClient = fake.NewClientBuilder().WithScheme(s).Build() - testReconcileOperator = istiocontrolplane.NewReconcileIstioOperator(testClient, kube.NewFakeClient(), s) -} - -// runManifestCommands runs all testedManifestCmds commands with the given input IOP file, flags and chartSource. -// It returns an ObjectSet for each cmd type. -// nolint: unparam -func runManifestCommands(inFile, flags string, chartSource chartSourceType, fileSelect []string) (map[cmdType]*ObjectSet, error) { - out := make(map[cmdType]*ObjectSet) - for _, cmd := range testedManifestCmds { - log.Infof("\nRunning test command using %s\n", cmd) - switch cmd { - case cmdApply, cmdController: - if err := cleanTestCluster(); err != nil { - return nil, err - } - if err := fakeApplyExtraResources(inFile); err != nil { - return nil, err - } - default: - } - - var objs *ObjectSet - var err error - switch cmd { - case cmdGenerate: - m, _, err := generateManifest(inFile, flags, chartSource, fileSelect) - if err != nil { - return nil, err - } - objs, err = parseObjectSetFromManifest(m) - if err != nil { - return nil, err - } - case cmdApply: - objs, err = fakeApplyManifest(inFile, flags, chartSource) - case cmdController: - objs, err = fakeControllerReconcile(inFile, chartSource, nil) - default: - } - if err != nil { - return nil, err - } - out[cmd] = objs - } - - return out, nil -} - -// fakeApplyManifest runs istioctl install. -func fakeApplyManifest(inFile, flags string, chartSource chartSourceType) (*ObjectSet, error) { - inPath := filepath.Join(testDataDir, "input", inFile+".yaml") - manifest, err := runManifestCommand("install", []string{inPath}, flags, chartSource, nil) - if err != nil { - return nil, fmt.Errorf("error %s: %s", err, manifest) - } - return NewObjectSet(getAllIstioObjects()), nil -} - -// fakeApplyExtraResources applies any extra resources for the given test name. -func fakeApplyExtraResources(inFile string) error { - reconciler, err := helmreconciler.NewHelmReconciler(testClient, nil, nil, nil) - if err != nil { - return err - } - - if rs, err := readFile(filepath.Join(testDataDir, "input-extra-resources", inFile+".yaml")); err == nil { - if err := applyWithReconciler(reconciler, rs); err != nil { - return err - } - } - return nil -} - -func fakeControllerReconcile(inFile string, chartSource chartSourceType, opts *helmreconciler.Options) (*ObjectSet, error) { - c := kube.NewFakeClientWithVersion("25") - l := clog.NewDefaultLogger() - _, iop, err := manifest.GenerateConfig( - []string{inFileAbsolutePath(inFile)}, - []string{"installPackagePath=" + string(chartSource)}, - false, c, l) - if err != nil { - return nil, err - } - - iop.Spec.InstallPackagePath = string(chartSource) - - reconciler, err := helmreconciler.NewHelmReconciler(testClient, c, iop, opts) - if err != nil { - return nil, err - } - if err := fakeInstallOperator(reconciler, chartSource, iop); err != nil { - return nil, err - } - - if _, err := reconciler.Reconcile(); err != nil { - return nil, err - } - - return NewObjectSet(getAllIstioObjects()), nil -} - -// fakeInstallOperator installs the operator manifest resources into a cluster using the given reconciler. -// The installation is for testing with a kubebuilder fake cluster only, since no functional Deployment will be -// created. -func fakeInstallOperator(reconciler *helmreconciler.HelmReconciler, chartSource chartSourceType, iop *v1alpha1.IstioOperator) error { - ocArgs := &operatorCommonArgs{ - manifestsPath: string(chartSource), - istioNamespace: istioDefaultNamespace, - watchedNamespaces: istioDefaultNamespace, - operatorNamespace: operatorDefaultNamespace, - // placeholders, since the fake API server does not actually pull images and create pods. - hub: "fake hub", - tag: "fake tag", - } - - _, mstr, err := renderOperatorManifest(nil, ocArgs) - if err != nil { - return err - } - if err := applyWithReconciler(reconciler, mstr); err != nil { - return err - } - iopStr, err := yaml.Marshal(iop) - if err != nil { - return err - } - if err := saveIOPToCluster(reconciler, string(iopStr)); err != nil { - return err - } - - return err -} - -// applyWithReconciler applies the given manifest string using the given reconciler. -func applyWithReconciler(reconciler *helmreconciler.HelmReconciler, manifest string) error { - m := name.Manifest{ - // Name is not important here, only Content will be applied. - Name: name.IstioOperatorComponentName, - Content: manifest, - } - _, _, err := reconciler.ApplyManifest(m, false) - return err -} - -// runManifestCommand runs the given manifest command. If filenames is set, passes the given filenames as -f flag, -// flags is passed to the command verbatim. If you set both flags and path, make sure to not use -f in flags. -func runManifestCommand(command string, filenames []string, flags string, chartSource chartSourceType, fileSelect []string) (string, error) { - var args string - if command == "install" { - args = "install" - } else { - args = "manifest " + command - } - for _, f := range filenames { - args += " -f " + f - } - if flags != "" { - args += " " + flags - } - if fileSelect != nil { - filters := []string{} - filters = append(filters, fileSelect...) - // Everything needs these - filters = append(filters, "templates/_affinity.tpl") - args += " --filter " + strings.Join(filters, ",") - } - args += " --set installPackagePath=" + string(chartSource) - return runCommand(args) -} - -// runCommand runs the given command string. -func runCommand(command string) (string, error) { - var out bytes.Buffer - rootCmd := GetRootCmd(strings.Split(command, " ")) - rootCmd.SetOut(&out) - - err := rootCmd.Execute() - return out.String(), err -} - -// cleanTestCluster resets the test cluster. -func cleanTestCluster() error { - // Needed in case we are running a test through this path that doesn't start a new process. - cache.FlushObjectCaches() - if !kubeBuilderInstalled() { - return nil - } - return recreateTestEnv() -} - -// getAllIstioObjects lists all Istio GVK resources from the testClient. -func getAllIstioObjects() object.K8sObjects { - var out object.K8sObjects - for _, gvk := range append(allClusterGVKs, allNamespacedGVKs...) { - objects := &unstructured.UnstructuredList{} - objects.SetGroupVersionKind(gvk) - if err := testClient.List(context.TODO(), objects); err != nil { - log.Error(err.Error()) - continue - } - for _, o := range objects.Items { - no := o.DeepCopy() - out = append(out, object.NewK8sObject(no, nil, nil)) - } - } - return out -} - -// readFile reads a file and returns the contents. -func readFile(path string) (string, error) { - b, err := os.ReadFile(path) - return string(b), err -} - -// writeFile writes a file and returns an error if operation is unsuccessful. -func writeFile(path string, data []byte) error { - return os.WriteFile(path, data, 0o644) -} - -// removeFile removes given file from provided path. -func removeFile(path string) error { - return os.Remove(path) -} - -// inFileAbsolutePath returns the absolute path for an input file like "gateways". -func inFileAbsolutePath(inFile string) string { - return filepath.Join(testDataDir, "input", inFile+".yaml") -} diff --git a/operator/cmd/mesh/operator-common.go b/operator/cmd/mesh/operator-common.go deleted file mode 100644 index 1abb34b03..000000000 --- a/operator/cmd/mesh/operator-common.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "context" -) - -import ( - v12 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - _ "k8s.io/client-go/plugin/pkg/client/auth" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/helm" - "github.com/apache/dubbo-go-pixiu/operator/pkg/manifest" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -type operatorCommonArgs struct { - // hub is the hub for the operator image. - hub string - // tag is the tag for the operator image. - tag string - // imagePullSecrets is an array of imagePullSecret to pull operator image from the private registry - imagePullSecrets []string - // operatorNamespace is the namespace the operator controller is installed into. - operatorNamespace string - // watchedNamespaces is the namespaces the operator controller watches, could be namespace list separated by comma. - watchedNamespaces string - // istioNamespace is deprecated, use watchedNamespaces instead. - istioNamespace string - // manifestsPath is a path to a charts and profiles directory in the local filesystem, or URL with a release tgz. - manifestsPath string - // revision is the Istio control plane revision the command targets. - revision string - // outputFormat controls the format of operator dumps - outputFormat string -} - -const ( - operatorResourceName = "istio-operator" - operatorDefaultNamespace = "istio-operator" - istioDefaultNamespace = "dubbo-system" -) - -// isControllerInstalled reports whether an operator deployment exists in the given namespace. -func isControllerInstalled(cs kubernetes.Interface, operatorNamespace string, revision string) (bool, error) { - orn := operatorResourceName - if revision != "" { - orn += "-" + revision - } - return deploymentExists(cs, operatorNamespace, orn) -} - -// renderOperatorManifest renders a manifest to install the operator with the given input arguments. -func renderOperatorManifest(_ *RootArgs, ocArgs *operatorCommonArgs) (string, string, error) { - // If manifestsPath is a URL, fetch and extract it and continue with the local filesystem path instead. - installPackagePath, _, err := manifest.RewriteURLToLocalInstallPath(ocArgs.manifestsPath, "" /*profileOrPath*/, false /*skipValidation */) - if err != nil { - return "", "", err - } - r := helm.NewHelmRenderer(installPackagePath, "istio-operator", string(name.IstioOperatorComponentName), ocArgs.operatorNamespace, nil) - - if err := r.Run(); err != nil { - return "", "", err - } - - tmpl := ` -istioNamespace: {{.IstioNamespace}} -watchedNamespaces: {{.WatchedNamespaces}} -hub: {{.Hub}} -tag: {{.Tag}} -{{- if .ImagePullSecrets }} -imagePullSecrets: -{{- range .ImagePullSecrets }} -- {{ . }} -{{- end }} -{{- end }} -revision: {{if .Revision }} {{.Revision}} {{else}} "" {{end}} -` - - tv := struct { - IstioNamespace string - WatchedNamespaces string - Hub string - Tag string - ImagePullSecrets []string - Revision string - }{ - IstioNamespace: ocArgs.istioNamespace, - WatchedNamespaces: ocArgs.watchedNamespaces, - Hub: ocArgs.hub, - Tag: ocArgs.tag, - ImagePullSecrets: ocArgs.imagePullSecrets, - Revision: ocArgs.revision, - } - vals, err := util.RenderTemplate(tmpl, tv) - if err != nil { - return "", "", err - } - manifest, err := r.RenderManifest(vals) - return vals, manifest, err -} - -// deploymentExists returns true if the given deployment in the namespace exists. -func deploymentExists(cs kubernetes.Interface, namespace, name string) (bool, error) { - d, err := cs.AppsV1().Deployments(namespace).Get(context.TODO(), name, v12.GetOptions{}) - if err != nil { - return false, err - } - return d != nil, nil -} diff --git a/operator/cmd/mesh/operator-dump.go b/operator/cmd/mesh/operator-dump.go deleted file mode 100644 index a63bc43c2..000000000 --- a/operator/cmd/mesh/operator-dump.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "fmt" -) - -import ( - "github.com/spf13/cobra" - buildversion "istio.io/pkg/version" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" -) - -type operatorDumpArgs struct { - // common is shared operator args - common operatorCommonArgs -} - -func addOperatorDumpFlags(cmd *cobra.Command, args *operatorDumpArgs) { - hub, tag := buildversion.DockerInfo.Hub, buildversion.DockerInfo.Tag - - cmd.PersistentFlags().StringVar(&args.common.hub, "hub", hub, HubFlagHelpStr) - cmd.PersistentFlags().StringVar(&args.common.tag, "tag", tag, TagFlagHelpStr) - cmd.PersistentFlags().StringSliceVar(&args.common.imagePullSecrets, "imagePullSecrets", nil, ImagePullSecretsHelpStr) - cmd.PersistentFlags().StringVar(&args.common.watchedNamespaces, "watchedNamespaces", istioDefaultNamespace, - "The namespaces the operator controller watches, could be namespace list separated by comma, eg. 'ns1,ns2'") - cmd.PersistentFlags().StringVar(&args.common.operatorNamespace, "operatorNamespace", operatorDefaultNamespace, OperatorNamespaceHelpstr) - cmd.PersistentFlags().StringVarP(&args.common.manifestsPath, "charts", "", "", ChartsDeprecatedStr) - cmd.PersistentFlags().StringVarP(&args.common.manifestsPath, "manifests", "d", "", ManifestsFlagHelpStr) - cmd.PersistentFlags().StringVarP(&args.common.revision, "revision", "r", "", OperatorRevFlagHelpStr) - cmd.PersistentFlags().StringVarP(&args.common.outputFormat, "output", "o", yamlOutput, - "Output format: one of json|yaml") -} - -func operatorDumpCmd(rootArgs *RootArgs, odArgs *operatorDumpArgs) *cobra.Command { - return &cobra.Command{ - Use: "dump", - Short: "Dumps the Istio operator controller manifest.", - Long: "The dump subcommand dumps the Istio operator controller manifest.", - Args: cobra.ExactArgs(0), - Run: func(cmd *cobra.Command, args []string) { - l := clog.NewConsoleLogger(cmd.OutOrStdout(), cmd.ErrOrStderr(), installerScope) - operatorDump(rootArgs, odArgs, l) - }, - } -} - -// operatorDump dumps the manifest used to install the operator. -func operatorDump(args *RootArgs, odArgs *operatorDumpArgs, l clog.Logger) { - if err := validateOperatorOutputFormatFlag(odArgs.common.outputFormat); err != nil { - l.LogAndFatal(fmt.Errorf("unknown output format: %v", odArgs.common.outputFormat)) - } - - _, mstr, err := renderOperatorManifest(args, &odArgs.common) - if err != nil { - l.LogAndFatal(err) - } - - var output string - if output, err = yamlToFormat(mstr, odArgs.common.outputFormat); err != nil { - l.LogAndFatal(err) - } - l.Print(output) -} - -// validateOutputFormatFlag validates if the output format is valid. -func validateOperatorOutputFormatFlag(outputFormat string) error { - switch outputFormat { - case jsonOutput, yamlOutput: - default: - return fmt.Errorf("unknown output format: %s", outputFormat) - } - return nil -} diff --git a/operator/cmd/mesh/operator-init.go b/operator/cmd/mesh/operator-init.go deleted file mode 100644 index 45152b131..000000000 --- a/operator/cmd/mesh/operator-init.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "fmt" - "strings" -) - -import ( - "github.com/fatih/color" - "github.com/spf13/cobra" - "istio.io/api/operator/v1alpha1" - buildversion "istio.io/pkg/version" -) - -import ( - iopv1alpha1 "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/translate" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" -) - -type operatorInitArgs struct { - // inFilenames is the path to the input IstioOperator CR. - inFilename string - // kubeConfigPath is the path to kube config file. - kubeConfigPath string - // context is the cluster context in the kube config. - context string - - // common is shared operator args - common operatorCommonArgs -} - -// kubeClients is a unit test override variable for client interfaces creation. -var kubeClients = KubernetesClients - -func addOperatorInitFlags(cmd *cobra.Command, args *operatorInitArgs) { - hub, tag := buildversion.DockerInfo.Hub, buildversion.DockerInfo.Tag - - cmd.PersistentFlags().StringVarP(&args.inFilename, "filename", "f", "", filenameFlagHelpStr) - cmd.PersistentFlags().StringVarP(&args.kubeConfigPath, "kubeconfig", "c", "", KubeConfigFlagHelpStr) - cmd.PersistentFlags().StringVar(&args.context, "context", "", ContextFlagHelpStr) - cmd.PersistentFlags().StringVar(&args.common.hub, "hub", hub, HubFlagHelpStr) - cmd.PersistentFlags().StringVar(&args.common.tag, "tag", tag, TagFlagHelpStr) - cmd.PersistentFlags().StringSliceVar(&args.common.imagePullSecrets, "imagePullSecrets", nil, ImagePullSecretsHelpStr) - cmd.PersistentFlags().StringVar(&args.common.operatorNamespace, "operatorNamespace", operatorDefaultNamespace, OperatorNamespaceHelpstr) - cmd.PersistentFlags().StringVar(&args.common.watchedNamespaces, "watchedNamespaces", istioDefaultNamespace, - "The namespaces the operator controller watches, could be namespace list separated by comma, eg. 'ns1,ns2'") - cmd.PersistentFlags().StringVarP(&args.common.manifestsPath, "charts", "", "", ChartsDeprecatedStr) - cmd.PersistentFlags().StringVarP(&args.common.manifestsPath, "manifests", "d", "", ManifestsFlagHelpStr) - cmd.PersistentFlags().StringVarP(&args.common.revision, "revision", "r", "", OperatorRevFlagHelpStr) -} - -func operatorInitCmd(rootArgs *RootArgs, oiArgs *operatorInitArgs) *cobra.Command { - return &cobra.Command{ - Use: "init", - Short: "Installs the Istio operator controller in the cluster.", - Long: "The init subcommand installs the Istio operator controller in the cluster.", - Args: cobra.ExactArgs(0), - PreRunE: func(cmd *cobra.Command, args []string) error { - if !labels.IsDNS1123Label(oiArgs.common.revision) && cmd.PersistentFlags().Changed("revision") { - return fmt.Errorf("invalid revision specified: %v", oiArgs.common.revision) - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - l := clog.NewConsoleLogger(cmd.OutOrStdout(), cmd.ErrOrStderr(), installerScope) - operatorInit(rootArgs, oiArgs, l) - }, - } -} - -// operatorInit installs the Istio operator controller into the cluster. -func operatorInit(args *RootArgs, oiArgs *operatorInitArgs, l clog.Logger) { - initLogsOrExit(args) - - kubeClient, client, err := kubeClients(oiArgs.kubeConfigPath, oiArgs.context, l) - if err != nil { - l.LogAndFatal(err) - } - // Error here likely indicates Deployment is missing. If some other K8s error, we will hit it again later. - already, _ := isControllerInstalled(kubeClient, oiArgs.common.operatorNamespace, oiArgs.common.revision) - if already { - l.LogAndPrintf("Operator controller is already installed in %s namespace.", oiArgs.common.operatorNamespace) - l.LogAndPrintf("Upgrading operator controller in namespace: %s using image: %s/operator:%s", - oiArgs.common.operatorNamespace, oiArgs.common.hub, oiArgs.common.tag) - } else { - l.LogAndPrintf("Installing operator controller in namespace: %s using image: %s/operator:%s", - oiArgs.common.operatorNamespace, oiArgs.common.hub, oiArgs.common.tag) - } - - l.LogAndPrintf("Operator controller will watch namespaces: %s", oiArgs.common.watchedNamespaces) - - vals, mstr, err := renderOperatorManifest(args, &oiArgs.common) - if err != nil { - l.LogAndFatal(err) - } - - installerScope.Debugf("Installing operator charts with the following values:\n%s", vals) - installerScope.Debugf("Using the following manifest to install operator:\n%s\n", mstr) - - opts := &applyOptions{ - DryRun: args.DryRun, - Kubeconfig: oiArgs.kubeConfigPath, - Context: oiArgs.context, - } - - // If CR was passed, we must create a namespace for it and install CR into it. - customResource, istioNamespace, err := getCRAndNamespaceFromFile(oiArgs.inFilename, l) - if err != nil { - l.LogAndFatal(err) - } - var iop *iopv1alpha1.IstioOperator - if oiArgs.common.revision != "" { - emptyiops := &v1alpha1.IstioOperatorSpec{Profile: "empty", Revision: oiArgs.common.revision} - iop, err = translate.IOPStoIOP(emptyiops, "", "") - if err != nil { - l.LogAndFatal(err) - } - } - - if err := createNamespace(kubeClient, oiArgs.common.operatorNamespace, "", opts.DryRun); err != nil { - l.LogAndFatal(err) - } - - // create watched namespaces - namespaces := strings.Split(oiArgs.common.watchedNamespaces, ",") - // if the namespace in the CR is provided, consider creating it too. - if istioNamespace != "" { - namespaces = append(namespaces, istioNamespace) - } - for _, ns := range namespaces { - if err := createNamespace(kubeClient, ns, "", opts.DryRun); err != nil { - l.LogAndFatal(err) - } - } - - if err := applyManifest(kubeClient, client, mstr, name.IstioOperatorComponentName, opts, iop, l); err != nil { - l.LogAndFatal(err) - } - - if customResource != "" { - if err := applyManifest(kubeClient, client, customResource, name.IstioOperatorComponentName, opts, iop, l); err != nil { - l.LogAndFatal(err) - } - } - - l.LogAndPrint(color.New(color.FgGreen).Sprint("✔ ") + installationCompleteStr) -} diff --git a/operator/cmd/mesh/operator-remove.go b/operator/cmd/mesh/operator-remove.go deleted file mode 100644 index 1a00bf79a..000000000 --- a/operator/cmd/mesh/operator-remove.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "github.com/fatih/color" - "github.com/spf13/cobra" - "istio.io/api/operator/v1alpha1" -) - -import ( - iopv1alpha1 "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/helmreconciler" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/translate" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" -) - -type operatorRemoveArgs struct { - // kubeConfigPath is the path to kube config file. - kubeConfigPath string - // context is the cluster context in the kube config. - context string - // force proceeds even if there are validation errors - force bool - // operatorNamespace is the namespace the operator controller is installed into. - operatorNamespace string - // revision is the Istio control plane revision the command targets. - revision string -} - -func addOperatorRemoveFlags(cmd *cobra.Command, oiArgs *operatorRemoveArgs) { - cmd.PersistentFlags().StringVarP(&oiArgs.kubeConfigPath, "kubeconfig", "c", "", KubeConfigFlagHelpStr) - cmd.PersistentFlags().StringVar(&oiArgs.context, "context", "", ContextFlagHelpStr) - cmd.PersistentFlags().BoolVar(&oiArgs.force, "force", false, ForceFlagHelpStr) - cmd.PersistentFlags().StringVar(&oiArgs.operatorNamespace, "operatorNamespace", operatorDefaultNamespace, OperatorNamespaceHelpstr) - cmd.PersistentFlags().StringVarP(&oiArgs.revision, "revision", "r", "", OperatorRevFlagHelpStr) -} - -func operatorRemoveCmd(rootArgs *RootArgs, orArgs *operatorRemoveArgs) *cobra.Command { - return &cobra.Command{ - Use: "remove", - Short: "Removes the Istio operator controller from the cluster.", - Long: "The remove subcommand removes the Istio operator controller from the cluster.", - Args: cobra.ExactArgs(0), - Run: func(cmd *cobra.Command, args []string) { - l := clog.NewConsoleLogger(cmd.OutOrStdout(), cmd.OutOrStderr(), installerScope) - operatorRemove(rootArgs, orArgs, l) - }, - } -} - -// operatorRemove removes the Istio operator controller from the cluster. -func operatorRemove(args *RootArgs, orArgs *operatorRemoveArgs, l clog.Logger) { - initLogsOrExit(args) - - kubeClient, client, err := KubernetesClients(orArgs.kubeConfigPath, orArgs.context, l) - if err != nil { - l.LogAndFatal(err) - } - - installed, err := isControllerInstalled(kubeClient, orArgs.operatorNamespace, orArgs.revision) - if installed && err != nil { - l.LogAndFatal(err) - } - if !installed { - l.LogAndPrintf("Operator controller is not installed in %s namespace (no Deployment detected).", orArgs.operatorNamespace) - if !orArgs.force { - l.LogAndFatal("Aborting, use --force to override.") - } - } - - l.LogAndPrintf("Removing Istio operator...") - // Create an empty IOP for the purpose of populating revision. Apply code requires a non-nil IOP. - var iop *iopv1alpha1.IstioOperator - if orArgs.revision != "" { - emptyiops := &v1alpha1.IstioOperatorSpec{Profile: "empty", Revision: orArgs.revision} - iop, err = translate.IOPStoIOP(emptyiops, "", "") - if err != nil { - l.LogAndFatal(err) - } - } - reconciler, err := helmreconciler.NewHelmReconciler(client, kubeClient, iop, &helmreconciler.Options{DryRun: args.DryRun, Log: l}) - if err != nil { - l.LogAndFatal(err) - } - rs, err := reconciler.GetPrunedResources(orArgs.revision, false, string(name.IstioOperatorComponentName)) - if err != nil { - l.LogAndFatal(err) - } - if err := reconciler.DeleteObjectsList(rs, string(name.IstioOperatorComponentName)); err != nil { - l.LogAndFatal(err) - } - - l.LogAndPrint(color.New(color.FgGreen).Sprint("✔ ") + "Removal complete") -} diff --git a/operator/cmd/mesh/operator.go b/operator/cmd/mesh/operator.go deleted file mode 100644 index 330862b78..000000000 --- a/operator/cmd/mesh/operator.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "github.com/spf13/cobra" -) - -// OperatorCmd is a group of commands related to installation and management of the operator controller. -func OperatorCmd() *cobra.Command { - oc := &cobra.Command{ - Use: "operator", - Short: "Commands related to Istio operator controller.", - Long: "The operator command installs, dumps, removes and shows the status of the operator controller.", - } - - odArgs := &operatorDumpArgs{} - oiArgs := &operatorInitArgs{} - orArgs := &operatorRemoveArgs{} - args := &RootArgs{} - - odc := operatorDumpCmd(args, odArgs) - oic := operatorInitCmd(args, oiArgs) - orc := operatorRemoveCmd(args, orArgs) - - addFlags(odc, args) - addFlags(oic, args) - addFlags(orc, args) - - addOperatorDumpFlags(odc, odArgs) - addOperatorInitFlags(oic, oiArgs) - addOperatorRemoveFlags(orc, orArgs) - - oc.AddCommand(odc) - oc.AddCommand(oic) - oc.AddCommand(orc) - - return oc -} diff --git a/operator/cmd/mesh/operator_test.go b/operator/cmd/mesh/operator_test.go deleted file mode 100644 index 57690fcb5..000000000 --- a/operator/cmd/mesh/operator_test.go +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "os" - "path/filepath" - "strings" - "testing" -) - -import ( - "k8s.io/client-go/kubernetes/fake" - "k8s.io/client-go/rest" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -var ( - extendedClient kube.ExtendedClient - kubeClient client.Client -) - -// TODO: rewrite this with running the actual top level command. -func TestOperatorDump(t *testing.T) { - goldenFilepath := filepath.Join(env.IstioSrc, "operator/cmd/mesh/testdata/operator/output/operator-dump.yaml") - - odArgs := &operatorDumpArgs{ - common: operatorCommonArgs{ - hub: "foo.io/istio", - tag: "1.2.3", - imagePullSecrets: []string{"imagePullSecret1,imagePullSecret2"}, - operatorNamespace: "operator-test-namespace", - watchedNamespaces: "istio-test-namespace1,istio-test-namespace2", - }, - } - - cmd := "operator dump --hub " + odArgs.common.hub - cmd += " --tag " + odArgs.common.tag - cmd += " --imagePullSecrets " + strings.Join(odArgs.common.imagePullSecrets, ",") - cmd += " --operatorNamespace " + odArgs.common.operatorNamespace - cmd += " --watchedNamespaces " + odArgs.common.watchedNamespaces - cmd += " --manifests=" + string(snapshotCharts) - - gotYAML, err := runCommand(cmd) - if err != nil { - t.Fatal(err) - } - - if refreshGoldenFiles() { - t.Logf("Refreshing golden file for %s", goldenFilepath) - if err := os.WriteFile(goldenFilepath, []byte(gotYAML), 0o644); err != nil { - t.Error(err) - } - } - - wantYAML, err := readFile(goldenFilepath) - if err != nil { - t.Fatal(err) - } - - if diff := util.YAMLDiff(wantYAML, gotYAML); diff != "" { - t.Fatalf("diff: %s", diff) - } -} - -func TestOperatorDumpJSONFormat(t *testing.T) { - goldenFilepath := filepath.Join(env.IstioSrc, "operator/cmd/mesh/testdata/operator/output/operator-dump.json") - - odArgs := &operatorDumpArgs{ - common: operatorCommonArgs{ - hub: "foo.io/istio", - tag: "1.2.3", - imagePullSecrets: []string{"imagePullSecret1,imagePullSecret2"}, - operatorNamespace: "operator-test-namespace", - watchedNamespaces: "istio-test-namespace1,istio-test-namespace2", - outputFormat: jsonOutput, - }, - } - - cmd := "operator dump --hub " + odArgs.common.hub - cmd += " --tag " + odArgs.common.tag - cmd += " --imagePullSecrets " + strings.Join(odArgs.common.imagePullSecrets, ",") - cmd += " --operatorNamespace " + odArgs.common.operatorNamespace - cmd += " --watchedNamespaces " + odArgs.common.watchedNamespaces - cmd += " --manifests=" + string(snapshotCharts) - cmd += " --output " + odArgs.common.outputFormat - - gotJSON, err := runCommand(cmd) - if err != nil { - t.Fatal(err) - } - - if refreshGoldenFiles() { - t.Logf("Refreshing golden file for %s", goldenFilepath) - if err := os.WriteFile(goldenFilepath, []byte(gotJSON), 0o644); err != nil { - t.Error(err) - } - } - - wantJSON, err := readFile(goldenFilepath) - if err != nil { - t.Fatal(err) - } - - var want, got []byte - if got, err = yaml.JSONToYAML([]byte(gotJSON)); err != nil { - t.Fatal(err) - } - if want, err = yaml.JSONToYAML([]byte(wantJSON)); err != nil { - t.Fatal(err) - } - - if diff := util.YAMLDiff(string(want), string(got)); diff != "" { - t.Fatalf("diff: %s", diff) - } -} - -// TODO: rewrite this with running the actual top level command. -func TestOperatorInit(t *testing.T) { - goldenFilepath := filepath.Join(operatorRootDir, "cmd/mesh/testdata/operator/output/operator-init.yaml") - rootArgs := &RootArgs{} - oiArgs := &operatorInitArgs{ - common: operatorCommonArgs{ - hub: "foo.io/istio", - tag: "1.2.3", - operatorNamespace: "operator-test-namespace", - watchedNamespaces: "istio-test-namespace1,istio-test-namespace2", - manifestsPath: string(snapshotCharts), - }, - } - - l := clog.NewConsoleLogger(os.Stdout, os.Stderr, installerScope) - _, gotYAML, err := renderOperatorManifest(rootArgs, &oiArgs.common) - if err != nil { - l.LogAndFatal(err) - } - - if refreshGoldenFiles() { - t.Logf("Refreshing golden file for %s", goldenFilepath) - if err := os.WriteFile(goldenFilepath, []byte(gotYAML), 0o644); err != nil { - t.Error(err) - } - } - - wantYAML, err := readFile(goldenFilepath) - if err != nil { - t.Fatal(err) - } - - if diff := util.YAMLDiff(wantYAML, gotYAML); diff != "" { - t.Fatalf("diff: %s", diff) - } -} - -func MockKubernetesClients(_, _ string, _ clog.Logger) (kube.ExtendedClient, client.Client, error) { - extendedClient = kube.MockClient{ - Interface: fake.NewSimpleClientset(), - } - kubeClient, _ = client.New(&rest.Config{}, client.Options{}) - return extendedClient, kubeClient, nil -} - -func TestOperatorInitDryRun(t *testing.T) { - tests := []struct { - operatorNamespace string - watchedNamespaces string - }{ - { - // default nss - operatorNamespace: "", - watchedNamespaces: "", - }, - { - operatorNamespace: "test", - watchedNamespaces: "test1", - }, - { - operatorNamespace: "", - watchedNamespaces: "test4, test5", - }, - } - - kubeClients = MockKubernetesClients - - for _, test := range tests { - t.Run("", func(t *testing.T) { - args := []string{"operator", "init", "--dry-run"} - if test.operatorNamespace != "" { - args = append(args, "--operatorNamespace", test.operatorNamespace) - } - if test.watchedNamespaces != "" { - args = append(args, "--watchedNamespaces", test.watchedNamespaces) - } - - rootCmd := GetRootCmd(args) - err := rootCmd.Execute() - assert.NoError(t, err) - - readActions := map[string]bool{ - "get": true, - "list": true, - "watch": true, - } - - actions := extendedClient.Kube().(*fake.Clientset).Actions() - for _, action := range actions { - if v := readActions[action.GetVerb()]; !v { - t.Fatalf("unexpected action: %+v, expected %s", action.GetVerb(), "get") - } - } - }) - } -} diff --git a/operator/cmd/mesh/profile-common_test.go b/operator/cmd/mesh/profile-common_test.go deleted file mode 100644 index 1a99a96ac..000000000 --- a/operator/cmd/mesh/profile-common_test.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "errors" - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/manifest" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" -) - -func TestParseYAMLFiles(t *testing.T) { - tests := []struct { - desc string - inYAML []string - inForce bool - inLogger clog.Logger - expectedOverlay string - expectedProfile string - expectedErr error - }{ - { - desc: "array-pilot-plugins", - inYAML: []string{"testdata/profile-dump/input/pilot_plugin_valid.yaml"}, - inForce: false, - inLogger: clog.NewDefaultLogger(), - expectedOverlay: `apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -metadata: - namespace: dubbo-system -spec: - values: - pilot: - plugins: - - aa - - bb `, - expectedProfile: "", - expectedErr: nil, - }, - { - desc: "invalid-pilot-plugins", - inYAML: []string{"testdata/profile-dump/input/pilot_plugin_invalid.yaml"}, - inForce: false, - inLogger: clog.NewDefaultLogger(), - expectedOverlay: "", - expectedProfile: "", - expectedErr: errors.New("json: cannot unmarshal object into Go value of type string"), - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - gotOverlay, gotProfile, err := manifest.ParseYAMLFiles(tt.inYAML, tt.inForce, tt.inLogger) - if tt.expectedProfile != gotProfile || tt.expectedOverlay != gotOverlay || - ((tt.expectedErr != nil && err == nil) || (tt.expectedErr == nil && err != nil)) { - t.Errorf("%s: expect overlay, profile,&err %v %v %v, got %v %v %v", tt.desc, tt.expectedOverlay, - tt.expectedProfile, tt.expectedErr, gotOverlay, gotProfile, err) - } - }) - } -} diff --git a/operator/cmd/mesh/profile-diff.go b/operator/cmd/mesh/profile-diff.go deleted file mode 100644 index c0c04c133..000000000 --- a/operator/cmd/mesh/profile-diff.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "fmt" - "io" - "os" -) - -import ( - "github.com/spf13/cobra" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/manifest" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" -) - -type profileDiffArgs struct { - // manifestsPath is a path to a charts and profiles directory in the local filesystem, or URL with a release tgz. - manifestsPath string -} - -func addProfileDiffFlags(cmd *cobra.Command, args *profileDiffArgs) { - cmd.PersistentFlags().StringVarP(&args.manifestsPath, "charts", "", "", ChartsDeprecatedStr) - cmd.PersistentFlags().StringVarP(&args.manifestsPath, "manifests", "d", "", ManifestsFlagHelpStr) -} - -func profileDiffCmd(rootArgs *RootArgs, pfArgs *profileDiffArgs, logOpts *log.Options) *cobra.Command { - return &cobra.Command{ - Use: "diff ", - Short: "Diffs two Istio configuration profiles", - Long: "The diff subcommand displays the differences between two Istio configuration profiles.", - Example: ` # Profile diff by providing yaml files - istioctl profile diff manifests/profiles/default.yaml manifests/profiles/demo.yaml - - # Profile diff by providing a profile name - istioctl profile diff default demo`, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) != 2 { - return fmt.Errorf("diff requires two profiles") - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - isdifferent, err := profileDiff(cmd, rootArgs, pfArgs, args, logOpts) - if err != nil { - return err - } - if isdifferent { - os.Exit(1) - } - return nil - }, - } -} - -// profileDiff compare two profile files. -func profileDiff(cmd *cobra.Command, rootArgs *RootArgs, pfArgs *profileDiffArgs, args []string, logOpts *log.Options) (bool, error) { - initLogsOrExit(rootArgs) - - l := clog.NewConsoleLogger(cmd.OutOrStdout(), cmd.OutOrStderr(), nil) - setFlags := make([]string, 0) - if pfArgs.manifestsPath != "" { - setFlags = append(setFlags, fmt.Sprintf("installPackagePath=%s", pfArgs.manifestsPath)) - } - if err := configLogs(logOpts); err != nil { - return false, fmt.Errorf("could not configure logs: %s", err) - } - return profileDiffInternal(args[0], args[1], setFlags, cmd.OutOrStdout(), l) -} - -func profileDiffInternal(profileA, profileB string, setFlags []string, writer io.Writer, l clog.Logger) (bool, error) { - a, _, err := manifest.GenIOPFromProfile(profileA, "", setFlags, true, true, nil, l) - if err != nil { - return false, fmt.Errorf("could not read %q: %v", profileA, err) - } - - b, _, err := manifest.GenIOPFromProfile(profileB, "", setFlags, true, true, nil, l) - if err != nil { - return false, fmt.Errorf("could not read %q: %v", profileB, err) - } - - diff := util.YAMLDiff(a, b) - if diff == "" { - fmt.Fprintln(writer, "Profiles are identical") - } else { - fmt.Fprintf(writer, "The difference between profiles:\n%s", diff) - return true, nil - } - - return false, nil -} diff --git a/operator/cmd/mesh/profile-diff_test.go b/operator/cmd/mesh/profile-diff_test.go deleted file mode 100644 index 6a3b4c25e..000000000 --- a/operator/cmd/mesh/profile-diff_test.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "bufio" - "bytes" - "fmt" - "os" - "strings" - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" -) - -type profileDiffTestcase struct { - args string - shouldFail bool - expectedString string // String output is expected to contain - notExpected string // String the output must NOT contain -} - -func TestProfileDiff(t *testing.T) { - cases := []profileDiffTestcase{ - { - args: "profile diff demo default --unknown-flag", - shouldFail: true, - }, - { - args: "profile diff demo", - shouldFail: true, - }, - { - args: "profile diff", - shouldFail: true, - }, - { - args: fmt.Sprintf("profile diff default unknown-profile --manifests %s", snapshotCharts), - shouldFail: true, - }, - { - args: fmt.Sprintf("profile diff default default --manifests %s", snapshotCharts), - expectedString: "Profiles are identical", - }, - { - args: fmt.Sprintf("profile diff demo demo --manifests %s", snapshotCharts), - expectedString: "Profiles are identical", - }, - { - args: fmt.Sprintf("profile diff openshift openshift --manifests %s", snapshotCharts), - expectedString: "Profiles are identical", - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("case %d %q", i, c.args), func(t *testing.T) { - output, fErr := runCommand(c.args) - verifyProfileDiffCommandCaseOutput(t, c, output, fErr) - }) - } -} - -func TestProfileDiffDirect(t *testing.T) { - cases := []profileDiffTestcase{ - { - // We won't be parsing with Cobra, but this is the command equivalent - args: "profile diff default openshift", - // This is just one of the many differences - expectedString: "+ cniBinDir: /var/lib/cni/bin", - // The profile doesn't change istiocoredns, so we shouldn't see this in the diff - notExpected: "- istiocoredns:", - // 'profile diff' "fails" so that the error level `$?` is 1, not 0, if there is a diff - shouldFail: false, - }, - } - - l := clog.NewConsoleLogger(os.Stdout, os.Stderr, nil) - for i, c := range cases { - t.Run(fmt.Sprintf("case %d %q", i, c.args), func(t *testing.T) { - args := strings.Split(c.args, " ") - setFlag := fmt.Sprintf("installPackagePath=%s", snapshotCharts) - var by bytes.Buffer - _, err := profileDiffInternal(args[len(args)-2], args[len(args)-1], []string{setFlag}, bufio.NewWriter(&by), l) - verifyProfileDiffCommandCaseOutput(t, c, by.String(), err) - }) - } -} - -func verifyProfileDiffCommandCaseOutput(t *testing.T, c profileDiffTestcase, output string, fErr error) { - t.Helper() - - if c.expectedString != "" && !strings.Contains(output, c.expectedString) { - t.Fatalf("Output didn't match for 'istioctl %s'\n got %v\nwant: %v", c.args, output, c.expectedString) - } - - if c.notExpected != "" && strings.Contains(output, c.notExpected) { - t.Fatalf("Output didn't match for 'istioctl %s'\n got %v\nDON'T want: %v", c.args, output, c.expectedString) - } - - if c.shouldFail { - if fErr == nil { - t.Fatalf("Command should have failed for 'istioctl %s', didn't get one, output was %q", - c.args, output) - } - } else { - if fErr != nil { - t.Fatalf("Command should not have failed for 'istioctl %s': %v", c.args, fErr) - } - } -} diff --git a/operator/cmd/mesh/profile-dump.go b/operator/cmd/mesh/profile-dump.go deleted file mode 100644 index 9c010b08d..000000000 --- a/operator/cmd/mesh/profile-dump.go +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "encoding/json" - "fmt" - "sort" - "strings" -) - -import ( - "github.com/spf13/cobra" - "istio.io/pkg/log" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/manifest" - "github.com/apache/dubbo-go-pixiu/operator/pkg/tpath" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" -) - -type profileDumpArgs struct { - // inFilenames is an array of paths to the input IstioOperator CR files. - inFilenames []string - // configPath sets the root node for the subtree to display the config for. - configPath string - // outputFormat controls the format of profile dumps - outputFormat string - // manifestsPath is a path to a charts and profiles directory in the local filesystem, or URL with a release tgz. - manifestsPath string -} - -const ( - jsonOutput = "json" - yamlOutput = "yaml" - flagsOutput = "flags" -) - -const ( - istioOperatorTreeString = ` -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -` -) - -func addProfileDumpFlags(cmd *cobra.Command, args *profileDumpArgs) { - cmd.PersistentFlags().StringSliceVarP(&args.inFilenames, "filename", "f", nil, filenameFlagHelpStr) - cmd.PersistentFlags().StringVarP(&args.configPath, "config-path", "p", "", - "The path the root of the configuration subtree to dump e.g. components.pilot. By default, dump whole tree") - cmd.PersistentFlags().StringVarP(&args.outputFormat, "output", "o", yamlOutput, - "Output format: one of json|yaml|flags") - cmd.PersistentFlags().StringVarP(&args.manifestsPath, "charts", "", "", ChartsDeprecatedStr) - cmd.PersistentFlags().StringVarP(&args.manifestsPath, "manifests", "d", "", ManifestsFlagHelpStr) -} - -func profileDumpCmd(rootArgs *RootArgs, pdArgs *profileDumpArgs, logOpts *log.Options) *cobra.Command { - return &cobra.Command{ - Use: "dump []", - Short: "Dumps an Istio configuration profile", - Long: "The dump subcommand dumps the values in an Istio configuration profile.", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) > 1 { - return fmt.Errorf("too many positional arguments") - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - l := clog.NewConsoleLogger(cmd.OutOrStdout(), cmd.ErrOrStderr(), installerScope) - return profileDump(args, rootArgs, pdArgs, l, logOpts) - }, - } -} - -func prependHeader(yml string) (string, error) { - out, err := tpath.AddSpecRoot(yml) - if err != nil { - return "", err - } - out2, err := util.OverlayYAML(istioOperatorTreeString, out) - if err != nil { - return "", err - } - return out2, nil -} - -// Convert the generated YAML to pretty JSON. -func yamlToPrettyJSON(yml string) (string, error) { - // YAML objects are not completely compatible with JSON - // objects. Let yaml.YAMLToJSON handle the edge cases and - // we'll re-encode the result to pretty JSON. - uglyJSON, err := yaml.YAMLToJSON([]byte(yml)) - if err != nil { - return "", err - } - var decoded interface{} - if uglyJSON[0] == '[' { - decoded = make([]interface{}, 0) - } else { - decoded = map[string]interface{}{} - } - if err := json.Unmarshal(uglyJSON, &decoded); err != nil { - return "", err - } - prettyJSON, err := json.MarshalIndent(decoded, "", " ") - if err != nil { - return "", err - } - return string(prettyJSON), nil -} - -func profileDump(args []string, rootArgs *RootArgs, pdArgs *profileDumpArgs, l clog.Logger, logOpts *log.Options) error { - initLogsOrExit(rootArgs) - - if len(args) == 1 && pdArgs.inFilenames != nil { - return fmt.Errorf("cannot specify both profile name and filename flag") - } - - if err := validateProfileOutputFormatFlag(pdArgs.outputFormat); err != nil { - return err - } - - setFlags := applyFlagAliases(make([]string, 0), pdArgs.manifestsPath, "") - if len(args) == 1 { - setFlags = append(setFlags, "profile="+args[0]) - } - - if err := configLogs(logOpts); err != nil { - return fmt.Errorf("could not configure logs: %s", err) - } - - y, _, err := manifest.GenerateConfig(pdArgs.inFilenames, setFlags, true, nil, l) - if err != nil { - return err - } - y, err = tpath.GetConfigSubtree(y, "spec") - if err != nil { - return err - } - - if pdArgs.configPath == "" { - if y, err = prependHeader(y); err != nil { - return err - } - } else { - if y, err = tpath.GetConfigSubtree(y, pdArgs.configPath); err != nil { - return err - } - } - - var output string - if output, err = yamlToFormat(y, pdArgs.outputFormat); err != nil { - return err - } - l.Print(output) - return nil -} - -// validateOutputFormatFlag validates if the output format is valid. -func validateProfileOutputFormatFlag(outputFormat string) error { - switch outputFormat { - case jsonOutput, yamlOutput, flagsOutput: - default: - return fmt.Errorf("unknown output format: %s", outputFormat) - } - return nil -} - -// yamlToFormat converts the generated yaml config to the expected format -func yamlToFormat(yaml, outputFormat string) (string, error) { - var output string - switch outputFormat { - case jsonOutput: - j, err := yamlToPrettyJSON(yaml) - if err != nil { - return "", err - } - output = fmt.Sprintf("%s\n", j) - case yamlOutput: - output = fmt.Sprintf("%s\n", yaml) - case flagsOutput: - f, err := yamlToFlags(yaml) - if err != nil { - return "", err - } - output = fmt.Sprintf("%s\n", strings.Join(f, "\n")) - } - return output, nil -} - -// Convert the generated YAML to --set flags -func yamlToFlags(yml string) ([]string, error) { - // YAML objects are not completely compatible with JSON - // objects. Let yaml.YAMLToJSON handle the edge cases and - // we'll re-encode the result to pretty JSON. - uglyJSON, err := yaml.YAMLToJSON([]byte(yml)) - if err != nil { - return []string{}, err - } - var decoded interface{} - if uglyJSON[0] == '[' { - decoded = make([]interface{}, 0) - } else { - decoded = map[string]interface{}{} - } - if err := json.Unmarshal(uglyJSON, &decoded); err != nil { - return []string{}, err - } - if d, ok := decoded.(map[string]interface{}); ok { - if v, ok := d["spec"]; ok { - // Fall back to showing the entire spec. - // (When --config-path is used there will be no spec to remove) - decoded = v - } - } - setflags, err := walk("", "", decoded) - if err != nil { - return []string{}, err - } - sort.Strings(setflags) - return setflags, nil -} - -func walk(path, separator string, obj interface{}) ([]string, error) { - switch v := obj.(type) { - case map[string]interface{}: - accum := make([]string, 0) - for key, vv := range v { - childwalk, err := walk(fmt.Sprintf("%s%s%s", path, separator, pathComponent(key)), ".", vv) - if err != nil { - return accum, err - } - accum = append(accum, childwalk...) - } - return accum, nil - case []interface{}: - accum := make([]string, 0) - for idx, vv := range v { - indexwalk, err := walk(fmt.Sprintf("%s[%d]", path, idx), ".", vv) - if err != nil { - return accum, err - } - accum = append(accum, indexwalk...) - } - return accum, nil - case string: - return []string{fmt.Sprintf("%s=%q", path, v)}, nil - default: - return []string{fmt.Sprintf("%s=%v", path, v)}, nil - } -} - -func pathComponent(component string) string { - if !strings.Contains(component, util.PathSeparator) { - return component - } - return strings.ReplaceAll(component, util.PathSeparator, util.EscapedPathSeparator) -} diff --git a/operator/cmd/mesh/profile-dump_test.go b/operator/cmd/mesh/profile-dump_test.go deleted file mode 100644 index 8f3918a11..000000000 --- a/operator/cmd/mesh/profile-dump_test.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "os" - "path/filepath" - "regexp" - "testing" -) - -import ( - "github.com/kylelemons/godebug/diff" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -func TestProfileDump(t *testing.T) { - testDataDir := filepath.Join(operatorRootDir, "cmd/mesh/testdata/profile-dump") - tests := []struct { - desc string - configPath string - }{ - { - desc: "all_off", - }, - { - desc: "config_path", - configPath: "components", - }, - { - desc: "list_path", - configPath: "values.gateways.istio-egressgateway.secretVolumes", - }, - } - installPackagePathRegex := regexp.MustCompile(" installPackagePath: .*") - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - inPath := filepath.Join(testDataDir, "input", tt.desc+".yaml") - outPath := filepath.Join(testDataDir, "output", tt.desc+".yaml") - - got, err := runProfileDump(inPath, tt.configPath, snapshotCharts, "") - if err != nil { - t.Fatal(err) - } - // installPackagePath may change, we will remove it for consistent output - got = installPackagePathRegex.ReplaceAllString(got, "") - - if refreshGoldenFiles() { - t.Logf("Refreshing golden file for %s", outPath) - if err := os.WriteFile(outPath, []byte(got), 0o644); err != nil { - t.Error(err) - } - } - - want, err := readFile(outPath) - if err != nil { - t.Fatal(err) - } - if !util.IsYAMLEqual(got, want) { - t.Errorf("profile-dump command(%s): got:\n%s\n\nwant:\n%s\nDiff:\n%s\n", tt.desc, got, want, util.YAMLDiff(got, want)) - } - }) - } -} - -func runProfileDump(profilePath, configPath string, chartSource chartSourceType, outfmt string) (string, error) { - cmd := "profile dump -f " + profilePath - if configPath != "" { - cmd += " --config-path " + configPath - } - if len(chartSource) > 0 { - cmd += " --manifests=" + string(chartSource) - } - if outfmt != "" { - cmd += " --output=" + outfmt - } - return runCommand(cmd) -} - -func TestProfileDumpFlags(t *testing.T) { - testDataDir := filepath.Join(operatorRootDir, "cmd/mesh/testdata/profile-dump") - tests := []struct { - desc string - configPath string - }{ - //{ - // desc: "all_off", - //}, - //{ - // desc: "config_path", - // configPath: "components", - //}, - { - desc: "list_path", - configPath: "values.gateways.istio-egressgateway.secretVolumes", - }, - } - installPackagePathRegex := regexp.MustCompile("(?m)^installPackagePath=\".*\"\n") - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - inPath := filepath.Join(testDataDir, "input", tt.desc+".yaml") - outPath := filepath.Join(testDataDir, "output", tt.desc+".txt") - - got, err := runProfileDump(inPath, tt.configPath, snapshotCharts, "flags") - if err != nil { - t.Fatal(err) - } - // installPackagePath may change, we will remove it for consistent output - got = installPackagePathRegex.ReplaceAllString(got, "") - - if refreshGoldenFiles() { - t.Logf("Refreshing golden file for %s", outPath) - if err := os.WriteFile(outPath, []byte(got), 0o644); err != nil { - t.Error(err) - } - } - - want, err := readFile(outPath) - if err != nil { - t.Fatal(err) - } - if got != want { - t.Errorf("profile-dump command(%s): got:\n%s\n\nwant:\n%s\nDiff:\n%s\n", tt.desc, got, want, diff.Diff(got, want)) - } - }) - } -} diff --git a/operator/cmd/mesh/profile-list.go b/operator/cmd/mesh/profile-list.go deleted file mode 100644 index 51391a08d..000000000 --- a/operator/cmd/mesh/profile-list.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "sort" -) - -import ( - "github.com/spf13/cobra" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/helm" -) - -type profileListArgs struct { - // manifestsPath is a path to a charts and profiles directory in the local filesystem, or URL with a release tgz. - manifestsPath string -} - -func addProfileListFlags(cmd *cobra.Command, args *profileListArgs) { - cmd.PersistentFlags().StringVarP(&args.manifestsPath, "charts", "", "", ChartsDeprecatedStr) - cmd.PersistentFlags().StringVarP(&args.manifestsPath, "manifests", "d", "", ManifestsFlagHelpStr) -} - -func profileListCmd(rootArgs *RootArgs, plArgs *profileListArgs) *cobra.Command { - return &cobra.Command{ - Use: "list", - Short: "Lists available Istio configuration profiles", - Long: "The list subcommand lists the available Istio configuration profiles.", - Args: cobra.ExactArgs(0), - RunE: func(cmd *cobra.Command, args []string) error { - return profileList(cmd, rootArgs, plArgs) - }, - } -} - -// profileList list all the builtin profiles. -func profileList(cmd *cobra.Command, args *RootArgs, plArgs *profileListArgs) error { - initLogsOrExit(args) - profiles, err := helm.ListProfiles(plArgs.manifestsPath) - if err != nil { - return err - } - if len(profiles) == 0 { - cmd.Println("No profiles available.") - } else { - cmd.Println("Istio configuration profiles:") - sort.Strings(profiles) - for _, profile := range profiles { - cmd.Printf(" %s\n", profile) - } - } - - return nil -} diff --git a/operator/cmd/mesh/profile-list_test.go b/operator/cmd/mesh/profile-list_test.go deleted file mode 100644 index 8ae371c5e..000000000 --- a/operator/cmd/mesh/profile-list_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "bytes" - "path/filepath" - "testing" -) - -import ( - "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/env" -) - -func TestProfileList(t *testing.T) { - g := gomega.NewWithT(t) - args := []string{"profile", "list", "--dry-run", "--manifests", filepath.Join(env.IstioSrc, "manifests")} - - rootCmd := GetRootCmd(args) - var out bytes.Buffer - rootCmd.SetOut(&out) - rootCmd.SetErr(&out) - - err := rootCmd.Execute() - if err != nil { - t.Fatalf("failed to execute istioctl profile command: %v", err) - } - output := out.String() - expectedProfiles := []string{"default", "demo", "empty", "minimal", "openshift", "preview", "remote"} - for _, prof := range expectedProfiles { - g.Expect(output).To(gomega.ContainSubstring(prof)) - } -} diff --git a/operator/cmd/mesh/profile.go b/operator/cmd/mesh/profile.go deleted file mode 100644 index 790fa75a8..000000000 --- a/operator/cmd/mesh/profile.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "github.com/spf13/cobra" - "istio.io/pkg/log" -) - -// ProfileCmd is a group of commands related to profile listing, dumping and diffing. -func ProfileCmd(logOpts *log.Options) *cobra.Command { - pc := &cobra.Command{ - Use: "profile", - Short: "Commands related to Istio configuration profiles", - Long: "The profile command lists, dumps or diffs Istio configuration profiles.", - Example: "istioctl profile list\n" + - "istioctl install --set profile=demo # Use a profile from the list", - } - - pdArgs := &profileDumpArgs{} - plArgs := &profileListArgs{} - pdfArgs := &profileDiffArgs{} - args := &RootArgs{} - - plc := profileListCmd(args, plArgs) - pdc := profileDumpCmd(args, pdArgs, logOpts) - pdfc := profileDiffCmd(args, pdfArgs, logOpts) - - addFlags(pc, args) - addFlags(plc, args) - addFlags(pdc, args) - addFlags(pdfc, args) - - addProfileDumpFlags(pdc, pdArgs) - addProfileListFlags(plc, plArgs) - addProfileDiffFlags(pdfc, pdfArgs) - - pc.AddCommand(plc) - pc.AddCommand(pdc) - pc.AddCommand(pdfc) - - return pc -} diff --git a/operator/cmd/mesh/root.go b/operator/cmd/mesh/root.go deleted file mode 100644 index aa3cc54e4..000000000 --- a/operator/cmd/mesh/root.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "flag" -) - -import ( - "github.com/spf13/cobra" - "istio.io/pkg/log" - "istio.io/pkg/version" -) - -import ( - binversion "github.com/apache/dubbo-go-pixiu/operator/version" - "github.com/apache/dubbo-go-pixiu/pkg/url" -) - -var ( - baseVersion = binversion.OperatorVersionString - setFlagHelpStr = `Override an IstioOperator value, e.g. to choose a profile -(--set profile=demo), enable or disable components (--set components.cni.enabled=true), or override Istio -settings (--set meshConfig.enableTracing=true). See documentation for more info:` + url.IstioOperatorSpec - // ManifestsFlagHelpStr is the command line description for --manifests - ManifestsFlagHelpStr = `Specify a path to a directory of charts and profiles -(e.g. ~/Downloads/istio-` + baseVersion + `/manifests) -or release tar URL (e.g. ` + url.ReleaseTar + `). -` -) - -const ( - ChartsDeprecatedStr = "Deprecated, use --manifests instead." - ControlPlaneRevStr = "Control plane revision" - revisionFlagHelpStr = `Target control plane revision for the command.` - skipConfirmationFlagHelpStr = `The skipConfirmation determines whether the user is prompted for confirmation. -If set to true, the user is not prompted and a Yes response is assumed in all cases.` - filenameFlagHelpStr = `Path to file containing IstioOperator custom resource -This flag can be specified multiple times to overlay multiple files. Multiple files are overlaid in left to right order.` - installationCompleteStr = `Installation complete` - ForceFlagHelpStr = `Proceed even with validation errors.` - KubeConfigFlagHelpStr = `Path to kube config.` - ContextFlagHelpStr = `The name of the kubeconfig context to use.` - HubFlagHelpStr = `The hub for the operator controller image.` - TagFlagHelpStr = `The tag for the operator controller image.` - ImagePullSecretsHelpStr = `The imagePullSecrets are used to pull the operator image from the private registry, -could be secret list separated by comma, eg. '--imagePullSecrets imagePullSecret1,imagePullSecret2'` - OperatorNamespaceHelpstr = `The namespace the operator controller is installed into.` - OperatorRevFlagHelpStr = `Target revision for the operator.` - ComponentFlagHelpStr = "Specify which component to generate manifests for." - VerifyCRInstallHelpStr = "Verify the Istio control plane after installation/in-place upgrade" -) - -type RootArgs struct { - // DryRun performs all steps except actually applying the manifests or creating output dirs/files. - DryRun bool -} - -func addFlags(cmd *cobra.Command, rootArgs *RootArgs) { - cmd.PersistentFlags().BoolVarP(&rootArgs.DryRun, "dry-run", "", - false, "Console/log output only, make no changes.") -} - -// GetRootCmd returns the root of the cobra command-tree. -func GetRootCmd(args []string) *cobra.Command { - rootCmd := &cobra.Command{ - Use: "mesh", - Short: "Command line Istio install utility.", - SilenceUsage: true, - Long: "This command uses the Istio operator code to generate templates, query configurations and perform " + - "utility operations.", - } - rootCmd.SetArgs(args) - rootCmd.PersistentFlags().AddGoFlagSet(flag.CommandLine) - - rootCmd.AddCommand(ManifestCmd(log.DefaultOptions())) - rootCmd.AddCommand(InstallCmd(log.DefaultOptions())) - rootCmd.AddCommand(ProfileCmd(log.DefaultOptions())) - rootCmd.AddCommand(OperatorCmd()) - rootCmd.AddCommand(version.CobraCommand()) - rootCmd.AddCommand(UpgradeCmd(log.DefaultOptions())) - - return rootCmd -} diff --git a/operator/cmd/mesh/shared.go b/operator/cmd/mesh/shared.go deleted file mode 100644 index 4bd2b41bc..000000000 --- a/operator/cmd/mesh/shared.go +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package mesh contains types and functions. -package mesh - -import ( - "fmt" - "io" - "os" - "strings" - "sync" - "time" -) - -import ( - "istio.io/pkg/log" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/install/k8sversion" - "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/cache" - "github.com/apache/dubbo-go-pixiu/operator/pkg/helmreconciler" - "github.com/apache/dubbo-go-pixiu/operator/pkg/manifest" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -var ( - // installerScope is the scope for all commands in the mesh package. - installerScope = log.RegisterScope("installer", "installer", 0) - - // testK8Interface is used if it is set. Not possible to inject due to cobra command boundary. - testK8Interface *kubernetes.Clientset - testRestConfig *rest.Config -) - -type Printer interface { - Printf(format string, a ...interface{}) - Println(string) -} - -func NewPrinterForWriter(w io.Writer) Printer { - return &writerPrinter{writer: w} -} - -type writerPrinter struct { - writer io.Writer -} - -func (w *writerPrinter) Printf(format string, a ...interface{}) { - _, _ = fmt.Fprintf(w.writer, format, a...) -} - -func (w *writerPrinter) Println(str string) { - _, _ = fmt.Fprintln(w.writer, str) -} - -func initLogsOrExit(_ *RootArgs) { - if err := configLogs(log.DefaultOptions()); err != nil { - _, _ = fmt.Fprintf(os.Stderr, "Could not configure logs: %s", err) - os.Exit(1) - } -} - -var logMutex = sync.Mutex{} - -func configLogs(opt *log.Options) error { - logMutex.Lock() - defer logMutex.Unlock() - op := []string{"stderr"} - opt2 := *opt - opt2.OutputPaths = op - opt2.ErrorOutputPaths = op - - return log.Configure(&opt2) -} - -func refreshGoldenFiles() bool { - ev := os.Getenv("REFRESH_GOLDEN") - return ev == "true" || ev == "1" -} - -func kubeBuilderInstalled() bool { - ev := os.Getenv("KUBEBUILDER") - return ev == "true" || ev == "1" -} - -// confirm waits for a user to confirm with the supplied message. -func confirm(msg string, writer io.Writer) bool { - _, _ = fmt.Fprintf(writer, "%s ", msg) - - var response string - _, err := fmt.Scanln(&response) - if err != nil { - return false - } - response = strings.ToUpper(response) - if response == "Y" || response == "YES" { - return true - } - - return false -} - -func KubernetesClients(kubeConfigPath, context string, l clog.Logger) (kube.ExtendedClient, client.Client, error) { - rc, err := kube.DefaultRestConfig(kubeConfigPath, context, func(config *rest.Config) { - // We are running a one-off command locally, so we don't need to worry too much about rate limitting - // Bumping this up greatly decreases install time - config.QPS = 50 - config.Burst = 100 - }) - if err != nil { - return nil, nil, err - } - kubeClient, err := kube.NewExtendedClient(kube.NewClientConfigForRestConfig(rc), "") - if err != nil { - return nil, nil, fmt.Errorf("create Kubernetes client: %v", err) - } - client, err := client.New(kubeClient.RESTConfig(), client.Options{Scheme: kube.IstioScheme}) - if err != nil { - return nil, nil, err - } - if err := k8sversion.IsK8VersionSupported(kubeClient, l); err != nil { - return nil, nil, fmt.Errorf("check minimum supported Kubernetes version: %v", err) - } - return kubeClient, client, nil -} - -// applyOptions contains the startup options for applying the manifest. -type applyOptions struct { - // Path to the kubeconfig file. - Kubeconfig string - // ComponentName of the kubeconfig context to use. - Context string - // DryRun performs all steps except actually applying the manifests or creating output dirs/files. - DryRun bool - // Maximum amount of time to wait for resources to be ready after install when Wait=true. - WaitTimeout time.Duration -} - -func applyManifest(kubeClient kube.Client, client client.Client, manifestStr string, - componentName name.ComponentName, opts *applyOptions, iop *v1alpha1.IstioOperator, l clog.Logger) error { - // Needed in case we are running a test through this path that doesn't start a new process. - cache.FlushObjectCaches() - reconciler, err := helmreconciler.NewHelmReconciler(client, kubeClient, iop, &helmreconciler.Options{DryRun: opts.DryRun, Log: l}) - if err != nil { - l.LogAndError(err) - return err - } - ms := name.Manifest{ - Name: componentName, - Content: manifestStr, - } - _, _, err = reconciler.ApplyManifest(ms, reconciler.CheckSSAEnabled()) - return err -} - -// --manifests is an alias for --set installPackagePath= -// --revision is an alias for --set revision= -func applyFlagAliases(flags []string, manifestsPath, revision string) []string { - if manifestsPath != "" { - flags = append(flags, fmt.Sprintf("installPackagePath=%s", manifestsPath)) - } - if revision != "" { - flags = append(flags, fmt.Sprintf("revision=%s", revision)) - } - return flags -} - -// getCRAndNamespaceFromFile returns the CR name and istio namespace from a file containing an IstioOperator CR. -func getCRAndNamespaceFromFile(filePath string, l clog.Logger) (customResource string, istioNamespace string, err error) { - if filePath == "" { - return "", "", nil - } - - _, mergedIOPS, err := manifest.GenerateConfig([]string{filePath}, nil, false, nil, l) - if err != nil { - return "", "", err - } - - b, err := os.ReadFile(filePath) - if err != nil { - return "", "", fmt.Errorf("could not read values from file %s: %s", filePath, err) - } - customResource = string(b) - istioNamespace = mergedIOPS.Namespace - return -} - -// createNamespace creates a namespace using the given k8s interface. -func createNamespace(cs kubernetes.Interface, namespace string, network string, dryRun bool) error { - return helmreconciler.CreateNamespace(cs, namespace, network, dryRun) -} - -// saveIOPToCluster saves the state in an IOP CR in the cluster. -func saveIOPToCluster(reconciler *helmreconciler.HelmReconciler, iop string) error { - obj, err := object.ParseYAMLToK8sObject([]byte(iop)) - if err != nil { - return err - } - return reconciler.ApplyObject(obj.UnstructuredObject(), false) -} diff --git a/operator/cmd/mesh/test-util.go b/operator/cmd/mesh/test-util.go deleted file mode 100644 index fcb95b42f..000000000 --- a/operator/cmd/mesh/test-util.go +++ /dev/null @@ -1,523 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "fmt" - "os" - "reflect" - "regexp" - "strings" - "testing" -) - -import ( - "github.com/onsi/gomega" - "github.com/onsi/gomega/types" - "istio.io/pkg/log" - labels2 "k8s.io/apimachinery/pkg/labels" -) - -import ( - name2 "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" - "github.com/apache/dubbo-go-pixiu/operator/pkg/tpath" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -// PathValue is a path/value type. -type PathValue struct { - path string - value interface{} -} - -// String implements the Stringer interface. -func (pv *PathValue) String() string { - return fmt.Sprintf("%s:%v", pv.path, pv.value) -} - -// ObjectSet is a set of objects maintained both as a slice (for ordering) and map (for speed). -type ObjectSet struct { - objSlice object.K8sObjects - objMap map[string]*object.K8sObject - keySlice []string -} - -// NewObjectSet creates a new ObjectSet from objs and returns a pointer to it. -func NewObjectSet(objs object.K8sObjects) *ObjectSet { - ret := &ObjectSet{} - for _, o := range objs { - ret.append(o) - } - return ret -} - -// parseObjectSetFromManifest parses an ObjectSet from the given manifest. -func parseObjectSetFromManifest(manifest string) (*ObjectSet, error) { - objSlice, err := object.ParseK8sObjectsFromYAMLManifest(manifest) - return NewObjectSet(objSlice), err -} - -// append appends an object to o. -func (o *ObjectSet) append(obj *object.K8sObject) { - h := obj.Hash() - o.objSlice = append(o.objSlice, obj) - if o.objMap == nil { - o.objMap = make(map[string]*object.K8sObject) - } - o.objMap[h] = obj - o.keySlice = append(o.keySlice, h) -} - -// size reports the length of o. -func (o *ObjectSet) size() int { - return len(o.keySlice) -} - -// nameMatches returns a subset of o where objects names match the given regex. -func (o *ObjectSet) nameMatches(nameRegex string) *ObjectSet { - ret := &ObjectSet{} - for k, v := range o.objMap { - _, _, objName := object.FromHash(k) - m, err := regexp.MatchString(nameRegex, objName) - if err != nil { - log.Error(err.Error()) - continue - } - if m { - ret.append(v) - } - } - return ret -} - -// nameEquals returns the object in o whose name matches "name", or nil if no object name matches. -func (o *ObjectSet) nameEquals(name string) *object.K8sObject { - for k, v := range o.objMap { - _, _, objName := object.FromHash(k) - if objName == name { - return v - } - } - return nil -} - -// kind returns a subset of o where kind matches the given value. -func (o *ObjectSet) kind(kind string) *ObjectSet { - ret := &ObjectSet{} - for k, v := range o.objMap { - objKind, _, _ := object.FromHash(k) - if objKind == kind { - ret.append(v) - } - } - return ret -} - -// namespace returns a subset of o where namespace matches the given value or fails if it's not found in objs. -func (o *ObjectSet) namespace(namespace string) *ObjectSet { - ret := &ObjectSet{} - for k, v := range o.objMap { - _, objNamespace, _ := object.FromHash(k) - if objNamespace == namespace { - ret.append(v) - } - } - return ret -} - -// labels returns a subset of o where the object's labels match all the given labels. -func (o *ObjectSet) labels(labels ...string) *ObjectSet { - ret := &ObjectSet{} - for _, obj := range o.objMap { - hasAll := true - for _, l := range labels { - lkv := strings.Split(l, "=") - if len(lkv) != 2 { - panic("label must have format key=value") - } - if !hasLabel(obj, lkv[0], lkv[1]) { - hasAll = false - break - } - } - if hasAll { - ret.append(obj) - } - } - return ret -} - -// HasLabel reports whether 0 has the given label. -func hasLabel(o *object.K8sObject, label, value string) bool { - got, found, err := tpath.Find(o.UnstructuredObject().UnstructuredContent(), util.PathFromString("metadata.labels")) - if err != nil { - log.Errorf("bad path: %s", err) - return false - } - if !found { - return false - } - return got.(map[string]interface{})[label] == value -} - -// mustGetService returns the service with the given name or fails if it's not found in objs. -func mustGetService(g *gomega.WithT, objs *ObjectSet, name string) *object.K8sObject { - obj := objs.kind(name2.ServiceStr).nameEquals(name) - g.Expect(obj).Should(gomega.Not(gomega.BeNil())) - return obj -} - -// mustGetDeployment returns the deployment with the given name or fails if it's not found in objs. -func mustGetDeployment(g *gomega.WithT, objs *ObjectSet, deploymentName string) *object.K8sObject { - obj := objs.kind(name2.DeploymentStr).nameEquals(deploymentName) - g.Expect(obj).Should(gomega.Not(gomega.BeNil())) - return obj -} - -// mustGetClusterRole returns the clusterRole with the given name or fails if it's not found in objs. -func mustGetClusterRole(g *gomega.WithT, objs *ObjectSet, name string) *object.K8sObject { - obj := objs.kind(name2.ClusterRoleStr).nameEquals(name) - g.Expect(obj).Should(gomega.Not(gomega.BeNil())) - return obj -} - -// mustGetRole returns the role with the given name or fails if it's not found in objs. -func mustGetRole(g *gomega.WithT, objs *ObjectSet, name string) *object.K8sObject { - obj := objs.kind(name2.RoleStr).nameEquals(name) - g.Expect(obj).Should(gomega.Not(gomega.BeNil())) - return obj -} - -// mustGetContainer returns the container tree with the given name in the deployment with the given name. -func mustGetContainer(g *gomega.WithT, objs *ObjectSet, deploymentName, containerName string) map[string]interface{} { - obj := mustGetDeployment(g, objs, deploymentName) - container := obj.Container(containerName) - g.Expect(container).Should(gomega.Not(gomega.BeNil()), fmt.Sprintf("Expected to get container %s in deployment %s", containerName, deploymentName)) - return container -} - -// mustGetEndpoint returns the endpoint tree with the given name in the deployment with the given name. -func mustGetEndpoint(g *gomega.WithT, objs *ObjectSet, endpointName string) *object.K8sObject { - obj := objs.kind(name2.EndpointStr).nameEquals(endpointName) - if obj == nil { - return nil - } - g.Expect(obj).Should(gomega.Not(gomega.BeNil())) - return obj -} - -// mustGetMutatingWebhookConfiguration returns the mutatingWebhookConfiguration with the given name or fails if it's not found in objs. -func mustGetMutatingWebhookConfiguration(g *gomega.WithT, objs *ObjectSet, mutatingWebhookConfigurationName string) *object.K8sObject { - obj := objs.kind(name2.MutatingWebhookConfigurationStr).nameEquals(mutatingWebhookConfigurationName) - g.Expect(obj).Should(gomega.Not(gomega.BeNil())) - return obj -} - -// HavePathValueEqual matches map[string]interface{} tree against a PathValue. -func HavePathValueEqual(expected interface{}) types.GomegaMatcher { - return &HavePathValueEqualMatcher{ - expected: expected, - } -} - -// HavePathValueEqualMatcher is a matcher type for HavePathValueEqual. -type HavePathValueEqualMatcher struct { - expected interface{} -} - -// Match implements the Matcher interface. -func (m *HavePathValueEqualMatcher) Match(actual interface{}) (bool, error) { - pv := m.expected.(PathValue) - node := actual.(map[string]interface{}) - got, f, err := tpath.GetPathContext(node, util.PathFromString(pv.path), false) - if err != nil || !f { - return false, err - } - if reflect.TypeOf(got.Node) != reflect.TypeOf(pv.value) { - return false, fmt.Errorf("comparison types don't match: got %v(%T), want %v(%T)", got.Node, got.Node, pv.value, pv.value) - } - if !reflect.DeepEqual(got.Node, pv.value) { - return false, fmt.Errorf("values don't match: got %v, want %v", got.Node, pv.value) - } - return true, nil -} - -// FailureMessage implements the Matcher interface. -func (m *HavePathValueEqualMatcher) FailureMessage(actual interface{}) string { - pv := m.expected.(PathValue) - node := actual.(map[string]interface{}) - return fmt.Sprintf("Expected the following parseObjectSetFromManifest to have path=value %s=%v\n\n%v", pv.path, pv.value, util.ToYAML(node)) -} - -// NegatedFailureMessage implements the Matcher interface. -func (m *HavePathValueEqualMatcher) NegatedFailureMessage(actual interface{}) string { - pv := m.expected.(PathValue) - node := actual.(map[string]interface{}) - return fmt.Sprintf("Expected the following parseObjectSetFromManifest not to have path=value %s=%v\n\n%v", pv.path, pv.value, util.ToYAML(node)) -} - -// HavePathValueMatchRegex matches map[string]interface{} tree against a PathValue. -func HavePathValueMatchRegex(expected interface{}) types.GomegaMatcher { - return &HavePathValueMatchRegexMatcher{ - expected: expected, - } -} - -// HavePathValueMatchRegexMatcher is a matcher type for HavePathValueMatchRegex. -type HavePathValueMatchRegexMatcher struct { - expected interface{} -} - -// Match implements the Matcher interface. -func (m *HavePathValueMatchRegexMatcher) Match(actual interface{}) (bool, error) { - pv := m.expected.(PathValue) - node := actual.(map[string]interface{}) - got, f, err := tpath.GetPathContext(node, util.PathFromString(pv.path), false) - if err != nil || !f { - return false, err - } - if reflect.TypeOf(got.Node).Kind() != reflect.String || reflect.TypeOf(pv.value).Kind() != reflect.String { - return false, fmt.Errorf("comparison types must both be string: got %v(%T), want %v(%T)", got.Node, got.Node, pv.value, pv.value) - } - gotS := got.Node.(string) - wantS := pv.value.(string) - ok, err := regexp.MatchString(wantS, gotS) - if err != nil { - return false, err - } - if !ok { - return false, fmt.Errorf("values don't match: got %v, want %v", got.Node, pv.value) - } - return true, nil -} - -// FailureMessage implements the Matcher interface. -func (m *HavePathValueMatchRegexMatcher) FailureMessage(actual interface{}) string { - pv := m.expected.(PathValue) - node := actual.(map[string]interface{}) - return fmt.Sprintf("Expected the following parseObjectSetFromManifest to regex match path=value %s=%v\n\n%v", pv.path, pv.value, util.ToYAML(node)) -} - -// NegatedFailureMessage implements the Matcher interface. -func (m *HavePathValueMatchRegexMatcher) NegatedFailureMessage(actual interface{}) string { - pv := m.expected.(PathValue) - node := actual.(map[string]interface{}) - return fmt.Sprintf("Expected the following parseObjectSetFromManifest not to regex match path=value %s=%v\n\n%v", pv.path, pv.value, util.ToYAML(node)) -} - -// HavePathValueContain matches map[string]interface{} tree against a PathValue. -func HavePathValueContain(expected interface{}) types.GomegaMatcher { - return &HavePathValueContainMatcher{ - expected: expected, - } -} - -// HavePathValueContainMatcher is a matcher type for HavePathValueContain. -type HavePathValueContainMatcher struct { - expected interface{} -} - -// Match implements the Matcher interface. -func (m *HavePathValueContainMatcher) Match(actual interface{}) (bool, error) { - pv := m.expected.(PathValue) - node := actual.(map[string]interface{}) - got, f, err := tpath.GetPathContext(node, util.PathFromString(pv.path), false) - if err != nil || !f { - return false, err - } - if reflect.TypeOf(got.Node) != reflect.TypeOf(pv.value) { - return false, fmt.Errorf("comparison types don't match: got %T, want %T", got.Node, pv.value) - } - gotValStr := util.ToYAML(got.Node) - subsetValStr := util.ToYAML(pv.value) - overlay, err := util.OverlayYAML(gotValStr, subsetValStr) - if err != nil { - return false, err - } - if overlay != gotValStr { - return false, fmt.Errorf("actual value:\n\n%s\ndoesn't contain expected subset:\n\n%s", gotValStr, subsetValStr) - } - return true, nil -} - -// FailureMessage implements the Matcher interface. -func (m *HavePathValueContainMatcher) FailureMessage(actual interface{}) string { - pv := m.expected.(PathValue) - node := actual.(map[string]interface{}) - return fmt.Sprintf("Expected path %s with value \n\n%v\nto be a subset of \n\n%v", pv.path, pv.value, util.ToYAML(node)) -} - -// NegatedFailureMessage implements the Matcher interface. -func (m *HavePathValueContainMatcher) NegatedFailureMessage(actual interface{}) string { - pv := m.expected.(PathValue) - node := actual.(map[string]interface{}) - return fmt.Sprintf("Expected path %s with value \n\n%v\nto NOT be a subset of \n\n%v", pv.path, pv.value, util.ToYAML(node)) -} - -func mustSelect(t test.Failer, selector map[string]string, labels map[string]string) { - t.Helper() - kselector := labels2.Set(selector).AsSelectorPreValidated() - if !kselector.Matches(labels2.Set(labels)) { - t.Fatalf("%v does not select %v", selector, labels) - } -} - -func mustNotSelect(t test.Failer, selector map[string]string, labels map[string]string) { - t.Helper() - kselector := labels2.Set(selector).AsSelectorPreValidated() - if kselector.Matches(labels2.Set(labels)) { - t.Fatalf("%v selects %v when it should not", selector, labels) - } -} - -func mustGetLabels(t test.Failer, obj object.K8sObject, path string) map[string]string { - t.Helper() - got := mustGetPath(t, obj, path) - conv, ok := got.(map[string]interface{}) - if !ok { - t.Fatalf("could not convert %v", got) - } - ret := map[string]string{} - for k, v := range conv { - sv, ok := v.(string) - if !ok { - t.Fatalf("could not convert to string %v", v) - } - ret[k] = sv - } - return ret -} - -func mustGetPath(t test.Failer, obj object.K8sObject, path string) interface{} { - t.Helper() - got, f, err := tpath.Find(obj.UnstructuredObject().UnstructuredContent(), util.PathFromString(path)) - if err != nil { - t.Fatal(err) - } - if !f { - t.Fatalf("couldn't find path %v", path) - } - return got -} - -func mustFindObject(t test.Failer, objs object.K8sObjects, name, kind string) object.K8sObject { - t.Helper() - o := findObject(objs, name, kind) - if o == nil { - t.Fatalf("expected %v/%v", name, kind) - return object.K8sObject{} - } - return *o -} - -func findObject(objs object.K8sObjects, name, kind string) *object.K8sObject { - for _, o := range objs { - if o.Kind == kind && o.Name == name { - return o - } - } - return nil -} - -// mustGetValueAtPath returns the value at the given path in the unstructured tree t. Fails if the path is not found -// in the tree. -func mustGetValueAtPath(g *gomega.WithT, t map[string]interface{}, path string) interface{} { - got, f, err := tpath.GetPathContext(t, util.PathFromString(path), false) - g.Expect(err).Should(gomega.BeNil(), "path %s should exist (%s)", path, err) - g.Expect(f).Should(gomega.BeTrue(), "path %s should exist", path) - return got.Node -} - -func createTempDirOrFail(t *testing.T, prefix string) string { - dir, err := os.MkdirTemp("", prefix) - if err != nil { - t.Fatal(err) - } - return dir -} - -func removeDirOrFail(t *testing.T, path string) { - err := os.RemoveAll(path) - if err != nil { - t.Fatal(err) - } -} - -// toMap transforms a comma separated key:value list (e.g. "a:aval, b:bval") to a map. -func toMap(s string) map[string]interface{} { - out := make(map[string]interface{}) - for _, l := range strings.Split(s, ",") { - l = strings.TrimSpace(l) - kv := strings.Split(l, ":") - if len(kv) != 2 { - panic("bad key:value in " + s) - } - out[strings.TrimSpace(kv[0])] = strings.TrimSpace(kv[1]) - } - if len(out) == 0 { - return nil - } - return out -} - -// endpointSubsetAddressVal returns a map having subset address type for an endpint. -func endpointSubsetAddressVal(hostname, ip, nodeName string) map[string]interface{} { - out := make(map[string]interface{}) - if hostname != "" { - out["hostname"] = hostname - } - if ip != "" { - out["ip"] = ip - } - if nodeName != "" { - out["nodeName"] = nodeName - } - return out -} - -// portVal returns a map having service port type. A value of -1 for port or targetPort leaves those keys unset. -func portVal(name string, port, targetPort int64) map[string]interface{} { - out := make(map[string]interface{}) - if name != "" { - out["name"] = name - } - if port != -1 { - out["port"] = port - } - if targetPort != -1 { - out["targetPort"] = targetPort - } - return out -} - -// checkRoleBindingsReferenceRoles fails if any RoleBinding in objs references a Role that isn't found in objs. -func checkRoleBindingsReferenceRoles(g *gomega.WithT, objs *ObjectSet) { - for _, o := range objs.kind(name2.RoleBindingStr).objSlice { - ou := o.Unstructured() - rrname := mustGetValueAtPath(g, ou, "roleRef.name") - mustGetRole(g, objs, rrname.(string)) - } -} - -// checkClusterRoleBindingsReferenceRoles fails if any RoleBinding in objs references a Role that isn't found in objs. -func checkClusterRoleBindingsReferenceRoles(g *gomega.WithT, objs *ObjectSet) { - for _, o := range objs.kind(name2.ClusterRoleBindingStr).objSlice { - ou := o.Unstructured() - rrname := mustGetValueAtPath(g, ou, "roleRef.name") - mustGetClusterRole(g, objs, rrname.(string)) - } -} diff --git a/operator/cmd/mesh/testdata/manifest-generate/data-snapshot.tar.gz b/operator/cmd/mesh/testdata/manifest-generate/data-snapshot.tar.gz deleted file mode 100644 index 527cce66f..000000000 Binary files a/operator/cmd/mesh/testdata/manifest-generate/data-snapshot.tar.gz and /dev/null differ diff --git a/operator/cmd/mesh/testdata/manifest-generate/input-extra-resources/duplicate_mwc.yaml b/operator/cmd/mesh/testdata/manifest-generate/input-extra-resources/duplicate_mwc.yaml deleted file mode 100644 index b101f6059..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input-extra-resources/duplicate_mwc.yaml +++ /dev/null @@ -1,201 +0,0 @@ -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - labels: - app: sidecar-injector - name: w-istio-sidecar-injector-dubbo-system - -webhooks: -- admissionReviewVersions: - - v1beta1 - - v1 - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: /inject - port: 443 - failurePolicy: Fail - matchPolicy: Equivalent - name: rev.namespace.sidecar-injector.istio.io - namespaceSelector: - matchExpressions: - - key: istio.io/rev - operator: In - values: - - default - - key: istio-injection - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: istio.io/rev - operator: NotIn - values: - - canary - reinvocationPolicy: Never - rules: - - apiGroups: - - "" - apiVersions: - - v1 - operations: - - CREATE - resources: - - pods - scope: '*' - sideEffects: None - timeoutSeconds: 10 -- admissionReviewVersions: - - v1beta1 - - v1 - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: /inject - port: 443 - failurePolicy: Fail - matchPolicy: Equivalent - name: rev.object.sidecar-injector.istio.io - namespaceSelector: - matchExpressions: - - key: istio.io/rev - operator: DoesNotExist - - key: istio-injection - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" - - key: istio.io/rev - operator: In - values: - - default - reinvocationPolicy: Never - rules: - - apiGroups: - - "" - apiVersions: - - v1 - operations: - - CREATE - resources: - - pods - scope: '*' - sideEffects: None - timeoutSeconds: 10 -- admissionReviewVersions: - - v1beta1 - - v1 - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: /inject - port: 443 - failurePolicy: Fail - matchPolicy: Equivalent - name: namespace.sidecar-injector.istio.io - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: In - values: - - enabled - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" - reinvocationPolicy: Never - rules: - - apiGroups: - - "" - apiVersions: - - v1 - operations: - - CREATE - resources: - - pods - scope: '*' - sideEffects: None - timeoutSeconds: 10 -- admissionReviewVersions: - - v1beta1 - - v1 - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: /inject - port: 443 - failurePolicy: Fail - matchPolicy: Equivalent - name: object.sidecar-injector.istio.io - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: DoesNotExist - - key: istio.io/rev - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: In - values: - - "true" - - key: istio.io/rev - operator: DoesNotExist - reinvocationPolicy: Never - rules: - - apiGroups: - - "" - apiVersions: - - v1 - operations: - - CREATE - resources: - - pods - scope: '*' - sideEffects: None - timeoutSeconds: 10 -- admissionReviewVersions: - - v1beta1 - - v1 - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: /inject - port: 443 - failurePolicy: Fail - matchPolicy: Equivalent - name: auto.sidecar-injector.istio.io - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: DoesNotExist - - key: istio.io/rev - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: DoesNotExist - - key: istio.io/rev - operator: DoesNotExist - reinvocationPolicy: Never - rules: - - apiGroups: - - "" - apiVersions: - - v1 - operations: - - CREATE - resources: - - pods - scope: '*' - sideEffects: None - timeoutSeconds: 10 diff --git a/operator/cmd/mesh/testdata/manifest-generate/input-extra-resources/gateways.yaml b/operator/cmd/mesh/testdata/manifest-generate/input-extra-resources/gateways.yaml deleted file mode 100644 index bff107ff8..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input-extra-resources/gateways.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: user-ingressgateway-ns - labels: - istio-injection: disabled - diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/all_off.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/all_off.yaml deleted file mode 100644 index c3fa5405a..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/all_off.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: empty diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/all_on.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/all_on.yaml deleted file mode 100644 index 89d118aa4..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/all_on.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - components: - base: - enabled: true - pilot: - enabled: true - cni: - enabled: false - ingressGateways: - - namespace: dubbo-system - name: istio-ingressgateway - enabled: true - egressGateways: - - namespace: dubbo-system - name: istio-egressgateway - enabled: true diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/bare_spec.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/bare_spec.yaml deleted file mode 100644 index a8f88862a..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/bare_spec.yaml +++ /dev/null @@ -1,3 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/bare_values.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/bare_values.yaml deleted file mode 100644 index 52100d866..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/bare_values.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -metadata: - namespace: dubbo-system -spec: - values: diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/bogus_cps.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/bogus_cps.yaml deleted file mode 100644 index 493da0392..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/bogus_cps.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -metadata: - namespace: dubbo-system -spec: - values: - global: - mountMtlsCerts: - pilot: - autoscaleEnabled: diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/component_hub_tag.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/component_hub_tag.yaml deleted file mode 100644 index 971e8a180..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/component_hub_tag.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - hub: istio-spec.hub - tag: istio-spec.tag - components: - pilot: - enabled: true - hub: component.pilot.hub - tag: 2 - cni: - enabled: true - hub: component.cni.hub - tag: v3.3.3 - diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/default.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/default.yaml deleted file mode 100644 index 56179d7b6..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/default.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: default - diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/deprecated_autoscaling_k8s_spec.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/deprecated_autoscaling_k8s_spec.yaml deleted file mode 100644 index fc501a2ba..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/deprecated_autoscaling_k8s_spec.yaml +++ /dev/null @@ -1,36 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: empty - hub: docker.io/istio - tag: 1.1.4 - meshConfig: - rootNamespace: istio-control - components: - pilot: - enabled: true - namespace: istio-control - k8s: - hpaSpec: - maxReplicas: 333 - scaleTargetRef: - name: istio-pilot - metrics: - - type: Resource - resource: - name: cpu - targetAverageUtilization: 444 - ingressGateways: - - namespace: dubbo-system - name: istio-ingressgateway - enabled: true - k8s: - hpaSpec: - maxReplicas: 222 - scaleTargetRef: - name: istio-ingressgateway - metrics: - - type: Resource - object: - metricName: cpu - averageValue: 111 \ No newline at end of file diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/duplicate_mwc.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/duplicate_mwc.yaml deleted file mode 100644 index 6bd0b9a02..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/duplicate_mwc.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -metadata: - name: istiod - namespace: dubbo-system -spec: - profile: default - namespace: dubbo-system - diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/empty.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/empty.yaml deleted file mode 100644 index 7d7b08037..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/empty.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: empty - diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/flag_force.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/flag_force.yaml deleted file mode 100644 index 06d9bef9f..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/flag_force.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: empty - components: - pilot: - enabled: true - values: - global: - badKey: badValue diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/flag_output.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/flag_output.yaml deleted file mode 100644 index 09abf0edb..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/flag_output.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: empty - components: - pilot: - enabled: true diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/gateways.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/gateways.yaml deleted file mode 100644 index ab1d4098d..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/gateways.yaml +++ /dev/null @@ -1,52 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: empty - components: - ingressGateways: - - name: istio-ingressgateway - enabled: true - label: - aaa: aaa-val - bbb: bbb-val - k8s: - resources: - requests: - cpu: 111m - memory: 111Mi - - name: user-ingressgateway - enabled: true - label: - ccc: ccc-val - ddd: ddd-val - k8s: - resources: - requests: - cpu: 222m - memory: 888Mi - - namespace: user-ingressgateway-ns - name: ilb-gateway - enabled: true - k8s: - resources: - requests: - cpu: 333m - serviceAnnotations: - cloud.google.com/load-balancer-type: "internal" - service: - ports: - ## You can add custom gateway ports - google ILB default quota is 5 ports, - - port: 15011 - name: grpc-pilot-mtls - - port: 8060 - targetPort: 8060 - name: tcp-citadel-grpc-tls - # Port 5353 is forwarded to kube-dns - - port: 5353 - name: tcp-dns - overlays: - - kind: Deployment - name: ilb-gateway - patches: - - path: spec.template.spec.containers.[name:istio-proxy].env.[name:PILOT_CERT_PROVIDER].value - value: foobar # OVERRIDDEN diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/helm_values_enablement.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/helm_values_enablement.yaml deleted file mode 100644 index ed1ad1b58..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/helm_values_enablement.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - values: - gateways: - istio-egressgateway: - enabled: true \ No newline at end of file diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/ingressgateway_k8s_settings.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/ingressgateway_k8s_settings.yaml deleted file mode 100644 index 3bdec4154..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/ingressgateway_k8s_settings.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - components: - pilot: - enabled: false - ingressGateways: - - namespace: dubbo-system - name: istio-ingressgateway - enabled: true - k8s: - service: - externalTrafficPolicy: Local - serviceAnnotations: - manifest-generate: "testserviceAnnotation" - securityContext: - sysctls: - - name: "net.ipv4.ip_local_port_range" - value: "80 65535" - - namespace: dubbo-system - name: istio-ingressgateway-custom - enabled: true - k8s: - service: - externalTrafficPolicy: Local - diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/install_package_path.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/install_package_path.yaml deleted file mode 100644 index e87f9acd2..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/install_package_path.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: empty - hub: docker.io/istio - tag: 1.1.4 - components: - pilot: - enabled: true - diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/istiod_remote.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/istiod_remote.yaml deleted file mode 100644 index 60d8af246..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/istiod_remote.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: empty - components: - base: - enabled: true - pilot: - enabled: true - values: - global: - multiCluster: - clusterName: remote0 - network: network2 - externalIstiod: true - remotePilotAddress: 169.10.112.88 - istiodRemote: - injectionURL: https://xxx:15017/inject - base: - validationURL: https://xxx:15017/validate diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/minimal.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/minimal.yaml deleted file mode 100644 index 2e1f33567..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/minimal.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: minimal - diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/multiple_iops.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/multiple_iops.yaml deleted file mode 100644 index ce50538d5..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/multiple_iops.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - ---- - -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/pilot_default.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/pilot_default.yaml deleted file mode 100644 index 83e4ca264..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/pilot_default.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: empty - hub: docker.io/istio - tag: 1.1.4 - meshConfig: - rootNamespace: istio-control - components: - pilot: - enabled: true diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/pilot_disable_tracing.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/pilot_disable_tracing.yaml deleted file mode 100644 index 90726c203..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/pilot_disable_tracing.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: empty - values: - global: - proxy: - tracer: none - components: - pilot: - enabled: true diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/pilot_k8s_settings.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/pilot_k8s_settings.yaml deleted file mode 100644 index ec12e7ccc..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/pilot_k8s_settings.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: empty - hub: docker.io/istio - tag: 1.1.4 - meshConfig: - rootNamespace: istio-control - components: - pilot: - enabled: true - namespace: istio-control - k8s: - env: - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: new.path - - name: GODEBUG - value: gctrace=111 - - name: NEW_VAR - value: new_value - hpaSpec: - maxReplicas: 333 - scaleTargetRef: - name: istio-pilot - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: 444 - readinessProbe: - httpGet: - path: /ready - port: 8080 - initialDelaySeconds: 555 - resources: - requests: - memory: 999Mi - nodeSelector: - master: "true" - diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/pilot_merge_meshconfig.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/pilot_merge_meshconfig.yaml deleted file mode 100644 index 4d1f81a08..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/pilot_merge_meshconfig.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: empty - hub: docker.io/istio - tag: 1.1.4 - meshConfig: - enablePrometheusMerge: true - rootNamespace: istio-control - outboundTrafficPolicy: - mode: REGISTRY_ONLY - defaultConfig: - discoveryAddress: my-discovery:123 - drainDuration: 12s - controlPlaneAuthPolicy: NONE - accessLogFormat: | - { - "key": "val" - } - components: - pilot: - enabled: true diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/pilot_override_kubernetes.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/pilot_override_kubernetes.yaml deleted file mode 100644 index 35f8f3bdb..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/pilot_override_kubernetes.yaml +++ /dev/null @@ -1,60 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: empty - hub: docker.io/istio - tag: 1.1.4 - meshConfig: - rootNamespace: istio-control - components: - base: - enabled: true - k8s: - # Base component only allows overlays field. - overlays: - - kind: ClusterRoleBinding - name: istio-reader-dubbo-system - patches: - # Select list item by value - - path: subjects.[name:istio-reader-service-account].name - value: my-service-role - - pilot: - enabled: true - namespace: istio-control - k8s: - resources: - requests: - cpu: 123m - overlays: - - kind: Deployment - name: istiod - patches: - # Select list item by value - - path: spec.template.spec.containers.[name:discovery].args.[30m] - value: "60m" # OVERRIDDEN - # Select list item by key:value - - path: spec.template.spec.containers.[name:discovery].ports.[containerPort:8080].containerPort - value: 1234 # OVERRIDDEN - # Override with object (note | on value: first line) - - path: spec.template.spec.containers.[name:discovery].env.[name:POD_NAMESPACE].valueFrom - value: | - fieldRef: - apiVersion: v2 - fieldPath: metadata.myPath - # Deletion of list item - - path: spec.template.spec.containers.[name:discovery].env.[name:REVISION] - # Deletion of map item - - path: spec.template.spec.containers.[name:discovery].securityContext - - kind: Service - name: istiod - patches: - - path: spec.ports.[name:https-dns].port - value: 11111 # OVERRIDDEN - # Cluster scope resource - - kind: MutatingWebhookConfiguration - name: istio-sidecar-injector-istio-control - patches: - - path: webhooks.[name:namespace\.sidecar-injector\.istio\.io].clientConfig.service.name - value: foo - diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/pilot_override_values.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/pilot_override_values.yaml deleted file mode 100644 index 4c9397e59..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/pilot_override_values.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: empty - hub: docker.io/istio - tag: 1.1.4 - meshConfig: - rootNamespace: istio-control - components: - pilot: - enabled: true - namespace: istio-control - values: - pilot: - resources: - requests: - cpu: 222m - memory: 333Mi - autoscaleMax: 8 - autoscaleMin: 2 - rollingMaxUnavailable: 30% - nodeSelector: - node-name: test - unvalidatedValues: - myCustomKey: someValue diff --git a/operator/cmd/mesh/testdata/manifest-generate/input/sidecar_template.yaml b/operator/cmd/mesh/testdata/manifest-generate/input/sidecar_template.yaml deleted file mode 100644 index 3cf2eb164..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/input/sidecar_template.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - values: - sidecarInjectorWebhook: - defaultTemplates: ["sidecar", "credential-volume"] - templates: - credential-volume: | - spec: - volumes: - - name: application-credentials - secret: - secretName: secret - containers: - - name: istio-proxy - volumeMounts: - - name: application-credentials - mountPath: /etc/istio/application-credentials - readOnly: true diff --git a/operator/cmd/mesh/testdata/manifest-generate/output/all_off.golden.yaml b/operator/cmd/mesh/testdata/manifest-generate/output/all_off.golden.yaml deleted file mode 100644 index e69de29bb..000000000 diff --git a/operator/cmd/mesh/testdata/manifest-generate/output/all_on.golden-show-in-gh-pull-request.yaml b/operator/cmd/mesh/testdata/manifest-generate/output/all_on.golden-show-in-gh-pull-request.yaml deleted file mode 100644 index 92a4da7e9..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/output/all_on.golden-show-in-gh-pull-request.yaml +++ /dev/null @@ -1,10434 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - istio: security - release: istio - name: authorizationpolicies.security.istio.io -spec: - group: security.istio.io - names: - categories: - - istio-io - - security-istio-io - kind: AuthorizationPolicy - listKind: AuthorizationPolicyList - plural: authorizationpolicies - singular: authorizationpolicy - scope: Namespaced - versions: - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration for access control on workloads. See more - details at: https://istio.io/docs/reference/config/security/authorization-policy.html' - oneOf: - - not: - anyOf: - - required: - - provider - - required: - - provider - properties: - action: - description: Optional. - enum: - - ALLOW - - DENY - - AUDIT - - CUSTOM - type: string - provider: - description: Specifies detailed configuration of the CUSTOM action. - properties: - name: - description: Specifies the name of the extension provider. - type: string - type: object - rules: - description: Optional. - items: - properties: - from: - description: Optional. - items: - properties: - source: - description: Source specifies the source of a request. - properties: - ipBlocks: - description: Optional. - items: - type: string - type: array - namespaces: - description: Optional. - items: - type: string - type: array - notIpBlocks: - description: Optional. - items: - type: string - type: array - notNamespaces: - description: Optional. - items: - type: string - type: array - notPrincipals: - description: Optional. - items: - type: string - type: array - notRemoteIpBlocks: - description: Optional. - items: - type: string - type: array - notRequestPrincipals: - description: Optional. - items: - type: string - type: array - principals: - description: Optional. - items: - type: string - type: array - remoteIpBlocks: - description: Optional. - items: - type: string - type: array - requestPrincipals: - description: Optional. - items: - type: string - type: array - type: object - type: object - type: array - to: - description: Optional. - items: - properties: - operation: - description: Operation specifies the operation of a request. - properties: - hosts: - description: Optional. - items: - type: string - type: array - methods: - description: Optional. - items: - type: string - type: array - notHosts: - description: Optional. - items: - type: string - type: array - notMethods: - description: Optional. - items: - type: string - type: array - notPaths: - description: Optional. - items: - type: string - type: array - notPorts: - description: Optional. - items: - type: string - type: array - paths: - description: Optional. - items: - type: string - type: array - ports: - description: Optional. - items: - type: string - type: array - type: object - type: object - type: array - when: - description: Optional. - items: - properties: - key: - description: The name of an Istio attribute. - type: string - notValues: - description: Optional. - items: - type: string - type: array - values: - description: Optional. - items: - type: string - type: array - type: object - type: array - type: object - type: array - selector: - description: Optional. - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: destinationrules.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: DestinationRule - listKind: DestinationRuleList - plural: destinationrules - shortNames: - - dr - singular: destinationrule - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The name of a service from the service registry - jsonPath: .spec.host - name: Host - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting load balancing, outlier detection, - etc. See more details at: https://istio.io/docs/reference/config/networking/destination-rule.html' - properties: - exportTo: - description: A list of namespaces to which this destination rule is - exported. - items: - type: string - type: array - host: - description: The name of a service from the service registry. - type: string - subsets: - items: - properties: - labels: - additionalProperties: - type: string - type: object - name: - description: Name of the subset. - type: string - trafficPolicy: - description: Traffic policies that apply to this subset. - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should - be upgraded to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests - to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will - be preserved while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the - socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query - parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to - traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this - is DestinationRule-level and will override mesh - wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list - of labels used to sort endpoints to do priority - based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local - origin failures from external errors. - type: boolean - type: object - portLevelSettings: - description: Traffic policies specific to individual ports. - items: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection - should be upgraded to http2 for the associated - destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP - requests to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to - a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream - connection pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per - connection to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol - will be preserved while initiating connection - to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and - TCP upstream connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP - connections to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE - on the socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between - keep-alive probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer - algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP - header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP - query parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' - separated, e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities - to traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, - this is DestinationRule-level and will override - mesh wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered - list of labels used to sort endpoints to - do priority based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of - Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a - host is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep - analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish - local origin failures from external errors. - type: boolean - type: object - port: - properties: - number: - type: integer - type: object - tls: - description: TLS related settings for connections - to the upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server - during TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - type: object - type: array - tls: - description: TLS related settings for connections to the - upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during - TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - tunnel: - properties: - protocol: - description: Specifies which protocol to use for tunneling - the downstream connection. - type: string - targetHost: - description: Specifies a host to which the downstream - connection is tunneled. - type: string - targetPort: - description: Specifies a port to which the downstream - connection is tunneled. - type: integer - type: object - type: object - type: object - type: array - trafficPolicy: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should be upgraded - to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests to - a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will be preserved - while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the socket - to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, failover - or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to traffic - distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this is DestinationRule-level - and will override mesh wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, failover - or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list of labels - used to sort endpoints to do priority based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local origin - failures from external errors. - type: boolean - type: object - portLevelSettings: - description: Traffic policies specific to individual ports. - items: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should - be upgraded to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests - to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will - be preserved while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the - socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query - parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to - traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this - is DestinationRule-level and will override mesh - wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list - of labels used to sort endpoints to do priority - based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local - origin failures from external errors. - type: boolean - type: object - port: - properties: - number: - type: integer - type: object - tls: - description: TLS related settings for connections to the - upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during - TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - type: object - type: array - tls: - description: TLS related settings for connections to the upstream - service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during TLS - handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - tunnel: - properties: - protocol: - description: Specifies which protocol to use for tunneling - the downstream connection. - type: string - targetHost: - description: Specifies a host to which the downstream connection - is tunneled. - type: string - targetPort: - description: Specifies a port to which the downstream connection - is tunneled. - type: integer - type: object - type: object - workloadSelector: - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - description: The name of a service from the service registry - jsonPath: .spec.host - name: Host - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting load balancing, outlier detection, - etc. See more details at: https://istio.io/docs/reference/config/networking/destination-rule.html' - properties: - exportTo: - description: A list of namespaces to which this destination rule is - exported. - items: - type: string - type: array - host: - description: The name of a service from the service registry. - type: string - subsets: - items: - properties: - labels: - additionalProperties: - type: string - type: object - name: - description: Name of the subset. - type: string - trafficPolicy: - description: Traffic policies that apply to this subset. - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should - be upgraded to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests - to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will - be preserved while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the - socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query - parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to - traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this - is DestinationRule-level and will override mesh - wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list - of labels used to sort endpoints to do priority - based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local - origin failures from external errors. - type: boolean - type: object - portLevelSettings: - description: Traffic policies specific to individual ports. - items: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection - should be upgraded to http2 for the associated - destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP - requests to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to - a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream - connection pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per - connection to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol - will be preserved while initiating connection - to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and - TCP upstream connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP - connections to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE - on the socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between - keep-alive probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer - algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP - header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP - query parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' - separated, e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities - to traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, - this is DestinationRule-level and will override - mesh wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered - list of labels used to sort endpoints to - do priority based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of - Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a - host is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep - analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish - local origin failures from external errors. - type: boolean - type: object - port: - properties: - number: - type: integer - type: object - tls: - description: TLS related settings for connections - to the upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server - during TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - type: object - type: array - tls: - description: TLS related settings for connections to the - upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during - TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - tunnel: - properties: - protocol: - description: Specifies which protocol to use for tunneling - the downstream connection. - type: string - targetHost: - description: Specifies a host to which the downstream - connection is tunneled. - type: string - targetPort: - description: Specifies a port to which the downstream - connection is tunneled. - type: integer - type: object - type: object - type: object - type: array - trafficPolicy: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should be upgraded - to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests to - a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will be preserved - while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the socket - to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, failover - or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to traffic - distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this is DestinationRule-level - and will override mesh wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, failover - or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list of labels - used to sort endpoints to do priority based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local origin - failures from external errors. - type: boolean - type: object - portLevelSettings: - description: Traffic policies specific to individual ports. - items: - properties: - connectionPool: - properties: - http: - description: HTTP connection pool settings. - properties: - h2UpgradePolicy: - description: Specify if http1.1 connection should - be upgraded to http2 for the associated destination. - enum: - - DEFAULT - - DO_NOT_UPGRADE - - UPGRADE - type: string - http1MaxPendingRequests: - description: Maximum number of pending HTTP requests - to a destination. - format: int32 - type: integer - http2MaxRequests: - description: Maximum number of requests to a backend. - format: int32 - type: integer - idleTimeout: - description: The idle timeout for upstream connection - pool connections. - type: string - maxRequestsPerConnection: - description: Maximum number of requests per connection - to a backend. - format: int32 - type: integer - maxRetries: - format: int32 - type: integer - useClientProtocol: - description: If set to true, client protocol will - be preserved while initiating connection to backend. - type: boolean - type: object - tcp: - description: Settings common to both HTTP and TCP upstream - connections. - properties: - connectTimeout: - description: TCP connection timeout. - type: string - maxConnections: - description: Maximum number of HTTP1 /TCP connections - to a destination host. - format: int32 - type: integer - tcpKeepalive: - description: If set then set SO_KEEPALIVE on the - socket to enable TCP Keepalives. - properties: - interval: - description: The time duration between keep-alive - probes. - type: string - probes: - type: integer - time: - type: string - type: object - type: object - type: object - loadBalancer: - description: Settings controlling the load balancer algorithms. - oneOf: - - not: - anyOf: - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - - required: - - simple - - properties: - consistentHash: - oneOf: - - not: - anyOf: - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - - required: - - httpHeaderName - - required: - - httpCookie - - required: - - useSourceIp - - required: - - httpQueryParameterName - required: - - consistentHash - properties: - consistentHash: - properties: - httpCookie: - description: Hash based on HTTP cookie. - properties: - name: - description: Name of the cookie. - type: string - path: - description: Path to set for the cookie. - type: string - ttl: - description: Lifetime of the cookie. - type: string - type: object - httpHeaderName: - description: Hash based on a specific HTTP header. - type: string - httpQueryParameterName: - description: Hash based on a specific HTTP query - parameter. - type: string - minimumRingSize: - type: integer - useSourceIp: - description: Hash based on the source IP address. - type: boolean - type: object - localityLbSetting: - properties: - distribute: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating locality, '/' separated, - e.g. - type: string - to: - additionalProperties: - type: integer - description: Map of upstream localities to - traffic distribution weights. - type: object - type: object - type: array - enabled: - description: enable locality load balancing, this - is DestinationRule-level and will override mesh - wide settings in entirety. - nullable: true - type: boolean - failover: - description: 'Optional: only one of distribute, - failover or failoverPriority can be set.' - items: - properties: - from: - description: Originating region. - type: string - to: - type: string - type: object - type: array - failoverPriority: - description: failoverPriority is an ordered list - of labels used to sort endpoints to do priority - based load balancing. - items: - type: string - type: array - type: object - simple: - enum: - - UNSPECIFIED - - LEAST_CONN - - RANDOM - - PASSTHROUGH - - ROUND_ROBIN - - LEAST_REQUEST - type: string - warmupDurationSecs: - description: Represents the warmup duration of Service. - type: string - type: object - outlierDetection: - properties: - baseEjectionTime: - description: Minimum ejection duration. - type: string - consecutive5xxErrors: - description: Number of 5xx errors before a host is ejected - from the connection pool. - nullable: true - type: integer - consecutiveErrors: - format: int32 - type: integer - consecutiveGatewayErrors: - description: Number of gateway errors before a host - is ejected from the connection pool. - nullable: true - type: integer - consecutiveLocalOriginFailures: - nullable: true - type: integer - interval: - description: Time interval between ejection sweep analysis. - type: string - maxEjectionPercent: - format: int32 - type: integer - minHealthPercent: - format: int32 - type: integer - splitExternalLocalOriginErrors: - description: Determines whether to distinguish local - origin failures from external errors. - type: boolean - type: object - port: - properties: - number: - type: integer - type: object - tls: - description: TLS related settings for connections to the - upstream service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during - TLS handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - type: object - type: array - tls: - description: TLS related settings for connections to the upstream - service. - properties: - caCertificates: - type: string - clientCertificate: - description: REQUIRED if mode is `MUTUAL`. - type: string - credentialName: - type: string - insecureSkipVerify: - nullable: true - type: boolean - mode: - enum: - - DISABLE - - SIMPLE - - MUTUAL - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `MUTUAL`. - type: string - sni: - description: SNI string to present to the server during TLS - handshake. - type: string - subjectAltNames: - items: - type: string - type: array - type: object - tunnel: - properties: - protocol: - description: Specifies which protocol to use for tunneling - the downstream connection. - type: string - targetHost: - description: Specifies a host to which the downstream connection - is tunneled. - type: string - targetPort: - description: Specifies a port to which the downstream connection - is tunneled. - type: integer - type: object - type: object - workloadSelector: - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: envoyfilters.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: EnvoyFilter - listKind: EnvoyFilterList - plural: envoyfilters - singular: envoyfilter - scope: Namespaced - versions: - - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Customizing Envoy configuration generated by Istio. See - more details at: https://istio.io/docs/reference/config/networking/envoy-filter.html' - properties: - configPatches: - description: One or more patches with match conditions. - items: - properties: - applyTo: - enum: - - INVALID - - LISTENER - - FILTER_CHAIN - - NETWORK_FILTER - - HTTP_FILTER - - ROUTE_CONFIGURATION - - VIRTUAL_HOST - - HTTP_ROUTE - - CLUSTER - - EXTENSION_CONFIG - - BOOTSTRAP - type: string - match: - description: Match on listener/route configuration/cluster. - oneOf: - - not: - anyOf: - - required: - - listener - - required: - - routeConfiguration - - required: - - cluster - - required: - - listener - - required: - - routeConfiguration - - required: - - cluster - properties: - cluster: - description: Match on envoy cluster attributes. - properties: - name: - description: The exact name of the cluster to match. - type: string - portNumber: - description: The service port for which this cluster - was generated. - type: integer - service: - description: The fully qualified service name for this - cluster. - type: string - subset: - description: The subset associated with the service. - type: string - type: object - context: - description: The specific config generation context to match - on. - enum: - - ANY - - SIDECAR_INBOUND - - SIDECAR_OUTBOUND - - GATEWAY - type: string - listener: - description: Match on envoy listener attributes. - properties: - filterChain: - description: Match a specific filter chain in a listener. - properties: - applicationProtocols: - description: Applies only to sidecars. - type: string - destinationPort: - description: The destination_port value used by - a filter chain's match condition. - type: integer - filter: - description: The name of a specific filter to apply - the patch to. - properties: - name: - description: The filter name to match on. - type: string - subFilter: - properties: - name: - description: The filter name to match on. - type: string - type: object - type: object - name: - description: The name assigned to the filter chain. - type: string - sni: - description: The SNI value used by a filter chain's - match condition. - type: string - transportProtocol: - description: Applies only to `SIDECAR_INBOUND` context. - type: string - type: object - name: - description: Match a specific listener by its name. - type: string - portName: - type: string - portNumber: - type: integer - type: object - proxy: - description: Match on properties associated with a proxy. - properties: - metadata: - additionalProperties: - type: string - type: object - proxyVersion: - type: string - type: object - routeConfiguration: - description: Match on envoy HTTP route configuration attributes. - properties: - gateway: - type: string - name: - description: Route configuration name to match on. - type: string - portName: - description: Applicable only for GATEWAY context. - type: string - portNumber: - type: integer - vhost: - properties: - name: - type: string - route: - description: Match a specific route within the virtual - host. - properties: - action: - description: Match a route with specific action - type. - enum: - - ANY - - ROUTE - - REDIRECT - - DIRECT_RESPONSE - type: string - name: - type: string - type: object - type: object - type: object - type: object - patch: - description: The patch to apply along with the operation. - properties: - filterClass: - description: Determines the filter insertion order. - enum: - - UNSPECIFIED - - AUTHN - - AUTHZ - - STATS - type: string - operation: - description: Determines how the patch should be applied. - enum: - - INVALID - - MERGE - - ADD - - REMOVE - - INSERT_BEFORE - - INSERT_AFTER - - INSERT_FIRST - - REPLACE - type: string - value: - description: The JSON config of the object being patched. - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - type: object - type: array - priority: - description: Priority defines the order in which patch sets are applied - within a context. - format: int32 - type: integer - workloadSelector: - properties: - labels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: gateways.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: Gateway - listKind: GatewayList - plural: gateways - shortNames: - - gw - singular: gateway - scope: Namespaced - versions: - - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting edge load balancer. See more details - at: https://istio.io/docs/reference/config/networking/gateway.html' - properties: - selector: - additionalProperties: - type: string - type: object - servers: - description: A list of server specifications. - items: - properties: - bind: - type: string - defaultEndpoint: - type: string - hosts: - description: One or more hosts exposed by this gateway. - items: - type: string - type: array - name: - description: An optional name of the server, when set must be - unique across all servers. - type: string - port: - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - tls: - description: Set of TLS related options that govern the server's - behavior. - properties: - caCertificates: - description: REQUIRED if mode is `MUTUAL`. - type: string - cipherSuites: - description: 'Optional: If specified, only support the specified - cipher list.' - items: - type: string - type: array - credentialName: - type: string - httpsRedirect: - type: boolean - maxProtocolVersion: - description: 'Optional: Maximum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - minProtocolVersion: - description: 'Optional: Minimum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - mode: - enum: - - PASSTHROUGH - - SIMPLE - - MUTUAL - - AUTO_PASSTHROUGH - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - serverCertificate: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - subjectAltNames: - items: - type: string - type: array - verifyCertificateHash: - items: - type: string - type: array - verifyCertificateSpki: - items: - type: string - type: array - type: object - type: object - type: array - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting edge load balancer. See more details - at: https://istio.io/docs/reference/config/networking/gateway.html' - properties: - selector: - additionalProperties: - type: string - type: object - servers: - description: A list of server specifications. - items: - properties: - bind: - type: string - defaultEndpoint: - type: string - hosts: - description: One or more hosts exposed by this gateway. - items: - type: string - type: array - name: - description: An optional name of the server, when set must be - unique across all servers. - type: string - port: - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - tls: - description: Set of TLS related options that govern the server's - behavior. - properties: - caCertificates: - description: REQUIRED if mode is `MUTUAL`. - type: string - cipherSuites: - description: 'Optional: If specified, only support the specified - cipher list.' - items: - type: string - type: array - credentialName: - type: string - httpsRedirect: - type: boolean - maxProtocolVersion: - description: 'Optional: Maximum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - minProtocolVersion: - description: 'Optional: Minimum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - mode: - enum: - - PASSTHROUGH - - SIMPLE - - MUTUAL - - AUTO_PASSTHROUGH - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - serverCertificate: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - subjectAltNames: - items: - type: string - type: array - verifyCertificateHash: - items: - type: string - type: array - verifyCertificateSpki: - items: - type: string - type: array - type: object - type: object - type: array - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: istiooperators.install.istio.io - labels: - release: istio -spec: - conversion: - strategy: None - group: install.istio.io - names: - kind: IstioOperator - listKind: IstioOperatorList - plural: istiooperators - singular: istiooperator - shortNames: - - iop - - io - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Istio control plane revision - jsonPath: .spec.revision - name: Revision - type: string - - description: IOP current state - jsonPath: .status.status - name: Status - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - subresources: - status: {} - name: v1alpha1 - schema: - openAPIV3Schema: - type: object - x-kubernetes-preserve-unknown-fields: true - served: true - storage: true ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - istio: security - release: istio - name: peerauthentications.security.istio.io -spec: - group: security.istio.io - names: - categories: - - istio-io - - security-istio-io - kind: PeerAuthentication - listKind: PeerAuthenticationList - plural: peerauthentications - shortNames: - - pa - singular: peerauthentication - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Defines the mTLS mode used for peer authentication. - jsonPath: .spec.mtls.mode - name: Mode - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: PeerAuthentication defines how traffic will be tunneled (or - not) to the sidecar. - properties: - mtls: - description: Mutual TLS settings for workload. - properties: - mode: - description: Defines the mTLS mode used for peer authentication. - enum: - - UNSET - - DISABLE - - PERMISSIVE - - STRICT - type: string - type: object - portLevelMtls: - additionalProperties: - properties: - mode: - description: Defines the mTLS mode used for peer authentication. - enum: - - UNSET - - DISABLE - - PERMISSIVE - - STRICT - type: string - type: object - description: Port specific mutual TLS settings. - type: object - selector: - description: The selector determines the workloads to apply the ChannelAuthentication - on. - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: proxyconfigs.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: ProxyConfig - listKind: ProxyConfigList - plural: proxyconfigs - singular: proxyconfig - scope: Namespaced - versions: - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Provides configuration for individual workloads. See more - details at: https://istio.io/docs/reference/config/networking/proxy-config.html' - properties: - concurrency: - description: The number of worker threads to run. - nullable: true - type: integer - environmentVariables: - additionalProperties: - type: string - description: Additional environment variables for the proxy. - type: object - image: - description: Specifies the details of the proxy image. - properties: - imageType: - description: The image type of the image. - type: string - type: object - selector: - description: Optional. - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - istio: security - release: istio - name: requestauthentications.security.istio.io -spec: - group: security.istio.io - names: - categories: - - istio-io - - security-istio-io - kind: RequestAuthentication - listKind: RequestAuthenticationList - plural: requestauthentications - shortNames: - - ra - singular: requestauthentication - scope: Namespaced - versions: - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: RequestAuthentication defines what request authentication - methods are supported by a workload. - properties: - jwtRules: - description: Define the list of JWTs that can be validated at the - selected workloads' proxy. - items: - properties: - audiences: - items: - type: string - type: array - forwardOriginalToken: - description: If set to true, the original token will be kept - for the upstream request. - type: boolean - fromHeaders: - description: List of header locations from which JWT is expected. - items: - properties: - name: - description: The HTTP header name. - type: string - prefix: - description: The prefix that should be stripped before - decoding the token. - type: string - type: object - type: array - fromParams: - description: List of query parameters from which JWT is expected. - items: - type: string - type: array - issuer: - description: Identifies the issuer that issued the JWT. - type: string - jwks: - description: JSON Web Key Set of public keys to validate signature - of the JWT. - type: string - jwks_uri: - type: string - jwksUri: - type: string - outputPayloadToHeader: - type: string - type: object - type: array - selector: - description: Optional. - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: serviceentries.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: ServiceEntry - listKind: ServiceEntryList - plural: serviceentries - shortNames: - - se - singular: serviceentry - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The hosts associated with the ServiceEntry - jsonPath: .spec.hosts - name: Hosts - type: string - - description: Whether the service is external to the mesh or part of the mesh - (MESH_EXTERNAL or MESH_INTERNAL) - jsonPath: .spec.location - name: Location - type: string - - description: Service discovery mode for the hosts (NONE, STATIC, or DNS) - jsonPath: .spec.resolution - name: Resolution - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting service registry. See more details - at: https://istio.io/docs/reference/config/networking/service-entry.html' - properties: - addresses: - description: The virtual IP addresses associated with the service. - items: - type: string - type: array - endpoints: - description: One or more endpoints associated with the service. - items: - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - type: array - exportTo: - description: A list of namespaces to which this service is exported. - items: - type: string - type: array - hosts: - description: The hosts associated with the ServiceEntry. - items: - type: string - type: array - location: - enum: - - MESH_EXTERNAL - - MESH_INTERNAL - type: string - ports: - description: The ports associated with the external service. - items: - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - type: array - resolution: - description: Service discovery mode for the hosts. - enum: - - NONE - - STATIC - - DNS - - DNS_ROUND_ROBIN - type: string - subjectAltNames: - items: - type: string - type: array - workloadSelector: - description: Applicable only for MESH_INTERNAL services. - properties: - labels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - description: The hosts associated with the ServiceEntry - jsonPath: .spec.hosts - name: Hosts - type: string - - description: Whether the service is external to the mesh or part of the mesh - (MESH_EXTERNAL or MESH_INTERNAL) - jsonPath: .spec.location - name: Location - type: string - - description: Service discovery mode for the hosts (NONE, STATIC, or DNS) - jsonPath: .spec.resolution - name: Resolution - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting service registry. See more details - at: https://istio.io/docs/reference/config/networking/service-entry.html' - properties: - addresses: - description: The virtual IP addresses associated with the service. - items: - type: string - type: array - endpoints: - description: One or more endpoints associated with the service. - items: - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - type: array - exportTo: - description: A list of namespaces to which this service is exported. - items: - type: string - type: array - hosts: - description: The hosts associated with the ServiceEntry. - items: - type: string - type: array - location: - enum: - - MESH_EXTERNAL - - MESH_INTERNAL - type: string - ports: - description: The ports associated with the external service. - items: - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - type: array - resolution: - description: Service discovery mode for the hosts. - enum: - - NONE - - STATIC - - DNS - - DNS_ROUND_ROBIN - type: string - subjectAltNames: - items: - type: string - type: array - workloadSelector: - description: Applicable only for MESH_INTERNAL services. - properties: - labels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: sidecars.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: Sidecar - listKind: SidecarList - plural: sidecars - singular: sidecar - scope: Namespaced - versions: - - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting network reachability of a sidecar. - See more details at: https://istio.io/docs/reference/config/networking/sidecar.html' - properties: - egress: - items: - properties: - bind: - type: string - captureMode: - enum: - - DEFAULT - - IPTABLES - - NONE - type: string - hosts: - items: - type: string - type: array - port: - description: The port associated with the listener. - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - type: object - type: array - ingress: - items: - properties: - bind: - description: The IP to which the listener should be bound. - type: string - captureMode: - enum: - - DEFAULT - - IPTABLES - - NONE - type: string - defaultEndpoint: - type: string - port: - description: The port associated with the listener. - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - tls: - properties: - caCertificates: - description: REQUIRED if mode is `MUTUAL`. - type: string - cipherSuites: - description: 'Optional: If specified, only support the specified - cipher list.' - items: - type: string - type: array - credentialName: - type: string - httpsRedirect: - type: boolean - maxProtocolVersion: - description: 'Optional: Maximum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - minProtocolVersion: - description: 'Optional: Minimum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - mode: - enum: - - PASSTHROUGH - - SIMPLE - - MUTUAL - - AUTO_PASSTHROUGH - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - serverCertificate: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - subjectAltNames: - items: - type: string - type: array - verifyCertificateHash: - items: - type: string - type: array - verifyCertificateSpki: - items: - type: string - type: array - type: object - type: object - type: array - outboundTrafficPolicy: - description: Configuration for the outbound traffic policy. - properties: - egressProxy: - properties: - host: - description: The name of a service from the service registry. - type: string - port: - description: Specifies the port on the host that is being - addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - mode: - enum: - - REGISTRY_ONLY - - ALLOW_ANY - type: string - type: object - workloadSelector: - properties: - labels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting network reachability of a sidecar. - See more details at: https://istio.io/docs/reference/config/networking/sidecar.html' - properties: - egress: - items: - properties: - bind: - type: string - captureMode: - enum: - - DEFAULT - - IPTABLES - - NONE - type: string - hosts: - items: - type: string - type: array - port: - description: The port associated with the listener. - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - type: object - type: array - ingress: - items: - properties: - bind: - description: The IP to which the listener should be bound. - type: string - captureMode: - enum: - - DEFAULT - - IPTABLES - - NONE - type: string - defaultEndpoint: - type: string - port: - description: The port associated with the listener. - properties: - name: - description: Label assigned to the port. - type: string - number: - description: A valid non-negative integer port number. - type: integer - protocol: - description: The protocol exposed on the port. - type: string - targetPort: - type: integer - type: object - tls: - properties: - caCertificates: - description: REQUIRED if mode is `MUTUAL`. - type: string - cipherSuites: - description: 'Optional: If specified, only support the specified - cipher list.' - items: - type: string - type: array - credentialName: - type: string - httpsRedirect: - type: boolean - maxProtocolVersion: - description: 'Optional: Maximum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - minProtocolVersion: - description: 'Optional: Minimum TLS protocol version.' - enum: - - TLS_AUTO - - TLSV1_0 - - TLSV1_1 - - TLSV1_2 - - TLSV1_3 - type: string - mode: - enum: - - PASSTHROUGH - - SIMPLE - - MUTUAL - - AUTO_PASSTHROUGH - - ISTIO_MUTUAL - type: string - privateKey: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - serverCertificate: - description: REQUIRED if mode is `SIMPLE` or `MUTUAL`. - type: string - subjectAltNames: - items: - type: string - type: array - verifyCertificateHash: - items: - type: string - type: array - verifyCertificateSpki: - items: - type: string - type: array - type: object - type: object - type: array - outboundTrafficPolicy: - description: Configuration for the outbound traffic policy. - properties: - egressProxy: - properties: - host: - description: The name of a service from the service registry. - type: string - port: - description: Specifies the port on the host that is being - addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - mode: - enum: - - REGISTRY_ONLY - - ALLOW_ANY - type: string - type: object - workloadSelector: - properties: - labels: - additionalProperties: - type: string - type: object - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - istio: telemetry - release: istio - name: telemetries.telemetry.istio.io -spec: - group: telemetry.istio.io - names: - categories: - - istio-io - - telemetry-istio-io - kind: Telemetry - listKind: TelemetryList - plural: telemetries - shortNames: - - telemetry - singular: telemetry - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Telemetry configuration for workloads. See more details - at: https://istio.io/docs/reference/config/telemetry.html' - properties: - accessLogging: - description: Optional. - items: - properties: - disabled: - description: Controls logging. - nullable: true - type: boolean - filter: - description: Optional. - properties: - expression: - description: CEL expression for selecting when requests/connections - should be logged. - type: string - type: object - match: - description: Allows tailoring of logging behavior to specific - conditions. - properties: - mode: - enum: - - CLIENT_AND_SERVER - - CLIENT - - SERVER - type: string - type: object - providers: - description: Optional. - items: - properties: - name: - description: Required. - type: string - type: object - type: array - type: object - type: array - metrics: - description: Optional. - items: - properties: - overrides: - description: Optional. - items: - properties: - disabled: - description: Optional. - nullable: true - type: boolean - match: - description: Match allows provides the scope of the override. - oneOf: - - not: - anyOf: - - required: - - metric - - required: - - customMetric - - required: - - metric - - required: - - customMetric - properties: - customMetric: - description: Allows free-form specification of a metric. - type: string - metric: - description: One of the well-known Istio Standard - Metrics. - enum: - - ALL_METRICS - - REQUEST_COUNT - - REQUEST_DURATION - - REQUEST_SIZE - - RESPONSE_SIZE - - TCP_OPENED_CONNECTIONS - - TCP_CLOSED_CONNECTIONS - - TCP_SENT_BYTES - - TCP_RECEIVED_BYTES - - GRPC_REQUEST_MESSAGES - - GRPC_RESPONSE_MESSAGES - type: string - mode: - enum: - - CLIENT_AND_SERVER - - CLIENT - - SERVER - type: string - type: object - tagOverrides: - additionalProperties: - properties: - operation: - description: Operation controls whether or not to - update/add a tag, or to remove it. - enum: - - UPSERT - - REMOVE - type: string - value: - description: Value is only considered if the operation - is `UPSERT`. - type: string - type: object - description: Optional. - type: object - type: object - type: array - providers: - description: Optional. - items: - properties: - name: - description: Required. - type: string - type: object - type: array - type: object - type: array - selector: - description: Optional. - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - tracing: - description: Optional. - items: - properties: - customTags: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - literal - - required: - - environment - - required: - - header - - required: - - literal - - required: - - environment - - required: - - header - properties: - environment: - description: Environment adds the value of an environment - variable to each span. - properties: - defaultValue: - description: Optional. - type: string - name: - description: Name of the environment variable from - which to extract the tag value. - type: string - type: object - header: - properties: - defaultValue: - description: Optional. - type: string - name: - description: Name of the header from which to extract - the tag value. - type: string - type: object - literal: - description: Literal adds the same, hard-coded value to - each span. - properties: - value: - description: The tag value to use. - type: string - type: object - type: object - description: Optional. - type: object - disableSpanReporting: - description: Controls span reporting. - nullable: true - type: boolean - match: - description: Allows tailoring of behavior to specific conditions. - properties: - mode: - enum: - - CLIENT_AND_SERVER - - CLIENT - - SERVER - type: string - type: object - providers: - description: Optional. - items: - properties: - name: - description: Required. - type: string - type: object - type: array - randomSamplingPercentage: - nullable: true - type: number - useRequestIdForTraceSampling: - nullable: true - type: boolean - type: object - type: array - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: virtualservices.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: VirtualService - listKind: VirtualServiceList - plural: virtualservices - shortNames: - - vs - singular: virtualservice - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: The names of gateways and sidecars that should apply these routes - jsonPath: .spec.gateways - name: Gateways - type: string - - description: The destination hosts to which traffic is being sent - jsonPath: .spec.hosts - name: Hosts - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting label/content routing, sni routing, - etc. See more details at: https://istio.io/docs/reference/config/networking/virtual-service.html' - properties: - exportTo: - description: A list of namespaces to which this virtual service is - exported. - items: - type: string - type: array - gateways: - description: The names of gateways and sidecars that should apply - these routes. - items: - type: string - type: array - hosts: - description: The destination hosts to which traffic is being sent. - items: - type: string - type: array - http: - description: An ordered list of route rules for HTTP traffic. - items: - properties: - corsPolicy: - description: Cross-Origin Resource Sharing policy (CORS). - properties: - allowCredentials: - nullable: true - type: boolean - allowHeaders: - items: - type: string - type: array - allowMethods: - description: List of HTTP methods allowed to access the - resource. - items: - type: string - type: array - allowOrigin: - description: The list of origins that are allowed to perform - CORS requests. - items: - type: string - type: array - allowOrigins: - description: String patterns that match allowed origins. - items: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - type: array - exposeHeaders: - items: - type: string - type: array - maxAge: - type: string - type: object - delegate: - properties: - name: - description: Name specifies the name of the delegate VirtualService. - type: string - namespace: - description: Namespace specifies the namespace where the - delegate VirtualService resides. - type: string - type: object - fault: - description: Fault injection policy to apply on HTTP traffic - at the client side. - properties: - abort: - oneOf: - - not: - anyOf: - - required: - - httpStatus - - required: - - grpcStatus - - required: - - http2Error - - required: - - httpStatus - - required: - - grpcStatus - - required: - - http2Error - properties: - grpcStatus: - type: string - http2Error: - type: string - httpStatus: - description: HTTP status code to use to abort the Http - request. - format: int32 - type: integer - percentage: - description: Percentage of requests to be aborted with - the error code provided. - properties: - value: - format: double - type: number - type: object - type: object - delay: - oneOf: - - not: - anyOf: - - required: - - fixedDelay - - required: - - exponentialDelay - - required: - - fixedDelay - - required: - - exponentialDelay - properties: - exponentialDelay: - type: string - fixedDelay: - description: Add a fixed delay before forwarding the - request. - type: string - percent: - description: Percentage of requests on which the delay - will be injected (0-100). - format: int32 - type: integer - percentage: - description: Percentage of requests on which the delay - will be injected. - properties: - value: - format: double - type: number - type: object - type: object - type: object - headers: - properties: - request: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - response: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - type: object - match: - items: - properties: - authority: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - headers: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - type: object - ignoreUriCase: - description: Flag to specify whether the URI matching - should be case-insensitive. - type: boolean - method: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - name: - description: The name assigned to a match. - type: string - port: - description: Specifies the ports on the host that is being - addressed. - type: integer - queryParams: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - description: Query parameters for matching. - type: object - scheme: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - uri: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - withoutHeaders: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - description: withoutHeader has the same syntax with the - header, but has opposite meaning. - type: object - type: object - type: array - mirror: - properties: - host: - description: The name of a service from the service registry. - type: string - port: - description: Specifies the port on the host that is being - addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - mirror_percent: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - nullable: true - type: integer - mirrorPercent: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - nullable: true - type: integer - mirrorPercentage: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - properties: - value: - format: double - type: number - type: object - name: - description: The name assigned to the route for debugging purposes. - type: string - redirect: - description: A HTTP rule can either redirect or forward (default) - traffic. - oneOf: - - not: - anyOf: - - required: - - port - - required: - - derivePort - - required: - - port - - required: - - derivePort - properties: - authority: - type: string - derivePort: - enum: - - FROM_PROTOCOL_DEFAULT - - FROM_REQUEST_PORT - type: string - port: - description: On a redirect, overwrite the port portion of - the URL with this value. - type: integer - redirectCode: - type: integer - scheme: - description: On a redirect, overwrite the scheme portion - of the URL with this value. - type: string - uri: - type: string - type: object - retries: - description: Retry policy for HTTP requests. - properties: - attempts: - description: Number of retries to be allowed for a given - request. - format: int32 - type: integer - perTryTimeout: - description: Timeout per attempt for a given request, including - the initial call and any retries. - type: string - retryOn: - description: Specifies the conditions under which retry - takes place. - type: string - retryRemoteLocalities: - description: Flag to specify whether the retries should - retry to other localities. - nullable: true - type: boolean - type: object - rewrite: - description: Rewrite HTTP URIs and Authority headers. - properties: - authority: - description: rewrite the Authority/Host header with this - value. - type: string - uri: - type: string - type: object - route: - description: A HTTP rule can either redirect or forward (default) - traffic. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - headers: - properties: - request: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - response: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - timeout: - description: Timeout for HTTP requests, default is disabled. - type: string - type: object - type: array - tcp: - description: An ordered list of route rules for opaque TCP traffic. - items: - properties: - match: - items: - properties: - destinationSubnets: - description: IPv4 or IPv6 ip addresses of destination - with optional subnet. - items: - type: string - type: array - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - port: - description: Specifies the port on the host that is being - addressed. - type: integer - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - sourceSubnet: - description: IPv4 or IPv6 ip address of source with optional - subnet. - type: string - type: object - type: array - route: - description: The destination to which the connection should - be forwarded to. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - type: object - type: array - tls: - items: - properties: - match: - items: - properties: - destinationSubnets: - description: IPv4 or IPv6 ip addresses of destination - with optional subnet. - items: - type: string - type: array - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - port: - description: Specifies the port on the host that is being - addressed. - type: integer - sniHosts: - description: SNI (server name indicator) to match on. - items: - type: string - type: array - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - type: object - type: array - route: - description: The destination to which the connection should - be forwarded to. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - type: object - type: array - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - description: The names of gateways and sidecars that should apply these routes - jsonPath: .spec.gateways - name: Gateways - type: string - - description: The destination hosts to which traffic is being sent - jsonPath: .spec.hosts - name: Hosts - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting label/content routing, sni routing, - etc. See more details at: https://istio.io/docs/reference/config/networking/virtual-service.html' - properties: - exportTo: - description: A list of namespaces to which this virtual service is - exported. - items: - type: string - type: array - gateways: - description: The names of gateways and sidecars that should apply - these routes. - items: - type: string - type: array - hosts: - description: The destination hosts to which traffic is being sent. - items: - type: string - type: array - http: - description: An ordered list of route rules for HTTP traffic. - items: - properties: - corsPolicy: - description: Cross-Origin Resource Sharing policy (CORS). - properties: - allowCredentials: - nullable: true - type: boolean - allowHeaders: - items: - type: string - type: array - allowMethods: - description: List of HTTP methods allowed to access the - resource. - items: - type: string - type: array - allowOrigin: - description: The list of origins that are allowed to perform - CORS requests. - items: - type: string - type: array - allowOrigins: - description: String patterns that match allowed origins. - items: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - type: array - exposeHeaders: - items: - type: string - type: array - maxAge: - type: string - type: object - delegate: - properties: - name: - description: Name specifies the name of the delegate VirtualService. - type: string - namespace: - description: Namespace specifies the namespace where the - delegate VirtualService resides. - type: string - type: object - fault: - description: Fault injection policy to apply on HTTP traffic - at the client side. - properties: - abort: - oneOf: - - not: - anyOf: - - required: - - httpStatus - - required: - - grpcStatus - - required: - - http2Error - - required: - - httpStatus - - required: - - grpcStatus - - required: - - http2Error - properties: - grpcStatus: - type: string - http2Error: - type: string - httpStatus: - description: HTTP status code to use to abort the Http - request. - format: int32 - type: integer - percentage: - description: Percentage of requests to be aborted with - the error code provided. - properties: - value: - format: double - type: number - type: object - type: object - delay: - oneOf: - - not: - anyOf: - - required: - - fixedDelay - - required: - - exponentialDelay - - required: - - fixedDelay - - required: - - exponentialDelay - properties: - exponentialDelay: - type: string - fixedDelay: - description: Add a fixed delay before forwarding the - request. - type: string - percent: - description: Percentage of requests on which the delay - will be injected (0-100). - format: int32 - type: integer - percentage: - description: Percentage of requests on which the delay - will be injected. - properties: - value: - format: double - type: number - type: object - type: object - type: object - headers: - properties: - request: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - response: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - type: object - match: - items: - properties: - authority: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - headers: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - type: object - ignoreUriCase: - description: Flag to specify whether the URI matching - should be case-insensitive. - type: boolean - method: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - name: - description: The name assigned to a match. - type: string - port: - description: Specifies the ports on the host that is being - addressed. - type: integer - queryParams: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - description: Query parameters for matching. - type: object - scheme: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - uri: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - withoutHeaders: - additionalProperties: - oneOf: - - not: - anyOf: - - required: - - exact - - required: - - prefix - - required: - - regex - - required: - - exact - - required: - - prefix - - required: - - regex - properties: - exact: - type: string - prefix: - type: string - regex: - description: RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). - type: string - type: object - description: withoutHeader has the same syntax with the - header, but has opposite meaning. - type: object - type: object - type: array - mirror: - properties: - host: - description: The name of a service from the service registry. - type: string - port: - description: Specifies the port on the host that is being - addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - mirror_percent: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - nullable: true - type: integer - mirrorPercent: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - nullable: true - type: integer - mirrorPercentage: - description: Percentage of the traffic to be mirrored by the - `mirror` field. - properties: - value: - format: double - type: number - type: object - name: - description: The name assigned to the route for debugging purposes. - type: string - redirect: - description: A HTTP rule can either redirect or forward (default) - traffic. - oneOf: - - not: - anyOf: - - required: - - port - - required: - - derivePort - - required: - - port - - required: - - derivePort - properties: - authority: - type: string - derivePort: - enum: - - FROM_PROTOCOL_DEFAULT - - FROM_REQUEST_PORT - type: string - port: - description: On a redirect, overwrite the port portion of - the URL with this value. - type: integer - redirectCode: - type: integer - scheme: - description: On a redirect, overwrite the scheme portion - of the URL with this value. - type: string - uri: - type: string - type: object - retries: - description: Retry policy for HTTP requests. - properties: - attempts: - description: Number of retries to be allowed for a given - request. - format: int32 - type: integer - perTryTimeout: - description: Timeout per attempt for a given request, including - the initial call and any retries. - type: string - retryOn: - description: Specifies the conditions under which retry - takes place. - type: string - retryRemoteLocalities: - description: Flag to specify whether the retries should - retry to other localities. - nullable: true - type: boolean - type: object - rewrite: - description: Rewrite HTTP URIs and Authority headers. - properties: - authority: - description: rewrite the Authority/Host header with this - value. - type: string - uri: - type: string - type: object - route: - description: A HTTP rule can either redirect or forward (default) - traffic. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - headers: - properties: - request: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - response: - properties: - add: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - type: object - type: object - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - timeout: - description: Timeout for HTTP requests, default is disabled. - type: string - type: object - type: array - tcp: - description: An ordered list of route rules for opaque TCP traffic. - items: - properties: - match: - items: - properties: - destinationSubnets: - description: IPv4 or IPv6 ip addresses of destination - with optional subnet. - items: - type: string - type: array - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - port: - description: Specifies the port on the host that is being - addressed. - type: integer - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - sourceSubnet: - description: IPv4 or IPv6 ip address of source with optional - subnet. - type: string - type: object - type: array - route: - description: The destination to which the connection should - be forwarded to. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - type: object - type: array - tls: - items: - properties: - match: - items: - properties: - destinationSubnets: - description: IPv4 or IPv6 ip addresses of destination - with optional subnet. - items: - type: string - type: array - gateways: - description: Names of gateways where the rule should be - applied. - items: - type: string - type: array - port: - description: Specifies the port on the host that is being - addressed. - type: integer - sniHosts: - description: SNI (server name indicator) to match on. - items: - type: string - type: array - sourceLabels: - additionalProperties: - type: string - type: object - sourceNamespace: - description: Source namespace constraining the applicability - of a rule to workloads in that namespace. - type: string - type: object - type: array - route: - description: The destination to which the connection should - be forwarded to. - items: - properties: - destination: - properties: - host: - description: The name of a service from the service - registry. - type: string - port: - description: Specifies the port on the host that is - being addressed. - properties: - number: - type: integer - type: object - subset: - description: The name of a subset within the service. - type: string - type: object - weight: - description: Weight specifies the relative proportion - of traffic to be forwarded to the destination. - format: int32 - type: integer - type: object - type: array - type: object - type: array - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: wasmplugins.extensions.istio.io -spec: - group: extensions.istio.io - names: - categories: - - istio-io - - extensions-istio-io - kind: WasmPlugin - listKind: WasmPluginList - plural: wasmplugins - singular: wasmplugin - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Extend the functionality provided by the Istio proxy through - WebAssembly filters. See more details at: https://istio.io/docs/reference/config/proxy_extensions/wasm-plugin.html' - properties: - imagePullPolicy: - description: The pull behaviour to be applied when fetching an OCI - image. - enum: - - UNSPECIFIED_POLICY - - IfNotPresent - - Always - type: string - imagePullSecret: - description: Credentials to use for OCI image pulling. - type: string - phase: - description: Determines where in the filter chain this `WasmPlugin` - is to be injected. - enum: - - UNSPECIFIED_PHASE - - AUTHN - - AUTHZ - - STATS - type: string - pluginConfig: - description: The configuration that will be passed on to the plugin. - type: object - x-kubernetes-preserve-unknown-fields: true - pluginName: - type: string - priority: - description: Determines ordering of `WasmPlugins` in the same `phase`. - nullable: true - type: integer - selector: - properties: - matchLabels: - additionalProperties: - type: string - type: object - type: object - sha256: - description: SHA256 checksum that will be used to verify Wasm module - or OCI container. - type: string - url: - description: URL of a Wasm module or OCI container. - type: string - verificationKey: - type: string - vmConfig: - description: Configuration for a Wasm VM. - properties: - env: - description: Specifies environment variables to be injected to - this VM. - items: - properties: - name: - type: string - value: - description: Value for the environment variable. - type: string - valueFrom: - enum: - - INLINE - - HOST - type: string - type: object - type: array - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: workloadentries.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: WorkloadEntry - listKind: WorkloadEntryList - plural: workloadentries - shortNames: - - we - singular: workloadentry - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - description: Address associated with the network endpoint. - jsonPath: .spec.address - name: Address - type: string - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting VMs onboarded into the mesh. See - more details at: https://istio.io/docs/reference/config/networking/workload-entry.html' - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - description: Address associated with the network endpoint. - jsonPath: .spec.address - name: Address - type: string - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Configuration affecting VMs onboarded into the mesh. See - more details at: https://istio.io/docs/reference/config/networking/workload-entry.html' - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - labels: - app: istio-pilot - chart: istio - heritage: Tiller - release: istio - name: workloadgroups.networking.istio.io -spec: - group: networking.istio.io - names: - categories: - - istio-io - - networking-istio-io - kind: WorkloadGroup - listKind: WorkloadGroupList - plural: workloadgroups - shortNames: - - wg - singular: workloadgroup - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha3 - schema: - openAPIV3Schema: - properties: - spec: - description: 'Describes a collection of workload instances. See more details - at: https://istio.io/docs/reference/config/networking/workload-group.html' - properties: - metadata: - description: Metadata that will be used for all corresponding `WorkloadEntries`. - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - probe: - description: '`ReadinessProbe` describes the configuration the user - must provide for healthchecking on their workload.' - oneOf: - - not: - anyOf: - - required: - - httpGet - - required: - - tcpSocket - - required: - - exec - - required: - - httpGet - - required: - - tcpSocket - - required: - - exec - properties: - exec: - description: Health is determined by how the command that is executed - exited. - properties: - command: - description: Command to run. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for the probe to be - considered failed after having succeeded. - format: int32 - type: integer - httpGet: - properties: - host: - description: Host name to connect to, defaults to the pod - IP. - type: string - httpHeaders: - description: Headers the proxy will pass on to make the request. - items: - properties: - name: - type: string - value: - type: string - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - description: Port on which the endpoint lives. - type: integer - scheme: - type: string - type: object - initialDelaySeconds: - description: Number of seconds after the container has started - before readiness probes are initiated. - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform the probe. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for the probe to be - considered successful after having failed. - format: int32 - type: integer - tcpSocket: - description: Health is determined by if the proxy is able to connect. - properties: - host: - type: string - port: - type: integer - type: object - timeoutSeconds: - description: Number of seconds after which the probe times out. - format: int32 - type: integer - type: object - template: - description: Template to be used for the generation of `WorkloadEntry` - resources that belong to this `WorkloadGroup`. - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - properties: - spec: - properties: - metadata: - description: Metadata that will be used for all corresponding `WorkloadEntries`. - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - probe: - description: '`ReadinessProbe` describes the configuration the user - must provide for healthchecking on their workload.' - oneOf: - - not: - anyOf: - - required: - - httpGet - - required: - - tcpSocket - - required: - - exec - - required: - - httpGet - - required: - - tcpSocket - - required: - - exec - properties: - exec: - description: Health is determined by how the command that is executed - exited. - properties: - command: - description: Command to run. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for the probe to be - considered failed after having succeeded. - format: int32 - type: integer - httpGet: - properties: - host: - description: Host name to connect to, defaults to the pod - IP. - type: string - httpHeaders: - description: Headers the proxy will pass on to make the request. - items: - properties: - name: - type: string - value: - type: string - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - description: Port on which the endpoint lives. - type: integer - scheme: - type: string - type: object - initialDelaySeconds: - description: Number of seconds after the container has started - before readiness probes are initiated. - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform the probe. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for the probe to be - considered successful after having failed. - format: int32 - type: integer - tcpSocket: - description: Health is determined by if the proxy is able to connect. - properties: - host: - type: string - port: - type: integer - type: object - timeoutSeconds: - description: Number of seconds after which the probe times out. - format: int32 - type: integer - type: object - template: - description: Template to be used for the generation of `WorkloadEntry` - resources that belong to this `WorkloadGroup`. - properties: - address: - type: string - labels: - additionalProperties: - type: string - description: One or more labels associated with the endpoint. - type: object - locality: - description: The locality associated with the endpoint. - type: string - network: - type: string - ports: - additionalProperties: - type: integer - description: Set of ports associated with the endpoint. - type: object - serviceAccount: - type: string - weight: - description: The load balancing weight associated with the endpoint. - type: integer - type: object - type: object - status: - type: object - x-kubernetes-preserve-unknown-fields: true - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: istio-egressgateway-service-account - namespace: dubbo-system - labels: - app: istio-egressgateway - istio: egressgateway - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "EgressGateways" ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: istio-ingressgateway-service-account - namespace: dubbo-system - labels: - app: istio-ingressgateway - istio: ingressgateway - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "IngressGateways" ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: istio-reader-service-account - namespace: dubbo-system - labels: - app: istio-reader - release: istio ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - release: istio ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: istiod-service-account - namespace: dubbo-system - labels: - app: istiod - release: istio ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istio-reader-clusterrole-dubbo-system - labels: - app: istio-reader - release: istio -rules: - - apiGroups: - - "config.istio.io" - - "security.istio.io" - - "networking.istio.io" - - "authentication.istio.io" - - "rbac.istio.io" - resources: ["*"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["endpoints", "pods", "services", "nodes", "replicationcontrollers", "namespaces", "secrets"] - verbs: ["get", "list", "watch"] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list" ] - resources: [ "workloadentries" ] - - apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "list", "watch"] - - apiGroups: ["discovery.k8s.io"] - resources: ["endpointslices"] - verbs: ["get", "list", "watch"] - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceexports"] - verbs: ["get", "list", "watch", "create", "delete"] - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceimports"] - verbs: ["get", "list", "watch"] - - apiGroups: ["apps"] - resources: ["replicasets"] - verbs: ["get", "list", "watch"] - - apiGroups: ["authentication.k8s.io"] - resources: ["tokenreviews"] - verbs: ["create"] - - apiGroups: ["authorization.k8s.io"] - resources: ["subjectaccessreviews"] - verbs: ["create"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istio-reader-dubbo-system - labels: - app: istio-reader - release: istio -rules: - - apiGroups: - - "config.istio.io" - - "security.istio.io" - - "networking.istio.io" - - "authentication.istio.io" - - "rbac.istio.io" - resources: ["*"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["endpoints", "pods", "services", "nodes", "replicationcontrollers", "namespaces", "secrets"] - verbs: ["get", "list", "watch"] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list" ] - resources: [ "workloadentries" ] - - apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "list", "watch"] - - apiGroups: ["discovery.k8s.io"] - resources: ["endpointslices"] - verbs: ["get", "list", "watch"] - - apiGroups: ["apps"] - resources: ["replicasets"] - verbs: ["get", "list", "watch"] - - apiGroups: ["authentication.k8s.io"] - resources: ["tokenreviews"] - verbs: ["create"] - - apiGroups: ["authorization.k8s.io"] - resources: ["subjectaccessreviews"] - verbs: ["create"] - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceexports"] - verbs: ["get", "watch", "list"] - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceimports"] - verbs: ["get", "watch", "list"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istiod-clusterrole-dubbo-system - labels: - app: istiod - release: istio -rules: - # sidecar injection controller - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["mutatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update", "patch"] - - # configuration validation webhook controller - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["validatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update"] - - # istio configuration - # removing CRD permissions can break older versions of Istio running alongside this control plane (https://github.com/istio/istio/issues/29382) - # please proceed with caution - - apiGroups: ["config.istio.io", "security.istio.io", "networking.istio.io", "authentication.istio.io", "rbac.istio.io", "telemetry.istio.io", "extensions.istio.io"] - verbs: ["get", "watch", "list"] - resources: ["*"] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "workloadentries" ] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "workloadentries/status" ] - - # auto-detect installed CRD definitions - - apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "list", "watch"] - - # discovery and routing - - apiGroups: [""] - resources: ["pods", "nodes", "services", "namespaces", "endpoints"] - verbs: ["get", "list", "watch"] - - apiGroups: ["discovery.k8s.io"] - resources: ["endpointslices"] - verbs: ["get", "list", "watch"] - - # ingress controller - - apiGroups: ["networking.k8s.io"] - resources: ["ingresses", "ingressclasses"] - verbs: ["get", "list", "watch"] - - apiGroups: ["networking.k8s.io"] - resources: ["ingresses/status"] - verbs: ["*"] - - # required for CA's namespace controller - - apiGroups: [""] - resources: ["configmaps"] - verbs: ["create", "get", "list", "watch", "update"] - - # Istiod and bootstrap. - - apiGroups: ["certificates.k8s.io"] - resources: - - "certificatesigningrequests" - - "certificatesigningrequests/approval" - - "certificatesigningrequests/status" - verbs: ["update", "create", "get", "delete", "watch"] - - apiGroups: ["certificates.k8s.io"] - resources: - - "signers" - resourceNames: - - "kubernetes.io/legacy-unknown" - verbs: ["approve"] - - # Used by Istiod to verify the JWT tokens - - apiGroups: ["authentication.k8s.io"] - resources: ["tokenreviews"] - verbs: ["create"] - - # Used by Istiod to verify gateway SDS - - apiGroups: ["authorization.k8s.io"] - resources: ["subjectaccessreviews"] - verbs: ["create"] - - # Use for Kubernetes Service APIs - - apiGroups: ["networking.x-k8s.io", "gateway.networking.k8s.io"] - resources: ["*"] - verbs: ["get", "watch", "list"] - - apiGroups: ["networking.x-k8s.io", "gateway.networking.k8s.io"] - resources: ["*"] # TODO: should be on just */status but wildcard is not supported - verbs: ["update", "patch"] - - apiGroups: ["gateway.networking.k8s.io"] - resources: ["gatewayclasses"] - verbs: ["create", "update", "patch", "delete"] - - # Needed for multicluster secret reading, possibly ingress certs in the future - - apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "watch", "list"] - - # Used for MCS serviceexport management - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceexports"] - verbs: [ "get", "watch", "list", "create", "delete"] - - # Used for MCS serviceimport management - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceimports"] - verbs: ["get", "watch", "list"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istiod-gateway-controller-dubbo-system - labels: - app: istiod - release: istio -rules: - - apiGroups: ["apps"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "deployments" ] - - apiGroups: [""] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "services" ] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istiod-dubbo-system - labels: - app: istiod - release: istio -rules: - # sidecar injection controller - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["mutatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update", "patch"] - - # configuration validation webhook controller - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["validatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update"] - - # istio configuration - # removing CRD permissions can break older versions of Istio running alongside this control plane (https://github.com/istio/istio/issues/29382) - # please proceed with caution - - apiGroups: ["config.istio.io", "security.istio.io", "networking.istio.io", "authentication.istio.io", "rbac.istio.io", "telemetry.istio.io"] - verbs: ["get", "watch", "list"] - resources: ["*"] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "workloadentries" ] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "workloadentries/status" ] - - # auto-detect installed CRD definitions - - apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "list", "watch"] - - # discovery and routing - - apiGroups: [""] - resources: ["pods", "nodes", "services", "namespaces", "endpoints"] - verbs: ["get", "list", "watch"] - - apiGroups: ["discovery.k8s.io"] - resources: ["endpointslices"] - verbs: ["get", "list", "watch"] - - # ingress controller - - apiGroups: ["networking.k8s.io"] - resources: ["ingresses", "ingressclasses"] - verbs: ["get", "list", "watch"] - - apiGroups: ["networking.k8s.io"] - resources: ["ingresses/status"] - verbs: ["*"] - - # required for CA's namespace controller - - apiGroups: [""] - resources: ["configmaps"] - verbs: ["create", "get", "list", "watch", "update"] - - # Istiod and bootstrap. - - apiGroups: ["certificates.k8s.io"] - resources: - - "certificatesigningrequests" - - "certificatesigningrequests/approval" - - "certificatesigningrequests/status" - verbs: ["update", "create", "get", "delete", "watch"] - - apiGroups: ["certificates.k8s.io"] - resources: - - "signers" - resourceNames: - - "kubernetes.io/legacy-unknown" - verbs: ["approve"] - - # Used by Istiod to verify the JWT tokens - - apiGroups: ["authentication.k8s.io"] - resources: ["tokenreviews"] - verbs: ["create"] - - # Used by Istiod to verify gateway SDS - - apiGroups: ["authorization.k8s.io"] - resources: ["subjectaccessreviews"] - verbs: ["create"] - - # Use for Kubernetes Service APIs - - apiGroups: ["networking.x-k8s.io", "gateway.networking.k8s.io"] - resources: ["*"] - verbs: ["get", "watch", "list"] - - apiGroups: ["networking.x-k8s.io", "gateway.networking.k8s.io"] - resources: ["*"] # TODO: should be on just */status but wildcard is not supported - verbs: ["update"] - - apiGroups: ["gateway.networking.k8s.io"] - resources: ["gatewayclasses"] - verbs: ["create", "update", "patch", "delete"] - - # Needed for multicluster secret reading, possibly ingress certs in the future - - apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "watch", "list"] - - # Used for MCS serviceexport management - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceexports"] - verbs: ["get", "watch", "list", "create", "delete"] - - # Used for MCS serviceimport management - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceimports"] - verbs: ["get", "watch", "list"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istio-reader-clusterrole-dubbo-system - labels: - app: istio-reader - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istio-reader-clusterrole-dubbo-system -subjects: - - kind: ServiceAccount - name: istio-reader-service-account - namespace: dubbo-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istio-reader-dubbo-system - labels: - app: istio-reader - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istio-reader-dubbo-system -subjects: - - kind: ServiceAccount - name: istio-reader-service-account - namespace: dubbo-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istiod-clusterrole-dubbo-system - labels: - app: istiod - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istiod-clusterrole-dubbo-system -subjects: - - kind: ServiceAccount - name: istiod - namespace: dubbo-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istiod-gateway-controller-dubbo-system - labels: - app: istiod - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istiod-gateway-controller-dubbo-system -subjects: -- kind: ServiceAccount - name: istiod - namespace: dubbo-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istiod-dubbo-system - labels: - app: istiod - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istiod-dubbo-system -subjects: - - kind: ServiceAccount - name: istiod-service-account - namespace: dubbo-system ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - name: istio-validator-dubbo-system - labels: - app: istiod - release: istio - istio: istiod - istio.io/rev: default -webhooks: - # Webhook handling per-revision validation. Mostly here so we can determine whether webhooks - # are rejecting invalid configs on a per-revision basis. - - name: rev.validation.istio.io - clientConfig: - # Should change from base but cannot for API compat - service: - name: istiod - namespace: dubbo-system - path: "/validate" - rules: - - operations: - - CREATE - - UPDATE - apiGroups: - - security.istio.io - - networking.istio.io - - telemetry.istio.io - - extensions.istio.io - apiVersions: - - "*" - resources: - - "*" - # Fail open until the validation webhook is ready. The webhook controller - # will update this to `Fail` and patch in the `caBundle` when the webhook - # endpoint is ready. - failurePolicy: Ignore - sideEffects: None - admissionReviewVersions: ["v1beta1", "v1"] - objectSelector: - matchExpressions: - - key: istio.io/rev - operator: In - values: - - "default" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.11 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.12 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.13 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.14 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.15 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.11 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: tcp_stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.12 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: tcp_stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.13 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: tcp_stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.14 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: tcp_stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.15 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: tcp_stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: istio - namespace: dubbo-system - labels: - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - release: istio -data: - - # Configuration file for the mesh networks to be used by the Split Horizon EDS. - meshNetworks: |- - networks: {} - - mesh: |- - defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 - enablePrometheusMerge: true - rootNamespace: dubbo-system - trustDomain: cluster.local ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: istio-sidecar-injector - namespace: dubbo-system - labels: - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - release: istio -data: - - values: |- - { - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": true, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "enabled": false - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } - } - - # To disable injection: use omitSidecarInjectorConfigMap, which disables the webhook patching - # and istiod webhook functionality. - # - # New fields should not use Values - it is a 'primary' config object, users should be able - # to fine tune it or use it with kube-inject. - config: |- - # defaultTemplates defines the default template to use for pods that do not explicitly specify a template - defaultTemplates: [sidecar] - policy: enabled - alwaysInjectSelector: - [] - neverInjectSelector: - [] - injectedAnnotations: - template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" - templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - name: istio-sidecar-injector - labels: - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - app: sidecar-injector - release: istio -webhooks: -- name: rev.namespace.sidecar-injector.istio.io - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: "/inject" - port: 443 - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] - namespaceSelector: - matchExpressions: - - key: istio.io/rev - operator: In - values: - - "default" - - key: istio-injection - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" -- name: rev.object.sidecar-injector.istio.io - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: "/inject" - port: 443 - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] - namespaceSelector: - matchExpressions: - - key: istio.io/rev - operator: DoesNotExist - - key: istio-injection - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" - - key: istio.io/rev - operator: In - values: - - "default" -- name: namespace.sidecar-injector.istio.io - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: "/inject" - port: 443 - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: In - values: - - enabled - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" -- name: object.sidecar-injector.istio.io - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: "/inject" - port: 443 - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: DoesNotExist - - key: istio.io/rev - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: In - values: - - "true" - - key: istio.io/rev - operator: DoesNotExist ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: istio-egressgateway - namespace: dubbo-system - labels: - app: istio-egressgateway - istio: egressgateway - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "EgressGateways" -spec: - selector: - matchLabels: - app: istio-egressgateway - istio: egressgateway - strategy: - rollingUpdate: - maxSurge: 100% - maxUnavailable: 25% - template: - metadata: - labels: - app: istio-egressgateway - istio: egressgateway - heritage: Tiller - release: istio - chart: gateways - service.istio.io/canonical-name: istio-egressgateway - service.istio.io/canonical-revision: latest - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "EgressGateways" - sidecar.istio.io/inject: "false" - annotations: - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - prometheus.io/path: "/stats/prometheus" - sidecar.istio.io/inject: "false" - spec: - securityContext: - runAsUser: 1337 - runAsGroup: 1337 - runAsNonRoot: true - fsGroup: 1337 - serviceAccountName: istio-egressgateway-service-account - containers: - - name: istio-proxy - image: "apache/dubbo-agent:latest" - ports: - - containerPort: 8080 - protocol: TCP - - containerPort: 8443 - protocol: TCP - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - scheme: HTTP - initialDelaySeconds: 1 - periodSeconds: 2 - successThreshold: 1 - timeoutSeconds: 1 - resources: - limits: - cpu: 2000m - memory: 1024Mi - requests: - cpu: 100m - memory: 128Mi - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: NODE_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.nodeName - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: status.podIP - - name: HOST_IP - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: status.hostIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: ISTIO_META_WORKLOAD_NAME - value: istio-egressgateway - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/dubbo-system/deployments/istio-egressgateway - - name: ISTIO_META_MESH_ID - value: "cluster.local" - - name: TRUST_DOMAIN - value: "cluster.local" - - name: ISTIO_META_UNPRIVILEGED_POD - value: "true" - - name: ISTIO_META_CLUSTER_ID - value: "Kubernetes" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - - name: istio-envoy - mountPath: /etc/istio/proxy - - name: config-volume - mountPath: /etc/istio/config - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - name: istio-token - mountPath: /var/run/secrets/tokens - readOnly: true - - mountPath: /var/lib/istio/data - name: istio-data - - name: podinfo - mountPath: /etc/istio/pod - - name: egressgateway-certs - mountPath: "/etc/istio/egressgateway-certs" - readOnly: true - - name: egressgateway-ca-certs - mountPath: "/etc/istio/egressgateway-ca-certs" - readOnly: true - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - - name: podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - - name: istio-envoy - emptyDir: {} - - name: istio-data - emptyDir: {} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: istio-ca - - name: config-volume - configMap: - name: istio - optional: true - - name: egressgateway-certs - secret: - secretName: "istio-egressgateway-certs" - optional: true - - name: egressgateway-ca-certs - secret: - secretName: "istio-egressgateway-ca-certs" - optional: true - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - preferredDuringSchedulingIgnoredDuringExecution: ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - istio: ingressgateway - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "IngressGateways" -spec: - selector: - matchLabels: - app: istio-ingressgateway - istio: ingressgateway - strategy: - rollingUpdate: - maxSurge: 100% - maxUnavailable: 25% - template: - metadata: - labels: - app: istio-ingressgateway - istio: ingressgateway - heritage: Tiller - release: istio - chart: gateways - service.istio.io/canonical-name: istio-ingressgateway - service.istio.io/canonical-revision: latest - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "IngressGateways" - sidecar.istio.io/inject: "false" - annotations: - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - prometheus.io/path: "/stats/prometheus" - sidecar.istio.io/inject: "false" - spec: - securityContext: - runAsUser: 1337 - runAsGroup: 1337 - runAsNonRoot: true - fsGroup: 1337 - serviceAccountName: istio-ingressgateway-service-account - containers: - - name: istio-proxy - image: "apache/dubbo-agent:latest" - ports: - - containerPort: 15021 - protocol: TCP - - containerPort: 8080 - protocol: TCP - - containerPort: 8443 - protocol: TCP - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - scheme: HTTP - initialDelaySeconds: 1 - periodSeconds: 2 - successThreshold: 1 - timeoutSeconds: 1 - resources: - limits: - cpu: 2000m - memory: 1024Mi - requests: - cpu: 100m - memory: 128Mi - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: NODE_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.nodeName - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: status.podIP - - name: HOST_IP - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: status.hostIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: ISTIO_META_WORKLOAD_NAME - value: istio-ingressgateway - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/dubbo-system/deployments/istio-ingressgateway - - name: ISTIO_META_MESH_ID - value: "cluster.local" - - name: TRUST_DOMAIN - value: "cluster.local" - - name: ISTIO_META_UNPRIVILEGED_POD - value: "true" - - name: ISTIO_META_CLUSTER_ID - value: "Kubernetes" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - - name: istio-envoy - mountPath: /etc/istio/proxy - - name: config-volume - mountPath: /etc/istio/config - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - name: istio-token - mountPath: /var/run/secrets/tokens - readOnly: true - - mountPath: /var/lib/istio/data - name: istio-data - - name: podinfo - mountPath: /etc/istio/pod - - name: ingressgateway-certs - mountPath: "/etc/istio/ingressgateway-certs" - readOnly: true - - name: ingressgateway-ca-certs - mountPath: "/etc/istio/ingressgateway-ca-certs" - readOnly: true - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - - name: podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - - name: istio-envoy - emptyDir: {} - - name: istio-data - emptyDir: {} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: istio-ca - - name: config-volume - configMap: - name: istio - optional: true - - name: ingressgateway-certs - secret: - secretName: "istio-ingressgateway-certs" - optional: true - - name: ingressgateway-ca-certs - secret: - secretName: "istio-ingressgateway-ca-certs" - optional: true - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - preferredDuringSchedulingIgnoredDuringExecution: ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - istio: pilot - release: istio -spec: - strategy: - rollingUpdate: - maxSurge: 100% - maxUnavailable: 25% - selector: - matchLabels: - istio: pilot - template: - metadata: - labels: - app: istiod - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - sidecar.istio.io/inject: "false" - operator.istio.io/component: "Pilot" - istio: pilot - annotations: - prometheus.io/port: "15014" - prometheus.io/scrape: "true" - sidecar.istio.io/inject: "false" - spec: - serviceAccountName: istiod - securityContext: - fsGroup: 1337 - containers: - - name: discovery - image: "apache/dubbo-pilot:latest" - args: - - "discovery" - - --monitoringAddr=:15014 - - --log_output_level=default:info - - --domain - - cluster.local - - --keepaliveMaxServerConnectionAge - - "30m" - ports: - - containerPort: 8080 - protocol: TCP - - containerPort: 15010 - protocol: TCP - - containerPort: 15017 - protocol: TCP - readinessProbe: - httpGet: - path: /ready - port: 8080 - initialDelaySeconds: 1 - periodSeconds: 3 - timeoutSeconds: 5 - env: - - name: REVISION - value: "default" - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.serviceAccountName - - name: KUBECONFIG - value: /var/run/secrets/remote/config - - name: PILOT_TRACE_SAMPLING - value: "1" - - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_OUTBOUND - value: "true" - - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_INBOUND - value: "true" - - name: ISTIOD_ADDR - value: istiod.dubbo-system.svc:15012 - - name: PILOT_ENABLE_ANALYSIS - value: "false" - - name: CLUSTER_ID - value: "Kubernetes" - resources: - requests: - cpu: 500m - memory: 2048Mi - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsUser: 1337 - runAsGroup: 1337 - runAsNonRoot: true - capabilities: - drop: - - ALL - volumeMounts: - - name: istio-token - mountPath: /var/run/secrets/tokens - readOnly: true - - name: local-certs - mountPath: /var/run/secrets/istio-dns - - name: cacerts - mountPath: /etc/cacerts - readOnly: true - - name: istio-kubeconfig - mountPath: /var/run/secrets/remote - readOnly: true - volumes: - # Technically not needed on this pod - but it helps debugging/testing SDS - # Should be removed after everything works. - - emptyDir: - medium: Memory - name: local-certs - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - # Optional: user-generated root - - name: cacerts - secret: - secretName: cacerts - optional: true - - name: istio-kubeconfig - secret: - secretName: istio-kubeconfig - optional: true ---- -apiVersion: policy/v1beta1 -kind: PodDisruptionBudget -metadata: - name: istio-egressgateway - namespace: dubbo-system - labels: - app: istio-egressgateway - istio: egressgateway - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "EgressGateways" -spec: - minAvailable: 1 - selector: - matchLabels: - app: istio-egressgateway - istio: egressgateway ---- -apiVersion: policy/v1beta1 -kind: PodDisruptionBudget -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - istio: ingressgateway - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "IngressGateways" -spec: - minAvailable: 1 - selector: - matchLabels: - app: istio-ingressgateway - istio: ingressgateway ---- -apiVersion: policy/v1beta1 -kind: PodDisruptionBudget -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - release: istio - istio: pilot -spec: - minAvailable: 1 - selector: - matchLabels: - app: istiod - istio: pilot ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: istio-egressgateway-sds - namespace: dubbo-system - labels: - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "EgressGateways" -rules: -- apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "watch", "list"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: istio-ingressgateway-sds - namespace: dubbo-system - labels: - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "IngressGateways" -rules: -- apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "watch", "list"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - release: istio -rules: -- apiGroups: ["networking.istio.io"] - verbs: ["create"] - resources: ["gateways"] - -- apiGroups: [""] - resources: ["secrets"] - # TODO lock this down to istio-ca-cert if not using the DNS cert mesh config - verbs: ["create", "get", "watch", "list", "update", "delete"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: istiod-dubbo-system - namespace: dubbo-system - labels: - app: istiod - release: istio -rules: -- apiGroups: ["networking.istio.io"] - verbs: ["create"] - resources: ["gateways"] - -- apiGroups: [""] - resources: ["secrets"] - # TODO lock this down to istio-ca-cert if not using the DNS cert mesh config - verbs: ["create", "get", "watch", "list", "update", "delete"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: istio-egressgateway-sds - namespace: dubbo-system - labels: - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "EgressGateways" -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: istio-egressgateway-sds -subjects: -- kind: ServiceAccount - name: istio-egressgateway-service-account ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: istio-ingressgateway-sds - namespace: dubbo-system - labels: - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "IngressGateways" -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: istio-ingressgateway-sds -subjects: -- kind: ServiceAccount - name: istio-ingressgateway-service-account ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: istiod -subjects: - - kind: ServiceAccount - name: istiod - namespace: dubbo-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: istiod-dubbo-system - namespace: dubbo-system - labels: - app: istiod - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: istiod-dubbo-system -subjects: - - kind: ServiceAccount - name: istiod-service-account - namespace: dubbo-system ---- -apiVersion: autoscaling/v2beta2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-egressgateway - namespace: dubbo-system - labels: - app: istio-egressgateway - istio: egressgateway - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "EgressGateways" -spec: - maxReplicas: 5 - minReplicas: 1 - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: istio-egressgateway - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: 80 ---- -apiVersion: autoscaling/v2beta2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - istio: ingressgateway - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "IngressGateways" -spec: - maxReplicas: 5 - minReplicas: 1 - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: istio-ingressgateway - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: 80 ---- -apiVersion: autoscaling/v2beta2 -kind: HorizontalPodAutoscaler -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" -spec: - maxReplicas: 5 - minReplicas: 1 - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: istiod - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: 80 ---- -apiVersion: v1 -kind: Service -metadata: - name: istio-egressgateway - namespace: dubbo-system - annotations: - labels: - app: istio-egressgateway - istio: egressgateway - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "EgressGateways" -spec: - type: ClusterIP - selector: - app: istio-egressgateway - istio: egressgateway - ports: - - - name: http2 - port: 80 - protocol: TCP - targetPort: 8080 - - - name: https - port: 443 - protocol: TCP - targetPort: 8443 ---- -apiVersion: v1 -kind: Service -metadata: - name: istio-ingressgateway - namespace: dubbo-system - annotations: - labels: - app: istio-ingressgateway - istio: ingressgateway - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "IngressGateways" -spec: - type: LoadBalancer - selector: - app: istio-ingressgateway - istio: ingressgateway - ports: - - - name: status-port - port: 15021 - protocol: TCP - targetPort: 15021 - - - name: http2 - port: 80 - protocol: TCP - targetPort: 8080 - - - name: https - port: 443 - protocol: TCP - targetPort: 8443 ---- -apiVersion: v1 -kind: Service -metadata: - name: istiod - namespace: dubbo-system - labels: - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - app: istiod - istio: pilot - release: istio -spec: - ports: - - port: 15010 - name: grpc-xds # plaintext - protocol: TCP - - port: 15012 - name: https-dns # mTLS with k8s-signed cert - protocol: TCP - - port: 443 - name: https-webhook # validation and injection - targetPort: 15017 - protocol: TCP - - port: 15014 - name: http-monitoring # prometheus stats - protocol: TCP - selector: - app: istiod - # Label used by the 'default' service. For versioned deployments we match with app and version. - # This avoids default deployment picking the canary - istio: pilot ---- diff --git a/operator/cmd/mesh/testdata/manifest-generate/output/deprecated_autoscaling_k8s_spec.golden.yaml b/operator/cmd/mesh/testdata/manifest-generate/output/deprecated_autoscaling_k8s_spec.golden.yaml deleted file mode 100644 index 3b94accf2..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/output/deprecated_autoscaling_k8s_spec.golden.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - labels: - app: istio-ingressgateway - install.operator.istio.io/owning-resource: unknown - istio: ingressgateway - istio.io/rev: default - operator.istio.io/component: IngressGateways - release: istio - name: istio-ingressgateway - namespace: dubbo-system -spec: - maxReplicas: 222 - metrics: - - object: - averageValue: 111 - metricName: cpu - type: Resource - minReplicas: 1 - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: istio-ingressgateway ---- - - -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - labels: - app: istiod - install.operator.istio.io/owning-resource: unknown - istio.io/rev: default - operator.istio.io/component: Pilot - release: istio - name: istiod - namespace: istio-control -spec: - maxReplicas: 333 - metrics: - - resource: - name: cpu - targetAverageUtilization: 444 - type: Resource - minReplicas: 1 - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: istio-pilot ---- diff --git a/operator/cmd/mesh/testdata/manifest-generate/output/flag_force.golden.yaml b/operator/cmd/mesh/testdata/manifest-generate/output/flag_force.golden.yaml deleted file mode 100644 index e69de29bb..000000000 diff --git a/operator/cmd/mesh/testdata/manifest-generate/output/flag_output.golden.yaml b/operator/cmd/mesh/testdata/manifest-generate/output/flag_output.golden.yaml deleted file mode 100644 index 2beddb559..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/output/flag_output.golden.yaml +++ /dev/null @@ -1,146 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - istio: pilot - release: istio -spec: - strategy: - rollingUpdate: - maxSurge: 100% - maxUnavailable: 25% - selector: - matchLabels: - istio: pilot - template: - metadata: - labels: - app: istiod - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - sidecar.istio.io/inject: "false" - operator.istio.io/component: "Pilot" - istio: pilot - annotations: - prometheus.io/port: "15014" - prometheus.io/scrape: "true" - sidecar.istio.io/inject: "false" - spec: - serviceAccountName: istiod - securityContext: - fsGroup: 1337 - containers: - - name: discovery - image: "apache/dubbo-pilot:latest" - args: - - "discovery" - - --monitoringAddr=:15014 - - --log_output_level=default:info - - --domain - - cluster.local - - --keepaliveMaxServerConnectionAge - - "30m" - ports: - - containerPort: 8080 - protocol: TCP - - containerPort: 15010 - protocol: TCP - - containerPort: 15017 - protocol: TCP - readinessProbe: - httpGet: - path: /ready - port: 8080 - initialDelaySeconds: 1 - periodSeconds: 3 - timeoutSeconds: 5 - env: - - name: REVISION - value: "default" - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.serviceAccountName - - name: KUBECONFIG - value: /var/run/secrets/remote/config - - name: PILOT_TRACE_SAMPLING - value: "1" - - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_OUTBOUND - value: "true" - - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_INBOUND - value: "true" - - name: ISTIOD_ADDR - value: istiod.dubbo-system.svc:15012 - - name: PILOT_ENABLE_ANALYSIS - value: "false" - - name: CLUSTER_ID - value: "Kubernetes" - resources: - requests: - cpu: 500m - memory: 2048Mi - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsUser: 1337 - runAsGroup: 1337 - runAsNonRoot: true - capabilities: - drop: - - ALL - volumeMounts: - - name: istio-token - mountPath: /var/run/secrets/tokens - readOnly: true - - name: local-certs - mountPath: /var/run/secrets/istio-dns - - name: cacerts - mountPath: /etc/cacerts - readOnly: true - - name: istio-kubeconfig - mountPath: /var/run/secrets/remote - readOnly: true - volumes: - # Technically not needed on this pod - but it helps debugging/testing SDS - # Should be removed after everything works. - - emptyDir: - medium: Memory - name: local-certs - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - # Optional: user-generated root - - name: cacerts - secret: - secretName: cacerts - optional: true - - name: istio-kubeconfig - secret: - secretName: istio-kubeconfig - optional: true ---- diff --git a/operator/cmd/mesh/testdata/manifest-generate/output/flag_output_set_values.golden.yaml b/operator/cmd/mesh/testdata/manifest-generate/output/flag_output_set_values.golden.yaml deleted file mode 100644 index b565fe6e1..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/output/flag_output_set_values.golden.yaml +++ /dev/null @@ -1,205 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - istio: ingressgateway - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "IngressGateways" -spec: - selector: - matchLabels: - app: istio-ingressgateway - istio: ingressgateway - strategy: - rollingUpdate: - maxSurge: 100% - maxUnavailable: 25% - template: - metadata: - labels: - app: istio-ingressgateway - istio: ingressgateway - heritage: Tiller - release: istio - chart: gateways - service.istio.io/canonical-name: istio-ingressgateway - service.istio.io/canonical-revision: latest - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "IngressGateways" - sidecar.istio.io/inject: "false" - annotations: - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - prometheus.io/path: "/stats/prometheus" - sidecar.istio.io/inject: "false" - spec: - securityContext: - runAsUser: 1337 - runAsGroup: 1337 - runAsNonRoot: true - fsGroup: 1337 - serviceAccountName: istio-ingressgateway-service-account - containers: - - name: istio-proxy - image: "gcr.io/istio-testing/mynewproxy:latest" - ports: - - containerPort: 15021 - protocol: TCP - - containerPort: 8080 - protocol: TCP - - containerPort: 8443 - protocol: TCP - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - scheme: HTTP - initialDelaySeconds: 1 - periodSeconds: 2 - successThreshold: 1 - timeoutSeconds: 1 - resources: - limits: - cpu: 2000m - memory: 1024Mi - requests: - cpu: 100m - memory: 128Mi - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: NODE_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.nodeName - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: status.podIP - - name: HOST_IP - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: status.hostIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: ISTIO_META_WORKLOAD_NAME - value: istio-ingressgateway - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/dubbo-system/deployments/istio-ingressgateway - - name: ISTIO_META_MESH_ID - value: "cluster.local" - - name: TRUST_DOMAIN - value: "cluster.local" - - name: ISTIO_META_UNPRIVILEGED_POD - value: "true" - - name: ISTIO_META_CLUSTER_ID - value: "Kubernetes" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - - name: istio-envoy - mountPath: /etc/istio/proxy - - name: config-volume - mountPath: /etc/istio/config - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - name: istio-token - mountPath: /var/run/secrets/tokens - readOnly: true - - mountPath: /var/lib/istio/data - name: istio-data - - name: podinfo - mountPath: /etc/istio/pod - - name: ingressgateway-certs - mountPath: "/etc/istio/ingressgateway-certs" - readOnly: true - - name: ingressgateway-ca-certs - mountPath: "/etc/istio/ingressgateway-ca-certs" - readOnly: true - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - - name: podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - - name: istio-envoy - emptyDir: {} - - name: istio-data - emptyDir: {} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: istio-ca - - name: config-volume - configMap: - name: istio - optional: true - - name: ingressgateway-certs - secret: - secretName: "istio-ingressgateway-certs" - optional: true - - name: ingressgateway-ca-certs - secret: - secretName: "istio-ingressgateway-ca-certs" - optional: true ---- diff --git a/operator/cmd/mesh/testdata/manifest-generate/output/flag_values_enable_egressgateway.golden.yaml b/operator/cmd/mesh/testdata/manifest-generate/output/flag_values_enable_egressgateway.golden.yaml deleted file mode 100644 index 51d666388..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/output/flag_values_enable_egressgateway.golden.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: istio-egressgateway - namespace: dubbo-system - annotations: - labels: - app: istio-egressgateway - istio: egressgateway - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "EgressGateways" -spec: - type: ClusterIP - selector: - app: istio-egressgateway - istio: egressgateway - ports: - - - name: http2 - port: 80 - protocol: TCP - targetPort: 8080 - - - name: https - port: 443 - protocol: TCP - targetPort: 8443 ---- diff --git a/operator/cmd/mesh/testdata/manifest-generate/output/gateways.golden.yaml b/operator/cmd/mesh/testdata/manifest-generate/output/gateways.golden.yaml deleted file mode 100644 index e69de29bb..000000000 diff --git a/operator/cmd/mesh/testdata/manifest-generate/output/helm_values_enablement.golden.yaml b/operator/cmd/mesh/testdata/manifest-generate/output/helm_values_enablement.golden.yaml deleted file mode 100644 index 074054006..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/output/helm_values_enablement.golden.yaml +++ /dev/null @@ -1,239 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: istio-egressgateway - namespace: dubbo-system - labels: - app: istio-egressgateway - istio: egressgateway - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "EgressGateways" -spec: - selector: - matchLabels: - app: istio-egressgateway - istio: egressgateway - strategy: - rollingUpdate: - maxSurge: 100% - maxUnavailable: 25% - template: - metadata: - labels: - app: istio-egressgateway - istio: egressgateway - heritage: Tiller - release: istio - chart: gateways - service.istio.io/canonical-name: istio-egressgateway - service.istio.io/canonical-revision: latest - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "EgressGateways" - sidecar.istio.io/inject: "false" - annotations: - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - prometheus.io/path: "/stats/prometheus" - sidecar.istio.io/inject: "false" - spec: - securityContext: - runAsUser: 1337 - runAsGroup: 1337 - runAsNonRoot: true - fsGroup: 1337 - serviceAccountName: istio-egressgateway-service-account - containers: - - name: istio-proxy - image: "apache/dubbo-agent:latest" - ports: - - containerPort: 8080 - protocol: TCP - - containerPort: 8443 - protocol: TCP - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - scheme: HTTP - initialDelaySeconds: 1 - periodSeconds: 2 - successThreshold: 1 - timeoutSeconds: 1 - resources: - limits: - cpu: 2000m - memory: 1024Mi - requests: - cpu: 100m - memory: 128Mi - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: NODE_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.nodeName - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: status.podIP - - name: HOST_IP - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: status.hostIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: ISTIO_META_WORKLOAD_NAME - value: istio-egressgateway - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/dubbo-system/deployments/istio-egressgateway - - name: ISTIO_META_MESH_ID - value: "cluster.local" - - name: TRUST_DOMAIN - value: "cluster.local" - - name: ISTIO_META_UNPRIVILEGED_POD - value: "true" - - name: ISTIO_META_CLUSTER_ID - value: "Kubernetes" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - - name: istio-envoy - mountPath: /etc/istio/proxy - - name: config-volume - mountPath: /etc/istio/config - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - name: istio-token - mountPath: /var/run/secrets/tokens - readOnly: true - - mountPath: /var/lib/istio/data - name: istio-data - - name: podinfo - mountPath: /etc/istio/pod - - name: egressgateway-certs - mountPath: "/etc/istio/egressgateway-certs" - readOnly: true - - name: egressgateway-ca-certs - mountPath: "/etc/istio/egressgateway-ca-certs" - readOnly: true - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - - name: podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - - name: istio-envoy - emptyDir: {} - - name: istio-data - emptyDir: {} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: istio-ca - - name: config-volume - configMap: - name: istio - optional: true - - name: egressgateway-certs - secret: - secretName: "istio-egressgateway-certs" - optional: true - - name: egressgateway-ca-certs - secret: - secretName: "istio-egressgateway-ca-certs" - optional: true - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - preferredDuringSchedulingIgnoredDuringExecution: ---- - - -apiVersion: v1 -kind: Service -metadata: - name: istio-egressgateway - namespace: dubbo-system - annotations: - labels: - app: istio-egressgateway - istio: egressgateway - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "EgressGateways" -spec: - type: ClusterIP - selector: - app: istio-egressgateway - istio: egressgateway - ports: - - - name: http2 - port: 80 - protocol: TCP - targetPort: 8080 - - - name: https - port: 443 - protocol: TCP - targetPort: 8443 ---- diff --git a/operator/cmd/mesh/testdata/manifest-generate/output/ingressgateway_k8s_settings.golden.yaml b/operator/cmd/mesh/testdata/manifest-generate/output/ingressgateway_k8s_settings.golden.yaml deleted file mode 100644 index 0c20a4ec3..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/output/ingressgateway_k8s_settings.golden.yaml +++ /dev/null @@ -1,494 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: istio-ingressgateway - install.operator.istio.io/owning-resource: unknown - istio: ingressgateway - istio.io/rev: default - operator.istio.io/component: IngressGateways - release: istio - name: istio-ingressgateway - namespace: dubbo-system -spec: - selector: - matchLabels: - app: istio-ingressgateway - istio: ingressgateway - strategy: - rollingUpdate: - maxSurge: 100% - maxUnavailable: 25% - template: - metadata: - annotations: - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/inject: "false" - labels: - app: istio-ingressgateway - chart: gateways - heritage: Tiller - install.operator.istio.io/owning-resource: unknown - istio: ingressgateway - istio.io/rev: default - operator.istio.io/component: IngressGateways - release: istio - service.istio.io/canonical-name: istio-ingressgateway - service.istio.io/canonical-revision: latest - sidecar.istio.io/inject: "false" - spec: - affinity: - nodeAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - requiredDuringSchedulingIgnoredDuringExecution: - containers: - - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: NODE_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.nodeName - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: status.podIP - - name: HOST_IP - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: status.hostIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: ISTIO_META_WORKLOAD_NAME - value: istio-ingressgateway - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/dubbo-system/deployments/istio-ingressgateway - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - - name: ISTIO_META_UNPRIVILEGED_POD - value: "true" - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15021 - protocol: TCP - - containerPort: 8080 - protocol: TCP - - containerPort: 8443 - protocol: TCP - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - scheme: HTTP - initialDelaySeconds: 1 - periodSeconds: 2 - successThreshold: 1 - timeoutSeconds: 1 - resources: - limits: - cpu: 2000m - memory: 1024Mi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /etc/istio/config - name: config-volume - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/run/secrets/tokens - name: istio-token - readOnly: true - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/pod - name: podinfo - - mountPath: /etc/istio/ingressgateway-certs - name: ingressgateway-certs - readOnly: true - - mountPath: /etc/istio/ingressgateway-ca-certs - name: ingressgateway-ca-certs - readOnly: true - securityContext: - fsGroup: 1337 - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - sysctls: - - name: net.ipv4.ip_local_port_range - value: 80 65535 - serviceAccountName: istio-ingressgateway-service-account - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: podinfo - - emptyDir: {} - name: istio-envoy - - emptyDir: {} - name: istio-data - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio - optional: true - name: config-volume - - name: ingressgateway-certs - secret: - optional: true - secretName: istio-ingressgateway-certs - - name: ingressgateway-ca-certs - secret: - optional: true - secretName: istio-ingressgateway-ca-certs ---- - - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: istio-ingressgateway-custom - namespace: dubbo-system - labels: - app: istio-ingressgateway - istio: ingressgateway - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "IngressGateways" -spec: - selector: - matchLabels: - app: istio-ingressgateway - istio: ingressgateway - strategy: - rollingUpdate: - maxSurge: 100% - maxUnavailable: 25% - template: - metadata: - labels: - app: istio-ingressgateway - istio: ingressgateway - heritage: Tiller - release: istio - chart: gateways - service.istio.io/canonical-name: istio-ingressgateway-custom - service.istio.io/canonical-revision: latest - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "IngressGateways" - sidecar.istio.io/inject: "false" - annotations: - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - prometheus.io/path: "/stats/prometheus" - sidecar.istio.io/inject: "false" - spec: - securityContext: - runAsUser: 1337 - runAsGroup: 1337 - runAsNonRoot: true - fsGroup: 1337 - serviceAccountName: istio-ingressgateway-custom-service-account - containers: - - name: istio-proxy - image: "apache/dubbo-agent:latest" - ports: - - containerPort: 15021 - protocol: TCP - - containerPort: 8080 - protocol: TCP - - containerPort: 8443 - protocol: TCP - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - scheme: HTTP - initialDelaySeconds: 1 - periodSeconds: 2 - successThreshold: 1 - timeoutSeconds: 1 - resources: - limits: - cpu: 2000m - memory: 1024Mi - requests: - cpu: 100m - memory: 128Mi - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: NODE_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.nodeName - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: status.podIP - - name: HOST_IP - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: status.hostIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: ISTIO_META_WORKLOAD_NAME - value: istio-ingressgateway-custom - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/dubbo-system/deployments/istio-ingressgateway-custom - - name: ISTIO_META_MESH_ID - value: "cluster.local" - - name: TRUST_DOMAIN - value: "cluster.local" - - name: ISTIO_META_UNPRIVILEGED_POD - value: "true" - - name: ISTIO_META_CLUSTER_ID - value: "Kubernetes" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - - name: istio-envoy - mountPath: /etc/istio/proxy - - name: config-volume - mountPath: /etc/istio/config - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - name: istio-token - mountPath: /var/run/secrets/tokens - readOnly: true - - mountPath: /var/lib/istio/data - name: istio-data - - name: podinfo - mountPath: /etc/istio/pod - - name: ingressgateway-certs - mountPath: "/etc/istio/ingressgateway-certs" - readOnly: true - - name: ingressgateway-ca-certs - mountPath: "/etc/istio/ingressgateway-ca-certs" - readOnly: true - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - - name: podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - - name: istio-envoy - emptyDir: {} - - name: istio-data - emptyDir: {} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: istio-ca - - name: config-volume - configMap: - name: istio - optional: true - - name: ingressgateway-certs - secret: - secretName: "istio-ingressgateway-certs" - optional: true - - name: ingressgateway-ca-certs - secret: - secretName: "istio-ingressgateway-ca-certs" - optional: true - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - preferredDuringSchedulingIgnoredDuringExecution: ---- - - -apiVersion: v1 -kind: Service -metadata: - annotations: - manifest-generate: testserviceAnnotation - labels: - app: istio-ingressgateway - install.operator.istio.io/owning-resource: unknown - istio: ingressgateway - istio.io/rev: default - operator.istio.io/component: IngressGateways - release: istio - name: istio-ingressgateway - namespace: dubbo-system -spec: - externalTrafficPolicy: Local - ports: - - name: status-port - port: 15021 - protocol: TCP - targetPort: 15021 - - name: http2 - port: 80 - protocol: TCP - targetPort: 8080 - - name: https - port: 443 - protocol: TCP - targetPort: 8443 - selector: - app: istio-ingressgateway - istio: ingressgateway - type: LoadBalancer ---- - - -apiVersion: v1 -kind: Service -metadata: - annotations: null - labels: - app: istio-ingressgateway - install.operator.istio.io/owning-resource: unknown - istio: ingressgateway - istio.io/rev: default - operator.istio.io/component: IngressGateways - release: istio - name: istio-ingressgateway-custom - namespace: dubbo-system -spec: - externalTrafficPolicy: Local - ports: - - name: status-port - port: 15021 - protocol: TCP - targetPort: 15021 - - name: http2 - port: 80 - protocol: TCP - targetPort: 8080 - - name: https - port: 443 - protocol: TCP - targetPort: 8443 - selector: - app: istio-ingressgateway - istio: ingressgateway - type: LoadBalancer ---- diff --git a/operator/cmd/mesh/testdata/manifest-generate/output/install_package_path.golden.yaml b/operator/cmd/mesh/testdata/manifest-generate/output/install_package_path.golden.yaml deleted file mode 100644 index bceee5fe8..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/output/install_package_path.golden.yaml +++ /dev/null @@ -1,146 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - istio: pilot - release: istio -spec: - strategy: - rollingUpdate: - maxSurge: 100% - maxUnavailable: 25% - selector: - matchLabels: - istio: pilot - template: - metadata: - labels: - app: istiod - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - sidecar.istio.io/inject: "false" - operator.istio.io/component: "Pilot" - istio: pilot - annotations: - prometheus.io/port: "15014" - prometheus.io/scrape: "true" - sidecar.istio.io/inject: "false" - spec: - serviceAccountName: istiod - securityContext: - fsGroup: 1337 - containers: - - name: discovery - image: "docker.io/istio/dubbo-pilot:1.1.4" - args: - - "discovery" - - --monitoringAddr=:15014 - - --log_output_level=default:info - - --domain - - cluster.local - - --keepaliveMaxServerConnectionAge - - "30m" - ports: - - containerPort: 8080 - protocol: TCP - - containerPort: 15010 - protocol: TCP - - containerPort: 15017 - protocol: TCP - readinessProbe: - httpGet: - path: /ready - port: 8080 - initialDelaySeconds: 1 - periodSeconds: 3 - timeoutSeconds: 5 - env: - - name: REVISION - value: "default" - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.serviceAccountName - - name: KUBECONFIG - value: /var/run/secrets/remote/config - - name: PILOT_TRACE_SAMPLING - value: "1" - - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_OUTBOUND - value: "true" - - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_INBOUND - value: "true" - - name: ISTIOD_ADDR - value: istiod.dubbo-system.svc:15012 - - name: PILOT_ENABLE_ANALYSIS - value: "false" - - name: CLUSTER_ID - value: "Kubernetes" - resources: - requests: - cpu: 500m - memory: 2048Mi - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsUser: 1337 - runAsGroup: 1337 - runAsNonRoot: true - capabilities: - drop: - - ALL - volumeMounts: - - name: istio-token - mountPath: /var/run/secrets/tokens - readOnly: true - - name: local-certs - mountPath: /var/run/secrets/istio-dns - - name: cacerts - mountPath: /etc/cacerts - readOnly: true - - name: istio-kubeconfig - mountPath: /var/run/secrets/remote - readOnly: true - volumes: - # Technically not needed on this pod - but it helps debugging/testing SDS - # Should be removed after everything works. - - emptyDir: - medium: Memory - name: local-certs - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - # Optional: user-generated root - - name: cacerts - secret: - secretName: cacerts - optional: true - - name: istio-kubeconfig - secret: - secretName: istio-kubeconfig - optional: true ---- diff --git a/operator/cmd/mesh/testdata/manifest-generate/output/pilot_default.golden.yaml b/operator/cmd/mesh/testdata/manifest-generate/output/pilot_default.golden.yaml deleted file mode 100644 index b958ba0b5..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/output/pilot_default.golden.yaml +++ /dev/null @@ -1,3048 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - release: istio ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istio-reader-clusterrole-dubbo-system - labels: - app: istio-reader - release: istio -rules: - - apiGroups: - - "config.istio.io" - - "security.istio.io" - - "networking.istio.io" - - "authentication.istio.io" - - "rbac.istio.io" - resources: ["*"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["endpoints", "pods", "services", "nodes", "replicationcontrollers", "namespaces", "secrets"] - verbs: ["get", "list", "watch"] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list" ] - resources: [ "workloadentries" ] - - apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "list", "watch"] - - apiGroups: ["discovery.k8s.io"] - resources: ["endpointslices"] - verbs: ["get", "list", "watch"] - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceexports"] - verbs: ["get", "list", "watch", "create", "delete"] - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceimports"] - verbs: ["get", "list", "watch"] - - apiGroups: ["apps"] - resources: ["replicasets"] - verbs: ["get", "list", "watch"] - - apiGroups: ["authentication.k8s.io"] - resources: ["tokenreviews"] - verbs: ["create"] - - apiGroups: ["authorization.k8s.io"] - resources: ["subjectaccessreviews"] - verbs: ["create"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istiod-clusterrole-dubbo-system - labels: - app: istiod - release: istio -rules: - # sidecar injection controller - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["mutatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update", "patch"] - - # configuration validation webhook controller - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["validatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update"] - - # istio configuration - # removing CRD permissions can break older versions of Istio running alongside this control plane (https://github.com/istio/istio/issues/29382) - # please proceed with caution - - apiGroups: ["config.istio.io", "security.istio.io", "networking.istio.io", "authentication.istio.io", "rbac.istio.io", "telemetry.istio.io", "extensions.istio.io"] - verbs: ["get", "watch", "list"] - resources: ["*"] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "workloadentries" ] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "workloadentries/status" ] - - # auto-detect installed CRD definitions - - apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "list", "watch"] - - # discovery and routing - - apiGroups: [""] - resources: ["pods", "nodes", "services", "namespaces", "endpoints"] - verbs: ["get", "list", "watch"] - - apiGroups: ["discovery.k8s.io"] - resources: ["endpointslices"] - verbs: ["get", "list", "watch"] - - # ingress controller - - apiGroups: ["networking.k8s.io"] - resources: ["ingresses", "ingressclasses"] - verbs: ["get", "list", "watch"] - - apiGroups: ["networking.k8s.io"] - resources: ["ingresses/status"] - verbs: ["*"] - - # required for CA's namespace controller - - apiGroups: [""] - resources: ["configmaps"] - verbs: ["create", "get", "list", "watch", "update"] - - # Istiod and bootstrap. - - apiGroups: ["certificates.k8s.io"] - resources: - - "certificatesigningrequests" - - "certificatesigningrequests/approval" - - "certificatesigningrequests/status" - verbs: ["update", "create", "get", "delete", "watch"] - - apiGroups: ["certificates.k8s.io"] - resources: - - "signers" - resourceNames: - - "kubernetes.io/legacy-unknown" - verbs: ["approve"] - - # Used by Istiod to verify the JWT tokens - - apiGroups: ["authentication.k8s.io"] - resources: ["tokenreviews"] - verbs: ["create"] - - # Used by Istiod to verify gateway SDS - - apiGroups: ["authorization.k8s.io"] - resources: ["subjectaccessreviews"] - verbs: ["create"] - - # Use for Kubernetes Service APIs - - apiGroups: ["networking.x-k8s.io", "gateway.networking.k8s.io"] - resources: ["*"] - verbs: ["get", "watch", "list"] - - apiGroups: ["networking.x-k8s.io", "gateway.networking.k8s.io"] - resources: ["*"] # TODO: should be on just */status but wildcard is not supported - verbs: ["update", "patch"] - - apiGroups: ["gateway.networking.k8s.io"] - resources: ["gatewayclasses"] - verbs: ["create", "update", "patch", "delete"] - - # Needed for multicluster secret reading, possibly ingress certs in the future - - apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "watch", "list"] - - # Used for MCS serviceexport management - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceexports"] - verbs: [ "get", "watch", "list", "create", "delete"] - - # Used for MCS serviceimport management - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceimports"] - verbs: ["get", "watch", "list"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istiod-gateway-controller-dubbo-system - labels: - app: istiod - release: istio -rules: - - apiGroups: ["apps"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "deployments" ] - - apiGroups: [""] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "services" ] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istio-reader-clusterrole-dubbo-system - labels: - app: istio-reader - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istio-reader-clusterrole-dubbo-system -subjects: - - kind: ServiceAccount - name: istio-reader-service-account - namespace: dubbo-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istiod-clusterrole-dubbo-system - labels: - app: istiod - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istiod-clusterrole-dubbo-system -subjects: - - kind: ServiceAccount - name: istiod - namespace: dubbo-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istiod-gateway-controller-dubbo-system - labels: - app: istiod - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istiod-gateway-controller-dubbo-system -subjects: -- kind: ServiceAccount - name: istiod - namespace: dubbo-system ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - name: istio-validator-dubbo-system - labels: - app: istiod - release: istio - istio: istiod - istio.io/rev: default -webhooks: - # Webhook handling per-revision validation. Mostly here so we can determine whether webhooks - # are rejecting invalid configs on a per-revision basis. - - name: rev.validation.istio.io - clientConfig: - # Should change from base but cannot for API compat - service: - name: istiod - namespace: dubbo-system - path: "/validate" - rules: - - operations: - - CREATE - - UPDATE - apiGroups: - - security.istio.io - - networking.istio.io - - telemetry.istio.io - - extensions.istio.io - apiVersions: - - "*" - resources: - - "*" - # Fail open until the validation webhook is ready. The webhook controller - # will update this to `Fail` and patch in the `caBundle` when the webhook - # endpoint is ready. - failurePolicy: Ignore - sideEffects: None - admissionReviewVersions: ["v1beta1", "v1"] - objectSelector: - matchExpressions: - - key: istio.io/rev - operator: In - values: - - "default" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.11 - namespace: istio-control - labels: - istio.io/rev: default -spec: - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.12 - namespace: istio-control - labels: - istio.io/rev: default -spec: - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.13 - namespace: istio-control - labels: - istio.io/rev: default -spec: - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.14 - namespace: istio-control - labels: - istio.io/rev: default -spec: - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.15 - namespace: istio-control - labels: - istio.io/rev: default -spec: - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.11 - namespace: istio-control - labels: - istio.io/rev: default -spec: - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: tcp_stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.12 - namespace: istio-control - labels: - istio.io/rev: default -spec: - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: tcp_stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.13 - namespace: istio-control - labels: - istio.io/rev: default -spec: - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: tcp_stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.14 - namespace: istio-control - labels: - istio.io/rev: default -spec: - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: tcp_stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.15 - namespace: istio-control - labels: - istio.io/rev: default -spec: - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: tcp_stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: istio - namespace: dubbo-system - labels: - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - release: istio -data: - - # Configuration file for the mesh networks to be used by the Split Horizon EDS. - meshNetworks: |- - networks: {} - - mesh: |- - defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 - enablePrometheusMerge: true - rootNamespace: istio-control - trustDomain: cluster.local ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: istio-sidecar-injector - namespace: dubbo-system - labels: - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - release: istio -data: - - values: |- - { - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": false, - "externalIstiod": false, - "hub": "docker.io/istio", - "imagePullPolicy": "", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "1.1.4", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "enabled": false - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } - } - - # To disable injection: use omitSidecarInjectorConfigMap, which disables the webhook patching - # and istiod webhook functionality. - # - # New fields should not use Values - it is a 'primary' config object, users should be able - # to fine tune it or use it with kube-inject. - config: |- - # defaultTemplates defines the default template to use for pods that do not explicitly specify a template - defaultTemplates: [sidecar] - policy: enabled - alwaysInjectSelector: - [] - neverInjectSelector: - [] - injectedAnnotations: - template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" - templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - name: istio-sidecar-injector - labels: - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - app: sidecar-injector - release: istio -webhooks: -- name: rev.namespace.sidecar-injector.istio.io - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: "/inject" - port: 443 - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] - namespaceSelector: - matchExpressions: - - key: istio.io/rev - operator: In - values: - - "default" - - key: istio-injection - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" -- name: rev.object.sidecar-injector.istio.io - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: "/inject" - port: 443 - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] - namespaceSelector: - matchExpressions: - - key: istio.io/rev - operator: DoesNotExist - - key: istio-injection - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" - - key: istio.io/rev - operator: In - values: - - "default" -- name: namespace.sidecar-injector.istio.io - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: "/inject" - port: 443 - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: In - values: - - enabled - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" -- name: object.sidecar-injector.istio.io - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: "/inject" - port: 443 - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: DoesNotExist - - key: istio.io/rev - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: In - values: - - "true" - - key: istio.io/rev - operator: DoesNotExist ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - istio: pilot - release: istio -spec: - strategy: - rollingUpdate: - maxSurge: 100% - maxUnavailable: 25% - selector: - matchLabels: - istio: pilot - template: - metadata: - labels: - app: istiod - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - sidecar.istio.io/inject: "false" - operator.istio.io/component: "Pilot" - istio: pilot - annotations: - prometheus.io/port: "15014" - prometheus.io/scrape: "true" - sidecar.istio.io/inject: "false" - spec: - serviceAccountName: istiod - securityContext: - fsGroup: 1337 - containers: - - name: discovery - image: "docker.io/istio/dubbo-pilot:1.1.4" - args: - - "discovery" - - --monitoringAddr=:15014 - - --log_output_level=default:info - - --domain - - cluster.local - - --keepaliveMaxServerConnectionAge - - "30m" - ports: - - containerPort: 8080 - protocol: TCP - - containerPort: 15010 - protocol: TCP - - containerPort: 15017 - protocol: TCP - readinessProbe: - httpGet: - path: /ready - port: 8080 - initialDelaySeconds: 1 - periodSeconds: 3 - timeoutSeconds: 5 - env: - - name: REVISION - value: "default" - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.serviceAccountName - - name: KUBECONFIG - value: /var/run/secrets/remote/config - - name: PILOT_TRACE_SAMPLING - value: "1" - - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_OUTBOUND - value: "true" - - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_INBOUND - value: "true" - - name: ISTIOD_ADDR - value: istiod.dubbo-system.svc:15012 - - name: PILOT_ENABLE_ANALYSIS - value: "false" - - name: CLUSTER_ID - value: "Kubernetes" - resources: - requests: - cpu: 500m - memory: 2048Mi - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsUser: 1337 - runAsGroup: 1337 - runAsNonRoot: true - capabilities: - drop: - - ALL - volumeMounts: - - name: istio-token - mountPath: /var/run/secrets/tokens - readOnly: true - - name: local-certs - mountPath: /var/run/secrets/istio-dns - - name: cacerts - mountPath: /etc/cacerts - readOnly: true - - name: istio-kubeconfig - mountPath: /var/run/secrets/remote - readOnly: true - volumes: - # Technically not needed on this pod - but it helps debugging/testing SDS - # Should be removed after everything works. - - emptyDir: - medium: Memory - name: local-certs - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - # Optional: user-generated root - - name: cacerts - secret: - secretName: cacerts - optional: true - - name: istio-kubeconfig - secret: - secretName: istio-kubeconfig - optional: true ---- -apiVersion: policy/v1beta1 -kind: PodDisruptionBudget -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - release: istio - istio: pilot -spec: - minAvailable: 1 - selector: - matchLabels: - app: istiod - istio: pilot ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - release: istio -rules: -- apiGroups: ["networking.istio.io"] - verbs: ["create"] - resources: ["gateways"] - -- apiGroups: [""] - resources: ["secrets"] - # TODO lock this down to istio-ca-cert if not using the DNS cert mesh config - verbs: ["create", "get", "watch", "list", "update", "delete"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: istiod -subjects: - - kind: ServiceAccount - name: istiod - namespace: dubbo-system ---- -apiVersion: autoscaling/v2beta2 -kind: HorizontalPodAutoscaler -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" -spec: - maxReplicas: 5 - minReplicas: 1 - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: istiod - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: 80 ---- -apiVersion: v1 -kind: Service -metadata: - name: istiod - namespace: dubbo-system - labels: - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - app: istiod - istio: pilot - release: istio -spec: - ports: - - port: 15010 - name: grpc-xds # plaintext - protocol: TCP - - port: 15012 - name: https-dns # mTLS with k8s-signed cert - protocol: TCP - - port: 443 - name: https-webhook # validation and injection - targetPort: 15017 - protocol: TCP - - port: 15014 - name: http-monitoring # prometheus stats - protocol: TCP - selector: - app: istiod - # Label used by the 'default' service. For versioned deployments we match with app and version. - # This avoids default deployment picking the canary - istio: pilot ---- diff --git a/operator/cmd/mesh/testdata/manifest-generate/output/pilot_disable_tracing.golden.yaml b/operator/cmd/mesh/testdata/manifest-generate/output/pilot_disable_tracing.golden.yaml deleted file mode 100644 index 02b9634e1..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/output/pilot_disable_tracing.golden.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: istio - namespace: dubbo-system - labels: - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - release: istio -data: - - # Configuration file for the mesh networks to be used by the Split Horizon EDS. - meshNetworks: |- - networks: {} - - mesh: |- - defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: {} - enablePrometheusMerge: true - rootNamespace: dubbo-system - trustDomain: cluster.local ---- diff --git a/operator/cmd/mesh/testdata/manifest-generate/output/pilot_k8s_settings.golden.yaml b/operator/cmd/mesh/testdata/manifest-generate/output/pilot_k8s_settings.golden.yaml deleted file mode 100644 index 984c7edb1..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/output/pilot_k8s_settings.golden.yaml +++ /dev/null @@ -1,177 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: istiod - install.operator.istio.io/owning-resource: unknown - istio: pilot - istio.io/rev: default - operator.istio.io/component: Pilot - release: istio - name: istiod - namespace: istio-control -spec: - selector: - matchLabels: - istio: pilot - strategy: - rollingUpdate: - maxSurge: 100% - maxUnavailable: 25% - template: - metadata: - annotations: - prometheus.io/port: "15014" - prometheus.io/scrape: "true" - sidecar.istio.io/inject: "false" - labels: - app: istiod - install.operator.istio.io/owning-resource: unknown - istio: pilot - istio.io/rev: default - operator.istio.io/component: Pilot - sidecar.istio.io/inject: "false" - spec: - containers: - - args: - - discovery - - --monitoringAddr=:15014 - - --log_output_level=default:info - - --domain - - cluster.local - - --keepaliveMaxServerConnectionAge - - 30m - env: - - name: REVISION - value: default - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: new.path - - name: GODEBUG - value: gctrace=111 - - name: NEW_VAR - value: new_value - - name: POD_NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.serviceAccountName - - name: KUBECONFIG - value: /var/run/secrets/remote/config - - name: PILOT_TRACE_SAMPLING - value: "1" - - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_OUTBOUND - value: "true" - - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_INBOUND - value: "true" - - name: ISTIOD_ADDR - value: istiod.istio-control.svc:15012 - - name: PILOT_ENABLE_ANALYSIS - value: "false" - - name: CLUSTER_ID - value: Kubernetes - image: docker.io/istio/dubbo-pilot:1.1.4 - name: discovery - ports: - - containerPort: 8080 - protocol: TCP - - containerPort: 15010 - protocol: TCP - - containerPort: 15017 - protocol: TCP - readinessProbe: - httpGet: - path: /ready - port: 8080 - initialDelaySeconds: 555 - periodSeconds: 3 - timeoutSeconds: 5 - resources: - requests: - cpu: 500m - memory: 999Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/tokens - name: istio-token - readOnly: true - - mountPath: /var/run/secrets/istio-dns - name: local-certs - - mountPath: /etc/cacerts - name: cacerts - readOnly: true - - mountPath: /var/run/secrets/remote - name: istio-kubeconfig - readOnly: true - nodeSelector: - master: "true" - securityContext: - fsGroup: 1337 - serviceAccountName: istiod - volumes: - - emptyDir: - medium: Memory - name: local-certs - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - name: cacerts - secret: - optional: true - secretName: cacerts - - name: istio-kubeconfig - secret: - optional: true - secretName: istio-kubeconfig ---- - - -apiVersion: autoscaling/v2beta2 -kind: HorizontalPodAutoscaler -metadata: - labels: - app: istiod - install.operator.istio.io/owning-resource: unknown - istio.io/rev: default - operator.istio.io/component: Pilot - release: istio - name: istiod - namespace: istio-control -spec: - maxReplicas: 333 - metrics: - - resource: - name: cpu - target: - averageUtilization: 444 - type: Utilization - type: Resource - minReplicas: 1 - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: istio-pilot ---- diff --git a/operator/cmd/mesh/testdata/manifest-generate/output/pilot_merge_meshconfig.golden.yaml b/operator/cmd/mesh/testdata/manifest-generate/output/pilot_merge_meshconfig.golden.yaml deleted file mode 100644 index 218fa0dbb..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/output/pilot_merge_meshconfig.golden.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: istio - namespace: dubbo-system - labels: - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - release: istio -data: - - # Configuration file for the mesh networks to be used by the Split Horizon EDS. - meshNetworks: |- - networks: {} - - mesh: |- - accessLogFormat: | - { - "key": "val" - } - defaultConfig: - controlPlaneAuthPolicy: NONE - discoveryAddress: my-discovery:123 - drainDuration: 12s - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 - enablePrometheusMerge: true - outboundTrafficPolicy: - mode: REGISTRY_ONLY - rootNamespace: istio-control - trustDomain: cluster.local ---- diff --git a/operator/cmd/mesh/testdata/manifest-generate/output/pilot_override_kubernetes.golden.yaml b/operator/cmd/mesh/testdata/manifest-generate/output/pilot_override_kubernetes.golden.yaml deleted file mode 100644 index 9a9accca6..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/output/pilot_override_kubernetes.golden.yaml +++ /dev/null @@ -1,337 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - app: istio-reader - release: istio - name: istio-reader-dubbo-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istio-reader-dubbo-system -subjects: -- kind: ServiceAccount - name: my-service-role - namespace: dubbo-system ---- - - -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - labels: - app: sidecar-injector - install.operator.istio.io/owning-resource: unknown - istio.io/rev: default - operator.istio.io/component: Pilot - release: istio - name: istio-sidecar-injector-istio-control -webhooks: -- admissionReviewVersions: - - v1beta1 - - v1 - clientConfig: - service: - name: istiod - namespace: istio-control - path: /inject - port: 443 - failurePolicy: Fail - name: rev.namespace.sidecar-injector.istio.io - namespaceSelector: - matchExpressions: - - key: istio.io/rev - operator: In - values: - - default - - key: istio-injection - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" - rules: - - apiGroups: - - "" - apiVersions: - - v1 - operations: - - CREATE - resources: - - pods - sideEffects: None -- admissionReviewVersions: - - v1beta1 - - v1 - clientConfig: - service: - name: istiod - namespace: istio-control - path: /inject - port: 443 - failurePolicy: Fail - name: rev.object.sidecar-injector.istio.io - namespaceSelector: - matchExpressions: - - key: istio.io/rev - operator: DoesNotExist - - key: istio-injection - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" - - key: istio.io/rev - operator: In - values: - - default - rules: - - apiGroups: - - "" - apiVersions: - - v1 - operations: - - CREATE - resources: - - pods - sideEffects: None -- admissionReviewVersions: - - v1beta1 - - v1 - clientConfig: - service: - name: foo - namespace: istio-control - path: /inject - port: 443 - failurePolicy: Fail - name: namespace.sidecar-injector.istio.io - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: In - values: - - enabled - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" - rules: - - apiGroups: - - "" - apiVersions: - - v1 - operations: - - CREATE - resources: - - pods - sideEffects: None -- admissionReviewVersions: - - v1beta1 - - v1 - clientConfig: - service: - name: istiod - namespace: istio-control - path: /inject - port: 443 - failurePolicy: Fail - name: object.sidecar-injector.istio.io - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: DoesNotExist - - key: istio.io/rev - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: In - values: - - "true" - - key: istio.io/rev - operator: DoesNotExist - rules: - - apiGroups: - - "" - apiVersions: - - v1 - operations: - - CREATE - resources: - - pods - sideEffects: None ---- - - -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: istiod - install.operator.istio.io/owning-resource: unknown - istio: pilot - istio.io/rev: default - operator.istio.io/component: Pilot - release: istio - name: istiod - namespace: istio-control -spec: - selector: - matchLabels: - istio: pilot - strategy: - rollingUpdate: - maxSurge: 100% - maxUnavailable: 25% - template: - metadata: - annotations: - prometheus.io/port: "15014" - prometheus.io/scrape: "true" - sidecar.istio.io/inject: "false" - labels: - app: istiod - install.operator.istio.io/owning-resource: unknown - istio: pilot - istio.io/rev: default - operator.istio.io/component: Pilot - sidecar.istio.io/inject: "false" - spec: - containers: - - args: - - discovery - - --monitoringAddr=:15014 - - --log_output_level=default:info - - --domain - - cluster.local - - --keepaliveMaxServerConnectionAge - - 60m - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - apiVersion: v2 - fieldPath: metadata.myPath - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.serviceAccountName - - name: KUBECONFIG - value: /var/run/secrets/remote/config - - name: PILOT_TRACE_SAMPLING - value: "1" - - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_OUTBOUND - value: "true" - - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_INBOUND - value: "true" - - name: ISTIOD_ADDR - value: istiod.istio-control.svc:15012 - - name: PILOT_ENABLE_ANALYSIS - value: "false" - - name: CLUSTER_ID - value: Kubernetes - image: docker.io/istio/dubbo-pilot:1.1.4 - name: discovery - ports: - - containerPort: 1234 - protocol: TCP - - containerPort: 15010 - protocol: TCP - - containerPort: 15017 - protocol: TCP - readinessProbe: - httpGet: - path: /ready - port: 8080 - initialDelaySeconds: 1 - periodSeconds: 3 - timeoutSeconds: 5 - resources: - requests: - cpu: 123m - memory: 2048Mi - volumeMounts: - - mountPath: /var/run/secrets/tokens - name: istio-token - readOnly: true - - mountPath: /var/run/secrets/istio-dns - name: local-certs - - mountPath: /etc/cacerts - name: cacerts - readOnly: true - - mountPath: /var/run/secrets/remote - name: istio-kubeconfig - readOnly: true - securityContext: - fsGroup: 1337 - serviceAccountName: istiod - volumes: - - emptyDir: - medium: Memory - name: local-certs - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - name: cacerts - secret: - optional: true - secretName: cacerts - - name: istio-kubeconfig - secret: - optional: true - secretName: istio-kubeconfig ---- - - -apiVersion: v1 -kind: Service -metadata: - labels: - app: istiod - install.operator.istio.io/owning-resource: unknown - istio: pilot - istio.io/rev: default - operator.istio.io/component: Pilot - release: istio - name: istiod - namespace: istio-control -spec: - ports: - - name: grpc-xds - port: 15010 - protocol: TCP - - name: https-dns - port: 11111 - protocol: TCP - - name: https-webhook - port: 443 - protocol: TCP - targetPort: 15017 - - name: http-monitoring - port: 15014 - protocol: TCP - selector: - app: istiod - istio: pilot ---- diff --git a/operator/cmd/mesh/testdata/manifest-generate/output/pilot_override_values.golden.yaml b/operator/cmd/mesh/testdata/manifest-generate/output/pilot_override_values.golden.yaml deleted file mode 100644 index abc77dacf..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/output/pilot_override_values.golden.yaml +++ /dev/null @@ -1,173 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: istiod - install.operator.istio.io/owning-resource: unknown - istio: pilot - istio.io/rev: default - operator.istio.io/component: Pilot - release: istio - name: istiod - namespace: istio-control -spec: - selector: - matchLabels: - istio: pilot - strategy: - rollingUpdate: - maxSurge: 100% - maxUnavailable: 30% - template: - metadata: - annotations: - prometheus.io/port: "15014" - prometheus.io/scrape: "true" - sidecar.istio.io/inject: "false" - labels: - app: istiod - install.operator.istio.io/owning-resource: unknown - istio: pilot - istio.io/rev: default - operator.istio.io/component: Pilot - sidecar.istio.io/inject: "false" - spec: - containers: - - args: - - discovery - - --monitoringAddr=:15014 - - --log_output_level=default:info - - --domain - - cluster.local - - --keepaliveMaxServerConnectionAge - - 30m - env: - - name: REVISION - value: default - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.serviceAccountName - - name: KUBECONFIG - value: /var/run/secrets/remote/config - - name: PILOT_TRACE_SAMPLING - value: "1" - - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_OUTBOUND - value: "true" - - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_INBOUND - value: "true" - - name: ISTIOD_ADDR - value: istiod.istio-control.svc:15012 - - name: PILOT_ENABLE_ANALYSIS - value: "false" - - name: CLUSTER_ID - value: Kubernetes - image: docker.io/istio/dubbo-pilot:1.1.4 - name: discovery - ports: - - containerPort: 8080 - protocol: TCP - - containerPort: 15010 - protocol: TCP - - containerPort: 15017 - protocol: TCP - readinessProbe: - httpGet: - path: /ready - port: 8080 - initialDelaySeconds: 1 - periodSeconds: 3 - timeoutSeconds: 5 - resources: - requests: - cpu: 222m - memory: 333Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/tokens - name: istio-token - readOnly: true - - mountPath: /var/run/secrets/istio-dns - name: local-certs - - mountPath: /etc/cacerts - name: cacerts - readOnly: true - - mountPath: /var/run/secrets/remote - name: istio-kubeconfig - readOnly: true - nodeSelector: - node-name: test - securityContext: - fsGroup: 1337 - serviceAccountName: istiod - volumes: - - emptyDir: - medium: Memory - name: local-certs - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - name: cacerts - secret: - optional: true - secretName: cacerts - - name: istio-kubeconfig - secret: - optional: true - secretName: istio-kubeconfig ---- - - -apiVersion: autoscaling/v2beta2 -kind: HorizontalPodAutoscaler -metadata: - labels: - app: istiod - install.operator.istio.io/owning-resource: unknown - istio.io/rev: default - operator.istio.io/component: Pilot - release: istio - name: istiod - namespace: istio-control -spec: - maxReplicas: 8 - metrics: - - resource: - name: cpu - target: - averageUtilization: 80 - type: Utilization - type: Resource - minReplicas: 2 - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: istiod ---- diff --git a/operator/cmd/mesh/testdata/manifest-generate/output/sidecar_template.golden.yaml b/operator/cmd/mesh/testdata/manifest-generate/output/sidecar_template.golden.yaml deleted file mode 100644 index 5431b91b1..000000000 --- a/operator/cmd/mesh/testdata/manifest-generate/output/sidecar_template.golden.yaml +++ /dev/null @@ -1,1205 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: istio-sidecar-injector - namespace: dubbo-system - labels: - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - release: istio -data: - - values: |- - { - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": true, - "externalIstiod": false, - "hub": "gcr.io/istio-testing", - "imagePullPolicy": "", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "enabled": false - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [ - "sidecar", - "credential-volume" - ], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": { - "credential-volume": "spec:\n volumes:\n - name: application-credentials\n secret:\n secretName: secret\n containers:\n - name: istio-proxy\n volumeMounts:\n - name: application-credentials\n mountPath: /etc/istio/application-credentials\n readOnly: true\n" - } - } - } - - # To disable injection: use omitSidecarInjectorConfigMap, which disables the webhook patching - # and istiod webhook functionality. - # - # New fields should not use Values - it is a 'primary' config object, users should be able - # to fine tune it or use it with kube-inject. - config: |- - # defaultTemplates defines the default template to use for pods that do not explicitly specify a template - defaultTemplates: - - sidecar - - credential-volume - policy: enabled - alwaysInjectSelector: - [] - neverInjectSelector: - [] - injectedAnnotations: - template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" - templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - credential-volume: | - spec: - volumes: - - name: application-credentials - secret: - secretName: secret - containers: - - name: istio-proxy - volumeMounts: - - name: application-credentials - mountPath: /etc/istio/application-credentials - readOnly: true ---- diff --git a/operator/cmd/mesh/testdata/manifest-versions/input/versions.yaml b/operator/cmd/mesh/testdata/manifest-versions/input/versions.yaml deleted file mode 100644 index 95c0a775c..000000000 --- a/operator/cmd/mesh/testdata/manifest-versions/input/versions.yaml +++ /dev/null @@ -1,38 +0,0 @@ -- operatorVersion: 1.3.0 - supportedIstioVersions: 1.3.0 - recommendedIstioVersions: 1.3.0 -- operatorVersion: 1.3.1 - supportedIstioVersions: ">=1.3.0,<=1.3.1" - recommendedIstioVersions: 1.3.1 -- operatorVersion: 1.3.2 - supportedIstioVersions: ">=1.3.0,<=1.3.2" - recommendedIstioVersions: 1.3.2 -- operatorVersion: 1.3.3 - supportedIstioVersions: ">=1.3.0,<=1.3.3" - recommendedIstioVersions: 1.3.3 -- operatorVersion: 1.3.4 - supportedIstioVersions: ">=1.3.0,<=1.3.4" - recommendedIstioVersions: 1.3.4 -- operatorVersion: 1.3.5 - supportedIstioVersions: ">=1.3.0,<=1.3.5" - recommendedIstioVersions: 1.3.5 -- operatorVersion: 1.3.6 - supportedIstioVersions: ">=1.3.0,<=1.3.6" - recommendedIstioVersions: 1.3.6 -- operatorVersion: 1.3.7 - operatorVersionRange: ">=1.3.7,<1.4.0" - supportedIstioVersions: ">=1.3.0,<1.4.0" - recommendedIstioVersions: 1.3.7 -- operatorVersion: 1.4.0 - supportedIstioVersions: ">=1.3.3, <1.6" - recommendedIstioVersions: 1.4.0 -- operatorVersion: 1.4.1 - supportedIstioVersions: ">=1.3.3, <1.6" - recommendedIstioVersions: 1.4.1 -- operatorVersion: 1.4.2 - supportedIstioVersions: ">=1.3.3, <1.6" - recommendedIstioVersions: 1.4.2 -- operatorVersion: 1.4.3 - operatorVersionRange: ">=1.4.3,<1.5.0" - supportedIstioVersions: ">=1.3.3, <1.6" - recommendedIstioVersions: 1.4.3 diff --git a/operator/cmd/mesh/testdata/operator/output/operator-dump.json b/operator/cmd/mesh/testdata/operator/output/operator-dump.json deleted file mode 100644 index 156398fdb..000000000 --- a/operator/cmd/mesh/testdata/operator/output/operator-dump.json +++ /dev/null @@ -1,187 +0,0 @@ -{ - "apiVersion": "rbac.authorization.k8s.io/v1", - "kind": "ClusterRole", - "metadata": { - "creationTimestamp": null, - "name": "istio-operator" - }, - "rules": [ - { - "apiGroups": [ - "authentication.istio.io" - ], - "resources": [ - "*" - ], - "verbs": [ - "*" - ] - }, - { - "apiGroups": [ - "config.istio.io" - ], - "resources": [ - "*" - ], - "verbs": [ - "*" - ] - }, - { - "apiGroups": [ - "install.istio.io" - ], - "resources": [ - "*" - ], - "verbs": [ - "*" - ] - }, - { - "apiGroups": [ - "networking.istio.io" - ], - "resources": [ - "*" - ], - "verbs": [ - "*" - ] - }, - { - "apiGroups": [ - "security.istio.io" - ], - "resources": [ - "*" - ], - "verbs": [ - "*" - ] - }, - { - "apiGroups": [ - "admissionregistration.k8s.io" - ], - "resources": [ - "mutatingwebhookconfigurations", - "validatingwebhookconfigurations" - ], - "verbs": [ - "*" - ] - }, - { - "apiGroups": [ - "apiextensions.k8s.io" - ], - "resources": [ - "customresourcedefinitions.apiextensions.k8s.io", - "customresourcedefinitions" - ], - "verbs": [ - "*" - ] - }, - { - "apiGroups": [ - "apps", - "extensions" - ], - "resources": [ - "daemonsets", - "deployments", - "deployments/finalizers", - "replicasets" - ], - "verbs": [ - "*" - ] - }, - { - "apiGroups": [ - "autoscaling" - ], - "resources": [ - "horizontalpodautoscalers" - ], - "verbs": [ - "*" - ] - }, - { - "apiGroups": [ - "monitoring.coreos.com" - ], - "resources": [ - "servicemonitors" - ], - "verbs": [ - "get", - "create", - "update" - ] - }, - { - "apiGroups": [ - "policy" - ], - "resources": [ - "poddisruptionbudgets" - ], - "verbs": [ - "*" - ] - }, - { - "apiGroups": [ - "rbac.authorization.k8s.io" - ], - "resources": [ - "clusterrolebindings", - "clusterroles", - "roles", - "rolebindings" - ], - "verbs": [ - "*" - ] - }, - { - "apiGroups": [ - "coordination.k8s.io" - ], - "resources": [ - "leases" - ], - "verbs": [ - "get", - "create", - "update" - ] - }, - { - "apiGroups": [ - "" - ], - "resources": [ - "configmaps", - "endpoints", - "events", - "namespaces", - "pods", - "pods/proxy", - "pods/portforward", - "persistentvolumeclaims", - "secrets", - "services", - "serviceaccounts" - ], - "verbs": [ - "*" - ] - } - ] -} diff --git a/operator/cmd/mesh/testdata/operator/output/operator-dump.yaml b/operator/cmd/mesh/testdata/operator/output/operator-dump.yaml deleted file mode 100644 index 6075bf0d0..000000000 --- a/operator/cmd/mesh/testdata/operator/output/operator-dump.yaml +++ /dev/null @@ -1,263 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - creationTimestamp: null - name: istio-operator -rules: -# istio groups -- apiGroups: - - authentication.istio.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - config.istio.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - install.istio.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - networking.istio.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - security.istio.io - resources: - - '*' - verbs: - - '*' -# k8s groups -- apiGroups: - - admissionregistration.k8s.io - resources: - - mutatingwebhookconfigurations - - validatingwebhookconfigurations - verbs: - - '*' -- apiGroups: - - apiextensions.k8s.io - resources: - - customresourcedefinitions.apiextensions.k8s.io - - customresourcedefinitions - verbs: - - '*' -- apiGroups: - - apps - - extensions - resources: - - daemonsets - - deployments - - deployments/finalizers - - replicasets - verbs: - - '*' -- apiGroups: - - autoscaling - resources: - - horizontalpodautoscalers - verbs: - - '*' -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - get - - create - - update -- apiGroups: - - policy - resources: - - poddisruptionbudgets - verbs: - - '*' -- apiGroups: - - rbac.authorization.k8s.io - resources: - - clusterrolebindings - - clusterroles - - roles - - rolebindings - verbs: - - '*' -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - create - - update -- apiGroups: - - "" - resources: - - configmaps - - endpoints - - events - - namespaces - - pods - - pods/proxy - - pods/portforward - - persistentvolumeclaims - - secrets - - services - - serviceaccounts - verbs: - - '*' ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: istio-operator -subjects: -- kind: ServiceAccount - name: istio-operator - namespace: operator-test-namespace -roleRef: - kind: ClusterRole - name: istio-operator - apiGroup: rbac.authorization.k8s.io ---- - - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - namespace: operator-test-namespace - name: istio-operator -spec: - replicas: 1 - revisionHistoryLimit: 10 - selector: - matchLabels: - name: istio-operator - template: - metadata: - labels: - name: istio-operator - spec: - serviceAccountName: istio-operator - containers: - - name: istio-operator - image: foo.io/istio/dubbo-operator:1.2.3 - command: - - operator - - server - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - imagePullPolicy: IfNotPresent - resources: - limits: - cpu: 200m - memory: 256Mi - requests: - cpu: 50m - memory: 128Mi - env: - - name: WATCH_NAMESPACE - value: "istio-test-namespace1,istio-test-namespace2" - - name: LEADER_ELECTION_NAMESPACE - value: "operator-test-namespace" - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: OPERATOR_NAME - value: "operator-test-namespace" - - name: WAIT_FOR_RESOURCES_TIMEOUT - value: "300s" - - name: REVISION - value: "" ---- -apiVersion: v1 -kind: Service -metadata: - namespace: operator-test-namespace - labels: - name: istio-operator - name: istio-operator -spec: - ports: - - name: http-metrics - port: 8383 - targetPort: 8383 - protocol: TCP - selector: - name: istio-operator ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - namespace: operator-test-namespace - name: istio-operator -imagePullSecrets: -- name: imagePullSecret1 -- name: imagePullSecret2 ---- -# SYNC WITH manifests/charts/base/files -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: istiooperators.install.istio.io - labels: - release: istio -spec: - conversion: - strategy: None - group: install.istio.io - names: - kind: IstioOperator - listKind: IstioOperatorList - plural: istiooperators - singular: istiooperator - shortNames: - - iop - - io - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Istio control plane revision - jsonPath: .spec.revision - name: Revision - type: string - - description: IOP current state - jsonPath: .status.status - name: Status - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - subresources: - status: {} - schema: - openAPIV3Schema: - type: object - x-kubernetes-preserve-unknown-fields: true - served: true - storage: true ---- - diff --git a/operator/cmd/mesh/testdata/operator/output/operator-init.yaml b/operator/cmd/mesh/testdata/operator/output/operator-init.yaml deleted file mode 100644 index 707da22d7..000000000 --- a/operator/cmd/mesh/testdata/operator/output/operator-init.yaml +++ /dev/null @@ -1,259 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - creationTimestamp: null - name: istio-operator -rules: -# istio groups -- apiGroups: - - authentication.istio.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - config.istio.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - install.istio.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - networking.istio.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - security.istio.io - resources: - - '*' - verbs: - - '*' -# k8s groups -- apiGroups: - - admissionregistration.k8s.io - resources: - - mutatingwebhookconfigurations - - validatingwebhookconfigurations - verbs: - - '*' -- apiGroups: - - apiextensions.k8s.io - resources: - - customresourcedefinitions.apiextensions.k8s.io - - customresourcedefinitions - verbs: - - '*' -- apiGroups: - - apps - - extensions - resources: - - daemonsets - - deployments - - deployments/finalizers - - replicasets - verbs: - - '*' -- apiGroups: - - autoscaling - resources: - - horizontalpodautoscalers - verbs: - - '*' -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - get - - create - - update -- apiGroups: - - policy - resources: - - poddisruptionbudgets - verbs: - - '*' -- apiGroups: - - rbac.authorization.k8s.io - resources: - - clusterrolebindings - - clusterroles - - roles - - rolebindings - verbs: - - '*' -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - create - - update -- apiGroups: - - "" - resources: - - configmaps - - endpoints - - events - - namespaces - - pods - - pods/proxy - - pods/portforward - - persistentvolumeclaims - - secrets - - services - - serviceaccounts - verbs: - - '*' ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: istio-operator -subjects: -- kind: ServiceAccount - name: istio-operator - namespace: operator-test-namespace -roleRef: - kind: ClusterRole - name: istio-operator - apiGroup: rbac.authorization.k8s.io ---- - - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - namespace: operator-test-namespace - name: istio-operator -spec: - replicas: 1 - revisionHistoryLimit: 10 - selector: - matchLabels: - name: istio-operator - template: - metadata: - labels: - name: istio-operator - spec: - serviceAccountName: istio-operator - containers: - - name: istio-operator - image: foo.io/istio/dubbo-operator:1.2.3 - command: - - operator - - server - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - imagePullPolicy: IfNotPresent - resources: - limits: - cpu: 200m - memory: 256Mi - requests: - cpu: 50m - memory: 128Mi - env: - - name: WATCH_NAMESPACE - value: "istio-test-namespace1,istio-test-namespace2" - - name: LEADER_ELECTION_NAMESPACE - value: "operator-test-namespace" - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: OPERATOR_NAME - value: "operator-test-namespace" - - name: WAIT_FOR_RESOURCES_TIMEOUT - value: "300s" - - name: REVISION - value: "" ---- -apiVersion: v1 -kind: Service -metadata: - namespace: operator-test-namespace - labels: - name: istio-operator - name: istio-operator -spec: - ports: - - name: http-metrics - port: 8383 - targetPort: 8383 - protocol: TCP - selector: - name: istio-operator ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - namespace: operator-test-namespace - name: istio-operator ---- -# SYNC WITH manifests/charts/base/files -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: istiooperators.install.istio.io - labels: - release: istio -spec: - conversion: - strategy: None - group: install.istio.io - names: - kind: IstioOperator - listKind: IstioOperatorList - plural: istiooperators - singular: istiooperator - shortNames: - - iop - - io - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Istio control plane revision - jsonPath: .spec.revision - name: Revision - type: string - - description: IOP current state - jsonPath: .status.status - name: Status - type: string - - description: 'CreationTimestamp is a timestamp representing the server time - when this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for - lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - subresources: - status: {} - schema: - openAPIV3Schema: - type: object - x-kubernetes-preserve-unknown-fields: true - served: true - storage: true ---- diff --git a/operator/cmd/mesh/testdata/operator/output/operator-remove.yaml b/operator/cmd/mesh/testdata/operator/output/operator-remove.yaml deleted file mode 100644 index d7e1361d7..000000000 --- a/operator/cmd/mesh/testdata/operator/output/operator-remove.yaml +++ /dev/null @@ -1,237 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - creationTimestamp: null - name: istio-operator -rules: -# istio groups -- apiGroups: - - authentication.istio.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - config.istio.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - install.istio.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - networking.istio.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - security.istio.io - resources: - - '*' - verbs: - - '*' -# k8s groups -- apiGroups: - - admissionregistration.k8s.io - resources: - - mutatingwebhookconfigurations - - validatingwebhookconfigurations - verbs: - - '*' -- apiGroups: - - apiextensions.k8s.io - resources: - - customresourcedefinitions.apiextensions.k8s.io - - customresourcedefinitions - verbs: - - '*' -- apiGroups: - - apps - - extensions - resources: - - daemonsets - - deployments - - deployments/finalizers - - ingresses - - replicasets - - statefulsets - verbs: - - '*' -- apiGroups: - - autoscaling - resources: - - horizontalpodautoscalers - verbs: - - '*' -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - get - - create -- apiGroups: - - policy - resources: - - poddisruptionbudgets - verbs: - - '*' -- apiGroups: - - rbac.authorization.k8s.io - resources: - - clusterrolebindings - - clusterroles - - roles - - rolebindings - verbs: - - '*' -- apiGroups: - - "" - resources: - - configmaps - - endpoints - - events - - namespaces - - pods - - persistentvolumeclaims - - secrets - - services - - serviceaccounts - verbs: - - '*' ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: istio-operator -subjects: -- kind: ServiceAccount - name: istio-operator - namespace: operator-test-namespace -roleRef: - kind: ClusterRole - name: istio-operator - apiGroup: rbac.authorization.k8s.io ---- -# SYNC WITH manifests/charts/base/files -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: istiooperators.install.istio.io -spec: - group: install.istio.io - names: - kind: IstioOperator - plural: istiooperators - singular: istiooperator - shortNames: - - iop - scope: Namespaced - subresources: - status: {} - validation: - openAPIV3Schema: - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. - More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. - More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - spec: - description: 'Specification of the desired state of the istio control plane resource. - More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' - type: object - status: - description: 'Status describes each of istio control plane component status at the current time. - 0 means NONE, 1 means UPDATING, 2 means HEALTHY, 3 means ERROR, 4 means RECONCILING. - More info: https://github.com/istio/api/blob/master/operator/v1alpha1/istio.operator.v1alpha1.pb.html & - https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' - type: object - versions: - - name: v1alpha1 - served: true - storage: true ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - namespace: operator-test-namespace - name: istio-operator -spec: - replicas: 1 - selector: - matchLabels: - name: istio-operator - template: - metadata: - labels: - name: istio-operator - spec: - serviceAccountName: istio-operator - containers: - - name: istio-operator - image: foo.io/istio/operator:1.2.3 - command: - - operator - - server - imagePullPolicy: IfNotPresent - resources: - limits: - cpu: 200m - memory: 256Mi - requests: - cpu: 50m - memory: 128Mi - env: - - name: WATCH_NAMESPACE - value: istio-test-namespace - - name: LEADER_ELECTION_NAMESPACE - value: operator-test-namespace - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: OPERATOR_NAME - value: operator-test-namespace ---- -apiVersion: v1 -kind: Namespace -metadata: - name: istio-operator - labels: - istio-operator-managed: Reconcile - istio-injection: disabled ---- -apiVersion: v1 -kind: Service -metadata: - namespace: operator-test-namespace - labels: - name: istio-operator - name: istio-operator -spec: - ports: - - name: http-metrics - port: 8383 - targetPort: 8383 - selector: - name: istio-operator ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - namespace: operator-test-namespace - name: istio-operator ---- diff --git a/operator/cmd/mesh/testdata/profile-dump/input/all_off.yaml b/operator/cmd/mesh/testdata/profile-dump/input/all_off.yaml deleted file mode 100644 index 9883994fb..000000000 --- a/operator/cmd/mesh/testdata/profile-dump/input/all_off.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: default - components: - pilot: - enabled: false - cni: - enabled: false diff --git a/operator/cmd/mesh/testdata/profile-dump/input/config_path.yaml b/operator/cmd/mesh/testdata/profile-dump/input/config_path.yaml deleted file mode 100644 index 9883994fb..000000000 --- a/operator/cmd/mesh/testdata/profile-dump/input/config_path.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - profile: default - components: - pilot: - enabled: false - cni: - enabled: false diff --git a/operator/cmd/mesh/testdata/profile-dump/input/list_path.yaml b/operator/cmd/mesh/testdata/profile-dump/input/list_path.yaml deleted file mode 100644 index 52c047b1d..000000000 --- a/operator/cmd/mesh/testdata/profile-dump/input/list_path.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -metadata: - namespace: dubbo-system -spec: - values: - gateways: - istio-egressgateway: - secretVolumes: - - mountPath: /etc/istio/egressgateway-certs - name: egressgateway-certs diff --git a/operator/cmd/mesh/testdata/profile-dump/input/pilot_plugin_invalid.yaml b/operator/cmd/mesh/testdata/profile-dump/input/pilot_plugin_invalid.yaml deleted file mode 100644 index a997f91ec..000000000 --- a/operator/cmd/mesh/testdata/profile-dump/input/pilot_plugin_invalid.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -metadata: - namespace: dubbo-system -spec: - values: - pilot: - plugins: - - 0: aa - - 1: bb \ No newline at end of file diff --git a/operator/cmd/mesh/testdata/profile-dump/input/pilot_plugin_valid.yaml b/operator/cmd/mesh/testdata/profile-dump/input/pilot_plugin_valid.yaml deleted file mode 100644 index 49be4875d..000000000 --- a/operator/cmd/mesh/testdata/profile-dump/input/pilot_plugin_valid.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -metadata: - namespace: dubbo-system -spec: - values: - pilot: - plugins: - - aa - - bb \ No newline at end of file diff --git a/operator/cmd/mesh/testdata/profile-dump/output/all_off.txt b/operator/cmd/mesh/testdata/profile-dump/output/all_off.txt deleted file mode 100644 index e32678e64..000000000 --- a/operator/cmd/mesh/testdata/profile-dump/output/all_off.txt +++ /dev/null @@ -1,100 +0,0 @@ -components.base.enabled=true -components.cni.enabled=false -components.egressGateways[0].enabled=false -components.egressGateways[0].name="istio-egressgateway" -components.ingressGateways[0].enabled=true -components.ingressGateways[0].name="istio-ingressgateway" -components.istiodRemote.enabled=false -components.pilot.enabled=false -hub="gcr.io/istio-testing" -meshConfig.enablePrometheusMerge=true -profile="default" -tag="latest" -values.base.enableCRDTemplates=false -values.base.validationURL="" -values.defaultRevision="" -values.gateways.istio-egressgateway.autoscaleEnabled=true -values.gateways.istio-egressgateway.name="istio-egressgateway" -values.gateways.istio-egressgateway.secretVolumes[0].mountPath="/etc/istio/egressgateway-certs" -values.gateways.istio-egressgateway.secretVolumes[0].name="egressgateway-certs" -values.gateways.istio-egressgateway.secretVolumes[0].secretName="istio-egressgateway-certs" -values.gateways.istio-egressgateway.secretVolumes[1].mountPath="/etc/istio/egressgateway-ca-certs" -values.gateways.istio-egressgateway.secretVolumes[1].name="egressgateway-ca-certs" -values.gateways.istio-egressgateway.secretVolumes[1].secretName="istio-egressgateway-ca-certs" -values.gateways.istio-egressgateway.type="ClusterIP" -values.gateways.istio-ingressgateway.autoscaleEnabled=true -values.gateways.istio-ingressgateway.name="istio-ingressgateway" -values.gateways.istio-ingressgateway.secretVolumes[0].mountPath="/etc/istio/ingressgateway-certs" -values.gateways.istio-ingressgateway.secretVolumes[0].name="ingressgateway-certs" -values.gateways.istio-ingressgateway.secretVolumes[0].secretName="istio-ingressgateway-certs" -values.gateways.istio-ingressgateway.secretVolumes[1].mountPath="/etc/istio/ingressgateway-ca-certs" -values.gateways.istio-ingressgateway.secretVolumes[1].name="ingressgateway-ca-certs" -values.gateways.istio-ingressgateway.secretVolumes[1].secretName="istio-ingressgateway-ca-certs" -values.gateways.istio-ingressgateway.type="LoadBalancer" -values.global.configValidation=true -values.global.defaultPodDisruptionBudget.enabled=true -values.global.defaultResources.requests.cpu="10m" -values.global.imagePullPolicy="" -values.global.istioNamespace="dubbo-system" -values.global.istiod.enableAnalysis=false -values.global.jwtPolicy="third-party-jwt" -values.global.logAsJson=false -values.global.logging.level="default:info" -values.global.mountMtlsCerts=false -values.global.multiCluster.clusterName="" -values.global.multiCluster.enabled=false -values.global.network="" -values.global.omitSidecarInjectorConfigMap=false -values.global.oneNamespace=false -values.global.operatorManageWebhooks=false -values.global.pilotCertProvider="istiod" -values.global.priorityClassName="" -values.global.proxy.autoInject="enabled" -values.global.proxy.clusterDomain="cluster.local" -values.global.proxy.componentLogLevel="misc:error" -values.global.proxy.enableCoreDump=false -values.global.proxy.excludeIPRanges="" -values.global.proxy.excludeInboundPorts="" -values.global.proxy.excludeOutboundPorts="" -values.global.proxy.image="dubbo-agent" -values.global.proxy.includeIPRanges="*" -values.global.proxy.logLevel="warning" -values.global.proxy.privileged=false -values.global.proxy.readinessFailureThreshold=30 -values.global.proxy.readinessInitialDelaySeconds=1 -values.global.proxy.readinessPeriodSeconds=2 -values.global.proxy.resources.limits.cpu="2000m" -values.global.proxy.resources.limits.memory="1024Mi" -values.global.proxy.resources.requests.cpu="100m" -values.global.proxy.resources.requests.memory="128Mi" -values.global.proxy.statusPort=15020 -values.global.proxy.tracer="zipkin" -values.global.proxy_init.image="dubbo-agent" -values.global.proxy_init.resources.limits.cpu="2000m" -values.global.proxy_init.resources.limits.memory="1024Mi" -values.global.proxy_init.resources.requests.cpu="10m" -values.global.proxy_init.resources.requests.memory="10Mi" -values.global.sds.token.aud="istio-ca" -values.global.sts.servicePort=0 -values.global.useMCP=false -values.istiodRemote.injectionURL="" -values.pilot.autoscaleEnabled=true -values.pilot.autoscaleMax=5 -values.pilot.autoscaleMin=1 -values.pilot.configMap=true -values.pilot.cpu.targetAverageUtilization=80 -values.pilot.enableProtocolSniffingForInbound=true -values.pilot.enableProtocolSniffingForOutbound=true -values.pilot.image="dubbo-pilot" -values.pilot.keepaliveMaxServerConnectionAge="30m" -values.pilot.replicaCount=1 -values.pilot.traceSampling=1 -values.telemetry.enabled=true -values.telemetry.v2.enabled=true -values.telemetry.v2.metadataExchange.wasmEnabled=false -values.telemetry.v2.prometheus.enabled=true -values.telemetry.v2.prometheus.wasmEnabled=false -values.telemetry.v2.stackdriver.enabled=false -values.telemetry.v2.stackdriver.logging=false -values.telemetry.v2.stackdriver.monitoring=false -values.telemetry.v2.stackdriver.topology=false diff --git a/operator/cmd/mesh/testdata/profile-dump/output/all_off.yaml b/operator/cmd/mesh/testdata/profile-dump/output/all_off.yaml deleted file mode 100644 index 48f850622..000000000 --- a/operator/cmd/mesh/testdata/profile-dump/output/all_off.yaml +++ /dev/null @@ -1,162 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - components: - base: - enabled: true - cni: - enabled: false - egressGateways: - - enabled: false - name: istio-egressgateway - ingressGateways: - - enabled: false - name: istio-ingressgateway - istiodRemote: - enabled: false - pilot: - enabled: false - hub: apache - - meshConfig: - defaultConfig: - proxyMetadata: {} - enablePrometheusMerge: true - profile: default - tag: latest - values: - base: - enableCRDTemplates: false - validationURL: "" - defaultRevision: "" - gateways: - istio-egressgateway: - autoscaleEnabled: true - env: {} - name: istio-egressgateway - secretVolumes: - - mountPath: /etc/istio/egressgateway-certs - name: egressgateway-certs - secretName: istio-egressgateway-certs - - mountPath: /etc/istio/egressgateway-ca-certs - name: egressgateway-ca-certs - secretName: istio-egressgateway-ca-certs - type: ClusterIP - istio-ingressgateway: - autoscaleEnabled: true - env: {} - name: istio-ingressgateway - secretVolumes: - - mountPath: /etc/istio/ingressgateway-certs - name: ingressgateway-certs - secretName: istio-ingressgateway-certs - - mountPath: /etc/istio/ingressgateway-ca-certs - name: ingressgateway-ca-certs - secretName: istio-ingressgateway-ca-certs - type: LoadBalancer - global: - configValidation: true - defaultNodeSelector: {} - defaultPodDisruptionBudget: - enabled: true - defaultResources: - requests: - cpu: 10m - imagePullPolicy: "" - imagePullSecrets: [] - istioNamespace: dubbo-system - istiod: - enableAnalysis: false - jwtPolicy: third-party-jwt - logAsJson: false - logging: - level: default:info - meshNetworks: {} - mountMtlsCerts: false - multiCluster: - clusterName: "" - enabled: false - network: "" - omitSidecarInjectorConfigMap: false - oneNamespace: false - operatorManageWebhooks: false - pilotCertProvider: istiod - priorityClassName: "" - proxy: - autoInject: enabled - clusterDomain: cluster.local - componentLogLevel: misc:error - enableCoreDump: false - excludeIPRanges: "" - excludeInboundPorts: "" - excludeOutboundPorts: "" - image: dubbo-agent - includeIPRanges: '*' - logLevel: warning - privileged: false - readinessFailureThreshold: 30 - readinessInitialDelaySeconds: 1 - readinessPeriodSeconds: 2 - resources: - limits: - cpu: 2000m - memory: 1024Mi - requests: - cpu: 100m - memory: 128Mi - statusPort: 15020 - tracer: zipkin - proxy_init: - image: dubbo-agent - resources: - limits: - cpu: 2000m - memory: 1024Mi - requests: - cpu: 10m - memory: 10Mi - sds: - token: - aud: istio-ca - sts: - servicePort: 0 - tracer: - datadog: {} - lightstep: {} - stackdriver: {} - zipkin: {} - useMCP: false - istiodRemote: - injectionURL: "" - pilot: - autoscaleEnabled: true - autoscaleMax: 5 - autoscaleMin: 1 - configMap: true - cpu: - targetAverageUtilization: 80 - enableProtocolSniffingForInbound: true - enableProtocolSniffingForOutbound: true - env: {} - image: dubbo-pilot - keepaliveMaxServerConnectionAge: 30m - nodeSelector: {} - podLabels: {} - replicaCount: 1 - traceSampling: 1 - telemetry: - enabled: true - v2: - enabled: true - metadataExchange: - wasmEnabled: false - prometheus: - enabled: true - wasmEnabled: false - stackdriver: - configOverride: {} - enabled: false - logging: false - monitoring: false - topology: false - diff --git a/operator/cmd/mesh/testdata/profile-dump/output/config_path.txt b/operator/cmd/mesh/testdata/profile-dump/output/config_path.txt deleted file mode 100644 index 34137cc36..000000000 --- a/operator/cmd/mesh/testdata/profile-dump/output/config_path.txt +++ /dev/null @@ -1,8 +0,0 @@ -base.enabled=true -cni.enabled=false -egressGateways[0].enabled=false -egressGateways[0].name="istio-egressgateway" -ingressGateways[0].enabled=true -ingressGateways[0].name="istio-ingressgateway" -istiodRemote.enabled=false -pilot.enabled=false diff --git a/operator/cmd/mesh/testdata/profile-dump/output/config_path.yaml b/operator/cmd/mesh/testdata/profile-dump/output/config_path.yaml deleted file mode 100644 index adb5852cf..000000000 --- a/operator/cmd/mesh/testdata/profile-dump/output/config_path.yaml +++ /dev/null @@ -1,15 +0,0 @@ -base: - enabled: true -cni: - enabled: false -egressGateways: -- enabled: false - name: istio-egressgateway -ingressGateways: -- enabled: false - name: istio-ingressgateway -istiodRemote: - enabled: false -pilot: - enabled: false - diff --git a/operator/cmd/mesh/testdata/profile-dump/output/list_path.txt b/operator/cmd/mesh/testdata/profile-dump/output/list_path.txt deleted file mode 100644 index 5d96a142e..000000000 --- a/operator/cmd/mesh/testdata/profile-dump/output/list_path.txt +++ /dev/null @@ -1,6 +0,0 @@ -[0].mountPath="/etc/istio/egressgateway-certs" -[0].name="egressgateway-certs" -[0].secretName="istio-egressgateway-certs" -[1].mountPath="/etc/istio/egressgateway-ca-certs" -[1].name="egressgateway-ca-certs" -[1].secretName="istio-egressgateway-ca-certs" diff --git a/operator/cmd/mesh/testdata/profile-dump/output/list_path.yaml b/operator/cmd/mesh/testdata/profile-dump/output/list_path.yaml deleted file mode 100644 index 3b398fd01..000000000 --- a/operator/cmd/mesh/testdata/profile-dump/output/list_path.yaml +++ /dev/null @@ -1,7 +0,0 @@ -- mountPath: /etc/istio/egressgateway-certs - name: egressgateway-certs - secretName: istio-egressgateway-certs -- mountPath: /etc/istio/egressgateway-ca-certs - name: egressgateway-ca-certs - secretName: istio-egressgateway-ca-certs - diff --git a/operator/cmd/mesh/uninstall.go b/operator/cmd/mesh/uninstall.go deleted file mode 100644 index 6cf8d63fc..000000000 --- a/operator/cmd/mesh/uninstall.go +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "fmt" - "os" - "strings" -) - -import ( - "github.com/spf13/cobra" - "istio.io/api/operator/v1alpha1" - "istio.io/pkg/log" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" -) - -import ( - iopv1alpha1 "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/cache" - "github.com/apache/dubbo-go-pixiu/operator/pkg/helmreconciler" - "github.com/apache/dubbo-go-pixiu/operator/pkg/manifest" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" - "github.com/apache/dubbo-go-pixiu/operator/pkg/translate" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/progress" - proxyinfo "github.com/apache/dubbo-go-pixiu/pkg/proxy" -) - -type uninstallArgs struct { - // kubeConfigPath is the path to kube config file. - kubeConfigPath string - // context is the cluster context in the kube config. - context string - // skipConfirmation determines whether the user is prompted for confirmation. - // If set to true, the user is not prompted and a Yes response is assumed in all cases. - skipConfirmation bool - // force proceeds even if there are validation errors - force bool - // purge results in deletion of all Istio resources. - purge bool - // revision is the Istio control plane revision the command targets. - revision string - // istioNamespace is the target namespace of istio control plane. - istioNamespace string - // filename is the path of input IstioOperator CR. - filename string - // set is a string with element format "path=value" where path is an IstioOperator path and the value is a - // value to set the node at that path to. - set []string - // manifestsPath is a path to a charts and profiles directory in the local filesystem, or URL with a release tgz. - manifestsPath string - // verbose generates verbose output. - verbose bool -} - -const ( - AllResourcesRemovedWarning = "All Istio resources will be pruned from the cluster\n" - NoResourcesRemovedWarning = "No resources will be pruned from the cluster. Please double check the input configs\n" - GatewaysRemovedWarning = "You are about to remove the following gateways: %s." + - " To avoid downtime, please quit this command and reinstall the gateway(s) with a revision that is not being removed from the cluster.\n" - PurgeWithRevisionOrOperatorSpecifiedWarning = "Purge uninstall will remove all Istio resources, ignoring the specified revision or operator file" -) - -func addUninstallFlags(cmd *cobra.Command, args *uninstallArgs) { - cmd.PersistentFlags().StringVarP(&args.kubeConfigPath, "kubeconfig", "c", "", KubeConfigFlagHelpStr) - cmd.PersistentFlags().StringVar(&args.context, "context", "", ContextFlagHelpStr) - cmd.PersistentFlags().BoolVarP(&args.skipConfirmation, "skip-confirmation", "y", false, skipConfirmationFlagHelpStr) - cmd.PersistentFlags().BoolVar(&args.force, "force", false, ForceFlagHelpStr) - cmd.PersistentFlags().BoolVar(&args.purge, "purge", false, "Delete all Istio related sources for all versions") - cmd.PersistentFlags().StringVarP(&args.revision, "revision", "r", "", revisionFlagHelpStr) - cmd.PersistentFlags().StringVar(&args.istioNamespace, "istioNamespace", istioDefaultNamespace, - "The namespace of Istio Control Plane.") - cmd.PersistentFlags().StringVarP(&args.filename, "filename", "f", "", - "The filename of the IstioOperator CR.") - cmd.PersistentFlags().StringVarP(&args.manifestsPath, "manifests", "d", "", ManifestsFlagHelpStr) - cmd.PersistentFlags().StringArrayVarP(&args.set, "set", "s", nil, setFlagHelpStr) - cmd.PersistentFlags().BoolVarP(&args.verbose, "verbose", "v", false, "Verbose output.") -} - -// UninstallCmd command uninstalls Istio from a cluster -func UninstallCmd(logOpts *log.Options) *cobra.Command { - rootArgs := &RootArgs{} - uiArgs := &uninstallArgs{} - uicmd := &cobra.Command{ - Use: "uninstall", - Short: "Uninstall Istio from a cluster", - Long: "The uninstall command uninstalls Istio from a cluster", - Example: ` # Uninstall a single control plane by revision - istioctl x uninstall --revision foo - - # Uninstall a single control plane by iop file - istioctl x uninstall -f iop.yaml - - # Uninstall all control planes and shared resources - istioctl x uninstall --purge`, - Args: func(cmd *cobra.Command, args []string) error { - if uiArgs.revision == "" && manifest.GetValueForSetFlag(uiArgs.set, "revision") == "" && uiArgs.filename == "" && !uiArgs.purge { - return fmt.Errorf("at least one of the --revision (or --set revision=), --filename or --purge flags must be set") - } - if len(args) > 0 { - return fmt.Errorf("istioctl uninstall does not take arguments") - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - return uninstall(cmd, rootArgs, uiArgs, logOpts) - }, - } - addFlags(uicmd, rootArgs) - addUninstallFlags(uicmd, uiArgs) - return uicmd -} - -// uninstall uninstalls control plane by either pruning by target revision or deleting specified manifests. -func uninstall(cmd *cobra.Command, rootArgs *RootArgs, uiArgs *uninstallArgs, logOpts *log.Options) error { - l := clog.NewConsoleLogger(cmd.OutOrStdout(), cmd.ErrOrStderr(), installerScope) - if err := configLogs(logOpts); err != nil { - return fmt.Errorf("could not configure logs: %s", err) - } - kubeClient, client, err := KubernetesClients(uiArgs.kubeConfigPath, uiArgs.context, l) - if err != nil { - l.LogAndFatal(err) - } - cache.FlushObjectCaches() - opts := &helmreconciler.Options{DryRun: rootArgs.DryRun, Log: l, ProgressLog: progress.NewLog()} - var h *helmreconciler.HelmReconciler - - // If the user is performing a purge install but also specified a revision or filename, we should warn - // that the purge will still remove all resources - if uiArgs.purge && (uiArgs.revision != "" || uiArgs.filename != "") { - l.LogAndPrint(PurgeWithRevisionOrOperatorSpecifiedWarning) - } - // If only revision flag is set, we would prune resources by the revision label. - // Otherwise we would merge the revision flag and the filename flag and delete resources by generated manifests. - if uiArgs.filename == "" { - emptyiops := &v1alpha1.IstioOperatorSpec{Profile: "empty", Revision: uiArgs.revision} - iop, err := translate.IOPStoIOP(emptyiops, "empty", iopv1alpha1.Namespace(emptyiops)) - if err != nil { - return err - } - h, err := helmreconciler.NewHelmReconciler(client, kubeClient, iop, opts) - if err != nil { - return fmt.Errorf("failed to create reconciler: %v", err) - } - objectsList, err := h.GetPrunedResources(uiArgs.revision, uiArgs.purge, "") - if err != nil { - return err - } - preCheckWarnings(cmd, uiArgs, uiArgs.revision, objectsList, nil, l) - - if err := h.DeleteObjectsList(objectsList, ""); err != nil { - return fmt.Errorf("failed to delete control plane resources by revision: %v", err) - } - opts.ProgressLog.SetState(progress.StateUninstallComplete) - return nil - } - manifestMap, iop, err := manifest.GenManifests([]string{uiArgs.filename}, - applyFlagAliases(uiArgs.set, uiArgs.manifestsPath, uiArgs.revision), uiArgs.force, nil, kubeClient, l) - if err != nil { - return err - } - cpManifest := manifestMap[name.PilotComponentName] - cpObjects, err := object.ParseK8sObjectsFromYAMLManifest(strings.Join(cpManifest, object.YAMLSeparator)) - if err != nil { - return err - } - preCheckWarnings(cmd, uiArgs, iop.Spec.Revision, nil, cpObjects, l) - h, err = helmreconciler.NewHelmReconciler(client, kubeClient, iop, opts) - if err != nil { - return fmt.Errorf("failed to create reconciler: %v", err) - } - if err := h.DeleteControlPlaneByManifests(manifestMap, iop.Spec.Revision, uiArgs.purge); err != nil { - return fmt.Errorf("failed to delete control plane by manifests: %v", err) - } - opts.ProgressLog.SetState(progress.StateUninstallComplete) - return nil -} - -// preCheckWarnings checks possible breaking changes and issue warnings to users, it checks the following: -// 1. checks proxies still pointing to the target control plane revision. -// 2. lists to be pruned resources if user uninstall by --revision flag. -func preCheckWarnings(cmd *cobra.Command, uiArgs *uninstallArgs, - rev string, resourcesList []*unstructured.UnstructuredList, objectsList object.K8sObjects, l *clog.ConsoleLogger) { - pids, err := proxyinfo.GetIDsFromProxyInfo(uiArgs.kubeConfigPath, uiArgs.context, rev, uiArgs.istioNamespace) - if err != nil { - l.LogAndError(err.Error()) - } - needConfirmation, message := false, "" - if uiArgs.purge { - needConfirmation = true - message += AllResourcesRemovedWarning - } else { - rmListString, gwList := constructResourceListOutput(resourcesList, objectsList) - if rmListString == "" { - l.LogAndPrint(NoResourcesRemovedWarning) - return - } - if uiArgs.verbose { - message += fmt.Sprintf("The following resources will be pruned from the cluster: %s\n", - rmListString) - } - - if len(pids) != 0 && rev != "" { - needConfirmation = true - message += fmt.Sprintf("There are still %d proxies pointing to the control plane revision %s\n", len(pids), rev) - // just print the count only if there is a large list of proxies - if len(pids) <= 30 { - message += fmt.Sprintf("%s\n", strings.Join(pids, "\n")) - } - message += "If you proceed with the uninstall, these proxies will become detached from any control plane" + - " and will not function correctly.\n" - } - if gwList != "" { - needConfirmation = true - message += fmt.Sprintf(GatewaysRemovedWarning, gwList) - } - } - if uiArgs.skipConfirmation { - l.LogAndPrint(message) - return - } - message += "Proceed? (y/N)" - if needConfirmation && !confirm(message, cmd.OutOrStdout()) { - cmd.Print("Cancelled.\n") - os.Exit(1) - } -} - -// constructResourceListOutput is a helper function to construct the output of to be removed resources list -func constructResourceListOutput(resourcesList []*unstructured.UnstructuredList, objectsList object.K8sObjects) (string, string) { - var items []unstructured.Unstructured - if objectsList != nil { - items = objectsList.UnstructuredItems() - } - for _, usList := range resourcesList { - items = append(items, usList.Items...) - } - kindNameMap := make(map[string][]string) - for _, o := range items { - nameList := kindNameMap[o.GetKind()] - if nameList == nil { - kindNameMap[o.GetKind()] = []string{} - } - kindNameMap[o.GetKind()] = append(kindNameMap[o.GetKind()], o.GetName()) - } - if len(kindNameMap) == 0 { - return "", "" - } - output, gwlist := "", []string{} - for kind, name := range kindNameMap { - output += fmt.Sprintf("%s: %s. ", kind, strings.Join(name, ", ")) - if kind == "Deployment" { - for _, n := range name { - if strings.Contains(n, "gateway") { - gwlist = append(gwlist, n) - } - } - } - } - return output, strings.Join(gwlist, ", ") -} diff --git a/operator/cmd/mesh/upgrade.go b/operator/cmd/mesh/upgrade.go deleted file mode 100644 index 629a1e411..000000000 --- a/operator/cmd/mesh/upgrade.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "time" -) - -import ( - "github.com/spf13/cobra" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" -) - -type upgradeArgs struct { - *InstallArgs -} - -func addUpgradeFlags(cmd *cobra.Command, args *upgradeArgs) { - cmd.PersistentFlags().StringSliceVarP(&args.InFilenames, "filename", "f", nil, filenameFlagHelpStr) - cmd.PersistentFlags().StringVarP(&args.KubeConfigPath, "kubeconfig", "c", "", KubeConfigFlagHelpStr) - cmd.PersistentFlags().StringVar(&args.Context, "context", "", ContextFlagHelpStr) - cmd.PersistentFlags().DurationVar(&args.ReadinessTimeout, "readiness-timeout", 300*time.Second, - "Maximum time to wait for Istio resources in each component to be ready.") - cmd.PersistentFlags().BoolVarP(&args.SkipConfirmation, "skip-confirmation", "y", false, skipConfirmationFlagHelpStr) - cmd.PersistentFlags().BoolVar(&args.Force, "force", false, ForceFlagHelpStr) - cmd.PersistentFlags().BoolVar(&args.Verify, "verify", false, VerifyCRInstallHelpStr) - cmd.PersistentFlags().StringArrayVarP(&args.Set, "set", "s", nil, setFlagHelpStr) - cmd.PersistentFlags().StringVarP(&args.ManifestsPath, "charts", "", "", ChartsDeprecatedStr) - cmd.PersistentFlags().StringVarP(&args.ManifestsPath, "manifests", "d", "", ManifestsFlagHelpStr) -} - -// UpgradeCmd upgrades Istio control plane in-place with eligibility checks. -func UpgradeCmd(logOpts *log.Options) *cobra.Command { - rootArgs := &RootArgs{} - upgradeArgs := &upgradeArgs{ - InstallArgs: &InstallArgs{}, - } - cmd := &cobra.Command{ - Use: "upgrade", - Short: "Upgrade Istio control plane in-place", - Long: "The upgrade command is an alias for the install command" + - " that performs additional upgrade-related checks.", - RunE: func(cmd *cobra.Command, args []string) (e error) { - l := clog.NewConsoleLogger(cmd.OutOrStdout(), cmd.ErrOrStderr(), installerScope) - p := NewPrinterForWriter(cmd.OutOrStderr()) - return Install(rootArgs, upgradeArgs.InstallArgs, logOpts, cmd.OutOrStdout(), l, p) - }, - } - addFlags(cmd, rootArgs) - addUpgradeFlags(cmd, upgradeArgs) - return cmd -} diff --git a/operator/cmd/operator/main.go b/operator/cmd/operator/main.go deleted file mode 100644 index 581e629c9..000000000 --- a/operator/cmd/operator/main.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "os" -) - -import ( - "istio.io/pkg/log" -) - -func main() { - log.EnableKlogWithCobra() - rootCmd := getRootCmd(os.Args[1:]) - - if err := rootCmd.Execute(); err != nil { - os.Exit(-1) - } -} diff --git a/operator/cmd/operator/root.go b/operator/cmd/operator/root.go deleted file mode 100644 index b141976cb..000000000 --- a/operator/cmd/operator/root.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "flag" -) - -import ( - "github.com/spf13/cobra" - "github.com/spf13/cobra/doc" - "istio.io/pkg/collateral" - "istio.io/pkg/version" -) - -// getRootCmd returns the root of the cobra command-tree. -func getRootCmd(args []string) *cobra.Command { - rootCmd := &cobra.Command{ - Use: "operator", - Short: "The Istio operator.", - Args: cobra.ExactArgs(0), - PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - return nil - }, - } - rootCmd.SetArgs(args) - rootCmd.PersistentFlags().AddGoFlagSet(flag.CommandLine) - - rootCmd.AddCommand(serverCmd()) - rootCmd.AddCommand(version.CobraCommand()) - rootCmd.AddCommand(collateral.CobraCommand(rootCmd, &doc.GenManHeader{ - Title: "Istio Operator", - Section: "operator CLI", - Manual: "Istio Operator", - })) - - return rootCmd -} diff --git a/operator/cmd/operator/server.go b/operator/cmd/operator/server.go deleted file mode 100644 index 159ff0605..000000000 --- a/operator/cmd/operator/server.go +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "fmt" - "os" - "strings" - "time" -) - -import ( - ocprom "contrib.go.opencensus.io/exporter/prometheus" - "github.com/prometheus/client_golang/prometheus" - "github.com/spf13/cobra" - "go.opencensus.io/stats/view" - "istio.io/pkg/ctrlz" - "istio.io/pkg/log" - "istio.io/pkg/version" - _ "k8s.io/client-go/plugin/pkg/client/auth" - "sigs.k8s.io/controller-runtime/pkg/cache" - "sigs.k8s.io/controller-runtime/pkg/client/config" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/manager/signals" - ctrlmetrics "sigs.k8s.io/controller-runtime/pkg/metrics" -) - -import ( - root "github.com/apache/dubbo-go-pixiu/operator/cmd/mesh" - "github.com/apache/dubbo-go-pixiu/operator/pkg/apis" - "github.com/apache/dubbo-go-pixiu/operator/pkg/controller" - "github.com/apache/dubbo-go-pixiu/operator/pkg/controller/istiocontrolplane" - "github.com/apache/dubbo-go-pixiu/operator/pkg/metrics" -) - -// Should match deploy/service.yaml -const ( - metricsHost = "0.0.0.0" - metricsPort int32 = 8383 -) - -type serverArgs struct { - // force proceeds even if there are validation errors - force bool -} - -func addServerFlags(cmd *cobra.Command, args *serverArgs) { - cmd.PersistentFlags().BoolVar(&args.force, "force", false, root.ForceFlagHelpStr) -} - -func serverCmd() *cobra.Command { - loggingOptions := log.DefaultOptions() - introspectionOptions := ctrlz.DefaultOptions() - sArgs := &serverArgs{} - serverCmd := &cobra.Command{ - Use: "server", - Short: "Starts the Istio operator server", - Args: cobra.ExactArgs(0), - RunE: func(cmd *cobra.Command, args []string) error { - if err := log.Configure(loggingOptions); err != nil { - log.Errorf("Unable to configure logging: %v", err) - } - - if cs, err := ctrlz.Run(introspectionOptions, nil); err == nil { - defer cs.Close() - } else { - log.Errorf("Unable to initialize ControlZ: %v", err) - } - - run(sArgs) - return nil - }, - } - - loggingOptions.AttachCobraFlags(serverCmd) - introspectionOptions.AttachCobraFlags(serverCmd) - addServerFlags(serverCmd, sArgs) - - return serverCmd -} - -// getWatchNamespaces returns the namespaces the operator should be watching for changes -func getWatchNamespaces() ([]string, error) { - value, found := os.LookupEnv("WATCH_NAMESPACE") - if !found { - return nil, fmt.Errorf("WATCH_NAMESPACE must be set") - } - if value == "" { - return nil, nil - } - return strings.Split(value, ","), nil -} - -// getLeaderElectionNamespace returns the namespace in which the leader election configmap will be created -func getLeaderElectionNamespace() (string, bool) { - return os.LookupEnv("LEADER_ELECTION_NAMESPACE") -} - -// getRenewDeadline returns the renew deadline for active control plane to refresh leadership. -func getRenewDeadline() *time.Duration { - ddl, found := os.LookupEnv("RENEW_DEADLINE") - df := time.Second * 10 - if !found { - return &df - } - duration, err := time.ParseDuration(ddl) - if err != nil { - log.Errorf("Failed to parse renewDeadline: %v, use default value: %s", err, df.String()) - return &df - } - return &duration -} - -func run(sArgs *serverArgs) { - watchNamespaces, err := getWatchNamespaces() - if err != nil { - log.Fatalf("Failed to get watch namespaces: %v", err) - } - - leaderElectionNS, leaderElectionEnabled := getLeaderElectionNamespace() - if !leaderElectionEnabled { - log.Warn("Leader election namespace not set. Leader election is disabled. NOT APPROPRIATE FOR PRODUCTION USE!") - } - - // renewDeadline cannot be greater than leaseDuration - renewDeadline := getRenewDeadline() - leaseDuration := time.Duration(renewDeadline.Nanoseconds() * 2) - - // Get a config to talk to the apiserver - cfg, err := config.GetConfig() - if err != nil { - log.Fatalf("Could not get apiserver config: %v", err) - } - - var mgrOpt manager.Options - leaderElectionID := "istio-operator-lock" - if operatorRevision, found := os.LookupEnv("REVISION"); found && operatorRevision != "" { - leaderElectionID += "-" + operatorRevision - } - log.Infof("Leader election cm: %s", leaderElectionID) - if len(watchNamespaces) > 0 { - // Create MultiNamespacedCache with watched namespaces if it's not empty. - mgrOpt = manager.Options{ - NewCache: cache.MultiNamespacedCacheBuilder(watchNamespaces), - MetricsBindAddress: fmt.Sprintf("%s:%d", metricsHost, metricsPort), - LeaderElection: leaderElectionEnabled, - LeaderElectionNamespace: leaderElectionNS, - LeaderElectionID: leaderElectionID, - LeaseDuration: &leaseDuration, - RenewDeadline: renewDeadline, - } - } else { - // Create manager option for watching all namespaces. - mgrOpt = manager.Options{ - Namespace: "", - MetricsBindAddress: fmt.Sprintf("%s:%d", metricsHost, metricsPort), - LeaderElection: leaderElectionEnabled, - LeaderElectionNamespace: leaderElectionNS, - LeaderElectionID: leaderElectionID, - LeaseDuration: &leaseDuration, - RenewDeadline: renewDeadline, - } - } - - // Create a new Cmd to provide shared dependencies and start components - mgr, err := manager.New(cfg, mgrOpt) - if err != nil { - log.Fatalf("Could not create a controller manager: %v", err) - } - - log.Info("Creating operator metrics exporter") - exporter, err := ocprom.NewExporter(ocprom.Options{ - Registry: ctrlmetrics.Registry.(*prometheus.Registry), - Namespace: "istio_install_operator", - }) - if err != nil { - log.Warnf("Error while building exporter: %v", err) - } else { - view.RegisterExporter(exporter) - } - - log.Info("Registering Components.") - - // Setup Scheme for all resources - if err := apis.AddToScheme(mgr.GetScheme()); err != nil { - log.Fatalf("Could not add manager scheme: %v", err) - } - - // Setup all Controllers - options := &istiocontrolplane.Options{Force: sArgs.force} - if err := controller.AddToManager(mgr, options); err != nil { - log.Fatalf("Could not add all controllers to operator manager: %v", err) - } - - // Record version of operator in metrics - metrics.Version. - With(metrics.OperatorVersionLabel.Value(version.Info.String())). - Record(1.0) - - log.Info("Starting the Cmd.") - - // Start the Cmd - if err := mgr.Start(signals.SetupSignalHandler()); err != nil { - log.Fatalf("Manager exited non-zero: %v", err) - } -} diff --git a/operator/docker/Dockerfile.operator b/operator/docker/Dockerfile.operator deleted file mode 100644 index 5bb051477..000000000 --- a/operator/docker/Dockerfile.operator +++ /dev/null @@ -1,26 +0,0 @@ -# BASE_DISTRIBUTION is used to switch between the old base distribution and distroless base images -ARG BASE_DISTRIBUTION=debug - -# Version is the base image version from the TLD Makefile -ARG BASE_VERSION=latest - -# The following section is used as base image if BASE_DISTRIBUTION=debug -FROM gcr.io/istio-release/base:${BASE_VERSION} as debug - -# The following section is used as base image if BASE_DISTRIBUTION=distroless -FROM gcr.io/istio-release/distroless:${BASE_VERSION} as distroless - -# This will build the final image based on either debug or distroless from above -# hadolint ignore=DL3006 -FROM ${BASE_DISTRIBUTION:-debug} - -# install operator binary -ARG TARGETARCH -COPY ${TARGETARCH:-amd64}/operator /usr/local/bin/operator - -# add operator manifests -COPY manifests/ /var/lib/istio/manifests/ - -USER 1337:1337 - -ENTRYPOINT ["/usr/local/bin/operator"] diff --git a/operator/images/operator_render_flow.svg b/operator/images/operator_render_flow.svg deleted file mode 100644 index 2306e8235..000000000 --- a/operator/images/operator_render_flow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/operator/pkg/apis/addtoscheme_istio_v1alpha1.go b/operator/pkg/apis/addtoscheme_istio_v1alpha1.go deleted file mode 100644 index 2bc5e11fc..000000000 --- a/operator/pkg/apis/addtoscheme_istio_v1alpha1.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package apis - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" -) - -func init() { - // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back - AddToSchemes = append(AddToSchemes, v1alpha1.SchemeBuilder.AddToScheme) -} diff --git a/operator/pkg/apis/apis.go b/operator/pkg/apis/apis.go deleted file mode 100644 index 14bf66d2f..000000000 --- a/operator/pkg/apis/apis.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package apis - -import ( - "k8s.io/apimachinery/pkg/runtime" -) - -// AddToSchemes may be used to add all resources defined in the project to a Scheme -var AddToSchemes runtime.SchemeBuilder - -// AddToScheme adds all Resources to the Scheme -func AddToScheme(s *runtime.Scheme) error { - return AddToSchemes.AddToScheme(s) -} diff --git a/operator/pkg/apis/istio/common.go b/operator/pkg/apis/istio/common.go deleted file mode 100644 index 90d474cb1..000000000 --- a/operator/pkg/apis/istio/common.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istio - -import ( - "fmt" -) - -import ( - "istio.io/api/operator/v1alpha1" - "sigs.k8s.io/yaml" -) - -import ( - operator_v1alpha1 "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/operator/pkg/validate" -) - -// UnmarshalAndValidateIOPS unmarshals a string containing IstioOperator YAML, validates it, and returns a struct -// representation if successful. In case of validation errors, it returns both the IstioOperatorSpec struct and -// an error, so the caller can decide how to handle it. -func UnmarshalAndValidateIOPS(iopsYAML string) (*v1alpha1.IstioOperatorSpec, error) { - iops := &v1alpha1.IstioOperatorSpec{} - if err := util.UnmarshalWithJSONPB(iopsYAML, iops, false); err != nil { - return nil, fmt.Errorf("could not unmarshal the merged YAML: %s\n\nYAML:\n%s", err, iopsYAML) - } - if errs := validate.CheckIstioOperatorSpec(iops, true); len(errs) != 0 { - return iops, fmt.Errorf(errs.Error()) - } - return iops, nil -} - -// UnmarshalIstioOperator unmarshals a string containing IstioOperator YAML. -func UnmarshalIstioOperator(iopYAML string, allowUnknownField bool) (*operator_v1alpha1.IstioOperator, error) { - iop := &operator_v1alpha1.IstioOperator{} - if allowUnknownField { - if err := yaml.Unmarshal([]byte(iopYAML), iop); err != nil { - return nil, fmt.Errorf("could not unmarshal: %v", err) - } - } else { - if err := yaml.UnmarshalStrict([]byte(iopYAML), iop); err != nil { - return nil, fmt.Errorf("could not unmarshal: %v", err) - } - } - return iop, nil -} diff --git a/operator/pkg/apis/istio/v1alpha1/common.go b/operator/pkg/apis/istio/v1alpha1/common.go deleted file mode 100644 index a02997d75..000000000 --- a/operator/pkg/apis/istio/v1alpha1/common.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha1 - -import ( - "istio.io/api/operator/v1alpha1" -) - -const ( - globalKey = "global" - istioNamespaceKey = "istioNamespace" -) - -// Namespace returns the namespace of the containing CR. -func Namespace(iops *v1alpha1.IstioOperatorSpec) string { - if iops.Namespace != "" { - return iops.Namespace - } - if iops.Values == nil { - return "" - } - v := iops.Values.AsMap() - if v[globalKey] == nil { - return "" - } - vg := v[globalKey].(map[string]interface{}) - n := vg[istioNamespaceKey] - if n == nil { - return "" - } - return n.(string) -} - -// SetNamespace returns the namespace of the containing CR. -func SetNamespace(iops *v1alpha1.IstioOperatorSpec, namespace string) { - if namespace != "" { - iops.Namespace = namespace - } - // TODO implement -} diff --git a/operator/pkg/apis/istio/v1alpha1/deepcopy_test.go b/operator/pkg/apis/istio/v1alpha1/deepcopy_test.go deleted file mode 100644 index 5d4c4dcf7..000000000 --- a/operator/pkg/apis/istio/v1alpha1/deepcopy_test.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha1_test - -import ( - "os" - "testing" -) - -import ( - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/testing/protocmp" - v1alpha12 "istio.io/api/operator/v1alpha1" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio" - install "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" -) - -// This is to verify that certain proto types handle marshal and unmarshal properly -func TestIstioOperatorSpec_DeepCopy(t *testing.T) { - x := &v1alpha12.ResourceMetricSource{} - err := yaml.UnmarshalStrict([]byte("targetAverageValue: 100m"), x) - t.Log(x) - if err != nil { - t.Fatal(err) - } - y := &v1alpha12.MetricSpec{} - err = yaml.UnmarshalStrict([]byte(` -type: Resource -resource: - targetAverageValue: 100m`), y) - t.Log(y) - if err != nil { - t.Fatal(err) - } - z := &v1alpha12.HorizontalPodAutoscalerSpec{} - err = yaml.UnmarshalStrict([]byte(`metrics: -- type: Resource - resource: - targetAverageValue: 100m`), z) - t.Log(z) - if err != nil { - t.Fatal(err) - } - fa := &install.IstioOperator{} - err = yaml.UnmarshalStrict([]byte(`apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -metadata: - namespace: dubbo-system - name: example-istiocontrolplane -spec: - profile: demo - components: - pilot: - k8s: - hpaSpec: - scaleTargetRef: - apiVersion: extensions/v1beta1 - kind: Deployment - name: istiod - minReplicas: 1 - maxReplicas: 5 - metrics: - - type: Resource - resource: - name: cpu - targetAverageValue: 100m -`), fa) - t.Log(fa) - if err != nil { - t.Error(err) - } - f := &install.IstioOperator{} - err = yaml.UnmarshalStrict([]byte(`apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -metadata: - namespace: dubbo-system - name: example-istiocontrolplane -spec: - profile: demo - components: - pilot: - k8s: - hpaSpec: - scaleTargetRef: - apiVersion: extensions/v1beta1 - kind: Deployment - name: istiod - minReplicas: 1 - maxReplicas: 5 - metrics: - - type: Resource - resource: - name: cpu - targetAverageValue: 100m -`), f) - t.Log(f) - if err != nil { - t.Fatal(err) - } - tests := []struct { - name string - iop string - }{ - { - name: "handles Kubernetes quantity types", - iop: "testdata/quantity.yaml", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - m := loadResource(t, tt.iop) - gotSpec := m.Spec.DeepCopy() - if diff := cmp.Diff(m.Spec, gotSpec, protocmp.Transform()); diff != "" { - t.Error(diff) - } - }) - } -} - -func loadResource(t *testing.T, filepath string) *install.IstioOperator { - t.Helper() - contents, err := os.ReadFile(filepath) - if err != nil { - t.Fatal(err) - } - resource, err := istio.UnmarshalIstioOperator(string(contents), false) - if err != nil { - t.Fatal(err) - } - return resource -} diff --git a/operator/pkg/apis/istio/v1alpha1/register.go b/operator/pkg/apis/istio/v1alpha1/register.go deleted file mode 100644 index cbabf2433..000000000 --- a/operator/pkg/apis/istio/v1alpha1/register.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// NOTE: Boilerplate only. Ignore this file. - -// Package v1alpha1 contains API Schema definitions for the istio v1alpha1 API group -// +k8s:deepcopy-gen=package,register -// +groupName=install.istio.io -package v1alpha1 - -import ( - "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/scheme" -) - -var ( - // IstioOperatorGVK is GVK for IstioOperator - IstioOperatorGVK = schema.GroupVersionKind{ - Version: "v1alpha1", - Group: "install.istio.io", - Kind: "IstioOperator", - } - - // SchemeGroupVersion is group version used to register these objects - SchemeGroupVersion = schema.GroupVersion{Group: "install.istio.io", Version: "v1alpha1"} - - // SchemeGroupKind is group version used to register these objects - SchemeGroupKind = schema.GroupKind{Group: "install.istio.io", Kind: "IstioOperator"} - - // SchemeBuilder is used to add go types to the GroupVersionKind scheme - SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} -) - -// Register the IstioOperator and IstioOperatorList API kind -func init() { - SchemeBuilder.Register(&IstioOperator{}, &IstioOperatorList{}) -} diff --git a/operator/pkg/apis/istio/v1alpha1/testdata/quantity.yaml b/operator/pkg/apis/istio/v1alpha1/testdata/quantity.yaml deleted file mode 100644 index 4f076f122..000000000 --- a/operator/pkg/apis/istio/v1alpha1/testdata/quantity.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -metadata: - namespace: dubbo-system - name: example-istiocontrolplane -spec: - profile: demo - components: - pilot: - k8s: - hpaSpec: - scaleTargetRef: - apiVersion: extensions/v1beta1 - kind: Deployment - name: istiod - minReplicas: 1 - maxReplicas: 5 - metrics: - - type: Resource - resource: - name: cpu - target: - type: AverageValue - averageValue: 100m diff --git a/operator/pkg/apis/istio/v1alpha1/types.go b/operator/pkg/apis/istio/v1alpha1/types.go deleted file mode 100644 index 179ef5608..000000000 --- a/operator/pkg/apis/istio/v1alpha1/types.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by kubetype-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - operatorv1alpha1 "istio.io/api/operator/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// IstioOperatorSpec defines the desired installed state of Istio components. -// The spec is a used to define a customization of the default profile values that are supplied with each Istio release. -// Because the spec is a customization API, specifying an empty IstioOperatorSpec results in a default Istio -// component values. -// -// ```yaml -// apiVersion: install.istio.io/v1alpha1 -// kind: IstioOperator -// spec: -// -// profile: default -// hub: gcr.io/istio-testing -// tag: latest -// revision: 1-8-0 -// meshConfig: -// accessLogFile: /dev/stdout -// enableTracing: true -// components: -// egressGateways: -// - name: istio-egressgateway -// enabled: true -// -// ``` -// +kubetype-gen -// +kubetype-gen:groupVersion=install.istio.io/v1alpha1 -// +k8s:deepcopy-gen=true -type IstioOperator struct { - v1.TypeMeta `json:",inline"` - // +optional - v1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` - - // Spec defines the implementation of this definition. - // +optional - Spec *operatorv1alpha1.IstioOperatorSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` - - Status *operatorv1alpha1.InstallStatus `json:"status,omitempty"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// IstioOperatorSpecList is a collection of IstioOperatorSpecs. -type IstioOperatorList struct { - v1.TypeMeta `json:",inline"` - // +optional - v1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` - Items []IstioOperator `json:"items" protobuf:"bytes,2,rep,name=items"` -} diff --git a/operator/pkg/apis/istio/v1alpha1/validation/validation.go b/operator/pkg/apis/istio/v1alpha1/validation/validation.go deleted file mode 100644 index e972a057b..000000000 --- a/operator/pkg/apis/istio/v1alpha1/validation/validation.go +++ /dev/null @@ -1,355 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validation - -import ( - "errors" - "fmt" - "reflect" - "strings" - "unicode" -) - -import ( - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - "istio.io/api/operator/v1alpha1" - "k8s.io/apimachinery/pkg/util/intstr" -) - -import ( - valuesv1alpha1 "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/tpath" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -const ( - validationMethodName = "Validate" -) - -type deprecatedSettings struct { - old string - new string - // In ordered to distinguish between unset for non-pointer values, we need to specify the default value - def interface{} -} - -// ValidateConfig calls validation func for every defined element in Values -func ValidateConfig(failOnMissingValidation bool, iopls *v1alpha1.IstioOperatorSpec) (util.Errors, string) { - var validationErrors util.Errors - var warningMessages []string - iopvalString := util.ToYAMLWithJSONPB(iopls.Values) - values := &valuesv1alpha1.Values{} - if err := util.UnmarshalWithJSONPB(iopvalString, values, true); err != nil { - return util.NewErrs(err), "" - } - - validationErrors = util.AppendErrs(validationErrors, ValidateSubTypes(reflect.ValueOf(values).Elem(), failOnMissingValidation, values, iopls)) - - featureErrors, featureWarningMessages := validateFeatures(values, iopls) - validationErrors = util.AppendErrs(validationErrors, featureErrors) - warningMessages = append(warningMessages, featureWarningMessages...) - - deprecatedErrors, deprecatedWarningMessages := checkDeprecatedSettings(iopls) - if deprecatedErrors != nil { - validationErrors = util.AppendErr(validationErrors, deprecatedErrors) - } - warningMessages = append(warningMessages, deprecatedWarningMessages...) - return validationErrors, strings.Join(warningMessages, "\n") -} - -// Converts from struct paths to helm paths -// Global.Proxy.AccessLogFormat -> global.proxy.accessLogFormat -func firstCharsToLower(s string) string { - // Use a closure here to remember state. - // Hackish but effective. Depends on Map scanning in order and calling - // the closure once per rune. - prev := '.' - return strings.Map( - func(r rune) rune { - if prev == '.' { - prev = r - return unicode.ToLower(r) - } - prev = r - return r - }, - s) -} - -func checkDeprecatedSettings(iop *v1alpha1.IstioOperatorSpec) (util.Errors, []string) { - var errs util.Errors - messages := []string{} - warningSettings := []deprecatedSettings{ - {"Values.global.certificates", "meshConfig.certificates", nil}, - {"Values.global.outboundTrafficPolicy", "meshConfig.outboundTrafficPolicy", nil}, - {"Values.global.localityLbSetting", "meshConfig.localityLbSetting", nil}, - {"Values.global.policyCheckFailOpen", "meshConfig.policyCheckFailOpen", false}, - {"Values.global.enableTracing", "meshConfig.enableTracing", false}, - {"Values.global.proxy.accessLogFormat", "meshConfig.accessLogFormat", ""}, - {"Values.global.proxy.accessLogFile", "meshConfig.accessLogFile", ""}, - {"Values.global.proxy.concurrency", "meshConfig.defaultConfig.concurrency", uint32(0)}, - {"Values.global.proxy.envoyAccessLogService", "meshConfig.defaultConfig.envoyAccessLogService", nil}, - {"Values.global.proxy.envoyAccessLogService.enabled", "meshConfig.enableEnvoyAccessLogService", nil}, - {"Values.global.proxy.envoyMetricsService", "meshConfig.defaultConfig.envoyMetricsService", nil}, - {"Values.global.proxy.protocolDetectionTimeout", "meshConfig.protocolDetectionTimeout", ""}, - {"Values.global.proxy.holdApplicationUntilProxyStarts", "meshConfig.defaultConfig.holdApplicationUntilProxyStarts", false}, - {"Values.pilot.ingress", "meshConfig.ingressService, meshConfig.ingressControllerMode, and meshConfig.ingressClass", nil}, - {"Values.global.mtls.enabled", "the PeerAuthentication resource", nil}, - {"Values.global.mtls.auto", "meshConfig.enableAutoMtls", nil}, - {"Values.global.tracer.lightstep.address", "meshConfig.defaultConfig.tracing.lightstep.address", ""}, - {"Values.global.tracer.lightstep.accessToken", "meshConfig.defaultConfig.tracing.lightstep.accessToken", ""}, - {"Values.global.tracer.zipkin.address", "meshConfig.defaultConfig.tracing.zipkin.address", nil}, - {"Values.global.tracer.stackdriver.debug", "meshConfig.defaultConfig.tracing.stackdriver.debug", false}, - {"Values.global.tracer.stackdriver.maxNumberOfAttributes", "meshConfig.defaultConfig.tracing.stackdriver.maxNumberOfAttributes", 0}, - {"Values.global.tracer.stackdriver.maxNumberOfAnnotations", "meshConfig.defaultConfig.tracing.stackdriver.maxNumberOfAnnotations", 0}, - {"Values.global.tracer.stackdriver.maxNumberOfMessageEvents", "meshConfig.defaultConfig.tracing.stackdriver.maxNumberOfMessageEvents", 0}, - {"Values.global.tracer.datadog.address", "meshConfig.defaultConfig.tracing.datadog.address", ""}, - {"Values.global.meshExpansion.enabled", "Gateway and other Istio networking resources, such as in samples/multicluster/", false}, - {"Values.gateways.istio-ingressgateway.meshExpansionPorts", "components.ingressGateways[name=istio-ingressgateway].k8s.service.ports", nil}, - {"AddonComponents.istiocoredns.Enabled", "the in-proxy DNS capturing (ISTIO_META_DNS_CAPTURE)", false}, - {"Values.istiocoredns.enabled", "the in-proxy DNS capturing (ISTIO_META_DNS_CAPTURE)", false}, - // nolint: lll - {"Values.global.jwtPolicy", "Values.global.jwtPolicy=third-party-jwt. See http://istio.io/latest/docs/ops/best-practices/security/#configure-third-party-service-account-tokens for more information", "third-party-jwt"}, - { - "Values.telemetry.v2.stackdriver.logging", "Values.telemetry.v2.stackdriver.outboundAccessLogging and Values.telemetry.v2.stackdriver.inboundAccessLogging", - false, - }, - {"Values.global.centralIstiod", "Values.global.externalIstiod", false}, - {"Values.global.arch", "the affinity of k8s settings", nil}, - } - - failHardSettings := []deprecatedSettings{ - {"Values.grafana.enabled", "the samples/addons/ deployments", false}, - {"Values.tracing.enabled", "the samples/addons/ deployments", false}, - {"Values.kiali.enabled", "the samples/addons/ deployments", false}, - {"Values.prometheus.enabled", "the samples/addons/ deployments", false}, - {"AddonComponents.grafana.Enabled", "the samples/addons/ deployments", false}, - {"AddonComponents.tracing.Enabled", "the samples/addons/ deployments", false}, - {"AddonComponents.kiali.Enabled", "the samples/addons/ deployments", false}, - {"AddonComponents.prometheus.Enabled", "the samples/addons/ deployments", false}, - } - - for _, d := range warningSettings { - v, f, _ := tpath.GetFromStructPath(iop, d.old) - if f { - switch t := v.(type) { - // need to do conversion for bool value defined in IstioOperator component spec. - case *wrappers.BoolValue: - v = t.Value - } - if v != d.def { - messages = append(messages, fmt.Sprintf("! %s is deprecated; use %s instead", firstCharsToLower(d.old), d.new)) - } - } - } - for _, d := range failHardSettings { - v, f, _ := tpath.GetFromStructPath(iop, d.old) - if f { - switch t := v.(type) { - // need to do conversion for bool value defined in IstioOperator component spec. - case *wrappers.BoolValue: - v = t.Value - } - if v != d.def { - ms := fmt.Sprintf("! %s is deprecated; use %s instead", firstCharsToLower(d.old), d.new) - errs = util.AppendErr(errs, errors.New(ms+"\n")) - } - } - } - return errs, messages -} - -type FeatureValidator func(*valuesv1alpha1.Values, *v1alpha1.IstioOperatorSpec) (util.Errors, []string) - -// validateFeatures check whether the config sematically make sense. For example, feature X and feature Y can't be enabled together. -func validateFeatures(values *valuesv1alpha1.Values, spec *v1alpha1.IstioOperatorSpec) (errs util.Errors, warnings []string) { - validators := []FeatureValidator{ - CheckServicePorts, - CheckAutoScaleAndReplicaCount, - } - - for _, validator := range validators { - newErrs, newWarnings := validator(values, spec) - errs = util.AppendErrs(errs, newErrs) - warnings = append(warnings, newWarnings...) - } - - return -} - -// CheckAutoScaleAndReplicaCount warns when autoscaleEnabled is true and k8s replicaCount is set. -func CheckAutoScaleAndReplicaCount(values *valuesv1alpha1.Values, spec *v1alpha1.IstioOperatorSpec) (errs util.Errors, warnings []string) { - if values.GetPilot().GetAutoscaleEnabled().GetValue() && spec.GetComponents().GetPilot().GetK8S().GetReplicaCount() != 0 { - warnings = append(warnings, - "components.pilot.k8s.replicaCount should not be set when values.pilot.autoscaleEnabled is true") - } - - validateGateways := func(gateways []*v1alpha1.GatewaySpec, gwType string) { - const format = "components.%sGateways[name=%s].k8s.replicaCount should not be set when values.gateways.istio-%sgateway.autoscaleEnabled is true" - for _, gw := range gateways { - if gw.GetK8S().GetReplicaCount() != 0 { - warnings = append(warnings, fmt.Sprintf(format, gwType, gw.Name, gwType)) - } - } - } - - if values.GetGateways().GetIstioIngressgateway().GetAutoscaleEnabled().GetValue() { - validateGateways(spec.GetComponents().GetIngressGateways(), "ingress") - } - - if values.GetGateways().GetIstioEgressgateway().GetAutoscaleEnabled().GetValue() { - validateGateways(spec.GetComponents().GetEgressGateways(), "egress") - } - - return -} - -// CheckServicePorts validates Service ports. Specifically, this currently -// asserts that all ports will bind to a port number greater than 1024 when not -// running as root. -func CheckServicePorts(values *valuesv1alpha1.Values, spec *v1alpha1.IstioOperatorSpec) (errs util.Errors, warnings []string) { - if !values.GetGateways().GetIstioIngressgateway().GetRunAsRoot().GetValue() { - errs = util.AppendErrs(errs, validateGateways(spec.GetComponents().GetIngressGateways(), "istio-ingressgateway")) - } - if !values.GetGateways().GetIstioEgressgateway().GetRunAsRoot().GetValue() { - errs = util.AppendErrs(errs, validateGateways(spec.GetComponents().GetEgressGateways(), "istio-egressgateway")) - } - for _, raw := range values.GetGateways().GetIstioIngressgateway().GetIngressPorts() { - p := raw.AsMap() - var tp int - if p["targetPort"] != nil { - t, ok := p["targetPort"].(float64) - if !ok { - continue - } - tp = int(t) - } - - rport, ok := p["port"].(float64) - if !ok { - continue - } - portnum := int(rport) - if tp == 0 && portnum > 1024 { - // Target port defaults to port. If its >1024, it is safe. - continue - } - if tp < 1024 { - // nolint: lll - errs = util.AppendErr(errs, fmt.Errorf("port %v is invalid: targetPort is set to %v, which requires root. Set targetPort to be greater than 1024 or configure values.gateways.istio-ingressgateway.runAsRoot=true", portnum, tp)) - } - } - return -} - -func validateGateways(gw []*v1alpha1.GatewaySpec, name string) util.Errors { - // nolint: lll - format := "port %v/%v in gateway %v invalid: targetPort is set to %d, which requires root. Set targetPort to be greater than 1024 or configure values.gateways.%s.runAsRoot=true" - var errs util.Errors - for _, gw := range gw { - for _, p := range gw.GetK8S().GetService().GetPorts() { - tp := 0 - if p.TargetPort != nil && p.TargetPort.Type == int64(intstr.String) { - // Do not validate named ports - continue - } - if p.TargetPort != nil && p.TargetPort.Type == int64(intstr.Int) { - tp = int(p.TargetPort.IntVal.GetValue()) - } - if tp == 0 && p.Port > 1024 { - // Target port defaults to port. If its >1024, it is safe. - continue - } - if tp < 1024 { - errs = util.AppendErr(errs, fmt.Errorf(format, p.Name, p.Port, gw.Name, tp, name)) - } - } - } - return errs -} - -func ValidateSubTypes(e reflect.Value, failOnMissingValidation bool, values *valuesv1alpha1.Values, iopls *v1alpha1.IstioOperatorSpec) util.Errors { - // Dealing with receiver pointer and receiver value - ptr := e - k := e.Kind() - if k == reflect.Ptr || k == reflect.Interface { - e = e.Elem() - } - if !e.IsValid() { - return nil - } - // check for method on value - method := e.MethodByName(validationMethodName) - if !method.IsValid() { - method = ptr.MethodByName(validationMethodName) - } - - var validationErrors util.Errors - if util.IsNilOrInvalidValue(method) { - if failOnMissingValidation { - validationErrors = append(validationErrors, fmt.Errorf("type %s is missing Validation method", e.Type().String())) - } - } else { - r := method.Call([]reflect.Value{reflect.ValueOf(failOnMissingValidation), reflect.ValueOf(values), reflect.ValueOf(iopls)})[0].Interface().(util.Errors) - if len(r) != 0 { - validationErrors = append(validationErrors, r...) - } - } - // If it is not a struct nothing to do, returning previously collected validation errors - if e.Kind() != reflect.Struct { - return validationErrors - } - for i := 0; i < e.NumField(); i++ { - // Corner case of a slice of something, if something is defined type, then process it recursiveley. - if e.Field(i).Kind() == reflect.Slice { - validationErrors = append(validationErrors, processSlice(e.Field(i), failOnMissingValidation, values, iopls)...) - continue - } - if e.Field(i).Kind() == reflect.Map { - validationErrors = append(validationErrors, processMap(e.Field(i), failOnMissingValidation, values, iopls)...) - continue - } - // Validation is not required if it is not a defined type - if e.Field(i).Kind() != reflect.Interface && e.Field(i).Kind() != reflect.Ptr { - continue - } - val := e.Field(i).Elem() - if util.IsNilOrInvalidValue(val) { - continue - } - validationErrors = append(validationErrors, ValidateSubTypes(e.Field(i), failOnMissingValidation, values, iopls)...) - } - - return validationErrors -} - -func processSlice(e reflect.Value, failOnMissingValidation bool, values *valuesv1alpha1.Values, iopls *v1alpha1.IstioOperatorSpec) util.Errors { - var validationErrors util.Errors - for i := 0; i < e.Len(); i++ { - validationErrors = append(validationErrors, ValidateSubTypes(e.Index(i), failOnMissingValidation, values, iopls)...) - } - - return validationErrors -} - -func processMap(e reflect.Value, failOnMissingValidation bool, values *valuesv1alpha1.Values, iopls *v1alpha1.IstioOperatorSpec) util.Errors { - var validationErrors util.Errors - for _, k := range e.MapKeys() { - v := e.MapIndex(k) - validationErrors = append(validationErrors, ValidateSubTypes(v, failOnMissingValidation, values, iopls)...) - } - - return validationErrors -} diff --git a/operator/pkg/apis/istio/v1alpha1/validation/validation_test.go b/operator/pkg/apis/istio/v1alpha1/validation/validation_test.go deleted file mode 100644 index 7344f2ab3..000000000 --- a/operator/pkg/apis/istio/v1alpha1/validation/validation_test.go +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validation_test - -import ( - "os" - "path/filepath" - "reflect" - "strings" - "testing" -) - -import ( - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - v1alpha12 "istio.io/api/operator/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1/validation" - "github.com/apache/dubbo-go-pixiu/operator/pkg/helm" - "github.com/apache/dubbo-go-pixiu/operator/pkg/manifest" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" -) - -// nolint: lll -func TestValidateConfig(t *testing.T) { - tests := []struct { - name string - value *v1alpha12.IstioOperatorSpec - values string - errors string - warnings string - }{ - { - name: "addons", - value: &v1alpha12.IstioOperatorSpec{ - AddonComponents: map[string]*v1alpha12.ExternalComponentSpec{ - "grafana": { - Enabled: &wrappers.BoolValue{Value: true}, - }, - }, - Values: util.MustStruct(map[string]interface{}{ - "grafana": map[string]interface{}{ - "enabled": true, - }, - }), - }, - errors: `! values.grafana.enabled is deprecated; use the samples/addons/ deployments instead -, ! addonComponents.grafana.enabled is deprecated; use the samples/addons/ deployments instead -`, - }, - { - name: "global", - value: &v1alpha12.IstioOperatorSpec{ - Values: util.MustStruct(map[string]interface{}{ - "global": map[string]interface{}{ - "localityLbSetting": map[string]interface{}{"foo": "bar"}, - }, - }), - }, - warnings: `! values.global.localityLbSetting is deprecated; use meshConfig.localityLbSetting instead`, - }, - { - name: "unset target port", - values: ` -components: - ingressGateways: - - name: istio-ingressgateway - enabled: true - - name: cluster-local-gateway - enabled: true - k8s: - service: - type: ClusterIP - ports: - - port: 15020 - name: status-port - - port: 80 - name: http2 -`, - errors: `port http2/80 in gateway cluster-local-gateway invalid: targetPort is set to 0, which requires root. Set targetPort to be greater than 1024 or configure values.gateways.istio-ingressgateway.runAsRoot=true`, - }, - { - name: "explicitly invalid target port", - values: ` -components: - ingressGateways: - - name: istio-ingressgateway - enabled: true - - name: cluster-local-gateway - enabled: true - k8s: - service: - type: ClusterIP - ports: - - port: 15020 - name: status-port - - port: 80 - name: http2 - targetPort: 90 -`, - errors: `port http2/80 in gateway cluster-local-gateway invalid: targetPort is set to 90, which requires root. Set targetPort to be greater than 1024 or configure values.gateways.istio-ingressgateway.runAsRoot=true`, - }, - { - name: "explicitly invalid target port for egress", - values: ` -components: - egressGateways: - - name: egress-gateway - enabled: true - k8s: - service: - type: ClusterIP - ports: - - port: 15020 - name: status-port - - port: 80 - name: http2 - targetPort: 90 -`, - errors: `port http2/80 in gateway egress-gateway invalid: targetPort is set to 90, which requires root. Set targetPort to be greater than 1024 or configure values.gateways.istio-egressgateway.runAsRoot=true`, - }, - { - name: "low target port with root", - values: ` -components: - ingressGateways: - - name: istio-ingressgateway - enabled: true - - name: cluster-local-gateway - enabled: true - k8s: - service: - type: ClusterIP - ports: - - port: 15020 - name: status-port - - port: 80 - name: http2 - targetPort: 90 -values: - gateways: - istio-ingressgateway: - runAsRoot: true -`, - errors: ``, - }, - { - name: "legacy values ports config empty targetPort", - values: ` -values: - gateways: - istio-ingressgateway: - ingressPorts: - - name: http - port: 80 -`, - errors: `port 80 is invalid: targetPort is set to 0, which requires root. Set targetPort to be greater than 1024 or configure values.gateways.istio-ingressgateway.runAsRoot=true`, - }, - { - name: "legacy values ports config explicit targetPort", - values: ` -values: - gateways: - istio-ingressgateway: - ingressPorts: - - name: http - port: 80 - targetPort: 90 -`, - errors: `port 80 is invalid: targetPort is set to 90, which requires root. Set targetPort to be greater than 1024 or configure values.gateways.istio-ingressgateway.runAsRoot=true`, - }, - { - name: "legacy values ports valid", - values: ` -values: - gateways: - istio-ingressgateway: - ingressPorts: - - name: http - port: 80 - targetPort: 8080 -`, - errors: ``, - }, - { - name: "replicaCount set when autoscaleEnabled is true", - values: ` -values: - pilot: - autoscaleEnabled: true - gateways: - istio-ingressgateway: - autoscaleEnabled: true - istio-egressgateway: - autoscaleEnabled: true -components: - pilot: - k8s: - replicaCount: 2 - ingressGateways: - - name: istio-ingressgateway - enabled: true - k8s: - replicaCount: 2 - egressGateways: - - name: istio-egressgateway - enabled: true - k8s: - replicaCount: 2 -`, - warnings: strings.TrimSpace(` -components.pilot.k8s.replicaCount should not be set when values.pilot.autoscaleEnabled is true -components.ingressGateways[name=istio-ingressgateway].k8s.replicaCount should not be set when values.gateways.istio-ingressgateway.autoscaleEnabled is true -components.egressGateways[name=istio-egressgateway].k8s.replicaCount should not be set when values.gateways.istio-egressgateway.autoscaleEnabled is true -`), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - iop := tt.value - if tt.values != "" { - iop = &v1alpha12.IstioOperatorSpec{} - if err := util.UnmarshalWithJSONPB(tt.values, iop, true); err != nil { - t.Fatal(err) - } - } - err, warnings := validation.ValidateConfig(false, iop) - if tt.errors != err.String() { - t.Fatalf("expected errors: \n%q\n got: \n%q\n", tt.errors, err.String()) - } - if tt.warnings != warnings { - t.Fatalf("expected warnings: \n%q\n got \n%q\n", tt.warnings, warnings) - } - }) - } -} - -func TestValidateProfiles(t *testing.T) { - manifests := filepath.Join(env.IstioSrc, helm.OperatorSubdirFilePath) - profiles, err := helm.ListProfiles(manifests) - if err != nil { - t.Fatal(err) - } - if len(profiles) < 2 { - // Just ensure we find some profiles, in case this code breaks - t.Fatalf("Maybe have failed getting profiles, got %v", profiles) - } - l := clog.NewConsoleLogger(os.Stdout, os.Stderr, nil) - for _, tt := range profiles { - t.Run(tt, func(t *testing.T) { - _, s, err := manifest.GenIOPFromProfile(tt, "", []string{"installPackagePath=" + manifests}, false, false, nil, l) - if err != nil { - t.Fatal(err) - } - verr, warnings := validation.ValidateConfig(false, s.Spec) - if verr != nil { - t.Fatalf("got error validating: %v", verr) - } - if warnings != "" { - t.Fatalf("got warning validating: %v", warnings) - } - }) - } -} - -func TestValidate(t *testing.T) { - tests := []struct { - name string - toValidate *v1alpha1.Values - validated bool - }{ - { - name: "Empty struct", - toValidate: &v1alpha1.Values{}, - validated: true, - }, - { - name: "With CNI defined", - toValidate: &v1alpha1.Values{ - Cni: &v1alpha1.CNIConfig{ - Enabled: &wrappers.BoolValue{Value: true}, - }, - }, - validated: true, - }, - } - - for _, tt := range tests { - err := validation.ValidateSubTypes(reflect.ValueOf(tt.toValidate).Elem(), false, tt.toValidate, nil) - if len(err) != 0 && tt.validated { - t.Fatalf("Test %s failed with errors: %+v but supposed to succeed", tt.name, err) - } - if len(err) == 0 && !tt.validated { - t.Fatalf("Test %s failed as it is supposed to fail but succeeded", tt.name) - } - } -} diff --git a/operator/pkg/apis/istio/v1alpha1/value_types_json.go b/operator/pkg/apis/istio/v1alpha1/value_types_json.go deleted file mode 100644 index bce3c3c02..000000000 --- a/operator/pkg/apis/istio/v1alpha1/value_types_json.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by protoc-gen-golang. DO NOT EDIT. -// source: operator/v1alpha1/operator.proto - -// Configuration affecting Istio control plane installation version and shape. - -package v1alpha1 - -import ( - "encoding/json" -) - -import ( - github_com_golang_protobuf_jsonpb "github.com/golang/protobuf/jsonpb" // nolint: staticcheck - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - "k8s.io/apimachinery/pkg/util/intstr" -) - -var _ github_com_golang_protobuf_jsonpb.JSONPBUnmarshaler = &IntOrString{} - -// UnmarshalJSON implements the json.Unmarshaller interface. -func (this *IntOrString) UnmarshalJSON(value []byte) error { - if value[0] == '"' { - this.Type = int64(intstr.String) - var s string - err := json.Unmarshal(value, &s) - if err != nil { - return err - } - this.StrVal = &wrappers.StringValue{Value: s} - return nil - } - this.Type = int64(intstr.Int) - var s int32 - err := json.Unmarshal(value, &s) - if err != nil { - return err - } - this.IntVal = &wrappers.Int32Value{Value: s} - return nil -} - -func (this *IntOrString) MarshalJSONPB(_ *github_com_golang_protobuf_jsonpb.Marshaler) ([]byte, error) { - return this.MarshalJSON() -} - -func (this *IntOrString) MarshalJSON() ([]byte, error) { - if this.IntVal != nil { - return json.Marshal(this.IntVal.GetValue()) - } - return json.Marshal(this.StrVal.GetValue()) -} - -func (this *IntOrString) UnmarshalJSONPB(_ *github_com_golang_protobuf_jsonpb.Unmarshaler, value []byte) error { - return this.UnmarshalJSON(value) -} - -func (this *IntOrString) ToKubernetes() intstr.IntOrString { - if this.IntVal != nil { - return intstr.FromInt(int(this.GetIntVal().GetValue())) - } - return intstr.FromString(this.GetStrVal().GetValue()) -} diff --git a/operator/pkg/apis/istio/v1alpha1/values_types.pb.go b/operator/pkg/apis/istio/v1alpha1/values_types.pb.go deleted file mode 100644 index 88c20d8f0..000000000 --- a/operator/pkg/apis/istio/v1alpha1/values_types.pb.go +++ /dev/null @@ -1,7043 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.27.1 -// protoc (unknown) -// source: pkg/apis/istio/v1alpha1/values_types.proto - -package v1alpha1 - -import ( - reflect "reflect" - sync "sync" -) - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - _ "google.golang.org/protobuf/types/known/anypb" - durationpb "google.golang.org/protobuf/types/known/durationpb" - structpb "google.golang.org/protobuf/types/known/structpb" - wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// Mode for the ingress controller. -type IngressControllerMode int32 - -const ( - // Unspecified Istio ingress controller. - IngressControllerMode_UNSPECIFIED IngressControllerMode = 0 - // Selects all Ingress resources, with or without Istio annotation. - IngressControllerMode_DEFAULT IngressControllerMode = 1 - // Selects only resources with istio annotation. - IngressControllerMode_STRICT IngressControllerMode = 2 - // No ingress or sync. - IngressControllerMode_OFF IngressControllerMode = 3 -) - -// Enum value maps for IngressControllerMode. -var ( - IngressControllerMode_name = map[int32]string{ - 0: "UNSPECIFIED", - 1: "DEFAULT", - 2: "STRICT", - 3: "OFF", - } - IngressControllerMode_value = map[string]int32{ - "UNSPECIFIED": 0, - "DEFAULT": 1, - "STRICT": 2, - "OFF": 3, - } -) - -func (x IngressControllerMode) Enum() *IngressControllerMode { - p := new(IngressControllerMode) - *p = x - return p -} - -func (x IngressControllerMode) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (IngressControllerMode) Descriptor() protoreflect.EnumDescriptor { - return file_pkg_apis_istio_v1alpha1_values_types_proto_enumTypes[0].Descriptor() -} - -func (IngressControllerMode) Type() protoreflect.EnumType { - return &file_pkg_apis_istio_v1alpha1_values_types_proto_enumTypes[0] -} - -func (x IngressControllerMode) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use IngressControllerMode.Descriptor instead. -func (IngressControllerMode) EnumDescriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{0} -} - -// Specifies which tracer to use. -type Tracer int32 - -const ( - Tracer_zipkin Tracer = 0 - Tracer_lightstep Tracer = 1 - Tracer_datadog Tracer = 2 - Tracer_stackdriver Tracer = 3 - Tracer_openCensusAgent Tracer = 4 - Tracer_none Tracer = 5 -) - -// Enum value maps for Tracer. -var ( - Tracer_name = map[int32]string{ - 0: "zipkin", - 1: "lightstep", - 2: "datadog", - 3: "stackdriver", - 4: "openCensusAgent", - 5: "none", - } - Tracer_value = map[string]int32{ - "zipkin": 0, - "lightstep": 1, - "datadog": 2, - "stackdriver": 3, - "openCensusAgent": 4, - "none": 5, - } -) - -func (x Tracer) Enum() *Tracer { - p := new(Tracer) - *p = x - return p -} - -func (x Tracer) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (Tracer) Descriptor() protoreflect.EnumDescriptor { - return file_pkg_apis_istio_v1alpha1_values_types_proto_enumTypes[1].Descriptor() -} - -func (Tracer) Type() protoreflect.EnumType { - return &file_pkg_apis_istio_v1alpha1_values_types_proto_enumTypes[1] -} - -func (x Tracer) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use Tracer.Descriptor instead. -func (Tracer) EnumDescriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{1} -} - -// Specifies the sidecar's default behavior when handling outbound traffic from the application. -type OutboundTrafficPolicyConfig_Mode int32 - -const ( - // Outbound traffic to unknown destinations will be allowed, in case there are no services or ServiceEntries for the destination port - OutboundTrafficPolicyConfig_ALLOW_ANY OutboundTrafficPolicyConfig_Mode = 0 - // Restrict outbound traffic to services defined in the service registry as well as those defined through ServiceEntries - OutboundTrafficPolicyConfig_REGISTRY_ONLY OutboundTrafficPolicyConfig_Mode = 1 -) - -// Enum value maps for OutboundTrafficPolicyConfig_Mode. -var ( - OutboundTrafficPolicyConfig_Mode_name = map[int32]string{ - 0: "ALLOW_ANY", - 1: "REGISTRY_ONLY", - } - OutboundTrafficPolicyConfig_Mode_value = map[string]int32{ - "ALLOW_ANY": 0, - "REGISTRY_ONLY": 1, - } -) - -func (x OutboundTrafficPolicyConfig_Mode) Enum() *OutboundTrafficPolicyConfig_Mode { - p := new(OutboundTrafficPolicyConfig_Mode) - *p = x - return p -} - -func (x OutboundTrafficPolicyConfig_Mode) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (OutboundTrafficPolicyConfig_Mode) Descriptor() protoreflect.EnumDescriptor { - return file_pkg_apis_istio_v1alpha1_values_types_proto_enumTypes[2].Descriptor() -} - -func (OutboundTrafficPolicyConfig_Mode) Type() protoreflect.EnumType { - return &file_pkg_apis_istio_v1alpha1_values_types_proto_enumTypes[2] -} - -func (x OutboundTrafficPolicyConfig_Mode) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use OutboundTrafficPolicyConfig_Mode.Descriptor instead. -func (OutboundTrafficPolicyConfig_Mode) EnumDescriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{19, 0} -} - -// Types of Access logs to export. -type TelemetryV2StackDriverConfig_AccessLogging int32 - -const ( - // No Logs. - TelemetryV2StackDriverConfig_NONE TelemetryV2StackDriverConfig_AccessLogging = 0 - // All logs including both success and error logs. - TelemetryV2StackDriverConfig_FULL TelemetryV2StackDriverConfig_AccessLogging = 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 != "-"` - TelemetryV2StackDriverConfig_ERRORS_ONLY TelemetryV2StackDriverConfig_AccessLogging = 2 -) - -// Enum value maps for TelemetryV2StackDriverConfig_AccessLogging. -var ( - TelemetryV2StackDriverConfig_AccessLogging_name = map[int32]string{ - 0: "NONE", - 1: "FULL", - 2: "ERRORS_ONLY", - } - TelemetryV2StackDriverConfig_AccessLogging_value = map[string]int32{ - "NONE": 0, - "FULL": 1, - "ERRORS_ONLY": 2, - } -) - -func (x TelemetryV2StackDriverConfig_AccessLogging) Enum() *TelemetryV2StackDriverConfig_AccessLogging { - p := new(TelemetryV2StackDriverConfig_AccessLogging) - *p = x - return p -} - -func (x TelemetryV2StackDriverConfig_AccessLogging) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (TelemetryV2StackDriverConfig_AccessLogging) Descriptor() protoreflect.EnumDescriptor { - return file_pkg_apis_istio_v1alpha1_values_types_proto_enumTypes[3].Descriptor() -} - -func (TelemetryV2StackDriverConfig_AccessLogging) Type() protoreflect.EnumType { - return &file_pkg_apis_istio_v1alpha1_values_types_proto_enumTypes[3] -} - -func (x TelemetryV2StackDriverConfig_AccessLogging) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use TelemetryV2StackDriverConfig_AccessLogging.Descriptor instead. -func (TelemetryV2StackDriverConfig_AccessLogging) EnumDescriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{27, 0} -} - -// ArchConfig specifies the pod scheduling target architecture(amd64, ppc64le, s390x, arm64) -// for all the Istio control plane components. -type ArchConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Sets pod scheduling weight for amd64 arch - Amd64 uint32 `protobuf:"varint,1,opt,name=amd64,proto3" json:"amd64,omitempty"` - // Sets pod scheduling weight for ppc64le arch. - Ppc64Le uint32 `protobuf:"varint,2,opt,name=ppc64le,proto3" json:"ppc64le,omitempty"` - // Sets pod scheduling weight for s390x arch. - S390X uint32 `protobuf:"varint,3,opt,name=s390x,proto3" json:"s390x,omitempty"` - // Sets pod scheduling weight for arm64 arch. - Arm64 uint32 `protobuf:"varint,4,opt,name=arm64,proto3" json:"arm64,omitempty"` -} - -func (x *ArchConfig) Reset() { - *x = ArchConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ArchConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ArchConfig) ProtoMessage() {} - -func (x *ArchConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ArchConfig.ProtoReflect.Descriptor instead. -func (*ArchConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{0} -} - -func (x *ArchConfig) GetAmd64() uint32 { - if x != nil { - return x.Amd64 - } - return 0 -} - -func (x *ArchConfig) GetPpc64Le() uint32 { - if x != nil { - return x.Ppc64Le - } - return 0 -} - -func (x *ArchConfig) GetS390X() uint32 { - if x != nil { - return x.S390X - } - return 0 -} - -func (x *ArchConfig) GetArm64() uint32 { - if x != nil { - return x.Arm64 - } - return 0 -} - -// Configuration for CNI. -type CNIConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Controls whether CNI is enabled. - Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - Hub string `protobuf:"bytes,2,opt,name=hub,proto3" json:"hub,omitempty"` - Tag *structpb.Value `protobuf:"bytes,3,opt,name=tag,proto3" json:"tag,omitempty"` - Image string `protobuf:"bytes,4,opt,name=image,proto3" json:"image,omitempty"` - PullPolicy string `protobuf:"bytes,5,opt,name=pullPolicy,proto3" json:"pullPolicy,omitempty"` - CniBinDir string `protobuf:"bytes,6,opt,name=cniBinDir,proto3" json:"cniBinDir,omitempty"` - CniConfDir string `protobuf:"bytes,7,opt,name=cniConfDir,proto3" json:"cniConfDir,omitempty"` - CniConfFileName string `protobuf:"bytes,8,opt,name=cniConfFileName,proto3" json:"cniConfFileName,omitempty"` - ExcludeNamespaces []string `protobuf:"bytes,9,rep,name=excludeNamespaces,proto3" json:"excludeNamespaces,omitempty"` - // Deprecated: Do not use. - PodAnnotations *structpb.Struct `protobuf:"bytes,10,opt,name=podAnnotations,proto3" json:"podAnnotations,omitempty"` - PspClusterRole string `protobuf:"bytes,11,opt,name=psp_cluster_role,json=pspClusterRole,proto3" json:"psp_cluster_role,omitempty"` - LogLevel string `protobuf:"bytes,12,opt,name=logLevel,proto3" json:"logLevel,omitempty"` - Repair *CNIRepairConfig `protobuf:"bytes,13,opt,name=repair,proto3" json:"repair,omitempty"` - Chained *wrapperspb.BoolValue `protobuf:"bytes,14,opt,name=chained,proto3" json:"chained,omitempty"` - Taint *CNITaintConfig `protobuf:"bytes,15,opt,name=taint,proto3" json:"taint,omitempty"` - ResourceQuotas *ResourceQuotas `protobuf:"bytes,16,opt,name=resource_quotas,json=resourceQuotas,proto3" json:"resource_quotas,omitempty"` - Resources *Resources `protobuf:"bytes,17,opt,name=resources,proto3" json:"resources,omitempty"` - Privileged *wrapperspb.BoolValue `protobuf:"bytes,18,opt,name=privileged,proto3" json:"privileged,omitempty"` -} - -func (x *CNIConfig) Reset() { - *x = CNIConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CNIConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CNIConfig) ProtoMessage() {} - -func (x *CNIConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CNIConfig.ProtoReflect.Descriptor instead. -func (*CNIConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{1} -} - -func (x *CNIConfig) GetEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.Enabled - } - return nil -} - -func (x *CNIConfig) GetHub() string { - if x != nil { - return x.Hub - } - return "" -} - -func (x *CNIConfig) GetTag() *structpb.Value { - if x != nil { - return x.Tag - } - return nil -} - -func (x *CNIConfig) GetImage() string { - if x != nil { - return x.Image - } - return "" -} - -func (x *CNIConfig) GetPullPolicy() string { - if x != nil { - return x.PullPolicy - } - return "" -} - -func (x *CNIConfig) GetCniBinDir() string { - if x != nil { - return x.CniBinDir - } - return "" -} - -func (x *CNIConfig) GetCniConfDir() string { - if x != nil { - return x.CniConfDir - } - return "" -} - -func (x *CNIConfig) GetCniConfFileName() string { - if x != nil { - return x.CniConfFileName - } - return "" -} - -func (x *CNIConfig) GetExcludeNamespaces() []string { - if x != nil { - return x.ExcludeNamespaces - } - return nil -} - -// Deprecated: Do not use. -func (x *CNIConfig) GetPodAnnotations() *structpb.Struct { - if x != nil { - return x.PodAnnotations - } - return nil -} - -func (x *CNIConfig) GetPspClusterRole() string { - if x != nil { - return x.PspClusterRole - } - return "" -} - -func (x *CNIConfig) GetLogLevel() string { - if x != nil { - return x.LogLevel - } - return "" -} - -func (x *CNIConfig) GetRepair() *CNIRepairConfig { - if x != nil { - return x.Repair - } - return nil -} - -func (x *CNIConfig) GetChained() *wrapperspb.BoolValue { - if x != nil { - return x.Chained - } - return nil -} - -func (x *CNIConfig) GetTaint() *CNITaintConfig { - if x != nil { - return x.Taint - } - return nil -} - -func (x *CNIConfig) GetResourceQuotas() *ResourceQuotas { - if x != nil { - return x.ResourceQuotas - } - return nil -} - -func (x *CNIConfig) GetResources() *Resources { - if x != nil { - return x.Resources - } - return nil -} - -func (x *CNIConfig) GetPrivileged() *wrapperspb.BoolValue { - if x != nil { - return x.Privileged - } - return nil -} - -type CNITaintConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Controls whether taint behavior is enabled. - Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` -} - -func (x *CNITaintConfig) Reset() { - *x = CNITaintConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CNITaintConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CNITaintConfig) ProtoMessage() {} - -func (x *CNITaintConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CNITaintConfig.ProtoReflect.Descriptor instead. -func (*CNITaintConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{2} -} - -func (x *CNITaintConfig) GetEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.Enabled - } - return nil -} - -type CNIRepairConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Controls whether repair behavior is enabled. - Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - Hub string `protobuf:"bytes,2,opt,name=hub,proto3" json:"hub,omitempty"` - Tag *structpb.Value `protobuf:"bytes,3,opt,name=tag,proto3" json:"tag,omitempty"` - Image string `protobuf:"bytes,4,opt,name=image,proto3" json:"image,omitempty"` - // Controls whether various repair behaviors are enabled. - LabelPods bool `protobuf:"varint,5,opt,name=labelPods,proto3" json:"labelPods,omitempty"` - // Deprecated: Do not use. - CreateEvents string `protobuf:"bytes,6,opt,name=createEvents,proto3" json:"createEvents,omitempty"` - DeletePods bool `protobuf:"varint,7,opt,name=deletePods,proto3" json:"deletePods,omitempty"` - BrokenPodLabelKey string `protobuf:"bytes,8,opt,name=brokenPodLabelKey,proto3" json:"brokenPodLabelKey,omitempty"` - BrokenPodLabelValue string `protobuf:"bytes,9,opt,name=brokenPodLabelValue,proto3" json:"brokenPodLabelValue,omitempty"` - InitContainerName string `protobuf:"bytes,10,opt,name=initContainerName,proto3" json:"initContainerName,omitempty"` -} - -func (x *CNIRepairConfig) Reset() { - *x = CNIRepairConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CNIRepairConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CNIRepairConfig) ProtoMessage() {} - -func (x *CNIRepairConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CNIRepairConfig.ProtoReflect.Descriptor instead. -func (*CNIRepairConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{3} -} - -func (x *CNIRepairConfig) GetEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.Enabled - } - return nil -} - -func (x *CNIRepairConfig) GetHub() string { - if x != nil { - return x.Hub - } - return "" -} - -func (x *CNIRepairConfig) GetTag() *structpb.Value { - if x != nil { - return x.Tag - } - return nil -} - -func (x *CNIRepairConfig) GetImage() string { - if x != nil { - return x.Image - } - return "" -} - -func (x *CNIRepairConfig) GetLabelPods() bool { - if x != nil { - return x.LabelPods - } - return false -} - -// Deprecated: Do not use. -func (x *CNIRepairConfig) GetCreateEvents() string { - if x != nil { - return x.CreateEvents - } - return "" -} - -func (x *CNIRepairConfig) GetDeletePods() bool { - if x != nil { - return x.DeletePods - } - return false -} - -func (x *CNIRepairConfig) GetBrokenPodLabelKey() string { - if x != nil { - return x.BrokenPodLabelKey - } - return "" -} - -func (x *CNIRepairConfig) GetBrokenPodLabelValue() string { - if x != nil { - return x.BrokenPodLabelValue - } - return "" -} - -func (x *CNIRepairConfig) GetInitContainerName() string { - if x != nil { - return x.InitContainerName - } - return "" -} - -type ResourceQuotas struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Controls whether to create resource quotas or not for the CNI DaemonSet. - Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - Pods int64 `protobuf:"varint,2,opt,name=pods,proto3" json:"pods,omitempty"` -} - -func (x *ResourceQuotas) Reset() { - *x = ResourceQuotas{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ResourceQuotas) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ResourceQuotas) ProtoMessage() {} - -func (x *ResourceQuotas) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ResourceQuotas.ProtoReflect.Descriptor instead. -func (*ResourceQuotas) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{4} -} - -func (x *ResourceQuotas) GetEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.Enabled - } - return nil -} - -func (x *ResourceQuotas) GetPods() int64 { - if x != nil { - return x.Pods - } - return 0 -} - -// Configuration for CPU target utilization for HorizontalPodAutoscaler target. -type CPUTargetUtilizationConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // K8s utilization setting for HorizontalPodAutoscaler target. - // - // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ - TargetAverageUtilization int32 `protobuf:"varint,1,opt,name=targetAverageUtilization,proto3" json:"targetAverageUtilization,omitempty"` -} - -func (x *CPUTargetUtilizationConfig) Reset() { - *x = CPUTargetUtilizationConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CPUTargetUtilizationConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CPUTargetUtilizationConfig) ProtoMessage() {} - -func (x *CPUTargetUtilizationConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CPUTargetUtilizationConfig.ProtoReflect.Descriptor instead. -func (*CPUTargetUtilizationConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{5} -} - -func (x *CPUTargetUtilizationConfig) GetTargetAverageUtilization() int32 { - if x != nil { - return x.TargetAverageUtilization - } - return 0 -} - -// Mirrors Resources for unmarshaling. -type Resources struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Limits map[string]string `protobuf:"bytes,1,rep,name=limits,proto3" json:"limits,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Requests map[string]string `protobuf:"bytes,2,rep,name=requests,proto3" json:"requests,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` -} - -func (x *Resources) Reset() { - *x = Resources{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Resources) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Resources) ProtoMessage() {} - -func (x *Resources) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Resources.ProtoReflect.Descriptor instead. -func (*Resources) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{6} -} - -func (x *Resources) GetLimits() map[string]string { - if x != nil { - return x.Limits - } - return nil -} - -func (x *Resources) GetRequests() map[string]string { - if x != nil { - return x.Requests - } - return nil -} - -// Mirrors ServiceAccount for unmarshaling. -type ServiceAccount struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Annotations *structpb.Struct `protobuf:"bytes,1,opt,name=annotations,proto3" json:"annotations,omitempty"` -} - -func (x *ServiceAccount) Reset() { - *x = ServiceAccount{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ServiceAccount) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ServiceAccount) ProtoMessage() {} - -func (x *ServiceAccount) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ServiceAccount.ProtoReflect.Descriptor instead. -func (*ServiceAccount) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{7} -} - -func (x *ServiceAccount) GetAnnotations() *structpb.Struct { - if x != nil { - return x.Annotations - } - return nil -} - -// DefaultPodDisruptionBudgetConfig specifies the default pod disruption budget configuration. -// -// See https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ -type DefaultPodDisruptionBudgetConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Controls whether a PodDisruptionBudget with a default minAvailable value of 1 is created for each deployment. - Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` -} - -func (x *DefaultPodDisruptionBudgetConfig) Reset() { - *x = DefaultPodDisruptionBudgetConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DefaultPodDisruptionBudgetConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DefaultPodDisruptionBudgetConfig) ProtoMessage() {} - -func (x *DefaultPodDisruptionBudgetConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DefaultPodDisruptionBudgetConfig.ProtoReflect.Descriptor instead. -func (*DefaultPodDisruptionBudgetConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{8} -} - -func (x *DefaultPodDisruptionBudgetConfig) GetEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.Enabled - } - return nil -} - -// DefaultResourcesConfig specifies the default k8s resources settings for all Istio control plane components. -type DefaultResourcesConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // k8s resources settings. - // - // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container - Requests *ResourcesRequestsConfig `protobuf:"bytes,1,opt,name=requests,proto3" json:"requests,omitempty"` -} - -func (x *DefaultResourcesConfig) Reset() { - *x = DefaultResourcesConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DefaultResourcesConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DefaultResourcesConfig) ProtoMessage() {} - -func (x *DefaultResourcesConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DefaultResourcesConfig.ProtoReflect.Descriptor instead. -func (*DefaultResourcesConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{9} -} - -func (x *DefaultResourcesConfig) GetRequests() *ResourcesRequestsConfig { - if x != nil { - return x.Requests - } - return nil -} - -// Configuration for an egress gateway. -type EgressGatewayConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Controls whether auto scaling with a HorizontalPodAutoscaler is enabled. - AutoscaleEnabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=autoscaleEnabled,proto3" json:"autoscaleEnabled,omitempty"` - // maxReplicas setting for HorizontalPodAutoscaler. - AutoscaleMax uint32 `protobuf:"varint,2,opt,name=autoscaleMax,proto3" json:"autoscaleMax,omitempty"` - // minReplicas setting for HorizontalPodAutoscaler. - AutoscaleMin uint32 `protobuf:"varint,3,opt,name=autoscaleMin,proto3" json:"autoscaleMin,omitempty"` - // K8s utilization setting for HorizontalPodAutoscaler target. - // - // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ - // - // Deprecated: Do not use. - Cpu *CPUTargetUtilizationConfig `protobuf:"bytes,5,opt,name=cpu,proto3" json:"cpu,omitempty"` - // Controls whether an egress gateway is enabled. - Enabled *wrapperspb.BoolValue `protobuf:"bytes,7,opt,name=enabled,proto3" json:"enabled,omitempty"` - // Environment variables passed to the proxy container. - Env *structpb.Struct `protobuf:"bytes,8,opt,name=env,proto3" json:"env,omitempty"` - Labels map[string]string `protobuf:"bytes,9,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Name string `protobuf:"bytes,25,opt,name=name,proto3" json:"name,omitempty"` - // K8s node selector. - // - // See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector - // - // Deprecated: Do not use. - NodeSelector *structpb.Struct `protobuf:"bytes,10,opt,name=nodeSelector,proto3" json:"nodeSelector,omitempty"` - // K8s annotations for pods. - // - // See: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ - // - // Deprecated: Do not use. - PodAnnotations *structpb.Struct `protobuf:"bytes,11,opt,name=podAnnotations,proto3" json:"podAnnotations,omitempty"` - // Pod anti-affinity label selector. - // - // Specify the pod anti-affinity that allows you to constrain which nodes - // your pod is eligible to be scheduled based on labels on pods that are - // already running on the node rather than based on labels on nodes. - // There are currently two types of anti-affinity: - // "requiredDuringSchedulingIgnoredDuringExecution" - // "preferredDuringSchedulingIgnoredDuringExecution" - // which denote “hard†vs. “soft†requirements, you can define your values - // in "podAntiAffinityLabelSelector" and "podAntiAffinityTermLabelSelector" - // correspondingly. - // See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity - // - // Examples: - // podAntiAffinityLabelSelector: - // - key: security - // operator: In - // values: S1,S2 - // topologyKey: "kubernetes.io/hostname" - // This pod anti-affinity rule says that the pod requires not to be scheduled - // onto a node if that node is already running a pod with label having key - // “security†and value “S1â€. - // - // Deprecated: Do not use. - PodAntiAffinityLabelSelector []*structpb.Struct `protobuf:"bytes,12,rep,name=podAntiAffinityLabelSelector,proto3" json:"podAntiAffinityLabelSelector,omitempty"` - // See PodAntiAffinityLabelSelector. - // - // Deprecated: Do not use. - PodAntiAffinityTermLabelSelector []*structpb.Struct `protobuf:"bytes,13,rep,name=podAntiAffinityTermLabelSelector,proto3" json:"podAntiAffinityTermLabelSelector,omitempty"` - // Ports Configuration for the egress gateway service. - Ports []*PortsConfig `protobuf:"bytes,14,rep,name=ports,proto3" json:"ports,omitempty"` - // K8s resources settings. - // - // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container - // - // Deprecated: Do not use. - Resources *Resources `protobuf:"bytes,15,opt,name=resources,proto3" json:"resources,omitempty"` - // Config for secret volume mounts. - SecretVolumes []*SecretVolume `protobuf:"bytes,16,rep,name=secretVolumes,proto3" json:"secretVolumes,omitempty"` - // Annotations to add to the egress gateway service. - ServiceAnnotations *structpb.Struct `protobuf:"bytes,17,opt,name=serviceAnnotations,proto3" json:"serviceAnnotations,omitempty"` - // Service type. - // - // See https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types - Type string `protobuf:"bytes,18,opt,name=type,proto3" json:"type,omitempty"` - // Enables cross-cluster access using SNI matching. - Zvpn *ZeroVPNConfig `protobuf:"bytes,19,opt,name=zvpn,proto3" json:"zvpn,omitempty"` - // Deprecated: Do not use. - Tolerations []*structpb.Struct `protobuf:"bytes,20,rep,name=tolerations,proto3" json:"tolerations,omitempty"` - // K8s rolling update strategy - // - // Deprecated: Do not use. - RollingMaxSurge *IntOrString `protobuf:"bytes,21,opt,name=rollingMaxSurge,proto3" json:"rollingMaxSurge,omitempty"` - // K8s rolling update strategy - // - // Deprecated: Do not use. - RollingMaxUnavailable *IntOrString `protobuf:"bytes,22,opt,name=rollingMaxUnavailable,proto3" json:"rollingMaxUnavailable,omitempty"` - ConfigVolumes []*structpb.Struct `protobuf:"bytes,23,rep,name=configVolumes,proto3" json:"configVolumes,omitempty"` - AdditionalContainers []*structpb.Struct `protobuf:"bytes,24,rep,name=additionalContainers,proto3" json:"additionalContainers,omitempty"` - RunAsRoot *wrapperspb.BoolValue `protobuf:"bytes,26,opt,name=runAsRoot,proto3" json:"runAsRoot,omitempty"` - // The injection template to use for the gateway. If not set, no injection will be performed. - InjectionTemplate string `protobuf:"bytes,27,opt,name=injectionTemplate,proto3" json:"injectionTemplate,omitempty"` - ServiceAccount *ServiceAccount `protobuf:"bytes,28,opt,name=serviceAccount,proto3" json:"serviceAccount,omitempty"` -} - -func (x *EgressGatewayConfig) Reset() { - *x = EgressGatewayConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *EgressGatewayConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*EgressGatewayConfig) ProtoMessage() {} - -func (x *EgressGatewayConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use EgressGatewayConfig.ProtoReflect.Descriptor instead. -func (*EgressGatewayConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{10} -} - -func (x *EgressGatewayConfig) GetAutoscaleEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.AutoscaleEnabled - } - return nil -} - -func (x *EgressGatewayConfig) GetAutoscaleMax() uint32 { - if x != nil { - return x.AutoscaleMax - } - return 0 -} - -func (x *EgressGatewayConfig) GetAutoscaleMin() uint32 { - if x != nil { - return x.AutoscaleMin - } - return 0 -} - -// Deprecated: Do not use. -func (x *EgressGatewayConfig) GetCpu() *CPUTargetUtilizationConfig { - if x != nil { - return x.Cpu - } - return nil -} - -func (x *EgressGatewayConfig) GetEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.Enabled - } - return nil -} - -func (x *EgressGatewayConfig) GetEnv() *structpb.Struct { - if x != nil { - return x.Env - } - return nil -} - -func (x *EgressGatewayConfig) GetLabels() map[string]string { - if x != nil { - return x.Labels - } - return nil -} - -func (x *EgressGatewayConfig) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -// Deprecated: Do not use. -func (x *EgressGatewayConfig) GetNodeSelector() *structpb.Struct { - if x != nil { - return x.NodeSelector - } - return nil -} - -// Deprecated: Do not use. -func (x *EgressGatewayConfig) GetPodAnnotations() *structpb.Struct { - if x != nil { - return x.PodAnnotations - } - return nil -} - -// Deprecated: Do not use. -func (x *EgressGatewayConfig) GetPodAntiAffinityLabelSelector() []*structpb.Struct { - if x != nil { - return x.PodAntiAffinityLabelSelector - } - return nil -} - -// Deprecated: Do not use. -func (x *EgressGatewayConfig) GetPodAntiAffinityTermLabelSelector() []*structpb.Struct { - if x != nil { - return x.PodAntiAffinityTermLabelSelector - } - return nil -} - -func (x *EgressGatewayConfig) GetPorts() []*PortsConfig { - if x != nil { - return x.Ports - } - return nil -} - -// Deprecated: Do not use. -func (x *EgressGatewayConfig) GetResources() *Resources { - if x != nil { - return x.Resources - } - return nil -} - -func (x *EgressGatewayConfig) GetSecretVolumes() []*SecretVolume { - if x != nil { - return x.SecretVolumes - } - return nil -} - -func (x *EgressGatewayConfig) GetServiceAnnotations() *structpb.Struct { - if x != nil { - return x.ServiceAnnotations - } - return nil -} - -func (x *EgressGatewayConfig) GetType() string { - if x != nil { - return x.Type - } - return "" -} - -func (x *EgressGatewayConfig) GetZvpn() *ZeroVPNConfig { - if x != nil { - return x.Zvpn - } - return nil -} - -// Deprecated: Do not use. -func (x *EgressGatewayConfig) GetTolerations() []*structpb.Struct { - if x != nil { - return x.Tolerations - } - return nil -} - -// Deprecated: Do not use. -func (x *EgressGatewayConfig) GetRollingMaxSurge() *IntOrString { - if x != nil { - return x.RollingMaxSurge - } - return nil -} - -// Deprecated: Do not use. -func (x *EgressGatewayConfig) GetRollingMaxUnavailable() *IntOrString { - if x != nil { - return x.RollingMaxUnavailable - } - return nil -} - -func (x *EgressGatewayConfig) GetConfigVolumes() []*structpb.Struct { - if x != nil { - return x.ConfigVolumes - } - return nil -} - -func (x *EgressGatewayConfig) GetAdditionalContainers() []*structpb.Struct { - if x != nil { - return x.AdditionalContainers - } - return nil -} - -func (x *EgressGatewayConfig) GetRunAsRoot() *wrapperspb.BoolValue { - if x != nil { - return x.RunAsRoot - } - return nil -} - -func (x *EgressGatewayConfig) GetInjectionTemplate() string { - if x != nil { - return x.InjectionTemplate - } - return "" -} - -func (x *EgressGatewayConfig) GetServiceAccount() *ServiceAccount { - if x != nil { - return x.ServiceAccount - } - return nil -} - -// Configuration for gateways. -type GatewaysConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Configuration for an egress gateway. - IstioEgressgateway *EgressGatewayConfig `protobuf:"bytes,1,opt,name=istio_egressgateway,json=istio-egressgateway,proto3" json:"istio_egressgateway,omitempty"` - // Controls whether any gateways are enabled. - Enabled *wrapperspb.BoolValue `protobuf:"bytes,2,opt,name=enabled,proto3" json:"enabled,omitempty"` - // Configuration for an ingress gateway. - IstioIngressgateway *IngressGatewayConfig `protobuf:"bytes,4,opt,name=istio_ingressgateway,json=istio-ingressgateway,proto3" json:"istio_ingressgateway,omitempty"` -} - -func (x *GatewaysConfig) Reset() { - *x = GatewaysConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GatewaysConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GatewaysConfig) ProtoMessage() {} - -func (x *GatewaysConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GatewaysConfig.ProtoReflect.Descriptor instead. -func (*GatewaysConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{11} -} - -func (x *GatewaysConfig) GetIstioEgressgateway() *EgressGatewayConfig { - if x != nil { - return x.IstioEgressgateway - } - return nil -} - -func (x *GatewaysConfig) GetEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.Enabled - } - return nil -} - -func (x *GatewaysConfig) GetIstioIngressgateway() *IngressGatewayConfig { - if x != nil { - return x.IstioIngressgateway - } - return nil -} - -// Global Configuration for Istio components. -type GlobalConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Specifies pod scheduling arch(amd64, ppc64le, s390x, arm64) and weight as follows: - // 0 - Never scheduled - // 1 - Least preferred - // 2 - No preference - // 3 - Most preferred - // - // Deprecated: replaced by the affinity k8s settings which allows architecture nodeAffinity configuration of this behavior. - // - // Deprecated: Do not use. - Arch *ArchConfig `protobuf:"bytes,1,opt,name=arch,proto3" json:"arch,omitempty"` - ConfigRootNamespace string `protobuf:"bytes,50,opt,name=configRootNamespace,proto3" json:"configRootNamespace,omitempty"` - // Controls whether the server-side validation is enabled. - ConfigValidation *wrapperspb.BoolValue `protobuf:"bytes,3,opt,name=configValidation,proto3" json:"configValidation,omitempty"` - DefaultConfigVisibilitySettings []string `protobuf:"bytes,52,rep,name=defaultConfigVisibilitySettings,proto3" json:"defaultConfigVisibilitySettings,omitempty"` - // Default k8s node selector for all the Istio control plane components - // - // See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector - // - // Deprecated: Do not use. - DefaultNodeSelector *structpb.Struct `protobuf:"bytes,6,opt,name=defaultNodeSelector,proto3" json:"defaultNodeSelector,omitempty"` - // Specifies the default pod disruption budget configuration. - // - // Deprecated: Do not use. - DefaultPodDisruptionBudget *DefaultPodDisruptionBudgetConfig `protobuf:"bytes,7,opt,name=defaultPodDisruptionBudget,proto3" json:"defaultPodDisruptionBudget,omitempty"` - // Default k8s resources settings for all Istio control plane components. - // - // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container - // - // Deprecated: Do not use. - DefaultResources *DefaultResourcesConfig `protobuf:"bytes,9,opt,name=defaultResources,proto3" json:"defaultResources,omitempty"` - // Deprecated: Do not use. - DefaultTolerations []*structpb.Struct `protobuf:"bytes,55,rep,name=defaultTolerations,proto3" json:"defaultTolerations,omitempty"` - // Specifies the docker hub for Istio images. - Hub string `protobuf:"bytes,12,opt,name=hub,proto3" json:"hub,omitempty"` - // Specifies the image pull policy for the Istio images. one of Always, Never, IfNotPresent. - // Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. - // - // More info: https://kubernetes.io/docs/concepts/containers/images#updating-images - ImagePullPolicy string `protobuf:"bytes,13,opt,name=imagePullPolicy,proto3" json:"imagePullPolicy,omitempty"` // ImagePullPolicy v1.PullPolicy `json:"imagePullPolicy,omitempty"` - ImagePullSecrets []string `protobuf:"bytes,37,rep,name=imagePullSecrets,proto3" json:"imagePullSecrets,omitempty"` - // Specifies the default namespace for the Istio control plane components. - IstioNamespace string `protobuf:"bytes,14,opt,name=istioNamespace,proto3" json:"istioNamespace,omitempty"` - LogAsJson *wrapperspb.BoolValue `protobuf:"bytes,36,opt,name=logAsJson,proto3" json:"logAsJson,omitempty"` - // Specifies the global logging level settings for the Istio control plane components. - Logging *GlobalLoggingConfig `protobuf:"bytes,17,opt,name=logging,proto3" json:"logging,omitempty"` - MeshID string `protobuf:"bytes,53,opt,name=meshID,proto3" json:"meshID,omitempty"` - // Configure the mesh networks to be used by the Split Horizon EDS. - // - // The following example defines two networks with different endpoints association methods. - // For `network1` all endpoints that their IP belongs to the provided CIDR range will be - // mapped to network1. The gateway for this network example is specified by its public IP - // address and port. - // The second network, `network2`, in this example is defined differently with all endpoints - // retrieved through the specified Multi-Cluster registry being mapped to network2. The - // gateway is also defined differently with the name of the gateway service on the remote - // cluster. The public IP for the gateway will be determined from that remote service (only - // LoadBalancer gateway service type is currently supported, for a NodePort type gateway service, - // it still need to be configured manually). - // - // meshNetworks: - // network1: - // endpoints: - // - fromCidr: "192.168.0.1/24" - // gateways: - // - address: 1.1.1.1 - // port: 80 - // network2: - // endpoints: - // - fromRegistry: reg1 - // gateways: - // - registryServiceName: istio-ingressgateway.dubbo-system.svc.cluster.local - // port: 443 - // - MeshNetworks *structpb.Struct `protobuf:"bytes,19,opt,name=meshNetworks,proto3" json:"meshNetworks,omitempty"` - // Specifies the Configuration for Istio mesh across multiple clusters through Istio gateways. - MultiCluster *MultiClusterConfig `protobuf:"bytes,22,opt,name=multiCluster,proto3" json:"multiCluster,omitempty"` - Network string `protobuf:"bytes,39,opt,name=network,proto3" json:"network,omitempty"` - // Custom DNS config for the pod to resolve names of services in other - // clusters. Use this to add additional search domains, and other settings. - // see https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#dns-config - // This does not apply to gateway pods as they typically need a different - // set of DNS settings than the normal application pods (e.g. in multicluster scenarios). - PodDNSSearchNamespaces []string `protobuf:"bytes,43,rep,name=podDNSSearchNamespaces,proto3" json:"podDNSSearchNamespaces,omitempty"` - OmitSidecarInjectorConfigMap *wrapperspb.BoolValue `protobuf:"bytes,38,opt,name=omitSidecarInjectorConfigMap,proto3" json:"omitSidecarInjectorConfigMap,omitempty"` - // Controls whether to restrict the applications namespace the controller manages; - // If set it to false, the controller watches all namespaces. - OneNamespace *wrapperspb.BoolValue `protobuf:"bytes,23,opt,name=oneNamespace,proto3" json:"oneNamespace,omitempty"` - OperatorManageWebhooks *wrapperspb.BoolValue `protobuf:"bytes,41,opt,name=operatorManageWebhooks,proto3" json:"operatorManageWebhooks,omitempty"` - // Specifies the k8s priorityClassName for the istio control plane components. - // - // See https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass - // - // Deprecated: Do not use. - PriorityClassName string `protobuf:"bytes,27,opt,name=priorityClassName,proto3" json:"priorityClassName,omitempty"` - // Specifies how proxies are configured within Istio. - Proxy *ProxyConfig `protobuf:"bytes,28,opt,name=proxy,proto3" json:"proxy,omitempty"` - // Specifies the Configuration for proxy_init container which sets the pods' networking to intercept the inbound/outbound traffic. - ProxyInit *ProxyInitConfig `protobuf:"bytes,29,opt,name=proxy_init,proto3" json:"proxy_init,omitempty"` - // Specifies the Configuration for the SecretDiscoveryService instead of using K8S secrets to mount the certificates. - Sds *SDSConfig `protobuf:"bytes,30,opt,name=sds,proto3" json:"sds,omitempty"` - // Specifies the tag for the Istio docker images. - Tag *structpb.Value `protobuf:"bytes,31,opt,name=tag,proto3" json:"tag,omitempty"` - // Specifies the Configuration for each of the supported tracers. - Tracer *TracerConfig `protobuf:"bytes,33,opt,name=tracer,proto3" json:"tracer,omitempty"` - // Controls whether to use of Mesh Configuration Protocol to distribute configuration. - UseMCP *wrapperspb.BoolValue `protobuf:"bytes,35,opt,name=useMCP,proto3" json:"useMCP,omitempty"` - // Specifies the Istio control plane’s pilot Pod IP address or remote cluster DNS resolvable hostname. - RemotePilotAddress string `protobuf:"bytes,48,opt,name=remotePilotAddress,proto3" json:"remotePilotAddress,omitempty"` - // Specifies the configution of istiod - Istiod *IstiodConfig `protobuf:"bytes,54,opt,name=istiod,proto3" json:"istiod,omitempty"` - // Configure the Pilot certificate provider. - // Currently, four providers are supported: "kubernetes", "istiod", "custom" and "none". - PilotCertProvider string `protobuf:"bytes,56,opt,name=pilotCertProvider,proto3" json:"pilotCertProvider,omitempty"` - // Configure the policy for validating JWT. - // Currently, two options are supported: "third-party-jwt" and "first-party-jwt". - JwtPolicy string `protobuf:"bytes,57,opt,name=jwtPolicy,proto3" json:"jwtPolicy,omitempty"` - // Specifies the configuration for Security Token Service. - Sts *STSConfig `protobuf:"bytes,58,opt,name=sts,proto3" json:"sts,omitempty"` - // Configures the revision this control plane is a part of - Revision string `protobuf:"bytes,59,opt,name=revision,proto3" json:"revision,omitempty"` - // Controls whether the in-cluster MTLS key and certs are loaded from the secret volume mounts. - MountMtlsCerts *wrapperspb.BoolValue `protobuf:"bytes,60,opt,name=mountMtlsCerts,proto3" json:"mountMtlsCerts,omitempty"` - // The address of the CA for CSR. - CaAddress string `protobuf:"bytes,61,opt,name=caAddress,proto3" json:"caAddress,omitempty"` - // Controls whether one external istiod is enabled. - ExternalIstiod *wrapperspb.BoolValue `protobuf:"bytes,62,opt,name=externalIstiod,proto3" json:"externalIstiod,omitempty"` - // Controls whether a remote cluster is the config cluster for an external istiod - ConfigCluster *wrapperspb.BoolValue `protobuf:"bytes,64,opt,name=configCluster,proto3" json:"configCluster,omitempty"` - // The name of the CA for workloads. - // For example, when caName=GkeWorkloadCertificate, GKE workload certificates - // will be used as the certificates for workloads. - // The default value is "" and when caName="", the CA will be configured by other - // mechanisms (e.g., environmental variable CA_PROVIDER). - CaName string `protobuf:"bytes,65,opt,name=caName,proto3" json:"caName,omitempty"` - Autoscalingv2API *wrapperspb.BoolValue `protobuf:"bytes,66,opt,name=autoscalingv2API,proto3" json:"autoscalingv2API,omitempty"` // The next available key is 67 -} - -func (x *GlobalConfig) Reset() { - *x = GlobalConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GlobalConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GlobalConfig) ProtoMessage() {} - -func (x *GlobalConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[12] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GlobalConfig.ProtoReflect.Descriptor instead. -func (*GlobalConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{12} -} - -// Deprecated: Do not use. -func (x *GlobalConfig) GetArch() *ArchConfig { - if x != nil { - return x.Arch - } - return nil -} - -func (x *GlobalConfig) GetConfigRootNamespace() string { - if x != nil { - return x.ConfigRootNamespace - } - return "" -} - -func (x *GlobalConfig) GetConfigValidation() *wrapperspb.BoolValue { - if x != nil { - return x.ConfigValidation - } - return nil -} - -func (x *GlobalConfig) GetDefaultConfigVisibilitySettings() []string { - if x != nil { - return x.DefaultConfigVisibilitySettings - } - return nil -} - -// Deprecated: Do not use. -func (x *GlobalConfig) GetDefaultNodeSelector() *structpb.Struct { - if x != nil { - return x.DefaultNodeSelector - } - return nil -} - -// Deprecated: Do not use. -func (x *GlobalConfig) GetDefaultPodDisruptionBudget() *DefaultPodDisruptionBudgetConfig { - if x != nil { - return x.DefaultPodDisruptionBudget - } - return nil -} - -// Deprecated: Do not use. -func (x *GlobalConfig) GetDefaultResources() *DefaultResourcesConfig { - if x != nil { - return x.DefaultResources - } - return nil -} - -// Deprecated: Do not use. -func (x *GlobalConfig) GetDefaultTolerations() []*structpb.Struct { - if x != nil { - return x.DefaultTolerations - } - return nil -} - -func (x *GlobalConfig) GetHub() string { - if x != nil { - return x.Hub - } - return "" -} - -func (x *GlobalConfig) GetImagePullPolicy() string { - if x != nil { - return x.ImagePullPolicy - } - return "" -} - -func (x *GlobalConfig) GetImagePullSecrets() []string { - if x != nil { - return x.ImagePullSecrets - } - return nil -} - -func (x *GlobalConfig) GetIstioNamespace() string { - if x != nil { - return x.IstioNamespace - } - return "" -} - -func (x *GlobalConfig) GetLogAsJson() *wrapperspb.BoolValue { - if x != nil { - return x.LogAsJson - } - return nil -} - -func (x *GlobalConfig) GetLogging() *GlobalLoggingConfig { - if x != nil { - return x.Logging - } - return nil -} - -func (x *GlobalConfig) GetMeshID() string { - if x != nil { - return x.MeshID - } - return "" -} - -func (x *GlobalConfig) GetMeshNetworks() *structpb.Struct { - if x != nil { - return x.MeshNetworks - } - return nil -} - -func (x *GlobalConfig) GetMultiCluster() *MultiClusterConfig { - if x != nil { - return x.MultiCluster - } - return nil -} - -func (x *GlobalConfig) GetNetwork() string { - if x != nil { - return x.Network - } - return "" -} - -func (x *GlobalConfig) GetPodDNSSearchNamespaces() []string { - if x != nil { - return x.PodDNSSearchNamespaces - } - return nil -} - -func (x *GlobalConfig) GetOmitSidecarInjectorConfigMap() *wrapperspb.BoolValue { - if x != nil { - return x.OmitSidecarInjectorConfigMap - } - return nil -} - -func (x *GlobalConfig) GetOneNamespace() *wrapperspb.BoolValue { - if x != nil { - return x.OneNamespace - } - return nil -} - -func (x *GlobalConfig) GetOperatorManageWebhooks() *wrapperspb.BoolValue { - if x != nil { - return x.OperatorManageWebhooks - } - return nil -} - -// Deprecated: Do not use. -func (x *GlobalConfig) GetPriorityClassName() string { - if x != nil { - return x.PriorityClassName - } - return "" -} - -func (x *GlobalConfig) GetProxy() *ProxyConfig { - if x != nil { - return x.Proxy - } - return nil -} - -func (x *GlobalConfig) GetProxyInit() *ProxyInitConfig { - if x != nil { - return x.ProxyInit - } - return nil -} - -func (x *GlobalConfig) GetSds() *SDSConfig { - if x != nil { - return x.Sds - } - return nil -} - -func (x *GlobalConfig) GetTag() *structpb.Value { - if x != nil { - return x.Tag - } - return nil -} - -func (x *GlobalConfig) GetTracer() *TracerConfig { - if x != nil { - return x.Tracer - } - return nil -} - -func (x *GlobalConfig) GetUseMCP() *wrapperspb.BoolValue { - if x != nil { - return x.UseMCP - } - return nil -} - -func (x *GlobalConfig) GetRemotePilotAddress() string { - if x != nil { - return x.RemotePilotAddress - } - return "" -} - -func (x *GlobalConfig) GetIstiod() *IstiodConfig { - if x != nil { - return x.Istiod - } - return nil -} - -func (x *GlobalConfig) GetPilotCertProvider() string { - if x != nil { - return x.PilotCertProvider - } - return "" -} - -func (x *GlobalConfig) GetJwtPolicy() string { - if x != nil { - return x.JwtPolicy - } - return "" -} - -func (x *GlobalConfig) GetSts() *STSConfig { - if x != nil { - return x.Sts - } - return nil -} - -func (x *GlobalConfig) GetRevision() string { - if x != nil { - return x.Revision - } - return "" -} - -func (x *GlobalConfig) GetMountMtlsCerts() *wrapperspb.BoolValue { - if x != nil { - return x.MountMtlsCerts - } - return nil -} - -func (x *GlobalConfig) GetCaAddress() string { - if x != nil { - return x.CaAddress - } - return "" -} - -func (x *GlobalConfig) GetExternalIstiod() *wrapperspb.BoolValue { - if x != nil { - return x.ExternalIstiod - } - return nil -} - -func (x *GlobalConfig) GetConfigCluster() *wrapperspb.BoolValue { - if x != nil { - return x.ConfigCluster - } - return nil -} - -func (x *GlobalConfig) GetCaName() string { - if x != nil { - return x.CaName - } - return "" -} - -func (x *GlobalConfig) GetAutoscalingv2API() *wrapperspb.BoolValue { - if x != nil { - return x.Autoscalingv2API - } - return nil -} - -// Configuration for Security Token Service (STS) server. -// -// See https://tools.ietf.org/html/draft-ietf-oauth-token-exchange-16 -type STSConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ServicePort uint32 `protobuf:"varint,1,opt,name=servicePort,proto3" json:"servicePort,omitempty"` -} - -func (x *STSConfig) Reset() { - *x = STSConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *STSConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*STSConfig) ProtoMessage() {} - -func (x *STSConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[13] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use STSConfig.ProtoReflect.Descriptor instead. -func (*STSConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{13} -} - -func (x *STSConfig) GetServicePort() uint32 { - if x != nil { - return x.ServicePort - } - return 0 -} - -type IstiodConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // If enabled, istiod will perform config analysis - EnableAnalysis *wrapperspb.BoolValue `protobuf:"bytes,2,opt,name=enableAnalysis,proto3" json:"enableAnalysis,omitempty"` -} - -func (x *IstiodConfig) Reset() { - *x = IstiodConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *IstiodConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*IstiodConfig) ProtoMessage() {} - -func (x *IstiodConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[14] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use IstiodConfig.ProtoReflect.Descriptor instead. -func (*IstiodConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{14} -} - -func (x *IstiodConfig) GetEnableAnalysis() *wrapperspb.BoolValue { - if x != nil { - return x.EnableAnalysis - } - return nil -} - -// GlobalLoggingConfig specifies the global logging level settings for the Istio control plane components. -type GlobalLoggingConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Comma-separated minimum per-scope logging level of messages to output, in the form of :,: - // The control plane has different scopes depending on component, but can configure default log level across all components - // If empty, default scope and level will be used as configured in code - Level string `protobuf:"bytes,1,opt,name=level,proto3" json:"level,omitempty"` -} - -func (x *GlobalLoggingConfig) Reset() { - *x = GlobalLoggingConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GlobalLoggingConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GlobalLoggingConfig) ProtoMessage() {} - -func (x *GlobalLoggingConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[15] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GlobalLoggingConfig.ProtoReflect.Descriptor instead. -func (*GlobalLoggingConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{15} -} - -func (x *GlobalLoggingConfig) GetLevel() string { - if x != nil { - return x.Level - } - return "" -} - -// Configuration for an ingress gateway. -type IngressGatewayConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Controls whether auto scaling with a HorizontalPodAutoscaler is enabled. - AutoscaleEnabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=autoscaleEnabled,proto3" json:"autoscaleEnabled,omitempty"` - // maxReplicas setting for HorizontalPodAutoscaler. - AutoscaleMax uint32 `protobuf:"varint,2,opt,name=autoscaleMax,proto3" json:"autoscaleMax,omitempty"` - // minReplicas setting for HorizontalPodAutoscaler. - AutoscaleMin uint32 `protobuf:"varint,3,opt,name=autoscaleMin,proto3" json:"autoscaleMin,omitempty"` - // K8s utilization setting for HorizontalPodAutoscaler target. - // - // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ - // - // Deprecated: Do not use. - Cpu *CPUTargetUtilizationConfig `protobuf:"bytes,5,opt,name=cpu,proto3" json:"cpu,omitempty"` - CustomService *wrapperspb.BoolValue `protobuf:"bytes,6,opt,name=customService,proto3" json:"customService,omitempty"` - // Controls whether an ingress gateway is enabled. - Enabled *wrapperspb.BoolValue `protobuf:"bytes,10,opt,name=enabled,proto3" json:"enabled,omitempty"` - // Environment variables passed to the proxy container. - Env *structpb.Struct `protobuf:"bytes,11,opt,name=env,proto3" json:"env,omitempty"` - Labels map[string]string `protobuf:"bytes,15,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - LoadBalancerIP string `protobuf:"bytes,16,opt,name=loadBalancerIP,proto3" json:"loadBalancerIP,omitempty"` - LoadBalancerSourceRanges []string `protobuf:"bytes,17,rep,name=loadBalancerSourceRanges,proto3" json:"loadBalancerSourceRanges,omitempty"` - Name string `protobuf:"bytes,44,opt,name=name,proto3" json:"name,omitempty"` - // K8s node selector. - // - // See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector - // - // Deprecated: Do not use. - NodeSelector *structpb.Struct `protobuf:"bytes,19,opt,name=nodeSelector,proto3" json:"nodeSelector,omitempty"` - // K8s annotations for pods. - // - // See: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ - // - // Deprecated: Do not use. - PodAnnotations *structpb.Struct `protobuf:"bytes,20,opt,name=podAnnotations,proto3" json:"podAnnotations,omitempty"` - // See EgressGatewayConfig. - // - // Deprecated: Do not use. - PodAntiAffinityLabelSelector []*structpb.Struct `protobuf:"bytes,21,rep,name=podAntiAffinityLabelSelector,proto3" json:"podAntiAffinityLabelSelector,omitempty"` - // See EgressGatewayConfig. - // - // Deprecated: Do not use. - PodAntiAffinityTermLabelSelector []*structpb.Struct `protobuf:"bytes,22,rep,name=podAntiAffinityTermLabelSelector,proto3" json:"podAntiAffinityTermLabelSelector,omitempty"` - // Port Configuration for the ingress gateway. - Ports []*PortsConfig `protobuf:"bytes,23,rep,name=ports,proto3" json:"ports,omitempty"` - // Number of replicas for the ingress gateway Deployment. - // - // Deprecated: Do not use. - ReplicaCount uint32 `protobuf:"varint,24,opt,name=replicaCount,proto3" json:"replicaCount,omitempty"` - // K8s resources settings. - // - // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container - // - // Deprecated: Do not use. - Resources *structpb.Struct `protobuf:"bytes,25,opt,name=resources,proto3" json:"resources,omitempty"` - // Config for secret volume mounts. - SecretVolumes []*SecretVolume `protobuf:"bytes,27,rep,name=secretVolumes,proto3" json:"secretVolumes,omitempty"` - // Annotations to add to the egress gateway service. - ServiceAnnotations *structpb.Struct `protobuf:"bytes,28,opt,name=serviceAnnotations,proto3" json:"serviceAnnotations,omitempty"` - // Service type. - // - // See https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types - Type string `protobuf:"bytes,29,opt,name=type,proto3" json:"type,omitempty"` - // Enables cross-cluster access using SNI matching. - Zvpn *IngressGatewayZvpnConfig `protobuf:"bytes,30,opt,name=zvpn,proto3" json:"zvpn,omitempty"` - // K8s rolling update strategy - // - // Deprecated: Do not use. - RollingMaxSurge *IntOrString `protobuf:"bytes,31,opt,name=rollingMaxSurge,proto3" json:"rollingMaxSurge,omitempty"` - // K8s rolling update strategy - // - // Deprecated: Do not use. - RollingMaxUnavailable *IntOrString `protobuf:"bytes,32,opt,name=rollingMaxUnavailable,proto3" json:"rollingMaxUnavailable,omitempty"` - ExternalTrafficPolicy string `protobuf:"bytes,34,opt,name=externalTrafficPolicy,proto3" json:"externalTrafficPolicy,omitempty"` - // Deprecated: Do not use. - Tolerations []*structpb.Struct `protobuf:"bytes,35,rep,name=tolerations,proto3" json:"tolerations,omitempty"` - IngressPorts []*structpb.Struct `protobuf:"bytes,36,rep,name=ingressPorts,proto3" json:"ingressPorts,omitempty"` - AdditionalContainers []*structpb.Struct `protobuf:"bytes,37,rep,name=additionalContainers,proto3" json:"additionalContainers,omitempty"` - ConfigVolumes []*structpb.Struct `protobuf:"bytes,38,rep,name=configVolumes,proto3" json:"configVolumes,omitempty"` - RunAsRoot *wrapperspb.BoolValue `protobuf:"bytes,45,opt,name=runAsRoot,proto3" json:"runAsRoot,omitempty"` - // The injection template to use for the gateway. If not set, no injection will be performed. - InjectionTemplate string `protobuf:"bytes,46,opt,name=injectionTemplate,proto3" json:"injectionTemplate,omitempty"` - ServiceAccount *ServiceAccount `protobuf:"bytes,47,opt,name=serviceAccount,proto3" json:"serviceAccount,omitempty"` -} - -func (x *IngressGatewayConfig) Reset() { - *x = IngressGatewayConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[16] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *IngressGatewayConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*IngressGatewayConfig) ProtoMessage() {} - -func (x *IngressGatewayConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[16] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use IngressGatewayConfig.ProtoReflect.Descriptor instead. -func (*IngressGatewayConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{16} -} - -func (x *IngressGatewayConfig) GetAutoscaleEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.AutoscaleEnabled - } - return nil -} - -func (x *IngressGatewayConfig) GetAutoscaleMax() uint32 { - if x != nil { - return x.AutoscaleMax - } - return 0 -} - -func (x *IngressGatewayConfig) GetAutoscaleMin() uint32 { - if x != nil { - return x.AutoscaleMin - } - return 0 -} - -// Deprecated: Do not use. -func (x *IngressGatewayConfig) GetCpu() *CPUTargetUtilizationConfig { - if x != nil { - return x.Cpu - } - return nil -} - -func (x *IngressGatewayConfig) GetCustomService() *wrapperspb.BoolValue { - if x != nil { - return x.CustomService - } - return nil -} - -func (x *IngressGatewayConfig) GetEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.Enabled - } - return nil -} - -func (x *IngressGatewayConfig) GetEnv() *structpb.Struct { - if x != nil { - return x.Env - } - return nil -} - -func (x *IngressGatewayConfig) GetLabels() map[string]string { - if x != nil { - return x.Labels - } - return nil -} - -func (x *IngressGatewayConfig) GetLoadBalancerIP() string { - if x != nil { - return x.LoadBalancerIP - } - return "" -} - -func (x *IngressGatewayConfig) GetLoadBalancerSourceRanges() []string { - if x != nil { - return x.LoadBalancerSourceRanges - } - return nil -} - -func (x *IngressGatewayConfig) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -// Deprecated: Do not use. -func (x *IngressGatewayConfig) GetNodeSelector() *structpb.Struct { - if x != nil { - return x.NodeSelector - } - return nil -} - -// Deprecated: Do not use. -func (x *IngressGatewayConfig) GetPodAnnotations() *structpb.Struct { - if x != nil { - return x.PodAnnotations - } - return nil -} - -// Deprecated: Do not use. -func (x *IngressGatewayConfig) GetPodAntiAffinityLabelSelector() []*structpb.Struct { - if x != nil { - return x.PodAntiAffinityLabelSelector - } - return nil -} - -// Deprecated: Do not use. -func (x *IngressGatewayConfig) GetPodAntiAffinityTermLabelSelector() []*structpb.Struct { - if x != nil { - return x.PodAntiAffinityTermLabelSelector - } - return nil -} - -func (x *IngressGatewayConfig) GetPorts() []*PortsConfig { - if x != nil { - return x.Ports - } - return nil -} - -// Deprecated: Do not use. -func (x *IngressGatewayConfig) GetReplicaCount() uint32 { - if x != nil { - return x.ReplicaCount - } - return 0 -} - -// Deprecated: Do not use. -func (x *IngressGatewayConfig) GetResources() *structpb.Struct { - if x != nil { - return x.Resources - } - return nil -} - -func (x *IngressGatewayConfig) GetSecretVolumes() []*SecretVolume { - if x != nil { - return x.SecretVolumes - } - return nil -} - -func (x *IngressGatewayConfig) GetServiceAnnotations() *structpb.Struct { - if x != nil { - return x.ServiceAnnotations - } - return nil -} - -func (x *IngressGatewayConfig) GetType() string { - if x != nil { - return x.Type - } - return "" -} - -func (x *IngressGatewayConfig) GetZvpn() *IngressGatewayZvpnConfig { - if x != nil { - return x.Zvpn - } - return nil -} - -// Deprecated: Do not use. -func (x *IngressGatewayConfig) GetRollingMaxSurge() *IntOrString { - if x != nil { - return x.RollingMaxSurge - } - return nil -} - -// Deprecated: Do not use. -func (x *IngressGatewayConfig) GetRollingMaxUnavailable() *IntOrString { - if x != nil { - return x.RollingMaxUnavailable - } - return nil -} - -func (x *IngressGatewayConfig) GetExternalTrafficPolicy() string { - if x != nil { - return x.ExternalTrafficPolicy - } - return "" -} - -// Deprecated: Do not use. -func (x *IngressGatewayConfig) GetTolerations() []*structpb.Struct { - if x != nil { - return x.Tolerations - } - return nil -} - -func (x *IngressGatewayConfig) GetIngressPorts() []*structpb.Struct { - if x != nil { - return x.IngressPorts - } - return nil -} - -func (x *IngressGatewayConfig) GetAdditionalContainers() []*structpb.Struct { - if x != nil { - return x.AdditionalContainers - } - return nil -} - -func (x *IngressGatewayConfig) GetConfigVolumes() []*structpb.Struct { - if x != nil { - return x.ConfigVolumes - } - return nil -} - -func (x *IngressGatewayConfig) GetRunAsRoot() *wrapperspb.BoolValue { - if x != nil { - return x.RunAsRoot - } - return nil -} - -func (x *IngressGatewayConfig) GetInjectionTemplate() string { - if x != nil { - return x.InjectionTemplate - } - return "" -} - -func (x *IngressGatewayConfig) GetServiceAccount() *ServiceAccount { - if x != nil { - return x.ServiceAccount - } - return nil -} - -// IngressGatewayZvpnConfig enables cross-cluster access using SNI matching. -type IngressGatewayZvpnConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Controls whether ZeroVPN is enabled. - Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - Suffix string `protobuf:"bytes,2,opt,name=suffix,proto3" json:"suffix,omitempty"` -} - -func (x *IngressGatewayZvpnConfig) Reset() { - *x = IngressGatewayZvpnConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[17] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *IngressGatewayZvpnConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*IngressGatewayZvpnConfig) ProtoMessage() {} - -func (x *IngressGatewayZvpnConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[17] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use IngressGatewayZvpnConfig.ProtoReflect.Descriptor instead. -func (*IngressGatewayZvpnConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{17} -} - -func (x *IngressGatewayZvpnConfig) GetEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.Enabled - } - return nil -} - -func (x *IngressGatewayZvpnConfig) GetSuffix() string { - if x != nil { - return x.Suffix - } - return "" -} - -// MultiClusterConfig specifies the Configuration for Istio mesh across multiple clusters through the istio gateways. -type MultiClusterConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Enables the connection between two kubernetes clusters via their respective ingressgateway services. - // Use if the pods in each cluster cannot directly talk to one another. - Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - ClusterName string `protobuf:"bytes,2,opt,name=clusterName,proto3" json:"clusterName,omitempty"` - GlobalDomainSuffix string `protobuf:"bytes,3,opt,name=globalDomainSuffix,proto3" json:"globalDomainSuffix,omitempty"` - IncludeEnvoyFilter *wrapperspb.BoolValue `protobuf:"bytes,4,opt,name=includeEnvoyFilter,proto3" json:"includeEnvoyFilter,omitempty"` -} - -func (x *MultiClusterConfig) Reset() { - *x = MultiClusterConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[18] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *MultiClusterConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*MultiClusterConfig) ProtoMessage() {} - -func (x *MultiClusterConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[18] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use MultiClusterConfig.ProtoReflect.Descriptor instead. -func (*MultiClusterConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{18} -} - -func (x *MultiClusterConfig) GetEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.Enabled - } - return nil -} - -func (x *MultiClusterConfig) GetClusterName() string { - if x != nil { - return x.ClusterName - } - return "" -} - -func (x *MultiClusterConfig) GetGlobalDomainSuffix() string { - if x != nil { - return x.GlobalDomainSuffix - } - return "" -} - -func (x *MultiClusterConfig) GetIncludeEnvoyFilter() *wrapperspb.BoolValue { - if x != nil { - return x.IncludeEnvoyFilter - } - return nil -} - -// OutboundTrafficPolicyConfig controls the default behavior of the sidecar for handling outbound traffic from the application. -type OutboundTrafficPolicyConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Mode OutboundTrafficPolicyConfig_Mode `protobuf:"varint,2,opt,name=mode,proto3,enum=v1alpha1.OutboundTrafficPolicyConfig_Mode" json:"mode,omitempty"` -} - -func (x *OutboundTrafficPolicyConfig) Reset() { - *x = OutboundTrafficPolicyConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[19] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *OutboundTrafficPolicyConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OutboundTrafficPolicyConfig) ProtoMessage() {} - -func (x *OutboundTrafficPolicyConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[19] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use OutboundTrafficPolicyConfig.ProtoReflect.Descriptor instead. -func (*OutboundTrafficPolicyConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{19} -} - -func (x *OutboundTrafficPolicyConfig) GetMode() OutboundTrafficPolicyConfig_Mode { - if x != nil { - return x.Mode - } - return OutboundTrafficPolicyConfig_ALLOW_ANY -} - -// Configuration for Pilot. -type PilotConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Controls whether Pilot is enabled. - Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - // Controls whether a HorizontalPodAutoscaler is installed for Pilot. - AutoscaleEnabled *wrapperspb.BoolValue `protobuf:"bytes,2,opt,name=autoscaleEnabled,proto3" json:"autoscaleEnabled,omitempty"` - // Minimum number of replicas in the HorizontalPodAutoscaler for Pilot. - AutoscaleMin uint32 `protobuf:"varint,3,opt,name=autoscaleMin,proto3" json:"autoscaleMin,omitempty"` - // Maximum number of replicas in the HorizontalPodAutoscaler for Pilot. - AutoscaleMax uint32 `protobuf:"varint,4,opt,name=autoscaleMax,proto3" json:"autoscaleMax,omitempty"` - // Number of replicas in the Pilot Deployment. - // - // Deprecated: Do not use. - ReplicaCount uint32 `protobuf:"varint,5,opt,name=replicaCount,proto3" json:"replicaCount,omitempty"` - // Image name used for Pilot. - // - // This can be set either to image name if hub is also set, or can be set to the full hub:name string. - // - // Examples: custom-pilot, docker.io/someuser:custom-pilot - Image string `protobuf:"bytes,6,opt,name=image,proto3" json:"image,omitempty"` - // Trace sampling fraction. - // - // Used to set the fraction of time that traces are sampled. Higher values are more accurate but add CPU overhead. - // - // Allowed values: 0.0 to 1.0 - TraceSampling float64 `protobuf:"fixed64,8,opt,name=traceSampling,proto3" json:"traceSampling,omitempty"` - // K8s resources settings. - // - // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container - // - // Deprecated: Do not use. - Resources *Resources `protobuf:"bytes,9,opt,name=resources,proto3" json:"resources,omitempty"` - // Namespace that the configuration management feature is installed into, if different from Pilot namespace. - ConfigNamespace string `protobuf:"bytes,10,opt,name=configNamespace,proto3" json:"configNamespace,omitempty"` - // Target CPU utilization used in HorizontalPodAutoscaler. - // - // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ - // - // Deprecated: Do not use. - Cpu *CPUTargetUtilizationConfig `protobuf:"bytes,11,opt,name=cpu,proto3" json:"cpu,omitempty"` - // K8s node selector. - // - // See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector - // - // Deprecated: Do not use. - NodeSelector *structpb.Struct `protobuf:"bytes,12,opt,name=nodeSelector,proto3" json:"nodeSelector,omitempty"` - // Maximum duration that a sidecar can be connected to a pilot. - // - // This setting balances out load across pilot instances, but adds some resource overhead. - // - // Examples: 300s, 30m, 1h - KeepaliveMaxServerConnectionAge *durationpb.Duration `protobuf:"bytes,13,opt,name=keepaliveMaxServerConnectionAge,proto3" json:"keepaliveMaxServerConnectionAge,omitempty"` - // Labels that are added to Pilot deployment and pods. - // - // See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ - DeploymentLabels *structpb.Struct `protobuf:"bytes,14,opt,name=deploymentLabels,proto3" json:"deploymentLabels,omitempty"` - PodLabels *structpb.Struct `protobuf:"bytes,36,opt,name=podLabels,proto3" json:"podLabels,omitempty"` - // Configuration settings passed to Pilot as a ConfigMap. - // - // This controls whether the mesh config map, generated from values.yaml is generated. - // If false, pilot wil use default values or user-supplied values, in that order of preference. - ConfigMap *wrapperspb.BoolValue `protobuf:"bytes,18,opt,name=configMap,proto3" json:"configMap,omitempty"` - // Controls whether Pilot is configured through the Mesh Control Protocol (MCP). - // - // If set to true, Pilot requires an MCP server (like Galley) to be installed. - UseMCP *wrapperspb.BoolValue `protobuf:"bytes,20,opt,name=useMCP,proto3" json:"useMCP,omitempty"` - // Environment variables passed to the Pilot container. - // - // Examples: - // env: - // ENV_VAR_1: value1 - // ENV_VAR_2: value2 - Env *structpb.Struct `protobuf:"bytes,21,opt,name=env,proto3" json:"env,omitempty"` - // K8s rolling update strategy - // - // Deprecated: Do not use. - RollingMaxSurge *IntOrString `protobuf:"bytes,24,opt,name=rollingMaxSurge,proto3" json:"rollingMaxSurge,omitempty"` - // K8s rolling update strategy - // - // Deprecated: Do not use. - RollingMaxUnavailable *IntOrString `protobuf:"bytes,25,opt,name=rollingMaxUnavailable,proto3" json:"rollingMaxUnavailable,omitempty"` - // - // - // Deprecated: Do not use. - Tolerations []*structpb.Struct `protobuf:"bytes,26,rep,name=tolerations,proto3" json:"tolerations,omitempty"` - // if protocol sniffing is enabled for outbound - EnableProtocolSniffingForOutbound *wrapperspb.BoolValue `protobuf:"bytes,28,opt,name=enableProtocolSniffingForOutbound,proto3" json:"enableProtocolSniffingForOutbound,omitempty"` - // if protocol sniffing is enabled for inbound - EnableProtocolSniffingForInbound *wrapperspb.BoolValue `protobuf:"bytes,29,opt,name=enableProtocolSniffingForInbound,proto3" json:"enableProtocolSniffingForInbound,omitempty"` - // K8s annotations for pods. - // - // See: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ - // - // Deprecated: Do not use. - PodAnnotations *structpb.Struct `protobuf:"bytes,30,opt,name=podAnnotations,proto3" json:"podAnnotations,omitempty"` - ServiceAnnotations *structpb.Struct `protobuf:"bytes,37,opt,name=serviceAnnotations,proto3" json:"serviceAnnotations,omitempty"` - // ConfigSource describes a source of configuration data for networking - // rules, and other Istio configuration artifacts. Multiple data sources - // can be configured for a single control plane. - ConfigSource *PilotConfigSource `protobuf:"bytes,31,opt,name=configSource,proto3" json:"configSource,omitempty"` - JwksResolverExtraRootCA string `protobuf:"bytes,32,opt,name=jwksResolverExtraRootCA,proto3" json:"jwksResolverExtraRootCA,omitempty"` - Plugins []string `protobuf:"bytes,33,rep,name=plugins,proto3" json:"plugins,omitempty"` - Hub string `protobuf:"bytes,34,opt,name=hub,proto3" json:"hub,omitempty"` - Tag *structpb.Value `protobuf:"bytes,35,opt,name=tag,proto3" json:"tag,omitempty"` -} - -func (x *PilotConfig) Reset() { - *x = PilotConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[20] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PilotConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PilotConfig) ProtoMessage() {} - -func (x *PilotConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[20] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PilotConfig.ProtoReflect.Descriptor instead. -func (*PilotConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{20} -} - -func (x *PilotConfig) GetEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.Enabled - } - return nil -} - -func (x *PilotConfig) GetAutoscaleEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.AutoscaleEnabled - } - return nil -} - -func (x *PilotConfig) GetAutoscaleMin() uint32 { - if x != nil { - return x.AutoscaleMin - } - return 0 -} - -func (x *PilotConfig) GetAutoscaleMax() uint32 { - if x != nil { - return x.AutoscaleMax - } - return 0 -} - -// Deprecated: Do not use. -func (x *PilotConfig) GetReplicaCount() uint32 { - if x != nil { - return x.ReplicaCount - } - return 0 -} - -func (x *PilotConfig) GetImage() string { - if x != nil { - return x.Image - } - return "" -} - -func (x *PilotConfig) GetTraceSampling() float64 { - if x != nil { - return x.TraceSampling - } - return 0 -} - -// Deprecated: Do not use. -func (x *PilotConfig) GetResources() *Resources { - if x != nil { - return x.Resources - } - return nil -} - -func (x *PilotConfig) GetConfigNamespace() string { - if x != nil { - return x.ConfigNamespace - } - return "" -} - -// Deprecated: Do not use. -func (x *PilotConfig) GetCpu() *CPUTargetUtilizationConfig { - if x != nil { - return x.Cpu - } - return nil -} - -// Deprecated: Do not use. -func (x *PilotConfig) GetNodeSelector() *structpb.Struct { - if x != nil { - return x.NodeSelector - } - return nil -} - -func (x *PilotConfig) GetKeepaliveMaxServerConnectionAge() *durationpb.Duration { - if x != nil { - return x.KeepaliveMaxServerConnectionAge - } - return nil -} - -func (x *PilotConfig) GetDeploymentLabels() *structpb.Struct { - if x != nil { - return x.DeploymentLabels - } - return nil -} - -func (x *PilotConfig) GetPodLabels() *structpb.Struct { - if x != nil { - return x.PodLabels - } - return nil -} - -func (x *PilotConfig) GetConfigMap() *wrapperspb.BoolValue { - if x != nil { - return x.ConfigMap - } - return nil -} - -func (x *PilotConfig) GetUseMCP() *wrapperspb.BoolValue { - if x != nil { - return x.UseMCP - } - return nil -} - -func (x *PilotConfig) GetEnv() *structpb.Struct { - if x != nil { - return x.Env - } - return nil -} - -// Deprecated: Do not use. -func (x *PilotConfig) GetRollingMaxSurge() *IntOrString { - if x != nil { - return x.RollingMaxSurge - } - return nil -} - -// Deprecated: Do not use. -func (x *PilotConfig) GetRollingMaxUnavailable() *IntOrString { - if x != nil { - return x.RollingMaxUnavailable - } - return nil -} - -// Deprecated: Do not use. -func (x *PilotConfig) GetTolerations() []*structpb.Struct { - if x != nil { - return x.Tolerations - } - return nil -} - -func (x *PilotConfig) GetEnableProtocolSniffingForOutbound() *wrapperspb.BoolValue { - if x != nil { - return x.EnableProtocolSniffingForOutbound - } - return nil -} - -func (x *PilotConfig) GetEnableProtocolSniffingForInbound() *wrapperspb.BoolValue { - if x != nil { - return x.EnableProtocolSniffingForInbound - } - return nil -} - -// Deprecated: Do not use. -func (x *PilotConfig) GetPodAnnotations() *structpb.Struct { - if x != nil { - return x.PodAnnotations - } - return nil -} - -func (x *PilotConfig) GetServiceAnnotations() *structpb.Struct { - if x != nil { - return x.ServiceAnnotations - } - return nil -} - -func (x *PilotConfig) GetConfigSource() *PilotConfigSource { - if x != nil { - return x.ConfigSource - } - return nil -} - -func (x *PilotConfig) GetJwksResolverExtraRootCA() string { - if x != nil { - return x.JwksResolverExtraRootCA - } - return "" -} - -func (x *PilotConfig) GetPlugins() []string { - if x != nil { - return x.Plugins - } - return nil -} - -func (x *PilotConfig) GetHub() string { - if x != nil { - return x.Hub - } - return "" -} - -func (x *PilotConfig) GetTag() *structpb.Value { - if x != nil { - return x.Tag - } - return nil -} - -// Controls legacy k8s ingress. Only one pilot profile should enable ingress support. -type PilotIngressConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Sets the type ingress service for Pilot. - // - // If empty, node-port is assumed. - // - // Allowed values: node-port, istio-ingressgateway, ingress - IngressService string `protobuf:"bytes,1,opt,name=ingressService,proto3" json:"ingressService,omitempty"` - IngressControllerMode IngressControllerMode `protobuf:"varint,2,opt,name=ingressControllerMode,proto3,enum=v1alpha1.IngressControllerMode" json:"ingressControllerMode,omitempty"` - // If mode is STRICT, this value must be set on "kubernetes.io/ingress.class" annotation to activate. - IngressClass string `protobuf:"bytes,3,opt,name=ingressClass,proto3" json:"ingressClass,omitempty"` -} - -func (x *PilotIngressConfig) Reset() { - *x = PilotIngressConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[21] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PilotIngressConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PilotIngressConfig) ProtoMessage() {} - -func (x *PilotIngressConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[21] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PilotIngressConfig.ProtoReflect.Descriptor instead. -func (*PilotIngressConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{21} -} - -func (x *PilotIngressConfig) GetIngressService() string { - if x != nil { - return x.IngressService - } - return "" -} - -func (x *PilotIngressConfig) GetIngressControllerMode() IngressControllerMode { - if x != nil { - return x.IngressControllerMode - } - return IngressControllerMode_UNSPECIFIED -} - -func (x *PilotIngressConfig) GetIngressClass() string { - if x != nil { - return x.IngressClass - } - return "" -} - -// Controls whether Istio policy is applied to Pilot. -type PilotPolicyConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Controls whether Istio policy is applied to Pilot. - Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` -} - -func (x *PilotPolicyConfig) Reset() { - *x = PilotPolicyConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[22] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PilotPolicyConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PilotPolicyConfig) ProtoMessage() {} - -func (x *PilotPolicyConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[22] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PilotPolicyConfig.ProtoReflect.Descriptor instead. -func (*PilotPolicyConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{22} -} - -func (x *PilotPolicyConfig) GetEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.Enabled - } - return nil -} - -// Controls telemetry configuration -type TelemetryConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Controls whether telemetry is exported for Pilot. - Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - // Use telemetry v2. - V2 *TelemetryV2Config `protobuf:"bytes,3,opt,name=v2,proto3" json:"v2,omitempty"` -} - -func (x *TelemetryConfig) Reset() { - *x = TelemetryConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[23] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TelemetryConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TelemetryConfig) ProtoMessage() {} - -func (x *TelemetryConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[23] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TelemetryConfig.ProtoReflect.Descriptor instead. -func (*TelemetryConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{23} -} - -func (x *TelemetryConfig) GetEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.Enabled - } - return nil -} - -func (x *TelemetryConfig) GetV2() *TelemetryV2Config { - if x != nil { - return x.V2 - } - return nil -} - -// Controls whether pilot will configure telemetry v2. -type TelemetryV2Config struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Controls whether pilot will configure telemetry v2. - Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - MetadataExchange *TelemetryV2MetadataExchangeConfig `protobuf:"bytes,4,opt,name=metadata_exchange,json=metadataExchange,proto3" json:"metadata_exchange,omitempty"` - Prometheus *TelemetryV2PrometheusConfig `protobuf:"bytes,2,opt,name=prometheus,proto3" json:"prometheus,omitempty"` - Stackdriver *TelemetryV2StackDriverConfig `protobuf:"bytes,3,opt,name=stackdriver,proto3" json:"stackdriver,omitempty"` - AccessLogPolicy *TelemetryV2AccessLogPolicyFilterConfig `protobuf:"bytes,5,opt,name=access_log_policy,json=accessLogPolicy,proto3" json:"access_log_policy,omitempty"` -} - -func (x *TelemetryV2Config) Reset() { - *x = TelemetryV2Config{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[24] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TelemetryV2Config) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TelemetryV2Config) ProtoMessage() {} - -func (x *TelemetryV2Config) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[24] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TelemetryV2Config.ProtoReflect.Descriptor instead. -func (*TelemetryV2Config) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{24} -} - -func (x *TelemetryV2Config) GetEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.Enabled - } - return nil -} - -func (x *TelemetryV2Config) GetMetadataExchange() *TelemetryV2MetadataExchangeConfig { - if x != nil { - return x.MetadataExchange - } - return nil -} - -func (x *TelemetryV2Config) GetPrometheus() *TelemetryV2PrometheusConfig { - if x != nil { - return x.Prometheus - } - return nil -} - -func (x *TelemetryV2Config) GetStackdriver() *TelemetryV2StackDriverConfig { - if x != nil { - return x.Stackdriver - } - return nil -} - -func (x *TelemetryV2Config) GetAccessLogPolicy() *TelemetryV2AccessLogPolicyFilterConfig { - if x != nil { - return x.AccessLogPolicy - } - return nil -} - -type TelemetryV2MetadataExchangeConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Controls whether enabled WebAssembly runtime for metadata exchange filter. - WasmEnabled *wrapperspb.BoolValue `protobuf:"bytes,2,opt,name=wasmEnabled,proto3" json:"wasmEnabled,omitempty"` -} - -func (x *TelemetryV2MetadataExchangeConfig) Reset() { - *x = TelemetryV2MetadataExchangeConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[25] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TelemetryV2MetadataExchangeConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TelemetryV2MetadataExchangeConfig) ProtoMessage() {} - -func (x *TelemetryV2MetadataExchangeConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[25] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TelemetryV2MetadataExchangeConfig.ProtoReflect.Descriptor instead. -func (*TelemetryV2MetadataExchangeConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{25} -} - -func (x *TelemetryV2MetadataExchangeConfig) GetWasmEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.WasmEnabled - } - return nil -} - -// Conrols telemetry v2 prometheus settings. -type TelemetryV2PrometheusConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Controls whether stats envoyfilter would be enabled or not. - Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - // Controls whether enabled WebAssembly runtime for stats filter. - WasmEnabled *wrapperspb.BoolValue `protobuf:"bytes,2,opt,name=wasmEnabled,proto3" json:"wasmEnabled,omitempty"` - // Overrides default telemetry v2 filter configuration. - ConfigOverride *TelemetryV2PrometheusConfig_ConfigOverride `protobuf:"bytes,3,opt,name=config_override,json=configOverride,proto3" json:"config_override,omitempty"` -} - -func (x *TelemetryV2PrometheusConfig) Reset() { - *x = TelemetryV2PrometheusConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[26] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TelemetryV2PrometheusConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TelemetryV2PrometheusConfig) ProtoMessage() {} - -func (x *TelemetryV2PrometheusConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[26] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TelemetryV2PrometheusConfig.ProtoReflect.Descriptor instead. -func (*TelemetryV2PrometheusConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{26} -} - -func (x *TelemetryV2PrometheusConfig) GetEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.Enabled - } - return nil -} - -func (x *TelemetryV2PrometheusConfig) GetWasmEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.WasmEnabled - } - return nil -} - -func (x *TelemetryV2PrometheusConfig) GetConfigOverride() *TelemetryV2PrometheusConfig_ConfigOverride { - if x != nil { - return x.ConfigOverride - } - return nil -} - -// Conrols telemetry v2 stackdriver settings. -type TelemetryV2StackDriverConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - // Deprecated: Do not use. - Logging *wrapperspb.BoolValue `protobuf:"bytes,2,opt,name=logging,proto3" json:"logging,omitempty"` - Monitoring *wrapperspb.BoolValue `protobuf:"bytes,3,opt,name=monitoring,proto3" json:"monitoring,omitempty"` - // Deprecated: Do not use. - Topology *wrapperspb.BoolValue `protobuf:"bytes,4,opt,name=topology,proto3" json:"topology,omitempty"` - DisableOutbound *wrapperspb.BoolValue `protobuf:"bytes,6,opt,name=disableOutbound,proto3" json:"disableOutbound,omitempty"` - ConfigOverride *structpb.Struct `protobuf:"bytes,5,opt,name=configOverride,proto3" json:"configOverride,omitempty"` - OutboundAccessLogging TelemetryV2StackDriverConfig_AccessLogging `protobuf:"varint,7,opt,name=outboundAccessLogging,proto3,enum=v1alpha1.TelemetryV2StackDriverConfig_AccessLogging" json:"outboundAccessLogging,omitempty"` - InboundAccessLogging TelemetryV2StackDriverConfig_AccessLogging `protobuf:"varint,8,opt,name=inboundAccessLogging,proto3,enum=v1alpha1.TelemetryV2StackDriverConfig_AccessLogging" json:"inboundAccessLogging,omitempty"` -} - -func (x *TelemetryV2StackDriverConfig) Reset() { - *x = TelemetryV2StackDriverConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[27] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TelemetryV2StackDriverConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TelemetryV2StackDriverConfig) ProtoMessage() {} - -func (x *TelemetryV2StackDriverConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[27] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TelemetryV2StackDriverConfig.ProtoReflect.Descriptor instead. -func (*TelemetryV2StackDriverConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{27} -} - -func (x *TelemetryV2StackDriverConfig) GetEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.Enabled - } - return nil -} - -// Deprecated: Do not use. -func (x *TelemetryV2StackDriverConfig) GetLogging() *wrapperspb.BoolValue { - if x != nil { - return x.Logging - } - return nil -} - -func (x *TelemetryV2StackDriverConfig) GetMonitoring() *wrapperspb.BoolValue { - if x != nil { - return x.Monitoring - } - return nil -} - -// Deprecated: Do not use. -func (x *TelemetryV2StackDriverConfig) GetTopology() *wrapperspb.BoolValue { - if x != nil { - return x.Topology - } - return nil -} - -func (x *TelemetryV2StackDriverConfig) GetDisableOutbound() *wrapperspb.BoolValue { - if x != nil { - return x.DisableOutbound - } - return nil -} - -func (x *TelemetryV2StackDriverConfig) GetConfigOverride() *structpb.Struct { - if x != nil { - return x.ConfigOverride - } - return nil -} - -func (x *TelemetryV2StackDriverConfig) GetOutboundAccessLogging() TelemetryV2StackDriverConfig_AccessLogging { - if x != nil { - return x.OutboundAccessLogging - } - return TelemetryV2StackDriverConfig_NONE -} - -func (x *TelemetryV2StackDriverConfig) GetInboundAccessLogging() TelemetryV2StackDriverConfig_AccessLogging { - if x != nil { - return x.InboundAccessLogging - } - return TelemetryV2StackDriverConfig_NONE -} - -// Conrols telemetry v2 access log policy filter settings. -type TelemetryV2AccessLogPolicyFilterConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - LogWindowDuration *durationpb.Duration `protobuf:"bytes,2,opt,name=logWindowDuration,proto3" json:"logWindowDuration,omitempty"` -} - -func (x *TelemetryV2AccessLogPolicyFilterConfig) Reset() { - *x = TelemetryV2AccessLogPolicyFilterConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[28] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TelemetryV2AccessLogPolicyFilterConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TelemetryV2AccessLogPolicyFilterConfig) ProtoMessage() {} - -func (x *TelemetryV2AccessLogPolicyFilterConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[28] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TelemetryV2AccessLogPolicyFilterConfig.ProtoReflect.Descriptor instead. -func (*TelemetryV2AccessLogPolicyFilterConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{28} -} - -func (x *TelemetryV2AccessLogPolicyFilterConfig) GetEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.Enabled - } - return nil -} - -func (x *TelemetryV2AccessLogPolicyFilterConfig) GetLogWindowDuration() *durationpb.Duration { - if x != nil { - return x.LogWindowDuration - } - return nil -} - -// PilotConfigSource describes information about a configuration store inside a -// mesh. A single control plane instance can interact with one or more data -// sources. -type PilotConfigSource struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Describes the source of configuration, if nothing is specified default is MCP. - SubscribedResources []string `protobuf:"bytes,1,rep,name=subscribedResources,proto3" json:"subscribedResources,omitempty"` -} - -func (x *PilotConfigSource) Reset() { - *x = PilotConfigSource{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[29] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PilotConfigSource) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PilotConfigSource) ProtoMessage() {} - -func (x *PilotConfigSource) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[29] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PilotConfigSource.ProtoReflect.Descriptor instead. -func (*PilotConfigSource) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{29} -} - -func (x *PilotConfigSource) GetSubscribedResources() []string { - if x != nil { - return x.SubscribedResources - } - return nil -} - -// Configuration for a port. -type PortsConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Port name. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Port number. - Port int32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` - // NodePort number. - NodePort int32 `protobuf:"varint,3,opt,name=nodePort,proto3" json:"nodePort,omitempty"` - // Target port number. - TargetPort int32 `protobuf:"varint,4,opt,name=targetPort,proto3" json:"targetPort,omitempty"` - // Protocol name. - Protocol string `protobuf:"bytes,5,opt,name=protocol,proto3" json:"protocol,omitempty"` -} - -func (x *PortsConfig) Reset() { - *x = PortsConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[30] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PortsConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PortsConfig) ProtoMessage() {} - -func (x *PortsConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[30] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PortsConfig.ProtoReflect.Descriptor instead. -func (*PortsConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{30} -} - -func (x *PortsConfig) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *PortsConfig) GetPort() int32 { - if x != nil { - return x.Port - } - return 0 -} - -func (x *PortsConfig) GetNodePort() int32 { - if x != nil { - return x.NodePort - } - return 0 -} - -func (x *PortsConfig) GetTargetPort() int32 { - if x != nil { - return x.TargetPort - } - return 0 -} - -func (x *PortsConfig) GetProtocol() string { - if x != nil { - return x.Protocol - } - return "" -} - -// Configuration for Proxy. -type ProxyConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - AutoInject string `protobuf:"bytes,4,opt,name=autoInject,proto3" json:"autoInject,omitempty"` - // Domain for the cluster, default: "cluster.local". - // - // K8s allows this to be customized, see https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/ - ClusterDomain string `protobuf:"bytes,5,opt,name=clusterDomain,proto3" json:"clusterDomain,omitempty"` - // Per Component log level for proxy, applies to gateways and sidecars. - // - // If a component level is not set, then the global "logLevel" will be used. If left empty, "misc:error" is used. - ComponentLogLevel string `protobuf:"bytes,6,opt,name=componentLogLevel,proto3" json:"componentLogLevel,omitempty"` - // Enables core dumps for newly injected sidecars. - // - // If set, newly injected sidecars will have core dumps enabled. - EnableCoreDump *wrapperspb.BoolValue `protobuf:"bytes,9,opt,name=enableCoreDump,proto3" json:"enableCoreDump,omitempty"` - // Specifies the Istio ingress ports not to capture. - ExcludeInboundPorts string `protobuf:"bytes,12,opt,name=excludeInboundPorts,proto3" json:"excludeInboundPorts,omitempty"` - // Lists the excluded IP ranges of Istio egress traffic that the sidecar captures. - ExcludeIPRanges string `protobuf:"bytes,13,opt,name=excludeIPRanges,proto3" json:"excludeIPRanges,omitempty"` - // Image name or path for the proxy, default: "proxyv2". - // - // If registry or tag are not specified, global.hub and global.tag are used. - // - // Examples: my-proxy (uses global.hub/tag), docker.io/myrepo/my-proxy:v1.0.0 - Image string `protobuf:"bytes,14,opt,name=image,proto3" json:"image,omitempty"` - // Lists the IP ranges of Istio egress traffic that the sidecar captures. - // - // Example: "172.30.0.0/16,172.20.0.0/16" - // This would only capture egress traffic on those two IP Ranges, all other outbound traffic would # be allowed by the sidecar." - IncludeIPRanges string `protobuf:"bytes,16,opt,name=includeIPRanges,proto3" json:"includeIPRanges,omitempty"` - // Log level for proxy, applies to gateways and sidecars. If left empty, "warning" is used. Expected values are: trace\|debug\|info\|warning\|error\|critical\|off - LogLevel string `protobuf:"bytes,18,opt,name=logLevel,proto3" json:"logLevel,omitempty"` - // Enables privileged securityContext for the istio-proxy container. - // - // See https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ - Privileged *wrapperspb.BoolValue `protobuf:"bytes,19,opt,name=privileged,proto3" json:"privileged,omitempty"` - // Sets the initial delay for readiness probes in seconds. - ReadinessInitialDelaySeconds uint32 `protobuf:"varint,20,opt,name=readinessInitialDelaySeconds,proto3" json:"readinessInitialDelaySeconds,omitempty"` - // Sets the interval between readiness probes in seconds. - ReadinessPeriodSeconds uint32 `protobuf:"varint,21,opt,name=readinessPeriodSeconds,proto3" json:"readinessPeriodSeconds,omitempty"` - // Sets the number of successive failed probes before indicating readiness failure. - ReadinessFailureThreshold uint32 `protobuf:"varint,22,opt,name=readinessFailureThreshold,proto3" json:"readinessFailureThreshold,omitempty"` - // Default port used for the Pilot agent's health checks. - StatusPort uint32 `protobuf:"varint,23,opt,name=statusPort,proto3" json:"statusPort,omitempty"` - // K8s resources settings. - // - // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container - // - // Deprecated: Do not use. - Resources *Resources `protobuf:"bytes,24,opt,name=resources,proto3" json:"resources,omitempty"` - Tracer Tracer `protobuf:"varint,25,opt,name=tracer,proto3,enum=v1alpha1.Tracer" json:"tracer,omitempty"` - ExcludeOutboundPorts string `protobuf:"bytes,28,opt,name=excludeOutboundPorts,proto3" json:"excludeOutboundPorts,omitempty"` - Lifecycle *structpb.Struct `protobuf:"bytes,36,opt,name=lifecycle,proto3" json:"lifecycle,omitempty"` - // Controls if sidecar is injected at the front of the container list and blocks the start of the other containers until the proxy is ready - // - // Deprecated: replaced by ProxyConfig setting which allows per-pod configuration of this behavior. - // - // Deprecated: Do not use. - HoldApplicationUntilProxyStarts *wrapperspb.BoolValue `protobuf:"bytes,37,opt,name=holdApplicationUntilProxyStarts,proto3" json:"holdApplicationUntilProxyStarts,omitempty"` - IncludeInboundPorts string `protobuf:"bytes,38,opt,name=includeInboundPorts,proto3" json:"includeInboundPorts,omitempty"` - IncludeOutboundPorts string `protobuf:"bytes,39,opt,name=includeOutboundPorts,proto3" json:"includeOutboundPorts,omitempty"` -} - -func (x *ProxyConfig) Reset() { - *x = ProxyConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[31] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ProxyConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProxyConfig) ProtoMessage() {} - -func (x *ProxyConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[31] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProxyConfig.ProtoReflect.Descriptor instead. -func (*ProxyConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{31} -} - -func (x *ProxyConfig) GetAutoInject() string { - if x != nil { - return x.AutoInject - } - return "" -} - -func (x *ProxyConfig) GetClusterDomain() string { - if x != nil { - return x.ClusterDomain - } - return "" -} - -func (x *ProxyConfig) GetComponentLogLevel() string { - if x != nil { - return x.ComponentLogLevel - } - return "" -} - -func (x *ProxyConfig) GetEnableCoreDump() *wrapperspb.BoolValue { - if x != nil { - return x.EnableCoreDump - } - return nil -} - -func (x *ProxyConfig) GetExcludeInboundPorts() string { - if x != nil { - return x.ExcludeInboundPorts - } - return "" -} - -func (x *ProxyConfig) GetExcludeIPRanges() string { - if x != nil { - return x.ExcludeIPRanges - } - return "" -} - -func (x *ProxyConfig) GetImage() string { - if x != nil { - return x.Image - } - return "" -} - -func (x *ProxyConfig) GetIncludeIPRanges() string { - if x != nil { - return x.IncludeIPRanges - } - return "" -} - -func (x *ProxyConfig) GetLogLevel() string { - if x != nil { - return x.LogLevel - } - return "" -} - -func (x *ProxyConfig) GetPrivileged() *wrapperspb.BoolValue { - if x != nil { - return x.Privileged - } - return nil -} - -func (x *ProxyConfig) GetReadinessInitialDelaySeconds() uint32 { - if x != nil { - return x.ReadinessInitialDelaySeconds - } - return 0 -} - -func (x *ProxyConfig) GetReadinessPeriodSeconds() uint32 { - if x != nil { - return x.ReadinessPeriodSeconds - } - return 0 -} - -func (x *ProxyConfig) GetReadinessFailureThreshold() uint32 { - if x != nil { - return x.ReadinessFailureThreshold - } - return 0 -} - -func (x *ProxyConfig) GetStatusPort() uint32 { - if x != nil { - return x.StatusPort - } - return 0 -} - -// Deprecated: Do not use. -func (x *ProxyConfig) GetResources() *Resources { - if x != nil { - return x.Resources - } - return nil -} - -func (x *ProxyConfig) GetTracer() Tracer { - if x != nil { - return x.Tracer - } - return Tracer_zipkin -} - -func (x *ProxyConfig) GetExcludeOutboundPorts() string { - if x != nil { - return x.ExcludeOutboundPorts - } - return "" -} - -func (x *ProxyConfig) GetLifecycle() *structpb.Struct { - if x != nil { - return x.Lifecycle - } - return nil -} - -// Deprecated: Do not use. -func (x *ProxyConfig) GetHoldApplicationUntilProxyStarts() *wrapperspb.BoolValue { - if x != nil { - return x.HoldApplicationUntilProxyStarts - } - return nil -} - -func (x *ProxyConfig) GetIncludeInboundPorts() string { - if x != nil { - return x.IncludeInboundPorts - } - return "" -} - -func (x *ProxyConfig) GetIncludeOutboundPorts() string { - if x != nil { - return x.IncludeOutboundPorts - } - return "" -} - -// Configuration for proxy_init container which sets the pods' networking to intercept the inbound/outbound traffic. -type ProxyInitConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Specifies the image for the proxy_init container. - Image string `protobuf:"bytes,1,opt,name=image,proto3" json:"image,omitempty"` - // K8s resources settings. - // - // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container - // - // Deprecated: Do not use. - Resources *Resources `protobuf:"bytes,5,opt,name=resources,proto3" json:"resources,omitempty"` -} - -func (x *ProxyInitConfig) Reset() { - *x = ProxyInitConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[32] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ProxyInitConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProxyInitConfig) ProtoMessage() {} - -func (x *ProxyInitConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[32] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProxyInitConfig.ProtoReflect.Descriptor instead. -func (*ProxyInitConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{32} -} - -func (x *ProxyInitConfig) GetImage() string { - if x != nil { - return x.Image - } - return "" -} - -// Deprecated: Do not use. -func (x *ProxyInitConfig) GetResources() *Resources { - if x != nil { - return x.Resources - } - return nil -} - -// Configuration for K8s resource requests. -type ResourcesRequestsConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Cpu string `protobuf:"bytes,1,opt,name=cpu,proto3" json:"cpu,omitempty"` - Memory string `protobuf:"bytes,2,opt,name=memory,proto3" json:"memory,omitempty"` -} - -func (x *ResourcesRequestsConfig) Reset() { - *x = ResourcesRequestsConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[33] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ResourcesRequestsConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ResourcesRequestsConfig) ProtoMessage() {} - -func (x *ResourcesRequestsConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[33] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ResourcesRequestsConfig.ProtoReflect.Descriptor instead. -func (*ResourcesRequestsConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{33} -} - -func (x *ResourcesRequestsConfig) GetCpu() string { - if x != nil { - return x.Cpu - } - return "" -} - -func (x *ResourcesRequestsConfig) GetMemory() string { - if x != nil { - return x.Memory - } - return "" -} - -// Configuration for the SecretDiscoveryService instead of using K8S secrets to mount the certificates. -type SDSConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Deprecated: Do not use. - Token *structpb.Struct `protobuf:"bytes,5,opt,name=token,proto3" json:"token,omitempty"` -} - -func (x *SDSConfig) Reset() { - *x = SDSConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[34] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SDSConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SDSConfig) ProtoMessage() {} - -func (x *SDSConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[34] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SDSConfig.ProtoReflect.Descriptor instead. -func (*SDSConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{34} -} - -// Deprecated: Do not use. -func (x *SDSConfig) GetToken() *structpb.Struct { - if x != nil { - return x.Token - } - return nil -} - -// Configuration for secret volume mounts. -// -// See https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets. -type SecretVolume struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - MountPath string `protobuf:"bytes,1,opt,name=mountPath,proto3" json:"mountPath,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - SecretName string `protobuf:"bytes,3,opt,name=secretName,proto3" json:"secretName,omitempty"` -} - -func (x *SecretVolume) Reset() { - *x = SecretVolume{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[35] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SecretVolume) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SecretVolume) ProtoMessage() {} - -func (x *SecretVolume) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[35] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SecretVolume.ProtoReflect.Descriptor instead. -func (*SecretVolume) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{35} -} - -func (x *SecretVolume) GetMountPath() string { - if x != nil { - return x.MountPath - } - return "" -} - -func (x *SecretVolume) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *SecretVolume) GetSecretName() string { - if x != nil { - return x.SecretName - } - return "" -} - -// ServiceConfig is described in istio.io documentation. -type ServiceConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Annotations *structpb.Struct `protobuf:"bytes,1,opt,name=annotations,proto3" json:"annotations,omitempty"` - ExternalPort uint32 `protobuf:"varint,2,opt,name=externalPort,proto3" json:"externalPort,omitempty"` - Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` - Type string `protobuf:"bytes,18,opt,name=type,proto3" json:"type,omitempty"` -} - -func (x *ServiceConfig) Reset() { - *x = ServiceConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[36] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ServiceConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ServiceConfig) ProtoMessage() {} - -func (x *ServiceConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[36] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ServiceConfig.ProtoReflect.Descriptor instead. -func (*ServiceConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{36} -} - -func (x *ServiceConfig) GetAnnotations() *structpb.Struct { - if x != nil { - return x.Annotations - } - return nil -} - -func (x *ServiceConfig) GetExternalPort() uint32 { - if x != nil { - return x.ExternalPort - } - return 0 -} - -func (x *ServiceConfig) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *ServiceConfig) GetType() string { - if x != nil { - return x.Type - } - return "" -} - -// SidecarInjectorConfig is described in istio.io documentation. -type SidecarInjectorConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Enables sidecar auto-injection in namespaces by default. - EnableNamespacesByDefault *wrapperspb.BoolValue `protobuf:"bytes,2,opt,name=enableNamespacesByDefault,proto3" json:"enableNamespacesByDefault,omitempty"` - // Instructs Istio to not inject the sidecar on those pods, based on labels that are present in those pods. - // - // Annotations in the pods have higher precedence than the label selectors. - // Order of evaluation: Pod Annotations → NeverInjectSelector → AlwaysInjectSelector → Default Policy. - // See https://istio.io/docs/setup/kubernetes/additional-setup/sidecar-injection/#more-control-adding-exceptions - NeverInjectSelector []*structpb.Struct `protobuf:"bytes,11,rep,name=neverInjectSelector,proto3" json:"neverInjectSelector,omitempty"` - // See NeverInjectSelector. - AlwaysInjectSelector []*structpb.Struct `protobuf:"bytes,12,rep,name=alwaysInjectSelector,proto3" json:"alwaysInjectSelector,omitempty"` - // If true, webhook or istioctl injector will rewrite PodSpec for liveness health check to redirect request to sidecar. This makes liveness check work even when mTLS is enabled. - RewriteAppHTTPProbe *wrapperspb.BoolValue `protobuf:"bytes,16,opt,name=rewriteAppHTTPProbe,proto3" json:"rewriteAppHTTPProbe,omitempty"` - // injectedAnnotations are additional annotations that will be added to the pod spec after injection - // This is primarily to support PSP annotations. - InjectedAnnotations *structpb.Struct `protobuf:"bytes,19,opt,name=injectedAnnotations,proto3" json:"injectedAnnotations,omitempty"` - // Enable objectSelector to filter out pods with no need for sidecar before calling istio-sidecar-injector. - ObjectSelector *structpb.Struct `protobuf:"bytes,21,opt,name=objectSelector,proto3" json:"objectSelector,omitempty"` - // Configure the injection url for sidecar injector webhook - InjectionURL string `protobuf:"bytes,22,opt,name=injectionURL,proto3" json:"injectionURL,omitempty"` - // Templates defines a set of custom injection templates that can be used. For example, defining: - // - // templates: - // hello: | - // metadata: - // labels: - // hello: world - // - // Then starting a pod with the `inject.istio.io/templates: hello` annotation, will result in the pod - // being injected with the hello=world labels. - // This is intended for advanced configuration only; most users should use the built in template - Templates *structpb.Struct `protobuf:"bytes,23,opt,name=templates,proto3" json:"templates,omitempty"` - // defaultTemplates: ["sidecar", "hello"] - DefaultTemplates []string `protobuf:"bytes,24,rep,name=defaultTemplates,proto3" json:"defaultTemplates,omitempty"` - // If enabled, the legacy webhook selection logic will be used. This relies on filtering of webhook - // requests in Istiod, rather than at the webhook selection level. - // This is option is intended for migration purposes only and will be removed in Istio 1.10. - // - // Deprecated: Do not use. - UseLegacySelectors *wrapperspb.BoolValue `protobuf:"bytes,4,opt,name=useLegacySelectors,proto3" json:"useLegacySelectors,omitempty"` -} - -func (x *SidecarInjectorConfig) Reset() { - *x = SidecarInjectorConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[37] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SidecarInjectorConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SidecarInjectorConfig) ProtoMessage() {} - -func (x *SidecarInjectorConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[37] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SidecarInjectorConfig.ProtoReflect.Descriptor instead. -func (*SidecarInjectorConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{37} -} - -func (x *SidecarInjectorConfig) GetEnableNamespacesByDefault() *wrapperspb.BoolValue { - if x != nil { - return x.EnableNamespacesByDefault - } - return nil -} - -func (x *SidecarInjectorConfig) GetNeverInjectSelector() []*structpb.Struct { - if x != nil { - return x.NeverInjectSelector - } - return nil -} - -func (x *SidecarInjectorConfig) GetAlwaysInjectSelector() []*structpb.Struct { - if x != nil { - return x.AlwaysInjectSelector - } - return nil -} - -func (x *SidecarInjectorConfig) GetRewriteAppHTTPProbe() *wrapperspb.BoolValue { - if x != nil { - return x.RewriteAppHTTPProbe - } - return nil -} - -func (x *SidecarInjectorConfig) GetInjectedAnnotations() *structpb.Struct { - if x != nil { - return x.InjectedAnnotations - } - return nil -} - -func (x *SidecarInjectorConfig) GetObjectSelector() *structpb.Struct { - if x != nil { - return x.ObjectSelector - } - return nil -} - -func (x *SidecarInjectorConfig) GetInjectionURL() string { - if x != nil { - return x.InjectionURL - } - return "" -} - -func (x *SidecarInjectorConfig) GetTemplates() *structpb.Struct { - if x != nil { - return x.Templates - } - return nil -} - -func (x *SidecarInjectorConfig) GetDefaultTemplates() []string { - if x != nil { - return x.DefaultTemplates - } - return nil -} - -// Deprecated: Do not use. -func (x *SidecarInjectorConfig) GetUseLegacySelectors() *wrapperspb.BoolValue { - if x != nil { - return x.UseLegacySelectors - } - return nil -} - -// Configuration for each of the supported tracers. -type TracerConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Configuration for the datadog tracing service. - Datadog *TracerDatadogConfig `protobuf:"bytes,1,opt,name=datadog,proto3" json:"datadog,omitempty"` - // Configuration for the lightstep tracing service. - Lightstep *TracerLightStepConfig `protobuf:"bytes,2,opt,name=lightstep,proto3" json:"lightstep,omitempty"` - // Configuration for the zipkin tracing service. - Zipkin *TracerZipkinConfig `protobuf:"bytes,3,opt,name=zipkin,proto3" json:"zipkin,omitempty"` - // Configuration for the stackdriver tracing service. - Stackdriver *TracerStackdriverConfig `protobuf:"bytes,4,opt,name=stackdriver,proto3" json:"stackdriver,omitempty"` -} - -func (x *TracerConfig) Reset() { - *x = TracerConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[38] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TracerConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TracerConfig) ProtoMessage() {} - -func (x *TracerConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[38] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TracerConfig.ProtoReflect.Descriptor instead. -func (*TracerConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{38} -} - -func (x *TracerConfig) GetDatadog() *TracerDatadogConfig { - if x != nil { - return x.Datadog - } - return nil -} - -func (x *TracerConfig) GetLightstep() *TracerLightStepConfig { - if x != nil { - return x.Lightstep - } - return nil -} - -func (x *TracerConfig) GetZipkin() *TracerZipkinConfig { - if x != nil { - return x.Zipkin - } - return nil -} - -func (x *TracerConfig) GetStackdriver() *TracerStackdriverConfig { - if x != nil { - return x.Stackdriver - } - return nil -} - -// Configuration for the datadog tracing service. -type TracerDatadogConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Address in host:port format for reporting trace data to the Datadog agent. - Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` -} - -func (x *TracerDatadogConfig) Reset() { - *x = TracerDatadogConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[39] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TracerDatadogConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TracerDatadogConfig) ProtoMessage() {} - -func (x *TracerDatadogConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[39] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TracerDatadogConfig.ProtoReflect.Descriptor instead. -func (*TracerDatadogConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{39} -} - -func (x *TracerDatadogConfig) GetAddress() string { - if x != nil { - return x.Address - } - return "" -} - -// Configuration for the lightstep tracing service. -type TracerLightStepConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Sets the lightstep satellite pool address in host:port format for reporting trace data. - Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` - // Sets the lightstep access token. - AccessToken string `protobuf:"bytes,2,opt,name=accessToken,proto3" json:"accessToken,omitempty"` -} - -func (x *TracerLightStepConfig) Reset() { - *x = TracerLightStepConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[40] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TracerLightStepConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TracerLightStepConfig) ProtoMessage() {} - -func (x *TracerLightStepConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[40] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TracerLightStepConfig.ProtoReflect.Descriptor instead. -func (*TracerLightStepConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{40} -} - -func (x *TracerLightStepConfig) GetAddress() string { - if x != nil { - return x.Address - } - return "" -} - -func (x *TracerLightStepConfig) GetAccessToken() string { - if x != nil { - return x.AccessToken - } - return "" -} - -// Configuration for the zipkin tracing service. -type TracerZipkinConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Address of zipkin instance in host:port format for reporting trace data. - // - // Example: .:941 - Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` -} - -func (x *TracerZipkinConfig) Reset() { - *x = TracerZipkinConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[41] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TracerZipkinConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TracerZipkinConfig) ProtoMessage() {} - -func (x *TracerZipkinConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[41] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TracerZipkinConfig.ProtoReflect.Descriptor instead. -func (*TracerZipkinConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{41} -} - -func (x *TracerZipkinConfig) GetAddress() string { - if x != nil { - return x.Address - } - return "" -} - -// Configuration for the stackdriver tracing service. -type TracerStackdriverConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // enables trace output to stdout. - Debug *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=debug,proto3" json:"debug,omitempty"` - // The global default max number of attributes per span. - MaxNumberOfAttributes uint32 `protobuf:"varint,2,opt,name=maxNumberOfAttributes,proto3" json:"maxNumberOfAttributes,omitempty"` - // The global default max number of annotation events per span. - MaxNumberOfAnnotations uint32 `protobuf:"varint,3,opt,name=maxNumberOfAnnotations,proto3" json:"maxNumberOfAnnotations,omitempty"` - // The global default max number of message events per span. - MaxNumberOfMessageEvents uint32 `protobuf:"varint,4,opt,name=maxNumberOfMessageEvents,proto3" json:"maxNumberOfMessageEvents,omitempty"` -} - -func (x *TracerStackdriverConfig) Reset() { - *x = TracerStackdriverConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[42] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TracerStackdriverConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TracerStackdriverConfig) ProtoMessage() {} - -func (x *TracerStackdriverConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[42] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TracerStackdriverConfig.ProtoReflect.Descriptor instead. -func (*TracerStackdriverConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{42} -} - -func (x *TracerStackdriverConfig) GetDebug() *wrapperspb.BoolValue { - if x != nil { - return x.Debug - } - return nil -} - -func (x *TracerStackdriverConfig) GetMaxNumberOfAttributes() uint32 { - if x != nil { - return x.MaxNumberOfAttributes - } - return 0 -} - -func (x *TracerStackdriverConfig) GetMaxNumberOfAnnotations() uint32 { - if x != nil { - return x.MaxNumberOfAnnotations - } - return 0 -} - -func (x *TracerStackdriverConfig) GetMaxNumberOfMessageEvents() uint32 { - if x != nil { - return x.MaxNumberOfMessageEvents - } - return 0 -} - -type BaseConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // For Helm2 use, adds the CRDs to templates. - EnableCRDTemplates *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enableCRDTemplates,proto3" json:"enableCRDTemplates,omitempty"` - // URL to use for validating webhook. - ValidationURL string `protobuf:"bytes,2,opt,name=validationURL,proto3" json:"validationURL,omitempty"` - // For istioctl usage to disable istio config crds in base - EnableIstioConfigCRDs *wrapperspb.BoolValue `protobuf:"bytes,3,opt,name=enableIstioConfigCRDs,proto3" json:"enableIstioConfigCRDs,omitempty"` -} - -func (x *BaseConfig) Reset() { - *x = BaseConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[43] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BaseConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BaseConfig) ProtoMessage() {} - -func (x *BaseConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[43] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BaseConfig.ProtoReflect.Descriptor instead. -func (*BaseConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{43} -} - -func (x *BaseConfig) GetEnableCRDTemplates() *wrapperspb.BoolValue { - if x != nil { - return x.EnableCRDTemplates - } - return nil -} - -func (x *BaseConfig) GetValidationURL() string { - if x != nil { - return x.ValidationURL - } - return "" -} - -func (x *BaseConfig) GetEnableIstioConfigCRDs() *wrapperspb.BoolValue { - if x != nil { - return x.EnableIstioConfigCRDs - } - return nil -} - -type IstiodRemoteConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // URL to use for sidecar injector webhook. - InjectionURL string `protobuf:"bytes,1,opt,name=injectionURL,proto3" json:"injectionURL,omitempty"` - // Path to use for the sidecar injector webhook service. - InjectionPath string `protobuf:"bytes,2,opt,name=injectionPath,proto3" json:"injectionPath,omitempty"` -} - -func (x *IstiodRemoteConfig) Reset() { - *x = IstiodRemoteConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[44] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *IstiodRemoteConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*IstiodRemoteConfig) ProtoMessage() {} - -func (x *IstiodRemoteConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[44] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use IstiodRemoteConfig.ProtoReflect.Descriptor instead. -func (*IstiodRemoteConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{44} -} - -func (x *IstiodRemoteConfig) GetInjectionURL() string { - if x != nil { - return x.InjectionURL - } - return "" -} - -func (x *IstiodRemoteConfig) GetInjectionPath() string { - if x != nil { - return x.InjectionPath - } - return "" -} - -type Values struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Cni *CNIConfig `protobuf:"bytes,2,opt,name=cni,proto3" json:"cni,omitempty"` - Gateways *GatewaysConfig `protobuf:"bytes,5,opt,name=gateways,proto3" json:"gateways,omitempty"` - Global *GlobalConfig `protobuf:"bytes,6,opt,name=global,proto3" json:"global,omitempty"` - Pilot *PilotConfig `protobuf:"bytes,10,opt,name=pilot,proto3" json:"pilot,omitempty"` - // Controls whether telemetry is exported for Pilot. - Telemetry *TelemetryConfig `protobuf:"bytes,23,opt,name=telemetry,proto3" json:"telemetry,omitempty"` - SidecarInjectorWebhook *SidecarInjectorConfig `protobuf:"bytes,13,opt,name=sidecarInjectorWebhook,proto3" json:"sidecarInjectorWebhook,omitempty"` - IstioCni *CNIConfig `protobuf:"bytes,19,opt,name=istio_cni,json=istioCni,proto3" json:"istio_cni,omitempty"` - Revision string `protobuf:"bytes,21,opt,name=revision,proto3" json:"revision,omitempty"` - OwnerName string `protobuf:"bytes,22,opt,name=ownerName,proto3" json:"ownerName,omitempty"` - // TODO can this import the real mesh config API? - MeshConfig *structpb.Value `protobuf:"bytes,36,opt,name=meshConfig,proto3" json:"meshConfig,omitempty"` - Base *BaseConfig `protobuf:"bytes,37,opt,name=base,proto3" json:"base,omitempty"` - IstiodRemote *IstiodRemoteConfig `protobuf:"bytes,38,opt,name=istiodRemote,proto3" json:"istiodRemote,omitempty"` - RevisionTags []string `protobuf:"bytes,39,rep,name=revisionTags,proto3" json:"revisionTags,omitempty"` - DefaultRevision string `protobuf:"bytes,40,opt,name=defaultRevision,proto3" json:"defaultRevision,omitempty"` -} - -func (x *Values) Reset() { - *x = Values{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[45] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Values) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Values) ProtoMessage() {} - -func (x *Values) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[45] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Values.ProtoReflect.Descriptor instead. -func (*Values) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{45} -} - -func (x *Values) GetCni() *CNIConfig { - if x != nil { - return x.Cni - } - return nil -} - -func (x *Values) GetGateways() *GatewaysConfig { - if x != nil { - return x.Gateways - } - return nil -} - -func (x *Values) GetGlobal() *GlobalConfig { - if x != nil { - return x.Global - } - return nil -} - -func (x *Values) GetPilot() *PilotConfig { - if x != nil { - return x.Pilot - } - return nil -} - -func (x *Values) GetTelemetry() *TelemetryConfig { - if x != nil { - return x.Telemetry - } - return nil -} - -func (x *Values) GetSidecarInjectorWebhook() *SidecarInjectorConfig { - if x != nil { - return x.SidecarInjectorWebhook - } - return nil -} - -func (x *Values) GetIstioCni() *CNIConfig { - if x != nil { - return x.IstioCni - } - return nil -} - -func (x *Values) GetRevision() string { - if x != nil { - return x.Revision - } - return "" -} - -func (x *Values) GetOwnerName() string { - if x != nil { - return x.OwnerName - } - return "" -} - -func (x *Values) GetMeshConfig() *structpb.Value { - if x != nil { - return x.MeshConfig - } - return nil -} - -func (x *Values) GetBase() *BaseConfig { - if x != nil { - return x.Base - } - return nil -} - -func (x *Values) GetIstiodRemote() *IstiodRemoteConfig { - if x != nil { - return x.IstiodRemote - } - return nil -} - -func (x *Values) GetRevisionTags() []string { - if x != nil { - return x.RevisionTags - } - return nil -} - -func (x *Values) GetDefaultRevision() string { - if x != nil { - return x.DefaultRevision - } - return "" -} - -// ZeroVPNConfig enables cross-cluster access using SNI matching. -type ZeroVPNConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Controls whether ZeroVPN is enabled. - Enabled *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - Suffix string `protobuf:"bytes,2,opt,name=suffix,proto3" json:"suffix,omitempty"` -} - -func (x *ZeroVPNConfig) Reset() { - *x = ZeroVPNConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[46] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ZeroVPNConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ZeroVPNConfig) ProtoMessage() {} - -func (x *ZeroVPNConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[46] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ZeroVPNConfig.ProtoReflect.Descriptor instead. -func (*ZeroVPNConfig) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{46} -} - -func (x *ZeroVPNConfig) GetEnabled() *wrapperspb.BoolValue { - if x != nil { - return x.Enabled - } - return nil -} - -func (x *ZeroVPNConfig) GetSuffix() string { - if x != nil { - return x.Suffix - } - return "" -} - -// IntOrString is a type that can hold an int32 or a string. When used in -// JSON or YAML marshalling and unmarshalling, it produces or consumes the -// inner type. This allows you to have, for example, a JSON field that can -// accept a name or number. -// TODO: Rename to Int32OrString -// -// +protobuf=true -// +protobuf.options.(gogoproto.goproto_stringer)=false -// +k8s:openapi-gen=true -type IntOrString struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Type int64 `protobuf:"varint,1,opt,name=type,proto3" json:"type,omitempty"` - IntVal *wrapperspb.Int32Value `protobuf:"bytes,2,opt,name=intVal,proto3" json:"intVal,omitempty"` - StrVal *wrapperspb.StringValue `protobuf:"bytes,3,opt,name=strVal,proto3" json:"strVal,omitempty"` -} - -func (x *IntOrString) Reset() { - *x = IntOrString{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[47] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *IntOrString) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*IntOrString) ProtoMessage() {} - -func (x *IntOrString) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[47] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use IntOrString.ProtoReflect.Descriptor instead. -func (*IntOrString) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{47} -} - -func (x *IntOrString) GetType() int64 { - if x != nil { - return x.Type - } - return 0 -} - -func (x *IntOrString) GetIntVal() *wrapperspb.Int32Value { - if x != nil { - return x.IntVal - } - return nil -} - -func (x *IntOrString) GetStrVal() *wrapperspb.StringValue { - if x != nil { - return x.StrVal - } - return nil -} - -type TelemetryV2PrometheusConfig_ConfigOverride struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Overrides default gateway telemetry v2 configuration. - Gateway *structpb.Struct `protobuf:"bytes,1,opt,name=gateway,proto3" json:"gateway,omitempty"` - // Overrides default inbound sidecar telemetry v2 configuration. - InboundSidecar *structpb.Struct `protobuf:"bytes,2,opt,name=inboundSidecar,proto3" json:"inboundSidecar,omitempty"` - // Overrides default outbound sidecar telemetry v2 configuration. - OutboundSidecar *structpb.Struct `protobuf:"bytes,3,opt,name=outboundSidecar,proto3" json:"outboundSidecar,omitempty"` -} - -func (x *TelemetryV2PrometheusConfig_ConfigOverride) Reset() { - *x = TelemetryV2PrometheusConfig_ConfigOverride{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[52] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TelemetryV2PrometheusConfig_ConfigOverride) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TelemetryV2PrometheusConfig_ConfigOverride) ProtoMessage() {} - -func (x *TelemetryV2PrometheusConfig_ConfigOverride) ProtoReflect() protoreflect.Message { - mi := &file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[52] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TelemetryV2PrometheusConfig_ConfigOverride.ProtoReflect.Descriptor instead. -func (*TelemetryV2PrometheusConfig_ConfigOverride) Descriptor() ([]byte, []int) { - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP(), []int{26, 0} -} - -func (x *TelemetryV2PrometheusConfig_ConfigOverride) GetGateway() *structpb.Struct { - if x != nil { - return x.Gateway - } - return nil -} - -func (x *TelemetryV2PrometheusConfig_ConfigOverride) GetInboundSidecar() *structpb.Struct { - if x != nil { - return x.InboundSidecar - } - return nil -} - -func (x *TelemetryV2PrometheusConfig_ConfigOverride) GetOutboundSidecar() *structpb.Struct { - if x != nil { - return x.OutboundSidecar - } - return nil -} - -var File_pkg_apis_istio_v1alpha1_values_types_proto protoreflect.FileDescriptor - -var file_pkg_apis_istio_v1alpha1_values_types_proto_rawDesc = []byte{ - 0x0a, 0x2a, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x69, 0x73, 0x74, 0x69, 0x6f, - 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, - 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, - 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, - 0x68, 0x0a, 0x0a, 0x41, 0x72, 0x63, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x14, 0x0a, - 0x05, 0x61, 0x6d, 0x64, 0x36, 0x34, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x61, 0x6d, - 0x64, 0x36, 0x34, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x70, 0x63, 0x36, 0x34, 0x6c, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x70, 0x70, 0x63, 0x36, 0x34, 0x6c, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x73, 0x33, 0x39, 0x30, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, 0x33, - 0x39, 0x30, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x72, 0x6d, 0x36, 0x34, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x05, 0x61, 0x72, 0x6d, 0x36, 0x34, 0x22, 0x9f, 0x06, 0x0a, 0x09, 0x43, 0x4e, - 0x49, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x10, 0x0a, - 0x03, 0x68, 0x75, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x68, 0x75, 0x62, 0x12, - 0x28, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, 0x61, - 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, - 0x1e, 0x0a, 0x0a, 0x70, 0x75, 0x6c, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x75, 0x6c, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, - 0x1c, 0x0a, 0x09, 0x63, 0x6e, 0x69, 0x42, 0x69, 0x6e, 0x44, 0x69, 0x72, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x63, 0x6e, 0x69, 0x42, 0x69, 0x6e, 0x44, 0x69, 0x72, 0x12, 0x1e, 0x0a, - 0x0a, 0x63, 0x6e, 0x69, 0x43, 0x6f, 0x6e, 0x66, 0x44, 0x69, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x63, 0x6e, 0x69, 0x43, 0x6f, 0x6e, 0x66, 0x44, 0x69, 0x72, 0x12, 0x28, 0x0a, - 0x0f, 0x63, 0x6e, 0x69, 0x43, 0x6f, 0x6e, 0x66, 0x46, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6e, 0x69, 0x43, 0x6f, 0x6e, 0x66, 0x46, - 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, - 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0e, 0x70, 0x6f, 0x64, 0x41, 0x6e, 0x6e, 0x6f, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0e, 0x70, 0x6f, 0x64, 0x41, - 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x70, 0x73, - 0x70, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x0b, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x73, 0x70, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, - 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, - 0x12, 0x31, 0x0a, 0x06, 0x72, 0x65, 0x70, 0x61, 0x69, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x4e, 0x49, 0x52, - 0x65, 0x70, 0x61, 0x69, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x72, 0x65, 0x70, - 0x61, 0x69, 0x72, 0x12, 0x34, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x18, 0x0e, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x05, 0x74, 0x61, 0x69, - 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x43, 0x4e, 0x49, 0x54, 0x61, 0x69, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x05, 0x74, 0x61, 0x69, 0x6e, 0x74, 0x12, 0x41, 0x0a, 0x0f, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x73, 0x18, 0x10, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x73, 0x52, 0x0e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x73, 0x12, 0x31, 0x0a, 0x09, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x13, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, - 0x3a, 0x0a, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x64, 0x18, 0x12, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x0a, 0x70, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x64, 0x22, 0x46, 0x0a, 0x0e, 0x43, - 0x4e, 0x49, 0x54, 0x61, 0x69, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, - 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x22, 0x8d, 0x03, 0x0a, 0x0f, 0x43, 0x4e, 0x49, 0x52, 0x65, 0x70, 0x61, 0x69, - 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x10, 0x0a, - 0x03, 0x68, 0x75, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x68, 0x75, 0x62, 0x12, - 0x28, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, 0x61, - 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, - 0x1c, 0x0a, 0x09, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x50, 0x6f, 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x09, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x26, 0x0a, - 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, - 0x6f, 0x64, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x50, 0x6f, 0x64, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x6e, 0x50, - 0x6f, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x4b, 0x65, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x11, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x6e, 0x50, 0x6f, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, - 0x4b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x13, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x6e, 0x50, 0x6f, 0x64, - 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x13, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x6e, 0x50, 0x6f, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x69, 0x6e, 0x69, 0x74, 0x43, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x11, 0x69, 0x6e, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, - 0x61, 0x6d, 0x65, 0x22, 0x5a, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x51, - 0x75, 0x6f, 0x74, 0x61, 0x73, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, - 0x6f, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x70, 0x6f, 0x64, 0x73, 0x22, - 0x58, 0x0a, 0x1a, 0x43, 0x50, 0x55, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x55, 0x74, 0x69, 0x6c, - 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3a, 0x0a, - 0x18, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x55, 0x74, - 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x18, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x55, 0x74, - 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xfb, 0x01, 0x0a, 0x09, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x06, 0x6c, 0x69, 0x6d, 0x69, 0x74, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x6d, - 0x69, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, - 0x12, 0x3d, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x1a, - 0x39, 0x0a, 0x0b, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3b, 0x0a, 0x0d, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4b, 0x0a, 0x0e, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x0b, 0x61, 0x6e, 0x6e, - 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x58, 0x0a, 0x20, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, - 0x6f, 0x64, 0x44, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x75, 0x64, 0x67, - 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x57, - 0x0a, 0x16, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3d, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x08, 0x72, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x22, 0xdb, 0x0c, 0x0a, 0x13, 0x45, 0x67, 0x72, 0x65, - 0x73, 0x73, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x46, 0x0a, 0x10, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x45, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x10, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, - 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x6f, 0x73, - 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x61, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x61, - 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x61, 0x78, 0x12, 0x22, 0x0a, 0x0c, 0x61, - 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x0c, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x69, 0x6e, 0x12, - 0x3a, 0x0a, 0x03, 0x63, 0x70, 0x75, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x50, 0x55, 0x54, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, 0x03, 0x63, 0x70, 0x75, 0x12, 0x34, 0x0a, 0x07, 0x65, - 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, - 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x64, 0x12, 0x29, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x41, 0x0a, 0x06, - 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x67, 0x72, 0x65, 0x73, 0x73, 0x47, 0x61, - 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x4c, 0x61, 0x62, 0x65, - 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x19, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, - 0x63, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x12, 0x43, 0x0a, 0x0e, 0x70, 0x6f, 0x64, 0x41, 0x6e, 0x6e, 0x6f, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, - 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0e, 0x70, 0x6f, 0x64, 0x41, 0x6e, - 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x5f, 0x0a, 0x1c, 0x70, 0x6f, 0x64, - 0x41, 0x6e, 0x74, 0x69, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x4c, 0x61, 0x62, 0x65, - 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x1c, 0x70, 0x6f, - 0x64, 0x41, 0x6e, 0x74, 0x69, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x4c, 0x61, 0x62, - 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x67, 0x0a, 0x20, 0x70, 0x6f, - 0x64, 0x41, 0x6e, 0x74, 0x69, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x54, 0x65, 0x72, - 0x6d, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x0d, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x02, 0x18, - 0x01, 0x52, 0x20, 0x70, 0x6f, 0x64, 0x41, 0x6e, 0x74, 0x69, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, - 0x74, 0x79, 0x54, 0x65, 0x72, 0x6d, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x12, 0x2b, 0x0a, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x0e, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x6f, - 0x72, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, - 0x12, 0x35, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x0f, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0d, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x0d, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x12, 0x47, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, - 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x7a, 0x76, 0x70, 0x6e, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x5a, 0x65, 0x72, 0x6f, - 0x56, 0x50, 0x4e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x04, 0x7a, 0x76, 0x70, 0x6e, 0x12, - 0x3d, 0x0a, 0x0b, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x14, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x02, 0x18, - 0x01, 0x52, 0x0b, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x43, - 0x0a, 0x0f, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x53, 0x75, 0x72, 0x67, - 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x4f, 0x72, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x42, 0x02, - 0x18, 0x01, 0x52, 0x0f, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x53, 0x75, - 0x72, 0x67, 0x65, 0x12, 0x4f, 0x0a, 0x15, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x61, - 0x78, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x16, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, - 0x74, 0x4f, 0x72, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, 0x15, 0x72, - 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, - 0x61, 0x62, 0x6c, 0x65, 0x12, 0x3d, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x17, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, - 0x72, 0x75, 0x63, 0x74, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x73, 0x12, 0x4b, 0x0a, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, - 0x6c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x18, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, - 0x12, 0x38, 0x0a, 0x09, 0x72, 0x75, 0x6e, 0x41, 0x73, 0x52, 0x6f, 0x6f, 0x74, 0x18, 0x1a, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x09, 0x72, 0x75, 0x6e, 0x41, 0x73, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x2c, 0x0a, 0x11, 0x69, 0x6e, - 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, - 0x1b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x40, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x18, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x0e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, - 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xeb, 0x01, 0x0a, 0x0e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, - 0x79, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x4f, 0x0a, 0x13, 0x69, 0x73, 0x74, 0x69, - 0x6f, 0x5f, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x45, 0x67, 0x72, 0x65, 0x73, 0x73, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x13, 0x69, 0x73, 0x74, 0x69, 0x6f, 0x2d, 0x65, 0x67, 0x72, 0x65, - 0x73, 0x73, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, - 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, - 0x52, 0x0a, 0x14, 0x69, 0x73, 0x74, 0x69, 0x6f, 0x5f, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, - 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, - 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x14, 0x69, - 0x73, 0x74, 0x69, 0x6f, 0x2d, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x67, 0x61, 0x74, 0x65, - 0x77, 0x61, 0x79, 0x22, 0xc4, 0x11, 0x0a, 0x0c, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2c, 0x0a, 0x04, 0x61, 0x72, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x72, - 0x63, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, 0x04, 0x61, 0x72, - 0x63, 0x68, 0x12, 0x30, 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x6f, 0x6f, 0x74, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x32, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x13, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x12, 0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x0a, 0x1f, - 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, 0x69, 0x73, - 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, - 0x34, 0x20, 0x03, 0x28, 0x09, 0x52, 0x1f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x56, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x53, 0x65, - 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4d, 0x0a, 0x13, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, - 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x02, 0x18, 0x01, - 0x52, 0x13, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x6e, 0x0a, 0x1a, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, - 0x50, 0x6f, 0x64, 0x44, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x75, 0x64, - 0x67, 0x65, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x6f, 0x64, 0x44, - 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x75, 0x64, 0x67, 0x65, 0x74, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, 0x1a, 0x64, 0x65, 0x66, 0x61, 0x75, - 0x6c, 0x74, 0x50, 0x6f, 0x64, 0x44, 0x69, 0x73, 0x72, 0x75, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, - 0x75, 0x64, 0x67, 0x65, 0x74, 0x12, 0x50, 0x0a, 0x10, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x20, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x66, 0x61, 0x75, - 0x6c, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, 0x10, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x4b, 0x0a, 0x12, 0x64, 0x65, 0x66, 0x61, 0x75, - 0x6c, 0x74, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x37, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x02, 0x18, 0x01, - 0x52, 0x12, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x68, 0x75, 0x62, 0x18, 0x0c, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x68, 0x75, 0x62, 0x12, 0x28, 0x0a, 0x0f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x50, - 0x75, 0x6c, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x50, 0x75, 0x6c, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x12, 0x2a, 0x0a, 0x10, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x50, 0x75, 0x6c, 0x6c, 0x53, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x73, 0x18, 0x25, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x69, 0x6d, 0x61, 0x67, - 0x65, 0x50, 0x75, 0x6c, 0x6c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x26, 0x0a, 0x0e, - 0x69, 0x73, 0x74, 0x69, 0x6f, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x0e, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x69, 0x73, 0x74, 0x69, 0x6f, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x6c, 0x6f, 0x67, 0x41, 0x73, 0x4a, 0x73, 0x6f, - 0x6e, 0x18, 0x24, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x52, 0x09, 0x6c, 0x6f, 0x67, 0x41, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x37, - 0x0a, 0x07, 0x6c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x6c, 0x6f, 0x62, 0x61, - 0x6c, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, - 0x6c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x73, 0x68, 0x49, - 0x44, 0x18, 0x35, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x73, 0x68, 0x49, 0x44, 0x12, - 0x3b, 0x0a, 0x0c, 0x6d, 0x65, 0x73, 0x68, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x18, - 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0c, - 0x6d, 0x65, 0x73, 0x68, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x12, 0x40, 0x0a, 0x0c, - 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x16, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4d, 0x75, - 0x6c, 0x74, 0x69, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x52, 0x0c, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x18, - 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x27, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x36, 0x0a, 0x16, 0x70, 0x6f, 0x64, 0x44, - 0x4e, 0x53, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x73, 0x18, 0x2b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x16, 0x70, 0x6f, 0x64, 0x44, 0x4e, 0x53, - 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, - 0x12, 0x5e, 0x0a, 0x1c, 0x6f, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x49, - 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, - 0x18, 0x26, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x1c, 0x6f, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x49, - 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, - 0x12, 0x3e, 0x0a, 0x0c, 0x6f, 0x6e, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x0c, 0x6f, 0x6e, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x12, 0x52, 0x0a, 0x16, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x4d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x18, 0x29, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x16, 0x6f, 0x70, - 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x57, 0x65, 0x62, 0x68, - 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x30, 0x0a, 0x11, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, - 0x43, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x02, 0x18, 0x01, 0x52, 0x11, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x43, 0x6c, 0x61, - 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, - 0x1c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x70, 0x72, - 0x6f, 0x78, 0x79, 0x12, 0x39, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x69, 0x6e, 0x69, - 0x74, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x49, 0x6e, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x12, 0x25, - 0x0a, 0x03, 0x73, 0x64, 0x73, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x44, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x52, 0x03, 0x73, 0x64, 0x73, 0x12, 0x28, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x1f, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, - 0x2e, 0x0a, 0x06, 0x74, 0x72, 0x61, 0x63, 0x65, 0x72, 0x18, 0x21, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x16, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, - 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x74, 0x72, 0x61, 0x63, 0x65, 0x72, 0x12, - 0x32, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x4d, 0x43, 0x50, 0x18, 0x23, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x75, 0x73, 0x65, - 0x4d, 0x43, 0x50, 0x12, 0x2e, 0x0a, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x69, 0x6c, - 0x6f, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x30, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x69, 0x6c, 0x6f, 0x74, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x69, 0x73, 0x74, 0x69, 0x6f, 0x64, 0x18, 0x36, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, - 0x73, 0x74, 0x69, 0x6f, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x69, 0x73, 0x74, - 0x69, 0x6f, 0x64, 0x12, 0x2c, 0x0a, 0x11, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x43, 0x65, 0x72, 0x74, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x38, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, - 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x43, 0x65, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x6a, 0x77, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x39, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6a, 0x77, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, - 0x25, 0x0a, 0x03, 0x73, 0x74, 0x73, 0x18, 0x3a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x54, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x52, 0x03, 0x73, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x18, 0x3b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x42, 0x0a, 0x0e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x74, 0x6c, 0x73, 0x43, - 0x65, 0x72, 0x74, 0x73, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, - 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x74, 0x6c, - 0x73, 0x43, 0x65, 0x72, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x61, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x18, 0x3d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x12, 0x42, 0x0a, 0x0e, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x49, 0x73, 0x74, 0x69, 0x6f, 0x64, 0x18, 0x3e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, - 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x49, 0x73, 0x74, 0x69, 0x6f, 0x64, 0x12, 0x40, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x40, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0d, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x61, - 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x41, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x61, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x46, 0x0a, 0x10, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x69, 0x6e, - 0x67, 0x76, 0x32, 0x41, 0x50, 0x49, 0x18, 0x42, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, - 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x10, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, - 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x76, 0x32, 0x41, 0x50, 0x49, 0x22, 0x2d, 0x0a, 0x09, 0x53, 0x54, - 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x52, 0x0a, 0x0c, 0x49, 0x73, 0x74, - 0x69, 0x6f, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x42, 0x0a, 0x0e, 0x65, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x65, - 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x22, 0x2b, 0x0a, - 0x13, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0xad, 0x0f, 0x0a, 0x14, 0x49, - 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x12, 0x46, 0x0a, 0x10, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, - 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x10, 0x61, 0x75, 0x74, 0x6f, 0x73, - 0x63, 0x61, 0x6c, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x61, - 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x61, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x0c, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x61, 0x78, 0x12, - 0x22, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x69, 0x6e, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, - 0x4d, 0x69, 0x6e, 0x12, 0x3a, 0x0a, 0x03, 0x63, 0x70, 0x75, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x24, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x50, 0x55, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, 0x03, 0x63, 0x70, 0x75, 0x12, - 0x40, 0x0a, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, - 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x29, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x0b, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x03, 0x65, - 0x6e, 0x76, 0x12, 0x42, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x0f, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, - 0x67, 0x72, 0x65, 0x73, 0x73, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, - 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x61, - 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x50, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, - 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x50, 0x12, 0x3a, - 0x0a, 0x18, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x53, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x18, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x53, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3f, - 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x13, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x02, 0x18, - 0x01, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, - 0x43, 0x0a, 0x0e, 0x70, 0x6f, 0x64, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, - 0x42, 0x02, 0x18, 0x01, 0x52, 0x0e, 0x70, 0x6f, 0x64, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x5f, 0x0a, 0x1c, 0x70, 0x6f, 0x64, 0x41, 0x6e, 0x74, 0x69, 0x41, - 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, - 0x75, 0x63, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x1c, 0x70, 0x6f, 0x64, 0x41, 0x6e, 0x74, 0x69, - 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x67, 0x0a, 0x20, 0x70, 0x6f, 0x64, 0x41, 0x6e, 0x74, 0x69, - 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x54, 0x65, 0x72, 0x6d, 0x4c, 0x61, 0x62, 0x65, - 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x16, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x20, 0x70, 0x6f, - 0x64, 0x41, 0x6e, 0x74, 0x69, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x54, 0x65, 0x72, - 0x6d, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x2b, - 0x0a, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x17, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x26, 0x0a, 0x0c, 0x72, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x18, 0x20, 0x01, 0x28, - 0x0d, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, - 0x02, 0x18, 0x01, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3c, - 0x0a, 0x0d, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, - 0x1b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x0d, 0x73, - 0x65, 0x63, 0x72, 0x65, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x12, 0x47, 0x0a, 0x12, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, - 0x74, 0x52, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x1d, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x36, 0x0a, 0x04, 0x7a, 0x76, 0x70, - 0x6e, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, - 0x79, 0x5a, 0x76, 0x70, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x04, 0x7a, 0x76, 0x70, - 0x6e, 0x12, 0x43, 0x0a, 0x0f, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x53, - 0x75, 0x72, 0x67, 0x65, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x4f, 0x72, 0x53, 0x74, 0x72, 0x69, 0x6e, - 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0f, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x61, - 0x78, 0x53, 0x75, 0x72, 0x67, 0x65, 0x12, 0x4f, 0x0a, 0x15, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, - 0x67, 0x4d, 0x61, 0x78, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, - 0x20, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x49, 0x6e, 0x74, 0x4f, 0x72, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x42, 0x02, 0x18, 0x01, - 0x52, 0x15, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x55, 0x6e, 0x61, 0x76, - 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x34, 0x0a, 0x15, 0x65, 0x78, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x18, 0x22, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x3d, 0x0a, - 0x0b, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x23, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, - 0x0b, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3b, 0x0a, 0x0c, - 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x24, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0c, 0x69, 0x6e, 0x67, - 0x72, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x14, 0x61, 0x64, 0x64, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x73, 0x18, 0x25, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, - 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x3d, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x26, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x72, 0x75, 0x6e, 0x41, 0x73, 0x52, 0x6f, - 0x6f, 0x74, 0x18, 0x2d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x09, 0x72, 0x75, 0x6e, 0x41, 0x73, 0x52, 0x6f, 0x6f, 0x74, 0x12, - 0x2c, 0x0a, 0x11, 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x18, 0x2e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x69, 0x6e, 0x6a, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x40, 0x0a, - 0x0e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x2f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, - 0x0e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x1a, - 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x68, 0x0a, 0x18, 0x49, 0x6e, - 0x67, 0x72, 0x65, 0x73, 0x73, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5a, 0x76, 0x70, 0x6e, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, - 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x75, - 0x66, 0x66, 0x69, 0x78, 0x22, 0xe8, 0x01, 0x0a, 0x12, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, - 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, - 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x12, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x44, 0x6f, 0x6d, - 0x61, 0x69, 0x6e, 0x53, 0x75, 0x66, 0x66, 0x69, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x12, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x75, 0x66, - 0x66, 0x69, 0x78, 0x12, 0x4a, 0x0a, 0x12, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x45, 0x6e, - 0x76, 0x6f, 0x79, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x69, 0x6e, 0x63, - 0x6c, 0x75, 0x64, 0x65, 0x45, 0x6e, 0x76, 0x6f, 0x79, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, - 0x87, 0x01, 0x0a, 0x1b, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x66, - 0x66, 0x69, 0x63, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x3e, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, - 0x64, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x22, - 0x28, 0x0a, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x4c, 0x4c, 0x4f, 0x57, - 0x5f, 0x41, 0x4e, 0x59, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, - 0x52, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x01, 0x22, 0x95, 0x0d, 0x0a, 0x0b, 0x50, 0x69, - 0x6c, 0x6f, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, - 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, - 0x46, 0x0a, 0x10, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x45, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x10, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, - 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x6f, 0x73, - 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x61, - 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x69, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x61, - 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x61, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x0c, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x61, 0x78, 0x12, - 0x26, 0x0a, 0x0c, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x72, 0x65, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, - 0x0d, 0x74, 0x72, 0x61, 0x63, 0x65, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x63, 0x65, 0x53, 0x61, 0x6d, 0x70, 0x6c, - 0x69, 0x6e, 0x67, 0x12, 0x35, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x42, 0x02, 0x18, 0x01, 0x52, - 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x12, 0x3a, 0x0a, 0x03, 0x63, 0x70, 0x75, 0x18, 0x0b, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x24, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x50, 0x55, - 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, 0x03, 0x63, 0x70, 0x75, - 0x12, 0x3f, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, - 0x02, 0x18, 0x01, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, - 0x72, 0x12, 0x63, 0x0a, 0x1f, 0x6b, 0x65, 0x65, 0x70, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x4d, 0x61, - 0x78, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x41, 0x67, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x1f, 0x6b, 0x65, 0x65, 0x70, 0x61, 0x6c, 0x69, 0x76, 0x65, - 0x4d, 0x61, 0x78, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x41, 0x67, 0x65, 0x12, 0x43, 0x0a, 0x10, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x10, 0x64, 0x65, 0x70, 0x6c, 0x6f, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x35, 0x0a, 0x09, 0x70, - 0x6f, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x24, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x09, 0x70, 0x6f, 0x64, 0x4c, 0x61, 0x62, 0x65, - 0x6c, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, 0x18, - 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, 0x12, 0x32, 0x0a, 0x06, - 0x75, 0x73, 0x65, 0x4d, 0x43, 0x50, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, - 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x75, 0x73, 0x65, 0x4d, 0x43, 0x50, - 0x12, 0x29, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x43, 0x0a, 0x0f, 0x72, - 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x53, 0x75, 0x72, 0x67, 0x65, 0x18, 0x18, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x49, 0x6e, 0x74, 0x4f, 0x72, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, - 0x0f, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x53, 0x75, 0x72, 0x67, 0x65, - 0x12, 0x4f, 0x0a, 0x15, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x55, 0x6e, - 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x4f, 0x72, - 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x42, 0x02, 0x18, 0x01, 0x52, 0x15, 0x72, 0x6f, 0x6c, 0x6c, - 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, - 0x65, 0x12, 0x3d, 0x0a, 0x0b, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x18, 0x1a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, - 0x02, 0x18, 0x01, 0x52, 0x0b, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x12, 0x68, 0x0a, 0x21, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, - 0x6f, 0x6c, 0x53, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x46, 0x6f, 0x72, 0x4f, 0x75, 0x74, - 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, - 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x21, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x53, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x46, - 0x6f, 0x72, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x66, 0x0a, 0x20, 0x65, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x53, 0x6e, 0x69, 0x66, - 0x66, 0x69, 0x6e, 0x67, 0x46, 0x6f, 0x72, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x1d, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x52, 0x20, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, - 0x53, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x46, 0x6f, 0x72, 0x49, 0x6e, 0x62, 0x6f, 0x75, - 0x6e, 0x64, 0x12, 0x43, 0x0a, 0x0e, 0x70, 0x6f, 0x64, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, - 0x75, 0x63, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0e, 0x70, 0x6f, 0x64, 0x41, 0x6e, 0x6e, 0x6f, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x47, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x25, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x12, 0x3f, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x50, 0x69, 0x6c, 0x6f, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x12, 0x38, 0x0a, 0x17, 0x6a, 0x77, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, - 0x72, 0x45, 0x78, 0x74, 0x72, 0x61, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x18, 0x20, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x17, 0x6a, 0x77, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, - 0x45, 0x78, 0x74, 0x72, 0x61, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x12, 0x18, 0x0a, 0x07, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x18, 0x21, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x68, 0x75, 0x62, 0x18, 0x22, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x68, 0x75, 0x62, 0x12, 0x28, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x23, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x74, 0x61, - 0x67, 0x22, 0xb7, 0x01, 0x0a, 0x12, 0x50, 0x69, 0x6c, 0x6f, 0x74, 0x49, 0x6e, 0x67, 0x72, 0x65, - 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x26, 0x0a, 0x0e, 0x69, 0x6e, 0x67, 0x72, - 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x12, 0x55, 0x0a, 0x15, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x1f, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x69, 0x6e, 0x67, 0x72, 0x65, - 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, - 0x52, 0x15, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x6c, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x67, 0x72, 0x65, - 0x73, 0x73, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, - 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x49, 0x0a, 0x11, 0x50, - 0x69, 0x6c, 0x6f, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, - 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x74, 0x0a, 0x0f, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, - 0x74, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, - 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, - 0x2b, 0x0a, 0x02, 0x76, 0x32, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, - 0x56, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x02, 0x76, 0x32, 0x22, 0x92, 0x03, 0x0a, - 0x11, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x56, 0x32, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x58, 0x0a, 0x11, 0x6d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, - 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x56, 0x32, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x52, 0x10, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x12, 0x45, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x56, 0x32, 0x50, 0x72, 0x6f, - 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, - 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x12, 0x48, 0x0a, 0x0b, 0x73, 0x74, 0x61, - 0x63, 0x6b, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, - 0x74, 0x72, 0x79, 0x56, 0x32, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x64, 0x72, 0x69, - 0x76, 0x65, 0x72, 0x12, 0x5c, 0x0a, 0x11, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6c, 0x6f, - 0x67, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, - 0x74, 0x72, 0x79, 0x56, 0x32, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x52, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x22, 0x61, 0x0a, 0x21, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x56, 0x32, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3c, 0x0a, 0x0b, 0x77, 0x61, 0x73, 0x6d, 0x45, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, - 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x77, 0x61, 0x73, 0x6d, 0x45, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x64, 0x22, 0xba, 0x03, 0x0a, 0x1b, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, - 0x72, 0x79, 0x56, 0x32, 0x50, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x3c, 0x0a, 0x0b, 0x77, 0x61, - 0x73, 0x6d, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x77, 0x61, 0x73, - 0x6d, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x5d, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x34, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x65, 0x6c, - 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x56, 0x32, 0x50, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, - 0x75, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4f, - 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4f, - 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x1a, 0xc7, 0x01, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x31, 0x0a, 0x07, 0x67, 0x61, - 0x74, 0x65, 0x77, 0x61, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, - 0x72, 0x75, 0x63, 0x74, 0x52, 0x07, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x3f, 0x0a, - 0x0e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0e, - 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x12, 0x41, - 0x0a, 0x0f, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x69, 0x64, 0x65, 0x63, 0x61, - 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, - 0x52, 0x0f, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x69, 0x64, 0x65, 0x63, 0x61, - 0x72, 0x22, 0x99, 0x05, 0x0a, 0x1c, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x56, - 0x32, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x38, 0x0a, 0x07, 0x6c, 0x6f, 0x67, 0x67, - 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x67, 0x69, - 0x6e, 0x67, 0x12, 0x3a, 0x0a, 0x0a, 0x6d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x0a, 0x6d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x3a, - 0x0a, 0x08, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x02, 0x18, 0x01, - 0x52, 0x08, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x12, 0x44, 0x0a, 0x0f, 0x64, 0x69, - 0x73, 0x61, 0x62, 0x6c, 0x65, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0x12, 0x3f, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, - 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, - 0x74, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, - 0x65, 0x12, 0x6a, 0x0a, 0x15, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x34, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x65, 0x6c, 0x65, - 0x6d, 0x65, 0x74, 0x72, 0x79, 0x56, 0x32, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x44, 0x72, 0x69, 0x76, - 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, - 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x52, 0x15, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x12, 0x68, 0x0a, - 0x14, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, - 0x67, 0x67, 0x69, 0x6e, 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x34, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, - 0x56, 0x32, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, - 0x67, 0x52, 0x14, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x22, 0x34, 0x0a, 0x0d, 0x41, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, - 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x55, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, - 0x45, 0x52, 0x52, 0x4f, 0x52, 0x53, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x02, 0x22, 0xa7, 0x01, - 0x0a, 0x26, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x56, 0x32, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x46, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x47, - 0x0a, 0x11, 0x6c, 0x6f, 0x67, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x44, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x11, 0x6c, 0x6f, 0x67, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x44, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x45, 0x0a, 0x11, 0x50, 0x69, 0x6c, 0x6f, 0x74, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x30, 0x0a, 0x13, - 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x73, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x62, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x22, 0x8d, - 0x01, 0x0a, 0x0b, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x50, 0x6f, - 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x50, 0x6f, - 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x6f, - 0x72, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0xaf, - 0x08, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1e, - 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x49, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x49, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x24, - 0x0a, 0x0d, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x44, 0x6f, - 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x2c, 0x0a, 0x11, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, - 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x11, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, - 0x65, 0x6c, 0x12, 0x42, 0x0a, 0x0e, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x72, 0x65, - 0x44, 0x75, 0x6d, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, - 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, - 0x72, 0x65, 0x44, 0x75, 0x6d, 0x70, 0x12, 0x30, 0x0a, 0x13, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, - 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x0c, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x13, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x49, 0x6e, 0x62, 0x6f, - 0x75, 0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x28, 0x0a, 0x0f, 0x65, 0x78, 0x63, 0x6c, - 0x75, 0x64, 0x65, 0x49, 0x50, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x49, 0x50, 0x52, 0x61, 0x6e, 0x67, - 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, - 0x75, 0x64, 0x65, 0x49, 0x50, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x49, 0x50, 0x52, 0x61, 0x6e, 0x67, - 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x12, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x3a, - 0x0a, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x64, 0x18, 0x13, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, - 0x70, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x64, 0x12, 0x42, 0x0a, 0x1c, 0x72, 0x65, - 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x44, 0x65, - 0x6c, 0x61, 0x79, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x1c, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x69, 0x74, 0x69, - 0x61, 0x6c, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x36, - 0x0a, 0x16, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x50, 0x65, 0x72, 0x69, 0x6f, - 0x64, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x16, - 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x53, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x3c, 0x0a, 0x19, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, - 0x65, 0x73, 0x73, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, - 0x6f, 0x6c, 0x64, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x19, 0x72, 0x65, 0x61, 0x64, 0x69, - 0x6e, 0x65, 0x73, 0x73, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x54, 0x68, 0x72, 0x65, 0x73, - 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x50, 0x6f, - 0x72, 0x74, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x50, 0x6f, 0x72, 0x74, 0x12, 0x35, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x42, 0x02, 0x18, 0x01, - 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x06, 0x74, - 0x72, 0x61, 0x63, 0x65, 0x72, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x72, 0x52, 0x06, 0x74, - 0x72, 0x61, 0x63, 0x65, 0x72, 0x12, 0x32, 0x0a, 0x14, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, - 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x1c, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x14, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4f, 0x75, 0x74, 0x62, - 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x35, 0x0a, 0x09, 0x6c, 0x69, 0x66, - 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x18, 0x24, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, - 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x09, 0x6c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, - 0x12, 0x68, 0x0a, 0x1f, 0x68, 0x6f, 0x6c, 0x64, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x74, 0x61, - 0x72, 0x74, 0x73, 0x18, 0x25, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x02, 0x18, 0x01, 0x52, 0x1f, 0x68, 0x6f, 0x6c, 0x64, 0x41, - 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x50, - 0x72, 0x6f, 0x78, 0x79, 0x53, 0x74, 0x61, 0x72, 0x74, 0x73, 0x12, 0x30, 0x0a, 0x13, 0x69, 0x6e, - 0x63, 0x6c, 0x75, 0x64, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, - 0x73, 0x18, 0x26, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, - 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x12, 0x32, 0x0a, 0x14, - 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, - 0x6f, 0x72, 0x74, 0x73, 0x18, 0x27, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x69, 0x6e, 0x63, 0x6c, - 0x75, 0x64, 0x65, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x73, - 0x22, 0x5e, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x49, 0x6e, 0x69, 0x74, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x35, 0x0a, 0x09, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x22, 0x43, 0x0a, 0x17, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x63, - 0x70, 0x75, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x63, 0x70, 0x75, 0x12, 0x16, 0x0a, - 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, - 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x22, 0x3e, 0x0a, 0x09, 0x53, 0x44, 0x53, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x31, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x05, - 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x60, 0x0a, 0x0c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x61, - 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x50, - 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x96, 0x01, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, 0x0a, 0x0b, 0x61, 0x6e, 0x6e, - 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x50, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x22, 0xba, 0x05, 0x0a, 0x15, 0x53, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x49, 0x6e, 0x6a, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x58, 0x0a, 0x19, 0x65, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x42, 0x79, - 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x19, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x42, 0x79, 0x44, 0x65, 0x66, - 0x61, 0x75, 0x6c, 0x74, 0x12, 0x49, 0x0a, 0x13, 0x6e, 0x65, 0x76, 0x65, 0x72, 0x49, 0x6e, 0x6a, - 0x65, 0x63, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x0b, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x13, 0x6e, 0x65, 0x76, 0x65, - 0x72, 0x49, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, - 0x4b, 0x0a, 0x14, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x49, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x53, - 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x14, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x49, 0x6e, - 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x4c, 0x0a, 0x13, - 0x72, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48, 0x54, 0x54, 0x50, 0x50, 0x72, - 0x6f, 0x62, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x72, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x41, 0x70, - 0x70, 0x48, 0x54, 0x54, 0x50, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x49, 0x0a, 0x13, 0x69, 0x6e, - 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, - 0x52, 0x13, 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3f, 0x0a, 0x0e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, - 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x55, 0x52, 0x4c, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, - 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x52, 0x4c, 0x12, 0x35, 0x0a, 0x09, 0x74, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x54, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x73, 0x18, 0x18, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x64, 0x65, 0x66, - 0x61, 0x75, 0x6c, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0x4e, 0x0a, - 0x12, 0x75, 0x73, 0x65, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x02, 0x18, 0x01, 0x52, 0x12, 0x75, 0x73, 0x65, 0x4c, 0x65, - 0x67, 0x61, 0x63, 0x79, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x22, 0x81, 0x02, - 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x63, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x37, - 0x0a, 0x07, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, - 0x72, 0x44, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, - 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x12, 0x3d, 0x0a, 0x09, 0x6c, 0x69, 0x67, 0x68, 0x74, - 0x73, 0x74, 0x65, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x72, 0x4c, 0x69, 0x67, 0x68, - 0x74, 0x53, 0x74, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x6c, 0x69, 0x67, - 0x68, 0x74, 0x73, 0x74, 0x65, 0x70, 0x12, 0x34, 0x0a, 0x06, 0x7a, 0x69, 0x70, 0x6b, 0x69, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x72, 0x5a, 0x69, 0x70, 0x6b, 0x69, 0x6e, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x7a, 0x69, 0x70, 0x6b, 0x69, 0x6e, 0x12, 0x43, 0x0a, 0x0b, - 0x73, 0x74, 0x61, 0x63, 0x6b, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x72, 0x61, - 0x63, 0x65, 0x72, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x64, 0x72, 0x69, 0x76, 0x65, - 0x72, 0x22, 0x2f, 0x0a, 0x13, 0x54, 0x72, 0x61, 0x63, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x64, - 0x6f, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x22, 0x53, 0x0a, 0x15, 0x54, 0x72, 0x61, 0x63, 0x65, 0x72, 0x4c, 0x69, 0x67, 0x68, - 0x74, 0x53, 0x74, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x2e, 0x0a, 0x12, 0x54, 0x72, 0x61, 0x63, 0x65, - 0x72, 0x5a, 0x69, 0x70, 0x6b, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, - 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0xf5, 0x01, 0x0a, 0x17, 0x54, 0x72, 0x61, 0x63, - 0x65, 0x72, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x12, 0x30, 0x0a, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, - 0x64, 0x65, 0x62, 0x75, 0x67, 0x12, 0x34, 0x0a, 0x15, 0x6d, 0x61, 0x78, 0x4e, 0x75, 0x6d, 0x62, - 0x65, 0x72, 0x4f, 0x66, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x15, 0x6d, 0x61, 0x78, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, - 0x66, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x36, 0x0a, 0x16, 0x6d, - 0x61, 0x78, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x16, 0x6d, 0x61, 0x78, - 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x18, 0x6d, 0x61, 0x78, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, - 0x4f, 0x66, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x18, 0x6d, 0x61, 0x78, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, - 0x4f, 0x66, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, - 0xd0, 0x01, 0x0a, 0x0a, 0x42, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x4a, - 0x0a, 0x12, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x52, 0x44, 0x54, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, - 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x52, - 0x44, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x52, 0x4c, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0d, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x52, 0x4c, - 0x12, 0x50, 0x0a, 0x15, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x73, 0x74, 0x69, 0x6f, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x43, 0x52, 0x44, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x15, 0x65, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x49, 0x73, 0x74, 0x69, 0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x43, 0x52, - 0x44, 0x73, 0x22, 0x5e, 0x0a, 0x12, 0x49, 0x73, 0x74, 0x69, 0x6f, 0x64, 0x52, 0x65, 0x6d, 0x6f, - 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x6a, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, - 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x52, 0x4c, 0x12, 0x24, 0x0a, 0x0d, - 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, - 0x74, 0x68, 0x22, 0xb2, 0x05, 0x0a, 0x06, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x25, 0x0a, - 0x03, 0x63, 0x6e, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x4e, 0x49, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, - 0x03, 0x63, 0x6e, 0x69, 0x12, 0x34, 0x0a, 0x08, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x52, 0x08, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x67, 0x6c, - 0x6f, 0x62, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x06, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x12, 0x2b, 0x0a, 0x05, 0x70, 0x69, - 0x6c, 0x6f, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x69, 0x6c, 0x6f, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x52, 0x05, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x12, 0x37, 0x0a, 0x09, 0x74, 0x65, 0x6c, 0x65, 0x6d, - 0x65, 0x74, 0x72, 0x79, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, - 0x12, 0x57, 0x0a, 0x16, 0x73, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x49, 0x6e, 0x6a, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1f, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x64, 0x65, - 0x63, 0x61, 0x72, 0x49, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x52, 0x16, 0x73, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x49, 0x6e, 0x6a, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x12, 0x30, 0x0a, 0x09, 0x69, 0x73, 0x74, - 0x69, 0x6f, 0x5f, 0x63, 0x6e, 0x69, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x4e, 0x49, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x52, 0x08, 0x69, 0x73, 0x74, 0x69, 0x6f, 0x43, 0x6e, 0x69, 0x12, 0x1a, 0x0a, 0x08, 0x72, - 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, - 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x77, 0x6e, 0x65, 0x72, - 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x77, 0x6e, 0x65, - 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x18, 0x24, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x52, 0x0a, 0x6d, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x28, 0x0a, - 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x25, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x69, 0x73, 0x74, 0x69, 0x6f, - 0x64, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x18, 0x26, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x73, 0x74, 0x69, 0x6f, 0x64, 0x52, - 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x69, 0x73, 0x74, - 0x69, 0x6f, 0x64, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x72, 0x65, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x67, 0x73, 0x18, 0x27, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0c, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x67, 0x73, 0x12, 0x28, 0x0a, - 0x0f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, - 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x0a, 0x0d, 0x5a, 0x65, 0x72, 0x6f, 0x56, - 0x50, 0x4e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x16, - 0x0a, 0x06, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x22, 0x8c, 0x01, 0x0a, 0x0b, 0x49, 0x6e, 0x74, 0x4f, 0x72, - 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x69, 0x6e, - 0x74, 0x56, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, - 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x12, - 0x34, 0x0a, 0x06, 0x73, 0x74, 0x72, 0x56, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x73, - 0x74, 0x72, 0x56, 0x61, 0x6c, 0x2a, 0x4a, 0x0a, 0x15, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, - 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0f, - 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, - 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, - 0x53, 0x54, 0x52, 0x49, 0x43, 0x54, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x46, 0x46, 0x10, - 0x03, 0x2a, 0x60, 0x0a, 0x06, 0x74, 0x72, 0x61, 0x63, 0x65, 0x72, 0x12, 0x0a, 0x0a, 0x06, 0x7a, - 0x69, 0x70, 0x6b, 0x69, 0x6e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x6c, 0x69, 0x67, 0x68, 0x74, - 0x73, 0x74, 0x65, 0x70, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, - 0x67, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x64, 0x72, 0x69, 0x76, - 0x65, 0x72, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x6f, 0x70, 0x65, 0x6e, 0x43, 0x65, 0x6e, 0x73, - 0x75, 0x73, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, 0x6e, 0x6f, 0x6e, - 0x65, 0x10, 0x05, 0x42, 0x43, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2d, 0x67, - 0x6f, 0x2d, 0x70, 0x69, 0x78, 0x69, 0x75, 0x2f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, - 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x69, 0x73, 0x74, 0x69, 0x6f, 0x2f, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescOnce sync.Once - file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescData = file_pkg_apis_istio_v1alpha1_values_types_proto_rawDesc -) - -func file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescGZIP() []byte { - file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescOnce.Do(func() { - file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescData = protoimpl.X.CompressGZIP(file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescData) - }) - return file_pkg_apis_istio_v1alpha1_values_types_proto_rawDescData -} - -var file_pkg_apis_istio_v1alpha1_values_types_proto_enumTypes = make([]protoimpl.EnumInfo, 4) -var file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes = make([]protoimpl.MessageInfo, 53) -var file_pkg_apis_istio_v1alpha1_values_types_proto_goTypes = []interface{}{ - (IngressControllerMode)(0), // 0: v1alpha1.ingressControllerMode - (Tracer)(0), // 1: v1alpha1.tracer - (OutboundTrafficPolicyConfig_Mode)(0), // 2: v1alpha1.OutboundTrafficPolicyConfig.Mode - (TelemetryV2StackDriverConfig_AccessLogging)(0), // 3: v1alpha1.TelemetryV2StackDriverConfig.AccessLogging - (*ArchConfig)(nil), // 4: v1alpha1.ArchConfig - (*CNIConfig)(nil), // 5: v1alpha1.CNIConfig - (*CNITaintConfig)(nil), // 6: v1alpha1.CNITaintConfig - (*CNIRepairConfig)(nil), // 7: v1alpha1.CNIRepairConfig - (*ResourceQuotas)(nil), // 8: v1alpha1.ResourceQuotas - (*CPUTargetUtilizationConfig)(nil), // 9: v1alpha1.CPUTargetUtilizationConfig - (*Resources)(nil), // 10: v1alpha1.Resources - (*ServiceAccount)(nil), // 11: v1alpha1.ServiceAccount - (*DefaultPodDisruptionBudgetConfig)(nil), // 12: v1alpha1.DefaultPodDisruptionBudgetConfig - (*DefaultResourcesConfig)(nil), // 13: v1alpha1.DefaultResourcesConfig - (*EgressGatewayConfig)(nil), // 14: v1alpha1.EgressGatewayConfig - (*GatewaysConfig)(nil), // 15: v1alpha1.GatewaysConfig - (*GlobalConfig)(nil), // 16: v1alpha1.GlobalConfig - (*STSConfig)(nil), // 17: v1alpha1.STSConfig - (*IstiodConfig)(nil), // 18: v1alpha1.IstiodConfig - (*GlobalLoggingConfig)(nil), // 19: v1alpha1.GlobalLoggingConfig - (*IngressGatewayConfig)(nil), // 20: v1alpha1.IngressGatewayConfig - (*IngressGatewayZvpnConfig)(nil), // 21: v1alpha1.IngressGatewayZvpnConfig - (*MultiClusterConfig)(nil), // 22: v1alpha1.MultiClusterConfig - (*OutboundTrafficPolicyConfig)(nil), // 23: v1alpha1.OutboundTrafficPolicyConfig - (*PilotConfig)(nil), // 24: v1alpha1.PilotConfig - (*PilotIngressConfig)(nil), // 25: v1alpha1.PilotIngressConfig - (*PilotPolicyConfig)(nil), // 26: v1alpha1.PilotPolicyConfig - (*TelemetryConfig)(nil), // 27: v1alpha1.TelemetryConfig - (*TelemetryV2Config)(nil), // 28: v1alpha1.TelemetryV2Config - (*TelemetryV2MetadataExchangeConfig)(nil), // 29: v1alpha1.TelemetryV2MetadataExchangeConfig - (*TelemetryV2PrometheusConfig)(nil), // 30: v1alpha1.TelemetryV2PrometheusConfig - (*TelemetryV2StackDriverConfig)(nil), // 31: v1alpha1.TelemetryV2StackDriverConfig - (*TelemetryV2AccessLogPolicyFilterConfig)(nil), // 32: v1alpha1.TelemetryV2AccessLogPolicyFilterConfig - (*PilotConfigSource)(nil), // 33: v1alpha1.PilotConfigSource - (*PortsConfig)(nil), // 34: v1alpha1.PortsConfig - (*ProxyConfig)(nil), // 35: v1alpha1.ProxyConfig - (*ProxyInitConfig)(nil), // 36: v1alpha1.ProxyInitConfig - (*ResourcesRequestsConfig)(nil), // 37: v1alpha1.ResourcesRequestsConfig - (*SDSConfig)(nil), // 38: v1alpha1.SDSConfig - (*SecretVolume)(nil), // 39: v1alpha1.SecretVolume - (*ServiceConfig)(nil), // 40: v1alpha1.ServiceConfig - (*SidecarInjectorConfig)(nil), // 41: v1alpha1.SidecarInjectorConfig - (*TracerConfig)(nil), // 42: v1alpha1.TracerConfig - (*TracerDatadogConfig)(nil), // 43: v1alpha1.TracerDatadogConfig - (*TracerLightStepConfig)(nil), // 44: v1alpha1.TracerLightStepConfig - (*TracerZipkinConfig)(nil), // 45: v1alpha1.TracerZipkinConfig - (*TracerStackdriverConfig)(nil), // 46: v1alpha1.TracerStackdriverConfig - (*BaseConfig)(nil), // 47: v1alpha1.BaseConfig - (*IstiodRemoteConfig)(nil), // 48: v1alpha1.IstiodRemoteConfig - (*Values)(nil), // 49: v1alpha1.Values - (*ZeroVPNConfig)(nil), // 50: v1alpha1.ZeroVPNConfig - (*IntOrString)(nil), // 51: v1alpha1.IntOrString - nil, // 52: v1alpha1.Resources.LimitsEntry - nil, // 53: v1alpha1.Resources.RequestsEntry - nil, // 54: v1alpha1.EgressGatewayConfig.LabelsEntry - nil, // 55: v1alpha1.IngressGatewayConfig.LabelsEntry - (*TelemetryV2PrometheusConfig_ConfigOverride)(nil), // 56: v1alpha1.TelemetryV2PrometheusConfig.ConfigOverride - (*wrapperspb.BoolValue)(nil), // 57: google.protobuf.BoolValue - (*structpb.Value)(nil), // 58: google.protobuf.Value - (*structpb.Struct)(nil), // 59: google.protobuf.Struct - (*durationpb.Duration)(nil), // 60: google.protobuf.Duration - (*wrapperspb.Int32Value)(nil), // 61: google.protobuf.Int32Value - (*wrapperspb.StringValue)(nil), // 62: google.protobuf.StringValue -} -var file_pkg_apis_istio_v1alpha1_values_types_proto_depIdxs = []int32{ - 57, // 0: v1alpha1.CNIConfig.enabled:type_name -> google.protobuf.BoolValue - 58, // 1: v1alpha1.CNIConfig.tag:type_name -> google.protobuf.Value - 59, // 2: v1alpha1.CNIConfig.podAnnotations:type_name -> google.protobuf.Struct - 7, // 3: v1alpha1.CNIConfig.repair:type_name -> v1alpha1.CNIRepairConfig - 57, // 4: v1alpha1.CNIConfig.chained:type_name -> google.protobuf.BoolValue - 6, // 5: v1alpha1.CNIConfig.taint:type_name -> v1alpha1.CNITaintConfig - 8, // 6: v1alpha1.CNIConfig.resource_quotas:type_name -> v1alpha1.ResourceQuotas - 10, // 7: v1alpha1.CNIConfig.resources:type_name -> v1alpha1.Resources - 57, // 8: v1alpha1.CNIConfig.privileged:type_name -> google.protobuf.BoolValue - 57, // 9: v1alpha1.CNITaintConfig.enabled:type_name -> google.protobuf.BoolValue - 57, // 10: v1alpha1.CNIRepairConfig.enabled:type_name -> google.protobuf.BoolValue - 58, // 11: v1alpha1.CNIRepairConfig.tag:type_name -> google.protobuf.Value - 57, // 12: v1alpha1.ResourceQuotas.enabled:type_name -> google.protobuf.BoolValue - 52, // 13: v1alpha1.Resources.limits:type_name -> v1alpha1.Resources.LimitsEntry - 53, // 14: v1alpha1.Resources.requests:type_name -> v1alpha1.Resources.RequestsEntry - 59, // 15: v1alpha1.ServiceAccount.annotations:type_name -> google.protobuf.Struct - 57, // 16: v1alpha1.DefaultPodDisruptionBudgetConfig.enabled:type_name -> google.protobuf.BoolValue - 37, // 17: v1alpha1.DefaultResourcesConfig.requests:type_name -> v1alpha1.ResourcesRequestsConfig - 57, // 18: v1alpha1.EgressGatewayConfig.autoscaleEnabled:type_name -> google.protobuf.BoolValue - 9, // 19: v1alpha1.EgressGatewayConfig.cpu:type_name -> v1alpha1.CPUTargetUtilizationConfig - 57, // 20: v1alpha1.EgressGatewayConfig.enabled:type_name -> google.protobuf.BoolValue - 59, // 21: v1alpha1.EgressGatewayConfig.env:type_name -> google.protobuf.Struct - 54, // 22: v1alpha1.EgressGatewayConfig.labels:type_name -> v1alpha1.EgressGatewayConfig.LabelsEntry - 59, // 23: v1alpha1.EgressGatewayConfig.nodeSelector:type_name -> google.protobuf.Struct - 59, // 24: v1alpha1.EgressGatewayConfig.podAnnotations:type_name -> google.protobuf.Struct - 59, // 25: v1alpha1.EgressGatewayConfig.podAntiAffinityLabelSelector:type_name -> google.protobuf.Struct - 59, // 26: v1alpha1.EgressGatewayConfig.podAntiAffinityTermLabelSelector:type_name -> google.protobuf.Struct - 34, // 27: v1alpha1.EgressGatewayConfig.ports:type_name -> v1alpha1.PortsConfig - 10, // 28: v1alpha1.EgressGatewayConfig.resources:type_name -> v1alpha1.Resources - 39, // 29: v1alpha1.EgressGatewayConfig.secretVolumes:type_name -> v1alpha1.SecretVolume - 59, // 30: v1alpha1.EgressGatewayConfig.serviceAnnotations:type_name -> google.protobuf.Struct - 50, // 31: v1alpha1.EgressGatewayConfig.zvpn:type_name -> v1alpha1.ZeroVPNConfig - 59, // 32: v1alpha1.EgressGatewayConfig.tolerations:type_name -> google.protobuf.Struct - 51, // 33: v1alpha1.EgressGatewayConfig.rollingMaxSurge:type_name -> v1alpha1.IntOrString - 51, // 34: v1alpha1.EgressGatewayConfig.rollingMaxUnavailable:type_name -> v1alpha1.IntOrString - 59, // 35: v1alpha1.EgressGatewayConfig.configVolumes:type_name -> google.protobuf.Struct - 59, // 36: v1alpha1.EgressGatewayConfig.additionalContainers:type_name -> google.protobuf.Struct - 57, // 37: v1alpha1.EgressGatewayConfig.runAsRoot:type_name -> google.protobuf.BoolValue - 11, // 38: v1alpha1.EgressGatewayConfig.serviceAccount:type_name -> v1alpha1.ServiceAccount - 14, // 39: v1alpha1.GatewaysConfig.istio_egressgateway:type_name -> v1alpha1.EgressGatewayConfig - 57, // 40: v1alpha1.GatewaysConfig.enabled:type_name -> google.protobuf.BoolValue - 20, // 41: v1alpha1.GatewaysConfig.istio_ingressgateway:type_name -> v1alpha1.IngressGatewayConfig - 4, // 42: v1alpha1.GlobalConfig.arch:type_name -> v1alpha1.ArchConfig - 57, // 43: v1alpha1.GlobalConfig.configValidation:type_name -> google.protobuf.BoolValue - 59, // 44: v1alpha1.GlobalConfig.defaultNodeSelector:type_name -> google.protobuf.Struct - 12, // 45: v1alpha1.GlobalConfig.defaultPodDisruptionBudget:type_name -> v1alpha1.DefaultPodDisruptionBudgetConfig - 13, // 46: v1alpha1.GlobalConfig.defaultResources:type_name -> v1alpha1.DefaultResourcesConfig - 59, // 47: v1alpha1.GlobalConfig.defaultTolerations:type_name -> google.protobuf.Struct - 57, // 48: v1alpha1.GlobalConfig.logAsJson:type_name -> google.protobuf.BoolValue - 19, // 49: v1alpha1.GlobalConfig.logging:type_name -> v1alpha1.GlobalLoggingConfig - 59, // 50: v1alpha1.GlobalConfig.meshNetworks:type_name -> google.protobuf.Struct - 22, // 51: v1alpha1.GlobalConfig.multiCluster:type_name -> v1alpha1.MultiClusterConfig - 57, // 52: v1alpha1.GlobalConfig.omitSidecarInjectorConfigMap:type_name -> google.protobuf.BoolValue - 57, // 53: v1alpha1.GlobalConfig.oneNamespace:type_name -> google.protobuf.BoolValue - 57, // 54: v1alpha1.GlobalConfig.operatorManageWebhooks:type_name -> google.protobuf.BoolValue - 35, // 55: v1alpha1.GlobalConfig.proxy:type_name -> v1alpha1.ProxyConfig - 36, // 56: v1alpha1.GlobalConfig.proxy_init:type_name -> v1alpha1.ProxyInitConfig - 38, // 57: v1alpha1.GlobalConfig.sds:type_name -> v1alpha1.SDSConfig - 58, // 58: v1alpha1.GlobalConfig.tag:type_name -> google.protobuf.Value - 42, // 59: v1alpha1.GlobalConfig.tracer:type_name -> v1alpha1.TracerConfig - 57, // 60: v1alpha1.GlobalConfig.useMCP:type_name -> google.protobuf.BoolValue - 18, // 61: v1alpha1.GlobalConfig.istiod:type_name -> v1alpha1.IstiodConfig - 17, // 62: v1alpha1.GlobalConfig.sts:type_name -> v1alpha1.STSConfig - 57, // 63: v1alpha1.GlobalConfig.mountMtlsCerts:type_name -> google.protobuf.BoolValue - 57, // 64: v1alpha1.GlobalConfig.externalIstiod:type_name -> google.protobuf.BoolValue - 57, // 65: v1alpha1.GlobalConfig.configCluster:type_name -> google.protobuf.BoolValue - 57, // 66: v1alpha1.GlobalConfig.autoscalingv2API:type_name -> google.protobuf.BoolValue - 57, // 67: v1alpha1.IstiodConfig.enableAnalysis:type_name -> google.protobuf.BoolValue - 57, // 68: v1alpha1.IngressGatewayConfig.autoscaleEnabled:type_name -> google.protobuf.BoolValue - 9, // 69: v1alpha1.IngressGatewayConfig.cpu:type_name -> v1alpha1.CPUTargetUtilizationConfig - 57, // 70: v1alpha1.IngressGatewayConfig.customService:type_name -> google.protobuf.BoolValue - 57, // 71: v1alpha1.IngressGatewayConfig.enabled:type_name -> google.protobuf.BoolValue - 59, // 72: v1alpha1.IngressGatewayConfig.env:type_name -> google.protobuf.Struct - 55, // 73: v1alpha1.IngressGatewayConfig.labels:type_name -> v1alpha1.IngressGatewayConfig.LabelsEntry - 59, // 74: v1alpha1.IngressGatewayConfig.nodeSelector:type_name -> google.protobuf.Struct - 59, // 75: v1alpha1.IngressGatewayConfig.podAnnotations:type_name -> google.protobuf.Struct - 59, // 76: v1alpha1.IngressGatewayConfig.podAntiAffinityLabelSelector:type_name -> google.protobuf.Struct - 59, // 77: v1alpha1.IngressGatewayConfig.podAntiAffinityTermLabelSelector:type_name -> google.protobuf.Struct - 34, // 78: v1alpha1.IngressGatewayConfig.ports:type_name -> v1alpha1.PortsConfig - 59, // 79: v1alpha1.IngressGatewayConfig.resources:type_name -> google.protobuf.Struct - 39, // 80: v1alpha1.IngressGatewayConfig.secretVolumes:type_name -> v1alpha1.SecretVolume - 59, // 81: v1alpha1.IngressGatewayConfig.serviceAnnotations:type_name -> google.protobuf.Struct - 21, // 82: v1alpha1.IngressGatewayConfig.zvpn:type_name -> v1alpha1.IngressGatewayZvpnConfig - 51, // 83: v1alpha1.IngressGatewayConfig.rollingMaxSurge:type_name -> v1alpha1.IntOrString - 51, // 84: v1alpha1.IngressGatewayConfig.rollingMaxUnavailable:type_name -> v1alpha1.IntOrString - 59, // 85: v1alpha1.IngressGatewayConfig.tolerations:type_name -> google.protobuf.Struct - 59, // 86: v1alpha1.IngressGatewayConfig.ingressPorts:type_name -> google.protobuf.Struct - 59, // 87: v1alpha1.IngressGatewayConfig.additionalContainers:type_name -> google.protobuf.Struct - 59, // 88: v1alpha1.IngressGatewayConfig.configVolumes:type_name -> google.protobuf.Struct - 57, // 89: v1alpha1.IngressGatewayConfig.runAsRoot:type_name -> google.protobuf.BoolValue - 11, // 90: v1alpha1.IngressGatewayConfig.serviceAccount:type_name -> v1alpha1.ServiceAccount - 57, // 91: v1alpha1.IngressGatewayZvpnConfig.enabled:type_name -> google.protobuf.BoolValue - 57, // 92: v1alpha1.MultiClusterConfig.enabled:type_name -> google.protobuf.BoolValue - 57, // 93: v1alpha1.MultiClusterConfig.includeEnvoyFilter:type_name -> google.protobuf.BoolValue - 2, // 94: v1alpha1.OutboundTrafficPolicyConfig.mode:type_name -> v1alpha1.OutboundTrafficPolicyConfig.Mode - 57, // 95: v1alpha1.PilotConfig.enabled:type_name -> google.protobuf.BoolValue - 57, // 96: v1alpha1.PilotConfig.autoscaleEnabled:type_name -> google.protobuf.BoolValue - 10, // 97: v1alpha1.PilotConfig.resources:type_name -> v1alpha1.Resources - 9, // 98: v1alpha1.PilotConfig.cpu:type_name -> v1alpha1.CPUTargetUtilizationConfig - 59, // 99: v1alpha1.PilotConfig.nodeSelector:type_name -> google.protobuf.Struct - 60, // 100: v1alpha1.PilotConfig.keepaliveMaxServerConnectionAge:type_name -> google.protobuf.Duration - 59, // 101: v1alpha1.PilotConfig.deploymentLabels:type_name -> google.protobuf.Struct - 59, // 102: v1alpha1.PilotConfig.podLabels:type_name -> google.protobuf.Struct - 57, // 103: v1alpha1.PilotConfig.configMap:type_name -> google.protobuf.BoolValue - 57, // 104: v1alpha1.PilotConfig.useMCP:type_name -> google.protobuf.BoolValue - 59, // 105: v1alpha1.PilotConfig.env:type_name -> google.protobuf.Struct - 51, // 106: v1alpha1.PilotConfig.rollingMaxSurge:type_name -> v1alpha1.IntOrString - 51, // 107: v1alpha1.PilotConfig.rollingMaxUnavailable:type_name -> v1alpha1.IntOrString - 59, // 108: v1alpha1.PilotConfig.tolerations:type_name -> google.protobuf.Struct - 57, // 109: v1alpha1.PilotConfig.enableProtocolSniffingForOutbound:type_name -> google.protobuf.BoolValue - 57, // 110: v1alpha1.PilotConfig.enableProtocolSniffingForInbound:type_name -> google.protobuf.BoolValue - 59, // 111: v1alpha1.PilotConfig.podAnnotations:type_name -> google.protobuf.Struct - 59, // 112: v1alpha1.PilotConfig.serviceAnnotations:type_name -> google.protobuf.Struct - 33, // 113: v1alpha1.PilotConfig.configSource:type_name -> v1alpha1.PilotConfigSource - 58, // 114: v1alpha1.PilotConfig.tag:type_name -> google.protobuf.Value - 0, // 115: v1alpha1.PilotIngressConfig.ingressControllerMode:type_name -> v1alpha1.ingressControllerMode - 57, // 116: v1alpha1.PilotPolicyConfig.enabled:type_name -> google.protobuf.BoolValue - 57, // 117: v1alpha1.TelemetryConfig.enabled:type_name -> google.protobuf.BoolValue - 28, // 118: v1alpha1.TelemetryConfig.v2:type_name -> v1alpha1.TelemetryV2Config - 57, // 119: v1alpha1.TelemetryV2Config.enabled:type_name -> google.protobuf.BoolValue - 29, // 120: v1alpha1.TelemetryV2Config.metadata_exchange:type_name -> v1alpha1.TelemetryV2MetadataExchangeConfig - 30, // 121: v1alpha1.TelemetryV2Config.prometheus:type_name -> v1alpha1.TelemetryV2PrometheusConfig - 31, // 122: v1alpha1.TelemetryV2Config.stackdriver:type_name -> v1alpha1.TelemetryV2StackDriverConfig - 32, // 123: v1alpha1.TelemetryV2Config.access_log_policy:type_name -> v1alpha1.TelemetryV2AccessLogPolicyFilterConfig - 57, // 124: v1alpha1.TelemetryV2MetadataExchangeConfig.wasmEnabled:type_name -> google.protobuf.BoolValue - 57, // 125: v1alpha1.TelemetryV2PrometheusConfig.enabled:type_name -> google.protobuf.BoolValue - 57, // 126: v1alpha1.TelemetryV2PrometheusConfig.wasmEnabled:type_name -> google.protobuf.BoolValue - 56, // 127: v1alpha1.TelemetryV2PrometheusConfig.config_override:type_name -> v1alpha1.TelemetryV2PrometheusConfig.ConfigOverride - 57, // 128: v1alpha1.TelemetryV2StackDriverConfig.enabled:type_name -> google.protobuf.BoolValue - 57, // 129: v1alpha1.TelemetryV2StackDriverConfig.logging:type_name -> google.protobuf.BoolValue - 57, // 130: v1alpha1.TelemetryV2StackDriverConfig.monitoring:type_name -> google.protobuf.BoolValue - 57, // 131: v1alpha1.TelemetryV2StackDriverConfig.topology:type_name -> google.protobuf.BoolValue - 57, // 132: v1alpha1.TelemetryV2StackDriverConfig.disableOutbound:type_name -> google.protobuf.BoolValue - 59, // 133: v1alpha1.TelemetryV2StackDriverConfig.configOverride:type_name -> google.protobuf.Struct - 3, // 134: v1alpha1.TelemetryV2StackDriverConfig.outboundAccessLogging:type_name -> v1alpha1.TelemetryV2StackDriverConfig.AccessLogging - 3, // 135: v1alpha1.TelemetryV2StackDriverConfig.inboundAccessLogging:type_name -> v1alpha1.TelemetryV2StackDriverConfig.AccessLogging - 57, // 136: v1alpha1.TelemetryV2AccessLogPolicyFilterConfig.enabled:type_name -> google.protobuf.BoolValue - 60, // 137: v1alpha1.TelemetryV2AccessLogPolicyFilterConfig.logWindowDuration:type_name -> google.protobuf.Duration - 57, // 138: v1alpha1.ProxyConfig.enableCoreDump:type_name -> google.protobuf.BoolValue - 57, // 139: v1alpha1.ProxyConfig.privileged:type_name -> google.protobuf.BoolValue - 10, // 140: v1alpha1.ProxyConfig.resources:type_name -> v1alpha1.Resources - 1, // 141: v1alpha1.ProxyConfig.tracer:type_name -> v1alpha1.tracer - 59, // 142: v1alpha1.ProxyConfig.lifecycle:type_name -> google.protobuf.Struct - 57, // 143: v1alpha1.ProxyConfig.holdApplicationUntilProxyStarts:type_name -> google.protobuf.BoolValue - 10, // 144: v1alpha1.ProxyInitConfig.resources:type_name -> v1alpha1.Resources - 59, // 145: v1alpha1.SDSConfig.token:type_name -> google.protobuf.Struct - 59, // 146: v1alpha1.ServiceConfig.annotations:type_name -> google.protobuf.Struct - 57, // 147: v1alpha1.SidecarInjectorConfig.enableNamespacesByDefault:type_name -> google.protobuf.BoolValue - 59, // 148: v1alpha1.SidecarInjectorConfig.neverInjectSelector:type_name -> google.protobuf.Struct - 59, // 149: v1alpha1.SidecarInjectorConfig.alwaysInjectSelector:type_name -> google.protobuf.Struct - 57, // 150: v1alpha1.SidecarInjectorConfig.rewriteAppHTTPProbe:type_name -> google.protobuf.BoolValue - 59, // 151: v1alpha1.SidecarInjectorConfig.injectedAnnotations:type_name -> google.protobuf.Struct - 59, // 152: v1alpha1.SidecarInjectorConfig.objectSelector:type_name -> google.protobuf.Struct - 59, // 153: v1alpha1.SidecarInjectorConfig.templates:type_name -> google.protobuf.Struct - 57, // 154: v1alpha1.SidecarInjectorConfig.useLegacySelectors:type_name -> google.protobuf.BoolValue - 43, // 155: v1alpha1.TracerConfig.datadog:type_name -> v1alpha1.TracerDatadogConfig - 44, // 156: v1alpha1.TracerConfig.lightstep:type_name -> v1alpha1.TracerLightStepConfig - 45, // 157: v1alpha1.TracerConfig.zipkin:type_name -> v1alpha1.TracerZipkinConfig - 46, // 158: v1alpha1.TracerConfig.stackdriver:type_name -> v1alpha1.TracerStackdriverConfig - 57, // 159: v1alpha1.TracerStackdriverConfig.debug:type_name -> google.protobuf.BoolValue - 57, // 160: v1alpha1.BaseConfig.enableCRDTemplates:type_name -> google.protobuf.BoolValue - 57, // 161: v1alpha1.BaseConfig.enableIstioConfigCRDs:type_name -> google.protobuf.BoolValue - 5, // 162: v1alpha1.Values.cni:type_name -> v1alpha1.CNIConfig - 15, // 163: v1alpha1.Values.gateways:type_name -> v1alpha1.GatewaysConfig - 16, // 164: v1alpha1.Values.global:type_name -> v1alpha1.GlobalConfig - 24, // 165: v1alpha1.Values.pilot:type_name -> v1alpha1.PilotConfig - 27, // 166: v1alpha1.Values.telemetry:type_name -> v1alpha1.TelemetryConfig - 41, // 167: v1alpha1.Values.sidecarInjectorWebhook:type_name -> v1alpha1.SidecarInjectorConfig - 5, // 168: v1alpha1.Values.istio_cni:type_name -> v1alpha1.CNIConfig - 58, // 169: v1alpha1.Values.meshConfig:type_name -> google.protobuf.Value - 47, // 170: v1alpha1.Values.base:type_name -> v1alpha1.BaseConfig - 48, // 171: v1alpha1.Values.istiodRemote:type_name -> v1alpha1.IstiodRemoteConfig - 57, // 172: v1alpha1.ZeroVPNConfig.enabled:type_name -> google.protobuf.BoolValue - 61, // 173: v1alpha1.IntOrString.intVal:type_name -> google.protobuf.Int32Value - 62, // 174: v1alpha1.IntOrString.strVal:type_name -> google.protobuf.StringValue - 59, // 175: v1alpha1.TelemetryV2PrometheusConfig.ConfigOverride.gateway:type_name -> google.protobuf.Struct - 59, // 176: v1alpha1.TelemetryV2PrometheusConfig.ConfigOverride.inboundSidecar:type_name -> google.protobuf.Struct - 59, // 177: v1alpha1.TelemetryV2PrometheusConfig.ConfigOverride.outboundSidecar:type_name -> google.protobuf.Struct - 178, // [178:178] is the sub-list for method output_type - 178, // [178:178] is the sub-list for method input_type - 178, // [178:178] is the sub-list for extension type_name - 178, // [178:178] is the sub-list for extension extendee - 0, // [0:178] is the sub-list for field type_name -} - -func init() { file_pkg_apis_istio_v1alpha1_values_types_proto_init() } -func file_pkg_apis_istio_v1alpha1_values_types_proto_init() { - if File_pkg_apis_istio_v1alpha1_values_types_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ArchConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CNIConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CNITaintConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CNIRepairConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ResourceQuotas); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CPUTargetUtilizationConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Resources); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ServiceAccount); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DefaultPodDisruptionBudgetConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DefaultResourcesConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EgressGatewayConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GatewaysConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GlobalConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*STSConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*IstiodConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GlobalLoggingConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*IngressGatewayConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*IngressGatewayZvpnConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MultiClusterConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*OutboundTrafficPolicyConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PilotConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PilotIngressConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PilotPolicyConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TelemetryConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TelemetryV2Config); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TelemetryV2MetadataExchangeConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TelemetryV2PrometheusConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TelemetryV2StackDriverConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TelemetryV2AccessLogPolicyFilterConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PilotConfigSource); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PortsConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProxyConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProxyInitConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ResourcesRequestsConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SDSConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SecretVolume); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ServiceConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SidecarInjectorConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TracerConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TracerDatadogConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TracerLightStepConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TracerZipkinConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TracerStackdriverConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BaseConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*IstiodRemoteConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Values); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ZeroVPNConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*IntOrString); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TelemetryV2PrometheusConfig_ConfigOverride); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_pkg_apis_istio_v1alpha1_values_types_proto_rawDesc, - NumEnums: 4, - NumMessages: 53, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_pkg_apis_istio_v1alpha1_values_types_proto_goTypes, - DependencyIndexes: file_pkg_apis_istio_v1alpha1_values_types_proto_depIdxs, - EnumInfos: file_pkg_apis_istio_v1alpha1_values_types_proto_enumTypes, - MessageInfos: file_pkg_apis_istio_v1alpha1_values_types_proto_msgTypes, - }.Build() - File_pkg_apis_istio_v1alpha1_values_types_proto = out.File - file_pkg_apis_istio_v1alpha1_values_types_proto_rawDesc = nil - file_pkg_apis_istio_v1alpha1_values_types_proto_goTypes = nil - file_pkg_apis_istio_v1alpha1_values_types_proto_depIdxs = nil -} diff --git a/operator/pkg/apis/istio/v1alpha1/values_types.proto b/operator/pkg/apis/istio/v1alpha1/values_types.proto deleted file mode 100644 index 6961f4c00..000000000 --- a/operator/pkg/apis/istio/v1alpha1/values_types.proto +++ /dev/null @@ -1,1181 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - - -syntax = 'proto3'; - -import "google/protobuf/any.proto"; -import "google/protobuf/struct.proto"; -import "google/protobuf/wrappers.proto"; -import "google/protobuf/duration.proto"; - -package v1alpha1; - -// Package-wide variables from generator "generated". -option go_package = "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1"; - -// ArchConfig specifies the pod scheduling target architecture(amd64, ppc64le, s390x, arm64) -// for all the Istio control plane components. -message ArchConfig { - // Sets pod scheduling weight for amd64 arch - uint32 amd64 = 1; - - // Sets pod scheduling weight for ppc64le arch. - uint32 ppc64le = 2; - - // Sets pod scheduling weight for s390x arch. - uint32 s390x = 3; - - // Sets pod scheduling weight for arm64 arch. - uint32 arm64 = 4; -} - -// Configuration for CNI. -message CNIConfig { - // Controls whether CNI is enabled. - google.protobuf.BoolValue enabled = 1; - - string hub = 2; - - google.protobuf.Value tag = 3; - - string image = 4; - - string pullPolicy = 5; - - string cniBinDir = 6; - - string cniConfDir = 7; - - string cniConfFileName = 8; - - repeated string excludeNamespaces = 9; - - google.protobuf.Struct podAnnotations = 10 [deprecated=true]; - - string psp_cluster_role = 11; - - string logLevel = 12; - - CNIRepairConfig repair = 13; - - google.protobuf.BoolValue chained = 14; - - CNITaintConfig taint = 15; - - ResourceQuotas resource_quotas = 16; - - Resources resources = 17; - - google.protobuf.BoolValue privileged = 18; -} - - -message CNITaintConfig { - // Controls whether taint behavior is enabled. - google.protobuf.BoolValue enabled = 1; -} - -message CNIRepairConfig { - // Controls whether repair behavior is enabled. - google.protobuf.BoolValue enabled = 1; - - string hub = 2; - - google.protobuf.Value tag = 3; - - string image = 4; - - // Controls whether various repair behaviors are enabled. - bool labelPods = 5; - - string createEvents = 6 [deprecated=true]; - - bool deletePods = 7; - - string brokenPodLabelKey = 8; - - string brokenPodLabelValue = 9; - - string initContainerName = 10; -} - -message ResourceQuotas { - // Controls whether to create resource quotas or not for the CNI DaemonSet. - google.protobuf.BoolValue enabled = 1; - - int64 pods = 2; -} - -// Configuration for CPU target utilization for HorizontalPodAutoscaler target. -message CPUTargetUtilizationConfig { - // K8s utilization setting for HorizontalPodAutoscaler target. - // - // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ - int32 targetAverageUtilization = 1; -} - -// Mirrors Resources for unmarshaling. -message Resources { - map limits = 1; - map requests = 2; -} - -// Mirrors ServiceAccount for unmarshaling. -message ServiceAccount { - google.protobuf.Struct annotations = 1; -} - -// DefaultPodDisruptionBudgetConfig specifies the default pod disruption budget configuration. -// -// See https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ -message DefaultPodDisruptionBudgetConfig { - // Controls whether a PodDisruptionBudget with a default minAvailable value of 1 is created for each deployment. - google.protobuf.BoolValue enabled = 1; -} - -// DefaultResourcesConfig specifies the default k8s resources settings for all Istio control plane components. -message DefaultResourcesConfig { - // k8s resources settings. - // - // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container - ResourcesRequestsConfig requests = 1; -} - -// Configuration for an egress gateway. -message EgressGatewayConfig { - // Controls whether auto scaling with a HorizontalPodAutoscaler is enabled. - google.protobuf.BoolValue autoscaleEnabled = 1; - - // maxReplicas setting for HorizontalPodAutoscaler. - uint32 autoscaleMax = 2; - - // minReplicas setting for HorizontalPodAutoscaler. - uint32 autoscaleMin = 3; - - // K8s utilization setting for HorizontalPodAutoscaler target. - // - // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ - CPUTargetUtilizationConfig cpu = 5 [deprecated=true]; - - // Controls whether an egress gateway is enabled. - google.protobuf.BoolValue enabled = 7; - - // Environment variables passed to the proxy container. - google.protobuf.Struct env = 8; - - map labels = 9; - - string name = 25; - - // K8s node selector. - // - // See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector - google.protobuf.Struct nodeSelector = 10 [deprecated=true]; - - // K8s annotations for pods. - // - // See: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ - google.protobuf.Struct podAnnotations = 11 [deprecated=true]; - - // Pod anti-affinity label selector. - // - // Specify the pod anti-affinity that allows you to constrain which nodes - // your pod is eligible to be scheduled based on labels on pods that are - // already running on the node rather than based on labels on nodes. - // There are currently two types of anti-affinity: - // "requiredDuringSchedulingIgnoredDuringExecution" - // "preferredDuringSchedulingIgnoredDuringExecution" - // which denote “hard†vs. “soft†requirements, you can define your values - // in "podAntiAffinityLabelSelector" and "podAntiAffinityTermLabelSelector" - // correspondingly. - // See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity - // - // Examples: - // podAntiAffinityLabelSelector: - // - key: security - // operator: In - // values: S1,S2 - // topologyKey: "kubernetes.io/hostname" - // This pod anti-affinity rule says that the pod requires not to be scheduled - // onto a node if that node is already running a pod with label having key - // “security†and value “S1â€. - repeated google.protobuf.Struct podAntiAffinityLabelSelector = 12 [deprecated=true]; - - // See PodAntiAffinityLabelSelector. - repeated google.protobuf.Struct podAntiAffinityTermLabelSelector = 13 [deprecated=true]; - - // Ports Configuration for the egress gateway service. - repeated PortsConfig ports = 14; - - // K8s resources settings. - // - // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container - Resources resources = 15 [deprecated=true]; - - // Config for secret volume mounts. - repeated SecretVolume secretVolumes = 16; - - // Annotations to add to the egress gateway service. - google.protobuf.Struct serviceAnnotations = 17; - - // Service type. - // - // See https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types - string type = 18; - - // Enables cross-cluster access using SNI matching. - ZeroVPNConfig zvpn = 19; - - repeated google.protobuf.Struct tolerations = 20 [deprecated=true]; - - // K8s rolling update strategy - IntOrString rollingMaxSurge = 21 [deprecated=true]; - - // K8s rolling update strategy - IntOrString rollingMaxUnavailable = 22 [deprecated=true]; - - repeated google.protobuf.Struct configVolumes = 23; - - repeated google.protobuf.Struct additionalContainers = 24; - - google.protobuf.BoolValue runAsRoot = 26; - - // The injection template to use for the gateway. If not set, no injection will be performed. - string injectionTemplate = 27; - - ServiceAccount serviceAccount = 28; - - // Next available 29. -} - -// Configuration for gateways. -message GatewaysConfig { - // Configuration for an egress gateway. - EgressGatewayConfig istio_egressgateway = 1 [json_name="istio-egressgateway"]; - - // Controls whether any gateways are enabled. - google.protobuf.BoolValue enabled = 2; - - // Configuration for an ingress gateway. - IngressGatewayConfig istio_ingressgateway = 4 [json_name="istio-ingressgateway"]; -} - -// Global Configuration for Istio components. -message GlobalConfig { - // Specifies pod scheduling arch(amd64, ppc64le, s390x, arm64) and weight as follows: - // 0 - Never scheduled - // 1 - Least preferred - // 2 - No preference - // 3 - Most preferred - // - // Deprecated: replaced by the affinity k8s settings which allows architecture nodeAffinity configuration of this behavior. - ArchConfig arch = 1 [deprecated=true]; - - string configRootNamespace = 50; - - // Controls whether the server-side validation is enabled. - google.protobuf.BoolValue configValidation = 3; - - repeated string defaultConfigVisibilitySettings = 52; - // Default k8s node selector for all the Istio control plane components - // - // See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector - google.protobuf.Struct defaultNodeSelector = 6 [deprecated=true]; - - // Specifies the default pod disruption budget configuration. - DefaultPodDisruptionBudgetConfig defaultPodDisruptionBudget = 7 [deprecated=true]; - - // Default k8s resources settings for all Istio control plane components. - // - // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container - DefaultResourcesConfig defaultResources = 9 [deprecated=true]; - - repeated google.protobuf.Struct defaultTolerations = 55 [deprecated=true]; - - // Specifies the docker hub for Istio images. - string hub = 12; - - // Specifies the image pull policy for the Istio images. one of Always, Never, IfNotPresent. - // Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. - // - // More info: https://kubernetes.io/docs/concepts/containers/images#updating-images - string imagePullPolicy = 13; - // ImagePullPolicy v1.PullPolicy `json:"imagePullPolicy,omitempty"` - - repeated string imagePullSecrets = 37; - - // Specifies the default namespace for the Istio control plane components. - string istioNamespace = 14; - - google.protobuf.BoolValue logAsJson = 36; - - // Specifies the global logging level settings for the Istio control plane components. - GlobalLoggingConfig logging = 17; - - string meshID = 53; - - // Configure the mesh networks to be used by the Split Horizon EDS. - // - // The following example defines two networks with different endpoints association methods. - // For `network1` all endpoints that their IP belongs to the provided CIDR range will be - // mapped to network1. The gateway for this network example is specified by its public IP - // address and port. - // The second network, `network2`, in this example is defined differently with all endpoints - // retrieved through the specified Multi-Cluster registry being mapped to network2. The - // gateway is also defined differently with the name of the gateway service on the remote - // cluster. The public IP for the gateway will be determined from that remote service (only - // LoadBalancer gateway service type is currently supported, for a NodePort type gateway service, - // it still need to be configured manually). - // - // meshNetworks: - // network1: - // endpoints: - // - fromCidr: "192.168.0.1/24" - // gateways: - // - address: 1.1.1.1 - // port: 80 - // network2: - // endpoints: - // - fromRegistry: reg1 - // gateways: - // - registryServiceName: istio-ingressgateway.dubbo-system.svc.cluster.local - // port: 443 - // - google.protobuf.Struct meshNetworks = 19; - - // Specifies the Configuration for Istio mesh across multiple clusters through Istio gateways. - MultiClusterConfig multiCluster = 22; - - string network = 39; - - // Custom DNS config for the pod to resolve names of services in other - // clusters. Use this to add additional search domains, and other settings. - // see https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#dns-config - // This does not apply to gateway pods as they typically need a different - // set of DNS settings than the normal application pods (e.g. in multicluster scenarios). - repeated string podDNSSearchNamespaces = 43; - - google.protobuf.BoolValue omitSidecarInjectorConfigMap = 38; - - // Controls whether to restrict the applications namespace the controller manages; - // If set it to false, the controller watches all namespaces. - google.protobuf.BoolValue oneNamespace = 23; - - google.protobuf.BoolValue operatorManageWebhooks = 41; - - // Specifies the k8s priorityClassName for the istio control plane components. - // - // See https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass - string priorityClassName = 27 [deprecated=true]; - - // Specifies how proxies are configured within Istio. - ProxyConfig proxy = 28; - - // Specifies the Configuration for proxy_init container which sets the pods' networking to intercept the inbound/outbound traffic. - ProxyInitConfig proxy_init = 29 [json_name="proxy_init"]; - - // Specifies the Configuration for the SecretDiscoveryService instead of using K8S secrets to mount the certificates. - SDSConfig sds = 30; - - // Specifies the tag for the Istio docker images. - google.protobuf.Value tag = 31; - - // Specifies the Configuration for each of the supported tracers. - TracerConfig tracer = 33; - - // Controls whether to use of Mesh Configuration Protocol to distribute configuration. - google.protobuf.BoolValue useMCP = 35; - - // Specifies the Istio control plane’s pilot Pod IP address or remote cluster DNS resolvable hostname. - string remotePilotAddress = 48; - - // Specifies the configution of istiod - IstiodConfig istiod = 54; - - // Configure the Pilot certificate provider. - // Currently, four providers are supported: "kubernetes", "istiod", "custom" and "none". - string pilotCertProvider = 56; - - // Configure the policy for validating JWT. - // Currently, two options are supported: "third-party-jwt" and "first-party-jwt". - string jwtPolicy = 57; - - // Specifies the configuration for Security Token Service. - STSConfig sts = 58; - - // Configures the revision this control plane is a part of - string revision = 59; - - // Controls whether the in-cluster MTLS key and certs are loaded from the secret volume mounts. - google.protobuf.BoolValue mountMtlsCerts = 60; - - // The address of the CA for CSR. - string caAddress = 61; - - // Controls whether one external istiod is enabled. - google.protobuf.BoolValue externalIstiod = 62; - - // Controls whether a remote cluster is the config cluster for an external istiod - google.protobuf.BoolValue configCluster = 64; - - // The name of the CA for workloads. - // For example, when caName=GkeWorkloadCertificate, GKE workload certificates - // will be used as the certificates for workloads. - // The default value is "" and when caName="", the CA will be configured by other - // mechanisms (e.g., environmental variable CA_PROVIDER). - string caName = 65; - - google.protobuf.BoolValue autoscalingv2API = 66; - // The next available key is 67 -} - -// Configuration for Security Token Service (STS) server. -// -// See https://tools.ietf.org/html/draft-ietf-oauth-token-exchange-16 -message STSConfig { - uint32 servicePort = 1; -} - -message IstiodConfig { - // If enabled, istiod will perform config analysis - google.protobuf.BoolValue enableAnalysis = 2; -} - -// GlobalLoggingConfig specifies the global logging level settings for the Istio control plane components. -message GlobalLoggingConfig { - // Comma-separated minimum per-scope logging level of messages to output, in the form of :,: - // The control plane has different scopes depending on component, but can configure default log level across all components - // If empty, default scope and level will be used as configured in code - string level = 1; -} - -// Configuration for an ingress gateway. -message IngressGatewayConfig { - // Controls whether auto scaling with a HorizontalPodAutoscaler is enabled. - google.protobuf.BoolValue autoscaleEnabled = 1; - - // maxReplicas setting for HorizontalPodAutoscaler. - uint32 autoscaleMax = 2; - - // minReplicas setting for HorizontalPodAutoscaler. - uint32 autoscaleMin = 3; - - // K8s utilization setting for HorizontalPodAutoscaler target. - // - // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ - CPUTargetUtilizationConfig cpu = 5 [deprecated=true]; - - google.protobuf.BoolValue customService = 6; - - // Controls whether an ingress gateway is enabled. - google.protobuf.BoolValue enabled = 10; - - // Environment variables passed to the proxy container. - google.protobuf.Struct env = 11; - - map labels = 15; - - string loadBalancerIP = 16; - - repeated string loadBalancerSourceRanges = 17; - - string name = 44; - - // K8s node selector. - // - // See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector - google.protobuf.Struct nodeSelector = 19 [deprecated=true]; - - // K8s annotations for pods. - // - // See: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ - google.protobuf.Struct podAnnotations = 20 [deprecated=true]; - - // See EgressGatewayConfig. - repeated google.protobuf.Struct podAntiAffinityLabelSelector = 21 [deprecated=true]; - - // See EgressGatewayConfig. - repeated google.protobuf.Struct podAntiAffinityTermLabelSelector = 22 [deprecated=true]; - - // Port Configuration for the ingress gateway. - repeated PortsConfig ports = 23; - - // Number of replicas for the ingress gateway Deployment. - uint32 replicaCount = 24 [deprecated=true]; - - // K8s resources settings. - // - // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container - google.protobuf.Struct resources = 25 [deprecated=true]; - - // Config for secret volume mounts. - repeated SecretVolume secretVolumes = 27; - - // Annotations to add to the egress gateway service. - google.protobuf.Struct serviceAnnotations = 28; - - // Service type. - // - // See https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types - string type = 29; - - // Enables cross-cluster access using SNI matching. - IngressGatewayZvpnConfig zvpn = 30; - - // K8s rolling update strategy - IntOrString rollingMaxSurge = 31 [deprecated=true]; - - // K8s rolling update strategy - IntOrString rollingMaxUnavailable = 32 [deprecated=true]; - - string externalTrafficPolicy = 34; - - repeated google.protobuf.Struct tolerations = 35 [deprecated=true]; - - repeated google.protobuf.Struct ingressPorts = 36; - - repeated google.protobuf.Struct additionalContainers = 37; - - repeated google.protobuf.Struct configVolumes = 38; - - google.protobuf.BoolValue runAsRoot = 45; - - // The injection template to use for the gateway. If not set, no injection will be performed. - string injectionTemplate = 46; - - ServiceAccount serviceAccount = 47; - - // Next available 48. -} - -// IngressGatewayZvpnConfig enables cross-cluster access using SNI matching. -message IngressGatewayZvpnConfig { - // Controls whether ZeroVPN is enabled. - google.protobuf.BoolValue enabled = 1; - - string suffix = 2; -} - -// MultiClusterConfig specifies the Configuration for Istio mesh across multiple clusters through the istio gateways. -message MultiClusterConfig { - // Enables the connection between two kubernetes clusters via their respective ingressgateway services. - // Use if the pods in each cluster cannot directly talk to one another. - google.protobuf.BoolValue enabled = 1; - - string clusterName = 2; - string globalDomainSuffix = 3; - google.protobuf.BoolValue includeEnvoyFilter = 4; -} - -// OutboundTrafficPolicyConfig controls the default behavior of the sidecar for handling outbound traffic from the application. -message OutboundTrafficPolicyConfig { - // Specifies the sidecar's default behavior when handling outbound traffic from the application. - enum Mode { - // Outbound traffic to unknown destinations will be allowed, in case there are no services or ServiceEntries for the destination port - ALLOW_ANY = 0; - // Restrict outbound traffic to services defined in the service registry as well as those defined through ServiceEntries - REGISTRY_ONLY = 1; - } - Mode mode = 2; -} - -// Configuration for Pilot. -message PilotConfig { - // Controls whether Pilot is enabled. - google.protobuf.BoolValue enabled = 1; - - // Controls whether a HorizontalPodAutoscaler is installed for Pilot. - google.protobuf.BoolValue autoscaleEnabled = 2; - - // Minimum number of replicas in the HorizontalPodAutoscaler for Pilot. - uint32 autoscaleMin = 3; - - // Maximum number of replicas in the HorizontalPodAutoscaler for Pilot. - uint32 autoscaleMax = 4; - - // Number of replicas in the Pilot Deployment. - uint32 replicaCount = 5 [deprecated=true]; - - // Image name used for Pilot. - // - // This can be set either to image name if hub is also set, or can be set to the full hub:name string. - // - // Examples: custom-pilot, docker.io/someuser:custom-pilot - string image = 6; - - // Trace sampling fraction. - // - // Used to set the fraction of time that traces are sampled. Higher values are more accurate but add CPU overhead. - // - // Allowed values: 0.0 to 1.0 - double traceSampling = 8; - - // K8s resources settings. - // - // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container - Resources resources = 9 [deprecated=true]; - - // Namespace that the configuration management feature is installed into, if different from Pilot namespace. - string configNamespace = 10; - - // Target CPU utilization used in HorizontalPodAutoscaler. - // - // See https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ - CPUTargetUtilizationConfig cpu = 11 [deprecated=true]; - - // K8s node selector. - // - // See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector - google.protobuf.Struct nodeSelector = 12 [deprecated=true]; - - // Maximum duration that a sidecar can be connected to a pilot. - // - // This setting balances out load across pilot instances, but adds some resource overhead. - // - // Examples: 300s, 30m, 1h - google.protobuf.Duration keepaliveMaxServerConnectionAge = 13; - - // Labels that are added to Pilot deployment and pods. - // - // See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ - google.protobuf.Struct deploymentLabels = 14; - - google.protobuf.Struct podLabels = 36; - - // Configuration settings passed to Pilot as a ConfigMap. - // - // This controls whether the mesh config map, generated from values.yaml is generated. - // If false, pilot wil use default values or user-supplied values, in that order of preference. - google.protobuf.BoolValue configMap = 18; - - // Controls whether Pilot is configured through the Mesh Control Protocol (MCP). - // - // If set to true, Pilot requires an MCP server (like Galley) to be installed. - google.protobuf.BoolValue useMCP = 20; - - // Environment variables passed to the Pilot container. - // - // Examples: - // env: - // ENV_VAR_1: value1 - // ENV_VAR_2: value2 - google.protobuf.Struct env = 21; - - // K8s rolling update strategy - IntOrString rollingMaxSurge = 24 [deprecated=true]; - - // K8s rolling update strategy - IntOrString rollingMaxUnavailable = 25 [deprecated=true]; - - // - repeated google.protobuf.Struct tolerations = 26 [deprecated=true]; - - // if protocol sniffing is enabled for outbound - google.protobuf.BoolValue enableProtocolSniffingForOutbound = 28; - // if protocol sniffing is enabled for inbound - google.protobuf.BoolValue enableProtocolSniffingForInbound = 29; - - // K8s annotations for pods. - // - // See: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ - google.protobuf.Struct podAnnotations = 30 [deprecated=true]; - - google.protobuf.Struct serviceAnnotations = 37; - - // ConfigSource describes a source of configuration data for networking - // rules, and other Istio configuration artifacts. Multiple data sources - // can be configured for a single control plane. - PilotConfigSource configSource = 31; - - string jwksResolverExtraRootCA = 32; - - repeated string plugins = 33; - - string hub = 34; - - google.protobuf.Value tag = 35; -} - -// Controls legacy k8s ingress. Only one pilot profile should enable ingress support. -message PilotIngressConfig { - // Sets the type ingress service for Pilot. - // - // If empty, node-port is assumed. - // - // Allowed values: node-port, istio-ingressgateway, ingress - string ingressService = 1; - - ingressControllerMode ingressControllerMode = 2; - // If mode is STRICT, this value must be set on "kubernetes.io/ingress.class" annotation to activate. - string ingressClass = 3; -} - -// Mode for the ingress controller. -enum ingressControllerMode { - // Unspecified Istio ingress controller. - UNSPECIFIED = 0; - // Selects all Ingress resources, with or without Istio annotation. - DEFAULT = 1; - // Selects only resources with istio annotation. - STRICT = 2; - // No ingress or sync. - OFF = 3; -} - -// Controls whether Istio policy is applied to Pilot. -message PilotPolicyConfig { - // Controls whether Istio policy is applied to Pilot. - google.protobuf.BoolValue enabled = 1; -} - -// Controls telemetry configuration -message TelemetryConfig { - // Controls whether telemetry is exported for Pilot. - google.protobuf.BoolValue enabled = 1; - - // Use telemetry v2. - TelemetryV2Config v2 = 3; -} - -// Controls whether pilot will configure telemetry v2. -message TelemetryV2Config { - // Controls whether pilot will configure telemetry v2. - google.protobuf.BoolValue enabled = 1; - - TelemetryV2MetadataExchangeConfig metadata_exchange = 4; - - TelemetryV2PrometheusConfig prometheus = 2; - - TelemetryV2StackDriverConfig stackdriver = 3; - - TelemetryV2AccessLogPolicyFilterConfig access_log_policy = 5; -} - -message TelemetryV2MetadataExchangeConfig { - // Controls whether enabled WebAssembly runtime for metadata exchange filter. - google.protobuf.BoolValue wasmEnabled = 2; -} - -// Conrols telemetry v2 prometheus settings. -message TelemetryV2PrometheusConfig { - // Controls whether stats envoyfilter would be enabled or not. - google.protobuf.BoolValue enabled = 1; - - // Controls whether enabled WebAssembly runtime for stats filter. - google.protobuf.BoolValue wasmEnabled = 2; - - message ConfigOverride { - // Overrides default gateway telemetry v2 configuration. - google.protobuf.Struct gateway = 1; - - // Overrides default inbound sidecar telemetry v2 configuration. - google.protobuf.Struct inboundSidecar = 2; - - // Overrides default outbound sidecar telemetry v2 configuration. - google.protobuf.Struct outboundSidecar = 3; - } - - // Overrides default telemetry v2 filter configuration. - ConfigOverride config_override = 3; -} - -// Conrols telemetry v2 stackdriver settings. -message TelemetryV2StackDriverConfig { -// Types of Access logs to export. - 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; - }; - - google.protobuf.BoolValue enabled = 1; - - google.protobuf.BoolValue logging = 2 [deprecated=true]; - - google.protobuf.BoolValue monitoring = 3; - - google.protobuf.BoolValue topology = 4 [deprecated=true]; - - google.protobuf.BoolValue disableOutbound = 6; - - google.protobuf.Struct configOverride = 5; - - AccessLogging outboundAccessLogging = 7; - - AccessLogging inboundAccessLogging = 8; -} - -// Conrols telemetry v2 access log policy filter settings. -message TelemetryV2AccessLogPolicyFilterConfig { - google.protobuf.BoolValue enabled = 1; - - google.protobuf.Duration logWindowDuration = 2; -} - -// PilotConfigSource describes information about a configuration store inside a -// mesh. A single control plane instance can interact with one or more data -// sources. -message PilotConfigSource { - // Describes the source of configuration, if nothing is specified default is MCP. - repeated string subscribedResources = 1; -} - -// Configuration for a port. -message PortsConfig { - // Port name. - string name = 1; - - // Port number. - int32 port = 2; - - // NodePort number. - int32 nodePort = 3; - - // Target port number. - int32 targetPort = 4; - - // Protocol name. - string protocol = 5; -} - -// Configuration for Proxy. -message ProxyConfig { - string autoInject = 4; - - // Domain for the cluster, default: "cluster.local". - // - // K8s allows this to be customized, see https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/ - string clusterDomain = 5; - - // Per Component log level for proxy, applies to gateways and sidecars. - // - // If a component level is not set, then the global "logLevel" will be used. If left empty, "misc:error" is used. - string componentLogLevel = 6; - - // Enables core dumps for newly injected sidecars. - // - // If set, newly injected sidecars will have core dumps enabled. - google.protobuf.BoolValue enableCoreDump = 9; - - // Specifies the Istio ingress ports not to capture. - string excludeInboundPorts = 12; - - // Lists the excluded IP ranges of Istio egress traffic that the sidecar captures. - string excludeIPRanges = 13; - - // Image name or path for the proxy, default: "proxyv2". - // - // If registry or tag are not specified, global.hub and global.tag are used. - // - // Examples: my-proxy (uses global.hub/tag), docker.io/myrepo/my-proxy:v1.0.0 - string image = 14; - - // Lists the IP ranges of Istio egress traffic that the sidecar captures. - // - // Example: "172.30.0.0/16,172.20.0.0/16" - // This would only capture egress traffic on those two IP Ranges, all other outbound traffic would # be allowed by the sidecar." - string includeIPRanges = 16; - - // Log level for proxy, applies to gateways and sidecars. If left empty, "warning" is used. Expected values are: trace\|debug\|info\|warning\|error\|critical\|off - string logLevel = 18; - - // Enables privileged securityContext for the istio-proxy container. - // - // See https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ - google.protobuf.BoolValue privileged = 19; - - // Sets the initial delay for readiness probes in seconds. - uint32 readinessInitialDelaySeconds = 20; - - // Sets the interval between readiness probes in seconds. - uint32 readinessPeriodSeconds = 21; - - // Sets the number of successive failed probes before indicating readiness failure. - uint32 readinessFailureThreshold = 22; - - // Default port used for the Pilot agent's health checks. - uint32 statusPort = 23; - - // K8s resources settings. - // - // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container - Resources resources = 24 [deprecated=true]; - - tracer tracer = 25; - - string excludeOutboundPorts = 28; - - google.protobuf.Struct lifecycle = 36; - - // Controls if sidecar is injected at the front of the container list and blocks the start of the other containers until the proxy is ready - // - // Deprecated: replaced by ProxyConfig setting which allows per-pod configuration of this behavior. - google.protobuf.BoolValue holdApplicationUntilProxyStarts = 37 [deprecated=true]; - - string includeInboundPorts = 38; - - string includeOutboundPorts = 39; -} - -// Specifies which tracer to use. -enum tracer { - zipkin = 0; - lightstep = 1; - datadog = 2; - stackdriver = 3; - openCensusAgent = 4; - none = 5; -} - -// Configuration for proxy_init container which sets the pods' networking to intercept the inbound/outbound traffic. -message ProxyInitConfig { - // Specifies the image for the proxy_init container. - string image = 1; - // K8s resources settings. - // - // See https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container - Resources resources = 5 [deprecated=true]; -} - -// Configuration for K8s resource requests. -message ResourcesRequestsConfig { - string cpu = 1; - - string memory = 2; -} - -// Configuration for the SecretDiscoveryService instead of using K8S secrets to mount the certificates. -message SDSConfig { - google.protobuf.Struct token = 5 [deprecated=true]; -} - -// Configuration for secret volume mounts. -// -// See https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets. -message SecretVolume { - string mountPath = 1; - - string name = 2; - - string secretName = 3; -} - -// ServiceConfig is described in istio.io documentation. -message ServiceConfig { - google.protobuf.Struct annotations = 1; - - uint32 externalPort = 2; - - string name = 3; - - string type = 18; -} - -// SidecarInjectorConfig is described in istio.io documentation. -message SidecarInjectorConfig { - // Enables sidecar auto-injection in namespaces by default. - google.protobuf.BoolValue enableNamespacesByDefault = 2; - - // Instructs Istio to not inject the sidecar on those pods, based on labels that are present in those pods. - // - // Annotations in the pods have higher precedence than the label selectors. - // Order of evaluation: Pod Annotations → NeverInjectSelector → AlwaysInjectSelector → Default Policy. - // See https://istio.io/docs/setup/kubernetes/additional-setup/sidecar-injection/#more-control-adding-exceptions - repeated google.protobuf.Struct neverInjectSelector = 11; - - // See NeverInjectSelector. - repeated google.protobuf.Struct alwaysInjectSelector = 12; - - // If true, webhook or istioctl injector will rewrite PodSpec for liveness health check to redirect request to sidecar. This makes liveness check work even when mTLS is enabled. - google.protobuf.BoolValue rewriteAppHTTPProbe = 16; - - // injectedAnnotations are additional annotations that will be added to the pod spec after injection - // This is primarily to support PSP annotations. - google.protobuf.Struct injectedAnnotations = 19; - - // Enable objectSelector to filter out pods with no need for sidecar before calling istio-sidecar-injector. - google.protobuf.Struct objectSelector = 21; - - // Configure the injection url for sidecar injector webhook - string injectionURL = 22; - - // Templates defines a set of custom injection templates that can be used. For example, defining: - // - // templates: - // hello: | - // metadata: - // labels: - // hello: world - // - // Then starting a pod with the `inject.istio.io/templates: hello` annotation, will result in the pod - // being injected with the hello=world labels. - // This is intended for advanced configuration only; most users should use the built in template - google.protobuf.Struct templates = 23; - - // Default templates specifies a set of default templates that are used in sidecar injection. - // By default, a template `sidecar` is always provided, which contains the template of default sidecar. - // To inject other additional templates, define it using the `templates` option, and add it to - // the default templates list. - // For example: - - // templates: - // hello: | - // metadata: - // labels: - // hello: world - - // defaultTemplates: ["sidecar", "hello"] - repeated string defaultTemplates = 24; - - // If enabled, the legacy webhook selection logic will be used. This relies on filtering of webhook - // requests in Istiod, rather than at the webhook selection level. - // This is option is intended for migration purposes only and will be removed in Istio 1.10. - google.protobuf.BoolValue useLegacySelectors = 4 [deprecated=true]; -} - -// Configuration for each of the supported tracers. -message TracerConfig { - // Configuration for the datadog tracing service. - TracerDatadogConfig datadog = 1; - - // Configuration for the lightstep tracing service. - TracerLightStepConfig lightstep = 2; - - // Configuration for the zipkin tracing service. - TracerZipkinConfig zipkin = 3; - - // Configuration for the stackdriver tracing service. - TracerStackdriverConfig stackdriver = 4; -} - -// Configuration for the datadog tracing service. -message TracerDatadogConfig { - // Address in host:port format for reporting trace data to the Datadog agent. - string address = 1; -} - -// Configuration for the lightstep tracing service. -message TracerLightStepConfig { - // Sets the lightstep satellite pool address in host:port format for reporting trace data. - string address = 1; - - // Sets the lightstep access token. - string accessToken = 2; -} - -// Configuration for the zipkin tracing service. -message TracerZipkinConfig { - // Address of zipkin instance in host:port format for reporting trace data. - // - // Example: .:941 - string address = 1; -} - -// Configuration for the stackdriver tracing service. -message TracerStackdriverConfig { - // enables trace output to stdout. - google.protobuf.BoolValue debug = 1; - - // The global default max number of attributes per span. - uint32 maxNumberOfAttributes = 2; - - // The global default max number of annotation events per span. - uint32 maxNumberOfAnnotations = 3; - - // The global default max number of message events per span. - uint32 maxNumberOfMessageEvents = 4; -} - -message BaseConfig { - // For Helm2 use, adds the CRDs to templates. - google.protobuf.BoolValue enableCRDTemplates = 1; - - // URL to use for validating webhook. - string validationURL = 2; - - // For istioctl usage to disable istio config crds in base - google.protobuf.BoolValue enableIstioConfigCRDs = 3; - -} - -message IstiodRemoteConfig { - // URL to use for sidecar injector webhook. - string injectionURL = 1; - // Path to use for the sidecar injector webhook service. - string injectionPath = 2; -} - -message Values { - CNIConfig cni = 2; - - GatewaysConfig gateways = 5; - - GlobalConfig global = 6; - - PilotConfig pilot = 10; - - // Controls whether telemetry is exported for Pilot. - TelemetryConfig telemetry = 23; - - SidecarInjectorConfig sidecarInjectorWebhook = 13; - - CNIConfig istio_cni = 19; - - string revision = 21; - - string ownerName = 22; - - // TODO can this import the real mesh config API? - google.protobuf.Value meshConfig = 36; - - BaseConfig base = 37; - - IstiodRemoteConfig istiodRemote = 38; - - repeated string revisionTags = 39; - - string defaultRevision = 40; -} - - -// ZeroVPNConfig enables cross-cluster access using SNI matching. -message ZeroVPNConfig { - // Controls whether ZeroVPN is enabled. - google.protobuf.BoolValue enabled = 1; - - string suffix = 2; -} - -// IntOrString is a type that can hold an int32 or a string. When used in -// JSON or YAML marshalling and unmarshalling, it produces or consumes the -// inner type. This allows you to have, for example, a JSON field that can -// accept a name or number. -// TODO: Rename to Int32OrString -// -// +protobuf=true -// +protobuf.options.(gogoproto.goproto_stringer)=false -// +k8s:openapi-gen=true -message IntOrString { - int64 type = 1; - - google.protobuf.Int32Value intVal = 2; - - google.protobuf.StringValue strVal = 3; -} diff --git a/operator/pkg/apis/istio/v1alpha1/zz_generated.deepcopy.go b/operator/pkg/apis/istio/v1alpha1/zz_generated.deepcopy.go deleted file mode 100644 index 477b9aafc..000000000 --- a/operator/pkg/apis/istio/v1alpha1/zz_generated.deepcopy.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build !ignore_autogenerated -// +build !ignore_autogenerated - -// Code generated by deepcopy-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *IstioOperator) DeepCopyInto(out *IstioOperator) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - if in.Spec != nil { - in.Spec.DeepCopyInto(out.Spec) - } - if in.Status != nil { - in.Status.DeepCopyInto(out.Status) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IstioOperator. -func (in *IstioOperator) DeepCopy() *IstioOperator { - if in == nil { - return nil - } - out := new(IstioOperator) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *IstioOperator) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *IstioOperatorList) DeepCopyInto(out *IstioOperatorList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]IstioOperator, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IstioOperatorList. -func (in *IstioOperatorList) DeepCopy() *IstioOperatorList { - if in == nil { - return nil - } - out := new(IstioOperatorList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *IstioOperatorList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} diff --git a/operator/pkg/cache/cache.go b/operator/pkg/cache/cache.go deleted file mode 100644 index afc943498..000000000 --- a/operator/pkg/cache/cache.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cache - -import ( - "sync" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/metrics" - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" -) - -// ObjectCache is a cache of objects, -type ObjectCache struct { - // Cache is a cache keyed by object Hash() function. - Cache map[string]*object.K8sObject - Mu *sync.RWMutex -} - -var ( - // objectCaches holds the latest copy of each object applied by the controller. The caches are divided by component - // name. - objectCaches = make(map[string]*ObjectCache) - objectCachesMu sync.RWMutex -) - -// FlushObjectCaches flushes all object caches. -func FlushObjectCaches() { - objectCachesMu.Lock() - defer objectCachesMu.Unlock() - objectCaches = make(map[string]*ObjectCache) - metrics.CacheFlushTotal.Increment() -} - -// GetCache returns the object Cache for the given name, creating one in the global Cache if needed. -func GetCache(name string) *ObjectCache { - objectCachesMu.Lock() - defer objectCachesMu.Unlock() - - // Create and/or get the Cache corresponding to the CR name we're processing. Per name partitioning is required to - // prune the Cache to remove any objects not in the manifest generated for a given CR. - if objectCaches[name] == nil { - objectCaches[name] = &ObjectCache{ - Cache: make(map[string]*object.K8sObject), - Mu: &sync.RWMutex{}, - } - } - return objectCaches[name] -} - -// RemoveObject removes object with objHash in the Cache with the given name from the object Cache. -func RemoveObject(name, objHash string) { - objectCachesMu.Lock() - objectCache := objectCaches[name] - objectCachesMu.Unlock() - - if objectCache != nil { - objectCache.Mu.Lock() - delete(objectCache.Cache, objHash) - objectCache.Mu.Unlock() - } -} diff --git a/operator/pkg/cache/cache_test.go b/operator/pkg/cache/cache_test.go deleted file mode 100644 index e2a0d3fd0..000000000 --- a/operator/pkg/cache/cache_test.go +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cache - -import ( - "reflect" - "sync" - "testing" -) - -import ( - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" -) - -func TestFlushObjectCaches(t *testing.T) { - tests := []struct { - desc string - wantSize int - }{ - { - desc: "flush-cache", - wantSize: 0, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - unstObjs := make(map[string]interface{}) - tUnstructured := &unstructured.Unstructured{Object: unstObjs} - testCache := make(map[string]*object.K8sObject) - testCache["foo"] = object.NewK8sObject(tUnstructured, nil, nil) - objectCaches["foo"] = &ObjectCache{ - Cache: testCache, - Mu: &sync.RWMutex{}, - } - if len(objectCaches) != 1 { - t.Errorf("%s: Expected len 1, got len 0.", tt.desc) - } - FlushObjectCaches() - if gotLen := len(objectCaches); gotLen != tt.wantSize { - t.Errorf("%s: Expected size %v after flush, got size %v", tt.desc, tt.wantSize, gotLen) - } - }) - } -} - -func TestGetCache(t *testing.T) { - tests := []struct { - desc string - key string - in map[string]*ObjectCache - want ObjectCache - }{ - { - desc: "value-exists", - key: "foo-key", - in: map[string]*ObjectCache{ - "foo-key": { - Cache: make(map[string]*object.K8sObject), - Mu: nil, - }, - }, - want: ObjectCache{ - Cache: make(map[string]*object.K8sObject), - Mu: nil, - }, - }, - { - desc: "key-does-not-exist", - key: "foo-key", - in: make(map[string]*ObjectCache), - want: ObjectCache{ - Cache: make(map[string]*object.K8sObject), - Mu: &sync.RWMutex{}, - }, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - defer FlushObjectCaches() - for key, value := range tt.in { - objectCaches[key] = value - } - if gotCache := GetCache(tt.key); !reflect.DeepEqual(*gotCache, tt.want) { - t.Errorf("%s: expected cache %v, got cache %v\n", tt.desc, tt.want, *gotCache) - } - }) - } -} - -func TestRemoveObject(t *testing.T) { - tests := []struct { - desc string - in map[string]*ObjectCache - // key for map of caces - objCacheRemovalKey string - // key for map of K8sObjects - removalKey string - // cache in position objectCaches[objCacheRemovalKey] - expectedCache ObjectCache - }{ - { - desc: "remove-cache", - in: map[string]*ObjectCache{ - "cache-foo-key": { - Cache: map[string]*object.K8sObject{ - "obj-foo-key": object.NewK8sObject(&unstructured.Unstructured{ - Object: make(map[string]interface{}), - }, nil, nil), - "dont-touch-me-key": object.NewK8sObject(&unstructured.Unstructured{ - Object: make(map[string]interface{}), - }, nil, nil), - }, - Mu: &sync.RWMutex{}, - }, - }, - objCacheRemovalKey: "cache-foo-key", - removalKey: "obj-foo-key", - expectedCache: ObjectCache{ - Cache: map[string]*object.K8sObject{ - "dont-touch-me-key": object.NewK8sObject(&unstructured.Unstructured{ - Object: make(map[string]interface{}), - }, nil, nil), - }, - Mu: &sync.RWMutex{}, - }, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - for key, value := range tt.in { - objectCaches[key] = value - } - defer FlushObjectCaches() - RemoveObject(tt.objCacheRemovalKey, tt.removalKey) - if got := objectCaches[tt.objCacheRemovalKey]; !reflect.DeepEqual(*got, tt.expectedCache) { - t.Errorf("%s: expected object cache %v, got %v\n", tt.desc, tt.expectedCache, got) - } - }) - } -} diff --git a/operator/pkg/compare/compare.go b/operator/pkg/compare/compare.go deleted file mode 100644 index 0954ae6ce..000000000 --- a/operator/pkg/compare/compare.go +++ /dev/null @@ -1,508 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package compare - -import ( - "fmt" - "io" - "path/filepath" - "reflect" - "regexp" - "sort" - "strings" -) - -import ( - "github.com/google/go-cmp/cmp" - "istio.io/pkg/log" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" - "github.com/apache/dubbo-go-pixiu/operator/pkg/tpath" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -// YAMLCmpReporter is a custom reporter to generate tree based diff for YAMLs, used by cmp.Equal(). -type YAMLCmpReporter struct { - path cmp.Path - diffTree map[string]interface{} -} - -// PushStep implements interface to keep track of current path by pushing. -// a step into YAMLCmpReporter.path -func (r *YAMLCmpReporter) PushStep(ps cmp.PathStep) { - r.path = append(r.path, ps) -} - -// PopStep implements interface to keep track of current path by popping a step out. -// of YAMLCmpReporter.path -func (r *YAMLCmpReporter) PopStep() { - r.path = r.path[:len(r.path)-1] -} - -// Report implements interface to add diff path into YAMLCmpReporter.diffTree. -func (r *YAMLCmpReporter) Report(rs cmp.Result) { - if !rs.Equal() { - vx, vy := r.path.Last().Values() - var dm string - isNonEmptyX := isValidAndNonEmpty(vx) - isNonEmptyY := isValidAndNonEmpty(vy) - if isNonEmptyX && !isNonEmptyY { - dm = fmt.Sprintf("%v -> (REMOVED)", vx) - } else if !isNonEmptyX && isNonEmptyY { - dm = fmt.Sprintf(" -> %v (ADDED)", vy) - } else if isNonEmptyX && isNonEmptyY { - dm = fmt.Sprintf("%v -> %v", vx, vy) - } else { - // ignore the case that both x and y are invalid or empty - return - } - if r.diffTree == nil { - r.diffTree = make(map[string]interface{}) - } - if err := tpath.WriteNode(r.diffTree, pathToStringList(r.path), dm); err != nil { - panic(err) - } - } -} - -func isValidAndNonEmpty(v reflect.Value) bool { - if !v.IsValid() { - return false - } - k := v.Kind() - switch k { - case reflect.Interface: - return isValidAndNonEmpty(v.Elem()) - case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String: - return v.Len() > 0 - } - return true -} - -// String returns a text representation of diff tree. -func (r *YAMLCmpReporter) String() string { - if len(r.diffTree) == 0 { - return "" - } - y, err := yaml.Marshal(r.diffTree) - if err != nil { - return err.Error() - } - return string(y) -} - -// YAMLCmp compares two yaml texts, return a tree based diff text. -func YAMLCmp(a, b string) string { - return YAMLCmpWithIgnore(a, b, nil, "") -} - -// YAMLCmpWithIgnore compares two yaml texts, and ignores paths in ignorePaths. -func YAMLCmpWithIgnore(a, b string, ignorePaths []string, ignoreYaml string) string { - ao, bo := make(map[string]interface{}), make(map[string]interface{}) - if err := yaml.Unmarshal([]byte(a), &ao); err != nil { - return err.Error() - } - if err := yaml.Unmarshal([]byte(b), &bo); err != nil { - return err.Error() - } - - if kind := ao["kind"]; kind == "ConfigMap" { - if err := UnmarshalInlineYaml(ao, "data"); err != nil { - log.Warnf("Unable to unmarshal ConfigMap Data, error: %v", err) - } - } - if kind := bo["kind"]; kind == "ConfigMap" { - if err := UnmarshalInlineYaml(bo, "data"); err != nil { - log.Warnf("Unable to unmarshal ConfigMap Data, error: %v", err) - } - } - - ignoreYamlOpt, err := genYamlIgnoreOpt(ignoreYaml) - if err != nil { - return err.Error() - } - - var r YAMLCmpReporter - cmp.Equal(ao, bo, cmp.Reporter(&r), genPathIgnoreOpt(ignorePaths), ignoreYamlOpt) - return r.String() -} - -// UnmarshalInlineYaml tries to unmarshal string values in obj into YAML objects -// at a given targetPath. Side effect: this will mutate obj in place. -func UnmarshalInlineYaml(obj map[string]interface{}, targetPath string) (err error) { - nodeList := strings.Split(targetPath, ".") - if len(nodeList) == 0 { - return fmt.Errorf("targetPath '%v' length is zero after split", targetPath) - } - - cur := obj - for _, nname := range nodeList { - ndata, ok := cur[nname] - if !ok || ndata == nil { // target path does not exist - return fmt.Errorf("targetPath '%v' doest not exist in obj: '%v' is missing", - targetPath, nname) - } - switch nnode := ndata.(type) { - case map[string]interface{}: - cur = nnode - default: // target path type does not match - return fmt.Errorf("targetPath '%v' doest not exist in obj: "+ - "'%v' type is not map[string]interface{}", targetPath, nname) - } - } - - for dk, dv := range cur { - switch vnode := dv.(type) { - case string: - vo := make(map[string]interface{}) - if err := yaml.Unmarshal([]byte(vnode), &vo); err != nil { - continue - } - // Replace the original text yaml tree node with yaml objects - cur[dk] = vo - } - } - return -} - -// genPathIgnoreOpt returns a cmp.Option to ignore paths specified in parameter ignorePaths. -func genYamlIgnoreOpt(yamlStr string) (cmp.Option, error) { - tree := make(map[string]interface{}) - if err := yaml.Unmarshal([]byte(yamlStr), &tree); err != nil { - return nil, err - } - return cmp.FilterPath(func(curPath cmp.Path) bool { - up := pathToStringList(curPath) - treeNode, found, _ := tpath.Find(tree, up) - return found && IsLeafNode(treeNode) - }, cmp.Ignore()), nil -} - -// genPathIgnoreOpt returns a cmp.Option to ignore paths specified in parameter ignorePaths. -func genPathIgnoreOpt(ignorePaths []string) cmp.Option { - return cmp.FilterPath(func(curPath cmp.Path) bool { - cp := strings.Join(pathToStringList(curPath), ".") - for _, ip := range ignorePaths { - if res, err := filepath.Match(ip, cp); err == nil && res { - return true - } - } - return false - }, cmp.Ignore()) -} - -func pathToStringList(path cmp.Path) (up []string) { - for _, step := range path { - switch t := step.(type) { - case cmp.MapIndex: - up = append(up, fmt.Sprintf("%v", t.Key())) - case cmp.SliceIndex: - // Create an element, but never an NPath - s := t.String() - if util.IsNPathElement(s) { - // Convert e.g. [0] to [#0] - s = fmt.Sprintf("%c%c%s", s[0], '#', s[1:]) - } - up = append(up, s) - } - } - return -} - -func ManifestDiff(a, b string, verbose bool) (string, error) { - ao, err := object.ParseK8sObjectsFromYAMLManifest(a) - if err != nil { - return "", err - } - bo, err := object.ParseK8sObjectsFromYAMLManifest(b) - if err != nil { - return "", err - } - - aom, bom := ao.ToMap(), bo.ToMap() - return manifestDiff(aom, bom, nil, verbose) -} - -// ManifestDiffWithSelect checks the manifest differences with selected and ignored resources. -// The selected filter will apply before the ignored filter. -func ManifestDiffWithRenameSelectIgnore(a, b, renameResources, selectResources, ignoreResources string, verbose bool) (string, error) { - rnm := getKeyValueMap(renameResources) - sm := getObjPathMap(selectResources) - im := getObjPathMap(ignoreResources) - - ao, err := object.ParseK8sObjectsFromYAMLManifest(a) - if err != nil { - return "", err - } - aom := ao.ToMap() - - bo, err := object.ParseK8sObjectsFromYAMLManifest(b) - if err != nil { - return "", err - } - bom := bo.ToMap() - - if len(rnm) != 0 { - aom, err = renameResource(aom, rnm) - if err != nil { - return "", err - } - } - - aosm, err := filterResourceWithSelectAndIgnore(aom, sm, im) - if err != nil { - return "", err - } - bosm, err := filterResourceWithSelectAndIgnore(bom, sm, im) - if err != nil { - return "", err - } - - return manifestDiff(aosm, bosm, im, verbose) -} - -// FilterManifest selects and ignores subset from the manifest string -func FilterManifest(ms string, selectResources string, ignoreResources string) (string, error) { - sm := getObjPathMap(selectResources) - im := getObjPathMap(ignoreResources) - ao, err := object.ParseK8sObjectsFromYAMLManifestFailOption(ms, false) - if err != nil { - return "", err - } - aom := ao.ToMap() - slrs, err := filterResourceWithSelectAndIgnore(aom, sm, im) - if err != nil { - return "", err - } - var sb strings.Builder - for _, ko := range slrs { - yl, err := ko.YAML() - if err != nil { - return "", err - } - sb.WriteString(string(yl) + object.YAMLSeparator) - } - k8sObjects, err := object.ParseK8sObjectsFromYAMLManifest(sb.String()) - if err != nil { - return "", err - } - k8sObjects.Sort(object.DefaultObjectOrder()) - sortdManifests, err := k8sObjects.YAMLManifest() - if err != nil { - return "", err - } - return sortdManifests, nil -} - -// renameResource filter the input resources with selected and ignored filter. -func renameResource(iom map[string]*object.K8sObject, rnm map[string]string) (map[string]*object.K8sObject, error) { - oom := make(map[string]*object.K8sObject) - for name, obj := range iom { - isRenamed := false - for fromPat, toPat := range rnm { - fromRe, err := buildResourceRegexp(strings.TrimSpace(fromPat)) - if err != nil { - return nil, fmt.Errorf("error building the regexp from "+ - "rename-from string: %v, error: %v", fromPat, err) - } - if fromRe.MatchString(name) { - fromList := strings.Split(name, ":") - if len(fromList) != 3 { - return nil, fmt.Errorf("failed to split the old name,"+ - " length != 3: %v", name) - } - toList := strings.Split(toPat, ":") - if len(toList) != 3 { - return nil, fmt.Errorf("failed to split the rename-to string,"+ - " length != 3: %v", toPat) - } - - // Use the old name if toList has "*" or "" - // Otherwise, use the name in toList - newList := make([]string, 3) - for i := range toList { - if toList[i] == "" || toList[i] == "*" { - newList[i] = fromList[i] - } else { - newList[i] = toList[i] - } - } - tk := strings.Join(newList, ":") - oom[tk] = obj - isRenamed = true - } - } - if !isRenamed { - oom[name] = obj - } - } - return oom, nil -} - -// filterResourceWithSelectAndIgnore filter the input resources with selected and ignored filter. -func filterResourceWithSelectAndIgnore(aom map[string]*object.K8sObject, sm, im map[string]string) (map[string]*object.K8sObject, error) { - aosm := make(map[string]*object.K8sObject) - for ak, av := range aom { - for selected := range sm { - re, err := buildResourceRegexp(strings.TrimSpace(selected)) - if err != nil { - return nil, fmt.Errorf("error building the resource regexp: %v", err) - } - if re.MatchString(ak) { - aosm[ak] = av - } - } - for ignored := range im { - re, err := buildResourceRegexp(strings.TrimSpace(ignored)) - if err != nil { - return nil, fmt.Errorf("error building the resource regexp: %v", err) - } - if re.MatchString(ak) { - delete(aosm, ak) - } - } - } - return aosm, nil -} - -// buildResourceRegexp translates the resource indicator to regexp. -func buildResourceRegexp(s string) (*regexp.Regexp, error) { - hash := strings.Split(s, ":") - for i, v := range hash { - if v == "" || v == "*" { - hash[i] = ".*" - } - } - return regexp.Compile(strings.Join(hash, ":")) -} - -// manifestDiff an internal function to compare the manifests difference specified in the input. -func manifestDiff(aom, bom map[string]*object.K8sObject, im map[string]string, verbose bool) (string, error) { - var sb strings.Builder - out := make(map[string]string) - for ak, av := range aom { - ay, err := av.YAML() - if err != nil { - return "", err - } - bo := bom[ak] - if bo == nil { - out[ak] = fmt.Sprintf("\n\nObject %s is missing in B:\n\n", ak) - continue - } - by, err := bo.YAML() - if err != nil { - return "", err - } - - var diff string - if verbose { - diff = util.YAMLDiff(string(ay), string(by)) - } else { - ignorePaths := objectIgnorePaths(ak, im) - diff = YAMLCmpWithIgnore(string(ay), string(by), ignorePaths, "") - } - - if diff != "" { - out[ak] = fmt.Sprintf("\n\nObject %s has diffs:\n\n%s", ak, diff) - } - } - for bk := range bom { - ao := aom[bk] - if ao == nil { - out[bk] = fmt.Sprintf("\n\nObject %s is missing in A:\n\n", bk) - continue - } - } - - keys := make([]string, 0, len(out)) - for k := range out { - keys = append(keys, k) - } - sort.Strings(keys) - - for i := range keys { - writeStringSafe(&sb, out[keys[i]]) - } - - return sb.String(), nil -} - -func getObjPathMap(rs string) map[string]string { - rm := make(map[string]string) - if len(rs) == 0 { - return rm - } - for _, r := range strings.Split(rs, ",") { - split := strings.Split(r, ":") - if len(split) < 4 { - rm[r] = "" - continue - } - kind, namespace, name, path := split[0], split[1], split[2], split[3] - obj := fmt.Sprintf("%v:%v:%v", kind, namespace, name) - rm[obj] = path - } - return rm -} - -func getKeyValueMap(rs string) map[string]string { - rm := make(map[string]string) - if len(rs) == 0 { - return rm - } - for _, r := range strings.Split(rs, ",") { - split := strings.Split(r, "->") - if len(split) != 2 { - continue - } - rm[split[0]] = split[1] - } - return rm -} - -func objectIgnorePaths(objectName string, im map[string]string) (ignorePaths []string) { - if im == nil { - im = make(map[string]string) - } - for obj, path := range im { - if path == "" { - continue - } - re, err := buildResourceRegexp(strings.TrimSpace(obj)) - if err != nil { - continue - } - if re.MatchString(objectName) { - ignorePaths = append(ignorePaths, path) - } - } - return ignorePaths -} - -func writeStringSafe(sb io.StringWriter, s string) { - _, err := sb.WriteString(s) - if err != nil { - log.Error(err.Error()) - } -} - -// IsLeafNode reports whether the given node is a leaf, assuming internal nodes can only be maps or slices. -func IsLeafNode(node interface{}) bool { - return !util.IsMap(node) && !util.IsSlice(node) -} diff --git a/operator/pkg/compare/compare_test.go b/operator/pkg/compare/compare_test.go deleted file mode 100644 index 457281f45..000000000 --- a/operator/pkg/compare/compare_test.go +++ /dev/null @@ -1,1497 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package compare - -import ( - "strings" - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" -) - -func TestYAMLCmp(t *testing.T) { - tests := []struct { - desc string - a string - b string - want interface{} - }{ - { - desc: "empty string into nil", - a: `metadata: ""`, - b: `metadata: `, - want: ``, - }, - { - desc: "empty array into nil", - a: `metadata: []`, - b: `metadata: `, - want: ``, - }, - { - desc: "empty map into nil", - a: `metadata: {}`, - b: `metadata: `, - want: ``, - }, - { - desc: "two additional", - a: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - namespace: dubbo-system - labels: - release: istio`, - b: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio`, - want: `metadata: - labels: - app: -> istio-ingressgateway (ADDED) - name: -> istio-ingressgateway (ADDED) -`, - }, - { - desc: "two missing", - a: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio`, - b: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - namespace: dubbo-system - labels: - release: istio`, - want: `metadata: - labels: - app: istio-ingressgateway -> (REMOVED) - name: istio-ingressgateway -> (REMOVED) -`, - }, - { - desc: "one missing", - a: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio`, - b: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - release: istio`, - want: `metadata: - labels: - app: istio-ingressgateway -> (REMOVED) -`, - }, - { - desc: "one additional", - a: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - release: istio`, - b: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio`, - want: `metadata: - labels: - app: -> istio-ingressgateway (ADDED) -`, - }, - { - desc: "identical", - a: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio`, - b: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio`, - want: ``, - }, - { - desc: "first item changed", - a: `apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio`, - b: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio`, - want: `apiVersion: autoscaling/v2beta1 -> autoscaling/v2 -`, - }, - { - desc: "nested item changed", - a: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio`, - b: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-egressgateway - release: istio`, - want: `metadata: - labels: - app: istio-ingressgateway -> istio-egressgateway -`, - }, - { - desc: "one map value changed, order changed", - a: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio -spec: - maxReplicas: 5 - minReplicas: 1 - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: ingressgateway - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: 80`, - b: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - labels: - app: istio-ingressgateway - release: istio - name: istio-ingressgateway - namespace: dubbo-system -spec: - maxReplicas: 5 - metrics: - - resource: - name: cpu - target: - type: Utilization - averageUtilization: 80 - type: Resource - minReplicas: 1 - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: istio-ingressgateway`, - want: `spec: - scaleTargetRef: - name: ingressgateway -> istio-ingressgateway -`, - }, - { - desc: "arrays with same items", - a: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - - label1 - - label2 - - label3 -`, - b: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - - label1 - - label2 - - label3 -`, - want: ``, - }, - { - desc: "arrays with different items", - a: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - - label1 - - label2 - - label3 -`, - b: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - - label1 - - label5 - - label6 -`, - want: `metadata: - labels: - '[#1]': label2 -> label5 - '[#2]': label3 -> label6 -`, - }, - { - desc: "arrays with same items, order changed", - a: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - - label1 - - label2 - - label3 -`, - b: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - - label2 - - label3 - - label1 -`, - want: `metadata: - labels: - '[?->2]': -> label1 (ADDED) - '[0->?]': label1 -> (REMOVED) -`, - }, - { - desc: "arrays with items", - a: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - - label0 - - label1 - - label2 -`, - b: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - - label4 - - label5 - - label2 - - label3 - - label1 - - label0 -`, - want: `metadata: - labels: - '[#0]': label0 -> label4 - '[?->1]': -> label5 (ADDED) - '[?->2]': -> label2 (ADDED) - '[?->3]': -> label3 (ADDED) - '[2->5]': label2 -> label0 -`, - }, - { - desc: "arrays with additional items", - a: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - - label1 - - label2 - - label3 -`, - b: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - - label1 - - label2 - - label3 - - label4 - - label5 -`, - want: `metadata: - labels: - '[?->3]': -> label4 (ADDED) - '[?->4]': -> label5 (ADDED) -`, - }, - { - desc: "arrays with missing items", - a: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - - label1 - - label2 - - label3 - - label4 - - label5 -`, - b: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - - label1 - - label2 - - label3 -`, - want: `metadata: - labels: - '[3->?]': label4 -> (REMOVED) - '[4->?]': label5 -> (REMOVED) -`, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got, want := YAMLCmp(tt.a, tt.b), tt.want; !(got == want) { - t.Errorf("%s: got:%v, want:%v", tt.desc, got, want) - } - }) - } -} - -func TestYAMLCmpWithIgnore(t *testing.T) { - tests := []struct { - desc string - a string - b string - i []string - want interface{} - }{ - { - desc: "identical", - a: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio`, - b: `apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio`, - i: []string{"metadata.annotations.checksum/config-volume"}, - want: ``, - }, - { - desc: "ignore checksum", - a: `apiVersion: autoscaling/v2 -kind: Deployment -metadata: - annotations: - checksum/config-volume: 43d72e930ed33e3e01731f8bcbf31dbf02cb1c1fc53bcc09199ab45c0d031b60 - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio`, - b: `apiVersion: autoscaling/v2 -kind: Deployment -metadata: - annotations: - checksum/config-volume: 03ba6246b2c39b48a4f8c3a92c3420a0416804d38ebe292e65cf674fb0875192 - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio`, - i: []string{"metadata.annotations.checksum/config-volume"}, - want: ``, - }, - { - desc: "ignore missing checksum value", - a: `apiVersion: autoscaling/v2 -kind: Deployment -metadata: - annotations: - checksum/config-volume: 43d72e930ed33e3e01731f8bcbf31dbf02cb1c1fc53bcc09199ab45c0d031b60 - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio`, - b: `apiVersion: autoscaling/v2 -kind: Deployment -metadata: - annotations: - checksum/config-volume: - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio`, - i: []string{"metadata.annotations.checksum/config-volume"}, - want: ``, - }, - { - desc: "ignore additional checksum value", - a: `apiVersion: autoscaling/v2 -kind: Deployment -metadata: - annotations: - checksum/config-volume: - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio`, - b: `apiVersion: autoscaling/v2 -kind: Deployment -metadata: - annotations: - checksum/config-volume: 43d72e930ed33e3e01731f8bcbf31dbf02cb1c1fc53bcc09199ab45c0d031b60 - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio`, - i: []string{"metadata.annotations.checksum/config-volume"}, - want: ``, - }, - { - desc: "show checksum not exist", - a: `apiVersion: autoscaling/v2 -kind: Deployment -metadata: - annotations: - checksum/config-volume: 43d72e930ed33e3e01731f8bcbf31dbf02cb1c1fc53bcc09199ab45c0d031b60 - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio`, - b: `apiVersion: autoscaling/v2 -kind: Deployment -metadata: - annotations: - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio`, - i: []string{"metadata.annotations.checksum/config-volume"}, - want: `metadata: - annotations: map[checksum/config-volume:43d72e930ed33e3e01731f8bcbf31dbf02cb1c1fc53bcc09199ab45c0d031b60] - -> (REMOVED) -`, - }, - { - desc: "ignore by wildcard", - a: `apiVersion: autoscaling/v2 -kind: Deployment -metadata: - annotations: - checksum/config-volume: 01d72e930ed33e3e01731f8bcbf31dbf02cb1c1fc53bcc09199ab45c0d031b60 - name: istio-ingressgateway - namespace: dubbo-system - labels: - checksum/config-volume: 02ba6246b2c39b48a4f8c3a92c3420a0416804d38ebe292e65cf674fb0875192 - app: istio-ingressgateway - release: istio`, - b: `apiVersion: autoscaling/v2 -kind: Deployment -metadata: - annotations: - checksum/config-volume: 03ba6246b2c39b48a4f8c3a92c3420a0416804d38ebe292e65cf674fb0875192 - name: istio-ingressgateway - namespace: dubbo-system - labels: - checksum/config-volume: 04ba6246b2c39b48a4f8c3a92c3420a0416804d38ebe292e65cf674fb0875192 - app: istio-ingressgateway - release: istio`, - i: []string{"*.checksum/config-volume"}, - want: ``, - }, - { - desc: "ignore by wildcard negative", - a: `apiVersion: autoscaling/v2 -kind: Deployment -metadata: - annotations: - checksum/config-volume: 01d72e930ed33e3e01731f8bcbf31dbf02cb1c1fc53bcc09199ab45c0d031b60 - name: istio-ingressgateway - namespace: dubbo-system - labels: - checksum/config-volume: 02ba6246b2c39b48a4f8c3a92c3420a0416804d38ebe292e65cf674fb0875192 - app: istio-ingressgateway - release: istio`, - b: `apiVersion: autoscaling/v2 -kind: Deployment -metadata: - annotations: - checksum/config-volume: 03ba6246b2c39b48a4f8c3a92c3420a0416804d38ebe292e65cf674fb0875192 - name: istio-ingressgateway - namespace: dubbo-system - labels: - checksum/config-volume: 04ba6246b2c39b48a4f8c3a92c3420a0416804d38ebe292e65cf674fb0875192 - app: istio-ingressgateway - release: istio`, - i: []string{"*labels.checksum/config-volume"}, - want: `metadata: - annotations: - checksum/config-volume: 01d72e930ed33e3e01731f8bcbf31dbf02cb1c1fc53bcc09199ab45c0d031b60 - -> 03ba6246b2c39b48a4f8c3a92c3420a0416804d38ebe292e65cf674fb0875192 -`, - }, - { - desc: "ignore multiple paths", - a: `apiVersion: autoscaling/v2 -kind: Deployment -metadata: - annotations: - checksum/config-volume: 43d72e930ed33e3e01731f8bcbf31dbf02cb1c1fc53bcc09199ab45c0d031b60 - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: ingressgateway - release: istio`, - b: `apiVersion: autoscaling/v2 -kind: Deployment -metadata: - annotations: - checksum/config-volume: 03ba6246b2c39b48a4f8c3a92c3420a0416804d38ebe292e65cf674fb0875192 - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio`, - i: []string{ - "metadata.annotations.checksum/config-volume", - "metadata.labels.app", - }, - want: ``, - }, - { - desc: "ignore multiple paths negative", - a: `apiVersion: autoscaling/v2 -kind: Deployment -metadata: - annotations: - checksum/config-volume: 43d72e930ed33e3e01731f8bcbf31dbf02cb1c1fc53bcc09199ab45c0d031b60 - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: ingressgateway - release: istio`, - b: `apiVersion: autoscaling/v2 -kind: Deployment -metadata: - annotations: - checksum/config-volume: 03ba6246b2c39b48a4f8c3a92c3420a0416804d38ebe292e65cf674fb0875192 - name: istio-ingressgateway - namespace: dubbo-system - labels: - app: istio-ingressgateway - release: istio`, - i: []string{ - "metadata.annotations.checksum/config-volume", - }, - want: `metadata: - labels: - app: ingressgateway -> istio-ingressgateway -`, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got, want := YAMLCmpWithIgnore(tt.a, tt.b, tt.i, ""), tt.want; !(got == want) { - t.Errorf("%s: got:%v, want:%v", tt.desc, got, want) - } - }) - } -} - -func TestYAMLCmpWithIgnoreTree(t *testing.T) { - tests := []struct { - desc string - a string - b string - mask string - want interface{} - }{ - { - desc: "ignore masked", - a: ` -ak: av -bk: - b1k: b1v - b2k: b2v -`, - b: ` -ak: av -bk: - b1k: b1v-changed - b2k: b2v-changed -`, - mask: ` -bk: - b1k: ignored - b2k: ignored -`, - want: ``, - }, - { - desc: "ignore nested masked", - a: ` -ak: av -bk: - bbk: - b1k: b1v - b2k: b2v -`, - b: ` -ak: av -bk: - bbk: - b1k: b1v-changed - b2k: b2v-changed -`, - mask: ` -bk: - bbk: - b1k: ignored -`, - want: `bk: - bbk: - b2k: b2v -> b2v-changed -`, - }, - { - desc: "not ignore non-masked", - a: ` -ak: av -bk: - bbk: - b1k: b1v - b2k: b2v -`, - b: ` -ak: av -bk: - bbk: - b1k: b1v-changed - b2k: b2v -`, - mask: ` -bk: - bbk: - b1k: - bbb1k: ignored -`, - want: `bk: - bbk: - b1k: b1v -> b1v-changed -`, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got, want := YAMLCmpWithIgnore(tt.a, tt.b, nil, tt.mask), tt.want; !(got == want) { - t.Errorf("%s: got:%v, want:%v", tt.desc, got, want) - } - }) - } -} - -func TestYAMLCmpWithYamlInline(t *testing.T) { - tests := []struct { - desc string - a string - b string - want interface{} - }{ - { - desc: "ConfigMap data order changed", - a: `kind: ConfigMap -data: - validatingwebhookconfiguration.yaml: |- - kind: ValidatingWebhookConfiguration - apiVersion: admissionregistration.k8s.io/v1beta1 - metadata: - name: istio-galley - value: foo`, - b: `kind: ConfigMap -data: - validatingwebhookconfiguration.yaml: |- - apiVersion: admissionregistration.k8s.io/v1beta1 - kind: ValidatingWebhookConfiguration - metadata: - value: foo - name: istio-galley`, - want: ``, - }, - { - desc: "ConfigMap data value change", - a: `kind: ConfigMap -data: - validatingwebhookconfiguration.yaml: |- - apiVersion: admissionregistration.k8s.io/v1beta1 - kind: ValidatingWebhookConfiguration - metadata: - name: istio-galley`, - b: `kind: ConfigMap -data: - validatingwebhookconfiguration.yaml: |- - kind: ValidatingWebhookConfiguration - apiVersion: admissionregistration.k8s.io/v1beta2 - metadata: - name: istio-galley`, - want: `data: - validatingwebhookconfiguration.yaml: - apiVersion: admissionregistration.k8s.io/v1beta1 -> admissionregistration.k8s.io/v1beta2 -`, - }, - { - desc: "ConfigMap data deep nested value change", - a: `apiVersion: v1 -kind: ConfigMap -metadata: - name: injector-mesh -data: - mesh: |- - defaultConfig: - tracing: - zipkin: - address: zipkin.dubbo-system:9411 - controlPlaneAuthPolicy: NONE - connectTimeout: 10s`, - b: `apiVersion: v1 -kind: ConfigMap -metadata: - name: injector-mesh -data: - mesh: |- - defaultConfig: - connectTimeout: 10s - tracing: - zipkin: - address: zipkin.dubbo-system:9412 - controlPlaneAuthPolicy: NONE`, - want: `data: - mesh: - defaultConfig: - tracing: - zipkin: - address: zipkin.dubbo-system:9411 -> zipkin.dubbo-system:9412 -`, - }, - { - desc: "ConfigMap data multiple changes", - a: `apiVersion: v1 -kind: ConfigMap -metadata: - name: injector-mesh - namespace: dubbo-system - labels: - release: istio -data: - mesh: |- - defaultConfig: - connectTimeout: 10s - configPath: "/etc/istio/proxyv1" - serviceCluster: istio-proxy - drainDuration: 45s - parentShutdownDuration: 1m0s - proxyAdminPortA: 15000 - concurrency: 2 - tracing: - zipkin: - address: zipkin.dubbo-system:9411 - controlPlaneAuthPolicy: NONE - discoveryAddress: istio-pilot.dubbo-system:15010`, - b: `apiVersion: v1 -kind: ConfigMap -metadata: - name: injector-mesh - namespace: dubbo-system - labels: - release: istio -data: - mesh: |- - defaultConfig: - connectTimeout: 10s - configPath: "/etc/istio/proxyv2" - serviceCluster: istio-proxy - drainDuration: 45s - parentShutdownDuration: 1m0s - proxyAdminPortB: 15000 - concurrency: 2 - tracing: - zipkin: - address: zipkin.dubbo-system:9411 - controlPlaneAuthPolicy: NONE - discoveryAddress: istio-pilot.dubbo-system:15010`, - want: `data: - mesh: - defaultConfig: - configPath: /etc/istio/proxyv1 -> /etc/istio/proxyv2 - proxyAdminPortA: 15000 -> (REMOVED) - proxyAdminPortB: -> 15000 (ADDED) -`, - }, - { - desc: "Not ConfigMap, identical", - a: `kind: Config -data: - validatingwebhookconfiguration.yaml: |- - apiVersion: admissionregistration.k8s.io/v1beta1 - kind: ValidatingWebhookConfiguration - metadata: - name: istio-galley`, - b: `kind: Config -data: - validatingwebhookconfiguration.yaml: |- - apiVersion: admissionregistration.k8s.io/v1beta1 - kind: ValidatingWebhookConfiguration - metadata: - name: istio-galley`, - want: ``, - }, - { - desc: "Not ConfigMap, Order changed", - a: `kind: Config -data: - validatingwebhookconfiguration.yaml: |- - apiVersion: admissionregistration.k8s.io/v1beta1 - kind: ValidatingWebhookConfiguration - metadata: - name: istio-galley`, - b: `kind: Config -data: - validatingwebhookconfiguration.yaml: |- - kind: ValidatingWebhookConfiguration - apiVersion: admissionregistration.k8s.io/v1beta1 - metadata: - name: istio-galley`, - want: `data: - validatingwebhookconfiguration.yaml: |- - apiVersion: admissionregistration.k8s.io/v1beta1 - kind: ValidatingWebhookConfiguration - metadata: - name: istio-galley -> kind: ValidatingWebhookConfiguration - apiVersion: admissionregistration.k8s.io/v1beta1 - metadata: - name: istio-galley -`, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got, want := YAMLCmp(tt.a, tt.b), tt.want; !(got == want) { - t.Errorf("%s: got:%v, want:%v", tt.desc, got, want) - } - }) - } -} - -func TestManifestDiff(t *testing.T) { - testDeploymentYaml1 := `apiVersion: apps/v1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system - labels: - istio: citadel -spec: - replicas: 1 - template: - metadata: - labels: - istio: citadel - spec: - containers: - - name: citadel - image: docker.io/istio/citadel:1.1.8 -` - - testDeploymentYaml2 := `apiVersion: apps/v1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system - labels: - istio: citadel -spec: - replicas: 1 - template: - metadata: - labels: - istio: citadel - spec: - containers: - - name: citadel - image: docker.io/istio/citadel:1.2.2 -` - - testPodYaml1 := `apiVersion: v1 -kind: Pod -metadata: - name: istio-galley-75bcd59768-hpt5t - namespace: dubbo-system - labels: - istio: galley -spec: - containers: - - name: galley - image: docker.io/istio/galley:1.1.8 - ports: - - containerPort: 443 - protocol: TCP - - containerPort: 15014 - protocol: TCP - - containerPort: 9901 - protocol: TCP -` - - testServiceYaml1 := `apiVersion: v1 -kind: Service -metadata: - labels: - app: pilot - name: istio-pilot - namespace: dubbo-system -spec: - type: ClusterIP - ports: - - name: grpc-xds - port: 15010 - protocol: TCP - targetPort: 15010 - - name: http-monitoring - port: 15014 - protocol: TCP - targetPort: 15014 - selector: - istio: pilot -` - - manifestDiffTests := []struct { - desc string - yamlStringA string - yamlStringB string - want string - }{ - { - "ManifestDiffWithIdenticalResource", - testDeploymentYaml1 + object.YAMLSeparator, - testDeploymentYaml1, - "", - }, - { - "ManifestDiffWithIdenticalMultipleResources", - testDeploymentYaml1 + object.YAMLSeparator + testPodYaml1 + object.YAMLSeparator + testServiceYaml1, - testPodYaml1 + object.YAMLSeparator + testServiceYaml1 + object.YAMLSeparator + testDeploymentYaml1, - "", - }, - { - "ManifestDiffWithDifferentResource", - testDeploymentYaml1, - testDeploymentYaml2, - "Object Deployment:dubbo-system:istio-citadel has diffs", - }, - { - "ManifestDiffWithDifferentMultipleResources", - testDeploymentYaml1 + object.YAMLSeparator + testPodYaml1 + object.YAMLSeparator + testServiceYaml1, - testDeploymentYaml2 + object.YAMLSeparator + testPodYaml1 + object.YAMLSeparator + testServiceYaml1, - "Object Deployment:dubbo-system:istio-citadel has diffs", - }, - { - "ManifestDiffMissingResourcesInA", - testPodYaml1 + object.YAMLSeparator + testDeploymentYaml1 + object.YAMLSeparator, - testDeploymentYaml1 + object.YAMLSeparator + testPodYaml1 + object.YAMLSeparator + testServiceYaml1, - "Object Service:dubbo-system:istio-pilot is missing in A", - }, - { - "ManifestDiffMissingResourcesInB", - testDeploymentYaml1 + object.YAMLSeparator + testPodYaml1 + object.YAMLSeparator + testServiceYaml1, - testServiceYaml1 + object.YAMLSeparator + testPodYaml1, - "Object Deployment:dubbo-system:istio-citadel is missing in B", - }, - } - - for _, tt := range manifestDiffTests { - for _, v := range []bool{true, false} { - t.Run(tt.desc, func(t *testing.T) { - got, err := ManifestDiff(tt.yamlStringA, tt.yamlStringB, v) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !strings.Contains(got, tt.want) { - t.Errorf("%s:\ngot:\n%v\ndoes't contains\nwant:\n%v", tt.desc, got, tt.want) - } - }) - } - } -} - -func TestManifestDiffWithSelectAndIgnore(t *testing.T) { - testDeploymentYaml1 := `apiVersion: apps/v1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system - labels: - istio: citadel -spec: - replicas: 1 - selector: - matchLabels: - istio: citadel - template: - metadata: - labels: - istio: citadel - spec: - containers: - - name: citadel - image: docker.io/istio/citadel:1.1.8 ---- -` - - testDeploymentYaml2 := `apiVersion: apps/v1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system - labels: - istio: citadel -spec: - replicas: 1 - selector: - matchLabels: - istio: citadel - template: - metadata: - labels: - istio: citadel - spec: - containers: - - name: citadel - image: docker.io/istio/citadel:1.2.2 ---- -` - - testPodYaml1 := `apiVersion: v1 -kind: Pod -metadata: - name: istio-galley-75bcd59768-hpt5t - namespace: dubbo-system - labels: - istio: galley -spec: - containers: - - name: galley - image: docker.io/istio/galley:1.1.8 - ports: - - containerPort: 443 - protocol: TCP - - containerPort: 15014 - protocol: TCP - - containerPort: 9901 - protocol: TCP ---- -` - - testServiceYaml1 := `apiVersion: v1 -kind: Service -metadata: - labels: - app: pilot - name: istio-pilot - namespace: dubbo-system -spec: - type: ClusterIP - ports: - - name: grpc-xds - port: 15010 - protocol: TCP - targetPort: 15010 - - name: http-monitoring - port: 15014 - protocol: TCP - targetPort: 15014 - selector: - istio: pilot ---- -` - - manifestDiffWithSelectAndIgnoreTests := []struct { - desc string - yamlStringA string - yamlStringB string - selectResources string - ignoreResources string - want string - }{ - { - "ManifestDiffWithSelectAndIgnoreForIdenticalResource", - testDeploymentYaml1 + object.YAMLSeparator, - testDeploymentYaml1, - "::", - "", - "", - }, - { - "ManifestDiffWithSelectAndIgnoreForDifferentResourcesIgnoreSingle", - testDeploymentYaml1 + object.YAMLSeparator + testPodYaml1 + object.YAMLSeparator + testServiceYaml1, - testDeploymentYaml1 + object.YAMLSeparator, - "Deployment:*:istio-citadel", - "Service:*:", - "", - }, - { - "ManifestDiffWithSelectAndIgnoreForDifferentResourcesIgnoreMultiple", - testDeploymentYaml1 + object.YAMLSeparator + testPodYaml1 + object.YAMLSeparator + testServiceYaml1, - testDeploymentYaml1, - "Deployment:*:istio-citadel", - "Pod::*,Service:dubbo-system:*", - "", - }, - { - "ManifestDiffWithSelectAndIgnoreForDifferentResourcesSelectSingle", - testDeploymentYaml1 + object.YAMLSeparator + testPodYaml1 + object.YAMLSeparator + testServiceYaml1, - testServiceYaml1 + object.YAMLSeparator + testDeploymentYaml1, - "Deployment::istio-citadel", - "Pod:*:*", - "", - }, - { - "ManifestDiffWithSelectAndIgnoreForDifferentResourcesSelectSingle", - testDeploymentYaml1 + object.YAMLSeparator + testPodYaml1 + object.YAMLSeparator + testServiceYaml1, - testServiceYaml1 + object.YAMLSeparator + testDeploymentYaml1, - "Deployment::istio-citadel,Service:dubbo-system:istio-pilot,Pod:*:*", - "Pod:*:*", - "", - }, - { - "ManifestDiffWithSelectAndIgnoreForDifferentResourceForDefault", - testDeploymentYaml1, - testDeploymentYaml2 + object.YAMLSeparator, - "::", - "", - "Object Deployment:dubbo-system:istio-citadel has diffs", - }, - { - "ManifestDiffWithSelectAndIgnoreForDifferentResourceForSingleSelectAndIgnore", - testDeploymentYaml1 + object.YAMLSeparator + testPodYaml1 + object.YAMLSeparator + testServiceYaml1, - testDeploymentYaml2 + object.YAMLSeparator + testPodYaml1 + object.YAMLSeparator + testServiceYaml1, - "Deployment:*:*", - "Pod:*:*", - "Object Deployment:dubbo-system:istio-citadel has diffs", - }, - { - "ManifestDiffWithSelectAndIgnoreForMissingResourcesInA", - testPodYaml1 + object.YAMLSeparator + testDeploymentYaml1, - testDeploymentYaml1 + object.YAMLSeparator + testPodYaml1 + object.YAMLSeparator + testServiceYaml1, - "Pod:dubbo-system:Citadel,Service:dubbo-system:", - "Pod:*:*", - "Object Service:dubbo-system:istio-pilot is missing in A", - }, - { - "ManifestDiffWithSelectAndIgnoreForMissingResourcesInB", - testDeploymentYaml1 + object.YAMLSeparator + testPodYaml1 + object.YAMLSeparator + testServiceYaml1, - testServiceYaml1 + object.YAMLSeparator + testPodYaml1 + object.YAMLSeparator, - "*:dubbo-system:*", - "Pod::", - "Object Deployment:dubbo-system:istio-citadel is missing in B", - }, - } - - for _, tt := range manifestDiffWithSelectAndIgnoreTests { - for _, v := range []bool{true, false} { - t.Run(tt.desc, func(t *testing.T) { - got, err := ManifestDiffWithRenameSelectIgnore(tt.yamlStringA, tt.yamlStringB, - "", tt.selectResources, tt.ignoreResources, v) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !strings.Contains(got, tt.want) { - t.Errorf("%s:\ngot:\n%v\ndoes't contains\nwant:\n%v", tt.desc, got, tt.want) - } - }) - } - } -} - -func TestManifestDiffWithRenameSelectIgnore(t *testing.T) { - testDeploymentYaml := `apiVersion: apps/v1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system - labels: - istio: citadel -spec: - replicas: 1 - selector: - matchLabels: - istio: citadel - template: - metadata: - labels: - istio: citadel - spec: - containers: - - name: citadel - image: docker.io/istio/citadel:1.1.8 ---- -` - - testDeploymentYamlRenamed := `apiVersion: apps/v1 -kind: Deployment -metadata: - name: istio-ca - namespace: dubbo-system - labels: - istio: citadel -spec: - replicas: 1 - selector: - matchLabels: - istio: citadel - template: - metadata: - labels: - istio: citadel - spec: - containers: - - name: citadel - image: docker.io/istio/citadel:1.1.8 ---- -` - - testServiceYaml := `apiVersion: v1 -kind: Service -metadata: - labels: - app: pilot - name: istio-pilot - namespace: dubbo-system -spec: - type: ClusterIP - ports: - - name: grpc-xds - port: 15010 - protocol: TCP - targetPort: 15010 - - name: http-monitoring - port: 15014 - protocol: TCP - targetPort: 15014 - selector: - istio: pilot ---- -` - - testServiceYamlRenamed := `apiVersion: v1 -kind: Service -metadata: - labels: - app: pilot - name: istio-control - namespace: dubbo-system -spec: - type: ClusterIP - ports: - - name: grpc-xds - port: 15010 - protocol: TCP - targetPort: 15010 - - name: http-monitoring - port: 15014 - protocol: TCP - targetPort: 15014 - selector: - istio: pilot ---- -` - - manifestDiffWithRenameSelectIgnoreTests := []struct { - desc string - yamlStringA string - yamlStringB string - renameResources string - selectResources string - ignoreResources string - want string - }{ - { - "ManifestDiffDeployWithRenamedFlagMultiResourceWildcard", - testDeploymentYaml + object.YAMLSeparator + testServiceYaml, - testDeploymentYamlRenamed + object.YAMLSeparator + testServiceYamlRenamed, - "Service:*:istio-pilot->::istio-control,Deployment::istio-citadel->::istio-ca", - "::", - "", - ` - -Object Deployment:dubbo-system:istio-ca has diffs: - -metadata: - name: istio-citadel -> istio-ca - - -Object Service:dubbo-system:istio-control has diffs: - -metadata: - name: istio-pilot -> istio-control -`, - }, - { - "ManifestDiffDeployWithRenamedFlagMultiResource", - testDeploymentYaml + object.YAMLSeparator + testServiceYaml, - testDeploymentYamlRenamed + object.YAMLSeparator + testServiceYamlRenamed, - "Service:dubbo-system:istio-pilot->Service:dubbo-system:istio-control,Deployment:dubbo-system:istio-citadel->Deployment:dubbo-system:istio-ca", - "::", - "", - ` - -Object Deployment:dubbo-system:istio-ca has diffs: - -metadata: - name: istio-citadel -> istio-ca - - -Object Service:dubbo-system:istio-control has diffs: - -metadata: - name: istio-pilot -> istio-control -`, - }, - { - "ManifestDiffDeployWithRenamedFlag", - testDeploymentYaml, - testDeploymentYamlRenamed, - "Deployment:dubbo-system:istio-citadel->Deployment:dubbo-system:istio-ca", - "::", - "", - ` - -Object Deployment:dubbo-system:istio-ca has diffs: - -metadata: - name: istio-citadel -> istio-ca -`, - }, - { - "ManifestDiffRenamedDeploy", - testDeploymentYaml, - testDeploymentYamlRenamed, - "", - "::", - "", - ` - -Object Deployment:dubbo-system:istio-ca is missing in A: - - - -Object Deployment:dubbo-system:istio-citadel is missing in B: - -`, - }, - } - - for _, tt := range manifestDiffWithRenameSelectIgnoreTests { - t.Run(tt.desc, func(t *testing.T) { - got, err := ManifestDiffWithRenameSelectIgnore(tt.yamlStringA, tt.yamlStringB, - tt.renameResources, tt.selectResources, tt.ignoreResources, false) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if got != tt.want { - t.Errorf("%s:\ngot:\n%v\ndoes't equals to\nwant:\n%v", tt.desc, got, tt.want) - } - }) - } -} diff --git a/operator/pkg/component/component.go b/operator/pkg/component/component.go deleted file mode 100644 index f9cabb7a9..000000000 --- a/operator/pkg/component/component.go +++ /dev/null @@ -1,506 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/* -Package component defines an in-memory representation of IstioOperator... It provides functions -for manipulating the component and rendering a manifest from it. -See ../README.md for an architecture overview. -*/ -package component - -import ( - "fmt" -) - -import ( - "istio.io/api/operator/v1alpha1" - "istio.io/pkg/log" - "k8s.io/apimachinery/pkg/version" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/helm" - "github.com/apache/dubbo-go-pixiu/operator/pkg/metrics" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/patch" - "github.com/apache/dubbo-go-pixiu/operator/pkg/tpath" - "github.com/apache/dubbo-go-pixiu/operator/pkg/translate" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -const ( - // String to emit for any component which is disabled. - componentDisabledStr = "component is disabled." - yamlCommentStr = "#" -) - -var scope = log.RegisterScope("installer", "installer", 0) - -// Options defines options for a component. -type Options struct { - // installSpec is the global IstioOperatorSpec. - InstallSpec *v1alpha1.IstioOperatorSpec - // translator is the translator for this component. - Translator *translate.Translator - // Namespace is the namespace for this component. - Namespace string - // Filter is the filenames to render - Filter sets.Set - // Version is the Kubernetes version information. - Version *version.Info -} - -// IstioComponent defines the interface for a component. -type IstioComponent interface { - // ComponentName returns the name of the component. - ComponentName() name.ComponentName - // ResourceName returns the name of the resources of the component. - ResourceName() string - // Namespace returns the namespace for the component. - Namespace() string - // Enabled reports whether the component is enabled. - Enabled() bool - // Run starts the component. Must me called before the component is used. - Run() error - // RenderManifest returns a string with the rendered manifest for the component. - RenderManifest() (string, error) -} - -// CommonComponentFields is a struct common to all components. -type CommonComponentFields struct { - *Options - ComponentName name.ComponentName - // resourceName is the name of all resources for this component. - ResourceName string - // index is the index of the component (only used for components with multiple instances like gateways). - index int - // componentSpec for the actual component e.g. GatewaySpec, ComponentSpec. - componentSpec interface{} - // started reports whether the component is in initialized and running. - started bool - renderer helm.TemplateRenderer -} - -// NewCoreComponent creates a new IstioComponent with the given componentName and options. -func NewCoreComponent(cn name.ComponentName, opts *Options) IstioComponent { - var component IstioComponent - switch cn { - case name.IstioBaseComponentName: - component = NewCRDComponent(opts) - case name.PilotComponentName: - component = NewPilotComponent(opts) - case name.CNIComponentName: - component = NewCNIComponent(opts) - case name.IstiodRemoteComponentName: - component = NewIstiodRemoteComponent(opts) - default: - scope.Errorf("Unknown component componentName: " + string(cn)) - } - return component -} - -// BaseComponent is the base component. -type BaseComponent struct { - *CommonComponentFields -} - -// NewCRDComponent creates a new BaseComponent and returns a pointer to it. -func NewCRDComponent(opts *Options) *BaseComponent { - return &BaseComponent{ - &CommonComponentFields{ - Options: opts, - ComponentName: name.IstioBaseComponentName, - }, - } -} - -// Run implements the IstioComponent interface. -func (c *BaseComponent) Run() error { - return runComponent(c.CommonComponentFields) -} - -// RenderManifest implements the IstioComponent interface. -func (c *BaseComponent) RenderManifest() (string, error) { - return renderManifest(c, c.CommonComponentFields) -} - -// ComponentName implements the IstioComponent interface. -func (c *BaseComponent) ComponentName() name.ComponentName { - return c.CommonComponentFields.ComponentName -} - -// ResourceName implements the IstioComponent interface. -func (c *BaseComponent) ResourceName() string { - return c.CommonComponentFields.ResourceName -} - -// Namespace implements the IstioComponent interface. -func (c *BaseComponent) Namespace() string { - return c.CommonComponentFields.Namespace -} - -// Enabled implements the IstioComponent interface. -func (c *BaseComponent) Enabled() bool { - return isCoreComponentEnabled(c.CommonComponentFields) -} - -// PilotComponent is the pilot component. -type PilotComponent struct { - *CommonComponentFields -} - -// NewPilotComponent creates a new PilotComponent and returns a pointer to it. -func NewPilotComponent(opts *Options) *PilotComponent { - cn := name.PilotComponentName - return &PilotComponent{ - &CommonComponentFields{ - Options: opts, - ComponentName: cn, - ResourceName: opts.Translator.ComponentMaps[cn].ResourceName, - }, - } -} - -// Run implements the IstioComponent interface. -func (c *PilotComponent) Run() error { - return runComponent(c.CommonComponentFields) -} - -// RenderManifest implements the IstioComponent interface. -func (c *PilotComponent) RenderManifest() (string, error) { - return renderManifest(c, c.CommonComponentFields) -} - -// ComponentName implements the IstioComponent interface. -func (c *PilotComponent) ComponentName() name.ComponentName { - return c.CommonComponentFields.ComponentName -} - -// ResourceName implements the IstioComponent interface. -func (c *PilotComponent) ResourceName() string { - return c.CommonComponentFields.ResourceName -} - -// Namespace implements the IstioComponent interface. -func (c *PilotComponent) Namespace() string { - return c.CommonComponentFields.Namespace -} - -// Enabled implements the IstioComponent interface. -func (c *PilotComponent) Enabled() bool { - return isCoreComponentEnabled(c.CommonComponentFields) -} - -// CNIComponent is the istio cni component. -type CNIComponent struct { - *CommonComponentFields -} - -// NewCNIComponent creates a new NewCNIComponent and returns a pointer to it. -func NewCNIComponent(opts *Options) *CNIComponent { - cn := name.CNIComponentName - return &CNIComponent{ - &CommonComponentFields{ - Options: opts, - ComponentName: cn, - }, - } -} - -// Run implements the IstioComponent interface. -func (c *CNIComponent) Run() error { - return runComponent(c.CommonComponentFields) -} - -// RenderManifest implements the IstioComponent interface. -func (c *CNIComponent) RenderManifest() (string, error) { - return renderManifest(c, c.CommonComponentFields) -} - -// ComponentName implements the IstioComponent interface. -func (c *CNIComponent) ComponentName() name.ComponentName { - return c.CommonComponentFields.ComponentName -} - -// ResourceName implements the IstioComponent interface. -func (c *CNIComponent) ResourceName() string { - return c.CommonComponentFields.ResourceName -} - -// Namespace implements the IstioComponent interface. -func (c *CNIComponent) Namespace() string { - return c.CommonComponentFields.Namespace -} - -// Enabled implements the IstioComponent interface. -func (c *CNIComponent) Enabled() bool { - return isCoreComponentEnabled(c.CommonComponentFields) -} - -// IstiodRemoteComponent is the istiod remote component. -type IstiodRemoteComponent struct { - *CommonComponentFields -} - -// NewIstiodRemoteComponent creates a new NewIstiodRemoteComponent and returns a pointer to it. -func NewIstiodRemoteComponent(opts *Options) *IstiodRemoteComponent { - cn := name.IstiodRemoteComponentName - return &IstiodRemoteComponent{ - &CommonComponentFields{ - Options: opts, - ComponentName: cn, - }, - } -} - -// Run implements the IstioComponent interface. -func (c *IstiodRemoteComponent) Run() error { - return runComponent(c.CommonComponentFields) -} - -// RenderManifest implements the IstioComponent interface. -func (c *IstiodRemoteComponent) RenderManifest() (string, error) { - return renderManifest(c, c.CommonComponentFields) -} - -// ComponentName implements the IstioComponent interface. -func (c *IstiodRemoteComponent) ComponentName() name.ComponentName { - return c.CommonComponentFields.ComponentName -} - -// ResourceName implements the IstioComponent interface. -func (c *IstiodRemoteComponent) ResourceName() string { - return c.CommonComponentFields.ResourceName -} - -// Namespace implements the IstioComponent interface. -func (c *IstiodRemoteComponent) Namespace() string { - return c.CommonComponentFields.Namespace -} - -// Enabled implements the IstioComponent interface. -func (c *IstiodRemoteComponent) Enabled() bool { - return isCoreComponentEnabled(c.CommonComponentFields) -} - -// IngressComponent is the ingress gateway component. -type IngressComponent struct { - *CommonComponentFields -} - -// NewIngressComponent creates a new IngressComponent and returns a pointer to it. -func NewIngressComponent(resourceName string, index int, spec *v1alpha1.GatewaySpec, opts *Options) *IngressComponent { - cn := name.IngressComponentName - return &IngressComponent{ - CommonComponentFields: &CommonComponentFields{ - Options: opts, - ComponentName: cn, - ResourceName: resourceName, - index: index, - componentSpec: spec, - }, - } -} - -// Run implements the IstioComponent interface. -func (c *IngressComponent) Run() error { - return runComponent(c.CommonComponentFields) -} - -// RenderManifest implements the IstioComponent interface. -func (c *IngressComponent) RenderManifest() (string, error) { - return renderManifest(c, c.CommonComponentFields) -} - -// ComponentName implements the IstioComponent interface. -func (c *IngressComponent) ComponentName() name.ComponentName { - return c.CommonComponentFields.ComponentName -} - -// ResourceName implements the IstioComponent interface. -func (c *IngressComponent) ResourceName() string { - return c.CommonComponentFields.ResourceName -} - -// Namespace implements the IstioComponent interface. -func (c *IngressComponent) Namespace() string { - return c.CommonComponentFields.Namespace -} - -// Enabled implements the IstioComponent interface. -func (c *IngressComponent) Enabled() bool { - // type assert is guaranteed to work in this context. - return c.componentSpec.(*v1alpha1.GatewaySpec).Enabled.GetValue() -} - -// EgressComponent is the egress gateway component. -type EgressComponent struct { - *CommonComponentFields -} - -// NewEgressComponent creates a new IngressComponent and returns a pointer to it. -func NewEgressComponent(resourceName string, index int, spec *v1alpha1.GatewaySpec, opts *Options) *EgressComponent { - cn := name.EgressComponentName - return &EgressComponent{ - CommonComponentFields: &CommonComponentFields{ - Options: opts, - ComponentName: cn, - index: index, - componentSpec: spec, - ResourceName: resourceName, - }, - } -} - -// Run implements the IstioComponent interface. -func (c *EgressComponent) Run() error { - return runComponent(c.CommonComponentFields) -} - -// RenderManifest implements the IstioComponent interface. -func (c *EgressComponent) RenderManifest() (string, error) { - return renderManifest(c, c.CommonComponentFields) -} - -// ComponentName implements the IstioComponent interface. -func (c *EgressComponent) ComponentName() name.ComponentName { - return c.CommonComponentFields.ComponentName -} - -// ResourceName implements the IstioComponent interface. -func (c *EgressComponent) ResourceName() string { - return c.CommonComponentFields.ResourceName -} - -// Namespace implements the IstioComponent interface. -func (c *EgressComponent) Namespace() string { - return c.CommonComponentFields.Namespace -} - -// Enabled implements the IstioComponent interface. -func (c *EgressComponent) Enabled() bool { - // type assert is guaranteed to work in this context. - return c.componentSpec.(*v1alpha1.GatewaySpec).Enabled.GetValue() -} - -// runComponent performs startup tasks for the component defined by the given CommonComponentFields. -func runComponent(c *CommonComponentFields) error { - r := createHelmRenderer(c) - if err := r.Run(); err != nil { - return err - } - c.renderer = r - c.started = true - return nil -} - -// renderManifest renders the manifest for the component defined by c and returns the resulting string. -func renderManifest(c IstioComponent, cf *CommonComponentFields) (string, error) { - if !cf.started { - metrics.CountManifestRenderError(c.ComponentName(), metrics.RenderNotStartedError) - return "", fmt.Errorf("component %s not started in RenderManifest", cf.ComponentName) - } - - if !c.Enabled() { - return disabledYAMLStr(cf.ComponentName, cf.ResourceName), nil - } - - mergedYAML, err := cf.Translator.TranslateHelmValues(cf.InstallSpec, cf.componentSpec, cf.ComponentName) - if err != nil { - metrics.CountManifestRenderError(c.ComponentName(), metrics.HelmTranslateIOPToValuesError) - return "", err - } - - scope.Debugf("Merged values:\n%s\n", mergedYAML) - - my, err := cf.renderer.RenderManifestFiltered(mergedYAML, func(s string) bool { - return len(cf.Filter) == 0 || cf.Filter.Contains(s) - }) - if err != nil { - log.Errorf("Error rendering the manifest: %s", err) - metrics.CountManifestRenderError(c.ComponentName(), metrics.HelmChartRenderError) - return "", err - } - my += helm.YAMLSeparator + "\n" - scope.Debugf("Initial manifest with merged values:\n%s\n", my) - - // Add the k8s resources from IstioOperatorSpec. - my, err = cf.Translator.OverlayK8sSettings(my, cf.InstallSpec, cf.ComponentName, cf.ResourceName, cf.index) - if err != nil { - metrics.CountManifestRenderError(c.ComponentName(), metrics.K8SSettingsOverlayError) - return "", err - } - cnOutput := string(cf.ComponentName) - my = "# Resources for " + cnOutput + " component\n\n" + my - scope.Debugf("Manifest after k8s API settings:\n%s\n", my) - - // Add the k8s resource overlays from IstioOperatorSpec. - pathToK8sOverlay := fmt.Sprintf("Components.%s.", cf.ComponentName) - if cf.ComponentName == name.IngressComponentName || cf.ComponentName == name.EgressComponentName { - pathToK8sOverlay += fmt.Sprintf("%d.", cf.index) - } - - pathToK8sOverlay += "K8S.Overlays" - var overlays []*v1alpha1.K8SObjectOverlay - found, err := tpath.SetFromPath(cf.InstallSpec, pathToK8sOverlay, &overlays) - if err != nil { - return "", err - } - if !found { - scope.Debugf("Manifest after resources: \n%s\n", my) - metrics.CountManifestRender(cf.ComponentName) - return my, nil - } - kyo, err := yaml.Marshal(overlays) - if err != nil { - return "", err - } - scope.Infof("Applying Kubernetes overlay: \n%s\n", kyo) - ret, err := patch.YAMLManifestPatch(my, cf.Namespace, overlays) - if err != nil { - metrics.CountManifestRenderError(c.ComponentName(), metrics.K8SManifestPatchError) - return "", err - } - - scope.Debugf("Manifest after resources and overlay: \n%s\n", ret) - metrics.CountManifestRender(cf.ComponentName) - return ret, nil -} - -// createHelmRenderer creates a helm renderer for the component defined by c and returns a ptr to it. -// If a helm subdir is not found in ComponentMap translations, it is assumed to be "addon/. -func createHelmRenderer(c *CommonComponentFields) helm.TemplateRenderer { - iop := c.InstallSpec - cns := string(c.ComponentName) - helmSubdir := c.Translator.ComponentMap(cns).HelmSubdir - return helm.NewHelmRenderer(iop.InstallPackagePath, helmSubdir, cns, c.Namespace, c.Version) -} - -func isCoreComponentEnabled(c *CommonComponentFields) bool { - enabled, err := c.Translator.IsComponentEnabled(c.ComponentName, c.InstallSpec) - if err != nil { - return false - } - return enabled -} - -// disabledYAMLStr returns the YAML comment string that the given component is disabled. -func disabledYAMLStr(componentName name.ComponentName, resourceName string) string { - fullName := string(componentName) - if resourceName != "" { - fullName += " " + resourceName - } - return fmt.Sprintf("%s %s %s\n", yamlCommentStr, fullName, componentDisabledStr) -} diff --git a/operator/pkg/controller/controller.go b/operator/pkg/controller/controller.go deleted file mode 100644 index 1e579c2ba..000000000 --- a/operator/pkg/controller/controller.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "sigs.k8s.io/controller-runtime/pkg/manager" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/controller/istiocontrolplane" -) - -// AddToManager adds all Controllers to the Manager -func AddToManager(m manager.Manager, options *istiocontrolplane.Options) error { - return istiocontrolplane.Add(m, options) -} diff --git a/operator/pkg/controller/istiocontrolplane/istiocontrolplane_controller.go b/operator/pkg/controller/istiocontrolplane/istiocontrolplane_controller.go deleted file mode 100644 index 2221a6b7f..000000000 --- a/operator/pkg/controller/istiocontrolplane/istiocontrolplane_controller.go +++ /dev/null @@ -1,517 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istiocontrolplane - -import ( - "context" - "fmt" - "os" - "reflect" - "strings" -) - -import ( - "istio.io/api/operator/v1alpha1" - "istio.io/pkg/log" - "istio.io/pkg/version" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/sets" - kubeversion "k8s.io/apimachinery/pkg/version" - "k8s.io/client-go/rest" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/predicate" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio" - iopv1alpha1 "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/cache" - "github.com/apache/dubbo-go-pixiu/operator/pkg/helm" - "github.com/apache/dubbo-go-pixiu/operator/pkg/helmreconciler" - "github.com/apache/dubbo-go-pixiu/operator/pkg/metrics" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" - "github.com/apache/dubbo-go-pixiu/operator/pkg/tpath" - "github.com/apache/dubbo-go-pixiu/operator/pkg/translate" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/progress" - "github.com/apache/dubbo-go-pixiu/pkg/errdict" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/url" -) - -const ( - finalizer = "istio-finalizer.install.istio.io" - // finalizerMaxRetries defines the maximum number of attempts to remove the finalizer. - finalizerMaxRetries = 1 - // IgnoreReconcileAnnotation is annotation of IstioOperator CR so it would be ignored during Reconcile loop. - IgnoreReconcileAnnotation = "install.istio.io/ignoreReconcile" -) - -var ( - scope = log.RegisterScope("installer", "installer", 0) - restConfig *rest.Config -) - -type Options struct { - Force bool -} - -const ( - autoscalingV2MinK8SVersion = 23 - pdbV1MinK8SVersion = 21 -) - -// watchedResources contains all resources we will watch and reconcile when changed -// Ideally this would also contain Istio CRDs, but there is a race condition here - we cannot watch -// a type that does not yet exist. -func watchedResources(version *kubeversion.Info) []schema.GroupVersionKind { - res := []schema.GroupVersionKind{ - {Group: "apps", Version: "v1", Kind: name.DeploymentStr}, - {Group: "apps", Version: "v1", Kind: name.DaemonSetStr}, - {Group: "", Version: "v1", Kind: name.ServiceStr}, - // Endpoints should not be pruned because these are generated and not in the manifest. - // {Group: "", Version: "v1", Kind: name.EndpointStr}, - {Group: "", Version: "v1", Kind: name.CMStr}, - {Group: "", Version: "v1", Kind: name.PVCStr}, - {Group: "", Version: "v1", Kind: name.PodStr}, - {Group: "", Version: "v1", Kind: name.SecretStr}, - {Group: "", Version: "v1", Kind: name.SAStr}, - {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: name.RoleBindingStr}, - {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: name.RoleStr}, - {Group: "admissionregistration.k8s.io", Version: "v1", Kind: name.MutatingWebhookConfigurationStr}, - {Group: "admissionregistration.k8s.io", Version: "v1", Kind: name.ValidatingWebhookConfigurationStr}, - {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: name.ClusterRoleStr}, - {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: name.ClusterRoleBindingStr}, - {Group: "apiextensions.k8s.io", Version: "v1", Kind: name.CRDStr}, - } - // autoscaling v2 API is available on >=1.23 - if kube.IsKubeAtLeastOrLessThanVersion(version, autoscalingV2MinK8SVersion, true) { - res = append(res, schema.GroupVersionKind{Group: "autoscaling", Version: "v2", Kind: name.HPAStr}) - } else { - res = append(res, schema.GroupVersionKind{Group: "autoscaling", Version: "v2beta2", Kind: name.HPAStr}) - } - // policy/v1 is available on >=1.21 - if kube.IsKubeAtLeastOrLessThanVersion(version, pdbV1MinK8SVersion, true) { - res = append(res, schema.GroupVersionKind{Group: "policy", Version: "v1", Kind: name.PDBStr}) - } else { - res = append(res, schema.GroupVersionKind{Group: "policy", Version: "v1beta1", Kind: name.PDBStr}) - } - return res -} - -var ( - ownedResourcePredicates = predicate.Funcs{ - CreateFunc: func(_ event.CreateEvent) bool { - // no action - return false - }, - GenericFunc: func(_ event.GenericEvent) bool { - // no action - return false - }, - DeleteFunc: func(e event.DeleteEvent) bool { - obj, err := meta.Accessor(e.Object) - scope.Debugf("got delete event for %s.%s", obj.GetName(), obj.GetNamespace()) - if err != nil { - return false - } - unsObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(e.Object) - if err != nil { - return false - } - if isOperatorCreatedResource(obj) { - crName := obj.GetLabels()[helmreconciler.OwningResourceName] - crNamespace := obj.GetLabels()[helmreconciler.OwningResourceNamespace] - componentName := obj.GetLabels()[helmreconciler.IstioComponentLabelStr] - var host string - if restConfig != nil { - host = restConfig.Host - } - crHash := strings.Join([]string{crName, crNamespace, componentName, host}, "-") - oh := object.NewK8sObject(&unstructured.Unstructured{Object: unsObj}, nil, nil).Hash() - cache.RemoveObject(crHash, oh) - return true - } - return false - }, - UpdateFunc: func(e event.UpdateEvent) bool { - // no action - return false - }, - } - - operatorPredicates = predicate.Funcs{ - CreateFunc: func(e event.CreateEvent) bool { - return true - }, - DeleteFunc: func(e event.DeleteEvent) bool { - return true - }, - UpdateFunc: func(e event.UpdateEvent) bool { - oldIOP, ok := e.ObjectOld.(*iopv1alpha1.IstioOperator) - if !ok { - scope.Error(errdict.OperatorFailedToGetObjectInCallback, "failed to get old IstioOperator") - return false - } - newIOP := e.ObjectNew.(*iopv1alpha1.IstioOperator) - if !ok { - scope.Error(errdict.OperatorFailedToGetObjectInCallback, "failed to get new IstioOperator") - return false - } - if !reflect.DeepEqual(oldIOP.Spec, newIOP.Spec) || - oldIOP.GetDeletionTimestamp() != newIOP.GetDeletionTimestamp() || - oldIOP.GetGeneration() != newIOP.GetGeneration() { - return true - } - return false - }, - } -) - -// NewReconcileIstioOperator creates a new ReconcileIstioOperator and returns a ptr to it. -func NewReconcileIstioOperator(client client.Client, kubeClient kube.Client, scheme *runtime.Scheme) *ReconcileIstioOperator { - return &ReconcileIstioOperator{ - client: client, - kubeClient: kubeClient, - scheme: scheme, - } -} - -// ReconcileIstioOperator reconciles a IstioOperator object -type ReconcileIstioOperator struct { - // This client, initialized using mgr.Client() above, is a split client - // that reads objects from the cache and writes to the apiserver - client client.Client - kubeClient kube.Client - scheme *runtime.Scheme - options *Options -} - -// Reconcile reads that state of the cluster for a IstioOperator object and makes changes based on the state read -// and what is in the IstioOperator.Spec -// Note: -// The Controller will requeue the Request to be processed again if the returned error is non-nil or -// Result.Requeue is true, otherwise upon completion it will remove the work from the queue. -func (r *ReconcileIstioOperator) Reconcile(_ context.Context, request reconcile.Request) (reconcile.Result, error) { - scope.Info("Reconciling IstioOperator") - - ns, iopName := request.Namespace, request.Name - reqNamespacedName := types.NamespacedName{ - Name: request.Name, - Namespace: ns, - } - // declare read-only iop instance to create the reconciler - iop := &iopv1alpha1.IstioOperator{} - if err := r.client.Get(context.TODO(), reqNamespacedName, iop); err != nil { - if errors.IsNotFound(err) { - // Request object not found, could have been deleted after reconcile request. - // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. - // Return and don't requeue - metrics.CRDeletionTotal.Increment() - return reconcile.Result{}, nil - } - // Error reading the object - requeue the request. - scope.Warnf(errdict.OperatorFailedToGetObjectFromAPIServer, "error getting IstioOperator %s: %s", iopName, err) - metrics.CountCRFetchFail(errors.ReasonForError(err)) - return reconcile.Result{}, err - } - if iop.Spec == nil { - iop.Spec = &v1alpha1.IstioOperatorSpec{Profile: name.DefaultProfileName} - } - operatorRevision, _ := os.LookupEnv("REVISION") - if operatorRevision != "" && operatorRevision != iop.Spec.Revision { - scope.Infof("Ignoring IstioOperator CR %s with revision %s, since operator revision is %s.", iopName, iop.Spec.Revision, operatorRevision) - return reconcile.Result{}, nil - } - if iop.Annotations != nil { - if ir := iop.Annotations[IgnoreReconcileAnnotation]; ir == "true" { - scope.Infof("Ignoring the IstioOperator CR %s because it is annotated to be ignored for reconcile ", iopName) - return reconcile.Result{}, nil - } - } - - // for backward compatibility, the previous applied installed-state CR does not have the ignore reconcile annotation - // TODO(richardwxn): remove this check and rely on annotation check only - if strings.HasPrefix(iop.Name, name.InstalledSpecCRPrefix) { - scope.Infof("Ignoring the installed-state IstioOperator CR %s ", iopName) - return reconcile.Result{}, nil - } - - var err error - iopMerged := &iopv1alpha1.IstioOperator{} - *iopMerged = *iop - // get the merged values in iop on top of the defaults for the profile given by iop.profile - iopMerged.Spec, err = mergeIOPSWithProfile(iopMerged) - if err != nil { - scope.Errorf(errdict.OperatorFailedToMergeUserIOP, "failed to merge base profile with user IstioOperator CR %s, %s", iopName, err) - return reconcile.Result{}, err - } - - deleted := iop.GetDeletionTimestamp() != nil - finalizers := sets.NewString(iop.GetFinalizers()...) - if deleted { - if !finalizers.Has(finalizer) { - scope.Infof("IstioOperator %s deleted", iopName) - metrics.CRDeletionTotal.Increment() - return reconcile.Result{}, nil - } - scope.Infof("Deleting IstioOperator %s", iopName) - - reconciler, err := helmreconciler.NewHelmReconciler(r.client, r.kubeClient, iopMerged, nil) - if err != nil { - return reconcile.Result{}, err - } - if err := reconciler.Delete(); err != nil { - scope.Errorf("Failed to delete resources with helm reconciler: %s.", err) - return reconcile.Result{}, err - } - - finalizers.Delete(finalizer) - iop.SetFinalizers(finalizers.List()) - finalizerError := r.client.Update(context.TODO(), iop) - for retryCount := 0; errors.IsConflict(finalizerError) && retryCount < finalizerMaxRetries; retryCount++ { - scope.Info("API server conflict during finalizer removal, retrying.") - _ = r.client.Get(context.TODO(), request.NamespacedName, iop) - finalizers = sets.NewString(iop.GetFinalizers()...) - finalizers.Delete(finalizer) - iop.SetFinalizers(finalizers.List()) - finalizerError = r.client.Update(context.TODO(), iop) - } - if finalizerError != nil { - if errors.IsNotFound(finalizerError) { - scope.Infof("Did not remove finalizer from %s: the object was previously deleted.", iopName) - metrics.CRDeletionTotal.Increment() - return reconcile.Result{}, nil - } else if errors.IsConflict(finalizerError) { - scope.Infof("Could not remove finalizer from %s due to conflict. Operation will be retried in next reconcile attempt.", iopName) - return reconcile.Result{}, nil - } - scope.Errorf(errdict.OperatorFailedToRemoveFinalizer, "error removing finalizer: %s", finalizerError) - return reconcile.Result{}, finalizerError - } - return reconcile.Result{}, nil - } else if !finalizers.Has(finalizer) { - log.Infof("Adding finalizer %v to %v", finalizer, request) - finalizers.Insert(finalizer) - iop.SetFinalizers(finalizers.List()) - err := r.client.Update(context.TODO(), iop) - if err != nil { - if errors.IsNotFound(err) { - scope.Infof("Could not add finalizer to %s: the object was deleted.", iopName) - metrics.CRDeletionTotal.Increment() - return reconcile.Result{}, nil - } else if errors.IsConflict(err) { - scope.Infof("Could not add finalizer to %s due to conflict. Operation will be retried in next reconcile attempt.", iopName) - return reconcile.Result{}, nil - } - scope.Errorf(errdict.OperatorFailedToAddFinalizer, "Failed to add finalizer to IstioOperator CR %s: %s", iopName, err) - return reconcile.Result{}, err - } - } - - scope.Info("Updating IstioOperator") - val := iopMerged.Spec.Values.AsMap() - if _, ok := val["global"]; !ok { - val["global"] = make(map[string]interface{}) - } - globalValues := val["global"].(map[string]interface{}) - scope.Info("Detecting third-party JWT support") - var jwtPolicy util.JWTPolicy - if jwtPolicy, err = util.DetectSupportedJWTPolicy(r.kubeClient); err != nil { - // TODO(howardjohn): add to dictionary. When resolved, replace this sentence with Done or WontFix - if WontFix, add reason. - scope.Warnf("Failed to detect third-party JWT support: %v", err) - } else { - if jwtPolicy == util.FirstPartyJWT { - scope.Info("Detected that your cluster does not support third party JWT authentication. " + - "Falling back to less secure first party JWT. " + - "See " + url.ConfigureSAToken + " for details.") - } - globalValues["jwtPolicy"] = string(jwtPolicy) - } - err = util.ValidateIOPCAConfig(r.kubeClient, iopMerged) - if err != nil { - scope.Errorf(errdict.OperatorFailedToConfigure, "failed to apply IstioOperator resources. Error %s", err) - return reconcile.Result{}, err - } - helmReconcilerOptions := &helmreconciler.Options{ - Log: clog.NewDefaultLogger(), - ProgressLog: progress.NewLog(), - } - if r.options != nil { - helmReconcilerOptions.Force = r.options.Force - } - reconciler, err := helmreconciler.NewHelmReconciler(r.client, r.kubeClient, iopMerged, helmReconcilerOptions) - if err != nil { - return reconcile.Result{}, err - } - if err := reconciler.SetStatusBegin(); err != nil { - return reconcile.Result{}, err - } - status, err := reconciler.Reconcile() - if err != nil { - scope.Errorf("Error during reconcile: %s", err) - } - if err := reconciler.SetStatusComplete(status); err != nil { - return reconcile.Result{}, err - } - - return reconcile.Result{}, err -} - -// mergeIOPSWithProfile overlays the values in iop on top of the defaults for the profile given by iop.profile and -// returns the merged result. -func mergeIOPSWithProfile(iop *iopv1alpha1.IstioOperator) (*v1alpha1.IstioOperatorSpec, error) { - profileYAML, err := helm.GetProfileYAML(iop.Spec.InstallPackagePath, iop.Spec.Profile) - if err != nil { - metrics.CountCRMergeFail(metrics.CannotFetchProfileError) - return nil, err - } - - // Due to the fact that base profile is compiled in before a tag can be created, we must allow an additional - // override from variables that are set during release build time. - hub := version.DockerInfo.Hub - tag := version.DockerInfo.Tag - if hub != "" && hub != "unknown" && tag != "" && tag != "unknown" { - buildHubTagOverlayYAML, err := helm.GenerateHubTagOverlay(hub, tag) - if err != nil { - metrics.CountCRMergeFail(metrics.OverlayError) - return nil, err - } - profileYAML, err = util.OverlayYAML(profileYAML, buildHubTagOverlayYAML) - if err != nil { - metrics.CountCRMergeFail(metrics.OverlayError) - return nil, err - } - } - - overlayYAMLB, err := yaml.Marshal(iop) - if err != nil { - metrics.CountCRMergeFail(metrics.IOPFormatError) - return nil, err - } - overlayYAML := string(overlayYAMLB) - t := translate.NewReverseTranslator() - overlayYAML, err = t.TranslateK8SfromValueToIOP(overlayYAML) - if err != nil { - metrics.CountCRMergeFail(metrics.TranslateValuesError) - return nil, fmt.Errorf("could not overlay k8s settings from values to IOP: %s", err) - } - - mergedYAML, err := util.OverlayIOP(profileYAML, overlayYAML) - if err != nil { - metrics.CountCRMergeFail(metrics.OverlayError) - return nil, err - } - - mergedYAML, err = translate.OverlayValuesEnablement(mergedYAML, overlayYAML, "") - if err != nil { - metrics.CountCRMergeFail(metrics.TranslateValuesError) - return nil, err - } - - mergedYAMLSpec, err := tpath.GetSpecSubtree(mergedYAML) - if err != nil { - metrics.CountCRMergeFail(metrics.InternalYAMLParseError) - return nil, err - } - - return istio.UnmarshalAndValidateIOPS(mergedYAMLSpec) -} - -// Add creates a new IstioOperator Controller and adds it to the Manager. The Manager will set fields on the Controller -// and Start it when the Manager is Started. It also provides additional options to modify internal reconciler behavior. -func Add(mgr manager.Manager, options *Options) error { - restConfig = mgr.GetConfig() - kubeClient, err := kube.NewExtendedClient(kube.NewClientConfigForRestConfig(restConfig), "") - if err != nil { - return fmt.Errorf("create Kubernetes client: %v", err) - } - return add(mgr, &ReconcileIstioOperator{client: mgr.GetClient(), scheme: mgr.GetScheme(), kubeClient: kubeClient, options: options}) -} - -// add adds a new Controller to mgr with r as the reconcile.Reconciler -func add(mgr manager.Manager, r *ReconcileIstioOperator) error { - scope.Info("Adding controller for IstioOperator.") - // Create a new controller - c, err := controller.New("istiocontrolplane-controller", mgr, controller.Options{Reconciler: r}) - if err != nil { - return err - } - - // Watch for changes to primary resource IstioOperator - err = c.Watch(&source.Kind{Type: &iopv1alpha1.IstioOperator{}}, &handler.EnqueueRequestForObject{}, operatorPredicates) - if err != nil { - return err - } - ver, err := r.kubeClient.GetKubernetesVersion() - if err != nil { - return err - } - // watch for changes to Istio resources - err = watchIstioResources(c, ver) - if err != nil { - return err - } - scope.Info("Controller added") - return nil -} - -// Watch changes for Istio resources managed by the operator -func watchIstioResources(c controller.Controller, ver *kubeversion.Info) error { - for _, t := range watchedResources(ver) { - u := &unstructured.Unstructured{} - u.SetGroupVersionKind(schema.GroupVersionKind{ - Kind: t.Kind, - Group: t.Group, - Version: t.Version, - }) - err := c.Watch(&source.Kind{Type: u}, handler.EnqueueRequestsFromMapFunc(func(a client.Object) []reconcile.Request { - scope.Infof("Watching a change for istio resource: %s/%s", a.GetNamespace(), a.GetName()) - return []reconcile.Request{ - {NamespacedName: types.NamespacedName{ - Name: a.GetLabels()[helmreconciler.OwningResourceName], - Namespace: a.GetLabels()[helmreconciler.OwningResourceNamespace], - }}, - } - }), - ownedResourcePredicates) - if err != nil { - scope.Errorf("Could not create watch for %s/%s/%s: %s.", t.Kind, t.Group, t.Version, err) - } - } - return nil -} - -// Check if the specified object is created by operator -func isOperatorCreatedResource(obj metav1.Object) bool { - return obj.GetLabels()[helmreconciler.OwningResourceName] != "" && - obj.GetLabels()[helmreconciler.OwningResourceNamespace] != "" && - obj.GetLabels()[helmreconciler.IstioComponentLabelStr] != "" -} diff --git a/operator/pkg/controlplane/control_plane.go b/operator/pkg/controlplane/control_plane.go deleted file mode 100644 index 273b88288..000000000 --- a/operator/pkg/controlplane/control_plane.go +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controlplane - -import ( - "fmt" - "sort" -) - -import ( - "istio.io/api/operator/v1alpha1" - "k8s.io/apimachinery/pkg/version" -) - -import ( - iop "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/component" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/translate" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// IstioControlPlane is an installation of an Istio control plane. -type IstioControlPlane struct { - // components is a slice of components that are part of the feature. - components []component.IstioComponent - started bool -} - -// NewIstioControlPlane creates a new IstioControlPlane and returns a pointer to it. -func NewIstioControlPlane( - installSpec *v1alpha1.IstioOperatorSpec, - translator *translate.Translator, - filter []string, - ver *version.Info, -) (*IstioControlPlane, error) { - out := &IstioControlPlane{} - opts := &component.Options{ - InstallSpec: installSpec, - Translator: translator, - Filter: sets.New(filter...), - Version: ver, - } - for _, c := range name.AllCoreComponentNames { - o := *opts - ns, err := name.Namespace(c, installSpec) - if err != nil { - return nil, err - } - o.Namespace = ns - out.components = append(out.components, component.NewCoreComponent(c, &o)) - } - - if installSpec.Components != nil { - for idx, c := range installSpec.Components.IngressGateways { - o := *opts - o.Namespace = defaultIfEmpty(c.Namespace, iop.Namespace(installSpec)) - out.components = append(out.components, component.NewIngressComponent(c.Name, idx, c, &o)) - } - for idx, c := range installSpec.Components.EgressGateways { - o := *opts - o.Namespace = defaultIfEmpty(c.Namespace, iop.Namespace(installSpec)) - out.components = append(out.components, component.NewEgressComponent(c.Name, idx, c, &o)) - } - } - return out, nil -} - -func orderedKeys(m map[string]*v1alpha1.ExternalComponentSpec) []string { - var keys []string - for k := range m { - keys = append(keys, k) - } - sort.Strings(keys) - return keys -} - -func defaultIfEmpty(val, dflt string) string { - if val == "" { - return dflt - } - return val -} - -// Run starts the Istio control plane. -func (i *IstioControlPlane) Run() error { - for _, c := range i.components { - if err := c.Run(); err != nil { - return err - } - } - i.started = true - return nil -} - -// RenderManifest returns a manifest rendered against -func (i *IstioControlPlane) RenderManifest() (manifests name.ManifestMap, errsOut util.Errors) { - if !i.started { - return nil, util.NewErrs(fmt.Errorf("istioControlPlane must be Run before calling RenderManifest")) - } - - manifests = make(name.ManifestMap) - for _, c := range i.components { - ms, err := c.RenderManifest() - errsOut = util.AppendErr(errsOut, err) - manifests[c.ComponentName()] = append(manifests[c.ComponentName()], ms) - } - if len(errsOut) > 0 { - return nil, errsOut - } - return -} - -// componentsEqual reports whether the given components are equal to those in i. -func (i *IstioControlPlane) componentsEqual(components []component.IstioComponent) bool { - if i.components == nil && components == nil { - return true - } - if len(i.components) != len(components) { - return false - } - for c := 0; c < len(i.components); c++ { - if i.components[c].ComponentName() != components[c].ComponentName() { - return false - } - if i.components[c].Namespace() != components[c].Namespace() { - return false - } - if i.components[c].Enabled() != components[c].Enabled() { - return false - } - if i.components[c].ResourceName() != components[c].ResourceName() { - return false - } - } - return true -} diff --git a/operator/pkg/controlplane/control_plane_test.go b/operator/pkg/controlplane/control_plane_test.go deleted file mode 100644 index e46d729cc..000000000 --- a/operator/pkg/controlplane/control_plane_test.go +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controlplane - -import ( - "fmt" - "reflect" - "testing" -) - -import ( - "istio.io/api/operator/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/component" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/translate" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -func TestOrderedKeys(t *testing.T) { - tests := []struct { - desc string - in map[string]*v1alpha1.ExternalComponentSpec - want []string - }{ - { - desc: "not-ordered", - in: map[string]*v1alpha1.ExternalComponentSpec{ - "graphql": nil, - "Abacus": nil, - "Astrology": nil, - "gRPC": nil, - "blackjack": nil, - }, - want: []string{ - "Abacus", - "Astrology", - "blackjack", - "gRPC", - "graphql", - }, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got := orderedKeys(tt.in); !(reflect.DeepEqual(got, tt.want)) { - t.Errorf("%s: got %+v want %+v", tt.desc, got, tt.want) - } - }) - } -} - -func TestNewIstioOperator(t *testing.T) { - coreComponentOptions := &component.Options{ - InstallSpec: &v1alpha1.IstioOperatorSpec{}, - Translator: &translate.Translator{}, - } - tests := []struct { - desc string - inInstallSpec *v1alpha1.IstioOperatorSpec - inTranslator *translate.Translator - wantIstioOperator *IstioControlPlane - wantErr error - }{ - { - desc: "core-components", - inInstallSpec: &v1alpha1.IstioOperatorSpec{}, - inTranslator: &translate.Translator{ - ComponentMaps: map[name.ComponentName]*translate.ComponentMaps{ - "Pilot": { - ResourceName: "test-resource", - }, - }, - }, - wantErr: nil, - wantIstioOperator: &IstioControlPlane{ - components: []component.IstioComponent{ - &component.BaseComponent{ - CommonComponentFields: &component.CommonComponentFields{ - Options: coreComponentOptions, - ComponentName: name.IstioBaseComponentName, - }, - }, - &component.PilotComponent{ - CommonComponentFields: &component.CommonComponentFields{ - Options: coreComponentOptions, - ResourceName: "test-resource", - ComponentName: name.PilotComponentName, - }, - }, - &component.CNIComponent{ - CommonComponentFields: &component.CommonComponentFields{ - ComponentName: name.CNIComponentName, - Options: coreComponentOptions, - }, - }, - &component.IstiodRemoteComponent{ - CommonComponentFields: &component.CommonComponentFields{ - ComponentName: name.IstiodRemoteComponentName, - Options: coreComponentOptions, - }, - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - gotOperator, err := NewIstioControlPlane(tt.inInstallSpec, tt.inTranslator, nil, nil) - if ((err != nil && tt.wantErr == nil) || (err == nil && tt.wantErr != nil)) || !gotOperator.componentsEqual(tt.wantIstioOperator.components) { - t.Errorf("%s: wanted components & err %+v %v, got components & err %+v %v", - tt.desc, tt.wantIstioOperator.components, tt.wantErr, gotOperator.components, err) - } - }) - } -} - -func TestIstioOperator_RenderManifest(t *testing.T) { - coreComponentOptions := &component.Options{ - InstallSpec: &v1alpha1.IstioOperatorSpec{}, - Translator: &translate.Translator{}, - } - tests := []struct { - desc string - testOperator *IstioControlPlane - wantManifests name.ManifestMap - wantErrs util.Errors - }{ - { - desc: "components-not-started-operator-started", - testOperator: &IstioControlPlane{ - components: []component.IstioComponent{ - &component.BaseComponent{ - CommonComponentFields: &component.CommonComponentFields{ - Options: coreComponentOptions, - ComponentName: name.IstioBaseComponentName, - }, - }, - &component.PilotComponent{ - CommonComponentFields: &component.CommonComponentFields{ - Options: &component.Options{ - InstallSpec: &v1alpha1.IstioOperatorSpec{}, - Translator: &translate.Translator{}, - }, - ResourceName: "test-resource", - ComponentName: name.PilotComponentName, - }, - }, - &component.CNIComponent{ - CommonComponentFields: &component.CommonComponentFields{ - ComponentName: name.CNIComponentName, - Options: coreComponentOptions, - }, - }, - }, - started: true, - }, - wantManifests: map[name.ComponentName][]string{}, - wantErrs: []error{ - fmt.Errorf("component Base not started in RenderManifest"), - fmt.Errorf("component Pilot not started in RenderManifest"), - fmt.Errorf("component Cni not started in RenderManifest"), - }, - }, - { - desc: "operator-not-started", - testOperator: &IstioControlPlane{ - components: []component.IstioComponent{ - &component.BaseComponent{ - CommonComponentFields: &component.CommonComponentFields{ - Options: coreComponentOptions, - ComponentName: name.IstioBaseComponentName, - }, - }, - &component.PilotComponent{ - CommonComponentFields: &component.CommonComponentFields{ - Options: &component.Options{ - InstallSpec: &v1alpha1.IstioOperatorSpec{}, - Translator: &translate.Translator{}, - }, - ResourceName: "test-resource", - ComponentName: name.PilotComponentName, - }, - }, - &component.CNIComponent{ - CommonComponentFields: &component.CommonComponentFields{ - ComponentName: name.CNIComponentName, - Options: coreComponentOptions, - }, - }, - }, - started: false, - }, - wantManifests: map[name.ComponentName][]string{}, - wantErrs: []error{ - fmt.Errorf("istioControlPlane must be Run before calling RenderManifest"), - }, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - gotManifests, gotErrs := tt.testOperator.RenderManifest() - if !reflect.DeepEqual(gotManifests, tt.wantManifests) || !reflect.DeepEqual(gotErrs, tt.wantErrs) { - // reflect.DeepEqual returns false on size 0 maps - if !(len(gotManifests) == 0) && (len(tt.wantManifests) == 0) { - t.Errorf("%s: expected manifest map %+v errs %+v, got manifest map %+v errs %+v", - tt.desc, tt.wantManifests, tt.wantErrs, gotManifests, gotErrs) - } - } - }) - } -} diff --git a/operator/pkg/helm/fs_renderer_test.go b/operator/pkg/helm/fs_renderer_test.go deleted file mode 100644 index 9279fd3d2..000000000 --- a/operator/pkg/helm/fs_renderer_test.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package helm - -import ( - "errors" - "os" - "testing" -) - -import ( - "helm.sh/helm/v3/pkg/chart" -) - -func TestRenderManifest(t *testing.T) { - tests := []struct { - desc string - inValues string - inChart chart.Chart - startRender bool - inPath string - objFileTemplateReader Renderer - wantResult string - wantErr error - }{ - { - desc: "not-started", - inValues: "", - startRender: false, - inChart: chart.Chart{}, - objFileTemplateReader: Renderer{}, - wantResult: "", - wantErr: errors.New("fileTemplateRenderer for not started in renderChart"), - }, - { - desc: "started-random-template", - inValues: ` -description: test -`, - inPath: "testdata/render/Chart.yaml", - startRender: true, - objFileTemplateReader: Renderer{ - namespace: "name-space", - componentName: "foo-component", - dir: "testdata/render", - files: os.DirFS("."), - }, - wantResult: `apiVersion: v1 -description: test -name: addon -version: 1.1.0 -appVersion: 1.1.0 -tillerVersion: ">=2.7.2" -keywords: - - istio-addon - ---- -`, - wantErr: nil, - }, - { - desc: "bad-file-path", - inValues: "", - inPath: "foo/bar/Chart.yaml", - startRender: true, - objFileTemplateReader: Renderer{ - namespace: "name-space", - componentName: "foo-component", - dir: "foo/bar", - files: os.DirFS("."), - }, - wantResult: "", - wantErr: errors.New(`component "foo-component" does not exist`), - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if tt.startRender { - err := tt.objFileTemplateReader.Run() - if err != nil && tt.wantErr != nil { - if err.Error() != tt.wantErr.Error() { - t.Errorf("%s: expected err: %q got %q", tt.desc, tt.wantErr.Error(), err.Error()) - } - } - } - if res, err := tt.objFileTemplateReader.RenderManifest(tt.inValues); res != tt.wantResult || - ((tt.wantErr != nil && err == nil) || (tt.wantErr == nil && err != nil)) { - t.Errorf("%s: \nexpected vals: \n%v\n\nexpected err:%v\ngot vals:\n%v\n\n got err %v", - tt.desc, tt.wantResult, tt.wantErr, res, err) - } - }) - } -} diff --git a/operator/pkg/helm/helm.go b/operator/pkg/helm/helm.go deleted file mode 100644 index dd00e155a..000000000 --- a/operator/pkg/helm/helm.go +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package helm - -import ( - "fmt" - "os" - "path/filepath" - "sort" - "strings" -) - -import ( - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/engine" - "istio.io/pkg/log" - "k8s.io/apimachinery/pkg/version" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/manifests" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -const ( - // YAMLSeparator is a separator for multi-document YAML files. - YAMLSeparator = "\n---\n" - - // DefaultProfileString is the name of the default profile. - DefaultProfileString = "default" - - // NotesFileNameSuffix is the file name suffix for helm notes. - // see https://helm.sh/docs/chart_template_guide/notes_files/ - NotesFileNameSuffix = ".txt" -) - -var scope = log.RegisterScope("installer", "installer", 0) - -// TemplateFilterFunc filters templates to render by their file name -type TemplateFilterFunc func(string) bool - -// TemplateRenderer defines a helm template renderer interface. -type TemplateRenderer interface { - // Run starts the renderer and should be called before using it. - Run() error - // RenderManifest renders the associated helm charts with the given values YAML string and returns the resulting - // string. - RenderManifest(values string) (string, error) - // RenderManifestFiltered filters manifests to render by template file name - RenderManifestFiltered(values string, filter TemplateFilterFunc) (string, error) -} - -// NewHelmRenderer creates a new helm renderer with the given parameters and returns an interface to it. -// The format of helmBaseDir and profile strings determines the type of helm renderer returned (compiled-in, file, -// HTTP etc.) -func NewHelmRenderer(operatorDataDir, helmSubdir, componentName, namespace string, version *version.Info) TemplateRenderer { - dir := strings.Join([]string{ChartsSubdirName, helmSubdir}, "/") - return NewGenericRenderer(manifests.BuiltinOrDir(operatorDataDir), dir, componentName, namespace, version) -} - -// ReadProfileYAML reads the YAML values associated with the given profile. It uses an appropriate reader for the -// profile format (compiled-in, file, HTTP, etc.). -func ReadProfileYAML(profile, manifestsPath string) (string, error) { - var err error - var globalValues string - - // Get global values from profile. - switch { - case util.IsFilePath(profile): - if globalValues, err = readFile(profile); err != nil { - return "", err - } - default: - if globalValues, err = LoadValues(profile, manifestsPath); err != nil { - return "", fmt.Errorf("failed to read profile %v from %v: %v", profile, manifestsPath, err) - } - } - - return globalValues, nil -} - -// renderChart renders the given chart with the given values and returns the resulting YAML manifest string. -func renderChart(namespace, values string, chrt *chart.Chart, filterFunc TemplateFilterFunc, version *version.Info) (string, error) { - options := chartutil.ReleaseOptions{ - Name: "istio", - Namespace: namespace, - } - valuesMap := map[string]interface{}{} - if err := yaml.Unmarshal([]byte(values), &valuesMap); err != nil { - return "", fmt.Errorf("failed to unmarshal values: %v", err) - } - - caps := *chartutil.DefaultCapabilities - if version != nil { - caps.KubeVersion = chartutil.KubeVersion{ - Version: version.GitVersion, - Major: version.Major, - Minor: version.Minor, - } - } - vals, err := chartutil.ToRenderValues(chrt, valuesMap, options, &caps) - if err != nil { - return "", err - } - - if filterFunc != nil { - filteredTemplates := []*chart.File{} - for _, t := range chrt.Templates { - if filterFunc(t.Name) { - filteredTemplates = append(filteredTemplates, t) - } - } - chrt.Templates = filteredTemplates - } - - files, err := engine.Render(chrt, vals) - crdFiles := chrt.CRDObjects() - if err != nil { - return "", err - } - if chrt.Metadata.Name == "base" { - base, _ := valuesMap["base"].(map[string]interface{}) - if enableIstioConfigCRDs, ok := base["enableIstioConfigCRDs"].(bool); ok && !enableIstioConfigCRDs { - crdFiles = []chart.CRD{} - } - } - - // Create sorted array of keys to iterate over, to stabilize the order of the rendered templates - keys := make([]string, 0, len(files)) - for k := range files { - if strings.HasSuffix(k, NotesFileNameSuffix) { - continue - } - keys = append(keys, k) - } - sort.Strings(keys) - - var sb strings.Builder - for i := 0; i < len(keys); i++ { - f := files[keys[i]] - // add yaml separator if the rendered file doesn't have one at the end - f = strings.TrimSpace(f) + "\n" - if !strings.HasSuffix(f, YAMLSeparator) { - f += YAMLSeparator - } - _, err := sb.WriteString(f) - if err != nil { - return "", err - } - } - - // Sort crd files by name to ensure stable manifest output - sort.Slice(crdFiles, func(i, j int) bool { return crdFiles[i].Name < crdFiles[j].Name }) - for _, crdFile := range crdFiles { - f := string(crdFile.File.Data) - // add yaml separator if the rendered file doesn't have one at the end - f = strings.TrimSpace(f) + "\n" - if !strings.HasSuffix(f, YAMLSeparator) { - f += YAMLSeparator - } - _, err := sb.WriteString(f) - if err != nil { - return "", err - } - } - - return sb.String(), nil -} - -// GenerateHubTagOverlay creates an IstioOperatorSpec overlay YAML for hub and tag. -func GenerateHubTagOverlay(hub, tag string) (string, error) { - hubTagYAMLTemplate := ` -spec: - hub: {{.Hub}} - tag: {{.Tag}} -` - ts := struct { - Hub string - Tag string - }{ - Hub: hub, - Tag: tag, - } - return util.RenderTemplate(hubTagYAMLTemplate, ts) -} - -// DefaultFilenameForProfile returns the profile name of the default profile for the given profile. -func DefaultFilenameForProfile(profile string) string { - switch { - case util.IsFilePath(profile): - return filepath.Join(filepath.Dir(profile), DefaultProfileFilename) - default: - return DefaultProfileString - } -} - -// IsDefaultProfile reports whether the given profile is the default profile. -func IsDefaultProfile(profile string) bool { - return profile == "" || profile == DefaultProfileString || filepath.Base(profile) == DefaultProfileFilename -} - -func readFile(path string) (string, error) { - b, err := os.ReadFile(path) - return string(b), err -} - -// GetProfileYAML returns the YAML for the given profile name, using the given profileOrPath string, which may be either -// a profile label or a file path. -func GetProfileYAML(installPackagePath, profileOrPath string) (string, error) { - if profileOrPath == "" { - profileOrPath = "default" - } - profiles, err := readProfiles(installPackagePath) - if err != nil { - return "", fmt.Errorf("failed to read profiles: %v", err) - } - // If charts are a file path and profile is a name like default, transform it to the file path. - if profiles[profileOrPath] && installPackagePath != "" { - profileOrPath = filepath.Join(installPackagePath, "profiles", profileOrPath+".yaml") - } - // This contains the IstioOperator CR. - baseCRYAML, err := ReadProfileYAML(profileOrPath, installPackagePath) - if err != nil { - return "", err - } - - if !IsDefaultProfile(profileOrPath) { - // Profile definitions are relative to the default profileOrPath, so read that first. - dfn := DefaultFilenameForProfile(profileOrPath) - defaultYAML, err := ReadProfileYAML(dfn, installPackagePath) - if err != nil { - return "", err - } - baseCRYAML, err = util.OverlayIOP(defaultYAML, baseCRYAML) - if err != nil { - return "", err - } - } - - return baseCRYAML, nil -} diff --git a/operator/pkg/helm/renderer.go b/operator/pkg/helm/renderer.go deleted file mode 100644 index 056885a44..000000000 --- a/operator/pkg/helm/renderer.go +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package helm - -import ( - "fmt" - "io/fs" - "os" - "path/filepath" - "strings" -) - -import ( - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" - "k8s.io/apimachinery/pkg/version" -) - -import ( - "github.com/apache/dubbo-go-pixiu/manifests" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -const ( - // DefaultProfileFilename is the name of the default profile yaml file. - DefaultProfileFilename = "default.yaml" - ChartsSubdirName = "charts" - profilesRoot = "profiles" -) - -// Renderer is a helm template renderer for a fs.FS. -type Renderer struct { - namespace string - componentName string - chart *chart.Chart - started bool - files fs.FS - dir string - // Kubernetes cluster version - version *version.Info -} - -// NewFileTemplateRenderer creates a TemplateRenderer with the given parameters and returns a pointer to it. -// helmChartDirPath must be an absolute file path to the root of the helm charts. -func NewGenericRenderer(files fs.FS, dir, componentName, namespace string, version *version.Info) *Renderer { - return &Renderer{ - namespace: namespace, - componentName: componentName, - dir: dir, - files: files, - version: version, - } -} - -// Run implements the TemplateRenderer interface. -func (h *Renderer) Run() error { - if err := h.loadChart(); err != nil { - return err - } - - h.started = true - return nil -} - -// RenderManifest renders the current helm templates with the current values and returns the resulting YAML manifest string. -func (h *Renderer) RenderManifest(values string) (string, error) { - if !h.started { - return "", fmt.Errorf("fileTemplateRenderer for %s not started in renderChart", h.componentName) - } - return renderChart(h.namespace, values, h.chart, nil, h.version) -} - -// RenderManifestFiltered filters templates to render using the supplied filter function. -func (h *Renderer) RenderManifestFiltered(values string, filter TemplateFilterFunc) (string, error) { - if !h.started { - return "", fmt.Errorf("fileTemplateRenderer for %s not started in renderChart", h.componentName) - } - return renderChart(h.namespace, values, h.chart, filter, h.version) -} - -func GetFilesRecursive(f fs.FS, root string) ([]string, error) { - res := []string{} - err := fs.WalkDir(f, root, func(path string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - if d.IsDir() { - return nil - } - res = append(res, path) - return nil - }) - return res, err -} - -// loadChart implements the TemplateRenderer interface. -func (h *Renderer) loadChart() error { - fnames, err := GetFilesRecursive(h.files, h.dir) - if err != nil { - if os.IsNotExist(err) { - return fmt.Errorf("component %q does not exist", h.componentName) - } - return fmt.Errorf("list files: %v", err) - } - var bfs []*loader.BufferedFile - for _, fname := range fnames { - b, err := fs.ReadFile(h.files, fname) - if err != nil { - return fmt.Errorf("read file: %v", err) - } - // Helm expects unix / separator, but on windows this will be \ - name := strings.ReplaceAll(stripPrefix(fname, h.dir), string(filepath.Separator), "/") - bf := &loader.BufferedFile{ - Name: name, - Data: b, - } - bfs = append(bfs, bf) - scope.Debugf("Chart loaded: %s", bf.Name) - } - - h.chart, err = loader.LoadFiles(bfs) - if err != nil { - return fmt.Errorf("load files: %v", err) - } - return nil -} - -func builtinProfileToFilename(name string) string { - if name == "" { - return DefaultProfileFilename - } - return name + ".yaml" -} - -func LoadValues(profileName string, chartsDir string) (string, error) { - path := strings.Join([]string{profilesRoot, builtinProfileToFilename(profileName)}, "/") - by, err := fs.ReadFile(manifests.BuiltinOrDir(chartsDir), path) - if err != nil { - return "", err - } - return string(by), nil -} - -func readProfiles(chartsDir string) (map[string]bool, error) { - profiles := map[string]bool{} - f := manifests.BuiltinOrDir(chartsDir) - dir, err := fs.ReadDir(f, profilesRoot) - if err != nil { - return nil, err - } - for _, f := range dir { - trimmedString := strings.TrimSuffix(f.Name(), ".yaml") - if f.Name() != trimmedString { - profiles[trimmedString] = true - } - } - return profiles, nil -} - -// stripPrefix removes the the given prefix from prefix. -func stripPrefix(path, prefix string) string { - pl := len(strings.Split(prefix, "/")) - pv := strings.Split(path, "/") - return strings.Join(pv[pl:], "/") -} - -// list all the profiles. -func ListProfiles(charts string) ([]string, error) { - profiles, err := readProfiles(charts) - if err != nil { - return nil, err - } - return util.StringBoolMapToSlice(profiles), nil -} diff --git a/operator/pkg/helm/testdata/addons/a/Chart.yaml b/operator/pkg/helm/testdata/addons/a/Chart.yaml deleted file mode 100644 index c03e3c6e5..000000000 --- a/operator/pkg/helm/testdata/addons/a/Chart.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: addon -version: 1.1.0 -appVersion: 1.1.0 -tillerVersion: ">=2.7.2" -keywords: - - istio-addon diff --git a/operator/pkg/helm/testdata/addons/invalid/a/Chart.yaml b/operator/pkg/helm/testdata/addons/invalid/a/Chart.yaml deleted file mode 100644 index c03e3c6e5..000000000 --- a/operator/pkg/helm/testdata/addons/invalid/a/Chart.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: addon -version: 1.1.0 -appVersion: 1.1.0 -tillerVersion: ">=2.7.2" -keywords: - - istio-addon diff --git a/operator/pkg/helm/testdata/addons/invalid/b/Chart.yaml b/operator/pkg/helm/testdata/addons/invalid/b/Chart.yaml deleted file mode 100644 index c03e3c6e5..000000000 --- a/operator/pkg/helm/testdata/addons/invalid/b/Chart.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: addon -version: 1.1.0 -appVersion: 1.1.0 -tillerVersion: ">=2.7.2" -keywords: - - istio-addon diff --git a/operator/pkg/helm/testdata/istio-1.3.0-linux.tar.gz b/operator/pkg/helm/testdata/istio-1.3.0-linux.tar.gz deleted file mode 100644 index e1ee4ff11..000000000 Binary files a/operator/pkg/helm/testdata/istio-1.3.0-linux.tar.gz and /dev/null differ diff --git a/operator/pkg/helm/testdata/render/Chart.yaml b/operator/pkg/helm/testdata/render/Chart.yaml deleted file mode 100644 index 0cff1f683..000000000 --- a/operator/pkg/helm/testdata/render/Chart.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: addon -version: 1.1.0 -appVersion: 1.1.0 -tillerVersion: ">=2.7.2ffff" -keywords: - - istio-addon diff --git a/operator/pkg/helm/testdata/render/templates/fs_template.yaml b/operator/pkg/helm/testdata/render/templates/fs_template.yaml deleted file mode 100644 index bf7918724..000000000 --- a/operator/pkg/helm/testdata/render/templates/fs_template.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -description: {{.Values.description}} -name: addon -version: 1.1.0 -appVersion: 1.1.0 -tillerVersion: ">=2.7.2" -keywords: - - istio-addon \ No newline at end of file diff --git a/operator/pkg/helm/urlfetcher.go b/operator/pkg/helm/urlfetcher.go deleted file mode 100644 index 5306df468..000000000 --- a/operator/pkg/helm/urlfetcher.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package helm - -import ( - "fmt" - "net/url" - "os" - "path" - "path/filepath" - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/httprequest" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/tgz" - "github.com/apache/dubbo-go-pixiu/operator/pkg/version" -) - -const ( - // InstallationDirectory is temporary folder name for caching downloaded installation packages. - InstallationDirectory = "istio-install-packages" - // OperatorSubdirFilePath is file path of installation packages to helm charts. - OperatorSubdirFilePath = "manifests" - // OperatorSubdirFilePath15 is the file path of installation packages to helm charts for 1.5 and earlier. - // TODO: remove in 1.7. - OperatorSubdirFilePath15 = "install/kubernetes/operator" -) - -// URLFetcher is used to fetch and manipulate charts from remote url -type URLFetcher struct { - // url is the source URL where release tar is downloaded from. It should be in the form https://.../istio-{version}-{platform}.tar.gz - // i.e. the full URL path to the Istio release tar. - url string - // destDirRoot is the root dir where charts are downloaded and extracted. If set to "", the destination dir will be - // set to the default value, which is static for caching purposes. - destDirRoot string -} - -// NewURLFetcher creates an URLFetcher pointing to installation package URL and destination dir to extract it into, -// and returns a pointer to it. -// url is the source URL where release tar is downloaded from. It should be in the form https://.../istio-{version}-{platform}.tar.gz -// i.e. the full URL path to the Istio release tar. -// destDirRoot is the root dir where charts are downloaded and extracted. If set to "", the destination dir will be set -// to the default value, which is static for caching purposes. -func NewURLFetcher(url string, destDirRoot string) *URLFetcher { - if destDirRoot == "" { - destDirRoot = filepath.Join(os.TempDir(), InstallationDirectory) - } - return &URLFetcher{ - url: url, - destDirRoot: destDirRoot, - } -} - -// DestDir returns path of destination dir that the tar was extracted to. -func (f *URLFetcher) DestDir() string { - // checked for error during download. - subdir, _, _ := URLToDirname(f.url) - return filepath.Join(f.destDirRoot, subdir) -} - -// Fetch fetches and untars the charts. -func (f *URLFetcher) Fetch() error { - if _, _, err := URLToDirname(f.url); err != nil { - return err - } - saved, err := DownloadTo(f.url, f.destDirRoot) - if err != nil { - return err - } - - reader, err := os.Open(saved) - if err != nil { - return err - } - defer reader.Close() - - return tgz.Extract(reader, f.destDirRoot) -} - -// DownloadTo downloads from remote srcURL to dest local file path -func DownloadTo(srcURL, dest string) (string, error) { - u, err := url.Parse(srcURL) - if err != nil { - return "", fmt.Errorf("invalid chart URL: %s", srcURL) - } - data, err := httprequest.Get(u.String()) - if err != nil { - return "", err - } - - name := filepath.Base(u.Path) - destFile := filepath.Join(dest, name) - dir := filepath.Dir(destFile) - if _, err := os.Stat(dir); os.IsNotExist(err) { - err := os.Mkdir(dir, 0o755) - if err != nil { - return "", err - } - } - - if err := os.WriteFile(destFile, data, 0o644); err != nil { - return destFile, err - } - - return destFile, nil -} - -// URLToDirname, given an input URL pointing to an Istio release tar, returns the subdirectory name that the tar would -// be extracted to and the version in the URL. The input URLs are expected to have the form -// https://.../istio-{version}-{platform}[optional suffix].tar.gz. -func URLToDirname(url string) (string, *version.Version, error) { - fn := path.Base(url) - fv := strings.Split(fn, "-") - fvl := len(fv) - if fvl < 2 || fv[0] != "istio" { - return "", nil, fmt.Errorf("wrong format for release tar name, got: %s, expect https://.../istio-{version}-{platform}.tar.gz", url) - } - ver, err := version.NewVersionFromString(fv[1]) - if err != nil { - return "", nil, err - } - // get rid of the suffix like -linux-arch or -osx - if fv[fvl-2] == "linux" { - // linux names also have arch appended, trim this off - fv = fv[:fvl-1] - } - - return strings.Join(fv[:len(fv)-1], "-"), ver, nil -} diff --git a/operator/pkg/helm/urlfetcher_test.go b/operator/pkg/helm/urlfetcher_test.go deleted file mode 100644 index 4cbbc0391..000000000 --- a/operator/pkg/helm/urlfetcher_test.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package helm - -import ( - "net/http" - "net/http/httptest" - "os" - "path/filepath" - "strings" - "testing" -) - -func TestFetch(t *testing.T) { - tests := []struct { - name string - installationPackageName string - }{ - { - name: "Charts download only", - installationPackageName: "istio-1.3.0-linux.tar.gz", - }, - { - name: "Charts download only in folders", - installationPackageName: "testdata/istio-1.3.0-linux.tar.gz", - }, - } - - server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - if !strings.HasSuffix(r.URL.Path, ".tar.gz") { - http.NotFound(rw, r) - return - } - http.ServeFile(rw, r, "testdata/istio-1.3.0-linux.tar.gz") - })) - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tmp := t.TempDir() - rootDir := tmp + "/testout" - fq := NewURLFetcher(server.URL+"/"+tt.installationPackageName, rootDir) - - err := fq.Fetch() - if err != nil { - t.Error(err) - return - } - - ef := filepath.Join(rootDir, filepath.Base(tt.installationPackageName)) - if _, err := os.Stat(ef); err != nil { - t.Error(err) - return - } - }) - } -} diff --git a/operator/pkg/helm/vfs_renderer_test.go b/operator/pkg/helm/vfs_renderer_test.go deleted file mode 100644 index 6c870bff6..000000000 --- a/operator/pkg/helm/vfs_renderer_test.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package helm - -import ( - "testing" -) - -func TestStripPrefix(t *testing.T) { - cases := []struct { - in string - prefix string - out string - }{ - {"foo/bar", "foo", "bar"}, - {"a/b/c/d", "a/b/c", "d"}, - {"a/b/c/d", "a/b", "c/d"}, - } - for _, tt := range cases { - t.Run(tt.in+"~"+tt.prefix, func(t *testing.T) { - got := stripPrefix(tt.in, tt.prefix) - if got != tt.out { - t.Fatalf("Wanted %v got %v", tt.out, got) - } - }) - } -} diff --git a/operator/pkg/helmreconciler/apply.go b/operator/pkg/helmreconciler/apply.go deleted file mode 100644 index 4147e832e..000000000 --- a/operator/pkg/helmreconciler/apply.go +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package helmreconciler - -import ( - "context" - "fmt" - "strings" - "time" -) - -import ( - errors2 "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/util/retry" - util2 "k8s.io/kubectl/pkg/util" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/cache" - "github.com/apache/dubbo-go-pixiu/operator/pkg/metrics" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/progress" -) - -const fieldOwnerOperator = "istio-operator" - -// ApplyManifest applies the manifest to create or update resources. It returns the processed (created or updated) -// objects and the number of objects in the manifests. -func (h *HelmReconciler) ApplyManifest(manifest name.Manifest, serverSideApply bool) (object.K8sObjects, int, error) { - var processedObjects object.K8sObjects - var deployedObjects int - var errs util.Errors - cname := string(manifest.Name) - crHash, err := h.getCRHash(cname) - if err != nil { - return nil, 0, err - } - - scope.Infof("Processing resources from manifest: %s for CR %s", cname, crHash) - allObjects, err := object.ParseK8sObjectsFromYAMLManifest(manifest.Content) - if err != nil { - return nil, 0, err - } - - objectCache := cache.GetCache(crHash) - - // Ensure that for a given CR crHash only one control loop uses the per-crHash cache at any time. - objectCache.Mu.Lock() - defer objectCache.Mu.Unlock() - - // No further locking required beyond this point, since we have a ptr to a cache corresponding to a CR crHash and no - // other controller is allowed to work on at the same time. - var changedObjects object.K8sObjects - var changedObjectKeys []string - allObjectsMap := make(map[string]bool) - - // Check which objects in the manifest have changed from those in the cache. - for _, obj := range allObjects { - oh := obj.Hash() - allObjectsMap[oh] = true - if co, ok := objectCache.Cache[oh]; ok && obj.Equal(co) { - // Object is in the cache and unchanged. - metrics.AddResource(obj.FullName(), obj.GroupVersionKind().GroupKind()) - deployedObjects++ - continue - } - changedObjects = append(changedObjects, obj) - changedObjectKeys = append(changedObjectKeys, oh) - } - - var plog *progress.ManifestLog - if len(changedObjectKeys) > 0 { - plog = h.opts.ProgressLog.NewComponent(cname) - scope.Infof("The following objects differ between generated manifest and cache: \n - %s", strings.Join(changedObjectKeys, "\n - ")) - } else { - scope.Infof("Generated manifest objects are the same as cached for component %s.", cname) - } - - // Objects are applied in groups: namespaces, CRDs, everything else, with wait for ready in between. - nsObjs := object.KindObjects(changedObjects, name.NamespaceStr) - crdObjs := object.KindObjects(changedObjects, name.CRDStr) - otherObjs := object.ObjectsNotInLists(changedObjects, nsObjs, crdObjs) - for _, objList := range []object.K8sObjects{nsObjs, crdObjs, otherObjs} { - // For a given group of objects, apply in sorted order of priority with no wait in between. - objList.Sort(object.DefaultObjectOrder()) - for _, obj := range objList { - obju := obj.UnstructuredObject() - if err := h.applyLabelsAndAnnotations(obju, cname); err != nil { - return nil, 0, err - } - if err := h.ApplyObject(obj.UnstructuredObject(), serverSideApply); err != nil { - scope.Error(err.Error()) - errs = util.AppendErr(errs, err) - continue - } - plog.ReportProgress() - metrics.AddResource(obj.FullName(), obj.GroupVersionKind().GroupKind()) - processedObjects = append(processedObjects, obj) - // Update the cache with the latest object. - objectCache.Cache[obj.Hash()] = obj - } - } - - // Prune anything not in the manifest out of the cache. - var removeKeys []string - for k := range objectCache.Cache { - if !allObjectsMap[k] { - removeKeys = append(removeKeys, k) - } - } - for _, k := range removeKeys { - scope.Infof("Pruning object %s from cache.", k) - delete(objectCache.Cache, k) - } - - if len(changedObjectKeys) > 0 { - if len(errs) != 0 { - plog.ReportError(util.ToString(errs.Dedup(), "\n")) - return processedObjects, 0, errs.ToError() - } - - err := WaitForResources(processedObjects, h.kubeClient, - h.opts.WaitTimeout, h.opts.DryRun, plog) - if err != nil { - werr := fmt.Errorf("failed to wait for resource: %v", err) - plog.ReportError(werr.Error()) - return processedObjects, 0, werr - } - plog.ReportFinished() - - } - return processedObjects, deployedObjects, nil -} - -// ApplyObject creates or updates an object in the API server depending on whether it already exists. -// It mutates obj. -func (h *HelmReconciler) ApplyObject(obj *unstructured.Unstructured, serverSideApply bool) error { - if obj.GetKind() == "List" { - var errs util.Errors - list, err := obj.ToList() - if err != nil { - scope.Errorf("error converting List object: %s", err) - return err - } - for _, item := range list.Items { - err = h.ApplyObject(&item, serverSideApply) - if err != nil { - errs = util.AppendErr(errs, err) - } - } - return errs.ToError() - } - - if err := util2.CreateApplyAnnotation(obj, unstructured.UnstructuredJSONScheme); err != nil { - scope.Errorf("unexpected error adding apply annotation to object: %s", err) - } - - receiver := &unstructured.Unstructured{} - receiver.SetGroupVersionKind(obj.GroupVersionKind()) - objectKey := client.ObjectKeyFromObject(obj) - objectStr := fmt.Sprintf("%s/%s/%s", obj.GetKind(), obj.GetNamespace(), obj.GetName()) - - if scope.DebugEnabled() { - scope.Debugf("Processing object:\n%s\n\n", util.ToYAML(obj)) - } - - if h.opts.DryRun { - scope.Infof("Not applying object %s because of dry run.", objectStr) - return nil - } - - gvk := obj.GetObjectKind().GroupVersionKind() - if serverSideApply { - return h.serverSideApply(obj) - } - - // for k8s version before 1.16 - backoff := wait.Backoff{Duration: time.Millisecond * 10, Factor: 2, Steps: 3} - return retry.RetryOnConflict(backoff, func() error { - err := h.client.Get(context.TODO(), objectKey, receiver) - - switch { - case errors2.IsNotFound(err): - scope.Infof("Creating %s (%s/%s)", objectStr, h.iop.Name, h.iop.Spec.Revision) - err = h.client.Create(context.TODO(), obj) - if err != nil { - return fmt.Errorf("failed to create %q: %w", objectStr, err) - } - metrics.ResourceCreationTotal. - With(metrics.ResourceKindLabel.Value(util.GKString(gvk.GroupKind()))). - Increment() - return nil - case err == nil: - scope.Infof("Updating %s (%s/%s)", objectStr, h.iop.Name, h.iop.Spec.Revision) - // The correct way to do this is with a server-side apply. However, this requires users to be running Kube 1.16. - // When we no longer support < 1.16 use the code described in the linked issue. - // https://github.com/kubernetes-sigs/controller-runtime/issues/347 - if err := applyOverlay(receiver, obj); err != nil { - return err - } - if err := h.client.Update(context.TODO(), receiver); err != nil { - return err - } - metrics.ResourceUpdateTotal. - With(metrics.ResourceKindLabel.Value(util.GKString(gvk.GroupKind()))). - Increment() - return nil - } - return nil - }) -} - -// use server-side apply, require kubernetes 1.16+ -func (h *HelmReconciler) serverSideApply(obj *unstructured.Unstructured) error { - objectStr := fmt.Sprintf("%s/%s/%s", obj.GetKind(), obj.GetNamespace(), obj.GetName()) - scope.Infof("using server side apply to update obj: %v", objectStr) - opts := []client.PatchOption{client.ForceOwnership, client.FieldOwner(fieldOwnerOperator)} - if err := h.client.Patch(context.TODO(), obj, client.Apply, opts...); err != nil { - return fmt.Errorf("failed to update resource with server-side apply for obj %v: %v", objectStr, err) - } - return nil -} diff --git a/operator/pkg/helmreconciler/apply_test.go b/operator/pkg/helmreconciler/apply_test.go deleted file mode 100644 index 92aadfca0..000000000 --- a/operator/pkg/helmreconciler/apply_test.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package helmreconciler - -import ( - "context" - "os" - "reflect" - "sync" - "testing" -) - -import ( - v1alpha12 "istio.io/api/operator/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" -) - -func TestHelmReconciler_ApplyObject(t *testing.T) { - tests := []struct { - name string - currentState string - input string - want string - wantErr bool - }{ - { - name: "creates if not present", - input: "testdata/configmap.yaml", - want: "testdata/configmap.yaml", - }, - { - name: "updates if present", - currentState: "testdata/configmap.yaml", - input: "testdata/configmap-changed.yaml", - want: "testdata/configmap-changed.yaml", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - obj := loadData(t, tt.input) - cl := &fakeClientWrapper{fake.NewClientBuilder().WithRuntimeObjects(loadData(t, tt.input).UnstructuredObject()).Build()} - h := &HelmReconciler{ - client: cl, - opts: &Options{}, - iop: &v1alpha1.IstioOperator{ - ObjectMeta: v1.ObjectMeta{ - Name: "test-operator", - Namespace: "istio-operator-test", - }, - Spec: &v1alpha12.IstioOperatorSpec{}, - }, - countLock: &sync.Mutex{}, - prunedKindSet: map[schema.GroupKind]struct{}{}, - } - if err := h.ApplyObject(obj.UnstructuredObject(), false); (err != nil) != tt.wantErr { - t.Errorf("HelmReconciler.ApplyObject() error = %v, wantErr %v", err, tt.wantErr) - } - - manifest := loadData(t, tt.want) - key := client.ObjectKeyFromObject(manifest.UnstructuredObject()) - got, want := obj.UnstructuredObject(), manifest.UnstructuredObject() - - if err := cl.Get(context.Background(), key, got); err != nil { - t.Errorf("error validating manifest %v: %v", manifest.Hash(), err) - } - // remove resource version and annotations (last applied config) when we compare as we don't care - unstructured.RemoveNestedField(got.Object, "metadata", "resourceVersion") - unstructured.RemoveNestedField(got.Object, "metadata", "annotations") - - if !reflect.DeepEqual(want, got) { - t.Errorf("wanted:\n%v\ngot:\n%v", - object.NewK8sObject(want, nil, nil).YAMLDebugString(), - object.NewK8sObject(got, nil, nil).YAMLDebugString(), - ) - } - }) - } -} - -type fakeClientWrapper struct { - client.Client -} - -// Patch converts apply patches to merge patches because fakeclient does not support apply patch. -func (c *fakeClientWrapper) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error { - patch, opts = convertApplyToMergePatch(patch, opts...) - return c.Client.Patch(ctx, obj, patch, opts...) -} - -func convertApplyToMergePatch(patch client.Patch, opts ...client.PatchOption) (client.Patch, []client.PatchOption) { - if patch.Type() == types.ApplyPatchType { - patch = client.Merge - patchOptions := make([]client.PatchOption, 0, len(opts)) - for _, opt := range opts { - if opt == client.ForceOwnership { - continue - } - patchOptions = append(patchOptions, opt) - } - opts = patchOptions - } - return patch, opts -} - -func loadData(t *testing.T, file string) *object.K8sObject { - contents, err := os.ReadFile(file) - if err != nil { - t.Fatal(err) - } - obj, err := object.ParseYAMLToK8sObject(contents) - if err != nil { - t.Fatal(err) - } - return obj -} diff --git a/operator/pkg/helmreconciler/common.go b/operator/pkg/helmreconciler/common.go deleted file mode 100644 index 1ac67b7b8..000000000 --- a/operator/pkg/helmreconciler/common.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package helmreconciler - -import ( - "fmt" - "io" - "strconv" - "strings" -) - -import ( - jsonpatch "github.com/evanphx/json-patch/v5" - "istio.io/pkg/log" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/kubectl/pkg/scheme" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" -) - -const ( - // MetadataNamespace is the namespace for mesh metadata (labels, annotations) - MetadataNamespace = "install.operator.istio.io" - // OwningResourceName represents the name of the owner to which the resource relates - OwningResourceName = MetadataNamespace + "/owning-resource" - // OwningResourceNamespace represents the namespace of the owner to which the resource relates - OwningResourceNamespace = MetadataNamespace + "/owning-resource-namespace" - // operatorLabelStr indicates Istio operator is managing this resource. - operatorLabelStr = name.OperatorAPINamespace + "/managed" - // operatorReconcileStr indicates that the operator will reconcile the resource. - operatorReconcileStr = "Reconcile" - // IstioComponentLabelStr indicates which Istio component a resource belongs to. - IstioComponentLabelStr = name.OperatorAPINamespace + "/component" - // istioVersionLabelStr indicates the Istio version of the installation. - istioVersionLabelStr = name.OperatorAPINamespace + "/version" - // istioOperatorInstallPrefix indicates the name of resource istiooperators.install.istio.io for Istio operator installation. - istioOperatorsInstallPrefix = "installed-state" -) - -var ( - // TestMode sets the controller into test mode. Used for unit tests to bypass things like waiting on resources. - TestMode = false - - scope = log.RegisterScope("installer", "installer", 0) -) - -func init() { - // Tree representation and wait channels are an inversion of ComponentDependencies and are constructed from it. - buildInstallTree() -} - -// ComponentTree represents a tree of component dependencies. -type ( - ComponentTree map[name.ComponentName]interface{} - componentNameToListMap map[name.ComponentName][]name.ComponentName -) - -var ( - // ComponentDependencies is a tree of component dependencies. The semantics are ComponentDependencies[cname] gives - // the subtree of components that must wait for cname to be installed before starting installation themselves. - ComponentDependencies = componentNameToListMap{ - name.PilotComponentName: { - name.CNIComponentName, - name.IngressComponentName, - name.EgressComponentName, - }, - name.IstioBaseComponentName: { - name.PilotComponentName, - }, - } - - // InstallTree is a top down hierarchy tree of dependencies where children must wait for the parent to complete - // before starting installation. - InstallTree = make(ComponentTree) -) - -// buildInstallTree builds a tree from buildInstallTree where parents are the root of each subtree. -func buildInstallTree() { - // Starting with root, recursively insert each first level child into each node. - insertChildrenRecursive(name.IstioBaseComponentName, InstallTree, ComponentDependencies) -} - -func insertChildrenRecursive(componentName name.ComponentName, tree ComponentTree, children componentNameToListMap) { - tree[componentName] = make(ComponentTree) - for _, child := range children[componentName] { - insertChildrenRecursive(child, tree[componentName].(ComponentTree), children) - } -} - -// InstallTreeString returns a string representation of the dependency tree. -func InstallTreeString() string { - var sb strings.Builder - buildInstallTreeString(name.IstioBaseComponentName, "", &sb) - return sb.String() -} - -func buildInstallTreeString(componentName name.ComponentName, prefix string, sb io.StringWriter) { - _, _ = sb.WriteString(prefix + string(componentName) + "\n") - if _, ok := InstallTree[componentName].(ComponentTree); !ok { - return - } - for k := range InstallTree[componentName].(ComponentTree) { - buildInstallTreeString(k, prefix+" ", sb) - } -} - -// applyOverlay applies an overlay using JSON patch strategy over the current Object in place. -func applyOverlay(current, overlay *unstructured.Unstructured) error { - cj, err := runtime.Encode(unstructured.UnstructuredJSONScheme, current) - if err != nil { - return err - } - - overlayUpdated := overlay.DeepCopy() - if strings.EqualFold(current.GetKind(), "service") { - if err := saveClusterIP(current, overlayUpdated); err != nil { - return err - } - - saveNodePorts(current, overlayUpdated) - } - - uj, err := runtime.Encode(unstructured.UnstructuredJSONScheme, overlayUpdated) - if err != nil { - return err - } - merged, err := jsonpatch.MergePatch(cj, uj) - if err != nil { - return err - } - return runtime.DecodeInto(unstructured.UnstructuredJSONScheme, merged, current) -} - -// createPortMap returns a map, mapping the value of the port and value of the nodePort -func createPortMap(current *unstructured.Unstructured) map[string]uint32 { - portMap := make(map[string]uint32) - svc := &v1.Service{} - if err := scheme.Scheme.Convert(current, svc, nil); err != nil { - log.Error(err.Error()) - return portMap - } - for _, p := range svc.Spec.Ports { - portMap[strconv.Itoa(int(p.Port))] = uint32(p.NodePort) - } - return portMap -} - -// saveNodePorts transfers the port values from the current cluster into the overlay -func saveNodePorts(current, overlay *unstructured.Unstructured) { - portMap := createPortMap(current) - ports, _, _ := unstructured.NestedFieldNoCopy(overlay.Object, "spec", "ports") - portList, ok := ports.([]interface{}) - if !ok { - return - } - for _, port := range portList { - m, ok := port.(map[string]interface{}) - if !ok { - continue - } - if nodePortNum, ok := m["nodePort"]; ok && fmt.Sprintf("%v", nodePortNum) == "0" { - if portNum, ok := m["port"]; ok { - if v, ok := portMap[fmt.Sprintf("%v", portNum)]; ok { - m["nodePort"] = v - } - } - } - } -} - -// saveClusterIP copies the cluster IP from the current cluster into the overlay -func saveClusterIP(current, overlay *unstructured.Unstructured) error { - // Save the value of spec.clusterIP set by the cluster - if clusterIP, found, err := unstructured.NestedString(current.Object, "spec", - "clusterIP"); err != nil { - return err - } else if found { - if err := unstructured.SetNestedField(overlay.Object, clusterIP, "spec", - "clusterIP"); err != nil { - return err - } - } - return nil -} - -// getIstioOperatorCRName get the Istio operator crd name based on specified revision -func getIstioOperatorCRName(revision string) string { - name := istioOperatorsInstallPrefix - if revision == "" || revision == "default" { - return name - } - return name + "-" + revision -} diff --git a/operator/pkg/helmreconciler/common_test.go b/operator/pkg/helmreconciler/common_test.go deleted file mode 100644 index 7a3b71b75..000000000 --- a/operator/pkg/helmreconciler/common_test.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package helmreconciler - -import ( - "testing" -) - -import ( - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" -) - -func TestHelmReconciler_saveClusterIP(t *testing.T) { - tests := []struct { - name string - current string - overlay string - expectedIP string - wantErr bool - }{ - { - name: "keeps the cluster IP set by the cluster", - current: "testdata/current.yaml", - overlay: "testdata/overlay.yaml", - expectedIP: "10.0.171.239", - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - obj := loadData(t, tt.current) - current := obj.UnstructuredObject() - objOverlay := loadData(t, tt.overlay) - overlay := objOverlay.UnstructuredObject() - if err := saveClusterIP(current, overlay); (err != nil) != tt.wantErr { - t.Errorf("HelmReconciler.saveClusterIP() error = %v, wantErr %v", err, tt.wantErr) - } - clusterIP, _, _ := unstructured.NestedString(current.Object, "spec", - "clusterIP") - if clusterIP != tt.expectedIP { - t.Errorf("wanted:\n%v\ngot:\n%v", tt.expectedIP, clusterIP) - } - }) - } -} - -func TestHelmReconciler_saveNodePorts(t *testing.T) { - tests := []struct { - name string - current string - overlay string - want string - }{ - { - name: "keeps the nodePorts set by the cluster", - current: "testdata/current.yaml", - overlay: "testdata/overlay.yaml", - want: "testdata/clusterIP-changed.yaml", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - obj := loadData(t, tt.current) - current := obj.UnstructuredObject() - objOverlay := loadData(t, tt.overlay) - overlay := objOverlay.UnstructuredObject() - overlayMap := createPortMap(overlay) - saveNodePorts(current, overlay) - currentMap := createPortMap(current) - overlayUpdatedMap := createPortMap(overlay) - for key, element := range overlayMap { - if element != 0 { - expectedVal, ok := overlayUpdatedMap[key] - if !ok { - t.Errorf("HelmReconciler.saveNodePorts() error = unmatched key %v between initial overlay "+ - "and updated overlay.", key) - break - } - if element != expectedVal { - t.Errorf("wanted:\n%v\ngot:\n%v", expectedVal, element) - } - continue - } - val, ok := overlayUpdatedMap[key] - if !ok { - t.Errorf("HelmReconciler.saveNodePorts() error = unmatched key %v between initial overlay "+ - "and updated overlay.", key) - break - } - expectedVal, ok := currentMap[key] - if !ok { - t.Errorf("HelmReconciler.saveNodePorts() error = unmatched key %v between initial overlay "+ - "and current.", key) - break - } - if val != expectedVal { - t.Errorf("wanted:\n%v\ngot:\n%v", expectedVal, val) - } - } - }) - } -} diff --git a/operator/pkg/helmreconciler/prune.go b/operator/pkg/helmreconciler/prune.go deleted file mode 100644 index f7029ec1e..000000000 --- a/operator/pkg/helmreconciler/prune.go +++ /dev/null @@ -1,472 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package helmreconciler - -import ( - "context" - "fmt" - "strings" -) - -import ( - "istio.io/api/label" - "istio.io/api/operator/v1alpha1" - kerrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - klabels "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/selection" - "k8s.io/apimachinery/pkg/version" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -import ( - v1alpha12 "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/cache" - "github.com/apache/dubbo-go-pixiu/operator/pkg/metrics" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" - "github.com/apache/dubbo-go-pixiu/operator/pkg/translate" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/proxy" -) - -const ( - autoscalingV2MinK8SVersion = 23 - pdbV1MinK8SVersion = 21 -) - -var ( - // ClusterResources are resource types the operator prunes, ordered by which types should be deleted, first to last. - ClusterResources = []schema.GroupVersionKind{ - {Group: "admissionregistration.k8s.io", Version: "v1", Kind: name.MutatingWebhookConfigurationStr}, - {Group: "admissionregistration.k8s.io", Version: "v1", Kind: name.ValidatingWebhookConfigurationStr}, - {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: name.ClusterRoleStr}, - {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: name.ClusterRoleBindingStr}, - // Cannot currently prune CRDs because this will also wipe out user config. - // {Group: "apiextensions.k8s.io", Version: "v1beta1", Kind: name.CRDStr}, - } - // ClusterCPResources lists cluster scope resources types which should be deleted during uninstall command. - ClusterCPResources = []schema.GroupVersionKind{ - {Group: "admissionregistration.k8s.io", Version: "v1", Kind: name.MutatingWebhookConfigurationStr}, - {Group: "admissionregistration.k8s.io", Version: "v1", Kind: name.ValidatingWebhookConfigurationStr}, - {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: name.ClusterRoleStr}, - {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: name.ClusterRoleBindingStr}, - } - // AllClusterResources lists all cluster scope resources types which should be deleted in purge case, including CRD. - AllClusterResources = append(ClusterResources, - schema.GroupVersionKind{Group: "apiextensions.k8s.io", Version: "v1", Kind: name.CRDStr}, - ) -) - -// NamespacedResources gets specific pruning resources based on the k8s version -func NamespacedResources(version *version.Info) []schema.GroupVersionKind { - res := []schema.GroupVersionKind{ - {Group: "apps", Version: "v1", Kind: name.DeploymentStr}, - {Group: "apps", Version: "v1", Kind: name.DaemonSetStr}, - {Group: "", Version: "v1", Kind: name.ServiceStr}, - {Group: "", Version: "v1", Kind: name.CMStr}, - {Group: "", Version: "v1", Kind: name.PVCStr}, - {Group: "", Version: "v1", Kind: name.PodStr}, - {Group: "", Version: "v1", Kind: name.SecretStr}, - {Group: "", Version: "v1", Kind: name.SAStr}, - {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: name.RoleBindingStr}, - {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: name.RoleStr}, - {Group: name.NetworkingAPIGroupName, Version: "v1alpha3", Kind: name.DestinationRuleStr}, - {Group: name.NetworkingAPIGroupName, Version: "v1alpha3", Kind: name.EnvoyFilterStr}, - {Group: name.NetworkingAPIGroupName, Version: "v1alpha3", Kind: name.GatewayStr}, - {Group: name.NetworkingAPIGroupName, Version: "v1alpha3", Kind: name.VirtualServiceStr}, - {Group: name.SecurityAPIGroupName, Version: "v1beta1", Kind: name.PeerAuthenticationStr}, - } - // autoscaling v2 API is available on >=1.23 - if kube.IsKubeAtLeastOrLessThanVersion(version, autoscalingV2MinK8SVersion, true) { - res = append(res, schema.GroupVersionKind{Group: "autoscaling", Version: "v2", Kind: name.HPAStr}) - } else { - res = append(res, schema.GroupVersionKind{Group: "autoscaling", Version: "v2beta2", Kind: name.HPAStr}) - } - // policy/v1 is available on >=1.21 - if kube.IsKubeAtLeastOrLessThanVersion(version, pdbV1MinK8SVersion, true) { - res = append(res, schema.GroupVersionKind{Group: "policy", Version: "v1", Kind: name.PDBStr}) - } else { - res = append(res, schema.GroupVersionKind{Group: "policy", Version: "v1beta1", Kind: name.PDBStr}) - } - return res -} - -// NamespacedResources gets specific pruning resources based on the k8s version -func (h *HelmReconciler) NamespacedResources() []schema.GroupVersionKind { - clusterVersion, err := h.kubeClient.GetKubernetesVersion() - if err != nil { - scope.Warnf("Failed to get kubernetes version: %v", err) - } - return NamespacedResources(clusterVersion) -} - -// Prune removes any resources not specified in manifests generated by HelmReconciler h. -func (h *HelmReconciler) Prune(manifests name.ManifestMap, all bool) error { - return h.runForAllTypes(func(labels map[string]string, objects *unstructured.UnstructuredList) error { - var errs util.Errors - if all { - errs = util.AppendErr(errs, h.deleteResources(nil, labels, "", objects, all)) - } else { - for cname, manifest := range manifests.Consolidated() { - errs = util.AppendErr(errs, h.deleteResources(object.AllObjectHashes(manifest), labels, cname, objects, all)) - } - } - return errs.ToError() - }) -} - -// PruneControlPlaneByRevisionWithController is called to remove specific control plane revision -// during reconciliation process of controller. -// It returns the install status and any error encountered. -func (h *HelmReconciler) PruneControlPlaneByRevisionWithController(iopSpec *v1alpha1.IstioOperatorSpec) (*v1alpha1.InstallStatus, error) { - ns := v1alpha12.Namespace(iopSpec) - if ns == "" { - ns = name.IstioDefaultNamespace - } - errStatus := &v1alpha1.InstallStatus{Status: v1alpha1.InstallStatus_ERROR} - enabledComponents, err := translate.GetEnabledComponents(iopSpec) - if err != nil { - return errStatus, - fmt.Errorf("failed to get enabled components: %v", err) - } - pilotEnabled := false - // check wherther the istiod is enabled - for _, c := range enabledComponents { - if c == string(name.PilotComponentName) { - pilotEnabled = true - break - } - } - // If istiod is enabled, check if it has any proxies connected. - if pilotEnabled { - // TODO(ramaraochavali): Find a better alternative instead of using debug interface - // of istiod as it is typically not recommended in production environments. - pids, err := proxy.GetIDsFromProxyInfo("", "", iopSpec.Revision, ns) - if err != nil { - return errStatus, - fmt.Errorf("failed to check proxy infos: %v", err) - } - // TODO(richardwxn): add warning message together with the status - if len(pids) != 0 { - msg := fmt.Sprintf("there are proxies still pointing to the pruned control plane: %s.", - strings.Join(pids, " ")) - st := &v1alpha1.InstallStatus{Status: v1alpha1.InstallStatus_ACTION_REQUIRED, Message: msg} - return st, nil - } - } - - for _, c := range enabledComponents { - uslist, err := h.GetPrunedResources(iopSpec.Revision, false, c) - if err != nil { - return errStatus, err - } - err = h.DeleteObjectsList(uslist, c) - if err != nil { - return errStatus, err - } - } - return &v1alpha1.InstallStatus{Status: v1alpha1.InstallStatus_HEALTHY}, nil -} - -// DeleteObjectsList removed resources that are in the slice of UnstructuredList. -func (h *HelmReconciler) DeleteObjectsList(objectsList []*unstructured.UnstructuredList, componentName string) error { - var errs util.Errors - deletedObjects := make(map[string]bool) - for _, ul := range objectsList { - for _, o := range ul.Items { - obj := object.NewK8sObject(&o, nil, nil) - oh := obj.Hash() - - // kube client does not differentiate API version when listing, added this check to deduplicate. - if deletedObjects[oh] { - continue - } - if err := h.deleteResource(obj, componentName, oh); err != nil { - errs = append(errs, err) - } - deletedObjects[oh] = true - } - } - - return errs.ToError() -} - -// GetPrunedResources get the list of resources to be removed -// 1. if includeClusterResources is false, we list the namespaced resources by matching revision and component labels. -// 2. if includeClusterResources is true, we list the namespaced and cluster resources by component labels only. -// If componentName is not empty, only resources associated with specific components would be returned -// UnstructuredList of objects and corresponding list of name kind hash of k8sObjects would be returned -func (h *HelmReconciler) GetPrunedResources(revision string, includeClusterResources bool, componentName string) ( - []*unstructured.UnstructuredList, error) { - var usList []*unstructured.UnstructuredList - labels := make(map[string]string) - if revision != "" { - labels[label.IoIstioRev.Name] = revision - } - if componentName != "" { - labels[IstioComponentLabelStr] = componentName - } - selector := klabels.Set(labels).AsSelectorPreValidated() - resources := h.NamespacedResources() - gvkList := append(resources, ClusterCPResources...) - if includeClusterResources { - gvkList = append(resources, AllClusterResources...) - if ioplist := h.getIstioOperatorCR(); ioplist.Items != nil { - usList = append(usList, ioplist) - } - } else if componentName == "" { - // Remove Istio operator CR with specified revision if it is uninstalled - ioplist := h.getIstioOperatorCR() - if ioplist.Items != nil { - for _, iop := range ioplist.Items { - revisionIop := getIstioOperatorCRName(revision) - if iop.GetName() == revisionIop { - if iopToList, err := iop.ToList(); err == nil { - iopToList.Items = []unstructured.Unstructured{iop} - usList = append(usList, iopToList) - break - } - } - } - } - } - for _, gvk := range gvkList { - objects := &unstructured.UnstructuredList{} - objects.SetGroupVersionKind(gvk) - componentRequirement, err := klabels.NewRequirement(IstioComponentLabelStr, selection.Exists, nil) - if err != nil { - return usList, err - } - if includeClusterResources { - s := klabels.NewSelector() - err = h.client.List(context.TODO(), objects, - client.MatchingLabelsSelector{Selector: s.Add(*componentRequirement)}) - } else { - // do not prune base components or unknown components - includeCN := []string{ - string(name.PilotComponentName), - string(name.IngressComponentName), string(name.EgressComponentName), - string(name.CNIComponentName), string(name.IstioOperatorComponentName), - string(name.IstiodRemoteComponentName), - } - includeRequirement, err := klabels.NewRequirement(IstioComponentLabelStr, selection.In, includeCN) - if err != nil { - return usList, err - } - if err = h.client.List(context.TODO(), objects, - client.MatchingLabelsSelector{ - Selector: selector.Add(*includeRequirement, *componentRequirement), - }, - ); err != nil { - continue - } - } - if err != nil { - continue - } - for _, obj := range objects.Items { - objName := fmt.Sprintf("%s/%s", obj.GetNamespace(), obj.GetName()) - metrics.AddResource(objName, gvk.GroupKind()) - } - usList = append(usList, objects) - } - - return usList, nil -} - -// getIstioOperatorCR is a helper function to get IstioOperator CR during purge, -// otherwise the resources would be reconciled back later if there is in-cluster operator deployment. -// And it is needed to remove the IstioOperator CRD. -func (h *HelmReconciler) getIstioOperatorCR() *unstructured.UnstructuredList { - objects := &unstructured.UnstructuredList{} - objects.SetGroupVersionKind(schema.GroupVersionKind{ - Group: "install.istio.io", - Version: "v1alpha1", Kind: name.IstioOperatorStr, - }) - if err := h.client.List(context.TODO(), objects); err != nil { - scope.Errorf("failed to list IstioOperator CR: %v", err) - } - return objects -} - -// DeleteControlPlaneByManifests removed resources by manifests with matching revision label. -// If purge option is set to true, all manifests would be removed regardless of labels match. -func (h *HelmReconciler) DeleteControlPlaneByManifests(manifestMap name.ManifestMap, - revision string, includeClusterResources bool) error { - labels := map[string]string{ - operatorLabelStr: operatorReconcileStr, - } - cpManifestMap := make(name.ManifestMap) - if revision != "" { - labels[label.IoIstioRev.Name] = revision - } - if !includeClusterResources { - // only delete istiod resources if revision is empty and --purge flag is not true. - cpManifestMap[name.PilotComponentName] = manifestMap[name.PilotComponentName] - manifestMap = cpManifestMap - } - for cn, mf := range manifestMap.Consolidated() { - if cn == string(name.IstioBaseComponentName) && !includeClusterResources { - continue - } - objects, err := object.ParseK8sObjectsFromYAMLManifest(mf) - if err != nil { - return fmt.Errorf("failed to parse k8s objects from yaml: %v", err) - } - if objects == nil { - continue - } - unstructuredObjects := unstructured.UnstructuredList{} - for _, obj := range objects { - if h.opts.DryRun { - h.opts.Log.LogAndPrintf("Not deleting object %s because of dry run.", obj.Hash()) - continue - } - obju := obj.UnstructuredObject() - if err := h.applyLabelsAndAnnotations(obju, cn); err != nil { - return err - } - unstructuredObjects.Items = append(unstructuredObjects.Items, *obju) - } - if err := h.deleteResources(nil, labels, cn, &unstructuredObjects, includeClusterResources); err != nil { - return fmt.Errorf("failed to delete resources: %v", err) - } - } - return nil -} - -// pruneAllTypes will collect all existing resource types we care about. For each type, the callback function -// will be called with the labels used to select this type, and all objects. -// This is in internal function meant to support prune and delete -func (h *HelmReconciler) runForAllTypes(callback func(labels map[string]string, objects *unstructured.UnstructuredList) error) error { - var errs util.Errors - // Ultimately, we want to prune based on component labels. Each of these share a common set of labels - // Rather than do N List() calls for each component, we will just filter for the common subset here - // and each component will do its own filtering - // Because we are filtering by the core labels, List() will only return items that some components will care - // about, so we are not querying for an overly broad set of resources. - labels, err := h.getCoreOwnerLabels() - if err != nil { - return err - } - selector := klabels.Set(labels).AsSelectorPreValidated() - resources := append(h.NamespacedResources(), ClusterResources...) - for _, gvk := range resources { - // First, we collect all objects for the provided GVK - objects := &unstructured.UnstructuredList{} - objects.SetGroupVersionKind(gvk) - componentRequirement, err := klabels.NewRequirement(IstioComponentLabelStr, selection.Exists, nil) - if err != nil { - return err - } - selector = selector.Add(*componentRequirement) - if err := h.client.List(context.TODO(), objects, client.MatchingLabelsSelector{Selector: selector}); err != nil { - // we only want to retrieve resources clusters - if !(h.opts.DryRun && meta.IsNoMatchError(err)) { - scope.Debugf("retrieving resources to prune type %s: %s", gvk.String(), err) - } - continue - } - for _, obj := range objects.Items { - objName := fmt.Sprintf("%s/%s", obj.GetNamespace(), obj.GetName()) - metrics.AddResource(objName, gvk.GroupKind()) - } - errs = util.AppendErr(errs, callback(labels, objects)) - } - return errs.ToError() -} - -// deleteResources delete any resources from the given component that are not in the excluded map. Resource -// labels are used to identify the resources belonging to the component. -func (h *HelmReconciler) deleteResources(excluded map[string]bool, coreLabels map[string]string, - componentName string, objects *unstructured.UnstructuredList, all bool) error { - var errs util.Errors - labels := h.addComponentLabels(coreLabels, componentName) - selector := klabels.Set(labels).AsSelectorPreValidated() - for _, o := range objects.Items { - obj := object.NewK8sObject(&o, nil, nil) - oh := obj.Hash() - if !all { - // Label mismatch. Provided objects don't select against the component, so this likely means the object - // is for another component. - if !selector.Matches(klabels.Set(o.GetLabels())) { - continue - } - if excluded[oh] { - continue - } - } - if err := h.deleteResource(obj, componentName, oh); err != nil { - errs = append(errs, err) - } - } - if all { - cache.FlushObjectCaches() - } - - return errs.ToError() -} - -func (h *HelmReconciler) deleteResource(obj *object.K8sObject, componentName, oh string) error { - if h.opts.DryRun { - h.opts.Log.LogAndPrintf("Not pruning object %s because of dry run.", oh) - return nil - } - u := obj.UnstructuredObject() - if u.GetKind() == name.IstioOperatorStr { - u.SetFinalizers([]string{}) - if err := h.client.Patch(context.TODO(), u, client.Merge); err != nil { - scope.Errorf("failed to patch IstioOperator CR: %s, %v", u.GetName(), err) - } - } - err := h.client.Delete(context.TODO(), u, client.PropagationPolicy(metav1.DeletePropagationBackground)) - scope.Debugf("Deleting %s (%s/%v)", oh, h.iop.Name, h.iop.Spec.Revision) - objGvk := u.GroupVersionKind() - if err != nil { - if !kerrors.IsNotFound(err) { - return err - } - // do not return error if resources are not found - h.opts.Log.LogAndPrintf("object: %s is not being deleted because it no longer exists", obj.Hash()) - return nil - } - if componentName != "" { - h.removeFromObjectCache(componentName, oh) - } else { - cache.FlushObjectCaches() - } - metrics.ResourceDeletionTotal. - With(metrics.ResourceKindLabel.Value(util.GKString(objGvk.GroupKind()))). - Increment() - h.addPrunedKind(objGvk.GroupKind()) - metrics.RemoveResource(obj.FullName(), objGvk.GroupKind()) - h.opts.Log.LogAndPrintf(" Removed %s.", oh) - return nil -} - -// RemoveObject removes object with objHash in componentName from the object cache. -func (h *HelmReconciler) removeFromObjectCache(componentName, objHash string) { - crHash, err := h.getCRHash(componentName) - if err != nil { - scope.Error(err.Error()) - } - cache.RemoveObject(crHash, objHash) - scope.Infof("Removed object %s from Cache.", objHash) -} diff --git a/operator/pkg/helmreconciler/prune_test.go b/operator/pkg/helmreconciler/prune_test.go deleted file mode 100644 index 440e301fd..000000000 --- a/operator/pkg/helmreconciler/prune_test.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package helmreconciler - -import ( - "context" - "os" - "path/filepath" - "sync" - "testing" -) - -import ( - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/progress" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" -) - -const ( - testRevision = "test" -) - -func TestHelmReconciler_DeleteControlPlaneByManifest(t *testing.T) { - t.Run("deleteControlPlaneByManifest", func(t *testing.T) { - cl := fake.NewClientBuilder().Build() - df := filepath.Join(env.IstioSrc, "manifests/profiles/default.yaml") - iopStr, err := os.ReadFile(df) - if err != nil { - t.Fatal(err) - } - iop := &v1alpha1.IstioOperator{} - if err := yaml.UnmarshalStrict(iopStr, iop); err != nil { - t.Fatal(err) - } - iop.Spec.Revision = testRevision - iop.Spec.InstallPackagePath = filepath.Join(env.IstioSrc, "manifests") - - h := &HelmReconciler{ - client: cl, - kubeClient: kube.NewFakeClientWithVersion("24"), - opts: &Options{ - ProgressLog: progress.NewLog(), - Log: clog.NewDefaultLogger(), - }, - iop: iop, - countLock: &sync.Mutex{}, - prunedKindSet: map[schema.GroupKind]struct{}{}, - } - manifestMap, err := h.RenderCharts() - if err != nil { - t.Fatalf("failed to render manifest: %v", err) - } - applyResourcesIntoCluster(t, h, manifestMap) - if err := h.DeleteControlPlaneByManifests(manifestMap, testRevision, false); err != nil { - t.Fatalf("HelmReconciler.DeleteControlPlaneByManifests() error = %v", err) - } - for _, gvk := range append(h.NamespacedResources(), ClusterCPResources...) { - receiver := &unstructured.Unstructured{} - receiver.SetGroupVersionKind(schema.GroupVersionKind{Group: gvk.Group, Version: gvk.Version, Kind: gvk.Kind}) - objKey := client.ObjectKey{Namespace: "dubbo-system", Name: "istiod-test"} - if gvk.Kind == name.MutatingWebhookConfigurationStr { - objKey.Name = "istio-sidecar-injector-test" - } - // List does not work well here as that requires adding all resource types to the fake client scheme - if err := h.client.Get(context.TODO(), objKey, receiver); err != nil { - // the error is expected because we expect resources do not exist any more in the cluster - t.Logf(err.Error()) - } - obj := receiver.Object - if obj["spec"] != nil { - t.Errorf("got resource: %s/%s from the cluster, expected to be deleted", receiver.GetKind(), receiver.GetName()) - } - } - }) -} - -func applyResourcesIntoCluster(t *testing.T, h *HelmReconciler, manifestMap name.ManifestMap) { - for cn, ms := range manifestMap.Consolidated() { - objects, err := object.ParseK8sObjectsFromYAMLManifest(ms) - if err != nil { - t.Fatalf("failed parse k8s objects from yaml: %v", err) - } - for _, obj := range objects { - obju := obj.UnstructuredObject() - if err := h.applyLabelsAndAnnotations(obju, cn); err != nil { - t.Errorf("failed to apply label and annotations: %v", err) - } - if err := h.ApplyObject(obj.UnstructuredObject(), false); err != nil { - t.Errorf("HelmReconciler.ApplyObject() error = %v", err) - } - } - } -} diff --git a/operator/pkg/helmreconciler/reconciler.go b/operator/pkg/helmreconciler/reconciler.go deleted file mode 100644 index baaa55b21..000000000 --- a/operator/pkg/helmreconciler/reconciler.go +++ /dev/null @@ -1,630 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package helmreconciler - -import ( - "context" - "fmt" - "os" - "strings" - "sync" - "time" -) - -import ( - "istio.io/api/label" - "istio.io/api/operator/v1alpha1" - "istio.io/pkg/version" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" - v12 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/kubernetes" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/formatting" - istioV1Alpha1 "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/metrics" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/progress" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/webhook" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/local" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -// HelmReconciler reconciles resources rendered by a set of helm charts. -type HelmReconciler struct { - client client.Client - kubeClient kube.Client - iop *istioV1Alpha1.IstioOperator - opts *Options - // copy of the last generated manifests. - manifests name.ManifestMap - // dependencyWaitCh is a map of signaling channels. A parent with children ch1...chN will signal - // dependencyWaitCh[ch1]...dependencyWaitCh[chN] when it's completely installed. - dependencyWaitCh map[name.ComponentName]chan struct{} - - // The fields below are for metrics and reporting - countLock *sync.Mutex - prunedKindSet map[schema.GroupKind]struct{} -} - -// Options are options for HelmReconciler. -type Options struct { - // DryRun executes all actions but does not write anything to the cluster. - DryRun bool - // Log is a console logger for user visible CLI output. - Log clog.Logger - // Wait determines if we will wait for resources to be fully applied. Only applies to components that have no - // dependencies. - Wait bool - // WaitTimeout controls the amount of time to wait for resources in a component to become ready before giving up. - WaitTimeout time.Duration - // Log tracks the installation progress for all components. - ProgressLog *progress.Log - // Force ignores validation errors - Force bool - // SkipPrune will skip pruning - SkipPrune bool -} - -var defaultOptions = &Options{ - Log: clog.NewDefaultLogger(), - ProgressLog: progress.NewLog(), -} - -// NewHelmReconciler creates a HelmReconciler and returns a ptr to it -func NewHelmReconciler(client client.Client, kubeClient kube.Client, iop *istioV1Alpha1.IstioOperator, opts *Options) (*HelmReconciler, error) { - if opts == nil { - opts = defaultOptions - } - if opts.ProgressLog == nil { - opts.ProgressLog = progress.NewLog() - } - if int64(opts.WaitTimeout) == 0 { - if waitForResourcesTimeoutStr, found := os.LookupEnv("WAIT_FOR_RESOURCES_TIMEOUT"); found { - if waitForResourcesTimeout, err := time.ParseDuration(waitForResourcesTimeoutStr); err == nil { - opts.WaitTimeout = waitForResourcesTimeout - } else { - scope.Warnf("invalid env variable value: %s for 'WAIT_FOR_RESOURCES_TIMEOUT'! falling back to default value...", waitForResourcesTimeoutStr) - // fallback to default wait resource timeout - opts.WaitTimeout = defaultWaitResourceTimeout - } - } else { - // fallback to default wait resource timeout - opts.WaitTimeout = defaultWaitResourceTimeout - } - } - if iop == nil { - // allows controller code to function for cases where IOP is not provided (e.g. operator remove). - iop = &istioV1Alpha1.IstioOperator{} - iop.Spec = &v1alpha1.IstioOperatorSpec{} - } - return &HelmReconciler{ - client: client, - kubeClient: kubeClient, - iop: iop, - opts: opts, - dependencyWaitCh: initDependencies(), - countLock: &sync.Mutex{}, - prunedKindSet: make(map[schema.GroupKind]struct{}), - }, nil -} - -// initDependencies initializes the dependencies channel tree. -func initDependencies() map[name.ComponentName]chan struct{} { - ret := make(map[name.ComponentName]chan struct{}) - for _, parent := range ComponentDependencies { - for _, child := range parent { - ret[child] = make(chan struct{}, 1) - } - } - return ret -} - -// Reconcile reconciles the associated resources. -func (h *HelmReconciler) Reconcile() (*v1alpha1.InstallStatus, error) { - if err := h.createNamespace(istioV1Alpha1.Namespace(h.iop.Spec), h.networkName()); err != nil { - return nil, err - } - manifestMap, err := h.RenderCharts() - if err != nil { - return nil, err - } - - err = h.analyzeWebhooks(manifestMap[name.PilotComponentName]) - if err != nil { - if h.opts.Force { - scope.Error("invalid webhook configs; continuing because of --force") - } else { - return nil, err - } - } - status := h.processRecursive(manifestMap) - - var pruneErr error - if !h.opts.SkipPrune { - h.opts.ProgressLog.SetState(progress.StatePruning) - pruneErr = h.Prune(manifestMap, false) - h.reportPrunedObjectKind() - } - return status, pruneErr -} - -// processRecursive processes the given manifests in an order of dependencies defined in h. Dependencies are a tree, -// where a child must wait for the parent to complete before starting. -func (h *HelmReconciler) processRecursive(manifests name.ManifestMap) *v1alpha1.InstallStatus { - componentStatus := make(map[string]*v1alpha1.InstallStatus_VersionStatus) - - // mu protects the shared InstallStatus componentStatus across goroutines - var mu sync.Mutex - // wg waits for all manifest processing goroutines to finish - var wg sync.WaitGroup - - serverSideApply := h.CheckSSAEnabled() - - for c, ms := range manifests { - c, ms := c, ms - wg.Add(1) - go func() { - var processedObjs object.K8sObjects - var deployedObjects int - defer wg.Done() - if s := h.dependencyWaitCh[c]; s != nil { - scope.Infof("%s is waiting on dependency...", c) - <-s - scope.Infof("Dependency for %s has completed, proceeding.", c) - } - - // Possible paths for status are RECONCILING -> {NONE, ERROR, HEALTHY}. NONE means component has no resources. - // In NONE case, the component is not shown in overall status. - mu.Lock() - setStatus(componentStatus, c, v1alpha1.InstallStatus_RECONCILING, nil) - mu.Unlock() - - status := v1alpha1.InstallStatus_NONE - var err error - if len(ms) != 0 { - m := name.Manifest{ - Name: c, - Content: name.MergeManifestSlices(ms), - } - processedObjs, deployedObjects, err = h.ApplyManifest(m, serverSideApply) - if err != nil { - status = v1alpha1.InstallStatus_ERROR - } else if len(processedObjs) != 0 || deployedObjects > 0 { - status = v1alpha1.InstallStatus_HEALTHY - } - } - - mu.Lock() - setStatus(componentStatus, c, status, err) - mu.Unlock() - - // Signal all the components that depend on us. - for _, ch := range ComponentDependencies[c] { - scope.Infof("Unblocking dependency %s.", ch) - h.dependencyWaitCh[ch] <- struct{}{} - } - }() - } - wg.Wait() - - metrics.ReportOwnedResourceCounts() - - out := &v1alpha1.InstallStatus{ - Status: overallStatus(componentStatus), - ComponentStatus: componentStatus, - } - - return out -} - -// CheckSSAEnabled is a helper function to check whether ServerSideApply should be used when applying manifests. -func (h *HelmReconciler) CheckSSAEnabled() bool { - if h.kubeClient != nil { - // There is a mutatingwebhook in gke that would corrupt the managedFields, which is fixed in k8s 1.18. - // See: https://github.com/kubernetes/kubernetes/issues/96351 - if kube.IsAtLeastVersion(h.kubeClient, 18) { - // todo(kebe7jun) a more general test method - // API Server does not support detecting whether ServerSideApply is enabled - // through the API for the time being. - ns, err := h.kubeClient.Kube().CoreV1().Namespaces().Get(context.TODO(), constants.KubeSystemNamespace, v12.GetOptions{}) - if err != nil { - scope.Warnf("failed to get namespace: %v", err) - return false - } - if ns.ManagedFields == nil { - scope.Infof("k8s support ServerSideApply but was manually disabled") - return false - } - return true - } - } - return false -} - -// Delete resources associated with the custom resource instance -func (h *HelmReconciler) Delete() error { - defer func() { - metrics.ReportOwnedResourceCounts() - h.reportPrunedObjectKind() - }() - iop := h.iop - if iop.Spec.Revision == "" { - err := h.Prune(nil, true) - return err - } - // Delete IOP with revision: - // for this case we update the status field to pending if there are still proxies pointing to this revision - // and we do not prune shared resources, same effect as `istioctl uninstall --revision foo` command. - status, err := h.PruneControlPlaneByRevisionWithController(iop.Spec) - if err != nil { - return err - } - - // check status here because terminating iop's status can't be updated. - if status.Status == v1alpha1.InstallStatus_ACTION_REQUIRED { - return fmt.Errorf("action is required before deleting the iop instance: %s", status.Message) - } - - // updating status taking no effect for terminating resources. - if err := h.SetStatusComplete(status); err != nil { - return err - } - return nil -} - -// DeleteAll deletes all Istio resources in the cluster. -func (h *HelmReconciler) DeleteAll() error { - manifestMap := name.ManifestMap{} - for _, c := range name.AllComponentNames { - manifestMap[c] = nil - } - return h.Prune(manifestMap, true) -} - -// SetStatusBegin updates the status field on the IstioOperator instance before reconciling. -func (h *HelmReconciler) SetStatusBegin() error { - isop := &istioV1Alpha1.IstioOperator{} - namespacedName := types.NamespacedName{ - Name: h.iop.Name, - Namespace: h.iop.Namespace, - } - if err := h.getClient().Get(context.TODO(), namespacedName, isop); err != nil { - if runtime.IsNotRegisteredError(err) { - // CRD not yet installed in cluster, nothing to update. - return nil - } - return fmt.Errorf("failed to get IstioOperator before updating status due to %v", err) - } - if isop.Status == nil { - isop.Status = &v1alpha1.InstallStatus{Status: v1alpha1.InstallStatus_RECONCILING} - } else { - cs := isop.Status.ComponentStatus - for cn := range cs { - cs[cn] = &v1alpha1.InstallStatus_VersionStatus{ - Status: v1alpha1.InstallStatus_RECONCILING, - } - } - isop.Status.Status = v1alpha1.InstallStatus_RECONCILING - } - return h.getClient().Status().Update(context.TODO(), isop) -} - -// SetStatusComplete updates the status field on the IstioOperator instance based on the resulting err parameter. -func (h *HelmReconciler) SetStatusComplete(status *v1alpha1.InstallStatus) error { - iop := &istioV1Alpha1.IstioOperator{} - namespacedName := types.NamespacedName{ - Name: h.iop.Name, - Namespace: h.iop.Namespace, - } - if err := h.getClient().Get(context.TODO(), namespacedName, iop); err != nil { - return fmt.Errorf("failed to get IstioOperator before updating status due to %v", err) - } - iop.Status = status - return h.getClient().Status().Update(context.TODO(), iop) -} - -// setStatus sets the status for the component with the given name, which is a key in the given map. -// If the status is InstallStatus_NONE, the component name is deleted from the map. -// Otherwise, if the map key/value is missing, one is created. -func setStatus(s map[string]*v1alpha1.InstallStatus_VersionStatus, componentName name.ComponentName, status v1alpha1.InstallStatus_Status, err error) { - cn := string(componentName) - if status == v1alpha1.InstallStatus_NONE { - delete(s, cn) - return - } - if _, ok := s[cn]; !ok { - s[cn] = &v1alpha1.InstallStatus_VersionStatus{} - } - s[cn].Status = status - if err != nil { - s[cn].Error = err.Error() - } -} - -// overallStatus returns the summary status over all components. -// - If all components are HEALTHY, overall status is HEALTHY. -// - If one or more components are RECONCILING and others are HEALTHY, overall status is RECONCILING. -// - If one or more components are UPDATING and others are HEALTHY, overall status is UPDATING. -// - If components are a mix of RECONCILING, UPDATING and HEALTHY, overall status is UPDATING. -// - If any component is in ERROR state, overall status is ERROR. -func overallStatus(componentStatus map[string]*v1alpha1.InstallStatus_VersionStatus) v1alpha1.InstallStatus_Status { - ret := v1alpha1.InstallStatus_HEALTHY - for _, cs := range componentStatus { - if cs.Status == v1alpha1.InstallStatus_ERROR { - ret = v1alpha1.InstallStatus_ERROR - break - } else if cs.Status == v1alpha1.InstallStatus_UPDATING { - ret = v1alpha1.InstallStatus_UPDATING - break - } else if cs.Status == v1alpha1.InstallStatus_RECONCILING { - ret = v1alpha1.InstallStatus_RECONCILING - break - } - } - return ret -} - -// getCoreOwnerLabels returns a map of labels for associating installation resources. This is the common -// labels shared between all resources; see getOwnerLabels to get labels per-component labels -func (h *HelmReconciler) getCoreOwnerLabels() (map[string]string, error) { - crName, err := h.getCRName() - if err != nil { - return nil, err - } - crNamespace, err := h.getCRNamespace() - if err != nil { - return nil, err - } - labels := make(map[string]string) - - labels[operatorLabelStr] = operatorReconcileStr - if crName != "" { - labels[OwningResourceName] = crName - } - if crNamespace != "" { - labels[OwningResourceNamespace] = crNamespace - } - labels[istioVersionLabelStr] = version.Info.Version - - revision := "" - if h.iop != nil { - revision = h.iop.Spec.Revision - } - if revision == "" { - revision = "default" - } - labels[label.IoIstioRev.Name] = revision - - return labels, nil -} - -func (h *HelmReconciler) addComponentLabels(coreLabels map[string]string, componentName string) map[string]string { - labels := map[string]string{} - for k, v := range coreLabels { - labels[k] = v - } - - labels[IstioComponentLabelStr] = componentName - - return labels -} - -// getOwnerLabels returns a map of labels for the given component name, revision and owning CR resource name. -func (h *HelmReconciler) getOwnerLabels(componentName string) (map[string]string, error) { - labels, err := h.getCoreOwnerLabels() - if err != nil { - return nil, err - } - - return h.addComponentLabels(labels, componentName), nil -} - -// applyLabelsAndAnnotations applies owner labels and annotations to the object. -func (h *HelmReconciler) applyLabelsAndAnnotations(obj runtime.Object, componentName string) error { - labels, err := h.getOwnerLabels(componentName) - if err != nil { - return err - } - - for k, v := range labels { - err := util.SetLabel(obj, k, v) - if err != nil { - return err - } - } - return nil -} - -// getCRName returns the name of the CR associated with h. -func (h *HelmReconciler) getCRName() (string, error) { - if h.iop == nil { - return "", nil - } - objAccessor, err := meta.Accessor(h.iop) - if err != nil { - return "", err - } - return objAccessor.GetName(), nil -} - -// getCRHash returns the cluster unique hash of the CR associated with h. -func (h *HelmReconciler) getCRHash(componentName string) (string, error) { - crName, err := h.getCRName() - if err != nil { - return "", err - } - crNamespace, err := h.getCRNamespace() - if err != nil { - return "", err - } - var host string - if h.kubeClient != nil && h.kubeClient.RESTConfig() != nil { - host = h.kubeClient.RESTConfig().Host - } - return strings.Join([]string{crName, crNamespace, componentName, host}, "-"), nil -} - -// getCRNamespace returns the namespace of the CR associated with h. -func (h *HelmReconciler) getCRNamespace() (string, error) { - if h.iop == nil { - return "", nil - } - objAccessor, err := meta.Accessor(h.iop) - if err != nil { - return "", err - } - return objAccessor.GetNamespace(), nil -} - -// getClient returns the kubernetes client associated with this HelmReconciler -func (h *HelmReconciler) getClient() client.Client { - return h.client -} - -func (h *HelmReconciler) addPrunedKind(gk schema.GroupKind) { - h.countLock.Lock() - defer h.countLock.Unlock() - h.prunedKindSet[gk] = struct{}{} -} - -func (h *HelmReconciler) reportPrunedObjectKind() { - h.countLock.Lock() - defer h.countLock.Unlock() - for gvk := range h.prunedKindSet { - metrics.ResourcePruneTotal. - With(metrics.ResourceKindLabel.Value(util.GKString(gvk))). - Increment() - } -} - -// CreateNamespace creates a namespace using the given k8s interface. -func CreateNamespace(cs kubernetes.Interface, namespace string, network string, dryRun bool) error { - if dryRun { - scope.Infof("Not applying Namespace %s because of dry run.", namespace) - return nil - } - if namespace == "" { - // Setup default namespace - namespace = name.IstioDefaultNamespace - } - // check if the namespace already exists. If yes, do nothing. If no, create a new one. - _, err := cs.CoreV1().Namespaces().Get(context.TODO(), namespace, v12.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - ns := &v1.Namespace{ObjectMeta: v12.ObjectMeta{ - Name: namespace, - Labels: map[string]string{}, - }} - if network != "" { - ns.Labels[label.TopologyNetwork.Name] = network - } - _, err := cs.CoreV1().Namespaces().Create(context.TODO(), ns, v12.CreateOptions{}) - if err != nil { - return fmt.Errorf("failed to create namespace %v: %v", namespace, err) - } - } else { - return fmt.Errorf("failed to check if namespace %v exists: %v", namespace, err) - } - } - - return nil -} - -func (h *HelmReconciler) analyzeWebhooks(whs []string) error { - if len(whs) == 0 { - return nil - } - - sa := local.NewSourceAnalyzer(analysis.Combine("webhook", &webhook.Analyzer{ - SkipServiceCheck: true, - }), - resource.Namespace(h.iop.Spec.GetNamespace()), resource.Namespace(istioV1Alpha1.Namespace(h.iop.Spec)), nil, true, 30*time.Second) - var localWebhookYAMLReaders []local.ReaderSource - var parsedK8sObjects object.K8sObjects - for _, wh := range whs { - k8sObjects, err := object.ParseK8sObjectsFromYAMLManifest(wh) - if err != nil { - return err - } - objYaml, err := k8sObjects.YAMLManifest() - if err != nil { - return err - } - whReaderSource := local.ReaderSource{ - Name: "", - Reader: strings.NewReader(objYaml), - } - localWebhookYAMLReaders = append(localWebhookYAMLReaders, whReaderSource) - parsedK8sObjects = append(parsedK8sObjects, k8sObjects...) - } - err := sa.AddReaderKubeSource(localWebhookYAMLReaders) - if err != nil { - return err - } - - if h.kubeClient != nil { - sa.AddRunningKubeSource(h.kubeClient) - } - - // Analyze webhooks - res, err := sa.Analyze(make(chan struct{})) - if err != nil { - return err - } - relevantMessages := res.Messages.FilterOutBasedOnResources(parsedK8sObjects) - if len(relevantMessages) > 0 { - o, err := formatting.Print(relevantMessages, formatting.LogFormat, false) - if err != nil { - return err - } - return fmt.Errorf("creating default tag would conflict:\n%v", o) - } - return nil -} - -// createNamespace creates a namespace using the given k8s client. -func (h *HelmReconciler) createNamespace(namespace string, network string) error { - return CreateNamespace(h.kubeClient, namespace, network, h.opts.DryRun) -} - -func (h *HelmReconciler) networkName() string { - if h.iop.Spec.GetValues() == nil { - return "" - } - globalI := h.iop.Spec.Values.AsMap()["global"] - global, ok := globalI.(map[string]interface{}) - if !ok { - return "" - } - nw, ok := global["network"].(string) - if !ok { - return "" - } - return nw -} diff --git a/operator/pkg/helmreconciler/render.go b/operator/pkg/helmreconciler/render.go deleted file mode 100644 index 11077afb6..000000000 --- a/operator/pkg/helmreconciler/render.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package helmreconciler - -import ( - "fmt" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/controlplane" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/translate" - "github.com/apache/dubbo-go-pixiu/operator/pkg/validate" -) - -// RenderCharts renders charts for h. -func (h *HelmReconciler) RenderCharts() (name.ManifestMap, error) { - iopSpec := h.iop.Spec - if err := validate.CheckIstioOperatorSpec(iopSpec, false); err != nil { - if !h.opts.Force { - return nil, err - } - h.opts.Log.PrintErr(fmt.Sprintf("spec invalid; continuing because of --force: %v\n", err)) - } - - t := translate.NewTranslator() - ver, err := h.kubeClient.GetKubernetesVersion() - if err != nil { - return nil, err - } - cp, err := controlplane.NewIstioControlPlane(iopSpec, t, nil, ver) - if err != nil { - return nil, err - } - if err := cp.Run(); err != nil { - return nil, fmt.Errorf("failed to create Istio control plane with spec: \n%v\nerror: %s", iopSpec, err) - } - - manifests, errs := cp.RenderManifest() - if errs != nil { - err = errs.ToError() - } - - h.manifests = manifests - - return manifests, err -} diff --git a/operator/pkg/helmreconciler/testdata/configmap-changed.yaml b/operator/pkg/helmreconciler/testdata/configmap-changed.yaml deleted file mode 100644 index 11d93c7fc..000000000 --- a/operator/pkg/helmreconciler/testdata/configmap-changed.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - creationTimestamp: null - name: config - namespace: dubbo-system -data: - # Can't delete a field until we mandate >= Kube 1.16. See ApplyObject comments. - field: two - new: new diff --git a/operator/pkg/helmreconciler/testdata/configmap.yaml b/operator/pkg/helmreconciler/testdata/configmap.yaml deleted file mode 100644 index 48627d903..000000000 --- a/operator/pkg/helmreconciler/testdata/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - creationTimestamp: null - name: config - namespace: dubbo-system -data: - field: one diff --git a/operator/pkg/helmreconciler/testdata/current.yaml b/operator/pkg/helmreconciler/testdata/current.yaml deleted file mode 100644 index badabed96..000000000 --- a/operator/pkg/helmreconciler/testdata/current.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: my-service -spec: - selector: - app: MyApp - ports: - - name: http - protocol: TCP - port: 80 - nodePort: 9376 - - name: https - protocol: TCP - port: 443 - nodePort: 9377 - clusterIP: 10.0.171.239 - type: LoadBalancer diff --git a/operator/pkg/helmreconciler/testdata/overlay.yaml b/operator/pkg/helmreconciler/testdata/overlay.yaml deleted file mode 100644 index 1fb4f7dd1..000000000 --- a/operator/pkg/helmreconciler/testdata/overlay.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: my-service -spec: - selector: - app: MyApp - ports: - - name: http - protocol: TCP - port: 80 - nodePort: 0 - - name: https - protocol: TCP - port: 443 - nodePort: 9379 - type: LoadBalancer diff --git a/operator/pkg/helmreconciler/wait.go b/operator/pkg/helmreconciler/wait.go deleted file mode 100644 index c5d18aa72..000000000 --- a/operator/pkg/helmreconciler/wait.go +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package helmreconciler - -import ( - "context" - "fmt" - "sort" - "strings" - "time" -) - -import ( - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/kubernetes" - kctldeployment "k8s.io/kubectl/pkg/util/deployment" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/progress" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -const ( - // defaultWaitResourceTimeout is the maximum wait time for all resources(namespace/deployment/pod) to be created. - defaultWaitResourceTimeout = 300 * time.Second - // cRDPollInterval is how often the state of CRDs is polled when waiting for their creation. - cRDPollInterval = 500 * time.Millisecond - // cRDPollTimeout is the maximum wait time for all CRDs to be created. - cRDPollTimeout = 60 * time.Second -) - -// deployment holds associated replicaSets for a deployment -type deployment struct { - replicaSets *appsv1.ReplicaSet - deployment *appsv1.Deployment -} - -// WaitForResources polls to get the current status of all pods, PVCs, and Services -// until all are ready or a timeout is reached -func WaitForResources(objects object.K8sObjects, client kube.Client, - waitTimeout time.Duration, dryRun bool, l *progress.ManifestLog) error { - if dryRun || TestMode { - return nil - } - - if err := waitForCRDs(objects, client); err != nil { - return err - } - - var notReady []string - var debugInfo map[string]string - - // Check if we are ready immediately, to avoid the 2s delay below when we are already redy - if ready, _, _, err := waitForResources(objects, client, l); err == nil && ready { - return nil - } - - errPoll := wait.Poll(2*time.Second, waitTimeout, func() (bool, error) { - isReady, notReadyObjects, debugInfoObjects, err := waitForResources(objects, client, l) - notReady = notReadyObjects - debugInfo = debugInfoObjects - return isReady, err - }) - - messages := []string{} - for _, id := range notReady { - debug, f := debugInfo[id] - if f { - messages = append(messages, fmt.Sprintf(" %s (%s)", id, debug)) - } else { - messages = append(messages, fmt.Sprintf(" %s", debug)) - } - } - if errPoll != nil { - msg := fmt.Sprintf("resources not ready after %v: %v\n%s", waitTimeout, errPoll, strings.Join(messages, "\n")) - return fmt.Errorf(msg) - } - return nil -} - -func waitForResources(objects object.K8sObjects, cs kubernetes.Interface, l *progress.ManifestLog) (bool, []string, map[string]string, error) { - pods := []corev1.Pod{} - deployments := []deployment{} - daemonsets := []*appsv1.DaemonSet{} - statefulsets := []*appsv1.StatefulSet{} - namespaces := []corev1.Namespace{} - - for _, o := range objects { - kind := o.GroupVersionKind().Kind - switch kind { - case name.NamespaceStr: - namespace, err := cs.CoreV1().Namespaces().Get(context.TODO(), o.Name, metav1.GetOptions{}) - if err != nil { - return false, nil, nil, err - } - namespaces = append(namespaces, *namespace) - case name.DeploymentStr: - currentDeployment, err := cs.AppsV1().Deployments(o.Namespace).Get(context.TODO(), o.Name, metav1.GetOptions{}) - if err != nil { - return false, nil, nil, err - } - _, _, newReplicaSet, err := kctldeployment.GetAllReplicaSets(currentDeployment, cs.AppsV1()) - if err != nil || newReplicaSet == nil { - return false, nil, nil, err - } - newDeployment := deployment{ - newReplicaSet, - currentDeployment, - } - deployments = append(deployments, newDeployment) - case name.DaemonSetStr: - ds, err := cs.AppsV1().DaemonSets(o.Namespace).Get(context.TODO(), o.Name, metav1.GetOptions{}) - if err != nil { - return false, nil, nil, err - } - daemonsets = append(daemonsets, ds) - case name.StatefulSetStr: - sts, err := cs.AppsV1().StatefulSets(o.Namespace).Get(context.TODO(), o.Name, metav1.GetOptions{}) - if err != nil { - return false, nil, nil, err - } - statefulsets = append(statefulsets, sts) - } - } - - resourceDebugInfo := map[string]string{} - dr, dnr := deploymentsReady(cs, deployments, resourceDebugInfo) - dsr, dsnr := daemonsetsReady(daemonsets) - stsr, stsnr := statefulsetsReady(statefulsets) - nsr, nnr := namespacesReady(namespaces) - pr, pnr := podsReady(pods) - isReady := dr && nsr && dsr && stsr && pr - notReady := append(append(append(append(nnr, dnr...), pnr...), dsnr...), stsnr...) - if !isReady { - l.ReportWaiting(notReady) - } - return isReady, notReady, resourceDebugInfo, nil -} - -func waitForCRDs(objects object.K8sObjects, client kube.Client) error { - var crdNames []string - for _, o := range object.KindObjects(objects, name.CRDStr) { - crdNames = append(crdNames, o.Name) - } - if len(crdNames) == 0 { - return nil - } - - errPoll := wait.Poll(cRDPollInterval, cRDPollTimeout, func() (bool, error) { - descriptor: - for _, crdName := range crdNames { - crd, errGet := client.Ext().ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), crdName, metav1.GetOptions{}) - if errGet != nil { - return false, errGet - } - for _, cond := range crd.Status.Conditions { - switch cond.Type { - case apiextensions.Established: - if cond.Status == apiextensions.ConditionTrue { - scope.Infof("established CRD %s", crdName) - continue descriptor - } - case apiextensions.NamesAccepted: - if cond.Status == apiextensions.ConditionFalse { - scope.Warnf("name conflict for %v: %v", crdName, cond.Reason) - } - } - } - scope.Infof("missing status condition for %q", crdName) - return false, nil - } - return true, nil - }) - - if errPoll != nil { - scope.Errorf("failed to verify CRD creation; %s", errPoll) - return fmt.Errorf("failed to verify CRD creation: %s", errPoll) - } - - scope.Info("Finished applying CRDs.") - return nil -} - -func getPods(client kubernetes.Interface, namespace string, selector labels.Selector) ([]corev1.Pod, error) { - list, err := client.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{ - LabelSelector: selector.String(), - }) - return list.Items, err -} - -func namespacesReady(namespaces []corev1.Namespace) (bool, []string) { - var notReady []string - for _, namespace := range namespaces { - if namespace.Status.Phase != corev1.NamespaceActive { - notReady = append(notReady, "Namespace/"+namespace.Name) - } - } - return len(notReady) == 0, notReady -} - -func podsReady(pods []corev1.Pod) (bool, []string) { - var notReady []string - for _, pod := range pods { - if !isPodReady(&pod) { - notReady = append(notReady, "Pod/"+pod.Namespace+"/"+pod.Name) - } - } - return len(notReady) == 0, notReady -} - -func isPodReady(pod *corev1.Pod) bool { - if len(pod.Status.Conditions) > 0 { - for _, condition := range pod.Status.Conditions { - if condition.Type == corev1.PodReady && - condition.Status == corev1.ConditionTrue { - return true - } - } - } - return false -} - -func deploymentsReady(cs kubernetes.Interface, deployments []deployment, info map[string]string) (bool, []string) { - var notReady []string - for _, v := range deployments { - if v.replicaSets.Status.ReadyReplicas >= *v.deployment.Spec.Replicas { - // Ready - continue - } - id := "Deployment/" + v.deployment.Namespace + "/" + v.deployment.Name - notReady = append(notReady, id) - failure := extractPodFailureReason(cs, v.deployment.Namespace, v.deployment.Spec.Selector) - if failure != "" { - info[id] = failure - } - } - return len(notReady) == 0, notReady -} - -func extractPodFailureReason(client kubernetes.Interface, namespace string, selector *metav1.LabelSelector) string { - sel, err := metav1.LabelSelectorAsSelector(selector) - if err != nil { - return fmt.Sprintf("failed to get label selector: %v", err) - } - pods, err := getPods(client, namespace, sel) - if err != nil { - return fmt.Sprintf("failed to fetch pods: %v", err) - } - sort.Slice(pods, func(i, j int) bool { - return pods[i].CreationTimestamp.After(pods[j].CreationTimestamp.Time) - }) - for _, pod := range pods { - for _, cs := range pod.Status.ContainerStatuses { - if cs.State.Waiting != nil { - return fmt.Sprintf("container failed to start: %v: %v", cs.State.Waiting.Reason, cs.State.Waiting.Message) - } - } - if c := getCondition(pod.Status.Conditions, corev1.PodReady); c != nil && c.Status == corev1.ConditionFalse { - return fmt.Sprintf(c.Message) - } - } - return "" -} - -func getCondition(conditions []corev1.PodCondition, condition corev1.PodConditionType) *corev1.PodCondition { - for _, cond := range conditions { - if cond.Type == condition { - return &cond - } - } - return nil -} - -func daemonsetsReady(daemonsets []*appsv1.DaemonSet) (bool, []string) { - var notReady []string - for _, ds := range daemonsets { - // Check if the wanting generation is same as the observed generation - // Only when the observed generation is the same as the generation, - // other checks will make sense. If not the same, daemon set is not - // ready - if ds.Status.ObservedGeneration != ds.Generation { - scope.Infof("DaemonSet is not ready: %s/%s. Observed generation: %d expected generation: %d", - ds.Namespace, ds.Name, ds.Status.ObservedGeneration, ds.Generation) - notReady = append(notReady, "DaemonSet/"+ds.Namespace+"/"+ds.Name) - } else { - // Make sure all the updated pods have been scheduled - if ds.Spec.UpdateStrategy.Type == appsv1.OnDeleteDaemonSetStrategyType && - ds.Status.UpdatedNumberScheduled != ds.Status.DesiredNumberScheduled { - scope.Infof("DaemonSet is not ready: %s/%s. %d out of %d expected pods have been scheduled", - ds.Namespace, ds.Name, ds.Status.UpdatedNumberScheduled, ds.Status.DesiredNumberScheduled) - notReady = append(notReady, "DaemonSet/"+ds.Namespace+"/"+ds.Name) - } - if ds.Spec.UpdateStrategy.Type == appsv1.RollingUpdateDaemonSetStrategyType { - if ds.Status.DesiredNumberScheduled <= 0 { - // If DesiredNumberScheduled less then or equal 0, there some cases: - // 1) daemenset is just created - // 2) daemonset desired no pod - // 3) somebody changed it manually - // All the case is not a ready signal - scope.Infof("DaemonSet is not ready: %s/%s. Initializing, no pods is running", - ds.Namespace, ds.Name) - notReady = append(notReady, "DaemonSet/"+ds.Namespace+"/"+ds.Name) - } else if ds.Status.NumberReady < ds.Status.DesiredNumberScheduled { - // Make sure every node has a ready pod - scope.Infof("DaemonSet is not ready: %s/%s. %d out of %d expected pods are ready", - ds.Namespace, ds.Name, ds.Status.NumberReady, ds.Status.UpdatedNumberScheduled) - notReady = append(notReady, "DaemonSet/"+ds.Namespace+"/"+ds.Name) - } - } - } - } - return len(notReady) == 0, notReady -} - -func statefulsetsReady(statefulsets []*appsv1.StatefulSet) (bool, []string) { - var notReady []string - for _, sts := range statefulsets { - // Make sure all the updated pods have been scheduled - if sts.Spec.UpdateStrategy.Type == appsv1.OnDeleteStatefulSetStrategyType && - sts.Status.UpdatedReplicas != sts.Status.Replicas { - scope.Infof("StatefulSet is not ready: %s/%s. %d out of %d expected pods have been scheduled", - sts.Namespace, sts.Name, sts.Status.UpdatedReplicas, sts.Status.Replicas) - notReady = append(notReady, "StatefulSet/"+sts.Namespace+"/"+sts.Name) - } - if sts.Spec.UpdateStrategy.Type == appsv1.RollingUpdateStatefulSetStrategyType { - // Dereference all the pointers because StatefulSets like them - var partition int - // default replicasfor sts is 1 - replicas := 1 - // the rollingUpdate field can be nil even if the update strategy is a rolling update. - if sts.Spec.UpdateStrategy.RollingUpdate != nil && - sts.Spec.UpdateStrategy.RollingUpdate.Partition != nil { - partition = int(*sts.Spec.UpdateStrategy.RollingUpdate.Partition) - } - if sts.Spec.Replicas != nil { - replicas = int(*sts.Spec.Replicas) - } - expectedReplicas := replicas - partition - // Make sure all the updated pods have been scheduled - if int(sts.Status.UpdatedReplicas) != expectedReplicas { - scope.Infof("StatefulSet is not ready: %s/%s. %d out of %d expected pods have been scheduled", - sts.Namespace, sts.Name, sts.Status.UpdatedReplicas, expectedReplicas) - notReady = append(notReady, "StatefulSet/"+sts.Namespace+"/"+sts.Name) - continue - } - } - } - return len(notReady) == 0, notReady -} diff --git a/operator/pkg/httprequest/httprequest.go b/operator/pkg/httprequest/httprequest.go deleted file mode 100644 index de7cdd6d2..000000000 --- a/operator/pkg/httprequest/httprequest.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package httprequest - -import ( - "fmt" - "io" - "net/http" -) - -// Get sends an HTTP GET request and returns the result. -func Get(url string) ([]byte, error) { - resp, err := http.Get(url) - if err != nil { - return nil, err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("failed to fetch URL %s : %s", url, resp.Status) - } - ret, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - return ret, nil -} diff --git a/operator/pkg/httprequest/httprequest_test.go b/operator/pkg/httprequest/httprequest_test.go deleted file mode 100644 index 1d127962d..000000000 --- a/operator/pkg/httprequest/httprequest_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package httprequest - -import ( - "net/http" - "net/http/httptest" - "testing" -) - -func TestGet(t *testing.T) { - tests := []struct { - desc string - expectedData string - expectedRoute string - }{ - { - desc: "test-get", - expectedData: "fooey-baroque", - expectedRoute: "/fooey/baroque/cazoo", - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - testServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - if req.URL.String() != tt.expectedRoute { - t.Errorf("%s: request made to wrong URL, got %s, want %s", tt.desc, req.URL.String(), tt.expectedRoute) - } - rw.Write([]byte(tt.expectedData)) - })) - defer testServer.Close() - response, err := Get(testServer.URL + tt.expectedRoute) - if err != nil { - t.Errorf("Unexpected Error In Making Request: %s", err.Error()) - } - if tt.expectedData != string(response) { - t.Errorf("Returned unexpected response, want: %s, got: %s", tt.expectedData, string(response)) - } - }) - } -} diff --git a/operator/pkg/manifest/shared.go b/operator/pkg/manifest/shared.go deleted file mode 100644 index caae8dda4..000000000 --- a/operator/pkg/manifest/shared.go +++ /dev/null @@ -1,577 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package manifest - -import ( - "fmt" - "io" - "os" - "path/filepath" - "strings" -) - -import ( - "istio.io/api/operator/v1alpha1" - "istio.io/pkg/log" - pkgversion "istio.io/pkg/version" - "k8s.io/apimachinery/pkg/version" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio" - iopv1alpha1 "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1/validation" - "github.com/apache/dubbo-go-pixiu/operator/pkg/controlplane" - "github.com/apache/dubbo-go-pixiu/operator/pkg/helm" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" - "github.com/apache/dubbo-go-pixiu/operator/pkg/tpath" - "github.com/apache/dubbo-go-pixiu/operator/pkg/translate" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" - "github.com/apache/dubbo-go-pixiu/operator/pkg/validate" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/url" -) - -// installerScope is the scope for shared manifest package. -var installerScope = log.RegisterScope("installer", "installer", 0) - -// GenManifests generates a manifest map, keyed by the component name, from input file list and a YAML tree -// representation of path-values passed through the --set flag. -// If force is set, validation errors will not cause processing to abort but will result in warnings going to the -// supplied logger. -func GenManifests(inFilename []string, setFlags []string, force bool, filter []string, - client kube.Client, l clog.Logger) (name.ManifestMap, *iopv1alpha1.IstioOperator, error) { - mergedYAML, _, err := GenerateConfig(inFilename, setFlags, force, client, l) - if err != nil { - return nil, nil, err - } - mergedIOPS, err := unmarshalAndValidateIOP(mergedYAML, force, false, l) - if err != nil { - return nil, nil, err - } - - t := translate.NewTranslator() - var ver *version.Info - if client != nil { - ver, err = client.GetKubernetesVersion() - if err != nil { - return nil, nil, err - } - } - cp, err := controlplane.NewIstioControlPlane(mergedIOPS.Spec, t, filter, ver) - if err != nil { - return nil, nil, err - } - if err := cp.Run(); err != nil { - return nil, nil, err - } - - manifests, errs := cp.RenderManifest() - if errs != nil { - return manifests, mergedIOPS, errs.ToError() - } - return manifests, mergedIOPS, nil -} - -// GenerateConfig creates an IstioOperatorSpec from the following sources, overlaid sequentially: -// 1. Compiled in base, or optionally base from paths pointing to one or multiple ICP/IOP files at inFilenames. -// 2. Profile overlay, if non-default overlay is selected. This also comes either from compiled in or path specified in IOP contained in inFilenames. -// 3. User overlays stored in inFilenames. -// 4. setOverlayYAML, which comes from --set flag passed to manifest command. -// -// Note that the user overlay at inFilenames can optionally contain a file path to a set of profiles different from the -// ones that are compiled in. If it does, the starting point will be the base and profile YAMLs at that file path. -// Otherwise it will be the compiled in profile YAMLs. -// In step 3, the remaining fields in the same user overlay are applied on the resulting profile base. -// The force flag causes validation errors not to abort but only emit log/console warnings. -func GenerateConfig(inFilenames []string, setFlags []string, force bool, client kube.Client, - l clog.Logger) (string, *iopv1alpha1.IstioOperator, error) { - if err := validateSetFlags(setFlags); err != nil { - return "", nil, err - } - - fy, profile, err := ReadYamlProfile(inFilenames, setFlags, force, l) - if err != nil { - return "", nil, err - } - - return OverlayYAMLStrings(profile, fy, setFlags, force, client, l) -} - -func OverlayYAMLStrings(profile string, fy string, - setFlags []string, force bool, client kube.Client, l clog.Logger) (string, *iopv1alpha1.IstioOperator, error) { - iopsString, iops, err := GenIOPFromProfile(profile, fy, setFlags, force, false, client, l) - if err != nil { - return "", nil, err - } - - errs, warning := validation.ValidateConfig(false, iops.Spec) - if warning != "" { - l.LogAndError(warning) - } - - if errs.ToError() != nil { - return "", nil, fmt.Errorf("generated config failed semantic validation: %v", errs) - } - return iopsString, iops, nil -} - -// GenIOPFromProfile generates an IstioOperator from the given profile name or path, and overlay YAMLs from user -// files and the --set flag. If successful, it returns an IstioOperator string and struct. -func GenIOPFromProfile(profileOrPath, fileOverlayYAML string, setFlags []string, skipValidation, allowUnknownField bool, - client kube.Client, l clog.Logger) (string, *iopv1alpha1.IstioOperator, error) { - installPackagePath, err := getInstallPackagePath(fileOverlayYAML) - if err != nil { - return "", nil, err - } - if sfp := GetValueForSetFlag(setFlags, "installPackagePath"); sfp != "" { - // set flag installPackagePath has the highest precedence, if set. - installPackagePath = sfp - } - - // If installPackagePath is a URL, fetch and extract it and continue with the local filesystem path instead. - installPackagePath, profileOrPath, err = RewriteURLToLocalInstallPath(installPackagePath, profileOrPath, skipValidation) - if err != nil { - return "", nil, err - } - - // To generate the base profileOrPath for overlaying with user values, we need the installPackagePath where the profiles - // can be found, and the selected profileOrPath. Both of these can come from either the user overlay file or --set flag. - outYAML, err := helm.GetProfileYAML(installPackagePath, profileOrPath) - if err != nil { - return "", nil, err - } - - // Hub and tag are only known at build time and must be passed in here during runtime from build stamps. - outYAML, err = overlayHubAndTag(outYAML) - if err != nil { - return "", nil, err - } - - // Merge k8s specific values. - if client != nil { - kubeOverrides, err := getClusterSpecificValues(client, skipValidation, l) - if err != nil { - return "", nil, err - } - installerScope.Infof("Applying Cluster specific settings: %v", kubeOverrides) - outYAML, err = util.OverlayYAML(outYAML, kubeOverrides) - if err != nil { - return "", nil, err - } - } - - // Combine file and --set overlays and translate any K8s settings in values to IOP format. Users should not set - // these but we have to support this path until it's deprecated. - overlayYAML, err := overlaySetFlagValues(fileOverlayYAML, setFlags) - if err != nil { - return "", nil, err - } - t := translate.NewReverseTranslator() - overlayYAML, err = t.TranslateK8SfromValueToIOP(overlayYAML) - if err != nil { - return "", nil, fmt.Errorf("could not overlay k8s settings from values to IOP: %s", err) - } - - // Merge user file and --set flags. - outYAML, err = util.OverlayIOP(outYAML, overlayYAML) - if err != nil { - return "", nil, fmt.Errorf("could not overlay user config over base: %s", err) - } - - // If enablement came from user values overlay (file or --set), translate into addonComponents paths and overlay that. - outYAML, err = translate.OverlayValuesEnablement(outYAML, overlayYAML, overlayYAML) - if err != nil { - return "", nil, err - } - - finalIOP, err := unmarshalAndValidateIOP(outYAML, skipValidation, allowUnknownField, l) - if err != nil { - return "", nil, err - } - - // Validate Final IOP config against K8s cluster - if client != nil { - err = util.ValidateIOPCAConfig(client, finalIOP) - if err != nil { - return "", nil, err - } - } - // InstallPackagePath may have been a URL, change to extracted to local file path. - finalIOP.Spec.InstallPackagePath = installPackagePath - if ns := GetValueForSetFlag(setFlags, "values.global.istioNamespace"); ns != "" { - finalIOP.Namespace = ns - } - if finalIOP.Spec.Profile == "" { - finalIOP.Spec.Profile = name.DefaultProfileName - } - return util.MustToYAMLGeneric(finalIOP), finalIOP, nil -} - -// ReadYamlProfile gets the overlay yaml file from list of files and return profile value from file overlay and set overlay. -func ReadYamlProfile(inFilenames []string, setFlags []string, force bool, l clog.Logger) (string, string, error) { - profile := name.DefaultProfileName - // Get the overlay YAML from the list of files passed in. Also get the profile from the overlay files. - fy, fp, err := ParseYAMLFiles(inFilenames, force, l) - if err != nil { - return "", "", err - } - if fp != "" { - profile = fp - } - // The profile coming from --set flag has the highest precedence. - psf := GetValueForSetFlag(setFlags, "profile") - if psf != "" { - profile = psf - } - return fy, profile, nil -} - -// ParseYAMLFiles parses the given slice of filenames containing YAML and merges them into a single IstioOperator -// format YAML strings. It returns the overlay YAML, the profile name and error result. -func ParseYAMLFiles(inFilenames []string, force bool, l clog.Logger) (overlayYAML string, profile string, err error) { - if inFilenames == nil { - return "", "", nil - } - y, err := ReadLayeredYAMLs(inFilenames) - if err != nil { - return "", "", err - } - var fileOverlayIOP *iopv1alpha1.IstioOperator - fileOverlayIOP, err = validate.UnmarshalIOP(y) - if err != nil { - return "", "", err - } - if err := validate.ValidIOP(fileOverlayIOP); err != nil { - if !force { - return "", "", fmt.Errorf("validation errors (use --force to override): \n%s", err) - } - l.LogAndErrorf("Validation errors (continuing because of --force):\n%s", err) - } - if fileOverlayIOP.Spec != nil && fileOverlayIOP.Spec.Profile != "" { - if profile != "" && profile != fileOverlayIOP.Spec.Profile { - return "", "", fmt.Errorf("different profiles cannot be overlaid") - } - profile = fileOverlayIOP.Spec.Profile - } - return y, profile, nil -} - -func ReadLayeredYAMLs(filenames []string) (string, error) { - return readLayeredYAMLs(filenames, os.Stdin) -} - -func readLayeredYAMLs(filenames []string, stdinReader io.Reader) (string, error) { - var ly string - var stdin bool - for _, fn := range filenames { - var b []byte - var err error - if fn == "-" { - if stdin { - continue - } - stdin = true - b, err = io.ReadAll(stdinReader) - } else { - b, err = os.ReadFile(strings.TrimSpace(fn)) - } - if err != nil { - return "", err - } - multiple := false - multiple, err = hasMultipleIOPs(string(b)) - if err != nil { - return "", err - } - if multiple { - return "", fmt.Errorf("input file %s contains multiple IstioOperator CRs, only one per file is supported", fn) - } - ly, err = util.OverlayIOP(ly, string(b)) - if err != nil { - return "", err - } - } - return ly, nil -} - -func hasMultipleIOPs(s string) (bool, error) { - objs, err := object.ParseK8sObjectsFromYAMLManifest(s) - if err != nil { - return false, err - } - found := false - for _, o := range objs { - if o.Kind == name.IstioOperator { - if found { - return true, nil - } - found = true - } - } - return false, nil -} - -func GetProfile(iop *iopv1alpha1.IstioOperator) string { - profile := "default" - if iop != nil && iop.Spec != nil && iop.Spec.Profile != "" { - profile = iop.Spec.Profile - } - return profile -} - -func GetMergedIOP(userIOPStr, profile, manifestsPath, revision string, client kube.Client, - logger clog.Logger) (*iopv1alpha1.IstioOperator, error) { - extraFlags := make([]string, 0) - if manifestsPath != "" { - extraFlags = append(extraFlags, fmt.Sprintf("installPackagePath=%s", manifestsPath)) - } - if revision != "" { - extraFlags = append(extraFlags, fmt.Sprintf("revision=%s", revision)) - } - _, mergedIOP, err := OverlayYAMLStrings(profile, userIOPStr, extraFlags, false, client, logger) - if err != nil { - return nil, err - } - return mergedIOP, nil -} - -// validateSetFlags validates that setFlags all have path=value format. -func validateSetFlags(setFlags []string) error { - for _, sf := range setFlags { - pv := strings.Split(sf, "=") - if len(pv) != 2 { - return fmt.Errorf("set flag %s has incorrect format, must be path=value", sf) - } - } - return nil -} - -// fetchExtractInstallPackageHTTP downloads installation tar from the URL specified and extracts it to a local -// filesystem dir. If successful, it returns the path to the filesystem path where the charts were extracted. -func fetchExtractInstallPackageHTTP(releaseTarURL string) (string, error) { - uf := helm.NewURLFetcher(releaseTarURL, "") - if err := uf.Fetch(); err != nil { - return "", err - } - return uf.DestDir(), nil -} - -// RewriteURLToLocalInstallPath checks installPackagePath and if it is a URL, it tries to download and extract the -// Istio release tar at the URL to a local file path. If successful, it returns the resulting local paths to the -// installation charts and profile file. -// If installPackagePath is not a URL, it returns installPackagePath and profileOrPath unmodified. -func RewriteURLToLocalInstallPath(installPackagePath, profileOrPath string, skipValidation bool) (string, string, error) { - isURL, err := util.IsHTTPURL(installPackagePath) - if err != nil && !skipValidation { - return "", "", err - } - if isURL { - installPackagePath, err = fetchExtractInstallPackageHTTP(installPackagePath) - if err != nil { - return "", "", err - } - // Transform a profileOrPath like "default" or "demo" into a filesystem path like - // /tmp/istio-install-packages/istio-1.5.1/manifests/profiles/default.yaml OR - // /tmp/istio-install-packages/istio-1.5.1/install/kubernetes/operator/profiles/default.yaml (before 1.6). - baseDir := filepath.Join(installPackagePath, helm.OperatorSubdirFilePath15) - if _, err := os.Stat(baseDir); os.IsNotExist(err) { - baseDir = filepath.Join(installPackagePath, helm.OperatorSubdirFilePath) - } - profileOrPath = filepath.Join(baseDir, "profiles", profileOrPath+".yaml") - // Rewrite installPackagePath to the local file path for further processing. - installPackagePath = baseDir - } - - return installPackagePath, profileOrPath, nil -} - -// Due to the fact that base profile is compiled in before a tag can be created, we must allow an additional -// override from variables that are set during release build time. -func overlayHubAndTag(yml string) (string, error) { - hub := pkgversion.DockerInfo.Hub - tag := pkgversion.DockerInfo.Tag - out := yml - if hub != "unknown" && tag != "unknown" { - buildHubTagOverlayYAML, err := helm.GenerateHubTagOverlay(hub, tag) - if err != nil { - return "", err - } - out, err = util.OverlayYAML(yml, buildHubTagOverlayYAML) - if err != nil { - return "", err - } - } - return out, nil -} - -func getClusterSpecificValues(client kube.Client, force bool, l clog.Logger) (string, error) { - overlays := []string{} - - fsgroup := getFSGroupOverlay(client) - if fsgroup != "" { - overlays = append(overlays, fsgroup) - } - jwt, err := getJwtTypeOverlay(client, l) - if err != nil { - if force { - l.LogAndPrint(err) - } else { - return "", err - } - } else { - overlays = append(overlays, jwt) - } - return makeTreeFromSetList(overlays) -} - -func getFSGroupOverlay(config kube.Client) string { - if kube.IsAtLeastVersion(config, 19) { - return "values.pilot.env.ENABLE_LEGACY_FSGROUP_INJECTION=false" - } - return "" -} - -// makeTreeFromSetList creates a YAML tree from a string slice containing key-value pairs in the format key=value. -func makeTreeFromSetList(setOverlay []string) (string, error) { - if len(setOverlay) == 0 { - return "", nil - } - tree := make(map[string]interface{}) - for _, kv := range setOverlay { - kvv := strings.Split(kv, "=") - if len(kvv) != 2 { - return "", fmt.Errorf("bad argument %s: expect format key=value", kv) - } - k := kvv[0] - v := util.ParseValue(kvv[1]) - if err := tpath.WriteNode(tree, util.PathFromString(k), v); err != nil { - return "", err - } - // To make errors more user friendly, test the path and error out immediately if we cannot unmarshal. - testTree, err := yaml.Marshal(tree) - if err != nil { - return "", err - } - iops := &v1alpha1.IstioOperatorSpec{} - if err := util.UnmarshalWithJSONPB(string(testTree), iops, false); err != nil { - return "", fmt.Errorf("bad path=value %s: %v", kv, err) - } - } - out, err := yaml.Marshal(tree) - if err != nil { - return "", err - } - return tpath.AddSpecRoot(string(out)) -} - -func getJwtTypeOverlay(client kube.Client, l clog.Logger) (string, error) { - jwtPolicy, err := util.DetectSupportedJWTPolicy(client) - if err != nil { - return "", fmt.Errorf("failed to determine JWT policy support. Use the --force flag to ignore this: %v", err) - } - if jwtPolicy == util.FirstPartyJWT { - // nolint: lll - l.LogAndPrint("Detected that your cluster does not support third party JWT authentication. " + - "Falling back to less secure first party JWT. See " + url.ConfigureSAToken + " for details.") - } - return "values.global.jwtPolicy=" + string(jwtPolicy), nil -} - -// unmarshalAndValidateIOP unmarshals a string containing IstioOperator YAML, validates it, and returns a struct -// representation if successful. If force is set, validation errors are written to logger rather than causing an -// error. -func unmarshalAndValidateIOP(iopsYAML string, force, allowUnknownField bool, l clog.Logger) (*iopv1alpha1.IstioOperator, error) { - iop, err := istio.UnmarshalIstioOperator(iopsYAML, allowUnknownField) - if err != nil { - return nil, fmt.Errorf("could not unmarshal merged YAML: %s\n\nYAML:\n%s", err, iopsYAML) - } - if errs := validate.CheckIstioOperatorSpec(iop.Spec, true); len(errs) != 0 && !force { - l.LogAndError("Run the command with the --force flag if you want to ignore the validation error and proceed.") - return iop, fmt.Errorf(errs.Error()) - } - return iop, nil -} - -// getInstallPackagePath returns the installPackagePath in the given IstioOperator YAML string. -func getInstallPackagePath(iopYAML string) (string, error) { - iop, err := validate.UnmarshalIOP(iopYAML) - if err != nil { - return "", err - } - if iop.Spec == nil { - return "", nil - } - return iop.Spec.InstallPackagePath, nil -} - -// overlaySetFlagValues overlays each of the setFlags on top of the passed in IOP YAML string. -func overlaySetFlagValues(iopYAML string, setFlags []string) (string, error) { - iop := make(map[string]interface{}) - if err := yaml.Unmarshal([]byte(iopYAML), &iop); err != nil { - return "", err - } - // Unmarshal returns nil for empty manifests but we need something to insert into. - if iop == nil { - iop = make(map[string]interface{}) - } - - for _, sf := range setFlags { - p, v := getPV(sf) - p = strings.TrimPrefix(p, "spec.") - inc, _, err := tpath.GetPathContext(iop, util.PathFromString("spec."+p), true) - if err != nil { - return "", err - } - // input value type is always string, transform it to correct type before setting. - if err := tpath.WritePathContext(inc, util.ParseValue(v), false); err != nil { - return "", err - } - } - - out, err := yaml.Marshal(iop) - if err != nil { - return "", err - } - - return string(out), nil -} - -// GetValueForSetFlag parses the passed set flags which have format key=value and if any set the given path, -// returns the corresponding value, otherwise returns the empty string. setFlags must have valid format. -func GetValueForSetFlag(setFlags []string, path string) string { - ret := "" - for _, sf := range setFlags { - p, v := getPV(sf) - if p == path { - ret = v - } - // if set multiple times, return last set value - } - return ret -} - -// getPV returns the path and value components for the given set flag string, which must be in path=value format. -func getPV(setFlag string) (path string, value string) { - pv := strings.Split(setFlag, "=") - if len(pv) != 2 { - return setFlag, "" - } - path, value = strings.TrimSpace(pv[0]), strings.TrimSpace(pv[1]) - return -} diff --git a/operator/pkg/manifest/shared_test.go b/operator/pkg/manifest/shared_test.go deleted file mode 100644 index 16a5a46aa..000000000 --- a/operator/pkg/manifest/shared_test.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package manifest - -import ( - "bytes" - "fmt" - "os" - "path/filepath" - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" -) - -func TestReadLayeredYAMLs(t *testing.T) { - testDataDir := filepath.Join(env.IstioSrc, "operator/pkg/util/testdata/yaml") - tests := []struct { - name string - overlays []string - wantErr bool - stdin bool - }{ - { - name: "layer1", - overlays: []string{"yaml_layer1"}, - wantErr: false, - }, - { - name: "layer1_stdin", - overlays: []string{"yaml_layer1"}, - wantErr: false, - stdin: true, - }, - { - name: "layer1_2", - overlays: []string{"yaml_layer1", "yaml_layer2"}, - wantErr: false, - }, - { - name: "layer1_2_3", - overlays: []string{"yaml_layer1", "yaml_layer2", "yaml_layer3"}, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(fmt.Sprintf("%s stdin=%v", tt.name, tt.stdin), func(t *testing.T) { - inDir := filepath.Join(testDataDir, "input") - outPath := filepath.Join(testDataDir, "output", tt.name+".yaml") - wantBytes, err := os.ReadFile(outPath) - want := string(wantBytes) - if err != nil { - t.Errorf("os.ReadFile() error = %v, filename: %v", err, outPath) - } - - stdinReader := &bytes.Buffer{} - - var filenames []string - for _, ol := range tt.overlays { - filename := filepath.Join(inDir, ol+".yaml") - if tt.stdin { - b, err := os.ReadFile(filename) - if err != nil { - t.Fatalf("os.ReadFile() error = %v, filenaem: %v", err, filename) - } - if _, err := stdinReader.Write(b); err != nil { - t.Fatalf("failed to populate fake sdtin") - } - filenames = append(filenames, "-") - } else { - filenames = append(filenames, filename) - } - } - got, err := readLayeredYAMLs(filenames, stdinReader) - if (err != nil) != tt.wantErr { - t.Errorf("ReadLayeredYAMLs() error = %v, wantErr %v", err, tt.wantErr) - return - } - - diff := util.YAMLDiff(got, want) - if diff != "" { - t.Errorf("ReadLayeredYAMLs() got:\n%s\nwant:\n%s\ndiff:\n%s", got, want, diff) - } - }) - } -} diff --git a/operator/pkg/metrics/monitoring.go b/operator/pkg/metrics/monitoring.go deleted file mode 100644 index e14218aaf..000000000 --- a/operator/pkg/metrics/monitoring.go +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package metrics defines metrics and monitoring functionality -// used throughout operator. -package metrics - -import ( - "istio.io/pkg/monitoring" -) - -var ( - // OperatorVersionLabel describes version of running binary. - OperatorVersionLabel = monitoring.MustCreateLabel("version") - - // MergeErrorLabel describes the type of merge error. - MergeErrorLabel = monitoring.MustCreateLabel("error_type") - - // RenderErrorLabel describes the type of the error while rendering. - RenderErrorLabel = monitoring.MustCreateLabel("render_error") - - // CRFetchErrorReasonLabel describes the reason/HTTP code - // for failing to fetch CR. - CRFetchErrorReasonLabel = monitoring.MustCreateLabel("reason") - - // ComponentNameLabel represents istio component name - like - // core, pilot, istio-cni etc. - ComponentNameLabel = monitoring.MustCreateLabel("component") - - // ResourceKindLabel indicates the kind of resource owned - // or created or updated or deleted or pruned by operator. - ResourceKindLabel = monitoring.MustCreateLabel("kind") -) - -// MergeErrorType describes the class of errors that could -// occur while merging profile, user supplied YAML, values -// overridden by --set and so on. -type MergeErrorType string - -const ( - // CannotFetchProfileError occurs when profile cannot be found. - CannotFetchProfileError MergeErrorType = "cannot_fetch_profile" - - // OverlayError overlaying YAMLs to combine profile, user - // defined settings in CR, Hub-tag etc fails. - OverlayError MergeErrorType = "overlay" - - // IOPFormatError occurs when supplied CR cannot be marshaled - // or unmarshaled to/from YAML. - IOPFormatError MergeErrorType = "iop_format" - - // TranslateValuesError occurs when translating from legacy API fails. - TranslateValuesError MergeErrorType = "translate_values" - - // InternalYAMLParseError occurs when spec section in merged CR - // cannot be accessed for some reason (either missing or multiple). - InternalYAMLParseError MergeErrorType = "internal_yaml_parse" -) - -// RenderErrorType describes the class of errors that could -// occur while rendering Kubernetes manifest from given CR. -type RenderErrorType string - -const ( - RenderNotStartedError RenderErrorType = "render_not_started" - - // HelmTranslateIOPToValuesError describes render error where renderer for - // a component cannot create values.yaml tree from given CR. - HelmTranslateIOPToValuesError RenderErrorType = "helm_translate_iop_to_values" - - // HelmChartRenderError describes error where Helm charts cannot be rendered - // for the generated values.yaml tree. - HelmChartRenderError RenderErrorType = "helm_chart_render" - - // K8SSettingsOverlayError describes the K8s overlay error after - // rendering Helm charts successfully. - K8SSettingsOverlayError RenderErrorType = "k8s_settings_overlay" - - // K8SManifestPatchError describes errors while patching generated manifest. - K8SManifestPatchError RenderErrorType = "k8s_manifest_patch" -) - -var ( - // Version is the version of the operator binary running currently. - // This is required for fleet level metrics although it is available from - // ControlZ (more precisely versionz endpoint). - Version = monitoring.NewGauge( - "version", - "Version of operator binary", - monitoring.WithLabels(OperatorVersionLabel), - ) - - // GetCRErrorTotal counts the number of times fetching - // CR fails from API server. - GetCRErrorTotal = monitoring.NewSum( - "get_cr_error_total", - "Number of times fetching CR from apiserver failed", - monitoring.WithLabels(CRFetchErrorReasonLabel), - ) - - // CRMergeFailureTotal counts number of CR merge failures. - CRMergeFailureTotal = monitoring.NewSum( - "cr_merge_failure_total", - "Number of IstioOperator CR merge failures", - monitoring.WithLabels(MergeErrorLabel), - ) - - // CRDeletionTotal counts the number of times - // IstioOperator CR was deleted. - CRDeletionTotal = monitoring.NewSum( - "cr_deletion_total", - "Number of IstioOperator CR deleted", - ) - - // CRValidationErrorTotal counts the number of CR - // validation failures. - CRValidationErrorTotal = monitoring.NewSum( - "cr_validation_error_total", - "Number of IstioOperator CR validation failures", - ) - - // RenderManifestTotal counts the number of manifest - // renders at each component level. - RenderManifestTotal = monitoring.NewSum( - "render_manifest_total", - "Number of component manifests rendered", - monitoring.WithLabels(ComponentNameLabel), - ) - - // OwnedResourceTotal indicates the number of resources - // currently owned by the CR with given name and revision. - OwnedResourceTotal = monitoring.NewGauge( - "owned_resource_total", - "Number of resources currently owned by the operator", - monitoring.WithLabels(ResourceKindLabel), - ) - - // ResourceCreationTotal indicates the number of resources - // created by the operator for a CR and revision. - ResourceCreationTotal = monitoring.NewSum( - "resource_creation_total", - "Number of resources created by the operator", - monitoring.WithLabels(ResourceKindLabel), - ) - - // ResourceUpdateTotal indicates the number of resources updated by - // the operator in response to CR updates for a revision. - ResourceUpdateTotal = monitoring.NewSum( - "resource_update_total", - "Number of resources updated by the operator", - monitoring.WithLabels(ResourceKindLabel), - ) - - // ResourceDeletionTotal indicates the number of resources deleted - // by the operator in response to CR update or delete operation (like - // ingress-gateway which was enabled could be disabled and this requires - // deleting ingress-gateway deployment). - ResourceDeletionTotal = monitoring.NewSum( - "resource_deletion_total", - "Number of resources deleted by the operator", - monitoring.WithLabels(ResourceKindLabel), - ) - - // ResourcePruneTotal indicates the resources pruned as a result of update. - ResourcePruneTotal = monitoring.NewSum( - "resource_prune_total", - "Number of resources pruned by the operator", - monitoring.WithLabels(ResourceKindLabel), - ) - - // ManifestPatchErrorTotal counts the total number of K8S patch errors. - ManifestPatchErrorTotal = monitoring.NewSum( - "manifest_patch_error_total", - "Number of times K8S patch overlays failed", - ) - - // ManifestRenderErrorTotal counts errors occurred while rendering manifest. - ManifestRenderErrorTotal = monitoring.NewSum( - "manifest_render_error_total", - "Number of times error occurred during rendering output manifest", - monitoring.WithLabels(ComponentNameLabel, RenderErrorLabel), - ) - - // LegacyPathTranslationTotal counts the translations from legacy API to new one. - LegacyPathTranslationTotal = monitoring.NewSum( - "legacy_path_translation_total", - "Number of times a legacy API path is translated", - ) - - // CacheFlushTotal counts number of cache flushes. - CacheFlushTotal = monitoring.NewSum( - "cache_flush_total", - "number of times operator cache was flushed", - ) -) - -func init() { - monitoring.MustRegister( - Version, - - GetCRErrorTotal, - CRMergeFailureTotal, - CRValidationErrorTotal, - CRDeletionTotal, - RenderManifestTotal, - - OwnedResourceTotal, - ResourceCreationTotal, - ResourceUpdateTotal, - ResourceDeletionTotal, - ResourcePruneTotal, - - ManifestPatchErrorTotal, - ManifestRenderErrorTotal, - LegacyPathTranslationTotal, - CacheFlushTotal, - ) - - initOperatorCrdResourceMetrics() -} diff --git a/operator/pkg/metrics/resource_counts.go b/operator/pkg/metrics/resource_counts.go deleted file mode 100644 index cce08d522..000000000 --- a/operator/pkg/metrics/resource_counts.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package metrics - -import ( - "sync" -) - -import ( - "k8s.io/apimachinery/pkg/runtime/schema" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -// resourceCounts keeps track of the number of resources owned by each -// IstioOperator resource. The reported metric is the sum across all these. -type resourceCounts struct { - mu *sync.Mutex - resources map[schema.GroupKind]map[string]struct{} -} - -var rc *resourceCounts - -func initOperatorCrdResourceMetrics() { - rc = &resourceCounts{ - mu: &sync.Mutex{}, - resources: map[schema.GroupKind]map[string]struct{}{}, - } -} - -// AddResource adds the resource of given kind to the set of owned objects -func AddResource(name string, gk schema.GroupKind) { - rc.mu.Lock() - defer rc.mu.Unlock() - if _, present := rc.resources[gk]; !present { - rc.resources[gk] = map[string]struct{}{} - } - rc.resources[gk][name] = struct{}{} -} - -// RemoveResource removes the resource of given kind to the set of owned objects -func RemoveResource(name string, gk schema.GroupKind) { - rc.mu.Lock() - defer rc.mu.Unlock() - delete(rc.resources[gk], name) -} - -// ReportOwnedResourceCounts reports the owned resource count -// metric by Group and Kind. -func ReportOwnedResourceCounts() { - rc.mu.Lock() - defer rc.mu.Unlock() - for gk, r := range rc.resources { - OwnedResourceTotal. - With(ResourceKindLabel.Value(util.GKString(gk))). - Record(float64(len(r))) - } -} diff --git a/operator/pkg/metrics/utils.go b/operator/pkg/metrics/utils.go deleted file mode 100644 index af54c7236..000000000 --- a/operator/pkg/metrics/utils.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package metrics - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" -) - -// CountCRMergeFail increments the count of CR merge failure -// for the given merge error type. -func CountCRMergeFail(reason MergeErrorType) { - CRMergeFailureTotal. - With(MergeErrorLabel.Value(string(reason))). - Increment() -} - -// CountManifestRenderError increments the count of manifest -// render errors. -func CountManifestRenderError(cn name.ComponentName, reason RenderErrorType) { - ManifestRenderErrorTotal. - With(ComponentNameLabel.Value(string(cn))). - With(RenderErrorLabel.Value(string(reason))). - Increment() -} - -// CountCRFetchFail increments the count of CR fetch failure -// for a given name and the error status. -func CountCRFetchFail(reason metav1.StatusReason) { - errorReason := string(reason) - if reason == metav1.StatusReasonUnknown { - errorReason = "unknown" - } - GetCRErrorTotal. - With(CRFetchErrorReasonLabel.Value(errorReason)). - Increment() -} - -// CountManifestRender increments the count of rendered -// manifest from IstioOperator CR by component name. -func CountManifestRender(name name.ComponentName) { - RenderManifestTotal. - With(ComponentNameLabel.Value(string(name))). - Increment() -} diff --git a/operator/pkg/name/name.go b/operator/pkg/name/name.go deleted file mode 100644 index 3f564af6b..000000000 --- a/operator/pkg/name/name.go +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package name - -import ( - "fmt" - "strings" -) - -import ( - "istio.io/api/operator/v1alpha1" -) - -import ( - iop "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/helm" - "github.com/apache/dubbo-go-pixiu/operator/pkg/tpath" -) - -// Istio default namespace -const ( - IstioDefaultNamespace = "dubbo-system" -) - -// Kubernetes Kind strings. -const ( - CRDStr = "CustomResourceDefinition" - ClusterRoleStr = "ClusterRole" - ClusterRoleBindingStr = "ClusterRoleBinding" - CMStr = "ConfigMap" - DaemonSetStr = "DaemonSet" - DeploymentStr = "Deployment" - EndpointStr = "Endpoints" - HPAStr = "HorizontalPodAutoscaler" - IngressStr = "Ingress" - IstioOperator = "IstioOperator" - MutatingWebhookConfigurationStr = "MutatingWebhookConfiguration" - NamespaceStr = "Namespace" - PVCStr = "PersistentVolumeClaim" - PodStr = "Pod" - PDBStr = "PodDisruptionBudget" - ReplicationControllerStr = "ReplicationController" - ReplicaSetStr = "ReplicaSet" - RoleStr = "Role" - RoleBindingStr = "RoleBinding" - SAStr = "ServiceAccount" - ServiceStr = "Service" - SecretStr = "Secret" - StatefulSetStr = "StatefulSet" - ValidatingWebhookConfigurationStr = "ValidatingWebhookConfiguration" -) - -// Istio Kind strings -const ( - EnvoyFilterStr = "EnvoyFilter" - GatewayStr = "Gateway" - DestinationRuleStr = "DestinationRule" - MeshPolicyStr = "MeshPolicy" - PeerAuthenticationStr = "PeerAuthentication" - VirtualServiceStr = "VirtualService" - IstioOperatorStr = "IstioOperator" -) - -// Istio API Group Names -const ( - AuthenticationAPIGroupName = "authentication.istio.io" - ConfigAPIGroupName = "config.istio.io" - NetworkingAPIGroupName = "networking.istio.io" - OperatorAPIGroupName = "operator.istio.io" - SecurityAPIGroupName = "security.istio.io" -) - -const ( - // OperatorAPINamespace is the API namespace for operator config. - // TODO: move this to a base definitions file when one is created. - OperatorAPINamespace = OperatorAPIGroupName - - // DefaultProfileName is the name of the default profile. - DefaultProfileName = "default" - - // installedSpecCRPrefix is the prefix of any IstioOperator CR stored in the cluster that is a copy of the CR used - // in the last install operation. - InstalledSpecCRPrefix = "installed-state" -) - -// ComponentName is a component name string, typed to constrain allowed values. -type ComponentName string - -const ( - // IstioComponent names corresponding to the IstioOperator proto component names. Must be the same, since these - // are used for struct traversal. - IstioBaseComponentName ComponentName = "Base" - PilotComponentName ComponentName = "Pilot" - - CNIComponentName ComponentName = "Cni" - - // istiod remote component - IstiodRemoteComponentName ComponentName = "IstiodRemote" - - // Gateway components - IngressComponentName ComponentName = "IngressGateways" - EgressComponentName ComponentName = "EgressGateways" - - // Operator components - IstioOperatorComponentName ComponentName = "IstioOperator" - IstioOperatorCustomResourceName ComponentName = "IstioOperatorCustomResource" -) - -// ComponentNamesConfig is used for unmarshaling legacy and addon naming data. -type ComponentNamesConfig struct { - DeprecatedComponentNames []string -} - -var ( - AllCoreComponentNames = []ComponentName{ - IstioBaseComponentName, - PilotComponentName, - CNIComponentName, - IstiodRemoteComponentName, - } - - // AllComponentNames is a list of all Istio components. - AllComponentNames = append(AllCoreComponentNames, IngressComponentName, EgressComponentName, - IstioOperatorComponentName, IstioOperatorCustomResourceName) - - allCoreComponentNamesMap = map[ComponentName]bool{} - - // ValuesEnablementPathMap defines a mapping between legacy values enablement paths and the corresponding enablement - // paths in IstioOperator. - ValuesEnablementPathMap = map[string]string{ - "spec.values.gateways.istio-ingressgateway.enabled": "spec.components.ingressGateways.[name:istio-ingressgateway].enabled", - "spec.values.gateways.istio-egressgateway.enabled": "spec.components.egressGateways.[name:istio-egressgateway].enabled", - } - - // userFacingComponentNames are the names of components that are displayed to the user in high level CLIs - // (like progress log). - userFacingComponentNames = map[ComponentName]string{ - IstioBaseComponentName: "Istio core", - PilotComponentName: "Istiod", - CNIComponentName: "CNI", - IngressComponentName: "Ingress gateways", - EgressComponentName: "Egress gateways", - IstioOperatorComponentName: "Istio operator", - IstioOperatorCustomResourceName: "Istio operator CRDs", - IstiodRemoteComponentName: "Istiod remote", - } -) - -// Manifest defines a manifest for a component. -type Manifest struct { - Name ComponentName - Content string -} - -// ManifestMap is a map of ComponentName to its manifest string. -type ManifestMap map[ComponentName][]string - -func init() { - for _, c := range AllCoreComponentNames { - allCoreComponentNamesMap[c] = true - } -} - -// Consolidated returns a representation of mm where all manifests in the slice under a key are combined into a single -// manifest. -func (mm ManifestMap) Consolidated() map[string]string { - out := make(map[string]string) - for cname, ms := range mm { - allM := "" - for _, m := range ms { - allM += m + helm.YAMLSeparator - } - out[string(cname)] = allM - } - return out -} - -// MergeManifestSlices merges a slice of manifests into a single manifest string. -func MergeManifestSlices(manifests []string) string { - return strings.Join(manifests, helm.YAMLSeparator) -} - -// String implements the Stringer interface. -func (mm ManifestMap) String() string { - out := "" - for _, ms := range mm { - for _, m := range ms { - out += m + helm.YAMLSeparator - } - } - return out -} - -// IsGateway reports whether cn is a gateway component. -func (cn ComponentName) IsGateway() bool { - return cn == IngressComponentName || cn == EgressComponentName -} - -// Namespace returns the namespace for the component. It follows these rules: -// 1. If DefaultNamespace is unset, log and error and return the empty string. -// 2. If the feature and component namespaces are unset, return DefaultNamespace. -// 3. If the feature namespace is set but component name is unset, return the feature namespace. -// 4. Otherwise return the component namespace. -// Namespace assumes that controlPlaneSpec has been validated. -// TODO: remove extra validations when comfort level is high enough. -func Namespace(componentName ComponentName, controlPlaneSpec *v1alpha1.IstioOperatorSpec) (string, error) { - defaultNamespace := iop.Namespace(controlPlaneSpec) - - componentNodeI, found, err := tpath.GetFromStructPath(controlPlaneSpec, "Components."+string(componentName)+".Namespace") - if err != nil { - return "", fmt.Errorf("error in Namepsace GetFromStructPath componentNamespace for component=%s: %s", componentName, err) - } - if !found { - return defaultNamespace, nil - } - if componentNodeI == nil { - return defaultNamespace, nil - } - componentNamespace, ok := componentNodeI.(string) - if !ok { - return "", fmt.Errorf("component %s enabled has bad type %T, expect string", componentName, componentNodeI) - } - if componentNamespace == "" { - return defaultNamespace, nil - } - return componentNamespace, nil -} - -// TitleCase returns a capitalized version of n. -func TitleCase(n ComponentName) ComponentName { - s := string(n) - return ComponentName(strings.ToUpper(s[0:1]) + s[1:]) -} - -// UserFacingComponentName returns the name of the given component that should be displayed to the user in high -// level CLIs (like progress log). -func UserFacingComponentName(name ComponentName) string { - ret, ok := userFacingComponentNames[name] - if !ok { - return "Unknown" - } - return ret -} diff --git a/operator/pkg/name/name_test.go b/operator/pkg/name/name_test.go deleted file mode 100644 index e1d2d6130..000000000 --- a/operator/pkg/name/name_test.go +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package name - -import ( - "reflect" - "testing" -) - -import ( - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/tpath" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -func TestGetFromTreePath(t *testing.T) { - type args struct { - inputTree string - path util.Path - } - - tests := []struct { - name string - args args - want string - found bool - wantErr bool - }{ - { - name: "found string node", - args: args{ - inputTree: ` -k1: v1 -`, - path: util.Path{"k1"}, - }, - want: ` -v1 -`, - found: true, - wantErr: false, - }, - { - name: "found tree node", - args: args{ - inputTree: ` -k1: - k2: v2 -`, - path: util.Path{"k1"}, - }, - want: ` -k2: v2 -`, - found: true, - wantErr: false, - }, - { - name: "path is longer than tree depth, string node", - args: args{ - inputTree: ` -k1: - k2: v1 -`, - path: util.Path{"k1", "k2", "k3"}, - }, - want: "", - found: false, - wantErr: false, - }, - { - name: "path not in map array tree", - args: args{ - inputTree: ` -a: - b: - - name: n1 - value: v1 - - name: n2 - list: - - v21 - - v22 -`, - path: util.Path{"a", "b", "unknown"}, - }, - want: ``, - found: false, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tree := make(map[string]interface{}) - if err := yaml.Unmarshal([]byte(tt.args.inputTree), &tree); err != nil { - t.Fatal(err) - } - got, found, err := tpath.Find(tree, tt.args.path) - if (err != nil) != tt.wantErr { - t.Errorf("Find() error = %v, wantErr %v", err, tt.wantErr) - return - } - - var wantTree interface{} - if err := yaml.Unmarshal([]byte(tt.want), &wantTree); err != nil { - t.Fatal(err) - } - - if !reflect.DeepEqual(got, wantTree) { - t.Errorf("Find() got = %v, want %v", got, tt.want) - } - if found != tt.found { - t.Errorf("Find() found = %v, want %v", found, tt.found) - } - }) - } -} diff --git a/operator/pkg/object/objects.go b/operator/pkg/object/objects.go deleted file mode 100644 index d1e2c6c62..000000000 --- a/operator/pkg/object/objects.go +++ /dev/null @@ -1,587 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/* -Package manifest provides functions for going between in-memory k8s objects (unstructured.Unstructured) and their JSON -or YAML representations. -*/ -package object - -import ( - "bufio" - "bytes" - "fmt" - "sort" - "strings" -) - -import ( - "istio.io/pkg/log" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/intstr" - k8syaml "k8s.io/apimachinery/pkg/util/yaml" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/helm" - names "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/tpath" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -const ( - // YAMLSeparator is a separator for multi-document YAML files. - YAMLSeparator = "\n---\n" -) - -// K8sObject is an in-memory representation of a k8s object, used for moving between different representations -// (Unstructured, JSON, YAML) with cached rendering. -type K8sObject struct { - object *unstructured.Unstructured - - Group string - Kind string - Name string - Namespace string - - json []byte - yaml []byte -} - -// NewK8sObject creates a new K8sObject and returns a ptr to it. -func NewK8sObject(u *unstructured.Unstructured, json, yaml []byte) *K8sObject { - o := &K8sObject{ - object: u, - json: json, - yaml: yaml, - } - - gvk := u.GetObjectKind().GroupVersionKind() - o.Group = gvk.Group - o.Kind = gvk.Kind - o.Name = u.GetName() - o.Namespace = u.GetNamespace() - - return o -} - -// Hash returns a unique, insecure hash based on kind, namespace and name. -func Hash(kind, namespace, name string) string { - switch kind { - case names.ClusterRoleStr, names.ClusterRoleBindingStr, names.MeshPolicyStr: - namespace = "" - } - return strings.Join([]string{kind, namespace, name}, ":") -} - -// FromHash parses kind, namespace and name from a hash. -func FromHash(hash string) (kind, namespace, name string) { - hv := strings.Split(hash, ":") - if len(hv) != 3 { - return "Bad hash string: " + hash, "", "" - } - kind, namespace, name = hv[0], hv[1], hv[2] - return -} - -// HashNameKind returns a unique, insecure hash based on kind and name. -func HashNameKind(kind, name string) string { - return strings.Join([]string{kind, name}, ":") -} - -// ParseJSONToK8sObject parses JSON to an K8sObject. -func ParseJSONToK8sObject(json []byte) (*K8sObject, error) { - o, _, err := unstructured.UnstructuredJSONScheme.Decode(json, nil, nil) - if err != nil { - return nil, fmt.Errorf("error parsing json into unstructured object: %v", err) - } - - u, ok := o.(*unstructured.Unstructured) - if !ok { - return nil, fmt.Errorf("parsed unexpected type %T", o) - } - - return NewK8sObject(u, json, nil), nil -} - -// ParseYAMLToK8sObject parses YAML to an Object. -func ParseYAMLToK8sObject(yaml []byte) (*K8sObject, error) { - r := bytes.NewReader(yaml) - decoder := k8syaml.NewYAMLOrJSONDecoder(r, 1024) - - out := &unstructured.Unstructured{} - err := decoder.Decode(out) - if err != nil { - return nil, fmt.Errorf("error decoding object %v: %v", string(yaml), err) - } - return NewK8sObject(out, nil, yaml), nil -} - -// UnstructuredObject exposes the raw object, primarily for testing -func (o *K8sObject) UnstructuredObject() *unstructured.Unstructured { - return o.object -} - -// ResolveK8sConflict - This method resolves k8s object possible -// conflicting settings. Which K8sObjects may need such method -// depends on the type of the K8sObject. -func (o *K8sObject) ResolveK8sConflict() *K8sObject { - if o.Kind == names.PDBStr { - return resolvePDBConflict(o) - } - return o -} - -// Unstructured exposes the raw object content, primarily for testing -func (o *K8sObject) Unstructured() map[string]interface{} { - return o.UnstructuredObject().UnstructuredContent() -} - -// Container returns a container subtree for Deployment objects if one is found, or nil otherwise. -func (o *K8sObject) Container(name string) map[string]interface{} { - u := o.Unstructured() - path := fmt.Sprintf("spec.template.spec.containers.[name:%s]", name) - node, f, err := tpath.GetPathContext(u, util.PathFromString(path), false) - if err == nil && f { - // Must be the type from the schema. - return node.Node.(map[string]interface{}) - } - return nil -} - -// GroupVersionKind returns the GroupVersionKind for the K8sObject -func (o *K8sObject) GroupVersionKind() schema.GroupVersionKind { - return o.object.GroupVersionKind() -} - -// Version returns the APIVersion of the K8sObject -func (o *K8sObject) Version() string { - return o.object.GetAPIVersion() -} - -// Hash returns a unique hash for the K8sObject -func (o *K8sObject) Hash() string { - return Hash(o.Kind, o.Namespace, o.Name) -} - -// HashNameKind returns a hash for the K8sObject based on the name and kind only. -func (o *K8sObject) HashNameKind() string { - return HashNameKind(o.Kind, o.Name) -} - -// JSON returns a JSON representation of the K8sObject, using an internal cache. -func (o *K8sObject) JSON() ([]byte, error) { - if o.json != nil { - return o.json, nil - } - - b, err := o.object.MarshalJSON() - if err != nil { - return nil, err - } - return b, nil -} - -// YAML returns a YAML representation of the K8sObject, using an internal cache. -func (o *K8sObject) YAML() ([]byte, error) { - if o == nil { - return nil, nil - } - if o.yaml != nil { - return o.yaml, nil - } - oj, err := o.JSON() - if err != nil { - return nil, err - } - o.json = oj - y, err := yaml.JSONToYAML(oj) - if err != nil { - return nil, err - } - o.yaml = y - return y, nil -} - -// YAMLDebugString returns a YAML representation of the K8sObject, or an error string if the K8sObject cannot be rendered to YAML. -func (o *K8sObject) YAMLDebugString() string { - y, err := o.YAML() - if err != nil { - return err.Error() - } - return string(y) -} - -// K8sObjects holds a collection of k8s objects, so that we can filter / sequence them -type K8sObjects []*K8sObject - -// String implements the Stringer interface. -func (os K8sObjects) String() string { - var out []string - for _, oo := range os { - out = append(out, oo.YAMLDebugString()) - } - return strings.Join(out, helm.YAMLSeparator) -} - -// Keys returns a slice with the keys of os. -func (os K8sObjects) Keys() []string { - var out []string - for _, oo := range os { - out = append(out, oo.Hash()) - } - return out -} - -// UnstructuredItems returns the list of items of unstructured.Unstructured. -func (os K8sObjects) UnstructuredItems() []unstructured.Unstructured { - var usList []unstructured.Unstructured - for _, obj := range os { - usList = append(usList, *obj.UnstructuredObject()) - } - return usList -} - -// ParseK8sObjectsFromYAMLManifest returns a K8sObjects representation of manifest. -func ParseK8sObjectsFromYAMLManifest(manifest string) (K8sObjects, error) { - return ParseK8sObjectsFromYAMLManifestFailOption(manifest, true) -} - -// ParseK8sObjectsFromYAMLManifestFailOption returns a K8sObjects representation of manifest. Continues parsing when a bad object -// is found if failOnError is set to false. -func ParseK8sObjectsFromYAMLManifestFailOption(manifest string, failOnError bool) (K8sObjects, error) { - var b bytes.Buffer - - var yamls []string - scanner := bufio.NewScanner(strings.NewReader(manifest)) - for scanner.Scan() { - line := scanner.Text() - if strings.HasPrefix(line, "---") { - // yaml separator - yamls = append(yamls, b.String()) - b.Reset() - } else { - if _, err := b.WriteString(line); err != nil { - return nil, err - } - if _, err := b.WriteString("\n"); err != nil { - return nil, err - } - } - } - yamls = append(yamls, b.String()) - - var objects K8sObjects - - for _, yaml := range yamls { - yaml = removeNonYAMLLines(yaml) - if yaml == "" { - continue - } - o, err := ParseYAMLToK8sObject([]byte(yaml)) - if err != nil { - e := fmt.Errorf("failed to parse YAML to a k8s object: %s", err) - if failOnError { - return nil, e - } - log.Error(err.Error()) - continue - } - if o.Valid() { - objects = append(objects, o) - } - } - - return objects, nil -} - -func removeNonYAMLLines(yms string) string { - var b strings.Builder - for _, s := range strings.Split(yms, "\n") { - if strings.HasPrefix(s, "#") { - continue - } - b.WriteString(s) - b.WriteString("\n") - } - - // helm charts sometimes emits blank objects with just a "disabled" comment. - return strings.TrimSpace(b.String()) -} - -// YAMLManifest returns a YAML representation of K8sObjects os. -func (os K8sObjects) YAMLManifest() (string, error) { - var b bytes.Buffer - - for i, item := range os { - if i != 0 { - if _, err := b.WriteString("\n\n"); err != nil { - return "", err - } - } - ym, err := item.YAML() - if err != nil { - return "", fmt.Errorf("error building yaml: %v", err) - } - if _, err := b.Write(ym); err != nil { - return "", err - } - if _, err := b.Write([]byte(YAMLSeparator)); err != nil { - return "", err - } - - } - - return b.String(), nil -} - -// Sort will order the items in K8sObjects in order of score, group, kind, name. The intent is to -// have a deterministic ordering in which K8sObjects are applied. -func (os K8sObjects) Sort(score func(o *K8sObject) int) { - sort.Slice(os, func(i, j int) bool { - iScore := score(os[i]) - jScore := score(os[j]) - return iScore < jScore || - (iScore == jScore && - os[i].Group < os[j].Group) || - (iScore == jScore && - os[i].Group == os[j].Group && - os[i].Kind < os[j].Kind) || - (iScore == jScore && - os[i].Group == os[j].Group && - os[i].Kind == os[j].Kind && - os[i].Name < os[j].Name) - }) -} - -// ToMap returns a map of K8sObject hash to K8sObject. -func (os K8sObjects) ToMap() map[string]*K8sObject { - ret := make(map[string]*K8sObject) - for _, oo := range os { - if oo.Valid() { - ret[oo.Hash()] = oo - } - } - return ret -} - -// ToNameKindMap returns a map of K8sObject name/kind hash to K8sObject. -func (os K8sObjects) ToNameKindMap() map[string]*K8sObject { - ret := make(map[string]*K8sObject) - for _, oo := range os { - if oo.Valid() { - ret[oo.HashNameKind()] = oo - } - } - return ret -} - -// Valid checks returns true if Kind of K8sObject is not empty. -func (o *K8sObject) Valid() bool { - return o.Kind != "" -} - -// FullName returns namespace/name of K8s object -func (o *K8sObject) FullName() string { - return fmt.Sprintf("%s/%s", o.Namespace, o.Name) -} - -// Equal returns true if o and other are both valid and equal to each other. -func (o *K8sObject) Equal(other *K8sObject) bool { - if o == nil { - return other == nil - } - if other == nil { - return o == nil - } - - ay, err := o.YAML() - if err != nil { - return false - } - by, err := other.YAML() - if err != nil { - return false - } - - return util.IsYAMLEqual(string(ay), string(by)) -} - -func istioCustomResources(group string) bool { - switch group { - case names.ConfigAPIGroupName, - names.SecurityAPIGroupName, - names.AuthenticationAPIGroupName, - names.NetworkingAPIGroupName: - return true - } - return false -} - -// DefaultObjectOrder is default sorting function used to sort k8s objects. -func DefaultObjectOrder() func(o *K8sObject) int { - return func(o *K8sObject) int { - gk := o.Group + "/" + o.Kind - switch { - // Create CRDs asap - both because they are slow and because we will likely create instances of them soon - case gk == "apiextensions.k8s.io/CustomResourceDefinition": - return -1000 - - // We need to create ServiceAccounts, Roles before we bind them with a RoleBinding - case gk == "/ServiceAccount" || gk == "rbac.authorization.k8s.io/ClusterRole": - return 1 - case gk == "rbac.authorization.k8s.io/ClusterRoleBinding": - return 2 - - // validatingwebhookconfiguration is configured to FAIL-OPEN in the default install. For the - // re-install case we want to apply the validatingwebhookconfiguration first to reset any - // orphaned validatingwebhookconfiguration that is FAIL-CLOSE. - case gk == "admissionregistration.k8s.io/ValidatingWebhookConfiguration": - return 3 - - case istioCustomResources(o.Group): - return 4 - - // Pods might need configmap or secrets - avoid backoff by creating them first - case gk == "/ConfigMap" || gk == "/Secrets": - return 100 - - // Create the pods after we've created other things they might be waiting for - case gk == "extensions/Deployment" || gk == "app/Deployment": - return 1000 - - // Autoscalers typically act on a deployment - case gk == "autoscaling/HorizontalPodAutoscaler": - return 1001 - - // Create services late - after pods have been started - case gk == "/Service": - return 10000 - - default: - return 1000 - } - } -} - -func ObjectsNotInLists(objects K8sObjects, lists ...K8sObjects) K8sObjects { - var ret K8sObjects - - filterMap := make(map[*K8sObject]bool) - for _, list := range lists { - for _, object := range list { - filterMap[object] = true - } - } - - for _, o := range objects { - if !filterMap[o] { - ret = append(ret, o) - } - } - return ret -} - -// KindObjects returns the subset of objs with the given kind. -func KindObjects(objs K8sObjects, kind string) K8sObjects { - var ret K8sObjects - for _, o := range objs { - if o.Kind == kind { - ret = append(ret, o) - } - } - return ret -} - -// ParseK8SYAMLToIstioOperator parses a IstioOperator CustomResource YAML string and unmarshals in into -// an IstioOperatorSpec object. It returns the object and an API group/version with it. -func ParseK8SYAMLToIstioOperator(yml string) (*v1alpha1.IstioOperator, *schema.GroupVersionKind, error) { - o, err := ParseYAMLToK8sObject([]byte(yml)) - if err != nil { - return nil, nil, err - } - iop := &v1alpha1.IstioOperator{} - if err := yaml.UnmarshalStrict([]byte(yml), iop); err != nil { - return nil, nil, err - } - gvk := o.GroupVersionKind() - v1alpha1.SetNamespace(iop.Spec, o.Namespace) - return iop, &gvk, nil -} - -// AllObjectHashes returns a map with object hashes of all the objects contained in cmm as the keys. -func AllObjectHashes(m string) map[string]bool { - ret := make(map[string]bool) - objs, err := ParseK8sObjectsFromYAMLManifest(m) - if err != nil { - log.Error(err.Error()) - } - for _, o := range objs { - ret[o.Hash()] = true - } - - return ret -} - -// resolvePDBConflict When user uses both minAvailable and -// maxUnavailable to configure istio instances, these two -// parameters are mutually exclusive, care must be taken -// to resolve the issue -func resolvePDBConflict(o *K8sObject) *K8sObject { - if o.json == nil { - return o - } - if o.object.Object["spec"] == nil { - return o - } - spec := o.object.Object["spec"].(map[string]interface{}) - isDefault := func(item interface{}) bool { - var ii intstr.IntOrString - switch item := item.(type) { - case int: - ii = intstr.FromInt(item) - case int64: - ii = intstr.FromInt(int(item)) - case string: - ii = intstr.FromString(item) - default: - ii = intstr.FromInt(0) - } - intVal, err := intstr.GetScaledValueFromIntOrPercent(&ii, 100, false) - if err != nil || intVal == 0 { - return true - } - return false - } - if spec["maxUnavailable"] != nil && spec["minAvailable"] != nil { - // When both maxUnavailable and minAvailable present and - // neither has value 0, this is considered a conflict, - // then maxUnavailale will take precedence. - if !isDefault(spec["maxUnavailable"]) && !isDefault(spec["minAvailable"]) { - delete(spec, "minAvailable") - // Make sure that the json and yaml representation of the object - // is consistent with the changed object - o.json = nil - o.json, _ = o.JSON() - if o.yaml != nil { - o.yaml = nil - o.yaml, _ = o.YAML() - } - } - } - return o -} diff --git a/operator/pkg/object/objects_test.go b/operator/pkg/object/objects_test.go deleted file mode 100644 index f6ede5701..000000000 --- a/operator/pkg/object/objects_test.go +++ /dev/null @@ -1,718 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package object - -import ( - "strings" - "testing" -) - -import ( - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -func TestHash(t *testing.T) { - hashTests := []struct { - desc string - kind string - namespace string - name string - want string - }{ - {"CalculateHashForObjectWithNormalCharacter", "Service", "default", "ingressgateway", "Service:default:ingressgateway"}, - {"CalculateHashForObjectWithDash", "Deployment", "dubbo-system", "istio-pilot", "Deployment:dubbo-system:istio-pilot"}, - {"CalculateHashForObjectWithDot", "ConfigMap", "dubbo-system", "my.config", "ConfigMap:dubbo-system:my.config"}, - } - - for _, tt := range hashTests { - t.Run(tt.desc, func(t *testing.T) { - got := Hash(tt.kind, tt.namespace, tt.name) - if got != tt.want { - t.Errorf("Hash(%s): got %s for kind %s, namespace %s, name %s, want %s", tt.desc, got, tt.kind, tt.namespace, tt.name, tt.want) - } - }) - } -} - -func TestFromHash(t *testing.T) { - hashTests := []struct { - desc string - hash string - kind string - namespace string - name string - }{ - {"ParseHashWithNormalCharacter", "Service:default:ingressgateway", "Service", "default", "ingressgateway"}, - {"ParseHashForObjectWithDash", "Deployment:dubbo-system:istio-pilot", "Deployment", "dubbo-system", "istio-pilot"}, - {"ParseHashForObjectWithDot", "ConfigMap:dubbo-system:my.config", "ConfigMap", "dubbo-system", "my.config"}, - {"InvalidHash", "test", "Bad hash string: test", "", ""}, - } - - for _, tt := range hashTests { - t.Run(tt.desc, func(t *testing.T) { - k, ns, name := FromHash(tt.hash) - if k != tt.kind || ns != tt.namespace || name != tt.name { - t.Errorf("FromHash(%s): got kind %s, namespace %s, name %s, want kind %s, namespace %s, name %s", tt.desc, k, ns, name, tt.kind, tt.namespace, tt.name) - } - }) - } -} - -func TestHashNameKind(t *testing.T) { - hashNameKindTests := []struct { - desc string - kind string - name string - want string - }{ - {"CalculateHashNameKindForObjectWithNormalCharacter", "Service", "ingressgateway", "Service:ingressgateway"}, - {"CalculateHashNameKindForObjectWithDash", "Deployment", "istio-pilot", "Deployment:istio-pilot"}, - {"CalculateHashNameKindForObjectWithDot", "ConfigMap", "my.config", "ConfigMap:my.config"}, - } - - for _, tt := range hashNameKindTests { - t.Run(tt.desc, func(t *testing.T) { - got := HashNameKind(tt.kind, tt.name) - if got != tt.want { - t.Errorf("HashNameKind(%s): got %s for kind %s, name %s, want %s", tt.desc, got, tt.kind, tt.name, tt.want) - } - }) - } -} - -func TestParseJSONToK8sObject(t *testing.T) { - testDeploymentJSON := `{ - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": { - "name": "istio-citadel", - "namespace": "dubbo-system", - "labels": { - "istio": "citadel" - } - }, - "spec": { - "replicas": 1, - "selector": { - "matchLabels": { - "istio": "citadel" - } - }, - "template": { - "metadata": { - "labels": { - "istio": "citadel" - } - }, - "spec": { - "containers": [ - { - "name": "citadel", - "image": "docker.io/istio/citadel:1.1.8", - "args": [ - "--append-dns-names=true", - "--grpc-port=8060", - "--grpc-hostname=citadel", - "--citadel-storage-namespace=dubbo-system", - "--custom-dns-names=istio-pilot-service-account.dubbo-system:istio-pilot.dubbo-system", - "--monitoring-port=15014", - "--self-signed-ca=true" - ] - } - ] - } - } - } -}` - testPodJSON := `{ - "apiVersion": "v1", - "kind": "Pod", - "metadata": { - "name": "istio-galley-75bcd59768-hpt5t", - "namespace": "dubbo-system", - "labels": { - "istio": "galley" - } - }, - "spec": { - "containers": [ - { - "name": "galley", - "image": "docker.io/istio/galley:1.1.8", - "command": [ - "/usr/local/bin/galley", - "server", - "--meshConfigFile=/etc/mesh-config/mesh", - "--livenessProbeInterval=1s", - "--livenessProbePath=/healthliveness", - "--readinessProbePath=/healthready", - "--readinessProbeInterval=1s", - "--deployment-namespace=dubbo-system", - "--insecure=true", - "--validation-webhook-config-file", - "/etc/config/validatingwebhookconfiguration.yaml", - "--monitoringPort=15014", - "--log_output_level=default:info" - ], - "ports": [ - { - "containerPort": 443, - "protocol": "TCP" - }, - { - "containerPort": 15014, - "protocol": "TCP" - }, - { - "containerPort": 9901, - "protocol": "TCP" - } - ] - } - ] - } -}` - testServiceJSON := `{ - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "labels": { - "app": "pilot" - }, - "name": "istio-pilot", - "namespace": "dubbo-system" - }, - "spec": { - "clusterIP": "10.102.230.31", - "ports": [ - { - "name": "grpc-xds", - "port": 15010, - "protocol": "TCP", - "targetPort": 15010 - }, - { - "name": "https-xds", - "port": 15011, - "protocol": "TCP", - "targetPort": 15011 - }, - { - "name": "http-legacy-discovery", - "port": 8080, - "protocol": "TCP", - "targetPort": 8080 - }, - { - "name": "http-monitoring", - "port": 15014, - "protocol": "TCP", - "targetPort": 15014 - } - ], - "selector": { - "istio": "pilot" - }, - "sessionAffinity": "None", - "type": "ClusterIP" - } -}` - - testInvalidJSON := `invalid json` - - parseJSONToK8sObjectTests := []struct { - desc string - objString string - wantGroup string - wantKind string - wantName string - wantNamespace string - wantErr bool - }{ - {"ParseJsonToK8sDeployment", testDeploymentJSON, "apps", "Deployment", "istio-citadel", "dubbo-system", false}, - {"ParseJsonToK8sPod", testPodJSON, "", "Pod", "istio-galley-75bcd59768-hpt5t", "dubbo-system", false}, - {"ParseJsonToK8sService", testServiceJSON, "", "Service", "istio-pilot", "dubbo-system", false}, - {"ParseJsonError", testInvalidJSON, "", "", "", "", true}, - } - - for _, tt := range parseJSONToK8sObjectTests { - t.Run(tt.desc, func(t *testing.T) { - k8sObj, err := ParseJSONToK8sObject([]byte(tt.objString)) - if err == nil { - if tt.wantErr { - t.Errorf("ParseJsonToK8sObject(%s): should be error", tt.desc) - } - k8sObjStr := k8sObj.YAMLDebugString() - if k8sObj.Group != tt.wantGroup { - t.Errorf("ParseJsonToK8sObject(%s): got group %s for k8s object %s, want %s", tt.desc, k8sObj.Group, k8sObjStr, tt.wantGroup) - } - if k8sObj.Kind != tt.wantKind { - t.Errorf("ParseJsonToK8sObject(%s): got kind %s for k8s object %s, want %s", tt.desc, k8sObj.Kind, k8sObjStr, tt.wantKind) - } - if k8sObj.Name != tt.wantName { - t.Errorf("ParseJsonToK8sObject(%s): got name %s for k8s object %s, want %s", tt.desc, k8sObj.Name, k8sObjStr, tt.wantName) - } - if k8sObj.Namespace != tt.wantNamespace { - t.Errorf("ParseJsonToK8sObject(%s): got group %s for k8s object %s, want %s", tt.desc, k8sObj.Namespace, k8sObjStr, tt.wantNamespace) - } - } else if !tt.wantErr { - t.Errorf("ParseJsonToK8sObject(%s): got unexpected error: %v", tt.desc, err) - } - }) - } -} - -func TestParseYAMLToK8sObject(t *testing.T) { - testDeploymentYaml := `apiVersion: apps/v1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system - labels: - istio: citadel -spec: - replicas: 1 - selector: - matchLabels: - istio: citadel - template: - metadata: - labels: - istio: citadel - spec: - containers: - - name: citadel - image: docker.io/istio/citadel:1.1.8 - args: - - "--append-dns-names=true" - - "--grpc-port=8060" - - "--grpc-hostname=citadel" - - "--citadel-storage-namespace=dubbo-system" - - "--custom-dns-names=istio-pilot-service-account.dubbo-system:istio-pilot.dubbo-system" - - "--monitoring-port=15014" - - "--self-signed-ca=true"` - - testPodYaml := `apiVersion: v1 -kind: Pod -metadata: - name: istio-galley-75bcd59768-hpt5t - namespace: dubbo-system - labels: - istio: galley -spec: - containers: - - name: galley - image: docker.io/istio/galley:1.1.8 - command: - - "/usr/local/bin/galley" - - server - - "--meshConfigFile=/etc/mesh-config/mesh" - - "--livenessProbeInterval=1s" - - "--livenessProbePath=/healthliveness" - - "--readinessProbePath=/healthready" - - "--readinessProbeInterval=1s" - - "--deployment-namespace=dubbo-system" - - "--insecure=true" - - "--validation-webhook-config-file" - - "/etc/config/validatingwebhookconfiguration.yaml" - - "--monitoringPort=15014" - - "--log_output_level=default:info" - ports: - - containerPort: 443 - protocol: TCP - - containerPort: 15014 - protocol: TCP - - containerPort: 9901 - protocol: TCP` - - testServiceYaml := `apiVersion: v1 -kind: Service -metadata: - labels: - app: pilot - name: istio-pilot - namespace: dubbo-system -spec: - clusterIP: 10.102.230.31 - ports: - - name: grpc-xds - port: 15010 - protocol: TCP - targetPort: 15010 - - name: https-xds - port: 15011 - protocol: TCP - targetPort: 15011 - - name: http-legacy-discovery - port: 8080 - protocol: TCP - targetPort: 8080 - - name: http-monitoring - port: 15014 - protocol: TCP - targetPort: 15014 - selector: - istio: pilot - sessionAffinity: None - type: ClusterIP` - - parseYAMLToK8sObjectTests := []struct { - desc string - objString string - wantGroup string - wantKind string - wantName string - wantNamespace string - }{ - {"ParseYamlToK8sDeployment", testDeploymentYaml, "apps", "Deployment", "istio-citadel", "dubbo-system"}, - {"ParseYamlToK8sPod", testPodYaml, "", "Pod", "istio-galley-75bcd59768-hpt5t", "dubbo-system"}, - {"ParseYamlToK8sService", testServiceYaml, "", "Service", "istio-pilot", "dubbo-system"}, - } - - for _, tt := range parseYAMLToK8sObjectTests { - t.Run(tt.desc, func(t *testing.T) { - k8sObj, err := ParseYAMLToK8sObject([]byte(tt.objString)) - if err != nil { - k8sObjStr := k8sObj.YAMLDebugString() - if k8sObj.Group != tt.wantGroup { - t.Errorf("ParseYAMLToK8sObject(%s): got group %s for k8s object %s, want %s", tt.desc, k8sObj.Group, k8sObjStr, tt.wantGroup) - } - if k8sObj.Group != tt.wantGroup { - t.Errorf("ParseYAMLToK8sObject(%s): got kind %s for k8s object %s, want %s", tt.desc, k8sObj.Kind, k8sObjStr, tt.wantKind) - } - if k8sObj.Name != tt.wantName { - t.Errorf("ParseYAMLToK8sObject(%s): got name %s for k8s object %s, want %s", tt.desc, k8sObj.Name, k8sObjStr, tt.wantName) - } - if k8sObj.Namespace != tt.wantNamespace { - t.Errorf("ParseYAMLToK8sObject(%s): got group %s for k8s object %s, want %s", tt.desc, k8sObj.Namespace, k8sObjStr, tt.wantNamespace) - } - } - }) - } -} - -func TestParseK8sObjectsFromYAMLManifest(t *testing.T) { - testDeploymentYaml := `apiVersion: apps/v1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system - labels: - istio: citadel -spec: - replicas: 1 - selector: - matchLabels: - istio: citadel - template: - metadata: - labels: - istio: citadel - spec: - containers: - - name: citadel - image: docker.io/istio/citadel:1.1.8 - args: - - "--append-dns-names=true" - - "--grpc-port=8060" - - "--grpc-hostname=citadel" - - "--citadel-storage-namespace=dubbo-system" - - "--custom-dns-names=istio-pilot-service-account.dubbo-system:istio-pilot.dubbo-system" - - "--monitoring-port=15014" - - "--self-signed-ca=true"` - - testPodYaml := `apiVersion: v1 -kind: Pod -metadata: - name: istio-galley-75bcd59768-hpt5t - namespace: dubbo-system - labels: - istio: galley -spec: - containers: - - name: galley - image: docker.io/istio/galley:1.1.8 - command: - - "/usr/local/bin/galley" - - server - - "--meshConfigFile=/etc/mesh-config/mesh" - - "--livenessProbeInterval=1s" - - "--livenessProbePath=/healthliveness" - - "--readinessProbePath=/healthready" - - "--readinessProbeInterval=1s" - - "--deployment-namespace=dubbo-system" - - "--insecure=true" - - "--validation-webhook-config-file" - - "/etc/config/validatingwebhookconfiguration.yaml" - - "--monitoringPort=15014" - - "--log_output_level=default:info" - ports: - - containerPort: 443 - protocol: TCP - - containerPort: 15014 - protocol: TCP - - containerPort: 9901 - protocol: TCP` - - testServiceYaml := `apiVersion: v1 -kind: Service -metadata: - labels: - app: pilot - name: istio-pilot - namespace: dubbo-system -spec: - clusterIP: 10.102.230.31 - ports: - - name: grpc-xds - port: 15010 - protocol: TCP - targetPort: 15010 - - name: https-xds - port: 15011 - protocol: TCP - targetPort: 15011 - - name: http-legacy-discovery - port: 8080 - protocol: TCP - targetPort: 8080 - - name: http-monitoring - port: 15014 - protocol: TCP - targetPort: 15014 - selector: - istio: pilot - sessionAffinity: None - type: ClusterIP` - - parseK8sObjectsFromYAMLManifestTests := []struct { - desc string - objsMap map[string]string - }{ - { - "FromHybridYAMLManifest", - map[string]string{ - "Deployment:dubbo-system:istio-citadel": testDeploymentYaml, - "Pod:dubbo-system:istio-galley-75bcd59768-hpt5t": testPodYaml, - "Service:dubbo-system:istio-pilot": testServiceYaml, - }, - }, - } - - for _, tt := range parseK8sObjectsFromYAMLManifestTests { - t.Run(tt.desc, func(t *testing.T) { - testManifestYaml := strings.Join([]string{testDeploymentYaml, testPodYaml, testServiceYaml}, YAMLSeparator) - gotK8sObjs, err := ParseK8sObjectsFromYAMLManifest(testManifestYaml) - if err != nil { - gotK8sObjsMap := gotK8sObjs.ToMap() - for objHash, want := range tt.objsMap { - if gotObj, ok := gotK8sObjsMap[objHash]; ok { - gotObjYaml := gotObj.YAMLDebugString() - if !util.IsYAMLEqual(gotObjYaml, want) { - t.Errorf("ParseK8sObjectsFromYAMLManifest(%s): got:\n%s\n\nwant:\n%s\nDiff:\n%s\n", tt.desc, gotObjYaml, want, util.YAMLDiff(gotObjYaml, want)) - } - } - } - } - }) - } -} - -func TestK8sObject_Equal(t *testing.T) { - obj1 := K8sObject{ - object: &unstructured.Unstructured{Object: map[string]interface{}{ - "key": "value1", - }}, - } - obj2 := K8sObject{ - object: &unstructured.Unstructured{Object: map[string]interface{}{ - "key": "value2", - }}, - } - cases := []struct { - desc string - o1 *K8sObject - o2 *K8sObject - want bool - }{ - { - desc: "Equals", - o1: &obj1, - o2: &obj1, - want: true, - }, - { - desc: "NotEquals", - o1: &obj1, - o2: &obj2, - want: false, - }, - { - desc: "NilSource", - o1: nil, - o2: &obj2, - want: false, - }, - { - desc: "NilDest", - o1: &obj1, - o2: nil, - want: false, - }, - { - desc: "TwoNils", - o1: nil, - o2: nil, - want: true, - }, - } - for _, tt := range cases { - t.Run(tt.desc, func(t *testing.T) { - res := tt.o1.Equal(tt.o2) - if res != tt.want { - t.Errorf("got %v, want: %v", res, tt.want) - } - }) - } -} - -func TestK8sObject_ResolveK8sConflict(t *testing.T) { - getK8sObject := func(ystr string) *K8sObject { - o, err := ParseYAMLToK8sObject([]byte(ystr)) - if err != nil { - panic(err) - } - // Ensure that json data is in sync. - // Since the object was created using yaml, json is empty. - // make sure the object json is set correctly. - o.json, _ = o.JSON() - return o - } - - cases := []struct { - desc string - o1 *K8sObject - o2 *K8sObject - }{ - { - desc: "not applicable kind", - o1: getK8sObject(` - apiVersion: v1 - kind: Service - metadata: - labels: - app: pilot - name: istio-pilot - namespace: dubbo-system - spec: - clusterIP: 10.102.230.31`), - o2: getK8sObject(` - apiVersion: v1 - kind: Service - metadata: - labels: - app: pilot - name: istio-pilot - namespace: dubbo-system - spec: - clusterIP: 10.102.230.31`), - }, - { - desc: "only minAvailable is set", - o1: getK8sObject(` - apiVersion: policy/v1 - kind: PodDisruptionBudget - metadata: - name: zk-pdb - spec: - minAvailable: 2`), - o2: getK8sObject(` - apiVersion: policy/v1 - kind: PodDisruptionBudget - metadata: - name: zk-pdb - spec: - minAvailable: 2`), - }, - { - desc: "only maxUnavailable is set", - o1: getK8sObject(` - apiVersion: policy/v1 - kind: PodDisruptionBudget - metadata: - name: istio - spec: - maxUnavailable: 3`), - o2: getK8sObject(` - apiVersion: policy/v1 - kind: PodDisruptionBudget - metadata: - name: istio - spec: - maxUnavailable: 3`), - }, - { - desc: "minAvailable and maxUnavailable are set to none zero values", - o1: getK8sObject(` - apiVersion: policy/v1 - kind: PodDisruptionBudget - metadata: - name: istio - spec: - maxUnavailable: 50% - minAvailable: 3`), - o2: getK8sObject(` - apiVersion: policy/v1 - kind: PodDisruptionBudget - metadata: - name: istio - spec: - maxUnavailable: 50%`), - }, - { - desc: "both minAvailable and maxUnavailable are set default", - o1: getK8sObject(` - apiVersion: policy/v1 - kind: PodDisruptionBudget - metadata: - name: istio - spec: - minAvailable: 0 - maxUnavailable: 0`), - o2: getK8sObject(` - apiVersion: policy/v1 - kind: PodDisruptionBudget - metadata: - name: istio - spec: - maxUnavailable: 0 - minAvailable: 0`), - }, - } - for _, tt := range cases { - t.Run(tt.desc, func(t *testing.T) { - newObj := tt.o1.ResolveK8sConflict() - if !newObj.Equal(tt.o2) { - newObjjson, _ := newObj.JSON() - wantedObjjson, _ := tt.o2.JSON() - t.Errorf("Got: %s, want: %s", string(newObjjson), string(wantedObjjson)) - } - }) - } -} diff --git a/operator/pkg/patch/patch.go b/operator/pkg/patch/patch.go deleted file mode 100644 index 3b2cb876f..000000000 --- a/operator/pkg/patch/patch.go +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/* -Package patch implements a simple patching mechanism for k8s resources. -Paths are specified in the form a.b.c.[key:value].d.[list_entry_value], where: - - [key:value] selects a list entry in list c which contains an entry with key:value - - [list_entry_value] selects a list entry in list d which is a regex match of list_entry_value. - -Some examples are given below. Given a resource: - -kind: Deployment -metadata: - - name: istio-citadel - namespace: dubbo-system - -a: - - b: - - name: n1 - value: v1 - - name: n2 - list: - - "vv1" - - vv2=foo - -values and list entries can be added, modifed or deleted. - -# MODIFY - -1. set v1 to v1new - - path: a.b.[name:n1].value - value: v1new - -2. set vv1 to vv3 - - // Note the lack of quotes around vv1 (see NOTES below). - path: a.b.[name:n2].list.[vv1] - value: vv3 - -3. set vv2=foo to vv2=bar (using regex match) - - path: a.b.[name:n2].list.[vv2] - value: vv2=bar - -4. replace a port whose port was 15010 - - - path: spec.ports.[port:15010] - value: - port: 15020 - name: grpc-xds - protocol: TCP - -# DELETE - -1. Delete container with name: n1 - - path: a.b.[name:n1] - -2. Delete list value vv1 - - path: a.b.[name:n2].list.[vv1] - -# ADD - -1. Add vv3 to list - - path: a.b.[name:n2].list.[1000] - value: vv3 - -Note: the value 1000 is an example. That value used in the patch should -be a value greater than number of the items in the list. Choose 1000 is -just an example which normally is greater than the most of the lists used. - -2. Add new key:value to container name: n1 - - path: a.b.[name:n1] - value: - new_attr: v3 - -*NOTES* -- Due to loss of string quoting during unmarshaling, keys and values should not be string quoted, even if they appear -that way in the object being patched. -- [key:value] treats ':' as a special separator character. Any ':' in the key or value string must be escaped as \:. -*/ -package patch - -import ( - "fmt" - "strings" -) - -import ( - yaml2 "gopkg.in/yaml.v2" - "istio.io/api/operator/v1alpha1" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/helm" - "github.com/apache/dubbo-go-pixiu/operator/pkg/metrics" - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" - "github.com/apache/dubbo-go-pixiu/operator/pkg/tpath" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -var scope = log.RegisterScope("patch", "patch", 0) - -// overlayMatches reports whether obj matches the overlay for either the default namespace or no namespace (cluster scope). -func overlayMatches(overlay *v1alpha1.K8SObjectOverlay, obj *object.K8sObject, defaultNamespace string) bool { - oh := obj.Hash() - if oh == object.Hash(overlay.Kind, defaultNamespace, overlay.Name) || - oh == object.Hash(overlay.Kind, "", overlay.Name) { - return true - } - return false -} - -// YAMLManifestPatch patches a base YAML in the given namespace with a list of overlays. -// Each overlay has the format described in the K8SObjectOverlay definition. -// It returns the patched manifest YAML. -func YAMLManifestPatch(baseYAML string, defaultNamespace string, overlays []*v1alpha1.K8SObjectOverlay) (string, error) { - var ret strings.Builder - var errs util.Errors - objs, err := object.ParseK8sObjectsFromYAMLManifest(baseYAML) - if err != nil { - return "", err - } - - matches := make(map[*v1alpha1.K8SObjectOverlay]object.K8sObjects) - // Try to apply the defined overlays. - for _, obj := range objs { - oy, err := obj.YAML() - if err != nil { - errs = util.AppendErr(errs, fmt.Errorf("object to YAML error (%s) for base object: \n%s", err, obj.YAMLDebugString())) - continue - } - oys := string(oy) - for _, overlay := range overlays { - if overlayMatches(overlay, obj, defaultNamespace) { - matches[overlay] = append(matches[overlay], obj) - var errs2 util.Errors - oys, errs2 = applyPatches(obj, overlay.Patches) - errs = util.AppendErrs(errs, errs2) - } - } - if _, err := ret.WriteString(oys + helm.YAMLSeparator); err != nil { - errs = util.AppendErr(errs, fmt.Errorf("writeString: %s", err)) - } - } - - for _, overlay := range overlays { - // Each overlay should have exactly one match in the output manifest. - switch { - case len(matches[overlay]) == 0: - errs = util.AppendErr(errs, fmt.Errorf("overlay for %s:%s does not match any object in output manifest. Available objects are:\n%s", - overlay.Kind, overlay.Name, strings.Join(objs.Keys(), "\n"))) - case len(matches[overlay]) > 1: - errs = util.AppendErr(errs, fmt.Errorf("overlay for %s:%s matches multiple objects in output manifest:\n%s", - overlay.Kind, overlay.Name, strings.Join(objs.Keys(), "\n"))) - } - } - - return ret.String(), errs.ToError() -} - -// applyPatches applies the given patches against the given object. It returns the resulting patched YAML if successful, -// or a list of errors otherwise. -func applyPatches(base *object.K8sObject, patches []*v1alpha1.K8SObjectOverlay_PathValue) (outYAML string, errs util.Errors) { - bo := make(map[interface{}]interface{}) - by, err := base.YAML() - if err != nil { - return "", util.NewErrs(err) - } - // Use yaml2 specifically to allow interface{} as key which WritePathContext treats specially - err = yaml2.Unmarshal(by, &bo) - if err != nil { - return "", util.NewErrs(err) - } - for _, p := range patches { - v := p.Value.AsInterface() - if strings.TrimSpace(p.Path) == "" { - scope.Warnf("value=%s has empty path, skip\n", v) - continue - } - scope.Debugf("applying path=%s, value=%s\n", p.Path, v) - inc, _, err := tpath.GetPathContext(bo, util.PathFromString(p.Path), true) - if err != nil { - errs = util.AppendErr(errs, err) - metrics.ManifestPatchErrorTotal.Increment() - continue - } - - err = tpath.WritePathContext(inc, v, false) - if err != nil { - errs = util.AppendErr(errs, err) - metrics.ManifestPatchErrorTotal.Increment() - } - } - oy, err := yaml2.Marshal(bo) - if err != nil { - return "", util.AppendErr(errs, err) - } - return string(oy), errs -} diff --git a/operator/pkg/patch/patch_test.go b/operator/pkg/patch/patch_test.go deleted file mode 100644 index 1178a2de7..000000000 --- a/operator/pkg/patch/patch_test.go +++ /dev/null @@ -1,522 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package patch - -import ( - "fmt" - "testing" -) - -import ( - "istio.io/api/operator/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -func TestPatchYAMLManifestSuccess(t *testing.T) { - base := ` -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system -a: - b: - - name: n1 - value: v1 - - name: n2 - list: - - v1 - - v2 - - v3_regex - c: -` - tests := []struct { - desc string - path string - value string - want string - wantErr string - }{ - { - desc: "ModifyListEntryValue", - path: `a.b.[name:n1].value`, - value: `v2`, - want: ` -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system -a: - b: - - name: n1 - value: v2 - - list: - - v1 - - v2 - - v3_regex - name: n2 - c: -`, - }, - { - desc: "ModifyListEntryValueQuoted", - path: `a.b.[name:n1].value`, - value: `v2`, - want: ` -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system -a: - b: - - name: "n1" - value: v2 - - list: - - v1 - - v2 - - v3_regex - name: n2 - c: -`, - }, - { - desc: "ModifyListEntry", - path: `a.b.[name:n2].list.[v2]`, - value: `v3`, - want: ` -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system -a: - b: - - name: n1 - value: v1 - - list: - - v1 - - v3 - - v3_regex - name: n2 - c: -`, - }, - { - desc: "DeleteListEntry", - path: `a.b.[name:n1]`, - want: ` -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system -a: - b: - - list: - - v1 - - v2 - - v3_regex - name: n2 - c: -`, - }, - { - desc: "DeleteListEntryValue", - path: `a.b.[name:n2].list.[v2]`, - want: ` -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system -a: - b: - - name: n1 - value: v1 - - list: - - v1 - - v3_regex - name: n2 - c: -`, - }, - { - desc: "DeleteListEntryValueRegex", - path: `a.b.[name:n2].list.[v3]`, - want: ` -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system -a: - b: - - name: n1 - value: v1 - - list: - - v1 - - v2 - name: n2 - c: -`, - }, - { - desc: "UpdateNullNode", - path: `a.c`, - value: ` - d: n3`, - want: ` -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system -a: - b: - - name: n1 - value: v1 - - name: n2 - list: - - v1 - - v2 - - v3_regex - c: - d: n3 -`, - }, - { - desc: "AppendToListEntry", - path: `a.b.[name:n2].list.[3]`, - value: `v4`, - want: ` -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system -a: - b: - - name: n1 - value: v1 - - list: - - v1 - - v2 - - v3_regex - - v4 - name: n2 - c: -`, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - rc := &v1alpha1.KubernetesResourcesSpec{} - oh := makeOverlayHeader(tt.path, tt.value) - err := util.UnmarshalWithJSONPB(oh, rc, false) - if err != nil { - t.Fatalf("unmarshalWithJSONPB(%s): got error %s for string:\n%s\n", tt.desc, err, oh) - } - got, err := YAMLManifestPatch(base, "dubbo-system", rc.Overlays) - if gotErr, wantErr := errToString(err), tt.wantErr; gotErr != wantErr { - t.Fatalf("YAMLManifestPatch(%s): gotErr:%s, wantErr:%s", tt.desc, gotErr, wantErr) - } - if want := tt.want; !util.IsYAMLEqual(got, want) { - t.Errorf("YAMLManifestPatch(%s): got:\n%s\n\nwant:\n%s\nDiff:\n%s\n", tt.desc, got, want, util.YAMLDiff(got, want)) - } - }) - } -} - -func TestPatchYAMLManifestRealYAMLSuccess(t *testing.T) { - base := ` -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system -spec: - template: - spec: - containers: - - name: deleteThis - foo: bar - - name: galley - ports: - - containerPort: 443 - - containerPort: 15014 - - containerPort: 9901 - command: - - /usr/local/bin/galley - - server - - --meshConfigFile=/etc/mesh-config/mesh - - --livenessProbeInterval=1s - - --validation-webhook-config-file -` - - tests := []struct { - desc string - path string - value string - want string - wantErr string - }{ - { - desc: "DeleteLeafListLeaf", - path: `spec.template.spec.containers.[name:galley].command.[--validation-webhook-config-file]`, - want: ` -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system -spec: - template: - spec: - containers: - - foo: bar - name: deleteThis - - command: - - /usr/local/bin/galley - - server - - --meshConfigFile=/etc/mesh-config/mesh - - --livenessProbeInterval=1s - name: galley - ports: - - containerPort: 443 - - containerPort: 15014 - - containerPort: 9901 -`, - }, - { - desc: "UpdateListItem", - path: `spec.template.spec.containers.[name:galley].command.[--livenessProbeInterval]`, - value: `--livenessProbeInterval=1111s`, - want: ` -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system -spec: - template: - spec: - containers: - - foo: bar - name: deleteThis - - command: - - /usr/local/bin/galley - - server - - --meshConfigFile=/etc/mesh-config/mesh - - --livenessProbeInterval=1111s - - --validation-webhook-config-file - name: galley - ports: - - containerPort: 443 - - containerPort: 15014 - - containerPort: 9901 -`, - }, - { - desc: "UpdateLeaf", - path: `spec.template.spec.containers.[name:galley].ports.[containerPort:15014].containerPort`, - value: `22222`, - want: ` -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system -spec: - template: - spec: - containers: - - foo: bar - name: deleteThis - - command: - - /usr/local/bin/galley - - server - - --meshConfigFile=/etc/mesh-config/mesh - - --livenessProbeInterval=1s - - --validation-webhook-config-file - name: galley - ports: - - containerPort: 443 - - containerPort: 22222 - - containerPort: 9901 -`, - }, - { - desc: "DeleteLeafList", - path: `spec.template.spec.containers.[name:galley].ports.[containerPort:9901]`, - want: ` -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system -spec: - template: - spec: - containers: - - foo: bar - name: deleteThis - - command: - - /usr/local/bin/galley - - server - - --meshConfigFile=/etc/mesh-config/mesh - - --livenessProbeInterval=1s - - --validation-webhook-config-file - name: galley - ports: - - containerPort: 443 - - containerPort: 15014 -`, - }, - { - desc: "DeleteInternalNode", - path: `spec.template.spec.containers.[name:deleteThis]`, - want: ` -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system -spec: - template: - spec: - containers: - - command: - - /usr/local/bin/galley - - server - - --meshConfigFile=/etc/mesh-config/mesh - - --livenessProbeInterval=1s - - --validation-webhook-config-file - name: galley - ports: - - containerPort: 443 - - containerPort: 15014 - - containerPort: 9901 -`, - }, - { - desc: "DeleteLeafListEntry", - path: `spec.template.spec.containers.[name:galley].command.[--validation-webhook-config-file]`, - want: ` -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system -spec: - template: - spec: - containers: - - foo: bar - name: deleteThis - - command: - - /usr/local/bin/galley - - server - - --meshConfigFile=/etc/mesh-config/mesh - - --livenessProbeInterval=1s - name: galley - ports: - - containerPort: 443 - - containerPort: 15014 - - containerPort: 9901 -`, - }, - { - desc: "UpdateInteriorNode", - path: `spec.template.spec.containers.[name:galley].ports.[containerPort:15014]`, - value: ` - fooPort: 15015`, - want: ` -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: istio-citadel - namespace: dubbo-system -spec: - template: - spec: - containers: - - foo: bar - name: deleteThis - - command: - - /usr/local/bin/galley - - server - - --meshConfigFile=/etc/mesh-config/mesh - - --livenessProbeInterval=1s - - --validation-webhook-config-file - name: galley - ports: - - containerPort: 443 - - fooPort: 15015 - - containerPort: 9901 - -`, - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - rc := &v1alpha1.KubernetesResourcesSpec{} - t.Log(makeOverlayHeader(tt.path, tt.value)) - err := util.UnmarshalWithJSONPB(makeOverlayHeader(tt.path, tt.value), rc, false) - if err != nil { - t.Fatalf("unmarshalWithJSONPB(%s): got error %s", tt.desc, err) - } - got, err := YAMLManifestPatch(base, "dubbo-system", rc.Overlays) - if gotErr, wantErr := errToString(err), tt.wantErr; gotErr != wantErr { - t.Fatalf("YAMLManifestPatch(%s): gotErr:%s, wantErr:%s", tt.desc, gotErr, wantErr) - } - if want := tt.want; !util.IsYAMLEqual(got, want) { - t.Errorf("YAMLManifestPatch(%s): got:\n%s\n\nwant:\n%s\nDiff:\n%s\n", tt.desc, got, want, util.YAMLDiff(got, want)) - } - }) - } -} - -func makeOverlayHeader(path, value string) string { - const ( - patchCommon = `overlays: -- kind: Deployment - name: istio-citadel - patches: - - path: ` - valueStr = ` value: ` - ) - - ret := patchCommon - ret += fmt.Sprintf("%s\n", path) - if value != "" { - ret += fmt.Sprintf("%s%s\n", valueStr, value) - } - return ret -} - -// errToString returns the string representation of err and the empty string if -// err is nil. -func errToString(err error) string { - if err == nil { - return "" - } - return err.Error() -} diff --git a/operator/pkg/tpath/struct.go b/operator/pkg/tpath/struct.go deleted file mode 100644 index ce4289576..000000000 --- a/operator/pkg/tpath/struct.go +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/* -struct.go contains functions for traversing and modifying trees of Go structs. -*/ -package tpath - -import ( - "fmt" - "reflect" - "strconv" -) - -import ( - "google.golang.org/protobuf/types/known/structpb" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -// GetFromStructPath returns the value at path from the given node, or false if the path does not exist. -func GetFromStructPath(node interface{}, path string) (interface{}, bool, error) { - return getFromStructPath(node, util.PathFromString(path)) -} - -// getFromStructPath is the internal implementation of GetFromStructPath which recurses through a tree of Go structs -// given a path. It terminates when the end of the path is reached or a path element does not exist. -func getFromStructPath(node interface{}, path util.Path) (interface{}, bool, error) { - scope.Debugf("getFromStructPath path=%s, node(%T)", path, node) - if len(path) == 0 { - scope.Debugf("getFromStructPath returning node(%T)%v", node, node) - return node, !util.IsValueNil(node), nil - } - // For protobuf types, switch them out with standard types; otherwise we will traverse protobuf internals rather - // than the standard representation - if v, ok := node.(*structpb.Struct); ok { - node = v.AsMap() - } - if v, ok := node.(*structpb.Value); ok { - node = v.AsInterface() - } - val := reflect.ValueOf(node) - kind := reflect.TypeOf(node).Kind() - var structElems reflect.Value - - switch kind { - case reflect.Map: - if path[0] == "" { - return nil, false, fmt.Errorf("getFromStructPath path %s, empty map key value", path) - } - mapVal := val.MapIndex(reflect.ValueOf(path[0])) - if !mapVal.IsValid() { - return nil, false, fmt.Errorf("getFromStructPath path %s, path does not exist", path) - } - return getFromStructPath(mapVal.Interface(), path[1:]) - case reflect.Slice: - idx, err := strconv.Atoi(path[0]) - if err != nil { - return nil, false, fmt.Errorf("getFromStructPath path %s, expected index number, got %s", path, path[0]) - } - return getFromStructPath(val.Index(idx).Interface(), path[1:]) - case reflect.Ptr: - structElems = reflect.ValueOf(node).Elem() - if !util.IsStruct(structElems) { - return nil, false, fmt.Errorf("getFromStructPath path %s, expected struct ptr, got %T", path, node) - } - default: - return nil, false, fmt.Errorf("getFromStructPath path %s, unsupported type %T", path, node) - } - - if util.IsNilOrInvalidValue(structElems) { - return nil, false, nil - } - - for i := 0; i < structElems.NumField(); i++ { - fieldName := structElems.Type().Field(i).Name - - if fieldName != path[0] { - continue - } - - fv := structElems.Field(i) - return getFromStructPath(fv.Interface(), path[1:]) - } - - return nil, false, nil -} - -// SetFromPath sets out with the value at path from node. out is not set if the path doesn't exist or the value is nil. -// All intermediate along path must be type struct ptr. Out must be either a struct ptr or map ptr. -// TODO: move these out to a separate package (istio/istio#15494). -func SetFromPath(node interface{}, path string, out interface{}) (bool, error) { - val, found, err := GetFromStructPath(node, path) - if err != nil { - return false, err - } - if !found { - return false, nil - } - - return true, Set(val, out) -} - -// Set sets out with the value at path from node. out is not set if the path doesn't exist or the value is nil. -func Set(val, out interface{}) error { - // Special case: map out type must be set through map ptr. - if util.IsMap(val) && util.IsMapPtr(out) { - reflect.ValueOf(out).Elem().Set(reflect.ValueOf(val)) - return nil - } - if util.IsSlice(val) && util.IsSlicePtr(out) { - reflect.ValueOf(out).Elem().Set(reflect.ValueOf(val)) - return nil - } - - if reflect.TypeOf(val) != reflect.TypeOf(out) { - return fmt.Errorf("setFromPath from type %T != to type %T, %v", val, out, util.IsSlicePtr(out)) - } - - if !reflect.ValueOf(out).CanSet() { - return fmt.Errorf("can't set %v(%T) to out type %T", val, val, out) - } - reflect.ValueOf(out).Set(reflect.ValueOf(val)) - return nil -} diff --git a/operator/pkg/tpath/struct_test.go b/operator/pkg/tpath/struct_test.go deleted file mode 100644 index 4f8c3524e..000000000 --- a/operator/pkg/tpath/struct_test.go +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package tpath - -import ( - "testing" -) - -import ( - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -func TestGetFromStructPath(t *testing.T) { - tests := []struct { - desc string - nodeYAML string - path string - wantYAML string - wantFound bool - wantErr string - }{ - { - desc: "GetStructItem", - nodeYAML: ` -a: va -b: vb -c: - d: vd - e: - f: vf -g: - h: - - i: vi - j: vj - k: - l: - m: vm - n: vn -`, - path: "c", - wantYAML: ` -d: vd -e: - f: vf -`, - wantFound: true, - }, - { - desc: "GetSliceEntryItem", - nodeYAML: ` -a: va -b: vb -c: - d: vd - e: - f: vf -g: - h: - - i: vi - j: vj - k: - l: - m: vm - n: vm -`, - path: "g.h.0", - wantYAML: ` -i: vi -j: vj -k: - l: - m: vm - n: vm -`, - wantFound: true, - }, - { - desc: "GetMapEntryItem", - nodeYAML: ` -a: va -b: vb -c: - d: vd - e: - f: vf -g: - h: - - i: vi - j: vj - k: - l: - m: vm - n: vm -`, - path: "g.h.0.k", - wantYAML: ` -l: - m: vm - n: vm -`, - wantFound: true, - }, - { - desc: "GetPathNotExists", - nodeYAML: ` -a: va -b: vb -c: - d: vd - e: - f: vf -g: - h: - - i: vi - j: vj - k: - l: - m: vm - n: vm -`, - path: "c.d.e", - wantFound: false, - wantErr: "getFromStructPath path e, unsupported type string", - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - rnode := make(map[string]interface{}) - if err := yaml.Unmarshal([]byte(tt.nodeYAML), &rnode); err != nil { - t.Fatal(err) - } - GotOut, GotFound, gotErr := GetFromStructPath(rnode, tt.path) - if GotFound != tt.wantFound { - t.Fatalf("GetFromStructPath(%s): gotFound:%v, wantFound:%v", tt.desc, GotFound, tt.wantFound) - } - if gotErr, wantErr := errToString(gotErr), tt.wantErr; gotErr != wantErr { - t.Fatalf("GetFromStructPath(%s): gotErr:%s, wantErr:%s", tt.desc, gotErr, wantErr) - } - if tt.wantErr != "" || !tt.wantFound { - return - } - gotYAML := util.ToYAML(GotOut) - diff := util.YAMLDiff(gotYAML, tt.wantYAML) - if diff != "" { - t.Errorf("GetFromStructPath(%s): YAML of gotOut:\n%s\n, YAML of wantOut:\n%s\n, diff:\n%s\n", tt.desc, gotYAML, tt.wantYAML, diff) - } - }) - } -} diff --git a/operator/pkg/tpath/tree.go b/operator/pkg/tpath/tree.go deleted file mode 100644 index 7773dd3d5..000000000 --- a/operator/pkg/tpath/tree.go +++ /dev/null @@ -1,578 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/* -tree.go contains functions for traversing and updating a tree constructed from yaml or json.Unmarshal. -Nodes in such trees have the form map[interface{}]interface{} or map[interface{}][]interface{}. -For some tree updates, like delete or append, it's necessary to have access to the parent node. PathContext is a -tree constructed during tree traversal that gives access to ancestor nodes all the way up to the root, which can be -used for this purpose. -*/ -package tpath - -import ( - "encoding/json" - "errors" - "fmt" - "reflect" - "regexp" - "strconv" - "strings" -) - -import ( - "gopkg.in/yaml.v2" - "istio.io/pkg/log" - yaml2 "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -var scope = log.RegisterScope("tpath", "tree traverser", 0) - -// PathContext provides a means for traversing a tree towards the root. -type PathContext struct { - // Parent in the Parent of this PathContext. - Parent *PathContext - // KeyToChild is the key required to reach the child. - KeyToChild interface{} - // Node is the actual Node in the data tree. - Node interface{} -} - -// String implements the Stringer interface. -func (nc *PathContext) String() string { - ret := "\n--------------- NodeContext ------------------\n" - if nc.Parent != nil { - ret += fmt.Sprintf("Parent.Node=\n%s\n", nc.Parent.Node) - ret += fmt.Sprintf("KeyToChild=%v\n", nc.Parent.KeyToChild) - } - - ret += fmt.Sprintf("Node=\n%s\n", nc.Node) - ret += "----------------------------------------------\n" - - return ret -} - -// GetPathContext returns the PathContext for the Node which has the given path from root. -// It returns false and and no error if the given path is not found, or an error code in other error situations, like -// a malformed path. -// It also creates a tree of PathContexts during the traversal so that Parent nodes can be updated if required. This is -// required when (say) appending to a list, where the parent list itself must be updated. -func GetPathContext(root interface{}, path util.Path, createMissing bool) (*PathContext, bool, error) { - return getPathContext(&PathContext{Node: root}, path, path, createMissing) -} - -// WritePathContext writes the given value to the Node in the given PathContext. -func WritePathContext(nc *PathContext, value interface{}, merge bool) error { - scope.Debugf("WritePathContext PathContext=%s, value=%v", nc, value) - - if !util.IsValueNil(value) { - return setPathContext(nc, value, merge) - } - - scope.Debug("delete") - if nc.Parent == nil { - return errors.New("cannot delete root element") - } - - switch { - case isSliceOrPtrInterface(nc.Parent.Node): - if err := util.DeleteFromSlicePtr(nc.Parent.Node, nc.Parent.KeyToChild.(int)); err != nil { - return err - } - if isMapOrInterface(nc.Parent.Parent.Node) { - return util.InsertIntoMap(nc.Parent.Parent.Node, nc.Parent.Parent.KeyToChild, nc.Parent.Node) - } - // TODO: The case of deleting a list.list.node element is not currently supported. - return fmt.Errorf("cannot delete path: unsupported parent.parent type %T for delete", nc.Parent.Parent.Node) - case util.IsMap(nc.Parent.Node): - return util.DeleteFromMap(nc.Parent.Node, nc.Parent.KeyToChild) - default: - } - return fmt.Errorf("cannot delete path: unsupported parent type %T for delete", nc.Parent.Node) -} - -// WriteNode writes value to the tree in root at the given path, creating any required missing internal nodes in path. -func WriteNode(root interface{}, path util.Path, value interface{}) error { - pc, _, err := getPathContext(&PathContext{Node: root}, path, path, true) - if err != nil { - return err - } - return WritePathContext(pc, value, false) -} - -// MergeNode merges value to the tree in root at the given path, creating any required missing internal nodes in path. -func MergeNode(root interface{}, path util.Path, value interface{}) error { - pc, _, err := getPathContext(&PathContext{Node: root}, path, path, true) - if err != nil { - return err - } - return WritePathContext(pc, value, true) -} - -// Find returns the value at path from the given tree, or false if the path does not exist. -// It behaves differently from GetPathContext in that it never creates map entries at the leaf and does not provide -// a way to mutate the parent of the found node. -func Find(inputTree map[string]interface{}, path util.Path) (interface{}, bool, error) { - scope.Debugf("Find path=%s", path) - if len(path) == 0 { - return nil, false, fmt.Errorf("path is empty") - } - node, found := find(inputTree, path) - return node, found, nil -} - -// Delete sets value at path of input untyped tree to nil -func Delete(root map[string]interface{}, path util.Path) (bool, error) { - pc, _, err := getPathContext(&PathContext{Node: root}, path, path, false) - if err != nil { - return false, err - } - return true, WritePathContext(pc, nil, false) -} - -// getPathContext is the internal implementation of GetPathContext. -// If createMissing is true, it creates any missing map (but NOT list) path entries in root. -func getPathContext(nc *PathContext, fullPath, remainPath util.Path, createMissing bool) (*PathContext, bool, error) { - scope.Debugf("getPathContext remainPath=%s, Node=%v", remainPath, nc.Node) - if len(remainPath) == 0 { - return nc, true, nil - } - pe := remainPath[0] - - if nc.Node == nil { - if !createMissing { - return nil, false, fmt.Errorf("node %s is zero", pe) - } - if util.IsNPathElement(pe) || util.IsKVPathElement(pe) { - nc.Node = []interface{}{} - } else { - nc.Node = make(map[string]interface{}) - } - } - - v := reflect.ValueOf(nc.Node) - if v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface { - v = v.Elem() - } - ncNode := v.Interface() - - // For list types, we need a key to identify the selected list item. This can be either a a value key of the - // form :matching_value in the case of a leaf list, or a matching key:value in the case of a non-leaf list. - if lst, ok := ncNode.([]interface{}); ok { - scope.Debug("list type") - // If the path element has the form [N], a list element is being selected by index. Return the element at index - // N if it exists. - if util.IsNPathElement(pe) { - idx, err := util.PathN(pe) - if err != nil { - return nil, false, fmt.Errorf("path %s, index %s: %s", fullPath, pe, err) - } - var foundNode interface{} - if idx >= len(lst) || idx < 0 { - if !createMissing { - return nil, false, fmt.Errorf("index %d exceeds list length %d at path %s", idx, len(lst), remainPath) - } - idx = len(lst) - foundNode = make(map[string]interface{}) - } else { - foundNode = lst[idx] - } - nn := &PathContext{ - Parent: nc, - Node: foundNode, - } - nc.KeyToChild = idx - return getPathContext(nn, fullPath, remainPath[1:], createMissing) - } - - // Otherwise the path element must have form [key:value]. In this case, go through all list elements, which - // must have map type, and try to find one which has a matching key:value. - for idx, le := range lst { - // non-leaf list, expect to match item by key:value. - if lm, ok := le.(map[interface{}]interface{}); ok { - k, v, err := util.PathKV(pe) - if err != nil { - return nil, false, fmt.Errorf("path %s: %s", fullPath, err) - } - if stringsEqual(lm[k], v) { - scope.Debugf("found matching kv %v:%v", k, v) - nn := &PathContext{ - Parent: nc, - Node: lm, - } - nc.KeyToChild = idx - nn.KeyToChild = k - if len(remainPath) == 1 { - scope.Debug("KV terminate") - return nn, true, nil - } - return getPathContext(nn, fullPath, remainPath[1:], createMissing) - } - continue - } - // repeat of the block above for the case where tree unmarshals to map[string]interface{}. There doesn't - // seem to be a way to merge this case into the above block. - if lm, ok := le.(map[string]interface{}); ok { - k, v, err := util.PathKV(pe) - if err != nil { - return nil, false, fmt.Errorf("path %s: %s", fullPath, err) - } - if stringsEqual(lm[k], v) { - scope.Debugf("found matching kv %v:%v", k, v) - nn := &PathContext{ - Parent: nc, - Node: lm, - } - nc.KeyToChild = idx - nn.KeyToChild = k - if len(remainPath) == 1 { - scope.Debug("KV terminate") - return nn, true, nil - } - return getPathContext(nn, fullPath, remainPath[1:], createMissing) - } - continue - } - // leaf list, expect path element [V], match based on value V. - v, err := util.PathV(pe) - if err != nil { - return nil, false, fmt.Errorf("path %s: %s", fullPath, err) - } - if matchesRegex(v, le) { - scope.Debugf("found matching key %v, index %d", le, idx) - nn := &PathContext{ - Parent: nc, - Node: le, - } - nc.KeyToChild = idx - return getPathContext(nn, fullPath, remainPath[1:], createMissing) - } - } - return nil, false, fmt.Errorf("path %s: element %s not found", fullPath, pe) - } - - if util.IsMap(ncNode) { - scope.Debug("map type") - var nn interface{} - if m, ok := ncNode.(map[interface{}]interface{}); ok { - nn, ok = m[pe] - if !ok { - // remainPath == 1 means the patch is creation of a new leaf. - if createMissing || len(remainPath) == 1 { - m[pe] = make(map[interface{}]interface{}) - nn = m[pe] - } else { - return nil, false, fmt.Errorf("path not found at element %s in path %s", pe, fullPath) - } - } - } - if reflect.ValueOf(ncNode).IsNil() { - ncNode = make(map[string]interface{}) - nc.Node = ncNode - } - if m, ok := ncNode.(map[string]interface{}); ok { - nn, ok = m[pe] - if !ok { - // remainPath == 1 means the patch is creation of a new leaf. - if createMissing || len(remainPath) == 1 { - nextElementNPath := len(remainPath) > 1 && util.IsNPathElement(remainPath[1]) - if nextElementNPath { - scope.Debug("map type, slice child") - m[pe] = make([]interface{}, 0) - } else { - scope.Debug("map type, map child") - m[pe] = make(map[string]interface{}) - } - nn = m[pe] - } else { - return nil, false, fmt.Errorf("path not found at element %s in path %s", pe, fullPath) - } - } - } - - npc := &PathContext{ - Parent: nc, - Node: nn, - } - // for slices, use the address so that the slice can be mutated. - if util.IsSlice(nn) { - npc.Node = &nn - } - nc.KeyToChild = pe - return getPathContext(npc, fullPath, remainPath[1:], createMissing) - } - - return nil, false, fmt.Errorf("leaf type %T in non-leaf Node %s", nc.Node, remainPath) -} - -// setPathContext writes the given value to the Node in the given PathContext, -// enlarging all PathContext lists to ensure all indexes are valid. -func setPathContext(nc *PathContext, value interface{}, merge bool) error { - processParent, err := setValueContext(nc, value, merge) - if err != nil || !processParent { - return err - } - - // If the path included insertions, process them now - if nc.Parent.Parent == nil { - return nil - } - return setPathContext(nc.Parent, nc.Parent.Node, false) // note: tail recursive -} - -// setValueContext writes the given value to the Node in the given PathContext. -// If setting the value requires growing the final slice, grows it. -func setValueContext(nc *PathContext, value interface{}, merge bool) (bool, error) { - if nc.Parent == nil { - return false, nil - } - - vv, mapFromString := tryToUnmarshalStringToYAML(value) - - switch parentNode := nc.Parent.Node.(type) { - case *interface{}: - switch vParentNode := (*parentNode).(type) { - case []interface{}: - idx := nc.Parent.KeyToChild.(int) - if idx == -1 { - // Treat -1 as insert-at-end of list - idx = len(vParentNode) - } - - if idx >= len(vParentNode) { - newElements := make([]interface{}, idx-len(vParentNode)+1) - vParentNode = append(vParentNode, newElements...) - *parentNode = vParentNode - } - - merged, err := mergeConditional(vv, nc.Node, merge) - if err != nil { - return false, err - } - - vParentNode[idx] = merged - nc.Node = merged - default: - return false, fmt.Errorf("don't know about vtype %T", vParentNode) - } - case map[string]interface{}: - key := nc.Parent.KeyToChild.(string) - - // Update is treated differently depending on whether the value is a scalar or map type. If scalar, - // insert a new element into the terminal node, otherwise replace the terminal node with the new subtree. - if ncNode, ok := nc.Node.(*interface{}); ok && !mapFromString { - switch vNcNode := (*ncNode).(type) { - case []interface{}: - switch vv.(type) { - case map[string]interface{}: - // the vv is a map, and the node is a slice - mergedValue := append(vNcNode, vv) - parentNode[key] = mergedValue - case *interface{}: - merged, err := mergeConditional(vv, vNcNode, merge) - if err != nil { - return false, err - } - - parentNode[key] = merged - nc.Node = merged - default: - // the vv is an basic JSON type (int, float, string, bool) - vv = append(vNcNode, vv) - parentNode[key] = vv - nc.Node = vv - } - default: - return false, fmt.Errorf("don't know about vnc type %T", vNcNode) - } - } else { - // For map passed as string type, the root is the new key. - if mapFromString { - if err := util.DeleteFromMap(nc.Parent.Node, nc.Parent.KeyToChild); err != nil { - return false, err - } - vm := vv.(map[string]interface{}) - newKey := getTreeRoot(vm) - return false, util.InsertIntoMap(nc.Parent.Node, newKey, vm[newKey]) - } - parentNode[key] = vv - nc.Node = vv - } - // TODO `map[interface{}]interface{}` is used by tests in operator/cmd/mesh, we should add our own tests - case map[interface{}]interface{}: - key := nc.Parent.KeyToChild.(string) - parentNode[key] = vv - nc.Node = vv - default: - return false, fmt.Errorf("don't know about type %T", parentNode) - } - - return true, nil -} - -// mergeConditional returns a merge of newVal and originalVal if merge is true, otherwise it returns newVal. -func mergeConditional(newVal, originalVal interface{}, merge bool) (interface{}, error) { - if !merge || util.IsValueNilOrDefault(originalVal) { - return newVal, nil - } - newS, err := yaml.Marshal(newVal) - if err != nil { - return nil, err - } - if util.IsYAMLEmpty(string(newS)) { - return originalVal, nil - } - originalS, err := yaml.Marshal(originalVal) - if err != nil { - return nil, err - } - if util.IsYAMLEmpty(string(originalS)) { - return newVal, nil - } - - mergedS, err := util.OverlayYAML(string(originalS), string(newS)) - if err != nil { - return nil, err - } - - if util.IsMap(originalVal) { - // For JSON compatibility - out := make(map[string]interface{}) - if err := yaml.Unmarshal([]byte(mergedS), &out); err != nil { - return nil, err - } - return out, nil - } - // For scalars and slices, copy the type - out := originalVal - if err := yaml.Unmarshal([]byte(mergedS), &out); err != nil { - return nil, err - } - return out, nil -} - -// find returns the value at path from the given tree, or false if the path does not exist. -func find(treeNode interface{}, path util.Path) (interface{}, bool) { - if len(path) == 0 || treeNode == nil { - return nil, false - } - switch nt := treeNode.(type) { - case map[interface{}]interface{}: - val := nt[path[0]] - if val == nil { - return nil, false - } - if len(path) == 1 { - return val, true - } - return find(val, path[1:]) - case map[string]interface{}: - val := nt[path[0]] - if val == nil { - return nil, false - } - if len(path) == 1 { - return val, true - } - return find(val, path[1:]) - case []interface{}: - idx, err := strconv.Atoi(path[0]) - if err != nil { - return nil, false - } - if idx >= len(nt) { - return nil, false - } - val := nt[idx] - return find(val, path[1:]) - default: - return nil, false - } -} - -// stringsEqual reports whether the string representations of a and b are equal. a and b may have different types. -func stringsEqual(a, b interface{}) bool { - return fmt.Sprint(a) == fmt.Sprint(b) -} - -// matchesRegex reports whether str regex matches pattern. -func matchesRegex(pattern, str interface{}) bool { - match, err := regexp.MatchString(fmt.Sprint(pattern), fmt.Sprint(str)) - if err != nil { - log.Errorf("bad regex expression %s", fmt.Sprint(pattern)) - return false - } - scope.Debugf("%v regex %v? %v\n", pattern, str, match) - return match -} - -// isSliceOrPtrInterface reports whether v is a slice, a ptr to slice or interface to slice. -func isSliceOrPtrInterface(v interface{}) bool { - vv := reflect.ValueOf(v) - if vv.Kind() == reflect.Ptr { - vv = vv.Elem() - } - if vv.Kind() == reflect.Interface { - vv = vv.Elem() - } - return vv.Kind() == reflect.Slice -} - -// isMapOrInterface reports whether v is a map, or interface to a map. -func isMapOrInterface(v interface{}) bool { - vv := reflect.ValueOf(v) - if vv.Kind() == reflect.Interface { - vv = vv.Elem() - } - return vv.Kind() == reflect.Map -} - -// tryToUnmarshalStringToYAML tries to unmarshal something that may be a YAML list or map into a structure. If not -// possible, returns original scalar value. -func tryToUnmarshalStringToYAML(s interface{}) (interface{}, bool) { - // If value type is a string it could either be a literal string or a map type passed as a string. Try to unmarshal - // to discover it's the latter. - vv := s - - if reflect.TypeOf(vv).Kind() == reflect.String { - sv := strings.Split(vv.(string), "\n") - // Need to be careful not to transform string literals into maps unless they really are maps, since scalar handling - // is different for inserts. - if len(sv) == 1 && strings.Contains(s.(string), ": ") || - len(sv) > 1 && strings.Contains(s.(string), ":") { - nv := make(map[string]interface{}) - if err := json.Unmarshal([]byte(vv.(string)), &nv); err == nil { - // treat JSON as string - return vv, false - } - if err := yaml2.Unmarshal([]byte(vv.(string)), &nv); err == nil { - return nv, true - } - } - } - // looks like a literal or failed unmarshal, return original type. - return vv, false -} - -// getTreeRoot returns the first key found in m. It assumes a single root tree. -func getTreeRoot(m map[string]interface{}) string { - for k := range m { - return k - } - return "" -} diff --git a/operator/pkg/tpath/tree_test.go b/operator/pkg/tpath/tree_test.go deleted file mode 100644 index dd4da6868..000000000 --- a/operator/pkg/tpath/tree_test.go +++ /dev/null @@ -1,848 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package tpath - -import ( - "testing" -) - -import ( - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -func TestWritePathContext(t *testing.T) { - rootYAML := ` -a: - b: - - name: n1 - value: v1 - - name: n2 - list: - - v1 - - v2 - - v3_regex -` - tests := []struct { - desc string - path string - value interface{} - want string - wantFound bool - wantErr string - }{ - { - desc: "AddListEntry", - path: `a.b.[name:n2].list`, - value: `foo`, - wantFound: true, - want: ` -a: - b: - - name: n1 - value: v1 - - name: n2 - list: - - v1 - - v2 - - v3_regex - - foo -`, - }, - { - desc: "ModifyListEntryValue", - path: `a.b.[name:n1].value`, - value: `v2`, - wantFound: true, - want: ` -a: - b: - - name: n1 - value: v2 - - list: - - v1 - - v2 - - v3_regex - name: n2 -`, - }, - { - desc: "ModifyListEntryValueQuoted", - path: `a.b.[name:n1].value`, - value: `v2`, - wantFound: true, - want: ` -a: - b: - - name: "n1" - value: v2 - - list: - - v1 - - v2 - - v3_regex - name: n2 -`, - }, - { - desc: "ModifyListEntry", - path: `a.b.[name:n2].list.[:v2]`, - value: `v3`, - wantFound: true, - want: ` -a: - b: - - name: n1 - value: v1 - - list: - - v1 - - v3 - - v3_regex - name: n2 -`, - }, - { - desc: "ModifyListEntryMapValue", - path: `a.b.[name:n2]`, - value: `name: n2 -list: - - nk1: nv1 - - nk2: nv2`, - wantFound: true, - want: ` -a: - b: - - name: n1 - value: v1 - - name: n2 - list: - - nk1: nv1 - - nk2: nv2 -`, - }, - { - desc: "ModifyNthListEntry", - path: `a.b.[1].list.[:v2]`, - value: `v-the-second`, - wantFound: true, - want: ` -a: - b: - - name: n1 - value: v1 - - list: - - v1 - - v-the-second - - v3_regex - name: n2 -`, - }, - { - desc: "ModifyNthLeafListEntry", - path: `a.b.[1].list.[2]`, - value: `v-the-third`, - wantFound: true, - want: ` -a: - b: - - name: n1 - value: v1 - - list: - - v1 - - v2 - - v-the-third - name: n2 -`, - }, - { - desc: "ModifyListEntryValueDotless", - path: `a.b[name:n1].value`, - value: `v2`, - wantFound: true, - want: ` -a: - b: - - name: n1 - value: v2 - - list: - - v1 - - v2 - - v3_regex - name: n2 -`, - }, - { - desc: "DeleteListEntry", - path: `a.b.[name:n1]`, - wantFound: true, - want: ` -a: - b: - - list: - - v1 - - v2 - - v3_regex - name: n2 -`, - }, - { - desc: "DeleteListEntryValue", - path: `a.b.[name:n2].list.[:v2]`, - wantFound: true, - want: ` -a: - b: - - name: n1 - value: v1 - - list: - - v1 - - v3_regex - name: n2 -`, - }, - { - desc: "DeleteListEntryIndex", - path: `a.b.[name:n2].list.[1]`, - wantFound: true, - want: ` -a: - b: - - name: n1 - value: v1 - - list: - - v1 - - v3_regex - name: n2 -`, - }, - { - desc: "DeleteListEntryValueRegex", - path: `a.b.[name:n2].list.[:v3]`, - wantFound: true, - want: ` -a: - b: - - name: n1 - value: v1 - - list: - - v1 - - v2 - name: n2 -`, - }, - { - desc: "DeleteListLeafEntryBogusIndex", - path: `a.b.[name:n2].list.[-200]`, - wantFound: false, - wantErr: `path a.b.[name:n2].list.[-200]: element [-200] not found`, - }, - { - desc: "DeleteListEntryBogusIndex", - path: `a.b.[1000000].list.[:v2]`, - wantFound: false, - wantErr: `index 1000000 exceeds list length 2 at path [1000000].list.[:v2]`, - }, - { - desc: "AddMapEntry", - path: `a.new_key`, - value: `new_val`, - wantFound: true, - want: ` -a: - b: - - name: n1 - value: v1 - - name: n2 - list: - - v1 - - v2 - - v3_regex - new_key: new_val -`, - }, - { - desc: "AddMapEntryMapValue", - path: `a.new_key`, - value: `new_key: - nk1: - nk2: nv2`, - wantFound: true, - want: ` -a: - b: - - name: n1 - value: v1 - - name: n2 - list: - - v1 - - v2 - - v3_regex - new_key: - nk1: - nk2: nv2 -`, - }, - { - desc: "ModifyMapEntryMapValue", - path: `a.b`, - value: `nk1: - nk2: nv2`, - wantFound: true, - want: ` -a: - nk1: - nk2: nv2 -`, - }, - { - desc: "DeleteMapEntry", - path: `a.b`, - wantFound: true, - want: ` -a: {} -`, - }, - { - desc: "path not found", - path: `a.c.[name:n2].list.[:v3]`, - wantFound: false, - wantErr: `path not found at element c in path a.c.[name:n2].list.[:v3]`, - }, - { - desc: "error key", - path: `a.b.[].list`, - wantFound: false, - wantErr: `path a.b.[].list: [] is not a valid key:value path element`, - }, - { - desc: "invalid index", - path: `a.c.[n2].list.[:v3]`, - wantFound: false, - wantErr: `path not found at element c in path a.c.[n2].list.[:v3]`, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - root := make(map[string]interface{}) - if err := yaml.Unmarshal([]byte(rootYAML), &root); err != nil { - t.Fatal(err) - } - pc, gotFound, gotErr := GetPathContext(root, util.PathFromString(tt.path), false) - if gotErr, wantErr := errToString(gotErr), tt.wantErr; gotErr != wantErr { - t.Fatalf("GetPathContext(%s): gotErr:%s, wantErr:%s", tt.desc, gotErr, wantErr) - } - if gotFound != tt.wantFound { - t.Fatalf("GetPathContext(%s): gotFound:%v, wantFound:%v", tt.desc, gotFound, tt.wantFound) - } - if tt.wantErr != "" || !tt.wantFound { - if tt.want != "" { - t.Error("tt.want is set but never checked") - } - return - } - - err := WritePathContext(pc, tt.value, false) - if err != nil { - t.Fatal(err) - } - - gotYAML := util.ToYAML(root) - diff := util.YAMLDiff(gotYAML, tt.want) - if diff != "" { - t.Errorf("%s: (got:-, want:+):\n%s\n", tt.desc, diff) - } - }) - } -} - -func TestWriteNode(t *testing.T) { - testTreeYAML := ` -a: - b: - c: val1 - list1: - - i1: val1 - - i2: val2 - - i3a: key1 - i3b: - list2: - - i1: val1 - - i2: val2 - - i3a: key1 - i3b: - i1: va11 -` - tests := []struct { - desc string - baseYAML string - path string - value string - want string - wantErr string - }{ - { - desc: "insert empty", - path: "a.b.c", - value: "val1", - want: ` -a: - b: - c: val1 -`, - }, - { - desc: "overwrite", - baseYAML: testTreeYAML, - path: "a.b.c", - value: "val2", - want: ` -a: - b: - c: val2 - list1: - - i1: val1 - - i2: val2 - - i3a: key1 - i3b: - list2: - - i1: val1 - - i2: val2 - - i3a: key1 - i3b: - i1: va11 -`, - }, - { - desc: "partial create", - baseYAML: testTreeYAML, - path: "a.b.d", - value: "val3", - want: ` -a: - b: - c: val1 - d: val3 - list1: - - i1: val1 - - i2: val2 - - i3a: key1 - i3b: - list2: - - i1: val1 - - i2: val2 - - i3a: key1 - i3b: - i1: va11 -`, - }, - { - desc: "list keys", - baseYAML: testTreeYAML, - path: "a.b.list1.[i3a:key1].i3b.list2.[i3a:key1].i3b.i1", - value: "val2", - want: ` -a: - b: - c: val1 - list1: - - i1: val1 - - i2: val2 - - i3a: key1 - i3b: - list2: - - i1: val1 - - i2: val2 - - i3a: key1 - i3b: - i1: val2 -`, - }, - // For https://github.com/istio/istio/issues/20950 - { - desc: "with initial list", - baseYAML: ` -components: - ingressGateways: - - enabled: true -`, - path: "components.ingressGateways[0].enabled", - value: "false", - want: ` -components: - ingressGateways: - - enabled: "false" -`, - }, - { - desc: "no initial list", - baseYAML: "", - path: "components.ingressGateways[0].enabled", - value: "false", - want: ` -components: - ingressGateways: - - enabled: "false" -`, - }, - { - desc: "no initial list for entry", - baseYAML: ` -a: {} -`, - path: "a.list.[0]", - value: "v1", - want: ` -a: - list: - - v1 -`, - }, - { - desc: "ExtendNthLeafListEntry", - baseYAML: ` -a: - list: - - v1 -`, - path: `a.list.[1]`, - value: `v2`, - want: ` -a: - list: - - v1 - - v2 -`, - }, - { - desc: "ExtendLeafListEntryLargeIndex", - baseYAML: ` -a: - list: - - v1 -`, - path: `a.list.[999]`, - value: `v2`, - want: ` -a: - list: - - v1 - - v2 -`, - }, - { - desc: "ExtendLeafListEntryNegativeIndex", - baseYAML: ` -a: - list: - - v1 -`, - path: `a.list.[-1]`, - value: `v2`, - want: ` -a: - list: - - v1 - - v2 -`, - }, - { - desc: "ExtendNthListEntry", - baseYAML: ` -a: - list: - - name: foo -`, - path: `a.list.[1].name`, - value: `bar`, - want: ` -a: - list: - - name: foo - - name: bar -`, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - root := make(map[string]interface{}) - if tt.baseYAML != "" { - if err := yaml.Unmarshal([]byte(tt.baseYAML), &root); err != nil { - t.Fatal(err) - } - } - p := util.PathFromString(tt.path) - err := WriteNode(root, p, tt.value) - if gotErr, wantErr := errToString(err), tt.wantErr; gotErr != wantErr { - t.Errorf("%s: gotErr:%s, wantErr:%s", tt.desc, gotErr, wantErr) - return - } - if got, want := util.ToYAML(root), tt.want; err == nil && util.YAMLDiff(got, want) != "" { - t.Errorf("%s: got:\n%s\nwant:\n%s\ndiff:\n%s\n", tt.desc, got, want, util.YAMLDiff(got, want)) - } - }) - } -} - -func TestMergeNode(t *testing.T) { - testTreeYAML := ` -a: - b: - c: val1 - list1: - - i1: val1 - - i2: val2 -` - tests := []struct { - desc string - baseYAML string - path string - value string - want string - wantErr string - }{ - { - desc: "merge list entry", - baseYAML: testTreeYAML, - path: "a.b.list1.[i1:val1]", - value: ` -i2b: val2`, - want: ` -a: - b: - c: val1 - list1: - - i1: val1 - i2b: val2 - - i2: val2 -`, - }, - { - desc: "merge list 2", - baseYAML: testTreeYAML, - path: "a.b.list1", - value: ` -i3: - a: val3 -`, - want: ` -a: - b: - c: val1 - list1: - - i1: val1 - - i2: val2 - - i3: - a: val3 -`, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - root := make(map[string]interface{}) - if tt.baseYAML != "" { - if err := yaml.Unmarshal([]byte(tt.baseYAML), &root); err != nil { - t.Fatal(err) - } - } - p := util.PathFromString(tt.path) - iv := make(map[string]interface{}) - err := yaml.Unmarshal([]byte(tt.value), &iv) - if err != nil { - t.Fatal(err) - } - err = MergeNode(root, p, iv) - if gotErr, wantErr := errToString(err), tt.wantErr; gotErr != wantErr { - t.Errorf("%s: gotErr:%s, wantErr:%s", tt.desc, gotErr, wantErr) - return - } - if got, want := util.ToYAML(root), tt.want; err == nil && util.YAMLDiff(got, want) != "" { - t.Errorf("%s: got:\n%s\nwant:\n%s\ndiff:\n%s\n", tt.desc, got, want, util.YAMLDiff(got, want)) - } - }) - } -} - -// errToString returns the string representation of err and the empty string if -// err is nil. -func errToString(err error) string { - if err == nil { - return "" - } - return err.Error() -} - -// TestSecretVolumes simulates https://github.com/istio/istio/issues/20381 -func TestSecretVolumes(t *testing.T) { - rootYAML := ` -values: - gateways: - istio-egressgateway: - secretVolumes: [] -` - root := make(map[string]interface{}) - if err := yaml.Unmarshal([]byte(rootYAML), &root); err != nil { - t.Fatal(err) - } - overrides := []struct { - path string - value interface{} - }{ - { - path: "values.gateways.istio-egressgateway.secretVolumes[0].name", - value: "egressgateway-certs", - }, - { - path: "values.gateways.istio-egressgateway.secretVolumes[0].secretName", - value: "istio-egressgateway-certs", - }, - { - path: "values.gateways.istio-egressgateway.secretVolumes[0].mountPath", - value: "/etc/istio/egressgateway-certs", - }, - { - path: "values.gateways.istio-egressgateway.secretVolumes[1].name", - value: "egressgateway-ca-certs", - }, - { - path: "values.gateways.istio-egressgateway.secretVolumes[1].secretName", - value: "istio-egressgateway-ca-certs", - }, - { - path: "values.gateways.istio-egressgateway.secretVolumes[1].mountPath", - value: "/etc/istio/egressgateway-ca-certs", - }, - { - path: "values.gateways.istio-egressgateway.secretVolumes[2].name", - value: "nginx-client-certs", - }, - { - path: "values.gateways.istio-egressgateway.secretVolumes[2].secretName", - value: "nginx-client-certs", - }, - { - path: "values.gateways.istio-egressgateway.secretVolumes[2].mountPath", - value: "/etc/istio/nginx-client-certs", - }, - { - path: "values.gateways.istio-egressgateway.secretVolumes[3].name", - value: "nginx-ca-certs", - }, - { - path: "values.gateways.istio-egressgateway.secretVolumes[3].secretName", - value: "nginx-ca-certs", - }, - { - path: "values.gateways.istio-egressgateway.secretVolumes[3].mountPath", - value: "/etc/istio/nginx-ca-certs", - }, - } - - for _, override := range overrides { - - pc, _, err := GetPathContext(root, util.PathFromString(override.path), true) - if err != nil { - t.Fatalf("GetPathContext(%q): %v", override.path, err) - } - err = WritePathContext(pc, override.value, false) - if err != nil { - t.Fatalf("WritePathContext(%q): %v", override.path, err) - } - } - - want := ` -values: - gateways: - istio-egressgateway: - secretVolumes: - - mountPath: /etc/istio/egressgateway-certs - name: egressgateway-certs - secretName: istio-egressgateway-certs - - mountPath: /etc/istio/egressgateway-ca-certs - name: egressgateway-ca-certs - secretName: istio-egressgateway-ca-certs - - mountPath: /etc/istio/nginx-client-certs - name: nginx-client-certs - secretName: nginx-client-certs - - mountPath: /etc/istio/nginx-ca-certs - name: nginx-ca-certs - secretName: nginx-ca-certs -` - gotYAML := util.ToYAML(root) - diff := util.YAMLDiff(gotYAML, want) - if diff != "" { - t.Errorf("TestSecretVolumes: diff:\n%s\n", diff) - } -} - -// Simulates https://github.com/istio/istio/issues/19196 -func TestWriteEscapedPathContext(t *testing.T) { - rootYAML := ` -values: - sidecarInjectorWebhook: - injectedAnnotations: {} -` - tests := []struct { - desc string - path string - value interface{} - want string - wantFound bool - wantErr string - }{ - { - desc: "ModifyEscapedPathValue", - path: `values.sidecarInjectorWebhook.injectedAnnotations.container\.apparmor\.security\.beta\.kubernetes\.io/istio-proxy`, - value: `runtime/default`, - wantFound: true, - want: ` -values: - sidecarInjectorWebhook: - injectedAnnotations: - container.apparmor.security.beta.kubernetes.io/istio-proxy: runtime/default -`, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - root := make(map[string]interface{}) - if err := yaml.Unmarshal([]byte(rootYAML), &root); err != nil { - t.Fatal(err) - } - pc, gotFound, gotErr := GetPathContext(root, util.PathFromString(tt.path), false) - if gotErr, wantErr := errToString(gotErr), tt.wantErr; gotErr != wantErr { - t.Fatalf("GetPathContext(%s): gotErr:%s, wantErr:%s", tt.desc, gotErr, wantErr) - } - if gotFound != tt.wantFound { - t.Fatalf("GetPathContext(%s): gotFound:%v, wantFound:%v", tt.desc, gotFound, tt.wantFound) - } - if tt.wantErr != "" || !tt.wantFound { - return - } - - err := WritePathContext(pc, tt.value, false) - if err != nil { - t.Fatal(err) - } - - gotYAML := util.ToYAML(root) - diff := util.YAMLDiff(gotYAML, tt.want) - if diff != "" { - t.Errorf("%s: diff:\n%s\n", tt.desc, diff) - } - }) - } -} diff --git a/operator/pkg/tpath/util.go b/operator/pkg/tpath/util.go deleted file mode 100644 index 40a009c7c..000000000 --- a/operator/pkg/tpath/util.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/* -util.go contains utility function for dealing with trees. -*/ - -package tpath - -import ( - "gopkg.in/yaml.v2" - yaml2 "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -// AddSpecRoot adds a root node called "spec" to the given tree and returns the resulting tree. -func AddSpecRoot(tree string) (string, error) { - t, nt := make(map[string]interface{}), make(map[string]interface{}) - if err := yaml.Unmarshal([]byte(tree), &t); err != nil { - return "", err - } - nt["spec"] = t - out, err := yaml.Marshal(nt) - if err != nil { - return "", err - } - return string(out), nil -} - -// GetSpecSubtree returns the subtree under "spec". -func GetSpecSubtree(yml string) (string, error) { - return GetConfigSubtree(yml, "spec") -} - -// GetConfigSubtree returns the subtree at the given path. -func GetConfigSubtree(manifest, path string) (string, error) { - root := make(map[string]interface{}) - if err := yaml2.Unmarshal([]byte(manifest), &root); err != nil { - return "", err - } - - nc, _, err := GetPathContext(root, util.PathFromString(path), false) - if err != nil { - return "", err - } - out, err := yaml2.Marshal(nc.Node) - if err != nil { - return "", err - } - return string(out), nil -} diff --git a/operator/pkg/tpath/util_test.go b/operator/pkg/tpath/util_test.go deleted file mode 100644 index 9a00f4bd8..000000000 --- a/operator/pkg/tpath/util_test.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package tpath - -import ( - "errors" - "testing" -) - -func TestAddSpecRoot(t *testing.T) { - tests := []struct { - desc string - in string - expect string - err error - }{ - { - desc: "empty", - in: ``, - expect: `spec: {} -`, - err: nil, - }, - { - desc: "add-root", - in: ` -a: va -b: foo`, - expect: `spec: - a: va - b: foo -`, - err: nil, - }, - { - desc: "err", - in: `i can't be yaml, can I?`, - expect: ``, - err: errors.New(""), - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got, err := AddSpecRoot(tt.in); got != tt.expect || - ((err != nil && tt.err == nil) || (err == nil && tt.err != nil)) { - t.Errorf("%s AddSpecRoot(%s) => %s, want %s", tt.desc, tt.in, got, tt.expect) - } - }) - } -} - -func TestGetConfigSubtree(t *testing.T) { - tests := []struct { - desc string - manifest string - path string - expect string - err bool - }{ - { - desc: "empty", - manifest: ``, - path: ``, - expect: `{} -`, - err: false, - }, - { - desc: "subtree", - manifest: ` -a: - b: - - name: n1 - value: v2 - - list: - - v1 - - v2 - - v3_regex - name: n2 -`, - path: `a`, - expect: `b: -- name: n1 - value: v2 -- list: - - v1 - - v2 - - v3_regex - name: n2 -`, - err: false, - }, - { - desc: "err", - manifest: "not-yaml", - path: "not-subnode", - expect: ``, - err: true, - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got, err := GetConfigSubtree(tt.manifest, tt.path); got != tt.expect || (err == nil) == tt.err { - t.Errorf("%s GetConfigSubtree(%s, %s) => %s, want %s", tt.desc, tt.manifest, tt.path, got, tt.expect) - } - }) - } -} diff --git a/operator/pkg/translate/strategic_port_merge_test.go b/operator/pkg/translate/strategic_port_merge_test.go deleted file mode 100644 index 73241f6b4..000000000 --- a/operator/pkg/translate/strategic_port_merge_test.go +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package translate - -import ( - "testing" -) - -import ( - "github.com/google/go-cmp/cmp" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/util/intstr" -) - -var ( - httpsPort = &v1.ServicePort{ - Name: "https", - Protocol: v1.ProtocolTCP, - Port: 443, - TargetPort: intstr.IntOrString{IntVal: 8443}, - } - quicPort = &v1.ServicePort{ - Name: "http3-quic", - Protocol: v1.ProtocolUDP, - Port: 443, - TargetPort: intstr.IntOrString{IntVal: 8443}, - } - httpPort = &v1.ServicePort{ - Name: "http-port", - Protocol: v1.ProtocolTCP, - Port: 80, - TargetPort: intstr.IntOrString{IntVal: 8080}, - } - httpNoProtoPort = &v1.ServicePort{ - Name: "http-port", - Port: 80, - TargetPort: intstr.IntOrString{IntVal: 8080}, - } - mysqlPort = &v1.ServicePort{ - Name: "mysql-port", - Protocol: v1.ProtocolTCP, - Port: 3306, - } - istioHealthcheckPort = &v1.ServicePort{ - Name: "status-port", - Protocol: v1.ProtocolTCP, - Port: 15021, - } - istioMetricsPort = &v1.ServicePort{ - Name: "metrics-port", - Protocol: v1.ProtocolTCP, - Port: 15020, - } - httpBaseBarPort = &v1.ServicePort{ - Name: "http-bar-base", - Protocol: v1.ProtocolTCP, - Port: 9000, - } - httpOverlayBarPort = &v1.ServicePort{ - Name: "http-bar-overlay", - Protocol: v1.ProtocolTCP, - Port: 9000, - } - httpOverlayDiffProtocolBarPort = &v1.ServicePort{ - Name: "http3-bar-overlay", - Protocol: v1.ProtocolUDP, - Port: 9000, - } - httpFooPort = &v1.ServicePort{ - Name: "http-foo", - Protocol: v1.ProtocolTCP, - Port: 8080, - } - httpFooProtocolOmittedPort = &v1.ServicePort{ - Name: "http-foo", - Port: 8080, - } -) - -func TestStrategicPortMergeByPortAndProtocol(t *testing.T) { - for _, tt := range []struct { - name string - basePorts []*v1.ServicePort - overlayPorts []*v1.ServicePort - expectedMergedPorts []*v1.ServicePort - }{ - { - name: "both base and overlay are nil", - basePorts: nil, - overlayPorts: nil, - expectedMergedPorts: nil, - }, - { - name: "overlay is nil", - basePorts: []*v1.ServicePort{httpPort, httpsPort, quicPort}, - overlayPorts: nil, - expectedMergedPorts: []*v1.ServicePort{httpPort, httpsPort, quicPort}, - }, - { - name: "base is nil", - basePorts: nil, - overlayPorts: []*v1.ServicePort{httpPort, httpsPort, quicPort}, - expectedMergedPorts: []*v1.ServicePort{httpPort, httpsPort, quicPort}, - }, - { - name: "same base and overlay", - basePorts: []*v1.ServicePort{httpPort, httpsPort}, - overlayPorts: []*v1.ServicePort{httpsPort, httpPort}, - expectedMergedPorts: []*v1.ServicePort{httpPort, httpsPort}, - }, - { - name: "base and overlay for the same port, different protocol", - basePorts: []*v1.ServicePort{httpPort, httpsPort, mysqlPort}, - overlayPorts: []*v1.ServicePort{quicPort}, - expectedMergedPorts: []*v1.ServicePort{httpPort, httpsPort, mysqlPort, quicPort}, - }, - { - name: "base and overlay with different ports", - basePorts: []*v1.ServicePort{httpPort}, - overlayPorts: []*v1.ServicePort{httpsPort}, - expectedMergedPorts: []*v1.ServicePort{httpPort, httpsPort}, - }, - { - name: "implicit ports", - basePorts: []*v1.ServicePort{httpPort}, - overlayPorts: []*v1.ServicePort{httpNoProtoPort}, - expectedMergedPorts: []*v1.ServicePort{httpPort}, - }, - { - name: "status and metrics port are present", - basePorts: []*v1.ServicePort{istioHealthcheckPort, istioMetricsPort, httpsPort}, - overlayPorts: []*v1.ServicePort{httpsPort, httpPort}, - expectedMergedPorts: []*v1.ServicePort{istioHealthcheckPort, istioMetricsPort, httpsPort, httpPort}, - }, - { - name: "status port is present", - basePorts: []*v1.ServicePort{istioHealthcheckPort, httpsPort, httpPort}, - overlayPorts: []*v1.ServicePort{httpsPort, httpPort}, - expectedMergedPorts: []*v1.ServicePort{istioHealthcheckPort, httpsPort, httpPort}, - }, - { - name: "metrics port is present", - basePorts: []*v1.ServicePort{istioMetricsPort, httpsPort, httpPort}, - overlayPorts: []*v1.ServicePort{httpsPort, httpPort}, - expectedMergedPorts: []*v1.ServicePort{istioMetricsPort, httpsPort, httpPort}, - }, - { - name: "overlay with port name changed", - basePorts: []*v1.ServicePort{httpBaseBarPort}, - overlayPorts: []*v1.ServicePort{httpOverlayBarPort}, - expectedMergedPorts: []*v1.ServicePort{httpOverlayBarPort}, - }, - { - name: "overlay with different protocol", - basePorts: []*v1.ServicePort{httpBaseBarPort}, - overlayPorts: []*v1.ServicePort{httpOverlayDiffProtocolBarPort}, - expectedMergedPorts: []*v1.ServicePort{httpBaseBarPort, httpOverlayDiffProtocolBarPort}, - }, - { - name: "same base and overlay with protocol omitted for overlay", - basePorts: []*v1.ServicePort{httpFooPort}, - overlayPorts: []*v1.ServicePort{httpFooProtocolOmittedPort}, - expectedMergedPorts: []*v1.ServicePort{httpFooPort}, - }, - { - name: "same base and overlay with protocol omitted for base", - basePorts: []*v1.ServicePort{httpFooProtocolOmittedPort}, - overlayPorts: []*v1.ServicePort{httpFooPort}, - expectedMergedPorts: []*v1.ServicePort{httpFooPort}, - }, - } { - t.Run(tt.name, func(t *testing.T) { - actualMergedPorts := strategicMergePorts(tt.basePorts, tt.overlayPorts) - if diff := cmp.Diff(actualMergedPorts, tt.expectedMergedPorts); diff != "" { - t.Fatalf("expected differs from actual. Diff:\n%s", diff) - } - }) - } -} diff --git a/operator/pkg/translate/translate.go b/operator/pkg/translate/translate.go deleted file mode 100644 index ca609cc5a..000000000 --- a/operator/pkg/translate/translate.go +++ /dev/null @@ -1,1086 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package translate defines translations from installer proto to values.yaml. -package translate - -import ( - "encoding/json" - "fmt" - "reflect" - "sort" - "strings" -) - -import ( - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/structpb" - "istio.io/api/operator/v1alpha1" - "istio.io/pkg/log" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/util/strategicpatch" - "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio" - iopv1alpha1 "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" - "github.com/apache/dubbo-go-pixiu/operator/pkg/tpath" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/operator/pkg/version" - oversion "github.com/apache/dubbo-go-pixiu/operator/version" -) - -const ( - // HelmValuesEnabledSubpath is the subpath from the component root to the enabled parameter. - HelmValuesEnabledSubpath = "enabled" - // HelmValuesNamespaceSubpath is the subpath from the component root to the namespace parameter. - HelmValuesNamespaceSubpath = "namespace" - // HelmValuesHubSubpath is the subpath from the component root to the hub parameter. - HelmValuesHubSubpath = "hub" - // HelmValuesTagSubpath is the subpath from the component root to the tag parameter. - HelmValuesTagSubpath = "tag" - // default ingress gateway name - defaultIngressGWName = "istio-ingressgateway" - // default egress gateway name - defaultEgressGWName = "istio-egressgateway" -) - -var scope = log.RegisterScope("translator", "API translator", 0) - -// Translator is a set of mappings to translate between API paths, charts, values.yaml and k8s paths. -type Translator struct { - // Translations remain the same within a minor version. - Version version.MinorVersion - // APIMapping is a mapping between an API path and the corresponding values.yaml path using longest prefix - // match. If the path is a non-leaf node, the output path is the matching portion of the path, plus any remaining - // output path. - APIMapping map[string]*Translation `yaml:"apiMapping"` - // KubernetesMapping defines mappings from an IstioOperator API paths to k8s resource paths. - KubernetesMapping map[string]*Translation `yaml:"kubernetesMapping"` - // GlobalNamespaces maps feature namespaces to Helm global namespace definitions. - GlobalNamespaces map[name.ComponentName]string `yaml:"globalNamespaces"` - // ComponentMaps is a set of mappings for each Istio component. - ComponentMaps map[name.ComponentName]*ComponentMaps `yaml:"componentMaps"` - // checkedDeprecatedAutoscalingFields represents whether the translator already checked the deprecated fields already. - // Different components do not need to rerun the translation logic - checkedDeprecatedAutoscalingFields bool -} - -// ComponentMaps is a set of mappings for an Istio component. -type ComponentMaps struct { - // ResourceType maps a ComponentName to the type of the rendered k8s resource. - ResourceType string - // ResourceName maps a ComponentName to the name of the rendered k8s resource. - ResourceName string - // ContainerName maps a ComponentName to the name of the container in a Deployment. - ContainerName string - // HelmSubdir is a mapping between a component name and the subdirectory of the component Chart. - HelmSubdir string - // ToHelmValuesTreeRoot is the tree root in values YAML files for the component. - ToHelmValuesTreeRoot string - // SkipReverseTranslate defines whether reverse translate of this component need to be skipped. - SkipReverseTranslate bool -} - -// TranslationFunc maps a yamlStr API path into a YAML values tree. -type TranslationFunc func(t *Translation, root map[string]interface{}, valuesPath string, value interface{}) error - -// Translation is a mapping to an output path using a translation function. -type Translation struct { - // OutPath defines the position in the yaml file - OutPath string `yaml:"outPath"` - translationFunc TranslationFunc -} - -// NewTranslator creates a new translator for minorVersion and returns a ptr to it. -func NewTranslator() *Translator { - t := &Translator{ - Version: oversion.OperatorBinaryVersion.MinorVersion, - APIMapping: map[string]*Translation{ - "hub": {OutPath: "global.hub"}, - "tag": {OutPath: "global.tag"}, - "revision": {OutPath: "revision"}, - "meshConfig": {OutPath: "meshConfig"}, - }, - GlobalNamespaces: map[name.ComponentName]string{ - name.PilotComponentName: "istioNamespace", - }, - ComponentMaps: map[name.ComponentName]*ComponentMaps{ - name.IstioBaseComponentName: { - HelmSubdir: "base", - ToHelmValuesTreeRoot: "global", - SkipReverseTranslate: true, - }, - name.PilotComponentName: { - ResourceType: "Deployment", - ResourceName: "istiod", - ContainerName: "discovery", - HelmSubdir: "istio-control/istio-discovery", - ToHelmValuesTreeRoot: "pilot", - }, - name.IngressComponentName: { - ResourceType: "Deployment", - ResourceName: "istio-ingressgateway", - ContainerName: "istio-proxy", - HelmSubdir: "gateways/istio-ingress", - ToHelmValuesTreeRoot: "gateways.istio-ingressgateway", - }, - name.EgressComponentName: { - ResourceType: "Deployment", - ResourceName: "istio-egressgateway", - ContainerName: "istio-proxy", - HelmSubdir: "gateways/istio-egress", - ToHelmValuesTreeRoot: "gateways.istio-egressgateway", - }, - name.CNIComponentName: { - ResourceType: "DaemonSet", - ResourceName: "istio-cni-node", - ContainerName: "install-cni", - HelmSubdir: "istio-cni", - ToHelmValuesTreeRoot: "cni", - }, - name.IstiodRemoteComponentName: { - HelmSubdir: "istiod-remote", - ToHelmValuesTreeRoot: "global", - SkipReverseTranslate: true, - }, - }, - // nolint: lll - KubernetesMapping: map[string]*Translation{ - "Components.{{.ComponentName}}.K8S.Affinity": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.template.spec.affinity"}, - "Components.{{.ComponentName}}.K8S.Env": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.template.spec.containers.[name:{{.ContainerName}}].env"}, - "Components.{{.ComponentName}}.K8S.HpaSpec": {OutPath: "[HorizontalPodAutoscaler:{{.ResourceName}}].spec"}, - "Components.{{.ComponentName}}.K8S.ImagePullPolicy": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.template.spec.containers.[name:{{.ContainerName}}].imagePullPolicy"}, - "Components.{{.ComponentName}}.K8S.NodeSelector": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.template.spec.nodeSelector"}, - "Components.{{.ComponentName}}.K8S.PodDisruptionBudget": {OutPath: "[PodDisruptionBudget:{{.ResourceName}}].spec"}, - "Components.{{.ComponentName}}.K8S.PodAnnotations": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.template.metadata.annotations"}, - "Components.{{.ComponentName}}.K8S.PriorityClassName": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.template.spec.priorityClassName."}, - "Components.{{.ComponentName}}.K8S.ReadinessProbe": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.template.spec.containers.[name:{{.ContainerName}}].readinessProbe"}, - "Components.{{.ComponentName}}.K8S.ReplicaCount": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.replicas"}, - "Components.{{.ComponentName}}.K8S.Resources": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.template.spec.containers.[name:{{.ContainerName}}].resources"}, - "Components.{{.ComponentName}}.K8S.Strategy": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.strategy"}, - "Components.{{.ComponentName}}.K8S.Tolerations": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.template.spec.tolerations"}, - "Components.{{.ComponentName}}.K8S.ServiceAnnotations": {OutPath: "[Service:{{.ResourceName}}].metadata.annotations"}, - "Components.{{.ComponentName}}.K8S.Service": {OutPath: "[Service:{{.ResourceName}}].spec"}, - "Components.{{.ComponentName}}.K8S.SecurityContext": {OutPath: "[{{.ResourceType}}:{{.ResourceName}}].spec.template.spec.securityContext"}, - }, - } - return t -} - -// OverlayK8sSettings overlays k8s settings from iop over the manifest objects, based on t's translation mappings. -func (t *Translator) OverlayK8sSettings(yml string, iop *v1alpha1.IstioOperatorSpec, componentName name.ComponentName, - resourceName string, index int) (string, error, -) { - // om is a map of kind:name string to Object ptr. - // This is lazy loaded to avoid parsing when there are no overlays - var om map[string]*object.K8sObject - var objects object.K8sObjects - - for inPath, v := range t.KubernetesMapping { - inPath, err := renderFeatureComponentPathTemplate(inPath, componentName) - if err != nil { - return "", err - } - renderedInPath := strings.Replace(inPath, "gressGateways.", "gressGateways."+fmt.Sprint(index)+".", 1) - scope.Debugf("Checking for path %s in IstioOperatorSpec", renderedInPath) - - m, found, err := tpath.GetFromStructPath(iop, renderedInPath) - if err != nil { - return "", err - } - if !found { - scope.Debugf("path %s not found in IstioOperatorSpec, skip mapping.", renderedInPath) - continue - } - if mstr, ok := m.(string); ok && mstr == "" { - scope.Debugf("path %s is empty string, skip mapping.", renderedInPath) - continue - } - // Zero int values are due to proto3 compiling to scalars rather than ptrs. Skip these because values of 0 are - // the default in destination fields and need not be set explicitly. - if mint, ok := util.ToIntValue(m); ok && mint == 0 { - scope.Debugf("path %s is int 0, skip mapping.", renderedInPath) - continue - } - if componentName == name.IstioBaseComponentName { - return "", fmt.Errorf("base component can only have k8s.overlays, not other K8s settings") - } - inPathParts := strings.Split(inPath, ".") - outPath, err := t.renderResourceComponentPathTemplate(v.OutPath, componentName, resourceName, iop.Revision) - if err != nil { - return "", err - } - scope.Debugf("path has value in IstioOperatorSpec, mapping to output path %s", outPath) - path := util.PathFromString(outPath) - pe := path[0] - // Output path must start with [kind:name], which is used to map to the object to overlay. - if !util.IsKVPathElement(pe) { - return "", fmt.Errorf("path %s has an unexpected first element %s in OverlayK8sSettings", path, pe) - } - - // We need to apply overlay, lazy load om - if om == nil { - objects, err = object.ParseK8sObjectsFromYAMLManifest(yml) - if err != nil { - return "", err - } - if scope.DebugEnabled() { - scope.Debugf("Manifest contains the following objects:") - for _, o := range objects { - scope.Debugf("%s", o.HashNameKind()) - } - } - om = objects.ToNameKindMap() - } - - // After brackets are removed, the remaining "kind:name" is the same format as the keys in om. - pe, _ = util.RemoveBrackets(pe) - oo, ok := om[pe] - if !ok { - // skip to overlay the K8s settings if the corresponding resource doesn't exist. - scope.Infof("resource Kind:name %s doesn't exist in the output manifest, skip overlay.", pe) - continue - } - - // When autoscale is enabled we should not overwrite replica count, consider following scenario: - // 0. Set values.pilot.autoscaleEnabled=true, components.pilot.k8s.replicaCount=1 - // 1. In istio operator it "caches" the generated manifests (with istiod.replicas=1) - // 2. HPA autoscales our pilot replicas to 3 - // 3. Set values.pilot.autoscaleEnabled=false - // 4. The generated manifests (with istiod.replicas=1) is same as istio operator "cache", - // the deployment will not get updated unless istio operator is restarted. - if inPathParts[len(inPathParts)-1] == "ReplicaCount" { - if skipReplicaCountWithAutoscaleEnabled(iop, componentName) { - continue - } - } - - // strategic merge overlay m to the base object oo - mergedObj, err := MergeK8sObject(oo, m, path[1:]) - if err != nil { - return "", err - } - - // Apply the workaround for merging service ports with (port,protocol) composite - // keys instead of just the merging by port. - if inPathParts[len(inPathParts)-1] == "Service" { - if msvc, ok := m.(*v1alpha1.ServiceSpec); ok { - mergedObj, err = t.fixMergedObjectWithCustomServicePortOverlay(oo, msvc, mergedObj) - if err != nil { - return "", err - } - } - } - - // Update the original object in objects slice, since the output should be ordered. - *(om[pe]) = *mergedObj - } - - if objects != nil { - return objects.YAMLManifest() - } - return yml, nil -} - -var componentToAutoScaleEnabledPath = map[name.ComponentName]string{ - name.PilotComponentName: "pilot.autoscaleEnabled", - name.IngressComponentName: "gateways.istio-ingressgateway.autoscaleEnabled", - name.EgressComponentName: "gateways.istio-egressgateway.autoscaleEnabled", -} - -// checkDeprecatedHPAFields is a helper function to check for the deprecated fields usage in HorizontalPodAutoscalerSpec -func checkDeprecatedHPAFields(iop *v1alpha1.IstioOperatorSpec) bool { - hpaSpecs := []*v1alpha1.HorizontalPodAutoscalerSpec{} - if iop.GetComponents().GetPilot().GetK8S().GetHpaSpec() != nil { - hpaSpecs = append(hpaSpecs, iop.GetComponents().GetPilot().GetK8S().GetHpaSpec()) - } - for _, gwSpec := range iop.GetComponents().GetIngressGateways() { - if gwSpec.Name == defaultIngressGWName && gwSpec.GetK8S().GetHpaSpec() != nil { - hpaSpecs = append(hpaSpecs, gwSpec.GetK8S().GetHpaSpec()) - } - } - for _, gwSpec := range iop.GetComponents().GetEgressGateways() { - if gwSpec.Name == defaultEgressGWName && gwSpec.GetK8S().GetHpaSpec() != nil { - hpaSpecs = append(hpaSpecs, gwSpec.GetK8S().GetHpaSpec()) - } - } - for _, hpaSpec := range hpaSpecs { - if hpaSpec.GetMetrics() != nil { - for _, me := range hpaSpec.GetMetrics() { - // nolint: staticcheck - if me.GetObject().GetMetricName() != "" || me.GetObject().GetAverageValue() != nil || - // nolint: staticcheck - me.GetObject().GetSelector() != nil || me.GetObject().GetTargetValue() != nil { - return true - } - // nolint: staticcheck - if me.GetPods().GetMetricName() != "" || me.GetPods().GetSelector() != nil || - // nolint: staticcheck - me.GetPods().GetTargetAverageValue() != nil { - return true - } - // nolint: staticcheck - if me.GetResource().GetTargetAverageValue() != nil || me.GetResource().GetTargetAverageUtilization() != 0 { - return true - } - // nolint: staticcheck - if me.GetExternal().GetTargetAverageValue() != nil || me.GetExternal().GetTargetValue() != nil || - // nolint: staticcheck - me.GetExternal().GetMetricName() != "" || me.GetExternal().GetMetricSelector() != nil { - return true - } - } - } - } - return false -} - -// translateDeprecatedAutoscalingFields checks for existence of deprecated HPA fields, if found, set values.global.autoscalingv2API to false -// It only needs to run the logic for the first component because we are setting the values.global field instead of per component ones. -// we do not set per component values because we may want to avoid mixture of v2 and v2beta1 autoscaling templates usage -func (t *Translator) translateDeprecatedAutoscalingFields(values map[string]interface{}, iop *v1alpha1.IstioOperatorSpec) error { - if t.checkedDeprecatedAutoscalingFields || checkDeprecatedHPAFields(iop) { - path := util.PathFromString("global.autoscalingv2API") - if err := tpath.WriteNode(values, path, false); err != nil { - return fmt.Errorf("failed to set autoscalingv2API path: %v", err) - } - t.checkedDeprecatedAutoscalingFields = true - } - return nil -} - -func skipReplicaCountWithAutoscaleEnabled(iop *v1alpha1.IstioOperatorSpec, componentName name.ComponentName) bool { - values := iop.GetValues().AsMap() - path, ok := componentToAutoScaleEnabledPath[componentName] - if !ok { - return false - } - - enabledVal, found, err := tpath.GetFromStructPath(values, path) - if err != nil || !found { - return false - } - - enabled, ok := enabledVal.(bool) - return ok && enabled -} - -func (t *Translator) fixMergedObjectWithCustomServicePortOverlay(oo *object.K8sObject, - msvc *v1alpha1.ServiceSpec, mergedObj *object.K8sObject) (*object.K8sObject, error) { - var basePorts []*v1.ServicePort - bps, _, err := unstructured.NestedSlice(oo.Unstructured(), "spec", "ports") - if err != nil { - return nil, err - } - bby, err := json.Marshal(bps) - if err != nil { - return nil, err - } - if err = json.Unmarshal(bby, &basePorts); err != nil { - return nil, err - } - overlayPorts := make([]*v1.ServicePort, 0, len(msvc.GetPorts())) - for _, p := range msvc.GetPorts() { - var pr v1.Protocol - switch strings.ToLower(p.GetProtocol()) { - case "udp": - pr = v1.ProtocolUDP - default: - pr = v1.ProtocolTCP - } - port := &v1.ServicePort{ - Name: p.GetName(), - Protocol: pr, - Port: p.GetPort(), - NodePort: p.GetNodePort(), - } - if p.TargetPort != nil { - port.TargetPort = p.TargetPort.ToKubernetes() - } - overlayPorts = append(overlayPorts, port) - } - mergedPorts := strategicMergePorts(basePorts, overlayPorts) - mpby, err := json.Marshal(mergedPorts) - if err != nil { - return nil, err - } - var mergedPortSlice []interface{} - if err = json.Unmarshal(mpby, &mergedPortSlice); err != nil { - return nil, err - } - if err = unstructured.SetNestedSlice(mergedObj.Unstructured(), mergedPortSlice, "spec", "ports"); err != nil { - return nil, err - } - // Now fix the merged object - mjsonby, err := json.Marshal(mergedObj.Unstructured()) - if err != nil { - return nil, err - } - if mergedObj, err = object.ParseJSONToK8sObject(mjsonby); err != nil { - return nil, err - } - return mergedObj, nil -} - -type portWithProtocol struct { - port int32 - protocol v1.Protocol -} - -func portIndexOf(element portWithProtocol, data []portWithProtocol) int { - for k, v := range data { - if element == v { - return k - } - } - return len(data) -} - -// strategicMergePorts merges the base with the given overlay considering both -// port and the protocol as the merge keys. This is a workaround for the strategic -// merge patch in Kubernetes which only uses port number as the key. This causes -// an issue when we have to expose the same port with different protocols. -// See - https://github.com/kubernetes/kubernetes/issues/103544 -// TODO(su225): Remove this once the above issue is addressed in Kubernetes -func strategicMergePorts(base, overlay []*v1.ServicePort) []*v1.ServicePort { - // We want to keep the original port order with base first and then the newly - // added ports through the overlay. This is because there are some cases where - // port order actually matters. For instance, some cloud load balancers use the - // first port for health-checking (in Istio it is 15021). So we must keep maintain - // it in order not to break the users - // See - https://github.com/istio/istio/issues/12503 for more information - // - // Or changing port order might generate weird diffs while upgrading or changing - // IstioOperator spec. It is annoying. So better maintain original order while - // appending newly added ports through overlay. - portPriority := make([]portWithProtocol, 0, len(base)+len(overlay)) - for _, p := range base { - if p.Protocol == "" { - p.Protocol = v1.ProtocolTCP - } - portPriority = append(portPriority, portWithProtocol{port: p.Port, protocol: p.Protocol}) - } - for _, p := range overlay { - if p.Protocol == "" { - p.Protocol = v1.ProtocolTCP - } - portPriority = append(portPriority, portWithProtocol{port: p.Port, protocol: p.Protocol}) - } - sortFn := func(ps []*v1.ServicePort) func(int, int) bool { - return func(i, j int) bool { - pi := portIndexOf(portWithProtocol{port: ps[i].Port, protocol: ps[i].Protocol}, portPriority) - pj := portIndexOf(portWithProtocol{port: ps[j].Port, protocol: ps[j].Protocol}, portPriority) - return pi < pj - } - } - if overlay == nil { - sort.Slice(base, sortFn(base)) - return base - } - if base == nil { - sort.Slice(overlay, sortFn(overlay)) - return overlay - } - // first add the base and then replace appropriate - // keys with the items in the overlay list - merged := make(map[portWithProtocol]*v1.ServicePort) - for _, p := range base { - key := portWithProtocol{port: p.Port, protocol: p.Protocol} - merged[key] = p - } - for _, p := range overlay { - key := portWithProtocol{port: p.Port, protocol: p.Protocol} - merged[key] = p - } - res := make([]*v1.ServicePort, 0, len(merged)) - for _, pv := range merged { - res = append(res, pv) - } - sort.Slice(res, sortFn(res)) - return res -} - -// ProtoToValues traverses the supplied IstioOperatorSpec and returns a values.yaml translation from it. -func (t *Translator) ProtoToValues(ii *v1alpha1.IstioOperatorSpec) (string, error) { - root, err := t.ProtoToHelmValues2(ii) - if err != nil { - return "", err - } - - // Special additional handling not covered by simple translation rules. - if err := t.setComponentProperties(root, ii); err != nil { - return "", err - } - - // Special handling of the settings of legacy fields in autoscaling/v2beta1 - if err := t.translateDeprecatedAutoscalingFields(root, ii); err != nil { - return "", err - } - - // Return blank string for empty case. - if len(root) == 0 { - return "", nil - } - - y, err := yaml.Marshal(root) - if err != nil { - return "", err - } - - return string(y), nil -} - -// TranslateHelmValues creates a Helm values.yaml config data tree from iop using the given translator. -func (t *Translator) TranslateHelmValues(iop *v1alpha1.IstioOperatorSpec, componentsSpec interface{}, componentName name.ComponentName) (string, error) { - apiVals := make(map[string]interface{}) - - // First, translate the IstioOperator API to helm Values. - apiValsStr, err := t.ProtoToValues(iop) - if err != nil { - return "", err - } - err = yaml.Unmarshal([]byte(apiValsStr), &apiVals) - if err != nil { - return "", err - } - - scope.Debugf("Values translated from IstioOperator API:\n%s", apiValsStr) - - // Add global overlay from IstioOperatorSpec.Values/UnvalidatedValues. - globalVals := iop.GetValues().AsMap() - globalUnvalidatedVals := iop.GetUnvalidatedValues().AsMap() - - if scope.DebugEnabled() { - scope.Debugf("Values from IstioOperatorSpec.Values:\n%s", util.ToYAML(globalVals)) - scope.Debugf("Values from IstioOperatorSpec.UnvalidatedValues:\n%s", util.ToYAML(globalUnvalidatedVals)) - } - - mergedVals, err := util.OverlayTrees(apiVals, globalVals) - if err != nil { - return "", err - } - mergedVals, err = util.OverlayTrees(mergedVals, globalUnvalidatedVals) - if err != nil { - return "", err - } - - mergedYAML, err := yaml.Marshal(mergedVals) - if err != nil { - return "", err - } - - mergedYAML, err = applyGatewayTranslations(mergedYAML, componentName, componentsSpec) - if err != nil { - return "", err - } - - return string(mergedYAML), err -} - -// applyGatewayTranslations writes gateway name gwName at the appropriate values path in iop and maps k8s.service.ports -// to values. It returns the resulting YAML tree. -func applyGatewayTranslations(iop []byte, componentName name.ComponentName, componentSpec interface{}) ([]byte, error) { - if !componentName.IsGateway() { - return iop, nil - } - iopt := make(map[string]interface{}) - if err := yaml.Unmarshal(iop, &iopt); err != nil { - return nil, err - } - gwSpec := componentSpec.(*v1alpha1.GatewaySpec) - k8s := gwSpec.K8S - switch componentName { - case name.IngressComponentName: - setYAMLNodeByMapPath(iopt, util.PathFromString("gateways.istio-ingressgateway.name"), gwSpec.Name) - if len(gwSpec.Label) != 0 { - setYAMLNodeByMapPath(iopt, util.PathFromString("gateways.istio-ingressgateway.labels"), gwSpec.Label) - } - if k8s != nil && k8s.Service != nil && k8s.Service.Ports != nil { - setYAMLNodeByMapPath(iopt, util.PathFromString("gateways.istio-ingressgateway.ports"), k8s.Service.Ports) - } - case name.EgressComponentName: - setYAMLNodeByMapPath(iopt, util.PathFromString("gateways.istio-egressgateway.name"), gwSpec.Name) - if len(gwSpec.Label) != 0 { - setYAMLNodeByMapPath(iopt, util.PathFromString("gateways.istio-egressgateway.labels"), gwSpec.Label) - } - if k8s != nil && k8s.Service != nil && k8s.Service.Ports != nil { - setYAMLNodeByMapPath(iopt, util.PathFromString("gateways.istio-egressgateway.ports"), k8s.Service.Ports) - } - } - return yaml.Marshal(iopt) -} - -// setYAMLNodeByMapPath sets the value at the given path to val in treeNode. The path cannot traverse lists and -// treeNode must be a YAML tree unmarshaled into a plain map data structure. -func setYAMLNodeByMapPath(treeNode interface{}, path util.Path, val interface{}) { - if len(path) == 0 || treeNode == nil { - return - } - pe := path[0] - switch nt := treeNode.(type) { - case map[interface{}]interface{}: - if len(path) == 1 { - nt[pe] = val - return - } - if nt[pe] == nil { - return - } - setYAMLNodeByMapPath(nt[pe], path[1:], val) - case map[string]interface{}: - if len(path) == 1 { - nt[pe] = val - return - } - if nt[pe] == nil { - return - } - setYAMLNodeByMapPath(nt[pe], path[1:], val) - } -} - -// ComponentMap returns a ComponentMaps struct ptr for the given component name if one exists. -// If the name of the component is lower case, the function will use the capitalized version -// of the name. -func (t *Translator) ComponentMap(cns string) *ComponentMaps { - cn := name.TitleCase(name.ComponentName(cns)) - return t.ComponentMaps[cn] -} - -func (t *Translator) ProtoToHelmValues2(ii *v1alpha1.IstioOperatorSpec) (map[string]interface{}, error) { - by, err := json.Marshal(ii) - if err != nil { - return nil, err - } - res := map[string]interface{}{} - err = json.Unmarshal(by, &res) - if err != nil { - return nil, err - } - r2 := map[string]interface{}{} - errs := t.ProtoToHelmValues(res, r2, nil) - return r2, errs.ToError() -} - -// ProtoToHelmValues function below is used by third party for integrations and has to be public - -// ProtoToHelmValues takes an interface which must be a struct ptr and recursively iterates through all its fields. -// For each leaf, if looks for a mapping from the struct data path to the corresponding YAML path and if one is -// found, it calls the associated mapping function if one is defined to populate the values YAML path. -// If no mapping function is defined, it uses the default mapping function. -func (t *Translator) ProtoToHelmValues(node interface{}, root map[string]interface{}, path util.Path) (errs util.Errors) { - scope.Debugf("ProtoToHelmValues with path %s, %v (%T)", path, node, node) - if util.IsValueNil(node) { - return nil - } - - vv := reflect.ValueOf(node) - vt := reflect.TypeOf(node) - switch vt.Kind() { - case reflect.Ptr: - if !util.IsNilOrInvalidValue(vv.Elem()) { - errs = util.AppendErrs(errs, t.ProtoToHelmValues(vv.Elem().Interface(), root, path)) - } - case reflect.Struct: - scope.Debug("Struct") - for i := 0; i < vv.NumField(); i++ { - fieldName := vv.Type().Field(i).Name - fieldValue := vv.Field(i) - scope.Debugf("Checking field %s", fieldName) - if a, ok := vv.Type().Field(i).Tag.Lookup("json"); ok && a == "-" { - continue - } - if !fieldValue.CanInterface() { - continue - } - errs = util.AppendErrs(errs, t.ProtoToHelmValues(fieldValue.Interface(), root, append(path, fieldName))) - } - case reflect.Map: - scope.Debug("Map") - for _, key := range vv.MapKeys() { - nnp := append(path, key.String()) - errs = util.AppendErrs(errs, t.insertLeaf(root, nnp, vv.MapIndex(key))) - } - case reflect.Slice: - scope.Debug("Slice") - for i := 0; i < vv.Len(); i++ { - errs = util.AppendErrs(errs, t.ProtoToHelmValues(vv.Index(i).Interface(), root, path)) - } - default: - // Must be a leaf - scope.Debugf("field has kind %s", vt.Kind()) - if vv.CanInterface() { - errs = util.AppendErrs(errs, t.insertLeaf(root, path, vv)) - } - } - - return errs -} - -// setComponentProperties translates properties (e.g., enablement and namespace) of each component -// in the baseYAML values tree, based on feature/component inheritance relationship. -func (t *Translator) setComponentProperties(root map[string]interface{}, iop *v1alpha1.IstioOperatorSpec) error { - var keys []string - for k := range t.ComponentMaps { - if k != name.IngressComponentName && k != name.EgressComponentName { - keys = append(keys, string(k)) - } - } - sort.Strings(keys) - l := len(keys) - for i := l - 1; i >= 0; i-- { - cn := name.ComponentName(keys[i]) - c := t.ComponentMaps[cn] - e, err := t.IsComponentEnabled(cn, iop) - if err != nil { - return err - } - - enablementPath := c.ToHelmValuesTreeRoot - // CNI calls itself "cni" in the chart but "istio_cni" for enablement outside of the chart. - if cn == name.CNIComponentName { - enablementPath = "istio_cni" - } - if err := tpath.WriteNode(root, util.PathFromString(enablementPath+"."+HelmValuesEnabledSubpath), e); err != nil { - return err - } - - ns, err := name.Namespace(cn, iop) - if err != nil { - return err - } - if err := tpath.WriteNode(root, util.PathFromString(c.ToHelmValuesTreeRoot+"."+HelmValuesNamespaceSubpath), ns); err != nil { - return err - } - - hub, found, _ := tpath.GetFromStructPath(iop, "Components."+string(cn)+".Hub") - // Unmarshal unfortunately creates struct fields with "" for unset values. Skip these cases to avoid - // overwriting current value with an empty string. - hubStr, ok := hub.(string) - if found && !(ok && hubStr == "") { - if err := tpath.WriteNode(root, util.PathFromString(c.ToHelmValuesTreeRoot+"."+HelmValuesHubSubpath), hub); err != nil { - return err - } - } - - tag, found, _ := tpath.GetFromStructPath(iop, "Components."+string(cn)+".Tag") - tagv, ok := tag.(*structpb.Value) - if found && !(ok && util.ValueString(tagv) == "") { - if err := tpath.WriteNode(root, util.PathFromString(c.ToHelmValuesTreeRoot+"."+HelmValuesTagSubpath), util.ValueString(tagv)); err != nil { - return err - } - } - } - - for cn, gns := range t.GlobalNamespaces { - ns, err := name.Namespace(cn, iop) - if err != nil { - return err - } - if err := tpath.WriteNode(root, util.PathFromString("global."+gns), ns); err != nil { - return err - } - } - - return nil -} - -// IsComponentEnabled reports whether the component with name cn is enabled, according to the translations in t, -// and the contents of ocp. -func (t *Translator) IsComponentEnabled(cn name.ComponentName, iop *v1alpha1.IstioOperatorSpec) (bool, error) { - if t.ComponentMaps[cn] == nil { - return false, nil - } - return IsComponentEnabledInSpec(cn, iop) -} - -// insertLeaf inserts a leaf with value into root at path, which is first mapped using t.APIMapping. -func (t *Translator) insertLeaf(root map[string]interface{}, path util.Path, value reflect.Value) (errs util.Errors) { - // Must be a scalar leaf. See if we have a mapping. - valuesPath, m := getValuesPathMapping(t.APIMapping, path) - var v interface{} - if value.Kind() == reflect.Ptr { - v = value.Elem().Interface() - } else { - v = value.Interface() - } - switch { - case m == nil: - break - case m.translationFunc == nil: - // Use default translation which just maps to a different part of the tree. - errs = util.AppendErr(errs, defaultTranslationFunc(m, root, valuesPath, v)) - default: - // Use a custom translation function. - errs = util.AppendErr(errs, m.translationFunc(m, root, valuesPath, v)) - } - return errs -} - -// getValuesPathMapping tries to map path against the passed in mappings with a longest prefix match. If a matching prefix -// is found, it returns the translated YAML path and the corresponding translation. -// e.g. for mapping "a.b" -> "1.2", the input path "a.b.c.d" would yield "1.2.c.d". -func getValuesPathMapping(mappings map[string]*Translation, path util.Path) (string, *Translation) { - p := path - var m *Translation - for ; len(p) > 0; p = p[0 : len(p)-1] { - m = mappings[p.String()] - if m != nil { - break - } - } - if m == nil { - return "", nil - } - - if m.OutPath == "" { - return "", m - } - - out := m.OutPath + "." + path[len(p):].String() - scope.Debugf("translating %s to %s", path, out) - return out, m -} - -// renderFeatureComponentPathTemplate renders a template of the form {{.ComponentName}} with -// the supplied parameters. -func renderFeatureComponentPathTemplate(tmpl string, componentName name.ComponentName) (string, error) { - type Temp struct { - ComponentName name.ComponentName - } - ts := Temp{ - ComponentName: componentName, - } - return util.RenderTemplate(tmpl, ts) -} - -// renderResourceComponentPathTemplate renders a template of the form {{.ResourceName}}{{.ContainerName}} with -// the supplied parameters. -func (t *Translator) renderResourceComponentPathTemplate(tmpl string, componentName name.ComponentName, - resourceName, revision string) (string, error) { - cn := string(componentName) - cmp := t.ComponentMap(cn) - if cmp == nil { - return "", fmt.Errorf("component: %s does not exist in the componentMap", cn) - } - if resourceName == "" { - resourceName = cmp.ResourceName - } - // The istiod resource will be istiod-, so we need to append the revision suffix - if revision != "" && resourceName == "istiod" { - resourceName += "-" + revision - } - ts := struct { - ResourceType string - ResourceName string - ContainerName string - }{ - ResourceType: cmp.ResourceType, - ResourceName: resourceName, - ContainerName: cmp.ContainerName, - } - return util.RenderTemplate(tmpl, ts) -} - -// defaultTranslationFunc is the default translation to values. It maps a Go data path into a YAML path. -func defaultTranslationFunc(m *Translation, root map[string]interface{}, valuesPath string, value interface{}) error { - var path []string - - if util.IsEmptyString(value) { - scope.Debugf("Skip empty string value for path %s", m.OutPath) - return nil - } - if valuesPath == "" { - scope.Debugf("Not mapping to values, resources path is %s", m.OutPath) - return nil - } - - for _, p := range util.PathFromString(valuesPath) { - path = append(path, firstCharToLower(p)) - } - - return tpath.WriteNode(root, path, value) -} - -func firstCharToLower(s string) string { - return strings.ToLower(s[0:1]) + s[1:] -} - -// MergeK8sObject function below is used by third party for integrations and has to be public - -// MergeK8sObject does strategic merge for overlayNode on the base object. -func MergeK8sObject(base *object.K8sObject, overlayNode interface{}, path util.Path) (*object.K8sObject, error) { - overlay, err := createPatchObjectFromPath(overlayNode, path) - if err != nil { - return nil, err - } - overlayYAML, err := yaml.Marshal(overlay) - if err != nil { - return nil, err - } - overlayJSON, err := yaml.YAMLToJSON(overlayYAML) - if err != nil { - return nil, fmt.Errorf("yamlToJSON error in overlayYAML: %s\n%s", err, overlayYAML) - } - baseJSON, err := base.JSON() - if err != nil { - return nil, err - } - - // get a versioned object from the scheme, we can use the strategic patching mechanism - // (i.e. take advantage of patchStrategy in the type) - versionedObject, err := scheme.Scheme.New(base.GroupVersionKind()) - if err != nil { - return nil, err - } - // strategic merge patch - newBytes, err := strategicpatch.StrategicMergePatch(baseJSON, overlayJSON, versionedObject) - if err != nil { - return nil, fmt.Errorf("get error: %s to merge patch:\n%s for base:\n%s", err, overlayJSON, baseJSON) - } - - newObj, err := object.ParseJSONToK8sObject(newBytes) - if err != nil { - return nil, err - } - - return newObj.ResolveK8sConflict(), nil -} - -// createPatchObjectFromPath constructs patch object for node with path, returns nil object and error if the path is invalid. -// eg. node: -// - name: NEW_VAR -// value: new_value -// -// and path: -// -// spec.template.spec.containers.[name:discovery].env -// will constructs the following patch object: -// spec: -// template: -// spec: -// containers: -// - name: discovery -// env: -// - name: NEW_VAR -// value: new_value -func createPatchObjectFromPath(node interface{}, path util.Path) (map[string]interface{}, error) { - if len(path) == 0 { - return nil, fmt.Errorf("empty path %s", path) - } - if util.IsKVPathElement(path[0]) { - return nil, fmt.Errorf("path %s has an unexpected first element %s", path, path[0]) - } - length := len(path) - if util.IsKVPathElement(path[length-1]) { - return nil, fmt.Errorf("path %s has an unexpected last element %s", path, path[length-1]) - } - - patchObj := make(map[string]interface{}) - var currentNode, nextNode interface{} - nextNode = patchObj - for i, pe := range path { - currentNode = nextNode - // last path element - if i == length-1 { - currentNode, ok := currentNode.(map[string]interface{}) - if !ok { - return nil, fmt.Errorf("path %s has an unexpected non KV element %s", path, pe) - } - currentNode[pe] = node - break - } - - if util.IsKVPathElement(pe) { - currentNode, ok := currentNode.([]interface{}) - if !ok { - return nil, fmt.Errorf("path %s has an unexpected KV element %s", path, pe) - } - k, v, err := util.PathKV(pe) - if err != nil { - return nil, err - } - if k == "" || v == "" { - return nil, fmt.Errorf("path %s has an invalid KV element %s", path, pe) - } - currentNode[0] = map[string]interface{}{k: v} - nextNode = currentNode[0] - continue - } - - currentNode, ok := currentNode.(map[string]interface{}) - if !ok { - return nil, fmt.Errorf("path %s has an unexpected non KV element %s", path, pe) - } - // next path element determines the next node type - if util.IsKVPathElement(path[i+1]) { - currentNode[pe] = make([]interface{}, 1) - } else { - currentNode[pe] = make(map[string]interface{}) - } - nextNode = currentNode[pe] - } - return patchObj, nil -} - -// IOPStoIOP takes an IstioOperatorSpec and returns a corresponding IstioOperator with the given name and namespace. -func IOPStoIOP(iops proto.Message, name, namespace string) (*iopv1alpha1.IstioOperator, error) { - iopStr, err := IOPStoIOPstr(iops, name, namespace) - if err != nil { - return nil, err - } - iop, err := istio.UnmarshalIstioOperator(iopStr, false) - if err != nil { - return nil, err - } - return iop, nil -} - -// IOPStoIOPstr takes an IstioOperatorSpec and returns a corresponding IstioOperator string with the given name and namespace. -func IOPStoIOPstr(iops proto.Message, name, namespace string) (string, error) { - iopsStr, err := util.MarshalWithJSONPB(iops) - if err != nil { - return "", err - } - spec, err := tpath.AddSpecRoot(iopsStr) - if err != nil { - return "", err - } - - tmpl := ` -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -metadata: - namespace: {{ .Namespace }} - name: {{ .Name }} -` - // Passing into template causes reformatting, use simple concatenation instead. - tmpl += spec - - type Temp struct { - Namespace string - Name string - } - ts := Temp{ - Namespace: namespace, - Name: name, - } - return util.RenderTemplate(tmpl, ts) -} diff --git a/operator/pkg/translate/translate_common.go b/operator/pkg/translate/translate_common.go deleted file mode 100644 index b5493ac8f..000000000 --- a/operator/pkg/translate/translate_common.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package translate - -import ( - "fmt" -) - -import ( - "github.com/golang/protobuf/ptypes/wrappers" - "istio.io/api/operator/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/tpath" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -// IsComponentEnabledInSpec reports whether the given component is enabled in the given spec. -// IsComponentEnabledInSpec assumes that controlPlaneSpec has been validated. -// TODO: remove extra validations when comfort level is high enough. -func IsComponentEnabledInSpec(componentName name.ComponentName, controlPlaneSpec *v1alpha1.IstioOperatorSpec) (bool, error) { - componentNodeI, found, err := tpath.GetFromStructPath(controlPlaneSpec, "Components."+string(componentName)+".Enabled") - if err != nil { - return false, fmt.Errorf("error in IsComponentEnabledInSpec GetFromStructPath componentEnabled for component=%s: %s", - componentName, err) - } - if !found || componentNodeI == nil { - return false, nil - } - componentNode, ok := componentNodeI.(*wrappers.BoolValue) - if !ok { - return false, fmt.Errorf("component %s enabled has bad type %T, expect *v1alpha1.BoolValueForPB", componentName, componentNodeI) - } - if componentNode == nil { - return false, nil - } - return componentNode.Value, nil -} - -// IsComponentEnabledFromValue get whether component is enabled in helm value.yaml tree. -// valuePath points to component path in the values tree. -func IsComponentEnabledFromValue(cn name.ComponentName, valueSpec map[string]interface{}) (enabled bool, pathExist bool, err error) { - t := NewTranslator() - cnMap, ok := t.ComponentMaps[cn] - if !ok { - return false, false, nil - } - valuePath := cnMap.ToHelmValuesTreeRoot - enabledPath := valuePath + ".enabled" - enableNodeI, found, err := tpath.Find(valueSpec, util.ToYAMLPath(enabledPath)) - if err != nil { - return false, false, fmt.Errorf("error finding component enablement path: %s in helm value.yaml tree", enabledPath) - } - if !found { - // Some components do not specify enablement should be treated as enabled if the root node in the component subtree exists. - _, found, err := tpath.Find(valueSpec, util.ToYAMLPath(valuePath)) - if err != nil { - return false, false, err - } - if found { - return true, false, nil - } - return false, false, nil - } - enableNode, ok := enableNodeI.(bool) - if !ok { - return false, true, fmt.Errorf("node at valuePath %s has bad type %T, expect bool", enabledPath, enableNodeI) - } - return enableNode, true, nil -} - -// OverlayValuesEnablement overlays any enablement in values path from the user file overlay or set flag overlay. -// The overlay is translated from values to the corresponding addonComponents enablement paths. -func OverlayValuesEnablement(baseYAML, fileOverlayYAML, setOverlayYAML string) (string, error) { - overlayYAML, err := util.OverlayYAML(fileOverlayYAML, setOverlayYAML) - if err != nil { - return "", fmt.Errorf("could not overlay user config over base: %s", err) - } - - return YAMLTree(overlayYAML, baseYAML, name.ValuesEnablementPathMap) -} - -// GetEnabledComponents get all the enabled components from the given istio operator spec -func GetEnabledComponents(iopSpec *v1alpha1.IstioOperatorSpec) ([]string, error) { - var enabledComponents []string - if iopSpec.Components != nil { - for _, c := range name.AllCoreComponentNames { - enabled, err := IsComponentEnabledInSpec(c, iopSpec) - if err != nil { - return nil, fmt.Errorf("failed to check if component: %s is enabled or not: %v", string(c), err) - } - if enabled { - enabledComponents = append(enabledComponents, string(c)) - } - } - for _, c := range iopSpec.Components.IngressGateways { - if c.Enabled.GetValue() { - enabledComponents = append(enabledComponents, string(name.IngressComponentName)) - break - } - } - for _, c := range iopSpec.Components.EgressGateways { - if c.Enabled.GetValue() { - enabledComponents = append(enabledComponents, string(name.EgressComponentName)) - break - } - } - } - - return enabledComponents, nil -} diff --git a/operator/pkg/translate/translate_common_test.go b/operator/pkg/translate/translate_common_test.go deleted file mode 100644 index e873bd075..000000000 --- a/operator/pkg/translate/translate_common_test.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package translate - -import ( - "testing" -) - -import ( - "istio.io/api/operator/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -func TestGetEnabledComponents(t *testing.T) { - tests := []struct { - desc string - yamlStr string - want []string - wantErrs bool - }{ - { - desc: "nil success", - yamlStr: "", - want: nil, - }, - { - desc: "all components disabled", - yamlStr: ` -components: - pilot: - enabled: false - ingressGateways: - - enabled: false`, - want: nil, - }, - { - desc: "only pilot component enabled", - yamlStr: ` -components: - pilot: - enabled: true - ingressGateways: - - enabled: false`, - want: []string{string(name.PilotComponentName)}, - }, - { - desc: "only gateway component enabled", - yamlStr: ` -components: - pilot: - enabled: false - ingressGateways: - - enabled: true`, - want: []string{string(name.IngressComponentName)}, - }, - { - desc: "all components enabled", - yamlStr: ` -components: - pilot: - enabled: true - ingressGateways: - - enabled: true`, - want: []string{string(name.PilotComponentName), string(name.IngressComponentName)}, - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - iopSpec := &v1alpha1.IstioOperatorSpec{} - err := util.UnmarshalWithJSONPB(tt.yamlStr, iopSpec, false) - if err != nil { - t.Fatalf("unmarshalWithJSONPB(%s): got error %s", tt.desc, err) - } - - enabledComponents, err := GetEnabledComponents(iopSpec) - if err != nil { - t.Errorf("GetEnabledComponents(%s)(%v): got error: %v", tt.desc, tt.yamlStr, err) - } - if !testEquality(enabledComponents, tt.want) { - t.Errorf("GetEnabledComponents(%s)(%v): got: %v, want: %v", tt.desc, tt.yamlStr, enabledComponents, tt.want) - } - }) - } -} - -func testEquality(a, b []string) bool { - if (a == nil) != (b == nil) { - return false - } - - if len(a) != len(b) { - return false - } - - for i := range a { - if a[i] != b[i] { - return false - } - } - - return true -} diff --git a/operator/pkg/translate/translate_test.go b/operator/pkg/translate/translate_test.go deleted file mode 100644 index 74425eb81..000000000 --- a/operator/pkg/translate/translate_test.go +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package translate - -import ( - "fmt" - "testing" -) - -import ( - "istio.io/api/operator/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/tpath" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -func Test_skipReplicaCountWithAutoscaleEnabled(t *testing.T) { - const valuesWithHPAndReplicaCountFormat = ` -values: - pilot: - autoscaleEnabled: %t - gateways: - istio-ingressgateway: - autoscaleEnabled: %t - istio-egressgateway: - autoscaleEnabled: %t -components: - pilot: - k8s: - replicaCount: 2 - ingressGateways: - - name: istio-ingressgateway - enabled: true - k8s: - replicaCount: 2 - egressGateways: - - name: istio-egressgateway - enabled: true - k8s: - replicaCount: 2 -` - - cases := []struct { - name string - component name.ComponentName - values string - expectSkip bool - }{ - { - name: "hpa enabled for pilot without replicas", - component: name.PilotComponentName, - values: fmt.Sprintf(valuesWithHPAndReplicaCountFormat, false, false, false), - expectSkip: false, - }, - { - name: "hpa enabled for ingressgateway without replica", - component: name.IngressComponentName, - values: fmt.Sprintf(valuesWithHPAndReplicaCountFormat, false, false, false), - expectSkip: false, - }, - { - name: "hpa enabled for pilot without replicas", - component: name.EgressComponentName, - values: fmt.Sprintf(valuesWithHPAndReplicaCountFormat, false, false, false), - expectSkip: false, - }, - { - name: "hpa enabled for pilot with replicas", - component: name.PilotComponentName, - values: fmt.Sprintf(valuesWithHPAndReplicaCountFormat, true, false, false), - expectSkip: true, - }, - { - name: "hpa enabled for ingressgateway with replicass", - component: name.IngressComponentName, - values: fmt.Sprintf(valuesWithHPAndReplicaCountFormat, false, true, false), - expectSkip: true, - }, - { - name: "hpa enabled for egressgateway with replicas", - component: name.EgressComponentName, - values: fmt.Sprintf(valuesWithHPAndReplicaCountFormat, true, false, true), - expectSkip: true, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - var iop *v1alpha1.IstioOperatorSpec - if tt.values != "" { - iop = &v1alpha1.IstioOperatorSpec{} - if err := util.UnmarshalWithJSONPB(tt.values, iop, true); err != nil { - t.Fatal(err) - } - } - - got := skipReplicaCountWithAutoscaleEnabled(iop, tt.component) - assert.Equal(t, tt.expectSkip, got) - }) - } -} - -func Test_translateDeprecatedAutoscalingAPI(t *testing.T) { - const iopString1 = ` -components: - ingressGateways: - - name: istio-ingressgateway - enabled: true - k8s: - hpaSpec: - metrics: - - object: - metricName: test1 -` - const iopString2 = ` -components: - pilot: - k8s: - hpaSpec: - metrics: - - resource: - targetAverageUtilization: 80 -` - const iopString3 = ` -components: - egressGateways: - - name: istio-egressgateway - enabled: true - k8s: - hpaSpec: - metrics: - - pods: - targetAverageValue: 100m -` - const iopString4 = ` -components: - pilot: - enabled: true - k8s: - hpaSpec: - scaleTargetRef: - apiVersion: extensions/v1beta1 - kind: Deployment - name: istiod - minReplicas: 1 - maxReplicas: 5 - metrics: - - resource: - name: cpu - target: - averageUtilization: 80 - type: Utilization - type: Resource -` - cases := []struct { - name string - iopString string - expectFound bool - }{ - { - name: "found deprecated fields ingress", - iopString: iopString1, - expectFound: true, - }, - { - name: "found deprecated fields pilot", - iopString: iopString2, - expectFound: true, - }, - { - name: "found deprecated fields egress", - iopString: iopString3, - expectFound: true, - }, - { - name: "no deprecated fields", - iopString: iopString4, - expectFound: false, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - var iop *v1alpha1.IstioOperatorSpec - if tt.iopString != "" { - iop = &v1alpha1.IstioOperatorSpec{} - if err := util.UnmarshalWithJSONPB(tt.iopString, iop, true); err != nil { - t.Fatal(err) - } - } - translator := NewTranslator() - values := make(map[string]interface{}) - _ = translator.translateDeprecatedAutoscalingFields(values, iop) - val, found, _ := tpath.GetFromStructPath(values, "global.autoscalingv2API") - if tt.expectFound { - assert.Equal(t, found, true) - assert.Equal(t, val, false) - } else { - assert.Equal(t, found, false) - } - }) - } -} diff --git a/operator/pkg/translate/translate_value.go b/operator/pkg/translate/translate_value.go deleted file mode 100644 index 48689579a..000000000 --- a/operator/pkg/translate/translate_value.go +++ /dev/null @@ -1,719 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package translate - -import ( - "fmt" - "sort" - "strings" -) - -import ( - "istio.io/api/operator/v1alpha1" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/metrics" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/tpath" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/operator/pkg/version" - oversion "github.com/apache/dubbo-go-pixiu/operator/version" -) - -// ReverseTranslator is a set of mappings to translate between values.yaml and API paths, charts, k8s paths. -type ReverseTranslator struct { - Version version.MinorVersion - // APIMapping is Values.yaml path to API path mapping using longest prefix match. If the path is a non-leaf node, - // the output path is the matching portion of the path, plus any remaining output path. - APIMapping map[string]*Translation `yaml:"apiMapping,omitempty"` - // KubernetesPatternMapping defines mapping patterns from k8s resource paths to IstioOperator API paths. - KubernetesPatternMapping map[string]string `yaml:"kubernetesPatternMapping,omitempty"` - // KubernetesMapping defines actual k8s mappings generated from KubernetesPatternMapping before each translation. - KubernetesMapping map[string]*Translation `yaml:"kubernetesMapping,omitempty"` - // GatewayKubernetesMapping defines actual k8s mappings for gateway components generated from KubernetesPatternMapping before each translation. - GatewayKubernetesMapping gatewayKubernetesMapping `yaml:"GatewayKubernetesMapping,omitempty"` - // ValuesToComponentName defines mapping from value path to component name in API paths. - ValuesToComponentName map[string]name.ComponentName `yaml:"valuesToComponentName,omitempty"` -} - -type gatewayKubernetesMapping struct { - IngressMapping map[string]*Translation - EgressMapping map[string]*Translation -} - -var ( - // Component enablement mapping. Ex "{{.ValueComponent}}.enabled": Components.{{.ComponentName}}.enabled}", nil}, - componentEnablementPattern = "Components.{{.ComponentName}}.Enabled" - // specialComponentPath lists cases of component path of values.yaml we need to have special treatment. - specialComponentPath = map[string]bool{ - "gateways": true, - "gateways.istio-ingressgateway": true, - "gateways.istio-egressgateway": true, - } - - skipTranslate = map[name.ComponentName]bool{ - name.IstioBaseComponentName: true, - name.IstioOperatorComponentName: true, - name.IstioOperatorCustomResourceName: true, - name.CNIComponentName: true, - name.IstiodRemoteComponentName: true, - } - - gatewayPathMapping = map[string]name.ComponentName{ - "gateways.istio-ingressgateway": name.IngressComponentName, - "gateways.istio-egressgateway": name.EgressComponentName, - } -) - -// initAPIMapping generate the reverse mapping from original translator apiMapping. -func (t *ReverseTranslator) initAPIAndComponentMapping() { - ts := NewTranslator() - t.APIMapping = make(map[string]*Translation) - t.KubernetesMapping = make(map[string]*Translation) - t.ValuesToComponentName = make(map[string]name.ComponentName) - for valKey, outVal := range ts.APIMapping { - t.APIMapping[outVal.OutPath] = &Translation{valKey, nil} - } - for cn, cm := range ts.ComponentMaps { - // we use dedicated translateGateway for gateway instead - if !skipTranslate[cn] && !cm.SkipReverseTranslate && !cn.IsGateway() { - t.ValuesToComponentName[cm.ToHelmValuesTreeRoot] = cn - } - } -} - -// initK8SMapping generates the k8s settings mapping for components that are enabled based on templates. -func (t *ReverseTranslator) initK8SMapping() error { - outputMapping := make(map[string]*Translation) - for valKey, componentName := range t.ValuesToComponentName { - for K8SValKey, outPathTmpl := range t.KubernetesPatternMapping { - newKey, err := renderComponentName(K8SValKey, valKey) - if err != nil { - return err - } - newVal, err := renderFeatureComponentPathTemplate(outPathTmpl, componentName) - if err != nil { - return err - } - outputMapping[newKey] = &Translation{newVal, nil} - } - } - - t.KubernetesMapping = outputMapping - - igwOutputMapping := make(map[string]*Translation) - egwOutputMapping := make(map[string]*Translation) - for valKey, componentName := range gatewayPathMapping { - mapping := igwOutputMapping - if componentName == name.EgressComponentName { - mapping = egwOutputMapping - } - for K8SValKey, outPathTmpl := range t.KubernetesPatternMapping { - newKey, err := renderComponentName(K8SValKey, valKey) - if err != nil { - return err - } - newP := util.PathFromString(outPathTmpl) - mapping[newKey] = &Translation{newP[len(newP)-2:].String(), nil} - } - } - t.GatewayKubernetesMapping = gatewayKubernetesMapping{IngressMapping: igwOutputMapping, EgressMapping: egwOutputMapping} - return nil -} - -// NewReverseTranslator creates a new ReverseTranslator for minorVersion and returns a ptr to it. -func NewReverseTranslator() *ReverseTranslator { - rt := &ReverseTranslator{ - KubernetesPatternMapping: map[string]string{ - "{{.ValueComponentName}}.env": "Components.{{.ComponentName}}.K8s.Env", - "{{.ValueComponentName}}.autoscaleEnabled": "Components.{{.ComponentName}}.K8s.HpaSpec", - "{{.ValueComponentName}}.imagePullPolicy": "Components.{{.ComponentName}}.K8s.ImagePullPolicy", - "{{.ValueComponentName}}.nodeSelector": "Components.{{.ComponentName}}.K8s.NodeSelector", - "{{.ValueComponentName}}.tolerations": "Components.{{.ComponentName}}.K8s.Tolerations", - "{{.ValueComponentName}}.podDisruptionBudget": "Components.{{.ComponentName}}.K8s.PodDisruptionBudget", - "{{.ValueComponentName}}.podAnnotations": "Components.{{.ComponentName}}.K8s.PodAnnotations", - "{{.ValueComponentName}}.priorityClassName": "Components.{{.ComponentName}}.K8s.PriorityClassName", - "{{.ValueComponentName}}.readinessProbe": "Components.{{.ComponentName}}.K8s.ReadinessProbe", - "{{.ValueComponentName}}.replicaCount": "Components.{{.ComponentName}}.K8s.ReplicaCount", - "{{.ValueComponentName}}.resources": "Components.{{.ComponentName}}.K8s.Resources", - "{{.ValueComponentName}}.rollingMaxSurge": "Components.{{.ComponentName}}.K8s.Strategy", - "{{.ValueComponentName}}.rollingMaxUnavailable": "Components.{{.ComponentName}}.K8s.Strategy", - "{{.ValueComponentName}}.serviceAnnotations": "Components.{{.ComponentName}}.K8s.ServiceAnnotations", - }, - } - rt.initAPIAndComponentMapping() - rt.Version = oversion.OperatorBinaryVersion.MinorVersion - return rt -} - -// TranslateFromValueToSpec translates from values.yaml value to IstioOperatorSpec. -func (t *ReverseTranslator) TranslateFromValueToSpec(values []byte, force bool) (controlPlaneSpec *v1alpha1.IstioOperatorSpec, err error) { - yamlTree := make(map[string]interface{}) - err = yaml.Unmarshal(values, &yamlTree) - if err != nil { - return nil, fmt.Errorf("error when unmarshalling into untype tree %v", err) - } - - outputTree := make(map[string]interface{}) - err = t.TranslateTree(yamlTree, outputTree, nil) - if err != nil { - return nil, err - } - outputVal, err := yaml.Marshal(outputTree) - if err != nil { - return nil, err - } - - cpSpec := &v1alpha1.IstioOperatorSpec{} - err = util.UnmarshalWithJSONPB(string(outputVal), cpSpec, force) - - if err != nil { - return nil, fmt.Errorf("error when unmarshalling into control plane spec %v, \nyaml:\n %s", err, outputVal) - } - - return cpSpec, nil -} - -// TranslateTree translates input value.yaml Tree to ControlPlaneSpec Tree. -func (t *ReverseTranslator) TranslateTree(valueTree map[string]interface{}, cpSpecTree map[string]interface{}, path util.Path) error { - // translate enablement and namespace - err := t.setEnablementFromValue(valueTree, cpSpecTree) - if err != nil { - return fmt.Errorf("error when translating enablement and namespace from value.yaml tree: %v", err) - } - // translate with api mapping - err = t.translateAPI(valueTree, cpSpecTree) - if err != nil { - return fmt.Errorf("error when translating value.yaml tree with global mapping: %v", err) - } - - // translate with k8s mapping - if err := t.TranslateK8S(valueTree, cpSpecTree); err != nil { - return err - } - - if err := t.translateGateway(valueTree, cpSpecTree); err != nil { - return fmt.Errorf("error when translating gateway with kubernetes mapping: %v", err.Error()) - } - // translate remaining untranslated paths into component values - err = t.translateRemainingPaths(valueTree, cpSpecTree, nil) - if err != nil { - return fmt.Errorf("error when translating remaining path: %v", err) - } - return nil -} - -// TranslateK8S is a helper function to translate k8s settings from values.yaml to IstioOperator, except for gateways. -func (t *ReverseTranslator) TranslateK8S(valueTree map[string]interface{}, cpSpecTree map[string]interface{}) error { - // translate with k8s mapping - if err := t.initK8SMapping(); err != nil { - return fmt.Errorf("error when initiating k8s mapping: %v", err) - } - if err := t.translateK8sTree(valueTree, cpSpecTree, t.KubernetesMapping); err != nil { - return fmt.Errorf("error when translating value.yaml tree with kubernetes mapping: %v", err) - } - return nil -} - -// setEnablementFromValue translates the enablement value of components in the values.yaml -// tree, based on feature/component inheritance relationship. -func (t *ReverseTranslator) setEnablementFromValue(valueSpec map[string]interface{}, root map[string]interface{}) error { - for _, cni := range t.ValuesToComponentName { - enabled, pathExist, err := IsComponentEnabledFromValue(cni, valueSpec) - if err != nil { - return err - } - if !pathExist { - continue - } - tmpl := componentEnablementPattern - ceVal, err := renderFeatureComponentPathTemplate(tmpl, cni) - if err != nil { - return err - } - outCP := util.ToYAMLPath(ceVal) - // set component enablement - if err := tpath.WriteNode(root, outCP, enabled); err != nil { - return err - } - } - - return nil -} - -// WarningForGatewayK8SSettings creates deprecated warning messages -// when user try to set kubernetes settings for gateways via values api. -func (t *ReverseTranslator) WarningForGatewayK8SSettings(valuesOverlay string) (string, error) { - gwOverlay, err := tpath.GetConfigSubtree(valuesOverlay, "gateways") - if err != nil { - return "", fmt.Errorf("error getting gateways overlay from valuesOverlayYaml %v", err) - } - if gwOverlay == "" { - return "", nil - } - var deprecatedFields []string - for inPath := range t.GatewayKubernetesMapping.IngressMapping { - _, found, err := tpath.GetPathContext(valuesOverlay, util.ToYAMLPath(inPath), false) - if err != nil { - scope.Debug(err.Error()) - continue - } - if found { - deprecatedFields = append(deprecatedFields, inPath) - } - } - for inPath := range t.GatewayKubernetesMapping.EgressMapping { - _, found, err := tpath.GetPathContext(valuesOverlay, util.ToYAMLPath(inPath), false) - if err != nil { - scope.Debug(err.Error()) - continue - } - if found { - deprecatedFields = append(deprecatedFields, inPath) - } - } - if len(deprecatedFields) == 0 { - return "", nil - } - warningMessage := fmt.Sprintf("using deprecated values api paths: %s.\n"+ - " please use k8s spec of gateway components instead\n", strings.Join(deprecatedFields, ",")) - return warningMessage, nil -} - -// translateGateway handles translation for gateways specific configuration -func (t *ReverseTranslator) translateGateway(valueSpec map[string]interface{}, root map[string]interface{}) error { - for inPath, outPath := range gatewayPathMapping { - enabled, pathExist, err := IsComponentEnabledFromValue(outPath, valueSpec) - if err != nil { - return err - } - if !pathExist && !enabled { - continue - } - gwSpecs := make([]map[string]interface{}, 1) - gwSpec := make(map[string]interface{}) - gwSpecs[0] = gwSpec - gwSpec["enabled"] = enabled - gwSpec["name"] = util.ToYAMLPath(inPath)[1] - outCP := util.ToYAMLPath("Components." + string(outPath)) - - if enabled { - mapping := t.GatewayKubernetesMapping.IngressMapping - if outPath == name.EgressComponentName { - mapping = t.GatewayKubernetesMapping.EgressMapping - } - err = t.translateK8sTree(valueSpec, gwSpec, mapping) - if err != nil { - return err - } - } - err = tpath.WriteNode(root, outCP, gwSpecs) - if err != nil { - return err - } - } - return nil -} - -// TranslateK8SfromValueToIOP use reverse translation to convert k8s settings defined in values API to IOP API. -// this ensures that user overlays that set k8s through spec.values -// are not overridden by spec.components.X.k8s settings in the base profiles -func (t *ReverseTranslator) TranslateK8SfromValueToIOP(userOverlayYaml string) (string, error) { - valuesOverlay, err := tpath.GetConfigSubtree(userOverlayYaml, "spec.values") - if err != nil { - scope.Debugf("no spec.values section from userOverlayYaml %v", err) - return "", nil - } - valuesOverlayTree := make(map[string]interface{}) - err = yaml.Unmarshal([]byte(valuesOverlay), &valuesOverlayTree) - if err != nil { - return "", fmt.Errorf("error unmarshalling values overlay yaml into untype tree %v", err) - } - iopSpecTree := make(map[string]interface{}) - iopSpecOverlay, err := tpath.GetConfigSubtree(userOverlayYaml, "spec") - if err != nil { - return "", fmt.Errorf("error getting iop spec subtree from overlay yaml %v", err) - } - err = yaml.Unmarshal([]byte(iopSpecOverlay), &iopSpecTree) - if err != nil { - return "", fmt.Errorf("error unmarshalling spec overlay yaml into tree %v", err) - } - if err = t.TranslateK8S(valuesOverlayTree, iopSpecTree); err != nil { - return "", err - } - warning, err := t.WarningForGatewayK8SSettings(valuesOverlay) - if err != nil { - return "", fmt.Errorf("error handling values gateway k8s settings: %v", err) - } - if warning != "" { - return "", fmt.Errorf(warning) - } - iopSpecTreeYAML, err := yaml.Marshal(iopSpecTree) - if err != nil { - return "", fmt.Errorf("error marshaling reverse translated tree %v", err) - } - iopTreeYAML, err := tpath.AddSpecRoot(string(iopSpecTreeYAML)) - if err != nil { - return "", fmt.Errorf("error adding spec root: %v", err) - } - // overlay the reverse translated iopTreeYAML back to userOverlayYaml - finalYAML, err := util.OverlayYAML(userOverlayYaml, iopTreeYAML) - if err != nil { - return "", fmt.Errorf("failed to overlay the reverse translated iopTreeYAML: %v", err) - } - return finalYAML, err -} - -// translateStrategy translates Deployment Strategy related configurations from helm values.yaml tree. -func translateStrategy(fieldName string, outPath string, value interface{}, cpSpecTree map[string]interface{}) error { - fieldMap := map[string]string{ - "rollingMaxSurge": "maxSurge", - "rollingMaxUnavailable": "maxUnavailable", - } - newFieldName, ok := fieldMap[fieldName] - if !ok { - return fmt.Errorf("expected field name found in values.yaml: %s", fieldName) - } - outPath += ".rollingUpdate." + newFieldName - - scope.Debugf("path has value in helm Value.yaml tree, mapping to output path %s", outPath) - if err := tpath.WriteNode(cpSpecTree, util.ToYAMLPath(outPath), value); err != nil { - return err - } - return nil -} - -// translateHPASpec translates HPA related configurations from helm values.yaml tree. -// do not translate if autoscaleEnabled is explicitly set to false -func translateHPASpec(inPath string, outPath string, valueTree map[string]interface{}, cpSpecTree map[string]interface{}) error { - m, found, err := tpath.Find(valueTree, util.ToYAMLPath(inPath)) - if err != nil { - return err - } - if found { - asEnabled, ok := m.(bool) - if !ok { - return fmt.Errorf("expect autoscaleEnabled node type to be bool but got: %T", m) - } - if !asEnabled { - return nil - } - } - - newP := util.PathFromString(inPath) - // last path element is k8s setting name - newPS := newP[:len(newP)-1].String() - valMap := map[string]string{ - ".autoscaleMin": ".minReplicas", - ".autoscaleMax": ".maxReplicas", - } - for key, newVal := range valMap { - valPath := newPS + key - asVal, found, err := tpath.Find(valueTree, util.ToYAMLPath(valPath)) - if found && err == nil { - if err := setOutputAndClean(valPath, outPath+newVal, asVal, valueTree, cpSpecTree, true); err != nil { - return err - } - } - } - valPath := newPS + ".cpu.targetAverageUtilization" - asVal, found, err := tpath.Find(valueTree, util.ToYAMLPath(valPath)) - if found && err == nil { - rs := make([]interface{}, 1) - rsVal := ` -- type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %f` - - rsString := fmt.Sprintf(rsVal, asVal) - if err = yaml.Unmarshal([]byte(rsString), &rs); err != nil { - return err - } - if err := setOutputAndClean(valPath, outPath+".metrics", rs, valueTree, cpSpecTree, true); err != nil { - return err - } - } - - // There is no direct source from value.yaml for scaleTargetRef value, we need to construct from component name - if found { - revision := "" - rev, ok := cpSpecTree["revision"] - if ok { - revision = rev.(string) - } - st := make(map[string]interface{}) - stVal := ` -apiVersion: apps/v1 -kind: Deployment -name: %s` - - // need to do special handling for gateways - if specialComponentPath[newPS] && len(newP) > 2 { - newPS = newP[1 : len(newP)-1].String() - } - // convert from values component name to correct deployment target - if newPS == "pilot" { - newPS = "istiod" - if revision != "" { - newPS = newPS + "-" + revision - } - } - stString := fmt.Sprintf(stVal, newPS) - if err := yaml.Unmarshal([]byte(stString), &st); err != nil { - return err - } - if err := setOutputAndClean(valPath, outPath+".scaleTargetRef", st, valueTree, cpSpecTree, false); err != nil { - return err - } - } - return nil -} - -// setOutputAndClean is helper function to set value of iscp tree and clean the original value from value.yaml tree. -func setOutputAndClean(valPath, outPath string, outVal interface{}, valueTree, cpSpecTree map[string]interface{}, clean bool) error { - scope.Debugf("path has value in helm Value.yaml tree, mapping to output path %s", outPath) - - if err := tpath.WriteNode(cpSpecTree, util.ToYAMLPath(outPath), outVal); err != nil { - return err - } - if !clean { - return nil - } - if _, err := tpath.Delete(valueTree, util.ToYAMLPath(valPath)); err != nil { - return err - } - return nil -} - -// translateEnv translates env value from helm values.yaml tree. -func translateEnv(outPath string, value interface{}, cpSpecTree map[string]interface{}) error { - envMap, ok := value.(map[string]interface{}) - if !ok { - return fmt.Errorf("expect env node type to be map[string]interface{} but got: %T", value) - } - if len(envMap) == 0 { - return nil - } - scope.Debugf("path has value in helm Value.yaml tree, mapping to output path %s", outPath) - nc, found, _ := tpath.GetPathContext(cpSpecTree, util.ToYAMLPath(outPath), false) - var envValStr []byte - if nc != nil { - envValStr, _ = yaml.Marshal(nc.Node) - } - if !found || strings.TrimSpace(string(envValStr)) == "{}" { - scope.Debugf("path doesn't have value in k8s setting with output path %s, override with helm Value.yaml tree", outPath) - outEnv := make([]map[string]interface{}, len(envMap)) - keys := make([]string, 0, len(envMap)) - for k := range envMap { - keys = append(keys, k) - } - sort.Strings(keys) - for i, k := range keys { - outEnv[i] = make(map[string]interface{}) - outEnv[i]["name"] = k - outEnv[i]["value"] = fmt.Sprintf("%v", envMap[k]) - } - if err := tpath.WriteNode(cpSpecTree, util.ToYAMLPath(outPath), outEnv); err != nil { - return err - } - } else { - scope.Debugf("path has value in k8s setting with output path %s, merge it with helm Value.yaml tree", outPath) - keys := make([]string, 0, len(envMap)) - for k := range envMap { - keys = append(keys, k) - } - sort.Strings(keys) - for _, k := range keys { - outEnv := make(map[string]interface{}) - outEnv["name"] = k - outEnv["value"] = fmt.Sprintf("%v", envMap[k]) - if err := tpath.MergeNode(cpSpecTree, util.ToYAMLPath(outPath), outEnv); err != nil { - return err - } - } - } - return nil -} - -// translateK8sTree is internal method for translating K8s configurations from value.yaml tree. -func (t *ReverseTranslator) translateK8sTree(valueTree map[string]interface{}, - cpSpecTree map[string]interface{}, mapping map[string]*Translation) error { - for inPath, v := range mapping { - scope.Debugf("Checking for k8s path %s in helm Value.yaml tree", inPath) - path := util.PathFromString(inPath) - k8sSettingName := "" - if len(path) != 0 { - k8sSettingName = path[len(path)-1] - } - if k8sSettingName == "autoscaleEnabled" { - if err := translateHPASpec(inPath, v.OutPath, valueTree, cpSpecTree); err != nil { - return fmt.Errorf("error in translating K8s HPA spec: %s", err) - } - metrics.LegacyPathTranslationTotal.Increment() - continue - } - m, found, err := tpath.Find(valueTree, util.ToYAMLPath(inPath)) - if err != nil { - return err - } - if !found { - scope.Debugf("path %s not found in helm Value.yaml tree, skip mapping.", inPath) - continue - } - - if mstr, ok := m.(string); ok && mstr == "" { - scope.Debugf("path %s is empty string, skip mapping.", inPath) - continue - } - // Zero int values are due to proto3 compiling to scalars rather than ptrs. Skip these because values of 0 are - // the default in destination fields and need not be set explicitly. - if mint, ok := util.ToIntValue(m); ok && mint == 0 { - scope.Debugf("path %s is int 0, skip mapping.", inPath) - continue - } - - switch k8sSettingName { - case "env": - err := translateEnv(v.OutPath, m, cpSpecTree) - if err != nil { - return fmt.Errorf("error in translating k8s Env: %s", err) - } - - case "rollingMaxSurge", "rollingMaxUnavailable": - err := translateStrategy(k8sSettingName, v.OutPath, m, cpSpecTree) - if err != nil { - return fmt.Errorf("error in translating k8s Strategy: %s", err) - } - - default: - if util.IsValueNilOrDefault(m) { - continue - } - output := util.ToYAMLPath(v.OutPath) - scope.Debugf("path has value in helm Value.yaml tree, mapping to output path %s", output) - - if err := tpath.WriteNode(cpSpecTree, output, m); err != nil { - return err - } - } - metrics.LegacyPathTranslationTotal.Increment() - - if _, err := tpath.Delete(valueTree, util.ToYAMLPath(inPath)); err != nil { - return err - } - } - return nil -} - -// translateRemainingPaths translates remaining paths that are not available in existing mappings. -func (t *ReverseTranslator) translateRemainingPaths(valueTree map[string]interface{}, - cpSpecTree map[string]interface{}, path util.Path) error { - for key, val := range valueTree { - newPath := append(path, key) - // value set to nil means no translation needed or being translated already. - if val == nil { - continue - } - switch node := val.(type) { - case map[string]interface{}: - err := t.translateRemainingPaths(node, cpSpecTree, newPath) - if err != nil { - return err - } - case []interface{}: - if err := tpath.WriteNode(cpSpecTree, util.ToYAMLPath("Values."+newPath.String()), node); err != nil { - return err - } - // remaining leaf need to be put into root.values - default: - if t.isEnablementPath(newPath) { - continue - } - if err := tpath.WriteNode(cpSpecTree, util.ToYAMLPath("Values."+newPath.String()), val); err != nil { - return err - } - } - } - return nil -} - -// translateAPI is internal method for translating value.yaml tree based on API mapping. -func (t *ReverseTranslator) translateAPI(valueTree map[string]interface{}, - cpSpecTree map[string]interface{}) error { - for inPath, v := range t.APIMapping { - scope.Debugf("Checking for path %s in helm Value.yaml tree", inPath) - m, found, err := tpath.Find(valueTree, util.ToYAMLPath(inPath)) - if err != nil { - return err - } - if !found { - scope.Debugf("path %s not found in helm Value.yaml tree, skip mapping.", inPath) - continue - } - if mstr, ok := m.(string); ok && mstr == "" { - scope.Debugf("path %s is empty string, skip mapping.", inPath) - continue - } - // Zero int values are due to proto3 compiling to scalars rather than ptrs. Skip these because values of 0 are - // the default in destination fields and need not be set explicitly. - if mint, ok := util.ToIntValue(m); ok && mint == 0 { - scope.Debugf("path %s is int 0, skip mapping.", inPath) - continue - } - - path := util.ToYAMLPath(v.OutPath) - scope.Debugf("path has value in helm Value.yaml tree, mapping to output path %s", path) - metrics.LegacyPathTranslationTotal. - With(metrics.ResourceKindLabel.Value(inPath)).Increment() - - if err := tpath.WriteNode(cpSpecTree, path, m); err != nil { - return err - } - - if _, err := tpath.Delete(valueTree, util.ToYAMLPath(inPath)); err != nil { - return err - } - } - return nil -} - -// isEnablementPath is helper function to check whether paths represent enablement of components in values.yaml -func (t *ReverseTranslator) isEnablementPath(path util.Path) bool { - if len(path) < 2 || path[len(path)-1] != "enabled" { - return false - } - - pf := path[:len(path)-1].String() - if specialComponentPath[pf] { - return true - } - - _, exist := t.ValuesToComponentName[pf] - return exist -} - -// renderComponentName renders a template of the form {{.ComponentName}} with -// the supplied parameters. -func renderComponentName(tmpl string, componentName string) (string, error) { - type temp struct { - ValueComponentName string - } - return util.RenderTemplate(tmpl, temp{componentName}) -} diff --git a/operator/pkg/translate/translate_value_test.go b/operator/pkg/translate/translate_value_test.go deleted file mode 100644 index 0abe1104f..000000000 --- a/operator/pkg/translate/translate_value_test.go +++ /dev/null @@ -1,983 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package translate - -import ( - "testing" -) - -import ( - "github.com/golang/protobuf/jsonpb" // nolint: staticcheck - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio" - "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -func TestValueToProto(t *testing.T) { - tests := []struct { - desc string - valueYAML string - want string - wantErr string - }{ - { - desc: "K8s resources translation", - valueYAML: ` -pilot: - enabled: true - rollingMaxSurge: 100% - rollingMaxUnavailable: 25% - resources: - requests: - cpu: 1000m - memory: 1G - replicaCount: 1 - nodeSelector: - kubernetes.io/os: linux - tolerations: - - key: dedicated - operator: Exists - effect: NoSchedule - - key: CriticalAddonsOnly - operator: Exists - autoscaleEnabled: true - autoscaleMax: 3 - autoscaleMin: 1 - cpu: - targetAverageUtilization: 80 - traceSampling: 1.0 - image: pilot - env: - GODEBUG: gctrace=1 -global: - hub: docker.io/istio - istioNamespace: dubbo-system - tag: 1.2.3 - proxy: - readinessInitialDelaySeconds: 2 -`, - want: ` -hub: docker.io/istio -tag: 1.2.3 -components: - pilot: - enabled: true - k8s: - replicaCount: 1 - env: - - name: GODEBUG - value: gctrace=1 - hpaSpec: - maxReplicas: 3 - minReplicas: 1 - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: istiod - metrics: - - resource: - name: cpu - target: - averageUtilization: 80 - type: Utilization - type: Resource - nodeSelector: - kubernetes.io/os: linux - tolerations: - - key: dedicated - operator: Exists - effect: NoSchedule - - key: CriticalAddonsOnly - operator: Exists - resources: - requests: - cpu: 1000m - memory: 1G - strategy: - rollingUpdate: - maxSurge: 100% - maxUnavailable: 25% -values: - global: - istioNamespace: dubbo-system - proxy: - readinessInitialDelaySeconds: 2 - pilot: - image: pilot - autoscaleEnabled: true - traceSampling: 1 -`, - }, - { - desc: "All Enabled", - valueYAML: ` -global: - hub: docker.io/istio - istioNamespace: dubbo-system - tag: 1.2.3 -pilot: - enabled: true -gateways: - enabled: true - istio-ingressgateway: - rollingMaxSurge: 4 - rollingMaxUnavailable: 1 - resources: - requests: - cpu: 1000m - memory: 1G - enabled: true -`, - want: ` -hub: docker.io/istio -tag: 1.2.3 -components: - pilot: - enabled: true - ingressGateways: - - name: istio-ingressgateway - enabled: true - k8s: - resources: - requests: - cpu: 1000m - memory: 1G - strategy: - rollingUpdate: - maxSurge: 4 - maxUnavailable: 1 -values: - global: - istioNamespace: dubbo-system -`, - }, - { - desc: "Some components Disabled", - valueYAML: ` -pilot: - enabled: true -global: - hub: docker.io/istio - istioNamespace: dubbo-system - tag: 1.2.3 -`, - want: ` -hub: docker.io/istio -tag: 1.2.3 -components: - pilot: - enabled: true -values: - global: - istioNamespace: dubbo-system -`, - }, - } - tr := NewReverseTranslator() - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - valueStruct := v1alpha1.Values{} - err := util.UnmarshalWithJSONPB(tt.valueYAML, &valueStruct, false) - if err != nil { - t.Fatalf("unmarshal(%s): got error %s", tt.desc, err) - } - gotSpec, err := tr.TranslateFromValueToSpec([]byte(tt.valueYAML), false) - if gotErr, wantErr := errToString(err), tt.wantErr; gotErr != wantErr { - t.Errorf("ValuesToProto(%s)(%v): gotErr:%s, wantErr:%s", tt.desc, tt.valueYAML, gotErr, wantErr) - } - if tt.wantErr == "" { - ms := jsonpb.Marshaler{} - gotString, err := ms.MarshalToString(gotSpec) - if err != nil { - t.Errorf("failed to marshal translated IstioOperatorSpec: %s", err) - } - cpYaml, _ := yaml.JSONToYAML([]byte(gotString)) - if want := tt.want; !util.IsYAMLEqual(gotString, want) { - t.Errorf("ValuesToProto(%s): got:\n%s\n\nwant:\n%s\nDiff:\n%s\n", tt.desc, string(cpYaml), want, util.YAMLDiff(gotString, want)) - } - - } - }) - } -} - -func TestValueToK8s(t *testing.T) { - tests := []struct { - desc string - inIOPSpec string - want string - wantErr string - }{ - { - desc: "pilot env k8s setting with values", - inIOPSpec: ` -spec: - components: - pilot: - k8s: - nodeSelector: - master: "true" - env: - - name: EXTERNAL_CA - value: ISTIOD_RA_KUBERNETES_API - - name: K8S_SIGNER - value: kubernetes.io/legacy-unknown - values: - pilot: - enabled: true - rollingMaxSurge: 100% - rollingMaxUnavailable: 25% - resources: - requests: - cpu: 1000m - memory: 1G - replicaCount: 1 - nodeSelector: - kubernetes.io/os: linux - tolerations: - - key: dedicated - operator: Exists - effect: NoSchedule - - key: CriticalAddonsOnly - operator: Exists - autoscaleEnabled: true - autoscaleMax: 3 - autoscaleMin: 1 - cpu: - targetAverageUtilization: 80 - traceSampling: 1.0 - image: pilot - env: - GODEBUG: gctrace=1 - global: - hub: docker.io/istio - tag: 1.2.3 - istioNamespace: dubbo-system - proxy: - readinessInitialDelaySeconds: 2 -`, - want: ` -spec: - components: - pilot: - k8s: - env: - - name: EXTERNAL_CA - value: ISTIOD_RA_KUBERNETES_API - - name: K8S_SIGNER - value: kubernetes.io/legacy-unknown - - name: GODEBUG - value: gctrace=1 - hpaSpec: - minReplicas: 1 - maxReplicas: 3 - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: istiod - metrics: - - type: Resource - resource: - name: cpu - target: - averageUtilization: 80 - type: Utilization - nodeSelector: - master: "true" - kubernetes.io/os: linux - replicaCount: 1 - resources: - requests: - cpu: 1000m - memory: 1G - strategy: - rollingUpdate: - maxSurge: 100% - maxUnavailable: 25% - tolerations: - - effect: NoSchedule - key: dedicated - operator: Exists - - key: CriticalAddonsOnly - operator: Exists - values: - global: - hub: docker.io/istio - tag: 1.2.3 - istioNamespace: dubbo-system - proxy: - readinessInitialDelaySeconds: 2 - pilot: - enabled: true - rollingMaxSurge: 100% - rollingMaxUnavailable: 25% - resources: - requests: - cpu: 1000m - memory: 1G - replicaCount: 1 - nodeSelector: - kubernetes.io/os: linux - tolerations: - - key: dedicated - operator: Exists - effect: NoSchedule - - key: CriticalAddonsOnly - operator: Exists - autoscaleEnabled: true - autoscaleMax: 3 - autoscaleMin: 1 - cpu: - targetAverageUtilization: 80 - traceSampling: 1.0 - image: pilot - env: - GODEBUG: gctrace=1 -`, - }, - { - desc: "pilot no env k8s setting with values", - inIOPSpec: ` -spec: - components: - pilot: - k8s: - nodeSelector: - master: "true" - values: - pilot: - enabled: true - rollingMaxSurge: 100% - rollingMaxUnavailable: 25% - resources: - requests: - cpu: 1000m - memory: 1G - replicaCount: 1 - nodeSelector: - kubernetes.io/os: linux - tolerations: - - key: dedicated - operator: Exists - effect: NoSchedule - - key: CriticalAddonsOnly - operator: Exists - autoscaleEnabled: true - autoscaleMax: 3 - autoscaleMin: 1 - cpu: - targetAverageUtilization: 80 - traceSampling: 1.0 - image: pilot - env: - GODEBUG: gctrace=1 - global: - hub: docker.io/istio - tag: 1.2.3 - istioNamespace: dubbo-system - proxy: - readinessInitialDelaySeconds: 2 -`, - want: ` -spec: - components: - pilot: - k8s: - env: - - name: GODEBUG - value: gctrace=1 - hpaSpec: - minReplicas: 1 - maxReplicas: 3 - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: istiod - metrics: - - type: Resource - resource: - name: cpu - target: - averageUtilization: 80 - type: Utilization - nodeSelector: - master: "true" - kubernetes.io/os: linux - replicaCount: 1 - resources: - requests: - cpu: 1000m - memory: 1G - strategy: - rollingUpdate: - maxSurge: 100% - maxUnavailable: 25% - tolerations: - - effect: NoSchedule - key: dedicated - operator: Exists - - key: CriticalAddonsOnly - operator: Exists - values: - global: - hub: docker.io/istio - tag: 1.2.3 - istioNamespace: dubbo-system - proxy: - readinessInitialDelaySeconds: 2 - pilot: - enabled: true - rollingMaxSurge: 100% - rollingMaxUnavailable: 25% - resources: - requests: - cpu: 1000m - memory: 1G - replicaCount: 1 - nodeSelector: - kubernetes.io/os: linux - tolerations: - - key: dedicated - operator: Exists - effect: NoSchedule - - key: CriticalAddonsOnly - operator: Exists - autoscaleEnabled: true - autoscaleMax: 3 - autoscaleMin: 1 - cpu: - targetAverageUtilization: 80 - traceSampling: 1.0 - image: pilot - env: - GODEBUG: gctrace=1 -`, - }, - { - desc: "pilot k8s setting with empty env in values", - inIOPSpec: ` -spec: - components: - pilot: - k8s: - nodeSelector: - master: "true" - env: - - name: SPIFFE_BUNDLE_ENDPOINTS - value: "SPIFFE_BUNDLE_ENDPOINT" - values: - pilot: - enabled: true - rollingMaxSurge: 100% - rollingMaxUnavailable: 25% - resources: - requests: - cpu: 1000m - memory: 1G - replicaCount: 1 - nodeSelector: - kubernetes.io/os: linux - tolerations: - - key: dedicated - operator: Exists - effect: NoSchedule - - key: CriticalAddonsOnly - operator: Exists - autoscaleEnabled: true - autoscaleMax: 3 - autoscaleMin: 1 - cpu: - targetAverageUtilization: 80 - traceSampling: 1.0 - image: pilot - env: {} - global: - hub: docker.io/istio - tag: 1.2.3 - istioNamespace: dubbo-system - proxy: - readinessInitialDelaySeconds: 2 -`, - want: ` -spec: - components: - pilot: - k8s: - env: - - name: SPIFFE_BUNDLE_ENDPOINTS - value: "SPIFFE_BUNDLE_ENDPOINT" - hpaSpec: - minReplicas: 1 - maxReplicas: 3 - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: istiod - metrics: - - type: Resource - resource: - name: cpu - target: - averageUtilization: 80 - type: Utilization - nodeSelector: - master: "true" - kubernetes.io/os: linux - replicaCount: 1 - resources: - requests: - cpu: 1000m - memory: 1G - strategy: - rollingUpdate: - maxSurge: 100% - maxUnavailable: 25% - tolerations: - - effect: NoSchedule - key: dedicated - operator: Exists - - key: CriticalAddonsOnly - operator: Exists - values: - global: - hub: docker.io/istio - tag: 1.2.3 - istioNamespace: dubbo-system - proxy: - readinessInitialDelaySeconds: 2 - pilot: - enabled: true - rollingMaxSurge: 100% - rollingMaxUnavailable: 25% - resources: - requests: - cpu: 1000m - memory: 1G - replicaCount: 1 - nodeSelector: - kubernetes.io/os: linux - tolerations: - - key: dedicated - operator: Exists - effect: NoSchedule - - key: CriticalAddonsOnly - operator: Exists - autoscaleEnabled: true - autoscaleMax: 3 - autoscaleMin: 1 - cpu: - targetAverageUtilization: 80 - traceSampling: 1.0 - image: pilot - env: {} -`, - }, - { - desc: "pilot no env k8s setting with multiple env variables in values", - inIOPSpec: ` -spec: - components: - pilot: - k8s: - nodeSelector: - master: "true" - values: - pilot: - enabled: true - rollingMaxSurge: 100% - rollingMaxUnavailable: 25% - resources: - requests: - cpu: 1000m - memory: 1G - replicaCount: 1 - nodeSelector: - kubernetes.io/os: linux - tolerations: - - key: dedicated - operator: Exists - effect: NoSchedule - - key: CriticalAddonsOnly - operator: Exists - autoscaleEnabled: true - autoscaleMax: 3 - autoscaleMin: 1 - cpu: - targetAverageUtilization: 80 - traceSampling: 1.0 - image: pilot - env: - PILOT_ENABLE_ISTIO_TAGS: false - PILOT_ENABLE_LEGACY_ISTIO_MUTUAL_CREDENTIAL_NAME: false - PROXY_XDS_DEBUG_VIA_AGENT: false - global: - hub: docker.io/istio - tag: 1.2.3 - istioNamespace: dubbo-system - proxy: - readinessInitialDelaySeconds: 2 -`, - want: ` -spec: - components: - pilot: - k8s: - env: - - name: PILOT_ENABLE_ISTIO_TAGS - value: "false" - - name: PILOT_ENABLE_LEGACY_ISTIO_MUTUAL_CREDENTIAL_NAME - value: "false" - - name: PROXY_XDS_DEBUG_VIA_AGENT - value: "false" - hpaSpec: - minReplicas: 1 - maxReplicas: 3 - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: istiod - metrics: - - resource: - name: cpu - target: - averageUtilization: 80 - type: Utilization - type: Resource - nodeSelector: - master: "true" - kubernetes.io/os: linux - replicaCount: 1 - resources: - requests: - cpu: 1000m - memory: 1G - strategy: - rollingUpdate: - maxSurge: 100% - maxUnavailable: 25% - tolerations: - - effect: NoSchedule - key: dedicated - operator: Exists - - key: CriticalAddonsOnly - operator: Exists - values: - global: - hub: docker.io/istio - tag: 1.2.3 - istioNamespace: dubbo-system - proxy: - readinessInitialDelaySeconds: 2 - pilot: - enabled: true - rollingMaxSurge: 100% - rollingMaxUnavailable: 25% - resources: - requests: - cpu: 1000m - memory: 1G - replicaCount: 1 - nodeSelector: - kubernetes.io/os: linux - tolerations: - - key: dedicated - operator: Exists - effect: NoSchedule - - key: CriticalAddonsOnly - operator: Exists - autoscaleEnabled: true - autoscaleMax: 3 - autoscaleMin: 1 - cpu: - targetAverageUtilization: 80 - traceSampling: 1.0 - image: pilot - env: - PILOT_ENABLE_ISTIO_TAGS: false - PILOT_ENABLE_LEGACY_ISTIO_MUTUAL_CREDENTIAL_NAME: false - PROXY_XDS_DEBUG_VIA_AGENT: false -`, - }, - { - desc: "pilot k8s setting with multiple env variables in values", - inIOPSpec: ` -spec: - components: - pilot: - k8s: - nodeSelector: - master: "true" - env: - - name: SPIFFE_BUNDLE_ENDPOINTS - value: "SPIFFE_BUNDLE_ENDPOINT" - values: - pilot: - enabled: true - rollingMaxSurge: 100% - rollingMaxUnavailable: 25% - resources: - requests: - cpu: 1000m - memory: 1G - replicaCount: 1 - nodeSelector: - kubernetes.io/os: linux - tolerations: - - key: dedicated - operator: Exists - effect: NoSchedule - - key: CriticalAddonsOnly - operator: Exists - autoscaleEnabled: true - autoscaleMax: 3 - autoscaleMin: 1 - cpu: - targetAverageUtilization: 80 - traceSampling: 1.0 - image: pilot - env: - PILOT_ENABLE_ISTIO_TAGS: false - PILOT_ENABLE_LEGACY_ISTIO_MUTUAL_CREDENTIAL_NAME: false - PROXY_XDS_DEBUG_VIA_AGENT: false - global: - hub: docker.io/istio - tag: 1.2.3 - istioNamespace: dubbo-system - proxy: - readinessInitialDelaySeconds: 2 -`, - want: ` -spec: - components: - pilot: - k8s: - env: - - name: SPIFFE_BUNDLE_ENDPOINTS - value: "SPIFFE_BUNDLE_ENDPOINT" - - name: PILOT_ENABLE_ISTIO_TAGS - value: "false" - - name: PILOT_ENABLE_LEGACY_ISTIO_MUTUAL_CREDENTIAL_NAME - value: "false" - - name: PROXY_XDS_DEBUG_VIA_AGENT - value: "false" - hpaSpec: - minReplicas: 1 - maxReplicas: 3 - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: istiod - metrics: - - resource: - name: cpu - target: - averageUtilization: 80 - type: Utilization - type: Resource - nodeSelector: - master: "true" - kubernetes.io/os: linux - replicaCount: 1 - resources: - requests: - cpu: 1000m - memory: 1G - strategy: - rollingUpdate: - maxSurge: 100% - maxUnavailable: 25% - tolerations: - - effect: NoSchedule - key: dedicated - operator: Exists - - key: CriticalAddonsOnly - operator: Exists - values: - global: - hub: docker.io/istio - tag: 1.2.3 - istioNamespace: dubbo-system - proxy: - readinessInitialDelaySeconds: 2 - pilot: - enabled: true - rollingMaxSurge: 100% - rollingMaxUnavailable: 25% - resources: - requests: - cpu: 1000m - memory: 1G - replicaCount: 1 - nodeSelector: - kubernetes.io/os: linux - tolerations: - - key: dedicated - operator: Exists - effect: NoSchedule - - key: CriticalAddonsOnly - operator: Exists - autoscaleEnabled: true - autoscaleMax: 3 - autoscaleMin: 1 - cpu: - targetAverageUtilization: 80 - traceSampling: 1.0 - image: pilot - env: - PILOT_ENABLE_ISTIO_TAGS: false - PILOT_ENABLE_LEGACY_ISTIO_MUTUAL_CREDENTIAL_NAME: false - PROXY_XDS_DEBUG_VIA_AGENT: false -`, - }, - { - desc: "ingressgateway k8s setting with values", - inIOPSpec: ` -spec: - components: - pilot: - enabled: false - ingressGateways: - - namespace: dubbo-system - name: istio-ingressgateway - enabled: true - k8s: - service: - externalTrafficPolicy: Local - serviceAnnotations: - manifest-generate: "testserviceAnnotation" - securityContext: - sysctls: - - name: "net.ipv4.ip_local_port_range" - value: "80 65535" - values: - gateways: - istio-ingressgateway: - serviceAnnotations: {} - nodeSelector: {} - tolerations: [] - global: - hub: docker.io/istio - tag: 1.2.3 - istioNamespace: dubbo-system -`, - want: ` -spec: - components: - ingressGateways: - - name: istio-ingressgateway - namespace: dubbo-system - enabled: true - k8s: - securityContext: - sysctls: - - name: net.ipv4.ip_local_port_range - value: "80 65535" - service: - externalTrafficPolicy: Local - serviceAnnotations: - manifest-generate: testserviceAnnotation - pilot: - enabled: false - values: - gateways: - istio-ingressgateway: - serviceAnnotations: {} - nodeSelector: {} - tolerations: [] - global: - hub: docker.io/istio - tag: 1.2.3 - istioNamespace: dubbo-system -`, - }, - { - desc: "pilot env k8s setting with non-empty hpa values", - inIOPSpec: ` -spec: - revision: canary - components: - pilot: - enabled: true - values: - pilot: - autoscaleMin: 1 - autoscaleMax: 3 - cpu: - targetAverageUtilization: 80 -`, - want: ` -spec: - revision: canary - components: - pilot: - enabled: true - k8s: - hpaSpec: - maxReplicas: 3 - minReplicas: 1 - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: istiod-canary - metrics: - - resource: - name: cpu - target: - averageUtilization: 80 - type: Utilization - type: Resource - values: - pilot: - autoscaleMin: 1 - autoscaleMax: 3 - cpu: - targetAverageUtilization: 80 -`, - }, - } - tr := NewReverseTranslator() - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - _, err := istio.UnmarshalIstioOperator(tt.inIOPSpec, false) - if err != nil { - t.Fatalf("unmarshal(%s): got error %s", tt.desc, err) - } - gotSpec, err := tr.TranslateK8SfromValueToIOP(tt.inIOPSpec) - if gotErr, wantErr := errToString(err), tt.wantErr; gotErr != wantErr { - t.Errorf("ValuesToK8s(%s)(%v): gotErr:%s, wantErr:%s", tt.desc, tt.inIOPSpec, gotErr, wantErr) - } - if tt.wantErr == "" { - if want := tt.want; !util.IsYAMLEqual(gotSpec, want) { - t.Errorf("ValuesToK8s(%s): got:\n%s\n\nwant:\n%s\nDiff:\n%s\n", tt.desc, gotSpec, want, util.YAMLDiff(gotSpec, want)) - } - } - }) - } -} - -// errToString returns the string representation of err and the empty string if -// err is nil. -func errToString(err error) string { - if err == nil { - return "" - } - return err.Error() -} diff --git a/operator/pkg/translate/yaml_tree.go b/operator/pkg/translate/yaml_tree.go deleted file mode 100644 index ce107628f..000000000 --- a/operator/pkg/translate/yaml_tree.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package translate - -import ( - "gopkg.in/yaml.v2" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/tpath" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -// YAMLTree takes an input tree inTreeStr, a partially constructed output tree outTreeStr, and a map of -// translations of source-path:dest-path in pkg/tpath format. It returns an output tree with paths from the input -// tree, translated and overlaid on the output tree. -func YAMLTree(inTreeStr, outTreeStr string, translations map[string]string) (string, error) { - inTree := make(map[string]interface{}) - if err := yaml.Unmarshal([]byte(inTreeStr), &inTree); err != nil { - return "", err - } - outTree := make(map[string]interface{}) - if err := yaml.Unmarshal([]byte(outTreeStr), &outTree); err != nil { - return "", err - } - - for inPath, translation := range translations { - path := util.PathFromString(inPath) - node, found, err := tpath.Find(inTree, path) - if err != nil { - return "", err - } - if !found { - continue - } - - if err := tpath.MergeNode(outTree, util.PathFromString(translation), node); err != nil { - return "", err - } - } - - outYAML, err := yaml.Marshal(outTree) - if err != nil { - return "", err - } - return string(outYAML), nil -} diff --git a/operator/pkg/util/clog/clog.go b/operator/pkg/util/clog/clog.go deleted file mode 100644 index 391d04845..000000000 --- a/operator/pkg/util/clog/clog.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package clog - -import ( - "fmt" - "io" - "os" -) - -import ( - "istio.io/pkg/log" -) - -// Logger provides optional log taps for console and test buffer outputs. -type Logger interface { - LogAndPrint(v ...interface{}) - LogAndError(v ...interface{}) - LogAndFatal(a ...interface{}) - LogAndPrintf(format string, a ...interface{}) - LogAndErrorf(format string, a ...interface{}) - LogAndFatalf(format string, a ...interface{}) - Print(s string) - PrintErr(s string) -} - -// ConsoleLogger is the struct used for mesh command -type ConsoleLogger struct { - stdOut io.Writer - stdErr io.Writer - scope *log.Scope -} - -// NewConsoleLogger creates a new logger and returns a pointer to it. -// stdOut and stdErr can be used to capture output for testing. If scope is nil, the default scope is used. -func NewConsoleLogger(stdOut, stdErr io.Writer, scope *log.Scope) *ConsoleLogger { - s := scope - if s == nil { - s = log.RegisterScope(log.DefaultScopeName, log.DefaultScopeName, 0) - } - return &ConsoleLogger{ - stdOut: stdOut, - stdErr: stdErr, - scope: s, - } -} - -// NewDefaultLogger creates a new logger that outputs to stdout/stderr at default scope. -func NewDefaultLogger() *ConsoleLogger { - return NewConsoleLogger(os.Stdout, os.Stderr, nil) -} - -func (l *ConsoleLogger) LogAndPrint(v ...interface{}) { - if len(v) == 0 { - return - } - s := fmt.Sprint(v...) - l.Print(s + "\n") - l.scope.Infof(s) -} - -func (l *ConsoleLogger) LogAndError(v ...interface{}) { - if len(v) == 0 { - return - } - s := fmt.Sprint(v...) - l.PrintErr(s + "\n") - l.scope.Infof(s) -} - -func (l *ConsoleLogger) LogAndFatal(a ...interface{}) { - l.LogAndError(a...) - os.Exit(-1) -} - -func (l *ConsoleLogger) LogAndPrintf(format string, a ...interface{}) { - s := fmt.Sprintf(format, a...) - l.Print(s + "\n") - l.scope.Infof(s) -} - -func (l *ConsoleLogger) LogAndErrorf(format string, a ...interface{}) { - s := fmt.Sprintf(format, a...) - l.PrintErr(s + "\n") - l.scope.Infof(s) -} - -func (l *ConsoleLogger) LogAndFatalf(format string, a ...interface{}) { - l.LogAndErrorf(format, a...) - os.Exit(-1) -} - -func (l *ConsoleLogger) Print(s string) { - _, _ = l.stdOut.Write([]byte(s)) -} - -func (l *ConsoleLogger) PrintErr(s string) { - _, _ = l.stdErr.Write([]byte(s)) -} diff --git a/operator/pkg/util/common.go b/operator/pkg/util/common.go deleted file mode 100644 index 6a30c9c2b..000000000 --- a/operator/pkg/util/common.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "fmt" - "net/url" - "strings" -) - -import ( - "istio.io/pkg/log" -) - -var scope = log.RegisterScope("util", "util", 0) - -// IsFilePath reports whether the given URL is a local file path. -func IsFilePath(path string) bool { - return strings.Contains(path, "/") || strings.Contains(path, ".") -} - -// IsHTTPURL checks whether the given URL is a HTTP URL. -func IsHTTPURL(path string) (bool, error) { - u, err := url.Parse(path) - valid := err == nil && u.Host != "" && (u.Scheme == "http" || u.Scheme == "https") - if strings.HasPrefix(path, "http") && !valid { - return false, fmt.Errorf("%s starts with http but is not a valid URL: %s", path, err) - } - return valid, nil -} diff --git a/operator/pkg/util/common_test.go b/operator/pkg/util/common_test.go deleted file mode 100644 index 079f8b1e4..000000000 --- a/operator/pkg/util/common_test.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "errors" - "testing" -) - -func TestIsFilePath(t *testing.T) { - tests := []struct { - desc string - in string - want bool - }{ - { - desc: "empty", - in: "", - want: false, - }, - { - desc: "no-markers", - in: "foobar", - want: false, - }, - { - desc: "with-slash", - in: "/home/bobby/go_rocks/main", - want: true, - }, - { - desc: "with-period", - in: "istio.go", - want: true, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got, want := IsFilePath(tt.in), tt.want; !(got == want) { - t.Errorf("%s: got %v, want: %v", tt.desc, got, want) - } - }) - } -} - -func TestIsHTTPURL(t *testing.T) { - tests := []struct { - desc string - in string - want bool - err error - }{ - { - desc: "empty", - in: "", - want: false, - err: nil, - }, - { - desc: "http-standard-url", - in: "http://localhost:3000", - want: true, - err: nil, - }, - { - desc: "https-standard-url", - in: "https://golang.org/", - want: true, - err: nil, - }, - { - desc: "ftp-url", - in: "ftp://gopher:gopwd@localhost:3000/go", - want: false, - err: nil, - }, - { - desc: "tcp-discovery-url", - in: "tcp://127.0.0.1:80", - want: false, - err: nil, - }, - { - desc: "empty-http", - in: "http://", - want: false, - err: errors.New(""), - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - got, err := IsHTTPURL(tt.in) - if want, wantErr := tt.want, tt.err; !(got == want) || ((err == nil && wantErr != nil) || (err != nil && wantErr == nil)) { - t.Errorf("%s: got :%v, wanted output: %v, got error: %v, wanted error: %v", tt.desc, got, want, err, wantErr) - } - }) - } -} diff --git a/operator/pkg/util/errs.go b/operator/pkg/util/errs.go deleted file mode 100644 index 4d7268315..000000000 --- a/operator/pkg/util/errs.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "fmt" -) - -const ( - defaultSeparator = ", " -) - -// Errors is a slice of error. -type Errors []error - -// Error implements the error#Error method. -func (e Errors) Error() string { - return ToString(e, defaultSeparator) -} - -// String implements the stringer#String method. -func (e Errors) String() string { - return e.Error() -} - -// ToError returns an error from Errors. -func (e Errors) ToError() error { - if len(e) == 0 { - return nil - } - return fmt.Errorf("%s", e) -} - -// Dedup removes any duplicated errors. -func (e Errors) Dedup() Errors { - logCountMap := make(map[string]int) - for _, ee := range e { - if ee == nil { - continue - } - item := ee.Error() - _, exist := logCountMap[item] - if exist { - logCountMap[item]++ - } else { - logCountMap[item] = 1 - } - } - var out Errors - for _, ee := range e { - item := ee.Error() - count := logCountMap[item] - if count == 0 { - continue - } - times := "" - if count > 1 { - times = fmt.Sprintf(" (repeated %d times)", count) - } - out = AppendErr(out, fmt.Errorf("%s%s", ee, times)) - // reset seen log count - logCountMap[item] = 0 - } - return out -} - -// NewErrs returns a slice of error with a single element err. -// If err is nil, returns nil. -func NewErrs(err error) Errors { - if err == nil { - return nil - } - return []error{err} -} - -// AppendErr appends err to errors if it is not nil and returns the result. -// If err is nil, it is not appended. -func AppendErr(errors []error, err error) Errors { - if err == nil { - if len(errors) == 0 { - return nil - } - return errors - } - return append(errors, err) -} - -// AppendErrs appends newErrs to errors and returns the result. -// If newErrs is empty, nothing is appended. -func AppendErrs(errors []error, newErrs []error) Errors { - if len(newErrs) == 0 { - return errors - } - for _, e := range newErrs { - errors = AppendErr(errors, e) - } - if len(errors) == 0 { - return nil - } - return errors -} - -// ToString returns a string representation of errors, with elements separated by separator string. Any nil errors in the -// slice are skipped. -func ToString(errors []error, separator string) string { - var out string - for i, e := range errors { - if e == nil { - continue - } - if i != 0 { - out += separator - } - out += e.Error() - } - return out -} - -// EqualErrors reports whether a and b are equal, regardless of ordering. -func EqualErrors(a, b Errors) bool { - if len(a) != len(b) { - return false - } - m := make(map[string]bool) - for _, e := range b { - m[e.Error()] = true - } - for _, ea := range a { - if !m[ea.Error()] { - return false - } - } - return true -} diff --git a/operator/pkg/util/errs_test.go b/operator/pkg/util/errs_test.go deleted file mode 100644 index 80d17cf12..000000000 --- a/operator/pkg/util/errs_test.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "fmt" - "testing" -) - -var ( - testErrs = Errors{fmt.Errorf("err1"), fmt.Errorf("err2")} - wantStr = "err1, err2" -) - -func TestError(t *testing.T) { - if got, want := testErrs.Error(), wantStr; got != want { - t.Errorf("got: %s, want: %s", got, want) - } -} - -func TestString(t *testing.T) { - if got, want := testErrs.String(), wantStr; got != want { - t.Errorf("got: %s, want: %s", got, want) - } -} - -func TestToString(t *testing.T) { - if got, want := ToString(testErrs, defaultSeparator), wantStr; got != want { - t.Errorf("got: %s, want: %s", got, want) - } -} - -func TestNewErrs(t *testing.T) { - errs := NewErrs(nil) - if errs != nil { - t.Errorf("got: %s, want: nil", errs) - } - - errs = NewErrs(fmt.Errorf("err1")) - if got, want := errs.String(), "err1"; got != want { - t.Errorf("got: %s, want: %s", got, want) - } -} - -func TestAppendErr(t *testing.T) { - var errs Errors - if got, want := errs.String(), ""; got != want { - t.Errorf("got: %s, want: %s", got, want) - } - - errs = AppendErr(errs, nil) - if got, want := errs.String(), ""; got != want { - t.Errorf("got: %s, want: %s", got, want) - } - - errs = AppendErr(errs, fmt.Errorf("err1")) - if got, want := errs.String(), "err1"; got != want { - t.Errorf("got: %s, want: %s", got, want) - } - - errs = AppendErr(errs, nil) - errs = AppendErr(errs, fmt.Errorf("err2")) - if got, want := errs.String(), "err1, err2"; got != want { - t.Errorf("got: %s, want: %s", got, want) - } -} - -func TestAppendErrs(t *testing.T) { - var errs Errors - - errs = AppendErrs(errs, []error{nil}) - if got, want := errs.String(), ""; got != want { - t.Errorf("got: %s, want: %s", got, want) - } - - errs = AppendErrs(errs, testErrs) - errs = AppendErrs(errs, []error{nil}) - if got, want := errs.String(), wantStr; got != want { - t.Errorf("got: %s, want: %s", got, want) - } -} - -func TestAppendErrsInFunction(t *testing.T) { - myAppendErrFunc := func() (errs Errors) { - errs = AppendErr(errs, fmt.Errorf("err1")) - errs = AppendErr(errs, fmt.Errorf("err2")) - return - } - if got, want := myAppendErrFunc().String(), wantStr; got != want { - t.Errorf("got: %s, want: %s", got, want) - } - - myAppendErrsFunc := func() (errs Errors) { - errs = AppendErrs(errs, testErrs) - return - } - if got, want := myAppendErrsFunc().String(), wantStr; got != want { - t.Errorf("got: %s, want: %s", got, want) - } - - myErrorSliceFunc := func() (errs []error) { - errs = AppendErrs(errs, testErrs) - return - } - - if got, want := Errors(myErrorSliceFunc()).String(), wantStr; got != want { - t.Errorf("got: %s, want: %s", got, want) - } -} diff --git a/operator/pkg/util/httpserver/httpserver.go b/operator/pkg/util/httpserver/httpserver.go deleted file mode 100644 index 6db33f2b4..000000000 --- a/operator/pkg/util/httpserver/httpserver.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package httpserver - -import ( - "net/http" - "net/http/httptest" - "os" - "path/filepath" -) - -// NewServer creates a new Server and returns a pointer to it. -func NewServer(root string) *Server { - srv := &Server{Root: root} - srv.start() - return srv -} - -// Server is an in-memory HTTP server for testing. -type Server struct { - Srv *httptest.Server - Root string -} - -// URL returns the root URL served by s. -func (s *Server) URL() string { - return s.Srv.URL -} - -// Close closes the server. -func (s *Server) Close() { - s.Srv.Close() -} - -func (s *Server) start() { - fd := http.FileServer(http.Dir(s.Root)) - http.Handle(s.Root+"/", fd) - s.Srv = httptest.NewServer(fd) -} - -func (s *Server) MoveFiles(origin string) ([]string, error) { - files, err := filepath.Glob(origin) - if err != nil { - return []string{}, err - } - tmpFiles := make([]string, len(files)) - for i, file := range files { - data, err := os.ReadFile(file) - if err != nil { - return []string{}, err - } - newName := filepath.Join(s.Root, filepath.Base(file)) - if err := os.WriteFile(newName, data, 0o755); err != nil { - return []string{}, err - } - tmpFiles[i] = newName - } - return tmpFiles, nil -} diff --git a/operator/pkg/util/k8s.go b/operator/pkg/util/k8s.go deleted file mode 100644 index 236612c75..000000000 --- a/operator/pkg/util/k8s.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "fmt" -) - -import ( - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/discovery" - "k8s.io/client-go/kubernetes" -) - -import ( - iopv1alpha1 "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -type JWTPolicy string - -const ( - FirstPartyJWT JWTPolicy = "first-party-jwt" - ThirdPartyJWT JWTPolicy = "third-party-jwt" -) - -// DetectSupportedJWTPolicy queries the api-server to detect whether it has TokenRequest support -func DetectSupportedJWTPolicy(client kubernetes.Interface) (JWTPolicy, error) { - _, s, err := client.Discovery().ServerGroupsAndResources() - // This may fail if any api service is down. We should only fail if the specific API we care about failed - if err != nil { - if discovery.IsGroupDiscoveryFailedError(err) { - derr := err.(*discovery.ErrGroupDiscoveryFailed) - if _, f := derr.Groups[schema.GroupVersion{Group: "authentication.k8s.io", Version: "v1"}]; f { - return "", err - } - } else { - return "", err - } - } - for _, res := range s { - for _, api := range res.APIResources { - // Appearance of this API indicates we do support third party jwt token - if api.Name == "serviceaccounts/token" { - return ThirdPartyJWT, nil - } - } - } - return FirstPartyJWT, nil -} - -// GKString differs from default representation of GroupKind -func GKString(gvk schema.GroupKind) string { - return fmt.Sprintf("%s/%s", gvk.Group, gvk.Kind) -} - -// ValidateIOPCAConfig validates if the IstioOperator CA configs are applicable to the K8s cluster -func ValidateIOPCAConfig(client kube.Client, iop *iopv1alpha1.IstioOperator) error { - globalI := iop.Spec.Values.AsMap()["global"] - global, ok := globalI.(map[string]interface{}) - if !ok { - // This means no explicit global configuration. Still okay - return nil - } - ca, ok := global["pilotCertProvider"].(string) - if !ok { - // This means the default pilotCertProvider is being used - return nil - } - if ca == "kubernetes" { - ver, err := client.GetKubernetesVersion() - if err != nil { - return fmt.Errorf("failed to determine support for K8s legacy signer. Use the --force flag to ignore this: %v", err) - } - - if kube.IsAtLeastVersion(client, 22) { - return fmt.Errorf("configuration PILOT_CERT_PROVIDER=%s not supported in Kubernetes %v."+ - "Please pick another value for PILOT_CERT_PROVIDER", ca, ver.String()) - } - } - return nil -} diff --git a/operator/pkg/util/k8s_test.go b/operator/pkg/util/k8s_test.go deleted file mode 100644 index f861cce7d..000000000 --- a/operator/pkg/util/k8s_test.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package util - -import ( - "testing" -) - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/version" - fakediscovery "k8s.io/client-go/discovery/fake" - "k8s.io/client-go/kubernetes/fake" - "sigs.k8s.io/yaml" -) - -import ( - pkgAPI "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -var ( - o1 = ` -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - values: - global: - pilotCertProvider: kubernetes -` - o2 = ` -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - values: - global: - pilotCertProvider: istiod -` - o3 = ` -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - values: -` -) - -func TestValidateIOPCAConfig(t *testing.T) { - var err error - - tests := []struct { - major string - minor string - expErr bool - operatorYaml string - }{ - { - major: "1", - minor: "16", - expErr: false, - operatorYaml: o1, - }, - { - major: "1", - minor: "22", - expErr: true, - operatorYaml: o1, - }, - { - major: "1", - minor: "23", - expErr: false, - operatorYaml: o2, - }, - { - major: "1", - minor: "24", - expErr: false, - operatorYaml: o3, - }, - } - - for i, tt := range tests { - k8sClient := kube.NewFakeClient() - k8sClient.Discovery().(*fakediscovery.FakeDiscovery).FakedServerVersion = &version.Info{ - Major: tt.major, - Minor: tt.minor, - } - op := &pkgAPI.IstioOperator{} - err = yaml.Unmarshal([]byte(tt.operatorYaml), op) - if err != nil { - t.Fatalf("Failure in test case %v. Error %s", i, err) - } - err = ValidateIOPCAConfig(k8sClient, op) - if !tt.expErr && err != nil { - t.Fatalf("Failure in test case %v. Expected No Error. Got %s", i, err) - } else if tt.expErr && err == nil { - t.Fatalf("Failure in test case %v. Expected Error. Got No error", i) - } - } -} - -func TestDetectSupportedJWTPolicy(t *testing.T) { - cli := fake.NewSimpleClientset() - cli.Resources = []*metav1.APIResourceList{} - t.Run("first-party-jwt", func(t *testing.T) { - res, err := DetectSupportedJWTPolicy(cli) - if err != nil { - t.Fatal(err) - } - if res != FirstPartyJWT { - t.Fatalf("unexpected jwt type, expected %s, got %s", FirstPartyJWT, res) - } - }) - cli.Resources = []*metav1.APIResourceList{ - { - APIResources: []metav1.APIResource{{Name: "serviceaccounts/token"}}, - }, - } - t.Run("third-party-jwt", func(t *testing.T) { - res, err := DetectSupportedJWTPolicy(cli) - if err != nil { - t.Fatal(err) - } - if res != ThirdPartyJWT { - t.Fatalf("unexpected jwt type, expected %s, got %s", ThirdPartyJWT, res) - } - }) -} diff --git a/operator/pkg/util/label.go b/operator/pkg/util/label.go deleted file mode 100644 index 307a5dc0e..000000000 --- a/operator/pkg/util/label.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/runtime" -) - -// SetLabel is a helper function which sets the specified label and value on the specified object. -func SetLabel(resource runtime.Object, label, value string) error { - resourceAccessor, err := meta.Accessor(resource) - if err != nil { - return err - } - labels := resourceAccessor.GetLabels() - if labels == nil { - labels = map[string]string{} - } - labels[label] = value - resourceAccessor.SetLabels(labels) - return nil -} diff --git a/operator/pkg/util/label_test.go b/operator/pkg/util/label_test.go deleted file mode 100644 index e8182b5ea..000000000 --- a/operator/pkg/util/label_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "testing" -) - -import ( - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" -) - -func TestSetLabel(t *testing.T) { - tests := []struct { - desc string - wantLabel string - wantValue string - wantErr error - }{ - { - desc: "AddMapLabelMapValue", - wantLabel: "foo", - wantValue: "bar", - wantErr: nil, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - resource := &unstructured.Unstructured{Object: make(map[string]interface{})} - gotErr := SetLabel(resource, tt.wantLabel, tt.wantValue) - resourceAccessor, _ := meta.Accessor(resource) - labels := resourceAccessor.GetLabels() - if gotVal, ok := labels[tt.wantLabel]; !ok || gotVal != tt.wantValue || gotErr != tt.wantErr { - t.Errorf("%s: ok: %v, got value: %v, want value: %v, got error: %v, want error: %v", tt.desc, ok, gotVal, tt.wantValue, gotErr, tt.wantErr) - } - }) - } -} diff --git a/operator/pkg/util/merge_iop.go b/operator/pkg/util/merge_iop.go deleted file mode 100644 index 8bf553fb6..000000000 --- a/operator/pkg/util/merge_iop.go +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "fmt" - "strings" -) - -import ( - "google.golang.org/protobuf/types/known/durationpb" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - v1alpha13 "istio.io/api/mesh/v1alpha1" - "istio.io/api/networking/v1alpha3" - "istio.io/api/operator/v1alpha1" - v11 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/strategicpatch" - yaml2 "sigs.k8s.io/yaml" -) - -import ( - v1alpha12 "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" -) - -// Partially mirrored from istio/api and operator/pkg/api (for values). -// Struct tags are required to use k8s strategic merge library. It would be possible -// to add these to source protos but because the values field is defined as -// map[string]interface{} here (and similar for MeshConfig in v1alpha1.Values) -// that alone would not be sufficient. -// Only non-scalar types require tags, therefore most fields are omitted here. -type iopMergeStructType struct { - v11.ObjectMeta `json:"metadata" patchStrategy:"merge"` - Spec istioOperatorSpec `json:"spec" patchStrategy:"merge"` -} - -type istioOperatorSpec struct { - MeshConfig *meshConfig `json:"meshConfig" patchStrategy:"merge"` - Components *istioComponentSetSpec `json:"components" patchStrategy:"merge"` - Values *values `json:"values" patchStrategy:"merge"` -} - -type istioComponentSetSpec struct { - Base *baseComponentSpec `json:"base" patchStrategy:"merge"` - Pilot *componentSpec `json:"pilot" patchStrategy:"merge"` - Cni *componentSpec `json:"cni" patchStrategy:"merge"` - IstiodRemote *componentSpec `json:"istiodRemote" patchStrategy:"merge"` - IngressGateways []*gatewaySpec `json:"ingressGateways" patchStrategy:"merge" patchMergeKey:"name"` - EgressGateways []*gatewaySpec `json:"egressGateways" patchStrategy:"merge" patchMergeKey:"name"` -} - -type baseComponentSpec struct { - K8S *v1alpha1.KubernetesResourcesSpec `json:"k8s" patchStrategy:"merge"` -} - -type componentSpec struct { - K8S *v1alpha1.KubernetesResourcesSpec `json:"k8s" patchStrategy:"merge"` -} - -type gatewaySpec struct { - Label map[string]string `json:"label" patchStrategy:"merge"` - K8S *v1alpha1.KubernetesResourcesSpec `json:"k8s" patchStrategy:"merge"` -} - -type values struct { - Cni *v1alpha12.CNIConfig `json:"cni" patchStrategy:"merge"` - Gateways *gatewaysConfig `json:"gateways" patchStrategy:"merge"` - Global *v1alpha12.GlobalConfig `json:"global" patchStrategy:"merge"` - Pilot *v1alpha12.PilotConfig `json:"pilot" patchStrategy:"merge"` - Telemetry *telemetryConfig `json:"telemetry" patchStrategy:"merge"` - SidecarInjectorWebhook *v1alpha12.SidecarInjectorConfig `json:"sidecarInjectorWebhook" patchStrategy:"merge"` - IstioCni *v1alpha12.CNIConfig `json:"istio_cni" patchStrategy:"merge"` - MeshConfig *meshConfig `json:"meshConfig" patchStrategy:"merge"` - Base *v1alpha12.BaseConfig `json:"base" patchStrategy:"merge"` - IstiodRemote *v1alpha12.IstiodRemoteConfig `json:"istiodRemote" patchStrategy:"merge"` -} - -type gatewaysConfig struct { - IstioEgressgateway *egressGatewayConfig `json:"istio-egressgateway" patchStrategy:"merge"` - IstioIngressgateway *ingressGatewayConfig `json:"istio-ingressgateway" patchStrategy:"merge"` -} - -// Configuration for an ingress gateway. -type ingressGatewayConfig struct { - Env map[string]interface{} `json:"env" patchStrategy:"merge"` - Labels map[string]string `json:"labels" patchStrategy:"merge"` - CPU *v1alpha12.CPUTargetUtilizationConfig `json:"cpu" patchStrategy:"replace"` - LoadBalancerSourceRanges []string `json:"loadBalancerSourceRanges" patchStrategy:"replace"` - NodeSelector map[string]interface{} `json:"nodeSelector" patchStrategy:"merge"` - PodAntiAffinityLabelSelector []map[string]interface{} `json:"podAntiAffinityLabelSelector" patchStrategy:"replace"` - PodAntiAffinityTermLabelSelector []map[string]interface{} `json:"podAntiAffinityTermLabelSelector" patchStrategy:"replace"` - PodAnnotations map[string]interface{} `json:"podAnnotations" patchStrategy:"merge"` - MeshExpansionPorts []*v1alpha12.PortsConfig `json:"meshExpansionPorts" patchStrategy:"merge" patchMergeKey:"name"` - Ports []*v1alpha12.PortsConfig `json:"ports" patchStrategy:"merge" patchMergeKey:"name"` - Resources *resources `json:"resources" patchStrategy:"merge"` - SecretVolumes []*v1alpha12.SecretVolume `json:"secretVolumes" patchStrategy:"merge" patchMergeKey:"name"` - ServiceAnnotations map[string]interface{} `json:"serviceAnnotations" patchStrategy:"merge"` - Tolerations []map[string]interface{} `json:"tolerations" patchStrategy:"replace"` - IngressPorts []map[string]interface{} `json:"ingressPorts" patchStrategy:"replace"` - AdditionalContainers []map[string]interface{} `json:"additionalContainers" patchStrategy:"replace"` - ConfigVolumes []map[string]interface{} `json:"configVolumes" patchStrategy:"replace"` - Zvpn *v1alpha12.IngressGatewayZvpnConfig `json:"zvpn" patchStrategy:"merge"` -} - -type resources struct { - Limits map[string]string `json:"limits" patchStrategy:"merge"` - Requests map[string]string `json:"requests" patchStrategy:"merge"` -} - -type egressGatewayConfig struct { - Env map[string]interface{} `json:"env" patchStrategy:"merge"` - Labels map[string]string `json:"labels" patchStrategy:"merge"` - NodeSelector map[string]interface{} `json:"nodeSelector" patchStrategy:"merge"` - PodAntiAffinityLabelSelector []map[string]interface{} `json:"podAntiAffinityLabelSelector" patchStrategy:"replace"` - PodAntiAffinityTermLabelSelector []map[string]interface{} `json:"podAntiAffinityTermLabelSelector" patchStrategy:"replace"` - PodAnnotations map[string]interface{} `json:"podAnnotations" patchStrategy:"merge"` - Ports []*v1alpha12.PortsConfig `json:"ports" patchStrategy:"merge" patchMergeKey:"name"` - Resources *resources `json:"resources" patchStrategy:"merge"` - SecretVolumes []*v1alpha12.SecretVolume `json:"secretVolumes" patchStrategy:"merge" patchMergeKey:"name"` - Tolerations []map[string]interface{} `json:"tolerations" patchStrategy:"replace"` - ConfigVolumes []map[string]interface{} `json:"configVolumes" patchStrategy:"replace"` - AdditionalContainers []map[string]interface{} `json:"additionalContainers" patchStrategy:"replace"` - Zvpn *v1alpha12.ZeroVPNConfig `json:"zvpn" patchStrategy:"replace"` -} - -type meshConfig struct { - ConnectTimeout *durationpb.Duration `json:"connectTimeout" patchStrategy:"replace"` - ProtocolDetectionTimeout *durationpb.Duration `json:"protocolDetectionTimeout" patchStrategy:"replace"` - RdsRefreshDelay *durationpb.Duration `json:"rdsRefreshDelay" patchStrategy:"replace"` - EnableAutoMtls *wrappers.BoolValue `json:"enableAutoMtls" patchStrategy:"replace"` - EnablePrometheusMerge *wrappers.BoolValue `json:"enablePrometheusMerge" patchStrategy:"replace"` - OutboundTrafficPolicy *v1alpha13.MeshConfig_OutboundTrafficPolicy `json:"outboundTrafficPolicy" patchStrategy:"merge"` - TCPKeepalive *v1alpha3.ConnectionPoolSettings_TCPSettings_TcpKeepalive `json:"tcpKeepalive" patchStrategy:"merge"` - DefaultConfig *proxyConfig `json:"defaultConfig" patchStrategy:"merge"` - ConfigSources []*v1alpha13.ConfigSource `json:"configSources" patchStrategy:"merge" patchMergeKey:"address"` - TrustDomainAliases []string `json:"trustDomainAliases" patchStrategy:"merge"` - DefaultServiceExportTo []string `json:"defaultServiceExportTo" patchStrategy:"merge"` - DefaultVirtualServiceExportTo []string `json:"defaultVirtualServiceExportTo" patchStrategy:"merge"` - DefaultDestinationRuleExportTo []string `json:"defaultDestinationRuleExportTo" patchStrategy:"merge"` - LocalityLbSetting *v1alpha3.LocalityLoadBalancerSetting `json:"localityLbSetting" patchStrategy:"merge"` - DNSRefreshRate *durationpb.Duration `json:"dnsRefreshRate" patchStrategy:"replace"` - Certificates []*v1alpha13.Certificate `json:"certificates" patchStrategy:"merge" patchMergeKey:"secretName"` - ThriftConfig *meshConfigThriftConfig `json:"thriftConfig" patchStrategy:"merge"` - ServiceSettings []*meshConfigServiceSettings `json:"serviceSettings" patchStrategy:"replace"` - DefaultProviders *meshConfigDefaultProviders `json:"defaultProviders" patchStrategy:"merge"` - ExtensionProviders []*meshConfigExtensionProvider `json:"extensionProviders" patchStrategy:"merge" patchMergeKey:"name"` -} - -type ( - meshConfigDefaultProviders struct { - AccessLogging []struct{} `json:"accessLogging"` - Tracing []struct{} `json:"tracing"` - Metrics []struct{} `json:"metrics"` - } - meshConfigExtensionProvider struct { - Name string `json:"string"` - EnvoyOtelAls struct{} `json:"envoyOtelAls"` - Prometheus struct{} `json:"prometheus"` - EnvoyFileAccessLog struct{} `json:"envoyFileAccessLog"` - Stackdriver struct{} `json:"stackdriver"` - EnvoyExtAuthzHTTP struct{} `json:"envoyExtAuthzHttp"` - EnvoyExtAuthzGrpc struct{} `json:"envoyExtAuthzGrpc"` - Zipkin struct{} `json:"zipkin"` - Lightstep struct{} `json:"lightstep"` - Datadog struct{} `json:"datadog"` - Opencensus struct{} `json:"opencensus"` - Skywalking struct{} `json:"skywalking"` - EnvoyHTTPAls struct{} `json:"envoyHttpAls"` - EnvoyTCPAls struct{} `json:"envoyTcpAls"` - } - clusterName struct { - ServiceCluster *v1alpha13.ProxyConfig_ServiceCluster `json:"serviceCluster,omitempty"` - TracingServiceName *v1alpha13.ProxyConfig_TracingServiceName_ `json:"tracingServiceName,omitempty"` - } -) - -type meshConfigThriftConfig struct { - RateLimitTimeout *durationpb.Duration `json:"rateLimitTimeout" patchStrategy:"replace"` -} - -type proxyConfig struct { - DrainDuration *durationpb.Duration `json:"drainDuration" patchStrategy:"replace"` - ParentShutdownDuration *durationpb.Duration `json:"parentShutdownDuration" patchStrategy:"replace"` - DiscoveryRefreshDelay *durationpb.Duration `json:"discoveryRefreshDelay" patchStrategy:"replace"` - TerminationDrainDuration *durationpb.Duration `json:"terminationDrainDuration" patchStrategy:"replace"` - Concurrency *wrappers.Int32Value `json:"concurrency" patchStrategy:"replace"` - ConfigSources []*v1alpha13.ConfigSource `json:"configSources" patchStrategy:"replace"` - ClusterName *clusterName `json:"clusterName" patchStrategy:"replace"` - TrustDomainAliases []string `json:"trustDomainAliases" patchStrategy:"replace"` - DefaultServiceExportTo []string `json:"defaultServiceExportTo" patchStrategy:"replace"` - DefaultVirtualServiceExportTo []string `json:"defaultVirtualServiceExportTo" patchStrategy:"replace"` - DefaultDestinationRuleExportTo []string `json:"defaultDestinationRuleExportTo" patchStrategy:"replace"` - LocalityLbSetting *v1alpha3.LocalityLoadBalancerSetting `json:"localityLbSetting" patchStrategy:"merge"` - DNSRefreshRate *durationpb.Duration `json:"dnsRefreshRate" patchStrategy:"replace"` - Certificates []*v1alpha13.Certificate `json:"certificates" patchStrategy:"replace"` - ThriftConfig *v1alpha13.MeshConfig_ThriftConfig `json:"thriftConfig" patchStrategy:"merge"` - ServiceSettings []*v1alpha13.MeshConfig_ServiceSettings `json:"serviceSettings" patchStrategy:"replace"` - Tracing *tracing `json:"tracing" patchStrategy:"replace"` - Sds *v1alpha13.SDS `json:"sds" patchStrategy:"replace"` - EnvoyAccessLogService *v1alpha13.RemoteService `json:"envoyAccessLogService" patchStrategy:"merge" patchMergeKey:"address"` - EnvoyMetricsService *v1alpha13.RemoteService `json:"envoyMetricsService" patchStrategy:"merge" patchMergeKey:"address"` - ProxyMetadata map[string]string `json:"proxyMetadata" patchStrategy:"merge"` - ExtraStatTags []string `json:"extraStatTags" patchStrategy:"replace"` - GatewayTopology *v1alpha13.Topology `json:"gatewayTopology" patchStrategy:"replace"` -} - -type tracing struct { - TlSSettings *v1alpha3.ClientTLSSettings `json:"tlsSettings" patchStrategy:"merge"` -} - -type meshConfigServiceSettings struct { - Settings *v1alpha13.MeshConfig_ServiceSettings_Settings `json:"settings" patchStrategy:"merge"` - Hosts []string `json:"hosts" patchStrategy:"merge"` -} - -type telemetryConfig struct { - V2 *telemetryV2Config `json:"v2" patchStrategy:"merge"` -} - -type telemetryV2Config struct { - MetadataExchange *v1alpha12.TelemetryV2MetadataExchangeConfig `json:"metadataExchange" patchStrategy:"merge"` - Prometheus *v1alpha12.TelemetryV2PrometheusConfig `json:"prometheus" patchStrategy:"merge"` - Stackdriver *v1alpha12.TelemetryV2StackDriverConfig `json:"stackdriver" patchStrategy:"merge"` - AccessLogPolicy *v1alpha12.TelemetryV2AccessLogPolicyFilterConfig `json:"accessLogPolicy" patchStrategy:"merge"` -} - -var iopMergeStruct iopMergeStructType - -// OverlayIOP overlays over base using JSON strategic merge. -func OverlayIOP(base, overlay string) (string, error) { - if strings.TrimSpace(base) == "" { - return overlay, nil - } - if strings.TrimSpace(overlay) == "" { - return base, nil - } - bj, err := yaml2.YAMLToJSON([]byte(base)) - if err != nil { - return "", fmt.Errorf("yamlToJSON error in base: %s\n%s", err, bj) - } - oj, err := yaml2.YAMLToJSON([]byte(overlay)) - if err != nil { - return "", fmt.Errorf("yamlToJSON error in overlay: %s\n%s", err, oj) - } - if base == "" { - bj = []byte("{}") - } - if overlay == "" { - oj = []byte("{}") - } - - merged, err := strategicpatch.StrategicMergePatch(bj, oj, &iopMergeStruct) - if err != nil { - return "", fmt.Errorf("json merge error (%s) for base object: \n%s\n override object: \n%s", err, bj, oj) - } - - my, err := yaml2.JSONToYAML(merged) - if err != nil { - return "", fmt.Errorf("jsonToYAML error (%s) for merged object: \n%s", err, merged) - } - - return string(my), nil -} diff --git a/operator/pkg/util/merge_iop_test.go b/operator/pkg/util/merge_iop_test.go deleted file mode 100644 index c5b44d434..000000000 --- a/operator/pkg/util/merge_iop_test.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "os" - "path/filepath" - "testing" -) - -import ( - meshconfig "istio.io/api/mesh/v1alpha1" - v1alpha12 "istio.io/api/operator/v1alpha1" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -func TestOverlayIOP(t *testing.T) { - cases := []struct { - path string - }{ - { - filepath.Join(env.IstioSrc, "manifests/profiles/default.yaml"), - }, - { - filepath.Join(env.IstioSrc, "manifests/profiles/demo.yaml"), - }, - { - filepath.Join("testdata", "overlay-iop.yaml"), - }, - } - - for _, tc := range cases { - t.Run(tc.path, func(t *testing.T) { - b, err := os.ReadFile(tc.path) - if err != nil { - t.Fatal(err) - } - // overlaying tree over itself exercises all paths for merging - if _, err := OverlayIOP(string(b), string(b)); err != nil { - t.Fatal(err) - } - }) - } -} - -// TestOverlayIOPExhaustiveness exhaustiveness check of `OverlayIOP` -// Once some one add a new `Provider` in api, we should update `wellknownProviders` and -// add to `meshConfigExtensionProvider` -func TestOverlayIOPExhaustiveness(t *testing.T) { - wellknownProviders := map[string]struct{}{ - "prometheus": {}, - "envoy_file_access_log": {}, - "stackdriver": {}, - "envoy_otel_als": {}, - "envoy_ext_authz_http": {}, - "envoy_ext_authz_grpc": {}, - "zipkin": {}, - "lightstep": {}, - "datadog": {}, - "opencensus": {}, - "skywalking": {}, - "envoy_http_als": {}, - "envoy_tcp_als": {}, - } - - unexpectedProviders := make([]string, 0) - - msg := &meshconfig.MeshConfig_ExtensionProvider{} - pb := msg.ProtoReflect() - md := pb.Descriptor() - - of := md.Oneofs().Get(0) - for i := 0; i < of.Fields().Len(); i++ { - o := of.Fields().Get(i) - n := string(o.Name()) - if _, ok := wellknownProviders[n]; ok { - delete(wellknownProviders, n) - } else { - unexpectedProviders = append(unexpectedProviders, n) - } - } - - if len(wellknownProviders) != 0 || len(unexpectedProviders) != 0 { - t.Errorf("unexpected provider not implemented in OverlayIOP, wellknownProviders: %v unexpectedProviders: %v", wellknownProviders, unexpectedProviders) - t.Fail() - } -} - -func TestOverlayIOPDefaultMeshConfig(t *testing.T) { - // Transform default mesh config into map[string]interface{} for inclusion in IstioOperator. - m := mesh.DefaultMeshConfig() - my, err := protomarshal.ToJSONMap(m) - if err != nil { - t.Fatal(err) - } - - iop := &v1alpha1.IstioOperator{ - Spec: &v1alpha12.IstioOperatorSpec{ - MeshConfig: MustStruct(my), - }, - } - - iy, err := yaml.Marshal(iop) - if err != nil { - t.Fatal(err) - } - - // overlaying tree over itself exercises all paths for merging - if _, err := OverlayIOP(string(iy), string(iy)); err != nil { - t.Fatal(err) - } -} - -func TestOverlayIOPIngressGatewayLabel(t *testing.T) { - l1, err := os.ReadFile("testdata/yaml/input/yaml_layer1.yaml") - if err != nil { - t.Fatal(err) - } - l2, err := os.ReadFile("testdata/yaml/input/yaml_layer2.yaml") - if err != nil { - t.Fatal(err) - } - - if _, err := OverlayIOP(string(l1), string(l2)); err != nil { - t.Fatal(err) - } -} diff --git a/operator/pkg/util/path.go b/operator/pkg/util/path.go deleted file mode 100644 index 502e089b6..000000000 --- a/operator/pkg/util/path.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "fmt" - "path/filepath" - "regexp" - "strconv" - "strings" -) - -const ( - // PathSeparator is the separator between path elements. - PathSeparator = "." - // KVSeparator is the separator between the key and value in a key/value path element, - KVSeparator = string(kvSeparatorRune) - kvSeparatorRune = ':' - - // InsertIndex is the index that means "insert" when setting values - InsertIndex = -1 - - // PathSeparatorRune is the separator between path elements, as a rune. - pathSeparatorRune = '.' - // EscapedPathSeparator is what to use when the path shouldn't separate - EscapedPathSeparator = "\\" + PathSeparator -) - -// ValidKeyRegex is a regex for a valid path key element. -var ValidKeyRegex = regexp.MustCompile("^[a-zA-Z0-9_-]*$") - -// Path is a path in slice form. -type Path []string - -// PathFromString converts a string path of form a.b.c to a string slice representation. -func PathFromString(path string) Path { - path = filepath.Clean(path) - path = strings.TrimPrefix(path, PathSeparator) - path = strings.TrimSuffix(path, PathSeparator) - pv := splitEscaped(path, pathSeparatorRune) - var r []string - for _, str := range pv { - if str != "" { - str = strings.ReplaceAll(str, EscapedPathSeparator, PathSeparator) - // Is str of the form node[expr], convert to "node", "[expr]"? - nBracket := strings.IndexRune(str, '[') - if nBracket > 0 { - r = append(r, str[:nBracket], str[nBracket:]) - } else { - // str is "[expr]" or "node" - r = append(r, str) - } - } - } - return r -} - -// String converts a string slice path representation of form ["a", "b", "c"] to a string representation like "a.b.c". -func (p Path) String() string { - return strings.Join(p, PathSeparator) -} - -func (p Path) Equals(p2 Path) bool { - if len(p) != len(p2) { - return false - } - for i, pp := range p { - if pp != p2[i] { - return false - } - } - return true -} - -// ToYAMLPath converts a path string to path such that the first letter of each path element is lower case. -func ToYAMLPath(path string) Path { - p := PathFromString(path) - for i := range p { - p[i] = firstCharToLowerCase(p[i]) - } - return p -} - -// ToYAMLPathString converts a path string such that the first letter of each path element is lower case. -func ToYAMLPathString(path string) string { - return ToYAMLPath(path).String() -} - -// IsValidPathElement reports whether pe is a valid path element. -func IsValidPathElement(pe string) bool { - return ValidKeyRegex.MatchString(pe) -} - -// IsKVPathElement report whether pe is a key/value path element. -func IsKVPathElement(pe string) bool { - pe, ok := RemoveBrackets(pe) - if !ok { - return false - } - - kv := splitEscaped(pe, kvSeparatorRune) - if len(kv) != 2 || len(kv[0]) == 0 || len(kv[1]) == 0 { - return false - } - return IsValidPathElement(kv[0]) -} - -// IsVPathElement report whether pe is a value path element. -func IsVPathElement(pe string) bool { - pe, ok := RemoveBrackets(pe) - if !ok { - return false - } - - return len(pe) > 1 && pe[0] == ':' -} - -// IsNPathElement report whether pe is an index path element. -func IsNPathElement(pe string) bool { - pe, ok := RemoveBrackets(pe) - if !ok { - return false - } - - n, err := strconv.Atoi(pe) - return err == nil && n >= InsertIndex -} - -// PathKV returns the key and value string parts of the entire key/value path element. -// It returns an error if pe is not a key/value path element. -func PathKV(pe string) (k, v string, err error) { - if !IsKVPathElement(pe) { - return "", "", fmt.Errorf("%s is not a valid key:value path element", pe) - } - pe, _ = RemoveBrackets(pe) - kv := splitEscaped(pe, kvSeparatorRune) - return kv[0], kv[1], nil -} - -// PathV returns the value string part of the entire value path element. -// It returns an error if pe is not a value path element. -func PathV(pe string) (string, error) { - // For :val, return the value only - if IsVPathElement(pe) { - v, _ := RemoveBrackets(pe) - return v[1:], nil - } - - // For key:val, return the whole thing - v, _ := RemoveBrackets(pe) - if len(v) > 0 { - return v, nil - } - return "", fmt.Errorf("%s is not a valid value path element", pe) -} - -// PathN returns the index part of the entire value path element. -// It returns an error if pe is not an index path element. -func PathN(pe string) (int, error) { - if !IsNPathElement(pe) { - return -1, fmt.Errorf("%s is not a valid index path element", pe) - } - v, _ := RemoveBrackets(pe) - return strconv.Atoi(v) -} - -// RemoveBrackets removes the [] around pe and returns the resulting string. It returns false if pe is not surrounded -// by []. -func RemoveBrackets(pe string) (string, bool) { - if !strings.HasPrefix(pe, "[") || !strings.HasSuffix(pe, "]") { - return "", false - } - return pe[1 : len(pe)-1], true -} - -// splitEscaped splits a string using the rune r as a separator. It does not split on r if it's prefixed by \. -func splitEscaped(s string, r rune) []string { - var prev rune - if len(s) == 0 { - return []string{} - } - prevIdx := 0 - var out []string - for i, c := range s { - if c == r && (i == 0 || (i > 0 && prev != '\\')) { - out = append(out, s[prevIdx:i]) - prevIdx = i + 1 - } - prev = c - } - out = append(out, s[prevIdx:]) - return out -} - -func firstCharToLowerCase(s string) string { - return strings.ToLower(s[0:1]) + s[1:] -} diff --git a/operator/pkg/util/path_test.go b/operator/pkg/util/path_test.go deleted file mode 100644 index 79f516850..000000000 --- a/operator/pkg/util/path_test.go +++ /dev/null @@ -1,383 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "errors" - "testing" -) - -func TestSplitEscaped(t *testing.T) { - tests := []struct { - desc string - in string - want []string - }{ - { - desc: "empty", - in: "", - want: []string{}, - }, - { - desc: "no match", - in: "foo", - want: []string{"foo"}, - }, - { - desc: "first", - in: ":foo", - want: []string{"", "foo"}, - }, - { - desc: "last", - in: "foo:", - want: []string{"foo", ""}, - }, - { - desc: "multiple", - in: "foo:bar:baz", - want: []string{"foo", "bar", "baz"}, - }, - { - desc: "multiple with escapes", - in: `foo\:bar:baz\:qux`, - want: []string{`foo\:bar`, `baz\:qux`}, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got, want := splitEscaped(tt.in, kvSeparatorRune), tt.want; !stringSlicesEqual(got, want) { - t.Errorf("%s: got:%v, want:%v", tt.desc, got, want) - } - }) - } -} - -func TestIsNPathElement(t *testing.T) { - tests := []struct { - desc string - in string - expect bool - }{ - { - desc: "empty", - in: "", - expect: false, - }, - { - desc: "negative", - in: "[-45]", - expect: false, - }, - { - desc: "negative-1", - in: "[-1]", - expect: true, - }, - { - desc: "valid", - in: "[0]", - expect: true, - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got := IsNPathElement(tt.in); got != tt.expect { - t.Errorf("%s: expect %v got %v", tt.desc, tt.expect, got) - } - }) - } -} - -func stringSlicesEqual(a, b []string) bool { - if len(a) != len(b) { - return false - } - for i, aa := range a { - if aa != b[i] { - return false - } - } - return true -} - -func TestPathFromString(t *testing.T) { - tests := []struct { - desc string - in string - expect Path - }{ - { - desc: "no-path", - in: "", - expect: Path{}, - }, - { - desc: "valid-path", - in: "a.b.c", - expect: Path{"a", "b", "c"}, - }, - { - desc: "surround-periods", - in: ".a.", - expect: Path{"a"}, - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got := PathFromString(tt.in); !got.Equals(tt.expect) { - t.Errorf("%s: expect %v got %v", tt.desc, tt.expect, got) - } - }) - } -} - -func TestToYAMLPath(t *testing.T) { - tests := []struct { - desc string - in string - expect Path - }{ - { - desc: "all-uppercase", - in: "A.B.C.D", - expect: Path{"a", "b", "c", "d"}, - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got := ToYAMLPath(tt.in); !got.Equals(tt.expect) { - t.Errorf("%s: expect %v got %v", tt.desc, tt.expect, got) - } - }) - } -} - -func TestIsKVPathElement(t *testing.T) { - tests := []struct { - desc string - in string - expect bool - }{ - { - desc: "valid", - in: "[1:2]", - expect: true, - }, - { - desc: "invalid", - in: "[:2]", - expect: false, - }, - { - desc: "invalid-2", - in: "[1:]", - expect: false, - }, - { - desc: "empty", - in: "", - expect: false, - }, - { - desc: "no-brackets", - in: "1:2", - expect: false, - }, - { - desc: "one-bracket", - in: "[1:2", - expect: false, - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got := IsKVPathElement(tt.in); got != tt.expect { - t.Errorf("%s: expect %v got %v", tt.desc, tt.expect, got) - } - }) - } -} - -func TestIsVPathElement(t *testing.T) { - tests := []struct { - desc string - in string - expect bool - }{ - { - desc: "valid", - in: "[:1]", - expect: true, - }, - { - desc: "kv-path-elem", - in: "[1:2]", - expect: false, - }, - { - desc: "invalid", - in: "1:2", - expect: false, - }, - { - desc: "empty", - in: "", - expect: false, - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got := IsVPathElement(tt.in); got != tt.expect { - t.Errorf("%s: expect %v got %v", tt.desc, tt.expect, got) - } - }) - } -} - -func TestPathKV(t *testing.T) { - tests := []struct { - desc string - in string - wantK string - wantV string - wantErr error - }{ - { - desc: "valid", - in: "[1:2]", - wantK: "1", - wantV: "2", - wantErr: nil, - }, - { - desc: "invalid", - in: "[1:", - wantErr: errors.New(""), - }, - { - desc: "empty", - in: "", - wantErr: errors.New(""), - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if k, v, err := PathKV(tt.in); k != tt.wantK || v != tt.wantV || errNilCheck(err, tt.wantErr) { - t.Errorf("%s: expect %v %v %v got %v %v %v", tt.desc, tt.wantK, tt.wantV, tt.wantErr, k, v, err) - } - }) - } -} - -func TestPathV(t *testing.T) { - tests := []struct { - desc string - in string - want string - err error - }{ - { - desc: "valid-kv", - in: "[1:2]", - want: "1:2", - err: nil, - }, - { - desc: "valid-v", - in: "[:1]", - want: "1", - err: nil, - }, - { - desc: "invalid", - in: "083fj", - want: "", - err: errors.New(""), - }, - { - desc: "empty", - in: "", - want: "", - err: errors.New(""), - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got, err := PathV(tt.in); got != tt.want || errNilCheck(err, tt.err) { - t.Errorf("%s: expect %v %v got %v %v", tt.desc, tt.want, tt.err, got, err) - } - }) - } -} - -func TestRemoveBrackets(t *testing.T) { - tests := []struct { - desc string - in string - expect string - expectStat bool - }{ - { - desc: "has-brackets", - in: "[yo]", - expect: "yo", - expectStat: true, - }, - { - desc: "one-bracket", - in: "[yo", - expect: "", - expectStat: false, - }, - { - desc: "other-bracket", - in: "yo]", - expect: "", - expectStat: false, - }, - { - desc: "no-brackets", - in: "yo", - expect: "", - expectStat: false, - }, - { - desc: "empty", - in: "", - expect: "", - expectStat: false, - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got, stat := RemoveBrackets(tt.in); got != tt.expect || stat != tt.expectStat { - t.Errorf("%s: expect %v %v got %v %v", tt.desc, tt.expect, tt.expectStat, got, stat) - } - }) - } -} - -func errNilCheck(err1, err2 error) bool { - return (err1 == nil && err2 != nil) || (err1 != nil && err2 == nil) -} diff --git a/operator/pkg/util/progress/progress.go b/operator/pkg/util/progress/progress.go deleted file mode 100644 index 5c7839ab8..000000000 --- a/operator/pkg/util/progress/progress.go +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package progress - -import ( - "fmt" - "io" - "sort" - "strings" - "sync" -) - -import ( - "github.com/cheggaaa/pb/v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" -) - -type InstallState int - -const ( - StateInstalling InstallState = iota - StatePruning - StateComplete - StateUninstallComplete -) - -// Log records the progress of an installation -// This aims to provide information about the install of multiple components in parallel, while working -// around the limitations of the pb library, which will only support single lines. To do this, we aggregate -// the current components into a single line, and as components complete there final state is persisted to a new line. -type Log struct { - components map[string]*ManifestLog - bar *pb.ProgressBar - template string - mu sync.Mutex - state InstallState -} - -func NewLog() *Log { - return &Log{ - components: map[string]*ManifestLog{}, - bar: createBar(), - } -} - -const inProgress = `{{ yellow (cycle . "-" "-" "-" " ") }} ` - -// createStatus will return a string to report the current status. -// ex: - Processing resources for components. Waiting for foo, bar -func (p *Log) createStatus(maxWidth int) string { - comps := make([]string, 0, len(p.components)) - wait := make([]string, 0, len(p.components)) - for c, l := range p.components { - comps = append(comps, name.UserFacingComponentName(name.ComponentName(c))) - wait = append(wait, l.waitingResources()...) - } - sort.Strings(comps) - sort.Strings(wait) - msg := fmt.Sprintf(`Processing resources for %s.`, strings.Join(comps, ", ")) - if len(wait) > 0 { - msg += fmt.Sprintf(` Waiting for %s`, strings.Join(wait, ", ")) - } - prefix := inProgress - if !p.bar.GetBool(pb.Terminal) { - // If we aren't a terminal, no need to spam extra lines - prefix = `{{ yellow "-" }} ` - } - // reduce by 2 to allow for the "- " that will be added below - maxWidth -= 2 - if maxWidth > 0 && len(msg) > maxWidth { - return prefix + msg[:maxWidth-3] + "..." - } - // cycle will alternate between "-" and " ". "-" is given multiple times to avoid quick flashing back and forth - return prefix + msg -} - -// For testing only -var testWriter *io.Writer - -func createBar() *pb.ProgressBar { - // Don't set a total and use Static so we can explicitly control when you write. This is needed - // for handling the multiline issues. - bar := pb.New(0) - bar.Set(pb.Static, true) - if testWriter != nil { - bar.SetWriter(*testWriter) - } - bar.Start() - // if we aren't a terminal, we will return a new line for each new message - if !bar.GetBool(pb.Terminal) { - bar.Set(pb.ReturnSymbol, "\n") - } - return bar -} - -// reportProgress will report an update for a given component -// Because the bar library does not support multiple lines/bars at once, we need to aggregate current -// progress into a single line. For example "Waiting for x, y, z". Once a component completes, we want -// a new line created so the information is not lost. To do this, we spin up a new bar with the remaining components -// on a new line, and create a new bar. For example, this becomes "x succeeded", "waiting for y, z". -func (p *Log) reportProgress(component string) func() { - return func() { - cliName := name.UserFacingComponentName(name.ComponentName(component)) - p.mu.Lock() - defer p.mu.Unlock() - cmp := p.components[component] - // The component has completed - cmp.mu.Lock() - finished := cmp.finished - cmpErr := cmp.err - cmp.mu.Unlock() - if finished || cmpErr != "" { - if finished { - p.SetMessage(fmt.Sprintf(`{{ green "✔" }} %s installed`, cliName), true) - } else { - p.SetMessage(fmt.Sprintf(`{{ red "✘" }} %s encountered an error: %s`, cliName, cmpErr), true) - } - // Close the bar out, outputting a new line - delete(p.components, component) - - // Now we create a new bar, which will have the remaining components - p.bar = createBar() - return - } - p.SetMessage(p.createStatus(p.bar.Width()), false) - } -} - -func (p *Log) SetState(state InstallState) { - p.mu.Lock() - defer p.mu.Unlock() - p.state = state - switch p.state { - case StatePruning: - p.bar.SetTemplateString(inProgress + `Pruning removed resources`) - p.bar.Write() - case StateComplete: - p.bar.SetTemplateString(`{{ green "✔" }} Installation complete`) - p.bar.Write() - case StateUninstallComplete: - p.bar.SetTemplateString(`{{ green "✔" }} Uninstall complete`) - p.bar.Write() - } -} - -func (p *Log) NewComponent(component string) *ManifestLog { - ml := &ManifestLog{ - report: p.reportProgress(component), - } - p.mu.Lock() - defer p.mu.Unlock() - p.components[component] = ml - return ml -} - -func (p *Log) SetMessage(status string, finish bool) { - // if we are not a terminal and there is no change, do not write - // This avoids redundant lines - if !p.bar.GetBool(pb.Terminal) && status == p.template { - return - } - p.template = status - p.bar.SetTemplateString(p.template) - if finish { - p.bar.Finish() - } - p.bar.Write() -} - -// ManifestLog records progress for a single component -type ManifestLog struct { - report func() - err string - finished bool - waiting []string - mu sync.Mutex -} - -func (p *ManifestLog) ReportProgress() { - if p == nil { - return - } - p.report() -} - -func (p *ManifestLog) ReportError(err string) { - if p == nil { - return - } - p.mu.Lock() - p.err = err - p.mu.Unlock() - p.report() -} - -func (p *ManifestLog) ReportFinished() { - if p == nil { - return - } - p.mu.Lock() - p.finished = true - p.mu.Unlock() - p.report() -} - -func (p *ManifestLog) ReportWaiting(resources []string) { - if p == nil { - return - } - p.mu.Lock() - p.waiting = resources - p.mu.Unlock() - p.report() -} - -func (p *ManifestLog) waitingResources() []string { - p.mu.Lock() - defer p.mu.Unlock() - return p.waiting -} diff --git a/operator/pkg/util/progress/progress_test.go b/operator/pkg/util/progress/progress_test.go deleted file mode 100644 index 3d7759a22..000000000 --- a/operator/pkg/util/progress/progress_test.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package progress - -import ( - "bytes" - "io" - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" -) - -func TestProgressLog(t *testing.T) { - buf := bytes.NewBuffer(nil) - testBuf := io.Writer(buf) - testWriter = &testBuf - expected := "" - expect := func(e string) { - t.Helper() - // In buffer mode we don't overwrite old data, so we are constantly appending to the expected - newExpected := expected + "\n" + e - if newExpected != buf.String() { - t.Fatalf("expected '%v', \ngot '%v'", newExpected, buf.String()) - } - expected = newExpected - } - - p := NewLog() - cnp := name.PilotComponentName - cnpo := name.UserFacingComponentName(cnp) - cnb := name.IstioBaseComponentName - cnbo := name.UserFacingComponentName(cnb) - foo := p.NewComponent(string(cnp)) - foo.ReportProgress() - expect(`- Processing resources for ` + cnpo + `.`) - - bar := p.NewComponent(string(cnb)) - bar.ReportProgress() - // string buffer won't rewrite, so we append - expect(`- Processing resources for ` + cnbo + `, ` + cnpo + `.`) - bar.ReportProgress() - bar.ReportProgress() - - bar.ReportWaiting([]string{"deployment"}) - expect(`- Processing resources for ` + cnbo + `, ` + cnpo + `. Waiting for deployment`) - - bar.ReportError("some error") - expect(`✘ ` + cnbo + ` encountered an error: some error`) - - foo.ReportProgress() - expect(`- Processing resources for ` + cnpo + `.`) - - foo.ReportFinished() - expect(`✔ ` + cnpo + ` installed`) - - p.SetState(StatePruning) - expect(`- Pruning removed resources`) - - p.SetState(StateComplete) - expect(`✔ Installation complete`) - - p.SetState(StateUninstallComplete) - expect(`✔ Uninstall complete`) -} diff --git a/operator/pkg/util/reflect.go b/operator/pkg/util/reflect.go deleted file mode 100644 index 2c8851ea7..000000000 --- a/operator/pkg/util/reflect.go +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "fmt" - "reflect" -) - -// kindOf returns the reflection Kind that represents the dynamic type of value. -// If value is a nil interface value, kindOf returns reflect.Invalid. -func kindOf(value interface{}) reflect.Kind { - if value == nil { - return reflect.Invalid - } - return reflect.TypeOf(value).Kind() -} - -// IsString reports whether value is a string type. -func IsString(value interface{}) bool { - return kindOf(value) == reflect.String -} - -// IsPtr reports whether value is a ptr type. -func IsPtr(value interface{}) bool { - return kindOf(value) == reflect.Ptr -} - -// IsMap reports whether value is a map type. -func IsMap(value interface{}) bool { - return kindOf(value) == reflect.Map -} - -// IsMapPtr reports whether v is a map ptr type. -func IsMapPtr(v interface{}) bool { - t := reflect.TypeOf(v) - return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Map -} - -// IsSlice reports whether value is a slice type. -func IsSlice(value interface{}) bool { - return kindOf(value) == reflect.Slice -} - -// IsStruct reports whether value is a struct type -func IsStruct(value interface{}) bool { - return kindOf(value) == reflect.Struct -} - -// IsSlicePtr reports whether v is a slice ptr type. -func IsSlicePtr(v interface{}) bool { - return kindOf(v) == reflect.Ptr && reflect.TypeOf(v).Elem().Kind() == reflect.Slice -} - -// IsSliceInterfacePtr reports whether v is a slice ptr type. -func IsSliceInterfacePtr(v interface{}) bool { - // Must use ValueOf because Elem().Elem() type resolves dynamically. - vv := reflect.ValueOf(v) - return vv.Kind() == reflect.Ptr && vv.Elem().Kind() == reflect.Interface && vv.Elem().Elem().Kind() == reflect.Slice -} - -// IsTypeStructPtr reports whether v is a struct ptr type. -func IsTypeStructPtr(t reflect.Type) bool { - if t == reflect.TypeOf(nil) { - return false - } - return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct -} - -// IsTypeSlicePtr reports whether v is a slice ptr type. -func IsTypeSlicePtr(t reflect.Type) bool { - if t == reflect.TypeOf(nil) { - return false - } - return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Slice -} - -// IsTypeMap reports whether v is a map type. -func IsTypeMap(t reflect.Type) bool { - if t == reflect.TypeOf(nil) { - return false - } - return t.Kind() == reflect.Map -} - -// IsTypeInterface reports whether v is an interface. -func IsTypeInterface(t reflect.Type) bool { - if t == reflect.TypeOf(nil) { - return false - } - return t.Kind() == reflect.Interface -} - -// IsTypeSliceOfInterface reports whether v is a slice of interface. -func IsTypeSliceOfInterface(t reflect.Type) bool { - if t == reflect.TypeOf(nil) { - return false - } - return t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Interface -} - -// IsNilOrInvalidValue reports whether v is nil or reflect.Zero. -func IsNilOrInvalidValue(v reflect.Value) bool { - return !v.IsValid() || (v.Kind() == reflect.Ptr && v.IsNil()) || IsValueNil(v.Interface()) -} - -// IsValueNil returns true if either value is nil, or has dynamic type {ptr, -// map, slice} with value nil. -func IsValueNil(value interface{}) bool { - if value == nil { - return true - } - switch kindOf(value) { - case reflect.Slice, reflect.Ptr, reflect.Map: - return reflect.ValueOf(value).IsNil() - } - return false -} - -// IsValueNilOrDefault returns true if either IsValueNil(value) or the default -// value for the type. -func IsValueNilOrDefault(value interface{}) bool { - if IsValueNil(value) { - return true - } - if !IsValueScalar(reflect.ValueOf(value)) { - // Default value is nil for non-scalar types. - return false - } - return value == reflect.New(reflect.TypeOf(value)).Elem().Interface() -} - -// IsValuePtr reports whether v is a ptr type. -func IsValuePtr(v reflect.Value) bool { - return v.Kind() == reflect.Ptr -} - -// IsValueInterface reports whether v is an interface type. -func IsValueInterface(v reflect.Value) bool { - return v.Kind() == reflect.Interface -} - -// IsValueStruct reports whether v is a struct type. -func IsValueStruct(v reflect.Value) bool { - return v.Kind() == reflect.Struct -} - -// IsValueStructPtr reports whether v is a struct ptr type. -func IsValueStructPtr(v reflect.Value) bool { - return v.Kind() == reflect.Ptr && IsValueStruct(v.Elem()) -} - -// IsValueMap reports whether v is a map type. -func IsValueMap(v reflect.Value) bool { - return v.Kind() == reflect.Map -} - -// IsValueSlice reports whether v is a slice type. -func IsValueSlice(v reflect.Value) bool { - return v.Kind() == reflect.Slice -} - -// IsValueScalar reports whether v is a scalar type. -func IsValueScalar(v reflect.Value) bool { - if IsNilOrInvalidValue(v) { - return false - } - if IsValuePtr(v) { - if v.IsNil() { - return false - } - v = v.Elem() - } - return !IsValueStruct(v) && !IsValueMap(v) && !IsValueSlice(v) -} - -// ValuesAreSameType returns true if v1 and v2 has the same reflect.Type, -// otherwise it returns false. -func ValuesAreSameType(v1 reflect.Value, v2 reflect.Value) bool { - return v1.Type() == v2.Type() -} - -// IsEmptyString returns true if value is an empty string. -func IsEmptyString(value interface{}) bool { - if value == nil { - return true - } - switch kindOf(value) { - case reflect.String: - if _, ok := value.(string); ok { - return value.(string) == "" - } - } - return false -} - -// DeleteFromSlicePtr deletes an entry at index from the parent, which must be a slice ptr. -func DeleteFromSlicePtr(parentSlice interface{}, index int) error { - scope.Debugf("DeleteFromSlicePtr index=%d, slice=\n%v", index, parentSlice) - pv := reflect.ValueOf(parentSlice) - - if !IsSliceInterfacePtr(parentSlice) { - return fmt.Errorf("deleteFromSlicePtr parent type is %T, must be *[]interface{}", parentSlice) - } - - pvv := pv.Elem() - if pvv.Kind() == reflect.Interface { - pvv = pvv.Elem() - } - - pv.Elem().Set(reflect.AppendSlice(pvv.Slice(0, index), pvv.Slice(index+1, pvv.Len()))) - - return nil -} - -// UpdateSlicePtr updates an entry at index in the parent, which must be a slice ptr, with the given value. -func UpdateSlicePtr(parentSlice interface{}, index int, value interface{}) error { - scope.Debugf("UpdateSlicePtr parent=\n%v\n, index=%d, value=\n%v", parentSlice, index, value) - pv := reflect.ValueOf(parentSlice) - v := reflect.ValueOf(value) - - if !IsSliceInterfacePtr(parentSlice) { - return fmt.Errorf("updateSlicePtr parent type is %T, must be *[]interface{}", parentSlice) - } - - pvv := pv.Elem() - if pvv.Kind() == reflect.Interface { - pv.Elem().Elem().Index(index).Set(v) - return nil - } - pv.Elem().Index(index).Set(v) - - return nil -} - -// InsertIntoMap inserts value with key into parent which must be a map, map ptr, or interface to map. -func InsertIntoMap(parentMap interface{}, key interface{}, value interface{}) error { - scope.Debugf("InsertIntoMap key=%v, value=%v, map=\n%v", key, value, parentMap) - v := reflect.ValueOf(parentMap) - kv := reflect.ValueOf(key) - vv := reflect.ValueOf(value) - - if v.Type().Kind() == reflect.Ptr { - v = v.Elem() - } - if v.Type().Kind() == reflect.Interface { - v = v.Elem() - } - - if v.Type().Kind() != reflect.Map { - scope.Debugf("error %v", v.Type().Kind()) - return fmt.Errorf("insertIntoMap parent type is %T, must be map", parentMap) - } - - v.SetMapIndex(kv, vv) - - return nil -} - -// DeleteFromMap deletes an entry with the given key parent, which must be a map. -func DeleteFromMap(parentMap interface{}, key interface{}) error { - scope.Debugf("DeleteFromMap key=%s, parent:\n%v\n", key, parentMap) - pv := reflect.ValueOf(parentMap) - - if !IsMap(parentMap) { - return fmt.Errorf("deleteFromMap parent type is %T, must be map", parentMap) - } - pv.SetMapIndex(reflect.ValueOf(key), reflect.Value{}) - - return nil -} - -// ToIntValue returns 0, false if val is not a number type, otherwise it returns the int value of val. -func ToIntValue(val interface{}) (int, bool) { - if IsValueNil(val) { - return 0, false - } - v := reflect.ValueOf(val) - switch { - case IsIntKind(v.Kind()): - return int(v.Int()), true - case IsUintKind(v.Kind()): - return int(v.Uint()), true - } - return 0, false -} - -// IsIntKind reports whether k is an integer kind of any size. -func IsIntKind(k reflect.Kind) bool { - switch k { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return true - } - return false -} - -// IsUintKind reports whether k is an unsigned integer kind of any size. -func IsUintKind(k reflect.Kind) bool { - switch k { - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return true - } - return false -} diff --git a/operator/pkg/util/reflect_test.go b/operator/pkg/util/reflect_test.go deleted file mode 100644 index de4084e60..000000000 --- a/operator/pkg/util/reflect_test.go +++ /dev/null @@ -1,412 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "reflect" - "testing" -) - -// TODO: add missing unit tests (istio/istio#17246). -// errToString returns the string representation of err and the empty string if -// err is nil. -func errToString(err error) string { - if err == nil { - return "" - } - return err.Error() -} - -// to ptr conversion utility functions -func toInt8Ptr(i int8) *int8 { return &i } - -func TestIsValueNil(t *testing.T) { - if !IsValueNil(nil) { - t.Error("got IsValueNil(nil) false, want true") - } - if !IsValueNil((*int)(nil)) { - t.Error("got IsValueNil(ptr) false, want true") - } - if !IsValueNil(map[int]int(nil)) { - t.Error("got IsValueNil(map) false, want true") - } - if !IsValueNil([]int(nil)) { - t.Error("got IsValueNil(slice) false, want true") - } - if !IsValueNil(interface{}(nil)) { - t.Error("got IsValueNil(interface) false, want true") - } - - if IsValueNil(toInt8Ptr(42)) { - t.Error("got IsValueNil(ptr) true, want false") - } - if IsValueNil(map[int]int{42: 42}) { - t.Error("got IsValueNil(map) true, want false") - } - if IsValueNil([]int{1, 2, 3}) { - t.Error("got IsValueNil(slice) true, want false") - } - if IsValueNil(interface{}(42)) { - t.Error("got IsValueNil(interface) true, want false") - } -} - -func TestIsValueNilOrDefault(t *testing.T) { - if !IsValueNilOrDefault(nil) { - t.Error("got IsValueNilOrDefault(nil) false, want true") - } - if !IsValueNilOrDefault((*int)(nil)) { - t.Error("got IsValueNilOrDefault(ptr) false, want true") - } - if !IsValueNilOrDefault(map[int]int(nil)) { - t.Error("got IsValueNilOrDefault(map) false, want true") - } - if !IsValueNilOrDefault([]int(nil)) { - t.Error("got IsValueNilOrDefault(slice) false, want true") - } - if !IsValueNilOrDefault(interface{}(nil)) { - t.Error("got IsValueNilOrDefault(interface) false, want true") - } - if !IsValueNilOrDefault(int(0)) { - t.Error("got IsValueNilOrDefault(int(0)) false, want true") - } - if !IsValueNilOrDefault("") { - t.Error("got IsValueNilOrDefault(\"\") false, want true") - } - if !IsValueNilOrDefault(false) { - t.Error("got IsValueNilOrDefault(false) false, want true") - } - i := 32 - ip := &i - if IsValueNilOrDefault(&ip) { - t.Error("got IsValueNilOrDefault(ptr to ptr) false, want true") - } -} - -func TestIsValueFuncs(t *testing.T) { - testInt := int(42) - testStruct := struct{}{} - testSlice := []bool{} - testMap := map[bool]bool{} - var testNilSlice []bool - var testNilMap map[bool]bool - - allValues := []interface{}{nil, testInt, &testInt, testStruct, &testStruct, testNilSlice, testSlice, &testSlice, testNilMap, testMap, &testMap} - - tests := []struct { - desc string - function func(v reflect.Value) bool - okValues []interface{} - }{ - { - desc: "IsValuePtr", - function: IsValuePtr, - okValues: []interface{}{&testInt, &testStruct, &testSlice, &testMap}, - }, - { - desc: "IsValueStruct", - function: IsValueStruct, - okValues: []interface{}{testStruct}, - }, - { - desc: "IsValueInterface", - function: IsValueInterface, - okValues: []interface{}{}, - }, - { - desc: "IsValueStructPtr", - function: IsValueStructPtr, - okValues: []interface{}{&testStruct}, - }, - { - desc: "IsValueMap", - function: IsValueMap, - okValues: []interface{}{testNilMap, testMap}, - }, - { - desc: "IsValueSlice", - function: IsValueSlice, - okValues: []interface{}{testNilSlice, testSlice}, - }, - { - desc: "IsValueScalar", - function: IsValueScalar, - okValues: []interface{}{testInt, &testInt}, - }, - } - - for _, tt := range tests { - for vidx, v := range allValues { - if got, want := tt.function(reflect.ValueOf(v)), isInListOfInterface(tt.okValues, v); got != want { - t.Errorf("%s with %s (#%d): got: %t, want: %t", tt.desc, reflect.TypeOf(v), vidx, got, want) - } - } - } -} - -func TestValuesAreSameType(t *testing.T) { - type EnumType int64 - - tests := []struct { - inDesc string - inV1 interface{} - inV2 interface{} - want bool - }{ - { - inDesc: "success both are int32 types", - inV1: int32(42), - inV2: int32(43), - want: true, - }, - { - inDesc: "fail unmatching int types", - inV1: int16(42), - inV2: int32(43), - want: false, - }, - { - inDesc: "fail unmatching int and string type", - inV1: int32(42), - inV2: "42", - want: false, - }, - { - inDesc: "fail EnumType and int64 types", - inV1: EnumType(42), - inV2: int64(43), - want: false, - }, - } - - for _, tt := range tests { - t.Run(tt.inDesc, func(t *testing.T) { - got := ValuesAreSameType(reflect.ValueOf(tt.inV1), reflect.ValueOf(tt.inV2)) - if got != tt.want { - t.Errorf("got %v, want %v for comparing %T against %T", got, tt.want, tt.inV1, tt.inV2) - } - }) - } -} - -func TestIsTypeFuncs(t *testing.T) { - testInt := int(42) - testStruct := struct{}{} - testSlice := []bool{} - testSliceOfInterface := []interface{}{} - testMap := map[bool]bool{} - var testNilSlice []bool - var testNilMap map[bool]bool - - allTypes := []interface{}{ - nil, testInt, &testInt, testStruct, &testStruct, testNilSlice, - testSlice, &testSlice, testSliceOfInterface, testNilMap, testMap, &testMap, - } - - tests := []struct { - desc string - function func(v reflect.Type) bool - okTypes []interface{} - }{ - { - desc: "IsTypeStructPtr", - function: IsTypeStructPtr, - okTypes: []interface{}{&testStruct}, - }, - { - desc: "IsTypeSlicePtr", - function: IsTypeSlicePtr, - okTypes: []interface{}{&testSlice}, - }, - { - desc: "IsTypeMap", - function: IsTypeMap, - okTypes: []interface{}{testNilMap, testMap}, - }, - { - desc: "IsTypeInterface", - function: IsTypeInterface, - okTypes: []interface{}{}, - }, - { - desc: "IsTypeSliceOfInterface", - function: IsTypeSliceOfInterface, - okTypes: []interface{}{testSliceOfInterface}, - }, - } - - for _, tt := range tests { - for vidx, v := range allTypes { - if got, want := tt.function(reflect.TypeOf(v)), isInListOfInterface(tt.okTypes, v); got != want { - t.Errorf("%s with %s (#%d): got: %t, want: %t", tt.desc, reflect.TypeOf(v), vidx, got, want) - } - } - } -} - -type interfaceContainer struct { - I anInterface -} - -type anInterface interface { - IsU() -} - -type implementsInterface struct { - A string -} - -func (*implementsInterface) IsU() {} - -func TestIsValueInterface(t *testing.T) { - intf := &interfaceContainer{ - I: &implementsInterface{ - A: "a", - }, - } - iField := reflect.ValueOf(intf).Elem().FieldByName("I") - if !IsValueInterface(iField) { - t.Errorf("IsValueInterface(): got false, want true") - } -} - -func TestIsTypeInterface(t *testing.T) { - intf := &interfaceContainer{ - I: &implementsInterface{ - A: "a", - }, - } - testIfField := reflect.ValueOf(intf).Elem().Field(0) - - if !IsTypeInterface(testIfField.Type()) { - t.Errorf("IsTypeInterface(): got false, want true") - } -} - -func isInListOfInterface(lv []interface{}, v interface{}) bool { - for _, vv := range lv { - if reflect.DeepEqual(vv, v) { - return true - } - } - return false -} - -func TestDeleteFromSlicePtr(t *testing.T) { - parentSlice := []int{42, 43, 44, 45} - var parentSliceI interface{} = parentSlice - if err := DeleteFromSlicePtr(&parentSliceI, 1); err != nil { - t.Fatalf("got error: %s, want error: nil", err) - } - wantSlice := []int{42, 44, 45} - if got, want := parentSliceI, wantSlice; !reflect.DeepEqual(got, want) { - t.Errorf("got:\n%v\nwant:\n%v\n", got, want) - } - - badParent := struct{}{} - wantErr := `deleteFromSlicePtr parent type is *struct {}, must be *[]interface{}` - if got, want := errToString(DeleteFromSlicePtr(&badParent, 1)), wantErr; got != want { - t.Fatalf("got error: %s, want error: %s", got, want) - } -} - -func TestUpdateSlicePtr(t *testing.T) { - parentSlice := []int{42, 43, 44, 45} - var parentSliceI interface{} = parentSlice - if err := UpdateSlicePtr(&parentSliceI, 1, 42); err != nil { - t.Fatalf("got error: %s, want error: nil", err) - } - wantSlice := []int{42, 42, 44, 45} - if got, want := parentSliceI, wantSlice; !reflect.DeepEqual(got, want) { - t.Errorf("got:\n%v\nwant:\n%v\n", got, want) - } - - badParent := struct{}{} - wantErr := `updateSlicePtr parent type is *struct {}, must be *[]interface{}` - if got, want := errToString(UpdateSlicePtr(&badParent, 1, 42)), wantErr; got != want { - t.Fatalf("got error: %s, want error: %s", got, want) - } -} - -func TestInsertIntoMap(t *testing.T) { - parentMap := map[int]string{42: "forty two", 43: "forty three"} - key := 44 - value := "forty four" - if err := InsertIntoMap(parentMap, key, value); err != nil { - t.Fatalf("got error: %s, want error: nil", err) - } - wantMap := map[int]string{42: "forty two", 43: "forty three", 44: "forty four"} - if got, want := parentMap, wantMap; !reflect.DeepEqual(got, want) { - t.Errorf("got:\n%v\nwant:\n%v\n", got, want) - } - - badParent := struct{}{} - wantErr := `insertIntoMap parent type is *struct {}, must be map` - if got, want := errToString(InsertIntoMap(&badParent, key, value)), wantErr; got != want { - t.Fatalf("got error: %s, want error: %s", got, want) - } -} - -var ( - allIntTypes = []interface{}{int(-42), int8(-43), int16(-44), int32(-45), int64(-46)} - allUintTypes = []interface{}{uint(42), uint8(43), uint16(44), uint32(45), uint64(46)} - allIntegerTypes = append(allIntTypes, allUintTypes...) - nonIntTypes = []interface{}{nil, "", []int{}, map[string]bool{}} - allTypes = append(allIntegerTypes, nonIntTypes...) -) - -func TestIsInteger(t *testing.T) { - tests := []struct { - desc string - function func(v reflect.Kind) bool - want []interface{} - }{ - { - desc: "ints", - function: IsIntKind, - want: allIntTypes, - }, - { - desc: "uints", - function: IsUintKind, - want: allUintTypes, - }, - } - - for _, tt := range tests { - var got []interface{} - for _, v := range allTypes { - if tt.function(reflect.ValueOf(v).Kind()) { - got = append(got, v) - } - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("%s: got %v, want %v", tt.desc, got, tt.want) - } - } -} - -func TestToIntValue(t *testing.T) { - var got []int - for _, v := range allTypes { - if i, ok := ToIntValue(v); ok { - got = append(got, i) - } - } - want := []int{-42, -43, -44, -45, -46, 42, 43, 44, 45, 46} - if !reflect.DeepEqual(got, want) { - t.Errorf("got %v, want %v", got, want) - } -} diff --git a/operator/pkg/util/testdata/overlay-iop.yaml b/operator/pkg/util/testdata/overlay-iop.yaml deleted file mode 100644 index e5601d121..000000000 --- a/operator/pkg/util/testdata/overlay-iop.yaml +++ /dev/null @@ -1,102 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - meshConfig: - accessLogFile: /dev/stdout - extensionProviders: - - name: otel - envoyOtelAls: - service: otel-collector.dubbo-system.svc.cluster.local - port: 4317 - - name: prometheus - prometheus: - - name: stackdriver - stackdriver: - - name: envoy - envoyFileAccessLog: - path: /dev/stdout - - name: envoyExtAuthzHttp - envoyExtAuthzHttp: - - name: envoyExtAuthzGrpc - envoyExtAuthzGrpc: - - name: zipkin - zipkin: - - name: lightstep - lightstep: - - name: datadog - datadog: - - name: opencensus - opencensus: - - name: skywalking - skywalking: - - name: envoyHttpAls - envoyHttpAls: - - name: envoyTcpAls - envoyTcpAls: - components: - egressGateways: - - name: istio-egressgateway - enabled: true - k8s: - resources: - requests: - cpu: 10m - memory: 40Mi - - ingressGateways: - - name: istio-ingressgateway - enabled: true - k8s: - resources: - requests: - cpu: 10m - memory: 40Mi - service: - ports: - ## You can add custom gateway ports in user values overrides, but it must include those ports since helm replaces. - # Note that AWS ELB will by default perform health checks on the first port - # on this list. Setting this to the health check port will ensure that health - # checks always work. https://github.com/istio/istio/issues/12503 - - port: 15021 - targetPort: 15021 - name: status-port - - port: 80 - targetPort: 8080 - name: http2 - - port: 443 - targetPort: 8443 - name: https - - port: 31400 - targetPort: 31400 - name: tcp - # This is the port where sni routing happens - - port: 15443 - targetPort: 15443 - name: tls - - pilot: - k8s: - env: - - name: PILOT_TRACE_SAMPLING - value: "100" - resources: - requests: - cpu: 10m - memory: 100Mi - - values: - global: - proxy: - resources: - requests: - cpu: 10m - memory: 40Mi - - pilot: - autoscaleEnabled: false - - gateways: - istio-egressgateway: - autoscaleEnabled: false - istio-ingressgateway: - autoscaleEnabled: false diff --git a/operator/pkg/util/testdata/yaml/input/yaml_layer1.yaml b/operator/pkg/util/testdata/yaml/input/yaml_layer1.yaml deleted file mode 100644 index 988abad6b..000000000 --- a/operator/pkg/util/testdata/yaml/input/yaml_layer1.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - components: - base: - enabled: false - pilot: - enabled: false - ingressGateways: - - namespace: dubbo-system - name: istio-ingressgateway - enabled: true - label: - api: default - k8s: - service: - externalTrafficPolicy: Local - serviceAnnotations: - manifest-generate: "testserviceAnnotation" diff --git a/operator/pkg/util/testdata/yaml/input/yaml_layer1_stdin.yaml b/operator/pkg/util/testdata/yaml/input/yaml_layer1_stdin.yaml deleted file mode 100644 index 29a049ee2..000000000 --- a/operator/pkg/util/testdata/yaml/input/yaml_layer1_stdin.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - components: - pilot: - enabled: false - ingressGateways: - - namespace: dubbo-system - name: istio-ingressgateway - enabled: true - k8s: - service: - externalTrafficPolicy: Local - serviceAnnotations: - manifest-generate: "testserviceAnnotation" diff --git a/operator/pkg/util/testdata/yaml/input/yaml_layer2.yaml b/operator/pkg/util/testdata/yaml/input/yaml_layer2.yaml deleted file mode 100644 index 6340f6d51..000000000 --- a/operator/pkg/util/testdata/yaml/input/yaml_layer2.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - components: - pilot: - enabled: true - ingressGateways: - - namespace: dubbo-system - name: istio-ingressgateway - enabled: true - label: - foo: bar - k8s: - service: - externalTrafficPolicy: Test diff --git a/operator/pkg/util/testdata/yaml/input/yaml_layer3.yaml b/operator/pkg/util/testdata/yaml/input/yaml_layer3.yaml deleted file mode 100644 index 65a0f9c9c..000000000 --- a/operator/pkg/util/testdata/yaml/input/yaml_layer3.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - components: - base: - enabled: true - ingressGateways: - - namespace: dubbo-system - name: istio-ingressgateway - enabled: false - k8s: - service: - externalTrafficPolicy: Test diff --git a/operator/pkg/util/testdata/yaml/output/layer1.yaml b/operator/pkg/util/testdata/yaml/output/layer1.yaml deleted file mode 100644 index 988abad6b..000000000 --- a/operator/pkg/util/testdata/yaml/output/layer1.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - components: - base: - enabled: false - pilot: - enabled: false - ingressGateways: - - namespace: dubbo-system - name: istio-ingressgateway - enabled: true - label: - api: default - k8s: - service: - externalTrafficPolicy: Local - serviceAnnotations: - manifest-generate: "testserviceAnnotation" diff --git a/operator/pkg/util/testdata/yaml/output/layer1_2.yaml b/operator/pkg/util/testdata/yaml/output/layer1_2.yaml deleted file mode 100644 index bcfe12503..000000000 --- a/operator/pkg/util/testdata/yaml/output/layer1_2.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - components: - base: - enabled: false - ingressGateways: - - enabled: true - label: - api: default - foo: bar - k8s: - service: - externalTrafficPolicy: Test - serviceAnnotations: - manifest-generate: testserviceAnnotation - name: istio-ingressgateway - namespace: dubbo-system - pilot: - enabled: true diff --git a/operator/pkg/util/testdata/yaml/output/layer1_2_3.yaml b/operator/pkg/util/testdata/yaml/output/layer1_2_3.yaml deleted file mode 100644 index 2031d7a6d..000000000 --- a/operator/pkg/util/testdata/yaml/output/layer1_2_3.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - components: - base: - enabled: true - ingressGateways: - - enabled: false - label: - api: default - foo: bar - k8s: - service: - externalTrafficPolicy: Test - serviceAnnotations: - manifest-generate: testserviceAnnotation - name: istio-ingressgateway - namespace: dubbo-system - pilot: - enabled: true diff --git a/operator/pkg/util/testdata/yaml/output/layer1_stdin.yaml b/operator/pkg/util/testdata/yaml/output/layer1_stdin.yaml deleted file mode 100644 index 988abad6b..000000000 --- a/operator/pkg/util/testdata/yaml/output/layer1_stdin.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - components: - base: - enabled: false - pilot: - enabled: false - ingressGateways: - - namespace: dubbo-system - name: istio-ingressgateway - enabled: true - label: - api: default - k8s: - service: - externalTrafficPolicy: Local - serviceAnnotations: - manifest-generate: "testserviceAnnotation" diff --git a/operator/pkg/util/tgz/tgz.go b/operator/pkg/util/tgz/tgz.go deleted file mode 100644 index b582e2c90..000000000 --- a/operator/pkg/util/tgz/tgz.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package tgz - -import ( - "archive/tar" - "compress/gzip" - "fmt" - "io" - "os" - "path" - "path/filepath" - "strings" -) - -// Create creates a gzipped tar file from srcDir and writes it to outPath. -func Create(srcDir, outPath string) error { - mw, err := os.Create(outPath) - if err != nil { - return err - } - - gzw := gzip.NewWriter(mw) - defer gzw.Close() - - tw := tar.NewWriter(gzw) - defer tw.Close() - - return filepath.Walk(srcDir, func(file string, fi os.FileInfo, err error) error { - if err != nil { - return err - } - if !fi.Mode().IsRegular() { - return nil - } - header, err := tar.FileInfoHeader(fi, fi.Name()) - if err != nil { - return err - } - header.Name = strings.TrimPrefix(strings.Replace(file, srcDir, "", -1), string(filepath.Separator)) - if err := tw.WriteHeader(header); err != nil { - return err - } - - f, err := os.Open(file) - if err != nil { - return err - } - defer f.Close() - - if _, err := io.Copy(tw, f); err != nil { - return err - } - return nil - }) -} - -func Extract(gzipStream io.Reader, destination string) error { - uncompressedStream, err := gzip.NewReader(gzipStream) - if err != nil { - return fmt.Errorf("create gzip reader: %v", err) - } - - tarReader := tar.NewReader(uncompressedStream) - - for { - header, err := tarReader.Next() - if err == io.EOF { - break - } - if err != nil { - return fmt.Errorf("next: %v", err) - } - - dest := filepath.Join(destination, header.Name) - switch header.Typeflag { - case tar.TypeDir: - if _, err := os.Stat(dest); err != nil { - if err := os.Mkdir(dest, 0o755); err != nil { - return fmt.Errorf("mkdir: %v", err) - } - } - case tar.TypeReg: - // Create containing folder if not present - dir := path.Dir(dest) - if _, err := os.Stat(dir); err != nil { - if err := os.MkdirAll(dir, 0o755); err != nil { - return err - } - } - outFile, err := os.Create(dest) - if err != nil { - return fmt.Errorf("create: %v", err) - } - if _, err := io.Copy(outFile, tarReader); err != nil { - return fmt.Errorf("copy: %v", err) - } - outFile.Close() - default: - return fmt.Errorf("uknown type: %v in %v", header.Typeflag, header.Name) - } - } - return nil -} diff --git a/operator/pkg/util/util.go b/operator/pkg/util/util.go deleted file mode 100644 index 9238c8ae3..000000000 --- a/operator/pkg/util/util.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "bytes" - "fmt" - "html/template" - "os" - "path/filepath" - "strconv" - "strings" -) - -import ( - "google.golang.org/protobuf/types/known/structpb" -) - -type FileFilter func(fileName string) bool - -// StringBoolMapToSlice creates and returns a slice of all the map keys with true. -func StringBoolMapToSlice(m map[string]bool) []string { - s := make([]string, 0, len(m)) - for k, v := range m { - if v { - s = append(s, k) - } - } - return s -} - -// ReadFilesWithFilter reads files from path, for a directory it recursively reads files and filters the results -// for single file it directly reads the file. It returns a concatenated output of all matching files' content. -func ReadFilesWithFilter(path string, filter FileFilter) (string, error) { - fileList, err := FindFiles(path, filter) - if err != nil { - return "", err - } - var sb strings.Builder - for _, file := range fileList { - a, err := os.ReadFile(file) - if err != nil { - return "", err - } - if _, err := sb.WriteString(string(a) + "\n"); err != nil { - return "", err - } - } - return sb.String(), nil -} - -// FindFiles reads files from path, and returns the file names that match the filter. -func FindFiles(path string, filter FileFilter) ([]string, error) { - fi, err := os.Stat(path) - if err != nil { - return nil, err - } - var fileList []string - if fi.IsDir() { - err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if info.IsDir() || !filter(path) { - return nil - } - fileList = append(fileList, path) - return nil - }) - if err != nil { - return nil, err - } - } else { - fileList = append(fileList, path) - } - return fileList, nil -} - -// ParseValue parses string into a value -func ParseValue(valueStr string) interface{} { - var value interface{} - if v, err := strconv.Atoi(valueStr); err == nil { - value = v - } else if v, err := strconv.ParseFloat(valueStr, 64); err == nil { - value = v - } else if v, err := strconv.ParseBool(valueStr); err == nil { - value = v - } else { - value = strings.ReplaceAll(valueStr, "\\,", ",") - } - return value -} - -// ConsolidateLog is a helper function to dedup the log message. -func ConsolidateLog(logMessage string) string { - logCountMap := make(map[string]int) - stderrSlice := strings.Split(logMessage, "\n") - for _, item := range stderrSlice { - if item == "" { - continue - } - _, exist := logCountMap[item] - if exist { - logCountMap[item]++ - } else { - logCountMap[item] = 1 - } - } - var sb strings.Builder - for _, item := range stderrSlice { - if logCountMap[item] == 0 { - continue - } - sb.WriteString(fmt.Sprintf("%s (repeated %v times)\n", item, logCountMap[item])) - // reset seen log count - logCountMap[item] = 0 - } - return sb.String() -} - -// RenderTemplate is a helper method to render a template with the given values. -func RenderTemplate(tmpl string, ts interface{}) (string, error) { - t, err := template.New("").Parse(tmpl) - if err != nil { - return "", err - } - buf := new(bytes.Buffer) - err = t.Execute(buf, ts) - if err != nil { - return "", err - } - return buf.String(), nil -} - -func ValueString(v *structpb.Value) string { - switch x := v.Kind.(type) { - case *structpb.Value_StringValue: - return x.StringValue - case *structpb.Value_NumberValue: - return fmt.Sprint(x.NumberValue) - default: - return v.String() - } -} - -func MustStruct(m map[string]interface{}) *structpb.Struct { - s, _ := structpb.NewStruct(m) - return s -} diff --git a/operator/pkg/util/util_test.go b/operator/pkg/util/util_test.go deleted file mode 100644 index 42073c88d..000000000 --- a/operator/pkg/util/util_test.go +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "errors" - "testing" -) - -func TestParseValue(t *testing.T) { - tests := []struct { - desc string - in string - want interface{} - }{ - { - desc: "empty", - in: "", - want: "", - }, - { - desc: "true", - in: "true", - want: true, - }, - { - desc: "false", - in: "false", - want: false, - }, - { - desc: "numeric-one", - in: "1", - want: 1, - }, - { - desc: "numeric-zero", - in: "0", - want: 0, - }, - { - desc: "numeric-large", - in: "12345678", - want: 12345678, - }, - { - desc: "numeric-negative", - in: "-12345678", - want: -12345678, - }, - { - desc: "float", - in: "1.23456", - want: 1.23456, - }, - { - desc: "float-zero", - in: "0.00", - want: 0.00, - }, - { - desc: "float-negative", - in: "-6.54321", - want: -6.54321, - }, - { - desc: "string", - in: "foobar", - want: "foobar", - }, - { - desc: "string-number-prefix", - in: "123foobar", - want: "123foobar", - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got, want := ParseValue(tt.in), tt.want; !(got == want) { - t.Errorf("%s: got:%v, want:%v", tt.desc, got, want) - } - }) - } -} - -func TestConsolidateLog(t *testing.T) { - tests := []struct { - desc string - in string - want string - }{ - { - desc: "empty", - in: "", - want: "", - }, - { - desc: "2 errors once", - in: "err1\nerr2\n", - want: "err1 (repeated 1 times)\nerr2 (repeated 1 times)\n", - }, - { - desc: "3 errors multiple times", - in: "err1\nerr2\nerr3\nerr1\nerr2\nerr3\nerr3\nerr3\n", - want: "err1 (repeated 2 times)\nerr2 (repeated 2 times)\nerr3 (repeated 4 times)\n", - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got, want := ConsolidateLog(tt.in), tt.want; !(got == want) { - t.Errorf("%s: got:%s, want:%s", tt.desc, got, want) - } - }) - } -} - -func TestStringBoolMapToSlice(t *testing.T) { - tests := []struct { - desc string - in map[string]bool - want []string - }{ - { - desc: "empty", - in: make(map[string]bool), - want: make([]string, 0), - }, - { - desc: "", - in: map[string]bool{ - "yo": true, - "yolo": false, - "test1": true, - "water bottle": false, - "baseball hat": true, - }, - want: []string{ - "yo", - "test1", - "baseball hat", - }, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got, want := StringBoolMapToSlice(tt.in), tt.want; !(sameStringSlice(got, want)) { - t.Errorf("%s: got:%s, want: %s", tt.desc, got, want) - } - }) - } -} - -// Helper function to check if values in 2 slices are the same, -// no correspondence to order -func sameStringSlice(x, y []string) bool { - if len(x) != len(y) { - return false - } - diff := make(map[string]int, len(x)) - for _, _x := range x { - diff[_x]++ - } - for _, _y := range y { - if _, ok := diff[_y]; !ok { - return false - } - diff[_y]-- - if diff[_y] == 0 { - delete(diff, _y) - } - } - return len(diff) == 0 -} - -func TestRenderTemplate(t *testing.T) { - type tmplValue struct { - Name string - Proxy string - } - tests := []struct { - desc string - template string - in tmplValue - want string - err error - }{ - { - desc: "valid-template", - template: "{{.Name}} uses {{.Proxy}} as sidecar", - in: tmplValue{ - Name: "istio", - Proxy: "envoy", - }, - want: "istio uses envoy as sidecar", - err: nil, - }, - { - desc: "empty-template", - template: "", - in: tmplValue{ - Name: "istio", - Proxy: "envoy", - }, - want: "", - err: nil, - }, - { - desc: "template with no template strings", - template: "this template is without handlebar expressions", - in: tmplValue{ - Name: "istio", - Proxy: "envoy", - }, - want: "this template is without handlebar expressions", - err: nil, - }, - { - desc: "template with missing variable", - template: "{{ .Name }} has replaced its control plane with {{ .Istiod }} component", - in: tmplValue{ - Name: "istio", - Proxy: "envoy", - }, - want: "", - err: errors.New(""), - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - got, err := RenderTemplate(tt.template, tt.in) - if got != tt.want { - t.Errorf("%s: got :%v, wanted output: %v", tt.desc, got, tt.want) - } - - if (err == nil && tt.err != nil) || (err != nil && tt.err == nil) { - t.Errorf("%s: got error :%v, wanted error: %v", tt.desc, err, tt.err) - } - }) - } -} diff --git a/operator/pkg/util/yaml.go b/operator/pkg/util/yaml.go deleted file mode 100644 index 62946e5a1..000000000 --- a/operator/pkg/util/yaml.go +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "bufio" - "bytes" - "fmt" - "io" - "reflect" - "strings" -) - -import ( - jsonpatch "github.com/evanphx/json-patch/v5" - "github.com/golang/protobuf/jsonpb" // nolint: staticcheck - legacyproto "github.com/golang/protobuf/proto" // nolint: staticcheck - "github.com/kylelemons/godebug/diff" - "google.golang.org/protobuf/proto" - yaml3 "k8s.io/apimachinery/pkg/util/yaml" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -func ToYAMLGeneric(root interface{}) ([]byte, error) { - var vs []byte - if proto, ok := root.(proto.Message); ok { - v, err := protomarshal.ToYAML(proto) - if err != nil { - return nil, err - } - vs = []byte(v) - } else { - v, err := yaml.Marshal(root) - if err != nil { - return nil, err - } - vs = v - } - return vs, nil -} - -func MustToYAMLGeneric(root interface{}) string { - var vs []byte - if proto, ok := root.(proto.Message); ok { - v, err := protomarshal.ToYAML(proto) - if err != nil { - return err.Error() - } - vs = []byte(v) - } else { - v, err := yaml.Marshal(root) - if err != nil { - return err.Error() - } - vs = v - } - return string(vs) -} - -// ToYAML returns a YAML string representation of val, or the error string if an error occurs. -func ToYAML(val interface{}) string { - y, err := yaml.Marshal(val) - if err != nil { - return err.Error() - } - return string(y) -} - -// ToYAMLWithJSONPB returns a YAML string representation of val (using jsonpb), or the error string if an error occurs. -func ToYAMLWithJSONPB(val proto.Message) string { - v := reflect.ValueOf(val) - if val == nil || (v.Kind() == reflect.Ptr && v.IsNil()) { - return "null" - } - m := jsonpb.Marshaler{EnumsAsInts: true} - js, err := m.MarshalToString(legacyproto.MessageV1(val)) - if err != nil { - return err.Error() - } - yb, err := yaml.JSONToYAML([]byte(js)) - if err != nil { - return err.Error() - } - return string(yb) -} - -// MarshalWithJSONPB returns a YAML string representation of val (using jsonpb). -func MarshalWithJSONPB(val proto.Message) (string, error) { - return protomarshal.ToYAML(val) -} - -// UnmarshalWithJSONPB unmarshals y into out using gogo jsonpb (required for many proto defined structs). -func UnmarshalWithJSONPB(y string, out proto.Message, allowUnknownField bool) error { - // Treat nothing as nothing. If we called jsonpb.Unmarshaler it would return the same. - if y == "" { - return nil - } - jb, err := yaml.YAMLToJSON([]byte(y)) - if err != nil { - return err - } - u := jsonpb.Unmarshaler{AllowUnknownFields: allowUnknownField} - err = u.Unmarshal(bytes.NewReader(jb), legacyproto.MessageV1(out)) - if err != nil { - return err - } - return nil -} - -// OverlayTrees performs a sequential JSON strategic of overlays over base. -func OverlayTrees(base map[string]interface{}, overlays ...map[string]interface{}) (map[string]interface{}, error) { - needsOverlay := false - for _, o := range overlays { - if len(o) > 0 { - needsOverlay = true - break - } - } - if !needsOverlay { - // Avoid expensive overlay if possible - return base, nil - } - bby, err := yaml.Marshal(base) - if err != nil { - return nil, err - } - by := string(bby) - - for _, o := range overlays { - oy, err := yaml.Marshal(o) - if err != nil { - return nil, err - } - - by, err = OverlayYAML(by, string(oy)) - if err != nil { - return nil, err - } - } - - out := make(map[string]interface{}) - err = yaml.Unmarshal([]byte(by), &out) - if err != nil { - return nil, err - } - return out, nil -} - -// OverlayYAML patches the overlay tree over the base tree and returns the result. All trees are expressed as YAML -// strings. -func OverlayYAML(base, overlay string) (string, error) { - if strings.TrimSpace(base) == "" { - return overlay, nil - } - if strings.TrimSpace(overlay) == "" { - return base, nil - } - bj, err := yaml.YAMLToJSON([]byte(base)) - if err != nil { - return "", fmt.Errorf("yamlToJSON error in base: %s\n%s", err, bj) - } - oj, err := yaml.YAMLToJSON([]byte(overlay)) - if err != nil { - return "", fmt.Errorf("yamlToJSON error in overlay: %s\n%s", err, oj) - } - if base == "" { - bj = []byte("{}") - } - if overlay == "" { - oj = []byte("{}") - } - - merged, err := jsonpatch.MergePatch(bj, oj) - if err != nil { - return "", fmt.Errorf("json merge error (%s) for base object: \n%s\n override object: \n%s", err, bj, oj) - } - my, err := yaml.JSONToYAML(merged) - if err != nil { - return "", fmt.Errorf("jsonToYAML error (%s) for merged object: \n%s", err, merged) - } - - return string(my), nil -} - -// yamlDiff compares single YAML file -func yamlDiff(a, b string) string { - ao, bo := make(map[string]interface{}), make(map[string]interface{}) - if err := yaml.Unmarshal([]byte(a), &ao); err != nil { - return err.Error() - } - if err := yaml.Unmarshal([]byte(b), &bo); err != nil { - return err.Error() - } - - ay, err := yaml.Marshal(ao) - if err != nil { - return err.Error() - } - by, err := yaml.Marshal(bo) - if err != nil { - return err.Error() - } - - return diff.Diff(string(ay), string(by)) -} - -// yamlStringsToList yaml string parse to string list -func yamlStringsToList(str string) []string { - reader := bufio.NewReader(strings.NewReader(str)) - decoder := yaml3.NewYAMLReader(reader) - res := make([]string, 0) - for { - doc, err := decoder.Read() - if err == io.EOF { - break - } - if err != nil { - break - } - - chunk := bytes.TrimSpace(doc) - res = append(res, string(chunk)) - } - return res -} - -// multiYamlDiffOutput multi yaml diff output format -func multiYamlDiffOutput(res, diff string) string { - if res == "" { - return diff - } - if diff == "" { - return res - } - - return res + "\n" + diff -} - -func diffStringList(l1, l2 []string) string { - var maxLen int - var minLen int - var l1Max bool - res := "" - if len(l1)-len(l2) > 0 { - maxLen = len(l1) - minLen = len(l2) - l1Max = true - } else { - maxLen = len(l2) - minLen = len(l1) - l1Max = false - } - - for i := 0; i < maxLen; i++ { - d := "" - if i >= minLen { - if l1Max { - d = yamlDiff(l1[i], "") - } else { - d = yamlDiff("", l2[i]) - } - } else { - d = yamlDiff(l1[i], l2[i]) - } - res = multiYamlDiffOutput(res, d) - } - return res -} - -// YAMLDiff compares multiple YAML files and single YAML file -func YAMLDiff(a, b string) string { - al := yamlStringsToList(a) - bl := yamlStringsToList(b) - res := diffStringList(al, bl) - - return res -} - -// IsYAMLEqual reports whether the YAML in strings a and b are equal. -func IsYAMLEqual(a, b string) bool { - if strings.TrimSpace(a) == "" && strings.TrimSpace(b) == "" { - return true - } - ajb, err := yaml.YAMLToJSON([]byte(a)) - if err != nil { - scope.Debugf("bad YAML in isYAMLEqual:\n%s", a) - return false - } - bjb, err := yaml.YAMLToJSON([]byte(b)) - if err != nil { - scope.Debugf("bad YAML in isYAMLEqual:\n%s", b) - return false - } - - return bytes.Equal(ajb, bjb) -} - -// IsYAMLEmpty reports whether the YAML string y is logically empty. -func IsYAMLEmpty(y string) bool { - var yc []string - for _, l := range strings.Split(y, "\n") { - yt := strings.TrimSpace(l) - if !strings.HasPrefix(yt, "#") && !strings.HasPrefix(yt, "---") { - yc = append(yc, l) - } - } - res := strings.TrimSpace(strings.Join(yc, "\n")) - return res == "{}" || res == "" -} diff --git a/operator/pkg/util/yaml_test.go b/operator/pkg/util/yaml_test.go deleted file mode 100644 index 62d0f4455..000000000 --- a/operator/pkg/util/yaml_test.go +++ /dev/null @@ -1,363 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "errors" - "reflect" - "testing" -) - -func TestToYAML(t *testing.T) { - tests := []struct { - desc string - inVals interface{} - expectedOut string - }{ - { - desc: "valid-yaml", - inVals: map[string]interface{}{ - "foo": "bar", - "yo": map[string]interface{}{ - "istio": "bar", - }, - }, - expectedOut: `foo: bar -yo: - istio: bar -`, - }, - { - desc: "alphabetical", - inVals: map[string]interface{}{ - "foo": "yaml", - "abc": "f", - }, - expectedOut: `abc: f -foo: yaml -`, - }, - { - desc: "expected-err-nil", - inVals: nil, - expectedOut: "null\n", - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got := ToYAML(tt.inVals); got != tt.expectedOut { - t.Errorf("%s: expected out %v got %s", tt.desc, tt.expectedOut, got) - } - }) - } -} - -func TestOverlayTrees(t *testing.T) { - tests := []struct { - desc string - inBase map[string]interface{} - inOverlays map[string]interface{} - expectedOverlay map[string]interface{} - expectedErr error - }{ - { - desc: "overlay-valid", - inBase: map[string]interface{}{ - "foo": "bar", - "baz": "naz", - }, - inOverlays: map[string]interface{}{ - "foo": "laz", - }, - expectedOverlay: map[string]interface{}{ - "baz": "naz", - "foo": "laz", - }, - expectedErr: nil, - }, - { - desc: "overlay-key-does-not-exist", - inBase: map[string]interface{}{ - "foo": "bar", - "baz": "naz", - }, - inOverlays: map[string]interface{}{ - "i-dont-exist": "i-really-dont-exist", - }, - expectedOverlay: map[string]interface{}{ - "baz": "naz", - "foo": "bar", - "i-dont-exist": "i-really-dont-exist", - }, - expectedErr: nil, - }, - { - desc: "remove-key-val", - inBase: map[string]interface{}{ - "foo": "bar", - }, - inOverlays: map[string]interface{}{ - "foo": nil, - }, - expectedOverlay: map[string]interface{}{}, - expectedErr: nil, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if gotOverlays, err := OverlayTrees(tt.inBase, tt.inOverlays); !reflect.DeepEqual(gotOverlays, tt.expectedOverlay) || - ((err != nil && tt.expectedErr == nil) || (err == nil && tt.expectedErr != nil)) { - t.Errorf("%s: expected overlay & err %v %v got %v %v", tt.desc, tt.expectedOverlay, tt.expectedErr, - gotOverlays, err) - } - }) - } -} - -func TestOverlayYAML(t *testing.T) { - tests := []struct { - desc string - base string - overlay string - expect string - err error - }{ - { - desc: "overlay-yaml", - base: `foo: bar -yo: lo -`, - overlay: `yo: go`, - expect: `foo: bar -yo: go -`, - err: nil, - }, - { - desc: "combine-yaml", - base: `foo: bar`, - overlay: `baz: razmatazz`, - expect: `baz: razmatazz -foo: bar -`, - err: nil, - }, - { - desc: "blank", - base: `R#)*J#FN`, - overlay: `FM#)M#F(*#M`, - expect: "", - err: errors.New("invalid json"), - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got, err := OverlayYAML(tt.base, tt.overlay); got != tt.expect || ((tt.err != nil && err == nil) || (tt.err == nil && err != nil)) { - t.Errorf("%s: expected overlay&err %v %v got %v %v", tt.desc, tt.expect, tt.err, got, err) - } - }) - } -} - -func TestYAMLDiff(t *testing.T) { - tests := []struct { - desc string - diff1 string - diff2 string - expect string - }{ - { - desc: "1-line-diff", - diff1: `hola: yo -foo: bar -goo: tar -`, - diff2: `hola: yo -foo: bar -notgoo: nottar -`, - expect: ` foo: bar --goo: tar - hola: yo -+notgoo: nottar - `, - }, - { - desc: "no-diff", - diff1: `foo: bar`, - diff2: `foo: bar`, - expect: ``, - }, - { - desc: "invalid-yaml", - diff1: `Ij#**#f#`, - diff2: `fm*##)n`, - expect: "error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {}", - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got := YAMLDiff(tt.diff1, tt.diff2); got != tt.expect { - t.Errorf("%s: expect %v got %v", tt.desc, tt.expect, got) - } - }) - } -} - -func TestMultipleYAMLDiff(t *testing.T) { - tests := []struct { - desc string - diff1 string - diff2 string - expect string - }{ - { - desc: "1-line-diff", - diff1: `hola: yo -foo: bar -goo: tar ---- -hola: yo1 -foo: bar1 -goo: tar1 -`, - diff2: `hola: yo -foo: bar -notgoo: nottar -`, - expect: ` foo: bar --goo: tar - hola: yo -+notgoo: nottar - --foo: bar1 --goo: tar1 --hola: yo1 -+{} - `, - }, - { - desc: "no-diff", - diff1: `foo: bar ---- -foo: bar1 -`, - diff2: `foo: bar ---- -foo: bar1 -`, - expect: ``, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got := YAMLDiff(tt.diff1, tt.diff2); got != tt.expect { - t.Errorf("%s: expect %v got %v", tt.desc, tt.expect, got) - } - }) - } -} - -func TestIsYAMLEqual(t *testing.T) { - tests := []struct { - desc string - in1 string - in2 string - expect bool - }{ - { - desc: "yaml-equal", - in1: `foo: bar`, - in2: `foo: bar`, - expect: true, - }, - { - desc: "bad-yaml-1", - in1: "O#JF*()#", - in2: `foo: bar`, - expect: false, - }, - { - desc: "bad-yaml-2", - in1: `foo: bar`, - in2: "#OHJ*#()F", - expect: false, - }, - { - desc: "yaml-not-equal", - in1: `zinc: iron -stoichiometry: avagadro -`, - in2: `i-swear: i-am -definitely-not: in1 -`, - expect: false, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got := IsYAMLEqual(tt.in1, tt.in2); got != tt.expect { - t.Errorf("%v: got %v want %v", tt.desc, got, tt.expect) - } - }) - } -} - -func TestIsYAMLEmpty(t *testing.T) { - tests := []struct { - desc string - in string - expect bool - }{ - { - desc: "completely-empty", - in: "", - expect: true, - }, - { - desc: "comment-logically-empty", - in: `# this is a comment -# this is another comment that serves no purpose -# (like all comments usually do) -`, - expect: true, - }, - { - desc: "start-yaml", - in: `--- I dont mean anything`, - expect: true, - }, - { - desc: "combine-comments-and-yaml", - in: `#this is another comment -foo: bar -# ^ that serves purpose -`, - expect: false, - }, - { - desc: "yaml-not-empty", - in: `foo: bar`, - expect: false, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got := IsYAMLEmpty(tt.in); got != tt.expect { - t.Errorf("%v: expect %v got %v", tt.desc, tt.expect, got) - } - }) - } -} diff --git a/operator/pkg/validate/common.go b/operator/pkg/validate/common.go deleted file mode 100644 index 0afbfd1cf..000000000 --- a/operator/pkg/validate/common.go +++ /dev/null @@ -1,350 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validate - -import ( - "fmt" - "net" - "reflect" - "regexp" - "strconv" - "strings" -) - -import ( - "istio.io/pkg/log" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -var ( - scope = log.RegisterScope("validation", "API validation", 0) - - // alphaNumericRegexp defines the alpha numeric atom, typically a - // component of names. This only allows lower case characters and digits. - alphaNumericRegexp = match(`[a-z0-9]+`) - - // separatorRegexp defines the separators allowed to be embedded in name - // components. This allow one period, one or two underscore and multiple - // dashes. - separatorRegexp = match(`(?:[._]|__|[-]*)`) - - // nameComponentRegexp restricts registry path component names to start - // with at least one letter or number, with following parts able to be - // separated by one period, one or two underscore and multiple dashes. - nameComponentRegexp = expression( - alphaNumericRegexp, - optional(repeated(separatorRegexp, alphaNumericRegexp))) - - // domainComponentRegexp restricts the registry domain component of a - // repository name to start with a component as defined by DomainRegexp - // and followed by an optional port. - domainComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`) - - // DomainRegexp defines the structure of potential domain components - // that may be part of image names. This is purposely a subset of what is - // allowed by DNS to ensure backwards compatibility with Docker image - // names. - DomainRegexp = expression( - domainComponentRegexp, - optional(repeated(literal(`.`), domainComponentRegexp)), - optional(literal(`:`), match(`[0-9]+`))) - - // TagRegexp matches valid tag names. From docker/docker:graph/tags.go. - TagRegexp = match(`[\w][\w.-]{0,127}`) - - // DigestRegexp matches valid digests. - DigestRegexp = match(`[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`) - - // NameRegexp is the format for the name component of references. The - // regexp has capturing groups for the domain and name part omitting - // the separating forward slash from either. - NameRegexp = expression( - optional(DomainRegexp, literal(`/`)), - nameComponentRegexp, - optional(repeated(literal(`/`), nameComponentRegexp))) - - // ReferenceRegexp is the full supported format of a reference. The regexp - // is anchored and has capturing groups for name, tag, and digest - // components. - ReferenceRegexp = anchored(capture(NameRegexp), - optional(literal(":"), capture(TagRegexp)), - optional(literal("@"), capture(DigestRegexp))) - - // ObjectNameRegexp is a legal name for a k8s object. - ObjectNameRegexp = match(`[a-z0-9.-]{1,254}`) -) - -// validateWithRegex checks whether the given value matches the regexp r. -func validateWithRegex(path util.Path, val interface{}, r *regexp.Regexp) (errs util.Errors) { - valStr := fmt.Sprint(val) - if len(r.FindString(valStr)) != len(valStr) { - errs = util.AppendErr(errs, fmt.Errorf("invalid value %s: %v", path, val)) - printError(errs.ToError()) - } - return errs -} - -// validateStringList returns a validator function that works on a string list, using the supplied ValidatorFunc vf on -// each element. -func validateStringList(vf ValidatorFunc) ValidatorFunc { - return func(path util.Path, val interface{}) util.Errors { - msg := fmt.Sprintf("validateStringList %v", val) - if !util.IsString(val) { - err := fmt.Errorf("validateStringList %s got %T, want string", path, val) - printError(err) - return util.NewErrs(err) - } - var errs util.Errors - for _, s := range strings.Split(val.(string), ",") { - errs = util.AppendErrs(errs, vf(path, strings.TrimSpace(s))) - scope.Debugf("\nerrors(%d): %v", len(errs), errs) - msg += fmt.Sprintf("\nerrors(%d): %v", len(errs), errs) - } - logWithError(errs.ToError(), msg) - return errs - } -} - -// validatePortNumberString checks if val is a string with a valid port number. -func validatePortNumberString(path util.Path, val interface{}) util.Errors { - scope.Debugf("validatePortNumberString %v:", val) - if !util.IsString(val) { - return util.NewErrs(fmt.Errorf("validatePortNumberString(%s) bad type %T, want string", path, val)) - } - if val.(string) == "*" || val.(string) == "" { - return nil - } - intV, err := strconv.ParseInt(val.(string), 10, 32) - if err != nil { - return util.NewErrs(fmt.Errorf("%s : %s", path, err)) - } - return validatePortNumber(path, intV) -} - -// validatePortNumber checks whether val is an integer representing a valid port number. -func validatePortNumber(path util.Path, val interface{}) util.Errors { - return validateIntRange(path, val, 0, 65535) -} - -// validateIPRangesOrStar validates IP ranges and also allow star, examples: "1.1.0.256/16,2.2.0.257/16", "*" -func validateIPRangesOrStar(path util.Path, val interface{}) (errs util.Errors) { - scope.Debugf("validateIPRangesOrStar at %v: %v", path, val) - - if !util.IsString(val) { - err := fmt.Errorf("validateIPRangesOrStar %s got %T, want string", path, val) - printError(err) - return util.NewErrs(err) - } - - if val.(string) == "*" || val.(string) == "" { - return errs - } - - return validateStringList(validateCIDR)(path, val) -} - -// validateIntRange checks whether val is an integer in [min, max]. -func validateIntRange(path util.Path, val interface{}, min, max int64) util.Errors { - k := reflect.TypeOf(val).Kind() - var err error - switch { - case util.IsIntKind(k): - v := reflect.ValueOf(val).Int() - if v < min || v > max { - err = fmt.Errorf("value %s:%v falls outside range [%v, %v]", path, v, min, max) - } - case util.IsUintKind(k): - v := reflect.ValueOf(val).Uint() - if int64(v) < min || int64(v) > max { - err = fmt.Errorf("value %s:%v falls out side range [%v, %v]", path, v, min, max) - } - default: - err = fmt.Errorf("validateIntRange %s unexpected type %T, want int type", path, val) - } - logWithError(err, "validateIntRange %s:%v in [%d, %d]?: ", path, val, min, max) - return util.NewErrs(err) -} - -// validateCIDR checks whether val is a string with a valid CIDR. -func validateCIDR(path util.Path, val interface{}) util.Errors { - var err error - if !util.IsString(val) { - err = fmt.Errorf("validateCIDR %s got %T, want string", path, val) - } else { - _, _, err = net.ParseCIDR(val.(string)) - if err != nil { - err = fmt.Errorf("%s %s", path, err) - } - } - logWithError(err, "validateCIDR (%s): ", val) - return util.NewErrs(err) -} - -func printError(err error) { - if err == nil { - scope.Debug("OK") - return - } - scope.Debugf("%v", err) -} - -// logWithError prints debug log with err message -func logWithError(err error, format string, args ...interface{}) { - msg := fmt.Sprintf(format, args...) - if err == nil { - msg += ": OK\n" - } else { - msg += fmt.Sprintf(": %v\n", err) - } - scope.Debug(msg) -} - -// match compiles the string to a regular expression. -var match = regexp.MustCompile - -// literal compiles s into a literal regular expression, escaping any regexp -// reserved characters. -func literal(s string) *regexp.Regexp { - re := match(regexp.QuoteMeta(s)) - - if _, complete := re.LiteralPrefix(); !complete { - panic("must be a literal") - } - - return re -} - -// expression defines a full expression, where each regular expression must -// follow the previous. -func expression(res ...*regexp.Regexp) *regexp.Regexp { - var s string - for _, re := range res { - s += re.String() - } - - return match(s) -} - -// optional wraps the expression in a non-capturing group and makes the -// production optional. -func optional(res ...*regexp.Regexp) *regexp.Regexp { - return match(group(expression(res...)).String() + `?`) -} - -// repeated wraps the regexp in a non-capturing group to get one or more -// matches. -func repeated(res ...*regexp.Regexp) *regexp.Regexp { - return match(group(expression(res...)).String() + `+`) -} - -// group wraps the regexp in a non-capturing group. -func group(res ...*regexp.Regexp) *regexp.Regexp { - return match(`(?:` + expression(res...).String() + `)`) -} - -// capture wraps the expression in a capturing group. -func capture(res ...*regexp.Regexp) *regexp.Regexp { - return match(`(` + expression(res...).String() + `)`) -} - -// anchored anchors the regular expression by adding start and end delimiters. -func anchored(res ...*regexp.Regexp) *regexp.Regexp { - return match(`^` + expression(res...).String() + `$`) -} - -// ValidatorFunc validates a value. -type ValidatorFunc func(path util.Path, i interface{}) util.Errors - -// UnmarshalIOP unmarshals a string containing IstioOperator as YAML. -func UnmarshalIOP(iopYAML string) (*v1alpha1.IstioOperator, error) { - // Remove creationDate (util.UnmarshalWithJSONPB fails if present) - mapIOP := make(map[string]interface{}) - if err := yaml.Unmarshal([]byte(iopYAML), &mapIOP); err != nil { - return nil, err - } - // Don't bother trying to remove the timestamp if there are no fields. - // This also preserves iopYAML if it is ""; we don't want iopYAML to be the string "null" - if len(mapIOP) > 0 { - un := &unstructured.Unstructured{Object: mapIOP} - un.SetCreationTimestamp(meta_v1.Time{}) // UnmarshalIstioOperator chokes on these - iopYAML = util.ToYAML(un) - } - iop := &v1alpha1.IstioOperator{} - - if err := yaml.UnmarshalStrict([]byte(iopYAML), iop); err != nil { - return nil, fmt.Errorf("%s:\n\nYAML:\n%s", err, iopYAML) - } - return iop, nil -} - -// ValidIOP validates the given IstioOperator object. -func ValidIOP(iop *v1alpha1.IstioOperator) error { - errs := CheckIstioOperatorSpec(iop.Spec, false) - return errs.ToError() -} - -// compose path for slice s with index i -func indexPathForSlice(s string, i int) string { - return fmt.Sprintf("%s[%d]", s, i) -} - -// get validation function for specified path -func getValidationFuncForPath(validations map[string]ValidatorFunc, path util.Path) (ValidatorFunc, bool) { - pstr := path.String() - // fast match - if !strings.Contains(pstr, "[") && !strings.Contains(pstr, "]") { - vf, ok := validations[pstr] - return vf, ok - } - for p, vf := range validations { - ps := strings.Split(p, ".") - if len(ps) != len(path) { - continue - } - for i, v := range ps { - if !matchPathNode(v, path[i]) { - break - } - if i == len(ps)-1 { - return vf, true - } - } - } - return nil, false -} - -// check whether the pn path node match pattern. -// pattern may container '*', eg. [1] match [*]. -func matchPathNode(pattern, pn string) bool { - if !strings.Contains(pattern, "[") && !strings.Contains(pattern, "]") { - return pattern == pn - } - if !strings.Contains(pn, "[") && !strings.Contains(pn, "]") { - return false - } - indexPattern := pattern[strings.IndexByte(pattern, '[')+1 : strings.IndexByte(pattern, ']')] - if indexPattern == "*" { - return true - } - index := pn[strings.IndexByte(pn, '[')+1 : strings.IndexByte(pn, ']')] - return indexPattern == index -} diff --git a/operator/pkg/validate/validate.go b/operator/pkg/validate/validate.go deleted file mode 100644 index 59db0ed05..000000000 --- a/operator/pkg/validate/validate.go +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validate - -import ( - "fmt" - "reflect" -) - -import ( - "google.golang.org/protobuf/types/known/structpb" - "istio.io/api/operator/v1alpha1" -) - -import ( - operator_v1alpha1 "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/metrics" - "github.com/apache/dubbo-go-pixiu/operator/pkg/tpath" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -var ( - // DefaultValidations maps a data path to a validation function. - DefaultValidations = map[string]ValidatorFunc{ - "Values": func(path util.Path, i interface{}) util.Errors { - return CheckValues(i) - }, - "MeshConfig": validateMeshConfig, - "Hub": validateHub, - "Tag": validateTag, - "Revision": validateRevision, - "Components.IngressGateways": validateGatewayName, - "Components.EgressGateways": validateGatewayName, - } - // requiredValues lists all the values that must be non-empty. - requiredValues = map[string]bool{} -) - -// CheckIstioOperator validates the operator CR. -func CheckIstioOperator(iop *operator_v1alpha1.IstioOperator, checkRequiredFields bool) error { - if iop == nil { - return nil - } - - errs := CheckIstioOperatorSpec(iop.Spec, checkRequiredFields) - return errs.ToError() -} - -// CheckIstioOperatorSpec validates the values in the given Installer spec, using the field map DefaultValidations to -// call the appropriate validation function. checkRequiredFields determines whether missing mandatory fields generate -// errors. -func CheckIstioOperatorSpec(is *v1alpha1.IstioOperatorSpec, checkRequiredFields bool) (errs util.Errors) { - if is == nil { - return util.Errors{} - } - - return Validate2(DefaultValidations, is) -} - -func Validate2(validations map[string]ValidatorFunc, iop *v1alpha1.IstioOperatorSpec) (errs util.Errors) { - for path, validator := range validations { - v, f, _ := tpath.GetFromStructPath(iop, path) - if f { - errs = append(errs, validator(util.PathFromString(path), v)...) - } - } - return -} - -// Validate function below is used by third party for integrations and has to be public - -// Validate validates the values of the tree using the supplied Func. -func Validate(validations map[string]ValidatorFunc, structPtr interface{}, path util.Path, checkRequired bool) (errs util.Errors) { - scope.Debugf("validate with path %s, %v (%T)", path, structPtr, structPtr) - if structPtr == nil { - return nil - } - if util.IsStruct(structPtr) { - scope.Debugf("validate path %s, skipping struct type %T", path, structPtr) - return nil - } - if !util.IsPtr(structPtr) { - metrics.CRValidationErrorTotal.Increment() - return util.NewErrs(fmt.Errorf("validate path %s, value: %v, expected ptr, got %T", path, structPtr, structPtr)) - } - structElems := reflect.ValueOf(structPtr).Elem() - if !util.IsStruct(structElems) { - metrics.CRValidationErrorTotal.Increment() - return util.NewErrs(fmt.Errorf("validate path %s, value: %v, expected struct, got %T", path, structElems, structElems)) - } - - if util.IsNilOrInvalidValue(structElems) { - return - } - - for i := 0; i < structElems.NumField(); i++ { - fieldName := structElems.Type().Field(i).Name - fieldValue := structElems.Field(i) - if !fieldValue.CanInterface() { - continue - } - kind := structElems.Type().Field(i).Type.Kind() - if a, ok := structElems.Type().Field(i).Tag.Lookup("json"); ok && a == "-" { - continue - } - - scope.Debugf("Checking field %s", fieldName) - switch kind { - case reflect.Struct: - errs = util.AppendErrs(errs, Validate(validations, fieldValue.Addr().Interface(), append(path, fieldName), checkRequired)) - case reflect.Map: - newPath := append(path, fieldName) - errs = util.AppendErrs(errs, validateLeaf(validations, newPath, fieldValue.Interface(), checkRequired)) - for _, key := range fieldValue.MapKeys() { - nnp := append(newPath, key.String()) - errs = util.AppendErrs(errs, validateLeaf(validations, nnp, fieldValue.MapIndex(key), checkRequired)) - } - case reflect.Slice: - for i := 0; i < fieldValue.Len(); i++ { - newValue := fieldValue.Index(i).Interface() - newPath := append(path, indexPathForSlice(fieldName, i)) - if util.IsStruct(newValue) || util.IsPtr(newValue) { - errs = util.AppendErrs(errs, Validate(validations, newValue, newPath, checkRequired)) - } else { - errs = util.AppendErrs(errs, validateLeaf(validations, newPath, newValue, checkRequired)) - } - } - case reflect.Ptr: - if util.IsNilOrInvalidValue(fieldValue.Elem()) { - continue - } - newPath := append(path, fieldName) - if fieldValue.Elem().Kind() == reflect.Struct { - errs = util.AppendErrs(errs, Validate(validations, fieldValue.Interface(), newPath, checkRequired)) - } else { - errs = util.AppendErrs(errs, validateLeaf(validations, newPath, fieldValue, checkRequired)) - } - default: - if structElems.Field(i).CanInterface() { - errs = util.AppendErrs(errs, validateLeaf(validations, append(path, fieldName), fieldValue.Interface(), checkRequired)) - } - } - } - if len(errs) > 0 { - metrics.CRValidationErrorTotal.Increment() - } - return errs -} - -func validateLeaf(validations map[string]ValidatorFunc, path util.Path, val interface{}, checkRequired bool) util.Errors { - pstr := path.String() - msg := fmt.Sprintf("validate %s:%v(%T) ", pstr, val, val) - if util.IsValueNil(val) || util.IsEmptyString(val) { - if checkRequired && requiredValues[pstr] { - return util.NewErrs(fmt.Errorf("field %s is required but not set", util.ToYAMLPathString(pstr))) - } - msg += fmt.Sprintf("validate %s: OK (empty value)", pstr) - scope.Debug(msg) - return nil - } - - vf, ok := getValidationFuncForPath(validations, path) - if !ok { - msg += fmt.Sprintf("validate %s: OK (no validation)", pstr) - scope.Debug(msg) - // No validation defined. - return nil - } - scope.Debug(msg) - return vf(path, val) -} - -func validateMeshConfig(path util.Path, root interface{}) util.Errors { - vs, err := util.ToYAMLGeneric(root) - if err != nil { - return util.Errors{err} - } - // ApplyMeshConfigDefaults allows unknown fields, so we first check for unknown fields - if err := protomarshal.ApplyYAMLStrict(string(vs), mesh.DefaultMeshConfig()); err != nil { - return util.Errors{fmt.Errorf("failed to unmarshall mesh config: %v", err)} - } - // This method will also perform validation automatically - if _, validErr := mesh.ApplyMeshConfigDefaults(string(vs)); validErr != nil { - return util.Errors{validErr} - } - return nil -} - -func validateHub(path util.Path, val interface{}) util.Errors { - if val == "" { - return nil - } - return validateWithRegex(path, val, ReferenceRegexp) -} - -func validateTag(path util.Path, val interface{}) util.Errors { - return validateWithRegex(path, val.(*structpb.Value).GetStringValue(), TagRegexp) -} - -func validateRevision(_ util.Path, val interface{}) util.Errors { - if val == "" { - return nil - } - if !labels.IsDNS1123Label(val.(string)) { - err := fmt.Errorf("invalid revision specified: %s", val.(string)) - return util.Errors{err} - } - return nil -} - -func validateGatewayName(path util.Path, val interface{}) (errs util.Errors) { - v := val.([]*v1alpha1.GatewaySpec) - for _, n := range v { - errs = append(errs, validateWithRegex(path, n.Name, ObjectNameRegexp)...) - } - return -} diff --git a/operator/pkg/validate/validate_test.go b/operator/pkg/validate/validate_test.go deleted file mode 100644 index fb3e76bcf..000000000 --- a/operator/pkg/validate/validate_test.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validate - -import ( - "testing" -) - -import ( - "istio.io/api/operator/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -func TestValidate(t *testing.T) { - tests := []struct { - desc string - yamlStr string - wantErrs util.Errors - }{ - { - desc: "nil success", - }, - { - desc: "complicated k8s overlay", - yamlStr: ` -profile: default -components: - ingressGateways: - - enabled: true - k8s: - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: zone - operator: In - values: - - istio`, - }, - { - desc: "CommonConfig", - yamlStr: ` -hub: docker.io/istio -tag: v1.2.3 -meshConfig: - rootNamespace: dubbo-system -values: - global: - proxy: - includeIPRanges: "1.1.0.0/16,2.2.0.0/16" - excludeIPRanges: "3.3.0.0/16,4.4.0.0/16" - -`, - }, - { - desc: "BadTag", - yamlStr: ` -hub: ?illegal-tag! -`, - wantErrs: makeErrors([]string{`invalid value Hub: ?illegal-tag!`}), - }, - { - desc: "BadHub", - yamlStr: ` -hub: docker.io:tag/istio -`, - wantErrs: makeErrors([]string{`invalid value Hub: docker.io:tag/istio`}), - }, - { - desc: "GoodURL", - yamlStr: ` -installPackagePath: /local/file/path -`, - }, - { - desc: "BadGatewayName", - yamlStr: ` -components: - ingressGateways: - - namespace: istio-ingress-ns2 - name: istio@ingress-1 - enabled: true -`, - wantErrs: makeErrors([]string{`invalid value Components.IngressGateways: istio@ingress-1`}), - }, - { - desc: "BadValuesIP", - yamlStr: ` -values: - global: - proxy: - includeIPRanges: "1.1.0.300/16,2.2.0.0/16" -`, - wantErrs: makeErrors([]string{`global.proxy.includeIPRanges invalid CIDR address: 1.1.0.300/16`}), - }, - { - desc: "EmptyValuesIP", - yamlStr: ` -values: - global: - proxy: - includeIPRanges: "" -`, - }, - { - desc: "Bad mesh config", - yamlStr: ` -meshConfig: - defaultConfig: - discoveryAddress: missingport -`, - wantErrs: makeErrors([]string{`1 error occurred: - * invalid discovery address: unable to split "missingport": address missingport: missing port in address - -`}), - }, - { - desc: "Bad mesh config values", - yamlStr: ` -values: - meshConfig: - defaultConfig: - discoveryAddress: missingport -`, - wantErrs: makeErrors([]string{`1 error occurred: - * invalid discovery address: unable to split "missingport": address missingport: missing port in address - -`}), - }, - { - desc: "Unknown mesh config", - yamlStr: ` -meshConfig: - foo: bar -`, - wantErrs: makeErrors([]string{`failed to unmarshall mesh config: unknown field "foo" in istio.mesh.v1alpha1.MeshConfig`}), - }, - { - desc: "Unknown mesh config values", - yamlStr: ` -values: - meshConfig: - foo: bar -`, - wantErrs: makeErrors([]string{`failed to unmarshall mesh config: unknown field "foo" in istio.mesh.v1alpha1.MeshConfig`}), - }, - { - desc: "Good mesh config", - yamlStr: ` -meshConfig: - defaultConfig: - discoveryAddress: istiod:15012 -`, - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - ispec := &v1alpha1.IstioOperatorSpec{} - err := util.UnmarshalWithJSONPB(tt.yamlStr, ispec, false) - if err != nil { - t.Fatalf("unmarshalWithJSONPB(%s): got error %s", tt.desc, err) - } - - errs := CheckIstioOperatorSpec(ispec, false) - if gotErrs, wantErrs := errs, tt.wantErrs; !util.EqualErrors(gotErrs, wantErrs) { - t.Errorf("ProtoToValues(%s)(%v): gotErrs:%s, wantErrs:%s", tt.desc, tt.yamlStr, gotErrs, wantErrs) - } - }) - } -} diff --git a/operator/pkg/validate/validate_values.go b/operator/pkg/validate/validate_values.go deleted file mode 100644 index 4ccde1171..000000000 --- a/operator/pkg/validate/validate_values.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validate - -import ( - "reflect" -) - -import ( - "google.golang.org/protobuf/types/known/structpb" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" -) - -// DefaultValuesValidations maps a data path to a validation function. -var DefaultValuesValidations = map[string]ValidatorFunc{ - "global.proxy.includeIPRanges": validateIPRangesOrStar, - "global.proxy.excludeIPRanges": validateIPRangesOrStar, - "global.proxy.includeInboundPorts": validateStringList(validatePortNumberString), - "global.proxy.excludeInboundPorts": validateStringList(validatePortNumberString), - "meshConfig": validateMeshConfig, -} - -// CheckValues validates the values in the given tree, which follows the Istio values.yaml schema. -func CheckValues(root interface{}) util.Errors { - v := reflect.ValueOf(root) - if root == nil || (v.Kind() == reflect.Ptr && v.IsNil()) { - return nil - } - vs, err := util.ToYAMLGeneric(root) - if err != nil { - return util.Errors{err} - } - val := &v1alpha1.Values{} - if err := util.UnmarshalWithJSONPB(string(vs), val, false); err != nil { - return util.Errors{err} - } - return ValuesValidate(DefaultValuesValidations, root.(*structpb.Struct).AsMap(), nil) -} - -// ValuesValidate validates the values of the tree using the supplied Func -func ValuesValidate(validations map[string]ValidatorFunc, node interface{}, path util.Path) (errs util.Errors) { - pstr := path.String() - scope.Debugf("ValuesValidate %s", pstr) - vf := validations[pstr] - if vf != nil { - errs = util.AppendErrs(errs, vf(path, node)) - } - - nn, ok := node.(map[string]interface{}) - if !ok { - // Leaf, nothing more to recurse. - return errs - } - for k, v := range nn { - errs = util.AppendErrs(errs, ValuesValidate(validations, v, append(path, k))) - } - - return errs -} diff --git a/operator/pkg/validate/validate_values_test.go b/operator/pkg/validate/validate_values_test.go deleted file mode 100644 index 96aa88adf..000000000 --- a/operator/pkg/validate/validate_values_test.go +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validate - -import ( - "fmt" - "os" - "path/filepath" - "testing" -) - -import ( - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/helm" - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" -) - -var repoRootDir string - -func init() { - repoRootDir = env.IstioSrc -} - -func TestValidateValues(t *testing.T) { - tests := []struct { - desc string - yamlStr string - wantErrs util.Errors - }{ - { - desc: "nil success", - }, - { - desc: "StarIPRange", - yamlStr: ` -global: - proxy: - includeIPRanges: "*" - excludeIPRanges: "*" -`, - }, - { - desc: "ProxyConfig", - yamlStr: ` -global: - podDNSSearchNamespaces: - - "my-namespace" - proxy: - includeIPRanges: "1.1.0.0/16,2.2.0.0/16" - excludeIPRanges: "3.3.0.0/16,4.4.0.0/16" - excludeInboundPorts: "333,444" - clusterDomain: "my.domain" - lifecycle: - preStop: - exec: - command: ["/bin/sh", "-c", "sleep 30"] -`, - }, - { - desc: "CNIConfig", - yamlStr: ` -cni: - cniBinDir: "/var/lib/cni/bin" - cniConfDir: "/var/run/multus/cni/net.d" -`, - }, - - { - desc: "BadIPRange", - yamlStr: ` -global: - proxy: - includeIPRanges: "1.1.0.256/16,2.2.0.257/16" - excludeIPRanges: "3.3.0.0/33,4.4.0.0/34" -`, - wantErrs: makeErrors([]string{ - `global.proxy.excludeIPRanges invalid CIDR address: 3.3.0.0/33`, - `global.proxy.excludeIPRanges invalid CIDR address: 4.4.0.0/34`, - `global.proxy.includeIPRanges invalid CIDR address: 1.1.0.256/16`, - `global.proxy.includeIPRanges invalid CIDR address: 2.2.0.257/16`, - }), - }, - { - desc: "BadIPMalformed", - yamlStr: ` -global: - proxy: - includeIPRanges: "1.2.3/16,1.2.3.x/16" -`, - wantErrs: makeErrors([]string{ - `global.proxy.includeIPRanges invalid CIDR address: 1.2.3/16`, - `global.proxy.includeIPRanges invalid CIDR address: 1.2.3.x/16`, - }), - }, - { - desc: "BadIPWithStar", - yamlStr: ` -global: - proxy: - includeIPRanges: "*,1.1.0.0/16,2.2.0.0/16" -`, - wantErrs: makeErrors([]string{`global.proxy.includeIPRanges invalid CIDR address: *`}), - }, - { - desc: "BadPortRange", - yamlStr: ` -global: - proxy: - excludeInboundPorts: "-1,444" -`, - wantErrs: makeErrors([]string{`value global.proxy.excludeInboundPorts:-1 falls outside range [0, 65535]`}), - }, - { - desc: "BadPortMalformed", - yamlStr: ` -global: - proxy: - excludeInboundPorts: "111,222x" -`, - wantErrs: makeErrors([]string{`global.proxy.excludeInboundPorts : strconv.ParseInt: parsing "222x": invalid syntax`}), - }, - { - desc: "unknown field", - yamlStr: ` -global: - proxy: - foo: "bar" -`, - wantErrs: makeErrors([]string{`unknown field "foo" in v1alpha1.ProxyConfig`}), - }, - { - desc: "unknown cni field", - yamlStr: ` -cni: - foo: "bar" -`, - wantErrs: makeErrors([]string{`unknown field "foo" in v1alpha1.CNIConfig`}), - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - root := make(map[string]interface{}) - err := yaml.Unmarshal([]byte(tt.yamlStr), &root) - if err != nil { - t.Fatalf("yaml.Unmarshal(%s): got error %s", tt.desc, err) - } - errs := CheckValues(util.MustStruct(root)) - if gotErr, wantErr := errs, tt.wantErrs; !util.EqualErrors(gotErr, wantErr) { - t.Errorf("CheckValues(%s)(%v): gotErr:%s, wantErr:%s", tt.desc, tt.yamlStr, gotErr, wantErr) - } - }) - } -} - -func TestValidateValuesFromProfile(t *testing.T) { - tests := []struct { - profile string - wantErrs util.Errors - }{ - { - profile: "default", - }, - { - profile: "demo", - }, - { - profile: "minimal", - }, - } - for _, tt := range tests { - t.Run(tt.profile, func(t *testing.T) { - pf, err := helm.ReadProfileYAML(tt.profile, filepath.Join(env.IstioSrc, "manifests")) - if err != nil { - t.Fatalf("fail to read profile: %s", tt.profile) - } - val, _, err := object.ParseK8SYAMLToIstioOperator(pf) - if err != nil { - t.Fatalf(" fail to parse profile to ISCP: (%s), got error %s", tt.profile, err) - } - errs := CheckValues(val.Spec.Values) - if gotErr, wantErr := errs, tt.wantErrs; !util.EqualErrors(gotErr, wantErr) { - t.Errorf("CheckValues of (%v): gotErr:%s, wantErr:%s", tt.profile, gotErr, wantErr) - } - }) - } -} - -func TestValidateValuesFromValuesYAMLs(t *testing.T) { - valuesYAML := "" - var allFiles []string - manifestDir := filepath.Join(repoRootDir, "manifests/charts") - for _, sd := range []string{"base", "gateways", "istio-cni", "istio-control"} { - dir := filepath.Join(manifestDir, sd) - files, err := util.FindFiles(dir, yamlFileFilter) - if err != nil { - t.Fatalf(err.Error()) - } - allFiles = append(allFiles, files...) - } - for _, f := range allFiles { - b, err := os.ReadFile(f) - if err != nil { - t.Fatal(err.Error()) - } - valuesYAML, err = util.OverlayYAML(valuesYAML, string(b)) - if err != nil { - t.Fatal(err.Error()) - } - valuesTree := make(map[string]interface{}) - if err := yaml.Unmarshal([]byte(valuesYAML), &valuesTree); err != nil { - t.Fatal(err.Error()) - } - if err := CheckValues(util.MustStruct(valuesTree)); err != nil { - t.Fatalf("file %s failed validation with: %s", f, err) - } - } -} - -func makeErrors(estr []string) util.Errors { - var errs util.Errors - for _, s := range estr { - errs = util.AppendErr(errs, fmt.Errorf("%s", s)) - } - return errs -} - -func yamlFileFilter(path string) bool { - return filepath.Base(path) == "values.yaml" -} diff --git a/operator/pkg/version/version.go b/operator/pkg/version/version.go deleted file mode 100644 index 81b894bdd..000000000 --- a/operator/pkg/version/version.go +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package version - -import ( - "fmt" - "strconv" - "strings" -) - -import ( - goversion "github.com/hashicorp/go-version" - "gopkg.in/yaml.v2" -) - -const ( - // releasePrefix is the prefix we used in http://gcr.io/istio-release for releases - releasePrefix = "release-" -) - -// NewVersionFromString creates a new Version from the provided SemVer formatted string and returns a pointer to it. -func NewVersionFromString(s string) (*Version, error) { - ver, err := goversion.NewVersion(s) - if err != nil { - return nil, err - } - - newVer := &Version{} - vv := ver.Segments() - if len(vv) > 0 { - newVer.Major = uint32(vv[0]) - } - if len(vv) > 1 { - newVer.Minor = uint32(vv[1]) - } - if len(vv) > 2 { - newVer.Patch = uint32(vv[2]) - } - - sv := strings.Split(s, "-") - if len(sv) > 0 { - newVer.Suffix = strings.Join(sv[1:], "-") - } - - return newVer, nil -} - -// IsVersionString checks whether the given string is a version string -func IsVersionString(path string) bool { - _, err := goversion.NewSemver(path) - if err != nil { - return false - } - vs := Version{} - return yaml.Unmarshal([]byte(path), &vs) == nil -} - -// TagToVersionString converts an istio container tag into a version string -func TagToVersionString(path string) (string, error) { - path = strings.TrimPrefix(path, releasePrefix) - ver, err := goversion.NewSemver(path) - if err != nil { - return "", err - } - segments := ver.Segments() - fmtParts := make([]string, len(segments)) - for i, s := range segments { - str := strconv.Itoa(s) - fmtParts[i] = str - } - return strings.Join(fmtParts, "."), nil -} - -// TagToVersionString converts an istio container tag into a version string, -// if any error, fallback to use the original tag. -func TagToVersionStringGrace(path string) string { - v, err := TagToVersionString(path) - if err != nil { - return path - } - return v -} - -// MajorVersion represents a major version. -type MajorVersion struct { - Major uint32 -} - -// MinorVersion represents a minor version. -type MinorVersion struct { - MajorVersion - Minor uint32 -} - -// PatchVersion represents a patch version. -type PatchVersion struct { - MinorVersion - Patch uint32 -} - -// Version represents a version with an optional suffix. -type Version struct { - PatchVersion - Suffix string -} - -// NewMajorVersion creates an initialized MajorVersion struct. -func NewMajorVersion(major uint32) MajorVersion { - return MajorVersion{ - Major: major, - } -} - -// NewMinorVersion creates an initialized MinorVersion struct. -func NewMinorVersion(major, minor uint32) MinorVersion { - return MinorVersion{ - MajorVersion: NewMajorVersion(major), - Minor: minor, - } -} - -// NewPatchVersion creates an initialized PatchVersion struct. -func NewPatchVersion(major, minor, patch uint32) PatchVersion { - return PatchVersion{ - MinorVersion: NewMinorVersion(major, minor), - Patch: patch, - } -} - -// NewVersion creates an initialized Version struct. -func NewVersion(major, minor, patch uint32, suffix string) Version { - return Version{ - PatchVersion: NewPatchVersion(major, minor, patch), - Suffix: suffix, - } -} - -// String implements the Stringer interface. -func (v MajorVersion) String() string { - return fmt.Sprintf("%d", v.Major) -} - -// String implements the Stringer interface. -func (v MinorVersion) String() string { - return fmt.Sprintf("%s.%d", v.MajorVersion, v.Minor) -} - -// String implements the Stringer interface. -func (v PatchVersion) String() string { - return fmt.Sprintf("%s.%d", v.MinorVersion, v.Patch) -} - -// String implements the Stringer interface. -func (v *Version) String() string { - if v.Suffix == "" { - return v.PatchVersion.String() - } - return fmt.Sprintf("%s-%s", v.PatchVersion, v.Suffix) -} - -// UnmarshalYAML implements the Unmarshaler interface. -func (v *Version) UnmarshalYAML(unmarshal func(interface{}) error) error { - s := "" - if err := unmarshal(&s); err != nil { - return err - } - out, err := NewVersionFromString(s) - if err != nil { - return err - } - *v = *out - return nil -} diff --git a/operator/pkg/version/version_test.go b/operator/pkg/version/version_test.go deleted file mode 100644 index 29d83d118..000000000 --- a/operator/pkg/version/version_test.go +++ /dev/null @@ -1,321 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package version - -import ( - "fmt" - "testing" -) - -import ( - "gopkg.in/yaml.v2" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -func TestVersion(t *testing.T) { - tests := []struct { - desc string - yamlStr string - want Version - wantErr string - }{ - { - desc: "nil success", - }, - { - desc: "major success", - yamlStr: "1", - want: NewVersion(1, 0, 0, ""), - }, - { - desc: "major fail", - yamlStr: "1..", - wantErr: `Malformed version: 1..`, - }, - { - desc: "major fail prefix", - yamlStr: ".1", - wantErr: `Malformed version: .1`, - }, - { - desc: "minor success", - yamlStr: "1.2", - want: NewVersion(1, 2, 0, ""), - }, - { - desc: "minor fail", - yamlStr: "1.1..", - wantErr: `Malformed version: 1.1..`, - }, - { - desc: "patch success", - yamlStr: "1.2.3", - want: NewVersion(1, 2, 3, ""), - }, - { - desc: "patch fail", - yamlStr: "1.1.-1", - wantErr: `Malformed version: 1.1.-1`, - }, - { - desc: "suffix success", - yamlStr: "1.2.3-istio-test", - want: NewVersion(1, 2, 3, "istio-test"), - }, - { - desc: "suffix fail", - yamlStr: ".1.1.1-something", - wantErr: `Malformed version: .1.1.1-something`, - }, - { - desc: "Malformed version fail", - yamlStr: "istio-testing-distroless", - wantErr: `Malformed version: istio-testing-distroless`, - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - got := Version{} - err := yaml.Unmarshal([]byte(tt.yamlStr), &got) - if gotErr, wantErr := errToString(err), tt.wantErr; gotErr != wantErr { - t.Fatalf("yaml.Unmarshal(%s): got error: %s, want error: %s", tt.desc, gotErr, wantErr) - } - if tt.wantErr == "" { - assert.Equal(t, got, tt.want) - } - }) - } -} - -// errToString returns the string representation of err and the empty string if -// err is nil. -func errToString(err error) string { - if err == nil { - return "" - } - return err.Error() -} - -func TestIsVersionString(t *testing.T) { - tests := []struct { - name string - ver string - want bool - }{ - { - name: "empty", - ver: "", - want: false, - }, - { - name: "unknown", - ver: "unknown", - want: false, - }, - { - name: "release branch dev", - ver: "1.4-dev", - want: true, - }, - { - name: "release", - ver: "1.4.5", - want: true, - }, - { - name: "incorrect", - ver: "1.4.xxx", - want: false, - }, - { - name: "dev sha", - ver: "a3703b76cf4745f3d56bf653ed751509be116351", - want: false, - }, - { - name: "dev sha digit prefix", - ver: "60023b76cf4745f3d56bf653ed751509be116351", - want: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := IsVersionString(tt.ver); got != tt.want { - t.Errorf("IsVersionString() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestTagToVersionString(t *testing.T) { - //type args struct { - // path string - //} - tests := []struct { - name string - // args args - want string - wantErr bool - }{ - { - name: "1.4.3", - want: "1.4.3", - wantErr: false, - }, - { - name: "1.4.3-distroless", - want: "1.4.3", - wantErr: false, - }, - { - name: "1.5.0-alpha.0", - want: "1.5.0", - wantErr: false, - }, - { - name: "1.5.0-alpha.0-distroless", - want: "1.5.0", - wantErr: false, - }, - { - name: "1.2.10", - want: "1.2.10", - wantErr: false, - }, - { - name: "1.4.0-beta.5", - want: "1.4.0", - wantErr: false, - }, - { - name: "1.3.0-rc.3", - want: "1.3.0", - wantErr: false, - }, - { - name: "1.3.0-rc.3-distroless", - want: "1.3.0", - wantErr: false, - }, - { - name: "1.5-dev", - want: "1.5.0", - wantErr: false, - }, - { - name: "1.5-dev-distroless", - want: "1.5.0", - wantErr: false, - }, - { - name: "1.5-alpha.f850909d7ac95501bbb2ae91f57df218bcf7c630", - want: "1.5.0", - wantErr: false, - }, - { - name: "1.5-alpha.f850909d7ac95501bbb2ae91f57df218bcf7c630-distroless", - want: "1.5.0", - wantErr: false, - }, - { - name: "release-1.3-20200108-10-15", - want: "1.3.0", - wantErr: false, - }, - { - name: "release-1.3-latest-daily", - want: "1.3.0", - wantErr: false, - }, - { - name: "release-1.3-20200108-10-15-distroless", - want: "1.3.0", - wantErr: false, - }, - { - name: "release-1.3-latest-daily-distroless", - want: "1.3.0", - wantErr: false, - }, - { - name: "latest", - want: "", - wantErr: true, - }, - { - name: "latest-distroless", - want: "", - wantErr: true, - }, - { - name: "999450fd4add69e26ba04d001b811863cba8175b", - want: "", - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := TagToVersionString(tt.name) - if (err != nil) != tt.wantErr { - t.Errorf("TagToVersionString() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.want { - t.Errorf("TagToVersionString() got = %v, want %v", got, tt.want) - } - }) - } -} - -func TestVersionString(t *testing.T) { - tests := map[string]struct { - version Version - want string - }{ - "with suffix": { - version: NewVersion(1, 2, 3, "xyz"), - want: "1.2.3-xyz", - }, - "without suffix": { - version: NewVersion(1, 5, 0, ""), - want: "1.5.0", - }, - } - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - got := tt.version.String() - if got != tt.want { - t.Errorf("Version.String(): got: %s, want: %s", got, tt.want) - } - }) - } -} - -func TestUnmarshalYAML(t *testing.T) { - v := &Version{} - expectedErr := fmt.Errorf("test error") - errReturn := func(interface{}) error { return expectedErr } - gotErr := v.UnmarshalYAML(errReturn) - if gotErr == nil { - t.Errorf("expected error but got nil") - } - if gotErr != expectedErr { - t.Errorf("error mismatch") - } -} diff --git a/operator/samples/cni-on.yaml b/operator/samples/cni-on.yaml deleted file mode 100644 index ae850b4d2..000000000 --- a/operator/samples/cni-on.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - components: - cni: - enabled: true diff --git a/operator/samples/default-install.yaml b/operator/samples/default-install.yaml deleted file mode 100644 index 9d4a24897..000000000 --- a/operator/samples/default-install.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -metadata: - namespace: dubbo-system - name: istio-operator -spec: - profile: default - diff --git a/operator/samples/pilot-advanced-override.yaml b/operator/samples/pilot-advanced-override.yaml deleted file mode 100644 index 4b57fd846..000000000 --- a/operator/samples/pilot-advanced-override.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - components: - pilot: - k8s: - overlays: - - kind: Deployment - name: istiod - patches: - - path: spec.template.spec.containers.[name:discovery].args.[30m] - value: "60m" # OVERRIDDEN - - path: spec.template.spec.containers.[name:discovery].ports.[containerPort:8080].containerPort - value: 8090 # OVERRIDDEN - - kind: Service - name: istiod - patches: - - path: spec.ports.[name:grpc-xds].port - value: 15099 # OVERRIDDEN diff --git a/operator/samples/pilot-k8s.yaml b/operator/samples/pilot-k8s.yaml deleted file mode 100644 index 081f55ed5..000000000 --- a/operator/samples/pilot-k8s.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - components: - pilot: - k8s: - resources: - requests: - cpu: 1000m # override from default 500m - memory: 4096Mi # ... default 2048Mi - hpaSpec: - maxReplicas: 10 # ... default 5 - minReplicas: 2 # ... default 1 diff --git a/operator/samples/values-global.yaml b/operator/samples/values-global.yaml deleted file mode 100644 index 8726f27e1..000000000 --- a/operator/samples/values-global.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - values: - global: - logging: - level: "default:warning" # override from info diff --git a/operator/samples/values-pilot.yaml b/operator/samples/values-pilot.yaml deleted file mode 100644 index bca6c2eea..000000000 --- a/operator/samples/values-pilot.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - values: - pilot: - traceSampling: 0.1 # override from 1.0 diff --git a/operator/scripts/run_update_branch.sh b/operator/scripts/run_update_branch.sh deleted file mode 100755 index ed881dce3..000000000 --- a/operator/scripts/run_update_branch.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash - -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -x -set -e - -WD=$(dirname "$0") -WD=$(cd "$WD"; pwd) -ROOT=$(dirname "$WD") - -MANIFESTS_DIR="${ROOT}/../manifests" - -function update_branch() { - local FROM="${1}" - local TO="${2}" - - if [ "${FROM}" != "${TO}" ]; then - echo "Updating version for branch ${TO}..." - # Update version string in docs. - sed -i "s|blob/${FROM}|blob/${TO}|g" "${ROOT}"/ARCHITECTURE.md - sed -i "s|blob/${FROM}|blob/${TO}|g" "${ROOT}"/README.md - # Update tag for building profiles. - find "${MANIFESTS_DIR}"/profiles -type f -exec sed -i "s/tag: ${FROM}-latest-daily/tag: ${TO}-latest-daily/g" {} \; - # Update tag for testdata. - find "${ROOT}"/cmd/mesh/testdata -type f -exec sed -i "s/tag: ${FROM}-latest-daily/tag: ${TO}-latest-daily/g" {} \; - find "${ROOT}"/pkg/values/testdata -type f -exec sed -i "s/tag: ${FROM}-latest-daily/tag: ${TO}-latest-daily/g" {} \; - # Update operator version. - find "${ROOT}"/version -type f -exec sed -r "s/[0-9]+\.[0-9]+\.[0-9]+/${OPERATOR_VERSION}/g" {} \; - fi -} - -FROM_BRANCH=${FROM_BRANCH:-master} -CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)" - -SHORT_VERSION=${CURRENT_BRANCH//release-/} -[[ ${SHORT_VERSION} =~ ^[0-9]+\.[0-9]+ ]] && SHORT_VERSION=${BASH_REMATCH[0]} -PATCH_VERSION=${PATCH_VERSION:-0} -OPERATOR_VERSION="${SHORT_VERSION}.${PATCH_VERSION}" - -update_branch "${FROM_BRANCH}" "${CURRENT_BRANCH}" diff --git a/operator/scripts/run_update_golden_snapshots.sh b/operator/scripts/run_update_golden_snapshots.sh deleted file mode 100755 index ec8d8ecd2..000000000 --- a/operator/scripts/run_update_golden_snapshots.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This command takes a snapshot of the charts and profiles which are used to generate golden files. - -# No unset vars, print commands as they're executed, and exit on any non-zero -# return code -set -u -set -x -set -e - -SCRIPTPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -ROOTDIR="${SCRIPTPATH}/../.." - -MANIFESTS_DIR="${ROOTDIR}/manifests" -CHARTS_SNAPSHOT="${ROOTDIR}/operator/cmd/mesh/testdata/manifest-generate" - -# Clean up existing files -rm -Rf "${CHARTS_SNAPSHOT:?}/data-snapshot.tar.gz" - -cd "$(mktemp -d)" -cp -Rf "${MANIFESTS_DIR}" ./ -rm -f ./**/*.md -tar cfz data-snapshot.tar.gz manifests -cp data-snapshot.tar.gz "${CHARTS_SNAPSHOT}" -rm -r "${PWD}" diff --git a/operator/scripts/update_version.sh b/operator/scripts/update_version.sh deleted file mode 100755 index 76410876c..000000000 --- a/operator/scripts/update_version.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -function update_version() { - local RELEASE_DIR="$1" - local DOCKER_HUB="$2" - local DOCKER_TAG="$3" - # Update version string in profiles. - sed -i "s|hub: gcr.io/istio-release|hub: ${DOCKER_HUB}|g" "${RELEASE_DIR}"/profiles/*.yaml - sed -i "s|tag: .*-latest-daily|tag: ${DOCKER_TAG}|g" "${RELEASE_DIR}"/profiles/*.yaml - # Update version string in global.yaml. - sed -i "s|hub: gcr.io/istio-release|hub: ${DOCKER_HUB}|g" "${RELEASE_DIR}"/charts/global.yaml - sed -i "s|tag: .*-latest-daily|tag: ${DOCKER_TAG}|g" "${RELEASE_DIR}"/charts/global.yaml -} diff --git a/operator/version/version.go b/operator/version/version.go deleted file mode 100644 index b605ea907..000000000 --- a/operator/version/version.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package version - -import ( - buildversion "istio.io/pkg/version" -) - -import ( - pkgversion "github.com/apache/dubbo-go-pixiu/operator/pkg/version" -) - -const ( - // OperatorCodeBaseVersion is the version string from the code base. - OperatorCodeBaseVersion = "1.14.0" -) - -var ( - // OperatorVersionString is the version string of this operator binary. - OperatorVersionString string - // OperatorBinaryVersion is the Istio operator version. - OperatorBinaryVersion pkgversion.Version -) - -func init() { - var err error - OperatorVersionString = OperatorCodeBaseVersion - // If dockerinfo has a tag (e.g., specified by LDFlags), we will use it as the version of operator - tag := buildversion.DockerInfo.Tag - if pkgversion.IsVersionString(tag) { - OperatorVersionString = pkgversion.TagToVersionStringGrace(tag) - } - v, err := pkgversion.NewVersionFromString(OperatorVersionString) - if err != nil { - panic(err) - } - OperatorBinaryVersion = *v -} diff --git a/pilot/cmd/pilot-agent/config/config.go b/pilot/cmd/pilot-agent/config/config.go deleted file mode 100644 index 17b65dce3..000000000 --- a/pilot/cmd/pilot-agent/config/config.go +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package config - -import ( - "fmt" - "os" - "strconv" - "strings" -) - -import ( - "google.golang.org/protobuf/types/known/wrapperspb" - "istio.io/api/annotation" - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/util/network" - "github.com/apache/dubbo-go-pixiu/pkg/bootstrap" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/validation" -) - -// ConstructProxyConfig returns proxyConfig -func ConstructProxyConfig(meshConfigFile, serviceCluster, proxyConfigEnv string, concurrency int, role *model.Proxy) (*meshconfig.ProxyConfig, error) { - annotations, err := bootstrap.ReadPodAnnotations("") - if err != nil { - if os.IsNotExist(err) { - log.Debugf("failed to read pod annotations: %v", err) - } else { - log.Warnf("failed to read pod annotations: %v", err) - } - } - var fileMeshContents string - if fileExists(meshConfigFile) { - contents, err := os.ReadFile(meshConfigFile) - if err != nil { - return nil, fmt.Errorf("failed to read mesh config file %v: %v", meshConfigFile, err) - } - fileMeshContents = string(contents) - } - meshConfig, err := getMeshConfig(fileMeshContents, annotations[annotation.ProxyConfig.Name], proxyConfigEnv, role.Type == model.SidecarProxy) - if err != nil { - return nil, err - } - proxyConfig := mesh.DefaultProxyConfig() - if meshConfig.DefaultConfig != nil { - proxyConfig = meshConfig.DefaultConfig - } - - if concurrency != 0 { - // If --concurrency is explicitly set, we will use that. Otherwise, use source determined by - // proxy config. - proxyConfig.Concurrency = &wrapperspb.Int32Value{Value: int32(concurrency)} - } - if x, ok := proxyConfig.GetClusterName().(*meshconfig.ProxyConfig_ServiceCluster); ok { - if x.ServiceCluster == "" { - proxyConfig.ClusterName = &meshconfig.ProxyConfig_ServiceCluster{ServiceCluster: serviceCluster} - } - } - // resolve statsd address - if proxyConfig.StatsdUdpAddress != "" { - addr, err := network.ResolveAddr(proxyConfig.StatsdUdpAddress) - if err != nil { - log.Warnf("resolve StatsdUdpAddress failed: %v", err) - proxyConfig.StatsdUdpAddress = "" - } else { - proxyConfig.StatsdUdpAddress = addr - } - } - if err := validation.ValidateMeshConfigProxyConfig(proxyConfig); err != nil { - return nil, err - } - return applyAnnotations(proxyConfig, annotations), nil -} - -// getMeshConfig gets the mesh config to use for proxy configuration -// 1. First we take the default config -// 2. Then we apply any settings from file (this comes from gateway mounting configmap) -// 3. Then we apply settings from environment variable (this comes from sidecar injection sticking meshconfig here) -// 4. Then we apply overrides from annotation (this comes from annotation on gateway, passed through downward API) -// -// Merging is done by replacement. Any fields present in the overlay will replace those existing fields, while -// untouched fields will remain untouched. This means lists will be replaced, not appended to, for example. -func getMeshConfig(fileOverride, annotationOverride, proxyConfigEnv string, isSidecar bool) (*meshconfig.MeshConfig, error) { - mc := mesh.DefaultMeshConfig() - // Gateway default should be concurrency unset (ie listen on all threads) - if !isSidecar { - mc.DefaultConfig.Concurrency = nil - } - - if fileOverride != "" { - log.Infof("Apply mesh config from file %v", fileOverride) - fileMesh, err := mesh.ApplyMeshConfig(fileOverride, mc) - if err != nil || fileMesh == nil { - return nil, fmt.Errorf("failed to unmarshal mesh config from file [%v]: %v", fileOverride, err) - } - mc = fileMesh - } - - if proxyConfigEnv != "" { - log.Infof("Apply proxy config from env %v", proxyConfigEnv) - envMesh, err := mesh.ApplyProxyConfig(proxyConfigEnv, mc) - if err != nil || envMesh == nil { - return nil, fmt.Errorf("failed to unmarshal mesh config from environment [%v]: %v", proxyConfigEnv, err) - } - mc = envMesh - } - - if annotationOverride != "" { - log.Infof("Apply proxy config from annotation %v", annotationOverride) - annotationMesh, err := mesh.ApplyProxyConfig(annotationOverride, mc) - if err != nil || annotationMesh == nil { - return nil, fmt.Errorf("failed to unmarshal mesh config from annotation [%v]: %v", annotationOverride, err) - } - mc = annotationMesh - } - - return mc, nil -} - -func fileExists(path string) bool { - if _, err := os.Stat(path); os.IsNotExist(err) { - return false - } - return true -} - -// Apply any overrides to proxy config from annotations -func applyAnnotations(config *meshconfig.ProxyConfig, annos map[string]string) *meshconfig.ProxyConfig { - if v, f := annos[annotation.SidecarDiscoveryAddress.Name]; f { - config.DiscoveryAddress = v - } - if v, f := annos[annotation.SidecarStatusPort.Name]; f { - p, err := strconv.Atoi(v) - if err != nil { - log.Errorf("Invalid annotation %v=%v: %v", annotation.SidecarStatusPort.Name, v, err) - } - config.StatusPort = int32(p) - } - return config -} - -func GetPilotSan(discoveryAddress string) string { - discHost := strings.Split(discoveryAddress, ":")[0] - // For local debugging - the discoveryAddress is set to localhost, but the cert issued for normal SA. - if discHost == "localhost" { - discHost = "istiod.dubbo-system.svc" - } - return discHost -} diff --git a/pilot/cmd/pilot-agent/config/config_test.go b/pilot/cmd/pilot-agent/config/config_test.go deleted file mode 100644 index f87715ce6..000000000 --- a/pilot/cmd/pilot-agent/config/config_test.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package config - -import ( - "testing" - "time" -) - -import ( - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/testing/protocmp" - "google.golang.org/protobuf/types/known/durationpb" - meshconfig "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" -) - -func TestGetMeshConfig(t *testing.T) { - meshOverride := ` -defaultConfig: - discoveryAddress: foo:123 - controlPlaneAuthPolicy: NONE - proxyMetadata: - SOME: setting - drainDuration: 1s` - proxyOverride := `discoveryAddress: foo:123 -proxyMetadata: - SOME: setting -drainDuration: 1s -controlPlaneAuthPolicy: NONE` - overridesExpected := func() *meshconfig.ProxyConfig { - m := mesh.DefaultProxyConfig() - m.DiscoveryAddress = "foo:123" - m.ProxyMetadata = map[string]string{"SOME": "setting"} - m.DrainDuration = durationpb.New(time.Second) - m.ControlPlaneAuthPolicy = meshconfig.AuthenticationPolicy_NONE - return m - }() - cases := []struct { - name string - annotation string - environment string - file string - expect *meshconfig.ProxyConfig - }{ - { - name: "Defaults", - expect: func() *meshconfig.ProxyConfig { - m := mesh.DefaultProxyConfig() - return m - }(), - }, - { - name: "Annotation Override", - annotation: proxyOverride, - expect: overridesExpected, - }, - { - name: "File Override", - file: meshOverride, - expect: overridesExpected, - }, - { - name: "Environment Override", - environment: proxyOverride, - expect: overridesExpected, - }, - { - // Hopefully no one actually has all three of these set in a real system, but we will still - // test them all together. - name: "Multiple Override", - // Order is file < env < annotation - file: ` -defaultConfig: - discoveryAddress: file:123 - proxyMetadata: - SOME: setting - drainDuration: 1s - extraStatTags: ["a"] - proxyStatsMatcher: - inclusionPrefixes: ["a"] - inclusionSuffixes: ["b"] - inclusionRegexps: ["c"] - controlPlaneAuthPolicy: NONE`, - environment: ` -discoveryAddress: environment:123 -proxyMetadata: -OTHER: option`, - annotation: ` -discoveryAddress: annotation:123 -proxyMetadata: - ANNOTATION: something -drainDuration: 5s -extraStatTags: ["b"] -proxyStatsMatcher: - inclusionPrefixes: ["a"] - inclusionSuffixes: ["e"] - inclusionRegexps: ["f"] -`, - expect: func() *meshconfig.ProxyConfig { - m := mesh.DefaultProxyConfig() - m.DiscoveryAddress = "annotation:123" - m.ProxyMetadata = map[string]string{"ANNOTATION": "something", "SOME": "setting"} - m.DrainDuration = durationpb.New(5 * time.Second) - m.ExtraStatTags = []string{"b"} - m.ProxyStatsMatcher = &meshconfig.ProxyConfig_ProxyStatsMatcher{} - m.ProxyStatsMatcher.InclusionPrefixes = []string{"a"} - m.ProxyStatsMatcher.InclusionSuffixes = []string{"e"} - m.ProxyStatsMatcher.InclusionRegexps = []string{"f"} - m.ControlPlaneAuthPolicy = meshconfig.AuthenticationPolicy_NONE - return m - }(), - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - proxyConfigEnv := tt.environment - got, err := getMeshConfig(tt.file, tt.annotation, proxyConfigEnv, true) - if err != nil { - t.Fatal(err) - } - if !cmp.Equal(got.DefaultConfig, tt.expect, protocmp.Transform()) { - t.Fatalf("got \n%v expected \n%v", got.DefaultConfig, tt.expect) - } - }) - } -} diff --git a/pilot/cmd/pilot-agent/main.go b/pilot/cmd/pilot-agent/main.go deleted file mode 100644 index 142cf3302..000000000 --- a/pilot/cmd/pilot-agent/main.go +++ /dev/null @@ -1,317 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "context" - "fmt" - "net" - "os" -) - -import ( - "github.com/spf13/cobra" - "github.com/spf13/cobra/doc" - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/collateral" - "istio.io/pkg/log" - "istio.io/pkg/version" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/config" - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/options" - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/status" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/util/network" - "github.com/apache/dubbo-go-pixiu/pkg/cmd" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/envoy" - istio_agent "github.com/apache/dubbo-go-pixiu/pkg/istio-agent" - "github.com/apache/dubbo-go-pixiu/pkg/security" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" - stsserver "github.com/apache/dubbo-go-pixiu/security/pkg/stsservice/server" - "github.com/apache/dubbo-go-pixiu/security/pkg/stsservice/tokenmanager" - cleaniptables "github.com/apache/dubbo-go-pixiu/tools/istio-clean-iptables/pkg/cmd" - iptables "github.com/apache/dubbo-go-pixiu/tools/istio-iptables/pkg/cmd" - iptableslog "github.com/apache/dubbo-go-pixiu/tools/istio-iptables/pkg/log" -) - -const ( - localHostIPv4 = "127.0.0.1" - localHostIPv6 = "[::1]" -) - -// TODO: Move most of this to pkg options. - -var ( - dnsDomain string - stsPort int - tokenManagerPlugin string - - meshConfigFile string - - // proxy config flags (named identically) - serviceCluster string - proxyLogLevel string - proxyComponentLogLevel string - concurrency int - templateFile string - loggingOptions = log.DefaultOptions() - outlierLogPath string - - rootCmd = &cobra.Command{ - Use: "pilot-agent", - Short: "Istio Pilot agent.", - Long: "Istio Pilot agent runs in the sidecar or gateway container and bootstraps Envoy.", - SilenceUsage: true, - FParseErrWhitelist: cobra.FParseErrWhitelist{ - // Allow unknown flags for backward-compatibility. - UnknownFlags: true, - }, - } - - proxyCmd = &cobra.Command{ - Use: "proxy", - Short: "XDS proxy agent", - FParseErrWhitelist: cobra.FParseErrWhitelist{ - // Allow unknown flags for backward-compatibility. - UnknownFlags: true, - }, - PersistentPreRunE: configureLogging, - RunE: func(c *cobra.Command, args []string) error { - cmd.PrintFlags(c.Flags()) - log.Infof("Version %s", version.Info.String()) - - proxy, err := initProxy(args) - if err != nil { - return err - } - proxyConfig, err := config.ConstructProxyConfig(meshConfigFile, serviceCluster, options.ProxyConfigEnv, concurrency, proxy) - if err != nil { - return fmt.Errorf("failed to get proxy config: %v", err) - } - if out, err := protomarshal.ToYAML(proxyConfig); err != nil { - log.Infof("Failed to serialize to YAML: %v", err) - } else { - log.Infof("Effective config: %s", out) - } - - secOpts, err := options.NewSecurityOptions(proxyConfig, stsPort, tokenManagerPlugin) - if err != nil { - return err - } - - // If security token service (STS) port is not zero, start STS server and - // listen on STS port for STS requests. For STS, see - // https://tools.ietf.org/html/draft-ietf-oauth-token-exchange-16. - // STS is used for stackdriver or other Envoy services using google gRPC. - if stsPort > 0 { - stsServer, err := initStsServer(proxy, secOpts.TokenManager) - if err != nil { - return err - } - defer stsServer.Stop() - } - - // If we are using a custom template file (for control plane proxy, for example), configure this. - if templateFile != "" && proxyConfig.CustomConfigFile == "" { - proxyConfig.ProxyBootstrapTemplatePath = templateFile - } - - envoyOptions := envoy.ProxyConfig{ - LogLevel: proxyLogLevel, - ComponentLogLevel: proxyComponentLogLevel, - LogAsJSON: loggingOptions.JSONEncoding, - NodeIPs: proxy.IPAddresses, - Sidecar: proxy.Type == model.SidecarProxy, - OutlierLogPath: outlierLogPath, - } - agentOptions := options.NewAgentOptions(proxy, proxyConfig) - agent := istio_agent.NewAgent(proxyConfig, agentOptions, secOpts, envoyOptions) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - // If a status port was provided, start handling status probes. - if proxyConfig.StatusPort > 0 { - if err := initStatusServer(ctx, proxy, proxyConfig, agentOptions.EnvoyPrometheusPort, agent); err != nil { - return err - } - } - - go iptableslog.ReadNFLOGSocket(ctx) - - // On SIGINT or SIGTERM, cancel the context, triggering a graceful shutdown - go cmd.WaitSignalFunc(cancel) - - // Start in process SDS, dns server, xds proxy, and Envoy. - wait, err := agent.Run(ctx) - if err != nil { - return err - } - wait() - return nil - }, - } -) - -func init() { - proxyCmd.PersistentFlags().StringVar(&dnsDomain, "domain", "", - "DNS domain suffix. If not provided uses ${POD_NAMESPACE}.svc.cluster.local") - proxyCmd.PersistentFlags().StringVar(&meshConfigFile, "meshConfig", "./etc/istio/config/mesh", - "File name for Istio mesh configuration. If not specified, a default mesh will be used. This may be overridden by "+ - "PROXY_CONFIG environment variable or proxy.istio.io/config annotation.") - proxyCmd.PersistentFlags().IntVar(&stsPort, "stsPort", 0, - "HTTP Port on which to serve Security Token Service (STS). If zero, STS service will not be provided.") - proxyCmd.PersistentFlags().StringVar(&tokenManagerPlugin, "tokenManagerPlugin", tokenmanager.GoogleTokenExchange, - "Token provider specific plugin name.") - // DEPRECATED. Flags for proxy configuration - proxyCmd.PersistentFlags().StringVar(&serviceCluster, "serviceCluster", constants.ServiceClusterName, "Service cluster") - // Log levels are provided by the library https://github.com/gabime/spdlog, used by Envoy. - proxyCmd.PersistentFlags().StringVar(&proxyLogLevel, "proxyLogLevel", "warning,misc:error", - fmt.Sprintf("The log level used to start the Envoy proxy (choose from {%s, %s, %s, %s, %s, %s, %s})."+ - "Level may also include one or more scopes, such as 'info,misc:error,upstream:debug'", - "trace", "debug", "info", "warning", "error", "critical", "off")) - proxyCmd.PersistentFlags().IntVar(&concurrency, "concurrency", 0, "number of worker threads to run") - // See https://www.envoyproxy.io/docs/envoy/latest/operations/cli#cmdoption-component-log-level - proxyCmd.PersistentFlags().StringVar(&proxyComponentLogLevel, "proxyComponentLogLevel", "", - "The component log level used to start the Envoy proxy. Deprecated, use proxyLogLevel instead") - proxyCmd.PersistentFlags().StringVar(&templateFile, "templateFile", "", - "Go template bootstrap config") - proxyCmd.PersistentFlags().StringVar(&outlierLogPath, "outlierLogPath", "", - "The log path for outlier detection") - - // Attach the Istio logging options to the command. - loggingOptions.AttachCobraFlags(rootCmd) - - cmd.AddFlags(rootCmd) - - rootCmd.AddCommand(proxyCmd) - rootCmd.AddCommand(version.CobraCommand()) - rootCmd.AddCommand(iptables.GetCommand()) - rootCmd.AddCommand(cleaniptables.GetCommand()) - - rootCmd.AddCommand(collateral.CobraCommand(rootCmd, &doc.GenManHeader{ - Title: "Istio Pilot Agent", - Section: "pilot-agent CLI", - Manual: "Istio Pilot Agent", - })) -} - -func initStatusServer(ctx context.Context, proxy *model.Proxy, proxyConfig *meshconfig.ProxyConfig, - envoyPrometheusPort int, agent *istio_agent.Agent) error { - o := options.NewStatusServerOptions(proxy, proxyConfig, agent) - o.EnvoyPrometheusPort = envoyPrometheusPort - o.Context = ctx - statusServer, err := status.NewServer(*o) - if err != nil { - return err - } - go statusServer.Run(ctx) - return nil -} - -func initStsServer(proxy *model.Proxy, tokenManager security.TokenManager) (*stsserver.Server, error) { - localHostAddr := localHostIPv4 - if proxy.IsIPv6() { - localHostAddr = localHostIPv6 - } - stsServer, err := stsserver.NewServer(stsserver.Config{ - LocalHostAddr: localHostAddr, - LocalPort: stsPort, - }, tokenManager) - if err != nil { - return nil, err - } - return stsServer, nil -} - -func getDNSDomain(podNamespace, domain string) string { - if len(domain) == 0 { - domain = podNamespace + ".svc." + constants.DefaultKubernetesDomain - } - return domain -} - -func configureLogging(_ *cobra.Command, _ []string) error { - if err := log.Configure(loggingOptions); err != nil { - return err - } - return nil -} - -func initProxy(args []string) (*model.Proxy, error) { - proxy := &model.Proxy{ - Type: model.SidecarProxy, - } - if len(args) > 0 { - proxy.Type = model.NodeType(args[0]) - if !model.IsApplicationNodeType(proxy.Type) { - return nil, fmt.Errorf("Invalid proxy Type: " + string(proxy.Type)) - } - } - - podIP := net.ParseIP(options.InstanceIPVar.Get()) // protobuf encoding of IP_ADDRESS type - if podIP != nil { - proxy.IPAddresses = []string{podIP.String()} - } - - // Obtain all the IPs from the node - if ipAddrs, ok := network.GetPrivateIPs(context.Background()); ok { - if len(proxy.IPAddresses) == 1 { - for _, ip := range ipAddrs { - // prevent duplicate ips, the first one must be the pod ip - // as we pick the first ip as pod ip in istiod - if proxy.IPAddresses[0] != ip { - proxy.IPAddresses = append(proxy.IPAddresses, ip) - } - } - } else { - proxy.IPAddresses = append(proxy.IPAddresses, ipAddrs...) - } - } - - // No IP addresses provided, append 127.0.0.1 for ipv4 and ::1 for ipv6 - if len(proxy.IPAddresses) == 0 { - proxy.IPAddresses = append(proxy.IPAddresses, localHostIPv4, localHostIPv6) - } - - // After IP addresses are set, let us discover IPMode. - proxy.DiscoverIPMode() - - // Extract pod variables. - podName := options.PodNameVar.Get() - podNamespace := options.PodNamespaceVar.Get() - proxy.ID = podName + "." + podNamespace - - // If not set, set a default based on platform - podNamespace.svc.cluster.local for - // K8S - proxy.DNSDomain = getDNSDomain(podNamespace, dnsDomain) - log.WithLabels("ips", proxy.IPAddresses, "type", proxy.Type, "id", proxy.ID, "domain", proxy.DNSDomain).Info("Proxy role") - - return proxy, nil -} - -// TODO: get the config and bootstrap from istiod, by passing the env - -// Use env variables - from injection, k8s and local namespace config map. -// No CLI parameters. -func main() { - log.EnableKlogWithCobra() - if err := rootCmd.Execute(); err != nil { - log.Error(err) - os.Exit(-1) - } -} diff --git a/pilot/cmd/pilot-agent/main_test.go b/pilot/cmd/pilot-agent/main_test.go deleted file mode 100644 index 693b4ffbe..000000000 --- a/pilot/cmd/pilot-agent/main_test.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "testing" -) - -import ( - "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" -) - -func TestPilotDefaultDomainKubernetes(t *testing.T) { - g := gomega.NewWithT(t) - role := &model.Proxy{} - role.DNSDomain = "" - - domain := getDNSDomain("default", role.DNSDomain) - - g.Expect(domain).To(gomega.Equal("default.svc.cluster.local")) -} diff --git a/pilot/cmd/pilot-agent/metrics/metrics.go b/pilot/cmd/pilot-agent/metrics/metrics.go deleted file mode 100644 index daa0e5664..000000000 --- a/pilot/cmd/pilot-agent/metrics/metrics.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package metrics - -import ( - "time" -) - -import ( - "istio.io/pkg/log" - "istio.io/pkg/monitoring" -) - -var ( - typeTag = monitoring.MustCreateLabel("type") - - // StartupTime measures the time it takes for the agent to get ready Note: This - // is dependant on readiness probes. This means our granularity is correlated to - // the probing interval. - startupTime = monitoring.NewGauge( - "startup_duration_seconds", - "The time from the process starting to being marked ready.", - ) - - // scrapeErrors records total number of failed scrapes. - scrapeErrors = monitoring.NewSum( - "scrape_failures_total", - "The total number of failed scrapes.", - monitoring.WithLabels(typeTag), - ) - EnvoyScrapeErrors = scrapeErrors.With(typeTag.Value(ScrapeTypeEnvoy)) - AppScrapeErrors = scrapeErrors.With(typeTag.Value(ScrapeTypeApp)) - AgentScrapeErrors = scrapeErrors.With(typeTag.Value(ScrapeTypeAgent)) - - // ScrapeTotals records total number of scrapes. - ScrapeTotals = monitoring.NewSum( - "scrapes_total", - "The total number of scrapes.", - ) -) - -var ( - ScrapeTypeEnvoy = "envoy" - ScrapeTypeApp = "application" - ScrapeTypeAgent = "agent" -) - -var processStartTime = time.Now() - -func RecordStartupTime() { - delta := time.Since(processStartTime) - startupTime.Record(delta.Seconds()) - log.Infof("Readiness succeeded in %v", delta) -} - -func init() { - monitoring.MustRegister( - ScrapeTotals, - scrapeErrors, - startupTime, - ) -} diff --git a/pilot/cmd/pilot-agent/options/agent.go b/pilot/cmd/pilot-agent/options/agent.go deleted file mode 100644 index 425e36f3b..000000000 --- a/pilot/cmd/pilot-agent/options/agent.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package options - -import ( - "os" - "path/filepath" - "strings" -) - -import ( - meshconfig "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/bootstrap/platform" - istioagent "github.com/apache/dubbo-go-pixiu/pkg/istio-agent" -) - -// Similar with ISTIO_META_, which is used to customize the node metadata - this customizes extra header. -const xdsHeaderPrefix = "XDS_HEADER_" - -func NewAgentOptions(proxy *model.Proxy, cfg *meshconfig.ProxyConfig) *istioagent.AgentOptions { - o := &istioagent.AgentOptions{ - XDSRootCerts: xdsRootCA, - CARootCerts: caRootCA, - XDSHeaders: map[string]string{}, - XdsUdsPath: filepath.Join(cfg.ConfigPath, "XDS"), - IsIPv6: proxy.IsIPv6(), - ProxyType: proxy.Type, - EnableDynamicProxyConfig: enableProxyConfigXdsEnv, - EnableDynamicBootstrap: enableBootstrapXdsEnv, - WASMInsecureRegistries: strings.Split(wasmInsecureRegistries, ","), - ProxyIPAddresses: proxy.IPAddresses, - ServiceNode: proxy.ServiceNode(), - EnvoyStatusPort: envoyStatusPortEnv, - EnvoyPrometheusPort: envoyPrometheusPortEnv, - MinimumDrainDuration: minimumDrainDurationEnv, - ExitOnZeroActiveConnections: exitOnZeroActiveConnectionsEnv, - Platform: platform.Discover(proxy.SupportsIPv6()), - GRPCBootstrapPath: grpcBootstrapEnv, - DisableEnvoy: disableEnvoyEnv, - ProxyXDSDebugViaAgent: proxyXDSDebugViaAgent, - ProxyXDSDebugViaAgentPort: proxyXDSDebugViaAgentPort, - DNSCapture: DNSCaptureByAgent.Get(), - DNSAddr: DNSCaptureAddr.Get(), - ProxyNamespace: PodNamespaceVar.Get(), - ProxyDomain: proxy.DNSDomain, - IstiodSAN: istiodSAN.Get(), - } - extractXDSHeadersFromEnv(o) - return o -} - -// Simplified extraction of gRPC headers from environment. -// Unlike ISTIO_META, where we need JSON and advanced features - this is just for small string headers. -func extractXDSHeadersFromEnv(o *istioagent.AgentOptions) { - envs := os.Environ() - for _, e := range envs { - if strings.HasPrefix(e, xdsHeaderPrefix) { - parts := strings.SplitN(e, "=", 2) - if len(parts) != 2 { - continue - } - o.XDSHeaders[parts[0][len(xdsHeaderPrefix):]] = parts[1] - } - } -} diff --git a/pilot/cmd/pilot-agent/options/options.go b/pilot/cmd/pilot-agent/options/options.go deleted file mode 100644 index c9f6f805e..000000000 --- a/pilot/cmd/pilot-agent/options/options.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package options - -import ( - "path/filepath" - "time" -) - -import ( - "istio.io/pkg/env" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/status" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/jwt" - "github.com/apache/dubbo-go-pixiu/pkg/security" -) - -var ( - InstanceIPVar = env.RegisterStringVar("INSTANCE_IP", "", "") - PodNameVar = env.RegisterStringVar("POD_NAME", "", "") - PodNamespaceVar = env.RegisterStringVar("POD_NAMESPACE", "", "") - kubeAppProberNameVar = env.RegisterStringVar(status.KubeAppProberEnvName, "", "") - ProxyConfigEnv = env.RegisterStringVar( - "PROXY_CONFIG", - "", - "The proxy configuration. This will be set by the injection - gateways will use file mounts.", - ).Get() - - serviceAccountVar = env.RegisterStringVar("SERVICE_ACCOUNT", "", "Name of service account") - clusterIDVar = env.RegisterStringVar("ISTIO_META_CLUSTER_ID", "", "") - // Provider for XDS auth, e.g., gcp. By default, it is empty, meaning no auth provider. - xdsAuthProvider = env.RegisterStringVar("XDS_AUTH_PROVIDER", "", "Provider for XDS auth") - - jwtPolicy = env.RegisterStringVar("JWT_POLICY", jwt.PolicyThirdParty, - "The JWT validation policy.") - // ProvCert is the environment controlling the use of pre-provisioned certs, for VMs. - // May also be used in K8S to use a Secret to bootstrap (as a 'refresh key'), but use short-lived tokens - // with extra SAN (labels, etc) in data path. - provCert = env.RegisterStringVar("PROV_CERT", "", - "Set to a directory containing provisioned certs, for VMs").Get() - - // set to "SYSTEM" for ACME/public signed XDS servers. - xdsRootCA = env.RegisterStringVar("XDS_ROOT_CA", "", - "Explicitly set the root CA to expect for the XDS connection.").Get() - - // set to "SYSTEM" for ACME/public signed CA servers. - caRootCA = env.RegisterStringVar("CA_ROOT_CA", "", - "Explicitly set the root CA to expect for the CA connection.").Get() - - outputKeyCertToDir = env.RegisterStringVar("OUTPUT_CERTS", "", - "The output directory for the key and certificate. If empty, key and certificate will not be saved. "+ - "Must be set for VMs using provisioning certificates.").Get() - - caProviderEnv = env.RegisterStringVar("CA_PROVIDER", "Citadel", "name of authentication provider").Get() - caEndpointEnv = env.RegisterStringVar("CA_ADDR", "", "Address of the spiffe certificate provider. Defaults to discoveryAddress").Get() - - trustDomainEnv = env.RegisterStringVar("TRUST_DOMAIN", "cluster.local", - "The trust domain for spiffe certificates").Get() - - secretTTLEnv = env.RegisterDurationVar("SECRET_TTL", 24*time.Hour, - "The cert lifetime requested by istio agent").Get() - - fileDebounceDuration = env.RegisterDurationVar("FILE_DEBOUNCE_DURATION", 100*time.Millisecond, - "The duration for which the file read operation is delayed once file update is detected").Get() - - secretRotationGracePeriodRatioEnv = env.RegisterFloatVar("SECRET_GRACE_PERIOD_RATIO", 0.5, - "The grace period ratio for the cert rotation, by default 0.5.").Get() - pkcs8KeysEnv = env.RegisterBoolVar("PKCS8_KEY", false, - "Whether to generate PKCS#8 private keys").Get() - eccSigAlgEnv = env.RegisterStringVar("ECC_SIGNATURE_ALGORITHM", "", "The type of ECC signature algorithm to use when generating private keys").Get() - fileMountedCertsEnv = env.RegisterBoolVar("FILE_MOUNTED_CERTS", false, "").Get() - credFetcherTypeEnv = env.RegisterStringVar("CREDENTIAL_FETCHER_TYPE", security.JWT, - "The type of the credential fetcher. Currently supported types include GoogleComputeEngine").Get() - credIdentityProvider = env.RegisterStringVar("CREDENTIAL_IDENTITY_PROVIDER", "GoogleComputeEngine", - "The identity provider for credential. Currently default supported identity provider is GoogleComputeEngine").Get() - proxyXDSDebugViaAgent = env.RegisterBoolVar("PROXY_XDS_DEBUG_VIA_AGENT", true, - "If set to true, the agent will listen on tap port and offer pilot's XDS istio.io/debug debug API there.").Get() - proxyXDSDebugViaAgentPort = env.RegisterIntVar("PROXY_XDS_DEBUG_VIA_AGENT_PORT", 15004, - "Agent debugging port.").Get() - // DNSCaptureByAgent is a copy of the env var in the init code. - DNSCaptureByAgent = env.RegisterBoolVar("ISTIO_META_DNS_CAPTURE", false, - "If set to true, enable the capture of outgoing DNS packets on port 53, redirecting to istio-agent on :15053") - - // DNSCaptureAddr is the address to listen. - DNSCaptureAddr = env.RegisterStringVar("DNS_PROXY_ADDR", "localhost:15053", - "Custom address for the DNS proxy. If it ends with :53 and running as root allows running without iptable DNS capture") - - // Ability of istio-agent to retrieve proxyConfig via XDS for dynamic configuration updates - enableProxyConfigXdsEnv = env.RegisterBoolVar("PROXY_CONFIG_XDS_AGENT", false, - "If set to true, agent retrieves dynamic proxy-config updates via xds channel").Get() - - wasmInsecureRegistries = env.RegisterStringVar("WASM_INSECURE_REGISTRIES", "", - "allow agent pull wasm plugin from insecure registries, for example: 'localhost:5000,docker-registry:5000'").Get() - - // Ability of istio-agent to retrieve bootstrap via XDS - enableBootstrapXdsEnv = env.RegisterBoolVar("BOOTSTRAP_XDS_AGENT", false, - "If set to true, agent retrieves the bootstrap configuration prior to starting Envoy").Get() - - envoyStatusPortEnv = env.RegisterIntVar("ENVOY_STATUS_PORT", 15021, - "Envoy health status port value").Get() - envoyPrometheusPortEnv = env.RegisterIntVar("ENVOY_PROMETHEUS_PORT", 15090, - "Envoy prometheus redirection port value").Get() - - // Defined by https://github.com/grpc/proposal/blob/c5722a35e71f83f07535c6c7c890cf0c58ec90c0/A27-xds-global-load-balancing.md#xdsclient-and-bootstrap-file - grpcBootstrapEnv = env.RegisterStringVar("GRPC_XDS_BOOTSTRAP", filepath.Join(constants.ConfigPathDir, "grpc-bootstrap.json"), - "Path where gRPC expects to read a bootstrap file. Agent will generate one if set.").Get() - - disableEnvoyEnv = env.RegisterBoolVar("DISABLE_ENVOY", false, - "Disables all Envoy agent features.").Get() - - // certSigner is cert signer for workload cert - certSigner = env.RegisterStringVar("ISTIO_META_CERT_SIGNER", "", - "The cert signer info for workload cert") - - istiodSAN = env.RegisterStringVar("ISTIOD_SAN", "", - "Override the ServerName used to validate Istiod certificate. "+ - "Can be used as an alternative to setting /etc/hosts for VMs - discovery address will be an IP:port") - - minimumDrainDurationEnv = env.RegisterDurationVar("MINIMUM_DRAIN_DURATION", - 5*time.Second, - "The minimum duration for which agent waits before it checks for active connections and terminates proxy"+ - "when number of active connections become zero").Get() - - exitOnZeroActiveConnectionsEnv = env.RegisterBoolVar("EXIT_ON_ZERO_ACTIVE_CONNECTIONS", - false, - "When set to true, terminates proxy when number of active connections become zero during draining").Get() -) diff --git a/pilot/cmd/pilot-agent/options/security.go b/pilot/cmd/pilot-agent/options/security.go deleted file mode 100644 index 68005cf96..000000000 --- a/pilot/cmd/pilot-agent/options/security.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package options - -import ( - "fmt" - "strings" -) - -import ( - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - securityModel "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/jwt" - "github.com/apache/dubbo-go-pixiu/pkg/security" - "github.com/apache/dubbo-go-pixiu/security/pkg/credentialfetcher" - "github.com/apache/dubbo-go-pixiu/security/pkg/nodeagent/cafile" - "github.com/apache/dubbo-go-pixiu/security/pkg/nodeagent/plugin/providers/google/stsclient" - "github.com/apache/dubbo-go-pixiu/security/pkg/stsservice/tokenmanager" -) - -func NewSecurityOptions(proxyConfig *meshconfig.ProxyConfig, stsPort int, tokenManagerPlugin string) (*security.Options, error) { - o := &security.Options{ - CAEndpoint: caEndpointEnv, - CAProviderName: caProviderEnv, - PilotCertProvider: features.PilotCertProvider, - OutputKeyCertToDir: outputKeyCertToDir, - ProvCert: provCert, - ClusterID: clusterIDVar.Get(), - FileMountedCerts: fileMountedCertsEnv, - WorkloadNamespace: PodNamespaceVar.Get(), - ServiceAccount: serviceAccountVar.Get(), - XdsAuthProvider: xdsAuthProvider.Get(), - TrustDomain: trustDomainEnv, - Pkcs8Keys: pkcs8KeysEnv, - ECCSigAlg: eccSigAlgEnv, - SecretTTL: secretTTLEnv, - FileDebounceDuration: fileDebounceDuration, - SecretRotationGracePeriodRatio: secretRotationGracePeriodRatioEnv, - STSPort: stsPort, - CertSigner: certSigner.Get(), - CARootPath: cafile.CACertFilePath, - CertChainFilePath: security.DefaultCertChainFilePath, - KeyFilePath: security.DefaultKeyFilePath, - RootCertFilePath: security.DefaultRootCertFilePath, - } - - o, err := SetupSecurityOptions(proxyConfig, o, jwtPolicy.Get(), - credFetcherTypeEnv, credIdentityProvider) - if err != nil { - return o, err - } - - var tokenManager security.TokenManager - if stsPort > 0 || xdsAuthProvider.Get() != "" { - // tokenManager is gcp token manager when using the default token manager plugin. - tokenManager, err = tokenmanager.CreateTokenManager(tokenManagerPlugin, - tokenmanager.Config{CredFetcher: o.CredFetcher, TrustDomain: o.TrustDomain}) - } - o.TokenManager = tokenManager - - return o, err -} - -func SetupSecurityOptions(proxyConfig *meshconfig.ProxyConfig, secOpt *security.Options, jwtPolicy, - credFetcherTypeEnv, credIdentityProvider string) (*security.Options, error) { - var jwtPath string - switch jwtPolicy { - case jwt.PolicyThirdParty: - log.Info("JWT policy is third-party-jwt") - jwtPath = constants.TrustworthyJWTPath - case jwt.PolicyFirstParty: - log.Info("JWT policy is first-party-jwt") - jwtPath = securityModel.K8sSAJwtFileName - default: - log.Info("Using existing certs") - } - - o := secOpt - - // If not set explicitly, default to the discovery address. - if o.CAEndpoint == "" { - o.CAEndpoint = proxyConfig.DiscoveryAddress - o.CAEndpointSAN = istiodSAN.Get() - } - - o.CredIdentityProvider = credIdentityProvider - credFetcher, err := credentialfetcher.NewCredFetcher(credFetcherTypeEnv, o.TrustDomain, jwtPath, o.CredIdentityProvider) - if err != nil { - return nil, fmt.Errorf("failed to create credential fetcher: %v", err) - } - log.Infof("using credential fetcher of %s type in %s trust domain", credFetcherTypeEnv, o.TrustDomain) - o.CredFetcher = credFetcher - - if o.CAProviderName == security.GkeWorkloadCertificateProvider { - if !security.CheckWorkloadCertificate(security.GkeWorkloadCertChainFilePath, - security.GkeWorkloadKeyFilePath, security.GkeWorkloadRootCertFilePath) { - return nil, fmt.Errorf("GKE workload certificate files (%v, %v, %v) not present", - security.GkeWorkloadCertChainFilePath, security.GkeWorkloadKeyFilePath, security.GkeWorkloadRootCertFilePath) - } - if o.ProvCert != "" { - return nil, fmt.Errorf( - "invalid options: PROV_CERT and FILE_MOUNTED_CERTS of GKE workload cert are mutually exclusive") - } - o.FileMountedCerts = true - o.CertChainFilePath = security.GkeWorkloadCertChainFilePath - o.KeyFilePath = security.GkeWorkloadKeyFilePath - o.RootCertFilePath = security.GkeWorkloadRootCertFilePath - return o, nil - } - - // Default the CA provider where possible - if strings.Contains(o.CAEndpoint, "googleapis.com") { - o.CAProviderName = security.GoogleCAProvider - } - // TODO extract this logic out to a plugin - if o.CAProviderName == security.GoogleCAProvider || o.CAProviderName == security.GoogleCASProvider { - var err error - o.TokenExchanger, err = stsclient.NewSecureTokenServiceExchanger(o.CredFetcher, o.TrustDomain) - if err != nil { - return nil, err - } - } - - if o.ProvCert != "" && o.FileMountedCerts { - return nil, fmt.Errorf("invalid options: PROV_CERT and FILE_MOUNTED_CERTS are mutually exclusive") - } - return o, nil -} diff --git a/pilot/cmd/pilot-agent/options/security_test.go b/pilot/cmd/pilot-agent/options/security_test.go deleted file mode 100644 index ddda1997b..000000000 --- a/pilot/cmd/pilot-agent/options/security_test.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package options - -import ( - "os" - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/security" -) - -func TestCheckGkeWorkloadCertificate(t *testing.T) { - cert, err := os.CreateTemp("", "existing-cert-file") - if err != nil { - t.Fatal(err) - } - defer os.Remove(cert.Name()) - - tests := []struct { - name string - paths []string - expected bool - }{ - { - name: "non-existing cert paths", - paths: []string{ - "/this-is-a-nonexisting-path-1", "/this-is-a-nonexisting-path-2", - "/this-is-a-nonexisting-path-3", - }, - expected: false, - }, - { - name: "existing cert paths", - paths: []string{cert.Name(), cert.Name(), cert.Name()}, - expected: true, - }, - { - name: "mixed non-existing and existing cert paths", - paths: []string{cert.Name(), "/this-is-a-nonexisting-path-1", "/this-is-a-nonexisting-path-2"}, - expected: false, - }, - } - for _, tt := range tests { - result := security.CheckWorkloadCertificate(tt.paths[0], tt.paths[1], tt.paths[2]) - if result != tt.expected { - t.Errorf("Test %s failed, expected: %t got: %t", tt.name, tt.expected, result) - } - } -} diff --git a/pilot/cmd/pilot-agent/options/statusserver.go b/pilot/cmd/pilot-agent/options/statusserver.go deleted file mode 100644 index f9375c549..000000000 --- a/pilot/cmd/pilot-agent/options/statusserver.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package options - -import ( - meshconfig "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/status" - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/status/ready" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - istioagent "github.com/apache/dubbo-go-pixiu/pkg/istio-agent" -) - -func NewStatusServerOptions(proxy *model.Proxy, proxyConfig *meshconfig.ProxyConfig, agent *istioagent.Agent) *status.Options { - return &status.Options{ - IPv6: proxy.IsIPv6(), - PodIP: InstanceIPVar.Get(), - AdminPort: uint16(proxyConfig.ProxyAdminPort), - StatusPort: uint16(proxyConfig.StatusPort), - KubeAppProbers: kubeAppProberNameVar.Get(), - NodeType: proxy.Type, - Probes: []ready.Prober{agent}, - NoEnvoy: agent.EnvoyDisabled(), - FetchDNS: agent.GetDNSTable, - GRPCBootstrap: agent.GRPCBootstrapPath(), - } -} diff --git a/pilot/cmd/pilot-agent/request.go b/pilot/cmd/pilot-agent/request.go deleted file mode 100644 index d5adf21e5..000000000 --- a/pilot/cmd/pilot-agent/request.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "fmt" - "net/http" - "sort" - "strings" - "time" -) - -import ( - "github.com/spf13/cobra" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/request" -) - -var ( - debugRequestPort int32 = 15000 - allowedPorts = map[int32]struct{}{ - 15000: {}, - 15021: {}, - 15020: {}, - 15004: {}, - } -) - -var allowedPortsString = func() string { - s := []string{} - for k := range allowedPorts { - s = append(s, fmt.Sprint(k)) - } - sort.Strings(s) - return strings.Join(s, ", ") -}() - -// NB: extra standard output in addition to what's returned from envoy -// must not be added in this command. Otherwise, it'd break istioctl proxy-config, -// which interprets the output literally as json document. -var ( - requestCmd = &cobra.Command{ - Use: "request []", - Short: "Makes an HTTP request to the Envoy admin API", - Args: cobra.MinimumNArgs(2), - PreRunE: func(cmd *cobra.Command, args []string) error { - if _, f := allowedPorts[debugRequestPort]; !f { - return fmt.Errorf("debug port %d is not in allowed list %v", debugRequestPort, allowedPortsString) - } - return nil - }, - RunE: func(c *cobra.Command, args []string) error { - command := &request.Command{ - Address: fmt.Sprintf("localhost:%d", debugRequestPort), - Client: &http.Client{ - Timeout: 60 * time.Second, - }, - } - body := "" - if len(args) >= 3 { - body = args[2] - } - return command.Do(args[0], args[1], body) - }, - } -) - -func init() { - rootCmd.AddCommand(requestCmd) - requestCmd.PersistentFlags().Int32Var(&debugRequestPort, "debug-port", debugRequestPort, - "Set the port to make a local request to. The default points to the Envoy admin API.") -} diff --git a/pilot/cmd/pilot-agent/status/grpcready/probe.go b/pilot/cmd/pilot-agent/status/grpcready/probe.go deleted file mode 100644 index 1b18f35ab..000000000 --- a/pilot/cmd/pilot-agent/status/grpcready/probe.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package grpcready - -import ( - "fmt" - "sync" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/status/ready" - "github.com/apache/dubbo-go-pixiu/pkg/file" - "github.com/apache/dubbo-go-pixiu/pkg/istio-agent/grpcxds" -) - -var _ ready.Prober = &probe{} - -type probe struct { - sync.RWMutex - bootstrapPath string - bootstrap *grpcxds.Bootstrap -} - -// NewProbe returns a probe that checks if a valid bootstrap file can be loaded once. -// If that bootstrap file has a file_watcher cert provider, we also ensure those certs exist. -func NewProbe(bootstrapPath string) ready.Prober { - return &probe{bootstrapPath: bootstrapPath} -} - -func (p *probe) Check() error { - // TODO file watch? - if p.getBootstrap() == nil { - bootstrap, err := grpcxds.LoadBootstrap(p.bootstrapPath) - if err != nil { - return fmt.Errorf("failed loading %s: %v", p.bootstrapPath, err) - } - p.setBootstrap(bootstrap) - } - if bootstrap := p.getBootstrap(); bootstrap != nil { - if fwp := bootstrap.FileWatcherProvider(); fwp != nil { - for _, path := range fwp.FilePaths() { - if !file.Exists(path) { - return fmt.Errorf("%s does not exist", path) - } - } - } - } - return nil -} - -func (p *probe) getBootstrap() *grpcxds.Bootstrap { - p.RLock() - defer p.RUnlock() - return p.bootstrap -} - -func (p *probe) setBootstrap(bootstrap *grpcxds.Bootstrap) { - p.Lock() - defer p.Unlock() - p.bootstrap = bootstrap -} diff --git a/pilot/cmd/pilot-agent/status/ready/probe.go b/pilot/cmd/pilot-agent/status/ready/probe.go deleted file mode 100644 index eac3bda55..000000000 --- a/pilot/cmd/pilot-agent/status/ready/probe.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ready - -import ( - "context" - "fmt" -) - -import ( - admin "github.com/envoyproxy/go-control-plane/envoy/admin/v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/metrics" - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/status/util" -) - -// Probe for readiness. -type Probe struct { - LocalHostAddr string - AdminPort uint16 - receivedFirstUpdate bool - // Indicates that Envoy is ready atleast once so that we can cache and reuse that probe. - atleastOnceReady bool - Context context.Context - // NoEnvoy so we only check config status - NoEnvoy bool -} - -type Prober interface { - // Check executes the probe and returns an error if the probe fails. - Check() error -} - -var _ Prober = &Probe{} - -// Check executes the probe and returns an error if the probe fails. -func (p *Probe) Check() error { - // First, check that Envoy has received a configuration update from Pilot. - if err := p.checkConfigStatus(); err != nil { - return err - } - return p.isEnvoyReady() -} - -// checkConfigStatus checks to make sure initial configs have been received from Pilot. -func (p *Probe) checkConfigStatus() error { - if p.NoEnvoy { - // TODO some way to verify XDS proxy -> control plane works - return nil - } - if p.receivedFirstUpdate { - return nil - } - - s, err := util.GetUpdateStatusStats(p.LocalHostAddr, p.AdminPort) - if err != nil { - return err - } - - CDSUpdated := s.CDSUpdatesSuccess > 0 - LDSUpdated := s.LDSUpdatesSuccess > 0 - if CDSUpdated && LDSUpdated { - p.receivedFirstUpdate = true - return nil - } - - if !CDSUpdated && !LDSUpdated { - return fmt.Errorf("config not received from XDS server (is Istiod running?): %s", s.String()) - } else if s.LDSUpdatesRejection > 0 || s.CDSUpdatesRejection > 0 { - return fmt.Errorf("config received from XDS server, but was rejected: %s", s.String()) - } else { - return fmt.Errorf("config not fully received from XDS server: %s", s.String()) - } -} - -// isEnvoyReady checks to ensure that Envoy is in the LIVE state and workers have started. -func (p *Probe) isEnvoyReady() error { - if p.NoEnvoy { - return nil - } - if p.Context == nil { - return p.checkEnvoyReadiness() - } - select { - case <-p.Context.Done(): - return fmt.Errorf("server is not live, current state is: %s", admin.ServerInfo_DRAINING.String()) - default: - return p.checkEnvoyReadiness() - } -} - -func (p *Probe) checkEnvoyReadiness() error { - // If Envoy is ready at least once i.e. server state is LIVE and workers - // have started, they will not go back in the life time of Envoy process. - // They will only change at hot restart or health check fails. Since istio - // does not use both of them, it is safe to cache this value. Since the - // actual readiness probe goes via Envoy, it ensures that Envoy is actively - // serving traffic and we can rely on that. - if p.atleastOnceReady { - return nil - } - - err := checkEnvoyStats(p.LocalHostAddr, p.AdminPort) - if err == nil { - metrics.RecordStartupTime() - p.atleastOnceReady = true - } - return err -} - -// checkEnvoyStats actually executes the Stats Query on Envoy admin endpoint. -func checkEnvoyStats(host string, port uint16) error { - state, ws, err := util.GetReadinessStats(host, port) - if err != nil { - return fmt.Errorf("failed to get readiness stats: %v", err) - } - - if state != nil && admin.ServerInfo_State(*state) != admin.ServerInfo_LIVE { - return fmt.Errorf("server is not live, current state is: %v", admin.ServerInfo_State(*state).String()) - } - - if !ws { - return fmt.Errorf("workers have not yet started") - } - - return nil -} diff --git a/pilot/cmd/pilot-agent/status/ready/probe_test.go b/pilot/cmd/pilot-agent/status/ready/probe_test.go deleted file mode 100644 index fb939fbe8..000000000 --- a/pilot/cmd/pilot-agent/status/ready/probe_test.go +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ready - -import ( - "context" - "net" - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/status/testserver" -) - -var ( - liveServerStats = "cluster_manager.cds.update_success: 1\nlistener_manager.lds.update_success: 1\nserver.state: 0\nlistener_manager.workers_started: 1" - onlyServerStats = "server.state: 0" - initServerStats = "cluster_manager.cds.update_success: 1\nlistener_manager.lds.update_success: 1\nserver.state: 2" - noServerStats = "" -) - -func TestEnvoyStatsCompleteAndSuccessful(t *testing.T) { - g := NewWithT(t) - - server := testserver.CreateAndStartServer(liveServerStats) - defer server.Close() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - probe := Probe{AdminPort: uint16(server.Listener.Addr().(*net.TCPAddr).Port)} - probe.Context = ctx - - err := probe.Check() - - g.Expect(err).NotTo(HaveOccurred()) -} - -func TestEnvoyDraining(t *testing.T) { - g := NewWithT(t) - - server := testserver.CreateAndStartServer(liveServerStats) - defer server.Close() - ctx, cancel := context.WithCancel(context.Background()) - probe := Probe{AdminPort: uint16(server.Listener.Addr().(*net.TCPAddr).Port), Context: ctx} - cancel() - - err := probe.isEnvoyReady() - - g.Expect(err).To(HaveOccurred()) -} - -func TestEnvoyStats(t *testing.T) { - cases := []struct { - name string - stats string - result string - }{ - { - "only lds", - "listener_manager.lds.update_success: 1", - "config not fully received from XDS server: cds updates: 0 successful, 0 rejected; lds updates: 1 successful, 0 rejected", - }, - { - "only cds", - "cluster_manager.cds.update_success: 1", - "config not fully received from XDS server: cds updates: 1 successful, 0 rejected; lds updates: 0 successful, 0 rejected", - }, - { - "reject CDS", - `cluster_manager.cds.update_rejected: 1 -listener_manager.lds.update_success: 1`, - "config received from XDS server, but was rejected: cds updates: 0 successful, 1 rejected; lds updates: 1 successful, 0 rejected", - }, - { - "no config", - ``, - "config not received from XDS server (is Istiod running?): cds updates: 0 successful, 0 rejected; lds updates: 0 successful, 0 rejected", - }, - { - "workers not started", - ` -cluster_manager.cds.update_success: 1 -listener_manager.lds.update_success: 1 -listener_manager.workers_started: 0 -server.state: 0`, - "workers have not yet started", - }, - { - "full", - ` -cluster_manager.cds.update_success: 1 -listener_manager.lds.update_success: 1 -listener_manager.workers_started: 1 -server.state: 0`, - "", - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - server := testserver.CreateAndStartServer(tt.stats) - defer server.Close() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - probe := Probe{AdminPort: uint16(server.Listener.Addr().(*net.TCPAddr).Port)} - probe.Context = ctx - err := probe.Check() - - // Expect no error - if tt.result == "" { - if err != nil { - t.Fatalf("Expected no error, got: %v", err) - } - return - } - // Expect error - if err.Error() != tt.result { - t.Fatalf("Expected: \n'%v', got: \n'%v'", tt.result, err.Error()) - } - }) - } -} - -func TestEnvoyInitializing(t *testing.T) { - g := NewWithT(t) - - server := testserver.CreateAndStartServer(initServerStats) - defer server.Close() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - probe := Probe{AdminPort: uint16(server.Listener.Addr().(*net.TCPAddr).Port)} - probe.Context = ctx - err := probe.Check() - - g.Expect(err).To(HaveOccurred()) -} - -func TestEnvoyNoClusterManagerStats(t *testing.T) { - g := NewWithT(t) - - server := testserver.CreateAndStartServer(onlyServerStats) - defer server.Close() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - probe := Probe{AdminPort: uint16(server.Listener.Addr().(*net.TCPAddr).Port)} - probe.Context = ctx - err := probe.Check() - - g.Expect(err).To(HaveOccurred()) -} - -func TestEnvoyNoServerStats(t *testing.T) { - g := NewWithT(t) - - server := testserver.CreateAndStartServer(noServerStats) - defer server.Close() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - probe := Probe{AdminPort: uint16(server.Listener.Addr().(*net.TCPAddr).Port)} - probe.Context = ctx - err := probe.Check() - - g.Expect(err).To(HaveOccurred()) -} - -func TestEnvoyReadinessCache(t *testing.T) { - g := NewWithT(t) - - server := testserver.CreateAndStartServer(noServerStats) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - probe := Probe{AdminPort: uint16(server.Listener.Addr().(*net.TCPAddr).Port)} - probe.Context = ctx - err := probe.Check() - g.Expect(err).To(HaveOccurred()) - g.Expect(probe.atleastOnceReady).Should(BeFalse()) - err = probe.Check() - g.Expect(err).To(HaveOccurred()) - g.Expect(probe.atleastOnceReady).Should(BeFalse()) - server.Close() - - server = testserver.CreateAndStartServer(liveServerStats) - probe.AdminPort = uint16(server.Listener.Addr().(*net.TCPAddr).Port) - err = probe.Check() - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(probe.atleastOnceReady).Should(BeTrue()) - server.Close() - - server = testserver.CreateAndStartServer(noServerStats) - probe.AdminPort = uint16(server.Listener.Addr().(*net.TCPAddr).Port) - err = probe.Check() - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(probe.atleastOnceReady).Should(BeTrue()) - server.Close() - - err = probe.Check() - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(probe.atleastOnceReady).Should(BeTrue()) -} diff --git a/pilot/cmd/pilot-agent/status/server.go b/pilot/cmd/pilot-agent/status/server.go deleted file mode 100644 index f353dee81..000000000 --- a/pilot/cmd/pilot-agent/status/server.go +++ /dev/null @@ -1,864 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package status - -import ( - "bytes" - "context" - "crypto/tls" - "encoding/json" - "errors" - "fmt" - "io" - "mime" - "net" - "net/http" - "net/http/pprof" - "os" - "regexp" - "strconv" - "strings" - "sync" - "syscall" - "time" -) - -import ( - ocprom "contrib.go.opencensus.io/exporter/prometheus" - "github.com/hashicorp/go-multierror" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/collectors" - "github.com/prometheus/common/expfmt" - "go.opencensus.io/stats/view" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - grpcHealth "google.golang.org/grpc/health/grpc_health_v1" - grpcStatus "google.golang.org/grpc/status" - "istio.io/pkg/env" - "istio.io/pkg/log" - "k8s.io/apimachinery/pkg/util/intstr" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/metrics" - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/status/grpcready" - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/status/ready" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" - dnsProto "github.com/apache/dubbo-go-pixiu/pkg/dns/proto" - "github.com/apache/dubbo-go-pixiu/pkg/kube/apimirror" -) - -const ( - // readyPath is for the pilot agent readiness itself. - readyPath = "/healthz/ready" - // quitPath is to notify the pilot agent to quit. - quitPath = "/quitquitquit" - // KubeAppProberEnvName is the name of the command line flag for pilot agent to pass app prober config. - // The json encoded string to pass app HTTP probe information from injector(istioctl or webhook). - // For example, ISTIO_KUBE_APP_PROBERS='{"/app-health/httpbin/livez":{"httpGet":{"path": "/hello", "port": 8080}}. - // indicates that httpbin container liveness prober port is 8080 and probing path is /hello. - // This environment variable should never be set manually. - KubeAppProberEnvName = "ISTIO_KUBE_APP_PROBERS" - - localHostIPv4 = "127.0.0.1" - localHostIPv6 = "[::1]" -) - -var ( - UpstreamLocalAddressIPv4 = &net.TCPAddr{IP: net.ParseIP("127.0.0.6")} - UpstreamLocalAddressIPv6 = &net.TCPAddr{IP: net.ParseIP("::6")} -) - -var PrometheusScrapingConfig = env.RegisterStringVar("ISTIO_PROMETHEUS_ANNOTATIONS", "", "") - -var ( - appProberPattern = regexp.MustCompile(`^/app-health/[^/]+/(livez|readyz|startupz)$`) - - promRegistry *prometheus.Registry - - LegacyLocalhostProbeDestination = env.RegisterBoolVar("REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION", false, - "If enabled, readiness probes will be sent to 'localhost'. Otherwise, they will be sent to the Pod's IP, matching Kubernetes' behavior.") - - ProbeKeepaliveConnections = env.RegisterBoolVar("ENABLE_PROBE_KEEPALIVE_CONNECTIONS", false, - "If enabled, readiness probes will keep the connection from pilot-agent to the application alive. "+ - "This mirrors older Istio versions' behaviors, but not kubelet's.").Get() -) - -// KubeAppProbers holds the information about a Kubernetes pod prober. -// It's a map from the prober URL path to the Kubernetes Prober config. -// For example, "/app-health/hello-world/livez" entry contains liveness prober config for -// container "hello-world". -type KubeAppProbers map[string]*Prober - -// Prober represents a single container prober -type Prober struct { - HTTPGet *apimirror.HTTPGetAction `json:"httpGet,omitempty"` - TCPSocket *apimirror.TCPSocketAction `json:"tcpSocket,omitempty"` - GRPC *apimirror.GRPCAction `json:"grpc,omitempty"` - TimeoutSeconds int32 `json:"timeoutSeconds,omitempty"` -} - -// Options for the status server. -type Options struct { - // Ip of the pod. Note: this is only applicable for Kubernetes pods and should only be used for - // the prober. - PodIP string - // KubeAppProbers is a json with Kubernetes application prober config encoded. - KubeAppProbers string - NodeType model.NodeType - StatusPort uint16 - AdminPort uint16 - IPv6 bool - Probes []ready.Prober - EnvoyPrometheusPort int - Context context.Context - FetchDNS func() *dnsProto.NameTable - NoEnvoy bool - GRPCBootstrap string -} - -// Server provides an endpoint for handling status probes. -type Server struct { - ready []ready.Prober - prometheus *PrometheusScrapeConfiguration - mutex sync.RWMutex - appProbersDestination string - appKubeProbers KubeAppProbers - appProbeClient map[string]*http.Client - statusPort uint16 - lastProbeSuccessful bool - envoyStatsPort int - fetchDNS func() *dnsProto.NameTable - upstreamLocalAddress *net.TCPAddr - config Options -} - -func init() { - registry := prometheus.NewRegistry() - wrapped := prometheus.WrapRegistererWithPrefix("istio_agent_", prometheus.Registerer(registry)) - wrapped.MustRegister(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{})) - wrapped.MustRegister(collectors.NewGoCollector()) - - promRegistry = registry - // go collector metrics collide with other metrics. - exporter, err := ocprom.NewExporter(ocprom.Options{Registry: registry, Registerer: wrapped}) - if err != nil { - log.Fatalf("could not setup exporter: %v", err) - } - view.RegisterExporter(exporter) -} - -// NewServer creates a new status server. -func NewServer(config Options) (*Server, error) { - localhost := localHostIPv4 - upstreamLocalAddress := UpstreamLocalAddressIPv4 - if config.IPv6 { - localhost = localHostIPv6 - upstreamLocalAddress = UpstreamLocalAddressIPv6 - } - probes := make([]ready.Prober, 0) - if !config.NoEnvoy { - probes = append(probes, &ready.Probe{ - LocalHostAddr: localhost, - AdminPort: config.AdminPort, - Context: config.Context, - NoEnvoy: config.NoEnvoy, - }) - } - - if config.GRPCBootstrap != "" { - probes = append(probes, grpcready.NewProbe(config.GRPCBootstrap)) - } - - probes = append(probes, config.Probes...) - s := &Server{ - statusPort: config.StatusPort, - ready: probes, - appProbersDestination: wrapIPv6(config.PodIP), - envoyStatsPort: config.EnvoyPrometheusPort, - fetchDNS: config.FetchDNS, - upstreamLocalAddress: upstreamLocalAddress, - config: config, - } - if LegacyLocalhostProbeDestination.Get() { - s.appProbersDestination = "localhost" - } - - // Enable prometheus server if its configured and a sidecar - // Because port 15020 is exposed in the gateway Services, we cannot safely serve this endpoint - // If we need to do this in the future, we should use envoy to do routing or have another port to make this internal - // only. For now, its not needed for gateway, as we can just get Envoy stats directly, but if we - // want to expose istio-agent metrics we may want to revisit this. - if cfg, f := PrometheusScrapingConfig.Lookup(); config.NodeType == model.SidecarProxy && f { - var prom PrometheusScrapeConfiguration - if err := json.Unmarshal([]byte(cfg), &prom); err != nil { - return nil, fmt.Errorf("failed to unmarshal %s: %v", PrometheusScrapingConfig.Name, err) - } - log.Infof("Prometheus scraping configuration: %v", prom) - if prom.Scrape != "false" { - s.prometheus = &prom - if s.prometheus.Path == "" { - s.prometheus.Path = "/metrics" - } - if s.prometheus.Port == "" { - s.prometheus.Port = "80" - } - if s.prometheus.Port == strconv.Itoa(int(config.StatusPort)) { - return nil, fmt.Errorf("invalid prometheus scrape configuration: "+ - "application port is the same as agent port, which may lead to a recursive loop. "+ - "Ensure pod does not have prometheus.io/port=%d label, or that injection is not happening multiple times", config.StatusPort) - } - } - } - - if config.KubeAppProbers == "" { - return s, nil - } - if err := json.Unmarshal([]byte(config.KubeAppProbers), &s.appKubeProbers); err != nil { - return nil, fmt.Errorf("failed to decode app prober err = %v, json string = %v", err, config.KubeAppProbers) - } - - s.appProbeClient = make(map[string]*http.Client, len(s.appKubeProbers)) - // Validate the map key matching the regex pattern. - for path, prober := range s.appKubeProbers { - err := validateAppKubeProber(path, prober) - if err != nil { - return nil, err - } - if prober.HTTPGet != nil { - d := &net.Dialer{ - LocalAddr: s.upstreamLocalAddress, - } - // Construct a http client and cache it in order to reuse the connection. - s.appProbeClient[path] = &http.Client{ - Timeout: time.Duration(prober.TimeoutSeconds) * time.Second, - // We skip the verification since kubelet skips the verification for HTTPS prober as well - // https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - DialContext: d.DialContext, - // https://github.com/kubernetes/kubernetes/blob/0153febd9f0098d4b8d0d484927710eaf899ef40/pkg/probe/http/http.go#L55 - // Match Kubernetes logic. This also ensures idle timeouts do not trigger probe failures - DisableKeepAlives: !ProbeKeepaliveConnections, - }, - CheckRedirect: redirectChecker(), - } - } - } - - return s, nil -} - -// Copies logic from https://github.com/kubernetes/kubernetes/blob/b152001f459/pkg/probe/http/http.go#L129-L130 -func isRedirect(code int) bool { - return code >= http.StatusMultipleChoices && code < http.StatusBadRequest -} - -// Using the same redirect logic that kubelet does: https://github.com/kubernetes/kubernetes/blob/b152001f459/pkg/probe/http/http.go#L141 -// This means that: -// * If we exceed 10 redirects, the probe fails -// * If we redirect somewhere external, the probe succeeds (https://github.com/kubernetes/kubernetes/blob/b152001f459/pkg/probe/http/http.go#L130) -// * If we redirect to the same address, the probe will follow the redirect -func redirectChecker() func(*http.Request, []*http.Request) error { - return func(req *http.Request, via []*http.Request) error { - if req.URL.Hostname() != via[0].URL.Hostname() { - return http.ErrUseLastResponse - } - // Default behavior: stop after 10 redirects. - if len(via) >= 10 { - return errors.New("stopped after 10 redirects") - } - return nil - } -} - -func validateAppKubeProber(path string, prober *Prober) error { - if !appProberPattern.MatchString(path) { - return fmt.Errorf(`invalid path, must be in form of regex pattern %v`, appProberPattern) - } - count := 0 - if prober.HTTPGet != nil { - count++ - } - if prober.TCPSocket != nil { - count++ - } - if prober.GRPC != nil { - count++ - } - if count != 1 { - return fmt.Errorf(`invalid prober type, must be one of type httpGet, tcpSocket or gRPC`) - } - if prober.HTTPGet != nil && prober.HTTPGet.Port.Type != intstr.Int { - return fmt.Errorf("invalid prober config for %v, the port must be int type", path) - } - if prober.TCPSocket != nil && prober.TCPSocket.Port.Type != intstr.Int { - return fmt.Errorf("invalid prober config for %v, the port must be int type", path) - } - return nil -} - -// FormatProberURL returns a set of HTTP URLs that pilot agent will serve to take over Kubernetes -// app probers. -func FormatProberURL(container string) (string, string, string) { - return fmt.Sprintf("/app-health/%v/readyz", container), - fmt.Sprintf("/app-health/%v/livez", container), - fmt.Sprintf("/app-health/%v/startupz", container) -} - -// Run opens a the status port and begins accepting probes. -func (s *Server) Run(ctx context.Context) { - log.Infof("Opening status port %d", s.statusPort) - - mux := http.NewServeMux() - - // Add the handler for ready probes. - mux.HandleFunc(readyPath, s.handleReadyProbe) - // Default path for prom - mux.HandleFunc(`/metrics`, s.handleStats) - // Envoy uses something else - and original agent used the same. - // Keep for backward compat with configs. - mux.HandleFunc(`/stats/prometheus`, s.handleStats) - mux.HandleFunc(quitPath, s.handleQuit) - mux.HandleFunc("/app-health/", s.handleAppProbe) - - // Add the handler for pprof. - mux.HandleFunc("/debug/pprof/", s.handlePprofIndex) - mux.HandleFunc("/debug/pprof/cmdline", s.handlePprofCmdline) - mux.HandleFunc("/debug/pprof/profile", s.handlePprofProfile) - mux.HandleFunc("/debug/pprof/symbol", s.handlePprofSymbol) - mux.HandleFunc("/debug/pprof/trace", s.handlePprofTrace) - mux.HandleFunc("/debug/ndsz", s.handleNdsz) - - l, err := net.Listen("tcp", fmt.Sprintf(":%d", s.statusPort)) - if err != nil { - log.Errorf("Error listening on status port: %v", err.Error()) - return - } - // for testing. - if s.statusPort == 0 { - addrs := strings.Split(l.Addr().String(), ":") - allocatedPort, _ := strconv.Atoi(addrs[len(addrs)-1]) - s.mutex.Lock() - s.statusPort = uint16(allocatedPort) - s.mutex.Unlock() - } - defer l.Close() - - go func() { - if err := http.Serve(l, mux); err != nil { - log.Error(err) - select { - case <-ctx.Done(): - // We are shutting down already, don't trigger SIGTERM - return - default: - // If the server errors then pilot-agent can never pass readiness or liveness probes - // Therefore, trigger graceful termination by sending SIGTERM to the binary pid - notifyExit() - } - } - }() - - // Wait for the agent to be shut down. - <-ctx.Done() - log.Info("Status server has successfully terminated") -} - -func (s *Server) handlePprofIndex(w http.ResponseWriter, r *http.Request) { - if !isRequestFromLocalhost(r) { - http.Error(w, "Only requests from localhost are allowed", http.StatusForbidden) - return - } - - pprof.Index(w, r) -} - -func (s *Server) handlePprofCmdline(w http.ResponseWriter, r *http.Request) { - if !isRequestFromLocalhost(r) { - http.Error(w, "Only requests from localhost are allowed", http.StatusForbidden) - return - } - - pprof.Cmdline(w, r) -} - -func (s *Server) handlePprofSymbol(w http.ResponseWriter, r *http.Request) { - if !isRequestFromLocalhost(r) { - http.Error(w, "Only requests from localhost are allowed", http.StatusForbidden) - return - } - - pprof.Symbol(w, r) -} - -func (s *Server) handlePprofProfile(w http.ResponseWriter, r *http.Request) { - if !isRequestFromLocalhost(r) { - http.Error(w, "Only requests from localhost are allowed", http.StatusForbidden) - return - } - - pprof.Profile(w, r) -} - -func (s *Server) handlePprofTrace(w http.ResponseWriter, r *http.Request) { - if !isRequestFromLocalhost(r) { - http.Error(w, "Only requests from localhost are allowed", http.StatusForbidden) - return - } - - pprof.Trace(w, r) -} - -func (s *Server) handleReadyProbe(w http.ResponseWriter, _ *http.Request) { - err := s.isReady() - s.mutex.Lock() - if err != nil { - w.WriteHeader(http.StatusServiceUnavailable) - - log.Warnf("Envoy proxy is NOT ready: %s", err.Error()) - s.lastProbeSuccessful = false - } else { - w.WriteHeader(http.StatusOK) - - if !s.lastProbeSuccessful { - log.Info("Envoy proxy is ready") - } - s.lastProbeSuccessful = true - } - s.mutex.Unlock() -} - -func (s *Server) isReady() error { - for _, p := range s.ready { - if err := p.Check(); err != nil { - return err - } - } - return nil -} - -func isRequestFromLocalhost(r *http.Request) bool { - ip, _, err := net.SplitHostPort(r.RemoteAddr) - if err != nil { - return false - } - - userIP := net.ParseIP(ip) - return userIP.IsLoopback() -} - -type PrometheusScrapeConfiguration struct { - Scrape string `json:"scrape"` - Path string `json:"path"` - Port string `json:"port"` -} - -// handleStats handles prometheus stats scraping. This will scrape envoy metrics, and, if configured, -// the application metrics and merge them together. -// The merge here is a simple string concatenation. This works for almost all cases, assuming the application -// is not exposing the same metrics as Envoy. -// This merging works for both FmtText and FmtOpenMetrics and will use the format of the application metrics -// Note that we do not return any errors here. If we do, we will drop metrics. For example, the app may be having issues, -// but we still want Envoy metrics. Instead, errors are tracked in the failed scrape metrics/logs. -func (s *Server) handleStats(w http.ResponseWriter, r *http.Request) { - metrics.ScrapeTotals.Increment() - var envoy, application, agent []byte - var err error - // Gather all the metrics we will merge - if !s.config.NoEnvoy { - if envoy, _, err = s.scrape(fmt.Sprintf("http://localhost:%d/stats/prometheus", s.envoyStatsPort), r.Header); err != nil { - log.Errorf("failed scraping envoy metrics: %v", err) - metrics.EnvoyScrapeErrors.Increment() - } - // Process envoy's metrics to make them compatible with FmtOpenMetrics - envoy = processMetrics(envoy) - } - // Scrape app metrics if defined and capture their format - var format expfmt.Format - if s.prometheus != nil { - var contentType string - url := fmt.Sprintf("http://localhost:%s%s", s.prometheus.Port, s.prometheus.Path) - if application, contentType, err = s.scrape(url, r.Header); err != nil { - log.Errorf("failed scraping application metrics: %v", err) - metrics.AppScrapeErrors.Increment() - } - format = negotiateMetricsFormat(contentType) - } else { - // Without app metrics format use a default - format = expfmt.FmtText - } - - if agent, err = scrapeAgentMetrics(); err != nil { - log.Errorf("failed scraping agent metrics: %v", err) - metrics.AgentScrapeErrors.Increment() - } - - w.Header().Set("Content-Type", string(format)) - - // Write out the metrics - if _, err := w.Write(agent); err != nil { - log.Errorf("failed to write agent metrics: %v", err) - metrics.AgentScrapeErrors.Increment() - } - if envoy != nil { - if _, err := w.Write(envoy); err != nil { - log.Errorf("failed to write envoy metrics: %v", err) - metrics.EnvoyScrapeErrors.Increment() - } - } - // App metrics must go last because if they are FmtOpenMetrics, - // they will have a trailing "# EOF" which terminates the full exposition - if _, err := w.Write(application); err != nil { - log.Errorf("failed to write application metrics: %v", err) - metrics.AppScrapeErrors.Increment() - } -} - -func negotiateMetricsFormat(contentType string) expfmt.Format { - mediaType, _, err := mime.ParseMediaType(contentType) - if err == nil && mediaType == expfmt.OpenMetricsType { - return expfmt.FmtOpenMetrics - } - return expfmt.FmtText -} - -func processMetrics(metrics []byte) []byte { - return bytes.ReplaceAll(metrics, []byte("\n\n"), []byte("\n")) -} - -func scrapeAgentMetrics() ([]byte, error) { - buf := &bytes.Buffer{} - mfs, err := promRegistry.Gather() - enc := expfmt.NewEncoder(buf, expfmt.FmtText) - if err != nil { - return nil, err - } - var errs error - for _, mf := range mfs { - if err := enc.Encode(mf); err != nil { - errs = multierror.Append(errs, err) - } - } - return buf.Bytes(), errs -} - -func applyHeaders(into http.Header, from http.Header, keys ...string) { - for _, key := range keys { - val := from.Get(key) - if val != "" { - into.Set(key, val) - } - } -} - -// getHeaderTimeout parse a string like (1.234) representing number of seconds -func getHeaderTimeout(timeout string) (time.Duration, error) { - timeoutSeconds, err := strconv.ParseFloat(timeout, 64) - if err != nil { - return 0 * time.Second, err - } - - return time.Duration(timeoutSeconds * 1e9), nil -} - -// scrape will send a request to the provided url to scrape metrics from -// This will attempt to mimic some of Prometheus functionality by passing some of the headers through -// such as accept, timeout, and user agent -// Returns the scraped metrics as well as the response's "Content-Type" header to determine the metrics format -func (s *Server) scrape(url string, header http.Header) ([]byte, string, error) { - ctx := context.Background() - if timeoutString := header.Get("X-Prometheus-Scrape-Timeout-Seconds"); timeoutString != "" { - timeout, err := getHeaderTimeout(timeoutString) - if err != nil { - log.Warnf("Failed to parse timeout header %v: %v", timeoutString, err) - } else { - c, cancel := context.WithTimeout(ctx, timeout) - ctx = c - defer cancel() - } - } - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, "", err - } - applyHeaders(req.Header, header, "Accept", - "User-Agent", - "X-Prometheus-Scrape-Timeout-Seconds", - ) - - resp, err := http.DefaultClient.Do(req) - if err != nil { - return nil, "", fmt.Errorf("error scraping %s: %v", url, err) - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return nil, "", fmt.Errorf("error scraping %s, status code: %v", url, resp.StatusCode) - } - metrics, err := io.ReadAll(resp.Body) - if err != nil { - return nil, "", fmt.Errorf("error reading %s: %v", url, err) - } - - format := resp.Header.Get("Content-Type") - return metrics, format, nil -} - -func (s *Server) handleQuit(w http.ResponseWriter, r *http.Request) { - if !isRequestFromLocalhost(r) { - http.Error(w, "Only requests from localhost are allowed", http.StatusForbidden) - return - } - if r.Method != http.MethodPost { - http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) - return - } - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("OK")) - log.Infof("handling %s, notifying pilot-agent to exit", quitPath) - notifyExit() -} - -func (s *Server) handleAppProbe(w http.ResponseWriter, req *http.Request) { - // Validate the request first. - path := req.URL.Path - if !strings.HasPrefix(path, "/") { - path = "/" + req.URL.Path - } - prober, exists := s.appKubeProbers[path] - if !exists { - log.Errorf("Prober does not exists url %v", path) - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte(fmt.Sprintf("app prober config does not exists for %v", path))) - return - } - - switch { - case prober.HTTPGet != nil: - s.handleAppProbeHTTPGet(w, req, prober, path) - case prober.TCPSocket != nil: - s.handleAppProbeTCPSocket(w, prober) - case prober.GRPC != nil: - s.handleAppProbeGRPC(w, req, prober) - } -} - -func (s *Server) handleAppProbeHTTPGet(w http.ResponseWriter, req *http.Request, prober *Prober, path string) { - proberPath := prober.HTTPGet.Path - if !strings.HasPrefix(proberPath, "/") { - proberPath = "/" + proberPath - } - var url string - if prober.HTTPGet.Scheme == apimirror.URISchemeHTTPS { - url = fmt.Sprintf("https://%s:%v%s", s.appProbersDestination, prober.HTTPGet.Port.IntValue(), proberPath) - } else { - url = fmt.Sprintf("http://%s:%v%s", s.appProbersDestination, prober.HTTPGet.Port.IntValue(), proberPath) - } - appReq, err := http.NewRequest(http.MethodGet, url, nil) - if err != nil { - log.Errorf("Failed to create request to probe app %v, original url %v", err, path) - w.WriteHeader(http.StatusInternalServerError) - return - } - - // Forward incoming headers to the application. - for name, values := range req.Header { - newValues := make([]string, len(values)) - copy(newValues, values) - appReq.Header[name] = newValues - } - - // If there are custom HTTPHeaders, it will override the forwarding header - if headers := prober.HTTPGet.HTTPHeaders; len(headers) != 0 { - for _, h := range headers { - delete(appReq.Header, h.Name) - } - for _, h := range headers { - if h.Name == "Host" || h.Name == ":authority" { - // Probe has specific host header override; honor it - appReq.Host = h.Value - appReq.Header.Set(h.Name, h.Value) - } else { - appReq.Header.Add(h.Name, h.Value) - } - } - } - - // get the http client must exist because - httpClient := s.appProbeClient[path] - - // Send the request. - response, err := httpClient.Do(appReq) - if err != nil { - log.Errorf("Request to probe app failed: %v, original URL path = %v\napp URL path = %v", err, path, proberPath) - w.WriteHeader(http.StatusInternalServerError) - return - } - defer func() { - // Drain and close the body to let the Transport reuse the connection - _, _ = io.Copy(io.Discard, response.Body) - _ = response.Body.Close() - }() - - if isRedirect(response.StatusCode) { // Redirect - // In other cases, we return the original status code. For redirects, it is illegal to - // not have Location header, so we need to switch to just 200. - w.WriteHeader(http.StatusOK) - return - } - // We only write the status code to the response. - w.WriteHeader(response.StatusCode) -} - -func (s *Server) handleAppProbeTCPSocket(w http.ResponseWriter, prober *Prober) { - port := prober.TCPSocket.Port.IntValue() - timeout := time.Duration(prober.TimeoutSeconds) * time.Second - - d := &net.Dialer{ - LocalAddr: s.upstreamLocalAddress, - Timeout: timeout, - } - - conn, err := d.Dial("tcp", fmt.Sprintf("%s:%d", s.appProbersDestination, port)) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - } else { - w.WriteHeader(http.StatusOK) - conn.Close() - } -} - -func (s *Server) handleAppProbeGRPC(w http.ResponseWriter, req *http.Request, prober *Prober) { - timeout := time.Duration(prober.TimeoutSeconds) * time.Second - // the DialOptions are referenced from https://github.com/kubernetes/kubernetes/blob/v1.23.1/pkg/probe/grpc/grpc.go#L55-L59 - opts := []grpc.DialOption{ - grpc.WithBlock(), - grpc.WithTransportCredentials(insecure.NewCredentials()), // credentials are currently not supported - grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) { - d := net.Dialer{ - LocalAddr: s.upstreamLocalAddress, - Timeout: timeout, - } - return d.DialContext(ctx, "tcp", addr) - }), - } - if userAgent := req.Header["User-Agent"]; len(userAgent) > 0 { - // simulate kubelet - // please refer to: - // https://github.com/kubernetes/kubernetes/blob/v1.23.1/pkg/probe/grpc/grpc.go#L56 - // https://github.com/kubernetes/kubernetes/blob/v1.23.1/pkg/probe/http/http.go#L103 - opts = append(opts, grpc.WithUserAgent(userAgent[0])) - } - - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - addr := fmt.Sprintf("%s:%d", s.appProbersDestination, prober.GRPC.Port) - conn, err := grpc.DialContext(ctx, addr, opts...) - if err != nil { - log.Errorf("Failed to create grpc connection to probe app: %v", err) - w.WriteHeader(http.StatusInternalServerError) - return - } - defer conn.Close() - - var svc string - if prober.GRPC.Service != nil { - svc = *prober.GRPC.Service - } - grpcClient := grpcHealth.NewHealthClient(conn) - resp, err := grpcClient.Check(ctx, &grpcHealth.HealthCheckRequest{ - Service: svc, - }) - // the error handling is referenced from https://github.com/kubernetes/kubernetes/blob/v1.23.1/pkg/probe/grpc/grpc.go#L88-L106 - if err != nil { - status, ok := grpcStatus.FromError(err) - if ok { - switch status.Code() { - case codes.Unimplemented: - log.Errorf("server does not implement the grpc health protocol (grpc.health.v1.Health): %v", err) - case codes.DeadlineExceeded: - log.Errorf("grpc request not finished within timeout: %v", err) - default: - log.Errorf("grpc probe failed: %v", err) - } - } else { - log.Errorf("grpc probe failed: %v", err) - } - w.WriteHeader(http.StatusInternalServerError) - return - } - - if resp.GetStatus() == grpcHealth.HealthCheckResponse_SERVING { - w.WriteHeader(http.StatusOK) - return - } - w.WriteHeader(http.StatusInternalServerError) -} - -func (s *Server) handleNdsz(w http.ResponseWriter, r *http.Request) { - if !isRequestFromLocalhost(r) { - http.Error(w, "Only requests from localhost are allowed", http.StatusForbidden) - return - } - nametable := s.fetchDNS() - if nametable == nil { - // See https://golang.org/doc/faq#nil_error for why writeJSONProto cannot handle this - w.WriteHeader(http.StatusNotFound) - _, _ = w.Write([]byte(`{}`)) - return - } - writeJSONProto(w, nametable) -} - -// writeJSONProto writes a protobuf to a json payload, handling content type, marshaling, and errors -func writeJSONProto(w http.ResponseWriter, obj interface{}) { - w.Header().Set("Content-Type", "application/json") - b, err := config.ToJSON(obj) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - _, _ = w.Write([]byte(err.Error())) - return - } - _, err = w.Write(b) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - } -} - -// notifyExit sends SIGTERM to itself -func notifyExit() { - p, err := os.FindProcess(os.Getpid()) - if err != nil { - log.Error(err) - } - if err := p.Signal(syscall.SIGTERM); err != nil { - log.Errorf("failed to send SIGTERM to self: %v", err) - } -} - -// wrapIPv6 wraps the ip into "[]" in case of ipv6 -func wrapIPv6(ipAddr string) string { - addr := net.ParseIP(ipAddr) - if addr == nil { - return ipAddr - } - if addr.To4() != nil { - return ipAddr - } - return fmt.Sprintf("[%s]", ipAddr) -} diff --git a/pilot/cmd/pilot-agent/status/server_test.go b/pilot/cmd/pilot-agent/status/server_test.go deleted file mode 100644 index adfb2ec76..000000000 --- a/pilot/cmd/pilot-agent/status/server_test.go +++ /dev/null @@ -1,1385 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package status - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "io" - "mime" - "net" - "net/http" - "net/http/httptest" - "os" - "os/signal" - "reflect" - "strconv" - "strings" - "syscall" - "testing" - "time" -) - -import ( - "github.com/prometheus/common/expfmt" - "github.com/prometheus/prometheus/pkg/textparse" - "google.golang.org/grpc" - "google.golang.org/grpc/health" - grpcHealth "google.golang.org/grpc/health/grpc_health_v1" - "istio.io/pkg/log" - "k8s.io/apimachinery/pkg/util/intstr" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/status/ready" - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/status/testserver" - "github.com/apache/dubbo-go-pixiu/pkg/kube/apimirror" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -type handler struct{} - -const ( - testHeader = "Some-Header" - testHeaderValue = "some-value" - testHostValue = "host" -) - -var liveServerStats = "cluster_manager.cds.update_success: 1\nlistener_manager.lds.update_success: 1\nserver.state: 0\nlistener_manager.workers_started: 1" - -func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - segments := strings.Split(r.URL.Path[1:], "/") - switch segments[0] { - case "header": - if r.Host != testHostValue { - log.Errorf("Missing expected host header, got %v", r.Host) - w.WriteHeader(http.StatusBadRequest) - } - if r.Header.Get(testHeader) != testHeaderValue { - log.Errorf("Missing expected Some-Header, got %v", r.Header) - w.WriteHeader(http.StatusBadRequest) - } - case "redirect": - http.Redirect(w, r, "/", http.StatusMovedPermanently) - case "redirect-loop": - http.Redirect(w, r, "/redirect-loop", http.StatusMovedPermanently) - case "remote-redirect": - http.Redirect(w, r, "http://example.com/foo", http.StatusMovedPermanently) - case "", "hello/sunnyvale": - w.Write([]byte("welcome, it works")) - case "status": - code, _ := strconv.Atoi(segments[1]) - w.Header().Set("Location", "/") - w.WriteHeader(code) - default: - return - } -} - -func TestNewServer(t *testing.T) { - testCases := []struct { - probe string - err string - }{ - // Json can't be parsed. - { - probe: "invalid-prober-json-encoding", - err: "failed to decode", - }, - // map key is not well formed. - { - probe: `{"abc": {"path": "/app-foo/health"}}`, - err: "invalid path", - }, - // invalid probe type - { - probe: `{"/app-health/hello-world/readyz": {"exec": {"command": [ "true" ]}}}`, - err: "invalid prober type", - }, - // tcp probes are valid as well - { - probe: `{"/app-health/hello-world/readyz": {"tcpSocket": {"port": 8888}}}`, - }, - // probes must be one of tcp, http or gRPC - { - probe: `{"/app-health/hello-world/readyz": {"tcpSocket": {"port": 8888}, "httpGet": {"path": "/", "port": 7777}}}`, - err: "must be one of type httpGet, tcpSocket or gRPC", - }, - // probes must be one of tcp, http or gRPC - { - probe: `{"/app-health/hello-world/readyz": {"grpc": {"port": 8888}, "httpGet": {"path": "/", "port": 7777}}}`, - err: "must be one of type httpGet, tcpSocket or gRPC", - }, - // Port is not Int typed (tcpSocket). - { - probe: `{"/app-health/hello-world/readyz": {"tcpSocket": {"port": "tcp"}}}`, - err: "must be int type", - }, - // Port is not Int typed (httpGet). - { - probe: `{"/app-health/hello-world/readyz": {"httpGet": {"path": "/hello/sunnyvale", "port": "container-port-dontknow"}}}`, - err: "must be int type", - }, - // A valid input. - { - probe: `{"/app-health/hello-world/readyz": {"httpGet": {"path": "/hello/sunnyvale", "port": 8080}},` + - `"/app-health/business/livez": {"httpGet": {"path": "/buisiness/live", "port": 9090}}}`, - }, - // long request timeout - { - probe: `{"/app-health/hello-world/readyz": {"httpGet": {"path": "/hello/sunnyvale", "port": 8080},` + - `"initialDelaySeconds": 120,"timeoutSeconds": 10,"periodSeconds": 20}}`, - }, - // A valid input with empty probing path, which happens when HTTPGetAction.Path is not specified. - { - probe: `{"/app-health/hello-world/readyz": {"httpGet": {"path": "/hello/sunnyvale", "port": 8080}}, -"/app-health/business/livez": {"httpGet": {"port": 9090}}}`, - }, - // A valid input without any prober info. - { - probe: `{}`, - }, - // A valid input with probing path not starting with /, which happens when HTTPGetAction.Path does not start with a /. - { - probe: `{"/app-health/hello-world/readyz": {"httpGet": {"path": "hello/sunnyvale", "port": 8080}}, -"/app-health/business/livez": {"httpGet": {"port": 9090}}}`, - }, - // A valid gRPC probe. - { - probe: `{"/app-health/hello-world/readyz": {"gRPC": {"port": 8080}}}`, - }, - // A valid gRPC probe with null service. - { - probe: `{"/app-health/hello-world/readyz": {"gRPC": {"port": 8080, "service": null}}}`, - }, - // A valid gRPC probe with service. - { - probe: `{"/app-health/hello-world/readyz": {"gRPC": {"port": 8080, "service": "foo"}}}`, - }, - // A valid gRPC probe with service and timeout. - { - probe: `{"/app-health/hello-world/readyz": {"gRPC": {"port": 8080, "service": "foo"}, "timeoutSeconds": 10}}`, - }, - } - for _, tc := range testCases { - _, err := NewServer(Options{ - KubeAppProbers: tc.probe, - }) - - if err == nil { - if tc.err != "" { - t.Errorf("test case failed [%v], expect error %v", tc.probe, tc.err) - } - continue - } - if tc.err == "" { - t.Errorf("test case failed [%v], expect no error, got %v", tc.probe, err) - } - // error case, error string should match. - if !strings.Contains(err.Error(), tc.err) { - t.Errorf("test case failed [%v], expect error %v, got %v", tc.probe, tc.err, err) - } - } -} - -func TestPprof(t *testing.T) { - pprofPath := "/debug/pprof/cmdline" - // Starts the pilot agent status server. - server, err := NewServer(Options{StatusPort: 0}) - if err != nil { - t.Fatalf("failed to create status server %v", err) - } - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - go server.Run(ctx) - - var statusPort uint16 - for statusPort == 0 { - server.mutex.RLock() - statusPort = server.statusPort - server.mutex.RUnlock() - } - - client := http.Client{} - req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost:%v/%s", statusPort, pprofPath), nil) - if err != nil { - t.Fatalf("[%v] failed to create request", pprofPath) - } - resp, err := client.Do(req) - if err != nil { - t.Fatal("request failed: ", err) - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - t.Errorf("[%v] unexpected status code, want = %v, got = %v", pprofPath, http.StatusOK, resp.StatusCode) - } -} - -func TestStats(t *testing.T) { - cases := []struct { - name string - envoy string - app string - output string - expectParseError bool - }{ - { - name: "envoy metric only", - envoy: `# TYPE my_metric counter -my_metric{} 0 -# TYPE my_other_metric counter -my_other_metric{} 0 -`, - output: `# TYPE my_metric counter -my_metric{} 0 -# TYPE my_other_metric counter -my_other_metric{} 0 -`, - }, - { - name: "app metric only", - app: `# TYPE my_metric counter -my_metric{} 0 -# TYPE my_other_metric counter -my_other_metric{} 0 -`, - output: `# TYPE my_metric counter -my_metric{} 0 -# TYPE my_other_metric counter -my_other_metric{} 0 -`, - }, - { - name: "multiple metric", - envoy: `# TYPE my_metric counter -my_metric{} 0 -`, - app: `# TYPE my_other_metric counter -my_other_metric{} 0 -`, - output: `# TYPE my_metric counter -my_metric{} 0 -# TYPE my_other_metric counter -my_other_metric{} 0 -`, - }, - { - name: "agent metric", - envoy: ``, - app: ``, - // Agent metric is dynamic, so we just check a substring of it not the actual metric - output: ` -# TYPE istio_agent_scrapes_total counter -istio_agent_scrapes_total`, - }, - // When the application and envoy share a metric, Prometheus will fail. This negative check validates this - // assumption. - { - name: "conflict metric", - envoy: `# TYPE my_metric counter -my_metric{} 0 -# TYPE my_other_metric counter -my_other_metric{} 0 -`, - app: `# TYPE my_metric counter -my_metric{} 0 -`, - output: `# TYPE my_metric counter -my_metric{} 0 -# TYPE my_other_metric counter -my_other_metric{} 0 -# TYPE my_metric counter -my_metric{} 0 -`, - expectParseError: true, - }, - { - name: "conflict metric labeled", - envoy: `# TYPE my_metric counter -my_metric{app="foo"} 0 -`, - app: `# TYPE my_metric counter -my_metric{app="bar"} 0 -`, - output: `# TYPE my_metric counter -my_metric{app="foo"} 0 -# TYPE my_metric counter -my_metric{app="bar"} 0 -`, - expectParseError: true, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - rec := httptest.NewRecorder() - envoy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if _, err := w.Write([]byte(tt.envoy)); err != nil { - t.Fatalf("write failed: %v", err) - } - })) - defer envoy.Close() - app := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if _, err := w.Write([]byte(tt.app)); err != nil { - t.Fatalf("write failed: %v", err) - } - })) - defer app.Close() - envoyPort, err := strconv.Atoi(strings.Split(envoy.URL, ":")[2]) - if err != nil { - t.Fatal(err) - } - server := &Server{ - prometheus: &PrometheusScrapeConfiguration{ - Port: strings.Split(app.URL, ":")[2], - }, - envoyStatsPort: envoyPort, - } - req := &http.Request{} - server.handleStats(rec, req) - if rec.Code != 200 { - t.Fatalf("handleStats() => %v; want 200", rec.Code) - } - if !strings.Contains(rec.Body.String(), tt.output) { - t.Fatalf("handleStats() => %v; want %v", rec.Body.String(), tt.output) - } - - parser := expfmt.TextParser{} - mfMap, err := parser.TextToMetricFamilies(strings.NewReader(rec.Body.String())) - if err != nil && !tt.expectParseError { - t.Fatalf("failed to parse metrics: %v", err) - } else if err == nil && tt.expectParseError { - t.Fatalf("expected a prse error, got %+v", mfMap) - } - }) - } -} - -func TestStatsContentType(t *testing.T) { - appOpenMetrics := `# TYPE jvm info -# HELP jvm VM version info -jvm_info{runtime="OpenJDK Runtime Environment",vendor="AdoptOpenJDK",version="16.0.1+9"} 1.0 -# TYPE jmx_config_reload_success counter -# HELP jmx_config_reload_success Number of times configuration have successfully been reloaded. -jmx_config_reload_success_total 0.0 -jmx_config_reload_success_created 1.623984612719E9 -# EOF -` - appText004 := `# HELP jvm_info VM version info -# TYPE jvm_info gauge -jvm_info{runtime="OpenJDK Runtime Environment",vendor="AdoptOpenJDK",version="16.0.1+9",} 1.0 -# HELP jmx_config_reload_failure_created Number of times configuration have failed to be reloaded. -# TYPE jmx_config_reload_failure_created gauge -jmx_config_reload_failure_created 1.624025983489E9 -` - envoy := `# TYPE my_metric counter -my_metric{} 0 - -# TYPE my_other_metric counter -my_other_metric{} 0 -` - cases := []struct { - name string - acceptHeader string - expectParseError bool - }{ - { - name: "openmetrics accept header", - acceptHeader: `application/openmetrics-text; version=0.0.1,text/plain;version=0.0.4;q=0.5,*/*;q=0.1`, - }, - { - name: "plaintext accept header", - acceptHeader: string(expfmt.FmtText), - }, - { - name: "empty accept header", - acceptHeader: "", - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - rec := httptest.NewRecorder() - envoy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if _, err := w.Write([]byte(envoy)); err != nil { - t.Fatalf("write failed: %v", err) - } - })) - defer envoy.Close() - app := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - format := expfmt.NegotiateIncludingOpenMetrics(r.Header) - var negotiatedMetrics string - if format == expfmt.FmtOpenMetrics { - negotiatedMetrics = appOpenMetrics - w.Header().Set("Content-Type", string(expfmt.FmtOpenMetrics)) - } else { - negotiatedMetrics = appText004 - w.Header().Set("Content-Type", string(expfmt.FmtText)) - } - if _, err := w.Write([]byte(negotiatedMetrics)); err != nil { - t.Fatalf("write failed: %v", err) - } - })) - defer app.Close() - envoyPort, err := strconv.Atoi(strings.Split(envoy.URL, ":")[2]) - if err != nil { - t.Fatal(err) - } - server := &Server{ - prometheus: &PrometheusScrapeConfiguration{ - Port: strings.Split(app.URL, ":")[2], - }, - envoyStatsPort: envoyPort, - } - req := &http.Request{} - req.Header = make(http.Header) - req.Header.Add("Accept", tt.acceptHeader) - server.handleStats(rec, req) - if rec.Code != 200 { - t.Fatalf("handleStats() => %v; want 200", rec.Code) - } - - var format expfmt.Format - mediaType, _, err := mime.ParseMediaType(rec.Header().Get("Content-Type")) - if err == nil && mediaType == "application/openmetrics-text" { - format = expfmt.FmtOpenMetrics - } else { - format = expfmt.FmtText - } - - if format == expfmt.FmtOpenMetrics { - omParser := textparse.NewOpenMetricsParser(rec.Body.Bytes()) - for { - _, err := omParser.Next() - if err == io.EOF { - break - } - if err != nil { - t.Fatalf("failed to parse openmetrics: %v", err) - } - } - } else { - textParser := expfmt.TextParser{} - _, err := textParser.TextToMetricFamilies(strings.NewReader(rec.Body.String())) - if err != nil { - t.Fatalf("failed to parse text metrics: %v", err) - } - } - }) - } -} - -func TestStatsError(t *testing.T) { - fail := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusInternalServerError) - })) - defer fail.Close() - pass := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - })) - defer pass.Close() - failPort, err := strconv.Atoi(strings.Split(fail.URL, ":")[2]) - if err != nil { - t.Fatal(err) - } - passPort, err := strconv.Atoi(strings.Split(pass.URL, ":")[2]) - if err != nil { - t.Fatal(err) - } - cases := []struct { - name string - envoy int - app int - }{ - {"both pass", passPort, passPort}, - {"envoy pass", passPort, failPort}, - {"app pass", failPort, passPort}, - {"both fail", failPort, failPort}, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - server := &Server{ - prometheus: &PrometheusScrapeConfiguration{ - Port: strconv.Itoa(tt.app), - }, - envoyStatsPort: tt.envoy, - } - req := &http.Request{} - rec := httptest.NewRecorder() - server.handleStats(rec, req) - if rec.Code != 200 { - t.Fatalf("handleStats() => %v; want 200", rec.Code) - } - }) - } -} - -func TestAppProbe(t *testing.T) { - // Starts the application first. - listener, err := net.Listen("tcp", ":0") - if err != nil { - t.Errorf("failed to allocate unused port %v", err) - } - go http.Serve(listener, &handler{}) - appPort := listener.Addr().(*net.TCPAddr).Port - - simpleHTTPConfig := KubeAppProbers{ - "/app-health/hello-world/readyz": &Prober{ - HTTPGet: &apimirror.HTTPGetAction{ - Path: "/hello/sunnyvale", - Port: intstr.IntOrString{IntVal: int32(appPort)}, - }, - }, - "/app-health/hello-world/livez": &Prober{ - HTTPGet: &apimirror.HTTPGetAction{ - Port: intstr.IntOrString{IntVal: int32(appPort)}, - }, - }, - } - simpleTCPConfig := KubeAppProbers{ - "/app-health/hello-world/readyz": &Prober{ - TCPSocket: &apimirror.TCPSocketAction{ - Port: intstr.IntOrString{IntVal: int32(appPort)}, - }, - }, - "/app-health/hello-world/livez": &Prober{ - TCPSocket: &apimirror.TCPSocketAction{ - Port: intstr.IntOrString{IntVal: int32(appPort)}, - }, - }, - } - - type test struct { - name string - probePath string - config KubeAppProbers - podIP string - ipv6 bool - statusCode int - } - testCases := []test{ - { - name: "http-bad-path", - probePath: "bad-path-should-be-404", - config: simpleHTTPConfig, - statusCode: http.StatusNotFound, - }, - { - name: "http-readyz", - probePath: "app-health/hello-world/readyz", - config: simpleHTTPConfig, - statusCode: http.StatusOK, - }, - { - name: "http-livez", - probePath: "app-health/hello-world/livez", - config: simpleHTTPConfig, - statusCode: http.StatusOK, - }, - { - name: "http-livez-localhost", - probePath: "app-health/hello-world/livez", - config: simpleHTTPConfig, - statusCode: http.StatusOK, - podIP: "localhost", - }, - { - name: "http-readyz-header", - probePath: "app-health/header/readyz", - config: KubeAppProbers{ - "/app-health/header/readyz": &Prober{ - HTTPGet: &apimirror.HTTPGetAction{ - Port: intstr.IntOrString{IntVal: int32(appPort)}, - Path: "/header", - HTTPHeaders: []apimirror.HTTPHeader{ - {Name: "Host", Value: testHostValue}, - {Name: testHeader, Value: testHeaderValue}, - }, - }, - }, - }, - statusCode: http.StatusOK, - }, - { - name: "http-readyz-path", - probePath: "app-health/hello-world/readyz", - config: KubeAppProbers{ - "/app-health/hello-world/readyz": &Prober{ - HTTPGet: &apimirror.HTTPGetAction{ - Path: "hello/texas", - Port: intstr.IntOrString{IntVal: int32(appPort)}, - }, - }, - }, - statusCode: http.StatusOK, - }, - { - name: "http-livez-path", - probePath: "app-health/hello-world/livez", - config: KubeAppProbers{ - "/app-health/hello-world/livez": &Prober{ - HTTPGet: &apimirror.HTTPGetAction{ - Path: "hello/texas", - Port: intstr.IntOrString{IntVal: int32(appPort)}, - }, - }, - }, - statusCode: http.StatusOK, - }, - { - name: "tcp-readyz", - probePath: "app-health/hello-world/readyz", - config: simpleTCPConfig, - statusCode: http.StatusOK, - }, - { - name: "tcp-livez", - probePath: "app-health/hello-world/livez", - config: simpleTCPConfig, - statusCode: http.StatusOK, - }, - { - name: "tcp-livez-ipv4", - probePath: "app-health/hello-world/livez", - config: simpleTCPConfig, - statusCode: http.StatusOK, - podIP: "127.0.0.1", - }, - { - name: "tcp-livez-ipv6", - probePath: "app-health/hello-world/livez", - config: simpleTCPConfig, - statusCode: http.StatusOK, - podIP: "::1", - ipv6: true, - }, - { - name: "tcp-livez-wrapped-ipv6", - probePath: "app-health/hello-world/livez", - config: simpleTCPConfig, - statusCode: http.StatusOK, - podIP: "[::1]", - ipv6: true, - }, - { - name: "tcp-livez-localhost", - probePath: "app-health/hello-world/livez", - config: simpleTCPConfig, - statusCode: http.StatusOK, - podIP: "localhost", - }, - { - name: "redirect", - probePath: "app-health/redirect/livez", - config: KubeAppProbers{ - "/app-health/redirect/livez": &Prober{ - HTTPGet: &apimirror.HTTPGetAction{ - Path: "redirect", - Port: intstr.IntOrString{IntVal: int32(appPort)}, - }, - }, - }, - statusCode: http.StatusOK, - }, - { - name: "redirect loop", - probePath: "app-health/redirect-loop/livez", - config: KubeAppProbers{ - "/app-health/redirect-loop/livez": &Prober{ - HTTPGet: &apimirror.HTTPGetAction{ - Path: "redirect-loop", - Port: intstr.IntOrString{IntVal: int32(appPort)}, - }, - }, - }, - statusCode: http.StatusInternalServerError, - }, - { - name: "remote redirect", - probePath: "app-health/remote-redirect/livez", - config: KubeAppProbers{ - "/app-health/remote-redirect/livez": &Prober{ - HTTPGet: &apimirror.HTTPGetAction{ - Path: "remote-redirect", - Port: intstr.IntOrString{IntVal: int32(appPort)}, - }, - }, - }, - statusCode: http.StatusOK, - }, - } - testFn := func(t *testing.T, tc test) { - appProber, err := json.Marshal(tc.config) - if err != nil { - t.Fatalf("invalid app probers") - } - config := Options{ - StatusPort: 0, - KubeAppProbers: string(appProber), - PodIP: tc.podIP, - IPv6: tc.ipv6, - } - // Starts the pilot agent status server. - server, err := NewServer(config) - if err != nil { - t.Fatalf("failed to create status server %v", err) - } - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - go server.Run(ctx) - - if tc.ipv6 { - server.upstreamLocalAddress = &net.TCPAddr{IP: net.ParseIP("::1")} // required because ::6 is NOT a loopback address (IPv6 only has ::1) - } - - var statusPort uint16 - for statusPort == 0 { - server.mutex.RLock() - statusPort = server.statusPort - server.mutex.RUnlock() - } - - client := http.Client{} - req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost:%v/%s", statusPort, tc.probePath), nil) - if err != nil { - t.Fatalf("[%v] failed to create request", tc.probePath) - } - resp, err := client.Do(req) - if err != nil { - t.Fatal("request failed: ", err) - } - defer resp.Body.Close() - if resp.StatusCode != tc.statusCode { - t.Errorf("[%v] unexpected status code, want = %v, got = %v", tc.probePath, tc.statusCode, resp.StatusCode) - } - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { testFn(t, tc) }) - } - // Next we check ever - t.Run("status codes", func(t *testing.T) { - for code := http.StatusOK; code <= http.StatusNetworkAuthenticationRequired; code++ { - if http.StatusText(code) == "" { // Not a valid HTTP code - continue - } - expect := code - if isRedirect(code) { - expect = 200 - } - t.Run(fmt.Sprint(code), func(t *testing.T) { - testFn(t, test{ - probePath: "app-health/code/livez", - config: KubeAppProbers{ - "/app-health/code/livez": &Prober{ - TimeoutSeconds: 1, - HTTPGet: &apimirror.HTTPGetAction{ - Path: fmt.Sprintf("status/%d", code), - Port: intstr.IntOrString{IntVal: int32(appPort)}, - }, - }, - }, - statusCode: expect, - }) - }) - } - }) -} - -func TestHttpsAppProbe(t *testing.T) { - // Starts the application first. - listener, err := net.Listen("tcp", ":0") - if err != nil { - t.Errorf("failed to allocate unused port %v", err) - } - keyFile := env.IstioSrc + "/pilot/cmd/pilot-agent/status/test-cert/cert.key" - crtFile := env.IstioSrc + "/pilot/cmd/pilot-agent/status/test-cert/cert.crt" - go http.ServeTLS(listener, &handler{}, crtFile, keyFile) - appPort := listener.Addr().(*net.TCPAddr).Port - - // Starts the pilot agent status server. - server, err := NewServer(Options{ - StatusPort: 0, - KubeAppProbers: fmt.Sprintf(`{"/app-health/hello-world/readyz": {"httpGet": {"path": "/hello/sunnyvale", "port": %v, "scheme": "HTTPS"}}, -"/app-health/hello-world/livez": {"httpGet": {"port": %v, "scheme": "HTTPS"}}}`, appPort, appPort), - }) - if err != nil { - t.Errorf("failed to create status server %v", err) - return - } - go server.Run(context.Background()) - - var statusPort uint16 - if err := retry.UntilSuccess(func() error { - server.mutex.RLock() - statusPort = server.statusPort - server.mutex.RUnlock() - if statusPort == 0 { - return fmt.Errorf("no port allocated") - } - return nil - }); err != nil { - t.Fatalf("failed to getport: %v", err) - } - t.Logf("status server starts at port %v, app starts at port %v", statusPort, appPort) - testCases := []struct { - probePath string - statusCode int - }{ - { - probePath: fmt.Sprintf(":%v/bad-path-should-be-disallowed", statusPort), - statusCode: http.StatusNotFound, - }, - { - probePath: fmt.Sprintf(":%v/app-health/hello-world/readyz", statusPort), - statusCode: http.StatusOK, - }, - { - probePath: fmt.Sprintf(":%v/app-health/hello-world/livez", statusPort), - statusCode: http.StatusOK, - }, - } - for _, tc := range testCases { - client := http.Client{} - req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost%s", tc.probePath), nil) - if err != nil { - t.Errorf("[%v] failed to create request", tc.probePath) - } - resp, err := client.Do(req) - if err != nil { - t.Fatal("request failed") - } - defer resp.Body.Close() - if resp.StatusCode != tc.statusCode { - t.Errorf("[%v] unexpected status code, want = %v, got = %v", tc.probePath, tc.statusCode, resp.StatusCode) - } - } -} - -func TestGRPCAppProbe(t *testing.T) { - appServer := grpc.NewServer() - healthServer := health.NewServer() - healthServer.SetServingStatus("serving-svc", grpcHealth.HealthCheckResponse_SERVING) - healthServer.SetServingStatus("unknown-svc", grpcHealth.HealthCheckResponse_UNKNOWN) - healthServer.SetServingStatus("not-serving-svc", grpcHealth.HealthCheckResponse_NOT_SERVING) - grpcHealth.RegisterHealthServer(appServer, healthServer) - - listener, err := net.Listen("tcp", ":0") - if err != nil { - t.Errorf("failed to allocate unused port %v", err) - } - go appServer.Serve(listener) - defer appServer.GracefulStop() - - appPort := listener.Addr().(*net.TCPAddr).Port - // Starts the pilot agent status server. - server, err := NewServer(Options{ - StatusPort: 0, - KubeAppProbers: fmt.Sprintf(` -{ - "/app-health/foo/livez": { - "grpc": { - "port": %v, - "service": null - }, - "timeoutSeconds": 1 - }, - "/app-health/foo/readyz": { - "grpc": { - "port": %v, - "service": "not-serving-svc" - }, - "timeoutSeconds": 1 - }, - "/app-health/bar/livez": { - "grpc": { - "port": %v, - "service": "serving-svc" - }, - "timeoutSeconds": 10 - }, - "/app-health/bar/readyz": { - "grpc": { - "port": %v, - "service": "unknown-svc" - }, - "timeoutSeconds": 10 - } -}`, appPort, appPort, appPort, appPort), - }) - if err != nil { - t.Errorf("failed to create status server %v", err) - return - } - go server.Run(context.Background()) - - var statusPort uint16 - if err := retry.UntilSuccess(func() error { - server.mutex.RLock() - statusPort = server.statusPort - server.mutex.RUnlock() - if statusPort == 0 { - return fmt.Errorf("no port allocated") - } - return nil - }); err != nil { - t.Fatalf("failed to getport: %v", err) - } - t.Logf("status server starts at port %v, app starts at port %v", statusPort, appPort) - - testCases := []struct { - probePath string - statusCode int - }{ - { - probePath: fmt.Sprintf(":%v/bad-path-should-be-disallowed", statusPort), - statusCode: http.StatusNotFound, - }, - { - probePath: fmt.Sprintf(":%v/app-health/foo/livez", statusPort), - statusCode: http.StatusOK, - }, - { - probePath: fmt.Sprintf(":%v/app-health/foo/readyz", statusPort), - statusCode: http.StatusInternalServerError, - }, - { - probePath: fmt.Sprintf(":%v/app-health/bar/livez", statusPort), - statusCode: http.StatusOK, - }, - { - probePath: fmt.Sprintf(":%v/app-health/bar/readyz", statusPort), - statusCode: http.StatusInternalServerError, - }, - } - for _, tc := range testCases { - client := http.Client{} - req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost%s", tc.probePath), nil) - if err != nil { - t.Errorf("[%v] failed to create request", tc.probePath) - } - resp, err := client.Do(req) - if err != nil { - t.Fatal("request failed") - } - defer resp.Body.Close() - if resp.StatusCode != tc.statusCode { - t.Errorf("[%v] unexpected status code, want = %v, got = %v", tc.probePath, tc.statusCode, resp.StatusCode) - } - } -} - -func TestGRPCAppProbeWithIPV6(t *testing.T) { - appServer := grpc.NewServer() - healthServer := health.NewServer() - healthServer.SetServingStatus("serving-svc", grpcHealth.HealthCheckResponse_SERVING) - healthServer.SetServingStatus("unknown-svc", grpcHealth.HealthCheckResponse_UNKNOWN) - healthServer.SetServingStatus("not-serving-svc", grpcHealth.HealthCheckResponse_NOT_SERVING) - grpcHealth.RegisterHealthServer(appServer, healthServer) - - listener, err := net.Listen("tcp", ":0") - if err != nil { - t.Errorf("failed to allocate unused port %v", err) - } - go appServer.Serve(listener) - defer appServer.GracefulStop() - - appPort := listener.Addr().(*net.TCPAddr).Port - // Starts the pilot agent status server. - server, err := NewServer(Options{ - StatusPort: 0, - IPv6: true, - PodIP: "::1", - KubeAppProbers: fmt.Sprintf(` -{ - "/app-health/foo/livez": { - "grpc": { - "port": %v, - "service": null - }, - "timeoutSeconds": 1 - }, - "/app-health/foo/readyz": { - "grpc": { - "port": %v, - "service": "not-serving-svc" - }, - "timeoutSeconds": 1 - }, - "/app-health/bar/livez": { - "grpc": { - "port": %v, - "service": "serving-svc" - }, - "timeoutSeconds": 10 - }, - "/app-health/bar/readyz": { - "grpc": { - "port": %v, - "service": "unknown-svc" - }, - "timeoutSeconds": 10 - } -}`, appPort, appPort, appPort, appPort), - }) - if err != nil { - t.Errorf("failed to create status server %v", err) - return - } - - server.upstreamLocalAddress = &net.TCPAddr{IP: net.ParseIP("::1")} // required because ::6 is NOT a loopback address (IPv6 only has ::1) - go server.Run(context.Background()) - - var statusPort uint16 - if err := retry.UntilSuccess(func() error { - server.mutex.RLock() - statusPort = server.statusPort - server.mutex.RUnlock() - if statusPort == 0 { - return fmt.Errorf("no port allocated") - } - return nil - }); err != nil { - t.Fatalf("failed to getport: %v", err) - } - t.Logf("status server starts at port %v, app starts at port %v", statusPort, appPort) - - testCases := []struct { - probePath string - statusCode int - }{ - { - probePath: fmt.Sprintf(":%v/bad-path-should-be-disallowed", statusPort), - statusCode: http.StatusNotFound, - }, - { - probePath: fmt.Sprintf(":%v/app-health/foo/livez", statusPort), - statusCode: http.StatusOK, - }, - { - probePath: fmt.Sprintf(":%v/app-health/foo/readyz", statusPort), - statusCode: http.StatusInternalServerError, - }, - { - probePath: fmt.Sprintf(":%v/app-health/bar/livez", statusPort), - statusCode: http.StatusOK, - }, - { - probePath: fmt.Sprintf(":%v/app-health/bar/readyz", statusPort), - statusCode: http.StatusInternalServerError, - }, - } - for _, tc := range testCases { - client := http.Client{} - req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost%s", tc.probePath), nil) - if err != nil { - t.Errorf("[%v] failed to create request", tc.probePath) - } - resp, err := client.Do(req) - if err != nil { - t.Fatal("request failed") - } - defer resp.Body.Close() - if resp.StatusCode != tc.statusCode { - t.Errorf("[%v] unexpected status code, want = %v, got = %v", tc.probePath, tc.statusCode, resp.StatusCode) - } - } -} - -func TestProbeHeader(t *testing.T) { - headerChecker := func(t *testing.T, header http.Header) net.Listener { - listener, err := net.Listen("tcp", ":0") - if err != nil { - t.Fatalf("failed to allocate unused port %v", err) - } - go http.Serve(listener, http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - r.Header.Del("User-Agent") - r.Header.Del("Accept-Encoding") - if !reflect.DeepEqual(r.Header, header) { - t.Errorf("unexpected header, want = %v, got = %v", header, r.Header) - http.Error(rw, "", http.StatusBadRequest) - return - } - http.Error(rw, "", http.StatusOK) - })) - return listener - } - - testCases := []struct { - name string - originHeaders http.Header - proxyHeaders []apimirror.HTTPHeader - want http.Header - }{ - { - name: "Only Origin", - originHeaders: http.Header{ - testHeader: []string{testHeaderValue}, - }, - proxyHeaders: []apimirror.HTTPHeader{}, - want: http.Header{ - testHeader: []string{testHeaderValue}, - "Connection": []string{"close"}, - }, - }, - { - name: "Only Origin, has multiple values", - originHeaders: http.Header{ - testHeader: []string{testHeaderValue, testHeaderValue}, - }, - proxyHeaders: []apimirror.HTTPHeader{}, - want: http.Header{ - testHeader: []string{testHeaderValue, testHeaderValue}, - "Connection": []string{"close"}, - }, - }, - { - name: "Only Proxy", - originHeaders: http.Header{}, - proxyHeaders: []apimirror.HTTPHeader{ - { - Name: testHeader, - Value: testHeaderValue, - }, - }, - want: http.Header{ - testHeader: []string{testHeaderValue}, - "Connection": []string{"close"}, - }, - }, - { - name: "Only Proxy, has multiple values", - originHeaders: http.Header{}, - proxyHeaders: []apimirror.HTTPHeader{ - { - Name: testHeader, - Value: testHeaderValue, - }, - { - Name: testHeader, - Value: testHeaderValue, - }, - }, - want: http.Header{ - testHeader: []string{testHeaderValue, testHeaderValue}, - "Connection": []string{"close"}, - }, - }, - { - name: "Proxy overwrites Origin", - originHeaders: http.Header{ - testHeader: []string{testHeaderValue, testHeaderValue}, - }, - proxyHeaders: []apimirror.HTTPHeader{ - { - Name: testHeader, - Value: testHeaderValue + "Over", - }, - }, - want: http.Header{ - testHeader: []string{testHeaderValue + "Over"}, - "Connection": []string{"close"}, - }, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - svc := headerChecker(t, tc.want) - defer svc.Close() - probePath := "/app-health/hello-world/livez" - appAddress := svc.Addr().(*net.TCPAddr) - appProber, err := json.Marshal(KubeAppProbers{ - probePath: &Prober{ - HTTPGet: &apimirror.HTTPGetAction{ - Port: intstr.IntOrString{IntVal: int32(appAddress.Port)}, - Host: appAddress.IP.String(), - Path: "/header", - HTTPHeaders: tc.proxyHeaders, - }, - }, - }) - if err != nil { - t.Fatalf("invalid app probers") - } - config := Options{ - StatusPort: 0, - KubeAppProbers: string(appProber), - } - // Starts the pilot agent status server. - server, err := NewServer(config) - if err != nil { - t.Fatal("failed to create status server: ", err) - } - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - go server.Run(ctx) - - var statusPort uint16 - for statusPort == 0 { - server.mutex.RLock() - statusPort = server.statusPort - server.mutex.RUnlock() - } - - client := http.Client{} - req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost:%v%s", statusPort, probePath), nil) - if err != nil { - t.Fatal("failed to create request: ", err) - } - req.Header = tc.originHeaders - resp, err := client.Do(req) - if err != nil { - t.Fatal("request failed: ", err) - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - t.Errorf("unexpected status code, want = %v, got = %v", http.StatusOK, resp.StatusCode) - } - }) - } -} - -func TestHandleQuit(t *testing.T) { - statusPort := 15020 - s, err := NewServer(Options{StatusPort: uint16(statusPort)}) - if err != nil { - t.Fatal(err) - } - - tests := []struct { - name string - method string - remoteAddr string - expected int - }{ - { - name: "should send a sigterm for valid requests", - method: "POST", - remoteAddr: "127.0.0.1", - expected: http.StatusOK, - }, - { - name: "should send a sigterm for valid ipv6 requests", - method: "POST", - remoteAddr: "[::1]", - expected: http.StatusOK, - }, - { - name: "should require POST method", - method: "GET", - remoteAddr: "127.0.0.1", - expected: http.StatusMethodNotAllowed, - }, - { - name: "should require localhost", - method: "POST", - expected: http.StatusForbidden, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Need to stop SIGTERM from killing the whole test run - termChannel := make(chan os.Signal, 1) - signal.Notify(termChannel, syscall.SIGTERM) - defer signal.Reset(syscall.SIGTERM) - - req, err := http.NewRequest(tt.method, "/quitquitquit", nil) - if err != nil { - t.Fatal(err) - } - - if tt.remoteAddr != "" { - req.RemoteAddr = tt.remoteAddr + ":15020" - } - - resp := httptest.NewRecorder() - s.handleQuit(resp, req) - if resp.Code != tt.expected { - t.Fatalf("Expected response code %v got %v", tt.expected, resp.Code) - } - - if tt.expected == http.StatusOK { - select { - case <-termChannel: - case <-time.After(time.Second): - t.Fatalf("Failed to receive expected SIGTERM") - } - } else if len(termChannel) != 0 { - t.Fatalf("A SIGTERM was sent when it should not have been") - } - }) - } -} - -func TestAdditionalProbes(t *testing.T) { - rp := readyProbe{} - urp := unreadyProbe{} - testCases := []struct { - name string - probes []ready.Prober - err error - }{ - { - name: "success probe", - probes: []ready.Prober{rp}, - err: nil, - }, - { - name: "not ready probe", - probes: []ready.Prober{urp}, - err: errors.New("not ready"), - }, - { - name: "both probes", - probes: []ready.Prober{rp, urp}, - err: errors.New("not ready"), - }, - } - testServer := testserver.CreateAndStartServer(liveServerStats) - defer testServer.Close() - for _, tc := range testCases { - server, err := NewServer(Options{ - Probes: tc.probes, - AdminPort: uint16(testServer.Listener.Addr().(*net.TCPAddr).Port), - }) - if err != nil { - t.Errorf("failed to construct server") - } - err = server.isReady() - if tc.err == nil { - if err != nil { - t.Errorf("Unexpected result, expected: %v got: %v", tc.err, err) - } - } else { - if err.Error() != tc.err.Error() { - t.Errorf("Unexpected result, expected: %v got: %v", tc.err, err) - } - } - - } -} - -type readyProbe struct{} - -func (s readyProbe) Check() error { - return nil -} - -type unreadyProbe struct{} - -func (u unreadyProbe) Check() error { - return errors.New("not ready") -} diff --git a/pilot/cmd/pilot-agent/status/test-cert/cert.crt b/pilot/cmd/pilot-agent/status/test-cert/cert.crt deleted file mode 100644 index 4f840c239..000000000 --- a/pilot/cmd/pilot-agent/status/test-cert/cert.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDgzCCAmugAwIBAgIJAPjLZPVcFqpYMA0GCSqGSIb3DQEBCwUAMFgxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQxETAPBgNVBAMMCHRlc3QuY29tMB4XDTE5MDMyODEwMjcx -NFoXDTI5MDMyNTEwMjcxNFowWDELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUt -U3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDERMA8GA1UE -AwwIdGVzdC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPEBi2 -la9fZJdaqiKtdLXRFgwPKy4oW8nMi2kw0Fs0RrNj6E7+0Td/12EckUYJDqOWY4i7 -JRHN2bv1NYx9a7ch5rVvBeNS2otn34/PrznY2bKP85nFbEwKc0808MdWSTDfP38f -ULPelcupzMbt4CRjRyUOgsnBzAx75jBr9L6/0kmhRgPk+FHrMf2rKCc1Q6wWtxfi -Y9oP/N+VaRKZnorXHST0UGEWgXmdh2qxn8OEoxHzrU79AujzGTtwXE6TYgZDO93r -Qd9olNGIs3INY6W3m/H4i1c/RE1ap7zu7r10if2GEnOv0z4oM+pMQwgdwDO0xvOd -n9+DVS9BlF2ah4wxAgMBAAGjUDBOMB0GA1UdDgQWBBS6ivHJJ8VYvfUcunC//A7R -VCabHjAfBgNVHSMEGDAWgBS6ivHJJ8VYvfUcunC//A7RVCabHjAMBgNVHRMEBTAD -AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQA3JsxnmPrMKoNCUNWMEsMhg2MgO3ptYi+n -8UZXvMQSezRG8vOyPibHMT6w/fM+x85Oz+IpXDQVTwEjO6Pm4HSzND3FloTQl7cp -ho//oVuLeDoNw7RVUO+tM4zLja7GhhAUbpDapLWSTq7A1EAxykKKuvuCqoKJlHwg -LKLGIdrxrKFjWklAxlJ7F29+xK0RGXjPIdovsEVs6xAyxpi/Ut0kHbXghM1M8vxV -h4nTTTgpvnE2NbI8gOsUvvqJyw1XuvxjTe7kwCUp1ispXQrNUZrlf+epXw0dSQPr -umuui2OJi54voYZgZ7ByGpXWckgVz2L6cn7OoUMYLx7FB7dzZk6L ------END CERTIFICATE----- \ No newline at end of file diff --git a/pilot/cmd/pilot-agent/status/test-cert/cert.key b/pilot/cmd/pilot-agent/status/test-cert/cert.key deleted file mode 100644 index 0b3c1e318..000000000 --- a/pilot/cmd/pilot-agent/status/test-cert/cert.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAzxAYtpWvX2SXWqoirXS10RYMDysuKFvJzItpMNBbNEazY+hO -/tE3f9dhHJFGCQ6jlmOIuyURzdm79TWMfWu3Iea1bwXjUtqLZ9+Pz6852Nmyj/OZ -xWxMCnNPNPDHVkkw3z9/H1Cz3pXLqczG7eAkY0clDoLJwcwMe+Ywa/S+v9JJoUYD -5PhR6zH9qygnNUOsFrcX4mPaD/zflWkSmZ6K1x0k9FBhFoF5nYdqsZ/DhKMR861O -/QLo8xk7cFxOk2IGQzvd60HfaJTRiLNyDWOlt5vx+ItXP0RNWqe87u69dIn9hhJz -r9M+KDPqTEMIHcAztMbznZ/fg1UvQZRdmoeMMQIDAQABAoIBAFsGTnblcoTS6Z5X -sIrkBZF2ybJZXx8qypl6p7FnxtBCTFYdJ6zpOCag/fXa/xi4ML3J36+1ahA+KVxw -P+Ra19S1YQj/Y6FmpWXyZ3v7IcjsWozhn7WkGAF4E1fIiTirUCqz9SRFC+1LmI56 -kPC9WgGyot2wLRVeqBZHaP3sR3Z3Jo0ed9jm0z2XmR6R2/UO6QOY4otGj9kYwh6a -wq5VKRxljyH2dAJKrS5H2pwXWxdxlx3QiIuJgC7eyAKIlUjccfP20Z4DvCz8DA/g -d1S87yF6jc/+tmcHFmysSYWVQOgq9cmWFnElTjXWTVRGhX8YD4YyC7MLlO9adgNw -UdaOgAECgYEA7p47X4NKPj2vM3K0d49lCn8gcCb3HkhZof0lLQAB4lK8mv70ErDC -/p9NAKRxLIjzFoRQ+UN0bMewrtKL8i5ojZl8MRo2QUYeCYqlPHnftWflr8tQUBHX -/0LjM7n6fuODy9Do1JcyQEOurw+4uPgKpSuZy2UY+krdsGVMT7POCDECgYEA3iVr -qp//wQAkM/7hhxpNMBAHDzJubfqQ0YN28XrDCDicXMBwvmxLssUHqx0kJbe0eTEy -3tmUovGkYOL5ycQOFESDydkVerEPtuEHn4CIb6QqWVQFdfxXqMiSNH2iEGs1GAXY -xzaqRjaDmpN2aDDfq4ThJh0zWescwmS6mUa0xAECgYAyPPI3K8cnz4jhhhbkzTXy -vc0wj6ObppPofQmkrcm3wr+eymrMvJZxUUy/A+AoBjVX2kfKEx+h/3D9faqlNIwi -s9vn4qLln0OXsq8TSn2FDfjXyDCCix80yPpY26EXsgL/mF5M1ABqc1WF2gOEPgTP -vZxFrGVT3QtLpigo56xLIQKBgQC40c9S5M0GyNRWAh+mpKZFb4BAD4g6rfXgqgzC -eY1cAKVusZjbhQQx1qU7owIY808OaXVWXRXBv2MwTIbfa+L+z8YJoDeznS5iy7Po -6yoYIDAvo6zrbaeMwFqLm17DZD6HHw4tJ/jgc6hoaXlg1BCzBdnAORkpHWgO/3kT -3vS0AQKBgQC7eoclzScXV77p7ISzbri/V4ooy5cnl+Wg4UlA8HQEQqO36rr7R7IL -jolIJFwc1GfA7RwxJZRZpFQ1ZTIy3EJ5JIW1lgrE8EZFKs8XFbnsdxpjc2p75t24 -mJTjQHNgoVgVtT8kas0fPTi3Jw7DVZom2tUbCnp/zObNzU9s9yOKUQ== ------END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/pilot/cmd/pilot-agent/status/testserver/server.go b/pilot/cmd/pilot-agent/status/testserver/server.go deleted file mode 100644 index 5230feae7..000000000 --- a/pilot/cmd/pilot-agent/status/testserver/server.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package testserver - -import ( - "net" - "net/http" - "net/http/httptest" -) - -// CreateAndStartServer starts a server and returns the response passed. -func CreateAndStartServer(response string) *httptest.Server { - return createHTTPServer(createDefaultFuncMap(response)) -} - -func createHTTPServer(handlers map[string]func(rw http.ResponseWriter, _ *http.Request)) *httptest.Server { - mux := http.NewServeMux() - for k, v := range handlers { - mux.HandleFunc(k, http.HandlerFunc(v)) - } - - // Start a local HTTP server - server := httptest.NewUnstartedServer(mux) - - l, err := net.Listen("tcp", ":0") - if err != nil { - panic("Could not create listener for test: " + err.Error()) - } - server.Listener = l - server.Start() - return server -} - -func createDefaultFuncMap(statsToReturn string) map[string]func(rw http.ResponseWriter, _ *http.Request) { - return map[string]func(rw http.ResponseWriter, _ *http.Request){ - "/stats": func(rw http.ResponseWriter, _ *http.Request) { - // Send response to be tested - _, err := rw.Write([]byte(statsToReturn)) - if err != nil { - panic("Could not write response: " + err.Error()) - } - }, - } -} diff --git a/pilot/cmd/pilot-agent/status/util/stats.go b/pilot/cmd/pilot-agent/status/util/stats.go deleted file mode 100644 index d6e901c9d..000000000 --- a/pilot/cmd/pilot-agent/status/util/stats.go +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "bytes" - "fmt" - "strconv" - "strings" - "time" -) - -import ( - multierror "github.com/hashicorp/go-multierror" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/http" -) - -const ( - statCdsRejected = "cluster_manager.cds.update_rejected" - statsCdsSuccess = "cluster_manager.cds.update_success" - statLdsRejected = "listener_manager.lds.update_rejected" - statLdsSuccess = "listener_manager.lds.update_success" - statServerState = "server.state" - statWorkersStarted = "listener_manager.workers_started" - readyStatsRegex = "^(server\\.state|listener_manager\\.workers_started)" - updateStatsRegex = "^(cluster_manager\\.cds|listener_manager\\.lds)\\.(update_success|update_rejected)$" -) - -var readinessTimeout = time.Second * 3 // Default Readiness timeout. It is set the same in helm charts. - -type stat struct { - name string - value *uint64 - found bool -} - -// Stats contains values of interest from a poll of Envoy stats. -type Stats struct { - // Update Stats. - CDSUpdatesSuccess uint64 - CDSUpdatesRejection uint64 - LDSUpdatesSuccess uint64 - LDSUpdatesRejection uint64 - // Server State of Envoy. - ServerState uint64 - WorkersStarted uint64 -} - -// String representation of the Stats. -func (s *Stats) String() string { - return fmt.Sprintf("cds updates: %d successful, %d rejected; lds updates: %d successful, %d rejected", - s.CDSUpdatesSuccess, - s.CDSUpdatesRejection, - s.LDSUpdatesSuccess, - s.LDSUpdatesRejection) -} - -// GetReadinessStats returns the current Envoy state by checking the "server.state" stat. -func GetReadinessStats(localHostAddr string, adminPort uint16) (*uint64, bool, error) { - // If the localHostAddr was not set, we use 'localhost' to void empty host in URL. - if localHostAddr == "" { - localHostAddr = "localhost" - } - - readinessURL := fmt.Sprintf("http://%s:%d/stats?usedonly&filter=%s", localHostAddr, adminPort, readyStatsRegex) - stats, err := http.DoHTTPGetWithTimeout(readinessURL, readinessTimeout) - if err != nil { - return nil, false, err - } - if !strings.Contains(stats.String(), "server.state") { - return nil, false, fmt.Errorf("server.state is not yet updated: %s", stats.String()) - } - - if !strings.Contains(stats.String(), "listener_manager.workers_started") { - return nil, false, fmt.Errorf("listener_manager.workers_started is not yet updated: %s", stats.String()) - } - - s := &Stats{} - allStats := []*stat{ - {name: statServerState, value: &s.ServerState}, - {name: statWorkersStarted, value: &s.WorkersStarted}, - } - if err := parseStats(stats, allStats); err != nil { - return nil, false, err - } - - return &s.ServerState, s.WorkersStarted == 1, nil -} - -// GetUpdateStatusStats returns the version stats for CDS and LDS. -func GetUpdateStatusStats(localHostAddr string, adminPort uint16) (*Stats, error) { - // If the localHostAddr was not set, we use 'localhost' to void empty host in URL. - if localHostAddr == "" { - localHostAddr = "localhost" - } - - stats, err := http.DoHTTPGet(fmt.Sprintf("http://%s:%d/stats?usedonly&filter=%s", localHostAddr, adminPort, updateStatsRegex)) - if err != nil { - return nil, err - } - - s := &Stats{} - allStats := []*stat{ - {name: statsCdsSuccess, value: &s.CDSUpdatesSuccess}, - {name: statCdsRejected, value: &s.CDSUpdatesRejection}, - {name: statLdsSuccess, value: &s.LDSUpdatesSuccess}, - {name: statLdsRejected, value: &s.LDSUpdatesRejection}, - } - if err := parseStats(stats, allStats); err != nil { - return nil, err - } - - return s, nil -} - -func parseStats(input *bytes.Buffer, stats []*stat) (err error) { - for input.Len() > 0 { - line, _ := input.ReadString('\n') - for _, stat := range stats { - if e := stat.processLine(line); e != nil { - err = multierror.Append(err, e) - } - } - } - for _, stat := range stats { - if !stat.found { - *stat.value = 0 - } - } - return -} - -func (s *stat) processLine(line string) error { - if !s.found && strings.HasPrefix(line, s.name) { - s.found = true - - parts := strings.Split(line, ":") - if len(parts) != 2 { - return fmt.Errorf("envoy stat %s missing separator. line:%s", s.name, line) - } - - val, err := strconv.ParseUint(strings.TrimSpace(parts[1]), 10, 64) - if err != nil { - return fmt.Errorf("failed parsing Envoy stat %s (error: %s) line: %s", s.name, err.Error(), line) - } - - *s.value = val - } - - return nil -} diff --git a/pilot/cmd/pilot-agent/wait.go b/pilot/cmd/pilot-agent/wait.go deleted file mode 100644 index da01d1daf..000000000 --- a/pilot/cmd/pilot-agent/wait.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "fmt" - "io" - "net/http" - "time" -) - -import ( - "github.com/spf13/cobra" - "istio.io/pkg/log" -) - -var ( - timeoutSeconds int - requestTimeoutMillis int - periodMillis int - url string - - waitCmd = &cobra.Command{ - Use: "wait", - Short: "Waits until the Envoy proxy is ready", - RunE: func(c *cobra.Command, args []string) error { - client := &http.Client{ - Timeout: time.Duration(requestTimeoutMillis) * time.Millisecond, - } - log.Infof("Waiting for Envoy proxy to be ready (timeout: %d seconds)...", timeoutSeconds) - - var err error - timeoutAt := time.Now().Add(time.Duration(timeoutSeconds) * time.Second) - for time.Now().Before(timeoutAt) { - err = checkIfReady(client, url) - if err == nil { - log.Infof("Envoy is ready!") - return nil - } - log.Debugf("Not ready yet: %v", err) - time.Sleep(time.Duration(periodMillis) * time.Millisecond) - } - return fmt.Errorf("timeout waiting for Envoy proxy to become ready. Last error: %v", err) - }, - } -) - -func checkIfReady(client *http.Client, url string) error { - req, err := http.NewRequest(http.MethodGet, url, nil) - if err != nil { - return err - } - resp, err := client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - _, err = io.ReadAll(resp.Body) - if err != nil { - return err - } - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("HTTP status code %v", resp.StatusCode) - } - return nil -} - -func init() { - waitCmd.PersistentFlags().IntVar(&timeoutSeconds, "timeoutSeconds", 60, "maximum number of seconds to wait for Envoy to be ready") - waitCmd.PersistentFlags().IntVar(&requestTimeoutMillis, "requestTimeoutMillis", 500, "number of milliseconds to wait for response") - waitCmd.PersistentFlags().IntVar(&periodMillis, "periodMillis", 500, "number of milliseconds to wait between attempts") - waitCmd.PersistentFlags().StringVar(&url, "url", "http://localhost:15021/healthz/ready", "URL to use in requests") - - rootCmd.AddCommand(waitCmd) -} diff --git a/pilot/cmd/pilot-discovery/app/cmd.go b/pilot/cmd/pilot-discovery/app/cmd.go deleted file mode 100644 index 86ccd2757..000000000 --- a/pilot/cmd/pilot-discovery/app/cmd.go +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package app - -import ( - "fmt" - "strings" - "time" -) - -import ( - "github.com/spf13/cobra" - "github.com/spf13/cobra/doc" - "istio.io/pkg/collateral" - "istio.io/pkg/ctrlz" - "istio.io/pkg/log" - "istio.io/pkg/version" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/bootstrap" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pkg/cmd" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" -) - -var ( - serverArgs *bootstrap.PilotArgs - loggingOptions = log.DefaultOptions() -) - -// NewRootCommand returns the root cobra command of pilot-discovery. -func NewRootCommand() *cobra.Command { - rootCmd := &cobra.Command{ - Use: "pilot-discovery", - Short: "Istio Pilot.", - Long: "Istio Pilot provides fleet-wide traffic management capabilities in the Istio Service Mesh.", - SilenceUsage: true, - PreRunE: func(c *cobra.Command, args []string) error { - cmd.AddFlags(c) - return nil - }, - } - - discoveryCmd := newDiscoveryCommand() - addFlags(discoveryCmd) - rootCmd.AddCommand(discoveryCmd) - rootCmd.AddCommand(version.CobraCommand()) - rootCmd.AddCommand(collateral.CobraCommand(rootCmd, &doc.GenManHeader{ - Title: "Istio Pilot Discovery", - Section: "pilot-discovery CLI", - Manual: "Istio Pilot Discovery", - })) - rootCmd.AddCommand(requestCmd) - - return rootCmd -} - -func newDiscoveryCommand() *cobra.Command { - return &cobra.Command{ - Use: "discovery", - Short: "Start Istio proxy discovery service.", - Args: cobra.ExactArgs(0), - PreRunE: func(c *cobra.Command, args []string) error { - if err := log.Configure(loggingOptions); err != nil { - return err - } - if err := validateFlags(serverArgs); err != nil { - return err - } - if err := serverArgs.Complete(); err != nil { - return err - } - return nil - }, - RunE: func(c *cobra.Command, args []string) error { - cmd.PrintFlags(c.Flags()) - - // Create the stop channel for all of the servers. - stop := make(chan struct{}) - - // Create the server for the discovery service. - discoveryServer, err := bootstrap.NewServer(serverArgs) - if err != nil { - return fmt.Errorf("failed to create discovery service: %v", err) - } - - // Start the server - if err := discoveryServer.Start(stop); err != nil { - return fmt.Errorf("failed to start discovery service: %v", err) - } - - cmd.WaitSignal(stop) - // Wait until we shut down. In theory this could block forever; in practice we will get - // forcibly shut down after 30s in Kubernetes. - discoveryServer.WaitUntilCompletion() - return nil - }, - } -} - -var ignored []string - -func addFlags(c *cobra.Command) { - serverArgs = bootstrap.NewPilotArgs(func(p *bootstrap.PilotArgs) { - // Set Defaults - p.CtrlZOptions = ctrlz.DefaultOptions() - // TODO replace with mesh config? - p.InjectionOptions = bootstrap.InjectionOptions{ - InjectionDirectory: "./var/lib/istio/inject", - } - }) - - // Process commandline args. - c.PersistentFlags().StringSliceVar(&serverArgs.RegistryOptions.Registries, "registries", - []string{string(provider.Kubernetes)}, - fmt.Sprintf("Comma separated list of platform service registries to read from (choose one or more from {%s, %s})", - provider.Kubernetes, provider.Mock)) - c.PersistentFlags().StringVar(&serverArgs.RegistryOptions.ClusterRegistriesNamespace, "clusterRegistriesNamespace", - serverArgs.RegistryOptions.ClusterRegistriesNamespace, "Namespace for ConfigMap which stores clusters configs") - c.PersistentFlags().StringVar(&serverArgs.RegistryOptions.KubeConfig, "kubeconfig", "", - "Use a Kubernetes configuration file instead of in-cluster configuration") - c.PersistentFlags().StringVar(&serverArgs.MeshConfigFile, "meshConfig", "./etc/istio/config/mesh", - "File name for Istio mesh configuration. If not specified, a default mesh will be used.") - c.PersistentFlags().StringVar(&serverArgs.NetworksConfigFile, "networksConfig", "./etc/istio/config/meshNetworks", - "File name for Istio mesh networks configuration. If not specified, a default mesh networks will be used.") - c.PersistentFlags().StringVarP(&serverArgs.Namespace, "namespace", "n", bootstrap.PodNamespace, - "Select a namespace where the controller resides. If not set, uses ${POD_NAMESPACE} environment variable") - c.PersistentFlags().StringSliceVar(&ignored, "plugins", nil, - "comma separated list of networking plugins to enable") - _ = c.PersistentFlags().MarkDeprecated("plugins", "no effect") - c.PersistentFlags().DurationVar(&serverArgs.ShutdownDuration, "shutdownDuration", 10*time.Second, - "Duration the discovery server needs to terminate gracefully") - - // RegistryOptions Controller options - c.PersistentFlags().StringVar(&serverArgs.RegistryOptions.FileDir, "configDir", "", - "Directory to watch for updates to config yaml files. If specified, the files will be used as the source of config, rather than a CRD client.") - c.PersistentFlags().StringVar(&serverArgs.RegistryOptions.KubeOptions.DomainSuffix, "domain", constants.DefaultKubernetesDomain, - "DNS domain suffix") - c.PersistentFlags().StringVar((*string)(&serverArgs.RegistryOptions.KubeOptions.ClusterID), "clusterID", features.ClusterName, - "The ID of the cluster that this Istiod instance resides") - c.PersistentFlags().StringToStringVar(&serverArgs.RegistryOptions.KubeOptions.ClusterAliases, "clusterAliases", map[string]string{}, - "Alias names for clusters") - - // using address, so it can be configured as localhost:.. (possibly UDS in future) - c.PersistentFlags().StringVar(&serverArgs.ServerOptions.HTTPAddr, "httpAddr", ":8080", - "Discovery service HTTP address") - c.PersistentFlags().StringVar(&serverArgs.ServerOptions.HTTPSAddr, "httpsAddr", ":15017", - "Injection and validation service HTTPS address") - c.PersistentFlags().StringVar(&serverArgs.ServerOptions.GRPCAddr, "grpcAddr", ":15010", - "Discovery service gRPC address") - c.PersistentFlags().StringVar(&serverArgs.ServerOptions.SecureGRPCAddr, "secureGRPCAddr", ":15012", - "Discovery service secured gRPC address") - c.PersistentFlags().StringVar(&serverArgs.ServerOptions.MonitoringAddr, "monitoringAddr", ":15014", - "HTTP address to use for pilot's self-monitoring information") - c.PersistentFlags().BoolVar(&serverArgs.ServerOptions.EnableProfiling, "profile", true, - "Enable profiling via web interface host:port/debug/pprof") - - // Use TLS certificates if provided. - c.PersistentFlags().StringVar(&serverArgs.ServerOptions.TLSOptions.CaCertFile, "caCertFile", "", - "File containing the x509 Server CA Certificate") - c.PersistentFlags().StringVar(&serverArgs.ServerOptions.TLSOptions.CertFile, "tlsCertFile", "", - "File containing the x509 Server Certificate") - c.PersistentFlags().StringVar(&serverArgs.ServerOptions.TLSOptions.KeyFile, "tlsKeyFile", "", - "File containing the x509 private key matching --tlsCertFile") - c.PersistentFlags().StringSliceVar(&serverArgs.ServerOptions.TLSOptions.TLSCipherSuites, "tls-cipher-suites", nil, - "Comma-separated list of cipher suites for istiod TLS server. "+ - "If omitted, the default Go cipher suites will be used. \n"+ - "Preferred values: "+strings.Join(secureTLSCipherNames(), ", ")+". \n"+ - "Insecure values: "+strings.Join(insecureTLSCipherNames(), ", ")+".") - - c.PersistentFlags().Float32Var(&serverArgs.RegistryOptions.KubeOptions.KubernetesAPIQPS, "kubernetesApiQPS", 80.0, - "Maximum QPS when communicating with the kubernetes API") - - c.PersistentFlags().IntVar(&serverArgs.RegistryOptions.KubeOptions.KubernetesAPIBurst, "kubernetesApiBurst", 160, - "Maximum burst for throttle when communicating with the kubernetes API") - - // Attach the Istio logging options to the command. - loggingOptions.AttachCobraFlags(c) - - // Attach the Istio Ctrlz options to the command. - serverArgs.CtrlZOptions.AttachCobraFlags(c) - - // Attach the Istio Keepalive options to the command. - serverArgs.KeepaliveOptions.AttachCobraFlags(c) -} diff --git a/pilot/cmd/pilot-discovery/app/options.go b/pilot/cmd/pilot-discovery/app/options.go deleted file mode 100644 index 601bb47f6..000000000 --- a/pilot/cmd/pilot-discovery/app/options.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package app - -import ( - "crypto/tls" -) - -import ( - "k8s.io/apimachinery/pkg/util/sets" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/bootstrap" - "github.com/apache/dubbo-go-pixiu/pkg/config/validation" -) - -// insecureTLSCipherNames returns a list of insecure cipher suite names implemented by crypto/tls -// which have security issues. -func insecureTLSCipherNames() []string { - cipherKeys := sets.NewString() - for _, cipher := range tls.InsecureCipherSuites() { - cipherKeys.Insert(cipher.Name) - } - return cipherKeys.List() -} - -// secureTLSCipherNames returns a list of secure cipher suite names implemented by crypto/tls. -func secureTLSCipherNames() []string { - cipherKeys := sets.NewString() - for _, cipher := range tls.CipherSuites() { - cipherKeys.Insert(cipher.Name) - } - return cipherKeys.List() -} - -func validateFlags(serverArgs *bootstrap.PilotArgs) error { - if serverArgs == nil { - return nil - } - - // If keepaliveMaxServerConnectionAge is negative, istiod crash - // https://github.com/istio/istio/issues/27257 - if err := validation.ValidateMaxServerConnectionAge(serverArgs.KeepaliveOptions.MaxServerConnectionAge); err != nil { - return err - } - - _, err := bootstrap.TLSCipherSuites(serverArgs.ServerOptions.TLSOptions.TLSCipherSuites) - - // TODO: add validation for other flags - return err -} diff --git a/pilot/cmd/pilot-discovery/app/request.go b/pilot/cmd/pilot-discovery/app/request.go deleted file mode 100644 index ecbef0cf8..000000000 --- a/pilot/cmd/pilot-discovery/app/request.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package app - -import ( - "net/http" - "time" -) - -import ( - "github.com/spf13/cobra" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/request" -) - -var requestCmd = &cobra.Command{ - Use: "request []", - Short: "Makes an HTTP request to Pilot metrics/debug endpoint", - Args: cobra.MinimumNArgs(2), - RunE: func(c *cobra.Command, args []string) error { - command := &request.Command{ - Address: "127.0.0.1:15014", - Client: &http.Client{ - Timeout: 60 * time.Second, - }, - } - body := "" - if len(args) > 2 { - body = args[2] - } - return command.Do(args[0], args[1], body) - }, -} diff --git a/pilot/cmd/pilot-discovery/main.go b/pilot/cmd/pilot-discovery/main.go deleted file mode 100644 index e2243cf79..000000000 --- a/pilot/cmd/pilot-discovery/main.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "os" -) - -import ( - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-discovery/app" -) - -func main() { - log.EnableKlogWithCobra() - rootCmd := app.NewRootCommand() - if err := rootCmd.Execute(); err != nil { - log.Error(err) - os.Exit(-1) - } -} diff --git a/pilot/docker/Dockerfile.pilot b/pilot/docker/Dockerfile.pilot deleted file mode 100644 index de346af81..000000000 --- a/pilot/docker/Dockerfile.pilot +++ /dev/null @@ -1,52 +0,0 @@ -FROM ubuntu:jammy - -ENV DEBIAN_FRONTEND=noninteractive - -# Do not add more stuff to this list that isn't small or critically useful. -# If you occasionally need something on the container do -# sudo apt-get update && apt-get whichever - -# hadolint ignore=DL3005,DL3008 -RUN apt-get update && \ - apt-get install --no-install-recommends -y \ - ca-certificates \ - curl \ - iptables \ - iproute2 \ - iputils-ping \ - knot-dnsutils \ - netcat \ - tcpdump \ - conntrack \ - bsdmainutils \ - net-tools \ - lsof \ - sudo \ - && update-ca-certificates \ - && apt-get upgrade -y \ - && apt-get clean \ - && rm -rf /var/log/*log /var/lib/apt/lists/* /var/log/apt/* /var/lib/dpkg/*-old /var/cache/debconf/*-old \ - && update-alternatives --set iptables /usr/sbin/iptables-legacy \ - && update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy - -# Sudoers used to allow tcpdump and other debug utilities. -RUN useradd -m --uid 1337 istio-proxy && \ - echo "istio-proxy ALL=NOPASSWD: ALL" >> /etc/sudoers - -# BASE_DISTRIBUTION is used to switch between the old base distribution and distroless base images -ARG BASE_DISTRIBUTION=debug - -# Version is the base image version from the TLD Makefile -ARG BASE_VERSION=latest - -ARG TARGETARCH -COPY ${TARGETARCH:-amd64}/pilot-discovery /usr/local/bin/pilot-discovery -# COPY ${TARGETARCH:-amd64}/dlv /usr/local/bin/dlv - -# Copy templates for bootstrap generation. -COPY envoy_bootstrap.json /var/lib/istio/envoy/envoy_bootstrap_tmpl.json -COPY gcp_envoy_bootstrap.json /var/lib/istio/envoy/gcp_envoy_bootstrap_tmpl.json - -USER 1337:1337 - -ENTRYPOINT ["/usr/local/bin/pilot-discovery"] diff --git a/pilot/docker/Dockerfile.proxyv2 b/pilot/docker/Dockerfile.proxyv2 deleted file mode 100644 index 7b4577488..000000000 --- a/pilot/docker/Dockerfile.proxyv2 +++ /dev/null @@ -1,49 +0,0 @@ -# BASE_DISTRIBUTION is used to switch between the old base distribution and distroless base images -ARG BASE_DISTRIBUTION=debug - -# Version is the base image version from the TLD Makefile -ARG BASE_VERSION=latest - -# The following section is used as base image if BASE_DISTRIBUTION=debug -FROM gcr.io/istio-release/base:${BASE_VERSION} as debug - -# The following section is used as base image if BASE_DISTRIBUTION=distroless -# This image is a custom built debian11 distroless image with multiarchitecture support. -# It is built on the base distroless image, with iptables binary and libraries added -# The source can be found at https://github.com/istio/distroless/tree/iptables -# This version is from commit 6faf2f9348d382c6a95bbc3171b89406103cb0ad. -FROM gcr.io/istio-release/iptables@sha256:64ba191166d2c3f87815148cc1a9f530b770e0dfe81acb164ab79cbb31da582c as distroless - -# This will build the final image based on either debug or distroless from above -# hadolint ignore=DL3006 -FROM ${BASE_DISTRIBUTION:-debug} - -WORKDIR / - -ARG proxy_version -ARG istio_version -ARG SIDECAR=envoy - -# Copy Envoy bootstrap templates used by pilot-agent -COPY envoy_bootstrap.json /var/lib/istio/envoy/envoy_bootstrap_tmpl.json -COPY gcp_envoy_bootstrap.json /var/lib/istio/envoy/gcp_envoy_bootstrap_tmpl.json - -# Install Envoy. -ARG TARGETARCH -COPY ${TARGETARCH:-amd64}/${SIDECAR} /usr/local/bin/${SIDECAR} - -# Environment variable indicating the exact proxy sha - for debugging or version-specific configs -ENV ISTIO_META_ISTIO_PROXY_SHA $proxy_version -# Environment variable indicating the exact build, for debugging -ENV ISTIO_META_ISTIO_VERSION $istio_version - -ARG TARGETARCH -COPY ${TARGETARCH:-amd64}/pilot-agent /usr/local/bin/pilot-agent - -COPY stats-filter.wasm /etc/istio/extensions/stats-filter.wasm -COPY stats-filter.compiled.wasm /etc/istio/extensions/stats-filter.compiled.wasm -COPY metadata-exchange-filter.wasm /etc/istio/extensions/metadata-exchange-filter.wasm -COPY metadata-exchange-filter.compiled.wasm /etc/istio/extensions/metadata-exchange-filter.compiled.wasm - -# The pilot-agent will bootstrap Envoy. -ENTRYPOINT ["/usr/local/bin/pilot-agent"] diff --git a/pilot/pkg/bootstrap/certcontroller.go b/pilot/pkg/bootstrap/certcontroller.go deleted file mode 100644 index 6eda90b56..000000000 --- a/pilot/pkg/bootstrap/certcontroller.go +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bootstrap - -import ( - "bytes" - "crypto/tls" - "crypto/x509" - "fmt" - "os" - "strings" - "time" -) - -import ( - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/security" - "github.com/apache/dubbo-go-pixiu/security/pkg/k8s/chiron" -) - -const ( - // defaultCertGracePeriodRatio is the default length of certificate rotation grace period, - // configured as the ratio of the certificate TTL. - defaultCertGracePeriodRatio = 0.5 - - // defaultMinCertGracePeriod is the default minimum grace period for workload cert rotation. - defaultMinCertGracePeriod = 10 * time.Minute - - // the interval polling root cert and re sign istiod cert when it changes. - rootCertPollingInterval = 60 * time.Second - - // Default CA certificate path - // Currently, custom CA path is not supported; no API to get custom CA cert yet. - defaultCACertPath = "./var/run/secrets/kubernetes.io/serviceaccount/ca.crt" -) - -// CertController can create certificates signed by K8S server. -func (s *Server) initCertController(args *PilotArgs) error { - var err error - var secretNames, dnsNames []string - - meshConfig := s.environment.Mesh() - if meshConfig.GetCertificates() == nil || len(meshConfig.GetCertificates()) == 0 { - // TODO: if the provider is set to Citadel, use that instead of k8s so the API is still preserved. - log.Info("No certificates specified, skipping K8S DNS certificate controller") - return nil - } - - k8sClient := s.kubeClient - for _, c := range meshConfig.GetCertificates() { - name := strings.Join(c.GetDnsNames(), ",") - if len(name) == 0 { // must have a DNS name - continue - } - if len(c.GetSecretName()) > 0 { - // Chiron will generate the key and certificate and save them in a secret - secretNames = append(secretNames, c.GetSecretName()) - dnsNames = append(dnsNames, name) - } - } - - // Provision and manage the certificates for non-Pilot services. - // If services are empty, the certificate controller will do nothing. - s.certController, err = chiron.NewWebhookController(defaultCertGracePeriodRatio, defaultMinCertGracePeriod, - k8sClient, defaultCACertPath, secretNames, dnsNames, args.Namespace, "") - if err != nil { - return fmt.Errorf("failed to create certificate controller: %v", err) - } - s.addStartFunc(func(stop <-chan struct{}) error { - go func() { - // Run Chiron to manage the lifecycles of certificates - s.certController.Run(stop) - }() - - return nil - }) - - return nil -} - -// initDNSCerts will create the certificates to be used by Istiod GRPC server and webhooks. -// If the certificate creation fails - for example no support in K8S - returns an error. -// Will use the mesh.yaml DiscoveryAddress to find the default expected address of the control plane, -// with an environment variable allowing override. -// -// Controlled by features.IstiodService env variable, which defines the name of the service to use in the DNS -// cert, or empty for disabling this feature. -// -// TODO: If the discovery address in mesh.yaml is set to port 15012 (XDS-with-DNS-certs) and the name -// matches the k8s namespace, failure to start DNS server is a fatal error. -func (s *Server) initDNSCerts(hostname, namespace string) error { - // Name in the Istiod cert - support the old service names as well. - // validate hostname contains namespace - parts := strings.Split(hostname, ".") - hostnamePrefix := parts[0] - - var certChain, keyPEM, caBundle []byte - var err error - pilotCertProviderName := features.PilotCertProvider - if strings.HasPrefix(pilotCertProviderName, constants.CertProviderKubernetesSignerPrefix) && s.RA != nil { - signerName := strings.TrimPrefix(pilotCertProviderName, constants.CertProviderKubernetesSignerPrefix) - log.Infof("Generating K8S-signed cert for %v using signer %v", s.dnsNames, signerName) - certChain, keyPEM, _, err = chiron.GenKeyCertK8sCA(s.kubeClient, - strings.Join(s.dnsNames, ","), hostnamePrefix+".csr.secret", namespace, "", signerName, true, SelfSignedCACertTTL.Get()) - if err != nil { - return fmt.Errorf("failed generating key and cert by kubernetes: %v", err) - } - caBundle, err = s.RA.GetRootCertFromMeshConfig(signerName) - if err != nil { - return err - } - // MeshConfig:Add callback for mesh config update - s.environment.AddMeshHandler(func() { - newCaBundle, _ := s.RA.GetRootCertFromMeshConfig(signerName) - if newCaBundle != nil && !bytes.Equal(newCaBundle, s.istiodCertBundleWatcher.GetKeyCertBundle().CABundle) { - newCertChain, newKeyPEM, _, err := chiron.GenKeyCertK8sCA(s.kubeClient, - strings.Join(s.dnsNames, ","), hostnamePrefix+".csr.secret", namespace, "", signerName, true, SelfSignedCACertTTL.Get()) - if err != nil { - log.Fatalf("failed regenerating key and cert for istiod by kubernetes: %v", err) - } - s.istiodCertBundleWatcher.SetAndNotify(newKeyPEM, newCertChain, newCaBundle) - } - }) - } else if pilotCertProviderName == constants.CertProviderKubernetes { - log.Infof("Generating K8S-signed cert for %v", s.dnsNames) - certChain, keyPEM, _, err = chiron.GenKeyCertK8sCA(s.kubeClient, - strings.Join(s.dnsNames, ","), hostnamePrefix+".csr.secret", namespace, defaultCACertPath, "", true, SelfSignedCACertTTL.Get()) - if err != nil { - return fmt.Errorf("failed generating key and cert by kubernetes: %v", err) - } - caBundle, err = os.ReadFile(defaultCACertPath) - if err != nil { - return fmt.Errorf("failed reading %s: %v", defaultCACertPath, err) - } - } else if pilotCertProviderName == constants.CertProviderIstiod { - certChain, keyPEM, err = s.CA.GenKeyCert(s.dnsNames, SelfSignedCACertTTL.Get(), false) - if err != nil { - return fmt.Errorf("failed generating istiod key cert %v", err) - } - log.Infof("Generating istiod-signed cert for %v:\n %s", s.dnsNames, certChain) - - fileBundle, err := detectSigningCABundle() - if err != nil { - return fmt.Errorf("unable to determine signing file format %v", err) - } - - // check if signing key file exists the cert dir - if _, err := os.Stat(fileBundle.SigningKeyFile); err != nil { - log.Infof("No plugged-in cert at %v; self-signed cert is used", fileBundle.SigningKeyFile) - caBundle = s.CA.GetCAKeyCertBundle().GetRootCertPem() - s.addStartFunc(func(stop <-chan struct{}) error { - go func() { - // regenerate istiod key cert when root cert changes. - s.watchRootCertAndGenKeyCert(stop) - }() - return nil - }) - } else { - log.Infof("Use plugged-in cert at %v", fileBundle.SigningKeyFile) - - caBundle, err = os.ReadFile(fileBundle.RootCertFile) - if err != nil { - return fmt.Errorf("failed reading %s: %v", fileBundle.RootCertFile, err) - } - } - } else { - customCACertPath := security.DefaultRootCertFilePath - log.Infof("User specified cert provider: %v, mounted in a well known location %v", - features.PilotCertProvider, customCACertPath) - caBundle, err = os.ReadFile(customCACertPath) - if err != nil { - return fmt.Errorf("failed reading %s: %v", customCACertPath, err) - } - } - s.istiodCertBundleWatcher.SetAndNotify(keyPEM, certChain, caBundle) - return nil -} - -// TODO(hzxuzonghu): support async notification instead of polling the CA root cert. -func (s *Server) watchRootCertAndGenKeyCert(stop <-chan struct{}) { - caBundle := s.CA.GetCAKeyCertBundle().GetRootCertPem() - for { - select { - case <-stop: - return - case <-time.After(rootCertPollingInterval): - newRootCert := s.CA.GetCAKeyCertBundle().GetRootCertPem() - if !bytes.Equal(caBundle, newRootCert) { - caBundle = newRootCert - certChain, keyPEM, err := s.CA.GenKeyCert(s.dnsNames, SelfSignedCACertTTL.Get(), false) - if err != nil { - log.Errorf("failed generating istiod key cert %v", err) - } else { - s.istiodCertBundleWatcher.SetAndNotify(keyPEM, certChain, caBundle) - log.Infof("regenerated istiod dns cert: %s", certChain) - } - } - } - } -} - -// updatePluggedinRootCertAndGenKeyCert when intermediate CA is updated, it generates new dns certs and notifies keycertbundle about the changes -func (s *Server) updatePluggedinRootCertAndGenKeyCert() error { - caBundle := s.CA.GetCAKeyCertBundle().GetRootCertPem() - certChain, keyPEM, err := s.CA.GenKeyCert(s.dnsNames, SelfSignedCACertTTL.Get(), false) - if err != nil { - return err - } - - s.istiodCertBundleWatcher.SetAndNotify(keyPEM, certChain, caBundle) - return nil -} - -// initCertificateWatches sets up watches for the plugin dns certs. -func (s *Server) initCertificateWatches(tlsOptions TLSOptions) error { - if err := s.istiodCertBundleWatcher.SetFromFilesAndNotify(tlsOptions.KeyFile, tlsOptions.CertFile, tlsOptions.CaCertFile); err != nil { - return fmt.Errorf("set keyCertBundle failed: %v", err) - } - // TODO: Setup watcher for root and restart server if it changes. - for _, file := range []string{tlsOptions.CertFile, tlsOptions.KeyFile} { - log.Infof("adding watcher for certificate %s", file) - if err := s.fileWatcher.Add(file); err != nil { - return fmt.Errorf("could not watch %v: %v", file, err) - } - } - s.addStartFunc(func(stop <-chan struct{}) error { - go func() { - var keyCertTimerC <-chan time.Time - for { - select { - case <-keyCertTimerC: - keyCertTimerC = nil - if err := s.istiodCertBundleWatcher.SetFromFilesAndNotify(tlsOptions.KeyFile, tlsOptions.CertFile, tlsOptions.CaCertFile); err != nil { - log.Errorf("Setting keyCertBundle failed: %v", err) - } - case <-s.fileWatcher.Events(tlsOptions.CertFile): - if keyCertTimerC == nil { - keyCertTimerC = time.After(watchDebounceDelay) - } - case <-s.fileWatcher.Events(tlsOptions.KeyFile): - if keyCertTimerC == nil { - keyCertTimerC = time.After(watchDebounceDelay) - } - case err := <-s.fileWatcher.Errors(tlsOptions.CertFile): - log.Errorf("error watching %v: %v", tlsOptions.CertFile, err) - case err := <-s.fileWatcher.Errors(tlsOptions.KeyFile): - log.Errorf("error watching %v: %v", tlsOptions.KeyFile, err) - case <-stop: - return - } - } - }() - return nil - }) - return nil -} - -func (s *Server) reloadIstiodCert(watchCh <-chan struct{}, stopCh <-chan struct{}) { - for { - select { - case <-stopCh: - return - case <-watchCh: - if err := s.loadIstiodCert(); err != nil { - log.Errorf("reload istiod cert failed: %v", err) - } - } - } -} - -// loadIstiodCert load IstiodCert received from watchCh once -func (s *Server) loadIstiodCert() error { - keyCertBundle := s.istiodCertBundleWatcher.GetKeyCertBundle() - keyPair, err := tls.X509KeyPair(keyCertBundle.CertPem, keyCertBundle.KeyPem) - if err != nil { - return fmt.Errorf("istiod loading x509 key pairs failed: %v", err) - } - for _, c := range keyPair.Certificate { - x509Cert, err := x509.ParseCertificates(c) - if err != nil { - // This can rarely happen, just in case. - return fmt.Errorf("x509 cert - ParseCertificates() error: %v", err) - } - for _, c := range x509Cert { - log.Infof("x509 cert - Issuer: %q, Subject: %q, SN: %x, NotBefore: %q, NotAfter: %q", - c.Issuer, c.Subject, c.SerialNumber, - c.NotBefore.Format(time.RFC3339), c.NotAfter.Format(time.RFC3339)) - } - } - - log.Info("Istiod certificates are reloaded") - s.certMu.Lock() - s.istiodCert = &keyPair - s.certMu.Unlock() - return nil -} diff --git a/pilot/pkg/bootstrap/config_compare.go b/pilot/pkg/bootstrap/config_compare.go deleted file mode 100644 index fcfab2fa8..000000000 --- a/pilot/pkg/bootstrap/config_compare.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bootstrap - -import ( - "strings" -) - -import ( - gogoproto "github.com/gogo/protobuf/proto" - "google.golang.org/protobuf/proto" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" -) - -// needsPush checks whether the passed in config has same spec and hence push needs -// to be triggered. This is to avoid unnecessary pushes only when labels have changed -// for example. -func needsPush(prev config.Config, curr config.Config) bool { - if prev.GroupVersionKind != curr.GroupVersionKind { - // This should never happen. - return true - } - // If the config is not Istio, let us just push. - if !strings.HasSuffix(prev.GroupVersionKind.Group, "istio.io") { - return true - } - // If current metadata has "*istio.io" label/annotation, just push - for label := range curr.Meta.Labels { - if strings.Contains(label, "istio.io") { - return true - } - } - for annotation := range curr.Meta.Annotations { - if strings.Contains(annotation, "istio.io") { - return true - } - } - prevspecProto, okProtoP := prev.Spec.(proto.Message) - currspecProto, okProtoC := curr.Spec.(proto.Message) - if okProtoP && okProtoC { - return !proto.Equal(prevspecProto, currspecProto) - } - prevspecGogo, okGogoP := prev.Spec.(gogoproto.Message) - currspecGogo, okGogoC := curr.Spec.(gogoproto.Message) - if okGogoP && okGogoC { - return !gogoproto.Equal(prevspecGogo, currspecGogo) - } - return true -} diff --git a/pilot/pkg/bootstrap/config_compare_test.go b/pilot/pkg/bootstrap/config_compare_test.go deleted file mode 100644 index 75724feab..000000000 --- a/pilot/pkg/bootstrap/config_compare_test.go +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bootstrap - -import ( - "testing" -) - -import ( - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -func TestNeedsPush(t *testing.T) { - cases := []struct { - name string - prev config.Config - curr config.Config - expected bool - }{ - { - name: "different gvk", - prev: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme2-v1", - Namespace: "not-default", - }, - Spec: &networking.VirtualService{}, - }, - curr: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: "acme2-v1", - Namespace: "not-default", - }, - Spec: &networking.VirtualService{}, - }, - expected: true, - }, - { - name: "same gvk label change", - prev: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme2-v1", - Namespace: "not-default", - Labels: map[string]string{"test": "test-v1"}, - }, - Spec: &networking.VirtualService{}, - }, - curr: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme2-v1", - Namespace: "not-default", - Labels: map[string]string{"test": "test-v2"}, - }, - Spec: &networking.VirtualService{}, - }, - expected: false, - }, - { - name: "same gvk spec change", - prev: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme2-v1", - Namespace: "not-default", - Labels: map[string]string{"test": "test-v1"}, - }, - Spec: &networking.VirtualService{Hosts: []string{"test-host"}}, - }, - curr: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme2-v1", - Namespace: "not-default", - Labels: map[string]string{"test": "test-v1"}, - }, - Spec: &networking.VirtualService{Hosts: []string{"test-host", "test-host2"}}, - }, - expected: true, - }, - { - name: "config with istio.io label", - prev: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.Ingress, - Name: "acme2-v1", - Namespace: "not-default", - Labels: map[string]string{constants.AlwaysPushLabel: "true"}, - }, - }, - curr: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.Ingress, - Name: "acme2-v1", - Namespace: "not-default", - Labels: map[string]string{constants.AlwaysPushLabel: "true"}, - }, - }, - expected: true, - }, - { - name: "config with istio.io annotation", - prev: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.Ingress, - Name: "acme2-v1", - Namespace: "not-default", - Annotations: map[string]string{constants.AlwaysPushLabel: "true"}, - }, - }, - curr: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.Ingress, - Name: "acme2-v1", - Namespace: "not-default", - Annotations: map[string]string{constants.AlwaysPushLabel: "true"}, - }, - }, - expected: true, - }, - { - name: "non istio resources", - prev: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.TCPRoute, - Name: "acme2-v1", - Namespace: "not-default", - Labels: map[string]string{"test": "test-v1"}, - }, - Spec: &networking.VirtualService{}, - }, - curr: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.TCPRoute, - Name: "acme2-v1", - Namespace: "not-default", - Labels: map[string]string{"test": "test-v2"}, - }, - Spec: &networking.VirtualService{}, - }, - expected: true, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - if got := needsPush(c.prev, c.curr); got != c.expected { - t.Errorf("unexpected needsPush result. expected: %v got: %v", c.expected, got) - } - }) - } -} diff --git a/pilot/pkg/bootstrap/configcontroller.go b/pilot/pkg/bootstrap/configcontroller.go deleted file mode 100644 index 4ec43d83c..000000000 --- a/pilot/pkg/bootstrap/configcontroller.go +++ /dev/null @@ -1,365 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bootstrap - -import ( - "fmt" - "net/url" -) - -import ( - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/log" -) - -import ( - configaggregate "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/aggregate" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/crdclient" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/gateway" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/ingress" - ingressv1 "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/ingressv1" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/memory" - configmonitor "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/monitor" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/controller/workloadentry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/leaderelection" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/status/distribution" - "github.com/apache/dubbo-go-pixiu/pkg/adsc" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/incluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -// URL schemes supported by the config store -type ConfigSourceAddressScheme string - -const ( - // fs:///PATH will load local files. This replaces --configDir. - // example fs:///tmp/configroot - // PATH can be mounted from a config map or volume - File ConfigSourceAddressScheme = "fs" - // xds://ADDRESS - load XDS-over-MCP sources - // example xds://127.0.0.1:49133 - XDS ConfigSourceAddressScheme = "xds" - // k8s:// - load in-cluster k8s controller - // example k8s:// - Kubernetes ConfigSourceAddressScheme = "k8s" -) - -// initConfigController creates the config controller in the pilotConfig. -func (s *Server) initConfigController(args *PilotArgs) error { - s.initStatusController(args, features.EnableStatus) - meshConfig := s.environment.Mesh() - if len(meshConfig.ConfigSources) > 0 { - // Using MCP for config. - if err := s.initConfigSources(args); err != nil { - return err - } - } else if args.RegistryOptions.FileDir != "" { - // Local files - should be added even if other options are specified - store := memory.Make(collections.Pilot) - configController := memory.NewController(store) - - err := s.makeFileMonitor(args.RegistryOptions.FileDir, args.RegistryOptions.KubeOptions.DomainSuffix, configController) - if err != nil { - return err - } - s.ConfigStores = append(s.ConfigStores, configController) - } else { - err2 := s.initK8SConfigStore(args) - if err2 != nil { - return err2 - } - } - - // If running in ingress mode (requires k8s), wrap the config controller. - if hasKubeRegistry(args.RegistryOptions.Registries) && meshConfig.IngressControllerMode != meshconfig.MeshConfig_OFF { - // Wrap the config controller with a cache. - // Supporting only Ingress/v1 means we lose support of Kubernetes 1.18 - // Supporting only Ingress/v1beta1 means we lose support of Kubernetes 1.22 - // Since supporting both in a monolith controller is painful due to lack of usable conversion logic between - // the two versions. - // As a compromise, we instead just fork the controller. Once 1.18 support is no longer needed, we can drop the old controller - ingressV1 := ingress.V1Available(s.kubeClient) - if ingressV1 { - s.ConfigStores = append(s.ConfigStores, - ingressv1.NewController(s.kubeClient, s.environment.Watcher, args.RegistryOptions.KubeOptions)) - } else { - s.ConfigStores = append(s.ConfigStores, - ingress.NewController(s.kubeClient, s.environment.Watcher, args.RegistryOptions.KubeOptions)) - } - - s.addTerminatingStartFunc(func(stop <-chan struct{}) error { - leaderelection. - NewLeaderElection(args.Namespace, args.PodName, leaderelection.IngressController, args.Revision, s.kubeClient). - AddRunFunction(func(leaderStop <-chan struct{}) { - if ingressV1 { - ingressSyncer := ingressv1.NewStatusSyncer(s.environment.Watcher, s.kubeClient) - // Start informers again. This fixes the case where informers for namespace do not start, - // as we create them only after acquiring the leader lock - // Note: stop here should be the overall pilot stop, NOT the leader election stop. We are - // basically lazy loading the informer, if we stop it when we lose the lock we will never - // recreate it again. - s.kubeClient.RunAndWait(stop) - log.Infof("Starting ingress controller") - ingressSyncer.Run(leaderStop) - } else { - ingressSyncer := ingress.NewStatusSyncer(s.environment.Watcher, s.kubeClient) - // Start informers again. This fixes the case where informers for namespace do not start, - // as we create them only after acquiring the leader lock - // Note: stop here should be the overall pilot stop, NOT the leader election stop. We are - // basically lazy loading the informer, if we stop it when we lose the lock we will never - // recreate it again. - s.kubeClient.RunAndWait(stop) - log.Infof("Starting ingress controller") - ingressSyncer.Run(leaderStop) - } - }). - Run(stop) - return nil - }) - } - - // Wrap the config controller with a cache. - aggregateConfigController, err := configaggregate.MakeCache(s.ConfigStores) - if err != nil { - return err - } - s.configController = aggregateConfigController - - // Create the config store. - s.environment.ConfigStore = model.MakeIstioStore(s.configController) - - // Defer starting the controller until after the service is created. - s.addStartFunc(func(stop <-chan struct{}) error { - go s.configController.Run(stop) - return nil - }) - - return nil -} - -func (s *Server) initK8SConfigStore(args *PilotArgs) error { - if s.kubeClient == nil { - return nil - } - configController, err := s.makeKubeConfigController(args) - if err != nil { - return err - } - s.ConfigStores = append(s.ConfigStores, configController) - if features.EnableGatewayAPI { - if s.statusManager == nil && features.EnableGatewayAPIStatus { - s.initStatusManager(args) - } - gwc := gateway.NewController(s.kubeClient, configController, args.RegistryOptions.KubeOptions) - s.environment.GatewayAPIController = gwc - s.ConfigStores = append(s.ConfigStores, s.environment.GatewayAPIController) - s.addTerminatingStartFunc(func(stop <-chan struct{}) error { - leaderelection. - NewLeaderElection(args.Namespace, args.PodName, leaderelection.GatewayStatusController, args.Revision, s.kubeClient). - AddRunFunction(func(leaderStop <-chan struct{}) { - log.Infof("Starting gateway status writer") - gwc.SetStatusWrite(true, s.statusManager) - - // Trigger a push so we can recompute status - s.XDSServer.ConfigUpdate(&model.PushRequest{ - Full: true, - Reason: []model.TriggerReason{model.GlobalUpdate}, - }) - <-leaderStop - log.Infof("Stopping gateway status writer") - gwc.SetStatusWrite(false, nil) - }). - Run(stop) - return nil - }) - if features.EnableGatewayAPIDeploymentController { - s.addTerminatingStartFunc(func(stop <-chan struct{}) error { - leaderelection. - NewLeaderElection(args.Namespace, args.PodName, leaderelection.GatewayDeploymentController, args.Revision, s.kubeClient). - AddRunFunction(func(leaderStop <-chan struct{}) { - // We can only run this if the Gateway CRD is created - if crdclient.WaitForCRD(gvk.KubernetesGateway, leaderStop) { - controller := gateway.NewDeploymentController(s.kubeClient) - // Start informers again. This fixes the case where informers for namespace do not start, - // as we create them only after acquiring the leader lock - // Note: stop here should be the overall pilot stop, NOT the leader election stop. We are - // basically lazy loading the informer, if we stop it when we lose the lock we will never - // recreate it again. - s.kubeClient.RunAndWait(stop) - controller.Run(leaderStop) - } - }). - Run(stop) - return nil - }) - } - } - if features.EnableAnalysis { - if err := s.initInprocessAnalysisController(args); err != nil { - return err - } - } - s.RWConfigStore, err = configaggregate.MakeWriteableCache(s.ConfigStores, configController) - if err != nil { - return err - } - s.XDSServer.WorkloadEntryController = workloadentry.NewController(configController, args.PodName, args.KeepaliveOptions.MaxServerConnectionAge) - return nil -} - -// initConfigSources will process mesh config 'configSources' and initialize -// associated configs. -func (s *Server) initConfigSources(args *PilotArgs) (err error) { - for _, configSource := range s.environment.Mesh().ConfigSources { - srcAddress, err := url.Parse(configSource.Address) - if err != nil { - return fmt.Errorf("invalid config URL %s %v", configSource.Address, err) - } - scheme := ConfigSourceAddressScheme(srcAddress.Scheme) - switch scheme { - case File: - if srcAddress.Path == "" { - return fmt.Errorf("invalid fs config URL %s, contains no file path", configSource.Address) - } - store := memory.Make(collections.Pilot) - configController := memory.NewController(store) - - err := s.makeFileMonitor(srcAddress.Path, args.RegistryOptions.KubeOptions.DomainSuffix, configController) - if err != nil { - return err - } - s.ConfigStores = append(s.ConfigStores, configController) - case XDS: - xdsMCP, err := adsc.New(srcAddress.Host, &adsc.Config{ - Namespace: args.Namespace, - Workload: args.PodName, - Revision: args.Revision, - Meta: model.NodeMetadata{ - Generator: "api", - // To reduce transported data if upstream server supports. Especially for custom servers. - IstioRevision: args.Revision, - }.ToStruct(), - InitialDiscoveryRequests: adsc.ConfigInitialRequests(), - }) - if err != nil { - return fmt.Errorf("failed to dial XDS %s %v", configSource.Address, err) - } - store := memory.Make(collections.Pilot) - configController := memory.NewController(store) - configController.RegisterHasSyncedHandler(xdsMCP.HasSynced) - xdsMCP.Store = model.MakeIstioStore(configController) - err = xdsMCP.Run() - if err != nil { - return fmt.Errorf("MCP: failed running %v", err) - } - s.ConfigStores = append(s.ConfigStores, configController) - log.Warn("Started XDS config ", s.ConfigStores) - case Kubernetes: - if srcAddress.Path == "" || srcAddress.Path == "/" { - err2 := s.initK8SConfigStore(args) - if err2 != nil { - log.Warn("Error loading k8s ", err2) - return err2 - } - log.Warn("Started K8S config") - } else { - log.Warnf("Not implemented, ignore: %v", configSource.Address) - // TODO: handle k8s:// scheme for remote cluster. Use same mechanism as service registry, - // using the cluster name as key to match a secret. - } - default: - log.Warnf("Ignoring unsupported config source: %v", configSource.Address) - } - } - return nil -} - -// initInprocessAnalysisController spins up an instance of Galley which serves no purpose other than -// running Analyzers for status updates. The Status Updater will eventually need to allow input from istiod -// to support config distribution status as well. -func (s *Server) initInprocessAnalysisController(args *PilotArgs) error { - if s.statusManager == nil { - s.initStatusManager(args) - } - s.addStartFunc(func(stop <-chan struct{}) error { - go leaderelection. - NewLeaderElection(args.Namespace, args.PodName, leaderelection.AnalyzeController, args.Revision, s.kubeClient). - AddRunFunction(func(stop <-chan struct{}) { - cont, err := incluster.NewController(stop, s.RWConfigStore, - s.kubeClient, args.Namespace, s.statusManager, args.RegistryOptions.KubeOptions.DomainSuffix) - if err != nil { - return - } - cont.Run(stop) - }).Run(stop) - return nil - }) - return nil -} - -func (s *Server) initStatusController(args *PilotArgs, writeStatus bool) { - if s.statusManager == nil && writeStatus { - s.initStatusManager(args) - } - s.statusReporter = &distribution.Reporter{ - UpdateInterval: features.StatusUpdateInterval, - PodName: args.PodName, - } - s.addStartFunc(func(stop <-chan struct{}) error { - s.statusReporter.Init(s.environment.GetLedger(), stop) - return nil - }) - s.addTerminatingStartFunc(func(stop <-chan struct{}) error { - if writeStatus { - s.statusReporter.Start(s.kubeClient, args.Namespace, args.PodName, stop) - } - return nil - }) - s.XDSServer.StatusReporter = s.statusReporter - if writeStatus { - s.addTerminatingStartFunc(func(stop <-chan struct{}) error { - leaderelection. - NewLeaderElection(args.Namespace, args.PodName, leaderelection.StatusController, args.Revision, s.kubeClient). - AddRunFunction(func(stop <-chan struct{}) { - // Controller should be created for calling the run function every time, so it can - // avoid concurrently calling of informer Run() for controller in controller.Start - controller := distribution.NewController(s.kubeClient.RESTConfig(), args.Namespace, s.RWConfigStore, s.statusManager) - s.statusReporter.SetController(controller) - controller.Start(stop) - }).Run(stop) - return nil - }) - } -} - -func (s *Server) makeKubeConfigController(args *PilotArgs) (model.ConfigStoreController, error) { - return crdclient.New(s.kubeClient, args.Revision, args.RegistryOptions.KubeOptions.DomainSuffix) -} - -func (s *Server) makeFileMonitor(fileDir string, domainSuffix string, configController model.ConfigStore) error { - fileSnapshot := configmonitor.NewFileSnapshot(fileDir, collections.Pilot, domainSuffix) - fileMonitor := configmonitor.NewMonitor("file-monitor", configController, fileSnapshot.ReadConfigFiles, fileDir) - - // Defer starting the file monitor until after the service is created. - s.addStartFunc(func(stop <-chan struct{}) error { - fileMonitor.Start(stop) - return nil - }) - - return nil -} diff --git a/pilot/pkg/bootstrap/istio_ca.go b/pilot/pkg/bootstrap/istio_ca.go deleted file mode 100644 index f00345ce2..000000000 --- a/pilot/pkg/bootstrap/istio_ca.go +++ /dev/null @@ -1,593 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bootstrap - -import ( - "bytes" - "context" - "encoding/base64" - "encoding/json" - "fmt" - "os" - "path" - "strings" - "time" -) - -import ( - "github.com/fsnotify/fsnotify" - "google.golang.org/grpc" - "istio.io/api/security/v1beta1" - "istio.io/pkg/env" - "istio.io/pkg/log" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - corev1 "k8s.io/client-go/kubernetes/typed/core/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - securityModel "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/jwt" - kubelib "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/security" - "github.com/apache/dubbo-go-pixiu/security/pkg/cmd" - "github.com/apache/dubbo-go-pixiu/security/pkg/pki/ca" - "github.com/apache/dubbo-go-pixiu/security/pkg/pki/ra" - caserver "github.com/apache/dubbo-go-pixiu/security/pkg/server/ca" - "github.com/apache/dubbo-go-pixiu/security/pkg/server/ca/authenticate" -) - -type caOptions struct { - // Either extCAK8s or extCAGrpc - ExternalCAType ra.CaExternalType - ExternalCASigner string - // domain to use in SPIFFE identity URLs - TrustDomain string - Namespace string - Authenticators []security.Authenticator - CertSignerDomain string -} - -// Based on istio_ca main - removing creation of Secrets with private keys in all namespaces and install complexity. -// -// For backward compat, will preserve support for the "cacerts" Secret used for self-signed certificates. -// It is mounted in the same location, and if found will be used - creating the secret is sufficient, no need for -// extra options. -// -// In old installer, the LocalCertDir is hardcoded to /etc/cacerts and mounted from "cacerts" secret. -// -// Support for signing other root CA has been removed - too dangerous, no clear use case. -// -// Default config, for backward compat with Citadel: -// - if "cacerts" secret exists in dubbo-system, will be mounted. It may contain an optional "root-cert.pem", -// with additional roots and optional {ca-key, ca-cert, cert-chain}.pem user-provided root CA. -// - if user-provided root CA is not found, the Secret "istio-ca-secret" is used, with ca-cert.pem and ca-key.pem files. -// - if neither is found, istio-ca-secret will be created. -// -// - a config map "istio-security" with a "caTLSRootCert" file will be used for root cert, and created if needed. -// The config map was used by node agent - no longer possible to use in sds-agent, but we still save it for -// backward compat. Will be removed with the node-agent. sds-agent is calling NewCitadelClient directly, using -// K8S root. - -var ( - // LocalCertDir replaces the "cert-chain", "signing-cert" and "signing-key" flags in citadel - Istio installer is - // requires a secret named "cacerts" with specific files inside. - LocalCertDir = env.RegisterStringVar("ROOT_CA_DIR", "./etc/cacerts", - "Location of a local or mounted CA root") - - useRemoteCerts = env.RegisterBoolVar("USE_REMOTE_CERTS", false, - "Whether to try to load CA certs from a remote Kubernetes cluster. Used for external Istiod.") - - workloadCertTTL = env.RegisterDurationVar("DEFAULT_WORKLOAD_CERT_TTL", - cmd.DefaultWorkloadCertTTL, - "The default TTL of issued workload certificates. Applied when the client sets a "+ - "non-positive TTL in the CSR.") - - maxWorkloadCertTTL = env.RegisterDurationVar("MAX_WORKLOAD_CERT_TTL", - cmd.DefaultMaxWorkloadCertTTL, - "The max TTL of issued workload certificates.") - - SelfSignedCACertTTL = env.RegisterDurationVar("CITADEL_SELF_SIGNED_CA_CERT_TTL", - cmd.DefaultSelfSignedCACertTTL, - "The TTL of self-signed CA root certificate.") - - selfSignedRootCertCheckInterval = env.RegisterDurationVar("CITADEL_SELF_SIGNED_ROOT_CERT_CHECK_INTERVAL", - cmd.DefaultSelfSignedRootCertCheckInterval, - "The interval that self-signed CA checks its root certificate "+ - "expiration time and rotates root certificate. Setting this interval "+ - "to zero or a negative value disables automated root cert check and "+ - "rotation. This interval is suggested to be larger than 10 minutes.") - - selfSignedRootCertGracePeriodPercentile = env.RegisterIntVar("CITADEL_SELF_SIGNED_ROOT_CERT_GRACE_PERIOD_PERCENTILE", - cmd.DefaultRootCertGracePeriodPercentile, - "Grace period percentile for self-signed root cert.") - - enableJitterForRootCertRotator = env.RegisterBoolVar("CITADEL_ENABLE_JITTER_FOR_ROOT_CERT_ROTATOR", - true, - "If true, set up a jitter to start root cert rotator. "+ - "Jitter selects a backoff time in seconds to start root cert rotator, "+ - "and the back off time is below root cert check interval.") - - k8sInCluster = env.RegisterStringVar("KUBERNETES_SERVICE_HOST", "", - "Kubernetes service host, set automatically when running in-cluster") - - // This value can also be extracted from the mounted token - trustedIssuer = env.RegisterStringVar("TOKEN_ISSUER", "", - "OIDC token issuer. If set, will be used to check the tokens.") - - audience = env.RegisterStringVar("AUDIENCE", "", - "Expected audience in the tokens. ") - - caRSAKeySize = env.RegisterIntVar("CITADEL_SELF_SIGNED_CA_RSA_KEY_SIZE", 2048, - "Specify the RSA key size to use for self-signed Istio CA certificates.") - - // TODO: Likely to be removed and added to mesh config - externalCaType = env.RegisterStringVar("EXTERNAL_CA", "", - "External CA Integration Type. Permitted Values are ISTIOD_RA_KUBERNETES_API or "+ - "ISTIOD_RA_ISTIO_API").Get() - - // TODO: Likely to be removed and added to mesh config - k8sSigner = env.RegisterStringVar("K8S_SIGNER", "", - "Kubernates CA Signer type. Valid from Kubernates 1.18").Get() -) - -// EnableCA returns whether CA functionality is enabled in istiod. -// This is a central consistent endpoint to get whether CA functionality is -// enabled in istiod. EnableCA() is called in multiple places. -func (s *Server) EnableCA() bool { - return features.EnableCAServer -} - -// RunCA will start the cert signing GRPC service on an existing server. -// Protected by installer options: the CA will be started only if the JWT token in /var/run/secrets -// is mounted. If it is missing - for example old versions of K8S that don't support such tokens - -// we will not start the cert-signing server, since pods will have no way to authenticate. -func (s *Server) RunCA(grpc *grpc.Server, ca caserver.CertificateAuthority, opts *caOptions) { - iss := trustedIssuer.Get() - aud := audience.Get() - - token, err := os.ReadFile(getJwtPath()) - if err == nil { - tok, err := detectAuthEnv(string(token)) - if err != nil { - log.Warn("Starting with invalid K8S JWT token", err, string(token)) - } else { - if iss == "" { - iss = tok.Iss - } - if len(tok.Aud) > 0 && len(aud) == 0 { - aud = tok.Aud[0] - } - } - } - - // The CA API uses cert with the max workload cert TTL. - // 'hostlist' must be non-empty - but is not used since a grpc server is passed. - // Adds client cert auth and kube (sds enabled) - caServer, startErr := caserver.New(ca, maxWorkloadCertTTL.Get(), opts.Authenticators) - if startErr != nil { - log.Fatalf("failed to create istio ca server: %v", startErr) - } - - // TODO: if not set, parse Istiod's own token (if present) and get the issuer. The same issuer is used - // for all tokens - no need to configure twice. The token may also include cluster info to auto-configure - // networking properties. - if iss != "" && // issuer set explicitly or extracted from our own JWT - k8sInCluster.Get() == "" { // not running in cluster - in cluster use direct call to apiserver - // Add a custom authenticator using standard JWT validation, if not running in K8S - // When running inside K8S - we can use the built-in validator, which also check pod removal (invalidation). - jwtRule := v1beta1.JWTRule{Issuer: iss, Audiences: []string{aud}} - oidcAuth, err := authenticate.NewJwtAuthenticator(&jwtRule, opts.TrustDomain) - if err == nil { - caServer.Authenticators = append(caServer.Authenticators, oidcAuth) - log.Info("Using out-of-cluster JWT authentication") - } else { - log.Info("K8S token doesn't support OIDC, using only in-cluster auth") - } - } - - caServer.Register(grpc) - - log.Info("Istiod CA has started") -} - -// detectAuthEnv will use the JWT token that is mounted in istiod to set the default audience -// and trust domain for Istiod, if not explicitly defined. -// K8S will use the same kind of tokens for the pods, and the value in istiod's own token is -// simplest and safest way to have things match. -// -// Note that K8S is not required to use JWT tokens - we will fallback to the defaults -// or require explicit user option for K8S clusters using opaque tokens. -func detectAuthEnv(jwt string) (*authenticate.JwtPayload, error) { - jwtSplit := strings.Split(jwt, ".") - if len(jwtSplit) != 3 { - return nil, fmt.Errorf("invalid JWT parts: %s", jwt) - } - payload := jwtSplit[1] - - payloadBytes, err := base64.RawStdEncoding.DecodeString(payload) - if err != nil { - return nil, fmt.Errorf("failed to decode jwt: %v", err.Error()) - } - - structuredPayload := &authenticate.JwtPayload{} - err = json.Unmarshal(payloadBytes, &structuredPayload) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal jwt: %v", err.Error()) - } - - return structuredPayload, nil -} - -// detectSigningCABundle determines in which format the signing ca files are created. -// kubernetes tls secrets mount files as tls.crt,tls.key,ca.crt -// istiod secret is ca-cert.pem ca-key.pem cert-chain.pem root-cert.pem -func detectSigningCABundle() (ca.SigningCAFileBundle, error) { - tlsSigningFile := path.Join(LocalCertDir.Get(), ca.TLSSecretCACertFile) - - // looking for tls file format (tls.crt) - if _, err := os.Stat(tlsSigningFile); !os.IsNotExist(err) { - log.Info("Using kubernetes.io/tls secret type for signing ca files") - return ca.SigningCAFileBundle{ - RootCertFile: path.Join(LocalCertDir.Get(), ca.TLSSecretRootCertFile), - CertChainFiles: []string{ - tlsSigningFile, - path.Join(LocalCertDir.Get(), ca.TLSSecretRootCertFile), - }, - SigningCertFile: tlsSigningFile, - SigningKeyFile: path.Join(LocalCertDir.Get(), ca.TLSSecretCAPrivateKeyFile), - }, nil - } else if os.IsNotExist(err) { - // noop, file does not exist, move on - } else if err != nil { - return ca.SigningCAFileBundle{}, err - } - log.Info("Using istiod file format for signing ca files") - // default ca file format - return ca.SigningCAFileBundle{ - RootCertFile: path.Join(LocalCertDir.Get(), ca.RootCertFile), - CertChainFiles: []string{path.Join(LocalCertDir.Get(), ca.CertChainFile)}, - SigningCertFile: path.Join(LocalCertDir.Get(), ca.CACertFile), - SigningKeyFile: path.Join(LocalCertDir.Get(), ca.CAPrivateKeyFile), - }, nil -} - -// loadCACerts loads an existing `cacerts` Secret if the files aren't mounted locally. -// By default, a cacerts Secret would be mounted during pod startup due to the -// Istiod Deployment configuration. But with external Istiod, we want to be -// able to load cacerts from a remote cluster instead. -func (s *Server) loadCACerts(caOpts *caOptions, dir string) error { - if s.kubeClient == nil { - return nil - } - - signingKeyFile := path.Join(dir, ca.CAPrivateKeyFile) - if _, err := os.Stat(signingKeyFile); err == nil { - return nil - } else if !os.IsNotExist(err) { - return fmt.Errorf("signing key file %s already exists", signingKeyFile) - } - - secret, err := s.kubeClient.Kube().CoreV1().Secrets(caOpts.Namespace).Get( - context.TODO(), "cacerts", metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return nil - } - return err - } - - log.Infof("cacerts Secret found in config cluster, saving contents to %s", dir) - if err := os.MkdirAll(dir, 0o700); err != nil { - return err - } - for key, data := range secret.Data { - filename := path.Join(dir, key) - if err := os.WriteFile(filename, data, 0o600); err != nil { - return err - } - } - return nil -} - -// loadRemoteCACerts mounts an existing cacerts Secret if the files aren't mounted locally. -// By default, a cacerts Secret would be mounted during pod startup due to the -// Istiod Deployment configuration. But with external Istiod, we want to be -// able to load cacerts from a remote cluster instead. -func (s *Server) loadRemoteCACerts(caOpts *caOptions, dir string) error { - if s.kubeClient == nil { - return nil - } - - signingKeyFile := path.Join(dir, ca.CAPrivateKeyFile) - if _, err := os.Stat(signingKeyFile); !os.IsNotExist(err) { - return fmt.Errorf("signing key file %s already exists", signingKeyFile) - } - - secret, err := s.kubeClient.Kube().CoreV1().Secrets(caOpts.Namespace).Get( - context.TODO(), "cacerts", metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return nil - } - return err - } - - log.Infof("cacerts Secret found in remote cluster, saving contents to %s", dir) - if err := os.MkdirAll(dir, 0o700); err != nil { - return err - } - for key, data := range secret.Data { - filename := path.Join(dir, key) - if err := os.WriteFile(filename, data, 0o600); err != nil { - return err - } - } - return nil -} - -// handleEvent handles the events on cacerts related files. -// If create/write(modified) event occurs, then it verifies that -// newly introduced cacerts are intermediate CA which is generated -// from cuurent root-cert.pem. Then it updates and keycertbundle -// and generates new dns certs. -// TODO(rveerama1): Add support for new ROOT-CA rotation also. -func handleEvent(s *Server) { - log.Info("Update Istiod cacerts") - - var newCABundle []byte - var err error - - currentCABundle := s.CA.GetCAKeyCertBundle().GetRootCertPem() - - fileBundle, err := detectSigningCABundle() - if err != nil { - log.Errorf("unable to determine signing file format %v", err) - return - } - newCABundle, err = os.ReadFile(fileBundle.RootCertFile) - - if err != nil { - log.Error("failed reading root-cert.pem: ", err) - return - } - - // Only updating intermediate CA is supported now - if !bytes.Equal(currentCABundle, newCABundle) { - log.Info("Updating new ROOT-CA not supported") - return - } - - err = s.CA.GetCAKeyCertBundle().UpdateVerifiedKeyCertBundleFromFile( - fileBundle.SigningCertFile, - fileBundle.SigningKeyFile, - fileBundle.CertChainFiles, - fileBundle.RootCertFile) - - if err != nil { - log.Error("Failed to update new Plug-in CA certs: ", err) - return - } - - err = s.updatePluggedinRootCertAndGenKeyCert() - if err != nil { - log.Error("Failed generating plugged-in istiod key cert: ", err) - return - } - - log.Info("Istiod has detected the newly added intermediate CA and updated its key and certs accordingly") -} - -// handleCACertsFileWatch handles the events on cacerts files -func (s *Server) handleCACertsFileWatch() { - var timerC <-chan time.Time - for { - select { - case <-timerC: - timerC = nil - handleEvent(s) - - case event, ok := <-s.cacertsWatcher.Events: - if !ok { - log.Debug("plugin cacerts watch stopped") - return - } - - if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create { - if timerC == nil { - timerC = time.After(100 * time.Millisecond) - } - } - - case err := <-s.cacertsWatcher.Errors: - if err != nil { - log.Error("Failed to catch events on cacerts file: ", err) - return - } - - case <-s.internalStop: - return - } - } -} - -func (s *Server) addCACertsFileWatcher(dir string) error { - err := s.cacertsWatcher.Add(dir) - if err != nil { - log.Info("AUTO_RELOAD_PLUGIN_CERTS will not work, failed to add file watcher: ", err) - return err - } - - log.Info("Added cacerts files watcher at ", dir) - - return nil -} - -// initCACertsWatcher initializes the cacerts (/etc/cacerts) directory. -// In particular it monitors 'ca-key.pem', 'ca-cert.pem', 'root-cert.pem' -// and 'cert-chain.pem'. -func (s *Server) initCACertsWatcher() { - var err error - - s.cacertsWatcher, err = fsnotify.NewWatcher() - if err != nil { - log.Info("Failed to add CAcerts watcher: ", err) - return - } - - err = s.addCACertsFileWatcher(LocalCertDir.Get()) - if err != nil { - return - } - - go s.handleCACertsFileWatch() -} - -// createIstioCA initializes the Istio CA signing functionality. -// - for 'plugged in', uses ./etc/cacert directory, mounted from 'cacerts' secret in k8s. -// Inside, the key/cert are 'ca-key.pem' and 'ca-cert.pem'. The root cert signing the intermediate is root-cert.pem, -// which may contain multiple roots. A 'cert-chain.pem' file has the full cert chain. -func (s *Server) createIstioCA(client corev1.CoreV1Interface, opts *caOptions) (*ca.IstioCA, error) { - var caOpts *ca.IstioCAOptions - var err error - - fileBundle, err := detectSigningCABundle() - if err != nil { - return nil, fmt.Errorf("unable to determine signing file format %v", err) - } - if _, err := os.Stat(fileBundle.RootCertFile); err != nil { - // In Citadel, normal self-signed doesn't use a root-cert.pem file for additional roots. - // In Istiod, it is possible to provide one via "cacerts" secret in both cases, for consistency. - fileBundle.RootCertFile = "" - } - if _, err := os.Stat(fileBundle.SigningKeyFile); err != nil { - // The user-provided certs are missing - create a self-signed cert. - if client != nil { - log.Info("Use self-signed certificate as the CA certificate") - - // Abort after 20 minutes. - ctx, cancel := context.WithTimeout(context.Background(), time.Minute*20) - defer cancel() - // rootCertFile will be added to "ca-cert.pem". - // readSigningCertOnly set to false - it doesn't seem to be used in Citadel, nor do we have a way - // to set it only for one job. - caOpts, err = ca.NewSelfSignedIstioCAOptions(ctx, - selfSignedRootCertGracePeriodPercentile.Get(), SelfSignedCACertTTL.Get(), - selfSignedRootCertCheckInterval.Get(), workloadCertTTL.Get(), - maxWorkloadCertTTL.Get(), opts.TrustDomain, true, - opts.Namespace, -1, client, fileBundle.RootCertFile, - enableJitterForRootCertRotator.Get(), caRSAKeySize.Get()) - } else { - log.Warnf( - "Use local self-signed CA certificate for testing. Will use in-memory root CA, no K8S access and no ca key file %s", - fileBundle.SigningKeyFile) - - caOpts, err = ca.NewSelfSignedDebugIstioCAOptions(fileBundle.RootCertFile, SelfSignedCACertTTL.Get(), - workloadCertTTL.Get(), maxWorkloadCertTTL.Get(), opts.TrustDomain, caRSAKeySize.Get()) - } - if err != nil { - return nil, fmt.Errorf("failed to create a self-signed istiod CA: %v", err) - } - } else { - log.Info("Use local CA certificate") - - caOpts, err = ca.NewPluggedCertIstioCAOptions(fileBundle, workloadCertTTL.Get(), maxWorkloadCertTTL.Get(), caRSAKeySize.Get()) - if err != nil { - return nil, fmt.Errorf("failed to create an istiod CA: %v", err) - } - - if features.AutoReloadPluginCerts { - s.initCACertsWatcher() - } - } - istioCA, err := ca.NewIstioCA(caOpts) - if err != nil { - return nil, fmt.Errorf("failed to create an istiod CA: %v", err) - } - - // TODO: provide an endpoint returning all the roots. SDS can only pull a single root in current impl. - // ca.go saves or uses the secret, but also writes to the configmap "istio-security", under caTLSRootCert - // rootCertRotatorChan channel accepts signals to stop root cert rotator for - // self-signed CA. - // Start root cert rotator in a separate goroutine. - istioCA.Run(s.internalStop) - return istioCA, nil -} - -// createIstioRA initializes the Istio RA signing functionality. -// the caOptions defines the external provider -// ca cert can come from three sources, order matters: -// 1. Define ca cert via kubernetes secret and mount the secret through `external-ca-cert` volume -// 2. Use kubernetes ca cert `/var/run/secrets/kubernetes.io/serviceaccount/ca.crt` if signer is -// kubernetes built-in `kubernetes.io/legacy-unknown" signer -// 3. Extract from the cert-chain signed by other CSR signer. -func (s *Server) createIstioRA(client kubelib.Client, - opts *caOptions) (ra.RegistrationAuthority, error) { - caCertFile := path.Join(ra.DefaultExtCACertDir, constants.CACertNamespaceConfigMapDataName) - certSignerDomain := opts.CertSignerDomain - _, err := os.Stat(caCertFile) - if err != nil { - if !os.IsNotExist(err) { - return nil, fmt.Errorf("failed to get file info: %v", err) - } - - // File does not exist. - if certSignerDomain == "" { - log.Infof("CA cert file %q not found, using %q.", caCertFile, defaultCACertPath) - caCertFile = defaultCACertPath - } else { - log.Infof("CA cert file %q not found - ignoring.", caCertFile) - caCertFile = "" - } - } - raOpts := &ra.IstioRAOptions{ - ExternalCAType: opts.ExternalCAType, - DefaultCertTTL: workloadCertTTL.Get(), - MaxCertTTL: maxWorkloadCertTTL.Get(), - CaSigner: opts.ExternalCASigner, - CaCertFile: caCertFile, - VerifyAppendCA: true, - K8sClient: client, - TrustDomain: opts.TrustDomain, - CertSignerDomain: opts.CertSignerDomain, - } - raServer, err := ra.NewIstioRA(raOpts) - if err != nil { - return nil, err - } - raServer.SetCACertificatesFromMeshConfig(s.environment.Mesh().CaCertificates) - s.environment.AddMeshHandler(func() { - meshConfig := s.environment.Mesh() - caCertificates := meshConfig.CaCertificates - s.RA.SetCACertificatesFromMeshConfig(caCertificates) - }) - return raServer, err -} - -// getJwtPath returns jwt path. -func getJwtPath() string { - log.Info("JWT policy is ", features.JwtPolicy) - switch features.JwtPolicy { - case jwt.PolicyThirdParty: - return securityModel.K8sSATrustworthyJwtFileName - case jwt.PolicyFirstParty: - return securityModel.K8sSAJwtFileName - default: - log.Infof("unknown JWT policy %v, default to certificates ", features.JwtPolicy) - return "" - } -} diff --git a/pilot/pkg/bootstrap/istio_ca_test.go b/pilot/pkg/bootstrap/istio_ca_test.go deleted file mode 100644 index 9632988dc..000000000 --- a/pilot/pkg/bootstrap/istio_ca_test.go +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bootstrap - -import ( - "context" - "os" - "path" - "testing" -) - -import ( - . "github.com/onsi/gomega" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/security/pkg/pki/ca" -) - -const namespace = "dubbo-system" - -func TestRemoteCerts(t *testing.T) { - g := NewWithT(t) - - dir := t.TempDir() - - s := Server{ - kubeClient: kube.NewFakeClient(), - } - caOpts := &caOptions{ - Namespace: namespace, - } - - // Should do nothing because cacerts doesn't exist. - err := s.loadRemoteCACerts(caOpts, dir) - g.Expect(err).Should(BeNil()) - - _, err = os.Stat(path.Join(dir, "root-cert.pem")) - g.Expect(os.IsNotExist(err)).Should(Equal(true)) - - // Should load remote cacerts successfully. - err = createCASecret(s.kubeClient) - g.Expect(err).Should(BeNil()) - - err = s.loadRemoteCACerts(caOpts, dir) - g.Expect(err).Should(BeNil()) - - expectedRoot, err := readSampleCertFromFile("root-cert.pem") - g.Expect(err).Should(BeNil()) - - g.Expect(os.ReadFile(path.Join(dir, "root-cert.pem"))).Should(Equal(expectedRoot)) - - // Should fail because certs already exist locally. - err = s.loadRemoteCACerts(caOpts, dir) - g.Expect(err).NotTo(BeNil()) -} - -func TestRemoteTLSCerts(t *testing.T) { - g := NewWithT(t) - - dir := t.TempDir() - - s := Server{ - kubeClient: kube.NewFakeClient(), - } - caOpts := &caOptions{ - Namespace: namespace, - } - - // Should do nothing because cacerts doesn't exist. - err := s.loadCACerts(caOpts, dir) - g.Expect(err).Should(BeNil()) - - _, err = os.Stat(path.Join(dir, "ca.crt")) - g.Expect(os.IsNotExist(err)).Should(Equal(true)) - - // Should load remote cacerts successfully. - err = createCATLSSecret(s.kubeClient) - g.Expect(err).Should(BeNil()) - - err = s.loadCACerts(caOpts, dir) - g.Expect(err).Should(BeNil()) - - expectedRoot, err := readSampleCertFromFile("root-cert.pem") - g.Expect(err).Should(BeNil()) - - g.Expect(os.ReadFile(path.Join(dir, "ca.crt"))).Should(Equal(expectedRoot)) - - // Should do nothing because certs already exist locally. - err = s.loadCACerts(caOpts, dir) - g.Expect(err).Should(BeNil()) -} - -func createCATLSSecret(client kube.Client) error { - var caCert, caKey, rootCert []byte - var err error - if caCert, err = readSampleCertFromFile("ca-cert.pem"); err != nil { - return err - } - if caKey, err = readSampleCertFromFile("ca-key.pem"); err != nil { - return err - } - if rootCert, err = readSampleCertFromFile("root-cert.pem"); err != nil { - return err - } - - secrets := client.Kube().CoreV1().Secrets(namespace) - secret := &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: "cacerts", - }, - Type: v1.SecretTypeTLS, - Data: map[string][]byte{ - "tls.crt": caCert, - "tls.key": caKey, - "ca.crt": rootCert, - }, - } - if _, err = secrets.Create(context.TODO(), secret, metav1.CreateOptions{}); err != nil { - return err - } - return nil -} - -func createCASecret(client kube.Client) error { - var caCert, caKey, certChain, rootCert []byte - var err error - if caCert, err = readSampleCertFromFile("ca-cert.pem"); err != nil { - return err - } - if caKey, err = readSampleCertFromFile("ca-key.pem"); err != nil { - return err - } - if certChain, err = readSampleCertFromFile("cert-chain.pem"); err != nil { - return err - } - if rootCert, err = readSampleCertFromFile("root-cert.pem"); err != nil { - return err - } - - secrets := client.Kube().CoreV1().Secrets(namespace) - secret := &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: "cacerts", - }, - Data: map[string][]byte{ - ca.CACertFile: caCert, - ca.CAPrivateKeyFile: caKey, - ca.CertChainFile: certChain, - ca.RootCertFile: rootCert, - }, - } - if _, err = secrets.Create(context.TODO(), secret, metav1.CreateOptions{}); err != nil { - return err - } - return nil -} - -func readSampleCertFromFile(f string) ([]byte, error) { - return os.ReadFile(path.Join(env.IstioSrc, "samples/certs", f)) -} diff --git a/pilot/pkg/bootstrap/leak_test.go b/pilot/pkg/bootstrap/leak_test.go deleted file mode 100644 index 26ba0facf..000000000 --- a/pilot/pkg/bootstrap/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bootstrap - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/bootstrap/mesh.go b/pilot/pkg/bootstrap/mesh.go deleted file mode 100644 index 6850faf8a..000000000 --- a/pilot/pkg/bootstrap/mesh.go +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bootstrap - -import ( - "encoding/json" - "os" -) - -import ( - "istio.io/pkg/filewatcher" - "istio.io/pkg/log" - "istio.io/pkg/version" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh/kubemesh" -) - -const ( - // defaultMeshConfigMapName is the default name of the ConfigMap with the mesh config - // The actual name can be different - use getMeshConfigMapName - defaultMeshConfigMapName = "istio" - // configMapKey should match the expected MeshConfig file name - configMapKey = "mesh" -) - -// initMeshConfiguration creates the mesh in the pilotConfig from the input arguments. -// Original/default behavior: -// - use the mounted file, if it exists. -// - use istio-REVISION if k8s is enabled -// - fallback to default -// -// If the 'SHARED_MESH_CONFIG' env is set (experimental feature in 1.10): -// - if a file exist, load it - will be merged -// - if istio-REVISION exists, will be used, even if the file is present. -// - the SHARED_MESH_CONFIG config map will also be loaded and merged. -func (s *Server) initMeshConfiguration(args *PilotArgs, fileWatcher filewatcher.FileWatcher) { - log.Info("initializing mesh configuration ", args.MeshConfigFile) - defer func() { - if s.environment.Watcher != nil { - log.Infof("mesh configuration: %s", mesh.PrettyFormatOfMeshConfig(s.environment.Mesh())) - log.Infof("version: %s", version.Info.String()) - argsdump, _ := json.MarshalIndent(args, "", " ") - log.Infof("flags: %s", argsdump) - } - }() - - // Watcher will be merging more than one mesh config source? - multiWatch := features.SharedMeshConfig != "" - - var err error - if _, err = os.Stat(args.MeshConfigFile); !os.IsNotExist(err) { - s.environment.Watcher, err = mesh.NewFileWatcher(fileWatcher, args.MeshConfigFile, multiWatch) - if err == nil { - if multiWatch && s.kubeClient != nil { - kubemesh.AddUserMeshConfig( - s.kubeClient, s.environment.Watcher, args.Namespace, configMapKey, features.SharedMeshConfig, s.internalStop) - } else { - // Normal install no longer uses this mode - testing and special installs still use this. - log.Warnf("Using local mesh config file %s, in cluster configs ignored", args.MeshConfigFile) - } - return - } - } - - // Config file either didn't exist or failed to load. - if s.kubeClient == nil { - // Use a default mesh. - meshConfig := mesh.DefaultMeshConfig() - s.environment.Watcher = mesh.NewFixedWatcher(meshConfig) - log.Warnf("Using default mesh - missing file %s and no k8s client", args.MeshConfigFile) - return - } - - // Watch the istio ConfigMap for mesh config changes. - // This may be necessary for external Istiod. - configMapName := getMeshConfigMapName(args.Revision) - multiWatcher := kubemesh.NewConfigMapWatcher( - s.kubeClient, args.Namespace, configMapName, configMapKey, multiWatch, s.internalStop) - s.environment.Watcher = multiWatcher - s.environment.NetworksWatcher = multiWatcher - log.Infof("initializing mesh networks from mesh config watcher") - - if multiWatch { - kubemesh.AddUserMeshConfig(s.kubeClient, s.environment.Watcher, args.Namespace, configMapKey, features.SharedMeshConfig, s.internalStop) - } -} - -// initMeshNetworks loads the mesh networks configuration from the file provided -// in the args and add a watcher for changes in this file. -func (s *Server) initMeshNetworks(args *PilotArgs, fileWatcher filewatcher.FileWatcher) { - if s.environment.NetworksWatcher != nil { - return - } - log.Info("initializing mesh networks") - if args.NetworksConfigFile != "" { - var err error - s.environment.NetworksWatcher, err = mesh.NewNetworksWatcher(fileWatcher, args.NetworksConfigFile) - if err != nil { - log.Info(err) - } - } - - if s.environment.NetworksWatcher == nil { - log.Info("mesh networks configuration not provided") - s.environment.NetworksWatcher = mesh.NewFixedNetworksWatcher(nil) - } -} - -func getMeshConfigMapName(revision string) string { - name := defaultMeshConfigMapName - if revision == "" || revision == "default" { - return name - } - return name + "-" + revision -} diff --git a/pilot/pkg/bootstrap/monitoring.go b/pilot/pkg/bootstrap/monitoring.go deleted file mode 100644 index ab9854542..000000000 --- a/pilot/pkg/bootstrap/monitoring.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bootstrap - -import ( - "fmt" - "net" - "net/http" - "time" -) - -import ( - ocprom "contrib.go.opencensus.io/exporter/prometheus" - "github.com/prometheus/client_golang/prometheus" - "go.opencensus.io/stats/view" - "istio.io/pkg/log" - "istio.io/pkg/monitoring" - "istio.io/pkg/version" -) - -type monitor struct { - monitoringServer *http.Server -} - -const ( - metricsPath = "/metrics" - versionPath = "/version" -) - -var ( - serverStart = time.Now() - - uptime = monitoring.NewDerivedGauge( // nolint: deadcode, varcheck - "istiod_uptime_seconds", - "Current istiod server uptime in seconds", - ) -) - -func init() { - uptime.ValueFrom(func() float64 { - return time.Since(serverStart).Seconds() - }) -} - -func addMonitor(mux *http.ServeMux) error { - exporter, err := ocprom.NewExporter(ocprom.Options{Registry: prometheus.DefaultRegisterer.(*prometheus.Registry)}) - if err != nil { - return fmt.Errorf("could not set up prometheus exporter: %v", err) - } - view.RegisterExporter(exporter) - mux.Handle(metricsPath, exporter) - - mux.HandleFunc(versionPath, func(out http.ResponseWriter, req *http.Request) { - if _, err := out.Write([]byte(version.Info.String())); err != nil { - log.Errorf("Unable to write version string: %v", err) - } - }) - - return nil -} - -// Deprecated: we shouldn't have 2 http ports. Will be removed after code using -// this port is removed. -func startMonitor(addr string, mux *http.ServeMux) (*monitor, error) { - m := &monitor{} - - // get the network stuff setup - var listener net.Listener - if addr != "" { - var err error - if listener, err = net.Listen("tcp", addr); err != nil { - return nil, fmt.Errorf("unable to listen on socket: %v", err) - } - } - - // NOTE: this is a temporary solution to provide bare-bones debug functionality - // for pilot. a full design / implementation of self-monitoring and reporting - // is coming. that design will include proper coverage of statusz/healthz type - // functionality, in addition to how pilot reports its own metrics. - if err := addMonitor(mux); err != nil { - return nil, fmt.Errorf("could not establish self-monitoring: %v", err) - } - if addr != "" { - m.monitoringServer = &http.Server{ - Addr: listener.Addr().String(), - Handler: mux, - IdleTimeout: 90 * time.Second, // matches http.DefaultTransport keep-alive timeout - ReadTimeout: 30 * time.Second, - } - } - - version.Info.RecordComponentBuildTag("pilot") - - if addr != "" { - go func() { - _ = m.monitoringServer.Serve(listener) - }() - } - - return m, nil -} - -func (m *monitor) Close() error { - if m.monitoringServer != nil { - return m.monitoringServer.Close() - } - return nil -} - -// initMonitor initializes the configuration for the pilot monitoring server. -func (s *Server) initMonitor(addr string) error { // nolint: unparam - s.addStartFunc(func(stop <-chan struct{}) error { - monitor, err := startMonitor(addr, s.monitoringMux) - if err != nil { - return err - } - go func() { - <-stop - err := monitor.Close() - log.Debugf("Monitoring server terminated: %v", err) - }() - return nil - }) - return nil -} diff --git a/pilot/pkg/bootstrap/options.go b/pilot/pkg/bootstrap/options.go deleted file mode 100644 index 251e19350..000000000 --- a/pilot/pkg/bootstrap/options.go +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bootstrap - -import ( - "crypto/tls" - "fmt" - "time" -) - -import ( - "istio.io/pkg/ctrlz" - "istio.io/pkg/env" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - kubecontroller "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube/controller" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/keepalive" -) - -// RegistryOptions provide configuration options for the configuration controller. If FileDir is set, that directory will -// be monitored for CRD yaml files and will update the controller as those files change (This is used for testing -// purposes). Otherwise, a CRD client is created based on the configuration. -type RegistryOptions struct { - // If FileDir is set, the below kubernetes options are ignored - FileDir string - - Registries []string - - // Kubernetes controller options - KubeOptions kubecontroller.Options - // ClusterRegistriesNamespace specifies where the multi-cluster secret resides - ClusterRegistriesNamespace string - KubeConfig string - - // DistributionTracking control - DistributionCacheRetention time.Duration - - // DistributionTracking control - DistributionTrackingEnabled bool -} - -// PilotArgs provides all of the configuration parameters for the Pilot discovery service. -type PilotArgs struct { - ServerOptions DiscoveryServerOptions - InjectionOptions InjectionOptions - PodName string - Namespace string - Revision string - MeshConfigFile string - NetworksConfigFile string - RegistryOptions RegistryOptions - CtrlZOptions *ctrlz.Options - KeepaliveOptions *keepalive.Options - ShutdownDuration time.Duration - JwtRule string -} - -// DiscoveryServerOptions contains options for create a new discovery server instance. -type DiscoveryServerOptions struct { - // The listening address for HTTP (debug). If the port in the address is empty or "0" (as in "127.0.0.1:" or "[::1]:0") - // a port number is automatically chosen. - HTTPAddr string - - // The listening address for HTTPS (webhooks). If the port in the address is empty or "0" (as in "127.0.0.1:" or "[::1]:0") - // a port number is automatically chosen. - // If the address is empty, the secure port is disabled, and the - // webhooks are registered on the HTTP port - a gateway in front will - // terminate TLS instead. - HTTPSAddr string - - // The listening address for gRPC. If the port in the address is empty or "0" (as in "127.0.0.1:" or "[::1]:0") - // a port number is automatically chosen. - GRPCAddr string - - // The listening address for the monitoring port. If the port in the address is empty or "0" (as in "127.0.0.1:" or "[::1]:0") - // a port number is automatically chosen. - MonitoringAddr string - - EnableProfiling bool - - // Optional TLS configuration - TLSOptions TLSOptions - - // The listening address for secured gRPC. If the port in the address is empty or "0" (as in "127.0.0.1:" or "[::1]:0") - // a port number is automatically chosen. - SecureGRPCAddr string -} - -type InjectionOptions struct { - // Directory of injection related config files. - InjectionDirectory string -} - -// TLSOptions is optional TLS parameters for Istiod server. -type TLSOptions struct { - CaCertFile string - CertFile string - KeyFile string - TLSCipherSuites []string - CipherSuits []uint16 // This is the parsed cipher suites -} - -var ( - PodNamespace = env.RegisterStringVar("POD_NAMESPACE", constants.IstioSystemNamespace, "").Get() - PodName = env.RegisterStringVar("POD_NAME", "", "").Get() - JwtRule = env.RegisterStringVar("JWT_RULE", "", - "The JWT rule used by istiod authentication").Get() -) - -// Revision is the value of the Istio control plane revision, e.g. "canary", -// and is the value used by the "istio.io/rev" label. -var Revision = env.RegisterStringVar("REVISION", "", "").Get() - -// NewPilotArgs constructs pilotArgs with default values. -func NewPilotArgs(initFuncs ...func(*PilotArgs)) *PilotArgs { - p := &PilotArgs{} - - // Apply Default Values. - p.applyDefaults() - - // Apply custom initialization functions. - for _, fn := range initFuncs { - fn(p) - } - - return p -} - -// Apply default value to PilotArgs -func (p *PilotArgs) applyDefaults() { - p.Namespace = PodNamespace - p.PodName = PodName - p.Revision = Revision - p.JwtRule = JwtRule - p.KeepaliveOptions = keepalive.DefaultOption() - p.RegistryOptions.DistributionTrackingEnabled = features.EnableDistributionTracking - p.RegistryOptions.DistributionCacheRetention = features.DistributionHistoryRetention - p.RegistryOptions.ClusterRegistriesNamespace = p.Namespace -} - -func (p *PilotArgs) Complete() error { - cipherSuits, err := TLSCipherSuites(p.ServerOptions.TLSOptions.TLSCipherSuites) - if err != nil { - return err - } - p.ServerOptions.TLSOptions.CipherSuits = cipherSuits - return nil -} - -func allCiphers() map[string]uint16 { - acceptedCiphers := make(map[string]uint16, len(tls.CipherSuites())+len(tls.InsecureCipherSuites())) - for _, cipher := range tls.InsecureCipherSuites() { - acceptedCiphers[cipher.Name] = cipher.ID - } - for _, cipher := range tls.CipherSuites() { - acceptedCiphers[cipher.Name] = cipher.ID - } - return acceptedCiphers -} - -// TLSCipherSuites returns a list of cipher suite IDs from the cipher suite names passed. -func TLSCipherSuites(cipherNames []string) ([]uint16, error) { - if len(cipherNames) == 0 { - return nil, nil - } - ciphersIntSlice := make([]uint16, 0) - possibleCiphers := allCiphers() - for _, cipher := range cipherNames { - intValue, ok := possibleCiphers[cipher] - if !ok { - return nil, fmt.Errorf("cipher suite %s not supported or doesn't exist", cipher) - } - ciphersIntSlice = append(ciphersIntSlice, intValue) - } - return ciphersIntSlice, nil -} diff --git a/pilot/pkg/bootstrap/server.go b/pilot/pkg/bootstrap/server.go deleted file mode 100644 index cc1cec1c1..000000000 --- a/pilot/pkg/bootstrap/server.go +++ /dev/null @@ -1,1322 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bootstrap - -import ( - "context" - "crypto/tls" - "crypto/x509" - "encoding/json" - "errors" - "fmt" - "net" - "net/http" - "os" - "strings" - "sync" - "time" -) - -import ( - "github.com/fsnotify/fsnotify" - prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" - prom "github.com/prometheus/client_golang/prometheus" - "golang.org/x/net/http2" - "golang.org/x/net/http2/h2c" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/reflection" - "istio.io/api/security/v1beta1" - "istio.io/pkg/ctrlz" - "istio.io/pkg/filewatcher" - "istio.io/pkg/log" - "istio.io/pkg/version" - v1 "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/cache" -) - -import ( - kubecredentials "github.com/apache/dubbo-go-pixiu/pilot/pkg/credentials/kube" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - istiogrpc "github.com/apache/dubbo-go-pixiu/pilot/pkg/grpc" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/keycertbundle" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - dubbov1alpha1 "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/dubbo/v1alpha1" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/server" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/aggregate" - kubecontroller "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube/controller" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/serviceentry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/status" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/status/distribution" - tb "github.com/apache/dubbo-go-pixiu/pilot/pkg/trustbundle" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - istiokeepalive "github.com/apache/dubbo-go-pixiu/pkg/keepalive" - kubelib "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/inject" - "github.com/apache/dubbo-go-pixiu/pkg/kube/multicluster" - "github.com/apache/dubbo-go-pixiu/pkg/security" - "github.com/apache/dubbo-go-pixiu/pkg/spiffe" - "github.com/apache/dubbo-go-pixiu/security/pkg/k8s/chiron" - "github.com/apache/dubbo-go-pixiu/security/pkg/pki/ca" - "github.com/apache/dubbo-go-pixiu/security/pkg/pki/ra" - "github.com/apache/dubbo-go-pixiu/security/pkg/server/ca/authenticate" - "github.com/apache/dubbo-go-pixiu/security/pkg/server/ca/authenticate/kubeauth" -) - -const ( - // debounce file watcher events to minimize noise in logs - watchDebounceDelay = 100 * time.Millisecond -) - -func init() { - // Disable gRPC tracing. It has performance impacts (See https://github.com/grpc/grpc-go/issues/695) - grpc.EnableTracing = false - - // Export pilot version as metric for fleet analytics. - pilotVersion := prom.NewGaugeVec(prom.GaugeOpts{ - Name: "pilot_info", - Help: "Pilot version and build information.", - }, []string{"version"}) - prom.MustRegister(pilotVersion) - pilotVersion.With(prom.Labels{"version": version.Info.String()}).Set(1) -} - -// readinessProbe defines a function that will be used indicate whether a server is ready. -type readinessProbe func() (bool, error) - -// Server contains the runtime configuration for the Pilot discovery service. -type Server struct { - XDSServer *xds.DiscoveryServer - - clusterID cluster.ID - environment *model.Environment - - kubeClient kubelib.Client - - multiclusterController *multicluster.Controller - - configController model.ConfigStoreController - ConfigStores []model.ConfigStoreController - serviceEntryController *serviceentry.Controller - - httpServer *http.Server // debug, monitoring and readiness Server. - httpsServer *http.Server // webhooks HTTPS Server. - httpsReadyClient *http.Client - - grpcServer *grpc.Server - grpcAddress string - secureGrpcServer *grpc.Server - secureGrpcAddress string - - // monitoringMux listens on monitoringAddr(:15014). - // Currently runs prometheus monitoring and debug (if enabled). - monitoringMux *http.ServeMux - - // httpMux listens on the httpAddr (8080). - // If a Gateway is used in front and https is off it is also multiplexing - // the rest of the features if their port is empty. - // Currently runs readiness and debug (if enabled) - httpMux *http.ServeMux - - // httpsMux listens on the httpsAddr(15017), handling webhooks - // If the address os empty, the webhooks will be set on the default httpPort. - httpsMux *http.ServeMux // webhooks - - // MultiplexGRPC will serve gRPC and HTTP (1 or 2) over the HTTPListener, if enabled. - MultiplexGRPC bool - - // fileWatcher used to watch mesh config, networks and certificates. - fileWatcher filewatcher.FileWatcher - - // certWatcher watches the certificates for changes and triggers a notification to Istiod. - cacertsWatcher *fsnotify.Watcher - dnsNames []string - - certController *chiron.WebhookController - CA *ca.IstioCA - RA ra.RegistrationAuthority - - // TrustAnchors for workload to workload mTLS - workloadTrustBundle *tb.TrustBundle - certMu sync.RWMutex - istiodCert *tls.Certificate - istiodCertBundleWatcher *keycertbundle.Watcher - server server.Instance - - readinessProbes map[string]readinessProbe - - // duration used for graceful shutdown. - shutdownDuration time.Duration - - // internalStop is closed when the server is shutdown. This should be avoided as much as possible, in - // favor of AddStartFunc. This is only required if we *must* start something outside of this process. - // For example, everything depends on mesh config, so we use it there rather than trying to sequence everything - // in AddStartFunc - internalStop chan struct{} - - statusReporter *distribution.Reporter - statusManager *status.Manager - // RWConfigStore is the configstore which allows updates, particularly for status. - RWConfigStore model.ConfigStoreController - - // ServiceMetadataServer - metadataServer *dubbov1alpha1.ServiceMetadataServer - - //Service Name mapping register - snpServer *dubbov1alpha1.Snp -} - -// NewServer creates a new Server instance based on the provided arguments. -func NewServer(args *PilotArgs, initFuncs ...func(*Server)) (*Server, error) { - e := &model.Environment{ - PushContext: model.NewPushContext(), - DomainSuffix: args.RegistryOptions.KubeOptions.DomainSuffix, - } - e.SetLedger(buildLedger(args.RegistryOptions)) - - ac := aggregate.NewController(aggregate.Options{ - MeshHolder: e, - }) - e.ServiceDiscovery = ac - - s := &Server{ - clusterID: getClusterID(args), - environment: e, - fileWatcher: filewatcher.NewWatcher(), - httpMux: http.NewServeMux(), - monitoringMux: http.NewServeMux(), - readinessProbes: make(map[string]readinessProbe), - workloadTrustBundle: tb.NewTrustBundle(nil), - server: server.New(), - shutdownDuration: args.ShutdownDuration, - internalStop: make(chan struct{}), - istiodCertBundleWatcher: keycertbundle.NewWatcher(), - } - // Apply custom initialization functions. - for _, fn := range initFuncs { - fn(s) - } - // Initialize workload Trust Bundle before XDS Server - e.TrustBundle = s.workloadTrustBundle - s.XDSServer = xds.NewDiscoveryServer(e, args.PodName, args.RegistryOptions.KubeOptions.ClusterAliases) - - prometheus.EnableHandlingTimeHistogram() - - // Apply the arguments to the configuration. - if err := s.initKubeClient(args); err != nil { - return nil, fmt.Errorf("error initializing kube client: %v", err) - } - - // used for both initKubeRegistry and initClusterRegistries - args.RegistryOptions.KubeOptions.EndpointMode = kubecontroller.DetectEndpointMode(s.kubeClient) - - s.initMeshConfiguration(args, s.fileWatcher) - spiffe.SetTrustDomain(s.environment.Mesh().GetTrustDomain()) - - s.initMeshNetworks(args, s.fileWatcher) - s.initMeshHandlers() - s.environment.Init() - if err := s.environment.InitNetworksManager(s.XDSServer); err != nil { - return nil, err - } - - // Options based on the current 'defaults' in istio. - caOpts := &caOptions{ - TrustDomain: s.environment.Mesh().TrustDomain, - Namespace: args.Namespace, - ExternalCAType: ra.CaExternalType(externalCaType), - CertSignerDomain: features.CertSignerDomain, - } - - if caOpts.ExternalCAType == ra.ExtCAK8s { - // Older environment variable preserved for backward compatibility - caOpts.ExternalCASigner = k8sSigner - } - // CA signing certificate must be created first if needed. - if err := s.maybeCreateCA(caOpts); err != nil { - return nil, err - } - - if err := s.initControllers(args); err != nil { - return nil, err - } - - s.XDSServer.InitGenerators(e, args.Namespace) - - // Initialize workloadTrustBundle after CA has been initialized - if err := s.initWorkloadTrustBundle(args); err != nil { - return nil, err - } - - // Parse and validate Istiod Address. - istiodHost, _, err := e.GetDiscoveryAddress() - if err != nil { - return nil, err - } - - // Create Istiod certs and setup watches. - if err := s.initIstiodCerts(args, string(istiodHost)); err != nil { - return nil, err - } - - if s.kubeClient != nil { - s.metadataServer = dubbov1alpha1.NewServiceMetadataServer(s.kubeClient) - } - - // Create Service Name mapping server - if s.kubeClient != nil { - s.snpServer = dubbov1alpha1.NewSnp(s.kubeClient) - } - - // Secure gRPC Server must be initialized after CA is created as may use a Citadel generated cert. - if err := s.initSecureDiscoveryService(args); err != nil { - return nil, fmt.Errorf("error initializing secure gRPC Listener: %v", err) - } - - var wh *inject.Webhook - // common https server for webhooks (e.g. injection, validation) - if s.kubeClient != nil { - s.initSecureWebhookServer(args) - wh, err = s.initSidecarInjector(args) - if err != nil { - return nil, fmt.Errorf("error initializing sidecar injector: %v", err) - } - if err := s.initConfigValidation(args); err != nil { - return nil, fmt.Errorf("error initializing config validator: %v", err) - } - } - - whc := func() map[string]string { - if wh != nil { - return wh.Config.RawTemplates - } - return map[string]string{} - } - - // Used for readiness, monitoring and debug handlers. - if err := s.initIstiodAdminServer(args, whc); err != nil { - return nil, fmt.Errorf("error initializing debug server: %v", err) - } - // This should be called only after controllers are initialized. - s.initRegistryEventHandlers() - - s.initDiscoveryService(args) - - s.initSDSServer() - - // Notice that the order of authenticators matters, since at runtime - // authenticators are activated sequentially and the first successful attempt - // is used as the authentication result. - authenticators := []security.Authenticator{ - &authenticate.ClientCertAuthenticator{}, - } - if args.JwtRule != "" { - jwtAuthn, err := initOIDC(args, s.environment.Mesh().TrustDomain) - if err != nil { - return nil, fmt.Errorf("error initializing OIDC: %v", err) - } - if jwtAuthn == nil { - return nil, fmt.Errorf("JWT authenticator is nil") - } - authenticators = append(authenticators, jwtAuthn) - } - // The k8s JWT authenticator requires the multicluster registry to be initialized, - // so we build it later. - authenticators = append(authenticators, - kubeauth.NewKubeJWTAuthenticator(s.environment.Watcher, s.kubeClient, s.clusterID, s.multiclusterController.GetRemoteKubeClient, features.JwtPolicy)) - if features.XDSAuth { - s.XDSServer.Authenticators = authenticators - } - caOpts.Authenticators = authenticators - - // Start CA or RA server. This should be called after CA and Istiod certs have been created. - s.startCA(caOpts) - - // TODO: don't run this if galley is started, one ctlz is enough - if args.CtrlZOptions != nil { - _, _ = ctrlz.Run(args.CtrlZOptions, nil) - } - - // This must be last, otherwise we will not know which informers to register - if s.kubeClient != nil { - s.addStartFunc(func(stop <-chan struct{}) error { - s.kubeClient.RunAndWait(stop) - return nil - }) - } - - s.addReadinessProbe("discovery", func() (bool, error) { - return s.XDSServer.IsServerReady(), nil - }) - - return s, nil -} - -func initOIDC(args *PilotArgs, trustDomain string) (security.Authenticator, error) { - // JWTRule is from the JWT_RULE environment variable. - // An example of json string for JWTRule is: - // `{"issuer": "foo", "jwks_uri": "baz", "audiences": ["aud1", "aud2"]}`. - jwtRule := &v1beta1.JWTRule{} - err := json.Unmarshal([]byte(args.JwtRule), jwtRule) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal JWT rule: %v", err) - } - log.Infof("Istiod authenticating using JWTRule: %v", jwtRule) - jwtAuthn, err := authenticate.NewJwtAuthenticator(jwtRule, trustDomain) - if err != nil { - return nil, fmt.Errorf("failed to create the JWT authenticator: %v", err) - } - return jwtAuthn, nil -} - -func getClusterID(args *PilotArgs) cluster.ID { - clusterID := args.RegistryOptions.KubeOptions.ClusterID - if clusterID == "" { - if hasKubeRegistry(args.RegistryOptions.Registries) { - clusterID = cluster.ID(provider.Kubernetes) - } - } - return clusterID -} - -func isUnexpectedListenerError(err error) bool { - if err == nil { - return false - } - if errors.Is(err, net.ErrClosed) { - return false - } - if errors.Is(err, http.ErrServerClosed) { - return false - } - return true -} - -// Start starts all components of the Pilot discovery service on the port specified in DiscoveryServerOptions. -// If Port == 0, a port number is automatically chosen. Content serving is started by this method, -// but is executed asynchronously. Serving can be canceled at any time by closing the provided stop channel. -func (s *Server) Start(stop <-chan struct{}) error { - log.Infof("Starting Istiod Server with primary cluster %s", s.clusterID) - - if features.UnsafeFeaturesEnabled() { - log.Warn("Server is starting with unsafe features enabled") - } - - // Now start all of the components. - if err := s.server.Start(stop); err != nil { - return err - } - if !s.waitForCacheSync(stop) { - return fmt.Errorf("failed to sync cache") - } - // Inform Discovery Server so that it can start accepting connections. - s.XDSServer.CachesSynced() - - // Race condition - if waitForCache is too fast and we run this as a startup function, - // the grpc server would be started before CA is registered. Listening should be last. - if s.secureGrpcAddress != "" { - grpcListener, err := net.Listen("tcp", s.secureGrpcAddress) - if err != nil { - return err - } - go func() { - log.Infof("starting secure gRPC discovery service at %s", grpcListener.Addr()) - if err := s.secureGrpcServer.Serve(grpcListener); err != nil { - log.Errorf("error serving secure GRPC server: %v", err) - } - }() - } - - if s.grpcAddress != "" { - grpcListener, err := net.Listen("tcp", s.grpcAddress) - if err != nil { - return err - } - go func() { - log.Infof("starting gRPC discovery service at %s", grpcListener.Addr()) - if err := s.grpcServer.Serve(grpcListener); err != nil { - log.Errorf("error serving GRPC server: %v", err) - } - }() - } - - if s.MultiplexGRPC { - log.Infof("multiplexing gRPC services with HTTP services") - h2s := &http2.Server{ - MaxConcurrentStreams: uint32(features.MaxConcurrentStreams), - } - // In the past, we have tried using "cmux" to handle multiplexing. This only works if we have - // only HTTP/1.1 and gRPC on the same port. If we have gRPC and HTTP2, clients (envoy) may - // multiplex the connections. cmux works at the connection level, so if the first request is - // gRPC then all future non-GRPC HTTP2 requests will match the gRPC server and fail. The major - // downside of multiplexing by using gRPC's ServeHTTP is that we are using the golang HTTP2 - // stack. This means a lot of features on the gRPC server (keepalives, etc) do not apply. - multiplexHandler := h2c.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // If we detect gRPC, serve using grpcServer - if r.ProtoMajor == 2 && strings.HasPrefix(r.Header.Get("content-type"), "application/grpc") { - s.grpcServer.ServeHTTP(w, r) - return - } - // Otherwise, this is meant for the standard HTTP server - s.httpMux.ServeHTTP(w, r) - }), h2s) - s.httpServer.Handler = multiplexHandler - } - - // At this point we are ready - start Http Listener so that it can respond to readiness events. - httpListener, err := net.Listen("tcp", s.httpServer.Addr) - if err != nil { - return err - } - go func() { - log.Infof("starting HTTP service at %s", httpListener.Addr()) - if err := s.httpServer.Serve(httpListener); isUnexpectedListenerError(err) { - log.Errorf("error serving http server: %v", err) - } - }() - - if s.httpsServer != nil { - httpsListener, err := net.Listen("tcp", s.httpsServer.Addr) - if err != nil { - return err - } - go func() { - log.Infof("starting webhook service at %s", httpsListener.Addr()) - if err := s.httpsServer.ServeTLS(httpsListener, "", ""); isUnexpectedListenerError(err) { - log.Errorf("error serving https server: %v", err) - } - }() - } - - s.waitForShutdown(stop) - - return nil -} - -// WaitUntilCompletion waits for everything marked as a "required termination" to complete. -// This should be called before exiting. -func (s *Server) WaitUntilCompletion() { - s.server.Wait() -} - -// initSDSServer starts the SDS server -func (s *Server) initSDSServer() { - if s.kubeClient == nil { - return - } - if !features.EnableXDSIdentityCheck { - // Make sure we have security - log.Warnf("skipping Kubernetes credential reader; PILOT_ENABLE_XDS_IDENTITY_CHECK must be set to true for this feature.") - } else { - creds := kubecredentials.NewMulticluster(s.clusterID) - creds.AddSecretHandler(func(name string, namespace string) { - s.XDSServer.ConfigUpdate(&model.PushRequest{ - Full: false, - ConfigsUpdated: map[model.ConfigKey]struct{}{ - { - Kind: gvk.Secret, - Name: name, - Namespace: namespace, - }: {}, - }, - Reason: []model.TriggerReason{model.SecretTrigger}, - }) - }) - s.XDSServer.Generators[v3.SecretType] = xds.NewSecretGen(creds, s.XDSServer.Cache, s.clusterID, s.environment.Mesh()) - s.multiclusterController.AddHandler(creds) - if ecdsGen, found := s.XDSServer.Generators[v3.ExtensionConfigurationType]; found { - ecdsGen.(*xds.EcdsGenerator).SetCredController(creds) - } - } -} - -// initKubeClient creates the k8s client if running in an k8s environment. -// This is determined by the presence of a kube registry, which -// uses in-context k8s, or a config source of type k8s. -func (s *Server) initKubeClient(args *PilotArgs) error { - if s.kubeClient != nil { - // Already initialized by startup arguments - return nil - } - hasK8SConfigStore := false - if args.RegistryOptions.FileDir == "" { - // If file dir is set - config controller will just use file. - if _, err := os.Stat(args.MeshConfigFile); !os.IsNotExist(err) { - meshConfig, err := mesh.ReadMeshConfig(args.MeshConfigFile) - if err != nil { - return fmt.Errorf("failed reading mesh config: %v", err) - } - if len(meshConfig.ConfigSources) == 0 && args.RegistryOptions.KubeConfig != "" { - hasK8SConfigStore = true - } - for _, cs := range meshConfig.ConfigSources { - if cs.Address == string(Kubernetes)+"://" { - hasK8SConfigStore = true - break - } - } - } else if args.RegistryOptions.KubeConfig != "" { - hasK8SConfigStore = true - } - } - - if hasK8SConfigStore || hasKubeRegistry(args.RegistryOptions.Registries) { - // Used by validation - kubeRestConfig, err := kubelib.DefaultRestConfig(args.RegistryOptions.KubeConfig, "", func(config *rest.Config) { - config.QPS = args.RegistryOptions.KubeOptions.KubernetesAPIQPS - config.Burst = args.RegistryOptions.KubeOptions.KubernetesAPIBurst - }) - if err != nil { - return fmt.Errorf("failed creating kube config: %v", err) - } - - s.kubeClient, err = kubelib.NewClient(kubelib.NewClientConfigForRestConfig(kubeRestConfig)) - if err != nil { - return fmt.Errorf("failed creating kube client: %v", err) - } - } - - return nil -} - -// A single container can't have two readiness probes. Make this readiness probe a generic one -// that can handle all istiod related readiness checks including webhook, gRPC etc. -// The "http" portion of the readiness check is satisfied by the fact we've started listening on -// this handler and everything has already initialized. -func (s *Server) istiodReadyHandler(w http.ResponseWriter, _ *http.Request) { - for name, fn := range s.readinessProbes { - if ready, err := fn(); !ready { - log.Warnf("%s is not ready: %v", name, err) - w.WriteHeader(http.StatusServiceUnavailable) - return - } - } - w.WriteHeader(http.StatusOK) -} - -// initIstiodAdminServer initializes monitoring, debug and readiness end points. -func (s *Server) initIstiodAdminServer(args *PilotArgs, whc func() map[string]string) error { - s.httpServer = &http.Server{ - Addr: args.ServerOptions.HTTPAddr, - Handler: s.httpMux, - IdleTimeout: 90 * time.Second, // matches http.DefaultTransport keep-alive timeout - ReadTimeout: 30 * time.Second, - } - - shouldMultiplex := args.ServerOptions.MonitoringAddr == "" - - if shouldMultiplex { - s.monitoringMux = s.httpMux - log.Info("initializing Istiod admin server multiplexed on httpAddr ", s.httpServer.Addr) - } else { - log.Info("initializing Istiod admin server") - } - - // Debug Server. - s.XDSServer.InitDebug(s.monitoringMux, s.ServiceController(), args.ServerOptions.EnableProfiling, whc) - - // Debug handlers are currently added on monitoring mux and readiness mux. - // If monitoring addr is empty, the mux is shared and we only add it once on the shared mux . - if !shouldMultiplex { - s.XDSServer.AddDebugHandlers(s.httpMux, nil, args.ServerOptions.EnableProfiling, whc) - } - - // Monitoring Server. - if err := s.initMonitor(args.ServerOptions.MonitoringAddr); err != nil { - return fmt.Errorf("error initializing monitor: %v", err) - } - - // Readiness Handler. - s.httpMux.HandleFunc("/ready", s.istiodReadyHandler) - - return nil -} - -// initDiscoveryService initializes discovery server on plain text port. -func (s *Server) initDiscoveryService(args *PilotArgs) { - log.Infof("starting discovery service") - // Implement EnvoyXdsServer grace shutdown - s.addStartFunc(func(stop <-chan struct{}) error { - log.Infof("Starting ADS server") - s.XDSServer.Start(stop) - return nil - }) - - // Implement ServiceNameMapping grace shutdown - s.addStartFunc(func(stop <-chan struct{}) error { - log.Infof("Starting SNP server") - s.snpServer.Start(stop) - return nil - }) - - // Implement ServiceNameMapping grace shutdown - s.addStartFunc(func(stop <-chan struct{}) error { - log.Infof("Starting MetaData server") - s.metadataServer.Start(stop) - return nil - }) - - s.initGrpcServer(args.KeepaliveOptions) - - if args.ServerOptions.GRPCAddr != "" { - s.grpcAddress = args.ServerOptions.GRPCAddr - } else { - // This happens only if the GRPC port (15010) is disabled. We will multiplex - // it on the HTTP port. Does not impact the HTTPS gRPC or HTTPS. - log.Info("multiplexing gRPC on http addr ", args.ServerOptions.HTTPAddr) - s.MultiplexGRPC = true - } -} - -// Wait for the stop, and do cleanups -func (s *Server) waitForShutdown(stop <-chan struct{}) { - go func() { - <-stop - close(s.internalStop) - _ = s.fileWatcher.Close() - - if s.cacertsWatcher != nil { - _ = s.cacertsWatcher.Close() - } - // Stop gRPC services. If gRPC services fail to stop in the shutdown duration, - // force stop them. This does not happen normally. - stopped := make(chan struct{}) - go func() { - // Some grpcServer implementations do not support GracefulStop. Unfortunately, this is not - // exposed; they just panic. To avoid this, we will recover and do a standard Stop when its not - // support. - defer func() { - if r := recover(); r != nil { - s.grpcServer.Stop() - if s.secureGrpcServer != nil { - s.secureGrpcServer.Stop() - } - close(stopped) - } - }() - s.grpcServer.GracefulStop() - if s.secureGrpcServer != nil { - s.secureGrpcServer.GracefulStop() - } - close(stopped) - }() - - t := time.NewTimer(s.shutdownDuration) - select { - case <-t.C: - s.grpcServer.Stop() - if s.secureGrpcServer != nil { - s.secureGrpcServer.Stop() - } - case <-stopped: - t.Stop() - } - - // Stop HTTP services. - ctx, cancel := context.WithTimeout(context.Background(), s.shutdownDuration) - defer cancel() - if err := s.httpServer.Shutdown(ctx); err != nil { - log.Warn(err) - } - if s.httpsServer != nil { - if err := s.httpsServer.Shutdown(ctx); err != nil { - log.Warn(err) - } - } - - // Shutdown the DiscoveryServer. - s.XDSServer.Shutdown() - }() -} - -func (s *Server) initGrpcServer(options *istiokeepalive.Options) { - interceptors := []grpc.UnaryServerInterceptor{ - // setup server prometheus monitoring (as final interceptor in chain) - prometheus.UnaryServerInterceptor, - } - grpcOptions := istiogrpc.ServerOptions(options, interceptors...) - s.grpcServer = grpc.NewServer(grpcOptions...) - s.XDSServer.Register(s.grpcServer) - reflection.Register(s.grpcServer) - s.metadataServer.Register(s.grpcServer) - s.snpServer.Register(s.grpcServer) -} - -// initialize secureGRPCServer. -func (s *Server) initSecureDiscoveryService(args *PilotArgs) error { - if args.ServerOptions.SecureGRPCAddr == "" { - log.Info("The secure discovery port is disabled, multiplexing on httpAddr ") - return nil - } - - peerCertVerifier, err := s.createPeerCertVerifier(args.ServerOptions.TLSOptions) - if err != nil { - return err - } - if peerCertVerifier == nil { - // Running locally without configured certs - no TLS mode - log.Warnf("The secure discovery service is disabled") - return nil - } - log.Info("initializing secure discovery service") - cfg := &tls.Config{ - GetCertificate: s.getIstiodCertificate, - ClientAuth: tls.VerifyClientCertIfGiven, - ClientCAs: peerCertVerifier.GetGeneralCertPool(), - VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { - err := peerCertVerifier.VerifyPeerCert(rawCerts, verifiedChains) - if err != nil { - log.Infof("Could not verify certificate: %v", err) - } - return err - }, - MinVersion: tls.VersionTLS12, - CipherSuites: args.ServerOptions.TLSOptions.CipherSuits, - } - - tlsCreds := credentials.NewTLS(cfg) - - s.secureGrpcAddress = args.ServerOptions.SecureGRPCAddr - - interceptors := []grpc.UnaryServerInterceptor{ - // setup server prometheus monitoring (as final interceptor in chain) - prometheus.UnaryServerInterceptor, - } - opts := istiogrpc.ServerOptions(args.KeepaliveOptions, interceptors...) - opts = append(opts, grpc.Creds(tlsCreds)) - - s.secureGrpcServer = grpc.NewServer(opts...) - s.XDSServer.Register(s.secureGrpcServer) - reflection.Register(s.secureGrpcServer) - s.metadataServer.Register(s.secureGrpcServer) - s.snpServer.Register(s.secureGrpcServer) - - s.addStartFunc(func(stop <-chan struct{}) error { - go func() { - <-stop - s.secureGrpcServer.Stop() - }() - return nil - }) - - return nil -} - -// addStartFunc appends a function to be run. These are run synchronously in order, -// so the function should start a go routine if it needs to do anything blocking -func (s *Server) addStartFunc(fn server.Component) { - s.server.RunComponent(fn) -} - -// adds a readiness probe for Istiod Server. -func (s *Server) addReadinessProbe(name string, fn readinessProbe) { - s.readinessProbes[name] = fn -} - -// addTerminatingStartFunc adds a function that should terminate before the serve shuts down -// This is useful to do cleanup activities -// This is does not guarantee they will terminate gracefully - best effort only -// Function should be synchronous; once it returns it is considered "done" -func (s *Server) addTerminatingStartFunc(fn server.Component) { - s.server.RunComponentAsyncAndWait(fn) -} - -func (s *Server) waitForCacheSync(stop <-chan struct{}) bool { - start := time.Now() - log.Info("Waiting for caches to be synced") - if !cache.WaitForCacheSync(stop, s.cachesSynced) { - log.Errorf("Failed waiting for cache sync") - return false - } - log.Infof("All controller caches have been synced up in %v", time.Since(start)) - - // At this point, we know that all update events of the initial state-of-the-world have been - // received. We wait to ensure we have committed at least this many updates. This avoids a race - // condition where we are marked ready prior to updating the push context, leading to incomplete - // pushes. - expected := s.XDSServer.InboundUpdates.Load() - if !cache.WaitForCacheSync(stop, func() bool { return s.pushContextReady(expected) }) { - log.Errorf("Failed waiting for push context initialization") - return false - } - - return true -} - -// pushContextReady indicates whether pushcontext has processed all inbound config updates. -func (s *Server) pushContextReady(expected int64) bool { - committed := s.XDSServer.CommittedUpdates.Load() - if committed < expected { - log.Debugf("Waiting for pushcontext to process inbound updates, inbound: %v, committed : %v", expected, committed) - return false - } - return true -} - -// cachesSynced checks whether caches have been synced. -func (s *Server) cachesSynced() bool { - if s.multiclusterController != nil && !s.multiclusterController.HasSynced() { - return false - } - if !s.ServiceController().HasSynced() { - return false - } - if !s.configController.HasSynced() { - return false - } - return true -} - -// initRegistryEventHandlers sets up event handlers for config and service updates -func (s *Server) initRegistryEventHandlers() { - log.Info("initializing registry event handlers") - // Flush cached discovery responses whenever services configuration change. - serviceHandler := func(svc *model.Service, _ model.Event) { - pushReq := &model.PushRequest{ - Full: true, - ConfigsUpdated: map[model.ConfigKey]struct{}{{ - Kind: gvk.ServiceEntry, - Name: string(svc.Hostname), - Namespace: svc.Attributes.Namespace, - }: {}}, - Reason: []model.TriggerReason{model.ServiceUpdate}, - } - s.XDSServer.ConfigUpdate(pushReq) - } - s.ServiceController().AppendServiceHandler(serviceHandler) - - if s.configController != nil { - configHandler := func(prev config.Config, curr config.Config, event model.Event) { - defer func() { - if event != model.EventDelete { - s.statusReporter.AddInProgressResource(curr) - } else { - s.statusReporter.DeleteInProgressResource(curr) - } - }() - // For update events, trigger push only if spec has changed. - if event == model.EventUpdate && !needsPush(prev, curr) { - log.Debugf("skipping push for %s as spec has not changed", prev.Key()) - return - } - pushReq := &model.PushRequest{ - Full: true, - ConfigsUpdated: map[model.ConfigKey]struct{}{{ - Kind: curr.GroupVersionKind, - Name: curr.Name, - Namespace: curr.Namespace, - }: {}}, - Reason: []model.TriggerReason{model.ConfigUpdate}, - } - s.XDSServer.ConfigUpdate(pushReq) - } - schemas := collections.Pilot.All() - if features.EnableGatewayAPI { - schemas = collections.PilotGatewayAPI.All() - } - for _, schema := range schemas { - // This resource type was handled in external/servicediscovery.go, no need to rehandle here. - if schema.Resource().GroupVersionKind() == collections.IstioNetworkingV1Alpha3Serviceentries. - Resource().GroupVersionKind() { - continue - } - if schema.Resource().GroupVersionKind() == collections.IstioNetworkingV1Alpha3Workloadentries. - Resource().GroupVersionKind() { - continue - } - if schema.Resource().GroupVersionKind() == collections.IstioNetworkingV1Alpha3Workloadgroups. - Resource().GroupVersionKind() { - continue - } - - s.configController.RegisterEventHandler(schema.Resource().GroupVersionKind(), configHandler) - } - if s.environment.GatewayAPIController != nil { - s.environment.GatewayAPIController.RegisterEventHandler(gvk.Namespace, func(config.Config, config.Config, model.Event) { - s.XDSServer.ConfigUpdate(&model.PushRequest{ - Full: true, - Reason: []model.TriggerReason{model.NamespaceUpdate}, - }) - }) - } - } -} - -func (s *Server) initIstiodCertLoader() error { - if err := s.loadIstiodCert(); err != nil { - return fmt.Errorf("first time load IstiodCert failed: %v", err) - } - _, watchCh := s.istiodCertBundleWatcher.AddWatcher() - s.addStartFunc(func(stop <-chan struct{}) error { - go s.reloadIstiodCert(watchCh, stop) - return nil - }) - return nil -} - -// initIstiodCerts creates Istiod certificates and also sets up watches to them. -func (s *Server) initIstiodCerts(args *PilotArgs, host string) error { - // Skip all certificates - var err error - - // Append custom hostname if there is any - customHost := features.IstiodServiceCustomHost - s.dnsNames = []string{host} - cHosts := strings.Split(customHost, ",") - for _, cHost := range cHosts { - if cHost != "" && cHost != host { - log.Infof("Adding custom hostname %s", cHost) - s.dnsNames = append(s.dnsNames, cHost) - } - } - - // The first is the recommended one, also used by Apiserver for webhooks. - // add a few known hostnames - knownHosts := []string{"istiod", "istiod-remote", "istio-pilot"} - // In some conditions, pilot address for sds is different from other xds, - // like multi-cluster primary-remote mode with revision. - if args.Revision != "" && args.Revision != "dubbo" { - knownHosts = append(knownHosts, "istiod"+"-"+args.Revision) - } - - for _, altName := range knownHosts { - name := fmt.Sprintf("%v.%v.svc", altName, args.Namespace) - exist := false - for _, cHost := range cHosts { - if name == host || name == cHost { - exist = true - } - } - if !exist { - s.dnsNames = append(s.dnsNames, name) - } - } - - if hasCustomTLSCerts(args.ServerOptions.TLSOptions) { - // Use the DNS certificate provided via args. - err = s.initCertificateWatches(args.ServerOptions.TLSOptions) - if err != nil { - // Not crashing istiod - This typically happens if certs are missing and in tests. - log.Errorf("error initializing certificate watches: %v", err) - return nil - } - err = s.initIstiodCertLoader() - } else if features.PilotCertProvider == constants.CertProviderNone { - return nil - } else if s.EnableCA() && features.PilotCertProvider == constants.CertProviderIstiod { - log.Infof("initializing Istiod DNS certificates host: %s, custom host: %s", host, features.IstiodServiceCustomHost) - err = s.initDNSCerts(host, args.Namespace) - if err == nil { - err = s.initIstiodCertLoader() - } - } else if features.PilotCertProvider == constants.CertProviderKubernetes { - log.Infof("initializing Istiod DNS certificates host: %s, custom host: %s", host, features.IstiodServiceCustomHost) - err = s.initDNSCerts(host, args.Namespace) - if err == nil { - err = s.initIstiodCertLoader() - } - } else if strings.HasPrefix(features.PilotCertProvider, constants.CertProviderKubernetesSignerPrefix) { - log.Infof("initializing Istiod DNS certificates host: %s, custom host: %s", host, features.IstiodServiceCustomHost) - err = s.initDNSCerts(host, args.Namespace) - if err == nil { - err = s.initIstiodCertLoader() - } - } - - return err -} - -// createPeerCertVerifier creates a SPIFFE certificate verifier with the current istiod configuration. -func (s *Server) createPeerCertVerifier(tlsOptions TLSOptions) (*spiffe.PeerCertVerifier, error) { - if tlsOptions.CaCertFile == "" && s.CA == nil && features.SpiffeBundleEndpoints == "" && !s.isDisableCa() { - // Running locally without configured certs - no TLS mode - return nil, nil - } - peerCertVerifier := spiffe.NewPeerCertVerifier() - var rootCertBytes []byte - var err error - if tlsOptions.CaCertFile != "" { - if rootCertBytes, err = os.ReadFile(tlsOptions.CaCertFile); err != nil { - return nil, err - } - } else { - if s.RA != nil { - if strings.HasPrefix(features.PilotCertProvider, constants.CertProviderKubernetesSignerPrefix) { - signerName := strings.TrimPrefix(features.PilotCertProvider, constants.CertProviderKubernetesSignerPrefix) - caBundle, _ := s.RA.GetRootCertFromMeshConfig(signerName) - rootCertBytes = append(rootCertBytes, caBundle...) - } else { - rootCertBytes = append(rootCertBytes, s.RA.GetCAKeyCertBundle().GetRootCertPem()...) - } - } - if s.CA != nil { - rootCertBytes = append(rootCertBytes, s.CA.GetCAKeyCertBundle().GetRootCertPem()...) - } - } - - if len(rootCertBytes) != 0 { - err := peerCertVerifier.AddMappingFromPEM(spiffe.GetTrustDomain(), rootCertBytes) - if err != nil { - return nil, fmt.Errorf("add root CAs into peerCertVerifier failed: %v", err) - } - } - - if features.SpiffeBundleEndpoints != "" { - certMap, err := spiffe.RetrieveSpiffeBundleRootCertsFromStringInput( - features.SpiffeBundleEndpoints, []*x509.Certificate{}) - if err != nil { - return nil, err - } - peerCertVerifier.AddMappings(certMap) - } - - return peerCertVerifier, nil -} - -// hasCustomTLSCerts returns true if custom TLS certificates are configured via args. -func hasCustomTLSCerts(tlsOptions TLSOptions) bool { - return tlsOptions.CaCertFile != "" && tlsOptions.CertFile != "" && tlsOptions.KeyFile != "" -} - -// getIstiodCertificate returns the istiod certificate. -func (s *Server) getIstiodCertificate(*tls.ClientHelloInfo) (*tls.Certificate, error) { - s.certMu.RLock() - defer s.certMu.RUnlock() - if s.istiodCert != nil { - return s.istiodCert, nil - } - return nil, fmt.Errorf("cert not initialized") -} - -// initControllers initializes the controllers. -func (s *Server) initControllers(args *PilotArgs) error { - log.Info("initializing controllers") - s.initMulticluster(args) - // Certificate controller is created before MCP controller in case MCP server pod - // waits to mount a certificate to be provisioned by the certificate controller. - if err := s.initCertController(args); err != nil { - return fmt.Errorf("error initializing certificate controller: %v", err) - } - if err := s.initConfigController(args); err != nil { - return fmt.Errorf("error initializing config controller: %v", err) - } - if err := s.initServiceControllers(args); err != nil { - return fmt.Errorf("error initializing service controllers: %v", err) - } - return nil -} - -func (s *Server) initMulticluster(args *PilotArgs) { - if s.kubeClient == nil { - return - } - s.multiclusterController = multicluster.NewController(s.kubeClient, args.Namespace, s.clusterID) - s.XDSServer.ListRemoteClusters = s.multiclusterController.ListRemoteClusters - s.addStartFunc(func(stop <-chan struct{}) error { - return s.multiclusterController.Run(stop) - }) -} - -// maybeCreateCA creates and initializes CA Key if needed. -func (s *Server) maybeCreateCA(caOpts *caOptions) error { - // CA signing certificate must be created only if CA is enabled. - if s.EnableCA() { - log.Info("creating CA and initializing public key") - var err error - var corev1 v1.CoreV1Interface - if s.kubeClient != nil { - corev1 = s.kubeClient.CoreV1() - } - if useRemoteCerts.Get() { - if err = s.loadRemoteCACerts(caOpts, LocalCertDir.Get()); err != nil { - return fmt.Errorf("failed to load remote CA certs: %v", err) - } - } - // May return nil, if the CA is missing required configs - This is not an error. - if caOpts.ExternalCAType != "" { - if s.RA, err = s.createIstioRA(s.kubeClient, caOpts); err != nil { - return fmt.Errorf("failed to create RA: %v", err) - } - } - if !s.isDisableCa() { - if s.CA, err = s.createIstioCA(corev1, caOpts); err != nil { - return fmt.Errorf("failed to create CA: %v", err) - } - } - - } - return nil -} - -func (s *Server) shouldStartNsController() bool { - if s.isDisableCa() { - return true - } - if s.CA == nil { - return false - } - - // For Kubernetes CA, we don't distribute it; it is mounted in all pods by Kubernetes. - if features.PilotCertProvider == constants.CertProviderKubernetes { - return false - } - // For no CA we don't distribute it either, as there is no cert - if features.PilotCertProvider == constants.CertProviderNone { - return false - } - - return true -} - -// StartCA starts the CA or RA server if configured. -func (s *Server) startCA(caOpts *caOptions) { - if s.CA == nil && s.RA == nil { - return - } - s.addStartFunc(func(stop <-chan struct{}) error { - grpcServer := s.secureGrpcServer - if s.secureGrpcServer == nil { - grpcServer = s.grpcServer - } - // Start the RA server if configured, else start the CA server - if s.RA != nil { - log.Infof("Starting RA") - s.RunCA(grpcServer, s.RA, caOpts) - } else if s.CA != nil { - log.Infof("Starting IstioD CA") - s.RunCA(grpcServer, s.CA, caOpts) - } - return nil - }) -} - -// initMeshHandlers initializes mesh and network handlers. -func (s *Server) initMeshHandlers() { - log.Info("initializing mesh handlers") - // When the mesh config or networks change, do a full push. - s.environment.AddMeshHandler(func() { - spiffe.SetTrustDomain(s.environment.Mesh().GetTrustDomain()) - s.XDSServer.ConfigGenerator.MeshConfigChanged(s.environment.Mesh()) - s.XDSServer.ConfigUpdate(&model.PushRequest{ - Full: true, - Reason: []model.TriggerReason{model.GlobalUpdate}, - }) - }) -} - -func (s *Server) addIstioCAToTrustBundle(args *PilotArgs) error { - var err error - if s.CA != nil { - // If IstioCA is setup, derive trustAnchor directly from CA - rootCerts := []string{string(s.CA.GetCAKeyCertBundle().GetRootCertPem())} - err = s.workloadTrustBundle.UpdateTrustAnchor(&tb.TrustAnchorUpdate{ - TrustAnchorConfig: tb.TrustAnchorConfig{Certs: rootCerts}, - Source: tb.SourceIstioCA, - }) - if err != nil { - log.Errorf("unable to add CA root from namespace %s as trustAnchor", args.Namespace) - return err - } - return nil - } - return nil -} - -func (s *Server) initWorkloadTrustBundle(args *PilotArgs) error { - var err error - - if !features.MultiRootMesh { - return nil - } - - s.workloadTrustBundle.UpdateCb(func() { - pushReq := &model.PushRequest{ - Full: true, - Reason: []model.TriggerReason{model.GlobalUpdate}, - } - s.XDSServer.ConfigUpdate(pushReq) - }) - - s.addStartFunc(func(stop <-chan struct{}) error { - go s.workloadTrustBundle.ProcessRemoteTrustAnchors(stop, tb.RemoteDefaultPollPeriod) - return nil - }) - - // MeshConfig: Add initial roots - err = s.workloadTrustBundle.AddMeshConfigUpdate(s.environment.Mesh()) - if err != nil { - return err - } - - // MeshConfig:Add callback for mesh config update - s.environment.AddMeshHandler(func() { - _ = s.workloadTrustBundle.AddMeshConfigUpdate(s.environment.Mesh()) - }) - - err = s.addIstioCAToTrustBundle(args) - if err != nil { - return err - } - - // IstioRA: Explicitly add roots corresponding to RA - if s.RA != nil { - // Implicitly add the Istio RA certificates to the Workload Trust Bundle - rootCerts := []string{string(s.RA.GetCAKeyCertBundle().GetRootCertPem())} - err = s.workloadTrustBundle.UpdateTrustAnchor(&tb.TrustAnchorUpdate{ - TrustAnchorConfig: tb.TrustAnchorConfig{Certs: rootCerts}, - Source: tb.SourceIstioRA, - }) - if err != nil { - log.Errorf("fatal: unable to add RA root as trustAnchor") - return err - } - } - log.Infof("done initializing workload trustBundle") - return nil -} - -// isDisableCa returns whether CA functionality is disabled in istiod. -// It return true only if istiod certs is signed by Kubernetes and -// workload certs are signed by external CA -func (s *Server) isDisableCa() bool { - if s.RA != nil { - // do not create CA server if PilotCertProvider is `kubernetes` and RA server exists - if features.PilotCertProvider == constants.CertProviderKubernetes { - return true - } - // do not create CA server if PilotCertProvider is `k8s.io/*` and RA server exists - if strings.HasPrefix(features.PilotCertProvider, constants.CertProviderKubernetesSignerPrefix) { - return true - } - } - return false -} - -func (s *Server) initStatusManager(_ *PilotArgs) { - s.addStartFunc(func(stop <-chan struct{}) error { - s.statusManager = status.NewManager(s.RWConfigStore) - s.statusManager.Start(stop) - return nil - }) -} diff --git a/pilot/pkg/bootstrap/server_test.go b/pilot/pkg/bootstrap/server_test.go deleted file mode 100644 index e05d1fdb4..000000000 --- a/pilot/pkg/bootstrap/server_test.go +++ /dev/null @@ -1,501 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package bootstrap - -import ( - "bytes" - "crypto/tls" - "fmt" - "net" - "net/http" - "net/url" - "os" - "path/filepath" - "strconv" - "testing" - "time" -) - -import ( - . "github.com/onsi/gomega" - "istio.io/pkg/filewatcher" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/keycertbundle" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/server" - kubecontroller "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube/controller" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" - "github.com/apache/dubbo-go-pixiu/pkg/testcerts" -) - -func TestNewServerCertInit(t *testing.T) { - configDir := t.TempDir() - - certsDir := t.TempDir() - - certFile := filepath.Join(certsDir, "cert-file.pem") - keyFile := filepath.Join(certsDir, "key-file.pem") - caCertFile := filepath.Join(certsDir, "ca-cert.pem") - - // load key and cert files. - if err := os.WriteFile(certFile, testcerts.ServerCert, 0o644); err != nil { // nolint: vetshadow - t.Fatalf("WriteFile(%v) failed: %v", certFile, err) - } - if err := os.WriteFile(keyFile, testcerts.ServerKey, 0o644); err != nil { // nolint: vetshadow - t.Fatalf("WriteFile(%v) failed: %v", keyFile, err) - } - if err := os.WriteFile(caCertFile, testcerts.CACert, 0o644); err != nil { // nolint: vetshadow - t.Fatalf("WriteFile(%v) failed: %v", caCertFile, err) - } - - cases := []struct { - name string - tlsOptions *TLSOptions - enableCA bool - certProvider string - expNewCert bool - expCert []byte - expKey []byte - }{ - { - name: "Load from existing DNS cert", - tlsOptions: &TLSOptions{ - CertFile: certFile, - KeyFile: keyFile, - CaCertFile: caCertFile, - }, - enableCA: false, - certProvider: constants.CertProviderKubernetes, - expNewCert: false, - expCert: testcerts.ServerCert, - expKey: testcerts.ServerKey, - }, - { - name: "Create new DNS cert using Istiod", - tlsOptions: &TLSOptions{ - CertFile: "", - KeyFile: "", - CaCertFile: "", - }, - enableCA: true, - certProvider: constants.CertProviderIstiod, - expNewCert: true, - expCert: []byte{}, - expKey: []byte{}, - }, - { - name: "No DNS cert created because CA is disabled", - tlsOptions: &TLSOptions{}, - enableCA: false, - certProvider: constants.CertProviderIstiod, - expNewCert: false, - expCert: []byte{}, - expKey: []byte{}, - }, - { - name: "No cert provider", - tlsOptions: &TLSOptions{}, - enableCA: true, - certProvider: constants.CertProviderNone, - expNewCert: false, - expCert: []byte{}, - expKey: []byte{}, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - test.SetStringForTest(t, &features.PilotCertProvider, c.certProvider) - test.SetBoolForTest(t, &features.EnableCAServer, c.enableCA) - args := NewPilotArgs(func(p *PilotArgs) { - p.Namespace = "dubbo-system" - p.ServerOptions = DiscoveryServerOptions{ - // Dynamically assign all ports. - HTTPAddr: ":0", - MonitoringAddr: ":0", - GRPCAddr: ":0", - SecureGRPCAddr: ":0", - TLSOptions: *c.tlsOptions, - } - p.RegistryOptions = RegistryOptions{ - FileDir: configDir, - } - - p.ShutdownDuration = 1 * time.Millisecond - }) - g := NewWithT(t) - s, err := NewServer(args) - g.Expect(err).To(Succeed()) - stop := make(chan struct{}) - g.Expect(s.Start(stop)).To(Succeed()) - defer func() { - close(stop) - s.WaitUntilCompletion() - }() - - if c.expNewCert { - if istiodCert, err := s.getIstiodCertificate(nil); istiodCert == nil || err != nil { - t.Errorf("Istiod failed to generate new DNS cert") - } - } else { - if len(c.expCert) != 0 { - if !checkCert(t, s, c.expCert, c.expKey) { - t.Errorf("Istiod certifiate does not match the expectation") - } - } else { - if _, err := s.getIstiodCertificate(nil); err == nil { - t.Errorf("Istiod should not generate new DNS cert") - } - } - } - }) - } -} - -func TestReloadIstiodCert(t *testing.T) { - dir := t.TempDir() - stop := make(chan struct{}) - s := &Server{ - fileWatcher: filewatcher.NewWatcher(), - server: server.New(), - istiodCertBundleWatcher: keycertbundle.NewWatcher(), - } - - defer func() { - close(stop) - _ = s.fileWatcher.Close() - }() - - certFile := filepath.Join(dir, "cert-file.yaml") - keyFile := filepath.Join(dir, "key-file.yaml") - caFile := filepath.Join(dir, "ca-file.yaml") - - // load key and cert files. - if err := os.WriteFile(certFile, testcerts.ServerCert, 0o644); err != nil { // nolint: vetshadow - t.Fatalf("WriteFile(%v) failed: %v", certFile, err) - } - if err := os.WriteFile(keyFile, testcerts.ServerKey, 0o644); err != nil { // nolint: vetshadow - t.Fatalf("WriteFile(%v) failed: %v", keyFile, err) - } - - if err := os.WriteFile(caFile, testcerts.CACert, 0o644); err != nil { // nolint: vetshadow - t.Fatalf("WriteFile(%v) failed: %v", caFile, err) - } - - tlsOptions := TLSOptions{ - CertFile: certFile, - KeyFile: keyFile, - CaCertFile: caFile, - } - - // setup cert watches. - if err := s.initCertificateWatches(tlsOptions); err != nil { - t.Fatalf("initCertificateWatches failed: %v", err) - } - - if err := s.initIstiodCertLoader(); err != nil { - t.Fatalf("istiod unable to load its cert") - } - - if err := s.server.Start(stop); err != nil { - t.Fatalf("Could not invoke startFuncs: %v", err) - } - - // Validate that the certs are loaded. - if !checkCert(t, s, testcerts.ServerCert, testcerts.ServerKey) { - t.Errorf("Istiod certifiate does not match the expectation") - } - - // Update cert/key files. - if err := os.WriteFile(tlsOptions.CertFile, testcerts.RotatedCert, 0o644); err != nil { // nolint: vetshadow - t.Fatalf("WriteFile(%v) failed: %v", tlsOptions.CertFile, err) - } - if err := os.WriteFile(tlsOptions.KeyFile, testcerts.RotatedKey, 0o644); err != nil { // nolint: vetshadow - t.Fatalf("WriteFile(%v) failed: %v", tlsOptions.KeyFile, err) - } - - g := NewWithT(t) - - // Validate that istiod cert is updated. - g.Eventually(func() bool { - return checkCert(t, s, testcerts.RotatedCert, testcerts.RotatedKey) - }, "10s", "100ms").Should(BeTrue()) -} - -func TestNewServer(t *testing.T) { - // All of the settings to apply and verify. Currently just testing domain suffix, - // but we should expand this list. - cases := []struct { - name string - domain string - expectedDomain string - enableSecureGRPC bool - jwtRule string - }{ - { - name: "default domain", - domain: "", - expectedDomain: constants.DefaultKubernetesDomain, - }, - { - name: "default domain with JwtRule", - domain: "", - expectedDomain: constants.DefaultKubernetesDomain, - jwtRule: `{"issuer": "foo", "jwks_uri": "baz", "audiences": ["aud1", "aud2"]}`, - }, - { - name: "override domain", - domain: "mydomain.com", - expectedDomain: "mydomain.com", - }, - { - name: "override default secured grpc port", - domain: "", - expectedDomain: constants.DefaultKubernetesDomain, - enableSecureGRPC: true, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - configDir := t.TempDir() - - var secureGRPCPort int - var err error - if c.enableSecureGRPC { - secureGRPCPort, err = findFreePort() - if err != nil { - t.Errorf("unable to find a free port: %v", err) - return - } - } - - args := NewPilotArgs(func(p *PilotArgs) { - p.Namespace = "dubbo-system" - p.ServerOptions = DiscoveryServerOptions{ - // Dynamically assign all ports. - HTTPAddr: ":0", - MonitoringAddr: ":0", - GRPCAddr: ":0", - SecureGRPCAddr: fmt.Sprintf(":%d", secureGRPCPort), - } - p.RegistryOptions = RegistryOptions{ - KubeOptions: kubecontroller.Options{ - DomainSuffix: c.domain, - }, - FileDir: configDir, - } - - p.ShutdownDuration = 1 * time.Millisecond - - p.JwtRule = c.jwtRule - }) - - g := NewWithT(t) - s, err := NewServer(args) - g.Expect(err).To(Succeed()) - stop := make(chan struct{}) - g.Expect(s.Start(stop)).To(Succeed()) - defer func() { - close(stop) - s.WaitUntilCompletion() - }() - - g.Expect(s.environment.DomainSuffix).To(Equal(c.expectedDomain)) - - if c.enableSecureGRPC { - tcpAddr := s.secureGrpcAddress - _, port, err := net.SplitHostPort(tcpAddr) - if err != nil { - t.Errorf("invalid SecureGrpcListener addr %v", err) - } - g.Expect(port).To(Equal(strconv.Itoa(secureGRPCPort))) - } - }) - } -} - -func TestIstiodCipherSuites(t *testing.T) { - cases := []struct { - name string - serverCipherSuites []uint16 - clientCipherSuites []uint16 - expectSuccess bool - }{ - { - name: "default cipher suites", - expectSuccess: true, - }, - { - name: "client and istiod cipher suites match", - serverCipherSuites: []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - clientCipherSuites: []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - expectSuccess: true, - }, - { - name: "client and istiod cipher suites mismatch", - serverCipherSuites: []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - clientCipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384}, - expectSuccess: false, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - configDir := t.TempDir() - - port, err := findFreePort() - if err != nil { - t.Errorf("unable to find a free port: %v", err) - return - } - - args := NewPilotArgs(func(p *PilotArgs) { - p.Namespace = "dubbo-system" - p.ServerOptions = DiscoveryServerOptions{ - // Dynamically assign all ports. - HTTPAddr: ":0", - MonitoringAddr: ":0", - GRPCAddr: ":0", - HTTPSAddr: fmt.Sprintf(":%d", port), - TLSOptions: TLSOptions{ - CipherSuits: c.serverCipherSuites, - }, - } - p.RegistryOptions = RegistryOptions{ - KubeConfig: "config", - FileDir: configDir, - } - - // Include all of the default plugins - p.ShutdownDuration = 1 * time.Millisecond - }) - - g := NewWithT(t) - s, err := NewServer(args, func(s *Server) { - s.kubeClient = kube.NewFakeClient() - }) - g.Expect(err).To(Succeed()) - - stop := make(chan struct{}) - g.Expect(s.Start(stop)).To(Succeed()) - defer func() { - close(stop) - s.WaitUntilCompletion() - }() - - httpsReadyClient := &http.Client{ - Timeout: time.Second, - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - CipherSuites: c.clientCipherSuites, - MinVersion: tls.VersionTLS12, - MaxVersion: tls.VersionTLS12, - }, - }, - } - - retry.UntilSuccessOrFail(t, func() error { - req := &http.Request{ - Method: http.MethodGet, - URL: &url.URL{ - Scheme: "https", - Host: s.httpsServer.Addr, - Path: HTTPSHandlerReadyPath, - }, - } - response, err := httpsReadyClient.Do(req) - if c.expectSuccess && err != nil { - return fmt.Errorf("expect success but got err %v", err) - } - if !c.expectSuccess && err == nil { - return fmt.Errorf("expect failure but succeeded") - } - if response != nil { - response.Body.Close() - } - return nil - }) - }) - } -} - -func TestInitOIDC(t *testing.T) { - tests := []struct { - name string - expectErr bool - jwtRule string - }{ - { - name: "valid jwt rule", - expectErr: false, - jwtRule: `{"issuer": "foo", "jwks_uri": "baz", "audiences": ["aud1", "aud2"]}`, - }, - { - name: "invalid jwt rule", - expectErr: true, - jwtRule: "invalid", - }, - { - name: "jwt rule with invalid audiences", - expectErr: true, - // audiences must be a string array - jwtRule: `{"issuer": "foo", "jwks_uri": "baz", "audiences": "aud1"}`, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - args := &PilotArgs{JwtRule: tt.jwtRule} - - _, err := initOIDC(args, "domain-foo") - gotErr := err != nil - if gotErr != tt.expectErr { - t.Errorf("expect error is %v while actual error is %v", tt.expectErr, gotErr) - } - }) - } -} - -func checkCert(t *testing.T, s *Server, cert, key []byte) bool { - t.Helper() - actual, err := s.getIstiodCertificate(nil) - if err != nil { - t.Fatalf("fail to load fetch certs.") - } - expected, err := tls.X509KeyPair(cert, key) - if err != nil { - t.Fatalf("fail to load test certs.") - } - return bytes.Equal(actual.Certificate[0], expected.Certificate[0]) -} - -func findFreePort() (int, error) { - ln, err := net.Listen("tcp", ":0") - if err != nil { - return 0, err - } - defer ln.Close() - tcpAddr, ok := ln.Addr().(*net.TCPAddr) - if !ok { - return 0, fmt.Errorf("invalid listen address: %q", ln.Addr().String()) - } - return tcpAddr.Port, nil -} diff --git a/pilot/pkg/bootstrap/servicecontroller.go b/pilot/pkg/bootstrap/servicecontroller.go deleted file mode 100644 index dc72f7003..000000000 --- a/pilot/pkg/bootstrap/servicecontroller.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bootstrap - -import ( - "fmt" -) - -import ( - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/aggregate" - kubecontroller "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube/controller" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/serviceentry" -) - -func (s *Server) ServiceController() *aggregate.Controller { - return s.environment.ServiceDiscovery.(*aggregate.Controller) -} - -// initServiceControllers creates and initializes the service controllers -func (s *Server) initServiceControllers(args *PilotArgs) error { - serviceControllers := s.ServiceController() - - s.serviceEntryController = serviceentry.NewController( - s.configController, s.environment.ConfigStore, s.XDSServer, - serviceentry.WithClusterID(s.clusterID), - ) - serviceControllers.AddRegistry(s.serviceEntryController) - - registered := make(map[provider.ID]bool) - for _, r := range args.RegistryOptions.Registries { - serviceRegistry := provider.ID(r) - if _, exists := registered[serviceRegistry]; exists { - log.Warnf("%s registry specified multiple times.", r) - continue - } - registered[serviceRegistry] = true - log.Infof("Adding %s registry adapter", serviceRegistry) - switch serviceRegistry { - case provider.Kubernetes: - if err := s.initKubeRegistry(args); err != nil { - return err - } - default: - return fmt.Errorf("service registry %s is not supported", r) - } - } - - // Defer running of the service controllers. - s.addStartFunc(func(stop <-chan struct{}) error { - go serviceControllers.Run(stop) - return nil - }) - - return nil -} - -// initKubeRegistry creates all the k8s service controllers under this pilot -func (s *Server) initKubeRegistry(args *PilotArgs) (err error) { - args.RegistryOptions.KubeOptions.ClusterID = s.clusterID - args.RegistryOptions.KubeOptions.Metrics = s.environment - args.RegistryOptions.KubeOptions.XDSUpdater = s.XDSServer - args.RegistryOptions.KubeOptions.NetworksWatcher = s.environment.NetworksWatcher - args.RegistryOptions.KubeOptions.MeshWatcher = s.environment.Watcher - args.RegistryOptions.KubeOptions.SystemNamespace = args.Namespace - args.RegistryOptions.KubeOptions.MeshServiceController = s.ServiceController() - - s.multiclusterController.AddHandler(kubecontroller.NewMulticluster(args.PodName, - s.kubeClient, - args.RegistryOptions.ClusterRegistriesNamespace, - args.RegistryOptions.KubeOptions, - s.serviceEntryController, - s.istiodCertBundleWatcher, - args.Revision, - s.shouldStartNsController(), - s.environment.ClusterLocal(), - s.server)) - - return -} diff --git a/pilot/pkg/bootstrap/sidecarinjector.go b/pilot/pkg/bootstrap/sidecarinjector.go deleted file mode 100644 index bbe855e59..000000000 --- a/pilot/pkg/bootstrap/sidecarinjector.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bootstrap - -import ( - "context" - "fmt" - "os" - "path/filepath" -) - -import ( - "istio.io/pkg/env" - "istio.io/pkg/log" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pkg/kube/inject" - "github.com/apache/dubbo-go-pixiu/pkg/webhooks" -) - -const ( - // Name of the webhook config in the config - no need to change it. - webhookName = "sidecar-injector.istio.io" - // defaultInjectorConfigMapName is the default name of the ConfigMap with the injection config - // The actual name can be different - use getInjectorConfigMapName - defaultInjectorConfigMapName = "istio-sidecar-injector" -) - -var injectionEnabled = env.RegisterBoolVar("INJECT_ENABLED", true, "Enable mutating webhook handler.") - -func (s *Server) initSidecarInjector(args *PilotArgs) (*inject.Webhook, error) { - // currently the constant: "./var/lib/istio/inject" - injectPath := args.InjectionOptions.InjectionDirectory - if injectPath == "" || !injectionEnabled.Get() { - log.Infof("Skipping sidecar injector, injection path is missing or disabled.") - return nil, nil - } - - // If the injection config exists either locally or remotely, we will set up injection. - var watcher inject.Watcher - if _, err := os.Stat(filepath.Join(injectPath, "config")); !os.IsNotExist(err) { - configFile := filepath.Join(injectPath, "config") - valuesFile := filepath.Join(injectPath, "values") - watcher, err = inject.NewFileWatcher(configFile, valuesFile) - if err != nil { - return nil, err - } - } else if s.kubeClient != nil { - configMapName := getInjectorConfigMapName(args.Revision) - cms := s.kubeClient.CoreV1().ConfigMaps(args.Namespace) - if _, err := cms.Get(context.TODO(), configMapName, metav1.GetOptions{}); err != nil { - if errors.IsNotFound(err) { - log.Infof("Skipping sidecar injector, template not found") - return nil, nil - } - return nil, err - } - watcher = inject.NewConfigMapWatcher(s.kubeClient, args.Namespace, configMapName, "config", "values") - } else { - log.Infof("Skipping sidecar injector, template not found") - return nil, nil - } - - log.Info("initializing sidecar injector") - - parameters := inject.WebhookParameters{ - Watcher: watcher, - Env: s.environment, - Mux: s.httpsMux, - Revision: args.Revision, - } - - wh, err := inject.NewWebhook(parameters) - if err != nil { - return nil, fmt.Errorf("failed to create injection webhook: %v", err) - } - // Patch cert if a webhook config name is provided. - // This requires RBAC permissions - a low-priv Istiod should not attempt to patch but rely on - // operator or CI/CD - if features.InjectionWebhookConfigName != "" { - s.addStartFunc(func(stop <-chan struct{}) error { - // No leader election - different istiod revisions will patch their own cert. - // update webhook configuration by watching the cabundle - patcher, err := webhooks.NewWebhookCertPatcher(s.kubeClient, args.Revision, webhookName, s.istiodCertBundleWatcher) - if err != nil { - log.Errorf("failed to create webhook cert patcher: %v", err) - return nil - } - - go patcher.Run(stop) - return nil - }) - } - s.addStartFunc(func(stop <-chan struct{}) error { - wh.Run(stop) - return nil - }) - return wh, nil -} - -func getInjectorConfigMapName(revision string) string { - name := defaultInjectorConfigMapName - if revision == "" || revision == "default" { - return name - } - return name + "-" + revision -} diff --git a/pilot/pkg/bootstrap/util.go b/pilot/pkg/bootstrap/util.go deleted file mode 100644 index 4cab1c174..000000000 --- a/pilot/pkg/bootstrap/util.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bootstrap - -import ( - "istio.io/pkg/ledger" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" -) - -func hasKubeRegistry(registries []string) bool { - for _, r := range registries { - if provider.ID(r) == provider.Kubernetes { - return true - } - } - return false -} - -func buildLedger(ca RegistryOptions) ledger.Ledger { - var result ledger.Ledger - if ca.DistributionTrackingEnabled { - result = ledger.Make(ca.DistributionCacheRetention) - } else { - result = &model.DisabledLedger{} - } - return result -} diff --git a/pilot/pkg/bootstrap/validation.go b/pilot/pkg/bootstrap/validation.go deleted file mode 100644 index 665c63b7c..000000000 --- a/pilot/pkg/bootstrap/validation.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bootstrap - -import ( - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/webhooks/validation/controller" - "github.com/apache/dubbo-go-pixiu/pkg/webhooks/validation/server" -) - -func (s *Server) initConfigValidation(args *PilotArgs) error { - if s.kubeClient == nil { - return nil - } - - log.Info("initializing config validator") - // always start the validation server - params := server.Options{ - Schemas: collections.Istio, - DomainSuffix: args.RegistryOptions.KubeOptions.DomainSuffix, - Mux: s.httpsMux, - } - _, err := server.New(params) - if err != nil { - return err - } - - if features.ValidationWebhookConfigName != "" && s.kubeClient != nil { - s.addStartFunc(func(stop <-chan struct{}) error { - log.Infof("Starting validation controller") - go controller.NewValidatingWebhookController( - s.kubeClient, args.Revision, args.Namespace, s.istiodCertBundleWatcher).Run(stop) - return nil - }) - } - return nil -} diff --git a/pilot/pkg/bootstrap/webhook.go b/pilot/pkg/bootstrap/webhook.go deleted file mode 100644 index 70e21eeb9..000000000 --- a/pilot/pkg/bootstrap/webhook.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bootstrap - -import ( - "crypto/tls" - "net/http" - "net/url" - "time" -) - -import ( - "istio.io/pkg/log" -) - -const ( - HTTPSHandlerReadyPath = "/httpsReady" -) - -// initSSecureWebhookServer handles initialization for the HTTPS webhook server. -// If https address is off the injection handlers will be registered on the main http endpoint, with -// TLS handled by a proxy/gateway in front of Istiod. -func (s *Server) initSecureWebhookServer(args *PilotArgs) { - // create the https server for hosting the k8s injectionWebhook handlers. - if args.ServerOptions.HTTPSAddr == "" { - s.httpsMux = s.httpMux - log.Info("HTTPS port is disabled, multiplexing webhooks on the httpAddr ", args.ServerOptions.HTTPAddr) - return - } - - log.Info("initializing secure webhook server for istiod webhooks") - // create the https server for hosting the k8s injectionWebhook handlers. - s.httpsMux = http.NewServeMux() - s.httpsServer = &http.Server{ - Addr: args.ServerOptions.HTTPSAddr, - Handler: s.httpsMux, - TLSConfig: &tls.Config{ - GetCertificate: s.getIstiodCertificate, - MinVersion: tls.VersionTLS12, - CipherSuites: args.ServerOptions.TLSOptions.CipherSuits, - }, - } - - // setup our readiness handler and the corresponding client we'll use later to check it with. - s.httpsMux.HandleFunc(HTTPSHandlerReadyPath, func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusOK) - }) - s.httpsReadyClient = &http.Client{ - Timeout: time.Second, - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - }, - }, - } - s.addReadinessProbe("Secure Webhook Server", s.webhookReadyHandler) -} - -func (s *Server) webhookReadyHandler() (bool, error) { - req := &http.Request{ - Method: http.MethodGet, - URL: &url.URL{ - Scheme: "https", - Host: s.httpsServer.Addr, - Path: HTTPSHandlerReadyPath, - }, - } - - response, err := s.httpsReadyClient.Do(req) - if err != nil { - return false, err - } - defer response.Body.Close() - - return response.StatusCode == http.StatusOK, nil -} diff --git a/pilot/pkg/config/aggregate/config.go b/pilot/pkg/config/aggregate/config.go deleted file mode 100644 index 3c6a03658..000000000 --- a/pilot/pkg/config/aggregate/config.go +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package aggregate implements a read-only aggregator for config stores. -package aggregate - -import ( - "errors" -) - -import ( - "github.com/hashicorp/go-multierror" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -var errorUnsupported = errors.New("unsupported operation: the config aggregator is read-only") - -// makeStore creates an aggregate config store from several config stores and -// unifies their descriptors -func makeStore(stores []model.ConfigStore, writer model.ConfigStore) (model.ConfigStore, error) { - union := collection.NewSchemasBuilder() - storeTypes := make(map[config.GroupVersionKind][]model.ConfigStore) - for _, store := range stores { - for _, s := range store.Schemas().All() { - if len(storeTypes[s.Resource().GroupVersionKind()]) == 0 { - if err := union.Add(s); err != nil { - return nil, err - } - } - storeTypes[s.Resource().GroupVersionKind()] = append(storeTypes[s.Resource().GroupVersionKind()], store) - } - } - - schemas := union.Build() - if err := schemas.Validate(); err != nil { - return nil, err - } - result := &store{ - schemas: schemas, - stores: storeTypes, - writer: writer, - } - - return result, nil -} - -// MakeWriteableCache creates an aggregate config store cache from several config store caches. An additional -// `writer` config store is passed, which may or may not be part of `caches`. -func MakeWriteableCache(caches []model.ConfigStoreController, writer model.ConfigStore) (model.ConfigStoreController, error) { - stores := make([]model.ConfigStore, 0, len(caches)) - for _, cache := range caches { - stores = append(stores, cache) - } - store, err := makeStore(stores, writer) - if err != nil { - return nil, err - } - return &storeCache{ - ConfigStore: store, - caches: caches, - }, nil -} - -// MakeCache creates an aggregate config store cache from several config store -// caches. -func MakeCache(caches []model.ConfigStoreController) (model.ConfigStoreController, error) { - return MakeWriteableCache(caches, nil) -} - -type store struct { - // schemas is the unified - schemas collection.Schemas - - // stores is a mapping from config type to a store - stores map[config.GroupVersionKind][]model.ConfigStore - - writer model.ConfigStore -} - -func (cr *store) Schemas() collection.Schemas { - return cr.schemas -} - -// Get the first config found in the stores. -func (cr *store) Get(typ config.GroupVersionKind, name, namespace string) *config.Config { - for _, store := range cr.stores[typ] { - config := store.Get(typ, name, namespace) - if config != nil { - return config - } - } - return nil -} - -// List all configs in the stores. -func (cr *store) List(typ config.GroupVersionKind, namespace string) ([]config.Config, error) { - if len(cr.stores[typ]) == 0 { - return nil, nil - } - var errs *multierror.Error - var configs []config.Config - // Used to remove duplicated config - configMap := sets.New() - - for _, store := range cr.stores[typ] { - storeConfigs, err := store.List(typ, namespace) - if err != nil { - errs = multierror.Append(errs, err) - } - for _, cfg := range storeConfigs { - key := cfg.GroupVersionKind.Kind + cfg.Namespace + cfg.Name - if !configMap.Contains(key) { - configs = append(configs, cfg) - configMap.Insert(key) - } - } - } - return configs, errs.ErrorOrNil() -} - -func (cr *store) Delete(typ config.GroupVersionKind, name, namespace string, resourceVersion *string) error { - if cr.writer == nil { - return errorUnsupported - } - return cr.writer.Delete(typ, name, namespace, resourceVersion) -} - -func (cr *store) Create(c config.Config) (string, error) { - if cr.writer == nil { - return "", errorUnsupported - } - return cr.writer.Create(c) -} - -func (cr *store) Update(c config.Config) (string, error) { - if cr.writer == nil { - return "", errorUnsupported - } - return cr.writer.Update(c) -} - -func (cr *store) UpdateStatus(c config.Config) (string, error) { - if cr.writer == nil { - return "", errorUnsupported - } - return cr.writer.UpdateStatus(c) -} - -func (cr *store) Patch(orig config.Config, patchFn config.PatchFunc) (string, error) { - if cr.writer == nil { - return "", errorUnsupported - } - return cr.writer.Patch(orig, patchFn) -} - -type storeCache struct { - model.ConfigStore - caches []model.ConfigStoreController -} - -func (cr *storeCache) HasSynced() bool { - for _, cache := range cr.caches { - if !cache.HasSynced() { - return false - } - } - return true -} - -func (cr *storeCache) RegisterEventHandler(kind config.GroupVersionKind, handler model.EventHandler) { - for _, cache := range cr.caches { - if _, exists := cache.Schemas().FindByGroupVersionKind(kind); exists { - cache.RegisterEventHandler(kind, handler) - } - } -} - -func (cr *storeCache) SetWatchErrorHandler(handler func(r *cache.Reflector, err error)) error { - var errs error - for _, cache := range cr.caches { - if err := cache.SetWatchErrorHandler(handler); err != nil { - errs = multierror.Append(errs, err) - } - } - return errs -} - -func (cr *storeCache) Run(stop <-chan struct{}) { - for _, cache := range cr.caches { - go cache.Run(stop) - } - <-stop -} diff --git a/pilot/pkg/config/aggregate/config_test.go b/pilot/pkg/config/aggregate/config_test.go deleted file mode 100644 index cb5cb1290..000000000 --- a/pilot/pkg/config/aggregate/config_test.go +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package aggregate - -import ( - "strings" - "testing" - "time" -) - -import ( - "github.com/onsi/gomega" - "go.uber.org/atomic" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/legacy/testing/fixtures" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/resource" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -func TestAggregateStoreBasicMake(t *testing.T) { - g := gomega.NewWithT(t) - - schema1 := collections.K8SGatewayApiV1Alpha2Httproutes - schema2 := collections.K8SGatewayApiV1Alpha2Gatewayclasses - store1 := memory.Make(collection.SchemasFor(schema1)) - store2 := memory.Make(collection.SchemasFor(schema2)) - - stores := []model.ConfigStore{store1, store2} - - store, err := makeStore(stores, nil) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - schemas := store.Schemas() - g.Expect(schemas.All()).To(gomega.HaveLen(2)) - fixtures.ExpectEqual(t, schemas, collection.SchemasFor(schema1, schema2)) -} - -func TestAggregateStoreMakeValidationFailure(t *testing.T) { - g := gomega.NewWithT(t) - - store1 := memory.Make(collection.SchemasFor(schemaFor("SomeConfig", "broken message name"))) - - stores := []model.ConfigStore{store1} - - store, err := makeStore(stores, nil) - g.Expect(err).To(gomega.MatchError(gomega.ContainSubstring("not found: broken message name"))) - g.Expect(store).To(gomega.BeNil()) -} - -func TestAggregateStoreGet(t *testing.T) { - g := gomega.NewWithT(t) - - store1 := memory.Make(collection.SchemasFor(collections.K8SGatewayApiV1Alpha2Gatewayclasses)) - store2 := memory.Make(collection.SchemasFor(collections.K8SGatewayApiV1Alpha2Gatewayclasses)) - - configReturn := &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.GatewayClass, - Name: "other", - }, - } - - _, err := store1.Create(*configReturn) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - stores := []model.ConfigStore{store1, store2} - - store, err := makeStore(stores, nil) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - c := store.Get(gvk.GatewayClass, "other", "") - g.Expect(c.Name).To(gomega.Equal(configReturn.Name)) -} - -func TestAggregateStoreList(t *testing.T) { - g := gomega.NewWithT(t) - - store1 := memory.Make(collection.SchemasFor(collections.K8SGatewayApiV1Alpha2Httproutes)) - store2 := memory.Make(collection.SchemasFor(collections.K8SGatewayApiV1Alpha2Httproutes)) - - if _, err := store1.Create(config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.HTTPRoute, - Name: "other", - }, - }); err != nil { - t.Fatal(err) - } - if _, err := store2.Create(config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.HTTPRoute, - Name: "another", - }, - }); err != nil { - t.Fatal(err) - } - - stores := []model.ConfigStore{store1, store2} - - store, err := makeStore(stores, nil) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - l, err := store.List(gvk.HTTPRoute, "") - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(l).To(gomega.HaveLen(2)) -} - -func TestAggregateStoreWrite(t *testing.T) { - g := gomega.NewWithT(t) - - store1 := memory.Make(collection.SchemasFor(collections.K8SGatewayApiV1Alpha2Httproutes)) - store2 := memory.Make(collection.SchemasFor(collections.K8SGatewayApiV1Alpha2Httproutes)) - - stores := []model.ConfigStore{store1, store2} - - store, err := makeStore(stores, store1) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - if _, err := store.Create(config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.HTTPRoute, - Name: "other", - }, - }); err != nil { - t.Fatal(err) - } - - la, err := store.List(gvk.HTTPRoute, "") - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(la).To(gomega.HaveLen(1)) - g.Expect(la[0].Name).To(gomega.Equal("other")) - - l, err := store1.List(gvk.HTTPRoute, "") - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(l).To(gomega.HaveLen(1)) - g.Expect(l[0].Name).To(gomega.Equal("other")) - - // Check the aggregated and individual store return identical response - g.Expect(la).To(gomega.BeEquivalentTo(l)) - - l, err = store2.List(gvk.HTTPRoute, "") - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(l).To(gomega.HaveLen(0)) -} - -func TestAggregateStoreWriteWithoutWriter(t *testing.T) { - g := gomega.NewWithT(t) - - store1 := memory.Make(collection.SchemasFor(collections.K8SGatewayApiV1Alpha2Httproutes)) - store2 := memory.Make(collection.SchemasFor(collections.K8SGatewayApiV1Alpha2Httproutes)) - - stores := []model.ConfigStore{store1, store2} - - store, err := makeStore(stores, nil) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - if _, err := store.Create(config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.HTTPRoute, - Name: "other", - }, - }); err != errorUnsupported { - t.Fatalf("unexpected error, want %v got %v", errorUnsupported, err) - } -} - -func TestAggregateStoreFails(t *testing.T) { - g := gomega.NewWithT(t) - - store1 := memory.Make(collection.SchemasFor(schemaFor("OtherConfig", "istio.networking.v1alpha3.Gateway"))) - - stores := []model.ConfigStore{store1} - - store, err := makeStore(stores, nil) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - t.Run("Fails to Delete", func(t *testing.T) { - g := gomega.NewWithT(t) - - err = store.Delete(config.GroupVersionKind{Kind: "not"}, "gonna", "work", nil) - g.Expect(err).To(gomega.MatchError(gomega.ContainSubstring("unsupported operation"))) - }) - - t.Run("Fails to Create", func(t *testing.T) { - g := gomega.NewWithT(t) - - c, err := store.Create(config.Config{}) - g.Expect(err).To(gomega.MatchError(gomega.ContainSubstring("unsupported operation"))) - g.Expect(c).To(gomega.BeEmpty()) - }) - - t.Run("Fails to Update", func(t *testing.T) { - g := gomega.NewWithT(t) - - c, err := store.Update(config.Config{}) - g.Expect(err).To(gomega.MatchError(gomega.ContainSubstring("unsupported operation"))) - g.Expect(c).To(gomega.BeEmpty()) - }) -} - -func TestAggregateStoreCache(t *testing.T) { - stop := make(chan struct{}) - defer func() { close(stop) }() - - store1 := memory.Make(collection.SchemasFor(collections.K8SGatewayApiV1Alpha2Httproutes)) - controller1 := memory.NewController(store1) - go controller1.Run(stop) - - store2 := memory.Make(collection.SchemasFor(collections.K8SGatewayApiV1Alpha2Gatewayclasses)) - controller2 := memory.NewController(store2) - go controller2.Run(stop) - - stores := []model.ConfigStoreController{controller1, controller2} - - cacheStore, err := MakeCache(stores) - if err != nil { - t.Fatal(err) - } - - t.Run("it registers an event handler", func(t *testing.T) { - handled := atomic.NewBool(false) - cacheStore.RegisterEventHandler(gvk.HTTPRoute, func(config.Config, config.Config, model.Event) { - handled.Store(true) - }) - - _, err := controller1.Create(config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.HTTPRoute, - Name: "another", - }, - }) - if err != nil { - t.Fatal(err) - } - - retry.UntilOrFail(t, handled.Load, retry.Timeout(time.Second)) - }) -} - -func schemaFor(kind, proto string) collection.Schema { - return collection.Builder{ - Name: strings.ToLower(kind), - Resource: resource.Builder{ - Kind: kind, - Plural: strings.ToLower(kind) + "s", - Proto: proto, - }.BuildNoValidate(), - }.MustBuild() -} diff --git a/pilot/pkg/config/file/store.go b/pilot/pkg/config/file/store.go deleted file mode 100644 index 69a89203c..000000000 --- a/pilot/pkg/config/file/store.go +++ /dev/null @@ -1,525 +0,0 @@ -/* - Copyright Istio Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package file - -import ( - "bufio" - "bytes" - "crypto/sha256" - "encoding/json" - "errors" - "fmt" - "io" - "strings" - "sync" -) - -import ( - "github.com/hashicorp/go-multierror" - yamlv3 "gopkg.in/yaml.v3" - "istio.io/pkg/log" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/serializer" - kubeJson "k8s.io/apimachinery/pkg/runtime/serializer/json" - "k8s.io/apimachinery/pkg/util/yaml" - "k8s.io/client-go/tools/cache" -) - -import ( - kubeyaml2 "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/file/util/kubeyaml" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" - kube2 "github.com/apache/dubbo-go-pixiu/pkg/config/legacy/source/kube" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - schemaresource "github.com/apache/dubbo-go-pixiu/pkg/config/schema/resource" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -var ( - inMemoryKubeNameDiscriminator int64 - scope = log.RegisterScope("file", "File client messages", 0) -) - -// KubeSource is an in-memory source implementation that can handle K8s style resources. -type KubeSource struct { - mu sync.Mutex - - name string - schemas *collection.Schemas - inner model.ConfigStore - defaultNs resource.Namespace - - versionCtr int64 - shas map[kubeResourceKey]resourceSha - byFile map[string]map[kubeResourceKey]config.GroupVersionKind -} - -func (s *KubeSource) Schemas() collection.Schemas { - return *s.schemas -} - -func (s *KubeSource) Get(typ config.GroupVersionKind, name, namespace string) *config.Config { - return s.inner.Get(typ, name, namespace) -} - -func (s *KubeSource) List(typ config.GroupVersionKind, namespace string) ([]config.Config, error) { - return s.inner.List(typ, namespace) -} - -func (s *KubeSource) Create(config config.Config) (revision string, err error) { - return s.inner.Create(config) -} - -func (s *KubeSource) Update(config config.Config) (newRevision string, err error) { - return s.inner.Update(config) -} - -func (s *KubeSource) UpdateStatus(config config.Config) (newRevision string, err error) { - return s.inner.UpdateStatus(config) -} - -func (s *KubeSource) Patch(orig config.Config, patchFn config.PatchFunc) (string, error) { - return s.inner.Patch(orig, patchFn) -} - -func (s *KubeSource) Delete(typ config.GroupVersionKind, name, namespace string, resourceVersion *string) error { - return s.inner.Delete(typ, name, namespace, resourceVersion) -} - -func (s *KubeSource) RegisterEventHandler(kind config.GroupVersionKind, handler model.EventHandler) { - panic("implement me") -} - -func (s *KubeSource) Run(stop <-chan struct{}) { -} - -func (s *KubeSource) SetWatchErrorHandler(f func(r *cache.Reflector, err error)) error { - panic("implement me") -} - -func (s *KubeSource) HasSynced() bool { - return true -} - -type resourceSha [sha256.Size]byte - -type kubeResource struct { - // resource *resource.Instance - config *config.Config - schema collection.Schema - sha resourceSha -} - -func (r *kubeResource) newKey() kubeResourceKey { - return kubeResourceKey{ - kind: r.schema.Resource().Kind(), - fullName: r.fullName(), - } -} - -func (r *kubeResource) fullName() resource.FullName { - return resource.NewFullName(resource.Namespace(r.config.Namespace), - resource.LocalName(r.config.Name)) -} - -type kubeResourceKey struct { - fullName resource.FullName - kind string -} - -var _ model.ConfigStore = &KubeSource{} - -// NewKubeSource returns a new in-memory Source that works with Kubernetes resources. -func NewKubeSource(schemas collection.Schemas) *KubeSource { - name := fmt.Sprintf("kube-inmemory-%d", inMemoryKubeNameDiscriminator) - inMemoryKubeNameDiscriminator++ - - return &KubeSource{ - name: name, - schemas: &schemas, - inner: memory.MakeSkipValidation(schemas), - shas: make(map[kubeResourceKey]resourceSha), - byFile: make(map[string]map[kubeResourceKey]config.GroupVersionKind), - } -} - -// SetDefaultNamespace enables injecting a default namespace for resources where none is already specified -func (s *KubeSource) SetDefaultNamespace(defaultNs resource.Namespace) { - s.defaultNs = defaultNs -} - -// Clear the contents of this source -func (s *KubeSource) Clear() { - s.versionCtr = 0 - s.shas = make(map[kubeResourceKey]resourceSha) - s.byFile = make(map[string]map[kubeResourceKey]config.GroupVersionKind) - s.inner = memory.MakeSkipValidation(*s.schemas) -} - -// ContentNames returns the names known to this source. -func (s *KubeSource) ContentNames() map[string]struct{} { - s.mu.Lock() - defer s.mu.Unlock() - - result := sets.New() - for n := range s.byFile { - result.Insert(n) - } - - return result -} - -// ApplyContent applies the given yamltext to this source. The content is tracked with the given name. If ApplyContent -// gets called multiple times with the same name, the contents applied by the previous incarnation will be overwritten -// or removed, depending on the new content. -// Returns an error if any were encountered, but that still may represent a partial success -func (s *KubeSource) ApplyContent(name, yamlText string) error { - s.mu.Lock() - defer s.mu.Unlock() - - // We hold off on dealing with parseErr until the end, since partial success is possible - resources, parseErrs := s.parseContent(s.schemas, name, yamlText) - - oldKeys := s.byFile[name] - newKeys := make(map[kubeResourceKey]config.GroupVersionKind) - - for _, r := range resources { - key := r.newKey() - - oldSha, found := s.shas[key] - if !found || oldSha != r.sha { - s.versionCtr++ - r.config.ResourceVersion = fmt.Sprintf("v%d", s.versionCtr) - scope.Debug("KubeSource.ApplyContent: Set: ", r.schema.Name(), r.fullName()) - // apply is idempotent, but configstore is not, thus the odd logic here - _, err := s.inner.Update(*r.config) - if err != nil { - _, err = s.inner.Create(*r.config) - if err != nil { - return fmt.Errorf("cannot store config %s/%s %s from reader: %s", - r.schema.Resource().Version(), r.schema.Resource().Kind(), r.fullName(), err) - } - } - s.shas[key] = r.sha - } - newKeys[key] = r.schema.Resource().GroupVersionKind() - if oldKeys != nil { - scope.Debug("KubeSource.ApplyContent: Delete: ", r.schema.Name(), key) - delete(oldKeys, key) - } - } - - for k, col := range oldKeys { - empty := "" - err := s.inner.Delete(col, k.fullName.Name.String(), k.fullName.Namespace.String(), &empty) - if err != nil { - scope.Errorf("encountered unexpected error removing resource from filestore: %s", err) - } - } - s.byFile[name] = newKeys - - if parseErrs != nil { - return fmt.Errorf("errors parsing content %q: %v", name, parseErrs) - } - return nil -} - -// RemoveContent removes the content for the given name -func (s *KubeSource) RemoveContent(name string) { - s.mu.Lock() - defer s.mu.Unlock() - - keys := s.byFile[name] - if keys != nil { - for key, col := range keys { - empty := "" - err := s.inner.Delete(col, key.fullName.Name.String(), key.fullName.Namespace.String(), &empty) - if err != nil { - scope.Errorf("encountered unexpected error removing resource from filestore: %s", err) - } - delete(s.shas, key) - } - - delete(s.byFile, name) - } -} - -func (s *KubeSource) parseContent(r *collection.Schemas, name, yamlText string) ([]kubeResource, error) { - var resources []kubeResource - var errs error - - reader := bufio.NewReader(strings.NewReader(yamlText)) - decoder := kubeyaml2.NewYAMLReader(reader) - chunkCount := -1 - - for { - chunkCount++ - doc, lineNum, err := decoder.Read() - if err == io.EOF { - break - } - if err != nil { - e := fmt.Errorf("error reading documents in %s[%d]: %v", name, chunkCount, err) - scope.Warnf("%v - skipping", e) - scope.Debugf("Failed to parse yamlText chunk: %v", yamlText) - errs = multierror.Append(errs, e) - break - } - - chunk := bytes.TrimSpace(doc) - r, err := s.parseChunk(r, name, lineNum, chunk) - if err != nil { - var uerr *unknownSchemaError - if errors.As(err, &uerr) { - // Note the error to the debug log but continue - scope.Debugf("skipping unknown yaml chunk %s: %s", name, uerr.Error()) - } else { - e := fmt.Errorf("error processing %s[%d]: %v", name, chunkCount, err) - scope.Warnf("%v - skipping", e) - scope.Debugf("Failed to parse yaml chunk: %v", string(chunk)) - errs = multierror.Append(errs, e) - } - continue - } - resources = append(resources, r) - } - - return resources, errs -} - -// unknownSchemaError represents a schema was not found for a group+version+kind. -type unknownSchemaError struct { - group string - version string - kind string -} - -func (e unknownSchemaError) Error() string { - return fmt.Sprintf("failed finding schema for group/version/kind: %s/%s/%s", e.group, e.version, e.kind) -} - -func (s *KubeSource) parseChunk(r *collection.Schemas, name string, lineNum int, yamlChunk []byte) (kubeResource, error) { - // Convert to JSON - jsonChunk, err := yaml.ToJSON(yamlChunk) - if err != nil { - return kubeResource{}, fmt.Errorf("failed converting YAML to JSON: %v", err) - } - - // Peek at the beginning of the JSON to - groupVersionKind, err := kubeJson.DefaultMetaFactory.Interpret(jsonChunk) - if err != nil { - return kubeResource{}, fmt.Errorf("failed interpreting jsonChunk: %v", err) - } - - if groupVersionKind.Empty() { - return kubeResource{}, fmt.Errorf("unable to parse resource with no group, version and kind") - } - - schema, found := r.FindByGroupVersionKind(schemaresource.FromKubernetesGVK(groupVersionKind)) - - if !found { - return kubeResource{}, &unknownSchemaError{ - group: groupVersionKind.Group, - version: groupVersionKind.Version, - kind: groupVersionKind.Kind, - } - } - - // Cannot create new instance. This occurs because while newer types do not implement proto.Message, - // this legacy code only supports proto.Messages. - // Note: while NewInstance can be slightly modified to not return error here, the rest of the code - // still requires a proto.Message so it won't work without completely refactoring galley/ - _, e := schema.Resource().NewInstance() - cannotHandleProto := e != nil - if cannotHandleProto { - return kubeResource{}, &unknownSchemaError{ - group: groupVersionKind.Group, - version: groupVersionKind.Version, - kind: groupVersionKind.Kind, - } - } - - runtimeScheme := runtime.NewScheme() - codecs := serializer.NewCodecFactory(runtimeScheme) - deserializer := codecs.UniversalDeserializer() - obj, err := kube.IstioScheme.New(schema.Resource().GroupVersionKind().Kubernetes()) - if err != nil { - return kubeResource{}, fmt.Errorf("failed to initialize interface for built-in type: %v", err) - } - _, _, err = deserializer.Decode(jsonChunk, nil, obj) - if err != nil { - return kubeResource{}, fmt.Errorf("failed parsing JSON for built-in type: %v", err) - } - objMeta, ok := obj.(metav1.Object) - if !ok { - return kubeResource{}, errors.New("failed to assert type of object metadata") - } - - // If namespace is blank and we have a default set, fill in the default - // (This mirrors the behavior if you kubectl apply a resource without a namespace defined) - // Don't do this for cluster scoped resources - if !schema.Resource().IsClusterScoped() { - if objMeta.GetNamespace() == "" && s.defaultNs != "" { - scope.Debugf("KubeSource.parseChunk: namespace not specified for %q, using %q", objMeta.GetName(), s.defaultNs) - objMeta.SetNamespace(string(s.defaultNs)) - } - } else { - // Clear the namespace if there is any specified. - objMeta.SetNamespace("") - } - - // Build flat map for analyzers if the line JSON object exists, if the YAML text is ill-formed, this will be nil - fieldMap := make(map[string]int) - - // yamlv3.Node contains information like line number of the node, which will be used with its name to construct the field map - yamlChunkNode := yamlv3.Node{} - err = yamlv3.Unmarshal(yamlChunk, &yamlChunkNode) - if err == nil && len(yamlChunkNode.Content) == 1 { - - // Get the Node that contains all the YAML chunk information - yamlNode := yamlChunkNode.Content[0] - - BuildFieldPathMap(yamlNode, lineNum, "", fieldMap) - } - - pos := kube2.Position{Filename: name, Line: lineNum} - c, err := ToConfig(objMeta, schema, &pos, fieldMap) - if err != nil { - return kubeResource{}, err - } - return kubeResource{ - schema: schema, - sha: sha256.Sum256(yamlChunk), - config: c, - }, nil -} - -const ( - FieldMapKey = "istiofilefieldmap" - ReferenceKey = "istiosource" -) - -// ToConfig converts the given object and proto to a config.Config -func ToConfig(object metav1.Object, schema collection.Schema, source resource.Reference, fieldMap map[string]int) (*config.Config, error) { - m, err := runtime.DefaultUnstructuredConverter.ToUnstructured(object) - if err != nil { - return nil, err - } - u := &unstructured.Unstructured{Object: m} - if len(fieldMap) > 0 || source != nil { - // TODO: populate - annots := u.GetAnnotations() - if annots == nil { - annots = map[string]string{} - } - jsonfm, err := json.Marshal(fieldMap) - if err != nil { - return nil, err - } - annots[FieldMapKey] = string(jsonfm) - jsonsource, err := json.Marshal(source) - if err != nil { - return nil, err - } - annots[ReferenceKey] = string(jsonsource) - u.SetAnnotations(annots) - } - result := TranslateObject(u, "", schema) - return result, nil -} - -func TranslateObject(obj *unstructured.Unstructured, domainSuffix string, schema collection.Schema) *config.Config { - mv2, err := schema.Resource().NewInstance() - if err != nil { - panic(err) - } - if spec, ok := obj.UnstructuredContent()["spec"]; ok { - err = runtime.DefaultUnstructuredConverter.FromUnstructured(spec.(map[string]interface{}), mv2) - } else { - err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), mv2) - } - if err != nil { - panic(err) - } - - m := obj - return &config.Config{ - Meta: config.Meta{ - GroupVersionKind: config.GroupVersionKind{ - Group: m.GetObjectKind().GroupVersionKind().Group, - Version: m.GetObjectKind().GroupVersionKind().Version, - Kind: m.GetObjectKind().GroupVersionKind().Kind, - }, - UID: string(m.GetUID()), - Name: m.GetName(), - Namespace: m.GetNamespace(), - Labels: m.GetLabels(), - Annotations: m.GetAnnotations(), - ResourceVersion: m.GetResourceVersion(), - CreationTimestamp: m.GetCreationTimestamp().Time, - OwnerReferences: m.GetOwnerReferences(), - Generation: m.GetGeneration(), - Domain: domainSuffix, - }, - Spec: mv2, - } -} - -// BuildFieldPathMap builds the flat map for each field of the YAML resource -func BuildFieldPathMap(yamlNode *yamlv3.Node, startLineNum int, curPath string, fieldPathMap map[string]int) { - // If no content in the node, terminate the DFS search - if len(yamlNode.Content) == 0 { - return - } - - nodeContent := yamlNode.Content - // Iterate content by a step of 2, because in the content array the value is in the key's next index position - for i := 0; i < len(nodeContent)-1; i += 2 { - // Two condition, i + 1 positions have no content, which means they have the format like "key: value", then build the map - // Or i + 1 has contents, which means "key:\n value...", then perform one more DFS search - keyNode := nodeContent[i] - valueNode := nodeContent[i+1] - pathKeyForMap := fmt.Sprintf("%s.%s", curPath, keyNode.Value) - - switch { - case valueNode.Kind == yamlv3.ScalarNode: - // Can build map because the value node has no content anymore - // minus one because startLineNum starts at line 1, and yamlv3.Node.line also starts at line 1 - fieldPathMap[fmt.Sprintf("{%s}", pathKeyForMap)] = valueNode.Line + startLineNum - 1 - - case valueNode.Kind == yamlv3.MappingNode: - BuildFieldPathMap(valueNode, startLineNum, pathKeyForMap, fieldPathMap) - - case valueNode.Kind == yamlv3.SequenceNode: - for j, node := range valueNode.Content { - pathWithIndex := fmt.Sprintf("%s[%d]", pathKeyForMap, j) - - // Array with values or array with maps - if node.Kind == yamlv3.ScalarNode { - fieldPathMap[fmt.Sprintf("{%s}", pathWithIndex)] = node.Line + startLineNum - 1 - } else { - BuildFieldPathMap(node, startLineNum, pathWithIndex, fieldPathMap) - } - } - } - } -} diff --git a/pilot/pkg/config/file/util/kubeyaml/kubeyaml.go b/pilot/pkg/config/file/util/kubeyaml/kubeyaml.go deleted file mode 100644 index e4533eec0..000000000 --- a/pilot/pkg/config/file/util/kubeyaml/kubeyaml.go +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kubeyaml - -import ( - "bufio" - "bytes" - "io" - "strings" - "unicode" -) - -const ( - yamlSeparator = "---\n" - separator = "---" -) - -// Join the given yaml parts into a single multipart document. -func Join(parts ...[]byte) []byte { - var b bytes.Buffer - - var lastIsNewLine bool - for _, p := range parts { - if len(p) == 0 { - continue - } - if b.Len() != 0 { - if !lastIsNewLine { - _, _ = b.WriteString("\n") - } - b.WriteString(yamlSeparator) - } - _, _ = b.Write(p) - s := string(p) - lastIsNewLine = s[len(s)-1] == '\n' - } - - return b.Bytes() -} - -// JoinString joins the given yaml parts into a single multipart document. -func JoinString(parts ...string) string { - var st strings.Builder - - var lastIsNewLine bool - for _, p := range parts { - if len(p) == 0 { - continue - } - if st.Len() != 0 { - if !lastIsNewLine { - _, _ = st.WriteString("\n") - } - st.WriteString(yamlSeparator) - } - _, _ = st.WriteString(p) - lastIsNewLine = p[len(p)-1] == '\n' - } - - return st.String() -} - -type Reader interface { - Read() ([]byte, error) -} - -// YAMLReader adapts from Kubernetes YAMLReader(apimachinery.k8s.io/pkg/util/yaml/decoder.go). -// It records the start line number of the chunk it reads each time. -type YAMLReader struct { - reader Reader - currLine int -} - -func NewYAMLReader(r *bufio.Reader) *YAMLReader { - return &YAMLReader{ - reader: &LineReader{reader: r}, - currLine: 0, - } -} - -// Read returns a full YAML document and its first line number. -func (r *YAMLReader) Read() ([]byte, int, error) { - var buffer bytes.Buffer - startLine := r.currLine + 1 - foundStart := false - for { - r.currLine++ - line, err := r.reader.Read() - if err != nil && err != io.EOF { - return nil, startLine, err - } - - // detect beginning of the chunk - if !bytes.Equal(line, []byte("\n")) && !bytes.Equal(line, []byte(yamlSeparator)) && !foundStart { - startLine = r.currLine - foundStart = true - } - - sep := len([]byte(separator)) - if i := bytes.Index(line, []byte(separator)); i == 0 { - // We have a potential document terminator - i += sep - after := line[i:] - if len(strings.TrimRightFunc(string(after), unicode.IsSpace)) == 0 { - if buffer.Len() != 0 { - return buffer.Bytes(), startLine, nil - } - if err == io.EOF { - return nil, startLine, err - } - } - } - if err == io.EOF { - if buffer.Len() != 0 { - // If we're at EOF, we have a final, non-terminated line. Return it. - return buffer.Bytes(), startLine, nil - } - return nil, startLine, err - } - buffer.Write(line) - } -} - -type LineReader struct { - reader *bufio.Reader -} - -// Read returns a single line (with '\n' ended) from the underlying reader. -// An error is returned iff there is an error with the underlying reader. -func (r *LineReader) Read() ([]byte, error) { - var ( - isPrefix = true - err error - line []byte - buffer bytes.Buffer - ) - - for isPrefix && err == nil { - line, isPrefix, err = r.reader.ReadLine() - buffer.Write(line) - } - buffer.WriteByte('\n') - return buffer.Bytes(), err -} diff --git a/pilot/pkg/config/file/util/kubeyaml/kubeyaml_test.go b/pilot/pkg/config/file/util/kubeyaml/kubeyaml_test.go deleted file mode 100644 index c600946b1..000000000 --- a/pilot/pkg/config/file/util/kubeyaml/kubeyaml_test.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kubeyaml - -import ( - "bufio" - "fmt" - "io" - "strings" - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -var joinCases = []struct { - merged string - split []string -}{ - { - merged: "", - split: nil, - }, - { - merged: `yaml: foo`, - split: []string{ - `yaml: foo`, - }, - }, - { - merged: ` -yaml: foo ---- -bar: boo -`, - split: []string{ - ` -yaml: foo -`, - `bar: boo -`, - }, - }, - { - merged: ` -yaml: foo ---- -bar: boo -`, - split: []string{ - ` -yaml: foo -`, - ``, - `bar: boo -`, - }, - }, - { - merged: ` -yaml: foo ---- -bar: boo`, - split: []string{ - ` -yaml: foo`, - `bar: boo`, - }, - }, -} - -func TestJoinBytes(t *testing.T) { - for i, c := range joinCases { - t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - g := NewWithT(t) - - var by [][]byte - for _, s := range c.split { - by = append(by, []byte(s)) - } - actual := Join(by...) - - g.Expect(actual).To(Equal([]byte(c.merged))) - }) - } -} - -func TestJoinString(t *testing.T) { - for i, c := range joinCases { - t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - g := NewWithT(t) - - actual := JoinString(c.split...) - - g.Expect(actual).To(Equal(c.merged)) - }) - } -} - -func TestLineNumber(t *testing.T) { - testCases := []struct { - input string - lineNumbers []int - }{ - { - input: "foo: bar\n---\nfoo: baz", - lineNumbers: []int{1, 3}, - }, - { - input: "\n\nfoo: bar\n---\n\n\nfoo: baz", - lineNumbers: []int{3, 7}, - }, - { - input: "---\n\nfoo: bar\n---\n\n\nfoo: baz", - lineNumbers: []int{3, 7}, - }, - } - for i, tc := range testCases { - t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - g := NewWithT(t) - - reader := bufio.NewReader(strings.NewReader(tc.input)) - decoder := NewYAMLReader(reader) - var expectedLineNumbers []int - for { - _, line, err := decoder.Read() - if err == io.EOF { - break - } - expectedLineNumbers = append(expectedLineNumbers, line) - } - g.Expect(expectedLineNumbers).To(Equal(tc.lineNumbers)) - }) - } -} diff --git a/pilot/pkg/config/kube/crd/config.go b/pilot/pkg/config/kube/crd/config.go deleted file mode 100644 index 33e867450..000000000 --- a/pilot/pkg/config/kube/crd/config.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package crd - -import ( - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -// IstioKind is the generic Kubernetes API object wrapper -type IstioKind struct { - meta_v1.TypeMeta - meta_v1.ObjectMeta `json:"metadata"` - Spec map[string]interface{} `json:"spec"` - Status map[string]interface{} `json:"status,omitempty"` -} - -// GetSpec from a wrapper -func (in *IstioKind) GetSpec() map[string]interface{} { - return in.Spec -} - -// GetStatus from a wrapper -func (in *IstioKind) GetStatus() map[string]interface{} { - return in.Status -} - -// GetObjectMeta from a wrapper -func (in *IstioKind) GetObjectMeta() meta_v1.ObjectMeta { - return in.ObjectMeta -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *IstioKind) DeepCopyInto(out *IstioKind) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IstioKind. -func (in *IstioKind) DeepCopy() *IstioKind { - if in == nil { - return nil - } - out := new(IstioKind) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *IstioKind) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// IstioObject is a k8s wrapper interface for config objects -type IstioObject interface { - runtime.Object - GetSpec() map[string]interface{} - GetStatus() map[string]interface{} - GetObjectMeta() meta_v1.ObjectMeta -} diff --git a/pilot/pkg/config/kube/crd/config_test.go b/pilot/pkg/config/kube/crd/config_test.go deleted file mode 100644 index b128f6df6..000000000 --- a/pilot/pkg/config/kube/crd/config_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package crd_test - -import ( - "reflect" - "testing" -) - -import ( - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/crd" -) - -func TestKind(t *testing.T) { - obj := crd.IstioKind{} - - spec := map[string]interface{}{"a": "b"} - obj.Spec = spec - if got := obj.GetSpec(); !reflect.DeepEqual(spec, got) { - t.Errorf("GetSpec() => got %v, want %v", got, spec) - } - - status := map[string]interface{}{"yo": "lit"} - obj.Status = status - if got := obj.GetStatus(); !reflect.DeepEqual(status, got) { - t.Errorf("GetStatus() => got %v, want %v", got, status) - } - - meta := meta_v1.ObjectMeta{Name: "test"} - obj.ObjectMeta = meta - if got := obj.GetObjectMeta(); !reflect.DeepEqual(meta, got) { - t.Errorf("GetObjectMeta() => got %v, want %v", got, meta) - } - - if got := obj.DeepCopy(); !reflect.DeepEqual(*got, obj) { - t.Errorf("DeepCopy() => got %v, want %v", got, obj) - } - - if got := obj.DeepCopyObject(); !reflect.DeepEqual(got, &obj) { - t.Errorf("DeepCopyObject() => got %v, want %v", got, obj) - } - - var empty *crd.IstioKind - if empty.DeepCopy() != nil { - t.Error("DeepCopy of nil should return nil") - } - - if empty.DeepCopyObject() != nil { - t.Error("DeepCopyObject of nil should return nil") - } -} diff --git a/pilot/pkg/config/kube/crd/conversion.go b/pilot/pkg/config/kube/crd/conversion.go deleted file mode 100644 index 999891777..000000000 --- a/pilot/pkg/config/kube/crd/conversion.go +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package crd - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "reflect" -) - -import ( - "github.com/hashicorp/go-multierror" - "gopkg.in/yaml.v2" - "istio.io/api/meta/v1alpha1" - "istio.io/pkg/log" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - kubeyaml "k8s.io/apimachinery/pkg/util/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/resource" -) - -// FromJSON converts a canonical JSON to a proto message -func FromJSON(s collection.Schema, js string) (config.Spec, error) { - c, err := s.Resource().NewInstance() - if err != nil { - return nil, err - } - if err = config.ApplyJSON(c, js); err != nil { - return nil, err - } - return c, nil -} - -func IstioStatusJSONFromMap(jsonMap map[string]interface{}) (config.Status, error) { - if jsonMap == nil { - return nil, nil - } - js, err := json.Marshal(jsonMap) - if err != nil { - return nil, err - } - var status v1alpha1.IstioStatus - err = json.Unmarshal(js, &status) - if err != nil { - return nil, err - } - return &status, nil -} - -// FromYAML converts a canonical YAML to a proto message -func FromYAML(s collection.Schema, yml string) (config.Spec, error) { - c, err := s.Resource().NewInstance() - if err != nil { - return nil, err - } - if err = config.ApplyYAML(c, yml); err != nil { - return nil, err - } - return c, nil -} - -// FromJSONMap converts from a generic map to a proto message using canonical JSON encoding -// JSON encoding is specified here: https://developers.google.com/protocol-buffers/docs/proto3#json -func FromJSONMap(s collection.Schema, data interface{}) (config.Spec, error) { - // Marshal to YAML bytes - str, err := yaml.Marshal(data) - if err != nil { - return nil, err - } - out, err := FromYAML(s, string(str)) - if err != nil { - return nil, multierror.Prefix(err, fmt.Sprintf("YAML decoding error: %v", string(str))) - } - return out, nil -} - -// ConvertObject converts an IstioObject k8s-style object to the internal configuration model. -func ConvertObject(schema collection.Schema, object IstioObject, domain string) (*config.Config, error) { - js, err := json.Marshal(object.GetSpec()) - if err != nil { - return nil, err - } - spec, err := FromJSON(schema, string(js)) - if err != nil { - return nil, err - } - status, err := IstioStatusJSONFromMap(object.GetStatus()) - if err != nil { - log.Errorf("could not get istio status from map %v, err %v", object.GetStatus(), err) - } - meta := object.GetObjectMeta() - - return &config.Config{ - Meta: config.Meta{ - GroupVersionKind: schema.Resource().GroupVersionKind(), - Name: meta.Name, - Namespace: meta.Namespace, - Domain: domain, - Labels: meta.Labels, - Annotations: meta.Annotations, - ResourceVersion: meta.ResourceVersion, - CreationTimestamp: meta.CreationTimestamp.Time, - }, - Spec: spec, - Status: status, - }, nil -} - -// ConvertConfig translates Istio config to k8s config JSON -func ConvertConfig(cfg config.Config) (IstioObject, error) { - spec, err := config.ToMap(cfg.Spec) - if err != nil { - return nil, err - } - status, err := config.ToMap(cfg.Status) - if err != nil { - return nil, err - } - namespace := cfg.Namespace - if namespace == "" { - namespace = meta_v1.NamespaceDefault - } - return &IstioKind{ - TypeMeta: meta_v1.TypeMeta{ - Kind: cfg.GroupVersionKind.Kind, - APIVersion: cfg.GroupVersionKind.Group + "/" + cfg.GroupVersionKind.Version, - }, - ObjectMeta: meta_v1.ObjectMeta{ - Name: cfg.Name, - Namespace: namespace, - ResourceVersion: cfg.ResourceVersion, - Labels: cfg.Labels, - Annotations: cfg.Annotations, - CreationTimestamp: meta_v1.NewTime(cfg.CreationTimestamp), - }, - Spec: spec, - Status: status, - }, nil -} - -// TODO - add special cases for type-to-kind and kind-to-type -// conversions with initial-isms. Consider adding additional type -// information to the abstract model and/or elevating k8s -// representation to first-class type to avoid extra conversions. - -func parseInputsImpl(inputs string, withValidate bool) ([]config.Config, []IstioKind, error) { - var varr []config.Config - var others []IstioKind - reader := bytes.NewReader([]byte(inputs)) - empty := IstioKind{} - - // We store configs as a YaML stream; there may be more than one decoder. - yamlDecoder := kubeyaml.NewYAMLOrJSONDecoder(reader, 512*1024) - for { - obj := IstioKind{} - err := yamlDecoder.Decode(&obj) - if err == io.EOF { - break - } - if err != nil { - return nil, nil, fmt.Errorf("cannot parse proto message: %v", err) - } - if reflect.DeepEqual(obj, empty) { - continue - } - - gvk := obj.GroupVersionKind() - s, exists := collections.PilotGatewayAPI.FindByGroupVersionKind(resource.FromKubernetesGVK(&gvk)) - if !exists { - log.Debugf("unrecognized type %v", obj.Kind) - others = append(others, obj) - continue - } - - cfg, err := ConvertObject(s, &obj, "") - if err != nil { - return nil, nil, fmt.Errorf("cannot parse proto message for %v: %v", obj.Name, err) - } - - if withValidate { - if _, err := s.Resource().ValidateConfig(*cfg); err != nil { - return nil, nil, fmt.Errorf("configuration is invalid: %v", err) - } - } - - varr = append(varr, *cfg) - } - - return varr, others, nil -} - -// ParseInputs reads multiple documents from `kubectl` output and checks with -// the schema. It also returns the list of unrecognized kinds as the second -// response. -// -// NOTE: This function only decodes a subset of the complete k8s -// ObjectMeta as identified by the fields in model.Meta. This -// would typically only be a problem if a user dumps an configuration -// object with kubectl and then re-ingests it. -func ParseInputs(inputs string) ([]config.Config, []IstioKind, error) { - return parseInputsImpl(inputs, true) -} diff --git a/pilot/pkg/config/kube/crd/conversion_test.go b/pilot/pkg/config/kube/crd/conversion_test.go deleted file mode 100644 index 5737e480f..000000000 --- a/pilot/pkg/config/kube/crd/conversion_test.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package crd - -import ( - "testing" -) - -import ( - "istio.io/api/meta/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/test/mock" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -func TestConvert(t *testing.T) { - if _, err := ConvertObject(collections.IstioNetworkingV1Alpha3Virtualservices, &IstioKind{Spec: map[string]interface{}{"x": 1}}, "local"); err != nil { - t.Errorf("error for converting object: %s", err) - } - cfg := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "test", - Namespace: "default", - Domain: "cluster", - ResourceVersion: "1234", - Labels: map[string]string{"label": "value"}, - Annotations: map[string]string{"annotation": "value"}, - }, - Spec: mock.ExampleVirtualService, - Status: &v1alpha1.IstioStatus{ - Conditions: []*v1alpha1.IstioCondition{ - {Type: "Health"}, - }, - }, - } - - obj, err := ConvertConfig(cfg) - if err != nil { - t.Errorf("ConvertConfig() => unexpected error %v", err) - } - got, err := ConvertObject(collections.IstioNetworkingV1Alpha3Virtualservices, obj, "cluster") - if err != nil { - t.Errorf("ConvertObject() => unexpected error %v", err) - } - assert.Equal(t, &cfg, got) -} - -func TestParseInputs(t *testing.T) { - if varr, _, err := ParseInputs(""); len(varr) > 0 || err != nil { - t.Errorf(`ParseInput("") => got %v, %v, want nil, nil`, varr, err) - } - if _, _, err := ParseInputs("a"); err == nil { - t.Error(`ParseInput("a") => got no error`) - } - if _, others, err := ParseInputs("apiVersion: v1\nkind: Pod"); err != nil || len(others) != 1 { - t.Errorf(`ParseInput("kind: Pod") => got %v, %v`, others, err) - } - if varr, others, err := ParseInputs("---\n"); err != nil || len(varr) != 0 || len(others) != 0 { - t.Errorf(`ParseInput("---") => got %v, %v, %v`, varr, others, err) - } - if _, _, err := ParseInputs("apiVersion: networking.istio.io/v1alpha3\nkind: VirtualService\nspec:\n destination: x"); err == nil { - t.Error("ParseInput(bad spec) => got no error") - } - if _, _, err := ParseInputs("apiVersion: networking.istio.io/v1alpha3\nkind: VirtualService\nspec:\n destination:\n service:"); err == nil { - t.Error("ParseInput(invalid spec) => got no error") - } - - // nolint: lll - validInput := `{"apiVersion": "networking.istio.io/v1alpha3", "kind":"VirtualService", "spec":{"hosts":["foo"],"http":[{"route":[{"destination":{"host":"bar"},"weight":100}]}]}}` - varr, _, err := ParseInputs(validInput) - if err != nil || len(varr) == 0 { - t.Errorf("ParseInputs(correct input) => got %v, %v", varr, err) - } -} diff --git a/pilot/pkg/config/kube/crd/leak_test.go b/pilot/pkg/config/kube/crd/leak_test.go deleted file mode 100644 index 6f501b0ad..000000000 --- a/pilot/pkg/config/kube/crd/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package crd - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/config/kube/crdclient/cache_handler.go b/pilot/pkg/config/kube/crdclient/cache_handler.go deleted file mode 100644 index 90154a171..000000000 --- a/pilot/pkg/config/kube/crdclient/cache_handler.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package crdclient - -import ( - "istio.io/pkg/log" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/informers" - _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" - _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" -) - -// cacheHandler abstracts the logic of an informer with a set of handlers. Handlers can be added at runtime -// and will be invoked on each informer event. -type cacheHandler struct { - client *Client - informer cache.SharedIndexInformer - schema collection.Schema - lister func(namespace string) cache.GenericNamespaceLister -} - -func (h *cacheHandler) onEvent(old interface{}, curr interface{}, event model.Event) error { - if err := h.client.checkReadyForEvents(curr); err != nil { - return err - } - - currItem, ok := curr.(runtime.Object) - if !ok { - scope.Warnf("New Object can not be converted to runtime Object %v, is type %T", curr, curr) - return nil - } - currConfig := TranslateObject(currItem, h.schema.Resource().GroupVersionKind(), h.client.domainSuffix) - - var oldConfig config.Config - if old != nil { - oldItem, ok := old.(runtime.Object) - if !ok { - log.Warnf("Old Object can not be converted to runtime Object %v, is type %T", old, old) - return nil - } - oldConfig = TranslateObject(oldItem, h.schema.Resource().GroupVersionKind(), h.client.domainSuffix) - } - - // TODO we may consider passing a pointer to handlers instead of the value. While spec is a pointer, the meta will be copied - for _, f := range h.client.handlers[h.schema.Resource().GroupVersionKind()] { - f(oldConfig, currConfig, event) - } - return nil -} - -func createCacheHandler(cl *Client, schema collection.Schema, i informers.GenericInformer) *cacheHandler { - scope.Debugf("registered CRD %v", schema.Resource().GroupVersionKind()) - h := &cacheHandler{ - client: cl, - schema: schema, - informer: i.Informer(), - } - h.lister = func(namespace string) cache.GenericNamespaceLister { - if schema.Resource().IsClusterScoped() { - return i.Lister() - } - return i.Lister().ByNamespace(namespace) - } - kind := schema.Resource().Kind() - i.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - incrementEvent(kind, "add") - if !cl.beginSync.Load() { - return - } - cl.queue.Push(func() error { - return h.onEvent(nil, obj, model.EventAdd) - }) - }, - UpdateFunc: func(old, cur interface{}) { - incrementEvent(kind, "update") - if !cl.beginSync.Load() { - return - } - cl.queue.Push(func() error { - return h.onEvent(old, cur, model.EventUpdate) - }) - }, - DeleteFunc: func(obj interface{}) { - incrementEvent(kind, "delete") - if !cl.beginSync.Load() { - return - } - cl.queue.Push(func() error { - return h.onEvent(nil, obj, model.EventDelete) - }) - }, - }) - return h -} diff --git a/pilot/pkg/config/kube/crdclient/client.go b/pilot/pkg/config/kube/crdclient/client.go deleted file mode 100644 index 0a061a2bb..000000000 --- a/pilot/pkg/config/kube/crdclient/client.go +++ /dev/null @@ -1,554 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package crdclient provides an implementation of the config store and cache -// using Kubernetes Custom Resources and the informer framework from Kubernetes -// -// This code relies heavily on code generation for performance reasons; to implement the -// Istio store interface, we need to take dynamic inputs. Using the dynamic informers results in poor -// performance, as the cache will store unstructured objects which need to be marshaled on each Get/List call. -// Using istio/client-go directly will cache objects marshaled, allowing us to have cheap Get/List calls, -// at the expense of some code gen. -package crdclient - -import ( - "context" - "errors" - "fmt" - "sync" - "time" -) - -import ( - "github.com/cenkalti/backoff/v4" - jsonmerge "github.com/evanphx/json-patch/v5" - "github.com/hashicorp/go-multierror" - "go.uber.org/atomic" - "gomodules.xyz/jsonpatch/v3" - istioclient "istio.io/client-go/pkg/clientset/versioned" - "istio.io/pkg/log" - crd "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - klabels "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/json" - "k8s.io/client-go/informers" - _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" - _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" - "k8s.io/client-go/tools/cache" - gatewayapiclient "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/queue" -) - -var scope = log.RegisterScope("kube", "Kubernetes client messages", 0) - -// Client is a client for Istio CRDs, implementing config store cache -// This is used for CRUD operators on Istio configuration, as well as handling of events on config changes -type Client struct { - // schemas defines the set of schemas used by this client. - // Note: this must be a subset of the schemas defined in the codegen - schemas collection.Schemas - - // domainSuffix for the config metadata - domainSuffix string - - // revision for this control plane instance. We will only read configs that match this revision. - revision string - - // kinds keeps track of all cache handlers for known types - kinds map[config.GroupVersionKind]*cacheHandler - kindsMu sync.RWMutex - queue queue.Instance - - // handlers defines a list of event handlers per-type - handlers map[config.GroupVersionKind][]model.EventHandler - - // The istio/client-go client we will use to access objects - istioClient istioclient.Interface - - // The gateway-api client we will use to access objects - gatewayAPIClient gatewayapiclient.Interface - - // beginSync is set to true when calling SyncAll, it indicates the controller has began sync resources. - beginSync *atomic.Bool - // initialSync is set to true after performing an initial processing of all objects. - initialSync *atomic.Bool - schemasByCRDName map[string]collection.Schema - client kube.Client - crdMetadataInformer cache.SharedIndexInformer -} - -var _ model.ConfigStoreController = &Client{} - -func New(client kube.Client, revision, domainSuffix string) (model.ConfigStoreController, error) { - schemas := collections.Pilot - if features.EnableGatewayAPI { - schemas = collections.PilotGatewayAPI - } - return NewForSchemas(client, revision, domainSuffix, schemas) -} - -var crdWatches = map[config.GroupVersionKind]*waiter{ - gvk.KubernetesGateway: newWaiter(), - gvk.GatewayClass: newWaiter(), -} - -type waiter struct { - once sync.Once - stop chan struct{} -} - -func newWaiter() *waiter { - return &waiter{ - once: sync.Once{}, - stop: make(chan struct{}), - } -} - -// WaitForCRD waits until the request CRD exists, and returns true on success. A false return value -// indicates the CRD does not exist but the wait failed or was canceled. -// This is useful to conditionally enable controllers based on CRDs being created. -func WaitForCRD(k config.GroupVersionKind, stop <-chan struct{}) bool { - ch, f := crdWatches[k] - if !f { - log.Warnf("waiting for CRD that is not registered") - return false - } - select { - case <-stop: - return false - case <-ch.stop: - return true - } -} - -func NewForSchemas(client kube.Client, revision, domainSuffix string, schemas collection.Schemas) (model.ConfigStoreController, error) { - schemasByCRDName := map[string]collection.Schema{} - for _, s := range schemas.All() { - // From the spec: "Its name MUST be in the format <.spec.name>.<.spec.group>." - name := fmt.Sprintf("%s.%s", s.Resource().Plural(), s.Resource().Group()) - schemasByCRDName[name] = s - } - out := &Client{ - domainSuffix: domainSuffix, - schemas: schemas, - schemasByCRDName: schemasByCRDName, - revision: revision, - queue: queue.NewQueue(1 * time.Second), - kinds: map[config.GroupVersionKind]*cacheHandler{}, - handlers: map[config.GroupVersionKind][]model.EventHandler{}, - client: client, - istioClient: client.Istio(), - gatewayAPIClient: client.GatewayAPI(), - crdMetadataInformer: client.MetadataInformer().ForResource(collections.K8SApiextensionsK8SIoV1Customresourcedefinitions.Resource(). - GroupVersionResource()).Informer(), - beginSync: atomic.NewBool(false), - initialSync: atomic.NewBool(false), - } - - known, err := knownCRDs(client.Ext()) - if err != nil { - return nil, err - } - for _, s := range schemas.All() { - // From the spec: "Its name MUST be in the format <.spec.name>.<.spec.group>." - name := fmt.Sprintf("%s.%s", s.Resource().Plural(), s.Resource().Group()) - crd := true - if _, f := collections.Builtin.Find(s.Name().String()); f { - crd = false - } - if !crd { - handleCRDAdd(out, name, nil) - } else { - if _, f := known[name]; f { - handleCRDAdd(out, name, nil) - } else { - scope.Warnf("Skipping CRD %v as it is not present", s.Resource().GroupVersionKind()) - } - } - } - - return out, nil -} - -// Validate we are ready to handle events. Until the informers are synced, we will block the queue -func (cl *Client) checkReadyForEvents(curr interface{}) error { - if !cl.informerSynced() { - return errors.New("waiting till full synchronization") - } - _, err := cache.DeletionHandlingMetaNamespaceKeyFunc(curr) - if err != nil { - scope.Infof("Error retrieving key: %v", err) - } - return nil -} - -func (cl *Client) RegisterEventHandler(kind config.GroupVersionKind, handler model.EventHandler) { - cl.handlers[kind] = append(cl.handlers[kind], handler) -} - -func (cl *Client) SetWatchErrorHandler(handler func(r *cache.Reflector, err error)) error { - var errs error - for _, h := range cl.allKinds() { - if err := h.informer.SetWatchErrorHandler(handler); err != nil { - errs = multierror.Append(errs, err) - } - } - return errs -} - -// Run the queue and all informers. Callers should wait for HasSynced() before depending on results. -func (cl *Client) Run(stop <-chan struct{}) { - t0 := time.Now() - scope.Info("Starting Pilot K8S CRD controller") - - if !cache.WaitForCacheSync(stop, cl.informerSynced) { - scope.Error("Failed to sync Pilot K8S CRD controller cache") - return - } - cl.SyncAll() - cl.initialSync.Store(true) - scope.Info("Pilot K8S CRD controller synced ", time.Since(t0)) - - cl.crdMetadataInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - crd, ok := obj.(*metav1.PartialObjectMetadata) - if !ok { - // Shouldn't happen - scope.Errorf("wrong type %T: %v", obj, obj) - return - } - handleCRDAdd(cl, crd.Name, stop) - }, - UpdateFunc: nil, - DeleteFunc: nil, - }) - - cl.queue.Run(stop) - scope.Info("controller terminated") -} - -func (cl *Client) informerSynced() bool { - for _, ctl := range cl.allKinds() { - if !ctl.informer.HasSynced() { - scope.Infof("controller %q is syncing...", ctl.schema.Resource().GroupVersionKind()) - return false - } - } - return true -} - -func (cl *Client) HasSynced() bool { - return cl.initialSync.Load() -} - -// SyncAll syncs all the objects during bootstrap to make the configs updated to caches -func (cl *Client) SyncAll() { - cl.beginSync.Store(true) - wg := sync.WaitGroup{} - for _, h := range cl.allKinds() { - handlers := cl.handlers[h.schema.Resource().GroupVersionKind()] - if len(handlers) == 0 { - continue - } - h := h - wg.Add(1) - go func() { - defer wg.Done() - objects := h.informer.GetIndexer().List() - for _, object := range objects { - currItem, ok := object.(runtime.Object) - if !ok { - scope.Warnf("New Object can not be converted to runtime Object %v, is type %T", object, object) - continue - } - currConfig := TranslateObject(currItem, h.schema.Resource().GroupVersionKind(), h.client.domainSuffix) - for _, f := range handlers { - f(config.Config{}, currConfig, model.EventAdd) - } - } - }() - } - wg.Wait() -} - -// Schemas for the store -func (cl *Client) Schemas() collection.Schemas { - return cl.schemas -} - -// Get implements store interface -func (cl *Client) Get(typ config.GroupVersionKind, name, namespace string) *config.Config { - h, f := cl.kind(typ) - if !f { - scope.Warnf("unknown type: %s", typ) - return nil - } - - obj, err := h.lister(namespace).Get(name) - if err != nil { - // TODO we should be returning errors not logging - scope.Warnf("error on get %v/%v: %v", name, namespace, err) - return nil - } - - cfg := TranslateObject(obj, typ, cl.domainSuffix) - if !cl.objectInRevision(&cfg) { - return nil - } - return &cfg -} - -// Create implements store interface -func (cl *Client) Create(cfg config.Config) (string, error) { - if cfg.Spec == nil { - return "", fmt.Errorf("nil spec for %v/%v", cfg.Name, cfg.Namespace) - } - - meta, err := create(cl.istioClient, cl.gatewayAPIClient, cfg, getObjectMetadata(cfg)) - if err != nil { - return "", err - } - return meta.GetResourceVersion(), nil -} - -// Update implements store interface -func (cl *Client) Update(cfg config.Config) (string, error) { - if cfg.Spec == nil { - return "", fmt.Errorf("nil spec for %v/%v", cfg.Name, cfg.Namespace) - } - - meta, err := update(cl.istioClient, cl.gatewayAPIClient, cfg, getObjectMetadata(cfg)) - if err != nil { - return "", err - } - return meta.GetResourceVersion(), nil -} - -func (cl *Client) UpdateStatus(cfg config.Config) (string, error) { - if cfg.Status == nil { - return "", fmt.Errorf("nil status for %v/%v on updateStatus()", cfg.Name, cfg.Namespace) - } - - meta, err := updateStatus(cl.istioClient, cl.gatewayAPIClient, cfg, getObjectMetadata(cfg)) - if err != nil { - return "", err - } - return meta.GetResourceVersion(), nil -} - -// Patch applies only the modifications made in the PatchFunc rather than doing a full replace. Useful to avoid -// read-modify-write conflicts when there are many concurrent-writers to the same resource. -func (cl *Client) Patch(orig config.Config, patchFn config.PatchFunc) (string, error) { - modified, patchType := patchFn(orig.DeepCopy()) - - meta, err := patch(cl.istioClient, cl.gatewayAPIClient, orig, getObjectMetadata(orig), modified, getObjectMetadata(modified), patchType) - if err != nil { - return "", err - } - return meta.GetResourceVersion(), nil -} - -// Delete implements store interface -// `resourceVersion` must be matched before deletion is carried out. If not possible, a 409 Conflict status will be -func (cl *Client) Delete(typ config.GroupVersionKind, name, namespace string, resourceVersion *string) error { - return delete(cl.istioClient, cl.gatewayAPIClient, typ, name, namespace, resourceVersion) -} - -// List implements store interface -func (cl *Client) List(kind config.GroupVersionKind, namespace string) ([]config.Config, error) { - h, f := cl.kind(kind) - if !f { - return nil, nil - } - - list, err := h.lister(namespace).List(klabels.Everything()) - if err != nil { - return nil, err - } - out := make([]config.Config, 0, len(list)) - for _, item := range list { - cfg := TranslateObject(item, kind, cl.domainSuffix) - if cl.objectInRevision(&cfg) { - out = append(out, cfg) - } - } - - return out, err -} - -func (cl *Client) objectInRevision(o *config.Config) bool { - return config.ObjectInRevision(o, cl.revision) -} - -func (cl *Client) allKinds() []*cacheHandler { - cl.kindsMu.RLock() - defer cl.kindsMu.RUnlock() - ret := make([]*cacheHandler, 0, len(cl.kinds)) - for _, k := range cl.kinds { - ret = append(ret, k) - } - return ret -} - -func (cl *Client) kind(r config.GroupVersionKind) (*cacheHandler, bool) { - cl.kindsMu.RLock() - defer cl.kindsMu.RUnlock() - ch, ok := cl.kinds[r] - return ch, ok -} - -// knownCRDs returns all CRDs present in the cluster, with timeout and retries. -func knownCRDs(crdClient apiextensionsclient.Interface) (map[string]struct{}, error) { - var res *crd.CustomResourceDefinitionList - b := backoff.NewExponentialBackOff() - b.InitialInterval = time.Second - b.MaxElapsedTime = time.Minute - err := backoff.Retry(func() error { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - var err error - res, err = crdClient.ApiextensionsV1().CustomResourceDefinitions().List(ctx, metav1.ListOptions{}) - if err == nil { - return nil - } - scope.Errorf("failed to list CRDs: %v", err) - return err - }, b) - if err != nil { - return nil, err - } - - mp := map[string]struct{}{} - for _, r := range res.Items { - mp[r.Name] = struct{}{} - } - return mp, nil -} - -func TranslateObject(r runtime.Object, gvk config.GroupVersionKind, domainSuffix string) config.Config { - translateFunc, f := translationMap[gvk] - if !f { - scope.Errorf("unknown type %v", gvk) - return config.Config{} - } - c := translateFunc(r) - c.Domain = domainSuffix - return c -} - -func getObjectMetadata(config config.Config) metav1.ObjectMeta { - return metav1.ObjectMeta{ - Name: config.Name, - Namespace: config.Namespace, - Labels: config.Labels, - Annotations: config.Annotations, - ResourceVersion: config.ResourceVersion, - OwnerReferences: config.OwnerReferences, - UID: types.UID(config.UID), - } -} - -func genPatchBytes(oldRes, modRes runtime.Object, patchType types.PatchType) ([]byte, error) { - oldJSON, err := json.Marshal(oldRes) - if err != nil { - return nil, fmt.Errorf("failed marhsalling original resource: %v", err) - } - newJSON, err := json.Marshal(modRes) - if err != nil { - return nil, fmt.Errorf("failed marhsalling modified resource: %v", err) - } - switch patchType { - case types.JSONPatchType: - ops, err := jsonpatch.CreatePatch(oldJSON, newJSON) - if err != nil { - return nil, err - } - return json.Marshal(ops) - case types.MergePatchType: - return jsonmerge.CreateMergePatch(oldJSON, newJSON) - default: - return nil, fmt.Errorf("unsupported patch type: %v. must be one of JSONPatchType or MergePatchType", patchType) - } -} - -func handleCRDAdd(cl *Client, name string, stop <-chan struct{}) { - scope.Debugf("adding CRD %q", name) - s, f := cl.schemasByCRDName[name] - if !f { - scope.Debugf("added resource that we are not watching: %v", name) - return - } - resourceGVK := s.Resource().GroupVersionKind() - gvr := s.Resource().GroupVersionResource() - - cl.kindsMu.Lock() - defer cl.kindsMu.Unlock() - if _, f := cl.kinds[resourceGVK]; f { - scope.Debugf("added resource that already exists: %v", resourceGVK) - return - } - var i informers.GenericInformer - var ifactory starter - var err error - switch s.Resource().Group() { - case gvk.KubernetesGateway.Group: - ifactory = cl.client.GatewayAPIInformer() - i, err = cl.client.GatewayAPIInformer().ForResource(gvr) - case gvk.Pod.Group, gvk.Deployment.Group, gvk.MutatingWebhookConfiguration.Group: - ifactory = cl.client.KubeInformer() - i, err = cl.client.KubeInformer().ForResource(gvr) - case gvk.CustomResourceDefinition.Group: - ifactory = cl.client.ExtInformer() - i, err = cl.client.ExtInformer().ForResource(gvr) - default: - ifactory = cl.client.IstioInformer() - i, err = cl.client.IstioInformer().ForResource(gvr) - } - - if err != nil { - // Shouldn't happen - scope.Errorf("failed to create informer for %v: %v", resourceGVK, err) - return - } - cl.kinds[resourceGVK] = createCacheHandler(cl, s, i) - if w, f := crdWatches[resourceGVK]; f { - scope.Infof("notifying watchers %v was created", resourceGVK) - w.once.Do(func() { - close(w.stop) - }) - } - if stop != nil { - // Start informer factory, only if stop is defined. In startup case, we will not start here as - // we will start all factories once we are ready to initialize. - // For dynamically added CRDs, we need to start immediately though - ifactory.Start(stop) - } -} - -type starter interface { - Start(stopCh <-chan struct{}) -} diff --git a/pilot/pkg/config/kube/crdclient/client_test.go b/pilot/pkg/config/kube/crdclient/client_test.go deleted file mode 100644 index f5ed62aab..000000000 --- a/pilot/pkg/config/kube/crdclient/client_test.go +++ /dev/null @@ -1,365 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package crdclient - -import ( - "context" - "fmt" - "reflect" - "testing" - "time" -) - -import ( - "istio.io/api/meta/v1alpha1" - "istio.io/api/networking/v1alpha3" - v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - metadatafake "k8s.io/client-go/metadata/fake" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/resource" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -func makeClient(t *testing.T, schemas collection.Schemas) (model.ConfigStoreController, kube.ExtendedClient) { - fake := kube.NewFakeClient() - for _, s := range schemas.All() { - createCRD(t, fake, s.Resource()) - } - stop := make(chan struct{}) - config, err := New(fake, "", "") - if err != nil { - t.Fatal(err) - } - go config.Run(stop) - fake.RunAndWait(stop) - cache.WaitForCacheSync(stop, config.HasSynced) - t.Cleanup(func() { - close(stop) - }) - return config, fake -} - -// Ensure that the client can run without CRDs present -func TestClientNoCRDs(t *testing.T) { - schema := collection.NewSchemasBuilder().MustAdd(collections.IstioNetworkingV1Alpha3Sidecars).Build() - store, _ := makeClient(t, schema) - retry.UntilOrFail(t, store.HasSynced, retry.Timeout(time.Second)) - r := collections.IstioNetworkingV1Alpha3Virtualservices.Resource() - configMeta := config.Meta{ - Name: "name", - Namespace: "ns", - GroupVersionKind: r.GroupVersionKind(), - } - pb, err := r.NewInstance() - if err != nil { - t.Fatal(err) - } - - if _, err := store.Create(config.Config{ - Meta: configMeta, - Spec: pb, - }); err != nil { - t.Fatalf("Create => got %v", err) - } - retry.UntilSuccessOrFail(t, func() error { - l, err := store.List(r.GroupVersionKind(), configMeta.Namespace) - // List should actually not return an error in this case; this allows running with missing CRDs - // Instead, we just return an empty list. - if err != nil { - return fmt.Errorf("expected no error, but got %v", err) - } - if len(l) != 0 { - return fmt.Errorf("expected no items returned for unknown CRD") - } - return nil - }, retry.Timeout(time.Second*5), retry.Converge(5)) - retry.UntilOrFail(t, func() bool { - return store.Get(r.GroupVersionKind(), configMeta.Name, configMeta.Namespace) == nil - }, retry.Message("expected no items returned for unknown CRD"), retry.Timeout(time.Second*5), retry.Converge(5)) -} - -// Ensure that the client can run without CRDs present, but then added later -func TestClientDelayedCRDs(t *testing.T) { - schema := collection.NewSchemasBuilder().MustAdd(collections.IstioNetworkingV1Alpha3Sidecars).Build() - store, fake := makeClient(t, schema) - retry.UntilOrFail(t, store.HasSynced, retry.Timeout(time.Second)) - r := collections.IstioNetworkingV1Alpha3Virtualservices.Resource() - - // Create a virtual service - configMeta := config.Meta{ - Name: "name", - Namespace: "ns", - GroupVersionKind: r.GroupVersionKind(), - } - pb, err := r.NewInstance() - if err != nil { - t.Fatal(err) - } - if _, err := store.Create(config.Config{ - Meta: configMeta, - Spec: pb, - }); err != nil { - t.Fatalf("Create => got %v", err) - } - - retry.UntilSuccessOrFail(t, func() error { - l, err := store.List(r.GroupVersionKind(), configMeta.Namespace) - // List should actually not return an error in this case; this allows running with missing CRDs - // Instead, we just return an empty list. - if err != nil { - return fmt.Errorf("expected no error, but got %v", err) - } - if len(l) != 0 { - return fmt.Errorf("expected no items returned for unknown CRD") - } - return nil - }, retry.Timeout(time.Second*5), retry.Converge(5)) - - createCRD(t, fake, r) - - retry.UntilSuccessOrFail(t, func() error { - l, err := store.List(r.GroupVersionKind(), configMeta.Namespace) - // List should actually not return an error in this case; this allows running with missing CRDs - // Instead, we just return an empty list. - if err != nil { - return fmt.Errorf("expected no error, but got %v", err) - } - if len(l) != 1 { - return fmt.Errorf("expected items returned") - } - return nil - }, retry.Timeout(time.Second*10), retry.Converge(5)) -} - -// CheckIstioConfigTypes validates that an empty store can do CRUD operators on all given types -func TestClient(t *testing.T) { - store, _ := makeClient(t, collections.PilotGatewayAPI.Union(collections.Kube)) - configName := "name" - configNamespace := "namespace" - timeout := retry.Timeout(time.Millisecond * 200) - for _, c := range collections.PilotGatewayAPI.All() { - name := c.Resource().Kind() - t.Run(name, func(t *testing.T) { - r := c.Resource() - configMeta := config.Meta{ - GroupVersionKind: r.GroupVersionKind(), - Name: configName, - } - if !r.IsClusterScoped() { - configMeta.Namespace = configNamespace - } - - pb, err := r.NewInstance() - if err != nil { - t.Fatal(err) - } - - if _, err := store.Create(config.Config{ - Meta: configMeta, - Spec: pb, - }); err != nil { - t.Fatalf("Create(%v) => got %v", name, err) - } - // Kubernetes is eventually consistent, so we allow a short time to pass before we get - retry.UntilSuccessOrFail(t, func() error { - cfg := store.Get(r.GroupVersionKind(), configName, configMeta.Namespace) - if cfg == nil || !reflect.DeepEqual(cfg.Meta, configMeta) { - return fmt.Errorf("get(%v) => got unexpected object %v", name, cfg) - } - return nil - }, timeout) - - // Validate it shows up in List - retry.UntilSuccessOrFail(t, func() error { - cfgs, err := store.List(r.GroupVersionKind(), configNamespace) - if err != nil { - return err - } - if len(cfgs) != 1 { - return fmt.Errorf("expected 1 config, got %v", len(cfgs)) - } - for _, cfg := range cfgs { - if !reflect.DeepEqual(cfg.Meta, configMeta) { - return fmt.Errorf("get(%v) => got %v", name, cfg) - } - } - return nil - }, timeout) - - // check we can update object metadata - annotations := map[string]string{ - "foo": "bar", - } - configMeta.Annotations = annotations - if _, err := store.Update(config.Config{ - Meta: configMeta, - Spec: pb, - }); err != nil { - t.Errorf("Unexpected Error in Update -> %v", err) - } - if r.StatusKind() != "" { - stat, err := r.Status() - if err != nil { - t.Fatal(err) - } - if _, err := store.UpdateStatus(config.Config{ - Meta: configMeta, - Status: stat, - }); err != nil { - t.Errorf("Unexpected Error in Update -> %v", err) - } - } - var cfg *config.Config - // validate it is updated - retry.UntilSuccessOrFail(t, func() error { - cfg = store.Get(r.GroupVersionKind(), configName, configMeta.Namespace) - if cfg == nil || !reflect.DeepEqual(cfg.Meta, configMeta) { - return fmt.Errorf("get(%v) => got unexpected object %v", name, cfg) - } - return nil - }) - - // check we can patch items - var patchedCfg config.Config - if _, err := store.(*Client).Patch(*cfg, func(cfg config.Config) (config.Config, types.PatchType) { - cfg.Annotations["fizz"] = "buzz" - patchedCfg = cfg - return cfg, types.JSONPatchType - }); err != nil { - t.Errorf("unexpected err in Patch: %v", err) - } - // validate it is updated - retry.UntilSuccessOrFail(t, func() error { - cfg := store.Get(r.GroupVersionKind(), configName, configMeta.Namespace) - if cfg == nil || !reflect.DeepEqual(cfg.Meta, patchedCfg.Meta) { - return fmt.Errorf("get(%v) => got unexpected object %v", name, cfg) - } - return nil - }) - - // Check we can remove items - if err := store.Delete(r.GroupVersionKind(), configName, configNamespace, nil); err != nil { - t.Fatalf("failed to delete: %v", err) - } - retry.UntilSuccessOrFail(t, func() error { - cfg := store.Get(r.GroupVersionKind(), configName, configNamespace) - if cfg != nil { - return fmt.Errorf("get(%v) => got %v, expected item to be deleted", name, cfg) - } - return nil - }, timeout) - }) - } - - t.Run("update status", func(t *testing.T) { - c := collections.IstioNetworkingV1Alpha3Workloadgroups - r := c.Resource() - name := "name1" - namespace := "bar" - cfgMeta := config.Meta{ - GroupVersionKind: r.GroupVersionKind(), - Name: name, - } - if !r.IsClusterScoped() { - cfgMeta.Namespace = namespace - } - pb := &v1alpha3.WorkloadGroup{Probe: &v1alpha3.ReadinessProbe{PeriodSeconds: 6}} - if _, err := store.Create(config.Config{ - Meta: cfgMeta, - Spec: config.Spec(pb), - }); err != nil { - t.Fatalf("Create bad: %v", err) - } - - retry.UntilSuccessOrFail(t, func() error { - cfg := store.Get(r.GroupVersionKind(), name, cfgMeta.Namespace) - if cfg == nil { - return fmt.Errorf("cfg shouldnt be nil :(") - } - if !reflect.DeepEqual(cfg.Meta, cfgMeta) { - return fmt.Errorf("something is deeply wrong....., %v", cfg.Meta) - } - return nil - }) - - stat := &v1alpha1.IstioStatus{ - Conditions: []*v1alpha1.IstioCondition{ - { - Type: "Health", - Message: "heath is badd", - }, - }, - } - - if _, err := store.UpdateStatus(config.Config{ - Meta: cfgMeta, - Spec: config.Spec(pb), - Status: config.Status(stat), - }); err != nil { - t.Errorf("bad: %v", err) - } - - retry.UntilSuccessOrFail(t, func() error { - cfg := store.Get(r.GroupVersionKind(), name, cfgMeta.Namespace) - if cfg == nil { - return fmt.Errorf("cfg cant be nil") - } - if !reflect.DeepEqual(cfg.Status, stat) { - return fmt.Errorf("status %v does not match %v", cfg.Status, stat) - } - return nil - }) - }) -} - -func createCRD(t test.Failer, client kube.Client, r resource.Schema) { - t.Helper() - crd := &v1.CustomResourceDefinition{ - ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s.%s", r.Plural(), r.Group()), - }, - } - if _, err := client.Ext().ApiextensionsV1().CustomResourceDefinitions().Create(context.TODO(), crd, metav1.CreateOptions{}); err != nil { - t.Fatal(err) - } - - // Metadata client fake is not kept in sync, so if using a fake clinet update that as well - fmc, ok := client.Metadata().(*metadatafake.FakeMetadataClient) - if !ok { - return - } - fmg := fmc.Resource(collections.K8SApiextensionsK8SIoV1Customresourcedefinitions.Resource().GroupVersionResource()) - fmd, ok := fmg.(metadatafake.MetadataClient) - if !ok { - return - } - if _, err := fmd.CreateFake(&metav1.PartialObjectMetadata{ - TypeMeta: crd.TypeMeta, - ObjectMeta: crd.ObjectMeta, - }, metav1.CreateOptions{}); err != nil { - t.Fatal(err) - } -} diff --git a/pilot/pkg/config/kube/crdclient/gen/main.go b/pilot/pkg/config/kube/crdclient/gen/main.go deleted file mode 100644 index 01e621d88..000000000 --- a/pilot/pkg/config/kube/crdclient/gen/main.go +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Tool to generate pilot/pkg/config/kube/crdclient/types.gen.go -// Example run command: -// REPO_ROOT=`pwd` go generate ./pilot/pkg/config/kube/crdclient/... -package main - -import ( - "bytes" - "flag" - "fmt" - "go/format" - "log" - "os" - "path" - "text/template" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" -) - -// ConfigData is data struct to feed to types.go template. -type ConfigData struct { - Namespaced bool - VariableName string - APIImport string - ClientImport string - ClientGroupPath string - ClientTypePath string - Kind string - StatusAPIImport string - StatusKind string - - // Support gateway-api, which require a custom client and the Spec suffix - Client string - TypeSuffix string - - Readonly bool - NoSpec bool -} - -var ( - GatewayAPITypes = collections.PilotGatewayAPI.Remove(collections.Pilot.All()...) - NonIstioTypes = collections.All.Remove(collections.Pilot.All()...) -) - -// MakeConfigData prepare data for code generation for the given schema. -func MakeConfigData(schema collection.Schema) ConfigData { - out := ConfigData{ - Namespaced: !schema.Resource().IsClusterScoped(), - VariableName: schema.VariableName(), - APIImport: apiImport[schema.Resource().ProtoPackage()], - ClientImport: clientGoImport[schema.Resource().ProtoPackage()], - ClientGroupPath: clientGoAccessPath[schema.Resource().ProtoPackage()], - ClientTypePath: clientGoTypePath[schema.Resource().Plural()], - Kind: schema.Resource().Kind(), - Client: "ic", - StatusAPIImport: apiImport[schema.Resource().StatusPackage()], - StatusKind: schema.Resource().StatusKind(), - } - if _, f := GatewayAPITypes.Find(schema.Name().String()); f { - out.Client = "sc" - out.TypeSuffix = "Spec" - } else if _, f := NonIstioTypes.Find(schema.Name().String()); f { - out.TypeSuffix = "Spec" - out.Readonly = true - } - if _, f := noSpec[schema.Resource().Plural()]; f { - out.NoSpec = true - } - log.Printf("Generating Istio type %s for %s/%s CRD\n", out.VariableName, out.APIImport, out.Kind) - return out -} - -var ( - // Mapping from istio/api path import to api import path - apiImport = map[string]string{ - "istio.io/api/networking/v1alpha3": "networkingv1alpha3", - "istio.io/api/networking/v1beta1": "networkingv1beta1", - "istio.io/api/security/v1beta1": "securityv1beta1", - "istio.io/api/telemetry/v1alpha1": "telemetryv1alpha1", - "sigs.k8s.io/gateway-api/apis/v1alpha2": "gatewayv1alpha2", - "istio.io/api/meta/v1alpha1": "metav1alpha1", - "istio.io/api/extensions/v1alpha1": "extensionsv1alpha1", - "k8s.io/api/admissionregistration/v1": "admissionregistrationv1", - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1": "apiextensionsv1", - "k8s.io/api/apps/v1": "appsv1", - "k8s.io/api/core/v1": "corev1", - "k8s.io/api/extensions/v1beta1": "extensionsv1beta1", - } - // Mapping from istio/api path import to client go import path - clientGoImport = map[string]string{ - "istio.io/api/networking/v1alpha3": "clientnetworkingv1alpha3", - "istio.io/api/networking/v1beta1": "clientnetworkingv1beta1", - "istio.io/api/security/v1beta1": "clientsecurityv1beta1", - "istio.io/api/telemetry/v1alpha1": "clienttelemetryv1alpha1", - "sigs.k8s.io/gateway-api/apis/v1alpha2": "gatewayv1alpha2", - "istio.io/api/extensions/v1alpha1": "clientextensionsv1alpha1", - "k8s.io/api/admissionregistration/v1": "admissionregistrationv1", - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1": "apiextensionsv1", - "k8s.io/api/apps/v1": "appsv1", - "k8s.io/api/core/v1": "corev1", - "k8s.io/api/extensions/v1beta1": "extensionsv1beta1", - } - // Translates an api import path to the top level path in client-go - clientGoAccessPath = map[string]string{ - "istio.io/api/networking/v1alpha3": "NetworkingV1alpha3", - "istio.io/api/networking/v1beta1": "NetworkingV1beta1", - "istio.io/api/security/v1beta1": "SecurityV1beta1", - "istio.io/api/telemetry/v1alpha1": "TelemetryV1alpha1", - "sigs.k8s.io/gateway-api/apis/v1alpha2": "GatewayV1alpha2", - "istio.io/api/extensions/v1alpha1": "ExtensionsV1alpha1", - "k8s.io/api/admissionregistration/v1": "admissionregistrationv1", - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1": "apiextensionsv1", - "k8s.io/api/apps/v1": "appsv1", - "k8s.io/api/core/v1": "corev1", - "k8s.io/api/extensions/v1beta1": "extensionsv1beta1", - } - // Translates a plural type name to the type path in client-go - // TODO: can we automatically derive this? I don't think we can, its internal to the kubegen - clientGoTypePath = map[string]string{ - "destinationrules": "DestinationRules", - "envoyfilters": "EnvoyFilters", - "gateways": "Gateways", - "serviceentries": "ServiceEntries", - "sidecars": "Sidecars", - "proxyconfigs": "ProxyConfigs", - "virtualservices": "VirtualServices", - "workloadentries": "WorkloadEntries", - "workloadgroups": "WorkloadGroups", - "authorizationpolicies": "AuthorizationPolicies", - "peerauthentications": "PeerAuthentications", - "requestauthentications": "RequestAuthentications", - "gatewayclasses": "GatewayClasses", - "httproutes": "HTTPRoutes", - "tcproutes": "TCPRoutes", - "tlsroutes": "TLSRoutes", - "referencepolicies": "ReferencePolicies", - "telemetries": "Telemetries", - "wasmplugins": "WasmPlugins", - "mutatingwebhookconfigurations": "MutatingWebhookConfigurations", - "customresourcedefinitions": "CustomResourceDefinitions", - "deployments": "Deployments", - "configmaps": "ConfigMaps", - "pods": "Pods", - "services": "Services", - "namespaces": "Namespaces", - "endpoints": "Endpoints", - "nodes": "Nodes", - "secrets": "Secrets", - "ingresses": "Ingresses", - "servicemetadatas": "ServiceMetadatas", - "servicenamemappings": "ServiceNameMappings", - } - - noSpec = map[string]struct{}{ - "secrets": {}, - "endpoints": {}, - "configmaps": {}, - "mutatingwebhookconfigurations": {}, - } -) - -func main() { - templateFile := flag.String("template", path.Join(env.IstioSrc, "pilot/pkg/config/kube/crdclient/gen/types.go.tmpl"), "Template file") - outputFile := flag.String("output", "", "Output file. Leave blank to go to stdout") - flag.Parse() - - tmpl := template.Must(template.ParseFiles(*templateFile)) - - // Prepare to generate types for mock schema and all Istio schemas - typeList := []ConfigData{} - for _, s := range collections.PilotGatewayAPI.Union(collections.Kube).All() { - c := MakeConfigData(s) - if c.ClientGroupPath == "" || c.ClientTypePath == "" || c.ClientImport == "" { - log.Fatalf("invalid config %+v", c) - } - typeList = append(typeList, c) - } - var buffer bytes.Buffer - if err := tmpl.Execute(&buffer, typeList); err != nil { - log.Fatal(fmt.Errorf("template: %v", err)) - } - - // Format source code. - out, err := format.Source(buffer.Bytes()) - if err != nil { - log.Fatal(err) - } - // Output - if outputFile == nil || *outputFile == "" { - fmt.Println(string(out)) - } else if err := os.WriteFile(*outputFile, out, 0o644); err != nil { - panic(err) - } -} diff --git a/pilot/pkg/config/kube/crdclient/gen/types.go.tmpl b/pilot/pkg/config/kube/crdclient/gen/types.go.tmpl deleted file mode 100644 index 5dcd3a84e..000000000 --- a/pilot/pkg/config/kube/crdclient/gen/types.go.tmpl +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by pilot/pkg/config/kube/crdclient/gen/types.go.tmpl DO NOT EDIT! - -package crdclient - -// This file contains Go definitions for Custom Resource Definition kinds -// to adhere to the idiomatic use of k8s API machinery. -// These definitions are synthesized from Istio configuration type descriptors -// as declared in the Istio config model. - -import ( - "context" - "fmt" - metav1alpha1 "istio.io/api/meta/v1alpha1" - - versionedclient "istio.io/client-go/pkg/clientset/versioned" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - gatewayapiclient "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned" - - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - - extensionsv1alpha1 "istio.io/api/extensions/v1alpha1" - networkingv1alpha3 "istio.io/api/networking/v1alpha3" - networkingv1beta1 "istio.io/api/networking/v1beta1" - securityv1beta1 "istio.io/api/security/v1beta1" - telemetryv1alpha1 "istio.io/api/telemetry/v1alpha1" - clientextensionsv1alpha1 "istio.io/client-go/pkg/apis/extensions/v1alpha1" - clientnetworkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3" - clientnetworkingv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1" - clientsecurityv1beta1 "istio.io/client-go/pkg/apis/security/v1beta1" - clienttelemetryv1alpha1 "istio.io/client-go/pkg/apis/telemetry/v1alpha1" - - extensionsv1beta1 "k8s.io/api/extensions/v1beta1" - corev1 "k8s.io/api/core/v1" - appsv1 "k8s.io/api/apps/v1" - admissionregistrationv1 "k8s.io/api/admissionregistration/v1" - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" -) - -func create(ic versionedclient.Interface, sc gatewayapiclient.Interface, cfg config.Config, objMeta metav1.ObjectMeta) (metav1.Object, error) { - switch cfg.GroupVersionKind { -{{- range . }} - {{- if not .Readonly }} - case collections.{{ .VariableName }}.Resource().GroupVersionKind(): - return {{.Client}}.{{ .ClientGroupPath }}().{{ .ClientTypePath }}({{if .Namespaced}}cfg.Namespace{{end}}).Create(context.TODO(), &{{ .ClientImport }}.{{ .Kind }}{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*{{ .APIImport }}.{{ .Kind }}{{ .TypeSuffix }})), - }, metav1.CreateOptions{}) - {{- end }} -{{- end }} - default: - return nil, fmt.Errorf("unsupported type: %v", cfg.GroupVersionKind) - } -} - -func update(ic versionedclient.Interface, sc gatewayapiclient.Interface, cfg config.Config, objMeta metav1.ObjectMeta) (metav1.Object, error) { - switch cfg.GroupVersionKind { -{{- range . }} - {{- if not .Readonly }} - case collections.{{ .VariableName }}.Resource().GroupVersionKind(): - return {{.Client}}.{{ .ClientGroupPath }}().{{ .ClientTypePath }}({{if .Namespaced}}cfg.Namespace{{end}}).Update(context.TODO(), &{{ .ClientImport }}.{{ .Kind }}{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*{{ .APIImport }}.{{ .Kind }}{{ .TypeSuffix }})), - }, metav1.UpdateOptions{}) - {{- end }} -{{- end }} - default: - return nil, fmt.Errorf("unsupported type: %v", cfg.GroupVersionKind) - } -} - -func updateStatus(ic versionedclient.Interface, sc gatewayapiclient.Interface, cfg config.Config, objMeta metav1.ObjectMeta) (metav1.Object, error) { - switch cfg.GroupVersionKind { - {{- range . }} - {{- if not .Readonly }} - {{ if .StatusKind }} - case collections.{{ .VariableName }}.Resource().GroupVersionKind(): - return {{.Client}}.{{ .ClientGroupPath }}().{{ .ClientTypePath }}({{if .Namespaced}}cfg.Namespace{{end}}).UpdateStatus(context.TODO(), &{{ .ClientImport }}.{{ .Kind }}{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*{{ .StatusAPIImport }}.{{ .StatusKind }})), - }, metav1.UpdateOptions{}) - {{- end }} - {{- end }} - {{- end }} - default: - return nil, fmt.Errorf("unsupported type: %v", cfg.GroupVersionKind) - } -} - -func patch(ic versionedclient.Interface, sc gatewayapiclient.Interface, orig config.Config, origMeta metav1.ObjectMeta, mod config.Config, modMeta metav1.ObjectMeta, typ types.PatchType) (metav1.Object, error) { - if orig.GroupVersionKind != mod.GroupVersionKind { - return nil, fmt.Errorf("gvk mismatch: %v, modified: %v", orig.GroupVersionKind, mod.GroupVersionKind) - } - // TODO support setting field manager - switch orig.GroupVersionKind { -{{- range . }} - {{- if not .Readonly }} - case collections.{{ .VariableName }}.Resource().GroupVersionKind(): - oldRes := &{{ .ClientImport }}.{{ .Kind }}{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*{{ .APIImport }}.{{ .Kind }}{{ .TypeSuffix }})), - } - modRes := &{{ .ClientImport }}.{{ .Kind }}{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*{{ .APIImport }}.{{ .Kind }}{{ .TypeSuffix }})), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return {{.Client}}.{{ .ClientGroupPath }}().{{ .ClientTypePath }}({{if .Namespaced}}orig.Namespace{{end}}). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - {{- end }} -{{- end }} - default: - return nil, fmt.Errorf("unsupported type: %v", orig.GroupVersionKind) - } -} - - -func delete(ic versionedclient.Interface, sc gatewayapiclient.Interface, typ config.GroupVersionKind, name, namespace string, resourceVersion *string) error { - var deleteOptions metav1.DeleteOptions - if resourceVersion != nil { - deleteOptions.Preconditions = &metav1.Preconditions{ResourceVersion: resourceVersion} - } - switch typ { -{{- range . }} - {{- if not .Readonly }} - case collections.{{ .VariableName }}.Resource().GroupVersionKind(): - return {{.Client}}.{{ .ClientGroupPath }}().{{ .ClientTypePath }}({{if .Namespaced}}namespace{{end}}).Delete(context.TODO(), name, deleteOptions) - {{- end }} -{{- end }} - default: - return fmt.Errorf("unsupported type: %v", typ) - } -} - -var translationMap = map[config.GroupVersionKind]func(r runtime.Object) config.Config{ -{{- range . }} - collections.{{ .VariableName }}.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*{{ .ClientImport }}.{{ .Kind }}) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.{{ .VariableName }}.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: {{ if not .NoSpec }}&obj.Spec{{ else }}obj{{ end }}, - {{- if .StatusKind }} - Status: &obj.Status, - {{- end }} - } - }, -{{- end }} -} diff --git a/pilot/pkg/config/kube/crdclient/generate.go b/pilot/pkg/config/kube/crdclient/generate.go deleted file mode 100644 index 4edfb44b0..000000000 --- a/pilot/pkg/config/kube/crdclient/generate.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package crdclient - -// nolint: lll -//go:generate go run $REPO_ROOT/pilot/pkg/config/kube/crdclient/gen/main.go --template $REPO_ROOT/pilot/pkg/config/kube/crdclient/gen/types.go.tmpl --output $REPO_ROOT/pilot/pkg/config/kube/crdclient/types.gen.go diff --git a/pilot/pkg/config/kube/crdclient/leak_test.go b/pilot/pkg/config/kube/crdclient/leak_test.go deleted file mode 100644 index 89423e288..000000000 --- a/pilot/pkg/config/kube/crdclient/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package crdclient - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/config/kube/crdclient/metrics.go b/pilot/pkg/config/kube/crdclient/metrics.go deleted file mode 100644 index 5ceed11f4..000000000 --- a/pilot/pkg/config/kube/crdclient/metrics.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package crdclient - -import ( - "istio.io/pkg/monitoring" -) - -var ( - typeTag = monitoring.MustCreateLabel("type") - eventTag = monitoring.MustCreateLabel("event") - - k8sEvents = monitoring.NewSum( - "pilot_k8s_cfg_events", - "Events from k8s config.", - monitoring.WithLabels(typeTag, eventTag), - ) -) - -func init() { - monitoring.MustRegister(k8sEvents) -} - -func incrementEvent(kind, event string) { - k8sEvents.With(typeTag.Value(kind), eventTag.Value(event)).Increment() -} diff --git a/pilot/pkg/config/kube/crdclient/types.gen.go b/pilot/pkg/config/kube/crdclient/types.gen.go deleted file mode 100644 index a842439f7..000000000 --- a/pilot/pkg/config/kube/crdclient/types.gen.go +++ /dev/null @@ -1,1439 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by pilot/pkg/config/kube/crdclient/gen/types.go.tmpl DO NOT EDIT! - -package crdclient - -// This file contains Go definitions for Custom Resource Definition kinds -// to adhere to the idiomatic use of k8s API machinery. -// These definitions are synthesized from Istio configuration type descriptors -// as declared in the Istio config model. - -import ( - "context" - "fmt" -) - -import ( - extensionsv1alpha1 "istio.io/api/extensions/v1alpha1" - metav1alpha1 "istio.io/api/meta/v1alpha1" - networkingv1alpha3 "istio.io/api/networking/v1alpha3" - networkingv1beta1 "istio.io/api/networking/v1beta1" - securityv1beta1 "istio.io/api/security/v1beta1" - telemetryv1alpha1 "istio.io/api/telemetry/v1alpha1" - clientextensionsv1alpha1 "istio.io/client-go/pkg/apis/extensions/v1alpha1" - clientnetworkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3" - clientnetworkingv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1" - clientsecurityv1beta1 "istio.io/client-go/pkg/apis/security/v1beta1" - clienttelemetryv1alpha1 "istio.io/client-go/pkg/apis/telemetry/v1alpha1" - versionedclient "istio.io/client-go/pkg/clientset/versioned" - admissionregistrationv1 "k8s.io/api/admissionregistration/v1" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - extensionsv1beta1 "k8s.io/api/extensions/v1beta1" - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gatewayapiclient "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -func create(ic versionedclient.Interface, sc gatewayapiclient.Interface, cfg config.Config, objMeta metav1.ObjectMeta) (metav1.Object, error) { - switch cfg.GroupVersionKind { - case collections.IstioExtensionsV1Alpha1Servicemetadatas.Resource().GroupVersionKind(): - return ic.ExtensionsV1alpha1().ServiceMetadatas(cfg.Namespace).Create(context.TODO(), &clientextensionsv1alpha1.ServiceMetadata{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*extensionsv1alpha1.ServiceMetadata)), - }, metav1.CreateOptions{}) - case collections.IstioExtensionsV1Alpha1Servicenamemappings.Resource().GroupVersionKind(): - return ic.ExtensionsV1alpha1().ServiceNameMappings(cfg.Namespace).Create(context.TODO(), &clientextensionsv1alpha1.ServiceNameMapping{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*extensionsv1alpha1.ServiceNameMapping)), - }, metav1.CreateOptions{}) - case collections.IstioExtensionsV1Alpha1Wasmplugins.Resource().GroupVersionKind(): - return ic.ExtensionsV1alpha1().WasmPlugins(cfg.Namespace).Create(context.TODO(), &clientextensionsv1alpha1.WasmPlugin{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*extensionsv1alpha1.WasmPlugin)), - }, metav1.CreateOptions{}) - case collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().DestinationRules(cfg.Namespace).Create(context.TODO(), &clientnetworkingv1alpha3.DestinationRule{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*networkingv1alpha3.DestinationRule)), - }, metav1.CreateOptions{}) - case collections.IstioNetworkingV1Alpha3Envoyfilters.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().EnvoyFilters(cfg.Namespace).Create(context.TODO(), &clientnetworkingv1alpha3.EnvoyFilter{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*networkingv1alpha3.EnvoyFilter)), - }, metav1.CreateOptions{}) - case collections.IstioNetworkingV1Alpha3Gateways.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().Gateways(cfg.Namespace).Create(context.TODO(), &clientnetworkingv1alpha3.Gateway{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*networkingv1alpha3.Gateway)), - }, metav1.CreateOptions{}) - case collections.IstioNetworkingV1Alpha3Serviceentries.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().ServiceEntries(cfg.Namespace).Create(context.TODO(), &clientnetworkingv1alpha3.ServiceEntry{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*networkingv1alpha3.ServiceEntry)), - }, metav1.CreateOptions{}) - case collections.IstioNetworkingV1Alpha3Sidecars.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().Sidecars(cfg.Namespace).Create(context.TODO(), &clientnetworkingv1alpha3.Sidecar{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*networkingv1alpha3.Sidecar)), - }, metav1.CreateOptions{}) - case collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().VirtualServices(cfg.Namespace).Create(context.TODO(), &clientnetworkingv1alpha3.VirtualService{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*networkingv1alpha3.VirtualService)), - }, metav1.CreateOptions{}) - case collections.IstioNetworkingV1Alpha3Workloadentries.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().WorkloadEntries(cfg.Namespace).Create(context.TODO(), &clientnetworkingv1alpha3.WorkloadEntry{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*networkingv1alpha3.WorkloadEntry)), - }, metav1.CreateOptions{}) - case collections.IstioNetworkingV1Alpha3Workloadgroups.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().WorkloadGroups(cfg.Namespace).Create(context.TODO(), &clientnetworkingv1alpha3.WorkloadGroup{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*networkingv1alpha3.WorkloadGroup)), - }, metav1.CreateOptions{}) - case collections.IstioNetworkingV1Beta1Proxyconfigs.Resource().GroupVersionKind(): - return ic.NetworkingV1beta1().ProxyConfigs(cfg.Namespace).Create(context.TODO(), &clientnetworkingv1beta1.ProxyConfig{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*networkingv1beta1.ProxyConfig)), - }, metav1.CreateOptions{}) - case collections.IstioSecurityV1Beta1Authorizationpolicies.Resource().GroupVersionKind(): - return ic.SecurityV1beta1().AuthorizationPolicies(cfg.Namespace).Create(context.TODO(), &clientsecurityv1beta1.AuthorizationPolicy{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*securityv1beta1.AuthorizationPolicy)), - }, metav1.CreateOptions{}) - case collections.IstioSecurityV1Beta1Peerauthentications.Resource().GroupVersionKind(): - return ic.SecurityV1beta1().PeerAuthentications(cfg.Namespace).Create(context.TODO(), &clientsecurityv1beta1.PeerAuthentication{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*securityv1beta1.PeerAuthentication)), - }, metav1.CreateOptions{}) - case collections.IstioSecurityV1Beta1Requestauthentications.Resource().GroupVersionKind(): - return ic.SecurityV1beta1().RequestAuthentications(cfg.Namespace).Create(context.TODO(), &clientsecurityv1beta1.RequestAuthentication{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*securityv1beta1.RequestAuthentication)), - }, metav1.CreateOptions{}) - case collections.IstioTelemetryV1Alpha1Telemetries.Resource().GroupVersionKind(): - return ic.TelemetryV1alpha1().Telemetries(cfg.Namespace).Create(context.TODO(), &clienttelemetryv1alpha1.Telemetry{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*telemetryv1alpha1.Telemetry)), - }, metav1.CreateOptions{}) - case collections.K8SGatewayApiV1Alpha2Gatewayclasses.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().GatewayClasses().Create(context.TODO(), &gatewayv1alpha2.GatewayClass{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*gatewayv1alpha2.GatewayClassSpec)), - }, metav1.CreateOptions{}) - case collections.K8SGatewayApiV1Alpha2Gateways.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().Gateways(cfg.Namespace).Create(context.TODO(), &gatewayv1alpha2.Gateway{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*gatewayv1alpha2.GatewaySpec)), - }, metav1.CreateOptions{}) - case collections.K8SGatewayApiV1Alpha2Httproutes.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().HTTPRoutes(cfg.Namespace).Create(context.TODO(), &gatewayv1alpha2.HTTPRoute{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*gatewayv1alpha2.HTTPRouteSpec)), - }, metav1.CreateOptions{}) - case collections.K8SGatewayApiV1Alpha2Referencepolicies.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().ReferencePolicies(cfg.Namespace).Create(context.TODO(), &gatewayv1alpha2.ReferencePolicy{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*gatewayv1alpha2.ReferencePolicySpec)), - }, metav1.CreateOptions{}) - case collections.K8SGatewayApiV1Alpha2Tcproutes.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().TCPRoutes(cfg.Namespace).Create(context.TODO(), &gatewayv1alpha2.TCPRoute{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*gatewayv1alpha2.TCPRouteSpec)), - }, metav1.CreateOptions{}) - case collections.K8SGatewayApiV1Alpha2Tlsroutes.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().TLSRoutes(cfg.Namespace).Create(context.TODO(), &gatewayv1alpha2.TLSRoute{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*gatewayv1alpha2.TLSRouteSpec)), - }, metav1.CreateOptions{}) - default: - return nil, fmt.Errorf("unsupported type: %v", cfg.GroupVersionKind) - } -} - -func update(ic versionedclient.Interface, sc gatewayapiclient.Interface, cfg config.Config, objMeta metav1.ObjectMeta) (metav1.Object, error) { - switch cfg.GroupVersionKind { - case collections.IstioExtensionsV1Alpha1Servicemetadatas.Resource().GroupVersionKind(): - return ic.ExtensionsV1alpha1().ServiceMetadatas(cfg.Namespace).Update(context.TODO(), &clientextensionsv1alpha1.ServiceMetadata{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*extensionsv1alpha1.ServiceMetadata)), - }, metav1.UpdateOptions{}) - case collections.IstioExtensionsV1Alpha1Servicenamemappings.Resource().GroupVersionKind(): - return ic.ExtensionsV1alpha1().ServiceNameMappings(cfg.Namespace).Update(context.TODO(), &clientextensionsv1alpha1.ServiceNameMapping{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*extensionsv1alpha1.ServiceNameMapping)), - }, metav1.UpdateOptions{}) - case collections.IstioExtensionsV1Alpha1Wasmplugins.Resource().GroupVersionKind(): - return ic.ExtensionsV1alpha1().WasmPlugins(cfg.Namespace).Update(context.TODO(), &clientextensionsv1alpha1.WasmPlugin{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*extensionsv1alpha1.WasmPlugin)), - }, metav1.UpdateOptions{}) - case collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().DestinationRules(cfg.Namespace).Update(context.TODO(), &clientnetworkingv1alpha3.DestinationRule{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*networkingv1alpha3.DestinationRule)), - }, metav1.UpdateOptions{}) - case collections.IstioNetworkingV1Alpha3Envoyfilters.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().EnvoyFilters(cfg.Namespace).Update(context.TODO(), &clientnetworkingv1alpha3.EnvoyFilter{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*networkingv1alpha3.EnvoyFilter)), - }, metav1.UpdateOptions{}) - case collections.IstioNetworkingV1Alpha3Gateways.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().Gateways(cfg.Namespace).Update(context.TODO(), &clientnetworkingv1alpha3.Gateway{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*networkingv1alpha3.Gateway)), - }, metav1.UpdateOptions{}) - case collections.IstioNetworkingV1Alpha3Serviceentries.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().ServiceEntries(cfg.Namespace).Update(context.TODO(), &clientnetworkingv1alpha3.ServiceEntry{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*networkingv1alpha3.ServiceEntry)), - }, metav1.UpdateOptions{}) - case collections.IstioNetworkingV1Alpha3Sidecars.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().Sidecars(cfg.Namespace).Update(context.TODO(), &clientnetworkingv1alpha3.Sidecar{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*networkingv1alpha3.Sidecar)), - }, metav1.UpdateOptions{}) - case collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().VirtualServices(cfg.Namespace).Update(context.TODO(), &clientnetworkingv1alpha3.VirtualService{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*networkingv1alpha3.VirtualService)), - }, metav1.UpdateOptions{}) - case collections.IstioNetworkingV1Alpha3Workloadentries.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().WorkloadEntries(cfg.Namespace).Update(context.TODO(), &clientnetworkingv1alpha3.WorkloadEntry{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*networkingv1alpha3.WorkloadEntry)), - }, metav1.UpdateOptions{}) - case collections.IstioNetworkingV1Alpha3Workloadgroups.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().WorkloadGroups(cfg.Namespace).Update(context.TODO(), &clientnetworkingv1alpha3.WorkloadGroup{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*networkingv1alpha3.WorkloadGroup)), - }, metav1.UpdateOptions{}) - case collections.IstioNetworkingV1Beta1Proxyconfigs.Resource().GroupVersionKind(): - return ic.NetworkingV1beta1().ProxyConfigs(cfg.Namespace).Update(context.TODO(), &clientnetworkingv1beta1.ProxyConfig{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*networkingv1beta1.ProxyConfig)), - }, metav1.UpdateOptions{}) - case collections.IstioSecurityV1Beta1Authorizationpolicies.Resource().GroupVersionKind(): - return ic.SecurityV1beta1().AuthorizationPolicies(cfg.Namespace).Update(context.TODO(), &clientsecurityv1beta1.AuthorizationPolicy{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*securityv1beta1.AuthorizationPolicy)), - }, metav1.UpdateOptions{}) - case collections.IstioSecurityV1Beta1Peerauthentications.Resource().GroupVersionKind(): - return ic.SecurityV1beta1().PeerAuthentications(cfg.Namespace).Update(context.TODO(), &clientsecurityv1beta1.PeerAuthentication{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*securityv1beta1.PeerAuthentication)), - }, metav1.UpdateOptions{}) - case collections.IstioSecurityV1Beta1Requestauthentications.Resource().GroupVersionKind(): - return ic.SecurityV1beta1().RequestAuthentications(cfg.Namespace).Update(context.TODO(), &clientsecurityv1beta1.RequestAuthentication{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*securityv1beta1.RequestAuthentication)), - }, metav1.UpdateOptions{}) - case collections.IstioTelemetryV1Alpha1Telemetries.Resource().GroupVersionKind(): - return ic.TelemetryV1alpha1().Telemetries(cfg.Namespace).Update(context.TODO(), &clienttelemetryv1alpha1.Telemetry{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*telemetryv1alpha1.Telemetry)), - }, metav1.UpdateOptions{}) - case collections.K8SGatewayApiV1Alpha2Gatewayclasses.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().GatewayClasses().Update(context.TODO(), &gatewayv1alpha2.GatewayClass{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*gatewayv1alpha2.GatewayClassSpec)), - }, metav1.UpdateOptions{}) - case collections.K8SGatewayApiV1Alpha2Gateways.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().Gateways(cfg.Namespace).Update(context.TODO(), &gatewayv1alpha2.Gateway{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*gatewayv1alpha2.GatewaySpec)), - }, metav1.UpdateOptions{}) - case collections.K8SGatewayApiV1Alpha2Httproutes.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().HTTPRoutes(cfg.Namespace).Update(context.TODO(), &gatewayv1alpha2.HTTPRoute{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*gatewayv1alpha2.HTTPRouteSpec)), - }, metav1.UpdateOptions{}) - case collections.K8SGatewayApiV1Alpha2Referencepolicies.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().ReferencePolicies(cfg.Namespace).Update(context.TODO(), &gatewayv1alpha2.ReferencePolicy{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*gatewayv1alpha2.ReferencePolicySpec)), - }, metav1.UpdateOptions{}) - case collections.K8SGatewayApiV1Alpha2Tcproutes.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().TCPRoutes(cfg.Namespace).Update(context.TODO(), &gatewayv1alpha2.TCPRoute{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*gatewayv1alpha2.TCPRouteSpec)), - }, metav1.UpdateOptions{}) - case collections.K8SGatewayApiV1Alpha2Tlsroutes.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().TLSRoutes(cfg.Namespace).Update(context.TODO(), &gatewayv1alpha2.TLSRoute{ - ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*gatewayv1alpha2.TLSRouteSpec)), - }, metav1.UpdateOptions{}) - default: - return nil, fmt.Errorf("unsupported type: %v", cfg.GroupVersionKind) - } -} - -func updateStatus(ic versionedclient.Interface, sc gatewayapiclient.Interface, cfg config.Config, objMeta metav1.ObjectMeta) (metav1.Object, error) { - switch cfg.GroupVersionKind { - - case collections.IstioExtensionsV1Alpha1Servicemetadatas.Resource().GroupVersionKind(): - return ic.ExtensionsV1alpha1().ServiceMetadatas(cfg.Namespace).UpdateStatus(context.TODO(), &clientextensionsv1alpha1.ServiceMetadata{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*metav1alpha1.IstioStatus)), - }, metav1.UpdateOptions{}) - - case collections.IstioExtensionsV1Alpha1Servicenamemappings.Resource().GroupVersionKind(): - return ic.ExtensionsV1alpha1().ServiceNameMappings(cfg.Namespace).UpdateStatus(context.TODO(), &clientextensionsv1alpha1.ServiceNameMapping{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*metav1alpha1.IstioStatus)), - }, metav1.UpdateOptions{}) - - case collections.IstioExtensionsV1Alpha1Wasmplugins.Resource().GroupVersionKind(): - return ic.ExtensionsV1alpha1().WasmPlugins(cfg.Namespace).UpdateStatus(context.TODO(), &clientextensionsv1alpha1.WasmPlugin{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*metav1alpha1.IstioStatus)), - }, metav1.UpdateOptions{}) - - case collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().DestinationRules(cfg.Namespace).UpdateStatus(context.TODO(), &clientnetworkingv1alpha3.DestinationRule{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*metav1alpha1.IstioStatus)), - }, metav1.UpdateOptions{}) - - case collections.IstioNetworkingV1Alpha3Envoyfilters.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().EnvoyFilters(cfg.Namespace).UpdateStatus(context.TODO(), &clientnetworkingv1alpha3.EnvoyFilter{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*metav1alpha1.IstioStatus)), - }, metav1.UpdateOptions{}) - - case collections.IstioNetworkingV1Alpha3Gateways.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().Gateways(cfg.Namespace).UpdateStatus(context.TODO(), &clientnetworkingv1alpha3.Gateway{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*metav1alpha1.IstioStatus)), - }, metav1.UpdateOptions{}) - - case collections.IstioNetworkingV1Alpha3Serviceentries.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().ServiceEntries(cfg.Namespace).UpdateStatus(context.TODO(), &clientnetworkingv1alpha3.ServiceEntry{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*metav1alpha1.IstioStatus)), - }, metav1.UpdateOptions{}) - - case collections.IstioNetworkingV1Alpha3Sidecars.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().Sidecars(cfg.Namespace).UpdateStatus(context.TODO(), &clientnetworkingv1alpha3.Sidecar{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*metav1alpha1.IstioStatus)), - }, metav1.UpdateOptions{}) - - case collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().VirtualServices(cfg.Namespace).UpdateStatus(context.TODO(), &clientnetworkingv1alpha3.VirtualService{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*metav1alpha1.IstioStatus)), - }, metav1.UpdateOptions{}) - - case collections.IstioNetworkingV1Alpha3Workloadentries.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().WorkloadEntries(cfg.Namespace).UpdateStatus(context.TODO(), &clientnetworkingv1alpha3.WorkloadEntry{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*metav1alpha1.IstioStatus)), - }, metav1.UpdateOptions{}) - - case collections.IstioNetworkingV1Alpha3Workloadgroups.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().WorkloadGroups(cfg.Namespace).UpdateStatus(context.TODO(), &clientnetworkingv1alpha3.WorkloadGroup{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*metav1alpha1.IstioStatus)), - }, metav1.UpdateOptions{}) - - case collections.IstioNetworkingV1Beta1Proxyconfigs.Resource().GroupVersionKind(): - return ic.NetworkingV1beta1().ProxyConfigs(cfg.Namespace).UpdateStatus(context.TODO(), &clientnetworkingv1beta1.ProxyConfig{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*metav1alpha1.IstioStatus)), - }, metav1.UpdateOptions{}) - - case collections.IstioSecurityV1Beta1Authorizationpolicies.Resource().GroupVersionKind(): - return ic.SecurityV1beta1().AuthorizationPolicies(cfg.Namespace).UpdateStatus(context.TODO(), &clientsecurityv1beta1.AuthorizationPolicy{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*metav1alpha1.IstioStatus)), - }, metav1.UpdateOptions{}) - - case collections.IstioSecurityV1Beta1Peerauthentications.Resource().GroupVersionKind(): - return ic.SecurityV1beta1().PeerAuthentications(cfg.Namespace).UpdateStatus(context.TODO(), &clientsecurityv1beta1.PeerAuthentication{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*metav1alpha1.IstioStatus)), - }, metav1.UpdateOptions{}) - - case collections.IstioSecurityV1Beta1Requestauthentications.Resource().GroupVersionKind(): - return ic.SecurityV1beta1().RequestAuthentications(cfg.Namespace).UpdateStatus(context.TODO(), &clientsecurityv1beta1.RequestAuthentication{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*metav1alpha1.IstioStatus)), - }, metav1.UpdateOptions{}) - - case collections.IstioTelemetryV1Alpha1Telemetries.Resource().GroupVersionKind(): - return ic.TelemetryV1alpha1().Telemetries(cfg.Namespace).UpdateStatus(context.TODO(), &clienttelemetryv1alpha1.Telemetry{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*metav1alpha1.IstioStatus)), - }, metav1.UpdateOptions{}) - - case collections.K8SGatewayApiV1Alpha2Gatewayclasses.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().GatewayClasses().UpdateStatus(context.TODO(), &gatewayv1alpha2.GatewayClass{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*gatewayv1alpha2.GatewayClassStatus)), - }, metav1.UpdateOptions{}) - - case collections.K8SGatewayApiV1Alpha2Gateways.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().Gateways(cfg.Namespace).UpdateStatus(context.TODO(), &gatewayv1alpha2.Gateway{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*gatewayv1alpha2.GatewayStatus)), - }, metav1.UpdateOptions{}) - - case collections.K8SGatewayApiV1Alpha2Httproutes.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().HTTPRoutes(cfg.Namespace).UpdateStatus(context.TODO(), &gatewayv1alpha2.HTTPRoute{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*gatewayv1alpha2.HTTPRouteStatus)), - }, metav1.UpdateOptions{}) - - case collections.K8SGatewayApiV1Alpha2Tcproutes.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().TCPRoutes(cfg.Namespace).UpdateStatus(context.TODO(), &gatewayv1alpha2.TCPRoute{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*gatewayv1alpha2.TCPRouteStatus)), - }, metav1.UpdateOptions{}) - - case collections.K8SGatewayApiV1Alpha2Tlsroutes.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().TLSRoutes(cfg.Namespace).UpdateStatus(context.TODO(), &gatewayv1alpha2.TLSRoute{ - ObjectMeta: objMeta, - Status: *(cfg.Status.(*gatewayv1alpha2.TLSRouteStatus)), - }, metav1.UpdateOptions{}) - default: - return nil, fmt.Errorf("unsupported type: %v", cfg.GroupVersionKind) - } -} - -func patch(ic versionedclient.Interface, sc gatewayapiclient.Interface, orig config.Config, origMeta metav1.ObjectMeta, mod config.Config, modMeta metav1.ObjectMeta, typ types.PatchType) (metav1.Object, error) { - if orig.GroupVersionKind != mod.GroupVersionKind { - return nil, fmt.Errorf("gvk mismatch: %v, modified: %v", orig.GroupVersionKind, mod.GroupVersionKind) - } - // TODO support setting field manager - switch orig.GroupVersionKind { - case collections.IstioExtensionsV1Alpha1Servicemetadatas.Resource().GroupVersionKind(): - oldRes := &clientextensionsv1alpha1.ServiceMetadata{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*extensionsv1alpha1.ServiceMetadata)), - } - modRes := &clientextensionsv1alpha1.ServiceMetadata{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*extensionsv1alpha1.ServiceMetadata)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return ic.ExtensionsV1alpha1().ServiceMetadatas(orig.Namespace). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - case collections.IstioExtensionsV1Alpha1Servicenamemappings.Resource().GroupVersionKind(): - oldRes := &clientextensionsv1alpha1.ServiceNameMapping{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*extensionsv1alpha1.ServiceNameMapping)), - } - modRes := &clientextensionsv1alpha1.ServiceNameMapping{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*extensionsv1alpha1.ServiceNameMapping)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return ic.ExtensionsV1alpha1().ServiceNameMappings(orig.Namespace). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - case collections.IstioExtensionsV1Alpha1Wasmplugins.Resource().GroupVersionKind(): - oldRes := &clientextensionsv1alpha1.WasmPlugin{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*extensionsv1alpha1.WasmPlugin)), - } - modRes := &clientextensionsv1alpha1.WasmPlugin{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*extensionsv1alpha1.WasmPlugin)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return ic.ExtensionsV1alpha1().WasmPlugins(orig.Namespace). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - case collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(): - oldRes := &clientnetworkingv1alpha3.DestinationRule{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*networkingv1alpha3.DestinationRule)), - } - modRes := &clientnetworkingv1alpha3.DestinationRule{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*networkingv1alpha3.DestinationRule)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return ic.NetworkingV1alpha3().DestinationRules(orig.Namespace). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - case collections.IstioNetworkingV1Alpha3Envoyfilters.Resource().GroupVersionKind(): - oldRes := &clientnetworkingv1alpha3.EnvoyFilter{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*networkingv1alpha3.EnvoyFilter)), - } - modRes := &clientnetworkingv1alpha3.EnvoyFilter{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*networkingv1alpha3.EnvoyFilter)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return ic.NetworkingV1alpha3().EnvoyFilters(orig.Namespace). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - case collections.IstioNetworkingV1Alpha3Gateways.Resource().GroupVersionKind(): - oldRes := &clientnetworkingv1alpha3.Gateway{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*networkingv1alpha3.Gateway)), - } - modRes := &clientnetworkingv1alpha3.Gateway{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*networkingv1alpha3.Gateway)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return ic.NetworkingV1alpha3().Gateways(orig.Namespace). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - case collections.IstioNetworkingV1Alpha3Serviceentries.Resource().GroupVersionKind(): - oldRes := &clientnetworkingv1alpha3.ServiceEntry{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*networkingv1alpha3.ServiceEntry)), - } - modRes := &clientnetworkingv1alpha3.ServiceEntry{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*networkingv1alpha3.ServiceEntry)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return ic.NetworkingV1alpha3().ServiceEntries(orig.Namespace). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - case collections.IstioNetworkingV1Alpha3Sidecars.Resource().GroupVersionKind(): - oldRes := &clientnetworkingv1alpha3.Sidecar{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*networkingv1alpha3.Sidecar)), - } - modRes := &clientnetworkingv1alpha3.Sidecar{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*networkingv1alpha3.Sidecar)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return ic.NetworkingV1alpha3().Sidecars(orig.Namespace). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - case collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(): - oldRes := &clientnetworkingv1alpha3.VirtualService{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*networkingv1alpha3.VirtualService)), - } - modRes := &clientnetworkingv1alpha3.VirtualService{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*networkingv1alpha3.VirtualService)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return ic.NetworkingV1alpha3().VirtualServices(orig.Namespace). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - case collections.IstioNetworkingV1Alpha3Workloadentries.Resource().GroupVersionKind(): - oldRes := &clientnetworkingv1alpha3.WorkloadEntry{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*networkingv1alpha3.WorkloadEntry)), - } - modRes := &clientnetworkingv1alpha3.WorkloadEntry{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*networkingv1alpha3.WorkloadEntry)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return ic.NetworkingV1alpha3().WorkloadEntries(orig.Namespace). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - case collections.IstioNetworkingV1Alpha3Workloadgroups.Resource().GroupVersionKind(): - oldRes := &clientnetworkingv1alpha3.WorkloadGroup{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*networkingv1alpha3.WorkloadGroup)), - } - modRes := &clientnetworkingv1alpha3.WorkloadGroup{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*networkingv1alpha3.WorkloadGroup)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return ic.NetworkingV1alpha3().WorkloadGroups(orig.Namespace). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - case collections.IstioNetworkingV1Beta1Proxyconfigs.Resource().GroupVersionKind(): - oldRes := &clientnetworkingv1beta1.ProxyConfig{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*networkingv1beta1.ProxyConfig)), - } - modRes := &clientnetworkingv1beta1.ProxyConfig{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*networkingv1beta1.ProxyConfig)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return ic.NetworkingV1beta1().ProxyConfigs(orig.Namespace). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - case collections.IstioSecurityV1Beta1Authorizationpolicies.Resource().GroupVersionKind(): - oldRes := &clientsecurityv1beta1.AuthorizationPolicy{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*securityv1beta1.AuthorizationPolicy)), - } - modRes := &clientsecurityv1beta1.AuthorizationPolicy{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*securityv1beta1.AuthorizationPolicy)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return ic.SecurityV1beta1().AuthorizationPolicies(orig.Namespace). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - case collections.IstioSecurityV1Beta1Peerauthentications.Resource().GroupVersionKind(): - oldRes := &clientsecurityv1beta1.PeerAuthentication{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*securityv1beta1.PeerAuthentication)), - } - modRes := &clientsecurityv1beta1.PeerAuthentication{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*securityv1beta1.PeerAuthentication)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return ic.SecurityV1beta1().PeerAuthentications(orig.Namespace). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - case collections.IstioSecurityV1Beta1Requestauthentications.Resource().GroupVersionKind(): - oldRes := &clientsecurityv1beta1.RequestAuthentication{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*securityv1beta1.RequestAuthentication)), - } - modRes := &clientsecurityv1beta1.RequestAuthentication{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*securityv1beta1.RequestAuthentication)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return ic.SecurityV1beta1().RequestAuthentications(orig.Namespace). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - case collections.IstioTelemetryV1Alpha1Telemetries.Resource().GroupVersionKind(): - oldRes := &clienttelemetryv1alpha1.Telemetry{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*telemetryv1alpha1.Telemetry)), - } - modRes := &clienttelemetryv1alpha1.Telemetry{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*telemetryv1alpha1.Telemetry)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return ic.TelemetryV1alpha1().Telemetries(orig.Namespace). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - case collections.K8SGatewayApiV1Alpha2Gatewayclasses.Resource().GroupVersionKind(): - oldRes := &gatewayv1alpha2.GatewayClass{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*gatewayv1alpha2.GatewayClassSpec)), - } - modRes := &gatewayv1alpha2.GatewayClass{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*gatewayv1alpha2.GatewayClassSpec)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return sc.GatewayV1alpha2().GatewayClasses(). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - case collections.K8SGatewayApiV1Alpha2Gateways.Resource().GroupVersionKind(): - oldRes := &gatewayv1alpha2.Gateway{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*gatewayv1alpha2.GatewaySpec)), - } - modRes := &gatewayv1alpha2.Gateway{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*gatewayv1alpha2.GatewaySpec)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return sc.GatewayV1alpha2().Gateways(orig.Namespace). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - case collections.K8SGatewayApiV1Alpha2Httproutes.Resource().GroupVersionKind(): - oldRes := &gatewayv1alpha2.HTTPRoute{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*gatewayv1alpha2.HTTPRouteSpec)), - } - modRes := &gatewayv1alpha2.HTTPRoute{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*gatewayv1alpha2.HTTPRouteSpec)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return sc.GatewayV1alpha2().HTTPRoutes(orig.Namespace). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - case collections.K8SGatewayApiV1Alpha2Referencepolicies.Resource().GroupVersionKind(): - oldRes := &gatewayv1alpha2.ReferencePolicy{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*gatewayv1alpha2.ReferencePolicySpec)), - } - modRes := &gatewayv1alpha2.ReferencePolicy{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*gatewayv1alpha2.ReferencePolicySpec)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return sc.GatewayV1alpha2().ReferencePolicies(orig.Namespace). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - case collections.K8SGatewayApiV1Alpha2Tcproutes.Resource().GroupVersionKind(): - oldRes := &gatewayv1alpha2.TCPRoute{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*gatewayv1alpha2.TCPRouteSpec)), - } - modRes := &gatewayv1alpha2.TCPRoute{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*gatewayv1alpha2.TCPRouteSpec)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return sc.GatewayV1alpha2().TCPRoutes(orig.Namespace). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - case collections.K8SGatewayApiV1Alpha2Tlsroutes.Resource().GroupVersionKind(): - oldRes := &gatewayv1alpha2.TLSRoute{ - ObjectMeta: origMeta, - Spec: *(orig.Spec.(*gatewayv1alpha2.TLSRouteSpec)), - } - modRes := &gatewayv1alpha2.TLSRoute{ - ObjectMeta: modMeta, - Spec: *(mod.Spec.(*gatewayv1alpha2.TLSRouteSpec)), - } - patchBytes, err := genPatchBytes(oldRes, modRes, typ) - if err != nil { - return nil, err - } - return sc.GatewayV1alpha2().TLSRoutes(orig.Namespace). - Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) - default: - return nil, fmt.Errorf("unsupported type: %v", orig.GroupVersionKind) - } -} - -func delete(ic versionedclient.Interface, sc gatewayapiclient.Interface, typ config.GroupVersionKind, name, namespace string, resourceVersion *string) error { - var deleteOptions metav1.DeleteOptions - if resourceVersion != nil { - deleteOptions.Preconditions = &metav1.Preconditions{ResourceVersion: resourceVersion} - } - switch typ { - case collections.IstioExtensionsV1Alpha1Servicemetadatas.Resource().GroupVersionKind(): - return ic.ExtensionsV1alpha1().ServiceMetadatas(namespace).Delete(context.TODO(), name, deleteOptions) - case collections.IstioExtensionsV1Alpha1Servicenamemappings.Resource().GroupVersionKind(): - return ic.ExtensionsV1alpha1().ServiceNameMappings(namespace).Delete(context.TODO(), name, deleteOptions) - case collections.IstioExtensionsV1Alpha1Wasmplugins.Resource().GroupVersionKind(): - return ic.ExtensionsV1alpha1().WasmPlugins(namespace).Delete(context.TODO(), name, deleteOptions) - case collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().DestinationRules(namespace).Delete(context.TODO(), name, deleteOptions) - case collections.IstioNetworkingV1Alpha3Envoyfilters.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().EnvoyFilters(namespace).Delete(context.TODO(), name, deleteOptions) - case collections.IstioNetworkingV1Alpha3Gateways.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().Gateways(namespace).Delete(context.TODO(), name, deleteOptions) - case collections.IstioNetworkingV1Alpha3Serviceentries.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().ServiceEntries(namespace).Delete(context.TODO(), name, deleteOptions) - case collections.IstioNetworkingV1Alpha3Sidecars.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().Sidecars(namespace).Delete(context.TODO(), name, deleteOptions) - case collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().VirtualServices(namespace).Delete(context.TODO(), name, deleteOptions) - case collections.IstioNetworkingV1Alpha3Workloadentries.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().WorkloadEntries(namespace).Delete(context.TODO(), name, deleteOptions) - case collections.IstioNetworkingV1Alpha3Workloadgroups.Resource().GroupVersionKind(): - return ic.NetworkingV1alpha3().WorkloadGroups(namespace).Delete(context.TODO(), name, deleteOptions) - case collections.IstioNetworkingV1Beta1Proxyconfigs.Resource().GroupVersionKind(): - return ic.NetworkingV1beta1().ProxyConfigs(namespace).Delete(context.TODO(), name, deleteOptions) - case collections.IstioSecurityV1Beta1Authorizationpolicies.Resource().GroupVersionKind(): - return ic.SecurityV1beta1().AuthorizationPolicies(namespace).Delete(context.TODO(), name, deleteOptions) - case collections.IstioSecurityV1Beta1Peerauthentications.Resource().GroupVersionKind(): - return ic.SecurityV1beta1().PeerAuthentications(namespace).Delete(context.TODO(), name, deleteOptions) - case collections.IstioSecurityV1Beta1Requestauthentications.Resource().GroupVersionKind(): - return ic.SecurityV1beta1().RequestAuthentications(namespace).Delete(context.TODO(), name, deleteOptions) - case collections.IstioTelemetryV1Alpha1Telemetries.Resource().GroupVersionKind(): - return ic.TelemetryV1alpha1().Telemetries(namespace).Delete(context.TODO(), name, deleteOptions) - case collections.K8SGatewayApiV1Alpha2Gatewayclasses.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().GatewayClasses().Delete(context.TODO(), name, deleteOptions) - case collections.K8SGatewayApiV1Alpha2Gateways.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().Gateways(namespace).Delete(context.TODO(), name, deleteOptions) - case collections.K8SGatewayApiV1Alpha2Httproutes.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().HTTPRoutes(namespace).Delete(context.TODO(), name, deleteOptions) - case collections.K8SGatewayApiV1Alpha2Referencepolicies.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().ReferencePolicies(namespace).Delete(context.TODO(), name, deleteOptions) - case collections.K8SGatewayApiV1Alpha2Tcproutes.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().TCPRoutes(namespace).Delete(context.TODO(), name, deleteOptions) - case collections.K8SGatewayApiV1Alpha2Tlsroutes.Resource().GroupVersionKind(): - return sc.GatewayV1alpha2().TLSRoutes(namespace).Delete(context.TODO(), name, deleteOptions) - default: - return fmt.Errorf("unsupported type: %v", typ) - } -} - -var translationMap = map[config.GroupVersionKind]func(r runtime.Object) config.Config{ - collections.IstioExtensionsV1Alpha1Servicemetadatas.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*clientextensionsv1alpha1.ServiceMetadata) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioExtensionsV1Alpha1Servicemetadatas.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, - collections.IstioExtensionsV1Alpha1Servicenamemappings.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*clientextensionsv1alpha1.ServiceNameMapping) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioExtensionsV1Alpha1Servicenamemappings.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, - collections.IstioExtensionsV1Alpha1Wasmplugins.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*clientextensionsv1alpha1.WasmPlugin) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioExtensionsV1Alpha1Wasmplugins.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, - collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*clientnetworkingv1alpha3.DestinationRule) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, - collections.IstioNetworkingV1Alpha3Envoyfilters.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*clientnetworkingv1alpha3.EnvoyFilter) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Envoyfilters.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, - collections.IstioNetworkingV1Alpha3Gateways.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*clientnetworkingv1alpha3.Gateway) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Gateways.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, - collections.IstioNetworkingV1Alpha3Serviceentries.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*clientnetworkingv1alpha3.ServiceEntry) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Serviceentries.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, - collections.IstioNetworkingV1Alpha3Sidecars.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*clientnetworkingv1alpha3.Sidecar) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Sidecars.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, - collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*clientnetworkingv1alpha3.VirtualService) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, - collections.IstioNetworkingV1Alpha3Workloadentries.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*clientnetworkingv1alpha3.WorkloadEntry) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Workloadentries.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, - collections.IstioNetworkingV1Alpha3Workloadgroups.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*clientnetworkingv1alpha3.WorkloadGroup) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Workloadgroups.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, - collections.IstioNetworkingV1Beta1Proxyconfigs.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*clientnetworkingv1beta1.ProxyConfig) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Beta1Proxyconfigs.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, - collections.IstioSecurityV1Beta1Authorizationpolicies.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*clientsecurityv1beta1.AuthorizationPolicy) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioSecurityV1Beta1Authorizationpolicies.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, - collections.IstioSecurityV1Beta1Peerauthentications.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*clientsecurityv1beta1.PeerAuthentication) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioSecurityV1Beta1Peerauthentications.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, - collections.IstioSecurityV1Beta1Requestauthentications.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*clientsecurityv1beta1.RequestAuthentication) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioSecurityV1Beta1Requestauthentications.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, - collections.IstioTelemetryV1Alpha1Telemetries.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*clienttelemetryv1alpha1.Telemetry) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioTelemetryV1Alpha1Telemetries.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, - collections.K8SGatewayApiV1Alpha2Gatewayclasses.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*gatewayv1alpha2.GatewayClass) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.K8SGatewayApiV1Alpha2Gatewayclasses.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, - collections.K8SGatewayApiV1Alpha2Gateways.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*gatewayv1alpha2.Gateway) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.K8SGatewayApiV1Alpha2Gateways.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, - collections.K8SGatewayApiV1Alpha2Httproutes.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*gatewayv1alpha2.HTTPRoute) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.K8SGatewayApiV1Alpha2Httproutes.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, - collections.K8SGatewayApiV1Alpha2Referencepolicies.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*gatewayv1alpha2.ReferencePolicy) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.K8SGatewayApiV1Alpha2Referencepolicies.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - } - }, - collections.K8SGatewayApiV1Alpha2Tcproutes.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*gatewayv1alpha2.TCPRoute) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.K8SGatewayApiV1Alpha2Tcproutes.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, - collections.K8SGatewayApiV1Alpha2Tlsroutes.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*gatewayv1alpha2.TLSRoute) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.K8SGatewayApiV1Alpha2Tlsroutes.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, - collections.K8SAdmissionregistrationK8SIoV1Mutatingwebhookconfigurations.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*admissionregistrationv1.MutatingWebhookConfiguration) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.K8SAdmissionregistrationK8SIoV1Mutatingwebhookconfigurations.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: obj, - } - }, - collections.K8SApiextensionsK8SIoV1Customresourcedefinitions.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*apiextensionsv1.CustomResourceDefinition) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.K8SApiextensionsK8SIoV1Customresourcedefinitions.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - } - }, - collections.K8SAppsV1Deployments.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*appsv1.Deployment) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.K8SAppsV1Deployments.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - } - }, - collections.K8SCoreV1Configmaps.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*corev1.ConfigMap) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.K8SCoreV1Configmaps.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: obj, - } - }, - collections.K8SCoreV1Endpoints.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*corev1.Endpoints) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.K8SCoreV1Endpoints.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: obj, - } - }, - collections.K8SCoreV1Namespaces.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*corev1.Namespace) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.K8SCoreV1Namespaces.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - } - }, - collections.K8SCoreV1Nodes.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*corev1.Node) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.K8SCoreV1Nodes.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - } - }, - collections.K8SCoreV1Pods.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*corev1.Pod) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.K8SCoreV1Pods.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - } - }, - collections.K8SCoreV1Secrets.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*corev1.Secret) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.K8SCoreV1Secrets.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: obj, - } - }, - collections.K8SCoreV1Services.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*corev1.Service) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.K8SCoreV1Services.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - } - }, - collections.K8SExtensionsV1Beta1Ingresses.Resource().GroupVersionKind(): func(r runtime.Object) config.Config { - obj := r.(*extensionsv1beta1.Ingress) - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.K8SExtensionsV1Beta1Ingresses.Resource().GroupVersionKind(), - Name: obj.Name, - Namespace: obj.Namespace, - Labels: obj.Labels, - Annotations: obj.Annotations, - ResourceVersion: obj.ResourceVersion, - CreationTimestamp: obj.CreationTimestamp.Time, - OwnerReferences: obj.OwnerReferences, - UID: string(obj.UID), - Generation: obj.Generation, - }, - Spec: &obj.Spec, - Status: &obj.Status, - } - }, -} diff --git a/pilot/pkg/config/kube/gateway/conditions.go b/pilot/pkg/config/kube/gateway/conditions.go deleted file mode 100644 index 07933a554..000000000 --- a/pilot/pkg/config/kube/gateway/conditions.go +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "fmt" - "sort" -) - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - k8s "sigs.k8s.io/gateway-api/apis/v1alpha2" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model/kstatus" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -func createRouteStatus(gateways []routeParentReference, obj config.Config, current []k8s.RouteParentStatus, routeErr *ConfigError) []k8s.RouteParentStatus { - gws := make([]k8s.RouteParentStatus, 0, len(gateways)) - // Fill in all the gateways that are already present but not owned by us. This is non-trivial as there may be multiple - // gateway controllers that are exposing their status on the same route. We need to attempt to manage ours properly (including - // removing gateway references when they are removed), without mangling other Controller's status. - for _, r := range current { - if r.ControllerName != ControllerName { - // We don't own this status, so keep it around - gws = append(gws, r) - } - } - // Collect all of our unique parent references. There may be multiple when we have a route without section name, - // but reference a parent with multiple sections. - seen := map[k8s.ParentReference]routeParentReference{} - failedCount := map[k8s.ParentReference]int{} - for _, gw := range gateways { - // We will append it if it is our first occurrence, or the existing one has an error. This means - // if *any* section has no errors, we will declare Admitted - if gw.DeniedReason != nil { - failedCount[gw.OriginalReference]++ - } - if exist, f := seen[gw.OriginalReference]; !f || (exist.DeniedReason != nil && gw.DeniedReason == nil) { - seen[gw.OriginalReference] = gw - } - } - // Now we fill in all the ones we do own - // TODO look into also reporting ResolvedRefs; we should be gracefully dropping invalid backends instead - // of rejecting the whole thing. - for k, gw := range seen { - var condition metav1.Condition - if routeErr != nil { - condition = metav1.Condition{ - Type: string(k8s.RouteConditionAccepted), - Status: kstatus.StatusFalse, - ObservedGeneration: obj.Generation, - LastTransitionTime: metav1.Now(), - Reason: routeErr.Reason, - Message: routeErr.Message, - } - } else if gw.DeniedReason != nil { - err := gw.DeniedReason.Error() - if failedCount[k] > 1 { - err = fmt.Sprintf("failed to bind to %d parents, last error: %v", failedCount[k], gw.DeniedReason.Error()) - } - condition = metav1.Condition{ - Type: string(k8s.RouteConditionAccepted), - Status: kstatus.StatusFalse, - ObservedGeneration: obj.Generation, - LastTransitionTime: metav1.Now(), - Reason: InvalidParentRef, - Message: err, - } - } else { - condition = metav1.Condition{ - Type: string(k8s.RouteConditionAccepted), - Status: kstatus.StatusTrue, - ObservedGeneration: obj.Generation, - LastTransitionTime: metav1.Now(), - Reason: "RouteAdmitted", - Message: "Route was valid", - } - } - gws = append(gws, k8s.RouteParentStatus{ - ParentRef: gw.OriginalReference, - ControllerName: ControllerName, - Conditions: []metav1.Condition{condition}, - }) - } - // Ensure output is deterministic. - // TODO: will we fight over other controllers doing similar (but not identical) ordering? - sort.SliceStable(gws, func(i, j int) bool { - return parentRefString(gws[i].ParentRef) > parentRefString(gws[j].ParentRef) - }) - return gws -} - -type ConfigErrorReason = string - -const ( - // InvalidDestination indicates an issue with the destination - InvalidDestination ConfigErrorReason = "InvalidDestination" - // InvalidParentRef indicates we could not refer to the parent we request - InvalidParentRef ConfigErrorReason = "InvalidParentReference" - // InvalidFilter indicates an issue with the filters - InvalidFilter ConfigErrorReason = "InvalidFilter" - // InvalidTLS indicates an issue with TLS settings - InvalidTLS ConfigErrorReason = "InvalidTLS" - // InvalidConfiguration indicates a generic error for all other invalid configurations - InvalidConfiguration ConfigErrorReason = "InvalidConfiguration" -) - -// ConfigError represents an invalid configuration that will be reported back to the user. -type ConfigError struct { - Reason ConfigErrorReason - Message string -} - -type condition struct { - // reason defines the reason to report on success. Ignored if error is set - reason string - // message defines the message to report on success. Ignored if error is set - message string - // status defines the status to report on success. The inverse will be set if error is set - // If not set, will default to StatusTrue - status metav1.ConditionStatus - // error defines an error state; the reason and message will be replaced with that of the error and - // the status inverted - error *ConfigError - // setOnce, if enabled, will only set the condition if it is not yet present or set to this reason - setOnce string -} - -// setConditions sets the existingConditions with the new conditions -func setConditions(generation int64, existingConditions []metav1.Condition, conditions map[string]*condition) []metav1.Condition { - // Sort keys for deterministic ordering - condKeys := make([]string, 0, len(conditions)) - for k := range conditions { - condKeys = append(condKeys, k) - } - sort.Strings(condKeys) - for _, k := range condKeys { - cond := conditions[k] - setter := kstatus.UpdateConditionIfChanged - if cond.setOnce != "" { - setter = func(conditions []metav1.Condition, condition metav1.Condition) []metav1.Condition { - return kstatus.CreateCondition(conditions, condition, cond.setOnce) - } - } - // A condition can be "negative polarity" (ex: ListenerInvalid) or "positive polarity" (ex: - // ListenerValid), so in order to determine the status we should set each `condition` defines its - // default positive status. When there is an error, we will invert that. Example: If we have - // condition ListenerInvalid, the status will be set to StatusFalse. If an error is reported, it - // will be inverted to StatusTrue to indicate listeners are invalid. See - // https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties - // for more information - if cond.error != nil { - existingConditions = setter(existingConditions, metav1.Condition{ - Type: k, - Status: kstatus.InvertStatus(cond.status), - ObservedGeneration: generation, - LastTransitionTime: metav1.Now(), - Reason: cond.error.Reason, - Message: cond.error.Message, - }) - } else { - status := cond.status - if status == "" { - status = kstatus.StatusTrue - } - existingConditions = setter(existingConditions, metav1.Condition{ - Type: k, - Status: status, - ObservedGeneration: generation, - LastTransitionTime: metav1.Now(), - Reason: cond.reason, - Message: cond.message, - }) - } - } - return existingConditions -} - -func reportGatewayCondition(obj config.Config, conditions map[string]*condition) { - obj.Status.(*kstatus.WrappedStatus).Mutate(func(s config.Status) config.Status { - gs := s.(*k8s.GatewayStatus) - gs.Conditions = setConditions(obj.Generation, gs.Conditions, conditions) - return gs - }) -} - -func reportListenerAttachedRoutes(index int, obj config.Config, i int32) { - obj.Status.(*kstatus.WrappedStatus).Mutate(func(s config.Status) config.Status { - gs := s.(*k8s.GatewayStatus) - for index >= len(gs.Listeners) { - gs.Listeners = append(gs.Listeners, k8s.ListenerStatus{}) - } - status := gs.Listeners[index] - status.AttachedRoutes = i - gs.Listeners[index] = status - return gs - }) -} - -func reportListenerCondition(index int, l k8s.Listener, obj config.Config, conditions map[string]*condition) { - obj.Status.(*kstatus.WrappedStatus).Mutate(func(s config.Status) config.Status { - gs := s.(*k8s.GatewayStatus) - for index >= len(gs.Listeners) { - gs.Listeners = append(gs.Listeners, k8s.ListenerStatus{}) - } - cond := gs.Listeners[index].Conditions - gs.Listeners[index] = k8s.ListenerStatus{ - Name: l.Name, - AttachedRoutes: 0, // this will be reported later - SupportedKinds: generateSupportedKinds(l), - Conditions: setConditions(obj.Generation, cond, conditions), - } - return gs - }) -} - -func generateSupportedKinds(l k8s.Listener) []k8s.RouteGroupKind { - supported := []k8s.RouteGroupKind{} - switch l.Protocol { - case k8s.HTTPProtocolType, k8s.HTTPSProtocolType: - // Only terminate allowed, so its always HTTP - supported = []k8s.RouteGroupKind{{Group: (*k8s.Group)(StrPointer(gvk.HTTPRoute.Group)), Kind: k8s.Kind(gvk.HTTPRoute.Kind)}} - case k8s.TCPProtocolType: - supported = []k8s.RouteGroupKind{{Group: (*k8s.Group)(StrPointer(gvk.TCPRoute.Group)), Kind: k8s.Kind(gvk.TCPRoute.Kind)}} - case k8s.TLSProtocolType: - if l.TLS != nil && l.TLS.Mode != nil && *l.TLS.Mode == k8s.TLSModePassthrough { - supported = []k8s.RouteGroupKind{{Group: (*k8s.Group)(StrPointer(gvk.TLSRoute.Group)), Kind: k8s.Kind(gvk.TLSRoute.Kind)}} - } else { - supported = []k8s.RouteGroupKind{{Group: (*k8s.Group)(StrPointer(gvk.TCPRoute.Group)), Kind: k8s.Kind(gvk.TCPRoute.Kind)}} - } - // UDP route note support - } - if l.AllowedRoutes != nil && len(l.AllowedRoutes.Kinds) > 0 { - // We need to filter down to only ones we actually support - intersection := []k8s.RouteGroupKind{} - for _, s := range supported { - for _, kind := range l.AllowedRoutes.Kinds { - if s == kind { - intersection = append(intersection, s) - break - } - } - } - return intersection - } - return supported -} diff --git a/pilot/pkg/config/kube/gateway/controller.go b/pilot/pkg/config/kube/gateway/controller.go deleted file mode 100644 index d2148b2b1..000000000 --- a/pilot/pkg/config/kube/gateway/controller.go +++ /dev/null @@ -1,400 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "fmt" - "sync" - "time" -) - -import ( - "go.uber.org/atomic" - istiolog "istio.io/pkg/log" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - klabels "k8s.io/apimachinery/pkg/labels" - listerv1 "k8s.io/client-go/listers/core/v1" - "k8s.io/client-go/tools/cache" - k8s "sigs.k8s.io/gateway-api/apis/v1alpha2" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/crdclient" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model/credentials" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model/kstatus" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube/controller" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/status" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -var log = istiolog.RegisterScope("gateway", "gateway-api controller", 0) - -var ( - errUnsupportedOp = fmt.Errorf("unsupported operation: the gateway config store is a read-only view") - errUnsupportedType = fmt.Errorf("unsupported type: this operation only supports gateway and virtual service resource type") -) - -// Controller defines the controller for the gateway-api. The controller acts a bit different from most. -// Rather than watching the CRs directly, we depend on the existing model.ConfigStoreController which -// already watches all CRs. When there are updates, a new PushContext will be computed, which will eventually -// call Controller.Recompute(). Once this happens, we will inspect the current state of the world, and transform -// gateway-api types into Istio types (Gateway/VirtualService). Future calls to Get/List will return these -// Istio types. These are not stored in the cluster at all, and are purely internal; they can be seen on /debug/configz. -// During Recompute(), the status on all gateway-api types is also tracked. Once completed, if the status -// has changed at all, it is queued to asynchronously update the status of the object in Kubernetes. -type Controller struct { - // client for accessing Kubernetes - client kube.Client - // cache provides access to the underlying gateway-configs - cache model.ConfigStoreController - - // Gateway-api types reference namespace labels directly, so we need access to these - namespaceLister listerv1.NamespaceLister - namespaceInformer cache.SharedIndexInformer - namespaceHandler model.EventHandler - - // domain stores the cluster domain, typically cluster.local - domain string - - // state is our computed Istio resources. Access is guarded by stateMu. This is updated from Recompute(). - state OutputResources - stateMu sync.RWMutex - - // statusController controls the status working queue. Status will only be written if statusEnabled is true, which - // is only the case when we are the leader. - statusController *status.Controller - statusEnabled *atomic.Bool -} - -var _ model.GatewayController = &Controller{} - -func NewController(client kube.Client, c model.ConfigStoreController, options controller.Options) *Controller { - var ctl *status.Controller - - nsInformer := client.KubeInformer().Core().V1().Namespaces().Informer() - gatewayController := &Controller{ - client: client, - cache: c, - namespaceLister: client.KubeInformer().Core().V1().Namespaces().Lister(), - namespaceInformer: nsInformer, - domain: options.DomainSuffix, - statusController: ctl, - // Disabled by default, we will enable only if we win the leader election - statusEnabled: atomic.NewBool(false), - } - - nsInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - gatewayController.namespaceEvent(nil, obj) - }, - UpdateFunc: func(oldObj, newObj interface{}) { - gatewayController.namespaceEvent(oldObj, newObj) - }, - }) - - return gatewayController -} - -func (c *Controller) Schemas() collection.Schemas { - return collection.SchemasFor( - collections.IstioNetworkingV1Alpha3Virtualservices, - collections.IstioNetworkingV1Alpha3Gateways, - ) -} - -func (c *Controller) Get(typ config.GroupVersionKind, name, namespace string) *config.Config { - return nil -} - -func (c *Controller) List(typ config.GroupVersionKind, namespace string) ([]config.Config, error) { - if typ != gvk.Gateway && typ != gvk.VirtualService { - return nil, errUnsupportedType - } - - c.stateMu.RLock() - defer c.stateMu.RUnlock() - switch typ { - case gvk.Gateway: - return filterNamespace(c.state.Gateway, namespace), nil - case gvk.VirtualService: - return filterNamespace(c.state.VirtualService, namespace), nil - default: - return nil, errUnsupportedType - } -} - -func (c *Controller) SetStatusWrite(enabled bool, statusManager *status.Manager) { - c.statusEnabled.Store(enabled) - if enabled && features.EnableGatewayAPIStatus && statusManager != nil { - c.statusController = statusManager.CreateGenericController(func(status interface{}, context interface{}) status.GenerationProvider { - return &gatewayGeneration{context} - }) - } else { - c.statusController = nil - } -} - -// Recompute takes in a current snapshot of the gateway-api configs, and regenerates our internal state. -// Any status updates required will be enqueued as well. -func (c *Controller) Recompute(context model.GatewayContext) error { - t0 := time.Now() - defer func() { - log.Debugf("recompute complete in %v", time.Since(t0)) - }() - gatewayClass, err := c.cache.List(gvk.GatewayClass, metav1.NamespaceAll) - if err != nil { - return fmt.Errorf("failed to list type GatewayClass: %v", err) - } - gateway, err := c.cache.List(gvk.KubernetesGateway, metav1.NamespaceAll) - if err != nil { - return fmt.Errorf("failed to list type Gateway: %v", err) - } - httpRoute, err := c.cache.List(gvk.HTTPRoute, metav1.NamespaceAll) - if err != nil { - return fmt.Errorf("failed to list type HTTPRoute: %v", err) - } - tcpRoute, err := c.cache.List(gvk.TCPRoute, metav1.NamespaceAll) - if err != nil { - return fmt.Errorf("failed to list type TCPRoute: %v", err) - } - tlsRoute, err := c.cache.List(gvk.TLSRoute, metav1.NamespaceAll) - if err != nil { - return fmt.Errorf("failed to list type TLSRoute: %v", err) - } - referencePolicy, err := c.cache.List(gvk.ReferencePolicy, metav1.NamespaceAll) - if err != nil { - return fmt.Errorf("failed to list type BackendPolicy: %v", err) - } - - input := &KubernetesResources{ - GatewayClass: deepCopyStatus(gatewayClass), - Gateway: deepCopyStatus(gateway), - HTTPRoute: deepCopyStatus(httpRoute), - TCPRoute: deepCopyStatus(tcpRoute), - TLSRoute: deepCopyStatus(tlsRoute), - ReferencePolicy: referencePolicy, - Domain: c.domain, - Context: context, - } - - if !anyApisUsed(input) { - // Early exit for common case of no gateway-api used. - c.stateMu.Lock() - defer c.stateMu.Unlock() - // make sure we clear out the state, to handle the last gateway-api resource being removed - c.state = OutputResources{} - return nil - } - - nsl, err := c.namespaceLister.List(klabels.Everything()) - if err != nil { - return fmt.Errorf("failed to list type Namespaces: %v", err) - } - namespaces := map[string]*corev1.Namespace{} - for _, ns := range nsl { - namespaces[ns.Name] = ns - } - input.Namespaces = namespaces - output := convertResources(input) - - // Handle all status updates - c.QueueStatusUpdates(input) - - c.stateMu.Lock() - defer c.stateMu.Unlock() - c.state = output - return nil -} - -func (c *Controller) QueueStatusUpdates(r *KubernetesResources) { - c.handleStatusUpdates(r.GatewayClass) - c.handleStatusUpdates(r.Gateway) - c.handleStatusUpdates(r.HTTPRoute) - c.handleStatusUpdates(r.TCPRoute) - c.handleStatusUpdates(r.TLSRoute) -} - -func (c *Controller) handleStatusUpdates(configs []config.Config) { - if c.statusController == nil || !c.statusEnabled.Load() { - return - } - for _, cfg := range configs { - ws := cfg.Status.(*kstatus.WrappedStatus) - if ws.Dirty { - res := status.ResourceFromModelConfig(cfg) - c.statusController.EnqueueStatusUpdateResource(ws.Unwrap(), res) - } - } -} - -func (c *Controller) Create(config config.Config) (revision string, err error) { - return "", errUnsupportedOp -} - -func (c *Controller) Update(config config.Config) (newRevision string, err error) { - return "", errUnsupportedOp -} - -func (c *Controller) UpdateStatus(config config.Config) (newRevision string, err error) { - return "", errUnsupportedOp -} - -func (c *Controller) Patch(orig config.Config, patchFn config.PatchFunc) (string, error) { - return "", errUnsupportedOp -} - -func (c *Controller) Delete(typ config.GroupVersionKind, name, namespace string, _ *string) error { - return errUnsupportedOp -} - -func (c *Controller) RegisterEventHandler(typ config.GroupVersionKind, handler model.EventHandler) { - switch typ { - case gvk.Namespace: - c.namespaceHandler = handler - } - // For all other types, do nothing as c.cache has been registered -} - -func (c *Controller) Run(stop <-chan struct{}) { - go func() { - if crdclient.WaitForCRD(gvk.GatewayClass, stop) { - gcc := NewClassController(c.client) - c.client.RunAndWait(stop) - gcc.Run(stop) - } - }() - cache.WaitForCacheSync(stop, c.namespaceInformer.HasSynced) -} - -func (c *Controller) SetWatchErrorHandler(handler func(r *cache.Reflector, err error)) error { - return c.cache.SetWatchErrorHandler(handler) -} - -func (c *Controller) HasSynced() bool { - return c.cache.HasSynced() -} - -func (c *Controller) SecretAllowed(resourceName string, namespace string) bool { - p, err := credentials.ParseResourceName(resourceName, "", "", "") - if err != nil { - log.Warnf("failed to parse resource name %q: %v", resourceName, err) - return false - } - from := Reference{Kind: gvk.KubernetesGateway, Namespace: k8s.Namespace(namespace)} - to := Reference{Kind: gvk.Secret, Namespace: k8s.Namespace(p.Namespace)} - allow := c.state.AllowedReferences[from][to] - if allow == nil { - return false - } - return allow.AllowAll || allow.AllowedNames.Contains(p.Name) -} - -// namespaceEvent handles a namespace add/update. Gateway's can select routes by label, so we need to handle -// when the labels change. -// Note: we don't handle delete as a delete would also clean up any relevant gateway-api types which will -// trigger its own event. -func (c *Controller) namespaceEvent(oldObj interface{}, newObj interface{}) { - // First, find all the label keys on the old/new namespace. We include NamespaceNameLabel - // since we have special logic to always allow this on namespace. - touchedNamespaceLabels := sets.New(NamespaceNameLabel) - touchedNamespaceLabels.InsertAll(getLabelKeys(oldObj)...) - touchedNamespaceLabels.InsertAll(getLabelKeys(newObj)...) - - // Next, we find all keys our Gateways actually reference. - c.stateMu.RLock() - intersection := touchedNamespaceLabels.Intersection(c.state.ReferencedNamespaceKeys) - c.stateMu.RUnlock() - - // If there was any overlap, then a relevant namespace label may have changed, and we trigger a - // push A more exact check could actually determine if the label selection result actually changed. - // However, this is a much simpler approach that is likely to scale well enough for now. - if !intersection.IsEmpty() && c.namespaceHandler != nil { - log.Debugf("namespace labels changed, triggering namespace handler: %v", intersection.UnsortedList()) - c.namespaceHandler(config.Config{}, config.Config{}, model.EventUpdate) - } -} - -// getLabelKeys extracts all label keys from a namespace object. -func getLabelKeys(obj interface{}) []string { - if obj == nil { - return nil - } - ns, ok := obj.(*corev1.Namespace) - if !ok { - tombstone, ok := obj.(cache.DeletedFinalStateUnknown) - if !ok { - return nil - } - ns, ok = tombstone.Obj.(*corev1.Namespace) - if !ok { - return nil - } - } - keys := make([]string, 0, len(ns.Labels)) - for k := range ns.Labels { - keys = append(keys, k) - } - return keys -} - -// deepCopyStatus creates a copy of all configs, with a copy of the status field that we can mutate. -// This allows our functions to call Status.Mutate, and then we can later persist all changes into the -// API server. -func deepCopyStatus(configs []config.Config) []config.Config { - res := make([]config.Config, 0, len(configs)) - for _, c := range configs { - nc := config.Config{ - Meta: c.Meta, - Spec: c.Spec, - Status: kstatus.Wrap(c.Status), - } - res = append(res, nc) - } - return res -} - -// filterNamespace allows filtering out configs to only a specific namespace. This allows implementing the -// List call which can specify a specific namespace. -func filterNamespace(cfgs []config.Config, namespace string) []config.Config { - if namespace == metav1.NamespaceAll { - return cfgs - } - filtered := make([]config.Config, 0, len(cfgs)) - for _, c := range cfgs { - if c.Namespace == namespace { - filtered = append(filtered, c) - } - } - return filtered -} - -// anyApisUsed determines if there are any gateway-api resources created at all. If not, we can -// short circuit all processing to avoid excessive work. -func anyApisUsed(input *KubernetesResources) bool { - return len(input.GatewayClass) > 0 || - len(input.Gateway) > 0 || - len(input.HTTPRoute) > 0 || - len(input.TCPRoute) > 0 || - len(input.TLSRoute) > 0 || - len(input.ReferencePolicy) > 0 -} diff --git a/pilot/pkg/config/kube/gateway/controller_test.go b/pilot/pkg/config/kube/gateway/controller_test.go deleted file mode 100644 index 0ef153548..000000000 --- a/pilot/pkg/config/kube/gateway/controller_test.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "testing" -) - -import ( - . "github.com/onsi/gomega" - networking "istio.io/api/networking/v1alpha3" - k8s "sigs.k8s.io/gateway-api/apis/v1alpha2" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube/controller" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -var ( - gatewayClassSpec = &k8s.GatewayClassSpec{ - ControllerName: ControllerName, - } - gatewaySpec = &k8s.GatewaySpec{ - GatewayClassName: "gwclass", - Listeners: []k8s.Listener{ - { - Name: "default", - Port: 9009, - Protocol: "HTTP", - AllowedRoutes: &k8s.AllowedRoutes{Namespaces: &k8s.RouteNamespaces{From: func() *k8s.FromNamespaces { x := k8s.NamespacesFromAll; return &x }()}}, - }, - }, - } - httpRouteSpec = &k8s.HTTPRouteSpec{ - CommonRouteSpec: k8s.CommonRouteSpec{ParentRefs: []k8s.ParentReference{{ - Name: "gwspec", - }}}, - Hostnames: []k8s.Hostname{"test.cluster.local"}, - } - - expectedgw = &networking.Gateway{ - Servers: []*networking.Server{ - { - Port: &networking.Port{ - Number: 9009, - Name: "default", - Protocol: "HTTP", - }, - Hosts: []string{"*/*"}, - }, - }, - } - - expectedvs = &networking.VirtualService{ - Hosts: []string{ - "test.cluster.local", - }, - Gateways: []string{ - "ns1/gwspec-istio-autogenerated-k8s-gateway-default", - }, - Http: []*networking.HTTPRoute{}, - } -) - -func TestListInvalidGroupVersionKind(t *testing.T) { - g := NewWithT(t) - clientSet := kube.NewFakeClient() - store := memory.NewController(memory.Make(collections.All)) - controller := NewController(clientSet, store, controller.Options{}) - - typ := config.GroupVersionKind{Kind: "wrong-kind"} - c, err := controller.List(typ, "ns1") - g.Expect(c).To(HaveLen(0)) - g.Expect(err).To(HaveOccurred()) -} - -func TestListGatewayResourceType(t *testing.T) { - g := NewWithT(t) - - clientSet := kube.NewFakeClient() - store := memory.NewController(memory.Make(collections.All)) - controller := NewController(clientSet, store, controller.Options{}) - - store.Create(config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.GatewayClass, - Name: "gwclass", - Namespace: "ns1", - }, - Spec: gatewayClassSpec, - }) - if _, err := store.Create(config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.KubernetesGateway, - Name: "gwspec", - Namespace: "ns1", - }, - Spec: gatewaySpec, - }); err != nil { - t.Fatal(err) - } - store.Create(config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.HTTPRoute, - Name: "http-route", - Namespace: "ns1", - }, - Spec: httpRouteSpec, - }) - - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - g.Expect(controller.Recompute(model.NewGatewayContext(cg.PushContext()))).ToNot(HaveOccurred()) - cfg, err := controller.List(gvk.Gateway, "ns1") - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(cfg).To(HaveLen(1)) - for _, c := range cfg { - g.Expect(c.GroupVersionKind).To(Equal(gvk.Gateway)) - g.Expect(c.Name).To(Equal("gwspec" + "-" + constants.KubernetesGatewayName + "-default")) - g.Expect(c.Namespace).To(Equal("ns1")) - g.Expect(c.Spec).To(Equal(expectedgw)) - } -} - -func TestListVirtualServiceResourceType(t *testing.T) { - g := NewWithT(t) - - clientSet := kube.NewFakeClient() - store := memory.NewController(memory.Make(collections.All)) - controller := NewController(clientSet, store, controller.Options{}) - - store.Create(config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.GatewayClass, - Name: "gwclass", - Namespace: "ns1", - }, - Spec: gatewayClassSpec, - }) - store.Create(config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.KubernetesGateway, - Name: "gwspec", - Namespace: "ns1", - }, - Spec: gatewaySpec, - }) - store.Create(config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.HTTPRoute, - Name: "http-route", - Namespace: "ns1", - }, - Spec: httpRouteSpec, - }) - - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - g.Expect(controller.Recompute(model.NewGatewayContext(cg.PushContext()))).ToNot(HaveOccurred()) - cfg, err := controller.List(gvk.VirtualService, "ns1") - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(cfg).To(HaveLen(1)) - for _, c := range cfg { - g.Expect(c.GroupVersionKind).To(Equal(gvk.VirtualService)) - g.Expect(c.Name).To(Equal("http-route-0-" + constants.KubernetesGatewayName)) - g.Expect(c.Namespace).To(Equal("ns1")) - g.Expect(c.Spec).To(Equal(expectedvs)) - } -} diff --git a/pilot/pkg/config/kube/gateway/conversion.go b/pilot/pkg/config/kube/gateway/conversion.go deleted file mode 100644 index 016317615..000000000 --- a/pilot/pkg/config/kube/gateway/conversion.go +++ /dev/null @@ -1,1617 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "fmt" - "sort" - "strings" -) - -import ( - "istio.io/api/label" - istio "istio.io/api/networking/v1alpha3" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - klabels "k8s.io/apimachinery/pkg/labels" - k8s "sigs.k8s.io/gateway-api/apis/v1alpha2" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model/credentials" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model/kstatus" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -const ( - DefaultClassName = "istio" - ControllerName = "istio.io/gateway-controller" - gatewayAliasForAnnotationKey = "gateway.istio.io/alias-for" -) - -// KubernetesResources stores all inputs to our conversion -type KubernetesResources struct { - GatewayClass []config.Config - Gateway []config.Config - HTTPRoute []config.Config - TCPRoute []config.Config - TLSRoute []config.Config - ReferencePolicy []config.Config - // Namespaces stores all namespace in the cluster, keyed by name - Namespaces map[string]*corev1.Namespace - - // Domain for the cluster. Typically, cluster.local - Domain string - Context model.GatewayContext -} - -// OutputResources stores all outputs of our conversion -type OutputResources struct { - Gateway []config.Config - VirtualService []config.Config - // AllowedReferences stores all allowed references, from Reference -> to Reference(s) - AllowedReferences map[Reference]map[Reference]*AllowedReferences - // ReferencedNamespaceKeys stores the label key of all namespace selections. This allows us to quickly - // determine if a namespace update could have impacted any Gateways. See namespaceEvent. - ReferencedNamespaceKeys sets.Set -} - -// Reference stores a reference to a namespaced GVK, as used by ReferencePolicy -type Reference struct { - Kind config.GroupVersionKind - Namespace k8s.Namespace -} - -// convertResources is the top level entrypoint to our conversion logic, computing the full state based -// on KubernetesResources inputs. -func convertResources(r *KubernetesResources) OutputResources { - // sort HTTPRoutes by creation timestamp and namespace/name - sort.Slice(r.HTTPRoute, func(i, j int) bool { - if r.HTTPRoute[i].CreationTimestamp.Equal(r.HTTPRoute[j].CreationTimestamp) { - in := r.HTTPRoute[i].Namespace + "/" + r.HTTPRoute[i].Name - jn := r.HTTPRoute[j].Namespace + "/" + r.HTTPRoute[j].Name - return in < jn - } - return r.HTTPRoute[i].CreationTimestamp.Before(r.HTTPRoute[j].CreationTimestamp) - }) - result := OutputResources{} - gw, gwMap, nsReferences := convertGateways(r) - result.Gateway = gw - result.VirtualService = convertVirtualService(r, gwMap) - - // Once we have gone through all route computation, we will know how many routes bound to each gateway. - // Report this in the status. - for _, dm := range gwMap { - for _, pri := range dm { - if pri.ReportAttachedRoutes != nil { - pri.ReportAttachedRoutes() - } - } - } - result.AllowedReferences = convertReferencePolicies(r) - result.ReferencedNamespaceKeys = nsReferences - return result -} - -type AllowedReferences struct { - AllowAll bool - AllowedNames sets.Set -} - -// convertReferencePolicies extracts all ReferencePolicy into an easily accessibly index. -// The currently supported references are: -// * Gateway -> Secret -func convertReferencePolicies(r *KubernetesResources) map[Reference]map[Reference]*AllowedReferences { - res := map[Reference]map[Reference]*AllowedReferences{} - for _, obj := range r.ReferencePolicy { - rp := obj.Spec.(*k8s.ReferencePolicySpec) - for _, from := range rp.From { - fromKey := Reference{ - Namespace: from.Namespace, - } - if string(from.Group) == gvk.KubernetesGateway.Group && string(from.Kind) == gvk.KubernetesGateway.Kind { - fromKey.Kind = gvk.KubernetesGateway - } else { - // Not supported type. Not an error; may be for another controller - continue - } - for _, to := range rp.To { - toKey := Reference{ - Namespace: k8s.Namespace(obj.Namespace), - } - if to.Group == "" && string(to.Kind) == gvk.Secret.Kind { - toKey.Kind = gvk.Secret - } else { - // Not supported type. Not an error; may be for another controller - continue - } - if _, f := res[fromKey]; !f { - res[fromKey] = map[Reference]*AllowedReferences{} - } - if _, f := res[fromKey][toKey]; !f { - res[fromKey][toKey] = &AllowedReferences{ - AllowedNames: sets.New(), - } - } - if to.Name != nil { - res[fromKey][toKey].AllowedNames.Insert(string(*to.Name)) - } else { - res[fromKey][toKey].AllowAll = true - } - } - } - } - return res -} - -// convertVirtualService takes all xRoute types and generates corresponding VirtualServices. -func convertVirtualService(r *KubernetesResources, gatewayMap map[parentKey]map[k8s.SectionName]*parentInfo) []config.Config { - result := []config.Config{} - for _, obj := range r.TCPRoute { - if vsConfig := buildTCPVirtualService(obj, gatewayMap, r.Domain); vsConfig != nil { - result = append(result, *vsConfig) - } - } - - for _, obj := range r.TLSRoute { - result = append(result, buildTLSVirtualService(obj, gatewayMap, r.Domain)...) - } - - // for gateway routes, build one VS per gateway+host - gatewayRoutes := make(map[string]map[string]*config.Config) - // for mesh routes, build one VS per namespace+host - meshRoutes := make(map[string]map[string]*config.Config) - for _, obj := range r.HTTPRoute { - buildHTTPVirtualServices(obj, gatewayMap, r.Domain, gatewayRoutes, meshRoutes) - } - for _, vsByHost := range gatewayRoutes { - for _, vsConfig := range vsByHost { - result = append(result, *vsConfig) - } - } - for _, vsByHost := range meshRoutes { - for _, vsConfig := range vsByHost { - result = append(result, *vsConfig) - } - } - return result -} - -func buildHTTPVirtualServices(obj config.Config, gateways map[parentKey]map[k8s.SectionName]*parentInfo, domain string, - gatewayRoutes map[string]map[string]*config.Config, meshRoutes map[string]map[string]*config.Config) { - route := obj.Spec.(*k8s.HTTPRouteSpec) - for _, r := range route.Rules { - if len(r.Matches) > 1 { - // if a rule has multiple matches, make a deep copy to avoid impacting the obj when splitting the rule - route = route.DeepCopy() - break - } - } - ns := obj.Namespace - parentRefs := extractParentReferenceInfo(gateways, route.ParentRefs, route.Hostnames, gvk.HTTPRoute, ns) - - reportError := func(routeErr *ConfigError) { - obj.Status.(*kstatus.WrappedStatus).Mutate(func(s config.Status) config.Status { - rs := s.(*k8s.HTTPRouteStatus) - rs.Parents = createRouteStatus(parentRefs, obj, rs.Parents, routeErr) - return rs - }) - } - - httproutes := []*istio.HTTPRoute{} - hosts := hostnameToStringList(route.Hostnames) - for i := 0; i < len(route.Rules); i++ { - r := route.Rules[i] - if len(r.Matches) > 1 { - // split the rule to make sure each rule has up to one match - for _, match := range r.Matches { - splitRule := r - splitRule.Matches = []k8s.HTTPRouteMatch{match} - route.Rules = append(route.Rules, splitRule) - } - continue - } - // TODO: implement rewrite, timeout, mirror, corspolicy, retries - vs := &istio.HTTPRoute{} - for _, match := range r.Matches { - uri, err := createURIMatch(match) - if err != nil { - reportError(err) - return - } - headers, err := createHeadersMatch(match) - if err != nil { - reportError(err) - return - } - qp, err := createQueryParamsMatch(match) - if err != nil { - reportError(err) - return - } - method, err := createMethodMatch(match) - if err != nil { - reportError(err) - return - } - vs.Match = append(vs.Match, &istio.HTTPMatchRequest{ - Uri: uri, - Headers: headers, - QueryParams: qp, - Method: method, - }) - } - for _, filter := range r.Filters { - switch filter.Type { - case k8s.HTTPRouteFilterRequestHeaderModifier: - vs.Headers = createHeadersFilter(filter.RequestHeaderModifier) - case k8s.HTTPRouteFilterRequestRedirect: - vs.Redirect = createRedirectFilter(filter.RequestRedirect) - case k8s.HTTPRouteFilterRequestMirror: - mirror, err := createMirrorFilter(filter.RequestMirror, ns, domain) - if err != nil { - reportError(err) - return - } - vs.Mirror = mirror - default: - reportError(&ConfigError{ - Reason: InvalidFilter, - Message: fmt.Sprintf("unsupported filter type %q", filter.Type), - }) - return - } - } - - zero := true - for _, w := range r.BackendRefs { - if w.Weight == nil || (w.Weight != nil && int(*w.Weight) != 0) { - zero = false - break - } - } - if zero && vs.Redirect == nil { - // The spec requires us to 503 when there are no >0 weight backends - vs.Fault = &istio.HTTPFaultInjection{Abort: &istio.HTTPFaultInjection_Abort{ - Percentage: &istio.Percent{ - Value: 100, - }, - ErrorType: &istio.HTTPFaultInjection_Abort_HttpStatus{ - HttpStatus: 503, - }, - }} - } - - route, err := buildHTTPDestination(r.BackendRefs, ns, domain, zero) - if err != nil { - reportError(err) - return - } - vs.Route = route - - httproutes = append(httproutes, vs) - } - reportError(nil) - - gatewayNames := referencesToInternalNames(parentRefs) - if len(gatewayNames) == 0 { - return - } - count := 0 - for _, gw := range gatewayNames { - // for gateway routes, build one VS per gateway+host - routeMap := gatewayRoutes - routeKey := gw - if gw == "mesh" { - // for mesh routes, build one VS per namespace+host - routeMap = meshRoutes - routeKey = ns - } - if _, f := routeMap[routeKey]; !f { - routeMap[routeKey] = make(map[string]*config.Config) - } - // Create one VS per hostname with a single hostname. - // This ensures we can treat each hostname independently, as the spec requires - for _, host := range hosts { - if cfg := routeMap[routeKey][host]; cfg != nil { - // merge http routes - vs := cfg.Spec.(*istio.VirtualService) - vs.Http = append(vs.Http, httproutes...) - } else { - name := fmt.Sprintf("%s-%d-%s", obj.Name, count, constants.KubernetesGatewayName) - routeMap[routeKey][host] = &config.Config{ - Meta: config.Meta{ - CreationTimestamp: obj.CreationTimestamp, - GroupVersionKind: gvk.VirtualService, - Name: name, - Annotations: routeMeta(obj), - Namespace: ns, - Domain: domain, - }, - Spec: &istio.VirtualService{ - Hosts: []string{host}, - Gateways: []string{gw}, - Http: httproutes, - }, - } - count++ - } - } - } - for _, vsByHost := range gatewayRoutes { - for _, cfg := range vsByHost { - vs := cfg.Spec.(*istio.VirtualService) - sortHTTPRoutes(vs.Http) - } - } - for _, vsByHost := range meshRoutes { - for _, cfg := range vsByHost { - vs := cfg.Spec.(*istio.VirtualService) - sortHTTPRoutes(vs.Http) - } - } -} - -func routeMeta(obj config.Config) map[string]string { - m := parentMeta(obj, nil) - m[constants.InternalRouteSemantics] = constants.RouteSemanticsGateway - return m -} - -// sortHTTPRoutes sorts generated vs routes to meet gateway-api requirements -// see https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.HTTPRouteRule -func sortHTTPRoutes(routes []*istio.HTTPRoute) { - sort.SliceStable(routes, func(i, j int) bool { - if len(routes[i].Match) == 0 { - return len(routes[j].Match) != 0 - } - if len(routes[j].Match) == 0 { - return false - } - m1, m2 := routes[i].Match[0], routes[j].Match[0] - len1, len2 := getURILength(m1), getURILength(m2) - if len1 == len2 { - if len(m1.Headers) == len(m2.Headers) { - return len(m1.QueryParams) > len(m2.QueryParams) - } - return len(m1.Headers) > len(m2.Headers) - } - return len1 > len2 - }) -} - -func getURILength(match *istio.HTTPMatchRequest) int { - if match.Uri == nil { - return 0 - } - switch match.Uri.MatchType.(type) { - case *istio.StringMatch_Prefix: - return len(match.Uri.GetPrefix()) - case *istio.StringMatch_Exact: - return len(match.Uri.GetExact()) - case *istio.StringMatch_Regex: - return len(match.Uri.GetRegex()) - } - // should not happen - return -1 -} - -func parentMeta(obj config.Config, sectionName *k8s.SectionName) map[string]string { - name := fmt.Sprintf("%s/%s.%s", obj.GroupVersionKind.Kind, obj.Name, obj.Namespace) - if sectionName != nil { - name = fmt.Sprintf("%s/%s/%s.%s", obj.GroupVersionKind.Kind, obj.Name, *sectionName, obj.Namespace) - } - return map[string]string{ - constants.InternalParentName: name, - } -} - -func hostnameToStringList(h []k8s.Hostname) []string { - // In the Istio API, empty hostname is not allowed. In the Kubernetes API hosts means "any" - if len(h) == 0 { - return []string{"*"} - } - res := make([]string, 0, len(h)) - for _, i := range h { - res = append(res, string(i)) - } - return res -} - -func toInternalParentReference(p k8s.ParentReference, localNamespace string) (parentKey, error) { - empty := parentKey{} - grp := defaultIfNil((*string)(p.Group), gvk.KubernetesGateway.Group) - kind := defaultIfNil((*string)(p.Kind), gvk.KubernetesGateway.Kind) - var ik config.GroupVersionKind - var ns string - // Currently supported types are Gateway and Mesh - if kind == gvk.KubernetesGateway.Kind && grp == gvk.KubernetesGateway.Group { - // Unset namespace means "same namespace" - ns = defaultIfNil((*string)(p.Namespace), localNamespace) - ik = gvk.KubernetesGateway - } else if kind == meshGVK.Kind && grp == meshGVK.Group { - ik = meshGVK - } else { - return empty, fmt.Errorf("unsupported parentKey: %v/%v", grp, kind) - } - return parentKey{ - Kind: ik, - Name: string(p.Name), - Namespace: ns, - }, nil -} - -func referenceAllowed(p *parentInfo, routeKind config.GroupVersionKind, parentKind config.GroupVersionKind, hostnames []k8s.Hostname, namespace string) error { - // First check the hostnames are a match. This is a bi-directional wildcard match. Only one route - // hostname must match for it to be allowed (but the others will be filtered at runtime) - // If either is empty its treated as a wildcard which always matches - - if len(hostnames) == 0 { - hostnames = []k8s.Hostname{"*"} - } - if len(p.Hostnames) > 0 { - // TODO: the spec actually has a label match, not a string match. That is, *.com does not match *.apple.com - // We are doing a string match here - matched := false - hostMatched := false - for _, routeHostname := range hostnames { - for _, parentHostNamespace := range p.Hostnames { - spl := strings.Split(parentHostNamespace, "/") - parentNamespace, parentHostname := spl[0], spl[1] - hostnameMatch := host.Name(parentHostname).Matches(host.Name(routeHostname)) - namespaceMatch := parentNamespace == "*" || parentNamespace == namespace - hostMatched = hostMatched || hostnameMatch - if hostnameMatch && namespaceMatch { - matched = true - break - } - } - } - if !matched { - if hostMatched { - return fmt.Errorf("hostnames matched parent hostname %q, but namespace %q is not allowed by the parent", p.OriginalHostname, namespace) - } - return fmt.Errorf("no hostnames matched parent hostname %q", p.OriginalHostname) - } - } - // Also make sure this route kind is allowed - if len(p.AllowedKinds) > 0 { - matched := false - for _, ak := range p.AllowedKinds { - if string(ak.Kind) == routeKind.Kind && defaultIfNil((*string)(ak.Group), gvk.GatewayClass.Group) == routeKind.Group { - matched = true - break - } - } - if !matched { - return fmt.Errorf("kind %v is not allowed", routeKind) - } - } - - if parentKind == meshGVK { - for _, h := range hostnames { - if h == "*" { - return fmt.Errorf("mesh requires hostname to be set") - } - } - } - return nil -} - -func extractParentReferenceInfo(gateways map[parentKey]map[k8s.SectionName]*parentInfo, routeRefs []k8s.ParentReference, - hostnames []k8s.Hostname, kind config.GroupVersionKind, localNamespace string) []routeParentReference { - parentRefs := []routeParentReference{} - for _, ref := range routeRefs { - ir, err := toInternalParentReference(ref, localNamespace) - if err != nil { - // Cannot handle the reference. Maybe it is for another controller, so we just ignore it - continue - } - appendParent := func(pr *parentInfo, pk parentKey) { - rpi := routeParentReference{ - InternalName: pr.InternalName, - DeniedReason: referenceAllowed(pr, kind, pk.Kind, hostnames, localNamespace), - OriginalReference: ref, - } - if rpi.DeniedReason == nil { - // Record that we were able to bind to the parent - pr.AttachedRoutes++ - } - parentRefs = append(parentRefs, rpi) - } - if ref.SectionName != nil { - // We are selecting a specific section, so attach just that section - if pr, f := gateways[ir][*ref.SectionName]; f { - appendParent(pr, ir) - } - } else { - // no section name set, match all sections - for _, pr := range gateways[ir] { - appendParent(pr, ir) - } - } - } - return parentRefs -} - -func buildTCPVirtualService(obj config.Config, gateways map[parentKey]map[k8s.SectionName]*parentInfo, domain string) *config.Config { - route := obj.Spec.(*k8s.TCPRouteSpec) - - parentRefs := extractParentReferenceInfo(gateways, route.ParentRefs, nil, gvk.TCPRoute, obj.Namespace) - - reportError := func(routeErr *ConfigError) { - obj.Status.(*kstatus.WrappedStatus).Mutate(func(s config.Status) config.Status { - rs := s.(*k8s.TCPRouteStatus) - rs.Parents = createRouteStatus(parentRefs, obj, rs.Parents, routeErr) - return rs - }) - } - gatewayNames := referencesToInternalNames(parentRefs) - if len(gatewayNames) == 0 { - reportError(nil) - return nil - } - - routes := []*istio.TCPRoute{} - for _, r := range route.Rules { - route, err := buildTCPDestination(r.BackendRefs, obj.Namespace, domain) - if err != nil { - reportError(err) - return nil - } - ir := &istio.TCPRoute{ - Route: route, - } - routes = append(routes, ir) - } - - reportError(nil) - vsConfig := config.Config{ - Meta: config.Meta{ - CreationTimestamp: obj.CreationTimestamp, - GroupVersionKind: gvk.VirtualService, - Name: fmt.Sprintf("%s-tcp-%s", obj.Name, constants.KubernetesGatewayName), - Annotations: routeMeta(obj), - Namespace: obj.Namespace, - Domain: domain, - }, - Spec: &istio.VirtualService{ - // We can use wildcard here since each listener can have at most one route bound to it, so we have - // a single VS per Gateway. - Hosts: []string{"*"}, - Gateways: gatewayNames, - Tcp: routes, - }, - } - return &vsConfig -} - -func buildTLSVirtualService(obj config.Config, gateways map[parentKey]map[k8s.SectionName]*parentInfo, domain string) []config.Config { - route := obj.Spec.(*k8s.TLSRouteSpec) - - parentRefs := extractParentReferenceInfo(gateways, route.ParentRefs, nil, gvk.TLSRoute, obj.Namespace) - - reportError := func(routeErr *ConfigError) { - obj.Status.(*kstatus.WrappedStatus).Mutate(func(s config.Status) config.Status { - rs := s.(*k8s.TLSRouteStatus) - rs.Parents = createRouteStatus(parentRefs, obj, rs.Parents, routeErr) - return rs - }) - } - - routes := []*istio.TLSRoute{} - for _, r := range route.Rules { - dest, err := buildTCPDestination(r.BackendRefs, obj.Namespace, domain) - if err != nil { - reportError(err) - return nil - } - if len(dest) == 0 { - return nil - } - ir := &istio.TLSRoute{ - Match: buildTLSMatch(route.Hostnames), - Route: dest, - } - routes = append(routes, ir) - } - - reportError(nil) - gatewayNames := referencesToInternalNames(parentRefs) - if len(gatewayNames) == 0 { - // TODO we need to properly return not admitted here - return nil - } - configs := make([]config.Config, 0, len(route.Hostnames)) - for i, host := range hostnameToStringList(route.Hostnames) { - name := fmt.Sprintf("%s-tls-%d-%s", obj.Name, i, constants.KubernetesGatewayName) - // Create one VS per hostname with a single hostname. - // This ensures we can treat each hostname independently, as the spec requires - vsConfig := config.Config{ - Meta: config.Meta{ - CreationTimestamp: obj.CreationTimestamp, - GroupVersionKind: gvk.VirtualService, - Name: name, - Annotations: routeMeta(obj), - Namespace: obj.Namespace, - Domain: domain, - }, - Spec: &istio.VirtualService{ - Hosts: []string{host}, - Gateways: gatewayNames, - Tls: routes, - }, - } - configs = append(configs, vsConfig) - } - return configs -} - -func buildTCPDestination(forwardTo []k8s.BackendRef, ns, domain string) ([]*istio.RouteDestination, *ConfigError) { - if forwardTo == nil { - return nil, nil - } - - weights := []int{} - action := []k8s.BackendRef{} - for i, w := range forwardTo { - wt := 1 - if w.Weight != nil { - wt = int(*w.Weight) - } - if wt == 0 { - continue - } - action = append(action, forwardTo[i]) - weights = append(weights, wt) - } - weights = standardizeWeights(weights) - res := []*istio.RouteDestination{} - for i, fwd := range action { - dst, err := buildDestination(fwd, ns, domain) - if err != nil { - return nil, err - } - res = append(res, &istio.RouteDestination{ - Destination: dst, - Weight: int32(weights[i]), - }) - } - return res, nil -} - -func buildTLSMatch(hostnames []k8s.Hostname) []*istio.TLSMatchAttributes { - // Currently, the spec only supports extensions beyond hostname, which are not currently implemented by Istio. - return []*istio.TLSMatchAttributes{{ - SniHosts: hostnamesToStringListWithWildcard(hostnames), - }} -} - -func hostnamesToStringListWithWildcard(h []k8s.Hostname) []string { - if len(h) == 0 { - return []string{"*"} - } - res := make([]string, 0, len(h)) - for _, i := range h { - res = append(res, string(i)) - } - return res -} - -func intSum(n []int) int { - r := 0 - for _, i := range n { - r += i - } - return r -} - -func buildHTTPDestination(forwardTo []k8s.HTTPBackendRef, ns string, domain string, totalZero bool) ([]*istio.HTTPRouteDestination, *ConfigError) { - if forwardTo == nil { - return nil, nil - } - - weights := []int{} - action := []k8s.HTTPBackendRef{} - for i, w := range forwardTo { - wt := 1 - if w.Weight != nil { - wt = int(*w.Weight) - } - // When total weight is zero, create destination to add falutInjection. - // When total weight is not zero, do not create the destination. - if wt == 0 && !totalZero { - continue - } - action = append(action, forwardTo[i]) - weights = append(weights, wt) - } - weights = standardizeWeights(weights) - res := []*istio.HTTPRouteDestination{} - for i, fwd := range action { - dst, err := buildDestination(fwd.BackendRef, ns, domain) - if err != nil { - return nil, err - } - rd := &istio.HTTPRouteDestination{ - Destination: dst, - Weight: int32(weights[i]), - } - for _, filter := range fwd.Filters { - switch filter.Type { - case k8s.HTTPRouteFilterRequestHeaderModifier: - rd.Headers = createHeadersFilter(filter.RequestHeaderModifier) - default: - return nil, &ConfigError{Reason: InvalidFilter, Message: fmt.Sprintf("unsupported filter type %q", filter.Type)} - } - } - res = append(res, rd) - } - return res, nil -} - -func buildDestination(to k8s.BackendRef, ns, domain string) (*istio.Destination, *ConfigError) { - namespace := defaultIfNil((*string)(to.Namespace), ns) - if nilOrEqual((*string)(to.Group), "") && nilOrEqual((*string)(to.Kind), gvk.Service.Kind) { - // Service - if to.Port == nil { - // "Port is required when the referent is a Kubernetes Service." - return nil, &ConfigError{Reason: InvalidDestination, Message: "port is required in backendRef"} - } - if strings.Contains(string(to.Name), ".") { - return nil, &ConfigError{Reason: InvalidDestination, Message: "serviceName invalid; the name of the Service must be used, not the hostname."} - } - return &istio.Destination{ - // TODO: implement ReferencePolicy for cross namespace - Host: fmt.Sprintf("%s.%s.svc.%s", to.Name, namespace, domain), - Port: &istio.PortSelector{Number: uint32(*to.Port)}, - }, nil - } - if nilOrEqual((*string)(to.Group), features.MCSAPIGroup) && nilOrEqual((*string)(to.Kind), "ServiceImport") { - // Service import - name := fmt.Sprintf("%s.%s.svc.clusterset.local", to.Name, namespace) - if !features.EnableMCSHost { - // They asked for ServiceImport, but actually don't have full support enabled... - // No problem, we can just treat it as Service, which is already cross-cluster in this mode anyways - name = fmt.Sprintf("%s.%s.svc.%s", to.Name, namespace, domain) - } - if to.Port == nil { - // We don't know where to send without port - return nil, &ConfigError{Reason: InvalidDestination, Message: "port is required in backendRef"} - } - if strings.Contains(string(to.Name), ".") { - return nil, &ConfigError{Reason: InvalidDestination, Message: "serviceName invalid; the name of the Service must be used, not the hostname."} - } - return &istio.Destination{ - Host: name, - Port: &istio.PortSelector{Number: uint32(*to.Port)}, - }, nil - } - if nilOrEqual((*string)(to.Group), gvk.ServiceEntry.Group) && nilOrEqual((*string)(to.Kind), "Hostname") { - // Hostname synthetic type - if to.Port == nil { - // We don't know where to send without port - return nil, &ConfigError{Reason: InvalidDestination, Message: "port is required in backendRef"} - } - if to.Namespace != nil { - return nil, &ConfigError{Reason: InvalidDestination, Message: "namespace may not be set with Hostname type"} - } - return &istio.Destination{ - Host: string(to.Name), - Port: &istio.PortSelector{Number: uint32(*to.Port)}, - }, nil - } - return nil, &ConfigError{ - Reason: InvalidDestination, - Message: fmt.Sprintf("referencing unsupported backendRef: group %q kind %q", emptyIfNil((*string)(to.Group)), emptyIfNil((*string)(to.Kind))), - } -} - -// standardizeWeights migrates a list of weights from relative weights, to weights out of 100 -// In the event we cannot cleanly move to 100 denominator, we will round up weights in order. See test for details. -// TODO in the future we should probably just make VirtualService support relative weights directly -func standardizeWeights(weights []int) []int { - if len(weights) == 1 { - // Instead of setting weight=100 for a single destination, we will not set weight at all - return []int{0} - } - total := intSum(weights) - if total == 0 { - // All empty, fallback to even weight - for i := range weights { - weights[i] = 1 - } - total = len(weights) - } - results := make([]int, 0, len(weights)) - remainders := make([]float64, 0, len(weights)) - for _, w := range weights { - perc := float64(w) / float64(total) - rounded := int(perc * 100) - remainders = append(remainders, (perc*100)-float64(rounded)) - results = append(results, rounded) - } - remaining := 100 - intSum(results) - order := argsort(remainders) - for _, idx := range order { - if remaining <= 0 { - break - } - remaining-- - results[idx]++ - } - return results -} - -type argSlice struct { - sort.Interface - idx []int -} - -func (s argSlice) Swap(i, j int) { - s.Interface.Swap(i, j) - s.idx[i], s.idx[j] = s.idx[j], s.idx[i] -} - -func argsort(n []float64) []int { - s := &argSlice{Interface: sort.Float64Slice(n), idx: make([]int, len(n))} - for i := range s.idx { - s.idx[i] = i - } - sort.Sort(sort.Reverse(s)) - return s.idx -} - -func headerListToMap(hl []k8s.HTTPHeader) map[string]string { - if len(hl) == 0 { - return nil - } - res := map[string]string{} - for _, e := range hl { - k := strings.ToLower(string(e.Name)) - if _, f := res[k]; f { - // "Subsequent entries with an equivalent header name MUST be ignored" - continue - } - res[k] = e.Value - } - return res -} - -func createMirrorFilter(filter *k8s.HTTPRequestMirrorFilter, ns, domain string) (*istio.Destination, *ConfigError) { - if filter == nil { - return nil, nil - } - var weightOne int32 = 1 - return buildDestination(k8s.BackendRef{ - BackendObjectReference: filter.BackendRef, - Weight: &weightOne, - }, ns, domain) -} - -func createRedirectFilter(filter *k8s.HTTPRequestRedirectFilter) *istio.HTTPRedirect { - if filter == nil { - return nil - } - resp := &istio.HTTPRedirect{} - if filter.StatusCode != nil { - // Istio allows 301, 302, 303, 307, 308. - // Gateway allows only 301 and 302. - resp.RedirectCode = uint32(*filter.StatusCode) - } - if filter.Hostname != nil { - resp.Authority = string(*filter.Hostname) - } - if filter.Scheme != nil { - // Both allow http and https - resp.Scheme = *filter.Scheme - } - if filter.Port != nil { - resp.RedirectPort = &istio.HTTPRedirect_Port{Port: uint32(*filter.Port)} - } else { - // "When empty, port (if specified) of the request is used." - // this differs from Istio default - resp.RedirectPort = &istio.HTTPRedirect_DerivePort{DerivePort: istio.HTTPRedirect_FROM_REQUEST_PORT} - } - return resp -} - -func createHeadersFilter(filter *k8s.HTTPRequestHeaderFilter) *istio.Headers { - if filter == nil { - return nil - } - return &istio.Headers{ - Request: &istio.Headers_HeaderOperations{ - Add: headerListToMap(filter.Add), - Remove: filter.Remove, - Set: headerListToMap(filter.Set), - }, - } -} - -// nolint: unparam -func createMethodMatch(match k8s.HTTPRouteMatch) (*istio.StringMatch, *ConfigError) { - if match.Method == nil { - return nil, nil - } - return &istio.StringMatch{ - MatchType: &istio.StringMatch_Exact{Exact: string(*match.Method)}, - }, nil -} - -func createQueryParamsMatch(match k8s.HTTPRouteMatch) (map[string]*istio.StringMatch, *ConfigError) { - res := map[string]*istio.StringMatch{} - for _, qp := range match.QueryParams { - tp := k8s.QueryParamMatchExact - if qp.Type != nil { - tp = *qp.Type - } - switch tp { - case k8s.QueryParamMatchExact: - res[qp.Name] = &istio.StringMatch{ - MatchType: &istio.StringMatch_Exact{Exact: qp.Value}, - } - case k8s.QueryParamMatchRegularExpression: - res[qp.Name] = &istio.StringMatch{ - MatchType: &istio.StringMatch_Regex{Regex: qp.Value}, - } - default: - // Should never happen, unless a new field is added - return nil, &ConfigError{Reason: InvalidConfiguration, Message: fmt.Sprintf("unknown type: %q is not supported QueryParams type", tp)} - } - } - - if len(res) == 0 { - return nil, nil - } - return res, nil -} - -func createHeadersMatch(match k8s.HTTPRouteMatch) (map[string]*istio.StringMatch, *ConfigError) { - res := map[string]*istio.StringMatch{} - for _, header := range match.Headers { - tp := k8s.HeaderMatchExact - if header.Type != nil { - tp = *header.Type - } - switch tp { - case k8s.HeaderMatchExact: - res[string(header.Name)] = &istio.StringMatch{ - MatchType: &istio.StringMatch_Exact{Exact: header.Value}, - } - case k8s.HeaderMatchRegularExpression: - res[string(header.Name)] = &istio.StringMatch{ - MatchType: &istio.StringMatch_Regex{Regex: header.Value}, - } - default: - // Should never happen, unless a new field is added - return nil, &ConfigError{Reason: InvalidConfiguration, Message: fmt.Sprintf("unknown type: %q is not supported HeaderMatch type", tp)} - } - } - - if len(res) == 0 { - return nil, nil - } - return res, nil -} - -func createURIMatch(match k8s.HTTPRouteMatch) (*istio.StringMatch, *ConfigError) { - tp := k8s.PathMatchPathPrefix - if match.Path.Type != nil { - tp = *match.Path.Type - } - dest := "/" - if match.Path.Value != nil { - dest = *match.Path.Value - } - switch tp { - case k8s.PathMatchPathPrefix: - return &istio.StringMatch{ - MatchType: &istio.StringMatch_Prefix{Prefix: dest}, - }, nil - case k8s.PathMatchExact: - return &istio.StringMatch{ - MatchType: &istio.StringMatch_Exact{Exact: dest}, - }, nil - case k8s.PathMatchRegularExpression: - return &istio.StringMatch{ - MatchType: &istio.StringMatch_Regex{Regex: dest}, - }, nil - default: - // Should never happen, unless a new field is added - return nil, &ConfigError{Reason: InvalidConfiguration, Message: fmt.Sprintf("unknown type: %q is not supported Path match type", tp)} - } -} - -// getGatewayClass finds all gateway class that are owned by Istio -func getGatewayClasses(r *KubernetesResources) map[string]struct{} { - classes := map[string]struct{}{} - builtinClassExists := false - for _, obj := range r.GatewayClass { - gwc := obj.Spec.(*k8s.GatewayClassSpec) - if obj.Name == DefaultClassName { - builtinClassExists = true - } - if gwc.ControllerName == ControllerName { - // TODO we can add any settings we need here needed for the controller - // For now, we have none, so just add a struct - classes[obj.Name] = struct{}{} - - obj.Status.(*kstatus.WrappedStatus).Mutate(func(s config.Status) config.Status { - gcs := s.(*k8s.GatewayClassStatus) - gcs.Conditions = kstatus.UpdateConditionIfChanged(gcs.Conditions, metav1.Condition{ - Type: string(k8s.GatewayClassConditionStatusAccepted), - Status: kstatus.StatusTrue, - ObservedGeneration: obj.Generation, - LastTransitionTime: metav1.Now(), - Reason: string(k8s.GatewayClassConditionStatusAccepted), - Message: "Handled by Istio controller", - }) - return gcs - }) - } - } - if !builtinClassExists { - // Allow `istio` class without explicit GatewayClass. However, if it already exists then do not - // add it here, in case it points to a different controller. - classes[DefaultClassName] = struct{}{} - } - return classes -} - -var meshGVK = config.GroupVersionKind{ - Group: gvk.KubernetesGateway.Group, - Version: gvk.KubernetesGateway.Version, - Kind: "Mesh", -} - -// parentKey holds info about a parentRef (ie route binding to a Gateway). This is a mirror of -// k8s.ParentReference in a form that can be stored in a map -type parentKey struct { - Kind config.GroupVersionKind - // Name is the original name of the resource (ie Kubernetes Gateway name) - Name string - // Namespace is the namespace of the resource - Namespace string -} - -// parentInfo holds info about a "parent" - something that can be referenced as a ParentRef in the API. -// Today, this is just Gateway and Mesh. -type parentInfo struct { - // InternalName refers to the internal name we can reference it by. For example, "mesh" or "my-ns/my-gateway" - InternalName string - // AllowedKinds indicates which kinds can be admitted by this parent - AllowedKinds []k8s.RouteGroupKind - // Hostnames is the hostnames that must be match to reference to the parent. For gateway this is listener hostname - // Format is ns/hostname - Hostnames []string - // OriginalHostname is the unprocessed form of Hostnames; how it appeared in users' config - OriginalHostname string - - // AttachedRoutes keeps track of how many routes are attached to this parent. This is tracked for status. - // Because this is mutate in the route generation, parentInfo must be passed as a pointer - AttachedRoutes int32 - // ReportAttachedRoutes is a callback that should be triggered once all AttachedRoutes are computed, to - // actually store the attached route count in the status - ReportAttachedRoutes func() -} - -// routeParentReference holds information about a route's parent reference -type routeParentReference struct { - // InternalName refers to the internal name of the parent we can reference it by. For example, "mesh" or "my-ns/my-gateway" - InternalName string - // DeniedReason, if present, indicates why the reference was not valid - DeniedReason error - // OriginalReference contains the original reference - OriginalReference k8s.ParentReference -} - -// referencesToInternalNames converts valid parent references to names that can be used in VirtualService -func referencesToInternalNames(parents []routeParentReference) []string { - ret := make([]string, 0, len(parents)) - for _, p := range parents { - if p.DeniedReason != nil { - // We should filter this out - continue - } - ret = append(ret, p.InternalName) - } - // To ensure deterministic order, sort them - sort.Strings(ret) - return ret -} - -func convertGateways(r *KubernetesResources) ([]config.Config, map[parentKey]map[k8s.SectionName]*parentInfo, sets.Set) { - // result stores our generated Istio Gateways - result := []config.Config{} - // gwMap stores an index to access parentInfo (which corresponds to a Kubernetes Gateway) - gwMap := map[parentKey]map[k8s.SectionName]*parentInfo{} - // namespaceLabelReferences keeps track of all namespace label keys referenced by Gateways. This is - // used to ensure we handle namespace updates for those keys. - namespaceLabelReferences := sets.New() - classes := getGatewayClasses(r) - for _, obj := range r.Gateway { - obj := obj - kgw := obj.Spec.(*k8s.GatewaySpec) - if _, f := classes[string(kgw.GatewayClassName)]; !f { - // No gateway class found, this may be meant for another controller; should be skipped. - continue - } - - // Setup initial conditions to the success state. If we encounter errors, we will update this. - gatewayConditions := map[string]*condition{ - string(k8s.GatewayConditionReady): { - reason: "ListenersValid", - message: "Listeners valid", - }, - } - if isManaged(kgw) { - gatewayConditions[string(k8s.GatewayConditionScheduled)] = &condition{ - error: &ConfigError{ - Reason: "ResourcesPending", - Message: "Resources not yet deployed to the cluster", - }, - setOnce: string(k8s.GatewayReasonNotReconciled), // Default reason - } - } else { - gatewayConditions[string(k8s.GatewayConditionScheduled)] = &condition{ - reason: "ResourcesAvailable", - message: "Resources available", - } - } - servers := []*istio.Server{} - - // Extract the addresses. A gateway will bind to a specific Service - gatewayServices, skippedAddresses := extractGatewayServices(r, kgw, obj) - invalidListeners := []k8s.SectionName{} - for i, l := range kgw.Listeners { - i := i - namespaceLabelReferences.InsertAll(getNamespaceLabelReferences(l.AllowedRoutes)...) - server, ok := buildListener(r, obj, l, i) - if !ok { - invalidListeners = append(invalidListeners, l.Name) - continue - } - meta := parentMeta(obj, &l.Name) - meta[model.InternalGatewayServiceAnnotation] = strings.Join(gatewayServices, ",") - // Each listener generates an Istio Gateway with a single Server. This allows binding to a specific listener. - gatewayConfig := config.Config{ - Meta: config.Meta{ - CreationTimestamp: obj.CreationTimestamp, - GroupVersionKind: gvk.Gateway, - Name: fmt.Sprintf("%s-%s-%s", obj.Name, constants.KubernetesGatewayName, l.Name), - Annotations: meta, - Namespace: obj.Namespace, - Domain: r.Domain, - }, - Spec: &istio.Gateway{ - Servers: []*istio.Server{server}, - }, - } - ref := parentKey{ - Kind: gvk.KubernetesGateway, - Name: obj.Name, - Namespace: obj.Namespace, - } - if _, f := gwMap[ref]; !f { - gwMap[ref] = map[k8s.SectionName]*parentInfo{} - } - - pri := &parentInfo{ - InternalName: obj.Namespace + "/" + gatewayConfig.Name, - AllowedKinds: generateSupportedKinds(l), - Hostnames: server.Hosts, - OriginalHostname: emptyIfNil((*string)(l.Hostname)), - } - pri.ReportAttachedRoutes = func() { - reportListenerAttachedRoutes(i, obj, pri.AttachedRoutes) - } - gwMap[ref][l.Name] = pri - result = append(result, gatewayConfig) - servers = append(servers, server) - } - - // If "gateway.istio.io/alias-for" annotation is present, any Route - // that binds to the gateway will bind to its alias instead. - // The typical usage is when the original gateway is not managed by the gateway controller - // but the ( generated ) alias is. This allows people to build their own - // gateway controllers on top of Istio Gateway Controller. - if obj.Annotations != nil && obj.Annotations[gatewayAliasForAnnotationKey] != "" { - ref := parentKey{ - Kind: gvk.KubernetesGateway, - Name: obj.Annotations[gatewayAliasForAnnotationKey], - Namespace: obj.Namespace, - } - alias := parentKey{ - Kind: gvk.KubernetesGateway, - Name: obj.Name, - Namespace: obj.Namespace, - } - gwMap[ref] = gwMap[alias] - } - - internal, external, warnings := r.Context.ResolveGatewayInstances(obj.Namespace, gatewayServices, servers) - if len(skippedAddresses) > 0 { - warnings = append(warnings, fmt.Sprintf("Only Hostname is supported, ignoring %v", skippedAddresses)) - } - if len(warnings) > 0 { - var msg string - if len(internal) > 0 { - msg = fmt.Sprintf("Assigned to service(s) %s, but failed to assign to all requested addresses: %s", - humanReadableJoin(internal), strings.Join(warnings, "; ")) - } else { - msg = fmt.Sprintf("failed to assign to any requested addresses: %s", strings.Join(warnings, "; ")) - } - gatewayConditions[string(k8s.GatewayConditionReady)].error = &ConfigError{ - Reason: string(k8s.GatewayReasonAddressNotAssigned), - Message: msg, - } - } else if len(invalidListeners) > 0 { - gatewayConditions[string(k8s.GatewayConditionReady)].error = &ConfigError{ - Reason: string(k8s.GatewayReasonListenersNotValid), - Message: fmt.Sprintf("Invalid listeners: %v", invalidListeners), - } - } else { - gatewayConditions[string(k8s.GatewayConditionReady)].message = fmt.Sprintf("Gateway valid, assigned to service(s) %s", humanReadableJoin(internal)) - } - obj.Status.(*kstatus.WrappedStatus).Mutate(func(s config.Status) config.Status { - gs := s.(*k8s.GatewayStatus) - addressesToReport := external - addrType := k8s.IPAddressType - if len(addressesToReport) == 0 { - // There are no external addresses, so report the internal ones - // TODO: should we always report both? - addressesToReport = internal - addrType = k8s.HostnameAddressType - } - gs.Addresses = make([]k8s.GatewayAddress, 0, len(addressesToReport)) - for _, addr := range addressesToReport { - gs.Addresses = append(gs.Addresses, k8s.GatewayAddress{ - Type: &addrType, - Value: addr, - }) - } - return gs - }) - reportGatewayCondition(obj, gatewayConditions) - } - // Insert a parent for Mesh references. - gwMap[parentKey{ - Kind: meshGVK, - Name: "istio", - }] = map[k8s.SectionName]*parentInfo{ - "": { - InternalName: "mesh", - }, - } - return result, gwMap, namespaceLabelReferences -} - -// isManaged checks if a Gateway is managed (ie we create the Deployment and Service) or unmanaged. -// This is based on the address field of the spec. If address is set with a Hostname type, it should point to an existing -// Service that handles the gateway traffic. If it is not set, or refers to only a single IP, we will consider it managed and provision the Service. -// If there is an IP, we will set the `loadBalancerIP` type. -// While there is no defined standard for this in the API yet, it is tracked in https://github.com/kubernetes-sigs/gateway-api/issues/892. -// So far, this mirrors how out of clusters work (address set means to use existing IP, unset means to provision one), -// and there has been growing consensus on this model for in cluster deployments. -// -// Currently, the supported options are: -// * 1 Hostname value. This can be short Service name ingress, or FQDN ingress.ns.svc.cluster.local, example.com. If its a non-k8s FQDN it is a ServiceEntry. -// * 1 IP address. This is managed, with IP explicit -// * Nothing. This is managed, with IP auto assigned -// -// Not supported: -// Multiple hostname/IP - It is feasible but preference is to create multiple Gateways. This would also break the 1:1 mapping of GW:Service -// Mixed hostname and IP - doesn't make sense; user should define the IP in service -// NamedAddress - Service has no concept of named address. For cloud's that have named addresses they can be configured by annotations, -// -// which users can add to the Gateway. -func isManaged(gw *k8s.GatewaySpec) bool { - if len(gw.Addresses) == 0 { - return true - } - if len(gw.Addresses) > 1 { - return false - } - if t := gw.Addresses[0].Type; t == nil || *t == k8s.IPAddressType { - return true - } - return false -} - -func extractGatewayServices(r *KubernetesResources, kgw *k8s.GatewaySpec, obj config.Config) ([]string, []string) { - if isManaged(kgw) { - return []string{fmt.Sprintf("%s.%s.svc.%v", obj.Name, obj.Namespace, r.Domain)}, nil - } - gatewayServices := []string{} - skippedAddresses := []string{} - for _, addr := range kgw.Addresses { - if addr.Type != nil && *addr.Type != k8s.HostnameAddressType { - // We only support HostnameAddressType. Keep track of invalid ones so we can report in status. - skippedAddresses = append(skippedAddresses, addr.Value) - continue - } - // TODO: For now we are using Addresses. There has been some discussion of allowing inline - // parameters on the class field like a URL, in which case we will probably just use that. See - // https://github.com/kubernetes-sigs/gateway-api/pull/614 - fqdn := addr.Value - if !strings.Contains(fqdn, ".") { - // Short name, expand it - fqdn = fmt.Sprintf("%s.%s.svc.%s", fqdn, obj.Namespace, r.Domain) - } - gatewayServices = append(gatewayServices, fqdn) - } - return gatewayServices, skippedAddresses -} - -// getNamespaceLabelReferences fetches all label keys used in namespace selectors. Return order may not be stable. -func getNamespaceLabelReferences(routes *k8s.AllowedRoutes) []string { - if routes == nil || routes.Namespaces == nil || routes.Namespaces.Selector == nil { - return nil - } - res := []string{} - for k := range routes.Namespaces.Selector.MatchLabels { - res = append(res, k) - } - for _, me := range routes.Namespaces.Selector.MatchExpressions { - res = append(res, me.Key) - } - return res -} - -func buildListener(r *KubernetesResources, obj config.Config, l k8s.Listener, listenerIndex int) (*istio.Server, bool) { - listenerConditions := map[string]*condition{ - string(k8s.ListenerConditionReady): { - reason: "ListenerReady", - message: "No errors found", - }, - string(k8s.ListenerConditionDetached): { - reason: "ListenerReady", - message: "No errors found", - status: kstatus.StatusFalse, - }, - string(k8s.ListenerConditionConflicted): { - reason: "ListenerReady", - message: "No errors found", - status: kstatus.StatusFalse, - }, - string(k8s.ListenerConditionResolvedRefs): { - reason: "ListenerReady", - message: "No errors found", - }, - } - - defer reportListenerCondition(listenerIndex, l, obj, listenerConditions) - tls, err := buildTLS(l.TLS, obj.Namespace, isAutoPassthrough(obj, l)) - if err != nil { - listenerConditions[string(k8s.ListenerConditionReady)].error = &ConfigError{ - Reason: string(k8s.ListenerReasonInvalid), - Message: err.Message, - } - listenerConditions[string(k8s.ListenerConditionResolvedRefs)].error = &ConfigError{ - Reason: string(k8s.ListenerReasonInvalidCertificateRef), - Message: err.Message, - } - return nil, false - } - hostnames := buildHostnameMatch(obj.Namespace, r, l) - server := &istio.Server{ - Port: &istio.Port{ - // Name is required. We only have one server per Gateway, so we can just name them all the same - Name: "default", - Number: uint32(l.Port), - Protocol: listenerProtocolToIstio(l.Protocol), - }, - Hosts: hostnames, - Tls: tls, - } - - return server, true -} - -// isAutoPassthrough determines if a listener should use auto passthrough mode. This is used for -// multi-network. In the Istio API, this is an explicit tls.Mode. However, this mode is not part of -// the gateway-api, and leaks implementation details. We already have an API to declare a Gateway as -// a multinetwork gateway, so we will use this as a signal. -// A user who wishes to expose multinetwork connectivity should create a listener with port 15443 (by default, overridable by label), -// and declare it as PASSTRHOUGH -func isAutoPassthrough(obj config.Config, l k8s.Listener) bool { - _, networkSet := obj.Labels[label.TopologyNetwork.Name] - if !networkSet { - return false - } - expectedPort := "15443" - - if port, f := obj.Labels[label.NetworkingGatewayPort.Name]; f { - expectedPort = port - } - return fmt.Sprint(l.Port) == expectedPort -} - -func listenerProtocolToIstio(protocol k8s.ProtocolType) string { - // Currently, all gateway-api protocols are valid Istio protocols. - return string(protocol) -} - -func buildTLS(tls *k8s.GatewayTLSConfig, namespace string, isAutoPassthrough bool) (*istio.ServerTLSSettings, *ConfigError) { - if tls == nil { - return nil, nil - } - // Explicitly not supported: file mounted - // Not yet implemented: TLS mode, https redirect, max protocol version, SANs, CipherSuites, VerifyCertificate - - out := &istio.ServerTLSSettings{ - HttpsRedirect: false, - } - mode := k8s.TLSModeTerminate - if tls.Mode != nil { - mode = *tls.Mode - } - switch mode { - case k8s.TLSModeTerminate: - out.Mode = istio.ServerTLSSettings_SIMPLE - if len(tls.CertificateRefs) != 1 { - // This is required in the API, should be rejected in validation - return nil, &ConfigError{Reason: InvalidConfiguration, Message: "exactly 1 certificateRefs should be present for TLS termination"} - } - cred, err := buildSecretReference(*tls.CertificateRefs[0], namespace) - if err != nil { - return nil, err - } - out.CredentialName = cred - case k8s.TLSModePassthrough: - out.Mode = istio.ServerTLSSettings_PASSTHROUGH - if isAutoPassthrough { - out.Mode = istio.ServerTLSSettings_AUTO_PASSTHROUGH - } - } - return out, nil -} - -func buildSecretReference(ref k8s.SecretObjectReference, defaultNamespace string) (string, *ConfigError) { - if !nilOrEqual((*string)(ref.Group), gvk.Secret.Group) || !nilOrEqual((*string)(ref.Kind), gvk.Secret.Kind) { - return "", &ConfigError{Reason: InvalidTLS, Message: fmt.Sprintf("invalid certificate reference %v, only secret is allowed", objectReferenceString(ref))} - } - return credentials.ToKubernetesGatewayResource(defaultIfNil((*string)(ref.Namespace), defaultNamespace), string(ref.Name)), nil -} - -func objectReferenceString(ref k8s.SecretObjectReference) string { - return fmt.Sprintf("%s/%s/%s.%s", - emptyIfNil((*string)(ref.Group)), - emptyIfNil((*string)(ref.Kind)), - ref.Name, - emptyIfNil((*string)(ref.Namespace))) -} - -func parentRefString(ref k8s.ParentReference) string { - return fmt.Sprintf("%s/%s/%s/%s.%s", - emptyIfNil((*string)(ref.Group)), - emptyIfNil((*string)(ref.Kind)), - ref.Name, - emptyIfNil((*string)(ref.SectionName)), - emptyIfNil((*string)(ref.Namespace))) -} - -// buildHostnameMatch generates a VirtualService.spec.hosts section from a listener -func buildHostnameMatch(localNamespace string, r *KubernetesResources, l k8s.Listener) []string { - // We may allow all hostnames or a specific one - hostname := "*" - if l.Hostname != nil { - hostname = string(*l.Hostname) - } - - resp := []string{} - for _, ns := range namespacesFromSelector(localNamespace, r, l.AllowedRoutes) { - resp = append(resp, fmt.Sprintf("%s/%s", ns, hostname)) - } - - // If nothing matched use ~ namespace (match nothing). We need this since its illegal to have an - // empty hostname list, but we still need the Gateway provisioned to ensure status is properly set and - // SNI matches are established; we just don't want to actually match any routing rules (yet). - if len(resp) == 0 { - return []string{"~/" + hostname} - } - return resp -} - -// namespacesFromSelector determines a list of allowed namespaces for a given AllowedRoutes -func namespacesFromSelector(localNamespace string, r *KubernetesResources, lr *k8s.AllowedRoutes) []string { - // Default is to allow only the same namespace - if lr == nil || lr.Namespaces == nil || lr.Namespaces.From == nil || *lr.Namespaces.From == k8s.NamespacesFromSame { - return []string{localNamespace} - } - if *lr.Namespaces.From == k8s.NamespacesFromAll { - return []string{"*"} - } - - if lr.Namespaces.Selector == nil { - // Should never happen, invalid config - return []string{"*"} - } - - // gateway-api has selectors, but Istio Gateway just has a list of names. We will run the selector - // against all namespaces and get a list of matching namespaces that can be converted into a list - // Istio can handle. - ls, err := metav1.LabelSelectorAsSelector(lr.Namespaces.Selector) - if err != nil { - return nil - } - namespaces := []string{} - for _, ns := range r.Namespaces { - if ls.Matches(toNamespaceSet(ns.Name, ns.Labels)) { - namespaces = append(namespaces, ns.Name) - } - } - // Ensure stable order - sort.Strings(namespaces) - return namespaces -} - -func emptyIfNil(s *string) string { - return defaultIfNil(s, "") -} - -func defaultIfNil(s *string, d string) string { - if s != nil { - return *s - } - return d -} - -func nilOrEqual(have *string, expected string) bool { - return have == nil || *have == expected -} - -func StrPointer(s string) *string { - return &s -} - -func humanReadableJoin(ss []string) string { - switch len(ss) { - case 0: - return "" - case 1: - return ss[0] - case 2: - return ss[0] + " and " + ss[1] - default: - return strings.Join(ss[:len(ss)-1], ", ") + ", and " + ss[len(ss)-1] - } -} - -// NamespaceNameLabel represents that label added automatically to namespaces is newer Kubernetes clusters -const NamespaceNameLabel = "kubernetes.io/metadata.name" - -// toNamespaceSet converts a set of namespace labels to a Set that can be used to select against. -func toNamespaceSet(name string, labels map[string]string) klabels.Set { - // If namespace label is not set, implicitly insert it to support older Kubernetes versions - if labels[NamespaceNameLabel] == name { - // Already set, avoid copies - return labels - } - // First we need a copy to not modify the underlying object - ret := make(map[string]string, len(labels)+1) - for k, v := range labels { - ret[k] = v - } - ret[NamespaceNameLabel] = name - return ret -} diff --git a/pilot/pkg/config/kube/gateway/conversion_test.go b/pilot/pkg/config/kube/gateway/conversion_test.go deleted file mode 100644 index beb411695..000000000 --- a/pilot/pkg/config/kube/gateway/conversion_test.go +++ /dev/null @@ -1,505 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "fmt" - "os" - "reflect" - "regexp" - "sort" - "strings" - "testing" -) - -import ( - "github.com/google/go-cmp/cmp" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - k8s "sigs.k8s.io/gateway-api/apis/v1alpha2" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/crd" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model/kstatus" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3" - "github.com/apache/dubbo-go-pixiu/pilot/test/util" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config" - crdvalidation "github.com/apache/dubbo-go-pixiu/pkg/config/crd" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -func TestConvertResources(t *testing.T) { - validator := crdvalidation.NewIstioValidator(t) - cases := []struct { - name string - }{ - {"http"}, - {"tcp"}, - {"tls"}, - {"mismatch"}, - {"weighted"}, - {"zero"}, - {"mesh"}, - {"invalid"}, - {"multi-gateway"}, - {"delegated"}, - //{"route-binding"}, - {"reference-policy-tls"}, - {"serviceentry"}, - {"eastwest"}, - {"alias"}, - {"mcs"}, - {"route-precedence"}, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - input := readConfig(t, fmt.Sprintf("testdata/%s.yaml", tt.name), validator) - // Setup a few preconfigured services - ports := []*model.Port{ - { - Name: "http", - Port: 80, - Protocol: "HTTP", - }, - { - Name: "tcp", - Port: 34000, - Protocol: "TCP", - }, - } - ingressSvc := &model.Service{ - Attributes: model.ServiceAttributes{ - Name: "istio-ingressgateway", - Namespace: "dubbo-system", - ClusterExternalAddresses: model.AddressMap{ - Addresses: map[cluster.ID][]string{ - "Kubernetes": {"1.2.3.4"}, - }, - }, - }, - Ports: ports, - Hostname: "istio-ingressgateway.dubbo-system.svc.domain.suffix", - } - altIngressSvc := &model.Service{ - Attributes: model.ServiceAttributes{ - Namespace: "dubbo-system", - }, - Ports: ports, - Hostname: "example.com", - } - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{ - Services: []*model.Service{ingressSvc, altIngressSvc}, - Instances: []*model.ServiceInstance{ - {Service: ingressSvc, ServicePort: ingressSvc.Ports[0], Endpoint: &model.IstioEndpoint{EndpointPort: 8080}}, - {Service: ingressSvc, ServicePort: ingressSvc.Ports[1], Endpoint: &model.IstioEndpoint{}}, - {Service: altIngressSvc, ServicePort: altIngressSvc.Ports[0], Endpoint: &model.IstioEndpoint{}}, - {Service: altIngressSvc, ServicePort: altIngressSvc.Ports[1], Endpoint: &model.IstioEndpoint{}}, - }, - }, - ) - kr := splitInput(input) - kr.Context = model.NewGatewayContext(cg.PushContext()) - output := convertResources(kr) - output.AllowedReferences = nil // Not tested here - output.ReferencedNamespaceKeys = nil // Not tested here - - goldenFile := fmt.Sprintf("testdata/%s.yaml.golden", tt.name) - if util.Refresh() { - res := append(output.Gateway, output.VirtualService...) - if err := os.WriteFile(goldenFile, marshalYaml(t, res), 0o644); err != nil { - t.Fatal(err) - } - } - golden := splitOutput(readConfig(t, goldenFile, validator)) - - // sort virtual services to make the order deterministic - sort.Slice(golden.VirtualService, func(i, j int) bool { - return golden.VirtualService[i].Namespace+"/"+golden.VirtualService[i].Name < golden.VirtualService[j].Namespace+"/"+golden.VirtualService[j].Name - }) - sort.Slice(output.VirtualService, func(i, j int) bool { - return output.VirtualService[i].Namespace+"/"+output.VirtualService[i].Name < output.VirtualService[j].Namespace+"/"+output.VirtualService[j].Name - }) - assert.Equal(t, golden, output) - - outputStatus := getStatus(t, kr.GatewayClass, kr.Gateway, kr.HTTPRoute, kr.TLSRoute, kr.TCPRoute) - goldenStatusFile := fmt.Sprintf("testdata/%s.status.yaml.golden", tt.name) - if util.Refresh() { - if err := os.WriteFile(goldenStatusFile, outputStatus, 0o644); err != nil { - t.Fatal(err) - } - } - goldenStatus, err := os.ReadFile(goldenStatusFile) - if err != nil { - t.Fatal(err) - } - if diff := cmp.Diff(string(goldenStatus), string(outputStatus)); diff != "" { - t.Fatalf("Diff:\n%s", diff) - } - }) - } -} - -func TestReferencePolicy(t *testing.T) { - validator := crdvalidation.NewIstioValidator(t) - type res struct { - name, namespace string - allowed bool - } - cases := []struct { - name string - config string - expectations []res - }{ - { - name: "simple", - config: `apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: ReferencePolicy -metadata: - name: allow-gateways-to-ref-secrets - namespace: default -spec: - from: - - group: gateway.networking.k8s.io - kind: Gateway - namespace: dubbo-system - to: - - group: "" - kind: Secret -`, - expectations: []res{ - // allow cross namespace - {"kubernetes-gateway://default/wildcard-example-com-cert", "dubbo-system", true}, - // denied same namespace. We do not implicitly allow (in this code - higher level code does) - {"kubernetes-gateway://default/wildcard-example-com-cert", "default", false}, - // denied namespace - {"kubernetes-gateway://default/wildcard-example-com-cert", "bad", false}, - }, - }, - { - name: "multiple in one", - config: `apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: ReferencePolicy -metadata: - name: allow-gateways-to-ref-secrets - namespace: default -spec: - from: - - group: gateway.networking.k8s.io - kind: Gateway - namespace: ns-1 - - group: gateway.networking.k8s.io - kind: Gateway - namespace: ns-2 - to: - - group: "" - kind: Secret -`, - expectations: []res{ - {"kubernetes-gateway://default/wildcard-example-com-cert", "ns-1", true}, - {"kubernetes-gateway://default/wildcard-example-com-cert", "ns-2", true}, - {"kubernetes-gateway://default/wildcard-example-com-cert", "bad", false}, - }, - }, - { - name: "multiple", - config: `apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: ReferencePolicy -metadata: - name: ns1 - namespace: default -spec: - from: - - group: gateway.networking.k8s.io - kind: Gateway - namespace: ns-1 - to: - - group: "" - kind: Secret ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: ReferencePolicy -metadata: - name: ns2 - namespace: default -spec: - from: - - group: gateway.networking.k8s.io - kind: Gateway - namespace: ns-2 - to: - - group: "" - kind: Secret -`, - expectations: []res{ - {"kubernetes-gateway://default/wildcard-example-com-cert", "ns-1", true}, - {"kubernetes-gateway://default/wildcard-example-com-cert", "ns-2", true}, - {"kubernetes-gateway://default/wildcard-example-com-cert", "bad", false}, - }, - }, - { - name: "same namespace", - config: `apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: ReferencePolicy -metadata: - name: allow-gateways-to-ref-secrets - namespace: default -spec: - from: - - group: gateway.networking.k8s.io - kind: Gateway - namespace: default - to: - - group: "" - kind: Secret -`, - expectations: []res{ - {"kubernetes-gateway://default/wildcard-example-com-cert", "dubbo-system", false}, - {"kubernetes-gateway://default/wildcard-example-com-cert", "default", true}, - {"kubernetes-gateway://default/wildcard-example-com-cert", "bad", false}, - }, - }, - { - name: "same name", - config: `apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: ReferencePolicy -metadata: - name: allow-gateways-to-ref-secrets - namespace: default -spec: - from: - - group: gateway.networking.k8s.io - kind: Gateway - namespace: default - to: - - group: "" - kind: Secret - name: public -`, - expectations: []res{ - {"kubernetes-gateway://default/public", "dubbo-system", false}, - {"kubernetes-gateway://default/public", "default", true}, - {"kubernetes-gateway://default/private", "default", false}, - }, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - input := readConfigString(t, tt.config, validator) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - kr := splitInput(input) - kr.Context = model.NewGatewayContext(cg.PushContext()) - output := convertResources(kr) - c := &Controller{ - state: output, - } - for _, sc := range tt.expectations { - t.Run(fmt.Sprintf("%v/%v", sc.name, sc.namespace), func(t *testing.T) { - got := c.SecretAllowed(sc.name, sc.namespace) - if got != sc.allowed { - t.Fatalf("expected allowed=%v, got allowed=%v", sc.allowed, got) - } - }) - } - }) - } -} - -func getStatus(t test.Failer, acfgs ...[]config.Config) []byte { - cfgs := []config.Config{} - for _, cl := range acfgs { - cfgs = append(cfgs, cl...) - } - for i, c := range cfgs { - c = c.DeepCopy() - c.Spec = nil - c.Labels = nil - c.Annotations = nil - if c.Status.(*kstatus.WrappedStatus) != nil { - c.Status = c.Status.(*kstatus.WrappedStatus).Status - } - cfgs[i] = c - } - return timestampRegex.ReplaceAll(marshalYaml(t, cfgs), []byte("lastTransitionTime: fake")) -} - -var timestampRegex = regexp.MustCompile(`lastTransitionTime:.*`) - -func splitOutput(configs []config.Config) OutputResources { - out := OutputResources{ - Gateway: []config.Config{}, - VirtualService: []config.Config{}, - } - for _, c := range configs { - c.Domain = "domain.suffix" - switch c.GroupVersionKind { - case gvk.Gateway: - out.Gateway = append(out.Gateway, c) - case gvk.VirtualService: - out.VirtualService = append(out.VirtualService, c) - } - } - return out -} - -func splitInput(configs []config.Config) *KubernetesResources { - out := &KubernetesResources{} - namespaces := sets.New() - for _, c := range configs { - namespaces.Insert(c.Namespace) - switch c.GroupVersionKind { - case gvk.GatewayClass: - out.GatewayClass = append(out.GatewayClass, c) - case gvk.KubernetesGateway: - out.Gateway = append(out.Gateway, c) - case gvk.HTTPRoute: - out.HTTPRoute = append(out.HTTPRoute, c) - case gvk.TCPRoute: - out.TCPRoute = append(out.TCPRoute, c) - case gvk.TLSRoute: - out.TLSRoute = append(out.TLSRoute, c) - case gvk.ReferencePolicy: - out.ReferencePolicy = append(out.ReferencePolicy, c) - } - } - out.Namespaces = map[string]*corev1.Namespace{} - for ns := range namespaces { - out.Namespaces[ns] = &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: ns, - Labels: map[string]string{ - "istio.io/test-name-part": strings.Split(ns, "-")[0], - }, - }, - } - } - out.Domain = "domain.suffix" - return out -} - -func readConfig(t *testing.T, filename string, validator *crdvalidation.Validator) []config.Config { - t.Helper() - - data, err := os.ReadFile(filename) - if err != nil { - t.Fatalf("failed to read input yaml file: %v", err) - } - return readConfigString(t, string(data), validator) -} - -func readConfigString(t *testing.T, data string, validator *crdvalidation.Validator) []config.Config { - if err := validator.ValidateCustomResourceYAML(data); err != nil { - t.Error(err) - } - c, _, err := crd.ParseInputs(data) - if err != nil { - t.Fatalf("failed to parse CRD: %v", err) - } - return insertDefaults(c) -} - -// insertDefaults sets default values that would be present when reading from Kubernetes but not from -// files -func insertDefaults(cfgs []config.Config) []config.Config { - res := make([]config.Config, 0, len(cfgs)) - for _, c := range cfgs { - switch c.GroupVersionKind { - case gvk.GatewayClass: - c.Status = kstatus.Wrap(&k8s.GatewayClassStatus{}) - case gvk.KubernetesGateway: - c.Status = kstatus.Wrap(&k8s.GatewayStatus{}) - case gvk.HTTPRoute: - c.Status = kstatus.Wrap(&k8s.HTTPRouteStatus{}) - case gvk.TCPRoute: - c.Status = kstatus.Wrap(&k8s.TCPRouteStatus{}) - case gvk.TLSRoute: - c.Status = kstatus.Wrap(&k8s.TLSRouteStatus{}) - } - res = append(res, c) - } - return res -} - -// Print as YAML -func marshalYaml(t test.Failer, cl []config.Config) []byte { - t.Helper() - result := []byte{} - separator := []byte("---\n") - for _, config := range cl { - obj, err := crd.ConvertConfig(config) - if err != nil { - t.Fatalf("Could not decode %v: %v", config.Name, err) - } - bytes, err := yaml.Marshal(obj) - if err != nil { - t.Fatalf("Could not convert %v to YAML: %v", config, err) - } - result = append(result, bytes...) - result = append(result, separator...) - } - return result -} - -func TestStandardizeWeight(t *testing.T) { - tests := []struct { - name string - input []int - output []int - }{ - {"single", []int{1}, []int{0}}, - {"double", []int{1, 1}, []int{50, 50}}, - {"zero", []int{1, 0}, []int{100, 0}}, - {"overflow", []int{1, 1, 1}, []int{34, 33, 33}}, - {"skewed", []int{9, 1}, []int{90, 10}}, - {"multiple overflow", []int{1, 1, 1, 1, 1, 1}, []int{17, 17, 17, 17, 16, 16}}, - {"skewed overflow", []int{1, 1, 1, 3}, []int{17, 17, 16, 50}}, - {"skewed overflow 2", []int{1, 1, 1, 1, 2}, []int{17, 17, 17, 16, 33}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := standardizeWeights(tt.input) - if !reflect.DeepEqual(tt.output, got) { - t.Errorf("standardizeWeights() = %v, want %v", got, tt.output) - } - if len(tt.output) > 1 && intSum(tt.output) != 100 { - t.Errorf("invalid weights, should sum to 100: %v", got) - } - }) - } -} - -func TestHumanReadableJoin(t *testing.T) { - tests := []struct { - input []string - want string - }{ - {[]string{"a"}, "a"}, - {[]string{"a", "b"}, "a and b"}, - {[]string{"a", "b", "c"}, "a, b, and c"}, - } - for _, tt := range tests { - t.Run(strings.Join(tt.input, "_"), func(t *testing.T) { - if got := humanReadableJoin(tt.input); !reflect.DeepEqual(got, tt.want) { - t.Errorf("got %v, want %v", got, tt.want) - } - }) - } -} diff --git a/pilot/pkg/config/kube/gateway/deploymentcontroller.go b/pilot/pkg/config/kube/gateway/deploymentcontroller.go deleted file mode 100644 index 16f4b8275..000000000 --- a/pilot/pkg/config/kube/gateway/deploymentcontroller.go +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "strings" - "text/template" - "time" -) - -import ( - istiolog "istio.io/pkg/log" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - klabels "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - appsinformersv1 "k8s.io/client-go/informers/apps/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/cache" - gateway "sigs.k8s.io/gateway-api/apis/v1alpha2" - "sigs.k8s.io/gateway-api/pkg/client/listers/apis/v1alpha2" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/controllers" -) - -// DeploymentController implements a controller that materializes a Gateway into an in cluster gateway proxy -// to serve requests from. This is implemented with a Deployment and Service today. -// The implementation makes a few non-obvious choices - namely using Server Side Apply from go templates -// and not using controller-runtime. -// -// controller-runtime has a number of constraints that make it inappropriate for usage here, despite this -// seeming to be the bread and butter of the library: -// * It is not readily possible to bring existing Informers, which would require extra watches (#1668) -// * Goroutine leaks (#1655) -// * Excessive API-server calls at startup which have no benefit to us (#1603) -// * Hard to use with SSA (#1669) -// While these can be worked around, at some point it isn't worth the effort. -// -// Server Side Apply with go templates is an odd choice (no one likes YAML templating...) but is one of the few -// remaining options after all others are ruled out. -// - Merge patch/Update cannot be used. If we always enforce that our object is *exactly* the same as -// the in-cluster object we will get in endless loops due to other controllers that like to add annotations, etc. -// If we chose to allow any unknown fields, then we would never be able to remove fields we added, as -// we cannot tell if we created it or someone else did. SSA fixes these issues -// - SSA using client-go Apply libraries is almost a good choice, but most third-party clients (Istio, MCS, and gateway-api) -// do not provide these libraries. -// - SSA using standard API types doesn't work well either: https://github.com/kubernetes-sigs/controller-runtime/issues/1669 -// - This leaves YAML templates, converted to unstructured types and Applied with the dynamic client. -type DeploymentController struct { - client kube.Client - queue controllers.Queue - templates *template.Template - patcher patcher - gatewayLister v1alpha2.GatewayLister - gatewayClassLister v1alpha2.GatewayClassLister -} - -// Patcher is a function that abstracts patching logic. This is largely because client-go fakes do not handle patching -type patcher func(gvr schema.GroupVersionResource, name string, namespace string, data []byte, subresources ...string) error - -// NewDeploymentController constructs a DeploymentController and registers required informers. -// The controller will not start until Run() is called. -func NewDeploymentController(client kube.Client) *DeploymentController { - gw := client.GatewayAPIInformer().Gateway().V1alpha2().Gateways() - gwc := client.GatewayAPIInformer().Gateway().V1alpha2().GatewayClasses() - dc := &DeploymentController{ - client: client, - templates: processTemplates(), - patcher: func(gvr schema.GroupVersionResource, name string, namespace string, data []byte, subresources ...string) error { - c := client.Dynamic().Resource(gvr).Namespace(namespace) - t := true - _, err := c.Patch(context.Background(), name, types.ApplyPatchType, data, metav1.PatchOptions{ - Force: &t, - FieldManager: ControllerName, - }, subresources...) - return err - }, - gatewayLister: gw.Lister(), - gatewayClassLister: gwc.Lister(), - } - dc.queue = controllers.NewQueue("gateway deployment", - controllers.WithReconciler(dc.Reconcile), - controllers.WithMaxAttempts(5)) - - // Set up a handler that will add the parent Gateway object onto the queue. - // The queue will only handle Gateway objects; if child resources (Service, etc) are updated we re-add - // the Gateway to the queue and reconcile the state of the world. - handler := controllers.ObjectHandler(controllers.EnqueueForParentHandler(dc.queue, gvk.KubernetesGateway)) - - // Use the full informer, since we are already fetching all Services for other purposes - // If we somehow stop watching Services in the future we can add a label selector like below. - client.KubeInformer().Core().V1().Services().Informer(). - AddEventHandler(handler) - - // For Deployments, this is the only controller watching. We can filter to just the deployments we care about - client.KubeInformer().InformerFor(&appsv1.Deployment{}, func(k kubernetes.Interface, resync time.Duration) cache.SharedIndexInformer { - return appsinformersv1.NewFilteredDeploymentInformer( - k, metav1.NamespaceAll, resync, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, - func(options *metav1.ListOptions) { - options.LabelSelector = "gateway.istio.io/managed=istio.io-gateway-controller" - }, - ) - }).AddEventHandler(handler) - - // Use the full informer; we are already watching all Gateways for the core Istiod logic - gw.Informer().AddEventHandler(controllers.ObjectHandler(dc.queue.AddObject)) - gwc.Informer().AddEventHandler(controllers.ObjectHandler(func(o controllers.Object) { - o.GetName() - gws, _ := dc.gatewayLister.List(klabels.Everything()) - for _, g := range gws { - if string(g.Spec.GatewayClassName) == o.GetName() { - dc.queue.AddObject(g) - } - } - })) - - return dc -} - -func (d *DeploymentController) Run(stop <-chan struct{}) { - d.queue.Run(stop) -} - -// Reconcile takes in the name of a Gateway and ensures the cluster is in the desired state -func (d *DeploymentController) Reconcile(req types.NamespacedName) error { - log := log.WithLabels("gateway", req) - - gw, err := d.gatewayLister.Gateways(req.Namespace).Get(req.Name) - if err != nil || gw == nil { - // we'll ignore not-found errors, since they can't be fixed by an immediate - // requeue (we'll need to wait for a new notification), and we can get them - // on deleted requests. - if err := controllers.IgnoreNotFound(err); err != nil { - log.Errorf("unable to fetch Gateway: %v", err) - return err - } - return nil - } - - gc, _ := d.gatewayClassLister.Get(string(gw.Spec.GatewayClassName)) - if gc != nil { - // We found the gateway class, but we do not implement it. Skip - if gc.Spec.ControllerName != ControllerName { - return nil - } - } else { - // Didn't find gateway class... it must use implicit Istio one. - if gw.Spec.GatewayClassName != DefaultClassName { - return nil - } - } - - // Matched class, reconcile it - return d.configureIstioGateway(log, *gw) -} - -func (d *DeploymentController) configureIstioGateway(log *istiolog.Scope, gw gateway.Gateway) error { - // If user explicitly sets addresses, we are assuming they are pointing to an existing deployment. - // We will not manage it in this case - if !isManaged(&gw.Spec) { - log.Debug("skip unmanaged gateway") - return nil - } - log.Info("reconciling") - - svc := serviceInput{Gateway: &gw, Ports: extractServicePorts(gw)} - if err := d.ApplyTemplate("service.yaml", svc); err != nil { - return fmt.Errorf("update service: %v", err) - } - log.Info("service updated") - - dep := deploymentInput{Gateway: &gw, KubeVersion122: kube.IsAtLeastVersion(d.client, 22)} - if err := d.ApplyTemplate("deployment.yaml", dep); err != nil { - return fmt.Errorf("update deployment: %v", err) - } - log.Info("deployment updated") - - gws := &gateway.Gateway{ - TypeMeta: metav1.TypeMeta{ - Kind: gvk.KubernetesGateway.Kind, - APIVersion: gvk.KubernetesGateway.Group + "/" + gvk.KubernetesGateway.Version, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: gw.Name, - Namespace: gw.Namespace, - }, - Status: gateway.GatewayStatus{ - Conditions: setConditions(gw.Generation, nil, map[string]*condition{ - string(gateway.GatewayConditionScheduled): { - reason: "ResourcesAvailable", - message: "Deployed gateway to the cluster", - }, - }), - }, - } - if err := d.ApplyObject(gws, "status"); err != nil { - return fmt.Errorf("update gateway status: %v", err) - } - log.Info("gateway updated") - return nil -} - -// ApplyTemplate renders a template with the given input and (server-side) applies the results to the cluster. -func (d *DeploymentController) ApplyTemplate(template string, input metav1.Object, subresources ...string) error { - var buf bytes.Buffer - if err := d.templates.ExecuteTemplate(&buf, template, input); err != nil { - return err - } - data := map[string]interface{}{} - err := yaml.Unmarshal(buf.Bytes(), &data) - if err != nil { - return err - } - us := unstructured.Unstructured{Object: data} - gvr, err := controllers.UnstructuredToGVR(us) - if err != nil { - return err - } - j, err := json.Marshal(us.Object) - if err != nil { - return err - } - - log.Debugf("applying %v", string(j)) - return d.patcher(gvr, us.GetName(), input.GetNamespace(), j, subresources...) -} - -// ApplyObject renders an object with the given input and (server-side) applies the results to the cluster. -func (d *DeploymentController) ApplyObject(obj controllers.Object, subresources ...string) error { - j, err := config.ToJSON(obj) - if err != nil { - return err - } - - gvr, err := controllers.ObjectToGVR(obj) - if err != nil { - return err - } - log.Debugf("applying %v", string(j)) - - return d.patcher(gvr, obj.GetName(), obj.GetNamespace(), j, subresources...) -} - -// Merge maps merges multiple maps. Latter maps take precedence over previous maps on overlapping fields -func mergeMaps(maps ...map[string]string) map[string]string { - if len(maps) == 0 { - return nil - } - res := make(map[string]string, len(maps[0])) - for _, m := range maps { - for k, v := range m { - res[k] = v - } - } - return res -} - -type serviceInput struct { - *gateway.Gateway - Ports []corev1.ServicePort -} - -type deploymentInput struct { - *gateway.Gateway - KubeVersion122 bool -} - -func extractServicePorts(gw gateway.Gateway) []corev1.ServicePort { - svcPorts := make([]corev1.ServicePort, 0, len(gw.Spec.Listeners)+1) - svcPorts = append(svcPorts, corev1.ServicePort{ - Name: "status-port", - Port: int32(15021), - }) - portNums := map[int32]struct{}{} - for i, l := range gw.Spec.Listeners { - if _, f := portNums[int32(l.Port)]; f { - continue - } - portNums[int32(l.Port)] = struct{}{} - name := string(l.Name) - if name == "" { - // Should not happen since name is required, but in case an invalid resource gets in... - name = fmt.Sprintf("%s-%d", strings.ToLower(string(l.Protocol)), i) - } - svcPorts = append(svcPorts, corev1.ServicePort{ - Name: name, - Port: int32(l.Port), - }) - } - return svcPorts -} diff --git a/pilot/pkg/config/kube/gateway/deploymentcontroller_test.go b/pilot/pkg/config/kube/gateway/deploymentcontroller_test.go deleted file mode 100644 index ad075db06..000000000 --- a/pilot/pkg/config/kube/gateway/deploymentcontroller_test.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "bytes" - "path/filepath" - "testing" -) - -import ( - istiolog "istio.io/pkg/log" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/gateway-api/apis/v1alpha2" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/test/util" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -func TestConfigureIstioGateway(t *testing.T) { - tests := []struct { - name string - gw v1alpha2.Gateway - }{ - { - "simple", - v1alpha2.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "default", - Namespace: "default", - }, - Spec: v1alpha2.GatewaySpec{}, - }, - }, - { - "manual-ip", - v1alpha2.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "default", - Namespace: "default", - }, - Spec: v1alpha2.GatewaySpec{ - Addresses: []v1alpha2.GatewayAddress{{ - Type: func() *v1alpha2.AddressType { x := v1alpha2.IPAddressType; return &x }(), - Value: "1.2.3.4", - }}, - }, - }, - }, - { - "cluster-ip", - v1alpha2.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "default", - Namespace: "default", - Annotations: map[string]string{"networking.istio.io/service-type": string(corev1.ServiceTypeClusterIP)}, - }, - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{ - Name: "http", - Port: v1alpha2.PortNumber(80), - }}, - }, - }, - }, - { - "multinetwork", - v1alpha2.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "default", - Namespace: "default", - Labels: map[string]string{"topology.istio.io/network": "network-1"}, - }, - Spec: v1alpha2.GatewaySpec{ - Listeners: []v1alpha2.Listener{{ - Name: "http", - Port: v1alpha2.PortNumber(80), - }}, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - buf := &bytes.Buffer{} - d := &DeploymentController{ - client: kube.NewFakeClient(), - templates: processTemplates(), - patcher: func(gvr schema.GroupVersionResource, name string, namespace string, data []byte, subresources ...string) error { - b, err := yaml.JSONToYAML(data) - if err != nil { - return err - } - buf.Write(b) - buf.Write([]byte("---\n")) - return nil - }, - } - err := d.configureIstioGateway(istiolog.FindScope(istiolog.DefaultScopeName), tt.gw) - if err != nil { - t.Fatal(err) - } - - resp := timestampRegex.ReplaceAll(buf.Bytes(), []byte("lastTransitionTime: fake")) - util.CompareContent(t, resp, filepath.Join("testdata", "deployment", tt.name+".yaml")) - }) - } -} diff --git a/pilot/pkg/config/kube/gateway/gatewayclass.go b/pilot/pkg/config/kube/gateway/gatewayclass.go deleted file mode 100644 index 504a918f3..000000000 --- a/pilot/pkg/config/kube/gateway/gatewayclass.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "context" -) - -import ( - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - gateway "sigs.k8s.io/gateway-api/apis/v1alpha2" - gatewayclient "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned/typed/apis/v1alpha2" - lister "sigs.k8s.io/gateway-api/pkg/client/listers/apis/v1alpha2" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/controllers" -) - -// ClassController is a controller that creates the default Istio GatewayClass. This will not -// continually reconcile the full state of the GatewayClass object, and instead only create the class -// if it doesn't exist. This allows users to manage it through other means or modify it as they wish. -// If it is deleted, however, it will be added back. -// This controller intentionally does not do leader election for simplicity. Because we only create -// and not update there is no need; the first controller to create the GatewayClass wins. -type ClassController struct { - queue controllers.Queue - classes lister.GatewayClassLister - directClient gatewayclient.GatewayClassInterface -} - -func NewClassController(client kube.Client) *ClassController { - gc := &ClassController{} - gc.queue = controllers.NewQueue("gateway class", - controllers.WithReconciler(gc.Reconcile), - controllers.WithMaxAttempts(25)) - - class := client.GatewayAPIInformer().Gateway().V1alpha2().GatewayClasses() - gc.classes = class.Lister() - gc.directClient = client.GatewayAPI().GatewayV1alpha2().GatewayClasses() - class.Informer(). - AddEventHandler(controllers.FilteredObjectHandler(gc.queue.AddObject, func(o controllers.Object) bool { - return o.GetName() == DefaultClassName - })) - return gc -} - -func (c *ClassController) Run(stop <-chan struct{}) { - // Ensure we initially reconcile the current state - c.queue.Add(types.NamespacedName{Name: DefaultClassName}) - c.queue.Run(stop) -} - -func (c *ClassController) Reconcile(name types.NamespacedName) error { - _, err := c.classes.Get(DefaultClassName) - if err := controllers.IgnoreNotFound(err); err != nil { - log.Errorf("unable to fetch GatewayClass: %v", err) - return err - } - if !apierrors.IsNotFound(err) { - log.Debugf("GatewayClass/%v already exists, no action", DefaultClassName) - return nil - } - desc := "The default Istio GatewayClass" - gc := &gateway.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Name: DefaultClassName, - }, - Spec: gateway.GatewayClassSpec{ - ControllerName: ControllerName, - Description: &desc, - }, - } - _, err = c.directClient.Create(context.Background(), gc, metav1.CreateOptions{}) - if apierrors.IsConflict(err) { - // This is not really an error, just a race condition - log.Infof("Attempted to create GatewayClass/%v, but it was already created", DefaultClassName) - return nil - } - return err -} diff --git a/pilot/pkg/config/kube/gateway/gatewayclass_test.go b/pilot/pkg/config/kube/gateway/gatewayclass_test.go deleted file mode 100644 index 4a8ae97cd..000000000 --- a/pilot/pkg/config/kube/gateway/gatewayclass_test.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "context" - "fmt" - "testing" - "time" -) - -import ( - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - gateway "sigs.k8s.io/gateway-api/apis/v1alpha2" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/controllers" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -func TestClassController(t *testing.T) { - client := kube.NewFakeClient() - cc := NewClassController(client) - stop := make(chan struct{}) - t.Cleanup(func() { - close(stop) - }) - client.RunAndWait(stop) - go cc.Run(stop) - createClass := func(name, controller string) { - gc := &gateway.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - Spec: gateway.GatewayClassSpec{ - ControllerName: gateway.GatewayController(controller), - }, - } - _, err := client.GatewayAPI().GatewayV1alpha2().GatewayClasses().Create(context.Background(), gc, metav1.CreateOptions{}) - if apierrors.IsAlreadyExists(err) { - _, _ = client.GatewayAPI().GatewayV1alpha2().GatewayClasses().Update(context.Background(), gc, metav1.UpdateOptions{}) - } - } - deleteClass := func(name string) { - client.GatewayAPI().GatewayV1alpha2().GatewayClasses().Delete(context.Background(), name, metav1.DeleteOptions{}) - } - expectClass := func(name, controller string) { - t.Helper() - retry.UntilSuccessOrFail(t, func() error { - gc, err := client.GatewayAPI().GatewayV1alpha2().GatewayClasses().Get(context.Background(), name, metav1.GetOptions{}) - if controllers.IgnoreNotFound(err) != nil { - return err - } - if controller == "" { - if gc == nil { // Expect none, got none - return nil - } - return fmt.Errorf("expected no class, got %v", gc.Spec.ControllerName) - } - if gc == nil { - return fmt.Errorf("expected class %v, got none", controller) - } - if gateway.GatewayController(controller) != gc.Spec.ControllerName { - return fmt.Errorf("expected class %v, got %v", controller, gc.Spec.ControllerName) - } - return nil - }, retry.Timeout(time.Second*3)) - } - - // Class should be created initially - expectClass(DefaultClassName, ControllerName) - - // Once we delete it, it should be added back - deleteClass(DefaultClassName) - expectClass(DefaultClassName, ControllerName) - - // Overwrite the class, controller should not reconcile it back - createClass(DefaultClassName, "different-controller") - expectClass(DefaultClassName, "different-controller") - - // Once we delete it, it should be added back - deleteClass(DefaultClassName) - expectClass(DefaultClassName, ControllerName) - - // Create an unrelated GatewayClass, we should not do anything to it - createClass("something-else", "different-controller") - expectClass("something-else", "different-controller") - deleteClass("something-else") - expectClass("something-else", "") -} diff --git a/pilot/pkg/config/kube/gateway/generation_adapter.go b/pilot/pkg/config/kube/gateway/generation_adapter.go deleted file mode 100644 index 5b04b2d05..000000000 --- a/pilot/pkg/config/kube/gateway/generation_adapter.go +++ /dev/null @@ -1,32 +0,0 @@ -/* - Copyright Istio Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package gateway - -type gatewayGeneration struct { - inner interface{} -} - -func (g *gatewayGeneration) SetObservedGeneration(i int64) { - // Intentionally blank. The observed generation of a gateway - // status type is contained in the individual conditions - // not at the top level, and is the responsibility - // of the condition functions to update. -} - -func (g *gatewayGeneration) Unwrap() interface{} { - return g.inner -} diff --git a/pilot/pkg/config/kube/gateway/leak_test.go b/pilot/pkg/config/kube/gateway/leak_test.go deleted file mode 100644 index fab84f22d..000000000 --- a/pilot/pkg/config/kube/gateway/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/config/kube/gateway/templates.go b/pilot/pkg/config/kube/gateway/templates.go deleted file mode 100644 index 13a4c2b22..000000000 --- a/pilot/pkg/config/kube/gateway/templates.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "embed" - "strings" - "text/template" -) - -import ( - "github.com/Masterminds/sprig/v3" - "sigs.k8s.io/yaml" -) - -// Templates embeds the templates -// -//go:embed templates/* -var Templates embed.FS - -func funcMap() template.FuncMap { - f := sprig.TxtFuncMap() - // strdict is the same as the "dict" function (http://masterminds.github.io/sprig/dicts.html) - // but returns a map[string]string instead of interface{} types. This allows it to be used - // in annotations/labels. - f["strdict"] = func(v ...string) map[string]string { - dict := map[string]string{} - lenv := len(v) - for i := 0; i < lenv; i += 2 { - key := v[i] - if i+1 >= lenv { - dict[key] = "" - continue - } - dict[key] = v[i+1] - } - return dict - } - f["toYamlMap"] = func(mps ...map[string]string) string { - data, err := yaml.Marshal(mergeMaps(mps...)) - if err != nil { - return "" - } - return strings.TrimSuffix(string(data), "\n") - } - f["omit"] = func(dict map[string]string, keys ...string) map[string]string { - res := map[string]string{} - - omit := make(map[string]bool, len(keys)) - for _, k := range keys { - omit[k] = true - } - - for k, v := range dict { - if _, ok := omit[k]; !ok { - res[k] = v - } - } - return res - } - return f -} - -func processTemplates() *template.Template { - t, err := template.New("gateway").Funcs(funcMap()).ParseFS(Templates, "templates/*.yaml") - if err != nil { - panic(err.Error()) - } - return t -} diff --git a/pilot/pkg/config/kube/gateway/templates/deployment.yaml b/pilot/pkg/config/kube/gateway/templates/deployment.yaml deleted file mode 100644 index 927b7183f..000000000 --- a/pilot/pkg/config/kube/gateway/templates/deployment.yaml +++ /dev/null @@ -1,87 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: - {{ toYamlMap (omit .Annotations "kubectl.kubernetes.io/last-applied-configuration") | nindent 4 }} - labels: - {{ toYamlMap .Labels - (strdict "gateway.istio.io/managed" "istio.io-gateway-controller") - | nindent 4}} - name: {{.Name}} - namespace: {{.Namespace}} - ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1alpha2 - kind: Gateway - name: {{.Name}} - uid: {{.UID}} -spec: - selector: - matchLabels: - istio.io/gateway-name: {{.Name}} - template: - metadata: - annotations: - {{ toYamlMap - (strdict "inject.istio.io/templates" "gateway") - (omit .Annotations "kubectl.kubernetes.io/last-applied-configuration") - | nindent 8}} - labels: - {{ toYamlMap - (strdict "sidecar.istio.io/inject" "true") - (strdict "istio.io/gateway-name" .Name) - .Labels - | nindent 8}} - spec: - {{- if .KubeVersion122 }} - {{/* safe since 1.22: https://github.com/kubernetes/kubernetes/pull/103326. */}} - securityContext: - sysctls: - - name: net.ipv4.ip_unprivileged_port_start - value: "0" - {{- end }} - containers: - - image: auto - name: istio-proxy - securityContext: - {{- if .KubeVersion122 }} - # Safe since 1.22: https://github.com/kubernetes/kubernetes/pull/103326 - capabilities: - drop: - - ALL - allowPrivilegeEscalation: false - privileged: false - readOnlyRootFilesystem: true - runAsUser: 1337 - runAsGroup: 1337 - runAsNonRoot: true - {{- else }} - capabilities: - drop: - - ALL - add: - - NET_BIND_SERVICE - runAsUser: 0 - runAsGroup: 1337 - runAsNonRoot: false - allowPrivilegeEscalation: true - readOnlyRootFilesystem: true - {{- end }} - ports: - - containerPort: 15021 - name: status-port - protocol: TCP - {{- with (index .Labels "topology.istio.io/network") }} - env: - - name: ISTIO_META_REQUESTED_NETWORK_VIEW - value: {{.|quote}} - {{- end }} - readinessProbe: - failureThreshold: 10 - successThreshold: 1 - timeoutSeconds: 2 - periodSeconds: 2 - httpGet: - path: /healthz/ready - port: 15021 - scheme: HTTP - diff --git a/pilot/pkg/config/kube/gateway/templates/service.yaml b/pilot/pkg/config/kube/gateway/templates/service.yaml deleted file mode 100644 index d2e8af664..000000000 --- a/pilot/pkg/config/kube/gateway/templates/service.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - annotations: - {{ toYamlMap (omit .Annotations "kubectl.kubernetes.io/last-applied-configuration") | nindent 4 }} - labels: - {{ toYamlMap .Labels - (strdict "gateway.istio.io/managed" "istio.io-gateway-controller") - | nindent 4}} - name: {{.Name}} - namespace: {{.Namespace}} - ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1alpha2 - kind: Gateway - name: {{.Name}} - uid: {{.UID}} -spec: - ports: - {{- range $key, $val := .Ports }} - - name: {{ $val.Name | quote }} - port: {{ $val.Port }} - protocol: TCP - {{- end }} - selector: - istio.io/gateway-name: {{.Name}} - {{- if .Spec.Addresses }} - loadBalancerIP: {{ (index .Spec.Addresses 0).Value | quote}} - {{- end }} - type: {{ index .Annotations "networking.istio.io/service-type" | default "LoadBalancer" | quote }} - diff --git a/pilot/pkg/config/kube/gateway/testdata/alias.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/alias.status.yaml.golden deleted file mode 100644 index 548463cb5..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/alias.status.yaml.golden +++ /dev/null @@ -1,93 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - creationTimestamp: null - name: istio - namespace: default -spec: null -status: - conditions: - - lastTransitionTime: fake - message: Handled by Istio controller - reason: Accepted - status: "True" - type: Accepted ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: third-party-gateway - namespace: dubbo-system -spec: null ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: gateway - namespace: dubbo-system -spec: null -status: - addresses: - - type: IPAddress - value: 1.2.3.4 - conditions: - - lastTransitionTime: fake - message: Gateway valid, assigned to service(s) istio-ingressgateway.dubbo-system.svc.domain.suffix:80 - reason: ListenersValid - status: "True" - type: Ready - - lastTransitionTime: fake - message: Resources available - reason: ResourcesAvailable - status: "True" - type: Scheduled - listeners: - - attachedRoutes: 1 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: default - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: http - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: third-party-gateway - namespace: dubbo-system ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/alias.yaml b/pilot/pkg/config/kube/gateway/testdata/alias.yaml deleted file mode 100644 index 71c3e17e3..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/alias.yaml +++ /dev/null @@ -1,65 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - name: istio -spec: - controllerName: istio.io/gateway-controller ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: third-party-gateway - namespace: dubbo-system -spec: - addresses: - - value: istio-ingressgateway - type: Hostname - gatewayClassName: third-party-gatewayclass - listeners: - - name: default - hostname: "*.domain.example" - port: 80 - protocol: HTTP - allowedRoutes: - namespaces: - from: All ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: gateway - namespace: dubbo-system - annotations: - gateway.istio.io/alias-for: third-party-gateway -spec: - addresses: - - value: istio-ingressgateway - type: Hostname - gatewayClassName: istio - listeners: - - name: default - hostname: "*.domain.example" - port: 80 - protocol: HTTP - allowedRoutes: - namespaces: - from: All ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: http - namespace: default -spec: - parentRefs: - - name: third-party-gateway - namespace: dubbo-system - hostnames: ["first.domain.example", "another.domain.example"] - rules: - - matches: - - path: - type: PathPrefix - value: / - backendRefs: - - name: httpbin - port: 80 diff --git a/pilot/pkg/config/kube/gateway/testdata/alias.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/alias.yaml.golden deleted file mode 100644 index cd96103db..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/alias.yaml.golden +++ /dev/null @@ -1,66 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/gateway/default.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-default - namespace: dubbo-system -spec: - servers: - - hosts: - - '*/*.domain.example' - port: - name: default - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/http.default - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: http-0-istio-autogenerated-k8s-gateway - namespace: default -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-default - hosts: - - first.domain.example - http: - - match: - - uri: - prefix: / - route: - - destination: - host: httpbin.default.svc.domain.suffix - port: - number: 80 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/http.default - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: http-1-istio-autogenerated-k8s-gateway - namespace: default -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-default - hosts: - - another.domain.example - http: - - match: - - uri: - prefix: / - route: - - destination: - host: httpbin.default.svc.domain.suffix - port: - number: 80 ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/delegated.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/delegated.status.yaml.golden deleted file mode 100644 index 67fdb20b7..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/delegated.status.yaml.golden +++ /dev/null @@ -1,131 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - creationTimestamp: null - name: istio - namespace: default -spec: null -status: - conditions: - - lastTransitionTime: fake - message: Handled by Istio controller - reason: Accepted - status: "True" - type: Accepted ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: gateway - namespace: dubbo-system -spec: null -status: - addresses: - - type: IPAddress - value: 1.2.3.4 - conditions: - - lastTransitionTime: fake - message: Gateway valid, assigned to service(s) istio-ingressgateway.dubbo-system.svc.domain.suffix:80 - reason: ListenersValid - status: "True" - type: Ready - - lastTransitionTime: fake - message: Resources available - reason: ResourcesAvailable - status: "True" - type: Scheduled - listeners: - - attachedRoutes: 1 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: apple - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute - - attachedRoutes: 1 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: banana - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: http - namespace: apple -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: http - namespace: banana -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/delegated.yaml b/pilot/pkg/config/kube/gateway/testdata/delegated.yaml deleted file mode 100644 index c6a7b9369..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/delegated.yaml +++ /dev/null @@ -1,67 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - name: istio -spec: - controllerName: istio.io/gateway-controller ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: gateway - namespace: dubbo-system -spec: - addresses: - - value: istio-ingressgateway - type: Hostname - gatewayClassName: istio - listeners: - - name: apple - hostname: "apple.example" - port: 80 - protocol: HTTP - allowedRoutes: - namespaces: - from: Selector - selector: - matchLabels: - kubernetes.io/metadata.name: apple - - name: banana - hostname: "banana.example" - port: 80 - protocol: HTTP - allowedRoutes: - namespaces: - from: Selector - selector: - matchLabels: - kubernetes.io/metadata.name: banana ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: http - namespace: apple -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - rules: - - backendRefs: - - name: httpbin-apple - port: 80 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: http - namespace: banana -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - rules: - - backendRefs: - - name: httpbin-banana - port: 80 ---- \ No newline at end of file diff --git a/pilot/pkg/config/kube/gateway/testdata/delegated.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/delegated.yaml.golden deleted file mode 100644 index 91583e009..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/delegated.yaml.golden +++ /dev/null @@ -1,78 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/gateway/apple.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-apple - namespace: dubbo-system -spec: - servers: - - hosts: - - apple/apple.example - port: - name: default - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/gateway/banana.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-banana - namespace: dubbo-system -spec: - servers: - - hosts: - - banana/banana.example - port: - name: default - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/http.apple - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: http-0-istio-autogenerated-k8s-gateway - namespace: apple -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-apple - hosts: - - '*' - http: - - route: - - destination: - host: httpbin-apple.apple.svc.domain.suffix - port: - number: 80 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/http.banana - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: http-0-istio-autogenerated-k8s-gateway - namespace: banana -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-banana - hosts: - - '*' - http: - - route: - - destination: - host: httpbin-banana.banana.svc.domain.suffix - port: - number: 80 ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/cluster-ip.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/cluster-ip.yaml deleted file mode 100644 index 881c1c38e..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/cluster-ip.yaml +++ /dev/null @@ -1,101 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - annotations: - networking.istio.io/service-type: ClusterIP - labels: - gateway.istio.io/managed: istio.io-gateway-controller - name: default - namespace: default - ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1alpha2 - kind: Gateway - name: default - uid: null -spec: - ports: - - name: status-port - port: 15021 - protocol: TCP - - name: http - port: 80 - protocol: TCP - selector: - istio.io/gateway-name: default - type: ClusterIP ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: - networking.istio.io/service-type: ClusterIP - labels: - gateway.istio.io/managed: istio.io-gateway-controller - name: default - namespace: default - ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1alpha2 - kind: Gateway - name: default - uid: null -spec: - selector: - matchLabels: - istio.io/gateway-name: default - template: - metadata: - annotations: - inject.istio.io/templates: gateway - networking.istio.io/service-type: ClusterIP - labels: - istio.io/gateway-name: default - sidecar.istio.io/inject: "true" - spec: - containers: - - image: auto - name: istio-proxy - ports: - - containerPort: 15021 - name: status-port - protocol: TCP - readinessProbe: - failureThreshold: 10 - httpGet: - path: /healthz/ready - port: 15021 - scheme: HTTP - periodSeconds: 2 - successThreshold: 1 - timeoutSeconds: 2 - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - securityContext: - sysctls: - - name: net.ipv4.ip_unprivileged_port_start - value: "0" ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: default - namespace: default -spec: - gatewayClassName: "" - listeners: null -status: - conditions: - - lastTransitionTime: fake - message: Deployed gateway to the cluster - reason: ResourcesAvailable - status: "True" - type: Scheduled ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/manual-ip.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/manual-ip.yaml deleted file mode 100644 index 5810ed324..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/manual-ip.yaml +++ /dev/null @@ -1,96 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - annotations: {} - labels: - gateway.istio.io/managed: istio.io-gateway-controller - name: default - namespace: default - ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1alpha2 - kind: Gateway - name: default - uid: null -spec: - loadBalancerIP: 1.2.3.4 - ports: - - name: status-port - port: 15021 - protocol: TCP - selector: - istio.io/gateway-name: default - type: LoadBalancer ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: {} - labels: - gateway.istio.io/managed: istio.io-gateway-controller - name: default - namespace: default - ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1alpha2 - kind: Gateway - name: default - uid: null -spec: - selector: - matchLabels: - istio.io/gateway-name: default - template: - metadata: - annotations: - inject.istio.io/templates: gateway - labels: - istio.io/gateway-name: default - sidecar.istio.io/inject: "true" - spec: - containers: - - image: auto - name: istio-proxy - ports: - - containerPort: 15021 - name: status-port - protocol: TCP - readinessProbe: - failureThreshold: 10 - httpGet: - path: /healthz/ready - port: 15021 - scheme: HTTP - periodSeconds: 2 - successThreshold: 1 - timeoutSeconds: 2 - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - securityContext: - sysctls: - - name: net.ipv4.ip_unprivileged_port_start - value: "0" ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: default - namespace: default -spec: - gatewayClassName: "" - listeners: null -status: - conditions: - - lastTransitionTime: fake - message: Deployed gateway to the cluster - reason: ResourcesAvailable - status: "True" - type: Scheduled ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/multinetwork.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/multinetwork.yaml deleted file mode 100644 index bf17ba6dd..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/multinetwork.yaml +++ /dev/null @@ -1,104 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - annotations: {} - labels: - gateway.istio.io/managed: istio.io-gateway-controller - topology.istio.io/network: network-1 - name: default - namespace: default - ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1alpha2 - kind: Gateway - name: default - uid: null -spec: - ports: - - name: status-port - port: 15021 - protocol: TCP - - name: http - port: 80 - protocol: TCP - selector: - istio.io/gateway-name: default - type: LoadBalancer ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: {} - labels: - gateway.istio.io/managed: istio.io-gateway-controller - topology.istio.io/network: network-1 - name: default - namespace: default - ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1alpha2 - kind: Gateway - name: default - uid: null -spec: - selector: - matchLabels: - istio.io/gateway-name: default - template: - metadata: - annotations: - inject.istio.io/templates: gateway - labels: - istio.io/gateway-name: default - sidecar.istio.io/inject: "true" - topology.istio.io/network: network-1 - spec: - containers: - - env: - - name: ISTIO_META_REQUESTED_NETWORK_VIEW - value: network-1 - image: auto - name: istio-proxy - ports: - - containerPort: 15021 - name: status-port - protocol: TCP - readinessProbe: - failureThreshold: 10 - httpGet: - path: /healthz/ready - port: 15021 - scheme: HTTP - periodSeconds: 2 - successThreshold: 1 - timeoutSeconds: 2 - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - securityContext: - sysctls: - - name: net.ipv4.ip_unprivileged_port_start - value: "0" ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: default - namespace: default -spec: - gatewayClassName: "" - listeners: null -status: - conditions: - - lastTransitionTime: fake - message: Deployed gateway to the cluster - reason: ResourcesAvailable - status: "True" - type: Scheduled ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/simple.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/simple.yaml deleted file mode 100644 index 874c0b79b..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/simple.yaml +++ /dev/null @@ -1,95 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - annotations: {} - labels: - gateway.istio.io/managed: istio.io-gateway-controller - name: default - namespace: default - ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1alpha2 - kind: Gateway - name: default - uid: null -spec: - ports: - - name: status-port - port: 15021 - protocol: TCP - selector: - istio.io/gateway-name: default - type: LoadBalancer ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: {} - labels: - gateway.istio.io/managed: istio.io-gateway-controller - name: default - namespace: default - ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1alpha2 - kind: Gateway - name: default - uid: null -spec: - selector: - matchLabels: - istio.io/gateway-name: default - template: - metadata: - annotations: - inject.istio.io/templates: gateway - labels: - istio.io/gateway-name: default - sidecar.istio.io/inject: "true" - spec: - containers: - - image: auto - name: istio-proxy - ports: - - containerPort: 15021 - name: status-port - protocol: TCP - readinessProbe: - failureThreshold: 10 - httpGet: - path: /healthz/ready - port: 15021 - scheme: HTTP - periodSeconds: 2 - successThreshold: 1 - timeoutSeconds: 2 - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - securityContext: - sysctls: - - name: net.ipv4.ip_unprivileged_port_start - value: "0" ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: default - namespace: default -spec: - gatewayClassName: "" - listeners: null -status: - conditions: - - lastTransitionTime: fake - message: Deployed gateway to the cluster - reason: ResourcesAvailable - status: "True" - type: Scheduled ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/eastwest.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/eastwest.status.yaml.golden deleted file mode 100644 index 5fa341979..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/eastwest.status.yaml.golden +++ /dev/null @@ -1,142 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: eastwestgateway - namespace: dubbo-system -spec: null -status: - conditions: - - lastTransitionTime: fake - message: 'failed to assign to any requested addresses: hostname "eastwestgateway.dubbo-system.svc.domain.suffix" - not found' - reason: AddressNotAssigned - status: "False" - type: Ready - - lastTransitionTime: fake - message: Resources not yet deployed to the cluster - reason: ResourcesPending - status: "False" - type: Scheduled - listeners: - - attachedRoutes: 1 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: istiod-grpc - supportedKinds: - - group: gateway.networking.k8s.io - kind: TLSRoute - - attachedRoutes: 1 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: istiod-webhook - supportedKinds: - - group: gateway.networking.k8s.io - kind: TLSRoute - - attachedRoutes: 0 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: cross-network - supportedKinds: - - group: gateway.networking.k8s.io - kind: TLSRoute ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: TLSRoute -metadata: - creationTimestamp: null - name: eastwestgateway-grpc - namespace: dubbo-system -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - kind: Gateway - name: eastwestgateway - sectionName: istiod-grpc ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: TLSRoute -metadata: - creationTimestamp: null - name: eastwestgateway-webhook - namespace: dubbo-system -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - kind: Gateway - name: eastwestgateway - sectionName: istiod-webhook ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/eastwest.yaml b/pilot/pkg/config/kube/gateway/testdata/eastwest.yaml deleted file mode 100644 index 94ebfc5ff..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/eastwest.yaml +++ /dev/null @@ -1,56 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: eastwestgateway - namespace: dubbo-system - labels: - topology.istio.io/network: "network-1" -spec: - gatewayClassName: istio - listeners: - - name: istiod-grpc - port: 15012 - protocol: TLS - tls: - mode: Passthrough - - name: istiod-webhook - port: 15017 - protocol: TLS - tls: - mode: Passthrough - - name: cross-network - hostname: "*.local" - port: 15443 - protocol: TLS - tls: - mode: Passthrough ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: TLSRoute -metadata: - name: eastwestgateway-grpc - namespace: dubbo-system -spec: - parentRefs: - - name: eastwestgateway - kind: Gateway - sectionName: istiod-grpc - rules: - - backendRefs: - - name: istiod - port: 15012 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: TLSRoute -metadata: - name: eastwestgateway-webhook - namespace: dubbo-system -spec: - parentRefs: - - name: eastwestgateway - kind: Gateway - sectionName: istiod-webhook - rules: - - backendRefs: - - name: istiod - port: 15017 diff --git a/pilot/pkg/config/kube/gateway/testdata/eastwest.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/eastwest.yaml.golden deleted file mode 100644 index caf985110..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/eastwest.yaml.golden +++ /dev/null @@ -1,106 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: eastwestgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/eastwestgateway/istiod-grpc.dubbo-system - creationTimestamp: null - name: eastwestgateway-istio-autogenerated-k8s-gateway-istiod-grpc - namespace: dubbo-system -spec: - servers: - - hosts: - - dubbo-system/* - port: - name: default - number: 15012 - protocol: TLS - tls: {} ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: eastwestgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/eastwestgateway/istiod-webhook.dubbo-system - creationTimestamp: null - name: eastwestgateway-istio-autogenerated-k8s-gateway-istiod-webhook - namespace: dubbo-system -spec: - servers: - - hosts: - - dubbo-system/* - port: - name: default - number: 15017 - protocol: TLS - tls: {} ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: eastwestgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/eastwestgateway/cross-network.dubbo-system - creationTimestamp: null - name: eastwestgateway-istio-autogenerated-k8s-gateway-cross-network - namespace: dubbo-system -spec: - servers: - - hosts: - - dubbo-system/*.local - port: - name: default - number: 15443 - protocol: TLS - tls: - mode: AUTO_PASSTHROUGH ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: TLSRoute/eastwestgateway-grpc.dubbo-system - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: eastwestgateway-grpc-tls-0-istio-autogenerated-k8s-gateway - namespace: dubbo-system -spec: - gateways: - - dubbo-system/eastwestgateway-istio-autogenerated-k8s-gateway-istiod-grpc - hosts: - - '*' - tls: - - match: - - sniHosts: - - '*' - route: - - destination: - host: istiod.dubbo-system.svc.domain.suffix - port: - number: 15012 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: TLSRoute/eastwestgateway-webhook.dubbo-system - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: eastwestgateway-webhook-tls-0-istio-autogenerated-k8s-gateway - namespace: dubbo-system -spec: - gateways: - - dubbo-system/eastwestgateway-istio-autogenerated-k8s-gateway-istiod-webhook - hosts: - - '*' - tls: - - match: - - sniHosts: - - '*' - route: - - destination: - host: istiod.dubbo-system.svc.domain.suffix - port: - number: 15017 ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/http.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/http.status.yaml.golden deleted file mode 100644 index d0e0117c5..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/http.status.yaml.golden +++ /dev/null @@ -1,165 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - creationTimestamp: null - name: istio - namespace: default -spec: null -status: - conditions: - - lastTransitionTime: fake - message: Handled by Istio controller - reason: Accepted - status: "True" - type: Accepted ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: gateway - namespace: dubbo-system -spec: null -status: - addresses: - - type: IPAddress - value: 1.2.3.4 - conditions: - - lastTransitionTime: fake - message: Gateway valid, assigned to service(s) istio-ingressgateway.dubbo-system.svc.domain.suffix:80 - reason: ListenersValid - status: "True" - type: Ready - - lastTransitionTime: fake - message: Resources available - reason: ResourcesAvailable - status: "True" - type: Scheduled - listeners: - - attachedRoutes: 4 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: default - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: http - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: http-not-selected - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: no hostnames matched parent hostname "*.domain.example" - reason: InvalidParentReference - status: "False" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: http2 - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: mirror - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: redirect - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/http.yaml b/pilot/pkg/config/kube/gateway/testdata/http.yaml deleted file mode 100644 index be789642e..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/http.yaml +++ /dev/null @@ -1,137 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - name: istio -spec: - controllerName: istio.io/gateway-controller ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: gateway - namespace: dubbo-system -spec: - addresses: - - value: istio-ingressgateway - type: Hostname - gatewayClassName: istio - listeners: - - name: default - hostname: "*.domain.example" - port: 80 - protocol: HTTP - allowedRoutes: - namespaces: - from: All ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: http - namespace: default -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - hostnames: ["first.domain.example", "another.domain.example"] - rules: - - matches: - - path: - type: PathPrefix - value: /get - headers: - - name: my-header - value: some-value - type: Exact - filters: - - type: RequestHeaderModifier - requestHeaderModifier: - add: - - name: my-added-header - value: added-value - remove: [my-removed-header] - backendRefs: - - name: httpbin - port: 80 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: http2 - namespace: default -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - hostnames: ["second.domain.example"] - rules: - - matches: - - path: - type: PathPrefix - value: /second - backendRefs: - - name: httpbin-second - port: 80 - - matches: - - path: - type: PathPrefix - value: / - backendRefs: - - name: httpbin-wildcard - port: 80 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: redirect - namespace: default -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - rules: - - filters: - - type: RequestRedirect - requestRedirect: - port: 8080 - statusCode: 302 - scheme: https ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: mirror - namespace: default -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - rules: - - filters: - - type: RequestMirror - requestMirror: - backendRef: - name: httpbin-mirror - port: 80 - backendRefs: - - name: httpbin - port: 80 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: http-not-selected - namespace: default -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - hostnames: ["should.not.select"] - rules: - - matches: - - path: - type: PathPrefix - value: /get - backendRefs: - - name: httpbin-bad - port: 80 \ No newline at end of file diff --git a/pilot/pkg/config/kube/gateway/testdata/http.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/http.yaml.golden deleted file mode 100644 index e9055184e..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/http.yaml.golden +++ /dev/null @@ -1,145 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/gateway/default.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-default - namespace: dubbo-system -spec: - servers: - - hosts: - - '*/*.domain.example' - port: - name: default - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/http.default - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: http-0-istio-autogenerated-k8s-gateway - namespace: default -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-default - hosts: - - first.domain.example - http: - - headers: - request: - add: - my-added-header: added-value - remove: - - my-removed-header - match: - - headers: - my-header: - exact: some-value - uri: - prefix: /get - route: - - destination: - host: httpbin.default.svc.domain.suffix - port: - number: 80 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/http.default - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: http-1-istio-autogenerated-k8s-gateway - namespace: default -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-default - hosts: - - another.domain.example - http: - - headers: - request: - add: - my-added-header: added-value - remove: - - my-removed-header - match: - - headers: - my-header: - exact: some-value - uri: - prefix: /get - route: - - destination: - host: httpbin.default.svc.domain.suffix - port: - number: 80 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/http2.default - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: http2-0-istio-autogenerated-k8s-gateway - namespace: default -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-default - hosts: - - second.domain.example - http: - - match: - - uri: - prefix: /second - route: - - destination: - host: httpbin-second.default.svc.domain.suffix - port: - number: 80 - - match: - - uri: - prefix: / - route: - - destination: - host: httpbin-wildcard.default.svc.domain.suffix - port: - number: 80 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/mirror.default - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: mirror-0-istio-autogenerated-k8s-gateway - namespace: default -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-default - hosts: - - '*' - http: - - mirror: - host: httpbin-mirror.default.svc.domain.suffix - port: - number: 80 - route: - - destination: - host: httpbin.default.svc.domain.suffix - port: - number: 80 - - redirect: - port: 8080 - redirectCode: 302 - scheme: https ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/invalid.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/invalid.status.yaml.golden deleted file mode 100644 index bfd9ba055..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/invalid.status.yaml.golden +++ /dev/null @@ -1,320 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - creationTimestamp: null - name: istio - namespace: default -spec: null -status: - conditions: - - lastTransitionTime: fake - message: Handled by Istio controller - reason: Accepted - status: "True" - type: Accepted ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: gateway - namespace: dubbo-system -spec: null -status: - addresses: - - type: IPAddress - value: 1.2.3.4 - conditions: - - lastTransitionTime: fake - message: Gateway valid, assigned to service(s) istio-ingressgateway.dubbo-system.svc.domain.suffix:80 - reason: ListenersValid - status: "True" - type: Ready - - lastTransitionTime: fake - message: Resources available - reason: ResourcesAvailable - status: "True" - type: Scheduled - listeners: - - attachedRoutes: 3 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: default - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: invalid-service - namespace: dubbo-system -spec: null -status: - conditions: - - lastTransitionTime: fake - message: 'failed to assign to any requested addresses: hostname "fake-service.com" - not found' - reason: AddressNotAssigned - status: "False" - type: Ready - - lastTransitionTime: fake - message: Resources available - reason: ResourcesAvailable - status: "True" - type: Scheduled - listeners: - - attachedRoutes: 0 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: default - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: target-port-reference - namespace: dubbo-system -spec: null -status: - conditions: - - lastTransitionTime: fake - message: 'failed to assign to any requested addresses: port 8080 not found for - hostname "istio-ingressgateway.dubbo-system.svc.domain.suffix" (hint: the service - port should be specified, not the workload port. Did you mean one of these ports: - [80]?)' - reason: AddressNotAssigned - status: "False" - type: Ready - - lastTransitionTime: fake - message: Resources available - reason: ResourcesAvailable - status: "True" - type: Scheduled - listeners: - - attachedRoutes: 0 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: default - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: invalid-gateway-address - namespace: invalid-gateway-address -spec: null -status: - conditions: - - lastTransitionTime: fake - message: 'failed to assign to any requested addresses: Only Hostname is supported, - ignoring [1.2.3.4]' - reason: AddressNotAssigned - status: "False" - type: Ready - - lastTransitionTime: fake - message: Resources available - reason: ResourcesAvailable - status: "True" - type: Scheduled - listeners: - - attachedRoutes: 0 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: default - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: invalid-tls - namespace: dubbo-system -spec: null -status: - conditions: - - lastTransitionTime: fake - message: 'Invalid listeners: [default]' - reason: ListenersNotValid - status: "False" - type: Ready - - lastTransitionTime: fake - message: Resources available - reason: ResourcesAvailable - status: "True" - type: Scheduled - listeners: - - attachedRoutes: 0 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: invalid certificate reference core/unknown/my-cert-http., only secret - is allowed - reason: Invalid - status: "False" - type: Ready - - lastTransitionTime: fake - message: invalid certificate reference core/unknown/my-cert-http., only secret - is allowed - reason: InvalidCertificateRef - status: "False" - type: ResolvedRefs - name: default - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: invalid-backendRef - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: 'referencing unsupported backendRef: group "" kind "GcsBucket"' - reason: InvalidDestination - status: "False" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: invalid-filter - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: unsupported filter type "ExtensionRef" - reason: InvalidFilter - status: "False" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: invalid-mirror - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: 'referencing unsupported backendRef: group "" kind "no-support"' - reason: InvalidDestination - status: "False" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/invalid.yaml b/pilot/pkg/config/kube/gateway/testdata/invalid.yaml deleted file mode 100644 index d9b86d0ab..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/invalid.yaml +++ /dev/null @@ -1,148 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - name: istio -spec: - controllerName: istio.io/gateway-controller ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: gateway - namespace: dubbo-system -spec: - addresses: - - value: istio-ingressgateway - type: Hostname - gatewayClassName: istio - listeners: - - name: default - hostname: "*.domain.example" - port: 80 - protocol: HTTP - allowedRoutes: - namespaces: - from: All ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: invalid-service - namespace: dubbo-system -spec: - gatewayClassName: istio - listeners: - - name: default - hostname: "*.example" - port: 80 - protocol: HTTP - addresses: - - value: fake-service.com - type: Hostname ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: target-port-reference - namespace: dubbo-system -spec: - addresses: - - value: istio-ingressgateway - type: Hostname - gatewayClassName: istio - listeners: - - name: default - hostname: "*.example" - port: 8080 # Test service has port 80 with targetPort 8080 - protocol: HTTP ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: invalid-gateway-address - namespace: invalid-gateway-address -spec: - gatewayClassName: istio - addresses: - - value: 1.2.3.4 - type: NamedAddress - listeners: - - name: default - hostname: "*.domain.example" - port: 80 - protocol: HTTP ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: invalid-tls - namespace: dubbo-system -spec: - addresses: - - value: istio-ingressgateway - type: Hostname - gatewayClassName: istio - listeners: - - name: default - hostname: "domain.example" - port: 34000 - protocol: HTTPS - tls: - mode: Terminate - certificateRefs: - - name: my-cert-http - group: core - kind: unknown ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: invalid-filter - namespace: default -spec: - hostnames: ["first.domain.example"] - parentRefs: - - name: gateway - namespace: dubbo-system - rules: - - filters: - - type: ExtensionRef - backendRefs: - - name: httpbin - port: 80 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: invalid-backendRef - namespace: default -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - hostnames: ["second.domain.example"] - rules: - - backendRefs: - - name: httpbin - kind: GcsBucket ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: invalid-mirror - namespace: default -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - rules: - - filters: - - type: RequestMirror - requestMirror: - backendRef: - kind: no-support - name: httpbin-mirror - port: 80 - backendRefs: - - name: httpbin - port: 80 \ No newline at end of file diff --git a/pilot/pkg/config/kube/gateway/testdata/invalid.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/invalid.yaml.golden deleted file mode 100644 index 015156cd4..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/invalid.yaml.golden +++ /dev/null @@ -1,72 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/gateway/default.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-default - namespace: dubbo-system -spec: - servers: - - hosts: - - '*/*.domain.example' - port: - name: default - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: fake-service.com - internal.istio.io/parent: Gateway/invalid-service/default.dubbo-system - creationTimestamp: null - name: invalid-service-istio-autogenerated-k8s-gateway-default - namespace: dubbo-system -spec: - servers: - - hosts: - - dubbo-system/*.example - port: - name: default - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/target-port-reference/default.dubbo-system - creationTimestamp: null - name: target-port-reference-istio-autogenerated-k8s-gateway-default - namespace: dubbo-system -spec: - servers: - - hosts: - - dubbo-system/*.example - port: - name: default - number: 8080 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: "" - internal.istio.io/parent: Gateway/invalid-gateway-address/default.invalid-gateway-address - creationTimestamp: null - name: invalid-gateway-address-istio-autogenerated-k8s-gateway-default - namespace: invalid-gateway-address -spec: - servers: - - hosts: - - invalid-gateway-address/*.domain.example - port: - name: default - number: 80 - protocol: HTTP ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/mcs.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/mcs.status.yaml.golden deleted file mode 100644 index 82154a7d5..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/mcs.status.yaml.golden +++ /dev/null @@ -1,70 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: gateway - namespace: dubbo-system -spec: null -status: - addresses: - - type: IPAddress - value: 1.2.3.4 - conditions: - - lastTransitionTime: fake - message: Gateway valid, assigned to service(s) istio-ingressgateway.dubbo-system.svc.domain.suffix:34000 - reason: ListenersValid - status: "True" - type: Ready - - lastTransitionTime: fake - message: Resources available - reason: ResourcesAvailable - status: "True" - type: Scheduled - listeners: - - attachedRoutes: 1 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: default - supportedKinds: - - group: gateway.networking.k8s.io - kind: TCPRoute ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: TCPRoute -metadata: - creationTimestamp: null - name: tcp - namespace: dubbo-system -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/mcs.yaml b/pilot/pkg/config/kube/gateway/testdata/mcs.yaml deleted file mode 100644 index 3ab9df454..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/mcs.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: gateway - namespace: dubbo-system -spec: - addresses: - - value: istio-ingressgateway - type: Hostname - gatewayClassName: istio - listeners: - - name: default - port: 34000 - protocol: TCP ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: TCPRoute -metadata: - name: tcp - namespace: dubbo-system -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - rules: - - backendRefs: - - group: multicluster.x-k8s.io - kind: ServiceImport - name: echo - port: 80 diff --git a/pilot/pkg/config/kube/gateway/testdata/mcs.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/mcs.yaml.golden deleted file mode 100644 index 21aa94e3a..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/mcs.yaml.golden +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/gateway/default.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-default - namespace: dubbo-system -spec: - servers: - - hosts: - - dubbo-system/* - port: - name: default - number: 34000 - protocol: TCP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: TCPRoute/tcp.dubbo-system - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: tcp-tcp-istio-autogenerated-k8s-gateway - namespace: dubbo-system -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-default - hosts: - - '*' - tcp: - - route: - - destination: - host: echo.dubbo-system.svc.domain.suffix - port: - number: 80 ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/mesh.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/mesh.status.yaml.golden deleted file mode 100644 index ba1ddbb39..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/mesh.status.yaml.golden +++ /dev/null @@ -1,155 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - creationTimestamp: null - name: istio - namespace: default -spec: null -status: - conditions: - - lastTransitionTime: fake - message: Handled by Istio controller - reason: Accepted - status: "True" - type: Accepted ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: gateway - namespace: dubbo-system -spec: null -status: - addresses: - - type: IPAddress - value: 1.2.3.4 - conditions: - - lastTransitionTime: fake - message: Gateway valid, assigned to service(s) istio-ingressgateway.dubbo-system.svc.domain.suffix:80 - reason: ListenersValid - status: "True" - type: Ready - - lastTransitionTime: fake - message: Resources available - reason: ResourcesAvailable - status: "True" - type: Scheduled - listeners: - - attachedRoutes: 1 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: default - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: dual - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - kind: Mesh - name: istio - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: echo - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - kind: Mesh - name: istio ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: no-hostname - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: mesh requires hostname to be set - reason: InvalidParentReference - status: "False" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - kind: Mesh - name: istio ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: TLSRoute -metadata: - creationTimestamp: null - name: echo - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: mesh requires hostname to be set - reason: InvalidParentReference - status: "False" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - kind: Mesh - name: istio ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/mesh.yaml b/pilot/pkg/config/kube/gateway/testdata/mesh.yaml deleted file mode 100644 index 2df1df440..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/mesh.yaml +++ /dev/null @@ -1,86 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - name: istio -spec: - controllerName: istio.io/gateway-controller ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: gateway - namespace: dubbo-system -spec: - addresses: - - value: istio-ingressgateway - type: Hostname - gatewayClassName: istio - listeners: - - name: default - hostname: "*.example.com" - port: 80 - protocol: HTTP - allowedRoutes: - namespaces: - from: All ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: echo - namespace: default -spec: - parentRefs: - - kind: Mesh - name: istio - hostnames: ["echo.default.svc.cluster.local"] - rules: - - backendRefs: - - name: echo - port: 80 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: TLSRoute -metadata: - name: echo - namespace: default -spec: - parentRefs: - - kind: Mesh - name: istio - hostnames: ["some-sni.com"] - rules: - - backendRefs: - - name: echo - port: 80 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: dual # applies to mesh and explicit gateway - namespace: default -spec: - parentRefs: - - kind: Mesh - name: istio - - name: gateway - namespace: dubbo-system - hostnames: ["foo.example.com"] - rules: - - backendRefs: - - name: example - port: 80 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: no-hostname - namespace: default -spec: - parentRefs: - - kind: Mesh - name: istio - rules: - - backendRefs: - - name: example - port: 80 diff --git a/pilot/pkg/config/kube/gateway/testdata/mesh.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/mesh.yaml.golden deleted file mode 100644 index 5f2b32e9a..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/mesh.yaml.golden +++ /dev/null @@ -1,81 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/gateway/default.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-default - namespace: dubbo-system -spec: - servers: - - hosts: - - '*/*.example.com' - port: - name: default - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/echo.default - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: echo-0-istio-autogenerated-k8s-gateway - namespace: default -spec: - gateways: - - mesh - hosts: - - echo.default.svc.cluster.local - http: - - route: - - destination: - host: echo.default.svc.domain.suffix - port: - number: 80 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/dual.default - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: dual-0-istio-autogenerated-k8s-gateway - namespace: default -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-default - hosts: - - foo.example.com - http: - - route: - - destination: - host: example.default.svc.domain.suffix - port: - number: 80 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/dual.default - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: dual-1-istio-autogenerated-k8s-gateway - namespace: default -spec: - gateways: - - mesh - hosts: - - foo.example.com - http: - - route: - - destination: - host: example.default.svc.domain.suffix - port: - number: 80 ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/mismatch.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/mismatch.status.yaml.golden deleted file mode 100644 index 8176a6aaf..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/mismatch.status.yaml.golden +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - creationTimestamp: null - name: istio - namespace: default -spec: null -status: - conditions: - - lastTransitionTime: fake - message: Handled by Istio controller - reason: Accepted - status: "True" - type: Accepted ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: gateway - namespace: dubbo-system -spec: null ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/mismatch.yaml b/pilot/pkg/config/kube/gateway/testdata/mismatch.yaml deleted file mode 100644 index 5f23f41f3..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/mismatch.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Mismatch shows that we don't generate config for Gateways that do not match the GatewayClass -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - name: istio -spec: - controllerName: istio.io/gateway-controller ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: gateway - namespace: dubbo-system -spec: - addresses: - - value: istio-ingressgateway - type: Hostname - gatewayClassName: something-else - listeners: - - name: default - port: 80 - protocol: HTTP diff --git a/pilot/pkg/config/kube/gateway/testdata/mismatch.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/mismatch.yaml.golden deleted file mode 100644 index e69de29bb..000000000 diff --git a/pilot/pkg/config/kube/gateway/testdata/multi-gateway.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/multi-gateway.status.yaml.golden deleted file mode 100644 index ec84715cb..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/multi-gateway.status.yaml.golden +++ /dev/null @@ -1,94 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - creationTimestamp: null - name: istio - namespace: default -spec: null -status: - conditions: - - lastTransitionTime: fake - message: Handled by Istio controller - reason: Accepted - status: "True" - type: Accepted ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: gateway - namespace: dubbo-system -spec: null -status: - addresses: - - type: IPAddress - value: 1.2.3.4 - conditions: - - lastTransitionTime: fake - message: 'Assigned to service(s) example.com:34000, example.com:80, istio-ingressgateway.dubbo-system.svc.domain.suffix:34000, - and istio-ingressgateway.dubbo-system.svc.domain.suffix:80, but failed to assign - to all requested addresses: hostname "istio-ingressgateway.not-default.svc.domain.suffix" - not found' - reason: AddressNotAssigned - status: "False" - type: Ready - - lastTransitionTime: fake - message: Resources available - reason: ResourcesAvailable - status: "True" - type: Scheduled - listeners: - - attachedRoutes: 0 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: http - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute - - attachedRoutes: 0 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: tcp - supportedKinds: - - group: gateway.networking.k8s.io - kind: TCPRoute ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/multi-gateway.yaml b/pilot/pkg/config/kube/gateway/testdata/multi-gateway.yaml deleted file mode 100644 index e25a3df27..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/multi-gateway.yaml +++ /dev/null @@ -1,32 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - name: istio -spec: - controllerName: istio.io/gateway-controller ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: gateway - namespace: dubbo-system -spec: - gatewayClassName: istio - addresses: - - type: Hostname - value: istio-ingressgateway - - type: Hostname - value: istio-ingressgateway.not-default.svc.domain.suffix - - type: Hostname - value: example.com - listeners: - - name: http - hostname: "*.domain.example" - port: 80 - protocol: HTTP - - name: tcp - port: 34000 - protocol: TCP - allowedRoutes: - namespaces: - from: All diff --git a/pilot/pkg/config/kube/gateway/testdata/multi-gateway.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/multi-gateway.yaml.golden deleted file mode 100644 index 00a1eca42..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/multi-gateway.yaml.golden +++ /dev/null @@ -1,36 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix,istio-ingressgateway.not-default.svc.domain.suffix,example.com - internal.istio.io/parent: Gateway/gateway/http.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-http - namespace: dubbo-system -spec: - servers: - - hosts: - - dubbo-system/*.domain.example - port: - name: default - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix,istio-ingressgateway.not-default.svc.domain.suffix,example.com - internal.istio.io/parent: Gateway/gateway/tcp.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-tcp - namespace: dubbo-system -spec: - servers: - - hosts: - - '*/*' - port: - name: default - number: 34000 - protocol: TCP ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/reference-policy-tls.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/reference-policy-tls.status.yaml.golden deleted file mode 100644 index 38f09a69c..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/reference-policy-tls.status.yaml.golden +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - creationTimestamp: null - name: istio - namespace: default -spec: null -status: - conditions: - - lastTransitionTime: fake - message: Handled by Istio controller - reason: Accepted - status: "True" - type: Accepted ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: gateway - namespace: dubbo-system -spec: null -status: - conditions: - - lastTransitionTime: fake - message: 'failed to assign to any requested addresses: port 443 not found for - hostname "istio-ingressgateway.dubbo-system.svc.domain.suffix"' - reason: AddressNotAssigned - status: "False" - type: Ready - - lastTransitionTime: fake - message: Resources available - reason: ResourcesAvailable - status: "True" - type: Scheduled - listeners: - - attachedRoutes: 1 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: cross - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: http - namespace: cert -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/reference-policy-tls.yaml b/pilot/pkg/config/kube/gateway/testdata/reference-policy-tls.yaml deleted file mode 100644 index 0b827a615..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/reference-policy-tls.yaml +++ /dev/null @@ -1,62 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - name: istio -spec: - controllerName: istio.io/gateway-controller ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: gateway - namespace: dubbo-system -spec: - addresses: - - value: istio-ingressgateway - type: Hostname - gatewayClassName: istio - listeners: - - name: cross - hostname: "cert.domain.example" - port: 443 - protocol: HTTPS - allowedRoutes: - namespaces: - from: Selector - selector: - matchLabels: - kubernetes.io/metadata.name: "cert" - tls: - mode: Terminate - certificateRefs: - - name: cert - namespace: cert ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: ReferencePolicy -metadata: - name: alllow-cert - namespace: cert -spec: - from: - - group: networking.gateway.k8s.io - kind: Gateway - namespace: dubbo-system - to: - - group: "" - kind: Secret ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: http - namespace: cert -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - hostnames: ["cert.domain.example"] - rules: - - backendRefs: - - name: httpbin - port: 80 diff --git a/pilot/pkg/config/kube/gateway/testdata/reference-policy-tls.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/reference-policy-tls.yaml.golden deleted file mode 100644 index d25159873..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/reference-policy-tls.yaml.golden +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/gateway/cross.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-cross - namespace: dubbo-system -spec: - servers: - - hosts: - - cert/cert.domain.example - port: - name: default - number: 443 - protocol: HTTPS - tls: - credentialName: kubernetes-gateway://cert/cert - mode: SIMPLE ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/http.cert - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: http-0-istio-autogenerated-k8s-gateway - namespace: cert -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-cross - hosts: - - cert.domain.example - http: - - route: - - destination: - host: httpbin.cert.svc.domain.suffix - port: - number: 80 ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/route-binding.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/route-binding.status.yaml.golden deleted file mode 100644 index e87c85b11..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/route-binding.status.yaml.golden +++ /dev/null @@ -1,368 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - creationTimestamp: null - name: istio - namespace: default -spec: null -status: - conditions: - - lastTransitionTime: fake - message: Handled by Istio controller - reason: Accepted - status: "True" - type: Accepted ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: gateway - namespace: dubbo-system -spec: null -status: - addresses: - - type: IPAddress - value: 1.2.3.4 - conditions: - - lastTransitionTime: fake - message: Gateway valid, assigned to service(s) istio-ingressgateway.dubbo-system.svc.domain.suffix:80 - reason: ListenersValid - status: "True" - type: Ready - - lastTransitionTime: fake - message: Resources available - reason: ResourcesAvailable - status: "True" - type: Scheduled - listeners: - - attachedRoutes: 1 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: default - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute - - attachedRoutes: 3 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: foobar - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute - - attachedRoutes: 1 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: same-namespace - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute - - attachedRoutes: 1 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: scope-route - supportedKinds: [] - - attachedRoutes: 2 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: namespace-selector - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: bind-all - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: host-mismatch - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: no hostnames matched parent hostname "*.foobar.example" - reason: InvalidParentReference - status: "False" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system - sectionName: foobar ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: invalid-bind-cross-namespace - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: hostnames matched parent hostname "*.namespace-selector.example", but - namespace "default" is not allowed by the parent - reason: InvalidParentReference - status: "False" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system - sectionName: namespace-selector ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: same-namespace-invalid - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: no hostnames matched parent hostname "*.same-namespace.example" - reason: InvalidParentReference - status: "False" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - kind: Gateway - name: gateway - namespace: dubbo-system - sectionName: same-namespace ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: section-name-cross-namespace - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system - sectionName: foobar ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: bind-cross-namespace - namespace: group-namespace1 -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system - sectionName: namespace-selector ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: bind-cross-namespace - namespace: group-namespace2 -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system - sectionName: namespace-selector ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: same-namespace-valid - namespace: dubbo-system -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system - sectionName: same-namespace - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system - sectionName: foobar ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: TCPRoute -metadata: - creationTimestamp: null - name: wrong-protocol - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: kind gateway.networking.k8s.io/v1alpha2/TCPRoute is not allowed - reason: InvalidParentReference - status: "False" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system - sectionName: foobar ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/route-binding.yaml b/pilot/pkg/config/kube/gateway/testdata/route-binding.yaml deleted file mode 100644 index 9b93a35ba..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/route-binding.yaml +++ /dev/null @@ -1,198 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - name: istio -spec: - controllerName: istio.io/gateway-controller ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: gateway - namespace: dubbo-system -spec: - addresses: - - value: istio-ingressgateway - type: Hostname - gatewayClassName: istio - listeners: - - name: default - hostname: "*.domain.example" - port: 80 - protocol: HTTP - allowedRoutes: - namespaces: - from: All - - name: foobar - hostname: "*.foobar.example" - port: 80 - protocol: HTTP - allowedRoutes: - namespaces: - from: All - - name: same-namespace - hostname: "*.same-namespace.example" - port: 80 - protocol: HTTP - - name: scope-route - hostname: "*.scope-route.example" - port: 80 - protocol: HTTP - allowedRoutes: - namespaces: - from: All - kinds: - - kind: TCPRoute - - name: namespace-selector - hostname: "*.namespace-selector.example" - port: 80 - protocol: HTTP - allowedRoutes: - namespaces: - from: Selector - selector: - matchLabels: - istio.io/test-name-part: group ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: section-name-cross-namespace - namespace: default -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - sectionName: foobar - hostnames: ["alpha.foobar.example"] - rules: - - backendRefs: - - name: httpbin - port: 80 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: same-namespace-valid - namespace: dubbo-system -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - sectionName: foobar - - name: gateway - namespace: dubbo-system - sectionName: same-namespace - rules: - - backendRefs: - - name: httpbin - port: 81 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: same-namespace-invalid - namespace: default -spec: - parentRefs: - - kind: Gateway - name: gateway - namespace: dubbo-system - sectionName: same-namespace - hostnames: ["foo.same.example"] - rules: - - backendRefs: - - name: echo - port: 80 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: TCPRoute -metadata: - # Should not generate anything, the protocol is HTTP - name: wrong-protocol - namespace: default -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - sectionName: foobar - rules: - - backendRefs: - - name: httpbin - port: 82 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: host-mismatch - namespace: default -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - sectionName: foobar - hostnames: ["no.match.example"] - rules: - - backendRefs: - - name: httpbin - port: 84 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: bind-all - namespace: default -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - rules: - - backendRefs: - - name: httpbin - port: 85 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: bind-cross-namespace - namespace: group-namespace1 -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - sectionName: namespace-selector - rules: - - backendRefs: - - name: httpbin - port: 86 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: bind-cross-namespace - namespace: group-namespace2 -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - sectionName: namespace-selector - rules: - - backendRefs: - - name: httpbin - port: 87 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: invalid-bind-cross-namespace - namespace: default -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - sectionName: namespace-selector - rules: - - backendRefs: - - name: httpbin - port: 87 - diff --git a/pilot/pkg/config/kube/gateway/testdata/route-binding.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/route-binding.yaml.golden deleted file mode 100644 index 90735fe6b..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/route-binding.yaml.golden +++ /dev/null @@ -1,227 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/gateway/default.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-default - namespace: dubbo-system -spec: - servers: - - hosts: - - '*/*.domain.example' - port: - name: default - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/gateway/foobar.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-foobar - namespace: dubbo-system -spec: - servers: - - hosts: - - '*/*.foobar.example' - port: - name: default - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/gateway/same-namespace.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-same-namespace - namespace: dubbo-system -spec: - servers: - - hosts: - - dubbo-system/*.same-namespace.example - port: - name: default - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/gateway/scope-route.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-scope-route - namespace: dubbo-system -spec: - servers: - - hosts: - - '*/*.scope-route.example' - port: - name: default - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/gateway/namespace-selector.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-namespace-selector - namespace: dubbo-system -spec: - servers: - - hosts: - - group-namespace1/*.namespace-selector.example - - group-namespace2/*.namespace-selector.example - port: - name: default - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/section-name-cross-namespace.default - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: section-name-cross-namespace-0-istio-autogenerated-k8s-gateway - namespace: default -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-foobar - hosts: - - alpha.foobar.example - http: - - route: - - destination: - host: httpbin.default.svc.domain.suffix - port: - number: 80 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/same-namespace-valid.dubbo-system - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: same-namespace-valid-0-istio-autogenerated-k8s-gateway - namespace: dubbo-system -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-same-namespace - hosts: - - '*' - http: - - route: - - destination: - host: httpbin.dubbo-system.svc.domain.suffix - port: - number: 81 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/bind-all.default - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: bind-all-0-istio-autogenerated-k8s-gateway - namespace: default -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-default - hosts: - - '*' - http: - - route: - - destination: - host: httpbin.default.svc.domain.suffix - port: - number: 85 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/bind-all.default - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: bind-all-1-istio-autogenerated-k8s-gateway - namespace: default -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-foobar - hosts: - - '*' - http: - - route: - - destination: - host: httpbin.default.svc.domain.suffix - port: - number: 85 - - route: - - destination: - host: httpbin.dubbo-system.svc.domain.suffix - port: - number: 81 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/bind-all.default - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: bind-all-2-istio-autogenerated-k8s-gateway - namespace: default -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-scope-route - hosts: - - '*' - http: - - route: - - destination: - host: httpbin.default.svc.domain.suffix - port: - number: 85 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/bind-cross-namespace.group-namespace1 - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: bind-cross-namespace-0-istio-autogenerated-k8s-gateway - namespace: group-namespace1 -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-namespace-selector - hosts: - - '*' - http: - - route: - - destination: - host: httpbin.group-namespace1.svc.domain.suffix - port: - number: 86 - - route: - - destination: - host: httpbin.group-namespace2.svc.domain.suffix - port: - number: 87 ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/route-precedence.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/route-precedence.status.yaml.golden deleted file mode 100644 index 82637223e..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/route-precedence.status.yaml.golden +++ /dev/null @@ -1,146 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - creationTimestamp: null - name: istio - namespace: default -spec: null -status: - conditions: - - lastTransitionTime: fake - message: Handled by Istio controller - reason: Accepted - status: "True" - type: Accepted ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: gateway - namespace: dubbo-system -spec: null -status: - addresses: - - type: IPAddress - value: 1.2.3.4 - conditions: - - lastTransitionTime: fake - message: Gateway valid, assigned to service(s) istio-ingressgateway.dubbo-system.svc.domain.suffix:80 - reason: ListenersValid - status: "True" - type: Ready - - lastTransitionTime: fake - message: Resources available - reason: ResourcesAvailable - status: "True" - type: Scheduled - listeners: - - attachedRoutes: 2 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: default - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: http - namespace: allowed-1 -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - kind: Mesh - name: istio - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: http - namespace: allowed-2 -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - kind: Mesh - name: istio - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: http - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: hostnames matched parent hostname "*.domain.example", but namespace - "default" is not allowed by the parent - reason: InvalidParentReference - status: "False" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/route-precedence.yaml b/pilot/pkg/config/kube/gateway/testdata/route-precedence.yaml deleted file mode 100644 index 81b719c98..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/route-precedence.yaml +++ /dev/null @@ -1,130 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - name: istio -spec: - controllerName: istio.io/gateway-controller ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: gateway - namespace: dubbo-system -spec: - addresses: - - value: istio-ingressgateway - type: Hostname - gatewayClassName: istio - listeners: - - name: default - hostname: "*.domain.example" - port: 80 - protocol: HTTP - allowedRoutes: - namespaces: - from: Selector - selector: - matchLabels: - istio.io/test-name-part: allowed ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: http - namespace: allowed-1 -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - - kind: Mesh - name: istio - hostnames: ["a.domain.example", "b.domain.example"] - rules: - - matches: - - path: - type: PathPrefix - value: /foo - headers: - - name: my-header - value: some-value - type: Exact - backendRefs: - - name: svc1 - port: 80 - - matches: - - path: - type: RegularExpression - value: /foo((\/).*)? - backendRefs: - - name: svc2 - port: 80 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: http - namespace: allowed-2 -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - - kind: Mesh - name: istio - hostnames: ["a.domain.example"] - rules: - - matches: - - path: - type: PathPrefix - value: /foo/bar - - path: - type: PathPrefix - value: /bar - backendRefs: - - name: svc2 - port: 80 - - matches: - - path: - type: Exact - value: /baz - headers: - - name: my-header - value: some-value - type: Exact - queryParams: - - name: my-param - value: some-value - type: RegularExpression - backendRefs: - - name: svc2 - port: 80 - - matches: - - path: - type: PathPrefix - value: / - backendRefs: - - name: svc3 - port: 80 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: http - namespace: default -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - hostnames: ["a.domain.example", "b.domain.example"] - rules: - - matches: - - path: - type: PathPrefix - value: /abc - headers: - - name: my-header - value: some-value - type: Exact - backendRefs: - - name: svc4 - port: 80 ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/route-precedence.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/route-precedence.yaml.golden deleted file mode 100644 index 3bc0b4548..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/route-precedence.yaml.golden +++ /dev/null @@ -1,251 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/gateway/default.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-default - namespace: dubbo-system -spec: - servers: - - hosts: - - allowed-1/*.domain.example - - allowed-2/*.domain.example - port: - name: default - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/http.allowed-1 - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: http-0-istio-autogenerated-k8s-gateway - namespace: allowed-1 -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-default - hosts: - - a.domain.example - http: - - match: - - uri: - regex: /foo((\/).*)? - route: - - destination: - host: svc2.allowed-1.svc.domain.suffix - port: - number: 80 - - match: - - uri: - prefix: /foo/bar - route: - - destination: - host: svc2.allowed-2.svc.domain.suffix - port: - number: 80 - - match: - - headers: - my-header: - exact: some-value - queryParams: - my-param: - regex: some-value - uri: - exact: /baz - route: - - destination: - host: svc2.allowed-2.svc.domain.suffix - port: - number: 80 - - match: - - headers: - my-header: - exact: some-value - uri: - prefix: /foo - route: - - destination: - host: svc1.allowed-1.svc.domain.suffix - port: - number: 80 - - match: - - uri: - prefix: /bar - route: - - destination: - host: svc2.allowed-2.svc.domain.suffix - port: - number: 80 - - match: - - uri: - prefix: / - route: - - destination: - host: svc3.allowed-2.svc.domain.suffix - port: - number: 80 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/http.allowed-1 - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: http-1-istio-autogenerated-k8s-gateway - namespace: allowed-1 -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-default - hosts: - - b.domain.example - http: - - match: - - uri: - regex: /foo((\/).*)? - route: - - destination: - host: svc2.allowed-1.svc.domain.suffix - port: - number: 80 - - match: - - headers: - my-header: - exact: some-value - uri: - prefix: /foo - route: - - destination: - host: svc1.allowed-1.svc.domain.suffix - port: - number: 80 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/http.allowed-1 - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: http-2-istio-autogenerated-k8s-gateway - namespace: allowed-1 -spec: - gateways: - - mesh - hosts: - - a.domain.example - http: - - match: - - uri: - regex: /foo((\/).*)? - route: - - destination: - host: svc2.allowed-1.svc.domain.suffix - port: - number: 80 - - match: - - headers: - my-header: - exact: some-value - uri: - prefix: /foo - route: - - destination: - host: svc1.allowed-1.svc.domain.suffix - port: - number: 80 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/http.allowed-1 - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: http-3-istio-autogenerated-k8s-gateway - namespace: allowed-1 -spec: - gateways: - - mesh - hosts: - - b.domain.example - http: - - match: - - uri: - regex: /foo((\/).*)? - route: - - destination: - host: svc2.allowed-1.svc.domain.suffix - port: - number: 80 - - match: - - headers: - my-header: - exact: some-value - uri: - prefix: /foo - route: - - destination: - host: svc1.allowed-1.svc.domain.suffix - port: - number: 80 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/http.allowed-2 - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: http-0-istio-autogenerated-k8s-gateway - namespace: allowed-2 -spec: - gateways: - - mesh - hosts: - - a.domain.example - http: - - match: - - uri: - prefix: /foo/bar - route: - - destination: - host: svc2.allowed-2.svc.domain.suffix - port: - number: 80 - - match: - - headers: - my-header: - exact: some-value - queryParams: - my-param: - regex: some-value - uri: - exact: /baz - route: - - destination: - host: svc2.allowed-2.svc.domain.suffix - port: - number: 80 - - match: - - uri: - prefix: /bar - route: - - destination: - host: svc2.allowed-2.svc.domain.suffix - port: - number: 80 - - match: - - uri: - prefix: / - route: - - destination: - host: svc3.allowed-2.svc.domain.suffix - port: - number: 80 ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/serviceentry.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/serviceentry.status.yaml.golden deleted file mode 100644 index ec3305d06..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/serviceentry.status.yaml.golden +++ /dev/null @@ -1,68 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: gateway - namespace: dubbo-system -spec: null -status: - conditions: - - lastTransitionTime: fake - message: 'failed to assign to any requested addresses: hostname "gateway.dubbo-system.svc.domain.suffix" - not found' - reason: AddressNotAssigned - status: "False" - type: Ready - - lastTransitionTime: fake - message: Resources not yet deployed to the cluster - reason: ResourcesPending - status: "False" - type: Scheduled - listeners: - - attachedRoutes: 1 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: default - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: http - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/serviceentry.yaml b/pilot/pkg/config/kube/gateway/testdata/serviceentry.yaml deleted file mode 100644 index d769ae230..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/serviceentry.yaml +++ /dev/null @@ -1,32 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: gateway - namespace: dubbo-system -spec: - gatewayClassName: istio - listeners: - - name: default - port: 80 - protocol: HTTP - allowedRoutes: - namespaces: - from: All ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: http - namespace: default -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - rules: - - - backendRefs: - - kind: Hostname - group: networking.istio.io - name: google.com - port: 80 ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/serviceentry.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/serviceentry.yaml.golden deleted file mode 100644 index 8883a3469..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/serviceentry.yaml.golden +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: gateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/gateway/default.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-default - namespace: dubbo-system -spec: - servers: - - hosts: - - '*/*' - port: - name: default - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/http.default - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: http-0-istio-autogenerated-k8s-gateway - namespace: default -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-default - hosts: - - '*' - http: - - route: - - destination: - host: google.com - port: - number: 80 ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/tcp.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/tcp.status.yaml.golden deleted file mode 100644 index b37f3634d..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/tcp.status.yaml.golden +++ /dev/null @@ -1,85 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - creationTimestamp: null - name: istio - namespace: default -spec: null -status: - conditions: - - lastTransitionTime: fake - message: Handled by Istio controller - reason: Accepted - status: "True" - type: Accepted ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: gateway - namespace: dubbo-system -spec: null -status: - addresses: - - type: IPAddress - value: 1.2.3.4 - conditions: - - lastTransitionTime: fake - message: Gateway valid, assigned to service(s) istio-ingressgateway.dubbo-system.svc.domain.suffix:34000 - reason: ListenersValid - status: "True" - type: Ready - - lastTransitionTime: fake - message: Resources available - reason: ResourcesAvailable - status: "True" - type: Scheduled - listeners: - - attachedRoutes: 1 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: default - supportedKinds: - - group: gateway.networking.k8s.io - kind: TCPRoute ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: TCPRoute -metadata: - creationTimestamp: null - name: tcp - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/tcp.yaml b/pilot/pkg/config/kube/gateway/testdata/tcp.yaml deleted file mode 100644 index 749e09b44..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/tcp.yaml +++ /dev/null @@ -1,38 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - name: istio -spec: - controllerName: istio.io/gateway-controller ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: gateway - namespace: dubbo-system -spec: - addresses: - - value: istio-ingressgateway - type: Hostname - gatewayClassName: istio - listeners: - - name: default - port: 34000 - protocol: TCP - allowedRoutes: - namespaces: - from: All ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: TCPRoute -metadata: - name: tcp - namespace: default -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - rules: - - backendRefs: - - name: httpbin - port: 9090 diff --git a/pilot/pkg/config/kube/gateway/testdata/tcp.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/tcp.yaml.golden deleted file mode 100644 index 40a68b5d1..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/tcp.yaml.golden +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/gateway/default.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-default - namespace: dubbo-system -spec: - servers: - - hosts: - - '*/*' - port: - name: default - number: 34000 - protocol: TCP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: TCPRoute/tcp.default - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: tcp-tcp-istio-autogenerated-k8s-gateway - namespace: default -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-default - hosts: - - '*' - tcp: - - route: - - destination: - host: httpbin.default.svc.domain.suffix - port: - number: 9090 ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/tls.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/tls.status.yaml.golden deleted file mode 100644 index 8386ba4eb..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/tls.status.yaml.golden +++ /dev/null @@ -1,151 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - creationTimestamp: null - name: istio - namespace: default -spec: null -status: - conditions: - - lastTransitionTime: fake - message: Handled by Istio controller - reason: Accepted - status: "True" - type: Accepted ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: gateway - namespace: dubbo-system -spec: null -status: - addresses: - - type: IPAddress - value: 1.2.3.4 - conditions: - - lastTransitionTime: fake - message: Gateway valid, assigned to service(s) istio-ingressgateway.dubbo-system.svc.domain.suffix:34000 - reason: ListenersValid - status: "True" - type: Ready - - lastTransitionTime: fake - message: Resources available - reason: ResourcesAvailable - status: "True" - type: Scheduled - listeners: - - attachedRoutes: 2 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: passthrough - supportedKinds: - - group: gateway.networking.k8s.io - kind: TLSRoute - - attachedRoutes: 1 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: terminate - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: http - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: TLSRoute -metadata: - creationTimestamp: null - name: tls - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: TLSRoute -metadata: - creationTimestamp: null - name: tls-match - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/tls.yaml b/pilot/pkg/config/kube/gateway/testdata/tls.yaml deleted file mode 100644 index 046cc493e..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/tls.yaml +++ /dev/null @@ -1,82 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - name: istio -spec: - controllerName: istio.io/gateway-controller ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: gateway - namespace: dubbo-system -spec: - addresses: - - value: istio-ingressgateway - type: Hostname - gatewayClassName: istio - listeners: - - name: passthrough - port: 34000 - protocol: TLS - allowedRoutes: - namespaces: - from: All - tls: - mode: Passthrough - - name: terminate - hostname: "domain.example" - port: 34000 - protocol: HTTPS - allowedRoutes: - namespaces: - from: All - tls: - mode: Terminate - certificateRefs: - - name: my-cert-http ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: TLSRoute -metadata: - name: tls - namespace: default -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - rules: - - backendRefs: - - name: httpbin - port: 443 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: TLSRoute -metadata: - name: tls-match - namespace: default -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - hostnames: - - "foo.com" - rules: - - backendRefs: - - name: httpbin-foo - port: 443 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: http - namespace: default -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - hostnames: ["domain.example"] - rules: - - backendRefs: - - name: httpbin - port: 80 \ No newline at end of file diff --git a/pilot/pkg/config/kube/gateway/testdata/tls.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/tls.yaml.golden deleted file mode 100644 index b7c2713c3..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/tls.yaml.golden +++ /dev/null @@ -1,109 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/gateway/passthrough.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-passthrough - namespace: dubbo-system -spec: - servers: - - hosts: - - '*/*' - port: - name: default - number: 34000 - protocol: TLS - tls: {} ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/gateway/terminate.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-terminate - namespace: dubbo-system -spec: - servers: - - hosts: - - '*/domain.example' - port: - name: default - number: 34000 - protocol: HTTPS - tls: - credentialName: kubernetes-gateway://dubbo-system/my-cert-http - mode: SIMPLE ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: TLSRoute/tls.default - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: tls-tls-0-istio-autogenerated-k8s-gateway - namespace: default -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-passthrough - hosts: - - '*' - tls: - - match: - - sniHosts: - - '*' - route: - - destination: - host: httpbin.default.svc.domain.suffix - port: - number: 443 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: TLSRoute/tls-match.default - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: tls-match-tls-0-istio-autogenerated-k8s-gateway - namespace: default -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-passthrough - hosts: - - foo.com - tls: - - match: - - sniHosts: - - foo.com - route: - - destination: - host: httpbin-foo.default.svc.domain.suffix - port: - number: 443 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/http.default - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: http-0-istio-autogenerated-k8s-gateway - namespace: default -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-terminate - hosts: - - domain.example - http: - - route: - - destination: - host: httpbin.default.svc.domain.suffix - port: - number: 80 ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/weighted.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/weighted.status.yaml.golden deleted file mode 100644 index 61626a1e9..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/weighted.status.yaml.golden +++ /dev/null @@ -1,132 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - creationTimestamp: null - name: istio - namespace: default -spec: null -status: - conditions: - - lastTransitionTime: fake - message: Handled by Istio controller - reason: Accepted - status: "True" - type: Accepted ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: gateway - namespace: dubbo-system -spec: null -status: - addresses: - - type: IPAddress - value: 1.2.3.4 - conditions: - - lastTransitionTime: fake - message: Gateway valid, assigned to service(s) istio-ingressgateway.dubbo-system.svc.domain.suffix:34000 - and istio-ingressgateway.dubbo-system.svc.domain.suffix:80 - reason: ListenersValid - status: "True" - type: Ready - - lastTransitionTime: fake - message: Resources available - reason: ResourcesAvailable - status: "True" - type: Scheduled - listeners: - - attachedRoutes: 1 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: http - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute - - attachedRoutes: 1 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: tcp - supportedKinds: - - group: gateway.networking.k8s.io - kind: TCPRoute ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: http - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: TCPRoute -metadata: - creationTimestamp: null - name: tcp - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/weighted.yaml b/pilot/pkg/config/kube/gateway/testdata/weighted.yaml deleted file mode 100644 index 5225be20d..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/weighted.yaml +++ /dev/null @@ -1,89 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - name: istio -spec: - controllerName: istio.io/gateway-controller ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: gateway - namespace: dubbo-system -spec: - addresses: - - value: istio-ingressgateway - type: Hostname - gatewayClassName: istio - listeners: - - name: http - hostname: "*.domain.example" - port: 80 - protocol: HTTP - allowedRoutes: - namespaces: - from: All - - name: tcp - port: 34000 - protocol: TCP - allowedRoutes: - namespaces: - from: All ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: http - namespace: default -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - hostnames: ["first.domain.example"] - rules: - - matches: - - path: - type: PathPrefix - value: /get - backendRefs: - - name: httpbin - port: 80 - weight: 2 - - name: httpbin-other - port: 8080 - weight: 3 - - name: httpbin-zero - port: 8080 - weight: 0 - - matches: - - path: - type: PathPrefix - value: /weighted-100 - backendRefs: - - filters: - - requestHeaderModifier: - add: - - name: foo - value: bar - type: RequestHeaderModifier - port: 8000 - name: foo-svc - weight: 100 ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: TCPRoute -metadata: - name: tcp - namespace: default -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - rules: - - backendRefs: - - name: httpbin - port: 9090 - weight: 1 - - name: httpbin-alt - port: 9090 - weight: 2 diff --git a/pilot/pkg/config/kube/gateway/testdata/weighted.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/weighted.yaml.golden deleted file mode 100644 index f14fd65a0..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/weighted.yaml.golden +++ /dev/null @@ -1,105 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/gateway/http.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-http - namespace: dubbo-system -spec: - servers: - - hosts: - - '*/*.domain.example' - port: - name: default - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/gateway/tcp.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-tcp - namespace: dubbo-system -spec: - servers: - - hosts: - - '*/*' - port: - name: default - number: 34000 - protocol: TCP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: TCPRoute/tcp.default - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: tcp-tcp-istio-autogenerated-k8s-gateway - namespace: default -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-tcp - hosts: - - '*' - tcp: - - route: - - destination: - host: httpbin.default.svc.domain.suffix - port: - number: 9090 - weight: 33 - - destination: - host: httpbin-alt.default.svc.domain.suffix - port: - number: 9090 - weight: 67 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/http.default - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: http-0-istio-autogenerated-k8s-gateway - namespace: default -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-http - hosts: - - first.domain.example - http: - - match: - - uri: - prefix: /weighted-100 - route: - - destination: - host: foo-svc.default.svc.domain.suffix - port: - number: 8000 - headers: - request: - add: - foo: bar - - match: - - uri: - prefix: /get - route: - - destination: - host: httpbin.default.svc.domain.suffix - port: - number: 80 - weight: 40 - - destination: - host: httpbin-other.default.svc.domain.suffix - port: - number: 8080 - weight: 60 ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/zero.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/zero.status.yaml.golden deleted file mode 100644 index 236cc5f77..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/zero.status.yaml.golden +++ /dev/null @@ -1,85 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - creationTimestamp: null - name: istio - namespace: default -spec: null -status: - conditions: - - lastTransitionTime: fake - message: Handled by Istio controller - reason: Accepted - status: "True" - type: Accepted ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - creationTimestamp: null - name: gateway - namespace: dubbo-system -spec: null -status: - addresses: - - type: IPAddress - value: 1.2.3.4 - conditions: - - lastTransitionTime: fake - message: Gateway valid, assigned to service(s) istio-ingressgateway.dubbo-system.svc.domain.suffix:80 - reason: ListenersValid - status: "True" - type: Ready - - lastTransitionTime: fake - message: Resources available - reason: ResourcesAvailable - status: "True" - type: Scheduled - listeners: - - attachedRoutes: 1 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "False" - type: Detached - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: Ready - - lastTransitionTime: fake - message: No errors found - reason: ListenerReady - status: "True" - type: ResolvedRefs - name: default - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - creationTimestamp: null - name: http - namespace: default -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: RouteAdmitted - status: "True" - type: Accepted - controllerName: istio.io/gateway-controller - parentRef: - name: gateway - namespace: dubbo-system ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/zero.yaml b/pilot/pkg/config/kube/gateway/testdata/zero.yaml deleted file mode 100644 index 48e46e568..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/zero.yaml +++ /dev/null @@ -1,59 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: GatewayClass -metadata: - name: istio -spec: - controllerName: istio.io/gateway-controller ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: Gateway -metadata: - name: gateway - namespace: dubbo-system -spec: - addresses: - - value: istio-ingressgateway - type: Hostname - gatewayClassName: istio - listeners: - - name: default - hostname: "*.domain.example" - port: 80 - protocol: HTTP - allowedRoutes: - namespaces: - from: All ---- -apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: http - namespace: default -spec: - parentRefs: - - name: gateway - namespace: dubbo-system - hostnames: ["first.domain.example"] - rules: - - matches: - - path: - type: PathPrefix - value: /get - backendRefs: - - name: httpbin-zero - port: 8080 - weight: 0 - - matches: - - path: - type: PathPrefix - value: /weighted-100 - backendRefs: - - filters: - - requestHeaderModifier: - add: - - name: foo - value: bar - type: RequestHeaderModifier - port: 8000 - name: foo-svc - weight: 100 diff --git a/pilot/pkg/config/kube/gateway/testdata/zero.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/zero.yaml.golden deleted file mode 100644 index a8a7091ec..000000000 --- a/pilot/pkg/config/kube/gateway/testdata/zero.yaml.golden +++ /dev/null @@ -1,59 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-service: istio-ingressgateway.dubbo-system.svc.domain.suffix - internal.istio.io/parent: Gateway/gateway/default.dubbo-system - creationTimestamp: null - name: gateway-istio-autogenerated-k8s-gateway-default - namespace: dubbo-system -spec: - servers: - - hosts: - - '*/*.domain.example' - port: - name: default - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parent: HTTPRoute/http.default - internal.istio.io/route-semantics: gateway - creationTimestamp: null - name: http-0-istio-autogenerated-k8s-gateway - namespace: default -spec: - gateways: - - dubbo-system/gateway-istio-autogenerated-k8s-gateway-default - hosts: - - first.domain.example - http: - - match: - - uri: - prefix: /weighted-100 - route: - - destination: - host: foo-svc.default.svc.domain.suffix - port: - number: 8000 - headers: - request: - add: - foo: bar - - fault: - abort: - httpStatus: 503 - percentage: - value: 100 - match: - - uri: - prefix: /get - route: - - destination: - host: httpbin-zero.default.svc.domain.suffix - port: - number: 8080 ---- diff --git a/pilot/pkg/config/kube/ingress/controller.go b/pilot/pkg/config/kube/ingress/controller.go deleted file mode 100644 index 64cfe2fb0..000000000 --- a/pilot/pkg/config/kube/ingress/controller.go +++ /dev/null @@ -1,402 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package ingress provides a read-only view of Kubernetes ingress resources -// as an ingress rule configuration type store -package ingress - -import ( - "errors" - "fmt" - "sort" - "sync" -) - -import ( - "github.com/hashicorp/go-multierror" - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/env" - "istio.io/pkg/log" - ingress "k8s.io/api/networking/v1beta1" - kerrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/version" - "k8s.io/client-go/informers/networking/v1beta1" - listerv1 "k8s.io/client-go/listers/core/v1" - networkinglister "k8s.io/client-go/listers/networking/v1beta1" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - kubecontroller "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube/controller" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/controllers" -) - -// In 1.0, the Gateway is defined in the namespace where the actual controller runs, and needs to be managed by -// user. -// The gateway is named by appending "-istio-autogenerated-k8s-ingress" to the name of the ingress. -// -// Currently the gateway namespace is hardcoded to dubbo-system (model.IstioIngressNamespace) -// -// VirtualServices are also auto-generated in the model.IstioIngressNamespace. -// -// The sync of Ingress objects to IP is done by status.go -// the 'ingress service' name is used to get the IP of the Service -// If ingress service is empty, it falls back to NodeExternalIP list, selected using the labels. -// This is using 'namespace' of pilot - but seems to be broken (never worked), since it uses Pilot's pod labels -// instead of the ingress labels. -// Follows mesh.IngressControllerMode setting to enable - OFF|STRICT|DEFAULT. -// STRICT requires "kubernetes.io/ingress.class" == mesh.IngressClass -// DEFAULT allows Ingress without explicit class. -// In 1.1: -// - K8S_INGRESS_NS - namespace of the Gateway that will act as ingress. -// - labels of the gateway set to "app=ingressgateway" for node_port, service set to 'ingressgateway' (matching default install) -// If we need more flexibility - we can add it (but likely we'll deprecate ingress support first) -// - -var schemas = collection.SchemasFor( - collections.IstioNetworkingV1Alpha3Virtualservices, - collections.IstioNetworkingV1Alpha3Gateways) - -// Control needs RBAC permissions to write to Pods. - -type controller struct { - meshWatcher mesh.Holder - domainSuffix string - - queue controllers.Queue - virtualServiceHandlers []model.EventHandler - gatewayHandlers []model.EventHandler - - mutex sync.RWMutex - // processed ingresses - ingresses map[types.NamespacedName]*ingress.Ingress - - ingressInformer cache.SharedInformer - ingressLister networkinglister.IngressLister - serviceInformer cache.SharedInformer - serviceLister listerv1.ServiceLister - // May be nil if ingress class is not supported in the cluster - classes v1beta1.IngressClassInformer -} - -// TODO: move to features ( and remove in 1.2 ) -var ingressNamespace = env.RegisterStringVar("K8S_INGRESS_NS", "", "").Get() - -var errUnsupportedOp = errors.New("unsupported operation: the ingress config store is a read-only view") - -// Check if the "networking/v1" Ingress is available. Implementation borrowed from ingress-nginx -func V1Available(client kube.Client) bool { - // check kubernetes version to use new ingress package or not - version119, _ := version.ParseGeneric("v1.19.0") - - serverVersion, err := client.GetKubernetesVersion() - if err != nil { - return false - } - - runningVersion, err := version.ParseGeneric(serverVersion.String()) - if err != nil { - log.Errorf("unexpected error parsing running Kubernetes version: %v", err) - return false - } - - return runningVersion.AtLeast(version119) -} - -// Check if the "networking" group Ingress is available. Implementation borrowed from ingress-nginx -func NetworkingIngressAvailable(client kube.Client) bool { - // check kubernetes version to use new ingress package or not - version118, _ := version.ParseGeneric("v1.18.0") - - serverVersion, err := client.GetKubernetesVersion() - if err != nil { - return false - } - - runningVersion, err := version.ParseGeneric(serverVersion.String()) - if err != nil { - log.Errorf("unexpected error parsing running Kubernetes version: %v", err) - return false - } - - return runningVersion.AtLeast(version118) -} - -// NewController creates a new Kubernetes controller -func NewController(client kube.Client, meshWatcher mesh.Holder, - options kubecontroller.Options) model.ConfigStoreController { - if ingressNamespace == "" { - ingressNamespace = constants.IstioIngressNamespace - } - - ingressInformer := client.KubeInformer().Networking().V1beta1().Ingresses() - serviceInformer := client.KubeInformer().Core().V1().Services() - - var classes v1beta1.IngressClassInformer - if NetworkingIngressAvailable(client) { - classes = client.KubeInformer().Networking().V1beta1().IngressClasses() - // Register the informer now, so it will be properly started - _ = classes.Informer() - } else { - log.Infof("Skipping IngressClass, resource not supported") - } - - c := &controller{ - meshWatcher: meshWatcher, - domainSuffix: options.DomainSuffix, - ingresses: make(map[types.NamespacedName]*ingress.Ingress), - ingressInformer: ingressInformer.Informer(), - ingressLister: ingressInformer.Lister(), - classes: classes, - serviceInformer: serviceInformer.Informer(), - serviceLister: serviceInformer.Lister(), - } - - c.queue = controllers.NewQueue("ingress", - controllers.WithReconciler(c.onEvent), - controllers.WithMaxAttempts(5)) - c.ingressInformer.AddEventHandler(controllers.ObjectHandler(c.queue.AddObject)) - - return c -} - -func (c *controller) Run(stop <-chan struct{}) { - c.queue.Run(stop) -} - -func (c *controller) shouldProcessIngress(mesh *meshconfig.MeshConfig, i *ingress.Ingress) (bool, error) { - var class *ingress.IngressClass - if c.classes != nil && i.Spec.IngressClassName != nil { - c, err := c.classes.Lister().Get(*i.Spec.IngressClassName) - if err != nil && !kerrors.IsNotFound(err) { - return false, fmt.Errorf("failed to get ingress class %v: %v", i.Spec.IngressClassName, err) - } - class = c - } - return shouldProcessIngressWithClass(mesh, i, class), nil -} - -// shouldProcessIngressUpdate checks whether we should renotify registered handlers about an update event -func (c *controller) shouldProcessIngressUpdate(ing *ingress.Ingress) (bool, error) { - shouldProcess, err := c.shouldProcessIngress(c.meshWatcher.Mesh(), ing) - if err != nil { - return false, err - } - item := types.NamespacedName{Name: ing.Name, Namespace: ing.Namespace} - if shouldProcess { - // record processed ingress - c.mutex.Lock() - c.ingresses[item] = ing - c.mutex.Unlock() - return true, nil - } - - c.mutex.Lock() - _, preProcessed := c.ingresses[item] - // previous processed but should not currently, delete it - if preProcessed && !shouldProcess { - delete(c.ingresses, item) - } else { - c.ingresses[item] = ing - } - c.mutex.Unlock() - - return preProcessed, nil -} - -func (c *controller) onEvent(item types.NamespacedName) error { - event := model.EventUpdate - ing, err := c.ingressLister.Ingresses(item.Namespace).Get(item.Name) - if err != nil { - if kerrors.IsNotFound(err) { - event = model.EventDelete - c.mutex.Lock() - ing = c.ingresses[item] - delete(c.ingresses, item) - c.mutex.Unlock() - } else { - return err - } - } - - // ingress deleted, and it is not processed before - if ing == nil { - return nil - } - // we should check need process only when event is not delete, - // if it is delete event, and previously processed, we need to process too. - if event != model.EventDelete { - shouldProcess, err := c.shouldProcessIngressUpdate(ing) - if err != nil { - return err - } - if !shouldProcess { - return nil - } - } - - vsmetadata := config.Meta{ - Name: item.Name + "-" + "virtualservice", - Namespace: item.Namespace, - GroupVersionKind: gvk.VirtualService, - // Set this label so that we do not compare configs and just push. - Labels: map[string]string{constants.AlwaysPushLabel: "true"}, - } - gatewaymetadata := config.Meta{ - Name: item.Name + "-" + "gateway", - Namespace: item.Namespace, - GroupVersionKind: gvk.Gateway, - // Set this label so that we do not compare configs and just push. - Labels: map[string]string{constants.AlwaysPushLabel: "true"}, - } - - // Trigger updates for Gateway and VirtualService - // TODO: we could be smarter here and only trigger when real changes were found - for _, f := range c.virtualServiceHandlers { - f(config.Config{Meta: vsmetadata}, config.Config{Meta: vsmetadata}, event) - } - for _, f := range c.gatewayHandlers { - f(config.Config{Meta: gatewaymetadata}, config.Config{Meta: gatewaymetadata}, event) - } - - return nil -} - -func (c *controller) RegisterEventHandler(kind config.GroupVersionKind, f model.EventHandler) { - switch kind { - case gvk.VirtualService: - c.virtualServiceHandlers = append(c.virtualServiceHandlers, f) - case gvk.Gateway: - c.gatewayHandlers = append(c.gatewayHandlers, f) - } -} - -func (c *controller) SetWatchErrorHandler(handler func(r *cache.Reflector, err error)) error { - var errs error - if err := c.serviceInformer.SetWatchErrorHandler(handler); err != nil { - errs = multierror.Append(err, errs) - } - if err := c.ingressInformer.SetWatchErrorHandler(handler); err != nil { - errs = multierror.Append(err, errs) - } - if c.classes != nil { - if err := c.classes.Informer().SetWatchErrorHandler(handler); err != nil { - errs = multierror.Append(err, errs) - } - } - return errs -} - -func (c *controller) HasSynced() bool { - // TODO: add c.queue.HasSynced() once #36332 is ready, ensuring Run is called before HasSynced - return c.ingressInformer.HasSynced() && c.serviceInformer.HasSynced() && - (c.classes == nil || c.classes.Informer().HasSynced()) -} - -func (c *controller) Schemas() collection.Schemas { - // TODO: are these two config descriptors right? - return schemas -} - -func (c *controller) Get(typ config.GroupVersionKind, name, namespace string) *config.Config { - return nil -} - -// sortIngressByCreationTime sorts the list of config objects in ascending order by their creation time (if available). -func sortIngressByCreationTime(configs []interface{}) []*ingress.Ingress { - ingr := make([]*ingress.Ingress, 0, len(configs)) - for _, i := range configs { - ingr = append(ingr, i.(*ingress.Ingress)) - } - sort.Slice(ingr, func(i, j int) bool { - // If creation time is the same, then behavior is nondeterministic. In this case, we can - // pick an arbitrary but consistent ordering based on name and namespace, which is unique. - // CreationTimestamp is stored in seconds, so this is not uncommon. - if ingr[i].CreationTimestamp == ingr[j].CreationTimestamp { - in := ingr[i].Name + "." + ingr[i].Namespace - jn := ingr[j].Name + "." + ingr[j].Namespace - return in < jn - } - return ingr[i].CreationTimestamp.Before(&ingr[j].CreationTimestamp) - }) - return ingr -} - -func (c *controller) List(typ config.GroupVersionKind, namespace string) ([]config.Config, error) { - if typ != gvk.Gateway && - typ != gvk.VirtualService { - return nil, errUnsupportedOp - } - - out := make([]config.Config, 0) - - ingressByHost := map[string]*config.Config{} - - for _, ingress := range sortIngressByCreationTime(c.ingressInformer.GetStore().List()) { - if namespace != "" && namespace != ingress.Namespace { - continue - } - process, err := c.shouldProcessIngress(c.meshWatcher.Mesh(), ingress) - if err != nil { - return nil, err - } - if !process { - continue - } - - switch typ { - case gvk.VirtualService: - ConvertIngressVirtualService(*ingress, c.domainSuffix, ingressByHost, c.serviceLister) - case gvk.Gateway: - gateways := ConvertIngressV1alpha3(*ingress, c.meshWatcher.Mesh(), c.domainSuffix) - out = append(out, gateways) - } - } - - if typ == gvk.VirtualService { - for _, obj := range ingressByHost { - out = append(out, *obj) - } - } - - return out, nil -} - -func (c *controller) Create(_ config.Config) (string, error) { - return "", errUnsupportedOp -} - -func (c *controller) Update(_ config.Config) (string, error) { - return "", errUnsupportedOp -} - -func (c *controller) UpdateStatus(config.Config) (string, error) { - return "", errUnsupportedOp -} - -func (c *controller) Patch(_ config.Config, _ config.PatchFunc) (string, error) { - return "", errUnsupportedOp -} - -func (c *controller) Delete(_ config.GroupVersionKind, _, _ string, _ *string) error { - return errUnsupportedOp -} diff --git a/pilot/pkg/config/kube/ingress/controller_test.go b/pilot/pkg/config/kube/ingress/controller_test.go deleted file mode 100644 index f27ddf29c..000000000 --- a/pilot/pkg/config/kube/ingress/controller_test.go +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ingress - -import ( - "context" - "testing" - "time" -) - -import ( - meshconfig "istio.io/api/mesh/v1alpha1" - "k8s.io/api/networking/v1beta1" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/intstr" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - kubecontroller "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube/controller" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -func newFakeController() (model.ConfigStoreController, kube.Client) { - meshHolder := mesh.NewTestWatcher(&meshconfig.MeshConfig{ - IngressControllerMode: meshconfig.MeshConfig_DEFAULT, - }) - fakeClient := kube.NewFakeClient() - return NewController(fakeClient, meshHolder, kubecontroller.Options{}), fakeClient -} - -func TestIngressController(t *testing.T) { - ingress1 := v1beta1.Ingress{ - ObjectMeta: metaV1.ObjectMeta{ - Namespace: "mock", // goes into backend full name - Name: "test", - }, - Spec: v1beta1.IngressSpec{ - Rules: []v1beta1.IngressRule{ - { - Host: "my.host.com", - IngressRuleValue: v1beta1.IngressRuleValue{ - HTTP: &v1beta1.HTTPIngressRuleValue{ - Paths: []v1beta1.HTTPIngressPath{ - { - Path: "/test", - Backend: v1beta1.IngressBackend{ - ServiceName: "foo", - ServicePort: intstr.IntOrString{IntVal: 8000}, - }, - }, - }, - }, - }, - }, - { - Host: "my2.host.com", - IngressRuleValue: v1beta1.IngressRuleValue{ - HTTP: &v1beta1.HTTPIngressRuleValue{ - Paths: []v1beta1.HTTPIngressPath{ - { - Path: "/test1.*", - Backend: v1beta1.IngressBackend{ - ServiceName: "bar", - ServicePort: intstr.IntOrString{IntVal: 8000}, - }, - }, - }, - }, - }, - }, - { - Host: "my3.host.com", - IngressRuleValue: v1beta1.IngressRuleValue{ - HTTP: &v1beta1.HTTPIngressRuleValue{ - Paths: []v1beta1.HTTPIngressPath{ - { - Path: "/test/*", - Backend: v1beta1.IngressBackend{ - ServiceName: "bar", - ServicePort: intstr.IntOrString{IntVal: 8000}, - }, - }, - }, - }, - }, - }, - }, - }, - } - - ingress2 := v1beta1.Ingress{ - ObjectMeta: metaV1.ObjectMeta{ - Namespace: "mock", - Name: "test", - }, - Spec: v1beta1.IngressSpec{ - Rules: []v1beta1.IngressRule{ - { - Host: "my.host.com", - IngressRuleValue: v1beta1.IngressRuleValue{ - HTTP: &v1beta1.HTTPIngressRuleValue{ - Paths: []v1beta1.HTTPIngressPath{ - { - Path: "/test2", - Backend: v1beta1.IngressBackend{ - ServiceName: "foo", - ServicePort: intstr.IntOrString{IntVal: 8000}, - }, - }, - }, - }, - }, - }, - }, - }, - } - - controller, client := newFakeController() - configCh := make(chan config.Config) - - configHandler := func(_, curr config.Config, event model.Event) { - configCh <- curr - } - - wait := func() config.Config { - select { - case x := <-configCh: - return x - case <-time.After(time.Second * 10): - t.Fatalf("timed out waiting for config") - } - return config.Config{} - } - - controller.RegisterEventHandler(gvk.VirtualService, configHandler) - stopCh := make(chan struct{}) - go controller.Run(stopCh) - defer close(stopCh) - - client.RunAndWait(stopCh) - - client.NetworkingV1beta1().Ingresses(ingress1.Namespace).Create(context.TODO(), &ingress1, metaV1.CreateOptions{}) - - vs := wait() - if vs.Name != ingress1.Name+"-"+"virtualservice" || vs.Namespace != ingress1.Namespace { - t.Errorf("received unecpected config %v/%v", vs.Namespace, vs.Name) - } - client.NetworkingV1beta1().Ingresses(ingress2.Namespace).Update(context.TODO(), &ingress2, metaV1.UpdateOptions{}) - vs = wait() - if vs.Name != ingress1.Name+"-"+"virtualservice" || vs.Namespace != ingress1.Namespace { - t.Errorf("received unecpected config %v/%v", vs.Namespace, vs.Name) - } -} diff --git a/pilot/pkg/config/kube/ingress/conversion.go b/pilot/pkg/config/kube/ingress/conversion.go deleted file mode 100644 index c92173d48..000000000 --- a/pilot/pkg/config/kube/ingress/conversion.go +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ingress - -import ( - "errors" - "fmt" - "sort" - "strconv" - "strings" -) - -import ( - "github.com/hashicorp/go-multierror" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" - "istio.io/pkg/log" - "k8s.io/api/networking/v1beta1" - "k8s.io/apimachinery/pkg/util/intstr" - listerv1 "k8s.io/client-go/listers/core/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -const ( - IstioIngressController = "istio.io/ingress-controller" -) - -var errNotFound = errors.New("item not found") - -// EncodeIngressRuleName encodes an ingress rule name for a given ingress resource name, -// as well as the position of the rule and path specified within it, counting from 1. -// ruleNum == pathNum == 0 indicates the default backend specified for an ingress. -func EncodeIngressRuleName(ingressName string, ruleNum, pathNum int) string { - return fmt.Sprintf("%s-%d-%d", ingressName, ruleNum, pathNum) -} - -// decodeIngressRuleName decodes an ingress rule name previously encoded with EncodeIngressRuleName. -func decodeIngressRuleName(name string) (ingressName string, ruleNum, pathNum int, err error) { - parts := strings.Split(name, "-") - if len(parts) < 3 { - err = fmt.Errorf("could not decode string into ingress rule name: %s", name) - return - } - - ingressName = strings.Join(parts[0:len(parts)-2], "-") - ruleNum, ruleErr := strconv.Atoi(parts[len(parts)-2]) - pathNum, pathErr := strconv.Atoi(parts[len(parts)-1]) - - if pathErr != nil || ruleErr != nil { - err = multierror.Append( - fmt.Errorf("could not decode string into ingress rule name: %s", name), - pathErr, ruleErr) - return - } - - return -} - -// ConvertIngressV1alpha3 converts from ingress spec to Istio Gateway -func ConvertIngressV1alpha3(ingress v1beta1.Ingress, mesh *meshconfig.MeshConfig, domainSuffix string) config.Config { - gateway := &networking.Gateway{} - gateway.Selector = getIngressGatewaySelector(mesh.IngressSelector, mesh.IngressService) - - for i, tls := range ingress.Spec.TLS { - if tls.SecretName == "" { - log.Infof("invalid ingress rule %s:%s for hosts %q, no secretName defined", ingress.Namespace, ingress.Name, tls.Hosts) - continue - } - // TODO validation when multiple wildcard tls secrets are given - if len(tls.Hosts) == 0 { - tls.Hosts = []string{"*"} - } - gateway.Servers = append(gateway.Servers, &networking.Server{ - Port: &networking.Port{ - Number: 443, - Protocol: string(protocol.HTTPS), - Name: fmt.Sprintf("https-443-ingress-%s-%s-%d", ingress.Name, ingress.Namespace, i), - }, - Hosts: tls.Hosts, - Tls: &networking.ServerTLSSettings{ - HttpsRedirect: false, - Mode: networking.ServerTLSSettings_SIMPLE, - CredentialName: tls.SecretName, - }, - }) - } - - gateway.Servers = append(gateway.Servers, &networking.Server{ - Port: &networking.Port{ - Number: 80, - Protocol: string(protocol.HTTP), - Name: fmt.Sprintf("http-80-ingress-%s-%s", ingress.Name, ingress.Namespace), - }, - Hosts: []string{"*"}, - }) - - gatewayConfig := config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.Gateway, - Name: ingress.Name + "-" + constants.IstioIngressGatewayName + "-" + ingress.Namespace, - Namespace: ingressNamespace, - Domain: domainSuffix, - }, - Spec: gateway, - } - - return gatewayConfig -} - -// ConvertIngressVirtualService converts from ingress spec to Istio VirtualServices -func ConvertIngressVirtualService(ingress v1beta1.Ingress, domainSuffix string, ingressByHost map[string]*config.Config, serviceLister listerv1.ServiceLister) { - // Ingress allows a single host - if missing '*' is assumed - // We need to merge all rules with a particular host across - // all ingresses, and return a separate VirtualService for each - // host. - if ingressNamespace == "" { - ingressNamespace = constants.IstioIngressNamespace - } - - for _, rule := range ingress.Spec.Rules { - if rule.HTTP == nil { - log.Infof("invalid ingress rule %s:%s for host %q, no paths defined", ingress.Namespace, ingress.Name, rule.Host) - continue - } - - host := rule.Host - namePrefix := strings.Replace(host, ".", "-", -1) - if host == "" { - host = "*" - } - virtualService := &networking.VirtualService{ - Hosts: []string{host}, - Gateways: []string{fmt.Sprintf("%s/%s-%s-%s", ingressNamespace, ingress.Name, constants.IstioIngressGatewayName, ingress.Namespace)}, - } - - httpRoutes := make([]*networking.HTTPRoute, 0, len(rule.HTTP.Paths)) - for _, httpPath := range rule.HTTP.Paths { - httpMatch := &networking.HTTPMatchRequest{} - if httpPath.PathType != nil { - switch *httpPath.PathType { - case v1beta1.PathTypeExact: - httpMatch.Uri = &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: httpPath.Path}, - } - case v1beta1.PathTypePrefix: - httpMatch.Uri = &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: httpPath.Path}, - } - default: - // Fallback to the legacy string matching - httpMatch.Uri = createFallbackStringMatch(httpPath.Path) - } - } else { - httpMatch.Uri = createFallbackStringMatch(httpPath.Path) - } - - httpRoute := ingressBackendToHTTPRoute(&httpPath.Backend, ingress.Namespace, domainSuffix, serviceLister) - if httpRoute == nil { - log.Infof("invalid ingress rule %s:%s for host %q, no backend defined for path", ingress.Namespace, ingress.Name, rule.Host) - continue - } - httpRoute.Match = []*networking.HTTPMatchRequest{httpMatch} - httpRoutes = append(httpRoutes, httpRoute) - } - - virtualService.Http = httpRoutes - - virtualServiceConfig := config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: namePrefix + "-" + ingress.Name + "-" + constants.IstioIngressGatewayName, - Namespace: ingress.Namespace, - Domain: domainSuffix, - Annotations: map[string]string{constants.InternalRouteSemantics: constants.RouteSemanticsIngress}, - }, - Spec: virtualService, - } - - old, f := ingressByHost[host] - if f { - vs := old.Spec.(*networking.VirtualService) - vs.Http = append(vs.Http, httpRoutes...) - if features.LegacyIngressBehavior { - sort.SliceStable(vs.Http, func(i, j int) bool { - r1 := vs.Http[i].Match[0].GetUri() - r2 := vs.Http[j].Match[0].GetUri() - _, r1Ex := r1.GetMatchType().(*networking.StringMatch_Exact) - _, r2Ex := r2.GetMatchType().(*networking.StringMatch_Exact) - // TODO: default at the end - if r1Ex && !r2Ex { - return true - } - return false - }) - } - } else { - ingressByHost[host] = &virtualServiceConfig - } - - if !features.LegacyIngressBehavior { - // sort routes to meet ingress route precedence requirements - // see https://kubernetes.io/docs/concepts/services-networking/ingress/#multiple-matches - vs := ingressByHost[host].Spec.(*networking.VirtualService) - sort.SliceStable(vs.Http, func(i, j int) bool { - r1Len, r1Ex := getMatchURILength(vs.Http[i].Match[0]) - r2Len, r2Ex := getMatchURILength(vs.Http[j].Match[0]) - // TODO: default at the end - if r1Len == r2Len { - return r1Ex && !r2Ex - } - return r1Len > r2Len - }) - } - } - - // Matches * and "/". Currently not supported - would conflict - // with any other explicit VirtualService. - if ingress.Spec.Backend != nil { - log.Infof("Ignore default wildcard ingress, use VirtualService %s:%s", - ingress.Namespace, ingress.Name) - } -} - -// getMatchURILength returns the length of matching path, and whether the match type is EXACT -func getMatchURILength(match *networking.HTTPMatchRequest) (length int, exact bool) { - uri := match.GetUri() - switch uri.GetMatchType().(type) { - case *networking.StringMatch_Exact: - return len(uri.GetExact()), true - case *networking.StringMatch_Prefix: - return len(uri.GetPrefix()), false - } - // should not happen - return -1, false -} - -func ingressBackendToHTTPRoute(backend *v1beta1.IngressBackend, namespace string, domainSuffix string, - serviceLister listerv1.ServiceLister) *networking.HTTPRoute { - if backend == nil { - return nil - } - - port := &networking.PortSelector{} - - if backend.ServicePort.Type == intstr.Int { - port.Number = uint32(backend.ServicePort.IntVal) - } else { - resolvedPort, err := resolveNamedPort(backend, namespace, serviceLister) - if err != nil { - log.Infof("failed to resolve named port %s, error: %v", backend.ServicePort.StrVal, err) - return nil - } - port.Number = uint32(resolvedPort) - } - - return &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: fmt.Sprintf("%s.%s.svc.%s", backend.ServiceName, namespace, domainSuffix), - Port: port, - }, - Weight: 100, - }, - }, - } -} - -func resolveNamedPort(backend *v1beta1.IngressBackend, namespace string, serviceLister listerv1.ServiceLister) (int32, error) { - svc, err := serviceLister.Services(namespace).Get(backend.ServiceName) - if err != nil { - return 0, err - } - for _, port := range svc.Spec.Ports { - if port.Name == backend.ServicePort.StrVal { - return port.Port, nil - } - } - return 0, errNotFound -} - -// shouldProcessIngress determines whether the given ingress resource should be processed -// by the controller, based on its ingress class annotation or, in more recent versions of -// kubernetes (v1.18+), based on the Ingress's specified IngressClass -// See https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class -func shouldProcessIngressWithClass(mesh *meshconfig.MeshConfig, ingress *v1beta1.Ingress, ingressClass *v1beta1.IngressClass) bool { - if class, exists := ingress.Annotations[kube.IngressClassAnnotation]; exists { - switch mesh.IngressControllerMode { - case meshconfig.MeshConfig_OFF: - return false - case meshconfig.MeshConfig_STRICT: - return class == mesh.IngressClass - case meshconfig.MeshConfig_DEFAULT: - return class == mesh.IngressClass - default: - log.Warnf("invalid ingress synchronization mode: %v", mesh.IngressControllerMode) - return false - } - } else if ingressClass != nil { - return ingressClass.Spec.Controller == IstioIngressController - } else { - switch mesh.IngressControllerMode { - case meshconfig.MeshConfig_OFF: - return false - case meshconfig.MeshConfig_STRICT: - return false - case meshconfig.MeshConfig_DEFAULT: - return true - default: - log.Warnf("invalid ingress synchronization mode: %v", mesh.IngressControllerMode) - return false - } - } -} - -func createFallbackStringMatch(s string) *networking.StringMatch { - if s == "" { - return nil - } - - // Note that this implementation only converts prefix and exact matches, not regexps. - - // Replace e.g. "foo.*" with prefix match - if strings.HasSuffix(s, ".*") { - return &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: strings.TrimSuffix(s, ".*")}, - } - } - if strings.HasSuffix(s, "/*") { - return &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: strings.TrimSuffix(s, "/*")}, - } - } - - // Replace e.g. "foo" with a exact match - return &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: s}, - } -} - -func getIngressGatewaySelector(ingressSelector, ingressService string) map[string]string { - // Setup the selector for the gateway - if ingressSelector != "" { - // If explicitly defined, use this one - return labels.Instance{constants.IstioLabel: ingressSelector} - } else if ingressService != "istio-ingressgateway" && ingressService != "" { - // Otherwise, we will use the ingress service as the default. It is common for the selector and service - // to be the same, so this removes the need for two configurations - // However, if its istio-ingressgateway we need to use the old values for backwards compatibility - return labels.Instance{constants.IstioLabel: ingressService} - } else { - // If we have neither an explicitly defined ingressSelector or ingressService then use a selector - // pointing to the ingressgateway from the default installation - return labels.Instance{constants.IstioLabel: constants.IstioIngressLabelValue} - } -} diff --git a/pilot/pkg/config/kube/ingress/conversion_test.go b/pilot/pkg/config/kube/ingress/conversion_test.go deleted file mode 100644 index 3b89acff5..000000000 --- a/pilot/pkg/config/kube/ingress/conversion_test.go +++ /dev/null @@ -1,495 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ingress - -import ( - "context" - "fmt" - "os" - "sort" - "strings" - "testing" - "time" -) - -import ( - "github.com/google/go-cmp/cmp" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" - coreV1 "k8s.io/api/core/v1" - "k8s.io/api/networking/v1beta1" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes/fake" - "k8s.io/client-go/kubernetes/scheme" - listerv1 "k8s.io/client-go/listers/core/v1" - "k8s.io/client-go/tools/cache" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/crd" - "github.com/apache/dubbo-go-pixiu/pilot/test/util" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" -) - -func TestGoldenConversion(t *testing.T) { - cases := []string{"simple", "tls", "overlay", "tls-no-secret"} - for _, tt := range cases { - t.Run(tt, func(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - input, err := readConfig(t, fmt.Sprintf("testdata/%s.yaml", tt)) - if err != nil { - t.Fatal(err) - } - serviceLister := createFakeLister(ctx) - cfgs := map[string]*config.Config{} - for _, obj := range input { - ingress := obj.(*v1beta1.Ingress) - ConvertIngressVirtualService(*ingress, "mydomain", cfgs, serviceLister) - } - ordered := []config.Config{} - for _, v := range cfgs { - ordered = append(ordered, *v) - } - for _, obj := range input { - ingress := obj.(*v1beta1.Ingress) - m := mesh.DefaultMeshConfig() - gws := ConvertIngressV1alpha3(*ingress, m, "mydomain") - ordered = append(ordered, gws) - } - - sort.Slice(ordered, func(i, j int) bool { - return ordered[i].Name < ordered[j].Name - }) - output := marshalYaml(t, ordered) - goldenFile := fmt.Sprintf("testdata/%s.yaml.golden", tt) - if util.Refresh() { - if err := os.WriteFile(goldenFile, output, 0o644); err != nil { - t.Fatal(err) - } - } - expected, err := os.ReadFile(goldenFile) - if err != nil { - t.Fatal(err) - } - if diff := cmp.Diff(expected, output); diff != "" { - t.Fatalf("Diff:\n%s", diff) - } - }) - } -} - -// Print as YAML -func marshalYaml(t *testing.T, cl []config.Config) []byte { - t.Helper() - result := []byte{} - separator := []byte("---\n") - for _, config := range cl { - obj, err := crd.ConvertConfig(config) - if err != nil { - t.Fatalf("Could not decode %v: %v", config.Name, err) - } - bytes, err := yaml.Marshal(obj) - if err != nil { - t.Fatalf("Could not convert %v to YAML: %v", config, err) - } - result = append(result, bytes...) - result = append(result, separator...) - } - return result -} - -func readConfig(t *testing.T, filename string) ([]runtime.Object, error) { - t.Helper() - - data, err := os.ReadFile(filename) - if err != nil { - t.Fatalf("failed to read input yaml file: %v", err) - } - var varr []runtime.Object - for _, yml := range strings.Split(string(data), "\n---") { - obj, _, err := scheme.Codecs.UniversalDeserializer().Decode([]byte(yml), nil, nil) - if err != nil { - return nil, err - } - varr = append(varr, obj) - } - - return varr, nil -} - -func TestConversion(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - prefix := v1beta1.PathTypePrefix - exact := v1beta1.PathTypeExact - - ingress := v1beta1.Ingress{ - ObjectMeta: metaV1.ObjectMeta{ - Namespace: "mock", // goes into backend full name - }, - Spec: v1beta1.IngressSpec{ - Rules: []v1beta1.IngressRule{ - { - Host: "my.host.com", - IngressRuleValue: v1beta1.IngressRuleValue{ - HTTP: &v1beta1.HTTPIngressRuleValue{ - Paths: []v1beta1.HTTPIngressPath{ - { - Path: "/test", - Backend: v1beta1.IngressBackend{ - ServiceName: "foo", - ServicePort: intstr.IntOrString{IntVal: 8000}, - }, - }, - { - Path: "/test/foo", - PathType: &prefix, - Backend: v1beta1.IngressBackend{ - ServiceName: "foo", - ServicePort: intstr.IntOrString{IntVal: 8000}, - }, - }, - }, - }, - }, - }, - { - Host: "my2.host.com", - IngressRuleValue: v1beta1.IngressRuleValue{ - HTTP: &v1beta1.HTTPIngressRuleValue{ - Paths: []v1beta1.HTTPIngressPath{ - { - Path: "/test1.*", - Backend: v1beta1.IngressBackend{ - ServiceName: "bar", - ServicePort: intstr.IntOrString{IntVal: 8000}, - }, - }, - }, - }, - }, - }, - { - Host: "my3.host.com", - IngressRuleValue: v1beta1.IngressRuleValue{ - HTTP: &v1beta1.HTTPIngressRuleValue{ - Paths: []v1beta1.HTTPIngressPath{ - { - Path: "/test/*", - Backend: v1beta1.IngressBackend{ - ServiceName: "bar", - ServicePort: intstr.IntOrString{IntVal: 8000}, - }, - }, - }, - }, - }, - }, - }, - }, - } - ingress2 := v1beta1.Ingress{ - ObjectMeta: metaV1.ObjectMeta{ - Namespace: "mock", - }, - Spec: v1beta1.IngressSpec{ - Rules: []v1beta1.IngressRule{ - { - Host: "my.host.com", - IngressRuleValue: v1beta1.IngressRuleValue{ - HTTP: &v1beta1.HTTPIngressRuleValue{ - Paths: []v1beta1.HTTPIngressPath{ - { - Path: "/test2", - Backend: v1beta1.IngressBackend{ - ServiceName: "foo", - ServicePort: intstr.IntOrString{IntVal: 8000}, - }, - }, - { - Path: "/test/foo/bar", - PathType: &prefix, - Backend: v1beta1.IngressBackend{ - ServiceName: "foo", - ServicePort: intstr.IntOrString{IntVal: 8000}, - }, - }, - { - Path: "/test/foo/bar", - PathType: &exact, - Backend: v1beta1.IngressBackend{ - ServiceName: "foo", - ServicePort: intstr.IntOrString{IntVal: 8000}, - }, - }, - }, - }, - }, - }, - }, - }, - } - serviceLister := createFakeLister(ctx) - cfgs := map[string]*config.Config{} - ConvertIngressVirtualService(ingress, "mydomain", cfgs, serviceLister) - ConvertIngressVirtualService(ingress2, "mydomain", cfgs, serviceLister) - - if len(cfgs) != 3 { - t.Error("VirtualServices, expected 3 got ", len(cfgs)) - } - - expectedLength := [5]int{13, 13, 9, 6, 5} - expectedExact := [5]bool{true, false, false, true, true} - - for n, cfg := range cfgs { - vs := cfg.Spec.(*networking.VirtualService) - - if n == "my.host.com" { - if vs.Hosts[0] != "my.host.com" { - t.Error("Unexpected host", vs) - } - if len(vs.Http) != 5 { - t.Error("Unexpected rules", vs.Http) - } - for i, route := range vs.Http { - length, exact := getMatchURILength(route.Match[0]) - if length != expectedLength[i] || exact != expectedExact[i] { - t.Errorf("Unexpected rule at idx:%d, want {length:%d, exact:%v}, got {length:%d, exact: %v}", - i, expectedLength[i], expectedExact[i], length, exact) - } - } - } - } -} - -func TestDecodeIngressRuleName(t *testing.T) { - cases := []struct { - ingressName string - ruleNum int - pathNum int - }{ - {"myingress", 0, 0}, - {"myingress", 1, 2}, - {"my-ingress", 1, 2}, - {"my-cool-ingress", 1, 2}, - } - - for _, c := range cases { - encoded := EncodeIngressRuleName(c.ingressName, c.ruleNum, c.pathNum) - ingressName, ruleNum, pathNum, err := decodeIngressRuleName(encoded) - if err != nil { - t.Errorf("decodeIngressRuleName(%q) => error %v", encoded, err) - } - if ingressName != c.ingressName || ruleNum != c.ruleNum || pathNum != c.pathNum { - t.Errorf("decodeIngressRuleName(%q) => (%q, %d, %d), want (%q, %d, %d)", - encoded, - ingressName, ruleNum, pathNum, - c.ingressName, c.ruleNum, c.pathNum, - ) - } - } -} - -func TestEncoding(t *testing.T) { - if got := EncodeIngressRuleName("name", 3, 5); got != "name-3-5" { - t.Errorf("unexpected ingress encoding %q", got) - } - - cases := []string{ - "name", - "name-path-5", - "name-3-path", - } - for _, code := range cases { - if _, _, _, err := decodeIngressRuleName(code); err == nil { - t.Errorf("expected error on decoding %q", code) - } - } -} - -func TestIngressClass(t *testing.T) { - istio := mesh.DefaultMeshConfig().IngressClass - ingressClassIstio := &v1beta1.IngressClass{ - ObjectMeta: metaV1.ObjectMeta{ - Name: "istio", - }, - Spec: v1beta1.IngressClassSpec{ - Controller: IstioIngressController, - }, - } - ingressClassOther := &v1beta1.IngressClass{ - ObjectMeta: metaV1.ObjectMeta{ - Name: "foo", - }, - Spec: v1beta1.IngressClassSpec{ - Controller: "foo", - }, - } - cases := []struct { - annotation string - ingressClass *v1beta1.IngressClass - ingressMode meshconfig.MeshConfig_IngressControllerMode - shouldProcess bool - }{ - // Annotation - {ingressMode: meshconfig.MeshConfig_DEFAULT, annotation: "nginx", shouldProcess: false}, - {ingressMode: meshconfig.MeshConfig_STRICT, annotation: "nginx", shouldProcess: false}, - {ingressMode: meshconfig.MeshConfig_OFF, annotation: istio, shouldProcess: false}, - {ingressMode: meshconfig.MeshConfig_DEFAULT, annotation: istio, shouldProcess: true}, - {ingressMode: meshconfig.MeshConfig_STRICT, annotation: istio, shouldProcess: true}, - {ingressMode: meshconfig.MeshConfig_DEFAULT, annotation: "", shouldProcess: true}, - {ingressMode: meshconfig.MeshConfig_STRICT, annotation: "", shouldProcess: false}, - - // IngressClass - {ingressMode: meshconfig.MeshConfig_DEFAULT, ingressClass: ingressClassOther, shouldProcess: false}, - {ingressMode: meshconfig.MeshConfig_STRICT, ingressClass: ingressClassOther, shouldProcess: false}, - {ingressMode: meshconfig.MeshConfig_DEFAULT, ingressClass: ingressClassIstio, shouldProcess: true}, - {ingressMode: meshconfig.MeshConfig_STRICT, ingressClass: ingressClassIstio, shouldProcess: true}, - {ingressMode: meshconfig.MeshConfig_DEFAULT, ingressClass: nil, shouldProcess: true}, - {ingressMode: meshconfig.MeshConfig_STRICT, ingressClass: nil, shouldProcess: false}, - - // IngressClass and Annotation - // note: k8s rejects Ingress resources configured with kubernetes.io/ingress.class annotation *and* ingressClassName field so this shouldn't happen - // see https://github.com/kubernetes/kubernetes/blob/ededd08ba131b727e60f663bd7217fffaaccd448/pkg/apis/networking/validation/validation.go#L226 - {ingressMode: meshconfig.MeshConfig_STRICT, ingressClass: ingressClassIstio, annotation: "nginx", shouldProcess: false}, - {ingressMode: meshconfig.MeshConfig_STRICT, ingressClass: ingressClassOther, annotation: istio, shouldProcess: true}, - {ingressMode: -1, shouldProcess: false}, - } - - for i, c := range cases { - className := "" - if c.ingressClass != nil { - className = c.ingressClass.Name - } - t.Run(fmt.Sprintf("%d %s %s %s", i, c.ingressMode, c.annotation, className), func(t *testing.T) { - ing := v1beta1.Ingress{ - ObjectMeta: metaV1.ObjectMeta{ - Name: "test-ingress", - Namespace: "default", - Annotations: make(map[string]string), - }, - Spec: v1beta1.IngressSpec{ - Backend: &v1beta1.IngressBackend{ - ServiceName: "default-http-backend", - ServicePort: intstr.FromInt(80), - }, - }, - } - - mesh := mesh.DefaultMeshConfig() - mesh.IngressControllerMode = c.ingressMode - - if c.annotation != "" { - ing.Annotations["kubernetes.io/ingress.class"] = c.annotation - } - - if c.shouldProcess != shouldProcessIngressWithClass(mesh, &ing, c.ingressClass) { - t.Errorf("got %v, want %v", - !c.shouldProcess, c.shouldProcess) - } - }) - } -} - -func TestNamedPortIngressConversion(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - ingress := v1beta1.Ingress{ - ObjectMeta: metaV1.ObjectMeta{ - Namespace: "mock", - }, - Spec: v1beta1.IngressSpec{ - Rules: []v1beta1.IngressRule{ - { - Host: "host.com", - IngressRuleValue: v1beta1.IngressRuleValue{ - HTTP: &v1beta1.HTTPIngressRuleValue{ - Paths: []v1beta1.HTTPIngressPath{ - { - Path: "/test/*", - Backend: v1beta1.IngressBackend{ - ServiceName: "foo", - ServicePort: intstr.IntOrString{Type: intstr.String, StrVal: "test-svc-port"}, - }, - }, - }, - }, - }, - }, - }, - }, - } - service := &coreV1.Service{ - ObjectMeta: metaV1.ObjectMeta{ - Name: "foo", - Namespace: "mock", - }, - Spec: coreV1.ServiceSpec{ - Ports: []coreV1.ServicePort{ - { - Name: "test-svc-port", - Protocol: "TCP", - Port: 8888, - TargetPort: intstr.IntOrString{ - Type: intstr.String, - StrVal: "test-port", - }, - }, - }, - Selector: map[string]string{ - "app": "test-app", - }, - }, - } - serviceLister := createFakeLister(ctx, service) - cfgs := map[string]*config.Config{} - ConvertIngressVirtualService(ingress, "mydomain", cfgs, serviceLister) - if len(cfgs) != 1 { - t.Error("VirtualServices, expected 1 got ", len(cfgs)) - } - if cfgs["host.com"] == nil { - t.Error("Host, found nil") - } - vs := cfgs["host.com"].Spec.(*networking.VirtualService) - if len(vs.Http) != 1 { - t.Error("HttpSpec, expected 1 got ", len(vs.Http)) - } - http := vs.Http[0] - if len(http.Route) != 1 { - t.Error("Route, expected 1 got ", len(http.Route)) - } - route := http.Route[0] - if route.Destination.Port.Number != 8888 { - t.Error("PortNumer, expected 8888 got ", route.Destination.Port.Number) - } -} - -func createFakeLister(ctx context.Context, objects ...runtime.Object) listerv1.ServiceLister { - client := fake.NewSimpleClientset(objects...) - informerFactory := informers.NewSharedInformerFactory(client, time.Hour) - svcInformer := informerFactory.Core().V1().Services().Informer() - go svcInformer.Run(ctx.Done()) - cache.WaitForCacheSync(ctx.Done(), svcInformer.HasSynced) - return informerFactory.Core().V1().Services().Lister() -} diff --git a/pilot/pkg/config/kube/ingress/leak_test.go b/pilot/pkg/config/kube/ingress/leak_test.go deleted file mode 100644 index f9d162316..000000000 --- a/pilot/pkg/config/kube/ingress/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ingress - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/config/kube/ingress/status.go b/pilot/pkg/config/kube/ingress/status.go deleted file mode 100644 index db1a3a401..000000000 --- a/pilot/pkg/config/kube/ingress/status.go +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ingress - -import ( - "context" - "net" - "sort" - "strings" - "time" -) - -import ( - "istio.io/pkg/log" - coreV1 "k8s.io/api/core/v1" - "k8s.io/api/networking/v1beta1" - kerrors "k8s.io/apimachinery/pkg/api/errors" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/kubernetes" - listerv1 "k8s.io/client-go/listers/core/v1" - listerv1beta1 "k8s.io/client-go/listers/networking/v1beta1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - kubelib "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/queue" -) - -const ( - updateInterval = 60 * time.Second -) - -// StatusSyncer keeps the status IP in each Ingress resource updated -type StatusSyncer struct { - meshHolder mesh.Holder - client kubernetes.Interface - - queue queue.Instance - ingressLister listerv1beta1.IngressLister - podLister listerv1.PodLister - serviceLister listerv1.ServiceLister - nodeLister listerv1.NodeLister - ingressClassLister listerv1beta1.IngressClassLister -} - -// Run the syncer until stopCh is closed -func (s *StatusSyncer) Run(stopCh <-chan struct{}) { - go s.queue.Run(stopCh) - go s.runUpdateStatus(stopCh) -} - -// NewStatusSyncer creates a new instance -func NewStatusSyncer(meshHolder mesh.Holder, client kubelib.Client) *StatusSyncer { - // as in controller, ingressClassListener can be nil since not supported in k8s version <1.18 - var ingressClassLister listerv1beta1.IngressClassLister - if NetworkingIngressAvailable(client) { - ingressClassLister = client.KubeInformer().Networking().V1beta1().IngressClasses().Lister() - } - - // queue requires a time duration for a retry delay after a handler error - q := queue.NewQueue(5 * time.Second) - - return &StatusSyncer{ - meshHolder: meshHolder, - client: client, - ingressLister: client.KubeInformer().Networking().V1beta1().Ingresses().Lister(), - podLister: client.KubeInformer().Core().V1().Pods().Lister(), - serviceLister: client.KubeInformer().Core().V1().Services().Lister(), - nodeLister: client.KubeInformer().Core().V1().Nodes().Lister(), - ingressClassLister: ingressClassLister, - queue: q, - } -} - -func (s *StatusSyncer) onEvent() error { - addrs, err := s.runningAddresses(ingressNamespace) - if err != nil { - if kerrors.IsNotFound(err) { - return nil - } - return err - } - - return s.updateStatus(sliceToStatus(addrs)) -} - -func (s *StatusSyncer) runUpdateStatus(stop <-chan struct{}) { - if _, err := s.runningAddresses(ingressNamespace); err != nil { - log.Warn("Missing ingress, skip status updates") - err = wait.PollUntil(10*time.Second, func() (bool, error) { - if sa, err := s.runningAddresses(ingressNamespace); err != nil || len(sa) == 0 { - return false, nil - } - return true, nil - }, stop) - if err != nil { - log.Warn("Error waiting for ingress") - return - } - } - err := wait.PollUntil(updateInterval, func() (bool, error) { - s.queue.Push(s.onEvent) - return false, nil - }, stop) - if err != nil { - log.Errorf("Stop requested") - } -} - -// updateStatus updates ingress status with the list of IP -func (s *StatusSyncer) updateStatus(status []coreV1.LoadBalancerIngress) error { - l, err := s.ingressLister.List(labels.Everything()) - if err != nil { - return err - } - - if len(l) == 0 { - return nil - } - - sort.SliceStable(status, lessLoadBalancerIngress(status)) - - for _, currIng := range l { - shouldTarget, err := s.shouldTargetIngress(currIng) - if err != nil { - log.Warnf("error determining whether should target ingress for status update: %v", err) - return err - } - if !shouldTarget { - continue - } - - curIPs := currIng.Status.LoadBalancer.Ingress - sort.SliceStable(curIPs, lessLoadBalancerIngress(curIPs)) - - if ingressSliceEqual(status, curIPs) { - log.Debugf("skipping update of Ingress %v/%v (no change)", currIng.Namespace, currIng.Name) - continue - } - - currIng.Status.LoadBalancer.Ingress = status - - _, err = s.client.NetworkingV1beta1().Ingresses(currIng.Namespace).UpdateStatus(context.TODO(), currIng, metaV1.UpdateOptions{}) - if err != nil { - log.Warnf("error updating ingress status: %v", err) - } - } - - return nil -} - -// runningAddresses returns a list of IP addresses and/or FQDN in the namespace -// where the ingress controller is currently running -func (s *StatusSyncer) runningAddresses(ingressNs string) ([]string, error) { - addrs := make([]string, 0) - ingressService := s.meshHolder.Mesh().IngressService - ingressSelector := s.meshHolder.Mesh().IngressSelector - - if ingressService != "" { - svc, err := s.serviceLister.Services(ingressNs).Get(ingressService) - if err != nil { - return nil, err - } - - if svc.Spec.Type == coreV1.ServiceTypeExternalName { - addrs = append(addrs, svc.Spec.ExternalName) - return addrs, nil - } - - for _, ip := range svc.Status.LoadBalancer.Ingress { - if ip.IP == "" { - addrs = append(addrs, ip.Hostname) - } else { - addrs = append(addrs, ip.IP) - } - } - - addrs = append(addrs, svc.Spec.ExternalIPs...) - return addrs, nil - } - - // get all pods acting as ingress gateways - igSelector := getIngressGatewaySelector(ingressSelector, ingressService) - igPods, err := s.podLister.Pods(ingressNamespace).List(labels.SelectorFromSet(igSelector)) - if err != nil { - return nil, err - } - - for _, pod := range igPods { - // only Running pods are valid - if pod.Status.Phase != coreV1.PodRunning { - continue - } - - // Find node external IP - node, err := s.nodeLister.Get(pod.Spec.NodeName) - if err != nil { - continue - } - - for _, address := range node.Status.Addresses { - if address.Type == coreV1.NodeExternalIP { - if address.Address != "" && !addressInSlice(address.Address, addrs) { - addrs = append(addrs, address.Address) - } - } - } - } - - return addrs, nil -} - -func addressInSlice(addr string, list []string) bool { - for _, v := range list { - if v == addr { - return true - } - } - - return false -} - -// sliceToStatus converts a slice of IP and/or hostnames to LoadBalancerIngress -func sliceToStatus(endpoints []string) []coreV1.LoadBalancerIngress { - lbi := make([]coreV1.LoadBalancerIngress, 0, len(endpoints)) - for _, ep := range endpoints { - if net.ParseIP(ep) == nil { - lbi = append(lbi, coreV1.LoadBalancerIngress{Hostname: ep}) - } else { - lbi = append(lbi, coreV1.LoadBalancerIngress{IP: ep}) - } - } - - return lbi -} - -func lessLoadBalancerIngress(addrs []coreV1.LoadBalancerIngress) func(int, int) bool { - return func(a, b int) bool { - switch strings.Compare(addrs[a].Hostname, addrs[b].Hostname) { - case -1: - return true - case 1: - return false - } - return addrs[a].IP < addrs[b].IP - } -} - -func ingressSliceEqual(lhs, rhs []coreV1.LoadBalancerIngress) bool { - if len(lhs) != len(rhs) { - return false - } - - for i := range lhs { - if lhs[i].IP != rhs[i].IP { - return false - } - if lhs[i].Hostname != rhs[i].Hostname { - return false - } - } - return true -} - -// shouldTargetIngress determines whether the status watcher should target a given ingress resource -func (s *StatusSyncer) shouldTargetIngress(ingress *v1beta1.Ingress) (bool, error) { - var ingressClass *v1beta1.IngressClass - if s.ingressClassLister != nil && ingress.Spec.IngressClassName != nil { - c, err := s.ingressClassLister.Get(*ingress.Spec.IngressClassName) - if err != nil && !kerrors.IsNotFound(err) { - return false, err - } - ingressClass = c - } - return shouldProcessIngressWithClass(s.meshHolder.Mesh(), ingress, ingressClass), nil -} diff --git a/pilot/pkg/config/kube/ingress/status_test.go b/pilot/pkg/config/kube/ingress/status_test.go deleted file mode 100644 index 4f2b9e62b..000000000 --- a/pilot/pkg/config/kube/ingress/status_test.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ingress - -import ( - "context" - "os" - "testing" -) - -import ( - coreV1 "k8s.io/api/core/v1" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - kubelib "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -var ( - pod = "test" - serviceIP = "1.2.3.4" - hostname = "foo.bar.com" - nodeIP = "10.0.0.2" - testNamespace = "test" -) - -func setupFake(t *testing.T, client kubelib.Client) { - t.Helper() - if _, err := client.Kube().CoreV1().Pods("dubbo-system").Create(context.TODO(), &coreV1.Pod{ - ObjectMeta: metaV1.ObjectMeta{ - Name: "ingressgateway", - Namespace: "dubbo-system", - Labels: map[string]string{ - "istio": "ingressgateway", - }, - }, - Spec: coreV1.PodSpec{ - NodeName: "foo_node", - }, - Status: coreV1.PodStatus{ - Phase: coreV1.PodRunning, - }, - }, metaV1.CreateOptions{}); err != nil { - t.Fatal(err) - } - - if _, err := client.Kube().CoreV1().Services(testNamespace).Create(context.TODO(), &coreV1.Service{ - ObjectMeta: metaV1.ObjectMeta{ - Name: "istio-ingress", - Namespace: testNamespace, - }, - Status: coreV1.ServiceStatus{ - LoadBalancer: coreV1.LoadBalancerStatus{ - Ingress: []coreV1.LoadBalancerIngress{{ - IP: serviceIP, - }}, - }, - }, - }, metaV1.CreateOptions{}); err != nil { - t.Fatal(err) - } - - if _, err := client.Kube().CoreV1().Services(testNamespace).Create(context.TODO(), &coreV1.Service{ - ObjectMeta: metaV1.ObjectMeta{ - Name: "istio-ingress-hostname", - Namespace: testNamespace, - }, - Status: coreV1.ServiceStatus{ - LoadBalancer: coreV1.LoadBalancerStatus{ - Ingress: []coreV1.LoadBalancerIngress{{ - Hostname: hostname, - }}, - }, - }, - }, metaV1.CreateOptions{}); err != nil { - t.Fatal(err) - } - if _, err := client.Kube().CoreV1().Nodes().Create(context.TODO(), &coreV1.Node{ - ObjectMeta: metaV1.ObjectMeta{ - Name: "foo_node", - }, - Status: coreV1.NodeStatus{ - Addresses: []coreV1.NodeAddress{ - { - Type: coreV1.NodeExternalIP, - Address: nodeIP, - }, - }, - }, - }, metaV1.CreateOptions{}); err != nil { - t.Fatal(err) - } -} - -func fakeMeshHolder(ingressService string) mesh.Holder { - config := mesh.DefaultMeshConfig() - config.IngressService = ingressService - return mesh.NewFixedWatcher(config) -} - -func makeStatusSyncer(t *testing.T) *StatusSyncer { - oldEnvs := setAndRestoreEnv(t, map[string]string{"POD_NAME": pod, "POD_NAMESPACE": testNamespace}) - // Restore env settings - defer setAndRestoreEnv(t, oldEnvs) - - client := kubelib.NewFakeClient() - setupFake(t, client) - sync := NewStatusSyncer(fakeMeshHolder("istio-ingress"), client) - stop := make(chan struct{}) - client.RunAndWait(stop) - t.Cleanup(func() { - close(stop) - }) - return sync -} - -// setAndRestoreEnv set the envs with given value, and return the old setting. -func setAndRestoreEnv(t *testing.T, inputs map[string]string) map[string]string { - oldEnvs := map[string]string{} - for k, v := range inputs { - oldEnvs[k] = os.Getenv(k) - if err := os.Setenv(k, v); err != nil { - t.Error(err) - } - } - - return oldEnvs -} - -func TestRunningAddresses(t *testing.T) { - t.Run("service", testRunningAddressesWithService) - t.Run("hostname", testRunningAddressesWithHostname) -} - -func testRunningAddressesWithService(t *testing.T) { - syncer := makeStatusSyncer(t) - address, err := syncer.runningAddresses(testNamespace) - if err != nil { - t.Fatal(err) - } - - if len(address) != 1 || address[0] != serviceIP { - t.Errorf("Address is not correctly set to service ip") - } -} - -func testRunningAddressesWithHostname(t *testing.T) { - syncer := makeStatusSyncer(t) - syncer.meshHolder = fakeMeshHolder("istio-ingress-hostname") - - address, err := syncer.runningAddresses(testNamespace) - if err != nil { - t.Fatal(err) - } - - if len(address) != 1 || address[0] != hostname { - t.Errorf("Address is not correctly set to hostname") - } -} - -func TestRunningAddressesWithPod(t *testing.T) { - ingressNamespace = "dubbo-system" // it is set in real pilot on newController. - syncer := makeStatusSyncer(t) - syncer.meshHolder = fakeMeshHolder("") - - address, err := syncer.runningAddresses(ingressNamespace) - if err != nil { - t.Fatal(err) - } - - if len(address) != 1 || address[0] != nodeIP { - t.Errorf("Address is not correctly set to node ip %v %v", address, nodeIP) - } -} diff --git a/pilot/pkg/config/kube/ingress/testdata/overlay.yaml b/pilot/pkg/config/kube/ingress/testdata/overlay.yaml deleted file mode 100644 index cdbc475ea..000000000 --- a/pilot/pkg/config/kube/ingress/testdata/overlay.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: networking.k8s.io/v1beta1 -kind: Ingress -metadata: - name: foo - namespace: ns -spec: - rules: - - host: foohost.bar.com - http: - paths: - - backend: - serviceName: service1 - servicePort: 4200 ---- -apiVersion: networking.k8s.io/v1beta1 -kind: Ingress -metadata: - name: foo2 - namespace: ns -spec: - rules: - - host: foohost.bar.com - http: - paths: - - path: /second - backend: - serviceName: service2 - servicePort: 4200 \ No newline at end of file diff --git a/pilot/pkg/config/kube/ingress/testdata/overlay.yaml.golden b/pilot/pkg/config/kube/ingress/testdata/overlay.yaml.golden deleted file mode 100644 index dfdea0ca3..000000000 --- a/pilot/pkg/config/kube/ingress/testdata/overlay.yaml.golden +++ /dev/null @@ -1,66 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - creationTimestamp: null - name: foo-istio-autogenerated-k8s-ingress-ns - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - '*' - port: - name: http-80-ingress-foo-ns - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - creationTimestamp: null - name: foo2-istio-autogenerated-k8s-ingress-ns - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - '*' - port: - name: http-80-ingress-foo2-ns - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/route-semantics: ingress - creationTimestamp: null - name: foohost-bar-com-foo-istio-autogenerated-k8s-ingress - namespace: ns -spec: - gateways: - - dubbo-system/foo-istio-autogenerated-k8s-ingress-ns - hosts: - - foohost.bar.com - http: - - match: - - uri: - exact: /second - route: - - destination: - host: service2.ns.svc.mydomain - port: - number: 4200 - weight: 100 - - match: - - {} - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4200 - weight: 100 ---- diff --git a/pilot/pkg/config/kube/ingress/testdata/simple.yaml b/pilot/pkg/config/kube/ingress/testdata/simple.yaml deleted file mode 100644 index 4328df9a9..000000000 --- a/pilot/pkg/config/kube/ingress/testdata/simple.yaml +++ /dev/null @@ -1,64 +0,0 @@ -apiVersion: networking.k8s.io/v1beta1 -kind: Ingress -metadata: - name: foo - namespace: ns -spec: - rules: - - host: foohost.bar.com - http: - paths: - # Simple path - - path: /path - backend: - serviceName: service1 - servicePort: 4200 - # Subpath without trailing / - - path: /sub/path - backend: - serviceName: service1 - servicePort: 4201 - # With a trailing / - - path: /sub/path/ - backend: - serviceName: service1 - servicePort: 4202 - # Regex ending with .* - - path: /regex1.* - backend: - serviceName: service1 - servicePort: 4203 - # Regex ending with * - - path: /regex2* - backend: - serviceName: service1 - servicePort: 4204 - # Regex ending with /* - - path: /regex3/* - backend: - serviceName: service1 - servicePort: 4205 - # Subpath without trailing /, Exact match - - path: /sub/path - pathType: Exact - backend: - serviceName: service1 - servicePort: 4206 - # With a trailing /, Exact match - - path: /sub/path/ - pathType: Exact - backend: - serviceName: service1 - servicePort: 4207 - # Subpath without trailing /, Prefix match - - path: /sub/path - pathType: Prefix - backend: - serviceName: service1 - servicePort: 4208 - # With a trailing /, Prefix match - - path: /sub/path/ - pathType: Prefix - backend: - serviceName: service1 - servicePort: 4209 diff --git a/pilot/pkg/config/kube/ingress/testdata/simple.yaml.golden b/pilot/pkg/config/kube/ingress/testdata/simple.yaml.golden deleted file mode 100644 index c0ba4ae9d..000000000 --- a/pilot/pkg/config/kube/ingress/testdata/simple.yaml.golden +++ /dev/null @@ -1,122 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - creationTimestamp: null - name: foo-istio-autogenerated-k8s-ingress-ns - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - '*' - port: - name: http-80-ingress-foo-ns - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/route-semantics: ingress - creationTimestamp: null - name: foohost-bar-com-foo-istio-autogenerated-k8s-ingress - namespace: ns -spec: - gateways: - - dubbo-system/foo-istio-autogenerated-k8s-ingress-ns - hosts: - - foohost.bar.com - http: - - match: - - uri: - exact: /sub/path/ - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4202 - weight: 100 - - match: - - uri: - exact: /sub/path/ - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4207 - weight: 100 - - match: - - uri: - prefix: /sub/path/ - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4209 - weight: 100 - - match: - - uri: - exact: /sub/path - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4201 - weight: 100 - - match: - - uri: - exact: /sub/path - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4206 - weight: 100 - - match: - - uri: - prefix: /sub/path - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4208 - weight: 100 - - match: - - uri: - exact: /regex2* - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4204 - weight: 100 - - match: - - uri: - prefix: /regex1 - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4203 - weight: 100 - - match: - - uri: - prefix: /regex3 - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4205 - weight: 100 - - match: - - uri: - exact: /path - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4200 - weight: 100 ---- diff --git a/pilot/pkg/config/kube/ingress/testdata/tls-no-secret.yaml b/pilot/pkg/config/kube/ingress/testdata/tls-no-secret.yaml deleted file mode 100644 index 6b453ec41..000000000 --- a/pilot/pkg/config/kube/ingress/testdata/tls-no-secret.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: networking.k8s.io/v1beta1 -kind: Ingress -metadata: - name: tls - namespace: bar -spec: - rules: - - host: foo.org - http: - paths: - - backend: - serviceName: httpbin - servicePort: 80 - path: /* - tls: - - hosts: - - foo.org diff --git a/pilot/pkg/config/kube/ingress/testdata/tls-no-secret.yaml.golden b/pilot/pkg/config/kube/ingress/testdata/tls-no-secret.yaml.golden deleted file mode 100644 index 29a96cb58..000000000 --- a/pilot/pkg/config/kube/ingress/testdata/tls-no-secret.yaml.golden +++ /dev/null @@ -1,41 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/route-semantics: ingress - creationTimestamp: null - name: foo-org-tls-istio-autogenerated-k8s-ingress - namespace: bar -spec: - gateways: - - dubbo-system/tls-istio-autogenerated-k8s-ingress-bar - hosts: - - foo.org - http: - - match: - - uri: - prefix: "" - route: - - destination: - host: httpbin.bar.svc.mydomain - port: - number: 80 - weight: 100 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - creationTimestamp: null - name: tls-istio-autogenerated-k8s-ingress-bar - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - '*' - port: - name: http-80-ingress-tls-bar - number: 80 - protocol: HTTP ---- diff --git a/pilot/pkg/config/kube/ingress/testdata/tls.yaml b/pilot/pkg/config/kube/ingress/testdata/tls.yaml deleted file mode 100644 index f2fa58dec..000000000 --- a/pilot/pkg/config/kube/ingress/testdata/tls.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: networking.k8s.io/v1beta1 -kind: Ingress -metadata: - name: tls - namespace: bar -spec: - rules: - - host: foo.org - http: - paths: - - backend: - serviceName: httpbin - servicePort: 80 - path: /* - tls: - - hosts: - - foo.org - secretName: myingress-cert diff --git a/pilot/pkg/config/kube/ingress/testdata/tls.yaml.golden b/pilot/pkg/config/kube/ingress/testdata/tls.yaml.golden deleted file mode 100644 index 6af1a705f..000000000 --- a/pilot/pkg/config/kube/ingress/testdata/tls.yaml.golden +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/route-semantics: ingress - creationTimestamp: null - name: foo-org-tls-istio-autogenerated-k8s-ingress - namespace: bar -spec: - gateways: - - dubbo-system/tls-istio-autogenerated-k8s-ingress-bar - hosts: - - foo.org - http: - - match: - - uri: - prefix: "" - route: - - destination: - host: httpbin.bar.svc.mydomain - port: - number: 80 - weight: 100 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - creationTimestamp: null - name: tls-istio-autogenerated-k8s-ingress-bar - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - foo.org - port: - name: https-443-ingress-tls-bar-0 - number: 443 - protocol: HTTPS - tls: - credentialName: myingress-cert - mode: SIMPLE - - hosts: - - '*' - port: - name: http-80-ingress-tls-bar - number: 80 - protocol: HTTP ---- diff --git a/pilot/pkg/config/kube/ingressv1/controller.go b/pilot/pkg/config/kube/ingressv1/controller.go deleted file mode 100644 index 470e42912..000000000 --- a/pilot/pkg/config/kube/ingressv1/controller.go +++ /dev/null @@ -1,353 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package ingress provides a read-only view of Kubernetes ingress resources -// as an ingress rule configuration type store -package ingress - -import ( - "errors" - "fmt" - "sort" - "sync" -) - -import ( - "github.com/hashicorp/go-multierror" - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/env" - knetworking "k8s.io/api/networking/v1" - kerrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" - ingressinformer "k8s.io/client-go/informers/networking/v1" - listerv1 "k8s.io/client-go/listers/core/v1" - networkinglister "k8s.io/client-go/listers/networking/v1" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - kubecontroller "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube/controller" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/controllers" -) - -// In 1.0, the Gateway is defined in the namespace where the actual controller runs, and needs to be managed by -// user. -// The gateway is named by appending "-istio-autogenerated-k8s-ingress" to the name of the ingress. -// -// Currently the gateway namespace is hardcoded to dubbo-system (model.IstioIngressNamespace) -// -// VirtualServices are also auto-generated in the model.IstioIngressNamespace. -// -// The sync of Ingress objects to IP is done by status.go -// the 'ingress service' name is used to get the IP of the Service -// If ingress service is empty, it falls back to NodeExternalIP list, selected using the labels. -// This is using 'namespace' of pilot - but seems to be broken (never worked), since it uses Pilot's pod labels -// instead of the ingress labels. -// Follows mesh.IngressControllerMode setting to enable - OFF|STRICT|DEFAULT. -// STRICT requires "kubernetes.io/ingress.class" == mesh.IngressClass -// DEFAULT allows Ingress without explicit class. -// In 1.1: -// - K8S_INGRESS_NS - namespace of the Gateway that will act as ingress. -// - labels of the gateway set to "app=ingressgateway" for node_port, service set to 'ingressgateway' (matching default install) -// If we need more flexibility - we can add it (but likely we'll deprecate ingress support first) -// - -var schemas = collection.SchemasFor( - collections.IstioNetworkingV1Alpha3Virtualservices, - collections.IstioNetworkingV1Alpha3Gateways) - -// Control needs RBAC permissions to write to Pods. - -type controller struct { - meshWatcher mesh.Holder - domainSuffix string - - queue controllers.Queue - virtualServiceHandlers []model.EventHandler - gatewayHandlers []model.EventHandler - - mutex sync.RWMutex - // processed ingresses - ingresses map[types.NamespacedName]*knetworking.Ingress - - ingressInformer cache.SharedInformer - ingressLister networkinglister.IngressLister - serviceInformer cache.SharedInformer - serviceLister listerv1.ServiceLister - // May be nil if ingress class is not supported in the cluster - classes ingressinformer.IngressClassInformer -} - -// TODO: move to features ( and remove in 1.2 ) -var ingressNamespace = env.RegisterStringVar("K8S_INGRESS_NS", "", "").Get() - -var errUnsupportedOp = errors.New("unsupported operation: the ingress config store is a read-only view") - -// NewController creates a new Kubernetes controller -func NewController(client kube.Client, meshWatcher mesh.Holder, - options kubecontroller.Options) model.ConfigStoreController { - if ingressNamespace == "" { - ingressNamespace = constants.IstioIngressNamespace - } - - ingressInformer := client.KubeInformer().Networking().V1().Ingresses() - serviceInformer := client.KubeInformer().Core().V1().Services() - - classes := client.KubeInformer().Networking().V1().IngressClasses() - classes.Informer() - - c := &controller{ - meshWatcher: meshWatcher, - domainSuffix: options.DomainSuffix, - ingresses: make(map[types.NamespacedName]*knetworking.Ingress), - ingressInformer: ingressInformer.Informer(), - ingressLister: ingressInformer.Lister(), - classes: classes, - serviceInformer: serviceInformer.Informer(), - serviceLister: serviceInformer.Lister(), - } - c.queue = controllers.NewQueue("ingress", - controllers.WithReconciler(c.onEvent), - controllers.WithMaxAttempts(5)) - c.ingressInformer.AddEventHandler(controllers.ObjectHandler(c.queue.AddObject)) - return c -} - -func (c *controller) Run(stop <-chan struct{}) { - c.queue.Run(stop) -} - -func (c *controller) shouldProcessIngress(mesh *meshconfig.MeshConfig, i *knetworking.Ingress) (bool, error) { - var class *knetworking.IngressClass - if c.classes != nil && i.Spec.IngressClassName != nil { - c, err := c.classes.Lister().Get(*i.Spec.IngressClassName) - if err != nil && !kerrors.IsNotFound(err) { - return false, fmt.Errorf("failed to get ingress class %v: %v", i.Spec.IngressClassName, err) - } - class = c - } - return shouldProcessIngressWithClass(mesh, i, class), nil -} - -// shouldProcessIngressUpdate checks whether we should renotify registered handlers about an update event -func (c *controller) shouldProcessIngressUpdate(ing *knetworking.Ingress) (bool, error) { - // ingress add/update - shouldProcess, err := c.shouldProcessIngress(c.meshWatcher.Mesh(), ing) - if err != nil { - return false, err - } - item := types.NamespacedName{Name: ing.Name, Namespace: ing.Namespace} - if shouldProcess { - // record processed ingress - c.mutex.Lock() - c.ingresses[item] = ing - c.mutex.Unlock() - return true, nil - } - - c.mutex.Lock() - _, preProcessed := c.ingresses[item] - // previous processed but should not currently, delete it - if preProcessed && !shouldProcess { - delete(c.ingresses, item) - } else { - c.ingresses[item] = ing - } - c.mutex.Unlock() - - return preProcessed, nil -} - -func (c *controller) onEvent(item types.NamespacedName) error { - event := model.EventUpdate - ing, err := c.ingressLister.Ingresses(item.Namespace).Get(item.Name) - if err != nil { - if kerrors.IsNotFound(err) { - event = model.EventDelete - c.mutex.Lock() - ing = c.ingresses[item] - delete(c.ingresses, item) - c.mutex.Unlock() - } else { - return err - } - } - - // ingress deleted, and it is not processed before - if ing == nil { - return nil - } - // we should check need process only when event is not delete, - // if it is delete event, and previously processed, we need to process too. - if event != model.EventDelete { - shouldProcess, err := c.shouldProcessIngressUpdate(ing) - if err != nil { - return err - } - if !shouldProcess { - return nil - } - } - - vsmetadata := config.Meta{ - Name: item.Name + "-" + "virtualservice", - Namespace: item.Namespace, - GroupVersionKind: gvk.VirtualService, - // Set this label so that we do not compare configs and just push. - Labels: map[string]string{constants.AlwaysPushLabel: "true"}, - } - gatewaymetadata := config.Meta{ - Name: item.Name + "-" + "gateway", - Namespace: item.Namespace, - GroupVersionKind: gvk.Gateway, - // Set this label so that we do not compare configs and just push. - Labels: map[string]string{constants.AlwaysPushLabel: "true"}, - } - - // Trigger updates for Gateway and VirtualService - // TODO: we could be smarter here and only trigger when real changes were found - for _, f := range c.virtualServiceHandlers { - f(config.Config{Meta: vsmetadata}, config.Config{Meta: vsmetadata}, event) - } - for _, f := range c.gatewayHandlers { - f(config.Config{Meta: gatewaymetadata}, config.Config{Meta: gatewaymetadata}, event) - } - - return nil -} - -func (c *controller) RegisterEventHandler(kind config.GroupVersionKind, f model.EventHandler) { - switch kind { - case gvk.VirtualService: - c.virtualServiceHandlers = append(c.virtualServiceHandlers, f) - case gvk.Gateway: - c.gatewayHandlers = append(c.gatewayHandlers, f) - } -} - -func (c *controller) SetWatchErrorHandler(handler func(r *cache.Reflector, err error)) error { - var errs error - if err := c.serviceInformer.SetWatchErrorHandler(handler); err != nil { - errs = multierror.Append(err, errs) - } - if err := c.ingressInformer.SetWatchErrorHandler(handler); err != nil { - errs = multierror.Append(err, errs) - } - if err := c.classes.Informer().SetWatchErrorHandler(handler); err != nil { - errs = multierror.Append(err, errs) - } - return errs -} - -func (c *controller) HasSynced() bool { - // TODO: add c.queue.HasSynced() once #36332 is ready, ensuring Run is called before HasSynced - return c.ingressInformer.HasSynced() && c.serviceInformer.HasSynced() && - (c.classes == nil || c.classes.Informer().HasSynced()) -} - -func (c *controller) Schemas() collection.Schemas { - // TODO: are these two config descriptors right? - return schemas -} - -func (c *controller) Get(typ config.GroupVersionKind, name, namespace string) *config.Config { - return nil -} - -// sortIngressByCreationTime sorts the list of config objects in ascending order by their creation time (if available). -func sortIngressByCreationTime(configs []interface{}) []*knetworking.Ingress { - ingr := make([]*knetworking.Ingress, 0, len(configs)) - for _, i := range configs { - ingr = append(ingr, i.(*knetworking.Ingress)) - } - sort.Slice(ingr, func(i, j int) bool { - // If creation time is the same, then behavior is nondeterministic. In this case, we can - // pick an arbitrary but consistent ordering based on name and namespace, which is unique. - // CreationTimestamp is stored in seconds, so this is not uncommon. - if ingr[i].CreationTimestamp == ingr[j].CreationTimestamp { - in := ingr[i].Name + "." + ingr[i].Namespace - jn := ingr[j].Name + "." + ingr[j].Namespace - return in < jn - } - return ingr[i].CreationTimestamp.Before(&ingr[j].CreationTimestamp) - }) - return ingr -} - -func (c *controller) List(typ config.GroupVersionKind, namespace string) ([]config.Config, error) { - if typ != gvk.Gateway && - typ != gvk.VirtualService { - return nil, errUnsupportedOp - } - - out := make([]config.Config, 0) - - ingressByHost := map[string]*config.Config{} - - for _, ingress := range sortIngressByCreationTime(c.ingressInformer.GetStore().List()) { - if namespace != "" && namespace != ingress.Namespace { - continue - } - process, err := c.shouldProcessIngress(c.meshWatcher.Mesh(), ingress) - if err != nil { - return nil, err - } - if !process { - continue - } - - switch typ { - case gvk.VirtualService: - ConvertIngressVirtualService(*ingress, c.domainSuffix, ingressByHost, c.serviceLister) - case gvk.Gateway: - gateways := ConvertIngressV1alpha3(*ingress, c.meshWatcher.Mesh(), c.domainSuffix) - out = append(out, gateways) - } - } - - if typ == gvk.VirtualService { - for _, obj := range ingressByHost { - out = append(out, *obj) - } - } - - return out, nil -} - -func (c *controller) Create(_ config.Config) (string, error) { - return "", errUnsupportedOp -} - -func (c *controller) Update(_ config.Config) (string, error) { - return "", errUnsupportedOp -} - -func (c *controller) UpdateStatus(config.Config) (string, error) { - return "", errUnsupportedOp -} - -func (c *controller) Patch(_ config.Config, _ config.PatchFunc) (string, error) { - return "", errUnsupportedOp -} - -func (c *controller) Delete(_ config.GroupVersionKind, _, _ string, _ *string) error { - return errUnsupportedOp -} diff --git a/pilot/pkg/config/kube/ingressv1/conversion.go b/pilot/pkg/config/kube/ingressv1/conversion.go deleted file mode 100644 index 91f4f60e0..000000000 --- a/pilot/pkg/config/kube/ingressv1/conversion.go +++ /dev/null @@ -1,382 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ingress - -import ( - "errors" - "fmt" - "sort" - "strconv" - "strings" -) - -import ( - "github.com/hashicorp/go-multierror" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" - "istio.io/pkg/log" - knetworking "k8s.io/api/networking/v1" - listerv1 "k8s.io/client-go/listers/core/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -const ( - IstioIngressController = "istio.io/ingress-controller" -) - -var errNotFound = errors.New("item not found") - -// EncodeIngressRuleName encodes an ingress rule name for a given ingress resource name, -// as well as the position of the rule and path specified within it, counting from 1. -// ruleNum == pathNum == 0 indicates the default backend specified for an ingress. -func EncodeIngressRuleName(ingressName string, ruleNum, pathNum int) string { - return fmt.Sprintf("%s-%d-%d", ingressName, ruleNum, pathNum) -} - -// decodeIngressRuleName decodes an ingress rule name previously encoded with EncodeIngressRuleName. -func decodeIngressRuleName(name string) (ingressName string, ruleNum, pathNum int, err error) { - parts := strings.Split(name, "-") - if len(parts) < 3 { - err = fmt.Errorf("could not decode string into ingress rule name: %s", name) - return - } - - ingressName = strings.Join(parts[0:len(parts)-2], "-") - ruleNum, ruleErr := strconv.Atoi(parts[len(parts)-2]) - pathNum, pathErr := strconv.Atoi(parts[len(parts)-1]) - - if pathErr != nil || ruleErr != nil { - err = multierror.Append( - fmt.Errorf("could not decode string into ingress rule name: %s", name), - pathErr, ruleErr) - return - } - - return -} - -// ConvertIngressV1alpha3 converts from ingress spec to Istio Gateway -func ConvertIngressV1alpha3(ingress knetworking.Ingress, mesh *meshconfig.MeshConfig, domainSuffix string) config.Config { - gateway := &networking.Gateway{} - gateway.Selector = getIngressGatewaySelector(mesh.IngressSelector, mesh.IngressService) - - for i, tls := range ingress.Spec.TLS { - if tls.SecretName == "" { - log.Infof("invalid ingress rule %s:%s for hosts %q, no secretName defined", ingress.Namespace, ingress.Name, tls.Hosts) - continue - } - // TODO validation when multiple wildcard tls secrets are given - if len(tls.Hosts) == 0 { - tls.Hosts = []string{"*"} - } - gateway.Servers = append(gateway.Servers, &networking.Server{ - Port: &networking.Port{ - Number: 443, - Protocol: string(protocol.HTTPS), - Name: fmt.Sprintf("https-443-ingress-%s-%s-%d", ingress.Name, ingress.Namespace, i), - }, - Hosts: tls.Hosts, - Tls: &networking.ServerTLSSettings{ - HttpsRedirect: false, - Mode: networking.ServerTLSSettings_SIMPLE, - CredentialName: tls.SecretName, - }, - }) - } - - gateway.Servers = append(gateway.Servers, &networking.Server{ - Port: &networking.Port{ - Number: 80, - Protocol: string(protocol.HTTP), - Name: fmt.Sprintf("http-80-ingress-%s-%s", ingress.Name, ingress.Namespace), - }, - Hosts: []string{"*"}, - }) - - gatewayConfig := config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.Gateway, - Name: ingress.Name + "-" + constants.IstioIngressGatewayName + "-" + ingress.Namespace, - Namespace: ingressNamespace, - Domain: domainSuffix, - }, - Spec: gateway, - } - - return gatewayConfig -} - -// ConvertIngressVirtualService converts from ingress spec to Istio VirtualServices -func ConvertIngressVirtualService(ingress knetworking.Ingress, domainSuffix string, - ingressByHost map[string]*config.Config, serviceLister listerv1.ServiceLister) { - // Ingress allows a single host - if missing '*' is assumed - // We need to merge all rules with a particular host across - // all ingresses, and return a separate VirtualService for each - // host. - if ingressNamespace == "" { - ingressNamespace = constants.IstioIngressNamespace - } - - for _, rule := range ingress.Spec.Rules { - if rule.HTTP == nil { - log.Infof("invalid ingress rule %s:%s for host %q, no paths defined", ingress.Namespace, ingress.Name, rule.Host) - continue - } - - host := rule.Host - namePrefix := strings.Replace(host, ".", "-", -1) - if host == "" { - host = "*" - } - virtualService := &networking.VirtualService{ - Hosts: []string{host}, - Gateways: []string{fmt.Sprintf("%s/%s-%s-%s", ingressNamespace, ingress.Name, constants.IstioIngressGatewayName, ingress.Namespace)}, - } - - httpRoutes := make([]*networking.HTTPRoute, 0, len(rule.HTTP.Paths)) - for _, httpPath := range rule.HTTP.Paths { - httpMatch := &networking.HTTPMatchRequest{} - if httpPath.PathType != nil { - switch *httpPath.PathType { - case knetworking.PathTypeExact: - httpMatch.Uri = &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: httpPath.Path}, - } - case knetworking.PathTypePrefix: - // Optimize common case of / to not needed regex - httpMatch.Uri = &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: httpPath.Path}, - } - default: - // Fallback to the legacy string matching - httpMatch.Uri = createFallbackStringMatch(httpPath.Path) - } - } else { - httpMatch.Uri = createFallbackStringMatch(httpPath.Path) - } - - httpRoute := ingressBackendToHTTPRoute(&httpPath.Backend, ingress.Namespace, domainSuffix, serviceLister) - if httpRoute == nil { - log.Infof("invalid ingress rule %s:%s for host %q, no backend defined for path", ingress.Namespace, ingress.Name, rule.Host) - continue - } - httpRoute.Match = []*networking.HTTPMatchRequest{httpMatch} - httpRoutes = append(httpRoutes, httpRoute) - } - - virtualService.Http = httpRoutes - - virtualServiceConfig := config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: namePrefix + "-" + ingress.Name + "-" + constants.IstioIngressGatewayName, - Namespace: ingress.Namespace, - Domain: domainSuffix, - Annotations: map[string]string{constants.InternalRouteSemantics: constants.RouteSemanticsIngress}, - }, - Spec: virtualService, - } - - old, f := ingressByHost[host] - if f { - vs := old.Spec.(*networking.VirtualService) - vs.Http = append(vs.Http, httpRoutes...) - if features.LegacyIngressBehavior { - sort.SliceStable(vs.Http, func(i, j int) bool { - r1 := vs.Http[i].Match[0].GetUri() - r2 := vs.Http[j].Match[0].GetUri() - _, r1Ex := r1.GetMatchType().(*networking.StringMatch_Exact) - _, r2Ex := r2.GetMatchType().(*networking.StringMatch_Exact) - // TODO: default at the end - if r1Ex && !r2Ex { - return true - } - return false - }) - } - } else { - ingressByHost[host] = &virtualServiceConfig - } - - if !features.LegacyIngressBehavior { - // sort routes to meet ingress route precedence requirements - // see https://kubernetes.io/docs/concepts/services-networking/ingress/#multiple-matches - vs := ingressByHost[host].Spec.(*networking.VirtualService) - sort.SliceStable(vs.Http, func(i, j int) bool { - r1Len, r1Ex := getMatchURILength(vs.Http[i].Match[0]) - r2Len, r2Ex := getMatchURILength(vs.Http[j].Match[0]) - // TODO: default at the end - if r1Len == r2Len { - return r1Ex && !r2Ex - } - return r1Len > r2Len - }) - } - } - - // Matches * and "/". Currently not supported - would conflict - // with any other explicit VirtualService. - if ingress.Spec.DefaultBackend != nil { - log.Infof("Ignore default wildcard ingress, use VirtualService %s:%s", - ingress.Namespace, ingress.Name) - } -} - -// getMatchURILength returns the length of matching path, and whether the match type is EXACT -func getMatchURILength(match *networking.HTTPMatchRequest) (length int, exact bool) { - uri := match.GetUri() - switch uri.GetMatchType().(type) { - case *networking.StringMatch_Exact: - return len(uri.GetExact()), true - case *networking.StringMatch_Prefix: - return len(uri.GetPrefix()), false - } - // should not happen - return -1, false -} - -func ingressBackendToHTTPRoute(backend *knetworking.IngressBackend, namespace string, domainSuffix string, - serviceLister listerv1.ServiceLister) *networking.HTTPRoute { - if backend == nil { - return nil - } - - port := &networking.PortSelector{} - - if backend.Service == nil { - log.Infof("backend service must be specified") - return nil - } - if backend.Service.Port.Number > 0 { - port.Number = uint32(backend.Service.Port.Number) - } else { - resolvedPort, err := resolveNamedPort(backend, namespace, serviceLister) - if err != nil { - log.Infof("failed to resolve named port %s, error: %v", backend.Service.Port.Name, err) - return nil - } - port.Number = uint32(resolvedPort) - } - - return &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: fmt.Sprintf("%s.%s.svc.%s", backend.Service.Name, namespace, domainSuffix), - Port: port, - }, - Weight: 100, - }, - }, - } -} - -func resolveNamedPort(backend *knetworking.IngressBackend, namespace string, serviceLister listerv1.ServiceLister) (int32, error) { - svc, err := serviceLister.Services(namespace).Get(backend.Service.Name) - if err != nil { - return 0, err - } - for _, port := range svc.Spec.Ports { - if port.Name == backend.Service.Port.Name { - return port.Port, nil - } - } - return 0, errNotFound -} - -// shouldProcessIngress determines whether the given knetworking resource should be processed -// by the controller, based on its knetworking class annotation or, in more recent versions of -// kubernetes (v1.18+), based on the Ingress's specified IngressClass -// See https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class -func shouldProcessIngressWithClass(mesh *meshconfig.MeshConfig, ingress *knetworking.Ingress, ingressClass *knetworking.IngressClass) bool { - if class, exists := ingress.Annotations[kube.IngressClassAnnotation]; exists { - switch mesh.IngressControllerMode { - case meshconfig.MeshConfig_OFF: - return false - case meshconfig.MeshConfig_STRICT: - return class == mesh.IngressClass - case meshconfig.MeshConfig_DEFAULT: - return class == mesh.IngressClass - default: - log.Warnf("invalid ingress synchronization mode: %v", mesh.IngressControllerMode) - return false - } - } else if ingressClass != nil { - return ingressClass.Spec.Controller == IstioIngressController - } else { - switch mesh.IngressControllerMode { - case meshconfig.MeshConfig_OFF: - return false - case meshconfig.MeshConfig_STRICT: - return false - case meshconfig.MeshConfig_DEFAULT: - return true - default: - log.Warnf("invalid ingress synchronization mode: %v", mesh.IngressControllerMode) - return false - } - } -} - -func createFallbackStringMatch(s string) *networking.StringMatch { - if s == "" { - return nil - } - - // Note that this implementation only converts prefix and exact matches, not regexps. - - // Replace e.g. "foo.*" with prefix match - if strings.HasSuffix(s, ".*") { - return &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: strings.TrimSuffix(s, ".*")}, - } - } - if strings.HasSuffix(s, "/*") { - return &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: strings.TrimSuffix(s, "/*")}, - } - } - - // Replace e.g. "foo" with a exact match - return &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: s}, - } -} - -func getIngressGatewaySelector(ingressSelector, ingressService string) map[string]string { - // Setup the selector for the gateway - if ingressSelector != "" { - // If explicitly defined, use this one - return labels.Instance{constants.IstioLabel: ingressSelector} - } else if ingressService != "istio-ingressgateway" && ingressService != "" { - // Otherwise, we will use the ingress service as the default. It is common for the selector and service - // to be the same, so this removes the need for two configurations - // However, if its istio-ingressgateway we need to use the old values for backwards compatibility - return labels.Instance{constants.IstioLabel: ingressService} - } else { - // If we have neither an explicitly defined ingressSelector or ingressService then use a selector - // pointing to the ingressgateway from the default installation - return labels.Instance{constants.IstioLabel: constants.IstioIngressLabelValue} - } -} diff --git a/pilot/pkg/config/kube/ingressv1/conversion_test.go b/pilot/pkg/config/kube/ingressv1/conversion_test.go deleted file mode 100644 index 0ab4750d1..000000000 --- a/pilot/pkg/config/kube/ingressv1/conversion_test.go +++ /dev/null @@ -1,513 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ingress - -import ( - "context" - "fmt" - "os" - "sort" - "strings" - "testing" - "time" -) - -import ( - "github.com/google/go-cmp/cmp" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" - coreV1 "k8s.io/api/core/v1" - knetworking "k8s.io/api/networking/v1" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes/fake" - "k8s.io/client-go/kubernetes/scheme" - listerv1 "k8s.io/client-go/listers/core/v1" - "k8s.io/client-go/tools/cache" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/crd" - "github.com/apache/dubbo-go-pixiu/pilot/test/util" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" -) - -func TestGoldenConversion(t *testing.T) { - cases := []string{"simple", "tls", "overlay", "tls-no-secret"} - for _, tt := range cases { - t.Run(tt, func(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - input, err := readConfig(t, fmt.Sprintf("testdata/%s.yaml", tt)) - if err != nil { - t.Fatal(err) - } - serviceLister := createFakeLister(ctx) - cfgs := map[string]*config.Config{} - for _, obj := range input { - ingress := obj.(*knetworking.Ingress) - ConvertIngressVirtualService(*ingress, "mydomain", cfgs, serviceLister) - } - ordered := []config.Config{} - for _, v := range cfgs { - ordered = append(ordered, *v) - } - for _, obj := range input { - ingress := obj.(*knetworking.Ingress) - m := mesh.DefaultMeshConfig() - gws := ConvertIngressV1alpha3(*ingress, m, "mydomain") - ordered = append(ordered, gws) - } - - sort.Slice(ordered, func(i, j int) bool { - return ordered[i].Name < ordered[j].Name - }) - output := marshalYaml(t, ordered) - goldenFile := fmt.Sprintf("testdata/%s.yaml.golden", tt) - if util.Refresh() { - if err := os.WriteFile(goldenFile, output, 0o644); err != nil { - t.Fatal(err) - } - } - expected, err := os.ReadFile(goldenFile) - if err != nil { - t.Fatal(err) - } - if diff := cmp.Diff(expected, output); diff != "" { - t.Fatalf("Diff:\n%s", diff) - } - }) - } -} - -// Print as YAML -func marshalYaml(t *testing.T, cl []config.Config) []byte { - t.Helper() - result := []byte{} - separator := []byte("---\n") - for _, config := range cl { - obj, err := crd.ConvertConfig(config) - if err != nil { - t.Fatalf("Could not decode %v: %v", config.Name, err) - } - bytes, err := yaml.Marshal(obj) - if err != nil { - t.Fatalf("Could not convert %v to YAML: %v", config, err) - } - result = append(result, bytes...) - result = append(result, separator...) - } - return result -} - -func readConfig(t *testing.T, filename string) ([]runtime.Object, error) { - t.Helper() - - data, err := os.ReadFile(filename) - if err != nil { - t.Fatalf("failed to read input yaml file: %v", err) - } - var varr []runtime.Object - for _, yml := range strings.Split(string(data), "\n---") { - obj, _, err := scheme.Codecs.UniversalDeserializer().Decode([]byte(yml), nil, nil) - if err != nil { - return nil, err - } - varr = append(varr, obj) - } - - return varr, nil -} - -func TestConversion(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - prefix := knetworking.PathTypePrefix - exact := knetworking.PathTypeExact - - ingress := knetworking.Ingress{ - ObjectMeta: metaV1.ObjectMeta{ - Namespace: "mock", // goes into backend full name - }, - Spec: knetworking.IngressSpec{ - Rules: []knetworking.IngressRule{ - { - Host: "my.host.com", - IngressRuleValue: knetworking.IngressRuleValue{ - HTTP: &knetworking.HTTPIngressRuleValue{ - Paths: []knetworking.HTTPIngressPath{ - { - Path: "/test", - Backend: knetworking.IngressBackend{ - Service: &knetworking.IngressServiceBackend{ - Name: "foo", - Port: knetworking.ServiceBackendPort{Number: 8000}, - }, - }, - }, - { - Path: "/test/foo", - PathType: &prefix, - Backend: knetworking.IngressBackend{ - Service: &knetworking.IngressServiceBackend{ - Name: "foo", - Port: knetworking.ServiceBackendPort{Number: 8000}, - }, - }, - }, - }, - }, - }, - }, - { - Host: "my2.host.com", - IngressRuleValue: knetworking.IngressRuleValue{ - HTTP: &knetworking.HTTPIngressRuleValue{ - Paths: []knetworking.HTTPIngressPath{ - { - Path: "/test1.*", - Backend: knetworking.IngressBackend{ - Service: &knetworking.IngressServiceBackend{ - Name: "bar", - Port: knetworking.ServiceBackendPort{Number: 8000}, - }, - }, - }, - }, - }, - }, - }, - { - Host: "my3.host.com", - IngressRuleValue: knetworking.IngressRuleValue{ - HTTP: &knetworking.HTTPIngressRuleValue{ - Paths: []knetworking.HTTPIngressPath{ - { - Path: "/test/*", - Backend: knetworking.IngressBackend{ - Service: &knetworking.IngressServiceBackend{ - Name: "bar", - Port: knetworking.ServiceBackendPort{Number: 8000}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - ingress2 := knetworking.Ingress{ - ObjectMeta: metaV1.ObjectMeta{ - Namespace: "mock", - }, - Spec: knetworking.IngressSpec{ - Rules: []knetworking.IngressRule{ - { - Host: "my.host.com", - IngressRuleValue: knetworking.IngressRuleValue{ - HTTP: &knetworking.HTTPIngressRuleValue{ - Paths: []knetworking.HTTPIngressPath{ - { - Path: "/test2", - Backend: knetworking.IngressBackend{ - Service: &knetworking.IngressServiceBackend{ - Name: "foo", - Port: knetworking.ServiceBackendPort{Number: 8000}, - }, - }, - }, - { - Path: "/test/foo/bar", - PathType: &prefix, - Backend: knetworking.IngressBackend{ - Service: &knetworking.IngressServiceBackend{ - Name: "foo", - Port: knetworking.ServiceBackendPort{Number: 8000}, - }, - }, - }, - { - Path: "/test/foo/bar", - PathType: &exact, - Backend: knetworking.IngressBackend{ - Service: &knetworking.IngressServiceBackend{ - Name: "foo", - Port: knetworking.ServiceBackendPort{Number: 8000}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - serviceLister := createFakeLister(ctx) - cfgs := map[string]*config.Config{} - ConvertIngressVirtualService(ingress, "mydomain", cfgs, serviceLister) - ConvertIngressVirtualService(ingress2, "mydomain", cfgs, serviceLister) - - if len(cfgs) != 3 { - t.Error("VirtualServices, expected 3 got ", len(cfgs)) - } - - expectedLength := [5]int{13, 13, 9, 6, 5} - expectedExact := [5]bool{true, false, false, true, true} - - for n, cfg := range cfgs { - vs := cfg.Spec.(*networking.VirtualService) - - if n == "my.host.com" { - if vs.Hosts[0] != "my.host.com" { - t.Error("Unexpected host", vs) - } - if len(vs.Http) != 5 { - t.Error("Unexpected rules", vs.Http) - } - for i, route := range vs.Http { - length, exact := getMatchURILength(route.Match[0]) - if length != expectedLength[i] || exact != expectedExact[i] { - t.Errorf("Unexpected rule at idx:%d, want {length:%d, exact:%v}, got {length:%d, exact: %v}", - i, expectedLength[i], expectedExact[i], length, exact) - } - } - } - } -} - -func TestDecodeIngressRuleName(t *testing.T) { - cases := []struct { - ingressName string - ruleNum int - pathNum int - }{ - {"myingress", 0, 0}, - {"myingress", 1, 2}, - {"my-ingress", 1, 2}, - {"my-cool-ingress", 1, 2}, - } - - for _, c := range cases { - encoded := EncodeIngressRuleName(c.ingressName, c.ruleNum, c.pathNum) - ingressName, ruleNum, pathNum, err := decodeIngressRuleName(encoded) - if err != nil { - t.Errorf("decodeIngressRuleName(%q) => error %v", encoded, err) - } - if ingressName != c.ingressName || ruleNum != c.ruleNum || pathNum != c.pathNum { - t.Errorf("decodeIngressRuleName(%q) => (%q, %d, %d), want (%q, %d, %d)", - encoded, - ingressName, ruleNum, pathNum, - c.ingressName, c.ruleNum, c.pathNum, - ) - } - } -} - -func TestEncoding(t *testing.T) { - if got := EncodeIngressRuleName("name", 3, 5); got != "name-3-5" { - t.Errorf("unexpected ingress encoding %q", got) - } - - cases := []string{ - "name", - "name-path-5", - "name-3-path", - } - for _, code := range cases { - if _, _, _, err := decodeIngressRuleName(code); err == nil { - t.Errorf("expected error on decoding %q", code) - } - } -} - -func TestIngressClass(t *testing.T) { - istio := mesh.DefaultMeshConfig().IngressClass - ingressClassIstio := &knetworking.IngressClass{ - ObjectMeta: metaV1.ObjectMeta{ - Name: "istio", - }, - Spec: knetworking.IngressClassSpec{ - Controller: IstioIngressController, - }, - } - ingressClassOther := &knetworking.IngressClass{ - ObjectMeta: metaV1.ObjectMeta{ - Name: "foo", - }, - Spec: knetworking.IngressClassSpec{ - Controller: "foo", - }, - } - cases := []struct { - annotation string - ingressClass *knetworking.IngressClass - ingressMode meshconfig.MeshConfig_IngressControllerMode - shouldProcess bool - }{ - // Annotation - {ingressMode: meshconfig.MeshConfig_DEFAULT, annotation: "nginx", shouldProcess: false}, - {ingressMode: meshconfig.MeshConfig_STRICT, annotation: "nginx", shouldProcess: false}, - {ingressMode: meshconfig.MeshConfig_OFF, annotation: istio, shouldProcess: false}, - {ingressMode: meshconfig.MeshConfig_DEFAULT, annotation: istio, shouldProcess: true}, - {ingressMode: meshconfig.MeshConfig_STRICT, annotation: istio, shouldProcess: true}, - {ingressMode: meshconfig.MeshConfig_DEFAULT, annotation: "", shouldProcess: true}, - {ingressMode: meshconfig.MeshConfig_STRICT, annotation: "", shouldProcess: false}, - - // IngressClass - {ingressMode: meshconfig.MeshConfig_DEFAULT, ingressClass: ingressClassOther, shouldProcess: false}, - {ingressMode: meshconfig.MeshConfig_STRICT, ingressClass: ingressClassOther, shouldProcess: false}, - {ingressMode: meshconfig.MeshConfig_DEFAULT, ingressClass: ingressClassIstio, shouldProcess: true}, - {ingressMode: meshconfig.MeshConfig_STRICT, ingressClass: ingressClassIstio, shouldProcess: true}, - {ingressMode: meshconfig.MeshConfig_DEFAULT, ingressClass: nil, shouldProcess: true}, - {ingressMode: meshconfig.MeshConfig_STRICT, ingressClass: nil, shouldProcess: false}, - - // IngressClass and Annotation - // note: k8s rejects Ingress resources configured with kubernetes.io/ingress.class annotation *and* ingressClassName field so this shouldn't happen - // see https://github.com/kubernetes/kubernetes/blob/ededd08ba131b727e60f663bd7217fffaaccd448/pkg/apis/networking/validation/validation.go#L226 - {ingressMode: meshconfig.MeshConfig_STRICT, ingressClass: ingressClassIstio, annotation: "nginx", shouldProcess: false}, - {ingressMode: meshconfig.MeshConfig_STRICT, ingressClass: ingressClassOther, annotation: istio, shouldProcess: true}, - {ingressMode: -1, shouldProcess: false}, - } - - for i, c := range cases { - className := "" - if c.ingressClass != nil { - className = c.ingressClass.Name - } - t.Run(fmt.Sprintf("%d %s %s %s", i, c.ingressMode, c.annotation, className), func(t *testing.T) { - ing := knetworking.Ingress{ - ObjectMeta: metaV1.ObjectMeta{ - Name: "test-ingress", - Namespace: "default", - Annotations: make(map[string]string), - }, - Spec: knetworking.IngressSpec{ - DefaultBackend: &knetworking.IngressBackend{ - Service: &knetworking.IngressServiceBackend{ - Name: "default-http-backend", - Port: knetworking.ServiceBackendPort{Number: 8000}, - }, - }, - }, - } - - mesh := mesh.DefaultMeshConfig() - mesh.IngressControllerMode = c.ingressMode - - if c.annotation != "" { - ing.Annotations["kubernetes.io/ingress.class"] = c.annotation - } - - if c.shouldProcess != shouldProcessIngressWithClass(mesh, &ing, c.ingressClass) { - t.Errorf("got %v, want %v", - !c.shouldProcess, c.shouldProcess) - } - }) - } -} - -func TestNamedPortIngressConversion(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - ingress := knetworking.Ingress{ - ObjectMeta: metaV1.ObjectMeta{ - Namespace: "mock", - }, - Spec: knetworking.IngressSpec{ - Rules: []knetworking.IngressRule{ - { - Host: "host.com", - IngressRuleValue: knetworking.IngressRuleValue{ - HTTP: &knetworking.HTTPIngressRuleValue{ - Paths: []knetworking.HTTPIngressPath{ - { - Path: "/test/*", - Backend: knetworking.IngressBackend{ - Service: &knetworking.IngressServiceBackend{ - Name: "foo", - Port: knetworking.ServiceBackendPort{Name: "test-svc-port"}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - service := &coreV1.Service{ - ObjectMeta: metaV1.ObjectMeta{ - Name: "foo", - Namespace: "mock", - }, - Spec: coreV1.ServiceSpec{ - Ports: []coreV1.ServicePort{ - { - Name: "test-svc-port", - Protocol: "TCP", - Port: 8888, - TargetPort: intstr.IntOrString{ - Type: intstr.String, - StrVal: "test-port", - }, - }, - }, - Selector: map[string]string{ - "app": "test-app", - }, - }, - } - serviceLister := createFakeLister(ctx, service) - cfgs := map[string]*config.Config{} - ConvertIngressVirtualService(ingress, "mydomain", cfgs, serviceLister) - if len(cfgs) != 1 { - t.Error("VirtualServices, expected 1 got ", len(cfgs)) - } - if cfgs["host.com"] == nil { - t.Error("Host, found nil") - } - vs := cfgs["host.com"].Spec.(*networking.VirtualService) - if len(vs.Http) != 1 { - t.Error("HttpSpec, expected 1 got ", len(vs.Http)) - } - http := vs.Http[0] - if len(http.Route) != 1 { - t.Error("Route, expected 1 got ", len(http.Route)) - } - route := http.Route[0] - if route.Destination.Port.Number != 8888 { - t.Error("PortNumer, expected 8888 got ", route.Destination.Port.Number) - } -} - -func createFakeLister(ctx context.Context, objects ...runtime.Object) listerv1.ServiceLister { - client := fake.NewSimpleClientset(objects...) - informerFactory := informers.NewSharedInformerFactory(client, time.Hour) - svcInformer := informerFactory.Core().V1().Services().Informer() - go svcInformer.Run(ctx.Done()) - cache.WaitForCacheSync(ctx.Done(), svcInformer.HasSynced) - return informerFactory.Core().V1().Services().Lister() -} diff --git a/pilot/pkg/config/kube/ingressv1/leak_test.go b/pilot/pkg/config/kube/ingressv1/leak_test.go deleted file mode 100644 index f9d162316..000000000 --- a/pilot/pkg/config/kube/ingressv1/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ingress - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/config/kube/ingressv1/status.go b/pilot/pkg/config/kube/ingressv1/status.go deleted file mode 100644 index d8f21192a..000000000 --- a/pilot/pkg/config/kube/ingressv1/status.go +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ingress - -import ( - "context" - "net" - "sort" - "strings" - "time" -) - -import ( - "istio.io/pkg/log" - coreV1 "k8s.io/api/core/v1" - knetworking "k8s.io/api/networking/v1" - kerrors "k8s.io/apimachinery/pkg/api/errors" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/kubernetes" - listerv1 "k8s.io/client-go/listers/core/v1" - ingresslister "k8s.io/client-go/listers/networking/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - kubelib "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/queue" -) - -const ( - updateInterval = 60 * time.Second -) - -// StatusSyncer keeps the status IP in each Ingress resource updated -type StatusSyncer struct { - meshHolder mesh.Holder - client kubernetes.Interface - - queue queue.Instance - ingressLister ingresslister.IngressLister - podLister listerv1.PodLister - serviceLister listerv1.ServiceLister - nodeLister listerv1.NodeLister - ingressClassLister ingresslister.IngressClassLister -} - -// Run the syncer until stopCh is closed -func (s *StatusSyncer) Run(stopCh <-chan struct{}) { - go s.queue.Run(stopCh) - go s.runUpdateStatus(stopCh) -} - -// NewStatusSyncer creates a new instance -func NewStatusSyncer(meshHolder mesh.Holder, client kubelib.Client) *StatusSyncer { - // queue requires a time duration for a retry delay after a handler error - q := queue.NewQueue(5 * time.Second) - - return &StatusSyncer{ - meshHolder: meshHolder, - client: client, - ingressLister: client.KubeInformer().Networking().V1().Ingresses().Lister(), - podLister: client.KubeInformer().Core().V1().Pods().Lister(), - serviceLister: client.KubeInformer().Core().V1().Services().Lister(), - nodeLister: client.KubeInformer().Core().V1().Nodes().Lister(), - ingressClassLister: client.KubeInformer().Networking().V1().IngressClasses().Lister(), - queue: q, - } -} - -func (s *StatusSyncer) onEvent() error { - addrs, err := s.runningAddresses(ingressNamespace) - if err != nil { - if kerrors.IsNotFound(err) { - return nil - } - return err - } - - return s.updateStatus(sliceToStatus(addrs)) -} - -func (s *StatusSyncer) runUpdateStatus(stop <-chan struct{}) { - if _, err := s.runningAddresses(ingressNamespace); err != nil { - log.Warn("Missing ingress, skip status updates") - err = wait.PollUntil(10*time.Second, func() (bool, error) { - if sa, err := s.runningAddresses(ingressNamespace); err != nil || len(sa) == 0 { - return false, nil - } - return true, nil - }, stop) - if err != nil { - log.Warn("Error waiting for ingress") - return - } - } - err := wait.PollUntil(updateInterval, func() (bool, error) { - s.queue.Push(s.onEvent) - return false, nil - }, stop) - if err != nil { - log.Errorf("Stop requested") - } -} - -// updateStatus updates ingress status with the list of IP -func (s *StatusSyncer) updateStatus(status []coreV1.LoadBalancerIngress) error { - l, err := s.ingressLister.List(labels.Everything()) - if err != nil { - return err - } - - if len(l) == 0 { - return nil - } - - sort.SliceStable(status, lessLoadBalancerIngress(status)) - - for _, currIng := range l { - shouldTarget, err := s.shouldTargetIngress(currIng) - if err != nil { - log.Warnf("error determining whether should target ingress for status update: %v", err) - return err - } - if !shouldTarget { - continue - } - - curIPs := currIng.Status.LoadBalancer.Ingress - sort.SliceStable(curIPs, lessLoadBalancerIngress(curIPs)) - - if ingressSliceEqual(status, curIPs) { - log.Debugf("skipping update of Ingress %v/%v (no change)", currIng.Namespace, currIng.Name) - continue - } - - currIng.Status.LoadBalancer.Ingress = status - - _, err = s.client.NetworkingV1().Ingresses(currIng.Namespace).UpdateStatus(context.TODO(), currIng, metaV1.UpdateOptions{}) - if err != nil { - log.Warnf("error updating ingress status: %v", err) - } - } - - return nil -} - -// runningAddresses returns a list of IP addresses and/or FQDN in the namespace -// where the ingress controller is currently running -func (s *StatusSyncer) runningAddresses(ingressNs string) ([]string, error) { - addrs := make([]string, 0) - ingressService := s.meshHolder.Mesh().IngressService - ingressSelector := s.meshHolder.Mesh().IngressSelector - - if ingressService != "" { - svc, err := s.serviceLister.Services(ingressNs).Get(ingressService) - if err != nil { - return nil, err - } - - if svc.Spec.Type == coreV1.ServiceTypeExternalName { - addrs = append(addrs, svc.Spec.ExternalName) - return addrs, nil - } - - for _, ip := range svc.Status.LoadBalancer.Ingress { - if ip.IP == "" { - addrs = append(addrs, ip.Hostname) - } else { - addrs = append(addrs, ip.IP) - } - } - - addrs = append(addrs, svc.Spec.ExternalIPs...) - return addrs, nil - } - - // get all pods acting as ingress gateways - igSelector := getIngressGatewaySelector(ingressSelector, ingressService) - igPods, err := s.podLister.Pods(ingressNamespace).List(labels.SelectorFromSet(igSelector)) - if err != nil { - return nil, err - } - - for _, pod := range igPods { - // only Running pods are valid - if pod.Status.Phase != coreV1.PodRunning { - continue - } - - // Find node external IP - node, err := s.nodeLister.Get(pod.Spec.NodeName) - if err != nil { - continue - } - - for _, address := range node.Status.Addresses { - if address.Type == coreV1.NodeExternalIP { - if address.Address != "" && !addressInSlice(address.Address, addrs) { - addrs = append(addrs, address.Address) - } - } - } - } - - return addrs, nil -} - -func addressInSlice(addr string, list []string) bool { - for _, v := range list { - if v == addr { - return true - } - } - - return false -} - -// sliceToStatus converts a slice of IP and/or hostnames to LoadBalancerIngress -func sliceToStatus(endpoints []string) []coreV1.LoadBalancerIngress { - lbi := make([]coreV1.LoadBalancerIngress, 0, len(endpoints)) - for _, ep := range endpoints { - if net.ParseIP(ep) == nil { - lbi = append(lbi, coreV1.LoadBalancerIngress{Hostname: ep}) - } else { - lbi = append(lbi, coreV1.LoadBalancerIngress{IP: ep}) - } - } - - return lbi -} - -func lessLoadBalancerIngress(addrs []coreV1.LoadBalancerIngress) func(int, int) bool { - return func(a, b int) bool { - switch strings.Compare(addrs[a].Hostname, addrs[b].Hostname) { - case -1: - return true - case 1: - return false - } - return addrs[a].IP < addrs[b].IP - } -} - -func ingressSliceEqual(lhs, rhs []coreV1.LoadBalancerIngress) bool { - if len(lhs) != len(rhs) { - return false - } - - for i := range lhs { - if lhs[i].IP != rhs[i].IP { - return false - } - if lhs[i].Hostname != rhs[i].Hostname { - return false - } - } - return true -} - -// shouldTargetIngress determines whether the status watcher should target a given ingress resource -func (s *StatusSyncer) shouldTargetIngress(ingress *knetworking.Ingress) (bool, error) { - var ingressClass *knetworking.IngressClass - if s.ingressClassLister != nil && ingress.Spec.IngressClassName != nil { - c, err := s.ingressClassLister.Get(*ingress.Spec.IngressClassName) - if err != nil && !kerrors.IsNotFound(err) { - return false, err - } - ingressClass = c - } - return shouldProcessIngressWithClass(s.meshHolder.Mesh(), ingress, ingressClass), nil -} diff --git a/pilot/pkg/config/kube/ingressv1/status_test.go b/pilot/pkg/config/kube/ingressv1/status_test.go deleted file mode 100644 index 4f2b9e62b..000000000 --- a/pilot/pkg/config/kube/ingressv1/status_test.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ingress - -import ( - "context" - "os" - "testing" -) - -import ( - coreV1 "k8s.io/api/core/v1" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - kubelib "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -var ( - pod = "test" - serviceIP = "1.2.3.4" - hostname = "foo.bar.com" - nodeIP = "10.0.0.2" - testNamespace = "test" -) - -func setupFake(t *testing.T, client kubelib.Client) { - t.Helper() - if _, err := client.Kube().CoreV1().Pods("dubbo-system").Create(context.TODO(), &coreV1.Pod{ - ObjectMeta: metaV1.ObjectMeta{ - Name: "ingressgateway", - Namespace: "dubbo-system", - Labels: map[string]string{ - "istio": "ingressgateway", - }, - }, - Spec: coreV1.PodSpec{ - NodeName: "foo_node", - }, - Status: coreV1.PodStatus{ - Phase: coreV1.PodRunning, - }, - }, metaV1.CreateOptions{}); err != nil { - t.Fatal(err) - } - - if _, err := client.Kube().CoreV1().Services(testNamespace).Create(context.TODO(), &coreV1.Service{ - ObjectMeta: metaV1.ObjectMeta{ - Name: "istio-ingress", - Namespace: testNamespace, - }, - Status: coreV1.ServiceStatus{ - LoadBalancer: coreV1.LoadBalancerStatus{ - Ingress: []coreV1.LoadBalancerIngress{{ - IP: serviceIP, - }}, - }, - }, - }, metaV1.CreateOptions{}); err != nil { - t.Fatal(err) - } - - if _, err := client.Kube().CoreV1().Services(testNamespace).Create(context.TODO(), &coreV1.Service{ - ObjectMeta: metaV1.ObjectMeta{ - Name: "istio-ingress-hostname", - Namespace: testNamespace, - }, - Status: coreV1.ServiceStatus{ - LoadBalancer: coreV1.LoadBalancerStatus{ - Ingress: []coreV1.LoadBalancerIngress{{ - Hostname: hostname, - }}, - }, - }, - }, metaV1.CreateOptions{}); err != nil { - t.Fatal(err) - } - if _, err := client.Kube().CoreV1().Nodes().Create(context.TODO(), &coreV1.Node{ - ObjectMeta: metaV1.ObjectMeta{ - Name: "foo_node", - }, - Status: coreV1.NodeStatus{ - Addresses: []coreV1.NodeAddress{ - { - Type: coreV1.NodeExternalIP, - Address: nodeIP, - }, - }, - }, - }, metaV1.CreateOptions{}); err != nil { - t.Fatal(err) - } -} - -func fakeMeshHolder(ingressService string) mesh.Holder { - config := mesh.DefaultMeshConfig() - config.IngressService = ingressService - return mesh.NewFixedWatcher(config) -} - -func makeStatusSyncer(t *testing.T) *StatusSyncer { - oldEnvs := setAndRestoreEnv(t, map[string]string{"POD_NAME": pod, "POD_NAMESPACE": testNamespace}) - // Restore env settings - defer setAndRestoreEnv(t, oldEnvs) - - client := kubelib.NewFakeClient() - setupFake(t, client) - sync := NewStatusSyncer(fakeMeshHolder("istio-ingress"), client) - stop := make(chan struct{}) - client.RunAndWait(stop) - t.Cleanup(func() { - close(stop) - }) - return sync -} - -// setAndRestoreEnv set the envs with given value, and return the old setting. -func setAndRestoreEnv(t *testing.T, inputs map[string]string) map[string]string { - oldEnvs := map[string]string{} - for k, v := range inputs { - oldEnvs[k] = os.Getenv(k) - if err := os.Setenv(k, v); err != nil { - t.Error(err) - } - } - - return oldEnvs -} - -func TestRunningAddresses(t *testing.T) { - t.Run("service", testRunningAddressesWithService) - t.Run("hostname", testRunningAddressesWithHostname) -} - -func testRunningAddressesWithService(t *testing.T) { - syncer := makeStatusSyncer(t) - address, err := syncer.runningAddresses(testNamespace) - if err != nil { - t.Fatal(err) - } - - if len(address) != 1 || address[0] != serviceIP { - t.Errorf("Address is not correctly set to service ip") - } -} - -func testRunningAddressesWithHostname(t *testing.T) { - syncer := makeStatusSyncer(t) - syncer.meshHolder = fakeMeshHolder("istio-ingress-hostname") - - address, err := syncer.runningAddresses(testNamespace) - if err != nil { - t.Fatal(err) - } - - if len(address) != 1 || address[0] != hostname { - t.Errorf("Address is not correctly set to hostname") - } -} - -func TestRunningAddressesWithPod(t *testing.T) { - ingressNamespace = "dubbo-system" // it is set in real pilot on newController. - syncer := makeStatusSyncer(t) - syncer.meshHolder = fakeMeshHolder("") - - address, err := syncer.runningAddresses(ingressNamespace) - if err != nil { - t.Fatal(err) - } - - if len(address) != 1 || address[0] != nodeIP { - t.Errorf("Address is not correctly set to node ip %v %v", address, nodeIP) - } -} diff --git a/pilot/pkg/config/kube/ingressv1/testdata/overlay.yaml b/pilot/pkg/config/kube/ingressv1/testdata/overlay.yaml deleted file mode 100644 index 1f61a7902..000000000 --- a/pilot/pkg/config/kube/ingressv1/testdata/overlay.yaml +++ /dev/null @@ -1,32 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: foo - namespace: ns -spec: - rules: - - host: foohost.bar.com - http: - paths: - - backend: - service: - name: service1 - port: - number: 4200 ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: foo2 - namespace: ns -spec: - rules: - - host: foohost.bar.com - http: - paths: - - path: /second - backend: - service: - name: service2 - port: - number: 4200 \ No newline at end of file diff --git a/pilot/pkg/config/kube/ingressv1/testdata/overlay.yaml.golden b/pilot/pkg/config/kube/ingressv1/testdata/overlay.yaml.golden deleted file mode 100644 index dfdea0ca3..000000000 --- a/pilot/pkg/config/kube/ingressv1/testdata/overlay.yaml.golden +++ /dev/null @@ -1,66 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - creationTimestamp: null - name: foo-istio-autogenerated-k8s-ingress-ns - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - '*' - port: - name: http-80-ingress-foo-ns - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - creationTimestamp: null - name: foo2-istio-autogenerated-k8s-ingress-ns - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - '*' - port: - name: http-80-ingress-foo2-ns - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/route-semantics: ingress - creationTimestamp: null - name: foohost-bar-com-foo-istio-autogenerated-k8s-ingress - namespace: ns -spec: - gateways: - - dubbo-system/foo-istio-autogenerated-k8s-ingress-ns - hosts: - - foohost.bar.com - http: - - match: - - uri: - exact: /second - route: - - destination: - host: service2.ns.svc.mydomain - port: - number: 4200 - weight: 100 - - match: - - {} - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4200 - weight: 100 ---- diff --git a/pilot/pkg/config/kube/ingressv1/testdata/simple.yaml b/pilot/pkg/config/kube/ingressv1/testdata/simple.yaml deleted file mode 100644 index 1c9b7c95a..000000000 --- a/pilot/pkg/config/kube/ingressv1/testdata/simple.yaml +++ /dev/null @@ -1,84 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: foo - namespace: ns -spec: - rules: - - host: foohost.bar.com - http: - paths: - # Simple path - - path: /path - backend: - service: - name: service1 - port: - number: 4200 - # Subpath without trailing / - - path: /sub/path - backend: - service: - name: service1 - port: - number: 4201 - # With a trailing / - - path: /sub/path/ - backend: - service: - name: service1 - port: - number: 4202 - # Regex ending with .* - - path: /regex1.* - backend: - service: - name: service1 - port: - number: 4203 - # Regex ending with * - - path: /regex2* - backend: - service: - name: service1 - port: - number: 4204 - # Regex ending with /* - - path: /regex3/* - backend: - service: - name: service1 - port: - number: 4205 - # Subpath without trailing /, Exact match - - path: /sub/path - pathType: Exact - backend: - service: - name: service1 - port: - number: 4206 - # With a trailing /, Exact match - - path: /sub/path/ - pathType: Exact - backend: - service: - name: service1 - port: - number: 4207 - # Subpath without trailing /, Prefix match - - path: /sub/path - pathType: Prefix - backend: - service: - name: service1 - port: - number: 4208 - # With a trailing /, Prefix match - - path: /sub/path/ - pathType: Prefix - backend: - service: - name: service1 - port: - number: 4209 diff --git a/pilot/pkg/config/kube/ingressv1/testdata/simple.yaml.golden b/pilot/pkg/config/kube/ingressv1/testdata/simple.yaml.golden deleted file mode 100644 index c0ba4ae9d..000000000 --- a/pilot/pkg/config/kube/ingressv1/testdata/simple.yaml.golden +++ /dev/null @@ -1,122 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - creationTimestamp: null - name: foo-istio-autogenerated-k8s-ingress-ns - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - '*' - port: - name: http-80-ingress-foo-ns - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/route-semantics: ingress - creationTimestamp: null - name: foohost-bar-com-foo-istio-autogenerated-k8s-ingress - namespace: ns -spec: - gateways: - - dubbo-system/foo-istio-autogenerated-k8s-ingress-ns - hosts: - - foohost.bar.com - http: - - match: - - uri: - exact: /sub/path/ - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4202 - weight: 100 - - match: - - uri: - exact: /sub/path/ - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4207 - weight: 100 - - match: - - uri: - prefix: /sub/path/ - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4209 - weight: 100 - - match: - - uri: - exact: /sub/path - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4201 - weight: 100 - - match: - - uri: - exact: /sub/path - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4206 - weight: 100 - - match: - - uri: - prefix: /sub/path - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4208 - weight: 100 - - match: - - uri: - exact: /regex2* - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4204 - weight: 100 - - match: - - uri: - prefix: /regex1 - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4203 - weight: 100 - - match: - - uri: - prefix: /regex3 - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4205 - weight: 100 - - match: - - uri: - exact: /path - route: - - destination: - host: service1.ns.svc.mydomain - port: - number: 4200 - weight: 100 ---- diff --git a/pilot/pkg/config/kube/ingressv1/testdata/tls-no-secret.yaml b/pilot/pkg/config/kube/ingressv1/testdata/tls-no-secret.yaml deleted file mode 100644 index 310f9b9e0..000000000 --- a/pilot/pkg/config/kube/ingressv1/testdata/tls-no-secret.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: tls - namespace: bar -spec: - rules: - - host: foo.org - http: - paths: - - backend: - service: - name: httpbin - port: - number: 80 - path: /* - tls: - - hosts: - - foo.org diff --git a/pilot/pkg/config/kube/ingressv1/testdata/tls-no-secret.yaml.golden b/pilot/pkg/config/kube/ingressv1/testdata/tls-no-secret.yaml.golden deleted file mode 100644 index 29a96cb58..000000000 --- a/pilot/pkg/config/kube/ingressv1/testdata/tls-no-secret.yaml.golden +++ /dev/null @@ -1,41 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/route-semantics: ingress - creationTimestamp: null - name: foo-org-tls-istio-autogenerated-k8s-ingress - namespace: bar -spec: - gateways: - - dubbo-system/tls-istio-autogenerated-k8s-ingress-bar - hosts: - - foo.org - http: - - match: - - uri: - prefix: "" - route: - - destination: - host: httpbin.bar.svc.mydomain - port: - number: 80 - weight: 100 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - creationTimestamp: null - name: tls-istio-autogenerated-k8s-ingress-bar - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - '*' - port: - name: http-80-ingress-tls-bar - number: 80 - protocol: HTTP ---- diff --git a/pilot/pkg/config/kube/ingressv1/testdata/tls.yaml b/pilot/pkg/config/kube/ingressv1/testdata/tls.yaml deleted file mode 100644 index 967a0e171..000000000 --- a/pilot/pkg/config/kube/ingressv1/testdata/tls.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: tls - namespace: bar -spec: - rules: - - host: foo.org - http: - paths: - - backend: - service: - name: httpbin - port: - number: 80 - path: /* - tls: - - hosts: - - foo.org - secretName: myingress-cert diff --git a/pilot/pkg/config/kube/ingressv1/testdata/tls.yaml.golden b/pilot/pkg/config/kube/ingressv1/testdata/tls.yaml.golden deleted file mode 100644 index 6af1a705f..000000000 --- a/pilot/pkg/config/kube/ingressv1/testdata/tls.yaml.golden +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - annotations: - internal.istio.io/route-semantics: ingress - creationTimestamp: null - name: foo-org-tls-istio-autogenerated-k8s-ingress - namespace: bar -spec: - gateways: - - dubbo-system/tls-istio-autogenerated-k8s-ingress-bar - hosts: - - foo.org - http: - - match: - - uri: - prefix: "" - route: - - destination: - host: httpbin.bar.svc.mydomain - port: - number: 80 - weight: 100 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - creationTimestamp: null - name: tls-istio-autogenerated-k8s-ingress-bar - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - foo.org - port: - name: https-443-ingress-tls-bar-0 - number: 443 - protocol: HTTPS - tls: - credentialName: myingress-cert - mode: SIMPLE - - hosts: - - '*' - port: - name: http-80-ingress-tls-bar - number: 80 - protocol: HTTP ---- diff --git a/pilot/pkg/config/memory/controller.go b/pilot/pkg/config/memory/controller.go deleted file mode 100644 index be5a40537..000000000 --- a/pilot/pkg/config/memory/controller.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package memory - -import ( - "errors" - "fmt" -) - -import ( - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" -) - -// Controller is an implementation of ConfigStoreController. -type Controller struct { - monitor Monitor - configStore model.ConfigStore - hasSynced func() bool -} - -// NewController return an implementation of ConfigStoreController -// This is a client-side monitor that dispatches events as the changes are being -// made on the client. -func NewController(cs model.ConfigStore) *Controller { - out := &Controller{ - configStore: cs, - monitor: NewMonitor(cs), - } - return out -} - -// NewSyncController return an implementation of model.ConfigStoreController which processes events synchronously -func NewSyncController(cs model.ConfigStore) *Controller { - out := &Controller{ - configStore: cs, - monitor: NewSyncMonitor(cs), - } - - return out -} - -func (c *Controller) RegisterHasSyncedHandler(cb func() bool) { - c.hasSynced = cb -} - -func (c *Controller) RegisterEventHandler(kind config.GroupVersionKind, f model.EventHandler) { - c.monitor.AppendEventHandler(kind, f) -} - -func (c *Controller) SetWatchErrorHandler(handler func(r *cache.Reflector, err error)) error { - return nil -} - -// HasSynced return whether store has synced -// It can be controlled externally (such as by the data source), -// otherwise it'll always consider synced. -func (c *Controller) HasSynced() bool { - if c.hasSynced != nil { - return c.hasSynced() - } - return true -} - -func (c *Controller) Run(stop <-chan struct{}) { - c.monitor.Run(stop) -} - -func (c *Controller) Schemas() collection.Schemas { - return c.configStore.Schemas() -} - -func (c *Controller) Get(kind config.GroupVersionKind, key, namespace string) *config.Config { - return c.configStore.Get(kind, key, namespace) -} - -func (c *Controller) Create(config config.Config) (revision string, err error) { - if revision, err = c.configStore.Create(config); err == nil { - c.monitor.ScheduleProcessEvent(ConfigEvent{ - config: config, - event: model.EventAdd, - }) - } - return -} - -func (c *Controller) Update(config config.Config) (newRevision string, err error) { - oldconfig := c.configStore.Get(config.GroupVersionKind, config.Name, config.Namespace) - if newRevision, err = c.configStore.Update(config); err == nil { - c.monitor.ScheduleProcessEvent(ConfigEvent{ - old: *oldconfig, - config: config, - event: model.EventUpdate, - }) - } - return -} - -func (c *Controller) UpdateStatus(config config.Config) (newRevision string, err error) { - oldconfig := c.configStore.Get(config.GroupVersionKind, config.Name, config.Namespace) - if newRevision, err = c.configStore.UpdateStatus(config); err == nil { - c.monitor.ScheduleProcessEvent(ConfigEvent{ - old: *oldconfig, - config: config, - event: model.EventUpdate, - }) - } - return -} - -func (c *Controller) Patch(orig config.Config, patchFn config.PatchFunc) (newRevision string, err error) { - cfg, typ := patchFn(orig.DeepCopy()) - switch typ { - case types.MergePatchType: - case types.JSONPatchType: - default: - return "", fmt.Errorf("unsupported merge type: %s", typ) - } - if newRevision, err = c.configStore.Patch(cfg, patchFn); err == nil { - c.monitor.ScheduleProcessEvent(ConfigEvent{ - old: orig, - config: cfg, - event: model.EventUpdate, - }) - } - return -} - -func (c *Controller) Delete(kind config.GroupVersionKind, key, namespace string, resourceVersion *string) (err error) { - if config := c.Get(kind, key, namespace); config != nil { - if err = c.configStore.Delete(kind, key, namespace, resourceVersion); err == nil { - c.monitor.ScheduleProcessEvent(ConfigEvent{ - config: *config, - event: model.EventDelete, - }) - return - } - } - return errors.New("Delete failure: config" + key + "does not exist") -} - -func (c *Controller) List(kind config.GroupVersionKind, namespace string) ([]config.Config, error) { - return c.configStore.List(kind, namespace) -} diff --git a/pilot/pkg/config/memory/controller_test.go b/pilot/pkg/config/memory/controller_test.go deleted file mode 100644 index ba5bc1590..000000000 --- a/pilot/pkg/config/memory/controller_test.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package memory_test - -import ( - "sync/atomic" - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/memory" - "github.com/apache/dubbo-go-pixiu/pilot/test/mock" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -const ( - // TestNamespace specifies the namespace for testing - TestNamespace = "istio-memory-test" -) - -func TestControllerEvents(t *testing.T) { - store := memory.Make(collections.Mocks) - ctl := memory.NewController(store) - // Note that the operations must go through the controller since the store does not trigger back events - mock.CheckCacheEvents(ctl, ctl, TestNamespace, 5, t) -} - -func TestControllerCacheFreshness(t *testing.T) { - store := memory.Make(collections.Mocks) - ctl := memory.NewController(store) - mock.CheckCacheFreshness(ctl, TestNamespace, t) -} - -func TestControllerClientSync(t *testing.T) { - store := memory.Make(collections.Mocks) - ctl := memory.NewController(store) - mock.CheckCacheSync(store, ctl, TestNamespace, 5, t) -} - -func TestControllerHashSynced(t *testing.T) { - store := memory.Make(collections.Mocks) - var v int32 - ctl := memory.NewController(store) - - ctl.RegisterHasSyncedHandler(func() bool { - return atomic.LoadInt32(&v) > 0 - }) - - if ctl.HasSynced() { - t.Error("has synced but should not") - } - atomic.StoreInt32(&v, 1) - if !ctl.HasSynced() { - t.Error("has not synced but should") - } -} diff --git a/pilot/pkg/config/memory/leak_test.go b/pilot/pkg/config/memory/leak_test.go deleted file mode 100644 index 21ed296a4..000000000 --- a/pilot/pkg/config/memory/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package memory - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/config/memory/monitor.go b/pilot/pkg/config/memory/monitor.go deleted file mode 100644 index f3b9a9b91..000000000 --- a/pilot/pkg/config/memory/monitor.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package memory - -import ( - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - config2 "github.com/apache/dubbo-go-pixiu/pkg/config" -) - -const ( - // BufferSize specifies the buffer size of event channel - BufferSize = 100 -) - -// Handler specifies a function to apply on a Config for a given event type -type Handler func(config2.Config, config2.Config, model.Event) - -// Monitor provides methods of manipulating changes in the config store -type Monitor interface { - Run(<-chan struct{}) - AppendEventHandler(config2.GroupVersionKind, Handler) - ScheduleProcessEvent(ConfigEvent) -} - -// ConfigEvent defines the event to be processed -type ConfigEvent struct { - config config2.Config - old config2.Config - event model.Event -} - -type configStoreMonitor struct { - store model.ConfigStore - handlers map[config2.GroupVersionKind][]Handler - eventCh chan ConfigEvent - // If enabled, events will be handled synchronously - sync bool -} - -// NewMonitor returns new Monitor implementation with a default event buffer size. -func NewMonitor(store model.ConfigStore) Monitor { - return newBufferedMonitor(store, BufferSize, false) -} - -// NewMonitor returns new Monitor implementation which will process events synchronously -func NewSyncMonitor(store model.ConfigStore) Monitor { - return newBufferedMonitor(store, BufferSize, true) -} - -// NewBufferedMonitor returns new Monitor implementation with the specified event buffer size -func newBufferedMonitor(store model.ConfigStore, bufferSize int, sync bool) Monitor { - handlers := make(map[config2.GroupVersionKind][]Handler) - - for _, s := range store.Schemas().All() { - handlers[s.Resource().GroupVersionKind()] = make([]Handler, 0) - } - - return &configStoreMonitor{ - store: store, - handlers: handlers, - eventCh: make(chan ConfigEvent, bufferSize), - sync: sync, - } -} - -func (m *configStoreMonitor) ScheduleProcessEvent(configEvent ConfigEvent) { - if m.sync { - m.processConfigEvent(configEvent) - } else { - m.eventCh <- configEvent - } -} - -func (m *configStoreMonitor) Run(stop <-chan struct{}) { - if m.sync { - <-stop - return - } - for { - select { - case <-stop: - return - case ce, ok := <-m.eventCh: - if ok { - m.processConfigEvent(ce) - } - } - } -} - -func (m *configStoreMonitor) processConfigEvent(ce ConfigEvent) { - if _, exists := m.handlers[ce.config.GroupVersionKind]; !exists { - log.Warnf("Config GroupVersionKind %s does not exist in config store", ce.config.GroupVersionKind) - return - } - m.applyHandlers(ce.old, ce.config, ce.event) -} - -func (m *configStoreMonitor) AppendEventHandler(typ config2.GroupVersionKind, h Handler) { - m.handlers[typ] = append(m.handlers[typ], h) -} - -func (m *configStoreMonitor) applyHandlers(old config2.Config, config config2.Config, e model.Event) { - for _, f := range m.handlers[config.GroupVersionKind] { - f(old, config, e) - } -} diff --git a/pilot/pkg/config/memory/monitor_test.go b/pilot/pkg/config/memory/monitor_test.go deleted file mode 100644 index e8e313e98..000000000 --- a/pilot/pkg/config/memory/monitor_test.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package memory_test - -import ( - "sync" - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/test/mock" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -func TestMonitorLifecycle(t *testing.T) { - // Regression test to ensure no race conditions during monitor shutdown - store := memory.Make(collections.Mocks) - m := memory.NewMonitor(store) - stop := make(chan struct{}) - go m.Run(stop) - m.ScheduleProcessEvent(memory.ConfigEvent{}) - close(stop) - m.ScheduleProcessEvent(memory.ConfigEvent{}) -} - -func TestEventConsistency(t *testing.T) { - store := memory.Make(collections.Mocks) - controller := memory.NewController(store) - - testConfig := mock.Make(TestNamespace, 0) - var testEvent model.Event - - done := make(chan bool) - - lock := sync.Mutex{} - - controller.RegisterEventHandler(collections.Mock.Resource().GroupVersionKind(), func(_, config config.Config, event model.Event) { - lock.Lock() - tc := testConfig - lock.Unlock() - - if event != testEvent { - t.Errorf("desired %v, but %v", testEvent, event) - } - if !mock.Compare(tc, config) { - t.Errorf("desired %v, but %v", tc, config) - } - done <- true - }) - - stop := make(chan struct{}) - go controller.Run(stop) - - // Test Add Event - testEvent = model.EventAdd - var rev string - var err error - if rev, err = controller.Create(testConfig); err != nil { - t.Error(err) - return - } - - lock.Lock() - testConfig.ResourceVersion = rev - lock.Unlock() - - <-done - - // Test Update Event - testEvent = model.EventUpdate - if _, err := controller.Update(testConfig); err != nil { - t.Error(err) - return - } - <-done - - // Test Delete Event - testEvent = model.EventDelete - if err := controller.Delete(collections.Mock.Resource().GroupVersionKind(), testConfig.Name, TestNamespace, nil); err != nil { - t.Error(err) - return - } - <-done - close(stop) -} diff --git a/pilot/pkg/config/memory/store.go b/pilot/pkg/config/memory/store.go deleted file mode 100644 index ace1e06c7..000000000 --- a/pilot/pkg/config/memory/store.go +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package memory provides an in-memory volatile config store implementation -package memory - -import ( - "errors" - "fmt" - "sync" - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" -) - -var ( - errNotFound = errors.New("item not found") - errAlreadyExists = errors.New("item already exists") - // TODO: can we make this compatible with kerror.IsConflict without imports the library? - errConflict = errors.New("conflicting resource version, try again") -) - -const ResourceVersion string = "ResourceVersion" - -// Make creates an in-memory config store from a config schemas -// It is with validation -func Make(schemas collection.Schemas) model.ConfigStore { - return newStore(schemas, false) -} - -// MakeSkipValidation creates an in-memory config store from a config schemas -// It is without validation -func MakeSkipValidation(schemas collection.Schemas) model.ConfigStore { - return newStore(schemas, true) -} - -func newStore(schemas collection.Schemas, skipValidation bool) model.ConfigStore { - out := store{ - schemas: schemas, - data: make(map[config.GroupVersionKind]map[string]*sync.Map), - skipValidation: skipValidation, - } - for _, s := range schemas.All() { - out.data[s.Resource().GroupVersionKind()] = make(map[string]*sync.Map) - } - return &out -} - -type store struct { - schemas collection.Schemas - data map[config.GroupVersionKind]map[string]*sync.Map - skipValidation bool - mutex sync.RWMutex -} - -func (cr *store) Schemas() collection.Schemas { - return cr.schemas -} - -func (cr *store) Get(kind config.GroupVersionKind, name, namespace string) *config.Config { - cr.mutex.RLock() - defer cr.mutex.RUnlock() - _, ok := cr.data[kind] - if !ok { - return nil - } - - ns, exists := cr.data[kind][namespace] - if !exists { - return nil - } - - out, exists := ns.Load(name) - if !exists { - return nil - } - config := out.(config.Config) - - return &config -} - -func (cr *store) List(kind config.GroupVersionKind, namespace string) ([]config.Config, error) { - cr.mutex.RLock() - defer cr.mutex.RUnlock() - data, exists := cr.data[kind] - if !exists { - return nil, nil - } - out := make([]config.Config, 0, len(cr.data[kind])) - if namespace == "" { - for _, ns := range data { - ns.Range(func(key, value interface{}) bool { - out = append(out, value.(config.Config)) - return true - }) - } - } else { - ns, exists := data[namespace] - if !exists { - return nil, nil - } - ns.Range(func(key, value interface{}) bool { - out = append(out, value.(config.Config)) - return true - }) - } - return out, nil -} - -func (cr *store) Delete(kind config.GroupVersionKind, name, namespace string, resourceVersion *string) error { - cr.mutex.Lock() - defer cr.mutex.Unlock() - data, ok := cr.data[kind] - if !ok { - return fmt.Errorf("unknown type %v", kind) - } - ns, exists := data[namespace] - if !exists { - return errNotFound - } - - _, exists = ns.Load(name) - if !exists { - return errNotFound - } - - ns.Delete(name) - return nil -} - -func (cr *store) Create(cfg config.Config) (string, error) { - cr.mutex.Lock() - defer cr.mutex.Unlock() - kind := cfg.GroupVersionKind - s, ok := cr.schemas.FindByGroupVersionKind(kind) - if !ok { - return "", fmt.Errorf("unknown type %v", kind) - } - if !cr.skipValidation { - if _, err := s.Resource().ValidateConfig(cfg); err != nil { - return "", err - } - } - ns, exists := cr.data[kind][cfg.Namespace] - if !exists { - ns = new(sync.Map) - cr.data[kind][cfg.Namespace] = ns - } - - _, exists = ns.Load(cfg.Name) - - if !exists { - tnow := time.Now() - if cfg.ResourceVersion == "" { - cfg.ResourceVersion = tnow.String() - } - // Set the creation timestamp, if not provided. - if cfg.CreationTimestamp.IsZero() { - cfg.CreationTimestamp = tnow - } - - ns.Store(cfg.Name, cfg) - return cfg.ResourceVersion, nil - } - return "", errAlreadyExists -} - -func (cr *store) Update(cfg config.Config) (string, error) { - cr.mutex.Lock() - defer cr.mutex.Unlock() - kind := cfg.GroupVersionKind - s, ok := cr.schemas.FindByGroupVersionKind(kind) - if !ok { - return "", fmt.Errorf("unknown type %v", kind) - } - if !cr.skipValidation { - if _, err := s.Resource().ValidateConfig(cfg); err != nil { - return "", err - } - } - - ns, exists := cr.data[kind][cfg.Namespace] - if !exists { - return "", errNotFound - } - - existing, exists := ns.Load(cfg.Name) - if !exists { - return "", errNotFound - } - if hasConflict(existing.(config.Config), cfg) { - return "", errConflict - } - if cfg.Annotations != nil && cfg.Annotations[ResourceVersion] != "" { - cfg.ResourceVersion = cfg.Annotations[ResourceVersion] - delete(cfg.Annotations, ResourceVersion) - } else { - cfg.ResourceVersion = time.Now().String() - } - - ns.Store(cfg.Name, cfg) - return cfg.ResourceVersion, nil -} - -func (cr *store) UpdateStatus(cfg config.Config) (string, error) { - return cr.Update(cfg) -} - -func (cr *store) Patch(orig config.Config, patchFn config.PatchFunc) (string, error) { - cr.mutex.Lock() - defer cr.mutex.Unlock() - - gvk := orig.GroupVersionKind - s, ok := cr.schemas.FindByGroupVersionKind(gvk) - if !ok { - return "", fmt.Errorf("unknown type %v", gvk) - } - - cfg, _ := patchFn(orig) - if !cr.skipValidation { - if _, err := s.Resource().ValidateConfig(cfg); err != nil { - return "", err - } - } - - _, ok = cr.data[gvk] - if !ok { - return "", errNotFound - } - ns, exists := cr.data[gvk][orig.Namespace] - if !exists { - return "", errNotFound - } - - rev := time.Now().String() - cfg.ResourceVersion = rev - ns.Store(cfg.Name, cfg) - - return rev, nil -} - -// hasConflict checks if the two resources have a conflict, which will block Update calls -func hasConflict(existing, replacement config.Config) bool { - if replacement.ResourceVersion == "" { - // We don't care about resource version, so just always overwrite - return false - } - // We set a resource version but its not matched, it is a conflict - if replacement.ResourceVersion != existing.ResourceVersion { - return true - } - return false -} diff --git a/pilot/pkg/config/memory/store_test.go b/pilot/pkg/config/memory/store_test.go deleted file mode 100644 index 530483d55..000000000 --- a/pilot/pkg/config/memory/store_test.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package memory_test - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/memory" - "github.com/apache/dubbo-go-pixiu/pilot/test/mock" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -func TestStoreInvariant(t *testing.T) { - store := memory.Make(collections.Mocks) - mock.CheckMapInvariant(store, t, "some-namespace", 10) -} - -func TestIstioConfig(t *testing.T) { - store := memory.Make(collections.Pilot) - mock.CheckIstioConfigTypes(store, "some-namespace", t) -} diff --git a/pilot/pkg/config/monitor/README.md b/pilot/pkg/config/monitor/README.md deleted file mode 100644 index 76875d387..000000000 --- a/pilot/pkg/config/monitor/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# Monitor - -This package provides the ability to populate a `crd.Controller` dynamically via a function that provides -additional config. The monitor will acquire snapshots of additional changes and populate the `crd.Controller` -as needed. - -## Creating a Monitor - -To create a monitor, you should provide the `crd.Controller`, a polling interval, and -a function that returns `[]*model.Config`. - -```golang -monitor := file.NewMonitor( - controller, // The crd controller holding the store and event handlers - 1*time.Second, // How quickly the monitor requests new snapshots - getSnapshotFunc) // The function used to acquire new config -``` - -## Running a Monitor - -Once created, you simply run the monitor, providing a stop channel. - -```golang -stop := make(chan struct{}) -... -monitor.Start(stop) -``` - -The `Start` method will kick off an asynchronous polling loop and will return immediately. - -## Example - -To configure and run a monitor that watches for file changes to update an in-memory config store: - -```golang -// Configure the config store -store := memory.Make(configDescriptor) -controller = memory.NewController(store) -// Create an object that will take snapshots of config -fileSnapshot := configmonitor.NewFileSnapshot(args.Config.FileDir, configDescriptor) -// Provide snapshot func to monitor -fileMonitor := configmonitor.NewMonitor(controller, 100*time.Millisecond, fileSnapshot.ReadFile) - -// Run the controller and monitor -stop := make(chan struct{}) -go controller.run(stop) -monitor.Start(stop) -``` - -See `monitor_test.go` and `file_snapshot_test.go` for more examples. - -## Notes - -### Always use a Controller - -While the API supports any `model.ConfigStore`, it is recommended to always use a `crd.Controller` so that other -system components can be notified of changes via `controller.RegisterEventHandler()`. - -### Start behavior - -The `Start` method will immediately check the provided `getSnapshotFunc` and update the controller appropriately -before returning. This helps to simplify tests that rely on starting in a particular state. - -After performing an initial update, the `Start` method then forks an asynchronous polling loop for update/termination. diff --git a/pilot/pkg/config/monitor/file_snapshot.go b/pilot/pkg/config/monitor/file_snapshot.go deleted file mode 100644 index c4b4f20e0..000000000 --- a/pilot/pkg/config/monitor/file_snapshot.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package monitor - -import ( - "os" - "path/filepath" - "sort" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/crd" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -var supportedExtensions = map[string]bool{ - ".yaml": true, - ".yml": true, -} - -// FileSnapshot holds a reference to a file directory that contains crd -// config and filter criteria for which of those configs will be parsed. -type FileSnapshot struct { - root string - domainSuffix string - configTypeFilter map[config.GroupVersionKind]bool -} - -// NewFileSnapshot returns a snapshotter. -// If no types are provided in the descriptor, all Istio types will be allowed. -func NewFileSnapshot(root string, schemas collection.Schemas, domainSuffix string) *FileSnapshot { - snapshot := &FileSnapshot{ - root: root, - domainSuffix: domainSuffix, - configTypeFilter: make(map[config.GroupVersionKind]bool), - } - - ss := schemas.All() - if len(ss) == 0 { - ss = collections.Pilot.All() - } - - for _, k := range ss { - if _, ok := collections.Pilot.FindByGroupVersionKind(k.Resource().GroupVersionKind()); ok { - snapshot.configTypeFilter[k.Resource().GroupVersionKind()] = true - } - } - - return snapshot -} - -// ReadConfigFiles parses files in the root directory and returns a sorted slice of -// eligible model.Config. This can be used as a configFunc when creating a Monitor. -func (f *FileSnapshot) ReadConfigFiles() ([]*config.Config, error) { - var result []*config.Config - - err := filepath.Walk(f.root, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } else if !supportedExtensions[filepath.Ext(path)] || (info.Mode()&os.ModeType) != 0 { - return nil - } - data, err := os.ReadFile(path) - if err != nil { - log.Warnf("Failed to read %s: %v", path, err) - return err - } - configs, err := parseInputs(data, f.domainSuffix) - if err != nil { - log.Warnf("Failed to parse %s: %v", path, err) - return err - } - - // Filter any unsupported types before appending to the result. - for _, cfg := range configs { - if !f.configTypeFilter[cfg.GroupVersionKind] { - continue - } - result = append(result, cfg) - } - return nil - }) - if err != nil { - log.Warnf("failure during filepath.Walk: %v", err) - } - - // Sort by the config IDs. - sort.Sort(byKey(result)) - return result, err -} - -// parseInputs is identical to crd.ParseInputs, except that it returns an array of config pointers. -func parseInputs(data []byte, domainSuffix string) ([]*config.Config, error) { - configs, _, err := crd.ParseInputs(string(data)) - - // Convert to an array of pointers. - refs := make([]*config.Config, len(configs)) - for i := range configs { - refs[i] = &configs[i] - refs[i].Domain = domainSuffix - } - return refs, err -} - -// byKey is an array of config objects that is capable or sorting by Namespace, GroupVersionKind, and Name. -type byKey []*config.Config - -func (rs byKey) Len() int { - return len(rs) -} - -func (rs byKey) Swap(i, j int) { - rs[i], rs[j] = rs[j], rs[i] -} - -func (rs byKey) Less(i, j int) bool { - return compareIds(rs[i], rs[j]) < 0 -} diff --git a/pilot/pkg/config/monitor/file_snapshot_test.go b/pilot/pkg/config/monitor/file_snapshot_test.go deleted file mode 100644 index f58e144c1..000000000 --- a/pilot/pkg/config/monitor/file_snapshot_test.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package monitor - -import ( - "os" - "path/filepath" - "testing" -) - -import ( - "github.com/onsi/gomega" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -var gatewayYAML = ` -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: some-ingress -spec: - servers: - - port: - number: 80 - name: http - protocol: http - hosts: - - "*.example.com" -` - -var statusRegressionYAML = ` -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: test - namespace: test-1 -spec: - selector: - app: istio-ingressgateway - servers: - - hosts: - - example.com - port: - name: http - number: 80 - protocol: HTTP -status: {}` - -var virtualServiceYAML = ` -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: route-for-myapp -spec: - hosts: - - some.example.com - gateways: - - some-ingress - http: - - route: - - destination: - host: some.example.internal -` - -func TestFileSnapshotNoFilter(t *testing.T) { - g := gomega.NewWithT(t) - - ts := &testState{ - ConfigFiles: map[string][]byte{"gateway.yml": []byte(gatewayYAML)}, - } - - ts.testSetup(t) - - fileWatcher := NewFileSnapshot(ts.rootPath, collection.SchemasFor(), "foo") - configs, err := fileWatcher.ReadConfigFiles() - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(configs).To(gomega.HaveLen(1)) - g.Expect(configs[0].Domain).To(gomega.Equal("foo")) - - gateway := configs[0].Spec.(*networking.Gateway) - g.Expect(gateway.Servers[0].Port.Number).To(gomega.Equal(uint32(80))) - g.Expect(gateway.Servers[0].Port.Protocol).To(gomega.Equal("http")) - g.Expect(gateway.Servers[0].Hosts).To(gomega.Equal([]string{"*.example.com"})) -} - -func TestFileSnapshotWithFilter(t *testing.T) { - g := gomega.NewWithT(t) - - ts := &testState{ - ConfigFiles: map[string][]byte{ - "gateway.yml": []byte(gatewayYAML), - "virtual_service.yml": []byte(virtualServiceYAML), - }, - } - - ts.testSetup(t) - - fileWatcher := NewFileSnapshot(ts.rootPath, collection.SchemasFor(collections.IstioNetworkingV1Alpha3Virtualservices), "") - configs, err := fileWatcher.ReadConfigFiles() - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(configs).To(gomega.HaveLen(1)) - - virtualService := configs[0].Spec.(*networking.VirtualService) - g.Expect(virtualService.Hosts).To(gomega.Equal([]string{"some.example.com"})) -} - -func TestFileSnapshotSorting(t *testing.T) { - g := gomega.NewWithT(t) - - ts := &testState{ - ConfigFiles: map[string][]byte{ - "z.yml": []byte(gatewayYAML), - "a.yml": []byte(virtualServiceYAML), - }, - } - - ts.testSetup(t) - - fileWatcher := NewFileSnapshot(ts.rootPath, collection.SchemasFor(), "") - - configs, err := fileWatcher.ReadConfigFiles() - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(configs).To(gomega.HaveLen(2)) - - g.Expect(configs[0].Spec).To(gomega.BeAssignableToTypeOf(&networking.Gateway{})) - g.Expect(configs[1].Spec).To(gomega.BeAssignableToTypeOf(&networking.VirtualService{})) -} - -type testState struct { - ConfigFiles map[string][]byte - rootPath string -} - -func (ts *testState) testSetup(t *testing.T) { - var err error - - ts.rootPath = t.TempDir() - - for name, content := range ts.ConfigFiles { - err = os.WriteFile(filepath.Join(ts.rootPath, name), content, 0o600) - if err != nil { - t.Fatal(err) - } - } -} diff --git a/pilot/pkg/config/monitor/leak_test.go b/pilot/pkg/config/monitor/leak_test.go deleted file mode 100644 index 0794670a6..000000000 --- a/pilot/pkg/config/monitor/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package monitor - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/config/monitor/monitor.go b/pilot/pkg/config/monitor/monitor.go deleted file mode 100644 index 12fe6db46..000000000 --- a/pilot/pkg/config/monitor/monitor.go +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package monitor - -import ( - "os" - "path/filepath" - "reflect" - "strings" - "time" -) - -import ( - "github.com/fsnotify/fsnotify" - istiolog "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" -) - -// Monitor will poll a config function in order to update a ConfigStore as -// changes are found. -type Monitor struct { - name string - root string - store model.ConfigStore - configs []*config.Config - getSnapshotFunc func() ([]*config.Config, error) - // channel to trigger updates on - // generally set to a file watch, but used in tests as well - updateCh chan struct{} -} - -var log = istiolog.RegisterScope("monitor", "file configuration monitor", 0) - -// NewMonitor creates a Monitor and will delegate to a passed in controller. -// The controller holds a reference to the actual store. -// Any func that returns a []*model.Config can be used with the Monitor -func NewMonitor(name string, delegateStore model.ConfigStore, getSnapshotFunc func() ([]*config.Config, error), root string) *Monitor { - monitor := &Monitor{ - name: name, - root: root, - store: delegateStore, - getSnapshotFunc: getSnapshotFunc, - } - return monitor -} - -const watchDebounceDelay = 50 * time.Millisecond - -// Trigger notifications when a file is mutated -func fileTrigger(path string, ch chan struct{}, stop <-chan struct{}) error { - if path == "" { - return nil - } - fs, err := fsnotify.NewWatcher() - if err != nil { - return err - } - watcher := recursiveWatcher{fs} - if err = watcher.watchRecursive(path); err != nil { - return err - } - go func() { - defer watcher.Close() - var debounceC <-chan time.Time - for { - select { - case <-debounceC: - debounceC = nil - ch <- struct{}{} - case e := <-watcher.Events: - s, err := os.Stat(e.Name) - if err == nil && s != nil && s.IsDir() { - // If it's a directory, add a watch for it so we see nested files. - if e.Op&fsnotify.Create != 0 { - log.Debugf("add watch for %v: %v", s.Name(), watcher.watchRecursive(e.Name)) - } - } - // Can't stat a deleted directory, so attempt to remove it. If it fails it is not a problem - if e.Op&fsnotify.Remove != 0 { - _ = watcher.Remove(e.Name) - } - if debounceC == nil { - debounceC = time.After(watchDebounceDelay) - } - case err := <-watcher.Errors: - log.Warnf("Error watching file trigger: %v %v", path, err) - return - case signal := <-stop: - log.Infof("Shutting down file watcher: %v %v", path, signal) - return - } - } - }() - return nil -} - -// recursiveWatcher wraps a fsnotify wrapper to add a best-effort recursive directory watching in user -// space. See https://github.com/fsnotify/fsnotify/issues/18. The implementation is inherently racy, -// as files added to a directory immediately after creation may not trigger events; as such it is only useful -// when an event causes a full reconciliation, rather than acting on an individual event -type recursiveWatcher struct { - *fsnotify.Watcher -} - -// watchRecursive adds all directories under the given one to the watch list. -func (m recursiveWatcher) watchRecursive(path string) error { - err := filepath.Walk(path, func(walkPath string, fi os.FileInfo, err error) error { - if err != nil { - return err - } - if fi.IsDir() { - if err = m.Watcher.Add(walkPath); err != nil { - return err - } - } - return nil - }) - return err -} - -// Start starts a new Monitor. Immediately checks the Monitor getSnapshotFunc -// and updates the controller. It then kicks off an asynchronous event loop that -// periodically polls the getSnapshotFunc for changes until a close event is sent. -func (m *Monitor) Start(stop <-chan struct{}) { - m.checkAndUpdate() - - c := make(chan struct{}, 1) - m.updateCh = c - if err := fileTrigger(m.root, m.updateCh, stop); err != nil { - log.Errorf("Unable to setup FileTrigger for %s: %v", m.root, err) - } - // Run the close loop asynchronously. - go func() { - for { - select { - case <-c: - log.Infof("Triggering reload of file configuration") - m.checkAndUpdate() - case <-stop: - return - } - } - }() -} - -func (m *Monitor) checkAndUpdate() { - newConfigs, err := m.getSnapshotFunc() - // If an error exists then log it and return to running the check and update - // Do not edit the local []*model.config until the connection has been reestablished - // The error will only come from a directory read error or a gRPC connection error - if err != nil { - log.Warnf("checkAndUpdate Error Caught %s: %v\n", m.name, err) - return - } - - // make a deep copy of newConfigs to prevent data race - copyConfigs := make([]*config.Config, 0) - for _, config := range newConfigs { - cpy := config.DeepCopy() - copyConfigs = append(copyConfigs, &cpy) - } - - // Compare the new list to the previous one and detect changes. - oldLen := len(m.configs) - newLen := len(newConfigs) - oldIndex, newIndex := 0, 0 - for oldIndex < oldLen && newIndex < newLen { - oldConfig := m.configs[oldIndex] - newConfig := newConfigs[newIndex] - if v := compareIds(oldConfig, newConfig); v < 0 { - m.deleteConfig(oldConfig) - oldIndex++ - } else if v > 0 { - m.createConfig(newConfig) - newIndex++ - } else { - // version may change without content changing - oldConfig.Meta.ResourceVersion = newConfig.Meta.ResourceVersion - if !reflect.DeepEqual(oldConfig, newConfig) { - m.updateConfig(newConfig) - } - oldIndex++ - newIndex++ - } - } - - // Detect remaining deletions - for ; oldIndex < oldLen; oldIndex++ { - m.deleteConfig(m.configs[oldIndex]) - } - - // Detect remaining additions - for ; newIndex < newLen; newIndex++ { - m.createConfig(newConfigs[newIndex]) - } - - // Save the updated list. - m.configs = copyConfigs -} - -func (m *Monitor) createConfig(c *config.Config) { - if _, err := m.store.Create(*c); err != nil { - log.Warnf("Failed to create config %s %s/%s: %v (%+v)", c.GroupVersionKind, c.Namespace, c.Name, err, *c) - } -} - -func (m *Monitor) updateConfig(c *config.Config) { - // Set the resource version and create timestamp based on the existing config. - if prev := m.store.Get(c.GroupVersionKind, c.Name, c.Namespace); prev != nil { - c.ResourceVersion = prev.ResourceVersion - c.CreationTimestamp = prev.CreationTimestamp - } - - if _, err := m.store.Update(*c); err != nil { - log.Warnf("Failed to update config (%+v): %v ", *c, err) - } -} - -func (m *Monitor) deleteConfig(c *config.Config) { - if err := m.store.Delete(c.GroupVersionKind, c.Name, c.Namespace, nil); err != nil { - log.Warnf("Failed to delete config (%+v): %v ", *c, err) - } -} - -// compareIds compares the IDs (i.e. Namespace, GroupVersionKind, and Name) of the two configs and returns -// 0 if a == b, -1 if a < b, and 1 if a > b. Used for sorting config arrays. -func compareIds(a, b *config.Config) int { - return strings.Compare(a.Key(), b.Key()) -} diff --git a/pilot/pkg/config/monitor/monitor_test.go b/pilot/pkg/config/monitor/monitor_test.go deleted file mode 100644 index 00af7953d..000000000 --- a/pilot/pkg/config/monitor/monitor_test.go +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package monitor - -import ( - "errors" - "testing" - "time" -) - -import ( - "github.com/onsi/gomega" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/memory" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -var createConfigSet = []*config.Config{ - { - Meta: config.Meta{ - Name: "magic", - GroupVersionKind: gvk.Gateway, - }, - Spec: &networking.Gateway{ - Servers: []*networking.Server{ - { - Port: &networking.Port{ - Number: 80, - Protocol: "HTTP", - Name: "http", - }, - Hosts: []string{"*.example.com"}, - }, - }, - }, - }, -} - -var updateConfigSet = []*config.Config{ - { - Meta: config.Meta{ - Name: "magic", - GroupVersionKind: gvk.Gateway, - }, - Spec: &networking.Gateway{ - Servers: []*networking.Server{ - { - Port: &networking.Port{ - Number: 80, - Protocol: "HTTP2", - Name: "http", - }, - Hosts: []string{"*.example.com"}, - }, - }, - }, - }, -} - -func TestMonitorForChange(t *testing.T) { - g := gomega.NewWithT(t) - - store := memory.Make(collection.SchemasFor(collections.IstioNetworkingV1Alpha3Gateways)) - - var ( - callCount int - configs []*config.Config - err error - ) - - someConfigFunc := func() ([]*config.Config, error) { - switch callCount { - case 0: - configs = createConfigSet - err = nil - case 3: - configs = updateConfigSet - case 6: - configs = []*config.Config{} - } - - callCount++ - return configs, err - } - mon := NewMonitor("", store, someConfigFunc, "") - stop := make(chan struct{}) - defer func() { close(stop) }() - mon.Start(stop) - - go func() { - for i := 0; i < 10; i++ { - select { - case <-stop: - return - case mon.updateCh <- struct{}{}: - } - time.Sleep(time.Millisecond * 100) - } - }() - g.Eventually(func() error { - c, err := store.List(gvk.Gateway, "") - g.Expect(err).NotTo(gomega.HaveOccurred()) - - if len(c) != 1 { - return errors.New("no configs") - } - - if c[0].Meta.Name != "magic" { - return errors.New("wrong config") - } - - return nil - }).Should(gomega.Succeed()) - - g.Eventually(func() error { - c, err := store.List(gvk.Gateway, "") - g.Expect(err).NotTo(gomega.HaveOccurred()) - if len(c) == 0 { - return errors.New("no config") - } - - gateway := c[0].Spec.(*networking.Gateway) - if gateway.Servers[0].Port.Protocol != "HTTP2" { - return errors.New("protocol has not been updated") - } - - return nil - }).Should(gomega.Succeed()) - - g.Eventually(func() ([]config.Config, error) { - return store.List(gvk.Gateway, "") - }).Should(gomega.HaveLen(0)) -} - -func TestMonitorFileSnapshot(t *testing.T) { - ts := &testState{ - ConfigFiles: map[string][]byte{"gateway.yml": []byte(statusRegressionYAML)}, - } - - ts.testSetup(t) - - store := memory.Make(collection.SchemasFor(collections.IstioNetworkingV1Alpha3Gateways)) - fileWatcher := NewFileSnapshot(ts.rootPath, collection.SchemasFor(), "foo") - - mon := NewMonitor("", store, fileWatcher.ReadConfigFiles, "") - stop := make(chan struct{}) - defer func() { close(stop) }() - mon.Start(stop) - retry.UntilOrFail(t, func() bool { return store.Get(gvk.Gateway, "test", "test-1") != nil }) -} - -func TestMonitorForError(t *testing.T) { - g := gomega.NewWithT(t) - - store := memory.Make(collection.SchemasFor(collections.IstioNetworkingV1Alpha3Gateways)) - - var ( - callCount int - configs []*config.Config - err error - ) - - delay := make(chan struct{}, 1) - - someConfigFunc := func() ([]*config.Config, error) { - switch callCount { - case 0: - configs = createConfigSet - err = nil - case 3: - configs = nil - err = errors.New("snapshotFunc can't connect") - delay <- struct{}{} - } - - callCount++ - return configs, err - } - mon := NewMonitor("", store, someConfigFunc, "") - stop := make(chan struct{}) - defer func() { close(stop) }() - mon.Start(stop) - - go func() { - updateTicker := time.NewTicker(100 * time.Millisecond) - numUpdates := 10 - for { - select { - case <-stop: - updateTicker.Stop() - return - case <-updateTicker.C: - mon.updateCh <- struct{}{} - numUpdates-- - if numUpdates == 0 { - updateTicker.Stop() - return - } - } - } - }() - // Test ensures that after a coplilot connection error the data remains - // nil data return and error return keeps the existing data aka createConfigSet - <-delay - g.Eventually(func() error { - c, err := store.List(gvk.Gateway, "") - g.Expect(err).NotTo(gomega.HaveOccurred()) - - if len(c) != 1 { - return errors.New("config files erased on Copilot error") - } - - return nil - }).Should(gomega.Succeed()) -} diff --git a/pilot/pkg/controller/workloadentry/leak_test.go b/pilot/pkg/controller/workloadentry/leak_test.go deleted file mode 100644 index 6f29a2a5d..000000000 --- a/pilot/pkg/controller/workloadentry/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package workloadentry - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/controller/workloadentry/workloadentry_controller.go b/pilot/pkg/controller/workloadentry/workloadentry_controller.go deleted file mode 100644 index 3d429d494..000000000 --- a/pilot/pkg/controller/workloadentry/workloadentry_controller.go +++ /dev/null @@ -1,622 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package workloadentry - -import ( - "context" - "fmt" - "math" - "strings" - "sync" - "time" -) - -import ( - "golang.org/x/time/rate" - "google.golang.org/grpc/codes" - grpcstatus "google.golang.org/grpc/status" - "google.golang.org/protobuf/types/known/timestamppb" - "istio.io/api/meta/v1alpha1" - "istio.io/api/networking/v1alpha3" - istiolog "istio.io/pkg/log" - "istio.io/pkg/monitoring" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - kubetypes "k8s.io/apimachinery/pkg/types" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model/status" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/kube/controllers" - "github.com/apache/dubbo-go-pixiu/pkg/queue" -) - -func init() { - monitoring.MustRegister(autoRegistrationSuccess) - monitoring.MustRegister(autoRegistrationUpdates) - monitoring.MustRegister(autoRegistrationUnregistrations) - monitoring.MustRegister(autoRegistrationDeletes) - monitoring.MustRegister(autoRegistrationErrors) -} - -var ( - autoRegistrationSuccess = monitoring.NewSum( - "auto_registration_success_total", - "Total number of successful auto registrations.", - ) - - autoRegistrationUpdates = monitoring.NewSum( - "auto_registration_updates_total", - "Total number of auto registration updates.", - ) - - autoRegistrationUnregistrations = monitoring.NewSum( - "auto_registration_unregister_total", - "Total number of unregistrations.", - ) - - autoRegistrationDeletes = monitoring.NewSum( - "auto_registration_deletes_total", - "Total number of auto registration cleaned up by periodic timer.", - ) - - autoRegistrationErrors = monitoring.NewSum( - "auto_registration_errors_total", - "Total number of auto registration errors.", - ) -) - -const ( - // TODO use status or another proper API instead of annotations - - // AutoRegistrationGroupAnnotation on a WorkloadEntry stores the associated WorkloadGroup. - AutoRegistrationGroupAnnotation = "istio.io/autoRegistrationGroup" - // WorkloadControllerAnnotation on a WorkloadEntry should store the current/last pilot instance connected to the workload for XDS. - WorkloadControllerAnnotation = "istio.io/workloadController" - // ConnectedAtAnnotation on a WorkloadEntry stores the time in nanoseconds when the associated workload connected to a Pilot instance. - ConnectedAtAnnotation = "istio.io/connectedAt" - // DisconnectedAtAnnotation on a WorkloadEntry stores the time in nanoseconds when the associated workload disconnected from a Pilot instance. - DisconnectedAtAnnotation = "istio.io/disconnectedAt" - - timeFormat = time.RFC3339Nano - // maxRetries is the number of times a service will be retried before it is dropped out of the queue. - // With the current rate-limiter in use (5ms*2^(maxRetries-1)) the following numbers represent the - // sequence of delays between successive queuings of a service. - // - // 5ms, 10ms, 20ms, 40ms, 80ms, 160ms, 320ms, 640ms, 1.3s, 2.6s, 5.1s, 10.2s, 20.4s, 41s, 82s - maxRetries = 15 -) - -type HealthEvent struct { - // whether or not the agent thought the target is healthy - Healthy bool `json:"healthy,omitempty"` - // error message propagated - Message string `json:"errMessage,omitempty"` -} - -type HealthCondition struct { - proxy *model.Proxy - entryName string - condition *v1alpha1.IstioCondition -} - -var log = istiolog.RegisterScope("wle", "wle controller debugging", 0) - -type Controller struct { - instanceID string - // TODO move WorkloadEntry related tasks into their own object and give InternalGen a reference. - // store should either be k8s (for running pilot) or in-memory (for tests). MCP and other config store implementations - // do not support writing. We only use it here for reading WorkloadEntry/WorkloadGroup. - store model.ConfigStoreController - - // Note: unregister is to update the workload entry status: like setting `DisconnectedAtAnnotation` - // and make the workload entry enqueue `cleanupQueue` - // cleanup is to delete the workload entry - - // queue contains workloadEntry that need to be unregistered - queue controllers.Queue - // cleanupLimit rate limit's auto registered WorkloadEntry cleanup calls to k8s - cleanupLimit *rate.Limiter - // cleanupQueue delays the cleanup of auto registered WorkloadEntries to allow for grace period - cleanupQueue queue.Delayed - - mutex sync.Mutex - // record the current adsConnections number - // note: this is to handle reconnect to the same istiod, but in rare case the disconnect event is later than the connect event - // keyed by proxy network+ip - adsConnections map[string]uint8 - - // maxConnectionAge is a duration that workload entry should be cleaned up if it does not reconnects. - maxConnectionAge time.Duration - - // healthCondition is a fifo queue used for updating health check status - healthCondition controllers.Queue -} - -type HealthStatus = v1alpha1.IstioCondition - -// NewController create a controller which manages workload lifecycle and health status. -func NewController(store model.ConfigStoreController, instanceID string, maxConnAge time.Duration) *Controller { - if features.WorkloadEntryAutoRegistration || features.WorkloadEntryHealthChecks { - maxConnAge := maxConnAge + maxConnAge/2 - // if overflow, set it to max int64 - if maxConnAge < 0 { - maxConnAge = time.Duration(math.MaxInt64) - } - c := &Controller{ - instanceID: instanceID, - store: store, - cleanupLimit: rate.NewLimiter(rate.Limit(20), 1), - cleanupQueue: queue.NewDelayed(), - adsConnections: map[string]uint8{}, - maxConnectionAge: maxConnAge, - } - c.queue = controllers.NewQueue("unregister_workloadentry", - controllers.WithMaxAttempts(maxRetries), - controllers.WithGenericReconciler(c.unregisterWorkload)) - c.healthCondition = controllers.NewQueue("healthcheck", - controllers.WithMaxAttempts(maxRetries), - controllers.WithGenericReconciler(c.updateWorkloadEntryHealth)) - return c - } - return nil -} - -func (c *Controller) Run(stop <-chan struct{}) { - if c == nil { - return - } - if c.store != nil && c.cleanupQueue != nil { - go c.periodicWorkloadEntryCleanup(stop) - go c.cleanupQueue.Run(stop) - } - - go c.queue.Run(stop) - go c.healthCondition.Run(stop) - <-stop -} - -// workItem contains the state of a "disconnect" event used to unregister a workload. -type workItem struct { - entryName string - proxy *model.Proxy - disConTime time.Time - origConTime time.Time -} - -func setConnectMeta(c *config.Config, controller string, conTime time.Time) { - if c.Annotations == nil { - c.Annotations = map[string]string{} - } - c.Annotations[WorkloadControllerAnnotation] = controller - c.Annotations[ConnectedAtAnnotation] = conTime.Format(timeFormat) - delete(c.Annotations, DisconnectedAtAnnotation) -} - -func (c *Controller) RegisterWorkload(proxy *model.Proxy, conTime time.Time) error { - if !features.WorkloadEntryAutoRegistration || c == nil { - return nil - } - // check if the WE already exists, update the status - entryName := autoregisteredWorkloadEntryName(proxy) - if entryName == "" { - return nil - } - proxy.AutoregisteredWorkloadEntryName = entryName - - c.mutex.Lock() - c.adsConnections[makeProxyKey(proxy)]++ - c.mutex.Unlock() - - if err := c.registerWorkload(entryName, proxy, conTime); err != nil { - log.Errorf(err) - return err - } - return nil -} - -func (c *Controller) registerWorkload(entryName string, proxy *model.Proxy, conTime time.Time) error { - wle := c.store.Get(gvk.WorkloadEntry, entryName, proxy.Metadata.Namespace) - if wle != nil { - lastConTime, _ := time.Parse(timeFormat, wle.Annotations[ConnectedAtAnnotation]) - // the proxy has reconnected to another pilot, not belong to this one. - if conTime.Before(lastConTime) { - return nil - } - // Try to patch, if it fails then try to create - _, err := c.store.Patch(*wle, func(cfg config.Config) (config.Config, kubetypes.PatchType) { - setConnectMeta(&cfg, c.instanceID, conTime) - return cfg, kubetypes.MergePatchType - }) - if err != nil { - return fmt.Errorf("failed updating WorkloadEntry %s/%s err: %v", proxy.Metadata.Namespace, entryName, err) - } - autoRegistrationUpdates.Increment() - log.Infof("updated auto-registered WorkloadEntry %s/%s", proxy.Metadata.Namespace, entryName) - return nil - } - - // No WorkloadEntry, create one using fields from the associated WorkloadGroup - groupCfg := c.store.Get(gvk.WorkloadGroup, proxy.Metadata.AutoRegisterGroup, proxy.Metadata.Namespace) - if groupCfg == nil { - autoRegistrationErrors.Increment() - return grpcstatus.Errorf(codes.FailedPrecondition, "auto-registration WorkloadEntry of %v failed: cannot find WorkloadGroup %s/%s", - proxy.ID, proxy.Metadata.Namespace, proxy.Metadata.AutoRegisterGroup) - } - entry := workloadEntryFromGroup(entryName, proxy, groupCfg) - setConnectMeta(entry, c.instanceID, conTime) - _, err := c.store.Create(*entry) - if err != nil { - autoRegistrationErrors.Increment() - return fmt.Errorf("auto-registration WorkloadEntry of %v failed: error creating WorkloadEntry: %v", proxy.ID, err) - } - hcMessage := "" - if _, f := entry.Annotations[status.WorkloadEntryHealthCheckAnnotation]; f { - hcMessage = " with health checking enabled" - } - autoRegistrationSuccess.Increment() - log.Infof("auto-registered WorkloadEntry %s/%s%s", proxy.Metadata.Namespace, entryName, hcMessage) - return nil -} - -func (c *Controller) QueueUnregisterWorkload(proxy *model.Proxy, origConnect time.Time) { - if !features.WorkloadEntryAutoRegistration || c == nil { - return - } - // check if the WE already exists, update the status - entryName := proxy.AutoregisteredWorkloadEntryName - if entryName == "" { - return - } - - c.mutex.Lock() - num := c.adsConnections[makeProxyKey(proxy)] - // if there is still ads connection, do not unregister. - if num > 1 { - c.adsConnections[makeProxyKey(proxy)] = num - 1 - c.mutex.Unlock() - return - } - delete(c.adsConnections, makeProxyKey(proxy)) - c.mutex.Unlock() - - workload := &workItem{ - entryName: entryName, - proxy: proxy, - disConTime: time.Now(), - origConTime: origConnect, - } - if err := c.unregisterWorkload(workload); err != nil { - log.Errorf(err) - c.queue.Add(workload) - } -} - -func (c *Controller) unregisterWorkload(item interface{}) error { - workItem, ok := item.(*workItem) - if !ok { - return nil - } - - // unset controller, set disconnect time - cfg := c.store.Get(gvk.WorkloadEntry, workItem.entryName, workItem.proxy.Metadata.Namespace) - if cfg == nil { - // return error and backoff retry to prevent workloadentry leak - // TODO(@hzxuzhonghu): update the Get interface, fallback to calling apiserver. - return fmt.Errorf("workloadentry %s/%s is not found, maybe deleted or because of propagate latency", - workItem.proxy.Metadata.Namespace, workItem.entryName) - } - - // only queue a delete if this disconnect event is associated with the last connect event written to the worload entry - if mostRecentConn, err := time.Parse(timeFormat, cfg.Annotations[ConnectedAtAnnotation]); err == nil { - if mostRecentConn.After(workItem.origConTime) { - // this disconnect event wasn't processed until after we successfully reconnected - return nil - } - } - // The wle has reconnected to another istiod and controlled by it. - if cfg.Annotations[WorkloadControllerAnnotation] != c.instanceID { - return nil - } - - conTime, _ := time.Parse(timeFormat, cfg.Annotations[ConnectedAtAnnotation]) - // The wle has reconnected to this istiod, - // this may happen when the unregister fails retry - if workItem.disConTime.Before(conTime) { - return nil - } - - wle := cfg.DeepCopy() - delete(wle.Annotations, ConnectedAtAnnotation) - wle.Annotations[DisconnectedAtAnnotation] = workItem.disConTime.Format(timeFormat) - // use update instead of patch to prevent race condition - if _, err := c.store.Update(wle); err != nil { - autoRegistrationErrors.Increment() - return fmt.Errorf("disconnect: failed updating WorkloadEntry %s/%s: %v", workItem.proxy.Metadata.Namespace, workItem.entryName, err) - } - - autoRegistrationUnregistrations.Increment() - - // after grace period, check if the workload ever reconnected - ns := workItem.proxy.Metadata.Namespace - c.cleanupQueue.PushDelayed(func() error { - wle := c.store.Get(gvk.WorkloadEntry, workItem.entryName, ns) - if wle == nil { - return nil - } - if c.shouldCleanupEntry(*wle) { - c.cleanupEntry(*wle) - } - return nil - }, features.WorkloadEntryCleanupGracePeriod) - return nil -} - -// QueueWorkloadEntryHealth enqueues the associated WorkloadEntries health status. -func (c *Controller) QueueWorkloadEntryHealth(proxy *model.Proxy, event HealthEvent) { - // we assume that the workload entry exists - // if auto registration does not exist, try looking - // up in NodeMetadata - entryName := proxy.AutoregisteredWorkloadEntryName - if entryName == "" { - log.Errorf("unable to derive WorkloadEntry for health update for %v", proxy.ID) - return - } - - condition := transformHealthEvent(proxy, entryName, event) - c.healthCondition.Add(condition) -} - -// updateWorkloadEntryHealth updates the associated WorkloadEntries health status -// based on the corresponding health check performed by istio-agent. -func (c *Controller) updateWorkloadEntryHealth(obj interface{}) error { - condition := obj.(HealthCondition) - // get previous status - cfg := c.store.Get(gvk.WorkloadEntry, condition.entryName, condition.proxy.Metadata.Namespace) - if cfg == nil { - return fmt.Errorf("failed to update health status for %v: WorkloadEntry %v not found", condition.proxy.ID, condition.entryName) - } - // The workloadentry has reconnected to the other istiod - if cfg.Annotations[WorkloadControllerAnnotation] != c.instanceID { - return nil - } - - // check if the existing health status is newer than this one - wleStatus, ok := cfg.Status.(*v1alpha1.IstioStatus) - if ok { - healthCondition := status.GetCondition(wleStatus.Conditions, status.ConditionHealthy) - if healthCondition != nil { - if healthCondition.LastProbeTime.AsTime().After(condition.condition.LastProbeTime.AsTime()) { - return nil - } - } - } - - // replace the updated status - wle := status.UpdateConfigCondition(*cfg, condition.condition) - // update the status - _, err := c.store.UpdateStatus(wle) - if err != nil { - return fmt.Errorf("error while updating WorkloadEntry health status for %s: %v", condition.proxy.ID, err) - } - log.Debugf("updated health status of %v to %v", condition.proxy.ID, condition.condition) - return nil -} - -// periodicWorkloadEntryCleanup checks lists all WorkloadEntry -func (c *Controller) periodicWorkloadEntryCleanup(stopCh <-chan struct{}) { - if !features.WorkloadEntryAutoRegistration { - return - } - ticker := time.NewTicker(10 * features.WorkloadEntryCleanupGracePeriod) - defer ticker.Stop() - for { - select { - case <-ticker.C: - wles, err := c.store.List(gvk.WorkloadEntry, metav1.NamespaceAll) - if err != nil { - log.Warnf("error listing WorkloadEntry for cleanup: %v", err) - continue - } - for _, wle := range wles { - wle := wle - if c.shouldCleanupEntry(wle) { - c.cleanupQueue.Push(func() error { - c.cleanupEntry(wle) - return nil - }) - } - } - case <-stopCh: - return - } - } -} - -func (c *Controller) shouldCleanupEntry(wle config.Config) bool { - // don't clean-up if connected or non-autoregistered WorkloadEntries - if wle.Annotations[AutoRegistrationGroupAnnotation] == "" { - return false - } - - // If there is ConnectedAtAnnotation set, don't cleanup this workload entry. - // This may happen when the workload fast reconnects to the same istiod. - // 1. disconnect: the workload entry has been updated - // 2. connect: but the patch is based on the old workloadentry because of the propagation latency. - // So in this case the `DisconnectedAtAnnotation` is still there and the cleanup procedure will go on. - connTime := wle.Annotations[ConnectedAtAnnotation] - if connTime != "" { - // handle workload leak when both workload/pilot down at the same time before pilot has a chance to set disconnTime - connAt, err := time.Parse(timeFormat, connTime) - // if it has been 1.5*maxConnectionAge since workload connected, should delete it. - if err == nil && uint64(time.Since(connAt)) > uint64(c.maxConnectionAge)+uint64(c.maxConnectionAge/2) { - return true - } - return false - } - - disconnTime := wle.Annotations[DisconnectedAtAnnotation] - if disconnTime == "" { - return false - } - - disconnAt, err := time.Parse(timeFormat, disconnTime) - // if we haven't passed the grace period, don't cleanup - if err == nil && time.Since(disconnAt) < features.WorkloadEntryCleanupGracePeriod { - return false - } - - return true -} - -func (c *Controller) cleanupEntry(wle config.Config) { - if err := c.cleanupLimit.Wait(context.TODO()); err != nil { - log.Errorf("error in WorkloadEntry cleanup rate limiter: %v", err) - return - } - if err := c.store.Delete(gvk.WorkloadEntry, wle.Name, wle.Namespace, &wle.ResourceVersion); err != nil && !errors.IsNotFound(err) { - log.Warnf("failed cleaning up auto-registered WorkloadEntry %s/%s: %v", wle.Namespace, wle.Name, err) - autoRegistrationErrors.Increment() - return - } - autoRegistrationDeletes.Increment() - log.Infof("cleaned up auto-registered WorkloadEntry %s/%s", wle.Namespace, wle.Name) -} - -func autoregisteredWorkloadEntryName(proxy *model.Proxy) string { - if proxy.Metadata.AutoRegisterGroup == "" { - return "" - } - if len(proxy.IPAddresses) == 0 { - log.Errorf("auto-registration of %v failed: missing IP addresses", proxy.ID) - return "" - } - if len(proxy.Metadata.Namespace) == 0 { - log.Errorf("auto-registration of %v failed: missing namespace", proxy.ID) - return "" - } - p := []string{proxy.Metadata.AutoRegisterGroup, sanitizeIP(proxy.IPAddresses[0])} - if proxy.Metadata.Network != "" { - p = append(p, string(proxy.Metadata.Network)) - } - - name := strings.Join(p, "-") - if len(name) > 253 { - name = name[len(name)-253:] - log.Warnf("generated WorkloadEntry name is too long, consider making the WorkloadGroup name shorter. Shortening from beginning to: %s", name) - } - return name -} - -// sanitizeIP ensures an IP address (IPv6) can be used in Kubernetes resource name -func sanitizeIP(s string) string { - return strings.ReplaceAll(s, ":", "-") -} - -func transformHealthEvent(proxy *model.Proxy, entryName string, event HealthEvent) HealthCondition { - cond := &v1alpha1.IstioCondition{ - Type: status.ConditionHealthy, - // last probe and transition are the same because - // we only send on transition in the agent - LastProbeTime: timestamppb.Now(), - LastTransitionTime: timestamppb.Now(), - } - out := HealthCondition{ - proxy: proxy, - entryName: entryName, - condition: cond, - } - if event.Healthy { - cond.Status = status.StatusTrue - return out - } - cond.Status = status.StatusFalse - cond.Message = event.Message - return out -} - -func mergeLabels(labels ...map[string]string) map[string]string { - if len(labels) == 0 { - return map[string]string{} - } - out := make(map[string]string, len(labels)*len(labels[0])) - for _, lm := range labels { - for k, v := range lm { - out[k] = v - } - } - return out -} - -var workloadGroupIsController = true - -func workloadEntryFromGroup(name string, proxy *model.Proxy, groupCfg *config.Config) *config.Config { - group := groupCfg.Spec.(*v1alpha3.WorkloadGroup) - entry := group.Template.DeepCopy() - entry.Address = proxy.IPAddresses[0] - // TODO move labels out of entry - // node metadata > WorkloadGroup.Metadata > WorkloadGroup.Template - if group.Metadata != nil && group.Metadata.Labels != nil { - entry.Labels = mergeLabels(entry.Labels, group.Metadata.Labels) - } - if proxy.Metadata != nil && proxy.Metadata.Labels != nil { - entry.Labels = mergeLabels(entry.Labels, proxy.Metadata.Labels) - } - - annotations := map[string]string{AutoRegistrationGroupAnnotation: groupCfg.Name} - if group.Metadata != nil && group.Metadata.Annotations != nil { - annotations = mergeLabels(annotations, group.Metadata.Annotations) - } - - if proxy.Metadata.Network != "" { - entry.Network = string(proxy.Metadata.Network) - } - // proxy.Locality is unset when auto registration takes place, because its - // state is not fully initialized. Therefore, we check the bootstrap node. - if proxy.XdsNode.Locality != nil { - entry.Locality = util.LocalityToString(proxy.XdsNode.Locality) - } - if proxy.Metadata.ProxyConfig != nil && proxy.Metadata.ProxyConfig.ReadinessProbe != nil { - annotations[status.WorkloadEntryHealthCheckAnnotation] = "true" - } - return &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.WorkloadEntry, - Name: name, - Namespace: proxy.Metadata.Namespace, - Labels: entry.Labels, - Annotations: annotations, - OwnerReferences: []metav1.OwnerReference{{ - APIVersion: groupCfg.GroupVersionKind.GroupVersion(), - Kind: groupCfg.GroupVersionKind.Kind, - Name: groupCfg.Name, - UID: kubetypes.UID(groupCfg.UID), - Controller: &workloadGroupIsController, - }}, - }, - Spec: entry, - // TODO status fields used for garbage collection - Status: nil, - } -} - -func makeProxyKey(proxy *model.Proxy) string { - return string(proxy.Metadata.Network) + proxy.IPAddresses[0] -} diff --git a/pilot/pkg/controller/workloadentry/workloadentry_controller_test.go b/pilot/pkg/controller/workloadentry/workloadentry_controller_test.go deleted file mode 100644 index 9c02a7c1b..000000000 --- a/pilot/pkg/controller/workloadentry/workloadentry_controller_test.go +++ /dev/null @@ -1,501 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package workloadentry - -import ( - "fmt" - "reflect" - "testing" - "time" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - "github.com/hashicorp/go-multierror" - "istio.io/api/meta/v1alpha1" - "istio.io/api/networking/v1alpha3" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - kubetypes "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/keepalive" - "github.com/apache/dubbo-go-pixiu/pkg/network" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -func init() { - features.WorkloadEntryAutoRegistration = true - features.WorkloadEntryHealthChecks = true - features.WorkloadEntryCleanupGracePeriod = 200 * time.Millisecond -} - -var ( - tmplA = &v1alpha3.WorkloadGroup{ - Template: &v1alpha3.WorkloadEntry{ - Ports: map[string]uint32{"http": 80}, - Labels: map[string]string{"app": "a"}, - Network: "nw0", - Locality: "reg0/zone0/subzone0", - Weight: 1, - ServiceAccount: "sa-a", - }, - } - wgA = config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.WorkloadGroup, - Namespace: "a", - Name: "wg-a", - Labels: map[string]string{ - "grouplabel": "notonentry", - }, - }, - Spec: tmplA, - Status: nil, - } -) - -func TestNonAutoregisteredWorkloads(t *testing.T) { - store := memory.NewController(memory.Make(collections.All)) - c := NewController(store, "", keepalive.Infinity) - createOrFail(t, store, wgA) - stop := make(chan struct{}) - go c.Run(stop) - defer close(stop) - - cases := map[string]*model.Proxy{ - "missing group": {IPAddresses: []string{"1.2.3.4"}, Metadata: &model.NodeMetadata{Namespace: wgA.Namespace}}, - "missing ip": {Metadata: &model.NodeMetadata{Namespace: wgA.Namespace, AutoRegisterGroup: wgA.Name}}, - "missing namespace": {IPAddresses: []string{"1.2.3.4"}, Metadata: &model.NodeMetadata{AutoRegisterGroup: wgA.Name}}, - "non-existent group": {IPAddresses: []string{"1.2.3.4"}, Metadata: &model.NodeMetadata{Namespace: wgA.Namespace, AutoRegisterGroup: "dne"}}, - } - - for name, tc := range cases { - tc := tc - t.Run(name, func(t *testing.T) { - c.RegisterWorkload(tc, time.Now()) - items, err := store.List(gvk.WorkloadEntry, model.NamespaceAll) - if err != nil { - t.Fatalf("failed listing WorkloadEntry: %v", err) - } - if len(items) != 0 { - t.Fatalf("expected 0 WorkloadEntry") - } - }) - } -} - -func TestAutoregistrationLifecycle(t *testing.T) { - maxConnAge := time.Hour - c1, c2, store := setup(t) - c2.maxConnectionAge = maxConnAge - stopped1 := false - stop1, stop2 := make(chan struct{}), make(chan struct{}) - defer func() { - // stop1 should be killed early, as part of test - if !stopped1 { - close(stop1) - } - }() - defer close(stop2) - go c1.Run(stop1) - go c2.Run(stop2) - - n := fakeNode("reg1", "zone1", "subzone1") - - p := fakeProxy("1.2.3.4", wgA, "nw1") - p.XdsNode = n - - p2 := fakeProxy("1.2.3.4", wgA, "nw2") - p2.XdsNode = n - - p3 := fakeProxy("1.2.3.5", wgA, "nw1") - p3.XdsNode = n - - // allows associating a Register call with Unregister - var origConnTime time.Time - - t.Run("initial registration", func(t *testing.T) { - // simply make sure the entry exists after connecting - c1.RegisterWorkload(p, time.Now()) - checkEntryOrFail(t, store, wgA, p, n, c1.instanceID) - }) - t.Run("multinetwork same ip", func(t *testing.T) { - // make sure we don't overrwrite a similar entry for a different network - c2.RegisterWorkload(p2, time.Now()) - checkEntryOrFail(t, store, wgA, p, n, c1.instanceID) - checkEntryOrFail(t, store, wgA, p2, n, c2.instanceID) - }) - t.Run("fast reconnect", func(t *testing.T) { - t.Run("same instance", func(t *testing.T) { - // disconnect, make sure entry is still there with disconnect meta - c1.QueueUnregisterWorkload(p, time.Now()) - time.Sleep(features.WorkloadEntryCleanupGracePeriod / 2) - checkEntryOrFail(t, store, wgA, p, n, "") - // reconnect, ensure entry is there with the same instance id - origConnTime = time.Now() - c1.RegisterWorkload(p, origConnTime) - checkEntryOrFail(t, store, wgA, p, n, c1.instanceID) - }) - t.Run("same instance: connect before disconnect ", func(t *testing.T) { - // reconnect, ensure entry is there with the same instance id - c1.RegisterWorkload(p, origConnTime.Add(10*time.Millisecond)) - // disconnect (associated with original connect, not the reconnect) - // make sure entry is still there with disconnect meta - c1.QueueUnregisterWorkload(p, origConnTime) - time.Sleep(features.WorkloadEntryCleanupGracePeriod / 2) - checkEntryOrFail(t, store, wgA, p, n, c1.instanceID) - }) - t.Run("different instance", func(t *testing.T) { - // disconnect, make sure entry is still there with disconnect metadata - c1.QueueUnregisterWorkload(p, time.Now()) - time.Sleep(features.WorkloadEntryCleanupGracePeriod / 2) - checkEntryOrFail(t, store, wgA, p, n, "") - // reconnect, ensure entry is there with the new instance id - origConnTime = time.Now() - c2.RegisterWorkload(p, origConnTime) - checkEntryOrFail(t, store, wgA, p, n, c2.instanceID) - }) - }) - t.Run("slow reconnect", func(t *testing.T) { - // disconnect, wait and make sure entry is gone - c2.QueueUnregisterWorkload(p, origConnTime) - retry.UntilSuccessOrFail(t, func() error { - return checkNoEntry(store, wgA, p) - }) - // reconnect - origConnTime = time.Now() - c1.RegisterWorkload(p, origConnTime) - checkEntryOrFail(t, store, wgA, p, n, c1.instanceID) - }) - t.Run("garbage collected if pilot stops after disconnect", func(t *testing.T) { - // disconnect, kill the cleanup queue from the first controller - c1.QueueUnregisterWorkload(p, origConnTime) - // stop processing the delayed close queue in c1, forces using periodic cleanup - close(stop1) - stopped1 = true - // unfortunately, this retry at worst could be twice as long as the sweep interval - retry.UntilSuccessOrFail(t, func() error { - return checkNoEntry(store, wgA, p) - }, retry.Timeout(time.Until(time.Now().Add(21*features.WorkloadEntryCleanupGracePeriod)))) - }) - - t.Run("garbage collected if pilot and workload stops simultaneously before pilot can do anything", func(t *testing.T) { - // simulate p3 has been registered long before - c2.RegisterWorkload(p3, time.Now().Add(-2*maxConnAge)) - - // keep silent to simulate the scenario - - // unfortunately, this retry at worst could be twice as long as the sweep interval - retry.UntilSuccessOrFail(t, func() error { - return checkNoEntry(store, wgA, p3) - }, retry.Timeout(time.Until(time.Now().Add(21*features.WorkloadEntryCleanupGracePeriod)))) - }) - - // TODO test garbage collection if pilot stops before disconnect meta is set (relies on heartbeat) -} - -func TestUpdateHealthCondition(t *testing.T) { - stop := make(chan struct{}) - t.Cleanup(func() { - close(stop) - }) - ig, ig2, store := setup(t) - go ig.Run(stop) - go ig2.Run(stop) - p := fakeProxy("1.2.3.4", wgA, "litNw") - p.XdsNode = fakeNode("reg1", "zone1", "subzone1") - ig.RegisterWorkload(p, time.Now()) - t.Run("auto registered healthy health", func(t *testing.T) { - ig.QueueWorkloadEntryHealth(p, HealthEvent{ - Healthy: true, - }) - checkHealthOrFail(t, store, p, true) - }) - t.Run("auto registered unhealthy health", func(t *testing.T) { - ig.QueueWorkloadEntryHealth(p, HealthEvent{ - Healthy: false, - Message: "lol health bad", - }) - checkHealthOrFail(t, store, p, false) - }) -} - -func TestWorkloadEntryFromGroup(t *testing.T) { - group := config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.WorkloadGroup, - Namespace: "a", - Name: "wg-a", - Labels: map[string]string{ - "grouplabel": "notonentry", - }, - }, - Spec: &v1alpha3.WorkloadGroup{ - Metadata: &v1alpha3.WorkloadGroup_ObjectMeta{ - Labels: map[string]string{"foo": "bar"}, - Annotations: map[string]string{"foo": "bar"}, - }, - Template: &v1alpha3.WorkloadEntry{ - Ports: map[string]uint32{"http": 80}, - Labels: map[string]string{"app": "a"}, - Weight: 1, - Network: "nw0", - Locality: "rgn1/zone1/subzone1", - ServiceAccount: "sa-a", - }, - }, - } - proxy := fakeProxy("10.0.0.1", group, "nw1") - proxy.XdsNode = fakeNode("rgn2", "zone2", "subzone2") - - wantLabels := map[string]string{ - "app": "a", // from WorkloadEntry template - "foo": "bar", // from WorkloadGroup.Metadata - "merge": "me", // from Node metadata - } - - want := config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.WorkloadEntry, - Name: "test-we", - Namespace: proxy.Metadata.Namespace, - Labels: wantLabels, - Annotations: map[string]string{ - AutoRegistrationGroupAnnotation: group.Name, - "foo": "bar", - }, - OwnerReferences: []metav1.OwnerReference{{ - APIVersion: group.GroupVersionKind.GroupVersion(), - Kind: group.GroupVersionKind.Kind, - Name: group.Name, - UID: kubetypes.UID(group.UID), - Controller: &workloadGroupIsController, - }}, - }, - Spec: &v1alpha3.WorkloadEntry{ - Address: "10.0.0.1", - Ports: map[string]uint32{ - "http": 80, - }, - Labels: wantLabels, - Network: "nw1", - Locality: "rgn2/zone2/subzone2", - Weight: 1, - ServiceAccount: "sa-a", - }, - } - - got := workloadEntryFromGroup("test-we", proxy, &group) - assert.Equal(t, got, &want) -} - -func setup(t *testing.T) (*Controller, *Controller, model.ConfigStoreController) { - store := memory.NewController(memory.Make(collections.All)) - c1 := NewController(store, "pilot-1", keepalive.Infinity) - c2 := NewController(store, "pilot-2", keepalive.Infinity) - createOrFail(t, store, wgA) - return c1, c2, store -} - -func checkNoEntry(store model.ConfigStoreController, wg config.Config, proxy *model.Proxy) error { - name := wg.Name + "-" + proxy.IPAddresses[0] - if proxy.Metadata.Network != "" { - name += "-" + string(proxy.Metadata.Network) - } - - cfg := store.Get(gvk.WorkloadEntry, name, wg.Namespace) - if cfg != nil { - return fmt.Errorf("did not expect WorkloadEntry %s/%s to exist", wg.Namespace, name) - } - return nil -} - -func checkEntry( - store model.ConfigStore, - wg config.Config, - proxy *model.Proxy, - node *core.Node, - connectedTo string, -) (err error) { - name := wg.Name + "-" + proxy.IPAddresses[0] - if proxy.Metadata.Network != "" { - name += "-" + string(proxy.Metadata.Network) - } - - cfg := store.Get(gvk.WorkloadEntry, name, wg.Namespace) - if cfg == nil { - err = multierror.Append(fmt.Errorf("expected WorkloadEntry %s/%s to exist", wg.Namespace, name)) - return - } - tmpl := wg.Spec.(*v1alpha3.WorkloadGroup) - we := cfg.Spec.(*v1alpha3.WorkloadEntry) - - // check workload entry specific fields - if !reflect.DeepEqual(we.Ports, tmpl.Template.Ports) { - err = multierror.Append(err, fmt.Errorf("expected ports from WorkloadGroup")) - } - if we.Address != proxy.IPAddresses[0] { - err = multierror.Append(fmt.Errorf("entry has address %s; expected %s", we.Address, proxy.IPAddresses[0])) - } - - if proxy.Metadata.Network != "" { - if we.Network != string(proxy.Metadata.Network) { - err = multierror.Append(fmt.Errorf("entry has network %s; expected to match meta network %s", we.Network, proxy.Metadata.Network)) - } - } else { - if we.Network != tmpl.Template.Network { - err = multierror.Append(fmt.Errorf("entry has network %s; expected to match group template network %s", we.Network, tmpl.Template.Network)) - } - } - - loc := tmpl.Template.Locality - if node.Locality != nil { - loc = util.LocalityToString(node.Locality) - } - if we.Locality != loc { - err = multierror.Append(fmt.Errorf("entry has locality %s; expected %s", we.Locality, loc)) - } - - // check controller annotations - if connectedTo != "" { - if v := cfg.Annotations[WorkloadControllerAnnotation]; v != connectedTo { - err = multierror.Append(err, fmt.Errorf("expected WorkloadEntry to be updated by %s; got %s", connectedTo, v)) - } - if _, ok := cfg.Annotations[ConnectedAtAnnotation]; !ok { - err = multierror.Append(err, fmt.Errorf("expected connection timestamp to be set")) - } - } else if _, ok := cfg.Annotations[DisconnectedAtAnnotation]; !ok { - err = multierror.Append(err, fmt.Errorf("expected disconnection timestamp to be set")) - } - - // check all labels are copied to the WorkloadEntry - if !reflect.DeepEqual(cfg.Labels, we.Labels) { - err = multierror.Append(err, fmt.Errorf("spec labels on WorkloadEntry should match meta labels")) - } - for k, v := range tmpl.Template.Labels { - if _, ok := proxy.Metadata.Labels[k]; ok { - // would be overwritten - continue - } - if we.Labels[k] != v { - err = multierror.Append(err, fmt.Errorf("labels missing on WorkloadEntry: %s=%s from template", k, v)) - } - } - for k, v := range proxy.Metadata.Labels { - if we.Labels[k] != v { - err = multierror.Append(err, fmt.Errorf("labels missing on WorkloadEntry: %s=%s from proxy meta", k, v)) - } - } - return -} - -func checkEntryOrFail( - t test.Failer, - store model.ConfigStoreController, - wg config.Config, - proxy *model.Proxy, - node *core.Node, - connectedTo string, -) { - if err := checkEntry(store, wg, proxy, node, connectedTo); err != nil { - t.Fatal(err) - } -} - -func checkEntryHealth(store model.ConfigStoreController, proxy *model.Proxy, healthy bool) (err error) { - name := proxy.AutoregisteredWorkloadEntryName - cfg := store.Get(gvk.WorkloadEntry, name, proxy.Metadata.Namespace) - if cfg == nil || cfg.Status == nil { - err = multierror.Append(fmt.Errorf("expected workloadEntry %s/%s to exist", name, proxy.Metadata.Namespace)) - return - } - stat := cfg.Status.(*v1alpha1.IstioStatus) - found := false - idx := 0 - for i, cond := range stat.Conditions { - if cond.Type == "Healthy" { - idx = i - found = true - } - } - if !found { - err = multierror.Append(err, fmt.Errorf("expected condition of type Health on WorkloadEntry %s/%s", - name, proxy.Metadata.Namespace)) - } else { - statStr := stat.Conditions[idx].Status - if healthy && statStr != "True" { - err = multierror.Append(err, fmt.Errorf("expected healthy condition on WorkloadEntry %s/%s", - name, proxy.Metadata.Namespace)) - } - if !healthy && statStr != "False" { - err = multierror.Append(err, fmt.Errorf("expected unhealthy condition on WorkloadEntry %s/%s", - name, proxy.Metadata.Namespace)) - } - } - return -} - -func checkHealthOrFail(t test.Failer, store model.ConfigStoreController, proxy *model.Proxy, healthy bool) { - err := wait.Poll(100*time.Millisecond, 1*time.Second, func() (done bool, err error) { - err2 := checkEntryHealth(store, proxy, healthy) - if err2 != nil { - return false, nil - } - return true, nil - }) - if err != nil { - t.Fatal(err) - } -} - -func fakeProxy(ip string, wg config.Config, nw network.ID) *model.Proxy { - return &model.Proxy{ - IPAddresses: []string{ip}, - Metadata: &model.NodeMetadata{ - AutoRegisterGroup: wg.Name, - Namespace: wg.Namespace, - Network: nw, - Labels: map[string]string{"merge": "me"}, - }, - } -} - -func fakeNode(r, z, sz string) *core.Node { - return &core.Node{ - Locality: &core.Locality{ - Region: r, - Zone: z, - SubZone: sz, - }, - } -} - -// createOrFail wraps config creation with convience for failing tests -func createOrFail(t test.Failer, store model.ConfigStoreController, cfg config.Config) { - if _, err := store.Create(cfg); err != nil { - t.Fatalf("failed creating %s/%s: %v", cfg.Namespace, cfg.Name, err) - } -} diff --git a/pilot/pkg/credentials/kube/leak_test.go b/pilot/pkg/credentials/kube/leak_test.go deleted file mode 100644 index 3d83f6726..000000000 --- a/pilot/pkg/credentials/kube/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/credentials/kube/multicluster.go b/pilot/pkg/credentials/kube/multicluster.go deleted file mode 100644 index 02ba059b6..000000000 --- a/pilot/pkg/credentials/kube/multicluster.go +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "fmt" - "sync" -) - -import ( - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/credentials" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/kube/multicluster" -) - -type secretHandler func(name string, namespace string) - -// Multicluster structure holds the remote kube Controllers and multicluster specific attributes. -type Multicluster struct { - remoteKubeControllers map[cluster.ID]*CredentialsController - m sync.Mutex // protects remoteKubeControllers - localCluster cluster.ID - secretHandlers []secretHandler -} - -var _ credentials.MulticlusterController = &Multicluster{} - -func NewMulticluster(localCluster cluster.ID) *Multicluster { - m := &Multicluster{ - remoteKubeControllers: map[cluster.ID]*CredentialsController{}, - localCluster: localCluster, - } - - return m -} - -func (m *Multicluster) ClusterAdded(cluster *multicluster.Cluster, _ <-chan struct{}) error { - log.Infof("initializing Kubernetes credential reader for cluster %v", cluster.ID) - sc := NewCredentialsController(cluster.Client, cluster.ID) - m.m.Lock() - m.remoteKubeControllers[cluster.ID] = sc - for _, onCredential := range m.secretHandlers { - sc.AddEventHandler(onCredential) - } - m.m.Unlock() - return nil -} - -func (m *Multicluster) ClusterUpdated(cluster *multicluster.Cluster, stop <-chan struct{}) error { - if err := m.ClusterDeleted(cluster.ID); err != nil { - return err - } - if err := m.ClusterAdded(cluster, stop); err != nil { - return err - } - return nil -} - -func (m *Multicluster) ClusterDeleted(key cluster.ID) error { - m.m.Lock() - delete(m.remoteKubeControllers, key) - m.m.Unlock() - return nil -} - -func (m *Multicluster) ForCluster(clusterID cluster.ID) (credentials.Controller, error) { - if _, f := m.remoteKubeControllers[clusterID]; !f { - return nil, fmt.Errorf("cluster %v is not configured", clusterID) - } - agg := &AggregateController{} - agg.controllers = []*CredentialsController{} - agg.authController = m.remoteKubeControllers[clusterID] - if clusterID != m.localCluster { - // If the request cluster is not the local cluster, we will append it and use it for auth - // This means we will prioritize the proxy cluster, then the local cluster for credential lookup - // Authorization will always use the proxy cluster. - agg.controllers = append(agg.controllers, m.remoteKubeControllers[clusterID]) - } - agg.controllers = append(agg.controllers, m.remoteKubeControllers[m.localCluster]) - return agg, nil -} - -func (m *Multicluster) AddSecretHandler(h secretHandler) { - m.secretHandlers = append(m.secretHandlers, h) - for _, c := range m.remoteKubeControllers { - c.AddEventHandler(h) - } -} - -type AggregateController struct { - // controllers to use to look up certs. Generally this will consistent of the local (config) cluster - // and a single remote cluster where the proxy resides - controllers []*CredentialsController - authController *CredentialsController -} - -var _ credentials.Controller = &AggregateController{} - -func (a *AggregateController) GetKeyAndCert(name, namespace string) (key []byte, cert []byte, err error) { - // Search through all clusters, find first non-empty result - var firstError error - for _, c := range a.controllers { - k, c, err := c.GetKeyAndCert(name, namespace) - if err != nil { - if firstError == nil { - firstError = err - } - } else { - return k, c, nil - } - } - return nil, nil, firstError -} - -func (a *AggregateController) GetCaCert(name, namespace string) (cert []byte, err error) { - // Search through all clusters, find first non-empty result - var firstError error - for _, c := range a.controllers { - k, err := c.GetCaCert(name, namespace) - if err != nil { - if firstError == nil { - firstError = err - } - } else { - return k, nil - } - } - return nil, firstError -} - -func (a *AggregateController) Authorize(serviceAccount, namespace string) error { - return a.authController.Authorize(serviceAccount, namespace) -} - -func (a *AggregateController) AddEventHandler(f func(name string, namespace string)) { - // no ops -} - -func (a *AggregateController) GetDockerCredential(name, namespace string) ([]byte, error) { - // Search through all clusters, find first non-empty result - var firstError error - for _, c := range a.controllers { - k, err := c.GetDockerCredential(name, namespace) - if err != nil { - if firstError == nil { - firstError = err - } - } else { - return k, nil - } - } - return nil, firstError -} diff --git a/pilot/pkg/credentials/kube/secrets.go b/pilot/pkg/credentials/kube/secrets.go deleted file mode 100644 index da7a8b5b3..000000000 --- a/pilot/pkg/credentials/kube/secrets.go +++ /dev/null @@ -1,317 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "context" - "fmt" - "sort" - "strings" - "sync" - "time" -) - -import ( - "istio.io/pkg/log" - authorizationv1 "k8s.io/api/authorization/v1" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/runtime" - informersv1 "k8s.io/client-go/informers/core/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/fake" - authorizationv1client "k8s.io/client-go/kubernetes/typed/authorization/v1" - listersv1 "k8s.io/client-go/listers/core/v1" - k8stesting "k8s.io/client-go/testing" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/credentials" - securitymodel "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/model" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/controllers" -) - -const ( - // The ID/name for the certificate chain in kubernetes generic secret. - GenericScrtCert = "cert" - // The ID/name for the private key in kubernetes generic secret. - GenericScrtKey = "key" - // The ID/name for the CA certificate in kubernetes generic secret. - GenericScrtCaCert = "cacert" - - // The ID/name for the certificate chain in kubernetes tls secret. - TLSSecretCert = "tls.crt" - // The ID/name for the k8sKey in kubernetes tls secret. - TLSSecretKey = "tls.key" - // The ID/name for the CA certificate in kubernetes tls secret - TLSSecretCaCert = "ca.crt" -) - -type CredentialsController struct { - secretInformer cache.SharedIndexInformer - secretLister listersv1.SecretLister - sar authorizationv1client.SubjectAccessReviewInterface - - clusterID cluster.ID - - mu sync.RWMutex - authorizationCache map[authorizationKey]authorizationResponse -} - -type authorizationKey string - -type authorizationResponse struct { - expiration time.Time - authorized error -} - -var _ credentials.Controller = &CredentialsController{} - -func NewCredentialsController(client kube.Client, clusterID cluster.ID) *CredentialsController { - informer := client.KubeInformer().InformerFor(&v1.Secret{}, func(k kubernetes.Interface, resync time.Duration) cache.SharedIndexInformer { - return informersv1.NewFilteredSecretInformer( - k, metav1.NamespaceAll, resync, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, - func(options *metav1.ListOptions) { - // We only care about TLS certificates and docker config for Wasm image pulling. - // Unfortunately, it is not as simple as selecting type=kubernetes.io/tls and type=kubernetes.io/dockerconfigjson. - // Because of legacy reasons and supporting an extra ca.crt, we also support generic types. - // Its also likely users have started to use random types and expect them to continue working. - // This makes the assumption we will never care about Helm secrets or SA token secrets - two common - // large secrets in clusters. - // This is a best effort optimization only; the code would behave correctly if we watched all secrets. - options.FieldSelector = fields.AndSelectors( - fields.OneTermNotEqualSelector("type", "helm.sh/release.v1"), - fields.OneTermNotEqualSelector("type", string(v1.SecretTypeServiceAccountToken)), - ).String() - }, - ) - }) - - return &CredentialsController{ - secretInformer: informer, - secretLister: listersv1.NewSecretLister(informer.GetIndexer()), - - sar: client.AuthorizationV1().SubjectAccessReviews(), - clusterID: clusterID, - authorizationCache: make(map[authorizationKey]authorizationResponse), - } -} - -func toUser(serviceAccount, namespace string) string { - return fmt.Sprintf("system:serviceaccount:%s:%s", namespace, serviceAccount) -} - -const cacheTTL = time.Minute - -// clearExpiredCache iterates through the cache and removes all expired entries. Should be called with mutex held. -func (s *CredentialsController) clearExpiredCache() { - for k, v := range s.authorizationCache { - if v.expiration.Before(time.Now()) { - delete(s.authorizationCache, k) - } - } -} - -// cachedAuthorization checks the authorization cache -// nolint -func (s *CredentialsController) cachedAuthorization(user string) (error, bool) { - key := authorizationKey(user) - s.mu.Lock() - defer s.mu.Unlock() - s.clearExpiredCache() - // No need to check expiration, we will evict expired entries above - got, f := s.authorizationCache[key] - if !f { - return nil, false - } - return got.authorized, true -} - -// cachedAuthorization checks the authorization cache -func (s *CredentialsController) insertCache(user string, response error) { - s.mu.Lock() - defer s.mu.Unlock() - key := authorizationKey(user) - expDelta := cacheTTL - if response == nil { - // Cache success a bit longer, there is no need to quickly revoke access - expDelta *= 5 - } - log.Debugf("cached authorization for user %s: %v", user, response) - s.authorizationCache[key] = authorizationResponse{ - expiration: time.Now().Add(expDelta), - authorized: response, - } -} - -// DisableAuthorizationForTest makes the authorization check always pass. Should be used only for tests. -func DisableAuthorizationForTest(fake *fake.Clientset) { - fake.Fake.PrependReactor("create", "subjectaccessreviews", func(action k8stesting.Action) (bool, runtime.Object, error) { - return true, &authorizationv1.SubjectAccessReview{ - Status: authorizationv1.SubjectAccessReviewStatus{ - Allowed: true, - }, - }, nil - }) -} - -func (s *CredentialsController) Authorize(serviceAccount, namespace string) error { - user := toUser(serviceAccount, namespace) - if cached, f := s.cachedAuthorization(user); f { - return cached - } - err := func() error { - resp, err := s.sar.Create(context.Background(), &authorizationv1.SubjectAccessReview{ - ObjectMeta: metav1.ObjectMeta{}, - Spec: authorizationv1.SubjectAccessReviewSpec{ - ResourceAttributes: &authorizationv1.ResourceAttributes{ - Namespace: namespace, - Verb: "list", - Resource: "secrets", - }, - User: user, - }, - }, metav1.CreateOptions{}) - if err != nil { - return err - } - if !resp.Status.Allowed { - return fmt.Errorf("%s/%s is not authorized to read secrets: %v", serviceAccount, namespace, resp.Status.Reason) - } - return nil - }() - s.insertCache(user, err) - return err -} - -func (s *CredentialsController) GetKeyAndCert(name, namespace string) (key []byte, cert []byte, err error) { - k8sSecret, err := s.secretLister.Secrets(namespace).Get(name) - if err != nil { - return nil, nil, fmt.Errorf("secret %v/%v not found", namespace, name) - } - - return extractKeyAndCert(k8sSecret) -} - -func (s *CredentialsController) GetCaCert(name, namespace string) (cert []byte, err error) { - strippedName := strings.TrimSuffix(name, securitymodel.SdsCaSuffix) - k8sSecret, err := s.secretLister.Secrets(namespace).Get(name) - if err != nil { - // Could not fetch cert, look for secret without -cacert suffix - k8sSecret, caCertErr := s.secretLister.Secrets(namespace).Get(strippedName) - if caCertErr != nil { - return nil, fmt.Errorf("secret %v/%v not found", namespace, strippedName) - } - return extractRoot(k8sSecret) - } - return extractRoot(k8sSecret) -} - -func (s *CredentialsController) GetDockerCredential(name, namespace string) ([]byte, error) { - k8sSecret, err := s.secretLister.Secrets(namespace).Get(name) - if err != nil || k8sSecret == nil { - return nil, fmt.Errorf("secret %v/%v not found", namespace, name) - } - if k8sSecret.Type != v1.SecretTypeDockerConfigJson { - return nil, fmt.Errorf("type of secret %v/%v is not %v", namespace, name, v1.SecretTypeDockerConfigJson) - } - if cred, found := k8sSecret.Data[v1.DockerConfigJsonKey]; found { - return cred, nil - } - return nil, fmt.Errorf("cannot find docker config at secret %v/%v", namespace, name) -} - -func hasKeys(d map[string][]byte, keys ...string) bool { - for _, k := range keys { - _, f := d[k] - if !f { - return false - } - } - return true -} - -func hasValue(d map[string][]byte, keys ...string) bool { - for _, k := range keys { - v := d[k] - if len(v) == 0 { - return false - } - } - return true -} - -// extractKeyAndCert extracts server key, certificate -func extractKeyAndCert(scrt *v1.Secret) (key, cert []byte, err error) { - if hasValue(scrt.Data, GenericScrtCert, GenericScrtKey) { - return scrt.Data[GenericScrtKey], scrt.Data[GenericScrtCert], nil - } - if hasValue(scrt.Data, TLSSecretCert, TLSSecretKey) { - return scrt.Data[TLSSecretKey], scrt.Data[TLSSecretCert], nil - } - // No cert found. Try to generate a helpful error messsage - if hasKeys(scrt.Data, GenericScrtCert, GenericScrtKey) { - return nil, nil, fmt.Errorf("found keys %q and %q, but they were empty", GenericScrtCert, GenericScrtKey) - } - if hasKeys(scrt.Data, TLSSecretCert, TLSSecretKey) { - return nil, nil, fmt.Errorf("found keys %q and %q, but they were empty", TLSSecretCert, TLSSecretKey) - } - found := truncatedKeysMessage(scrt.Data) - return nil, nil, fmt.Errorf("found secret, but didn't have expected keys (%s and %s) or (%s and %s); found: %s", - GenericScrtCert, GenericScrtKey, TLSSecretCert, TLSSecretKey, found) -} - -func truncatedKeysMessage(data map[string][]byte) string { - keys := []string{} - for k := range data { - keys = append(keys, k) - } - sort.Strings(keys) - if len(keys) < 3 { - return strings.Join(keys, ", ") - } - return fmt.Sprintf("%s, and %d more...", strings.Join(keys[:3], ", "), len(keys)-3) -} - -// extractRoot extracts the root certificate -func extractRoot(scrt *v1.Secret) (cert []byte, err error) { - if hasValue(scrt.Data, GenericScrtCaCert) { - return scrt.Data[GenericScrtCaCert], nil - } - if hasValue(scrt.Data, TLSSecretCaCert) { - return scrt.Data[TLSSecretCaCert], nil - } - // No cert found. Try to generate a helpful error messsage - if hasKeys(scrt.Data, GenericScrtCaCert) { - return nil, fmt.Errorf("found key %q, but it was empty", GenericScrtCaCert) - } - if hasKeys(scrt.Data, TLSSecretCaCert) { - return nil, fmt.Errorf("found key %q, but it was empty", TLSSecretCaCert) - } - found := truncatedKeysMessage(scrt.Data) - return nil, fmt.Errorf("found secret, but didn't have expected keys %s or %s; found: %s", - GenericScrtCaCert, TLSSecretCaCert, found) -} - -func (s *CredentialsController) AddEventHandler(h func(name string, namespace string)) { - // register handler before informer starts - s.secretInformer.AddEventHandler(controllers.ObjectHandler(func(o controllers.Object) { - h(o.GetName(), o.GetNamespace()) - })) -} diff --git a/pilot/pkg/credentials/kube/secrets_test.go b/pilot/pkg/credentials/kube/secrets_test.go deleted file mode 100644 index 0998dbf5b..000000000 --- a/pilot/pkg/credentials/kube/secrets_test.go +++ /dev/null @@ -1,475 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "fmt" - "testing" -) - -import ( - authorizationv1 "k8s.io/api/authorization/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes/fake" - k8stesting "k8s.io/client-go/testing" -) - -import ( - cluster2 "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/multicluster" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -func makeSecret(name string, data map[string]string, secretType corev1.SecretType) *corev1.Secret { - bdata := map[string][]byte{} - for k, v := range data { - bdata[k] = []byte(v) - } - return &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: "default", - }, - Data: bdata, - Type: secretType, - } -} - -var ( - genericCert = makeSecret("generic", map[string]string{ - GenericScrtCert: "generic-cert", GenericScrtKey: "generic-key", - }, corev1.SecretTypeTLS) - genericMtlsCert = makeSecret("generic-mtls", map[string]string{ - GenericScrtCert: "generic-mtls-cert", GenericScrtKey: "generic-mtls-key", GenericScrtCaCert: "generic-mtls-ca", - }, corev1.SecretTypeTLS) - genericMtlsCertSplit = makeSecret("generic-mtls-split", map[string]string{ - GenericScrtCert: "generic-mtls-split-cert", GenericScrtKey: "generic-mtls-split-key", - }, corev1.SecretTypeTLS) - genericMtlsCertSplitCa = makeSecret("generic-mtls-split-cacert", map[string]string{ - GenericScrtCaCert: "generic-mtls-split-ca", - }, corev1.SecretTypeTLS) - overlapping = makeSecret("overlap", map[string]string{ - GenericScrtCert: "cert", GenericScrtKey: "key", GenericScrtCaCert: "main-ca", - }, corev1.SecretTypeTLS) - overlappingCa = makeSecret("overlap-cacert", map[string]string{ - GenericScrtCaCert: "split-ca", - }, corev1.SecretTypeTLS) - tlsCert = makeSecret("tls", map[string]string{ - TLSSecretCert: "tls-cert", TLSSecretKey: "tls-key", - }, corev1.SecretTypeTLS) - tlsMtlsCert = makeSecret("tls-mtls", map[string]string{ - TLSSecretCert: "tls-mtls-cert", TLSSecretKey: "tls-mtls-key", TLSSecretCaCert: "tls-mtls-ca", - }, corev1.SecretTypeTLS) - tlsMtlsCertSplit = makeSecret("tls-mtls-split", map[string]string{ - TLSSecretCert: "tls-mtls-split-cert", TLSSecretKey: "tls-mtls-split-key", - }, corev1.SecretTypeTLS) - tlsMtlsCertSplitCa = makeSecret("tls-mtls-split-cacert", map[string]string{ - TLSSecretCaCert: "tls-mtls-split-ca", - }, corev1.SecretTypeTLS) - emptyCert = makeSecret("empty-cert", map[string]string{ - TLSSecretCert: "", TLSSecretKey: "tls-key", - }, corev1.SecretTypeTLS) - wrongKeys = makeSecret("wrong-keys", map[string]string{ - "foo-bar": "my-cert", TLSSecretKey: "tls-key", - }, corev1.SecretTypeTLS) - dockerjson = makeSecret("docker-json", map[string]string{ - corev1.DockerConfigJsonKey: "docker-cred", - }, corev1.SecretTypeDockerConfigJson) - badDockerjson = makeSecret("bad-docker-json", map[string]string{ - "docker-key": "docker-cred", - }, corev1.SecretTypeDockerConfigJson) -) - -func TestSecretsController(t *testing.T) { - secrets := []runtime.Object{ - genericCert, - genericMtlsCert, - genericMtlsCertSplit, - genericMtlsCertSplitCa, - overlapping, - overlappingCa, - tlsCert, - tlsMtlsCert, - tlsMtlsCertSplit, - tlsMtlsCertSplitCa, - emptyCert, - wrongKeys, - } - client := kube.NewFakeClient(secrets...) - sc := NewCredentialsController(client, "") - stop := make(chan struct{}) - t.Cleanup(func() { - close(stop) - }) - client.RunAndWait(stop) - cases := []struct { - name string - namespace string - cert string - key string - caCert string - expectedError string - expectedCAError string - }{ - { - name: "generic", - namespace: "default", - cert: "generic-cert", - key: "generic-key", - expectedCAError: "found secret, but didn't have expected keys cacert or ca.crt; found: cert, key", - }, - { - name: "generic-mtls", - namespace: "default", - cert: "generic-mtls-cert", - key: "generic-mtls-key", - caCert: "generic-mtls-ca", - }, - { - name: "generic-mtls-split", - namespace: "default", - cert: "generic-mtls-split-cert", - key: "generic-mtls-split-key", - expectedCAError: "found secret, but didn't have expected keys cacert or ca.crt; found: cert, key", - }, - { - name: "generic-mtls-split-cacert", - namespace: "default", - caCert: "generic-mtls-split-ca", - expectedError: "found secret, but didn't have expected keys (cert and key) or (tls.crt and tls.key); found: cacert", - }, - // The -cacert secret has precedence - { - name: "overlap-cacert", - namespace: "default", - caCert: "split-ca", - expectedError: "found secret, but didn't have expected keys (cert and key) or (tls.crt and tls.key); found: cacert", - }, - { - name: "tls", - namespace: "default", - cert: "tls-cert", - key: "tls-key", - expectedCAError: "found secret, but didn't have expected keys cacert or ca.crt; found: tls.crt, tls.key", - }, - { - name: "tls-mtls", - namespace: "default", - cert: "tls-mtls-cert", - key: "tls-mtls-key", - caCert: "tls-mtls-ca", - }, - { - name: "tls-mtls-split", - namespace: "default", - cert: "tls-mtls-split-cert", - key: "tls-mtls-split-key", - expectedCAError: "found secret, but didn't have expected keys cacert or ca.crt; found: tls.crt, tls.key", - }, - { - name: "tls-mtls-split-cacert", - namespace: "default", - caCert: "tls-mtls-split-ca", - expectedError: "found secret, but didn't have expected keys (cert and key) or (tls.crt and tls.key); found: ca.crt", - }, - { - name: "generic", - namespace: "wrong-namespace", - expectedError: `secret wrong-namespace/generic not found`, - expectedCAError: `secret wrong-namespace/generic not found`, - }, - { - name: "empty-cert", - namespace: "default", - expectedError: `found keys "tls.crt" and "tls.key", but they were empty`, - expectedCAError: "found secret, but didn't have expected keys cacert or ca.crt; found: tls.crt, tls.key", - }, - { - name: "wrong-keys", - namespace: "default", - expectedError: `found secret, but didn't have expected keys (cert and key) or (tls.crt and tls.key); found: foo-bar, tls.key`, - expectedCAError: "found secret, but didn't have expected keys cacert or ca.crt; found: foo-bar, tls.key", - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - key, cert, err := sc.GetKeyAndCert(tt.name, tt.namespace) - if tt.key != string(key) { - t.Errorf("got key %q, wanted %q", string(key), tt.key) - } - if tt.cert != string(cert) { - t.Errorf("got cert %q, wanted %q", string(cert), tt.cert) - } - if tt.expectedError != errString(err) { - t.Errorf("got err %q, wanted %q", errString(err), tt.expectedError) - } - caCert, err := sc.GetCaCert(tt.name, tt.namespace) - if tt.caCert != string(caCert) { - t.Errorf("got caCert %q, wanted %q", string(caCert), tt.caCert) - } - if tt.expectedCAError != errString(err) { - t.Errorf("got ca err %q, wanted %q", errString(err), tt.expectedCAError) - } - }) - } -} - -func TestDockerCredentials(t *testing.T) { - secrets := []runtime.Object{ - dockerjson, - badDockerjson, - genericCert, - } - client := kube.NewFakeClient(secrets...) - sc := NewCredentialsController(client, "") - stop := make(chan struct{}) - t.Cleanup(func() { - close(stop) - }) - client.RunAndWait(stop) - cases := []struct { - name string - namespace string - expectedType corev1.SecretType - expectedDockerCred string - expectedDockerError string - }{ - { - name: "docker-json", - namespace: "default", - expectedType: corev1.SecretTypeDockerConfigJson, - expectedDockerCred: "docker-cred", - }, - { - name: "bad-docker-json", - namespace: "default", - expectedDockerError: "cannot find docker config at secret default/bad-docker-json", - }, - { - name: "wrong-name", - namespace: "default", - expectedDockerError: "secret default/wrong-name not found", - }, - { - name: "generic", - namespace: "default", - expectedDockerError: "type of secret default/generic is not kubernetes.io/dockerconfigjson", - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - dockerCred, err := sc.GetDockerCredential(tt.name, tt.namespace) - if tt.expectedDockerCred != "" && tt.expectedDockerCred != string(dockerCred) { - t.Errorf("got docker credential %q, want %q", string(dockerCred), tt.expectedDockerCred) - } - if tt.expectedDockerError != "" && tt.expectedDockerError != errString(err) { - t.Errorf("got docker err %q, wanted %q", errString(err), tt.expectedDockerError) - } - }) - } -} - -func errString(e error) string { - if e == nil { - return "" - } - return e.Error() -} - -func allowIdentities(c kube.Client, identities ...string) { - allowed := sets.New(identities...) - c.Kube().(*fake.Clientset).Fake.PrependReactor("create", "subjectaccessreviews", func(action k8stesting.Action) (bool, runtime.Object, error) { - a := action.(k8stesting.CreateAction).GetObject().(*authorizationv1.SubjectAccessReview) - if allowed.Contains(a.Spec.User) { - return true, &authorizationv1.SubjectAccessReview{ - Status: authorizationv1.SubjectAccessReviewStatus{ - Allowed: true, - }, - }, nil - } - return true, &authorizationv1.SubjectAccessReview{ - Status: authorizationv1.SubjectAccessReviewStatus{ - Allowed: false, - Reason: fmt.Sprintf("user %s cannot access secrets", a.Spec.User), - }, - }, nil - }) -} - -func TestForCluster(t *testing.T) { - localClient := kube.NewFakeClient() - remoteClient := kube.NewFakeClient() - sc := NewMulticluster("local") - _ = sc.ClusterAdded(&multicluster.Cluster{ID: "local", Client: localClient}, nil) - _ = sc.ClusterAdded(&multicluster.Cluster{ID: "remote", Client: remoteClient}, nil) - _ = sc.ClusterAdded(&multicluster.Cluster{ID: "remote2", Client: remoteClient}, nil) - cases := []struct { - cluster cluster2.ID - allowed bool - }{ - {"local", true}, - {"remote", true}, - {"remote2", true}, - {"invalid", false}, - } - for _, tt := range cases { - t.Run(string(tt.cluster), func(t *testing.T) { - _, err := sc.ForCluster(tt.cluster) - if (err == nil) != tt.allowed { - t.Fatalf("expected allowed=%v, got err=%v", tt.allowed, err) - } - }) - } -} - -func TestAuthorize(t *testing.T) { - localClient := kube.NewFakeClient() - remoteClient := kube.NewFakeClient() - allowIdentities(localClient, "system:serviceaccount:ns-local:sa-allowed") - allowIdentities(remoteClient, "system:serviceaccount:ns-remote:sa-allowed") - sc := NewMulticluster("local") - _ = sc.ClusterAdded(&multicluster.Cluster{ID: "local", Client: localClient}, nil) - _ = sc.ClusterAdded(&multicluster.Cluster{ID: "remote", Client: remoteClient}, nil) - cases := []struct { - sa string - ns string - cluster cluster2.ID - allowed bool - }{ - {"sa-denied", "ns-local", "local", false}, - {"sa-allowed", "ns-local", "local", true}, - {"sa-denied", "ns-local", "remote", false}, - {"sa-allowed", "ns-local", "remote", false}, - {"sa-denied", "ns-remote", "local", false}, - {"sa-allowed", "ns-remote", "local", false}, - {"sa-denied", "ns-remote", "remote", false}, - {"sa-allowed", "ns-remote", "remote", true}, - } - for _, tt := range cases { - t.Run(fmt.Sprintf("%v/%v/%v", tt.sa, tt.ns, tt.cluster), func(t *testing.T) { - con, err := sc.ForCluster(tt.cluster) - if err != nil { - t.Fatal(err) - } - got := con.Authorize(tt.sa, tt.ns) - if (got == nil) != tt.allowed { - t.Fatalf("expected allowed=%v, got error=%v", tt.allowed, got) - } - }) - } -} - -func TestSecretsControllerMulticluster(t *testing.T) { - stop := make(chan struct{}) - defer close(stop) - secretsLocal := []runtime.Object{ - tlsCert, - tlsMtlsCert, - tlsMtlsCertSplit, - tlsMtlsCertSplitCa, - } - tlsCertModified := makeSecret("tls", map[string]string{ - TLSSecretCert: "tls-cert-mod", TLSSecretKey: "tls-key", - }, corev1.SecretTypeTLS) - secretsRemote := []runtime.Object{ - tlsCertModified, - genericCert, - genericMtlsCert, - genericMtlsCertSplit, - genericMtlsCertSplitCa, - } - - localClient := kube.NewFakeClient(secretsLocal...) - remoteClient := kube.NewFakeClient(secretsRemote...) - otherRemoteClient := kube.NewFakeClient() - sc := NewMulticluster("local") - _ = sc.ClusterAdded(&multicluster.Cluster{ID: "local", Client: localClient}, nil) - _ = sc.ClusterAdded(&multicluster.Cluster{ID: "remote", Client: remoteClient}, nil) - _ = sc.ClusterAdded(&multicluster.Cluster{ID: "other", Client: otherRemoteClient}, nil) - - // normally the remote secrets controller would start these - localClient.RunAndWait(stop) - remoteClient.RunAndWait(stop) - otherRemoteClient.RunAndWait(stop) - - cases := []struct { - name string - namespace string - cluster cluster2.ID - cert string - key string - caCert string - }{ - // From local cluster - // These are only in remote cluster, we do not have access - {"generic", "default", "local", "", "", ""}, - {"generic-mtls", "default", "local", "", "", ""}, - {"generic-mtls-split", "default", "local", "", "", ""}, - {"generic-mtls-split-cacert", "default", "local", "", "", ""}, - // These are in local cluster, we can access - {"tls", "default", "local", "tls-cert", "tls-key", ""}, - {"tls-mtls", "default", "local", "tls-mtls-cert", "tls-mtls-key", "tls-mtls-ca"}, - {"tls-mtls-split", "default", "local", "tls-mtls-split-cert", "tls-mtls-split-key", ""}, - {"tls-mtls-split-cacert", "default", "local", "", "", "tls-mtls-split-ca"}, - {"generic", "wrong-namespace", "local", "", "", ""}, - - // From remote cluster - // We can access all credentials - local and remote - {"generic", "default", "remote", "generic-cert", "generic-key", ""}, - {"generic-mtls", "default", "remote", "generic-mtls-cert", "generic-mtls-key", "generic-mtls-ca"}, - {"generic-mtls-split", "default", "remote", "generic-mtls-split-cert", "generic-mtls-split-key", ""}, - {"generic-mtls-split-cacert", "default", "remote", "", "", "generic-mtls-split-ca"}, - // This is present in local and remote, but with a different value. We have the remote. - {"tls", "default", "remote", "tls-cert-mod", "tls-key", ""}, - {"tls-mtls", "default", "remote", "tls-mtls-cert", "tls-mtls-key", "tls-mtls-ca"}, - {"tls-mtls-split", "default", "remote", "tls-mtls-split-cert", "tls-mtls-split-key", ""}, - {"tls-mtls-split-cacert", "default", "remote", "", "", "tls-mtls-split-ca"}, - {"generic", "wrong-namespace", "remote", "", "", ""}, - - // From other remote cluster - // We have no in cluster credentials; can only access those in config cluster - {"generic", "default", "other", "", "", ""}, - {"generic-mtls", "default", "other", "", "", ""}, - {"generic-mtls-split", "default", "other", "", "", ""}, - {"generic-mtls-split-cacert", "default", "other", "", "", ""}, - {"tls", "default", "other", "tls-cert", "tls-key", ""}, - {"tls-mtls", "default", "other", "tls-mtls-cert", "tls-mtls-key", "tls-mtls-ca"}, - {"tls-mtls-split", "default", "other", "tls-mtls-split-cert", "tls-mtls-split-key", ""}, - {"tls-mtls-split-cacert", "default", "other", "", "", "tls-mtls-split-ca"}, - {"generic", "wrong-namespace", "other", "", "", ""}, - } - for _, tt := range cases { - t.Run(fmt.Sprintf("%s-%v", tt.name, tt.cluster), func(t *testing.T) { - con, err := sc.ForCluster(tt.cluster) - if err != nil { - t.Fatal(err) - } - key, cert, _ := con.GetKeyAndCert(tt.name, tt.namespace) - if tt.key != string(key) { - t.Errorf("got key %q, wanted %q", string(key), tt.key) - } - if tt.cert != string(cert) { - t.Errorf("got cert %q, wanted %q", string(cert), tt.cert) - } - caCert, err := con.GetCaCert(tt.name, tt.namespace) - if tt.caCert != string(caCert) { - t.Errorf("got caCert %q, wanted %q with err %v", string(caCert), tt.caCert, err) - } - }) - } -} diff --git a/pilot/pkg/credentials/model.go b/pilot/pkg/credentials/model.go deleted file mode 100644 index c1604ad1c..000000000 --- a/pilot/pkg/credentials/model.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package credentials - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/cluster" -) - -type Controller interface { - GetKeyAndCert(name, namespace string) (key []byte, cert []byte, err error) - GetCaCert(name, namespace string) (cert []byte, err error) - GetDockerCredential(name, namespace string) (cred []byte, err error) - Authorize(serviceAccount, namespace string) error - AddEventHandler(func(name, namespace string)) -} - -type MulticlusterController interface { - ForCluster(cluster cluster.ID) (Controller, error) -} diff --git a/pilot/pkg/extensionproviders/cluster_lookup.go b/pilot/pkg/extensionproviders/cluster_lookup.go deleted file mode 100644 index bb34b62f3..000000000 --- a/pilot/pkg/extensionproviders/cluster_lookup.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package extensionproviders - -import ( - "fmt" - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" -) - -func LookupCluster(push *model.PushContext, service string, port int) (hostname string, cluster string, err error) { - if service == "" { - err = fmt.Errorf("service must not be empty") - return - } - - // TODO(yangminzhu): Verify the service and its cluster is supported, e.g. resolution type is not OriginalDst. - if parts := strings.Split(service, "/"); len(parts) == 2 { - namespace, name := parts[0], parts[1] - if svc := push.ServiceIndex.HostnameAndNamespace[host.Name(name)][namespace]; svc != nil { - hostname = string(svc.Hostname) - cluster = model.BuildSubsetKey(model.TrafficDirectionOutbound, "", svc.Hostname, port) - return - } - } else { - namespaceToServices := push.ServiceIndex.HostnameAndNamespace[host.Name(service)] - var namespaces []string - for k := range namespaceToServices { - namespaces = append(namespaces, k) - } - // If namespace is omitted, return successfully if there is only one such host name in the service index. - if len(namespaces) == 1 { - svc := namespaceToServices[namespaces[0]] - hostname = string(svc.Hostname) - cluster = model.BuildSubsetKey(model.TrafficDirectionOutbound, "", svc.Hostname, port) - return - } else if len(namespaces) > 1 { - err = fmt.Errorf("found %s in multiple namespaces %v, specify the namespace explicitly in "+ - "the format of /", service, namespaces) - return - } - } - - err = fmt.Errorf("could not find service %s in Istio service registry", service) - return -} diff --git a/pilot/pkg/features/pilot.go b/pilot/pkg/features/pilot.go deleted file mode 100644 index c3392a42b..000000000 --- a/pilot/pkg/features/pilot.go +++ /dev/null @@ -1,694 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package features - -import ( - "strings" - "time" -) - -import ( - "google.golang.org/protobuf/types/known/durationpb" - "istio.io/pkg/env" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/jwt" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -var ( - MaxConcurrentStreams = env.RegisterIntVar( - "ISTIO_GPRC_MAXSTREAMS", - 100000, - "Sets the maximum number of concurrent grpc streams.", - ).Get() - - // MaxRecvMsgSize The max receive buffer size of gRPC received channel of Pilot in bytes. - MaxRecvMsgSize = env.RegisterIntVar( - "ISTIO_GPRC_MAXRECVMSGSIZE", - 4*1024*1024, - "Sets the max receive buffer size of gRPC stream in bytes.", - ).Get() - - traceSamplingVar = env.RegisterFloatVar( - "PILOT_TRACE_SAMPLING", - 1.0, - "Sets the mesh-wide trace sampling percentage. Should be 0.0 - 100.0. Precision to 0.01. "+ - "Default is 1.0.", - ) - - TraceSampling = func() float64 { - f := traceSamplingVar.Get() - if f < 0.0 || f > 100.0 { - log.Warnf("PILOT_TRACE_SAMPLING out of range: %v", f) - return 1.0 - } - return f - }() - - // EnableIstioTags controls whether or not to configure Envoy with support for Istio-specific tags - // in trace spans. This is a temporary flag for controlling the feature that will be replaced by - // Telemetry API (or accepted as an always-on feature). - EnableIstioTags = env.RegisterBoolVar( - "PILOT_ENABLE_ISTIO_TAGS", - true, - "Determines whether or not trace spans generated by Envoy will include Istio-specific tags.", - ).Get() - - PushThrottle = env.RegisterIntVar( - "PILOT_PUSH_THROTTLE", - 100, - "Limits the number of concurrent pushes allowed. On larger machines this can be increased for faster pushes", - ).Get() - - RequestLimit = env.RegisterFloatVar( - "PILOT_MAX_REQUESTS_PER_SECOND", - 25.0, - "Limits the number of incoming XDS requests per second. On larger machines this can be increased to handle more proxies concurrently.", - ).Get() - - // FilterGatewayClusterConfig controls if a subset of clusters(only those required) should be pushed to gateways - // TODO enable by default once https://github.com/istio/istio/issues/28315 is resolved - // Currently this may cause a bug when we go from N clusters -> 0 clusters -> N clusters - FilterGatewayClusterConfig = env.RegisterBoolVar("PILOT_FILTER_GATEWAY_CLUSTER_CONFIG", false, - "If enabled, Pilot will send only clusters that referenced in gateway virtual services attached to gateway").Get() - - DebounceAfter = env.RegisterDurationVar( - "PILOT_DEBOUNCE_AFTER", - 100*time.Millisecond, - "The delay added to config/registry events for debouncing. This will delay the push by "+ - "at least this interval. If no change is detected within this period, the push will happen, "+ - " otherwise we'll keep delaying until things settle, up to a max of PILOT_DEBOUNCE_MAX.", - ).Get() - - DebounceMax = env.RegisterDurationVar( - "PILOT_DEBOUNCE_MAX", - 10*time.Second, - "The maximum amount of time to wait for events while debouncing. If events keep showing up with no breaks "+ - "for this time, we'll trigger a push.", - ).Get() - - EnableEDSDebounce = env.RegisterBoolVar( - "PILOT_ENABLE_EDS_DEBOUNCE", - true, - "If enabled, Pilot will include EDS pushes in the push debouncing, configured by PILOT_DEBOUNCE_AFTER and PILOT_DEBOUNCE_MAX."+ - " EDS pushes may be delayed, but there will be fewer pushes. By default this is enabled", - ).Get() - - SMDebounceAfter = env.RegisterDurationVar( - "PILOT_SM_DEBOUNCE_AFTER", - 100*time.Millisecond, - "The delay added to config/registry events for debouncing. This will delay the push by "+ - "at least this interval. If no change is detected within this period, the push will happen, "+ - " otherwise we'll keep delaying until things settle, up to a max of PILOT_SM_DEBOUNCE_MAX.", - ).Get() - - SMDebounceMax = env.RegisterDurationVar( - "PILOT_SM_DEBOUNCE_MAX", - 1*time.Second, - "The maximum amount of time to wait for events while debouncing. If events keep showing up with no breaks "+ - "for this time, we'll trigger a push.", - ).Get() - - SMEnableDebounce = env.RegisterBoolVar( - "PILOT_SM_ENABLE_DEBOUNCE", - true, - "If enabled, Pilot will include EDS pushes in the push debouncing, configured by PILOT_SM_DEBOUNCE_AFTER and PILOT_SM_DEBOUNCE_MAX."+ - " SNP register may be delayed, but there will be fewer pushes. By default this is enabled", - ).Get() - - SNPDebounceAfter = env.RegisterDurationVar( - "PILOT_SNP_DEBOUNCE_AFTER", - 100*time.Millisecond, - "The delay added to config/registry events for debouncing. This will delay the push by "+ - "at least this interval. If no change is detected within this period, the push will happen, "+ - " otherwise we'll keep delaying until things settle, up to a max of PILOT_SNP_DEBOUNCE_MAX.", - ).Get() - - SNPDebounceMax = env.RegisterDurationVar( - "PILOT_SNP_DEBOUNCE_MAX", - 1*time.Second, - "The maximum amount of time to wait for events while debouncing. If events keep showing up with no breaks "+ - "for this time, we'll trigger a push.", - ).Get() - - SNPEnableDebounce = env.RegisterBoolVar( - "PILOT_SNP_ENABLE_DEBOUNCE", - true, - "If enabled, Pilot will include EDS pushes in the push debouncing, configured by PILOT_SNP_DEBOUNCE_AFTER and PILOT_SNP_DEBOUNCE_MAX."+ - " SNP register may be delayed, but there will be fewer pushes. By default this is enabled", - ).Get() - - SendUnhealthyEndpoints = env.RegisterBoolVar( - "PILOT_SEND_UNHEALTHY_ENDPOINTS", - false, - "If enabled, Pilot will include unhealthy endpoints in EDS pushes and even if they are sent Envoy does not use them for load balancing."+ - " To avoid, sending traffic to non ready endpoints, enabling this flag, disables panic threshold in Envoy i.e. Envoy does not load balance requests"+ - " to unhealthy/non-ready hosts even if the percentage of healthy hosts fall below minimum health percentage(panic threshold).", - ).Get() - - // HTTP10 will add "accept_http_10" to http outbound listeners. Can also be set only for specific sidecars via meta. - HTTP10 = env.RegisterBoolVar( - "PILOT_HTTP10", - false, - "Enables the use of HTTP 1.0 in the outbound HTTP listeners, to support legacy applications.", - ).Get() - - // EnableMysqlFilter enables injection of `envoy.filters.network.mysql_proxy` in the filter chain. - // Pilot injects this outbound filter if the service port name is `mysql`. - EnableMysqlFilter = env.RegisterBoolVar( - "PILOT_ENABLE_MYSQL_FILTER", - false, - "EnableMysqlFilter enables injection of `envoy.filters.network.mysql_proxy` in the filter chain.", - ).Get() - - // EnableRedisFilter enables injection of `envoy.filters.network.redis_proxy` in the filter chain. - // Pilot injects this outbound filter if the service port name is `redis`. - EnableRedisFilter = env.RegisterBoolVar( - "PILOT_ENABLE_REDIS_FILTER", - false, - "EnableRedisFilter enables injection of `envoy.filters.network.redis_proxy` in the filter chain.", - ).Get() - - // EnableMongoFilter enables injection of `envoy.filters.network.mongo_proxy` in the filter chain. - EnableMongoFilter = env.RegisterBoolVar( - "PILOT_ENABLE_MONGO_FILTER", - true, - "EnableMongoFilter enables injection of `envoy.filters.network.mongo_proxy` in the filter chain.", - ).Get() - - // UseRemoteAddress sets useRemoteAddress to true for side car outbound listeners so that it picks up the localhost - // address of the sender, which is an internal address, so that trusted headers are not sanitized. - UseRemoteAddress = env.RegisterBoolVar( - "PILOT_SIDECAR_USE_REMOTE_ADDRESS", - false, - "UseRemoteAddress sets useRemoteAddress to true for side car outbound listeners.", - ).Get() - - // SkipValidateTrustDomain tells the server proxy to not to check the peer's trust domain when - // mTLS is enabled in authentication policy. - SkipValidateTrustDomain = env.RegisterBoolVar( - "PILOT_SKIP_VALIDATE_TRUST_DOMAIN", - false, - "Skip validating the peer is from the same trust domain when mTLS is enabled in authentication policy").Get() - - EnableAutomTLSCheckPolicies = env.RegisterBoolVar( - "ENABLE_AUTO_MTLS_CHECK_POLICIES", true, - "Enable the auto mTLS EDS output to consult the PeerAuthentication Policy, only set the {tlsMode: istio} "+ - " when server side policy enables mTLS PERMISSIVE or STRICT.").Get() - - EnableProtocolSniffingForOutbound = env.RegisterBoolVar( - "PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_OUTBOUND", - true, - "If enabled, protocol sniffing will be used for outbound listeners whose port protocol is not specified or unsupported", - ).Get() - - EnableProtocolSniffingForInbound = env.RegisterBoolVar( - "PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_INBOUND", - true, - "If enabled, protocol sniffing will be used for inbound listeners whose port protocol is not specified or unsupported", - ).Get() - - EnableWasmTelemetry = env.RegisterBoolVar( - "ENABLE_WASM_TELEMETRY", - false, - "If enabled, Wasm-based telemetry will be enabled.", - ).Get() - - ScopeGatewayToNamespace = env.RegisterBoolVar( - "PILOT_SCOPE_GATEWAY_TO_NAMESPACE", - false, - "If enabled, a gateway workload can only select gateway resources in the same namespace. "+ - "Gateways with same selectors in different namespaces will not be applicable.", - ).Get() - - // nolint - InboundProtocolDetectionTimeout, InboundProtocolDetectionTimeoutSet = env.RegisterDurationVar( - "PILOT_INBOUND_PROTOCOL_DETECTION_TIMEOUT", - 1*time.Second, - "Protocol detection timeout for inbound listener", - ).Lookup() - - EnableHeadlessService = env.RegisterBoolVar( - "PILOT_ENABLE_HEADLESS_SERVICE_POD_LISTENERS", - true, - "If enabled, for a headless service/stateful set in Kubernetes, pilot will generate an "+ - "outbound listener for each pod in a headless service. This feature should be disabled "+ - "if headless services have a large number of pods.", - ).Get() - - EnableRemoteJwks = env.RegisterBoolVar( - "PILOT_JWT_ENABLE_REMOTE_JWKS", - false, - "If enabled, checks to see if the configured JwksUri in RequestAuthentication is a mesh cluster URL "+ - "and configures remote Jwks to let Envoy fetch the Jwks instead of Istiod.", - ).Get() - - EnableEDSForHeadless = env.RegisterBoolVar( - "PILOT_ENABLE_EDS_FOR_HEADLESS_SERVICES", - false, - "If enabled, for headless service in Kubernetes, pilot will send endpoints over EDS, "+ - "allowing the sidecar to load balance among pods in the headless service. This feature "+ - "should be enabled if applications access all services explicitly via a HTTP proxy port in the sidecar.", - ).Get() - - EnableDistributionTracking = env.RegisterBoolVar( - "PILOT_ENABLE_CONFIG_DISTRIBUTION_TRACKING", - true, - "If enabled, Pilot will assign meaningful nonces to each Envoy configuration message, and allow "+ - "users to interrogate which envoy has which config from the debug interface.", - ).Get() - - DistributionHistoryRetention = env.RegisterDurationVar( - "PILOT_DISTRIBUTION_HISTORY_RETENTION", - time.Minute*1, - "If enabled, Pilot will keep track of old versions of distributed config for this duration.", - ).Get() - - enableEndpointSliceController, endpointSliceControllerSpecified = env.RegisterBoolVar( - "PILOT_USE_ENDPOINT_SLICE", - false, - "If enabled, Pilot will use EndpointSlices as the source of endpoints for Kubernetes services. "+ - "By default, this is false, and Endpoints will be used. This requires the Kubernetes EndpointSlice controller to be enabled. "+ - "Currently this is mutual exclusive - either Endpoints or EndpointSlices will be used", - ).Lookup() - - MCSAPIGroup = env.RegisterStringVar("MCS_API_GROUP", "multicluster.x-k8s.io", - "The group to be used for the Kubernetes Multi-Cluster Services (MCS) API.").Get() - - MCSAPIVersion = env.RegisterStringVar("MCS_API_VERSION", "v1alpha1", - "The version to be used for the Kubernets Multi-Cluster Services (MCS) API.").Get() - - EnableMCSAutoExport = env.RegisterBoolVar( - "ENABLE_MCS_AUTO_EXPORT", - false, - "If enabled, istiod will automatically generate Kubernetes "+ - "Multi-Cluster Services (MCS) ServiceExport resources for every "+ - "service in the mesh. Services defined to be cluster-local in "+ - "MeshConfig are excluded.", - ).Get() - - EnableMCSServiceDiscovery = env.RegisterBoolVar( - "ENABLE_MCS_SERVICE_DISCOVERY", - false, - "If enabled, istiod will enable Kubernetes Multi-Cluster "+ - "Services (MCS) service discovery mode. In this mode, service "+ - "endpoints in a cluster will only be discoverable within the "+ - "same cluster unless explicitly exported via ServiceExport.").Get() - - EnableMCSHost = env.RegisterBoolVar( - "ENABLE_MCS_HOST", - false, - "If enabled, istiod will configure a Kubernetes Multi-Cluster "+ - "Services (MCS) host (..svc.clusterset.local) "+ - "for each service exported (via ServiceExport) in at least one "+ - "cluster. Clients must, however, be able to successfully lookup "+ - "these DNS hosts. That means that either Istio DNS interception "+ - "must be enabled or an MCS controller must be used. Requires "+ - "that ENABLE_MCS_SERVICE_DISCOVERY also be enabled.").Get() && - EnableMCSServiceDiscovery - - EnableMCSClusterLocal = env.RegisterBoolVar( - "ENABLE_MCS_CLUSTER_LOCAL", - false, - "If enabled, istiod will treat the host "+ - "`..svc.cluster.local` as defined by the "+ - "Kubernetes Multi-Cluster Services (MCS) spec. In this mode, "+ - "requests to `cluster.local` will be routed to only those "+ - "endpoints residing within the same cluster as the client. "+ - "Requires that both ENABLE_MCS_SERVICE_DISCOVERY and "+ - "ENABLE_MCS_HOST also be enabled.").Get() && - EnableMCSHost - - EnableLegacyLBAlgorithmDefault = env.RegisterBoolVar( - "ENABLE_LEGACY_LB_ALGORITHM_DEFAULT", - false, - "If enabled, destinations for which no LB algorithm is specified will use the legacy "+ - "default, ROUND_ROBIN. Care should be taken when using ROUND_ROBIN in general as it can "+ - "overburden endpoints, especially when weights are used.").Get() - - EnableAnalysis = env.RegisterBoolVar( - "PILOT_ENABLE_ANALYSIS", - false, - "If enabled, pilot will run istio analyzers and write analysis errors to the Status field of any "+ - "Istio Resources", - ).Get() - - AnalysisInterval = func() time.Duration { - val, _ := env.RegisterDurationVar( - "PILOT_ANALYSIS_INTERVAL", - 10*time.Second, - "If analysis is enabled, pilot will run istio analyzers using this value as interval in seconds "+ - "Istio Resources", - ).Lookup() - if val < 1*time.Second { - log.Warnf("PILOT_ANALYSIS_INTERVAL %s is too small, it will be set to default 10 seconds", val.String()) - return 10 * time.Second - } - return val - }() - - EnableStatus = env.RegisterBoolVar( - "PILOT_ENABLE_STATUS", - false, - "If enabled, pilot will update the CRD Status field of all istio resources with reconciliation status.", - ).Get() - - StatusUpdateInterval = env.RegisterDurationVar( - "PILOT_STATUS_UPDATE_INTERVAL", - 500*time.Millisecond, - "Interval to update the XDS distribution status.", - ).Get() - - StatusQPS = env.RegisterFloatVar( - "PILOT_STATUS_QPS", - 100, - "If status is enabled, controls the QPS with which status will be updated. "+ - "See https://godoc.org/k8s.io/client-go/rest#Config QPS", - ).Get() - - StatusBurst = env.RegisterIntVar( - "PILOT_STATUS_BURST", - 500, - "If status is enabled, controls the Burst rate with which status will be updated. "+ - "See https://godoc.org/k8s.io/client-go/rest#Config Burst", - ).Get() - - StatusMaxWorkers = env.RegisterIntVar("PILOT_STATUS_MAX_WORKERS", 100, "The maximum number of workers"+ - " Pilot will use to keep configuration status up to date. Smaller numbers will result in higher status latency, "+ - "but larger numbers may impact CPU in high scale environments.").Get() - - // IstiodServiceCustomHost allow user to bring a custom address or multiple custom addresses for istiod server - // for examples: 1. istiod.mycompany.com 2. istiod.mycompany.com,istiod-canary.mycompany.com - IstiodServiceCustomHost = env.RegisterStringVar("ISTIOD_CUSTOM_HOST", "", - "Custom host name of istiod that istiod signs the server cert. "+ - "Multiple custom host names are supported, and multiple values are separated by commas.").Get() - - PilotCertProvider = env.RegisterStringVar("PILOT_CERT_PROVIDER", constants.CertProviderIstiod, - "The provider of Pilot DNS certificate.").Get() - - JwtPolicy = env.RegisterStringVar("JWT_POLICY", jwt.PolicyThirdParty, - "The JWT validation policy.").Get() - - // Default request timeout for virtual services if a timeout is not configured in virtual service. It defaults to zero - // which disables timeout when it is not configured, to preserve the current behavior. - defaultRequestTimeoutVar = env.RegisterDurationVar( - "ISTIO_DEFAULT_REQUEST_TIMEOUT", - 0*time.Millisecond, - "Default Http and gRPC Request timeout", - ) - - DefaultRequestTimeout = func() *durationpb.Duration { - return durationpb.New(defaultRequestTimeoutVar.Get()) - }() - - LegacyIngressBehavior = env.RegisterBoolVar("PILOT_LEGACY_INGRESS_BEHAVIOR", false, - "If this is set to true, istio ingress will perform the legacy behavior, "+ - "which does not meet https://kubernetes.io/docs/concepts/services-networking/ingress/#multiple-matches.").Get() - - EnableGatewayAPI = env.RegisterBoolVar("PILOT_ENABLE_GATEWAY_API", true, - "If this is set to true, support for Kubernetes gateway-api (github.com/kubernetes-sigs/gateway-api) will "+ - " be enabled. In addition to this being enabled, the gateway-api CRDs need to be installed.").Get() - - EnableGatewayAPIStatus = env.RegisterBoolVar("PILOT_ENABLE_GATEWAY_API_STATUS", true, - "If this is set to true, gateway-api resources will have status written to them").Get() - - EnableGatewayAPIDeploymentController = env.RegisterBoolVar("PILOT_ENABLE_GATEWAY_API_DEPLOYMENT_CONTROLLER", true, - "If this is set to true, gateway-api resources will automatically provision in cluster deployment, services, etc").Get() - - ClusterName = env.RegisterStringVar("CLUSTER_ID", "Kubernetes", - "Defines the cluster and service registry that this Istiod instance is belongs to").Get() - - ExternalIstiod = env.RegisterBoolVar("EXTERNAL_ISTIOD", false, - "If this is set to true, one Istiod will control remote clusters including CA.").Get() - - EnableCAServer = env.RegisterBoolVar("ENABLE_CA_SERVER", true, - "If this is set to false, will not create CA server in istiod.").Get() - - EnableDebugOnHTTP = env.RegisterBoolVar("ENABLE_DEBUG_ON_HTTP", true, - "If this is set to false, the debug interface will not be enabled, recommended for production").Get() - - EnableUnsafeAdminEndpoints = env.RegisterBoolVar("UNSAFE_ENABLE_ADMIN_ENDPOINTS", false, - "If this is set to true, dangerous admin endpoints will be exposed on the debug interface. Not recommended for production.").Get() - - XDSAuth = env.RegisterBoolVar("XDS_AUTH", true, - "If true, will authenticate XDS clients.").Get() - - EnableXDSIdentityCheck = env.RegisterBoolVar( - "PILOT_ENABLE_XDS_IDENTITY_CHECK", - true, - "If enabled, pilot will authorize XDS clients, to ensure they are acting only as namespaces they have permissions for.", - ).Get() - - EnableServiceEntrySelectPods = env.RegisterBoolVar("PILOT_ENABLE_SERVICEENTRY_SELECT_PODS", true, - "If enabled, service entries with selectors will select pods from the cluster. "+ - "It is safe to disable it if you are quite sure you don't need this feature").Get() - - InjectionWebhookConfigName = env.RegisterStringVar("INJECTION_WEBHOOK_CONFIG_NAME", "istio-sidecar-injector", - "Name of the mutatingwebhookconfiguration to patch, if istioctl is not used.").Get() - - ValidationWebhookConfigName = env.RegisterStringVar("VALIDATION_WEBHOOK_CONFIG_NAME", "istio-dubbo-system", - "Name of the validatingwebhookconfiguration to patch. Empty will skip using cluster admin to patch.").Get() - - SpiffeBundleEndpoints = env.RegisterStringVar("SPIFFE_BUNDLE_ENDPOINTS", "", - "The SPIFFE bundle trust domain to endpoint mappings. Istiod retrieves the root certificate from each SPIFFE "+ - "bundle endpoint and uses it to verify client certifiates from that trust domain. The endpoint must be "+ - "compliant to the SPIFFE Bundle Endpoint standard. For details, please refer to "+ - "https://github.com/spiffe/spiffe/blob/master/standards/SPIFFE_Trust_Domain_and_Bundle.md . "+ - "No need to configure this for root certificates issued via Istiod or web-PKI based root certificates. "+ - "Use || between tuples. Use | as delimiter between trust domain and endpoint in "+ - "each tuple. For example: foo|https://url/for/foo||bar|https://url/for/bar").Get() - - EnableXDSCaching = env.RegisterBoolVar("PILOT_ENABLE_XDS_CACHE", true, - "If true, Pilot will cache XDS responses.").Get() - - // EnableCDSCaching determines if CDS caching is enabled. This is explicitly split out of ENABLE_XDS_CACHE, - // so that in case there are issues with the CDS cache we can just disable the CDS cache. - EnableCDSCaching = env.RegisterBoolVar("PILOT_ENABLE_CDS_CACHE", true, - "If true, Pilot will cache CDS responses. Note: this depends on PILOT_ENABLE_XDS_CACHE.").Get() - - // EnableRDSCaching determines if RDS caching is enabled. This is explicitly split out of ENABLE_XDS_CACHE, - // so that in case there are issues with the RDS cache we can just disable the RDS cache. - EnableRDSCaching = env.RegisterBoolVar("PILOT_ENABLE_RDS_CACHE", true, - "If true, Pilot will cache RDS responses. Note: this depends on PILOT_ENABLE_XDS_CACHE.").Get() - - EnableXDSCacheMetrics = env.RegisterBoolVar("PILOT_XDS_CACHE_STATS", false, - "If true, Pilot will collect metrics for XDS cache efficiency.").Get() - - XDSCacheMaxSize = env.RegisterIntVar("PILOT_XDS_CACHE_SIZE", 60000, - "The maximum number of cache entries for the XDS cache.").Get() - - // EnableLegacyFSGroupInjection has first-party-jwt as allowed because we only - // need the fsGroup configuration for the projected service account volume mount, - // which is only used by first-party-jwt. The installer will automatically - // configure this on Kubernetes 1.19+. - // Note: while this appears unused in the go code, this sets a default which is used in the injection template. - EnableLegacyFSGroupInjection = env.RegisterBoolVar("ENABLE_LEGACY_FSGROUP_INJECTION", JwtPolicy != jwt.PolicyFirstParty, - "If true, Istiod will set the pod fsGroup to 1337 on injection. This is required for Kubernetes 1.18 and older "+ - `(see https://github.com/kubernetes/kubernetes/issues/57923 for details) unless JWT_POLICY is "first-party-jwt".`).Get() - - XdsPushSendTimeout = env.RegisterDurationVar( - "PILOT_XDS_SEND_TIMEOUT", - 0*time.Second, - "The timeout to send the XDS configuration to proxies. After this timeout is reached, Pilot will discard that push.", - ).Get() - - RemoteClusterTimeout = env.RegisterDurationVar( - "PILOT_REMOTE_CLUSTER_TIMEOUT", - 30*time.Second, - "After this timeout expires, pilot can become ready without syncing data from clusters added via remote-secrets. "+ - "Setting the timeout to 0 disables this behavior.", - ).Get() - - EnableTelemetryLabel = env.RegisterBoolVar("PILOT_ENABLE_TELEMETRY_LABEL", true, - "If true, pilot will add telemetry related metadata to cluster and endpoint resources, which will be consumed by telemetry filter.", - ).Get() - - EndpointTelemetryLabel = env.RegisterBoolVar("PILOT_ENDPOINT_TELEMETRY_LABEL", true, - "If true, pilot will add telemetry related metadata to Endpoint resource, which will be consumed by telemetry filter.", - ).Get() - - MetadataExchange = env.RegisterBoolVar("PILOT_ENABLE_METADATA_EXCHANGE", true, - "If true, pilot will add metadata exchange filters, which will be consumed by telemetry filter.", - ).Get() - - ALPNFilter = env.RegisterBoolVar("PILOT_ENABLE_ALPN_FILTER", true, - "If true, pilot will add Istio ALPN filters, required for proper protocol sniffing.", - ).Get() - - WorkloadEntryAutoRegistration = env.RegisterBoolVar("PILOT_ENABLE_WORKLOAD_ENTRY_AUTOREGISTRATION", true, - "Enables auto-registering WorkloadEntries based on associated WorkloadGroups upon XDS connection by the workload.").Get() - - WorkloadEntryCleanupGracePeriod = env.RegisterDurationVar("PILOT_WORKLOAD_ENTRY_GRACE_PERIOD", 10*time.Second, - "The amount of time an auto-registered workload can remain disconnected from all Pilot instances before the "+ - "associated WorkloadEntry is cleaned up.").Get() - - WorkloadEntryHealthChecks = env.RegisterBoolVar("PILOT_ENABLE_WORKLOAD_ENTRY_HEALTHCHECKS", true, - "Enables automatic health checks of WorkloadEntries based on the config provided in the associated WorkloadGroup").Get() - - WorkloadEntryCrossCluster = env.RegisterBoolVar("PILOT_ENABLE_CROSS_CLUSTER_WORKLOAD_ENTRY", true, - "If enabled, pilot will read WorkloadEntry from other clusters, selectable by Services in that cluster.").Get() - - FlowControlTimeout = env.RegisterDurationVar( - "PILOT_FLOW_CONTROL_TIMEOUT", - 15*time.Second, - "If set, the max amount of time to delay a push by. Depends on PILOT_ENABLE_FLOW_CONTROL.", - ).Get() - - EnableDestinationRuleInheritance = env.RegisterBoolVar( - "PILOT_ENABLE_DESTINATION_RULE_INHERITANCE", - false, - "If set, workload specific DestinationRules will inherit configurations settings from mesh and namespace level rules", - ).Get() - - WasmRemoteLoadConversion = env.RegisterBoolVar("ISTIO_AGENT_ENABLE_WASM_REMOTE_LOAD_CONVERSION", true, - "If enabled, Istio agent will intercept ECDS resource update, downloads Wasm module, "+ - "and replaces Wasm module remote load with downloaded local module file.").Get() - - PilotJwtPubKeyRefreshInterval = env.RegisterDurationVar( - "PILOT_JWT_PUB_KEY_REFRESH_INTERVAL", - 20*time.Minute, - "The interval for istiod to fetch the jwks_uri for the jwks public key.", - ).Get() - - EnableInboundPassthrough = env.RegisterBoolVar( - "PILOT_ENABLE_INBOUND_PASSTHROUGH", - true, - "If enabled, inbound clusters will be configured as ORIGINAL_DST clusters. When disabled, "+ - "requests are always sent to localhost. The primary implication of this is that when enabled, binding to POD_IP "+ - "will work while localhost will not; when disable, bind to POD_IP will not work, while localhost will. "+ - "The enabled behavior matches the behavior without Istio enabled at all; this flag exists only for backwards compatibility. "+ - "Regardless of this setting, the configuration can be overridden with the Sidecar.Ingress.DefaultEndpoint configuration.", - ).Get() - - StripHostPort = env.RegisterBoolVar("ISTIO_GATEWAY_STRIP_HOST_PORT", false, - "If enabled, Gateway will remove any port from host/authority header "+ - "before any processing of request by HTTP filters or routing.").Get() - - // EnableUnsafeAssertions enables runtime checks to test assertions in our code. This should never be enabled in - // production; when assertions fail Istio will panic. - EnableUnsafeAssertions = env.RegisterBoolVar( - "UNSAFE_PILOT_ENABLE_RUNTIME_ASSERTIONS", - false, - "If enabled, addition runtime asserts will be performed. "+ - "These checks are both expensive and panic on failure. As a result, this should be used only for testing.", - ).Get() - - // EnableUnsafeDeltaTest enables runtime checks to test Delta XDS efficiency. This should never be enabled in - // production. - EnableUnsafeDeltaTest = env.RegisterBoolVar( - "UNSAFE_PILOT_ENABLE_DELTA_TEST", - false, - "If enabled, addition runtime tests for Delta XDS efficiency are added. "+ - "These checks are extremely expensive, so this should be used only for testing, not production.", - ).Get() - - DeltaXds = env.RegisterBoolVar("ISTIO_DELTA_XDS", false, - "If enabled, pilot will only send the delta configs as opposed to the state of the world on a "+ - "Resource Request. This feature uses the delta xds api, but does not currently send the actual deltas.").Get() - - PartialFullPushes = env.RegisterBoolVar("PILOT_PARTIAL_FULL_PUSHES", false, - "If enabled, pilot will send partial pushes in for child resources (RDS, EDS, etc) when possible. "+ - "This occurs for EDS in many cases regardless of this setting.").Get() - - EnableLegacyIstioMutualCredentialName = env.RegisterBoolVar("PILOT_ENABLE_LEGACY_ISTIO_MUTUAL_CREDENTIAL_NAME", - false, - "If enabled, Gateway's with ISTIO_MUTUAL mode and credentialName configured will use simple TLS. "+ - "This is to retain legacy behavior only and not recommended for use beyond migration.").Get() - - EnableLegacyAutoPassthrough = env.RegisterBoolVar( - "PILOT_ENABLE_LEGACY_AUTO_PASSTHROUGH", - false, - "If enabled, pilot will allow any upstream cluster to be used with AUTO_PASSTHROUGH. "+ - "This option is intended for backwards compatibility only and is not secure with untrusted downstreams; it will be removed in the future.").Get() - - SharedMeshConfig = env.RegisterStringVar("SHARED_MESH_CONFIG", "", - "Additional config map to load for shared MeshConfig settings. The standard mesh config will take precedence.").Get() - - MultiRootMesh = env.RegisterBoolVar("ISTIO_MULTIROOT_MESH", false, - "If enabled, mesh will support certificates signed by more than one trustAnchor for ISTIO_MUTUAL mTLS").Get() - - EnableEnvoyFilterMetrics = env.RegisterBoolVar("PILOT_ENVOY_FILTER_STATS", false, - "If true, Pilot will collect metrics for envoy filter operations.").Get() - - EnableRouteCollapse = env.RegisterBoolVar("PILOT_ENABLE_ROUTE_COLLAPSE_OPTIMIZATION", true, - "If true, Pilot will merge virtual hosts with the same routes into a single virtual host, as an optimization.").Get() - - MulticlusterHeadlessEnabled = env.RegisterBoolVar("ENABLE_MULTICLUSTER_HEADLESS", true, - "If true, the DNS name table for a headless service will resolve to same-network endpoints in any cluster.").Get() - - ResolveHostnameGateways = env.RegisterBoolVar("RESOLVE_HOSTNAME_GATEWAYS", true, - "If true, hostnames in the LoadBalancer addresses of a Service will be resolved at the control plane for use in cross-network gateways.").Get() - - CertSignerDomain = env.RegisterStringVar("CERT_SIGNER_DOMAIN", "", "The cert signer domain info").Get() - - AutoReloadPluginCerts = env.RegisterBoolVar( - "AUTO_RELOAD_PLUGIN_CERTS", - false, - "If enabled, if user introduces new intermediate plug-in CA, user need not to restart istiod to pick up certs."+ - "Istiod picks newly added intermediate plug-in CA certs and updates it. Plug-in new Root-CA not supported.").Get() - - RewriteTCPProbes = env.RegisterBoolVar( - "REWRITE_TCP_PROBES", - true, - "If false, TCP probes will not be rewritten and therefor always succeed when a sidecar is used.", - ).Get() - - EnableQUICListeners = env.RegisterBoolVar("PILOT_ENABLE_QUIC_LISTENERS", false, - "If true, QUIC listeners will be generated wherever there are listeners terminating TLS on gateways "+ - "if the gateway service exposes a UDP port with the same number (for example 443/TCP and 443/UDP)").Get() - - VerifyCertAtClient = env.RegisterBoolVar("VERIFY_CERTIFICATE_AT_CLIENT", false, - "If enabled, certificates received by the proxy will be verified against the OS CA certificate bundle.").Get() - - PrioritizedLeaderElection = env.RegisterBoolVar("PRIORITIZED_LEADER_ELECTION", true, - "If enabled, the default revision will steal leader locks from non-default revisions").Get() - - EnableTLSOnSidecarIngress = env.RegisterBoolVar("ENABLE_TLS_ON_SIDECAR_INGRESS", false, - "If enabled, the TLS configuration on Sidecar.ingress will take effect").Get() - - EnableAutoSni = env.RegisterBoolVar("ENABLE_AUTO_SNI", false, - "If enabled, automatically set SNI when `DestinationRules` do not specify the same").Get() - - InsecureKubeConfigOptions = func() sets.Set { - v := env.RegisterStringVar( - "PILOT_INSECURE_MULTICLUSTER_KUBECONFIG_OPTIONS", - "", - "Comma separated list of potentially insecure kubeconfig authentication options that are allowed for multicluster authentication."+ - "Support values: all authProviders (`gcp`, `azure`, `exec`, `openstack`), "+ - "`clientKey`, `clientCertificate`, `tokenFile`, and `exec`.").Get() - return sets.New(strings.Split(v, ",")...) - }() - - VerifySDSCertificate = env.RegisterBoolVar("VERIFY_SDS_CERTIFICATE", true, - "If enabled, certificates fetched from SDS server will be verified before sending back to proxy.").Get() - - CanonicalServiceForMeshExternalServiceEntry = env.RegisterBoolVar("LABEL_CANONICAL_SERVICES_FOR_MESH_EXTERNAL_SERVICE_ENTRIES", false, - "If enabled, metadata representing canonical services for ServiceEntry resources with a location of mesh_external will be populated"+ - "in the cluster metadata for those endpoints.").Get() -) - -// EnableEndpointSliceController returns the value of the feature flag and whether it was actually specified. -func EnableEndpointSliceController() (value bool, ok bool) { - return enableEndpointSliceController, endpointSliceControllerSpecified -} - -// UnsafeFeaturesEnabled returns true if any unsafe features are enabled. -func UnsafeFeaturesEnabled() bool { - return EnableUnsafeAdminEndpoints || EnableUnsafeAssertions -} diff --git a/pilot/pkg/grpc/grpc.go b/pilot/pkg/grpc/grpc.go deleted file mode 100644 index 758761bc4..000000000 --- a/pilot/pkg/grpc/grpc.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package grpc - -import ( - "context" - "io" - "strings" -) - -import ( - middleware "github.com/grpc-ecosystem/go-grpc-middleware" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/keepalive" - "google.golang.org/grpc/status" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - istiokeepalive "github.com/apache/dubbo-go-pixiu/pkg/keepalive" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -type SendHandler func() error - -// Send with timeout if specified. If timeout is zero, sends without timeout. -func Send(ctx context.Context, send SendHandler) error { - if features.XdsPushSendTimeout.Nanoseconds() > 0 { - errChan := make(chan error, 1) - timeoutCtx, cancel := context.WithTimeout(ctx, features.XdsPushSendTimeout) - defer cancel() - go func() { - err := send() - errChan <- err - close(errChan) - }() - select { - case <-timeoutCtx.Done(): - return status.Errorf(codes.DeadlineExceeded, "timeout sending") - case err := <-errChan: - return err - } - } - err := send() - return err -} - -func ServerOptions(options *istiokeepalive.Options, interceptors ...grpc.UnaryServerInterceptor) []grpc.ServerOption { - maxStreams := features.MaxConcurrentStreams - maxRecvMsgSize := features.MaxRecvMsgSize - - grpcOptions := []grpc.ServerOption{ - grpc.UnaryInterceptor(middleware.ChainUnaryServer(interceptors...)), - grpc.MaxConcurrentStreams(uint32(maxStreams)), - grpc.MaxRecvMsgSize(maxRecvMsgSize), - // Ensure we allow clients sufficient ability to send keep alives. If this is higher than client - // keep alive setting, it will prematurely get a GOAWAY sent. - grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{ - MinTime: options.Time / 2, - }), - grpc.KeepaliveParams(keepalive.ServerParameters{ - Time: options.Time, - Timeout: options.Timeout, - MaxConnectionAge: options.MaxServerConnectionAge, - MaxConnectionAgeGrace: options.MaxServerConnectionAgeGrace, - }), - } - - return grpcOptions -} - -var expectedGrpcFailureMessages = sets.New( - "client disconnected", - "error reading from server: EOF", - "transport is closing", -) - -func containsExpectedMessage(msg string) bool { - for m := range expectedGrpcFailureMessages { - if strings.Contains(msg, m) { - return true - } - } - return false -} - -// IsExpectedGRPCError checks a gRPC error code and determines whether it is an expected error when -// things are operating normally. This is basically capturing when the client disconnects. -func IsExpectedGRPCError(err error) bool { - if err == io.EOF { - return true - } - - if s, ok := status.FromError(err); ok { - if s.Code() == codes.Canceled || s.Code() == codes.DeadlineExceeded { - return true - } - if s.Code() == codes.Unavailable && containsExpectedMessage(s.Message()) { - return true - } - } - // If this is not a gRPCStatus we should just error message. - if strings.Contains(err.Error(), "stream terminated by RST_STREAM with error code: NO_ERROR") { - return true - } - - return false -} diff --git a/pilot/pkg/grpc/grpc_test.go b/pilot/pkg/grpc/grpc_test.go deleted file mode 100644 index af2070d45..000000000 --- a/pilot/pkg/grpc/grpc_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package grpc - -import ( - "errors" - "testing" -) - -func TestIsExpectedGRPCError(t *testing.T) { - err := errors.New("code = Internal desc = stream terminated by RST_STREAM with error code: NO_ERROR") - if got := IsExpectedGRPCError(err); !got { - t.Fatalf("expected true, got %v", got) - } -} diff --git a/pilot/pkg/grpc/leak_test.go b/pilot/pkg/grpc/leak_test.go deleted file mode 100644 index baa1bfba5..000000000 --- a/pilot/pkg/grpc/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package grpc - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/keycertbundle/watcher.go b/pilot/pkg/keycertbundle/watcher.go deleted file mode 100644 index b566c4472..000000000 --- a/pilot/pkg/keycertbundle/watcher.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package keycertbundle - -import ( - "os" - "sync" -) - -import ( - "go.uber.org/atomic" -) - -// KeyCertBundle stores the cert, private key and root cert for istiod. -type KeyCertBundle struct { - CertPem []byte - KeyPem []byte - CABundle []byte -} - -type Watcher struct { - // Indicated whether bundle has been set, it is used to invoke watcher for the first time. - initDone atomic.Bool - mutex sync.Mutex - bundle KeyCertBundle - watcherID int32 - watchers map[int32]chan struct{} -} - -func NewWatcher() *Watcher { - return &Watcher{ - watchers: make(map[int32]chan struct{}), - } -} - -// AddWatcher returns channel to receive the updated items. -func (w *Watcher) AddWatcher() (int32, chan struct{}) { - ch := make(chan struct{}, 1) - w.mutex.Lock() - defer w.mutex.Unlock() - id := w.watcherID - w.watchers[id] = ch - w.watcherID++ - - return id, ch -} - -// RemoveWatcher removes the given watcher. -func (w *Watcher) RemoveWatcher(id int32) { - w.mutex.Lock() - defer w.mutex.Unlock() - ch := w.watchers[id] - if ch != nil { - close(ch) - } - delete(w.watchers, id) -} - -// SetAndNotify sets the key cert and root cert and notify the watchers. -func (w *Watcher) SetAndNotify(key, cert, caBundle []byte) { - w.mutex.Lock() - defer w.mutex.Unlock() - if len(key) != 0 { - w.bundle.KeyPem = key - } - if len(cert) != 0 { - w.bundle.CertPem = cert - } - if len(caBundle) != 0 { - w.bundle.CABundle = caBundle - } - w.initDone.Store(true) - for _, ch := range w.watchers { - select { - case ch <- struct{}{}: - default: - } - } -} - -// SetFromFilesAndNotify sets the key cert and root cert from files and notify the watchers. -func (w *Watcher) SetFromFilesAndNotify(keyFile, certFile, rootCert string) error { - cert, err := os.ReadFile(certFile) - if err != nil { - return err - } - key, err := os.ReadFile(keyFile) - if err != nil { - return err - } - caBundle, err := os.ReadFile(rootCert) - if err != nil { - return err - } - w.mutex.Lock() - defer w.mutex.Unlock() - if len(key) != 0 { - w.bundle.KeyPem = key - } - if len(cert) != 0 { - w.bundle.CertPem = cert - } - if len(caBundle) != 0 { - w.bundle.CABundle = caBundle - } - w.initDone.Store(true) - for _, ch := range w.watchers { - select { - case ch <- struct{}{}: - default: - } - } - return nil -} - -// GetCABundle returns the CABundle. -func (w *Watcher) GetCABundle() []byte { - w.mutex.Lock() - defer w.mutex.Unlock() - return w.bundle.CABundle -} - -// GetCABundle returns the CABundle. -func (w *Watcher) GetKeyCertBundle() KeyCertBundle { - w.mutex.Lock() - defer w.mutex.Unlock() - return w.bundle -} diff --git a/pilot/pkg/keycertbundle/watcher_test.go b/pilot/pkg/keycertbundle/watcher_test.go deleted file mode 100644 index 24459a476..000000000 --- a/pilot/pkg/keycertbundle/watcher_test.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package keycertbundle - -import ( - "bytes" - "os" - "path" - "testing" -) - -func TestWatcher(t *testing.T) { - watcher := NewWatcher() - - // 1. no key cert bundle - _, watch1 := watcher.AddWatcher() - select { - case bundle := <-watch1: - t.Errorf("watched unexpected keyCertBundle: %v", bundle) - return - default: - } - - key := []byte("key") - cert := []byte("cert") - ca := []byte("caBundle") - // 2. set key cert bundle - watcher.SetAndNotify(key, cert, ca) - select { - case <-watch1: - keyCertBundle := watcher.GetKeyCertBundle() - if !bytes.Equal(keyCertBundle.KeyPem, key) || !bytes.Equal(keyCertBundle.CertPem, cert) || - !bytes.Equal(keyCertBundle.CABundle, ca) { - t.Errorf("got wrong keyCertBundle %v", keyCertBundle) - } - default: - t.Errorf("watched non keyCertBundle") - } - - // 3. set new key cert bundle, notify all watchers - _, watch2 := watcher.AddWatcher() - - key = []byte("key2") - cert = []byte("cert2") - ca = []byte("caBundle2") - watcher.SetAndNotify(key, cert, ca) - select { - case <-watch1: - keyCertBundle := watcher.GetKeyCertBundle() - if !bytes.Equal(keyCertBundle.KeyPem, key) || !bytes.Equal(keyCertBundle.CertPem, cert) || - !bytes.Equal(keyCertBundle.CABundle, ca) { - t.Errorf("got wrong keyCertBundle %v", keyCertBundle) - } - default: - t.Errorf("watcher1 watched non keyCertBundle") - } - select { - case <-watch2: - keyCertBundle := watcher.GetKeyCertBundle() - if !bytes.Equal(keyCertBundle.KeyPem, key) || !bytes.Equal(keyCertBundle.CertPem, cert) || - !bytes.Equal(keyCertBundle.CABundle, ca) { - t.Errorf("got wrong keyCertBundle %v", keyCertBundle) - } - default: - t.Errorf("watcher2 watched non keyCertBundle") - } -} - -func TestWatcherFromFile(t *testing.T) { - watcher := NewWatcher() - - // 1. no key cert bundle - _, watch1 := watcher.AddWatcher() - select { - case bundle := <-watch1: - t.Errorf("watched unexpected keyCertBundle: %v", bundle) - return - default: - } - - tmpDir := t.TempDir() - - key := []byte("key") - cert := []byte("cert") - ca := []byte("caBundle") - keyFile := path.Join(tmpDir, "key.pem") - certFile := path.Join(tmpDir, "cert.pem") - caFile := path.Join(tmpDir, "ca.pem") - - os.WriteFile(keyFile, key, os.ModePerm) - os.WriteFile(certFile, cert, os.ModePerm) - os.WriteFile(caFile, ca, os.ModePerm) - - // 2. set key cert bundle - watcher.SetFromFilesAndNotify(keyFile, certFile, caFile) - select { - case <-watch1: - keyCertBundle := watcher.GetKeyCertBundle() - if !bytes.Equal(keyCertBundle.KeyPem, key) || !bytes.Equal(keyCertBundle.CertPem, cert) || - !bytes.Equal(keyCertBundle.CABundle, ca) { - t.Errorf("got wrong keyCertBundle %v", keyCertBundle) - } - default: - t.Errorf("watched non keyCertBundle") - } -} diff --git a/pilot/pkg/leaderelection/k8sleaderelection/README.md b/pilot/pkg/leaderelection/k8sleaderelection/README.md deleted file mode 100644 index 117efe05a..000000000 --- a/pilot/pkg/leaderelection/k8sleaderelection/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# k8sleaderelection - -This directory is a fork of the [Kubernetes leader election package](https://github.com/kubernetes/kubernetes/tree/67fa728bcf795e864ef5a63fa4144ae044967581/staging/src/k8s.io/client-go/tools/leaderelection) that includes [prioritized leader election](https://github.com/kubernetes/kubernetes/pull/103442). These changes are required in order to make it so that the default revision wins leader elections and steals locks from non-default revisions. The changes were not upstreamed to k8s in time for the Istio 1.12 release. - -Ideally, prioritized leader election gets upstreamed in the near future ([KEP](https://github.com/kubernetes/enhancements/pull/2836)), and hopefully what gets merged isn't too far from what we have forked here (if it is, we'd have to potentially maintain migration code in our fork for a few releases before abandoning). Once merged we can abandon this forked code and use upstream again. diff --git a/pilot/pkg/leaderelection/k8sleaderelection/healthzadaptor.go b/pilot/pkg/leaderelection/k8sleaderelection/healthzadaptor.go deleted file mode 100644 index 777df8f50..000000000 --- a/pilot/pkg/leaderelection/k8sleaderelection/healthzadaptor.go +++ /dev/null @@ -1,69 +0,0 @@ -/* -Copyright 2015 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package k8sleaderelection - -import ( - "net/http" - "sync" - "time" -) - -// HealthzAdaptor associates the /healthz endpoint with the LeaderElection object. -// It helps deal with the /healthz endpoint being set up prior to the LeaderElection. -// This contains the code needed to act as an adaptor between the leader -// election code the health check code. It allows us to provide health -// status about the leader election. Most specifically about if the leader -// has failed to renew without exiting the process. In that case we should -// report not healthy and rely on the kubelet to take down the process. -type HealthzAdaptor struct { - pointerLock sync.Mutex - le *LeaderElector - timeout time.Duration -} - -// Name returns the name of the health check we are implementing. -func (l *HealthzAdaptor) Name() string { - return "leaderElection" -} - -// Check is called by the healthz endpoint handler. -// It fails (returns an error) if we own the lease but had not been able to renew it. -func (l *HealthzAdaptor) Check(req *http.Request) error { - l.pointerLock.Lock() - defer l.pointerLock.Unlock() - if l.le == nil { - return nil - } - return l.le.Check(l.timeout) -} - -// SetLeaderElection ties a leader election object to a HealthzAdaptor -func (l *HealthzAdaptor) SetLeaderElection(le *LeaderElector) { - l.pointerLock.Lock() - defer l.pointerLock.Unlock() - l.le = le -} - -// NewLeaderHealthzAdaptor creates a basic healthz adaptor to monitor a leader election. -// timeout determines the time beyond the lease expiry to be allowed for timeout. -// checks within the timeout period after the lease expires will still return healthy. -func NewLeaderHealthzAdaptor(timeout time.Duration) *HealthzAdaptor { - result := &HealthzAdaptor{ - timeout: timeout, - } - return result -} diff --git a/pilot/pkg/leaderelection/k8sleaderelection/healthzadaptor_test.go b/pilot/pkg/leaderelection/k8sleaderelection/healthzadaptor_test.go deleted file mode 100644 index a938ed208..000000000 --- a/pilot/pkg/leaderelection/k8sleaderelection/healthzadaptor_test.go +++ /dev/null @@ -1,187 +0,0 @@ -/* -Copyright 2015 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package k8sleaderelection - -import ( - "context" - "fmt" - "net/http" - "testing" - "time" -) - -import ( - clock "k8s.io/utils/clock/testing" -) - -import ( - rl "github.com/apache/dubbo-go-pixiu/pilot/pkg/leaderelection/k8sleaderelection/k8sresourcelock" -) - -type fakeLock struct { - identity string - key string -} - -// Get is a dummy to allow us to have a fakeLock for testing. -func (fl *fakeLock) Get(ctx context.Context) (ler *rl.LeaderElectionRecord, rawRecord []byte, err error) { - return nil, nil, nil -} - -// Create is a dummy to allow us to have a fakeLock for testing. -func (fl *fakeLock) Create(ctx context.Context, ler rl.LeaderElectionRecord) error { - return nil -} - -// Update is a dummy to allow us to have a fakeLock for testing. -func (fl *fakeLock) Update(ctx context.Context, ler rl.LeaderElectionRecord) error { - return nil -} - -// RecordEvent is a dummy to allow us to have a fakeLock for testing. -func (fl *fakeLock) RecordEvent(string) {} - -// Identity is a dummy to allow us to have a fakeLock for testing. -func (fl *fakeLock) Identity() string { - return fl.identity -} - -// Key is a dummy to allow us to have a fakeLock for testing. -func (fl *fakeLock) Key() string { - return fl.key -} - -// Describe is a dummy to allow us to have a fakeLock for testing. -func (fl *fakeLock) Describe() string { - return "Dummy implementation of lock for testing" -} - -// TestLeaderElectionHealthChecker tests that the healthcheck for leader election handles its edge cases. -func TestLeaderElectionHealthChecker(t *testing.T) { - current := time.Now() - req := &http.Request{} - - tests := []struct { - description string - expected error - adaptorTimeout time.Duration - elector *LeaderElector - }{ - { - description: "call check before leader elector initialized", - expected: nil, - adaptorTimeout: time.Second * 20, - elector: nil, - }, - { - description: "call check when the lease is far expired", - expected: fmt.Errorf("failed election to renew leadership on lease %s", "foo"), - adaptorTimeout: time.Second * 20, - elector: &LeaderElector{ - config: LeaderElectionConfig{ - Lock: &fakeLock{identity: "healthTest"}, - LeaseDuration: time.Minute, - Name: "foo", - }, - observedRecord: rl.LeaderElectionRecord{ - HolderIdentity: "healthTest", - }, - observedTime: current, - clock: clock.NewFakeClock(current.Add(time.Hour)), - }, - }, - { - description: "call check when the lease is far expired but held by another server", - expected: nil, - adaptorTimeout: time.Second * 20, - elector: &LeaderElector{ - config: LeaderElectionConfig{ - Lock: &fakeLock{identity: "healthTest"}, - LeaseDuration: time.Minute, - Name: "foo", - }, - observedRecord: rl.LeaderElectionRecord{ - HolderIdentity: "otherServer", - }, - observedTime: current, - clock: clock.NewFakeClock(current.Add(time.Hour)), - }, - }, - { - description: "call check when the lease is not expired", - expected: nil, - adaptorTimeout: time.Second * 20, - elector: &LeaderElector{ - config: LeaderElectionConfig{ - Lock: &fakeLock{identity: "healthTest"}, - LeaseDuration: time.Minute, - Name: "foo", - }, - observedRecord: rl.LeaderElectionRecord{ - HolderIdentity: "healthTest", - }, - observedTime: current, - clock: clock.NewFakeClock(current), - }, - }, - { - description: "call check when the lease is expired but inside the timeout", - expected: nil, - adaptorTimeout: time.Second * 20, - elector: &LeaderElector{ - config: LeaderElectionConfig{ - Lock: &fakeLock{identity: "healthTest"}, - LeaseDuration: time.Minute, - Name: "foo", - }, - observedRecord: rl.LeaderElectionRecord{ - HolderIdentity: "healthTest", - }, - observedTime: current, - clock: clock.NewFakeClock(current.Add(time.Minute).Add(time.Second)), - }, - }, - } - - for _, test := range tests { - adaptor := NewLeaderHealthzAdaptor(test.adaptorTimeout) - if adaptor.le != nil { - t.Errorf("[%s] leaderChecker started with a LeaderElector %v", test.description, adaptor.le) - } - if test.elector != nil { - test.elector.config.WatchDog = adaptor - adaptor.SetLeaderElection(test.elector) - if adaptor.le == nil { - t.Errorf("[%s] adaptor failed to set the LeaderElector", test.description) - } - } - err := adaptor.Check(req) - if test.expected == nil { - if err == nil { - continue - } - t.Errorf("[%s] called check, expected no error but received \"%v\"", test.description, err) - } else { - if err == nil { - t.Errorf("[%s] called check and failed to received the expected error \"%v\"", test.description, test.expected) - } - if err.Error() != test.expected.Error() { - t.Errorf("[%s] called check, expected %v, received %v", test.description, test.expected, err) - } - } - } -} diff --git a/pilot/pkg/leaderelection/k8sleaderelection/k8sresourcelock/configmaplock.go b/pilot/pkg/leaderelection/k8sleaderelection/k8sresourcelock/configmaplock.go deleted file mode 100644 index d10645560..000000000 --- a/pilot/pkg/leaderelection/k8sleaderelection/k8sresourcelock/configmaplock.go +++ /dev/null @@ -1,128 +0,0 @@ -/* -Copyright 2017 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package k8sresourcelock - -import ( - "context" - "encoding/json" - "errors" - "fmt" -) - -import ( - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - corev1client "k8s.io/client-go/kubernetes/typed/core/v1" -) - -// TODO: This is almost a exact replica of Endpoints lock. -// going forwards as we self host more and more components -// and use ConfigMaps as the means to pass that configuration -// data we will likely move to deprecate the Endpoints lock. -type ConfigMapLock struct { - // ConfigMapMeta should contain a Name and a Namespace of a - // ConfigMapMeta object that the LeaderElector will attempt to lead. - ConfigMapMeta metav1.ObjectMeta - Client corev1client.ConfigMapsGetter - LockConfig ResourceLockConfig - cm *v1.ConfigMap -} - -// Get returns the election record from a ConfigMap Annotation -func (cml *ConfigMapLock) Get(ctx context.Context) (*LeaderElectionRecord, []byte, error) { - var record LeaderElectionRecord - var err error - cml.cm, err = cml.Client.ConfigMaps(cml.ConfigMapMeta.Namespace).Get(ctx, cml.ConfigMapMeta.Name, metav1.GetOptions{}) - if err != nil { - return nil, nil, err - } - if cml.cm.Annotations == nil { - cml.cm.Annotations = make(map[string]string) - } - recordStr, found := cml.cm.Annotations[LeaderElectionRecordAnnotationKey] - recordBytes := []byte(recordStr) - if found { - if err := json.Unmarshal(recordBytes, &record); err != nil { - return nil, nil, err - } - } - return &record, recordBytes, nil -} - -// Create attempts to create a LeaderElectionRecord annotation -func (cml *ConfigMapLock) Create(ctx context.Context, ler LeaderElectionRecord) error { - recordBytes, err := json.Marshal(ler) - if err != nil { - return err - } - cml.cm, err = cml.Client.ConfigMaps(cml.ConfigMapMeta.Namespace).Create(ctx, &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: cml.ConfigMapMeta.Name, - Namespace: cml.ConfigMapMeta.Namespace, - Annotations: map[string]string{ - LeaderElectionRecordAnnotationKey: string(recordBytes), - }, - }, - }, metav1.CreateOptions{}) - return err -} - -// Update will update an existing annotation on a given resource. -func (cml *ConfigMapLock) Update(ctx context.Context, ler LeaderElectionRecord) error { - if cml.cm == nil { - return errors.New("configmap not initialized, call get or create first") - } - recordBytes, err := json.Marshal(ler) - if err != nil { - return err - } - if cml.cm.Annotations == nil { - cml.cm.Annotations = make(map[string]string) - } - cml.cm.Annotations[LeaderElectionRecordAnnotationKey] = string(recordBytes) - cm, err := cml.Client.ConfigMaps(cml.ConfigMapMeta.Namespace).Update(ctx, cml.cm, metav1.UpdateOptions{}) - if err != nil { - return err - } - cml.cm = cm - return nil -} - -// RecordEvent in leader election while adding meta-data -func (cml *ConfigMapLock) RecordEvent(s string) { - if cml.LockConfig.EventRecorder == nil { - return - } - events := fmt.Sprintf("%v %v", cml.LockConfig.Identity, s) - cml.LockConfig.EventRecorder.Eventf(&v1.ConfigMap{ObjectMeta: cml.cm.ObjectMeta}, v1.EventTypeNormal, "LeaderElection", events) -} - -// Describe is used to convert details on current resource lock -// into a string -func (cml *ConfigMapLock) Describe() string { - return fmt.Sprintf("%v/%v", cml.ConfigMapMeta.Namespace, cml.ConfigMapMeta.Name) -} - -// Identity returns the Identity of the lock -func (cml *ConfigMapLock) Identity() string { - return cml.LockConfig.Identity -} - -// Identity returns the Identity of the lock -func (cml *ConfigMapLock) Key() string { - return cml.LockConfig.Key -} diff --git a/pilot/pkg/leaderelection/k8sleaderelection/k8sresourcelock/endpointslock.go b/pilot/pkg/leaderelection/k8sleaderelection/k8sresourcelock/endpointslock.go deleted file mode 100644 index fe5975bf5..000000000 --- a/pilot/pkg/leaderelection/k8sleaderelection/k8sresourcelock/endpointslock.go +++ /dev/null @@ -1,124 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package k8sresourcelock - -import ( - "context" - "encoding/json" - "errors" - "fmt" -) - -import ( - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - corev1client "k8s.io/client-go/kubernetes/typed/core/v1" -) - -type EndpointsLock struct { - // EndpointsMeta should contain a Name and a Namespace of an - // Endpoints object that the LeaderElector will attempt to lead. - EndpointsMeta metav1.ObjectMeta - Client corev1client.EndpointsGetter - LockConfig ResourceLockConfig - e *v1.Endpoints -} - -// Get returns the election record from a Endpoints Annotation -func (el *EndpointsLock) Get(ctx context.Context) (*LeaderElectionRecord, []byte, error) { - var record LeaderElectionRecord - var err error - el.e, err = el.Client.Endpoints(el.EndpointsMeta.Namespace).Get(ctx, el.EndpointsMeta.Name, metav1.GetOptions{}) - if err != nil { - return nil, nil, err - } - if el.e.Annotations == nil { - el.e.Annotations = make(map[string]string) - } - recordStr, found := el.e.Annotations[LeaderElectionRecordAnnotationKey] - recordBytes := []byte(recordStr) - if found { - if err := json.Unmarshal(recordBytes, &record); err != nil { - return nil, nil, err - } - } - return &record, recordBytes, nil -} - -// Create attempts to create a LeaderElectionRecord annotation -func (el *EndpointsLock) Create(ctx context.Context, ler LeaderElectionRecord) error { - recordBytes, err := json.Marshal(ler) - if err != nil { - return err - } - el.e, err = el.Client.Endpoints(el.EndpointsMeta.Namespace).Create(ctx, &v1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: el.EndpointsMeta.Name, - Namespace: el.EndpointsMeta.Namespace, - Annotations: map[string]string{ - LeaderElectionRecordAnnotationKey: string(recordBytes), - }, - }, - }, metav1.CreateOptions{}) - return err -} - -// Update will update and existing annotation on a given resource. -func (el *EndpointsLock) Update(ctx context.Context, ler LeaderElectionRecord) error { - if el.e == nil { - return errors.New("endpoint not initialized, call get or create first") - } - recordBytes, err := json.Marshal(ler) - if err != nil { - return err - } - if el.e.Annotations == nil { - el.e.Annotations = make(map[string]string) - } - el.e.Annotations[LeaderElectionRecordAnnotationKey] = string(recordBytes) - e, err := el.Client.Endpoints(el.EndpointsMeta.Namespace).Update(ctx, el.e, metav1.UpdateOptions{}) - if err != nil { - return err - } - el.e = e - return nil -} - -// RecordEvent in leader election while adding meta-data -func (el *EndpointsLock) RecordEvent(s string) { - if el.LockConfig.EventRecorder == nil { - return - } - events := fmt.Sprintf("%v %v", el.LockConfig.Identity, s) - el.LockConfig.EventRecorder.Eventf(&v1.Endpoints{ObjectMeta: el.e.ObjectMeta}, v1.EventTypeNormal, "LeaderElection", events) -} - -// Describe is used to convert details on current resource lock -// into a string -func (el *EndpointsLock) Describe() string { - return fmt.Sprintf("%v/%v", el.EndpointsMeta.Namespace, el.EndpointsMeta.Name) -} - -// Identity returns the Identity of the lock -func (el *EndpointsLock) Identity() string { - return el.LockConfig.Identity -} - -// Key returns the Key of the lock -func (el *EndpointsLock) Key() string { - return el.LockConfig.Key -} diff --git a/pilot/pkg/leaderelection/k8sleaderelection/k8sresourcelock/interface.go b/pilot/pkg/leaderelection/k8sleaderelection/k8sresourcelock/interface.go deleted file mode 100644 index dcc36a82b..000000000 --- a/pilot/pkg/leaderelection/k8sleaderelection/k8sresourcelock/interface.go +++ /dev/null @@ -1,172 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package k8sresourcelock - -import ( - "context" - "fmt" - "time" -) - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - clientset "k8s.io/client-go/kubernetes" - coordinationv1 "k8s.io/client-go/kubernetes/typed/coordination/v1" - corev1 "k8s.io/client-go/kubernetes/typed/core/v1" - restclient "k8s.io/client-go/rest" -) - -const ( - LeaderElectionRecordAnnotationKey = "control-plane.alpha.kubernetes.io/leader" - EndpointsResourceLock = "endpoints" - ConfigMapsResourceLock = "configmaps" - LeasesResourceLock = "leases" - EndpointsLeasesResourceLock = "endpointsleases" - ConfigMapsLeasesResourceLock = "configmapsleases" -) - -// LeaderElectionRecord is the record that is stored in the leader election annotation. -// This information should be used for observational purposes only and could be replaced -// with a random string (e.g. UUID) with only slight modification of this code. -// TODO(mikedanese): this should potentially be versioned -type LeaderElectionRecord struct { - // HolderIdentity is the ID that owns the lease. If empty, no one owns this lease and - // all callers may acquire. Versions of this library prior to Kubernetes 1.14 will not - // attempt to acquire leases with empty identities and will wait for the full lease - // interval to expire before attempting to reacquire. This value is set to empty when - // a client voluntarily steps down. - HolderIdentity string `json:"holderIdentity"` - // HolderKey is the Key of the lease owner. This may be empty if a key is not set. - HolderKey string `json:"holderKey"` - LeaseDurationSeconds int `json:"leaseDurationSeconds"` - AcquireTime metav1.Time `json:"acquireTime"` - RenewTime metav1.Time `json:"renewTime"` - LeaderTransitions int `json:"leaderTransitions"` -} - -// EventRecorder records a change in the ResourceLock. -type EventRecorder interface { - Eventf(obj runtime.Object, eventType, reason, message string, args ...interface{}) -} - -// ResourceLockConfig common data that exists across different -// resource locks -type ResourceLockConfig struct { - // Identity is the unique string identifying a lease holder across - // all participants in an election. - Identity string - // Key is a user-defined value used to indicate how high priority this lock - // have. Other locks may steal the lock from us if they believe their key - // has a higher priority. - Key string - // EventRecorder is optional. - EventRecorder EventRecorder -} - -// Interface offers a common interface for locking on arbitrary -// resources used in leader election. The Interface is used -// to hide the details on specific implementations in order to allow -// them to change over time. This interface is strictly for use -// by the leaderelection code. -type Interface interface { - // Get returns the LeaderElectionRecord - Get(ctx context.Context) (*LeaderElectionRecord, []byte, error) - - // Create attempts to create a LeaderElectionRecord - Create(ctx context.Context, ler LeaderElectionRecord) error - - // Update will update and existing LeaderElectionRecord - Update(ctx context.Context, ler LeaderElectionRecord) error - - // RecordEvent is used to record events - RecordEvent(string) - - // Identity will return the locks Identity - Identity() string - Key() string - - // Describe is used to convert details on current resource lock - // into a string - Describe() string -} - -// Manufacture will create a lock of a given type according to the input parameters -// nolint: lll -func New(lockType string, ns string, name string, coreClient corev1.CoreV1Interface, coordinationClient coordinationv1.CoordinationV1Interface, rlc ResourceLockConfig) (Interface, error) { - endpointsLock := &EndpointsLock{ - EndpointsMeta: metav1.ObjectMeta{ - Namespace: ns, - Name: name, - }, - Client: coreClient, - LockConfig: rlc, - } - configmapLock := &ConfigMapLock{ - ConfigMapMeta: metav1.ObjectMeta{ - Namespace: ns, - Name: name, - }, - Client: coreClient, - LockConfig: rlc, - } - leaseLock := &LeaseLock{ - LeaseMeta: metav1.ObjectMeta{ - Namespace: ns, - Name: name, - }, - Client: coordinationClient, - LockConfig: rlc, - } - switch lockType { - case EndpointsResourceLock: - return endpointsLock, nil - case ConfigMapsResourceLock: - return configmapLock, nil - case LeasesResourceLock: - return leaseLock, nil - case EndpointsLeasesResourceLock: - return &MultiLock{ - Primary: endpointsLock, - Secondary: leaseLock, - }, nil - case ConfigMapsLeasesResourceLock: - return &MultiLock{ - Primary: configmapLock, - Secondary: leaseLock, - }, nil - default: - return nil, fmt.Errorf("invalid lock-type %s", lockType) - } -} - -// NewFromKubeconfig will create a lock of a given type according to the input parameters. -// Timeout set for a client used to contact to Kubernetes should be lower than -// RenewDeadline to keep a single hung request from forcing a leader loss. -// Setting it to max(time.Second, RenewDeadline/2) as a reasonable heuristic. -// nolint: lll -func NewFromKubeconfig(lockType string, ns string, name string, rlc ResourceLockConfig, kubeconfig *restclient.Config, renewDeadline time.Duration) (Interface, error) { - // shallow copy, do not modify the kubeconfig - config := *kubeconfig - timeout := renewDeadline / 2 - if timeout < time.Second { - timeout = time.Second - } - config.Timeout = timeout - leaderElectionClient := clientset.NewForConfigOrDie(restclient.AddUserAgent(&config, "leader-election")) - return New(lockType, ns, name, leaderElectionClient.CoreV1(), leaderElectionClient.CoordinationV1(), rlc) -} diff --git a/pilot/pkg/leaderelection/k8sleaderelection/k8sresourcelock/leaselock.go b/pilot/pkg/leaderelection/k8sleaderelection/k8sresourcelock/leaselock.go deleted file mode 100644 index b1b1835cf..000000000 --- a/pilot/pkg/leaderelection/k8sleaderelection/k8sresourcelock/leaselock.go +++ /dev/null @@ -1,141 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package k8sresourcelock - -import ( - "context" - "encoding/json" - "errors" - "fmt" -) - -import ( - coordinationv1 "k8s.io/api/coordination/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - coordinationv1client "k8s.io/client-go/kubernetes/typed/coordination/v1" -) - -type LeaseLock struct { - // LeaseMeta should contain a Name and a Namespace of a - // LeaseMeta object that the LeaderElector will attempt to lead. - LeaseMeta metav1.ObjectMeta - Client coordinationv1client.LeasesGetter - LockConfig ResourceLockConfig - lease *coordinationv1.Lease -} - -// Get returns the election record from a Lease spec -func (ll *LeaseLock) Get(ctx context.Context) (*LeaderElectionRecord, []byte, error) { - var err error - ll.lease, err = ll.Client.Leases(ll.LeaseMeta.Namespace).Get(ctx, ll.LeaseMeta.Name, metav1.GetOptions{}) - if err != nil { - return nil, nil, err - } - record := LeaseSpecToLeaderElectionRecord(&ll.lease.Spec) - recordByte, err := json.Marshal(*record) - if err != nil { - return nil, nil, err - } - return record, recordByte, nil -} - -// Create attempts to create a Lease -func (ll *LeaseLock) Create(ctx context.Context, ler LeaderElectionRecord) error { - var err error - ll.lease, err = ll.Client.Leases(ll.LeaseMeta.Namespace).Create(ctx, &coordinationv1.Lease{ - ObjectMeta: metav1.ObjectMeta{ - Name: ll.LeaseMeta.Name, - Namespace: ll.LeaseMeta.Namespace, - }, - Spec: LeaderElectionRecordToLeaseSpec(&ler), - }, metav1.CreateOptions{}) - return err -} - -// Update will update an existing Lease spec. -func (ll *LeaseLock) Update(ctx context.Context, ler LeaderElectionRecord) error { - if ll.lease == nil { - return errors.New("lease not initialized, call get or create first") - } - ll.lease.Spec = LeaderElectionRecordToLeaseSpec(&ler) - - lease, err := ll.Client.Leases(ll.LeaseMeta.Namespace).Update(ctx, ll.lease, metav1.UpdateOptions{}) - if err != nil { - return err - } - - ll.lease = lease - return nil -} - -// RecordEvent in leader election while adding meta-data -func (ll *LeaseLock) RecordEvent(s string) { - if ll.LockConfig.EventRecorder == nil { - return - } - events := fmt.Sprintf("%v %v", ll.LockConfig.Identity, s) - ll.LockConfig.EventRecorder.Eventf(&coordinationv1.Lease{ObjectMeta: ll.lease.ObjectMeta}, corev1.EventTypeNormal, "LeaderElection", events) -} - -// Describe is used to convert details on current resource lock -// into a string -func (ll *LeaseLock) Describe() string { - return fmt.Sprintf("%v/%v", ll.LeaseMeta.Namespace, ll.LeaseMeta.Name) -} - -// Identity returns the Identity of the lock -func (ll *LeaseLock) Identity() string { - return ll.LockConfig.Identity -} - -// Key returns the Key of the lock -func (ll *LeaseLock) Key() string { - return ll.LockConfig.Key -} - -func LeaseSpecToLeaderElectionRecord(spec *coordinationv1.LeaseSpec) *LeaderElectionRecord { - var r LeaderElectionRecord - if spec.HolderIdentity != nil { - r.HolderIdentity = *spec.HolderIdentity - } - if spec.LeaseDurationSeconds != nil { - r.LeaseDurationSeconds = int(*spec.LeaseDurationSeconds) - } - if spec.LeaseTransitions != nil { - r.LeaderTransitions = int(*spec.LeaseTransitions) - } - if spec.AcquireTime != nil { - r.AcquireTime = metav1.Time{Time: spec.AcquireTime.Time} - } - if spec.RenewTime != nil { - r.RenewTime = metav1.Time{Time: spec.RenewTime.Time} - } - return &r -} - -func LeaderElectionRecordToLeaseSpec(ler *LeaderElectionRecord) coordinationv1.LeaseSpec { - leaseDurationSeconds := int32(ler.LeaseDurationSeconds) - leaseTransitions := int32(ler.LeaderTransitions) - return coordinationv1.LeaseSpec{ - HolderIdentity: &ler.HolderIdentity, - LeaseDurationSeconds: &leaseDurationSeconds, - AcquireTime: &metav1.MicroTime{Time: ler.AcquireTime.Time}, - RenewTime: &metav1.MicroTime{Time: ler.RenewTime.Time}, - LeaseTransitions: &leaseTransitions, - } -} diff --git a/pilot/pkg/leaderelection/k8sleaderelection/k8sresourcelock/multilock.go b/pilot/pkg/leaderelection/k8sleaderelection/k8sresourcelock/multilock.go deleted file mode 100644 index 8ee7954e2..000000000 --- a/pilot/pkg/leaderelection/k8sleaderelection/k8sresourcelock/multilock.go +++ /dev/null @@ -1,111 +0,0 @@ -/* -Copyright 2019 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package k8sresourcelock - -import ( - "bytes" - "context" - "encoding/json" -) - -import ( - apierrors "k8s.io/apimachinery/pkg/api/errors" -) - -const ( - UnknownLeader = "leaderelection.k8s.io/unknown" -) - -// MultiLock is used for lock's migration -type MultiLock struct { - Primary Interface - Secondary Interface -} - -// Get returns the older election record of the lock -func (ml *MultiLock) Get(ctx context.Context) (*LeaderElectionRecord, []byte, error) { - primary, primaryRaw, err := ml.Primary.Get(ctx) - if err != nil { - return nil, nil, err - } - - secondary, secondaryRaw, err := ml.Secondary.Get(ctx) - if err != nil { - // Lock is held by old client - if apierrors.IsNotFound(err) && primary.HolderIdentity != ml.Identity() { - return primary, primaryRaw, nil - } - return nil, nil, err - } - - if primary.HolderIdentity != secondary.HolderIdentity { - primary.HolderIdentity = UnknownLeader - primaryRaw, err = json.Marshal(primary) - if err != nil { - return nil, nil, err - } - } - return primary, ConcatRawRecord(primaryRaw, secondaryRaw), nil -} - -// Create attempts to create both primary lock and secondary lock -func (ml *MultiLock) Create(ctx context.Context, ler LeaderElectionRecord) error { - err := ml.Primary.Create(ctx, ler) - if err != nil && !apierrors.IsAlreadyExists(err) { - return err - } - return ml.Secondary.Create(ctx, ler) -} - -// Update will update and existing annotation on both two resources. -func (ml *MultiLock) Update(ctx context.Context, ler LeaderElectionRecord) error { - err := ml.Primary.Update(ctx, ler) - if err != nil { - return err - } - _, _, err = ml.Secondary.Get(ctx) - if err != nil && apierrors.IsNotFound(err) { - return ml.Secondary.Create(ctx, ler) - } - return ml.Secondary.Update(ctx, ler) -} - -// RecordEvent in leader election while adding meta-data -func (ml *MultiLock) RecordEvent(s string) { - ml.Primary.RecordEvent(s) - ml.Secondary.RecordEvent(s) -} - -// Describe is used to convert details on current resource lock -// into a string -func (ml *MultiLock) Describe() string { - return ml.Primary.Describe() -} - -// Identity returns the Identity of the lock -func (ml *MultiLock) Identity() string { - return ml.Primary.Identity() -} - -// Key returns the Key of the lock -func (ml *MultiLock) Key() string { - return ml.Primary.Key() -} - -func ConcatRawRecord(primaryRaw, secondaryRaw []byte) []byte { - return bytes.Join([][]byte{primaryRaw, secondaryRaw}, []byte(",")) -} diff --git a/pilot/pkg/leaderelection/k8sleaderelection/leaderelection.go b/pilot/pkg/leaderelection/k8sleaderelection/leaderelection.go deleted file mode 100644 index cd1b0ca9a..000000000 --- a/pilot/pkg/leaderelection/k8sleaderelection/leaderelection.go +++ /dev/null @@ -1,450 +0,0 @@ -/* -Copyright 2015 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package leaderelection implements leader election of a set of endpoints. -// It uses an annotation in the endpoints object to store the record of the -// election state. This implementation does not guarantee that only one -// client is acting as a leader (a.k.a. fencing). -// -// A client only acts on timestamps captured locally to infer the state of the -// leader election. The client does not consider timestamps in the leader -// election record to be accurate because these timestamps may not have been -// produced by a local clock. The implemention does not depend on their -// accuracy and only uses their change to indicate that another client has -// renewed the leader lease. Thus the implementation is tolerant to arbitrary -// clock skew, but is not tolerant to arbitrary clock skew rate. -// -// However the level of tolerance to skew rate can be configured by setting -// RenewDeadline and LeaseDuration appropriately. The tolerance expressed as a -// maximum tolerated ratio of time passed on the fastest node to time passed on -// the slowest node can be approximately achieved with a configuration that sets -// the same ratio of LeaseDuration to RenewDeadline. For example if a user wanted -// to tolerate some nodes progressing forward in time twice as fast as other nodes, -// the user could set LeaseDuration to 60 seconds and RenewDeadline to 30 seconds. -// -// While not required, some method of clock synchronization between nodes in the -// cluster is highly recommended. It's important to keep in mind when configuring -// this client that the tolerance to skew rate varies inversely to master -// availability. -// -// Larger clusters often have a more lenient SLA for API latency. This should be -// taken into account when configuring the client. The rate of leader transitions -// should be monitored and RetryPeriod and LeaseDuration should be increased -// until the rate is stable and acceptably low. It's important to keep in mind -// when configuring this client that the tolerance to API latency varies inversely -// to master availability. -// -// DISCLAIMER: this is an alpha API. This library will likely change significantly -// or even be removed entirely in subsequent releases. Depend on this API at -// your own risk. -package k8sleaderelection - -import ( - "bytes" - "context" - "fmt" - "sync" - "time" -) - -import ( - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/klog/v2" - "k8s.io/utils/clock" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/leaderelection/k8sleaderelection/k8sresourcelock" -) - -const ( - JitterFactor = 1.2 -) - -// NewLeaderElector creates a LeaderElector from a LeaderElectionConfig -func NewLeaderElector(lec LeaderElectionConfig) (*LeaderElector, error) { - if lec.LeaseDuration <= lec.RenewDeadline { - return nil, fmt.Errorf("leaseDuration must be greater than renewDeadline") - } - if lec.RenewDeadline <= time.Duration(JitterFactor*float64(lec.RetryPeriod)) { - return nil, fmt.Errorf("renewDeadline must be greater than retryPeriod*JitterFactor") - } - if lec.LeaseDuration < 1 { - return nil, fmt.Errorf("leaseDuration must be greater than zero") - } - if lec.RenewDeadline < 1 { - return nil, fmt.Errorf("renewDeadline must be greater than zero") - } - if lec.RetryPeriod < 1 { - return nil, fmt.Errorf("retryPeriod must be greater than zero") - } - if lec.Callbacks.OnStartedLeading == nil { - return nil, fmt.Errorf("callback OnStartedLeading must not be nil") - } - if lec.Callbacks.OnStoppedLeading == nil { - return nil, fmt.Errorf("callback OnStoppedLeading must not be nil") - } - - if lec.Lock == nil { - return nil, fmt.Errorf("lock must not be nil") - } - le := LeaderElector{ - config: lec, - clock: clock.RealClock{}, - metrics: globalMetricsFactory.newLeaderMetrics(), - } - le.metrics.leaderOff(le.config.Name) - return &le, nil -} - -type KeyComparisonFunc func(existingKey string) bool - -type LeaderElectionConfig struct { - // Lock is the resource that will be used for locking - Lock k8sresourcelock.Interface - - // LeaseDuration is the duration that non-leader candidates will - // wait to force acquire leadership. This is measured against time of - // last observed ack. - // - // A client needs to wait a full LeaseDuration without observing a change to - // the record before it can attempt to take over. When all clients are - // shutdown and a new set of clients are started with different names against - // the same leader record, they must wait the full LeaseDuration before - // attempting to acquire the lease. Thus LeaseDuration should be as short as - // possible (within your tolerance for clock skew rate) to avoid a possible - // long waits in the scenario. - // - // Core clients default this value to 15 seconds. - LeaseDuration time.Duration - // RenewDeadline is the duration that the acting master will retry - // refreshing leadership before giving up. - // - // Core clients default this value to 10 seconds. - RenewDeadline time.Duration - // RetryPeriod is the duration the LeaderElector clients should wait - // between tries of actions. - // - // Core clients default this value to 2 seconds. - RetryPeriod time.Duration - - // KeyComparison defines a function to compare the existing leader's key to our own. - // If the function returns true, indicating our key has high precedence, we will take over - // leadership even if their is another un-expired leader. - // - // This can be used to implemented a prioritized leader election. For example, if multiple - // versions of the same application run simultaneously, we can ensure the newest version - // will become the leader. - // - // It is the responsibility of the caller to ensure that all KeyComparison functions are - // logically consistent between all clients participating in the leader election to avoid multiple - // clients claiming to have high precedence and constantly pre-empting the existing leader. - // - // KeyComparison functions should ensure they handle an empty existingKey, as "key" is not a required field. - // - // Warning: when a lock is stolen (from KeyComparison returning true), the old leader may not - // immediately be notified they have lost the leader election. - KeyComparison KeyComparisonFunc - - // Callbacks are callbacks that are triggered during certain lifecycle - // events of the LeaderElector - Callbacks LeaderCallbacks - - // WatchDog is the associated health checker - // WatchDog may be null if its not needed/configured. - WatchDog *HealthzAdaptor - - // ReleaseOnCancel should be set true if the lock should be released - // when the run context is canceled. If you set this to true, you must - // ensure all code guarded by this lease has successfully completed - // prior to canceling the context, or you may have two processes - // simultaneously acting on the critical path. - ReleaseOnCancel bool - - // Name is the name of the resource lock for debugging - Name string -} - -// LeaderCallbacks are callbacks that are triggered during certain -// lifecycle events of the LeaderElector. These are invoked asynchronously. -// -// possible future callbacks: -// - OnChallenge() -type LeaderCallbacks struct { - // OnStartedLeading is called when a LeaderElector client starts leading - OnStartedLeading func(context.Context) - // OnStoppedLeading is called when a LeaderElector client stops leading - OnStoppedLeading func() - // OnNewLeader is called when the client observes a leader that is - // not the previously observed leader. This includes the first observed - // leader when the client starts. - OnNewLeader func(identity string) -} - -// LeaderElector is a leader election client. -type LeaderElector struct { - config LeaderElectionConfig - // internal bookkeeping - observedRecord k8sresourcelock.LeaderElectionRecord - observedRawRecord []byte - observedTime time.Time - // used to implement OnNewLeader(), may lag slightly from the - // value observedRecord.HolderIdentity if the transition has - // not yet been reported. - reportedLeader string - - // clock is wrapper around time to allow for less flaky testing - clock clock.Clock - - // used to lock the observedRecord - observedRecordLock sync.Mutex - - metrics leaderMetricsAdapter -} - -// Run starts the leader election loop. Run will not return -// before leader election loop is stopped by ctx or it has -// stopped holding the leader lease -func (le *LeaderElector) Run(ctx context.Context) { - defer runtime.HandleCrash() - defer func() { - le.config.Callbacks.OnStoppedLeading() - }() - - if !le.acquire(ctx) { - return // ctx signaled done - } - ctx, cancel := context.WithCancel(ctx) - defer cancel() - go le.config.Callbacks.OnStartedLeading(ctx) - le.renew(ctx) -} - -// RunOrDie starts a client with the provided config or panics if the config -// fails to validate. RunOrDie blocks until leader election loop is -// stopped by ctx or it has stopped holding the leader lease -func RunOrDie(ctx context.Context, lec LeaderElectionConfig) { - le, err := NewLeaderElector(lec) - if err != nil { - panic(err) - } - if lec.WatchDog != nil { - lec.WatchDog.SetLeaderElection(le) - } - le.Run(ctx) -} - -// GetLeader returns the identity of the last observed leader or returns the empty string if -// no leader has yet been observed. -// This function is for informational purposes. (e.g. monitoring, logs, etc.) -func (le *LeaderElector) GetLeader() string { - return le.getObservedRecord().HolderIdentity -} - -// IsLeader returns true if the last observed leader was this client else returns false. -func (le *LeaderElector) IsLeader() bool { - return le.getObservedRecord().HolderIdentity == le.config.Lock.Identity() -} - -// acquire loops calling tryAcquireOrRenew and returns true immediately when tryAcquireOrRenew succeeds. -// Returns false if ctx signals done. -func (le *LeaderElector) acquire(ctx context.Context) bool { - ctx, cancel := context.WithCancel(ctx) - defer cancel() - succeeded := false - desc := le.config.Lock.Describe() - klog.Infof("attempting to acquire leader lease %v...", desc) - wait.JitterUntil(func() { - succeeded = le.tryAcquireOrRenew(ctx) - le.maybeReportTransition() - if !succeeded { - klog.V(4).Infof("failed to acquire lease %v", desc) - return - } - le.config.Lock.RecordEvent("became leader") - le.metrics.leaderOn(le.config.Name) - klog.Infof("successfully acquired lease %v", desc) - cancel() - }, le.config.RetryPeriod, JitterFactor, true, ctx.Done()) - return succeeded -} - -// renew loops calling tryAcquireOrRenew and returns immediately when tryAcquireOrRenew fails or ctx signals done. -func (le *LeaderElector) renew(ctx context.Context) { - ctx, cancel := context.WithCancel(ctx) - defer cancel() - wait.Until(func() { - timeoutCtx, timeoutCancel := context.WithTimeout(ctx, le.config.RenewDeadline) - defer timeoutCancel() - err := wait.PollImmediateUntil(le.config.RetryPeriod, func() (bool, error) { - return le.tryAcquireOrRenew(timeoutCtx), nil - }, timeoutCtx.Done()) - - le.maybeReportTransition() - desc := le.config.Lock.Describe() - if err == nil { - klog.V(5).Infof("successfully renewed lease %v", desc) - return - } - le.config.Lock.RecordEvent("stopped leading") - le.metrics.leaderOff(le.config.Name) - klog.Infof("failed to renew lease %v: %v", desc, err) - cancel() - }, le.config.RetryPeriod, ctx.Done()) - - // if we hold the lease, give it up - if le.config.ReleaseOnCancel { - le.release() - } -} - -// release attempts to release the leader lease if we have acquired it. -func (le *LeaderElector) release() bool { - if !le.IsLeader() { - return true - } - now := metav1.Now() - leaderElectionRecord := k8sresourcelock.LeaderElectionRecord{ - LeaderTransitions: le.observedRecord.LeaderTransitions, - LeaseDurationSeconds: 1, - RenewTime: now, - AcquireTime: now, - } - if err := le.config.Lock.Update(context.TODO(), leaderElectionRecord); err != nil { - klog.Errorf("Failed to release lock: %v", err) - return false - } - - le.setObservedRecord(&leaderElectionRecord) - return true -} - -// tryAcquireOrRenew tries to acquire a leader lease if it is not already acquired, -// else it tries to renew the lease if it has already been acquired. Returns true -// on success else returns false. -func (le *LeaderElector) tryAcquireOrRenew(ctx context.Context) bool { - now := metav1.Now() - leaderElectionRecord := k8sresourcelock.LeaderElectionRecord{ - HolderIdentity: le.config.Lock.Identity(), - HolderKey: le.config.Lock.Key(), - LeaseDurationSeconds: int(le.config.LeaseDuration / time.Second), - RenewTime: now, - AcquireTime: now, - } - - // 1. obtain or create the ElectionRecord - oldLeaderElectionRecord, oldLeaderElectionRawRecord, err := le.config.Lock.Get(ctx) - if err != nil { - if !errors.IsNotFound(err) { - klog.Errorf("error retrieving resource lock %v: %v", le.config.Lock.Describe(), err) - return false - } - if err = le.config.Lock.Create(ctx, leaderElectionRecord); err != nil { - klog.Errorf("error initially creating leader election record: %v", err) - return false - } - - le.setObservedRecord(&leaderElectionRecord) - - return true - } - - // 2. Record obtained, check the Identity & Time - if !bytes.Equal(le.observedRawRecord, oldLeaderElectionRawRecord) { - le.setObservedRecord(oldLeaderElectionRecord) - - le.observedRawRecord = oldLeaderElectionRawRecord - } - if len(oldLeaderElectionRecord.HolderIdentity) > 0 && - le.observedTime.Add(le.config.LeaseDuration).After(now.Time) && - !le.IsLeader() { - if le.config.KeyComparison != nil && le.config.KeyComparison(oldLeaderElectionRecord.HolderKey) { - // Lock is held and not expired, but our key is higher than the existing one. - // We will pre-empt the existing leader. - // nolint: lll - klog.V(4).Infof("lock is held by %v with key %v, but our key (%v) evicts it", oldLeaderElectionRecord.HolderIdentity, oldLeaderElectionRecord.HolderKey, le.config.Lock.Key()) - } else { - klog.V(4).Infof("lock is held by %v and has not yet expired", oldLeaderElectionRecord.HolderIdentity) - return false - } - } - - // 3. We're going to try to update. The leaderElectionRecord is set to it's default - // here. Let's correct it before updating. - if le.IsLeader() { - leaderElectionRecord.AcquireTime = oldLeaderElectionRecord.AcquireTime - leaderElectionRecord.LeaderTransitions = oldLeaderElectionRecord.LeaderTransitions - } else { - leaderElectionRecord.LeaderTransitions = oldLeaderElectionRecord.LeaderTransitions + 1 - } - - // update the lock itself - if err = le.config.Lock.Update(ctx, leaderElectionRecord); err != nil { - klog.Errorf("Failed to update lock: %v", err) - return false - } - - le.setObservedRecord(&leaderElectionRecord) - return true -} - -func (le *LeaderElector) maybeReportTransition() { - if le.observedRecord.HolderIdentity == le.reportedLeader { - return - } - le.reportedLeader = le.observedRecord.HolderIdentity - if le.config.Callbacks.OnNewLeader != nil { - go le.config.Callbacks.OnNewLeader(le.reportedLeader) - } -} - -// Check will determine if the current lease is expired by more than timeout. -func (le *LeaderElector) Check(maxTolerableExpiredLease time.Duration) error { - if !le.IsLeader() { - // Currently not concerned with the case that we are hot standby - return nil - } - // If we are more than timeout seconds after the lease duration that is past the timeout - // on the lease renew. Time to start reporting ourselves as unhealthy. We should have - // died but conditions like deadlock can prevent this. (See #70819) - if le.clock.Since(le.observedTime) > le.config.LeaseDuration+maxTolerableExpiredLease { - return fmt.Errorf("failed election to renew leadership on lease %s", le.config.Name) - } - - return nil -} - -// setObservedRecord will set a new observedRecord and update observedTime to the current time. -// Protect critical sections with lock. -func (le *LeaderElector) setObservedRecord(observedRecord *k8sresourcelock.LeaderElectionRecord) { - le.observedRecordLock.Lock() - defer le.observedRecordLock.Unlock() - - le.observedRecord = *observedRecord - le.observedTime = le.clock.Now() -} - -// getObservedRecord returns observersRecord. -// Protect critical sections with lock. -func (le *LeaderElector) getObservedRecord() k8sresourcelock.LeaderElectionRecord { - le.observedRecordLock.Lock() - defer le.observedRecordLock.Unlock() - - return le.observedRecord -} diff --git a/pilot/pkg/leaderelection/k8sleaderelection/leaderelection_test.go b/pilot/pkg/leaderelection/k8sleaderelection/leaderelection_test.go deleted file mode 100644 index 6d117a875..000000000 --- a/pilot/pkg/leaderelection/k8sleaderelection/leaderelection_test.go +++ /dev/null @@ -1,1326 +0,0 @@ -/* -Copyright 2015 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package k8sleaderelection - -import ( - "context" - "encoding/json" - "fmt" - "sync" - "testing" - "time" -) - -import ( - coordinationv1 "k8s.io/api/coordination/v1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/diff" - "k8s.io/client-go/kubernetes/fake" - fakeclient "k8s.io/client-go/testing" - "k8s.io/client-go/tools/record" - "k8s.io/utils/clock" -) - -import ( - rl "github.com/apache/dubbo-go-pixiu/pilot/pkg/leaderelection/k8sleaderelection/k8sresourcelock" -) - -func createLockObject(t *testing.T, objectType, namespace, name string, record *rl.LeaderElectionRecord) (obj runtime.Object) { - objectMeta := metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - } - if record != nil { - recordBytes, _ := json.Marshal(record) - objectMeta.Annotations = map[string]string{ - rl.LeaderElectionRecordAnnotationKey: string(recordBytes), - } - } - switch objectType { - case "endpoints": - obj = &corev1.Endpoints{ObjectMeta: objectMeta} - case "configmaps": - obj = &corev1.ConfigMap{ObjectMeta: objectMeta} - case "leases": - var spec coordinationv1.LeaseSpec - if record != nil { - spec = rl.LeaderElectionRecordToLeaseSpec(record) - } - obj = &coordinationv1.Lease{ObjectMeta: objectMeta, Spec: spec} - default: - t.Fatal("unexpected objType:" + objectType) - } - return -} - -// Will test leader election using endpoints as the resource -func TestTryAcquireOrRenewEndpoints(t *testing.T) { - testTryAcquireOrRenew(t, "endpoints") -} - -type Reactor struct { - verb string - objectType string - reaction fakeclient.ReactionFunc -} - -func testTryAcquireOrRenew(t *testing.T, objectType string) { - future := time.Now().Add(1000 * time.Hour) - past := time.Now().Add(-1000 * time.Hour) - - tests := []struct { - name string - observedRecord rl.LeaderElectionRecord - observedTime time.Time - reactors []Reactor - - key string - keyComparisonFunc KeyComparisonFunc - - expectSuccess bool - transitionLeader bool - outHolder string - }{ - { - name: "acquire from no object", - reactors: []Reactor{ - { - verb: "get", - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, nil, errors.NewNotFound(action.(fakeclient.GetAction).GetResource().GroupResource(), action.(fakeclient.GetAction).GetName()) - }, - }, - { - verb: "create", - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.CreateAction).GetObject(), nil - }, - }, - }, - expectSuccess: true, - outHolder: "baz", - }, - { - name: "acquire from object without annotations", - reactors: []Reactor{ - { - verb: "get", - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, createLockObject(t, objectType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), nil), nil - }, - }, - { - verb: "update", - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.CreateAction).GetObject(), nil - }, - }, - }, - expectSuccess: true, - transitionLeader: true, - outHolder: "baz", - }, - { - name: "acquire from unled object", - reactors: []Reactor{ - { - verb: "get", - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, createLockObject(t, objectType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{}), nil - }, - }, - { - verb: "update", - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.CreateAction).GetObject(), nil - }, - }, - }, - - expectSuccess: true, - transitionLeader: true, - outHolder: "baz", - }, - { - name: "acquire from led, unacked object", - reactors: []Reactor{ - { - verb: "get", - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, objectType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: "bing"}), nil - }, - }, - { - verb: "update", - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.CreateAction).GetObject(), nil - }, - }, - }, - observedRecord: rl.LeaderElectionRecord{HolderIdentity: "bing"}, - observedTime: past, - - expectSuccess: true, - transitionLeader: true, - outHolder: "baz", - }, - { - name: "acquire from empty led, acked object", - reactors: []Reactor{ - { - verb: "get", - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, objectType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: ""}), nil - }, - }, - { - verb: "update", - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.CreateAction).GetObject(), nil - }, - }, - }, - observedTime: future, - - expectSuccess: true, - transitionLeader: true, - outHolder: "baz", - }, - { - name: "don't acquire from led, acked object", - reactors: []Reactor{ - { - verb: "get", - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, objectType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: "bing"}), nil - }, - }, - }, - observedTime: future, - - expectSuccess: false, - outHolder: "bing", - }, - { - name: "don't acquire from led, acked object with key", - reactors: []Reactor{ - { - verb: "get", - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, createLockObject(t, objectType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), - &rl.LeaderElectionRecord{HolderIdentity: "bing", HolderKey: "a"}), nil - }, - }, - }, - observedTime: future, - - expectSuccess: false, - outHolder: "bing", - }, - // Uncomment when https://github.com/kubernetes/kubernetes/pull/103442/files#r715818684 is resolved. - //{ - // name: "don't acquire from led, acked object with key when our key is smaller", - // reactors: []Reactor{ - // { - // verb: "get", - // reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // return true, createLockObject(t, objectType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), - // &rl.LeaderElectionRecord{HolderIdentity: "bing", HolderKey: "zzzz"}), nil - // }, - // }, - // }, - // observedTime: future, - // - // key: "aaa", - // keyComparisonFunc: func(existingKey string) bool { - // return "aaa" > existingKey - // }, - // - // expectSuccess: false, - // outHolder: "bing", - //}, - { - name: "steal from led object with key when our key is larger", - reactors: []Reactor{ - { - verb: "get", - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, objectType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), - &rl.LeaderElectionRecord{HolderIdentity: "bing", HolderKey: "aaa"}), nil - }, - }, - { - verb: "update", - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.CreateAction).GetObject(), nil - }, - }, - }, - observedTime: future, - - key: "zzz", - keyComparisonFunc: func(existingKey string) bool { - return "zzz" > existingKey - }, - - transitionLeader: true, - expectSuccess: true, - outHolder: "baz", - }, - { - name: "handle led object with no key", - reactors: []Reactor{ - { - verb: "get", - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, objectType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), - &rl.LeaderElectionRecord{HolderIdentity: "bing"}), nil - }, - }, - }, - observedTime: future, - - key: "zzz", - keyComparisonFunc: func(existingKey string) bool { - return existingKey != "" && "zzz" > existingKey - }, - - expectSuccess: false, - outHolder: "bing", - }, - { - name: "renew already acquired object", - reactors: []Reactor{ - { - verb: "get", - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, objectType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: "baz"}), nil - }, - }, - { - verb: "update", - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.CreateAction).GetObject(), nil - }, - }, - }, - observedTime: future, - observedRecord: rl.LeaderElectionRecord{HolderIdentity: "baz"}, - - expectSuccess: true, - outHolder: "baz", - }, - } - - for i := range tests { - test := &tests[i] - t.Run(test.name, func(t *testing.T) { - // OnNewLeader is called async so we have to wait for it. - var wg sync.WaitGroup - wg.Add(1) - var reportedLeader string - var lock rl.Interface - - objectMeta := metav1.ObjectMeta{Namespace: "foo", Name: "bar"} - resourceLockConfig := rl.ResourceLockConfig{ - Identity: "baz", - Key: test.key, - EventRecorder: &record.FakeRecorder{}, - } - c := &fake.Clientset{} - for _, reactor := range test.reactors { - c.AddReactor(reactor.verb, objectType, reactor.reaction) - } - c.AddReactor("*", "*", func(action fakeclient.Action) (bool, runtime.Object, error) { - t.Errorf("unreachable action. testclient called too many times: %+v", action) - return true, nil, fmt.Errorf("unreachable action") - }) - - switch objectType { - case "endpoints": - lock = &rl.EndpointsLock{ - EndpointsMeta: objectMeta, - LockConfig: resourceLockConfig, - Client: c.CoreV1(), - } - case "configmaps": - lock = &rl.ConfigMapLock{ - ConfigMapMeta: objectMeta, - LockConfig: resourceLockConfig, - Client: c.CoreV1(), - } - case "leases": - lock = &rl.LeaseLock{ - LeaseMeta: objectMeta, - LockConfig: resourceLockConfig, - Client: c.CoordinationV1(), - } - } - - lec := LeaderElectionConfig{ - Lock: lock, - KeyComparison: test.keyComparisonFunc, - LeaseDuration: 10 * time.Second, - Callbacks: LeaderCallbacks{ - OnNewLeader: func(l string) { - defer wg.Done() - reportedLeader = l - }, - }, - } - observedRawRecord := GetRawRecordOrDie(t, objectType, test.observedRecord) - le := &LeaderElector{ - config: lec, - observedRecord: test.observedRecord, - observedRawRecord: observedRawRecord, - observedTime: test.observedTime, - clock: clock.RealClock{}, - } - if test.expectSuccess != le.tryAcquireOrRenew(context.Background()) { - t.Errorf("unexpected result of tryAcquireOrRenew: [succeeded=%v]", !test.expectSuccess) - } - - le.observedRecord.AcquireTime = metav1.Time{} - le.observedRecord.RenewTime = metav1.Time{} - if le.observedRecord.HolderIdentity != test.outHolder { - t.Errorf("expected holder:\n\t%+v\ngot:\n\t%+v", test.outHolder, le.observedRecord.HolderIdentity) - } - if len(test.reactors) != len(c.Actions()) { - t.Errorf("wrong number of api interactions") - } - if test.transitionLeader && le.observedRecord.LeaderTransitions != 1 { - t.Errorf("leader should have transitioned but did not") - } - if !test.transitionLeader && le.observedRecord.LeaderTransitions != 0 { - t.Errorf("leader should not have transitioned but did") - } - - le.maybeReportTransition() - wg.Wait() - if reportedLeader != test.outHolder { - t.Errorf("reported leader was not the new leader. expected %q, got %q", test.outHolder, reportedLeader) - } - }) - } -} - -// Will test leader election using configmap as the resource -func TestTryAcquireOrRenewConfigMaps(t *testing.T) { - testTryAcquireOrRenew(t, "configmaps") -} - -// Will test leader election using lease as the resource -func TestTryAcquireOrRenewLeases(t *testing.T) { - testTryAcquireOrRenew(t, "leases") -} - -func TestLeaseSpecToLeaderElectionRecordRoundTrip(t *testing.T) { - holderIdentity := "foo" - leaseDurationSeconds := int32(10) - leaseTransitions := int32(1) - oldSpec := coordinationv1.LeaseSpec{ - HolderIdentity: &holderIdentity, - LeaseDurationSeconds: &leaseDurationSeconds, - AcquireTime: &metav1.MicroTime{Time: time.Now()}, - RenewTime: &metav1.MicroTime{Time: time.Now()}, - LeaseTransitions: &leaseTransitions, - } - - oldRecord := rl.LeaseSpecToLeaderElectionRecord(&oldSpec) - newSpec := rl.LeaderElectionRecordToLeaseSpec(oldRecord) - - if !equality.Semantic.DeepEqual(oldSpec, newSpec) { - t.Errorf("diff: %v", diff.ObjectReflectDiff(oldSpec, newSpec)) - } - - newRecord := rl.LeaseSpecToLeaderElectionRecord(&newSpec) - - if !equality.Semantic.DeepEqual(oldRecord, newRecord) { - t.Errorf("diff: %v", diff.ObjectReflectDiff(oldRecord, newRecord)) - } -} - -func multiLockType(t *testing.T, objectType string) (primaryType, secondaryType string) { - switch objectType { - case rl.EndpointsLeasesResourceLock: - return rl.EndpointsResourceLock, rl.LeasesResourceLock - case rl.ConfigMapsLeasesResourceLock: - return rl.ConfigMapsResourceLock, rl.LeasesResourceLock - default: - t.Fatal("unexpected objType:" + objectType) - } - return -} - -func GetRawRecordOrDie(t *testing.T, objectType string, ler rl.LeaderElectionRecord) (ret []byte) { - var err error - switch objectType { - case "endpoints", "configmaps", "leases": - ret, err = json.Marshal(ler) - if err != nil { - t.Fatalf("lock %s get raw record %v failed: %v", objectType, ler, err) - } - case "endpointsleases", "configmapsleases": - recordBytes, err := json.Marshal(ler) - if err != nil { - t.Fatalf("lock %s get raw record %v failed: %v", objectType, ler, err) - } - ret = rl.ConcatRawRecord(recordBytes, recordBytes) - default: - t.Fatal("unexpected objType:" + objectType) - } - return -} - -func testTryAcquireOrRenewMultiLock(t *testing.T, objectType string) { - future := time.Now().Add(1000 * time.Hour) - past := time.Now().Add(-1000 * time.Hour) - primaryType, secondaryType := multiLockType(t, objectType) - tests := []struct { - name string - observedRecord rl.LeaderElectionRecord - observedRawRecord []byte - observedTime time.Time - reactors []Reactor - - expectSuccess bool - transitionLeader bool - outHolder string - }{ - { - name: "acquire from no object", - reactors: []Reactor{ - { - verb: "get", - objectType: primaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, nil, errors.NewNotFound(action.(fakeclient.GetAction).GetResource().GroupResource(), action.(fakeclient.GetAction).GetName()) - }, - }, - { - verb: "create", - objectType: primaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.CreateAction).GetObject(), nil - }, - }, - { - verb: "create", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.CreateAction).GetObject(), nil - }, - }, - }, - expectSuccess: true, - outHolder: "baz", - }, - { - name: "acquire from unled old object", - reactors: []Reactor{ - { - verb: "get", - objectType: primaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, primaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{}), nil - }, - }, - { - verb: "get", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, nil, errors.NewNotFound(action.(fakeclient.GetAction).GetResource().GroupResource(), action.(fakeclient.GetAction).GetName()) - }, - }, - { - verb: "update", - objectType: primaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.UpdateAction).GetObject(), nil - }, - }, - { - verb: "get", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, nil, errors.NewNotFound(action.(fakeclient.GetAction).GetResource().GroupResource(), action.(fakeclient.GetAction).GetName()) - }, - }, - { - verb: "create", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.CreateAction).GetObject(), nil - }, - }, - }, - expectSuccess: true, - transitionLeader: true, - outHolder: "baz", - }, - { - name: "acquire from unled transition object", - reactors: []Reactor{ - { - verb: "get", - objectType: primaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, createLockObject(t, primaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{}), nil - }, - }, - { - verb: "get", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, createLockObject(t, secondaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{}), nil - }, - }, - { - verb: "update", - objectType: primaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.UpdateAction).GetObject(), nil - }, - }, - { - verb: "get", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, createLockObject(t, secondaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{}), nil - }, - }, - { - verb: "update", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.UpdateAction).GetObject(), nil - }, - }, - }, - expectSuccess: true, - transitionLeader: true, - outHolder: "baz", - }, - { - name: "acquire from led, unack old object", - reactors: []Reactor{ - { - verb: "get", - objectType: primaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, primaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: "bing"}), nil - }, - }, - { - verb: "get", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, nil, errors.NewNotFound(action.(fakeclient.GetAction).GetResource().GroupResource(), action.(fakeclient.GetAction).GetName()) - }, - }, - { - verb: "update", - objectType: primaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.UpdateAction).GetObject(), nil - }, - }, - { - verb: "get", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, secondaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: "bing"}), nil - }, - }, - { - verb: "create", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.CreateAction).GetObject(), nil - }, - }, - }, - observedRecord: rl.LeaderElectionRecord{HolderIdentity: "bing"}, - observedRawRecord: GetRawRecordOrDie(t, primaryType, rl.LeaderElectionRecord{HolderIdentity: "bing"}), - observedTime: past, - - expectSuccess: true, - transitionLeader: true, - outHolder: "baz", - }, - { - name: "acquire from led, unack transition object", - reactors: []Reactor{ - { - verb: "get", - objectType: primaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, primaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: "bing"}), nil - }, - }, - { - verb: "get", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, secondaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: "bing"}), nil - }, - }, - { - verb: "update", - objectType: primaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.UpdateAction).GetObject(), nil - }, - }, - { - verb: "get", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, secondaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: "bing"}), nil - }, - }, - { - verb: "update", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.UpdateAction).GetObject(), nil - }, - }, - }, - observedRecord: rl.LeaderElectionRecord{HolderIdentity: "bing"}, - observedRawRecord: GetRawRecordOrDie(t, objectType, rl.LeaderElectionRecord{HolderIdentity: "bing"}), - observedTime: past, - - expectSuccess: true, - transitionLeader: true, - outHolder: "baz", - }, - { - name: "acquire from conflict led, ack transition object", - reactors: []Reactor{ - { - verb: "get", - objectType: primaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, primaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: "bing"}), nil - }, - }, - { - verb: "get", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, secondaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: "baz"}), nil - }, - }, - }, - observedRecord: rl.LeaderElectionRecord{HolderIdentity: "bing"}, - observedRawRecord: GetRawRecordOrDie(t, objectType, rl.LeaderElectionRecord{HolderIdentity: "bing"}), - observedTime: future, - - expectSuccess: false, - outHolder: rl.UnknownLeader, - }, - { - name: "acquire from led, unack unknown object", - reactors: []Reactor{ - { - verb: "get", - objectType: primaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, primaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: rl.UnknownLeader}), nil - }, - }, - { - verb: "get", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, secondaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: rl.UnknownLeader}), nil - }, - }, - { - verb: "update", - objectType: primaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.UpdateAction).GetObject(), nil - }, - }, - { - verb: "get", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, secondaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: rl.UnknownLeader}), nil - }, - }, - { - verb: "update", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.UpdateAction).GetObject(), nil - }, - }, - }, - observedRecord: rl.LeaderElectionRecord{HolderIdentity: rl.UnknownLeader}, - observedRawRecord: GetRawRecordOrDie(t, objectType, rl.LeaderElectionRecord{HolderIdentity: rl.UnknownLeader}), - observedTime: past, - - expectSuccess: true, - transitionLeader: true, - outHolder: "baz", - }, - { - name: "don't acquire from led, ack old object", - reactors: []Reactor{ - { - verb: "get", - objectType: primaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, primaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: "bing"}), nil - }, - }, - { - verb: "get", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, nil, errors.NewNotFound(action.(fakeclient.GetAction).GetResource().GroupResource(), action.(fakeclient.GetAction).GetName()) - }, - }, - }, - observedRecord: rl.LeaderElectionRecord{HolderIdentity: "bing"}, - observedRawRecord: GetRawRecordOrDie(t, primaryType, rl.LeaderElectionRecord{HolderIdentity: "bing"}), - observedTime: future, - - expectSuccess: false, - outHolder: "bing", - }, - { - name: "don't acquire from led, acked new object, observe new record", - reactors: []Reactor{ - { - verb: "get", - objectType: primaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, primaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: "baz"}), nil - }, - }, - { - verb: "get", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, secondaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: "bing"}), nil - }, - }, - }, - observedRecord: rl.LeaderElectionRecord{HolderIdentity: "bing"}, - observedRawRecord: GetRawRecordOrDie(t, secondaryType, rl.LeaderElectionRecord{HolderIdentity: "bing"}), - observedTime: future, - - expectSuccess: false, - outHolder: rl.UnknownLeader, - }, - { - name: "don't acquire from led, acked new object, observe transition record", - reactors: []Reactor{ - { - verb: "get", - objectType: primaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, primaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: "bing"}), nil - }, - }, - { - verb: "get", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, secondaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: "bing"}), nil - }, - }, - }, - observedRecord: rl.LeaderElectionRecord{HolderIdentity: "bing"}, - observedRawRecord: GetRawRecordOrDie(t, objectType, rl.LeaderElectionRecord{HolderIdentity: "bing"}), - observedTime: future, - - expectSuccess: false, - outHolder: "bing", - }, - { - name: "renew already required object", - reactors: []Reactor{ - { - verb: "get", - objectType: primaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, primaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: "baz"}), nil - }, - }, - { - verb: "get", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, secondaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: "baz"}), nil - }, - }, - { - verb: "update", - objectType: primaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.UpdateAction).GetObject(), nil - }, - }, - { - verb: "get", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, createLockObject(t, secondaryType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: "baz"}), nil - }, - }, - { - verb: "update", - objectType: secondaryType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.UpdateAction).GetObject(), nil - }, - }, - }, - observedRecord: rl.LeaderElectionRecord{HolderIdentity: "baz"}, - observedRawRecord: GetRawRecordOrDie(t, objectType, rl.LeaderElectionRecord{HolderIdentity: "baz"}), - observedTime: future, - - expectSuccess: true, - outHolder: "baz", - }, - } - - for i := range tests { - test := &tests[i] - t.Run(test.name, func(t *testing.T) { - // OnNewLeader is called async so we have to wait for it. - var wg sync.WaitGroup - wg.Add(1) - var reportedLeader string - var lock rl.Interface - - objectMeta := metav1.ObjectMeta{Namespace: "foo", Name: "bar"} - resourceLockConfig := rl.ResourceLockConfig{ - Identity: "baz", - EventRecorder: &record.FakeRecorder{}, - } - c := &fake.Clientset{} - for _, reactor := range test.reactors { - c.AddReactor(reactor.verb, reactor.objectType, reactor.reaction) - } - c.AddReactor("*", "*", func(action fakeclient.Action) (bool, runtime.Object, error) { - t.Errorf("unreachable action. testclient called too many times: %+v", action) - return true, nil, fmt.Errorf("unreachable action") - }) - - switch objectType { - case rl.EndpointsLeasesResourceLock: - lock = &rl.MultiLock{ - Primary: &rl.EndpointsLock{ - EndpointsMeta: objectMeta, - LockConfig: resourceLockConfig, - Client: c.CoreV1(), - }, - Secondary: &rl.LeaseLock{ - LeaseMeta: objectMeta, - LockConfig: resourceLockConfig, - Client: c.CoordinationV1(), - }, - } - case rl.ConfigMapsLeasesResourceLock: - lock = &rl.MultiLock{ - Primary: &rl.ConfigMapLock{ - ConfigMapMeta: objectMeta, - LockConfig: resourceLockConfig, - Client: c.CoreV1(), - }, - Secondary: &rl.LeaseLock{ - LeaseMeta: objectMeta, - LockConfig: resourceLockConfig, - Client: c.CoordinationV1(), - }, - } - } - - lec := LeaderElectionConfig{ - Lock: lock, - LeaseDuration: 10 * time.Second, - Callbacks: LeaderCallbacks{ - OnNewLeader: func(l string) { - defer wg.Done() - reportedLeader = l - }, - }, - } - le := &LeaderElector{ - config: lec, - observedRecord: test.observedRecord, - observedRawRecord: test.observedRawRecord, - observedTime: test.observedTime, - clock: clock.RealClock{}, - } - if test.expectSuccess != le.tryAcquireOrRenew(context.Background()) { - t.Errorf("unexpected result of tryAcquireOrRenew: [succeeded=%v]", !test.expectSuccess) - } - - le.observedRecord.AcquireTime = metav1.Time{} - le.observedRecord.RenewTime = metav1.Time{} - if le.observedRecord.HolderIdentity != test.outHolder { - t.Errorf("expected holder:\n\t%+v\ngot:\n\t%+v", test.outHolder, le.observedRecord.HolderIdentity) - } - if len(test.reactors) != len(c.Actions()) { - t.Errorf("wrong number of api interactions") - } - if test.transitionLeader && le.observedRecord.LeaderTransitions != 1 { - t.Errorf("leader should have transitioned but did not") - } - if !test.transitionLeader && le.observedRecord.LeaderTransitions != 0 { - t.Errorf("leader should not have transitioned but did") - } - - le.maybeReportTransition() - wg.Wait() - if reportedLeader != test.outHolder { - t.Errorf("reported leader was not the new leader. expected %q, got %q", test.outHolder, reportedLeader) - } - }) - } -} - -// Will test leader election using endpointsleases as the resource -func TestTryAcquireOrRenewEndpointsLeases(t *testing.T) { - testTryAcquireOrRenewMultiLock(t, "endpointsleases") -} - -// Will test leader election using configmapsleases as the resource -func TestTryAcquireOrRenewConfigMapsLeases(t *testing.T) { - testTryAcquireOrRenewMultiLock(t, "configmapsleases") -} - -func testReleaseLease(t *testing.T, objectType string) { - tests := []struct { - name string - observedRecord rl.LeaderElectionRecord - observedTime time.Time - reactors []Reactor - - expectSuccess bool - transitionLeader bool - outHolder string - }{ - { - name: "release acquired lock from no object", - reactors: []Reactor{ - { - verb: "get", - objectType: objectType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - // nolint: lll - return true, nil, errors.NewNotFound(action.(fakeclient.GetAction).GetResource().GroupResource(), action.(fakeclient.GetAction).GetName()) - }, - }, - { - verb: "create", - objectType: objectType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.CreateAction).GetObject(), nil - }, - }, - { - verb: "update", - objectType: objectType, - reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - return true, action.(fakeclient.UpdateAction).GetObject(), nil - }, - }, - }, - expectSuccess: true, - outHolder: "", - }, - } - - for i := range tests { - test := &tests[i] - t.Run(test.name, func(t *testing.T) { - // OnNewLeader is called async so we have to wait for it. - var wg sync.WaitGroup - wg.Add(1) - var reportedLeader string - var lock rl.Interface - - objectMeta := metav1.ObjectMeta{Namespace: "foo", Name: "bar"} - resourceLockConfig := rl.ResourceLockConfig{ - Identity: "baz", - EventRecorder: &record.FakeRecorder{}, - } - c := &fake.Clientset{} - for _, reactor := range test.reactors { - c.AddReactor(reactor.verb, objectType, reactor.reaction) - } - c.AddReactor("*", "*", func(action fakeclient.Action) (bool, runtime.Object, error) { - t.Errorf("unreachable action. testclient called too many times: %+v", action) - return true, nil, fmt.Errorf("unreachable action") - }) - - switch objectType { - case "endpoints": - lock = &rl.EndpointsLock{ - EndpointsMeta: objectMeta, - LockConfig: resourceLockConfig, - Client: c.CoreV1(), - } - case "configmaps": - lock = &rl.ConfigMapLock{ - ConfigMapMeta: objectMeta, - LockConfig: resourceLockConfig, - Client: c.CoreV1(), - } - case "leases": - lock = &rl.LeaseLock{ - LeaseMeta: objectMeta, - LockConfig: resourceLockConfig, - Client: c.CoordinationV1(), - } - } - - lec := LeaderElectionConfig{ - Lock: lock, - LeaseDuration: 10 * time.Second, - Callbacks: LeaderCallbacks{ - OnNewLeader: func(l string) { - defer wg.Done() - reportedLeader = l - }, - }, - } - observedRawRecord := GetRawRecordOrDie(t, objectType, test.observedRecord) - le := &LeaderElector{ - config: lec, - observedRecord: test.observedRecord, - observedRawRecord: observedRawRecord, - observedTime: test.observedTime, - clock: clock.RealClock{}, - } - if !le.tryAcquireOrRenew(context.Background()) { - t.Errorf("unexpected result of tryAcquireOrRenew: [succeeded=%v]", true) - } - - le.maybeReportTransition() - - // Wait for a response to the leader transition, and add 1 so that we can track the final transition. - wg.Wait() - wg.Add(1) - - if test.expectSuccess != le.release() { - t.Errorf("unexpected result of release: [succeeded=%v]", !test.expectSuccess) - } - - le.observedRecord.AcquireTime = metav1.Time{} - le.observedRecord.RenewTime = metav1.Time{} - if le.observedRecord.HolderIdentity != test.outHolder { - t.Errorf("expected holder:\n\t%+v\ngot:\n\t%+v", test.outHolder, le.observedRecord.HolderIdentity) - } - if len(test.reactors) != len(c.Actions()) { - t.Errorf("wrong number of api interactions") - } - if test.transitionLeader && le.observedRecord.LeaderTransitions != 1 { - t.Errorf("leader should have transitioned but did not") - } - if !test.transitionLeader && le.observedRecord.LeaderTransitions != 0 { - t.Errorf("leader should not have transitioned but did") - } - le.maybeReportTransition() - wg.Wait() - if reportedLeader != test.outHolder { - t.Errorf("reported leader was not the new leader. expected %q, got %q", test.outHolder, reportedLeader) - } - }) - } -} - -// Will test leader election using endpoints as the resource -func TestReleaseLeaseEndpoints(t *testing.T) { - testReleaseLease(t, "endpoints") -} - -// Will test leader election using endpoints as the resource -func TestReleaseLeaseConfigMaps(t *testing.T) { - testReleaseLease(t, "configmaps") -} - -// Will test leader election using endpoints as the resource -func TestReleaseLeaseLeases(t *testing.T) { - testReleaseLease(t, "leases") -} - -func TestReleaseOnCancellation_Endpoints(t *testing.T) { - testReleaseOnCancellation(t, "endpoints") -} - -func TestReleaseOnCancellation_ConfigMaps(t *testing.T) { - testReleaseOnCancellation(t, "configmaps") -} - -func TestReleaseOnCancellation_Leases(t *testing.T) { - testReleaseOnCancellation(t, "leases") -} - -func testReleaseOnCancellation(t *testing.T, objectType string) { - var ( - onNewLeader = make(chan struct{}) - onRenewCalled = make(chan struct{}) - onRenewResume = make(chan struct{}) - onRelease = make(chan struct{}) - - lockObj runtime.Object - updates int - ) - - resourceLockConfig := rl.ResourceLockConfig{ - Identity: "baz", - EventRecorder: &record.FakeRecorder{}, - } - c := &fake.Clientset{} - - c.AddReactor("get", objectType, func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - if lockObj != nil { - return true, lockObj, nil - } - return true, nil, errors.NewNotFound(action.(fakeclient.GetAction).GetResource().GroupResource(), action.(fakeclient.GetAction).GetName()) - }) - - // create lock - c.AddReactor("create", objectType, func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - lockObj = action.(fakeclient.CreateAction).GetObject() - return true, lockObj, nil - }) - - c.AddReactor("update", objectType, func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) { - updates++ - - // Second update (first renew) should return our canceled error - // FakeClient doesn't do anything with the context so we're doing this ourselves - if updates == 2 { - close(onRenewCalled) - <-onRenewResume - return true, nil, context.Canceled - } else if updates == 3 { - close(onRelease) - } - - lockObj = action.(fakeclient.UpdateAction).GetObject() - return true, lockObj, nil - }) - - c.AddReactor("*", "*", func(action fakeclient.Action) (bool, runtime.Object, error) { - t.Errorf("unreachable action. testclient called too many times: %+v", action) - return true, nil, fmt.Errorf("unreachable action") - }) - - lock, err := rl.New(objectType, "foo", "bar", c.CoreV1(), c.CoordinationV1(), resourceLockConfig) - if err != nil { - t.Fatal("resourcelock.New() = ", err) - } - - lec := LeaderElectionConfig{ - Lock: lock, - LeaseDuration: 15 * time.Second, - RenewDeadline: 2 * time.Second, - RetryPeriod: 1 * time.Second, - - // This is what we're testing - ReleaseOnCancel: true, - - Callbacks: LeaderCallbacks{ - OnNewLeader: func(identity string) {}, - OnStoppedLeading: func() {}, - OnStartedLeading: func(context.Context) { - close(onNewLeader) - }, - }, - } - - elector, err := NewLeaderElector(lec) - if err != nil { - t.Fatal("Failed to create leader elector: ", err) - } - - ctx, cancel := context.WithCancel(context.Background()) - - go elector.Run(ctx) - - // Wait for us to become the leader - select { - case <-onNewLeader: - case <-time.After(10 * time.Second): - t.Fatal("failed to become the leader") - } - - // Wait for renew (update) to be invoked - select { - case <-onRenewCalled: - case <-time.After(10 * time.Second): - t.Fatal("the elector failed to renew the lock") - } - - // Cancel the context - stopping the elector while - // it's running - cancel() - - // Resume the update call to return the cancellation - // which should trigger the release flow - close(onRenewResume) - - select { - case <-onRelease: - case <-time.After(10 * time.Second): - t.Fatal("the lock was not released") - } -} diff --git a/pilot/pkg/leaderelection/k8sleaderelection/metrics.go b/pilot/pkg/leaderelection/k8sleaderelection/metrics.go deleted file mode 100644 index c532a5b93..000000000 --- a/pilot/pkg/leaderelection/k8sleaderelection/metrics.go +++ /dev/null @@ -1,108 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package k8sleaderelection - -import ( - "sync" -) - -// This file provides abstractions for setting the provider (e.g., prometheus) -// of metrics. -type leaderMetricsAdapter interface { - leaderOn(name string) - leaderOff(name string) -} - -// GaugeMetric represents a single numerical value that can arbitrarily go up -// and down. -type SwitchMetric interface { - On(name string) - Off(name string) -} - -type noopMetric struct{} - -func (noopMetric) On(name string) {} -func (noopMetric) Off(name string) {} - -// defaultLeaderMetrics expects the caller to lock before setting any metrics. -type defaultLeaderMetrics struct { - // leader's value indicates if the current process is the owner of name lease - leader SwitchMetric -} - -func (m *defaultLeaderMetrics) leaderOn(name string) { - if m == nil { - return - } - m.leader.On(name) -} - -func (m *defaultLeaderMetrics) leaderOff(name string) { - if m == nil { - return - } - m.leader.Off(name) -} - -type noMetrics struct{} - -func (noMetrics) leaderOn(name string) {} -func (noMetrics) leaderOff(name string) {} - -// MetricsProvider generates various metrics used by the leader election. -type MetricsProvider interface { - NewLeaderMetric() SwitchMetric -} - -type noopMetricsProvider struct{} - -func (noopMetricsProvider) NewLeaderMetric() SwitchMetric { - return noopMetric{} -} - -var globalMetricsFactory = leaderMetricsFactory{ - metricsProvider: noopMetricsProvider{}, -} - -type leaderMetricsFactory struct { - metricsProvider MetricsProvider - - onlyOnce sync.Once -} - -func (f *leaderMetricsFactory) setProvider(mp MetricsProvider) { - f.onlyOnce.Do(func() { - f.metricsProvider = mp - }) -} - -func (f *leaderMetricsFactory) newLeaderMetrics() leaderMetricsAdapter { - mp := f.metricsProvider - if mp == (noopMetricsProvider{}) { - return noMetrics{} - } - return &defaultLeaderMetrics{ - leader: mp.NewLeaderMetric(), - } -} - -// SetProvider sets the metrics provider for all subsequently created work -// queues. Only the first call has an effect. -func SetProvider(metricsProvider MetricsProvider) { - globalMetricsFactory.setProvider(metricsProvider) -} diff --git a/pilot/pkg/leaderelection/leaderelection.go b/pilot/pkg/leaderelection/leaderelection.go deleted file mode 100644 index a265fc935..000000000 --- a/pilot/pkg/leaderelection/leaderelection.go +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package leaderelection - -import ( - "context" - "fmt" - "os" - "strings" - "sync" - "time" -) - -import ( - "go.uber.org/atomic" - "istio.io/pkg/log" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/leaderelection/k8sleaderelection" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/leaderelection/k8sleaderelection/k8sresourcelock" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/revisions" -) - -// Various locks used throughout the code -const ( - NamespaceController = "istio-namespace-controller-election" - ServiceExportController = "istio-serviceexport-controller-election" - // This holds the legacy name to not conflict with older control plane deployments which are just - // doing the ingress syncing. - IngressController = "istio-leader" - // GatewayStatusController controls the status of gateway.networking.k8s.io objects. For the v1alpha1 - // this was formally "istio-gateway-leader"; because they are a different API group we need a different - // election to ensure we do not only handle one or the other. - GatewayStatusController = "istio-gateway-status-leader" - // GatewayDeploymentController controls the Deployment/Service generation from Gateways. This is - // separate from GatewayStatusController to allow running in a separate process (for low priv). - GatewayDeploymentController = "istio-gateway-deployment-leader" - StatusController = "istio-status-leader" - AnalyzeController = "istio-analyze-leader" -) - -// Leader election key prefix for remote istiod managed clusters -const remoteIstiodPrefix = "^" - -type LeaderElection struct { - namespace string - name string - runFns []func(stop <-chan struct{}) - client kubernetes.Interface - ttl time.Duration - - // Criteria to determine leader priority. - revision string - remote bool - prioritized bool - defaultWatcher revisions.DefaultWatcher - - // Records which "cycle" the election is on. This is incremented each time an election is won and then lost - // This is mostly just for testing - cycle *atomic.Int32 - electionID string - - // Store as field for testing - le *k8sleaderelection.LeaderElector - mu sync.RWMutex -} - -// Run will start leader election, calling all runFns when we become the leader. -func (l *LeaderElection) Run(stop <-chan struct{}) { - if l.prioritized && l.defaultWatcher != nil { - go l.defaultWatcher.Run(stop) - } - for { - le, err := l.create() - if err != nil { - // This should never happen; errors are only from invalid input and the input is not user modifiable - panic("LeaderElection creation failed: " + err.Error()) - } - l.mu.Lock() - l.le = le - l.cycle.Inc() - l.mu.Unlock() - ctx, cancel := context.WithCancel(context.Background()) - go func() { - <-stop - cancel() - }() - le.Run(ctx) - select { - case <-stop: - // We were told to stop explicitly. Exit now - return - default: - cancel() - // Otherwise, we may have lost our lock. This can happen when the default revision changes and steals - // the lock from us. - log.Infof("Leader election cycle %v lost. Trying again", l.cycle.Load()) - } - } -} - -func (l *LeaderElection) create() (*k8sleaderelection.LeaderElector, error) { - callbacks := k8sleaderelection.LeaderCallbacks{ - OnStartedLeading: func(ctx context.Context) { - log.Infof("leader election lock obtained: %v", l.electionID) - for _, f := range l.runFns { - go f(ctx.Done()) - } - }, - OnStoppedLeading: func() { - log.Infof("leader election lock lost: %v", l.electionID) - }, - } - - key := l.revision - if l.remote { - key = remoteIstiodPrefix + key - } - lock := k8sresourcelock.ConfigMapLock{ - ConfigMapMeta: metaV1.ObjectMeta{Namespace: l.namespace, Name: l.electionID}, - Client: l.client.CoreV1(), - LockConfig: k8sresourcelock.ResourceLockConfig{ - Identity: l.name, - Key: key, - }, - } - - config := k8sleaderelection.LeaderElectionConfig{ - Lock: &lock, - LeaseDuration: l.ttl, - RenewDeadline: l.ttl / 2, - RetryPeriod: l.ttl / 4, - Callbacks: callbacks, - // When Pilot exits, the lease will be dropped. This is more likely to lead to a case where - // to instances are both considered the leaders. As such, if this is intended to be use for mission-critical - // usages (rather than avoiding duplication of work), this may need to be re-evaluated. - ReleaseOnCancel: true, - } - - if l.prioritized { - // Function to use to decide whether this leader should steal the existing lock. - config.KeyComparison = func(leaderKey string) bool { - return LocationPrioritizedComparison(leaderKey, l) - } - } - - return k8sleaderelection.NewLeaderElector(config) -} - -func LocationPrioritizedComparison(currentLeaderRevision string, l *LeaderElection) bool { - var currentLeaderRemote bool - if currentLeaderRemote = strings.HasPrefix(currentLeaderRevision, remoteIstiodPrefix); currentLeaderRemote { - currentLeaderRevision = strings.TrimPrefix(currentLeaderRevision, remoteIstiodPrefix) - } - defaultRevision := l.defaultWatcher.GetDefault() - if l.revision != currentLeaderRevision && defaultRevision != "" && defaultRevision == l.revision { - // Always steal the lock if the new one is the default revision and the current one is not - return true - } - // Otherwise steal the lock if the new one and the current one are the same revision, but new one is local and current is remote - return l.revision == currentLeaderRevision && !l.remote && currentLeaderRemote -} - -// AddRunFunction registers a function to run when we are the leader. These will be run asynchronously. -// To avoid running when not a leader, functions should respect the stop channel. -func (l *LeaderElection) AddRunFunction(f func(stop <-chan struct{})) *LeaderElection { - l.runFns = append(l.runFns, f) - return l -} - -func NewLeaderElection(namespace, name, electionID, revision string, client kube.Client) *LeaderElection { - return NewLeaderElectionMulticluster(namespace, name, electionID, revision, false, client) -} - -func NewLeaderElectionMulticluster(namespace, name, electionID, revision string, remote bool, client kube.Client) *LeaderElection { - var watcher revisions.DefaultWatcher - if features.PrioritizedLeaderElection { - watcher = revisions.NewDefaultWatcher(client, revision) - } - if name == "" { - hn, _ := os.Hostname() - name = fmt.Sprintf("unknown-%s", hn) - } - return &LeaderElection{ - namespace: namespace, - name: name, - client: client, - electionID: electionID, - revision: revision, - remote: remote, - prioritized: features.PrioritizedLeaderElection, - defaultWatcher: watcher, - // Default to a 30s ttl. Overridable for tests - ttl: time.Second * 30, - cycle: atomic.NewInt32(0), - mu: sync.RWMutex{}, - } -} - -func (l *LeaderElection) isLeader() bool { - l.mu.RLock() - defer l.mu.RUnlock() - if l.le == nil { - return false - } - return l.le.IsLeader() -} diff --git a/pilot/pkg/leaderelection/leaderelection_test.go b/pilot/pkg/leaderelection/leaderelection_test.go deleted file mode 100644 index abc5fb561..000000000 --- a/pilot/pkg/leaderelection/leaderelection_test.go +++ /dev/null @@ -1,321 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package leaderelection - -import ( - "context" - "fmt" - "testing" - "time" -) - -import ( - "go.uber.org/atomic" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/fake" - k8stesting "k8s.io/client-go/testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/revisions" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -const testLock = "test-lock" - -func createElection(t *testing.T, - name string, revision string, - watcher revisions.DefaultWatcher, - prioritized, expectLeader bool, - client kubernetes.Interface, fns ...func(stop <-chan struct{})) (*LeaderElection, chan struct{}) { - return createElectionMulticluster(t, name, revision, false, watcher, prioritized, expectLeader, client, fns...) -} - -func createElectionMulticluster(t *testing.T, - name, revision string, - remote bool, - watcher revisions.DefaultWatcher, - prioritized, expectLeader bool, - client kubernetes.Interface, fns ...func(stop <-chan struct{})) (*LeaderElection, chan struct{}) { - t.Helper() - l := &LeaderElection{ - namespace: "ns", - name: name, - electionID: testLock, - client: client, - revision: revision, - remote: remote, - prioritized: prioritized, - defaultWatcher: watcher, - ttl: time.Second, - cycle: atomic.NewInt32(0), - } - gotLeader := make(chan struct{}) - l.AddRunFunction(func(stop <-chan struct{}) { - gotLeader <- struct{}{} - }) - for _, fn := range fns { - l.AddRunFunction(fn) - } - stop := make(chan struct{}) - go l.Run(stop) - - retry.UntilOrFail(t, func() bool { - return l.isLeader() == expectLeader - }, retry.Converge(5), retry.Delay(time.Millisecond*100), retry.Timeout(time.Second*10)) - return l, stop -} - -type fakeDefaultWatcher struct { - defaultRevision string -} - -func (w *fakeDefaultWatcher) setDefaultRevision(r string) { - w.defaultRevision = r -} - -func (w *fakeDefaultWatcher) Run(stop <-chan struct{}) { -} - -func (w *fakeDefaultWatcher) HasSynced() bool { - return true -} - -func (w *fakeDefaultWatcher) GetDefault() string { - return w.defaultRevision -} - -func (w *fakeDefaultWatcher) AddHandler(handler revisions.DefaultHandler) { - panic("unimplemented") -} - -func TestLeaderElection(t *testing.T) { - client := fake.NewSimpleClientset() - watcher := &fakeDefaultWatcher{} - // First pod becomes the leader - _, stop := createElection(t, "pod1", "", watcher, true, true, client) - // A new pod is not the leader - _, stop2 := createElection(t, "pod2", "", watcher, true, false, client) - close(stop2) - close(stop) -} - -func TestPrioritizedLeaderElection(t *testing.T) { - client := fake.NewSimpleClientset() - watcher := &fakeDefaultWatcher{defaultRevision: "red"} - - // First pod, revision "green" becomes the leader, but is not the default revision - _, stop := createElection(t, "pod1", "green", watcher, true, true, client) - // Second pod, revision "red", steals the leader lock from "green" since it is the default revision - _, stop2 := createElection(t, "pod2", "red", watcher, true, true, client) - // Third pod with revision "red" comes in and cannot take the lock since another revision with "red" has it - _, stop3 := createElection(t, "pod3", "red", watcher, true, false, client) - // Fourth pod with revision "green" cannot take the lock since a revision with "red" has it. - _, stop4 := createElection(t, "pod4", "green", watcher, true, false, client) - close(stop2) - close(stop3) - close(stop4) - // Now that revision "green" has stopped acting as leader, revision "red" should be able to claim lock. - _, stop5 := createElection(t, "pod2", "red", watcher, true, true, client) - close(stop5) - close(stop) - // Revision "green" can reclaim once "red" releases. - _, stop6 := createElection(t, "pod4", "green", watcher, true, true, client) - // Test that "red" doesn't steal lock if "prioritized" is disabled - _, stop7 := createElection(t, "pod5", "red", watcher, false, false, client) - close(stop6) - - close(stop7) -} - -func TestMulticlusterLeaderElection(t *testing.T) { - client := fake.NewSimpleClientset() - watcher := &fakeDefaultWatcher{} - // First remote pod becomes the leader - _, stop := createElectionMulticluster(t, "pod1", "", true, watcher, false, true, client) - // A new local pod cannot become leader - _, stop2 := createElectionMulticluster(t, "pod2", "", false, watcher, false, false, client) - // A new remote pod cannot become leader - _, stop3 := createElectionMulticluster(t, "pod3", "", true, watcher, false, false, client) - close(stop3) - close(stop2) - close(stop) -} - -func TestPrioritizedMulticlusterLeaderElection(t *testing.T) { - client := fake.NewSimpleClientset() - watcher := &fakeDefaultWatcher{defaultRevision: "red"} - - // First pod, revision "green" becomes the remote leader - _, stop := createElectionMulticluster(t, "pod1", "green", true, watcher, true, true, client) - // Second pod, revision "red", steals the leader lock from "green" since it is the default revision - _, stop2 := createElectionMulticluster(t, "pod2", "red", true, watcher, true, true, client) - // Third pod with revision "red" comes in and can take the lock since it is a local revision "red" - _, stop3 := createElectionMulticluster(t, "pod3", "red", false, watcher, true, true, client) - // Fourth pod with revision "red" cannot take the lock since it is remote - _, stop4 := createElectionMulticluster(t, "pod4", "red", true, watcher, true, false, client) - close(stop4) - close(stop3) - close(stop2) - close(stop) -} - -func SimpleRevisionComparison(currentLeaderRevision string, l *LeaderElection) bool { - // Old key comparison impl for interoperablilty testing - defaultRevision := l.defaultWatcher.GetDefault() - return l.revision != currentLeaderRevision && - // empty default revision indicates that there is no default set - defaultRevision != "" && defaultRevision == l.revision -} - -type LeaderComparison func(string, *LeaderElection) bool - -type instance struct { - revision string - remote bool - comp string -} - -func (i instance) GetComp() (LeaderComparison, string) { - key := i.revision - switch i.comp { - case "location": - if i.remote { - key = remoteIstiodPrefix + key - } - return LocationPrioritizedComparison, key - case "simple": - return SimpleRevisionComparison, key - default: - panic("unknown comparison type") - } -} - -// TestPrioritizationCycles -func TestPrioritizationCycles(t *testing.T) { - cases := []instance{} - for _, rev := range []string{"", "default", "not-default"} { - for _, loc := range []bool{false, true} { - for _, comp := range []string{"location", "simple"} { - cases = append(cases, instance{ - revision: rev, - remote: loc, - comp: comp, - }) - } - } - } - - for _, start := range cases { - t.Run(fmt.Sprint(start), func(t *testing.T) { - checkCycles(t, start, cases, nil) - }) - } -} - -func alreadyHit(cur instance, chain []instance) bool { - for _, cc := range chain { - if cur == cc { - return true - } - } - return false -} - -func checkCycles(t *testing.T, start instance, cases []instance, chain []instance) { - if alreadyHit(start, chain) { - t.Fatalf("cycle on leader election: cur %v, chain %v", start, chain) - } - for _, nextHop := range cases { - next := LeaderElection{ - remote: nextHop.remote, - defaultWatcher: &fakeDefaultWatcher{defaultRevision: "default"}, - revision: nextHop.revision, - } - cmpFunc, key := start.GetComp() - if cmpFunc(key, &next) { - nc := append([]instance{}, chain...) - nc = append(nc, start) - checkCycles(t, nextHop, cases, nc) - } - } -} - -func TestLeaderElectionConfigMapRemoved(t *testing.T) { - client := fake.NewSimpleClientset() - watcher := &fakeDefaultWatcher{} - _, stop := createElection(t, "pod1", "", watcher, true, true, client) - if err := client.CoreV1().ConfigMaps("ns").Delete(context.TODO(), testLock, v1.DeleteOptions{}); err != nil { - t.Fatal(err) - } - retry.UntilSuccessOrFail(t, func() error { - l, err := client.CoreV1().ConfigMaps("ns").List(context.TODO(), v1.ListOptions{}) - if err != nil { - return err - } - if len(l.Items) != 1 { - return fmt.Errorf("got unexpected config map entry: %v", l.Items) - } - return nil - }) - close(stop) -} - -func TestLeaderElectionNoPermission(t *testing.T) { - client := fake.NewSimpleClientset() - watcher := &fakeDefaultWatcher{} - allowRbac := atomic.NewBool(true) - client.Fake.PrependReactor("update", "*", func(action k8stesting.Action) (bool, runtime.Object, error) { - if allowRbac.Load() { - return false, nil, nil - } - return true, nil, fmt.Errorf("nope, out of luck") - }) - - completions := atomic.NewInt32(0) - l, stop := createElection(t, "pod1", "", watcher, true, true, client, func(stop <-chan struct{}) { - completions.Add(1) - }) - // Expect to run once - expectInt(t, completions.Load, 1) - - // drop RBAC permssions to update the configmap - // This simulates loosing an active lease - allowRbac.Store(false) - - // We should start a new cycle at this point - expectInt(t, l.cycle.Load, 2) - - // Add configmap permission back - allowRbac.Store(true) - - // We should get the leader lock back - expectInt(t, completions.Load, 2) - - close(stop) -} - -func expectInt(t *testing.T, f func() int32, expected int32) { - t.Helper() - retry.UntilSuccessOrFail(t, func() error { - got := f() - if got != expected { - return fmt.Errorf("unexpected count: %v, want %v", got, expected) - } - return nil - }, retry.Timeout(time.Second)) -} diff --git a/pilot/pkg/model/addressmap.go b/pilot/pkg/model/addressmap.go deleted file mode 100644 index 84a993ab6..000000000 --- a/pilot/pkg/model/addressmap.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "sync" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/cluster" -) - -// AddressMap provides a thread-safe mapping of addresses for each Kubernetes cluster. -type AddressMap struct { - // Addresses hold the underlying map. Most code should only access this through the available methods. - // Should only be used by tests and construction/initialization logic, where there is no concern - // for race conditions. - Addresses map[cluster.ID][]string - - // NOTE: The copystructure library is not able to copy unexported fields, so the mutex will not be copied. - mutex sync.RWMutex -} - -func (m *AddressMap) IsEmpty() bool { - if m == nil { - return true - } - m.mutex.RLock() - defer m.mutex.RUnlock() - - return len(m.Addresses) == 0 -} - -func (m *AddressMap) DeepCopy() AddressMap { - return AddressMap{ - Addresses: m.GetAddresses(), - } -} - -// GetAddresses returns the mapping of clusters to addresses. -func (m *AddressMap) GetAddresses() map[cluster.ID][]string { - if m == nil { - return nil - } - - m.mutex.RLock() - defer m.mutex.RUnlock() - - if m.Addresses == nil { - return nil - } - - out := make(map[cluster.ID][]string) - for k, v := range m.Addresses { - if v == nil { - out[k] = nil - } else { - out[k] = append([]string{}, v...) - } - } - return out -} - -// SetAddresses sets the addresses per cluster. -func (m *AddressMap) SetAddresses(addrs map[cluster.ID][]string) { - if len(addrs) == 0 { - addrs = nil - } - - m.mutex.Lock() - m.Addresses = addrs - m.mutex.Unlock() -} - -func (m *AddressMap) GetAddressesFor(c cluster.ID) []string { - if m == nil { - return nil - } - - m.mutex.RLock() - defer m.mutex.RUnlock() - - if m.Addresses == nil { - return nil - } - - // Copy the Addresses array. - return append([]string{}, m.Addresses[c]...) -} - -func (m *AddressMap) SetAddressesFor(c cluster.ID, addresses []string) *AddressMap { - m.mutex.Lock() - defer m.mutex.Unlock() - - if len(addresses) == 0 { - // Setting an empty array for the cluster. Remove the entry for the cluster if it exists. - if m.Addresses != nil { - delete(m.Addresses, c) - - // Delete the map if there's nothing left. - if len(m.Addresses) == 0 { - m.Addresses = nil - } - } - } else { - // Create the map if if doesn't already exist. - if m.Addresses == nil { - m.Addresses = make(map[cluster.ID][]string) - } - m.Addresses[c] = addresses - } - return m -} - -func (m *AddressMap) AddAddressesFor(c cluster.ID, addresses []string) *AddressMap { - if len(addresses) == 0 { - return m - } - - m.mutex.Lock() - defer m.mutex.Unlock() - - // Create the map if nil. - if m.Addresses == nil { - m.Addresses = make(map[cluster.ID][]string) - } - - m.Addresses[c] = append(m.Addresses[c], addresses...) - return m -} - -func (m *AddressMap) ForEach(fn func(c cluster.ID, addresses []string)) { - if m == nil { - return - } - - m.mutex.RLock() - defer m.mutex.RUnlock() - - if m.Addresses == nil { - return - } - - for c, addresses := range m.Addresses { - fn(c, addresses) - } -} diff --git a/pilot/pkg/model/addressmap_test.go b/pilot/pkg/model/addressmap_test.go deleted file mode 100644 index 4a415a37a..000000000 --- a/pilot/pkg/model/addressmap_test.go +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model_test - -import ( - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" -) - -const ( - c1ID = "cluster-1" - c2ID = "cluster-2" -) - -var ( - c1Addresses = []string{"1.1.1.1", "1.1.1.2"} - c2Addresses = []string{"2.1.1.1", "2.1.1.2"} -) - -func TestAddressMapIsEmpty(t *testing.T) { - cases := []struct { - name string - newMap func() *model.AddressMap - expected bool - }{ - { - name: "created empty", - newMap: func() *model.AddressMap { - return &model.AddressMap{} - }, - expected: true, - }, - { - name: "set nil addresses", - newMap: func() *model.AddressMap { - m := model.AddressMap{} - m.SetAddressesFor(c1ID, nil) - return &m - }, - expected: true, - }, - { - name: "set empty addresses", - newMap: func() *model.AddressMap { - m := model.AddressMap{} - m.SetAddressesFor(c1ID, make([]string, 0)) - return &m - }, - expected: true, - }, - { - name: "set addresses", - newMap: func() *model.AddressMap { - m := model.AddressMap{} - m.SetAddressesFor(c1ID, c1Addresses) - return &m - }, - expected: false, - }, - { - name: "add nil addresses", - newMap: func() *model.AddressMap { - m := model.AddressMap{} - m.AddAddressesFor(c1ID, nil) - return &m - }, - expected: true, - }, - { - name: "add empty addresses", - newMap: func() *model.AddressMap { - m := model.AddressMap{} - m.AddAddressesFor(c1ID, make([]string, 0)) - return &m - }, - expected: true, - }, - { - name: "add addresses", - newMap: func() *model.AddressMap { - m := model.AddressMap{} - m.AddAddressesFor(c1ID, c1Addresses) - return &m - }, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - g := NewWithT(t) - g.Expect(c.newMap().IsEmpty()).To(Equal(c.expected)) - }) - } -} - -func TestAddressMapGetAddressesFor(t *testing.T) { - g := NewWithT(t) - - m := model.AddressMap{ - Addresses: map[cluster.ID][]string{ - c1ID: c1Addresses, - c2ID: c2Addresses, - }, - } - - g.Expect(m.GetAddressesFor(c1ID)).To(Equal(c1Addresses)) - g.Expect(m.GetAddressesFor(c2ID)).To(Equal(c2Addresses)) -} - -func TestAddressMapSetAddressesFor(t *testing.T) { - g := NewWithT(t) - - m := model.AddressMap{} - m.SetAddressesFor(c1ID, c1Addresses) - m.SetAddressesFor(c2ID, c2Addresses) - - g.Expect(m.GetAddressesFor(c1ID)).To(Equal(c1Addresses)) - g.Expect(m.GetAddressesFor(c2ID)).To(Equal(c2Addresses)) -} - -func TestAddressMapAddAddressesFor(t *testing.T) { - g := NewWithT(t) - - m := model.AddressMap{} - m.SetAddressesFor(c1ID, c1Addresses) - m.AddAddressesFor(c1ID, []string{"1.1.1.3", "1.1.1.4"}) - - g.Expect(m.GetAddressesFor(c1ID)).To(Equal([]string{"1.1.1.1", "1.1.1.2", "1.1.1.3", "1.1.1.4"})) -} - -func TestAddressMapForEach(t *testing.T) { - g := NewWithT(t) - - m := model.AddressMap{ - Addresses: map[cluster.ID][]string{ - c1ID: c1Addresses, - c2ID: c2Addresses, - }, - } - - found := make(map[cluster.ID][]string) - m.ForEach(func(id cluster.ID, addrs []string) { - found[id] = addrs - }) - - g.Expect(found[c1ID]).To(Equal(c1Addresses)) - g.Expect(found[c2ID]).To(Equal(c2Addresses)) -} diff --git a/pilot/pkg/model/authentication.go b/pilot/pkg/model/authentication.go deleted file mode 100644 index 9b18a42cb..000000000 --- a/pilot/pkg/model/authentication.go +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "crypto/md5" - "fmt" - "strings" - "time" -) - -import ( - "istio.io/api/security/v1beta1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -// MutualTLSMode is the mutual TLS mode specified by authentication policy. -type MutualTLSMode int - -const ( - // MTLSUnknown is used to indicate the variable hasn't been initialized correctly (with the authentication policy). - MTLSUnknown MutualTLSMode = iota - - // MTLSDisable if authentication policy disable mTLS. - MTLSDisable - - // MTLSPermissive if authentication policy enable mTLS in permissive mode. - MTLSPermissive - - // MTLSStrict if authentication policy enable mTLS in strict mode. - MTLSStrict -) - -// String converts MutualTLSMode to human readable string for debugging. -func (mode MutualTLSMode) String() string { - switch mode { - case MTLSDisable: - return "DISABLE" - case MTLSPermissive: - return "PERMISSIVE" - case MTLSStrict: - return "STRICT" - default: - return "UNKNOWN" - } -} - -// ConvertToMutualTLSMode converts from peer authn MTLS mode (`PeerAuthentication_MutualTLS_Mode`) -// to the MTLS mode specified by authn policy. -func ConvertToMutualTLSMode(mode v1beta1.PeerAuthentication_MutualTLS_Mode) MutualTLSMode { - switch mode { - case v1beta1.PeerAuthentication_MutualTLS_DISABLE: - return MTLSDisable - case v1beta1.PeerAuthentication_MutualTLS_PERMISSIVE: - return MTLSPermissive - case v1beta1.PeerAuthentication_MutualTLS_STRICT: - return MTLSStrict - default: - return MTLSUnknown - } -} - -// AuthenticationPolicies organizes authentication (mTLS + JWT) policies by namespace. -type AuthenticationPolicies struct { - // Maps from namespace to the v1beta1 authentication policies. - requestAuthentications map[string][]config.Config - - peerAuthentications map[string][]config.Config - - // namespaceMutualTLSMode is the MutualTLSMode corresponding to the namespace-level PeerAuthentication. - // All namespace-level policies, and only them, are added to this map. If the policy mTLS mode is set - // to UNSET, it will be resolved to the value set by mesh policy if exist (i.e not UNKNOWN), or MTLSPermissive - // otherwise. - namespaceMutualTLSMode map[string]MutualTLSMode - - // globalMutualTLSMode is the MutualTLSMode corresponding to the mesh-level PeerAuthentication. - // This value can be MTLSUnknown, if there is no mesh-level policy. - globalMutualTLSMode MutualTLSMode - - rootNamespace string - - // aggregateVersion contains the versions of all peer authentications. - aggregateVersion string -} - -// initAuthenticationPolicies creates a new AuthenticationPolicies struct and populates with the -// authentication policies in the mesh environment. -func initAuthenticationPolicies(env *Environment) (*AuthenticationPolicies, error) { - policy := &AuthenticationPolicies{ - requestAuthentications: map[string][]config.Config{}, - peerAuthentications: map[string][]config.Config{}, - globalMutualTLSMode: MTLSUnknown, - rootNamespace: env.Mesh().GetRootNamespace(), - } - - if configs, err := env.List( - gvk.RequestAuthentication, NamespaceAll); err == nil { - sortConfigByCreationTime(configs) - policy.addRequestAuthentication(configs) - } else { - return nil, err - } - - if configs, err := env.List( - gvk.PeerAuthentication, NamespaceAll); err == nil { - policy.addPeerAuthentication(configs) - } else { - return nil, err - } - - return policy, nil -} - -func (policy *AuthenticationPolicies) addRequestAuthentication(configs []config.Config) { - for _, config := range configs { - policy.requestAuthentications[config.Namespace] = append(policy.requestAuthentications[config.Namespace], config) - } -} - -func (policy *AuthenticationPolicies) addPeerAuthentication(configs []config.Config) { - // Sort configs in ascending order by their creation time. - sortConfigByCreationTime(configs) - - foundNamespaceMTLS := make(map[string]v1beta1.PeerAuthentication_MutualTLS_Mode) - // Track which namespace/mesh level policy seen so far to make sure the oldest one is used. - seenNamespaceOrMeshConfig := make(map[string]time.Time) - versions := []string{} - - for _, config := range configs { - versions = append(versions, config.UID+"."+config.ResourceVersion) - // Mesh & namespace level policy are those that have empty selector. - spec := config.Spec.(*v1beta1.PeerAuthentication) - if spec.Selector == nil || len(spec.Selector.MatchLabels) == 0 { - if t, ok := seenNamespaceOrMeshConfig[config.Namespace]; ok { - log.Warnf( - "Namespace/mesh-level PeerAuthentication is already defined for %q at time %v. Ignore %q which was created at time %v", - config.Namespace, t, config.Name, config.CreationTimestamp) - continue - } - seenNamespaceOrMeshConfig[config.Namespace] = config.CreationTimestamp - - mode := v1beta1.PeerAuthentication_MutualTLS_UNSET - if spec.Mtls != nil { - mode = spec.Mtls.Mode - } - if config.Namespace == policy.rootNamespace { - // This is mesh-level policy. UNSET is treated as permissive for mesh-policy. - if mode == v1beta1.PeerAuthentication_MutualTLS_UNSET { - policy.globalMutualTLSMode = MTLSPermissive - } else { - policy.globalMutualTLSMode = ConvertToMutualTLSMode(mode) - } - } else { - // For regular namespace, just add to the intermediate map. - foundNamespaceMTLS[config.Namespace] = mode - } - } - - // Add the config to the map by namespace for future look up. This is done after namespace/mesh - // singleton check so there should be at most one namespace/mesh config is added to the map. - policy.peerAuthentications[config.Namespace] = append(policy.peerAuthentications[config.Namespace], config) - } - - policy.aggregateVersion = fmt.Sprintf("%x", md5.Sum([]byte(strings.Join(versions, ";")))) - - // Process found namespace-level policy. - policy.namespaceMutualTLSMode = make(map[string]MutualTLSMode, len(foundNamespaceMTLS)) - - inheritedMTLSMode := policy.globalMutualTLSMode - if inheritedMTLSMode == MTLSUnknown { - // If the mesh policy is not explicitly presented, use default value MTLSPermissive. - inheritedMTLSMode = MTLSPermissive - } - for ns, mtlsMode := range foundNamespaceMTLS { - if mtlsMode == v1beta1.PeerAuthentication_MutualTLS_UNSET { - policy.namespaceMutualTLSMode[ns] = inheritedMTLSMode - } else { - policy.namespaceMutualTLSMode[ns] = ConvertToMutualTLSMode(mtlsMode) - } - } -} - -// GetNamespaceMutualTLSMode returns the MutualTLSMode as defined by a namespace or mesh level -// PeerAuthentication. The return value could be `MTLSUnknown` if there is no mesh nor namespace -// PeerAuthentication policy for the given namespace. -func (policy *AuthenticationPolicies) GetNamespaceMutualTLSMode(namespace string) MutualTLSMode { - if mode, ok := policy.namespaceMutualTLSMode[namespace]; ok { - return mode - } - return policy.globalMutualTLSMode -} - -// GetJwtPoliciesForWorkload returns a list of JWT policies matching to labels. -func (policy *AuthenticationPolicies) GetJwtPoliciesForWorkload(namespace string, - workloadLabels labels.Instance) []*config.Config { - return getConfigsForWorkload(policy.requestAuthentications, policy.rootNamespace, namespace, workloadLabels) -} - -// GetPeerAuthenticationsForWorkload returns a list of peer authentication policies matching to labels. -func (policy *AuthenticationPolicies) GetPeerAuthenticationsForWorkload(namespace string, - workloadLabels labels.Instance) []*config.Config { - return getConfigsForWorkload(policy.peerAuthentications, policy.rootNamespace, namespace, workloadLabels) -} - -// GetRootNamespace return root namespace that is tracked by the policy object. -func (policy *AuthenticationPolicies) GetRootNamespace() string { - return policy.rootNamespace -} - -// GetVersion return versions of all peer authentications.. -func (policy *AuthenticationPolicies) GetVersion() string { - return policy.aggregateVersion -} - -func getConfigsForWorkload(configsByNamespace map[string][]config.Config, - rootNamespace string, - namespace string, - workloadLabels labels.Instance) []*config.Config { - configs := make([]*config.Config, 0) - lookupInNamespaces := []string{namespace} - if namespace != rootNamespace { - // Only check the root namespace if the (workload) namespace is not already the root namespace - // to avoid double inclusion. - lookupInNamespaces = append(lookupInNamespaces, rootNamespace) - } - for _, ns := range lookupInNamespaces { - if nsConfig, ok := configsByNamespace[ns]; ok { - for idx := range nsConfig { - cfg := &nsConfig[idx] - if ns != cfg.Namespace { - // Should never come here. Log warning just in case. - log.Warnf("Seeing config %s with namespace %s in map entry for %s. Ignored", cfg.Name, cfg.Namespace, ns) - continue - } - var selector labels.Instance - switch cfg.GroupVersionKind { - case collections.IstioSecurityV1Beta1Requestauthentications.Resource().GroupVersionKind(): - selector = cfg.Spec.(*v1beta1.RequestAuthentication).GetSelector().GetMatchLabels() - case collections.IstioSecurityV1Beta1Peerauthentications.Resource().GroupVersionKind(): - selector = cfg.Spec.(*v1beta1.PeerAuthentication).GetSelector().GetMatchLabels() - default: - log.Warnf("Not support authentication type %q", cfg.GroupVersionKind) - continue - } - if selector.SubsetOf(workloadLabels) { - configs = append(configs, cfg) - } - } - } - } - - return configs -} diff --git a/pilot/pkg/model/authentication_test.go b/pilot/pkg/model/authentication_test.go deleted file mode 100644 index 4c3dceef4..000000000 --- a/pilot/pkg/model/authentication_test.go +++ /dev/null @@ -1,841 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "fmt" - "reflect" - "testing" - "time" -) - -import ( - meshconfig "istio.io/api/mesh/v1alpha1" - securityBeta "istio.io/api/security/v1beta1" - selectorpb "istio.io/api/type/v1beta1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model/test" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -const ( - rootNamespace = "istio-config" -) - -var baseTimestamp = time.Date(2020, 2, 2, 2, 2, 2, 0, time.UTC) - -func TestGetPoliciesForWorkload(t *testing.T) { - policies := getTestAuthenticationPolicies(createTestConfigs(true /* with mesh peer authn */), t) - - cases := []struct { - name string - workloadNamespace string - workloadLabels labels.Instance - wantRequestAuthn []*config.Config - wantPeerAuthn []*config.Config - wantNamespaceMutualTLS MutualTLSMode - }{ - { - name: "Empty workload labels in foo", - workloadNamespace: "foo", - workloadLabels: nil, - wantRequestAuthn: []*config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.RequestAuthentication, - Name: "default", - Namespace: "foo", - }, - Spec: &securityBeta.RequestAuthentication{}, - }, - { - Meta: config.Meta{ - GroupVersionKind: gvk.RequestAuthentication, - Name: "default", - Namespace: "istio-config", - }, - Spec: &securityBeta.RequestAuthentication{}, - }, - }, - wantPeerAuthn: []*config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - CreationTimestamp: baseTimestamp, - Name: "default", - Namespace: "foo", - }, - Spec: &securityBeta.PeerAuthentication{ - Mtls: &securityBeta.PeerAuthentication_MutualTLS{ - Mode: securityBeta.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - { - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - CreationTimestamp: baseTimestamp, - Name: "default", - Namespace: "istio-config", - }, - Spec: &securityBeta.PeerAuthentication{ - Mtls: &securityBeta.PeerAuthentication_MutualTLS{ - Mode: securityBeta.PeerAuthentication_MutualTLS_UNSET, - }, - }, - }, - }, - wantNamespaceMutualTLS: MTLSStrict, - }, - { - name: "Empty workload labels in bar", - workloadNamespace: "bar", - workloadLabels: nil, - wantRequestAuthn: []*config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.RequestAuthentication, - Name: "default", - Namespace: "bar", - }, - Spec: &securityBeta.RequestAuthentication{}, - }, - { - Meta: config.Meta{ - GroupVersionKind: gvk.RequestAuthentication, - Name: "default", - Namespace: "istio-config", - }, - Spec: &securityBeta.RequestAuthentication{}, - }, - }, - wantPeerAuthn: []*config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - CreationTimestamp: baseTimestamp, - Name: "default", - Namespace: "istio-config", - }, - Spec: &securityBeta.PeerAuthentication{ - Mtls: &securityBeta.PeerAuthentication_MutualTLS{ - Mode: securityBeta.PeerAuthentication_MutualTLS_UNSET, - }, - }, - }, - }, - wantNamespaceMutualTLS: MTLSPermissive, - }, - { - name: "Empty workload labels in baz", - workloadNamespace: "baz", - workloadLabels: nil, - wantRequestAuthn: []*config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.RequestAuthentication, - Name: "default", - Namespace: "istio-config", - }, - Spec: &securityBeta.RequestAuthentication{}, - }, - }, - wantPeerAuthn: []*config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - CreationTimestamp: baseTimestamp, - Name: "default", - Namespace: "istio-config", - }, - Spec: &securityBeta.PeerAuthentication{ - Mtls: &securityBeta.PeerAuthentication_MutualTLS{ - Mode: securityBeta.PeerAuthentication_MutualTLS_UNSET, - }, - }, - }, - }, - wantNamespaceMutualTLS: MTLSPermissive, - }, - { - name: "Match workload labels in foo", - workloadNamespace: "foo", - workloadLabels: labels.Instance{"app": "httpbin", "version": "v1", "other": "labels"}, - wantRequestAuthn: []*config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.RequestAuthentication, - Name: "default", - Namespace: "foo", - }, - Spec: &securityBeta.RequestAuthentication{}, - }, - { - Meta: config.Meta{ - GroupVersionKind: gvk.RequestAuthentication, - Name: "with-selector", - Namespace: "foo", - }, - Spec: &securityBeta.RequestAuthentication{ - Selector: &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - "version": "v1", - }, - }, - }, - }, - { - Meta: config.Meta{ - GroupVersionKind: gvk.RequestAuthentication, - Name: "default", - Namespace: "istio-config", - }, - Spec: &securityBeta.RequestAuthentication{}, - }, - { - Meta: config.Meta{ - GroupVersionKind: gvk.RequestAuthentication, - Name: "global-with-selector", - Namespace: "istio-config", - }, - Spec: &securityBeta.RequestAuthentication{ - Selector: &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - }, - }, - }, - }, - }, - wantPeerAuthn: []*config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - CreationTimestamp: baseTimestamp, - Name: "default", - Namespace: "foo", - }, - Spec: &securityBeta.PeerAuthentication{ - Mtls: &securityBeta.PeerAuthentication_MutualTLS{ - Mode: securityBeta.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - { - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - CreationTimestamp: baseTimestamp, - Name: "peer-with-selector", - Namespace: "foo", - }, - Spec: &securityBeta.PeerAuthentication{ - Selector: &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{ - "version": "v1", - }, - }, - Mtls: &securityBeta.PeerAuthentication_MutualTLS{ - Mode: securityBeta.PeerAuthentication_MutualTLS_DISABLE, - }, - }, - }, - { - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - CreationTimestamp: baseTimestamp, - Name: "default", - Namespace: "istio-config", - }, - Spec: &securityBeta.PeerAuthentication{ - Mtls: &securityBeta.PeerAuthentication_MutualTLS{ - Mode: securityBeta.PeerAuthentication_MutualTLS_UNSET, - }, - }, - }, - }, - wantNamespaceMutualTLS: MTLSStrict, - }, - { - name: "Match workload labels in bar", - workloadNamespace: "bar", - workloadLabels: labels.Instance{"app": "httpbin", "version": "v1"}, - wantRequestAuthn: []*config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.RequestAuthentication, - Name: "default", - Namespace: "bar", - }, - Spec: &securityBeta.RequestAuthentication{}, - }, - { - Meta: config.Meta{ - GroupVersionKind: gvk.RequestAuthentication, - Name: "default", - Namespace: "istio-config", - }, - Spec: &securityBeta.RequestAuthentication{}, - }, - { - Meta: config.Meta{ - GroupVersionKind: gvk.RequestAuthentication, - Name: "global-with-selector", - Namespace: "istio-config", - }, - Spec: &securityBeta.RequestAuthentication{ - Selector: &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - }, - }, - }, - }, - }, - wantPeerAuthn: []*config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - CreationTimestamp: baseTimestamp, - Name: "default", - Namespace: "istio-config", - }, - Spec: &securityBeta.PeerAuthentication{ - Mtls: &securityBeta.PeerAuthentication_MutualTLS{ - Mode: securityBeta.PeerAuthentication_MutualTLS_UNSET, - }, - }, - }, - }, - wantNamespaceMutualTLS: MTLSPermissive, - }, - { - name: "Paritial match workload labels in foo", - workloadNamespace: "foo", - workloadLabels: labels.Instance{"app": "httpbin"}, - wantRequestAuthn: []*config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.RequestAuthentication, - Name: "default", - Namespace: "foo", - }, - Spec: &securityBeta.RequestAuthentication{}, - }, - { - Meta: config.Meta{ - GroupVersionKind: gvk.RequestAuthentication, - Name: "default", - Namespace: "istio-config", - }, - Spec: &securityBeta.RequestAuthentication{}, - }, - { - Meta: config.Meta{ - GroupVersionKind: gvk.RequestAuthentication, - Name: "global-with-selector", - Namespace: "istio-config", - }, - Spec: &securityBeta.RequestAuthentication{ - Selector: &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - }, - }, - }, - }, - }, - wantPeerAuthn: []*config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - CreationTimestamp: baseTimestamp, - Name: "default", - Namespace: "foo", - }, - Spec: &securityBeta.PeerAuthentication{ - Mtls: &securityBeta.PeerAuthentication_MutualTLS{ - Mode: securityBeta.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - { - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - CreationTimestamp: baseTimestamp, - Name: "default", - Namespace: "istio-config", - }, - Spec: &securityBeta.PeerAuthentication{ - Mtls: &securityBeta.PeerAuthentication_MutualTLS{ - Mode: securityBeta.PeerAuthentication_MutualTLS_UNSET, - }, - }, - }, - }, - wantNamespaceMutualTLS: MTLSStrict, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - if got := policies.GetJwtPoliciesForWorkload(tc.workloadNamespace, tc.workloadLabels); !reflect.DeepEqual(tc.wantRequestAuthn, got) { - t.Fatalf("want %+v\n, but got %+v\n", printConfigs(tc.wantRequestAuthn), printConfigs(got)) - } - if got := policies.GetPeerAuthenticationsForWorkload(tc.workloadNamespace, tc.workloadLabels); !reflect.DeepEqual(tc.wantPeerAuthn, got) { - t.Fatalf("want %+v\n, but got %+v\n", printConfigs(tc.wantPeerAuthn), printConfigs(got)) - } - if got := policies.GetNamespaceMutualTLSMode(tc.workloadNamespace); got != tc.wantNamespaceMutualTLS { - t.Fatalf("want %s\n, but got %s\n", tc.wantNamespaceMutualTLS, got) - } - }) - } -} - -func TestGetPoliciesForWorkloadWithoutMeshPeerAuthn(t *testing.T) { - policies := getTestAuthenticationPolicies(createTestConfigs(false /* with mesh peer authn */), t) - - cases := []struct { - name string - workloadNamespace string - workloadLabels labels.Instance - wantPeerAuthn []*config.Config - wantNamespaceMutualTLS MutualTLSMode - }{ - { - name: "Empty workload labels in foo", - workloadNamespace: "foo", - workloadLabels: nil, - wantPeerAuthn: []*config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - CreationTimestamp: baseTimestamp, - Name: "default", - Namespace: "foo", - }, - Spec: &securityBeta.PeerAuthentication{ - Mtls: &securityBeta.PeerAuthentication_MutualTLS{ - Mode: securityBeta.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - }, - wantNamespaceMutualTLS: MTLSStrict, - }, - { - name: "Empty workload labels in bar", - workloadNamespace: "bar", - workloadLabels: nil, - wantPeerAuthn: []*config.Config{}, - wantNamespaceMutualTLS: MTLSUnknown, - }, - { - name: "Empty workload labels in baz", - workloadNamespace: "baz", - workloadLabels: nil, - wantPeerAuthn: []*config.Config{}, - wantNamespaceMutualTLS: MTLSUnknown, - }, - { - name: "Match workload labels in foo", - workloadNamespace: "foo", - workloadLabels: labels.Instance{"app": "httpbin", "version": "v1", "other": "labels"}, - wantPeerAuthn: []*config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - CreationTimestamp: baseTimestamp, - Name: "default", - Namespace: "foo", - }, - Spec: &securityBeta.PeerAuthentication{ - Mtls: &securityBeta.PeerAuthentication_MutualTLS{ - Mode: securityBeta.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - { - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - CreationTimestamp: baseTimestamp, - Name: "peer-with-selector", - Namespace: "foo", - }, - Spec: &securityBeta.PeerAuthentication{ - Selector: &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{ - "version": "v1", - }, - }, - Mtls: &securityBeta.PeerAuthentication_MutualTLS{ - Mode: securityBeta.PeerAuthentication_MutualTLS_DISABLE, - }, - }, - }, - }, - wantNamespaceMutualTLS: MTLSStrict, - }, - { - name: "Match workload labels in bar", - workloadNamespace: "bar", - workloadLabels: labels.Instance{"app": "httpbin", "version": "v1"}, - wantPeerAuthn: []*config.Config{}, - wantNamespaceMutualTLS: MTLSUnknown, - }, - { - name: "Paritial match workload labels in foo", - workloadNamespace: "foo", - workloadLabels: labels.Instance{"app": "httpbin"}, - wantPeerAuthn: []*config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - CreationTimestamp: baseTimestamp, - Name: "default", - Namespace: "foo", - }, - Spec: &securityBeta.PeerAuthentication{ - Mtls: &securityBeta.PeerAuthentication_MutualTLS{ - Mode: securityBeta.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - }, - wantNamespaceMutualTLS: MTLSStrict, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - if got := policies.GetPeerAuthenticationsForWorkload(tc.workloadNamespace, tc.workloadLabels); !reflect.DeepEqual(tc.wantPeerAuthn, got) { - t.Fatalf("want %+v\n, but got %+v\n", printConfigs(tc.wantPeerAuthn), printConfigs(got)) - } - if got := policies.GetNamespaceMutualTLSMode(tc.workloadNamespace); got != tc.wantNamespaceMutualTLS { - t.Fatalf("want %s, but got %s", tc.wantNamespaceMutualTLS, got) - } - }) - } -} - -func TestGetPoliciesForWorkloadWithJwksResolver(t *testing.T) { - ms, err := test.StartNewServer() - defer ms.Stop() - if err != nil { - t.Fatal("failed to start a mock server") - } - - policies := getTestAuthenticationPolicies(createNonTrivialRequestAuthnTestConfigs(ms.URL), t) - - cases := []struct { - name string - workloadNamespace string - workloadLabels labels.Instance - wantRequestAuthn []*config.Config - }{ - { - name: "single hit", - workloadNamespace: "foo", - workloadLabels: nil, - wantRequestAuthn: []*config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.RequestAuthentication, - Name: "default", - Namespace: rootNamespace, - }, - Spec: &securityBeta.RequestAuthentication{ - JwtRules: []*securityBeta.JWTRule{ - { - Issuer: ms.URL, - }, - }, - }, - }, - }, - }, - { - name: "double hit", - workloadNamespace: "foo", - workloadLabels: labels.Instance{"app": "httpbin"}, - wantRequestAuthn: []*config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.RequestAuthentication, - Name: "default", - Namespace: rootNamespace, - }, - Spec: &securityBeta.RequestAuthentication{ - JwtRules: []*securityBeta.JWTRule{ - { - Issuer: ms.URL, - }, - }, - }, - }, - { - Meta: config.Meta{ - GroupVersionKind: gvk.RequestAuthentication, - Name: "global-with-selector", - Namespace: rootNamespace, - }, - Spec: &securityBeta.RequestAuthentication{ - Selector: &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - }, - }, - JwtRules: []*securityBeta.JWTRule{ - { - Issuer: ms.URL, - }, - { - Issuer: "bad-issuer", - }, - }, - }, - }, - }, - }, - { - name: "tripple hit", - workloadNamespace: "foo", - workloadLabels: labels.Instance{"app": "httpbin", "version": "v1"}, - wantRequestAuthn: []*config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.RequestAuthentication, - Name: "with-selector", - Namespace: "foo", - }, - Spec: &securityBeta.RequestAuthentication{ - Selector: &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - "version": "v1", - }, - }, - JwtRules: []*securityBeta.JWTRule{ - { - Issuer: "issuer-with-jwks-uri", - JwksUri: "example.com", - }, - { - Issuer: "issuer-with-jwks", - Jwks: "deadbeef", - }, - }, - }, - }, - { - Meta: config.Meta{ - GroupVersionKind: gvk.RequestAuthentication, - Name: "default", - Namespace: rootNamespace, - }, - Spec: &securityBeta.RequestAuthentication{ - JwtRules: []*securityBeta.JWTRule{ - { - Issuer: ms.URL, - }, - }, - }, - }, - { - Meta: config.Meta{ - GroupVersionKind: gvk.RequestAuthentication, - Name: "global-with-selector", - Namespace: rootNamespace, - }, - Spec: &securityBeta.RequestAuthentication{ - Selector: &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - }, - }, - JwtRules: []*securityBeta.JWTRule{ - { - Issuer: ms.URL, - }, - { - Issuer: "bad-issuer", - }, - }, - }, - }, - }, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - if got := policies.GetJwtPoliciesForWorkload(tc.workloadNamespace, tc.workloadLabels); !reflect.DeepEqual(tc.wantRequestAuthn, got) { - t.Fatalf("want %+v\n, but got %+v\n", printConfigs(tc.wantRequestAuthn), printConfigs(got)) - } - }) - } -} - -func getTestAuthenticationPolicies(configs []*config.Config, t *testing.T) *AuthenticationPolicies { - configStore := NewFakeStore() - for _, cfg := range configs { - log.Infof("add config %s", cfg.Name) - if _, err := configStore.Create(*cfg); err != nil { - t.Fatalf("getTestAuthenticationPolicies %v", err) - } - } - environment := &Environment{ - ConfigStore: MakeIstioStore(configStore), - Watcher: mesh.NewFixedWatcher(&meshconfig.MeshConfig{RootNamespace: rootNamespace}), - } - authnPolicy, err := initAuthenticationPolicies(environment) - if err != nil { - t.Fatalf("getTestAuthenticationPolicies %v", err) - } - - return authnPolicy -} - -func createTestRequestAuthenticationResource(name string, namespace string, selector *selectorpb.WorkloadSelector) *config.Config { - return &config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioSecurityV1Beta1Requestauthentications.Resource().GroupVersionKind(), - Name: name, - Namespace: namespace, - }, - Spec: &securityBeta.RequestAuthentication{ - Selector: selector, - }, - } -} - -func createTestPeerAuthenticationResource(name string, namespace string, timestamp time.Time, - selector *selectorpb.WorkloadSelector, mode securityBeta.PeerAuthentication_MutualTLS_Mode) *config.Config { - return &config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioSecurityV1Beta1Peerauthentications.Resource().GroupVersionKind(), - Name: name, - Namespace: namespace, - CreationTimestamp: timestamp, - }, - Spec: &securityBeta.PeerAuthentication{ - Selector: selector, - Mtls: &securityBeta.PeerAuthentication_MutualTLS{ - Mode: mode, - }, - }, - } -} - -func createTestConfigs(withMeshPeerAuthn bool) []*config.Config { - configs := make([]*config.Config, 0) - - selector := &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - "version": "v1", - }, - } - configs = append(configs, createTestRequestAuthenticationResource("default", rootNamespace, nil), - createTestRequestAuthenticationResource("global-with-selector", rootNamespace, &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - }, - }), - createTestRequestAuthenticationResource("default", "foo", nil), - createTestRequestAuthenticationResource("default", "bar", nil), - createTestRequestAuthenticationResource("with-selector", "foo", selector), - createTestPeerAuthenticationResource("global-peer-with-selector", rootNamespace, baseTimestamp, &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - "version": "v2", - }, - }, securityBeta.PeerAuthentication_MutualTLS_UNSET), - createTestPeerAuthenticationResource("default", "foo", baseTimestamp, nil, securityBeta.PeerAuthentication_MutualTLS_STRICT), - createTestPeerAuthenticationResource("ignored-newer-default", "foo", baseTimestamp.Add(time.Second), nil, securityBeta.PeerAuthentication_MutualTLS_STRICT), - createTestPeerAuthenticationResource("peer-with-selector", "foo", baseTimestamp, &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{ - "version": "v1", - }, - }, securityBeta.PeerAuthentication_MutualTLS_DISABLE)) - - if withMeshPeerAuthn { - configs = append(configs, - createTestPeerAuthenticationResource("ignored-newer", rootNamespace, baseTimestamp.Add(time.Second*2), - nil, securityBeta.PeerAuthentication_MutualTLS_UNSET), - createTestPeerAuthenticationResource("default", rootNamespace, baseTimestamp, - nil, securityBeta.PeerAuthentication_MutualTLS_UNSET), - createTestPeerAuthenticationResource("ignored-another-newer", rootNamespace, baseTimestamp.Add(time.Second), - nil, securityBeta.PeerAuthentication_MutualTLS_UNSET)) - } - - return configs -} - -func addJwtRule(issuer, jwksURI, jwks string, config *config.Config) { - spec := config.Spec.(*securityBeta.RequestAuthentication) - if spec.JwtRules == nil { - spec.JwtRules = make([]*securityBeta.JWTRule, 0) - } - spec.JwtRules = append(spec.JwtRules, &securityBeta.JWTRule{ - Issuer: issuer, - JwksUri: jwksURI, - Jwks: jwks, - }) -} - -func createNonTrivialRequestAuthnTestConfigs(issuer string) []*config.Config { - configs := make([]*config.Config, 0) - - globalCfg := createTestRequestAuthenticationResource("default", rootNamespace, nil) - addJwtRule(issuer, "", "", globalCfg) - configs = append(configs, globalCfg) - - httpbinCfg := createTestRequestAuthenticationResource("global-with-selector", rootNamespace, &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - }, - }) - - addJwtRule(issuer, "", "", httpbinCfg) - addJwtRule("bad-issuer", "", "", httpbinCfg) - configs = append(configs, httpbinCfg) - - httpbinCfgV1 := createTestRequestAuthenticationResource("with-selector", "foo", &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - "version": "v1", - }, - }) - addJwtRule("issuer-with-jwks-uri", "example.com", "", httpbinCfgV1) - addJwtRule("issuer-with-jwks", "", "deadbeef", httpbinCfgV1) - configs = append(configs, httpbinCfgV1) - - return configs -} - -func printConfigs(configs []*config.Config) string { - s := "[\n" - for _, c := range configs { - s += fmt.Sprintf("%+v\n", c) - } - return s + "]" -} diff --git a/pilot/pkg/model/authorization.go b/pilot/pkg/model/authorization.go deleted file mode 100644 index 5828d078d..000000000 --- a/pilot/pkg/model/authorization.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - authpb "istio.io/api/security/v1beta1" - istiolog "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -var authzLog = istiolog.RegisterScope("authorization", "Istio Authorization Policy", 0) - -type AuthorizationPolicy struct { - Name string `json:"name"` - Namespace string `json:"namespace"` - Annotations map[string]string `json:"annotations"` - Spec *authpb.AuthorizationPolicy `json:"spec"` -} - -// AuthorizationPolicies organizes AuthorizationPolicy by namespace. -type AuthorizationPolicies struct { - // Maps from namespace to the Authorization policies. - NamespaceToPolicies map[string][]AuthorizationPolicy `json:"namespace_to_policies"` - - // The name of the root namespace. Policy in the root namespace applies to workloads in all namespaces. - RootNamespace string `json:"root_namespace"` -} - -// GetAuthorizationPolicies returns the AuthorizationPolicies for the given environment. -func GetAuthorizationPolicies(env *Environment) (*AuthorizationPolicies, error) { - policy := &AuthorizationPolicies{ - NamespaceToPolicies: map[string][]AuthorizationPolicy{}, - RootNamespace: env.Mesh().GetRootNamespace(), - } - - policies, err := env.List(collections.IstioSecurityV1Beta1Authorizationpolicies.Resource().GroupVersionKind(), NamespaceAll) - if err != nil { - return nil, err - } - sortConfigByCreationTime(policies) - for _, config := range policies { - authzConfig := AuthorizationPolicy{ - Name: config.Name, - Namespace: config.Namespace, - Annotations: config.Annotations, - Spec: config.Spec.(*authpb.AuthorizationPolicy), - } - policy.NamespaceToPolicies[config.Namespace] = append(policy.NamespaceToPolicies[config.Namespace], authzConfig) - } - - return policy, nil -} - -type AuthorizationPoliciesResult struct { - Custom []AuthorizationPolicy - Deny []AuthorizationPolicy - Allow []AuthorizationPolicy - Audit []AuthorizationPolicy -} - -// ListAuthorizationPolicies returns authorization policies applied to the workload in the given namespace. -func (policy *AuthorizationPolicies) ListAuthorizationPolicies(namespace string, workload labels.Instance) AuthorizationPoliciesResult { - ret := AuthorizationPoliciesResult{} - if policy == nil { - return ret - } - - var namespaces []string - if policy.RootNamespace != "" { - namespaces = append(namespaces, policy.RootNamespace) - } - // To prevent duplicate policies in case root namespace equals proxy's namespace. - if namespace != policy.RootNamespace { - namespaces = append(namespaces, namespace) - } - - for _, ns := range namespaces { - for _, config := range policy.NamespaceToPolicies[ns] { - spec := config.Spec - selector := labels.Instance(spec.GetSelector().GetMatchLabels()) - if selector.SubsetOf(workload) { - switch config.Spec.GetAction() { - case authpb.AuthorizationPolicy_ALLOW: - ret.Allow = append(ret.Allow, config) - case authpb.AuthorizationPolicy_DENY: - ret.Deny = append(ret.Deny, config) - case authpb.AuthorizationPolicy_AUDIT: - ret.Audit = append(ret.Audit, config) - case authpb.AuthorizationPolicy_CUSTOM: - ret.Custom = append(ret.Custom, config) - default: - log.Errorf("ignored authorization policy %s.%s with unsupported action: %s", - config.Namespace, config.Name, config.Spec.GetAction()) - } - } - } - } - - return ret -} diff --git a/pilot/pkg/model/authorization_test.go b/pilot/pkg/model/authorization_test.go deleted file mode 100644 index fa0d39ef2..000000000 --- a/pilot/pkg/model/authorization_test.go +++ /dev/null @@ -1,429 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "fmt" - "reflect" - "testing" -) - -import ( - "github.com/golang/protobuf/proto" // nolint: staticcheck - meshconfig "istio.io/api/mesh/v1alpha1" - authpb "istio.io/api/security/v1beta1" - selectorpb "istio.io/api/type/v1beta1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -func TestAuthorizationPolicies_ListAuthorizationPolicies(t *testing.T) { - policy := &authpb.AuthorizationPolicy{ - Rules: []*authpb.Rule{ - { - From: []*authpb.Rule_From{ - { - Source: &authpb.Source{ - Principals: []string{"sleep"}, - }, - }, - }, - To: []*authpb.Rule_To{ - { - Operation: &authpb.Operation{ - Methods: []string{"GET"}, - }, - }, - }, - }, - }, - } - policyWithSelector := proto.Clone(policy).(*authpb.AuthorizationPolicy) - policyWithSelector.Selector = &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - "version": "v1", - }, - } - denyPolicy := proto.Clone(policy).(*authpb.AuthorizationPolicy) - denyPolicy.Action = authpb.AuthorizationPolicy_DENY - - auditPolicy := proto.Clone(policy).(*authpb.AuthorizationPolicy) - auditPolicy.Action = authpb.AuthorizationPolicy_AUDIT - - customPolicy := proto.Clone(policy).(*authpb.AuthorizationPolicy) - customPolicy.Action = authpb.AuthorizationPolicy_CUSTOM - - cases := []struct { - name string - ns string - workloadLabels map[string]string - configs []config.Config - wantDeny []AuthorizationPolicy - wantAllow []AuthorizationPolicy - wantAudit []AuthorizationPolicy - wantCustom []AuthorizationPolicy - }{ - { - name: "no policies", - ns: "foo", - wantAllow: nil, - }, - { - name: "no policies in namespace foo", - ns: "foo", - configs: []config.Config{ - newConfig("authz-1", "bar", policy), - newConfig("authz-2", "bar", policy), - }, - wantAllow: nil, - }, - { - name: "one allow policy", - ns: "bar", - configs: []config.Config{ - newConfig("authz-1", "bar", policy), - }, - wantAllow: []AuthorizationPolicy{ - { - Name: "authz-1", - Namespace: "bar", - Spec: policy, - }, - }, - }, - { - name: "one deny policy", - ns: "bar", - configs: []config.Config{ - newConfig("authz-1", "bar", denyPolicy), - }, - wantDeny: []AuthorizationPolicy{ - { - Name: "authz-1", - Namespace: "bar", - Spec: denyPolicy, - }, - }, - }, - { - name: "one audit policy", - ns: "bar", - configs: []config.Config{ - newConfig("authz-1", "bar", auditPolicy), - }, - wantAudit: []AuthorizationPolicy{ - { - Name: "authz-1", - Namespace: "bar", - Spec: auditPolicy, - }, - }, - }, - { - name: "one custom policy", - ns: "bar", - configs: []config.Config{ - newConfig("authz-1", "bar", customPolicy), - }, - wantCustom: []AuthorizationPolicy{ - { - Name: "authz-1", - Namespace: "bar", - Spec: customPolicy, - }, - }, - }, - { - name: "two policies", - ns: "bar", - configs: []config.Config{ - newConfig("authz-1", "foo", policy), - newConfig("authz-1", "bar", policy), - newConfig("authz-2", "bar", policy), - }, - wantAllow: []AuthorizationPolicy{ - { - Name: "authz-1", - Namespace: "bar", - Spec: policy, - }, - { - Name: "authz-2", - Namespace: "bar", - Spec: policy, - }, - }, - }, - { - name: "mixing allow, deny, and audit policies", - ns: "bar", - configs: []config.Config{ - newConfig("authz-1", "bar", policy), - newConfig("authz-2", "bar", denyPolicy), - newConfig("authz-3", "bar", auditPolicy), - newConfig("authz-4", "bar", auditPolicy), - }, - wantDeny: []AuthorizationPolicy{ - { - Name: "authz-2", - Namespace: "bar", - Spec: denyPolicy, - }, - }, - wantAllow: []AuthorizationPolicy{ - { - Name: "authz-1", - Namespace: "bar", - Spec: policy, - }, - }, - wantAudit: []AuthorizationPolicy{ - { - Name: "authz-3", - Namespace: "bar", - Spec: auditPolicy, - }, - { - Name: "authz-4", - Namespace: "bar", - Spec: auditPolicy, - }, - }, - }, - { - name: "selector exact match", - ns: "bar", - workloadLabels: map[string]string{ - "app": "httpbin", - "version": "v1", - }, - configs: []config.Config{ - newConfig("authz-1", "bar", policyWithSelector), - }, - wantAllow: []AuthorizationPolicy{ - { - Name: "authz-1", - Namespace: "bar", - Spec: policyWithSelector, - }, - }, - }, - { - name: "selector subset match", - ns: "bar", - workloadLabels: map[string]string{ - "app": "httpbin", - "version": "v1", - "env": "dev", - }, - configs: []config.Config{ - newConfig("authz-1", "bar", policyWithSelector), - }, - wantAllow: []AuthorizationPolicy{ - { - Name: "authz-1", - Namespace: "bar", - Spec: policyWithSelector, - }, - }, - }, - { - name: "selector not match", - ns: "bar", - workloadLabels: map[string]string{ - "app": "httpbin", - "version": "v2", - }, - configs: []config.Config{ - newConfig("authz-1", "bar", policyWithSelector), - }, - wantAllow: nil, - }, - { - name: "namespace not match", - ns: "foo", - workloadLabels: map[string]string{ - "app": "httpbin", - "version": "v1", - }, - configs: []config.Config{ - newConfig("authz-1", "bar", policyWithSelector), - }, - wantAllow: nil, - }, - { - name: "root namespace", - ns: "bar", - configs: []config.Config{ - newConfig("authz-1", "istio-config", policy), - }, - wantAllow: []AuthorizationPolicy{ - { - Name: "authz-1", - Namespace: "istio-config", - Spec: policy, - }, - }, - }, - { - name: "root namespace equals config namespace", - ns: "istio-config", - configs: []config.Config{ - newConfig("authz-1", "istio-config", policy), - }, - wantAllow: []AuthorizationPolicy{ - { - Name: "authz-1", - Namespace: "istio-config", - Spec: policy, - }, - }, - }, - { - name: "root namespace and config namespace", - ns: "bar", - configs: []config.Config{ - newConfig("authz-1", "istio-config", policy), - newConfig("authz-2", "bar", policy), - }, - wantAllow: []AuthorizationPolicy{ - { - Name: "authz-1", - Namespace: "istio-config", - Spec: policy, - }, - { - Name: "authz-2", - Namespace: "bar", - Spec: policy, - }, - }, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - authzPolicies := createFakeAuthorizationPolicies(tc.configs, t) - - result := authzPolicies.ListAuthorizationPolicies(tc.ns, tc.workloadLabels) - if !reflect.DeepEqual(tc.wantAllow, result.Allow) { - t.Errorf("wantAllow:%v\n but got: %v\n", tc.wantAllow, result.Allow) - } - if !reflect.DeepEqual(tc.wantDeny, result.Deny) { - t.Errorf("wantDeny:%v\n but got: %v\n", tc.wantDeny, result.Deny) - } - if !reflect.DeepEqual(tc.wantAudit, result.Audit) { - t.Errorf("wantAudit:%v\n but got: %v\n", tc.wantAudit, result.Audit) - } - if !reflect.DeepEqual(tc.wantCustom, result.Custom) { - t.Errorf("wantCustom:%v\n but got: %v\n", tc.wantCustom, result.Custom) - } - }) - } -} - -func createFakeAuthorizationPolicies(configs []config.Config, t *testing.T) *AuthorizationPolicies { - store := &authzFakeStore{} - for _, cfg := range configs { - store.add(cfg) - } - environment := &Environment{ - ConfigStore: MakeIstioStore(store), - Watcher: mesh.NewFixedWatcher(&meshconfig.MeshConfig{RootNamespace: "istio-config"}), - } - authzPolicies, err := GetAuthorizationPolicies(environment) - if err != nil { - t.Fatalf("GetAuthorizationPolicies failed: %v", err) - } - return authzPolicies -} - -func newConfig(name, ns string, spec config.Spec) config.Config { - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioSecurityV1Beta1Authorizationpolicies.Resource().GroupVersionKind(), - Name: name, - Namespace: ns, - }, - Spec: spec, - } -} - -type authzFakeStore struct { - data []struct { - typ config.GroupVersionKind - ns string - cfg config.Config - } -} - -func (fs *authzFakeStore) add(cfg config.Config) { - fs.data = append(fs.data, struct { - typ config.GroupVersionKind - ns string - cfg config.Config - }{ - typ: cfg.GroupVersionKind, - ns: cfg.Namespace, - cfg: cfg, - }) -} - -func (fs *authzFakeStore) Schemas() collection.Schemas { - return collection.SchemasFor() -} - -func (fs *authzFakeStore) Get(_ config.GroupVersionKind, _, _ string) *config.Config { - return nil -} - -func (fs *authzFakeStore) List(typ config.GroupVersionKind, namespace string) ([]config.Config, error) { - var configs []config.Config - for _, data := range fs.data { - if data.typ == typ { - if namespace != "" && data.ns == namespace { - continue - } - configs = append(configs, data.cfg) - } - } - return configs, nil -} - -func (fs *authzFakeStore) Delete(_ config.GroupVersionKind, _, _ string, _ *string) error { - return fmt.Errorf("not implemented") -} - -func (fs *authzFakeStore) Create(config.Config) (string, error) { - return "not implemented", nil -} - -func (fs *authzFakeStore) Update(config.Config) (string, error) { - return "not implemented", nil -} - -func (fs *authzFakeStore) UpdateStatus(config.Config) (string, error) { - return "not implemented", nil -} - -func (fs *authzFakeStore) Patch(orig config.Config, patchFn config.PatchFunc) (string, error) { - return "not implemented", nil -} diff --git a/pilot/pkg/model/cluster_local.go b/pilot/pkg/model/cluster_local.go deleted file mode 100644 index de2d17db7..000000000 --- a/pilot/pkg/model/cluster_local.go +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "strings" - "sync" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/host" -) - -var ( - defaultClusterLocalNamespaces = []string{"kube-system"} - defaultClusterLocalServices = []string{"kubernetes.default.svc"} -) - -// ClusterLocalHosts is a map of host names or wildcard patterns which should only -// be made accessible from within the same cluster. -type ClusterLocalHosts map[host.Name]struct{} - -// IsClusterLocal indicates whether the given host should be treated as a -// cluster-local destination. -func (c ClusterLocalHosts) IsClusterLocal(h host.Name) bool { - _, ok := MostSpecificHostMatch2(h, c) - return ok -} - -// ClusterLocalProvider provides the cluster-local hosts. -type ClusterLocalProvider interface { - // GetClusterLocalHosts returns the list of cluster-local hosts, sorted in - // ascending order. The caller must not modify the returned list. - GetClusterLocalHosts() ClusterLocalHosts -} - -// NewClusterLocalProvider returns a new ClusterLocalProvider for the Environment. -func NewClusterLocalProvider(e *Environment) ClusterLocalProvider { - c := &clusterLocalProvider{} - - // Register a handler to update the environment when the mesh config is updated. - e.AddMeshHandler(func() { - c.onMeshUpdated(e) - }) - - // Update the cluster-local hosts now. - c.onMeshUpdated(e) - return c -} - -var _ ClusterLocalProvider = &clusterLocalProvider{} - -type clusterLocalProvider struct { - mutex sync.Mutex - hosts ClusterLocalHosts -} - -func (c *clusterLocalProvider) GetClusterLocalHosts() ClusterLocalHosts { - c.mutex.Lock() - out := c.hosts - c.mutex.Unlock() - return out -} - -func (c *clusterLocalProvider) onMeshUpdated(e *Environment) { - // Create the default list of cluster-local hosts. - domainSuffix := e.DomainSuffix - defaultClusterLocalHosts := make([]host.Name, 0) - for _, n := range defaultClusterLocalNamespaces { - defaultClusterLocalHosts = append(defaultClusterLocalHosts, host.Name("*."+n+".svc."+domainSuffix)) - } - for _, s := range defaultClusterLocalServices { - defaultClusterLocalHosts = append(defaultClusterLocalHosts, host.Name(s+"."+domainSuffix)) - } - - if discoveryHost, _, err := e.GetDiscoveryAddress(); err != nil { - log.Errorf("failed to make discoveryAddress cluster-local: %v", err) - } else { - if !strings.HasSuffix(string(discoveryHost), domainSuffix) { - discoveryHost += host.Name("." + domainSuffix) - } - defaultClusterLocalHosts = append(defaultClusterLocalHosts, discoveryHost) - } - - // Collect the cluster-local hosts. - hosts := make(ClusterLocalHosts, 0) - for _, serviceSettings := range e.Mesh().ServiceSettings { - if serviceSettings.Settings.ClusterLocal { - for _, h := range serviceSettings.Hosts { - hosts[host.Name(h)] = struct{}{} - } - } else { - // Remove defaults if specified to be non-cluster-local. - for _, h := range serviceSettings.Hosts { - for i, defaultClusterLocalHost := range defaultClusterLocalHosts { - if len(defaultClusterLocalHost) > 0 { - if h == string(defaultClusterLocalHost) || - (defaultClusterLocalHost.IsWildCarded() && - strings.HasSuffix(h, string(defaultClusterLocalHost[1:]))) { - // This default was explicitly overridden, so remove it. - defaultClusterLocalHosts[i] = "" - } - } - } - } - } - } - - // Add any remaining defaults to the end of the list. - for _, defaultClusterLocalHost := range defaultClusterLocalHosts { - if len(defaultClusterLocalHost) > 0 { - hosts[defaultClusterLocalHost] = struct{}{} - } - } - - c.mutex.Lock() - c.hosts = hosts - c.mutex.Unlock() -} diff --git a/pilot/pkg/model/cluster_local_test.go b/pilot/pkg/model/cluster_local_test.go deleted file mode 100644 index 2b2fd7454..000000000 --- a/pilot/pkg/model/cluster_local_test.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model_test - -import ( - "testing" -) - -import ( - . "github.com/onsi/gomega" - meshconfig "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" -) - -func TestIsClusterLocal(t *testing.T) { - cases := []struct { - name string - m *meshconfig.MeshConfig - host string - expected bool - }{ - { - name: "kube-system is local", - m: mesh.DefaultMeshConfig(), - host: "s.kube-system.svc.cluster.local", - expected: true, - }, - { - name: "api server local is local", - m: mesh.DefaultMeshConfig(), - host: "kubernetes.default.svc.cluster.local", - expected: true, - }, - { - name: "discovery server is local", - m: mesh.DefaultMeshConfig(), - host: "istiod.dubbo-system.svc.cluster.local", - expected: true, - }, - { - name: "not local by default", - m: mesh.DefaultMeshConfig(), - host: "not.cluster.local", - expected: false, - }, - { - name: "override default namespace", - m: &meshconfig.MeshConfig{ - // Remove the cluster-local setting for kube-system. - ServiceSettings: []*meshconfig.MeshConfig_ServiceSettings{ - { - Settings: &meshconfig.MeshConfig_ServiceSettings_Settings{ - ClusterLocal: false, - }, - Hosts: []string{"*.kube-system.svc.cluster.local"}, - }, - }, - }, - host: "s.kube-system.svc.cluster.local", - expected: false, - }, - { - name: "override default service", - m: &meshconfig.MeshConfig{ - // Remove the cluster-local setting for kube-system. - ServiceSettings: []*meshconfig.MeshConfig_ServiceSettings{ - { - Settings: &meshconfig.MeshConfig_ServiceSettings_Settings{ - ClusterLocal: false, - }, - Hosts: []string{"kubernetes.default.svc.cluster.local"}, - }, - }, - }, - host: "kubernetes.default.svc.cluster.local", - expected: false, - }, - { - name: "local 1", - m: &meshconfig.MeshConfig{ - ServiceSettings: []*meshconfig.MeshConfig_ServiceSettings{ - { - Settings: &meshconfig.MeshConfig_ServiceSettings_Settings{ - ClusterLocal: true, - }, - Hosts: []string{ - "*.ns1.svc.cluster.local", - "*.ns2.svc.cluster.local", - }, - }, - }, - }, - host: "s.ns1.svc.cluster.local", - expected: true, - }, - { - name: "local 2", - m: &meshconfig.MeshConfig{ - ServiceSettings: []*meshconfig.MeshConfig_ServiceSettings{ - { - Settings: &meshconfig.MeshConfig_ServiceSettings_Settings{ - ClusterLocal: true, - }, - Hosts: []string{ - "*.ns1.svc.cluster.local", - "*.ns2.svc.cluster.local", - }, - }, - }, - }, - host: "s.ns2.svc.cluster.local", - expected: true, - }, - { - name: "not local", - m: &meshconfig.MeshConfig{ - ServiceSettings: []*meshconfig.MeshConfig_ServiceSettings{ - { - Settings: &meshconfig.MeshConfig_ServiceSettings_Settings{ - ClusterLocal: true, - }, - Hosts: []string{ - "*.ns1.svc.cluster.local", - "*.ns2.svc.cluster.local", - }, - }, - }, - }, - host: "s.ns3.svc.cluster.local", - expected: false, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - g := NewWithT(t) - - env := &model.Environment{Watcher: mesh.NewFixedWatcher(c.m)} - env.Init() - - clusterLocal := env.ClusterLocal().GetClusterLocalHosts().IsClusterLocal(host.Name(c.host)) - g.Expect(clusterLocal).To(Equal(c.expected)) - }) - } -} diff --git a/pilot/pkg/model/config.go b/pilot/pkg/model/config.go deleted file mode 100644 index 0bbcd817d..000000000 --- a/pilot/pkg/model/config.go +++ /dev/null @@ -1,424 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "crypto/md5" - "encoding/binary" - "net" - "sort" - "strings" -) - -import ( - udpa "github.com/cncf/xds/go/udpa/type/v1" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// Statically link protobuf descriptors from UDPA -var _ = udpa.TypedStruct{} - -// NamespacedName defines a name and namespace of a resource, with the type elided. This can be used in -// places where the type is implied. -// This is preferred to a ConfigKey with empty Kind, especially in performance sensitive code - hashing this struct -// is 2x faster than ConfigKey. -type NamespacedName struct { - Name string - Namespace string -} - -func (key NamespacedName) String() string { - return key.Namespace + "/" + key.Name -} - -// ConfigKey describe a specific config item. -// In most cases, the name is the config's name. However, for ServiceEntry it is service's FQDN. -type ConfigKey struct { - Kind config.GroupVersionKind - Name string - Namespace string -} - -func (key ConfigKey) HashCode() uint64 { - hash := md5.New() - for _, v := range []string{ - key.Name, - key.Namespace, - key.Kind.Kind, - key.Kind.Group, - key.Kind.Version, - } { - hash.Write([]byte(v)) - } - var tmp [md5.Size]byte - sum := hash.Sum(tmp[:0]) - return binary.BigEndian.Uint64(sum) -} - -func (key ConfigKey) String() string { - return key.Kind.Kind + "/" + key.Namespace + "/" + key.Name -} - -// ConfigsOfKind extracts configs of the specified kind. -func ConfigsOfKind(configs map[ConfigKey]struct{}, kind config.GroupVersionKind) map[ConfigKey]struct{} { - ret := make(map[ConfigKey]struct{}) - - for conf := range configs { - if conf.Kind == kind { - ret[conf] = struct{}{} - } - } - - return ret -} - -// ConfigsHaveKind checks if configurations have the specified kind. -func ConfigsHaveKind(configs map[ConfigKey]struct{}, kind config.GroupVersionKind) bool { - for conf := range configs { - if conf.Kind == kind { - return true - } - } - return false -} - -// ConfigNamesOfKind extracts config names of the specified kind. -func ConfigNamesOfKind(configs map[ConfigKey]struct{}, kind config.GroupVersionKind) map[string]struct{} { - ret := sets.New() - - for conf := range configs { - if conf.Kind == kind { - ret.Insert(conf.Name) - } - } - - return ret -} - -// ConfigStore describes a set of platform agnostic APIs that must be supported -// by the underlying platform to store and retrieve Istio configuration. -// -// Configuration key is defined to be a combination of the type, name, and -// namespace of the configuration object. The configuration key is guaranteed -// to be unique in the store. -// -// The storage interface presented here assumes that the underlying storage -// layer supports _Get_ (list), _Update_ (update), _Create_ (create) and -// _Delete_ semantics but does not guarantee any transactional semantics. -// -// _Update_, _Create_, and _Delete_ are mutator operations. These operations -// are asynchronous, and you might not see the effect immediately (e.g. _Get_ -// might not return the object by key immediately after you mutate the store.) -// Intermittent errors might occur even though the operation succeeds, so you -// should always check if the object store has been modified even if the -// mutating operation returns an error. Objects should be created with -// _Create_ operation and updated with _Update_ operation. -// -// Resource versions record the last mutation operation on each object. If a -// mutation is applied to a different revision of an object than what the -// underlying storage expects as defined by pure equality, the operation is -// blocked. The client of this interface should not make assumptions about the -// structure or ordering of the revision identifier. -// -// Object references supplied and returned from this interface should be -// treated as read-only. Modifying them violates thread-safety. -type ConfigStore interface { - // Schemas exposes the configuration type schema known by the config store. - // The type schema defines the bidirectional mapping between configuration - // types and the protobuf encoding schema. - Schemas() collection.Schemas - - // Get retrieves a configuration element by a type and a key - Get(typ config.GroupVersionKind, name, namespace string) *config.Config - - // List returns objects by type and namespace. - // Use "" for the namespace to list across namespaces. - List(typ config.GroupVersionKind, namespace string) ([]config.Config, error) - - // Create adds a new configuration object to the store. If an object with the - // same name and namespace for the type already exists, the operation fails - // with no side effects. - Create(config config.Config) (revision string, err error) - - // Update modifies an existing configuration object in the store. Update - // requires that the object has been created. Resource version prevents - // overriding a value that has been changed between prior _Get_ and _Put_ - // operation to achieve optimistic concurrency. This method returns a new - // revision if the operation succeeds. - Update(config config.Config) (newRevision string, err error) - UpdateStatus(config config.Config) (newRevision string, err error) - - // Patch applies only the modifications made in the PatchFunc rather than doing a full replace. Useful to avoid - // read-modify-write conflicts when there are many concurrent-writers to the same resource. - Patch(orig config.Config, patchFn config.PatchFunc) (string, error) - - // Delete removes an object from the store by key - // For k8s, resourceVersion must be fulfilled before a deletion is carried out. - // If not possible, a 409 Conflict status will be returned. - Delete(typ config.GroupVersionKind, name, namespace string, resourceVersion *string) error -} - -type EventHandler = func(config.Config, config.Config, Event) - -// ConfigStoreController is a local fully-replicated cache of the config store with additional handlers. The -// controller actively synchronizes its local state with the remote store and -// provides a notification mechanism to receive update events. As such, the -// notification handlers must be registered prior to calling _Run_, and the -// cache requires initial synchronization grace period after calling _Run_. -// -// Update notifications require the following consistency guarantee: the view -// in the cache must be AT LEAST as fresh as the moment notification arrives, but -// MAY BE more fresh (e.g. if _Delete_ cancels an _Add_ event). -// -// Handlers execute on the single worker queue in the order they are appended. -// Handlers receive the notification event and the associated object. Note -// that all handlers must be registered before starting the cache controller. -type ConfigStoreController interface { - ConfigStore - - // RegisterEventHandler adds a handler to receive config update events for a - // configuration type - RegisterEventHandler(kind config.GroupVersionKind, handler EventHandler) - - // Run until a signal is received - Run(stop <-chan struct{}) - SetWatchErrorHandler(func(r *cache.Reflector, err error)) error - - // HasSynced returns true after initial cache synchronization is complete - HasSynced() bool -} - -const ( - // NamespaceAll is a designated symbol for listing across all namespaces - NamespaceAll = "" -) - -// ResolveShortnameToFQDN uses metadata information to resolve a reference -// to shortname of the service to FQDN -func ResolveShortnameToFQDN(hostname string, meta config.Meta) host.Name { - out := hostname - // Treat the wildcard hostname as fully qualified. Any other variant of a wildcard hostname will contain a `.` too, - // and skip the next if, so we only need to check for the literal wildcard itself. - if hostname == "*" { - return host.Name(out) - } - - // if the hostname is a valid ipv4 or ipv6 address, do not append domain or namespace - if net.ParseIP(hostname) != nil { - return host.Name(out) - } - - // if FQDN is specified, do not append domain or namespace to hostname - if !strings.Contains(hostname, ".") { - if meta.Namespace != "" { - out = out + "." + meta.Namespace - } - - // FIXME this is a gross hack to hardcode a service's domain name in kubernetes - // BUG this will break non kubernetes environments if they use shortnames in the - // rules. - if meta.Domain != "" { - out = out + ".svc." + meta.Domain - } - } - - return host.Name(out) -} - -// resolveGatewayName uses metadata information to resolve a reference -// to shortname of the gateway to FQDN -func resolveGatewayName(gwname string, meta config.Meta) string { - out := gwname - - // New way of binding to a gateway in remote namespace - // is ns/name. Old way is either FQDN or short name - if !strings.Contains(gwname, "/") { - if !strings.Contains(gwname, ".") { - // we have a short name. Resolve to a gateway in same namespace - out = meta.Namespace + "/" + gwname - } else { - // parse namespace from FQDN. This is very hacky, but meant for backward compatibility only - // This is a legacy FQDN format. Transform name.ns.svc.cluster.local -> ns/name - i := strings.Index(gwname, ".") - fqdn := strings.Index(gwname[i+1:], ".") - if fqdn == -1 { - out = gwname[i+1:] + "/" + gwname[:i] - } else { - out = gwname[i+1:i+1+fqdn] + "/" + gwname[:i] - } - } - } else { - // remove the . from ./gateway and substitute it with the namespace name - i := strings.Index(gwname, "/") - if gwname[:i] == "." { - out = meta.Namespace + "/" + gwname[i+1:] - } - } - return out -} - -// MostSpecificHostMatch compares the map of the stack to the needle, and returns the longest element -// matching the needle, or false if no element in the map matches the needle. -func MostSpecificHostMatch(needle host.Name, m map[host.Name][]*consolidatedDestRule) (host.Name, bool) { - matches := []host.Name{} - - // exact match first - if m != nil { - if _, ok := m[needle]; ok { - return needle, true - } - } - - if needle.IsWildCarded() { - for h := range m { - // both needle and h are wildcards - if h.IsWildCarded() { - if len(needle) < len(h) { - continue - } - if strings.HasSuffix(string(needle[1:]), string(h[1:])) { - matches = append(matches, h) - } - } - } - } else { - for h := range m { - // only n is wildcard - if h.IsWildCarded() { - if strings.HasSuffix(string(needle), string(h[1:])) { - matches = append(matches, h) - } - } - } - } - if len(matches) > 1 { - // Sort the host names, find the most specific one. - sort.Sort(host.Names(matches)) - } - if len(matches) > 0 { - // TODO: return closest match out of all non-exact matching hosts - return matches[0], true - } - return "", false -} - -// MostSpecificHostMatch2 compares the map of the stack to the needle, and returns the longest element -// matching the needle, or false if no element in the map matches the needle. -// TODO: merge with MostSpecificHostMatch once go 1.18 is used -func MostSpecificHostMatch2(needle host.Name, m map[host.Name]struct{}) (host.Name, bool) { - matches := []host.Name{} - - // exact match first - if m != nil { - if _, ok := m[needle]; ok { - return needle, true - } - } - - if needle.IsWildCarded() { - for h := range m { - // both needle and h are wildcards - if h.IsWildCarded() { - if len(needle) < len(h) { - continue - } - if strings.HasSuffix(string(needle[1:]), string(h[1:])) { - matches = append(matches, h) - } - } - } - } else { - for h := range m { - // only n is wildcard - if h.IsWildCarded() { - if strings.HasSuffix(string(needle), string(h[1:])) { - matches = append(matches, h) - } - } - } - } - if len(matches) > 1 { - // Sort the host names, find the most specific one. - sort.Sort(host.Names(matches)) - } - if len(matches) > 0 { - // TODO: return closest match out of all non-exact matching hosts - return matches[0], true - } - return "", false -} - -// istioConfigStore provides a simple adapter for Istio configuration types -// from the generic config registry -type istioConfigStore struct { - ConfigStore -} - -// MakeIstioStore creates a wrapper around a store. -// In pilot it is initialized with a ConfigStoreController, tests only use -// a regular ConfigStore. -func MakeIstioStore(store ConfigStore) ConfigStore { - return &istioConfigStore{store} -} - -func (store *istioConfigStore) ServiceEntries() []config.Config { - serviceEntries, err := store.List(gvk.ServiceEntry, NamespaceAll) - if err != nil { - return nil - } - - // To ensure the ip allocation logic deterministically - // allocates the same IP to a service entry. - sortConfigByCreationTime(serviceEntries) - return serviceEntries -} - -// sortConfigByCreationTime sorts the list of config objects in ascending order by their creation time (if available). -func sortConfigByCreationTime(configs []config.Config) { - sort.Slice(configs, func(i, j int) bool { - // If creation time is the same, then behavior is nondeterministic. In this case, we can - // pick an arbitrary but consistent ordering based on name and namespace, which is unique. - // CreationTimestamp is stored in seconds, so this is not uncommon. - if configs[i].CreationTimestamp == configs[j].CreationTimestamp { - in := configs[i].Name + "." + configs[i].Namespace - jn := configs[j].Name + "." + configs[j].Namespace - return in < jn - } - return configs[i].CreationTimestamp.Before(configs[j].CreationTimestamp) - }) -} - -// key creates a key from a reference's name and namespace. -func key(name, namespace string) string { - return name + "/" + namespace -} - -func (store *istioConfigStore) AuthorizationPolicies(namespace string) []config.Config { - authorizationPolicies, err := store.List(gvk.AuthorizationPolicy, namespace) - if err != nil { - log.Errorf("failed to get AuthorizationPolicy in namespace %s: %v", namespace, err) - return nil - } - - return authorizationPolicies -} diff --git a/pilot/pkg/model/config_test.go b/pilot/pkg/model/config_test.go deleted file mode 100644 index 61f0ddd3b..000000000 --- a/pilot/pkg/model/config_test.go +++ /dev/null @@ -1,408 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model_test - -import ( - "fmt" - "reflect" - "strconv" - "testing" -) - -import ( - "github.com/davecgh/go-spew/spew" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - mock_config "github.com/apache/dubbo-go-pixiu/pilot/test/mock" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/resource" -) - -// getByMessageName finds a schema by message name if it is available -// In test setup, we do not have more than one descriptor with the same message type, so this -// function is ok for testing purpose. -func getByMessageName(schemas collection.Schemas, name string) (collection.Schema, bool) { - for _, s := range schemas.All() { - if s.Resource().Proto() == name { - return s, true - } - } - return nil, false -} - -func schemaFor(kind, proto string) collection.Schema { - return collection.Builder{ - Name: kind, - Resource: resource.Builder{ - Kind: kind, - Plural: kind + "s", - Proto: proto, - }.BuildNoValidate(), - }.MustBuild() -} - -func TestConfigDescriptor(t *testing.T) { - a := schemaFor("a", "proxy.A") - schemas := collection.SchemasFor( - a, - schemaFor("b", "proxy.B"), - schemaFor("c", "proxy.C")) - want := []string{"a", "b", "c"} - got := schemas.Kinds() - if !reflect.DeepEqual(got, want) { - t.Errorf("descriptor.Types() => got %+vwant %+v", spew.Sdump(got), spew.Sdump(want)) - } - - aType, aExists := schemas.FindByGroupVersionKind(a.Resource().GroupVersionKind()) - if !aExists || !reflect.DeepEqual(aType, a) { - t.Errorf("descriptor.GetByType(a) => got %+v, want %+v", aType, a) - } - if _, exists := schemas.FindByGroupVersionKind(config.GroupVersionKind{Kind: "missing"}); exists { - t.Error("descriptor.GetByType(missing) => got true, want false") - } - - aSchema, aSchemaExists := getByMessageName(schemas, a.Resource().Proto()) - if !aSchemaExists || !reflect.DeepEqual(aSchema, a) { - t.Errorf("descriptor.GetByMessageName(a) => got %+v, want %+v", aType, a) - } - _, aSchemaNotExist := getByMessageName(schemas, "blah") - if aSchemaNotExist { - t.Errorf("descriptor.GetByMessageName(blah) => got true, want false") - } -} - -func TestEventString(t *testing.T) { - cases := []struct { - in model.Event - want string - }{ - {model.EventAdd, "add"}, - {model.EventUpdate, "update"}, - {model.EventDelete, "delete"}, - } - for _, c := range cases { - if got := c.in.String(); got != c.want { - t.Errorf("Failed: got %q want %q", got, c.want) - } - } -} - -func TestPortList(t *testing.T) { - pl := model.PortList{ - {Name: "http", Port: 80, Protocol: protocol.HTTP}, - {Name: "http-alt", Port: 8080, Protocol: protocol.HTTP}, - } - - gotNames := pl.GetNames() - wantNames := []string{"http", "http-alt"} - if !reflect.DeepEqual(gotNames, wantNames) { - t.Errorf("GetNames() failed: got %v want %v", gotNames, wantNames) - } - - cases := []struct { - name string - port *model.Port - found bool - }{ - {name: pl[0].Name, port: pl[0], found: true}, - {name: "foobar", found: false}, - } - - for _, c := range cases { - gotPort, gotFound := pl.Get(c.name) - if c.found != gotFound || !reflect.DeepEqual(gotPort, c.port) { - t.Errorf("Get() failed: gotFound=%v wantFound=%v\ngot %+vwant %+v", - gotFound, c.found, spew.Sdump(gotPort), spew.Sdump(c.port)) - } - } -} - -func TestSubsetKey(t *testing.T) { - hostname := host.Name("hostname") - cases := []struct { - hostname host.Name - subset string - port int - want string - }{ - { - hostname: "hostname", - subset: "subset", - port: 80, - want: "outbound|80|subset|hostname", - }, - { - hostname: "hostname", - subset: "", - port: 80, - want: "outbound|80||hostname", - }, - } - - for _, c := range cases { - got := model.BuildSubsetKey(model.TrafficDirectionOutbound, c.subset, hostname, c.port) - if got != c.want { - t.Errorf("Failed: got %q want %q", got, c.want) - } - - // test parse subset key. ParseSubsetKey is the inverse of BuildSubsetKey - _, s, h, p := model.ParseSubsetKey(got) - if s != c.subset || h != c.hostname || p != c.port { - t.Errorf("Failed: got %s,%s,%d want %s,%s,%d", s, h, p, c.subset, c.hostname, c.port) - } - } -} - -func TestLabelsEquals(t *testing.T) { - cases := []struct { - a, b labels.Instance - want bool - }{ - { - a: nil, - b: nil, - want: true, - }, - { - a: nil, - b: labels.Instance{"a": "b"}, - }, - { - a: labels.Instance{"a": "b"}, - b: nil, - }, - { - a: labels.Instance{"a": "b"}, - b: labels.Instance{"a": "b"}, - want: true, - }, - { - a: labels.Instance{"a": "b"}, - b: labels.Instance{"a": "b", "c": "d"}, - }, - { - b: labels.Instance{"a": "b", "c": "d"}, - a: labels.Instance{"a": "b"}, - }, - { - b: labels.Instance{"a": "b", "c": "d"}, - a: labels.Instance{"a": "b", "c": "d"}, - want: true, - }, - } - for _, c := range cases { - if got := c.a.Equals(c.b); got != c.want { - t.Errorf("Failed: got eq=%v want=%v for %q ?= %q", got, c.want, c.a, c.b) - } - } -} - -func TestConfigKey(t *testing.T) { - cfg := mock_config.Make("ns", 2) - want := "test.istio.io/v1/MockConfig/ns/mock-config2" - if key := cfg.Meta.Key(); key != want { - t.Fatalf("config.Key() => got %q, want %q", key, want) - } -} - -func TestResolveShortnameToFQDN(t *testing.T) { - tests := []struct { - name string - meta config.Meta - out host.Name - }{ - { - "*", config.Meta{}, "*", - }, - { - "*", config.Meta{Namespace: "default", Domain: "cluster.local"}, "*", - }, - { - "foo", config.Meta{Namespace: "default", Domain: "cluster.local"}, "foo.default.svc.cluster.local", - }, - { - "foo.bar", config.Meta{Namespace: "default", Domain: "cluster.local"}, "foo.bar", - }, - { - "foo", config.Meta{Domain: "cluster.local"}, "foo.svc.cluster.local", - }, - { - "foo", config.Meta{Namespace: "default"}, "foo.default", - }, - { - "42.185.131.210", config.Meta{Namespace: "default"}, "42.185.131.210", - }, - { - "42.185.131.210", config.Meta{Namespace: "cluster.local"}, "42.185.131.210", - }, - { - "2a00:4000::614", config.Meta{Namespace: "default"}, "2a00:4000::614", - }, - { - "2a00:4000::614", config.Meta{Namespace: "cluster.local"}, "2a00:4000::614", - }, - } - - for idx, tt := range tests { - t.Run(fmt.Sprintf("[%d] %s", idx, tt.out), func(t *testing.T) { - if actual := model.ResolveShortnameToFQDN(tt.name, tt.meta); actual != tt.out { - t.Fatalf("model.ResolveShortnameToFQDN(%q, %v) = %q wanted %q", tt.name, tt.meta, actual, tt.out) - } - }) - } -} - -func TestMostSpecificHostMatch(t *testing.T) { - tests := []struct { - in []host.Name - needle host.Name - want host.Name - }{ - // this has to be a sorted list - {[]host.Name{}, "*", ""}, - {[]host.Name{"*.foo.com", "*.com"}, "bar.foo.com", "*.foo.com"}, - {[]host.Name{"*.foo.com", "*.com"}, "foo.com", "*.com"}, - {[]host.Name{"foo.com", "*.com"}, "*.foo.com", "*.com"}, - - {[]host.Name{"*.foo.com", "foo.com"}, "foo.com", "foo.com"}, - {[]host.Name{"*.foo.com", "foo.com"}, "*.foo.com", "*.foo.com"}, - - // this passes because we sort alphabetically - {[]host.Name{"bar.com", "foo.com"}, "*.com", ""}, - - {[]host.Name{"bar.com", "*.foo.com"}, "*foo.com", ""}, - {[]host.Name{"foo.com", "*.foo.com"}, "*foo.com", ""}, - - // should prioritize closest match - {[]host.Name{"*.bar.com", "foo.bar.com"}, "foo.bar.com", "foo.bar.com"}, - {[]host.Name{"*.foo.bar.com", "bar.foo.bar.com"}, "bar.foo.bar.com", "bar.foo.bar.com"}, - - // should not match non-wildcards for wildcard needle - {[]host.Name{"bar.foo.com", "foo.bar.com"}, "*.foo.com", ""}, - {[]host.Name{"foo.bar.foo.com", "bar.foo.bar.com"}, "*.bar.foo.com", ""}, - } - - for idx, tt := range tests { - m := make(map[host.Name]struct{}) - for _, h := range tt.in { - m[h] = struct{}{} - } - - t.Run(fmt.Sprintf("[%d] %s", idx, tt.needle), func(t *testing.T) { - actual, found := model.MostSpecificHostMatch2(tt.needle, m) - if tt.want != "" && !found { - t.Fatalf("model.MostSpecificHostMatch(%q, %v) = %v, %t; want: %v", tt.needle, tt.in, actual, found, tt.want) - } else if actual != tt.want { - t.Fatalf("model.MostSpecificHostMatch(%q, %v) = %v, %t; want: %v", tt.needle, tt.in, actual, found, tt.want) - } - }) - } -} - -func BenchmarkMostSpecificHostMatch(b *testing.B) { - benchmarks := []struct { - name string - needle host.Name - baseHost string - hosts []host.Name - hostsMap map[host.Name]struct{} - time int - }{ - {"10Exact", host.Name("foo.bar.com.10"), "foo.bar.com", []host.Name{}, nil, 10}, - {"50Exact", host.Name("foo.bar.com.50"), "foo.bar.com", []host.Name{}, nil, 50}, - {"100Exact", host.Name("foo.bar.com.100"), "foo.bar.com", []host.Name{}, nil, 100}, - {"1000Exact", host.Name("foo.bar.com.1000"), "foo.bar.com", []host.Name{}, nil, 1000}, - {"5000Exact", host.Name("foo.bar.com.5000"), "foo.bar.com", []host.Name{}, nil, 5000}, - - {"10DestRuleWildcard", host.Name("foo.bar.com.10"), "*.foo.bar.com", []host.Name{}, nil, 10}, - {"50DestRuleWildcard", host.Name("foo.bar.com.50"), "*.foo.bar.com", []host.Name{}, nil, 50}, - {"100DestRuleWildcard", host.Name("foo.bar.com.100"), "*.foo.bar.com", []host.Name{}, nil, 100}, - {"1000DestRuleWildcard", host.Name("foo.bar.com.1000"), "*.foo.bar.com", []host.Name{}, nil, 1000}, - {"5000DestRuleWildcard", host.Name("foo.bar.com.5000"), "*.foo.bar.com", []host.Name{}, nil, 5000}, - - {"10NeedleWildcard", host.Name("*.bar.foo.bar.com"), "*.foo.bar.com", []host.Name{}, nil, 10}, - {"50NeedleWildcard", host.Name("*.bar.foo.bar.com"), "*.foo.bar.com", []host.Name{}, nil, 50}, - {"100NeedleWildcard", host.Name("*.bar.foo.bar.com"), "*.foo.bar.com", []host.Name{}, nil, 100}, - {"1000NeedleWildcard", host.Name("*.bar.foo.bar.com"), "*.foo.bar.com", []host.Name{}, nil, 1000}, - {"5000NeedleWildcard", host.Name("*.bar.foo.bar.com"), "*.foo.bar.com", []host.Name{}, nil, 5000}, - } - - for _, bm := range benchmarks { - bm.hostsMap = make(map[host.Name]struct{}, bm.time) - - for i := 1; i <= bm.time; i++ { - h := host.Name(bm.baseHost + "." + strconv.Itoa(i)) - bm.hostsMap[h] = struct{}{} - } - b.Run(bm.name, func(b *testing.B) { - for n := 0; n < b.N; n++ { - _, _ = model.MostSpecificHostMatch2(bm.needle, bm.hostsMap) - } - }) - } -} - -func TestConfigsOnlyHaveKind(t *testing.T) { - tests := []struct { - name string - configs map[model.ConfigKey]struct{} - want bool - }{ - { - name: "mix", - configs: map[model.ConfigKey]struct{}{ - {Kind: gvk.Deployment}: {}, - {Kind: gvk.Secret}: {}, - }, - want: true, - }, - { - name: "no secret", - configs: map[model.ConfigKey]struct{}{ - {Kind: gvk.Deployment}: {}, - }, - want: false, - }, - { - name: "only secret", - configs: map[model.ConfigKey]struct{}{ - {Kind: gvk.Secret}: {}, - {Kind: gvk.Secret}: {}, - }, - want: true, - }, - { - name: "empty", - configs: map[model.ConfigKey]struct{}{}, - want: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := model.ConfigsHaveKind(tt.configs, gvk.Secret) - if tt.want != got { - t.Errorf("got %v want %v", got, tt.want) - } - }) - } -} diff --git a/pilot/pkg/model/context.go b/pilot/pkg/model/context.go deleted file mode 100644 index 2f63c5c3f..000000000 --- a/pilot/pkg/model/context.go +++ /dev/null @@ -1,1117 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "bytes" - "encoding/json" - "fmt" - "net" - "regexp" - "sort" - "strconv" - "strings" - "sync" - "time" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "github.com/golang/protobuf/jsonpb" // nolint: staticcheck - any "google.golang.org/protobuf/types/known/anypb" - "google.golang.org/protobuf/types/known/structpb" - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/ledger" - "istio.io/pkg/monitoring" -) - -import ( - istionetworking "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/trustbundle" - networkutil "github.com/apache/dubbo-go-pixiu/pilot/pkg/util/network" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/network" - "github.com/apache/dubbo-go-pixiu/pkg/spiffe" - "github.com/apache/dubbo-go-pixiu/pkg/util/identifier" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -var _ mesh.Holder = &Environment{} - -// Environment provides an aggregate environmental API for Pilot -type Environment struct { - // Discovery interface for listing services and instances. - ServiceDiscovery - - // Config interface for listing routing rules - ConfigStore - - // Watcher is the watcher for the mesh config (to be merged into the config store) - mesh.Watcher - - // NetworksWatcher (loaded from a config map) provides information about the - // set of networks inside a mesh and how to route to endpoints in each - // network. Each network provides information about the endpoints in a - // routable L3 network. A single routable L3 network can have one or more - // service registries. - NetworksWatcher mesh.NetworksWatcher - - NetworkManager *NetworkManager - - // PushContext holds information during push generation. It is reset on config change, at the beginning - // of the pushAll. It will hold all errors and stats and possibly caches needed during the entire cache computation. - // DO NOT USE EXCEPT FOR TESTS AND HANDLING OF NEW CONNECTIONS. - // ALL USE DURING A PUSH SHOULD USE THE ONE CREATED AT THE - // START OF THE PUSH, THE GLOBAL ONE MAY CHANGE AND REFLECT A DIFFERENT - // CONFIG AND PUSH - PushContext *PushContext - - // DomainSuffix provides a default domain for the Istio server. - DomainSuffix string - - ledger ledger.Ledger - - // TrustBundle: List of Mesh TrustAnchors - TrustBundle *trustbundle.TrustBundle - - clusterLocalServices ClusterLocalProvider - - GatewayAPIController GatewayController -} - -func (e *Environment) Mesh() *meshconfig.MeshConfig { - if e != nil && e.Watcher != nil { - return e.Watcher.Mesh() - } - return nil -} - -// GetDiscoveryAddress parses the DiscoveryAddress specified via MeshConfig. -func (e *Environment) GetDiscoveryAddress() (host.Name, string, error) { - proxyConfig := mesh.DefaultProxyConfig() - if e.Mesh().DefaultConfig != nil { - proxyConfig = e.Mesh().DefaultConfig - } - hostname, port, err := net.SplitHostPort(proxyConfig.DiscoveryAddress) - if err != nil { - return "", "", fmt.Errorf("invalid Istiod Address: %s, %v", proxyConfig.DiscoveryAddress, err) - } - if _, err := strconv.Atoi(port); err != nil { - return "", "", fmt.Errorf("invalid Istiod Port: %s, %s, %v", port, proxyConfig.DiscoveryAddress, err) - } - return host.Name(hostname), port, nil -} - -func (e *Environment) AddMeshHandler(h func()) { - if e != nil && e.Watcher != nil { - e.Watcher.AddMeshHandler(h) - } -} - -func (e *Environment) AddNetworksHandler(h func()) { - if e != nil && e.NetworksWatcher != nil { - e.NetworksWatcher.AddNetworksHandler(h) - } -} - -func (e *Environment) AddMetric(metric monitoring.Metric, key string, proxyID, msg string) { - if e != nil && e.PushContext != nil { - e.PushContext.AddMetric(metric, key, proxyID, msg) - } -} - -func (e *Environment) Version() string { - if x := e.GetLedger(); x != nil { - return x.RootHash() - } - return "" -} - -// Init initializes the Environment for use. -func (e *Environment) Init() { - // Use a default DomainSuffix, if none was provided. - if len(e.DomainSuffix) == 0 { - e.DomainSuffix = constants.DefaultKubernetesDomain - } - - // Create the cluster-local service registry. - e.clusterLocalServices = NewClusterLocalProvider(e) -} - -func (e *Environment) InitNetworksManager(updater XDSUpdater) (err error) { - e.NetworkManager, err = NewNetworkManager(e, updater) - return -} - -func (e *Environment) ClusterLocal() ClusterLocalProvider { - return e.clusterLocalServices -} - -func (e *Environment) GetLedger() ledger.Ledger { - return e.ledger -} - -func (e *Environment) SetLedger(l ledger.Ledger) { - e.ledger = l -} - -// Resources is an alias for array of marshaled resources. -type Resources = []*discovery.Resource - -// DeletedResources is an alias for array of strings that represent removed resources in delta. -type DeletedResources = []string - -func AnyToUnnamedResources(r []*any.Any) Resources { - a := make(Resources, 0, len(r)) - for _, rr := range r { - a = append(a, &discovery.Resource{Resource: rr}) - } - return a -} - -func ResourcesToAny(r Resources) []*any.Any { - a := make([]*any.Any, 0, len(r)) - for _, rr := range r { - a = append(a, rr.Resource) - } - return a -} - -// XdsUpdates include information about the subset of updated resources. -// See for example EDS incremental updates. -type XdsUpdates = map[ConfigKey]struct{} - -// XdsLogDetails contains additional metadata that is captured by Generators and used by xds processors -// like Ads and Delta to uniformly log. -type XdsLogDetails struct { - Incremental bool - AdditionalInfo string -} - -var DefaultXdsLogDetails = XdsLogDetails{} - -// XdsResourceGenerator creates the response for a typeURL DiscoveryRequest or DeltaDiscoveryRequest. If no generator -// is associated with a Proxy, the default (a networking.core.ConfigGenerator instance) will be used. -// The server may associate a different generator based on client metadata. Different -// WatchedResources may use same or different Generator. -// Note: any errors returned will completely close the XDS stream. Use with caution; typically and empty -// or no response is preferred. -type XdsResourceGenerator interface { - // Generate generates the Sotw resources for Xds. - Generate(proxy *Proxy, w *WatchedResource, req *PushRequest) (Resources, XdsLogDetails, error) -} - -// XdsDeltaResourceGenerator generates Sotw and delta resources. -type XdsDeltaResourceGenerator interface { - XdsResourceGenerator - // GenerateDeltas returns the changed and removed resources, along with whether or not delta was actually used. - GenerateDeltas(proxy *Proxy, req *PushRequest, w *WatchedResource) (Resources, DeletedResources, XdsLogDetails, bool, error) -} - -// Proxy contains information about an specific instance of a proxy (envoy sidecar, gateway, -// etc). The Proxy is initialized when a sidecar connects to Pilot, and populated from -// 'node' info in the protocol as well as data extracted from registries. -// -// In current Istio implementation nodes use a 4-parts '~' delimited ID. -// Type~IPAddress~ID~Domain -type Proxy struct { - sync.RWMutex - - // Type specifies the node type. First part of the ID. - Type NodeType - - // IPAddresses is the IP addresses of the proxy used to identify it and its - // co-located service instances. Example: "10.60.1.6". In some cases, the host - // where the proxy and service instances reside may have more than one IP address - IPAddresses []string - - // ID is the unique platform-specific sidecar proxy ID. For k8s it is the pod ID and - // namespace . - ID string - - // Locality is the location of where Envoy proxy runs. This is extracted from - // the registry where possible. If the registry doesn't provide a locality for the - // proxy it will use the one sent via ADS that can be configured in the Envoy bootstrap - Locality *core.Locality - - // DNSDomain defines the DNS domain suffix for short hostnames (e.g. - // "default.svc.cluster.local") - DNSDomain string - - // ConfigNamespace defines the namespace where this proxy resides - // for the purposes of network scoping. - // NOTE: DO NOT USE THIS FIELD TO CONSTRUCT DNS NAMES - ConfigNamespace string - - // Metadata key-value pairs extending the Node identifier - Metadata *NodeMetadata - - // the sidecarScope associated with the proxy - SidecarScope *SidecarScope - - // the sidecarScope associated with the proxy previously - PrevSidecarScope *SidecarScope - - // The merged gateways associated with the proxy if this is a Router - MergedGateway *MergedGateway - - // service instances associated with the proxy - ServiceInstances []*ServiceInstance - - // Istio version associated with the Proxy - IstioVersion *IstioVersion - - // VerifiedIdentity determines whether a proxy had its identity verified. This - // generally occurs by JWT or mTLS authentication. This can be false when - // connecting over plaintext. If this is set to true, we can verify the proxy has - // access to ConfigNamespace namespace. However, other options such as node type - // are not part of an Istio identity and thus are not verified. - VerifiedIdentity *spiffe.Identity - - // IPMode of proxy. - ipMode IPMode - - // GlobalUnicastIP stores the global unicast IP if available, otherwise nil - GlobalUnicastIP string - - // XdsResourceGenerator is used to generate resources for the node, based on the PushContext. - // If nil, the default networking/core v2 generator is used. This field can be set - // at connect time, based on node metadata, to trigger generation of a different style - // of configuration. - XdsResourceGenerator XdsResourceGenerator - - // WatchedResources contains the list of watched resources for the proxy, keyed by the DiscoveryRequest TypeUrl. - WatchedResources map[string]*WatchedResource - - // XdsNode is the xDS node identifier - XdsNode *core.Node - - AutoregisteredWorkloadEntryName string - - // LastPushContext stores the most recent push context for this proxy. This will be monotonically - // increasing in version. Requests should send config based on this context; not the global latest. - // Historically, the latest was used which can cause problems when computing whether a push is - // required, as the computed sidecar scope version would not monotonically increase. - LastPushContext *PushContext - // LastPushTime records the time of the last push. This is used in conjunction with - // LastPushContext; the XDS cache depends on knowing the time of the PushContext to determine if a - // key is stale or not. - LastPushTime time.Time -} - -// WatchedResource tracks an active DiscoveryRequest subscription. -type WatchedResource struct { - // TypeUrl is copied from the DiscoveryRequest.TypeUrl that initiated watching this resource. - // nolint - TypeUrl string - - // ResourceNames tracks the list of resources that are actively watched. - // For LDS and CDS, all resources of the TypeUrl type are watched if it is empty. - // For endpoints the resource names will have list of clusters and for clusters it is empty. - // For Delta Xds, all resources of the TypeUrl that a client has subscribed to. - ResourceNames []string - - // VersionSent is the version of the resource included in the last sent response. - // It corresponds to the [Cluster/Route/Listener]VersionSent in the XDS package. - VersionSent string - - // NonceSent is the nonce sent in the last sent response. If it is equal with NonceAcked, the - // last message has been processed. If empty: we never sent a message of this type. - NonceSent string - - // NonceAcked is the last acked message. - NonceAcked string - - // NonceNacked is the last nacked message. This is reset following a successful ACK - NonceNacked string - - // LastSent tracks the time of the generated push, to determine the time it takes the client to ack. - LastSent time.Time - - // LastResources tracks the contents of the last push. - // This field is extremely expensive to maintain and is typically disabled - LastResources Resources -} - -var istioVersionRegexp = regexp.MustCompile(`^([1-9]+)\.([0-9]+)(\.([0-9]+))?`) - -// StringList is a list that will be marshaled to a comma separate string in Json -type StringList []string - -func (l StringList) MarshalJSON() ([]byte, error) { - if l == nil { - return nil, nil - } - return []byte(`"` + strings.Join(l, ",") + `"`), nil -} - -func (l *StringList) UnmarshalJSON(data []byte) error { - if len(data) < 2 || string(data) == `""` { - *l = []string{} - } else { - *l = strings.Split(string(data[1:len(data)-1]), ",") - } - return nil -} - -// PodPort describes a mapping of port name to port number. Generally, this is just the definition of -// a port in Kubernetes, but without depending on Kubernetes api. -type PodPort struct { - // If specified, this must be an IANA_SVC_NAME and unique within the pod. Each - // named port in a pod must have a unique name. Name for the port that can be - // referred to by services. - // +optional - Name string `json:"name,omitempty"` - // Number of port to expose on the pod's IP address. - // This must be a valid port number, 0 < x < 65536. - ContainerPort int `json:"containerPort"` - // Name of the protocol - Protocol string `json:"protocol"` -} - -// PodPortList defines a list of PodPort's that is serialized as a string -// This is for legacy reasons, where proper JSON was not supported and was written as a string -type PodPortList []PodPort - -func (l PodPortList) MarshalJSON() ([]byte, error) { - if l == nil { - return nil, nil - } - b, err := json.Marshal([]PodPort(l)) - if err != nil { - return nil, err - } - b = bytes.ReplaceAll(b, []byte{'"'}, []byte{'\\', '"'}) - out := append([]byte{'"'}, b...) - out = append(out, '"') - return out, nil -} - -func (l *PodPortList) UnmarshalJSON(data []byte) error { - var pl []PodPort - pls, err := strconv.Unquote(string(data)) - if err != nil { - return err - } - if err := json.Unmarshal([]byte(pls), &pl); err != nil { - return err - } - *l = pl - return nil -} - -// StringBool defines a boolean that is serialized as a string for legacy reasons -type StringBool bool - -func (s StringBool) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf(`"%t"`, s)), nil -} - -func (s *StringBool) UnmarshalJSON(data []byte) error { - pls, err := strconv.Unquote(string(data)) - if err != nil { - return err - } - b, err := strconv.ParseBool(pls) - if err != nil { - return err - } - *s = StringBool(b) - return nil -} - -// ProxyConfig can only be marshaled using (gogo) jsonpb. However, the rest of node meta is not a proto -// To allow marshaling, we need to define a custom type that calls out to the gogo marshaller -type NodeMetaProxyConfig meshconfig.ProxyConfig - -func (s *NodeMetaProxyConfig) MarshalJSON() ([]byte, error) { - var buf bytes.Buffer - pc := (*meshconfig.ProxyConfig)(s) - if err := (&jsonpb.Marshaler{}).Marshal(&buf, pc); err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -func (s *NodeMetaProxyConfig) UnmarshalJSON(data []byte) error { - pc := (*meshconfig.ProxyConfig)(s) - return jsonpb.Unmarshal(bytes.NewReader(data), pc) -} - -// Node is a typed version of Envoy node with metadata. -type Node struct { - // ID of the Envoy node - ID string - // Metadata is the typed node metadata - Metadata *BootstrapNodeMetadata - // RawMetadata is the untyped node metadata - RawMetadata map[string]interface{} - // Locality from Envoy bootstrap - Locality *core.Locality -} - -// BootstrapNodeMetadata is a superset of NodeMetadata, intended to model the entirety of the node metadata -// we configure in the Envoy bootstrap. This is split out from NodeMetadata to explicitly segment the parameters -// that are consumed by Pilot from the parameters used only as part of the bootstrap. Fields used by bootstrap only -// are consumed by Envoy itself, such as the telemetry filters. -type BootstrapNodeMetadata struct { - NodeMetadata - - // InstanceName is the short name for the workload instance (ex: pod name) - // replaces POD_NAME - InstanceName string `json:"NAME,omitempty"` - - // WorkloadName specifies the name of the workload represented by this node. - WorkloadName string `json:"WORKLOAD_NAME,omitempty"` - - // Owner specifies the workload owner (opaque string). Typically, this is the owning controller of - // of the workload instance (ex: k8s deployment for a k8s pod). - Owner string `json:"OWNER,omitempty"` - - // PilotSAN is the list of subject alternate names for the xDS server. - PilotSubjectAltName []string `json:"PILOT_SAN,omitempty"` - - // XDSRootCert defines the root cert to use for XDS connections - XDSRootCert string `json:"-"` - - // OutlierLogPath is the cluster manager outlier event log path. - OutlierLogPath string `json:"OUTLIER_LOG_PATH,omitempty"` - - // ProvCertDir is the directory containing pre-provisioned certs. - ProvCert string `json:"PROV_CERT,omitempty"` - - // AppContainers is the list of containers in the pod. - AppContainers string `json:"APP_CONTAINERS,omitempty"` - - // IstioProxySHA is the SHA of the proxy version. - IstioProxySHA string `json:"ISTIO_PROXY_SHA,omitempty"` -} - -// NodeMetadata defines the metadata associated with a proxy -// Fields should not be assumed to exist on the proxy, especially newly added fields which will not exist -// on older versions. -// The JSON field names should never change, as they are needed for backward compatibility with older proxies -// nolint: maligned -type NodeMetadata struct { - // ProxyConfig defines the proxy config specified for a proxy. - // Note that this setting may be configured different for each proxy, due user overrides - // or from different versions of proxies connecting. While Pilot has access to the meshConfig.defaultConfig, - // this field should be preferred if it is present. - ProxyConfig *NodeMetaProxyConfig `json:"PROXY_CONFIG,omitempty"` - - // IstioVersion specifies the Istio version associated with the proxy - IstioVersion string `json:"ISTIO_VERSION,omitempty"` - - // IstioRevision specifies the Istio revision associated with the proxy. - // Mostly used when istiod requests the upstream. - IstioRevision string `json:"ISTIO_REVISION,omitempty"` - - // Labels specifies the set of workload instance (ex: k8s pod) labels associated with this node. - Labels map[string]string `json:"LABELS,omitempty"` - - // Labels specifies the set of workload instance (ex: k8s pod) annotations associated with this node. - Annotations map[string]string `json:"ANNOTATIONS,omitempty"` - - // InstanceIPs is the set of IPs attached to this proxy - InstanceIPs StringList `json:"INSTANCE_IPS,omitempty"` - - // Namespace is the namespace in which the workload instance is running. - Namespace string `json:"NAMESPACE,omitempty"` - - // InterceptionMode is the name of the metadata variable that carries info about - // traffic interception mode at the proxy - InterceptionMode TrafficInterceptionMode `json:"INTERCEPTION_MODE,omitempty"` - - // ServiceAccount specifies the service account which is running the workload. - ServiceAccount string `json:"SERVICE_ACCOUNT,omitempty"` - - // HTTPProxyPort enables http proxy on the port for the current sidecar. - // Same as MeshConfig.HttpProxyPort, but with per/sidecar scope. - HTTPProxyPort string `json:"HTTP_PROXY_PORT,omitempty"` - - // MeshID specifies the mesh ID environment variable. - MeshID string `json:"MESH_ID,omitempty"` - - // ClusterID defines the cluster the node belongs to. - ClusterID cluster.ID `json:"CLUSTER_ID,omitempty"` - - // Network defines the network the node belongs to. It is an optional metadata, - // set at injection time. When set, the Endpoints returned to a node and not on same network - // will be replaced with the gateway defined in the settings. - Network network.ID `json:"NETWORK,omitempty"` - - // RequestedNetworkView specifies the networks that the proxy wants to see - RequestedNetworkView StringList `json:"REQUESTED_NETWORK_VIEW,omitempty"` - - // PodPorts defines the ports on a pod. This is used to lookup named ports. - PodPorts PodPortList `json:"POD_PORTS,omitempty"` - - // TLSServerCertChain is the absolute path to server cert-chain file - TLSServerCertChain string `json:"TLS_SERVER_CERT_CHAIN,omitempty"` - // TLSServerKey is the absolute path to server private key file - TLSServerKey string `json:"TLS_SERVER_KEY,omitempty"` - // TLSServerRootCert is the absolute path to server root cert file - TLSServerRootCert string `json:"TLS_SERVER_ROOT_CERT,omitempty"` - // TLSClientCertChain is the absolute path to client cert-chain file - TLSClientCertChain string `json:"TLS_CLIENT_CERT_CHAIN,omitempty"` - // TLSClientKey is the absolute path to client private key file - TLSClientKey string `json:"TLS_CLIENT_KEY,omitempty"` - // TLSClientRootCert is the absolute path to client root cert file - TLSClientRootCert string `json:"TLS_CLIENT_ROOT_CERT,omitempty"` - - CertBaseDir string `json:"BASE,omitempty"` - - // IdleTimeout specifies the idle timeout for the proxy, in duration format (10s). - // If not set, default timeout is 1 hour. - IdleTimeout string `json:"IDLE_TIMEOUT,omitempty"` - - // HTTP10 indicates the application behind the sidecar is making outbound http requests with HTTP/1.0 - // protocol. It will enable the "AcceptHttp_10" option on the http options for outbound HTTP listeners. - // Alpha in 1.1, based on feedback may be turned into an API or change. Set to "1" to enable. - HTTP10 string `json:"HTTP10,omitempty"` - - // Generator indicates the client wants to use a custom Generator plugin. - Generator string `json:"GENERATOR,omitempty"` - - // DNSCapture indicates whether the workload has enabled dns capture - DNSCapture StringBool `json:"DNS_CAPTURE,omitempty"` - - // DNSAutoAllocate indicates whether the workload should have auto allocated addresses for ServiceEntry - // This allows resolving ServiceEntries, which is especially useful for distinguishing TCP traffic - // This depends on DNSCapture. - DNSAutoAllocate StringBool `json:"DNS_AUTO_ALLOCATE,omitempty"` - - // AutoRegister will enable auto registration of the connected endpoint to the service registry using the given WorkloadGroup name - AutoRegisterGroup string `json:"AUTO_REGISTER_GROUP,omitempty"` - - // UnprivilegedPod is used to determine whether a Gateway Pod can open ports < 1024 - UnprivilegedPod string `json:"UNPRIVILEGED_POD,omitempty"` - - // PlatformMetadata contains any platform specific metadata - PlatformMetadata map[string]string `json:"PLATFORM_METADATA,omitempty"` - - // StsPort specifies the port of security token exchange server (STS). - // Used by envoy filters - StsPort string `json:"STS_PORT,omitempty"` - - // Envoy status port redirecting to agent status port. - EnvoyStatusPort int `json:"ENVOY_STATUS_PORT,omitempty"` - - // Envoy prometheus port redirecting to admin port prometheus endpoint. - EnvoyPrometheusPort int `json:"ENVOY_PROMETHEUS_PORT,omitempty"` - - // ExitOnZeroActiveConnections terminates Envoy if there are no active connections if set. - ExitOnZeroActiveConnections StringBool `json:"EXIT_ON_ZERO_ACTIVE_CONNECTIONS,omitempty"` - - // InboundListenerExactBalance sets connection balance config to use exact_balance for virtualInbound, - // as long as QUIC, since it uses UDP, isn't also used. - InboundListenerExactBalance StringBool `json:"INBOUND_LISTENER_EXACT_BALANCE,omitempty"` - - // OutboundListenerExactBalance sets connection balance config to use exact_balance for outbound - // redirected tcp listeners. This does not change the virtualOutbound listener. - OutboundListenerExactBalance StringBool `json:"OUTBOUND_LISTENER_EXACT_BALANCE,omitempty"` - - // The istiod address when running ASM Managed Control Plane. - CloudrunAddr string `json:"CLOUDRUN_ADDR,omitempty"` - - // Contains a copy of the raw metadata. This is needed to lookup arbitrary values. - // If a value is known ahead of time it should be added to the struct rather than reading from here, - Raw map[string]interface{} `json:"-"` -} - -// ProxyConfigOrDefault is a helper function to get the ProxyConfig from metadata, or fallback to a default -// This is useful as the logic should check for proxy config from proxy first and then defer to mesh wide defaults -// if not present. -func (m NodeMetadata) ProxyConfigOrDefault(def *meshconfig.ProxyConfig) *meshconfig.ProxyConfig { - if m.ProxyConfig != nil { - return (*meshconfig.ProxyConfig)(m.ProxyConfig) - } - return def -} - -// GetView returns a restricted view of the mesh for this proxy. The view can be -// restricted by network (via ISTIO_META_REQUESTED_NETWORK_VIEW). -// If not set, we assume that the proxy wants to see endpoints in any network. -func (node *Proxy) GetView() ProxyView { - return newProxyView(node) -} - -// InNetwork returns true if the proxy is on the given network, or if either -// the proxy's network or the given network is unspecified (""). -func (node *Proxy) InNetwork(network network.ID) bool { - return node == nil || identifier.IsSameOrEmpty(network.String(), node.Metadata.Network.String()) -} - -// InCluster returns true if the proxy is in the given cluster, or if either -// the proxy's cluster id or the given cluster id is unspecified (""). -func (node *Proxy) InCluster(cluster cluster.ID) bool { - return node == nil || identifier.IsSameOrEmpty(cluster.String(), node.Metadata.ClusterID.String()) -} - -func (m *BootstrapNodeMetadata) UnmarshalJSON(data []byte) error { - // Create a new type from the target type to avoid recursion. - type BootstrapNodeMetadata2 BootstrapNodeMetadata - - t2 := &BootstrapNodeMetadata2{} - if err := json.Unmarshal(data, t2); err != nil { - return err - } - var raw map[string]interface{} - if err := json.Unmarshal(data, &raw); err != nil { - return err - } - *m = BootstrapNodeMetadata(*t2) - m.Raw = raw - - return nil -} - -// ToStruct converts NodeMetadata to a protobuf structure. This should be used only for debugging - performance is bad. -func (m NodeMetadata) ToStruct() *structpb.Struct { - j, err := json.Marshal(m) - if err != nil { - return nil - } - - pbs := &structpb.Struct{} - if err := protomarshal.Unmarshal(j, pbs); err != nil { - return nil - } - - return pbs -} - -// IstioVersion encodes the Istio version of the proxy. This is a low key way to -// do semver style comparisons and generate the appropriate envoy config -type IstioVersion struct { - Major int - Minor int - Patch int -} - -var MaxIstioVersion = &IstioVersion{Major: 65535, Minor: 65535, Patch: 65535} - -// Compare returns -1/0/1 if version is less than, equal or greater than inv -// To compare only on major, call this function with { X, -1, -1}. -// to compare only on major & minor, call this function with {X, Y, -1}. -func (pversion *IstioVersion) Compare(inv *IstioVersion) int { - // check major - if r := compareVersion(pversion.Major, inv.Major); r != 0 { - return r - } - - // check minor - if inv.Minor > -1 { - if r := compareVersion(pversion.Minor, inv.Minor); r != 0 { - return r - } - - // check patch - if inv.Patch > -1 { - if r := compareVersion(pversion.Patch, inv.Patch); r != 0 { - return r - } - } - } - return 0 -} - -func compareVersion(ov, nv int) int { - if ov == nv { - return 0 - } - if ov < nv { - return -1 - } - return 1 -} - -// NodeType decides the responsibility of the proxy serves in the mesh -type NodeType string - -const ( - // SidecarProxy type is used for sidecar proxies in the application containers - SidecarProxy NodeType = "sidecar" - - // Router type is used for standalone proxies acting as L7/L4 routers - Router NodeType = "router" -) - -var NodeTypes = [...]NodeType{SidecarProxy, Router} - -// IPMode represents the IP mode of proxy. -type IPMode int - -// IPMode constants starting with index 1. -const ( - IPv4 IPMode = iota + 1 - IPv6 - Dual -) - -// IsApplicationNodeType verifies that the NodeType is one of the declared constants in the model -func IsApplicationNodeType(nType NodeType) bool { - switch nType { - case SidecarProxy, Router: - return true - default: - return false - } -} - -// ServiceNode encodes the proxy node attributes into a URI-acceptable string -func (node *Proxy) ServiceNode() string { - ip := "" - if len(node.IPAddresses) > 0 { - ip = node.IPAddresses[0] - } - return strings.Join([]string{ - string(node.Type), ip, node.ID, node.DNSDomain, - }, serviceNodeSeparator) -} - -// SetSidecarScope identifies the sidecar scope object associated with this -// proxy and updates the proxy Node. This is a convenience hack so that -// callers can simply call push.Services(node) while the implementation of -// push.Services can return the set of services from the proxyNode's -// sidecar scope or from the push context's set of global services. Similar -// logic applies to push.VirtualServices and push.DestinationRule. The -// short cut here is useful only for CDS and parts of RDS generation code. -// -// Listener generation code will still use the SidecarScope object directly -// as it needs the set of services for each listener port. -func (node *Proxy) SetSidecarScope(ps *PushContext) { - sidecarScope := node.SidecarScope - - if node.Type == SidecarProxy { - node.SidecarScope = ps.getSidecarScope(node, node.Metadata.Labels) - } else { - // Gateways should just have a default scope with egress: */* - node.SidecarScope = ps.getSidecarScope(node, nil) - } - node.PrevSidecarScope = sidecarScope -} - -// SetGatewaysForProxy merges the Gateway objects associated with this -// proxy and caches the merged object in the proxy Node. This is a convenience hack so that -// callers can simply call push.MergedGateways(node) instead of having to -// fetch all the gateways and invoke the merge call in multiple places (lds/rds). -// Must be called after ServiceInstances are set -func (node *Proxy) SetGatewaysForProxy(ps *PushContext) { - if node.Type != Router { - return - } - node.MergedGateway = ps.mergeGateways(node) -} - -func (node *Proxy) SetServiceInstances(serviceDiscovery ServiceDiscovery) { - instances := serviceDiscovery.GetProxyServiceInstances(node) - - // Keep service instances in order of creation/hostname. - sort.SliceStable(instances, func(i, j int) bool { - if instances[i].Service != nil && instances[j].Service != nil { - if !instances[i].Service.CreationTime.Equal(instances[j].Service.CreationTime) { - return instances[i].Service.CreationTime.Before(instances[j].Service.CreationTime) - } - // Additionally, sort by hostname just in case services created automatically at the same second. - return instances[i].Service.Hostname < instances[j].Service.Hostname - } - return true - }) - - node.ServiceInstances = instances -} - -// SetWorkloadLabels will set the node.Metadata.Labels only when it is nil. -func (node *Proxy) SetWorkloadLabels(env *Environment) { - // First get the workload labels from node meta - if len(node.Metadata.Labels) > 0 { - return - } - // Fallback to calling GetProxyWorkloadLabels - node.Metadata.Labels = env.GetProxyWorkloadLabels(node) -} - -// DiscoverIPMode discovers the IP Versions supported by Proxy based on its IP addresses. -func (node *Proxy) DiscoverIPMode() { - if networkutil.AllIPv4(node.IPAddresses) { - node.ipMode = IPv4 - } else if networkutil.AllIPv6(node.IPAddresses) { - node.ipMode = IPv6 - } else { - node.ipMode = Dual - } - node.GlobalUnicastIP = networkutil.GlobalUnicastIP(node.IPAddresses) -} - -// SupportsIPv4 returns true if proxy supports IPv4 addresses. -func (node *Proxy) SupportsIPv4() bool { - return node.ipMode == IPv4 || node.ipMode == Dual -} - -// SupportsIPv6 returns true if proxy supports IPv6 addresses. -func (node *Proxy) SupportsIPv6() bool { - return node.ipMode == IPv6 || node.ipMode == Dual -} - -// IsIPv6 returns true if proxy only supports IPv6 addresses. -func (node *Proxy) IsIPv6() bool { - return node.ipMode == IPv6 -} - -// ParseMetadata parses the opaque Metadata from an Envoy Node into string key-value pairs. -// Any non-string values are ignored. -func ParseMetadata(metadata *structpb.Struct) (*NodeMetadata, error) { - if metadata == nil { - return &NodeMetadata{}, nil - } - - b, err := protomarshal.MarshalProtoNames(metadata) - if err != nil { - return nil, fmt.Errorf("failed to read node metadata %v: %v", metadata, err) - } - meta := &BootstrapNodeMetadata{} - if err := json.Unmarshal(b, meta); err != nil { - return nil, fmt.Errorf("failed to unmarshal node metadata (%v): %v", string(b), err) - } - return &meta.NodeMetadata, nil -} - -// ParseServiceNodeWithMetadata parse the Envoy Node from the string generated by ServiceNode -// function and the metadata. -func ParseServiceNodeWithMetadata(nodeID string, metadata *NodeMetadata) (*Proxy, error) { - parts := strings.Split(nodeID, serviceNodeSeparator) - out := &Proxy{ - Metadata: metadata, - } - - if len(parts) != 4 { - return out, fmt.Errorf("missing parts in the service node %q", nodeID) - } - - if !IsApplicationNodeType(NodeType(parts[0])) { - return out, fmt.Errorf("invalid node type (valid types: sidecar, router in the service node %q", nodeID) - } - out.Type = NodeType(parts[0]) - - // Get all IP Addresses from Metadata - if hasValidIPAddresses(metadata.InstanceIPs) { - out.IPAddresses = metadata.InstanceIPs - } else if isValidIPAddress(parts[1]) { - // Fall back, use IP from node id, it's only for backward-compatibility, IP should come from metadata - out.IPAddresses = append(out.IPAddresses, parts[1]) - } - - // Does query from ingress or router have to carry valid IP address? - if len(out.IPAddresses) == 0 { - return out, fmt.Errorf("no valid IP address in the service node id or metadata") - } - - out.ID = parts[2] - out.DNSDomain = parts[3] - if len(metadata.IstioVersion) == 0 { - log.Warnf("Istio Version is not found in metadata for %v, which may have undesirable side effects", out.ID) - } - out.IstioVersion = ParseIstioVersion(metadata.IstioVersion) - return out, nil -} - -// ParseIstioVersion parses a version string and returns IstioVersion struct -func ParseIstioVersion(ver string) *IstioVersion { - // strip the release- prefix if any and extract the version string - ver = istioVersionRegexp.FindString(strings.TrimPrefix(ver, "release-")) - - if ver == "" { - // return very large values assuming latest version - return MaxIstioVersion - } - - parts := strings.Split(ver, ".") - // we are guaranteed to have atleast major and minor based on the regex - major, _ := strconv.Atoi(parts[0]) - minor, _ := strconv.Atoi(parts[1]) - // Assume very large patch release if not set - patch := 65535 - if len(parts) > 2 { - patch, _ = strconv.Atoi(parts[2]) - } - return &IstioVersion{Major: major, Minor: minor, Patch: patch} -} - -// GetOrDefault returns either the value, or the default if the value is empty. Useful when retrieving node metadata fields. -func GetOrDefault(s string, def string) string { - if len(s) > 0 { - return s - } - return def -} - -// GetProxyConfigNamespace extracts the namespace associated with the proxy -// from the proxy metadata or the proxy ID -func GetProxyConfigNamespace(proxy *Proxy) string { - if proxy == nil { - return "" - } - - // First look for ISTIO_META_CONFIG_NAMESPACE - // All newer proxies (from Istio 1.1 onwards) are supposed to supply this - if len(proxy.Metadata.Namespace) > 0 { - return proxy.Metadata.Namespace - } - - // if not found, for backward compatibility, extract the namespace from - // the proxy domain. this is a k8s specific hack and should be enabled - parts := strings.Split(proxy.DNSDomain, ".") - if len(parts) > 1 { // k8s will have namespace. - return parts[0] - } - - return "" -} - -const ( - serviceNodeSeparator = "~" -) - -// ParsePort extracts port number from a valid proxy address -func ParsePort(addr string) int { - _, sPort, err := net.SplitHostPort(addr) - if sPort == "" { - return 0 - } - if err != nil { - log.Warn(err) - } - port, pErr := strconv.Atoi(sPort) - if pErr != nil { - log.Warn(pErr) - } - - return port -} - -// hasValidIPAddresses returns true if the input ips are all valid, otherwise returns false. -func hasValidIPAddresses(ipAddresses []string) bool { - if len(ipAddresses) == 0 { - return false - } - for _, ipAddress := range ipAddresses { - if !isValidIPAddress(ipAddress) { - return false - } - } - return true -} - -// Tell whether the given IP address is valid or not -func isValidIPAddress(ip string) bool { - return net.ParseIP(ip) != nil -} - -// TrafficInterceptionMode indicates how traffic to/from the workload is captured and -// sent to Envoy. This should not be confused with the CaptureMode in the API that indicates -// how the user wants traffic to be intercepted for the listener. TrafficInterceptionMode is -// always derived from the Proxy metadata -type TrafficInterceptionMode string - -const ( - // InterceptionNone indicates that the workload is not using IPtables for traffic interception - InterceptionNone TrafficInterceptionMode = "NONE" - - // InterceptionTproxy implies traffic intercepted by IPtables with TPROXY mode - InterceptionTproxy TrafficInterceptionMode = "TPROXY" - - // InterceptionRedirect implies traffic intercepted by IPtables with REDIRECT mode - // This is our default mode - InterceptionRedirect TrafficInterceptionMode = "REDIRECT" -) - -// GetInterceptionMode extracts the interception mode associated with the proxy -// from the proxy metadata -func (node *Proxy) GetInterceptionMode() TrafficInterceptionMode { - if node == nil { - return InterceptionRedirect - } - - switch node.Metadata.InterceptionMode { - case "TPROXY": - return InterceptionTproxy - case "REDIRECT": - return InterceptionRedirect - case "NONE": - return InterceptionNone - } - - return InterceptionRedirect -} - -// IsUnprivileged returns true if the proxy has explicitly indicated that it is -// unprivileged, i.e. it cannot bind to the privileged ports 1-1023. -func (node *Proxy) IsUnprivileged() bool { - if node == nil || node.Metadata == nil { - return false - } - // expect explicit "true" value - unprivileged, _ := strconv.ParseBool(node.Metadata.UnprivilegedPod) - return unprivileged -} - -// CanBindToPort returns true if the proxy can bind to a given port. -func (node *Proxy) CanBindToPort(bindTo bool, port uint32) bool { - if bindTo && IsPrivilegedPort(port) && node.IsUnprivileged() { - return false - } - return true -} - -// IsPrivilegedPort returns true if a given port is in the range 1-1023. -func IsPrivilegedPort(port uint32) bool { - // check for 0 is important because: - // 1) technically, 0 is not a privileged port; any process can ask to bind to 0 - // 2) this function will be receiving 0 on input in the case of UDS listeners - return 0 < port && port < 1024 -} - -func (node *Proxy) IsVM() bool { - // TODO use node metadata to indicate that this is a VM intstead of the TestVMLabel - return node.Metadata != nil && node.Metadata.Labels[constants.TestVMLabel] != "" -} - -func (node *Proxy) IsProxylessGrpc() bool { - return node.Metadata != nil && node.Metadata.Generator == "grpc" -} - -type GatewayController interface { - ConfigStoreController - // Recompute updates the internal state of the gateway controller for a given input. This should be - // called before any List/Get calls if the state has changed - Recompute(GatewayContext) error - // SecretAllowed determines if a SDS credential is accessible to a given namespace. - // For example, for resourceName of `kubernetes-gateway://ns-name/secret-name` and namespace of `ingress-ns`, - // this would return true only if there was a policy allowing `ingress-ns` to access Secrets in the `ns-name` namespace. - SecretAllowed(resourceName string, namespace string) bool -} - -// OutboundListenerClass is a helper to turn a NodeType for outbound to a ListenerClass. -func OutboundListenerClass(t NodeType) istionetworking.ListenerClass { - if t == Router { - return istionetworking.ListenerClassGateway - } - return istionetworking.ListenerClassSidecarOutbound -} diff --git a/pilot/pkg/model/context_test.go b/pilot/pkg/model/context_test.go deleted file mode 100644 index 6f102cd6d..000000000 --- a/pilot/pkg/model/context_test.go +++ /dev/null @@ -1,656 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model_test - -import ( - "encoding/json" - "reflect" - "testing" - "time" -) - -import ( - "google.golang.org/protobuf/types/known/durationpb" - structpb "google.golang.org/protobuf/types/known/structpb" - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/mock" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -func TestNodeMetadata(t *testing.T) { - tests := []struct { - name string - in model.BootstrapNodeMetadata - out string - inOut model.BootstrapNodeMetadata - }{ - { - "empty", - model.BootstrapNodeMetadata{}, - "{}", - model.BootstrapNodeMetadata{NodeMetadata: model.NodeMetadata{Raw: map[string]interface{}{}}}, - }, - { - "csvlists", - model.BootstrapNodeMetadata{NodeMetadata: model.NodeMetadata{InstanceIPs: []string{"abc", "1.2.3.4"}}}, - `{"INSTANCE_IPS":"abc,1.2.3.4"}`, - model.BootstrapNodeMetadata{ - NodeMetadata: model.NodeMetadata{ - InstanceIPs: []string{"abc", "1.2.3.4"}, - Raw: map[string]interface{}{ - "INSTANCE_IPS": "abc,1.2.3.4", - }, - }, - }, - }, - { - "labels", - model.BootstrapNodeMetadata{NodeMetadata: model.NodeMetadata{Labels: map[string]string{"foo": "bar"}}}, - `{"LABELS":{"foo":"bar"}}`, - model.BootstrapNodeMetadata{ - NodeMetadata: model.NodeMetadata{ - Labels: map[string]string{"foo": "bar"}, - Raw: map[string]interface{}{ - "LABELS": map[string]interface{}{ - "foo": "bar", - }, - }, - }, - }, - }, - { - "proxy config", - model.BootstrapNodeMetadata{ - NodeMetadata: model.NodeMetadata{ - ProxyConfig: (*model.NodeMetaProxyConfig)(&meshconfig.ProxyConfig{ - ConfigPath: "foo", - DrainDuration: durationpb.New(time.Second * 5), - ControlPlaneAuthPolicy: meshconfig.AuthenticationPolicy_MUTUAL_TLS, - EnvoyAccessLogService: &meshconfig.RemoteService{ - Address: "address", - TlsSettings: &v1alpha3.ClientTLSSettings{ - SubjectAltNames: []string{"san"}, - }, - }, - }), - }, - }, - // nolint: lll - `{"PROXY_CONFIG":{"configPath":"foo","drainDuration":"5s","controlPlaneAuthPolicy":"MUTUAL_TLS","envoyAccessLogService":{"address":"address","tlsSettings":{"subjectAltNames":["san"]}}}}`, - model.BootstrapNodeMetadata{ - NodeMetadata: model.NodeMetadata{ - ProxyConfig: (*model.NodeMetaProxyConfig)(&meshconfig.ProxyConfig{ - ConfigPath: "foo", - DrainDuration: durationpb.New(time.Second * 5), - ControlPlaneAuthPolicy: meshconfig.AuthenticationPolicy_MUTUAL_TLS, - EnvoyAccessLogService: &meshconfig.RemoteService{ - Address: "address", - TlsSettings: &v1alpha3.ClientTLSSettings{ - SubjectAltNames: []string{"san"}, - }, - }, - }), - Raw: map[string]interface{}{ - "PROXY_CONFIG": map[string]interface{}{ - "drainDuration": "5s", - "configPath": "foo", - "controlPlaneAuthPolicy": "MUTUAL_TLS", - "envoyAccessLogService": map[string]interface{}{ - "address": "address", - "tlsSettings": map[string]interface{}{ - "subjectAltNames": []interface{}{"san"}, - }, - }, - }, - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - j, err := json.Marshal(tt.in) - if err != nil { - t.Fatalf("failed to marshal: %v", err) - } - if string(j) != tt.out { - t.Errorf("Got json '%s', expected '%s'", string(j), tt.out) - } - var meta model.BootstrapNodeMetadata - if err := json.Unmarshal(j, &meta); err != nil { - t.Fatalf("failed to unmarshal: %v", err) - } - - assert.Equal(t, (*meshconfig.ProxyConfig)(meta.NodeMetadata.ProxyConfig), (*meshconfig.ProxyConfig)(tt.inOut.NodeMetadata.ProxyConfig)) - // cmp cannot handle the type-alias in the metadata, so check them separately. - meta.NodeMetadata.ProxyConfig = nil - tt.inOut.NodeMetadata.ProxyConfig = nil - assert.Equal(t, meta, tt.inOut) - }) - } -} - -func TestStringList(t *testing.T) { - cases := []struct { - in string - expect model.StringList - noRoundTrip bool - }{ - {in: `"a,b,c"`, expect: []string{"a", "b", "c"}}, - {in: `"a"`, expect: []string{"a"}}, - {in: `""`, expect: []string{}}, - {in: `"123,@#$#,abcdef"`, expect: []string{"123", "@#$#", "abcdef"}}, - {in: `1`, expect: []string{}, noRoundTrip: true}, - } - for _, tt := range cases { - t.Run(tt.in, func(t *testing.T) { - var out model.StringList - if err := json.Unmarshal([]byte(tt.in), &out); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(out, tt.expect) { - t.Fatalf("Expected %v, got %v", tt.expect, out) - } - b, err := json.Marshal(out) - if err != nil { - t.Fatal(err) - } - if tt.noRoundTrip { - return - } - if !reflect.DeepEqual(string(b), tt.in) { - t.Fatalf("Expected %v, got %v", tt.in, string(b)) - } - }) - } -} - -func TestPodPortList(t *testing.T) { - cases := []struct { - name string - in string - expect model.PodPortList - expUnmarshalErr string - }{ - {"no port", `"[]"`, model.PodPortList{}, ""}, - {"one port", `"[{\"name\":\"foo\",\"containerPort\":9080,\"protocol\":\"TCP\"}]"`, model.PodPortList{{"foo", 9080, "TCP"}}, ""}, - { - "two ports", - `"[{\"name\":\"foo\",\"containerPort\":9080,\"protocol\":\"TCP\"},{\"containerPort\":8888,\"protocol\":\"TCP\"}]"`, - model.PodPortList{{"foo", 9080, "TCP"}, {ContainerPort: 8888, Protocol: "TCP"}}, - "", - }, - {"invalid syntax", `[]`, nil, "invalid syntax"}, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - var out model.PodPortList - if err := json.Unmarshal([]byte(tt.in), &out); err != nil { - if tt.expUnmarshalErr == "" { - t.Fatal(err) - } - if out != nil { - t.Fatalf("%s: Expected null unmarshal output but obtained a non-null one.", tt.name) - } - if err.Error() != tt.expUnmarshalErr { - t.Fatalf("%s: Expected error: %s but got error: %s.", tt.name, tt.expUnmarshalErr, err.Error()) - } - return - } - if !reflect.DeepEqual(out, tt.expect) { - t.Fatalf("%s: Expected %v, got %v", tt.name, tt.expect, out) - } - b, err := json.Marshal(out) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(string(b), tt.in) { - t.Fatalf("%s: Expected %v, got %v", tt.name, tt.in, string(b)) - } - }) - } -} - -func TestStringBool(t *testing.T) { - cases := []struct { - name string - in string - expect string - }{ - {"1", `"1"`, `"true"`}, - {"0", `"0"`, `"false"`}, - {"false", `"false"`, `"false"`}, - {"true", `"true"`, `"true"`}, - {"invalid input", `"foo"`, ``}, - {"no quotes", `true`, ``}, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - var out model.StringBool - if err := json.Unmarshal([]byte(tt.in), &out); err != nil { - if tt.expect == "" { - return - } - t.Fatal(err) - } - b, err := json.Marshal(out) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(string(b), tt.expect) { - t.Fatalf("Expected %v, got %v", tt.expect, string(b)) - } - }) - } -} - -func TestServiceNode(t *testing.T) { - cases := []struct { - in *model.Proxy - out string - }{ - { - in: &mock.HelloProxyV0, - out: "sidecar~10.1.1.0~v0.default~default.svc.cluster.local", - }, - { - in: &model.Proxy{ - Type: model.Router, - ID: "random", - IPAddresses: []string{"10.3.3.3"}, - DNSDomain: "local", - Metadata: &model.NodeMetadata{}, - IstioVersion: model.MaxIstioVersion, - }, - out: "router~10.3.3.3~random~local", - }, - { - in: &model.Proxy{ - Type: model.SidecarProxy, - ID: "random", - IPAddresses: []string{"10.3.3.3", "10.4.4.4", "10.5.5.5", "10.6.6.6"}, - DNSDomain: "local", - Metadata: &model.NodeMetadata{ - InstanceIPs: []string{"10.3.3.3", "10.4.4.4", "10.5.5.5", "10.6.6.6"}, - }, - IstioVersion: model.MaxIstioVersion, - }, - out: "sidecar~10.3.3.3~random~local", - }, - } - - for _, node := range cases { - out := node.in.ServiceNode() - if out != node.out { - t.Errorf("%#v.ServiceNode() => Got %s, want %s", node.in, out, node.out) - } - in, err := model.ParseServiceNodeWithMetadata(node.out, node.in.Metadata) - if err != nil { - t.Errorf("ParseServiceNode(%q) => Got error %v", node.out, err) - } - if !reflect.DeepEqual(in, node.in) { - t.Errorf("ParseServiceNode(%q) => Got %#v, want %#v", node.out, in, node.in) - } - } -} - -func TestParseMetadata(t *testing.T) { - cases := []struct { - name string - metadata map[string]interface{} - out *model.Proxy - }{ - { - name: "Basic Case", - out: &model.Proxy{ - Type: "sidecar", IPAddresses: []string{"1.1.1.1"}, DNSDomain: "domain", ID: "id", IstioVersion: model.MaxIstioVersion, - Metadata: &model.NodeMetadata{Raw: map[string]interface{}{}}, - }, - }, - { - name: "Capture Arbitrary Metadata", - metadata: map[string]interface{}{"foo": "bar"}, - out: &model.Proxy{ - Type: "sidecar", IPAddresses: []string{"1.1.1.1"}, DNSDomain: "domain", ID: "id", IstioVersion: model.MaxIstioVersion, - Metadata: &model.NodeMetadata{ - Raw: map[string]interface{}{ - "foo": "bar", - }, - }, - }, - }, - { - name: "Capture Labels", - metadata: map[string]interface{}{ - "LABELS": map[string]string{ - "foo": "bar", - }, - }, - out: &model.Proxy{ - Type: "sidecar", IPAddresses: []string{"1.1.1.1"}, DNSDomain: "domain", ID: "id", IstioVersion: model.MaxIstioVersion, - Metadata: &model.NodeMetadata{ - Raw: map[string]interface{}{ - "LABELS": map[string]interface{}{"foo": "bar"}, - }, - Labels: map[string]string{"foo": "bar"}, - }, - }, - }, - { - name: "Capture Pod Ports", - metadata: map[string]interface{}{ - "POD_PORTS": `[{"name":"http","containerPort":8080,"protocol":"TCP"},{"name":"grpc","containerPort":8079,"protocol":"TCP"}]`, - }, - out: &model.Proxy{ - Type: "sidecar", IPAddresses: []string{"1.1.1.1"}, DNSDomain: "domain", ID: "id", IstioVersion: model.MaxIstioVersion, - Metadata: &model.NodeMetadata{ - Raw: map[string]interface{}{ - "POD_PORTS": `[{"name":"http","containerPort":8080,"protocol":"TCP"},{"name":"grpc","containerPort":8079,"protocol":"TCP"}]`, - }, - PodPorts: []model.PodPort{ - {"http", 8080, "TCP"}, - {"grpc", 8079, "TCP"}, - }, - }, - }, - }, - } - - nodeID := "sidecar~1.1.1.1~id~domain" - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - meta, err := mapToStruct(tt.metadata) - if err != nil { - t.Fatalf("failed to setup metadata: %v", err) - } - parsed, err := model.ParseMetadata(meta) - if err != nil { - t.Fatalf("failed to parse service node: %v", err) - } - node, err := model.ParseServiceNodeWithMetadata(nodeID, parsed) - if err != nil { - t.Fatalf("failed to parse service node: %v", err) - } - if !reflect.DeepEqual(*tt.out.Metadata, *node.Metadata) { - t.Errorf("Got \n%v, want \n%v", *node.Metadata, *tt.out.Metadata) - } - }) - } -} - -func mapToStruct(msg map[string]interface{}) (*structpb.Struct, error) { - if msg == nil { - return &structpb.Struct{}, nil - } - b, err := json.Marshal(msg) - if err != nil { - return nil, err - } - - pbs := &structpb.Struct{} - if err := protomarshal.Unmarshal(b, pbs); err != nil { - return nil, err - } - - return pbs, nil -} - -func TestParsePort(t *testing.T) { - if port := model.ParsePort("localhost:3000"); port != 3000 { - t.Errorf("ParsePort(localhost:3000) => Got %d, want 3000", port) - } - if port := model.ParsePort("localhost"); port != 0 { - t.Errorf("ParsePort(localhost) => Got %d, want 0", port) - } - if port := model.ParsePort("127.0.0.1:3000"); port != 3000 { - t.Errorf("ParsePort(127.0.0.1:3000) => Got %d, want 3000", port) - } - if port := model.ParsePort("127.0.0.1"); port != 0 { - t.Errorf("ParsePort(127.0.0.1) => Got %d, want 0", port) - } - if port := model.ParsePort("[::1]:3000"); port != 3000 { - t.Errorf("ParsePort([::1]:3000) => Got %d, want 3000", port) - } - if port := model.ParsePort("::1"); port != 0 { - t.Errorf("ParsePort(::1) => Got %d, want 0", port) - } - if port := model.ParsePort("[2001:4860:0:2001::68]:3000"); port != 3000 { - t.Errorf("ParsePort([2001:4860:0:2001::68]:3000) => Got %d, want 3000", port) - } - if port := model.ParsePort("2001:4860:0:2001::68"); port != 0 { - t.Errorf("ParsePort(2001:4860:0:2001::68) => Got %d, want 0", port) - } -} - -func TestGetOrDefault(t *testing.T) { - assert.Equal(t, "a", model.GetOrDefault("a", "b")) - assert.Equal(t, "b", model.GetOrDefault("", "b")) -} - -func TestProxyVersion_Compare(t *testing.T) { - type fields struct { - Major int - Minor int - Patch int - } - type args struct { - inv *model.IstioVersion - } - tests := []struct { - name string - fields fields - args args - want int - }{ - { - name: "greater major", - fields: fields{Major: 2, Minor: 1, Patch: 1}, - args: args{&model.IstioVersion{Major: 1, Minor: 2, Patch: 1}}, - want: 1, - }, - { - name: "equal at minor", - fields: fields{Major: 2, Minor: 1, Patch: 1}, - args: args{&model.IstioVersion{Major: 2, Minor: 1, Patch: -1}}, - want: 0, - }, - { - name: "less at patch", - fields: fields{Major: 2, Minor: 1, Patch: 0}, - args: args{&model.IstioVersion{Major: 2, Minor: 1, Patch: 1}}, - want: -1, - }, - { - name: "ignore minor", - fields: fields{Major: 2, Minor: 1, Patch: 11}, - args: args{&model.IstioVersion{Major: 2, Minor: -1, Patch: 1}}, - want: 0, - }, - { - name: "ignore patch", - fields: fields{Major: 2, Minor: 1, Patch: 11}, - args: args{&model.IstioVersion{Major: 2, Minor: 1, Patch: -1}}, - want: 0, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - pversion := &model.IstioVersion{ - Major: tt.fields.Major, - Minor: tt.fields.Minor, - Patch: tt.fields.Patch, - } - if got := pversion.Compare(tt.args.inv); got != tt.want { - t.Errorf("ProxyVersion.Compare() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_parseIstioVersion(t *testing.T) { - type args struct { - ver string - } - tests := []struct { - name string - args args - want *model.IstioVersion - }{ - { - name: "major.minor.patch", - args: args{ver: "1.2.3"}, - want: &model.IstioVersion{Major: 1, Minor: 2, Patch: 3}, - }, - { - name: "major.minor", - args: args{ver: "1.2"}, - want: &model.IstioVersion{Major: 1, Minor: 2, Patch: 65535}, - }, - { - name: "dev", - args: args{ver: "1.5-alpha.f70faea2aa817eeec0b08f6cc3b5078e5dcf3beb"}, - want: &model.IstioVersion{Major: 1, Minor: 5, Patch: 65535}, - }, - { - name: "release-major.minor-date", - args: args{ver: "release-1.2-123214234"}, - want: &model.IstioVersion{Major: 1, Minor: 2, Patch: 65535}, - }, - { - name: "master-date", - args: args{ver: "master-123214234"}, - want: model.MaxIstioVersion, - }, - { - name: "master-sha", - args: args{ver: "master-0b94e017f5b6c7c4598a4da42ea9d45eeb099e5f"}, - want: model.MaxIstioVersion, - }, - { - name: "junk-major.minor.patch", - args: args{ver: "junk-1.2.3214234"}, - want: model.MaxIstioVersion, - }, - { - name: "junk-garbage", - args: args{ver: "junk-garbage"}, - want: model.MaxIstioVersion, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := model.ParseIstioVersion(tt.args.ver); !reflect.DeepEqual(got, tt.want) { - t.Errorf("parseIstioVersion() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestSetServiceInstances(t *testing.T) { - tnow := time.Now() - instances := []*model.ServiceInstance{ - { - Service: &model.Service{ - CreationTime: tnow.Add(1 * time.Second), - Hostname: host.Name("test1.com"), - }, - }, - { - Service: &model.Service{ - CreationTime: tnow, - Hostname: host.Name("test3.com"), - }, - }, - { - Service: &model.Service{ - CreationTime: tnow, - Hostname: host.Name("test2.com"), - }, - }, - } - - serviceDiscovery := memory.NewServiceDiscovery() - serviceDiscovery.WantGetProxyServiceInstances = instances - - env := &model.Environment{ - ServiceDiscovery: serviceDiscovery, - } - - proxy := &model.Proxy{} - proxy.SetServiceInstances(env) - - assert.Equal(t, len(proxy.ServiceInstances), 3) - assert.Equal(t, proxy.ServiceInstances[0].Service.Hostname, host.Name("test2.com")) - assert.Equal(t, proxy.ServiceInstances[1].Service.Hostname, host.Name("test3.com")) - assert.Equal(t, proxy.ServiceInstances[2].Service.Hostname, host.Name("test1.com")) -} - -func TestGlobalUnicastIP(t *testing.T) { - cases := []struct { - name string - in []string - expect string - }{ - { - name: "single IPv4 (k8s)", - in: []string{"10.0.4.16"}, - expect: "10.0.4.16", - }, - { - name: "single IPv6 (k8s)", - in: []string{"fc00:f853:ccd:e793::1"}, - expect: "fc00:f853:ccd:e793::1", - }, - { - name: "multi IPv4 [1st] (VM)", - in: []string{"10.128.0.51", "fc00:f853:ccd:e793::1", "172.17.0.1", "fe80::42:35ff:fec1:7436", "fe80::345d:33ff:fe54:5c8e"}, - expect: "10.128.0.51", - }, - { - name: "multi IPv6 [1st] (VM)", - in: []string{"fc00:f853:ccd:e793::1", "172.17.0.1", "fe80::42:35ff:fec1:7436", "10.128.0.51", "fe80::345d:33ff:fe54:5c8e"}, - expect: "fc00:f853:ccd:e793::1", - }, - { - name: "multi IPv4 [2nd] (VM)", - in: []string{"127.0.0.1", "10.128.0.51", "fc00:f853:ccd:e793::1", "172.17.0.1", "fe80::42:35ff:fec1:7436", "fe80::345d:33ff:fe54:5c8e"}, - expect: "10.128.0.51", - }, - { - name: "multi IPv6 [2nd] (VM)", - in: []string{"fe80::42:35ff:fec1:7436", "fc00:f853:ccd:e793::1", "172.17.0.1", "10.128.0.51", "fe80::345d:33ff:fe54:5c8e"}, - expect: "fc00:f853:ccd:e793::1", - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - var node model.Proxy - node.IPAddresses = tt.in - node.DiscoverIPMode() - if got := node.GlobalUnicastIP; got != tt.expect { - t.Errorf("GlobalUnicastIP = %v, want %v", got, tt.expect) - } - }) - } -} diff --git a/pilot/pkg/model/controller.go b/pilot/pkg/model/controller.go deleted file mode 100644 index 2f8de7316..000000000 --- a/pilot/pkg/model/controller.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "sync" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/cluster" -) - -// Controller defines an event controller loop. Proxy agent registers itself -// with the controller loop and receives notifications on changes to the -// service topology or changes to the configuration artifacts. -// -// The controller guarantees the following consistency requirement: registry -// view in the controller is as AT LEAST as fresh as the moment notification -// arrives, but MAY BE more fresh (e.g. "delete" cancels an "add" event). For -// example, an event for a service creation will see a service registry without -// the service if the event is immediately followed by the service deletion -// event. -// -// Handlers execute on the single worker queue in the order they are appended. -// Handlers receive the notification event and the associated object. Note -// that all handlers must be appended before starting the controller. -type Controller interface { - // Note: AppendXXXHandler is used to register high level handlers. - // For per cluster handlers, they should be registered by the `AppendXXXHandlerForCluster` interface. - - // AppendServiceHandler notifies about changes to the service catalog. - AppendServiceHandler(f func(*Service, Event)) - - // AppendWorkloadHandler notifies about changes to workloads. This differs from InstanceHandler, - // which deals with service instances (the result of a merge of Service and Workload) - AppendWorkloadHandler(f func(*WorkloadInstance, Event)) - - // Run until a signal is received - Run(stop <-chan struct{}) - - // HasSynced returns true after initial cache synchronization is complete - HasSynced() bool -} - -// AggregateController is a wrapper of Controller, it supports registering handlers of a specific cluster。 -type AggregateController interface { - Controller - // AppendServiceHandlerForCluster is similar to Controller.AppendServiceHandler, - // but it is used to store the handler from a specific cluster. - AppendServiceHandlerForCluster(clusterID cluster.ID, f func(*Service, Event)) - // AppendWorkloadHandlerForCluster is similar to Controller.AppendWorkloadHandler, - // but it is used to store the handler from a specific cluster. - AppendWorkloadHandlerForCluster(clusterID cluster.ID, f func(*WorkloadInstance, Event)) - UnRegisterHandlersForCluster(clusterID cluster.ID) -} - -// ControllerHandlers is a utility to help Controller implementations manage their lists of handlers. -type ControllerHandlers struct { - mutex sync.RWMutex - serviceHandlers []func(*Service, Event) - workloadHandlers []func(*WorkloadInstance, Event) -} - -func (c *ControllerHandlers) AppendServiceHandler(f func(*Service, Event)) { - // Copy on write. - c.mutex.Lock() - handlers := make([]func(*Service, Event), 0, len(c.serviceHandlers)+1) - handlers = append(handlers, c.serviceHandlers...) - handlers = append(handlers, f) - c.serviceHandlers = handlers - c.mutex.Unlock() -} - -func (c *ControllerHandlers) AppendWorkloadHandler(f func(*WorkloadInstance, Event)) { - // Copy on write. - c.mutex.Lock() - handlers := make([]func(*WorkloadInstance, Event), 0, len(c.workloadHandlers)+1) - handlers = append(handlers, c.workloadHandlers...) - handlers = append(handlers, f) - c.workloadHandlers = handlers - c.mutex.Unlock() -} - -func (c *ControllerHandlers) GetServiceHandlers() []func(*Service, Event) { - c.mutex.RLock() - defer c.mutex.RUnlock() - // Return a shallow copy of the array - return c.serviceHandlers -} - -func (c *ControllerHandlers) GetWorkloadHandlers() []func(*WorkloadInstance, Event) { - c.mutex.RLock() - defer c.mutex.RUnlock() - // Return a shallow copy of the array - return c.workloadHandlers -} - -func (c *ControllerHandlers) NotifyServiceHandlers(svc *Service, event Event) { - for _, f := range c.GetServiceHandlers() { - f(svc, event) - } -} - -func (c *ControllerHandlers) NotifyWorkloadHandlers(w *WorkloadInstance, event Event) { - for _, f := range c.GetWorkloadHandlers() { - f(w, event) - } -} - -// Event represents a registry update event -type Event int - -const ( - // EventAdd is sent when an object is added - EventAdd Event = iota - - // EventUpdate is sent when an object is modified - // Captures the modified object - EventUpdate - - // EventDelete is sent when an object is deleted - // Captures the object at the last known state - EventDelete -) - -func (event Event) String() string { - out := "unknown" - switch event { - case EventAdd: - out = "add" - case EventUpdate: - out = "update" - case EventDelete: - out = "delete" - } - return out -} diff --git a/pilot/pkg/model/conversion_test.go b/pilot/pkg/model/conversion_test.go deleted file mode 100644 index 4d84181b9..000000000 --- a/pilot/pkg/model/conversion_test.go +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model_test - -import ( - "fmt" - "reflect" - "strings" - "testing" -) - -import ( - "github.com/davecgh/go-spew/spew" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/crd" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -func TestApplyJSON(t *testing.T) { - cases := []struct { - in string - want *meshconfig.MeshConfig - wantErr bool - }{ - { - in: `{"enableTracing": true}`, - want: &meshconfig.MeshConfig{EnableTracing: true}, - }, - { - in: `{"enableTracing": true, "unknownField": "unknownValue"}`, - want: &meshconfig.MeshConfig{EnableTracing: true}, - }, - } - for i, c := range cases { - t.Run(fmt.Sprintf("[%v]", i), func(tt *testing.T) { - var got meshconfig.MeshConfig - err := protomarshal.ApplyJSON(c.in, &got) - if err != nil { - if !c.wantErr { - tt.Fatalf("got unexpected error: %v", err) - } - } else { - if c.wantErr { - tt.Fatal("unexpected success, expected error") - } - assert.Equal(t, &got, c.want) - } - }) - } -} - -func TestProtoSchemaConversions(t *testing.T) { - destinationRuleSchema := collections.IstioNetworkingV1Alpha3Destinationrules - - msg := &networking.DestinationRule{ - Host: "something.svc.local", - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{}, - }, - }, - Subsets: []*networking.Subset{ - { - Name: "foo", - Labels: map[string]string{ - "test": "label", - }, - }, - }, - } - - wantJSON := ` - { - "host":"something.svc.local", - "trafficPolicy": { - "loadBalancer":{"simple":"UNSPECIFIED"} - }, - "subsets": [ - {"name":"foo","labels":{"test":"label"}} - ] - }` - - wantYAML := `host: something.svc.local -subsets: -- labels: - test: label - name: foo -trafficPolicy: - loadBalancer: - simple: UNSPECIFIED -` - - wantJSONMap := map[string]interface{}{ - "host": "something.svc.local", - "trafficPolicy": map[string]interface{}{ - "loadBalancer": map[string]interface{}{ - "simple": "UNSPECIFIED", - }, - }, - "subsets": []interface{}{ - map[string]interface{}{ - "name": "foo", - "labels": map[string]interface{}{ - "test": "label", - }, - }, - }, - } - - badSchema := schemaFor("bad", "bad-name") - if _, err := crd.FromYAML(badSchema, wantYAML); err == nil { - t.Errorf("FromYAML should have failed using Schema with bad MessageName") - } - - gotJSON, err := protomarshal.ToJSON(msg) - if err != nil { - t.Errorf("ToJSON failed: %v", err) - } - if gotJSON != strings.Join(strings.Fields(wantJSON), "") { - t.Errorf("ToJSON failed: got %s, want %s", gotJSON, wantJSON) - } - - if _, err = protomarshal.ToJSON(nil); err == nil { - t.Error("should produce an error") - } - - gotFromJSON, err := crd.FromJSON(destinationRuleSchema, wantJSON) - if err != nil { - t.Errorf("FromJSON failed: %v", err) - } - if !reflect.DeepEqual(gotFromJSON, msg) { - t.Errorf("FromYAML failed: got %+v want %+v", spew.Sdump(gotFromJSON), spew.Sdump(msg)) - } - - gotYAML, err := protomarshal.ToYAML(msg) - if err != nil { - t.Errorf("ToYAML failed: %v", err) - } - if !reflect.DeepEqual(gotYAML, wantYAML) { - t.Errorf("ToYAML failed: got %+v want %+v", spew.Sdump(gotYAML), spew.Sdump(wantYAML)) - } - - if _, err = protomarshal.ToYAML(nil); err == nil { - t.Error("should produce an error") - } - - gotFromYAML, err := crd.FromYAML(destinationRuleSchema, wantYAML) - if err != nil { - t.Errorf("FromYAML failed: %v", err) - } - if !reflect.DeepEqual(gotFromYAML, msg) { - t.Errorf("FromYAML failed: got %+v want %+v", spew.Sdump(gotFromYAML), spew.Sdump(msg)) - } - - if _, err = crd.FromYAML(destinationRuleSchema, ":"); err == nil { - t.Errorf("should produce an error") - } - - gotJSONMap, err := protomarshal.ToJSONMap(msg) - if err != nil { - t.Errorf("ToJSONMap failed: %v", err) - } - if !reflect.DeepEqual(gotJSONMap, wantJSONMap) { - t.Errorf("ToJSONMap failed: \ngot %vwant %v", spew.Sdump(gotJSONMap), spew.Sdump(wantJSONMap)) - } - - if _, err = protomarshal.ToJSONMap(nil); err == nil { - t.Error("should produce an error") - } - - gotFromJSONMap, err := crd.FromJSONMap(destinationRuleSchema, wantJSONMap) - if err != nil { - t.Errorf("FromJSONMap failed: %v", err) - } - if !reflect.DeepEqual(gotFromJSONMap, msg) { - t.Errorf("FromJSONMap failed: got %+v want %+v", spew.Sdump(gotFromJSONMap), spew.Sdump(msg)) - } - - if _, err = crd.FromJSONMap(destinationRuleSchema, 1); err == nil { - t.Error("should produce an error") - } - if _, err = crd.FromJSONMap(destinationRuleSchema, ":"); err == nil { - t.Errorf("should produce an error") - } -} diff --git a/pilot/pkg/model/credentials/resource.go b/pilot/pkg/model/credentials/resource.go deleted file mode 100644 index 1cf1f38d7..000000000 --- a/pilot/pkg/model/credentials/resource.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package credentials - -import ( - "fmt" - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/cluster" -) - -const ( - // KubernetesSecretType is the name of a SDS secret stored in Kubernetes. Secrets here take the form - // kubernetes://secret-name. They will be pulled from the same namespace and cluster as the requesting proxy lives in. - KubernetesSecretType = "kubernetes" - KubernetesSecretTypeURI = KubernetesSecretType + "://" - // KubernetesGatewaySecretType is the name of a SDS secret stored in Kubernetes, used by the gateway-api. Secrets here - // take the form kubernetes-gateway://namespace/name. They are pulled from the config cluster. - KubernetesGatewaySecretType = "kubernetes-gateway" - kubernetesGatewaySecretTypeURI = KubernetesGatewaySecretType + "://" - // BuiltinGatewaySecretType is the name of a SDS secret that uses the workloads own mTLS certificate - BuiltinGatewaySecretType = "builtin" - BuiltinGatewaySecretTypeURI = BuiltinGatewaySecretType + "://" -) - -// SecretResource defines a reference to a secret -type SecretResource struct { - // Type is the type of secret. One of KubernetesSecretType or KubernetesGatewaySecretType - Type string - // Name is the name of the secret - Name string - // Namespace is the namespace the secret resides in. For implicit namespace references (such as in KubernetesSecretType), - // this will be resolved to the appropriate namespace. As a result, this should never be empty. - Namespace string - // ResourceName is the original name of the resource - ResourceName string - // Cluster is the cluster the secret should be fetched from. - Cluster cluster.ID -} - -func (sr SecretResource) Key() string { - return "sds://" + sr.Type + "/" + sr.Name + "/" + sr.Namespace + "/" + string(sr.Cluster) -} - -func (sr SecretResource) KubernetesResourceName() string { - return fmt.Sprintf("%s://%s/%s", sr.Type, sr.Namespace, sr.Name) -} - -func ToKubernetesGatewayResource(namespace, name string) string { - if strings.HasPrefix(name, BuiltinGatewaySecretTypeURI) { - return BuiltinGatewaySecretTypeURI - } - return fmt.Sprintf("%s://%s/%s", KubernetesGatewaySecretType, namespace, name) -} - -// ToResourceName turns a `credentialName` into a resource name used for SDS -func ToResourceName(name string) string { - if strings.HasPrefix(name, BuiltinGatewaySecretTypeURI) { - return "default" - } - // If they explicitly defined the type, keep it - if strings.HasPrefix(name, KubernetesSecretTypeURI) || strings.HasPrefix(name, kubernetesGatewaySecretTypeURI) { - return name - } - // Otherwise, to kubernetes:// - return KubernetesSecretTypeURI + name -} - -// ParseResourceName parses a raw resourceName string. -func ParseResourceName(resourceName string, proxyNamespace string, proxyCluster cluster.ID, configCluster cluster.ID) (SecretResource, error) { - sep := "/" - if strings.HasPrefix(resourceName, KubernetesSecretTypeURI) { - // Valid formats: - // * kubernetes://secret-name - // * kubernetes://secret-namespace/secret-name - // If namespace is not set, we will fetch from the namespace of the proxy. The secret will be read from - // the cluster the proxy resides in. This mirrors the legacy behavior mounting a secret as a file - res := strings.TrimPrefix(resourceName, KubernetesSecretTypeURI) - split := strings.Split(res, sep) - namespace := proxyNamespace - name := split[0] - if len(split) > 1 { - namespace = split[0] - name = split[1] - } - return SecretResource{Type: KubernetesSecretType, Name: name, Namespace: namespace, ResourceName: resourceName, Cluster: proxyCluster}, nil - } else if strings.HasPrefix(resourceName, kubernetesGatewaySecretTypeURI) { - // Valid formats: - // * kubernetes-gateway://secret-namespace/secret-name - // Namespace is required. The secret is read from the config cluster; this is the primary difference from KubernetesSecretType. - res := strings.TrimPrefix(resourceName, kubernetesGatewaySecretTypeURI) - split := strings.Split(res, sep) - if len(split) <= 1 { - return SecretResource{}, fmt.Errorf("invalid resource name %q. Expected namespace and name", resourceName) - } - namespace := split[0] - name := split[1] - if len(namespace) == 0 { - return SecretResource{}, fmt.Errorf("invalid resource name %q. Expected namespace", resourceName) - } - if len(name) == 0 { - return SecretResource{}, fmt.Errorf("invalid resource name %q. Expected name", resourceName) - } - return SecretResource{Type: KubernetesGatewaySecretType, Name: name, Namespace: namespace, ResourceName: resourceName, Cluster: configCluster}, nil - } - return SecretResource{}, fmt.Errorf("unknown resource type: %v", resourceName) -} diff --git a/pilot/pkg/model/credentials/resource_test.go b/pilot/pkg/model/credentials/resource_test.go deleted file mode 100644 index fcb73f4ab..000000000 --- a/pilot/pkg/model/credentials/resource_test.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package credentials - -import ( - "testing" -) - -func TestParseResourceName(t *testing.T) { - cases := []struct { - name string - resource string - defaultNamespace string - expected SecretResource - err bool - }{ - { - name: "simple", - resource: "kubernetes://cert", - defaultNamespace: "default", - expected: SecretResource{ - Type: KubernetesSecretType, - Name: "cert", - Namespace: "default", - ResourceName: "kubernetes://cert", - Cluster: "cluster", - }, - }, - { - name: "with namespace", - resource: "kubernetes://namespace/cert", - defaultNamespace: "default", - expected: SecretResource{ - Type: KubernetesSecretType, - Name: "cert", - Namespace: "namespace", - ResourceName: "kubernetes://namespace/cert", - Cluster: "cluster", - }, - }, - { - name: "kubernetes-gateway", - resource: "kubernetes-gateway://namespace/cert", - defaultNamespace: "default", - expected: SecretResource{ - Type: KubernetesGatewaySecretType, - Name: "cert", - Namespace: "namespace", - ResourceName: "kubernetes-gateway://namespace/cert", - Cluster: "config", - }, - }, - { - name: "kubernetes-gateway without namespace", - resource: "kubernetes-gateway://cert", - defaultNamespace: "default", - err: true, - }, - { - name: "kubernetes-gateway with empty namespace", - resource: "kubernetes-gateway:///cert", - defaultNamespace: "default", - err: true, - }, - { - name: "kubernetes-gateway with empty name", - resource: "kubernetes-gateway://ns/", - defaultNamespace: "default", - err: true, - }, - { - name: "plain", - resource: "cert", - defaultNamespace: "default", - err: true, - }, - { - name: "non kubernetes", - resource: "vault://cert", - defaultNamespace: "default", - err: true, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - got, err := ParseResourceName(tt.resource, tt.defaultNamespace, "cluster", "config") - if tt.err != (err != nil) { - t.Fatalf("expected err=%v but got err=%v", tt.err, err) - } - if got != tt.expected { - t.Fatalf("want %+v, got %+v", tt.expected, got) - } - }) - } -} - -func TestToResourceName(t *testing.T) { - tests := []struct { - name string - want string - }{ - {"foo", "kubernetes://foo"}, - {"kubernetes://bar", "kubernetes://bar"}, - {"kubernetes-gateway://bar", "kubernetes-gateway://bar"}, - {"builtin://", "default"}, - {"builtin://extra", "default"}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := ToResourceName(tt.name); got != tt.want { - t.Fatalf("got %v, want %v", got, tt.want) - } - }) - } -} - -func TestToKubernetesGatewayResource(t *testing.T) { - tests := []struct { - name string - namespace string - want string - }{ - {"foo", "ns", "kubernetes-gateway://ns/foo"}, - {"builtin://", "anything", "builtin://"}, - {"builtin://extra", "anything", "builtin://"}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := ToKubernetesGatewayResource(tt.namespace, tt.name); got != tt.want { - t.Fatalf("got %v, want %v", got, tt.want) - } - }) - } -} diff --git a/pilot/pkg/model/destination_rule.go b/pilot/pkg/model/destination_rule.go deleted file mode 100644 index 013ff8b7a..000000000 --- a/pilot/pkg/model/destination_rule.go +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "fmt" -) - -import ( - "google.golang.org/protobuf/proto" - networking "istio.io/api/networking/v1alpha3" - "k8s.io/apimachinery/pkg/types" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/visibility" -) - -// This function merges one or more destination rules for a given host string -// into a single destination rule. Note that it does not perform inheritance style merging. -// IOW, given three dest rules (*.foo.com, *.foo.com, *.com), calling this function for -// each config will result in a final dest rule set (*.foo.com, and *.com). -// -// The following is the merge logic: -// 1. Unique subsets (based on subset name) are concatenated to the original rule's list of subsets -// 2. If the original rule did not have any top level traffic policy, traffic policies from the new rule will be -// used. -// 3. If the original rule did not have any exportTo, exportTo settings from the new rule will be used. -func (ps *PushContext) mergeDestinationRule(p *consolidatedDestRules, destRuleConfig config.Config, exportToMap map[visibility.Instance]bool) { - rule := destRuleConfig.Spec.(*networking.DestinationRule) - resolvedHost := ResolveShortnameToFQDN(rule.Host, destRuleConfig.Meta) - if mdrList, exists := p.destRules[resolvedHost]; exists { - // `addRuleToProcessedDestRules` determines if the incoming destination rule would become a new unique entry in the processedDestRules list. - addRuleToProcessedDestRules := true - for _, mdr := range mdrList { - existingRule := mdr.rule.Spec.(*networking.DestinationRule) - bothWithoutSelector := rule.GetWorkloadSelector() == nil && existingRule.GetWorkloadSelector() == nil - bothWithSelector := existingRule.GetWorkloadSelector() != nil && rule.GetWorkloadSelector() != nil - selectorsMatch := labels.Instance(existingRule.GetWorkloadSelector().GetMatchLabels()).Equals(rule.GetWorkloadSelector().GetMatchLabels()) - - if bothWithSelector && !selectorsMatch { - // If the new destination rule and the existing one has workload selectors associated with them, skip merging - // if the selectors do not match - continue - } - // If both the destination rules are without a workload selector or with matching workload selectors, simply merge them. - // If the incoming rule has a workload selector, it has to be merged with the existing rules with workload selector, and - // at the same time added as a unique entry in the processedDestRules. - if bothWithoutSelector || (rule.GetWorkloadSelector() != nil && selectorsMatch) { - addRuleToProcessedDestRules = false - } - - // Deep copy destination rule, to prevent mutate it later when merge with a new one. - // This can happen when there are more than one destination rule of same host in one namespace. - copied := mdr.rule.DeepCopy() - mdr.rule = &copied - mdr.from = append(mdr.from, types.NamespacedName{Namespace: destRuleConfig.Namespace, Name: destRuleConfig.Name}) - mergedRule := copied.Spec.(*networking.DestinationRule) - - existingSubset := map[string]struct{}{} - for _, subset := range mergedRule.Subsets { - existingSubset[subset.Name] = struct{}{} - } - // we have an another destination rule for same host. - // concatenate both of them -- essentially add subsets from one to other. - // Note: we only add the subsets and do not overwrite anything else like exportTo or top level - // traffic policies if they already exist - for _, subset := range rule.Subsets { - if _, ok := existingSubset[subset.Name]; !ok { - // if not duplicated, append - mergedRule.Subsets = append(mergedRule.Subsets, subset) - } else { - // duplicate subset - ps.AddMetric(DuplicatedSubsets, string(resolvedHost), "", - fmt.Sprintf("Duplicate subset %s found while merging destination rules for %s", - subset.Name, string(resolvedHost))) - } - } - - // If there is no top level policy and the incoming rule has top level - // traffic policy, use the one from the incoming rule. - if mergedRule.TrafficPolicy == nil && rule.TrafficPolicy != nil { - mergedRule.TrafficPolicy = rule.TrafficPolicy - } - // If there is no exportTo in the existing rule and - // the incoming rule has an explicit exportTo, use the - // one from the incoming rule. - if len(p.exportTo[resolvedHost]) == 0 && len(exportToMap) > 0 { - p.exportTo[resolvedHost] = exportToMap - } - } - if addRuleToProcessedDestRules { - p.destRules[resolvedHost] = append(p.destRules[resolvedHost], convertConsolidatedDestRule(&destRuleConfig)) - } - return - } - // DestinationRule does not exist for the resolved host so add it - p.destRules[resolvedHost] = append(p.destRules[resolvedHost], convertConsolidatedDestRule(&destRuleConfig)) - p.exportTo[resolvedHost] = exportToMap -} - -// inheritDestinationRule child config inherits settings from parent mesh/namespace -func (ps *PushContext) inheritDestinationRule(parent, child *consolidatedDestRule) *consolidatedDestRule { - if parent == nil { - return child - } - if child == nil { - return parent - } - - if parent.Equals(child) { - return parent - } - - parentDR := parent.rule.Spec.(*networking.DestinationRule) - if parentDR.TrafficPolicy == nil { - return child - } - - merged := parent.rule.DeepCopy() - // merge child into parent, child fields will overwrite parent's - proto.Merge(merged.Spec.(proto.Message), child.rule.Spec.(proto.Message)) - merged.Meta = child.rule.Meta - merged.Status = child.rule.Status - - childDR := child.rule.Spec.(*networking.DestinationRule) - // if parent has MUTUAL+certs/secret specified and child specifies SIMPLE, could break caCertificates - // if both parent and child specify TLS context, child's will be used only - if parentDR.TrafficPolicy.Tls != nil && (childDR.TrafficPolicy != nil && childDR.TrafficPolicy.Tls != nil) { - mergedDR := merged.Spec.(*networking.DestinationRule) - mergedDR.TrafficPolicy.Tls = childDR.TrafficPolicy.Tls.DeepCopy() - } - out := &consolidatedDestRule{} - out.rule = &merged - out.from = append(out.from, parent.from...) - out.from = append(out.from, child.from...) - return out -} - -func convertConsolidatedDestRule(cfg *config.Config) *consolidatedDestRule { - return &consolidatedDestRule{ - rule: cfg, - from: []types.NamespacedName{ - { - Namespace: cfg.Namespace, - Name: cfg.Name, - }, - }, - } -} - -// Equals compare l equals r consolidatedDestRule or not. -func (l *consolidatedDestRule) Equals(r *consolidatedDestRule) bool { - if l == r { - return true - } - if l == nil || r == nil { - return false - } - - // compare from - if len(l.from) != len(r.from) { - return false - } - for i, v := range l.from { - if v != r.from[i] { - return false - } - } - return true -} diff --git a/pilot/pkg/model/destination_rule_test.go b/pilot/pkg/model/destination_rule_test.go deleted file mode 100644 index f23b81be9..000000000 --- a/pilot/pkg/model/destination_rule_test.go +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "testing" -) - -import ( - "k8s.io/apimachinery/pkg/types" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -func TestConsolidatedDestRuleEquals(t *testing.T) { - testcases := []struct { - name string - l *consolidatedDestRule - r *consolidatedDestRule - expected bool - }{ - { - name: "two nil", - expected: true, - }, - { - name: "l is nil", - l: nil, - r: &consolidatedDestRule{ - from: []types.NamespacedName{ - { - Namespace: "default", - Name: "dr1", - }, - }, - }, - expected: false, - }, - { - name: "r is nil", - l: &consolidatedDestRule{ - from: []types.NamespacedName{ - { - Namespace: "default", - Name: "dr1", - }, - }, - }, - r: nil, - expected: false, - }, - { - name: "from length not equal", - l: &consolidatedDestRule{ - from: []types.NamespacedName{ - { - Namespace: "default", - Name: "dr1", - }, - }, - }, - r: &consolidatedDestRule{ - from: []types.NamespacedName{ - { - Namespace: "default", - Name: "dr1", - }, - { - Namespace: "default", - Name: "dr2", - }, - }, - }, - expected: false, - }, - { - name: "from length equals but element is different", - l: &consolidatedDestRule{ - from: []types.NamespacedName{ - { - Namespace: "default", - Name: "dr1", - }, - { - Namespace: "default", - Name: "dr2", - }, - }, - }, - r: &consolidatedDestRule{ - from: []types.NamespacedName{ - { - Namespace: "default", - Name: "dr1", - }, - { - Namespace: "default", - Name: "dr3", - }, - }, - }, - expected: false, - }, - { - name: "all from elements equal", - l: &consolidatedDestRule{ - from: []types.NamespacedName{ - { - Namespace: "default", - Name: "dr1", - }, - { - Namespace: "default", - Name: "dr2", - }, - }, - }, - r: &consolidatedDestRule{ - from: []types.NamespacedName{ - { - Namespace: "default", - Name: "dr1", - }, - { - Namespace: "default", - Name: "dr2", - }, - }, - }, - expected: true, - }, - } - - for _, tc := range testcases { - t.Run(tc.name, func(t *testing.T) { - assert.Equal(t, tc.l.Equals(tc.r), tc.expected) - }) - } -} diff --git a/pilot/pkg/model/disabled_ledger.go b/pilot/pkg/model/disabled_ledger.go deleted file mode 100644 index 13f946efb..000000000 --- a/pilot/pkg/model/disabled_ledger.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "errors" -) - -import ( - "istio.io/pkg/ledger" -) - -// DisabledLedger is an empty mock of the ledger.Ledger interface -// which we will substitute when distribution tracking is disabled. -type DisabledLedger struct { - ledger.Ledger -} - -func (d *DisabledLedger) Put(key, value string) (string, error) { - return "", nil -} - -func (d *DisabledLedger) Delete(key string) error { - return nil -} - -func (d *DisabledLedger) Get(key string) (string, error) { - return "", nil -} - -func (d *DisabledLedger) RootHash() string { - return "" -} - -func (d *DisabledLedger) GetPreviousValue(previousHash, key string) (result string, err error) { - return "", errors.New("distribution tracking is disabled") -} diff --git a/pilot/pkg/model/endpointshards.go b/pilot/pkg/model/endpointshards.go deleted file mode 100644 index 782e0a88a..000000000 --- a/pilot/pkg/model/endpointshards.go +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "fmt" - "sort" - "sync" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// shardRegistry is a simplified interface for registries that can produce a shard key -type shardRegistry interface { - Cluster() cluster.ID - Provider() provider.ID -} - -// ShardKeyFromRegistry computes the shard key based on provider type and cluster id. -func ShardKeyFromRegistry(instance shardRegistry) ShardKey { - return ShardKey{Cluster: instance.Cluster(), Provider: instance.Provider()} -} - -// ShardKey is the key for EndpointShards made of a key with the format "cluster/provider" -type ShardKey struct { - Cluster cluster.ID - Provider provider.ID -} - -func (sk ShardKey) String() string { - return fmt.Sprintf("%s/%s", sk.Provider, sk.Cluster) -} - -// EndpointShards holds the set of endpoint shards of a service. Registries update -// individual shards incrementally. The shards are aggregated and split into -// clusters when a push for the specific cluster is needed. -type EndpointShards struct { - // mutex protecting below map. - sync.RWMutex - - // Shards is used to track the shards. EDS updates are grouped by shard. - // Current implementation uses the registry name as key - in multicluster this is the - // name of the k8s cluster, derived from the config (secret). - Shards map[ShardKey][]*IstioEndpoint - - // ServiceAccounts has the concatenation of all service accounts seen so far in endpoints. - // This is updated on push, based on shards. If the previous list is different than - // current list, a full push will be forced, to trigger a secure naming update. - // Due to the larger time, it is still possible that connection errors will occur while - // CDS is updated. - ServiceAccounts sets.Set -} - -// Keys gives a sorted list of keys for EndpointShards.Shards. -// Calls to Keys should be guarded with a lock on the EndpointShards. -func (es *EndpointShards) Keys() []ShardKey { - // len(shards) ~= number of remote clusters which isn't too large, doing this sort frequently - // shouldn't be too problematic. If it becomes an issue we can cache it in the EndpointShards struct. - keys := make([]ShardKey, 0, len(es.Shards)) - for k := range es.Shards { - keys = append(keys, k) - } - if len(keys) >= 2 { - sort.Slice(keys, func(i, j int) bool { - if keys[i].Provider == keys[j].Provider { - return keys[i].Cluster < keys[j].Cluster - } - return keys[i].Provider < keys[j].Provider - }) - } - return keys -} - -// EndpointIndex is a mutex protected index of endpoint shards -type EndpointIndex struct { - mu sync.RWMutex - // keyed by svc then ns - shardsBySvc map[string]map[string]*EndpointShards - // We'll need to clear the cache in-sync with endpoint shards modifications. - cache XdsCache -} - -func NewEndpointIndex() *EndpointIndex { - return &EndpointIndex{ - shardsBySvc: make(map[string]map[string]*EndpointShards), - } -} - -func (e *EndpointIndex) SetCache(cache XdsCache) { - e.mu.Lock() - defer e.mu.Unlock() - e.cache = cache -} - -// must be called with lock -func (e *EndpointIndex) clearCacheForService(svc, ns string) { - if e.cache == nil { - return - } - e.cache.Clear(map[ConfigKey]struct{}{{ - Kind: gvk.ServiceEntry, - Name: svc, - Namespace: ns, - }: {}}) -} - -// Shardz returns a copy of the global map of shards but does NOT copy the underlying individual EndpointShards. -func (e *EndpointIndex) Shardz() map[string]map[string]*EndpointShards { - e.mu.RLock() - defer e.mu.RUnlock() - out := make(map[string]map[string]*EndpointShards, len(e.shardsBySvc)) - for svcKey, v := range e.shardsBySvc { - out[svcKey] = make(map[string]*EndpointShards, len(v)) - for nsKey, v := range v { - out[svcKey][nsKey] = v - } - } - return out -} - -// ShardsForService returns the shards and true if they are found, or returns nil, false. -func (e *EndpointIndex) ShardsForService(serviceName, namespace string) (*EndpointShards, bool) { - e.mu.RLock() - defer e.mu.RUnlock() - byNs, ok := e.shardsBySvc[serviceName] - if !ok { - return nil, false - } - shards, ok := byNs[namespace] - return shards, ok -} - -// GetOrCreateEndpointShard returns the shards. The second return parameter will be true if this service was seen -// for the first time. -func (e *EndpointIndex) GetOrCreateEndpointShard(serviceName, namespace string) (*EndpointShards, bool) { - e.mu.Lock() - defer e.mu.Unlock() - - if _, exists := e.shardsBySvc[serviceName]; !exists { - e.shardsBySvc[serviceName] = map[string]*EndpointShards{} - } - if ep, exists := e.shardsBySvc[serviceName][namespace]; exists { - return ep, false - } - // This endpoint is for a service that was not previously loaded. - ep := &EndpointShards{ - Shards: map[ShardKey][]*IstioEndpoint{}, - ServiceAccounts: sets.Set{}, - } - e.shardsBySvc[serviceName][namespace] = ep - // Clear the cache here to avoid race in cache writes. - e.clearCacheForService(serviceName, namespace) - return ep, true -} - -func (e *EndpointIndex) DeleteServiceShard(shard ShardKey, serviceName, namespace string, preserveKeys bool) { - e.mu.Lock() - defer e.mu.Unlock() - e.deleteServiceInner(shard, serviceName, namespace, preserveKeys) -} - -func (e *EndpointIndex) DeleteShard(shardKey ShardKey) { - e.mu.Lock() - defer e.mu.Unlock() - for svc, shardsByNamespace := range e.shardsBySvc { - for ns := range shardsByNamespace { - e.deleteServiceInner(shardKey, svc, ns, false) - } - } - e.cache.ClearAll() -} - -// must be called with lock -func (e *EndpointIndex) deleteServiceInner(shard ShardKey, serviceName, namespace string, preserveKeys bool) { - if e.shardsBySvc[serviceName] == nil || - e.shardsBySvc[serviceName][namespace] == nil { - return - } - epShards := e.shardsBySvc[serviceName][namespace] - epShards.Lock() - delete(epShards.Shards, shard) - epShards.ServiceAccounts = sets.Set{} - for _, shard := range epShards.Shards { - for _, ep := range shard { - if ep.ServiceAccount != "" { - epShards.ServiceAccounts.Insert(ep.ServiceAccount) - } - } - } - // Clear the cache here to avoid race in cache writes. - e.clearCacheForService(serviceName, namespace) - if !preserveKeys { - if len(epShards.Shards) == 0 { - delete(e.shardsBySvc[serviceName], namespace) - } - if len(e.shardsBySvc[serviceName]) == 0 { - delete(e.shardsBySvc, serviceName) - } - } - epShards.Unlock() -} diff --git a/pilot/pkg/model/envoyfilter.go b/pilot/pkg/model/envoyfilter.go deleted file mode 100644 index ee3c0605b..000000000 --- a/pilot/pkg/model/envoyfilter.go +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "regexp" - "strings" -) - -import ( - "google.golang.org/protobuf/proto" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/xds" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// EnvoyFilterWrapper is a wrapper for the EnvoyFilter api object with pre-processed data -type EnvoyFilterWrapper struct { - Name string - Namespace string - workloadSelector labels.Instance - Patches map[networking.EnvoyFilter_ApplyTo][]*EnvoyFilterConfigPatchWrapper -} - -// EnvoyFilterConfigPatchWrapper is a wrapper over the EnvoyFilter ConfigPatch api object -// fields are ordered such that this struct is aligned -type EnvoyFilterConfigPatchWrapper struct { - Value proto.Message - Match *networking.EnvoyFilter_EnvoyConfigObjectMatch - ApplyTo networking.EnvoyFilter_ApplyTo - Operation networking.EnvoyFilter_Patch_Operation - // Pre-compile the regex from proxy version match in the match - ProxyVersionRegex *regexp.Regexp - // ProxyPrefixMatch provides a prefix match for the proxy version. The current API only allows - // regex match, but as an optimization we can reduce this to a prefix match for common cases. - // If this is set, ProxyVersionRegex is ignored. - ProxyPrefixMatch string - Name string - Namespace string -} - -// wellKnownVersions defines a mapping of well known regex matches to prefix matches -// This is done only as an optimization; behavior should remain the same -// All versions specified by the default installation (Telemetry V2) should be added here. -var wellKnownVersions = map[string]string{ - `^1\.4.*`: "1.4", - `^1\.5.*`: "1.5", - `^1\.6.*`: "1.6", - `^1\.7.*`: "1.7", - `^1\.8.*`: "1.8", - `^1\.9.*`: "1.9", - `^1\.10.*`: "1.10", - `^1\.11.*`: "1.11", - `^1\.12.*`: "1.12", - `^1\.13.*`: "1.13", - `^1\.14.*`: "1.14", - `^1\.15.*`: "1.15", - `^1\.16.*`: "1.16", - `^1\.17.*`: "1.17", - `^1\.18.*`: "1.18", - `^1\.19.*`: "1.19", - // Hopefully we have a better API by 1.19. If not, add it here -} - -// convertToEnvoyFilterWrapper converts from EnvoyFilter config to EnvoyFilterWrapper object -func convertToEnvoyFilterWrapper(local *config.Config) *EnvoyFilterWrapper { - localEnvoyFilter := local.Spec.(*networking.EnvoyFilter) - - out := &EnvoyFilterWrapper{Name: local.Name, Namespace: local.Namespace} - if localEnvoyFilter.WorkloadSelector != nil { - out.workloadSelector = localEnvoyFilter.WorkloadSelector.Labels - } - out.Patches = make(map[networking.EnvoyFilter_ApplyTo][]*EnvoyFilterConfigPatchWrapper) - for _, cp := range localEnvoyFilter.ConfigPatches { - if cp.Patch == nil { - // Should be caught by validation, but sometimes its disabled and we don't want to crash - // as a result. - log.Debugf("envoyfilter %v/%v discarded due to missing patch", local.Namespace, local.Name) - continue - } - cpw := &EnvoyFilterConfigPatchWrapper{ - Name: local.Name, - Namespace: local.Namespace, - ApplyTo: cp.ApplyTo, - Match: cp.Match, - Operation: cp.Patch.Operation, - } - var err error - // Use non-strict building to avoid issues where EnvoyFilter is valid but meant - // for a different version of the API than we are built with - cpw.Value, err = xds.BuildXDSObjectFromStruct(cp.ApplyTo, cp.Patch.Value, false) - // There generally won't be an error here because validation catches mismatched types - // Should only happen in tests or without validation - if err != nil { - log.Errorf("failed to build envoy filter value: %v", err) - } - if cp.Match == nil { - // create a match all object - cpw.Match = &networking.EnvoyFilter_EnvoyConfigObjectMatch{Context: networking.EnvoyFilter_ANY} - } else if cp.Match.Proxy != nil && cp.Match.Proxy.ProxyVersion != "" { - // Attempt to convert regex to a simple prefix match for the common case of matching - // a standard Istio version. This field should likely be replaced with semver, but for now - // we can workaround the performance impact of regex - if prefix, f := wellKnownVersions[cp.Match.Proxy.ProxyVersion]; f { - cpw.ProxyPrefixMatch = prefix - } else { - // pre-compile the regex for proxy version if it exists - // ignore the error because validation catches invalid regular expressions. - cpw.ProxyVersionRegex, _ = regexp.Compile(cp.Match.Proxy.ProxyVersion) - } - } - - if _, exists := out.Patches[cp.ApplyTo]; !exists { - out.Patches[cp.ApplyTo] = make([]*EnvoyFilterConfigPatchWrapper, 0) - } - if cpw.Operation == networking.EnvoyFilter_Patch_INSERT_AFTER || - cpw.Operation == networking.EnvoyFilter_Patch_INSERT_BEFORE || - cpw.Operation == networking.EnvoyFilter_Patch_INSERT_FIRST { - // insert_before, after or first is applicable for network filter, - // http filter and http route, convert the rest to add - if cpw.ApplyTo != networking.EnvoyFilter_HTTP_FILTER && - cpw.ApplyTo != networking.EnvoyFilter_NETWORK_FILTER && - cpw.ApplyTo != networking.EnvoyFilter_HTTP_ROUTE { - cpw.Operation = networking.EnvoyFilter_Patch_ADD - } - } - out.Patches[cp.ApplyTo] = append(out.Patches[cp.ApplyTo], cpw) - } - return out -} - -func proxyMatch(proxy *Proxy, cp *EnvoyFilterConfigPatchWrapper) bool { - if cp.Match.Proxy == nil { - return true - } - - if cp.ProxyPrefixMatch != "" { - if !strings.HasPrefix(proxy.Metadata.IstioVersion, cp.ProxyPrefixMatch) { - return false - } - } - if cp.ProxyVersionRegex != nil { - ver := proxy.Metadata.IstioVersion - if ver == "" { - // we do not have a proxy version but the user has a regex. so this is a mismatch - return false - } - if !cp.ProxyVersionRegex.MatchString(ver) { - return false - } - } - - for k, v := range cp.Match.Proxy.Metadata { - if proxy.Metadata.Raw[k] != v { - return false - } - } - return true -} - -// Returns the keys of all the wrapped envoyfilters. -func (efw *EnvoyFilterWrapper) Keys() []string { - if efw == nil { - return nil - } - keys := sets.Set{} - for _, patches := range efw.Patches { - for _, patch := range patches { - keys.Insert(patch.Key()) - } - } - return keys.SortedList() -} - -func (cpw *EnvoyFilterConfigPatchWrapper) Key() string { - if cpw == nil { - return "" - } - return cpw.Namespace + "/" + cpw.Name -} diff --git a/pilot/pkg/model/envoyfilter_test.go b/pilot/pkg/model/envoyfilter_test.go deleted file mode 100644 index 0d128af4e..000000000 --- a/pilot/pkg/model/envoyfilter_test.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "testing" -) - -import ( - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" -) - -// TestEnvoyFilterMatch tests the matching logic for EnvoyFilter, in particular the regex -> prefix optimization -func TestEnvoyFilterMatch(t *testing.T) { - cases := []struct { - name string - config *networking.EnvoyFilter - expectedVersionPrefix string - matches map[string]bool - }{ - { - "version prefix match", - &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - Patch: &networking.EnvoyFilter_Patch{}, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Proxy: &networking.EnvoyFilter_ProxyMatch{ProxyVersion: `^1\.6.*`}, - }, - }, - }, - }, - "1.6", - map[string]bool{ - "1.6": true, - "1.6.0": true, - "1.6-dev.foo": true, - "1.5": false, - "11.6": false, - "foo1.6": false, - }, - }, - { - "version prefix mismatch", - &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - Patch: &networking.EnvoyFilter_Patch{}, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Proxy: &networking.EnvoyFilter_ProxyMatch{ProxyVersion: `1\.6.*`}, - }, - }, - }, - }, - "", - map[string]bool{ - "1.6": true, - "1.6.0": true, - "1.6-dev.foo": true, - "1.5": false, - "11.6": true, - "foo1.6": true, - }, - }, - { - "non-numeric", - &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - Patch: &networking.EnvoyFilter_Patch{}, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Proxy: &networking.EnvoyFilter_ProxyMatch{ProxyVersion: `foobar`}, - }, - }, - }, - }, - "", - map[string]bool{ - "1.6": false, - "1.6.0": false, - "1.6-dev.foo": false, - "foobar": true, - }, - }, - } - for _, tt := range cases { - got := convertToEnvoyFilterWrapper(&config.Config{ - Meta: config.Meta{}, - Spec: tt.config, - }) - if len(got.Patches[networking.EnvoyFilter_INVALID]) != 1 { - t.Fatalf("unexpected patches: %v", got.Patches) - } - filter := got.Patches[networking.EnvoyFilter_INVALID][0] - if filter.ProxyPrefixMatch != tt.expectedVersionPrefix { - t.Errorf("unexpected prefix: got %v wanted %v", filter.ProxyPrefixMatch, tt.expectedVersionPrefix) - } - for ver, match := range tt.matches { - got := proxyMatch(&Proxy{Metadata: &NodeMetadata{IstioVersion: ver}}, filter) - if got != match { - t.Errorf("expected %v to match %v, got %v", ver, match, got) - } - } - } -} - -func TestConvertEnvoyFilter(t *testing.T) { - cfilter := convertToEnvoyFilterWrapper(&config.Config{ - Meta: config.Meta{Name: "test", Namespace: "testns"}, - Spec: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - Patch: &networking.EnvoyFilter_Patch{}, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Proxy: &networking.EnvoyFilter_ProxyMatch{ProxyVersion: `foobar`}, - }, - }, - }, - }, - }) - if cfilter.Name != "test" && cfilter.Namespace != "testns" { - t.Errorf("expected name %s got %s and namespace %s got %s", "test", cfilter.Name, "testns", cfilter.Namespace) - } -} diff --git a/pilot/pkg/model/extensions.go b/pilot/pkg/model/extensions.go deleted file mode 100644 index 780837433..000000000 --- a/pilot/pkg/model/extensions.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "net/url" - "strings" - "time" -) - -import ( - envoyCoreV3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - envoyWasmFilterV3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/wasm/v3" - envoyExtensionsWasmV3 "github.com/envoyproxy/go-control-plane/envoy/extensions/wasm/v3" - "google.golang.org/protobuf/types/known/anypb" - "google.golang.org/protobuf/types/known/durationpb" - "google.golang.org/protobuf/types/known/wrapperspb" - extensions "istio.io/api/extensions/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model/credentials" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -const ( - defaultRuntime = "envoy.wasm.runtime.v8" - fileScheme = "file" - ociScheme = "oci" - - // name of environment variable at Wasm VM, which will carry the Wasm image pull secret. - WasmSecretEnv = "ISTIO_META_WASM_IMAGE_PULL_SECRET" - // name of environment variable at Wasm VM, which will carry the Wasm image pull policy. - WasmPolicyEnv = "ISTIO_META_WASM_IMAGE_PULL_POLICY" - // name of environment variable at Wasm VM, which will carry the resource version of WasmPlugin. - WasmResourceVersionEnv = "ISTIO_META_WASM_PLUGIN_RESOURCE_VERSION" -) - -type WasmPluginWrapper struct { - *extensions.WasmPlugin - - Name string - Namespace string - ResourceName string - - WasmExtensionConfig *envoyWasmFilterV3.Wasm -} - -func convertToWasmPluginWrapper(originPlugin config.Config) *WasmPluginWrapper { - var ok bool - // Make a deep copy since we are going to mutate the resource later for secret env variable. - // We do not want to mutate the underlying resource at informer cache. - plugin := originPlugin.DeepCopy() - var wasmPlugin *extensions.WasmPlugin - if wasmPlugin, ok = plugin.Spec.(*extensions.WasmPlugin); !ok { - return nil - } - - cfg := &anypb.Any{} - if wasmPlugin.PluginConfig != nil && len(wasmPlugin.PluginConfig.Fields) > 0 { - cfgJSON, err := protomarshal.ToJSON(wasmPlugin.PluginConfig) - if err != nil { - log.Warnf("wasmplugin %v/%v discarded due to json marshaling error: %s", plugin.Namespace, plugin.Name, err) - return nil - } - cfg = networking.MessageToAny(&wrapperspb.StringValue{ - Value: cfgJSON, - }) - } - - u, err := url.Parse(wasmPlugin.Url) - if err != nil { - log.Warnf("wasmplugin %v/%v discarded due to failure to parse URL: %s", plugin.Namespace, plugin.Name, err) - return nil - } - // when no scheme is given, default to oci:// - if u.Scheme == "" { - u.Scheme = ociScheme - } - // Normalize the image pull secret to the full resource name. - wasmPlugin.ImagePullSecret = toSecretResourceName(wasmPlugin.ImagePullSecret, plugin.Namespace) - datasource := buildDataSource(u, wasmPlugin) - wasmExtensionConfig := &envoyWasmFilterV3.Wasm{ - Config: &envoyExtensionsWasmV3.PluginConfig{ - Name: plugin.Namespace + "." + plugin.Name, - RootId: wasmPlugin.PluginName, - Configuration: cfg, - Vm: buildVMConfig(datasource, plugin.ResourceVersion, wasmPlugin), - }, - } - if err != nil { - log.Warnf("WasmPlugin %s/%s failed to marshal to TypedExtensionConfig: %s", plugin.Namespace, plugin.Name, err) - return nil - } - return &WasmPluginWrapper{ - Name: plugin.Name, - Namespace: plugin.Namespace, - ResourceName: plugin.Namespace + "." + plugin.Name, - WasmPlugin: wasmPlugin, - WasmExtensionConfig: wasmExtensionConfig, - } -} - -// toSecretResourceName converts a imagePullSecret to a resource name referenced at Wasm SDS. -// NOTE: the secret referenced by WasmPlugin has to be in the same namespace as the WasmPlugin, -// so this function makes sure that the secret resource name, which will be used to retrieve secret at -// xds generation time, has the same namespace as the WasmPlugin. -func toSecretResourceName(name, pluginNamespace string) string { - if name == "" { - return "" - } - // Convert user provided secret name to secret resource name. - rn := credentials.ToResourceName(name) - // Parse the secret resource name. - sr, err := credentials.ParseResourceName(rn, pluginNamespace, "", "") - if err != nil { - log.Debugf("Failed to parse wasm secret resource name %v", err) - return "" - } - // Forcely rewrite secret namespace to plugin namespace, since we require secret resource - // referenced by WasmPlugin co-located with WasmPlugin in the same namespace. - sr.Namespace = pluginNamespace - return sr.KubernetesResourceName() -} - -func buildDataSource(u *url.URL, wasmPlugin *extensions.WasmPlugin) *envoyCoreV3.AsyncDataSource { - if u.Scheme == fileScheme { - return &envoyCoreV3.AsyncDataSource{ - Specifier: &envoyCoreV3.AsyncDataSource_Local{ - Local: &envoyCoreV3.DataSource{ - Specifier: &envoyCoreV3.DataSource_Filename{ - Filename: strings.TrimPrefix(wasmPlugin.Url, "file://"), - }, - }, - }, - } - } - - return &envoyCoreV3.AsyncDataSource{ - Specifier: &envoyCoreV3.AsyncDataSource_Remote{ - Remote: &envoyCoreV3.RemoteDataSource{ - HttpUri: &envoyCoreV3.HttpUri{ - Uri: u.String(), - Timeout: durationpb.New(30 * time.Second), - HttpUpstreamType: &envoyCoreV3.HttpUri_Cluster{ - // this will be fetched by the agent anyway, so no need for a cluster - Cluster: "_", - }, - }, - Sha256: wasmPlugin.Sha256, - }, - }, - } -} - -func buildVMConfig( - datasource *envoyCoreV3.AsyncDataSource, - resourceVersion string, - wasmPlugin *extensions.WasmPlugin) *envoyExtensionsWasmV3.PluginConfig_VmConfig { - cfg := &envoyExtensionsWasmV3.PluginConfig_VmConfig{ - VmConfig: &envoyExtensionsWasmV3.VmConfig{ - Runtime: defaultRuntime, - Code: datasource, - EnvironmentVariables: &envoyExtensionsWasmV3.EnvironmentVariables{ - KeyValues: map[string]string{}, - }, - }, - } - - if wasmPlugin.ImagePullSecret != "" { - cfg.VmConfig.EnvironmentVariables.KeyValues[WasmSecretEnv] = wasmPlugin.ImagePullSecret - } - - if wasmPlugin.ImagePullPolicy != extensions.PullPolicy_UNSPECIFIED_POLICY { - cfg.VmConfig.EnvironmentVariables.KeyValues[WasmPolicyEnv] = wasmPlugin.ImagePullPolicy.String() - } - - cfg.VmConfig.EnvironmentVariables.KeyValues[WasmResourceVersionEnv] = resourceVersion - - vm := wasmPlugin.VmConfig - if vm != nil && len(vm.Env) != 0 { - hostEnvKeys := make([]string, 0, len(vm.Env)) - for _, e := range vm.Env { - switch e.ValueFrom { - case extensions.EnvValueSource_INLINE: - cfg.VmConfig.EnvironmentVariables.KeyValues[e.Name] = e.Value - case extensions.EnvValueSource_HOST: - hostEnvKeys = append(hostEnvKeys, e.Name) - } - } - cfg.VmConfig.EnvironmentVariables.HostEnvKeys = hostEnvKeys - } - - return cfg -} diff --git a/pilot/pkg/model/extensions_test.go b/pilot/pkg/model/extensions_test.go deleted file mode 100644 index 8033d3ff9..000000000 --- a/pilot/pkg/model/extensions_test.go +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "net/url" - "testing" - "time" -) - -import ( - envoyCoreV3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - envoyExtensionsWasmV3 "github.com/envoyproxy/go-control-plane/envoy/extensions/wasm/v3" - "google.golang.org/protobuf/types/known/durationpb" - extensions "istio.io/api/extensions/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model/credentials" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -func TestBuildDataSource(t *testing.T) { - cases := []struct { - url string - wasmPlugin *extensions.WasmPlugin - - expected *envoyCoreV3.AsyncDataSource - }{ - { - url: "file://fake.wasm", - wasmPlugin: &extensions.WasmPlugin{ - Url: "file://fake.wasm", - }, - expected: &envoyCoreV3.AsyncDataSource{ - Specifier: &envoyCoreV3.AsyncDataSource_Local{ - Local: &envoyCoreV3.DataSource{ - Specifier: &envoyCoreV3.DataSource_Filename{ - Filename: "fake.wasm", - }, - }, - }, - }, - }, - { - url: "oci://ghcr.io/istio/fake-wasm:latest", - wasmPlugin: &extensions.WasmPlugin{ - Sha256: "fake-sha256", - }, - expected: &envoyCoreV3.AsyncDataSource{ - Specifier: &envoyCoreV3.AsyncDataSource_Remote{ - Remote: &envoyCoreV3.RemoteDataSource{ - HttpUri: &envoyCoreV3.HttpUri{ - Uri: "oci://ghcr.io/istio/fake-wasm:latest", - Timeout: durationpb.New(30 * time.Second), - HttpUpstreamType: &envoyCoreV3.HttpUri_Cluster{ - Cluster: "_", - }, - }, - Sha256: "fake-sha256", - }, - }, - }, - }, - } - - for _, tc := range cases { - t.Run("", func(t *testing.T) { - u, err := url.Parse(tc.url) - assert.NoError(t, err) - got := buildDataSource(u, tc.wasmPlugin) - assert.Equal(t, tc.expected, got) - }) - } -} - -func TestBuildVMConfig(t *testing.T) { - cases := []struct { - desc string - vm *extensions.VmConfig - policy extensions.PullPolicy - expected *envoyExtensionsWasmV3.PluginConfig_VmConfig - }{ - { - desc: "Build VMConfig without a base VMConfig", - vm: nil, - policy: extensions.PullPolicy_UNSPECIFIED_POLICY, - expected: &envoyExtensionsWasmV3.PluginConfig_VmConfig{ - VmConfig: &envoyExtensionsWasmV3.VmConfig{ - Runtime: defaultRuntime, - EnvironmentVariables: &envoyExtensionsWasmV3.EnvironmentVariables{ - KeyValues: map[string]string{ - WasmSecretEnv: "secret-name", - WasmResourceVersionEnv: "dummy-resource-version", - }, - }, - }, - }, - }, - { - desc: "Build VMConfig on top of a base VMConfig", - vm: &extensions.VmConfig{ - Env: []*extensions.EnvVar{ - { - Name: "POD_NAME", - ValueFrom: extensions.EnvValueSource_HOST, - }, - { - Name: "ENV1", - Value: "VAL1", - }, - }, - }, - policy: extensions.PullPolicy_UNSPECIFIED_POLICY, - expected: &envoyExtensionsWasmV3.PluginConfig_VmConfig{ - VmConfig: &envoyExtensionsWasmV3.VmConfig{ - Runtime: defaultRuntime, - EnvironmentVariables: &envoyExtensionsWasmV3.EnvironmentVariables{ - HostEnvKeys: []string{"POD_NAME"}, - KeyValues: map[string]string{ - "ENV1": "VAL1", - WasmSecretEnv: "secret-name", - WasmResourceVersionEnv: "dummy-resource-version", - }, - }, - }, - }, - }, - { - desc: "Build VMConfig with if-not-present pull policy", - vm: nil, - policy: extensions.PullPolicy_IfNotPresent, - expected: &envoyExtensionsWasmV3.PluginConfig_VmConfig{ - VmConfig: &envoyExtensionsWasmV3.VmConfig{ - Runtime: defaultRuntime, - EnvironmentVariables: &envoyExtensionsWasmV3.EnvironmentVariables{ - KeyValues: map[string]string{ - WasmSecretEnv: "secret-name", - WasmPolicyEnv: extensions.PullPolicy_name[int32(extensions.PullPolicy_IfNotPresent)], - WasmResourceVersionEnv: "dummy-resource-version", - }, - }, - }, - }, - }, - } - - for _, tc := range cases { - t.Run(tc.desc, func(t *testing.T) { - got := buildVMConfig(nil, "dummy-resource-version", &extensions.WasmPlugin{ - VmConfig: tc.vm, - ImagePullSecret: "secret-name", - ImagePullPolicy: tc.policy, - }) - assert.Equal(t, tc.expected, got) - }) - } -} - -func TestToSecretName(t *testing.T) { - cases := []struct { - name string - namespace string - want string - wantResourceName string - wantResourceNamespace string - }{ - { - name: "sec", - namespace: "nm", - want: credentials.KubernetesSecretTypeURI + "nm/sec", - wantResourceName: "sec", - wantResourceNamespace: "nm", - }, - { - name: "nm/sec", - namespace: "nm", - want: credentials.KubernetesSecretTypeURI + "nm/sec", - wantResourceName: "sec", - wantResourceNamespace: "nm", - }, - { - name: "nm2/sec", - namespace: "nm", - // Makes sure we won't search namespace outside of nm (which is the WasmPlugin namespace). - want: credentials.KubernetesSecretTypeURI + "nm/sec", - wantResourceName: "sec", - wantResourceNamespace: "nm", - }, - { - name: credentials.KubernetesSecretTypeURI + "nm/sec", - namespace: "nm", - want: credentials.KubernetesSecretTypeURI + "nm/sec", - wantResourceName: "sec", - wantResourceNamespace: "nm", - }, - { - name: "kubernetes://nm2/sec", - namespace: "nm", - want: credentials.KubernetesSecretTypeURI + "nm/sec", - wantResourceName: "sec", - wantResourceNamespace: "nm", - }, - { - name: "kubernetes://sec", - namespace: "nm", - want: credentials.KubernetesSecretTypeURI + "nm/sec", - wantResourceName: "sec", - wantResourceNamespace: "nm", - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - got := toSecretResourceName(tt.name, tt.namespace) - if got != tt.want { - t.Errorf("got secret name %q, want %q", got, tt.want) - } - sr, err := credentials.ParseResourceName(got, tt.namespace, cluster.ID("cluster"), cluster.ID("cluster")) - if err != nil { - t.Error(err) - } - if sr.Name != tt.wantResourceName { - t.Errorf("parse secret name got %v want %v", sr.Name, tt.name) - } - if sr.Namespace != tt.wantResourceNamespace { - t.Errorf("parse secret name got %v want %v", sr.Name, tt.name) - } - }) - } -} diff --git a/pilot/pkg/model/fake_store.go b/pilot/pkg/model/fake_store.go deleted file mode 100644 index 6fb6dc119..000000000 --- a/pilot/pkg/model/fake_store.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -type FakeStore struct { - store map[config.GroupVersionKind]map[string][]config.Config -} - -func NewFakeStore() *FakeStore { - f := FakeStore{ - store: make(map[config.GroupVersionKind]map[string][]config.Config), - } - return &f -} - -var _ ConfigStore = (*FakeStore)(nil) - -func (s *FakeStore) Schemas() collection.Schemas { - return collections.Pilot -} - -func (*FakeStore) Get(typ config.GroupVersionKind, name, namespace string) *config.Config { return nil } - -func (s *FakeStore) List(typ config.GroupVersionKind, namespace string) ([]config.Config, error) { - nsConfigs := s.store[typ] - if nsConfigs == nil { - return nil, nil - } - var res []config.Config - if namespace == NamespaceAll { - for _, configs := range nsConfigs { - res = append(res, configs...) - } - return res, nil - } - return nsConfigs[namespace], nil -} - -func (s *FakeStore) Create(cfg config.Config) (revision string, err error) { - configs := s.store[cfg.GroupVersionKind] - if configs == nil { - configs = make(map[string][]config.Config) - } - configs[cfg.Namespace] = append(configs[cfg.Namespace], cfg) - s.store[cfg.GroupVersionKind] = configs - return "", nil -} - -func (*FakeStore) Update(config config.Config) (newRevision string, err error) { return "", nil } - -func (*FakeStore) UpdateStatus(config config.Config) (string, error) { return "", nil } - -func (*FakeStore) Patch(orig config.Config, patchFn config.PatchFunc) (string, error) { - return "", nil -} - -func (*FakeStore) Delete(typ config.GroupVersionKind, name, namespace string, rv *string) error { - return nil -} diff --git a/pilot/pkg/model/gateway.go b/pilot/pkg/model/gateway.go deleted file mode 100644 index 5c8870406..000000000 --- a/pilot/pkg/model/gateway.go +++ /dev/null @@ -1,542 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "fmt" - "sort" - "strconv" - "strings" -) - -import ( - networking "istio.io/api/networking/v1alpha3" - "istio.io/pkg/monitoring" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model/credentials" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/gateway" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// ServerPort defines port for the gateway server. -type ServerPort struct { - // A valid non-negative integer port number. - Number uint32 - // The protocol exposed on the port. - Protocol string - // The bind server specified on this port. - Bind string -} - -// MergedServers describes set of servers defined in all gateways per port. -type MergedServers struct { - Servers []*networking.Server - RouteName string // RouteName for http servers. For HTTPS, TLSServerInfo will hold the route name. -} - -// TLSServerInfo contains additional information for TLS Servers. -type TLSServerInfo struct { - RouteName string - SNIHosts []string -} - -// MergedGateway describes a set of gateways for a workload merged into a single logical gateway. -type MergedGateway struct { - // ServerPorts maintains a list of unique server ports, used for stable ordering. - ServerPorts []ServerPort - - // MergedServers map from physical port to virtual servers - // using TCP protocols (like HTTP1.1, H2, mysql, redis etc) - MergedServers map[ServerPort]*MergedServers - - // MergedQUICTransportServers map from physical port to servers listening - // on QUIC (like HTTP3). Currently the support is experimental and - // is limited to HTTP3 only - MergedQUICTransportServers map[ServerPort]*MergedServers - - // HTTP3AdvertisingRoutes represents the set of HTTP routes which advertise HTTP/3. - // This mapping is used to generate alt-svc header that is needed for HTTP/3 server discovery. - HTTP3AdvertisingRoutes map[string]struct{} - - // GatewayNameForServer maps from server to the owning gateway name. - // Used for select the set of virtual services that apply to a port. - GatewayNameForServer map[*networking.Server]string - - // ServersByRouteName maps from port names to virtual hosts - // Used for RDS. No two port names share same port except for HTTPS - // The typical length of the value is always 1, except for HTTP (not HTTPS), - ServersByRouteName map[string][]*networking.Server - - // TLSServerInfo maps from server to a corresponding TLS information like TLS Routename and SNIHosts. - TLSServerInfo map[*networking.Server]*TLSServerInfo - - // ContainsAutoPassthroughGateways determines if there are any type AUTO_PASSTHROUGH Gateways, requiring additional - // clusters to be sent to the workload - ContainsAutoPassthroughGateways bool - - // PortMap defines a mapping of targetPorts to the set of Service ports that reference them - PortMap GatewayPortMap - - // VerifiedCertificateReferences contains a set of all credentialNames referenced by gateways *in the same namespace as the proxy*. - // These are considered "verified", since there is mutually agreement from the pod, Secret, and Gateway, as all - // reside in the same namespace and trust boundary. - // Note: Secrets that are not referenced by any Gateway, but are in the same namespace as the pod, are explicitly *not* - // included. This ensures we don't give permission to unexpected secrets, such as the citadel root key/cert. - VerifiedCertificateReferences sets.Set -} - -var ( - typeTag = monitoring.MustCreateLabel("type") - nameTag = monitoring.MustCreateLabel("name") - - totalRejectedConfigs = monitoring.NewSum( - "pilot_total_rejected_configs", - "Total number of configs that Pilot had to reject or ignore.", - monitoring.WithLabels(typeTag, nameTag), - ) -) - -func init() { - monitoring.MustRegister(totalRejectedConfigs) -} - -func RecordRejectedConfig(gatewayName string) { - totalRejectedConfigs.With(typeTag.Value("gateway"), nameTag.Value(gatewayName)).Increment() -} - -// DisableGatewayPortTranslationLabel is a label on Service that declares that, for that particular -// service, we should not translate Gateway ports to target ports. For example, if I have a Service -// on port 80 with target port 8080, with the label. Gateways on port 80 would *not* match. Instead, -// only Gateways on port 8080 would be used. This prevents ambiguities when there are multiple -// Services on port 80 referring to different target ports. Long term, this will be replaced by -// Gateways directly referencing a Service, rather than label selectors. Warning: this label is -// intended solely for as a workaround for Knative's Istio integration, and not intended for any -// other usage. It can, and will, be removed immediately after the new direct reference is ready for -// use. -const DisableGatewayPortTranslationLabel = "experimental.istio.io/disable-gateway-port-translation" - -// MergeGateways combines multiple gateways targeting the same workload into a single logical Gateway. -// Note that today any Servers in the combined gateways listening on the same port must have the same protocol. -// If servers with different protocols attempt to listen on the same port, one of the protocols will be chosen at random. -func MergeGateways(gateways []gatewayWithInstances, proxy *Proxy, ps *PushContext) *MergedGateway { - gatewayPorts := make(map[uint32]bool) - mergedServers := make(map[ServerPort]*MergedServers) - mergedQUICServers := make(map[ServerPort]*MergedServers) - serverPorts := make([]ServerPort, 0) - plainTextServers := make(map[uint32]ServerPort) - serversByRouteName := make(map[string][]*networking.Server) - tlsServerInfo := make(map[*networking.Server]*TLSServerInfo) - gatewayNameForServer := make(map[*networking.Server]string) - verifiedCertificateReferences := sets.New() - http3AdvertisingRoutes := sets.New() - tlsHostsByPort := map[uint32]sets.Set{} // port -> host set - autoPassthrough := false - - log.Debugf("MergeGateways: merging %d gateways", len(gateways)) - for _, gwAndInstance := range gateways { - gatewayConfig := gwAndInstance.gateway - gatewayName := gatewayConfig.Namespace + "/" + gatewayConfig.Name // Format: %s/%s - gatewayCfg := gatewayConfig.Spec.(*networking.Gateway) - log.Debugf("MergeGateways: merging gateway %q :\n%v", gatewayName, gatewayCfg) - snames := sets.Set{} - for _, s := range gatewayCfg.Servers { - if len(s.Name) > 0 { - if snames.Contains(s.Name) { - log.Warnf("Server name %s is not unique in gateway %s and may create possible issues like stat prefix collision ", - s.Name, gatewayName) - } else { - snames.Insert(s.Name) - } - } - if s.Port == nil { - // Should be rejected in validation, this is an extra check - log.Debugf("invalid server without port: %q", gatewayName) - RecordRejectedConfig(gatewayName) - continue - } - sanitizeServerHostNamespace(s, gatewayConfig.Namespace) - gatewayNameForServer[s] = gatewayName - log.Debugf("MergeGateways: gateway %q processing server %s :%v", gatewayName, s.Name, s.Hosts) - - cn := s.GetTls().GetCredentialName() - if cn != "" && proxy.VerifiedIdentity != nil { - rn := credentials.ToResourceName(cn) - parse, _ := credentials.ParseResourceName(rn, proxy.VerifiedIdentity.Namespace, "", "") - if gatewayConfig.Namespace == proxy.VerifiedIdentity.Namespace && parse.Namespace == proxy.VerifiedIdentity.Namespace { - // Same namespace is always allowed - verifiedCertificateReferences.Insert(rn) - } else if ps.ReferenceAllowed(gvk.Secret, rn, proxy.VerifiedIdentity.Namespace) { - // Explicitly allowed by some policy - verifiedCertificateReferences.Insert(rn) - } - } - for _, resolvedPort := range resolvePorts(s.Port.Number, gwAndInstance.instances, gwAndInstance.legacyGatewaySelector) { - routeName := gatewayRDSRouteName(s, resolvedPort, gatewayConfig) - if s.Tls != nil { - // Envoy will reject config that has multiple filter chain matches with the same matching rules. - // To avoid this, we need to make sure we don't have duplicated hosts, which will become - // SNI filter chain matches. - if tlsHostsByPort[resolvedPort] == nil { - tlsHostsByPort[resolvedPort] = sets.New() - } - if duplicateHosts := CheckDuplicates(s.Hosts, tlsHostsByPort[resolvedPort]); len(duplicateHosts) != 0 { - log.Debugf("skipping server on gateway %s, duplicate host names: %v", gatewayName, duplicateHosts) - RecordRejectedConfig(gatewayName) - continue - } - tlsServerInfo[s] = &TLSServerInfo{SNIHosts: GetSNIHostsForServer(s), RouteName: routeName} - if s.Tls.Mode == networking.ServerTLSSettings_AUTO_PASSTHROUGH { - autoPassthrough = true - } - } - serverPort := ServerPort{resolvedPort, s.Port.Protocol, s.Bind} - serverProtocol := protocol.Parse(serverPort.Protocol) - if gatewayPorts[resolvedPort] { - // We have two servers on the same port. Should we merge? - // 1. Yes if both servers are plain text and HTTP - // 2. Yes if both servers are using TLS - // if using HTTPS ensure that port name is distinct so that we can setup separate RDS - // for each server (as each server ends up as a separate http connection manager due to filter chain match) - // 3. No for everything else. - if current, exists := plainTextServers[resolvedPort]; exists { - if !canMergeProtocols(serverProtocol, protocol.Parse(current.Protocol)) { - log.Infof("skipping server on gateway %s port %s.%d.%s: conflict with existing server %d.%s", - gatewayConfig.Name, s.Port.Name, resolvedPort, s.Port.Protocol, serverPort.Number, serverPort.Protocol) - RecordRejectedConfig(gatewayName) - continue - } - if routeName == "" { - log.Debugf("skipping server on gateway %s port %s.%d.%s: could not build RDS name from server", - gatewayConfig.Name, s.Port.Name, resolvedPort, s.Port.Protocol) - RecordRejectedConfig(gatewayName) - continue - } - if current.Bind != serverPort.Bind { - // Merge it to servers with the same port and bind. - if mergedServers[serverPort] == nil { - mergedServers[serverPort] = &MergedServers{Servers: []*networking.Server{}} - serverPorts = append(serverPorts, serverPort) - } - ms := mergedServers[serverPort] - ms.RouteName = routeName - ms.Servers = append(ms.Servers, s) - } else { - // Merge this to current known port with same bind. - ms := mergedServers[current] - ms.Servers = append(ms.Servers, s) - } - serversByRouteName[routeName] = append(serversByRouteName[routeName], s) - } else { - // We have duplicate port. Its not in plaintext servers. So, this has to be a TLS server. - // Check if this is also a HTTP server and if so, ensure uniqueness of port name. - if gateway.IsHTTPServer(s) { - if routeName == "" { - log.Debugf("skipping server on gateway %s port %s.%d.%s: could not build RDS name from server", - gatewayConfig.Name, s.Port.Name, resolvedPort, s.Port.Protocol) - RecordRejectedConfig(gatewayName) - continue - } - - // Both servers are HTTPS servers. Make sure the port names are different so that RDS can pick out individual servers. - // We cannot have two servers with same port name because we need the port name to distinguish one HTTPS server from another. - // We cannot merge two HTTPS servers even if their TLS settings have same path to the keys, because we don't know if the contents - // of the keys are same. So we treat them as effectively different TLS settings. - // This check is largely redundant now since we create rds names for https using gateway name, namespace - // and validation ensures that all port names within a single gateway config are unique. - if _, exists := serversByRouteName[routeName]; exists { - log.Infof("skipping server on gateway %s port %s.%d.%s: non unique port name for HTTPS port", - gatewayConfig.Name, s.Port.Name, resolvedPort, s.Port.Protocol) - RecordRejectedConfig(gatewayName) - continue - } - serversByRouteName[routeName] = []*networking.Server{s} - } - - // We have another TLS server on the same port. Can differentiate servers using SNI - if s.Tls == nil { - log.Warnf("TLS server without TLS options %s %s", gatewayName, s.String()) - continue - } - if mergedServers[serverPort] == nil { - mergedServers[serverPort] = &MergedServers{Servers: []*networking.Server{s}} - serverPorts = append(serverPorts, serverPort) - } else { - mergedServers[serverPort].Servers = append(mergedServers[serverPort].Servers, s) - } - - // We have TLS settings defined and we have already taken care of unique route names - // if it is HTTPS. So we can construct a QUIC server on the same port. It is okay as - // QUIC listens on UDP port, not TCP - if features.EnableQUICListeners && gateway.IsEligibleForHTTP3Upgrade(s) && - udpSupportedPort(s.GetPort().GetNumber(), gwAndInstance.instances) { - log.Debugf("Server at port %d eligible for HTTP3 upgrade. Add UDP listener for QUIC", serverPort.Number) - if mergedQUICServers[serverPort] == nil { - mergedQUICServers[serverPort] = &MergedServers{Servers: []*networking.Server{}} - } - mergedQUICServers[serverPort].Servers = append(mergedQUICServers[serverPort].Servers, s) - http3AdvertisingRoutes[routeName] = struct{}{} - } - } - } else { - // This is a new gateway on this port. Create MergedServers for it. - gatewayPorts[resolvedPort] = true - if !gateway.IsTLSServer(s) { - plainTextServers[serverPort.Number] = serverPort - } - if gateway.IsHTTPServer(s) { - serversByRouteName[routeName] = []*networking.Server{s} - - if features.EnableQUICListeners && gateway.IsEligibleForHTTP3Upgrade(s) && - udpSupportedPort(s.GetPort().GetNumber(), gwAndInstance.instances) { - log.Debugf("Server at port %d eligible for HTTP3 upgrade. So QUIC listener will be added", serverPort.Number) - http3AdvertisingRoutes[routeName] = struct{}{} - - if mergedQUICServers[serverPort] == nil { - // This should be treated like non-passthrough HTTPS case. There will be multiple filter - // chains, multiple routes per server port. So just like in TLS server case we do not - // track route name here. Instead, TLS server info is used (it is fine for now because - // this would be a mirror of an existing non-passthrough HTTPS server) - mergedQUICServers[serverPort] = &MergedServers{Servers: []*networking.Server{s}} - } - } - } - mergedServers[serverPort] = &MergedServers{Servers: []*networking.Server{s}, RouteName: routeName} - serverPorts = append(serverPorts, serverPort) - } - log.Debugf("MergeGateways: gateway %q merged server %v", gatewayName, s.Hosts) - } - } - } - - return &MergedGateway{ - MergedServers: mergedServers, - MergedQUICTransportServers: mergedQUICServers, - ServerPorts: serverPorts, - GatewayNameForServer: gatewayNameForServer, - TLSServerInfo: tlsServerInfo, - ServersByRouteName: serversByRouteName, - HTTP3AdvertisingRoutes: http3AdvertisingRoutes, - ContainsAutoPassthroughGateways: autoPassthrough, - PortMap: getTargetPortMap(serversByRouteName), - VerifiedCertificateReferences: verifiedCertificateReferences, - } -} - -func udpSupportedPort(number uint32, instances []*ServiceInstance) bool { - for _, w := range instances { - if int(number) == w.ServicePort.Port && w.ServicePort.Protocol == protocol.UDP { - return true - } - } - return false -} - -// resolvePorts takes a Gateway port, and resolves it to the port that will actually be listened on. -// When legacyGatewaySelector=false, then the gateway is directly referencing a Service. In this -// case, the translation is un-ambiguous - we just find the matching port and return the targetPort -// When legacyGatewaySelector=true things are a bit more complex, as we support referencing a Service -// port and translating to the targetPort in addition to just directly referencing a port. In this -// case, we just make a best effort guess by picking the first match. -func resolvePorts(number uint32, instances []*ServiceInstance, legacyGatewaySelector bool) []uint32 { - ports := map[uint32]struct{}{} - for _, w := range instances { - if _, disablePortTranslation := w.Service.Attributes.Labels[DisableGatewayPortTranslationLabel]; disablePortTranslation && legacyGatewaySelector { - // Skip this Service, they opted out of port translation - // This is only done for legacyGatewaySelector, as the new gateway selection mechanism *only* allows - // referencing the Service port, and references are un-ambiguous. - continue - } - if w.ServicePort.Port == int(number) && w.Endpoint != nil { - if legacyGatewaySelector { - // When we are using legacy gateway label selection, we only resolve to a single port - // This has pros and cons; we don't allow merging of routes when it would be desirable, but - // we also avoid accidentally merging routes when we didn't intend to. While neither option is great, - // picking the first one here preserves backwards compatibility. - return []uint32{w.Endpoint.EndpointPort} - } - ports[w.Endpoint.EndpointPort] = struct{}{} - } - } - ret := make([]uint32, 0, len(ports)) - for p := range ports { - ret = append(ret, p) - } - if len(ret) == 0 && legacyGatewaySelector { - // When we are using legacy gateway label selection, we should bind to the port as-is if there is - // no matching ServiceInstance. - return []uint32{number} - } - // For cases where we are directly referencing a Service, we know that they port *must* be in the Service, - // so we have no fallback. If there was no match, the Gateway is a no-op. - return ret -} - -func canMergeProtocols(current protocol.Instance, p protocol.Instance) bool { - return (current.IsHTTP() || current == p) && p.IsHTTP() -} - -func GetSNIHostsForServer(server *networking.Server) []string { - if server.Tls == nil { - return nil - } - // sanitize the server hosts as it could contain hosts of form ns/host - sniHosts := make(map[string]bool) - for _, h := range server.Hosts { - if strings.Contains(h, "/") { - parts := strings.Split(h, "/") - h = parts[1] - } - // do not add hosts, that have already been added - if !sniHosts[h] { - sniHosts[h] = true - } - } - sniHostsSlice := make([]string, 0, len(sniHosts)) - for host := range sniHosts { - sniHostsSlice = append(sniHostsSlice, host) - } - sort.Strings(sniHostsSlice) - - return sniHostsSlice -} - -// CheckDuplicates returns all of the hosts provided that are already known -// If there were no duplicates, all hosts are added to the known hosts. -func CheckDuplicates(hosts []string, knownHosts sets.Set) []string { - var duplicates []string - for _, h := range hosts { - if knownHosts.Contains(h) { - duplicates = append(duplicates, h) - } - } - // No duplicates found, so we can mark all of these hosts as known - if len(duplicates) == 0 { - for _, h := range hosts { - knownHosts.Insert(h) - } - } - return duplicates -} - -// gatewayRDSRouteName generates the RDS route config name for gateway's servers. -// Unlike sidecars where the RDS route name is the listener port number, gateways have a different -// structure for RDS. -// HTTP servers have route name set to http.. -// -// Multiple HTTP servers can exist on the same port and the code will combine all of them into -// one single RDS payload for http. -// -// HTTPS servers with TLS termination (i.e. envoy decoding the content, and making outbound http calls to backends) -// will use route name https..... HTTPS servers using SNI passthrough or -// non-HTTPS servers (e.g., TCP+TLS) with SNI passthrough will be setup as opaque TCP proxies without terminating -// the SSL connection. They would inspect the SNI header and forward to the appropriate upstream as opaque TCP. -// -// Within HTTPS servers terminating TLS, user could setup multiple servers in the gateway. each server could have -// one or more hosts but have different TLS certificates. In this case, we end up having separate filter chain -// for each server, with the filter chain match matching on the server specific TLS certs and SNI headers. -// We have two options here: either have all filter chains use the same RDS route name (e.g. "443") and expose -// all virtual hosts on that port to every filter chain uniformly or expose only the set of virtual hosts -// configured under the server for those certificates. We adopt the latter approach. In other words, each -// filter chain in the multi-filter-chain listener will have a distinct RDS route name -// (https....) so that when a RDS request comes in, we serve the virtual -// hosts and associated routes for that server. -// -// Note that the common case is one where multiple servers are exposed under a single multi-SAN cert on a single port. -// In this case, we have a single https.... RDS for the HTTPS server. -// While we can use the same RDS route name for two servers (say HTTP and HTTPS) exposing the same set of hosts on -// different ports, the optimization (one RDS instead of two) could quickly become useless the moment the set of -// hosts on the two servers start differing -- necessitating the need for two different RDS routes. -func gatewayRDSRouteName(server *networking.Server, portNumber uint32, cfg config.Config) string { - p := protocol.Parse(server.Port.Protocol) - bind := "" - if server.Bind != "" { - bind = "." + server.Bind - } - if p.IsHTTP() { - return "http" + "." + strconv.Itoa(int(portNumber)) + bind // Format: http.%d.%s - } - - if p == protocol.HTTPS && server.Tls != nil && !gateway.IsPassThroughServer(server) { - return "https" + "." + strconv.Itoa(int(server.Port.Number)) + "." + - server.Port.Name + "." + cfg.Name + "." + cfg.Namespace + bind // Format: https.%d.%s.%s.%s.%s - } - - return "" -} - -// ParseGatewayRDSRouteName is used by the EnvoyFilter patching logic to match -// a specific route configuration to patch. -func ParseGatewayRDSRouteName(name string) (portNumber int, portName, gatewayName string) { - parts := strings.Split(name, ".") - if strings.HasPrefix(name, "http.") { - // this is a http gateway. Parse port number and return empty string for rest - if len(parts) >= 2 { - portNumber, _ = strconv.Atoi(parts[1]) - } - } else if strings.HasPrefix(name, "https.") { - if len(parts) >= 5 { - portNumber, _ = strconv.Atoi(parts[1]) - portName = parts[2] - // gateway name should be ns/name - gatewayName = parts[4] + "/" + parts[3] - } - } - return -} - -// convert ./host to currentNamespace/Host -// */host to just host -// */* to just * -func sanitizeServerHostNamespace(server *networking.Server, namespace string) { - for i, h := range server.Hosts { - if strings.Contains(h, "/") { - parts := strings.Split(h, "/") - if parts[0] == "." { - server.Hosts[i] = fmt.Sprintf("%s/%s", namespace, parts[1]) - } else if parts[0] == "*" { - if parts[1] == "*" { - server.Hosts = []string{"*"} - return - } - server.Hosts[i] = parts[1] - } - } - } -} - -type GatewayPortMap map[int]map[int]struct{} - -func getTargetPortMap(serversByRouteName map[string][]*networking.Server) GatewayPortMap { - pm := GatewayPortMap{} - for r, s := range serversByRouteName { - portNumber, _, _ := ParseGatewayRDSRouteName(r) - if _, f := pm[portNumber]; !f { - pm[portNumber] = map[int]struct{}{} - } - for _, se := range s { - if se.Port == nil { - continue - } - pm[portNumber][int(se.Port.Number)] = struct{}{} - } - } - return pm -} diff --git a/pilot/pkg/model/gateway_test.go b/pilot/pkg/model/gateway_test.go deleted file mode 100644 index 7c5535d96..000000000 --- a/pilot/pkg/model/gateway_test.go +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "fmt" - "testing" -) - -import ( - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" -) - -// nolint lll -func TestMergeGateways(t *testing.T) { - gwHTTPFoo := makeConfig("foo1", "not-default", "foo.bar.com", "name1", "http", 7, "ingressgateway", "", networking.ServerTLSSettings_SIMPLE) - gwHTTPbar := makeConfig("bar1", "not-default", "bar.foo.com", "bname1", "http", 7, "ingressgateway", "", networking.ServerTLSSettings_SIMPLE) - gwHTTPlocalbar := makeConfig("lcoalbar1", "not-default", "localbar.foo.com", "bname1", "http", 7, "ingressgateway", "127.0.0.1", networking.ServerTLSSettings_SIMPLE) - gwHTTP2Wildcard := makeConfig("foo5", "not-default", "*", "name5", "http2", 8, "ingressgateway", "", networking.ServerTLSSettings_SIMPLE) - gwHTTPWildcard := makeConfig("foo3", "not-default", "*", "name3", "http", 8, "ingressgateway", "", networking.ServerTLSSettings_SIMPLE) - gwTCPWildcard := makeConfig("foo4", "not-default-2", "*", "name4", "tcp", 8, "ingressgateway", "", networking.ServerTLSSettings_SIMPLE) - - gwHTTPWildcardAlternate := makeConfig("foo2", "not-default", "*", "name2", "http", 7, "ingressgateway2", "", networking.ServerTLSSettings_SIMPLE) - - gwSimple := makeConfig("foo-simple", "not-default-2", "*.example.com", "https", "HTTPS", 443, "ingressgateway", "", networking.ServerTLSSettings_SIMPLE) - gwPassthrough := makeConfig("foo-passthrough", "not-default-2", "foo.example.com", "tls-foo", "TLS", 443, "ingressgateway", "", networking.ServerTLSSettings_PASSTHROUGH) - - // TODO(ramaraochavali): Add more test cases here. - tests := []struct { - name string - gwConfig []config.Config - mergedServersNum int - serverNum int - serversForRouteNum map[string]int - gatewaysNum int - }{ - { - "single-server-config", - []config.Config{gwHTTPFoo}, - 1, - 1, - map[string]int{"http.7": 1}, - 1, - }, - { - "two servers on the same port", - []config.Config{gwHTTPFoo, gwHTTPbar}, - 1, - 2, - map[string]int{"http.7": 2}, - 2, - }, - { - "two servers on the same port with different bind", - []config.Config{gwHTTPbar, gwHTTPlocalbar}, - 2, - 2, - map[string]int{"http.7": 1, "http.7.127.0.0.1": 1}, - 2, - }, - { - "same-server-config", - []config.Config{gwHTTPFoo, gwHTTPWildcardAlternate}, - 1, - 2, - map[string]int{"http.7": 2}, - 2, - }, - { - "multi-server-config", - []config.Config{gwHTTPFoo, gwHTTPWildcardAlternate, gwHTTPWildcard}, - 2, - 3, - map[string]int{"http.7": 2, "http.8": 1}, - 3, - }, - { - "http-tcp-wildcard-server-config", - []config.Config{gwHTTPFoo, gwTCPWildcard}, - 2, - 2, - map[string]int{"http.7": 1}, - 2, - }, - { - "tcp-http-server-config", - []config.Config{gwTCPWildcard, gwHTTPWildcard}, - 1, - 1, - map[string]int{}, - 2, - }, - { - "tcp-tcp-server-config", - []config.Config{gwHTTPWildcard, gwTCPWildcard}, // order matters - 1, - 1, - map[string]int{"http.8": 1}, - 2, - }, - { - "http-http2-server-config", - []config.Config{gwHTTPWildcard, gwHTTP2Wildcard}, - 1, - 1, - // http and http2 both present - map[string]int{"http.8": 1}, - 2, - }, - { - "simple-passthrough", - []config.Config{gwSimple, gwPassthrough}, - 2, - 2, - map[string]int{"https.443.https.foo-simple.not-default-2": 1}, - 2, - }, - } - - for idx, tt := range tests { - t.Run(fmt.Sprintf("[%d] %s", idx, tt.name), func(t *testing.T) { - instances := []gatewayWithInstances{} - for _, c := range tt.gwConfig { - instances = append(instances, gatewayWithInstances{c, true, nil}) - } - mgw := MergeGateways(instances, &Proxy{}, nil) - if len(mgw.MergedServers) != tt.mergedServersNum { - t.Errorf("Incorrect number of merged servers. Expected: %v Got: %d", tt.mergedServersNum, len(mgw.MergedServers)) - } - if len(mgw.ServersByRouteName) != len(tt.serversForRouteNum) { - t.Errorf("Incorrect number of routes. Expected: %v Got: %d", len(tt.serversForRouteNum), len(mgw.ServersByRouteName)) - } - for k, v := range mgw.ServersByRouteName { - if tt.serversForRouteNum[k] != len(v) { - t.Errorf("for route %v expected %v servers got %v", k, tt.serversForRouteNum[k], len(v)) - } - } - ns := 0 - for _, ms := range mgw.MergedServers { - ns += len(ms.Servers) - } - if ns != tt.serverNum { - t.Errorf("Incorrect number of total servers. Expected: %v Got: %d", tt.serverNum, ns) - } - if len(mgw.GatewayNameForServer) != tt.gatewaysNum { - t.Errorf("Incorrect number of gateways. Expected: %v Got: %d", tt.gatewaysNum, len(mgw.GatewayNameForServer)) - } - }) - } -} - -func makeConfig(name, namespace, host, portName, portProtocol string, portNumber uint32, gw string, bind string, - mode networking.ServerTLSSettings_TLSmode) config.Config { - c := config.Config{ - Meta: config.Meta{ - Name: name, - Namespace: namespace, - }, - Spec: &networking.Gateway{ - Selector: map[string]string{"istio": gw}, - Servers: []*networking.Server{ - { - Hosts: []string{host}, - Port: &networking.Port{Name: portName, Number: portNumber, Protocol: portProtocol}, - Bind: bind, - Tls: &networking.ServerTLSSettings{Mode: mode}, - }, - }, - }, - } - return c -} - -func TestParseGatewayRDSRouteName(t *testing.T) { - type args struct { - name string - } - tests := []struct { - name string - args args - wantPortNumber int - wantPortName string - wantGateway string - }{ - { - name: "invalid rds name", - args: args{"https.scooby.dooby.doo"}, - wantPortNumber: 0, - wantPortName: "", - wantGateway: "", - }, - { - name: "gateway http rds name", - args: args{"http.80"}, - wantPortNumber: 80, - wantPortName: "", - wantGateway: "", - }, - { - name: "https rds name", - args: args{"https.443.app1.gw1.ns1"}, - wantPortNumber: 443, - wantPortName: "app1", - wantGateway: "ns1/gw1", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotPortNumber, gotPortName, gotGateway := ParseGatewayRDSRouteName(tt.args.name) - if gotPortNumber != tt.wantPortNumber { - t.Errorf("ParseGatewayRDSRouteName() gotPortNumber = %v, want %v", gotPortNumber, tt.wantPortNumber) - } - if gotPortName != tt.wantPortName { - t.Errorf("ParseGatewayRDSRouteName() gotPortName = %v, want %v", gotPortName, tt.wantPortName) - } - if gotGateway != tt.wantGateway { - t.Errorf("ParseGatewayRDSRouteName() gotGateway = %v, want %v", gotGateway, tt.wantGateway) - } - }) - } -} diff --git a/pilot/pkg/model/jwks_resolver.go b/pilot/pkg/model/jwks_resolver.go deleted file mode 100644 index 5ee533e7e..000000000 --- a/pilot/pkg/model/jwks_resolver.go +++ /dev/null @@ -1,568 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "crypto/tls" - "crypto/x509" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "net/url" - "os" - "reflect" - "sort" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - envoy_jwt "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/jwt_authn/v3" - "istio.io/pkg/monitoring" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" -) - -const ( - // https://openid.net/specs/openid-connect-discovery-1_0.html - // OpenID Providers supporting Discovery MUST make a JSON document available at the path - // formed by concatenating the string /.well-known/openid-configuration to the Issuer. - openIDDiscoveryCfgURLSuffix = "/.well-known/openid-configuration" - - // OpenID Discovery web request timeout. - jwksHTTPTimeOutInSec = 5 - - // JwtPubKeyEvictionDuration is the life duration for cached item. - // Cached item will be removed from the cache if it hasn't been used longer than JwtPubKeyEvictionDuration or if pilot - // has failed to refresh it for more than JwtPubKeyEvictionDuration. - JwtPubKeyEvictionDuration = 24 * 7 * time.Hour - - // JwtPubKeyRefreshIntervalOnFailure is the running interval of JWT pubKey refresh job on failure. - JwtPubKeyRefreshIntervalOnFailure = time.Minute - - // JwtPubKeyRetryInterval is the retry interval between the attempt to retry getting the remote - // content from network. - JwtPubKeyRetryInterval = time.Second - - // JwtPubKeyRefreshIntervalOnFailureResetThreshold is the threshold to reset the refresh interval on failure. - JwtPubKeyRefreshIntervalOnFailureResetThreshold = 60 * time.Minute - - // How many times should we retry the failed network fetch on main flow. The main flow - // means it's called when Pilot is pushing configs. Do not retry to make sure not to block Pilot - // too long. - networkFetchRetryCountOnMainFlow = 0 - - // How many times should we retry the failed network fetch on refresh flow. The refresh flow - // means it's called when the periodically refresh job is triggered. We can retry more aggressively - // as it's running separately from the main flow. - networkFetchRetryCountOnRefreshFlow = 3 - - // jwksExtraRootCABundlePath is the path to any additional CA certificates pilot should accept when resolving JWKS URIs - jwksExtraRootCABundlePath = "/cacerts/extra.pem" -) - -var ( - // Close channel - closeChan = make(chan bool) - - networkFetchSuccessCounter = monitoring.NewSum( - "pilot_jwks_resolver_network_fetch_success_total", - "Total number of successfully network fetch by pilot jwks resolver", - ) - networkFetchFailCounter = monitoring.NewSum( - "pilot_jwks_resolver_network_fetch_fail_total", - "Total number of failed network fetch by pilot jwks resolver", - ) - - // JwtPubKeyRefreshInterval is the running interval of JWT pubKey refresh job. - JwtPubKeyRefreshInterval = features.PilotJwtPubKeyRefreshInterval -) - -// jwtPubKeyEntry is a single cached entry for jwt public key. -type jwtPubKeyEntry struct { - pubKey string - - // The last success refreshed time of the pubKey. - lastRefreshedTime time.Time - - // Cached item's last used time, which is set in GetPublicKey. - lastUsedTime time.Time -} - -// jwtKey is a key in the JwksResolver keyEntries map. -type jwtKey struct { - jwksURI string - issuer string -} - -// JwksResolver is resolver for jwksURI and jwt public key. -type JwksResolver struct { - // Callback function to invoke when detecting jwt public key change. - PushFunc func() - - // cache for JWT public key. - // map key is jwtKey, map value is jwtPubKeyEntry. - keyEntries sync.Map - - secureHTTPClient *http.Client - httpClient *http.Client - refreshTicker *time.Ticker - - // Cached key will be removed from cache if (time.now - cachedItem.lastUsedTime >= evictionDuration), this prevents key cache growing indefinitely. - evictionDuration time.Duration - - // Refresher job running interval. - refreshInterval time.Duration - - // Refresher job running interval on failure. - refreshIntervalOnFailure time.Duration - - // Refresher job default running interval without failure. - refreshDefaultInterval time.Duration - - retryInterval time.Duration - - // How many times refresh job has detected JWT public key change happened, used in unit test. - refreshJobKeyChangedCount uint64 - - // How many times refresh job failed to fetch the public key from network, used in unit test. - refreshJobFetchFailedCount uint64 -} - -func init() { - monitoring.MustRegister(networkFetchSuccessCounter, networkFetchFailCounter) -} - -// NewJwksResolver creates new instance of JwksResolver. -func NewJwksResolver(evictionDuration, refreshDefaultInterval, refreshIntervalOnFailure, retryInterval time.Duration) *JwksResolver { - return newJwksResolverWithCABundlePaths( - evictionDuration, - refreshDefaultInterval, - refreshIntervalOnFailure, - retryInterval, - []string{jwksExtraRootCABundlePath}, - ) -} - -func newJwksResolverWithCABundlePaths( - evictionDuration, - refreshDefaultInterval, - refreshIntervalOnFailure, - retryInterval time.Duration, - caBundlePaths []string, -) *JwksResolver { - ret := &JwksResolver{ - evictionDuration: evictionDuration, - refreshInterval: refreshDefaultInterval, - refreshDefaultInterval: refreshDefaultInterval, - refreshIntervalOnFailure: refreshIntervalOnFailure, - retryInterval: retryInterval, - httpClient: &http.Client{ - Timeout: jwksHTTPTimeOutInSec * time.Second, - Transport: &http.Transport{ - Proxy: http.ProxyFromEnvironment, - DisableKeepAlives: true, - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }, - }, - } - - caCertPool, err := x509.SystemCertPool() - caCertsFound := true - if err != nil { - caCertsFound = false - log.Errorf("Failed to fetch Cert from SystemCertPool: %v", err) - } - - if caCertPool != nil { - for _, pemFile := range caBundlePaths { - caCert, err := os.ReadFile(pemFile) - if err == nil { - caCertsFound = caCertPool.AppendCertsFromPEM(caCert) || caCertsFound - } - } - } - - if caCertsFound { - ret.secureHTTPClient = &http.Client{ - Timeout: jwksHTTPTimeOutInSec * time.Second, - Transport: &http.Transport{ - Proxy: http.ProxyFromEnvironment, - DisableKeepAlives: true, - TLSClientConfig: &tls.Config{ - RootCAs: caCertPool, - }, - }, - } - } - - atomic.StoreUint64(&ret.refreshJobKeyChangedCount, 0) - atomic.StoreUint64(&ret.refreshJobFetchFailedCount, 0) - go ret.refresher() - - return ret -} - -var errEmptyPubKeyFoundInCache = errors.New("empty public key found in cache") - -// GetPublicKey gets JWT public key and cache the key for future use. -func (r *JwksResolver) GetPublicKey(issuer string, jwksURI string) (string, error) { - now := time.Now() - key := jwtKey{issuer: issuer, jwksURI: jwksURI} - if val, found := r.keyEntries.Load(key); found { - e := val.(jwtPubKeyEntry) - // Update cached key's last used time. - e.lastUsedTime = now - r.keyEntries.Store(key, e) - if e.pubKey == "" { - return e.pubKey, errEmptyPubKeyFoundInCache - } - return e.pubKey, nil - } - - var err error - var pubKey string - if jwksURI == "" { - // Fetch the jwks URI if it is not hardcoded on config. - jwksURI, err = r.resolveJwksURIUsingOpenID(issuer) - } - if err != nil { - log.Errorf("Failed to jwks URI from %q: %v", issuer, err) - } else { - var resp []byte - resp, err = r.getRemoteContentWithRetry(jwksURI, networkFetchRetryCountOnMainFlow) - if err != nil { - log.Errorf("Failed to fetch public key from %q: %v", jwksURI, err) - } - pubKey = string(resp) - } - - r.keyEntries.Store(key, jwtPubKeyEntry{ - pubKey: pubKey, - lastRefreshedTime: now, - lastUsedTime: now, - }) - - return pubKey, err -} - -// BuildLocalJwks builds local Jwks by fetching the Jwt Public Key from the URL passed if it is empty. -func (r *JwksResolver) BuildLocalJwks(jwksURI, jwtIssuer, jwtPubKey string) *envoy_jwt.JwtProvider_LocalJwks { - if jwtPubKey == "" { - var err error - - // jwtKeyResolver should never be nil since the function is only called in Discovery Server request processing - // workflow, where the JWT key resolver should have already been initialized on server creation. - jwtPubKey, err = r.GetPublicKey(jwtIssuer, jwksURI) - if err != nil { - log.Errorf("Failed to fetch jwt public key from issuer %q, jwks uri %q: %s", jwtIssuer, jwksURI, err) - // This is a temporary workaround to reject a request with JWT token by using a fake jwks when istiod failed to fetch it. - // TODO(xulingqing): Find a better way to reject the request without using the fake jwks. - jwtPubKey = CreateFakeJwks(jwksURI) - } - } - return &envoy_jwt.JwtProvider_LocalJwks{ - LocalJwks: &core.DataSource{ - Specifier: &core.DataSource_InlineString{ - InlineString: jwtPubKey, - }, - }, - } -} - -// CreateFakeJwks is a helper function to make a fake jwks when istiod failed to fetch it. -func CreateFakeJwks(jwksURI string) string { - // Encode jwksURI with base64 to make dynamic n in jwks - encodedString := base64.RawURLEncoding.EncodeToString([]byte(jwksURI)) - return fmt.Sprintf(`{"keys":[ {"e":"AQAB","kid":"abc","kty":"RSA","n":"Error-IstiodFailedToFetchJwksUri-%s"}]}`, encodedString) -} - -// Resolve jwks_uri through openID discovery. -func (r *JwksResolver) resolveJwksURIUsingOpenID(issuer string) (string, error) { - // Try to get jwks_uri through OpenID Discovery. - body, err := r.getRemoteContentWithRetry(issuer+openIDDiscoveryCfgURLSuffix, networkFetchRetryCountOnMainFlow) - if err != nil { - log.Errorf("Failed to fetch jwks_uri from %q: %v", issuer+openIDDiscoveryCfgURLSuffix, err) - return "", err - } - var data map[string]interface{} - if err := json.Unmarshal(body, &data); err != nil { - return "", err - } - - jwksURI, ok := data["jwks_uri"].(string) - if !ok { - return "", fmt.Errorf("invalid jwks_uri %v in openID discovery configuration", data["jwks_uri"]) - } - - return jwksURI, nil -} - -func (r *JwksResolver) getRemoteContentWithRetry(uri string, retry int) ([]byte, error) { - u, err := url.Parse(uri) - if err != nil { - log.Errorf("Failed to parse %q", uri) - return nil, err - } - - client := r.httpClient - if strings.EqualFold(u.Scheme, "https") { - // https client may be uninitialized because of root CA bundle missing. - if r.secureHTTPClient == nil { - return nil, fmt.Errorf("pilot does not support fetch public key through https endpoint %q", uri) - } - - client = r.secureHTTPClient - } - - getPublicKey := func() (b []byte, e error) { - defer func() { - if e != nil { - networkFetchFailCounter.Increment() - } else { - networkFetchSuccessCounter.Increment() - } - }() - resp, err := client.Get(uri) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - if resp.StatusCode < 200 || resp.StatusCode >= 300 { - message := strconv.Quote(string(body)) - if len(message) > 100 { - message = message[:100] - return nil, fmt.Errorf("status %d, message %s(truncated)", resp.StatusCode, message) - } - return nil, fmt.Errorf("status %d, message %s", resp.StatusCode, message) - } - - return body, nil - } - - for i := 0; i < retry; i++ { - body, err := getPublicKey() - if err == nil { - return body, nil - } - log.Warnf("Failed to GET from %q: %s. Retry in %v", uri, err, r.retryInterval) - time.Sleep(r.retryInterval) - } - - // Return the last fetch directly, reaching here means we have tried `retry` times, this will be - // the last time for the retry. - return getPublicKey() -} - -func (r *JwksResolver) refresher() { - // Wake up once in a while and refresh stale items. - r.refreshTicker = time.NewTicker(r.refreshInterval) - lastHasError := false - for { - select { - case <-r.refreshTicker.C: - currentHasError := r.refresh() - if currentHasError { - if lastHasError { - // update to exponential backoff if last time also failed. - r.refreshInterval *= 2 - if r.refreshInterval > JwtPubKeyRefreshIntervalOnFailureResetThreshold { - r.refreshInterval = JwtPubKeyRefreshIntervalOnFailureResetThreshold - } - } else { - // change to the refreshIntervalOnFailure if failed for the first time. - r.refreshInterval = r.refreshIntervalOnFailure - } - } else { - // reset the refresh interval if success. - r.refreshInterval = r.refreshDefaultInterval - } - lastHasError = currentHasError - r.refreshTicker.Reset(r.refreshInterval) - case <-closeChan: - r.refreshTicker.Stop() - return - } - } -} - -func (r *JwksResolver) refresh() bool { - var wg sync.WaitGroup - hasChange := false - hasErrors := false - - r.keyEntries.Range(func(key interface{}, value interface{}) bool { - now := time.Now() - k := key.(jwtKey) - e := value.(jwtPubKeyEntry) - - // Remove cached item for either of the following 2 situations - // 1) it hasn't been used for a while - // 2) it hasn't been refreshed successfully for a while - // This makes sure 2 things, we don't grow the cache infinitely and also we don't reuse a cached public key - // with no success refresh for too much time. - if now.Sub(e.lastUsedTime) >= r.evictionDuration || now.Sub(e.lastRefreshedTime) >= r.evictionDuration { - log.Infof("Removed cached JWT public key (lastRefreshed: %s, lastUsed: %s) from %q", - e.lastRefreshedTime, e.lastUsedTime, k.issuer) - r.keyEntries.Delete(k) - return true - } - - oldPubKey := e.pubKey - - // Increment the WaitGroup counter. - wg.Add(1) - - go func() { - // Decrement the counter when the goroutine completes. - defer wg.Done() - jwksURI := k.jwksURI - if jwksURI == "" { - var err error - jwksURI, err = r.resolveJwksURIUsingOpenID(k.issuer) - if err != nil { - hasErrors = true - log.Errorf("Failed to resolve Jwks from issuer %q: %v", k.issuer, err) - atomic.AddUint64(&r.refreshJobFetchFailedCount, 1) - return - } - } - - resp, err := r.getRemoteContentWithRetry(jwksURI, networkFetchRetryCountOnRefreshFlow) - if err != nil { - hasErrors = true - log.Errorf("Failed to refresh JWT public key from %q: %v", jwksURI, err) - atomic.AddUint64(&r.refreshJobFetchFailedCount, 1) - return - } - newPubKey := string(resp) - r.keyEntries.Store(k, jwtPubKeyEntry{ - pubKey: newPubKey, - lastRefreshedTime: now, // update the lastRefreshedTime if we get a success response from the network. - lastUsedTime: e.lastUsedTime, // keep original lastUsedTime. - }) - isNewKey, err := compareJWKSResponse(oldPubKey, newPubKey) - if err != nil { - hasErrors = true - log.Errorf("Failed to refresh JWT public key from %q: %v", jwksURI, err) - return - } - if isNewKey { - hasChange = true - log.Infof("Updated cached JWT public key from %q", jwksURI) - } - }() - - return true - }) - - // Wait for all go routine to complete. - wg.Wait() - - if hasChange { - atomic.AddUint64(&r.refreshJobKeyChangedCount, 1) - // Push public key changes to sidecars. - if r.PushFunc != nil { - r.PushFunc() - } - } - return hasErrors -} - -// Close will shut down the refresher job. -// TODO: may need to figure out the right place to call this function. -// (right now calls it from initDiscoveryService in pkg/bootstrap/server.go). -func (r *JwksResolver) Close() { - closeChan <- true -} - -// Compare two JWKS responses, returning true if there is a difference and false otherwise -func compareJWKSResponse(oldKeyString string, newKeyString string) (bool, error) { - if oldKeyString == newKeyString { - return false, nil - } - - var oldJWKs map[string]interface{} - var newJWKs map[string]interface{} - if err := json.Unmarshal([]byte(newKeyString), &newJWKs); err != nil { - // If the new key is not parseable as JSON return an error since we will not want to use this key - log.Warnf("New JWKs public key JSON is not parseable: %s", newKeyString) - return false, err - } - if err := json.Unmarshal([]byte(oldKeyString), &oldJWKs); err != nil { - log.Warnf("Previous JWKs public key JSON is not parseable: %s", oldKeyString) - return true, nil - } - - // Sort both sets of keys by "kid (key ID)" to be able to directly compare - oldKeys, oldKeysExists := oldJWKs["keys"].([]interface{}) - newKeys, newKeysExists := newJWKs["keys"].([]interface{}) - if oldKeysExists && newKeysExists { - sort.Slice(oldKeys, func(i, j int) bool { - key1, ok1 := oldKeys[i].(map[string]interface{}) - key2, ok2 := oldKeys[j].(map[string]interface{}) - if ok1 && ok2 { - key1Id, kid1Exists := key1["kid"] - key2Id, kid2Exists := key2["kid"] - if kid1Exists && kid2Exists { - key1IdStr, ok1 := key1Id.(string) - key2IdStr, ok2 := key2Id.(string) - if ok1 && ok2 { - return key1IdStr < key2IdStr - } - } - } - return len(key1) < len(key2) - }) - sort.Slice(newKeys, func(i, j int) bool { - key1, ok1 := newKeys[i].(map[string]interface{}) - key2, ok2 := newKeys[j].(map[string]interface{}) - if ok1 && ok2 { - key1Id, kid1Exists := key1["kid"] - key2Id, kid2Exists := key2["kid"] - if kid1Exists && kid2Exists { - key1IdStr, ok1 := key1Id.(string) - key2IdStr, ok2 := key2Id.(string) - if ok1 && ok2 { - return key1IdStr < key2IdStr - } - } - } - return len(key1) < len(key2) - }) - - // Once sorted, return the result of deep comparison of the arrays of keys - return !reflect.DeepEqual(oldKeys, newKeys), nil - } - - // If we aren't able to compare using keys, we should return true - // since we already checked exact equality of the responses - return true, nil -} diff --git a/pilot/pkg/model/jwks_resolver_test.go b/pilot/pkg/model/jwks_resolver_test.go deleted file mode 100644 index 268ce7395..000000000 --- a/pilot/pkg/model/jwks_resolver_test.go +++ /dev/null @@ -1,591 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "fmt" - "sync/atomic" - "testing" - "time" -) - -import ( - "go.opencensus.io/stats/view" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -const testRetryInterval = time.Millisecond * 10 - -func TestResolveJwksURIUsingOpenID(t *testing.T) { - r := NewJwksResolver(JwtPubKeyEvictionDuration, JwtPubKeyRefreshInterval, JwtPubKeyRefreshIntervalOnFailure, testRetryInterval) - defer r.Close() - - ms, err := test.StartNewServer() - defer ms.Stop() - if err != nil { - t.Fatal("failed to start a mock server") - } - - mockCertURL := ms.URL + "/oauth2/v3/certs" - cases := []struct { - in string - expectedJwksURI string - expectedError bool - }{ - { - in: ms.URL, - expectedJwksURI: mockCertURL, - }, - { - in: ms.URL, // Send two same request, mock server is expected to hit twice. - expectedJwksURI: mockCertURL, - }, - { - in: "http://xyz", - expectedError: true, - }, - } - for _, c := range cases { - jwksURI, err := r.resolveJwksURIUsingOpenID(c.in) - if err != nil && !c.expectedError { - t.Errorf("resolveJwksURIUsingOpenID(%+v): got error (%v)", c.in, err) - } else if err == nil && c.expectedError { - t.Errorf("resolveJwksURIUsingOpenID(%+v): expected error, got no error", c.in) - } else if c.expectedJwksURI != jwksURI { - t.Errorf("resolveJwksURIUsingOpenID(%+v): expected (%s), got (%s)", - c.in, c.expectedJwksURI, jwksURI) - } - } - - // Verify mock openID discovery http://localhost:9999/.well-known/openid-configuration was called twice. - if got, want := ms.OpenIDHitNum, uint64(2); got != want { - t.Errorf("Mock OpenID discovery Hit number => expected %d but got %d", want, got) - } -} - -func TestGetPublicKey(t *testing.T) { - r := NewJwksResolver(JwtPubKeyEvictionDuration, JwtPubKeyRefreshInterval, JwtPubKeyRefreshIntervalOnFailure, testRetryInterval) - defer r.Close() - - ms, err := test.StartNewServer() - defer ms.Stop() - if err != nil { - t.Fatal("failed to start a mock server") - } - - mockCertURL := ms.URL + "/oauth2/v3/certs" - - cases := []struct { - in []string - expectedJwtPubkey string - }{ - { - in: []string{"testIssuer", mockCertURL}, - expectedJwtPubkey: test.JwtPubKey1, - }, - { - in: []string{"testIssuer", mockCertURL}, // Send two same request, mock server is expected to hit only once because of the cache. - expectedJwtPubkey: test.JwtPubKey1, - }, - } - for _, c := range cases { - pk, err := r.GetPublicKey(c.in[0], c.in[1]) - if err != nil { - t.Errorf("GetPublicKey(\"\", %+v) fails: expected no error, got (%v)", c.in, err) - } - if c.expectedJwtPubkey != pk { - t.Errorf("GetPublicKey(\"\", %+v): expected (%s), got (%s)", c.in, c.expectedJwtPubkey, pk) - } - } - - // Verify mock server http://localhost:9999/oauth2/v3/certs was only called once because of the cache. - if got, want := ms.PubKeyHitNum, uint64(1); got != want { - t.Errorf("Mock server Hit number => expected %d but got %d", want, got) - } -} - -func TestGetPublicKeyReorderedKey(t *testing.T) { - r := NewJwksResolver(JwtPubKeyEvictionDuration, testRetryInterval*20, testRetryInterval*10, testRetryInterval) - defer r.Close() - - ms, err := test.StartNewServer() - defer ms.Stop() - if err != nil { - t.Fatal("failed to start a mock server") - } - ms.ReturnReorderedKeyAfterFirstNumHits = 1 - - mockCertURL := ms.URL + "/oauth2/v3/certs" - - cases := []struct { - in []string - expectedJwtPubkey string - }{ - { - in: []string{"", mockCertURL}, - expectedJwtPubkey: test.JwtPubKey1, - }, - { - in: []string{"", mockCertURL}, // Send two same request, mock server is expected to hit only once because of the cache. - expectedJwtPubkey: test.JwtPubKey1Reordered, - }, - } - for _, c := range cases { - pk, err := r.GetPublicKey(c.in[0], c.in[1]) - if err != nil { - t.Errorf("GetPublicKey(\"\", %+v) fails: expected no error, got (%v)", c.in, err) - } - if c.expectedJwtPubkey != pk { - t.Errorf("GetPublicKey(\"\", %+v): expected (%s), got (%s)", c.in, c.expectedJwtPubkey, pk) - } - r.refresh() - } - - // Verify refresh job key changed count is zero. - if got, want := r.refreshJobKeyChangedCount, uint64(0); got != want { - t.Errorf("JWKs Resolver Refreshed Key Count => expected %d but got %d", want, got) - } -} - -func TestGetPublicKeyUsingTLS(t *testing.T) { - r := newJwksResolverWithCABundlePaths( - JwtPubKeyEvictionDuration, - JwtPubKeyRefreshInterval, - JwtPubKeyRefreshIntervalOnFailure, - testRetryInterval, - []string{"./test/testcert/cert.pem"}, - ) - defer r.Close() - - ms, err := test.StartNewTLSServer("./test/testcert/cert.pem", "./test/testcert/key.pem") - defer ms.Stop() - if err != nil { - t.Fatal("failed to start a mock server") - } - - mockCertURL := ms.URL + "/oauth2/v3/certs" - pk, err := r.GetPublicKey("", mockCertURL) - if err != nil { - t.Errorf("GetPublicKey(\"\", %+v) fails: expected no error, got (%v)", mockCertURL, err) - } - if test.JwtPubKey1 != pk { - t.Errorf("GetPublicKey(\"\", %+v): expected (%s), got (%s)", mockCertURL, test.JwtPubKey1, pk) - } -} - -func TestGetPublicKeyUsingTLSBadCert(t *testing.T) { - r := newJwksResolverWithCABundlePaths( - JwtPubKeyEvictionDuration, - JwtPubKeyRefreshInterval, - testRetryInterval, - JwtPubKeyRefreshIntervalOnFailure, - []string{"./test/testcert/cert2.pem"}, - ) - defer r.Close() - - ms, err := test.StartNewTLSServer("./test/testcert/cert.pem", "./test/testcert/key.pem") - defer ms.Stop() - if err != nil { - t.Fatal("failed to start a mock server") - } - - mockCertURL := ms.URL + "/oauth2/v3/certs" - _, err = r.GetPublicKey("", mockCertURL) - if err == nil { - t.Errorf("GetPublicKey(\"\", %+v) did not fail: expected bad certificate error, got no error", mockCertURL) - } -} - -func TestGetPublicKeyUsingTLSWithoutCABundles(t *testing.T) { - r := newJwksResolverWithCABundlePaths( - JwtPubKeyEvictionDuration, - JwtPubKeyRefreshInterval, - testRetryInterval, - JwtPubKeyRefreshIntervalOnFailure, - []string{}, - ) - defer r.Close() - - ms, err := test.StartNewTLSServer("./test/testcert/cert.pem", "./test/testcert/key.pem") - defer ms.Stop() - if err != nil { - t.Fatal("failed to start a mock server") - } - - mockCertURL := ms.URL + "/oauth2/v3/certs" - _, err = r.GetPublicKey("", mockCertURL) - if err == nil { - t.Errorf("GetPublicKey(\"\", %+v) did not fail: expected https unsupported error, got no error", mockCertURL) - } -} - -func TestJwtPubKeyEvictionForNotUsed(t *testing.T) { - r := NewJwksResolver( - 100*time.Millisecond, /*EvictionDuration*/ - 2*time.Millisecond, /*RefreshInterval*/ - 2*time.Millisecond, /*RefreshIntervalOnFailure*/ - testRetryInterval, - ) - defer r.Close() - - ms := startMockServer(t) - defer ms.Stop() - - // Mock server returns JwtPubKey2 for later calls. - // Verify the refresher has run and got new key from mock server. - verifyKeyRefresh(t, r, ms, test.JwtPubKey2) - - // Wait until unused keys are evicted. - key := jwtKey{jwksURI: ms.URL + "/oauth2/v3/certs", issuer: "istio-test"} - - retry.UntilSuccessOrFail(t, func() error { - // Verify the public key is evicted. - if _, found := r.keyEntries.Load(key); found { - return fmt.Errorf("public key is not evicted") - } - return nil - }) -} - -func TestJwtPubKeyEvictionForNotRefreshed(t *testing.T) { - r := NewJwksResolver( - 100*time.Millisecond, /*EvictionDuration*/ - 10*time.Millisecond, /*RefreshInterval*/ - 10*time.Millisecond, /*RefreshIntervalOnFailure*/ - testRetryInterval, /*RetryInterval*/ - ) - defer r.Close() - - ms := startMockServer(t) - defer ms.Stop() - - // Configures the mock server to return error after the first request. - ms.ReturnErrorAfterFirstNumHits = 1 - - mockCertURL := ms.URL + "/oauth2/v3/certs" - - pk, err := r.GetPublicKey("", mockCertURL) - if err != nil { - t.Fatalf("GetPublicKey(\"\", %+v) fails: expected no error, got (%v)", mockCertURL, err) - } - // Mock server returns JwtPubKey1 for first call. - if test.JwtPubKey1 != pk { - t.Fatalf("GetPublicKey(\"\", %+v): expected (%s), got (%s)", mockCertURL, test.JwtPubKey1, pk) - } - - // Keep getting the public key to change the lastUsedTime of the public key. - done := make(chan struct{}) - go func() { - c := time.NewTicker(10 * time.Millisecond) - for { - select { - case <-done: - return - case <-c.C: - _, _ = r.GetPublicKey(mockCertURL, "") - } - } - }() - defer func() { - done <- struct{}{} - }() - - // Verify the cached public key is removed after failed to refresh longer than the eviction duration. - retry.UntilSuccessOrFail(t, func() error { - _, err = r.GetPublicKey(mockCertURL, "") - if err == nil { - return fmt.Errorf("getPublicKey(\"\", %+v) fails: expected error, got no error", mockCertURL) - } - return nil - }) -} - -func TestJwtPubKeyLastRefreshedTime(t *testing.T) { - r := NewJwksResolver( - JwtPubKeyEvictionDuration, - 2*time.Millisecond, /*RefreshInterval*/ - 2*time.Millisecond, /*RefreshIntervalOnFailure*/ - testRetryInterval, /*RetryInterval*/ - ) - defer r.Close() - - ms := startMockServer(t) - defer ms.Stop() - - // Mock server returns JwtPubKey2 for later calls. - // Verify the refresher has run and got new key from mock server. - verifyKeyRefresh(t, r, ms, test.JwtPubKey2) - - // The lastRefreshedTime should change for each successful refresh. - verifyKeyLastRefreshedTime(t, r, ms, true /* wantChanged */) -} - -func TestJwtPubKeyRefreshWithNetworkError(t *testing.T) { - r := NewJwksResolver( - JwtPubKeyEvictionDuration, - time.Second, /*RefreshInterval*/ - time.Second, /*RefreshIntervalOnFailure*/ - testRetryInterval, - ) - defer r.Close() - - ms := startMockServer(t) - defer ms.Stop() - - // Configures the mock server to return error after the first request. - ms.ReturnErrorAfterFirstNumHits = 1 - - // The refresh job should continue using the previously fetched public key (JwtPubKey1). - verifyKeyRefresh(t, r, ms, test.JwtPubKey1) - - // The lastRefreshedTime should not change the refresh failed due to network error. - verifyKeyLastRefreshedTime(t, r, ms, false /* wantChanged */) -} - -func TestJwtRefreshIntervalRecoverFromInitialFailOnFirstHit(t *testing.T) { - defaultRefreshInterval := 50 * time.Millisecond - refreshIntervalOnFail := 2 * time.Millisecond - r := NewJwksResolver(JwtPubKeyEvictionDuration, defaultRefreshInterval, refreshIntervalOnFail, 1*time.Millisecond) - - ms := startMockServer(t) - defer ms.Stop() - - // Configures the mock server to return error for the first 3 requests. - ms.ReturnErrorForFirstNumHits = 3 - - mockCertURL := ms.URL + "/oauth2/v3/certs" - pk, err := r.GetPublicKey("", mockCertURL) - if err == nil { - t.Fatalf("GetPublicKey(%q, %+v) fails: expected error, got no error: (%v)", pk, mockCertURL, err) - } - - retry.UntilOrFail(t, func() bool { - pk, _ := r.GetPublicKey("", mockCertURL) - return test.JwtPubKey2 == pk - }, retry.Delay(time.Millisecond)) - r.Close() - - i := 0 - r.keyEntries.Range(func(_ interface{}, _ interface{}) bool { - i++ - return true - }) - - expectedEntries := 1 - if i != expectedEntries { - t.Errorf("expected entries in cache: %d , got %d", expectedEntries, i) - } - - if r.refreshInterval != defaultRefreshInterval { - t.Errorf("expected refreshInterval to be refreshDefaultInterval: %v, got %v", defaultRefreshInterval, r.refreshInterval) - } -} - -func TestJwtRefreshIntervalRecoverFromFail(t *testing.T) { - defaultRefreshInterval := 50 * time.Millisecond - refreshIntervalOnFail := 2 * time.Millisecond - r := NewJwksResolver(JwtPubKeyEvictionDuration, defaultRefreshInterval, refreshIntervalOnFail, 1*time.Millisecond) - - ms := startMockServer(t) - defer ms.Stop() - - // Configures the mock server to return error after the first request. - ms.ReturnErrorAfterFirstNumHits = 1 - ms.ReturnSuccessAfterFirstNumHits = 3 - - mockCertURL := ms.URL + "/oauth2/v3/certs" - _, err := r.GetPublicKey("", mockCertURL) - if err != nil { - t.Fatalf("GetPublicKey(%q, %+v) fails: expected no error, got (%v)", "", mockCertURL, err) - } - - retry.UntilOrFail(t, func() bool { - pk, _ := r.GetPublicKey("", mockCertURL) - return test.JwtPubKey1 == pk - }, retry.Delay(time.Millisecond)) - r.Close() - - if r.refreshInterval != defaultRefreshInterval { - t.Errorf("expected defaultRefreshInterval: %v , got %v", defaultRefreshInterval, r.refreshInterval) - } -} - -func getCounterValue(counterName string, t *testing.T) float64 { - counterValue := 0.0 - if data, err := view.RetrieveData(counterName); err == nil { - if len(data) != 0 { - counterValue = data[0].Data.(*view.SumData).Value - } - } else { - t.Fatalf("failed to get value for counter %s: %v", counterName, err) - } - return counterValue -} - -func TestJwtPubKeyMetric(t *testing.T) { - defaultRefreshInterval := 50 * time.Millisecond - refreshIntervalOnFail := 2 * time.Millisecond - r := NewJwksResolver(JwtPubKeyEvictionDuration, defaultRefreshInterval, refreshIntervalOnFail, 1*time.Millisecond) - defer r.Close() - - ms := startMockServer(t) - defer ms.Stop() - - ms.ReturnErrorForFirstNumHits = 1 - - successValueBefore := getCounterValue(networkFetchSuccessCounter.Name(), t) - failValueBefore := getCounterValue(networkFetchFailCounter.Name(), t) - - mockCertURL := ms.URL + "/oauth2/v3/certs" - cases := []struct { - in []string - expectedJwtPubkey string - }{ - { - in: []string{"", mockCertURL}, - expectedJwtPubkey: "", - }, - { - in: []string{"", mockCertURL}, - expectedJwtPubkey: test.JwtPubKey2, - }, - } - - for _, c := range cases { - retry.UntilOrFail(t, func() bool { - pk, _ := r.GetPublicKey(c.in[0], c.in[1]) - return c.expectedJwtPubkey == pk - }, retry.Delay(time.Millisecond)) - } - - successValueAfter := getCounterValue(networkFetchSuccessCounter.Name(), t) - failValueAfter := getCounterValue(networkFetchFailCounter.Name(), t) - if successValueBefore >= successValueAfter { - t.Errorf("the success counter is not incremented") - } - if failValueBefore >= failValueAfter { - t.Errorf("the fail counter is not incremented") - } -} - -func startMockServer(t *testing.T) *test.MockOpenIDDiscoveryServer { - t.Helper() - - ms, err := test.StartNewServer() - if err != nil { - t.Fatal("failed to start a mock server") - } - return ms -} - -func verifyKeyRefresh(t *testing.T, r *JwksResolver, ms *test.MockOpenIDDiscoveryServer, expectedJwtPubkey string) { - t.Helper() - mockCertURL := ms.URL + "/oauth2/v3/certs" - - pk, err := r.GetPublicKey("", mockCertURL) - if err != nil { - t.Fatalf("GetPublicKey(\"\", %+v) fails: expected no error, got (%v)", mockCertURL, err) - } - // Mock server returns JwtPubKey1 for first call. - if test.JwtPubKey1 != pk { - t.Fatalf("GetPublicKey(\"\", %+v): expected (%s), got (%s)", mockCertURL, test.JwtPubKey1, pk) - } - - // Wait until refresh job at least finished once. - retry.UntilSuccessOrFail(t, func() error { - // Make sure refresh job has run and detect change or refresh happened. - if atomic.LoadUint64(&r.refreshJobKeyChangedCount) > 0 || atomic.LoadUint64(&r.refreshJobFetchFailedCount) > 0 { - return nil - } - return fmt.Errorf("refresher failed to run") - }) - pk, err = r.GetPublicKey("", mockCertURL) - if err != nil { - t.Fatalf("GetPublicKey(\"\", %+v) fails: expected no error, got (%v)", mockCertURL, err) - } - if expectedJwtPubkey != pk { - t.Fatalf("GetPublicKey(\"\", %+v): expected (%s), got (%s)", mockCertURL, expectedJwtPubkey, pk) - } -} - -func verifyKeyLastRefreshedTime(t *testing.T, r *JwksResolver, ms *test.MockOpenIDDiscoveryServer, wantChanged bool) { - t.Helper() - mockCertURL := ms.URL + "/oauth2/v3/certs" - key := jwtKey{jwksURI: mockCertURL} - - e, found := r.keyEntries.Load(key) - if !found { - t.Fatalf("No cached public key for %+v", key) - } - oldRefreshedTime := e.(jwtPubKeyEntry).lastRefreshedTime - - time.Sleep(200 * time.Millisecond) - - e, found = r.keyEntries.Load(key) - if !found { - t.Fatalf("No cached public key for %+v", key) - } - newRefreshedTime := e.(jwtPubKeyEntry).lastRefreshedTime - - if actualChanged := oldRefreshedTime != newRefreshedTime; actualChanged != wantChanged { - t.Errorf("Want changed: %t but got %t", wantChanged, actualChanged) - } -} - -func TestCompareJWKSResponse(t *testing.T) { - type args struct { - oldKeyString string - newKeyString string - } - tests := []struct { - name string - args args - want bool - wantErr bool - }{ - {"testEquivalentStrings", args{test.JwtPubKey1, test.JwtPubKey1}, false, false}, - {"testReorderedKeys", args{test.JwtPubKey1, test.JwtPubKey1Reordered}, false, false}, - {"testDifferentKeys", args{test.JwtPubKey1, test.JwtPubKey2}, true, false}, - {"testOldJsonParseFailure", args{"This is not JSON", test.JwtPubKey1}, true, false}, - {"testNewJsonParseFailure", args{test.JwtPubKey1, "This is not JSON"}, false, true}, - {"testNewNoKid", args{test.JwtPubKey1, test.JwtPubKeyNoKid}, true, false}, - {"testOldNoKid", args{test.JwtPubKeyNoKid, test.JwtPubKey1}, true, false}, - {"testBothNoKidSame", args{test.JwtPubKeyNoKid, test.JwtPubKeyNoKid}, false, false}, - {"testBothNoKidDifferent", args{test.JwtPubKeyNoKid, test.JwtPubKeyNoKid2}, true, false}, - {"testNewNoKeys", args{test.JwtPubKey1, test.JwtPubKeyNoKeys}, true, false}, - {"testOldNoKeys", args{test.JwtPubKeyNoKeys, test.JwtPubKey1}, true, false}, - {"testBothNoKeysSame", args{test.JwtPubKeyNoKeys, test.JwtPubKeyNoKeys}, false, false}, - {"testBothNoKeysDifferent", args{test.JwtPubKeyNoKeys, test.JwtPubKeyNoKeys2}, true, false}, - {"testNewExtraElements", args{test.JwtPubKey1, test.JwtPubKeyExtraElements}, true, false}, - {"testOldExtraElements", args{test.JwtPubKeyExtraElements, test.JwtPubKey1}, true, false}, - {"testBothExtraElements", args{test.JwtPubKeyExtraElements, test.JwtPubKeyExtraElements}, false, false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := compareJWKSResponse(tt.args.oldKeyString, tt.args.newKeyString) - if (err != nil) != tt.wantErr { - t.Errorf("compareJWKSResponse() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.want { - t.Errorf("compareJWKSResponse() got = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/pilot/pkg/model/kstatus/helper.go b/pilot/pkg/model/kstatus/helper.go deleted file mode 100644 index fe66830a5..000000000 --- a/pilot/pkg/model/kstatus/helper.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kstatus - -import ( - "reflect" -) - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" -) - -const ( - StatusTrue = "True" - StatusFalse = "False" -) - -// InvertStatus returns the opposite of the provided status. If an invalid status is passed in, False is returned -func InvertStatus(status metav1.ConditionStatus) metav1.ConditionStatus { - switch status { - case StatusFalse: - return StatusTrue - default: - return StatusFalse - } -} - -// WrappedStatus provides a wrapper around a status message that keeps track of whether or not any -// changes have been made. This allows users to declarative write status, without worrying about -// tracking changes. When read to commit (typically to Kubernetes), any messages with Dirty=false can -// be discarded. -type WrappedStatus struct { - // Status is the object that is wrapped. - config.Status - // Dirty indicates if this object has been modified at all. - // Note: only changes wrapped in Mutate are tracked. - Dirty bool -} - -func Wrap(s config.Status) *WrappedStatus { - return &WrappedStatus{config.DeepCopy(s), false} -} - -func (w *WrappedStatus) Mutate(f func(s config.Status) config.Status) { - if w.Status == nil { - return - } - old := config.DeepCopy(w.Status) - w.Status = f(w.Status) - // TODO: change this to be more efficient. Likely we allow modifications via WrappedStatus that - // modify specific things (ie conditions). - if !reflect.DeepEqual(old, w.Status) { - w.Dirty = true - } -} - -func (w *WrappedStatus) Unwrap() config.Status { - return w.Status -} - -var EmptyCondition = metav1.Condition{} - -func GetCondition(conditions []metav1.Condition, condition string) metav1.Condition { - for _, cond := range conditions { - if cond.Type == condition { - return cond - } - } - return EmptyCondition -} - -// UpdateConditionIfChanged updates a condition if it has been changed. -func UpdateConditionIfChanged(conditions []metav1.Condition, condition metav1.Condition) []metav1.Condition { - ret := append([]metav1.Condition(nil), conditions...) - idx := -1 - for i, cond := range ret { - if cond.Type == condition.Type { - idx = i - break - } - } - - if idx == -1 { - ret = append(ret, condition) - return ret - } - if ret[idx].Message == condition.Message && - ret[idx].ObservedGeneration == condition.ObservedGeneration && - ret[idx].Status == condition.Status { - // Skip update, no changes - return conditions - } - ret[idx] = condition - - return ret -} - -// CreateCondition sets a condition only if it has not already been set -func CreateCondition(conditions []metav1.Condition, condition metav1.Condition, unsetReason string) []metav1.Condition { - ret := append([]metav1.Condition(nil), conditions...) - idx := -1 - for i, cond := range ret { - if cond.Type == condition.Type { - idx = i - if cond.Reason == unsetReason { - // Condition is set, but its for unsetReason. This is needed because some conditions have defaults - ret[idx] = condition - return ret - } - break - } - } - - if idx == -1 { - // Not found! We should set it - ret = append(ret, condition) - } - return ret -} diff --git a/pilot/pkg/model/leak_test.go b/pilot/pkg/model/leak_test.go deleted file mode 100644 index 1665150e5..000000000 --- a/pilot/pkg/model/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/model/listener.go b/pilot/pkg/model/listener.go deleted file mode 100644 index 2f5365553..000000000 --- a/pilot/pkg/model/listener.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -const ( - // RDSHttpProxy is the special name for HTTP PROXY route - RDSHttpProxy = "http_proxy" - - // VirtualOutboundListenerName is the name for traffic capture listener - VirtualOutboundListenerName = "virtualOutbound" - - // VirtualOutboundCatchAllTCPFilterChainName is the name of the catch all tcp filter chain - VirtualOutboundCatchAllTCPFilterChainName = "virtualOutbound-catchall-tcp" - - // VirtualOutboundBlackholeFilterChainName is the name of the filter chain to blackhole undesired traffic - VirtualOutboundBlackholeFilterChainName = "virtualOutbound-blackhole" - // VirtualInboundBlackholeFilterChainName is the name of the filter chain to blackhole undesired traffic - VirtualInboundBlackholeFilterChainName = "virtualInbound-blackhole" - - // VirtualInboundListenerName is the name for traffic capture listener - VirtualInboundListenerName = "virtualInbound" - - // VirtualInboundCatchAllHTTPFilterChainName is the name of the catch all http filter chain - VirtualInboundCatchAllHTTPFilterChainName = "virtualInbound-catchall-http" -) diff --git a/pilot/pkg/model/log.go b/pilot/pkg/model/log.go deleted file mode 100644 index a820a9667..000000000 --- a/pilot/pkg/model/log.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - istiolog "istio.io/pkg/log" -) - -var log = istiolog.RegisterScope("model", "model", 0) diff --git a/pilot/pkg/model/network.go b/pilot/pkg/model/network.go deleted file mode 100644 index 1ca20456b..000000000 --- a/pilot/pkg/model/network.go +++ /dev/null @@ -1,567 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "math" - "net" - "reflect" - "sort" - "strings" - "sync" - "time" -) - -import ( - "github.com/miekg/dns" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/network" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// NetworkGateway is the gateway of a network -type NetworkGateway struct { - // Network is the ID of the network where this Gateway resides. - Network network.ID - // Cluster is the ID of the k8s cluster where this Gateway resides. - Cluster cluster.ID - // gateway ip address - Addr string - // gateway port - Port uint32 -} - -type NetworkGatewaysWatcher interface { - NetworkGateways() []NetworkGateway - AppendNetworkGatewayHandler(h func()) -} - -// NetworkGatewaysHandler can be embedded to easily implement NetworkGatewaysWatcher. -type NetworkGatewaysHandler struct { - handlers []func() -} - -func (ngh *NetworkGatewaysHandler) AppendNetworkGatewayHandler(h func()) { - ngh.handlers = append(ngh.handlers, h) -} - -func (ngh *NetworkGatewaysHandler) NotifyGatewayHandlers() { - for _, handler := range ngh.handlers { - handler() - } -} - -// NewNetworkManager creates a new NetworkManager from the Environment by merging -// together the MeshNetworks and ServiceRegistry-specific gateways. -func NewNetworkManager(env *Environment, xdsUpdater XDSUpdater) (*NetworkManager, error) { - nameCache, err := newNetworkGatewayNameCache() - if err != nil { - return nil, err - } - mgr := &NetworkManager{env: env, NameCache: nameCache, xdsUpdater: xdsUpdater} - env.AddNetworksHandler(mgr.reloadAndPush) - env.AppendNetworkGatewayHandler(mgr.reloadAndPush) - nameCache.AppendNetworkGatewayHandler(mgr.reloadAndPush) - mgr.reload() - return mgr, nil -} - -func (mgr *NetworkManager) reloadAndPush() { - mgr.mu.Lock() - defer mgr.mu.Unlock() - oldGateways := make(NetworkGatewaySet) - for _, gateway := range mgr.allGateways() { - oldGateways.Add(gateway) - } - changed := !mgr.reload().Equals(oldGateways) - - if changed && mgr.xdsUpdater != nil { - log.Infof("gateways changed, triggering push") - mgr.xdsUpdater.ConfigUpdate(&PushRequest{Full: true, Reason: []TriggerReason{NetworksTrigger}}) - } -} - -func (mgr *NetworkManager) reload() NetworkGatewaySet { - log.Infof("reloading network gateways") - - // Generate a snapshot of the state of gateways by merging the contents of - // MeshNetworks and the ServiceRegistries. - - // Store all gateways in a set initially to eliminate duplicates. - gatewaySet := make(NetworkGatewaySet) - - // First, load gateways from the static MeshNetworks config. - meshNetworks := mgr.env.NetworksWatcher.Networks() - if meshNetworks != nil { - for nw, networkConf := range meshNetworks.Networks { - for _, gw := range networkConf.Gateways { - if gw.GetAddress() == "" { - // registryServiceName addresses will be populated via kube service registry - continue - } - gatewaySet[NetworkGateway{ - Cluster: "", /* TODO(nmittler): Add Cluster to the API */ - Network: network.ID(nw), - Addr: gw.GetAddress(), - Port: gw.Port, - }] = struct{}{} - } - } - } - - // Second, load registry-specific gateways. - for _, gw := range mgr.env.NetworkGateways() { - // - the internal map of label gateways - these get deleted if the service is deleted, updated if the ip changes etc. - // - the computed map from meshNetworks (triggered by reloadNetworkLookup, the ported logic from getGatewayAddresses) - gatewaySet[gw] = struct{}{} - } - - mgr.resolveHostnameGateways(gatewaySet) - - // Now populate the maps by network and by network+cluster. - byNetwork := make(map[network.ID][]NetworkGateway) - byNetworkAndCluster := make(map[networkAndCluster][]NetworkGateway) - for gw := range gatewaySet { - byNetwork[gw.Network] = append(byNetwork[gw.Network], gw) - nc := networkAndClusterForGateway(&gw) - byNetworkAndCluster[nc] = append(byNetworkAndCluster[nc], gw) - } - - gwNum := []int{} - // Sort the gateways in byNetwork, and also calculate the max number - // of gateways per network. - for k, gws := range byNetwork { - byNetwork[k] = SortGateways(gws) - gwNum = append(gwNum, len(gws)) - } - - // Sort the gateways in byNetworkAndCluster. - for k, gws := range byNetworkAndCluster { - byNetworkAndCluster[k] = SortGateways(gws) - gwNum = append(gwNum, len(gws)) - } - - lcmVal := 1 - // calculate lcm - for _, num := range gwNum { - lcmVal = lcm(lcmVal, num) - } - - mgr.lcm = uint32(lcmVal) - mgr.byNetwork = byNetwork - mgr.byNetworkAndCluster = byNetworkAndCluster - - return gatewaySet -} - -func (mgr *NetworkManager) resolveHostnameGateways(gatewaySet map[NetworkGateway]struct{}) { - // filter the list of gateways to resolve - hostnameGateways := map[string][]NetworkGateway{} - names := sets.New() - for gw := range gatewaySet { - if gwIP := net.ParseIP(gw.Addr); gwIP != nil { - continue - } - delete(gatewaySet, gw) - if !features.ResolveHostnameGateways { - log.Warnf("Failed parsing gateway address %s from Service Registry. "+ - "Set RESOLVE_HOSTNAME_GATEWAYS on istiod to enable resolving hostnames in the control plane.", - gw.Addr) - continue - } - hostnameGateways[gw.Addr] = append(hostnameGateways[gw.Addr], gw) - names.Insert(gw.Addr) - } - - // resolve each hostname - for host, addrs := range mgr.NameCache.Resolve(names) { - gwsForHost := hostnameGateways[host] - if len(addrs) == 0 { - log.Warnf("could not resolve hostname %q for %d gateways", host, len(gwsForHost)) - } - // expand each resolved address into a NetworkGateway - for _, gw := range gwsForHost { - for _, resolved := range addrs { - // copy the base gateway to preserve the port/network, but update with the resolved IP - resolvedGw := gw - resolvedGw.Addr = resolved - gatewaySet[resolvedGw] = struct{}{} - } - } - } -} - -// NetworkManager provides gateway details for accessing remote networks. -type NetworkManager struct { - env *Environment - // exported for test - NameCache *networkGatewayNameCache - xdsUpdater XDSUpdater - - // least common multiple of gateway number of {per network, per cluster} - mu sync.RWMutex - lcm uint32 - byNetwork map[network.ID][]NetworkGateway - byNetworkAndCluster map[networkAndCluster][]NetworkGateway -} - -func (mgr *NetworkManager) IsMultiNetworkEnabled() bool { - if mgr == nil { - return false - } - mgr.mu.RLock() - defer mgr.mu.RUnlock() - return len(mgr.byNetwork) > 0 -} - -// GetLBWeightScaleFactor returns the least common multiple of the number of gateways per network. -func (mgr *NetworkManager) GetLBWeightScaleFactor() uint32 { - mgr.mu.RLock() - defer mgr.mu.RUnlock() - return mgr.lcm -} - -func (mgr *NetworkManager) AllGateways() []NetworkGateway { - mgr.mu.RLock() - defer mgr.mu.RUnlock() - return mgr.allGateways() -} - -func (mgr *NetworkManager) allGateways() []NetworkGateway { - if mgr.byNetwork == nil { - return nil - } - out := make([]NetworkGateway, 0) - for _, gateways := range mgr.byNetwork { - out = append(out, gateways...) - } - return SortGateways(out) -} - -func (mgr *NetworkManager) GatewaysByNetwork() map[network.ID][]NetworkGateway { - mgr.mu.RLock() - defer mgr.mu.RUnlock() - if mgr.byNetwork == nil { - return nil - } - out := make(map[network.ID][]NetworkGateway) - for k, v := range mgr.byNetwork { - out[k] = append(make([]NetworkGateway, 0, len(v)), v...) - } - return out -} - -func (mgr *NetworkManager) GatewaysForNetwork(nw network.ID) []NetworkGateway { - mgr.mu.RLock() - defer mgr.mu.RUnlock() - if mgr.byNetwork == nil { - return nil - } - return mgr.byNetwork[nw] -} - -func (mgr *NetworkManager) GatewaysForNetworkAndCluster(nw network.ID, c cluster.ID) []NetworkGateway { - mgr.mu.RLock() - defer mgr.mu.RUnlock() - if mgr.byNetwork == nil { - return nil - } - return mgr.byNetworkAndCluster[networkAndClusterFor(nw, c)] -} - -type networkAndCluster struct { - network network.ID - cluster cluster.ID -} - -func networkAndClusterForGateway(g *NetworkGateway) networkAndCluster { - return networkAndClusterFor(g.Network, g.Cluster) -} - -func networkAndClusterFor(nw network.ID, c cluster.ID) networkAndCluster { - return networkAndCluster{ - network: nw, - cluster: c, - } -} - -func SortGateways(gws []NetworkGateway) []NetworkGateway { - // Sort the array so that it's stable. - sort.SliceStable(gws, func(i, j int) bool { - if cmp := strings.Compare(gws[i].Addr, gws[j].Addr); cmp < 0 { - return true - } - return gws[i].Port < gws[j].Port - }) - return gws -} - -// greatest common divisor of x and y -func gcd(x, y int) int { - var tmp int - for { - tmp = x % y - if tmp > 0 { - x = y - y = tmp - } else { - return y - } - } -} - -// least common multiple of x and y -func lcm(x, y int) int { - return x * y / gcd(x, y) -} - -// NetworkGatewaySet is a helper to manage a set of NetworkGateway instances. -type NetworkGatewaySet map[NetworkGateway]struct{} - -func (s NetworkGatewaySet) Equals(other NetworkGatewaySet) bool { - if len(s) != len(other) { - return false - } - // deepequal won't catch nil-map == empty map - if len(s) == 0 && len(other) == 0 { - return true - } - return reflect.DeepEqual(s, other) -} - -func (s NetworkGatewaySet) Add(gw NetworkGateway) { - s[gw] = struct{}{} -} - -func (s NetworkGatewaySet) AddAll(other NetworkGatewaySet) { - for gw := range other { - s.Add(gw) - } -} - -func (s NetworkGatewaySet) ToArray() []NetworkGateway { - gws := make([]NetworkGateway, 0, len(s)) - for gw := range s { - gws = append(gws, gw) - } - - // Sort the array so that it's stable. - gws = SortGateways(gws) - return gws -} - -// MinGatewayTTL is exported for testing -var MinGatewayTTL = 30 * time.Second - -type networkGatewayNameCache struct { - NetworkGatewaysHandler - client *dnsClient - - sync.Mutex - cache map[string]nameCacheEntry -} - -type nameCacheEntry struct { - value []string - expiry time.Time - timer *time.Timer -} - -func newNetworkGatewayNameCache() (*networkGatewayNameCache, error) { - c, err := newClient() - if err != nil { - return nil, err - } - return newNetworkGatewayNameCacheWithClient(c), nil -} - -// newNetworkGatewayNameCacheWithClient exported for test -func newNetworkGatewayNameCacheWithClient(c *dnsClient) *networkGatewayNameCache { - return &networkGatewayNameCache{client: c, cache: map[string]nameCacheEntry{}} -} - -// Resolve takes a list of hostnames and returns a map of names to addresses -func (n *networkGatewayNameCache) Resolve(names sets.Set) map[string][]string { - n.Lock() - defer n.Unlock() - - n.cleanupWatches(names) - - out := make(map[string][]string, len(names)) - for name := range names { - out[name] = n.resolveFromCache(name) - } - - return out -} - -// cleanupWatches cancels any scheduled re-resolve for names we no longer care about -func (n *networkGatewayNameCache) cleanupWatches(names sets.Set) { - for name, entry := range n.cache { - if names.Contains(name) { - continue - } - entry.timer.Stop() - delete(n.cache, name) - } -} - -func (n *networkGatewayNameCache) resolveFromCache(name string) []string { - if entry, ok := n.cache[name]; ok && entry.expiry.After(time.Now()) { - return entry.value - } - // ideally this will not happen more than once for each name and the cache auto-updates in the background - // even if it does, this happens on the SotW ingestion path (kube or meshnetworks changes) and not xds push path. - return n.resolveAndCache(name) -} - -func (n *networkGatewayNameCache) resolveAndCache(name string) []string { - if entry, ok := n.cache[name]; ok { - entry.timer.Stop() - } - delete(n.cache, name) - addrs, ttl := n.resolve(name) - // avoid excessive pushes due to small TTL - if ttl < MinGatewayTTL { - ttl = MinGatewayTTL - } - expiry := time.Now().Add(ttl) - n.cache[name] = nameCacheEntry{ - value: addrs, - expiry: expiry, - // TTL expires, try to refresh TODO should this be < ttl? - timer: time.AfterFunc(ttl, n.refreshAndNotify(name)), - } - - return addrs -} - -// refreshAndNotify is triggered via time.AfterFunc and will recursively schedule itself that way until timer is cleaned -// up via cleanupWatches. -func (n *networkGatewayNameCache) refreshAndNotify(name string) func() { - return func() { - log.Debugf("network gateways: refreshing DNS for %s", name) - n.Lock() - old := n.cache[name] - addrs := n.resolveAndCache(name) - n.Unlock() - - if !stringSliceEqual(old.value, addrs) { - log.Debugf("network gateways: DNS for %s changed: %v -> %v", name, old.value, addrs) - n.NotifyGatewayHandlers() - } - } -} - -// avoid import cycle -func stringSliceEqual(a, b []string) bool { - if len(a) != len(b) { - return false - } - - for i := range a { - if a[i] != b[i] { - return false - } - } - - return true -} - -// resolve gets all the A and AAAA records for the given name -func (n *networkGatewayNameCache) resolve(name string) ([]string, time.Duration) { - // TODO figure out how to query only A + AAAA - res := n.client.Query(new(dns.Msg).SetQuestion(dns.Fqdn(name), dns.TypeANY)) - if res == nil || len(res.Answer) == 0 { - return nil, 0 - } - ttl := uint32(math.MaxUint32) - var out []string - for _, rr := range res.Answer { - switch v := rr.(type) { - case *dns.A: - out = append(out, v.A.String()) - case *dns.AAAA: - // TODO may not always want ipv6t? - out = append(out, v.AAAA.String()) - default: - // not a valid record, don't inspect TTL - continue - } - if nextTTL := rr.Header().Ttl; nextTTL < ttl { - ttl = nextTTL - } - } - sort.Strings(out) - return out, time.Duration(ttl) -} - -// TODO share code with pkg/dns -type dnsClient struct { - *dns.Client - resolvConfServers []string -} - -// NetworkGatewayTestDNSServers if set will ignore resolv.conf and use the given DNS servers for tests. -var NetworkGatewayTestDNSServers []string - -func newClient() (*dnsClient, error) { - servers := NetworkGatewayTestDNSServers - if len(servers) == 0 { - dnsConfig, err := dns.ClientConfigFromFile("/etc/resolv.conf") - if err != nil { - return nil, err - } - if dnsConfig != nil { - for _, s := range dnsConfig.Servers { - servers = append(servers, net.JoinHostPort(s, dnsConfig.Port)) - } - } - // TODO take search namespaces into account - // TODO what about /etc/hosts? - } - - c := &dnsClient{ - Client: &dns.Client{ - DialTimeout: 5 * time.Second, - ReadTimeout: 5 * time.Second, - WriteTimeout: 5 * time.Second, - }, - } - c.resolvConfServers = append(c.resolvConfServers, servers...) - return c, nil -} - -func (c *dnsClient) Query(req *dns.Msg) *dns.Msg { - var response *dns.Msg - for _, upstream := range c.resolvConfServers { - cResponse, _, err := c.Exchange(req, upstream) - if err == nil { - response = cResponse - break - } - log.Infof("upstream dns failure: %v", err) - } - if response == nil { - response = new(dns.Msg) - response.SetReply(req) - response.Rcode = dns.RcodeServerFailure - } - return response -} diff --git a/pilot/pkg/model/network_test.go b/pilot/pkg/model/network_test.go deleted file mode 100644 index 96e2b46d4..000000000 --- a/pilot/pkg/model/network_test.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model_test - -import ( - "fmt" - "net" - "reflect" - "sync" - "testing" - "time" -) - -import ( - "github.com/miekg/dns" - meshconfig "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -func TestGatewayHostnames(t *testing.T) { - origMinGatewayTTL := model.MinGatewayTTL - model.MinGatewayTTL = 30 * time.Millisecond - t.Cleanup(func() { - model.MinGatewayTTL = origMinGatewayTTL - }) - - gwHost := "test.gw.istio.io" - dnsServer := newFakeDNSServer(":0", 1, sets.New(gwHost)) - model.NetworkGatewayTestDNSServers = []string{dnsServer.Server.PacketConn.LocalAddr().String()} - t.Cleanup(func() { - if err := dnsServer.Shutdown(); err != nil { - t.Logf("failed shutting down fake dns server") - } - }) - - meshNetworks := mesh.NewFixedNetworksWatcher(nil) - xdsUpdater := &xds.FakeXdsUpdater{Events: make(chan xds.FakeXdsEvent, 10)} - env := &model.Environment{NetworksWatcher: meshNetworks, ServiceDiscovery: memory.NewServiceDiscovery()} - if err := env.InitNetworksManager(xdsUpdater); err != nil { - t.Fatal(err) - } - - var firstIP string - t.Run("initial resolution", func(t *testing.T) { - meshNetworks.SetNetworks(&meshconfig.MeshNetworks{Networks: map[string]*meshconfig.Network{ - "nw0": {Gateways: []*meshconfig.Network_IstioNetworkGateway{{ - Gw: &meshconfig.Network_IstioNetworkGateway_Address{ - Address: gwHost, - }, - Port: 15443, - }}}, - }}) - xdsUpdater.WaitDurationOrFail(t, model.MinGatewayTTL+5*time.Second, "xds") - gws := env.NetworkManager.AllGateways() - if len(gws) != 1 { - t.Fatalf("expected 1 IP") - } - if gws[0].Network != "nw0" { - t.Fatalf("unexpected network: %v", gws) - } - firstIP = gws[0].Addr - }) - t.Run("re-resolve after TTL", func(t *testing.T) { - if testing.Short() { - t.Skip() - } - // after the update, we should see the next gateway. Since TTL is low we don't know the exact IP, but we know it should change from - // the original - retry.UntilOrFail(t, func() bool { - return !reflect.DeepEqual(env.NetworkManager.AllGateways(), []model.NetworkGateway{{Network: "nw0", Addr: firstIP, Port: 15443}}) - }) - }) - t.Run("forget", func(t *testing.T) { - meshNetworks.SetNetworks(nil) - xdsUpdater.WaitDurationOrFail(t, 5*time.Second, "xds") - if len(env.NetworkManager.AllGateways()) > 0 { - t.Fatalf("expected no gateways") - } - }) -} - -type fakeDNSServer struct { - *dns.Server - ttl uint32 - - mu sync.Mutex - // map fqdn hostname -> query count - hosts map[string]int -} - -func newFakeDNSServer(addr string, ttl uint32, hosts sets.Set) *fakeDNSServer { - waitLock := sync.Mutex{} - waitLock.Lock() - s := &fakeDNSServer{ - Server: &dns.Server{Addr: addr, Net: "udp", NotifyStartedFunc: waitLock.Unlock}, - ttl: ttl, - hosts: make(map[string]int, len(hosts)), - } - s.Handler = s - - for k := range hosts { - s.hosts[dns.Fqdn(k)] = 0 - } - - go func() { - if err := s.ListenAndServe(); err != nil { - scopes.Framework.Errorf("fake dns server error: %v", err) - } - }() - waitLock.Lock() - return s -} - -func (s *fakeDNSServer) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { - s.mu.Lock() - defer s.mu.Unlock() - - msg := (&dns.Msg{}).SetReply(r) - switch r.Question[0].Qtype { - case dns.TypeA, dns.TypeANY: - domain := msg.Question[0].Name - c, ok := s.hosts[domain] - if ok { - s.hosts[domain]++ - msg.Answer = append(msg.Answer, &dns.A{ - Hdr: dns.RR_Header{Name: domain, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: s.ttl}, - A: net.ParseIP(fmt.Sprintf("10.0.0.%d", c)), - }) - } - } - if err := w.WriteMsg(msg); err != nil { - scopes.Framework.Errorf("failed writing fake DNS response: %v", err) - } -} diff --git a/pilot/pkg/model/proxy_config.go b/pilot/pkg/model/proxy_config.go deleted file mode 100644 index a76690ca7..000000000 --- a/pilot/pkg/model/proxy_config.go +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "google.golang.org/protobuf/proto" - "istio.io/api/annotation" - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/api/networking/v1beta1" - istiolog "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -var pclog = istiolog.RegisterScope("proxyconfig", "Istio ProxyConfig", 0) - -// ProxyConfigs organizes ProxyConfig configuration by namespace. -type ProxyConfigs struct { - // namespaceToProxyConfigs - namespaceToProxyConfigs map[string][]*v1beta1.ProxyConfig - - // root namespace - rootNamespace string -} - -// EffectiveProxyConfig generates the correct merged ProxyConfig for a given ProxyConfigTarget. -func (p *ProxyConfigs) EffectiveProxyConfig(meta *NodeMetadata, mc *meshconfig.MeshConfig) *meshconfig.ProxyConfig { - if p == nil || meta == nil { - return nil - } - - effectiveProxyConfig := mesh.DefaultProxyConfig() - - // Merge the proxy config from default config. - effectiveProxyConfig = mergeWithPrecedence(mc.GetDefaultConfig(), effectiveProxyConfig) - if p.rootNamespace != "" { - effectiveProxyConfig = mergeWithPrecedence(p.mergedGlobalConfig(), effectiveProxyConfig) - } - - if meta.Namespace != p.rootNamespace { - namespacedConfig := p.mergedNamespaceConfig(meta.Namespace) - effectiveProxyConfig = mergeWithPrecedence(namespacedConfig, effectiveProxyConfig) - } - - workloadConfig := p.mergedWorkloadConfig(meta.Namespace, meta.Labels) - - // Check for proxy.istio.io/config annotation and merge it with lower priority than the - // workload-matching ProxyConfig CRs. - if v, ok := meta.Annotations[annotation.ProxyConfig.Name]; ok { - pca, err := proxyConfigFromAnnotation(v) - if err == nil { - workloadConfig = mergeWithPrecedence(workloadConfig, pca) - } - } - effectiveProxyConfig = mergeWithPrecedence(workloadConfig, effectiveProxyConfig) - - return effectiveProxyConfig -} - -func GetProxyConfigs(store ConfigStore, mc *meshconfig.MeshConfig) (*ProxyConfigs, error) { - proxyconfigs := &ProxyConfigs{ - namespaceToProxyConfigs: map[string][]*v1beta1.ProxyConfig{}, - rootNamespace: mc.GetRootNamespace(), - } - resources, err := store.List(collections.IstioNetworkingV1Beta1Proxyconfigs.Resource().GroupVersionKind(), NamespaceAll) - if err != nil { - return nil, err - } - sortConfigByCreationTime(resources) - ns := proxyconfigs.namespaceToProxyConfigs - for _, resource := range resources { - ns[resource.Namespace] = append(ns[resource.Namespace], resource.Spec.(*v1beta1.ProxyConfig)) - } - return proxyconfigs, nil -} - -func (p *ProxyConfigs) mergedGlobalConfig() *meshconfig.ProxyConfig { - return p.mergedNamespaceConfig(p.rootNamespace) -} - -// mergedNamespaceConfig merges ProxyConfig resources matching the given namespace. -func (p *ProxyConfigs) mergedNamespaceConfig(namespace string) *meshconfig.ProxyConfig { - for _, pc := range p.namespaceToProxyConfigs[namespace] { - if pc.GetSelector() == nil { - // return the first match. this is consistent since - // we sort the resources by creation time beforehand. - return toMeshConfigProxyConfig(pc) - } - } - return nil -} - -// mergedWorkloadConfig merges ProxyConfig resources matching the given namespace and labels. -func (p *ProxyConfigs) mergedWorkloadConfig(namespace string, l map[string]string) *meshconfig.ProxyConfig { - for _, pc := range p.namespaceToProxyConfigs[namespace] { - if len(pc.GetSelector().GetMatchLabels()) == 0 { - continue - } - selector := labels.Instance(pc.GetSelector().GetMatchLabels()) - if selector.SubsetOf(l) { - // return the first match. this is consistent since - // we sort the resources by creation time beforehand. - return toMeshConfigProxyConfig(pc) - } - } - return nil -} - -// mergeWithPrecedence merges the ProxyConfigs together with earlier items having -// the highest priority. -func mergeWithPrecedence(pcs ...*meshconfig.ProxyConfig) *meshconfig.ProxyConfig { - merged := &meshconfig.ProxyConfig{} - for i := len(pcs) - 1; i >= 0; i-- { - if pcs[i] == nil { - continue - } - // TODO(Monkeyanator) some fields seem not to merge when set to the type's default value - // such as overriding with a concurrency value 0. Do we need a custom merge similar to what the - // telemetry code does with shallowMerge? - proto.Merge(merged, pcs[i]) - if pcs[i].GetConcurrency() != nil { - merged.Concurrency = pcs[i].GetConcurrency() - } - if pcs[i].GetImage() != nil { - merged.Image = pcs[i].GetImage() - } - } - return merged -} - -func toMeshConfigProxyConfig(pc *v1beta1.ProxyConfig) *meshconfig.ProxyConfig { - mcpc := &meshconfig.ProxyConfig{} - if pc.Concurrency != nil { - mcpc.Concurrency = pc.Concurrency - } - if pc.EnvironmentVariables != nil { - mcpc.ProxyMetadata = pc.EnvironmentVariables - } - if pc.Image != nil { - mcpc.Image = pc.Image - } - return mcpc -} - -func proxyConfigFromAnnotation(pcAnnotation string) (*meshconfig.ProxyConfig, error) { - pc := &meshconfig.ProxyConfig{} - if err := protomarshal.ApplyYAML(pcAnnotation, pc); err != nil { - return nil, err - } - return pc, nil -} diff --git a/pilot/pkg/model/proxy_config_test.go b/pilot/pkg/model/proxy_config_test.go deleted file mode 100644 index a251f1df3..000000000 --- a/pilot/pkg/model/proxy_config_test.go +++ /dev/null @@ -1,464 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "testing" - "time" -) - -import ( - "github.com/golang/protobuf/proto" // nolint: staticcheck - "github.com/golang/protobuf/ptypes/wrappers" - "istio.io/api/annotation" - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/api/networking/v1beta1" - istioTypes "istio.io/api/type/v1beta1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -var now = time.Now() - -const istioRootNamespace = "dubbo-system" - -func TestConvertToMeshConfigProxyConfig(t *testing.T) { - cases := []struct { - name string - pc *v1beta1.ProxyConfig - expected *meshconfig.ProxyConfig - }{ - { - name: "concurrency", - pc: &v1beta1.ProxyConfig{ - Concurrency: &wrappers.Int32Value{Value: 3}, - }, - expected: &meshconfig.ProxyConfig{ - Concurrency: &wrappers.Int32Value{Value: 3}, - }, - }, - { - name: "environment variables", - pc: &v1beta1.ProxyConfig{ - EnvironmentVariables: map[string]string{ - "a": "b", - "c": "d", - }, - }, - expected: &meshconfig.ProxyConfig{ - ProxyMetadata: map[string]string{ - "a": "b", - "c": "d", - }, - }, - }, - } - - for _, tc := range cases { - converted := toMeshConfigProxyConfig(tc.pc) - assert.Equal(t, converted, tc.expected) - } -} - -func TestMergeWithPrecedence(t *testing.T) { - cases := []struct { - name string - first *meshconfig.ProxyConfig - second *meshconfig.ProxyConfig - expected *meshconfig.ProxyConfig - }{ - { - name: "concurrency", - first: &meshconfig.ProxyConfig{ - Concurrency: v(1), - }, - second: &meshconfig.ProxyConfig{ - Concurrency: v(2), - }, - expected: &meshconfig.ProxyConfig{ - Concurrency: v(1), - }, - }, - { - name: "concurrency value 0", - first: &meshconfig.ProxyConfig{ - Concurrency: v(0), - }, - second: &meshconfig.ProxyConfig{ - Concurrency: v(2), - }, - expected: &meshconfig.ProxyConfig{ - Concurrency: v(0), - }, - }, - { - name: "source concurrency nil", - first: &meshconfig.ProxyConfig{ - Concurrency: nil, - }, - second: &meshconfig.ProxyConfig{ - Concurrency: v(2), - }, - expected: &meshconfig.ProxyConfig{ - Concurrency: v(2), - }, - }, - { - name: "dest concurrency nil", - first: &meshconfig.ProxyConfig{ - Concurrency: v(2), - }, - second: &meshconfig.ProxyConfig{ - Concurrency: nil, - }, - expected: &meshconfig.ProxyConfig{ - Concurrency: v(2), - }, - }, - { - name: "both concurrency nil", - first: &meshconfig.ProxyConfig{ - Concurrency: nil, - }, - second: &meshconfig.ProxyConfig{ - Concurrency: nil, - }, - expected: &meshconfig.ProxyConfig{ - Concurrency: nil, - }, - }, - { - name: "envvars", - first: &meshconfig.ProxyConfig{ - ProxyMetadata: map[string]string{ - "a": "x", - "b": "y", - }, - }, - second: &meshconfig.ProxyConfig{ - ProxyMetadata: map[string]string{ - "a": "z", - "b": "y", - "c": "d", - }, - }, - expected: &meshconfig.ProxyConfig{ - ProxyMetadata: map[string]string{ - "a": "x", - "b": "y", - "c": "d", - }, - }, - }, - { - name: "empty envars merge with populated", - first: &meshconfig.ProxyConfig{ - ProxyMetadata: map[string]string{}, - }, - second: &meshconfig.ProxyConfig{ - ProxyMetadata: map[string]string{ - "a": "z", - "b": "y", - "c": "d", - }, - }, - expected: &meshconfig.ProxyConfig{ - ProxyMetadata: map[string]string{ - "a": "z", - "b": "y", - "c": "d", - }, - }, - }, - { - name: "nil proxyconfig", - first: nil, - second: &meshconfig.ProxyConfig{ - ProxyMetadata: map[string]string{ - "a": "z", - "b": "y", - "c": "d", - }, - }, - expected: &meshconfig.ProxyConfig{ - ProxyMetadata: map[string]string{ - "a": "z", - "b": "y", - "c": "d", - }, - }, - }, - } - - for _, tc := range cases { - merged := mergeWithPrecedence(tc.first, tc.second) - assert.Equal(t, merged, tc.expected) - } -} - -func TestEffectiveProxyConfig(t *testing.T) { - cases := []struct { - name string - configs []config.Config - defaultConfig *meshconfig.ProxyConfig - proxy *NodeMetadata - expected *meshconfig.ProxyConfig - }{ - { - name: "CR applies to matching namespace", - configs: []config.Config{ - newProxyConfig("ns", "test-ns", - &v1beta1.ProxyConfig{ - Concurrency: v(3), - Image: &v1beta1.ProxyImage{ - ImageType: "debug", - }, - }), - }, - proxy: newMeta("test-ns", nil, nil), - expected: &meshconfig.ProxyConfig{ - Concurrency: v(3), - Image: &v1beta1.ProxyImage{ - ImageType: "debug", - }, - }, - }, - { - name: "CR takes precedence over meshConfig.defaultConfig", - configs: []config.Config{ - newProxyConfig("ns", istioRootNamespace, - &v1beta1.ProxyConfig{ - Concurrency: v(3), - }), - }, - defaultConfig: &meshconfig.ProxyConfig{Concurrency: v(2)}, - proxy: newMeta("bar", nil, nil), - expected: &meshconfig.ProxyConfig{Concurrency: v(3)}, - }, - { - name: "workload matching CR takes precedence over namespace matching CR", - configs: []config.Config{ - newProxyConfig("workload", "test-ns", - &v1beta1.ProxyConfig{ - Selector: selector(map[string]string{ - "test": "selector", - }), - Concurrency: v(3), - }), - newProxyConfig("ns", "test-ns", - &v1beta1.ProxyConfig{ - Concurrency: v(2), - }), - }, - proxy: newMeta("test-ns", map[string]string{"test": "selector"}, nil), - expected: &meshconfig.ProxyConfig{Concurrency: v(3)}, - }, - { - name: "matching workload CR takes precedence over annotation", - configs: []config.Config{ - newProxyConfig("workload", "test-ns", - &v1beta1.ProxyConfig{ - Selector: selector(map[string]string{ - "test": "selector", - }), - Concurrency: v(3), - Image: &v1beta1.ProxyImage{ - ImageType: "debug", - }, - }), - }, - proxy: newMeta( - "test-ns", - map[string]string{ - "test": "selector", - }, map[string]string{ - annotation.ProxyConfig.Name: "{ \"concurrency\": 5 }", - }), - expected: &meshconfig.ProxyConfig{ - Concurrency: v(3), - Image: &v1beta1.ProxyImage{ - ImageType: "debug", - }, - }, - }, - { - name: "CR in other namespaces get ignored", - configs: []config.Config{ - newProxyConfig("ns", "wrong-ns", - &v1beta1.ProxyConfig{ - Concurrency: v(1), - }), - newProxyConfig("workload", "wrong-ns", - &v1beta1.ProxyConfig{ - Selector: selector(map[string]string{ - "test": "selector", - }), - Concurrency: v(2), - }), - newProxyConfig("global", istioRootNamespace, - &v1beta1.ProxyConfig{ - Concurrency: v(3), - }), - }, - proxy: newMeta("test-ns", map[string]string{"test": "selector"}, nil), - expected: &meshconfig.ProxyConfig{Concurrency: v(3)}, - }, - { - name: "multiple matching workload CRs, oldest applies", - configs: []config.Config{ - setCreationTimestamp(newProxyConfig("workload-a", "test-ns", - &v1beta1.ProxyConfig{ - Selector: selector(map[string]string{ - "test": "selector", - }), - EnvironmentVariables: map[string]string{ - "A": "1", - }, - }), now), - setCreationTimestamp(newProxyConfig("workload-b", "test-ns", - &v1beta1.ProxyConfig{ - Selector: selector(map[string]string{ - "test": "selector", - }), - EnvironmentVariables: map[string]string{ - "B": "2", - }, - }), now.Add(time.Hour)), - setCreationTimestamp(newProxyConfig("workload-c", "test-ns", - &v1beta1.ProxyConfig{ - Selector: selector(map[string]string{ - "test": "selector", - }), - EnvironmentVariables: map[string]string{ - "C": "3", - }, - }), now.Add(time.Hour)), - }, - proxy: newMeta( - "test-ns", - map[string]string{ - "test": "selector", - }, map[string]string{}), - expected: &meshconfig.ProxyConfig{ProxyMetadata: map[string]string{ - "A": "1", - }}, - }, - { - name: "multiple matching namespace CRs, oldest applies", - configs: []config.Config{ - setCreationTimestamp(newProxyConfig("workload-a", "test-ns", - &v1beta1.ProxyConfig{ - EnvironmentVariables: map[string]string{ - "A": "1", - }, - }), now), - setCreationTimestamp(newProxyConfig("workload-b", "test-ns", - &v1beta1.ProxyConfig{ - EnvironmentVariables: map[string]string{ - "B": "2", - }, - }), now.Add(time.Hour)), - setCreationTimestamp(newProxyConfig("workload-c", "test-ns", - &v1beta1.ProxyConfig{ - EnvironmentVariables: map[string]string{ - "C": "3", - }, - }), now.Add(time.Hour)), - }, - proxy: newMeta( - "test-ns", - map[string]string{}, map[string]string{}), - expected: &meshconfig.ProxyConfig{ProxyMetadata: map[string]string{ - "A": "1", - }}, - }, - { - name: "no configured CR or default config", - proxy: newMeta("ns", nil, nil), - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - store := newProxyConfigStore(t, tc.configs) - m := &meshconfig.MeshConfig{ - RootNamespace: istioRootNamespace, - DefaultConfig: tc.defaultConfig, - } - pcs, err := GetProxyConfigs(store, m) - if err != nil { - t.Fatalf("failed to list proxyconfigs: %v", err) - } - merged := pcs.EffectiveProxyConfig( - tc.proxy, - &meshconfig.MeshConfig{ - RootNamespace: istioRootNamespace, - DefaultConfig: tc.defaultConfig, - }) - pc := mesh.DefaultProxyConfig() - proto.Merge(pc, tc.expected) - - assert.Equal(t, merged, pc) - }) - } -} - -func newProxyConfig(name, ns string, spec config.Spec) config.Config { - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.ProxyConfig, - Name: name, - Namespace: ns, - }, - Spec: spec, - } -} - -func newProxyConfigStore(t *testing.T, configs []config.Config) ConfigStore { - t.Helper() - - store := NewFakeStore() - for _, cfg := range configs { - store.Create(cfg) - } - - return MakeIstioStore(store) -} - -func setCreationTimestamp(c config.Config, t time.Time) config.Config { - c.Meta.CreationTimestamp = t - return c -} - -func newMeta(ns string, labels, annotations map[string]string) *NodeMetadata { - return &NodeMetadata{ - Namespace: ns, - Labels: labels, - Annotations: annotations, - } -} - -func v(x int32) *wrappers.Int32Value { - return &wrappers.Int32Value{Value: x} -} - -func selector(l map[string]string) *istioTypes.WorkloadSelector { - return &istioTypes.WorkloadSelector{MatchLabels: l} -} diff --git a/pilot/pkg/model/proxy_view.go b/pilot/pkg/model/proxy_view.go deleted file mode 100644 index 191c4a201..000000000 --- a/pilot/pkg/model/proxy_view.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "fmt" - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/util/identifier" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// ProxyView provides a restricted view of mesh endpoints for a Proxy. -type ProxyView interface { - fmt.Stringer - IsVisible(ep *IstioEndpoint) bool -} - -// ProxyViewAll is a ProxyView where all endpoints are visible. -var ProxyViewAll ProxyView = proxyViewAll{} - -type proxyViewAll struct{} - -func (v proxyViewAll) IsVisible(*IstioEndpoint) bool { - return true -} - -func (v proxyViewAll) String() string { - return "" -} - -func newProxyView(node *Proxy) ProxyView { - if node == nil || node.Metadata == nil || len(node.Metadata.RequestedNetworkView) == 0 { - return ProxyViewAll - } - - // Restrict the view to the requested networks. - return &proxyViewImpl{ - visible: sets.New(node.Metadata.RequestedNetworkView...).Insert(identifier.Undefined), - getValue: func(ep *IstioEndpoint) string { - return ep.Network.String() - }, - } -} - -type proxyViewImpl struct { - visible sets.Set - getValue func(ep *IstioEndpoint) string -} - -func (v *proxyViewImpl) IsVisible(ep *IstioEndpoint) bool { - return v.visible.Contains(v.getValue(ep)) -} - -func (v *proxyViewImpl) String() string { - return strings.Join(v.visible.SortedList(), ",") -} diff --git a/pilot/pkg/model/proxy_view_test.go b/pilot/pkg/model/proxy_view_test.go deleted file mode 100644 index 7509a8e71..000000000 --- a/pilot/pkg/model/proxy_view_test.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model_test - -import ( - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/network" -) - -func TestProxyView(t *testing.T) { - cases := []struct { - name string - networkView []string - network string - visible bool - }{ - { - name: "no views", - network: "network1", - visible: true, - }, - { - name: "network visible", - networkView: []string{"network1"}, - network: "network1", - visible: true, - }, - { - name: "network not visible", - networkView: []string{"network1"}, - network: "network2", - visible: false, - }, - { - name: "no network label", - networkView: []string{"network1"}, - network: "", - visible: true, - }, - } - - for _, c := range cases { - c := c - t.Run(c.name, func(t *testing.T) { - g := NewWithT(t) - - view := (&model.Proxy{ - Metadata: &model.NodeMetadata{ - RequestedNetworkView: c.networkView, - }, - }).GetView() - - actual := view.IsVisible(&model.IstioEndpoint{ - Network: network.ID(c.network), - }) - - g.Expect(actual).To(Equal(c.visible)) - }) - } -} diff --git a/pilot/pkg/model/push_context.go b/pilot/pkg/model/push_context.go deleted file mode 100644 index 9c31fdcb7..000000000 --- a/pilot/pkg/model/push_context.go +++ /dev/null @@ -1,2338 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "encoding/json" - "fmt" - "math" - "sort" - "strconv" - "strings" - "sync" - "time" -) - -import ( - "go.uber.org/atomic" - extensions "istio.io/api/extensions/v1alpha1" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" - "istio.io/pkg/monitoring" - "k8s.io/apimachinery/pkg/types" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/config/visibility" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// Metrics is an interface for capturing metrics on a per-node basis. -type Metrics interface { - // AddMetric will add an case to the metric for the given node. - AddMetric(metric monitoring.Metric, key string, proxyID, msg string) -} - -var _ Metrics = &PushContext{} - -// serviceIndex is an index of all services by various fields for easy access during push. -type serviceIndex struct { - // privateByNamespace are services that can reachable within the same namespace, with exportTo "." - privateByNamespace map[string][]*Service - // public are services reachable within the mesh with exportTo "*" - public []*Service - // exportedToNamespace are services that were made visible to this namespace - // by an exportTo explicitly specifying this namespace. - exportedToNamespace map[string][]*Service - - // HostnameAndNamespace has all services, indexed by hostname then namespace. - HostnameAndNamespace map[host.Name]map[string]*Service `json:"-"` - - // instancesByPort contains a map of service key and instances by port. It is stored here - // to avoid recomputations during push. This caches instanceByPort calls with empty labels. - // Call InstancesByPort directly when instances need to be filtered by actual labels. - instancesByPort map[string]map[int][]*ServiceInstance -} - -func newServiceIndex() serviceIndex { - return serviceIndex{ - public: []*Service{}, - privateByNamespace: map[string][]*Service{}, - exportedToNamespace: map[string][]*Service{}, - HostnameAndNamespace: map[host.Name]map[string]*Service{}, - instancesByPort: map[string]map[int][]*ServiceInstance{}, - } -} - -// exportToDefaults contains the default exportTo values. -type exportToDefaults struct { - service map[visibility.Instance]bool - virtualService map[visibility.Instance]bool - destinationRule map[visibility.Instance]bool -} - -// virtualServiceIndex is the index of virtual services by various fields. -type virtualServiceIndex struct { - exportedToNamespaceByGateway map[string]map[string][]config.Config - // this contains all the virtual services with exportTo "." and current namespace. The keys are namespace,gateway. - privateByNamespaceAndGateway map[string]map[string][]config.Config - // This contains all virtual services whose exportTo is "*", keyed by gateway - publicByGateway map[string][]config.Config - // root vs namespace/name ->delegate vs virtualservice gvk/namespace/name - delegates map[ConfigKey][]ConfigKey -} - -func newVirtualServiceIndex() virtualServiceIndex { - return virtualServiceIndex{ - publicByGateway: map[string][]config.Config{}, - privateByNamespaceAndGateway: map[string]map[string][]config.Config{}, - exportedToNamespaceByGateway: map[string]map[string][]config.Config{}, - delegates: map[ConfigKey][]ConfigKey{}, - } -} - -// destinationRuleIndex is the index of destination rules by various fields. -type destinationRuleIndex struct { - // namespaceLocal contains all public/private dest rules pertaining to a service defined in a given namespace. - namespaceLocal map[string]*consolidatedDestRules - // exportedByNamespace contains all dest rules pertaining to a service exported by a namespace. - exportedByNamespace map[string]*consolidatedDestRules - rootNamespaceLocal *consolidatedDestRules - // mesh/namespace dest rules to be inherited - inheritedByNamespace map[string]*consolidatedDestRule -} - -func newDestinationRuleIndex() destinationRuleIndex { - return destinationRuleIndex{ - namespaceLocal: map[string]*consolidatedDestRules{}, - exportedByNamespace: map[string]*consolidatedDestRules{}, - inheritedByNamespace: map[string]*consolidatedDestRule{}, - } -} - -// sidecarIndex is the index of sidecar rules -type sidecarIndex struct { - // sidecars for each namespace - sidecarsByNamespace map[string][]*SidecarScope - // the Sidecar for the root namespace (if present). This applies to any namespace without its own Sidecar. - rootConfig *config.Config - // computedSidecarsByNamespace contains the default sidecar for namespaces that do not have a sidecar. - // These may be DefaultSidecarScopeForNamespace if rootConfig is empty or ConvertToSidecarScope if not. - // These are lazy-loaded. Access protected by defaultSidecarMu - computedSidecarsByNamespace map[string]*SidecarScope - // gatewayDefaultSidecarsByNamespace contains the default sidecar for namespaces that do not have a sidecar, - // for gateways. - // Unlike computedSidecarsByNamespace, this is *always* the output of DefaultSidecarScopeForNamespace. - // These are lazy-loaded. Access protected by defaultSidecarMu - gatewayDefaultSidecarsByNamespace map[string]*SidecarScope - defaultSidecarMu *sync.Mutex -} - -func newSidecarIndex() sidecarIndex { - return sidecarIndex{ - sidecarsByNamespace: map[string][]*SidecarScope{}, - computedSidecarsByNamespace: map[string]*SidecarScope{}, - gatewayDefaultSidecarsByNamespace: map[string]*SidecarScope{}, - defaultSidecarMu: &sync.Mutex{}, - } -} - -// gatewayIndex is the index of gateways by various fields. -type gatewayIndex struct { - // namespace contains gateways by namespace. - namespace map[string][]config.Config - // all contains all gateways. - all []config.Config -} - -func newGatewayIndex() gatewayIndex { - return gatewayIndex{ - namespace: map[string][]config.Config{}, - all: []config.Config{}, - } -} - -// serviceMetadataIndex is the index of service metadata by various fields. -type serviceMetadataIndex struct { - namespace map[string][]*config.Config - applicationNameByNamespace map[string]map[string]*config.Config - all []*config.Config -} - -func newServiceMetadataIndex() serviceMetadataIndex { - return serviceMetadataIndex{ - namespace: map[string][]*config.Config{}, - applicationNameByNamespace: map[string]map[string]*config.Config{}, - all: []*config.Config{}, - } -} - -// serviceNameMappingIndex is the index of Service Name Mapping by various fields. -type serviceNameMappingIndex struct { - // namespace contains Service Name Mapping by namespace. - namespace map[string][]*config.Config - // interface by namespace - interfaceByNamespace map[string]map[string]*config.Config - // all contains all Service Name Mapping. - all []*config.Config -} - -func newServiceNameMappingIndex() serviceNameMappingIndex { - return serviceNameMappingIndex{ - namespace: map[string][]*config.Config{}, - interfaceByNamespace: map[string]map[string]*config.Config{}, - all: []*config.Config{}, - } -} - -// PushContext tracks the status of a push - metrics and errors. -// Metrics are reset after a push - at the beginning all -// values are zero, and when push completes the status is reset. -// The struct is exposed in a debug endpoint - fields public to allow -// easy serialization as json. -type PushContext struct { - proxyStatusMutex sync.RWMutex - // ProxyStatus is keyed by the error code, and holds a map keyed - // by the ID. - ProxyStatus map[string]map[string]ProxyPushStatus - - // Synthesized from env.Mesh - exportToDefaults exportToDefaults - - // ServiceIndex is the index of services by various fields. - ServiceIndex serviceIndex - - // ServiceAccounts contains a map of hostname and port to service accounts. - ServiceAccounts map[host.Name]map[int][]string `json:"-"` - - // virtualServiceIndex is the index of virtual services by various fields. - virtualServiceIndex virtualServiceIndex - - // destinationRuleIndex is the index of destination rules by various fields. - destinationRuleIndex destinationRuleIndex - - // gatewayIndex is the index of gateways. - gatewayIndex gatewayIndex - - // clusterLocalHosts extracted from the MeshConfig - clusterLocalHosts ClusterLocalHosts - - // sidecarIndex stores sidecar resources - sidecarIndex sidecarIndex - - // serviceMetadataIndex stores service metadata resources - serviceMetadataIndex serviceMetadataIndex - - // serviceNameMappingIndex is the index of service name mapping. - serviceNameMappingIndex serviceNameMappingIndex - - // envoy filters for each namespace including global config namespace - envoyFiltersByNamespace map[string][]*EnvoyFilterWrapper - - // wasm plugins for each namespace including global config namespace - wasmPluginsByNamespace map[string][]*WasmPluginWrapper - - // AuthnPolicies contains Authn policies by namespace. - AuthnPolicies *AuthenticationPolicies `json:"-"` - - // AuthzPolicies stores the existing authorization policies in the cluster. Could be nil if there - // are no authorization policies in the cluster. - AuthzPolicies *AuthorizationPolicies `json:"-"` - - // Telemetry stores the existing Telemetry resources for the cluster. - Telemetry *Telemetries `json:"-"` - - // ProxyConfig stores the existing ProxyConfig resources for the cluster. - ProxyConfigs *ProxyConfigs `json:"-"` - - // The following data is either a global index or used in the inbound path. - // Namespace specific views do not apply here. - - // Mesh configuration for the mesh. - Mesh *meshconfig.MeshConfig `json:"-"` - - // PushVersion describes the push version this push context was computed for - PushVersion string - - // LedgerVersion is the version of the configuration ledger - LedgerVersion string - - // JwtKeyResolver holds a reference to the JWT key resolver instance. - JwtKeyResolver *JwksResolver - - // GatewayAPIController holds a reference to the gateway API controller. - GatewayAPIController GatewayController - - // cache gateways addresses for each network - // this is mainly used for kubernetes multi-cluster scenario - networkMgr *NetworkManager - - InitDone atomic.Bool - initializeMutex sync.Mutex -} - -type consolidatedDestRules struct { - // Map of dest rule host to the list of namespaces to which this destination rule has been exported to - exportTo map[host.Name]map[visibility.Instance]bool - // Map of dest rule host and the merged destination rules for that host - destRules map[host.Name][]*consolidatedDestRule -} - -// consolidatedDestRule represents a dr and from which it is consolidated. -type consolidatedDestRule struct { - // rule is merged from the following destinationRules. - rule *config.Config - // the original dest rules from which above rule is merged. - from []types.NamespacedName -} - -// XDSUpdater is used for direct updates of the xDS model and incremental push. -// Pilot uses multiple registries - for example each K8S cluster is a registry -// instance. Each registry is responsible for tracking a set -// of endpoints associated with mesh services, and calling the EDSUpdate on changes. -// A registry may group endpoints for a service in smaller subsets - for example by -// deployment, or to deal with very large number of endpoints for a service. We want -// to avoid passing around large objects - like full list of endpoints for a registry, -// or the full list of endpoints for a service across registries, since it limits -// scalability. -// -// Future optimizations will include grouping the endpoints by labels, gateway or region to -// reduce the time when subsetting or split-horizon is used. This design assumes pilot -// tracks all endpoints in the mesh and they fit in RAM - so limit is few M endpoints. -// It is possible to split the endpoint tracking in future. -type XDSUpdater interface { - // EDSUpdate is called when the list of endpoints or labels in a Service is changed. - // For each cluster and hostname, the full list of active endpoints (including empty list) - // must be sent. The shard name is used as a key - current implementation is using the - // registry name. - EDSUpdate(shard ShardKey, hostname string, namespace string, entry []*IstioEndpoint) - - // EDSCacheUpdate is called when the list of endpoints or labels in a Service is changed. - // For each cluster and hostname, the full list of active endpoints (including empty list) - // must be sent. The shard name is used as a key - current implementation is using the - // registry name. - // Note: the difference with `EDSUpdate` is that it only update the cache rather than requesting a push - EDSCacheUpdate(shard ShardKey, hostname string, namespace string, entry []*IstioEndpoint) - - // SvcUpdate is called when a service definition is updated/deleted. - SvcUpdate(shard ShardKey, hostname string, namespace string, event Event) - - // ConfigUpdate is called to notify the XDS server of config updates and request a push. - // The requests may be collapsed and throttled. - ConfigUpdate(req *PushRequest) - - // ProxyUpdate is called to notify the XDS server to send a push to the specified proxy. - // The requests may be collapsed and throttled. - ProxyUpdate(clusterID cluster.ID, ip string) - - // RemoveShard removes all endpoints for the given shard key - RemoveShard(shardKey ShardKey) -} - -// PushRequest defines a request to push to proxies -// It is used to send updates to the config update debouncer and pass to the PushQueue. -type PushRequest struct { - // Full determines whether a full push is required or not. If false, an incremental update will be sent. - // Incremental pushes: - // * Do not recompute the push context - // * Do not recompute proxy state (such as ServiceInstances) - // * Are not reported in standard metrics such as push time - // As a result, configuration updates should never be incremental. Generally, only EDS will set this, but - // in the future SDS will as well. - Full bool - - // ConfigsUpdated keeps track of configs that have changed. - // This is used as an optimization to avoid unnecessary pushes to proxies that are scoped with a Sidecar. - // If this is empty, then all proxies will get an update. - // Otherwise only proxies depend on these configs will get an update. - // The kind of resources are defined in pkg/config/schemas. - ConfigsUpdated map[ConfigKey]struct{} - - // Push stores the push context to use for the update. This may initially be nil, as we will - // debounce changes before a PushContext is eventually created. - Push *PushContext - - // Start represents the time a push was started. This represents the time of adding to the PushQueue. - // Note that this does not include time spent debouncing. - Start time.Time - - // Reason represents the reason for requesting a push. This should only be a fixed set of values, - // to avoid unbounded cardinality in metrics. If this is not set, it may be automatically filled in later. - // There should only be multiple reasons if the push request is the result of two distinct triggers, rather than - // classifying a single trigger as having multiple reasons. - Reason []TriggerReason - - // Delta defines the resources that were added or removed as part of this push request. - // This is set only on requests from the client which change the set of resources they (un)subscribe from. - Delta ResourceDelta -} - -// ResourceDelta records the difference in requested resources by an XDS client -type ResourceDelta struct { - // Subscribed indicates the client requested these additional resources - Subscribed sets.Set - // Unsubscribed indicates the client no longer requires these resources - Unsubscribed sets.Set -} - -func (rd ResourceDelta) IsEmpty() bool { - return len(rd.Subscribed) == 0 && len(rd.Unsubscribed) == 0 -} - -type TriggerReason string - -// If adding a new reason, update xds/monitoring.go:triggerMetric -const ( - // EndpointUpdate describes a push triggered by an Endpoint change - EndpointUpdate TriggerReason = "endpoint" - // ConfigUpdate describes a push triggered by a config (generally and Istio CRD) change. - ConfigUpdate TriggerReason = "config" - // ServiceUpdate describes a push triggered by a Service change - ServiceUpdate TriggerReason = "service" - // ProxyUpdate describes a push triggered by a change to an individual proxy (such as label change) - ProxyUpdate TriggerReason = "proxy" - // GlobalUpdate describes a push triggered by a change to global config, such as mesh config - GlobalUpdate TriggerReason = "global" - // UnknownTrigger describes a push triggered by an unknown reason - UnknownTrigger TriggerReason = "unknown" - // DebugTrigger describes a push triggered for debugging - DebugTrigger TriggerReason = "debug" - // SecretTrigger describes a push triggered for a Secret change - SecretTrigger TriggerReason = "secret" - // NetworksTrigger describes a push triggered for Networks change - NetworksTrigger TriggerReason = "networks" - // ProxyRequest describes a push triggered based on proxy request - ProxyRequest TriggerReason = "proxyrequest" - // NamespaceUpdate describes a push triggered by a Namespace change - NamespaceUpdate TriggerReason = "namespace" - // ClusterUpdate describes a push triggered by a Cluster change - ClusterUpdate TriggerReason = "cluster" -) - -// Merge two update requests together -// Merge behaves similarly to a list append; usage should in the form `a = a.merge(b)`. -// Importantly, Merge may decide to allocate a new PushRequest object or reuse the existing one - both -// inputs should not be used after completion. -func (pr *PushRequest) Merge(other *PushRequest) *PushRequest { - if pr == nil { - return other - } - if other == nil { - return pr - } - - // Keep the first (older) start time - - // Merge the two reasons. Note that we shouldn't deduplicate here, or we would under count - pr.Reason = append(pr.Reason, other.Reason...) - - // If either is full we need a full push - pr.Full = pr.Full || other.Full - - // The other push context is presumed to be later and more up to date - if other.Push != nil { - pr.Push = other.Push - } - - // Do not merge when any one is empty - if len(pr.ConfigsUpdated) == 0 || len(other.ConfigsUpdated) == 0 { - pr.ConfigsUpdated = nil - } else { - for conf := range other.ConfigsUpdated { - pr.ConfigsUpdated[conf] = struct{}{} - } - } - - return pr -} - -// CopyMerge two update requests together. Unlike Merge, this will not mutate either input. -// This should be used when we are modifying a shared PushRequest (typically any time it's in the context -// of a single proxy) -func (pr *PushRequest) CopyMerge(other *PushRequest) *PushRequest { - if pr == nil { - return other - } - if other == nil { - return pr - } - - var reason []TriggerReason - if len(pr.Reason)+len(other.Reason) > 0 { - reason = make([]TriggerReason, 0, len(pr.Reason)+len(other.Reason)) - reason = append(reason, pr.Reason...) - reason = append(reason, other.Reason...) - } - merged := &PushRequest{ - // Keep the first (older) start time - Start: pr.Start, - - // If either is full we need a full push - Full: pr.Full || other.Full, - - // The other push context is presumed to be later and more up to date - Push: other.Push, - - // Merge the two reasons. Note that we shouldn't deduplicate here, or we would under count - Reason: reason, - } - - // Do not merge when any one is empty - if len(pr.ConfigsUpdated) > 0 && len(other.ConfigsUpdated) > 0 { - merged.ConfigsUpdated = make(map[ConfigKey]struct{}, len(pr.ConfigsUpdated)+len(other.ConfigsUpdated)) - for conf := range pr.ConfigsUpdated { - merged.ConfigsUpdated[conf] = struct{}{} - } - for conf := range other.ConfigsUpdated { - merged.ConfigsUpdated[conf] = struct{}{} - } - } - - return merged -} - -func (pr *PushRequest) PushReason() string { - if len(pr.Reason) == 1 && pr.Reason[0] == ProxyRequest { - return " request" - } - return "" -} - -// ProxyPushStatus represents an event captured during config push to proxies. -// It may contain additional message and the affected proxy. -type ProxyPushStatus struct { - Proxy string `json:"proxy,omitempty"` - Message string `json:"message,omitempty"` -} - -// AddMetric will add an case to the metric. -func (ps *PushContext) AddMetric(metric monitoring.Metric, key string, proxyID, msg string) { - if ps == nil { - log.Infof("Metric without context %s %v %s", key, proxyID, msg) - return - } - ps.proxyStatusMutex.Lock() - defer ps.proxyStatusMutex.Unlock() - - metricMap, f := ps.ProxyStatus[metric.Name()] - if !f { - metricMap = map[string]ProxyPushStatus{} - ps.ProxyStatus[metric.Name()] = metricMap - } - ev := ProxyPushStatus{Message: msg, Proxy: proxyID} - metricMap[key] = ev -} - -var ( - - // EndpointNoPod tracks endpoints without an associated pod. This is an error condition, since - // we can't figure out the labels. It may be a transient problem, if endpoint is processed before - // pod. - EndpointNoPod = monitoring.NewGauge( - "endpoint_no_pod", - "Endpoints without an associated pod.", - ) - - // ProxyStatusNoService represents proxies not selected by any service - // This can be normal - for workloads that act only as client, or are not covered by a Service. - // It can also be an error, for example in cases the Endpoint list of a service was not updated by the time - // the sidecar calls. - // Updated by GetProxyServiceInstances - ProxyStatusNoService = monitoring.NewGauge( - "pilot_no_ip", - "Pods not found in the endpoint table, possibly invalid.", - ) - - // ProxyStatusEndpointNotReady represents proxies found not be ready. - // Updated by GetProxyServiceInstances. Normal condition when starting - // an app with readiness, error if it doesn't change to 0. - ProxyStatusEndpointNotReady = monitoring.NewGauge( - "pilot_endpoint_not_ready", - "Endpoint found in unready state.", - ) - - // ProxyStatusConflictOutboundListenerTCPOverHTTP metric tracks number of - // wildcard TCP listeners that conflicted with existing wildcard HTTP listener on same port - ProxyStatusConflictOutboundListenerTCPOverHTTP = monitoring.NewGauge( - "pilot_conflict_outbound_listener_tcp_over_current_http", - "Number of conflicting wildcard tcp listeners with current wildcard http listener.", - ) - - // ProxyStatusConflictOutboundListenerTCPOverTCP metric tracks number of - // TCP listeners that conflicted with existing TCP listeners on same port - ProxyStatusConflictOutboundListenerTCPOverTCP = monitoring.NewGauge( - "pilot_conflict_outbound_listener_tcp_over_current_tcp", - "Number of conflicting tcp listeners with current tcp listener.", - ) - - // ProxyStatusConflictOutboundListenerHTTPOverTCP metric tracks number of - // wildcard HTTP listeners that conflicted with existing wildcard TCP listener on same port - ProxyStatusConflictOutboundListenerHTTPOverTCP = monitoring.NewGauge( - "pilot_conflict_outbound_listener_http_over_current_tcp", - "Number of conflicting wildcard http listeners with current wildcard tcp listener.", - ) - - // ProxyStatusConflictInboundListener tracks cases of multiple inbound - // listeners - 2 services selecting the same port of the pod. - ProxyStatusConflictInboundListener = monitoring.NewGauge( - "pilot_conflict_inbound_listener", - "Number of conflicting inbound listeners.", - ) - - // DuplicatedClusters tracks duplicate clusters seen while computing CDS - DuplicatedClusters = monitoring.NewGauge( - "pilot_duplicate_envoy_clusters", - "Duplicate envoy clusters caused by service entries with same hostname", - ) - - // DNSNoEndpointClusters tracks dns clusters without endpoints - DNSNoEndpointClusters = monitoring.NewGauge( - "pilot_dns_cluster_without_endpoints", - "DNS clusters without endpoints caused by the endpoint field in "+ - "STRICT_DNS type cluster is not set or the corresponding subset cannot select any endpoint", - ) - - // ProxyStatusClusterNoInstances tracks clusters (services) without workloads. - ProxyStatusClusterNoInstances = monitoring.NewGauge( - "pilot_eds_no_instances", - "Number of clusters without instances.", - ) - - // DuplicatedDomains tracks rejected VirtualServices due to duplicated hostname. - DuplicatedDomains = monitoring.NewGauge( - "pilot_vservice_dup_domain", - "Virtual services with dup domains.", - ) - - // DuplicatedSubsets tracks duplicate subsets that we rejected while merging multiple destination rules for same host - DuplicatedSubsets = monitoring.NewGauge( - "pilot_destrule_subsets", - "Duplicate subsets across destination rules for same host", - ) - - // totalVirtualServices tracks the total number of virtual service - totalVirtualServices = monitoring.NewGauge( - "pilot_virt_services", - "Total virtual services known to pilot.", - ) - - // LastPushStatus preserves the metrics and data collected during lasts global push. - // It can be used by debugging tools to inspect the push event. It will be reset after each push with the - // new version. - LastPushStatus *PushContext - // LastPushMutex will protect the LastPushStatus - LastPushMutex sync.Mutex - - // All metrics we registered. - metrics = []monitoring.Metric{ - EndpointNoPod, - ProxyStatusNoService, - ProxyStatusEndpointNotReady, - ProxyStatusConflictOutboundListenerTCPOverHTTP, - ProxyStatusConflictOutboundListenerTCPOverTCP, - ProxyStatusConflictOutboundListenerHTTPOverTCP, - ProxyStatusConflictInboundListener, - DuplicatedClusters, - ProxyStatusClusterNoInstances, - DuplicatedDomains, - DuplicatedSubsets, - } -) - -func init() { - for _, m := range metrics { - monitoring.MustRegister(m) - } - monitoring.MustRegister(totalVirtualServices) -} - -// NewPushContext creates a new PushContext structure to track push status. -func NewPushContext() *PushContext { - return &PushContext{ - ServiceIndex: newServiceIndex(), - virtualServiceIndex: newVirtualServiceIndex(), - destinationRuleIndex: newDestinationRuleIndex(), - sidecarIndex: newSidecarIndex(), - envoyFiltersByNamespace: map[string][]*EnvoyFilterWrapper{}, - gatewayIndex: newGatewayIndex(), - ProxyStatus: map[string]map[string]ProxyPushStatus{}, - ServiceAccounts: map[host.Name]map[int][]string{}, - serviceMetadataIndex: newServiceMetadataIndex(), - serviceNameMappingIndex: newServiceNameMappingIndex(), - } -} - -// AddPublicServices adds the services to context public services - mainly used in tests. -func (ps *PushContext) AddPublicServices(services []*Service) { - ps.ServiceIndex.public = append(ps.ServiceIndex.public, services...) -} - -// AddServiceInstances adds instances to the context service instances - mainly used in tests. -func (ps *PushContext) AddServiceInstances(service *Service, instances map[int][]*ServiceInstance) { - svcKey := service.Key() - for port, inst := range instances { - if _, exists := ps.ServiceIndex.instancesByPort[svcKey]; !exists { - ps.ServiceIndex.instancesByPort[svcKey] = make(map[int][]*ServiceInstance) - } - ps.ServiceIndex.instancesByPort[svcKey][port] = append(ps.ServiceIndex.instancesByPort[svcKey][port], inst...) - } -} - -// StatusJSON implements json.Marshaller, with a lock. -func (ps *PushContext) StatusJSON() ([]byte, error) { - if ps == nil { - return []byte{'{', '}'}, nil - } - ps.proxyStatusMutex.RLock() - defer ps.proxyStatusMutex.RUnlock() - return json.MarshalIndent(ps.ProxyStatus, "", " ") -} - -// OnConfigChange is called when a config change is detected. -func (ps *PushContext) OnConfigChange() { - LastPushMutex.Lock() - LastPushStatus = ps - LastPushMutex.Unlock() - ps.UpdateMetrics() -} - -// UpdateMetrics will update the prometheus metrics based on the -// current status of the push. -func (ps *PushContext) UpdateMetrics() { - ps.proxyStatusMutex.RLock() - defer ps.proxyStatusMutex.RUnlock() - - for _, pm := range metrics { - mmap := ps.ProxyStatus[pm.Name()] - pm.Record(float64(len(mmap))) - } -} - -// It is called after virtual service short host name is resolved to FQDN -func virtualServiceDestinationHosts(v *networking.VirtualService) []string { - if v == nil { - return nil - } - - var out []string - - for _, h := range v.Http { - for _, r := range h.Route { - if r.Destination != nil { - out = append(out, r.Destination.Host) - } - } - if h.Mirror != nil { - out = append(out, h.Mirror.Host) - } - } - for _, t := range v.Tcp { - for _, r := range t.Route { - if r.Destination != nil { - out = append(out, r.Destination.Host) - } - } - } - for _, t := range v.Tls { - for _, r := range t.Route { - if r.Destination != nil { - out = append(out, r.Destination.Host) - } - } - } - - return out -} - -// GatewayServices returns the set of services which are referred from the proxy gateways. -func (ps *PushContext) GatewayServices(proxy *Proxy) []*Service { - svcs := proxy.SidecarScope.services - - // MergedGateway will be nil when there are no configs in the - // system during initial installation. - if proxy.MergedGateway == nil { - return nil - } - - // host set. - hostsFromGateways := sets.New() - for _, gw := range proxy.MergedGateway.GatewayNameForServer { - for _, vsConfig := range ps.VirtualServicesForGateway(proxy.ConfigNamespace, gw) { - vs, ok := vsConfig.Spec.(*networking.VirtualService) - if !ok { // should never happen - log.Errorf("Failed in getting a virtual service: %v", vsConfig.Labels) - return svcs - } - - for _, host := range virtualServiceDestinationHosts(vs) { - hostsFromGateways.Insert(host) - } - } - } - - hostsFromMeshConfig := getHostsFromMeshConfig(ps) - hostsFromGateways.Merge(hostsFromMeshConfig) - - log.Debugf("GatewayServices: gateway %v is exposing these hosts:%v", proxy.ID, hostsFromGateways) - - gwSvcs := make([]*Service, 0, len(svcs)) - - for _, s := range svcs { - svcHost := string(s.Hostname) - - if _, ok := hostsFromGateways[svcHost]; ok { - gwSvcs = append(gwSvcs, s) - } - } - - log.Debugf("GatewayServices:: gateways len(services)=%d, len(filtered)=%d", len(svcs), len(gwSvcs)) - - return gwSvcs -} - -// add services from MeshConfig.ExtensionProviders -// TODO: include cluster from EnvoyFilter such as global ratelimit [demo](https://istio.io/latest/docs/tasks/policy-enforcement/rate-limit/#global-rate-limit) -func getHostsFromMeshConfig(ps *PushContext) sets.Set { - hostsFromMeshConfig := sets.New() - - for _, prov := range ps.Mesh.ExtensionProviders { - switch p := prov.Provider.(type) { - case *meshconfig.MeshConfig_ExtensionProvider_EnvoyExtAuthzHttp: - hostsFromMeshConfig.Insert(p.EnvoyExtAuthzHttp.Service) - case *meshconfig.MeshConfig_ExtensionProvider_EnvoyExtAuthzGrpc: - hostsFromMeshConfig.Insert(p.EnvoyExtAuthzGrpc.Service) - case *meshconfig.MeshConfig_ExtensionProvider_Zipkin: - hostsFromMeshConfig.Insert(p.Zipkin.Service) - case *meshconfig.MeshConfig_ExtensionProvider_Lightstep: - hostsFromMeshConfig.Insert(p.Lightstep.Service) - case *meshconfig.MeshConfig_ExtensionProvider_Datadog: - hostsFromMeshConfig.Insert(p.Datadog.Service) - case *meshconfig.MeshConfig_ExtensionProvider_Opencensus: - hostsFromMeshConfig.Insert(p.Opencensus.Service) - case *meshconfig.MeshConfig_ExtensionProvider_Skywalking: - hostsFromMeshConfig.Insert(p.Skywalking.Service) - case *meshconfig.MeshConfig_ExtensionProvider_EnvoyHttpAls: - hostsFromMeshConfig.Insert(p.EnvoyHttpAls.Service) - case *meshconfig.MeshConfig_ExtensionProvider_EnvoyTcpAls: - hostsFromMeshConfig.Insert(p.EnvoyTcpAls.Service) - case *meshconfig.MeshConfig_ExtensionProvider_EnvoyOtelAls: - hostsFromMeshConfig.Insert(p.EnvoyOtelAls.Service) - } - } - return hostsFromMeshConfig -} - -// servicesExportedToNamespace returns the list of services that are visible to a namespace. -// namespace "" indicates all namespaces -func (ps *PushContext) servicesExportedToNamespace(ns string) []*Service { - out := make([]*Service, 0) - - // First add private services and explicitly exportedTo services - if ns == NamespaceAll { - for _, privateServices := range ps.ServiceIndex.privateByNamespace { - out = append(out, privateServices...) - } - } else { - out = append(out, ps.ServiceIndex.privateByNamespace[ns]...) - out = append(out, ps.ServiceIndex.exportedToNamespace[ns]...) - } - - // Second add public services - out = append(out, ps.ServiceIndex.public...) - - return out -} - -// GetAllServices returns the total services within the mesh. -// Note: per proxy services should use SidecarScope.Services. -func (ps *PushContext) GetAllServices() []*Service { - return ps.servicesExportedToNamespace(NamespaceAll) -} - -// ServiceForHostname returns the service associated with a given hostname following SidecarScope -func (ps *PushContext) ServiceForHostname(proxy *Proxy, hostname host.Name) *Service { - if proxy != nil && proxy.SidecarScope != nil { - return proxy.SidecarScope.servicesByHostname[hostname] - } - - // SidecarScope shouldn't be null here. If it is, we can't disambiguate the hostname to use for a namespace, - // so the selection must be undefined. - for _, service := range ps.ServiceIndex.HostnameAndNamespace[hostname] { - return service - } - - // No service found - return nil -} - -// IsServiceVisible returns true if the input service is visible to the given namespace. -func (ps *PushContext) IsServiceVisible(service *Service, namespace string) bool { - if service == nil { - return false - } - - ns := service.Attributes.Namespace - if len(service.Attributes.ExportTo) == 0 { - if ps.exportToDefaults.service[visibility.Private] { - return ns == namespace - } else if ps.exportToDefaults.service[visibility.Public] { - return true - } - } - - return service.Attributes.ExportTo[visibility.Public] || - (service.Attributes.ExportTo[visibility.Private] && ns == namespace) || - service.Attributes.ExportTo[visibility.Instance(namespace)] -} - -// VirtualServicesForGateway lists all virtual services bound to the specified gateways -// This replaces store.VirtualServices. Used only by the gateways -// Sidecars use the egressListener.VirtualServices(). -func (ps *PushContext) VirtualServicesForGateway(proxyNamespace, gateway string) []config.Config { - res := make([]config.Config, 0, len(ps.virtualServiceIndex.privateByNamespaceAndGateway[proxyNamespace][gateway])+ - len(ps.virtualServiceIndex.exportedToNamespaceByGateway[proxyNamespace][gateway])+ - len(ps.virtualServiceIndex.publicByGateway[gateway])) - res = append(res, ps.virtualServiceIndex.privateByNamespaceAndGateway[proxyNamespace][gateway]...) - res = append(res, ps.virtualServiceIndex.exportedToNamespaceByGateway[proxyNamespace][gateway]...) - res = append(res, ps.virtualServiceIndex.publicByGateway[gateway]...) - - return res -} - -func (ps *PushContext) ServiceNameMappingsByNameSpaceAndInterfaceName(proxyNamespace, interfaceName string) *config.Config { - if namespace, exists := ps.serviceNameMappingIndex.interfaceByNamespace[proxyNamespace]; exists { - if snp, exists := namespace[interfaceName]; exists { - return snp - } - } - return nil -} - -// DelegateVirtualServicesConfigKey lists all the delegate virtual services configkeys associated with the provided virtual services -func (ps *PushContext) DelegateVirtualServicesConfigKey(vses []config.Config) []ConfigKey { - var out []ConfigKey - for _, vs := range vses { - out = append(out, ps.virtualServiceIndex.delegates[ConfigKey{Kind: gvk.VirtualService, Namespace: vs.Namespace, Name: vs.Name}]...) - } - - return out -} - -// getSidecarScope returns a SidecarScope object associated with the -// proxy. The SidecarScope object is a semi-processed view of the service -// registry, and config state associated with the sidecar crd. The scope contains -// a set of inbound and outbound listeners, services/configs per listener, -// etc. The sidecar scopes are precomputed in the initSidecarContext -// function based on the Sidecar API objects in each namespace. If there is -// no sidecar api object, a default sidecarscope is assigned to the -// namespace which enables connectivity to all services in the mesh. -// -// Callers can check if the sidecarScope is from user generated object or not -// by checking the sidecarScope.Config field, that contains the user provided config -func (ps *PushContext) getSidecarScope(proxy *Proxy, workloadLabels labels.Instance) *SidecarScope { - // Find the most specific matching sidecar config from the proxy's - // config namespace If none found, construct a sidecarConfig on the fly - // that allows the sidecar to talk to any namespace (the default - // behavior in the absence of sidecars). - if sidecars, ok := ps.sidecarIndex.sidecarsByNamespace[proxy.ConfigNamespace]; ok { - // TODO: logic to merge multiple sidecar resources - // Currently we assume that there will be only one sidecar config for a namespace. - if proxy.Type == Router { - for _, wrapper := range sidecars { - // Gateways should just have a default scope with egress: */* - if wrapper.Sidecar == nil { - return wrapper - } - } - } - if proxy.Type == SidecarProxy { - for _, wrapper := range sidecars { - if wrapper.Sidecar != nil { - sidecar := wrapper.Sidecar - // if there is no workload selector, the config applies to all workloads - // if there is a workload selector, check for matching workload labels - if sidecar.GetWorkloadSelector() != nil { - workloadSelector := labels.Instance(sidecar.GetWorkloadSelector().GetLabels()) - // exclude workload selector that not match - if !workloadSelector.SubsetOf(workloadLabels) { - continue - } - } - - // it is guaranteed sidecars with selectors are put in front - // and the sidecars are sorted by creation timestamp, - // return exact/wildcard matching one directly - return wrapper - } - // this happens at last, it is the default sidecar scope - return wrapper - } - } - } - - // We didn't have a Sidecar in the namespace. This means we should use the default - either an implicit - // default selecting everything, or pulling from the root namespace. - ps.sidecarIndex.defaultSidecarMu.Lock() - defer ps.sidecarIndex.defaultSidecarMu.Unlock() - if proxy.Type == Router { - sc, f := ps.sidecarIndex.gatewayDefaultSidecarsByNamespace[proxy.ConfigNamespace] - if f { - // We have already computed the scope for this namespace, just fetch it - return sc - } - computed := DefaultSidecarScopeForNamespace(ps, proxy.ConfigNamespace) - ps.sidecarIndex.gatewayDefaultSidecarsByNamespace[proxy.ConfigNamespace] = computed - return computed - } - sc, f := ps.sidecarIndex.computedSidecarsByNamespace[proxy.ConfigNamespace] - if f { - // We have already computed the scope for this namespace, just fetch it - return sc - } - // We need to compute this namespace - var computed *SidecarScope - if ps.sidecarIndex.rootConfig != nil { - computed = ConvertToSidecarScope(ps, ps.sidecarIndex.rootConfig, proxy.ConfigNamespace) - } else { - computed = DefaultSidecarScopeForNamespace(ps, proxy.ConfigNamespace) - // Even though we are a sidecar, we can store this as a gateway one since it could be used by a gateway - ps.sidecarIndex.gatewayDefaultSidecarsByNamespace[proxy.ConfigNamespace] = computed - } - ps.sidecarIndex.computedSidecarsByNamespace[proxy.ConfigNamespace] = computed - return computed -} - -// destinationRule returns a destination rule for a service name in a given namespace. -func (ps *PushContext) destinationRule(proxyNameSpace string, service *Service) []*consolidatedDestRule { - if service == nil { - return nil - } - // If the proxy config namespace is same as the root config namespace - // look for dest rules in the service's namespace first. This hack is needed - // because sometimes, dubbo-system tends to become the root config namespace. - // Destination rules are defined here for global purposes. We do not want these - // catch all destination rules to be the only dest rule, when processing CDS for - // proxies like the istio-ingressgateway or istio-egressgateway. - // If there are no service specific dest rules, we will end up picking up the same - // rules anyway, later in the code - - // 1. select destination rule from proxy config namespace - if proxyNameSpace != ps.Mesh.RootNamespace { - // search through the DestinationRules in proxy's namespace first - if ps.destinationRuleIndex.namespaceLocal[proxyNameSpace] != nil { - if hostname, ok := MostSpecificHostMatch(service.Hostname, - ps.destinationRuleIndex.namespaceLocal[proxyNameSpace].destRules, - ); ok { - return ps.destinationRuleIndex.namespaceLocal[proxyNameSpace].destRules[hostname] - } - } - } else { - // If this is a namespace local DR in the same namespace, this must be meant for this proxy, so we do not - // need to worry about overriding other DRs with *.local type rules here. If we ignore this, then exportTo=. in - // root namespace would always be ignored - if hostname, ok := MostSpecificHostMatch(service.Hostname, - ps.destinationRuleIndex.rootNamespaceLocal.destRules, - ); ok { - return ps.destinationRuleIndex.rootNamespaceLocal.destRules[hostname] - } - } - - // 2. select destination rule from service namespace - svcNs := service.Attributes.Namespace - - // This can happen when finding the subset labels for a proxy in root namespace. - // Because based on a pure cluster's fqdn, we do not know the service and - // construct a fake service without setting Attributes at all. - if svcNs == "" { - for _, svc := range ps.servicesExportedToNamespace(proxyNameSpace) { - if service.Hostname == svc.Hostname && svc.Attributes.Namespace != "" { - svcNs = svc.Attributes.Namespace - break - } - } - } - - // 3. if no private/public rule matched in the calling proxy's namespace, - // check the target service's namespace for exported rules - if svcNs != "" { - if out := ps.getExportedDestinationRuleFromNamespace(svcNs, service.Hostname, proxyNameSpace); out != nil { - return out - } - } - - // 4. if no public/private rule in calling proxy's namespace matched, and no public rule in the - // target service's namespace matched, search for any exported destination rule in the config root namespace - if out := ps.getExportedDestinationRuleFromNamespace(ps.Mesh.RootNamespace, service.Hostname, proxyNameSpace); out != nil { - return out - } - - // 5. service DestinationRules were merged in SetDestinationRules, return mesh/namespace rules if present - if features.EnableDestinationRuleInheritance { - // return namespace rule if present - if out := ps.destinationRuleIndex.inheritedByNamespace[proxyNameSpace]; out != nil { - return []*consolidatedDestRule{out} - } - // return mesh rule - if out := ps.destinationRuleIndex.inheritedByNamespace[ps.Mesh.RootNamespace]; out != nil { - return []*consolidatedDestRule{out} - } - } - - return nil -} - -func (ps *PushContext) getExportedDestinationRuleFromNamespace(owningNamespace string, hostname host.Name, clientNamespace string) []*consolidatedDestRule { - if ps.destinationRuleIndex.exportedByNamespace[owningNamespace] != nil { - if specificHostname, ok := MostSpecificHostMatch(hostname, - ps.destinationRuleIndex.exportedByNamespace[owningNamespace].destRules, - ); ok { - // Check if the dest rule for this host is actually exported to the proxy's (client) namespace - exportToMap := ps.destinationRuleIndex.exportedByNamespace[owningNamespace].exportTo[specificHostname] - if len(exportToMap) == 0 || exportToMap[visibility.Public] || exportToMap[visibility.Instance(clientNamespace)] { - if features.EnableDestinationRuleInheritance { - var parent *consolidatedDestRule - // client inherits global DR from its own namespace, not from the exported DR's owning namespace - // grab the client namespace DR or mesh if none exists - if parent = ps.destinationRuleIndex.inheritedByNamespace[clientNamespace]; parent == nil { - parent = ps.destinationRuleIndex.inheritedByNamespace[ps.Mesh.RootNamespace] - } - var inheritedDrList []*consolidatedDestRule - for _, child := range ps.destinationRuleIndex.exportedByNamespace[owningNamespace].destRules[specificHostname] { - inheritedDr := ps.inheritDestinationRule(parent, child) - if inheritedDr != nil { - inheritedDrList = append(inheritedDrList, inheritedDr) - } - - } - return inheritedDrList - } - if dr, ok := ps.destinationRuleIndex.exportedByNamespace[owningNamespace].destRules[specificHostname]; ok { - return dr - } - } - } - } - return nil -} - -func (ps *PushContext) ServiceMetadata(namespace, applicationName, revision string) *config.Config { - if conf, ok := ps.serviceMetadataIndex.applicationNameByNamespace[namespace][strings.ToLower(fmt.Sprintf("%s-%s", applicationName, revision))]; ok { - return conf - } - return nil -} - -// IsClusterLocal indicates whether the endpoints for the service should only be accessible to clients -// within the cluster. -func (ps *PushContext) IsClusterLocal(service *Service) bool { - if service == nil { - return false - } - return ps.clusterLocalHosts.IsClusterLocal(service.Hostname) -} - -// InitContext will initialize the data structures used for code generation. -// This should be called before starting the push, from the thread creating -// the push context. -func (ps *PushContext) InitContext(env *Environment, oldPushContext *PushContext, pushReq *PushRequest) error { - // Acquire a lock to ensure we don't concurrently initialize the same PushContext. - // If this does happen, one thread will block then exit early from InitDone=true - ps.initializeMutex.Lock() - defer ps.initializeMutex.Unlock() - if ps.InitDone.Load() { - return nil - } - - ps.Mesh = env.Mesh() - ps.LedgerVersion = env.Version() - - // Must be initialized first - // as initServiceRegistry/VirtualServices/Destrules - // use the default export map - ps.initDefaultExportMaps() - - // create new or incremental update - if pushReq == nil || oldPushContext == nil || !oldPushContext.InitDone.Load() || len(pushReq.ConfigsUpdated) == 0 { - if err := ps.createNewContext(env); err != nil { - return err - } - } else { - if err := ps.updateContext(env, oldPushContext, pushReq); err != nil { - return err - } - } - - ps.networkMgr = env.NetworkManager - - ps.clusterLocalHosts = env.ClusterLocal().GetClusterLocalHosts() - - ps.InitDone.Store(true) - return nil -} - -func (ps *PushContext) createNewContext(env *Environment) error { - if err := ps.initServiceRegistry(env); err != nil { - return err - } - - if err := ps.initKubernetesGateways(env); err != nil { - return err - } - - if err := ps.initVirtualServices(env); err != nil { - return err - } - - if err := ps.initDestinationRules(env); err != nil { - return err - } - - if err := ps.initAuthnPolicies(env); err != nil { - return err - } - - if err := ps.initAuthorizationPolicies(env); err != nil { - authzLog.Errorf("failed to initialize authorization policies: %v", err) - return err - } - - if err := ps.initTelemetry(env); err != nil { - return err - } - - if err := ps.initProxyConfigs(env); err != nil { - return err - } - - if err := ps.initWasmPlugins(env); err != nil { - return err - } - - if err := ps.initEnvoyFilters(env); err != nil { - return err - } - - if err := ps.initGateways(env); err != nil { - return err - } - - if err := ps.initServiceMetadata(env); err != nil { - return err - } - - // Must be initialized in the end - if err := ps.initSidecarScopes(env); err != nil { - return err - } - // service name mapping context init - if err := ps.initServiceNameMappings(env); err != nil { - return err - } - return nil -} - -func (ps *PushContext) updateContext( - env *Environment, - oldPushContext *PushContext, - pushReq *PushRequest) error { - var servicesChanged, virtualServicesChanged, destinationRulesChanged, gatewayChanged, - authnChanged, authzChanged, envoyFiltersChanged, sidecarsChanged, telemetryChanged, gatewayAPIChanged, - wasmPluginsChanged, proxyConfigsChanged, servicenamemappingsChanged bool - - for conf := range pushReq.ConfigsUpdated { - switch conf.Kind { - case gvk.ServiceEntry: - servicesChanged = true - case gvk.DestinationRule: - destinationRulesChanged = true - case gvk.VirtualService: - virtualServicesChanged = true - case gvk.Gateway: - gatewayChanged = true - case gvk.Sidecar: - sidecarsChanged = true - case gvk.WasmPlugin: - wasmPluginsChanged = true - case gvk.EnvoyFilter: - envoyFiltersChanged = true - case gvk.AuthorizationPolicy: - authzChanged = true - case gvk.RequestAuthentication, - gvk.PeerAuthentication: - authnChanged = true - case gvk.HTTPRoute, gvk.TCPRoute, gvk.GatewayClass, gvk.KubernetesGateway, gvk.TLSRoute, gvk.ReferencePolicy: - gatewayAPIChanged = true - // VS and GW are derived from gatewayAPI, so if it changed we need to update those as well - virtualServicesChanged = true - gatewayChanged = true - case gvk.Telemetry: - telemetryChanged = true - case gvk.ProxyConfig: - proxyConfigsChanged = true - case gvk.ServiceNameMapping: - servicenamemappingsChanged = true - } - } - - if servicesChanged { - // Services have changed. initialize service registry - if err := ps.initServiceRegistry(env); err != nil { - return err - } - } else { - // make sure we copy over things that would be generated in initServiceRegistry - ps.ServiceIndex = oldPushContext.ServiceIndex - ps.ServiceAccounts = oldPushContext.ServiceAccounts - } - - if servicesChanged || gatewayAPIChanged { - // Gateway status depends on services, so recompute if they change as well - if err := ps.initKubernetesGateways(env); err != nil { - return err - } - } - - if virtualServicesChanged { - if err := ps.initVirtualServices(env); err != nil { - return err - } - } else { - ps.virtualServiceIndex = oldPushContext.virtualServiceIndex - } - - if destinationRulesChanged { - if err := ps.initDestinationRules(env); err != nil { - return err - } - } else { - ps.destinationRuleIndex = oldPushContext.destinationRuleIndex - } - - if authnChanged { - if err := ps.initAuthnPolicies(env); err != nil { - return err - } - } else { - ps.AuthnPolicies = oldPushContext.AuthnPolicies - } - - if authzChanged { - if err := ps.initAuthorizationPolicies(env); err != nil { - authzLog.Errorf("failed to initialize authorization policies: %v", err) - return err - } - } else { - ps.AuthzPolicies = oldPushContext.AuthzPolicies - } - - if telemetryChanged { - if err := ps.initTelemetry(env); err != nil { - return err - } - } else { - ps.Telemetry = oldPushContext.Telemetry - } - - if proxyConfigsChanged { - if err := ps.initProxyConfigs(env); err != nil { - return err - } - } else { - ps.ProxyConfigs = oldPushContext.ProxyConfigs - } - - if wasmPluginsChanged { - if err := ps.initWasmPlugins(env); err != nil { - return err - } - } else { - ps.wasmPluginsByNamespace = oldPushContext.wasmPluginsByNamespace - } - - if envoyFiltersChanged { - if err := ps.initEnvoyFilters(env); err != nil { - return err - } - } else { - ps.envoyFiltersByNamespace = oldPushContext.envoyFiltersByNamespace - } - - if gatewayChanged { - if err := ps.initGateways(env); err != nil { - return err - } - } else { - ps.gatewayIndex = oldPushContext.gatewayIndex - } - - // Must be initialized in the end - // Sidecars need to be updated if services, virtual services, destination rules, or the sidecar configs change - if servicesChanged || virtualServicesChanged || destinationRulesChanged || sidecarsChanged { - if err := ps.initSidecarScopes(env); err != nil { - return err - } - } else { - ps.sidecarIndex.sidecarsByNamespace = oldPushContext.sidecarIndex.sidecarsByNamespace - } - if servicenamemappingsChanged { - if err := ps.initServiceNameMappings(env); err != nil { - return err - } - } else { - ps.serviceNameMappingIndex = oldPushContext.serviceNameMappingIndex - } - - return nil -} - -// Caches list of services in the registry, and creates a map -// of hostname to service -func (ps *PushContext) initServiceRegistry(env *Environment) error { - // Sort the services in order of creation. - allServices := SortServicesByCreationTime(env.Services()) - for _, s := range allServices { - svcKey := s.Key() - // Precache instances - for _, port := range s.Ports { - if _, ok := ps.ServiceIndex.instancesByPort[svcKey]; !ok { - ps.ServiceIndex.instancesByPort[svcKey] = make(map[int][]*ServiceInstance) - } - instances := make([]*ServiceInstance, 0) - instances = append(instances, env.InstancesByPort(s, port.Port, nil)...) - ps.ServiceIndex.instancesByPort[svcKey][port.Port] = instances - } - - if _, f := ps.ServiceIndex.HostnameAndNamespace[s.Hostname]; !f { - ps.ServiceIndex.HostnameAndNamespace[s.Hostname] = map[string]*Service{} - } - ps.ServiceIndex.HostnameAndNamespace[s.Hostname][s.Attributes.Namespace] = s - - ns := s.Attributes.Namespace - if len(s.Attributes.ExportTo) == 0 { - if ps.exportToDefaults.service[visibility.Private] { - ps.ServiceIndex.privateByNamespace[ns] = append(ps.ServiceIndex.privateByNamespace[ns], s) - } else if ps.exportToDefaults.service[visibility.Public] { - ps.ServiceIndex.public = append(ps.ServiceIndex.public, s) - } - } else { - // if service has exportTo ~ - i.e. not visible to anyone, ignore all exportTos - // if service has exportTo *, make public and ignore all other exportTos - // if service has exportTo ., replace with current namespace - if s.Attributes.ExportTo[visibility.Public] { - ps.ServiceIndex.public = append(ps.ServiceIndex.public, s) - continue - } else if s.Attributes.ExportTo[visibility.None] { - continue - } else { - // . or other namespaces - for exportTo := range s.Attributes.ExportTo { - if exportTo == visibility.Private || string(exportTo) == ns { - // exportTo with same namespace is effectively private - ps.ServiceIndex.privateByNamespace[ns] = append(ps.ServiceIndex.privateByNamespace[ns], s) - } else { - // exportTo is a specific target namespace - ps.ServiceIndex.exportedToNamespace[string(exportTo)] = append(ps.ServiceIndex.exportedToNamespace[string(exportTo)], s) - } - } - } - } - } - - ps.initServiceAccounts(env, allServices) - - return nil -} - -// SortServicesByCreationTime sorts the list of services in ascending order by their creation time (if available). -func SortServicesByCreationTime(services []*Service) []*Service { - sort.SliceStable(services, func(i, j int) bool { - // If creation time is the same, then behavior is nondeterministic. In this case, we can - // pick an arbitrary but consistent ordering based on name and namespace, which is unique. - // CreationTimestamp is stored in seconds, so this is not uncommon. - if services[i].CreationTime.Equal(services[j].CreationTime) { - in := services[i].Attributes.Name + "." + services[i].Attributes.Namespace - jn := services[j].Attributes.Name + "." + services[j].Attributes.Namespace - return in < jn - } - return services[i].CreationTime.Before(services[j].CreationTime) - }) - return services -} - -// Caches list of service accounts in the registry -func (ps *PushContext) initServiceAccounts(env *Environment, services []*Service) { - for _, svc := range services { - if ps.ServiceAccounts[svc.Hostname] == nil { - ps.ServiceAccounts[svc.Hostname] = map[int][]string{} - } - for _, port := range svc.Ports { - if port.Protocol == protocol.UDP { - continue - } - ps.ServiceAccounts[svc.Hostname][port.Port] = env.GetIstioServiceAccounts(svc, []int{port.Port}) - } - } -} - -// Caches list of authentication policies -func (ps *PushContext) initAuthnPolicies(env *Environment) error { - // Init beta policy. - var err error - ps.AuthnPolicies, err = initAuthenticationPolicies(env) - return err -} - -// Caches list of virtual services -func (ps *PushContext) initVirtualServices(env *Environment) error { - ps.virtualServiceIndex.exportedToNamespaceByGateway = map[string]map[string][]config.Config{} - ps.virtualServiceIndex.privateByNamespaceAndGateway = map[string]map[string][]config.Config{} - ps.virtualServiceIndex.publicByGateway = map[string][]config.Config{} - - virtualServices, err := env.List(gvk.VirtualService, NamespaceAll) - if err != nil { - return err - } - - // values returned from ConfigStore.List are immutable. - // Therefore, we make a copy - vservices := make([]config.Config, len(virtualServices)) - - for i := range vservices { - vservices[i] = virtualServices[i].DeepCopy() - } - - totalVirtualServices.Record(float64(len(virtualServices))) - - // TODO(rshriram): parse each virtual service and maintain a map of the - // virtualservice name, the list of registry hosts in the VS and non - // registry DNS names in the VS. This should cut down processing in - // the RDS code. See separateVSHostsAndServices in route/route.go - sortConfigByCreationTime(vservices) - - // convert all shortnames in virtual services into FQDNs - for _, r := range vservices { - resolveVirtualServiceShortnames(r.Spec.(*networking.VirtualService), r.Meta) - } - - vservices, ps.virtualServiceIndex.delegates = mergeVirtualServicesIfNeeded(vservices, ps.exportToDefaults.virtualService) - - for _, virtualService := range vservices { - ns := virtualService.Namespace - rule := virtualService.Spec.(*networking.VirtualService) - gwNames := getGatewayNames(rule) - if len(rule.ExportTo) == 0 { - // No exportTo in virtualService. Use the global default - // We only honor ., * - if ps.exportToDefaults.virtualService[visibility.Private] { - if _, f := ps.virtualServiceIndex.privateByNamespaceAndGateway[ns]; !f { - ps.virtualServiceIndex.privateByNamespaceAndGateway[ns] = map[string][]config.Config{} - } - // add to local namespace only - private := ps.virtualServiceIndex.privateByNamespaceAndGateway - for _, gw := range gwNames { - private[ns][gw] = append(private[ns][gw], virtualService) - } - } else if ps.exportToDefaults.virtualService[visibility.Public] { - for _, gw := range gwNames { - ps.virtualServiceIndex.publicByGateway[gw] = append(ps.virtualServiceIndex.publicByGateway[gw], virtualService) - } - } - } else { - exportToMap := make(map[visibility.Instance]bool) - for _, e := range rule.ExportTo { - exportToMap[visibility.Instance(e)] = true - } - // if vs has exportTo ~ - i.e. not visible to anyone, ignore all exportTos - // if vs has exportTo *, make public and ignore all other exportTos - // if vs has exportTo ., replace with current namespace - if exportToMap[visibility.Public] { - for _, gw := range gwNames { - ps.virtualServiceIndex.publicByGateway[gw] = append(ps.virtualServiceIndex.publicByGateway[gw], virtualService) - } - continue - } else if exportToMap[visibility.None] { - // not possible - continue - } else { - // . or other namespaces - for exportTo := range exportToMap { - if exportTo == visibility.Private || string(exportTo) == ns { - if _, f := ps.virtualServiceIndex.privateByNamespaceAndGateway[ns]; !f { - ps.virtualServiceIndex.privateByNamespaceAndGateway[ns] = map[string][]config.Config{} - } - // add to local namespace only - for _, gw := range gwNames { - ps.virtualServiceIndex.privateByNamespaceAndGateway[ns][gw] = append(ps.virtualServiceIndex.privateByNamespaceAndGateway[ns][gw], virtualService) - } - } else { - if _, f := ps.virtualServiceIndex.exportedToNamespaceByGateway[string(exportTo)]; !f { - ps.virtualServiceIndex.exportedToNamespaceByGateway[string(exportTo)] = map[string][]config.Config{} - } - exported := ps.virtualServiceIndex.exportedToNamespaceByGateway - // add to local namespace only - for _, gw := range gwNames { - exported[string(exportTo)][gw] = append(exported[string(exportTo)][gw], virtualService) - } - } - } - } - } - } - - return nil -} - -var meshGateways = []string{constants.IstioMeshGateway} - -func getGatewayNames(vs *networking.VirtualService) []string { - if len(vs.Gateways) == 0 { - return meshGateways - } - res := make([]string, 0, len(vs.Gateways)) - res = append(res, vs.Gateways...) - return res -} - -func (ps *PushContext) initDefaultExportMaps() { - ps.exportToDefaults.destinationRule = make(map[visibility.Instance]bool) - if ps.Mesh.DefaultDestinationRuleExportTo != nil { - for _, e := range ps.Mesh.DefaultDestinationRuleExportTo { - ps.exportToDefaults.destinationRule[visibility.Instance(e)] = true - } - } else { - // default to * - ps.exportToDefaults.destinationRule[visibility.Public] = true - } - - ps.exportToDefaults.service = make(map[visibility.Instance]bool) - if ps.Mesh.DefaultServiceExportTo != nil { - for _, e := range ps.Mesh.DefaultServiceExportTo { - ps.exportToDefaults.service[visibility.Instance(e)] = true - } - } else { - ps.exportToDefaults.service[visibility.Public] = true - } - - ps.exportToDefaults.virtualService = make(map[visibility.Instance]bool) - if ps.Mesh.DefaultVirtualServiceExportTo != nil { - for _, e := range ps.Mesh.DefaultVirtualServiceExportTo { - ps.exportToDefaults.virtualService[visibility.Instance(e)] = true - } - } else { - ps.exportToDefaults.virtualService[visibility.Public] = true - } -} - -// initSidecarScopes synthesizes Sidecar CRDs into objects called -// SidecarScope. The SidecarScope object is a semi-processed view of the -// service registry, and config state associated with the sidecar CRD. The -// scope contains a set of inbound and outbound listeners, services/configs -// per listener, etc. The sidecar scopes are precomputed based on the -// Sidecar API objects in each namespace. If there is no sidecar api object -// for a namespace, a default sidecarscope is assigned to the namespace -// which enables connectivity to all services in the mesh. -// -// When proxies connect to Pilot, we identify the sidecar scope associated -// with the proxy and derive listeners/routes/clusters based on the sidecar -// scope. -func (ps *PushContext) initSidecarScopes(env *Environment) error { - sidecarConfigs, err := env.List(gvk.Sidecar, NamespaceAll) - if err != nil { - return err - } - - sortConfigByCreationTime(sidecarConfigs) - - sidecarConfigWithSelector := make([]config.Config, 0) - sidecarConfigWithoutSelector := make([]config.Config, 0) - sidecarsWithoutSelectorByNamespace := sets.New() - for _, sidecarConfig := range sidecarConfigs { - sidecar := sidecarConfig.Spec.(*networking.Sidecar) - if sidecar.WorkloadSelector != nil { - sidecarConfigWithSelector = append(sidecarConfigWithSelector, sidecarConfig) - } else { - sidecarsWithoutSelectorByNamespace.Insert(sidecarConfig.Namespace) - sidecarConfigWithoutSelector = append(sidecarConfigWithoutSelector, sidecarConfig) - } - } - - sidecarNum := len(sidecarConfigs) - sidecarConfigs = make([]config.Config, 0, sidecarNum) - // sidecars with selector take preference - sidecarConfigs = append(sidecarConfigs, sidecarConfigWithSelector...) - sidecarConfigs = append(sidecarConfigs, sidecarConfigWithoutSelector...) - - // Hold reference root namespace's sidecar config - // Root namespace can have only one sidecar config object - // Currently we expect that it has no workloadSelectors - var rootNSConfig *config.Config - ps.sidecarIndex.sidecarsByNamespace = make(map[string][]*SidecarScope, sidecarNum) - for i, sidecarConfig := range sidecarConfigs { - ps.sidecarIndex.sidecarsByNamespace[sidecarConfig.Namespace] = append(ps.sidecarIndex.sidecarsByNamespace[sidecarConfig.Namespace], - ConvertToSidecarScope(ps, &sidecarConfig, sidecarConfig.Namespace)) - if rootNSConfig == nil && sidecarConfig.Namespace == ps.Mesh.RootNamespace && - sidecarConfig.Spec.(*networking.Sidecar).WorkloadSelector == nil { - rootNSConfig = &sidecarConfigs[i] - } - } - ps.sidecarIndex.rootConfig = rootNSConfig - - return nil -} - -// Split out of DestinationRule expensive conversions - once per push. -func (ps *PushContext) initDestinationRules(env *Environment) error { - configs, err := env.List(gvk.DestinationRule, NamespaceAll) - if err != nil { - return err - } - - // values returned from ConfigStore.List are immutable. - // Therefore, we make a copy - destRules := make([]config.Config, len(configs)) - for i := range destRules { - destRules[i] = configs[i].DeepCopy() - } - - ps.SetDestinationRules(destRules) - return nil -} - -func newConsolidatedDestRules() *consolidatedDestRules { - return &consolidatedDestRules{ - exportTo: map[host.Name]map[visibility.Instance]bool{}, - destRules: map[host.Name][]*consolidatedDestRule{}, - } -} - -// SetDestinationRules is updates internal structures using a set of configs. -// Split out of DestinationRule expensive conversions, computed once per push. -// This also allows tests to inject a config without having the mock. -// This will not work properly for Sidecars, which will precompute their destination rules on init -func (ps *PushContext) SetDestinationRules(configs []config.Config) { - // Sort by time first. So if two destination rule have top level traffic policies - // we take the first one. - sortConfigByCreationTime(configs) - namespaceLocalDestRules := make(map[string]*consolidatedDestRules) - exportedDestRulesByNamespace := make(map[string]*consolidatedDestRules) - rootNamespaceLocalDestRules := newConsolidatedDestRules() - inheritedConfigs := make(map[string]*consolidatedDestRule) - - for i := range configs { - rule := configs[i].Spec.(*networking.DestinationRule) - - if features.EnableDestinationRuleInheritance && rule.Host == "" { - if t, ok := inheritedConfigs[configs[i].Namespace]; ok { - log.Warnf("Namespace/mesh-level DestinationRule is already defined for %q at time %v."+ - " Ignore %q which was created at time %v", - configs[i].Namespace, t.rule.CreationTimestamp, configs[i].Name, configs[i].CreationTimestamp) - continue - } - inheritedConfigs[configs[i].Namespace] = convertConsolidatedDestRule(&configs[i]) - } - - rule.Host = string(ResolveShortnameToFQDN(rule.Host, configs[i].Meta)) - exportToMap := make(map[visibility.Instance]bool) - - // destination rules with workloadSelector should not be exported to other namespaces - if rule.GetWorkloadSelector() == nil { - for _, e := range rule.ExportTo { - exportToMap[visibility.Instance(e)] = true - } - } else { - exportToMap[visibility.Private] = true - } - - // add only if the dest rule is exported with . or * or explicit exportTo containing this namespace - // The global exportTo doesn't matter here (its either . or * - both of which are applicable here) - if len(exportToMap) == 0 || exportToMap[visibility.Public] || exportToMap[visibility.Private] || - exportToMap[visibility.Instance(configs[i].Namespace)] { - // Store in an index for the config's namespace - // a proxy from this namespace will first look here for the destination rule for a given service - // This pool consists of both public/private destination rules. - if _, exist := namespaceLocalDestRules[configs[i].Namespace]; !exist { - namespaceLocalDestRules[configs[i].Namespace] = newConsolidatedDestRules() - } - // Merge this destination rule with any public/private dest rules for same host in the same namespace - // If there are no duplicates, the dest rule will be added to the list - ps.mergeDestinationRule(namespaceLocalDestRules[configs[i].Namespace], configs[i], exportToMap) - } - - isPrivateOnly := false - // No exportTo in destinationRule. Use the global default - // We only honor . and * - if len(exportToMap) == 0 && ps.exportToDefaults.destinationRule[visibility.Private] { - isPrivateOnly = true - } else if len(exportToMap) == 1 && (exportToMap[visibility.Private] || exportToMap[visibility.Instance(configs[i].Namespace)]) { - isPrivateOnly = true - } - - if !isPrivateOnly { - if _, exist := exportedDestRulesByNamespace[configs[i].Namespace]; !exist { - exportedDestRulesByNamespace[configs[i].Namespace] = newConsolidatedDestRules() - } - // Merge this destination rule with any other exported dest rule for the same host in the same namespace - // If there are no duplicates, the dest rule will be added to the list - ps.mergeDestinationRule(exportedDestRulesByNamespace[configs[i].Namespace], configs[i], exportToMap) - } else if configs[i].Namespace == ps.Mesh.RootNamespace { - // Keep track of private root namespace destination rules - ps.mergeDestinationRule(rootNamespaceLocalDestRules, configs[i], exportToMap) - } - } - - // precompute DestinationRules with inherited fields - if features.EnableDestinationRuleInheritance { - globalRule := inheritedConfigs[ps.Mesh.RootNamespace] - for ns := range namespaceLocalDestRules { - nsRule := inheritedConfigs[ns] - inheritedRule := ps.inheritDestinationRule(globalRule, nsRule) - for hostname, cfgList := range namespaceLocalDestRules[ns].destRules { - for i, cfg := range cfgList { - namespaceLocalDestRules[ns].destRules[hostname][i] = ps.inheritDestinationRule(inheritedRule, cfg) - } - } - // update namespace rule after it has been merged with mesh rule - inheritedConfigs[ns] = inheritedRule - } - // can't precalculate exportedDestRulesByNamespace since we don't know all the client namespaces in advance - // inheritance is performed in getExportedDestinationRuleFromNamespace - } - - ps.destinationRuleIndex.namespaceLocal = namespaceLocalDestRules - ps.destinationRuleIndex.exportedByNamespace = exportedDestRulesByNamespace - ps.destinationRuleIndex.rootNamespaceLocal = rootNamespaceLocalDestRules - ps.destinationRuleIndex.inheritedByNamespace = inheritedConfigs -} - -func (ps *PushContext) initAuthorizationPolicies(env *Environment) error { - var err error - if ps.AuthzPolicies, err = GetAuthorizationPolicies(env); err != nil { - authzLog.Errorf("failed to initialize authorization policies: %v", err) - return err - } - return nil -} - -func (ps *PushContext) initTelemetry(env *Environment) (err error) { - if ps.Telemetry, err = getTelemetries(env); err != nil { - telemetryLog.Errorf("failed to initialize telemetry: %v", err) - return - } - return -} - -func (ps *PushContext) initProxyConfigs(env *Environment) error { - var err error - if ps.ProxyConfigs, err = GetProxyConfigs(env.ConfigStore, env.Mesh()); err != nil { - pclog.Errorf("failed to initialize proxy configs: %v", err) - return err - } - return nil -} - -// pre computes WasmPlugins per namespace -func (ps *PushContext) initWasmPlugins(env *Environment) error { - wasmplugins, err := env.List(gvk.WasmPlugin, NamespaceAll) - if err != nil { - return err - } - - sortConfigByCreationTime(wasmplugins) - ps.wasmPluginsByNamespace = map[string][]*WasmPluginWrapper{} - for _, plugin := range wasmplugins { - if pluginWrapper := convertToWasmPluginWrapper(plugin); pluginWrapper != nil { - ps.wasmPluginsByNamespace[plugin.Namespace] = append(ps.wasmPluginsByNamespace[plugin.Namespace], pluginWrapper) - } - } - - return nil -} - -// WasmPlugins return the WasmPluginWrappers of a proxy -func (ps *PushContext) WasmPlugins(proxy *Proxy) map[extensions.PluginPhase][]*WasmPluginWrapper { - if proxy == nil { - return nil - } - matchedPlugins := make(map[extensions.PluginPhase][]*WasmPluginWrapper) - // First get all the extension configs from the config root namespace - // and then add the ones from proxy's own namespace - if ps.Mesh.RootNamespace != "" { - // if there is no workload selector, the config applies to all workloads - // if there is a workload selector, check for matching workload labels - for _, plugin := range ps.wasmPluginsByNamespace[ps.Mesh.RootNamespace] { - if plugin.Selector == nil || labels.Instance(plugin.Selector.MatchLabels).SubsetOf(proxy.Metadata.Labels) { - matchedPlugins[plugin.Phase] = append(matchedPlugins[plugin.Phase], plugin) - } - } - } - - // To prevent duplicate extensions in case root namespace equals proxy's namespace - if proxy.ConfigNamespace != ps.Mesh.RootNamespace { - for _, plugin := range ps.wasmPluginsByNamespace[proxy.ConfigNamespace] { - if plugin.Selector == nil || labels.Instance(plugin.Selector.MatchLabels).SubsetOf(proxy.Metadata.Labels) { - matchedPlugins[plugin.Phase] = append(matchedPlugins[plugin.Phase], plugin) - } - } - } - - // sort slices by priority - for i, slice := range matchedPlugins { - sort.SliceStable(slice, func(i, j int) bool { - iPriority := int64(math.MinInt64) - if prio := slice[i].Priority; prio != nil { - iPriority = prio.Value - } - jPriority := int64(math.MinInt64) - if prio := slice[j].Priority; prio != nil { - jPriority = prio.Value - } - return iPriority > jPriority - }) - matchedPlugins[i] = slice - } - - return matchedPlugins -} - -// pre computes envoy filters per namespace -func (ps *PushContext) initEnvoyFilters(env *Environment) error { - envoyFilterConfigs, err := env.List(gvk.EnvoyFilter, NamespaceAll) - if err != nil { - return err - } - - sort.Slice(envoyFilterConfigs, func(i, j int) bool { - ifilter := envoyFilterConfigs[i].Spec.(*networking.EnvoyFilter) - jfilter := envoyFilterConfigs[j].Spec.(*networking.EnvoyFilter) - if ifilter.Priority != jfilter.Priority { - return ifilter.Priority < jfilter.Priority - } - // If priority is same fallback to name and creation timestamp, else use priority. - // If creation time is the same, then behavior is nondeterministic. In this case, we can - // pick an arbitrary but consistent ordering based on name and namespace, which is unique. - // CreationTimestamp is stored in seconds, so this is not uncommon. - if envoyFilterConfigs[i].CreationTimestamp != envoyFilterConfigs[j].CreationTimestamp { - return envoyFilterConfigs[i].CreationTimestamp.Before(envoyFilterConfigs[j].CreationTimestamp) - } - in := envoyFilterConfigs[i].Name + "." + envoyFilterConfigs[i].Namespace - jn := envoyFilterConfigs[j].Name + "." + envoyFilterConfigs[j].Namespace - return in < jn - }) - - ps.envoyFiltersByNamespace = make(map[string][]*EnvoyFilterWrapper) - for _, envoyFilterConfig := range envoyFilterConfigs { - efw := convertToEnvoyFilterWrapper(&envoyFilterConfig) - if _, exists := ps.envoyFiltersByNamespace[envoyFilterConfig.Namespace]; !exists { - ps.envoyFiltersByNamespace[envoyFilterConfig.Namespace] = make([]*EnvoyFilterWrapper, 0) - } - ps.envoyFiltersByNamespace[envoyFilterConfig.Namespace] = append(ps.envoyFiltersByNamespace[envoyFilterConfig.Namespace], efw) - } - return nil -} - -// EnvoyFilters return the merged EnvoyFilterWrapper of a proxy -func (ps *PushContext) EnvoyFilters(proxy *Proxy) *EnvoyFilterWrapper { - // this should never happen - if proxy == nil { - return nil - } - var matchedEnvoyFilters []*EnvoyFilterWrapper - // EnvoyFilters supports inheritance (global ones plus namespace local ones). - // First get all the filter configs from the config root namespace - // and then add the ones from proxy's own namespace - if ps.Mesh.RootNamespace != "" { - matchedEnvoyFilters = ps.getMatchedEnvoyFilters(proxy, ps.Mesh.RootNamespace) - } - - // To prevent duplicate envoyfilters in case root namespace equals proxy's namespace - if proxy.ConfigNamespace != ps.Mesh.RootNamespace { - matched := ps.getMatchedEnvoyFilters(proxy, proxy.ConfigNamespace) - matchedEnvoyFilters = append(matchedEnvoyFilters, matched...) - } - - var out *EnvoyFilterWrapper - if len(matchedEnvoyFilters) > 0 { - out = &EnvoyFilterWrapper{ - // no need populate workloadSelector, as it is not used later. - Patches: make(map[networking.EnvoyFilter_ApplyTo][]*EnvoyFilterConfigPatchWrapper), - } - // merge EnvoyFilterWrapper - for _, efw := range matchedEnvoyFilters { - for applyTo, cps := range efw.Patches { - for _, cp := range cps { - if proxyMatch(proxy, cp) { - out.Patches[applyTo] = append(out.Patches[applyTo], cp) - } - } - } - } - } - - return out -} - -// if there is no workload selector, the config applies to all workloads -// if there is a workload selector, check for matching workload labels -func (ps *PushContext) getMatchedEnvoyFilters(proxy *Proxy, namespaces string) []*EnvoyFilterWrapper { - matchedEnvoyFilters := make([]*EnvoyFilterWrapper, 0) - for _, efw := range ps.envoyFiltersByNamespace[namespaces] { - if efw.workloadSelector == nil || efw.workloadSelector.SubsetOf(proxy.Metadata.Labels) { - matchedEnvoyFilters = append(matchedEnvoyFilters, efw) - } - } - return matchedEnvoyFilters -} - -// HasEnvoyFilters checks if an EnvoyFilter exists with the given name at the given namespace. -func (ps *PushContext) HasEnvoyFilters(name, namespace string) bool { - for _, efw := range ps.envoyFiltersByNamespace[namespace] { - if efw.Name == name { - return true - } - } - return false -} - -// pre computes gateways per namespace -func (ps *PushContext) initGateways(env *Environment) error { - gatewayConfigs, err := env.List(gvk.Gateway, NamespaceAll) - if err != nil { - return err - } - - sortConfigByCreationTime(gatewayConfigs) - - if features.ScopeGatewayToNamespace { - ps.gatewayIndex.namespace = make(map[string][]config.Config) - for _, gatewayConfig := range gatewayConfigs { - if _, exists := ps.gatewayIndex.namespace[gatewayConfig.Namespace]; !exists { - ps.gatewayIndex.namespace[gatewayConfig.Namespace] = make([]config.Config, 0) - } - ps.gatewayIndex.namespace[gatewayConfig.Namespace] = append(ps.gatewayIndex.namespace[gatewayConfig.Namespace], gatewayConfig) - } - } else { - ps.gatewayIndex.all = gatewayConfigs - } - return nil -} - -func (ps *PushContext) initServiceMetadata(env *Environment) error { - metadataConfig, err := env.List(gvk.ServiceMetadata, NamespaceAll) - if err != nil { - return err - } - - sortConfigByCreationTime(metadataConfig) - - ps.serviceMetadataIndex.namespace = make(map[string][]*config.Config) - ps.serviceMetadataIndex.applicationNameByNamespace = make(map[string]map[string]*config.Config) - ps.serviceMetadataIndex.all = make([]*config.Config, 0) - - for _, conf := range metadataConfig { - if _, ok := ps.serviceMetadataIndex.namespace[conf.Namespace]; !ok { - ps.serviceMetadataIndex.namespace[conf.Namespace] = make([]*config.Config, 0) - } - - if _, ok := ps.serviceMetadataIndex.applicationNameByNamespace[conf.Namespace]; !ok { - ps.serviceMetadataIndex.applicationNameByNamespace[conf.Namespace] = make(map[string]*config.Config, 0) - } - - ps.serviceMetadataIndex.namespace[conf.Namespace] = append(ps.serviceMetadataIndex.namespace[conf.Namespace], &conf) - ps.serviceMetadataIndex.applicationNameByNamespace[conf.Namespace][conf.Name] = &conf - ps.serviceMetadataIndex.all = append(ps.serviceMetadataIndex.all, &conf) - } - - return nil -} - -// InternalGatewayServiceAnnotation represents the hostname of the service a gateway will use. This is -// only used internally to transfer information from the Kubernetes Gateway API to the Istio Gateway API -// which does not have a field to represent this. -// The format is a comma separated list of hostnames. For example, "ingress.dubbo-system.svc.cluster.local,ingress.example.com" -// The Gateway will apply to all ServiceInstances of these services, *in the same namespace as the Gateway*. -const InternalGatewayServiceAnnotation = "internal.istio.io/gateway-service" - -type gatewayWithInstances struct { - gateway config.Config - // If true, ports that are not present in any instance will be used directly (without targetPort translation) - // This supports the legacy behavior of selecting gateways by pod label selector - legacyGatewaySelector bool - instances []*ServiceInstance -} - -func (ps *PushContext) mergeGateways(proxy *Proxy) *MergedGateway { - // this should never happen - if proxy == nil { - return nil - } - gatewayInstances := make([]gatewayWithInstances, 0) - - var configs []config.Config - if features.ScopeGatewayToNamespace { - configs = ps.gatewayIndex.namespace[proxy.ConfigNamespace] - } else { - configs = ps.gatewayIndex.all - } - - for _, cfg := range configs { - gw := cfg.Spec.(*networking.Gateway) - if gwsvcstr, f := cfg.Annotations[InternalGatewayServiceAnnotation]; f { - gwsvcs := strings.Split(gwsvcstr, ",") - known := map[host.Name]struct{}{} - for _, g := range gwsvcs { - known[host.Name(g)] = struct{}{} - } - matchingInstances := make([]*ServiceInstance, 0, len(proxy.ServiceInstances)) - for _, si := range proxy.ServiceInstances { - if _, f := known[si.Service.Hostname]; f && si.Service.Attributes.Namespace == cfg.Namespace { - matchingInstances = append(matchingInstances, si) - } - } - // Only if we have a matching instance should we apply the configuration - if len(matchingInstances) > 0 { - gatewayInstances = append(gatewayInstances, gatewayWithInstances{cfg, false, matchingInstances}) - } - } else if gw.GetSelector() == nil { - // no selector. Applies to all workloads asking for the gateway - gatewayInstances = append(gatewayInstances, gatewayWithInstances{cfg, true, proxy.ServiceInstances}) - } else { - gatewaySelector := labels.Instance(gw.GetSelector()) - if gatewaySelector.SubsetOf(proxy.Metadata.Labels) { - gatewayInstances = append(gatewayInstances, gatewayWithInstances{cfg, true, proxy.ServiceInstances}) - } - } - } - - if len(gatewayInstances) == 0 { - return nil - } - - return MergeGateways(gatewayInstances, proxy, ps) -} - -// GatewayContext contains a minimal subset of push context functionality to be exposed to GatewayAPIControllers -type GatewayContext struct { - ps *PushContext -} - -func NewGatewayContext(ps *PushContext) GatewayContext { - return GatewayContext{ps} -} - -// ResolveGatewayInstances attempts to resolve all instances that a gateway will be exposed on. -// Note: this function considers *all* instances of the service; its possible those instances will not actually be properly functioning -// gateways, so this is not 100% accurate, but sufficient to expose intent to users. -// The actual configuration generation is done on a per-workload basis and will get the exact set of matched instances for that workload. -// Three sets are exposed: -// * Internal addresses (ie istio-ingressgateway.dubbo-system.svc.cluster.local:80). -// * External addresses (ie 1.2.3.4), this comes from LoadBalancer services. There may be multiple in some cases (especially multi cluster). -// * Warnings for references that could not be resolved. These are intended to be user facing. -func (gc GatewayContext) ResolveGatewayInstances(namespace string, gwsvcs []string, servers []*networking.Server) (internal, external, warns []string) { - ports := map[int]struct{}{} - for _, s := range servers { - ports[int(s.Port.Number)] = struct{}{} - } - foundInternal := sets.New() - foundExternal := sets.New() - warnings := []string{} - for _, g := range gwsvcs { - svc, f := gc.ps.ServiceIndex.HostnameAndNamespace[host.Name(g)][namespace] - if !f { - otherNamespaces := []string{} - for ns := range gc.ps.ServiceIndex.HostnameAndNamespace[host.Name(g)] { - otherNamespaces = append(otherNamespaces, `"`+ns+`"`) // Wrap in quotes for output - } - if len(otherNamespaces) > 0 { - sort.Strings(otherNamespaces) - warnings = append(warnings, fmt.Sprintf("hostname %q not found in namespace %q, but it was found in namespace(s) %v", - g, namespace, strings.Join(otherNamespaces, ", "))) - } else { - warnings = append(warnings, fmt.Sprintf("hostname %q not found", g)) - } - continue - } - svcKey := svc.Key() - for port := range ports { - instances := gc.ps.ServiceIndex.instancesByPort[svcKey][port] - if len(instances) > 0 { - foundInternal.Insert(fmt.Sprintf("%s:%d", g, port)) - // Fetch external IPs from all clusters - svc.Attributes.ClusterExternalAddresses.ForEach(func(c cluster.ID, externalIPs []string) { - foundExternal.InsertAll(externalIPs...) - }) - } else { - if instancesEmpty(gc.ps.ServiceIndex.instancesByPort[svcKey]) { - warnings = append(warnings, fmt.Sprintf("no instances found for hostname %q", g)) - } else { - hintPort := sets.New() - for _, instances := range gc.ps.ServiceIndex.instancesByPort[svcKey] { - for _, i := range instances { - if i.Endpoint.EndpointPort == uint32(port) { - hintPort.Insert(strconv.Itoa(i.ServicePort.Port)) - } - } - } - if len(hintPort) > 0 { - warnings = append(warnings, fmt.Sprintf( - "port %d not found for hostname %q (hint: the service port should be specified, not the workload port. Did you mean one of these ports: %v?)", - port, g, hintPort.SortedList())) - } else { - warnings = append(warnings, fmt.Sprintf("port %d not found for hostname %q", port, g)) - } - } - } - } - } - sort.Strings(warnings) - return foundInternal.SortedList(), foundExternal.SortedList(), warnings -} - -func instancesEmpty(m map[int][]*ServiceInstance) bool { - for _, instances := range m { - if len(instances) > 0 { - return false - } - } - return true -} - -func (ps *PushContext) NetworkManager() *NetworkManager { - return ps.networkMgr -} - -// BestEffortInferServiceMTLSMode infers the mTLS mode for the service + port from all authentication -// policies (both alpha and beta) in the system. The function always returns MTLSUnknown for external service. -// The result is a best effort. It is because the PeerAuthentication is workload-based, this function is unable -// to compute the correct service mTLS mode without knowing service to workload binding. For now, this -// function uses only mesh and namespace level PeerAuthentication and ignore workload & port level policies. -// This function is used to give a hint for auto-mTLS configuration on client side. -func (ps *PushContext) BestEffortInferServiceMTLSMode(tp *networking.TrafficPolicy, service *Service, port *Port) MutualTLSMode { - if service.MeshExternal { - // Only need the authentication mTLS mode when service is not external. - return MTLSUnknown - } - - // For passthrough traffic (headless service or explicitly defined in DestinationRule), we look at the instances - // If ALL instances have a sidecar, we enable TLS, otherwise we disable - // TODO(https://github.com/istio/istio/issues/27376) enable mixed deployments - // A service with passthrough resolution is always passthrough, regardless of the TrafficPolicy. - if service.Resolution == Passthrough || tp.GetLoadBalancer().GetSimple() == networking.LoadBalancerSettings_PASSTHROUGH { - instances := ps.ServiceInstancesByPort(service, port.Port, nil) - if len(instances) == 0 { - return MTLSDisable - } - for _, i := range instances { - // Infer mTls disabled if any of the endpoint is with tls disabled - if i.Endpoint.TLSMode == DisabledTLSModeLabel { - return MTLSDisable - } - } - } - - // 2. check mTLS settings from beta policy (i.e PeerAuthentication) at namespace / mesh level. - // If the mode is not unknown, use it. - if serviceMTLSMode := ps.AuthnPolicies.GetNamespaceMutualTLSMode(service.Attributes.Namespace); serviceMTLSMode != MTLSUnknown { - return serviceMTLSMode - } - - // Fallback to permissive. - return MTLSPermissive -} - -// ServiceInstancesByPort returns the cached instances by port if it exists. -func (ps *PushContext) ServiceInstancesByPort(svc *Service, port int, labels labels.Instance) []*ServiceInstance { - out := []*ServiceInstance{} - if instances, exists := ps.ServiceIndex.instancesByPort[svc.Key()][port]; exists { - // Use cached version of instances by port when labels are empty. - if len(labels) == 0 { - return instances - } - // If there are labels, we will filter instances by pod labels. - for _, instance := range instances { - // check that one of the input labels is a subset of the labels - if labels.SubsetOf(instance.Endpoint.Labels) { - out = append(out, instance) - } - } - } - - return out -} - -// initKubernetesGateways initializes Kubernetes gateway-api objects -func (ps *PushContext) initKubernetesGateways(env *Environment) error { - if env.GatewayAPIController != nil { - ps.GatewayAPIController = env.GatewayAPIController - return env.GatewayAPIController.Recompute(GatewayContext{ps}) - } - return nil -} - -// Split out of ServiceNameMapping expensive conversions - once per push. -func (ps *PushContext) initServiceNameMappings(env *Environment) error { - configs, err := env.List(gvk.ServiceNameMapping, NamespaceAll) - if err != nil { - return err - } - - // values returned from ConfigStore.List are immutable. - // Therefore, we make a copy - snpMappings := make([]*config.Config, len(configs)) - for i := range snpMappings { - deepCopy := configs[i].DeepCopy() - snpMappings[i] = &deepCopy - } - for _, snp := range snpMappings { - byNamespace := ps.serviceNameMappingIndex.namespace - if _, exists := byNamespace[snp.Namespace]; !exists { - byNamespace[snp.Namespace] = make([]*config.Config, 0) - } - byNamespace[snp.Namespace] = append(byNamespace[snp.Namespace], snp) - - interfaceByNamespace := ps.serviceNameMappingIndex.interfaceByNamespace - if _, exists := interfaceByNamespace[snp.Namespace]; !exists { - interfaceByNamespace[snp.Namespace] = make(map[string]*config.Config, 0) - } - mapping := snp.Spec.(*extensions.ServiceNameMapping) - interfaceByNamespace[snp.Namespace][mapping.GetInterfaceName()] = snp - } - ps.serviceNameMappingIndex.all = snpMappings - return nil -} - -// ReferenceAllowed determines if a given resource (of type `kind` and name `resourceName`) can be -// accessed by `namespace`, based of specific reference policies. -// Note: this function only determines if a reference is *explicitly* allowed; the reference may not require -// explicitly authorization to be made at all in most cases. Today, this only is for allowing cross-namespace -// secret access. -func (ps *PushContext) ReferenceAllowed(kind config.GroupVersionKind, resourceName string, namespace string) bool { - // Currently, only Secret has reference policy, and only implemented by Gateway API controller. - switch kind { - case gvk.Secret: - if ps.GatewayAPIController != nil { - return ps.GatewayAPIController.SecretAllowed(resourceName, namespace) - } - default: - } - return false -} diff --git a/pilot/pkg/model/push_context_test.go b/pilot/pkg/model/push_context_test.go deleted file mode 100644 index ee152d84f..000000000 --- a/pilot/pkg/model/push_context_test.go +++ /dev/null @@ -1,2443 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "fmt" - "reflect" - "regexp" - "sort" - "sync" - "testing" - "time" -) - -import ( - "github.com/golang/protobuf/ptypes/wrappers" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - . "github.com/onsi/gomega" - "go.uber.org/atomic" - "google.golang.org/protobuf/testing/protocmp" - "google.golang.org/protobuf/types/known/durationpb" - "google.golang.org/protobuf/types/known/structpb" - extensions "istio.io/api/extensions/v1alpha1" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" - securityBeta "istio.io/api/security/v1beta1" - selectorpb "istio.io/api/type/v1beta1" - "k8s.io/apimachinery/pkg/types" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/config/visibility" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -func TestMergeUpdateRequest(t *testing.T) { - push0 := &PushContext{} - // trivially different push contexts just for testing - push1 := &PushContext{ProxyStatus: make(map[string]map[string]ProxyPushStatus)} - - var t0 time.Time - t1 := t0.Add(time.Minute) - - cases := []struct { - name string - left *PushRequest - right *PushRequest - merged PushRequest - }{ - { - "left nil", - nil, - &PushRequest{Full: true}, - PushRequest{Full: true}, - }, - { - "right nil", - &PushRequest{Full: true}, - nil, - PushRequest{Full: true}, - }, - { - "simple merge", - &PushRequest{ - Full: true, - Push: push0, - Start: t0, - ConfigsUpdated: map[ConfigKey]struct{}{ - {Kind: config.GroupVersionKind{Kind: "cfg1"}, Namespace: "ns1"}: {}, - }, - Reason: []TriggerReason{ServiceUpdate, ServiceUpdate}, - }, - &PushRequest{ - Full: false, - Push: push1, - Start: t1, - ConfigsUpdated: map[ConfigKey]struct{}{ - {Kind: config.GroupVersionKind{Kind: "cfg2"}, Namespace: "ns2"}: {}, - }, - Reason: []TriggerReason{EndpointUpdate}, - }, - PushRequest{ - Full: true, - Push: push1, - Start: t0, - ConfigsUpdated: map[ConfigKey]struct{}{ - {Kind: config.GroupVersionKind{Kind: "cfg1"}, Namespace: "ns1"}: {}, - {Kind: config.GroupVersionKind{Kind: "cfg2"}, Namespace: "ns2"}: {}, - }, - Reason: []TriggerReason{ServiceUpdate, ServiceUpdate, EndpointUpdate}, - }, - }, - { - "skip config type merge: one empty", - &PushRequest{Full: true, ConfigsUpdated: nil}, - &PushRequest{Full: true, ConfigsUpdated: map[ConfigKey]struct{}{{ - Kind: config.GroupVersionKind{Kind: "cfg2"}, - }: {}}}, - PushRequest{Full: true, ConfigsUpdated: nil, Reason: nil}, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - got := tt.left.CopyMerge(tt.right) - if !reflect.DeepEqual(&tt.merged, got) { - t.Fatalf("expected %v, got %v", &tt.merged, got) - } - got = tt.left.Merge(tt.right) - if !reflect.DeepEqual(&tt.merged, got) { - t.Fatalf("expected %v, got %v", &tt.merged, got) - } - }) - } -} - -func TestConcurrentMerge(t *testing.T) { - reqA := &PushRequest{Reason: make([]TriggerReason, 0, 100)} - reqB := &PushRequest{Reason: []TriggerReason{ServiceUpdate, ProxyUpdate}} - for i := 0; i < 50; i++ { - go func() { - reqA.CopyMerge(reqB) - }() - } - if len(reqA.Reason) != 0 { - t.Fatalf("reqA modified: %v", reqA.Reason) - } - if len(reqB.Reason) != 2 { - t.Fatalf("reqB modified: %v", reqB.Reason) - } -} - -func TestEnvoyFilters(t *testing.T) { - proxyVersionRegex := regexp.MustCompile(`1\.4.*`) - envoyFilters := []*EnvoyFilterWrapper{ - { - Name: "ef1", - workloadSelector: map[string]string{"app": "v1"}, - Patches: map[networking.EnvoyFilter_ApplyTo][]*EnvoyFilterConfigPatchWrapper{ - networking.EnvoyFilter_LISTENER: { - { - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Proxy: &networking.EnvoyFilter_ProxyMatch{ - ProxyVersion: "1\\.4.*", - }, - }, - ProxyVersionRegex: proxyVersionRegex, - }, - }, - }, - }, - { - Name: "ef2", - workloadSelector: map[string]string{"app": "v1"}, - Patches: map[networking.EnvoyFilter_ApplyTo][]*EnvoyFilterConfigPatchWrapper{ - networking.EnvoyFilter_CLUSTER: { - { - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Proxy: &networking.EnvoyFilter_ProxyMatch{ - ProxyVersion: `1\\.4.*`, - }, - }, - ProxyVersionRegex: proxyVersionRegex, - }, - }, - }, - }, - } - - push := &PushContext{ - Mesh: &meshconfig.MeshConfig{ - RootNamespace: "dubbo-system", - }, - envoyFiltersByNamespace: map[string][]*EnvoyFilterWrapper{ - "dubbo-system": envoyFilters, - "test-ns": envoyFilters, - }, - } - - if !push.HasEnvoyFilters("ef1", "test-ns") { - t.Errorf("Check presence of EnvoyFilter ef1 at test-ns got false, want true") - } - if push.HasEnvoyFilters("ef3", "test-ns") { - t.Errorf("Check presence of EnvoyFilter ef3 at test-ns got true, want false") - } - - cases := []struct { - name string - proxy *Proxy - expectedListenerPatches int - expectedClusterPatches int - }{ - { - name: "proxy matches two envoyfilters", - proxy: &Proxy{ - Metadata: &NodeMetadata{IstioVersion: "1.4.0", Labels: map[string]string{"app": "v1"}}, - ConfigNamespace: "test-ns", - }, - expectedListenerPatches: 2, - expectedClusterPatches: 2, - }, - { - name: "proxy in root namespace matches an envoyfilter", - proxy: &Proxy{ - Metadata: &NodeMetadata{IstioVersion: "1.4.0", Labels: map[string]string{"app": "v1"}}, - ConfigNamespace: "dubbo-system", - }, - expectedListenerPatches: 1, - expectedClusterPatches: 1, - }, - - { - name: "proxy matches no envoyfilter", - proxy: &Proxy{ - Metadata: &NodeMetadata{IstioVersion: "1.4.0", Labels: map[string]string{"app": "v2"}}, - ConfigNamespace: "test-ns", - }, - expectedListenerPatches: 0, - expectedClusterPatches: 0, - }, - { - name: "proxy matches envoyfilter in root ns", - proxy: &Proxy{ - Metadata: &NodeMetadata{IstioVersion: "1.4.0", Labels: map[string]string{"app": "v1"}}, - ConfigNamespace: "test-n2", - }, - expectedListenerPatches: 1, - expectedClusterPatches: 1, - }, - { - name: "proxy version matches no envoyfilters", - proxy: &Proxy{ - Metadata: &NodeMetadata{IstioVersion: "1.3.0", Labels: map[string]string{"app": "v1"}}, - ConfigNamespace: "test-ns", - }, - expectedListenerPatches: 0, - expectedClusterPatches: 0, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - filter := push.EnvoyFilters(tt.proxy) - if filter == nil { - if tt.expectedClusterPatches != 0 || tt.expectedListenerPatches != 0 { - t.Errorf("Got no envoy filter") - } - return - } - if len(filter.Patches[networking.EnvoyFilter_CLUSTER]) != tt.expectedClusterPatches { - t.Errorf("Expect %d envoy filter cluster patches, but got %d", tt.expectedClusterPatches, len(filter.Patches[networking.EnvoyFilter_CLUSTER])) - } - if len(filter.Patches[networking.EnvoyFilter_LISTENER]) != tt.expectedListenerPatches { - t.Errorf("Expect %d envoy filter listener patches, but got %d", tt.expectedListenerPatches, len(filter.Patches[networking.EnvoyFilter_LISTENER])) - } - }) - } -} - -func TestEnvoyFilterOrder(t *testing.T) { - env := &Environment{} - store := istioConfigStore{ConfigStore: NewFakeStore()} - - ctime := time.Now() - - envoyFilters := []config.Config{ - { - Meta: config.Meta{Name: "default-priority", Namespace: "testns-1", GroupVersionKind: gvk.EnvoyFilter}, - Spec: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - Patch: &networking.EnvoyFilter_Patch{}, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Proxy: &networking.EnvoyFilter_ProxyMatch{ProxyVersion: `foobar`}, - }, - }, - }, - }, - }, - { - Meta: config.Meta{Name: "default-priority", Namespace: "testns", GroupVersionKind: gvk.EnvoyFilter}, - Spec: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - Patch: &networking.EnvoyFilter_Patch{}, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Proxy: &networking.EnvoyFilter_ProxyMatch{ProxyVersion: `foobar`}, - }, - }, - }, - }, - }, - { - Meta: config.Meta{Name: "b-medium-priority", Namespace: "testns-1", GroupVersionKind: gvk.EnvoyFilter, CreationTimestamp: ctime}, - Spec: &networking.EnvoyFilter{ - Priority: 10, - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - Patch: &networking.EnvoyFilter_Patch{}, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Proxy: &networking.EnvoyFilter_ProxyMatch{ProxyVersion: `foobar`}, - }, - }, - }, - }, - }, - { - Meta: config.Meta{Name: "a-medium-priority", Namespace: "testns-1", GroupVersionKind: gvk.EnvoyFilter, CreationTimestamp: ctime}, - Spec: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - Patch: &networking.EnvoyFilter_Patch{}, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Proxy: &networking.EnvoyFilter_ProxyMatch{ProxyVersion: `foobar`}, - }, - }, - }, - }, - }, - { - Meta: config.Meta{Name: "b-medium-priority", Namespace: "testns", GroupVersionKind: gvk.EnvoyFilter, CreationTimestamp: ctime}, - Spec: &networking.EnvoyFilter{ - Priority: 10, - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - Patch: &networking.EnvoyFilter_Patch{}, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Proxy: &networking.EnvoyFilter_ProxyMatch{ProxyVersion: `foobar`}, - }, - }, - }, - }, - }, - { - Meta: config.Meta{Name: "a-medium-priority", Namespace: "testns", GroupVersionKind: gvk.EnvoyFilter, CreationTimestamp: ctime}, - Spec: &networking.EnvoyFilter{ - Priority: 10, - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - Patch: &networking.EnvoyFilter_Patch{}, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Proxy: &networking.EnvoyFilter_ProxyMatch{ProxyVersion: `foobar`}, - }, - }, - }, - }, - }, - { - Meta: config.Meta{Name: "a-low-priority", Namespace: "testns", GroupVersionKind: gvk.EnvoyFilter, CreationTimestamp: time.Now()}, - Spec: &networking.EnvoyFilter{ - Priority: 20, - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - Patch: &networking.EnvoyFilter_Patch{}, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Proxy: &networking.EnvoyFilter_ProxyMatch{ProxyVersion: `foobar`}, - }, - }, - }, - }, - }, - { - Meta: config.Meta{Name: "b-low-priority", Namespace: "testns", GroupVersionKind: gvk.EnvoyFilter, CreationTimestamp: ctime}, - Spec: &networking.EnvoyFilter{ - Priority: 20, - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - Patch: &networking.EnvoyFilter_Patch{}, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Proxy: &networking.EnvoyFilter_ProxyMatch{ProxyVersion: `foobar`}, - }, - }, - }, - }, - }, - { - Meta: config.Meta{Name: "high-priority", Namespace: "testns", GroupVersionKind: gvk.EnvoyFilter}, - Spec: &networking.EnvoyFilter{ - Priority: -1, - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - Patch: &networking.EnvoyFilter_Patch{}, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Proxy: &networking.EnvoyFilter_ProxyMatch{ProxyVersion: `foobar`}, - }, - }, - }, - }, - }, - { - Meta: config.Meta{Name: "super-high-priority", Namespace: "testns", GroupVersionKind: gvk.EnvoyFilter}, - Spec: &networking.EnvoyFilter{ - Priority: -10, - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - Patch: &networking.EnvoyFilter_Patch{}, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Proxy: &networking.EnvoyFilter_ProxyMatch{ProxyVersion: `foobar`}, - }, - }, - }, - }, - }, - } - - expectedns := []string{ - "testns/super-high-priority", "testns/high-priority", "testns/default-priority", "testns/a-medium-priority", - "testns/b-medium-priority", "testns/b-low-priority", "testns/a-low-priority", - } - - expectedns1 := []string{"testns-1/default-priority", "testns-1/a-medium-priority", "testns-1/b-medium-priority"} - - for _, cfg := range envoyFilters { - _, _ = store.Create(cfg) - } - env.ConfigStore = &store - m := mesh.DefaultMeshConfig() - env.Watcher = mesh.NewFixedWatcher(m) - env.Init() - - // Init a new push context - pc := NewPushContext() - if err := pc.initEnvoyFilters(env); err != nil { - t.Fatal(err) - } - gotns := make([]string, 0) - for _, filter := range pc.envoyFiltersByNamespace["testns"] { - gotns = append(gotns, filter.Keys()...) - } - gotns1 := make([]string, 0) - for _, filter := range pc.envoyFiltersByNamespace["testns-1"] { - gotns1 = append(gotns1, filter.Keys()...) - } - if !reflect.DeepEqual(expectedns, gotns) { - t.Errorf("Envoy filters are not ordered as expected. expected: %v got: %v", expectedns, gotns) - } - if !reflect.DeepEqual(expectedns1, gotns1) { - t.Errorf("Envoy filters are not ordered as expected. expected: %v got: %v", expectedns1, gotns1) - } -} - -func TestWasmPlugins(t *testing.T) { - env := &Environment{} - store := istioConfigStore{ConfigStore: NewFakeStore()} - - wasmPlugins := map[string]config.Config{ - "invalid-type": { - Meta: config.Meta{Name: "invalid-type", Namespace: constants.IstioSystemNamespace, GroupVersionKind: gvk.WasmPlugin}, - Spec: &networking.DestinationRule{}, - }, - "invalid-url": { - Meta: config.Meta{Name: "invalid-url", Namespace: constants.IstioSystemNamespace, GroupVersionKind: gvk.WasmPlugin}, - Spec: &extensions.WasmPlugin{ - Phase: extensions.PluginPhase_AUTHN, - Priority: &wrappers.Int64Value{Value: 5}, - Url: "notavalid%%Url;", - }, - }, - "authn-low-prio-all": { - Meta: config.Meta{Name: "authn-low-prio-all", Namespace: "testns-1", GroupVersionKind: gvk.WasmPlugin}, - Spec: &extensions.WasmPlugin{ - Phase: extensions.PluginPhase_AUTHN, - Priority: &wrappers.Int64Value{Value: 10}, - Url: "file:///etc/istio/filters/authn.wasm", - PluginConfig: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "test": { - Kind: &structpb.Value_StringValue{StringValue: "test"}, - }, - }, - }, - Sha256: "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2", - }, - }, - "global-authn-low-prio-ingress": { - Meta: config.Meta{Name: "global-authn-low-prio-ingress", Namespace: constants.IstioSystemNamespace, GroupVersionKind: gvk.WasmPlugin}, - Spec: &extensions.WasmPlugin{ - Phase: extensions.PluginPhase_AUTHN, - Priority: &wrappers.Int64Value{Value: 5}, - Selector: &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{ - "istio": "ingressgateway", - }, - }, - }, - }, - "authn-med-prio-all": { - Meta: config.Meta{Name: "authn-med-prio-all", Namespace: "testns-1", GroupVersionKind: gvk.WasmPlugin}, - Spec: &extensions.WasmPlugin{ - Phase: extensions.PluginPhase_AUTHN, - Priority: &wrappers.Int64Value{Value: 50}, - }, - }, - "global-authn-high-prio-app": { - Meta: config.Meta{Name: "global-authn-high-prio-app", Namespace: constants.IstioSystemNamespace, GroupVersionKind: gvk.WasmPlugin}, - Spec: &extensions.WasmPlugin{ - Phase: extensions.PluginPhase_AUTHN, - Priority: &wrappers.Int64Value{Value: 1000}, - Selector: &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "productpage", - }, - }, - }, - }, - "global-authz-med-prio-app": { - Meta: config.Meta{Name: "global-authz-med-prio-app", Namespace: constants.IstioSystemNamespace, GroupVersionKind: gvk.WasmPlugin}, - Spec: &extensions.WasmPlugin{ - Phase: extensions.PluginPhase_AUTHZ, - Priority: &wrappers.Int64Value{Value: 50}, - Selector: &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "productpage", - }, - }, - }, - }, - "authz-high-prio-ingress": { - Meta: config.Meta{Name: "authz-high-prio-ingress", Namespace: "testns-2", GroupVersionKind: gvk.WasmPlugin}, - Spec: &extensions.WasmPlugin{ - Phase: extensions.PluginPhase_AUTHZ, - Priority: &wrappers.Int64Value{Value: 1000}, - }, - }, - } - - testCases := []struct { - name string - node *Proxy - expectedExtensions map[extensions.PluginPhase][]*WasmPluginWrapper - }{ - { - name: "nil proxy", - node: nil, - expectedExtensions: nil, - }, - { - name: "nomatch", - node: &Proxy{ - ConfigNamespace: "other", - Metadata: &NodeMetadata{}, - }, - expectedExtensions: map[extensions.PluginPhase][]*WasmPluginWrapper{}, - }, - { - name: "ingress", - node: &Proxy{ - ConfigNamespace: "other", - Metadata: &NodeMetadata{ - Labels: map[string]string{ - "istio": "ingressgateway", - }, - }, - }, - expectedExtensions: map[extensions.PluginPhase][]*WasmPluginWrapper{ - extensions.PluginPhase_AUTHN: { - convertToWasmPluginWrapper(wasmPlugins["global-authn-low-prio-ingress"]), - }, - }, - }, - { - name: "ingress-testns-1", - node: &Proxy{ - ConfigNamespace: "testns-1", - Metadata: &NodeMetadata{ - Labels: map[string]string{ - "istio": "ingressgateway", - }, - }, - }, - expectedExtensions: map[extensions.PluginPhase][]*WasmPluginWrapper{ - extensions.PluginPhase_AUTHN: { - convertToWasmPluginWrapper(wasmPlugins["authn-med-prio-all"]), - convertToWasmPluginWrapper(wasmPlugins["authn-low-prio-all"]), - convertToWasmPluginWrapper(wasmPlugins["global-authn-low-prio-ingress"]), - }, - }, - }, - { - name: "testns-2", - node: &Proxy{ - ConfigNamespace: "testns-2", - Metadata: &NodeMetadata{ - Labels: map[string]string{ - "app": "productpage", - }, - }, - }, - expectedExtensions: map[extensions.PluginPhase][]*WasmPluginWrapper{ - extensions.PluginPhase_AUTHN: { - convertToWasmPluginWrapper(wasmPlugins["global-authn-high-prio-app"]), - }, - extensions.PluginPhase_AUTHZ: { - convertToWasmPluginWrapper(wasmPlugins["authz-high-prio-ingress"]), - convertToWasmPluginWrapper(wasmPlugins["global-authz-med-prio-app"]), - }, - }, - }, - } - - for _, config := range wasmPlugins { - store.Create(config) - } - env.ConfigStore = &store - m := mesh.DefaultMeshConfig() - env.Watcher = mesh.NewFixedWatcher(m) - env.Init() - - // Init a new push context - pc := NewPushContext() - pc.Mesh = m - if err := pc.initWasmPlugins(env); err != nil { - t.Fatal(err) - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - result := pc.WasmPlugins(tc.node) - if !reflect.DeepEqual(tc.expectedExtensions, result) { - t.Errorf("WasmPlugins did not match expectations\n\ngot: %v\n\nexpected: %v", result, tc.expectedExtensions) - } - }) - } -} - -func TestServiceIndex(t *testing.T) { - g := NewWithT(t) - env := &Environment{} - store := istioConfigStore{ConfigStore: NewFakeStore()} - - env.ConfigStore = &store - env.ServiceDiscovery = &localServiceDiscovery{ - services: []*Service{ - { - Hostname: "svc-unset", - Ports: allPorts, - Attributes: ServiceAttributes{ - Namespace: "test1", - }, - }, - { - Hostname: "svc-public", - Ports: allPorts, - Attributes: ServiceAttributes{ - Namespace: "test1", - ExportTo: map[visibility.Instance]bool{visibility.Public: true}, - }, - }, - { - Hostname: "svc-private", - Ports: allPorts, - Attributes: ServiceAttributes{ - Namespace: "test1", - ExportTo: map[visibility.Instance]bool{visibility.Private: true}, - }, - }, - { - Hostname: "svc-none", - Ports: allPorts, - Attributes: ServiceAttributes{ - Namespace: "test1", - ExportTo: map[visibility.Instance]bool{visibility.None: true}, - }, - }, - { - Hostname: "svc-namespace", - Ports: allPorts, - Attributes: ServiceAttributes{ - Namespace: "test1", - ExportTo: map[visibility.Instance]bool{"namespace": true}, - }, - }, - }, - serviceInstances: []*ServiceInstance{{ - Endpoint: &IstioEndpoint{ - Address: "192.168.1.2", - EndpointPort: 8000, - TLSMode: DisabledTLSModeLabel, - }, - }}, - } - m := mesh.DefaultMeshConfig() - env.Watcher = mesh.NewFixedWatcher(m) - env.Init() - - // Init a new push context - pc := NewPushContext() - if err := pc.InitContext(env, nil, nil); err != nil { - t.Fatal(err) - } - si := pc.ServiceIndex - - // Should have all 5 services - g.Expect(si.instancesByPort).To(HaveLen(5)) - g.Expect(si.HostnameAndNamespace).To(HaveLen(5)) - - // Should just have "namespace" - g.Expect(si.exportedToNamespace).To(HaveLen(1)) - g.Expect(serviceNames(si.exportedToNamespace["namespace"])).To(Equal([]string{"svc-namespace"})) - - g.Expect(serviceNames(si.public)).To(Equal([]string{"svc-public", "svc-unset"})) - - // Should just have "test1" - g.Expect(si.privateByNamespace).To(HaveLen(1)) - g.Expect(serviceNames(si.privateByNamespace["test1"])).To(Equal([]string{"svc-private"})) -} - -func TestIsServiceVisible(t *testing.T) { - targetNamespace := "foo" - cases := []struct { - name string - pushContext *PushContext - service *Service - expect bool - }{ - { - name: "service whose namespace is foo has no exportTo map with global private", - pushContext: &PushContext{ - exportToDefaults: exportToDefaults{ - service: map[visibility.Instance]bool{ - visibility.Private: true, - }, - }, - }, - service: &Service{ - Attributes: ServiceAttributes{ - Namespace: "foo", - }, - }, - expect: true, - }, - { - name: "service whose namespace is bar has no exportTo map with global private", - pushContext: &PushContext{ - exportToDefaults: exportToDefaults{ - service: map[visibility.Instance]bool{ - visibility.Private: true, - }, - }, - }, - service: &Service{ - Attributes: ServiceAttributes{ - Namespace: "bar", - }, - }, - expect: false, - }, - { - name: "service whose namespace is bar has no exportTo map with global public", - pushContext: &PushContext{ - exportToDefaults: exportToDefaults{ - service: map[visibility.Instance]bool{ - visibility.Public: true, - }, - }, - }, - service: &Service{ - Attributes: ServiceAttributes{ - Namespace: "bar", - }, - }, - expect: true, - }, - { - name: "service whose namespace is foo has exportTo map with private", - pushContext: &PushContext{}, - service: &Service{ - Attributes: ServiceAttributes{ - Namespace: "foo", - ExportTo: map[visibility.Instance]bool{ - visibility.Private: true, - }, - }, - }, - expect: true, - }, - { - name: "service whose namespace is bar has exportTo map with private", - pushContext: &PushContext{}, - service: &Service{ - Attributes: ServiceAttributes{ - Namespace: "bar", - ExportTo: map[visibility.Instance]bool{ - visibility.Private: true, - }, - }, - }, - expect: false, - }, - { - name: "service whose namespace is bar has exportTo map with public", - pushContext: &PushContext{}, - service: &Service{ - Attributes: ServiceAttributes{ - Namespace: "bar", - ExportTo: map[visibility.Instance]bool{ - visibility.Public: true, - }, - }, - }, - expect: true, - }, - { - name: "service whose namespace is bar has exportTo map with specific namespace foo", - pushContext: &PushContext{}, - service: &Service{ - Attributes: ServiceAttributes{ - Namespace: "bar", - ExportTo: map[visibility.Instance]bool{ - visibility.Instance("foo"): true, - }, - }, - }, - expect: true, - }, - { - name: "service whose namespace is bar has exportTo map with specific namespace baz", - pushContext: &PushContext{}, - service: &Service{ - Attributes: ServiceAttributes{ - Namespace: "bar", - ExportTo: map[visibility.Instance]bool{ - visibility.Instance("baz"): true, - }, - }, - }, - expect: false, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - isVisible := c.pushContext.IsServiceVisible(c.service, targetNamespace) - - g := NewWithT(t) - g.Expect(isVisible).To(Equal(c.expect)) - }) - } -} - -func serviceNames(svcs []*Service) []string { - var s []string - for _, ss := range svcs { - s = append(s, string(ss.Hostname)) - } - sort.Strings(s) - return s -} - -func TestInitPushContext(t *testing.T) { - env := &Environment{} - configStore := NewFakeStore() - _, _ = configStore.Create(config.Config{ - Meta: config.Meta{ - Name: "rule1", - Namespace: "test1", - GroupVersionKind: gvk.VirtualService, - }, - Spec: &networking.VirtualService{ - Hosts: []string{"rule1.com"}, - ExportTo: []string{".", "ns1"}, - }, - }) - _, _ = configStore.Create(config.Config{ - Meta: config.Meta{ - Name: "rule1", - Namespace: "test1", - GroupVersionKind: gvk.DestinationRule, - }, - Spec: &networking.DestinationRule{ - ExportTo: []string{".", "ns1"}, - }, - }) - store := istioConfigStore{ConfigStore: configStore} - - env.ConfigStore = &store - env.ServiceDiscovery = &localServiceDiscovery{ - services: []*Service{ - { - Hostname: "svc1", - Ports: allPorts, - Attributes: ServiceAttributes{ - Namespace: "test1", - }, - }, - { - Hostname: "svc2", - Ports: allPorts, - Attributes: ServiceAttributes{ - Namespace: "test1", - ExportTo: map[visibility.Instance]bool{visibility.Public: true}, - }, - }, - }, - serviceInstances: []*ServiceInstance{{ - Endpoint: &IstioEndpoint{ - Address: "192.168.1.2", - EndpointPort: 8000, - TLSMode: DisabledTLSModeLabel, - }, - }}, - } - m := mesh.DefaultMeshConfig() - env.Watcher = mesh.NewFixedWatcher(m) - env.Init() - - // Init a new push context - old := NewPushContext() - if err := old.InitContext(env, nil, nil); err != nil { - t.Fatal(err) - } - - // Create a new one, copying from the old one - // Pass a ConfigsUpdated otherwise we would just copy it directly - newPush := NewPushContext() - if err := newPush.InitContext(env, old, &PushRequest{ - ConfigsUpdated: map[ConfigKey]struct{}{ - {Kind: gvk.Secret}: {}, - }, - }); err != nil { - t.Fatal(err) - } - - // Check to ensure the update is identical to the old one - // There is probably a better way to do this. - diff := cmp.Diff(old, newPush, - // Allow looking into exported fields for parts of push context - cmp.AllowUnexported(PushContext{}, exportToDefaults{}, serviceIndex{}, virtualServiceIndex{}, - destinationRuleIndex{}, gatewayIndex{}, consolidatedDestRules{}, IstioEgressListenerWrapper{}, SidecarScope{}, - AuthenticationPolicies{}, NetworkManager{}, sidecarIndex{}, Telemetries{}, ProxyConfigs{}, consolidatedDestRule{}, serviceNameMappingIndex{}, serviceMetadataIndex{}), - // These are not feasible/worth comparing - cmpopts.IgnoreTypes(sync.RWMutex{}, localServiceDiscovery{}, FakeStore{}, atomic.Bool{}, sync.Mutex{}), - cmpopts.IgnoreInterfaces(struct{ mesh.Holder }{}), - protocmp.Transform(), - ) - if diff != "" { - t.Fatalf("Push context had a diff after update: %v", diff) - } -} - -func TestSidecarScope(t *testing.T) { - ps := NewPushContext() - env := &Environment{Watcher: mesh.NewFixedWatcher(&meshconfig.MeshConfig{RootNamespace: "dubbo-system"})} - ps.Mesh = env.Mesh() - ps.ServiceIndex.HostnameAndNamespace["svc1.default.cluster.local"] = map[string]*Service{"default": nil} - ps.ServiceIndex.HostnameAndNamespace["svc2.nosidecar.cluster.local"] = map[string]*Service{"nosidecar": nil} - ps.ServiceIndex.HostnameAndNamespace["svc3.dubbo-system.cluster.local"] = map[string]*Service{"dubbo-system": nil} - - configStore := NewFakeStore() - sidecarWithWorkloadSelector := &networking.Sidecar{ - WorkloadSelector: &networking.WorkloadSelector{ - Labels: map[string]string{"app": "foo"}, - }, - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"default/*"}, - }, - }, - OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{}, - } - sidecarWithoutWorkloadSelector := &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"default/*"}, - }, - }, - OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{}, - } - configWithWorkloadSelector := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Sidecars.Resource().GroupVersionKind(), - Name: "foo", - Namespace: "default", - }, - Spec: sidecarWithWorkloadSelector, - } - rootConfig := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Sidecars.Resource().GroupVersionKind(), - Name: "global", - Namespace: constants.IstioSystemNamespace, - }, - Spec: sidecarWithoutWorkloadSelector, - } - _, _ = configStore.Create(configWithWorkloadSelector) - _, _ = configStore.Create(rootConfig) - - store := istioConfigStore{ConfigStore: configStore} - - env.ConfigStore = &store - if err := ps.initSidecarScopes(env); err != nil { - t.Fatalf("init sidecar scope failed: %v", err) - } - cases := []struct { - proxy *Proxy - labels labels.Instance - sidecar string - describe string - }{ - { - proxy: &Proxy{Type: SidecarProxy, ConfigNamespace: "default"}, - labels: labels.Instance{"app": "foo"}, - sidecar: "default/foo", - describe: "match local sidecar", - }, - { - proxy: &Proxy{Type: SidecarProxy, ConfigNamespace: "default"}, - labels: labels.Instance{"app": "bar"}, - sidecar: "default/global", - describe: "no match local sidecar", - }, - { - proxy: &Proxy{Type: SidecarProxy, ConfigNamespace: "nosidecar"}, - labels: labels.Instance{"app": "bar"}, - sidecar: "nosidecar/global", - describe: "no sidecar", - }, - { - proxy: &Proxy{Type: Router, ConfigNamespace: "dubbo-system"}, - labels: labels.Instance{"app": "istio-gateway"}, - sidecar: "dubbo-system/default-sidecar", - describe: "gateway sidecar scope", - }, - } - for _, c := range cases { - t.Run(c.describe, func(t *testing.T) { - scope := ps.getSidecarScope(c.proxy, c.labels) - if c.sidecar != scopeToSidecar(scope) { - t.Errorf("should get sidecar %s but got %s", c.sidecar, scopeToSidecar(scope)) - } - }) - } -} - -func TestBestEffortInferServiceMTLSMode(t *testing.T) { - const partialNS string = "partial" - const wholeNS string = "whole" - ps := NewPushContext() - env := &Environment{Watcher: mesh.NewFixedWatcher(&meshconfig.MeshConfig{RootNamespace: "dubbo-system"})} - sd := &localServiceDiscovery{} - env.ServiceDiscovery = sd - ps.Mesh = env.Mesh() - - configStore := NewFakeStore() - - // Add beta policies - _, _ = configStore.Create(*createTestPeerAuthenticationResource("default", wholeNS, time.Now(), nil, securityBeta.PeerAuthentication_MutualTLS_STRICT)) - // workload level beta policy. - _, _ = configStore.Create(*createTestPeerAuthenticationResource("workload-beta-policy", partialNS, time.Now(), &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - "version": "v1", - }, - }, securityBeta.PeerAuthentication_MutualTLS_DISABLE)) - - store := istioConfigStore{ConfigStore: configStore} - env.ConfigStore = &store - if err := ps.initAuthnPolicies(env); err != nil { - t.Fatalf("init authn policies failed: %v", err) - } - - instancePlainText := &ServiceInstance{ - Endpoint: &IstioEndpoint{ - Address: "192.168.1.2", - EndpointPort: 1000000, - TLSMode: DisabledTLSModeLabel, - }, - } - - cases := []struct { - name string - serviceNamespace string - servicePort int - serviceResolution Resolution - serviceInstances []*ServiceInstance - wanted MutualTLSMode - }{ - { - name: "from namespace policy", - serviceNamespace: wholeNS, - servicePort: 80, - serviceResolution: ClientSideLB, - wanted: MTLSStrict, - }, - { - name: "from mesh default", - serviceNamespace: partialNS, - servicePort: 80, - serviceResolution: ClientSideLB, - wanted: MTLSPermissive, - }, - { - name: "headless service with no instances found yet", - serviceNamespace: partialNS, - servicePort: 80, - serviceResolution: Passthrough, - wanted: MTLSDisable, - }, - { - name: "headless service with instances", - serviceNamespace: partialNS, - servicePort: 80, - serviceResolution: Passthrough, - serviceInstances: []*ServiceInstance{instancePlainText}, - wanted: MTLSDisable, - }, - } - - serviceName := host.Name("some-service") - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - service := &Service{ - Hostname: host.Name(fmt.Sprintf("%s.%s.svc.cluster.local", serviceName, tc.serviceNamespace)), - Resolution: tc.serviceResolution, - Attributes: ServiceAttributes{Namespace: tc.serviceNamespace}, - } - // Intentionally use the externalService with the same name and namespace for test, though - // these attributes don't matter. - externalService := &Service{ - Hostname: host.Name(fmt.Sprintf("%s.%s.svc.cluster.local", serviceName, tc.serviceNamespace)), - Resolution: tc.serviceResolution, - Attributes: ServiceAttributes{Namespace: tc.serviceNamespace}, - MeshExternal: true, - } - - sd.serviceInstances = tc.serviceInstances - port := &Port{ - Port: tc.servicePort, - } - if got := ps.BestEffortInferServiceMTLSMode(nil, service, port); got != tc.wanted { - t.Fatalf("want %s, but got %s", tc.wanted, got) - } - if got := ps.BestEffortInferServiceMTLSMode(nil, externalService, port); got != MTLSUnknown { - t.Fatalf("MTLS mode for external service should always be %s, but got %s", MTLSUnknown, got) - } - }) - } -} - -func scopeToSidecar(scope *SidecarScope) string { - if scope == nil { - return "" - } - return scope.Namespace + "/" + scope.Name -} - -func TestSetDestinationRuleInheritance(t *testing.T) { - test.SetBoolForTest(t, &features.EnableDestinationRuleInheritance, true) - ps := NewPushContext() - ps.Mesh = &meshconfig.MeshConfig{RootNamespace: "dubbo-system"} - testhost := "httpbin.org" - meshDestinationRule := config.Config{ - Meta: config.Meta{ - Name: "meshRule", - Namespace: ps.Mesh.RootNamespace, - }, - Spec: &networking.DestinationRule{ - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - ConnectTimeout: &durationpb.Duration{Seconds: 1}, - MaxConnections: 111, - }, - }, - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - ClientCertificate: "/etc/certs/myclientcert.pem", - PrivateKey: "/etc/certs/client_private_key.pem", - CaCertificates: "/etc/certs/rootcacerts.pem", - }, - }, - }, - } - nsDestinationRule := config.Config{ - Meta: config.Meta{ - Name: "nsRule", - Namespace: "test", - }, - Spec: &networking.DestinationRule{ - TrafficPolicy: &networking.TrafficPolicy{ - OutlierDetection: &networking.OutlierDetection{ - ConsecutiveGatewayErrors: &wrappers.UInt32Value{Value: 222}, - Interval: &durationpb.Duration{Seconds: 22}, - }, - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 2, - }, - }, - }, - }, - } - svcDestinationRule := config.Config{ - Meta: config.Meta{ - Name: "svcRule", - Namespace: "test", - }, - Spec: &networking.DestinationRule{ - Host: testhost, - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 33, - }, - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - ConnectTimeout: &durationpb.Duration{Seconds: 33}, - }, - }, - OutlierDetection: &networking.OutlierDetection{ - Consecutive_5XxErrors: &wrappers.UInt32Value{Value: 3}, - }, - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - }, - }, - }, - } - destinationRuleNamespace2 := config.Config{ - Meta: config.Meta{ - Name: "svcRule2", - Namespace: "test2", - }, - Spec: &networking.DestinationRule{ - Host: testhost, - Subsets: []*networking.Subset{ - { - Name: "subset1", - }, - { - Name: "subset2", - }, - }, - }, - } - workloadSpecificDrNamespace2 := config.Config{ - Meta: config.Meta{ - Name: "drRule2", - Namespace: "test2", - }, - Spec: &networking.DestinationRule{ - Host: testhost, - WorkloadSelector: &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{"app": "app1"}, - }, - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 33, - }, - }, - OutlierDetection: &networking.OutlierDetection{ - Consecutive_5XxErrors: &wrappers.UInt32Value{Value: 3}, - }, - }, - }, - } - testCases := []struct { - name string - proxyNs string - serviceNs string - serviceHostname string - expectedConfig string - expectedSourceRule []types.NamespacedName - expectedPolicy *networking.TrafficPolicy - }{ - { - name: "merge mesh+namespace+service DR", - proxyNs: "test", - serviceNs: "test", - serviceHostname: testhost, - expectedConfig: "svcRule", - expectedSourceRule: []types.NamespacedName{ - {Namespace: "dubbo-system", Name: "meshRule"}, - {Namespace: "test", Name: "nsRule"}, - {Namespace: "test", Name: "svcRule"}, - }, - expectedPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 33, - }, - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - ConnectTimeout: &durationpb.Duration{Seconds: 33}, - MaxConnections: 111, - }, - }, - OutlierDetection: &networking.OutlierDetection{ - Consecutive_5XxErrors: &wrappers.UInt32Value{Value: 3}, - ConsecutiveGatewayErrors: &wrappers.UInt32Value{Value: 222}, - Interval: &durationpb.Duration{Seconds: 22}, - }, - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - }, - }, - }, - { - name: "merge mesh+service DR", - proxyNs: "test2", - serviceNs: "test2", - serviceHostname: testhost, - expectedConfig: "svcRule2", - expectedSourceRule: []types.NamespacedName{ - {Namespace: "dubbo-system", Name: "meshRule"}, - {Namespace: "test2", Name: "svcRule2"}, - }, - expectedPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - ConnectTimeout: &durationpb.Duration{Seconds: 1}, - MaxConnections: 111, - }, - }, - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - ClientCertificate: "/etc/certs/myclientcert.pem", - PrivateKey: "/etc/certs/client_private_key.pem", - CaCertificates: "/etc/certs/rootcacerts.pem", - }, - }, - }, - { - name: "merge mesh+workloadselector DR", - proxyNs: "test2", - serviceNs: "test2", - serviceHostname: testhost, - expectedConfig: "drRule2", - expectedSourceRule: []types.NamespacedName{ - {Namespace: "dubbo-system", Name: "meshRule"}, - {Namespace: "test2", Name: "drRule2"}, - {Namespace: "test2", Name: "svcRule2"}, - }, - expectedPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 33, - }, - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - ConnectTimeout: &durationpb.Duration{Seconds: 1}, - MaxConnections: 111, - }, - }, - OutlierDetection: &networking.OutlierDetection{ - Consecutive_5XxErrors: &wrappers.UInt32Value{Value: 3}, - }, - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - ClientCertificate: "/etc/certs/myclientcert.pem", - PrivateKey: "/etc/certs/client_private_key.pem", - CaCertificates: "/etc/certs/rootcacerts.pem", - }, - }, - }, - { - name: "unknown host returns merged mesh+namespace", - proxyNs: "test", - serviceNs: "test", - serviceHostname: "unknown.host", - expectedConfig: "nsRule", - expectedSourceRule: []types.NamespacedName{ - {Namespace: "dubbo-system", Name: "meshRule"}, - {Namespace: "test", Name: "nsRule"}, - }, - expectedPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 2, - }, - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - ConnectTimeout: &durationpb.Duration{Seconds: 1}, - MaxConnections: 111, - }, - }, - OutlierDetection: &networking.OutlierDetection{ - ConsecutiveGatewayErrors: &wrappers.UInt32Value{Value: 222}, - Interval: &durationpb.Duration{Seconds: 22}, - }, - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - ClientCertificate: "/etc/certs/myclientcert.pem", - PrivateKey: "/etc/certs/client_private_key.pem", - CaCertificates: "/etc/certs/rootcacerts.pem", - }, - }, - }, - { - name: "unknown namespace+host returns mesh", - proxyNs: "unknown", - serviceNs: "unknown", - serviceHostname: "unknown.host", - expectedConfig: "meshRule", - expectedSourceRule: []types.NamespacedName{ - {Namespace: "dubbo-system", Name: "meshRule"}, - }, - expectedPolicy: meshDestinationRule.Spec.(*networking.DestinationRule).TrafficPolicy, - }, - } - - ps.SetDestinationRules([]config.Config{meshDestinationRule, nsDestinationRule, svcDestinationRule, destinationRuleNamespace2, workloadSpecificDrNamespace2}) - - for _, tt := range testCases { - t.Run(tt.name, func(t *testing.T) { - mergedConfigList := ps.destinationRule(tt.proxyNs, - &Service{ - Hostname: host.Name(tt.serviceHostname), - Attributes: ServiceAttributes{ - Namespace: tt.serviceNs, - }, - }) - expectedConfigPresent := false - for _, mergedConfig := range mergedConfigList { - if mergedConfig.rule.Name == tt.expectedConfig { - expectedConfigPresent = true - mergedPolicy := mergedConfig.rule.Spec.(*networking.DestinationRule).TrafficPolicy - assert.Equal(t, mergedPolicy, tt.expectedPolicy) - assert.Equal(t, mergedConfig.from, tt.expectedSourceRule) - } - } - if !expectedConfigPresent { - t.Errorf("case %s failed, merged config should contain most specific config name, wanted %v but missing", tt.name, tt.expectedConfig) - } - }) - } -} - -func TestSetDestinationRuleWithWorkloadSelector(t *testing.T) { - ps := NewPushContext() - ps.Mesh = &meshconfig.MeshConfig{RootNamespace: "dubbo-system"} - testhost := "httpbin.org" - app1DestinationRule := config.Config{ - Meta: config.Meta{ - Name: "nsRule1", - Namespace: "test", - }, - Spec: &networking.DestinationRule{ - Host: testhost, - ExportTo: []string{"test2", "."}, - WorkloadSelector: &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{"app": "app1"}, - }, - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - ConnectTimeout: &durationpb.Duration{Seconds: 1}, - MaxConnections: 111, - }, - }, - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - ClientCertificate: "/etc/certs/myclientcert.pem", - PrivateKey: "/etc/certs/client_private_key.pem", - CaCertificates: "/etc/certs/rootcacerts.pem", - }, - }, - }, - } - app2DestinationRule := config.Config{ - Meta: config.Meta{ - Name: "nsRule2", - Namespace: "test", - }, - Spec: &networking.DestinationRule{ - Host: testhost, - ExportTo: []string{"test2", "."}, - WorkloadSelector: &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{"app": "app2"}, - }, - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 33, - }, - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - ConnectTimeout: &durationpb.Duration{Seconds: 33}, - }, - }, - OutlierDetection: &networking.OutlierDetection{ - Consecutive_5XxErrors: &wrappers.UInt32Value{Value: 3}, - }, - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - }, - }, - }, - } - app3DestinationRule := config.Config{ - Meta: config.Meta{ - Name: "nsRule3", - Namespace: "test", - }, - Spec: &networking.DestinationRule{ - Host: testhost, - ExportTo: []string{"test2", "."}, - WorkloadSelector: &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{"app": "app2"}, - }, - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 33, - }, - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - ConnectTimeout: &durationpb.Duration{Seconds: 33}, - }, - }, - OutlierDetection: &networking.OutlierDetection{ - Consecutive_5XxErrors: &wrappers.UInt32Value{Value: 3}, - }, - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - }, - }, - }, - } - namespaceDestinationRule := config.Config{ - Meta: config.Meta{ - Name: "nsRule4", - Namespace: "test", - }, - Spec: &networking.DestinationRule{ - Host: testhost, - ExportTo: []string{".", "test2"}, - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 33, - }, - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - ConnectTimeout: &durationpb.Duration{Seconds: 33}, - }, - }, - OutlierDetection: &networking.OutlierDetection{ - Consecutive_5XxErrors: &wrappers.UInt32Value{Value: 3}, - }, - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - }, - }, - }, - } - testCases := []struct { - name string - proxyNs string - serviceNs string - serviceHostname string - expectedDrCount int - expectedDrName []string - }{ - { - name: "return list of DRs for specific host", - proxyNs: "test", - serviceNs: "test", - serviceHostname: testhost, - expectedDrCount: 3, - expectedDrName: []string{app1DestinationRule.Meta.Name, app2DestinationRule.Meta.Name, namespaceDestinationRule.Meta.Name}, - }, - { - name: "workload specific DR should not be exported", - proxyNs: "test2", - serviceNs: "test", - serviceHostname: testhost, - expectedDrCount: 1, - expectedDrName: []string{namespaceDestinationRule.Meta.Name}, - }, - { - name: "rules with same workloadselector should be merged", - proxyNs: "test", - serviceNs: "test", - serviceHostname: testhost, - expectedDrCount: 3, - expectedDrName: []string{app1DestinationRule.Meta.Name, app2DestinationRule.Meta.Name, namespaceDestinationRule.Meta.Name}, - }, - } - - ps.SetDestinationRules([]config.Config{app1DestinationRule, app2DestinationRule, app3DestinationRule, namespaceDestinationRule}) - - for _, tt := range testCases { - drList := ps.destinationRule(tt.proxyNs, - &Service{ - Hostname: host.Name(tt.serviceHostname), - Attributes: ServiceAttributes{ - Namespace: tt.serviceNs, - }, - }) - if len(drList) != tt.expectedDrCount { - t.Errorf("case %s failed, %d destinationRules for host %v got %v", tt.name, tt.expectedDrCount, tt.serviceHostname, drList) - } - for i, dr := range drList { - if dr.rule.Name != tt.expectedDrName[i] { - t.Errorf("case %s failed, destinationRuleName expected %v got %v", tt.name, tt.expectedDrName[i], dr.rule.Name) - } - } - } -} - -func TestSetDestinationRuleMerging(t *testing.T) { - ps := NewPushContext() - ps.exportToDefaults.destinationRule = map[visibility.Instance]bool{visibility.Public: true} - testhost := "httpbin.org" - destinationRuleNamespace1 := config.Config{ - Meta: config.Meta{ - Name: "rule1", - Namespace: "test", - }, - Spec: &networking.DestinationRule{ - Host: testhost, - Subsets: []*networking.Subset{ - { - Name: "subset1", - }, - { - Name: "subset2", - }, - }, - }, - } - destinationRuleNamespace2 := config.Config{ - Meta: config.Meta{ - Name: "rule2", - Namespace: "test", - }, - Spec: &networking.DestinationRule{ - Host: testhost, - Subsets: []*networking.Subset{ - { - Name: "subset3", - }, - { - Name: "subset4", - }, - }, - }, - } - expectedDestRules := []types.NamespacedName{ - {Namespace: "test", Name: "rule1"}, - {Namespace: "test", Name: "rule2"}, - } - ps.SetDestinationRules([]config.Config{destinationRuleNamespace1, destinationRuleNamespace2}) - private := ps.destinationRuleIndex.namespaceLocal["test"].destRules[host.Name(testhost)] - public := ps.destinationRuleIndex.exportedByNamespace["test"].destRules[host.Name(testhost)] - subsetsLocal := private[0].rule.Spec.(*networking.DestinationRule).Subsets - subsetsExport := public[0].rule.Spec.(*networking.DestinationRule).Subsets - assert.Equal(t, private[0].from, expectedDestRules) - assert.Equal(t, public[0].from, expectedDestRules) - if len(subsetsLocal) != 4 { - t.Errorf("want %d, but got %d", 4, len(subsetsLocal)) - } - if len(subsetsExport) != 4 { - t.Errorf("want %d, but got %d", 4, len(subsetsExport)) - } -} - -func TestSetDestinationRuleWithExportTo(t *testing.T) { - ps := NewPushContext() - ps.Mesh = &meshconfig.MeshConfig{RootNamespace: "dubbo-system"} - testhost := "httpbin.org" - appHost := "foo.app.org" - wildcardHost1 := "*.org" - wildcardHost2 := "*.app.org" - destinationRuleNamespace1 := config.Config{ - Meta: config.Meta{ - Name: "rule1", - Namespace: "test1", - }, - Spec: &networking.DestinationRule{ - Host: testhost, - ExportTo: []string{".", "ns1"}, - Subsets: []*networking.Subset{ - { - Name: "subset1", - }, - { - Name: "subset2", - }, - }, - }, - } - destinationRuleNamespace2 := config.Config{ - Meta: config.Meta{ - Name: "rule2", - Namespace: "test2", - }, - Spec: &networking.DestinationRule{ - Host: testhost, - ExportTo: []string{"test2", "ns1", "test1"}, - Subsets: []*networking.Subset{ - { - Name: "subset3", - }, - { - Name: "subset4", - }, - }, - }, - } - destinationRuleNamespace3 := config.Config{ - Meta: config.Meta{ - Name: "rule3", - Namespace: "test3", - }, - Spec: &networking.DestinationRule{ - Host: testhost, - ExportTo: []string{"test1", "test2", "*"}, - Subsets: []*networking.Subset{ - { - Name: "subset5", - }, - { - Name: "subset6", - }, - }, - }, - } - destinationRuleNamespace4Local := config.Config{ - Meta: config.Meta{ - Name: "rule4-local", - Namespace: "test4", - }, - Spec: &networking.DestinationRule{ - Host: testhost, - ExportTo: []string{"test4"}, - Subsets: []*networking.Subset{ - { - Name: "subset15", - }, - { - Name: "subset16", - }, - }, - }, - } - destinationRuleRootNamespace := config.Config{ - Meta: config.Meta{ - Name: "rule4", - Namespace: "dubbo-system", - }, - Spec: &networking.DestinationRule{ - Host: testhost, - Subsets: []*networking.Subset{ - { - Name: "subset7", - }, - { - Name: "subset8", - }, - }, - }, - } - destinationRuleRootNamespaceLocal := config.Config{ - Meta: config.Meta{ - Name: "rule1", - Namespace: "dubbo-system", - }, - Spec: &networking.DestinationRule{ - Host: testhost, - ExportTo: []string{"."}, - Subsets: []*networking.Subset{ - { - Name: "subset9", - }, - { - Name: "subset10", - }, - }, - }, - } - destinationRuleRootNamespaceLocalWithWildcardHost1 := config.Config{ - Meta: config.Meta{ - Name: "rule2", - Namespace: "dubbo-system", - }, - Spec: &networking.DestinationRule{ - Host: wildcardHost1, - ExportTo: []string{"."}, - Subsets: []*networking.Subset{ - { - Name: "subset11", - }, - { - Name: "subset12", - }, - }, - }, - } - destinationRuleRootNamespaceLocalWithWildcardHost2 := config.Config{ - Meta: config.Meta{ - Name: "rule3", - Namespace: "dubbo-system", - }, - Spec: &networking.DestinationRule{ - Host: wildcardHost2, - ExportTo: []string{"."}, - Subsets: []*networking.Subset{ - { - Name: "subset13", - }, - { - Name: "subset14", - }, - }, - }, - } - ps.SetDestinationRules([]config.Config{ - destinationRuleNamespace1, destinationRuleNamespace2, - destinationRuleNamespace3, destinationRuleNamespace4Local, - destinationRuleRootNamespace, destinationRuleRootNamespaceLocal, - destinationRuleRootNamespaceLocalWithWildcardHost1, destinationRuleRootNamespaceLocalWithWildcardHost2, - }) - cases := []struct { - proxyNs string - serviceNs string - host string - wantSubsets []string - }{ - { - proxyNs: "test1", - serviceNs: "test1", - host: testhost, - wantSubsets: []string{"subset1", "subset2"}, - }, - { - proxyNs: "test1", - serviceNs: "test2", - host: testhost, - wantSubsets: []string{"subset1", "subset2"}, - }, - { - proxyNs: "test2", - serviceNs: "test1", - host: testhost, - wantSubsets: []string{"subset3", "subset4"}, - }, - { - proxyNs: "test3", - serviceNs: "test1", - host: testhost, - wantSubsets: []string{"subset5", "subset6"}, - }, - { - proxyNs: "test5", - serviceNs: "test4", - host: testhost, - wantSubsets: []string{"subset7", "subset8"}, - }, - { - proxyNs: "ns1", - serviceNs: "test1", - host: testhost, - wantSubsets: []string{"subset1", "subset2"}, - }, - { - proxyNs: "ns1", - serviceNs: "random", - host: testhost, - wantSubsets: []string{"subset7", "subset8"}, - }, - { - proxyNs: "random", - serviceNs: "random", - host: testhost, - wantSubsets: []string{"subset7", "subset8"}, - }, - { - proxyNs: "test3", - serviceNs: "random", - host: testhost, - wantSubsets: []string{"subset5", "subset6"}, - }, - { - proxyNs: "dubbo-system", - serviceNs: "random", - host: testhost, - wantSubsets: []string{"subset9", "subset10"}, - }, - { - proxyNs: "dubbo-system", - serviceNs: "dubbo-system", - host: testhost, - wantSubsets: []string{"subset9", "subset10"}, - }, - { - proxyNs: "dubbo-system", - serviceNs: "dubbo-system", - host: appHost, - wantSubsets: []string{"subset13", "subset14"}, - }, - } - for _, tt := range cases { - t.Run(fmt.Sprintf("%s-%s", tt.proxyNs, tt.serviceNs), func(t *testing.T) { - destRuleConfig := ps.destinationRule(tt.proxyNs, - &Service{ - Hostname: host.Name(tt.host), - Attributes: ServiceAttributes{ - Namespace: tt.serviceNs, - }, - })[0] - if destRuleConfig == nil { - t.Fatalf("proxy in %s namespace: dest rule is nil, expected subsets %+v", tt.proxyNs, tt.wantSubsets) - } - destRule := destRuleConfig.rule.Spec.(*networking.DestinationRule) - var gotSubsets []string - for _, ss := range destRule.Subsets { - gotSubsets = append(gotSubsets, ss.Name) - } - if !reflect.DeepEqual(gotSubsets, tt.wantSubsets) { - t.Fatalf("want %+v, got %+v", tt.wantSubsets, gotSubsets) - } - }) - } -} - -func TestVirtualServiceWithExportTo(t *testing.T) { - ps := NewPushContext() - env := &Environment{Watcher: mesh.NewFixedWatcher(&meshconfig.MeshConfig{RootNamespace: "zzz"})} - ps.Mesh = env.Mesh() - configStore := NewFakeStore() - gatewayName := "default/gateway" - - rule1 := config.Config{ - Meta: config.Meta{ - Name: "rule1", - Namespace: "test1", - GroupVersionKind: gvk.VirtualService, - }, - Spec: &networking.VirtualService{ - Hosts: []string{"rule1.com"}, - ExportTo: []string{".", "ns1"}, - }, - } - rule2 := config.Config{ - Meta: config.Meta{ - Name: "rule2", - Namespace: "test2", - GroupVersionKind: gvk.VirtualService, - }, - Spec: &networking.VirtualService{ - Hosts: []string{"rule2.com"}, - ExportTo: []string{"test2", "ns1", "test1"}, - }, - } - rule2Gw := config.Config{ - Meta: config.Meta{ - Name: "rule2Gw", - Namespace: "test2", - GroupVersionKind: gvk.VirtualService, - }, - Spec: &networking.VirtualService{ - Gateways: []string{gatewayName, constants.IstioMeshGateway}, - Hosts: []string{"rule2gw.com"}, - ExportTo: []string{"test2", "ns1", "test1"}, - }, - } - rule3 := config.Config{ - Meta: config.Meta{ - Name: "rule3", - Namespace: "test3", - GroupVersionKind: gvk.VirtualService, - }, - Spec: &networking.VirtualService{ - Gateways: []string{constants.IstioMeshGateway}, - Hosts: []string{"rule3.com"}, - ExportTo: []string{"test1", "test2", "*"}, - }, - } - rule3Gw := config.Config{ - Meta: config.Meta{ - Name: "rule3Gw", - Namespace: "test3", - GroupVersionKind: gvk.VirtualService, - }, - Spec: &networking.VirtualService{ - Gateways: []string{gatewayName}, - Hosts: []string{"rule3gw.com"}, - ExportTo: []string{"test1", "test2", "*"}, - }, - } - rootNS := config.Config{ - Meta: config.Meta{ - Name: "zzz", - Namespace: "zzz", - GroupVersionKind: gvk.VirtualService, - }, - Spec: &networking.VirtualService{ - Hosts: []string{"rootNS.com"}, - }, - } - - for _, c := range []config.Config{rule1, rule2, rule3, rule2Gw, rule3Gw, rootNS} { - if _, err := configStore.Create(c); err != nil { - t.Fatalf("could not create %v", c.Name) - } - } - - store := istioConfigStore{ConfigStore: configStore} - env.ConfigStore = &store - ps.initDefaultExportMaps() - if err := ps.initVirtualServices(env); err != nil { - t.Fatalf("init virtual services failed: %v", err) - } - - cases := []struct { - proxyNs string - gateway string - wantHosts []string - }{ - { - proxyNs: "test1", - wantHosts: []string{"rule1.com", "rule2.com", "rule2gw.com", "rule3.com", "rootNS.com"}, - gateway: constants.IstioMeshGateway, - }, - { - proxyNs: "test2", - wantHosts: []string{"rule2.com", "rule2gw.com", "rule3.com", "rootNS.com"}, - gateway: constants.IstioMeshGateway, - }, - { - proxyNs: "ns1", - wantHosts: []string{"rule1.com", "rule2.com", "rule2gw.com", "rule3.com", "rootNS.com"}, - gateway: constants.IstioMeshGateway, - }, - { - proxyNs: "random", - wantHosts: []string{"rule3.com", "rootNS.com"}, - gateway: constants.IstioMeshGateway, - }, - { - proxyNs: "test1", - wantHosts: []string{"rule2gw.com", "rule3gw.com"}, - gateway: gatewayName, - }, - { - proxyNs: "test2", - wantHosts: []string{"rule2gw.com", "rule3gw.com"}, - gateway: gatewayName, - }, - { - proxyNs: "ns1", - wantHosts: []string{"rule2gw.com", "rule3gw.com"}, - gateway: gatewayName, - }, - { - proxyNs: "random", - wantHosts: []string{"rule3gw.com"}, - gateway: gatewayName, - }, - } - for _, tt := range cases { - t.Run(fmt.Sprintf("%s-%s", tt.proxyNs, tt.gateway), func(t *testing.T) { - rules := ps.VirtualServicesForGateway(tt.proxyNs, tt.gateway) - gotHosts := make([]string, 0) - for _, r := range rules { - vs := r.Spec.(*networking.VirtualService) - gotHosts = append(gotHosts, vs.Hosts...) - } - if !reflect.DeepEqual(gotHosts, tt.wantHosts) { - t.Errorf("want %+v, got %+v", tt.wantHosts, gotHosts) - } - }) - } -} - -func TestInitVirtualService(t *testing.T) { - ps := NewPushContext() - env := &Environment{Watcher: mesh.NewFixedWatcher(&meshconfig.MeshConfig{RootNamespace: "dubbo-system"})} - ps.Mesh = env.Mesh() - configStore := NewFakeStore() - gatewayName := "ns1/gateway" - - vs1 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "vs1", - Namespace: "ns1", - }, - Spec: &networking.VirtualService{ - Hosts: []string{"*.org"}, - Gateways: []string{"gateway"}, - Http: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"}, - }, - }, - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/login"}, - }, - }, - }, - Delegate: &networking.Delegate{ - Name: "vs2", - Namespace: "ns2", - }, - }, - }, - }, - } - vs2 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "vs2", - Namespace: "ns2", - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{gatewayName}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "test", - Port: &networking.PortSelector{ - Number: 80, - }, - }, - }, - }, - }, - }, - }, - } - - for _, c := range []config.Config{vs1, vs2} { - if _, err := configStore.Create(c); err != nil { - t.Fatalf("could not create %v", c.Name) - } - } - - store := istioConfigStore{ConfigStore: configStore} - env.ConfigStore = &store - ps.initDefaultExportMaps() - if err := ps.initVirtualServices(env); err != nil { - t.Fatalf("init virtual services failed: %v", err) - } - - t.Run("resolve shortname", func(t *testing.T) { - rules := ps.VirtualServicesForGateway("ns1", gatewayName) - if len(rules) != 1 { - t.Fatalf("wanted 1 virtualservice for gateway %s, actually got %d", gatewayName, len(rules)) - } - gotHTTPHosts := make([]string, 0) - for _, r := range rules { - vs := r.Spec.(*networking.VirtualService) - for _, route := range vs.GetHttp() { - for _, dst := range route.Route { - gotHTTPHosts = append(gotHTTPHosts, dst.Destination.Host) - } - } - } - if !reflect.DeepEqual(gotHTTPHosts, []string{"test.ns2"}) { - t.Errorf("got %+v", gotHTTPHosts) - } - }) -} - -func TestServiceWithExportTo(t *testing.T) { - ps := NewPushContext() - env := &Environment{Watcher: mesh.NewFixedWatcher(&meshconfig.MeshConfig{RootNamespace: "zzz"})} - ps.Mesh = env.Mesh() - - svc1 := &Service{ - Hostname: "svc1", - Attributes: ServiceAttributes{ - Namespace: "test1", - ExportTo: map[visibility.Instance]bool{visibility.Private: true, visibility.Instance("ns1"): true}, - }, - } - svc2 := &Service{ - Hostname: "svc2", - Attributes: ServiceAttributes{ - Namespace: "test2", - ExportTo: map[visibility.Instance]bool{ - visibility.Instance("test1"): true, - visibility.Instance("ns1"): true, - visibility.Instance("test2"): true, - }, - }, - } - svc3 := &Service{ - Hostname: "svc3", - Attributes: ServiceAttributes{ - Namespace: "test3", - ExportTo: map[visibility.Instance]bool{ - visibility.Instance("test1"): true, - visibility.Public: true, - visibility.Instance("test2"): true, - }, - }, - } - svc4 := &Service{ - Hostname: "svc4", - Attributes: ServiceAttributes{ - Namespace: "test4", - }, - } - env.ServiceDiscovery = &localServiceDiscovery{ - services: []*Service{svc1, svc2, svc3, svc4}, - } - ps.initDefaultExportMaps() - if err := ps.initServiceRegistry(env); err != nil { - t.Fatalf("init services failed: %v", err) - } - - cases := []struct { - proxyNs string - wantHosts []string - }{ - { - proxyNs: "test1", - wantHosts: []string{"svc1", "svc2", "svc3", "svc4"}, - }, - { - proxyNs: "test2", - wantHosts: []string{"svc2", "svc3", "svc4"}, - }, - { - proxyNs: "ns1", - wantHosts: []string{"svc1", "svc2", "svc3", "svc4"}, - }, - { - proxyNs: "random", - wantHosts: []string{"svc3", "svc4"}, - }, - } - for _, tt := range cases { - services := ps.servicesExportedToNamespace(tt.proxyNs) - gotHosts := make([]string, 0) - for _, r := range services { - gotHosts = append(gotHosts, string(r.Hostname)) - } - if !reflect.DeepEqual(gotHosts, tt.wantHosts) { - t.Errorf("proxy in %s namespace: want %+v, got %+v", tt.proxyNs, tt.wantHosts, gotHosts) - } - } -} - -func TestGetHostsFromMeshConfig(t *testing.T) { - ps := NewPushContext() - env := &Environment{Watcher: mesh.NewFixedWatcher(&meshconfig.MeshConfig{ - RootNamespace: "dubbo-system", - ExtensionProviders: []*meshconfig.MeshConfig_ExtensionProvider{ - { - Name: "otel", - Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyOtelAls{ - EnvoyOtelAls: &meshconfig.MeshConfig_ExtensionProvider_EnvoyOpenTelemetryLogProvider{ - Service: "otel.foo.svc.cluster.local", - Port: 9811, - }, - }, - }, - }, - DefaultProviders: &meshconfig.MeshConfig_DefaultProviders{ - AccessLogging: []string{"otel"}, - }, - })} - ps.Mesh = env.Mesh() - configStore := NewFakeStore() - gatewayName := "ns1/gateway" - - vs1 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "vs1", - Namespace: "ns1", - }, - Spec: &networking.VirtualService{ - Hosts: []string{"*.org"}, - Gateways: []string{"gateway"}, - Http: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"}, - }, - }, - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/login"}, - }, - }, - }, - Delegate: &networking.Delegate{ - Name: "vs2", - Namespace: "ns2", - }, - }, - }, - }, - } - vs2 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "vs2", - Namespace: "ns2", - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{gatewayName}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "test", - Port: &networking.PortSelector{ - Number: 80, - }, - }, - }, - }, - }, - }, - }, - } - - for _, c := range []config.Config{vs1, vs2} { - if _, err := configStore.Create(c); err != nil { - t.Fatalf("could not create %v", c.Name) - } - } - - store := istioConfigStore{ConfigStore: configStore} - env.ConfigStore = &store - ps.initTelemetry(env) - ps.initDefaultExportMaps() - if err := ps.initVirtualServices(env); err != nil { - t.Fatalf("init virtual services failed: %v", err) - } - got := getHostsFromMeshConfig(ps) - assert.Equal(t, []string{"otel.foo.svc.cluster.local"}, got.SortedList()) -} - -// TestGetHostsFromMeshConfigExhaustiveness exhaustiveness check of `getHostsFromMeshConfig` -// Once some one add a new `Provider` in api, we should update `wellknownProviders` and -// implements of `getHostsFromMeshConfig` -func TestGetHostsFromMeshConfigExhaustiveness(t *testing.T) { - wellknownProviders := map[string]struct{}{ - "envoy_ext_authz_http": {}, - "envoy_ext_authz_grpc": {}, - "zipkin": {}, - "lightstep": {}, - "datadog": {}, - "opencensus": {}, - "skywalking": {}, - "envoy_http_als": {}, - "envoy_tcp_als": {}, - "envoy_otel_als": {}, - } - - unexpectedProviders := make([]string, 0) - msg := &meshconfig.MeshConfig_ExtensionProvider{} - pb := msg.ProtoReflect() - md := pb.Descriptor() - - of := md.Oneofs().Get(0) - for i := 0; i < of.Fields().Len(); i++ { - o := of.Fields().Get(i) - if o.Message().Fields().ByName("service") != nil { - n := string(o.Name()) - if _, ok := wellknownProviders[n]; ok { - delete(wellknownProviders, n) - } else { - unexpectedProviders = append(unexpectedProviders, n) - } - } - } - - if len(wellknownProviders) != 0 || len(unexpectedProviders) != 0 { - t.Errorf("unexpected provider not implemented in getHostsFromMeshConfig") - t.Fail() - } -} - -var _ ServiceDiscovery = &localServiceDiscovery{} - -// MockDiscovery is an in-memory ServiceDiscover with mock services -type localServiceDiscovery struct { - services []*Service - serviceInstances []*ServiceInstance - - NetworkGatewaysHandler -} - -var _ ServiceDiscovery = &localServiceDiscovery{} - -func (l *localServiceDiscovery) Services() []*Service { - return l.services -} - -func (l *localServiceDiscovery) GetService(host.Name) *Service { - panic("implement me") -} - -func (l *localServiceDiscovery) InstancesByPort(*Service, int, labels.Instance) []*ServiceInstance { - return l.serviceInstances -} - -func (l *localServiceDiscovery) GetProxyServiceInstances(*Proxy) []*ServiceInstance { - panic("implement me") -} - -func (l *localServiceDiscovery) GetProxyWorkloadLabels(*Proxy) labels.Instance { - panic("implement me") -} - -func (l *localServiceDiscovery) GetIstioServiceAccounts(*Service, []int) []string { - return nil -} - -func (l *localServiceDiscovery) NetworkGateways() []NetworkGateway { - // TODO implement fromRegistry logic from kube controller if needed - return nil -} - -func (l *localServiceDiscovery) MCSServices() []MCSServiceInfo { - return nil -} diff --git a/pilot/pkg/model/service.go b/pilot/pkg/model/service.go deleted file mode 100644 index 4e218dbaa..000000000 --- a/pilot/pkg/model/service.go +++ /dev/null @@ -1,884 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// This file describes the abstract model of services (and their instances) as -// represented in Istio. This model is independent of the underlying platform -// (Kubernetes, Mesos, etc.). Platform specific adapters found populate the -// model object with various fields, from the metadata found in the platform. -// The platform independent proxy code uses the representation in the model to -// generate the configuration files for the Layer 7 proxy sidecar. The proxy -// code is specific to individual proxy implementations - -package model - -import ( - "fmt" - "strconv" - "strings" - "time" -) - -import ( - endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" - "github.com/mitchellh/copystructure" - "istio.io/api/label" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/visibility" - "github.com/apache/dubbo-go-pixiu/pkg/network" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// Service describes an Istio service (e.g., catalog.mystore.com:8080) -// Each service has a fully qualified domain name (FQDN) and one or more -// ports where the service is listening for connections. *Optionally*, a -// service can have a single load balancer/virtual IP address associated -// with it, such that the DNS queries for the FQDN resolves to the virtual -// IP address (a load balancer IP). -// -// E.g., in kubernetes, a service foo is associated with -// foo.default.svc.cluster.local hostname, has a virtual IP of 10.0.1.1 and -// listens on ports 80, 8080 -type Service struct { - // Attributes contains additional attributes associated with the service - // used mostly by RBAC for policy enforcement purposes. - Attributes ServiceAttributes - - // Ports is the set of network ports where the service is listening for - // connections - Ports PortList `json:"ports,omitempty"` - - // ServiceAccounts specifies the service accounts that run the service. - ServiceAccounts []string `json:"serviceAccounts,omitempty"` - - // CreationTime records the time this service was created, if available. - CreationTime time.Time `json:"creationTime,omitempty"` - - // Name of the service, e.g. "catalog.mystore.com" - Hostname host.Name `json:"hostname"` - - // ClusterVIPs specifies the service address of the load balancer - // in each of the clusters where the service resides - ClusterVIPs AddressMap `json:"clusterVIPs,omitempty"` - - // DefaultAddress specifies the default service IP of the load balancer. - // Do not access directly. Use GetAddressForProxy - DefaultAddress string `json:"defaultAddress,omitempty"` - - // AutoAllocatedIPv4Address and AutoAllocatedIPv6Address specifies - // the automatically allocated IPv4/IPv6 address out of the reserved - // Class E subnet (240.240.0.0/16) or reserved Benchmarking IP range - // (2001:2::/48) in RFC5180.for service entries with non-wildcard - // hostnames. The IPs assigned to services are not - // synchronized across istiod replicas as the DNS resolution - // for these service entries happens completely inside a pod - // whose proxy is managed by one istiod. That said, the algorithm - // to allocate IPs is pretty deterministic that at stable state, two - // istiods will allocate the exact same set of IPs for a given set of - // service entries. - AutoAllocatedIPv4Address string `json:"autoAllocatedIPv4Address,omitempty"` - AutoAllocatedIPv6Address string `json:"autoAllocatedIPv6Address,omitempty"` - - // Resolution indicates how the service instances need to be resolved before routing - // traffic. Most services in the service registry will use static load balancing wherein - // the proxy will decide the service instance that will receive the traffic. Service entries - // could either use DNS load balancing (i.e. proxy will query DNS server for the IP of the service) - // or use the passthrough model (i.e. proxy will forward the traffic to the network endpoint requested - // by the caller) - Resolution Resolution - - // MeshExternal (if true) indicates that the service is external to the mesh. - // These services are defined using Istio's ServiceEntry spec. - MeshExternal bool - - // ResourceVersion represents the internal version of this object. - ResourceVersion string -} - -func (s *Service) Key() string { - if s == nil { - return "" - } - - return s.Attributes.Namespace + "/" + string(s.Hostname) -} - -// Resolution indicates how the service instances need to be resolved before routing traffic. -type Resolution int - -const ( - // ClientSideLB implies that the proxy will decide the endpoint from its local lb pool - ClientSideLB Resolution = iota - // DNSLB implies that the proxy will resolve a DNS address and forward to the resolved address - DNSLB - // Passthrough implies that the proxy should forward traffic to the destination IP requested by the caller - Passthrough - // DNSRoundRobinLB implies that the proxy will resolve a DNS address and forward to the resolved address - DNSRoundRobinLB -) - -// String converts Resolution in to String. -func (resolution Resolution) String() string { - switch resolution { - case ClientSideLB: - return "ClientSide" - case DNSLB: - return "DNS" - case DNSRoundRobinLB: - return "DNSRoundRobin" - case Passthrough: - return "Passthrough" - default: - return fmt.Sprintf("%d", int(resolution)) - } -} - -const ( - // IstioDefaultConfigNamespace constant for default namespace - IstioDefaultConfigNamespace = "default" - - // LocalityLabel indicates the region/zone/subzone of an instance. It is used to override the native - // registry's value. - // - // Note: because k8s labels does not support `/`, so we use `.` instead in k8s. - LocalityLabel = "istio-locality" - // k8s istio-locality label separator - k8sSeparator = "." -) - -const ( - // TLSModeLabelShortname name used for determining endpoint level tls transport socket configuration - TLSModeLabelShortname = "tlsMode" - - // DisabledTLSModeLabel implies that this endpoint should receive traffic as is (mostly plaintext) - DisabledTLSModeLabel = "disabled" - - // IstioMutualTLSModeLabel implies that the endpoint is ready to receive Istio mTLS connections. - IstioMutualTLSModeLabel = "istio" - - // IstioCanonicalServiceLabelName is the name of label for the Istio Canonical Service for a workload instance. - IstioCanonicalServiceLabelName = "service.istio.io/canonical-name" - - // IstioCanonicalServiceRevisionLabelName is the name of label for the Istio Canonical Service revision for a workload instance. - IstioCanonicalServiceRevisionLabelName = "service.istio.io/canonical-revision" -) - -// Port represents a network port where a service is listening for -// connections. The port should be annotated with the type of protocol -// used by the port. -type Port struct { - // Name ascribes a human readable name for the port object. When a - // service has multiple ports, the name field is mandatory - Name string `json:"name,omitempty"` - - // Port number where the service can be reached. Does not necessarily - // map to the corresponding port numbers for the instances behind the - // service. - Port int `json:"port"` - - // Protocol to be used for the port. - Protocol protocol.Instance `json:"protocol,omitempty"` -} - -// PortList is a set of ports -type PortList []*Port - -// TrafficDirection defines whether traffic exists a service instance or enters a service instance -type TrafficDirection string - -const ( - // TrafficDirectionInbound indicates inbound traffic - TrafficDirectionInbound TrafficDirection = "inbound" - // TrafficDirectionOutbound indicates outbound traffic - TrafficDirectionOutbound TrafficDirection = "outbound" - - // trafficDirectionOutboundSrvPrefix the prefix for a DNS SRV type subset key - trafficDirectionOutboundSrvPrefix = string(TrafficDirectionOutbound) + "_" - // trafficDirectionInboundSrvPrefix the prefix for a DNS SRV type subset key - trafficDirectionInboundSrvPrefix = string(TrafficDirectionInbound) + "_" -) - -// ServiceInstance represents an individual instance of a specific version -// of a service. It binds a network endpoint (ip:port), the service -// description (which is oblivious to various versions) and a set of labels -// that describe the service version associated with this instance. -// -// Since a ServiceInstance has a single IstioEndpoint, which has a single port, -// multiple ServiceInstances are required to represent a workload that listens -// on multiple ports. -// -// The labels associated with a service instance are unique per a network endpoint. -// There is one well defined set of labels for each service instance network endpoint. -// -// For example, the set of service instances associated with catalog.mystore.com -// are modeled like this -// -// --> IstioEndpoint(172.16.0.1:8888), Service(catalog.myservice.com), Labels(foo=bar) -// --> IstioEndpoint(172.16.0.2:8888), Service(catalog.myservice.com), Labels(foo=bar) -// --> IstioEndpoint(172.16.0.3:8888), Service(catalog.myservice.com), Labels(kitty=cat) -// --> IstioEndpoint(172.16.0.4:8888), Service(catalog.myservice.com), Labels(kitty=cat) -type ServiceInstance struct { - Service *Service `json:"service,omitempty"` - ServicePort *Port `json:"servicePort,omitempty"` - Endpoint *IstioEndpoint `json:"endpoint,omitempty"` -} - -// DeepCopy creates a copy of ServiceInstance. -func (instance *ServiceInstance) DeepCopy() *ServiceInstance { - return &ServiceInstance{ - Service: instance.Service.DeepCopy(), - Endpoint: instance.Endpoint.DeepCopy(), - ServicePort: &Port{ - Name: instance.ServicePort.Name, - Port: instance.ServicePort.Port, - Protocol: instance.ServicePort.Protocol, - }, - } -} - -type workloadKind int - -const ( - // PodKind indicates the workload is from pod - PodKind workloadKind = iota - // WorkloadEntryKind indicates the workload is from workloadentry - WorkloadEntryKind -) - -func (k workloadKind) String() string { - if k == PodKind { - return "Pod" - } - - if k == WorkloadEntryKind { - return "WorkloadEntry" - } - return "" -} - -type WorkloadInstance struct { - Name string `json:"name,omitempty"` - Namespace string `json:"namespace,omitempty"` - // Where the workloadInstance come from, valid values are`Pod` or `WorkloadEntry` - Kind workloadKind `json:"kind"` - Endpoint *IstioEndpoint `json:"endpoint,omitempty"` - PortMap map[string]uint32 `json:"portMap,omitempty"` - // Can only be selected by service entry of DNS type. - DNSServiceEntryOnly bool `json:"dnsServiceEntryOnly,omitempty"` -} - -// DeepCopy creates a copy of WorkloadInstance. -func (instance *WorkloadInstance) DeepCopy() *WorkloadInstance { - pmap := map[string]uint32{} - for k, v := range instance.PortMap { - pmap[k] = v - } - return &WorkloadInstance{ - Name: instance.Name, - Namespace: instance.Namespace, - Kind: instance.Kind, - PortMap: pmap, - Endpoint: instance.Endpoint.DeepCopy(), - } -} - -// WorkloadInstancesEqual is a custom comparison of workload instances based on the fields that we need -// i.e. excluding the ports. Returns true if equal, false otherwise. -func WorkloadInstancesEqual(first, second *WorkloadInstance) bool { - if first.Endpoint == nil || second.Endpoint == nil { - return first.Endpoint == second.Endpoint - } - if first.Endpoint.Address != second.Endpoint.Address { - return false - } - if first.Endpoint.Network != second.Endpoint.Network { - return false - } - if first.Endpoint.TLSMode != second.Endpoint.TLSMode { - return false - } - if !first.Endpoint.Labels.Equals(second.Endpoint.Labels) { - return false - } - if first.Endpoint.ServiceAccount != second.Endpoint.ServiceAccount { - return false - } - if first.Endpoint.Locality != second.Endpoint.Locality { - return false - } - if first.Endpoint.GetLoadBalancingWeight() != second.Endpoint.GetLoadBalancingWeight() { - return false - } - if first.Namespace != second.Namespace { - return false - } - if first.Name != second.Name { - return false - } - if first.Kind != second.Kind { - return false - } - if !portMapEquals(first.PortMap, second.PortMap) { - return false - } - return true -} - -func portMapEquals(a, b map[string]uint32) bool { - if len(a) != len(b) { - return false - } - for k, v := range a { - if b[k] != v { - return false - } - } - return true -} - -// GetLocalityLabelOrDefault returns the locality from the supplied label, or falls back to -// the supplied default locality if the supplied label is empty. Because Kubernetes -// labels don't support `/`, we replace "." with "/" in the supplied label as a workaround. -func GetLocalityLabelOrDefault(label, defaultLabel string) string { - if len(label) > 0 { - // if there are /'s present we don't need to replace - if strings.Contains(label, "/") { - return label - } - // replace "." with "/" - return strings.Replace(label, k8sSeparator, "/", -1) - } - return defaultLabel -} - -// SplitLocalityLabel splits a locality label into region, zone and subzone strings. -func SplitLocalityLabel(locality string) (region, zone, subzone string) { - items := strings.Split(locality, "/") - switch len(items) { - case 1: - return items[0], "", "" - case 2: - return items[0], items[1], "" - default: - return items[0], items[1], items[2] - } -} - -// Locality information for an IstioEndpoint -type Locality struct { - // Label for locality on the endpoint. This is a "/" separated string. - Label string - - // ClusterID where the endpoint is located - ClusterID cluster.ID -} - -// Endpoint health status. -type HealthStatus int32 - -const ( - // Healthy. - Healthy HealthStatus = 0 - // Unhealthy. - UnHealthy HealthStatus = 1 -) - -// IstioEndpoint defines a network address (IP:port) associated with an instance of the -// service. A service has one or more instances each running in a -// container/VM/pod. If a service has multiple ports, then the same -// instance IP is expected to be listening on multiple ports (one per each -// service port). Note that the port associated with an instance does not -// have to be the same as the port associated with the service. Depending -// on the network setup (NAT, overlays), this could vary. -// -// For e.g., if catalog.mystore.com is accessible through port 80 and 8080, -// and it maps to an instance with IP 172.16.0.1, such that connections to -// port 80 are forwarded to port 55446, and connections to port 8080 are -// forwarded to port 33333, -// -// then internally, we have two endpoint structs for the -// service catalog.mystore.com -// -// --> 172.16.0.1:55446 (with ServicePort pointing to 80) and -// --> 172.16.0.1:33333 (with ServicePort pointing to 8080) -// -// TODO: Investigate removing ServiceInstance entirely. -type IstioEndpoint struct { - // Labels points to the workload or deployment labels. - Labels labels.Instance - - // Address is the address of the endpoint, using envoy proto. - Address string - - // ServicePortName tracks the name of the port, this is used to select the IstioEndpoint by service port. - ServicePortName string - - // EnvoyEndpoint is a cached LbEndpoint, converted from the data, to - // avoid recomputation - EnvoyEndpoint *endpoint.LbEndpoint - - // ServiceAccount holds the associated service account. - ServiceAccount string - - // Network holds the network where this endpoint is present - Network network.ID - - // The locality where the endpoint is present. - Locality Locality - - // EndpointPort is the port where the workload is listening, can be different - // from the service port. - EndpointPort uint32 - - // The load balancing weight associated with this endpoint. - LbWeight uint32 - - // TLSMode endpoint is injected with istio sidecar and ready to configure Istio mTLS - TLSMode string - - // Namespace that this endpoint belongs to. This is for telemetry purpose. - Namespace string - - // Name of the workload that this endpoint belongs to. This is for telemetry purpose. - WorkloadName string - - // Specifies the hostname of the Pod, empty for vm workload. - HostName string - - // If specified, the fully qualified Pod hostname will be "...svc.". - SubDomain string - - // The ingress tunnel supportability of this endpoint. - // If this endpoint sidecar proxy does not support h2 tunnel, this endpoint will not show up in the EDS clusters - // which are generated for h2 tunnel. - TunnelAbility networking.TunnelAbility - - // Determines the discoverability of this endpoint throughout the mesh. - DiscoverabilityPolicy EndpointDiscoverabilityPolicy `json:"-"` - - // Indicatesthe endpoint health status. - HealthStatus HealthStatus -} - -// GetLoadBalancingWeight returns the weight for this endpoint, normalized to always be > 0. -func (ep *IstioEndpoint) GetLoadBalancingWeight() uint32 { - if ep.LbWeight > 0 { - return ep.LbWeight - } - return 1 -} - -// IsDiscoverableFromProxy indicates whether this endpoint is discoverable from the given Proxy. -func (ep *IstioEndpoint) IsDiscoverableFromProxy(p *Proxy) bool { - if ep == nil || ep.DiscoverabilityPolicy == nil { - // If no policy was assigned, default to discoverable mesh-wide. - // TODO(nmittler): Will need to re-think this default when cluster.local is actually cluster-local. - return true - } - return ep.DiscoverabilityPolicy.IsDiscoverableFromProxy(ep, p) -} - -// EndpointDiscoverabilityPolicy determines the discoverability of an endpoint throughout the mesh. -type EndpointDiscoverabilityPolicy interface { - // IsDiscoverableFromProxy indicates whether an endpoint is discoverable from the given Proxy. - IsDiscoverableFromProxy(*IstioEndpoint, *Proxy) bool - - // String returns name of this policy. - String() string -} - -type endpointDiscoverabilityPolicyImpl struct { - name string - f func(*IstioEndpoint, *Proxy) bool -} - -func (p *endpointDiscoverabilityPolicyImpl) IsDiscoverableFromProxy(ep *IstioEndpoint, proxy *Proxy) bool { - return p.f(ep, proxy) -} - -func (p *endpointDiscoverabilityPolicyImpl) String() string { - return p.name -} - -// AlwaysDiscoverable is an EndpointDiscoverabilityPolicy that allows an endpoint to be discoverable throughout the mesh. -var AlwaysDiscoverable EndpointDiscoverabilityPolicy = &endpointDiscoverabilityPolicyImpl{ - name: "AlwaysDiscoverable", - f: func(*IstioEndpoint, *Proxy) bool { - return true - }, -} - -// DiscoverableFromSameCluster is an EndpointDiscoverabilityPolicy that only allows an endpoint to be discoverable -// from proxies within the same cluster. -var DiscoverableFromSameCluster EndpointDiscoverabilityPolicy = &endpointDiscoverabilityPolicyImpl{ - name: "DiscoverableFromSameCluster", - f: func(ep *IstioEndpoint, p *Proxy) bool { - return p.InCluster(ep.Locality.ClusterID) - }, -} - -// ServiceAttributes represents a group of custom attributes of the service. -type ServiceAttributes struct { - // ServiceRegistry indicates the backing service registry system where this service - // was sourced from. - // TODO: move the ServiceRegistry type from platform.go to model - ServiceRegistry provider.ID - // Name is "destination.service.name" attribute - Name string - // Namespace is "destination.service.namespace" attribute - Namespace string - // Labels applied to the service - Labels map[string]string - // ExportTo defines the visibility of Service in - // a namespace when the namespace is imported. - ExportTo map[visibility.Instance]bool - - // LabelSelectors are the labels used by the service to select workloads. - // Applicable to both Kubernetes and ServiceEntries. - LabelSelectors map[string]string - - // For Kubernetes platform - - // ClusterExternalAddresses is a mapping between a cluster name and the external - // address(es) to access the service from outside the cluster. - // Used by the aggregator to aggregate the Attributes.ClusterExternalAddresses - // for clusters where the service resides - ClusterExternalAddresses AddressMap - - // ClusterExternalPorts is a mapping between a cluster name and the service port - // to node port mappings for a given service. When accessing the service via - // node port IPs, we need to use the kubernetes assigned node ports of the service - // The port that the user provides in the meshNetworks config is the service port. - // We translate that to the appropriate node port here. - ClusterExternalPorts map[cluster.ID]map[uint32]uint32 -} - -// DeepCopy creates a deep copy of ServiceAttributes, but skips internal mutexes. -func (s *ServiceAttributes) DeepCopy() ServiceAttributes { - // Nested mutexes are configured to be ignored by copystructure.Copy. - // Disabling `go vet` warning since this is actually safe in this case. - // nolint: vet - return copyInternal(*s).(ServiceAttributes) -} - -// ServiceDiscovery enumerates Istio service instances. -// nolint: lll -type ServiceDiscovery interface { - NetworkGatewaysWatcher - - // Services list declarations of all services in the system - Services() []*Service - - // GetService retrieves a service by host name if it exists - GetService(hostname host.Name) *Service - - // InstancesByPort retrieves instances for a service on the given ports with labels that match - // any of the supplied labels. All instances match an empty tag list. - // - // For example, consider an example of catalog.mystore.com: - // Instances(catalog.myservice.com, 80) -> - // --> IstioEndpoint(172.16.0.1:8888), Service(catalog.myservice.com), Labels(foo=bar) - // --> IstioEndpoint(172.16.0.2:8888), Service(catalog.myservice.com), Labels(foo=bar) - // --> IstioEndpoint(172.16.0.3:8888), Service(catalog.myservice.com), Labels(kitty=cat) - // --> IstioEndpoint(172.16.0.4:8888), Service(catalog.myservice.com), Labels(kitty=cat) - // - // Calling Instances with specific labels returns a trimmed list. - // e.g., Instances(catalog.myservice.com, 80, foo=bar) -> - // --> IstioEndpoint(172.16.0.1:8888), Service(catalog.myservice.com), Labels(foo=bar) - // --> IstioEndpoint(172.16.0.2:8888), Service(catalog.myservice.com), Labels(foo=bar) - // - // Similar concepts apply for calling this function with a specific - // port, hostname and labels. - // - // Introduced in Istio 0.8. It is only called with 1 port. - // CDS (clusters.go) calls it for building 'dnslb' type clusters. - // EDS calls it for building the endpoints result. - // Consult istio-dev before using this for anything else (except debugging/tools) - InstancesByPort(svc *Service, servicePort int, labels labels.Instance) []*ServiceInstance - - // GetProxyServiceInstances returns the service instances that co-located with a given Proxy - // - // Co-located generally means running in the same network namespace and security context. - // - // A Proxy operating as a Sidecar will return a non-empty slice. A stand-alone Proxy - // will return an empty slice. - // - // There are two reasons why this returns multiple ServiceInstances instead of one: - // - A ServiceInstance has a single IstioEndpoint which has a single Port. But a Service - // may have many ports. So a workload implementing such a Service would need - // multiple ServiceInstances, one for each port. - // - A single workload may implement multiple logical Services. - // - // In the second case, multiple services may be implemented by the same physical port number, - // though with a different ServicePort and IstioEndpoint for each. If any of these overlapping - // services are not HTTP or H2-based, behavior is undefined, since the listener may not be able to - // determine the intended destination of a connection without a Host header on the request. - GetProxyServiceInstances(*Proxy) []*ServiceInstance - GetProxyWorkloadLabels(*Proxy) labels.Instance - - // GetIstioServiceAccounts returns a list of service accounts looked up from - // the specified service hostname and ports. - // Deprecated - service account tracking moved to XdsServer, incremental. - GetIstioServiceAccounts(svc *Service, ports []int) []string - - // MCSServices returns information about the services that have been exported/imported via the - // Kubernetes Multi-Cluster Services (MCS) ServiceExport API. Only applies to services in - // Kubernetes clusters. - MCSServices() []MCSServiceInfo -} - -// MCSServiceInfo combines the name of a service with a particular Kubernetes cluster. This -// is used for debug information regarding the state of Kubernetes Multi-Cluster Services (MCS). -type MCSServiceInfo struct { - Cluster cluster.ID - Name string - Namespace string - Exported bool - Imported bool - ClusterSetVIP string - Discoverability map[host.Name]string -} - -// GetNames returns port names -func (ports PortList) GetNames() []string { - names := make([]string, 0, len(ports)) - for _, port := range ports { - names = append(names, port.Name) - } - return names -} - -// Get retrieves a port declaration by name -func (ports PortList) Get(name string) (*Port, bool) { - for _, port := range ports { - if port.Name == name { - return port, true - } - } - return nil, false -} - -// GetByPort retrieves a port declaration by port value -func (ports PortList) GetByPort(num int) (*Port, bool) { - for _, port := range ports { - if port.Port == num && port.Protocol != protocol.UDP { - return port, true - } - } - return nil, false -} - -// External predicate checks whether the service is external -func (s *Service) External() bool { - return s.MeshExternal -} - -// BuildSubsetKey generates a unique string referencing service instances for a given service name, a subset and a port. -// The proxy queries Pilot with this key to obtain the list of instances in a subset. -func BuildSubsetKey(direction TrafficDirection, subsetName string, hostname host.Name, port int) string { - return string(direction) + "|" + strconv.Itoa(port) + "|" + subsetName + "|" + string(hostname) -} - -// BuildInboundSubsetKey generates a unique string referencing service instances with port. -func BuildInboundSubsetKey(port int) string { - return BuildSubsetKey(TrafficDirectionInbound, "", "", port) -} - -// BuildDNSSrvSubsetKey generates a unique string referencing service instances for a given service name, a subset and a port. -// The proxy queries Pilot with this key to obtain the list of instances in a subset. -// This is used only for the SNI-DNAT router. Do not use for other purposes. -// The DNS Srv format of the cluster is also used as the default SNI string for Istio mTLS connections -func BuildDNSSrvSubsetKey(direction TrafficDirection, subsetName string, hostname host.Name, port int) string { - return string(direction) + "_." + strconv.Itoa(port) + "_." + subsetName + "_." + string(hostname) -} - -// IsValidSubsetKey checks if a string is valid for subset key parsing. -func IsValidSubsetKey(s string) bool { - return strings.Count(s, "|") == 3 -} - -// IsDNSSrvSubsetKey checks whether the given key is a DNSSrv key (built by BuildDNSSrvSubsetKey). -func IsDNSSrvSubsetKey(s string) bool { - if strings.HasPrefix(s, trafficDirectionOutboundSrvPrefix) || - strings.HasPrefix(s, trafficDirectionInboundSrvPrefix) { - return true - } - return false -} - -// ParseSubsetKey is the inverse of the BuildSubsetKey method -func ParseSubsetKey(s string) (direction TrafficDirection, subsetName string, hostname host.Name, port int) { - var parts []string - dnsSrvMode := false - // This could be the DNS srv form of the cluster that uses outbound_.port_.subset_.hostname - // Since we do not want every callsite to implement the logic to differentiate between the two forms - // we add an alternate parser here. - if strings.HasPrefix(s, trafficDirectionOutboundSrvPrefix) || - strings.HasPrefix(s, trafficDirectionInboundSrvPrefix) { - parts = strings.SplitN(s, ".", 4) - dnsSrvMode = true - } else { - parts = strings.Split(s, "|") - } - - if len(parts) < 4 { - return - } - - direction = TrafficDirection(strings.TrimSuffix(parts[0], "_")) - port, _ = strconv.Atoi(strings.TrimSuffix(parts[1], "_")) - subsetName = parts[2] - - if dnsSrvMode { - subsetName = strings.TrimSuffix(parts[2], "_") - } - - hostname = host.Name(parts[3]) - return -} - -// GetAddresses returns a Service's addresses. -// This method returns all the VIPs of a service if the ClusterID is explicitly set to "", otherwise only return the VIP -// specific to the cluster where the node resides -func (s *Service) GetAddresses(node *Proxy) []string { - if node.Metadata != nil && node.Metadata.ClusterID == "" { - return s.getAllAddresses() - } - - return []string{s.GetAddressForProxy(node)} -} - -// GetAddressForProxy returns a Service's address specific to the cluster where the node resides -func (s *Service) GetAddressForProxy(node *Proxy) string { - if node.Metadata != nil { - if node.Metadata.ClusterID != "" { - addresses := s.ClusterVIPs.GetAddressesFor(node.Metadata.ClusterID) - if len(addresses) > 0 { - return addresses[0] - } - } - - if node.Metadata.DNSCapture && node.Metadata.DNSAutoAllocate && s.DefaultAddress == constants.UnspecifiedIP { - if node.SupportsIPv4() && s.AutoAllocatedIPv4Address != "" { - return s.AutoAllocatedIPv4Address - } - if node.SupportsIPv6() && s.AutoAllocatedIPv6Address != "" { - return s.AutoAllocatedIPv6Address - } - } - } - - return s.DefaultAddress -} - -// getAllAddresses returns a Service's all addresses. -func (s *Service) getAllAddresses() []string { - var addresses []string - addressMap := s.ClusterVIPs.GetAddresses() - for _, clusterAddresses := range addressMap { - addresses = append(addresses, clusterAddresses...) - } - - return addresses -} - -// GetTLSModeFromEndpointLabels returns the value of the label -// security.istio.io/tlsMode if set. Do not return Enums or constants -// from this function as users could provide values other than istio/disabled -// and apply custom transport socket matchers here. -func GetTLSModeFromEndpointLabels(labels map[string]string) string { - if labels != nil { - if val, exists := labels[label.SecurityTlsMode.Name]; exists { - return val - } - } - return DisabledTLSModeLabel -} - -// GetServiceAccounts returns aggregated list of service accounts of Service plus its instances. -func GetServiceAccounts(svc *Service, ports []int, discovery ServiceDiscovery) []string { - sa := sets.Set{} - - instances := make([]*ServiceInstance, 0) - // Get the service accounts running service within Kubernetes. This is reflected by the pods that - // the service is deployed on, and the service accounts of the pods. - for _, port := range ports { - svcInstances := discovery.InstancesByPort(svc, port, nil) - instances = append(instances, svcInstances...) - } - - for _, si := range instances { - if si.Endpoint.ServiceAccount != "" { - sa.Insert(si.Endpoint.ServiceAccount) - } - } - sa.InsertAll(svc.ServiceAccounts...) - - return sa.UnsortedList() -} - -// DeepCopy creates a clone of Service. -func (s *Service) DeepCopy() *Service { - // nolint: govet - out := *s - out.Attributes = s.Attributes.DeepCopy() - if s.Ports != nil { - out.Ports = make(PortList, len(s.Ports)) - for i, port := range s.Ports { - if port != nil { - out.Ports[i] = &Port{ - Name: port.Name, - Port: port.Port, - Protocol: port.Protocol, - } - } else { - out.Ports[i] = nil - } - } - } - - if s.ServiceAccounts != nil { - out.ServiceAccounts = make([]string, len(s.ServiceAccounts)) - for i, sa := range s.ServiceAccounts { - out.ServiceAccounts[i] = sa - } - } - out.ClusterVIPs = s.ClusterVIPs.DeepCopy() - return &out -} - -// DeepCopy creates a clone of IstioEndpoint. -func (ep *IstioEndpoint) DeepCopy() *IstioEndpoint { - return copyInternal(ep).(*IstioEndpoint) -} - -func copyInternal(v interface{}) interface{} { - copied, err := copystructure.Copy(v) - if err != nil { - // There are 2 locations where errors are generated in copystructure.Copy: - // * The reflection walk over the structure fails, which should never happen - // * A configurable copy function returns an error. This is only used for copying times, which never returns an error. - // Therefore, this should never happen - panic(err) - } - return copied -} diff --git a/pilot/pkg/model/service_test.go b/pilot/pkg/model/service_test.go deleted file mode 100644 index 8a28837bd..000000000 --- a/pilot/pkg/model/service_test.go +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "reflect" - "testing" -) - -import ( - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - fuzz "github.com/google/gofuzz" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/visibility" -) - -func TestGetByPort(t *testing.T) { - ports := PortList{{ - Name: "http", - Port: 80, - }} - - if port, exists := ports.GetByPort(80); !exists || port == nil || port.Name != "http" { - t.Errorf("GetByPort(80) => want http but got %v, %t", port, exists) - } - if port, exists := ports.GetByPort(88); exists || port != nil { - t.Errorf("GetByPort(88) => want none but got %v, %t", port, exists) - } -} - -func BenchmarkParseSubsetKey(b *testing.B) { - for n := 0; n < b.N; n++ { - ParseSubsetKey("outbound|80|v1|example.com") - ParseSubsetKey("outbound_.8080_.v1_.foo.example.org") - } -} - -func TestParseSubsetKey(t *testing.T) { - tests := []struct { - input string - direction TrafficDirection - subsetName string - hostname host.Name - port int - }{ - {"outbound|80|v1|example.com", TrafficDirectionOutbound, "v1", "example.com", 80}, - {"", "", "", "", 0}, - {"|||", "", "", "", 0}, - {"outbound_.8080_.v1_.foo.example.org", TrafficDirectionOutbound, "v1", "foo.example.org", 8080}, - {"inbound_.8080_.v1_.foo.example.org", TrafficDirectionInbound, "v1", "foo.example.org", 8080}, - } - - for _, tt := range tests { - t.Run(tt.input, func(t *testing.T) { - d, s, h, p := ParseSubsetKey(tt.input) - if d != tt.direction { - t.Errorf("Expected direction %v got %v", tt.direction, d) - } - if s != tt.subsetName { - t.Errorf("Expected subset %v got %v", tt.subsetName, s) - } - if h != tt.hostname { - t.Errorf("Expected hostname %v got %v", tt.hostname, h) - } - if p != tt.port { - t.Errorf("Expected direction %v got %v", tt.port, p) - } - }) - } -} - -func TestIsValidSubsetKey(t *testing.T) { - cases := []struct { - subsetkey string - expectErr bool - }{ - { - subsetkey: "outbound|80|subset|hostname", - expectErr: false, - }, - { - subsetkey: "outbound|80||hostname", - expectErr: false, - }, - { - subsetkey: "outbound|80|subset||hostname", - expectErr: true, - }, - { - subsetkey: "", - expectErr: true, - }, - } - - for _, c := range cases { - err := IsValidSubsetKey(c.subsetkey) - if !err != c.expectErr { - t.Errorf("got %v but want %v\n", err, c.expectErr) - } - } -} - -func TestGetLocalityOrDefault(t *testing.T) { - cases := []struct { - name string - label string - defaultLabel string - expected string - }{ - { - name: "with label", - label: "region/zone/subzone-1", - defaultLabel: "region/zone/subzone-2", - expected: "region/zone/subzone-1", - }, - { - name: "default", - defaultLabel: "region/zone/subzone-1", - expected: "region/zone/subzone-1", - }, - { - name: "label with k8s label separator", - label: "region" + k8sSeparator + "zone" + k8sSeparator + "subzone-2", - expected: "region/zone/subzone-2", - }, - { - name: "label with both k8s label separators and slashes", - label: "region/zone/subzone.2", - expected: "region/zone/subzone.2", - }, - } - - for _, testCase := range cases { - t.Run(testCase.name, func(t *testing.T) { - got := GetLocalityLabelOrDefault(testCase.label, testCase.defaultLabel) - if got != testCase.expected { - t.Errorf("expected locality %s, but got %s", testCase.expected, got) - } - }) - } -} - -func TestWorkloadInstanceEqual(t *testing.T) { - exampleInstance := &WorkloadInstance{ - Endpoint: &IstioEndpoint{ - Labels: labels.Instance{"app": "prod-app"}, - Address: "an-address", - ServicePortName: "service-port-name", - EnvoyEndpoint: nil, - ServiceAccount: "service-account", - Network: "Network", - Locality: Locality{ - ClusterID: "cluster-id", - Label: "region1/zone1/subzone1", - }, - EndpointPort: 22, - LbWeight: 100, - TLSMode: "mutual", - }, - } - differingAddr := exampleInstance.DeepCopy() - differingAddr.Endpoint.Address = "another-address" - differingNetwork := exampleInstance.DeepCopy() - differingNetwork.Endpoint.Network = "AnotherNetwork" - differingTLSMode := exampleInstance.DeepCopy() - differingTLSMode.Endpoint.TLSMode = "permitted" - differingLabels := exampleInstance.DeepCopy() - differingLabels.Endpoint.Labels = labels.Instance{ - "app": "prod-app", - "another-app": "blah", - } - differingServiceAccount := exampleInstance.DeepCopy() - differingServiceAccount.Endpoint.ServiceAccount = "service-account-two" - differingLocality := exampleInstance.DeepCopy() - differingLocality.Endpoint.Locality = Locality{ - ClusterID: "cluster-id-two", - Label: "region2/zone2/subzone2", - } - differingLbWeight := exampleInstance.DeepCopy() - differingLbWeight.Endpoint.LbWeight = 0 - - cases := []struct { - comparer *WorkloadInstance - comparee *WorkloadInstance - shouldEq bool - name string - }{ - { - comparer: &WorkloadInstance{}, - comparee: &WorkloadInstance{}, - shouldEq: true, - name: "two null endpoints", - }, - { - comparer: exampleInstance.DeepCopy(), - comparee: exampleInstance.DeepCopy(), - shouldEq: true, - name: "exact same endpoints", - }, - { - comparer: exampleInstance.DeepCopy(), - comparee: differingAddr.DeepCopy(), - shouldEq: false, - name: "different Addresses", - }, - { - comparer: exampleInstance.DeepCopy(), - comparee: differingNetwork.DeepCopy(), - shouldEq: false, - name: "different Network", - }, - { - comparer: exampleInstance.DeepCopy(), - comparee: differingTLSMode.DeepCopy(), - shouldEq: false, - name: "different TLS Mode", - }, - { - comparer: exampleInstance.DeepCopy(), - comparee: differingLabels.DeepCopy(), - shouldEq: false, - name: "different Labels", - }, - { - comparer: exampleInstance.DeepCopy(), - comparee: differingServiceAccount.DeepCopy(), - shouldEq: false, - name: "different Service Account", - }, - { - comparer: exampleInstance.DeepCopy(), - comparee: differingLocality.DeepCopy(), - shouldEq: false, - name: "different Locality", - }, - { - comparer: exampleInstance.DeepCopy(), - comparee: differingLbWeight.DeepCopy(), - shouldEq: false, - name: "different LbWeight", - }, - } - - for _, testCase := range cases { - t.Run("WorkloadInstancesEqual: "+testCase.name, func(t *testing.T) { - isEq := WorkloadInstancesEqual(testCase.comparer, testCase.comparee) - isEqReverse := WorkloadInstancesEqual(testCase.comparee, testCase.comparer) - - if isEq != isEqReverse { - t.Errorf( - "returned different for reversing arguments for structs: %v , and %v", - testCase.comparer, - testCase.comparee, - ) - } - if isEq != testCase.shouldEq { - t.Errorf( - "equality of %v , and %v do not equal expected %t", - testCase.comparer, - testCase.comparee, - testCase.shouldEq, - ) - } - }) - } -} - -func BenchmarkBuildSubsetKey(b *testing.B) { - for n := 0; n < b.N; n++ { - _ = BuildSubsetKey(TrafficDirectionInbound, "v1", "someHost", 80) - } -} - -func BenchmarkServiceDeepCopy(b *testing.B) { - svc1 := buildHTTPService("test.com", visibility.Public, "10.10.0.1", "default", 80, 8080, 9090, 9999) - svc1.ServiceAccounts = []string{"sa1"} - svc1.ClusterVIPs = AddressMap{ - Addresses: map[cluster.ID][]string{ - "cluster1": {"10.10.0.1"}, - "cluster2": {"10.10.0.2"}, - }, - } - for n := 0; n < b.N; n++ { - _ = svc1.DeepCopy() - } -} - -func TestFuzzServiceDeepCopy(t *testing.T) { - fuzzer := fuzz.New() - originalSvc := &Service{} - fuzzer.Fuzz(originalSvc) - copied := originalSvc.DeepCopy() - if !reflect.DeepEqual(originalSvc, copied) { - cmp.AllowUnexported() - diff := cmp.Diff(originalSvc, copied, cmp.AllowUnexported(), cmpopts.IgnoreFields(AddressMap{}, "mutex")) - t.Errorf("unexpected diff %v", diff) - } -} diff --git a/pilot/pkg/model/sidecar.go b/pilot/pkg/model/sidecar.go deleted file mode 100644 index 8ada77882..000000000 --- a/pilot/pkg/model/sidecar.go +++ /dev/null @@ -1,677 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "encoding/json" - "sort" - "strings" -) - -import ( - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -const ( - wildcardNamespace = "*" - currentNamespace = "." - wildcardService = host.Name("*") -) - -var ( - sidecarScopeKnownConfigTypes = map[config.GroupVersionKind]struct{}{ - gvk.ServiceEntry: {}, - gvk.VirtualService: {}, - gvk.DestinationRule: {}, - gvk.Sidecar: {}, - } - - // clusterScopedConfigTypes includes configs when they are in root namespace, - // they will be applied to all namespaces within the cluster. - clusterScopedConfigTypes = map[config.GroupVersionKind]struct{}{ - gvk.EnvoyFilter: {}, - gvk.AuthorizationPolicy: {}, - gvk.RequestAuthentication: {}, - } -) - -// SidecarScope is a wrapper over the Sidecar resource with some -// preprocessed data to determine the list of services, virtualServices, -// and destinationRules that are accessible to a given -// sidecar. Precomputing the list of services, virtual services, dest rules -// for a sidecar improves performance as we no longer need to compute this -// list for every sidecar. We simply have to match a sidecar to a -// SidecarScope. Note that this is not the same as public/private scoped -// services. The list of services seen by every sidecar scope (namespace -// wide or per workload) depends on the imports, the listeners, and other -// settings. -// -// Every proxy workload of SidecarProxy type will always map to a -// SidecarScope object. If the proxy's namespace does not have a user -// specified Sidecar CRD, we will construct one that has a catch all egress -// listener that imports every public service/virtualService in the mesh. -type SidecarScope struct { - Name string - // This is the namespace where the sidecar takes effect, - // maybe different from the ns where sidecar resides if sidecar is in root ns. - Namespace string - // The crd itself. Can be nil if we are constructing the default - // sidecar scope - Sidecar *networking.Sidecar - - // Version this sidecar was computed for - Version string - - // Set of egress listeners, and their associated services. A sidecar - // scope should have either ingress/egress listeners or both. For - // every proxy workload that maps to a sidecar API object (or the - // default object), we will go through every egress listener in the - // object and process the Envoy listener or RDS based on the imported - // services/virtual services in that listener. - EgressListeners []*IstioEgressListenerWrapper - - // Union of services imported across all egress listeners for use by CDS code. - services []*Service - servicesByHostname map[host.Name]*Service - - // Destination rules imported across all egress listeners. This - // contains the computed set based on public/private destination rules - // as well as the inherited ones, in addition to the wildcard matches - // such as *.com applying to foo.bar.com. Each hostname in this map - // corresponds to a service in the services array above. When computing - // CDS, we simply have to find the matching service and return the - // destination rule. - destinationRules map[host.Name][]*consolidatedDestRule - - // OutboundTrafficPolicy defines the outbound traffic policy for this sidecar. - // If OutboundTrafficPolicy is ALLOW_ANY traffic to unknown destinations will - // be forwarded. - OutboundTrafficPolicy *networking.OutboundTrafficPolicy - - // Set of known configs this sidecar depends on. - // This field will be used to determine the config/resource scope - // which means which config changes will affect the proxies within this scope. - configDependencies map[uint64]struct{} - - // The namespace to treat as the administrative root namespace for - // Istio configuration. - // - // Changes to Sidecar resources in this namespace will trigger a push. - RootNamespace string -} - -// MarshalJSON implements json.Marshaller -func (sc *SidecarScope) MarshalJSON() ([]byte, error) { - // Json cannot expose unexported fields, so copy the ones we want here - return json.MarshalIndent(map[string]interface{}{ - "version": sc.Version, - "rootNamespace": sc.RootNamespace, - "name": sc.Name, - "namespace": sc.Namespace, - "outboundTrafficPolicy": sc.OutboundTrafficPolicy, - "services": sc.services, - "sidecar": sc.Sidecar, - "destinationRules": sc.destinationRules, - }, "", " ") -} - -// IstioEgressListenerWrapper is a wrapper for -// networking.IstioEgressListener object. The wrapper provides performance -// optimizations as it allows us to precompute and store the list of -// services/virtualServices that apply to this listener. -type IstioEgressListenerWrapper struct { - // The actual IstioEgressListener api object from the Config. It can be - // nil if this is for the default sidecar scope. - IstioListener *networking.IstioEgressListener - - // List of services imported by this egress listener above. - // This will be used by LDS and RDS code when - // building the set of virtual hosts or the tcp filterchain matches for - // a given listener port. Two listeners, on user specified ports or - // unix domain sockets could have completely different sets of - // services. So a global list of services per sidecar scope will be - // incorrect. Hence the per listener set of services. - services []*Service - - // List of virtual services imported by this egress listener above. - // As with per listener services, this - // will be used by RDS code to compute the virtual host configs for - // http listeners, as well as by TCP/TLS filter code to compute the - // service routing configs and the filter chain matches. We need a - // virtualService set per listener and not one per sidecarScope because - // each listener imports an independent set of virtual services. - // Listener 1 could import a public virtual service for serviceA from - // namespace A that has some path rewrite, while listener2 could import - // a private virtual service for serviceA from the local namespace, - // with a different path rewrite or no path rewrites. - virtualServices []config.Config - - listenerHosts map[string][]host.Name -} - -const defaultSidecar = "default-sidecar" - -// DefaultSidecarScopeForNamespace is a sidecar scope object with a default catch all egress listener -// that matches the default Istio behavior: a sidecar has listeners for all services in the mesh -// We use this scope when the user has not set any sidecar Config for a given config namespace. -func DefaultSidecarScopeForNamespace(ps *PushContext, configNamespace string) *SidecarScope { - defaultEgressListener := &IstioEgressListenerWrapper{ - IstioListener: &networking.IstioEgressListener{ - Hosts: []string{"*/*"}, - }, - } - defaultEgressListener.services = ps.servicesExportedToNamespace(configNamespace) - defaultEgressListener.virtualServices = ps.VirtualServicesForGateway(configNamespace, constants.IstioMeshGateway) - - out := &SidecarScope{ - Name: defaultSidecar, - Namespace: configNamespace, - EgressListeners: []*IstioEgressListenerWrapper{defaultEgressListener}, - services: defaultEgressListener.services, - destinationRules: make(map[host.Name][]*consolidatedDestRule), - servicesByHostname: make(map[host.Name]*Service, len(defaultEgressListener.services)), - configDependencies: make(map[uint64]struct{}), - RootNamespace: ps.Mesh.RootNamespace, - Version: ps.PushVersion, - } - - // Now that we have all the services that sidecars using this scope (in - // this config namespace) will see, identify all the destinationRules - // that these services need - for _, s := range out.services { - // In some scenarios, there may be multiple Services defined for the same hostname due to ServiceEntry allowing - // arbitrary hostnames. In these cases, we want to pick the first Service, which is the oldest. This ensures - // newly created Services cannot take ownership unexpectedly. - // However, the Service is from Kubernetes it should take precedence over ones not. This prevents someone from - // "domain squatting" on the hostname before a Kubernetes Service is created. - // This relies on the assumption that - if existing, f := out.servicesByHostname[s.Hostname]; f && - !(existing.Attributes.ServiceRegistry != provider.Kubernetes && s.Attributes.ServiceRegistry == provider.Kubernetes) { - continue - } - out.servicesByHostname[s.Hostname] = s - if dr := ps.destinationRule(configNamespace, s); dr != nil { - out.destinationRules[s.Hostname] = dr - } - out.AddConfigDependencies(ConfigKey{ - Kind: gvk.ServiceEntry, - Name: string(s.Hostname), - Namespace: s.Attributes.Namespace, - }) - } - - for _, drList := range out.destinationRules { - for _, dr := range drList { - for _, namespacedName := range dr.from { - out.AddConfigDependencies(ConfigKey{ - Kind: gvk.DestinationRule, - Name: namespacedName.Name, - Namespace: namespacedName.Namespace, - }) - } - } - } - - for _, el := range out.EgressListeners { - // add dependencies on delegate virtual services - delegates := ps.DelegateVirtualServicesConfigKey(el.virtualServices) - for _, delegate := range delegates { - out.AddConfigDependencies(delegate) - } - for _, vs := range el.virtualServices { - out.AddConfigDependencies(ConfigKey{ - Kind: gvk.VirtualService, - Name: vs.Name, - Namespace: vs.Namespace, - }) - } - } - - if ps.Mesh.OutboundTrafficPolicy != nil { - out.OutboundTrafficPolicy = &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_Mode(ps.Mesh.OutboundTrafficPolicy.Mode), - } - } - - return out -} - -// ConvertToSidecarScope converts from Sidecar config to SidecarScope object -func ConvertToSidecarScope(ps *PushContext, sidecarConfig *config.Config, configNamespace string) *SidecarScope { - if sidecarConfig == nil { - return DefaultSidecarScopeForNamespace(ps, configNamespace) - } - - sidecar := sidecarConfig.Spec.(*networking.Sidecar) - out := &SidecarScope{ - Name: sidecarConfig.Name, - Namespace: configNamespace, - Sidecar: sidecar, - configDependencies: make(map[uint64]struct{}), - RootNamespace: ps.Mesh.RootNamespace, - Version: ps.PushVersion, - } - - out.AddConfigDependencies(ConfigKey{ - Kind: gvk.Sidecar, - Name: sidecarConfig.Name, - Namespace: sidecarConfig.Namespace, - }) - - egressConfigs := sidecar.Egress - // If egress not set, setup a default listener - if len(egressConfigs) == 0 { - egressConfigs = append(egressConfigs, &networking.IstioEgressListener{Hosts: []string{"*/*"}}) - } - out.EgressListeners = make([]*IstioEgressListenerWrapper, 0, len(egressConfigs)) - for _, e := range egressConfigs { - out.EgressListeners = append(out.EgressListeners, - convertIstioListenerToWrapper(ps, configNamespace, e)) - } - - // Now collect all the imported services across all egress listeners in - // this sidecar crd. This is needed to generate CDS output - out.services = make([]*Service, 0) - type serviceIndex struct { - svc *Service - index int // index record the position of the svc in slice - } - servicesAdded := make(map[host.Name]serviceIndex) - addService := func(s *Service) { - if s == nil { - return - } - if foundSvc, found := servicesAdded[s.Hostname]; !found { - out.AddConfigDependencies(ConfigKey{ - Kind: gvk.ServiceEntry, - Name: string(s.Hostname), - Namespace: s.Attributes.Namespace, - }) - out.services = append(out.services, s) - servicesAdded[s.Hostname] = serviceIndex{s, len(out.services) - 1} - } else if foundSvc.svc.Attributes.Namespace == s.Attributes.Namespace && s.Ports != nil && len(s.Ports) > 0 { - // merge the ports to service when each listener generates partial service - // we only merge if the found service is in the same namespace as the one we're trying to add - copied := foundSvc.svc.DeepCopy() - for _, p := range s.Ports { - found := false - for _, osp := range copied.Ports { - if p.Port == osp.Port { - found = true - break - } - } - if !found { - copied.Ports = append(copied.Ports, p) - } - } - // replace service in slice - out.services[foundSvc.index] = copied - // Update index as well, so that future reads will merge into the new service - foundSvc.svc = copied - servicesAdded[foundSvc.svc.Hostname] = foundSvc - } - } - - for _, listener := range out.EgressListeners { - // First add the explicitly requested services, which take priority - for _, s := range listener.services { - addService(s) - } - // add dependencies on delegate virtual services - delegates := ps.DelegateVirtualServicesConfigKey(listener.virtualServices) - for _, delegate := range delegates { - out.AddConfigDependencies(delegate) - } - - matchPort := needsPortMatch(listener) - // Infer more possible destinations from virtual services - // Services chosen here will not override services explicitly requested in listener.services. - // That way, if there is ambiguity around what hostname to pick, a user can specify the one they - // want in the hosts field, and the potentially random choice below won't matter - for _, vs := range listener.virtualServices { - v := vs.Spec.(*networking.VirtualService) - out.AddConfigDependencies(ConfigKey{ - Kind: gvk.VirtualService, - Name: vs.Name, - Namespace: vs.Namespace, - }) - - for _, h := range virtualServiceDestinationHosts(v) { - // Default to this hostname in our config namespace - if s, ok := ps.ServiceIndex.HostnameAndNamespace[host.Name(h)][configNamespace]; ok { - // This won't overwrite hostnames that have already been found eg because they were requested in hosts - var vss *Service - if matchPort { - vss = serviceMatchingListenerPort(s, listener) - } else { - vss = s - } - if vss != nil { - addService(vss) - } - } else { - - // We couldn't find the hostname in our config namespace - // We have to pick one arbitrarily for now, so we'll pick the first namespace alphabetically - // TODO: could we choose services more intelligently based on their ports? - byNamespace := ps.ServiceIndex.HostnameAndNamespace[host.Name(h)] - if len(byNamespace) == 0 { - // This hostname isn't found anywhere - log.Debugf("Could not find service hostname %s parsed from %s", h, vs.Key()) - continue - } - - ns := make([]string, 0, len(byNamespace)) - for k := range byNamespace { - if ps.IsServiceVisible(byNamespace[k], configNamespace) { - ns = append(ns, k) - } - } - if len(ns) > 0 { - sort.Strings(ns) - // Pick first namespace alphabetically - // This won't overwrite hostnames that have already been found eg because they were requested in hosts - var vss *Service - if matchPort { - vss = serviceMatchingListenerPort(byNamespace[ns[0]], listener) - } else { - vss = byNamespace[ns[0]] - } - if vss != nil { - addService(vss) - } - } - } - } - } - } - - // Now that we have all the services that sidecars using this scope (in - // this config namespace) will see, identify all the destinationRules - // that these services need - out.servicesByHostname = make(map[host.Name]*Service, len(out.services)) - out.destinationRules = make(map[host.Name][]*consolidatedDestRule) - for _, s := range out.services { - out.servicesByHostname[s.Hostname] = s - drList := ps.destinationRule(configNamespace, s) - if drList != nil { - out.destinationRules[s.Hostname] = drList - for _, dr := range drList { - for _, key := range dr.from { - out.AddConfigDependencies(ConfigKey{ - Kind: gvk.DestinationRule, - Name: key.Name, - Namespace: key.Namespace, - }) - } - } - } - } - - if sidecar.OutboundTrafficPolicy == nil { - if ps.Mesh.OutboundTrafficPolicy != nil { - out.OutboundTrafficPolicy = &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_Mode(ps.Mesh.OutboundTrafficPolicy.Mode), - } - } - } else { - out.OutboundTrafficPolicy = sidecar.OutboundTrafficPolicy - } - - return out -} - -func convertIstioListenerToWrapper(ps *PushContext, configNamespace string, - istioListener *networking.IstioEgressListener) *IstioEgressListenerWrapper { - out := &IstioEgressListenerWrapper{ - IstioListener: istioListener, - } - - out.listenerHosts = make(map[string][]host.Name) - for _, h := range istioListener.Hosts { - parts := strings.SplitN(h, "/", 2) - if len(parts) < 2 { - log.Errorf("Illegal host in sidecar resource: %s, host must be of form namespace/dnsName", h) - continue - } - if parts[0] == currentNamespace { - parts[0] = configNamespace - } - if _, exists := out.listenerHosts[parts[0]]; !exists { - out.listenerHosts[parts[0]] = make([]host.Name, 0) - } - out.listenerHosts[parts[0]] = append(out.listenerHosts[parts[0]], host.Name(parts[1])) - } - - vses := ps.VirtualServicesForGateway(configNamespace, constants.IstioMeshGateway) - out.virtualServices = SelectVirtualServices(vses, out.listenerHosts) - svces := ps.servicesExportedToNamespace(configNamespace) - out.services = out.selectServices(svces, configNamespace, out.listenerHosts) - - return out -} - -// GetEgressListenerForRDS returns the egress listener corresponding to -// the listener port or the bind address or the catch all listener -func (sc *SidecarScope) GetEgressListenerForRDS(port int, bind string) *IstioEgressListenerWrapper { - if sc == nil { - return nil - } - - for _, e := range sc.EgressListeners { - // We hit a catchall listener. This is the last listener in the list of listeners - // return as is - if e.IstioListener == nil || e.IstioListener.Port == nil { - return e - } - - // Check if the ports match - // for unix domain sockets (i.e. port == 0), check if the bind is equal to the routeName - if int(e.IstioListener.Port.Number) == port { - if port == 0 { // unix domain socket - if e.IstioListener.Bind == bind { - return e - } - // no match.. continue searching - continue - } - // this is a non-zero port match - return e - } - } - - // This should never be reached unless user explicitly set an empty array for egress - // listeners which we actually forbid - return nil -} - -// HasIngressListener returns if the sidecar scope has ingress listener set -func (sc *SidecarScope) HasIngressListener() bool { - if sc == nil { - return false - } - - if sc.Sidecar == nil || len(sc.Sidecar.Ingress) == 0 { - return false - } - - return true -} - -// Services returns the list of services imported by this egress listener -func (ilw *IstioEgressListenerWrapper) Services() []*Service { - return ilw.services -} - -// VirtualServices returns the list of virtual services imported by this -// egress listener -func (ilw *IstioEgressListenerWrapper) VirtualServices() []config.Config { - return ilw.virtualServices -} - -// DependsOnConfig determines if the proxy depends on the given config. -// Returns whether depends on this config or this kind of config is not scoped(unknown to be depended) here. -func (sc *SidecarScope) DependsOnConfig(config ConfigKey) bool { - if sc == nil { - return true - } - - // This kind of config will trigger a change if made in the root namespace or the same namespace - if _, f := clusterScopedConfigTypes[config.Kind]; f { - return config.Namespace == sc.RootNamespace || config.Namespace == sc.Namespace - } - - // This kind of config is unknown to sidecarScope. - if _, f := sidecarScopeKnownConfigTypes[config.Kind]; !f { - return true - } - - _, exists := sc.configDependencies[config.HashCode()] - return exists -} - -// AddConfigDependencies add extra config dependencies to this scope. This action should be done before the -// SidecarScope being used to avoid concurrent read/write. -func (sc *SidecarScope) AddConfigDependencies(dependencies ...ConfigKey) { - if sc == nil { - return - } - if sc.configDependencies == nil { - sc.configDependencies = make(map[uint64]struct{}) - } - - for _, config := range dependencies { - sc.configDependencies[config.HashCode()] = struct{}{} - } -} - -// DestinationRule returns a destinationrule for a svc. -func (sc *SidecarScope) DestinationRule(direction TrafficDirection, proxy *Proxy, svc host.Name) *config.Config { - destinationRules := sc.destinationRules[svc] - var catchAllDr *config.Config - for _, destRule := range destinationRules { - destinationRule := destRule.rule.Spec.(*networking.DestinationRule) - if destinationRule.GetWorkloadSelector() == nil { - catchAllDr = destRule.rule - } - // filter DestinationRule based on workloadSelector for outbound configs. - // WorkloadSelector configuration is honored only for outbound configuration, because - // for inbound configuration, the settings at sidecar would be more explicit and the preferred way forward. - if sc.Namespace == destRule.rule.Namespace && - destinationRule.GetWorkloadSelector() != nil && direction == TrafficDirectionOutbound { - workloadSelector := labels.Instance(destinationRule.GetWorkloadSelector().GetMatchLabels()) - // return destination rule if workload selector matches - if workloadSelector.SubsetOf(proxy.Metadata.Labels) { - return destRule.rule - } - } - } - // If there is no workload specific destinationRule, return the wild carded dr if present. - if catchAllDr != nil { - return catchAllDr - } - return nil -} - -// Services returns the list of services that are visible to a sidecar. -func (sc *SidecarScope) Services() []*Service { - return sc.services -} - -// Return filtered services through the hosts field in the egress portion of the Sidecar config. -// Note that the returned service could be trimmed. -func (ilw *IstioEgressListenerWrapper) selectServices(services []*Service, configNamespace string, hosts map[string][]host.Name) []*Service { - importedServices := make([]*Service, 0) - wildcardHosts, wnsFound := hosts[wildcardNamespace] - for _, s := range services { - configNamespace := s.Attributes.Namespace - - // Check if there is an explicit import of form ns/* or ns/host - if importedHosts, nsFound := hosts[configNamespace]; nsFound { - if svc := matchingService(importedHosts, s, ilw); svc != nil { - importedServices = append(importedServices, svc) - } - } else if wnsFound { // Check if there is an import of form */host or */* - if svc := matchingService(wildcardHosts, s, ilw); svc != nil { - importedServices = append(importedServices, svc) - } - } - } - - validServices := make(map[host.Name]string) - for _, svc := range importedServices { - _, f := validServices[svc.Hostname] - // Select a single namespace for a given hostname. - // If the same hostname is imported from multiple namespaces, pick the one in the configNamespace - // If neither are in configNamespace, an arbitrary one will be chosen - if !f || svc.Attributes.Namespace == configNamespace { - validServices[svc.Hostname] = svc.Attributes.Namespace - } - } - - filteredServices := make([]*Service, 0) - // Filter down to just instances in scope for the service - for _, svc := range importedServices { - if validServices[svc.Hostname] == svc.Attributes.Namespace { - filteredServices = append(filteredServices, svc) - } - } - return filteredServices -} - -// Return the original service or a trimmed service which has a subset of the ports in original service. -func matchingService(importedHosts []host.Name, service *Service, ilw *IstioEgressListenerWrapper) *Service { - matchPort := needsPortMatch(ilw) - for _, importedHost := range importedHosts { - // Check if the hostnames match per usual hostname matching rules - if service.Hostname.SubsetOf(importedHost) { - if matchPort { - return serviceMatchingListenerPort(service, ilw) - } - return service - } - } - return nil -} - -// serviceMatchingListenerPort constructs service with listener port. -func serviceMatchingListenerPort(service *Service, ilw *IstioEgressListenerWrapper) *Service { - for _, port := range service.Ports { - if port.Port == int(ilw.IstioListener.Port.GetNumber()) { - sc := service.DeepCopy() - sc.Ports = []*Port{port} - return sc - } - } - return nil -} - -func needsPortMatch(ilw *IstioEgressListenerWrapper) bool { - // If a listener is defined with a port, we should match services with port except in the following case. - // - If Port's protocol is proxy protocol(HTTP_PROXY) in which case the egress listener is used as generic egress http proxy. - return ilw.IstioListener != nil && ilw.IstioListener.Port.GetNumber() != 0 && - protocol.Parse(ilw.IstioListener.Port.Protocol) != protocol.HTTP_PROXY -} diff --git a/pilot/pkg/model/sidecar_test.go b/pilot/pkg/model/sidecar_test.go deleted file mode 100644 index 0fd03b7b6..000000000 --- a/pilot/pkg/model/sidecar_test.go +++ /dev/null @@ -1,2154 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "encoding/json" - "fmt" - "reflect" - "testing" -) - -import ( - "github.com/golang/protobuf/ptypes/wrappers" - "google.golang.org/protobuf/types/known/durationpb" - "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" - "istio.io/api/type/v1beta1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/config/visibility" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -var ( - port9999 = []*Port{ - { - Name: "uds", - Port: 9999, - Protocol: "HTTP", - }, - } - - port7443 = []*Port{ - { - Port: 7443, - Protocol: "GRPC", - Name: "service-grpc-tls", - }, - } - - port7442 = []*Port{ - { - Port: 7442, - Protocol: "HTTP", - Name: "http-tls", - }, - } - - twoMatchingPorts = []*Port{ - { - Port: 7443, - Protocol: "GRPC", - Name: "service-grpc-tls", - }, - { - Port: 7442, - Protocol: "HTTP", - Name: "http-tls", - }, - } - - port8000 = []*Port{ - { - Name: "uds", - Port: 8000, - Protocol: "HTTP", - }, - } - - port9000 = []*Port{ - { - Name: "port1", - Port: 9000, - }, - } - - twoPorts = []*Port{ - { - Name: "uds", - Port: 8000, - Protocol: "HTTP", - }, - { - Name: "uds", - Port: 7000, - Protocol: "HTTP", - }, - } - - allPorts = []*Port{ - { - Name: "uds", - Port: 8000, - Protocol: "HTTP", - }, - { - Name: "uds", - Port: 7000, - Protocol: "HTTP", - }, - { - Name: "port1", - Port: 9000, - }, - } - - configs1 = &config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "not-default", - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Number: 9000, - Protocol: "HTTP", - Name: "uds", - }, - Bind: "1.1.1.1", - Hosts: []string{"*/*"}, - }, - { - Hosts: []string{"*/*"}, - }, - }, - }, - } - configs2 = &config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "not-default", - }, - Spec: &networking.Sidecar{}, - } - - configs3 = &config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "not-default", - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"foo/bar", "*/*"}, - }, - }, - }, - } - - configs4 = &config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "not-default", - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Number: 8000, - Protocol: "HTTP", - Name: "uds", - }, - Hosts: []string{"foo/*"}, - }, - }, - }, - } - - configs5 = &config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "not-default", - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Number: 8000, - Protocol: "HTTP", - Name: "uds", - }, - Hosts: []string{"foo/*"}, - }, - }, - }, - } - - configs6 = &config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "not-default", - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Number: 8000, - Protocol: "HTTP", - Name: "uds", - }, - Hosts: []string{"foo/*"}, - }, - { - Port: &networking.Port{ - Number: 7000, - Protocol: "HTTP", - Name: "uds", - }, - Hosts: []string{"foo/*"}, - }, - }, - }, - } - - configs7 = &config.Config{ - Meta: config.Meta{ - Name: "sidecar-scope-ns1-ns2", - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Number: 23145, - Protocol: "TCP", - Name: "outbound-tcp", - }, - Bind: "7.7.7.7", - Hosts: []string{ - "*/bookinginfo.com", - "*/private.com", - }, - }, - { - Hosts: []string{ - "ns1/*", - "*/*.tcp.com", - }, - }, - }, - }, - } - - configs8 = &config.Config{ - Meta: config.Meta{ - Name: "different-port-name", - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Number: 7443, - Protocol: "GRPC", - Name: "listener-grpc-tls", - }, - Hosts: []string{"mesh/*"}, - }, - }, - }, - } - - configs9 = &config.Config{ - Meta: config.Meta{ - Name: "sidecar-scope-wildcards", - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Number: 7443, - Protocol: "GRPC", - Name: "grpc-tls", - }, - Hosts: []string{"*/*"}, - }, - { - Port: &networking.Port{ - Number: 7442, - Protocol: "HTTP", - Name: "http-tls", - }, - Hosts: []string{"ns2/*"}, - }, - }, - }, - } - - configs10 = &config.Config{ - Meta: config.Meta{ - Name: "sidecar-scope-with-http-proxy", - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Number: 7443, - Protocol: "http_proxy", - Name: "grpc-tls", - }, - Hosts: []string{"*/*"}, - }, - }, - }, - } - - configs11 = &config.Config{ - Meta: config.Meta{ - Name: "sidecar-scope-with-http-proxy-match-virtual-service", - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Number: 7443, - Protocol: "http_proxy", - Name: "grpc-tls", - }, - Hosts: []string{"foo/virtualbar"}, - }, - }, - }, - } - - configs12 = &config.Config{ - Meta: config.Meta{ - Name: "sidecar-scope-with-http-proxy-match-virtual-service-and-service", - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Number: 7443, - Protocol: "http_proxy", - Name: "grpc-tls", - }, - Hosts: []string{"foo/virtualbar", "ns2/foo.svc.cluster.local"}, - }, - }, - }, - } - - configs13 = &config.Config{ - Meta: config.Meta{ - Name: "sidecar-scope-with-illegal-host", - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Number: 7443, - Protocol: "http_proxy", - Name: "grpc-tls", - }, - Hosts: []string{"foo", "foo/bar"}, - }, - }, - }, - } - - configs14 = &config.Config{ - Meta: config.Meta{ - Name: "sidecar-scope-wildcards", - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Number: 7443, - Protocol: "GRPC", - Name: "grpc-tls", - }, - Hosts: []string{"*/*"}, - }, - { - Port: &networking.Port{ - Number: 7442, - Protocol: "HTTP", - Name: "http-tls", - }, - Hosts: []string{"ns2/*"}, - }, - { - Hosts: []string{"*/*"}, - }, - }, - }, - } - - configs15 = &config.Config{ - Meta: config.Meta{ - Name: "sidecar-scope-with-virtual-service", - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Number: 7443, - Protocol: "http", - Name: "grpc-tls", - }, - Hosts: []string{"*/*"}, - }, - }, - }, - } - - configs16 = &config.Config{ - Meta: config.Meta{ - Name: "sidecar-scope-with-specific-host", - Namespace: "ns1", - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"*/en.wikipedia.org"}, - }, - }, - }, - } - - configs17 = &config.Config{ - Meta: config.Meta{ - Name: "sidecar-scope-with-wildcard-host", - Namespace: "ns1", - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"*/*.wikipedia.org"}, - }, - }, - }, - } - - configs18 = &config.Config{ - Meta: config.Meta{ - Name: "sidecar-scope-with-workloadselector-specific-dr-match", - Namespace: "mynamespace", - Labels: map[string]string{"app": "app2"}, - }, - Spec: &networking.Sidecar{}, - } - - configs19 = &config.Config{ - Meta: config.Meta{ - Name: "sidecar-scope-with-workloadselector-specific-dr-no-match", - Namespace: "mynamespace", - Labels: map[string]string{"app": "app5"}, - }, - Spec: &networking.Sidecar{}, - } - - configs20 = &config.Config{ - Meta: config.Meta{ - Name: "sidecar-scope-with-same-workloadselector-label-drs-merged", - Namespace: "mynamespace", - Labels: map[string]string{"app": "app1"}, - }, - Spec: &networking.Sidecar{}, - } - - services1 = []*Service{ - { - Hostname: "bar", - }, - } - - services2 = []*Service{ - { - Hostname: "bar", - Ports: port8000, - }, - { - Hostname: "barprime", - }, - } - - services3 = []*Service{ - { - Hostname: "bar", - Ports: port9000, - }, - { - Hostname: "barprime", - }, - } - - services4 = []*Service{ - { - Hostname: "bar", - }, - { - Hostname: "barprime", - }, - } - - services5 = []*Service{ - { - Hostname: "bar", - Ports: port8000, - Attributes: ServiceAttributes{ - Name: "bar", - Namespace: "foo", - }, - }, - { - Hostname: "barprime", - Attributes: ServiceAttributes{ - Name: "barprime", - Namespace: "foo", - }, - }, - } - - services6 = []*Service{ - { - Hostname: "bar", - Ports: twoPorts, - Attributes: ServiceAttributes{ - Name: "bar", - Namespace: "foo", - }, - }, - } - - services7 = []*Service{ - { - Hostname: "bar", - Ports: twoPorts, - Attributes: ServiceAttributes{ - Name: "bar", - Namespace: "foo", - }, - }, - { - Hostname: "barprime", - Ports: port8000, - Attributes: ServiceAttributes{ - Name: "barprime", - Namespace: "foo", - }, - }, - { - Hostname: "foo", - Ports: allPorts, - Attributes: ServiceAttributes{ - Name: "foo", - Namespace: "foo", - }, - }, - } - - services8 = []*Service{ - { - Hostname: "bookinginfo.com", - Ports: port9999, - Attributes: ServiceAttributes{ - Name: "bookinginfo.com", - Namespace: "ns1", - }, - }, - { - Hostname: "private.com", - Attributes: ServiceAttributes{ - Name: "private.com", - Namespace: "ns1", - }, - }, - } - - services9 = []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port7443, - Attributes: ServiceAttributes{ - Name: "foo", - Namespace: "mesh", - }, - }, - } - - services10 = []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port7443, - Attributes: ServiceAttributes{ - Name: "foo", - Namespace: "ns1", - }, - }, - { - Hostname: "baz.svc.cluster.local", - Ports: port7443, - Attributes: ServiceAttributes{ - Name: "baz", - Namespace: "ns3", - }, - }, - { - Hostname: "bar.svc.cluster.local", - Ports: port7442, - Attributes: ServiceAttributes{ - Name: "bar", - Namespace: "ns2", - }, - }, - { - Hostname: "barprime.svc.cluster.local", - Ports: port7442, - Attributes: ServiceAttributes{ - Name: "barprime", - Namespace: "ns3", - }, - }, - } - - services11 = []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port7443, - Attributes: ServiceAttributes{ - Name: "foo", - Namespace: "ns1", - }, - }, - { - Hostname: "baz.svc.cluster.local", - Ports: port7443, - Attributes: ServiceAttributes{ - Name: "baz", - Namespace: "ns3", - }, - }, - { - Hostname: "bar.svc.cluster.local", - Ports: twoMatchingPorts, - Attributes: ServiceAttributes{ - Name: "bar", - Namespace: "ns2", - }, - }, - { - Hostname: "barprime.svc.cluster.local", - Ports: port7442, - Attributes: ServiceAttributes{ - Name: "barprime", - Namespace: "ns3", - }, - }, - } - - services12 = []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port7443, - Attributes: ServiceAttributes{ - Name: "foo", - Namespace: "ns1", - }, - }, - { - Hostname: "foo.svc.cluster.local", - Ports: port8000, - Attributes: ServiceAttributes{ - Name: "foo", - Namespace: "ns2", - }, - }, - { - Hostname: "baz.svc.cluster.local", - Ports: port7443, - Attributes: ServiceAttributes{ - Name: "baz", - Namespace: "ns3", - }, - }, - } - - services13 = []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port7443, - Attributes: ServiceAttributes{ - Name: "foo", - Namespace: "ns1", - }, - }, - { - Hostname: "foo.svc.cluster.local", - Ports: port8000, - Attributes: ServiceAttributes{ - Name: "foo", - Namespace: "mynamespace", - }, - }, - { - Hostname: "baz.svc.cluster.local", - Ports: port7443, - Attributes: ServiceAttributes{ - Name: "baz", - Namespace: "ns3", - }, - }, - } - - services14 = []*Service{ - { - Hostname: "bar", - Ports: port7443, - Attributes: ServiceAttributes{ - Name: "bar", - Namespace: "foo", - }, - }, - } - - services15 = []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port7443, - Attributes: ServiceAttributes{ - Name: "foo", - Namespace: "ns1", - ExportTo: map[visibility.Instance]bool{visibility.Private: true}, - }, - }, - { - Hostname: "foo.svc.cluster.local", - Ports: port8000, - Attributes: ServiceAttributes{ - Name: "foo", - Namespace: "ns2", - }, - }, - { - Hostname: "baz.svc.cluster.local", - Ports: port7443, - Attributes: ServiceAttributes{ - Name: "baz", - Namespace: "ns3", - }, - }, - } - - services16 = []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port7443, - Attributes: ServiceAttributes{ - Name: "foo", - Namespace: "ns1", - }, - }, - { - Hostname: "baz.svc.cluster.local", - Ports: port7443, - Attributes: ServiceAttributes{ - Name: "baz", - Namespace: "ns3", - }, - }, - { - Hostname: "bar.svc.cluster.local", - Ports: port7442, - Attributes: ServiceAttributes{ - Name: "bar", - Namespace: "ns2", - }, - }, - { - Hostname: "barprime.svc.cluster.local", - Ports: port7442, - Attributes: ServiceAttributes{ - Name: "barprime", - Namespace: "ns3", - }, - }, - { - Hostname: "barprime.svc.cluster.local", - Ports: port7442, - Attributes: ServiceAttributes{ - Name: "barprime", - Namespace: "ns3", - }, - }, - { - Hostname: "random.svc.cluster.local", - Ports: port9999, - Attributes: ServiceAttributes{ - Name: "random", - Namespace: "randomns", // nolint - }, - }, - } - - services17 = []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port7443, - Attributes: ServiceAttributes{ - Name: "foo", - Namespace: "ns1", - }, - }, - { - Hostname: "baz.svc.cluster.local", - Ports: port7442, - Attributes: ServiceAttributes{ - Name: "baz", - Namespace: "ns3", - }, - }, - } - - services18 = []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port7443, - Attributes: ServiceAttributes{ - Name: "foo", - Namespace: "*", - }, - }, - { - Hostname: "baz.svc.cluster.local", - Ports: port7443, - Attributes: ServiceAttributes{ - Name: "baz", - Namespace: "*", - }, - }, - } - - services19 = []*Service{ - { - Hostname: "en.wikipedia.org", - Attributes: ServiceAttributes{ - Name: "en.wikipedia.org", - Namespace: "ns1", - }, - }, - { - Hostname: "*.wikipedia.org", - Attributes: ServiceAttributes{ - Name: "*.wikipedia.org", - Namespace: "ns1", - }, - }, - } - - services20 = []*Service{ - { - Hostname: "httpbin.org", - Attributes: ServiceAttributes{ - Namespace: "mynamespace", - }, - }, - } - - virtualServices1 = []config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "virtualbar", - Namespace: "foo", - }, - Spec: &networking.VirtualService{ - Hosts: []string{"virtualbar"}, - Http: []*networking.HTTPRoute{ - { - Mirror: &networking.Destination{Host: "foo.svc.cluster.local"}, - Route: []*networking.HTTPRouteDestination{{Destination: &networking.Destination{Host: "baz.svc.cluster.local"}}}, - }, - }, - }, - }, - } - - virtualServices2 = []config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "virtualbar", - Namespace: "foo", - }, - Spec: &networking.VirtualService{ - Hosts: []string{"virtualbar", "*"}, - Http: []*networking.HTTPRoute{ - { - Mirror: &networking.Destination{Host: "foo.svc.cluster.local"}, - Route: []*networking.HTTPRouteDestination{{Destination: &networking.Destination{Host: "baz.svc.cluster.local"}}}, - }, - }, - }, - }, - } - destinationRule1 = config.Config{ - Meta: config.Meta{ - Name: "drRule1", - Namespace: "mynamespace", - }, - Spec: &networking.DestinationRule{ - Host: "httpbin.org", - WorkloadSelector: &v1beta1.WorkloadSelector{ - MatchLabels: map[string]string{"app": "app1"}, - }, - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 33, - }, - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - ConnectTimeout: &durationpb.Duration{Seconds: 33}, - }, - }, - }, - }, - } - destinationRule2 = config.Config{ - Meta: config.Meta{ - Name: "drRule2", - Namespace: "mynamespace", - }, - Spec: &networking.DestinationRule{ - Host: "httpbin.org", - WorkloadSelector: &v1beta1.WorkloadSelector{ - MatchLabels: map[string]string{"app": "app2"}, - }, - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 33, - }, - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - ConnectTimeout: &durationpb.Duration{Seconds: 33}, - }, - }, - OutlierDetection: &networking.OutlierDetection{ - Consecutive_5XxErrors: &wrappers.UInt32Value{Value: 3}, - }, - }, - }, - } - mergedDr1and3 = config.Config{ - Meta: config.Meta{ - Name: "drRule1", - Namespace: "mynamespace", - }, - Spec: &networking.DestinationRule{ - Host: "httpbin.org", - WorkloadSelector: &v1beta1.WorkloadSelector{ - MatchLabels: map[string]string{"app": "app1"}, - }, - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 33, - }, - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - ConnectTimeout: &durationpb.Duration{Seconds: 33}, - }, - }, - }, - Subsets: []*networking.Subset{ - { - Name: "subset1", - }, - { - Name: "subset2", - }, - }, - }, - } - destinationRule3 = config.Config{ - Meta: config.Meta{ - Name: "drRule3", - Namespace: "mynamespace", - }, - Spec: &networking.DestinationRule{ - Host: "httpbin.org", - WorkloadSelector: &v1beta1.WorkloadSelector{ - MatchLabels: map[string]string{"app": "app1"}, - }, - Subsets: []*networking.Subset{ - { - Name: "subset1", - }, - { - Name: "subset2", - }, - }, - }, - } - nonWorkloadSelectorDr = config.Config{ - Meta: config.Meta{ - Name: "drRule3", - Namespace: "mynamespace", - }, - Spec: &networking.DestinationRule{ - Host: "httpbin.org", - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 33, - }, - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - ConnectTimeout: &durationpb.Duration{Seconds: 33}, - }, - }, - }, - }, - } -) - -func TestCreateSidecarScope(t *testing.T) { - tests := []struct { - name string - sidecarConfig *config.Config - // list of available service for a given proxy - services []*Service - virtualServices []config.Config - // list of services expected to be in the listener - excpectedServices []*Service - expectedDr *config.Config - }{ - { - "no-sidecar-config", - nil, - nil, - nil, - nil, - nil, - }, - { - "no-sidecar-config-with-service", - nil, - services1, - nil, - []*Service{ - { - Hostname: "bar", - }, - }, - nil, - }, - { - "sidecar-with-multiple-egress", - configs1, - nil, - nil, - nil, - nil, - }, - { - "sidecar-with-multiple-egress-with-service", - configs1, - services1, - nil, - - []*Service{ - { - Hostname: "bar", - }, - }, - nil, - }, - { - "sidecar-with-multiple-egress-with-service-on-same-port", - configs1, - services3, - nil, - []*Service{ - { - Hostname: "bar", - }, - { - Hostname: "barprime", - }, - }, - nil, - }, - { - "sidecar-with-multiple-egress-with-multiple-service", - configs1, - services4, - nil, - []*Service{ - { - Hostname: "bar", - }, - { - Hostname: "barprime", - }, - }, - nil, - }, - { - "sidecar-with-zero-egress", - configs2, - nil, - nil, - nil, - nil, - }, - { - "sidecar-with-zero-egress-multiple-service", - configs2, - services4, - nil, - []*Service{ - { - Hostname: "bar", - }, - { - Hostname: "barprime", - }, - }, - nil, - }, - { - "sidecar-with-multiple-egress-noport", - configs3, - nil, - nil, - nil, - nil, - }, - { - "sidecar-with-multiple-egress-noport-with-specific-service", - configs3, - services2, - nil, - []*Service{ - { - Hostname: "bar", - }, - { - Hostname: "barprime", - }, - }, - nil, - }, - { - "sidecar-with-multiple-egress-noport-with-services", - configs3, - services4, - nil, - []*Service{ - { - Hostname: "bar", - }, - { - Hostname: "barprime", - }, - }, - nil, - }, - { - "sidecar-with-egress-port-match-with-services-with-and-without-port", - configs4, - services5, - nil, - []*Service{ - { - Hostname: "bar", - }, - }, - nil, - }, - { - "sidecar-with-egress-port-trims-service-non-matching-ports", - configs5, - services6, - nil, - []*Service{ - { - Hostname: "bar", - Ports: port8000, - }, - }, - nil, - }, - { - "sidecar-with-egress-port-merges-service-ports", - configs6, - services6, - nil, - []*Service{ - { - Hostname: "bar", - Ports: twoPorts, - }, - }, - nil, - }, - { - "sidecar-with-egress-port-trims-and-merges-service-ports", - configs6, - services7, - nil, - []*Service{ - { - Hostname: "bar", - Ports: twoPorts, - }, - { - Hostname: "barprime", - Ports: port8000, - }, - { - Hostname: "foo", - Ports: twoPorts, - }, - }, - nil, - }, - { - "two-egresslisteners-one-with-port-and-without-port", - configs7, - services8, - nil, - []*Service{ - { - Hostname: "bookinginfo.com", - Ports: port9999, - }, - { - Hostname: "private.com", - }, - }, - nil, - }, - // Validates when service is scoped to Sidecar, it uses service port rather than listener port. - { - "service-port-used-while-cloning", - configs8, - services9, - nil, - []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port7443, - }, - }, - nil, - }, - { - "wild-card-egress-listener-match", - configs9, - services10, - nil, - []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port7443, - }, - { - Hostname: "baz.svc.cluster.local", - Ports: port7443, - }, - { - Hostname: "bar.svc.cluster.local", - Ports: port7442, - Attributes: ServiceAttributes{ - Name: "bar", - Namespace: "ns2", - }, - }, - }, - nil, - }, - { - "wild-card-egress-listener-match-and-all-hosts", - configs14, - services16, - nil, - []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port7443, - }, - { - Hostname: "baz.svc.cluster.local", - Ports: port7443, - }, - { - Hostname: "bar.svc.cluster.local", - Ports: port7442, - Attributes: ServiceAttributes{ - Name: "bar", - Namespace: "ns2", - }, - }, - { - Hostname: "barprime.svc.cluster.local", - Ports: port7442, - Attributes: ServiceAttributes{ - Name: "barprime", - Namespace: "ns3", - }, - }, - { - Hostname: "random.svc.cluster.local", - Ports: port9999, - Attributes: ServiceAttributes{ - Name: "random", - Namespace: "randomns", // nolint - }, - }, - }, - nil, - }, - { - "wild-card-egress-listener-match-with-two-ports", - configs9, - services11, - nil, - []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port7443, - }, - { - Hostname: "baz.svc.cluster.local", - Ports: port7443, - }, - { - Hostname: "bar.svc.cluster.local", - Ports: twoMatchingPorts, - Attributes: ServiceAttributes{ - Name: "bar", - Namespace: "ns2", - }, - }, - }, - nil, - }, - { - "http-proxy-protocol-matches-any-port", - configs10, - services7, - nil, - []*Service{ - { - Hostname: "bar", - }, - { - Hostname: "barprime", - }, - { - Hostname: "foo", - }, - }, - nil, - }, - { - "virtual-service", - configs11, - services11, - virtualServices1, - []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port7443, - }, - { - Hostname: "baz.svc.cluster.local", - Ports: port7443, - }, - }, - nil, - }, - { - "virtual-service-destinations-matching-ports", - configs15, - services17, - virtualServices1, - []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port7443, - }, - }, - nil, - }, - { - "virtual-service-prefer-required", - configs12, - services12, - virtualServices1, - []*Service{ - { - Hostname: "foo.svc.cluster.local", - // Ports should not be merged even though virtual service will select the service with 7443 - // as ns1 comes before ns2, because 8000 was already picked explicitly and is in different namespace - Ports: port8000, - }, - { - Hostname: "baz.svc.cluster.local", - Ports: port7443, - }, - }, - nil, - }, - { - "virtual-service-prefer-config-namespace", - configs11, - services13, - virtualServices1, - []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port8000, - }, - { - Hostname: "baz.svc.cluster.local", - Ports: port7443, - }, - }, - nil, - }, - { - "virtual-service-pick-alphabetical", - configs11, - // Ambiguous; same hostname in ns1 and ns2, neither is config namespace - // ns1 should always win - services12, - virtualServices1, - []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port7443, - }, - { - Hostname: "baz.svc.cluster.local", - Ports: port7443, - }, - }, - nil, - }, - { - "virtual-service-pick-public", - configs11, - // Ambiguous; same hostname in ns1 and ns2, neither is config namespace - // ns1 should always win - services15, - virtualServices1, - []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port8000, - }, - { - Hostname: "baz.svc.cluster.local", - Ports: port7443, - }, - }, - nil, - }, - { - "virtual-service-bad-host", - configs11, - services9, - virtualServices1, - []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port7443, - }, - }, - nil, - }, - { - "virtual-service-2-match-service", - configs11, - services18, - virtualServices2, - []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port7443, - }, - { - Hostname: "baz.svc.cluster.local", - Ports: port7443, - }, - }, - nil, - }, - { - "virtual-service-2-match-service-and-domain", - configs12, - services18, - virtualServices2, - []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port7443, - }, - { - Hostname: "baz.svc.cluster.local", - Ports: port7443, - }, - }, - nil, - }, - { - "virtual-service-2-match-all-services", - configs15, - services18, - virtualServices2, - []*Service{ - { - Hostname: "foo.svc.cluster.local", - Ports: port7443, - }, - { - Hostname: "baz.svc.cluster.local", - Ports: port7443, - }, - }, - nil, - }, - { - "sidecar-scope-with-illegal-host", - configs13, - services14, - nil, - []*Service{ - { - Hostname: "bar", - Ports: port7443, - }, - }, - nil, - }, - { - "sidecar-scope-with-specific-host", - configs16, - services19, - nil, - []*Service{ - { - Hostname: "en.wikipedia.org", - }, - }, - nil, - }, - { - "sidecar-scope-with-wildcard-host", - configs17, - services19, - nil, - []*Service{ - { - Hostname: "en.wikipedia.org", - }, - { - Hostname: "*.wikipedia.org", - }, - }, - nil, - }, - { - "sidecar-scope-with-matching-workloadselector-dr", - configs18, - services20, - nil, - []*Service{ - { - Hostname: "httpbin.org", - Attributes: ServiceAttributes{ - Namespace: "mynamespace", - }, - }, - }, - &destinationRule2, - }, - { - "sidecar-scope-with-non-matching-workloadselector-dr", - configs19, - services20, - nil, - []*Service{ - { - Hostname: "httpbin.org", - Attributes: ServiceAttributes{ - Namespace: "mynamespace", - }, - }, - }, - &nonWorkloadSelectorDr, - }, - { - "sidecar-scope-same-workloadselector-labels-drs-should-be-merged", - configs20, - services20, - nil, - []*Service{ - { - Hostname: "httpbin.org", - Attributes: ServiceAttributes{ - Namespace: "mynamespace", - }, - }, - }, - &mergedDr1and3, - }, - { - name: "multi-service-merge", - sidecarConfig: &config.Config{ - Meta: config.Meta{ - Name: "default", - Namespace: "default", - }, - Spec: &networking.Sidecar{}, - }, - services: []*Service{ - { - Hostname: "proxy", - Ports: port9999, - Attributes: ServiceAttributes{ - Name: "s1", - Namespace: "default", - }, - }, - { - Hostname: "proxy", - Ports: port7443, - Attributes: ServiceAttributes{ - Name: "s2", - Namespace: "default", - }, - }, - { - Hostname: "proxy", - Ports: port7442, - Attributes: ServiceAttributes{ - Name: "s3", - Namespace: "default", - }, - }, - }, - excpectedServices: []*Service{ - { - Hostname: "proxy", - Ports: PortList{port9999[0], port7443[0], port7442[0]}, - Attributes: ServiceAttributes{ - Name: "s1", - Namespace: "default", - }, - }, - }, - }, - } - - for idx, tt := range tests { - t.Run(fmt.Sprintf("[%d] %s", idx, tt.name), func(t *testing.T) { - var found bool - ps := NewPushContext() - meshConfig := mesh.DefaultMeshConfig() - ps.Mesh = meshConfig - ps.SetDestinationRules([]config.Config{destinationRule1, destinationRule2, destinationRule3, nonWorkloadSelectorDr}) - if tt.services != nil { - ps.ServiceIndex.public = append(ps.ServiceIndex.public, tt.services...) - - for _, s := range tt.services { - if _, f := ps.ServiceIndex.HostnameAndNamespace[s.Hostname]; !f { - ps.ServiceIndex.HostnameAndNamespace[s.Hostname] = map[string]*Service{} - } - ps.ServiceIndex.HostnameAndNamespace[s.Hostname][s.Attributes.Namespace] = s - } - } - if tt.virtualServices != nil { - // nolint lll - ps.virtualServiceIndex.publicByGateway[constants.IstioMeshGateway] = append(ps.virtualServiceIndex.publicByGateway[constants.IstioMeshGateway], tt.virtualServices...) - } - - ps.exportToDefaults = exportToDefaults{ - virtualService: map[visibility.Instance]bool{visibility.Public: true}, - service: map[visibility.Instance]bool{visibility.Public: true}, - destinationRule: map[visibility.Instance]bool{visibility.Public: true}, - } - - sidecarConfig := tt.sidecarConfig - sidecarScope := ConvertToSidecarScope(ps, sidecarConfig, "mynamespace") - configuredListeneres := 1 - if sidecarConfig != nil { - r := sidecarConfig.Spec.(*networking.Sidecar) - if len(r.Egress) > 0 { - configuredListeneres = len(r.Egress) - } - } - - numberListeners := len(sidecarScope.EgressListeners) - if numberListeners != configuredListeneres { - t.Errorf("Expected %d listeners, Got: %d", configuredListeneres, numberListeners) - } - - for _, s1 := range sidecarScope.services { - found = false - for _, s2 := range tt.excpectedServices { - if s1.Hostname == s2.Hostname { - if len(s2.Ports) > 0 { - if reflect.DeepEqual(s2.Ports, s1.Ports) { - found = true - break - } - } else { - found = true - break - } - } - } - if !found { - t.Errorf("Expected service %v in SidecarScope but not found", s1.Hostname) - } - } - - for _, s1 := range tt.excpectedServices { - found = false - for _, s2 := range sidecarScope.services { - if s1.Hostname == s2.Hostname { - found = true - break - } - } - if !found { - t.Errorf("UnExpected service %v in SidecarScope", s1.Hostname) - } - } - - if tt.sidecarConfig != nil { - dr := sidecarScope.DestinationRule(TrafficDirectionOutbound, - &Proxy{ - Metadata: &NodeMetadata{Labels: tt.sidecarConfig.Labels}, - ConfigNamespace: tt.sidecarConfig.Namespace, - }, host.Name("httpbin.org")) - assert.Equal(t, dr, tt.expectedDr) - } - }) - } -} - -func TestIstioEgressListenerWrapper(t *testing.T) { - serviceA8000 := &Service{ - Hostname: "host", - Ports: port8000, - Attributes: ServiceAttributes{Namespace: "a"}, - } - serviceA9000 := &Service{ - Hostname: "host", - Ports: port9000, - Attributes: ServiceAttributes{Namespace: "a"}, - } - serviceAalt := &Service{ - Hostname: "alt", - Ports: port8000, - Attributes: ServiceAttributes{Namespace: "a"}, - } - - serviceB8000 := &Service{ - Hostname: "host", - Ports: port8000, - Attributes: ServiceAttributes{Namespace: "b"}, - } - serviceB9000 := &Service{ - Hostname: "host", - Ports: port9000, - Attributes: ServiceAttributes{Namespace: "b"}, - } - serviceBalt := &Service{ - Hostname: "alt", - Ports: port8000, - Attributes: ServiceAttributes{Namespace: "b"}, - } - allServices := []*Service{serviceA8000, serviceA9000, serviceAalt, serviceB8000, serviceB9000, serviceBalt} - - tests := []struct { - name string - listenerHosts map[string][]host.Name - services []*Service - expected []*Service - namespace string - }{ - { - name: "*/* imports only those in a", - listenerHosts: map[string][]host.Name{wildcardNamespace: {wildcardService}}, - services: allServices, - expected: []*Service{serviceA8000, serviceA9000, serviceAalt}, - namespace: "a", - }, - { - name: "*/* will bias towards configNamespace", - listenerHosts: map[string][]host.Name{wildcardNamespace: {wildcardService}}, - services: []*Service{serviceB8000, serviceB9000, serviceBalt, serviceA8000, serviceA9000, serviceAalt}, - expected: []*Service{serviceA8000, serviceA9000, serviceAalt}, - namespace: "a", - }, - { - name: "a/* imports only those in a", - listenerHosts: map[string][]host.Name{"a": {wildcardService}}, - services: allServices, - expected: []*Service{serviceA8000, serviceA9000, serviceAalt}, - namespace: "a", - }, - { - name: "b/*, b/* imports only those in b", - listenerHosts: map[string][]host.Name{"b": {wildcardService, wildcardService}}, - services: allServices, - expected: []*Service{serviceB8000, serviceB9000, serviceBalt}, - namespace: "a", - }, - { - name: "*/alt imports alt in namespace a", - listenerHosts: map[string][]host.Name{wildcardNamespace: {"alt"}}, - services: allServices, - expected: []*Service{serviceAalt}, - namespace: "a", - }, - { - name: "b/alt imports alt in a namespaces", - listenerHosts: map[string][]host.Name{"b": {"alt"}}, - services: allServices, - expected: []*Service{serviceBalt}, - namespace: "a", - }, - { - name: "b/* imports doesn't import in namespace a with proxy in a", - listenerHosts: map[string][]host.Name{"b": {wildcardService}}, - services: []*Service{serviceA8000}, - expected: []*Service{}, - namespace: "a", - }, - { - name: "multiple hosts selected same service", - listenerHosts: map[string][]host.Name{"a": {wildcardService}, "*": {wildcardService}}, - services: []*Service{serviceA8000}, - expected: []*Service{serviceA8000}, - namespace: "a", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ilw := &IstioEgressListenerWrapper{} - got := ilw.selectServices(tt.services, tt.namespace, tt.listenerHosts) - if !reflect.DeepEqual(got, tt.expected) { - gots, _ := json.MarshalIndent(got, "", " ") - expecteds, _ := json.MarshalIndent(tt.expected, "", " ") - t.Errorf("Got %v, expected %v", string(gots), string(expecteds)) - } - }) - } -} - -func TestContainsEgressDependencies(t *testing.T) { - const ( - svcName = "svc1.com" - nsName = "ns" - drName = "dr1" - vsName = "vs1" - ) - - allContains := func(ns string, contains bool) map[ConfigKey]bool { - return map[ConfigKey]bool{ - {gvk.ServiceEntry, svcName, ns}: contains, - {gvk.VirtualService, vsName, ns}: contains, - {gvk.DestinationRule, drName, ns}: contains, - } - } - - cases := []struct { - name string - egress []string - - contains map[ConfigKey]bool - }{ - {"Just wildcard", []string{"*/*"}, allContains(nsName, true)}, - {"Namespace and wildcard", []string{"ns/*", "*/*"}, allContains(nsName, true)}, - {"Just Namespace", []string{"ns/*"}, allContains(nsName, true)}, - {"Wrong Namespace", []string{"ns/*"}, allContains("other-ns", false)}, - {"No Sidecar", nil, allContains("ns", true)}, - {"No Sidecar Other Namespace", nil, allContains("other-ns", false)}, - {"clusterScope resource", []string{"*/*"}, map[ConfigKey]bool{ - {gvk.AuthorizationPolicy, "authz", "default"}: true, - }}, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - cfg := &config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "default", - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: tt.egress, - }, - }, - }, - } - ps := NewPushContext() - meshConfig := mesh.DefaultMeshConfig() - ps.Mesh = meshConfig - - services := []*Service{ - { - Hostname: "nomatch", - Attributes: ServiceAttributes{Namespace: "nomatch"}, - }, - { - Hostname: svcName, - Attributes: ServiceAttributes{Namespace: nsName}, - }, - } - virtualServices := []config.Config{ - { - Meta: config.Meta{ - Name: vsName, - Namespace: nsName, - }, - Spec: &networking.VirtualService{ - Hosts: []string{svcName}, - }, - }, - } - destinationRules := []config.Config{ - { - Meta: config.Meta{ - Name: drName, - Namespace: nsName, - }, - Spec: &networking.DestinationRule{ - Host: svcName, - ExportTo: []string{"*"}, - }, - }, - } - ps.ServiceIndex.public = append(ps.ServiceIndex.public, services...) - // nolint lll - ps.virtualServiceIndex.publicByGateway[constants.IstioMeshGateway] = append(ps.virtualServiceIndex.publicByGateway[constants.IstioMeshGateway], virtualServices...) - ps.SetDestinationRules(destinationRules) - sidecarScope := ConvertToSidecarScope(ps, cfg, "default") - if len(tt.egress) == 0 { - sidecarScope = DefaultSidecarScopeForNamespace(ps, "default") - } - - for k, v := range tt.contains { - if ok := sidecarScope.DependsOnConfig(k); ok != v { - t.Fatalf("Expected contains %v-%v, but no match", k, v) - } - } - }) - } -} - -func TestRootNsSidecarDependencies(t *testing.T) { - cases := []struct { - name string - egress []string - - contains map[ConfigKey]bool - }{ - {"authorizationPolicy in same ns with workload", []string{"*/*"}, map[ConfigKey]bool{ - {gvk.AuthorizationPolicy, "authz", "default"}: true, - }}, - {"authorizationPolicy in different ns with workload", []string{"*/*"}, map[ConfigKey]bool{ - {gvk.AuthorizationPolicy, "authz", "ns1"}: false, - }}, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - cfg := &config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: constants.IstioSystemNamespace, - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: tt.egress, - }, - }, - }, - } - ps := NewPushContext() - meshConfig := mesh.DefaultMeshConfig() - ps.Mesh = meshConfig - sidecarScope := ConvertToSidecarScope(ps, cfg, "default") - if len(tt.egress) == 0 { - sidecarScope = DefaultSidecarScopeForNamespace(ps, "default") - } - - for k, v := range tt.contains { - if ok := sidecarScope.DependsOnConfig(k); ok != v { - t.Fatalf("Expected contains %v-%v, but no match", k, v) - } - } - }) - } -} - -func TestSidecarOutboundTrafficPolicy(t *testing.T) { - configWithoutOutboundTrafficPolicy := &config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "not-default", - }, - Spec: &networking.Sidecar{}, - } - configRegistryOnly := &config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "not-default", - }, - Spec: &networking.Sidecar{ - OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_REGISTRY_ONLY, - }, - }, - } - configAllowAny := &config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "not-default", - }, - Spec: &networking.Sidecar{ - OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, - }, - }, - } - - meshConfigWithRegistryOnly, err := mesh.ApplyMeshConfigDefaults(` -outboundTrafficPolicy: - mode: REGISTRY_ONLY -`) - if err != nil { - t.Fatalf("unexpected error reading test mesh config: %v", err) - } - - tests := []struct { - name string - meshConfig *v1alpha1.MeshConfig - sidecar *config.Config - outboundTrafficPolicy *networking.OutboundTrafficPolicy - }{ - { - name: "default MeshConfig, no Sidecar", - meshConfig: mesh.DefaultMeshConfig(), - sidecar: nil, - outboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, - }, - }, - { - name: "default MeshConfig, sidecar without OutboundTrafficPolicy", - meshConfig: mesh.DefaultMeshConfig(), - sidecar: configWithoutOutboundTrafficPolicy, - outboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, - }, - }, - { - name: "default MeshConfig, Sidecar with registry only", - meshConfig: mesh.DefaultMeshConfig(), - sidecar: configRegistryOnly, - outboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_REGISTRY_ONLY, - }, - }, - { - name: "default MeshConfig, Sidecar with allow any", - meshConfig: mesh.DefaultMeshConfig(), - sidecar: configAllowAny, - outboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, - }, - }, - { - name: "MeshConfig registry only, no Sidecar", - meshConfig: meshConfigWithRegistryOnly, - sidecar: nil, - outboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_REGISTRY_ONLY, - }, - }, - { - name: "MeshConfig registry only, sidecar without OutboundTrafficPolicy", - meshConfig: meshConfigWithRegistryOnly, - sidecar: configWithoutOutboundTrafficPolicy, - outboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_REGISTRY_ONLY, - }, - }, - { - name: "MeshConfig registry only, Sidecar with registry only", - meshConfig: meshConfigWithRegistryOnly, - sidecar: configRegistryOnly, - outboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_REGISTRY_ONLY, - }, - }, - { - name: "MeshConfig registry only, Sidecar with allow any", - meshConfig: meshConfigWithRegistryOnly, - sidecar: configAllowAny, - outboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, - }, - }, - } - - for i, test := range tests { - t.Run(test.name, func(t *testing.T) { - ps := NewPushContext() - ps.Mesh = tests[i].meshConfig - - var sidecarScope *SidecarScope - if test.sidecar == nil { - sidecarScope = DefaultSidecarScopeForNamespace(ps, "not-default") - } else { - sidecarScope = ConvertToSidecarScope(ps, test.sidecar, test.sidecar.Namespace) - } - - if !reflect.DeepEqual(test.outboundTrafficPolicy, sidecarScope.OutboundTrafficPolicy) { - t.Errorf("Unexpected sidecar outbound traffic, want %v, found %v", - test.outboundTrafficPolicy, sidecarScope.OutboundTrafficPolicy) - } - }) - } -} diff --git a/pilot/pkg/model/status/helper.go b/pilot/pkg/model/status/helper.go deleted file mode 100644 index 2a6dbc24f..000000000 --- a/pilot/pkg/model/status/helper.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package status - -import ( - "istio.io/api/meta/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" -) - -const ( - StatusTrue = "True" - StatusFalse = "False" -) - -func GetConditionFromSpec(cfg config.Config, condition string) *v1alpha1.IstioCondition { - c, ok := cfg.Status.(*v1alpha1.IstioStatus) - if !ok { - return nil - } - return GetCondition(c.Conditions, condition) -} - -func GetBoolConditionFromSpec(cfg config.Config, condition string, defaultValue bool) bool { - c, ok := cfg.Status.(*v1alpha1.IstioStatus) - if !ok { - return defaultValue - } - return GetBoolCondition(c.Conditions, condition, defaultValue) -} - -func GetBoolCondition(conditions []*v1alpha1.IstioCondition, condition string, defaultValue bool) bool { - got := GetCondition(conditions, condition) - if got == nil { - return defaultValue - } - if got.Status == StatusTrue { - return true - } - if got.Status == StatusFalse { - return false - } - return defaultValue -} - -func GetCondition(conditions []*v1alpha1.IstioCondition, condition string) *v1alpha1.IstioCondition { - for _, cond := range conditions { - if cond.Type == condition { - return cond - } - } - return nil -} - -func UpdateConfigCondition(cfg config.Config, condition *v1alpha1.IstioCondition) config.Config { - cfg = cfg.DeepCopy() - var status *v1alpha1.IstioStatus - if cfg.Status == nil { - cfg.Status = &v1alpha1.IstioStatus{} - } - status = cfg.Status.(*v1alpha1.IstioStatus) - status.Conditions = UpdateCondition(status.Conditions, condition) - return cfg -} - -func UpdateCondition(conditions []*v1alpha1.IstioCondition, condition *v1alpha1.IstioCondition) []*v1alpha1.IstioCondition { - ret := append([]*v1alpha1.IstioCondition(nil), conditions...) - idx := -1 - for i, cond := range ret { - if cond.Type == condition.Type { - idx = i - break - } - } - if idx == -1 { - ret = append(ret, condition) - } else { - ret[idx] = condition - } - return ret -} diff --git a/pilot/pkg/model/status/workloadentry.go b/pilot/pkg/model/status/workloadentry.go deleted file mode 100644 index 77529204e..000000000 --- a/pilot/pkg/model/status/workloadentry.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package status - -const ( - // WorkloadEntryHealthCheckAnnotation is the annotation that is added to workload entries when - // health checks are enabled. - // If this annotation is present, a WorkloadEntry with the condition Healthy=False or Healthy not set - // should be treated as unhealthy and not sent to proxies - WorkloadEntryHealthCheckAnnotation = "proxy.istio.io/health-checks-enabled" - - // ConditionHealthy defines a status field to declare if a WorkloadEntry is healthy or not - ConditionHealthy = "Healthy" -) diff --git a/pilot/pkg/model/telemetry.go b/pilot/pkg/model/telemetry.go deleted file mode 100644 index df18844b2..000000000 --- a/pilot/pkg/model/telemetry.go +++ /dev/null @@ -1,1000 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "sort" - "strings" - "sync" - "time" -) - -import ( - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - httpwasm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/wasm/v3" - hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - wasmfilter "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/wasm/v3" - wasm "github.com/envoyproxy/go-control-plane/envoy/extensions/wasm/v3" - "google.golang.org/protobuf/types/known/anypb" - "google.golang.org/protobuf/types/known/durationpb" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - sd "istio.io/api/envoy/extensions/stackdriver/config/v1alpha1" - "istio.io/api/envoy/extensions/stats" - meshconfig "istio.io/api/mesh/v1alpha1" - tpb "istio.io/api/telemetry/v1alpha1" - istiolog "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/xds" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -var telemetryLog = istiolog.RegisterScope("telemetry", "Istio Telemetry", 0) - -// Telemetry holds configuration for Telemetry API resources. -type Telemetry struct { - Name string `json:"name"` - Namespace string `json:"namespace"` - Spec *tpb.Telemetry `json:"spec"` -} - -// Telemetries organizes Telemetry configuration by namespace. -type Telemetries struct { - // Maps from namespace to the Telemetry configs. - NamespaceToTelemetries map[string][]Telemetry `json:"namespace_to_telemetries"` - - // The name of the root namespace. - RootNamespace string `json:"root_namespace"` - - // Computed meshConfig - meshConfig *meshconfig.MeshConfig - - // computedMetricsFilters contains the set of cached HCM/listener filters for the metrics portion. - // These filters are extremely costly, as we insert them into every listener on every proxy, and to - // generate them we need to merge many telemetry specs and perform 2 Any marshals. - // To improve performance, we store a cache based on the Telemetries that impacted the filter, as well as - // its class and protocol. This is protected by mu. - // Currently, this only applies to metrics, but a similar concept can likely be applied to logging and - // tracing for performance. - // The computedMetricsFilters lifetime is bound to the Telemetries object. During a push context - // creation, we will preserve the Telemetries (and thus the cache) if not Telemetries are modified. - // As result, this cache will live until any Telemetry is modified. - computedMetricsFilters map[metricsKey]interface{} - mu sync.Mutex -} - -// telemetryKey defines a key into the computedMetricsFilters cache. -type telemetryKey struct { - // Root stores the Telemetry in the root namespace, if any - Root NamespacedName - // Namespace stores the Telemetry in the root namespace, if any - Namespace NamespacedName - // Workload stores the Telemetry in the root namespace, if any - Workload NamespacedName -} - -// metricsKey defines a key into the computedMetricsFilters cache. -type metricsKey struct { - telemetryKey - Class networking.ListenerClass - Protocol networking.ListenerProtocol -} - -// getTelemetries returns the Telemetry configurations for the given environment. -func getTelemetries(env *Environment) (*Telemetries, error) { - telemetries := &Telemetries{ - NamespaceToTelemetries: map[string][]Telemetry{}, - RootNamespace: env.Mesh().GetRootNamespace(), - meshConfig: env.Mesh(), - computedMetricsFilters: map[metricsKey]interface{}{}, - } - - fromEnv, err := env.List(collections.IstioTelemetryV1Alpha1Telemetries.Resource().GroupVersionKind(), NamespaceAll) - if err != nil { - return nil, err - } - sortConfigByCreationTime(fromEnv) - for _, config := range fromEnv { - telemetry := Telemetry{ - Name: config.Name, - Namespace: config.Namespace, - Spec: config.Spec.(*tpb.Telemetry), - } - telemetries.NamespaceToTelemetries[config.Namespace] = append(telemetries.NamespaceToTelemetries[config.Namespace], telemetry) - } - - return telemetries, nil -} - -type metricsConfig struct { - ClientMetrics []metricsOverride - ServerMetrics []metricsOverride -} - -type telemetryFilterConfig struct { - metricsConfig - Provider *meshconfig.MeshConfig_ExtensionProvider - Metrics bool - AccessLogging bool - LogsFilter *tpb.AccessLogging_Filter -} - -func (t telemetryFilterConfig) MetricsForClass(c networking.ListenerClass) []metricsOverride { - switch c { - case networking.ListenerClassGateway: - return t.ClientMetrics - case networking.ListenerClassSidecarInbound: - return t.ServerMetrics - case networking.ListenerClassSidecarOutbound: - return t.ClientMetrics - default: - return t.ClientMetrics - } -} - -type metricsOverride struct { - Name string - Disabled bool - Tags []tagOverride -} - -type tagOverride struct { - Name string - Remove bool - Value string -} - -// computedTelemetries contains the various Telemetry configurations in scope for a given proxy. -// This can include the root namespace, namespace, and workload Telemetries combined -type computedTelemetries struct { - telemetryKey - Metrics []*tpb.Metrics - Logging []*tpb.AccessLogging - Tracing []*tpb.Tracing -} - -type TracingConfig struct { - ServerSpec TracingSpec - ClientSpec TracingSpec -} - -type TracingSpec struct { - Provider *meshconfig.MeshConfig_ExtensionProvider - Disabled bool - RandomSamplingPercentage float64 - CustomTags map[string]*tpb.Tracing_CustomTag - UseRequestIDForTraceSampling bool -} - -type LoggingConfig struct { - Providers []*meshconfig.MeshConfig_ExtensionProvider - Filter *tpb.AccessLogging_Filter -} - -func workloadMode(class networking.ListenerClass) tpb.WorkloadMode { - switch class { - case networking.ListenerClassGateway: - return tpb.WorkloadMode_CLIENT - case networking.ListenerClassSidecarInbound: - return tpb.WorkloadMode_SERVER - case networking.ListenerClassSidecarOutbound: - return tpb.WorkloadMode_CLIENT - case networking.ListenerClassUndefined: - // this should not happened, just in case - return tpb.WorkloadMode_CLIENT - } - - return tpb.WorkloadMode_CLIENT -} - -// AccessLogging returns the logging configuration for a given proxy and listener class. -// If nil is returned, access logs are not configured via Telemetry and should use fallback mechanisms. -// If a non-nil but empty configuration is passed, access logging is explicitly disabled. -func (t *Telemetries) AccessLogging(proxy *Proxy, class networking.ListenerClass) *LoggingConfig { - ct := t.applicableTelemetries(proxy) - if len(ct.Logging) == 0 && len(t.meshConfig.GetDefaultProviders().GetAccessLogging()) == 0 { - return nil - } - - cfg := LoggingConfig{} - providers, f := mergeLogs(ct.Logging, t.meshConfig, workloadMode(class)) - cfg.Filter = f - for _, p := range providers.SortedList() { - fp := t.fetchProvider(p) - if fp != nil { - cfg.Providers = append(cfg.Providers, fp) - } - } - return &cfg -} - -// Tracing returns the logging tracing for a given proxy. If nil is returned, tracing -// are not configured via Telemetry and should use fallback mechanisms. If a non-nil but disabled is set, -// then tracing is explicitly disabled -func (t *Telemetries) Tracing(proxy *Proxy) *TracingConfig { - ct := t.applicableTelemetries(proxy) - - providerNames := t.meshConfig.GetDefaultProviders().GetTracing() - hasDefaultProvider := len(providerNames) > 0 - - if len(ct.Tracing) == 0 && !hasDefaultProvider { - return nil - } - - clientSpec := TracingSpec{UseRequestIDForTraceSampling: true} - serverSpec := TracingSpec{UseRequestIDForTraceSampling: true} - - if hasDefaultProvider { - // todo: what do we want to do with more than one default provider? - // for now, use only the first provider. - fetched := t.fetchProvider(providerNames[0]) - clientSpec.Provider = fetched - serverSpec.Provider = fetched - } - - for _, m := range ct.Tracing { - names := getProviderNames(m.Providers) - - specs := []*TracingSpec{&clientSpec, &serverSpec} - if m.Match != nil { - switch m.Match.Mode { - case tpb.WorkloadMode_CLIENT: - specs = []*TracingSpec{&clientSpec} - case tpb.WorkloadMode_SERVER: - specs = []*TracingSpec{&serverSpec} - } - } - - if len(names) > 0 { - // NOTE: we only support a single provider per mode - // so, choosing the first provider returned in the list - // is the "safest" - fetched := t.fetchProvider(names[0]) - for _, spec := range specs { - spec.Provider = fetched - } - } - - // Now merge in any overrides - if m.DisableSpanReporting != nil { - for _, spec := range specs { - spec.Disabled = m.DisableSpanReporting.GetValue() - } - } - // TODO: metrics overrides do a deep merge, but here we do a shallow merge. - // We should consider if we want to reconcile the two. - if m.CustomTags != nil { - for _, spec := range specs { - spec.CustomTags = m.CustomTags - } - } - if m.RandomSamplingPercentage != nil { - for _, spec := range specs { - spec.RandomSamplingPercentage = m.RandomSamplingPercentage.GetValue() - } - } - if m.UseRequestIdForTraceSampling != nil { - for _, spec := range specs { - spec.UseRequestIDForTraceSampling = m.UseRequestIdForTraceSampling.Value - } - } - } - - // If no provider is configured (and retrieved) for the tracing specs, - // then we will disable the configuration. - if clientSpec.Provider == nil { - clientSpec.Disabled = true - } - if serverSpec.Provider == nil { - serverSpec.Disabled = true - } - - cfg := TracingConfig{ - ClientSpec: clientSpec, - ServerSpec: serverSpec, - } - return &cfg -} - -// HTTPFilters computes the HttpFilter for a given proxy/class -func (t *Telemetries) HTTPFilters(proxy *Proxy, class networking.ListenerClass) []*hcm.HttpFilter { - if res := t.telemetryFilters(proxy, class, networking.ListenerProtocolHTTP); res != nil { - return res.([]*hcm.HttpFilter) - } - return nil -} - -// TCPFilters computes the TCPFilters for a given proxy/class -func (t *Telemetries) TCPFilters(proxy *Proxy, class networking.ListenerClass) []*listener.Filter { - if res := t.telemetryFilters(proxy, class, networking.ListenerProtocolTCP); res != nil { - return res.([]*listener.Filter) - } - return nil -} - -// applicableTelemetries fetches the relevant telemetry configurations for a given proxy -func (t *Telemetries) applicableTelemetries(proxy *Proxy) computedTelemetries { - if t == nil { - return computedTelemetries{} - } - - namespace := proxy.ConfigNamespace - // Order here matters. The latter elements will override the first elements - ms := []*tpb.Metrics{} - ls := []*tpb.AccessLogging{} - ts := []*tpb.Tracing{} - key := telemetryKey{} - if t.RootNamespace != "" { - telemetry := t.namespaceWideTelemetryConfig(t.RootNamespace) - if telemetry != (Telemetry{}) { - key.Root = NamespacedName{Name: telemetry.Name, Namespace: telemetry.Namespace} - ms = append(ms, telemetry.Spec.GetMetrics()...) - ls = append(ls, telemetry.Spec.GetAccessLogging()...) - ts = append(ts, telemetry.Spec.GetTracing()...) - } - } - - if namespace != t.RootNamespace { - telemetry := t.namespaceWideTelemetryConfig(namespace) - if telemetry != (Telemetry{}) { - key.Namespace = NamespacedName{Name: telemetry.Name, Namespace: telemetry.Namespace} - ms = append(ms, telemetry.Spec.GetMetrics()...) - ls = append(ls, telemetry.Spec.GetAccessLogging()...) - ts = append(ts, telemetry.Spec.GetTracing()...) - } - } - - for _, telemetry := range t.NamespaceToTelemetries[namespace] { - spec := telemetry.Spec - if len(spec.GetSelector().GetMatchLabels()) == 0 { - continue - } - selector := labels.Instance(spec.GetSelector().GetMatchLabels()) - if selector.SubsetOf(proxy.Metadata.Labels) { - key.Workload = NamespacedName{Name: telemetry.Name, Namespace: telemetry.Namespace} - ms = append(ms, spec.GetMetrics()...) - ls = append(ls, spec.GetAccessLogging()...) - ts = append(ts, spec.GetTracing()...) - break - } - } - - return computedTelemetries{ - telemetryKey: key, - Metrics: ms, - Logging: ls, - Tracing: ts, - } -} - -// telemetryFilters computes the filters for the given proxy/class and protocol. This computes the -// set of applicable Telemetries, merges them, then translates to the appropriate filters based on the -// extension providers in the mesh config. Where possible, the result is cached. -// Currently, this includes metrics and access logging, as some providers are implemented in filters. -func (t *Telemetries) telemetryFilters(proxy *Proxy, class networking.ListenerClass, protocol networking.ListenerProtocol) interface{} { - if t == nil { - return nil - } - - c := t.applicableTelemetries(proxy) - - key := metricsKey{ - telemetryKey: c.telemetryKey, - Class: class, - Protocol: protocol, - } - t.mu.Lock() - defer t.mu.Unlock() - precomputed, f := t.computedMetricsFilters[key] - if f { - return precomputed - } - - // First, take all the metrics configs and transform them into a normalized form - tmm := mergeMetrics(c.Metrics, t.meshConfig) - log.Debugf("merged metrics, proxyID: %s metrics: %+v", proxy.ID, tmm) - // Additionally, fetch relevant access logging configurations - tml, logsFilter := mergeLogs(c.Logging, t.meshConfig, workloadMode(class)) - - // The above result is in a nested map to deduplicate responses. This loses ordering, so we convert to - // a list to retain stable naming - allKeys := sets.New(tml.UnsortedList()...) - for k := range tmm { - allKeys.Insert(k) - } - - m := make([]telemetryFilterConfig, 0, len(allKeys)) - for _, k := range allKeys.SortedList() { - p := t.fetchProvider(k) - if p == nil { - continue - } - _, logging := tml[k] - _, metrics := tmm[k] - cfg := telemetryFilterConfig{ - Provider: p, - metricsConfig: tmm[k], - AccessLogging: logging, - Metrics: metrics, - LogsFilter: logsFilter, - } - m = append(m, cfg) - } - - var res interface{} - // Finally, compute the actual filters based on the protoc - switch protocol { - case networking.ListenerProtocolHTTP: - res = buildHTTPTelemetryFilter(class, m) - default: - res = buildTCPTelemetryFilter(class, m) - } - - // Update cache - t.computedMetricsFilters[key] = res - return res -} - -// mergeLogs returns the set of providers for the given logging configuration. -func mergeLogs(logs []*tpb.AccessLogging, mesh *meshconfig.MeshConfig, mode tpb.WorkloadMode) (sets.Set, *tpb.AccessLogging_Filter) { - providers := sets.New() - - if len(logs) == 0 { - for _, dp := range mesh.GetDefaultProviders().GetAccessLogging() { - // Insert the default provider. - providers.Insert(dp) - } - return providers, nil - } - var loggingFilter *tpb.AccessLogging_Filter - providerNames := mesh.GetDefaultProviders().GetAccessLogging() - for _, m := range logs { - names := getProviderNames(m.Providers) - if len(names) > 0 { - providerNames = names - } - - if m.Filter != nil { - loggingFilter = m.Filter - } - } - inScopeProviders := sets.New(providerNames...) - - parentProviders := mesh.GetDefaultProviders().GetAccessLogging() - for _, m := range logs { - providerNames := getProviderNames(m.Providers) - if len(providerNames) == 0 { - providerNames = parentProviders - } - parentProviders = providerNames - for _, provider := range providerNames { - if !inScopeProviders.Contains(provider) { - // We don't care about this, remove it - // This occurs when a top level provider is later disabled by a lower level - continue - } - - if !matchWorkloadMode(m.Match, mode) { - continue - } - - if m.GetDisabled().GetValue() { - providers.Delete(provider) - continue - } - - providers.Insert(provider) - } - } - - return providers, loggingFilter -} - -func matchWorkloadMode(selector *tpb.AccessLogging_LogSelector, mode tpb.WorkloadMode) bool { - if selector == nil { - return true - } - - if selector.Mode == tpb.WorkloadMode_CLIENT_AND_SERVER { - return true - } - - return selector.Mode == mode -} - -func (t *Telemetries) namespaceWideTelemetryConfig(namespace string) Telemetry { - for _, tel := range t.NamespaceToTelemetries[namespace] { - if len(tel.Spec.GetSelector().GetMatchLabels()) == 0 { - return tel - } - } - return Telemetry{} -} - -// fetchProvider finds the matching ExtensionProviders from the mesh config -func (t *Telemetries) fetchProvider(m string) *meshconfig.MeshConfig_ExtensionProvider { - for _, p := range t.meshConfig.ExtensionProviders { - if strings.EqualFold(m, p.Name) { - return p - } - } - return nil -} - -var allMetrics = func() []string { - r := make([]string, 0, len(tpb.MetricSelector_IstioMetric_value)) - for k := range tpb.MetricSelector_IstioMetric_value { - if k != tpb.MetricSelector_IstioMetric_name[int32(tpb.MetricSelector_ALL_METRICS)] { - r = append(r, k) - } - } - sort.Strings(r) - return r -}() - -// mergeMetrics merges many Metrics objects into a normalized configuration -func mergeMetrics(metrics []*tpb.Metrics, mesh *meshconfig.MeshConfig) map[string]metricsConfig { - type metricOverride struct { - Disabled *wrappers.BoolValue - TagOverrides map[string]*tpb.MetricsOverrides_TagOverride - } - // provider -> mode -> metric -> overrides - providers := map[string]map[tpb.WorkloadMode]map[string]metricOverride{} - - if len(metrics) == 0 { - for _, dp := range mesh.GetDefaultProviders().GetMetrics() { - // Insert the default provider. It has no overrides; presence of the key is sufficient to - // get the filter created. - providers[dp] = map[tpb.WorkloadMode]map[string]metricOverride{} - } - } - - providerNames := mesh.GetDefaultProviders().GetMetrics() - for _, m := range metrics { - names := getProviderNames(m.Providers) - // If providers is set, it overrides the parent. If not, inherent from the parent. It is not a deep merge. - if len(names) > 0 { - providerNames = names - } - } - // Record the names of all providers we should configure. Anything else we will ignore - inScopeProviders := sets.New(providerNames...) - - parentProviders := mesh.GetDefaultProviders().GetMetrics() - disabledAllMetricsProviders := sets.New() - for _, m := range metrics { - providerNames := getProviderNames(m.Providers) - // If providers is not set, use parent's - if len(providerNames) == 0 { - providerNames = parentProviders - } - parentProviders = providerNames - for _, provider := range providerNames { - if !inScopeProviders.Contains(provider) { - // We don't care about this, remove it - // This occurs when a top level provider is later disabled by a lower level - continue - } - if _, f := providers[provider]; !f { - providers[provider] = map[tpb.WorkloadMode]map[string]metricOverride{ - tpb.WorkloadMode_CLIENT: {}, - tpb.WorkloadMode_SERVER: {}, - } - } - - mp := providers[provider] - // For each override, we normalize the configuration. The metrics list is an ordered list - latter - // elements have precedence. As a result, we will apply updates on top of previous entries. - for _, o := range m.Overrides { - // if we disable all metrics, we should drop the entire filter - if isAllMetrics(o.GetMatch()) && o.Disabled.GetValue() { - disabledAllMetricsProviders.Insert(provider) - continue - } - - // root namespace disables all, but then enables them by namespace scoped - disabledAllMetricsProviders.Delete(provider) - - metricsNames := getMatches(o.GetMatch()) - // If client or server is set explicitly, only apply there. Otherwise, we will apply to both. - // Note: client and server keys may end up the same, which is fine - for _, mode := range getModes(o.GetMatch().GetMode()) { - // Next, get all matches. - // This is a bit funky because the matches are oneof of ENUM and customer metric. We normalize - // these to strings, so we may end up with a list like [REQUEST_COUNT, my-customer-metric]. - // TODO: we always flatten ALL_METRICS into each metric mode. For some stats providers (prometheus), - // we are able to apply overrides to all metrics directly rather than duplicating the config. - // We should tweak this to collapse to this mode where possible - for _, metricName := range metricsNames { - if _, f := mp[mode]; !f { - mp[mode] = map[string]metricOverride{} - } - override := mp[mode][metricName] - if o.Disabled != nil { - override.Disabled = o.Disabled - } - for k, v := range o.TagOverrides { - if override.TagOverrides == nil { - override.TagOverrides = map[string]*tpb.MetricsOverrides_TagOverride{} - } - override.TagOverrides[k] = v - } - mp[mode][metricName] = override - } - } - } - } - } - - processed := map[string]metricsConfig{} - for provider, modeMap := range providers { - if disabledAllMetricsProviders.Contains(provider) { - continue - } - - for mode, metricMap := range modeMap { - for metric, override := range metricMap { - tags := []tagOverride{} - for k, v := range override.TagOverrides { - o := tagOverride{Name: k} - switch v.Operation { - case tpb.MetricsOverrides_TagOverride_REMOVE: - o.Remove = true - o.Value = "" - case tpb.MetricsOverrides_TagOverride_UPSERT: - o.Value = v.GetValue() - o.Remove = false - } - tags = append(tags, o) - } - // Keep order deterministic - sort.Slice(tags, func(i, j int) bool { - return tags[i].Name < tags[j].Name - }) - mo := metricsOverride{ - Name: metric, - Disabled: override.Disabled.GetValue(), - Tags: tags, - } - tmm := processed[provider] - switch mode { - case tpb.WorkloadMode_CLIENT: - tmm.ClientMetrics = append(tmm.ClientMetrics, mo) - default: - tmm.ServerMetrics = append(tmm.ServerMetrics, mo) - } - processed[provider] = tmm - } - } - - // Keep order deterministic - tmm := processed[provider] - sort.Slice(tmm.ServerMetrics, func(i, j int) bool { - return tmm.ServerMetrics[i].Name < tmm.ServerMetrics[j].Name - }) - sort.Slice(tmm.ClientMetrics, func(i, j int) bool { - return tmm.ClientMetrics[i].Name < tmm.ClientMetrics[j].Name - }) - processed[provider] = tmm - } - return processed -} - -func getProviderNames(providers []*tpb.ProviderRef) []string { - res := make([]string, 0, len(providers)) - for _, p := range providers { - res = append(res, p.GetName()) - } - return res -} - -func getModes(mode tpb.WorkloadMode) []tpb.WorkloadMode { - switch mode { - case tpb.WorkloadMode_CLIENT, tpb.WorkloadMode_SERVER: - return []tpb.WorkloadMode{mode} - default: - return []tpb.WorkloadMode{tpb.WorkloadMode_CLIENT, tpb.WorkloadMode_SERVER} - } -} - -func isAllMetrics(match *tpb.MetricSelector) bool { - switch m := match.GetMetricMatch().(type) { - case *tpb.MetricSelector_CustomMetric: - return false - case *tpb.MetricSelector_Metric: - return m.Metric == tpb.MetricSelector_ALL_METRICS - default: - return false - } -} - -func getMatches(match *tpb.MetricSelector) []string { - switch m := match.GetMetricMatch().(type) { - case *tpb.MetricSelector_CustomMetric: - return []string{m.CustomMetric} - case *tpb.MetricSelector_Metric: - if m.Metric == tpb.MetricSelector_ALL_METRICS { - return allMetrics - } - return []string{m.Metric.String()} - default: - return allMetrics - } -} - -func statsRootIDForClass(class networking.ListenerClass) string { - switch class { - case networking.ListenerClassSidecarInbound: - return "stats_inbound" - default: - return "stats_outbound" - } -} - -func buildHTTPTelemetryFilter(class networking.ListenerClass, metricsCfg []telemetryFilterConfig) []*hcm.HttpFilter { - res := make([]*hcm.HttpFilter, 0, len(metricsCfg)) - for _, cfg := range metricsCfg { - switch cfg.Provider.GetProvider().(type) { - case *meshconfig.MeshConfig_ExtensionProvider_Prometheus: - if !cfg.Metrics { - // No logging for prometheus - continue - } - statsCfg := generateStatsConfig(class, cfg) - vmConfig := ConstructVMConfig("/etc/istio/extensions/stats-filter.compiled.wasm", "envoy.wasm.stats") - root := statsRootIDForClass(class) - vmConfig.VmConfig.VmId = root - - wasmConfig := &httpwasm.Wasm{ - Config: &wasm.PluginConfig{ - RootId: root, - Vm: vmConfig, - Configuration: statsCfg, - }, - } - - f := &hcm.HttpFilter{ - Name: xds.StatsFilterName, - ConfigType: &hcm.HttpFilter_TypedConfig{TypedConfig: networking.MessageToAny(wasmConfig)}, - } - res = append(res, f) - case *meshconfig.MeshConfig_ExtensionProvider_Stackdriver: - sdCfg := generateSDConfig(class, cfg) - vmConfig := ConstructVMConfig("", "envoy.wasm.null.stackdriver") - vmConfig.VmConfig.VmId = stackdriverVMID(class) - - wasmConfig := &httpwasm.Wasm{ - Config: &wasm.PluginConfig{ - RootId: vmConfig.VmConfig.VmId, - Vm: vmConfig, - Configuration: sdCfg, - }, - } - - f := &hcm.HttpFilter{ - Name: xds.StackdriverFilterName, - ConfigType: &hcm.HttpFilter_TypedConfig{TypedConfig: networking.MessageToAny(wasmConfig)}, - } - res = append(res, f) - default: - // Only prometheus and SD supported currently - continue - } - } - return res -} - -func buildTCPTelemetryFilter(class networking.ListenerClass, telemetryConfigs []telemetryFilterConfig) []*listener.Filter { - res := []*listener.Filter{} - for _, telemetryCfg := range telemetryConfigs { - switch telemetryCfg.Provider.GetProvider().(type) { - case *meshconfig.MeshConfig_ExtensionProvider_Prometheus: - cfg := generateStatsConfig(class, telemetryCfg) - vmConfig := ConstructVMConfig("/etc/istio/extensions/stats-filter.compiled.wasm", "envoy.wasm.stats") - root := statsRootIDForClass(class) - vmConfig.VmConfig.VmId = "tcp_" + root - - wasmConfig := &wasmfilter.Wasm{ - Config: &wasm.PluginConfig{ - RootId: root, - Vm: vmConfig, - Configuration: cfg, - }, - } - - f := &listener.Filter{ - Name: xds.StatsFilterName, - ConfigType: &listener.Filter_TypedConfig{TypedConfig: networking.MessageToAny(wasmConfig)}, - } - res = append(res, f) - case *meshconfig.MeshConfig_ExtensionProvider_Stackdriver: - cfg := generateSDConfig(class, telemetryCfg) - vmConfig := ConstructVMConfig("", "envoy.wasm.null.stackdriver") - vmConfig.VmConfig.VmId = stackdriverVMID(class) - - wasmConfig := &wasmfilter.Wasm{ - Config: &wasm.PluginConfig{ - RootId: vmConfig.VmConfig.VmId, - Vm: vmConfig, - Configuration: cfg, - }, - } - - f := &listener.Filter{ - Name: xds.StackdriverFilterName, - ConfigType: &listener.Filter_TypedConfig{TypedConfig: networking.MessageToAny(wasmConfig)}, - } - res = append(res, f) - default: - // Only prometheus and SD supported currently - continue - } - } - return res -} - -func stackdriverVMID(class networking.ListenerClass) string { - switch class { - case networking.ListenerClassSidecarInbound: - return "stackdriver_inbound" - default: - return "stackdriver_outbound" - } -} - -var metricToSDServerMetrics = map[string]string{ - "REQUEST_COUNT": "server/request_count", - "REQUEST_DURATION": "server/response_latencies", - "REQUEST_SIZE": "server/request_bytes", - "RESPONSE_SIZE": "server/response_bytes", - "TCP_OPENED_CONNECTIONS": "server/connection_open_count", - "TCP_CLOSED_CONNECTIONS": "server/connection_close_count", - "TCP_SENT_BYTES": "server/sent_bytes_count", - "TCP_RECEIVED_BYTES": "server/received_bytes_count", - "GRPC_REQUEST_MESSAGES": "", - "GRPC_RESPONSE_MESSAGES": "", -} - -var metricToSDClientMetrics = map[string]string{ - "REQUEST_COUNT": "client/request_count", - "REQUEST_DURATION": "client/response_latencies", - "REQUEST_SIZE": "client/request_bytes", - "RESPONSE_SIZE": "client/response_bytes", - "TCP_OPENED_CONNECTIONS": "client/connection_open_count", - "TCP_CLOSED_CONNECTIONS": "client/connection_close_count", - "TCP_SENT_BYTES": "client/sent_bytes_count", - "TCP_RECEIVED_BYTES": "client/received_bytes_count", - "GRPC_REQUEST_MESSAGES": "", - "GRPC_RESPONSE_MESSAGES": "", -} - -// used for CEL expressions in stackdriver serialization -var jsonUnescaper = strings.NewReplacer(`\u003e`, `>`, `\u003c`, `<`, `\u0026`, `&`) - -func generateSDConfig(class networking.ListenerClass, telemetryConfig telemetryFilterConfig) *anypb.Any { - cfg := sd.PluginConfig{ - DisableHostHeaderFallback: disableHostHeaderFallback(class), - } - metricNameMap := metricToSDClientMetrics - if class == networking.ListenerClassSidecarInbound { - metricNameMap = metricToSDServerMetrics - } - for _, override := range telemetryConfig.MetricsForClass(class) { - metricName, f := metricNameMap[override.Name] - if !f { - // Not a predefined metric, must be a custom one - metricName = override.Name - } - if metricName == "" { - continue - } - if cfg.MetricsOverrides == nil { - cfg.MetricsOverrides = map[string]*sd.MetricsOverride{} - } - if _, f := cfg.MetricsOverrides[metricName]; !f { - cfg.MetricsOverrides[metricName] = &sd.MetricsOverride{} - } - cfg.MetricsOverrides[metricName].Drop = override.Disabled - for _, t := range override.Tags { - if t.Remove { - // Remove is not supported by SD - continue - } - if cfg.MetricsOverrides[metricName].TagOverrides == nil { - cfg.MetricsOverrides[metricName].TagOverrides = map[string]string{} - } - cfg.MetricsOverrides[metricName].TagOverrides[t.Name] = t.Value - } - } - - if telemetryConfig.AccessLogging { - if telemetryConfig.LogsFilter != nil { - cfg.AccessLoggingFilterExpression = telemetryConfig.LogsFilter.Expression - } else { - if class == networking.ListenerClassSidecarInbound { - cfg.AccessLogging = sd.PluginConfig_FULL - } else { - // this can be achieved via CEL: `response.code >= 400 || response.code == 0` - cfg.AccessLogging = sd.PluginConfig_ERRORS_ONLY - } - } - } else { - // The field is deprecated, but until it is removed we need to set it. - cfg.DisableServerAccessLogging = true // nolint: staticcheck - } - - cfg.MetricExpiryDuration = durationpb.New(1 * time.Hour) - // In WASM we are not actually processing protobuf at all, so we need to encode this to JSON - cfgJSON, _ := protomarshal.MarshalProtoNames(&cfg) - - // MarshalProtoNames() forces HTML-escaped JSON encoding. - // this can be problematic for CEL expressions, particularly those using - // '>', '<', and '&'s. It is easier to use replaceAll operations than it is - // to mimic MarshalProtoNames() with configured JSON Encoder. - pb := &wrappers.StringValue{Value: jsonUnescaper.Replace(string(cfgJSON))} - - return networking.MessageToAny(pb) -} - -var metricToPrometheusMetric = map[string]string{ - "REQUEST_COUNT": "requests_total", - "REQUEST_DURATION": "request_duration_milliseconds", - "REQUEST_SIZE": "request_bytes", - "RESPONSE_SIZE": "response_bytes", - "TCP_OPENED_CONNECTIONS": "tcp_connections_opened_total", - "TCP_CLOSED_CONNECTIONS": "tcp_connections_closed_total", - "TCP_SENT_BYTES": "tcp_sent_bytes_total", - "TCP_RECEIVED_BYTES": "tcp_received_bytes_total", - "GRPC_REQUEST_MESSAGES": "request_messages_total", - "GRPC_RESPONSE_MESSAGES": "response_messages_total", -} - -func generateStatsConfig(class networking.ListenerClass, metricsCfg telemetryFilterConfig) *anypb.Any { - cfg := stats.PluginConfig{ - DisableHostHeaderFallback: disableHostHeaderFallback(class), - } - for _, override := range metricsCfg.MetricsForClass(class) { - metricName, f := metricToPrometheusMetric[override.Name] - if !f { - // Not a predefined metric, must be a custom one - metricName = override.Name - } - mc := &stats.MetricConfig{ - Dimensions: map[string]string{}, - Name: metricName, - Drop: override.Disabled, - } - for _, t := range override.Tags { - if t.Remove { - mc.TagsToRemove = append(mc.TagsToRemove, t.Name) - } else { - mc.Dimensions[t.Name] = t.Value - } - } - cfg.Metrics = append(cfg.Metrics, mc) - } - // In WASM we are not actually processing protobuf at all, so we need to encode this to JSON - cfgJSON, _ := protomarshal.MarshalProtoNames(&cfg) - return networking.MessageToAny(&wrappers.StringValue{Value: string(cfgJSON)}) -} - -func disableHostHeaderFallback(class networking.ListenerClass) bool { - return class == networking.ListenerClassSidecarInbound || class == networking.ListenerClassGateway -} diff --git a/pilot/pkg/model/telemetry_test.go b/pilot/pkg/model/telemetry_test.go deleted file mode 100644 index be089d3de..000000000 --- a/pilot/pkg/model/telemetry_test.go +++ /dev/null @@ -1,1246 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "reflect" - "testing" -) - -import ( - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - httpwasm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/wasm/v3" - httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - wasmfilter "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/wasm/v3" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/types/known/structpb" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - meshconfig "istio.io/api/mesh/v1alpha1" - tpb "istio.io/api/telemetry/v1alpha1" - "istio.io/api/type/v1beta1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -func createTestTelemetries(configs []config.Config, t *testing.T) *Telemetries { - t.Helper() - - store := &telemetryStore{} - for _, cfg := range configs { - store.add(cfg) - } - m := mesh.DefaultMeshConfig() - jsonTextProvider := &meshconfig.MeshConfig_ExtensionProvider{ - Name: "envoy-json", - Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLog{ - EnvoyFileAccessLog: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider{ - Path: "/dev/null", - LogFormat: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider_LogFormat{ - LogFormat: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider_LogFormat_Labels{ - Labels: &structpb.Struct{}, - }, - }, - }, - }, - } - m.ExtensionProviders = append(m.ExtensionProviders, jsonTextProvider) - - environment := &Environment{ - ConfigStore: MakeIstioStore(store), - Watcher: mesh.NewFixedWatcher(m), - } - telemetries, err := getTelemetries(environment) - if err != nil { - t.Fatalf("getTelemetries failed: %v", err) - } - return telemetries -} - -func newTelemetry(ns string, spec config.Spec) config.Config { - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioTelemetryV1Alpha1Telemetries.Resource().GroupVersionKind(), - Name: "default", - Namespace: ns, - }, - Spec: spec, - } -} - -type telemetryStore struct { - ConfigStore - - data []struct { - typ config.GroupVersionKind - ns string - cfg config.Config - } -} - -func (ts *telemetryStore) add(cfg config.Config) { - ts.data = append(ts.data, struct { - typ config.GroupVersionKind - ns string - cfg config.Config - }{ - typ: cfg.GroupVersionKind, - ns: cfg.Namespace, - cfg: cfg, - }) -} - -func (ts *telemetryStore) Schemas() collection.Schemas { - return collection.SchemasFor() -} - -func (ts *telemetryStore) Get(_ config.GroupVersionKind, _, _ string) *config.Config { - return nil -} - -func (ts *telemetryStore) List(typ config.GroupVersionKind, namespace string) ([]config.Config, error) { - var configs []config.Config - for _, data := range ts.data { - if data.typ == typ { - if namespace != "" && data.ns == namespace { - continue - } - configs = append(configs, data.cfg) - } - } - return configs, nil -} - -func TestAccessLogging(t *testing.T) { - labels := map[string]string{"app": "test"} - sidecar := &Proxy{ConfigNamespace: "default", Metadata: &NodeMetadata{Labels: labels}} - envoy := &tpb.Telemetry{ - AccessLogging: []*tpb.AccessLogging{ - { - Providers: []*tpb.ProviderRef{ - { - Name: "envoy", - }, - }, - }, - }, - } - - client := &tpb.Telemetry{ - AccessLogging: []*tpb.AccessLogging{ - { - Match: &tpb.AccessLogging_LogSelector{ - Mode: tpb.WorkloadMode_CLIENT, - }, - Providers: []*tpb.ProviderRef{ - { - Name: "envoy", - }, - }, - }, - }, - } - clientDisabled := &tpb.Telemetry{ - AccessLogging: []*tpb.AccessLogging{ - { - Match: &tpb.AccessLogging_LogSelector{ - Mode: tpb.WorkloadMode_CLIENT, - }, - Providers: []*tpb.ProviderRef{ - { - Name: "envoy", - }, - }, - Disabled: &wrappers.BoolValue{ - Value: true, - }, - }, - }, - } - sidecarClient := &tpb.Telemetry{ - Selector: &v1beta1.WorkloadSelector{ - MatchLabels: labels, - }, - AccessLogging: []*tpb.AccessLogging{ - { - Match: &tpb.AccessLogging_LogSelector{ - Mode: tpb.WorkloadMode_CLIENT, - }, - Providers: []*tpb.ProviderRef{ - { - Name: "envoy", - }, - }, - }, - }, - } - server := &tpb.Telemetry{ - AccessLogging: []*tpb.AccessLogging{ - { - Match: &tpb.AccessLogging_LogSelector{ - Mode: tpb.WorkloadMode_SERVER, - }, - Providers: []*tpb.ProviderRef{ - { - Name: "envoy", - }, - }, - }, - }, - } - serverDisabled := &tpb.Telemetry{ - AccessLogging: []*tpb.AccessLogging{ - { - Match: &tpb.AccessLogging_LogSelector{ - Mode: tpb.WorkloadMode_SERVER, - }, - Providers: []*tpb.ProviderRef{ - { - Name: "envoy", - }, - }, - Disabled: &wrappers.BoolValue{ - Value: true, - }, - }, - }, - } - serverAndClient := &tpb.Telemetry{ - AccessLogging: []*tpb.AccessLogging{ - { - Match: &tpb.AccessLogging_LogSelector{ - Mode: tpb.WorkloadMode_CLIENT_AND_SERVER, - }, - Providers: []*tpb.ProviderRef{ - { - Name: "envoy", - }, - }, - }, - }, - } - stackdriver := &tpb.Telemetry{ - AccessLogging: []*tpb.AccessLogging{ - { - Providers: []*tpb.ProviderRef{ - { - Name: "stackdriver", - }, - }, - }, - }, - } - empty := &tpb.Telemetry{ - AccessLogging: []*tpb.AccessLogging{{}}, - } - defaultJSON := &tpb.Telemetry{ - AccessLogging: []*tpb.AccessLogging{ - { - Providers: []*tpb.ProviderRef{ - { - Name: "envoy-json", - }, - }, - }, - }, - } - disabled := &tpb.Telemetry{ - AccessLogging: []*tpb.AccessLogging{ - { - Disabled: &wrappers.BoolValue{Value: true}, - }, - }, - } - nonExistant := &tpb.Telemetry{ - AccessLogging: []*tpb.AccessLogging{ - { - Providers: []*tpb.ProviderRef{ - { - Name: "custom-provider", - }, - }, - }, - }, - } - tests := []struct { - name string - cfgs []config.Config - class networking.ListenerClass - proxy *Proxy - defaultProviders []string - want []string - }{ - { - "empty", - nil, - networking.ListenerClassSidecarOutbound, - sidecar, - nil, - nil, - }, - { - "default provider only", - nil, - networking.ListenerClassSidecarOutbound, - sidecar, - []string{"envoy"}, - []string{"envoy"}, - }, - { - "provider only", - []config.Config{newTelemetry("dubbo-system", envoy)}, - networking.ListenerClassSidecarOutbound, - sidecar, - nil, - []string{"envoy"}, - }, - { - "client - gateway", - []config.Config{newTelemetry("dubbo-system", client)}, - networking.ListenerClassGateway, - sidecar, - nil, - []string{"envoy"}, - }, - { - "client - outbound", - []config.Config{newTelemetry("dubbo-system", client)}, - networking.ListenerClassSidecarOutbound, - sidecar, - nil, - []string{"envoy"}, - }, - { - "client - inbound", - []config.Config{newTelemetry("dubbo-system", client)}, - networking.ListenerClassSidecarInbound, - sidecar, - nil, - []string{}, - }, - { - "client - disabled server", - []config.Config{newTelemetry("dubbo-system", client), newTelemetry("default", serverDisabled)}, - networking.ListenerClassSidecarOutbound, - sidecar, - nil, - []string{"envoy"}, - }, - { - "client - disabled client", - []config.Config{newTelemetry("dubbo-system", client), newTelemetry("default", clientDisabled)}, - networking.ListenerClassSidecarOutbound, - sidecar, - nil, - []string{}, - }, - { - "client - disabled - enabled", - []config.Config{newTelemetry("dubbo-system", client), newTelemetry("default", clientDisabled), newTelemetry("default", sidecarClient)}, - networking.ListenerClassSidecarOutbound, - sidecar, - nil, - []string{"envoy"}, - }, - { - "server - gateway", - []config.Config{newTelemetry("dubbo-system", server)}, - networking.ListenerClassGateway, - sidecar, - nil, - []string{}, - }, - { - "server - inbound", - []config.Config{newTelemetry("dubbo-system", server)}, - networking.ListenerClassSidecarInbound, - sidecar, - nil, - []string{"envoy"}, - }, - { - "server - outbound", - []config.Config{newTelemetry("dubbo-system", server)}, - networking.ListenerClassSidecarOutbound, - sidecar, - nil, - []string{}, - }, - { - "server and client - gateway", - []config.Config{newTelemetry("dubbo-system", serverAndClient)}, - networking.ListenerClassGateway, - sidecar, - nil, - []string{"envoy"}, - }, - { - "server and client - inbound", - []config.Config{newTelemetry("dubbo-system", serverAndClient)}, - networking.ListenerClassSidecarInbound, - sidecar, - nil, - []string{"envoy"}, - }, - { - "server and client - outbound", - []config.Config{newTelemetry("dubbo-system", serverAndClient)}, - networking.ListenerClassSidecarOutbound, - sidecar, - nil, - []string{"envoy"}, - }, - { - "override default", - []config.Config{newTelemetry("dubbo-system", envoy)}, - networking.ListenerClassSidecarOutbound, - sidecar, - []string{"stackdriver"}, - []string{"envoy"}, - }, - { - "override namespace", - []config.Config{newTelemetry("dubbo-system", envoy), newTelemetry("default", stackdriver)}, - networking.ListenerClassSidecarOutbound, - sidecar, - nil, - []string{"stackdriver"}, - }, - { - "empty config inherits", - []config.Config{newTelemetry("dubbo-system", envoy), newTelemetry("default", empty)}, - networking.ListenerClassSidecarOutbound, - sidecar, - nil, - []string{"envoy"}, - }, - { - "default envoy JSON", - []config.Config{newTelemetry("dubbo-system", defaultJSON)}, - networking.ListenerClassSidecarOutbound, - sidecar, - nil, - []string{"envoy-json"}, - }, - { - "disable config", - []config.Config{newTelemetry("dubbo-system", envoy), newTelemetry("default", disabled)}, - networking.ListenerClassSidecarOutbound, - sidecar, - nil, - []string{}, - }, - { - "disable default", - []config.Config{newTelemetry("default", disabled)}, - networking.ListenerClassSidecarOutbound, - sidecar, - []string{"envoy"}, - []string{}, - }, - { - "non existing", - []config.Config{newTelemetry("default", nonExistant)}, - networking.ListenerClassSidecarOutbound, - sidecar, - []string{"envoy"}, - []string{}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - telemetry := createTestTelemetries(tt.cfgs, t) - telemetry.meshConfig.DefaultProviders.AccessLogging = tt.defaultProviders - al := telemetry.AccessLogging(tt.proxy, tt.class) - var got []string - if al != nil { - got = []string{} // We distinguish between nil vs empty in the test - for _, p := range al.Providers { - got = append(got, p.Name) - } - } - if !reflect.DeepEqual(got, tt.want) { - t.Fatalf("got %v want %v", got, tt.want) - } - }) - } -} - -func TestAccessLoggingWithFilter(t *testing.T) { - sidecar := &Proxy{ConfigNamespace: "default", Metadata: &NodeMetadata{Labels: map[string]string{"app": "test"}}} - filter1 := &tpb.Telemetry{ - AccessLogging: []*tpb.AccessLogging{ - { - Providers: []*tpb.ProviderRef{ - { - Name: "custom-provider", - }, - }, - Filter: &tpb.AccessLogging_Filter{ - Expression: "response.code >= 400", - }, - }, - }, - } - filter2 := &tpb.Telemetry{ - AccessLogging: []*tpb.AccessLogging{ - { - Providers: []*tpb.ProviderRef{ - { - Name: "custom-provider", - }, - }, - Filter: &tpb.AccessLogging_Filter{ - Expression: "response.code >= 500", - }, - }, - }, - } - tests := []struct { - name string - cfgs []config.Config - proxy *Proxy - defaultProviders []string - want *LoggingConfig - }{ - { - "filter", - []config.Config{newTelemetry("default", filter1)}, - sidecar, - []string{"custom-provider"}, - &LoggingConfig{ - Filter: &tpb.AccessLogging_Filter{ - Expression: "response.code >= 400", - }, - }, - }, - { - "multi-filter", - []config.Config{newTelemetry("default", filter2), newTelemetry("default", filter1)}, - sidecar, - []string{"custom-provider"}, - &LoggingConfig{ - Filter: &tpb.AccessLogging_Filter{ - Expression: "response.code >= 500", - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - telemetry := createTestTelemetries(tt.cfgs, t) - telemetry.meshConfig.DefaultProviders.AccessLogging = tt.defaultProviders - got := telemetry.AccessLogging(tt.proxy, networking.ListenerClassSidecarOutbound) - if !reflect.DeepEqual(got, tt.want) { - t.Fatalf("got %v want %v", got, tt.want) - } - }) - } -} - -func newTracingConfig(providerName string, disabled bool) *TracingConfig { - return &TracingConfig{ - ClientSpec: TracingSpec{ - Provider: &meshconfig.MeshConfig_ExtensionProvider{Name: providerName}, - Disabled: disabled, - UseRequestIDForTraceSampling: true, - }, - ServerSpec: TracingSpec{ - Provider: &meshconfig.MeshConfig_ExtensionProvider{Name: providerName}, - Disabled: disabled, - UseRequestIDForTraceSampling: true, - }, - } -} - -const ( - reportingEnabled = false - reportingDisabled = !reportingEnabled -) - -func TestTracing(t *testing.T) { - sidecar := &Proxy{ConfigNamespace: "default", Metadata: &NodeMetadata{Labels: map[string]string{"app": "test"}}} - envoy := &tpb.Telemetry{ - Tracing: []*tpb.Tracing{ - { - Providers: []*tpb.ProviderRef{ - { - Name: "envoy", - }, - }, - }, - }, - } - stackdriver := &tpb.Telemetry{ - Tracing: []*tpb.Tracing{ - { - Providers: []*tpb.ProviderRef{ - { - Name: "stackdriver", - }, - }, - }, - }, - } - empty := &tpb.Telemetry{ - Tracing: []*tpb.Tracing{{}}, - } - disabled := &tpb.Telemetry{ - Tracing: []*tpb.Tracing{ - { - DisableSpanReporting: &wrappers.BoolValue{Value: true}, - }, - }, - } - overidesA := &tpb.Telemetry{ - Tracing: []*tpb.Tracing{ - { - RandomSamplingPercentage: &wrappers.DoubleValue{Value: 50.0}, - CustomTags: map[string]*tpb.Tracing_CustomTag{ - "foo": {}, - "bar": {}, - }, - UseRequestIdForTraceSampling: &wrappers.BoolValue{Value: false}, - }, - }, - } - overidesB := &tpb.Telemetry{ - Tracing: []*tpb.Tracing{ - { - RandomSamplingPercentage: &wrappers.DoubleValue{Value: 80.0}, - CustomTags: map[string]*tpb.Tracing_CustomTag{ - "foo": {}, - "baz": {}, - }, - UseRequestIdForTraceSampling: &wrappers.BoolValue{Value: true}, - }, - }, - } - overridesWithDefaultSampling := &tpb.Telemetry{ - Tracing: []*tpb.Tracing{ - { - CustomTags: map[string]*tpb.Tracing_CustomTag{ - "foo": {}, - "baz": {}, - }, - }, - }, - } - nonExistant := &tpb.Telemetry{ - Tracing: []*tpb.Tracing{ - { - Providers: []*tpb.ProviderRef{ - { - Name: "custom-provider", - }, - }, - }, - }, - } - clientSideSampling := &tpb.Telemetry{ - Tracing: []*tpb.Tracing{ - { - Match: &tpb.Tracing_TracingSelector{ - Mode: tpb.WorkloadMode_CLIENT, - }, - Providers: []*tpb.ProviderRef{ - { - Name: "stackdriver", - }, - }, - RandomSamplingPercentage: &wrappers.DoubleValue{Value: 99.9}, - }, - }, - } - serverSideDisabled := &tpb.Telemetry{ - Tracing: []*tpb.Tracing{ - { - Match: &tpb.Tracing_TracingSelector{ - Mode: tpb.WorkloadMode_SERVER, - }, - DisableSpanReporting: &wrappers.BoolValue{Value: true}, - }, - }, - } - - tests := []struct { - name string - cfgs []config.Config - proxy *Proxy - defaultProviders []string - want *TracingConfig - }{ - { - "empty", - nil, - sidecar, - nil, - nil, - }, - { - "default provider only", - nil, - sidecar, - []string{"envoy"}, - newTracingConfig("envoy", reportingEnabled), - }, - { - "provider only", - []config.Config{newTelemetry("dubbo-system", envoy)}, - sidecar, - nil, - newTracingConfig("envoy", reportingEnabled), - }, - { - "override default", - []config.Config{newTelemetry("dubbo-system", envoy)}, - sidecar, - []string{"stackdriver"}, - newTracingConfig("envoy", reportingEnabled), - }, - { - "override namespace", - []config.Config{newTelemetry("dubbo-system", envoy), newTelemetry("default", stackdriver)}, - sidecar, - nil, - newTracingConfig("stackdriver", reportingEnabled), - }, - { - "empty config inherits", - []config.Config{newTelemetry("dubbo-system", envoy), newTelemetry("default", empty)}, - sidecar, - nil, - newTracingConfig("envoy", reportingEnabled), - }, - { - "disable config", - []config.Config{newTelemetry("dubbo-system", envoy), newTelemetry("default", disabled)}, - sidecar, - nil, - newTracingConfig("envoy", reportingDisabled), - }, - { - "disable default", - []config.Config{newTelemetry("default", disabled)}, - sidecar, - []string{"envoy"}, - newTracingConfig("envoy", reportingDisabled), - }, - { - "non existing", - []config.Config{newTelemetry("default", nonExistant)}, - sidecar, - []string{"envoy"}, - &TracingConfig{ - ClientSpec: TracingSpec{Disabled: true, UseRequestIDForTraceSampling: true}, - ServerSpec: TracingSpec{Disabled: true, UseRequestIDForTraceSampling: true}, - }, - }, - { - "overrides", - []config.Config{newTelemetry("dubbo-system", overidesA)}, - sidecar, - []string{"envoy"}, - &TracingConfig{ - ClientSpec: TracingSpec{ - Provider: &meshconfig.MeshConfig_ExtensionProvider{Name: "envoy"}, - RandomSamplingPercentage: 50.0, - CustomTags: map[string]*tpb.Tracing_CustomTag{ - "foo": {}, - "bar": {}, - }, - UseRequestIDForTraceSampling: false, - }, ServerSpec: TracingSpec{ - Provider: &meshconfig.MeshConfig_ExtensionProvider{Name: "envoy"}, - RandomSamplingPercentage: 50.0, - CustomTags: map[string]*tpb.Tracing_CustomTag{ - "foo": {}, - "bar": {}, - }, - UseRequestIDForTraceSampling: false, - }, - }, - }, - { - "overrides with default sampling", - []config.Config{newTelemetry("dubbo-system", overridesWithDefaultSampling)}, - sidecar, - []string{"envoy"}, - &TracingConfig{ - ClientSpec: TracingSpec{ - Provider: &meshconfig.MeshConfig_ExtensionProvider{Name: "envoy"}, - RandomSamplingPercentage: 0.0, - CustomTags: map[string]*tpb.Tracing_CustomTag{ - "foo": {}, - "baz": {}, - }, - UseRequestIDForTraceSampling: true, - }, ServerSpec: TracingSpec{ - Provider: &meshconfig.MeshConfig_ExtensionProvider{Name: "envoy"}, - RandomSamplingPercentage: 0.0, - CustomTags: map[string]*tpb.Tracing_CustomTag{ - "foo": {}, - "baz": {}, - }, - UseRequestIDForTraceSampling: true, - }, - }, - }, - { - "multi overrides", - []config.Config{ - newTelemetry("dubbo-system", overidesA), - newTelemetry("default", overidesB), - }, - sidecar, - []string{"envoy"}, - &TracingConfig{ - ClientSpec: TracingSpec{ - Provider: &meshconfig.MeshConfig_ExtensionProvider{Name: "envoy"}, - RandomSamplingPercentage: 80, - CustomTags: map[string]*tpb.Tracing_CustomTag{ - "foo": {}, - "baz": {}, - }, - UseRequestIDForTraceSampling: true, - }, - ServerSpec: TracingSpec{ - Provider: &meshconfig.MeshConfig_ExtensionProvider{Name: "envoy"}, - RandomSamplingPercentage: 80, - CustomTags: map[string]*tpb.Tracing_CustomTag{ - "foo": {}, - "baz": {}, - }, - UseRequestIDForTraceSampling: true, - }, - }, - }, - { - "client-only override", - []config.Config{newTelemetry("dubbo-system", envoy), newTelemetry("default", clientSideSampling)}, - sidecar, - []string{"envoy"}, - &TracingConfig{ - ClientSpec: TracingSpec{ - Provider: &meshconfig.MeshConfig_ExtensionProvider{ - Name: "stackdriver", - Provider: &meshconfig.MeshConfig_ExtensionProvider_Stackdriver{ - Stackdriver: &meshconfig.MeshConfig_ExtensionProvider_StackdriverProvider{}, - }, - }, - RandomSamplingPercentage: 99.9, - UseRequestIDForTraceSampling: true, - }, - ServerSpec: TracingSpec{ - Provider: &meshconfig.MeshConfig_ExtensionProvider{Name: "envoy"}, - UseRequestIDForTraceSampling: true, - }, - }, - }, - { - "server-only override", - []config.Config{newTelemetry("dubbo-system", envoy), newTelemetry("default", serverSideDisabled)}, - sidecar, - []string{"envoy"}, - &TracingConfig{ - ClientSpec: TracingSpec{ - Provider: &meshconfig.MeshConfig_ExtensionProvider{Name: "envoy"}, - UseRequestIDForTraceSampling: true, - }, - ServerSpec: TracingSpec{ - Provider: &meshconfig.MeshConfig_ExtensionProvider{Name: "envoy"}, - Disabled: true, - UseRequestIDForTraceSampling: true, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - telemetry := createTestTelemetries(tt.cfgs, t) - telemetry.meshConfig.DefaultProviders.Tracing = tt.defaultProviders - got := telemetry.Tracing(tt.proxy) - if got != nil && got.ServerSpec.Provider != nil { - // We don't match on this, just the name for test simplicity - got.ServerSpec.Provider.Provider = nil - } - assert.Equal(t, got, tt.want) - }) - } -} - -func TestTelemetryFilters(t *testing.T) { - overrides := []*tpb.MetricsOverrides{{ - Match: &tpb.MetricSelector{ - MetricMatch: &tpb.MetricSelector_Metric{ - Metric: tpb.MetricSelector_REQUEST_COUNT, - }, - }, - TagOverrides: map[string]*tpb.MetricsOverrides_TagOverride{ - "remove": { - Operation: tpb.MetricsOverrides_TagOverride_REMOVE, - }, - "add": { - Operation: tpb.MetricsOverrides_TagOverride_UPSERT, - Value: "bar", - }, - }, - }} - sidecar := &Proxy{ConfigNamespace: "default", Metadata: &NodeMetadata{Labels: map[string]string{"app": "test"}}} - emptyPrometheus := &tpb.Telemetry{ - Metrics: []*tpb.Metrics{ - { - Providers: []*tpb.ProviderRef{{Name: "prometheus"}}, - }, - }, - } - overridesPrometheus := &tpb.Telemetry{ - Metrics: []*tpb.Metrics{ - { - Providers: []*tpb.ProviderRef{{Name: "prometheus"}}, - Overrides: overrides, - }, - }, - } - emptyStackdriver := &tpb.Telemetry{ - Metrics: []*tpb.Metrics{ - { - Providers: []*tpb.ProviderRef{{Name: "stackdriver"}}, - }, - }, - } - overridesStackdriver := &tpb.Telemetry{ - Metrics: []*tpb.Metrics{ - { - Providers: []*tpb.ProviderRef{{Name: "stackdriver"}}, - Overrides: overrides, - }, - }, - AccessLogging: []*tpb.AccessLogging{ - { - Providers: []*tpb.ProviderRef{{Name: "stackdriver"}}, - Filter: &tpb.AccessLogging_Filter{ - Expression: `response.code >= 500 && response.code <= 800`, - }, - }, - }, - } - overridesEmptyProvider := &tpb.Telemetry{ - Metrics: []*tpb.Metrics{ - { - Overrides: overrides, - }, - }, - } - sdLogging := &tpb.Telemetry{ - AccessLogging: []*tpb.AccessLogging{ - { - Providers: []*tpb.ProviderRef{{Name: "stackdriver"}}, - }, - }, - } - emptyLogging := &tpb.Telemetry{ - AccessLogging: []*tpb.AccessLogging{ - {}, - }, - } - disbaledAllMetrics := &tpb.Telemetry{ - Metrics: []*tpb.Metrics{ - { - Overrides: []*tpb.MetricsOverrides{{ - Match: &tpb.MetricSelector{ - MetricMatch: &tpb.MetricSelector_Metric{ - Metric: tpb.MetricSelector_ALL_METRICS, - }, - }, - Disabled: &wrappers.BoolValue{ - Value: true, - }, - }}, - - Providers: []*tpb.ProviderRef{{Name: "prometheus"}}, - }, - }, - } - - tests := []struct { - name string - cfgs []config.Config - proxy *Proxy - class networking.ListenerClass - protocol networking.ListenerProtocol - defaultProviders *meshconfig.MeshConfig_DefaultProviders - want map[string]string - }{ - { - "empty", - nil, - sidecar, - networking.ListenerClassSidecarOutbound, - networking.ListenerProtocolHTTP, - nil, - map[string]string{}, - }, - { - "disabled-prometheus", - []config.Config{newTelemetry("dubbo-system", disbaledAllMetrics)}, - sidecar, - networking.ListenerClassSidecarOutbound, - networking.ListenerProtocolHTTP, - nil, - map[string]string{}, - }, - { - "disabled-then-empty", - []config.Config{ - newTelemetry("dubbo-system", disbaledAllMetrics), - newTelemetry("default", emptyPrometheus), - }, - sidecar, - networking.ListenerClassSidecarOutbound, - networking.ListenerProtocolHTTP, - nil, - map[string]string{}, - }, - { - "disabled-then-overrides", - []config.Config{ - newTelemetry("dubbo-system", disbaledAllMetrics), - newTelemetry("default", overridesPrometheus), - }, - sidecar, - networking.ListenerClassSidecarOutbound, - networking.ListenerProtocolHTTP, - nil, - map[string]string{ - "istio.stats": `{"metrics":[{"dimensions":{"add":"bar"},"name":"requests_total","tags_to_remove":["remove"]}]}`, - }, - }, - { - "default prometheus", - []config.Config{newTelemetry("dubbo-system", emptyPrometheus)}, - sidecar, - networking.ListenerClassSidecarOutbound, - networking.ListenerProtocolHTTP, - nil, - map[string]string{ - "istio.stats": "{}", - }, - }, - { - "default provider prometheus", - []config.Config{}, - sidecar, - networking.ListenerClassSidecarOutbound, - networking.ListenerProtocolHTTP, - &meshconfig.MeshConfig_DefaultProviders{Metrics: []string{"prometheus"}}, - map[string]string{ - "istio.stats": "{}", - }, - }, - { - "prometheus overrides", - []config.Config{newTelemetry("dubbo-system", overridesPrometheus)}, - sidecar, - networking.ListenerClassSidecarOutbound, - networking.ListenerProtocolHTTP, - nil, - map[string]string{ - "istio.stats": `{"metrics":[{"dimensions":{"add":"bar"},"name":"requests_total","tags_to_remove":["remove"]}]}`, - }, - }, - { - "prometheus overrides TCP", - []config.Config{newTelemetry("dubbo-system", overridesPrometheus)}, - sidecar, - networking.ListenerClassSidecarOutbound, - networking.ListenerProtocolTCP, - nil, - map[string]string{ - "istio.stats": `{"metrics":[{"dimensions":{"add":"bar"},"name":"requests_total","tags_to_remove":["remove"]}]}`, - }, - }, - { - "empty stackdriver", - []config.Config{newTelemetry("dubbo-system", emptyStackdriver)}, - sidecar, - networking.ListenerClassSidecarOutbound, - networking.ListenerProtocolHTTP, - nil, - map[string]string{ - "istio.stackdriver": `{"disable_server_access_logging":true,"metric_expiry_duration":"3600s"}`, - }, - }, - { - "overrides stackdriver", - []config.Config{newTelemetry("dubbo-system", overridesStackdriver)}, - sidecar, - networking.ListenerClassSidecarOutbound, - networking.ListenerProtocolHTTP, - nil, - map[string]string{ - "istio.stackdriver": `{"access_logging_filter_expression":"response.code >= 500 && response.code <= 800",` + - `"metric_expiry_duration":"3600s","metrics_overrides":{"client/request_count":{"tag_overrides":{"add":"bar"}}}}`, - }, - }, - { - "namespace empty merge", - []config.Config{ - newTelemetry("dubbo-system", emptyPrometheus), - newTelemetry("default", emptyStackdriver), - }, - sidecar, - networking.ListenerClassSidecarOutbound, - networking.ListenerProtocolHTTP, - nil, - map[string]string{ - "istio.stackdriver": `{"disable_server_access_logging":true,"metric_expiry_duration":"3600s"}`, - }, - }, - { - "namespace overrides merge without provider", - []config.Config{ - newTelemetry("dubbo-system", emptyPrometheus), - newTelemetry("default", overridesEmptyProvider), - }, - sidecar, - networking.ListenerClassSidecarOutbound, - networking.ListenerProtocolHTTP, - nil, - map[string]string{ - "istio.stats": `{"metrics":[{"dimensions":{"add":"bar"},"name":"requests_total","tags_to_remove":["remove"]}]}`, - }, - }, - { - "namespace overrides merge with default provider", - []config.Config{ - newTelemetry("default", overridesEmptyProvider), - }, - sidecar, - networking.ListenerClassSidecarOutbound, - networking.ListenerProtocolHTTP, - &meshconfig.MeshConfig_DefaultProviders{Metrics: []string{"prometheus"}}, - map[string]string{ - "istio.stats": `{"metrics":[{"dimensions":{"add":"bar"},"name":"requests_total","tags_to_remove":["remove"]}]}`, - }, - }, - { - "namespace overrides default provider", - []config.Config{ - newTelemetry("default", emptyStackdriver), - }, - sidecar, - networking.ListenerClassSidecarOutbound, - networking.ListenerProtocolHTTP, - &meshconfig.MeshConfig_DefaultProviders{Metrics: []string{"prometheus"}}, - map[string]string{ - "istio.stackdriver": `{"disable_server_access_logging":true,"metric_expiry_duration":"3600s"}`, - }, - }, - { - "stackdriver logging", - []config.Config{ - newTelemetry("default", sdLogging), - }, - sidecar, - networking.ListenerClassSidecarOutbound, - networking.ListenerProtocolHTTP, - nil, - map[string]string{ - "istio.stackdriver": `{"access_logging":"ERRORS_ONLY","metric_expiry_duration":"3600s"}`, - }, - }, - { - "stackdriver logging default provider", - []config.Config{ - newTelemetry("default", emptyLogging), - }, - sidecar, - networking.ListenerClassSidecarInbound, - networking.ListenerProtocolHTTP, - &meshconfig.MeshConfig_DefaultProviders{AccessLogging: []string{"stackdriver"}}, - map[string]string{ - "istio.stackdriver": `{"disable_host_header_fallback":true,"access_logging":"FULL","metric_expiry_duration":"3600s"}`, - }, - }, - { - "stackdriver default for all", - []config.Config{}, - sidecar, - networking.ListenerClassSidecarInbound, - networking.ListenerProtocolHTTP, - &meshconfig.MeshConfig_DefaultProviders{ - Metrics: []string{"stackdriver"}, - AccessLogging: []string{"stackdriver"}, - }, - map[string]string{ - "istio.stackdriver": `{"disable_host_header_fallback":true,"access_logging":"FULL","metric_expiry_duration":"3600s"}`, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - telemetry := createTestTelemetries(tt.cfgs, t) - telemetry.meshConfig.DefaultProviders = tt.defaultProviders - got := telemetry.telemetryFilters(tt.proxy, tt.class, tt.protocol) - res := map[string]string{} - http, ok := got.([]*httppb.HttpFilter) - if ok { - for _, f := range http { - w := &httpwasm.Wasm{} - - if err := f.GetTypedConfig().UnmarshalTo(w); err != nil { - t.Fatal(err) - } - cfg := &wrappers.StringValue{} - if err := w.GetConfig().GetConfiguration().UnmarshalTo(cfg); err != nil { - t.Fatal(err) - } - if _, dupe := res[f.GetName()]; dupe { - t.Fatalf("duplicate filter found: %v", f.GetName()) - } - res[f.GetName()] = cfg.GetValue() - } - } - tcp, ok := got.([]*listener.Filter) - if ok { - for _, f := range tcp { - w := &wasmfilter.Wasm{} - - if err := f.GetTypedConfig().UnmarshalTo(w); err != nil { - t.Fatal(err) - } - cfg := &wrappers.StringValue{} - if err := w.GetConfig().GetConfiguration().UnmarshalTo(cfg); err != nil { - t.Fatal(err) - } - if _, dupe := res[f.GetName()]; dupe { - t.Fatalf("duplicate filter found: %v", f.GetName()) - } - res[f.GetName()] = cfg.GetValue() - } - } - if diff := cmp.Diff(res, tt.want); diff != "" { - t.Errorf("got diff: %v", diff) - } - }) - } -} diff --git a/pilot/pkg/model/test/mockopenidserver.go b/pilot/pkg/model/test/mockopenidserver.go deleted file mode 100644 index da9b44c60..000000000 --- a/pilot/pkg/model/test/mockopenidserver.go +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "crypto/tls" - "errors" - "fmt" - "net" - "net/http" - "strconv" - "sync" - "sync/atomic" - "time" -) - -import ( - "github.com/gorilla/mux" - "istio.io/pkg/log" -) - -var ( - cfgContent = "{\"jwks_uri\": \"%s\"}" - serverMutex = &sync.Mutex{} -) - -const ( - // JwtPubKey1 is the response to 1st call for JWT public key returned by mock server. - JwtPubKey1 = `{ "keys": [ { "kid": "fakeKey1_1", "alg": "RS256", "kty": "RSA", "n": "abc", "e": "def" }, - { "kid": "fakeKey1_2", "alg": "RS256", "kty": "RSA", "n": "123", "e": "456" } ] }` - - // JwtPubKey1Reordered is the response to 1st call for JWT public key returned by mock server, but in a modified order of json elements. - JwtPubKey1Reordered = `{ "keys": [ { "alg": "RS256", "kid": "fakeKey1_2", "n": "123", "kty": "RSA", "e": "456" }, - { "n": "abc", "alg": "RS256", "kty": "RSA", "kid": "fakeKey1_1", "e": "def" } ] }` - - // JwtPubKey2 is the response to later calls for JWT public key returned by mock server. - JwtPubKey2 = `{ "keys": [ { "kid": "fakeKey2_1", "alg": "RS256", "kty": "RSA", "n": "ghi", "e": "lmn" }, - { "kid": "fakeKey2_2", "alg": "RS256", "kty": "RSA", "n": "789", "e": "1234" } ] }` - - JwtPubKeyNoKid = `{ "keys": [ { "alg": "RS256", "kty": "RSA", "n": "abc", "e": "def" }, - { "alg": "RS256", "kty": "RSA", "n": "123", "e": "456" } ] }` - - JwtPubKeyNoKid2 = `{ "keys": [ { "alg": "RS256", "kty": "RSA", "n": "ghi", "e": "lmn" }, - { "alg": "RS256", "kty": "RSA", "n": "789", "e": "123" } ] }` - - JwtPubKeyNoKeys = `{ "pub": [ { "kid": "fakeKey1_1", "alg": "RS256", "kty": "RSA", "n": "abc", "e": "def" }, - { "kid": "fakeKey1_2", "alg": "RS256", "kty": "RSA", "n": "123", "e": "456" } ] }` - - JwtPubKeyNoKeys2 = `{ "pub": [ { "kid": "fakeKey1_3", "alg": "RS256", "kty": "RSA", "n": "abc", "e": "def" }, - { "kid": "fakeKey1_4", "alg": "RS256", "kty": "RSA", "n": "123", "e": "456" } ] }` - - JwtPubKeyExtraElements = `{ "keys": [ { "kid": "fakeKey1_1", "alg": "RS256", "kty": "RSA", "n": "abc", "e": "def", "bla": "blah" }, - { "kid": "fakeKey1_2", "alg": "RS256", "kty": "RSA", "n": "123", "e": "456", "bla": "blah" } ] }` -) - -// MockOpenIDDiscoveryServer is the in-memory openID discovery server. -type MockOpenIDDiscoveryServer struct { - Port int - URL string - server *http.Server - - // How many times openIDCfg is called, use this number to verify cache takes effect. - OpenIDHitNum uint64 - - // How many times jwtPubKey is called, use this number to verify cache takes effect. - PubKeyHitNum uint64 - - // The mock server will return an error for the first number of hits for public key, this is used - // to simulate network errors and test the retry logic in jwks resolver for public key fetch. - ReturnErrorForFirstNumHits uint64 - - // The mock server will start to return an error after the first number of hits for public key, - // this is used to simulate network errors and test the refresh logic in jwks resolver. - ReturnErrorAfterFirstNumHits uint64 - - // The mock server will start to return a successful response after the first number of hits for public key, - // this is used to simulate network errors and test the refresh logic in jwks resolver. Note the idea is to - // use this in combination with ReturnErrorAfterFirstNumHits to simulate something like this: - // { success, success, error, error, success, success } - ReturnSuccessAfterFirstNumHits uint64 - - // The mock server will start to return an error after the first number of hits for public key, - // this is used to simulate network errors and test the refresh logic in jwks resolver. - ReturnReorderedKeyAfterFirstNumHits uint64 - - // If both TLSKeyFile and TLSCertFile are set, Start() will attempt to start a HTTPS server. - TLSKeyFile string - TLSCertFile string -} - -// StartNewServer creates a mock openID discovery server and starts it -func StartNewServer() (*MockOpenIDDiscoveryServer, error) { - serverMutex.Lock() - defer serverMutex.Unlock() - - server := &MockOpenIDDiscoveryServer{ - // 0 means the mock server always return the success result. - ReturnErrorForFirstNumHits: 0, - ReturnErrorAfterFirstNumHits: 0, - } - - return server, server.Start() -} - -// StartNewTLSServer creates a mock openID discovery server that serves HTTPS and starts it -func StartNewTLSServer(tlsCert, tlsKey string) (*MockOpenIDDiscoveryServer, error) { - serverMutex.Lock() - defer serverMutex.Unlock() - - server := &MockOpenIDDiscoveryServer{ - // 0 means the mock server always return the success result. - ReturnErrorForFirstNumHits: 0, - ReturnErrorAfterFirstNumHits: 0, - - TLSCertFile: tlsCert, - TLSKeyFile: tlsKey, - } - - return server, server.Start() -} - -// Start starts the mock server. -func (ms *MockOpenIDDiscoveryServer) Start() error { - router := mux.NewRouter() - router.HandleFunc("/.well-known/openid-configuration", ms.openIDCfg).Methods("GET") - router.HandleFunc("/oauth2/v3/certs", ms.jwtPubKey).Methods("GET") - - server := &http.Server{ - Addr: ":" + strconv.Itoa(ms.Port), - Handler: router, - } - ln, err := net.Listen("tcp", ":0") - if err != nil { - log.Errorf("Server failed to listen %v", err) - return err - } - - scheme := "http" - if ms.TLSCertFile != "" && ms.TLSKeyFile != "" { - scheme = "https" - } - - port := ln.Addr().(*net.TCPAddr).Port - ms.URL = fmt.Sprintf("%s://localhost:%d", scheme, port) - server.Addr = ":" + strconv.Itoa(port) - - // Starts the HTTP and waits for it to begin receiving requests. - // Returns an error if the server doesn't serve traffic within about 2 seconds. - go func() { - if scheme == "https" { - if err := server.ServeTLS(ln, ms.TLSCertFile, ms.TLSKeyFile); err != nil { - log.Errorf("Server failed to serve TLS in %q: %v", ms.URL, err) - } - return - } - if err := server.Serve(ln); err != nil { - log.Errorf("Server failed to serve in %q: %v", ms.URL, err) - } - }() - - httpClient := &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }, - } - wait := 10 * time.Millisecond - for try := 0; try < 10; try++ { - // Try to call the server - res, err := httpClient.Get(fmt.Sprintf("%s/.well-known/openid-configuration", ms.URL)) - if err != nil { - log.Infof("Server not yet serving: %v", err) - // Retry after some sleep. - wait *= 2 - time.Sleep(wait) - continue - } - res.Body.Close() - log.Infof("Successfully serving on %s", ms.URL) - atomic.StoreUint64(&ms.OpenIDHitNum, 0) - atomic.StoreUint64(&ms.PubKeyHitNum, 0) - ms.server = server - return nil - } - - _ = ms.Stop() - return errors.New("server failed to start") -} - -// Stop stops he mock server. -func (ms *MockOpenIDDiscoveryServer) Stop() error { - atomic.StoreUint64(&ms.OpenIDHitNum, 0) - atomic.StoreUint64(&ms.PubKeyHitNum, 0) - if ms.server == nil { - return nil - } - - return ms.server.Close() -} - -func (ms *MockOpenIDDiscoveryServer) openIDCfg(w http.ResponseWriter, req *http.Request) { - atomic.AddUint64(&ms.OpenIDHitNum, 1) - fmt.Fprintf(w, "%v", fmt.Sprintf(cfgContent, ms.URL+"/oauth2/v3/certs")) -} - -func (ms *MockOpenIDDiscoveryServer) jwtPubKey(w http.ResponseWriter, req *http.Request) { - atomic.AddUint64(&ms.PubKeyHitNum, 1) - - if ms.ReturnSuccessAfterFirstNumHits > 0 && atomic.LoadUint64(&ms.PubKeyHitNum) >= ms.ReturnSuccessAfterFirstNumHits { - fmt.Fprintf(w, "%v", JwtPubKey1) - return - } - - if ms.ReturnErrorAfterFirstNumHits != 0 && atomic.LoadUint64(&ms.PubKeyHitNum) > ms.ReturnErrorAfterFirstNumHits { - w.WriteHeader(http.StatusForbidden) - fmt.Fprintf(w, "Mock server configured to return error after %d hits", ms.ReturnErrorAfterFirstNumHits) - return - } - - if atomic.LoadUint64(&ms.PubKeyHitNum) <= ms.ReturnErrorForFirstNumHits { - w.WriteHeader(http.StatusForbidden) - fmt.Fprintf(w, "Mock server configured to return error until %d retries", ms.ReturnErrorForFirstNumHits) - return - } - - if atomic.LoadUint64(&ms.PubKeyHitNum) == ms.ReturnErrorForFirstNumHits+1 { - fmt.Fprintf(w, "%v", JwtPubKey1) - return - } - - if ms.ReturnReorderedKeyAfterFirstNumHits != 0 && atomic.LoadUint64(&ms.PubKeyHitNum) >= ms.ReturnReorderedKeyAfterFirstNumHits+1 { - fmt.Fprintf(w, "%v", JwtPubKey1Reordered) - return - } - - fmt.Fprintf(w, "%v", JwtPubKey2) -} diff --git a/pilot/pkg/model/test/testcert/cert.pem b/pilot/pkg/model/test/testcert/cert.pem deleted file mode 100644 index a6a50d57c..000000000 --- a/pilot/pkg/model/test/testcert/cert.pem +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFlzCCA3+gAwIBAgIUIJ++HYHkjnV3zi6grRY/7TEmztwwDQYJKoZIhvcNAQEL -BQAwUDELMAkGA1UEBhMCVVMxDzANBgNVBAgMBkRlbmlhbDEOMAwGA1UEBwwFRXRo -ZXIxDDAKBgNVBAoMA0RpczESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIwMDgxMTIy -MTM1OFoXDTMwMDgwOTIyMTM1OFowUDELMAkGA1UEBhMCVVMxDzANBgNVBAgMBkRl -bmlhbDEOMAwGA1UEBwwFRXRoZXIxDDAKBgNVBAoMA0RpczESMBAGA1UEAwwJbG9j -YWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuVjbx5ZtjubT -rvVhWbNclNFvEmQWH0tDe9cQ8bsYOqcAyGSylXX2mz2O/yYs3vMNiQT75DBi2Sdd -6VUwGMCnKZNbu0LkW9lE9BGIvE6zmVhUw+aaLpaQj5ud5N+fXy299UJN1uBVG62r -6d2CE6ZktsMogYHDgFQiraJEOp3f5Z/vsWRocfIwnDmbKs3gptq7Qc6VkTdetlKH -/EpCE4PPeX8KsmppqHlpHMmrgEg2yxqpHvxQjXyUIs2J+PauDRHlzGMImlHz0weE -mGDmOhbJKGfUwFJo1xQB67MmkWdQjdmwQ9KFy/KVdAm4R3WznzAe5vMHBXb6XERV -+pWTmQmNNLHIdU/pCc2rYyP8KMYddxOEQl07Ba3VtPgMSxabvEOS8nI2IT81yWmX -MXwD2NnH1xd4RgyqBPeAwoYs/Ug5c8ruA8a1p9oxDTxsflS/QiSeir3peUmfYdvt -R+tZcTG+cdHkGgcSMEcwU4CcFbqPPj2O/7pRttoq6F0/SlQMyHHUNXGb2NqfHxJ2 -nGU0Y7TRWf8WBTpwOWdyKANgyWu9EPWXhVachDx+u58kdkXwxFmQaqRw251EPIVT -4UzcaFN8xSf4yaq0vaysTRC4EgBNLXyW+i9vpnyAs6JTWxpxCGwL9pk6KlosO+Ou -XAq/U2a3Kw91KTtVQafdupzfF34HoSUCAwEAAaNpMGcwHQYDVR0OBBYEFFMLAgBD -1ZDarEAPa8ElgaL6zY8+MB8GA1UdIwQYMBaAFFMLAgBD1ZDarEAPa8ElgaL6zY8+ -MA8GA1UdEwEB/wQFMAMBAf8wFAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqGSIb3 -DQEBCwUAA4ICAQAOmbfnVLxEZSnRk5fOlUIt6OHFrRYi/ezrtoSqZhL4nK7BqMfM -b+hqBiBCj3H/dFz/B8rKPrvnJqjK1PeSEpcQkcv1G7ZC8UYgmXyl437J0TcKNAR1 -DMuFdNpfnBQ21v+ckTZkl1KsT36c9r3Q45TvF+d5P3qmYuwbBQRAx91lmDkMdmub -KjRNNdNeBdV6NpQXKd6wsNpjjeM7yg0PeGyF6D9qSr3z7x8kfXNtgeCzyfYa2Jvz -74YNActTqBLBTpH1IusakXj21IPI+ij0Pbrq919q9nAzR5JoS8IM1zXF6xrN+0uH -2hwQH45dulrOLiy7yX2qYcebbPodcc3MJoGD71/FTJhNu7do5W/hSW2suK2BUycE -eNrcnEYBzQq2XWyg/YkAar9c/UNd7uM50oh09NlzG4gQ1gsUAuPCbuELjwmwSpbw -sd2BbBa025tEHM1RTPf/kPLB0Y5oNHiw+0Yjaw71V+xsugS4d5GWIjnXeZ/w0oVz -rePsZgNZwna56RH3Wqc/gsHWOm2g6y3VisDR/jUT8K6R3CYyD8e8NbwOLzqB+b0S -QCS5L7YDrDjOcyJ/JQ/Jh6jIhoNZNFIjzCi0yig6ub59wafA4E9t4hcEQOwnPIb8 -iQmsvjBFdRPVa9E2i5OvSB6kdumwPNhB0cJmQsadVckj2myQZtPlHn+9Zw== ------END CERTIFICATE----- diff --git a/pilot/pkg/model/test/testcert/cert2.pem b/pilot/pkg/model/test/testcert/cert2.pem deleted file mode 100644 index 094dab5cb..000000000 --- a/pilot/pkg/model/test/testcert/cert2.pem +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFmzCCA4OgAwIBAgIURYi9Ze7H6Q9Z21WmsPRGke2V/2gwDQYJKoZIhvcNAQEL -BQAwUjELMAkGA1UEBhMCVVMxDzANBgNVBAgMBkRlbmlhbDEOMAwGA1UEBwwFRXRo -ZXIxDDAKBgNVBAoMA0RpczEUMBIGA1UEAwwLYW5vdGhlcmhvc3QwHhcNMjAwODEx -MjIxMzU4WhcNMzAwODA5MjIxMzU4WjBSMQswCQYDVQQGEwJVUzEPMA0GA1UECAwG -RGVuaWFsMQ4wDAYDVQQHDAVFdGhlcjEMMAoGA1UECgwDRGlzMRQwEgYDVQQDDAth -bm90aGVyaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALr0Ciit -ktznXQYCQR+G9PebNlcKkp+lwiy94bNm3eYCNkU6XMLHMvpWRTeQx3N0tP6OHwP2 -Y3vqOAszoh3slLUwgL5PKSvXb01nFkLrUb5yrFpPg+siWLDSzfuwn14r63Cbri3W -Xf2v4zr3NZzxPz8HPE+zv17Kyumr3BySgtfJXBJc5XktIR4ACfICR/BGwoVR304W -KX+PgVXldTHcjr7LxY2wfzqLlNcMVA3yaRybyakZE/4kdDmL234b2F3KTiZEklIe -ksfR2890NL6N64fyEQB9tW/MSdAmT76K6Pg2Gk4XMUC6gnuq9k2KcWaEyIOMgHXz -PUpKT3fNHmDg0B8pFk1rf9Nn6i9AsSJtkgUGVVKBH7h/fdmK52355AvUUCHh+CWh -/7B//TConsbI6YhOtYOmoUa3lv1jJxNptDgNkovPhDQOEQC9lpLuT2LIXarU5l8F -ANMHW7wEJwYrK9H4dS5yJMp45D8/pEsnRkmxZFljC2qWPVEwnkMlxGgzICbqwn9z -3b6Y479yIZE7OGiwjAOgd9nR/MFxHQIcXHo7AWmr/Atfzevqy2Y0dKEl24Wiwvux -w9n6akKA+/bOtkFy9KzonsdyYjm5surXcKKqsuqB1lOV1xC3SHfzd38iEktB+lhK -TpHGRtoCiEM6adqVlbabRsdwsP09EdmQSLBrAgMBAAGjaTBnMB0GA1UdDgQWBBQX -D5ZaeSfZgLOQbDs5Ncjc7ZTDXDAfBgNVHSMEGDAWgBQXD5ZaeSfZgLOQbDs5Ncjc -7ZTDXDAPBgNVHRMBAf8EBTADAQH/MBQGA1UdEQQNMAuCCWxvY2FsaG9zdDANBgkq -hkiG9w0BAQsFAAOCAgEAZUZtnt5RhVWb08yiQA2e3IxeScCoKtguZOYkvlmMF+ir -ItfmhyiseduYe2U1gSJwKJs83012Hxr5HJ3i3jtbxqZuzJCfZW8/vNuvlMnRFbXd -PGXaihJiZ4/jWSJsVm5Tn4zX5LyQCXZ1Ncja9LVPI4dD0/jugG+gZ5hmCtms/OFC -OGlcs8Fbl8IrE/731FBSO4sqxkicRSyBWXqVwH9PX6vnIhoCztxJlUP/rUfvtxL+ -ujZ8Vks+P69eNU88hcpUPbRrKd3fIod93dASMHpXywLYI3NmHb437N2pltW7YnYN -Sxo5EQ10n3gK9H4MekPe/gsxPa4loz2ZRm5KVQMeqXRFeKFL4yPD8T5Q7tFyhVmO -FgZ/qypxHweD8M9SgDZl+CuVF/jCiIV96E/MfSz/esS8xTST7IVXf34/d4j5oZkY -/JwSafo99YhARfyAh+e+V63inKiLVAoSiJbuFScm/T01IToGce2P8XbS5gjhtDuG -8wQ/8JeYqV9W5azjrk8+7WQPEQ11r7mVtEz/DFBjdorNq15IvIFBabdM13R9tUkC -lnIR9Qau5AzfaAwGF4qWcmJ/toN6aui9io7A6HlaS+35z3doBtploPERfP9HqT9o -tInc2n0nQnbQXUAXAFjRGvfDaXuBPbuGbCsvBDk9avpVEwz4uRpMh6t/TMJlgpI= ------END CERTIFICATE----- diff --git a/pilot/pkg/model/test/testcert/generate.sh b/pilot/pkg/model/test/testcert/generate.sh deleted file mode 100755 index b6f4edf11..000000000 --- a/pilot/pkg/model/test/testcert/generate.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh - -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -openssl req -new -newkey rsa:4096 -x509 -sha256 \ - -days 3650 -nodes -out cert.pem -keyout key.pem \ - -subj "/C=US/ST=Denial/L=Ether/O=Dis/CN=localhost/SAN=localhost" \ - -addext "subjectAltName = DNS:localhost" - -openssl req -new -newkey rsa:4096 -x509 -sha256 \ - -days 3650 -nodes -out cert2.pem -keyout key2.pem \ - -subj "/C=US/ST=Denial/L=Ether/O=Dis/CN=anotherhost" \ - -addext "subjectAltName = DNS:localhost" diff --git a/pilot/pkg/model/test/testcert/key.pem b/pilot/pkg/model/test/testcert/key.pem deleted file mode 100644 index d66505a24..000000000 --- a/pilot/pkg/model/test/testcert/key.pem +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQC5WNvHlm2O5tOu -9WFZs1yU0W8SZBYfS0N71xDxuxg6pwDIZLKVdfabPY7/Jize8w2JBPvkMGLZJ13p -VTAYwKcpk1u7QuRb2UT0EYi8TrOZWFTD5poulpCPm53k359fLb31Qk3W4FUbravp -3YITpmS2wyiBgcOAVCKtokQ6nd/ln++xZGhx8jCcOZsqzeCm2rtBzpWRN162Uof8 -SkITg895fwqyammoeWkcyauASDbLGqke/FCNfJQizYn49q4NEeXMYwiaUfPTB4SY -YOY6FskoZ9TAUmjXFAHrsyaRZ1CN2bBD0oXL8pV0CbhHdbOfMB7m8wcFdvpcRFX6 -lZOZCY00sch1T+kJzatjI/woxh13E4RCXTsFrdW0+AxLFpu8Q5LycjYhPzXJaZcx -fAPY2cfXF3hGDKoE94DChiz9SDlzyu4DxrWn2jENPGx+VL9CJJ6Kvel5SZ9h2+1H -61lxMb5x0eQaBxIwRzBTgJwVuo8+PY7/ulG22iroXT9KVAzIcdQ1cZvY2p8fEnac -ZTRjtNFZ/xYFOnA5Z3IoA2DJa70Q9ZeFVpyEPH67nyR2RfDEWZBqpHDbnUQ8hVPh -TNxoU3zFJ/jJqrS9rKxNELgSAE0tfJb6L2+mfICzolNbGnEIbAv2mToqWiw7465c -Cr9TZrcrD3UpO1VBp926nN8XfgehJQIDAQABAoICAA7Pl/hx/908esvvVdEqMQq/ -VaXdk5r6k7Dwtazra3s8XyakTVT+MS+kkqm27jz+pSU+3bYVW8DIkYuUwBwqQPFk -kh77sG3ahdUPFaMekuF9GoDxC38Pjwu6vrpMWFrtt9FCdB/om0Rz81nj1oIleMTW -AYWfzfSSRJclcVhRx1N9HVVKlcINPW8ud0TLYNlUf0Q58t/A0fO10qIJeRBAJzMp -QLyTHlBVK14ei3Lb0m0We087kGBOBTRmwlC/vZbTyE4117klNVu2rRTUIXGg4oA1 -E94ud6xAMAsarMlPvTMooxo4V3K9ts2n/S7QrALbgIyp/PSl1knLOCenDj1bcwoR -HoZsJMUIn60xXlm/CKD67f9JZmRk/ys03aor/iHh+38+qM3KJ3FVRmjRrzGY9+1i -JcIwS2xbQgz7ZVDpb8MzdzCPXoDpXtvjsFN+ddHXx7CDjlyA+GeDn3S3riSPl6FQ -BRUAuo/g4MuWs8C21ZzGF0TTcrrXrlRu4xBv/8CcW6HcX/k4JcQWYeOIsgZpxa8D -c45zJePIWclC/SS1jh4dQeLlx6J1+CZAH0fq6YpY/DrFta1smiezK5UjJZPyNycD -d5dMHTbO6KIOi28fPakNL9aveqbSPw8X5uMf3QQ3QG4CiN98aCkVb7Zfu4UiFRLh -P+T59boT/opux7lU7u1hAoIBAQDa6S0OGvDl3pbaWPPgCSF5xGmoivoKnSA2Q7l0 -lX3mpnO+G0EeSvp+sZdRm3Vq8o3P2LEeqNeEwiZgvyZCpmwffjdrXMPBoLJVbzCt -tVvCypuYx8Je7PGPtDL0jEEYCpV2v8eLhHba1JUz5O7XFpK0CHhxN2422B6nigG4 -FYrkteaz+mIcb/VtVtz8RQbNhjv1uyRmYsgfgUa0CyD2kSJLeCNsCM7zJ1268OP9 -UjWy547BsFZVzT6GexdEQEQpF0ORnTIfG88zVtGoCQvlsYuYVJevFxMPON30LGGn -hf1Qr0Mbs29dAh72RUO4+Dpm3m/wE8VKedcdLhdLd4B/8Yq9AoIBAQDYv+v7c/9S -CWv8bL7T4GuFj9U3OIlk9ypvUwDoQRrRyMydhMhsIv/2AYKAcO3cjLoMkNn/q5m9 -Ue5xefKnGXnM2oIskCn0sezKh3AHFA2CEIjeso1Alr9Kj/qoi/dcORJKgJyyOdxB -W3njWyJDzb7cSEFyFppyrSq7WrAK3zWHb6NbVdAYUzoP+7mc99B5Aj5K7ZSu4obT -nt+cKKbPd4V/vbTvlf5vSyKhyty5E56DJHJmOaVhr8eGJBpQCrW/xynDZfd30tyT -qQZTWMMA5/knuSCweq520ZH6tDIqUfAoxftyoZDvl6K9Xlma9e8gS3YJAMlhTWjL -q49PJfvBzQqJAoIBAQCdg3I71/7GJ+d7rM2twmQn7b/o2jcYM/7djNWAoaA5ukNF -4u50EZc0Wix7N9TA405O46huWybYC6r5qhCpnH3oGzVdNx5T0IEBIERyeTBRdN+I -ixO8odkegZa4h8K+vb7V9yBpryKmSpghAvUoJsHgcMfZDv1q0Q8dlhuujTGnZ4tc -rSizNYvu5LWVf0Vp1NCH++QxF5rPsIFjQy6wu7VlgEhcj+n447Z5sKMbv2+MDzmU -/IDitA/tS9xt66AfwPGJcQaDg8sPMcUhl4DMAXhkX1nVTlEUfuKVsumBVNP0nmc3 -I2FaFxCYOzaDfXjoU91Pbapj8E9sW6G/V3lKlk4xAoIBAD6IwLC/er5UcY9WYIgI -i3zxTnasHRfAJ8IOlIhtoCs+X6pikOCCCI8VSG1+J2ImR9cwQ3T8MVLpvH2IzXVg -aD+uGzh4L2Kloseb47pnEq6fzp1RrmcgFviQKaVPHILDBH/yM0NIZj+FANmThYNC -bPpo7ljYJJ9JTrbXJ0s7stnsX5kOXQdnm6zY12huJvuCpi4/9VGvkW6ku6b2I4pf -1WhRd05YXBwH9WnweVX8TxNPBKJxp0FT0aDl8aF+NYmrEGWLmaY8c982ONj7FufA -BaZsRv4jpwoo1JEcRAzprvxUgoNEEejOOumfuwJ2ShNrMhbO30rvZni8epA/SS9U -1OECggEANtch7HtkiWJMTfa9iOOvWBmKaEbU+NdOlhe+5sx1Ol9zGt25r6hAewG4 -Z0mtyqflacCQac/wQHNpNPI9w+Gg0efx4GvlfDA+Zb3h6MtThUuDY2PI9G+N/9na -iv/6Xr9L6/qbBW1yP/2ve1xhUV+z5Oxmh90YaYPT4Pc8K8RK7o9KWnmVS9rO88xe -mnrU6+YFfEylY8lJaQ/yRyodYly0Kdwof2t0SMLfMILVeTJQkp+9/Cfjdb/tegCB -4f2WOkQmadYfaDNTe57tcBhcG6DcbnXlttprAz7sfVQ6tOh14E6YTN4vf9sis+Qo -bt43X/yBt63A8Owiunx2s9crB28/2w== ------END PRIVATE KEY----- diff --git a/pilot/pkg/model/test/testcert/key2.pem b/pilot/pkg/model/test/testcert/key2.pem deleted file mode 100644 index 0688b075c..000000000 --- a/pilot/pkg/model/test/testcert/key2.pem +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQC69AoorZLc510G -AkEfhvT3mzZXCpKfpcIsveGzZt3mAjZFOlzCxzL6VkU3kMdzdLT+jh8D9mN76jgL -M6Id7JS1MIC+Tykr129NZxZC61G+cqxaT4PrIliw0s37sJ9eK+twm64t1l39r+M6 -9zWc8T8/BzxPs79eysrpq9wckoLXyVwSXOV5LSEeAAnyAkfwRsKFUd9OFil/j4FV -5XUx3I6+y8WNsH86i5TXDFQN8mkcm8mpGRP+JHQ5i9t+G9hdyk4mRJJSHpLH0dvP -dDS+jeuH8hEAfbVvzEnQJk++iuj4NhpOFzFAuoJ7qvZNinFmhMiDjIB18z1KSk93 -zR5g4NAfKRZNa3/TZ+ovQLEibZIFBlVSgR+4f33Ziudt+eQL1FAh4fglof+wf/0w -qJ7GyOmITrWDpqFGt5b9YycTabQ4DZKLz4Q0DhEAvZaS7k9iyF2q1OZfBQDTB1u8 -BCcGKyvR+HUuciTKeOQ/P6RLJ0ZJsWRZYwtqlj1RMJ5DJcRoMyAm6sJ/c92+mOO/ -ciGROzhosIwDoHfZ0fzBcR0CHFx6OwFpq/wLX83r6stmNHShJduFosL7scPZ+mpC -gPv2zrZBcvSs6J7HcmI5ubLq13CiqrLqgdZTldcQt0h383d/IhJLQfpYSk6Rxkba -AohDOmnalZW2m0bHcLD9PRHZkEiwawIDAQABAoICACjXWUTLrnK9xG+knG9zvx48 -dSGMuL1Rq9q0XJMhnMY7o3l6J3uRosxXfmWGJlxNmaCmkZX/BHq0jyu7SuY4ob/3 -aZipmHfwZ2tEYvOZjtEJIifUiSAb774pP0CFtDsRYyzFhcaTtxLNktl12euANG3j -ecR7ownlSXDEz6AaTaUwYwHL84GGX167aXDKGMcf3izb1umrI8OvKPUH3JNY+IIr -5+ttLE4w4gIculwVoerTCbn8NYbmldFggUHKeXj70hjAECsKbMxkRx3J6B/cxyB6 -vEYcMD/NJC7pv7ADjqNyxkpa/LKETC8SCR6laOKZjWhXkFS0H6aUF0o3sDQ/yJc1 -8VG/gHrodG9ZdRMC28E6jRqGlT8RM608tQkxmrD9mLYHGkbcS9ASwu+I6zB8xHyA -AH9AgZMoscEc3moHaybD4AdZXtK7KBTHrhNOfjddKva/qzh/gBNraX7EeRGFgQ5E -w6gI7jhR8TXempt6MNtyh+sM9BWGYAENtRW2d02B1+x2LItEk287SLekf8K8tKGP -C7cUrBzp1d954bBMOKBB22kRhZVcCaBJOhKzHHw7s8wzYCQ3xt1D2QAS+NR8SQlr -RvAwa+ukwX7q2wImi80WtaYp2/2a7v1gZlCYY1jHe5yvQh5Kuy2ld3WhKkd7wkZL -YulkWwsjqWl6syufW4yBAoIBAQDzsnaon8QNogW7GvDK3F19Gy7N8kpZrJEtVdoF -TxBfpWhpyf/mD6V1jImMny4uk19D/i4LpCzV1QE8GSknLgywQJ54IsLwk2krWUUP -xnGmXl4qZmyw6SrDfEUqKmF4WkV/pEOrATLSbr4iZpKzgyVMZEKwuh30f7fl68TJ -UT7E6v1QPx36Q6uvPbKkxdPbLTQ1Z2ViI7Tlbb6rOq6XamMh+D6+0E1nRSevni3U -beNeTOHM8E3LUHg4rZW8Nn9qm+4/Igqe1q5XXeteyTFE8pd/gvT6H5sB4wiEWDyU -J66aP+1TmPWQzeZERPJM/pEWOsYBjgUXWmFt3Ds9/olH25pBAoIBAQDEZDhN7jJG -bZKJtafh/ntGho+QEEXaqz4GBOlA4a4v/e5GYG3RxqkGj0uUd3Gx52tjkOv7tZDb -Ld0Qg9vi+8lJowxpPZIx8xspHt8InfjsBusPomZl5DctjyPrtbE5BZYGBb2eRtlS -20kUOHrfytZfnJbZ0F6zfKFKKIkP2Rnaijvt/64zFct71+KeDms0UYsjKhZgvu+f -SA3YeirJ0vN9CArinAkvNtAWuqQfK1HCDKFPmm72CpTdiXcuJRW934nAUvTWbiyV -ZAvCtmy14SC94u5C7GQXoo+1/huIJxQ6/6f1zfLB6oSNRT4dtpnXMX5Lu++upp5b -3ikc+n0+aOerAoIBAQCLk4uiyN7hcAsYPzXq+owRO6BzFHLBKbV84c8de0tFfNdw -v+FxQ0ThLr0ZbZJl2sKVjt+Qi1eioX0zjrWjWxypVlltNXuxjryNt3N2tZIiV6pX -XpKiQOndTS/QFrX82Lc1hD+9kdIp9IOx8E3Vwyd5uXL5BaBiiUQqy/7Jz17EKlMr -wvrIfDm5BP1somHVBu8AJ1hJ/Wl+fnzWVaXp02Uz75sAbw3mOBWi4FYfdVDkkeRC -HOBxsoUo8ULxqcUQqn6qajTrBMqV/5TvlQxDES70OW1WVQPS8NXP9zm7IX8/mZ20 -Y8dCSKu6LkMZFjTDJqaB/XWtCncDFUBZIZA/N+gBAoIBAFzSPWZf4XU2Dp91sy52 -N8+ig7IEbu+mJf90TMZ0o/5c10R+CQmwCm1mRsOaHKuIBFKfyVlDfH6LU8Odfszu -UjmyjYgkKLfg/gJWa/XHrEHqFylhiWI5Ffc7gy/BxUx9HmYkSnXHaKnnqMec1j51 -wRKZkRbLjCbrDdReCkHqS3TmBuM7CBtu/qi+5YXsrLCipc9t8fux4tF2ncymGRkg -0iJTLcnjE9gz4PANMMrmj5LWo1tbU3yIjcmTUjgGIRuHzrSOwNekZQAKtCBIVmM0 -T2RQIGqTDc+57pCG1H5qcf5cnQbpWwZaEGIwyuYCSv3Rj6692c/DQm+LNgVTQuS/ -clcCggEBALjDQ/rB++ipZK5LRcOeU68Sifkh+jAkHTvcxBGUaREoSmAubH94jn4R -4VnCz0N/HWwnS0JTLOFD5E/WfV4emUytLnh9POT7fAQFIrYgh2mmwUAHFdroutsb -UpbdwKa+F4F+chR/jr7zYKtbktJE8nTjT6E7I7nm5tgR5p3asiu2W3/mYSwQdnVO -ClkjbTarPxvefucCkGEwvR0JzZjNYsyz7aLHPVUeIbpxl7NIak5QYCxQy96Z6N34 -QjrtzhtfweFTRqDLaQLCR+72gLIe9WpOehhhwnnop26a9UaXqYx2RBvNe0CCzEnG -JIPHEUOqgF1LD0YPvcu7sRZ5PvCY2Hs= ------END PRIVATE KEY----- diff --git a/pilot/pkg/model/validation.go b/pilot/pkg/model/validation.go deleted file mode 100644 index f0e805db8..000000000 --- a/pilot/pkg/model/validation.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "fmt" - "strings" -) - -import ( - "github.com/hashicorp/go-multierror" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/validation" -) - -// UnixAddressPrefix is the prefix used to indicate an address is for a Unix Domain socket. It is used in -// ServiceEntry.Endpoint.Address message. -const ( - UnixAddressPrefix = "unix://" - PodIPAddressPrefix = "0.0.0.0" - LocalhostAddressPrefix = "127.0.0.1" -) - -// Validate ensures that the service object is well-defined -func (s *Service) Validate() error { - var errs error - if len(s.Hostname) == 0 { - errs = multierror.Append(errs, fmt.Errorf("invalid empty hostname")) - } - parts := strings.Split(string(s.Hostname), ".") - for _, part := range parts { - if !labels.IsDNS1123Label(part) { - errs = multierror.Append(errs, fmt.Errorf("invalid hostname part: %q", part)) - } - } - - // Require at least one port - if len(s.Ports) == 0 { - errs = multierror.Append(errs, fmt.Errorf("service must have at least one declared port")) - } - - // Port names can be empty if there exists only one port - for _, port := range s.Ports { - if port.Name == "" { - if len(s.Ports) > 1 { - errs = multierror.Append(errs, - fmt.Errorf("empty port names are not allowed for services with multiple ports")) - } - } else if !labels.IsDNS1123Label(port.Name) { - errs = multierror.Append(errs, fmt.Errorf("invalid name: %q", port.Name)) - } - if err := validation.ValidatePort(port.Port); err != nil { - errs = multierror.Append(errs, - fmt.Errorf("invalid service port value %d for %q: %v", port.Port, port.Name, err)) - } - } - return errs -} - -// Validate ensures that the service instance is well-defined -func (instance *ServiceInstance) Validate() error { - var errs error - if instance.Service == nil { - errs = multierror.Append(errs, fmt.Errorf("missing service in the instance")) - } else if err := instance.Service.Validate(); err != nil { - errs = multierror.Append(errs, err) - } - - if instance.Endpoint != nil { - if err := instance.Endpoint.Labels.Validate(); err != nil { - errs = multierror.Append(errs, err) - } - - if err := validation.ValidatePort(int(instance.Endpoint.EndpointPort)); err != nil { - errs = multierror.Append(errs, err) - } - } - - port := instance.ServicePort - if port == nil { - errs = multierror.Append(errs, fmt.Errorf("missing service port")) - } else if instance.Service != nil { - expected, ok := instance.Service.Ports.Get(port.Name) - if !ok { - errs = multierror.Append(errs, fmt.Errorf("missing service port %q", port.Name)) - } else { - if expected.Port != port.Port { - errs = multierror.Append(errs, - fmt.Errorf("unexpected service port value %d, expected %d", port.Port, expected.Port)) - } - if expected.Protocol != port.Protocol { - errs = multierror.Append(errs, - fmt.Errorf("unexpected service protocol %s, expected %s", port.Protocol, expected.Protocol)) - } - } - } - - return errs -} diff --git a/pilot/pkg/model/validation_test.go b/pilot/pkg/model/validation_test.go deleted file mode 100644 index c329dbfa2..000000000 --- a/pilot/pkg/model/validation_test.go +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" -) - -var ( - endpoint1 = IstioEndpoint{ - Address: "192.168.1.1", - EndpointPort: 10001, - } - - service1 = &Service{ - Hostname: "one.service.com", - DefaultAddress: "192.168.3.1", // VIP - Ports: PortList{ - &Port{Name: "http", Port: 81, Protocol: protocol.HTTP}, - &Port{Name: "http-alt", Port: 8081, Protocol: protocol.HTTP}, - }, - } -) - -func TestServiceInstanceValidate(t *testing.T) { - endpointWithLabels := func(lbls labels.Instance) *IstioEndpoint { - cpy := endpoint1 - cpy.Labels = lbls - return &cpy - } - - cases := []struct { - name string - instance *ServiceInstance - valid bool - }{ - { - name: "nil service", - instance: &ServiceInstance{ - Endpoint: endpointWithLabels(labels.Instance{}), - }, - }, - { - name: "bad label", - instance: &ServiceInstance{ - Service: service1, - Endpoint: endpointWithLabels(labels.Instance{"*": "-"}), - }, - }, - { - name: "invalid service", - instance: &ServiceInstance{ - Service: &Service{}, - Endpoint: &IstioEndpoint{}, - }, - }, - { - name: "invalid endpoint port and service port", - instance: &ServiceInstance{ - Service: service1, - Endpoint: &IstioEndpoint{ - Address: "192.168.1.2", - EndpointPort: 1000000, - }, - }, - }, - { - name: "endpoint missing service port", - instance: &ServiceInstance{ - Service: service1, - ServicePort: &Port{ - Name: service1.Ports[1].Name + "-extra", - Port: service1.Ports[1].Port, - Protocol: service1.Ports[1].Protocol, - }, - Endpoint: &IstioEndpoint{ - Address: "192.168.1.2", - EndpointPort: uint32(service1.Ports[1].Port), - }, - }, - }, - { - name: "endpoint port and protocol mismatch", - instance: &ServiceInstance{ - Service: service1, - ServicePort: &Port{ - Name: "http", - Port: service1.Ports[1].Port + 1, - Protocol: protocol.GRPC, - }, - Endpoint: &IstioEndpoint{ - Address: "192.168.1.2", - EndpointPort: uint32(service1.Ports[1].Port), - }, - }, - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - if got := c.instance.Validate(); (got == nil) != c.valid { - t.Fatalf("%s failed: got valid=%v but wanted valid=%v: %v", c.name, got == nil, c.valid, got) - } - }) - } -} - -func TestServiceValidate(t *testing.T) { - ports := PortList{ - {Name: "http", Port: 80, Protocol: protocol.HTTP}, - {Name: "http-alt", Port: 8080, Protocol: protocol.HTTP}, - } - badPorts := PortList{ - {Port: 80, Protocol: protocol.HTTP}, - {Name: "http-alt^", Port: 8080, Protocol: protocol.HTTP}, - {Name: "http", Port: -80, Protocol: protocol.HTTP}, - } - - address := "192.168.1.1" - - cases := []struct { - name string - service *Service - valid bool - }{ - { - name: "empty hostname", - service: &Service{ - Hostname: "", - DefaultAddress: address, - Ports: ports, - }, - }, - { - name: "invalid hostname", - service: &Service{ - Hostname: "hostname.^.com", - DefaultAddress: address, - Ports: ports, - }, - }, - { - name: "empty ports", - service: &Service{ - Hostname: "hostname", - DefaultAddress: address, - }, - }, - { - name: "bad ports", - service: &Service{ - Hostname: "hostname", - DefaultAddress: address, - Ports: badPorts, - }, - }, - } - for _, c := range cases { - if got := c.service.Validate(); (got == nil) != c.valid { - t.Errorf("%s failed: got valid=%v but wanted valid=%v: %v", c.name, got == nil, c.valid, got) - } - } -} diff --git a/pilot/pkg/model/virtualservice.go b/pilot/pkg/model/virtualservice.go deleted file mode 100644 index c35236608..000000000 --- a/pilot/pkg/model/virtualservice.go +++ /dev/null @@ -1,545 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "strings" -) - -import ( - "github.com/gogo/protobuf/jsonpb" - "google.golang.org/protobuf/proto" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/config/visibility" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// SelectVirtualServices selects the virtual services by matching given services' host names. -// This is a common function used by both sidecar converter and http route. -func SelectVirtualServices(virtualServices []config.Config, hosts map[string][]host.Name) []config.Config { - importedVirtualServices := make([]config.Config, 0) - - vsset := sets.New() - addVirtualService := func(vs config.Config, hosts host.Names) { - vsname := vs.Name + "/" + vs.Namespace - rule := vs.Spec.(*networking.VirtualService) - for _, ih := range hosts { - // Check if the virtual service is already processed. - if vsset.Contains(vsname) { - break - } - for _, h := range rule.Hosts { - // VirtualServices can have many hosts, so we need to avoid appending - // duplicated virtualservices to slice importedVirtualServices - if vsHostMatches(h, ih, vs) { - importedVirtualServices = append(importedVirtualServices, vs) - vsset.Insert(vsname) - break - } - } - } - } - for _, c := range virtualServices { - configNamespace := c.Namespace - // Selection algorithm: - // virtualservices have a list of hosts in the API spec - // if any host in the list matches one service hostname, select the virtual service - // and break out of the loop. - - // Check if there is an explicit import of form ns/* or ns/host - if importedHosts, nsFound := hosts[configNamespace]; nsFound { - addVirtualService(c, importedHosts) - } - - // Check if there is an import of form */host or */* - if importedHosts, wnsFound := hosts[wildcardNamespace]; wnsFound { - addVirtualService(c, importedHosts) - } - } - - return importedVirtualServices -} - -// vsHostMatches checks if the given VirtualService host matches the importedHost (from Sidecar) -func vsHostMatches(vsHost string, importedHost host.Name, vs config.Config) bool { - if UseGatewaySemantics(vs) { - // The new way. Matching logic exactly mirrors Service matching - // If a route defines `*.com` and we import `a.com`, it will not match - return host.Name(vsHost).SubsetOf(importedHost) - } - - // The old way. We check Matches which is bi-directional. This is for backwards compatibility - return host.Name(vsHost).Matches(importedHost) -} - -func resolveVirtualServiceShortnames(rule *networking.VirtualService, meta config.Meta) { - // resolve top level hosts - for i, h := range rule.Hosts { - rule.Hosts[i] = string(ResolveShortnameToFQDN(h, meta)) - } - // resolve gateways to bind to - for i, g := range rule.Gateways { - if g != constants.IstioMeshGateway { - rule.Gateways[i] = resolveGatewayName(g, meta) - } - } - // resolve host in http route.destination, route.mirror - for _, d := range rule.Http { - for _, m := range d.Match { - for i, g := range m.Gateways { - if g != constants.IstioMeshGateway { - m.Gateways[i] = resolveGatewayName(g, meta) - } - } - } - for _, w := range d.Route { - if w.Destination != nil { - w.Destination.Host = string(ResolveShortnameToFQDN(w.Destination.Host, meta)) - } - } - if d.Mirror != nil { - d.Mirror.Host = string(ResolveShortnameToFQDN(d.Mirror.Host, meta)) - } - } - // resolve host in tcp route.destination - for _, d := range rule.Tcp { - for _, m := range d.Match { - for i, g := range m.Gateways { - if g != constants.IstioMeshGateway { - m.Gateways[i] = resolveGatewayName(g, meta) - } - } - } - for _, w := range d.Route { - if w.Destination != nil { - w.Destination.Host = string(ResolveShortnameToFQDN(w.Destination.Host, meta)) - } - } - } - // resolve host in tls route.destination - for _, tls := range rule.Tls { - for _, m := range tls.Match { - for i, g := range m.Gateways { - if g != constants.IstioMeshGateway { - m.Gateways[i] = resolveGatewayName(g, meta) - } - } - } - for _, w := range tls.Route { - if w.Destination != nil { - w.Destination.Host = string(ResolveShortnameToFQDN(w.Destination.Host, meta)) - } - } - } -} - -// Return merged virtual services and the root->delegate vs map -func mergeVirtualServicesIfNeeded( - vServices []config.Config, - defaultExportTo map[visibility.Instance]bool) ([]config.Config, map[ConfigKey][]ConfigKey) { - out := make([]config.Config, 0, len(vServices)) - delegatesMap := map[string]config.Config{} - delegatesExportToMap := map[string]map[visibility.Instance]bool{} - // root virtualservices with delegate - var rootVses []config.Config - - // 1. classify virtualservices - for _, vs := range vServices { - rule := vs.Spec.(*networking.VirtualService) - // it is delegate, add it to the indexer cache along with the exportTo for the delegate - if len(rule.Hosts) == 0 { - delegatesMap[key(vs.Name, vs.Namespace)] = vs - if len(rule.ExportTo) == 0 { - // No exportTo in virtualService. Use the global default - delegatesExportToMap[key(vs.Name, vs.Namespace)] = defaultExportTo - } else { - exportToMap := make(map[visibility.Instance]bool) - for _, e := range rule.ExportTo { - if e == string(visibility.Private) { - exportToMap[visibility.Instance(vs.Namespace)] = true - } else { - exportToMap[visibility.Instance(e)] = true - } - } - delegatesExportToMap[key(vs.Name, vs.Namespace)] = exportToMap - } - continue - } - - // root vs - if isRootVs(rule) { - rootVses = append(rootVses, vs) - continue - } - - // the others are normal vs without delegate - out = append(out, vs) - } - - delegatesByRoot := make(map[ConfigKey][]ConfigKey, len(rootVses)) - - // 2. merge delegates and root - for _, root := range rootVses { - rootConfigKey := ConfigKey{Kind: gvk.VirtualService, Name: root.Name, Namespace: root.Namespace} - rootVs := root.Spec.(*networking.VirtualService) - mergedRoutes := []*networking.HTTPRoute{} - for _, route := range rootVs.Http { - // it is root vs with delegate - if delegate := route.Delegate; delegate != nil { - delegateNamespace := delegate.Namespace - if delegateNamespace == "" { - delegateNamespace = root.Namespace - } - delegateConfigKey := ConfigKey{Kind: gvk.VirtualService, Name: delegate.Name, Namespace: delegateNamespace} - delegatesByRoot[rootConfigKey] = append(delegatesByRoot[rootConfigKey], delegateConfigKey) - delegateVS, ok := delegatesMap[key(delegate.Name, delegateNamespace)] - if !ok { - log.Debugf("delegate virtual service %s/%s of %s/%s not found", - delegateNamespace, delegate.Name, root.Namespace, root.Name) - // delegate not found, ignore only the current HTTP route - continue - } - // make sure that the delegate is visible to root virtual service's namespace - exportTo := delegatesExportToMap[key(delegate.Name, delegateNamespace)] - if !exportTo[visibility.Public] && !exportTo[visibility.Instance(root.Namespace)] { - log.Debugf("delegate virtual service %s/%s of %s/%s is not exported to %s", - delegateNamespace, delegate.Name, root.Namespace, root.Name, root.Namespace) - continue - } - // DeepCopy to prevent mutate the original delegate, it can conflict - // when multiple routes delegate to one single VS. - copiedDelegate := delegateVS.DeepCopy() - vs := copiedDelegate.Spec.(*networking.VirtualService) - merged := mergeHTTPRoutes(route, vs.Http) - mergedRoutes = append(mergedRoutes, merged...) - } else { - mergedRoutes = append(mergedRoutes, route) - } - } - rootVs.Http = mergedRoutes - if log.DebugEnabled() { - jsonm := &jsonpb.Marshaler{Indent: " "} - vsString, _ := jsonm.MarshalToString(rootVs) - log.Debugf("merged virtualService: %s", vsString) - } - out = append(out, root) - } - - return out, delegatesByRoot -} - -// merge root's route with delegate's and the merged route number equals the delegate's. -// if there is a conflict with root, the route is ignored -func mergeHTTPRoutes(root *networking.HTTPRoute, delegate []*networking.HTTPRoute) []*networking.HTTPRoute { - root.Delegate = nil - - out := make([]*networking.HTTPRoute, 0, len(delegate)) - for _, subRoute := range delegate { - merged := mergeHTTPRoute(root, subRoute) - if merged != nil { - out = append(out, merged) - } - } - return out -} - -// merge the two HTTPRoutes, if there is a conflict with root, the delegate route is ignored -func mergeHTTPRoute(root *networking.HTTPRoute, delegate *networking.HTTPRoute) *networking.HTTPRoute { - // suppose there are N1 match conditions in root, N2 match conditions in delegate - // if match condition of N2 is a subset of anyone in N1, this is a valid matching conditions - merged, conflict := mergeHTTPMatchRequests(root.Match, delegate.Match) - if conflict { - log.Debugf("HTTPMatchRequests conflict: root route %s, delegate route %s", root.Name, delegate.Name) - return nil - } - delegate.Match = merged - - if delegate.Name == "" { - delegate.Name = root.Name - } else if root.Name != "" { - delegate.Name = root.Name + "-" + delegate.Name - } - if delegate.Rewrite == nil { - delegate.Rewrite = root.Rewrite - } - if delegate.Timeout == nil { - delegate.Timeout = root.Timeout - } - if delegate.Retries == nil { - delegate.Retries = root.Retries - } - if delegate.Fault == nil { - delegate.Fault = root.Fault - } - if delegate.Mirror == nil { - delegate.Mirror = root.Mirror - } - // nolint: staticcheck - if delegate.MirrorPercent == nil { - delegate.MirrorPercent = root.MirrorPercent - } - if delegate.MirrorPercentage == nil { - delegate.MirrorPercentage = root.MirrorPercentage - } - if delegate.CorsPolicy == nil { - delegate.CorsPolicy = root.CorsPolicy - } - if delegate.Headers == nil { - delegate.Headers = root.Headers - } - return delegate -} - -// return merged match conditions if not conflicts -func mergeHTTPMatchRequests(root, delegate []*networking.HTTPMatchRequest) (out []*networking.HTTPMatchRequest, conflict bool) { - if len(root) == 0 { - return delegate, false - } - - if len(delegate) == 0 { - return root, false - } - - // each HTTPMatchRequest of delegate must find a superset in root. - // otherwise it conflicts - for _, subMatch := range delegate { - foundMatch := false - for _, rootMatch := range root { - if hasConflict(rootMatch, subMatch) { - log.Debugf("HTTPMatchRequests conflict: root %v, delegate %v", rootMatch, subMatch) - continue - } - // merge HTTPMatchRequest - out = append(out, mergeHTTPMatchRequest(rootMatch, subMatch)) - foundMatch = true - } - if !foundMatch { - return nil, true - } - } - if len(out) == 0 { - conflict = true - } - return -} - -func mergeHTTPMatchRequest(root, delegate *networking.HTTPMatchRequest) *networking.HTTPMatchRequest { - out := proto.Clone(delegate).(*networking.HTTPMatchRequest) - if out.Name == "" { - out.Name = root.Name - } else if root.Name != "" { - out.Name = root.Name + "-" + out.Name - } - if out.Uri == nil { - out.Uri = root.Uri - } - if out.Scheme == nil { - out.Scheme = root.Scheme - } - if out.Method == nil { - out.Method = root.Method - } - if out.Authority == nil { - out.Authority = root.Authority - } - // headers - if len(root.Headers) > 0 || len(delegate.Headers) > 0 { - out.Headers = make(map[string]*networking.StringMatch) - } - for k, v := range root.Headers { - out.Headers[k] = v - } - for k, v := range delegate.Headers { - out.Headers[k] = v - } - // withoutheaders - if len(root.WithoutHeaders) > 0 || len(delegate.WithoutHeaders) > 0 { - out.WithoutHeaders = make(map[string]*networking.StringMatch) - } - for k, v := range root.WithoutHeaders { - out.WithoutHeaders[k] = v - } - for k, v := range delegate.WithoutHeaders { - out.WithoutHeaders[k] = v - } - // queryparams - if len(root.QueryParams) > 0 || len(delegate.QueryParams) > 0 { - out.QueryParams = make(map[string]*networking.StringMatch) - } - for k, v := range root.QueryParams { - out.QueryParams[k] = v - } - for k, v := range delegate.QueryParams { - out.QueryParams[k] = v - } - - if out.Port == 0 { - out.Port = root.Port - } - - // SourceLabels - if len(root.SourceLabels) > 0 || len(delegate.SourceLabels) > 0 { - out.SourceLabels = make(map[string]string) - } - for k, v := range root.SourceLabels { - out.SourceLabels[k] = v - } - for k, v := range delegate.SourceLabels { - out.SourceLabels[k] = v - } - - if out.SourceNamespace == "" { - out.SourceNamespace = root.SourceNamespace - } - - if len(out.Gateways) == 0 { - out.Gateways = root.Gateways - } - - return out -} - -func hasConflict(root, leaf *networking.HTTPMatchRequest) bool { - roots := []*networking.StringMatch{root.Uri, root.Scheme, root.Method, root.Authority} - leaves := []*networking.StringMatch{leaf.Uri, leaf.Scheme, leaf.Method, leaf.Authority} - for i := range roots { - if stringMatchConflict(roots[i], leaves[i]) { - return true - } - } - // header conflicts - for key, leafHeader := range leaf.Headers { - if stringMatchConflict(root.Headers[key], leafHeader) { - return true - } - } - - // without headers - for key, leafValue := range leaf.WithoutHeaders { - if stringMatchConflict(root.WithoutHeaders[key], leafValue) { - return true - } - } - - // query params conflict - for key, value := range leaf.QueryParams { - if stringMatchConflict(root.QueryParams[key], value) { - return true - } - } - - if root.IgnoreUriCase != leaf.IgnoreUriCase { - return true - } - if root.Port > 0 && leaf.Port > 0 && root.Port != leaf.Port { - return true - } - - // sourceNamespace - if root.SourceNamespace != "" && leaf.SourceNamespace != root.SourceNamespace { - return true - } - - // sourceLabels should not conflict, root should have superset of sourceLabels. - for key, leafValue := range leaf.SourceLabels { - if v, ok := root.SourceLabels[key]; ok && v != leafValue { - return true - } - } - - // gateways should not conflict, root should have superset of gateways. - if len(root.Gateways) > 0 && len(leaf.Gateways) > 0 { - if len(root.Gateways) < len(leaf.Gateways) { - return true - } - rootGateway := sets.New(root.Gateways...) - for _, gw := range leaf.Gateways { - if !rootGateway.Contains(gw) { - return true - } - } - } - - return false -} - -func stringMatchConflict(root, leaf *networking.StringMatch) bool { - // no conflict when root or leaf is not specified - if root == nil || leaf == nil { - return false - } - // regex match is not allowed - if root.GetRegex() != "" || leaf.GetRegex() != "" { - return true - } - // root is exact match - if exact := root.GetExact(); exact != "" { - // leaf is prefix match, conflict - if leaf.GetPrefix() != "" { - return true - } - // both exact, but not equal - if leaf.GetExact() != exact { - return true - } - return false - } - // root is prefix match - if prefix := root.GetPrefix(); prefix != "" { - // leaf is prefix match - if leaf.GetPrefix() != "" { - // leaf(`/a`) is not subset of root(`/a/b`) - return !strings.HasPrefix(leaf.GetPrefix(), prefix) - } - // leaf is exact match - if leaf.GetExact() != "" { - // leaf(`/a`) is not subset of root(`/a/b`) - return !strings.HasPrefix(leaf.GetExact(), prefix) - } - } - - return true -} - -func isRootVs(vs *networking.VirtualService) bool { - for _, route := range vs.Http { - // it is root vs with delegate - if route.Delegate != nil { - return true - } - } - return false -} - -// UseIngressSemantics determines which logic we should use for VirtualService -// This allows ingress and VS to both be represented by VirtualService, but have different -// semantics. -func UseIngressSemantics(cfg config.Config) bool { - return cfg.Annotations[constants.InternalRouteSemantics] == constants.RouteSemanticsIngress -} - -// UseGatewaySemantics determines which logic we should use for VirtualService -// This allows gateway-api and VS to both be represented by VirtualService, but have different -// semantics. -func UseGatewaySemantics(cfg config.Config) bool { - return cfg.Annotations[constants.InternalRouteSemantics] == constants.RouteSemanticsGateway -} diff --git a/pilot/pkg/model/virtualservice_test.go b/pilot/pkg/model/virtualservice_test.go deleted file mode 100644 index cd272d185..000000000 --- a/pilot/pkg/model/virtualservice_test.go +++ /dev/null @@ -1,2084 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "fmt" - "testing" - "time" -) - -import ( - "github.com/golang/protobuf/ptypes/wrappers" - fuzz "github.com/google/gofuzz" - "google.golang.org/protobuf/types/known/durationpb" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/visibility" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -const wildcardIP = "0.0.0.0" - -func TestMergeVirtualServices(t *testing.T) { - independentVs := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "virtual-service", - Namespace: "default", - }, - Spec: &networking.VirtualService{ - Hosts: []string{"example.org"}, - Gateways: []string{"gateway"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "example.org", - Port: &networking.PortSelector{ - Number: 80, - }, - }, - }, - }, - }, - }, - }, - } - - rootVs := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "root-vs", - Namespace: "dubbo-system", - }, - Spec: &networking.VirtualService{ - Hosts: []string{"*.org"}, - Gateways: []string{"gateway"}, - Http: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"}, - }, - }, - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/login"}, - }, - }, - }, - Delegate: &networking.Delegate{ - Name: "productpage-vs", - Namespace: "default", - }, - }, - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "example.org", - Port: &networking.PortSelector{ - Number: 80, - }, - }, - }, - }, - }, - }, - }, - } - - defaultVs := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "default-vs", - Namespace: "default", - }, - Spec: &networking.VirtualService{ - Hosts: []string{"*.org"}, - Gateways: []string{"gateway"}, - Http: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"}, - }, - }, - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/login"}, - }, - }, - }, - Delegate: &networking.Delegate{ - Name: "productpage-vs", - }, - }, - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "example.org", - Port: &networking.PortSelector{ - Number: 80, - }, - }, - }, - }, - }, - }, - }, - } - - oneRoot := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "root-vs", - Namespace: "dubbo-system", - }, - Spec: &networking.VirtualService{ - Hosts: []string{"*.org"}, - Gateways: []string{"gateway"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "example.org", - Port: &networking.PortSelector{ - Number: 80, - }, - }, - }, - }, - }, - }, - }, - } - - createDelegateVs := func(name, namespace string, exportTo []string) config.Config { - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: name, - Namespace: namespace, - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"gateway"}, - ExportTo: exportTo, - Http: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v1"}, - }, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v1", - }, - }, - }, - }, - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"}, - }, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v2", - }, - }, - }, - }, - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v3", - }, - }, - }, - }, - }, - }, - } - } - - delegateVs := createDelegateVs("productpage-vs", "default", []string{"dubbo-system"}) - delegateVsExportedToAll := createDelegateVs("productpage-vs", "default", []string{}) - - delegateVsNotExported := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "productpage-vs", - Namespace: "default2", - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"gateway"}, - ExportTo: []string{"."}, - }, - } - - mergedVs := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "root-vs", - Namespace: "dubbo-system", - }, - Spec: &networking.VirtualService{ - Hosts: []string{"*.org"}, - Gateways: []string{"gateway"}, - Http: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v1"}, - }, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v1", - }, - }, - }, - }, - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"}, - }, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v2", - }, - }, - }, - }, - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"}, - }, - }, - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/login"}, - }, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v3", - }, - }, - }, - }, - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "example.org", - Port: &networking.PortSelector{ - Number: 80, - }, - }, - }, - }, - }, - }, - }, - } - - mergedVsInDefault := mergedVs.DeepCopy() - mergedVsInDefault.Name = "default-vs" - mergedVsInDefault.Namespace = "default" - - // invalid delegate, match condition conflicts with root - delegateVs2 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "productpage-vs", - Namespace: "default", - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"gateway"}, - Http: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v1"}, - }, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v1", - }, - }, - }, - }, - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"}, - }, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v2", - }, - }, - }, - }, - { - // mismatch, this route will be ignored - Match: []*networking.HTTPMatchRequest{ - { - Name: "mismatch", - Uri: &networking.StringMatch{ - // conflicts with root's HTTPMatchRequest - MatchType: &networking.StringMatch_Prefix{Prefix: "/mis-match/path"}, - }, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v3", - }, - }, - }, - }, - }, - }, - } - - mergedVs2 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "root-vs", - Namespace: "dubbo-system", - }, - Spec: &networking.VirtualService{ - Hosts: []string{"*.org"}, - Gateways: []string{"gateway"}, - Http: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v1"}, - }, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v1", - }, - }, - }, - }, - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"}, - }, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v2", - }, - }, - }, - }, - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "example.org", - Port: &networking.PortSelector{ - Number: 80, - }, - }, - }, - }, - }, - }, - }, - } - - // multiple routes delegate to one single sub VS - multiRoutes := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "root-vs", - Namespace: "dubbo-system", - }, - Spec: &networking.VirtualService{ - Hosts: []string{"*.org"}, - Gateways: []string{"gateway"}, - Http: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"}, - }, - }, - }, - Delegate: &networking.Delegate{ - Name: "productpage-vs", - Namespace: "default", - }, - }, - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/legacy/path"}, - }, - }, - }, - Rewrite: &networking.HTTPRewrite{ - Uri: "/productpage", - }, - Delegate: &networking.Delegate{ - Name: "productpage-vs", - Namespace: "default", - }, - }, - }, - }, - } - - singleDelegate := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "productpage-vs", - Namespace: "default", - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"gateway"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v1", - }, - }, - }, - }, - }, - }, - } - - mergedVs3 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "root-vs", - Namespace: "dubbo-system", - }, - Spec: &networking.VirtualService{ - Hosts: []string{"*.org"}, - Gateways: []string{"gateway"}, - Http: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"}, - }, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v1", - }, - }, - }, - }, - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/legacy/path"}, - }, - }, - }, - Rewrite: &networking.HTTPRewrite{ - Uri: "/productpage", - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v1", - }, - }, - }, - }, - }, - }, - } - - cases := []struct { - name string - virtualServices []config.Config - expectedVirtualServices []config.Config - }{ - { - name: "one independent vs", - virtualServices: []config.Config{independentVs}, - expectedVirtualServices: []config.Config{independentVs}, - }, - { - name: "one root vs", - virtualServices: []config.Config{rootVs}, - expectedVirtualServices: []config.Config{oneRoot}, - }, - { - name: "one delegate vs", - virtualServices: []config.Config{delegateVs}, - expectedVirtualServices: []config.Config{}, - }, - { - name: "root and delegate vs", - virtualServices: []config.Config{rootVs.DeepCopy(), delegateVs}, - expectedVirtualServices: []config.Config{mergedVs}, - }, - { - name: "root and conflicted delegate vs", - virtualServices: []config.Config{rootVs.DeepCopy(), delegateVs2}, - expectedVirtualServices: []config.Config{mergedVs2}, - }, - { - name: "multiple routes delegate to one", - virtualServices: []config.Config{multiRoutes.DeepCopy(), singleDelegate}, - expectedVirtualServices: []config.Config{mergedVs3}, - }, - { - name: "root not specify delegate namespace", - virtualServices: []config.Config{defaultVs.DeepCopy(), delegateVsExportedToAll}, - expectedVirtualServices: []config.Config{mergedVsInDefault}, - }, - { - name: "delegate not exported to root vs namespace", - virtualServices: []config.Config{rootVs, delegateVsNotExported}, - expectedVirtualServices: []config.Config{oneRoot}, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - got, _ := mergeVirtualServicesIfNeeded(tc.virtualServices, map[visibility.Instance]bool{visibility.Public: true}) - assert.Equal(t, got, tc.expectedVirtualServices) - }) - } -} - -func TestMergeHttpRoutes(t *testing.T) { - cases := []struct { - name string - root *networking.HTTPRoute - delegate []*networking.HTTPRoute - expected []*networking.HTTPRoute - }{ - { - name: "root catch all", - root: &networking.HTTPRoute{ - Match: nil, // catch all - Timeout: &durationpb.Duration{Seconds: 10}, - Headers: &networking.Headers{ - Request: &networking.Headers_HeaderOperations{ - Add: map[string]string{ - "istio": "test", - }, - Remove: []string{"trace-id"}, - }, - }, - Delegate: &networking.Delegate{ - Name: "delegate", - Namespace: "default", - }, - }, - delegate: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v1"}, - }, - Headers: map[string]*networking.StringMatch{ - "version": { - MatchType: &networking.StringMatch_Exact{Exact: "v1"}, - }, - }, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v1", - }, - }, - }, - }, - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"}, - }, - Headers: map[string]*networking.StringMatch{ - "version": { - MatchType: &networking.StringMatch_Exact{Exact: "v2"}, - }, - }, - }, - { - Headers: map[string]*networking.StringMatch{ - "version": { - MatchType: &networking.StringMatch_Exact{Exact: "v2"}, - }, - }, - QueryParams: map[string]*networking.StringMatch{ - "version": { - MatchType: &networking.StringMatch_Exact{Exact: "v2"}, - }, - }, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v2", - }, - }, - }, - }, - }, - expected: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v1"}, - }, - Headers: map[string]*networking.StringMatch{ - "version": { - MatchType: &networking.StringMatch_Exact{Exact: "v1"}, - }, - }, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v1", - }, - }, - }, - Timeout: &durationpb.Duration{Seconds: 10}, - Headers: &networking.Headers{ - Request: &networking.Headers_HeaderOperations{ - Add: map[string]string{ - "istio": "test", - }, - Remove: []string{"trace-id"}, - }, - }, - }, - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"}, - }, - Headers: map[string]*networking.StringMatch{ - "version": { - MatchType: &networking.StringMatch_Exact{Exact: "v2"}, - }, - }, - }, - { - Headers: map[string]*networking.StringMatch{ - "version": { - MatchType: &networking.StringMatch_Exact{Exact: "v2"}, - }, - }, - QueryParams: map[string]*networking.StringMatch{ - "version": { - MatchType: &networking.StringMatch_Exact{Exact: "v2"}, - }, - }, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v2", - }, - }, - }, - Timeout: &durationpb.Duration{Seconds: 10}, - Headers: &networking.Headers{ - Request: &networking.Headers_HeaderOperations{ - Add: map[string]string{ - "istio": "test", - }, - Remove: []string{"trace-id"}, - }, - }, - }, - }, - }, - { - name: "delegate with empty match", - root: &networking.HTTPRoute{ - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"}, - }, - Port: 8080, - }, - }, - Delegate: &networking.Delegate{ - Name: "delegate", - Namespace: "default", - }, - }, - delegate: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v1"}, - }, - Headers: map[string]*networking.StringMatch{ - "version": { - MatchType: &networking.StringMatch_Exact{Exact: "v1"}, - }, - }, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v1", - }, - }, - }, - }, - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"}, - }, - Headers: map[string]*networking.StringMatch{ - "version": { - MatchType: &networking.StringMatch_Exact{Exact: "v2"}, - }, - }, - }, - { - Headers: map[string]*networking.StringMatch{ - "version": { - MatchType: &networking.StringMatch_Exact{Exact: "v2"}, - }, - }, - QueryParams: map[string]*networking.StringMatch{ - "version": { - MatchType: &networking.StringMatch_Exact{Exact: "v2"}, - }, - }, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v2", - }, - }, - }, - }, - { - // default route to v3 - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v3", - }, - }, - }, - }, - }, - expected: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v1"}, - }, - Headers: map[string]*networking.StringMatch{ - "version": { - MatchType: &networking.StringMatch_Exact{Exact: "v1"}, - }, - }, - Port: 8080, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v1", - }, - }, - }, - }, - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"}, - }, - Headers: map[string]*networking.StringMatch{ - "version": { - MatchType: &networking.StringMatch_Exact{Exact: "v2"}, - }, - }, - Port: 8080, - }, - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"}, - }, - Headers: map[string]*networking.StringMatch{ - "version": { - MatchType: &networking.StringMatch_Exact{Exact: "v2"}, - }, - }, - QueryParams: map[string]*networking.StringMatch{ - "version": { - MatchType: &networking.StringMatch_Exact{Exact: "v2"}, - }, - }, - Port: 8080, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v2", - }, - }, - }, - }, - { - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"}, - }, - Port: 8080, - }, - }, - // default route to v3 - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "productpage.org", - Port: &networking.PortSelector{ - Number: 80, - }, - Subset: "v3", - }, - }, - }, - }, - }, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - got := mergeHTTPRoutes(tc.root, tc.delegate) - assert.Equal(t, got, tc.expected) - }) - } -} - -func TestMergeHTTPMatchRequests(t *testing.T) { - cases := []struct { - name string - root []*networking.HTTPMatchRequest - delegate []*networking.HTTPMatchRequest - expected []*networking.HTTPMatchRequest - }{ - { - name: "url match", - root: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"}, - }, - }, - }, - delegate: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"}, - }, - }, - }, - expected: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"}, - }, - }, - }, - }, - { - name: "multi url match", - root: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"}, - }, - }, - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/reviews"}, - }, - }, - }, - delegate: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"}, - }, - }, - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/reviews/v1"}, - }, - }, - }, - expected: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"}, - }, - }, - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/reviews/v1"}, - }, - }, - }, - }, - { - name: "url mismatch", - root: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"}, - }, - }, - }, - delegate: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/reviews"}, - }, - }, - }, - expected: nil, - }, - { - name: "url match", - root: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"}, - }, - }, - }, - delegate: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"}, - }, - }, - { - Uri: &networking.StringMatch{ - // conflicts - MatchType: &networking.StringMatch_Exact{Exact: "/reviews"}, - }, - }, - }, - expected: nil, - }, - { - name: "headers", - root: []*networking.HTTPMatchRequest{ - { - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Exact{Exact: "h1"}, - }, - }, - }, - }, - delegate: []*networking.HTTPMatchRequest{}, - expected: []*networking.HTTPMatchRequest{ - { - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Exact{Exact: "h1"}, - }, - }, - }, - }, - }, - { - name: "headers", - root: []*networking.HTTPMatchRequest{ - { - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Exact{Exact: "h1"}, - }, - }, - }, - }, - delegate: []*networking.HTTPMatchRequest{ - { - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Exact{Exact: "h2"}, - }, - }, - }, - }, - expected: nil, - }, - { - name: "headers", - root: []*networking.HTTPMatchRequest{ - { - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Exact{Exact: "h1"}, - }, - }, - }, - }, - delegate: []*networking.HTTPMatchRequest{ - { - Headers: map[string]*networking.StringMatch{ - "header-2": { - MatchType: &networking.StringMatch_Exact{Exact: "h2"}, - }, - }, - }, - }, - expected: []*networking.HTTPMatchRequest{ - { - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Exact{Exact: "h1"}, - }, - "header-2": { - MatchType: &networking.StringMatch_Exact{Exact: "h2"}, - }, - }, - }, - }, - }, - { - name: "complicated merge", - root: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"}, - }, - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Exact{Exact: "h1"}, - }, - }, - Port: 8080, - Authority: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "productpage.com"}, - }, - }, - }, - delegate: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"}, - }, - }, - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v2"}, - }, - }, - }, - expected: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"}, - }, - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Exact{Exact: "h1"}, - }, - }, - Port: 8080, - Authority: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "productpage.com"}, - }, - }, - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v2"}, - }, - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Exact{Exact: "h1"}, - }, - }, - Port: 8080, - Authority: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "productpage.com"}, - }, - }, - }, - }, - { - name: "conflicted merge", - root: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"}, - }, - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Exact{Exact: "h1"}, - }, - }, - Port: 8080, - Authority: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "productpage.com"}, - }, - }, - }, - delegate: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"}, - }, - }, - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v2"}, - }, - Port: 9090, // conflicts - }, - }, - expected: nil, - }, - { - name: "gateway merge", - root: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"}, - }, - Gateways: []string{"ingress-gateway", "mesh"}, - }, - }, - delegate: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"}, - }, - Gateways: []string{"ingress-gateway"}, - }, - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v2"}, - }, - Gateways: []string{"mesh"}, - }, - }, - expected: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"}, - }, - Gateways: []string{"ingress-gateway"}, - }, - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v2"}, - }, - Gateways: []string{"mesh"}, - }, - }, - }, - { - name: "gateway conflicted merge", - root: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"}, - }, - Gateways: []string{"ingress-gateway"}, - }, - }, - delegate: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v1"}, - }, - Gateways: []string{"ingress-gateway"}, - }, - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v2"}, - }, - Gateways: []string{"mesh"}, - }, - }, - expected: nil, - }, - { - name: "source labels merge", - root: []*networking.HTTPMatchRequest{ - { - SourceLabels: map[string]string{"app": "test"}, - }, - }, - delegate: []*networking.HTTPMatchRequest{ - { - SourceLabels: map[string]string{"version": "v1"}, - }, - }, - expected: []*networking.HTTPMatchRequest{ - { - SourceLabels: map[string]string{"app": "test", "version": "v1"}, - }, - }, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - got, _ := mergeHTTPMatchRequests(tc.root, tc.delegate) - assert.Equal(t, got, tc.expected) - }) - } -} - -func TestHasConflict(t *testing.T) { - cases := []struct { - name string - root *networking.HTTPMatchRequest - leaf *networking.HTTPMatchRequest - expected bool - }{ - { - name: "regex uri", - root: &networking.HTTPMatchRequest{ - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Regex{Regex: "^/productpage"}, - }, - }, - leaf: &networking.HTTPMatchRequest{ - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"}, - }, - }, - expected: true, - }, - { - name: "match uri", - root: &networking.HTTPMatchRequest{ - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"}, - }, - }, - leaf: &networking.HTTPMatchRequest{ - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"}, - }, - }, - expected: false, - }, - { - name: "mismatch uri", - root: &networking.HTTPMatchRequest{ - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"}, - }, - }, - leaf: &networking.HTTPMatchRequest{ - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage"}, - }, - }, - expected: true, - }, - { - name: "match uri", - root: &networking.HTTPMatchRequest{ - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/productpage/v2"}, - }, - }, - leaf: &networking.HTTPMatchRequest{ - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/productpage/v2"}, - }, - }, - expected: false, - }, - { - name: "headers not equal", - root: &networking.HTTPMatchRequest{ - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Exact{Exact: "h1"}, - }, - }, - }, - leaf: &networking.HTTPMatchRequest{ - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Exact{Exact: "h2"}, - }, - }, - }, - expected: true, - }, - { - name: "headers equal", - root: &networking.HTTPMatchRequest{ - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Exact{Exact: "h1"}, - }, - }, - }, - leaf: &networking.HTTPMatchRequest{ - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Exact{Exact: "h1"}, - }, - }, - }, - expected: false, - }, - { - name: "headers match", - root: &networking.HTTPMatchRequest{ - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Prefix{Prefix: "h1"}, - }, - }, - }, - leaf: &networking.HTTPMatchRequest{ - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Exact{Exact: "h1-v1"}, - }, - }, - }, - expected: false, - }, - { - name: "headers mismatch", - root: &networking.HTTPMatchRequest{ - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Prefix{Prefix: "h1"}, - }, - }, - }, - leaf: &networking.HTTPMatchRequest{ - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Exact{Exact: "h2"}, - }, - }, - }, - expected: true, - }, - { - name: "headers prefix mismatch", - root: &networking.HTTPMatchRequest{ - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Prefix{Prefix: "h1"}, - }, - }, - }, - leaf: &networking.HTTPMatchRequest{ - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Prefix{Prefix: "h2"}, - }, - }, - }, - expected: true, - }, - { - name: "diff headers", - root: &networking.HTTPMatchRequest{ - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Exact{Exact: "h1"}, - }, - }, - }, - leaf: &networking.HTTPMatchRequest{ - Headers: map[string]*networking.StringMatch{ - "header-2": { - MatchType: &networking.StringMatch_Exact{Exact: "h2"}, - }, - }, - }, - expected: false, - }, - { - name: "withoutHeaders mismatch", - root: &networking.HTTPMatchRequest{ - WithoutHeaders: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Prefix{Prefix: "h1"}, - }, - }, - }, - leaf: &networking.HTTPMatchRequest{ - WithoutHeaders: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Exact{Exact: "h2"}, - }, - }, - }, - expected: true, - }, - { - name: "port", - root: &networking.HTTPMatchRequest{ - Port: 0, - }, - leaf: &networking.HTTPMatchRequest{ - Port: 8080, - }, - expected: false, - }, - { - name: "port", - root: &networking.HTTPMatchRequest{ - Port: 8080, - }, - leaf: &networking.HTTPMatchRequest{ - Port: 0, - }, - expected: false, - }, - { - name: "port", - root: &networking.HTTPMatchRequest{ - Port: 8080, - }, - leaf: &networking.HTTPMatchRequest{ - Port: 8090, - }, - expected: true, - }, - { - name: "sourceLabels mismatch", - root: &networking.HTTPMatchRequest{ - SourceLabels: map[string]string{"a": "b"}, - }, - leaf: &networking.HTTPMatchRequest{ - SourceLabels: map[string]string{"a": "c"}, - }, - expected: true, - }, - { - name: "sourceNamespace mismatch", - root: &networking.HTTPMatchRequest{ - SourceNamespace: "test1", - }, - leaf: &networking.HTTPMatchRequest{ - SourceNamespace: "test2", - }, - expected: true, - }, - { - name: "root has less gateways than delegate", - root: &networking.HTTPMatchRequest{ - Gateways: []string{"ingress-gateway"}, - }, - leaf: &networking.HTTPMatchRequest{ - Gateways: []string{"ingress-gateway", "mesh"}, - }, - expected: true, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - got := hasConflict(tc.root, tc.leaf) - if got != tc.expected { - t.Errorf("got %v, expected %v", got, tc.expected) - } - }) - } -} - -// Note: this is to prevent missing merge new added HTTPRoute fields -func TestFuzzMergeHttpRoute(t *testing.T) { - f := fuzz.New().NilChance(0.5).NumElements(0, 1).Funcs( - func(r *networking.HTTPRoute, c fuzz.Continue) { - c.FuzzNoCustom(r) - r.Match = []*networking.HTTPMatchRequest{{}} - r.Route = nil - r.Redirect = nil - r.Delegate = nil - }, - func(r *networking.HTTPMatchRequest, c fuzz.Continue) { - *r = networking.HTTPMatchRequest{} - }, - func(r *networking.HTTPRouteDestination, c fuzz.Continue) { - *r = networking.HTTPRouteDestination{} - }, - func(r *networking.HTTPRedirect, c fuzz.Continue) { - *r = networking.HTTPRedirect{} - }, - func(r *networking.Delegate, c fuzz.Continue) { - *r = networking.Delegate{} - }, - - func(r *networking.HTTPRewrite, c fuzz.Continue) { - *r = networking.HTTPRewrite{} - }, - - func(r *durationpb.Duration, c fuzz.Continue) { - *r = durationpb.Duration{} - }, - func(r *networking.HTTPRetry, c fuzz.Continue) { - *r = networking.HTTPRetry{} - }, - func(r *networking.HTTPFaultInjection, c fuzz.Continue) { - *r = networking.HTTPFaultInjection{} - }, - func(r *networking.Destination, c fuzz.Continue) { - *r = networking.Destination{} - }, - func(r *wrappers.UInt32Value, c fuzz.Continue) { - *r = wrappers.UInt32Value{} - }, - func(r *networking.Percent, c fuzz.Continue) { - *r = networking.Percent{} - }, - func(r *networking.CorsPolicy, c fuzz.Continue) { - *r = networking.CorsPolicy{} - }, - func(r *networking.Headers, c fuzz.Continue) { - *r = networking.Headers{} - }) - - root := &networking.HTTPRoute{} - f.Fuzz(root) - - delegate := &networking.HTTPRoute{} - expected := mergeHTTPRoute(root, delegate) - assert.Equal(t, expected, root) -} - -// Note: this is to prevent missing merge new added HTTPMatchRequest fields -func TestFuzzMergeHttpMatchRequest(t *testing.T) { - f := fuzz.New().NilChance(0.5).NumElements(1, 1).Funcs( - func(r *networking.StringMatch, c fuzz.Continue) { - *r = networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{ - Exact: "fuzz", - }, - } - }, - func(m *map[string]*networking.StringMatch, c fuzz.Continue) { - *m = map[string]*networking.StringMatch{ - "test": nil, - } - }, - func(m *map[string]string, c fuzz.Continue) { - *m = map[string]string{"test": "fuzz"} - }) - - root := &networking.HTTPMatchRequest{} - f.Fuzz(root) - root.SourceNamespace = "" - root.SourceLabels = nil - root.Gateways = nil - root.IgnoreUriCase = false - delegate := &networking.HTTPMatchRequest{} - merged := mergeHTTPMatchRequest(root, delegate) - - assert.Equal(t, merged, root) -} - -var gatewayNameTests = []struct { - gateway string - namespace string - resolved string -}{ - { - "./gateway", - "default", - "default/gateway", - }, - { - "gateway", - "default", - "default/gateway", - }, - { - "default/gateway", - "foo", - "default/gateway", - }, - { - "gateway.default", - "default", - "default/gateway", - }, - { - "gateway.default", - "foo", - "default/gateway", - }, - { - "private.ingress.svc.cluster.local", - "foo", - "ingress/private", - }, -} - -func TestResolveGatewayName(t *testing.T) { - for _, tt := range gatewayNameTests { - t.Run(fmt.Sprintf("%s-%s", tt.gateway, tt.namespace), func(t *testing.T) { - if got := resolveGatewayName(tt.gateway, config.Meta{Namespace: tt.namespace}); got != tt.resolved { - t.Fatalf("expected %q got %q", tt.resolved, got) - } - }) - } -} - -func BenchmarkResolveGatewayName(b *testing.B) { - for i := 0; i < b.N; i++ { - for _, tt := range gatewayNameTests { - _ = resolveGatewayName(tt.gateway, config.Meta{Namespace: tt.namespace}) - } - } -} - -func TestSelectVirtualService(t *testing.T) { - services := []*Service{ - buildHTTPService("bookinfo.com", visibility.Public, wildcardIP, "default", 9999, 70), - buildHTTPService("private.com", visibility.Private, wildcardIP, "default", 9999, 80), - buildHTTPService("test.com", visibility.Public, "8.8.8.8", "not-default", 8080), - buildHTTPService("test-private.com", visibility.Private, "9.9.9.9", "not-default", 80, 70), - buildHTTPService("test-private-2.com", visibility.Private, "9.9.9.10", "not-default", 60), - buildHTTPService("test-headless.com", visibility.Public, wildcardIP, "not-default", 8888), - buildHTTPService("test-headless-someother.com", visibility.Public, wildcardIP, "some-other-ns", 8888), - } - - hostsByNamespace := make(map[string][]host.Name) - for _, svc := range services { - hostsByNamespace[svc.Attributes.Namespace] = append(hostsByNamespace[svc.Attributes.Namespace], svc.Hostname) - } - - virtualServiceSpec1 := &networking.VirtualService{ - Hosts: []string{"test-private-2.com"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - // Subset: "some-subset", - Host: "example.org", - Port: &networking.PortSelector{ - Number: 61, - }, - }, - Weight: 100, - }, - }, - }, - }, - } - virtualServiceSpec2 := &networking.VirtualService{ - Hosts: []string{"test-private-2.com"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "test.org", - Port: &networking.PortSelector{ - Number: 62, - }, - }, - Weight: 100, - }, - }, - }, - }, - } - virtualServiceSpec3 := &networking.VirtualService{ - Hosts: []string{"test-private-3.com"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "test.org", - Port: &networking.PortSelector{ - Number: 63, - }, - }, - Weight: 100, - }, - }, - }, - }, - } - virtualServiceSpec4 := &networking.VirtualService{ - Hosts: []string{"test-headless.com", "example.com"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "test.org", - Port: &networking.PortSelector{ - Number: 64, - }, - }, - Weight: 100, - }, - }, - }, - }, - } - virtualServiceSpec5 := &networking.VirtualService{ - Hosts: []string{"test-svc.testns.svc.cluster.local"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "test-svc.testn.svc.cluster.local", - }, - Weight: 100, - }, - }, - }, - }, - } - virtualServiceSpec6 := &networking.VirtualService{ - Hosts: []string{"match-no-service"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "non-exist-service", - }, - Weight: 100, - }, - }, - }, - }, - } - virtualServiceSpec7 := &networking.VirtualService{ - Hosts: []string{"test-headless-someother.com"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "test.org", - Port: &networking.PortSelector{ - Number: 64, - }, - }, - Weight: 100, - }, - }, - }, - }, - } - virtualService1 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "acme2-v1", - Namespace: "not-default", - }, - Spec: virtualServiceSpec1, - } - virtualService2 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "acme-v2", - Namespace: "not-default", - }, - Spec: virtualServiceSpec2, - } - virtualService3 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "acme-v3", - Namespace: "not-default", - }, - Spec: virtualServiceSpec3, - } - virtualService4 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "acme-v4", - Namespace: "not-default", - }, - Spec: virtualServiceSpec4, - } - virtualService5 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "acme-v3", - Namespace: "not-default", - }, - Spec: virtualServiceSpec5, - } - virtualService6 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "acme-v3", - Namespace: "not-default", - }, - Spec: virtualServiceSpec6, - } - virtualService7 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "acme2-v1", - Namespace: "some-other-ns", - }, - Spec: virtualServiceSpec7, - } - configs := SelectVirtualServices( - []config.Config{virtualService1, virtualService2, virtualService3, virtualService4, virtualService5, virtualService6, virtualService7}, - hostsByNamespace) - expectedVS := []string{virtualService1.Name, virtualService2.Name, virtualService4.Name, virtualService7.Name} - if len(expectedVS) != len(configs) { - t.Fatalf("Unexpected virtualService, got %d, epxected %d", len(configs), len(expectedVS)) - } - for i, config := range configs { - if config.Name != expectedVS[i] { - t.Fatalf("Unexpected virtualService, got %s, epxected %s", config.Name, expectedVS[i]) - } - } -} - -func buildHTTPService(hostname string, v visibility.Instance, ip, namespace string, ports ...int) *Service { - service := &Service{ - CreationTime: time.Now(), - Hostname: host.Name(hostname), - DefaultAddress: ip, - Resolution: DNSLB, - Attributes: ServiceAttributes{ - ServiceRegistry: provider.Kubernetes, - Namespace: namespace, - ExportTo: map[visibility.Instance]bool{v: true}, - }, - } - if ip == wildcardIP { - service.Resolution = Passthrough - } - - Ports := make([]*Port, 0) - - for _, p := range ports { - Ports = append(Ports, &Port{ - Name: fmt.Sprintf("http-%d", p), - Port: p, - Protocol: protocol.HTTP, - }) - } - - service.Ports = Ports - return service -} diff --git a/pilot/pkg/model/wasm.go b/pilot/pkg/model/wasm.go deleted file mode 100644 index 3341ea57f..000000000 --- a/pilot/pkg/model/wasm.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - wasm "github.com/envoyproxy/go-control-plane/envoy/extensions/wasm/v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" -) - -// ConstructVMConfig constructs a VM config. If WASM is enabled, the wasm plugin at filename will be used. -// If not, the builtin (null vm) extension, name, will be used. -func ConstructVMConfig(filename, name string) *wasm.PluginConfig_VmConfig { - var vmConfig *wasm.PluginConfig_VmConfig - if features.EnableWasmTelemetry && filename != "" { - vmConfig = &wasm.PluginConfig_VmConfig{ - VmConfig: &wasm.VmConfig{ - Runtime: "envoy.wasm.runtime.v8", - AllowPrecompiled: true, - Code: &core.AsyncDataSource{Specifier: &core.AsyncDataSource_Local{ - Local: &core.DataSource{ - Specifier: &core.DataSource_Filename{ - Filename: filename, - }, - }, - }}, - }, - } - } else { - vmConfig = &wasm.PluginConfig_VmConfig{ - VmConfig: &wasm.VmConfig{ - Runtime: "envoy.wasm.runtime.null", - Code: &core.AsyncDataSource{Specifier: &core.AsyncDataSource_Local{ - Local: &core.DataSource{ - Specifier: &core.DataSource_InlineString{ - InlineString: name, - }, - }, - }}, - }, - } - } - return vmConfig -} diff --git a/pilot/pkg/model/xds_cache.go b/pilot/pkg/model/xds_cache.go deleted file mode 100644 index 7b5676232..000000000 --- a/pilot/pkg/model/xds_cache.go +++ /dev/null @@ -1,398 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "fmt" - "sync" - "time" -) - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "github.com/google/go-cmp/cmp" - "github.com/hashicorp/golang-lru/simplelru" - "google.golang.org/protobuf/testing/protocmp" - "istio.io/pkg/monitoring" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -func init() { - monitoring.MustRegister(xdsCacheReads) - monitoring.MustRegister(xdsCacheEvictions) - monitoring.MustRegister(xdsCacheSize) -} - -var ( - xdsCacheReads = monitoring.NewSum( - "xds_cache_reads", - "Total number of xds cache xdsCacheReads.", - monitoring.WithLabels(typeTag), - ) - - xdsCacheEvictions = monitoring.NewSum( - "xds_cache_evictions", - "Total number of xds cache evictions.", - ) - - xdsCacheSize = monitoring.NewGauge( - "xds_cache_size", - "Current size of xds cache", - ) - - xdsCacheHits = xdsCacheReads.With(typeTag.Value("hit")) - xdsCacheMisses = xdsCacheReads.With(typeTag.Value("miss")) -) - -func hit() { - if features.EnableXDSCacheMetrics { - xdsCacheHits.Increment() - } -} - -func miss() { - if features.EnableXDSCacheMetrics { - xdsCacheMisses.Increment() - } -} - -func size(cs int) { - if features.EnableXDSCacheMetrics { - xdsCacheSize.Record(float64(cs)) - } -} - -func indexConfig(configIndex map[ConfigKey]sets.Set, k string, dependentConfigs []ConfigKey) { - for _, cfg := range dependentConfigs { - if configIndex[cfg] == nil { - configIndex[cfg] = sets.New() - } - configIndex[cfg].Insert(k) - } -} - -func clearIndexConfig(configIndex map[ConfigKey]sets.Set, k string, dependentConfigs []ConfigKey) { - for _, cfg := range dependentConfigs { - index := configIndex[cfg] - if index != nil { - index.Delete(k) - if index.IsEmpty() { - delete(configIndex, cfg) - } - } - } -} - -func indexType(typeIndex map[config.GroupVersionKind]sets.Set, k string, dependentTypes []config.GroupVersionKind) { - for _, t := range dependentTypes { - if typeIndex[t] == nil { - typeIndex[t] = sets.New() - } - typeIndex[t].Insert(k) - } -} - -func clearIndexType(typeIndex map[config.GroupVersionKind]sets.Set, k string, dependentTypes []config.GroupVersionKind) { - for _, t := range dependentTypes { - index := typeIndex[t] - if index != nil { - index.Delete(k) - if index.IsEmpty() { - delete(typeIndex, t) - } - } - } -} - -// XdsCacheEntry interface defines functions that should be implemented by -// resources that can be cached. -type XdsCacheEntry interface { - // Key is the key to be used in cache. - Key() string - // DependentTypes are config types that this cache key is dependant on. - // Whenever any configs of this type changes, we should invalidate this cache entry. - // Note: DependentConfigs should be preferred wherever possible. - DependentTypes() []config.GroupVersionKind - // DependentConfigs is config items that this cache key is dependent on. - // Whenever these configs change, we should invalidate this cache entry. - DependentConfigs() []ConfigKey - // Cacheable indicates whether this entry is valid for cache. For example - // for EDS to be cacheable, the Endpoint should have corresponding service. - Cacheable() bool -} - -type CacheToken uint64 - -// XdsCache interface defines a store for caching XDS responses. -// All operations are thread safe. -type XdsCache interface { - // Add adds the given XdsCacheEntry with the value for the given pushContext to the cache. - // If the cache has been updated to a newer push context, the write will be dropped silently. - // This ensures stale data does not overwrite fresh data when dealing with concurrent - // writers. - Add(entry XdsCacheEntry, pushRequest *PushRequest, value *discovery.Resource) - // Get retrieves the cached value if it exists. The boolean indicates - // whether the entry exists in the cache. - Get(entry XdsCacheEntry) (*discovery.Resource, bool) - // Clear removes the cache entries that are dependent on the configs passed. - Clear(map[ConfigKey]struct{}) - // ClearAll clears the entire cache. - ClearAll() - // Keys returns all currently configured keys. This is for testing/debug only - Keys() []string - // Snapshot returns a snapshot of all keys and values. This is for testing/debug only - Snapshot() map[string]*discovery.Resource -} - -// NewXdsCache returns an instance of a cache. -func NewXdsCache() XdsCache { - cache := &lruCache{ - enableAssertions: features.EnableUnsafeAssertions, - configIndex: map[ConfigKey]sets.Set{}, - typesIndex: map[config.GroupVersionKind]sets.Set{}, - } - cache.store = newLru(cache.evict) - - return cache -} - -// NewLenientXdsCache returns an instance of a cache that does not validate token based get/set and enable assertions. -func NewLenientXdsCache() XdsCache { - cache := &lruCache{ - enableAssertions: false, - configIndex: map[ConfigKey]sets.Set{}, - typesIndex: map[config.GroupVersionKind]sets.Set{}, - } - cache.store = newLru(cache.evict) - - return cache -} - -type lruCache struct { - enableAssertions bool - store simplelru.LRUCache - // token stores the latest token of the store, used to prevent stale data overwrite. - // It is refreshed when Clear or ClearAll are called - token CacheToken - mu sync.RWMutex - configIndex map[ConfigKey]sets.Set - typesIndex map[config.GroupVersionKind]sets.Set -} - -var _ XdsCache = &lruCache{} - -func newLru(evictCallback simplelru.EvictCallback) simplelru.LRUCache { - sz := features.XDSCacheMaxSize - if sz <= 0 { - sz = 20000 - } - l, err := simplelru.NewLRU(sz, evictCallback) - if err != nil { - panic(fmt.Errorf("invalid lru configuration: %v", err)) - } - return l -} - -func (l *lruCache) evict(k interface{}, v interface{}) { - if features.EnableXDSCacheMetrics { - xdsCacheEvictions.Increment() - } - - key := k.(string) - value := v.(cacheValue) - - // we don't need to acquire locks, since this function is called when we write to the store - clearIndexConfig(l.configIndex, key, value.dependentConfigs) - clearIndexType(l.typesIndex, key, value.dependentTypes) -} - -// assertUnchanged checks that a cache entry is not changed. This helps catch bad cache invalidation -// We should never have a case where we overwrite an existing item with a new change. Instead, when -// config sources change, Clear/ClearAll should be called. At this point, we may get multiple writes -// because multiple writers may get cache misses concurrently, but they ought to generate identical -// configuration. This also checks that our XDS config generation is deterministic, which is a very -// important property. -func (l *lruCache) assertUnchanged(key string, existing *discovery.Resource, replacement *discovery.Resource) { - if l.enableAssertions { - if existing == nil { - // This is a new addition, not an update - return - } - // Record time so that we can correlate when the error actually happened, since the async reporting - // may be delayed - t0 := time.Now() - // This operation is really slow, which makes tests fail for unrelated reasons, so we process it async. - go func() { - if !cmp.Equal(existing, replacement, protocmp.Transform()) { - warning := fmt.Errorf("assertion failed at %v, cache entry changed but not cleared for key %v: %v\n%v\n%v", - t0, key, cmp.Diff(existing, replacement, protocmp.Transform()), existing, replacement) - panic(warning) - } - }() - } -} - -func (l *lruCache) Add(entry XdsCacheEntry, pushReq *PushRequest, value *discovery.Resource) { - if !entry.Cacheable() || pushReq == nil || pushReq.Start.Equal(time.Time{}) { - return - } - // It will not overflow until year 2262 - token := CacheToken(pushReq.Start.UnixNano()) - k := entry.Key() - l.mu.Lock() - defer l.mu.Unlock() - cur, f := l.store.Get(k) - if f { - // This is the stale resource - if token < cur.(cacheValue).token || token < l.token { - // entry may be stale, we need to drop it. This can happen when the cache is invalidated - // after we call Get. - return - } - if l.enableAssertions { - l.assertUnchanged(k, cur.(cacheValue).value, value) - } - } - - if token < l.token { - return - } - - // we have to make sure we evict old entries with the same key - // to prevent leaking in the index maps - if old, ok := l.store.Get(k); ok { - l.evict(k, old) - } - - dependentConfigs := entry.DependentConfigs() - dependentTypes := entry.DependentTypes() - toWrite := cacheValue{value: value, token: token, dependentConfigs: dependentConfigs, dependentTypes: dependentTypes} - l.store.Add(k, toWrite) - l.token = token - indexConfig(l.configIndex, k, dependentConfigs) - indexType(l.typesIndex, k, dependentTypes) - size(l.store.Len()) -} - -type cacheValue struct { - value *discovery.Resource - token CacheToken - dependentConfigs []ConfigKey - dependentTypes []config.GroupVersionKind -} - -func (l *lruCache) Get(entry XdsCacheEntry) (*discovery.Resource, bool) { - if !entry.Cacheable() { - return nil, false - } - k := entry.Key() - l.mu.Lock() - defer l.mu.Unlock() - val, ok := l.store.Get(k) - if !ok { - miss() - return nil, false - } - cv := val.(cacheValue) - if cv.value == nil { - miss() - return nil, false - } - hit() - return cv.value, true -} - -func (l *lruCache) Clear(configs map[ConfigKey]struct{}) { - l.mu.Lock() - defer l.mu.Unlock() - l.token = CacheToken(time.Now().UnixNano()) - for ckey := range configs { - referenced := l.configIndex[ckey] - delete(l.configIndex, ckey) - for key := range referenced { - l.store.Remove(key) - } - tReferenced := l.typesIndex[ckey.Kind] - delete(l.typesIndex, ckey.Kind) - for key := range tReferenced { - l.store.Remove(key) - } - } - size(l.store.Len()) -} - -func (l *lruCache) ClearAll() { - l.mu.Lock() - defer l.mu.Unlock() - l.token = CacheToken(time.Now().UnixNano()) - // Purge with an evict function would turn up to be pretty slow since - // it runs the function for every key in the store, might be better to just - // create a new store. - l.store = newLru(l.evict) - l.configIndex = map[ConfigKey]sets.Set{} - l.typesIndex = map[config.GroupVersionKind]sets.Set{} - size(l.store.Len()) -} - -func (l *lruCache) Keys() []string { - l.mu.RLock() - defer l.mu.RUnlock() - iKeys := l.store.Keys() - keys := make([]string, 0, len(iKeys)) - for _, ik := range iKeys { - keys = append(keys, ik.(string)) - } - return keys -} - -func (l *lruCache) Snapshot() map[string]*discovery.Resource { - l.mu.RLock() - defer l.mu.RUnlock() - iKeys := l.store.Keys() - res := make(map[string]*discovery.Resource, len(iKeys)) - for _, ik := range iKeys { - v, ok := l.store.Get(ik) - if !ok { - continue - } - - res[ik.(string)] = v.(cacheValue).value - } - return res -} - -// DisabledCache is a cache that is always empty -type DisabledCache struct{} - -var _ XdsCache = &DisabledCache{} - -func (d DisabledCache) Add(key XdsCacheEntry, pushReq *PushRequest, value *discovery.Resource) {} - -func (d DisabledCache) Get(XdsCacheEntry) (*discovery.Resource, bool) { - return nil, false -} - -func (d DisabledCache) Clear(configsUpdated map[ConfigKey]struct{}) {} - -func (d DisabledCache) ClearAll() {} - -func (d DisabledCache) Keys() []string { return nil } - -func (d DisabledCache) Snapshot() map[string]*discovery.Resource { return nil } diff --git a/pilot/pkg/networking/apigen/apigen.go b/pilot/pkg/networking/apigen/apigen.go deleted file mode 100644 index 6818546f4..000000000 --- a/pilot/pkg/networking/apigen/apigen.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package apigen - -import ( - "strings" -) - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/serviceentry" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -// APIGenerator supports generation of high-level API resources, similar with the MCP -// protocol. This is a replacement for MCP, using XDS (and in future UDPA) as a transport. -// Based on lessons from MCP, the protocol allows incremental updates by -// default, using the same mechanism that EDS is using, i.e. sending only changed resources -// in a push. Incremental deletes are sent as a resource with empty body. -// -// Example: networking.istio.io/v1alpha3/VirtualService -// -// TODO: we can also add a special marker in the header) -type APIGenerator struct { - // ConfigStore interface for listing istio api resources. - store model.ConfigStore -} - -func NewGenerator(store model.ConfigStore) *APIGenerator { - return &APIGenerator{ - store: store, - } -} - -// TODO: take 'updates' into account, don't send pushes for resources that haven't changed -// TODO: support WorkloadEntry - to generate endpoints (equivalent with EDS) -// TODO: based on lessons from MCP, we want to send 'chunked' responses, like apiserver does. -// A first attempt added a 'sync' record at the end. Based on feedback and common use, a -// different approach can be used - for large responses, we can mark the last one as 'hasMore' -// by adding a field to the envelope. - -// Generate implements the generate method for high level APIs, like Istio config types. -// This provides similar functionality with MCP and :8080/debug/configz. -// -// Names are based on the current resource naming in istiod stores. -func (g *APIGenerator) Generate(proxy *model.Proxy, w *model.WatchedResource, req *model.PushRequest) (model.Resources, model.XdsLogDetails, error) { - resp := model.Resources{} - - // Note: this is the style used by MCP and its config. Pilot is using 'Group/Version/Kind' as the - // key, which is similar. - // - // The actual type in the Any should be a real proto - which is based on the generated package name. - // For example: type is for Any is 'type.googlepis.com/istio.networking.v1alpha3.EnvoyFilter - // We use: networking.istio.io/v1alpha3/EnvoyFilter - kind := strings.SplitN(w.TypeUrl, "/", 3) - if len(kind) != 3 { - log.Warnf("ADS: Unknown watched resources %s", w.TypeUrl) - // Still return an empty response - to not break waiting code. It is fine to not know about some resource. - return resp, model.DefaultXdsLogDetails, nil - } - // TODO: extra validation may be needed - at least logging that a resource - // of unknown type was requested. This should not be an error - maybe client asks - // for a valid CRD we just don't know about. An empty set indicates we have no such config. - rgvk := config.GroupVersionKind{ - Group: kind[0], - Version: kind[1], - Kind: kind[2], - } - if w.TypeUrl == collections.IstioMeshV1Alpha1MeshConfig.Resource().GroupVersionKind().String() { - resp = append(resp, &discovery.Resource{ - Resource: util.MessageToAny(req.Push.Mesh), - }) - return resp, model.DefaultXdsLogDetails, nil - } - - // TODO: what is the proper way to handle errors ? - // Normally once istio is 'ready' List can't return errors on a valid config - - // even if k8s is disconnected, we still cache all previous results. - // This needs further consideration - I don't think XDS or MCP transports - // have a clear recommendation. - cfg, err := g.store.List(rgvk, "") - if err != nil { - log.Warnf("ADS: Error reading resource %s %v", w.TypeUrl, err) - return resp, model.DefaultXdsLogDetails, nil - } - for _, c := range cfg { - // Right now model.Config is not a proto - until we change it, mcp.Resource. - // This also helps migrating MCP users. - - b, err := config.PilotConfigToResource(&c) - if err != nil { - log.Warn("Resource error ", err, " ", c.Namespace, "/", c.Name) - continue - } - resp = append(resp, &discovery.Resource{ - Name: c.Namespace + "/" + c.Name, - Resource: util.MessageToAny(b), - }) - } - - // TODO: MeshConfig, current dynamic ProxyConfig (for this proxy), Networks - - if w.TypeUrl == gvk.ServiceEntry.String() { - // Include 'synthetic' SE - but without the endpoints. Used to generate CDS, LDS. - // EDS is pass-through. - svcs := proxy.SidecarScope.Services() - for _, s := range svcs { - // Ignore services that are result of conversion from ServiceEntry. - if s.Attributes.ServiceRegistry == provider.External { - continue - } - c := serviceentry.ServiceToServiceEntry(s, proxy) - b, err := config.PilotConfigToResource(c) - if err != nil { - log.Warn("Resource error ", err, " ", c.Namespace, "/", c.Name) - continue - } - resp = append(resp, &discovery.Resource{ - Name: c.Namespace + "/" + c.Name, - Resource: util.MessageToAny(b), - }) - } - } - - return resp, model.DefaultXdsLogDetails, nil -} diff --git a/pilot/pkg/networking/apigen/apigen_test.go b/pilot/pkg/networking/apigen/apigen_test.go deleted file mode 100644 index a5017efd2..000000000 --- a/pilot/pkg/networking/apigen/apigen_test.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package apigen_test - -import ( - "testing" - "time" -) - -import ( - xdsapi "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/apigen" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/adsc" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -// Creates an in-process discovery server, using the same code as Istiod, but -// backed by an in-memory config and endpoint Store. -func initDS() *xds.SimpleServer { - ds := xds.NewXDS(make(chan struct{})) - - sd := ds.DiscoveryServer.MemRegistry - sd.AddHTTPService("fortio1.fortio.svc.cluster.local", "10.10.10.1", 8081) - sd.SetEndpoints("fortio1.fortio.svc.cluster.local", "", []*model.IstioEndpoint{ - { - Address: "127.0.0.1", - EndpointPort: uint32(14056), - ServicePortName: "http-main", - }, - }) - return ds -} - -// Test using resolving DNS over GRPC. This uses XDS protocol, and Listener resources -// to represent the names. The protocol is based on GRPC resolution of XDS resources. -func TestAPIGen(t *testing.T) { - ds := initDS() - ds.DiscoveryServer.Generators["api"] = apigen.NewGenerator(ds.DiscoveryServer.Env.ConfigStore) - epGen := &xds.EdsGenerator{Server: ds.DiscoveryServer} - ds.DiscoveryServer.Generators["api/"+v3.EndpointType] = epGen - - xdsAddr, err := ds.StartGRPC("127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - defer ds.GRPCListener.Close() - - // Verify we can receive the DNS cluster IPs using XDS - t.Run("adsc", func(t *testing.T) { - adscConn, err := adsc.New(xdsAddr, &adsc.Config{ - IP: "1.2.3.4", - Meta: model.NodeMetadata{ - Generator: "api", - }.ToStruct(), - }) - if err != nil { - t.Fatal("Error connecting ", err) - } - store := memory.Make(collections.Pilot) - - configController := memory.NewController(store) - adscConn.Store = model.MakeIstioStore(configController) - err = adscConn.Run() - if err != nil { - t.Fatal("ADSC: failed running ", err) - } - - adscConn.Send(&xdsapi.DiscoveryRequest{ - TypeUrl: v3.ListenerType, - }) - - adscConn.WatchConfig() - - _, err = adscConn.WaitVersion(10*time.Second, gvk.ServiceEntry.String(), "") - if err != nil { - t.Fatal("Failed to receive lds", err) - } - - ses, _ := adscConn.Store.List(gvk.ServiceEntry, "") - for _, se := range ses { - t.Log(se) - } - sec, _ := adscConn.Store.List(gvk.EnvoyFilter, "") - for _, se := range sec { - t.Log(se) - } - }) -} diff --git a/pilot/pkg/networking/core/configgen.go b/pilot/pkg/networking/core/configgen.go deleted file mode 100644 index 3d80bebef..000000000 --- a/pilot/pkg/networking/core/configgen.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - meshconfig "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3" - dnsProto "github.com/apache/dubbo-go-pixiu/pkg/dns/proto" -) - -// ConfigGenerator represents the interfaces to be implemented by code that generates xDS responses -type ConfigGenerator interface { - // BuildListeners returns the list of inbound/outbound listeners for the given proxy. This is the LDS output - // Internally, the computation will be optimized to ensure that listeners are computed only - // once and shared across multiple invocations of this function. - BuildListeners(node *model.Proxy, push *model.PushContext) []*listener.Listener - - // BuildClusters returns the list of clusters for the given proxy. This is the CDS output - BuildClusters(node *model.Proxy, req *model.PushRequest) ([]*discovery.Resource, model.XdsLogDetails) - - // BuildDeltaClusters returns both a list of resources that need to be pushed for a given proxy and a list of resources - // that have been deleted and should be removed from a given proxy. This is Delta CDS output. - BuildDeltaClusters(proxy *model.Proxy, updates *model.PushRequest, - watched *model.WatchedResource) ([]*discovery.Resource, []string, model.XdsLogDetails, bool) - - // BuildHTTPRoutes returns the list of HTTP routes for the given proxy. This is the RDS output - BuildHTTPRoutes(node *model.Proxy, req *model.PushRequest, routeNames []string) ([]*discovery.Resource, model.XdsLogDetails) - - // BuildNameTable returns list of hostnames and the associated IPs - BuildNameTable(node *model.Proxy, push *model.PushContext) *dnsProto.NameTable - - // BuildExtensionConfiguration returns the list of extension configuration for the given proxy and list of names. This is the ECDS output. - BuildExtensionConfiguration(node *model.Proxy, push *model.PushContext, extensionConfigNames []string, - pullSecrets map[string][]byte) []*core.TypedExtensionConfig - - // MeshConfigChanged is invoked when mesh config is changed, giving a chance to rebuild any cached config. - MeshConfigChanged(mesh *meshconfig.MeshConfig) -} - -// NewConfigGenerator creates a new instance of the dataplane configuration generator -func NewConfigGenerator(cache model.XdsCache) ConfigGenerator { - return v1alpha3.NewConfigGenerator(cache) -} diff --git a/pilot/pkg/networking/core/v1alpha3/accesslog.go b/pilot/pkg/networking/core/v1alpha3/accesslog.go deleted file mode 100644 index c8e3f5ee6..000000000 --- a/pilot/pkg/networking/core/v1alpha3/accesslog.go +++ /dev/null @@ -1,708 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "strings" - "sync" -) - -import ( - accesslog "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v3" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - fileaccesslog "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/file/v3" - cel "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/filters/cel/v3" - grpcaccesslog "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/grpc/v3" - otelaccesslog "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/open_telemetry/v3" - hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - tcp "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3" - formatters "github.com/envoyproxy/go-control-plane/envoy/extensions/formatter/req_without_query/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - otlpcommon "go.opentelemetry.io/proto/otlp/common/v1" - "google.golang.org/protobuf/types/known/structpb" - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -const ( - // EnvoyTextLogFormat format for envoy text based access logs for Istio 1.9 onwards. - // This includes the additional new operator RESPONSE_CODE_DETAILS and CONNECTION_TERMINATION_DETAILS that tells - // the reason why Envoy rejects a request. - EnvoyTextLogFormat = "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% " + - "%PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% " + - "%RESPONSE_CODE_DETAILS% %CONNECTION_TERMINATION_DETAILS% " + - "\"%UPSTREAM_TRANSPORT_FAILURE_REASON%\" %BYTES_RECEIVED% %BYTES_SENT% " + - "%DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" " + - "\"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\" " + - "%UPSTREAM_CLUSTER% %UPSTREAM_LOCAL_ADDRESS% %DOWNSTREAM_LOCAL_ADDRESS% " + - "%DOWNSTREAM_REMOTE_ADDRESS% %REQUESTED_SERVER_NAME% %ROUTE_NAME%\n" - - // EnvoyServerName for istio's envoy - EnvoyServerName = "istio-envoy" - - httpEnvoyAccessLogFriendlyName = "http_envoy_accesslog" - tcpEnvoyAccessLogFriendlyName = "tcp_envoy_accesslog" - otelEnvoyAccessLogFriendlyName = "otel_envoy_accesslog" - listenerEnvoyAccessLogFriendlyName = "listener_envoy_accesslog" - - tcpEnvoyALSName = "envoy.tcp_grpc_access_log" - otelEnvoyALSName = "envoy.access_loggers.open_telemetry" - - // EnvoyAccessLogCluster is the cluster name that has details for server implementing Envoy ALS. - // This cluster is created in bootstrap. - EnvoyAccessLogCluster = "envoy_accesslog_service" - - requestWithoutQuery = "%REQ_WITHOUT_QUERY" - - devStdout = "/dev/stdout" - - celFilter = "envoy.access_loggers.extension_filters.cel" -) - -var ( - // EnvoyJSONLogFormatIstio map of values for envoy json based access logs for Istio 1.9 onwards. - // This includes the additional log operator RESPONSE_CODE_DETAILS and CONNECTION_TERMINATION_DETAILS that tells - // the reason why Envoy rejects a request. - EnvoyJSONLogFormatIstio = &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "start_time": {Kind: &structpb.Value_StringValue{StringValue: "%START_TIME%"}}, - "route_name": {Kind: &structpb.Value_StringValue{StringValue: "%ROUTE_NAME%"}}, - "method": {Kind: &structpb.Value_StringValue{StringValue: "%REQ(:METHOD)%"}}, - "path": {Kind: &structpb.Value_StringValue{StringValue: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"}}, - "protocol": {Kind: &structpb.Value_StringValue{StringValue: "%PROTOCOL%"}}, - "response_code": {Kind: &structpb.Value_StringValue{StringValue: "%RESPONSE_CODE%"}}, - "response_flags": {Kind: &structpb.Value_StringValue{StringValue: "%RESPONSE_FLAGS%"}}, - "response_code_details": {Kind: &structpb.Value_StringValue{StringValue: "%RESPONSE_CODE_DETAILS%"}}, - "connection_termination_details": {Kind: &structpb.Value_StringValue{StringValue: "%CONNECTION_TERMINATION_DETAILS%"}}, - "bytes_received": {Kind: &structpb.Value_StringValue{StringValue: "%BYTES_RECEIVED%"}}, - "bytes_sent": {Kind: &structpb.Value_StringValue{StringValue: "%BYTES_SENT%"}}, - "duration": {Kind: &structpb.Value_StringValue{StringValue: "%DURATION%"}}, - "upstream_service_time": {Kind: &structpb.Value_StringValue{StringValue: "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%"}}, - "x_forwarded_for": {Kind: &structpb.Value_StringValue{StringValue: "%REQ(X-FORWARDED-FOR)%"}}, - "user_agent": {Kind: &structpb.Value_StringValue{StringValue: "%REQ(USER-AGENT)%"}}, - "request_id": {Kind: &structpb.Value_StringValue{StringValue: "%REQ(X-REQUEST-ID)%"}}, - "authority": {Kind: &structpb.Value_StringValue{StringValue: "%REQ(:AUTHORITY)%"}}, - "upstream_host": {Kind: &structpb.Value_StringValue{StringValue: "%UPSTREAM_HOST%"}}, - "upstream_cluster": {Kind: &structpb.Value_StringValue{StringValue: "%UPSTREAM_CLUSTER%"}}, - "upstream_local_address": {Kind: &structpb.Value_StringValue{StringValue: "%UPSTREAM_LOCAL_ADDRESS%"}}, - "downstream_local_address": {Kind: &structpb.Value_StringValue{StringValue: "%DOWNSTREAM_LOCAL_ADDRESS%"}}, - "downstream_remote_address": {Kind: &structpb.Value_StringValue{StringValue: "%DOWNSTREAM_REMOTE_ADDRESS%"}}, - "requested_server_name": {Kind: &structpb.Value_StringValue{StringValue: "%REQUESTED_SERVER_NAME%"}}, - "upstream_transport_failure_reason": {Kind: &structpb.Value_StringValue{StringValue: "%UPSTREAM_TRANSPORT_FAILURE_REASON%"}}, - }, - } - - // State logged by the metadata exchange filter about the upstream and downstream service instances - // We need to propagate these as part of access log service stream - // Logging them by default on the console may be an issue as the base64 encoded string is bound to be a big one. - // But end users can certainly configure it on their own via the meshConfig using the %FILTERSTATE% macro. - envoyWasmStateToLog = []string{"wasm.upstream_peer", "wasm.upstream_peer_id", "wasm.downstream_peer", "wasm.downstream_peer_id"} - - // accessLogBuilder is used to set accessLog to filters - accessLogBuilder = newAccessLogBuilder() - - // accessLogFormatters configures additional formatters needed for some of the format strings like "REQ_WITHOUT_QUERY" - accessLogFormatters = []*core.TypedExtensionConfig{ - { - Name: "envoy.formatter.req_without_query", - TypedConfig: util.MessageToAny(&formatters.ReqWithoutQuery{}), - }, - } -) - -type AccessLogBuilder struct { - // tcpGrpcAccessLog is used when access log service is enabled in mesh config. - tcpGrpcAccessLog *accesslog.AccessLog - // httpGrpcAccessLog is used when access log service is enabled in mesh config. - httpGrpcAccessLog *accesslog.AccessLog - // tcpGrpcListenerAccessLog is used when access log service is enabled in mesh config. - tcpGrpcListenerAccessLog *accesslog.AccessLog - - // file accessLog which is cached and reset on MeshConfig change. - mutex sync.RWMutex - fileAccesslog *accesslog.AccessLog - listenerFileAccessLog *accesslog.AccessLog -} - -func newAccessLogBuilder() *AccessLogBuilder { - return &AccessLogBuilder{ - tcpGrpcAccessLog: buildTCPGrpcAccessLog(false), - httpGrpcAccessLog: buildHTTPGrpcAccessLog(), - tcpGrpcListenerAccessLog: buildTCPGrpcAccessLog(true), - } -} - -func (b *AccessLogBuilder) setTCPAccessLog(push *model.PushContext, proxy *model.Proxy, tcp *tcp.TcpProxy, class networking.ListenerClass) { - mesh := push.Mesh - cfg := push.Telemetry.AccessLogging(proxy, class) - - if cfg == nil { - // No Telemetry API configured, fall back to legacy mesh config setting - if mesh.AccessLogFile != "" { - tcp.AccessLog = append(tcp.AccessLog, b.buildFileAccessLog(mesh)) - } - - if mesh.EnableEnvoyAccessLogService { - // Setting it to TCP as the low level one. - tcp.AccessLog = append(tcp.AccessLog, b.tcpGrpcAccessLog) - } - return - } - - if al := buildAccessLogFromTelemetry(push, cfg, false); len(al) != 0 { - tcp.AccessLog = append(tcp.AccessLog, al...) - } -} - -func buildAccessLogFromTelemetry(push *model.PushContext, spec *model.LoggingConfig, forListener bool) []*accesslog.AccessLog { - als := make([]*accesslog.AccessLog, 0) - telFilter := buildAccessLogFilterFromTelemetry(spec) - filters := []*accesslog.AccessLogFilter{} - if forListener { - filters = append(filters, addAccessLogFilter()) - } - if telFilter != nil { - filters = append(filters, telFilter) - } - - for _, p := range spec.Providers { - var al *accesslog.AccessLog - switch prov := p.Provider.(type) { - case *meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLog: - al = buildEnvoyFileAccessLogHelper(prov.EnvoyFileAccessLog) - case *meshconfig.MeshConfig_ExtensionProvider_EnvoyHttpAls: - al = buildHTTPGrpcAccessLogHelper(push, prov.EnvoyHttpAls) - case *meshconfig.MeshConfig_ExtensionProvider_EnvoyTcpAls: - al = buildTCPGrpcAccessLogHelper(push, prov.EnvoyTcpAls) - case *meshconfig.MeshConfig_ExtensionProvider_EnvoyOtelAls: - al = buildOpenTelemetryLogHelper(push, prov.EnvoyOtelAls) - } - if al == nil { - continue - } - - al.Filter = buildAccessLogFilter(filters...) - als = append(als, al) - } - return als -} - -func buildAccessLogFilterFromTelemetry(spec *model.LoggingConfig) *accesslog.AccessLogFilter { - if spec == nil || spec.Filter == nil { - return nil - } - - fl := &cel.ExpressionFilter{ - Expression: spec.Filter.Expression, - } - - return &accesslog.AccessLogFilter{ - FilterSpecifier: &accesslog.AccessLogFilter_ExtensionFilter{ - ExtensionFilter: &accesslog.ExtensionFilter{ - Name: celFilter, - ConfigType: &accesslog.ExtensionFilter_TypedConfig{TypedConfig: util.MessageToAny(fl)}, - }, - }, - } -} - -func (b *AccessLogBuilder) setHTTPAccessLog(push *model.PushContext, proxy *model.Proxy, - connectionManager *hcm.HttpConnectionManager, class networking.ListenerClass) { - mesh := push.Mesh - cfg := push.Telemetry.AccessLogging(proxy, class) - - if cfg == nil { - // No Telemetry API configured, fall back to legacy mesh config setting - if mesh.AccessLogFile != "" { - connectionManager.AccessLog = append(connectionManager.AccessLog, b.buildFileAccessLog(mesh)) - } - - if mesh.EnableEnvoyAccessLogService { - connectionManager.AccessLog = append(connectionManager.AccessLog, b.httpGrpcAccessLog) - } - return - } - - if al := buildAccessLogFromTelemetry(push, cfg, false); len(al) != 0 { - connectionManager.AccessLog = append(connectionManager.AccessLog, al...) - } -} - -func (b *AccessLogBuilder) setListenerAccessLog(push *model.PushContext, proxy *model.Proxy, - listener *listener.Listener, class networking.ListenerClass) { - mesh := push.Mesh - if mesh.DisableEnvoyListenerLog { - return - } - cfg := push.Telemetry.AccessLogging(proxy, class) - - if cfg == nil { - // No Telemetry API configured, fall back to legacy mesh config setting - if mesh.AccessLogFile != "" { - listener.AccessLog = append(listener.AccessLog, b.buildListenerFileAccessLog(mesh)) - } - - if mesh.EnableEnvoyAccessLogService { - // Setting it to TCP as the low level one. - listener.AccessLog = append(listener.AccessLog, b.tcpGrpcListenerAccessLog) - } - return - } - - if al := buildAccessLogFromTelemetry(push, cfg, true); len(al) != 0 { - listener.AccessLog = append(listener.AccessLog, al...) - } -} - -func buildTCPGrpcAccessLogHelper(push *model.PushContext, prov *meshconfig.MeshConfig_ExtensionProvider_EnvoyTcpGrpcV3LogProvider) *accesslog.AccessLog { - logName := tcpEnvoyAccessLogFriendlyName - if prov != nil && prov.LogName != "" { - logName = prov.LogName - } - - filterObjects := envoyWasmStateToLog - if len(prov.FilterStateObjectsToLog) != 0 { - filterObjects = prov.FilterStateObjectsToLog - } - - hostname, cluster, err := clusterLookupFn(push, prov.Service, int(prov.Port)) - if err != nil { - log.Errorf("could not find cluster for tcp grpc provider %q: %v", prov, err) - return nil - } - - fl := &grpcaccesslog.TcpGrpcAccessLogConfig{ - CommonConfig: &grpcaccesslog.CommonGrpcAccessLogConfig{ - LogName: logName, - GrpcService: &core.GrpcService{ - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ - ClusterName: cluster, - Authority: hostname, - }, - }, - }, - TransportApiVersion: core.ApiVersion_V3, - FilterStateObjectsToLog: filterObjects, - }, - } - - return &accesslog.AccessLog{ - Name: tcpEnvoyALSName, - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(fl)}, - } -} - -func buildEnvoyFileAccessLogHelper(prov *meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider) *accesslog.AccessLog { - p := prov.Path - if p == "" { - p = devStdout - } - - fl := &fileaccesslog.FileAccessLog{ - Path: p, - } - needsFormatter := false - if prov.LogFormat != nil { - switch logFormat := prov.LogFormat.LogFormat.(type) { - case *meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider_LogFormat_Text: - fl.AccessLogFormat, needsFormatter = buildFileAccessTextLogFormat(logFormat.Text) - case *meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider_LogFormat_Labels: - fl.AccessLogFormat, needsFormatter = buildFileAccessJSONLogFormat(logFormat) - } - } else { - fl.AccessLogFormat, needsFormatter = buildFileAccessTextLogFormat("") - } - if needsFormatter { - fl.GetLogFormat().Formatters = accessLogFormatters - } - - al := &accesslog.AccessLog{ - Name: wellknown.FileAccessLog, - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(fl)}, - } - - return al -} - -func buildFileAccessTextLogFormat(text string) (*fileaccesslog.FileAccessLog_LogFormat, bool) { - formatString := EnvoyTextLogFormat - if text != "" { - formatString = text - } - needsFormatter := strings.Contains(formatString, requestWithoutQuery) - return &fileaccesslog.FileAccessLog_LogFormat{ - LogFormat: &core.SubstitutionFormatString{ - Format: &core.SubstitutionFormatString_TextFormatSource{ - TextFormatSource: &core.DataSource{ - Specifier: &core.DataSource_InlineString{ - InlineString: formatString, - }, - }, - }, - }, - }, needsFormatter -} - -func buildFileAccessJSONLogFormat( - logFormat *meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider_LogFormat_Labels) (*fileaccesslog.FileAccessLog_LogFormat, bool) { - jsonLogStruct := EnvoyJSONLogFormatIstio - if logFormat.Labels != nil { - jsonLogStruct = logFormat.Labels - } - - // allow default behavior when no labels supplied. - if len(jsonLogStruct.Fields) == 0 { - jsonLogStruct = EnvoyJSONLogFormatIstio - } - - needsFormatter := false - for _, value := range jsonLogStruct.Fields { - if value.GetStringValue() == requestWithoutQuery { - needsFormatter = true - break - } - } - return &fileaccesslog.FileAccessLog_LogFormat{ - LogFormat: &core.SubstitutionFormatString{ - Format: &core.SubstitutionFormatString_JsonFormat{ - JsonFormat: jsonLogStruct, - }, - }, - }, needsFormatter -} - -func buildHTTPGrpcAccessLogHelper(push *model.PushContext, prov *meshconfig.MeshConfig_ExtensionProvider_EnvoyHttpGrpcV3LogProvider) *accesslog.AccessLog { - logName := httpEnvoyAccessLogFriendlyName - if prov != nil && prov.LogName != "" { - logName = prov.LogName - } - - filterObjects := envoyWasmStateToLog - if len(prov.FilterStateObjectsToLog) != 0 { - filterObjects = prov.FilterStateObjectsToLog - } - - hostname, cluster, err := clusterLookupFn(push, prov.Service, int(prov.Port)) - if err != nil { - log.Errorf("could not find cluster for http grpc provider %q: %v", prov, err) - return nil - } - - fl := &grpcaccesslog.HttpGrpcAccessLogConfig{ - CommonConfig: &grpcaccesslog.CommonGrpcAccessLogConfig{ - LogName: logName, - GrpcService: &core.GrpcService{ - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ - ClusterName: cluster, - Authority: hostname, - }, - }, - }, - TransportApiVersion: core.ApiVersion_V3, - FilterStateObjectsToLog: filterObjects, - }, - AdditionalRequestHeadersToLog: prov.AdditionalRequestHeadersToLog, - AdditionalResponseHeadersToLog: prov.AdditionalResponseHeadersToLog, - AdditionalResponseTrailersToLog: prov.AdditionalResponseTrailersToLog, - } - - return &accesslog.AccessLog{ - Name: wellknown.HTTPGRPCAccessLog, - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(fl)}, - } -} - -func buildFileAccessLogHelper(path string, mesh *meshconfig.MeshConfig) *accesslog.AccessLog { - // We need to build access log. This is needed either on first access or when mesh config changes. - fl := &fileaccesslog.FileAccessLog{ - Path: path, - } - needsFormatter := false - switch mesh.AccessLogEncoding { - case meshconfig.MeshConfig_TEXT: - formatString := EnvoyTextLogFormat - if mesh.AccessLogFormat != "" { - formatString = mesh.AccessLogFormat - } - needsFormatter = strings.Contains(formatString, requestWithoutQuery) - fl.AccessLogFormat = &fileaccesslog.FileAccessLog_LogFormat{ - LogFormat: &core.SubstitutionFormatString{ - Format: &core.SubstitutionFormatString_TextFormatSource{ - TextFormatSource: &core.DataSource{ - Specifier: &core.DataSource_InlineString{ - InlineString: formatString, - }, - }, - }, - }, - } - case meshconfig.MeshConfig_JSON: - jsonLogStruct := EnvoyJSONLogFormatIstio - if len(mesh.AccessLogFormat) > 0 { - parsedJSONLogStruct := structpb.Struct{} - if err := protomarshal.UnmarshalAllowUnknown([]byte(mesh.AccessLogFormat), &parsedJSONLogStruct); err != nil { - log.Errorf("error parsing provided json log format, default log format will be used: %v", err) - } else { - jsonLogStruct = &parsedJSONLogStruct - } - } - for _, value := range jsonLogStruct.Fields { - if value.GetStringValue() == requestWithoutQuery { - needsFormatter = true - break - } - } - fl.AccessLogFormat = &fileaccesslog.FileAccessLog_LogFormat{ - LogFormat: &core.SubstitutionFormatString{ - Format: &core.SubstitutionFormatString_JsonFormat{ - JsonFormat: jsonLogStruct, - }, - }, - } - default: - log.Warnf("unsupported access log format %v", mesh.AccessLogEncoding) - } - if needsFormatter { - fl.GetLogFormat().Formatters = accessLogFormatters - } - al := &accesslog.AccessLog{ - Name: wellknown.FileAccessLog, - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(fl)}, - } - - return al -} - -func buildOpenTelemetryLogHelper(pushCtx *model.PushContext, - provider *meshconfig.MeshConfig_ExtensionProvider_EnvoyOpenTelemetryLogProvider, -) *accesslog.AccessLog { - hostname, cluster, err := clusterLookupFn(pushCtx, provider.Service, int(provider.Port)) - if err != nil { - log.Errorf("could not find cluster for open telemetry provider %q: %v", provider, err) - return nil - } - - logName := provider.LogName - if logName == "" { - logName = otelEnvoyAccessLogFriendlyName - } - - f := EnvoyTextLogFormat - if provider.LogFormat != nil && provider.LogFormat.Text != "" { - f = provider.LogFormat.Text - } - - var labels *structpb.Struct - if provider.LogFormat != nil { - labels = provider.LogFormat.Labels - } - - cfg := buildOpenTelemetryAccessLogConfig(logName, hostname, cluster, f, labels) - - return &accesslog.AccessLog{ - Name: otelEnvoyALSName, - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(cfg)}, - } -} - -func buildOpenTelemetryAccessLogConfig(logName, hostname, clusterName, format string, labels *structpb.Struct) *otelaccesslog.OpenTelemetryAccessLogConfig { - cfg := &otelaccesslog.OpenTelemetryAccessLogConfig{ - CommonConfig: &grpcaccesslog.CommonGrpcAccessLogConfig{ - LogName: logName, - GrpcService: &core.GrpcService{ - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ - ClusterName: clusterName, - Authority: hostname, - }, - }, - }, - TransportApiVersion: core.ApiVersion_V3, - FilterStateObjectsToLog: envoyWasmStateToLog, - }, - } - - if format != "" { - cfg.Body = &otlpcommon.AnyValue{ - Value: &otlpcommon.AnyValue_StringValue{ - StringValue: format, - }, - } - } - - if labels != nil && len(labels.Fields) != 0 { - cfg.Attributes = &otlpcommon.KeyValueList{ - Values: convertStructToAttributeKeyValues(labels.Fields), - } - } - - return cfg -} - -func convertStructToAttributeKeyValues(labels map[string]*structpb.Value) []*otlpcommon.KeyValue { - if len(labels) == 0 { - return nil - } - attrList := make([]*otlpcommon.KeyValue, 0, len(labels)) - for key, value := range labels { - kv := &otlpcommon.KeyValue{ - Key: key, - Value: &otlpcommon.AnyValue{Value: &otlpcommon.AnyValue_StringValue{StringValue: value.GetStringValue()}}, - } - attrList = append(attrList, kv) - } - return attrList -} - -func (b *AccessLogBuilder) buildFileAccessLog(mesh *meshconfig.MeshConfig) *accesslog.AccessLog { - if cal := b.cachedFileAccessLog(); cal != nil { - return cal - } - - // We need to build access log. This is needed either on first access or when mesh config changes. - al := buildFileAccessLogHelper(mesh.AccessLogFile, mesh) - - b.mutex.Lock() - defer b.mutex.Unlock() - b.fileAccesslog = al - - return al -} - -func addAccessLogFilter() *accesslog.AccessLogFilter { - return &accesslog.AccessLogFilter{ - FilterSpecifier: &accesslog.AccessLogFilter_ResponseFlagFilter{ - ResponseFlagFilter: &accesslog.ResponseFlagFilter{Flags: []string{"NR"}}, - }, - } -} - -func buildAccessLogFilter(f ...*accesslog.AccessLogFilter) *accesslog.AccessLogFilter { - if len(f) == 0 { - return nil - } - - if len(f) == 1 { - return f[0] - } - - return &accesslog.AccessLogFilter{ - FilterSpecifier: &accesslog.AccessLogFilter_AndFilter{ - AndFilter: &accesslog.AndFilter{ - Filters: f, - }, - }, - } -} - -func (b *AccessLogBuilder) buildListenerFileAccessLog(mesh *meshconfig.MeshConfig) *accesslog.AccessLog { - if cal := b.cachedListenerFileAccessLog(); cal != nil { - return cal - } - - // We need to build access log. This is needed either on first access or when mesh config changes. - lal := buildFileAccessLogHelper(mesh.AccessLogFile, mesh) - // We add ResponseFlagFilter here, as we want to get listener access logs only on scenarios where we might - // not get filter Access Logs like in cases like NR to upstream. - lal.Filter = addAccessLogFilter() - - b.mutex.Lock() - defer b.mutex.Unlock() - b.listenerFileAccessLog = lal - - return lal -} - -func (b *AccessLogBuilder) cachedFileAccessLog() *accesslog.AccessLog { - b.mutex.RLock() - defer b.mutex.RUnlock() - return b.fileAccesslog -} - -func (b *AccessLogBuilder) cachedListenerFileAccessLog() *accesslog.AccessLog { - b.mutex.RLock() - defer b.mutex.RUnlock() - return b.listenerFileAccessLog -} - -func buildTCPGrpcAccessLog(isListener bool) *accesslog.AccessLog { - accessLogFriendlyName := tcpEnvoyAccessLogFriendlyName - if isListener { - accessLogFriendlyName = listenerEnvoyAccessLogFriendlyName - } - fl := &grpcaccesslog.TcpGrpcAccessLogConfig{ - CommonConfig: &grpcaccesslog.CommonGrpcAccessLogConfig{ - LogName: accessLogFriendlyName, - GrpcService: &core.GrpcService{ - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ - ClusterName: EnvoyAccessLogCluster, - }, - }, - }, - TransportApiVersion: core.ApiVersion_V3, - FilterStateObjectsToLog: envoyWasmStateToLog, - }, - } - - var filter *accesslog.AccessLogFilter - if isListener { - filter = addAccessLogFilter() - } - return &accesslog.AccessLog{ - Name: tcpEnvoyALSName, - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(fl)}, - Filter: filter, - } -} - -func buildHTTPGrpcAccessLog() *accesslog.AccessLog { - fl := &grpcaccesslog.HttpGrpcAccessLogConfig{ - CommonConfig: &grpcaccesslog.CommonGrpcAccessLogConfig{ - LogName: httpEnvoyAccessLogFriendlyName, - GrpcService: &core.GrpcService{ - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ - ClusterName: EnvoyAccessLogCluster, - }, - }, - }, - TransportApiVersion: core.ApiVersion_V3, - FilterStateObjectsToLog: envoyWasmStateToLog, - }, - } - - return &accesslog.AccessLog{ - Name: wellknown.HTTPGRPCAccessLog, - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(fl)}, - } -} - -func (b *AccessLogBuilder) reset() { - b.mutex.Lock() - b.fileAccesslog = nil - b.listenerFileAccessLog = nil - b.mutex.Unlock() -} diff --git a/pilot/pkg/networking/core/v1alpha3/accesslog_test.go b/pilot/pkg/networking/core/v1alpha3/accesslog_test.go deleted file mode 100644 index 9adae9920..000000000 --- a/pilot/pkg/networking/core/v1alpha3/accesslog_test.go +++ /dev/null @@ -1,957 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "testing" -) - -import ( - accesslog "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v3" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - fileaccesslog "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/file/v3" - cel "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/filters/cel/v3" - grpcaccesslog "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/grpc/v3" - otelaccesslog "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/open_telemetry/v3" - httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - tcp "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3" - "github.com/envoyproxy/go-control-plane/pkg/conversion" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "github.com/google/go-cmp/cmp" - otlpcommon "go.opentelemetry.io/proto/otlp/common/v1" - "google.golang.org/protobuf/testing/protocmp" - "google.golang.org/protobuf/types/known/structpb" - meshconfig "istio.io/api/mesh/v1alpha1" - tpb "istio.io/api/telemetry/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -var ( - httpCodeExpress = "response.code >= 400" - httpCodeFilter = &cel.ExpressionFilter{ - Expression: httpCodeExpress, - } -) - -func TestListenerAccessLog(t *testing.T) { - defaultFormatJSON, _ := protomarshal.ToJSON(EnvoyJSONLogFormatIstio) - - for _, tc := range []struct { - name string - encoding meshconfig.MeshConfig_AccessLogEncoding - format string - wantFormat string - }{ - { - name: "valid json object", - encoding: meshconfig.MeshConfig_JSON, - format: `{"foo": "bar"}`, - wantFormat: `{"foo":"bar"}`, - }, - { - name: "valid nested json object", - encoding: meshconfig.MeshConfig_JSON, - format: `{"foo": {"bar": "ha"}}`, - wantFormat: `{"foo":{"bar":"ha"}}`, - }, - { - name: "invalid json object", - encoding: meshconfig.MeshConfig_JSON, - format: `foo`, - wantFormat: defaultFormatJSON, - }, - { - name: "incorrect json type", - encoding: meshconfig.MeshConfig_JSON, - format: `[]`, - wantFormat: defaultFormatJSON, - }, - { - name: "incorrect json type", - encoding: meshconfig.MeshConfig_JSON, - format: `"{}"`, - wantFormat: defaultFormatJSON, - }, - { - name: "default json format", - encoding: meshconfig.MeshConfig_JSON, - wantFormat: defaultFormatJSON, - }, - { - name: "default text format", - encoding: meshconfig.MeshConfig_TEXT, - wantFormat: EnvoyTextLogFormat, - }, - } { - tc := tc - t.Run(tc.name, func(t *testing.T) { - // Update MeshConfig - m := mesh.DefaultMeshConfig() - m.AccessLogFile = "foo" - m.AccessLogEncoding = tc.encoding - m.AccessLogFormat = tc.format - listeners := buildListeners(t, TestOptions{MeshConfig: m}, nil) - accessLogBuilder.reset() - // Validate that access log filter uses the new format. - for _, l := range listeners { - if l.AccessLog[0].Filter == nil { - t.Fatal("expected filter config in listener access log configuration") - } - // Verify listener access log. - verify(t, tc.encoding, l.AccessLog[0], tc.wantFormat) - - for _, fc := range l.FilterChains { - for _, filter := range fc.Filters { - switch filter.Name { - case wellknown.TCPProxy: - tcpConfig := &tcp.TcpProxy{} - if err := filter.GetTypedConfig().UnmarshalTo(tcpConfig); err != nil { - t.Fatal(err) - } - if tcpConfig.GetCluster() == util.BlackHoleCluster { - // Ignore the tcp_proxy filter with black hole cluster that just doesn't have access log. - continue - } - if len(tcpConfig.AccessLog) < 1 { - t.Fatalf("tcp_proxy want at least 1 access log, got 0") - } - - for _, tcpAccessLog := range tcpConfig.AccessLog { - if tcpAccessLog.Filter != nil { - t.Fatalf("tcp_proxy filter chain's accesslog filter must be empty") - } - } - - // Verify tcp proxy access log. - verify(t, tc.encoding, tcpConfig.AccessLog[0], tc.wantFormat) - case wellknown.HTTPConnectionManager: - httpConfig := &httppb.HttpConnectionManager{} - if err := filter.GetTypedConfig().UnmarshalTo(httpConfig); err != nil { - t.Fatal(err) - } - if len(httpConfig.AccessLog) < 1 { - t.Fatalf("http_connection_manager want at least 1 access log, got 0") - } - // Verify HTTP connection manager access log. - verify(t, tc.encoding, httpConfig.AccessLog[0], tc.wantFormat) - } - } - } - } - }) - } -} - -func verify(t *testing.T, encoding meshconfig.MeshConfig_AccessLogEncoding, got *accesslog.AccessLog, wantFormat string) { - cfg, _ := conversion.MessageToStruct(got.GetTypedConfig()) - if encoding == meshconfig.MeshConfig_JSON { - jsonFormat := cfg.GetFields()["log_format"].GetStructValue().GetFields()["json_format"] - jsonFormatString, _ := protomarshal.ToJSON(jsonFormat) - if jsonFormatString != wantFormat { - t.Errorf("\nwant: %s\n got: %s", wantFormat, jsonFormatString) - } - } else { - textFormatString := cfg.GetFields()["log_format"].GetStructValue().GetFields()["text_format_source"].GetStructValue(). - GetFields()["inline_string"].GetStringValue() - if textFormatString != wantFormat { - t.Errorf("\nwant: %s\n got: %s", wantFormat, textFormatString) - } - } -} - -func TestBuildAccessLogFromTelemetry(t *testing.T) { - singleCfg := &model.LoggingConfig{ - Providers: []*meshconfig.MeshConfig_ExtensionProvider{ - { - Name: "", - Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLog{ - EnvoyFileAccessLog: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider{ - Path: devStdout, - }, - }, - }, - }, - } - - singleCfgWithFilter := &model.LoggingConfig{ - Providers: []*meshconfig.MeshConfig_ExtensionProvider{ - { - Name: "", - Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLog{ - EnvoyFileAccessLog: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider{ - Path: devStdout, - }, - }, - }, - }, - Filter: &tpb.AccessLogging_Filter{ - Expression: httpCodeExpress, - }, - } - - customTextFormat := &model.LoggingConfig{ - Providers: []*meshconfig.MeshConfig_ExtensionProvider{ - { - Name: "", - Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLog{ - EnvoyFileAccessLog: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider{ - Path: devStdout, - LogFormat: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider_LogFormat{ - LogFormat: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider_LogFormat_Text{ - Text: "%LOCAL_REPLY_BODY%:%RESPONSE_CODE%:path=%REQ(:path)%\n", - }, - }, - }, - }, - }, - }, - } - - defaultJSONFormat := &model.LoggingConfig{ - Providers: []*meshconfig.MeshConfig_ExtensionProvider{ - { - Name: "", - Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLog{ - EnvoyFileAccessLog: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider{ - Path: devStdout, - LogFormat: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider_LogFormat{ - LogFormat: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider_LogFormat_Labels{ - Labels: &structpb.Struct{}, - }, - }, - }, - }, - }, - }, - } - - customLabelsFormat := &model.LoggingConfig{ - Providers: []*meshconfig.MeshConfig_ExtensionProvider{ - { - Name: "", - Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLog{ - EnvoyFileAccessLog: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider{ - Path: devStdout, - LogFormat: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider_LogFormat{ - LogFormat: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider_LogFormat_Labels{ - Labels: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "start_time": {Kind: &structpb.Value_StringValue{StringValue: "%START_TIME%"}}, - "route_name": {Kind: &structpb.Value_StringValue{StringValue: "%ROUTE_NAME%"}}, - "method": {Kind: &structpb.Value_StringValue{StringValue: "%REQ(:METHOD)%"}}, - "path": {Kind: &structpb.Value_StringValue{StringValue: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"}}, - "protocol": {Kind: &structpb.Value_StringValue{StringValue: "%PROTOCOL%"}}, - "response_code": {Kind: &structpb.Value_StringValue{StringValue: "%RESPONSE_CODE%"}}, - "response_flags": {Kind: &structpb.Value_StringValue{StringValue: "%RESPONSE_FLAGS%"}}, - "response_code_details": {Kind: &structpb.Value_StringValue{StringValue: "%RESPONSE_CODE_DETAILS%"}}, - "connection_termination_details": {Kind: &structpb.Value_StringValue{StringValue: "%CONNECTION_TERMINATION_DETAILS%"}}, - "bytes_received": {Kind: &structpb.Value_StringValue{StringValue: "%BYTES_RECEIVED%"}}, - "bytes_sent": {Kind: &structpb.Value_StringValue{StringValue: "%BYTES_SENT%"}}, - "duration": {Kind: &structpb.Value_StringValue{StringValue: "%DURATION%"}}, - "upstream_service_time": {Kind: &structpb.Value_StringValue{StringValue: "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%"}}, - "x_forwarded_for": {Kind: &structpb.Value_StringValue{StringValue: "%REQ(X-FORWARDED-FOR)%"}}, - "user_agent": {Kind: &structpb.Value_StringValue{StringValue: "%REQ(USER-AGENT)%"}}, - "request_id": {Kind: &structpb.Value_StringValue{StringValue: "%REQ(X-REQUEST-ID)%"}}, - "authority": {Kind: &structpb.Value_StringValue{StringValue: "%REQ(:AUTHORITY)%"}}, - "upstream_host": {Kind: &structpb.Value_StringValue{StringValue: "%UPSTREAM_HOST%"}}, - "upstream_cluster": {Kind: &structpb.Value_StringValue{StringValue: "%UPSTREAM_CLUSTER%"}}, - "upstream_local_address": {Kind: &structpb.Value_StringValue{StringValue: "%UPSTREAM_LOCAL_ADDRESS%"}}, - "downstream_local_address": {Kind: &structpb.Value_StringValue{StringValue: "%DOWNSTREAM_LOCAL_ADDRESS%"}}, - "downstream_remote_address": {Kind: &structpb.Value_StringValue{StringValue: "%DOWNSTREAM_REMOTE_ADDRESS%"}}, - "requested_server_name": {Kind: &structpb.Value_StringValue{StringValue: "%REQUESTED_SERVER_NAME%"}}, - }, - }, - }, - }, - }, - }, - }, - }, - } - - multiCfg := &model.LoggingConfig{ - Providers: []*meshconfig.MeshConfig_ExtensionProvider{ - { - Name: "stdout", - Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLog{ - EnvoyFileAccessLog: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider{ - Path: devStdout, - }, - }, - }, - { - Name: "stderr", - Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLog{ - EnvoyFileAccessLog: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider{ - Path: "/dev/stderr", - }, - }, - }, - }, - } - - fakeFilterStateObjects := []string{"fake-filter-state-object1", "fake-filter-state-object1"} - grpcCfg := &model.LoggingConfig{ - Providers: []*meshconfig.MeshConfig_ExtensionProvider{ - { - Name: "stdout", - Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLog{ - EnvoyFileAccessLog: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider{ - Path: devStdout, - }, - }, - }, - { - Name: "grpc-http-als", - Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyHttpAls{ - EnvoyHttpAls: &meshconfig.MeshConfig_ExtensionProvider_EnvoyHttpGrpcV3LogProvider{ - LogName: "grpc-otel-als", - Service: "otel.foo.svc.cluster.local", - Port: 9811, - AdditionalRequestHeadersToLog: []string{"fake-request-header1"}, - AdditionalResponseHeadersToLog: []string{"fake-response-header1"}, - AdditionalResponseTrailersToLog: []string{"fake-response-trailer1"}, - FilterStateObjectsToLog: fakeFilterStateObjects, - }, - }, - }, - }, - } - - grpcTCPCfg := &model.LoggingConfig{ - Providers: []*meshconfig.MeshConfig_ExtensionProvider{ - { - Name: "stdout", - Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLog{ - EnvoyFileAccessLog: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider{ - Path: devStdout, - }, - }, - }, - { - Name: "grpc-tcp-als", - Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyTcpAls{ - EnvoyTcpAls: &meshconfig.MeshConfig_ExtensionProvider_EnvoyTcpGrpcV3LogProvider{ - LogName: "grpc-tcp-otel-als", - Service: "otel.foo.svc.cluster.local", - Port: 9811, - FilterStateObjectsToLog: fakeFilterStateObjects, - }, - }, - }, - }, - } - - labels := &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "protocol": {Kind: &structpb.Value_StringValue{StringValue: "%PROTOCOL%"}}, - }, - } - - multiWithOtelCfg := &model.LoggingConfig{ - Providers: []*meshconfig.MeshConfig_ExtensionProvider{ - { - Name: "stdout", - Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLog{ - EnvoyFileAccessLog: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider{ - Path: devStdout, - }, - }, - }, - { - Name: otelEnvoyAccessLogFriendlyName, - Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyOtelAls{ - EnvoyOtelAls: &meshconfig.MeshConfig_ExtensionProvider_EnvoyOpenTelemetryLogProvider{ - Service: "otel.foo.svc.cluster.local", - Port: 9811, - LogFormat: &meshconfig.MeshConfig_ExtensionProvider_EnvoyOpenTelemetryLogProvider_LogFormat{ - Labels: labels, - }, - }, - }, - }, - }, - } - - grpcBackendClusterName := "outbound|9811||grpc-als.foo.svc.cluster.local" - grpcBackendAuthority := "grpc-als.foo.svc.cluster.local" - otelCfg := &otelaccesslog.OpenTelemetryAccessLogConfig{ - CommonConfig: &grpcaccesslog.CommonGrpcAccessLogConfig{ - LogName: otelEnvoyAccessLogFriendlyName, - GrpcService: &core.GrpcService{ - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ - ClusterName: grpcBackendClusterName, - Authority: grpcBackendAuthority, - }, - }, - }, - TransportApiVersion: core.ApiVersion_V3, - FilterStateObjectsToLog: envoyWasmStateToLog, - }, - Body: &otlpcommon.AnyValue{ - Value: &otlpcommon.AnyValue_StringValue{ - StringValue: EnvoyTextLogFormat, - }, - }, - Attributes: &otlpcommon.KeyValueList{ - Values: convertStructToAttributeKeyValues(labels.Fields), - }, - } - - clusterLookupFn = func(push *model.PushContext, service string, port int) (hostname string, cluster string, err error) { - return grpcBackendAuthority, grpcBackendClusterName, nil - } - - stdout := &fileaccesslog.FileAccessLog{ - Path: devStdout, - AccessLogFormat: &fileaccesslog.FileAccessLog_LogFormat{ - LogFormat: &core.SubstitutionFormatString{ - Format: &core.SubstitutionFormatString_TextFormatSource{ - TextFormatSource: &core.DataSource{ - Specifier: &core.DataSource_InlineString{ - InlineString: EnvoyTextLogFormat, - }, - }, - }, - }, - }, - } - - customTextOut := &fileaccesslog.FileAccessLog{ - Path: devStdout, - AccessLogFormat: &fileaccesslog.FileAccessLog_LogFormat{ - LogFormat: &core.SubstitutionFormatString{ - Format: &core.SubstitutionFormatString_TextFormatSource{ - TextFormatSource: &core.DataSource{ - Specifier: &core.DataSource_InlineString{ - InlineString: "%LOCAL_REPLY_BODY%:%RESPONSE_CODE%:path=%REQ(:path)%\n", - }, - }, - }, - }, - }, - } - - defaultJSONLabelsOut := &fileaccesslog.FileAccessLog{ - Path: devStdout, - AccessLogFormat: &fileaccesslog.FileAccessLog_LogFormat{ - LogFormat: &core.SubstitutionFormatString{ - Format: &core.SubstitutionFormatString_JsonFormat{ - JsonFormat: EnvoyJSONLogFormatIstio, - }, - }, - }, - } - - customLabelsOut := &fileaccesslog.FileAccessLog{ - Path: devStdout, - AccessLogFormat: &fileaccesslog.FileAccessLog_LogFormat{ - LogFormat: &core.SubstitutionFormatString{ - Format: &core.SubstitutionFormatString_JsonFormat{ - JsonFormat: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "start_time": {Kind: &structpb.Value_StringValue{StringValue: "%START_TIME%"}}, - "route_name": {Kind: &structpb.Value_StringValue{StringValue: "%ROUTE_NAME%"}}, - "method": {Kind: &structpb.Value_StringValue{StringValue: "%REQ(:METHOD)%"}}, - "path": {Kind: &structpb.Value_StringValue{StringValue: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"}}, - "protocol": {Kind: &structpb.Value_StringValue{StringValue: "%PROTOCOL%"}}, - "response_code": {Kind: &structpb.Value_StringValue{StringValue: "%RESPONSE_CODE%"}}, - "response_flags": {Kind: &structpb.Value_StringValue{StringValue: "%RESPONSE_FLAGS%"}}, - "response_code_details": {Kind: &structpb.Value_StringValue{StringValue: "%RESPONSE_CODE_DETAILS%"}}, - "connection_termination_details": {Kind: &structpb.Value_StringValue{StringValue: "%CONNECTION_TERMINATION_DETAILS%"}}, - "bytes_received": {Kind: &structpb.Value_StringValue{StringValue: "%BYTES_RECEIVED%"}}, - "bytes_sent": {Kind: &structpb.Value_StringValue{StringValue: "%BYTES_SENT%"}}, - "duration": {Kind: &structpb.Value_StringValue{StringValue: "%DURATION%"}}, - "upstream_service_time": {Kind: &structpb.Value_StringValue{StringValue: "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%"}}, - "x_forwarded_for": {Kind: &structpb.Value_StringValue{StringValue: "%REQ(X-FORWARDED-FOR)%"}}, - "user_agent": {Kind: &structpb.Value_StringValue{StringValue: "%REQ(USER-AGENT)%"}}, - "request_id": {Kind: &structpb.Value_StringValue{StringValue: "%REQ(X-REQUEST-ID)%"}}, - "authority": {Kind: &structpb.Value_StringValue{StringValue: "%REQ(:AUTHORITY)%"}}, - "upstream_host": {Kind: &structpb.Value_StringValue{StringValue: "%UPSTREAM_HOST%"}}, - "upstream_cluster": {Kind: &structpb.Value_StringValue{StringValue: "%UPSTREAM_CLUSTER%"}}, - "upstream_local_address": {Kind: &structpb.Value_StringValue{StringValue: "%UPSTREAM_LOCAL_ADDRESS%"}}, - "downstream_local_address": {Kind: &structpb.Value_StringValue{StringValue: "%DOWNSTREAM_LOCAL_ADDRESS%"}}, - "downstream_remote_address": {Kind: &structpb.Value_StringValue{StringValue: "%DOWNSTREAM_REMOTE_ADDRESS%"}}, - "requested_server_name": {Kind: &structpb.Value_StringValue{StringValue: "%REQUESTED_SERVER_NAME%"}}, - }, - }, - }, - }, - }, - } - - errout := &fileaccesslog.FileAccessLog{ - Path: "/dev/stderr", - AccessLogFormat: &fileaccesslog.FileAccessLog_LogFormat{ - LogFormat: &core.SubstitutionFormatString{ - Format: &core.SubstitutionFormatString_TextFormatSource{ - TextFormatSource: &core.DataSource{ - Specifier: &core.DataSource_InlineString{ - InlineString: EnvoyTextLogFormat, - }, - }, - }, - }, - }, - } - - grpcHTTPout := &grpcaccesslog.HttpGrpcAccessLogConfig{ - CommonConfig: &grpcaccesslog.CommonGrpcAccessLogConfig{ - LogName: "grpc-otel-als", - GrpcService: &core.GrpcService{ - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ - ClusterName: grpcBackendClusterName, - Authority: grpcBackendAuthority, - }, - }, - }, - TransportApiVersion: core.ApiVersion_V3, - FilterStateObjectsToLog: fakeFilterStateObjects, - }, - AdditionalRequestHeadersToLog: []string{"fake-request-header1"}, - AdditionalResponseHeadersToLog: []string{"fake-response-header1"}, - AdditionalResponseTrailersToLog: []string{"fake-response-trailer1"}, - } - - grpcTCPOut := &grpcaccesslog.TcpGrpcAccessLogConfig{ - CommonConfig: &grpcaccesslog.CommonGrpcAccessLogConfig{ - LogName: "grpc-tcp-otel-als", - GrpcService: &core.GrpcService{ - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ - ClusterName: grpcBackendClusterName, - Authority: grpcBackendAuthority, - }, - }, - }, - TransportApiVersion: core.ApiVersion_V3, - FilterStateObjectsToLog: fakeFilterStateObjects, - }, - } - - ctx := model.NewPushContext() - ctx.ServiceIndex.HostnameAndNamespace["otel-collector.foo.svc.cluster.local"] = map[string]*model.Service{ - "foo": { - Hostname: "otel-collector.foo.svc.cluster.local", - DefaultAddress: "172.217.0.0/16", - Ports: model.PortList{ - &model.Port{ - Name: "grpc-port", - Port: 3417, - Protocol: protocol.TCP, - }, - &model.Port{ - Name: "http-port", - Port: 3418, - Protocol: protocol.HTTP, - }, - }, - Resolution: model.ClientSideLB, - Attributes: model.ServiceAttributes{ - Name: "otel-collector", - Namespace: "foo", - ServiceRegistry: provider.Kubernetes, - }, - }, - } - - for _, tc := range []struct { - name string - ctx *model.PushContext - meshConfig *meshconfig.MeshConfig - spec *model.LoggingConfig - forListener bool - - expected []*accesslog.AccessLog - }{ - { - name: "single", - meshConfig: &meshconfig.MeshConfig{ - AccessLogEncoding: meshconfig.MeshConfig_TEXT, - }, - spec: singleCfg, - forListener: false, - expected: []*accesslog.AccessLog{ - { - Name: wellknown.FileAccessLog, - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(stdout)}, - }, - }, - }, - { - name: "with-filter", - meshConfig: &meshconfig.MeshConfig{ - AccessLogEncoding: meshconfig.MeshConfig_TEXT, - }, - spec: singleCfgWithFilter, - forListener: false, - expected: []*accesslog.AccessLog{ - { - Name: wellknown.FileAccessLog, - Filter: &accesslog.AccessLogFilter{ - FilterSpecifier: &accesslog.AccessLogFilter_ExtensionFilter{ - ExtensionFilter: &accesslog.ExtensionFilter{ - Name: celFilter, - ConfigType: &accesslog.ExtensionFilter_TypedConfig{TypedConfig: util.MessageToAny(httpCodeFilter)}, - }, - }, - }, - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(stdout)}, - }, - }, - }, - { - name: "tcp-with-filter", - meshConfig: &meshconfig.MeshConfig{ - AccessLogEncoding: meshconfig.MeshConfig_TEXT, - }, - spec: singleCfgWithFilter, - forListener: true, - expected: []*accesslog.AccessLog{ - { - Name: wellknown.FileAccessLog, - Filter: &accesslog.AccessLogFilter{ - FilterSpecifier: &accesslog.AccessLogFilter_AndFilter{ - AndFilter: &accesslog.AndFilter{ - Filters: []*accesslog.AccessLogFilter{ - { - FilterSpecifier: &accesslog.AccessLogFilter_ResponseFlagFilter{ - ResponseFlagFilter: &accesslog.ResponseFlagFilter{Flags: []string{"NR"}}, - }, - }, - { - FilterSpecifier: &accesslog.AccessLogFilter_ExtensionFilter{ - ExtensionFilter: &accesslog.ExtensionFilter{ - Name: celFilter, - ConfigType: &accesslog.ExtensionFilter_TypedConfig{TypedConfig: util.MessageToAny(httpCodeFilter)}, - }, - }, - }, - }, - }, - }, - }, - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(stdout)}, - }, - }, - }, - { - name: "custom-text", - meshConfig: &meshconfig.MeshConfig{ - AccessLogEncoding: meshconfig.MeshConfig_TEXT, - }, - spec: customTextFormat, - forListener: false, - expected: []*accesslog.AccessLog{ - { - Name: wellknown.FileAccessLog, - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(customTextOut)}, - }, - }, - }, - { - name: "default-labels", - meshConfig: &meshconfig.MeshConfig{ - AccessLogEncoding: meshconfig.MeshConfig_TEXT, - }, - spec: defaultJSONFormat, - forListener: false, - expected: []*accesslog.AccessLog{ - { - Name: wellknown.FileAccessLog, - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(defaultJSONLabelsOut)}, - }, - }, - }, - { - name: "custom-labels", - meshConfig: &meshconfig.MeshConfig{ - AccessLogEncoding: meshconfig.MeshConfig_TEXT, - }, - spec: customLabelsFormat, - forListener: false, - expected: []*accesslog.AccessLog{ - { - Name: wellknown.FileAccessLog, - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(customLabelsOut)}, - }, - }, - }, - { - name: "single-listener", - meshConfig: &meshconfig.MeshConfig{ - AccessLogEncoding: meshconfig.MeshConfig_TEXT, - }, - spec: singleCfg, - forListener: true, - expected: []*accesslog.AccessLog{ - { - Name: wellknown.FileAccessLog, - Filter: addAccessLogFilter(), - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(stdout)}, - }, - }, - }, - { - name: "multi", - meshConfig: &meshconfig.MeshConfig{ - AccessLogEncoding: meshconfig.MeshConfig_TEXT, - }, - spec: multiCfg, - forListener: false, - expected: []*accesslog.AccessLog{ - { - Name: wellknown.FileAccessLog, - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(stdout)}, - }, - { - Name: wellknown.FileAccessLog, - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(errout)}, - }, - }, - }, - { - name: "multi-listener", - meshConfig: &meshconfig.MeshConfig{ - AccessLogEncoding: meshconfig.MeshConfig_TEXT, - }, - spec: multiCfg, - forListener: true, - expected: []*accesslog.AccessLog{ - { - Name: wellknown.FileAccessLog, - Filter: addAccessLogFilter(), - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(stdout)}, - }, - { - Name: wellknown.FileAccessLog, - Filter: addAccessLogFilter(), - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(errout)}, - }, - }, - }, - { - name: "grpc-als", - spec: grpcCfg, - meshConfig: &meshconfig.MeshConfig{ - AccessLogEncoding: meshconfig.MeshConfig_TEXT, - }, - forListener: false, - expected: []*accesslog.AccessLog{ - { - Name: wellknown.FileAccessLog, - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(stdout)}, - }, - { - Name: wellknown.HTTPGRPCAccessLog, - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(grpcHTTPout)}, - }, - }, - }, - { - name: "grpc-tcp-als", - spec: grpcTCPCfg, - meshConfig: &meshconfig.MeshConfig{ - AccessLogEncoding: meshconfig.MeshConfig_TEXT, - }, - forListener: false, - expected: []*accesslog.AccessLog{ - { - Name: wellknown.FileAccessLog, - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(stdout)}, - }, - { - Name: tcpEnvoyALSName, - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(grpcTCPOut)}, - }, - }, - }, - { - name: "multi-with-open-telemetry", - ctx: ctx, - meshConfig: &meshconfig.MeshConfig{ - AccessLogEncoding: meshconfig.MeshConfig_TEXT, - }, - spec: multiWithOtelCfg, - forListener: true, - expected: []*accesslog.AccessLog{ - { - Name: wellknown.FileAccessLog, - Filter: addAccessLogFilter(), - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(stdout)}, - }, - { - Name: otelEnvoyALSName, - Filter: addAccessLogFilter(), - ConfigType: &accesslog.AccessLog_TypedConfig{TypedConfig: util.MessageToAny(otelCfg)}, - }, - }, - }, - } { - t.Run(tc.name, func(t *testing.T) { - got := buildAccessLogFromTelemetry(tc.ctx, tc.spec, tc.forListener) - - assert.Equal(t, tc.expected, got) - }) - } -} - -func TestAccessLogPatch(t *testing.T) { - // Regression test for https://github.com/istio/istio/issues/35778 - cg := NewConfigGenTest(t, TestOptions{ - Configs: nil, - ConfigPointers: nil, - ConfigString: ` -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: access-log-format - namespace: default -spec: - configPatches: - - applyTo: NETWORK_FILTER - match: - context: ANY - listener: - filterChain: - filter: - name: envoy.filters.network.tcp_proxy - patch: - operation: MERGE - value: - typed_config: - '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - access_log: - - name: envoy.access_loggers.stream - typed_config: - '@type': type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog - log_format: - json_format: - envoyproxy_authority: '%REQ(:AUTHORITY)%' -`, - }) - - proxy := cg.SetupProxy(nil) - l1 := cg.Listeners(proxy) - l2 := cg.Listeners(proxy) - // Make sure it doesn't change between patches - if d := cmp.Diff(l1, l2, protocmp.Transform()); d != "" { - t.Fatal(d) - } - // Make sure we have exactly 1 access log - fc := xdstest.ExtractFilterChain("virtualOutbound-blackhole", xdstest.ExtractListener("virtualOutbound", l1)) - if len(xdstest.ExtractTCPProxy(t, fc).GetAccessLog()) != 1 { - t.Fatalf("unexpected access log: %v", xdstest.ExtractTCPProxy(t, fc).GetAccessLog()) - } -} - -func TestBuildOpenTelemetryAccessLogConfig(t *testing.T) { - fakeCluster := "outbound|55680||otel-collector.monitoring.svc.cluster.local" - fakeAuthority := "otel-collector.monitoring.svc.cluster.local" - for _, tc := range []struct { - name string - logName string - clusterName string - hostname string - body string - labels *structpb.Struct - expected *otelaccesslog.OpenTelemetryAccessLogConfig - }{ - { - name: "default", - logName: otelEnvoyAccessLogFriendlyName, - clusterName: fakeCluster, - hostname: fakeAuthority, - body: EnvoyTextLogFormat, - expected: &otelaccesslog.OpenTelemetryAccessLogConfig{ - CommonConfig: &grpcaccesslog.CommonGrpcAccessLogConfig{ - LogName: otelEnvoyAccessLogFriendlyName, - GrpcService: &core.GrpcService{ - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ - ClusterName: fakeCluster, - Authority: fakeAuthority, - }, - }, - }, - TransportApiVersion: core.ApiVersion_V3, - FilterStateObjectsToLog: envoyWasmStateToLog, - }, - Body: &otlpcommon.AnyValue{ - Value: &otlpcommon.AnyValue_StringValue{ - StringValue: EnvoyTextLogFormat, - }, - }, - }, - }, - { - name: "with attrs", - logName: otelEnvoyAccessLogFriendlyName, - clusterName: fakeCluster, - hostname: fakeAuthority, - body: EnvoyTextLogFormat, - labels: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "protocol": {Kind: &structpb.Value_StringValue{StringValue: "%PROTOCOL%"}}, - }, - }, - expected: &otelaccesslog.OpenTelemetryAccessLogConfig{ - CommonConfig: &grpcaccesslog.CommonGrpcAccessLogConfig{ - LogName: otelEnvoyAccessLogFriendlyName, - GrpcService: &core.GrpcService{ - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ - ClusterName: fakeCluster, - Authority: fakeAuthority, - }, - }, - }, - TransportApiVersion: core.ApiVersion_V3, - FilterStateObjectsToLog: envoyWasmStateToLog, - }, - Body: &otlpcommon.AnyValue{ - Value: &otlpcommon.AnyValue_StringValue{ - StringValue: EnvoyTextLogFormat, - }, - }, - Attributes: &otlpcommon.KeyValueList{ - Values: []*otlpcommon.KeyValue{ - { - Key: "protocol", - Value: &otlpcommon.AnyValue{Value: &otlpcommon.AnyValue_StringValue{StringValue: "%PROTOCOL%"}}, - }, - }, - }, - }, - }, - } { - t.Run(tc.name, func(t *testing.T) { - got := buildOpenTelemetryAccessLogConfig(tc.logName, tc.hostname, tc.clusterName, tc.body, tc.labels) - assert.Equal(t, tc.expected, got) - }) - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/cluster.go b/pilot/pkg/networking/core/v1alpha3/cluster.go deleted file mode 100644 index 7ba7138b5..000000000 --- a/pilot/pkg/networking/core/v1alpha3/cluster.go +++ /dev/null @@ -1,982 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "fmt" - "math" - "strconv" - "strings" -) - -import ( - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - xdstype "github.com/envoyproxy/go-control-plane/envoy/type/v3" - "google.golang.org/protobuf/types/known/durationpb" - "google.golang.org/protobuf/types/known/structpb" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3/envoyfilter" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3/loadbalancer" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/telemetry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// deltaConfigTypes are used to detect changes and trigger delta calculations. When config updates has ONLY entries -// in this map, then delta calculation is triggered. -var deltaConfigTypes = sets.New(gvk.ServiceEntry.Kind) - -// getDefaultCircuitBreakerThresholds returns a copy of the default circuit breaker thresholds for the given traffic direction. -func getDefaultCircuitBreakerThresholds() *cluster.CircuitBreakers_Thresholds { - return &cluster.CircuitBreakers_Thresholds{ - // DefaultMaxRetries specifies the default for the Envoy circuit breaker parameter max_retries. This - // defines the maximum number of parallel retries a given Envoy will allow to the upstream cluster. Envoy defaults - // this value to 3, however that has shown to be insufficient during periods of pod churn (e.g. rolling updates), - // where multiple endpoints in a cluster are terminated. In these scenarios the circuit breaker can kick - // in before Pilot is able to deliver an updated endpoint list to Envoy, leading to client-facing 503s. - MaxRetries: &wrappers.UInt32Value{Value: math.MaxUint32}, - MaxRequests: &wrappers.UInt32Value{Value: math.MaxUint32}, - MaxConnections: &wrappers.UInt32Value{Value: math.MaxUint32}, - MaxPendingRequests: &wrappers.UInt32Value{Value: math.MaxUint32}, - TrackRemaining: true, - } -} - -// BuildClusters returns the list of clusters for the given proxy. This is the CDS output -// For outbound: Cluster for each service/subset hostname or cidr with SNI set to service hostname -// Cluster type based on resolution -// For inbound (sidecar only): Cluster for each inbound endpoint port and for each service port -func (configgen *ConfigGeneratorImpl) BuildClusters(proxy *model.Proxy, req *model.PushRequest) ([]*discovery.Resource, model.XdsLogDetails) { - // In Sotw, we care about all services. - var services []*model.Service - if features.FilterGatewayClusterConfig && proxy.Type == model.Router { - services = req.Push.GatewayServices(proxy) - } else { - services = proxy.SidecarScope.Services() - } - return configgen.buildClusters(proxy, req, services) -} - -// BuildDeltaClusters generates the deltas (add and delete) for a given proxy. Currently, only service changes are reflected with deltas. -// Otherwise, we fall back onto generating everything. -func (configgen *ConfigGeneratorImpl) BuildDeltaClusters(proxy *model.Proxy, updates *model.PushRequest, - watched *model.WatchedResource, -) ([]*discovery.Resource, []string, model.XdsLogDetails, bool) { - // if we can't use delta, fall back to generate all - if !shouldUseDelta(updates) { - cl, lg := configgen.BuildClusters(proxy, updates) - return cl, nil, lg, false - } - - var deletedClusters []string - var services []*model.Service - // holds clusters per service, keyed by hostname. - serviceClusters := make(map[string]sets.Set) - // holds service ports, keyed by hostname. - // inner map holds port and its cluster name. - servicePorts := make(map[string]map[int]string) - - for _, cluster := range watched.ResourceNames { - // WatchedResources.ResourceNames will contain the names of the clusters it is subscribed to. We can - // check with the name of our service (cluster names are in the format outbound|||). - _, _, svcHost, port := model.ParseSubsetKey(cluster) - if serviceClusters[string(svcHost)] == nil { - serviceClusters[string(svcHost)] = sets.New() - } - serviceClusters[string(svcHost)].Insert(cluster) - if servicePorts[string(svcHost)] == nil { - servicePorts[string(svcHost)] = make(map[int]string) - } - servicePorts[string(svcHost)][port] = cluster - } - - // In delta, we only care about the services that have changed. - for key := range updates.ConfigsUpdated { - // get the service that has changed. - service := updates.Push.ServiceForHostname(proxy, host.Name(key.Name)) - // if this service removed, we can conclude that it is a removed cluster. - if service == nil { - for cluster := range serviceClusters[key.Name] { - deletedClusters = append(deletedClusters, cluster) - } - } else { - services = append(services, service) - // If servicePorts has this service, that means it is old service. - if servicePorts[service.Hostname.String()] != nil { - oldPorts := servicePorts[service.Hostname.String()] - for port, cluster := range oldPorts { - // if this service port is removed, we can conclude that it is a removed cluster. - if _, exists := service.Ports.GetByPort(port); !exists { - deletedClusters = append(deletedClusters, cluster) - } - } - } - } - } - clusters, log := configgen.buildClusters(proxy, updates, services) - return clusters, deletedClusters, log, true -} - -// buildClusters builds clusters for the proxy with the services passed. -func (configgen *ConfigGeneratorImpl) buildClusters(proxy *model.Proxy, req *model.PushRequest, - services []*model.Service, -) ([]*discovery.Resource, model.XdsLogDetails) { - clusters := make([]*cluster.Cluster, 0) - resources := model.Resources{} - envoyFilterPatches := req.Push.EnvoyFilters(proxy) - cb := NewClusterBuilder(proxy, req, configgen.Cache) - instances := proxy.ServiceInstances - cacheStats := cacheStats{} - switch proxy.Type { - case model.SidecarProxy: - // Setup outbound clusters - outboundPatcher := clusterPatcher{efw: envoyFilterPatches, pctx: networking.EnvoyFilter_SIDECAR_OUTBOUND} - ob, cs := configgen.buildOutboundClusters(cb, proxy, outboundPatcher, services) - cacheStats = cacheStats.merge(cs) - resources = append(resources, ob...) - // Add a blackhole and passthrough cluster for catching traffic to unresolved routes - clusters = outboundPatcher.conditionallyAppend(clusters, nil, cb.buildBlackHoleCluster(), cb.buildDefaultPassthroughCluster()) - clusters = append(clusters, outboundPatcher.insertedClusters()...) - - // Setup inbound clusters - inboundPatcher := clusterPatcher{efw: envoyFilterPatches, pctx: networking.EnvoyFilter_SIDECAR_INBOUND} - clusters = append(clusters, configgen.buildInboundClusters(cb, proxy, instances, inboundPatcher)...) - // Pass through clusters for inbound traffic. These cluster bind loopback-ish src address to access node local service. - clusters = inboundPatcher.conditionallyAppend(clusters, nil, cb.buildInboundPassthroughClusters()...) - clusters = append(clusters, inboundPatcher.insertedClusters()...) - default: // Gateways - patcher := clusterPatcher{efw: envoyFilterPatches, pctx: networking.EnvoyFilter_GATEWAY} - ob, cs := configgen.buildOutboundClusters(cb, proxy, patcher, services) - cacheStats = cacheStats.merge(cs) - resources = append(resources, ob...) - // Gateways do not require the default passthrough cluster as they do not have original dst listeners. - clusters = patcher.conditionallyAppend(clusters, nil, cb.buildBlackHoleCluster()) - if proxy.Type == model.Router && proxy.MergedGateway != nil && proxy.MergedGateway.ContainsAutoPassthroughGateways { - clusters = append(clusters, configgen.buildOutboundSniDnatClusters(proxy, req, patcher)...) - } - clusters = append(clusters, patcher.insertedClusters()...) - } - - for _, c := range clusters { - resources = append(resources, &discovery.Resource{Name: c.Name, Resource: util.MessageToAny(c)}) - } - resources = cb.normalizeClusters(resources) - - if cacheStats.empty() { - return resources, model.DefaultXdsLogDetails - } - return resources, model.XdsLogDetails{AdditionalInfo: fmt.Sprintf("cached:%v/%v", cacheStats.hits, cacheStats.hits+cacheStats.miss)} -} - -func shouldUseDelta(updates *model.PushRequest) bool { - return updates != nil && deltaAwareConfigTypes(updates.ConfigsUpdated) && len(updates.ConfigsUpdated) > 0 -} - -// deltaAwareConfigTypes returns true if all updated configs are delta enabled. -func deltaAwareConfigTypes(cfgs map[model.ConfigKey]struct{}) bool { - for k := range cfgs { - if !deltaConfigTypes.Contains(k.Kind.Kind) { - return false - } - } - return true -} - -type cacheStats struct { - hits, miss int -} - -func (c cacheStats) empty() bool { - return c.hits == 0 && c.miss == 0 -} - -func (c cacheStats) merge(other cacheStats) cacheStats { - return cacheStats{ - hits: c.hits + other.hits, - miss: c.miss + other.miss, - } -} - -func buildClusterKey(service *model.Service, port *model.Port, cb *ClusterBuilder, proxy *model.Proxy, efKeys []string) *clusterCache { - clusterName := model.BuildSubsetKey(model.TrafficDirectionOutbound, "", service.Hostname, port.Port) - clusterKey := &clusterCache{ - clusterName: clusterName, - proxyVersion: cb.proxyVersion, - locality: cb.locality, - proxyClusterID: cb.clusterID, - proxySidecar: cb.sidecarProxy(), - proxyView: cb.proxyView, - http2: port.Protocol.IsHTTP2(), - downstreamAuto: cb.sidecarProxy() && util.IsProtocolSniffingEnabledForOutboundPort(port), - supportsIPv4: cb.supportsIPv4, - service: service, - destinationRule: proxy.SidecarScope.DestinationRule(model.TrafficDirectionOutbound, proxy, service.Hostname), - envoyFilterKeys: efKeys, - metadataCerts: cb.metadataCerts, - peerAuthVersion: cb.req.Push.AuthnPolicies.GetVersion(), - serviceAccounts: cb.req.Push.ServiceAccounts[service.Hostname][port.Port], - } - return clusterKey -} - -// buildOutboundClusters generates all outbound (including subsets) clusters for a given proxy. -func (configgen *ConfigGeneratorImpl) buildOutboundClusters(cb *ClusterBuilder, proxy *model.Proxy, cp clusterPatcher, - services []*model.Service, -) ([]*discovery.Resource, cacheStats) { - resources := make([]*discovery.Resource, 0) - efKeys := cp.efw.Keys() - hit, miss := 0, 0 - for _, service := range services { - for _, port := range service.Ports { - if port.Protocol == protocol.UDP { - continue - } - clusterKey := buildClusterKey(service, port, cb, proxy, efKeys) - cached, allFound := cb.getAllCachedSubsetClusters(*clusterKey) - if allFound && !features.EnableUnsafeAssertions { - hit += len(cached) - resources = append(resources, cached...) - continue - } - miss += len(cached) - - // We have a cache miss, so we will re-generate the cluster and later store it in the cache. - lbEndpoints := cb.buildLocalityLbEndpoints(clusterKey.proxyView, service, port.Port, nil) - - // create default cluster - discoveryType := convertResolution(cb.proxyType, service) - defaultCluster := cb.buildDefaultCluster(clusterKey.clusterName, discoveryType, lbEndpoints, model.TrafficDirectionOutbound, port, service, nil) - if defaultCluster == nil { - continue - } - // If stat name is configured, build the alternate stats name. - if len(cb.req.Push.Mesh.OutboundClusterStatName) != 0 { - defaultCluster.cluster.AltStatName = telemetry.BuildStatPrefix(cb.req.Push.Mesh.OutboundClusterStatName, - string(service.Hostname), "", port, &service.Attributes) - } - - subsetClusters := cb.applyDestinationRule(defaultCluster, DefaultClusterMode, service, port, - clusterKey.proxyView, clusterKey.destinationRule, clusterKey.serviceAccounts) - - if patched := cp.applyResource(nil, defaultCluster.build()); patched != nil { - resources = append(resources, patched) - if features.EnableCDSCaching { - cb.cache.Add(clusterKey, cb.req, patched) - } - } - for _, ss := range subsetClusters { - if patched := cp.applyResource(nil, ss); patched != nil { - resources = append(resources, patched) - if features.EnableCDSCaching { - nk := *clusterKey - nk.clusterName = ss.Name - cb.cache.Add(&nk, cb.req, patched) - } - } - } - } - } - - return resources, cacheStats{hits: hit, miss: miss} -} - -type clusterPatcher struct { - efw *model.EnvoyFilterWrapper - pctx networking.EnvoyFilter_PatchContext -} - -func (p clusterPatcher) applyResource(hosts []host.Name, c *cluster.Cluster) *discovery.Resource { - cluster := p.apply(hosts, c) - if cluster == nil { - return nil - } - return &discovery.Resource{Name: cluster.Name, Resource: util.MessageToAny(cluster)} -} - -func (p clusterPatcher) apply(hosts []host.Name, c *cluster.Cluster) *cluster.Cluster { - if !envoyfilter.ShouldKeepCluster(p.pctx, p.efw, c, hosts) { - return nil - } - return envoyfilter.ApplyClusterMerge(p.pctx, p.efw, c, hosts) -} - -func (p clusterPatcher) conditionallyAppend(l []*cluster.Cluster, hosts []host.Name, clusters ...*cluster.Cluster) []*cluster.Cluster { - if !p.hasPatches() { - return append(l, clusters...) - } - for _, c := range clusters { - if patched := p.apply(hosts, c); patched != nil { - l = append(l, patched) - } - } - return l -} - -func (p clusterPatcher) insertedClusters() []*cluster.Cluster { - return envoyfilter.InsertedClusters(p.pctx, p.efw) -} - -func (p clusterPatcher) hasPatches() bool { - return p.efw != nil && len(p.efw.Patches[networking.EnvoyFilter_CLUSTER]) > 0 -} - -// SniDnat clusters do not have any TLS setting, as they simply forward traffic to upstream -// All SniDnat clusters are internal services in the mesh. -// TODO enable cache - there is no blockers here, skipped to simplify the original caching implementation -func (configgen *ConfigGeneratorImpl) buildOutboundSniDnatClusters(proxy *model.Proxy, req *model.PushRequest, - cp clusterPatcher, -) []*cluster.Cluster { - clusters := make([]*cluster.Cluster, 0) - cb := NewClusterBuilder(proxy, req, nil) - - proxyView := proxy.GetView() - - for _, service := range proxy.SidecarScope.Services() { - if service.MeshExternal { - continue - } - - destRule := proxy.SidecarScope.DestinationRule(model.TrafficDirectionOutbound, proxy, service.Hostname) - for _, port := range service.Ports { - if port.Protocol == protocol.UDP { - continue - } - lbEndpoints := cb.buildLocalityLbEndpoints(proxyView, service, port.Port, nil) - - // create default cluster - discoveryType := convertResolution(cb.proxyType, service) - - clusterName := model.BuildDNSSrvSubsetKey(model.TrafficDirectionOutbound, "", - service.Hostname, port.Port) - defaultCluster := cb.buildDefaultCluster(clusterName, discoveryType, lbEndpoints, model.TrafficDirectionOutbound, port, service, nil) - if defaultCluster == nil { - continue - } - subsetClusters := cb.applyDestinationRule(defaultCluster, SniDnatClusterMode, service, port, proxyView, destRule, nil) - clusters = cp.conditionallyAppend(clusters, nil, defaultCluster.build()) - clusters = cp.conditionallyAppend(clusters, nil, subsetClusters...) - } - } - - return clusters -} - -func buildInboundLocalityLbEndpoints(bind string, port uint32) []*endpoint.LocalityLbEndpoints { - if bind == "" { - return nil - } - address := util.BuildAddress(bind, port) - lbEndpoint := &endpoint.LbEndpoint{ - HostIdentifier: &endpoint.LbEndpoint_Endpoint{ - Endpoint: &endpoint.Endpoint{ - Address: address, - }, - }, - } - return []*endpoint.LocalityLbEndpoints{ - { - LbEndpoints: []*endpoint.LbEndpoint{lbEndpoint}, - }, - } -} - -func (configgen *ConfigGeneratorImpl) buildInboundClusters(cb *ClusterBuilder, proxy *model.Proxy, instances []*model.ServiceInstance, - cp clusterPatcher, -) []*cluster.Cluster { - clusters := make([]*cluster.Cluster, 0) - - // The inbound clusters for a node depends on whether the node has a SidecarScope with inbound listeners - // or not. If the node has a sidecarscope with ingress listeners, we only return clusters corresponding - // to those listeners i.e. clusters made out of the defaultEndpoint field. - // If the node has no sidecarScope and has interception mode set to NONE, then we should skip the inbound - // clusters, because there would be no corresponding inbound listeners - sidecarScope := proxy.SidecarScope - noneMode := proxy.GetInterceptionMode() == model.InterceptionNone - - _, actualLocalHost := getActualWildcardAndLocalHost(proxy) - - // No user supplied sidecar scope or the user supplied one has no ingress listeners - if !sidecarScope.HasIngressListener() { - // We should not create inbound listeners in NONE mode based on the service instances - // Doing so will prevent the workloads from starting as they would be listening on the same port - // Users are required to provide the sidecar config to define the inbound listeners - if noneMode { - return nil - } - - clustersToBuild := make(map[int][]*model.ServiceInstance) - for _, instance := range instances { - // For service instances with the same port, - // we still need to capture all the instances on this port, as its required to populate telemetry metadata - // The first instance will be used as the "primary" instance; this means if we have an conflicts between - // Services the first one wins - ep := int(instance.Endpoint.EndpointPort) - clustersToBuild[ep] = append(clustersToBuild[ep], instance) - } - - bind := actualLocalHost - if features.EnableInboundPassthrough { - bind = "" - } - // For each workload port, we will construct a cluster - for epPort, instances := range clustersToBuild { - // The inbound cluster port equals to endpoint port. - localCluster := cb.buildInboundClusterForPortOrUDS(epPort, bind, proxy, instances[0], instances) - // If inbound cluster match has service, we should see if it matches with any host name across all instances. - hosts := make([]host.Name, 0, len(instances)) - for _, si := range instances { - hosts = append(hosts, si.Service.Hostname) - } - clusters = cp.conditionallyAppend(clusters, hosts, localCluster.build()) - } - return clusters - } - - for _, ingressListener := range sidecarScope.Sidecar.Ingress { - // LDS would have setup the inbound clusters - // as inbound|portNumber|portName|Hostname[or]SidecarScopeID - listenPort := &model.Port{ - Port: int(ingressListener.Port.Number), - Protocol: protocol.Parse(ingressListener.Port.Protocol), - Name: ingressListener.Port.Name, - } - - // Set up the endpoint. By default, we set this empty which will use ORIGINAL_DST passthrough. - // This can be overridden by ingress.defaultEndpoint. - // * 127.0.0.1: send to localhost - // * 0.0.0.0: send to INSTANCE_IP - // * unix:///...: send to configured unix domain socket - endpointAddress := "" - port := 0 - if strings.HasPrefix(ingressListener.DefaultEndpoint, model.UnixAddressPrefix) { - // this is a UDS endpoint. assign it as is - endpointAddress = ingressListener.DefaultEndpoint - } else if len(ingressListener.DefaultEndpoint) > 0 { - // parse the ip, port. Validation guarantees presence of : - parts := strings.Split(ingressListener.DefaultEndpoint, ":") - if len(parts) < 2 { - continue - } - var err error - if port, err = strconv.Atoi(parts[1]); err != nil { - continue - } - if parts[0] == model.PodIPAddressPrefix { - endpointAddress = cb.proxyIPAddresses[0] - } else if parts[0] == model.LocalhostAddressPrefix { - endpointAddress = actualLocalHost - } - } - - // Find the service instance that corresponds to this ingress listener by looking - // for a service instance that matches this ingress port as this will allow us - // to generate the right cluster name that LDS expects inbound|portNumber|portName|Hostname - instance := findOrCreateServiceInstance(instances, ingressListener, sidecarScope.Name, sidecarScope.Namespace) - instance.Endpoint.Address = endpointAddress - instance.ServicePort = listenPort - instance.Endpoint.ServicePortName = listenPort.Name - instance.Endpoint.EndpointPort = uint32(port) - - localCluster := cb.buildInboundClusterForPortOrUDS(int(ingressListener.Port.Number), endpointAddress, proxy, instance, nil) - clusters = cp.conditionallyAppend(clusters, []host.Name{instance.Service.Hostname}, localCluster.build()) - } - - return clusters -} - -func findOrCreateServiceInstance(instances []*model.ServiceInstance, - ingressListener *networking.IstioIngressListener, sidecar string, sidecarns string, -) *model.ServiceInstance { - for _, realInstance := range instances { - if realInstance.Endpoint.EndpointPort == ingressListener.Port.Number { - // We need to create a copy of the instance, as it is modified later while building clusters/listeners. - return realInstance.DeepCopy() - } - } - // We didn't find a matching instance. Create a dummy one because we need the right - // params to generate the right cluster name i.e. inbound|portNumber|portName|SidecarScopeID - which is uniformly generated by LDS/CDS. - return &model.ServiceInstance{ - Service: &model.Service{ - Hostname: host.Name(sidecar + "." + sidecarns), - Attributes: model.ServiceAttributes{ - Name: sidecar, - // This will ensure that the right AuthN policies are selected - Namespace: sidecarns, - }, - }, - Endpoint: &model.IstioEndpoint{ - EndpointPort: ingressListener.Port.Number, - }, - } -} - -func convertResolution(proxyType model.NodeType, service *model.Service) cluster.Cluster_DiscoveryType { - switch service.Resolution { - case model.ClientSideLB: - return cluster.Cluster_EDS - case model.DNSLB: - return cluster.Cluster_STRICT_DNS - case model.DNSRoundRobinLB: - return cluster.Cluster_LOGICAL_DNS - case model.Passthrough: - // Gateways cannot use passthrough clusters. So fallback to EDS - if proxyType == model.SidecarProxy { - if service.Attributes.ServiceRegistry == provider.Kubernetes && features.EnableEDSForHeadless { - return cluster.Cluster_EDS - } - - return cluster.Cluster_ORIGINAL_DST - } - return cluster.Cluster_EDS - default: - return cluster.Cluster_EDS - } -} - -// SelectTrafficPolicyComponents returns the components of TrafficPolicy that should be used for given port. -func selectTrafficPolicyComponents(policy *networking.TrafficPolicy) ( - *networking.ConnectionPoolSettings, *networking.OutlierDetection, *networking.LoadBalancerSettings, *networking.ClientTLSSettings, -) { - if policy == nil { - return nil, nil, nil, nil - } - connectionPool := policy.ConnectionPool - outlierDetection := policy.OutlierDetection - loadBalancer := policy.LoadBalancer - tls := policy.Tls - - // Check if CA Certificate should be System CA Certificate - if features.VerifyCertAtClient && tls != nil && tls.CaCertificates == "" { - tls.CaCertificates = "system" - } - - return connectionPool, outlierDetection, loadBalancer, tls -} - -// ClusterMode defines whether the cluster is being built for SNI-DNATing (sni passthrough) or not -type ClusterMode string - -const ( - // SniDnatClusterMode indicates cluster is being built for SNI dnat mode - SniDnatClusterMode ClusterMode = "sni-dnat" - // DefaultClusterMode indicates usual cluster with mTLS et al - DefaultClusterMode ClusterMode = "outbound" -) - -type buildClusterOpts struct { - mesh *meshconfig.MeshConfig - mutable *MutableCluster - policy *networking.TrafficPolicy - port *model.Port - serviceAccounts []string - serviceInstances []*model.ServiceInstance - // Used for traffic across multiple network clusters - // the east-west gateway in a remote cluster will use this value to route - // traffic to the appropriate service - istioMtlsSni string - clusterMode ClusterMode - direction model.TrafficDirection - meshExternal bool - serviceMTLSMode model.MutualTLSMode - // Indicates the service registry of the cluster being built. - serviceRegistry provider.ID - // Indicates if the destionationRule has a workloadSelector - isDrWithSelector bool -} - -type upgradeTuple struct { - meshdefault meshconfig.MeshConfig_H2UpgradePolicy - override networking.ConnectionPoolSettings_HTTPSettings_H2UpgradePolicy -} - -func applyTCPKeepalive(mesh *meshconfig.MeshConfig, c *cluster.Cluster, tcp *networking.ConnectionPoolSettings_TCPSettings) { - // Apply mesh wide TCP keepalive if available. - setKeepAliveSettings(c, mesh.TcpKeepalive) - - // Apply/Override individual attributes with DestinationRule TCP keepalive if set. - if tcp != nil { - setKeepAliveSettings(c, tcp.TcpKeepalive) - } -} - -func setKeepAliveSettings(c *cluster.Cluster, keepalive *networking.ConnectionPoolSettings_TCPSettings_TcpKeepalive) { - if keepalive == nil { - return - } - // Start with empty tcp_keepalive, which would set SO_KEEPALIVE on the socket with OS default values. - if c.UpstreamConnectionOptions == nil { - c.UpstreamConnectionOptions = &cluster.UpstreamConnectionOptions{ - TcpKeepalive: &core.TcpKeepalive{}, - } - } - if keepalive.Probes > 0 { - c.UpstreamConnectionOptions.TcpKeepalive.KeepaliveProbes = &wrappers.UInt32Value{Value: keepalive.Probes} - } - - if keepalive.Time != nil { - c.UpstreamConnectionOptions.TcpKeepalive.KeepaliveTime = &wrappers.UInt32Value{Value: uint32(keepalive.Time.Seconds)} - } - - if keepalive.Interval != nil { - c.UpstreamConnectionOptions.TcpKeepalive.KeepaliveInterval = &wrappers.UInt32Value{Value: uint32(keepalive.Interval.Seconds)} - } -} - -// FIXME: there isn't a way to distinguish between unset values and zero values -func applyOutlierDetection(c *cluster.Cluster, outlier *networking.OutlierDetection) { - if outlier == nil { - return - } - - out := &cluster.OutlierDetection{} - - // SuccessRate based outlier detection should be disabled. - out.EnforcingSuccessRate = &wrappers.UInt32Value{Value: 0} - - if e := outlier.Consecutive_5XxErrors; e != nil { - v := e.GetValue() - - out.Consecutive_5Xx = &wrappers.UInt32Value{Value: v} - - if v > 0 { - v = 100 - } - out.EnforcingConsecutive_5Xx = &wrappers.UInt32Value{Value: v} - } - if e := outlier.ConsecutiveGatewayErrors; e != nil { - v := e.GetValue() - - out.ConsecutiveGatewayFailure = &wrappers.UInt32Value{Value: v} - - if v > 0 { - v = 100 - } - out.EnforcingConsecutiveGatewayFailure = &wrappers.UInt32Value{Value: v} - } - - if outlier.Interval != nil { - out.Interval = outlier.Interval - } - if outlier.BaseEjectionTime != nil { - out.BaseEjectionTime = outlier.BaseEjectionTime - } - if outlier.MaxEjectionPercent > 0 { - out.MaxEjectionPercent = &wrappers.UInt32Value{Value: uint32(outlier.MaxEjectionPercent)} - } - - if outlier.SplitExternalLocalOriginErrors { - out.SplitExternalLocalOriginErrors = true - if outlier.ConsecutiveLocalOriginFailures.GetValue() > 0 { - out.ConsecutiveLocalOriginFailure = &wrappers.UInt32Value{Value: outlier.ConsecutiveLocalOriginFailures.Value} - out.EnforcingConsecutiveLocalOriginFailure = &wrappers.UInt32Value{Value: 100} - } - // SuccessRate based outlier detection should be disabled. - out.EnforcingLocalOriginSuccessRate = &wrappers.UInt32Value{Value: 0} - } - - c.OutlierDetection = out - - // Disable panic threshold by default as its not typically applicable in k8s environments - // with few pods per service. - // To do so, set the healthy_panic_threshold field even if its value is 0 (defaults to 50). - // FIXME: we can't distinguish between it being unset or being explicitly set to 0 - minHealthPercent := outlier.MinHealthPercent - if minHealthPercent >= 0 { - if c.CommonLbConfig == nil { - c.CommonLbConfig = &cluster.Cluster_CommonLbConfig{} - } - // When we are sending unhealthy endpoints, we should disble Panic Threshold. Otherwise - // Envoy will send traffic to "Unready" pods when the percentage of healthy hosts fall - // below minimum health percentage. - if features.SendUnhealthyEndpoints { - minHealthPercent = 0 - } - c.CommonLbConfig.HealthyPanicThreshold = &xdstype.Percent{Value: float64(minHealthPercent)} - } -} - -func defaultLBAlgorithm() cluster.Cluster_LbPolicy { - if features.EnableLegacyLBAlgorithmDefault { - return cluster.Cluster_ROUND_ROBIN - } - return cluster.Cluster_LEAST_REQUEST -} - -func applyLoadBalancer(c *cluster.Cluster, lb *networking.LoadBalancerSettings, port *model.Port, - locality *core.Locality, proxyLabels map[string]string, meshConfig *meshconfig.MeshConfig, -) { - // Disable panic threshold when SendUnhealthyEndpoints is enabled as enabling it "may" send traffic to unready - // end points when load balancer is in panic mode. - if features.SendUnhealthyEndpoints { - if c.CommonLbConfig == nil { - c.CommonLbConfig = &cluster.Cluster_CommonLbConfig{} - } - c.CommonLbConfig.HealthyPanicThreshold = &xdstype.Percent{Value: 0} - } - localityLbSetting := loadbalancer.GetLocalityLbSetting(meshConfig.GetLocalityLbSetting(), lb.GetLocalityLbSetting()) - if localityLbSetting != nil { - if c.CommonLbConfig == nil { - c.CommonLbConfig = &cluster.Cluster_CommonLbConfig{} - } - c.CommonLbConfig.LocalityConfigSpecifier = &cluster.Cluster_CommonLbConfig_LocalityWeightedLbConfig_{ - LocalityWeightedLbConfig: &cluster.Cluster_CommonLbConfig_LocalityWeightedLbConfig{}, - } - } - // Use locality lb settings from load balancer settings if present, else use mesh wide locality lb settings - applyLocalityLBSetting(locality, proxyLabels, c, localityLbSetting) - - if c.GetType() == cluster.Cluster_ORIGINAL_DST { - c.LbPolicy = cluster.Cluster_CLUSTER_PROVIDED - return - } - - // Redis protocol must be defaulted with MAGLEV to benefit from client side sharding. - if features.EnableRedisFilter && port != nil && port.Protocol == protocol.Redis { - c.LbPolicy = cluster.Cluster_MAGLEV - return - } - - // DO not do if else here. since lb.GetSimple returns a enum value (not pointer). - switch lb.GetSimple() { - // nolint: staticcheck - case networking.LoadBalancerSettings_LEAST_CONN, networking.LoadBalancerSettings_LEAST_REQUEST: - applyLeastRequestLoadBalancer(c, lb) - case networking.LoadBalancerSettings_RANDOM: - c.LbPolicy = cluster.Cluster_RANDOM - case networking.LoadBalancerSettings_ROUND_ROBIN: - applyRoundRobinLoadBalancer(c, lb) - case networking.LoadBalancerSettings_PASSTHROUGH: - c.LbPolicy = cluster.Cluster_CLUSTER_PROVIDED - c.ClusterDiscoveryType = &cluster.Cluster_Type{Type: cluster.Cluster_ORIGINAL_DST} - default: - applySimpleDefaultLoadBalancer(c, lb) - } - - ApplyRingHashLoadBalancer(c, lb) -} - -// applySimpleDefaultLoadBalancer will set the DefaultLBPolicy and create an LbConfig if used in LoadBalancerSettings -func applySimpleDefaultLoadBalancer(c *cluster.Cluster, loadbalancer *networking.LoadBalancerSettings) { - c.LbPolicy = defaultLBAlgorithm() - switch c.LbPolicy { - case cluster.Cluster_ROUND_ROBIN: - applyRoundRobinLoadBalancer(c, loadbalancer) - case cluster.Cluster_LEAST_REQUEST: - applyLeastRequestLoadBalancer(c, loadbalancer) - } -} - -// applyRoundRobinLoadBalancer will set the LbPolicy and create an LbConfig for ROUND_ROBIN if used in LoadBalancerSettings -func applyRoundRobinLoadBalancer(c *cluster.Cluster, loadbalancer *networking.LoadBalancerSettings) { - c.LbPolicy = cluster.Cluster_ROUND_ROBIN - - if loadbalancer.GetWarmupDurationSecs() != nil { - c.LbConfig = &cluster.Cluster_RoundRobinLbConfig_{ - RoundRobinLbConfig: &cluster.Cluster_RoundRobinLbConfig{ - SlowStartConfig: setSlowStartConfig(loadbalancer.GetWarmupDurationSecs()), - }, - } - } -} - -// applyLeastRequestLoadBalancer will set the LbPolicy and create an LbConfig for LEAST_REQUEST if used in LoadBalancerSettings -func applyLeastRequestLoadBalancer(c *cluster.Cluster, loadbalancer *networking.LoadBalancerSettings) { - c.LbPolicy = cluster.Cluster_LEAST_REQUEST - - if loadbalancer.GetWarmupDurationSecs() != nil { - c.LbConfig = &cluster.Cluster_LeastRequestLbConfig_{ - LeastRequestLbConfig: &cluster.Cluster_LeastRequestLbConfig{ - SlowStartConfig: setSlowStartConfig(loadbalancer.GetWarmupDurationSecs()), - }, - } - } -} - -// setSlowStartConfig will set the warmupDurationSecs for LEAST_REQUEST and ROUND_ROBIN if provided in DestinationRule -func setSlowStartConfig(dur *durationpb.Duration) *cluster.Cluster_SlowStartConfig { - return &cluster.Cluster_SlowStartConfig{ - SlowStartWindow: dur, - } -} - -// ApplyRingHashLoadBalancer will set the LbPolicy and create an LbConfig for RING_HASH if used in LoadBalancerSettings -func ApplyRingHashLoadBalancer(c *cluster.Cluster, lb *networking.LoadBalancerSettings) { - consistentHash := lb.GetConsistentHash() - if consistentHash == nil { - return - } - - // TODO MinimumRingSize is an int, and zero could potentially be a valid value - // unable to distinguish between set and unset case currently GregHanson - // 1024 is the default value for envoy - minRingSize := &wrappers.UInt64Value{Value: 1024} - if consistentHash.MinimumRingSize != 0 { - minRingSize = &wrappers.UInt64Value{Value: consistentHash.GetMinimumRingSize()} - } - c.LbPolicy = cluster.Cluster_RING_HASH - c.LbConfig = &cluster.Cluster_RingHashLbConfig_{ - RingHashLbConfig: &cluster.Cluster_RingHashLbConfig{ - MinimumRingSize: minRingSize, - }, - } -} - -func applyLocalityLBSetting(locality *core.Locality, proxyLabels map[string]string, cluster *cluster.Cluster, - localityLB *networking.LocalityLoadBalancerSetting, -) { - // Failover should only be applied with outlier detection, or traffic will never failover. - enabledFailover := cluster.OutlierDetection != nil - if cluster.LoadAssignment != nil { - // TODO: enable failoverPriority for `STRICT_DNS` cluster type - loadbalancer.ApplyLocalityLBSetting(cluster.LoadAssignment, nil, locality, proxyLabels, localityLB, enabledFailover) - } -} - -func addTelemetryMetadata(opts buildClusterOpts, service *model.Service, direction model.TrafficDirection, instances []*model.ServiceInstance) { - if !features.EnableTelemetryLabel { - return - } - if opts.mutable.cluster == nil { - return - } - if direction == model.TrafficDirectionInbound && (opts.serviceInstances == nil || - len(opts.serviceInstances) == 0 || opts.port == nil) { - // At inbound, port and local service instance has to be provided - return - } - if direction == model.TrafficDirectionOutbound && service == nil { - // At outbound, the service corresponding to the cluster has to be provided. - return - } - - im := getOrCreateIstioMetadata(opts.mutable.cluster) - - // Add services field into istio metadata - im.Fields["services"] = &structpb.Value{ - Kind: &structpb.Value_ListValue{ - ListValue: &structpb.ListValue{ - Values: []*structpb.Value{}, - }, - }, - } - - svcMetaList := im.Fields["services"].GetListValue() - - // Add service related metadata. This will be consumed by telemetry v2 filter for metric labels. - if direction == model.TrafficDirectionInbound { - // For inbound cluster, add all services on the cluster port - have := make(map[host.Name]bool) - for _, svc := range instances { - if svc.ServicePort.Port != opts.port.Port { - // If the service port is different from the the port of the cluster that is being built, - // skip adding telemetry metadata for the service to the cluster. - continue - } - if _, ok := have[svc.Service.Hostname]; ok { - // Skip adding metadata for instance with the same host name. - // This could happen when a service has multiple IPs. - continue - } - svcMetaList.Values = append(svcMetaList.Values, buildServiceMetadata(svc.Service)) - have[svc.Service.Hostname] = true - } - } else if direction == model.TrafficDirectionOutbound { - // For outbound cluster, add telemetry metadata based on the service that the cluster is built for. - svcMetaList.Values = append(svcMetaList.Values, buildServiceMetadata(service)) - } -} - -// Insert the original port into the istio metadata. The port is used in BTS delivered from client sidecar to server sidecar. -// Server side car uses this port after de-multiplexed from tunnel. -func addNetworkingMetadata(opts buildClusterOpts, service *model.Service, direction model.TrafficDirection) { - if opts.mutable == nil || direction == model.TrafficDirectionInbound { - return - } - if service == nil { - // At outbound, the service corresponding to the cluster has to be provided. - return - } - - if port, ok := service.Ports.GetByPort(opts.port.Port); ok { - im := getOrCreateIstioMetadata(opts.mutable.cluster) - - // Add original_port field into istio metadata - // Endpoint could override this port but the chance should be small. - im.Fields["default_original_port"] = &structpb.Value{ - Kind: &structpb.Value_NumberValue{ - NumberValue: float64(port.Port), - }, - } - } -} - -// Build a struct which contains service metadata and will be added into cluster label. -func buildServiceMetadata(svc *model.Service) *structpb.Value { - return &structpb.Value{ - Kind: &structpb.Value_StructValue{ - StructValue: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - // service fqdn - "host": { - Kind: &structpb.Value_StringValue{ - StringValue: string(svc.Hostname), - }, - }, - // short name of the service - "name": { - Kind: &structpb.Value_StringValue{ - StringValue: svc.Attributes.Name, - }, - }, - // namespace of the service - "namespace": { - Kind: &structpb.Value_StringValue{ - StringValue: svc.Attributes.Namespace, - }, - }, - }, - }, - }, - } -} - -func getOrCreateIstioMetadata(cluster *cluster.Cluster) *structpb.Struct { - if cluster.Metadata == nil { - cluster.Metadata = &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{}, - } - } - // Create Istio metadata if does not exist yet - if _, ok := cluster.Metadata.FilterMetadata[util.IstioMetadataKey]; !ok { - cluster.Metadata.FilterMetadata[util.IstioMetadataKey] = &structpb.Struct{ - Fields: map[string]*structpb.Value{}, - } - } - return cluster.Metadata.FilterMetadata[util.IstioMetadataKey] -} diff --git a/pilot/pkg/networking/core/v1alpha3/cluster_builder.go b/pilot/pkg/networking/core/v1alpha3/cluster_builder.go deleted file mode 100644 index 5213791cd..000000000 --- a/pilot/pkg/networking/core/v1alpha3/cluster_builder.go +++ /dev/null @@ -1,1364 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "crypto/md5" - "encoding/hex" - "fmt" - "math" - "sort" - "strconv" - "strings" -) - -import ( - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" - auth "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - http "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - any "google.golang.org/protobuf/types/known/anypb" - "google.golang.org/protobuf/types/known/durationpb" - "google.golang.org/protobuf/types/known/structpb" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/telemetry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - authn_model "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - xdsfilters "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/filters" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - istio_cluster "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/security" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -var istioMtlsTransportSocketMatch = &structpb.Struct{ - Fields: map[string]*structpb.Value{ - model.TLSModeLabelShortname: {Kind: &structpb.Value_StringValue{StringValue: model.IstioMutualTLSModeLabel}}, - }, -} - -// h2UpgradeMap specifies the truth table when upgrade takes place. -var h2UpgradeMap = map[upgradeTuple]bool{ - {meshconfig.MeshConfig_DO_NOT_UPGRADE, networking.ConnectionPoolSettings_HTTPSettings_UPGRADE}: true, - {meshconfig.MeshConfig_DO_NOT_UPGRADE, networking.ConnectionPoolSettings_HTTPSettings_DO_NOT_UPGRADE}: false, - {meshconfig.MeshConfig_DO_NOT_UPGRADE, networking.ConnectionPoolSettings_HTTPSettings_DEFAULT}: false, - {meshconfig.MeshConfig_UPGRADE, networking.ConnectionPoolSettings_HTTPSettings_UPGRADE}: true, - {meshconfig.MeshConfig_UPGRADE, networking.ConnectionPoolSettings_HTTPSettings_DO_NOT_UPGRADE}: false, - {meshconfig.MeshConfig_UPGRADE, networking.ConnectionPoolSettings_HTTPSettings_DEFAULT}: true, -} - -// passthroughHttpProtocolOptions are http protocol options used for pass through clusters. -// nolint -var passthroughHttpProtocolOptions = util.MessageToAny(&http.HttpProtocolOptions{ - UpstreamProtocolOptions: &http.HttpProtocolOptions_UseDownstreamProtocolConfig{ - UseDownstreamProtocolConfig: &http.HttpProtocolOptions_UseDownstreamHttpConfig{ - HttpProtocolOptions: &core.Http1ProtocolOptions{}, - Http2ProtocolOptions: http2ProtocolOptions(), - }, - }, -}) - -// MutableCluster wraps Cluster object along with options. -type MutableCluster struct { - cluster *cluster.Cluster - // httpProtocolOptions stores the HttpProtocolOptions which will be marshaled when build is called. - httpProtocolOptions *http.HttpProtocolOptions -} - -// metadataCerts hosts client certificate related metadata specified in proxy metadata. -type metadataCerts struct { - // tlsClientCertChain is the absolute path to client cert-chain file - tlsClientCertChain string - // tlsClientKey is the absolute path to client private key file - tlsClientKey string - // tlsClientRootCert is the absolute path to client root cert file - tlsClientRootCert string -} - -// ClusterBuilder interface provides an abstraction for building Envoy Clusters. -type ClusterBuilder struct { - // Proxy related information used to build clusters. - serviceInstances []*model.ServiceInstance // Service instances of Proxy. - metadataCerts *metadataCerts // Client certificates specified in metadata. - clusterID string // Cluster in which proxy is running. - proxyID string // Identifier that uniquely identifies a proxy. - proxyVersion string // Version of Proxy. - proxyType model.NodeType // Indicates whether the proxy is sidecar or gateway. - sidecarScope *model.SidecarScope // Computed sidecar for the proxy. - passThroughBindIP string // Passthrough IP to be used while building clusters. - supportsIPv4 bool // Whether Proxy IPs has IPv4 address. - supportsIPv6 bool // Whether Proxy IPs has IPv6 address. - locality *core.Locality // Locality information of proxy. - proxyLabels map[string]string // Proxy labels. - proxyView model.ProxyView // Proxy view of endpoints. - proxyIPAddresses []string // IP addresses on which proxy is listening on. - configNamespace string // Proxy config namespace. - // PushRequest to look for updates. - req *model.PushRequest - cache model.XdsCache -} - -// NewClusterBuilder builds an instance of ClusterBuilder. -func NewClusterBuilder(proxy *model.Proxy, req *model.PushRequest, cache model.XdsCache) *ClusterBuilder { - cb := &ClusterBuilder{ - serviceInstances: proxy.ServiceInstances, - proxyID: proxy.ID, - proxyType: proxy.Type, - proxyVersion: proxy.Metadata.IstioVersion, - sidecarScope: proxy.SidecarScope, - passThroughBindIP: getPassthroughBindIP(proxy), - supportsIPv4: proxy.SupportsIPv4(), - supportsIPv6: proxy.SupportsIPv6(), - locality: proxy.Locality, - proxyLabels: proxy.Metadata.Labels, - proxyView: proxy.GetView(), - proxyIPAddresses: proxy.IPAddresses, - configNamespace: proxy.ConfigNamespace, - req: req, - cache: cache, - } - if proxy.Metadata != nil { - if proxy.Metadata.TLSClientCertChain != "" { - cb.metadataCerts = &metadataCerts{ - tlsClientCertChain: proxy.Metadata.TLSClientCertChain, - tlsClientKey: proxy.Metadata.TLSClientKey, - tlsClientRootCert: proxy.Metadata.TLSClientRootCert, - } - } - cb.clusterID = string(proxy.Metadata.ClusterID) - } - return cb -} - -func (m *metadataCerts) String() string { - return m.tlsClientCertChain + "~" + m.tlsClientKey + "~" + m.tlsClientRootCert -} - -// NewMutableCluster initializes MutableCluster with the cluster passed. -func NewMutableCluster(cluster *cluster.Cluster) *MutableCluster { - return &MutableCluster{ - cluster: cluster, - } -} - -// sidecarProxy returns true if the clusters are being built for sidecar proxy otherwise false. -func (cb *ClusterBuilder) sidecarProxy() bool { - return cb.proxyType == model.SidecarProxy -} - -func (cb *ClusterBuilder) buildSubsetCluster(opts buildClusterOpts, destRule *config.Config, subset *networking.Subset, service *model.Service, - proxyView model.ProxyView) *cluster.Cluster { - opts.serviceMTLSMode = cb.req.Push.BestEffortInferServiceMTLSMode(subset.GetTrafficPolicy(), service, opts.port) - var subsetClusterName string - var defaultSni string - if opts.clusterMode == DefaultClusterMode { - subsetClusterName = model.BuildSubsetKey(model.TrafficDirectionOutbound, subset.Name, service.Hostname, opts.port.Port) - defaultSni = model.BuildDNSSrvSubsetKey(model.TrafficDirectionOutbound, subset.Name, service.Hostname, opts.port.Port) - } else { - subsetClusterName = model.BuildDNSSrvSubsetKey(model.TrafficDirectionOutbound, subset.Name, service.Hostname, opts.port.Port) - } - // clusters with discovery type STATIC, STRICT_DNS rely on cluster.LoadAssignment field. - // ServiceEntry's need to filter hosts based on subset.labels in order to perform weighted routing - var lbEndpoints []*endpoint.LocalityLbEndpoints - - isPassthrough := subset.GetTrafficPolicy().GetLoadBalancer().GetSimple() == networking.LoadBalancerSettings_PASSTHROUGH - clusterType := opts.mutable.cluster.GetType() - if isPassthrough { - clusterType = cluster.Cluster_ORIGINAL_DST - } - if !(isPassthrough || clusterType == cluster.Cluster_EDS) { - if len(subset.Labels) != 0 { - lbEndpoints = cb.buildLocalityLbEndpoints(proxyView, service, opts.port.Port, subset.Labels) - } else { - lbEndpoints = cb.buildLocalityLbEndpoints(proxyView, service, opts.port.Port, nil) - } - if len(lbEndpoints) == 0 { - log.Debugf("locality endpoints missing for cluster %s", subsetClusterName) - } - } - - subsetCluster := cb.buildDefaultCluster(subsetClusterName, clusterType, lbEndpoints, model.TrafficDirectionOutbound, opts.port, service, nil) - if subsetCluster == nil { - return nil - } - - if len(cb.req.Push.Mesh.OutboundClusterStatName) != 0 { - subsetCluster.cluster.AltStatName = telemetry.BuildStatPrefix(cb.req.Push.Mesh.OutboundClusterStatName, - string(service.Hostname), subset.Name, opts.port, &service.Attributes) - } - - // Apply traffic policy for subset cluster with the destination rule traffic policy. - opts.mutable = subsetCluster - opts.istioMtlsSni = defaultSni - - // If subset has a traffic policy, apply it so that it overrides the destination rule traffic policy. - opts.policy = MergeTrafficPolicy(opts.policy, subset.TrafficPolicy, opts.port) - - if destRule != nil { - destinationRule := CastDestinationRule(destRule) - opts.isDrWithSelector = destinationRule.GetWorkloadSelector() != nil - } - // Apply traffic policy for the subset cluster. - cb.applyTrafficPolicy(opts) - - maybeApplyEdsConfig(subsetCluster.cluster) - - if cb.proxyType == model.Router || opts.direction == model.TrafficDirectionOutbound { - cb.applyMetadataExchange(opts.mutable.cluster) - } - - // Add the DestinationRule+subsets metadata. Metadata here is generated on a per-cluster - // basis in buildDefaultCluster, so we can just insert without a copy. - subsetCluster.cluster.Metadata = util.AddConfigInfoMetadata(subsetCluster.cluster.Metadata, destRule.Meta) - util.AddSubsetToMetadata(subsetCluster.cluster.Metadata, subset.Name) - return subsetCluster.build() -} - -// applyDestinationRule applies the destination rule if it exists for the Service. It returns the subset clusters if any created as it -// applies the destination rule. -func (cb *ClusterBuilder) applyDestinationRule(mc *MutableCluster, clusterMode ClusterMode, service *model.Service, - port *model.Port, proxyView model.ProxyView, destRule *config.Config, serviceAccounts []string) []*cluster.Cluster { - destinationRule := CastDestinationRule(destRule) - // merge applicable port level traffic policy settings - trafficPolicy := MergeTrafficPolicy(nil, destinationRule.GetTrafficPolicy(), port) - opts := buildClusterOpts{ - mesh: cb.req.Push.Mesh, - serviceInstances: cb.serviceInstances, - mutable: mc, - policy: trafficPolicy, - port: port, - clusterMode: clusterMode, - direction: model.TrafficDirectionOutbound, - } - - if clusterMode == DefaultClusterMode { - opts.serviceAccounts = serviceAccounts - opts.istioMtlsSni = model.BuildDNSSrvSubsetKey(model.TrafficDirectionOutbound, "", service.Hostname, port.Port) - opts.meshExternal = service.MeshExternal - opts.serviceRegistry = service.Attributes.ServiceRegistry - opts.serviceMTLSMode = cb.req.Push.BestEffortInferServiceMTLSMode(destinationRule.GetTrafficPolicy(), service, port) - } - - if destRule != nil { - opts.isDrWithSelector = destinationRule.GetWorkloadSelector() != nil - } - // Apply traffic policy for the main default cluster. - cb.applyTrafficPolicy(opts) - - // Apply EdsConfig if needed. This should be called after traffic policy is applied because, traffic policy might change - // discovery type. - maybeApplyEdsConfig(mc.cluster) - - if cb.proxyType == model.Router || opts.direction == model.TrafficDirectionOutbound { - cb.applyMetadataExchange(opts.mutable.cluster) - } - - if destRule != nil { - mc.cluster.Metadata = util.AddConfigInfoMetadata(mc.cluster.Metadata, destRule.Meta) - } - subsetClusters := make([]*cluster.Cluster, 0) - for _, subset := range destinationRule.GetSubsets() { - subsetCluster := cb.buildSubsetCluster(opts, destRule, subset, service, proxyView) - if subsetCluster != nil { - subsetClusters = append(subsetClusters, subsetCluster) - } - } - return subsetClusters -} - -func (cb *ClusterBuilder) applyMetadataExchange(c *cluster.Cluster) { - if features.MetadataExchange { - c.Filters = append(c.Filters, xdsfilters.TCPClusterMx) - } -} - -// MergeTrafficPolicy returns the merged TrafficPolicy for a destination-level and subset-level policy on a given port. -func MergeTrafficPolicy(original, subsetPolicy *networking.TrafficPolicy, port *model.Port) *networking.TrafficPolicy { - if subsetPolicy == nil { - return original - } - - // Sanity check that top-level port level settings have already been merged for the given port - if original != nil && len(original.PortLevelSettings) != 0 { - original = MergeTrafficPolicy(nil, original, port) - } - - mergedPolicy := &networking.TrafficPolicy{} - if original != nil { - mergedPolicy.ConnectionPool = original.ConnectionPool - mergedPolicy.LoadBalancer = original.LoadBalancer - mergedPolicy.OutlierDetection = original.OutlierDetection - mergedPolicy.Tls = original.Tls - } - - // Override with subset values. - if subsetPolicy.ConnectionPool != nil { - mergedPolicy.ConnectionPool = subsetPolicy.ConnectionPool - } - if subsetPolicy.OutlierDetection != nil { - mergedPolicy.OutlierDetection = subsetPolicy.OutlierDetection - } - if subsetPolicy.LoadBalancer != nil { - mergedPolicy.LoadBalancer = subsetPolicy.LoadBalancer - } - if subsetPolicy.Tls != nil { - mergedPolicy.Tls = subsetPolicy.Tls - } - - // Check if port level overrides exist, if yes override with them. - if port != nil { - for _, p := range subsetPolicy.PortLevelSettings { - if p.Port != nil && uint32(port.Port) == p.Port.Number { - // per the docs, port level policies do not inherit and instead to defaults if not provided - mergedPolicy.ConnectionPool = p.ConnectionPool - mergedPolicy.OutlierDetection = p.OutlierDetection - mergedPolicy.LoadBalancer = p.LoadBalancer - mergedPolicy.Tls = p.Tls - break - } - } - } - return mergedPolicy -} - -// buildDefaultCluster builds the default cluster and also applies default traffic policy. -func (cb *ClusterBuilder) buildDefaultCluster(name string, discoveryType cluster.Cluster_DiscoveryType, - localityLbEndpoints []*endpoint.LocalityLbEndpoints, direction model.TrafficDirection, - port *model.Port, service *model.Service, allInstances []*model.ServiceInstance) *MutableCluster { - if allInstances == nil { - allInstances = cb.serviceInstances - } - c := &cluster.Cluster{ - Name: name, - ClusterDiscoveryType: &cluster.Cluster_Type{Type: discoveryType}, - } - ec := NewMutableCluster(c) - switch discoveryType { - case cluster.Cluster_STRICT_DNS, cluster.Cluster_LOGICAL_DNS: - if cb.supportsIPv4 { - c.DnsLookupFamily = cluster.Cluster_V4_ONLY - } else { - c.DnsLookupFamily = cluster.Cluster_V6_ONLY - } - dnsRate := cb.req.Push.Mesh.DnsRefreshRate - c.DnsRefreshRate = dnsRate - c.RespectDnsTtl = true - fallthrough - case cluster.Cluster_STATIC: - if len(localityLbEndpoints) == 0 { - cb.req.Push.AddMetric(model.DNSNoEndpointClusters, c.Name, cb.proxyID, - fmt.Sprintf("%s cluster without endpoints %s found while pushing CDS", discoveryType.String(), c.Name)) - return nil - } - c.LoadAssignment = &endpoint.ClusterLoadAssignment{ - ClusterName: name, - Endpoints: localityLbEndpoints, - } - } - - // For inbound clusters, the default traffic policy is used. For outbound clusters, the default traffic policy - // will be applied, which would be overridden by traffic policy specified in destination rule, if any. - opts := buildClusterOpts{ - mesh: cb.req.Push.Mesh, - mutable: ec, - policy: nil, - port: port, - serviceAccounts: nil, - istioMtlsSni: "", - clusterMode: DefaultClusterMode, - direction: direction, - serviceInstances: cb.serviceInstances, - } - // decides whether the cluster corresponds to a service external to mesh or not. - if direction == model.TrafficDirectionInbound { - // Inbound cluster always corresponds to service in the mesh. - opts.meshExternal = false - } else if service != nil { - // otherwise, read this information from service object. - opts.meshExternal = service.MeshExternal - } - - cb.setUpstreamProtocol(ec, port, direction) - addTelemetryMetadata(opts, service, direction, allInstances) - addNetworkingMetadata(opts, service, direction) - return ec -} - -type clusterCache struct { - clusterName string - - // proxy related cache fields - proxyVersion string // will be matched by envoyfilter patches - locality *core.Locality // identifies the locality the cluster is generated for - proxyClusterID string // identifies the kubernetes cluster a proxy is in - proxySidecar bool // identifies if this proxy is a Sidecar - proxyView model.ProxyView - metadataCerts *metadataCerts // metadata certificates of proxy - - // service attributes - http2 bool // http2 identifies if the cluster is for an http2 service - downstreamAuto bool - supportsIPv4 bool - - // Dependent configs - service *model.Service - destinationRule *config.Config - envoyFilterKeys []string - peerAuthVersion string // identifies the versions of all peer authentications - serviceAccounts []string // contains all the service accounts associated with the service -} - -func (t *clusterCache) Key() string { - params := []string{ - t.clusterName, t.proxyVersion, util.LocalityToString(t.locality), - t.proxyClusterID, strconv.FormatBool(t.proxySidecar), - strconv.FormatBool(t.http2), strconv.FormatBool(t.downstreamAuto), strconv.FormatBool(t.supportsIPv4), - } - if t.proxyView != nil { - params = append(params, t.proxyView.String()) - } - if t.metadataCerts != nil { - params = append(params, t.metadataCerts.String()) - } - if t.service != nil { - params = append(params, string(t.service.Hostname)+"/"+t.service.Attributes.Namespace) - } - if t.destinationRule != nil { - params = append(params, t.destinationRule.Name+"/"+t.destinationRule.Namespace) - } - params = append(params, t.envoyFilterKeys...) - params = append(params, t.peerAuthVersion) - params = append(params, t.serviceAccounts...) - - hash := md5.New() - for _, param := range params { - hash.Write([]byte(param)) - } - sum := hash.Sum(nil) - return hex.EncodeToString(sum) -} - -func (t clusterCache) DependentConfigs() []model.ConfigKey { - configs := []model.ConfigKey{} - if t.destinationRule != nil { - configs = append(configs, model.ConfigKey{Kind: gvk.DestinationRule, Name: t.destinationRule.Name, Namespace: t.destinationRule.Namespace}) - } - if t.service != nil { - configs = append(configs, model.ConfigKey{Kind: gvk.ServiceEntry, Name: string(t.service.Hostname), Namespace: t.service.Attributes.Namespace}) - } - for _, efKey := range t.envoyFilterKeys { - items := strings.Split(efKey, "/") - configs = append(configs, model.ConfigKey{Kind: gvk.EnvoyFilter, Name: items[1], Namespace: items[0]}) - } - return configs -} - -func (t *clusterCache) DependentTypes() []config.GroupVersionKind { - return nil -} - -func (t clusterCache) Cacheable() bool { - return true -} - -// buildInboundClusterForPortOrUDS constructs a single inbound listener. The cluster will be bound to -// `inbound|clusterPort||`, and send traffic to :. A workload -// will have a single inbound cluster per port. In general this works properly, with the exception of -// the Service-oriented DestinationRule, and upstream protocol selection. Our documentation currently -// requires a single protocol per port, and the DestinationRule issue is slated to move to Sidecar. -// Note: clusterPort and instance.Endpoint.EndpointPort are identical for standard Services; however, -// Sidecar.Ingress allows these to be different. -func (cb *ClusterBuilder) buildInboundClusterForPortOrUDS(clusterPort int, bind string, - proxy *model.Proxy, instance *model.ServiceInstance, allInstance []*model.ServiceInstance) *MutableCluster { - clusterName := model.BuildInboundSubsetKey(clusterPort) - localityLbEndpoints := buildInboundLocalityLbEndpoints(bind, instance.Endpoint.EndpointPort) - clusterType := cluster.Cluster_ORIGINAL_DST - if len(localityLbEndpoints) > 0 { - clusterType = cluster.Cluster_STATIC - } - localCluster := cb.buildDefaultCluster(clusterName, clusterType, localityLbEndpoints, - model.TrafficDirectionInbound, instance.ServicePort, instance.Service, allInstance) - if clusterType == cluster.Cluster_ORIGINAL_DST { - // Extend cleanupInterval beyond 5s default. This ensures that upstream connections will stay - // open for up to 60s. With the default of 5s, we may tear things down too quickly for - // infrequently accessed services. - localCluster.cluster.CleanupInterval = &durationpb.Duration{Seconds: 60} - } - // If stat name is configured, build the alt statname. - if len(cb.req.Push.Mesh.InboundClusterStatName) != 0 { - localCluster.cluster.AltStatName = telemetry.BuildStatPrefix(cb.req.Push.Mesh.InboundClusterStatName, - string(instance.Service.Hostname), "", instance.ServicePort, &instance.Service.Attributes) - } - - opts := buildClusterOpts{ - mesh: cb.req.Push.Mesh, - mutable: localCluster, - policy: nil, - port: instance.ServicePort, - serviceAccounts: nil, - serviceInstances: cb.serviceInstances, - istioMtlsSni: "", - clusterMode: DefaultClusterMode, - direction: model.TrafficDirectionInbound, - } - // When users specify circuit breakers, they need to be set on the receiver end - // (server side) as well as client side, so that the server has enough capacity - // (not the defaults) to handle the increased traffic volume - // TODO: This is not foolproof - if instance is part of multiple services listening on same port, - // choice of inbound cluster is arbitrary. So the connection pool settings may not apply cleanly. - cfg := proxy.SidecarScope.DestinationRule(model.TrafficDirectionInbound, proxy, instance.Service.Hostname) - if cfg != nil { - destinationRule := cfg.Spec.(*networking.DestinationRule) - opts.isDrWithSelector = destinationRule.GetWorkloadSelector() != nil - if destinationRule.TrafficPolicy != nil { - opts.policy = MergeTrafficPolicy(opts.policy, destinationRule.TrafficPolicy, instance.ServicePort) - util.AddConfigInfoMetadata(localCluster.cluster.Metadata, cfg.Meta) - } - } - cb.applyTrafficPolicy(opts) - - if bind != LocalhostAddress && bind != LocalhostIPv6Address { - // iptables will redirect our own traffic to localhost back to us if we do not use the "magic" upstream bind - // config which will be skipped. - localCluster.cluster.UpstreamBindConfig = &core.BindConfig{ - SourceAddress: &core.SocketAddress{ - Address: cb.passThroughBindIP, - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: uint32(0), - }, - }, - } - } - return localCluster -} - -func (cb *ClusterBuilder) buildLocalityLbEndpoints(proxyView model.ProxyView, service *model.Service, - port int, labels labels.Instance) []*endpoint.LocalityLbEndpoints { - if !(service.Resolution == model.DNSLB || service.Resolution == model.DNSRoundRobinLB) { - return nil - } - - instances := cb.req.Push.ServiceInstancesByPort(service, port, labels) - - // Determine whether or not the target service is considered local to the cluster - // and should, therefore, not be accessed from outside the cluster. - isClusterLocal := cb.req.Push.IsClusterLocal(service) - - lbEndpoints := make(map[string][]*endpoint.LbEndpoint) - for _, instance := range instances { - // Only send endpoints from the networks in the network view requested by the proxy. - // The default network view assigned to the Proxy is nil, in that case match any network. - if !proxyView.IsVisible(instance.Endpoint) { - // Endpoint's network doesn't match the set of networks that the proxy wants to see. - continue - } - // If the downstream service is configured as cluster-local, only include endpoints that - // reside in the same cluster. - if isClusterLocal && (cb.clusterID != string(instance.Endpoint.Locality.ClusterID)) { - continue - } - // TODO(nmittler): Consider merging discoverability policy with cluster-local - // TODO(ramaraochavali): Find a better way here so that we do not have build proxy. - // Currently it works because we only determine discoverability only by cluster. - if !instance.Endpoint.IsDiscoverableFromProxy(&model.Proxy{Metadata: &model.NodeMetadata{ClusterID: istio_cluster.ID(cb.clusterID)}}) { - continue - } - addr := util.BuildAddress(instance.Endpoint.Address, instance.Endpoint.EndpointPort) - ep := &endpoint.LbEndpoint{ - HostIdentifier: &endpoint.LbEndpoint_Endpoint{ - Endpoint: &endpoint.Endpoint{ - Address: addr, - }, - }, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: instance.Endpoint.GetLoadBalancingWeight(), - }, - } - - labels := instance.Endpoint.Labels - ns := instance.Endpoint.Namespace - if features.CanonicalServiceForMeshExternalServiceEntry && service.MeshExternal { - ns = service.Attributes.Namespace - svcLabels := service.Attributes.Labels - if _, ok := svcLabels[model.IstioCanonicalServiceLabelName]; ok { - labels = map[string]string{ - model.IstioCanonicalServiceLabelName: svcLabels[model.IstioCanonicalServiceLabelName], - model.IstioCanonicalServiceRevisionLabelName: svcLabels[model.IstioCanonicalServiceRevisionLabelName], - } - for k, v := range instance.Endpoint.Labels { - labels[k] = v - } - } - } - - ep.Metadata = util.BuildLbEndpointMetadata(instance.Endpoint.Network, instance.Endpoint.TLSMode, instance.Endpoint.WorkloadName, - ns, instance.Endpoint.Locality.ClusterID, labels) - - locality := instance.Endpoint.Locality.Label - lbEndpoints[locality] = append(lbEndpoints[locality], ep) - } - - localityLbEndpoints := make([]*endpoint.LocalityLbEndpoints, 0, len(lbEndpoints)) - locs := make([]string, 0, len(lbEndpoints)) - for k := range lbEndpoints { - locs = append(locs, k) - } - if len(locs) >= 2 { - sort.Strings(locs) - } - for _, locality := range locs { - eps := lbEndpoints[locality] - var weight uint32 - var overflowStatus bool - for _, ep := range eps { - weight, overflowStatus = addUint32(weight, ep.LoadBalancingWeight.GetValue()) - } - if overflowStatus { - log.Warnf("Sum of localityLbEndpoints weight is overflow: service:%s, port: %d, locality:%s", - service.Hostname, port, locality) - } - localityLbEndpoints = append(localityLbEndpoints, &endpoint.LocalityLbEndpoints{ - Locality: util.ConvertLocality(locality), - LbEndpoints: eps, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: weight, - }, - }) - } - - return localityLbEndpoints -} - -// addUint32AvoidOverflow returns sum of two uint32 and status. If sum overflows, -// and returns MaxUint32 and status. -func addUint32(left, right uint32) (uint32, bool) { - if math.MaxUint32-right < left { - return math.MaxUint32, true - } - return left + right, false -} - -// buildInboundPassthroughClusters builds passthrough clusters for inbound. -func (cb *ClusterBuilder) buildInboundPassthroughClusters() []*cluster.Cluster { - // ipv4 and ipv6 feature detection. Envoy cannot ignore a config where the ip version is not supported - clusters := make([]*cluster.Cluster, 0, 2) - if cb.supportsIPv4 { - inboundPassthroughClusterIpv4 := cb.buildDefaultPassthroughCluster() - inboundPassthroughClusterIpv4.Name = util.InboundPassthroughClusterIpv4 - inboundPassthroughClusterIpv4.Filters = nil - inboundPassthroughClusterIpv4.UpstreamBindConfig = &core.BindConfig{ - SourceAddress: &core.SocketAddress{ - Address: InboundPassthroughBindIpv4, - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: uint32(0), - }, - }, - } - clusters = append(clusters, inboundPassthroughClusterIpv4) - } - if cb.supportsIPv6 { - inboundPassthroughClusterIpv6 := cb.buildDefaultPassthroughCluster() - inboundPassthroughClusterIpv6.Name = util.InboundPassthroughClusterIpv6 - inboundPassthroughClusterIpv6.Filters = nil - inboundPassthroughClusterIpv6.UpstreamBindConfig = &core.BindConfig{ - SourceAddress: &core.SocketAddress{ - Address: InboundPassthroughBindIpv6, - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: uint32(0), - }, - }, - } - clusters = append(clusters, inboundPassthroughClusterIpv6) - } - return clusters -} - -// generates a cluster that sends traffic to dummy localport 0 -// This cluster is used to catch all traffic to unresolved destinations in virtual service -func (cb *ClusterBuilder) buildBlackHoleCluster() *cluster.Cluster { - c := &cluster.Cluster{ - Name: util.BlackHoleCluster, - ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STATIC}, - ConnectTimeout: cb.req.Push.Mesh.ConnectTimeout, - LbPolicy: cluster.Cluster_ROUND_ROBIN, - } - return c -} - -// generates a cluster that sends traffic to the original destination. -// This cluster is used to catch all traffic to unknown listener ports -func (cb *ClusterBuilder) buildDefaultPassthroughCluster() *cluster.Cluster { - cluster := &cluster.Cluster{ - Name: util.PassthroughCluster, - ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_ORIGINAL_DST}, - ConnectTimeout: cb.req.Push.Mesh.ConnectTimeout, - LbPolicy: cluster.Cluster_CLUSTER_PROVIDED, - TypedExtensionProtocolOptions: map[string]*any.Any{ - v3.HttpProtocolOptionsType: passthroughHttpProtocolOptions, - }, - } - cb.applyConnectionPool(cb.req.Push.Mesh, NewMutableCluster(cluster), &networking.ConnectionPoolSettings{}) - cb.applyMetadataExchange(cluster) - return cluster -} - -// applyH2Upgrade function will upgrade outbound cluster to http2 if specified by configuration. -func (cb *ClusterBuilder) applyH2Upgrade(opts buildClusterOpts, connectionPool *networking.ConnectionPoolSettings) { - if cb.shouldH2Upgrade(opts.mutable.cluster.Name, opts.direction, opts.port, opts.mesh, connectionPool) { - cb.setH2Options(opts.mutable) - } -} - -// shouldH2Upgrade function returns true if the cluster should be upgraded to http2. -func (cb *ClusterBuilder) shouldH2Upgrade(clusterName string, direction model.TrafficDirection, port *model.Port, mesh *meshconfig.MeshConfig, - connectionPool *networking.ConnectionPoolSettings) bool { - if direction != model.TrafficDirectionOutbound { - return false - } - - // TODO (mjog) - // Upgrade if tls.GetMode() == networking.TLSSettings_ISTIO_MUTUAL - override := networking.ConnectionPoolSettings_HTTPSettings_DEFAULT - if connectionPool != nil && connectionPool.Http != nil { - override = connectionPool.Http.H2UpgradePolicy - } - // If user wants an upgrade at destination rule/port level that means he is sure that - // it is a Http port - upgrade in such case. This is useful incase protocol sniffing is - // enabled and user wants to upgrade/preserve http protocol from client. - if override == networking.ConnectionPoolSettings_HTTPSettings_UPGRADE { - log.Debugf("Upgrading cluster: %v (%v %v)", clusterName, mesh.H2UpgradePolicy, override) - return true - } - - // Do not upgrade non-http ports. This also ensures that we are only upgrading - // named ports so that protocol sniffing does not interfere. Protocol sniffing - // uses downstream protocol. Therefore if the client upgrades connection to http2, - // the server will send h2 stream to the application,even though the application only - // supports http 1.1. - if port != nil && !port.Protocol.IsHTTP() { - return false - } - - if !h2UpgradeMap[upgradeTuple{mesh.H2UpgradePolicy, override}] { - log.Debugf("Not upgrading cluster: %v (%v %v)", clusterName, mesh.H2UpgradePolicy, override) - return false - } - - log.Debugf("Upgrading cluster: %v (%v %v)", clusterName, mesh.H2UpgradePolicy, override) - return true -} - -// setH2Options make the cluster an h2 cluster by setting http2ProtocolOptions. -func (cb *ClusterBuilder) setH2Options(mc *MutableCluster) { - if mc == nil { - return - } - if mc.httpProtocolOptions == nil { - mc.httpProtocolOptions = &http.HttpProtocolOptions{} - } - options := mc.httpProtocolOptions - if options.UpstreamHttpProtocolOptions == nil { - options.UpstreamProtocolOptions = &http.HttpProtocolOptions_ExplicitHttpConfig_{ - ExplicitHttpConfig: &http.HttpProtocolOptions_ExplicitHttpConfig{ - ProtocolConfig: &http.HttpProtocolOptions_ExplicitHttpConfig_Http2ProtocolOptions{ - Http2ProtocolOptions: http2ProtocolOptions(), - }, - }, - } - } -} - -func (cb *ClusterBuilder) applyTrafficPolicy(opts buildClusterOpts) { - connectionPool, outlierDetection, loadBalancer, tls := selectTrafficPolicyComponents(opts.policy) - // Connection pool settings are applicable for both inbound and outbound clusters. - if connectionPool == nil { - connectionPool = &networking.ConnectionPoolSettings{} - } - cb.applyConnectionPool(opts.mesh, opts.mutable, connectionPool) - if opts.direction != model.TrafficDirectionInbound { - cb.applyH2Upgrade(opts, connectionPool) - applyOutlierDetection(opts.mutable.cluster, outlierDetection) - applyLoadBalancer(opts.mutable.cluster, loadBalancer, opts.port, cb.locality, cb.proxyLabels, opts.mesh) - if opts.clusterMode != SniDnatClusterMode { - autoMTLSEnabled := opts.mesh.GetEnableAutoMtls().Value - tls, mtlsCtxType := cb.buildAutoMtlsSettings(tls, opts.serviceAccounts, opts.istioMtlsSni, - autoMTLSEnabled, opts.meshExternal, opts.serviceMTLSMode) - cb.applyUpstreamTLSSettings(&opts, tls, mtlsCtxType) - } - } - - if opts.mutable.cluster.GetType() == cluster.Cluster_ORIGINAL_DST { - opts.mutable.cluster.LbPolicy = cluster.Cluster_CLUSTER_PROVIDED - } -} - -// buildAutoMtlsSettings fills key cert fields for all TLSSettings when the mode is `ISTIO_MUTUAL`. -// If the (input) TLS setting is nil (i.e not set), *and* the service mTLS mode is STRICT, it also -// creates and populates the config as if they are set as ISTIO_MUTUAL. -func (cb *ClusterBuilder) buildAutoMtlsSettings( - tls *networking.ClientTLSSettings, - serviceAccounts []string, - sni string, - autoMTLSEnabled bool, - meshExternal bool, - serviceMTLSMode model.MutualTLSMode) (*networking.ClientTLSSettings, mtlsContextType) { - if tls != nil { - if tls.Mode == networking.ClientTLSSettings_DISABLE || tls.Mode == networking.ClientTLSSettings_SIMPLE { - return tls, userSupplied - } - // For backward compatibility, use metadata certs if provided. - if cb.hasMetadataCerts() { - // When building Mutual TLS settings, we should always use user supplied SubjectAltNames and SNI - // in destination rule. The Service Accounts and auto computed SNI should only be used for - // ISTIO_MUTUAL. - return cb.buildMutualTLS(tls.SubjectAltNames, tls.Sni), userSupplied - } - if tls.Mode != networking.ClientTLSSettings_ISTIO_MUTUAL { - return tls, userSupplied - } - // Update TLS settings for ISTIO_MUTUAL. Use client provided SNI if set. Otherwise, - // overwrite with the auto generated SNI. User specified SNIs in the istio mtls settings - // are useful when routing via gateways. Use Service Accounts if Subject Alt names - // are not specified in TLS settings. - sniToUse := tls.Sni - if len(sniToUse) == 0 { - sniToUse = sni - } - subjectAltNamesToUse := tls.SubjectAltNames - if subjectAltNamesToUse == nil { - subjectAltNamesToUse = serviceAccounts - } - return cb.buildIstioMutualTLS(subjectAltNamesToUse, sniToUse), userSupplied - } - - if meshExternal || !autoMTLSEnabled || serviceMTLSMode == model.MTLSUnknown || serviceMTLSMode == model.MTLSDisable { - return nil, userSupplied - } - - // For backward compatibility, use metadata certs if provided. - if cb.hasMetadataCerts() { - return cb.buildMutualTLS(serviceAccounts, sni), autoDetected - } - - // Build settings for auto MTLS. - return cb.buildIstioMutualTLS(serviceAccounts, sni), autoDetected -} - -func (cb *ClusterBuilder) hasMetadataCerts() bool { - return cb.metadataCerts != nil -} - -type mtlsContextType int - -const ( - userSupplied mtlsContextType = iota - autoDetected -) - -// buildMutualTLS returns a `TLSSettings` for MUTUAL mode with proxy metadata certificates. -func (cb *ClusterBuilder) buildMutualTLS(serviceAccounts []string, sni string) *networking.ClientTLSSettings { - return &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - CaCertificates: cb.metadataCerts.tlsClientRootCert, - ClientCertificate: cb.metadataCerts.tlsClientCertChain, - PrivateKey: cb.metadataCerts.tlsClientKey, - SubjectAltNames: serviceAccounts, - Sni: sni, - } -} - -// buildIstioMutualTLS returns a `TLSSettings` for ISTIO_MUTUAL mode. -func (cb *ClusterBuilder) buildIstioMutualTLS(san []string, sni string) *networking.ClientTLSSettings { - return &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_ISTIO_MUTUAL, - SubjectAltNames: san, - Sni: sni, - } -} - -func (cb *ClusterBuilder) applyDefaultConnectionPool(cluster *cluster.Cluster) { - cluster.ConnectTimeout = cb.req.Push.Mesh.ConnectTimeout -} - -// FIXME: there isn't a way to distinguish between unset values and zero values -func (cb *ClusterBuilder) applyConnectionPool(mesh *meshconfig.MeshConfig, mc *MutableCluster, settings *networking.ConnectionPoolSettings) { - if settings == nil { - return - } - - threshold := getDefaultCircuitBreakerThresholds() - var idleTimeout *durationpb.Duration - var maxRequestsPerConnection uint32 - - if settings.Http != nil { - if settings.Http.Http2MaxRequests > 0 { - // Envoy only applies MaxRequests in HTTP/2 clusters - threshold.MaxRequests = &wrappers.UInt32Value{Value: uint32(settings.Http.Http2MaxRequests)} - } - if settings.Http.Http1MaxPendingRequests > 0 { - // Envoy only applies MaxPendingRequests in HTTP/1.1 clusters - threshold.MaxPendingRequests = &wrappers.UInt32Value{Value: uint32(settings.Http.Http1MaxPendingRequests)} - } - - // FIXME: zero is a valid value if explicitly set, otherwise we want to use the default - if settings.Http.MaxRetries > 0 { - threshold.MaxRetries = &wrappers.UInt32Value{Value: uint32(settings.Http.MaxRetries)} - } - - idleTimeout = settings.Http.IdleTimeout - maxRequestsPerConnection = uint32(settings.Http.MaxRequestsPerConnection) - } - - cb.applyDefaultConnectionPool(mc.cluster) - if settings.Tcp != nil { - if settings.Tcp != nil && settings.Tcp.ConnectTimeout != nil { - mc.cluster.ConnectTimeout = settings.Tcp.ConnectTimeout - } - - if settings.Tcp != nil && settings.Tcp.MaxConnections > 0 { - threshold.MaxConnections = &wrappers.UInt32Value{Value: uint32(settings.Tcp.MaxConnections)} - } - } - applyTCPKeepalive(mesh, mc.cluster, settings.Tcp) - - mc.cluster.CircuitBreakers = &cluster.CircuitBreakers{ - Thresholds: []*cluster.CircuitBreakers_Thresholds{threshold}, - } - - if idleTimeout != nil || maxRequestsPerConnection > 0 { - if mc.httpProtocolOptions == nil { - mc.httpProtocolOptions = &http.HttpProtocolOptions{} - } - commonOptions := mc.httpProtocolOptions - if commonOptions.CommonHttpProtocolOptions == nil { - commonOptions.CommonHttpProtocolOptions = &core.HttpProtocolOptions{} - } - if idleTimeout != nil { - idleTimeoutDuration := idleTimeout - commonOptions.CommonHttpProtocolOptions.IdleTimeout = idleTimeoutDuration - } - if maxRequestsPerConnection > 0 { - commonOptions.CommonHttpProtocolOptions.MaxRequestsPerConnection = &wrappers.UInt32Value{Value: maxRequestsPerConnection} - } - } - - if settings.Http != nil && settings.Http.UseClientProtocol { - // Use downstream protocol. If the incoming traffic use HTTP 1.1, the - // upstream cluster will use HTTP 1.1, if incoming traffic use HTTP2, - // the upstream cluster will use HTTP2. - cb.setUseDownstreamProtocol(mc) - } -} - -func (cb *ClusterBuilder) applyUpstreamTLSSettings(opts *buildClusterOpts, tls *networking.ClientTLSSettings, mtlsCtxType mtlsContextType) { - if tls == nil { - return - } - - c := opts.mutable - - tlsContext, err := cb.buildUpstreamClusterTLSContext(opts, tls) - if err != nil { - log.Errorf("failed to build Upstream TLSContext: %s", err.Error()) - return - } - - if tlsContext != nil { - c.cluster.TransportSocket = &core.TransportSocket{ - Name: util.EnvoyTLSSocketName, - ConfigType: &core.TransportSocket_TypedConfig{TypedConfig: util.MessageToAny(tlsContext)}, - } - } - - // For headless service, discover type will be `Cluster_ORIGINAL_DST` - // Apply auto mtls to clusters excluding these kind of headless service - if c.cluster.GetType() != cluster.Cluster_ORIGINAL_DST { - // convert to transport socket matcher if the mode was auto detected - if tls.Mode == networking.ClientTLSSettings_ISTIO_MUTUAL && mtlsCtxType == autoDetected { - transportSocket := c.cluster.TransportSocket - c.cluster.TransportSocket = nil - c.cluster.TransportSocketMatches = []*cluster.Cluster_TransportSocketMatch{ - { - Name: "tlsMode-" + model.IstioMutualTLSModeLabel, - Match: istioMtlsTransportSocketMatch, - TransportSocket: transportSocket, - }, - defaultTransportSocketMatch(), - } - } - } -} - -func (cb *ClusterBuilder) buildUpstreamClusterTLSContext(opts *buildClusterOpts, tls *networking.ClientTLSSettings) (*auth.UpstreamTlsContext, error) { - // Hack to avoid egress sds cluster config generation for sidecar when - // CredentialName is set in DestinationRule without a workloadSelector. - // We do not want to support CredentialName setting in non workloadSelector based DestinationRules, because - // that would result in the CredentialName being supplied to all the sidecars which the DestinationRule is scoped to, - // resulting in delayed startup of sidecars who do not have access to the credentials. - if tls.CredentialName != "" && cb.sidecarProxy() && !opts.isDrWithSelector { - if tls.Mode == networking.ClientTLSSettings_SIMPLE || tls.Mode == networking.ClientTLSSettings_MUTUAL { - return nil, nil - } - } - - c := opts.mutable - var tlsContext *auth.UpstreamTlsContext - - switch tls.Mode { - case networking.ClientTLSSettings_DISABLE: - tlsContext = nil - case networking.ClientTLSSettings_ISTIO_MUTUAL: - tlsContext = &auth.UpstreamTlsContext{ - CommonTlsContext: defaultUpstreamCommonTLSContext(), - Sni: tls.Sni, - } - - tlsContext.CommonTlsContext.TlsCertificateSdsSecretConfigs = append(tlsContext.CommonTlsContext.TlsCertificateSdsSecretConfigs, - authn_model.ConstructSdsSecretConfig(authn_model.SDSDefaultResourceName)) - - tlsContext.CommonTlsContext.ValidationContextType = &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{MatchSubjectAltNames: util.StringToExactMatch(tls.SubjectAltNames)}, - ValidationContextSdsSecretConfig: authn_model.ConstructSdsSecretConfig(authn_model.SDSRootResourceName), - }, - } - // Set default SNI of cluster name for istio_mutual if sni is not set. - if len(tlsContext.Sni) == 0 { - tlsContext.Sni = c.cluster.Name - } - // `istio-peer-exchange` alpn is only used when using mtls communication between peers. - // We add `istio-peer-exchange` to the list of alpn strings. - // The code has repeated snippets because We want to use predefined alpn strings for efficiency. - if cb.IsHttp2Cluster(c) { - // This is HTTP/2 in-mesh cluster, advertise it with ALPN. - if features.MetadataExchange { - tlsContext.CommonTlsContext.AlpnProtocols = util.ALPNInMeshH2WithMxc - } else { - tlsContext.CommonTlsContext.AlpnProtocols = util.ALPNInMeshH2 - } - } else { - // This is in-mesh cluster, advertise it with ALPN. - if features.MetadataExchange { - tlsContext.CommonTlsContext.AlpnProtocols = util.ALPNInMeshWithMxc - } else { - tlsContext.CommonTlsContext.AlpnProtocols = util.ALPNInMesh - } - } - case networking.ClientTLSSettings_SIMPLE: - tlsContext = &auth.UpstreamTlsContext{ - CommonTlsContext: defaultUpstreamCommonTLSContext(), - Sni: tls.Sni, - } - - cb.setAutoSniAndAutoSanValidation(c, tls) - - // Use subject alt names specified in service entry if TLS settings does not have subject alt names. - if opts.serviceRegistry == provider.External && len(tls.SubjectAltNames) == 0 { - tls.SubjectAltNames = opts.serviceAccounts - } - if tls.CredentialName != "" { - // If credential name is specified at Destination Rule config and originating node is egress gateway, create - // SDS config for egress gateway to fetch key/cert at gateway agent. - authn_model.ApplyCustomSDSToClientCommonTLSContext(tlsContext.CommonTlsContext, tls) - } else { - // If CredentialName is not set fallback to files specified in DR. - res := security.SdsCertificateConfig{ - CaCertificatePath: tls.CaCertificates, - } - // If tls.CaCertificate or CaCertificate in Metadata isn't configured don't set up SdsSecretConfig - if !res.IsRootCertificate() { - tlsContext.CommonTlsContext.ValidationContextType = &auth.CommonTlsContext_ValidationContext{} - } else { - tlsContext.CommonTlsContext.ValidationContextType = &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{MatchSubjectAltNames: util.StringToExactMatch(tls.SubjectAltNames)}, - ValidationContextSdsSecretConfig: authn_model.ConstructSdsSecretConfig(res.GetRootResourceName()), - }, - } - } - } - - if cb.IsHttp2Cluster(c) { - // This is HTTP/2 cluster, advertise it with ALPN. - tlsContext.CommonTlsContext.AlpnProtocols = util.ALPNH2Only - } - - case networking.ClientTLSSettings_MUTUAL: - tlsContext = &auth.UpstreamTlsContext{ - CommonTlsContext: defaultUpstreamCommonTLSContext(), - Sni: tls.Sni, - } - - cb.setAutoSniAndAutoSanValidation(c, tls) - - // Use subject alt names specified in service entry if TLS settings does not have subject alt names. - if opts.serviceRegistry == provider.External && len(tls.SubjectAltNames) == 0 { - tls.SubjectAltNames = opts.serviceAccounts - } - if tls.CredentialName != "" { - // If credential name is specified at Destination Rule config and originating node is egress gateway, create - // SDS config for egress gateway to fetch key/cert at gateway agent. - authn_model.ApplyCustomSDSToClientCommonTLSContext(tlsContext.CommonTlsContext, tls) - } else { - // If CredentialName is not set fallback to file based approach - if tls.ClientCertificate == "" || tls.PrivateKey == "" { - err := fmt.Errorf("failed to apply tls setting for %s: client certificate and private key must not be empty", - c.cluster.Name) - return nil, err - } - // These are certs being mounted from within the pod and specified in Destination Rules. - // Rather than reading directly in Envoy, which does not support rotation, we will - // serve them over SDS by reading the files. - res := security.SdsCertificateConfig{ - CertificatePath: tls.ClientCertificate, - PrivateKeyPath: tls.PrivateKey, - CaCertificatePath: tls.CaCertificates, - } - tlsContext.CommonTlsContext.TlsCertificateSdsSecretConfigs = append(tlsContext.CommonTlsContext.TlsCertificateSdsSecretConfigs, - authn_model.ConstructSdsSecretConfig(res.GetResourceName())) - - // If tls.CaCertificate or CaCertificate in Metadata isn't configured don't set up RootSdsSecretConfig - if !res.IsRootCertificate() { - tlsContext.CommonTlsContext.ValidationContextType = &auth.CommonTlsContext_ValidationContext{} - } else { - tlsContext.CommonTlsContext.ValidationContextType = &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{MatchSubjectAltNames: util.StringToExactMatch(tls.SubjectAltNames)}, - ValidationContextSdsSecretConfig: authn_model.ConstructSdsSecretConfig(res.GetRootResourceName()), - }, - } - } - } - - if cb.IsHttp2Cluster(c) { - // This is HTTP/2 cluster, advertise it with ALPN. - tlsContext.CommonTlsContext.AlpnProtocols = util.ALPNH2Only - } - } - return tlsContext, nil -} - -// Set auto_sni if EnableAutoSni feature flag is enabled and if sni field is not explicitly set in DR. -// Set auto_san_validation if VerifyCertAtClient feature flag is enabled and if there is no explicit SubjectAltNames specified in DR. -func (cb *ClusterBuilder) setAutoSniAndAutoSanValidation(mc *MutableCluster, tls *networking.ClientTLSSettings) { - if mc == nil || !features.EnableAutoSni { - return - } - - setAutoSni := false - setAutoSanValidation := false - if len(tls.Sni) == 0 { - setAutoSni = true - } - if features.VerifyCertAtClient && len(tls.SubjectAltNames) == 0 { - setAutoSanValidation = true - } - - if setAutoSni || setAutoSanValidation { - if mc.httpProtocolOptions == nil { - mc.httpProtocolOptions = &http.HttpProtocolOptions{} - } - if mc.httpProtocolOptions.UpstreamHttpProtocolOptions == nil { - mc.httpProtocolOptions.UpstreamHttpProtocolOptions = &core.UpstreamHttpProtocolOptions{} - } - if setAutoSni { - mc.httpProtocolOptions.UpstreamHttpProtocolOptions.AutoSni = true - } - if setAutoSanValidation { - mc.httpProtocolOptions.UpstreamHttpProtocolOptions.AutoSanValidation = true - } - } -} - -func (cb *ClusterBuilder) setUseDownstreamProtocol(mc *MutableCluster) { - if mc.httpProtocolOptions == nil { - mc.httpProtocolOptions = &http.HttpProtocolOptions{} - } - options := mc.httpProtocolOptions - options.UpstreamProtocolOptions = &http.HttpProtocolOptions_UseDownstreamProtocolConfig{ - UseDownstreamProtocolConfig: &http.HttpProtocolOptions_UseDownstreamHttpConfig{ - HttpProtocolOptions: &core.Http1ProtocolOptions{}, - Http2ProtocolOptions: http2ProtocolOptions(), - }, - } -} - -func http2ProtocolOptions() *core.Http2ProtocolOptions { - return &core.Http2ProtocolOptions{ - // Envoy default value of 100 is too low for data path. - MaxConcurrentStreams: &wrappers.UInt32Value{ - Value: 1073741824, - }, - } -} - -// nolint -func (cb *ClusterBuilder) IsHttp2Cluster(mc *MutableCluster) bool { - options := mc.httpProtocolOptions - return options != nil && options.GetExplicitHttpConfig().GetHttp2ProtocolOptions() != nil -} - -func (cb *ClusterBuilder) setUpstreamProtocol(mc *MutableCluster, port *model.Port, direction model.TrafficDirection) { - if port.Protocol.IsHTTP2() { - cb.setH2Options(mc) - return - } - - // Add use_downstream_protocol for sidecar proxy only if protocol sniffing is enabled. Since - // protocol detection is disabled for gateway and use_downstream_protocol is used under protocol - // detection for cluster to select upstream connection protocol when the service port is unnamed. - // use_downstream_protocol should be disabled for gateway; while it sort of makes sense there, even - // without sniffing, a concern is that clients will do ALPN negotiation, and we always advertise - // h2. Clients would then connect with h2, while the upstream may not support it. This is not a - // concern for plaintext, but we do not have a way to distinguish https vs http here. If users of - // gateway want this behavior, they can configure UseClientProtocol explicitly. - if cb.sidecarProxy() && ((util.IsProtocolSniffingEnabledForInboundPort(port) && direction == model.TrafficDirectionInbound) || - (util.IsProtocolSniffingEnabledForOutboundPort(port) && direction == model.TrafficDirectionOutbound)) { - // Use downstream protocol. If the incoming traffic use HTTP 1.1, the - // upstream cluster will use HTTP 1.1, if incoming traffic use HTTP2, - // the upstream cluster will use HTTP2. - cb.setUseDownstreamProtocol(mc) - } -} - -// normalizeClusters normalizes clusters to avoid duplicate clusters. This should be called -// at the end before adding the cluster to list of clusters. -func (cb *ClusterBuilder) normalizeClusters(clusters []*discovery.Resource) []*discovery.Resource { - // resolve cluster name conflicts. there can be duplicate cluster names if there are conflicting service definitions. - // for any clusters that share the same name the first cluster is kept and the others are discarded. - have := sets.Set{} - out := make([]*discovery.Resource, 0, len(clusters)) - for _, c := range clusters { - if !have.Contains(c.Name) { - out = append(out, c) - } else { - cb.req.Push.AddMetric(model.DuplicatedClusters, c.Name, cb.proxyID, - fmt.Sprintf("Duplicate cluster %s found while pushing CDS", c.Name)) - } - have.Insert(c.Name) - } - return out -} - -// getAllCachedSubsetClusters either fetches all cached clusters for a given key (there may be multiple due to subsets) -// and returns them along with allFound=True, or returns allFound=False indicating a cache miss. In either case, -// the cache tokens are returned to allow future writes to the cache. -// This code will only trigger a cache hit if all subset clusters are present. This simplifies the code a bit, -// as the non-subset and subset cluster generation are tightly coupled, in exchange for a likely trivial cache hit rate impact. -func (cb *ClusterBuilder) getAllCachedSubsetClusters(clusterKey clusterCache) ([]*discovery.Resource, bool) { - if !features.EnableCDSCaching { - return nil, false - } - destinationRule := CastDestinationRule(clusterKey.destinationRule) - res := make([]*discovery.Resource, 0, 1+len(destinationRule.GetSubsets())) - cachedCluster, f := cb.cache.Get(&clusterKey) - allFound := f - res = append(res, cachedCluster) - dir, _, host, port := model.ParseSubsetKey(clusterKey.clusterName) - for _, ss := range destinationRule.GetSubsets() { - clusterKey.clusterName = model.BuildSubsetKey(dir, ss.Name, host, port) - cachedCluster, f := cb.cache.Get(&clusterKey) - if !f { - allFound = false - } - res = append(res, cachedCluster) - } - return res, allFound -} - -// build does any final build operations needed, like marshaling etc. -func (mc *MutableCluster) build() *cluster.Cluster { - if mc == nil { - return nil - } - // Marshall Http Protocol options if they exist. - if mc.httpProtocolOptions != nil { - // UpstreamProtocolOptions is required field in Envoy. If we have not set this option earlier - // we need to set it to default http protocol options. - if mc.httpProtocolOptions.UpstreamProtocolOptions == nil { - mc.httpProtocolOptions.UpstreamProtocolOptions = &http.HttpProtocolOptions_ExplicitHttpConfig_{ - ExplicitHttpConfig: &http.HttpProtocolOptions_ExplicitHttpConfig{ - ProtocolConfig: &http.HttpProtocolOptions_ExplicitHttpConfig_HttpProtocolOptions{}, - }, - } - } - mc.cluster.TypedExtensionProtocolOptions = map[string]*any.Any{ - v3.HttpProtocolOptionsType: util.MessageToAny(mc.httpProtocolOptions), - } - } - return mc.cluster -} - -// CastDestinationRule returns the destination rule enclosed by the config, if not null. -// Otherwise, return nil. -func CastDestinationRule(config *config.Config) *networking.DestinationRule { - if config != nil { - return config.Spec.(*networking.DestinationRule) - } - - return nil -} - -// maybeApplyEdsConfig applies EdsClusterConfig on the passed in cluster if it is an EDS type of cluster. -func maybeApplyEdsConfig(c *cluster.Cluster) { - if c.GetType() != cluster.Cluster_EDS { - return - } - - c.EdsClusterConfig = &cluster.Cluster_EdsClusterConfig{ - ServiceName: c.Name, - EdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_Ads{ - Ads: &core.AggregatedConfigSource{}, - }, - InitialFetchTimeout: durationpb.New(0), - ResourceApiVersion: core.ApiVersion_V3, - }, - } -} - -func defaultUpstreamCommonTLSContext() *auth.CommonTlsContext { - return &auth.CommonTlsContext{ - TlsParams: &auth.TlsParameters{ - // if not specified, envoy use TLSv1_2 as default for client. - TlsMaximumProtocolVersion: auth.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: auth.TlsParameters_TLSv1_2, - }, - } -} - -// defaultTransportSocketMatch applies to endpoints that have no security.istio.io/tlsMode label -// or those whose label value does not match "istio" -func defaultTransportSocketMatch() *cluster.Cluster_TransportSocketMatch { - return &cluster.Cluster_TransportSocketMatch{ - Name: "tlsMode-disabled", - Match: &structpb.Struct{}, - TransportSocket: xdsfilters.RawBufferTransportSocket, - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/cluster_builder_test.go b/pilot/pkg/networking/core/v1alpha3/cluster_builder_test.go deleted file mode 100644 index c17b4161a..000000000 --- a/pilot/pkg/networking/core/v1alpha3/cluster_builder_test.go +++ /dev/null @@ -1,3513 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "encoding/json" - "fmt" - "reflect" - "sort" - "strings" - "testing" - "time" -) - -import ( - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" - tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - http "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/testing/protocmp" - "google.golang.org/protobuf/types/known/durationpb" - "google.golang.org/protobuf/types/known/structpb" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - authn_model "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - xdsfilters "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/filters" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -func TestApplyDestinationRule(t *testing.T) { - servicePort := model.PortList{ - &model.Port{ - Name: "default", - Port: 8080, - Protocol: protocol.HTTP, - }, - &model.Port{ - Name: "auto", - Port: 9090, - Protocol: protocol.Unsupported, - }, - } - service := &model.Service{ - Hostname: host.Name("foo.default.svc.cluster.local"), - Ports: servicePort, - Resolution: model.ClientSideLB, - Attributes: model.ServiceAttributes{ - Namespace: TestServiceNamespace, - }, - } - - cases := []struct { - name string - cluster *cluster.Cluster - clusterMode ClusterMode - service *model.Service - port *model.Port - proxyView model.ProxyView - destRule *networking.DestinationRule - expectedSubsetClusters []*cluster.Cluster - }{ - // TODO(ramaraochavali): Add more tests to cover additional conditions. - { - name: "nil destination rule", - cluster: &cluster.Cluster{}, - clusterMode: DefaultClusterMode, - service: &model.Service{}, - port: &model.Port{}, - proxyView: model.ProxyViewAll, - destRule: nil, - expectedSubsetClusters: []*cluster.Cluster{}, - }, - { - name: "destination rule with subsets", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}}, - clusterMode: DefaultClusterMode, - service: service, - port: servicePort[0], - proxyView: model.ProxyViewAll, - destRule: &networking.DestinationRule{ - Host: "foo.default.svc.cluster.local", - Subsets: []*networking.Subset{ - { - Name: "foobar", - Labels: map[string]string{"foo": "bar"}, - }, - }, - }, - expectedSubsetClusters: []*cluster.Cluster{ - { - Name: "outbound|8080|foobar|foo.default.svc.cluster.local", - ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}, - EdsClusterConfig: &cluster.Cluster_EdsClusterConfig{ - ServiceName: "outbound|8080|foobar|foo.default.svc.cluster.local", - }, - }, - }, - }, - { - name: "destination rule with pass through subsets", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STATIC}}, - clusterMode: DefaultClusterMode, - service: service, - port: servicePort[0], - proxyView: model.ProxyViewAll, - destRule: &networking.DestinationRule{ - Host: "foo.default.svc.cluster.local", - Subsets: []*networking.Subset{ - { - Name: "foobar", - Labels: map[string]string{"foo": "bar"}, - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{Simple: networking.LoadBalancerSettings_PASSTHROUGH}, - }, - }, - }, - }, - }, - expectedSubsetClusters: []*cluster.Cluster{ - { - Name: "outbound|8080|foobar|foo.default.svc.cluster.local", - ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_ORIGINAL_DST}, - }, - }, - }, - { - name: "destination rule with subsets for SniDnat cluster", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}}, - clusterMode: SniDnatClusterMode, - service: service, - port: servicePort[0], - proxyView: model.ProxyViewAll, - destRule: &networking.DestinationRule{ - Host: "foo.default.svc.cluster.local", - Subsets: []*networking.Subset{ - { - Name: "foobar", - Labels: map[string]string{"foo": "bar"}, - }, - }, - }, - expectedSubsetClusters: []*cluster.Cluster{ - { - Name: "outbound_.8080_.foobar_.foo.default.svc.cluster.local", - ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}, - EdsClusterConfig: &cluster.Cluster_EdsClusterConfig{ - ServiceName: "outbound_.8080_.foobar_.foo.default.svc.cluster.local", - }, - }, - }, - }, - { - name: "destination rule with subset traffic policy", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}}, - clusterMode: DefaultClusterMode, - service: service, - port: servicePort[0], - proxyView: model.ProxyViewAll, - destRule: &networking.DestinationRule{ - Host: "foo.default.svc.cluster.local", - Subsets: []*networking.Subset{ - { - Name: "foobar", - Labels: map[string]string{"foo": "bar"}, - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - }, - }, - }, - }, - }, - }, - expectedSubsetClusters: []*cluster.Cluster{ - { - Name: "outbound|8080|foobar|foo.default.svc.cluster.local", - ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}, - EdsClusterConfig: &cluster.Cluster_EdsClusterConfig{ - ServiceName: "outbound|8080|foobar|foo.default.svc.cluster.local", - }, - CircuitBreakers: &cluster.CircuitBreakers{ - Thresholds: []*cluster.CircuitBreakers_Thresholds{ - { - MaxRetries: &wrappers.UInt32Value{ - Value: 10, - }, - }, - }, - }, - }, - }, - }, - { - name: "destination rule with use client protocol traffic policy", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}}, - clusterMode: DefaultClusterMode, - service: service, - port: servicePort[0], - proxyView: model.ProxyViewAll, - destRule: &networking.DestinationRule{ - Host: "foo.default.svc.cluster.local", - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - UseClientProtocol: true, - }, - }, - }, - }, - expectedSubsetClusters: []*cluster.Cluster{}, - }, - { - name: "destination rule with maxRequestsPerConnection", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}}, - clusterMode: DefaultClusterMode, - service: service, - port: servicePort[0], - proxyView: model.ProxyViewAll, - destRule: &networking.DestinationRule{ - Host: "foo.default.svc.cluster.local", - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - MaxRequestsPerConnection: 10, - }, - }, - }, - }, - expectedSubsetClusters: []*cluster.Cluster{}, - }, - { - name: "subset without labels in both", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STRICT_DNS}}, - clusterMode: DefaultClusterMode, - service: &model.Service{ - Hostname: host.Name("foo.example.com"), - Ports: servicePort, - Resolution: model.DNSLB, - Attributes: model.ServiceAttributes{ - Namespace: TestServiceNamespace, - }, - }, - port: servicePort[0], - proxyView: model.ProxyViewAll, - destRule: &networking.DestinationRule{ - Host: "foo.example.com", - Subsets: []*networking.Subset{{Name: "v1"}}, - }, - expectedSubsetClusters: []*cluster.Cluster{{ - Name: "outbound|8080|v1|foo.example.com", - ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STRICT_DNS}, - }}, - }, - { - name: "subset without labels in dest rule", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STRICT_DNS}}, - clusterMode: DefaultClusterMode, - service: &model.Service{ - Hostname: host.Name("foo.example.com"), - Ports: servicePort, - Resolution: model.DNSLB, - Attributes: model.ServiceAttributes{ - Namespace: TestServiceNamespace, - Labels: map[string]string{"foo": "bar"}, - }, - }, - port: servicePort[0], - proxyView: model.ProxyViewAll, - destRule: &networking.DestinationRule{ - Host: "foo.example.com", - Subsets: []*networking.Subset{{Name: "v1"}}, - }, - expectedSubsetClusters: []*cluster.Cluster{{ - Name: "outbound|8080|v1|foo.example.com", - ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STRICT_DNS}, - }}, - }, - { - name: "subset with labels in both", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STRICT_DNS}}, - clusterMode: DefaultClusterMode, - service: &model.Service{ - Hostname: host.Name("foo.example.com"), - Ports: servicePort, - Resolution: model.DNSLB, - Attributes: model.ServiceAttributes{ - Namespace: TestServiceNamespace, - Labels: map[string]string{"foo": "bar"}, - }, - }, - port: servicePort[0], - proxyView: model.ProxyViewAll, - destRule: &networking.DestinationRule{ - Host: "foo.example.com", - Subsets: []*networking.Subset{{ - Name: "v1", - Labels: map[string]string{"foo": "bar"}, - }}, - }, - expectedSubsetClusters: []*cluster.Cluster{{ - Name: "outbound|8080|v1|foo.example.com", - ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STRICT_DNS}, - }}, - }, - { - name: "subset with labels in both, not matching", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STRICT_DNS}}, - clusterMode: DefaultClusterMode, - service: &model.Service{ - Hostname: host.Name("foo.example.com"), - Ports: servicePort, - Resolution: model.DNSLB, - Attributes: model.ServiceAttributes{ - Namespace: TestServiceNamespace, - Labels: map[string]string{"foo": "bar"}, - }, - }, - port: servicePort[0], - proxyView: model.ProxyViewAll, - destRule: &networking.DestinationRule{ - Host: "foo.example.com", - Subsets: []*networking.Subset{{ - Name: "v1", - Labels: map[string]string{"foo": "not-match"}, - }}, - }, - expectedSubsetClusters: []*cluster.Cluster{}, - }, - { - name: "subset without labels in both and resolution of DNS_ROUND_ROBIN", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_LOGICAL_DNS}}, - clusterMode: DefaultClusterMode, - service: &model.Service{ - Hostname: host.Name("foo.example.com"), - Ports: servicePort, - Resolution: model.DNSRoundRobinLB, - Attributes: model.ServiceAttributes{ - Namespace: TestServiceNamespace, - }, - }, - port: servicePort[0], - proxyView: model.ProxyViewAll, - destRule: &networking.DestinationRule{ - Host: "foo.example.com", - Subsets: []*networking.Subset{{Name: "v1"}}, - }, - expectedSubsetClusters: []*cluster.Cluster{{ - Name: "outbound|8080|v1|foo.example.com", - ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_LOGICAL_DNS}, - }}, - }, - { - name: "subset without labels in dest rule and a resolution of DNS_ROUND_ROBIN", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_LOGICAL_DNS}}, - clusterMode: DefaultClusterMode, - service: &model.Service{ - Hostname: host.Name("foo.example.com"), - Ports: servicePort, - Resolution: model.DNSRoundRobinLB, - Attributes: model.ServiceAttributes{ - Namespace: TestServiceNamespace, - Labels: map[string]string{"foo": "bar"}, - }, - }, - port: servicePort[0], - proxyView: model.ProxyViewAll, - destRule: &networking.DestinationRule{ - Host: "foo.example.com", - Subsets: []*networking.Subset{{Name: "v1"}}, - }, - expectedSubsetClusters: []*cluster.Cluster{{ - Name: "outbound|8080|v1|foo.example.com", - ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_LOGICAL_DNS}, - }}, - }, - { - name: "subset with labels in both", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_LOGICAL_DNS}}, - clusterMode: DefaultClusterMode, - service: &model.Service{ - Hostname: host.Name("foo.example.com"), - Ports: servicePort, - Resolution: model.DNSRoundRobinLB, - Attributes: model.ServiceAttributes{ - Namespace: TestServiceNamespace, - Labels: map[string]string{"foo": "bar"}, - }, - }, - port: servicePort[0], - proxyView: model.ProxyViewAll, - destRule: &networking.DestinationRule{ - Host: "foo.example.com", - Subsets: []*networking.Subset{{ - Name: "v1", - Labels: map[string]string{"foo": "bar"}, - }}, - }, - expectedSubsetClusters: []*cluster.Cluster{{ - Name: "outbound|8080|v1|foo.example.com", - ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_LOGICAL_DNS}, - }}, - }, - { - name: "subset with labels in both, not matching", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_LOGICAL_DNS}}, - clusterMode: DefaultClusterMode, - service: &model.Service{ - Hostname: host.Name("foo.example.com"), - Ports: servicePort, - Resolution: model.DNSRoundRobinLB, - Attributes: model.ServiceAttributes{ - Namespace: TestServiceNamespace, - Labels: map[string]string{"foo": "bar"}, - }, - }, - port: servicePort[0], - proxyView: model.ProxyViewAll, - destRule: &networking.DestinationRule{ - Host: "foo.example.com", - Subsets: []*networking.Subset{{ - Name: "v1", - Labels: map[string]string{"foo": "not-match"}, - }}, - }, - expectedSubsetClusters: []*cluster.Cluster{}, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - instances := []*model.ServiceInstance{ - { - Service: tt.service, - ServicePort: tt.port, - Endpoint: &model.IstioEndpoint{ - Address: "192.168.1.1", - EndpointPort: 10001, - Locality: model.Locality{ - ClusterID: "", - Label: "region1/zone1/subzone1", - }, - Labels: tt.service.Attributes.Labels, - TLSMode: model.IstioMutualTLSModeLabel, - }, - }, - } - - var cfg *config.Config - if tt.destRule != nil { - cfg = &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: "acme", - Namespace: "default", - }, - Spec: tt.destRule, - } - } - cg := NewConfigGenTest(t, TestOptions{ - Instances: instances, - ConfigPointers: []*config.Config{cfg}, - Services: []*model.Service{tt.service}, - }) - cg.MemRegistry.WantGetProxyServiceInstances = instances - proxy := cg.SetupProxy(nil) - cb := NewClusterBuilder(proxy, &model.PushRequest{Push: cg.PushContext()}, nil) - - ec := NewMutableCluster(tt.cluster) - destRule := proxy.SidecarScope.DestinationRule(model.TrafficDirectionOutbound, proxy, tt.service.Hostname) - - subsetClusters := cb.applyDestinationRule(ec, tt.clusterMode, tt.service, tt.port, tt.proxyView, destRule, nil) - if len(subsetClusters) != len(tt.expectedSubsetClusters) { - t.Fatalf("Unexpected subset clusters want %v, got %v. keys=%v", - len(tt.expectedSubsetClusters), len(subsetClusters), xdstest.MapKeys(xdstest.ExtractClusters(subsetClusters))) - } - if len(tt.expectedSubsetClusters) > 0 { - compareClusters(t, tt.expectedSubsetClusters[0], subsetClusters[0]) - } - // Validate that use client protocol configures cluster correctly. - if tt.destRule != nil && tt.destRule.TrafficPolicy != nil && tt.destRule.TrafficPolicy.GetConnectionPool().GetHttp().UseClientProtocol { - if ec.httpProtocolOptions == nil { - t.Errorf("Expected cluster %s to have http protocol options but not found", tt.cluster.Name) - } - if ec.httpProtocolOptions.UpstreamProtocolOptions == nil && - ec.httpProtocolOptions.GetUseDownstreamProtocolConfig() == nil { - t.Errorf("Expected cluster %s to have downstream protocol options but not found", tt.cluster.Name) - } - } - - // Validate that use client protocol configures cluster correctly. - if tt.destRule != nil && tt.destRule.TrafficPolicy != nil && tt.destRule.TrafficPolicy.GetConnectionPool().GetHttp().MaxRequestsPerConnection > 0 { - if ec.httpProtocolOptions == nil { - t.Errorf("Expected cluster %s to have http protocol options but not found", tt.cluster.Name) - } - if ec.httpProtocolOptions.CommonHttpProtocolOptions == nil { - t.Errorf("Expected cluster %s to have common http protocol options but not found", tt.cluster.Name) - } - if ec.httpProtocolOptions.CommonHttpProtocolOptions.MaxRequestsPerConnection.GetValue() != - uint32(tt.destRule.TrafficPolicy.GetConnectionPool().GetHttp().MaxRequestsPerConnection) { - t.Errorf("Unexpected max_requests_per_connection found") - } - - } - - // Validate that ORIGINAL_DST cluster does not have load assignments - for _, subset := range subsetClusters { - if subset.GetType() == cluster.Cluster_ORIGINAL_DST && subset.GetLoadAssignment() != nil { - t.Errorf("Passthrough subsets should not have load assignments") - } - } - }) - } -} - -func compareClusters(t *testing.T, ec *cluster.Cluster, gc *cluster.Cluster) { - // TODO(ramaraochavali): Expand the comparison to more fields. - t.Helper() - if ec.Name != gc.Name { - t.Errorf("Unexpected cluster name want %s, got %s", ec.Name, gc.Name) - } - if ec.GetType() != gc.GetType() { - t.Errorf("Unexpected cluster discovery type want %v, got %v", ec.GetType(), gc.GetType()) - } - if ec.GetType() == cluster.Cluster_EDS && ec.EdsClusterConfig.ServiceName != gc.EdsClusterConfig.ServiceName { - t.Errorf("Unexpected service name in EDS config want %v, got %v", ec.EdsClusterConfig.ServiceName, gc.EdsClusterConfig.ServiceName) - } - if ec.CircuitBreakers != nil { - if ec.CircuitBreakers.Thresholds[0].MaxRetries.Value != gc.CircuitBreakers.Thresholds[0].MaxRetries.Value { - t.Errorf("Unexpected circuit breaker thresholds want %v, got %v", ec.CircuitBreakers.Thresholds[0].MaxRetries, gc.CircuitBreakers.Thresholds[0].MaxRetries) - } - } -} - -func TestMergeTrafficPolicy(t *testing.T) { - cases := []struct { - name string - original *networking.TrafficPolicy - subset *networking.TrafficPolicy - port *model.Port - expected *networking.TrafficPolicy - }{ - { - name: "all nil policies", - original: nil, - subset: nil, - port: nil, - expected: nil, - }, - { - name: "no subset policy", - original: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - }, - }, - }, - subset: nil, - port: nil, - expected: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - }, - }, - }, - }, - { - name: "no parent policy", - original: nil, - subset: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - }, - }, - }, - port: nil, - expected: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - }, - }, - }, - }, - { - name: "merge non-conflicting fields", - original: &networking.TrafficPolicy{ - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_ISTIO_MUTUAL, - }, - }, - subset: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - }, - }, - }, - port: nil, - expected: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - }, - }, - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_ISTIO_MUTUAL, - }, - }, - }, - { - name: "subset overwrite top-level fields", - original: &networking.TrafficPolicy{ - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_ISTIO_MUTUAL, - }, - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - }, - }, - }, - subset: &networking.TrafficPolicy{ - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - }, - }, - port: nil, - expected: &networking.TrafficPolicy{ - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - }, - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - }, - }, - }, - }, - { - name: "merge port level policy, and do not inherit top-level fields", - original: nil, - subset: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: networking.LoadBalancerSettings_ROUND_ROBIN, - }, - }, - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - }, - }, - PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ - { - Port: &networking.PortSelector{ - Number: 8080, - }, - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: networking.LoadBalancerSettings_LEAST_REQUEST, - }, - }, - }, - }, - }, - port: &model.Port{Port: 8080}, - expected: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: networking.LoadBalancerSettings_LEAST_REQUEST, - }, - }, - }, - }, - { - name: "merge port level policy, and do not inherit top-level fields", - original: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: networking.LoadBalancerSettings_ROUND_ROBIN, - }, - }, - OutlierDetection: &networking.OutlierDetection{ - ConsecutiveErrors: 20, - }, - PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ - { - Port: &networking.PortSelector{ - Number: 8080, - }, - OutlierDetection: &networking.OutlierDetection{ - ConsecutiveErrors: 15, - }, - }, - }, - }, - subset: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: networking.LoadBalancerSettings_ROUND_ROBIN, - }, - }, - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - }, - }, - PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ - { - Port: &networking.PortSelector{ - Number: 8080, - }, - OutlierDetection: &networking.OutlierDetection{ - ConsecutiveErrors: 13, - }, - }, - }, - }, - port: &model.Port{Port: 8080}, - expected: &networking.TrafficPolicy{ - OutlierDetection: &networking.OutlierDetection{ - ConsecutiveErrors: 13, - }, - }, - }, - { - name: "default cluster, non-matching port selector", - original: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: networking.LoadBalancerSettings_ROUND_ROBIN, - }, - }, - OutlierDetection: &networking.OutlierDetection{ - ConsecutiveErrors: 20, - }, - PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ - { - Port: &networking.PortSelector{ - Number: 8080, - }, - OutlierDetection: &networking.OutlierDetection{ - ConsecutiveErrors: 15, - }, - }, - }, - }, - subset: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: networking.LoadBalancerSettings_ROUND_ROBIN, - }, - }, - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - }, - }, - PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ - { - Port: &networking.PortSelector{ - Number: 8080, - }, - OutlierDetection: &networking.OutlierDetection{ - ConsecutiveErrors: 13, - }, - }, - }, - }, - port: &model.Port{Port: 9090}, - expected: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: networking.LoadBalancerSettings_ROUND_ROBIN, - }, - }, - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - }, - }, - OutlierDetection: &networking.OutlierDetection{ - ConsecutiveErrors: 20, - }, - }, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - policy := MergeTrafficPolicy(tt.original, tt.subset, tt.port) - if !reflect.DeepEqual(policy, tt.expected) { - t.Errorf("Unexpected merged TrafficPolicy. want %v, got %v", tt.expected, policy) - } - }) - } -} - -func TestApplyEdsConfig(t *testing.T) { - cases := []struct { - name string - cluster *cluster.Cluster - edsConfig *cluster.Cluster_EdsClusterConfig - }{ - { - name: "non eds type of cluster", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STRICT_DNS}}, - edsConfig: nil, - }, - { - name: "eds type of cluster", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}}, - edsConfig: &cluster.Cluster_EdsClusterConfig{ - ServiceName: "foo", - EdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_Ads{ - Ads: &core.AggregatedConfigSource{}, - }, - InitialFetchTimeout: durationpb.New(0), - ResourceApiVersion: core.ApiVersion_V3, - }, - }, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - maybeApplyEdsConfig(tt.cluster) - if !reflect.DeepEqual(tt.cluster.EdsClusterConfig, tt.edsConfig) { - t.Errorf("Unexpected Eds config in cluster. want %v, got %v", tt.edsConfig, tt.cluster.EdsClusterConfig) - } - }) - } -} - -func TestBuildDefaultCluster(t *testing.T) { - servicePort := &model.Port{ - Name: "default", - Port: 8080, - Protocol: protocol.HTTP, - } - - cases := []struct { - name string - clusterName string - discovery cluster.Cluster_DiscoveryType - endpoints []*endpoint.LocalityLbEndpoints - direction model.TrafficDirection - external bool - expectedCluster *cluster.Cluster - }{ - { - name: "default EDS cluster", - clusterName: "foo", - discovery: cluster.Cluster_EDS, - endpoints: nil, - direction: model.TrafficDirectionOutbound, - external: false, - expectedCluster: &cluster.Cluster{ - Name: "foo", - ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}, - ConnectTimeout: &durationpb.Duration{Seconds: 10, Nanos: 1}, - CircuitBreakers: &cluster.CircuitBreakers{ - Thresholds: []*cluster.CircuitBreakers_Thresholds{getDefaultCircuitBreakerThresholds()}, - }, - Filters: []*cluster.Filter{xdsfilters.TCPClusterMx}, - LbPolicy: defaultLBAlgorithm(), - Metadata: &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - util.IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "services": {Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{Values: []*structpb.Value{ - {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{ - "host": { - Kind: &structpb.Value_StringValue{ - StringValue: "host", - }, - }, - "name": { - Kind: &structpb.Value_StringValue{ - StringValue: "svc", - }, - }, - "namespace": { - Kind: &structpb.Value_StringValue{ - StringValue: "default", - }, - }, - }}}}, - }}}}, - "default_original_port": { - Kind: &structpb.Value_NumberValue{ - NumberValue: float64(8080), - }, - }, - }, - }, - }, - }, - EdsClusterConfig: &cluster.Cluster_EdsClusterConfig{ - ServiceName: "foo", - EdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_Ads{ - Ads: &core.AggregatedConfigSource{}, - }, - InitialFetchTimeout: durationpb.New(0), - ResourceApiVersion: core.ApiVersion_V3, - }, - }, - }, - }, - { - name: "static cluster with no endpoints", - clusterName: "foo", - discovery: cluster.Cluster_STATIC, - endpoints: nil, - direction: model.TrafficDirectionOutbound, - external: false, - expectedCluster: nil, - }, - { - name: "strict DNS cluster with no endpoints", - clusterName: "foo", - discovery: cluster.Cluster_STRICT_DNS, - endpoints: nil, - direction: model.TrafficDirectionOutbound, - external: false, - expectedCluster: nil, - }, - { - name: "static cluster with endpoints", - clusterName: "foo", - discovery: cluster.Cluster_STATIC, - endpoints: []*endpoint.LocalityLbEndpoints{ - { - Locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, - LbEndpoints: []*endpoint.LbEndpoint{}, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - Priority: 0, - }, - }, - direction: model.TrafficDirectionOutbound, - external: false, - expectedCluster: &cluster.Cluster{ - Name: "foo", - ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STATIC}, - ConnectTimeout: &durationpb.Duration{Seconds: 10, Nanos: 1}, - Filters: []*cluster.Filter{xdsfilters.TCPClusterMx}, - LbPolicy: defaultLBAlgorithm(), - LoadAssignment: &endpoint.ClusterLoadAssignment{ - ClusterName: "foo", - Endpoints: []*endpoint.LocalityLbEndpoints{ - { - Locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, - LbEndpoints: []*endpoint.LbEndpoint{}, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - Priority: 0, - }, - }, - }, - CircuitBreakers: &cluster.CircuitBreakers{ - Thresholds: []*cluster.CircuitBreakers_Thresholds{getDefaultCircuitBreakerThresholds()}, - }, - Metadata: &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - util.IstioMetadataKey: {Fields: map[string]*structpb.Value{ - "services": {Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{Values: []*structpb.Value{ - {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{ - "host": { - Kind: &structpb.Value_StringValue{ - StringValue: "host", - }, - }, - "name": { - Kind: &structpb.Value_StringValue{ - StringValue: "svc", - }, - }, - "namespace": { - Kind: &structpb.Value_StringValue{ - StringValue: "default", - }, - }, - }}}}, - }}}}, - "default_original_port": { - Kind: &structpb.Value_NumberValue{ - NumberValue: float64(8080), - }, - }, - }}, - }, - }, - }, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - mesh := testMesh() - cg := NewConfigGenTest(t, TestOptions{MeshConfig: mesh}) - cb := NewClusterBuilder(cg.SetupProxy(nil), &model.PushRequest{Push: cg.PushContext()}, nil) - service := &model.Service{ - Ports: model.PortList{ - servicePort, - }, - Hostname: "host", - MeshExternal: false, - Attributes: model.ServiceAttributes{Name: "svc", Namespace: "default"}, - } - defaultCluster := cb.buildDefaultCluster(tt.clusterName, tt.discovery, tt.endpoints, tt.direction, servicePort, service, nil) - if defaultCluster != nil { - _ = cb.applyDestinationRule(defaultCluster, DefaultClusterMode, service, servicePort, cb.proxyView, nil, nil) - } - - if diff := cmp.Diff(defaultCluster.build(), tt.expectedCluster, protocmp.Transform()); diff != "" { - t.Errorf("Unexpected default cluster, diff: %v", diff) - } - }) - } -} - -func TestBuildLocalityLbEndpoints(t *testing.T) { - proxy := &model.Proxy{ - Metadata: &model.NodeMetadata{ - ClusterID: "cluster-1", - }, - } - servicePort := &model.Port{ - Name: "default", - Port: 8080, - Protocol: protocol.HTTP, - } - service := &model.Service{ - Hostname: host.Name("*.example.org"), - Ports: model.PortList{servicePort}, - Attributes: model.ServiceAttributes{ - Name: "TestService", - Namespace: "test-ns", - }, - } - - cases := []struct { - name string - mesh *meshconfig.MeshConfig - labels labels.Instance - instances []*model.ServiceInstance - expected []*endpoint.LocalityLbEndpoints - }{ - { - name: "basics", - mesh: testMesh(), - instances: []*model.ServiceInstance{ - { - Service: service, - ServicePort: servicePort, - Endpoint: &model.IstioEndpoint{ - Address: "192.168.1.1", - EndpointPort: 10001, - WorkloadName: "workload-1", - Namespace: "namespace-1", - Locality: model.Locality{ - ClusterID: "cluster-1", - Label: "region1/zone1/subzone1", - }, - LbWeight: 30, - Network: "nw-0", - }, - }, - { - Service: service, - ServicePort: servicePort, - Endpoint: &model.IstioEndpoint{ - Address: "192.168.1.2", - EndpointPort: 10001, - WorkloadName: "workload-2", - Namespace: "namespace-2", - Locality: model.Locality{ - ClusterID: "cluster-2", - Label: "region1/zone1/subzone1", - }, - LbWeight: 30, - Network: "nw-1", - }, - }, - { - Service: service, - ServicePort: servicePort, - Endpoint: &model.IstioEndpoint{ - Address: "192.168.1.3", - EndpointPort: 10001, - WorkloadName: "workload-3", - Namespace: "namespace-3", - Locality: model.Locality{ - ClusterID: "cluster-3", - Label: "region2/zone1/subzone1", - }, - LbWeight: 40, - Network: "", - }, - }, - { - Service: service, - ServicePort: servicePort, - Endpoint: &model.IstioEndpoint{ - Address: "192.168.1.4", - EndpointPort: 10001, - WorkloadName: "workload-1", - Namespace: "namespace-1", - Locality: model.Locality{ - ClusterID: "cluster-1", - Label: "region1/zone1/subzone1", - }, - LbWeight: 30, - Network: "filtered-out", - }, - }, - }, - expected: []*endpoint.LocalityLbEndpoints{ - { - Locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 60, - }, - LbEndpoints: []*endpoint.LbEndpoint{ - { - HostIdentifier: &endpoint.LbEndpoint_Endpoint{ - Endpoint: &endpoint.Endpoint{ - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - Address: "192.168.1.1", - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 10001, - }, - }, - }, - }, - }, - }, - Metadata: util.BuildLbEndpointMetadata("nw-0", "", "workload-1", "namespace-1", "cluster-1", map[string]string{}), - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 30, - }, - }, - { - HostIdentifier: &endpoint.LbEndpoint_Endpoint{ - Endpoint: &endpoint.Endpoint{ - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - Address: "192.168.1.2", - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 10001, - }, - }, - }, - }, - }, - }, - Metadata: util.BuildLbEndpointMetadata("nw-1", "", "workload-2", "namespace-2", "cluster-2", map[string]string{}), - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 30, - }, - }, - }, - }, - { - Locality: &core.Locality{ - Region: "region2", - Zone: "zone1", - SubZone: "subzone1", - }, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 40, - }, - LbEndpoints: []*endpoint.LbEndpoint{ - { - HostIdentifier: &endpoint.LbEndpoint_Endpoint{ - Endpoint: &endpoint.Endpoint{ - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - Address: "192.168.1.3", - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 10001, - }, - }, - }, - }, - }, - }, - Metadata: util.BuildLbEndpointMetadata("", "", "workload-3", "namespace-3", "cluster-3", map[string]string{}), - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 40, - }, - }, - }, - }, - }, - }, - { - name: "cluster local", - mesh: withClusterLocalHosts(testMesh(), "*.example.org"), - instances: []*model.ServiceInstance{ - { - Service: service, - ServicePort: servicePort, - Endpoint: &model.IstioEndpoint{ - Address: "192.168.1.1", - EndpointPort: 10001, - Locality: model.Locality{ - ClusterID: "cluster-1", - Label: "region1/zone1/subzone1", - }, - LbWeight: 30, - }, - }, - { - Service: service, - ServicePort: servicePort, - Endpoint: &model.IstioEndpoint{ - Address: "192.168.1.2", - EndpointPort: 10001, - Locality: model.Locality{ - ClusterID: "cluster-2", - Label: "region1/zone1/subzone1", - }, - LbWeight: 30, - }, - }, - }, - expected: []*endpoint.LocalityLbEndpoints{ - { - Locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 30, - }, - LbEndpoints: []*endpoint.LbEndpoint{ - { - HostIdentifier: &endpoint.LbEndpoint_Endpoint{ - Endpoint: &endpoint.Endpoint{ - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - Address: "192.168.1.1", - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 10001, - }, - }, - }, - }, - }, - }, - Metadata: util.BuildLbEndpointMetadata("", "", "", "", "cluster-1", map[string]string{}), - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 30, - }, - }, - }, - }, - }, - }, - { - name: "subset cluster endpoints with labels", - mesh: testMesh(), - labels: labels.Instance{"version": "v1"}, - instances: []*model.ServiceInstance{ - { - Service: service, - ServicePort: servicePort, - Endpoint: &model.IstioEndpoint{ - Address: "192.168.1.1", - EndpointPort: 10001, - WorkloadName: "workload-1", - Namespace: "namespace-1", - Labels: map[string]string{ - "version": "v1", - "app": "example", - }, - Locality: model.Locality{ - ClusterID: "cluster-1", - Label: "region1/zone1/subzone1", - }, - LbWeight: 30, - Network: "nw-0", - }, - }, - { - Service: service, - ServicePort: servicePort, - Endpoint: &model.IstioEndpoint{ - Address: "192.168.1.2", - EndpointPort: 10001, - WorkloadName: "workload-2", - Namespace: "namespace-2", - Labels: map[string]string{ - "version": "v2", - "app": "example", - }, - Locality: model.Locality{ - ClusterID: "cluster-2", - Label: "region1/zone1/subzone1", - }, - LbWeight: 30, - Network: "nw-1", - }, - }, - { - Service: service, - ServicePort: servicePort, - Endpoint: &model.IstioEndpoint{ - Address: "192.168.1.3", - EndpointPort: 10001, - WorkloadName: "workload-3", - Namespace: "namespace-3", - Labels: map[string]string{ - "version": "v3", - "app": "example", - }, - Locality: model.Locality{ - ClusterID: "cluster-3", - Label: "region2/zone1/subzone1", - }, - LbWeight: 40, - Network: "", - }, - }, - { - Service: service, - ServicePort: servicePort, - Endpoint: &model.IstioEndpoint{ - Address: "192.168.1.4", - EndpointPort: 10001, - WorkloadName: "workload-1", - Namespace: "namespace-1", - Labels: map[string]string{ - "version": "v4", - "app": "example", - }, - Locality: model.Locality{ - ClusterID: "cluster-1", - Label: "region1/zone1/subzone1", - }, - LbWeight: 30, - Network: "filtered-out", - }, - }, - }, - expected: []*endpoint.LocalityLbEndpoints{ - { - Locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 30, - }, - LbEndpoints: []*endpoint.LbEndpoint{ - { - HostIdentifier: &endpoint.LbEndpoint_Endpoint{ - Endpoint: &endpoint.Endpoint{ - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - Address: "192.168.1.1", - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 10001, - }, - }, - }, - }, - }, - }, - Metadata: util.BuildLbEndpointMetadata("nw-0", "", "workload-1", "namespace-1", "cluster-1", map[string]string{}), - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 30, - }, - }, - }, - }, - }, - }, - } - - sortEndpoints := func(endpoints []*endpoint.LocalityLbEndpoints) { - sort.SliceStable(endpoints, func(i, j int) bool { - if strings.Compare(endpoints[i].Locality.Region, endpoints[j].Locality.Region) < 0 { - return true - } - if strings.Compare(endpoints[i].Locality.Zone, endpoints[j].Locality.Zone) < 0 { - return true - } - return strings.Compare(endpoints[i].Locality.SubZone, endpoints[j].Locality.SubZone) < 0 - }) - } - - for _, tt := range cases { - for _, resolution := range []model.Resolution{model.DNSLB, model.DNSRoundRobinLB} { - t.Run(fmt.Sprintf("%s_%s", tt.name, resolution), func(t *testing.T) { - service.Resolution = resolution - cg := NewConfigGenTest(t, TestOptions{ - MeshConfig: tt.mesh, - Services: []*model.Service{service}, - Instances: tt.instances, - }) - - cb := NewClusterBuilder(cg.SetupProxy(proxy), &model.PushRequest{Push: cg.PushContext()}, nil) - view := (&model.Proxy{ - Metadata: &model.NodeMetadata{ - RequestedNetworkView: []string{"nw-0", "nw-1"}, - }, - }).GetView() - actual := cb.buildLocalityLbEndpoints(view, service, 8080, tt.labels) - sortEndpoints(actual) - if v := cmp.Diff(tt.expected, actual, protocmp.Transform()); v != "" { - t.Fatalf("Expected (-) != actual (+):\n%s", v) - } - }) - } - } -} - -func TestBuildPassthroughClusters(t *testing.T) { - cases := []struct { - name string - ips []string - ipv4Expected bool - ipv6Expected bool - }{ - { - name: "both ipv4 and ipv6", - ips: []string{"6.6.6.6", "::1"}, - ipv4Expected: true, - ipv6Expected: true, - }, - { - name: "ipv4 only", - ips: []string{"6.6.6.6"}, - ipv4Expected: true, - ipv6Expected: false, - }, - { - name: "ipv6 only", - ips: []string{"::1"}, - ipv4Expected: false, - ipv6Expected: true, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - proxy := &model.Proxy{IPAddresses: tt.ips} - cg := NewConfigGenTest(t, TestOptions{}) - - cb := NewClusterBuilder(cg.SetupProxy(proxy), &model.PushRequest{Push: cg.PushContext()}, nil) - clusters := cb.buildInboundPassthroughClusters() - - var hasIpv4, hasIpv6 bool - for _, c := range clusters { - hasIpv4 = hasIpv4 || c.Name == util.InboundPassthroughClusterIpv4 - hasIpv6 = hasIpv6 || c.Name == util.InboundPassthroughClusterIpv6 - } - if hasIpv4 != tt.ipv4Expected { - t.Errorf("Unexpected Ipv4 Passthrough Cluster, want %v got %v", tt.ipv4Expected, hasIpv4) - } - if hasIpv6 != tt.ipv6Expected { - t.Errorf("Unexpected Ipv6 Passthrough Cluster, want %v got %v", tt.ipv6Expected, hasIpv6) - } - - passthrough := xdstest.ExtractCluster(util.InboundPassthroughClusterIpv4, clusters) - if passthrough == nil { - passthrough = xdstest.ExtractCluster(util.InboundPassthroughClusterIpv6, clusters) - } - // Validate that Passthrough Cluster LB Policy is set correctly. - if passthrough.GetType() != cluster.Cluster_ORIGINAL_DST || passthrough.GetLbPolicy() != cluster.Cluster_CLUSTER_PROVIDED { - t.Errorf("Unexpected Discovery type or Lb policy, got Discovery type: %v, Lb Policy: %v", passthrough.GetType(), passthrough.GetLbPolicy()) - } - }) - } -} - -func TestApplyUpstreamTLSSettings(t *testing.T) { - istioMutualTLSSettings := &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_ISTIO_MUTUAL, - SubjectAltNames: []string{"custom.foo.com"}, - Sni: "custom.foo.com", - } - mutualTLSSettingsWithCerts := &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - CaCertificates: constants.DefaultRootCert, - ClientCertificate: constants.DefaultCertChain, - PrivateKey: constants.DefaultKey, - SubjectAltNames: []string{"custom.foo.com"}, - Sni: "custom.foo.com", - } - simpleTLSSettingsWithCerts := &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - CaCertificates: constants.DefaultRootCert, - SubjectAltNames: []string{"custom.foo.com"}, - Sni: "custom.foo.com", - } - - tests := []struct { - name string - mtlsCtx mtlsContextType - discoveryType cluster.Cluster_DiscoveryType - tls *networking.ClientTLSSettings - h2 bool - expectTransportSocket bool - expectTransportSocketMatch bool - - validateTLSContext func(t *testing.T, ctx *tls.UpstreamTlsContext) - }{ - { - name: "user specified without tls", - mtlsCtx: userSupplied, - discoveryType: cluster.Cluster_EDS, - tls: nil, - expectTransportSocket: false, - expectTransportSocketMatch: false, - }, - { - name: "user specified with istio_mutual tls", - mtlsCtx: userSupplied, - discoveryType: cluster.Cluster_EDS, - tls: istioMutualTLSSettings, - expectTransportSocket: true, - expectTransportSocketMatch: false, - validateTLSContext: func(t *testing.T, ctx *tls.UpstreamTlsContext) { - if got := ctx.CommonTlsContext.GetAlpnProtocols(); !reflect.DeepEqual(got, util.ALPNInMeshWithMxc) { - t.Fatalf("expected alpn list %v; got %v", util.ALPNInMeshWithMxc, got) - } - }, - }, - { - name: "user specified with istio_mutual tls with h2", - mtlsCtx: userSupplied, - discoveryType: cluster.Cluster_EDS, - tls: istioMutualTLSSettings, - expectTransportSocket: true, - expectTransportSocketMatch: false, - h2: true, - validateTLSContext: func(t *testing.T, ctx *tls.UpstreamTlsContext) { - if got := ctx.CommonTlsContext.GetAlpnProtocols(); !reflect.DeepEqual(got, util.ALPNInMeshH2WithMxc) { - t.Fatalf("expected alpn list %v; got %v", util.ALPNInMeshH2WithMxc, got) - } - }, - }, - { - name: "user specified simple tls", - mtlsCtx: userSupplied, - discoveryType: cluster.Cluster_EDS, - tls: simpleTLSSettingsWithCerts, - expectTransportSocket: true, - expectTransportSocketMatch: false, - validateTLSContext: func(t *testing.T, ctx *tls.UpstreamTlsContext) { - rootName := "file-root:" + mutualTLSSettingsWithCerts.CaCertificates - if got := ctx.CommonTlsContext.GetCombinedValidationContext().GetValidationContextSdsSecretConfig().GetName(); rootName != got { - t.Fatalf("expected root name %v got %v", rootName, got) - } - if got := ctx.CommonTlsContext.GetAlpnProtocols(); got != nil { - t.Fatalf("expected alpn list nil as not h2 or Istio_Mutual TLS Setting; got %v", got) - } - if got := ctx.GetSni(); got != simpleTLSSettingsWithCerts.Sni { - t.Fatalf("expected TLSContext SNI %v; got %v", simpleTLSSettingsWithCerts.Sni, got) - } - }, - }, - { - name: "user specified simple tls with h2", - mtlsCtx: userSupplied, - discoveryType: cluster.Cluster_EDS, - tls: simpleTLSSettingsWithCerts, - expectTransportSocket: true, - expectTransportSocketMatch: false, - h2: true, - validateTLSContext: func(t *testing.T, ctx *tls.UpstreamTlsContext) { - rootName := "file-root:" + mutualTLSSettingsWithCerts.CaCertificates - if got := ctx.CommonTlsContext.GetCombinedValidationContext().GetValidationContextSdsSecretConfig().GetName(); rootName != got { - t.Fatalf("expected root name %v got %v", rootName, got) - } - if got := ctx.CommonTlsContext.GetAlpnProtocols(); !reflect.DeepEqual(got, util.ALPNH2Only) { - t.Fatalf("expected alpn list %v; got %v", util.ALPNH2Only, got) - } - if got := ctx.GetSni(); got != simpleTLSSettingsWithCerts.Sni { - t.Fatalf("expected TLSContext SNI %v; got %v", simpleTLSSettingsWithCerts.Sni, got) - } - }, - }, - { - name: "user specified mutual tls", - mtlsCtx: userSupplied, - discoveryType: cluster.Cluster_EDS, - tls: mutualTLSSettingsWithCerts, - expectTransportSocket: true, - expectTransportSocketMatch: false, - validateTLSContext: func(t *testing.T, ctx *tls.UpstreamTlsContext) { - rootName := "file-root:" + mutualTLSSettingsWithCerts.CaCertificates - certName := fmt.Sprintf("file-cert:%s~%s", mutualTLSSettingsWithCerts.ClientCertificate, mutualTLSSettingsWithCerts.PrivateKey) - if got := ctx.CommonTlsContext.GetCombinedValidationContext().GetValidationContextSdsSecretConfig().GetName(); rootName != got { - t.Fatalf("expected root name %v got %v", rootName, got) - } - if got := ctx.CommonTlsContext.GetTlsCertificateSdsSecretConfigs()[0].GetName(); certName != got { - t.Fatalf("expected cert name %v got %v", certName, got) - } - if got := ctx.CommonTlsContext.GetAlpnProtocols(); got != nil { - t.Fatalf("expected alpn list nil as not h2 or Istio_Mutual TLS Setting; got %v", got) - } - if got := ctx.GetSni(); got != mutualTLSSettingsWithCerts.Sni { - t.Fatalf("expected TLSContext SNI %v; got %v", mutualTLSSettingsWithCerts.Sni, got) - } - }, - }, - { - name: "user specified mutual tls with h2", - mtlsCtx: userSupplied, - discoveryType: cluster.Cluster_EDS, - tls: mutualTLSSettingsWithCerts, - expectTransportSocket: true, - expectTransportSocketMatch: false, - h2: true, - validateTLSContext: func(t *testing.T, ctx *tls.UpstreamTlsContext) { - rootName := "file-root:" + mutualTLSSettingsWithCerts.CaCertificates - certName := fmt.Sprintf("file-cert:%s~%s", mutualTLSSettingsWithCerts.ClientCertificate, mutualTLSSettingsWithCerts.PrivateKey) - if got := ctx.CommonTlsContext.GetCombinedValidationContext().GetValidationContextSdsSecretConfig().GetName(); rootName != got { - t.Fatalf("expected root name %v got %v", rootName, got) - } - if got := ctx.CommonTlsContext.GetTlsCertificateSdsSecretConfigs()[0].GetName(); certName != got { - t.Fatalf("expected cert name %v got %v", certName, got) - } - if got := ctx.CommonTlsContext.GetAlpnProtocols(); !reflect.DeepEqual(got, util.ALPNH2Only) { - t.Fatalf("expected alpn list %v; got %v", util.ALPNH2Only, got) - } - if got := ctx.GetSni(); got != mutualTLSSettingsWithCerts.Sni { - t.Fatalf("expected TLSContext SNI %v; got %v", mutualTLSSettingsWithCerts.Sni, got) - } - }, - }, - { - name: "auto detect with tls", - mtlsCtx: autoDetected, - discoveryType: cluster.Cluster_EDS, - tls: istioMutualTLSSettings, - expectTransportSocket: false, - expectTransportSocketMatch: true, - validateTLSContext: func(t *testing.T, ctx *tls.UpstreamTlsContext) { - if got := ctx.CommonTlsContext.GetAlpnProtocols(); !reflect.DeepEqual(got, util.ALPNInMeshWithMxc) { - t.Fatalf("expected alpn list %v; got %v", util.ALPNInMeshWithMxc, got) - } - }, - }, - { - name: "auto detect with tls and h2 options", - mtlsCtx: autoDetected, - discoveryType: cluster.Cluster_EDS, - tls: istioMutualTLSSettings, - expectTransportSocket: false, - expectTransportSocketMatch: true, - h2: true, - validateTLSContext: func(t *testing.T, ctx *tls.UpstreamTlsContext) { - if got := ctx.CommonTlsContext.GetAlpnProtocols(); !reflect.DeepEqual(got, util.ALPNInMeshH2WithMxc) { - t.Fatalf("expected alpn list %v; got %v", util.ALPNInMeshH2WithMxc, got) - } - }, - }, - } - - proxy := &model.Proxy{ - Type: model.SidecarProxy, - Metadata: &model.NodeMetadata{}, - IstioVersion: &model.IstioVersion{Major: 1, Minor: 5}, - } - push := model.NewPushContext() - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - cb := NewClusterBuilder(proxy, &model.PushRequest{Push: push}, model.DisabledCache{}) - opts := &buildClusterOpts{ - mutable: NewMutableCluster(&cluster.Cluster{ - ClusterDiscoveryType: &cluster.Cluster_Type{Type: test.discoveryType}, - }), - mesh: push.Mesh, - } - if test.h2 { - cb.setH2Options(opts.mutable) - } - cb.applyUpstreamTLSSettings(opts, test.tls, test.mtlsCtx) - - if test.expectTransportSocket && opts.mutable.cluster.TransportSocket == nil || - !test.expectTransportSocket && opts.mutable.cluster.TransportSocket != nil { - t.Errorf("Expected TransportSocket %v", test.expectTransportSocket) - } - if test.expectTransportSocketMatch && opts.mutable.cluster.TransportSocketMatches == nil || - !test.expectTransportSocketMatch && opts.mutable.cluster.TransportSocketMatches != nil { - t.Errorf("Expected TransportSocketMatch %v", test.expectTransportSocketMatch) - } - - if test.validateTLSContext != nil { - ctx := &tls.UpstreamTlsContext{} - if test.expectTransportSocket { - if err := opts.mutable.cluster.TransportSocket.GetTypedConfig().UnmarshalTo(ctx); err != nil { - t.Fatal(err) - } - } else if test.expectTransportSocketMatch { - if err := opts.mutable.cluster.TransportSocketMatches[0].TransportSocket.GetTypedConfig().UnmarshalTo(ctx); err != nil { - t.Fatal(err) - } - } - test.validateTLSContext(t, ctx) - } - }) - } -} - -type expectedResult struct { - tlsContext *tls.UpstreamTlsContext - err error -} - -// TestBuildUpstreamClusterTLSContext tests the buildUpstreamClusterTLSContext function -func TestBuildUpstreamClusterTLSContext(t *testing.T) { - clientCert := "/path/to/cert" - rootCert := "path/to/cacert" - clientKey := "/path/to/key" - - credentialName := "some-fake-credential" - - testCases := []struct { - name string - opts *buildClusterOpts - tls *networking.ClientTLSSettings - h2 bool - router bool - result expectedResult - enableAutoSni bool - enableVerifyCertAtClient bool - }{ - { - name: "tls mode disabled", - opts: &buildClusterOpts{ - mutable: NewMutableCluster(&cluster.Cluster{ - Name: "test-cluster", - }), - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_DISABLE, - }, - result: expectedResult{nil, nil}, - }, - { - name: "tls mode ISTIO_MUTUAL", - opts: &buildClusterOpts{ - mutable: newTestCluster(), - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_ISTIO_MUTUAL, - SubjectAltNames: []string{"SAN"}, - Sni: "some-sni.com", - }, - result: expectedResult{ - tlsContext: &tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - // if not specified, envoy use TLSv1_2 as default for client. - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - }, - TlsCertificateSdsSecretConfigs: []*tls.SdsSecretConfig{ - { - Name: "default", - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"}, - }, - }, - }, - }, - }, - InitialFetchTimeout: durationpb.New(time.Second * 0), - ResourceApiVersion: core.ApiVersion_V3, - }, - }, - }, - ValidationContextType: &tls.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &tls.CertificateValidationContext{MatchSubjectAltNames: util.StringToExactMatch([]string{"SAN"})}, - ValidationContextSdsSecretConfig: &tls.SdsSecretConfig{ - Name: "ROOTCA", - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"}, - }, - }, - }, - }, - }, - InitialFetchTimeout: durationpb.New(time.Second * 0), - ResourceApiVersion: core.ApiVersion_V3, - }, - }, - }, - }, - AlpnProtocols: util.ALPNInMeshWithMxc, - }, - Sni: "some-sni.com", - }, - err: nil, - }, - }, - { - name: "tls mode ISTIO_MUTUAL and H2", - opts: &buildClusterOpts{ - mutable: newTestCluster(), - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_ISTIO_MUTUAL, - SubjectAltNames: []string{"SAN"}, - Sni: "some-sni.com", - }, - h2: true, - result: expectedResult{ - tlsContext: &tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - // if not specified, envoy use TLSv1_2 as default for client. - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - }, - TlsCertificateSdsSecretConfigs: []*tls.SdsSecretConfig{ - { - Name: "default", - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"}, - }, - }, - }, - }, - }, - InitialFetchTimeout: durationpb.New(time.Second * 0), - ResourceApiVersion: core.ApiVersion_V3, - }, - }, - }, - ValidationContextType: &tls.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &tls.CertificateValidationContext{MatchSubjectAltNames: util.StringToExactMatch([]string{"SAN"})}, - ValidationContextSdsSecretConfig: &tls.SdsSecretConfig{ - Name: "ROOTCA", - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"}, - }, - }, - }, - }, - }, - InitialFetchTimeout: durationpb.New(time.Second * 0), - ResourceApiVersion: core.ApiVersion_V3, - }, - }, - }, - }, - AlpnProtocols: util.ALPNInMeshH2WithMxc, - }, - Sni: "some-sni.com", - }, - err: nil, - }, - }, - { - name: "tls mode SIMPLE, with no certs specified in tls", - opts: &buildClusterOpts{ - mutable: newTestCluster(), - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - SubjectAltNames: []string{"SAN"}, - Sni: "some-sni.com", - }, - result: expectedResult{ - tlsContext: &tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - // if not specified, envoy use TLSv1_2 as default for client. - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - }, - ValidationContextType: &tls.CommonTlsContext_ValidationContext{}, - }, - Sni: "some-sni.com", - }, - err: nil, - }, - }, - { - name: "tls mode SIMPLE, with AutoSni enabled and no sni specified in tls", - opts: &buildClusterOpts{ - mutable: newTestCluster(), - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - SubjectAltNames: []string{"SAN"}, - }, - result: expectedResult{ - tlsContext: &tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - // if not specified, envoy use TLSv1_2 as default for client. - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - }, - ValidationContextType: &tls.CommonTlsContext_ValidationContext{}, - }, - }, - err: nil, - }, - enableAutoSni: true, - }, - { - name: "tls mode SIMPLE, with AutoSni enabled and sni specified in tls", - opts: &buildClusterOpts{ - mutable: newTestCluster(), - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - SubjectAltNames: []string{"SAN"}, - Sni: "some-sni.com", - }, - result: expectedResult{ - tlsContext: &tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - // if not specified, envoy use TLSv1_2 as default for client. - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - }, - ValidationContextType: &tls.CommonTlsContext_ValidationContext{}, - }, - Sni: "some-sni.com", - }, - err: nil, - }, - enableAutoSni: true, - }, - { - name: "tls mode SIMPLE, with VerifyCert and AutoSni enabled with SubjectAltNames set", - opts: &buildClusterOpts{ - mutable: newTestCluster(), - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - SubjectAltNames: []string{"SAN"}, - Sni: "some-sni.com", - }, - result: expectedResult{ - tlsContext: &tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - // if not specified, envoy use TLSv1_2 as default for client. - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - }, - ValidationContextType: &tls.CommonTlsContext_ValidationContext{}, - }, - Sni: "some-sni.com", - }, - err: nil, - }, - enableAutoSni: true, - enableVerifyCertAtClient: true, - }, - { - name: "tls mode SIMPLE, with VerifyCert and AutoSni enabled without SubjectAltNames set", - opts: &buildClusterOpts{ - mutable: newTestCluster(), - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - Sni: "some-sni.com", - }, - result: expectedResult{ - tlsContext: &tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - // if not specified, envoy use TLSv1_2 as default for client. - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - }, - ValidationContextType: &tls.CommonTlsContext_ValidationContext{}, - }, - Sni: "some-sni.com", - }, - err: nil, - }, - enableAutoSni: true, - enableVerifyCertAtClient: true, - }, - { - name: "tls mode SIMPLE, with certs specified in tls", - opts: &buildClusterOpts{ - mutable: newTestCluster(), - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - CaCertificates: rootCert, - SubjectAltNames: []string{"SAN"}, - Sni: "some-sni.com", - }, - result: expectedResult{ - tlsContext: &tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - // if not specified, envoy use TLSv1_2 as default for client. - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - }, - ValidationContextType: &tls.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &tls.CertificateValidationContext{MatchSubjectAltNames: util.StringToExactMatch([]string{"SAN"})}, - ValidationContextSdsSecretConfig: &tls.SdsSecretConfig{ - Name: fmt.Sprintf("file-root:%s", rootCert), - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"}, - }, - }, - }, - }, - }, - ResourceApiVersion: core.ApiVersion_V3, - }, - }, - }, - }, - }, - Sni: "some-sni.com", - }, - err: nil, - }, - }, - { - name: "tls mode SIMPLE, with certs specified in tls with h2", - opts: &buildClusterOpts{ - mutable: newH2TestCluster(), - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - CaCertificates: rootCert, - SubjectAltNames: []string{"SAN"}, - Sni: "some-sni.com", - }, - h2: true, - result: expectedResult{ - tlsContext: &tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - // if not specified, envoy use TLSv1_2 as default for client. - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - }, - ValidationContextType: &tls.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &tls.CertificateValidationContext{MatchSubjectAltNames: util.StringToExactMatch([]string{"SAN"})}, - ValidationContextSdsSecretConfig: &tls.SdsSecretConfig{ - Name: fmt.Sprintf("file-root:%s", rootCert), - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"}, - }, - }, - }, - }, - }, - ResourceApiVersion: core.ApiVersion_V3, - }, - }, - }, - }, - AlpnProtocols: util.ALPNH2Only, - }, - Sni: "some-sni.com", - }, - err: nil, - }, - }, - { - name: "tls mode SIMPLE, with certs specified in tls", - opts: &buildClusterOpts{ - mutable: newTestCluster(), - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - CaCertificates: rootCert, - SubjectAltNames: []string{"SAN"}, - Sni: "some-sni.com", - }, - result: expectedResult{ - tlsContext: &tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - // if not specified, envoy use TLSv1_2 as default for client. - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - }, - ValidationContextType: &tls.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &tls.CertificateValidationContext{MatchSubjectAltNames: util.StringToExactMatch([]string{"SAN"})}, - ValidationContextSdsSecretConfig: &tls.SdsSecretConfig{ - Name: fmt.Sprintf("file-root:%s", rootCert), - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"}, - }, - }, - }, - }, - }, - ResourceApiVersion: core.ApiVersion_V3, - }, - }, - }, - }, - }, - Sni: "some-sni.com", - }, - err: nil, - }, - }, - { - name: "tls mode SIMPLE, with SANs specified in service entries", - opts: &buildClusterOpts{ - mutable: newTestCluster(), - serviceAccounts: []string{"se-san.com"}, - serviceRegistry: provider.External, - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - CaCertificates: rootCert, - Sni: "some-sni.com", - }, - result: expectedResult{ - tlsContext: &tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - // if not specified, envoy use TLSv1_2 as default for client. - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - }, - ValidationContextType: &tls.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &tls.CertificateValidationContext{MatchSubjectAltNames: util.StringToExactMatch([]string{"se-san.com"})}, - ValidationContextSdsSecretConfig: &tls.SdsSecretConfig{ - Name: fmt.Sprintf("file-root:%s", rootCert), - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"}, - }, - }, - }, - }, - }, - ResourceApiVersion: core.ApiVersion_V3, - }, - }, - }, - }, - }, - Sni: "some-sni.com", - }, - err: nil, - }, - }, - { - name: "tls mode MUTUAL, with no client certificate", - opts: &buildClusterOpts{ - mutable: newTestCluster(), - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - ClientCertificate: "", - PrivateKey: "some-fake-key", - }, - result: expectedResult{ - nil, - fmt.Errorf("client cert must be provided"), - }, - }, - { - name: "tls mode MUTUAL, with no client key", - opts: &buildClusterOpts{ - mutable: newTestCluster(), - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - ClientCertificate: "some-fake-cert", - PrivateKey: "", - }, - result: expectedResult{ - nil, - fmt.Errorf("client key must be provided"), - }, - }, - { - name: "tls mode MUTUAL, with node metadata sdsEnabled true no root CA specified", - opts: &buildClusterOpts{ - mutable: newTestCluster(), - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - ClientCertificate: clientCert, - PrivateKey: clientKey, - SubjectAltNames: []string{"SAN"}, - Sni: "some-sni.com", - }, - result: expectedResult{ - tlsContext: &tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - // if not specified, envoy use TLSv1_2 as default for client. - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - }, - TlsCertificateSdsSecretConfigs: []*tls.SdsSecretConfig{ - { - Name: fmt.Sprintf("file-cert:%s~%s", clientCert, clientKey), - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"}, - }, - }, - }, - }, - }, - ResourceApiVersion: core.ApiVersion_V3, - }, - }, - }, - ValidationContextType: &tls.CommonTlsContext_ValidationContext{}, - }, - Sni: "some-sni.com", - }, - err: nil, - }, - }, - { - name: "tls mode MUTUAL, with node metadata sdsEnabled true", - opts: &buildClusterOpts{ - mutable: newTestCluster(), - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - ClientCertificate: clientCert, - PrivateKey: clientKey, - CaCertificates: rootCert, - SubjectAltNames: []string{"SAN"}, - Sni: "some-sni.com", - }, - result: expectedResult{ - tlsContext: &tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - // if not specified, envoy use TLSv1_2 as default for client. - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - }, - TlsCertificateSdsSecretConfigs: []*tls.SdsSecretConfig{ - { - Name: fmt.Sprintf("file-cert:%s~%s", clientCert, clientKey), - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"}, - }, - }, - }, - }, - }, - ResourceApiVersion: core.ApiVersion_V3, - }, - }, - }, - ValidationContextType: &tls.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &tls.CertificateValidationContext{MatchSubjectAltNames: util.StringToExactMatch([]string{"SAN"})}, - ValidationContextSdsSecretConfig: &tls.SdsSecretConfig{ - Name: fmt.Sprintf("file-root:%s", rootCert), - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"}, - }, - }, - }, - }, - }, - ResourceApiVersion: core.ApiVersion_V3, - }, - }, - }, - }, - }, - Sni: "some-sni.com", - }, - err: nil, - }, - }, - { - name: "tls mode SIMPLE, with CredentialName specified", - opts: &buildClusterOpts{ - mutable: newTestCluster(), - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - CredentialName: credentialName, - SubjectAltNames: []string{"SAN"}, - Sni: "some-sni.com", - }, - router: true, - result: expectedResult{ - tlsContext: &tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - // if not specified, envoy use TLSv1_2 as default for client. - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - }, - ValidationContextType: &tls.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &tls.CertificateValidationContext{ - MatchSubjectAltNames: util.StringToExactMatch([]string{"SAN"}), - }, - ValidationContextSdsSecretConfig: &tls.SdsSecretConfig{ - Name: "kubernetes://" + credentialName + authn_model.SdsCaSuffix, - SdsConfig: authn_model.SDSAdsConfig, - }, - }, - }, - }, - Sni: "some-sni.com", - }, - err: nil, - }, - }, - { - name: "tls mode SIMPLE, with CredentialName specified with h2 and no SAN", - opts: &buildClusterOpts{ - mutable: newH2TestCluster(), - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - CredentialName: credentialName, - Sni: "some-sni.com", - }, - h2: true, - router: true, - result: expectedResult{ - tlsContext: &tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - // if not specified, envoy use TLSv1_2 as default for client. - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - }, - ValidationContextType: &tls.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &tls.CertificateValidationContext{}, - ValidationContextSdsSecretConfig: &tls.SdsSecretConfig{ - Name: "kubernetes://" + credentialName + authn_model.SdsCaSuffix, - SdsConfig: authn_model.SDSAdsConfig, - }, - }, - }, - AlpnProtocols: util.ALPNH2Only, - }, - Sni: "some-sni.com", - }, - err: nil, - }, - }, - { - name: "tls mode MUTUAL, with CredentialName specified", - opts: &buildClusterOpts{ - mutable: newTestCluster(), - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - CredentialName: credentialName, - SubjectAltNames: []string{"SAN"}, - Sni: "some-sni.com", - }, - router: true, - result: expectedResult{ - tlsContext: &tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - // if not specified, envoy use TLSv1_2 as default for client. - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - }, - TlsCertificateSdsSecretConfigs: []*tls.SdsSecretConfig{ - { - Name: "kubernetes://" + credentialName, - SdsConfig: authn_model.SDSAdsConfig, - }, - }, - ValidationContextType: &tls.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &tls.CertificateValidationContext{ - MatchSubjectAltNames: util.StringToExactMatch([]string{"SAN"}), - }, - ValidationContextSdsSecretConfig: &tls.SdsSecretConfig{ - Name: "kubernetes://" + credentialName + authn_model.SdsCaSuffix, - SdsConfig: authn_model.SDSAdsConfig, - }, - }, - }, - }, - Sni: "some-sni.com", - }, - err: nil, - }, - }, - { - name: "tls mode MUTUAL, with CredentialName specified with h2 and no SAN", - opts: &buildClusterOpts{ - mutable: newH2TestCluster(), - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - CredentialName: credentialName, - Sni: "some-sni.com", - }, - h2: true, - router: true, - result: expectedResult{ - tlsContext: &tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - // if not specified, envoy use TLSv1_2 as default for client. - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - }, - TlsCertificateSdsSecretConfigs: []*tls.SdsSecretConfig{ - { - Name: "kubernetes://" + credentialName, - SdsConfig: authn_model.SDSAdsConfig, - }, - }, - ValidationContextType: &tls.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &tls.CertificateValidationContext{}, - ValidationContextSdsSecretConfig: &tls.SdsSecretConfig{ - Name: "kubernetes://" + credentialName + authn_model.SdsCaSuffix, - SdsConfig: authn_model.SDSAdsConfig, - }, - }, - }, - AlpnProtocols: util.ALPNH2Only, - }, - Sni: "some-sni.com", - }, - err: nil, - }, - }, - { - name: "tls mode MUTUAL, credentialName is set with proxy type Sidecar", - opts: &buildClusterOpts{ - mutable: newTestCluster(), - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - CredentialName: "fake-cred", - }, - result: expectedResult{ - nil, - nil, - }, - }, - { - name: "tls mode SIMPLE, credentialName is set with proxy type Sidecar", - opts: &buildClusterOpts{ - mutable: newTestCluster(), - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - CredentialName: "fake-cred", - }, - result: expectedResult{ - nil, - nil, - }, - }, - { - name: "tls mode SIMPLE, CredentialName is set with proxy type Sidecar and destinationRule has workload Selector", - opts: &buildClusterOpts{ - mutable: newTestCluster(), - isDrWithSelector: true, - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - CredentialName: credentialName, - SubjectAltNames: []string{"SAN"}, - Sni: "some-sni.com", - }, - result: expectedResult{ - tlsContext: &tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - // if not specified, envoy use TLSv1_2 as default for client. - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - }, - ValidationContextType: &tls.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &tls.CertificateValidationContext{ - MatchSubjectAltNames: util.StringToExactMatch([]string{"SAN"}), - }, - ValidationContextSdsSecretConfig: &tls.SdsSecretConfig{ - Name: "kubernetes://" + credentialName + authn_model.SdsCaSuffix, - SdsConfig: authn_model.SDSAdsConfig, - }, - }, - }, - }, - Sni: "some-sni.com", - }, - err: nil, - }, - }, - { - name: "tls mode MUTUAL, CredentialName is set with proxy type Sidecar and destinationRule has workload Selector", - opts: &buildClusterOpts{ - mutable: newTestCluster(), - isDrWithSelector: true, - }, - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - CredentialName: credentialName, - SubjectAltNames: []string{"SAN"}, - Sni: "some-sni.com", - }, - result: expectedResult{ - tlsContext: &tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - // if not specified, envoy use TLSv1_2 as default for client. - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - }, - TlsCertificateSdsSecretConfigs: []*tls.SdsSecretConfig{ - { - Name: "kubernetes://" + credentialName, - SdsConfig: authn_model.SDSAdsConfig, - }, - }, - ValidationContextType: &tls.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &tls.CertificateValidationContext{ - MatchSubjectAltNames: util.StringToExactMatch([]string{"SAN"}), - }, - ValidationContextSdsSecretConfig: &tls.SdsSecretConfig{ - Name: "kubernetes://" + credentialName + authn_model.SdsCaSuffix, - SdsConfig: authn_model.SDSAdsConfig, - }, - }, - }, - }, - Sni: "some-sni.com", - }, - err: nil, - }, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - test.SetBoolForTest(t, &features.EnableAutoSni, tc.enableAutoSni) - test.SetBoolForTest(t, &features.VerifyCertAtClient, tc.enableVerifyCertAtClient) - var proxy *model.Proxy - if tc.router { - proxy = newGatewayProxy() - } else { - proxy = newSidecarProxy() - } - cb := NewClusterBuilder(proxy, nil, model.DisabledCache{}) - if tc.h2 { - cb.setH2Options(tc.opts.mutable) - } - ret, err := cb.buildUpstreamClusterTLSContext(tc.opts, tc.tls) - if err != nil && tc.result.err == nil || err == nil && tc.result.err != nil { - t.Errorf("expecting:\n err=%v but got err=%v", tc.result.err, err) - } else if diff := cmp.Diff(tc.result.tlsContext, ret, protocmp.Transform()); diff != "" { - t.Errorf("got diff: `%v", diff) - } - if tc.enableAutoSni { - if len(tc.tls.Sni) == 0 { - assert.Equal(t, tc.opts.mutable.httpProtocolOptions.UpstreamHttpProtocolOptions.AutoSni, true) - } - if tc.enableVerifyCertAtClient && len(tc.tls.SubjectAltNames) == 0 { - assert.Equal(t, tc.opts.mutable.httpProtocolOptions.UpstreamHttpProtocolOptions.AutoSanValidation, true) - } - } - }) - } -} - -func newTestCluster() *MutableCluster { - return NewMutableCluster(&cluster.Cluster{ - Name: "test-cluster", - }) -} - -func newH2TestCluster() *MutableCluster { - cb := NewClusterBuilder(newSidecarProxy(), nil, model.DisabledCache{}) - mc := NewMutableCluster(&cluster.Cluster{ - Name: "test-cluster", - }) - cb.setH2Options(mc) - return mc -} - -func newDownstreamTestCluster() *MutableCluster { - cb := NewClusterBuilder(newSidecarProxy(), nil, model.DisabledCache{}) - mc := NewMutableCluster(&cluster.Cluster{ - Name: "test-cluster", - }) - cb.setUseDownstreamProtocol(mc) - return mc -} - -func newSidecarProxy() *model.Proxy { - return &model.Proxy{Type: model.SidecarProxy, Metadata: &model.NodeMetadata{}} -} - -func newGatewayProxy() *model.Proxy { - return &model.Proxy{Type: model.Router, Metadata: &model.NodeMetadata{}} -} - -// Helper function to extract TLS context from a cluster -func getTLSContext(t *testing.T, c *cluster.Cluster) *tls.UpstreamTlsContext { - t.Helper() - if c.TransportSocket == nil { - return nil - } - tlsContext := &tls.UpstreamTlsContext{} - err := c.TransportSocket.GetTypedConfig().UnmarshalTo(tlsContext) - if err != nil { - t.Fatalf("Failed to unmarshall tls context: %v", err) - } - return tlsContext -} - -func TestShouldH2Upgrade(t *testing.T) { - tests := []struct { - name string - clusterName string - direction model.TrafficDirection - port *model.Port - mesh *meshconfig.MeshConfig - connectionPool *networking.ConnectionPoolSettings - - upgrade bool - }{ - { - name: "mesh upgrade - dr default", - clusterName: "bar", - direction: model.TrafficDirectionOutbound, - port: &model.Port{Protocol: protocol.HTTP}, - mesh: &meshconfig.MeshConfig{H2UpgradePolicy: meshconfig.MeshConfig_UPGRADE}, - connectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - H2UpgradePolicy: networking.ConnectionPoolSettings_HTTPSettings_DEFAULT, - }, - }, - upgrade: true, - }, - { - name: "mesh default - dr upgrade non http port", - clusterName: "bar", - direction: model.TrafficDirectionOutbound, - port: &model.Port{Protocol: protocol.Unsupported}, - mesh: &meshconfig.MeshConfig{}, - connectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - H2UpgradePolicy: networking.ConnectionPoolSettings_HTTPSettings_UPGRADE, - }, - }, - upgrade: true, - }, - { - name: "mesh no_upgrade - dr default", - clusterName: "bar", - direction: model.TrafficDirectionOutbound, - port: &model.Port{Protocol: protocol.HTTP}, - mesh: &meshconfig.MeshConfig{H2UpgradePolicy: meshconfig.MeshConfig_DO_NOT_UPGRADE}, - connectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - H2UpgradePolicy: networking.ConnectionPoolSettings_HTTPSettings_DEFAULT, - }, - }, - upgrade: false, - }, - { - name: "mesh no_upgrade - dr upgrade", - clusterName: "bar", - direction: model.TrafficDirectionOutbound, - port: &model.Port{Protocol: protocol.HTTP}, - mesh: &meshconfig.MeshConfig{H2UpgradePolicy: meshconfig.MeshConfig_DO_NOT_UPGRADE}, - connectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - H2UpgradePolicy: networking.ConnectionPoolSettings_HTTPSettings_UPGRADE, - }, - }, - upgrade: true, - }, - { - name: "mesh upgrade - dr no_upgrade", - clusterName: "bar", - direction: model.TrafficDirectionOutbound, - port: &model.Port{Protocol: protocol.HTTP}, - mesh: &meshconfig.MeshConfig{H2UpgradePolicy: meshconfig.MeshConfig_UPGRADE}, - connectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - H2UpgradePolicy: networking.ConnectionPoolSettings_HTTPSettings_DO_NOT_UPGRADE, - }, - }, - upgrade: false, - }, - { - name: "inbound ignore", - clusterName: "bar", - direction: model.TrafficDirectionInbound, - port: &model.Port{Protocol: protocol.HTTP}, - mesh: &meshconfig.MeshConfig{H2UpgradePolicy: meshconfig.MeshConfig_UPGRADE}, - connectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - H2UpgradePolicy: networking.ConnectionPoolSettings_HTTPSettings_DEFAULT, - }, - }, - upgrade: false, - }, - { - name: "non-http", - clusterName: "bar", - direction: model.TrafficDirectionOutbound, - port: &model.Port{Protocol: protocol.Unsupported}, - mesh: &meshconfig.MeshConfig{H2UpgradePolicy: meshconfig.MeshConfig_UPGRADE}, - connectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - H2UpgradePolicy: networking.ConnectionPoolSettings_HTTPSettings_DEFAULT, - }, - }, - upgrade: false, - }, - } - - cb := NewClusterBuilder(newSidecarProxy(), nil, model.DisabledCache{}) - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - upgrade := cb.shouldH2Upgrade(test.clusterName, test.direction, test.port, test.mesh, test.connectionPool) - - if upgrade != test.upgrade { - t.Fatalf("got: %t, want: %t (%v, %v)", upgrade, test.upgrade, test.mesh.H2UpgradePolicy, test.connectionPool.Http.H2UpgradePolicy) - } - }) - } -} - -// nolint -func TestIsHttp2Cluster(t *testing.T) { - tests := []struct { - name string - cluster *MutableCluster - isHttp2Cluster bool - }{ - { - name: "with no h2 options", - cluster: newTestCluster(), - isHttp2Cluster: false, - }, - { - name: "with h2 options", - cluster: newH2TestCluster(), - isHttp2Cluster: true, - }, - { - name: "with downstream config and h2 options", - cluster: newDownstreamTestCluster(), - isHttp2Cluster: false, - }, - } - - cb := NewClusterBuilder(newSidecarProxy(), nil, model.DisabledCache{}) - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - isHttp2Cluster := cb.IsHttp2Cluster(test.cluster) - if isHttp2Cluster != test.isHttp2Cluster { - t.Errorf("got: %t, want: %t", isHttp2Cluster, test.isHttp2Cluster) - } - }) - } -} - -func TestBuildAutoMtlsSettings(t *testing.T) { - tlsSettings := &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_ISTIO_MUTUAL, - SubjectAltNames: []string{"custom.foo.com"}, - Sni: "custom.foo.com", - } - tests := []struct { - name string - tls *networking.ClientTLSSettings - sans []string - sni string - proxy *model.Proxy - autoMTLSEnabled bool - meshExternal bool - serviceMTLSMode model.MutualTLSMode - want *networking.ClientTLSSettings - wantCtxType mtlsContextType - }{ - { - "Destination rule TLS sni and SAN override", - tlsSettings, - []string{"spiffe://foo/serviceaccount/1"}, - "foo.com", - &model.Proxy{Metadata: &model.NodeMetadata{}}, - false, false, model.MTLSUnknown, - tlsSettings, - userSupplied, - }, - { - "Metadata cert path override ISTIO_MUTUAL", - tlsSettings, - []string{"custom.foo.com"}, - "custom.foo.com", - &model.Proxy{Metadata: &model.NodeMetadata{ - TLSClientCertChain: "/custom/chain.pem", - TLSClientKey: "/custom/key.pem", - TLSClientRootCert: "/custom/root.pem", - }}, - false, false, model.MTLSUnknown, - &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - PrivateKey: "/custom/key.pem", - ClientCertificate: "/custom/chain.pem", - CaCertificates: "/custom/root.pem", - SubjectAltNames: []string{"custom.foo.com"}, - Sni: "custom.foo.com", - }, - userSupplied, - }, - { - "Auto fill nil settings when mTLS nil for internal service in strict mode", - nil, - []string{"spiffe://foo/serviceaccount/1"}, - "foo.com", - &model.Proxy{Metadata: &model.NodeMetadata{}}, - true, false, model.MTLSStrict, - &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_ISTIO_MUTUAL, - SubjectAltNames: []string{"spiffe://foo/serviceaccount/1"}, - Sni: "foo.com", - }, - autoDetected, - }, - { - "Auto fill nil settings when mTLS nil for internal service in permissive mode", - nil, - []string{"spiffe://foo/serviceaccount/1"}, - "foo.com", - &model.Proxy{Metadata: &model.NodeMetadata{}}, - true, false, model.MTLSPermissive, - &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_ISTIO_MUTUAL, - SubjectAltNames: []string{"spiffe://foo/serviceaccount/1"}, - Sni: "foo.com", - }, - autoDetected, - }, - { - "Auto fill nil settings when mTLS nil for internal service in plaintext mode", - nil, - []string{"spiffe://foo/serviceaccount/1"}, - "foo.com", - &model.Proxy{Metadata: &model.NodeMetadata{}}, - true, false, model.MTLSDisable, - nil, - userSupplied, - }, - { - "Auto fill nil settings when mTLS nil for internal service in unknown mode", - nil, - []string{"spiffe://foo/serviceaccount/1"}, - "foo.com", - &model.Proxy{Metadata: &model.NodeMetadata{}}, - true, false, model.MTLSUnknown, - nil, - userSupplied, - }, - { - "Do not auto fill nil settings for external", - nil, - []string{"spiffe://foo/serviceaccount/1"}, - "foo.com", - &model.Proxy{Metadata: &model.NodeMetadata{}}, - true, true, model.MTLSUnknown, - nil, - userSupplied, - }, - { - "Do not auto fill nil settings if server mTLS is disabled", - nil, - []string{"spiffe://foo/serviceaccount/1"}, - "foo.com", - &model.Proxy{Metadata: &model.NodeMetadata{}}, - false, false, model.MTLSDisable, - nil, - userSupplied, - }, - { - "TLS nil auto build tls with metadata cert path", - nil, - []string{"spiffe://foo/serviceaccount/1"}, - "foo.com", - &model.Proxy{Metadata: &model.NodeMetadata{ - TLSClientCertChain: "/custom/chain.pem", - TLSClientKey: "/custom/key.pem", - TLSClientRootCert: "/custom/root.pem", - }}, - true, false, model.MTLSPermissive, - &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - ClientCertificate: "/custom/chain.pem", - PrivateKey: "/custom/key.pem", - CaCertificates: "/custom/root.pem", - SubjectAltNames: []string{"spiffe://foo/serviceaccount/1"}, - Sni: "foo.com", - }, - autoDetected, - }, - { - "Simple TLS", - &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - PrivateKey: "/custom/key.pem", - ClientCertificate: "/custom/chain.pem", - CaCertificates: "/custom/root.pem", - }, - []string{"custom.foo.com"}, - "custom.foo.com", - &model.Proxy{Metadata: &model.NodeMetadata{ - TLSClientCertChain: "/custom/meta/chain.pem", - TLSClientKey: "/custom/meta/key.pem", - TLSClientRootCert: "/custom/meta/root.pem", - }}, - false, false, model.MTLSUnknown, - &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - PrivateKey: "/custom/key.pem", - ClientCertificate: "/custom/chain.pem", - CaCertificates: "/custom/root.pem", - }, - userSupplied, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - cb := NewClusterBuilder(tt.proxy, nil, nil) - gotTLS, gotCtxType := cb.buildAutoMtlsSettings(tt.tls, tt.sans, tt.sni, - tt.autoMTLSEnabled, tt.meshExternal, tt.serviceMTLSMode) - if !reflect.DeepEqual(gotTLS, tt.want) { - t.Errorf("cluster TLS does not match expected result want %#v, got %#v", tt.want, gotTLS) - } - if gotCtxType != tt.wantCtxType { - t.Errorf("cluster TLS context type does not match expected result want %#v, got %#v", tt.wantCtxType, gotCtxType) - } - }) - } -} - -func TestApplyDestinationRuleOSCACert(t *testing.T) { - servicePort := model.PortList{ - &model.Port{ - Name: "default", - Port: 8080, - Protocol: protocol.HTTP, - }, - &model.Port{ - Name: "auto", - Port: 9090, - Protocol: protocol.Unsupported, - }, - } - service := &model.Service{ - Hostname: host.Name("foo.default.svc.cluster.local"), - Ports: servicePort, - Resolution: model.ClientSideLB, - Attributes: model.ServiceAttributes{ - Namespace: TestServiceNamespace, - }, - } - - cases := []struct { - name string - cluster *cluster.Cluster - clusterMode ClusterMode - service *model.Service - port *model.Port - proxyView model.ProxyView - destRule *networking.DestinationRule - expectedCaCertificateName string - enableVerifyCertAtClient bool - }{ - { - name: "VerifyCertAtClient set and destination rule with empty string CaCertificates", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}}, - clusterMode: DefaultClusterMode, - service: service, - port: servicePort[0], - proxyView: model.ProxyViewAll, - destRule: &networking.DestinationRule{ - Host: "foo.default.svc.cluster.local", - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - UseClientProtocol: true, - }, - }, - Tls: &networking.ClientTLSSettings{ - CaCertificates: "", - Mode: networking.ClientTLSSettings_SIMPLE, - }, - }, - }, - expectedCaCertificateName: "system", - enableVerifyCertAtClient: true, - }, - { - name: "VerifyCertAtClient set and destination rule with CaCertificates", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}}, - clusterMode: DefaultClusterMode, - service: service, - port: servicePort[0], - proxyView: model.ProxyViewAll, - destRule: &networking.DestinationRule{ - Host: "foo.default.svc.cluster.local", - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - UseClientProtocol: true, - }, - }, - Tls: &networking.ClientTLSSettings{ - CaCertificates: constants.DefaultRootCert, - Mode: networking.ClientTLSSettings_SIMPLE, - }, - }, - }, - expectedCaCertificateName: constants.DefaultRootCert, - enableVerifyCertAtClient: true, - }, - { - name: "VerifyCertAtClient set and destination rule without CaCertificates", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}}, - clusterMode: DefaultClusterMode, - service: service, - port: servicePort[0], - proxyView: model.ProxyViewAll, - destRule: &networking.DestinationRule{ - Host: "foo.default.svc.cluster.local", - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - UseClientProtocol: true, - }, - }, - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - }, - }, - }, - expectedCaCertificateName: "system", - enableVerifyCertAtClient: true, - }, - { - name: "VerifyCertAtClient false and destination rule without CaCertificates", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}}, - clusterMode: DefaultClusterMode, - service: service, - port: servicePort[0], - proxyView: model.ProxyViewAll, - destRule: &networking.DestinationRule{ - Host: "foo.default.svc.cluster.local", - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - UseClientProtocol: true, - }, - }, - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - }, - }, - }, - expectedCaCertificateName: "", - enableVerifyCertAtClient: false, - }, - { - name: "VerifyCertAtClient false and destination rule with CaCertificates", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}}, - clusterMode: DefaultClusterMode, - service: service, - port: servicePort[0], - proxyView: model.ProxyViewAll, - destRule: &networking.DestinationRule{ - Host: "foo.default.svc.cluster.local", - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - UseClientProtocol: true, - }, - }, - Tls: &networking.ClientTLSSettings{ - CaCertificates: constants.DefaultRootCert, - Mode: networking.ClientTLSSettings_SIMPLE, - }, - }, - }, - expectedCaCertificateName: constants.DefaultRootCert, - enableVerifyCertAtClient: false, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - test.SetBoolForTest(t, &features.VerifyCertAtClient, tt.enableVerifyCertAtClient) - instances := []*model.ServiceInstance{ - { - Service: tt.service, - ServicePort: tt.port, - Endpoint: &model.IstioEndpoint{ - Address: "192.168.1.1", - EndpointPort: 10001, - Locality: model.Locality{ - ClusterID: "", - Label: "region1/zone1/subzone1", - }, - TLSMode: model.IstioMutualTLSModeLabel, - }, - }, - } - - var cfg *config.Config - if tt.destRule != nil { - cfg = &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: "acme", - Namespace: "default", - }, - Spec: tt.destRule, - } - } - cg := NewConfigGenTest(t, TestOptions{ - ConfigPointers: []*config.Config{cfg}, - Services: []*model.Service{tt.service}, - }) - cg.MemRegistry.WantGetProxyServiceInstances = instances - proxy := cg.SetupProxy(nil) - cb := NewClusterBuilder(proxy, &model.PushRequest{Push: cg.PushContext()}, nil) - - ec := NewMutableCluster(tt.cluster) - destRule := proxy.SidecarScope.DestinationRule(model.TrafficDirectionOutbound, proxy, tt.service.Hostname) - - // ACT - _ = cb.applyDestinationRule(ec, tt.clusterMode, tt.service, tt.port, tt.proxyView, destRule, nil) - - byteArray, err := config.ToJSON(destRule.Spec) - if err != nil { - t.Errorf("Could not parse destination rule: %v", err) - } - dr := &networking.DestinationRule{} - err = json.Unmarshal(byteArray, dr) - if err != nil { - t.Errorf("Could not unmarshal destination rule: %v", err) - } - ca := dr.TrafficPolicy.Tls.CaCertificates - if ca != tt.expectedCaCertificateName { - t.Errorf("%v: got unexpected caCertitifcates field. Expected (%v), received (%v)", tt.name, tt.expectedCaCertificateName, ca) - } - }) - } -} - -func TestApplyTCPKeepalive(t *testing.T) { - cases := []struct { - name string - mesh *meshconfig.MeshConfig - connectionPool *networking.ConnectionPoolSettings - wantConnOpts *cluster.UpstreamConnectionOptions - }{ - { - name: "no tcp alive", - mesh: &meshconfig.MeshConfig{}, - connectionPool: &networking.ConnectionPoolSettings{}, - wantConnOpts: nil, - }, - { - name: "destination rule tcp alive", - mesh: &meshconfig.MeshConfig{}, - connectionPool: &networking.ConnectionPoolSettings{ - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - TcpKeepalive: &networking.ConnectionPoolSettings_TCPSettings_TcpKeepalive{ - Time: &durationpb.Duration{Seconds: 10}, - }, - }, - }, - wantConnOpts: &cluster.UpstreamConnectionOptions{ - TcpKeepalive: &core.TcpKeepalive{ - KeepaliveTime: &wrappers.UInt32Value{Value: uint32(10)}, - }, - }, - }, - { - name: "mesh tcp alive", - mesh: &meshconfig.MeshConfig{ - TcpKeepalive: &networking.ConnectionPoolSettings_TCPSettings_TcpKeepalive{ - Time: &durationpb.Duration{Seconds: 10}, - }, - }, - connectionPool: &networking.ConnectionPoolSettings{}, - wantConnOpts: &cluster.UpstreamConnectionOptions{ - TcpKeepalive: &core.TcpKeepalive{ - KeepaliveTime: &wrappers.UInt32Value{Value: uint32(10)}, - }, - }, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - cg := NewConfigGenTest(t, TestOptions{}) - proxy := cg.SetupProxy(nil) - cb := NewClusterBuilder(proxy, &model.PushRequest{Push: cg.PushContext()}, nil) - mc := &MutableCluster{ - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}}, - } - - cb.applyConnectionPool(tt.mesh, mc, tt.connectionPool) - - if !reflect.DeepEqual(tt.wantConnOpts, mc.cluster.UpstreamConnectionOptions) { - t.Errorf("unexpected tcp keepalive settings, want %v, got %v", tt.wantConnOpts, - mc.cluster.UpstreamConnectionOptions) - } - }) - } -} - -func TestApplyConnectionPool(t *testing.T) { - // only test connectionPool.Http.IdleTimeout and connectionPool.Http.IdleTimeout.MaxRequestsPerConnection - cases := []struct { - name string - cluster *cluster.Cluster - httpProtocolOptions *http.HttpProtocolOptions - connectionPool *networking.ConnectionPoolSettings - expectedHTTPPOpt *http.HttpProtocolOptions - }{ - { - name: "only update IdleTimeout", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}}, - httpProtocolOptions: &http.HttpProtocolOptions{ - CommonHttpProtocolOptions: &core.HttpProtocolOptions{ - IdleTimeout: &durationpb.Duration{ - Seconds: 10, - }, - MaxRequestsPerConnection: &wrappers.UInt32Value{Value: 10}, - }, - }, - connectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - IdleTimeout: &durationpb.Duration{ - Seconds: 22, - }, - }, - }, - expectedHTTPPOpt: &http.HttpProtocolOptions{ - CommonHttpProtocolOptions: &core.HttpProtocolOptions{ - IdleTimeout: &durationpb.Duration{ - Seconds: 22, - }, - MaxRequestsPerConnection: &wrappers.UInt32Value{Value: 10}, - }, - }, - }, - { - name: "only update MaxRequestsPerConnection ", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}}, - httpProtocolOptions: &http.HttpProtocolOptions{ - CommonHttpProtocolOptions: &core.HttpProtocolOptions{ - IdleTimeout: &durationpb.Duration{ - Seconds: 10, - }, - MaxRequestsPerConnection: &wrappers.UInt32Value{Value: 10}, - }, - }, - connectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRequestsPerConnection: 22, - }, - }, - expectedHTTPPOpt: &http.HttpProtocolOptions{ - CommonHttpProtocolOptions: &core.HttpProtocolOptions{ - IdleTimeout: &durationpb.Duration{ - Seconds: 10, - }, - MaxRequestsPerConnection: &wrappers.UInt32Value{Value: 22}, - }, - }, - }, - { - name: "update MaxRequestsPerConnection and IdleTimeout", - cluster: &cluster.Cluster{Name: "foo", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}}, - httpProtocolOptions: &http.HttpProtocolOptions{ - CommonHttpProtocolOptions: &core.HttpProtocolOptions{ - IdleTimeout: &durationpb.Duration{ - Seconds: 10, - }, - MaxRequestsPerConnection: &wrappers.UInt32Value{Value: 10}, - }, - }, - connectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - IdleTimeout: &durationpb.Duration{ - Seconds: 22, - }, - MaxRequestsPerConnection: 22, - }, - }, - expectedHTTPPOpt: &http.HttpProtocolOptions{ - CommonHttpProtocolOptions: &core.HttpProtocolOptions{ - IdleTimeout: &durationpb.Duration{ - Seconds: 22, - }, - MaxRequestsPerConnection: &wrappers.UInt32Value{Value: 22}, - }, - }, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - cg := NewConfigGenTest(t, TestOptions{}) - proxy := cg.SetupProxy(nil) - cb := NewClusterBuilder(proxy, &model.PushRequest{Push: cg.PushContext()}, nil) - mc := &MutableCluster{ - cluster: tt.cluster, - httpProtocolOptions: tt.httpProtocolOptions, - } - - opts := buildClusterOpts{ - mesh: cb.req.Push.Mesh, - mutable: mc, - } - cb.applyConnectionPool(opts.mesh, opts.mutable, tt.connectionPool) - // assert httpProtocolOptions - assert.Equal(t, opts.mutable.httpProtocolOptions.CommonHttpProtocolOptions.IdleTimeout, - tt.expectedHTTPPOpt.CommonHttpProtocolOptions.IdleTimeout) - assert.Equal(t, opts.mutable.httpProtocolOptions.CommonHttpProtocolOptions.MaxRequestsPerConnection, - tt.expectedHTTPPOpt.CommonHttpProtocolOptions.MaxRequestsPerConnection) - }) - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/cluster_test.go b/pilot/pkg/networking/core/v1alpha3/cluster_test.go deleted file mode 100644 index 316b25a3a..000000000 --- a/pilot/pkg/networking/core/v1alpha3/cluster_test.go +++ /dev/null @@ -1,2576 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "fmt" - "math" - "reflect" - "sort" - "strings" - "testing" - "time" -) - -import ( - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - http "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3" - "github.com/google/go-cmp/cmp" - . "github.com/onsi/gomega" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/testing/protocmp" - "google.golang.org/protobuf/types/known/durationpb" - "google.golang.org/protobuf/types/known/structpb" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" - authn_beta "istio.io/api/security/v1beta1" - selectorpb "istio.io/api/type/v1beta1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -type ConfigType int - -const ( - None ConfigType = iota - Mesh - DestinationRule - DestinationRuleForOsDefault - MeshWideTCPKeepaliveSeconds = 11 - DestinationRuleTCPKeepaliveSeconds = 21 - TestServiceNamespace = "bar" - // FQDN service name in namespace TestServiceNamespace. Note the mesh config domain is empty. - TestServiceNHostname = "foo.bar" -) - -func testMesh() *meshconfig.MeshConfig { - return &meshconfig.MeshConfig{ - ConnectTimeout: &durationpb.Duration{ - Seconds: 10, - Nanos: 1, - }, - EnableAutoMtls: &wrappers.BoolValue{ - Value: false, - }, - } -} - -func TestHTTPCircuitBreakerThresholds(t *testing.T) { - checkClusters := []string{"outbound|8080||*.example.org", "inbound|10001||"} - settings := []*networking.ConnectionPoolSettings{ - nil, - { - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - Http1MaxPendingRequests: 1, - Http2MaxRequests: 2, - MaxRequestsPerConnection: 3, - MaxRetries: 4, - }, - }, - } - - for _, s := range settings { - testName := "default" - if s != nil { - testName = "override" - } - t.Run(testName, func(t *testing.T) { - g := NewWithT(t) - clusters := xdstest.ExtractClusters(buildTestClusters(clusterTest{ - t: t, - serviceHostname: "*.example.org", - nodeType: model.SidecarProxy, - mesh: testMesh(), - destRule: &networking.DestinationRule{ - Host: "*.example.org", - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: s, - }, - }, - })) - - for _, c := range checkClusters { - cluster := clusters[c] - if cluster == nil { - t.Fatalf("cluster %v not found", c) - } - g.Expect(len(cluster.CircuitBreakers.Thresholds)).To(Equal(1)) - thresholds := cluster.CircuitBreakers.Thresholds[0] - - if s == nil { - // Assume the correct defaults for this direction. - g.Expect(thresholds).To(Equal(getDefaultCircuitBreakerThresholds())) - } else { - // Verify that the values were set correctly. - g.Expect(thresholds.MaxPendingRequests).To(Not(BeNil())) - g.Expect(thresholds.MaxPendingRequests.Value).To(Equal(uint32(s.Http.Http1MaxPendingRequests))) - g.Expect(thresholds.MaxRequests).To(Not(BeNil())) - g.Expect(thresholds.MaxRequests.Value).To(Equal(uint32(s.Http.Http2MaxRequests))) - g.Expect(cluster.TypedExtensionProtocolOptions).To(Not(BeNil())) - anyOptions := cluster.TypedExtensionProtocolOptions[v3.HttpProtocolOptionsType] - g.Expect(anyOptions).To(Not(BeNil())) - httpProtocolOptions := &http.HttpProtocolOptions{} - anyOptions.UnmarshalTo(httpProtocolOptions) - g.Expect(httpProtocolOptions.CommonHttpProtocolOptions.MaxRequestsPerConnection.GetValue()).To(Equal(uint32(s.Http.MaxRequestsPerConnection))) - g.Expect(thresholds.MaxRetries).To(Not(BeNil())) - g.Expect(thresholds.MaxRetries.Value).To(Equal(uint32(s.Http.MaxRetries))) - } - } - }) - } -} - -func TestCommonHttpProtocolOptions(t *testing.T) { - cases := []struct { - clusterName string - useDownStreamProtocol bool - sniffingEnabledForInbound bool - proxyType model.NodeType - clusters int - }{ - { - clusterName: "outbound|8080||*.example.org", - useDownStreamProtocol: false, - sniffingEnabledForInbound: false, - proxyType: model.SidecarProxy, - clusters: 8, - }, - { - clusterName: "inbound|10001||", - useDownStreamProtocol: false, - sniffingEnabledForInbound: false, - proxyType: model.SidecarProxy, - clusters: 8, - }, - { - clusterName: "outbound|9090||*.example.org", - useDownStreamProtocol: true, - sniffingEnabledForInbound: false, - proxyType: model.SidecarProxy, - clusters: 8, - }, - { - clusterName: "inbound|10002||", - useDownStreamProtocol: true, - sniffingEnabledForInbound: true, - proxyType: model.SidecarProxy, - clusters: 8, - }, - { - clusterName: "outbound|8080||*.example.org", - useDownStreamProtocol: true, - sniffingEnabledForInbound: true, - proxyType: model.Router, - clusters: 3, - }, - } - settings := &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - Http1MaxPendingRequests: 1, - IdleTimeout: &durationpb.Duration{Seconds: 15}, - }, - } - - for _, tc := range cases { - test.SetBoolForTest(t, &features.EnableProtocolSniffingForInbound, tc.sniffingEnabledForInbound) - test.SetBoolForTest(t, &features.FilterGatewayClusterConfig, false) - - settingsName := "default" - if settings != nil { - settingsName = "override" - } - testName := fmt.Sprintf("%s-%s-%s", tc.clusterName, settingsName, tc.proxyType) - t.Run(testName, func(t *testing.T) { - g := NewWithT(t) - clusters := xdstest.ExtractClusters(buildTestClusters(clusterTest{ - t: t, serviceHostname: "*.example.org", nodeType: tc.proxyType, mesh: testMesh(), - destRule: &networking.DestinationRule{ - Host: "*.example.org", - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: settings, - }, - }, - })) - g.Expect(len(clusters)).To(Equal(tc.clusters)) - c := clusters[tc.clusterName] - - anyOptions := c.TypedExtensionProtocolOptions[v3.HttpProtocolOptionsType] - httpProtocolOptions := &http.HttpProtocolOptions{} - if anyOptions != nil { - anyOptions.UnmarshalTo(httpProtocolOptions) - } - - if tc.useDownStreamProtocol && tc.proxyType == model.SidecarProxy { - if httpProtocolOptions.GetUseDownstreamProtocolConfig() == nil { - t.Errorf("Expected cluster to use downstream protocol but got %v", httpProtocolOptions) - } - } else { - if httpProtocolOptions.GetUseDownstreamProtocolConfig() != nil { - t.Errorf("Expected cluster to not to use downstream protocol but got %v", httpProtocolOptions) - } - } - - // Verify that the values were set correctly. - g.Expect(httpProtocolOptions.CommonHttpProtocolOptions.IdleTimeout).To(Not(BeNil())) - g.Expect(httpProtocolOptions.CommonHttpProtocolOptions.IdleTimeout).To(Equal(durationpb.New(time.Duration(15000000000)))) - }) - } -} - -// clusterTest defines a structure containing all information needed to build a cluster for tests -type clusterTest struct { - // Required - t testing.TB - serviceHostname string - serviceResolution model.Resolution - nodeType model.NodeType - locality *core.Locality - mesh *meshconfig.MeshConfig - destRule proto.Message - peerAuthn *authn_beta.PeerAuthentication - externalService bool - - meta *model.NodeMetadata - istioVersion *model.IstioVersion - proxyIps []string -} - -func (c clusterTest) fillDefaults() clusterTest { - if c.proxyIps == nil { - c.proxyIps = []string{"6.6.6.6", "::1"} - } - if c.istioVersion == nil { - c.istioVersion = model.MaxIstioVersion - } - if c.meta == nil { - c.meta = &model.NodeMetadata{} - } - return c -} - -func buildTestClusters(c clusterTest) []*cluster.Cluster { - c = c.fillDefaults() - - servicePort := model.PortList{ - &model.Port{ - Name: "default", - Port: 8080, - Protocol: protocol.HTTP, - }, - &model.Port{ - Name: "auto", - Port: 9090, - Protocol: protocol.Unsupported, - }, - } - - service := &model.Service{ - Hostname: host.Name(c.serviceHostname), - Ports: servicePort, - Resolution: c.serviceResolution, - MeshExternal: c.externalService, - Attributes: model.ServiceAttributes{ - Namespace: TestServiceNamespace, - }, - } - - instances := []*model.ServiceInstance{ - { - Service: service, - ServicePort: servicePort[0], - Endpoint: &model.IstioEndpoint{ - Address: "6.6.6.6", - EndpointPort: 10001, - Locality: model.Locality{ - ClusterID: "", - Label: "region1/zone1/subzone1", - }, - LbWeight: 40, - TLSMode: model.IstioMutualTLSModeLabel, - }, - }, - { - Service: service, - ServicePort: servicePort[0], - Endpoint: &model.IstioEndpoint{ - Address: "6.6.6.6", - EndpointPort: 10001, - Locality: model.Locality{ - ClusterID: "", - Label: "region1/zone1/subzone2", - }, - LbWeight: 20, - TLSMode: model.IstioMutualTLSModeLabel, - }, - }, - { - Service: service, - ServicePort: servicePort[0], - Endpoint: &model.IstioEndpoint{ - Address: "6.6.6.6", - EndpointPort: 10001, - Locality: model.Locality{ - ClusterID: "", - Label: "region2/zone1/subzone1", - }, - LbWeight: 40, - TLSMode: model.IstioMutualTLSModeLabel, - }, - }, - { - Service: service, - ServicePort: servicePort[1], - Endpoint: &model.IstioEndpoint{ - Address: "6.6.6.6", - EndpointPort: 10002, - Locality: model.Locality{ - ClusterID: "", - Label: "region1/zone1/subzone1", - }, - LbWeight: 0, - TLSMode: model.IstioMutualTLSModeLabel, - }, - }, - } - - configs := []config.Config{} - if c.destRule != nil { - configs = append(configs, config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: "acme", - }, - Spec: c.destRule, - }) - } - if c.peerAuthn != nil { - policyName := "default" - if c.peerAuthn.Selector != nil { - policyName = "acme" - } - configs = append(configs, config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - Name: policyName, - Namespace: TestServiceNamespace, - }, - Spec: c.peerAuthn, - }) - } - cg := NewConfigGenTest(c.t, TestOptions{ - Services: []*model.Service{service}, - Instances: instances, - Configs: configs, - MeshConfig: c.mesh, - }) - - var proxy *model.Proxy - switch c.nodeType { - case model.SidecarProxy: - proxy = &model.Proxy{ - Type: model.SidecarProxy, - IPAddresses: c.proxyIps, - Locality: c.locality, - DNSDomain: "com", - Metadata: c.meta, - IstioVersion: c.istioVersion, - } - case model.Router: - proxy = &model.Proxy{ - Type: model.Router, - IPAddresses: []string{"6.6.6.6"}, - Locality: c.locality, - DNSDomain: "default.example.org", - Metadata: c.meta, - IstioVersion: c.istioVersion, - } - default: - panic(fmt.Sprintf("unsupported node type: %v", c.nodeType)) - } - clusters := cg.Clusters(cg.SetupProxy(proxy)) - xdstest.ValidateClusters(c.t, clusters) - if len(cg.PushContext().ProxyStatus[model.DuplicatedClusters.Name()]) > 0 { - c.t.Fatalf("duplicate clusters detected %#v", cg.PushContext().ProxyStatus[model.DuplicatedClusters.Name()]) - } - return clusters -} - -func TestBuildGatewayClustersWithRingHashLb(t *testing.T) { - cases := []struct { - name string - ringSize int - expectedRingSize int - }{ - { - "default", - 0, - 1024, - }, - { - "ring size", - 2, - 2, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - g := NewWithT(t) - - test.SetBoolForTest(t, &features.FilterGatewayClusterConfig, false) - - c := xdstest.ExtractCluster("outbound|8080||*.example.org", - buildTestClusters(clusterTest{ - t: t, serviceHostname: "*.example.org", nodeType: model.Router, mesh: testMesh(), - destRule: &networking.DestinationRule{ - Host: "*.example.org", - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{ - ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ - MinimumRingSize: uint64(tt.ringSize), - HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpCookie{ - HttpCookie: &networking.LoadBalancerSettings_ConsistentHashLB_HTTPCookie{ - Name: "hash-cookie", - Ttl: &durationpb.Duration{Nanos: 100}, - }, - }, - }, - }, - }, - }, - }, - })) - - g.Expect(c.LbPolicy).To(Equal(cluster.Cluster_RING_HASH)) - g.Expect(c.GetRingHashLbConfig().GetMinimumRingSize().GetValue()).To(Equal(uint64(tt.expectedRingSize))) - g.Expect(c.ConnectTimeout).To(Equal(durationpb.New(time.Duration(10000000001)))) - }) - } -} - -func withClusterLocalHosts(m *meshconfig.MeshConfig, hosts ...string) *meshconfig.MeshConfig { // nolint:interfacer - m.ServiceSettings = append(append(make([]*meshconfig.MeshConfig_ServiceSettings, 0), m.ServiceSettings...), - &meshconfig.MeshConfig_ServiceSettings{ - Settings: &meshconfig.MeshConfig_ServiceSettings_Settings{ - ClusterLocal: true, - }, - Hosts: hosts, - }) - return m -} - -func TestBuildSidecarClustersWithIstioMutualAndSNI(t *testing.T) { - cases := []struct { - sni string - expected string - }{ - {"foo.com", "foo.com"}, - {"", "outbound_.8080_.foobar_.foo.example.org"}, - } - for _, tt := range cases { - t.Run(tt.sni, func(t *testing.T) { - g := NewWithT(t) - - c := xdstest.ExtractCluster("outbound|8080|foobar|foo.example.org", buildSniTestClustersForSidecar(t, tt.sni)) - g.Expect(getTLSContext(t, c).GetSni()).To(Equal(tt.expected)) - }) - } -} - -func TestBuildClustersWithMutualTlsAndNodeMetadataCertfileOverrides(t *testing.T) { - expectedClientKeyPath := "/clientKeyFromNodeMetadata.pem" - expectedClientCertPath := "/clientCertFromNodeMetadata.pem" - expectedRootCertPath := "/clientRootCertFromNodeMetadata.pem" - - g := NewWithT(t) - - envoyMetadata := &model.NodeMetadata{ - TLSClientCertChain: expectedClientCertPath, - TLSClientKey: expectedClientKeyPath, - TLSClientRootCert: expectedRootCertPath, - } - - destRule := &networking.DestinationRule{ - Host: "*.example.org", - TrafficPolicy: &networking.TrafficPolicy{ - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - ClientCertificate: "/defaultCert.pem", - PrivateKey: "/defaultPrivateKey.pem", - CaCertificates: "/defaultCaCert.pem", - }, - }, - Subsets: []*networking.Subset{ - { - Name: "foobar", - Labels: map[string]string{"foo": "bar"}, - TrafficPolicy: &networking.TrafficPolicy{ - PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ - { - Port: &networking.PortSelector{ - Number: 8080, - }, - }, - }, - }, - }, - }, - } - - clusters := buildTestClusters(clusterTest{ - t: t, - serviceHostname: "foo.example.org", - serviceResolution: model.ClientSideLB, - nodeType: model.SidecarProxy, - mesh: testMesh(), - destRule: destRule, - meta: envoyMetadata, - istioVersion: model.MaxIstioVersion, - }) - - // per the docs: default values will be applied to fields omitted in port-level traffic policies rather than inheriting - // settings specified at the destination level - g.Expect(getTLSContext(t, xdstest.ExtractCluster("outbound|8080|foobar|foo.example.org", clusters))).To(BeNil()) - - expected := []string{ - "outbound|8080||foo.example.org", - "outbound|9090||foo.example.org", - "outbound|9090|foobar|foo.example.org", - } - for _, e := range expected { - c := xdstest.ExtractCluster(e, clusters) - tlsContext := getTLSContext(t, c) - g.Expect(tlsContext).NotTo(BeNil()) - - rootSdsConfig := tlsContext.CommonTlsContext.GetCombinedValidationContext().GetValidationContextSdsSecretConfig() - g.Expect(rootSdsConfig.GetName()).To(Equal("file-root:/clientRootCertFromNodeMetadata.pem")) - - certSdsConfig := tlsContext.CommonTlsContext.GetTlsCertificateSdsSecretConfigs() - g.Expect(certSdsConfig).To(HaveLen(1)) - g.Expect(certSdsConfig[0].GetName()).To(Equal("file-cert:/clientCertFromNodeMetadata.pem~/clientKeyFromNodeMetadata.pem")) - } -} - -func buildSniTestClustersForSidecar(t *testing.T, sniValue string) []*cluster.Cluster { - return buildSniTestClustersWithMetadata(t, sniValue, model.SidecarProxy, &model.NodeMetadata{}) -} - -func buildSniDnatTestClustersForGateway(t *testing.T, sniValue string) []*cluster.Cluster { - return buildSniTestClustersWithMetadata(t, sniValue, model.Router, &model.NodeMetadata{}) -} - -func buildSniTestClustersWithMetadata(t testing.TB, sniValue string, typ model.NodeType, meta *model.NodeMetadata) []*cluster.Cluster { - return buildTestClusters(clusterTest{ - t: t, serviceHostname: "foo.example.org", nodeType: typ, mesh: testMesh(), - destRule: &networking.DestinationRule{ - Host: "*.example.org", - Subsets: []*networking.Subset{ - { - Name: "foobar", - Labels: map[string]string{"foo": "bar"}, - TrafficPolicy: &networking.TrafficPolicy{ - PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ - { - Port: &networking.PortSelector{ - Number: 8080, - }, - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_ISTIO_MUTUAL, - Sni: sniValue, - }, - }, - }, - }, - }, - }, - }, - meta: meta, - istioVersion: model.MaxIstioVersion, - }) -} - -func TestBuildSidecarClustersWithMeshWideTCPKeepalive(t *testing.T) { - cases := []struct { - name string - rule ConfigType - keepalive *core.TcpKeepalive - }{ - { - // Do not set tcp_keepalive anywhere - "unset", - None, - nil, - }, - { - // Set mesh wide default for tcp_keepalive. - "mesh", - Mesh, - &core.TcpKeepalive{KeepaliveTime: &wrappers.UInt32Value{Value: uint32(MeshWideTCPKeepaliveSeconds)}}, - }, - { - // Set DestinationRule override for tcp_keepalive. - "destination rule", - DestinationRule, - &core.TcpKeepalive{KeepaliveTime: &wrappers.UInt32Value{Value: uint32(DestinationRuleTCPKeepaliveSeconds)}}, - }, - { - // Set DestinationRule override for tcp_keepalive with empty value. - "destination rule default", - DestinationRuleForOsDefault, - &core.TcpKeepalive{KeepaliveTime: &wrappers.UInt32Value{Value: uint32(MeshWideTCPKeepaliveSeconds)}}, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - c := xdstest.ExtractCluster("outbound|8080|foobar|foo.example.org", buildTestClustersWithTCPKeepalive(t, tt.rule)) - if diff := cmp.Diff(c.GetUpstreamConnectionOptions().GetTcpKeepalive(), tt.keepalive, protocmp.Transform()); diff != "" { - t.Fatalf("got diff: %v", diff) - } - }) - } -} - -func buildTestClustersWithTCPKeepalive(t testing.TB, configType ConfigType) []*cluster.Cluster { - // Set mesh wide defaults. - m := testMesh() - if configType != None { - m.TcpKeepalive = &networking.ConnectionPoolSettings_TCPSettings_TcpKeepalive{ - Time: &durationpb.Duration{ - Seconds: MeshWideTCPKeepaliveSeconds, - Nanos: 0, - }, - } - } - - // Set DestinationRule override. - var destinationRuleTCPKeepalive *networking.ConnectionPoolSettings_TCPSettings_TcpKeepalive - if configType == DestinationRule { - destinationRuleTCPKeepalive = &networking.ConnectionPoolSettings_TCPSettings_TcpKeepalive{ - Time: &durationpb.Duration{ - Seconds: DestinationRuleTCPKeepaliveSeconds, - Nanos: 0, - }, - } - } - - // Set empty tcp_keepalive. - if configType == DestinationRuleForOsDefault { - destinationRuleTCPKeepalive = &networking.ConnectionPoolSettings_TCPSettings_TcpKeepalive{} - } - - return buildTestClusters(clusterTest{ - t: t, serviceHostname: "foo.example.org", nodeType: model.SidecarProxy, mesh: m, - destRule: &networking.DestinationRule{ - Host: "*.example.org", - Subsets: []*networking.Subset{ - { - Name: "foobar", - Labels: map[string]string{"foo": "bar"}, - TrafficPolicy: &networking.TrafficPolicy{ - PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ - { - Port: &networking.PortSelector{ - Number: 8080, - }, - ConnectionPool: &networking.ConnectionPoolSettings{ - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - TcpKeepalive: destinationRuleTCPKeepalive, - }, - }, - }, - }, - }, - }, - }, - }, - }) -} - -func TestClusterMetadata(t *testing.T) { - g := NewWithT(t) - - destRule := &networking.DestinationRule{ - Host: "*.example.org", - Subsets: []*networking.Subset{ - {Name: "Subset 1"}, - {Name: "Subset 2"}, - }, - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRequestsPerConnection: 1, - }, - }, - }, - } - - clusters := buildTestClusters(clusterTest{t: t, serviceHostname: "*.example.org", nodeType: model.SidecarProxy, mesh: testMesh(), destRule: destRule}) - - clustersWithMetadata := 0 - - foundSubset := false - for _, cluster := range clusters { - if strings.HasPrefix(cluster.Name, "outbound") || strings.HasPrefix(cluster.Name, "inbound") { - clustersWithMetadata++ - g.Expect(cluster.Metadata).NotTo(BeNil()) - md := cluster.Metadata - g.Expect(md.FilterMetadata[util.IstioMetadataKey]).NotTo(BeNil()) - istio := md.FilterMetadata[util.IstioMetadataKey] - g.Expect(istio.Fields["config"]).NotTo(BeNil()) - dr := istio.Fields["config"] - g.Expect(dr.GetStringValue()).To(Equal("/apis/networking.istio.io/v1alpha3/namespaces//destination-rule/acme")) - if strings.Contains(cluster.Name, "Subset") { - foundSubset = true - sub := istio.Fields["subset"] - g.Expect(sub.GetStringValue()).To(HavePrefix("Subset ")) - } else { - _, ok := istio.Fields["subset"] - g.Expect(ok).To(Equal(false)) - } - } else { - g.Expect(cluster.Metadata).To(BeNil()) - } - } - - g.Expect(foundSubset).To(Equal(true)) - g.Expect(clustersWithMetadata).To(Equal(len(destRule.Subsets) + 6)) // outbound outbound subsets inbound - - sniClusters := buildSniDnatTestClustersForGateway(t, "test-sni") - - foundSNISubset := false - for _, cluster := range sniClusters { - if strings.HasPrefix(cluster.Name, "outbound") { - g.Expect(cluster.Metadata).NotTo(BeNil()) - md := cluster.Metadata - g.Expect(md.FilterMetadata[util.IstioMetadataKey]).NotTo(BeNil()) - istio := md.FilterMetadata[util.IstioMetadataKey] - g.Expect(istio.Fields["config"]).NotTo(BeNil()) - dr := istio.Fields["config"] - g.Expect(dr.GetStringValue()).To(Equal("/apis/networking.istio.io/v1alpha3/namespaces//destination-rule/acme")) - if strings.Contains(cluster.Name, "foobar") { - foundSNISubset = true - sub := istio.Fields["subset"] - g.Expect(sub.GetStringValue()).To(Equal("foobar")) - } else { - _, ok := istio.Fields["subset"] - g.Expect(ok).To(Equal(false)) - } - } else { - g.Expect(cluster.Metadata).To(BeNil()) - } - } - - g.Expect(foundSNISubset).To(Equal(true)) -} - -func TestDisablePanicThresholdAsDefault(t *testing.T) { - g := NewWithT(t) - - outliers := []*networking.OutlierDetection{ - // Unset MinHealthPercent - {}, - // Explicitly set MinHealthPercent to 0 - { - MinHealthPercent: 0, - }, - } - - for _, outlier := range outliers { - c := xdstest.ExtractCluster("outbound|8080||*.example.org", - buildTestClusters(clusterTest{ - t: t, serviceHostname: "*.example.org", serviceResolution: model.DNSLB, nodeType: model.SidecarProxy, - locality: &core.Locality{}, mesh: testMesh(), - destRule: &networking.DestinationRule{ - Host: "*.example.org", - TrafficPolicy: &networking.TrafficPolicy{ - OutlierDetection: outlier, - }, - }, - })) - g.Expect(c.CommonLbConfig.HealthyPanicThreshold).To(Not(BeNil())) - g.Expect(c.CommonLbConfig.HealthyPanicThreshold.GetValue()).To(Equal(float64(0))) - } -} - -func TestApplyOutlierDetection(t *testing.T) { - g := NewWithT(t) - - tests := []struct { - name string - cfg *networking.OutlierDetection - o *cluster.OutlierDetection - }{ - { - "Nil outlier detection", - nil, - nil, - }, - { - "No outlier detection is set", - &networking.OutlierDetection{}, - &cluster.OutlierDetection{ - EnforcingSuccessRate: &wrappers.UInt32Value{Value: 0}, - }, - }, - { - "Consecutive gateway and 5xx errors are set", - &networking.OutlierDetection{ - Consecutive_5XxErrors: &wrappers.UInt32Value{Value: 4}, - ConsecutiveGatewayErrors: &wrappers.UInt32Value{Value: 3}, - }, - &cluster.OutlierDetection{ - Consecutive_5Xx: &wrappers.UInt32Value{Value: 4}, - EnforcingConsecutive_5Xx: &wrappers.UInt32Value{Value: 100}, - ConsecutiveGatewayFailure: &wrappers.UInt32Value{Value: 3}, - EnforcingConsecutiveGatewayFailure: &wrappers.UInt32Value{Value: 100}, - EnforcingSuccessRate: &wrappers.UInt32Value{Value: 0}, - }, - }, - { - "Only consecutive gateway is set", - &networking.OutlierDetection{ - ConsecutiveGatewayErrors: &wrappers.UInt32Value{Value: 3}, - }, - &cluster.OutlierDetection{ - ConsecutiveGatewayFailure: &wrappers.UInt32Value{Value: 3}, - EnforcingConsecutiveGatewayFailure: &wrappers.UInt32Value{Value: 100}, - EnforcingSuccessRate: &wrappers.UInt32Value{Value: 0}, - }, - }, - { - "Only consecutive 5xx is set", - &networking.OutlierDetection{ - Consecutive_5XxErrors: &wrappers.UInt32Value{Value: 3}, - }, - &cluster.OutlierDetection{ - Consecutive_5Xx: &wrappers.UInt32Value{Value: 3}, - EnforcingConsecutive_5Xx: &wrappers.UInt32Value{Value: 100}, - EnforcingSuccessRate: &wrappers.UInt32Value{Value: 0}, - }, - }, - { - "Consecutive gateway is set to 0", - &networking.OutlierDetection{ - ConsecutiveGatewayErrors: &wrappers.UInt32Value{Value: 0}, - }, - &cluster.OutlierDetection{ - ConsecutiveGatewayFailure: &wrappers.UInt32Value{Value: 0}, - EnforcingConsecutiveGatewayFailure: &wrappers.UInt32Value{Value: 0}, - EnforcingSuccessRate: &wrappers.UInt32Value{Value: 0}, - }, - }, - { - "Consecutive 5xx is set to 0", - &networking.OutlierDetection{ - Consecutive_5XxErrors: &wrappers.UInt32Value{Value: 0}, - }, - &cluster.OutlierDetection{ - Consecutive_5Xx: &wrappers.UInt32Value{Value: 0}, - EnforcingConsecutive_5Xx: &wrappers.UInt32Value{Value: 0}, - EnforcingSuccessRate: &wrappers.UInt32Value{Value: 0}, - }, - }, - { - "Local origin errors is enabled", - &networking.OutlierDetection{ - SplitExternalLocalOriginErrors: true, - ConsecutiveLocalOriginFailures: &wrappers.UInt32Value{Value: 10}, - }, - &cluster.OutlierDetection{ - EnforcingSuccessRate: &wrappers.UInt32Value{Value: 0}, - SplitExternalLocalOriginErrors: true, - ConsecutiveLocalOriginFailure: &wrappers.UInt32Value{Value: 10}, - EnforcingLocalOriginSuccessRate: &wrappers.UInt32Value{Value: 0}, - EnforcingConsecutiveLocalOriginFailure: &wrappers.UInt32Value{Value: 100}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := xdstest.ExtractCluster("outbound|8080||*.example.org", - buildTestClusters(clusterTest{ - t: t, serviceHostname: "*.example.org", serviceResolution: model.DNSLB, nodeType: model.SidecarProxy, - locality: &core.Locality{}, mesh: testMesh(), - destRule: &networking.DestinationRule{ - Host: "*.example.org", - TrafficPolicy: &networking.TrafficPolicy{ - OutlierDetection: tt.cfg, - }, - }, - })) - g.Expect(c.OutlierDetection).To(Equal(tt.o)) - }) - } -} - -func TestStatNamePattern(t *testing.T) { - g := NewWithT(t) - - statConfigMesh := &meshconfig.MeshConfig{ - ConnectTimeout: &durationpb.Duration{ - Seconds: 10, - Nanos: 1, - }, - EnableAutoMtls: &wrappers.BoolValue{ - Value: false, - }, - InboundClusterStatName: "LocalService_%SERVICE%", - OutboundClusterStatName: "%SERVICE%_%SERVICE_PORT_NAME%_%SERVICE_PORT%", - } - - clusters := buildTestClusters(clusterTest{ - t: t, serviceHostname: "*.example.org", serviceResolution: model.DNSLB, nodeType: model.SidecarProxy, - locality: &core.Locality{}, mesh: statConfigMesh, - destRule: &networking.DestinationRule{ - Host: "*.example.org", - }, - }) - g.Expect(xdstest.ExtractCluster("outbound|8080||*.example.org", clusters).AltStatName).To(Equal("*.example.org_default_8080")) - g.Expect(xdstest.ExtractCluster("inbound|10001||", clusters).AltStatName).To(Equal("LocalService_*.example.org")) -} - -func TestDuplicateClusters(t *testing.T) { - buildTestClusters(clusterTest{ - t: t, serviceHostname: "*.example.org", serviceResolution: model.DNSLB, nodeType: model.SidecarProxy, - locality: &core.Locality{}, mesh: testMesh(), - destRule: &networking.DestinationRule{ - Host: "*.example.org", - }, - }) -} - -func TestSidecarLocalityLB(t *testing.T) { - g := NewWithT(t) - // Distribute locality loadbalancing setting - mesh := testMesh() - mesh.LocalityLbSetting = &networking.LocalityLoadBalancerSetting{ - Distribute: []*networking.LocalityLoadBalancerSetting_Distribute{ - { - From: "region1/zone1/subzone1", - To: map[string]uint32{ - "region1/zone1/*": 50, - "region2/zone1/subzone1": 50, - }, - }, - }, - } - - c := xdstest.ExtractCluster("outbound|8080||*.example.org", - buildTestClusters(clusterTest{ - t: t, serviceHostname: "*.example.org", serviceResolution: model.DNSLB, nodeType: model.SidecarProxy, - locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, mesh: mesh, - destRule: &networking.DestinationRule{ - Host: "*.example.org", - TrafficPolicy: &networking.TrafficPolicy{ - OutlierDetection: &networking.OutlierDetection{ - MinHealthPercent: 10, - }, - }, - }, - })) - - if c.CommonLbConfig == nil { - t.Fatalf("CommonLbConfig should be set for cluster %+v", c) - } - g.Expect(c.CommonLbConfig.HealthyPanicThreshold.GetValue()).To(Equal(float64(10))) - - g.Expect(len(c.LoadAssignment.Endpoints)).To(Equal(3)) - for _, localityLbEndpoint := range c.LoadAssignment.Endpoints { - locality := localityLbEndpoint.Locality - if locality.Region == "region1" && locality.SubZone == "subzone1" { - g.Expect(localityLbEndpoint.LoadBalancingWeight.GetValue()).To(Equal(uint32(34))) - g.Expect(localityLbEndpoint.LbEndpoints[0].LoadBalancingWeight.GetValue()).To(Equal(uint32(40))) - } else if locality.Region == "region1" && locality.SubZone == "subzone2" { - g.Expect(localityLbEndpoint.LoadBalancingWeight.GetValue()).To(Equal(uint32(17))) - g.Expect(localityLbEndpoint.LbEndpoints[0].LoadBalancingWeight.GetValue()).To(Equal(uint32(20))) - } else if locality.Region == "region2" { - g.Expect(localityLbEndpoint.LoadBalancingWeight.GetValue()).To(Equal(uint32(50))) - g.Expect(len(localityLbEndpoint.LbEndpoints)).To(Equal(1)) - g.Expect(localityLbEndpoint.LbEndpoints[0].LoadBalancingWeight.GetValue()).To(Equal(uint32(40))) - } - } - - // Test failover - // failover locality loadbalancing setting - mesh = testMesh() - mesh.LocalityLbSetting = &networking.LocalityLoadBalancerSetting{Failover: []*networking.LocalityLoadBalancerSetting_Failover{}} - - c = xdstest.ExtractCluster("outbound|8080||*.example.org", - buildTestClusters(clusterTest{ - t: t, serviceHostname: "*.example.org", serviceResolution: model.DNSLB, nodeType: model.SidecarProxy, - locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, mesh: mesh, - destRule: &networking.DestinationRule{ - Host: "*.example.org", - TrafficPolicy: &networking.TrafficPolicy{ - OutlierDetection: &networking.OutlierDetection{ - MinHealthPercent: 10, - }, - }, - }, - })) - if c.CommonLbConfig == nil { - t.Fatalf("CommonLbConfig should be set for cluster %+v", c) - } - g.Expect(c.CommonLbConfig.HealthyPanicThreshold.GetValue()).To(Equal(float64(10))) - - g.Expect(len(c.LoadAssignment.Endpoints)).To(Equal(3)) - for _, localityLbEndpoint := range c.LoadAssignment.Endpoints { - locality := localityLbEndpoint.Locality - if locality.Region == "region1" && locality.Zone == "zone1" && locality.SubZone == "subzone1" { - g.Expect(localityLbEndpoint.Priority).To(Equal(uint32(0))) - } else if locality.Region == "region1" && locality.Zone == "zone1" && locality.SubZone == "subzone2" { - g.Expect(localityLbEndpoint.Priority).To(Equal(uint32(1))) - } else if locality.Region == "region2" { - g.Expect(localityLbEndpoint.Priority).To(Equal(uint32(2))) - } - } -} - -func TestLocalityLBDestinationRuleOverride(t *testing.T) { - g := NewWithT(t) - mesh := testMesh() - // Distribute locality loadbalancing setting - mesh.LocalityLbSetting = &networking.LocalityLoadBalancerSetting{ - Distribute: []*networking.LocalityLoadBalancerSetting_Distribute{ - { - From: "region1/zone1/subzone1", - To: map[string]uint32{ - "region1/zone1/*": 50, - "region2/zone1/subzone1": 50, - }, - }, - }, - } - - c := xdstest.ExtractCluster("outbound|8080||*.example.org", - buildTestClusters(clusterTest{ - t: t, serviceHostname: "*.example.org", serviceResolution: model.DNSLB, nodeType: model.SidecarProxy, - locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, mesh: mesh, - destRule: &networking.DestinationRule{ - Host: "*.example.org", - TrafficPolicy: &networking.TrafficPolicy{ - OutlierDetection: &networking.OutlierDetection{ - MinHealthPercent: 10, - }, - LoadBalancer: &networking.LoadBalancerSettings{LocalityLbSetting: &networking.LocalityLoadBalancerSetting{ - Distribute: []*networking.LocalityLoadBalancerSetting_Distribute{ - { - From: "region1/zone1/subzone1", - To: map[string]uint32{ - "region1/zone1/*": 60, - "region2/zone1/subzone1": 40, - }, - }, - }, - }}, - }, - }, - })) - - if c.CommonLbConfig == nil { - t.Fatalf("CommonLbConfig should be set for cluster %+v", c) - } - g.Expect(c.CommonLbConfig.HealthyPanicThreshold.GetValue()).To(Equal(float64(10))) - - g.Expect(len(c.LoadAssignment.Endpoints)).To(Equal(3)) - for _, localityLbEndpoint := range c.LoadAssignment.Endpoints { - locality := localityLbEndpoint.Locality - if locality.Region == "region1" && locality.SubZone == "subzone1" { - g.Expect(localityLbEndpoint.LoadBalancingWeight.GetValue()).To(Equal(uint32(40))) - g.Expect(localityLbEndpoint.LbEndpoints[0].LoadBalancingWeight.GetValue()).To(Equal(uint32(40))) - } else if locality.Region == "region1" && locality.SubZone == "subzone2" { - g.Expect(localityLbEndpoint.LoadBalancingWeight.GetValue()).To(Equal(uint32(20))) - g.Expect(localityLbEndpoint.LbEndpoints[0].LoadBalancingWeight.GetValue()).To(Equal(uint32(20))) - } else if locality.Region == "region2" { - g.Expect(localityLbEndpoint.LoadBalancingWeight.GetValue()).To(Equal(uint32(40))) - g.Expect(len(localityLbEndpoint.LbEndpoints)).To(Equal(1)) - g.Expect(localityLbEndpoint.LbEndpoints[0].LoadBalancingWeight.GetValue()).To(Equal(uint32(40))) - } - } -} - -func TestGatewayLocalityLB(t *testing.T) { - g := NewWithT(t) - - // Test distribute - // Distribute locality loadbalancing setting - mesh := testMesh() - mesh.LocalityLbSetting = &networking.LocalityLoadBalancerSetting{ - Distribute: []*networking.LocalityLoadBalancerSetting_Distribute{ - { - From: "region1/zone1/subzone1", - To: map[string]uint32{ - "region1/zone1/*": 50, - "region2/zone1/subzone1": 50, - }, - }, - }, - } - - test.SetBoolForTest(t, &features.FilterGatewayClusterConfig, false) - - c := xdstest.ExtractCluster("outbound|8080||*.example.org", - buildTestClusters(clusterTest{ - t: t, serviceHostname: "*.example.org", serviceResolution: model.DNSLB, nodeType: model.Router, - locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, mesh: mesh, - destRule: &networking.DestinationRule{ - Host: "*.example.org", - TrafficPolicy: &networking.TrafficPolicy{ - OutlierDetection: &networking.OutlierDetection{ - MinHealthPercent: 10, - }, - }, - }, - meta: &model.NodeMetadata{}, - })) - - if c.CommonLbConfig == nil { - t.Errorf("CommonLbConfig should be set for cluster %+v", c) - } - g.Expect(c.CommonLbConfig.HealthyPanicThreshold.GetValue()).To(Equal(float64(10))) - g.Expect(len(c.LoadAssignment.Endpoints)).To(Equal(3)) - for _, localityLbEndpoint := range c.LoadAssignment.Endpoints { - locality := localityLbEndpoint.Locality - if locality.Region == "region1" && locality.SubZone == "subzone1" { - g.Expect(localityLbEndpoint.LoadBalancingWeight.GetValue()).To(Equal(uint32(34))) - g.Expect(localityLbEndpoint.LbEndpoints[0].LoadBalancingWeight.GetValue()).To(Equal(uint32(40))) - } else if locality.Region == "region1" && locality.SubZone == "subzone2" { - g.Expect(localityLbEndpoint.LoadBalancingWeight.GetValue()).To(Equal(uint32(17))) - g.Expect(localityLbEndpoint.LbEndpoints[0].LoadBalancingWeight.GetValue()).To(Equal(uint32(20))) - } else if locality.Region == "region2" { - g.Expect(localityLbEndpoint.LoadBalancingWeight.GetValue()).To(Equal(uint32(50))) - g.Expect(len(localityLbEndpoint.LbEndpoints)).To(Equal(1)) - g.Expect(localityLbEndpoint.LbEndpoints[0].LoadBalancingWeight.GetValue()).To(Equal(uint32(40))) - } - } - - // Test failover - mesh = testMesh() - mesh.LocalityLbSetting = &networking.LocalityLoadBalancerSetting{Failover: []*networking.LocalityLoadBalancerSetting_Failover{}} - - c = xdstest.ExtractCluster("outbound|8080||*.example.org", - buildTestClusters(clusterTest{ - t: t, serviceHostname: "*.example.org", serviceResolution: model.DNSLB, nodeType: model.Router, - locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, mesh: mesh, - destRule: &networking.DestinationRule{ - Host: "*.example.org", - TrafficPolicy: &networking.TrafficPolicy{ - OutlierDetection: &networking.OutlierDetection{ - MinHealthPercent: 10, - }, - }, - }, // peerAuthn - meta: &model.NodeMetadata{}, - })) - - if c.CommonLbConfig == nil { - t.Fatalf("CommonLbConfig should be set for cluster %+v", c) - } - g.Expect(c.CommonLbConfig.HealthyPanicThreshold.GetValue()).To(Equal(float64(10))) - - g.Expect(len(c.LoadAssignment.Endpoints)).To(Equal(3)) - for _, localityLbEndpoint := range c.LoadAssignment.Endpoints { - locality := localityLbEndpoint.Locality - if locality.Region == "region1" && locality.Zone == "zone1" && locality.SubZone == "subzone1" { - g.Expect(localityLbEndpoint.Priority).To(Equal(uint32(0))) - } else if locality.Region == "region1" && locality.Zone == "zone1" && locality.SubZone == "subzone2" { - g.Expect(localityLbEndpoint.Priority).To(Equal(uint32(1))) - } else if locality.Region == "region2" { - g.Expect(localityLbEndpoint.Priority).To(Equal(uint32(2))) - } - } -} - -func TestFindServiceInstanceForIngressListener(t *testing.T) { - servicePort := &model.Port{ - Name: "default", - Port: 7443, - Protocol: protocol.HTTP, - } - service := &model.Service{ - Hostname: host.Name("*.example.org"), - Ports: model.PortList{servicePort}, - Resolution: model.ClientSideLB, - } - - instances := []*model.ServiceInstance{ - { - Service: service, - ServicePort: servicePort, - Endpoint: &model.IstioEndpoint{ - Address: "192.168.1.1", - EndpointPort: 7443, - Locality: model.Locality{ - ClusterID: "", - Label: "region1/zone1/subzone1", - }, - LbWeight: 30, - }, - }, - } - - ingress := &networking.IstioIngressListener{ - CaptureMode: networking.CaptureMode_NONE, - DefaultEndpoint: "127.0.0.1:7020", - Port: &networking.Port{ - Number: 7443, - Name: "grpc-core", - Protocol: "GRPC", - }, - } - instance := findOrCreateServiceInstance(instances, ingress, "sidecar", "sidecarns") - if instance == nil || instance.Service.Hostname.Matches("sidecar.sidecarns") { - t.Fatal("Expected to return a valid instance, but got nil/default instance") - } - if instance == instances[0] { - t.Fatal("Expected to return a copy of instance, but got the same instance") - } - if !reflect.DeepEqual(instance, instances[0]) { - t.Fatal("Expected returned copy of instance to be equal, but they are different") - } -} - -func TestClusterDiscoveryTypeAndLbPolicyRoundRobin(t *testing.T) { - g := NewWithT(t) - - clusters := buildTestClusters(clusterTest{ - t: t, - serviceHostname: "*.example.org", - serviceResolution: model.Passthrough, - nodeType: model.SidecarProxy, - mesh: testMesh(), - destRule: &networking.DestinationRule{ - Host: "*.example.org", - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: networking.LoadBalancerSettings_ROUND_ROBIN, - }, - }, - }, - }, - }) - - c := xdstest.ExtractCluster("outbound|8080||*.example.org", - clusters) - g.Expect(c.LbPolicy).To(Equal(cluster.Cluster_CLUSTER_PROVIDED)) - g.Expect(c.GetClusterDiscoveryType()).To(Equal(&cluster.Cluster_Type{Type: cluster.Cluster_ORIGINAL_DST})) -} - -func TestSlowStartConfig(t *testing.T) { - g := NewWithT(t) - testcases := []struct { - name string - lbType networking.LoadBalancerSettings_SimpleLB - enableSlowStartMode bool - }{ - {name: "roundrobin", lbType: networking.LoadBalancerSettings_ROUND_ROBIN, enableSlowStartMode: true}, - {name: "leastrequest", lbType: networking.LoadBalancerSettings_LEAST_REQUEST, enableSlowStartMode: true}, - {name: "passthrough", lbType: networking.LoadBalancerSettings_PASSTHROUGH, enableSlowStartMode: true}, - {name: "roundrobin-without-warmup", lbType: networking.LoadBalancerSettings_ROUND_ROBIN, enableSlowStartMode: false}, - {name: "leastrequest-without-warmup", lbType: networking.LoadBalancerSettings_LEAST_REQUEST, enableSlowStartMode: false}, - {name: "empty lb type", enableSlowStartMode: true}, - } - - for _, test := range testcases { - t.Run(test.name, func(t *testing.T) { - clusters := buildTestClusters(clusterTest{ - t: t, - serviceHostname: test.name, - nodeType: model.SidecarProxy, - mesh: testMesh(), - destRule: &networking.DestinationRule{ - Host: test.name, - TrafficPolicy: getSlowStartTrafficPolicy(test.enableSlowStartMode, test.lbType), - }, - }) - - c := xdstest.ExtractCluster("outbound|8080||"+test.name, - clusters) - - if !test.enableSlowStartMode { - g.Expect(c.GetLbConfig()).To(BeNil()) - } else { - switch c.LbPolicy { - case cluster.Cluster_ROUND_ROBIN: - g.Expect(c.GetRoundRobinLbConfig().GetSlowStartConfig().GetSlowStartWindow().Seconds).To(Equal(int64(15))) - case cluster.Cluster_LEAST_REQUEST: - g.Expect(c.GetLeastRequestLbConfig().GetSlowStartConfig().GetSlowStartWindow().Seconds).To(Equal(int64(15))) - default: - g.Expect(c.GetLbConfig()).To(BeNil()) - } - } - }) - } -} - -func getSlowStartTrafficPolicy(slowStartEnabled bool, lbType networking.LoadBalancerSettings_SimpleLB) *networking.TrafficPolicy { - var warmupDurationSecs *durationpb.Duration - if slowStartEnabled { - warmupDurationSecs = &durationpb.Duration{Seconds: 15} - } - return &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: lbType, - }, - WarmupDurationSecs: warmupDurationSecs, - }, - } -} - -func TestClusterDiscoveryTypeAndLbPolicyPassthrough(t *testing.T) { - g := NewWithT(t) - - clusters := buildTestClusters(clusterTest{ - t: t, - serviceHostname: "*.example.org", - serviceResolution: model.ClientSideLB, - nodeType: model.SidecarProxy, - mesh: testMesh(), - destRule: &networking.DestinationRule{ - Host: "*.example.org", - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: networking.LoadBalancerSettings_PASSTHROUGH, - }, - }, - }, - }, - }) - - c := xdstest.ExtractCluster("outbound|8080||*.example.org", clusters) - g.Expect(c.LbPolicy).To(Equal(cluster.Cluster_CLUSTER_PROVIDED)) - g.Expect(c.GetClusterDiscoveryType()).To(Equal(&cluster.Cluster_Type{Type: cluster.Cluster_ORIGINAL_DST})) - g.Expect(c.EdsClusterConfig).To(BeNil()) -} - -func TestBuildInboundClustersPortLevelCircuitBreakerThresholds(t *testing.T) { - servicePort := &model.Port{ - Name: "default", - Port: 80, - Protocol: protocol.HTTP, - } - - service := &model.Service{ - Hostname: host.Name("backend.default.svc.cluster.local"), - Ports: model.PortList{servicePort}, - Resolution: model.Passthrough, - } - - instances := []*model.ServiceInstance{ - { - Service: service, - ServicePort: servicePort, - Endpoint: &model.IstioEndpoint{ - Address: "1.1.1.1", - EndpointPort: 10001, - }, - }, - } - inboundFilter := func(c *cluster.Cluster) bool { - return strings.HasPrefix(c.Name, "inbound|") - } - - cases := []struct { - name string - filter func(c *cluster.Cluster) bool - destRule *networking.DestinationRule - expected *cluster.CircuitBreakers_Thresholds - }{ - { - name: "defaults", - filter: func(c *cluster.Cluster) bool { - return strings.HasPrefix(c.Name, "inbound|") || strings.HasPrefix(c.Name, "outbound|") - }, - destRule: nil, - expected: getDefaultCircuitBreakerThresholds(), - }, - { - name: "port-level policy matched", - filter: inboundFilter, - destRule: &networking.DestinationRule{ - Host: "backend.default.svc.cluster.local", - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - MaxConnections: 1000, - }, - }, - PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ - { - Port: &networking.PortSelector{ - Number: 80, - }, - ConnectionPool: &networking.ConnectionPoolSettings{ - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - MaxConnections: 100, - }, - }, - }, - }, - }, - }, - expected: &cluster.CircuitBreakers_Thresholds{ - MaxRetries: &wrappers.UInt32Value{Value: math.MaxUint32}, - MaxRequests: &wrappers.UInt32Value{Value: math.MaxUint32}, - MaxConnections: &wrappers.UInt32Value{Value: 100}, - MaxPendingRequests: &wrappers.UInt32Value{Value: math.MaxUint32}, - TrackRemaining: true, - }, - }, - { - name: "port-level policy not matched", - filter: inboundFilter, - destRule: &networking.DestinationRule{ - Host: "backend.default.svc.cluster.local", - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - MaxConnections: 1000, - }, - }, - PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ - { - Port: &networking.PortSelector{ - Number: 8080, - }, - ConnectionPool: &networking.ConnectionPoolSettings{ - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - MaxConnections: 100, - }, - }, - }, - }, - }, - }, - expected: &cluster.CircuitBreakers_Thresholds{ - MaxRetries: &wrappers.UInt32Value{Value: math.MaxUint32}, - MaxRequests: &wrappers.UInt32Value{Value: math.MaxUint32}, - MaxConnections: &wrappers.UInt32Value{Value: 1000}, - MaxPendingRequests: &wrappers.UInt32Value{Value: math.MaxUint32}, - TrackRemaining: true, - }, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - g := NewWithT(t) - cfgs := []config.Config{} - if c.destRule != nil { - cfgs = append(cfgs, config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: "acme", - Namespace: "default", - }, - Spec: c.destRule, - }) - } - cg := NewConfigGenTest(t, TestOptions{ - Services: []*model.Service{service}, - Instances: instances, - Configs: cfgs, - }) - clusters := cg.Clusters(cg.SetupProxy(nil)) - xdstest.ValidateClusters(t, clusters) - if c.filter != nil { - clusters = xdstest.FilterClusters(clusters, c.filter) - } - g.Expect(len(clusters)).ShouldNot(Equal(0)) - - for _, cluster := range clusters { - g.Expect(cluster.CircuitBreakers).NotTo(BeNil()) - g.Expect(cluster.CircuitBreakers.Thresholds[0]).To(Equal(c.expected)) - } - }) - } -} - -func TestRedisProtocolWithPassThroughResolutionAtGateway(t *testing.T) { - servicePort := &model.Port{ - Name: "redis-port", - Port: 6379, - Protocol: protocol.Redis, - } - service := &model.Service{ - Hostname: host.Name("redis.com"), - Ports: model.PortList{servicePort}, - Resolution: model.Passthrough, - } - - cases := []struct { - name string - redisEnabled bool - resolution model.Resolution - lbType cluster.Cluster_LbPolicy - discoveryType cluster.Cluster_DiscoveryType - }{ - { - name: "redis disabled", - redisEnabled: false, - resolution: model.ClientSideLB, - lbType: defaultLBAlgorithm(), - discoveryType: cluster.Cluster_EDS, - }, - { - name: "redis disabled passthrough", - redisEnabled: false, - resolution: model.Passthrough, - lbType: defaultLBAlgorithm(), - discoveryType: cluster.Cluster_EDS, - }, - { - name: "redis enabled", - redisEnabled: true, - resolution: model.ClientSideLB, - lbType: cluster.Cluster_MAGLEV, - discoveryType: cluster.Cluster_EDS, - }, - { - name: "redis enabled passthrough", - redisEnabled: true, - resolution: model.Passthrough, - lbType: cluster.Cluster_MAGLEV, - discoveryType: cluster.Cluster_EDS, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - g := NewWithT(t) - test.SetBoolForTest(t, &features.FilterGatewayClusterConfig, false) - test.SetBoolForTest(t, &features.EnableRedisFilter, tt.redisEnabled) - cg := NewConfigGenTest(t, TestOptions{Services: []*model.Service{service}}) - clusters := cg.Clusters(cg.SetupProxy(&model.Proxy{Type: model.Router})) - xdstest.ValidateClusters(t, clusters) - - c := xdstest.ExtractCluster("outbound|6379||redis.com", clusters) - g.Expect(c.LbPolicy).To(Equal(tt.lbType)) - g.Expect(c.GetClusterDiscoveryType()).To(Equal(&cluster.Cluster_Type{Type: tt.discoveryType})) - }) - } -} - -func TestAutoMTLSClusterSubsets(t *testing.T) { - g := NewWithT(t) - - destRule := &networking.DestinationRule{ - Host: TestServiceNHostname, - Subsets: []*networking.Subset{ - { - Name: "foobar", - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRequestsPerConnection: 1, - }, - }, - PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ - { - Port: &networking.PortSelector{ - Number: 8080, - }, - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_ISTIO_MUTUAL, - Sni: "custom.sni.com", - }, - }, - }, - }, - }, - }, - } - - mesh := testMesh() - mesh.EnableAutoMtls.Value = true - - clusters := buildTestClusters(clusterTest{t: t, serviceHostname: TestServiceNHostname, nodeType: model.SidecarProxy, mesh: mesh, destRule: destRule}) - - tlsContext := getTLSContext(t, clusters[1]) - g.Expect(tlsContext).ToNot(BeNil()) - g.Expect(tlsContext.GetSni()).To(Equal("custom.sni.com")) - g.Expect(clusters[1].TransportSocketMatches).To(HaveLen(0)) - - for _, i := range []int{0, 2, 3} { - g.Expect(getTLSContext(t, clusters[i])).To(BeNil()) - g.Expect(clusters[i].TransportSocketMatches).To(HaveLen(2)) - } -} - -func TestAutoMTLSClusterIgnoreWorkloadLevelPeerAuthn(t *testing.T) { - g := NewWithT(t) - - destRule := &networking.DestinationRule{ - Host: TestServiceNHostname, - TrafficPolicy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRequestsPerConnection: 1, - }, - }, - PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ - { - Port: &networking.PortSelector{ - Number: 9090, - }, - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_DISABLE, - }, - }, - }, - }, - } - - // This workload-level beta policy doesn't affect CDS (yet). - peerAuthn := &authn_beta.PeerAuthentication{ - Selector: &selectorpb.WorkloadSelector{ - MatchLabels: map[string]string{ - "version": "v1", - }, - }, - Mtls: &authn_beta.PeerAuthentication_MutualTLS{ - Mode: authn_beta.PeerAuthentication_MutualTLS_STRICT, - }, - } - - mesh := testMesh() - mesh.EnableAutoMtls.Value = true - - clusters := buildTestClusters(clusterTest{ - t: t, - serviceHostname: TestServiceNHostname, - nodeType: model.SidecarProxy, - mesh: mesh, - destRule: destRule, - peerAuthn: peerAuthn, - }) - - // No policy visible, auto-mTLS should set to PERMISSIVE. - // For port 8080, (m)TLS settings is automatically added, thus its cluster should have TLS context. - // TlsContext is nil because we use socket match instead - g.Expect(getTLSContext(t, clusters[0])).To(BeNil()) - g.Expect(clusters[0].TransportSocketMatches).To(HaveLen(2)) - - // For 9090, use the TLS settings are explicitly specified in DR (which disable TLS) - g.Expect(getTLSContext(t, clusters[1])).To(BeNil()) - - // Sanity check: make sure TLS is not accidentally added to other clusters. - for i := 2; i < len(clusters); i++ { - cluster := clusters[i] - g.Expect(getTLSContext(t, cluster)).To(BeNil()) - } -} - -func TestApplyLoadBalancer(t *testing.T) { - testcases := []struct { - name string - lbSettings *networking.LoadBalancerSettings - discoveryType cluster.Cluster_DiscoveryType - port *model.Port - sendUnhealthyEndpoints bool - expectedLbPolicy cluster.Cluster_LbPolicy - expectedLocalityWeightedConfig bool - }{ - { - name: "ORIGINAL_DST discovery type is a no op", - discoveryType: cluster.Cluster_ORIGINAL_DST, - expectedLbPolicy: cluster.Cluster_CLUSTER_PROVIDED, - }, - { - name: "redis protocol", - discoveryType: cluster.Cluster_EDS, - port: &model.Port{Protocol: protocol.Redis}, - expectedLbPolicy: cluster.Cluster_MAGLEV, - }, - { - name: "Loadbalancer has distribute", - lbSettings: &networking.LoadBalancerSettings{ - LocalityLbSetting: &networking.LocalityLoadBalancerSetting{ - Enabled: &wrappers.BoolValue{Value: true}, - Distribute: []*networking.LocalityLoadBalancerSetting_Distribute{ - { - From: "region1/zone1/subzone1", - To: map[string]uint32{ - "region1/zone1/subzone1": 80, - "region1/zone1/subzone2": 15, - "region1/zone1/subzone3": 5, - }, - }, - }, - }, - }, - discoveryType: cluster.Cluster_EDS, - port: &model.Port{Protocol: protocol.HTTP}, - expectedLbPolicy: defaultLBAlgorithm(), - expectedLocalityWeightedConfig: true, - }, - { - name: "Send Unhealthy Endpoints enabled", - discoveryType: cluster.Cluster_EDS, - sendUnhealthyEndpoints: true, - expectedLbPolicy: defaultLBAlgorithm(), - }, - // TODO: add more to cover all cases - } - - proxy := model.Proxy{ - Type: model.SidecarProxy, - IstioVersion: &model.IstioVersion{Major: 1, Minor: 5}, - Metadata: &model.NodeMetadata{}, - } - - for _, tt := range testcases { - t.Run(tt.name, func(t *testing.T) { - test.SetBoolForTest(t, &features.SendUnhealthyEndpoints, tt.sendUnhealthyEndpoints) - c := &cluster.Cluster{ - ClusterDiscoveryType: &cluster.Cluster_Type{Type: tt.discoveryType}, - } - - if tt.discoveryType == cluster.Cluster_ORIGINAL_DST { - c.LbPolicy = cluster.Cluster_CLUSTER_PROVIDED - } - - if tt.port != nil && tt.port.Protocol == protocol.Redis { - test.SetBoolForTest(t, &features.EnableRedisFilter, true) - } - - applyLoadBalancer(c, tt.lbSettings, tt.port, proxy.Locality, nil, &meshconfig.MeshConfig{}) - - if c.LbPolicy != tt.expectedLbPolicy { - t.Errorf("cluster LbPolicy %s != expected %s", c.LbPolicy, tt.expectedLbPolicy) - } - - if tt.sendUnhealthyEndpoints && c.CommonLbConfig.HealthyPanicThreshold.GetValue() != 0 { - t.Errorf("panic threshold should be disabled when sendHealthyEndpoints is enabeld") - } - - if tt.expectedLocalityWeightedConfig && c.CommonLbConfig.GetLocalityWeightedLbConfig() == nil { - t.Errorf("cluster expected to have weighed config, but is nil") - } - }) - } -} - -func TestBuildStaticClusterWithNoEndPoint(t *testing.T) { - g := NewWithT(t) - - service := &model.Service{ - Hostname: host.Name("static.test"), - Ports: []*model.Port{ - { - Name: "default", - Port: 8080, - Protocol: protocol.HTTP, - }, - }, - Resolution: model.DNSLB, - MeshExternal: true, - Attributes: model.ServiceAttributes{ - Namespace: TestServiceNamespace, - }, - } - cg := NewConfigGenTest(t, TestOptions{ - Services: []*model.Service{service}, - }) - clusters := cg.Clusters(cg.SetupProxy(nil)) - xdstest.ValidateClusters(t, clusters) - - // Expect to ignore STRICT_DNS cluster without endpoints. - g.Expect(xdstest.MapKeys(xdstest.ExtractClusters(clusters))).To(Equal([]string{"BlackHoleCluster", "InboundPassthroughClusterIpv4", "PassthroughCluster"})) -} - -func TestEnvoyFilterPatching(t *testing.T) { - service := &model.Service{ - Hostname: host.Name("static.test"), - Ports: []*model.Port{ - { - Name: "default", - Port: 8080, - Protocol: protocol.HTTP, - }, - }, - Resolution: model.Passthrough, - } - - cases := []struct { - name string - want []string - efs []*networking.EnvoyFilter - proxy model.NodeType - svc *model.Service - }{ - { - "no config", - []string{"outbound|8080||static.test", "BlackHoleCluster", "PassthroughCluster", "InboundPassthroughClusterIpv4"}, - nil, - model.SidecarProxy, - service, - }, - { - "add cluster", - []string{"outbound|8080||static.test", "BlackHoleCluster", "PassthroughCluster", "InboundPassthroughClusterIpv4", "new-cluster1"}, - []*networking.EnvoyFilter{{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{{ - ApplyTo: networking.EnvoyFilter_CLUSTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_ADD, - Value: buildPatchStruct(`{"name":"new-cluster1"}`), - }, - }}, - }}, - model.SidecarProxy, - service, - }, - { - "remove cluster", - []string{"outbound|8080||static.test", "PassthroughCluster", "InboundPassthroughClusterIpv4"}, - []*networking.EnvoyFilter{{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{{ - ApplyTo: networking.EnvoyFilter_CLUSTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Cluster{ - Cluster: &networking.EnvoyFilter_ClusterMatch{ - Name: "BlackHoleCluster", - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }}, - }}, - model.SidecarProxy, - service, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - cfgs := []config.Config{} - for i, c := range tt.efs { - cfgs = append(cfgs, config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.EnvoyFilter, - Name: fmt.Sprint(i), - Namespace: "default", - }, - Spec: c, - }) - } - cg := NewConfigGenTest(t, TestOptions{Configs: cfgs, Services: []*model.Service{tt.svc}}) - clusters := cg.Clusters(cg.SetupProxy(nil)) - clusterNames := xdstest.MapKeys(xdstest.ExtractClusters(clusters)) - sort.Strings(tt.want) - if !cmp.Equal(clusterNames, tt.want) { - t.Fatalf("want %v got %v", tt.want, clusterNames) - } - }) - } -} - -func TestTelemetryMetadata(t *testing.T) { - cases := []struct { - name string - direction model.TrafficDirection - cluster *cluster.Cluster - svcInsts []*model.ServiceInstance - service *model.Service - want *core.Metadata - }{ - { - name: "no cluster", - direction: model.TrafficDirectionInbound, - cluster: nil, - svcInsts: []*model.ServiceInstance{ - { - Service: &model.Service{ - Attributes: model.ServiceAttributes{ - Name: "a", - Namespace: "default", - }, - Hostname: "a.default", - }, - }, - }, - want: nil, - }, - { - name: "inbound no service", - direction: model.TrafficDirectionInbound, - cluster: &cluster.Cluster{}, - svcInsts: []*model.ServiceInstance{}, - want: nil, - }, - { - name: "inbound existing metadata", - direction: model.TrafficDirectionInbound, - cluster: &cluster.Cluster{ - Metadata: &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - "some-metadata": { - Fields: map[string]*structpb.Value{ - "some-key": {Kind: &structpb.Value_StringValue{StringValue: "some-val"}}, - }, - }, - }, - }, - }, - svcInsts: []*model.ServiceInstance{ - { - Service: &model.Service{ - Attributes: model.ServiceAttributes{ - Name: "a", - Namespace: "default", - }, - Hostname: "a.default", - }, - ServicePort: &model.Port{ - Port: 80, - }, - }, - }, - want: &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - "some-metadata": { - Fields: map[string]*structpb.Value{ - "some-key": {Kind: &structpb.Value_StringValue{StringValue: "some-val"}}, - }, - }, - util.IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "services": { - Kind: &structpb.Value_ListValue{ - ListValue: &structpb.ListValue{ - Values: []*structpb.Value{ - { - Kind: &structpb.Value_StructValue{ - StructValue: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "host": { - Kind: &structpb.Value_StringValue{ - StringValue: "a.default", - }, - }, - "name": { - Kind: &structpb.Value_StringValue{ - StringValue: "a", - }, - }, - "namespace": { - Kind: &structpb.Value_StringValue{ - StringValue: "default", - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - { - name: "inbound existing istio metadata", - direction: model.TrafficDirectionInbound, - cluster: &cluster.Cluster{ - Metadata: &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - util.IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "some-key": {Kind: &structpb.Value_StringValue{StringValue: "some-val"}}, - }, - }, - }, - }, - }, - svcInsts: []*model.ServiceInstance{ - { - Service: &model.Service{ - Attributes: model.ServiceAttributes{ - Name: "a", - Namespace: "default", - }, - Hostname: "a.default", - }, - ServicePort: &model.Port{ - Port: 80, - }, - }, - }, - want: &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - util.IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "some-key": {Kind: &structpb.Value_StringValue{StringValue: "some-val"}}, - "services": { - Kind: &structpb.Value_ListValue{ - ListValue: &structpb.ListValue{ - Values: []*structpb.Value{ - { - Kind: &structpb.Value_StructValue{ - StructValue: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "host": { - Kind: &structpb.Value_StringValue{ - StringValue: "a.default", - }, - }, - "name": { - Kind: &structpb.Value_StringValue{ - StringValue: "a", - }, - }, - "namespace": { - Kind: &structpb.Value_StringValue{ - StringValue: "default", - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - { - name: "inbound multiple services", - direction: model.TrafficDirectionInbound, - cluster: &cluster.Cluster{}, - svcInsts: []*model.ServiceInstance{ - { - Service: &model.Service{ - Attributes: model.ServiceAttributes{ - Name: "a", - Namespace: "default", - }, - Hostname: "a.default", - }, - ServicePort: &model.Port{ - Port: 80, - }, - }, - { - Service: &model.Service{ - Attributes: model.ServiceAttributes{ - Name: "b", - Namespace: "default", - }, - Hostname: "b.default", - }, - ServicePort: &model.Port{ - Port: 80, - }, - }, - }, - want: &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - util.IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "services": { - Kind: &structpb.Value_ListValue{ - ListValue: &structpb.ListValue{ - Values: []*structpb.Value{ - { - Kind: &structpb.Value_StructValue{ - StructValue: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "host": { - Kind: &structpb.Value_StringValue{ - StringValue: "a.default", - }, - }, - "name": { - Kind: &structpb.Value_StringValue{ - StringValue: "a", - }, - }, - "namespace": { - Kind: &structpb.Value_StringValue{ - StringValue: "default", - }, - }, - }, - }, - }, - }, - { - Kind: &structpb.Value_StructValue{ - StructValue: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "host": { - Kind: &structpb.Value_StringValue{ - StringValue: "b.default", - }, - }, - "name": { - Kind: &structpb.Value_StringValue{ - StringValue: "b", - }, - }, - "namespace": { - Kind: &structpb.Value_StringValue{ - StringValue: "default", - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - { - name: "inbound existing services metadata", - direction: model.TrafficDirectionInbound, - cluster: &cluster.Cluster{ - Metadata: &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - util.IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "services": {Kind: &structpb.Value_StringValue{StringValue: "some-val"}}, - }, - }, - }, - }, - }, - svcInsts: []*model.ServiceInstance{ - { - Service: &model.Service{ - Attributes: model.ServiceAttributes{ - Name: "a", - Namespace: "default", - }, - Hostname: "a.default", - }, - ServicePort: &model.Port{ - Port: 80, - }, - }, - }, - want: &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - util.IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "services": { - Kind: &structpb.Value_ListValue{ - ListValue: &structpb.ListValue{ - Values: []*structpb.Value{ - { - Kind: &structpb.Value_StructValue{ - StructValue: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "host": { - Kind: &structpb.Value_StringValue{ - StringValue: "a.default", - }, - }, - "name": { - Kind: &structpb.Value_StringValue{ - StringValue: "a", - }, - }, - "namespace": { - Kind: &structpb.Value_StringValue{ - StringValue: "default", - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - { - name: "outbound service metadata", - direction: model.TrafficDirectionOutbound, - cluster: &cluster.Cluster{}, - service: &model.Service{ - Attributes: model.ServiceAttributes{ - Name: "a", - Namespace: "default", - }, - Hostname: "a.default", - }, - want: &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - util.IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "services": { - Kind: &structpb.Value_ListValue{ - ListValue: &structpb.ListValue{ - Values: []*structpb.Value{ - { - Kind: &structpb.Value_StructValue{ - StructValue: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "host": { - Kind: &structpb.Value_StringValue{ - StringValue: "a.default", - }, - }, - "name": { - Kind: &structpb.Value_StringValue{ - StringValue: "a", - }, - }, - "namespace": { - Kind: &structpb.Value_StringValue{ - StringValue: "default", - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - { - name: "inbound duplicated metadata", - direction: model.TrafficDirectionInbound, - cluster: &cluster.Cluster{}, - svcInsts: []*model.ServiceInstance{ - { - Service: &model.Service{ - Attributes: model.ServiceAttributes{ - Name: "a", - Namespace: "default", - }, - Hostname: "a.default", - }, - ServicePort: &model.Port{ - Port: 80, - }, - }, - { - Service: &model.Service{ - Attributes: model.ServiceAttributes{ - Name: "a", - Namespace: "default", - }, - Hostname: "a.default", - }, - ServicePort: &model.Port{ - Port: 80, - }, - }, - }, - want: &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - util.IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "services": { - Kind: &structpb.Value_ListValue{ - ListValue: &structpb.ListValue{ - Values: []*structpb.Value{ - { - Kind: &structpb.Value_StructValue{ - StructValue: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "host": { - Kind: &structpb.Value_StringValue{ - StringValue: "a.default", - }, - }, - "name": { - Kind: &structpb.Value_StringValue{ - StringValue: "a", - }, - }, - "namespace": { - Kind: &structpb.Value_StringValue{ - StringValue: "default", - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - opt := buildClusterOpts{ - mutable: NewMutableCluster(tt.cluster), - port: &model.Port{Port: 80}, - serviceInstances: tt.svcInsts, - } - addTelemetryMetadata(opt, tt.service, tt.direction, tt.svcInsts) - if opt.mutable.cluster != nil && !reflect.DeepEqual(opt.mutable.cluster.Metadata, tt.want) { - t.Errorf("cluster metadata does not match expectation want %+v, got %+v", tt.want, opt.mutable.cluster.Metadata) - } - }) - } -} - -func TestVerifyCertAtClient(t *testing.T) { - testCases := []struct { - name string - policy *networking.TrafficPolicy - verifyCertAtClient bool - expectedCARootPath string - }{ - { - name: "VERIFY_CERTIFICATE_AT_CLIENT works as expected", - policy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - }, - }, - Tls: &networking.ClientTLSSettings{ - CaCertificates: "", - }, - }, - verifyCertAtClient: true, - expectedCARootPath: "system", - }, - { - name: "VERIFY_CERTIFICATE_AT_CLIENT does not override CaCertificates", - policy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - }, - }, - Tls: &networking.ClientTLSSettings{ - CaCertificates: "file-root:certPath", - }, - }, - verifyCertAtClient: true, - expectedCARootPath: "file-root:certPath", - }, - { - name: "Filled CaCertificates does not get over written by VERIFY_CERTIFICATE_AT_CLIENT is false", - policy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - }, - }, - Tls: &networking.ClientTLSSettings{ - CaCertificates: "file-root:certPath", - }, - }, - verifyCertAtClient: false, - expectedCARootPath: "file-root:certPath", - }, - { - name: "Empty CaCertificates does not get over written by VERIFY_CERTIFICATE_AT_CLIENT is false", - policy: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - MaxRetries: 10, - }, - }, - Tls: &networking.ClientTLSSettings{ - CaCertificates: "", - }, - }, - verifyCertAtClient: false, - expectedCARootPath: "", - }, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - test.SetBoolForTest(t, &features.VerifyCertAtClient, testCase.verifyCertAtClient) - selectTrafficPolicyComponents(testCase.policy) - if testCase.policy.Tls.CaCertificates != testCase.expectedCARootPath { - t.Errorf("%v got %v when expecting %v", testCase.name, testCase.policy.Tls.CaCertificates, testCase.expectedCARootPath) - } - }) - } -} - -func TestBuildDeltaClusters(t *testing.T) { - g := NewWithT(t) - - testService1 := &model.Service{ - Hostname: host.Name("test.com"), - Ports: []*model.Port{ - { - Name: "default", - Port: 8080, - Protocol: protocol.HTTP, - }, - }, - Resolution: model.ClientSideLB, - MeshExternal: false, - Attributes: model.ServiceAttributes{ - Namespace: TestServiceNamespace, - }, - } - - testService2 := &model.Service{ - Hostname: host.Name("testnew.com"), - Ports: []*model.Port{ - { - Name: "default", - Port: 8080, - Protocol: protocol.HTTP, - }, - }, - Resolution: model.ClientSideLB, - MeshExternal: false, - Attributes: model.ServiceAttributes{ - Namespace: TestServiceNamespace, - }, - } - - // TODO: Add more test cases. - testCases := []struct { - name string - services []*model.Service - configUpdated map[model.ConfigKey]struct{} - watchedResourceNames []string - usedDelta bool - removedClusters []string - expectedClusters []string - }{ - { - name: "service is added", - services: []*model.Service{testService1, testService2}, - configUpdated: map[model.ConfigKey]struct{}{{Kind: gvk.ServiceEntry, Name: "testnew.com", Namespace: TestServiceNamespace}: {}}, - watchedResourceNames: []string{"outbound|8080||test.com"}, - usedDelta: true, - removedClusters: nil, - expectedClusters: []string{"BlackHoleCluster", "InboundPassthroughClusterIpv4", "PassthroughCluster", "outbound|8080||testnew.com"}, - }, - { - name: "service is removed", - services: []*model.Service{}, - configUpdated: map[model.ConfigKey]struct{}{{Kind: gvk.ServiceEntry, Name: "test.com", Namespace: TestServiceNamespace}: {}}, - watchedResourceNames: []string{"outbound|8080||test.com"}, - usedDelta: true, - removedClusters: []string{"outbound|8080||test.com"}, - expectedClusters: []string{"BlackHoleCluster", "InboundPassthroughClusterIpv4", "PassthroughCluster"}, - }, - { - name: "service port is removed", - services: []*model.Service{testService1}, - configUpdated: map[model.ConfigKey]struct{}{{Kind: gvk.ServiceEntry, Name: "test.com", Namespace: TestServiceNamespace}: {}}, - watchedResourceNames: []string{"outbound|7070||test.com"}, - usedDelta: true, - removedClusters: []string{"outbound|7070||test.com"}, - expectedClusters: []string{"BlackHoleCluster", "InboundPassthroughClusterIpv4", "PassthroughCluster", "outbound|8080||test.com"}, - }, - { - name: "config update that is not delta aware", - services: []*model.Service{testService1, testService2}, - configUpdated: map[model.ConfigKey]struct{}{{Kind: gvk.DestinationRule, Name: "test.com", Namespace: TestServiceNamespace}: {}}, - watchedResourceNames: []string{"outbound|7070||test.com"}, - usedDelta: false, - removedClusters: nil, - expectedClusters: []string{ - "BlackHoleCluster", "InboundPassthroughClusterIpv4", "PassthroughCluster", - "outbound|8080||test.com", "outbound|8080||testnew.com", - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - cg := NewConfigGenTest(t, TestOptions{ - Services: tc.services, - }) - clusters, removed, delta := cg.DeltaClusters(cg.SetupProxy(nil), tc.configUpdated, - &model.WatchedResource{ResourceNames: tc.watchedResourceNames}) - if delta != tc.usedDelta { - t.Errorf("un expected delta, want %v got %v", tc.usedDelta, delta) - } - g.Expect(removed).To(Equal(tc.removedClusters)) - g.Expect(xdstest.MapKeys(xdstest.ExtractClusters(clusters))).To(Equal(tc.expectedClusters)) - }) - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/configgen.go b/pilot/pkg/networking/core/v1alpha3/configgen.go deleted file mode 100644 index 498c3470e..000000000 --- a/pilot/pkg/networking/core/v1alpha3/configgen.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - meshconfig "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" -) - -type ConfigGeneratorImpl struct { - Cache model.XdsCache -} - -func NewConfigGenerator(cache model.XdsCache) *ConfigGeneratorImpl { - return &ConfigGeneratorImpl{ - Cache: cache, - } -} - -// MeshConfigChanged is called when mesh config is changed. -func (configgen *ConfigGeneratorImpl) MeshConfigChanged(_ *meshconfig.MeshConfig) { - accessLogBuilder.reset() -} diff --git a/pilot/pkg/networking/core/v1alpha3/envoyfilter/cluster_patch.go b/pilot/pkg/networking/core/v1alpha3/envoyfilter/cluster_patch.go deleted file mode 100644 index 591ea7ed0..000000000 --- a/pilot/pkg/networking/core/v1alpha3/envoyfilter/cluster_patch.go +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package envoyfilter - -import ( - "fmt" -) - -import ( - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - "google.golang.org/protobuf/proto" - networking "istio.io/api/networking/v1alpha3" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/util/runtime" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/proto/merge" -) - -// ApplyClusterMerge processes the MERGE operation and merges the supplied configuration to the matched clusters. -func ApplyClusterMerge(pctx networking.EnvoyFilter_PatchContext, efw *model.EnvoyFilterWrapper, - c *cluster.Cluster, hosts []host.Name) (out *cluster.Cluster) { - defer runtime.HandleCrash(runtime.LogPanic, func(interface{}) { - log.Errorf("clusters patch caused panic, so the patches did not take effect") - IncrementEnvoyFilterErrorMetric(Cluster) - }) - // In case the patches cause panic, use the clusters generated before to reduce the influence. - out = c - if efw == nil { - return - } - for _, cp := range efw.Patches[networking.EnvoyFilter_CLUSTER] { - applied := false - if cp.Operation != networking.EnvoyFilter_Patch_MERGE { - IncrementEnvoyFilterMetric(cp.Key(), Cluster, applied) - continue - } - if commonConditionMatch(pctx, cp) && clusterMatch(c, cp, hosts) { - - ret, err := mergeTransportSocketCluster(c, cp) - if err != nil { - log.Debugf("Merge of transport socket failed for cluster: %v", err) - continue - } - applied = true - if !ret { - merge.Merge(c, cp.Value) - } - } - IncrementEnvoyFilterMetric(cp.Key(), Cluster, applied) - } - return c -} - -// Test if the patch contains a config for TransportSocket -// Returns a boolean indicating if the merge was handled by this function; if false, it should still be called -// outside of this function. -func mergeTransportSocketCluster(c *cluster.Cluster, cp *model.EnvoyFilterConfigPatchWrapper) (merged bool, err error) { - cpValueCast, okCpCast := (cp.Value).(*cluster.Cluster) - if !okCpCast { - return false, fmt.Errorf("cast of cp.Value failed: %v", okCpCast) - } - - // Check if cluster patch has a transport socket. - if cpValueCast.GetTransportSocket() == nil { - return false, nil - } - var tsmPatch *core.TransportSocket - - // First check if the transport socket matches with any cluster transport socket matches. - if len(c.GetTransportSocketMatches()) > 0 { - for _, tsm := range c.GetTransportSocketMatches() { - if tsm.GetTransportSocket() != nil && cpValueCast.GetTransportSocket().Name == tsm.GetTransportSocket().Name { - tsmPatch = tsm.GetTransportSocket() - break - } - } - if tsmPatch == nil { - // If we merged we would get both a transport_socket and transport_socket_matches which is not valid - // Drop the filter, but indicate that we handled the merge so that the outer function does not try - // to merge it again - return true, nil - } - } else if c.GetTransportSocket() != nil { - if cpValueCast.GetTransportSocket().Name == c.GetTransportSocket().Name { - tsmPatch = c.GetTransportSocket() - } - } - // This means either there is a name mismatch or cluster does not have transport socket matches/transport socket. - // We cannot do a deep merge. Instead just replace the transport socket - if tsmPatch == nil { - c.TransportSocket = cpValueCast.TransportSocket - } else { - // Merge the patch and the cluster at a lower level - dstCluster := tsmPatch.GetTypedConfig() - srcPatch := cpValueCast.GetTransportSocket().GetTypedConfig() - - if dstCluster != nil && srcPatch != nil { - - retVal, errMerge := util.MergeAnyWithAny(dstCluster, srcPatch) - if errMerge != nil { - return false, fmt.Errorf("function MergeAnyWithAny failed for ApplyClusterMerge: %v", errMerge) - } - - // Merge the above result with the whole cluster - merge.Merge(dstCluster, retVal) - } - } - return true, nil -} - -// ShouldKeepCluster checks if there is a REMOVE patch on the cluster, returns false if there is on so that it is removed. -func ShouldKeepCluster(pctx networking.EnvoyFilter_PatchContext, efw *model.EnvoyFilterWrapper, c *cluster.Cluster, hosts []host.Name) bool { - if efw == nil { - return true - } - for _, cp := range efw.Patches[networking.EnvoyFilter_CLUSTER] { - if cp.Operation != networking.EnvoyFilter_Patch_REMOVE { - continue - } - if commonConditionMatch(pctx, cp) && clusterMatch(c, cp, hosts) { - return false - } - } - return true -} - -// InsertedClusters collects all clusters that are added via ADD operation and match the patch context. -func InsertedClusters(pctx networking.EnvoyFilter_PatchContext, efw *model.EnvoyFilterWrapper) []*cluster.Cluster { - if efw == nil { - return nil - } - var result []*cluster.Cluster - // Add cluster if the operation is add, and patch context matches - for _, cp := range efw.Patches[networking.EnvoyFilter_CLUSTER] { - if cp.Operation == networking.EnvoyFilter_Patch_ADD { - // If cluster ADD patch does not specify a patch context, only add for sidecar outbound and gateway. - if cp.Match.Context == networking.EnvoyFilter_ANY && pctx != networking.EnvoyFilter_SIDECAR_OUTBOUND && - pctx != networking.EnvoyFilter_GATEWAY { - continue - } - if commonConditionMatch(pctx, cp) { - result = append(result, proto.Clone(cp.Value).(*cluster.Cluster)) - } - } - } - return result -} - -func clusterMatch(cluster *cluster.Cluster, cp *model.EnvoyFilterConfigPatchWrapper, hosts []host.Name) bool { - cMatch := cp.Match.GetCluster() - if cMatch == nil { - return true - } - - if cMatch.Name != "" { - return cMatch.Name == cluster.Name - } - - direction, subset, hostname, port := model.ParseSubsetKey(cluster.Name) - - hostMatches := []host.Name{hostname} - // For inbound clusters, host parsed from subset key will be empty. Use the passed in service name. - if direction == model.TrafficDirectionInbound && len(hosts) > 0 { - hostMatches = hosts - } - - if cMatch.Subset != "" && cMatch.Subset != subset { - return false - } - - if cMatch.Service != "" && !hostContains(hostMatches, host.Name(cMatch.Service)) { - return false - } - - // FIXME: Ports on a cluster can be 0. the API only takes uint32 for ports - // We should either make that field in API as a wrapper type or switch to int - if cMatch.PortNumber != 0 && int(cMatch.PortNumber) != port { - return false - } - return true -} - -func hostContains(hosts []host.Name, service host.Name) bool { - for _, h := range hosts { - if h == service { - return true - } - } - return false -} diff --git a/pilot/pkg/networking/core/v1alpha3/envoyfilter/cluster_patch_test.go b/pilot/pkg/networking/core/v1alpha3/envoyfilter/cluster_patch_test.go deleted file mode 100644 index 841f071fe..000000000 --- a/pilot/pkg/networking/core/v1alpha3/envoyfilter/cluster_patch_test.go +++ /dev/null @@ -1,632 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package envoyfilter - -import ( - "testing" -) - -import ( - udpa "github.com/cncf/xds/go/udpa/type/v1" - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/testing/protocmp" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/memory" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" -) - -func Test_clusterMatch(t *testing.T) { - type args struct { - proxy *model.Proxy - cluster *cluster.Cluster - matchCondition *networking.EnvoyFilter_EnvoyConfigObjectMatch - operation networking.EnvoyFilter_Patch_Operation - host host.Name - } - tests := []struct { - name string - args args - want bool - }{ - { - name: "name mismatch", - args: args{ - proxy: &model.Proxy{Type: model.SidecarProxy}, - operation: networking.EnvoyFilter_Patch_MERGE, - matchCondition: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Cluster{ - Cluster: &networking.EnvoyFilter_ClusterMatch{Name: "scooby"}, - }, - }, - cluster: &cluster.Cluster{Name: "scrappy"}, - }, - want: false, - }, - { - name: "subset mismatch", - args: args{ - proxy: &model.Proxy{Type: model.SidecarProxy}, - operation: networking.EnvoyFilter_Patch_MERGE, - matchCondition: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Cluster{ - Cluster: &networking.EnvoyFilter_ClusterMatch{ - PortNumber: 80, - Service: "foo.bar", - Subset: "v1", - }, - }, - }, - cluster: &cluster.Cluster{Name: "outbound|80|v2|foo.bar"}, - }, - want: false, - }, - { - name: "service mismatch", - args: args{ - proxy: &model.Proxy{Type: model.SidecarProxy}, - operation: networking.EnvoyFilter_Patch_MERGE, - matchCondition: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Cluster{ - Cluster: &networking.EnvoyFilter_ClusterMatch{ - PortNumber: 80, - Service: "foo.bar", - Subset: "v1", - }, - }, - }, - cluster: &cluster.Cluster{Name: "outbound|80|v1|google.com"}, - }, - want: false, - }, - { - name: "service name match for inbound cluster", - args: args{ - proxy: &model.Proxy{Type: model.SidecarProxy}, - operation: networking.EnvoyFilter_Patch_MERGE, - matchCondition: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Cluster{ - Cluster: &networking.EnvoyFilter_ClusterMatch{ - Service: "foo.bar", - }, - }, - }, - cluster: &cluster.Cluster{Name: "inbound|80||"}, - host: "foo.bar", - }, - want: true, - }, - { - name: "port mismatch", - args: args{ - proxy: &model.Proxy{Type: model.SidecarProxy}, - operation: networking.EnvoyFilter_Patch_MERGE, - matchCondition: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Cluster{ - Cluster: &networking.EnvoyFilter_ClusterMatch{ - PortNumber: 80, - Service: "foo.bar", - Subset: "v1", - }, - }, - }, - cluster: &cluster.Cluster{Name: "outbound|90|v1|foo.bar"}, - }, - want: false, - }, - { - name: "full match", - args: args{ - proxy: &model.Proxy{Type: model.SidecarProxy}, - operation: networking.EnvoyFilter_Patch_MERGE, - matchCondition: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Cluster{ - Cluster: &networking.EnvoyFilter_ClusterMatch{ - PortNumber: 80, - Service: "foo.bar", - Subset: "v1", - }, - }, - }, - cluster: &cluster.Cluster{Name: "outbound|80|v1|foo.bar"}, - }, - want: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := clusterMatch(tt.args.cluster, &model.EnvoyFilterConfigPatchWrapper{Match: tt.args.matchCondition}, []host.Name{tt.args.host}); got != tt.want { - t.Errorf("clusterMatch() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestClusterPatching(t *testing.T) { - configPatches := []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_CLUSTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_ADD, - Value: buildPatchStruct(`{"name":"new-cluster1"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_CLUSTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_ADD, - Value: buildPatchStruct(`{"name":"new-cluster2"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_CLUSTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_GATEWAY, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Cluster{ - Cluster: &networking.EnvoyFilter_ClusterMatch{ - Service: "gateway.com", - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{Operation: networking.EnvoyFilter_Patch_REMOVE}, - }, - { - ApplyTo: networking.EnvoyFilter_CLUSTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Cluster{ - Cluster: &networking.EnvoyFilter_ClusterMatch{ - PortNumber: 9999, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{Operation: networking.EnvoyFilter_Patch_REMOVE}, - }, - { - ApplyTo: networking.EnvoyFilter_CLUSTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Cluster{ - Cluster: &networking.EnvoyFilter_ClusterMatch{ - Service: "service.servicens", - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(`{"type":"EDS"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_CLUSTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_ANY, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(`{"dns_lookup_family":"V6_ONLY"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_CLUSTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_ANY, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(`{"lb_policy":"RING_HASH"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_CLUSTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Cluster{ - Cluster: &networking.EnvoyFilter_ClusterMatch{ - PortNumber: 443, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(` - {"transport_socket":{ - "name":"envoy.transport_sockets.tls", - "typed_config":{ - "@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", - "common_tls_context":{ - "tls_params":{ - "tls_minimum_protocol_version":"TLSv1_3"}}}}}`), - }, - }, - // Patch custom TLS type - { - ApplyTo: networking.EnvoyFilter_CLUSTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Cluster{ - Cluster: &networking.EnvoyFilter_ClusterMatch{ - PortNumber: 7777, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(` - {"transport_socket":{ - "name":"transport_sockets.alts", - "typed_config":{ - "@type":"type.googleapis.com/udpa.type.v1.TypedStruct", - "type_url": "type.googleapis.com/envoy.extensions.transport_sockets.alts.v3.Alts", - "value":{"handshaker_service":"1.2.3.4"}}}}`), - }, - }, - } - - sidecarOutboundIn := []*cluster.Cluster{ - { - Name: "outbound|443||cluster1", - DnsLookupFamily: cluster.Cluster_V4_ONLY, - LbPolicy: cluster.Cluster_ROUND_ROBIN, - TransportSocketMatches: []*cluster.Cluster_TransportSocketMatch{ - { - Name: "tlsMode-istio", - TransportSocket: &core.TransportSocket{ - Name: "envoy.transport_sockets.tls", - ConfigType: &core.TransportSocket_TypedConfig{ - TypedConfig: util.MessageToAny(&tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - EcdhCurves: []string{"X25519"}, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_1, - }, - TlsCertificateCertificateProviderInstance: &tls.CommonTlsContext_CertificateProviderInstance{ - InstanceName: "instance", - CertificateName: "certificate", - }, - }, - }), - }, - }, - }, - }, - }, - { - Name: "outbound|443||cluster2", - Http2ProtocolOptions: &core.Http2ProtocolOptions{ - AllowConnect: true, - AllowMetadata: true, - }, LbPolicy: cluster.Cluster_MAGLEV, - }, - { - Name: "outbound|443||cluster3", - DnsLookupFamily: cluster.Cluster_V4_ONLY, - LbPolicy: cluster.Cluster_ROUND_ROBIN, - TransportSocket: &core.TransportSocket{ - Name: "envoy.transport_sockets.tls", - ConfigType: &core.TransportSocket_TypedConfig{ - TypedConfig: util.MessageToAny(&tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - EcdhCurves: []string{"X25519"}, - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - }, - TlsCertificateCertificateProviderInstance: &tls.CommonTlsContext_CertificateProviderInstance{ - InstanceName: "instance", - CertificateName: "certificate", - }, - }, - }), - }, - }, - }, - { - Name: "outbound|7777||custom-tls-addition", - }, - { - Name: "outbound|7777||custom-tls-replacement", - DnsLookupFamily: cluster.Cluster_V4_ONLY, - LbPolicy: cluster.Cluster_ROUND_ROBIN, - TransportSocket: &core.TransportSocket{ - Name: "envoy.transport_sockets.tls", - ConfigType: &core.TransportSocket_TypedConfig{ - TypedConfig: util.MessageToAny(&tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - EcdhCurves: []string{"X25519"}, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_1, - }, - TlsCertificateCertificateProviderInstance: &tls.CommonTlsContext_CertificateProviderInstance{ - InstanceName: "instance", - CertificateName: "certificate", - }, - }, - }), - }, - }, - }, - { - Name: "outbound|7777||custom-tls-replacement-tsm", - DnsLookupFamily: cluster.Cluster_V4_ONLY, - LbPolicy: cluster.Cluster_ROUND_ROBIN, - TransportSocketMatches: []*cluster.Cluster_TransportSocketMatch{ - { - Name: "tlsMode-istio", - TransportSocket: &core.TransportSocket{ - Name: "envoy.transport_sockets.tls", - ConfigType: &core.TransportSocket_TypedConfig{ - TypedConfig: util.MessageToAny(&tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - EcdhCurves: []string{"X25519"}, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_1, - }, - TlsCertificateCertificateProviderInstance: &tls.CommonTlsContext_CertificateProviderInstance{ - InstanceName: "instance", - CertificateName: "certificate", - }, - }, - }), - }, - }, - }, - }, - }, - } - - sidecarOutboundOut := []*cluster.Cluster{ - { - Name: "outbound|443||cluster1", - DnsLookupFamily: cluster.Cluster_V6_ONLY, - LbPolicy: cluster.Cluster_RING_HASH, - TransportSocketMatches: []*cluster.Cluster_TransportSocketMatch{ - { - Name: "tlsMode-istio", - TransportSocket: &core.TransportSocket{ - Name: "envoy.transport_sockets.tls", - ConfigType: &core.TransportSocket_TypedConfig{ - TypedConfig: util.MessageToAny(&tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - EcdhCurves: []string{"X25519"}, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_3, - }, - TlsCertificateCertificateProviderInstance: &tls.CommonTlsContext_CertificateProviderInstance{ - InstanceName: "instance", - CertificateName: "certificate", - }, - }, - }), - }, - }, - }, - }, - }, - { - Name: "outbound|443||cluster2", - Http2ProtocolOptions: &core.Http2ProtocolOptions{ - AllowConnect: true, - AllowMetadata: true, - }, - LbPolicy: cluster.Cluster_RING_HASH, - DnsLookupFamily: cluster.Cluster_V6_ONLY, - TransportSocket: &core.TransportSocket{ - Name: "envoy.transport_sockets.tls", - ConfigType: &core.TransportSocket_TypedConfig{ - TypedConfig: util.MessageToAny(&tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_3, - }, - }, - }), - }, - }, - }, - { - Name: "outbound|443||cluster3", - DnsLookupFamily: cluster.Cluster_V6_ONLY, - LbPolicy: cluster.Cluster_RING_HASH, - TransportSocket: &core.TransportSocket{ - Name: "envoy.transport_sockets.tls", - ConfigType: &core.TransportSocket_TypedConfig{ - TypedConfig: util.MessageToAny(&tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - EcdhCurves: []string{"X25519"}, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - }, - TlsCertificateCertificateProviderInstance: &tls.CommonTlsContext_CertificateProviderInstance{ - InstanceName: "instance", - CertificateName: "certificate", - }, - }, - }), - }, - }, - }, - { - Name: "outbound|7777||custom-tls-addition", - DnsLookupFamily: cluster.Cluster_V6_ONLY, - LbPolicy: cluster.Cluster_RING_HASH, - TransportSocket: &core.TransportSocket{ - Name: "transport_sockets.alts", - ConfigType: &core.TransportSocket_TypedConfig{ - TypedConfig: util.MessageToAny(&udpa.TypedStruct{ - TypeUrl: "type.googleapis.com/envoy.extensions.transport_sockets.alts.v3.Alts", - Value: buildGolangPatchStruct(`{"handshaker_service":"1.2.3.4"}`), - }), - }, - }, - }, - { - Name: "outbound|7777||custom-tls-replacement", - DnsLookupFamily: cluster.Cluster_V6_ONLY, - LbPolicy: cluster.Cluster_RING_HASH, - TransportSocket: &core.TransportSocket{ - Name: "transport_sockets.alts", - ConfigType: &core.TransportSocket_TypedConfig{ - TypedConfig: util.MessageToAny(&udpa.TypedStruct{ - TypeUrl: "type.googleapis.com/envoy.extensions.transport_sockets.alts.v3.Alts", - Value: buildGolangPatchStruct(`{"handshaker_service":"1.2.3.4"}`), - }), - }, - }, - }, - { - Name: "outbound|7777||custom-tls-replacement-tsm", - DnsLookupFamily: cluster.Cluster_V6_ONLY, - LbPolicy: cluster.Cluster_RING_HASH, - TransportSocketMatches: []*cluster.Cluster_TransportSocketMatch{ - { - Name: "tlsMode-istio", - TransportSocket: &core.TransportSocket{ - Name: "envoy.transport_sockets.tls", - ConfigType: &core.TransportSocket_TypedConfig{ - TypedConfig: util.MessageToAny(&tls.UpstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - EcdhCurves: []string{"X25519"}, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_1, - }, - TlsCertificateCertificateProviderInstance: &tls.CommonTlsContext_CertificateProviderInstance{ - InstanceName: "instance", - CertificateName: "certificate", - }, - }, - }), - }, - }, - }, - }, - }, - {Name: "new-cluster1"}, - {Name: "new-cluster2"}, - } - - sidecarInboundIn := []*cluster.Cluster{ - {Name: "cluster1", DnsLookupFamily: cluster.Cluster_V4_ONLY, LbPolicy: cluster.Cluster_ROUND_ROBIN}, - {Name: "inbound|9999||mgmtCluster"}, - } - sidecarInboundOut := []*cluster.Cluster{ - {Name: "cluster1", DnsLookupFamily: cluster.Cluster_V6_ONLY, LbPolicy: cluster.Cluster_RING_HASH}, - } - - sidecarInboundServiceIn := []*cluster.Cluster{ - {Name: "inbound|7443||", DnsLookupFamily: cluster.Cluster_V4_ONLY, LbPolicy: cluster.Cluster_ROUND_ROBIN}, - } - sidecarInboundServiceOut := []*cluster.Cluster{ - { - Name: "inbound|7443||", ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}, - DnsLookupFamily: cluster.Cluster_V6_ONLY, LbPolicy: cluster.Cluster_RING_HASH, - }, - } - - gatewayInput := []*cluster.Cluster{ - {Name: "cluster1", DnsLookupFamily: cluster.Cluster_V4_ONLY, LbPolicy: cluster.Cluster_ROUND_ROBIN}, - { - Name: "cluster2", - Http2ProtocolOptions: &core.Http2ProtocolOptions{ - AllowConnect: true, - AllowMetadata: true, - }, LbPolicy: cluster.Cluster_MAGLEV, - }, - {Name: "outbound|443||gateway.com"}, - } - gatewayOutput := []*cluster.Cluster{ - {Name: "cluster1", DnsLookupFamily: cluster.Cluster_V6_ONLY, LbPolicy: cluster.Cluster_RING_HASH}, - { - Name: "cluster2", - Http2ProtocolOptions: &core.Http2ProtocolOptions{ - AllowConnect: true, - AllowMetadata: true, - }, LbPolicy: cluster.Cluster_RING_HASH, DnsLookupFamily: cluster.Cluster_V6_ONLY, - }, - } - - testCases := []struct { - name string - input []*cluster.Cluster - proxy *model.Proxy - host string - patchContext networking.EnvoyFilter_PatchContext - output []*cluster.Cluster - }{ - { - name: "sidecar outbound cluster patch", - input: sidecarOutboundIn, - proxy: &model.Proxy{Type: model.SidecarProxy, ConfigNamespace: "not-default"}, - patchContext: networking.EnvoyFilter_SIDECAR_OUTBOUND, - output: sidecarOutboundOut, - }, - { - name: "sidecar inbound cluster patch", - input: sidecarInboundIn, - patchContext: networking.EnvoyFilter_SIDECAR_INBOUND, - proxy: &model.Proxy{Type: model.SidecarProxy, ConfigNamespace: "not-default"}, - output: sidecarInboundOut, - }, - { - name: "sidecar inbound cluster patch with service name", - input: sidecarInboundServiceIn, - patchContext: networking.EnvoyFilter_SIDECAR_INBOUND, - proxy: &model.Proxy{Type: model.SidecarProxy, ConfigNamespace: "not-default"}, - host: "service.servicens", - output: sidecarInboundServiceOut, - }, - { - name: "gateway cds patch", - input: gatewayInput, - patchContext: networking.EnvoyFilter_GATEWAY, - proxy: &model.Proxy{Type: model.Router, ConfigNamespace: "not-default"}, - output: gatewayOutput, - }, - } - - serviceDiscovery := memory.NewServiceDiscovery() - env := newTestEnvironment(serviceDiscovery, testMesh, buildEnvoyFilterConfigStore(configPatches)) - push := model.NewPushContext() - push.InitContext(env, nil, nil) - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - efw := push.EnvoyFilters(tc.proxy) - output := []*cluster.Cluster{} - for _, c := range tc.input { - if ShouldKeepCluster(tc.patchContext, efw, c, []host.Name{host.Name(tc.host)}) { - pc := ApplyClusterMerge(tc.patchContext, efw, c, []host.Name{host.Name(tc.host)}) - output = append(output, pc) - } - } - output = append(output, InsertedClusters(tc.patchContext, efw)...) - if diff := cmp.Diff(tc.output, output, protocmp.Transform()); diff != "" { - t.Errorf("%s mismatch (-want +got):\n%s", tc.name, diff) - } - }) - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/envoyfilter/extension_configuration_patch.go b/pilot/pkg/networking/core/v1alpha3/envoyfilter/extension_configuration_patch.go deleted file mode 100644 index df1e688d9..000000000 --- a/pilot/pkg/networking/core/v1alpha3/envoyfilter/extension_configuration_patch.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package envoyfilter - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - "google.golang.org/protobuf/proto" - networking "istio.io/api/networking/v1alpha3" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// InsertedExtensionConfigurations returns extension configurations added via EnvoyFilter. -func InsertedExtensionConfigurations(efw *model.EnvoyFilterWrapper, names []string) []*core.TypedExtensionConfig { - result := make([]*core.TypedExtensionConfig, 0) - if efw == nil { - return result - } - hasName := sets.New(names...) - for _, p := range efw.Patches[networking.EnvoyFilter_EXTENSION_CONFIG] { - if p.Operation != networking.EnvoyFilter_Patch_ADD { - continue - } - ec, ok := p.Value.(*core.TypedExtensionConfig) - if !ok { - log.Errorf("extension config patch %+v does not match TypeExtensionConfig type", p.Value) - continue - } - if hasName.Contains(ec.GetName()) { - result = append(result, proto.Clone(p.Value).(*core.TypedExtensionConfig)) - } - } - return result -} diff --git a/pilot/pkg/networking/core/v1alpha3/envoyfilter/extension_configuration_patch_test.go b/pilot/pkg/networking/core/v1alpha3/envoyfilter/extension_configuration_patch_test.go deleted file mode 100644 index 6980f2bb1..000000000 --- a/pilot/pkg/networking/core/v1alpha3/envoyfilter/extension_configuration_patch_test.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package envoyfilter - -import ( - "reflect" - "testing" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/memory" -) - -func TestInsertedExtensionConfig(t *testing.T) { - configPatches := []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_EXTENSION_CONFIG, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_ADD, - Value: buildPatchStruct(`{"name":"add-extension-config1"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_EXTENSION_CONFIG, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_ADD, - Value: buildPatchStruct(`{"name":"add-extension-config2"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_EXTENSION_CONFIG, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(`{"name":"merge-extension-config"}`), - }, - }, - } - - testCases := []struct { - name string - requestedNames []string - wantExtensionConfig []*core.TypedExtensionConfig - }{ - { - name: "add extension config", - requestedNames: []string{"add-extension-config1", "add-extension-config2"}, - wantExtensionConfig: []*core.TypedExtensionConfig{ - { - Name: "add-extension-config1", - }, - { - Name: "add-extension-config2", - }, - }, - }, - { - name: "miss extension config", - requestedNames: []string{"random-extension-config"}, - wantExtensionConfig: []*core.TypedExtensionConfig{}, - }, - { - name: "only add extension config", - requestedNames: []string{"merge-extension-config"}, - wantExtensionConfig: []*core.TypedExtensionConfig{}, - }, - } - - serviceDiscovery := memory.NewServiceDiscovery() - env := newTestEnvironment(serviceDiscovery, testMesh, buildEnvoyFilterConfigStore(configPatches)) - push := model.NewPushContext() - push.InitContext(env, nil, nil) - - for _, c := range testCases { - t.Run(c.name, func(t *testing.T) { - gotConfigs := InsertedExtensionConfigurations(push.EnvoyFilters(&model.Proxy{Type: model.SidecarProxy, ConfigNamespace: "not-default"}), - c.requestedNames) - if len(gotConfigs) != len(c.wantExtensionConfig) { - t.Fatalf("number of extension config got %v want %v", len(gotConfigs), len(c.wantExtensionConfig)) - } - for i, gc := range gotConfigs { - if !reflect.DeepEqual(gc, c.wantExtensionConfig[i]) { - t.Errorf("extension config %d got %v want %v", i, gc, c.wantExtensionConfig[i]) - } - } - }) - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/envoyfilter/leak_test.go b/pilot/pkg/networking/core/v1alpha3/envoyfilter/leak_test.go deleted file mode 100644 index 905e83a17..000000000 --- a/pilot/pkg/networking/core/v1alpha3/envoyfilter/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package envoyfilter - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/networking/core/v1alpha3/envoyfilter/listener_patch.go b/pilot/pkg/networking/core/v1alpha3/envoyfilter/listener_patch.go deleted file mode 100644 index fd813f659..000000000 --- a/pilot/pkg/networking/core/v1alpha3/envoyfilter/listener_patch.go +++ /dev/null @@ -1,742 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package envoyfilter - -import ( - "fmt" -) - -import ( - xdslistener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "google.golang.org/protobuf/proto" - any "google.golang.org/protobuf/types/known/anypb" - networking "istio.io/api/networking/v1alpha3" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/util/runtime" - "github.com/apache/dubbo-go-pixiu/pkg/config/xds" - "github.com/apache/dubbo-go-pixiu/pkg/proto/merge" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// ApplyListenerPatches applies patches to LDS output -func ApplyListenerPatches( - patchContext networking.EnvoyFilter_PatchContext, - efw *model.EnvoyFilterWrapper, - listeners []*xdslistener.Listener, - skipAdds bool) (out []*xdslistener.Listener) { - defer runtime.HandleCrash(runtime.LogPanic, func(interface{}) { - IncrementEnvoyFilterErrorMetric(Listener) - log.Errorf("listeners patch caused panic, so the patches did not take effect") - }) - // In case the patches cause panic, use the listeners generated before to reduce the influence. - out = listeners - - if efw == nil { - return - } - - return patchListeners(patchContext, efw, listeners, skipAdds) -} - -func patchListeners( - patchContext networking.EnvoyFilter_PatchContext, - efw *model.EnvoyFilterWrapper, - listeners []*xdslistener.Listener, - skipAdds bool) []*xdslistener.Listener { - listenersRemoved := false - - // do all the changes for a single envoy filter crd object. [including adds] - // then move on to the next one - - // only removes/merges plus next level object operations [add/remove/merge] - for _, listener := range listeners { - if listener.Name == "" { - // removed by another op - continue - } - patchListener(patchContext, efw.Patches, listener, &listenersRemoved) - } - // adds at listener level if enabled - if !skipAdds { - for _, lp := range efw.Patches[networking.EnvoyFilter_LISTENER] { - if lp.Operation == networking.EnvoyFilter_Patch_ADD { - // If listener ADD patch does not specify a patch context, only add for sidecar outbound and gateway. - if lp.Match.Context == networking.EnvoyFilter_ANY && patchContext != networking.EnvoyFilter_SIDECAR_OUTBOUND && - patchContext != networking.EnvoyFilter_GATEWAY { - continue - } - if !commonConditionMatch(patchContext, lp) { - IncrementEnvoyFilterMetric(lp.Key(), Listener, false) - continue - } - // clone before append. Otherwise, subsequent operations on this listener will corrupt - // the master value stored in CP. - listeners = append(listeners, proto.Clone(lp.Value).(*xdslistener.Listener)) - IncrementEnvoyFilterMetric(lp.Key(), Listener, true) - } - } - } - if listenersRemoved { - tempArray := make([]*xdslistener.Listener, 0, len(listeners)) - for _, l := range listeners { - if l.Name != "" { - tempArray = append(tempArray, l) - } - } - return tempArray - } - return listeners -} - -func patchListener(patchContext networking.EnvoyFilter_PatchContext, - patches map[networking.EnvoyFilter_ApplyTo][]*model.EnvoyFilterConfigPatchWrapper, - listener *xdslistener.Listener, listenersRemoved *bool) { - for _, lp := range patches[networking.EnvoyFilter_LISTENER] { - if !commonConditionMatch(patchContext, lp) || - !listenerMatch(listener, lp) { - IncrementEnvoyFilterMetric(lp.Key(), Listener, false) - continue - } - IncrementEnvoyFilterMetric(lp.Key(), Listener, true) - if lp.Operation == networking.EnvoyFilter_Patch_REMOVE { - listener.Name = "" - *listenersRemoved = true - // terminate the function here as we have nothing more do to for this listener - return - } else if lp.Operation == networking.EnvoyFilter_Patch_MERGE { - merge.Merge(listener, lp.Value) - } - } - patchFilterChains(patchContext, patches, listener) -} - -func patchFilterChains(patchContext networking.EnvoyFilter_PatchContext, - patches map[networking.EnvoyFilter_ApplyTo][]*model.EnvoyFilterConfigPatchWrapper, - listener *xdslistener.Listener) { - filterChainsRemoved := false - for i, fc := range listener.FilterChains { - if fc.Filters == nil { - continue - } - patchFilterChain(patchContext, patches, listener, listener.FilterChains[i], &filterChainsRemoved) - } - if fc := listener.GetDefaultFilterChain(); fc.GetFilters() != nil { - removed := false - patchFilterChain(patchContext, patches, listener, fc, &removed) - if removed { - listener.DefaultFilterChain = nil - } - } - for _, lp := range patches[networking.EnvoyFilter_FILTER_CHAIN] { - if lp.Operation == networking.EnvoyFilter_Patch_ADD { - if !commonConditionMatch(patchContext, lp) || - !listenerMatch(listener, lp) { - IncrementEnvoyFilterMetric(lp.Key(), FilterChain, false) - continue - } - IncrementEnvoyFilterMetric(lp.Key(), FilterChain, true) - listener.FilterChains = append(listener.FilterChains, proto.Clone(lp.Value).(*xdslistener.FilterChain)) - } - } - if filterChainsRemoved { - tempArray := make([]*xdslistener.FilterChain, 0, len(listener.FilterChains)) - for _, fc := range listener.FilterChains { - if fc.Filters != nil { - tempArray = append(tempArray, fc) - } - } - listener.FilterChains = tempArray - } -} - -func patchFilterChain(patchContext networking.EnvoyFilter_PatchContext, - patches map[networking.EnvoyFilter_ApplyTo][]*model.EnvoyFilterConfigPatchWrapper, - listener *xdslistener.Listener, - fc *xdslistener.FilterChain, filterChainRemoved *bool) { - for _, lp := range patches[networking.EnvoyFilter_FILTER_CHAIN] { - if !commonConditionMatch(patchContext, lp) || - !listenerMatch(listener, lp) || - !filterChainMatch(listener, fc, lp) { - IncrementEnvoyFilterMetric(lp.Key(), FilterChain, false) - continue - } - IncrementEnvoyFilterMetric(lp.Key(), FilterChain, true) - if lp.Operation == networking.EnvoyFilter_Patch_REMOVE { - fc.Filters = nil - *filterChainRemoved = true - // nothing more to do in other patches as we removed this filter chain - return - } else if lp.Operation == networking.EnvoyFilter_Patch_MERGE { - merged, err := mergeTransportSocketListener(fc, lp) - if err != nil { - log.Debugf("merge of transport socket failed for listener: %v", err) - continue - } - if !merged { - merge.Merge(fc, lp.Value) - } - } - } - patchNetworkFilters(patchContext, patches, listener, fc) -} - -// Test if the patch contains a config for TransportSocket -// Returns a boolean indicating if the merge was handled by this function; if false, it should still be called -// outside of this function. -func mergeTransportSocketListener(fc *xdslistener.FilterChain, lp *model.EnvoyFilterConfigPatchWrapper) (merged bool, err error) { - lpValueCast, ok := (lp.Value).(*xdslistener.FilterChain) - if !ok { - return false, fmt.Errorf("cast of cp.Value failed: %v", ok) - } - - // Test if the patch contains a config for TransportSocket - applyPatch := false - if lpValueCast.GetTransportSocket() != nil { - if fc.GetTransportSocket() == nil { - // There is no existing filter chain, we will add it outside this function; report back that we did not merge. - return false, nil - } - // Test if the listener contains a config for TransportSocket - applyPatch = fc.GetTransportSocket() != nil && lpValueCast.GetTransportSocket().Name == fc.GetTransportSocket().Name - } else { - return false, nil - } - - if applyPatch { - // Merge the patch and the listener at a lower level - dstListener := fc.GetTransportSocket().GetTypedConfig() - srcPatch := lpValueCast.GetTransportSocket().GetTypedConfig() - - if dstListener != nil && srcPatch != nil { - - retVal, errMerge := util.MergeAnyWithAny(dstListener, srcPatch) - if errMerge != nil { - return false, fmt.Errorf("function mergeAnyWithAny failed for doFilterChainOperation: %v", errMerge) - } - - // Merge the above result with the whole listener - merge.Merge(dstListener, retVal) - } - } - // If we already applied the patch, we skip merge.Merge() in the outer function - return applyPatch, nil -} - -func patchNetworkFilters(patchContext networking.EnvoyFilter_PatchContext, - patches map[networking.EnvoyFilter_ApplyTo][]*model.EnvoyFilterConfigPatchWrapper, - listener *xdslistener.Listener, fc *xdslistener.FilterChain) { - for _, lp := range patches[networking.EnvoyFilter_NETWORK_FILTER] { - if !commonConditionMatch(patchContext, lp) || - !listenerMatch(listener, lp) || - !filterChainMatch(listener, fc, lp) { - IncrementEnvoyFilterMetric(lp.Key(), NetworkFilter, false) - continue - } - applied := false - if lp.Operation == networking.EnvoyFilter_Patch_ADD { - fc.Filters = append(fc.Filters, proto.Clone(lp.Value).(*xdslistener.Filter)) - applied = true - } else if lp.Operation == networking.EnvoyFilter_Patch_INSERT_FIRST { - fc.Filters = append([]*xdslistener.Filter{proto.Clone(lp.Value).(*xdslistener.Filter)}, fc.Filters...) - applied = true - } else if lp.Operation == networking.EnvoyFilter_Patch_INSERT_AFTER { - // Insert after without a filter match is same as ADD in the end - if !hasNetworkFilterMatch(lp) { - fc.Filters = append(fc.Filters, proto.Clone(lp.Value).(*xdslistener.Filter)) - continue - } - // find the matching filter first - insertPosition := -1 - for i := 0; i < len(fc.Filters); i++ { - if networkFilterMatch(fc.Filters[i], lp) { - insertPosition = i + 1 - break - } - } - - if insertPosition == -1 { - continue - } - applied = true - clonedVal := proto.Clone(lp.Value).(*xdslistener.Filter) - fc.Filters = append(fc.Filters, clonedVal) - if insertPosition < len(fc.Filters)-1 { - copy(fc.Filters[insertPosition+1:], fc.Filters[insertPosition:]) - fc.Filters[insertPosition] = clonedVal - } - } else if lp.Operation == networking.EnvoyFilter_Patch_INSERT_BEFORE { - // insert before without a filter match is same as insert in the beginning - if !hasNetworkFilterMatch(lp) { - fc.Filters = append([]*xdslistener.Filter{proto.Clone(lp.Value).(*xdslistener.Filter)}, fc.Filters...) - continue - } - // find the matching filter first - insertPosition := -1 - for i := 0; i < len(fc.Filters); i++ { - if networkFilterMatch(fc.Filters[i], lp) { - insertPosition = i - break - } - } - - // If matching filter is not found, then don't insert and continue. - if insertPosition == -1 { - continue - } - applied = true - clonedVal := proto.Clone(lp.Value).(*xdslistener.Filter) - fc.Filters = append(fc.Filters, clonedVal) - copy(fc.Filters[insertPosition+1:], fc.Filters[insertPosition:]) - fc.Filters[insertPosition] = clonedVal - } else if lp.Operation == networking.EnvoyFilter_Patch_REPLACE { - if !hasNetworkFilterMatch(lp) { - continue - } - // find the matching filter first - replacePosition := -1 - for i := 0; i < len(fc.Filters); i++ { - if networkFilterMatch(fc.Filters[i], lp) { - replacePosition = i - break - } - } - if replacePosition == -1 { - continue - } - applied = true - fc.Filters[replacePosition] = proto.Clone(lp.Value).(*xdslistener.Filter) - } - IncrementEnvoyFilterMetric(lp.Key(), NetworkFilter, applied) - } - removedFilters := sets.New() - for i, filter := range fc.Filters { - if patchNetworkFilter(patchContext, patches, listener, fc, fc.Filters[i]) { - removedFilters.Insert(filter.Name) - } - } - if len(removedFilters) > 0 { - tempArray := make([]*xdslistener.Filter, 0, len(fc.Filters)-len(removedFilters)) - for _, filter := range fc.Filters { - if removedFilters.Contains(filter.Name) { - continue - } - tempArray = append(tempArray, filter) - } - fc.Filters = tempArray - } -} - -// patchNetworkFilter patches passed in filter if it is MERGE operation. -// The return value indicates whether the filter has been removed for REMOVE operations. -func patchNetworkFilter(patchContext networking.EnvoyFilter_PatchContext, - patches map[networking.EnvoyFilter_ApplyTo][]*model.EnvoyFilterConfigPatchWrapper, - listener *xdslistener.Listener, fc *xdslistener.FilterChain, - filter *xdslistener.Filter) bool { - for _, lp := range patches[networking.EnvoyFilter_NETWORK_FILTER] { - if !commonConditionMatch(patchContext, lp) || - !listenerMatch(listener, lp) || - !filterChainMatch(listener, fc, lp) || - !networkFilterMatch(filter, lp) { - IncrementEnvoyFilterMetric(lp.Key(), NetworkFilter, false) - continue - } - if lp.Operation == networking.EnvoyFilter_Patch_REMOVE { - return true - } else if lp.Operation == networking.EnvoyFilter_Patch_MERGE { - // proto merge doesn't work well when merging two filters with ANY typed configs - // especially when the incoming cp.Value is a struct that could contain the json config - // of an ANY typed filter. So convert our filter's typed config to Struct (retaining the any - // typed output of json) - if filter.GetTypedConfig() == nil { - // TODO(rshriram): fixme - // skip this op as we would possibly have to do a merge of Any with struct - // which doesn't seem to work well. - continue - } - userFilter := lp.Value.(*xdslistener.Filter) - var err error - // we need to be able to overwrite filter names or simply empty out a filter's configs - // as they could be supplied through per route filter configs - filterName := filter.Name - if userFilter.Name != "" { - filterName = userFilter.Name - } - var retVal *any.Any - if userFilter.GetTypedConfig() != nil { - IncrementEnvoyFilterMetric(lp.Key(), NetworkFilter, true) - // user has any typed struct - // The type may not match up exactly. For example, if we use v2 internally but they use v3. - // Assuming they are not using deprecated/new fields, we can safely swap out the TypeUrl - // If we did not do this, merge.Merge below will panic (which is recovered), so even though this - // is not 100% reliable its better than doing nothing - if userFilter.GetTypedConfig().TypeUrl != filter.GetTypedConfig().TypeUrl { - userFilter.ConfigType.(*xdslistener.Filter_TypedConfig).TypedConfig.TypeUrl = filter.GetTypedConfig().TypeUrl - } - if retVal, err = util.MergeAnyWithAny(filter.GetTypedConfig(), userFilter.GetTypedConfig()); err != nil { - retVal = filter.GetTypedConfig() - } - } - filter.Name = toCanonicalName(filterName) - if retVal != nil { - filter.ConfigType = &xdslistener.Filter_TypedConfig{TypedConfig: retVal} - } - } - } - if filter.Name == wellknown.HTTPConnectionManager { - patchHTTPFilters(patchContext, patches, listener, fc, filter) - } - return false -} - -func patchHTTPFilters(patchContext networking.EnvoyFilter_PatchContext, - patches map[networking.EnvoyFilter_ApplyTo][]*model.EnvoyFilterConfigPatchWrapper, - listener *xdslistener.Listener, fc *xdslistener.FilterChain, filter *xdslistener.Filter) { - httpconn := &hcm.HttpConnectionManager{} - if filter.GetTypedConfig() != nil { - if err := filter.GetTypedConfig().UnmarshalTo(httpconn); err != nil { - return - // todo: figure out a non noisy logging option here - // as this loop will be called very frequently - } - } - for _, lp := range patches[networking.EnvoyFilter_HTTP_FILTER] { - applied := false - if !commonConditionMatch(patchContext, lp) || - !listenerMatch(listener, lp) || - !filterChainMatch(listener, fc, lp) || - !networkFilterMatch(filter, lp) { - IncrementEnvoyFilterMetric(lp.Key(), HttpFilter, false) - continue - } - if lp.Operation == networking.EnvoyFilter_Patch_ADD { - applied = true - httpconn.HttpFilters = append(httpconn.HttpFilters, proto.Clone(lp.Value).(*hcm.HttpFilter)) - } else if lp.Operation == networking.EnvoyFilter_Patch_INSERT_FIRST { - httpconn.HttpFilters = append([]*hcm.HttpFilter{proto.Clone(lp.Value).(*hcm.HttpFilter)}, httpconn.HttpFilters...) - } else if lp.Operation == networking.EnvoyFilter_Patch_INSERT_AFTER { - // Insert after without a filter match is same as ADD in the end - if !hasHTTPFilterMatch(lp) { - httpconn.HttpFilters = append(httpconn.HttpFilters, proto.Clone(lp.Value).(*hcm.HttpFilter)) - continue - } - - // find the matching filter first - insertPosition := -1 - for i := 0; i < len(httpconn.HttpFilters); i++ { - if httpFilterMatch(httpconn.HttpFilters[i], lp) { - insertPosition = i + 1 - break - } - } - - if insertPosition == -1 { - continue - } - applied = true - clonedVal := proto.Clone(lp.Value).(*hcm.HttpFilter) - httpconn.HttpFilters = append(httpconn.HttpFilters, clonedVal) - if insertPosition < len(httpconn.HttpFilters)-1 { - copy(httpconn.HttpFilters[insertPosition+1:], httpconn.HttpFilters[insertPosition:]) - httpconn.HttpFilters[insertPosition] = clonedVal - } - } else if lp.Operation == networking.EnvoyFilter_Patch_INSERT_BEFORE { - // insert before without a filter match is same as insert in the beginning - if !hasHTTPFilterMatch(lp) { - httpconn.HttpFilters = append([]*hcm.HttpFilter{proto.Clone(lp.Value).(*hcm.HttpFilter)}, httpconn.HttpFilters...) - continue - } - - // find the matching filter first - insertPosition := -1 - for i := 0; i < len(httpconn.HttpFilters); i++ { - if httpFilterMatch(httpconn.HttpFilters[i], lp) { - insertPosition = i - break - } - } - - if insertPosition == -1 { - continue - } - applied = true - clonedVal := proto.Clone(lp.Value).(*hcm.HttpFilter) - httpconn.HttpFilters = append(httpconn.HttpFilters, clonedVal) - copy(httpconn.HttpFilters[insertPosition+1:], httpconn.HttpFilters[insertPosition:]) - httpconn.HttpFilters[insertPosition] = clonedVal - } else if lp.Operation == networking.EnvoyFilter_Patch_REPLACE { - if !hasHTTPFilterMatch(lp) { - continue - } - - // find the matching filter first - replacePosition := -1 - for i := 0; i < len(httpconn.HttpFilters); i++ { - if httpFilterMatch(httpconn.HttpFilters[i], lp) { - replacePosition = i - break - } - } - if replacePosition == -1 { - log.Debugf("EnvoyFilter patch %v is not applied because no matching HTTP filter found.", lp) - continue - } - applied = true - clonedVal := proto.Clone(lp.Value).(*hcm.HttpFilter) - httpconn.HttpFilters[replacePosition] = clonedVal - } - IncrementEnvoyFilterMetric(lp.Key(), HttpFilter, applied) - } - removedFilters := sets.Set{} - for _, httpFilter := range httpconn.HttpFilters { - if patchHTTPFilter(patchContext, patches, listener, fc, filter, httpFilter) { - removedFilters.Insert(httpFilter.Name) - } - } - if len(removedFilters) > 0 { - tempArray := make([]*hcm.HttpFilter, 0, len(httpconn.HttpFilters)-len(removedFilters)) - for _, filter := range httpconn.HttpFilters { - if removedFilters.Contains(filter.Name) { - continue - } - tempArray = append(tempArray, filter) - } - httpconn.HttpFilters = tempArray - } - if filter.GetTypedConfig() != nil { - // convert to any type - filter.ConfigType = &xdslistener.Filter_TypedConfig{TypedConfig: util.MessageToAny(httpconn)} - } -} - -// patchHTTPFilter patches passed in filter if it is MERGE operation. -// The return value indicates whether the filter has been removed for REMOVE operations. -func patchHTTPFilter(patchContext networking.EnvoyFilter_PatchContext, - patches map[networking.EnvoyFilter_ApplyTo][]*model.EnvoyFilterConfigPatchWrapper, - listener *xdslistener.Listener, fc *xdslistener.FilterChain, filter *xdslistener.Filter, - httpFilter *hcm.HttpFilter) bool { - for _, lp := range patches[networking.EnvoyFilter_HTTP_FILTER] { - applied := false - if !commonConditionMatch(patchContext, lp) || - !listenerMatch(listener, lp) || - !filterChainMatch(listener, fc, lp) || - !networkFilterMatch(filter, lp) || - !httpFilterMatch(httpFilter, lp) { - IncrementEnvoyFilterMetric(lp.Key(), HttpFilter, applied) - continue - } - if lp.Operation == networking.EnvoyFilter_Patch_REMOVE { - return true - } else if lp.Operation == networking.EnvoyFilter_Patch_MERGE { - // proto merge doesn't work well when merging two filters with ANY typed configs - // especially when the incoming cp.Value is a struct that could contain the json config - // of an ANY typed filter. So convert our filter's typed config to Struct (retaining the any - // typed output of json) - if httpFilter.GetTypedConfig() == nil { - // TODO(rshriram): fixme - // skip this op as we would possibly have to do a merge of Any with struct - // which doesn't seem to work well. - continue - } - userHTTPFilter := lp.Value.(*hcm.HttpFilter) - var err error - // we need to be able to overwrite filter names or simply empty out a filter's configs - // as they could be supplied through per route filter configs - httpFilterName := httpFilter.Name - if userHTTPFilter.Name != "" { - httpFilterName = userHTTPFilter.Name - } - var retVal *any.Any - if userHTTPFilter.GetTypedConfig() != nil { - // user has any typed struct - // The type may not match up exactly. For example, if we use v2 internally but they use v3. - // Assuming they are not using deprecated/new fields, we can safely swap out the TypeUrl - // If we did not do this, merge.Merge below will panic (which is recovered), so even though this - // is not 100% reliable its better than doing nothing - if userHTTPFilter.GetTypedConfig().TypeUrl != httpFilter.GetTypedConfig().TypeUrl { - userHTTPFilter.ConfigType.(*hcm.HttpFilter_TypedConfig).TypedConfig.TypeUrl = httpFilter.GetTypedConfig().TypeUrl - } - if retVal, err = util.MergeAnyWithAny(httpFilter.GetTypedConfig(), userHTTPFilter.GetTypedConfig()); err != nil { - retVal = httpFilter.GetTypedConfig() - } - } - applied = true - httpFilter.Name = toCanonicalName(httpFilterName) - if retVal != nil { - httpFilter.ConfigType = &hcm.HttpFilter_TypedConfig{TypedConfig: retVal} - } - } - IncrementEnvoyFilterMetric(lp.Key(), HttpFilter, applied) - } - return false -} - -func listenerMatch(listener *xdslistener.Listener, lp *model.EnvoyFilterConfigPatchWrapper) bool { - lMatch := lp.Match.GetListener() - if lMatch == nil { - return true - } - - if lMatch.Name != "" && lMatch.Name != listener.Name { - return false - } - - // skip listener port check for special virtual inbound and outbound listeners - // to support portNumber listener filter field within those special listeners as well - if lp.ApplyTo != networking.EnvoyFilter_LISTENER && - (listener.Name == model.VirtualInboundListenerName || listener.Name == model.VirtualOutboundListenerName) { - return true - } - - // FIXME: Ports on a listener can be 0. the API only takes uint32 for ports - // We should either make that field in API as a wrapper type or switch to int - if lMatch.PortNumber != 0 { - sockAddr := listener.Address.GetSocketAddress() - if sockAddr == nil || sockAddr.GetPortValue() != lMatch.PortNumber { - return false - } - } - - return true -} - -// We assume that the parent listener has already been matched -func filterChainMatch(listener *xdslistener.Listener, fc *xdslistener.FilterChain, lp *model.EnvoyFilterConfigPatchWrapper) bool { - lMatch := lp.Match.GetListener() - if lMatch == nil { - return true - } - - isVirtual := listener.Name == model.VirtualInboundListenerName || listener.Name == model.VirtualOutboundListenerName - // We only do this for virtual listeners, which will move the listener port into a FCM. For non-virtual listeners, - // we will handle this in the proper listener match. - if isVirtual && lMatch.GetPortNumber() > 0 && fc.GetFilterChainMatch().GetDestinationPort().GetValue() != lMatch.GetPortNumber() { - return false - } - - match := lMatch.FilterChain - if match == nil { - return true - } - if match.Name != "" { - if match.Name != fc.Name { - return false - } - } - if match.Sni != "" { - if fc.FilterChainMatch == nil || len(fc.FilterChainMatch.ServerNames) == 0 { - return false - } - sniMatched := false - for _, sni := range fc.FilterChainMatch.ServerNames { - if sni == match.Sni { - sniMatched = true - break - } - } - if !sniMatched { - return false - } - } - - if match.TransportProtocol != "" { - if fc.FilterChainMatch == nil || fc.FilterChainMatch.TransportProtocol != match.TransportProtocol { - return false - } - } - - // check match for destination port within the FilterChainMatch - if match.DestinationPort > 0 { - if fc.FilterChainMatch == nil || fc.FilterChainMatch.DestinationPort == nil { - return false - } else if fc.FilterChainMatch.DestinationPort.Value != match.DestinationPort { - return false - } - } - return true -} - -func hasNetworkFilterMatch(lp *model.EnvoyFilterConfigPatchWrapper) bool { - lMatch := lp.Match.GetListener() - if lMatch == nil { - return false - } - - fcMatch := lMatch.FilterChain - if fcMatch == nil { - return false - } - - return fcMatch.Filter != nil -} - -// We assume that the parent listener and filter chain have already been matched -func networkFilterMatch(filter *xdslistener.Filter, cp *model.EnvoyFilterConfigPatchWrapper) bool { - if !hasNetworkFilterMatch(cp) { - return true - } - - return nameMatches(cp.Match.GetListener().FilterChain.Filter.Name, filter.Name) -} - -func hasHTTPFilterMatch(lp *model.EnvoyFilterConfigPatchWrapper) bool { - if !hasNetworkFilterMatch(lp) { - return false - } - - match := lp.Match.GetListener().FilterChain.Filter.SubFilter - return match != nil -} - -// We assume that the parent listener and filter chain, and network filter have already been matched -func httpFilterMatch(filter *hcm.HttpFilter, lp *model.EnvoyFilterConfigPatchWrapper) bool { - if !hasHTTPFilterMatch(lp) { - return true - } - - match := lp.Match.GetListener().FilterChain.Filter.SubFilter - - return nameMatches(match.Name, filter.Name) -} - -func patchContextMatch(patchContext networking.EnvoyFilter_PatchContext, - lp *model.EnvoyFilterConfigPatchWrapper) bool { - return lp.Match.Context == patchContext || lp.Match.Context == networking.EnvoyFilter_ANY -} - -func commonConditionMatch(patchContext networking.EnvoyFilter_PatchContext, - lp *model.EnvoyFilterConfigPatchWrapper) bool { - return patchContextMatch(patchContext, lp) -} - -// toCanonicalName converts a deprecated filter name to the replacement, if present. Otherwise, the -// same name is returned. -func toCanonicalName(name string) string { - if nn, f := xds.ReverseDeprecatedFilterNames[name]; f { - return nn - } - return name -} - -// nameMatches compares two filter names, matching even if a deprecated filter name is used. -func nameMatches(matchName, filterName string) bool { - return matchName == filterName || matchName == xds.DeprecatedFilterNames[filterName] -} diff --git a/pilot/pkg/networking/core/v1alpha3/envoyfilter/listener_patch_test.go b/pilot/pkg/networking/core/v1alpha3/envoyfilter/listener_patch_test.go deleted file mode 100644 index 125f5c3b4..000000000 --- a/pilot/pkg/networking/core/v1alpha3/envoyfilter/listener_patch_test.go +++ /dev/null @@ -1,2010 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package envoyfilter - -import ( - "fmt" - "os" - "path/filepath" - "strings" - "testing" - "time" -) - -import ( - udpa "github.com/cncf/xds/go/udpa/type/v1" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - fault "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/fault/v3" - http_conn "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - redis "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/redis_proxy/v3" - tcp_proxy "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3" - tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "github.com/golang/protobuf/jsonpb" // nolint: staticcheck - "github.com/golang/protobuf/ptypes/wrappers" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/testing/protocmp" - "google.golang.org/protobuf/types/known/anypb" - "google.golang.org/protobuf/types/known/durationpb" - "google.golang.org/protobuf/types/known/structpb" - "google.golang.org/protobuf/types/known/wrapperspb" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/crd" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - memregistry "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/memory" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - istio_proto "github.com/apache/dubbo-go-pixiu/pkg/proto" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -var testMesh = &meshconfig.MeshConfig{ - ConnectTimeout: &durationpb.Duration{ - Seconds: 10, - Nanos: 1, - }, -} - -func buildEnvoyFilterConfigStore(configPatches []*networking.EnvoyFilter_EnvoyConfigObjectPatch) model.ConfigStore { - store := model.MakeIstioStore(memory.Make(collections.Pilot)) - - for i, cp := range configPatches { - store.Create(config.Config{ - Meta: config.Meta{ - Name: fmt.Sprintf("test-envoyfilter-%d", i), - Namespace: "not-default", - GroupVersionKind: gvk.EnvoyFilter, - }, - Spec: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{cp}, - }, - }) - } - return store -} - -func buildPatchStruct(config string) *structpb.Struct { - val := &structpb.Struct{} - _ = jsonpb.Unmarshal(strings.NewReader(config), val) - return val -} - -// nolint: unparam -func buildGolangPatchStruct(config string) *structpb.Struct { - val := &structpb.Struct{} - _ = protomarshal.Unmarshal([]byte(config), val) - return val -} - -func newTestEnvironment(serviceDiscovery model.ServiceDiscovery, meshConfig *meshconfig.MeshConfig, - configStore model.ConfigStore) *model.Environment { - e := &model.Environment{ - ServiceDiscovery: serviceDiscovery, - ConfigStore: configStore, - Watcher: mesh.NewFixedWatcher(meshConfig), - } - - e.PushContext = model.NewPushContext() - e.Init() - _ = e.PushContext.InitContext(e, nil, nil) - - return e -} - -func TestApplyListenerPatches(t *testing.T) { - configPatches := []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_LISTENER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - Proxy: &networking.EnvoyFilter_ProxyMatch{ - Metadata: map[string]string{"foo": "sidecar"}, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_ADD, - Value: buildPatchStruct(`{"name":"new-outbound-listener1"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_NETWORK_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 12345, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{Name: "filter1"}, - }, - }, - }, - Proxy: &networking.EnvoyFilter_ProxyMatch{ - ProxyVersion: `^1\.[2-9](.*?)$`, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_INSERT_BEFORE, - Value: buildPatchStruct(`{"name":"filter0"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_NETWORK_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 12345, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{Name: "filter1"}, - }, - }, - }, - Proxy: &networking.EnvoyFilter_ProxyMatch{ - ProxyVersion: `^1\.[5-9](.*?)$`, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_INSERT_FIRST, - Value: buildPatchStruct(`{"name":"filter0"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_NETWORK_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 12345, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{Name: "filter2"}, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - { - ApplyTo: networking.EnvoyFilter_LISTENER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 12345, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - { - ApplyTo: networking.EnvoyFilter_LISTENER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 80, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(`{listener_filters: nil}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_FILTER_CHAIN, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 80, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{TransportProtocol: "tls"}, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - { - ApplyTo: networking.EnvoyFilter_LISTENER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_GATEWAY, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 80, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(`{"listener_filters": [{"name":"foo"}]}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_FILTER_CHAIN, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_GATEWAY, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 80, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Sni: "*.foo.com", - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(`{"filter_chain_match": { "server_names": ["foo.com"] }}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_NETWORK_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{Name: wellknown.RedisProxy}, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(` -{"name": "envoy.filters.network.redis_proxy", -"typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.redis_proxy.v3.RedisProxy", - "settings": {"op_timeout": "0.2s"} -} -}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_HTTP_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_GATEWAY, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 80, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Sni: "*.foo.com", - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: wellknown.HTTPConnectionManager, - SubFilter: &networking.EnvoyFilter_ListenerMatch_SubFilterMatch{Name: "http-filter2"}, - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_INSERT_AFTER, - Value: buildPatchStruct(`{"name": "http-filter3"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_HTTP_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 80, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: wellknown.HTTPConnectionManager, - SubFilter: &networking.EnvoyFilter_ListenerMatch_SubFilterMatch{Name: "http-filter2"}, - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_INSERT_BEFORE, - Value: buildPatchStruct(`{"name": "http-filter3"}`), - }, - }, - // Remove inline http filter patch - { - ApplyTo: networking.EnvoyFilter_HTTP_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: wellknown.HTTPConnectionManager, - SubFilter: &networking.EnvoyFilter_ListenerMatch_SubFilterMatch{Name: "http-filter3"}, - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_INSERT_BEFORE, - Value: buildPatchStruct(`{"name": "http-filter-to-be-removed2"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_HTTP_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 80, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: wellknown.HTTPConnectionManager, - SubFilter: &networking.EnvoyFilter_ListenerMatch_SubFilterMatch{Name: "http-filter-to-be-removed2"}, - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - { - ApplyTo: networking.EnvoyFilter_HTTP_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 80, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: wellknown.HTTPConnectionManager, - SubFilter: &networking.EnvoyFilter_ListenerMatch_SubFilterMatch{Name: "http-filter2"}, - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_INSERT_AFTER, - Value: buildPatchStruct(`{"name": "http-filter4"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_HTTP_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 80, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: wellknown.HTTPConnectionManager, - SubFilter: &networking.EnvoyFilter_ListenerMatch_SubFilterMatch{Name: "http-filter-to-be-removed"}, - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - // Merge v3 any with v2 any - { - ApplyTo: networking.EnvoyFilter_HTTP_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 80, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: wellknown.HTTPConnectionManager, - SubFilter: &networking.EnvoyFilter_ListenerMatch_SubFilterMatch{Name: "envoy.fault"}, // Use deprecated name for test. - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(` -{"name": "envoy.filters.http.fault", -"typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault", - "downstreamNodes": ["foo"] -} -}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_HTTP_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 80, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: wellknown.HTTPConnectionManager, - SubFilter: &networking.EnvoyFilter_ListenerMatch_SubFilterMatch{Name: "http-filter2"}, - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_INSERT_FIRST, - Value: buildPatchStruct(`{"name": "http-filter0"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_HTTP_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 80, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: wellknown.HTTPConnectionManager, - SubFilter: &networking.EnvoyFilter_ListenerMatch_SubFilterMatch{ - Name: wellknown.Fault, - }, - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(`{"typed_config": { -"@type": "type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault", -"upstream_cluster": "scooby"}}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_NETWORK_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 80, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: wellknown.HTTPConnectionManager, - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(` -{"name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager", - "xffNumTrustedHops": "4" - } -}`), - }, - }, - // Ensure we can mix v3 patches with v2 internal - // Note that alwaysSetRequestIdInResponse is only present in v3 protos. It will be silently ignored - // as we are working in v2 protos internally - { - ApplyTo: networking.EnvoyFilter_NETWORK_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 80, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: "envoy.http_connection_manager", // Use deprecated name for test. - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(` -{"name": "envoy.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "mergeSlashes": true, - "alwaysSetRequestIdInResponse": true - } -}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_HTTP_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 80, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: "envoy.http_connection_manager", // Use deprecated name for test. - SubFilter: &networking.EnvoyFilter_ListenerMatch_SubFilterMatch{Name: "http-filter2"}, - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_INSERT_AFTER, - Value: buildPatchStruct(`{"name": "http-filter-5"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_NETWORK_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 6379, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: "filter1", - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REPLACE, - Value: buildPatchStruct(` -{"name": "envoy.redis_proxy", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.redis_proxy.v3.RedisProxy", - "stat_prefix": "redis_stats", - "prefix_routes": { - "catch_all_route": { - "cluster": "custom-redis-cluster" - } - } - } -}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_NETWORK_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 6381, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: "default-network-filter", - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REPLACE, - Value: buildPatchStruct(`{"name": "default-network-filter-replaced"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_NETWORK_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 6381, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: "default-network-filter-removed", - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - // This patch should not be applied because network filter name doesn't match - { - ApplyTo: networking.EnvoyFilter_NETWORK_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 6380, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: "network-filter-should-not-be-replaced-not-match", - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REPLACE, - Value: buildPatchStruct(`{"name": "network-filter-replaced-should-not-be-applied"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_HTTP_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - Name: "listener-http-filter-to-be-replaced", - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: wellknown.HTTPConnectionManager, - SubFilter: &networking.EnvoyFilter_ListenerMatch_SubFilterMatch{ - Name: "http-filter-to-be-replaced", - }, - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REPLACE, - Value: buildPatchStruct(`{"name": "http-filter-replaced"}`), - }, - }, - // This patch should not be applied because the subfilter name doesn't match - { - ApplyTo: networking.EnvoyFilter_HTTP_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - Name: "listener-http-filter-to-be-replaced-not-found", - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: wellknown.HTTPConnectionManager, - SubFilter: &networking.EnvoyFilter_ListenerMatch_SubFilterMatch{ - Name: "http-filter-should-not-be-replaced-not-match", - }, - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REPLACE, - Value: buildPatchStruct(`{"name": "http-filter-replaced-should-not-be-applied"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_NETWORK_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - Name: model.VirtualInboundListenerName, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - DestinationPort: 6380, - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: "network-filter-to-be-replaced", - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REPLACE, - Value: buildPatchStruct(` -{"name": "envoy.redis_proxy", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.redis_proxy.v3.RedisProxy", - "stat_prefix": "redis_stats", - "prefix_routes": { - "catch_all_route": { - "cluster": "custom-redis-cluster" - } - } - } -}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_FILTER_CHAIN, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 12345, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{TransportProtocol: "tls"}, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(` - {"transport_socket":{ - "name":"envoy.transport_sockets.tls", - "typed_config":{ - "@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext", - "common_tls_context":{ - "tls_params":{ - "tls_maximum_protocol_version":"TLSv1_3", - "tls_minimum_protocol_version":"TLSv1_2"}}}}}`), - }, - }, - // Patch custom TLS type - { - ApplyTo: networking.EnvoyFilter_FILTER_CHAIN, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 7777, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(` - {"transport_socket":{ - "name":"transport_sockets.alts", - "typed_config":{ - "@type":"type.googleapis.com/udpa.type.v1.TypedStruct", - "type_url": "type.googleapis.com/envoy.extensions.transport_sockets.alts.v3.Alts", - "value":{"handshaker_service":"1.2.3.4"}}}}`), - }, - }, - // Patch custom TLS type to a FC without TLS already set - { - ApplyTo: networking.EnvoyFilter_FILTER_CHAIN, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 7778, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(` - {"transport_socket":{ - "name":"transport_sockets.alts", - "typed_config":{ - "@type":"type.googleapis.com/udpa.type.v1.TypedStruct", - "type_url": "type.googleapis.com/envoy.extensions.transport_sockets.alts.v3.Alts", - "value":{"handshaker_service":"1.2.3.4"}}}}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_NETWORK_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - Name: model.VirtualInboundListenerName, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Name: "filter-chain-name-match", - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: "custom-network-filter-2", - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - // Remove inline network filter patch - { - ApplyTo: networking.EnvoyFilter_NETWORK_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - Name: model.VirtualInboundListenerName, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Name: "filter-chain-name-match", - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: "custom-network-filter-1", - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_INSERT_BEFORE, - Value: buildPatchStruct(`{"name":"network-filter-to-be-removed"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_NETWORK_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - Name: model.VirtualInboundListenerName, - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Name: "filter-chain-name-match", - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: "network-filter-to-be-removed", - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - // Add transport socket to virtual inbound. - { - ApplyTo: networking.EnvoyFilter_FILTER_CHAIN, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 80, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(` - {"transport_socket":{ - "name":"envoy.transport_sockets.tls", - "typed_config":{ - "@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext", - "common_tls_context":{ - "alpn_protocols": [ "h2-80", "http/1.1-80" ]}}}}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_FILTER_CHAIN, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 6380, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(` - {"transport_socket":{ - "name":"envoy.transport_sockets.tls", - "typed_config":{ - "@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext", - "common_tls_context":{ - "alpn_protocols": [ "h2-6380", "http/1.1-6380" ]}}}}`), - }, - }, - } - - sidecarOutboundIn := []*listener.Listener{ - { - Name: "12345", - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 12345, - }, - }, - }, - }, - FilterChains: []*listener.FilterChain{ - { - FilterChainMatch: &listener.FilterChainMatch{TransportProtocol: "tls"}, - TransportSocket: &core.TransportSocket{ - Name: "envoy.transport_sockets.tls", - ConfigType: &core.TransportSocket_TypedConfig{ - TypedConfig: util.MessageToAny(&tls.DownstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - EcdhCurves: []string{"X25519"}, - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_1, - }, - }, - }), - }, - }, - Filters: []*listener.Filter{{Name: "envoy.transport_sockets.tls"}}, - }, - { - Filters: []*listener.Filter{ - {Name: "filter1"}, - {Name: "filter2"}, - }, - }, - }, - }, - { - Name: "network-filter-to-be-replaced", - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 6379, - }, - }, - }, - }, - FilterChains: []*listener.FilterChain{ - { - Filters: []*listener.Filter{ - {Name: "filter1"}, - {Name: "filter2"}, - }, - }, - }, - }, - { - Name: "redis-proxy", - FilterChains: []*listener.FilterChain{ - { - FilterChainMatch: &listener.FilterChainMatch{ - DestinationPort: &wrapperspb.UInt32Value{ - Value: 9999, - }, - }, - Filters: []*listener.Filter{ - { - Name: wellknown.RedisProxy, - ConfigType: &listener.Filter_TypedConfig{ - TypedConfig: util.MessageToAny(&redis.RedisProxy{ - Settings: &redis.RedisProxy_ConnPoolSettings{ - OpTimeout: durationpb.New(time.Second * 5), - }, - }), - }, - }, - }, - }, - }, - }, - { - Name: "network-filter-to-be-replaced-not-found", - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 6380, - }, - }, - }, - }, - FilterChains: []*listener.FilterChain{ - { - Filters: []*listener.Filter{ - {Name: "network-filter-should-not-be-replaced"}, - }, - }, - }, - }, - { - Name: "default-filter-chain", - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 6381, - }, - }, - }, - }, - FilterChains: []*listener.FilterChain{ - { - Filters: []*listener.Filter{ - {Name: "network-filter"}, - }, - }, - }, - DefaultFilterChain: &listener.FilterChain{ - Filters: []*listener.Filter{ - {Name: "default-network-filter"}, - {Name: "default-network-filter-removed"}, - }, - }, - }, - { - Name: "another-listener", - }, - { - Name: "listener-http-filter-to-be-replaced", - FilterChains: []*listener.FilterChain{ - { - FilterChainMatch: &listener.FilterChainMatch{ - DestinationPort: &wrapperspb.UInt32Value{ - Value: 80, - }, - }, - Filters: []*listener.Filter{ - { - Name: wellknown.HTTPConnectionManager, - ConfigType: &listener.Filter_TypedConfig{ - TypedConfig: util.MessageToAny(&http_conn.HttpConnectionManager{ - HttpFilters: []*http_conn.HttpFilter{ - {Name: "http-filter-to-be-replaced"}, - {Name: "another-http-filter"}, - }, - }), - }, - }, - }, - }, - }, - }, - { - Name: "listener-http-filter-to-be-replaced-not-found", - FilterChains: []*listener.FilterChain{ - { - FilterChainMatch: &listener.FilterChainMatch{ - DestinationPort: &wrapperspb.UInt32Value{ - Value: 80, - }, - }, - Filters: []*listener.Filter{ - { - Name: wellknown.HTTPConnectionManager, - ConfigType: &listener.Filter_TypedConfig{ - TypedConfig: util.MessageToAny(&http_conn.HttpConnectionManager{ - HttpFilters: []*http_conn.HttpFilter{ - {Name: "http-filter-should-not-be-replaced"}, - {Name: "another-http-filter"}, - }, - }), - }, - }, - }, - }, - }, - }, - { - Name: "custom-tls-replacement", - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 7777, - }, - }, - }, - }, - FilterChains: []*listener.FilterChain{ - { - TransportSocket: &core.TransportSocket{ - Name: "envoy.transport_sockets.tls", - ConfigType: &core.TransportSocket_TypedConfig{ - TypedConfig: util.MessageToAny(&tls.DownstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{}, - }, - }), - }, - }, - Filters: []*listener.Filter{{Name: "filter"}}, - }, - }, - }, - { - Name: "custom-tls-addition", - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 7778, - }, - }, - }, - }, - FilterChains: []*listener.FilterChain{ - { - Filters: []*listener.Filter{{Name: "filter"}}, - }, - }, - }, - } - - sidecarOutboundOut := []*listener.Listener{ - { - Name: "12345", - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 12345, - }, - }, - }, - }, - FilterChains: []*listener.FilterChain{ - { - FilterChainMatch: &listener.FilterChainMatch{TransportProtocol: "tls"}, - TransportSocket: &core.TransportSocket{ - Name: "envoy.transport_sockets.tls", - ConfigType: &core.TransportSocket_TypedConfig{ - TypedConfig: util.MessageToAny(&tls.DownstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - EcdhCurves: []string{"X25519"}, - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - }, - }, - }), - }, - }, - Filters: []*listener.Filter{{Name: "envoy.transport_sockets.tls"}}, - }, - { - Filters: []*listener.Filter{ - {Name: "filter0"}, - {Name: "filter1"}, - }, - }, - }, - }, - { - Name: "network-filter-to-be-replaced", - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 6379, - }, - }, - }, - }, - FilterChains: []*listener.FilterChain{ - { - Filters: []*listener.Filter{ - { - Name: "envoy.redis_proxy", - ConfigType: &listener.Filter_TypedConfig{ - TypedConfig: util.MessageToAny(&redis.RedisProxy{ - StatPrefix: "redis_stats", - PrefixRoutes: &redis.RedisProxy_PrefixRoutes{ - CatchAllRoute: &redis.RedisProxy_PrefixRoutes_Route{ - Cluster: "custom-redis-cluster", - }, - }, - }), - }, - }, - {Name: "filter2"}, - }, - }, - }, - }, - { - Name: "redis-proxy", - FilterChains: []*listener.FilterChain{ - { - FilterChainMatch: &listener.FilterChainMatch{ - DestinationPort: &wrapperspb.UInt32Value{ - Value: 9999, - }, - }, - Filters: []*listener.Filter{ - { - Name: wellknown.RedisProxy, - ConfigType: &listener.Filter_TypedConfig{ - TypedConfig: util.MessageToAny(&redis.RedisProxy{ - Settings: &redis.RedisProxy_ConnPoolSettings{ - OpTimeout: durationpb.New(time.Millisecond * 200), - }, - }), - }, - }, - }, - }, - }, - }, - { - Name: "network-filter-to-be-replaced-not-found", - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 6380, - }, - }, - }, - }, - FilterChains: []*listener.FilterChain{ - { - Filters: []*listener.Filter{ - {Name: "network-filter-should-not-be-replaced"}, - }, - }, - }, - }, - { - Name: "default-filter-chain", - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 6381, - }, - }, - }, - }, - FilterChains: []*listener.FilterChain{ - { - Filters: []*listener.Filter{ - {Name: "network-filter"}, - }, - }, - }, - DefaultFilterChain: &listener.FilterChain{ - Filters: []*listener.Filter{ - {Name: "default-network-filter-replaced"}, - }, - }, - }, - { - Name: "another-listener", - }, - { - Name: "listener-http-filter-to-be-replaced", - FilterChains: []*listener.FilterChain{ - { - FilterChainMatch: &listener.FilterChainMatch{ - DestinationPort: &wrapperspb.UInt32Value{ - Value: 80, - }, - }, - Filters: []*listener.Filter{ - { - Name: wellknown.HTTPConnectionManager, - ConfigType: &listener.Filter_TypedConfig{ - TypedConfig: util.MessageToAny(&http_conn.HttpConnectionManager{ - HttpFilters: []*http_conn.HttpFilter{ - {Name: "http-filter-replaced"}, - {Name: "another-http-filter"}, - }, - }), - }, - }, - }, - }, - }, - }, - { - Name: "listener-http-filter-to-be-replaced-not-found", - FilterChains: []*listener.FilterChain{ - { - FilterChainMatch: &listener.FilterChainMatch{ - DestinationPort: &wrapperspb.UInt32Value{ - Value: 80, - }, - }, - Filters: []*listener.Filter{ - { - Name: wellknown.HTTPConnectionManager, - ConfigType: &listener.Filter_TypedConfig{ - TypedConfig: util.MessageToAny(&http_conn.HttpConnectionManager{ - HttpFilters: []*http_conn.HttpFilter{ - {Name: "http-filter-should-not-be-replaced"}, - {Name: "another-http-filter"}, - }, - }), - }, - }, - }, - }, - }, - }, - { - Name: "custom-tls-replacement", - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 7777, - }, - }, - }, - }, - FilterChains: []*listener.FilterChain{ - { - TransportSocket: &core.TransportSocket{ - Name: "transport_sockets.alts", - ConfigType: &core.TransportSocket_TypedConfig{ - TypedConfig: util.MessageToAny(&udpa.TypedStruct{ - TypeUrl: "type.googleapis.com/envoy.extensions.transport_sockets.alts.v3.Alts", - Value: buildGolangPatchStruct(`{"handshaker_service":"1.2.3.4"}`), - }), - }, - }, - Filters: []*listener.Filter{{Name: "filter"}}, - }, - }, - }, - { - Name: "custom-tls-addition", - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 7778, - }, - }, - }, - }, - FilterChains: []*listener.FilterChain{ - { - TransportSocket: &core.TransportSocket{ - Name: "transport_sockets.alts", - ConfigType: &core.TransportSocket_TypedConfig{ - TypedConfig: util.MessageToAny(&udpa.TypedStruct{ - TypeUrl: "type.googleapis.com/envoy.extensions.transport_sockets.alts.v3.Alts", - Value: buildGolangPatchStruct(`{"handshaker_service":"1.2.3.4"}`), - }), - }, - }, - Filters: []*listener.Filter{{Name: "filter"}}, - }, - }, - }, - { - Name: "new-outbound-listener1", - }, - } - - sidecarOutboundInNoAdd := []*listener.Listener{ - { - Name: "12345", - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 12345, - }, - }, - }, - }, - FilterChains: []*listener.FilterChain{ - { - FilterChainMatch: &listener.FilterChainMatch{TransportProtocol: "tls"}, - TransportSocket: &core.TransportSocket{ - Name: "envoy.transport_sockets.tls", - ConfigType: &core.TransportSocket_TypedConfig{ - TypedConfig: util.MessageToAny(&tls.DownstreamTlsContext{}), - }, - }, - Filters: []*listener.Filter{{Name: "envoy.transport_sockets.tls"}}, - }, - { - Filters: []*listener.Filter{ - {Name: "filter1"}, - {Name: "filter2"}, - }, - }, - }, - }, - { - Name: "another-listener", - }, - } - - sidecarOutboundOutNoAdd := []*listener.Listener{ - { - Name: "12345", - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 12345, - }, - }, - }, - }, - FilterChains: []*listener.FilterChain{ - { - FilterChainMatch: &listener.FilterChainMatch{TransportProtocol: "tls"}, - TransportSocket: &core.TransportSocket{ - Name: "envoy.transport_sockets.tls", - ConfigType: &core.TransportSocket_TypedConfig{ - TypedConfig: util.MessageToAny(&tls.DownstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsParams: &tls.TlsParameters{ - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - }, - }, - }), - }, - }, - Filters: []*listener.Filter{{Name: "envoy.transport_sockets.tls"}}, - }, - { - Filters: []*listener.Filter{ - {Name: "filter0"}, - {Name: "filter1"}, - }, - }, - }, - }, - { - Name: "another-listener", - }, - } - - faultFilterIn := &fault.HTTPFault{ - UpstreamCluster: "foobar", - } - faultFilterInAny, _ := anypb.New(faultFilterIn) - faultFilterOut := &fault.HTTPFault{ - UpstreamCluster: "scooby", - DownstreamNodes: []string{"foo"}, - } - faultFilterOutAny, _ := anypb.New(faultFilterOut) - - gatewayIn := []*listener.Listener{ - { - Name: "80", - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 80, - }, - }, - }, - }, - FilterChains: []*listener.FilterChain{ - { - FilterChainMatch: &listener.FilterChainMatch{ - ServerNames: []string{"match.com", "*.foo.com"}, - }, - Filters: []*listener.Filter{ - { - Name: wellknown.HTTPConnectionManager, - ConfigType: &listener.Filter_TypedConfig{ - TypedConfig: util.MessageToAny(&http_conn.HttpConnectionManager{ - HttpFilters: []*http_conn.HttpFilter{ - {Name: "http-filter1"}, - {Name: "http-filter2"}, - }, - }), - }, - }, - }, - }, - }, - }, - { - Name: "another-listener", - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 443, - }, - }, - }, - }, - FilterChains: []*listener.FilterChain{ - { - FilterChainMatch: &listener.FilterChainMatch{ - ServerNames: []string{"nomatch.com", "*.foo.com"}, - }, - Filters: []*listener.Filter{{Name: "network-filter"}}, - }, - }, - }, - } - - gatewayOut := []*listener.Listener{ - { - Name: "80", - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 80, - }, - }, - }, - }, - ListenerFilters: []*listener.ListenerFilter{{Name: "foo"}}, - FilterChains: []*listener.FilterChain{ - { - FilterChainMatch: &listener.FilterChainMatch{ - ServerNames: []string{"match.com", "*.foo.com", "foo.com"}, - }, - Filters: []*listener.Filter{ - { - Name: wellknown.HTTPConnectionManager, - ConfigType: &listener.Filter_TypedConfig{ - TypedConfig: util.MessageToAny(&http_conn.HttpConnectionManager{ - HttpFilters: []*http_conn.HttpFilter{ - {Name: "http-filter1"}, - {Name: "http-filter2"}, - {Name: "http-filter3"}, - }, - }), - }, - }, - }, - }, - }, - }, - { - Name: "another-listener", - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 443, - }, - }, - }, - }, - FilterChains: []*listener.FilterChain{ - { - FilterChainMatch: &listener.FilterChainMatch{ - ServerNames: []string{"nomatch.com", "*.foo.com"}, - }, - Filters: []*listener.Filter{{Name: "network-filter"}}, - }, - }, - }, - } - - sidecarVirtualInboundIn := []*listener.Listener{ - { - Name: model.VirtualInboundListenerName, - UseOriginalDst: istio_proto.BoolTrue, - TrafficDirection: core.TrafficDirection_INBOUND, - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 15006, - }, - }, - }, - }, - FilterChains: []*listener.FilterChain{ - { - Name: "virtualInbound-blackhole", - FilterChainMatch: &listener.FilterChainMatch{ - DestinationPort: &wrappers.UInt32Value{ - Value: 15006, - }, - }, - Filters: []*listener.Filter{ - { - Name: wellknown.TCPProxy, - ConfigType: &listener.Filter_TypedConfig{ - TypedConfig: util.MessageToAny(&tcp_proxy.TcpProxy{ - StatPrefix: util.BlackHoleCluster, - ClusterSpecifier: &tcp_proxy.TcpProxy_Cluster{Cluster: util.BlackHoleCluster}, - }), - }, - }, - }, - }, - { - FilterChainMatch: &listener.FilterChainMatch{ - DestinationPort: &wrapperspb.UInt32Value{ - Value: 80, - }, - }, - Filters: []*listener.Filter{ - { - Name: wellknown.HTTPConnectionManager, - ConfigType: &listener.Filter_TypedConfig{ - TypedConfig: util.MessageToAny(&http_conn.HttpConnectionManager{ - HttpFilters: []*http_conn.HttpFilter{ - { - Name: wellknown.Fault, - ConfigType: &http_conn.HttpFilter_TypedConfig{TypedConfig: faultFilterInAny}, - }, - {Name: "http-filter2"}, - {Name: "http-filter-to-be-removed"}, - }, - }), - }, - }, - }, - }, - { - FilterChainMatch: &listener.FilterChainMatch{ - AddressSuffix: "0.0.0.0", - }, - Filters: []*listener.Filter{ - {Name: "network-filter-should-not-be-replaced"}, - }, - }, - { - FilterChainMatch: &listener.FilterChainMatch{ - DestinationPort: &wrapperspb.UInt32Value{ - Value: 6380, - }, - }, - Filters: []*listener.Filter{ - {Name: "network-filter-to-be-replaced"}, - }, - }, - { - Name: "filter-chain-name-not-match", - Filters: []*listener.Filter{ - {Name: "custom-network-filter-1"}, - {Name: "custom-network-filter-2"}, - }, - }, - { - Name: "filter-chain-name-match", - Filters: []*listener.Filter{ - {Name: "custom-network-filter-1"}, - {Name: "custom-network-filter-2"}, - }, - }, - { - Name: "catch-all", - FilterChainMatch: &listener.FilterChainMatch{}, - Filters: []*listener.Filter{ - { - Name: wellknown.HTTPConnectionManager, - ConfigType: &listener.Filter_TypedConfig{ - TypedConfig: util.MessageToAny(&http_conn.HttpConnectionManager{ - HttpFilters: []*http_conn.HttpFilter{ - {Name: "base"}, - }, - }), - }, - }, - }, - }, - }, - }, - } - - sidecarVirtualInboundOut := []*listener.Listener{ - { - Name: model.VirtualInboundListenerName, - UseOriginalDst: istio_proto.BoolTrue, - TrafficDirection: core.TrafficDirection_INBOUND, - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 15006, - }, - }, - }, - }, - FilterChains: []*listener.FilterChain{ - { - Name: "virtualInbound-blackhole", - FilterChainMatch: &listener.FilterChainMatch{ - DestinationPort: &wrappers.UInt32Value{ - Value: 15006, - }, - }, - Filters: []*listener.Filter{ - { - Name: wellknown.TCPProxy, - ConfigType: &listener.Filter_TypedConfig{ - TypedConfig: util.MessageToAny(&tcp_proxy.TcpProxy{ - StatPrefix: util.BlackHoleCluster, - ClusterSpecifier: &tcp_proxy.TcpProxy_Cluster{Cluster: util.BlackHoleCluster}, - }), - }, - }, - }, - }, - { - FilterChainMatch: &listener.FilterChainMatch{ - DestinationPort: &wrapperspb.UInt32Value{ - Value: 80, - }, - }, - TransportSocket: &core.TransportSocket{ - Name: "envoy.transport_sockets.tls", - ConfigType: &core.TransportSocket_TypedConfig{ - TypedConfig: util.MessageToAny(&tls.DownstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - AlpnProtocols: []string{"h2-80", "http/1.1-80"}, - }, - }), - }, - }, - Filters: []*listener.Filter{ - { - Name: wellknown.HTTPConnectionManager, - ConfigType: &listener.Filter_TypedConfig{ - TypedConfig: util.MessageToAny(&http_conn.HttpConnectionManager{ - XffNumTrustedHops: 4, - MergeSlashes: true, - AlwaysSetRequestIdInResponse: true, - HttpFilters: []*http_conn.HttpFilter{ - {Name: "http-filter0"}, - { - Name: wellknown.Fault, - ConfigType: &http_conn.HttpFilter_TypedConfig{TypedConfig: faultFilterOutAny}, - }, - {Name: "http-filter3"}, - {Name: "http-filter2"}, - {Name: "http-filter-5"}, - {Name: "http-filter4"}, - }, - }), - }, - }, - }, - }, - { - FilterChainMatch: &listener.FilterChainMatch{ - AddressSuffix: "0.0.0.0", - }, - Filters: []*listener.Filter{ - {Name: "network-filter-should-not-be-replaced"}, - }, - }, - { - FilterChainMatch: &listener.FilterChainMatch{ - DestinationPort: &wrapperspb.UInt32Value{ - Value: 6380, - }, - }, - TransportSocket: &core.TransportSocket{ - Name: "envoy.transport_sockets.tls", - ConfigType: &core.TransportSocket_TypedConfig{ - TypedConfig: util.MessageToAny(&tls.DownstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - AlpnProtocols: []string{"h2-6380", "http/1.1-6380"}, - }, - }), - }, - }, - Filters: []*listener.Filter{ - { - Name: "envoy.redis_proxy", - ConfigType: &listener.Filter_TypedConfig{ - TypedConfig: util.MessageToAny(&redis.RedisProxy{ - StatPrefix: "redis_stats", - PrefixRoutes: &redis.RedisProxy_PrefixRoutes{ - CatchAllRoute: &redis.RedisProxy_PrefixRoutes_Route{ - Cluster: "custom-redis-cluster", - }, - }, - }), - }, - }, - }, - }, - { - Name: "filter-chain-name-not-match", - Filters: []*listener.Filter{ - {Name: "custom-network-filter-1"}, - {Name: "custom-network-filter-2"}, - }, - }, - { - Name: "filter-chain-name-match", - Filters: []*listener.Filter{ - {Name: "custom-network-filter-1"}, - }, - }, - { - Name: "catch-all", - FilterChainMatch: &listener.FilterChainMatch{}, - Filters: []*listener.Filter{ - { - Name: wellknown.HTTPConnectionManager, - ConfigType: &listener.Filter_TypedConfig{ - TypedConfig: util.MessageToAny(&http_conn.HttpConnectionManager{ - HttpFilters: []*http_conn.HttpFilter{ - {Name: "base"}, - }, - }), - }, - }, - }, - }, - }, - }, - } - - sidecarProxy := &model.Proxy{ - Type: model.SidecarProxy, - ConfigNamespace: "not-default", - Metadata: &model.NodeMetadata{ - IstioVersion: "1.2.2", - Raw: map[string]interface{}{ - "foo": "sidecar", - "bar": "proxy", - }, - }, - } - - gatewayProxy := &model.Proxy{ - Type: model.Router, - ConfigNamespace: "not-default", - Metadata: &model.NodeMetadata{ - IstioVersion: "1.2.2", - Raw: map[string]interface{}{ - "foo": "sidecar", - "bar": "proxy", - }, - }, - } - serviceDiscovery := memregistry.NewServiceDiscovery() - e := newTestEnvironment(serviceDiscovery, testMesh, buildEnvoyFilterConfigStore(configPatches)) - push := model.NewPushContext() - _ = push.InitContext(e, nil, nil) - - type args struct { - patchContext networking.EnvoyFilter_PatchContext - proxy *model.Proxy - push *model.PushContext - listeners []*listener.Listener - skipAdds bool - } - tests := []struct { - name string - args args - want []*listener.Listener - }{ - { - name: "gateway lds", - args: args{ - patchContext: networking.EnvoyFilter_GATEWAY, - proxy: gatewayProxy, - push: push, - listeners: gatewayIn, - skipAdds: false, - }, - want: gatewayOut, - }, - { - name: "sidecar outbound lds", - args: args{ - patchContext: networking.EnvoyFilter_SIDECAR_OUTBOUND, - proxy: sidecarProxy, - push: push, - listeners: sidecarOutboundIn, - skipAdds: false, - }, - want: sidecarOutboundOut, - }, - { - name: "sidecar outbound lds - skip adds", - args: args{ - patchContext: networking.EnvoyFilter_SIDECAR_OUTBOUND, - proxy: sidecarProxy, - push: push, - listeners: sidecarOutboundInNoAdd, - skipAdds: true, - }, - want: sidecarOutboundOutNoAdd, - }, - { - name: "sidecar inbound virtual", - args: args{ - patchContext: networking.EnvoyFilter_SIDECAR_INBOUND, - proxy: sidecarProxy, - push: push, - listeners: sidecarVirtualInboundIn, - skipAdds: false, - }, - want: sidecarVirtualInboundOut, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := ApplyListenerPatches(tt.args.patchContext, tt.args.push.EnvoyFilters(tt.args.proxy), - tt.args.listeners, tt.args.skipAdds) - if diff := cmp.Diff(tt.want, got, protocmp.Transform()); diff != "" { - t.Errorf("ApplyListenerPatches(): %s mismatch (-want +got):\n%s", tt.name, diff) - } - }) - } -} - -// This benchmark measures the performance of Telemetry V2 EnvoyFilter patches. The intent here is to -// measure overhead of using EnvoyFilters rather than native code. -func BenchmarkTelemetryV2Filters(b *testing.B) { - l := &listener.Listener{ - Name: "another-listener", - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 80, - }, - }, - }, - }, - ListenerFilters: []*listener.ListenerFilter{{Name: "envoy.tls_inspector"}}, - FilterChains: []*listener.FilterChain{ - { - Filters: []*listener.Filter{ - { - Name: wellknown.HTTPConnectionManager, - ConfigType: &listener.Filter_TypedConfig{ - TypedConfig: util.MessageToAny(&http_conn.HttpConnectionManager{ - XffNumTrustedHops: 4, - MergeSlashes: true, - AlwaysSetRequestIdInResponse: true, - HttpFilters: []*http_conn.HttpFilter{ - {Name: "http-filter3"}, - {Name: "envoy.router"}, // Use deprecated name for test. - {Name: "http-filter2"}, - }, - }), - }, - }, - }, - }, - }, - } - - file, err := os.ReadFile(filepath.Join(env.IstioSrc, "manifests/charts/istio-control/istio-discovery/files/gen-istio.yaml")) - if err != nil { - b.Fatalf("failed to read telemetry v2 Envoy Filters") - } - var configPatches []*networking.EnvoyFilter_EnvoyConfigObjectPatch - - configs, _, err := crd.ParseInputs(string(file)) - if err != nil { - b.Fatalf("failed to unmarshal EnvoyFilter: %v", err) - } - for _, c := range configs { - if c.GroupVersionKind != gvk.EnvoyFilter { - continue - } - configPatches = append(configPatches, c.Spec.(*networking.EnvoyFilter).ConfigPatches...) - } - if len(configPatches) == 0 { - b.Fatalf("found no patches, failed to read telemetry config?") - } - - sidecarProxy := &model.Proxy{ - Type: model.SidecarProxy, - ConfigNamespace: "not-default", - Metadata: &model.NodeMetadata{ - IstioVersion: "1.2.2", - Raw: map[string]interface{}{ - "foo": "sidecar", - "bar": "proxy", - }, - }, - } - serviceDiscovery := memregistry.NewServiceDiscovery() - e := newTestEnvironment(serviceDiscovery, testMesh, buildEnvoyFilterConfigStore(configPatches)) - push := model.NewPushContext() - _ = push.InitContext(e, nil, nil) - - var got interface{} - b.ResetTimer() - for n := 0; n < b.N; n++ { - copied := proto.Clone(l) - got = ApplyListenerPatches(networking.EnvoyFilter_SIDECAR_OUTBOUND, push.EnvoyFilters(sidecarProxy), - []*listener.Listener{copied.(*listener.Listener)}, false) - } - _ = got -} diff --git a/pilot/pkg/networking/core/v1alpha3/envoyfilter/monitoring.go b/pilot/pkg/networking/core/v1alpha3/envoyfilter/monitoring.go deleted file mode 100644 index 2d46e6f6c..000000000 --- a/pilot/pkg/networking/core/v1alpha3/envoyfilter/monitoring.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package envoyfilter - -import ( - "sync" -) - -import ( - "istio.io/pkg/monitoring" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" -) - -type Result string - -const ( - Error Result = "error" - Applied Result = "applied" -) - -type PatchType string - -const ( - Cluster PatchType = "cluster" - Listener PatchType = "listener" - FilterChain PatchType = "filterchain" - NetworkFilter PatchType = "networkfilter" - // nolint - HttpFilter PatchType = "httpfilter" - Route PatchType = "route" - VirtualHost PatchType = "vhost" - Bootstrap PatchType = "bootstrap" -) - -var ( - patchType = monitoring.MustCreateLabel("patch") - resultType = monitoring.MustCreateLabel("result") - nameType = monitoring.MustCreateLabel("name") - - envoyFilterStatus = monitoring.NewGauge( - "pilot_envoy_filter_status", - "Status of Envoy filters whether it was applied or errored.", - monitoring.WithLabels(nameType, patchType, resultType), - ) -) - -var ( - envoyFilterStatusMap map[string]map[string]bool // Map of Envoy filter name, patch and status. - envoyFilterMutex sync.RWMutex -) - -func init() { - if features.EnableEnvoyFilterMetrics { - monitoring.MustRegister(envoyFilterStatus) - envoyFilterStatusMap = make(map[string]map[string]bool) - } -} - -// IncrementEnvoyFilterMetric increments filter metric. -func IncrementEnvoyFilterMetric(name string, pt PatchType, applied bool) { - if !features.EnableEnvoyFilterMetrics { - return - } - envoyFilterMutex.Lock() - defer envoyFilterMutex.Unlock() - if _, exists := envoyFilterStatusMap[name]; !exists { - envoyFilterStatusMap[name] = make(map[string]bool) - } - if applied { - envoyFilterStatusMap[name][string(pt)] = true - } -} - -// IncrementEnvoyFilterErrorMetric increments filter metric for errors. -func IncrementEnvoyFilterErrorMetric(pt PatchType) { - if !features.EnableEnvoyFilterMetrics { - return - } - envoyFilterStatus.With(patchType.Value(string(pt))).With(resultType.Value(string(Error))).Record(1) -} - -func RecordMetrics() { - if !features.EnableEnvoyFilterMetrics { - return - } - envoyFilterMutex.RLock() - defer envoyFilterMutex.RUnlock() - for name, pmap := range envoyFilterStatusMap { - for pt, applied := range pmap { - if applied { - envoyFilterStatus.With(nameType.Value(name)).With(patchType.Value(pt)). - With(resultType.Value(string(Applied))).Record(1) - } else { - envoyFilterStatus.With(nameType.Value(name)).With(patchType.Value(pt)). - With(resultType.Value(string(Applied))).Record(0) - } - } - } - envoyFilterStatusMap = make(map[string]map[string]bool) -} diff --git a/pilot/pkg/networking/core/v1alpha3/envoyfilter/rc_patch.go b/pilot/pkg/networking/core/v1alpha3/envoyfilter/rc_patch.go deleted file mode 100644 index 9fc91ad85..000000000 --- a/pilot/pkg/networking/core/v1alpha3/envoyfilter/rc_patch.go +++ /dev/null @@ -1,399 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package envoyfilter - -import ( - "strconv" - "strings" -) - -import ( - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - "google.golang.org/protobuf/proto" - networking "istio.io/api/networking/v1alpha3" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/util/runtime" - "github.com/apache/dubbo-go-pixiu/pkg/proto/merge" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -func ApplyRouteConfigurationPatches( - patchContext networking.EnvoyFilter_PatchContext, - proxy *model.Proxy, - efw *model.EnvoyFilterWrapper, - routeConfiguration *route.RouteConfiguration) (out *route.RouteConfiguration) { - defer runtime.HandleCrash(runtime.LogPanic, func(interface{}) { - IncrementEnvoyFilterErrorMetric(Route) - log.Errorf("route patch caused panic, so the patches did not take effect") - }) - // In case the patches cause panic, use the route generated before to reduce the influence. - out = routeConfiguration - if efw == nil { - return out - } - - var portMap model.GatewayPortMap - if proxy.MergedGateway != nil { - portMap = proxy.MergedGateway.PortMap - } - - // only merge is applicable for route configuration. - for _, rp := range efw.Patches[networking.EnvoyFilter_ROUTE_CONFIGURATION] { - if rp.Operation != networking.EnvoyFilter_Patch_MERGE { - continue - } - if commonConditionMatch(patchContext, rp) && - routeConfigurationMatch(patchContext, routeConfiguration, rp, portMap) { - merge.Merge(routeConfiguration, rp.Value) - IncrementEnvoyFilterMetric(rp.Key(), Route, true) - } else { - IncrementEnvoyFilterMetric(rp.Key(), Route, false) - } - } - patchVirtualHosts(patchContext, efw.Patches, routeConfiguration, portMap) - - return routeConfiguration -} - -func patchVirtualHosts(patchContext networking.EnvoyFilter_PatchContext, - patches map[networking.EnvoyFilter_ApplyTo][]*model.EnvoyFilterConfigPatchWrapper, - routeConfiguration *route.RouteConfiguration, portMap model.GatewayPortMap) { - removedVirtualHosts := sets.New() - // first do removes/merges/replaces - for i := range routeConfiguration.VirtualHosts { - if patchVirtualHost(patchContext, patches, routeConfiguration, routeConfiguration.VirtualHosts, i, portMap) { - removedVirtualHosts.Insert(routeConfiguration.VirtualHosts[i].Name) - } - } - - // now for the adds - for _, rp := range patches[networking.EnvoyFilter_VIRTUAL_HOST] { - if rp.Operation != networking.EnvoyFilter_Patch_ADD { - continue - } - if commonConditionMatch(patchContext, rp) && - routeConfigurationMatch(patchContext, routeConfiguration, rp, portMap) { - routeConfiguration.VirtualHosts = append(routeConfiguration.VirtualHosts, proto.Clone(rp.Value).(*route.VirtualHost)) - IncrementEnvoyFilterMetric(rp.Key(), VirtualHost, true) - } else { - IncrementEnvoyFilterMetric(rp.Key(), VirtualHost, false) - } - } - if len(removedVirtualHosts) > 0 { - trimmedVirtualHosts := make([]*route.VirtualHost, 0, len(routeConfiguration.VirtualHosts)) - for _, virtualHost := range routeConfiguration.VirtualHosts { - if removedVirtualHosts.Contains(virtualHost.Name) { - continue - } - trimmedVirtualHosts = append(trimmedVirtualHosts, virtualHost) - } - routeConfiguration.VirtualHosts = trimmedVirtualHosts - } -} - -// patchVirtualHost patches passed in virtual host if it is MERGE operation. -// The return value indicates whether the virtual host has been removed for REMOVE operations. -func patchVirtualHost(patchContext networking.EnvoyFilter_PatchContext, - patches map[networking.EnvoyFilter_ApplyTo][]*model.EnvoyFilterConfigPatchWrapper, - routeConfiguration *route.RouteConfiguration, virtualHosts []*route.VirtualHost, - idx int, portMap model.GatewayPortMap) bool { - for _, rp := range patches[networking.EnvoyFilter_VIRTUAL_HOST] { - applied := false - if commonConditionMatch(patchContext, rp) && - routeConfigurationMatch(patchContext, routeConfiguration, rp, portMap) && - virtualHostMatch(virtualHosts[idx], rp) { - applied = true - if rp.Operation == networking.EnvoyFilter_Patch_REMOVE { - return true - } else if rp.Operation == networking.EnvoyFilter_Patch_MERGE { - merge.Merge(virtualHosts[idx], rp.Value) - } else if rp.Operation == networking.EnvoyFilter_Patch_REPLACE { - virtualHosts[idx] = proto.Clone(rp.Value).(*route.VirtualHost) - } - } - IncrementEnvoyFilterMetric(rp.Key(), VirtualHost, applied) - } - patchHTTPRoutes(patchContext, patches, routeConfiguration, virtualHosts[idx], portMap) - return false -} - -func hasRouteMatch(rp *model.EnvoyFilterConfigPatchWrapper) bool { - cMatch := rp.Match.GetRouteConfiguration() - if cMatch == nil { - return false - } - - vhMatch := cMatch.Vhost - if vhMatch == nil { - return false - } - - return vhMatch.Route != nil -} - -func patchHTTPRoutes(patchContext networking.EnvoyFilter_PatchContext, - patches map[networking.EnvoyFilter_ApplyTo][]*model.EnvoyFilterConfigPatchWrapper, - routeConfiguration *route.RouteConfiguration, virtualHost *route.VirtualHost, portMap model.GatewayPortMap) { - routesRemoved := false - // Apply the route level removes/merges if any. - for index := range virtualHost.Routes { - patchHTTPRoute(patchContext, patches, routeConfiguration, virtualHost, index, &routesRemoved, portMap) - } - - // now for the adds - for _, rp := range patches[networking.EnvoyFilter_HTTP_ROUTE] { - applied := false - if !commonConditionMatch(patchContext, rp) || - !routeConfigurationMatch(patchContext, routeConfiguration, rp, portMap) || - !virtualHostMatch(virtualHost, rp) { - IncrementEnvoyFilterMetric(rp.Key(), Route, applied) - continue - } - if rp.Operation == networking.EnvoyFilter_Patch_ADD { - virtualHost.Routes = append(virtualHost.Routes, proto.Clone(rp.Value).(*route.Route)) - applied = true - } else if rp.Operation == networking.EnvoyFilter_Patch_INSERT_AFTER { - // Insert after without a route match is same as ADD in the end - if !hasRouteMatch(rp) { - virtualHost.Routes = append(virtualHost.Routes, proto.Clone(rp.Value).(*route.Route)) - continue - } - // find the matching route first - insertPosition := -1 - for i := 0; i < len(virtualHost.Routes); i++ { - if routeMatch(virtualHost.Routes[i], rp) { - insertPosition = i + 1 - break - } - } - - if insertPosition == -1 { - continue - } - applied = true - clonedVal := proto.Clone(rp.Value).(*route.Route) - virtualHost.Routes = append(virtualHost.Routes, clonedVal) - if insertPosition < len(virtualHost.Routes)-1 { - copy(virtualHost.Routes[insertPosition+1:], virtualHost.Routes[insertPosition:]) - virtualHost.Routes[insertPosition] = clonedVal - } - } else if rp.Operation == networking.EnvoyFilter_Patch_INSERT_BEFORE || rp.Operation == networking.EnvoyFilter_Patch_INSERT_FIRST { - // insert before/first without a route match is same as insert in the beginning - if !hasRouteMatch(rp) { - virtualHost.Routes = append([]*route.Route{proto.Clone(rp.Value).(*route.Route)}, virtualHost.Routes...) - continue - } - // find the matching route first - insertPosition := -1 - for i := 0; i < len(virtualHost.Routes); i++ { - if routeMatch(virtualHost.Routes[i], rp) { - insertPosition = i - break - } - } - - // If matching route is not found, then don't insert and continue. - if insertPosition == -1 { - continue - } - - applied = true - - // In case of INSERT_FIRST, if a match is found, still insert it at the top of the routes. - if rp.Operation == networking.EnvoyFilter_Patch_INSERT_FIRST { - insertPosition = 0 - } - - clonedVal := proto.Clone(rp.Value).(*route.Route) - virtualHost.Routes = append(virtualHost.Routes, clonedVal) - copy(virtualHost.Routes[insertPosition+1:], virtualHost.Routes[insertPosition:]) - virtualHost.Routes[insertPosition] = clonedVal - } - IncrementEnvoyFilterMetric(rp.Key(), Route, applied) - } - if routesRemoved { - trimmedRoutes := make([]*route.Route, 0, len(virtualHost.Routes)) - for i := range virtualHost.Routes { - if virtualHost.Routes[i] == nil { - continue - } - trimmedRoutes = append(trimmedRoutes, virtualHost.Routes[i]) - } - virtualHost.Routes = trimmedRoutes - } -} - -func patchHTTPRoute(patchContext networking.EnvoyFilter_PatchContext, - patches map[networking.EnvoyFilter_ApplyTo][]*model.EnvoyFilterConfigPatchWrapper, - routeConfiguration *route.RouteConfiguration, virtualHost *route.VirtualHost, routeIndex int, routesRemoved *bool, portMap model.GatewayPortMap) { - for _, rp := range patches[networking.EnvoyFilter_HTTP_ROUTE] { - applied := false - if commonConditionMatch(patchContext, rp) && - routeConfigurationMatch(patchContext, routeConfiguration, rp, portMap) && - virtualHostMatch(virtualHost, rp) && - routeMatch(virtualHost.Routes[routeIndex], rp) { - - // different virtualHosts may share same routes pointer - virtualHost.Routes = cloneVhostRoutes(virtualHost.Routes) - if rp.Operation == networking.EnvoyFilter_Patch_REMOVE { - virtualHost.Routes[routeIndex] = nil - *routesRemoved = true - return - } else if rp.Operation == networking.EnvoyFilter_Patch_MERGE { - merge.Merge(virtualHost.Routes[routeIndex], rp.Value) - } - applied = true - } - IncrementEnvoyFilterMetric(rp.Key(), Route, applied) - } -} - -func routeConfigurationMatch(patchContext networking.EnvoyFilter_PatchContext, rc *route.RouteConfiguration, - rp *model.EnvoyFilterConfigPatchWrapper, portMap model.GatewayPortMap) bool { - rMatch := rp.Match.GetRouteConfiguration() - if rMatch == nil { - return true - } - - // we match on the port number and virtual host for sidecars - // we match on port number, server port name, gateway name, plus virtual host for gateways - if patchContext != networking.EnvoyFilter_GATEWAY { - listenerPort := 0 - if strings.HasPrefix(rc.Name, string(model.TrafficDirectionInbound)) { - _, _, _, listenerPort = model.ParseSubsetKey(rc.Name) - } else { - listenerPort, _ = strconv.Atoi(rc.Name) - } - - // FIXME: Ports on a route can be 0. the API only takes uint32 for ports - // We should either make that field in API as a wrapper type or switch to int - if rMatch.PortNumber != 0 && int(rMatch.PortNumber) != listenerPort { - return false - } - - if rMatch.Name != "" && rMatch.Name != rc.Name { - return false - } - - return true - } - - // This is a gateway. Get all the fields in the gateway's RDS route name - routePortNumber, portName, gateway := model.ParseGatewayRDSRouteName(rc.Name) - if rMatch.PortNumber != 0 && !anyPortMatches(portMap, routePortNumber, int(rMatch.PortNumber)) { - return false - } - if rMatch.PortName != "" && rMatch.PortName != portName { - return false - } - if rMatch.Gateway != "" && rMatch.Gateway != gateway { - return false - } - - if rMatch.Name != "" && rMatch.Name != rc.Name { - return false - } - - return true -} - -func anyPortMatches(m model.GatewayPortMap, number int, matchNumber int) bool { - if servicePorts, f := m[number]; f { - // We do have service ports mapping to this, see if we match those - for s := range servicePorts { - if s == matchNumber { - return true - } - } - return false - } - // Otherwise, check the port directly - return number == matchNumber -} - -func virtualHostMatch(vh *route.VirtualHost, rp *model.EnvoyFilterConfigPatchWrapper) bool { - rMatch := rp.Match.GetRouteConfiguration() - if rMatch == nil { - return true - } - - match := rMatch.Vhost - if match == nil { - // match any virtual host in the named route configuration - return true - } - if vh == nil { - // route configuration has a specific match for a virtual host but - // we do not have a virtual host to match. - return false - } - // check if virtual host names match - return match.Name == "" || match.Name == vh.Name -} - -func routeMatch(httpRoute *route.Route, rp *model.EnvoyFilterConfigPatchWrapper) bool { - rMatch := rp.Match.GetRouteConfiguration() - if rMatch == nil { - return true - } - - vMatch := rMatch.Vhost - if vMatch == nil { - // match any virtual host in the named httpRoute configuration - return true - } - - match := vMatch.Route - if match == nil { - // match any httpRoute in the virtual host - return true - } - - if httpRoute == nil { - // we have a specific match for particular httpRoute but - // we do not have a httpRoute to match. - return false - } - - // check if httpRoute names match - if match.Name != "" && match.Name != httpRoute.Name { - return false - } - - if match.Action != networking.EnvoyFilter_RouteConfigurationMatch_RouteMatch_ANY { - switch httpRoute.Action.(type) { - case *route.Route_Route: - return match.Action == networking.EnvoyFilter_RouteConfigurationMatch_RouteMatch_ROUTE - case *route.Route_Redirect: - return match.Action == networking.EnvoyFilter_RouteConfigurationMatch_RouteMatch_REDIRECT - case *route.Route_DirectResponse: - return match.Action == networking.EnvoyFilter_RouteConfigurationMatch_RouteMatch_DIRECT_RESPONSE - } - } - return true -} - -func cloneVhostRoutes(routes []*route.Route) []*route.Route { - out := make([]*route.Route, len(routes)) - for i := 0; i < len(routes); i++ { - clone := proto.Clone(routes[i]).(*route.Route) - out[i] = clone - } - return out -} diff --git a/pilot/pkg/networking/core/v1alpha3/envoyfilter/rc_patch_test.go b/pilot/pkg/networking/core/v1alpha3/envoyfilter/rc_patch_test.go deleted file mode 100644 index 65f002e11..000000000 --- a/pilot/pkg/networking/core/v1alpha3/envoyfilter/rc_patch_test.go +++ /dev/null @@ -1,953 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package envoyfilter - -import ( - "testing" -) - -import ( - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/testing/protocmp" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/memory" -) - -func Test_virtualHostMatch(t *testing.T) { - type args struct { - cp *model.EnvoyFilterConfigPatchWrapper - vh *route.VirtualHost - } - tests := []struct { - name string - args args - want bool - }{ - { - name: "vh is nil", - args: args{ - cp: &model.EnvoyFilterConfigPatchWrapper{ - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - Vhost: &networking.EnvoyFilter_RouteConfigurationMatch_VirtualHostMatch{}, - }, - }, - }, - }, - }, - want: false, - }, - { - name: "full match", - args: args{ - vh: &route.VirtualHost{ - Name: "scooby", - }, - cp: &model.EnvoyFilterConfigPatchWrapper{ - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - Vhost: &networking.EnvoyFilter_RouteConfigurationMatch_VirtualHostMatch{ - Name: "scooby", - }, - }, - }, - }, - }, - }, - want: true, - }, - { - name: "mismatch", - args: args{ - vh: &route.VirtualHost{ - Name: "scoobydoo", - }, - cp: &model.EnvoyFilterConfigPatchWrapper{ - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - Vhost: &networking.EnvoyFilter_RouteConfigurationMatch_VirtualHostMatch{ - Name: "scooby", - }, - }, - }, - }, - }, - }, - want: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := virtualHostMatch(tt.args.vh, tt.args.cp); got != tt.want { - t.Errorf("virtualHostMatch() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_routeConfigurationMatch(t *testing.T) { - type args struct { - rc *route.RouteConfiguration - patchContext networking.EnvoyFilter_PatchContext - cp *model.EnvoyFilterConfigPatchWrapper - portMap model.GatewayPortMap - } - tests := []struct { - name string - args args - want bool - }{ - { - name: "nil route match", - args: args{ - patchContext: networking.EnvoyFilter_SIDECAR_OUTBOUND, - cp: &model.EnvoyFilterConfigPatchWrapper{ - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{}, - }, - }, - want: true, - }, - { - name: "rc name mismatch", - args: args{ - patchContext: networking.EnvoyFilter_SIDECAR_OUTBOUND, - cp: &model.EnvoyFilterConfigPatchWrapper{ - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{Name: "scooby.80"}, - }, - }, - }, - rc: &route.RouteConfiguration{Name: "scooby.90"}, - }, - want: false, - }, - { - name: "sidecar port match", - args: args{ - patchContext: networking.EnvoyFilter_SIDECAR_OUTBOUND, - cp: &model.EnvoyFilterConfigPatchWrapper{ - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{PortNumber: 80}, - }, - }, - }, - rc: &route.RouteConfiguration{Name: "80"}, - }, - want: true, - }, - { - name: "sidecar port mismatch", - args: args{ - patchContext: networking.EnvoyFilter_SIDECAR_OUTBOUND, - cp: &model.EnvoyFilterConfigPatchWrapper{ - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{PortNumber: 80}, - }, - }, - }, - rc: &route.RouteConfiguration{Name: "90"}, - }, - want: false, - }, - { - name: "gateway fields match", - args: args{ - patchContext: networking.EnvoyFilter_GATEWAY, - cp: &model.EnvoyFilterConfigPatchWrapper{ - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - PortNumber: 443, - PortName: "app1", - Gateway: "ns1/gw1", - }, - }, - }, - }, - rc: &route.RouteConfiguration{Name: "https.443.app1.gw1.ns1"}, - }, - want: true, - }, - { - name: "gateway fields mismatch", - args: args{ - patchContext: networking.EnvoyFilter_GATEWAY, - cp: &model.EnvoyFilterConfigPatchWrapper{ - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - PortNumber: 443, - PortName: "app1", - Gateway: "ns1/gw1", - }, - }, - }, - }, - rc: &route.RouteConfiguration{Name: "http.80"}, - }, - want: false, - }, - { - name: "http target port match", - args: args{ - patchContext: networking.EnvoyFilter_GATEWAY, - cp: &model.EnvoyFilterConfigPatchWrapper{ - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - PortNumber: 80, - }, - }, - }, - }, - rc: &route.RouteConfiguration{Name: "http.8080"}, - portMap: map[int]map[int]struct{}{ - 8080: {80: {}, 81: {}}, - }, - }, - want: true, - }, - { - name: "http target port no match", - args: args{ - patchContext: networking.EnvoyFilter_GATEWAY, - cp: &model.EnvoyFilterConfigPatchWrapper{ - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - PortNumber: 9090, - }, - }, - }, - }, - rc: &route.RouteConfiguration{Name: "http.9090"}, - portMap: map[int]map[int]struct{}{ - 8080: {80: {}, 81: {}}, - }, - }, - want: true, - }, - { - name: "https.443.app1.gw1.ns1", - args: args{ - patchContext: networking.EnvoyFilter_GATEWAY, - cp: &model.EnvoyFilterConfigPatchWrapper{ - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - PortNumber: 443, - }, - }, - }, - }, - rc: &route.RouteConfiguration{Name: "http.8443"}, - portMap: map[int]map[int]struct{}{ - 8443: {443: {}}, - }, - }, - want: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := routeConfigurationMatch(tt.args.patchContext, tt.args.rc, tt.args.cp, tt.args.portMap); got != tt.want { - t.Errorf("routeConfigurationMatch() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestApplyRouteConfigurationPatches(t *testing.T) { - configPatches := []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_ROUTE_CONFIGURATION, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - PortNumber: 80, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(`{"request_headers_to_remove":["h3", "h4"]}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_HTTP_ROUTE, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - PortNumber: 80, - Vhost: &networking.EnvoyFilter_RouteConfigurationMatch_VirtualHostMatch{ - Route: &networking.EnvoyFilter_RouteConfigurationMatch_RouteMatch{ - Action: networking.EnvoyFilter_RouteConfigurationMatch_RouteMatch_ROUTE, - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(`{"route": { "prefix_rewrite": "/foo"}}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_HTTP_ROUTE, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - PortNumber: 80, - Vhost: &networking.EnvoyFilter_RouteConfigurationMatch_VirtualHostMatch{ - Route: &networking.EnvoyFilter_RouteConfigurationMatch_RouteMatch{ - Name: "bar", - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - { - ApplyTo: networking.EnvoyFilter_VIRTUAL_HOST, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - Vhost: &networking.EnvoyFilter_RouteConfigurationMatch_VirtualHostMatch{ - Name: "allow_any", - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - { - ApplyTo: networking.EnvoyFilter_VIRTUAL_HOST, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_ADD, - Value: buildPatchStruct(`{"name":"new-vhost"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_VIRTUAL_HOST, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_GATEWAY, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - Vhost: &networking.EnvoyFilter_RouteConfigurationMatch_VirtualHostMatch{ - Name: "vhost1", - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{Operation: networking.EnvoyFilter_Patch_REMOVE}, - }, - { - ApplyTo: networking.EnvoyFilter_VIRTUAL_HOST, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - Vhost: &networking.EnvoyFilter_RouteConfigurationMatch_VirtualHostMatch{ - Name: "vhost2", - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{Operation: networking.EnvoyFilter_Patch_REMOVE}, - }, - { - ApplyTo: networking.EnvoyFilter_VIRTUAL_HOST, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_ANY, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_MERGE, - Value: buildPatchStruct(`{"domains":["domain:80"]}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_HTTP_ROUTE, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_ANY, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - PortNumber: 9090, - Vhost: &networking.EnvoyFilter_RouteConfigurationMatch_VirtualHostMatch{ - Name: "test.com", - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_ADD, - Value: buildPatchStruct(`{"name": "route4.0"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_HTTP_ROUTE, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_ANY, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - PortNumber: 9090, - Vhost: &networking.EnvoyFilter_RouteConfigurationMatch_VirtualHostMatch{ - Name: "test.com", - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_INSERT_FIRST, - Value: buildPatchStruct(`{"name": "route0.0"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_HTTP_ROUTE, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_ANY, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - PortNumber: 9090, - Vhost: &networking.EnvoyFilter_RouteConfigurationMatch_VirtualHostMatch{ - Name: "test.com", - Route: &networking.EnvoyFilter_RouteConfigurationMatch_RouteMatch{ - Name: "route2.0", - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_INSERT_AFTER, - Value: buildPatchStruct(`{"name": "route2.5"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_HTTP_ROUTE, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_ANY, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - PortNumber: 9090, - Vhost: &networking.EnvoyFilter_RouteConfigurationMatch_VirtualHostMatch{ - Name: "test.com", - Route: &networking.EnvoyFilter_RouteConfigurationMatch_RouteMatch{ - Name: "route2.0", - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_INSERT_BEFORE, - Value: buildPatchStruct(`{"name": "route1.5"}`), - }, - }, - } - - sidecarOutboundRC := &route.RouteConfiguration{ - Name: "80", - VirtualHosts: []*route.VirtualHost{ - { - Name: "foo.com", - Domains: []string{"domain"}, - Routes: []*route.Route{ - { - Name: "foo", - Action: &route.Route_Route{ - Route: &route.RouteAction{ - PrefixRewrite: "/", - }, - }, - }, - { - Name: "bar", - Action: &route.Route_Redirect{ - Redirect: &route.RedirectAction{ - ResponseCode: 301, - }, - }, - }, - }, - }, - }, - RequestHeadersToRemove: []string{"h1", "h2"}, - } - patchedSidecarOutputRC := &route.RouteConfiguration{ - Name: "80", - VirtualHosts: []*route.VirtualHost{ - { - Name: "foo.com", - Domains: []string{"domain", "domain:80"}, - Routes: []*route.Route{ - { - Name: "foo", - Action: &route.Route_Route{ - Route: &route.RouteAction{ - PrefixRewrite: "/foo", - }, - }, - }, - }, - }, - { - Name: "new-vhost", - }, - }, - RequestHeadersToRemove: []string{"h1", "h2", "h3", "h4"}, - } - sidecarInboundRC := &route.RouteConfiguration{ - Name: "inbound|http|80", - VirtualHosts: []*route.VirtualHost{ - { - Name: "vhost2", - }, - }, - } - patchedSidecarInboundRC := &route.RouteConfiguration{ - Name: "inbound|http|80", - VirtualHosts: []*route.VirtualHost{ - { - Name: "new-vhost", - }, - }, - } - - gatewayRC := &route.RouteConfiguration{ - Name: "80", - VirtualHosts: []*route.VirtualHost{ - { - Name: "vhost1", - Domains: []string{"domain"}, - }, - { - Name: "gateway", - Domains: []string{"gateway"}, - }, - }, - } - patchedGatewayRC := &route.RouteConfiguration{ - Name: "80", - VirtualHosts: []*route.VirtualHost{ - { - Name: "gateway", - Domains: []string{"gateway", "domain:80"}, - }, - { - Name: "new-vhost", - }, - }, - } - arrayInsert := &route.RouteConfiguration{ - Name: "9090", - VirtualHosts: []*route.VirtualHost{ - { - Name: "test.com", - Domains: []string{"domain"}, - Routes: []*route.Route{ - { - Name: "route1.0", - Action: &route.Route_Route{ - Route: &route.RouteAction{ - PrefixRewrite: "/", - }, - }, - }, - { - Name: "route2.0", - Action: &route.Route_Redirect{ - Redirect: &route.RedirectAction{ - ResponseCode: 301, - }, - }, - }, - { - Name: "route3.0", - Action: &route.Route_Redirect{ - Redirect: &route.RedirectAction{ - ResponseCode: 404, - }, - }, - }, - }, - }, - }, - } - patchedArrayInsert := &route.RouteConfiguration{ - Name: "9090", - VirtualHosts: []*route.VirtualHost{ - { - Name: "test.com", - Domains: []string{"domain", "domain:80"}, - Routes: []*route.Route{ - { - Name: "route0.0", - }, - { - Name: "route1.0", - Action: &route.Route_Route{ - Route: &route.RouteAction{ - PrefixRewrite: "/", - }, - }, - }, - { - Name: "route1.5", - }, - { - Name: "route2.0", - Action: &route.Route_Redirect{ - Redirect: &route.RedirectAction{ - ResponseCode: 301, - }, - }, - }, - { - Name: "route2.5", - }, - { - Name: "route3.0", - Action: &route.Route_Redirect{ - Redirect: &route.RedirectAction{ - ResponseCode: 404, - }, - }, - }, - { - Name: "route4.0", - }, - }, - }, - { - Name: "new-vhost", - }, - }, - } - - serviceDiscovery := memory.NewServiceDiscovery() - env := newTestEnvironment(serviceDiscovery, testMesh, buildEnvoyFilterConfigStore(configPatches)) - push := model.NewPushContext() - push.InitContext(env, nil, nil) - - sidecarNode := &model.Proxy{Type: model.SidecarProxy, ConfigNamespace: "not-default"} - gatewayNode := &model.Proxy{Type: model.Router, ConfigNamespace: "not-default"} - - type args struct { - patchContext networking.EnvoyFilter_PatchContext - proxy *model.Proxy - push *model.PushContext - routeConfiguration *route.RouteConfiguration - } - tests := []struct { - name string - args args - want *route.RouteConfiguration - }{ - { - name: "sidecar outbound rds patch", - args: args{ - patchContext: networking.EnvoyFilter_SIDECAR_OUTBOUND, - proxy: sidecarNode, - push: push, - routeConfiguration: sidecarOutboundRC, - }, - want: patchedSidecarOutputRC, - }, - { - name: "sidecar inbound rc patch", - args: args{ - patchContext: networking.EnvoyFilter_SIDECAR_INBOUND, - proxy: sidecarNode, - push: push, - routeConfiguration: sidecarInboundRC, - }, - want: patchedSidecarInboundRC, - }, - { - name: "gateway rds patch", - args: args{ - patchContext: networking.EnvoyFilter_GATEWAY, - proxy: gatewayNode, - push: push, - routeConfiguration: gatewayRC, - }, - want: patchedGatewayRC, - }, - { - name: "array insert patch", - args: args{ - patchContext: networking.EnvoyFilter_SIDECAR_OUTBOUND, - proxy: sidecarNode, - push: push, - routeConfiguration: arrayInsert, - }, - want: patchedArrayInsert, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - efw := tt.args.push.EnvoyFilters(tt.args.proxy) - got := ApplyRouteConfigurationPatches(tt.args.patchContext, tt.args.proxy, - efw, tt.args.routeConfiguration) - if diff := cmp.Diff(tt.want, got, protocmp.Transform()); diff != "" { - t.Errorf("ApplyRouteConfigurationPatches(): %s mismatch (-want +got):\n%s", tt.name, diff) - } - }) - } -} - -func TestReplaceVhost(t *testing.T) { - configPatches := []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_VIRTUAL_HOST, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - Vhost: &networking.EnvoyFilter_RouteConfigurationMatch_VirtualHostMatch{ - Name: "to-be-replaced", - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REPLACE, - Value: buildPatchStruct(`{ - "name":"replaced", - "domains":["replaced.com"], - "rate_limits": [ - { - "actions": [ - { - "request_headers": { - "header_name": ":path", - "descriptor_key": "PATH" - } - } - ] - } - ] - }`), - }, - }, - } - - sidecarInboundRCToBeReplaced := &route.RouteConfiguration{ - Name: "inbound|http|80", - VirtualHosts: []*route.VirtualHost{ - { - Name: "to-be-replaced", - Domains: []string{"xxx"}, - Routes: []*route.Route{ - { - Name: "xxx", - Action: &route.Route_Route{ - Route: &route.RouteAction{ - PrefixRewrite: "/xxx", - }, - }, - }, - }, - }, - }, - } - replacedSidecarInboundRC := &route.RouteConfiguration{ - Name: "inbound|http|80", - VirtualHosts: []*route.VirtualHost{ - { - Name: "replaced", - Domains: []string{"replaced.com"}, - RateLimits: []*route.RateLimit{ - { - Actions: []*route.RateLimit_Action{ - { - ActionSpecifier: &route.RateLimit_Action_RequestHeaders_{ - RequestHeaders: &route.RateLimit_Action_RequestHeaders{ - HeaderName: ":path", - DescriptorKey: "PATH", - }, - }, - }, - }, - }, - }, - }, - }, - } - - serviceDiscovery := memory.NewServiceDiscovery() - env := newTestEnvironment(serviceDiscovery, testMesh, buildEnvoyFilterConfigStore(configPatches)) - push := model.NewPushContext() - push.InitContext(env, nil, nil) - - sidecarNode := &model.Proxy{Type: model.SidecarProxy, ConfigNamespace: "not-default"} - - type args struct { - patchContext networking.EnvoyFilter_PatchContext - proxy *model.Proxy - push *model.PushContext - routeConfiguration *route.RouteConfiguration - } - tests := []struct { - name string - args args - want *route.RouteConfiguration - }{ - { - name: "sidecar inbound vhost replace", - args: args{ - patchContext: networking.EnvoyFilter_SIDECAR_INBOUND, - proxy: sidecarNode, - push: push, - routeConfiguration: sidecarInboundRCToBeReplaced, - }, - want: replacedSidecarInboundRC, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - efw := tt.args.push.EnvoyFilters(tt.args.proxy) - got := ApplyRouteConfigurationPatches(tt.args.patchContext, tt.args.proxy, - efw, tt.args.routeConfiguration) - if diff := cmp.Diff(tt.want, got, protocmp.Transform()); diff != "" { - t.Errorf("ReplaceVhost(): %s mismatch (-want +got):\n%s", tt.name, diff) - } - }) - } -} - -func Test_routeMatch(t *testing.T) { - type args struct { - httpRoute *route.Route - cp *model.EnvoyFilterConfigPatchWrapper - } - tests := []struct { - name string - args args - want bool - }{ - { - name: "route is nil", - args: args{ - cp: &model.EnvoyFilterConfigPatchWrapper{ - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - Vhost: &networking.EnvoyFilter_RouteConfigurationMatch_VirtualHostMatch{ - Route: &networking.EnvoyFilter_RouteConfigurationMatch_RouteMatch{}, - }, - }, - }, - }, - }, - }, - want: false, - }, - { - name: "full match by name", - args: args{ - httpRoute: &route.Route{ - Name: "scooby", - }, - cp: &model.EnvoyFilterConfigPatchWrapper{ - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - Vhost: &networking.EnvoyFilter_RouteConfigurationMatch_VirtualHostMatch{ - Route: &networking.EnvoyFilter_RouteConfigurationMatch_RouteMatch{ - Name: "scooby", - }, - }, - }, - }, - }, - }, - }, - want: true, - }, - { - name: "full match by action", - args: args{ - httpRoute: &route.Route{ - Action: &route.Route_Redirect{Redirect: &route.RedirectAction{}}, - }, - cp: &model.EnvoyFilterConfigPatchWrapper{ - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - Vhost: &networking.EnvoyFilter_RouteConfigurationMatch_VirtualHostMatch{ - Route: &networking.EnvoyFilter_RouteConfigurationMatch_RouteMatch{ - Action: networking.EnvoyFilter_RouteConfigurationMatch_RouteMatch_REDIRECT, - }, - }, - }, - }, - }, - }, - }, - want: true, - }, - { - name: "mis match by action", - args: args{ - httpRoute: &route.Route{ - Action: &route.Route_Redirect{Redirect: &route.RedirectAction{}}, - }, - cp: &model.EnvoyFilterConfigPatchWrapper{ - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_RouteConfiguration{ - RouteConfiguration: &networking.EnvoyFilter_RouteConfigurationMatch{ - Vhost: &networking.EnvoyFilter_RouteConfigurationMatch_VirtualHostMatch{ - Route: &networking.EnvoyFilter_RouteConfigurationMatch_RouteMatch{ - Action: networking.EnvoyFilter_RouteConfigurationMatch_RouteMatch_ROUTE, - }, - }, - }, - }, - }, - }, - }, - want: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := routeMatch(tt.args.httpRoute, tt.args.cp); got != tt.want { - t.Errorf("routeMatch() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/extension/wasmplugin.go b/pilot/pkg/networking/core/v1alpha3/extension/wasmplugin.go deleted file mode 100644 index 353ddf4d6..000000000 --- a/pilot/pkg/networking/core/v1alpha3/extension/wasmplugin.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package extension - -import ( - envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - extensionsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/wasm/v3" - hcm_filter "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/anypb" - "google.golang.org/protobuf/types/known/durationpb" - extensions "istio.io/api/extensions/v1alpha1" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/xds" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" - _ "github.com/apache/dubbo-go-pixiu/pkg/wasm" -) - -var defaultConfigSource = &envoy_config_core_v3.ConfigSource{ - ConfigSourceSpecifier: &envoy_config_core_v3.ConfigSource_Ads{ - Ads: &envoy_config_core_v3.AggregatedConfigSource{}, - }, - ResourceApiVersion: envoy_config_core_v3.ApiVersion_V3, - // we block proxy init until WasmPlugins are loaded because they might be - // critical for security (e.g. authn/authz) - InitialFetchTimeout: &durationpb.Duration{Seconds: 0}, -} - -// PopAppend takes a list of filters and a set of WASM plugins, keyed by phase. It will remove all -// plugins of a provided phase from the WASM plugin set and append them to the list of filters -func PopAppend(list []*hcm_filter.HttpFilter, - filterMap map[extensions.PluginPhase][]*model.WasmPluginWrapper, - phase extensions.PluginPhase) []*hcm_filter.HttpFilter { - for _, ext := range filterMap[phase] { - list = append(list, toEnvoyHTTPFilter(ext)) - } - delete(filterMap, phase) - return list -} - -func toEnvoyHTTPFilter(wasmPlugin *model.WasmPluginWrapper) *hcm_filter.HttpFilter { - return &hcm_filter.HttpFilter{ - Name: wasmPlugin.ResourceName, - ConfigType: &hcm_filter.HttpFilter_ConfigDiscovery{ - ConfigDiscovery: &envoy_config_core_v3.ExtensionConfigSource{ - ConfigSource: defaultConfigSource, - TypeUrls: []string{xds.WasmHTTPFilterType}, - }, - }, - } -} - -// InsertedExtensionConfigurations returns pre-generated extension configurations added via WasmPlugin. -func InsertedExtensionConfigurations( - wasmPlugins map[extensions.PluginPhase][]*model.WasmPluginWrapper, - names []string, pullSecrets map[string][]byte) []*envoy_config_core_v3.TypedExtensionConfig { - result := make([]*envoy_config_core_v3.TypedExtensionConfig, 0) - if len(wasmPlugins) == 0 { - return result - } - hasName := sets.New(names...) - for _, list := range wasmPlugins { - for _, p := range list { - if !hasName.Contains(p.ResourceName) { - continue - } - wasmExtensionConfig := proto.Clone(p.WasmExtensionConfig).(*extensionsv3.Wasm) - // Find the pull secret resource name from wasm vm env variables. - // The Wasm extension config should already have a `ISTIO_META_WASM_IMAGE_PULL_SECRET` env variable - // at in the VM env variables, with value being the secret resource name. We try to find the actual - // secret, and replace the env variable value with it. When ECDS config update reaches the proxy, - // agent will extract out the secret from env variable, use it for image pulling, and strip the - // env variable from VM config before forwarding it to envoy. - envs := wasmExtensionConfig.GetConfig().GetVmConfig().GetEnvironmentVariables().GetKeyValues() - secretName := envs[model.WasmSecretEnv] - if secretName != "" { - if sec, found := pullSecrets[secretName]; found { - envs[model.WasmSecretEnv] = string(sec) - } else { - envs[model.WasmSecretEnv] = "" - } - } - typedConfig, err := anypb.New(wasmExtensionConfig) - if err != nil { - log.Warnf("wasmplugin %s/%s failed to marshal to TypedExtensionConfig: %s", p.Namespace, p.Name, err) - continue - } - ec := &envoy_config_core_v3.TypedExtensionConfig{ - Name: p.ResourceName, - TypedConfig: typedConfig, - } - result = append(result, ec) - } - } - return result -} diff --git a/pilot/pkg/networking/core/v1alpha3/extension/wasmplugin_test.go b/pilot/pkg/networking/core/v1alpha3/extension/wasmplugin_test.go deleted file mode 100644 index 363134b7f..000000000 --- a/pilot/pkg/networking/core/v1alpha3/extension/wasmplugin_test.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package extension - -import ( - "testing" -) - -import ( - envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - extensionsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/wasm/v3" - "github.com/golang/protobuf/ptypes/wrappers" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/testing/protocmp" - "google.golang.org/protobuf/types/known/anypb" - extensions "istio.io/api/extensions/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" -) - -var ( - someAuthNFilter = &model.WasmPluginWrapper{ - Name: "someAuthNFilter", - Namespace: "dubbo-system", - ResourceName: "dubbo-system.someAuthNFilter", - WasmPlugin: &extensions.WasmPlugin{ - Priority: &wrappers.Int64Value{Value: 1}, - }, - } - someAuthZFilter = &model.WasmPluginWrapper{ - Name: "someAuthZFilter", - Namespace: "dubbo-system", - ResourceName: "dubbo-system.someAuthZFilter", - WasmPlugin: &extensions.WasmPlugin{ - Priority: &wrappers.Int64Value{Value: 1000}, - }, - } -) - -func TestInsertedExtensionConfigurations(t *testing.T) { - wasm, _ := anypb.New(&extensionsv3.Wasm{}) - testCases := []struct { - name string - wasmPlugins map[extensions.PluginPhase][]*model.WasmPluginWrapper - names []string - expectedECs []*envoy_config_core_v3.TypedExtensionConfig - }{ - { - name: "empty", - wasmPlugins: map[extensions.PluginPhase][]*model.WasmPluginWrapper{}, - names: []string{someAuthNFilter.Name}, - expectedECs: []*envoy_config_core_v3.TypedExtensionConfig{}, - }, - { - name: "authn", - wasmPlugins: map[extensions.PluginPhase][]*model.WasmPluginWrapper{ - extensions.PluginPhase_AUTHN: { - someAuthNFilter, - someAuthZFilter, - }, - }, - names: []string{someAuthNFilter.Namespace + "." + someAuthNFilter.Name}, - expectedECs: []*envoy_config_core_v3.TypedExtensionConfig{ - { - Name: "dubbo-system.someAuthNFilter", - TypedConfig: wasm, - }, - }, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - ecs := InsertedExtensionConfigurations(tc.wasmPlugins, tc.names, nil) - if diff := cmp.Diff(tc.expectedECs, ecs, protocmp.Transform()); diff != "" { - t.Fatal(diff) - } - }) - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/extension_config_builder.go b/pilot/pkg/networking/core/v1alpha3/extension_config_builder.go deleted file mode 100644 index 661f7ba50..000000000 --- a/pilot/pkg/networking/core/v1alpha3/extension_config_builder.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3/envoyfilter" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3/extension" -) - -// BuildExtensionConfiguration returns the list of extension configuration for the given proxy and list of names. -// This is the ECDS output. -func (configgen *ConfigGeneratorImpl) BuildExtensionConfiguration( - proxy *model.Proxy, push *model.PushContext, extensionConfigNames []string, pullSecrets map[string][]byte) []*core.TypedExtensionConfig { - envoyFilterPatches := push.EnvoyFilters(proxy) - extensions := envoyfilter.InsertedExtensionConfigurations(envoyFilterPatches, extensionConfigNames) - wasmPlugins := push.WasmPlugins(proxy) - extensions = append(extensions, extension.InsertedExtensionConfigurations(wasmPlugins, extensionConfigNames, pullSecrets)...) - return extensions -} diff --git a/pilot/pkg/networking/core/v1alpha3/fake.go b/pilot/pkg/networking/core/v1alpha3/fake.go deleted file mode 100644 index af30c9a23..000000000 --- a/pilot/pkg/networking/core/v1alpha3/fake.go +++ /dev/null @@ -1,366 +0,0 @@ -//go:build !agent -// +build !agent - -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "bytes" - "sync" - "text/template" - "time" -) - -import ( - "github.com/Masterminds/sprig/v3" - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - meshconfig "istio.io/api/mesh/v1alpha1" -) - -import ( - configaggregate "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/aggregate" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/crd" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/aggregate" - memregistry "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/serviceentry" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - cluster2 "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -type TestOptions struct { - // If provided, these configs will be used directly - Configs []config.Config - ConfigPointers []*config.Config - - // If provided, the yaml string will be parsed and used as configs - ConfigString string - // If provided, the ConfigString will be treated as a go template, with this as input params - ConfigTemplateInput interface{} - - // Services to pre-populate as part of the service discovery - Services []*model.Service - Instances []*model.ServiceInstance - Gateways []model.NetworkGateway - - // If provided, this mesh config will be used - MeshConfig *meshconfig.MeshConfig - NetworksWatcher mesh.NetworksWatcher - - // Additional service registries to use. A ServiceEntry and memory registry will always be created. - ServiceRegistries []serviceregistry.Instance - - // Additional ConfigStoreController to use - ConfigStoreCaches []model.ConfigStoreController - - // CreateConfigStore defines a function that, given a ConfigStoreController, returns another ConfigStoreController to use - CreateConfigStore func(c model.ConfigStoreController) model.ConfigStoreController - - // Mutex used for push context access. Should generally only be used by NewFakeDiscoveryServer - PushContextLock *sync.RWMutex - - // If set, we will not run immediately, allowing adding event handlers, etc prior to start. - SkipRun bool - - // Used to set the serviceentry registry's cluster id - ClusterID cluster2.ID -} - -type ConfigGenTest struct { - t test.Failer - pushContextLock *sync.RWMutex - store model.ConfigStoreController - env *model.Environment - ConfigGen *ConfigGeneratorImpl - MemRegistry *memregistry.ServiceDiscovery - ServiceEntryRegistry *serviceentry.Controller - Registry model.Controller - initialConfigs []config.Config - stop chan struct{} -} - -func NewConfigGenTest(t test.Failer, opts TestOptions) *ConfigGenTest { - t.Helper() - stop := make(chan struct{}) - t.Cleanup(func() { - close(stop) - }) - - configs := getConfigs(t, opts) - configStore := memory.MakeSkipValidation(collections.PilotGatewayAPI) - - cc := memory.NewSyncController(configStore) - controllers := []model.ConfigStoreController{cc} - if opts.CreateConfigStore != nil { - controllers = append(controllers, opts.CreateConfigStore(cc)) - } - controllers = append(controllers, opts.ConfigStoreCaches...) - configController, _ := configaggregate.MakeWriteableCache(controllers, cc) - - m := opts.MeshConfig - if m == nil { - m = mesh.DefaultMeshConfig() - } - - serviceDiscovery := aggregate.NewController(aggregate.Options{}) - se := serviceentry.NewController( - configController, model.MakeIstioStore(configStore), - &FakeXdsUpdater{}, serviceentry.WithClusterID(opts.ClusterID)) - // TODO allow passing in registry, for k8s, mem reigstry - serviceDiscovery.AddRegistry(se) - msd := memregistry.NewServiceDiscovery(opts.Services...) - for _, instance := range opts.Instances { - msd.AddInstance(instance.Service.Hostname, instance) - } - msd.AddGateways(opts.Gateways...) - msd.ClusterID = cluster2.ID(provider.Mock) - serviceDiscovery.AddRegistry(serviceregistry.Simple{ - ClusterID: cluster2.ID(provider.Mock), - ProviderID: provider.Mock, - ServiceDiscovery: msd, - Controller: msd.Controller, - }) - for _, reg := range opts.ServiceRegistries { - serviceDiscovery.AddRegistry(reg) - } - - env := &model.Environment{PushContext: model.NewPushContext()} - env.Watcher = mesh.NewFixedWatcher(m) - if opts.NetworksWatcher == nil { - opts.NetworksWatcher = mesh.NewFixedNetworksWatcher(nil) - } - env.ServiceDiscovery = serviceDiscovery - env.ConfigStore = model.MakeIstioStore(configController) - env.NetworksWatcher = opts.NetworksWatcher - env.Init() - - fake := &ConfigGenTest{ - t: t, - store: configController, - env: env, - initialConfigs: configs, - stop: stop, - ConfigGen: NewConfigGenerator(&model.DisabledCache{}), - MemRegistry: msd, - Registry: serviceDiscovery, - ServiceEntryRegistry: se, - pushContextLock: opts.PushContextLock, - } - if !opts.SkipRun { - fake.Run() - if err := env.InitNetworksManager(&FakeXdsUpdater{}); err != nil { - t.Fatal(err) - } - if err := env.PushContext.InitContext(env, nil, nil); err != nil { - t.Fatalf("Failed to initialize push context: %v", err) - } - } - return fake -} - -func (f *ConfigGenTest) Run() { - go f.Registry.Run(f.stop) - go f.store.Run(f.stop) - // Setup configuration. This should be done after registries are added so they can process events. - for _, cfg := range f.initialConfigs { - if _, err := f.store.Create(cfg); err != nil { - f.t.Fatalf("failed to create config %v: %v", cfg.Name, err) - } - } - - // TODO allow passing event handlers for controller - - retry.UntilOrFail(f.t, f.store.HasSynced, retry.Delay(time.Millisecond)) - retry.UntilOrFail(f.t, f.Registry.HasSynced, retry.Delay(time.Millisecond)) - - f.ServiceEntryRegistry.ResyncEDS() -} - -// SetupProxy initializes a proxy for the current environment. This should generally be used when creating -// any proxy. For example, `p := SetupProxy(&model.Proxy{...})`. -func (f *ConfigGenTest) SetupProxy(p *model.Proxy) *model.Proxy { - // Setup defaults - if p == nil { - p = &model.Proxy{} - } - if p.Metadata == nil { - p.Metadata = &model.NodeMetadata{} - } - if p.Metadata.IstioVersion == "" { - p.Metadata.IstioVersion = "1.14.0" - } - if p.IstioVersion == nil { - p.IstioVersion = model.ParseIstioVersion(p.Metadata.IstioVersion) - } - if p.Type == "" { - p.Type = model.SidecarProxy - } - if p.ConfigNamespace == "" { - p.ConfigNamespace = "default" - } - if p.Metadata.Namespace == "" { - p.Metadata.Namespace = p.ConfigNamespace - } - if p.ID == "" { - p.ID = "app.test" - } - if p.DNSDomain == "" { - p.DNSDomain = p.ConfigNamespace + ".svc.cluster.local" - } - if len(p.IPAddresses) == 0 { - p.IPAddresses = []string{"1.1.1.1"} - } - - // Initialize data structures - pc := f.PushContext() - p.SetSidecarScope(pc) - p.SetServiceInstances(f.env.ServiceDiscovery) - p.SetGatewaysForProxy(pc) - p.DiscoverIPMode() - return p -} - -func (f *ConfigGenTest) Listeners(p *model.Proxy) []*listener.Listener { - return f.ConfigGen.BuildListeners(p, f.PushContext()) -} - -func (f *ConfigGenTest) Clusters(p *model.Proxy) []*cluster.Cluster { - raw, _ := f.ConfigGen.BuildClusters(p, &model.PushRequest{Push: f.PushContext()}) - res := make([]*cluster.Cluster, 0, len(raw)) - for _, r := range raw { - c := &cluster.Cluster{} - if err := r.Resource.UnmarshalTo(c); err != nil { - f.t.Fatal(err) - } - res = append(res, c) - } - return res -} - -func (f *ConfigGenTest) DeltaClusters( - p *model.Proxy, - configUpdated map[model.ConfigKey]struct{}, - watched *model.WatchedResource, -) ([]*cluster.Cluster, []string, bool) { - raw, removed, _, delta := f.ConfigGen.BuildDeltaClusters(p, - &model.PushRequest{ - Push: f.PushContext(), ConfigsUpdated: configUpdated, - }, watched) - res := make([]*cluster.Cluster, 0, len(raw)) - for _, r := range raw { - c := &cluster.Cluster{} - if err := r.Resource.UnmarshalTo(c); err != nil { - f.t.Fatal(err) - } - res = append(res, c) - } - return res, removed, delta -} - -func (f *ConfigGenTest) RoutesFromListeners(p *model.Proxy, l []*listener.Listener) []*route.RouteConfiguration { - resources, _ := f.ConfigGen.BuildHTTPRoutes(p, &model.PushRequest{Push: f.PushContext()}, xdstest.ExtractRoutesFromListeners(l)) - out := make([]*route.RouteConfiguration, 0, len(resources)) - for _, resource := range resources { - routeConfig := &route.RouteConfiguration{} - _ = resource.Resource.UnmarshalTo(routeConfig) - out = append(out, routeConfig) - } - return out -} - -func (f *ConfigGenTest) Routes(p *model.Proxy) []*route.RouteConfiguration { - return f.RoutesFromListeners(p, f.Listeners(p)) -} - -func (f *ConfigGenTest) PushContext() *model.PushContext { - if f.pushContextLock != nil { - f.pushContextLock.RLock() - defer f.pushContextLock.RUnlock() - } - return f.env.PushContext -} - -func (f *ConfigGenTest) Env() *model.Environment { - return f.env -} - -func (f *ConfigGenTest) Store() model.ConfigStoreController { - return f.store -} - -var _ model.XDSUpdater = &FakeXdsUpdater{} - -func getConfigs(t test.Failer, opts TestOptions) []config.Config { - for _, p := range opts.ConfigPointers { - if p != nil { - opts.Configs = append(opts.Configs, *p) - } - } - configStr := opts.ConfigString - if opts.ConfigTemplateInput != nil { - tmpl := template.Must(template.New("").Funcs(sprig.TxtFuncMap()).Parse(opts.ConfigString)) - var buf bytes.Buffer - if err := tmpl.Execute(&buf, opts.ConfigTemplateInput); err != nil { - t.Fatalf("failed to execute template: %v", err) - } - configStr = buf.String() - } - cfgs := opts.Configs - if configStr != "" { - t0 := time.Now() - configs, _, err := crd.ParseInputs(configStr) - if err != nil { - t.Fatalf("failed to read config: %v: %v", err, configStr) - } - // setup default namespace if not defined - for _, c := range configs { - if c.Namespace == "" { - c.Namespace = "default" - } - // Set creation timestamp to same time for all of them for consistency. - // If explicit setting is needed it can be set in the yaml - if c.CreationTimestamp.IsZero() { - c.CreationTimestamp = t0 - } - cfgs = append(cfgs, c) - } - } - return cfgs -} - -type FakeXdsUpdater struct{} - -func (f *FakeXdsUpdater) ConfigUpdate(*model.PushRequest) {} - -func (f *FakeXdsUpdater) EDSUpdate(_ model.ShardKey, _, _ string, _ []*model.IstioEndpoint) {} - -func (f *FakeXdsUpdater) EDSCacheUpdate(_ model.ShardKey, _, _ string, _ []*model.IstioEndpoint) {} - -func (f *FakeXdsUpdater) SvcUpdate(_ model.ShardKey, _, _ string, _ model.Event) {} - -func (f *FakeXdsUpdater) ProxyUpdate(_ cluster2.ID, _ string) {} - -func (f *FakeXdsUpdater) RemoveShard(_ model.ShardKey) {} diff --git a/pilot/pkg/networking/core/v1alpha3/filterchain_options.go b/pilot/pkg/networking/core/v1alpha3/filterchain_options.go deleted file mode 100644 index 5f893fdb8..000000000 --- a/pilot/pkg/networking/core/v1alpha3/filterchain_options.go +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/plugin" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - xdsfilters "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/filters" -) - -// FilterChainMatchOptions describes options used for filter chain matches. -type FilterChainMatchOptions struct { - // Application protocols of the filter chain match - ApplicationProtocols []string - // Transport protocol of the filter chain match. "tls" or empty - TransportProtocol string - // Filter chain protocol. HTTP for HTTP proxy and TCP for TCP proxy - Protocol networking.ListenerProtocol - // Whether this chain should terminate TLS or not - TLS bool -} - -// Set of filter chain match options used for various combinations. -var ( - // Same as inboundPermissiveFilterChainMatchOptions except for following case: - // FCM 3: ALPN [istio-peer-exchange, istio] Transport protocol: tls --> TCP traffic from sidecar over TLS - inboundPermissiveFilterChainMatchWithMxcOptions = []FilterChainMatchOptions{ - { - // client side traffic was detected as HTTP by the outbound listener, sent over mTLS - ApplicationProtocols: mtlsHTTPALPNs, - // If client sends mTLS traffic, transport protocol will be set by the TLS inspector - TransportProtocol: xdsfilters.TLSTransportProtocol, - Protocol: networking.ListenerProtocolHTTP, - TLS: true, - }, - { - // client side traffic was detected as HTTP by the outbound listener, sent out as plain text - ApplicationProtocols: plaintextHTTPALPNs, - // No transport protocol match as this filter chain (+match) will be used for plain text connections - Protocol: networking.ListenerProtocolHTTP, - TransportProtocol: xdsfilters.RawBufferTransportProtocol, - }, - { - // client side traffic could not be identified by the outbound listener, but sent over mTLS - ApplicationProtocols: mtlsTCPWithMxcALPNs, - // If client sends mTLS traffic, transport protocol will be set by the TLS inspector - TransportProtocol: xdsfilters.TLSTransportProtocol, - Protocol: networking.ListenerProtocolTCP, - TLS: true, - }, - { - // client side traffic could not be identified by the outbound listener, sent over plaintext - // or it could be that the client has no sidecar. In this case, this filter chain is simply - // receiving plaintext TCP traffic. - Protocol: networking.ListenerProtocolTCP, - TransportProtocol: xdsfilters.RawBufferTransportProtocol, - }, - { - // client side traffic could not be identified by the outbound listener, sent over one-way - // TLS (HTTPS for example) by the downstream application. - // or it could be that the client has no sidecar, and it is directly making a HTTPS connection to - // this sidecar. In this case, this filter chain is receiving plaintext one-way TLS traffic. The TLS - // inspector would detect this as TLS traffic [not necessarily mTLS]. But since there is no ALPN to match, - // this filter chain match will treat the traffic as just another TCP proxy. - TransportProtocol: xdsfilters.TLSTransportProtocol, - Protocol: networking.ListenerProtocolTCP, - }, - } - inboundPermissiveHTTPFilterChainMatchWithMxcOptions = []FilterChainMatchOptions{ - { - // HTTP over MTLS - ApplicationProtocols: allIstioMtlsALPNs, - TransportProtocol: xdsfilters.TLSTransportProtocol, - Protocol: networking.ListenerProtocolHTTP, - TLS: true, - }, - { - // Plaintext HTTP - Protocol: networking.ListenerProtocolHTTP, - TransportProtocol: xdsfilters.RawBufferTransportProtocol, - }, - // We do not need to handle other simple TLS or others, as this is explicitly declared as HTTP type. - } - inboundPermissiveTCPFilterChainMatchWithMxcOptions = []FilterChainMatchOptions{ - { - // MTLS - ApplicationProtocols: allIstioMtlsALPNs, - TransportProtocol: xdsfilters.TLSTransportProtocol, - Protocol: networking.ListenerProtocolTCP, - TLS: true, - }, - { - // Plain TLS - TransportProtocol: xdsfilters.TLSTransportProtocol, - Protocol: networking.ListenerProtocolTCP, - }, - { - // Plaintext - Protocol: networking.ListenerProtocolTCP, - TransportProtocol: xdsfilters.RawBufferTransportProtocol, - }, - } - - inboundStrictFilterChainMatchOptions = []FilterChainMatchOptions{ - { - // client side traffic was detected as HTTP by the outbound listener. - // If we are in strict mode, we will get mTLS HTTP ALPNS only. - ApplicationProtocols: mtlsHTTPALPNs, - Protocol: networking.ListenerProtocolHTTP, - TransportProtocol: xdsfilters.TLSTransportProtocol, - TLS: true, - }, - { - // Could not detect traffic on the client side. Server side has no mTLS. - Protocol: networking.ListenerProtocolTCP, - TransportProtocol: xdsfilters.TLSTransportProtocol, - TLS: true, - }, - } - inboundStrictTCPFilterChainMatchOptions = []FilterChainMatchOptions{ - { - Protocol: networking.ListenerProtocolTCP, - TransportProtocol: xdsfilters.TLSTransportProtocol, - TLS: true, - }, - } - inboundStrictHTTPFilterChainMatchOptions = []FilterChainMatchOptions{ - { - Protocol: networking.ListenerProtocolHTTP, - TransportProtocol: xdsfilters.TLSTransportProtocol, - TLS: true, - }, - } - - inboundPlainTextFilterChainMatchOptions = []FilterChainMatchOptions{ - { - ApplicationProtocols: plaintextHTTPALPNs, - Protocol: networking.ListenerProtocolHTTP, - TransportProtocol: xdsfilters.RawBufferTransportProtocol, - }, - { - // Could not detect traffic on the client side. Server side has no mTLS. - Protocol: networking.ListenerProtocolTCP, - TransportProtocol: xdsfilters.RawBufferTransportProtocol, - }, - } - inboundPlainTextTCPFilterChainMatchOptions = []FilterChainMatchOptions{ - { - Protocol: networking.ListenerProtocolTCP, - TransportProtocol: xdsfilters.RawBufferTransportProtocol, - }, - } - inboundPlainTextHTTPFilterChainMatchOptions = []FilterChainMatchOptions{ - { - Protocol: networking.ListenerProtocolHTTP, - TransportProtocol: xdsfilters.RawBufferTransportProtocol, - }, - } - - emptyFilterChainMatch = &listener.FilterChainMatch{} -) - -// getTLSFilterChainMatchOptions returns the FilterChainMatchOptions that should be used based on mTLS mode and protocol -func getTLSFilterChainMatchOptions(protocol networking.ListenerProtocol) []FilterChainMatchOptions { - return []FilterChainMatchOptions{{ - Protocol: protocol, - TransportProtocol: xdsfilters.TLSTransportProtocol, - TLS: true, - }} -} - -// getFilterChainMatchOptions returns the FilterChainMatchOptions that should be used based on mTLS mode and protocol -func getFilterChainMatchOptions(settings plugin.MTLSSettings, protocol networking.ListenerProtocol) []FilterChainMatchOptions { - switch protocol { - case networking.ListenerProtocolHTTP: - switch settings.Mode { - case model.MTLSStrict: - return inboundStrictHTTPFilterChainMatchOptions - case model.MTLSPermissive: - return inboundPermissiveHTTPFilterChainMatchWithMxcOptions - default: - return inboundPlainTextHTTPFilterChainMatchOptions - } - case networking.ListenerProtocolAuto: - switch settings.Mode { - case model.MTLSStrict: - return inboundStrictFilterChainMatchOptions - case model.MTLSPermissive: - return inboundPermissiveFilterChainMatchWithMxcOptions - default: - return inboundPlainTextFilterChainMatchOptions - } - default: - switch settings.Mode { - case model.MTLSStrict: - return inboundStrictTCPFilterChainMatchOptions - case model.MTLSPermissive: - return inboundPermissiveTCPFilterChainMatchWithMxcOptions - default: - return inboundPlainTextTCPFilterChainMatchOptions - } - } -} - -type fcOpts struct { - matchOpts FilterChainMatchOptions - fc networking.FilterChain -} - -func (opt fcOpts) populateFilterChain(mtls plugin.MTLSSettings, port uint32, matchingIP string) fcOpts { - opt.fc.FilterChainMatch = &listener.FilterChainMatch{} - opt.fc.FilterChainMatch.ApplicationProtocols = opt.matchOpts.ApplicationProtocols - opt.fc.FilterChainMatch.TransportProtocol = opt.matchOpts.TransportProtocol - if len(matchingIP) > 0 { - opt.fc.FilterChainMatch.PrefixRanges = []*core.CidrRange{util.ConvertAddressToCidr(matchingIP)} - } - if port > 0 { - opt.fc.FilterChainMatch.DestinationPort = &wrappers.UInt32Value{Value: port} - } - opt.fc.ListenerProtocol = opt.matchOpts.Protocol - if opt.fc.ListenerProtocol == networking.ListenerProtocolHTTP { - opt.fc.TLSContext = mtls.HTTP - } else { - opt.fc.TLSContext = mtls.TCP - } - return opt -} - -func (opt FilterChainMatchOptions) ToTransportSocket(mtls plugin.MTLSSettings) *tls.DownstreamTlsContext { - if !opt.TLS { - return nil - } - if opt.Protocol == networking.ListenerProtocolHTTP { - return mtls.HTTP - } - return mtls.TCP -} diff --git a/pilot/pkg/networking/core/v1alpha3/gateway.go b/pilot/pkg/networking/core/v1alpha3/gateway.go deleted file mode 100644 index 4b0e4383f..000000000 --- a/pilot/pkg/networking/core/v1alpha3/gateway.go +++ /dev/null @@ -1,1024 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "crypto/md5" - "encoding/binary" - "fmt" - "sort" - "strconv" - "strings" - "unsafe" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - "github.com/hashicorp/go-multierror" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - istionetworking "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - istio_route "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3/route" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/telemetry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/gateway" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/security" - "github.com/apache/dubbo-go-pixiu/pkg/proto" - "github.com/apache/dubbo-go-pixiu/pkg/util/istiomultierror" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -type mutableListenerOpts struct { - mutable *MutableListener - opts *buildListenerOpts -} - -func (configgen *ConfigGeneratorImpl) buildGatewayListeners(builder *ListenerBuilder) *ListenerBuilder { - if builder.node.MergedGateway == nil { - log.Debugf("buildGatewayListeners: no gateways for router %v", builder.node.ID) - return builder - } - - mergedGateway := builder.node.MergedGateway - log.Debugf("buildGatewayListeners: gateways after merging: %v", mergedGateway) - - actualWildcard, _ := getActualWildcardAndLocalHost(builder.node) - errs := istiomultierror.New() - // Mutable objects keyed by listener name so that we can build listeners at the end. - mutableopts := make(map[string]mutableListenerOpts) - proxyConfig := builder.node.Metadata.ProxyConfigOrDefault(builder.push.Mesh.DefaultConfig) - for _, port := range mergedGateway.ServerPorts { - // Skip ports we cannot bind to. Note that MergeGateways will already translate Service port to - // targetPort, which handles the common case of exposing ports like 80 and 443 but listening on - // higher numbered ports. - if builder.node.IsUnprivileged() && port.Number < 1024 { - log.Warnf("buildGatewayListeners: skipping privileged gateway port %d for node %s as it is an unprivileged pod", - port.Number, builder.node.ID) - continue - } - bind := actualWildcard - if len(port.Bind) > 0 { - bind = port.Bind - } - - // NOTE: There is no gating here to check for the value of the QUIC feature flag. However, - // they are created in MergeGatways only when the flag is set. So when it is turned off, the - // MergedQUICTransportServers would be nil so that no listener would be created. It is written this way - // to make testing a little easier. - transportToServers := map[istionetworking.TransportProtocol]map[model.ServerPort]*model.MergedServers{ - istionetworking.TransportProtocolTCP: mergedGateway.MergedServers, - istionetworking.TransportProtocolQUIC: mergedGateway.MergedQUICTransportServers, - } - - for transport, gwServers := range transportToServers { - if gwServers == nil { - log.Debugf("buildGatewayListeners: no gateway-server for transport %s at port %d", transport.String(), port) - continue - } - - // on a given port, we can either have plain text HTTP servers or - // HTTPS/TLS servers with SNI. We cannot have a mix of http and https server on same port. - // We can also have QUIC on a given port along with HTTPS/TLS on a given port. It does not - // cause port-conflict as they use different transport protocols - opts := &buildListenerOpts{ - push: builder.push, - proxy: builder.node, - bind: bind, - port: &model.Port{Port: int(port.Number)}, - bindToPort: true, - class: istionetworking.ListenerClassGateway, - transport: transport, - } - lname := getListenerName(bind, int(port.Number), transport) - p := protocol.Parse(port.Protocol) - serversForPort := gwServers[port] - if serversForPort == nil { - continue - } - - var newFilterChains []istionetworking.FilterChain - switch transport { - case istionetworking.TransportProtocolTCP: - newFilterChains = configgen.buildGatewayTCPBasedFilterChains(builder, p, port, opts, serversForPort, proxyConfig, mergedGateway) - case istionetworking.TransportProtocolQUIC: - // Currently, we just assume that QUIC is HTTP/3 although that does not - // have to be the case (it is just the most common case now, in the future - // we will support more cases) - newFilterChains = configgen.buildGatewayHTTP3FilterChains(builder, serversForPort, mergedGateway, proxyConfig, opts) - } - var mutable *MutableListener - if mopts, exists := mutableopts[lname]; !exists { - mutable = &MutableListener{ - MutableObjects: istionetworking.MutableObjects{ - // Note: buildListener creates filter chains but does not populate the filters in the chain; that's what - // this is for. - FilterChains: newFilterChains, - }, - } - mutableopts[lname] = mutableListenerOpts{mutable: mutable, opts: opts} - } else { - mopts.opts.filterChainOpts = append(mopts.opts.filterChainOpts, opts.filterChainOpts...) - mopts.mutable.MutableObjects.FilterChains = append(mopts.mutable.MutableObjects.FilterChains, newFilterChains...) - mutable = mopts.mutable - } - - for cnum := range mutable.FilterChains { - if mutable.FilterChains[cnum].ListenerProtocol == istionetworking.ListenerProtocolTCP { - mutable.FilterChains[cnum].TCP = append(mutable.FilterChains[cnum].TCP, builder.authzCustomBuilder.BuildTCP()...) - mutable.FilterChains[cnum].TCP = append(mutable.FilterChains[cnum].TCP, builder.authzBuilder.BuildTCP()...) - } - } - } - } - listeners := make([]*listener.Listener, 0) - for _, ml := range mutableopts { - ml.mutable.Listener = buildListener(*ml.opts, core.TrafficDirection_OUTBOUND) - log.Debugf("buildGatewayListeners: marshaling listener %q with %d filter chains", - ml.mutable.Listener.GetName(), len(ml.mutable.Listener.GetFilterChains())) - - // Filters are serialized one time into an opaque struct once we have the complete list. - if err := ml.mutable.build(builder, *ml.opts); err != nil { - errs = multierror.Append(errs, fmt.Errorf("gateway omitting listener %q due to: %v", ml.mutable.Listener.Name, err.Error())) - continue - } - listeners = append(listeners, ml.mutable.Listener) - } - // We'll try to return any listeners we successfully marshaled; if we have none, we'll emit the error we built up - err := errs.ErrorOrNil() - if err != nil { - // we have some listeners to return, but we also have some errors; log them - log.Info(err.Error()) - } - - if len(mutableopts) == 0 { - log.Warnf("gateway has zero listeners for node %v", builder.node.ID) - return builder - } - - builder.gatewayListeners = listeners - return builder -} - -func (configgen *ConfigGeneratorImpl) buildGatewayTCPBasedFilterChains( - builder *ListenerBuilder, - p protocol.Instance, port model.ServerPort, - opts *buildListenerOpts, - serversForPort *model.MergedServers, - proxyConfig *meshconfig.ProxyConfig, - mergedGateway *model.MergedGateway, -) []istionetworking.FilterChain { - newFilterChains := make([]istionetworking.FilterChain, 0) - if p.IsHTTP() { - // We have a list of HTTP servers on this port. Build a single listener for the server port. - // We only need to look at the first server in the list as the merge logic - // ensures that all servers are of same type. - port := &networking.Port{Number: port.Number, Protocol: port.Protocol} - opts.filterChainOpts = []*filterChainOpts{ - configgen.createGatewayHTTPFilterChainOpts(builder.node, port, nil, serversForPort.RouteName, - proxyConfig, istionetworking.ListenerProtocolTCP), - } - newFilterChains = append(newFilterChains, istionetworking.FilterChain{ - ListenerProtocol: istionetworking.ListenerProtocolHTTP, - }) - } else { - // build http connection manager with TLS context, for HTTPS servers using simple/mutual TLS - // build listener with tcp proxy, with or without TLS context, for TCP servers - // or TLS servers using simple/mutual/passthrough TLS - // or HTTPS servers using passthrough TLS - // This process typically yields multiple filter chain matches (with SNI) [if TLS is used] - tcpFilterChainOpts := make([]*filterChainOpts, 0) - for _, server := range serversForPort.Servers { - if gateway.IsTLSServer(server) && gateway.IsHTTPServer(server) { - routeName := mergedGateway.TLSServerInfo[server].RouteName - // This is a HTTPS server, where we are doing TLS termination. Build a http connection manager with TLS context - tcpFilterChainOpts = append(tcpFilterChainOpts, configgen.createGatewayHTTPFilterChainOpts(builder.node, server.Port, server, - routeName, proxyConfig, istionetworking.TransportProtocolTCP)) - newFilterChains = append(newFilterChains, istionetworking.FilterChain{ - ListenerProtocol: istionetworking.ListenerProtocolHTTP, - }) - } else { - // This is the case of TCP or PASSTHROUGH. - tcpChainOpts := configgen.createGatewayTCPFilterChainOpts(builder.node, builder.push, - server, mergedGateway.GatewayNameForServer[server]) - tcpFilterChainOpts = append(tcpFilterChainOpts, tcpChainOpts...) - for i := 0; i < len(tcpChainOpts); i++ { - newFilterChains = append(newFilterChains, istionetworking.FilterChain{ - ListenerProtocol: istionetworking.ListenerProtocolTCP, - }) - } - } - } - - opts.filterChainOpts = tcpFilterChainOpts - } - return newFilterChains -} - -func (configgen *ConfigGeneratorImpl) buildGatewayHTTP3FilterChains( - builder *ListenerBuilder, - serversForPort *model.MergedServers, - mergedGateway *model.MergedGateway, - proxyConfig *meshconfig.ProxyConfig, - opts *buildListenerOpts, -) []istionetworking.FilterChain { - newFilterChains := make([]istionetworking.FilterChain, 0) - quicFilterChainOpts := make([]*filterChainOpts, 0) - for _, server := range serversForPort.Servers { - log.Debugf("buildGatewayListeners: creating QUIC filter chain for port %d(%s:%s)", - server.GetPort().GetNumber(), server.GetPort().GetName(), server.GetPort().GetProtocol()) - - // Here it is assumed that this HTTP/3 server is a mirror of an existing HTTPS - // server. So the same route name would be reused instead of creating new one. - routeName := mergedGateway.TLSServerInfo[server].RouteName - quicFilterChainOpts = append(quicFilterChainOpts, configgen.createGatewayHTTPFilterChainOpts(builder.node, server.Port, server, - routeName, proxyConfig, istionetworking.TransportProtocolQUIC)) - newFilterChains = append(newFilterChains, istionetworking.FilterChain{ - // Make sure that this is set to HTTP so that JWT and Authorization - // filters that are applied to HTTPS are also applied to this chain. - // Not doing so is a security hole as would allow bypassing auth. - ListenerProtocol: istionetworking.ListenerProtocolHTTP, - TransportProtocol: istionetworking.TransportProtocolQUIC, - }) - } - opts.filterChainOpts = quicFilterChainOpts - return newFilterChains -} - -func getListenerName(bind string, port int, transport istionetworking.TransportProtocol) string { - switch transport { - case istionetworking.TransportProtocolTCP: - return bind + "_" + strconv.Itoa(port) - case istionetworking.TransportProtocolQUIC: - return "udp_" + bind + "_" + strconv.Itoa(port) - } - return "unknown" -} - -func buildNameToServiceMapForHTTPRoutes(node *model.Proxy, push *model.PushContext, - virtualService config.Config) map[host.Name]*model.Service { - vs := virtualService.Spec.(*networking.VirtualService) - nameToServiceMap := map[host.Name]*model.Service{} - - addService := func(hostname host.Name) { - if _, exist := nameToServiceMap[hostname]; exist { - return - } - - var service *model.Service - // First, we obtain the service which has the same namespace as virtualService - s, exist := push.ServiceIndex.HostnameAndNamespace[hostname][virtualService.Namespace] - if exist { - // We should check whether the selected service is visible to the proxy node. - if push.IsServiceVisible(s, node.ConfigNamespace) { - service = s - } - } - // If we find no service for the namespace of virtualService or the selected service is not visible to the proxy node, - // we should fallback to pick one service which is visible to the ConfigNamespace of node. - if service == nil { - service = push.ServiceForHostname(node, hostname) - } - nameToServiceMap[hostname] = service - } - - for _, httpRoute := range vs.Http { - if httpRoute.GetMirror() != nil { - addService(host.Name(httpRoute.GetMirror().GetHost())) - } - - for _, route := range httpRoute.GetRoute() { - if route.GetDestination() != nil { - addService(host.Name(route.GetDestination().GetHost())) - } - } - } - - return nameToServiceMap -} - -func (configgen *ConfigGeneratorImpl) buildGatewayHTTPRouteConfig(node *model.Proxy, push *model.PushContext, - routeName string) *route.RouteConfiguration { - if node.MergedGateway == nil { - log.Warnf("buildGatewayRoutes: no gateways for router %v", node.ID) - return &route.RouteConfiguration{ - Name: routeName, - VirtualHosts: []*route.VirtualHost{}, - ValidateClusters: proto.BoolFalse, - } - } - - merged := node.MergedGateway - log.Debugf("buildGatewayRoutes: gateways after merging: %v", merged) - - // make sure that there is some server listening on this port - if _, ok := merged.ServersByRouteName[routeName]; !ok { - log.Warnf("Gateway missing for route %s. This is normal if gateway was recently deleted.", routeName) - - // This can happen when a gateway has recently been deleted. Envoy will still request route - // information due to the draining of listeners, so we should not return an error. - return nil - } - - servers := merged.ServersByRouteName[routeName] - - // When this is true, we add alt-svc header to the response to tell the client - // that HTTP/3 over QUIC is available on the same port for this host. This is - // very important for discovering HTTP/3 services - _, isH3DiscoveryNeeded := merged.HTTP3AdvertisingRoutes[routeName] - - gatewayRoutes := make(map[string]map[string][]*route.Route) - gatewayVirtualServices := make(map[string][]config.Config) - vHostDedupMap := make(map[host.Name]*route.VirtualHost) - for _, server := range servers { - gatewayName := merged.GatewayNameForServer[server] - port := int(server.Port.Number) - - var virtualServices []config.Config - var exists bool - - if virtualServices, exists = gatewayVirtualServices[gatewayName]; !exists { - virtualServices = push.VirtualServicesForGateway(node.ConfigNamespace, gatewayName) - gatewayVirtualServices[gatewayName] = virtualServices - } - - for _, virtualService := range virtualServices { - virtualServiceHosts := host.NewNames(virtualService.Spec.(*networking.VirtualService).Hosts) - serverHosts := host.NamesForNamespace(server.Hosts, virtualService.Namespace) - - // We have two cases here: - // 1. virtualService hosts are 1.foo.com, 2.foo.com, 3.foo.com and server hosts are ns/*.foo.com - // 2. virtualService hosts are *.foo.com, and server hosts are ns/1.foo.com, ns/2.foo.com, ns/3.foo.com - intersectingHosts := serverHosts.Intersection(virtualServiceHosts) - if len(intersectingHosts) == 0 { - continue - } - - // Make sure we can obtain services which are visible to this virtualService as much as possible. - nameToServiceMap := buildNameToServiceMapForHTTPRoutes(node, push, virtualService) - - var routes []*route.Route - var exists bool - var err error - if _, exists = gatewayRoutes[gatewayName]; !exists { - gatewayRoutes[gatewayName] = make(map[string][]*route.Route) - } - - vskey := virtualService.Name + "/" + virtualService.Namespace - - if routes, exists = gatewayRoutes[gatewayName][vskey]; !exists { - hashByDestination := istio_route.GetConsistentHashForVirtualService(push, node, virtualService, nameToServiceMap) - routes, err = istio_route.BuildHTTPRoutesForVirtualService(node, virtualService, nameToServiceMap, - hashByDestination, port, map[string]bool{gatewayName: true}, isH3DiscoveryNeeded, push.Mesh) - if err != nil { - log.Debugf("%s omitting routes for virtual service %v/%v due to error: %v", node.ID, virtualService.Namespace, virtualService.Name, err) - continue - } - gatewayRoutes[gatewayName][vskey] = routes - } - - for _, hostname := range intersectingHosts { - if vHost, exists := vHostDedupMap[hostname]; exists { - vHost.Routes = append(vHost.Routes, routes...) - if server.Tls != nil && server.Tls.HttpsRedirect { - vHost.RequireTls = route.VirtualHost_ALL - } - } else { - newVHost := &route.VirtualHost{ - Name: util.DomainName(string(hostname), port), - Domains: buildGatewayVirtualHostDomains(string(hostname), port), - Routes: routes, - IncludeRequestAttemptCount: true, - } - if server.Tls != nil && server.Tls.HttpsRedirect { - newVHost.RequireTls = route.VirtualHost_ALL - } - vHostDedupMap[hostname] = newVHost - } - } - } - - // check all hostname in vHostDedupMap and if is not exist with HttpsRedirect set to true - // create VirtualHost to redirect - for _, hostname := range server.Hosts { - if !server.GetTls().GetHttpsRedirect() { - continue - } - if vHost, exists := vHostDedupMap[host.Name(hostname)]; exists { - vHost.RequireTls = route.VirtualHost_ALL - continue - } - newVHost := &route.VirtualHost{ - Name: util.DomainName(hostname, port), - Domains: buildGatewayVirtualHostDomains(hostname, port), - IncludeRequestAttemptCount: true, - RequireTls: route.VirtualHost_ALL, - } - vHostDedupMap[host.Name(hostname)] = newVHost - } - } - - var virtualHosts []*route.VirtualHost - if len(vHostDedupMap) == 0 { - port := int(servers[0].Port.Number) - log.Warnf("constructed http route config for route %s on port %d with no vhosts; Setting up a default 404 vhost", routeName, port) - virtualHosts = []*route.VirtualHost{{ - Name: util.DomainName("blackhole", port), - Domains: []string{"*"}, - // Empty route list will cause Envoy to 404 NR any requests - Routes: []*route.Route{}, - }} - } else { - virtualHosts = make([]*route.VirtualHost, 0, len(vHostDedupMap)) - vHostDedupMap = collapseDuplicateRoutes(vHostDedupMap) - for _, v := range vHostDedupMap { - v.Routes = istio_route.CombineVHostRoutes(v.Routes) - virtualHosts = append(virtualHosts, v) - } - } - - util.SortVirtualHosts(virtualHosts) - - routeCfg := &route.RouteConfiguration{ - // Retain the routeName as its used by EnvoyFilter patching logic - Name: routeName, - VirtualHosts: virtualHosts, - ValidateClusters: proto.BoolFalse, - } - - return routeCfg -} - -// hashRouteList returns a hash of a list of pointers -func hashRouteList(r []*route.Route) uint64 { - hash := md5.New() - for _, v := range r { - u := uintptr(unsafe.Pointer(v)) - size := unsafe.Sizeof(u) - b := make([]byte, size) - switch size { - case 4: - binary.LittleEndian.PutUint32(b, uint32(u)) - default: - binary.LittleEndian.PutUint64(b, uint64(u)) - } - hash.Write(b) - } - var tmp [md5.Size]byte - sum := hash.Sum(tmp[:0]) - return binary.LittleEndian.Uint64(sum) -} - -// collapseDuplicateRoutes prevents cardinality explosion when we have multiple hostnames defined for the same set of routes -// with virtual service: {hosts: [a, b], routes: [r1, r2]} -// before: [{vhosts: [a], routes: [r1, r2]},{vhosts: [b], routes: [r1, r2]}] -// after: [{vhosts: [a,b], routes: [r1, r2]}] -// Note: At this point in the code, r1 and r2 are just pointers. However, once we send them over the wire -// they are fully expanded and expensive, so the optimization is important. -func collapseDuplicateRoutes(input map[host.Name]*route.VirtualHost) map[host.Name]*route.VirtualHost { - if !features.EnableRouteCollapse { - return input - } - dedupe := make(map[host.Name]*route.VirtualHost, len(input)) - known := make(map[uint64]host.Name, len(input)) - - // In order to ensure stable XDS, we need to sort things. First vhost alphabetically will be the "primary" - var hostnameKeys host.Names = make([]host.Name, 0, len(input)) - for k := range input { - hostnameKeys = append(hostnameKeys, k) - } - sort.Sort(hostnameKeys) - for _, h := range hostnameKeys { - vh := input[h] - hash := hashRouteList(vh.Routes) - eh, f := known[hash] - if f && vhostMergeable(vh, dedupe[eh]) { - // Merge domains, routes are identical. We check the hash *and* routesEqual so that we don't depend on not having - // collisions. - // routesEqual is fairly cheap, but not cheap enough to do n^2 checks, so both are needed - dedupe[eh].Domains = append(dedupe[eh].Domains, vh.Domains...) - } else { - known[hash] = h - dedupe[h] = vh - } - } - return dedupe -} - -// vhostMergeable checks if two virtual hosts can be merged -// We explicitly do not check domains or name, as those are the keys for the merge -func vhostMergeable(a, b *route.VirtualHost) bool { - if a.IncludeRequestAttemptCount != b.IncludeRequestAttemptCount { - return false - } - if a.RequireTls != b.RequireTls { - return false - } - if !routesEqual(a.Routes, b.Routes) { - return false - } - return true -} - -func routesEqual(a, b []*route.Route) bool { - if len(a) != len(b) { - return false - } - for i := range a { - if a[i] != b[i] { - return false - } - } - return true -} - -// builds a HTTP connection manager for servers of type HTTP or HTTPS (mode: simple/mutual) -func (configgen *ConfigGeneratorImpl) createGatewayHTTPFilterChainOpts(node *model.Proxy, port *networking.Port, server *networking.Server, - routeName string, proxyConfig *meshconfig.ProxyConfig, transportProtocol istionetworking.TransportProtocol) *filterChainOpts { - serverProto := protocol.Parse(port.Protocol) - - if serverProto.IsHTTP() { - return &filterChainOpts{ - // This works because we validate that only HTTPS servers can have same port but still different port names - // and that no two non-HTTPS servers can be on same port or share port names. - // Validation is done per gateway and also during merging - sniHosts: nil, - tlsContext: nil, - httpOpts: &httpListenerOpts{ - rds: routeName, - useRemoteAddress: true, - connectionManager: buildGatewayConnectionManager(proxyConfig, node, false /* http3SupportEnabled */), - protocol: serverProto, - class: istionetworking.ListenerClassGateway, - }, - } - } - - // Build a filter chain for the HTTPS server - // We know that this is a HTTPS server because this function is called only for ports of type HTTP/HTTPS - // where HTTPS server's TLS mode is not passthrough and not nil - http3Enabled := transportProtocol == istionetworking.TransportProtocolQUIC - return &filterChainOpts{ - // This works because we validate that only HTTPS servers can have same port but still different port names - // and that no two non-HTTPS servers can be on same port or share port names. - // Validation is done per gateway and also during merging - sniHosts: node.MergedGateway.TLSServerInfo[server].SNIHosts, - tlsContext: buildGatewayListenerTLSContext(server, node, transportProtocol), - httpOpts: &httpListenerOpts{ - rds: routeName, - useRemoteAddress: true, - connectionManager: buildGatewayConnectionManager(proxyConfig, node, http3Enabled), - protocol: serverProto, - statPrefix: server.Name, - http3Only: http3Enabled, - class: istionetworking.ListenerClassGateway, - }, - } -} - -func buildGatewayConnectionManager(proxyConfig *meshconfig.ProxyConfig, node *model.Proxy, http3SupportEnabled bool) *hcm.HttpConnectionManager { - httpProtoOpts := &core.Http1ProtocolOptions{} - if features.HTTP10 || enableHTTP10(node.Metadata.HTTP10) { - httpProtoOpts.AcceptHttp_10 = true - } - xffNumTrustedHops := uint32(0) - forwardClientCertDetails := util.MeshConfigToEnvoyForwardClientCertDetails(meshconfig.Topology_SANITIZE_SET) - - if proxyConfig != nil && proxyConfig.GatewayTopology != nil { - xffNumTrustedHops = proxyConfig.GatewayTopology.NumTrustedProxies - if proxyConfig.GatewayTopology.ForwardClientCertDetails != meshconfig.Topology_UNDEFINED { - forwardClientCertDetails = util.MeshConfigToEnvoyForwardClientCertDetails(proxyConfig.GatewayTopology.ForwardClientCertDetails) - } - } - - var stripPortMode *hcm.HttpConnectionManager_StripAnyHostPort - if features.StripHostPort { - stripPortMode = &hcm.HttpConnectionManager_StripAnyHostPort{StripAnyHostPort: true} - } - - httpConnManager := &hcm.HttpConnectionManager{ - XffNumTrustedHops: xffNumTrustedHops, - // Forward client cert if connection is mTLS - ForwardClientCertDetails: forwardClientCertDetails, - SetCurrentClientCertDetails: &hcm.HttpConnectionManager_SetCurrentClientCertDetails{ - Subject: proto.BoolTrue, - Cert: true, - Uri: true, - Dns: true, - }, - ServerName: EnvoyServerName, - HttpProtocolOptions: httpProtoOpts, - StripPortMode: stripPortMode, - } - if http3SupportEnabled { - httpConnManager.Http3ProtocolOptions = &core.Http3ProtocolOptions{} - httpConnManager.CodecType = hcm.HttpConnectionManager_HTTP3 - } - return httpConnManager -} - -// sdsPath: is the path to the mesh-wide workload sds uds path, and it is assumed that if this path is unset, that sds is -// disabled mesh-wide -// metadata: map of miscellaneous configuration values sent from the Envoy instance back to Pilot, could include the field -// -// Below is a table of potential scenarios for the gateway configuration: -// -// TLS mode | Mesh-wide SDS | Ingress SDS | Resulting Configuration -// SIMPLE/MUTUAL | ENABLED | ENABLED | support SDS at ingress gateway to terminate SSL communication outside the mesh -// ISTIO_MUTUAL | ENABLED | DISABLED | support SDS at gateway to terminate workload mTLS, with internal workloads -// -// | for egress or with another trusted cluster for ingress) -// -// ISTIO_MUTUAL | DISABLED | DISABLED | use file-mounted secret paths to terminate workload mTLS from gateway -// -// Note that ISTIO_MUTUAL TLS mode and ingressSds should not be used simultaneously on the same ingress gateway. -func buildGatewayListenerTLSContext( - server *networking.Server, proxy *model.Proxy, transportProtocol istionetworking.TransportProtocol) *tls.DownstreamTlsContext { - // Server.TLS cannot be nil or passthrough. But as a safety guard, return nil - if server.Tls == nil || gateway.IsPassThroughServer(server) { - return nil // We don't need to setup TLS context for passthrough mode - } - - server.Tls.CipherSuites = filteredGatewayCipherSuites(server) - return BuildListenerTLSContext(server.Tls, proxy, transportProtocol) -} - -func convertTLSProtocol(in networking.ServerTLSSettings_TLSProtocol) tls.TlsParameters_TlsProtocol { - out := tls.TlsParameters_TlsProtocol(in) // There should be a one-to-one enum mapping - if out < tls.TlsParameters_TLS_AUTO || out > tls.TlsParameters_TLSv1_3 { - log.Warnf("was not able to map TLS protocol to Envoy TLS protocol") - return tls.TlsParameters_TLS_AUTO - } - return out -} - -func (configgen *ConfigGeneratorImpl) createGatewayTCPFilterChainOpts( - node *model.Proxy, push *model.PushContext, server *networking.Server, - gatewayName string) []*filterChainOpts { - // We have a TCP/TLS server. This could be TLS termination (user specifies server.TLS with simple/mutual) - // or opaque TCP (server.TLS is nil). or it could be a TLS passthrough with SNI based routing. - - // This is opaque TCP server. Find matching virtual services with TCP blocks and forward - if server.Tls == nil { - if filters := buildGatewayNetworkFiltersFromTCPRoutes(node, - push, server, gatewayName); len(filters) > 0 { - return []*filterChainOpts{ - { - sniHosts: nil, - tlsContext: nil, - networkFilters: filters, - }, - } - } - } else if !gateway.IsPassThroughServer(server) { - // TCP with TLS termination and forwarding. Setup TLS context to terminate, find matching services with TCP blocks - // and forward to backend - // Validation ensures that non-passthrough servers will have certs - if filters := buildGatewayNetworkFiltersFromTCPRoutes(node, - push, server, gatewayName); len(filters) > 0 { - return []*filterChainOpts{ - { - sniHosts: node.MergedGateway.TLSServerInfo[server].SNIHosts, - tlsContext: buildGatewayListenerTLSContext(server, node, istionetworking.TransportProtocolTCP), - networkFilters: filters, - }, - } - } - } else { - // Passthrough server. - return buildGatewayNetworkFiltersFromTLSRoutes(node, push, server, gatewayName) - } - - return []*filterChainOpts{} -} - -// buildGatewayNetworkFiltersFromTCPRoutes builds tcp proxy routes for all VirtualServices with TCP blocks. -// It first obtains all virtual services bound to the set of Gateways for this workload, filters them by this -// server's port and hostnames, and produces network filters for each destination from the filtered services. -func buildGatewayNetworkFiltersFromTCPRoutes(node *model.Proxy, push *model.PushContext, server *networking.Server, gateway string) []*listener.Filter { - port := &model.Port{ - Name: server.Port.Name, - Port: int(server.Port.Number), - Protocol: protocol.Parse(server.Port.Protocol), - } - - gatewayServerHosts := make(map[host.Name]bool, len(server.Hosts)) - for _, hostname := range server.Hosts { - gatewayServerHosts[host.Name(hostname)] = true - } - - virtualServices := push.VirtualServicesForGateway(node.ConfigNamespace, gateway) - if len(virtualServices) == 0 { - log.Warnf("no virtual service bound to gateway: %v", gateway) - } - for _, v := range virtualServices { - vsvc := v.Spec.(*networking.VirtualService) - // We have two cases here: - // 1. virtualService hosts are 1.foo.com, 2.foo.com, 3.foo.com and gateway's hosts are ns/*.foo.com - // 2. virtualService hosts are *.foo.com, and gateway's hosts are ns/1.foo.com, ns/2.foo.com, ns/3.foo.com - // Since this is TCP, neither matters. We are simply looking for matching virtual service for this gateway - matchingHosts := pickMatchingGatewayHosts(gatewayServerHosts, v) - if len(matchingHosts) == 0 { - // the VirtualService's hosts don't include hosts advertised by server - continue - } - - // ensure we satisfy the rule's l4 match conditions, if any exist - // For the moment, there can be only one match that succeeds - // based on the match port/server port and the gateway name - for _, tcp := range vsvc.Tcp { - if l4MultiMatch(tcp.Match, server, gateway) { - return buildOutboundNetworkFilters(node, tcp.Route, push, port, v.Meta) - } - } - } - - return nil -} - -// buildGatewayNetworkFiltersFromTLSRoutes builds tcp proxy routes for all VirtualServices with TLS blocks. -// It first obtains all virtual services bound to the set of Gateways for this workload, filters them by this -// server's port and hostnames, and produces network filters for each destination from the filtered services -func buildGatewayNetworkFiltersFromTLSRoutes(node *model.Proxy, push *model.PushContext, server *networking.Server, - gatewayName string) []*filterChainOpts { - port := &model.Port{ - Name: server.Port.Name, - Port: int(server.Port.Number), - Protocol: protocol.Parse(server.Port.Protocol), - } - - gatewayServerHosts := make(map[host.Name]bool, len(server.Hosts)) - for _, hostname := range server.Hosts { - gatewayServerHosts[host.Name(hostname)] = true - } - - filterChains := make([]*filterChainOpts, 0) - - if server.Tls.Mode == networking.ServerTLSSettings_AUTO_PASSTHROUGH { - if features.EnableLegacyAutoPassthrough { - // auto passthrough does not require virtual services. It sets up envoy.filters.network.sni_cluster filter - filterChains = append(filterChains, &filterChainOpts{ - sniHosts: node.MergedGateway.TLSServerInfo[server].SNIHosts, - tlsContext: nil, // NO TLS context because this is passthrough - networkFilters: buildOutboundAutoPassthroughFilterStack(push, node, port), - }) - } else { - filterChains = append(filterChains, builtAutoPassthroughFilterChains(push, node, node.MergedGateway.TLSServerInfo[server].SNIHosts)...) - } - } else { - tlsSniHosts := map[string]struct{}{} // sni host -> exists - - virtualServices := push.VirtualServicesForGateway(node.ConfigNamespace, gatewayName) - for _, v := range virtualServices { - vsvc := v.Spec.(*networking.VirtualService) - // We have two cases here: - // 1. virtualService hosts are 1.foo.com, 2.foo.com, 3.foo.com and gateway's hosts are ns/*.foo.com - // 2. virtualService hosts are *.foo.com, and gateway's hosts are ns/1.foo.com, ns/2.foo.com, ns/3.foo.com - // The code below only handles 1. - // TODO: handle case 2 - matchingHosts := pickMatchingGatewayHosts(gatewayServerHosts, v) - if len(matchingHosts) == 0 { - // the VirtualService's hosts don't include hosts advertised by server - continue - } - - // For every matching TLS block, generate a filter chain with sni match - // TODO: Bug..if there is a single virtual service with *.foo.com, and multiple TLS block - // matches, one for 1.foo.com, another for 2.foo.com, this code will produce duplicate filter - // chain matches - for _, tls := range vsvc.Tls { - for i, match := range tls.Match { - if l4SingleMatch(convertTLSMatchToL4Match(match), server, gatewayName) { - // Envoy will reject config that has multiple filter chain matches with the same matching rules - // To avoid this, we need to make sure we don't have duplicated SNI hosts, which will become - // SNI filter chain matches - if duplicateSniHosts := model.CheckDuplicates(match.SniHosts, tlsSniHosts); len(duplicateSniHosts) != 0 { - log.Debugf( - "skipping VirtualService %s rule #%v on server port %d of gateway %s, duplicate SNI host names: %v", - v.Meta.Name, i, port.Port, gatewayName, duplicateSniHosts) - model.RecordRejectedConfig(gatewayName) - continue - } - - // the sni hosts in the match will become part of a filter chain match - filterChains = append(filterChains, &filterChainOpts{ - sniHosts: match.SniHosts, - tlsContext: nil, // NO TLS context because this is passthrough - networkFilters: buildOutboundNetworkFilters(node, tls.Route, push, port, v.Meta), - }) - } - } - } - } - } - - return filterChains -} - -// builtAutoPassthroughFilterChains builds a set of filter chains for auto_passthrough gateway servers. -// These servers allow connecting to any SNI-DNAT upstream cluster that matches the server's hostname. -// To handle this, we generate a filter chain per upstream cluster -func builtAutoPassthroughFilterChains(push *model.PushContext, proxy *model.Proxy, hosts []string) []*filterChainOpts { - filterChains := make([]*filterChainOpts, 0) - for _, service := range proxy.SidecarScope.Services() { - if service.MeshExternal { - continue - } - for _, port := range service.Ports { - if port.Protocol == protocol.UDP { - continue - } - matchFound := false - for _, h := range hosts { - if service.Hostname.SubsetOf(host.Name(h)) { - matchFound = true - break - } - } - if !matchFound { - continue - } - clusterName := model.BuildDNSSrvSubsetKey(model.TrafficDirectionOutbound, "", service.Hostname, port.Port) - statPrefix := clusterName - if len(push.Mesh.OutboundClusterStatName) != 0 { - statPrefix = telemetry.BuildStatPrefix(push.Mesh.OutboundClusterStatName, string(service.Hostname), "", port, &service.Attributes) - } - destinationRule := CastDestinationRule(proxy.SidecarScope.DestinationRule( - model.TrafficDirectionOutbound, proxy, service.Hostname)) - - // First, we build the standard cluster. We match on the SNI matching the cluster name - // (per the spec of AUTO_PASSTHROUGH), as well as all possible Istio mTLS ALPNs. This, - // along with filtering out plaintext destinations in EDS, ensures that our requests will - // always hit an Istio mTLS filter chain on the inbound side. As a result, it should not - // be possible for anyone to access a cluster without mTLS. Note that we cannot actually - // check for mTLS here, as we are doing passthrough TLS. - filterChains = append(filterChains, &filterChainOpts{ - sniHosts: []string{clusterName}, - match: &listener.FilterChainMatch{ApplicationProtocols: allIstioMtlsALPNs}, - tlsContext: nil, // NO TLS context because this is passthrough - networkFilters: buildOutboundNetworkFiltersWithSingleDestination(push, proxy, statPrefix, clusterName, "", port, destinationRule), - }) - - // Do the same, but for each subset - for _, subset := range destinationRule.GetSubsets() { - subsetClusterName := model.BuildDNSSrvSubsetKey(model.TrafficDirectionOutbound, subset.Name, service.Hostname, port.Port) - subsetStatPrefix := subsetClusterName - // If stat name is configured, build the stat prefix from configured pattern. - if len(push.Mesh.OutboundClusterStatName) != 0 { - subsetStatPrefix = telemetry.BuildStatPrefix(push.Mesh.OutboundClusterStatName, string(service.Hostname), subset.Name, port, &service.Attributes) - } - filterChains = append(filterChains, &filterChainOpts{ - sniHosts: []string{subsetClusterName}, - match: &listener.FilterChainMatch{ApplicationProtocols: allIstioMtlsALPNs}, - tlsContext: nil, // NO TLS context because this is passthrough - networkFilters: buildOutboundNetworkFiltersWithSingleDestination(push, proxy, subsetStatPrefix, subsetClusterName, subset.Name, port, destinationRule), - }) - } - } - } - return filterChains -} - -// Select the virtualService's hosts that match the ones specified in the gateway server's hosts -// based on the wildcard hostname match and the namespace match -func pickMatchingGatewayHosts(gatewayServerHosts map[host.Name]bool, virtualService config.Config) map[string]host.Name { - matchingHosts := make(map[string]host.Name) - virtualServiceHosts := virtualService.Spec.(*networking.VirtualService).Hosts - for _, vsvcHost := range virtualServiceHosts { - for gatewayHost := range gatewayServerHosts { - gwHostnameForMatching := gatewayHost - if strings.Contains(string(gwHostnameForMatching), "/") { - // match the namespace first - // gateway merging code ensures that we only have ns/host - // and no ./* or */host - parts := strings.Split(string(gwHostnameForMatching), "/") - if parts[0] != virtualService.Namespace { - continue - } - // strip the namespace - gwHostnameForMatching = host.Name(parts[1]) - } - if gwHostnameForMatching.Matches(host.Name(vsvcHost)) { - // assign the actual gateway host because calling code uses it as a key - // to locate TLS redirect servers - matchingHosts[vsvcHost] = gatewayHost - } - } - } - return matchingHosts -} - -func convertTLSMatchToL4Match(tlsMatch *networking.TLSMatchAttributes) *networking.L4MatchAttributes { - return &networking.L4MatchAttributes{ - DestinationSubnets: tlsMatch.DestinationSubnets, - Port: tlsMatch.Port, - SourceLabels: tlsMatch.SourceLabels, - Gateways: tlsMatch.Gateways, - SourceNamespace: tlsMatch.SourceNamespace, - } -} - -func l4MultiMatch(predicates []*networking.L4MatchAttributes, server *networking.Server, gateway string) bool { - // NB from proto definitions: each set of predicates is OR'd together; inside of a predicate all conditions are AND'd. - // This means we can return as soon as we get any match of an entire predicate. - for _, match := range predicates { - if l4SingleMatch(match, server, gateway) { - return true - } - } - // If we had no predicates we match; otherwise we don't match since we'd have exited at the first match. - return len(predicates) == 0 -} - -func l4SingleMatch(match *networking.L4MatchAttributes, server *networking.Server, gateway string) bool { - // if there's no gateway predicate, gatewayMatch is true; otherwise we match against the gateways for this workload - return isPortMatch(match.Port, server) && isGatewayMatch(gateway, match.Gateways) -} - -func isPortMatch(port uint32, server *networking.Server) bool { - // if there's no port predicate, portMatch is true; otherwise we evaluate the port predicate against the server's port - portMatch := port == 0 - if port != 0 { - portMatch = server.Port.Number == port - } - return portMatch -} - -func isGatewayMatch(gateway string, gatewayNames []string) bool { - // if there's no gateway predicate, gatewayMatch is true; otherwise we match against the gateways for this workload - if len(gatewayNames) == 0 { - return true - } - if len(gatewayNames) > 0 { - for _, gatewayName := range gatewayNames { - if gatewayName == gateway { - return true - } - } - } - return false -} - -func buildGatewayVirtualHostDomains(hostname string, port int) []string { - domains := []string{hostname} - if features.StripHostPort || hostname == "*" { - return domains - } - - // Per https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#config-route-v3-virtualhost - // we can only have one wildcard. Ideally, we want to match any port, as the host - // header may have a different port (behind a LB, nodeport, etc). However, if we - // have a wildcard domain we cannot do that since we would need two wildcards. - // Therefore, we we will preserve the original port if there is a wildcard host. - // TODO(https://github.com/envoyproxy/envoy/issues/12647) support wildcard host with wildcard port. - if len(hostname) > 0 && hostname[0] == '*' { - domains = append(domains, util.DomainName(hostname, port)) - } else { - domains = append(domains, util.IPv6Compliant(hostname)+":*") - } - return domains -} - -// Invalid cipher suites lead Envoy to NACKing. This filters the list down to just the supported set. -func filteredGatewayCipherSuites(server *networking.Server) []string { - suites := server.Tls.CipherSuites - ret := make([]string, 0, len(suites)) - validCiphers := sets.New() - for _, s := range suites { - if security.IsValidCipherSuite(s) { - if !validCiphers.Contains(s) { - ret = append(ret, s) - validCiphers = validCiphers.Insert(s) - } else if log.DebugEnabled() { - log.Debugf("ignoring duplicated cipherSuite: %q for server %s", s, server.String()) - } - } else if log.DebugEnabled() { - log.Debugf("ignoring unsupported cipherSuite: %q for server %s", s, server.String()) - } - } - return ret -} diff --git a/pilot/pkg/networking/core/v1alpha3/gateway_simulation_test.go b/pilot/pkg/networking/core/v1alpha3/gateway_simulation_test.go deleted file mode 100644 index b905efd5d..000000000 --- a/pilot/pkg/networking/core/v1alpha3/gateway_simulation_test.go +++ /dev/null @@ -1,1905 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3_test - -import ( - "testing" -) - -import ( - "istio.io/pkg/env" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/simulation" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/tmpl" -) - -func TestDisablePortTranslation(t *testing.T) { - virtualServices := ` -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: a -spec: - hosts: - - "example.com" - gateways: - - gateway80 - - gateway8081 - http: - - match: - - uri: - prefix: / - route: - - destination: - host: a - port: - number: 80 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: b -spec: - hosts: - - "example.com" - gateways: - - gateway80 - - gateway8081 - http: - - match: - - uri: - prefix: / - route: - - destination: - host: b - port: - number: 8081` - runGatewayTest(t, - simulationTest{ - name: "multiple target port, target port first", - config: createGateway("gateway80", "", ` -port: - number: 80 - name: http - protocol: HTTP -hosts: -- "example.com" -`) + createGateway("gateway8081", "", ` -port: - number: 8081 - name: http - protocol: HTTP -hosts: -- "example.com" -`) + ` ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-instance - namespace: dubbo-system - labels: - experimental.istio.io/disable-gateway-port-translation: "true" -spec: - hosts: ["a.example.com"] - ports: - - number: 80 - targetPort: 8081 - name: http - protocol: HTTP - resolution: STATIC - location: MESH_INTERNAL - endpoints: - - address: 1.1.1.1 - labels: - istio: ingressgateway ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-instance-2 - namespace: dubbo-system -spec: - hosts: ["b.example.com"] - ports: - - number: 80 - targetPort: 8080 - name: http - protocol: HTTP - resolution: STATIC - location: MESH_INTERNAL - endpoints: - - address: 1.1.1.1 - labels: - istio: ingressgateway ---- -` + virtualServices, - calls: []simulation.Expect{ - { - Name: "target port 1", - Call: simulation.Call{ - Port: 8080, - HostHeader: "example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_8080", - ClusterMatched: "outbound|80||a.default", - RouteConfigMatched: "http.8080", - VirtualHostMatched: "example.com:80", - }, - }, - { - Name: "target port 2", - Call: simulation.Call{ - Port: 8081, - HostHeader: "example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_8081", - ClusterMatched: "outbound|80||a.default", - RouteConfigMatched: "http.8081", - VirtualHostMatched: "example.com:8081", - }, - }, - }, - }, - simulationTest{ - name: "multiple target port, service port first", - config: createGateway("gateway80", "", ` -port: - number: 80 - name: http - protocol: HTTP -hosts: -- "example.com" -`) + createGateway("gateway8081", "", ` -port: - number: 8081 - name: http - protocol: HTTP -hosts: -- "example.com" -`) + ` ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-instance - namespace: dubbo-system - labels: - experimental.istio.io/disable-gateway-port-translation: "true" -spec: - hosts: ["b.example.com"] - ports: - - number: 80 - targetPort: 8081 - name: http - protocol: HTTP - resolution: STATIC - location: MESH_INTERNAL - endpoints: - - address: 1.1.1.1 - labels: - istio: ingressgateway ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-instance-2 - namespace: dubbo-system -spec: - hosts: ["a.example.com"] - ports: - - number: 80 - targetPort: 8080 - name: http - protocol: HTTP - resolution: STATIC - location: MESH_INTERNAL - endpoints: - - address: 1.1.1.1 - labels: - istio: ingressgateway ---- -` + virtualServices, - calls: []simulation.Expect{ - { - Name: "target port 1", - Call: simulation.Call{ - Port: 8080, - HostHeader: "example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_8080", - ClusterMatched: "outbound|80||a.default", - RouteConfigMatched: "http.8080", - VirtualHostMatched: "example.com:80", - }, - }, - { - Name: "target port 2", - Call: simulation.Call{ - Port: 8081, - HostHeader: "example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_8081", - ClusterMatched: "outbound|80||a.default", - RouteConfigMatched: "http.8081", - VirtualHostMatched: "example.com:8081", - }, - }, - }, - }) -} - -func TestHTTPGateway(t *testing.T) { - httpServer := `port: - number: 80 - name: http - protocol: HTTP -hosts: -- "foo.bar"` - simpleRoute := `apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: vs -spec: - hosts: - - "example.com" - gateways: - - gateway - http: - - match: - - uri: - prefix: / - route: - - destination: - host: b ---- -` - runGatewayTest(t, - simulationTest{ - name: "no virtual services", - config: createGateway("", "", httpServer), - calls: []simulation.Expect{ - { - // Expect listener, but no routing - Name: "defined port", - Call: simulation.Call{ - Port: 80, - HostHeader: "foo.bar", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - Error: simulation.ErrNoRoute, - ListenerMatched: "0.0.0.0_80", - RouteConfigMatched: "http.80", - VirtualHostMatched: "blackhole:80", - }, - }, - { - // There will be no listener - Name: "undefined port", - Call: simulation.Call{ - Port: 81, - HostHeader: "foo.bar", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - Error: simulation.ErrNoListener, - }, - }, - }, - }, - simulationTest{ - name: "simple http and virtual service", - config: createGateway("gateway", "", httpServer) + ` -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: bookinfo -spec: - hosts: - - "*" - gateways: - - gateway - http: - - match: - - uri: - exact: /productpage - route: - - destination: - host: productpage - port: - number: 9080 -`, - calls: []simulation.Expect{ - { - Name: "uri mismatch", - Call: simulation.Call{ - Port: 80, - HostHeader: "foo.bar", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - // We didn't match the URI - Error: simulation.ErrNoRoute, - ListenerMatched: "0.0.0.0_80", - RouteConfigMatched: "http.80", - VirtualHostMatched: "foo.bar:80", - }, - }, - { - Name: "host mismatch", - Call: simulation.Call{ - Port: 80, - HostHeader: "bad.bar", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - // We didn't match the host - Error: simulation.ErrNoVirtualHost, - ListenerMatched: "0.0.0.0_80", - RouteConfigMatched: "http.80", - }, - }, - { - Name: "match", - Call: simulation.Call{ - Port: 80, - HostHeader: "foo.bar", - Path: "/productpage", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_80", - VirtualHostMatched: "foo.bar:80", - ClusterMatched: "outbound|9080||productpage.default", - }, - }, - }, - }, - simulationTest{ - name: "virtual service merging", - config: createGateway("gateway", "", `port: - number: 80 - name: http - protocol: HTTP -hosts: -- "*.example.com"`) + ` -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: a -spec: - hosts: - - "a.example.com" - gateways: - - gateway - http: - - match: - - uri: - prefix: / - route: - - destination: - host: a - port: - number: 80 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: b -spec: - hosts: - - "b.example.com" - gateways: - - gateway - http: - - match: - - uri: - prefix: / - route: - - destination: - host: b - port: - number: 80 -`, - calls: []simulation.Expect{ - { - Name: "a", - Call: simulation.Call{ - Port: 80, - HostHeader: "a.example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ClusterMatched: "outbound|80||a.default"}, - }, - { - Name: "b", - Call: simulation.Call{ - Port: 80, - HostHeader: "b.example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ClusterMatched: "outbound|80||b.default"}, - }, - { - Name: "undefined hostname", - Call: simulation.Call{ - Port: 80, - HostHeader: "c.example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{Error: simulation.ErrNoVirtualHost}, - }, - }, - }, - simulationTest{ - name: "httpsRedirect without routes", - config: createGateway("gateway", "", `port: - number: 80 - name: http - protocol: HTTP -hosts: -- "example.com" -tls: - httpsRedirect: true`), - calls: []simulation.Expect{ - { - Name: "request", - Call: simulation.Call{ - Port: 80, - HostHeader: "example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - Error: simulation.ErrTLSRedirect, - ListenerMatched: "0.0.0.0_80", - VirtualHostMatched: "example.com:80", - RouteConfigMatched: "http.80", - StrictMatch: true, - }, - }, - }, - }, - simulationTest{ - // This should be the same as without, we elide the routes anyways since they always - // redirect - name: "httpsRedirect with routes", - config: createGateway("gateway", "", `port: - number: 80 - name: http - protocol: HTTP -hosts: -- "example.com" -tls: - httpsRedirect: true`) + simpleRoute, - calls: []simulation.Expect{ - { - Name: "request", - Call: simulation.Call{ - Port: 80, - HostHeader: "example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - Error: simulation.ErrTLSRedirect, - ListenerMatched: "0.0.0.0_80", - VirtualHostMatched: "example.com:80", - RouteConfigMatched: "http.80", - StrictMatch: true, - }, - }, - }, - }, - simulationTest{ - // A common example of when this would occur is when a user defines their Gateway with - // httpsRedirect, then cert-manager creates an Ingress which requires sending HTTP - // traffic Unfortunately, Envoy doesn't have a good way to do HTTPS redirect per-route. - name: "mixed httpsRedirect with routes", - config: createGateway("gateway", "", `port: - number: 80 - name: http - protocol: HTTP -hosts: -- "example.com" -tls: - httpsRedirect: true`) + createGateway("gateway2", "", `port: - number: 80 - name: http - protocol: HTTP -hosts: -- "example.com"`) + simpleRoute, - calls: []simulation.Expect{ - { - Name: "request", - Call: simulation.Call{ - Port: 80, - HostHeader: "example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - Error: simulation.ErrTLSRedirect, - ListenerMatched: "0.0.0.0_80", - VirtualHostMatched: "example.com:80", - RouteConfigMatched: "http.80", - StrictMatch: true, - }, - }, - }, - }, - simulationTest{ - name: "httpsRedirect on https", - config: createGateway("gateway", "", `port: - number: 443 - name: https - protocol: HTTPS -hosts: -- "example.com" -tls: - httpsRedirect: true - mode: SIMPLE - credentialName: test`) + simpleRoute, - calls: []simulation.Expect{ - { - Name: "request", - Call: simulation.Call{ - Port: 443, - HostHeader: "example.com", - Protocol: simulation.HTTP, - TLS: simulation.TLS, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_443", - VirtualHostMatched: "example.com:443", - RouteConfigMatched: "https.443.https.gateway.default", - ClusterMatched: "outbound|443||b.default", - StrictMatch: true, - }, - }, - }, - }, - ) -} - -func TestGatewayConflicts(t *testing.T) { - tcpServer := `port: - number: 80 - name: tcp - protocol: TCP -hosts: -- "foo.bar"` - httpServer := `port: - number: 80 - name: http - protocol: HTTP -hosts: -- "foo.bar"` - tlsServer := `hosts: - - ./* -port: - name: https-ingress - number: 443 - protocol: HTTPS -tls: - credentialName: sds-credential - mode: SIMPLE` - gatewayCollision := ` -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: bookinfo -spec: - hosts: - - "*" - gateways: - - dubbo-system/gateway - tcp: - - route: - - destination: - host: productpage - port: - number: 9080 -` - runGatewayTest(t, - simulationTest{ - name: "duplicate cross namespace gateway collision", - config: createGateway("gateway", "dubbo-system", tcpServer) + - createGateway("gateway", "alpha", tcpServer) + // namespace comes before dubbo-system - - gatewayCollision, - calls: []simulation.Expect{ - { - Name: "call", - Call: simulation.Call{Port: 80, Protocol: simulation.TCP}, - // TODO(https://github.com/istio/istio/issues/21394) This is a bug! - // Should have identical result to the test below - Result: simulation.Result{Error: simulation.ErrNoListener}, - }, - }, - }, - simulationTest{ - name: "duplicate cross namespace gateway collision - selected first", - config: createGateway("gateway", "dubbo-system", tcpServer) + - createGateway("gateway", "zeta", tcpServer) + // namespace comes after dubbo-system - gatewayCollision, - calls: []simulation.Expect{ - { - Name: "call", - Call: simulation.Call{Port: 80, Protocol: simulation.TCP}, - Result: simulation.Result{ListenerMatched: "0.0.0.0_80", ClusterMatched: "outbound|9080||productpage.default"}, - }, - }, - }, - simulationTest{ - name: "duplicate tls gateway", - skipValidation: true, - // Create the same gateway in two namespaces - config: createGateway("", "dubbo-system", tlsServer) + - createGateway("", "default", tlsServer), - calls: []simulation.Expect{ - { - // TODO(https://github.com/istio/istio/issues/24638) This is a bug! - // We should not have multiple matches, envoy will NACK this - Name: "call", - Call: simulation.Call{Port: 443, Protocol: simulation.HTTP, TLS: simulation.TLS, HostHeader: "foo.bar"}, - Result: simulation.Result{Error: simulation.ErrMultipleFilterChain}, - }, - }, - }, - simulationTest{ - name: "duplicate tls virtual service", - // Create the same virtual service in two namespaces - config: ` -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: ingressgateway - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - '*.example.com' - port: - name: https - number: 443 - protocol: HTTPS - tls: - mode: PASSTHROUGH ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: vs1 - namespace: default -spec: - gateways: - - dubbo-system/ingressgateway - hosts: - - mysite.example.com - tls: - - match: - - port: 443 - sniHosts: - - mysite.example.com - route: - - destination: - host: mysite.default.svc.cluster.local - port: - number: 443 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: vs2 - namespace: default -spec: - gateways: - - dubbo-system/ingressgateway - hosts: - - mysite.example.com - tls: - - match: - - port: 443 - sniHosts: - - mysite.example.com - route: - - destination: - host: mysite.default.svc.cluster.local - port: - number: 443 -`, - calls: []simulation.Expect{ - { - Name: "call", - Call: simulation.Call{Port: 443, Protocol: simulation.HTTP, TLS: simulation.TLS, HostHeader: "mysite.example.com"}, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_443", - ClusterMatched: "outbound|443||mysite.default.svc.cluster.local", - }, - }, - }, - }, - simulationTest{ - // TODO(https://github.com/istio/istio/issues/27481) this may be a bug. At very least, this should have indication to user - name: "multiple protocols on a port - tcp first", - config: createGateway("alpha", "", tcpServer) + - createGateway("beta", "", httpServer), - calls: []simulation.Expect{ - { - Name: "call tcp", - // TCP takes precedence. Since we have no tcp routes, this will result in no listeners - Call: simulation.Call{ - Port: 80, - Protocol: simulation.TCP, - }, - Result: simulation.Result{ - Error: simulation.ErrNoListener, - }, - }, - }, - }, - simulationTest{ - // TODO(https://github.com/istio/istio/issues/27481) this may be a bug. At very least, this should have indication to user - name: "multiple protocols on a port - http first", - config: createGateway("beta", "", tcpServer) + - createGateway("alpha", "", httpServer), - calls: []simulation.Expect{ - { - // Port define in gateway, but no virtual services - // Expect a 404 - // HTTP protocol takes precedence - Name: "call http", - Call: simulation.Call{ - Port: 80, - HostHeader: "foo.bar", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - Error: simulation.ErrNoRoute, - ListenerMatched: "0.0.0.0_80", - RouteConfigMatched: "http.80", - VirtualHostMatched: "blackhole:80", - }, - }, - }, - }, - simulationTest{ - name: "multiple wildcards with virtual service disambiguator", - config: createGateway("alpha", "", ` -hosts: - - ns-1/*.example.com -port: - name: http - number: 80 - protocol: HTTP`) + - createGateway("beta", "", ` -hosts: - - ns-2/*.example.com -port: - name: http - number: 80 - protocol: HTTP`) + ` -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: default - namespace: ns-1 -spec: - hosts: - - "ns-1.example.com" - gateways: - - default/alpha - http: - - route: - - destination: - host: echo ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: default - namespace: ns-2 -spec: - hosts: - - "ns-2.example.com" - gateways: - - default/beta - http: - - route: - - destination: - host: echo -`, - calls: []simulation.Expect{ - { - Name: "ns-1", - Call: simulation.Call{ - Port: 80, - HostHeader: "ns-1.example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_80", - RouteConfigMatched: "http.80", - ClusterMatched: "outbound|80||echo.ns-1", - }, - }, - { - Name: "ns-2", - Call: simulation.Call{ - Port: 80, - HostHeader: "ns-2.example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_80", - RouteConfigMatched: "http.80", - ClusterMatched: "outbound|80||echo.ns-2", - }, - }, - }, - }, - ) -} - -func TestIngress(t *testing.T) { - cfg := ` -apiVersion: networking.k8s.io/v1beta1 -kind: Ingress -metadata: - name: {{.Name}} - namespace: default - creationTimestamp: "{{.Time}}" - annotations: - kubernetes.io/ingress.class: istio -spec: - rules: - - host: example.com - http: - paths: - - backend: - serviceName: {{.Name}} - servicePort: 80 - path: /{{.Name}} - tls: - - hosts: - - example.com - secretName: ingressgateway-certs ----` - runGatewayTest(t, simulationTest{ - name: "ingress shared TLS cert conflict - beta first", - // TODO(https://github.com/istio/istio/issues/24385) this is a bug - // "alpha" is created after "beta". This triggers a mismatch in the conflict resolution logic in Ingress and VirtualService, leading to unexpected results - kubeConfig: tmpl.MustEvaluate(cfg, map[string]string{"Name": "alpha", "Time": "2020-01-01T00:00:00Z"}) + - tmpl.MustEvaluate(cfg, map[string]string{"Name": "beta", "Time": "2010-01-01T00:00:00Z"}), - calls: []simulation.Expect{ - { - Name: "http alpha", - Call: simulation.Call{ - Port: 80, - HostHeader: "example.com", - Path: "/alpha", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_80", - RouteConfigMatched: "http.80", - ClusterMatched: "outbound|80||alpha.default.svc.cluster.local", - }, - }, - { - Name: "http beta", - Call: simulation.Call{ - Port: 80, - HostHeader: "example.com", - Path: "/beta", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_80", - RouteConfigMatched: "http.80", - ClusterMatched: "outbound|80||beta.default.svc.cluster.local", - }, - }, - { - Name: "https alpha", - Call: simulation.Call{ - Port: 443, - HostHeader: "example.com", - Path: "/alpha", - Protocol: simulation.HTTP, - TLS: simulation.TLS, - }, - Result: simulation.Result{ - Error: simulation.ErrNoRoute, - ListenerMatched: "0.0.0.0_443", - RouteConfigMatched: "https.443.https-443-ingress-alpha-default-0.alpha-istio-autogenerated-k8s-ingress-default.dubbo-system", - VirtualHostMatched: "blackhole:443", - }, - }, - { - Name: "https beta", - Call: simulation.Call{ - Port: 443, - HostHeader: "example.com", - Path: "/beta", - Protocol: simulation.HTTP, - TLS: simulation.TLS, - }, - Result: simulation.Result{ - Error: simulation.ErrNoRoute, - ListenerMatched: "0.0.0.0_443", - RouteConfigMatched: "https.443.https-443-ingress-alpha-default-0.alpha-istio-autogenerated-k8s-ingress-default.dubbo-system", - VirtualHostMatched: "blackhole:443", - }, - }, - }, - }, simulationTest{ - name: "ingress shared TLS cert conflict - alpha first", - // "alpha" is created before "beta". This avoids the bug in the previous test - kubeConfig: tmpl.MustEvaluate(cfg, map[string]string{"Name": "alpha", "Time": "2010-01-01T00:00:00Z"}) + - tmpl.MustEvaluate(cfg, map[string]string{"Name": "beta", "Time": "2020-01-01T00:00:00Z"}), - calls: []simulation.Expect{ - { - Name: "http alpha", - Call: simulation.Call{ - Port: 80, - HostHeader: "example.com", - Path: "/alpha", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_80", - RouteConfigMatched: "http.80", - ClusterMatched: "outbound|80||alpha.default.svc.cluster.local", - }, - }, - { - Name: "http beta", - Call: simulation.Call{ - Port: 80, - HostHeader: "example.com", - Path: "/beta", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_80", - RouteConfigMatched: "http.80", - ClusterMatched: "outbound|80||beta.default.svc.cluster.local", - }, - }, - { - Name: "https alpha", - Call: simulation.Call{ - Port: 443, - HostHeader: "example.com", - Path: "/alpha", - Protocol: simulation.HTTP, - TLS: simulation.TLS, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_443", - RouteConfigMatched: "https.443.https-443-ingress-alpha-default-0.alpha-istio-autogenerated-k8s-ingress-default.dubbo-system", - ClusterMatched: "outbound|80||alpha.default.svc.cluster.local", - }, - }, - { - Name: "https beta", - Call: simulation.Call{ - Port: 443, - HostHeader: "example.com", - Path: "/beta", - Protocol: simulation.HTTP, - TLS: simulation.TLS, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_443", - RouteConfigMatched: "https.443.https-443-ingress-alpha-default-0.alpha-istio-autogenerated-k8s-ingress-default.dubbo-system", - ClusterMatched: "outbound|80||beta.default.svc.cluster.local", - }, - }, - }, - }) -} - -type simulationTest struct { - name string - config string - kubeConfig string - // skipValidation disables validation of XDS resources. Should be used only when we expect a failure (regression catching) - skipValidation bool - calls []simulation.Expect -} - -var debugMode = env.RegisterBoolVar("SIMULATION_DEBUG", true, "if enabled, will dump verbose output").Get() - -func runGatewayTest(t *testing.T, cases ...simulationTest) { - for _, tt := range cases { - proxy := &model.Proxy{ - Metadata: &model.NodeMetadata{ - Labels: map[string]string{"istio": "ingressgateway"}, - Namespace: "dubbo-system", - }, - Type: model.Router, - } - runSimulationTest(t, proxy, xds.FakeOptions{}, tt) - } -} - -func runSimulationTest(t *testing.T, proxy *model.Proxy, o xds.FakeOptions, tt simulationTest) { - runTest := func(t *testing.T) { - o.ConfigString = tt.config - o.KubernetesObjectString = tt.kubeConfig - s := xds.NewFakeDiscoveryServer(t, o) - sim := simulation.NewSimulation(t, s, s.SetupProxy(proxy)) - sim.RunExpectations(tt.calls) - if t.Failed() && debugMode { - t.Log(xdstest.MapKeys(xdstest.ExtractClusters(sim.Clusters))) - t.Log(xdstest.ExtractListenerNames(sim.Listeners)) - t.Log(xdstest.DumpList(t, xdstest.InterfaceSlice(sim.Listeners))) - t.Log(xdstest.DumpList(t, xdstest.InterfaceSlice(sim.Routes))) - t.Log(tt.config) - t.Log(tt.kubeConfig) - } - t.Run("validate configs", func(t *testing.T) { - if tt.skipValidation { - t.Skip() - } - xdstest.ValidateClusters(t, sim.Clusters) - xdstest.ValidateListeners(t, sim.Listeners) - xdstest.ValidateRouteConfigurations(t, sim.Routes) - }) - } - if tt.name != "" { - t.Run(tt.name, runTest) - } else { - runTest(t) - } -} - -func createGateway(name, namespace string, servers ...string) string { - if name == "" { - name = "default" - } - if namespace == "" { - namespace = "default" - } - return tmpl.MustEvaluate(`apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: "{{.Name}}" - namespace: "{{.Namespace}}" -spec: - selector: - istio: ingressgateway - servers: -{{- range $i, $p := $.Servers }} - - -{{$p | trim | indent 4}} -{{- end }} ---- -`, struct { - Name string - Namespace string - Servers []string - }{name, namespace, servers}) -} - -func createGatewayWithServiceSelector(name, service string, servers ...string) string { - if name == "" { - name = "default" - } - return tmpl.MustEvaluate(`apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: "{{.Name}}" - namespace: "dubbo-system" - annotations: - internal.istio.io/gateway-service: "{{.Service}}" -spec: - servers: -{{- range $i, $p := $.Servers }} - - -{{$p | trim | indent 4}} -{{- end }} ---- -`, struct { - Name string - Service string - Servers []string - }{name, service, servers}) -} - -func TestTargetPort(t *testing.T) { - runGatewayTest(t, - simulationTest{ - name: "basic", - config: createGatewayWithServiceSelector("gateway80", "istio-ingressgateway.dubbo-system.svc.cluster.local", ` -port: - number: 80 - name: http - protocol: HTTP -hosts: -- "example.com" -`) + createGatewayWithServiceSelector("gateway81", "istio-ingressgateway.dubbo-system.svc.cluster.local", ` -port: - number: 8080 - name: http - protocol: HTTP -hosts: -- "example.com" -`) + ` ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-instance - namespace: dubbo-system -spec: - hosts: ["istio-ingressgateway.dubbo-system.svc.cluster.local"] - ports: - - number: 80 - targetPort: 8080 - name: http - protocol: HTTP - resolution: STATIC - location: MESH_INTERNAL - endpoints: - - address: 1.1.1.1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: a -spec: - hosts: - - "example.com" - gateways: - - dubbo-system/gateway80 - - dubbo-system/gateway81 - http: - - match: - - uri: - prefix: / - route: - - destination: - host: a - port: - number: 80`, - calls: []simulation.Expect{ - { - Call: simulation.Call{ - Port: 8080, - HostHeader: "example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_8080", - ClusterMatched: "outbound|80||a.default", - RouteConfigMatched: "http.8080", - VirtualHostMatched: "example.com:80", - }, - }, - }, - }, - simulationTest{ - name: "multiple target port", - config: createGatewayWithServiceSelector("gateway80", "ingress1.dubbo-system.svc.cluster.local", ` -port: - number: 80 - name: http - protocol: HTTP -hosts: -- "example.com" -`) + createGatewayWithServiceSelector("gateway81", "ingress2.dubbo-system.svc.cluster.local", ` -port: - number: 80 - name: http - protocol: HTTP -hosts: -- "example.com" -`) + ` ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-instance - namespace: dubbo-system -spec: - hosts: ["ingress1.dubbo-system.svc.cluster.local"] - ports: - - number: 80 - targetPort: 8080 - name: http - protocol: HTTP - resolution: STATIC - location: MESH_INTERNAL - endpoints: - - address: 1.1.1.1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-instance-2 - namespace: dubbo-system -spec: - hosts: ["ingress2.dubbo-system.svc.cluster.local"] - ports: - - number: 80 - targetPort: 8081 - name: http - protocol: HTTP - resolution: STATIC - location: MESH_INTERNAL - endpoints: - - address: 1.1.1.1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: a -spec: - hosts: - - "example.com" - gateways: - - dubbo-system/gateway80 - - dubbo-system/gateway81 - http: - - match: - - uri: - prefix: / - route: - - destination: - host: a - port: - number: 80 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: b -spec: - hosts: - - "example.com" - gateways: - - dubbo-system/gateway80 - - dubbo-system/gateway81 - http: - - match: - - uri: - prefix: / - route: - - destination: - host: b - port: - number: 8081`, - calls: []simulation.Expect{ - { - Name: "target port 1", - Call: simulation.Call{ - Port: 8080, - HostHeader: "example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_8080", - ClusterMatched: "outbound|80||a.default", - RouteConfigMatched: "http.8080", - VirtualHostMatched: "example.com:80", - }, - }, - { - Name: "target port 2", - Call: simulation.Call{ - Port: 8081, - HostHeader: "example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_8081", - ClusterMatched: "outbound|80||a.default", - RouteConfigMatched: "http.8081", - VirtualHostMatched: "example.com:80", - }, - }, - }, - }) -} - -func TestGatewayServices(t *testing.T) { - runGatewayTest(t, - simulationTest{ - name: "single service", - config: createGatewayWithServiceSelector("gateway", "istio-ingressgateway.dubbo-system.svc.cluster.local", ` -port: - number: 80 - name: http-80 - protocol: HTTP -hosts: -- "80.example.com" -`, ` -port: - number: 82 - name: http-82 - protocol: HTTP -hosts: -- "82.example.com" -`) + ` ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-instance - namespace: dubbo-system -spec: - hosts: ["istio-ingressgateway.dubbo-system.svc.cluster.local"] - ports: - - number: 80 - targetPort: 8080 - name: http - protocol: HTTP - resolution: STATIC - location: MESH_INTERNAL - endpoints: - - address: 1.1.1.1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: a -spec: - hosts: - - "*.example.com" - gateways: - - dubbo-system/gateway - http: - - match: - - uri: - prefix: / - route: - - destination: - host: a - port: - number: 80`, - calls: []simulation.Expect{ - { - Name: "port 8080", - Call: simulation.Call{ - Port: 8080, - HostHeader: "80.example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_8080", - ClusterMatched: "outbound|80||a.default", - RouteConfigMatched: "http.8080", - VirtualHostMatched: "80.example.com:80", - }, - }, - { - Name: "no service match", - Call: simulation.Call{ - Port: 82, - HostHeader: "81.example.com", - Protocol: simulation.HTTP, - }, - // For gateway service reference, if there is no matching port we do not setup a listener - Result: simulation.Result{ - Error: simulation.ErrNoListener, - }, - }, - }, - }, - simulationTest{ - name: "wrong namespace", - config: createGatewayWithServiceSelector("gateway", "istio-ingressgateway.not-dubbo-system.svc.cluster.local", ` -port: - number: 80 - name: http-80 - protocol: HTTP -hosts: -- "80.example.com" -`) + ` ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-instance - namespace: not-dubbo-system -spec: - hosts: ["istio-ingressgateway.not-dubbo-system.svc.cluster.local"] - ports: - - number: 80 - name: http - protocol: HTTP - resolution: STATIC - location: MESH_INTERNAL - endpoints: - - address: 1.1.1.1 ----`, - calls: []simulation.Expect{ - { - Name: "port 80", - Call: simulation.Call{ - Port: 80, - HostHeader: "80.example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - // Service selected is in another namespace, we cannot cross namespace boundary - Error: simulation.ErrNoListener, - }, - }, - }, - }, - simulationTest{ - name: "multiple services", - config: createGatewayWithServiceSelector("gateway", "istio-ingressgateway.dubbo-system.svc.cluster.local,ingress.com", ` -port: - number: 80 - name: http-80 - protocol: HTTP -hosts: -- "80.example.com" -`, ` -port: - number: 81 - name: http-81 - protocol: HTTP -hosts: -- "81.example.com" -`, ` -port: - number: 82 - name: http-82 - protocol: HTTP -hosts: -- "82.example.com" -`) + ` ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-instance - namespace: dubbo-system -spec: - hosts: ["istio-ingressgateway.dubbo-system.svc.cluster.local"] - ports: - - number: 80 - targetPort: 8080 - name: http - protocol: HTTP - resolution: STATIC - location: MESH_INTERNAL - endpoints: - - address: 1.1.1.1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-instance2 - namespace: dubbo-system -spec: - hosts: ["ingress.com"] - ports: - - number: 81 - targetPort: 8081 - name: http - protocol: HTTP - resolution: STATIC - location: MESH_INTERNAL - endpoints: - - address: 1.1.1.1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: a -spec: - hosts: - - "*.example.com" - gateways: - - dubbo-system/gateway - http: - - match: - - uri: - prefix: / - route: - - destination: - host: a - port: - number: 80`, - calls: []simulation.Expect{ - { - Name: "port 8080", - Call: simulation.Call{ - Port: 8080, - HostHeader: "80.example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_8080", - ClusterMatched: "outbound|80||a.default", - RouteConfigMatched: "http.8080", - VirtualHostMatched: "80.example.com:80", - }, - }, - { - Name: "port 8081", - Call: simulation.Call{ - Port: 8081, - HostHeader: "81.example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_8081", - ClusterMatched: "outbound|80||a.default", - RouteConfigMatched: "http.8081", - VirtualHostMatched: "81.example.com:81", - }, - }, - { - // For gateway service reference, if there is no matching port we do not setup a listener - Name: "no service match", - Call: simulation.Call{ - Port: 82, - HostHeader: "81.example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - Error: simulation.ErrNoListener, - }, - }, - }, - }, - simulationTest{ - name: "multiple overlapping services", - config: createGatewayWithServiceSelector("gateway", "istio-ingressgateway.dubbo-system.svc.cluster.local,ingress.com", ` -port: - number: 80 - name: http-80 - protocol: HTTP -hosts: -- "80.example.com" -`, ` -port: - number: 81 - name: http-81 - protocol: HTTP -hosts: -- "81.example.com" -`) + ` ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-instance - namespace: dubbo-system -spec: - hosts: ["istio-ingressgateway.dubbo-system.svc.cluster.local"] - ports: - - number: 80 - targetPort: 8080 - name: http - protocol: HTTP - resolution: STATIC - location: MESH_INTERNAL - endpoints: - - address: 1.1.1.1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-instance2 - namespace: dubbo-system -spec: - hosts: ["ingress.com"] - ports: - - number: 81 - targetPort: 8080 - name: http - protocol: HTTP - resolution: STATIC - location: MESH_INTERNAL - endpoints: - - address: 1.1.1.1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: a-80 -spec: - hosts: - - "80.example.com" - gateways: - - dubbo-system/gateway - http: - - match: - - uri: - prefix: / - route: - - destination: - host: a - port: - number: 80 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: a-81 -spec: - hosts: - - "81.example.com" - gateways: - - dubbo-system/gateway - http: - - match: - - uri: - prefix: / - route: - - destination: - host: a - port: - number: 80`, - calls: []simulation.Expect{ - { - Name: "port 8080 from 80", - Call: simulation.Call{ - Port: 8080, - HostHeader: "80.example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_8080", - ClusterMatched: "outbound|80||a.default", - RouteConfigMatched: "http.8080", - VirtualHostMatched: "80.example.com:80", - }, - }, - { - Name: "port 8080 from 81", - Call: simulation.Call{ - Port: 8080, - HostHeader: "81.example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_8080", - ClusterMatched: "outbound|80||a.default", - RouteConfigMatched: "http.8080", - VirtualHostMatched: "81.example.com:81", - }, - }, - }, - }, - simulationTest{ - name: "multiple overlapping services wildcard", - config: createGatewayWithServiceSelector("gateway", "istio-ingressgateway.dubbo-system.svc.cluster.local,ingress.com", ` -port: - number: 80 - name: http-80 - protocol: HTTP -hosts: -- "80.example.com" -`, ` -port: - number: 81 - name: http-81 - protocol: HTTP -hosts: -- "81.example.com" -`) + ` ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-instance - namespace: dubbo-system -spec: - hosts: ["istio-ingressgateway.dubbo-system.svc.cluster.local"] - ports: - - number: 80 - targetPort: 8080 - name: http - protocol: HTTP - resolution: STATIC - location: MESH_INTERNAL - endpoints: - - address: 1.1.1.1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-instance2 - namespace: dubbo-system -spec: - hosts: ["ingress.com"] - ports: - - number: 81 - targetPort: 8080 - name: http - protocol: HTTP - resolution: STATIC - location: MESH_INTERNAL - endpoints: - - address: 1.1.1.1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: a-80 -spec: - hosts: - - "80.example.com" - gateways: - - dubbo-system/gateway - http: - - match: - - uri: - prefix: / - route: - - destination: - host: a - port: - number: 80 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: a-81 -spec: - hosts: - - "81.example.com" - gateways: - - dubbo-system/gateway - http: - - match: - - uri: - prefix: / - route: - - destination: - host: a - port: - number: 80`, - calls: []simulation.Expect{ - { - Name: "port 8080 from 80", - Call: simulation.Call{ - Port: 8080, - HostHeader: "80.example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_8080", - ClusterMatched: "outbound|80||a.default", - RouteConfigMatched: "http.8080", - VirtualHostMatched: "80.example.com:80", - }, - }, - { - Name: "port 8080 from 81", - Call: simulation.Call{ - Port: 8080, - HostHeader: "81.example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_8080", - ClusterMatched: "outbound|80||a.default", - RouteConfigMatched: "http.8080", - VirtualHostMatched: "81.example.com:81", - }, - }, - }, - }, - simulationTest{ - name: "no match selector", - config: createGateway("gateway", "dubbo-system", ` -port: - number: 80 - name: http-80 - protocol: HTTP -hosts: -- "80.example.com" -`, ` -port: - number: 82 - name: http-82 - protocol: HTTP -hosts: -- "82.example.com" -`) + ` ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-instance - namespace: dubbo-system -spec: - hosts: ["istio-ingressgateway.dubbo-system.svc.cluster.local"] - ports: - - number: 80 - targetPort: 8080 - name: http - protocol: HTTP - resolution: STATIC - location: MESH_INTERNAL - endpoints: - - address: 1.1.1.1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: a -spec: - hosts: - - "*.example.com" - gateways: - - dubbo-system/gateway - http: - - match: - - uri: - prefix: / - route: - - destination: - host: a - port: - number: 80`, - calls: []simulation.Expect{ - { - Name: "port 8080", - Call: simulation.Call{ - Port: 8080, - HostHeader: "80.example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_8080", - ClusterMatched: "outbound|80||a.default", - RouteConfigMatched: "http.8080", - VirtualHostMatched: "80.example.com:80", - }, - }, - { - // For gateway selector more, if there is no matching service we use the port as is - Name: "no service match", - Call: simulation.Call{ - Port: 82, - HostHeader: "82.example.com", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_82", - ClusterMatched: "outbound|80||a.default", - RouteConfigMatched: "http.82", - VirtualHostMatched: "82.example.com:82", - }, - }, - }, - }) -} diff --git a/pilot/pkg/networking/core/v1alpha3/gateway_test.go b/pilot/pkg/networking/core/v1alpha3/gateway_test.go deleted file mode 100644 index b32641100..000000000 --- a/pilot/pkg/networking/core/v1alpha3/gateway_test.go +++ /dev/null @@ -1,2377 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "reflect" - "sort" - "testing" - "time" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - auth "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - "github.com/google/go-cmp/cmp" - "github.com/google/uuid" - "google.golang.org/protobuf/testing/protocmp" - "google.golang.org/protobuf/types/known/durationpb" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - pilot_model "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - istionetworking "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/model" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/config/visibility" - "github.com/apache/dubbo-go-pixiu/pkg/proto" - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -func TestBuildGatewayListenerTlsContext(t *testing.T) { - testCases := []struct { - name string - server *networking.Server - result *auth.DownstreamTlsContext - transportProtocol istionetworking.TransportProtocol - }{ - { - name: "mesh SDS enabled, tls mode ISTIO_MUTUAL", - server: &networking.Server{ - Hosts: []string{"httpbin.example.com"}, - Port: &networking.Port{ - Protocol: string(protocol.HTTPS), - }, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, - }, - }, - result: &auth.DownstreamTlsContext{ - CommonTlsContext: &auth.CommonTlsContext{ - AlpnProtocols: util.ALPNHttp, - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "default", - SdsConfig: &core.ConfigSource{ - InitialFetchTimeout: durationpb.New(time.Second * 0), - ResourceApiVersion: core.ApiVersion_V3, - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{}, - ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ - Name: "ROOTCA", - SdsConfig: &core.ConfigSource{ - InitialFetchTimeout: durationpb.New(time.Second * 0), - ResourceApiVersion: core.ApiVersion_V3, - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - RequireClientCertificate: proto.BoolTrue, - }, - }, - { - // regression test for having both fields set. This is rejected in validation. - name: "tls mode ISTIO_MUTUAL, with credentialName", - server: &networking.Server{ - Hosts: []string{"httpbin.example.com"}, - Port: &networking.Port{ - Protocol: string(protocol.HTTPS), - }, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, - CredentialName: "ignored", - }, - }, - result: &auth.DownstreamTlsContext{ - CommonTlsContext: &auth.CommonTlsContext{ - AlpnProtocols: util.ALPNHttp, - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "default", - SdsConfig: &core.ConfigSource{ - InitialFetchTimeout: durationpb.New(time.Second * 0), - ResourceApiVersion: core.ApiVersion_V3, - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{}, - ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ - Name: "ROOTCA", - SdsConfig: &core.ConfigSource{ - InitialFetchTimeout: durationpb.New(time.Second * 0), - ResourceApiVersion: core.ApiVersion_V3, - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - RequireClientCertificate: proto.BoolTrue, - }, - }, - { // No credential name is specified, generate file paths for key/cert. - name: "no credential name no key no cert tls SIMPLE", - server: &networking.Server{ - Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, - Port: &networking.Port{ - Protocol: string(protocol.HTTPS), - }, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - }, - }, - result: &auth.DownstreamTlsContext{ - CommonTlsContext: &auth.CommonTlsContext{ - AlpnProtocols: util.ALPNHttp, - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "default", - SdsConfig: &core.ConfigSource{ - InitialFetchTimeout: durationpb.New(time.Second * 0), - ResourceApiVersion: core.ApiVersion_V3, - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - RequireClientCertificate: proto.BoolFalse, - }, - }, - { // Credential name is specified, SDS config is generated for fetching key/cert. - name: "credential name no key no cert tls SIMPLE", - server: &networking.Server{ - Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, - Port: &networking.Port{ - Protocol: string(protocol.HTTPS), - }, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - CredentialName: "ingress-sds-resource-name", - }, - }, - result: &auth.DownstreamTlsContext{ - CommonTlsContext: &auth.CommonTlsContext{ - AlpnProtocols: util.ALPNHttp, - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "kubernetes://ingress-sds-resource-name", - SdsConfig: model.SDSAdsConfig, - }, - }, - }, - RequireClientCertificate: proto.BoolFalse, - }, - }, - { // Credential name and subject alternative names are specified, generate SDS configs for - // key/cert and static validation context config. - name: "credential name subject alternative name no key no cert tls SIMPLE", - server: &networking.Server{ - Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, - Port: &networking.Port{ - Protocol: string(protocol.HTTPS), - }, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - CredentialName: "ingress-sds-resource-name", - SubjectAltNames: []string{"subject.name.a.com", "subject.name.b.com"}, - }, - }, - result: &auth.DownstreamTlsContext{ - CommonTlsContext: &auth.CommonTlsContext{ - AlpnProtocols: util.ALPNHttp, - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "kubernetes://ingress-sds-resource-name", - SdsConfig: model.SDSAdsConfig, - }, - }, - ValidationContextType: &auth.CommonTlsContext_ValidationContext{ - ValidationContext: &auth.CertificateValidationContext{ - MatchSubjectAltNames: util.StringToExactMatch([]string{"subject.name.a.com", "subject.name.b.com"}), - }, - }, - }, - RequireClientCertificate: proto.BoolFalse, - }, - }, - { - name: "no credential name key and cert tls SIMPLE", - server: &networking.Server{ - Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, - Port: &networking.Port{ - Protocol: string(protocol.HTTPS), - }, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - ServerCertificate: "server-cert.crt", - PrivateKey: "private-key.key", - }, - }, - result: &auth.DownstreamTlsContext{ - CommonTlsContext: &auth.CommonTlsContext{ - AlpnProtocols: util.ALPNHttp, - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "file-cert:server-cert.crt~private-key.key", - SdsConfig: &core.ConfigSource{ - ResourceApiVersion: core.ApiVersion_V3, - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - RequireClientCertificate: proto.BoolFalse, - }, - }, - { - name: "no credential name key and cert tls MUTUAL", - server: &networking.Server{ - Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, - Port: &networking.Port{ - Protocol: string(protocol.HTTPS), - }, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_MUTUAL, - ServerCertificate: "server-cert.crt", - PrivateKey: "private-key.key", - CaCertificates: "ca-cert.crt", - }, - }, - result: &auth.DownstreamTlsContext{ - CommonTlsContext: &auth.CommonTlsContext{ - AlpnProtocols: util.ALPNHttp, - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "file-cert:server-cert.crt~private-key.key", - SdsConfig: &core.ConfigSource{ - ResourceApiVersion: core.ApiVersion_V3, - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{}, - ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ - Name: "file-root:ca-cert.crt", - SdsConfig: &core.ConfigSource{ - ResourceApiVersion: core.ApiVersion_V3, - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - RequireClientCertificate: proto.BoolTrue, - }, - }, - { - name: "no credential name key and cert subject alt names tls MUTUAL", - server: &networking.Server{ - Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, - Port: &networking.Port{ - Protocol: string(protocol.HTTPS), - }, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_MUTUAL, - ServerCertificate: "server-cert.crt", - PrivateKey: "private-key.key", - CaCertificates: "ca-cert.crt", - SubjectAltNames: []string{"subject.name.a.com", "subject.name.b.com"}, - }, - }, - result: &auth.DownstreamTlsContext{ - CommonTlsContext: &auth.CommonTlsContext{ - AlpnProtocols: util.ALPNHttp, - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "file-cert:server-cert.crt~private-key.key", - SdsConfig: &core.ConfigSource{ - ResourceApiVersion: core.ApiVersion_V3, - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{ - MatchSubjectAltNames: util.StringToExactMatch([]string{"subject.name.a.com", "subject.name.b.com"}), - }, - ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ - Name: "file-root:ca-cert.crt", - SdsConfig: &core.ConfigSource{ - ResourceApiVersion: core.ApiVersion_V3, - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - RequireClientCertificate: proto.BoolTrue, - }, - }, - { - // Credential name and subject names are specified, SDS configs are generated for fetching - // key/cert and root cert. - name: "credential name subject alternative name key and cert tls MUTUAL", - server: &networking.Server{ - Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, - Port: &networking.Port{ - Protocol: string(protocol.HTTPS), - }, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_MUTUAL, - CredentialName: "ingress-sds-resource-name", - ServerCertificate: "server-cert.crt", - PrivateKey: "private-key.key", - SubjectAltNames: []string{"subject.name.a.com", "subject.name.b.com"}, - }, - }, - result: &auth.DownstreamTlsContext{ - CommonTlsContext: &auth.CommonTlsContext{ - AlpnProtocols: util.ALPNHttp, - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "kubernetes://ingress-sds-resource-name", - SdsConfig: model.SDSAdsConfig, - }, - }, - ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{ - MatchSubjectAltNames: util.StringToExactMatch([]string{"subject.name.a.com", "subject.name.b.com"}), - }, - ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ - Name: "kubernetes://ingress-sds-resource-name-cacert", - SdsConfig: model.SDSAdsConfig, - }, - }, - }, - }, - RequireClientCertificate: proto.BoolTrue, - }, - }, - { - // Credential name and VerifyCertificateSpki options are specified, SDS configs are generated for fetching - // key/cert and root cert - name: "credential name verify spki key and cert tls MUTUAL", - server: &networking.Server{ - Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, - Port: &networking.Port{ - Protocol: string(protocol.HTTPS), - }, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_MUTUAL, - CredentialName: "ingress-sds-resource-name", - VerifyCertificateSpki: []string{"abcdef"}, - }, - }, - result: &auth.DownstreamTlsContext{ - CommonTlsContext: &auth.CommonTlsContext{ - AlpnProtocols: util.ALPNHttp, - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "kubernetes://ingress-sds-resource-name", - SdsConfig: model.SDSAdsConfig, - }, - }, - ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{ - VerifyCertificateSpki: []string{"abcdef"}, - }, - ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ - Name: "kubernetes://ingress-sds-resource-name-cacert", - SdsConfig: model.SDSAdsConfig, - }, - }, - }, - }, - RequireClientCertificate: proto.BoolTrue, - }, - }, - { - // Credential name and VerifyCertificateHash options are specified, SDS configs are generated for fetching - // key/cert and root cert - name: "credential name verify hash key and cert tls MUTUAL", - server: &networking.Server{ - Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, - Port: &networking.Port{ - Protocol: string(protocol.HTTPS), - }, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_MUTUAL, - CredentialName: "ingress-sds-resource-name", - VerifyCertificateHash: []string{"fedcba"}, - }, - }, - result: &auth.DownstreamTlsContext{ - CommonTlsContext: &auth.CommonTlsContext{ - AlpnProtocols: util.ALPNHttp, - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "kubernetes://ingress-sds-resource-name", - SdsConfig: model.SDSAdsConfig, - }, - }, - ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{ - VerifyCertificateHash: []string{"fedcba"}, - }, - ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ - Name: "kubernetes://ingress-sds-resource-name-cacert", - SdsConfig: model.SDSAdsConfig, - }, - }, - }, - }, - RequireClientCertificate: proto.BoolTrue, - }, - }, - { - name: "no credential name key and cert tls PASSTHROUGH", - server: &networking.Server{ - Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, - Port: &networking.Port{ - Protocol: string(protocol.HTTPS), - }, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_PASSTHROUGH, - ServerCertificate: "server-cert.crt", - PrivateKey: "private-key.key", - }, - }, - result: nil, - }, - { - name: "Downstream TLS settings for QUIC transport", - server: &networking.Server{ - Hosts: []string{"httpbin.example.com"}, - Port: &networking.Port{ - Protocol: string(protocol.HTTPS), - }, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - CredentialName: "httpbin-cred", - }, - }, - transportProtocol: istionetworking.TransportProtocolQUIC, - result: &auth.DownstreamTlsContext{ - CommonTlsContext: &auth.CommonTlsContext{ - AlpnProtocols: util.ALPNHttp3OverQUIC, - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "kubernetes://httpbin-cred", - SdsConfig: model.SDSAdsConfig, - }, - }, - }, - RequireClientCertificate: proto.BoolFalse, - }, - }, - { - name: "duplicated cipher suites with tls SIMPLE", - server: &networking.Server{ - Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, - Port: &networking.Port{ - Protocol: string(protocol.HTTPS), - }, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - ServerCertificate: "server-cert.crt", - PrivateKey: "private-key.key", - CipherSuites: []string{"ECDHE-ECDSA-AES128-SHA", "ECDHE-ECDSA-AES128-SHA"}, - }, - }, - result: &auth.DownstreamTlsContext{ - CommonTlsContext: &auth.CommonTlsContext{ - AlpnProtocols: util.ALPNHttp, - TlsParams: &auth.TlsParameters{ - CipherSuites: []string{"ECDHE-ECDSA-AES128-SHA"}, - }, - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "file-cert:server-cert.crt~private-key.key", - SdsConfig: &core.ConfigSource{ - ResourceApiVersion: core.ApiVersion_V3, - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - RequireClientCertificate: proto.BoolFalse, - }, - }, - { - // tcp server is simple tls, no istio-peer-exchange in the alpns - name: "tcp server, tls SIMPLE", - server: &networking.Server{ - Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, - Port: &networking.Port{ - Protocol: string(protocol.TLS), - }, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - CredentialName: "ingress-sds-resource-name", - }, - }, - result: &auth.DownstreamTlsContext{ - CommonTlsContext: &auth.CommonTlsContext{ - AlpnProtocols: util.ALPNHttp, - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "kubernetes://ingress-sds-resource-name", - SdsConfig: model.SDSAdsConfig, - }, - }, - }, - RequireClientCertificate: proto.BoolFalse, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - ret := buildGatewayListenerTLSContext(tc.server, &pilot_model.Proxy{ - Metadata: &pilot_model.NodeMetadata{}, - }, tc.transportProtocol) - if diff := cmp.Diff(tc.result, ret, protocmp.Transform()); diff != "" { - t.Errorf("got diff: %v", diff) - } - }) - } -} - -func TestCreateGatewayHTTPFilterChainOpts(t *testing.T) { - var stripPortMode *hcm.HttpConnectionManager_StripAnyHostPort - testCases := []struct { - name string - node *pilot_model.Proxy - server *networking.Server - routeName string - proxyConfig *meshconfig.ProxyConfig - result *filterChainOpts - transportProtocol istionetworking.TransportProtocol - }{ - { - name: "HTTP1.0 mode enabled", - node: &pilot_model.Proxy{ - Metadata: &pilot_model.NodeMetadata{HTTP10: "1"}, - }, - server: &networking.Server{ - Port: &networking.Port{ - Protocol: protocol.HTTP.String(), - }, - }, - routeName: "some-route", - proxyConfig: nil, - result: &filterChainOpts{ - sniHosts: nil, - tlsContext: nil, - httpOpts: &httpListenerOpts{ - rds: "some-route", - useRemoteAddress: true, - connectionManager: &hcm.HttpConnectionManager{ - XffNumTrustedHops: 0, - ForwardClientCertDetails: hcm.HttpConnectionManager_SANITIZE_SET, - SetCurrentClientCertDetails: &hcm.HttpConnectionManager_SetCurrentClientCertDetails{ - Subject: proto.BoolTrue, - Cert: true, - Uri: true, - Dns: true, - }, - ServerName: EnvoyServerName, - HttpProtocolOptions: &core.Http1ProtocolOptions{ - AcceptHttp_10: true, - }, - StripPortMode: stripPortMode, - }, - class: istionetworking.ListenerClassGateway, - protocol: protocol.HTTP, - }, - }, - }, - { - name: "Duplicate hosts in TLS filterChain", - node: &pilot_model.Proxy{Metadata: &pilot_model.NodeMetadata{}}, - server: &networking.Server{ - Port: &networking.Port{ - Protocol: "HTTPS", - }, - Hosts: []string{"example.org", "example.org"}, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, - }, - }, - routeName: "some-route", - proxyConfig: nil, - result: &filterChainOpts{ - sniHosts: []string{"example.org"}, - tlsContext: &auth.DownstreamTlsContext{ - CommonTlsContext: &auth.CommonTlsContext{ - AlpnProtocols: util.ALPNHttp, - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "default", - SdsConfig: &core.ConfigSource{ - ResourceApiVersion: core.ApiVersion_V3, - InitialFetchTimeout: durationpb.New(time.Second * 0), - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{}, - ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ - Name: "ROOTCA", - SdsConfig: &core.ConfigSource{ - ResourceApiVersion: core.ApiVersion_V3, - InitialFetchTimeout: durationpb.New(time.Second * 0), - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - RequireClientCertificate: proto.BoolTrue, - }, - httpOpts: &httpListenerOpts{ - rds: "some-route", - useRemoteAddress: true, - connectionManager: &hcm.HttpConnectionManager{ - XffNumTrustedHops: 0, - ForwardClientCertDetails: hcm.HttpConnectionManager_SANITIZE_SET, - SetCurrentClientCertDetails: &hcm.HttpConnectionManager_SetCurrentClientCertDetails{ - Subject: proto.BoolTrue, - Cert: true, - Uri: true, - Dns: true, - }, - ServerName: EnvoyServerName, - HttpProtocolOptions: &core.Http1ProtocolOptions{}, - StripPortMode: stripPortMode, - }, - class: istionetworking.ListenerClassGateway, - protocol: protocol.HTTPS, - }, - }, - }, - { - name: "Unique hosts in TLS filterChain", - node: &pilot_model.Proxy{Metadata: &pilot_model.NodeMetadata{}}, - server: &networking.Server{ - Port: &networking.Port{ - Protocol: "HTTPS", - }, - Hosts: []string{"example.org", "test.org"}, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, - }, - }, - routeName: "some-route", - proxyConfig: nil, - result: &filterChainOpts{ - sniHosts: []string{"example.org", "test.org"}, - tlsContext: &auth.DownstreamTlsContext{ - CommonTlsContext: &auth.CommonTlsContext{ - AlpnProtocols: util.ALPNHttp, - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "default", - SdsConfig: &core.ConfigSource{ - ResourceApiVersion: core.ApiVersion_V3, - InitialFetchTimeout: durationpb.New(time.Second * 0), - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{}, - ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ - Name: "ROOTCA", - SdsConfig: &core.ConfigSource{ - ResourceApiVersion: core.ApiVersion_V3, - InitialFetchTimeout: durationpb.New(time.Second * 0), - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - RequireClientCertificate: proto.BoolTrue, - }, - httpOpts: &httpListenerOpts{ - rds: "some-route", - useRemoteAddress: true, - connectionManager: &hcm.HttpConnectionManager{ - XffNumTrustedHops: 0, - ForwardClientCertDetails: hcm.HttpConnectionManager_SANITIZE_SET, - SetCurrentClientCertDetails: &hcm.HttpConnectionManager_SetCurrentClientCertDetails{ - Subject: proto.BoolTrue, - Cert: true, - Uri: true, - Dns: true, - }, - ServerName: EnvoyServerName, - HttpProtocolOptions: &core.Http1ProtocolOptions{}, - StripPortMode: stripPortMode, - }, - class: istionetworking.ListenerClassGateway, - protocol: protocol.HTTPS, - }, - }, - }, - { - name: "Wildcard hosts in TLS filterChain are not duplicates", - node: &pilot_model.Proxy{Metadata: &pilot_model.NodeMetadata{}}, - server: &networking.Server{ - Port: &networking.Port{ - Protocol: "HTTPS", - }, - Hosts: []string{"*.example.org", "example.org"}, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, - }, - }, - routeName: "some-route", - proxyConfig: nil, - result: &filterChainOpts{ - sniHosts: []string{"*.example.org", "example.org"}, - tlsContext: &auth.DownstreamTlsContext{ - CommonTlsContext: &auth.CommonTlsContext{ - AlpnProtocols: util.ALPNHttp, - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "default", - SdsConfig: &core.ConfigSource{ - ResourceApiVersion: core.ApiVersion_V3, - InitialFetchTimeout: durationpb.New(time.Second * 0), - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{}, - ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ - Name: "ROOTCA", - SdsConfig: &core.ConfigSource{ - ResourceApiVersion: core.ApiVersion_V3, - InitialFetchTimeout: durationpb.New(time.Second * 0), - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - RequireClientCertificate: proto.BoolTrue, - }, - httpOpts: &httpListenerOpts{ - rds: "some-route", - useRemoteAddress: true, - connectionManager: &hcm.HttpConnectionManager{ - XffNumTrustedHops: 0, - ForwardClientCertDetails: hcm.HttpConnectionManager_SANITIZE_SET, - SetCurrentClientCertDetails: &hcm.HttpConnectionManager_SetCurrentClientCertDetails{ - Subject: proto.BoolTrue, - Cert: true, - Uri: true, - Dns: true, - }, - ServerName: EnvoyServerName, - HttpProtocolOptions: &core.Http1ProtocolOptions{}, - StripPortMode: stripPortMode, - }, - class: istionetworking.ListenerClassGateway, - protocol: protocol.HTTPS, - }, - }, - }, - { - name: "Topology HTTP Protocol", - node: &pilot_model.Proxy{Metadata: &pilot_model.NodeMetadata{}}, - server: &networking.Server{ - Port: &networking.Port{ - Protocol: protocol.HTTP.String(), - }, - }, - routeName: "some-route", - proxyConfig: &meshconfig.ProxyConfig{ - GatewayTopology: &meshconfig.Topology{ - NumTrustedProxies: 2, - ForwardClientCertDetails: meshconfig.Topology_APPEND_FORWARD, - }, - }, - result: &filterChainOpts{ - sniHosts: nil, - tlsContext: nil, - httpOpts: &httpListenerOpts{ - rds: "some-route", - useRemoteAddress: true, - connectionManager: &hcm.HttpConnectionManager{ - XffNumTrustedHops: 2, - ForwardClientCertDetails: hcm.HttpConnectionManager_APPEND_FORWARD, - SetCurrentClientCertDetails: &hcm.HttpConnectionManager_SetCurrentClientCertDetails{ - Subject: proto.BoolTrue, - Cert: true, - Uri: true, - Dns: true, - }, - ServerName: EnvoyServerName, - HttpProtocolOptions: &core.Http1ProtocolOptions{}, - StripPortMode: stripPortMode, - }, - class: istionetworking.ListenerClassGateway, - protocol: protocol.HTTP, - }, - }, - }, - { - name: "Topology HTTPS Protocol", - node: &pilot_model.Proxy{Metadata: &pilot_model.NodeMetadata{}}, - server: &networking.Server{ - Port: &networking.Port{ - Protocol: "HTTPS", - }, - Hosts: []string{"example.org"}, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, - }, - }, - routeName: "some-route", - proxyConfig: &meshconfig.ProxyConfig{ - GatewayTopology: &meshconfig.Topology{ - NumTrustedProxies: 3, - ForwardClientCertDetails: meshconfig.Topology_FORWARD_ONLY, - }, - }, - result: &filterChainOpts{ - sniHosts: []string{"example.org"}, - tlsContext: &auth.DownstreamTlsContext{ - CommonTlsContext: &auth.CommonTlsContext{ - AlpnProtocols: util.ALPNHttp, - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "default", - SdsConfig: &core.ConfigSource{ - ResourceApiVersion: core.ApiVersion_V3, - InitialFetchTimeout: durationpb.New(time.Second * 0), - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{}, - ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ - Name: "ROOTCA", - SdsConfig: &core.ConfigSource{ - ResourceApiVersion: core.ApiVersion_V3, - InitialFetchTimeout: durationpb.New(time.Second * 0), - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - RequireClientCertificate: proto.BoolTrue, - }, - httpOpts: &httpListenerOpts{ - rds: "some-route", - useRemoteAddress: true, - connectionManager: &hcm.HttpConnectionManager{ - XffNumTrustedHops: 3, - ForwardClientCertDetails: hcm.HttpConnectionManager_FORWARD_ONLY, - SetCurrentClientCertDetails: &hcm.HttpConnectionManager_SetCurrentClientCertDetails{ - Subject: proto.BoolTrue, - Cert: true, - Uri: true, - Dns: true, - }, - ServerName: EnvoyServerName, - HttpProtocolOptions: &core.Http1ProtocolOptions{}, - StripPortMode: stripPortMode, - }, - class: istionetworking.ListenerClassGateway, - protocol: protocol.HTTPS, - }, - }, - }, - { - name: "HTTPS Protocol with server name", - node: &pilot_model.Proxy{Metadata: &pilot_model.NodeMetadata{}}, - server: &networking.Server{ - Name: "server1", - Port: &networking.Port{ - Protocol: "HTTPS", - }, - Hosts: []string{"example.org"}, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, - }, - }, - routeName: "some-route", - proxyConfig: &meshconfig.ProxyConfig{ - GatewayTopology: &meshconfig.Topology{ - NumTrustedProxies: 3, - ForwardClientCertDetails: meshconfig.Topology_FORWARD_ONLY, - }, - }, - result: &filterChainOpts{ - sniHosts: []string{"example.org"}, - tlsContext: &auth.DownstreamTlsContext{ - RequireClientCertificate: proto.BoolTrue, - CommonTlsContext: &auth.CommonTlsContext{ - AlpnProtocols: util.ALPNHttp, - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "default", - SdsConfig: &core.ConfigSource{ - ResourceApiVersion: core.ApiVersion_V3, - InitialFetchTimeout: durationpb.New(time.Second * 0), - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{}, - ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ - Name: "ROOTCA", - SdsConfig: &core.ConfigSource{ - ResourceApiVersion: core.ApiVersion_V3, - InitialFetchTimeout: durationpb.New(time.Second * 0), - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - httpOpts: &httpListenerOpts{ - rds: "some-route", - useRemoteAddress: true, - connectionManager: &hcm.HttpConnectionManager{ - XffNumTrustedHops: 3, - ForwardClientCertDetails: hcm.HttpConnectionManager_FORWARD_ONLY, - SetCurrentClientCertDetails: &hcm.HttpConnectionManager_SetCurrentClientCertDetails{ - Subject: proto.BoolTrue, - Cert: true, - Uri: true, - Dns: true, - }, - ServerName: EnvoyServerName, - HttpProtocolOptions: &core.Http1ProtocolOptions{}, - StripPortMode: stripPortMode, - }, - statPrefix: "server1", - class: istionetworking.ListenerClassGateway, - protocol: protocol.HTTPS, - }, - }, - }, - { - name: "QUIC protocol with server name", - node: &pilot_model.Proxy{Metadata: &pilot_model.NodeMetadata{}}, - transportProtocol: istionetworking.TransportProtocolQUIC, - server: &networking.Server{ - Name: "server1", - Port: &networking.Port{ - Name: "https-app", - Number: 443, - TargetPort: 8443, - Protocol: "HTTPS", - }, - Hosts: []string{"example.org"}, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - ServerCertificate: "/etc/cert/example.crt", - PrivateKey: "/etc/cert/example.key", - }, - }, - routeName: "some-route", - proxyConfig: &meshconfig.ProxyConfig{ - GatewayTopology: &meshconfig.Topology{ - NumTrustedProxies: 3, - ForwardClientCertDetails: meshconfig.Topology_FORWARD_ONLY, - }, - }, - result: &filterChainOpts{ - sniHosts: []string{"example.org"}, - tlsContext: &auth.DownstreamTlsContext{ - RequireClientCertificate: proto.BoolFalse, - CommonTlsContext: &auth.CommonTlsContext{ - AlpnProtocols: util.ALPNHttp3OverQUIC, - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "file-cert:/etc/cert/example.crt~/etc/cert/example.key", - SdsConfig: &core.ConfigSource{ - ResourceApiVersion: core.ApiVersion_V3, - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ - ClusterName: "sds-grpc", - }, - }, - }, - }, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - }, - }, - }, - }, - }, - }, - }, - httpOpts: &httpListenerOpts{ - rds: "some-route", - http3Only: true, - connectionManager: &hcm.HttpConnectionManager{ - XffNumTrustedHops: 3, - ForwardClientCertDetails: hcm.HttpConnectionManager_FORWARD_ONLY, - SetCurrentClientCertDetails: &hcm.HttpConnectionManager_SetCurrentClientCertDetails{ - Subject: proto.BoolTrue, - Cert: true, - Uri: true, - Dns: true, - }, - ServerName: EnvoyServerName, - HttpProtocolOptions: &core.Http1ProtocolOptions{}, - Http3ProtocolOptions: &core.Http3ProtocolOptions{}, - CodecType: hcm.HttpConnectionManager_HTTP3, - StripPortMode: stripPortMode, - }, - useRemoteAddress: true, - statPrefix: "server1", - class: istionetworking.ListenerClassGateway, - protocol: protocol.HTTPS, - }, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - cgi := NewConfigGenerator(&pilot_model.DisabledCache{}) - tc.node.MergedGateway = &pilot_model.MergedGateway{TLSServerInfo: map[*networking.Server]*pilot_model.TLSServerInfo{ - tc.server: {SNIHosts: pilot_model.GetSNIHostsForServer(tc.server)}, - }} - ret := cgi.createGatewayHTTPFilterChainOpts(tc.node, tc.server.Port, tc.server, - tc.routeName, tc.proxyConfig, tc.transportProtocol) - if diff := cmp.Diff(tc.result.tlsContext, ret.tlsContext, protocmp.Transform()); diff != "" { - t.Errorf("got diff in tls context: %v", diff) - } - if !reflect.DeepEqual(tc.result.httpOpts, ret.httpOpts) { - t.Errorf("expecting httpopts:\n %+v \nbut got:\n %+v", tc.result.httpOpts, ret.httpOpts) - } - if !reflect.DeepEqual(tc.result.sniHosts, ret.sniHosts) { - t.Errorf("expecting snihosts %+v but got %+v", tc.result.sniHosts, ret.sniHosts) - } - }) - } -} - -func TestGatewayHTTPRouteConfig(t *testing.T) { - httpRedirectGateway := config.Config{ - Meta: config.Meta{ - Name: "gateway-redirect", - Namespace: "default", - GroupVersionKind: gvk.Gateway, - }, - Spec: &networking.Gateway{ - Selector: map[string]string{"istio": "ingressgateway"}, - Servers: []*networking.Server{ - { - Hosts: []string{"example.org"}, - Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, - Tls: &networking.ServerTLSSettings{HttpsRedirect: true}, - }, - }, - }, - } - httpRedirectGatewayWithoutVS := config.Config{ - Meta: config.Meta{ - Name: "gateway-redirect-noroutes", - Namespace: "default", - GroupVersionKind: gvk.Gateway, - }, - Spec: &networking.Gateway{ - Selector: map[string]string{"istio": "ingressgateway"}, - Servers: []*networking.Server{ - { - Hosts: []string{"example.org"}, - Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, - Tls: &networking.ServerTLSSettings{HttpsRedirect: true}, - }, - }, - }, - } - httpGateway := config.Config{ - Meta: config.Meta{ - Name: "gateway", - Namespace: "default", - GroupVersionKind: gvk.Gateway, - }, - Spec: &networking.Gateway{ - Selector: map[string]string{"istio": "ingressgateway"}, - Servers: []*networking.Server{ - { - Hosts: []string{"example.org"}, - Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, - }, - }, - }, - } - httpsGateway := config.Config{ - Meta: config.Meta{ - Name: "gateway-https", - Namespace: "default", - GroupVersionKind: gvk.Gateway, - }, - Spec: &networking.Gateway{ - Selector: map[string]string{"istio": "ingressgateway"}, - Servers: []*networking.Server{ - { - Hosts: []string{"example.org"}, - Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, - Tls: &networking.ServerTLSSettings{HttpsRedirect: true}, - }, - { - Hosts: []string{"example.org"}, - Port: &networking.Port{Name: "https", Number: 443, Protocol: "HTTPS"}, - Tls: &networking.ServerTLSSettings{Mode: networking.ServerTLSSettings_TLSmode(networking.ClientTLSSettings_SIMPLE)}, - }, - }, - }, - } - httpsGatewayRedirect := config.Config{ - Meta: config.Meta{ - Name: "gateway-https", - Namespace: "default", - GroupVersionKind: gvk.Gateway, - }, - Spec: &networking.Gateway{ - Selector: map[string]string{"istio": "ingressgateway"}, - Servers: []*networking.Server{ - { - Hosts: []string{"example.org"}, - Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, - Tls: &networking.ServerTLSSettings{HttpsRedirect: true}, - }, - { - Hosts: []string{"example.org"}, - Port: &networking.Port{Name: "https", Number: 443, Protocol: "HTTPS"}, - Tls: &networking.ServerTLSSettings{HttpsRedirect: true, Mode: networking.ServerTLSSettings_TLSmode(networking.ClientTLSSettings_SIMPLE)}, - }, - }, - }, - } - httpGatewayWildcard := config.Config{ - Meta: config.Meta{ - Name: "gateway", - Namespace: "default", - GroupVersionKind: gvk.Gateway, - }, - Spec: &networking.Gateway{ - Selector: map[string]string{"istio": "ingressgateway"}, - Servers: []*networking.Server{ - { - Hosts: []string{"*"}, - Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, - }, - }, - }, - } - virtualServiceSpec := &networking.VirtualService{ - Hosts: []string{"example.org"}, - Gateways: []string{"gateway", "gateway-redirect"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "example.org", - Port: &networking.PortSelector{ - Number: 80, - }, - }, - }, - }, - }, - }, - } - virtualServiceHTTPSMatchSpec := &networking.VirtualService{ - Hosts: []string{"example.org"}, - Gateways: []string{"gateway-https"}, - Http: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Port: 443, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "example.default.svc.cluster.local", - Port: &networking.PortSelector{ - Number: 8080, - }, - }, - }, - }, - }, - }, - } - virtualService := config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "virtual-service", - Namespace: "default", - }, - Spec: virtualServiceSpec, - } - virtualServiceHTTPS := config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "virtual-service-https", - Namespace: "default", - }, - Spec: virtualServiceHTTPSMatchSpec, - } - virtualServiceCopy := config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "virtual-service-copy", - Namespace: "default", - }, - Spec: virtualServiceSpec, - } - virtualServiceWildcard := config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "virtual-service-wildcard", - Namespace: "default", - }, - Spec: &networking.VirtualService{ - Hosts: []string{"*.org"}, - Gateways: []string{"gateway", "gateway-redirect"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "example.org", - Port: &networking.PortSelector{ - Number: 80, - }, - }, - }, - }, - }, - }, - }, - } - cases := []struct { - name string - virtualServices []config.Config - gateways []config.Config - routeName string - expectedVirtualHosts map[string][]string - expectedVirtualHostsHostPortStrip map[string][]string - expectedHTTPRoutes map[string]int - redirect bool - }{ - { - "404 when no services", - []config.Config{}, - []config.Config{httpGateway}, - "http.80", - map[string][]string{ - "blackhole:80": { - "*", - }, - }, - map[string][]string{ - "blackhole:80": { - "*", - }, - }, - map[string]int{"blackhole:80": 0}, - false, - }, - { - "tls redirect without virtual services", - []config.Config{virtualService}, - []config.Config{httpRedirectGatewayWithoutVS}, - "http.80", - map[string][]string{ - "example.org:80": { - "example.org", "example.org:*", - }, - }, - map[string][]string{ - "example.org:80": {"example.org"}, - }, - // We will setup a VHost which just redirects; no routes - map[string]int{"example.org:80": 0}, - true, - }, - { - "virtual services with tls redirect", - []config.Config{virtualService}, - []config.Config{httpRedirectGateway}, - "http.80", - map[string][]string{ - "example.org:80": { - "example.org", "example.org:*", - }, - }, - map[string][]string{ - "example.org:80": {"example.org"}, - }, - map[string]int{"example.org:80": 1}, - true, - }, - { - "merging of virtual services when tls redirect is set", - []config.Config{virtualService, virtualServiceCopy}, - []config.Config{httpRedirectGateway, httpGateway}, - "http.80", - map[string][]string{ - "example.org:80": { - "example.org", "example.org:*", - }, - }, - map[string][]string{ - "example.org:80": {"example.org"}, - }, - map[string]int{"example.org:80": 4}, - true, - }, - { - "reverse merging of virtual services when tls redirect is set", - []config.Config{virtualService, virtualServiceCopy}, - []config.Config{httpGateway, httpRedirectGateway}, - "http.80", - map[string][]string{ - "example.org:80": { - "example.org", "example.org:*", - }, - }, - map[string][]string{ - "example.org:80": {"example.org"}, - }, - map[string]int{"example.org:80": 4}, - true, - }, - { - "merging of virtual services when tls redirect is set without VS", - []config.Config{virtualService, virtualServiceCopy}, - []config.Config{httpGateway, httpRedirectGatewayWithoutVS}, - "http.80", - map[string][]string{ - "example.org:80": { - "example.org", "example.org:*", - }, - }, - map[string][]string{ - "example.org:80": {"example.org"}, - }, - map[string]int{"example.org:80": 2}, - true, - }, - { - "reverse merging of virtual services when tls redirect is set without VS", - []config.Config{virtualService, virtualServiceCopy}, - []config.Config{httpRedirectGatewayWithoutVS, httpGateway}, - "http.80", - map[string][]string{ - "example.org:80": { - "example.org", "example.org:*", - }, - }, - map[string][]string{ - "example.org:80": {"example.org"}, - }, - map[string]int{"example.org:80": 2}, - true, - }, - { - "add a route for a virtual service", - []config.Config{virtualService}, - []config.Config{httpGateway}, - "http.80", - map[string][]string{ - "example.org:80": { - "example.org", "example.org:*", - }, - }, - map[string][]string{ - "example.org:80": {"example.org"}, - }, - map[string]int{"example.org:80": 1}, - false, - }, - { - "duplicate virtual service should merge", - []config.Config{virtualService, virtualServiceCopy}, - []config.Config{httpGateway}, - "http.80", - map[string][]string{ - "example.org:80": { - "example.org", "example.org:*", - }, - }, - map[string][]string{ - "example.org:80": {"example.org"}, - }, - map[string]int{"example.org:80": 2}, - false, - }, - { - "duplicate by wildcard should merge", - []config.Config{virtualService, virtualServiceWildcard}, - []config.Config{httpGateway}, - "http.80", - map[string][]string{ - "example.org:80": { - "example.org", "example.org:*", - }, - }, - map[string][]string{ - "example.org:80": {"example.org"}, - }, - map[string]int{"example.org:80": 2}, - false, - }, - { - "wildcard virtual service", - []config.Config{virtualServiceWildcard}, - []config.Config{httpGatewayWildcard}, - "http.80", - map[string][]string{ - "*.org:80": {"*.org", "*.org:80"}, - }, - map[string][]string{ - "*.org:80": {"*.org"}, - }, - map[string]int{"*.org:80": 1}, - false, - }, - { - "http redirection not working when virtualservice not match http port", - []config.Config{virtualServiceHTTPS}, - []config.Config{httpsGateway}, - "https.443.https.gateway-https.default", - map[string][]string{ - "example.org:443": {"example.org", "example.org:*"}, - }, - map[string][]string{ - "example.org:443": {"example.org"}, - }, - map[string]int{"example.org:443": 1}, - false, - }, - { - "http redirection not working when virtualservice not match http port", - []config.Config{virtualServiceHTTPS}, - []config.Config{httpsGateway}, - "http.80", - map[string][]string{ - "example.org:80": {"example.org", "example.org:*"}, - }, - map[string][]string{ - "example.org:80": {"example.org"}, - }, - // We will setup a VHost which just redirects; no routes - map[string]int{"example.org:80": 0}, - true, - }, - { - "http & https redirection not working when virtualservice not match http port", - []config.Config{virtualServiceHTTPS}, - []config.Config{httpsGatewayRedirect}, - "https.443.https.gateway-https.default", - map[string][]string{ - "example.org:443": {"example.org", "example.org:*"}, - }, - map[string][]string{ - "example.org:443": {"example.org"}, - }, - map[string]int{"example.org:443": 1}, - true, - }, - { - "http & https redirection not working when virtualservice not match http port", - []config.Config{virtualServiceHTTPS}, - []config.Config{httpsGatewayRedirect}, - "http.80", - map[string][]string{ - "example.org:80": {"example.org", "example.org:*"}, - }, - map[string][]string{ - "example.org:80": {"example.org"}, - }, - // We will setup a VHost which just redirects; no routes - map[string]int{"example.org:80": 0}, - true, - }, - } - - for _, value := range []bool{false, true} { - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - test.SetBoolForTest(t, &features.StripHostPort, value) - cfgs := tt.gateways - cfgs = append(cfgs, tt.virtualServices...) - cg := NewConfigGenTest(t, TestOptions{ - Configs: cfgs, - }) - r := cg.ConfigGen.buildGatewayHTTPRouteConfig(cg.SetupProxy(&proxyGateway), cg.PushContext(), tt.routeName) - if r == nil { - t.Fatal("got an empty route configuration") - } - vh := make(map[string][]string) - hr := make(map[string]int) - for _, h := range r.VirtualHosts { - vh[h.Name] = h.Domains - hr[h.Name] = len(h.Routes) - if h.Name != "blackhole:80" && !h.IncludeRequestAttemptCount { - t.Errorf("expected attempt count to be set in virtual host, but not found") - } - if tt.redirect != (h.RequireTls == route.VirtualHost_ALL) { - t.Errorf("expected redirect %v, got %v", tt.redirect, h.RequireTls) - } - } - - if features.StripHostPort { - if !reflect.DeepEqual(tt.expectedVirtualHostsHostPortStrip, vh) { - t.Errorf("got unexpected virtual hosts. Expected: %v, Got: %v", tt.expectedVirtualHostsHostPortStrip, vh) - } - } else { - if !reflect.DeepEqual(tt.expectedVirtualHosts, vh) { - t.Errorf("got unexpected virtual hosts. Expected: %v, Got: %v", tt.expectedVirtualHosts, vh) - } - } - if !reflect.DeepEqual(tt.expectedHTTPRoutes, hr) { - t.Errorf("got unexpected number of http routes. Expected: %v, Got: %v", tt.expectedHTTPRoutes, hr) - } - }) - } - } -} - -func TestBuildGatewayListeners(t *testing.T) { - cases := []struct { - name string - node *pilot_model.Proxy - gateways []config.Config - virtualServices []config.Config - expectedListeners []string - }{ - { - "targetPort overrides service port", - &pilot_model.Proxy{ - ServiceInstances: []*pilot_model.ServiceInstance{ - { - Service: &pilot_model.Service{ - Hostname: "test", - }, - ServicePort: &pilot_model.Port{ - Port: 80, - }, - Endpoint: &pilot_model.IstioEndpoint{ - EndpointPort: 8080, - }, - }, - }, - }, - []config.Config{ - { - Meta: config.Meta{Name: uuid.NewString(), Namespace: uuid.NewString(), GroupVersionKind: gvk.Gateway}, - Spec: &networking.Gateway{ - Servers: []*networking.Server{ - { - Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, - }, - }, - }, - }, - }, - nil, - []string{"0.0.0.0_8080"}, - }, - { - "multiple ports", - &pilot_model.Proxy{}, - []config.Config{ - { - Meta: config.Meta{Name: uuid.NewString(), Namespace: uuid.NewString(), GroupVersionKind: gvk.Gateway}, - Spec: &networking.Gateway{ - Servers: []*networking.Server{ - { - Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, - }, - { - Port: &networking.Port{Name: "http", Number: 801, Protocol: "HTTP"}, - }, - }, - }, - }, - }, - nil, - []string{"0.0.0.0_80", "0.0.0.0_801"}, - }, - { - "privileged port on unprivileged pod", - &pilot_model.Proxy{ - Metadata: &pilot_model.NodeMetadata{ - UnprivilegedPod: "true", - }, - }, - []config.Config{ - { - Meta: config.Meta{Name: uuid.NewString(), Namespace: uuid.NewString(), GroupVersionKind: gvk.Gateway}, - Spec: &networking.Gateway{ - Servers: []*networking.Server{ - { - Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, - }, - { - Port: &networking.Port{Name: "http", Number: 8080, Protocol: "HTTP"}, - }, - }, - }, - }, - }, - nil, - []string{"0.0.0.0_8080"}, - }, - { - "privileged port on privileged pod when empty env var is set", - &pilot_model.Proxy{ - Metadata: &pilot_model.NodeMetadata{ - UnprivilegedPod: "", - }, - }, - []config.Config{ - { - Meta: config.Meta{Name: uuid.NewString(), Namespace: uuid.NewString(), GroupVersionKind: gvk.Gateway}, - Spec: &networking.Gateway{ - Servers: []*networking.Server{ - { - Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, - }, - { - Port: &networking.Port{Name: "http", Number: 8080, Protocol: "HTTP"}, - }, - }, - }, - }, - }, - nil, - []string{"0.0.0.0_80", "0.0.0.0_8080"}, - }, - { - "privileged port on privileged pod", - &pilot_model.Proxy{}, - []config.Config{ - { - Meta: config.Meta{Name: uuid.NewString(), Namespace: uuid.NewString(), GroupVersionKind: gvk.Gateway}, - Spec: &networking.Gateway{ - Servers: []*networking.Server{ - { - Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, - }, - { - Port: &networking.Port{Name: "http", Number: 8080, Protocol: "HTTP"}, - }, - }, - }, - }, - }, - nil, - []string{"0.0.0.0_80", "0.0.0.0_8080"}, - }, - { - "gateway with bind", - &pilot_model.Proxy{}, - []config.Config{ - { - Meta: config.Meta{Name: uuid.NewString(), Namespace: uuid.NewString(), GroupVersionKind: gvk.Gateway}, - Spec: &networking.Gateway{ - Servers: []*networking.Server{ - { - Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, - }, - { - Port: &networking.Port{Name: "http", Number: 8080, Protocol: "HTTP"}, - Hosts: []string{"externalgatewayclient.com"}, - }, - { - Port: &networking.Port{Name: "http", Number: 8080, Protocol: "HTTP"}, - Bind: "127.0.0.1", - Hosts: []string{"internalmesh.svc.cluster.local"}, - }, - }, - }, - }, - }, - nil, - []string{"0.0.0.0_80", "0.0.0.0_8080", "127.0.0.1_8080"}, - }, - { - "gateway with simple and passthrough", - &pilot_model.Proxy{}, - []config.Config{ - { - Meta: config.Meta{Name: uuid.NewString(), Namespace: uuid.NewString(), GroupVersionKind: gvk.Gateway}, - Spec: &networking.Gateway{ - Servers: []*networking.Server{ - { - Port: &networking.Port{Name: "http", Number: 443, Protocol: "HTTPS"}, - Hosts: []string{"*.example.com"}, - Tls: &networking.ServerTLSSettings{CredentialName: "test", Mode: networking.ServerTLSSettings_SIMPLE}, - }, - { - Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, - Hosts: []string{"foo.example.com"}, - }, - }, - }, - }, - { - Meta: config.Meta{Name: "passthrough-gateway", Namespace: "testns", GroupVersionKind: gvk.Gateway}, - Spec: &networking.Gateway{ - Servers: []*networking.Server{ - { - Port: &networking.Port{Name: "http", Number: 443, Protocol: "HTTPS"}, - Hosts: []string{"*.example.com"}, - Tls: &networking.ServerTLSSettings{CredentialName: "test", Mode: networking.ServerTLSSettings_SIMPLE}, - }, - { - Port: &networking.Port{Name: "tcp", Number: 9443, Protocol: "TLS"}, - Hosts: []string{"barone.example.com"}, - Tls: &networking.ServerTLSSettings{CredentialName: "test", Mode: networking.ServerTLSSettings_PASSTHROUGH}, - }, - { - Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, - Hosts: []string{"bar.example.com"}, - }, - }, - }, - }, - }, - []config.Config{ - { - Meta: config.Meta{Name: uuid.NewString(), Namespace: uuid.NewString(), GroupVersionKind: gvk.VirtualService}, - Spec: &networking.VirtualService{ - Gateways: []string{"testns/passthrough-gateway"}, - Hosts: []string{"barone.example.com"}, - Tls: []*networking.TLSRoute{ - { - Match: []*networking.TLSMatchAttributes{ - { - Port: 9443, - SniHosts: []string{"barone.example.com"}, - }, - }, - Route: []*networking.RouteDestination{ - { - Destination: &networking.Destination{ - Host: "foo.com", - }, - }, - }, - }, - }, - }, - }, - }, - []string{"0.0.0.0_443", "0.0.0.0_80", "0.0.0.0_9443"}, - }, - { - "gateway with multiple http servers", - &pilot_model.Proxy{}, - []config.Config{ - { - Meta: config.Meta{Name: "gateway1", Namespace: "testns", GroupVersionKind: gvk.Gateway}, - Spec: &networking.Gateway{ - Servers: []*networking.Server{ - { - Port: &networking.Port{Name: "http", Number: 443, Protocol: "HTTPS"}, - Hosts: []string{"*.example.com"}, - Tls: &networking.ServerTLSSettings{CredentialName: "test", Mode: networking.ServerTLSSettings_SIMPLE}, - }, - { - Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, - Hosts: []string{"foo.example.com"}, - }, - }, - }, - }, - { - Meta: config.Meta{Name: "gateway2", Namespace: "testns", GroupVersionKind: gvk.Gateway}, - Spec: &networking.Gateway{ - Servers: []*networking.Server{ - { - Port: &networking.Port{Name: "http", Number: 443, Protocol: "HTTPS"}, - Hosts: []string{"*.exampleone.com"}, - Tls: &networking.ServerTLSSettings{CredentialName: "test", Mode: networking.ServerTLSSettings_SIMPLE}, - }, - { - Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, - Hosts: []string{"bar.example.com"}, - }, - }, - }, - }, - }, - nil, - []string{"0.0.0.0_443", "0.0.0.0_80"}, - }, - { - "gateway with multiple TLS HTTPS TCP servers", - &pilot_model.Proxy{}, - []config.Config{ - { - Meta: config.Meta{Name: "gateway1", Namespace: "testns", GroupVersionKind: gvk.Gateway}, - Spec: &networking.Gateway{ - Servers: []*networking.Server{ - { - Port: &networking.Port{Name: "tcp", Number: 443, Protocol: "TLS"}, - Hosts: []string{"*.example.com"}, - Tls: &networking.ServerTLSSettings{CredentialName: "test", Mode: networking.ServerTLSSettings_SIMPLE}, - }, - { - Port: &networking.Port{Name: "tcp", Number: 443, Protocol: "HTTPS"}, - Hosts: []string{"https.example.com"}, - Tls: &networking.ServerTLSSettings{CredentialName: "test", Mode: networking.ServerTLSSettings_SIMPLE}, - }, - { - Port: &networking.Port{Name: "tcp", Number: 9443, Protocol: "TCP"}, - Hosts: []string{"tcp.example.com"}, - }, - }, - }, - }, - { - Meta: config.Meta{Name: "gateway2", Namespace: "testns", GroupVersionKind: gvk.Gateway}, - Spec: &networking.Gateway{ - Servers: []*networking.Server{ - { - Port: &networking.Port{Name: "http", Number: 443, Protocol: "HTTPS"}, - Hosts: []string{"*.exampleone.com"}, - Tls: &networking.ServerTLSSettings{CredentialName: "test", Mode: networking.ServerTLSSettings_SIMPLE}, - }, - { - Port: &networking.Port{Name: "tcp", Number: 443, Protocol: "TLS"}, - Hosts: []string{"*.exampleone.com"}, - Tls: &networking.ServerTLSSettings{CredentialName: "test", Mode: networking.ServerTLSSettings_SIMPLE}, - }, - }, - }, - }, - }, - []config.Config{ - { - Meta: config.Meta{Name: uuid.NewString(), Namespace: uuid.NewString(), GroupVersionKind: gvk.VirtualService}, - Spec: &networking.VirtualService{ - Gateways: []string{"testns/gateway1"}, - Hosts: []string{"tcp.example.com"}, - Tcp: []*networking.TCPRoute{ - { - Match: []*networking.L4MatchAttributes{ - { - Port: 9443, - }, - }, - Route: []*networking.RouteDestination{ - { - Destination: &networking.Destination{ - Host: "foo.com", - }, - }, - }, - }, - }, - }, - }, - }, - []string{"0.0.0.0_443", "0.0.0.0_9443"}, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - Configs := make([]config.Config, 0) - Configs = append(Configs, tt.gateways...) - Configs = append(Configs, tt.virtualServices...) - cg := NewConfigGenTest(t, TestOptions{ - Configs: Configs, - }) - cg.MemRegistry.WantGetProxyServiceInstances = tt.node.ServiceInstances - proxy := cg.SetupProxy(&proxyGateway) - if tt.node.Metadata != nil { - proxy.Metadata = tt.node.Metadata - } else { - proxy.Metadata = &proxyGatewayMetadata - } - - builder := cg.ConfigGen.buildGatewayListeners(NewListenerBuilder(proxy, cg.PushContext())) - listeners := xdstest.ExtractListenerNames(builder.gatewayListeners) - sort.Strings(listeners) - sort.Strings(tt.expectedListeners) - if !reflect.DeepEqual(listeners, tt.expectedListeners) { - t.Fatalf("Expected listeners: %v, got: %v\n%v", tt.expectedListeners, listeners, proxyGateway.MergedGateway.MergedServers) - } - xdstest.ValidateListeners(t, builder.gatewayListeners) - - // gateways bind to port, but exact_balance can still be used - for _, l := range builder.gatewayListeners { - if l.ConnectionBalanceConfig != nil { - t.Fatalf("expected connection balance config to be empty, found %v", l.ConnectionBalanceConfig) - } - } - }) - } -} - -func TestBuildNameToServiceMapForHttpRoutes(t *testing.T) { - virtualServiceSpec := &networking.VirtualService{ - Hosts: []string{"*.example.org"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "foo.example.org", - }, - }, - { - Destination: &networking.Destination{ - Host: "bar.example.org", - }, - }, - }, - Mirror: &networking.Destination{ - Host: "baz.example.org", - }, - }, - }, - } - virtualService := config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "virtual-service", - Namespace: "test", - }, - Spec: virtualServiceSpec, - } - - fooHostName := host.Name("foo.example.org") - fooServiceInTestNamespace := &pilot_model.Service{ - Hostname: fooHostName, - Ports: []*pilot_model.Port{{ - Name: "http", - Protocol: "HTTP", - Port: 80, - }}, - Attributes: pilot_model.ServiceAttributes{ - Namespace: "test", - ExportTo: map[visibility.Instance]bool{ - visibility.Private: true, - }, - }, - } - - barHostName := host.Name("bar.example.org") - barServiceInDefaultNamespace := &pilot_model.Service{ - Hostname: barHostName, - Ports: []*pilot_model.Port{{ - Name: "http", - Protocol: "HTTP", - Port: 8080, - }}, - Attributes: pilot_model.ServiceAttributes{ - Namespace: "default", - ExportTo: map[visibility.Instance]bool{ - visibility.Public: true, - }, - }, - } - - bazHostName := host.Name("baz.example.org") - bazServiceInDefaultNamespace := &pilot_model.Service{ - Hostname: bazHostName, - Ports: []*pilot_model.Port{{ - Name: "http", - Protocol: "HTTP", - Port: 8090, - }}, - Attributes: pilot_model.ServiceAttributes{ - Namespace: "default", - ExportTo: map[visibility.Instance]bool{ - visibility.Private: true, - }, - }, - } - - cg := NewConfigGenTest(t, TestOptions{ - Configs: []config.Config{virtualService}, - Services: []*pilot_model.Service{fooServiceInTestNamespace, barServiceInDefaultNamespace, bazServiceInDefaultNamespace}, - }) - proxy := &pilot_model.Proxy{ - Type: pilot_model.Router, - ConfigNamespace: "test", - } - proxy = cg.SetupProxy(proxy) - - nameToServiceMap := buildNameToServiceMapForHTTPRoutes(proxy, cg.env.PushContext, virtualService) - - if len(nameToServiceMap) != 3 { - t.Errorf("The length of nameToServiceMap is wrong.") - } - - if service, exist := nameToServiceMap[fooHostName]; !exist || service == nil { - t.Errorf("The service of %s not found or should be not nil.", fooHostName) - } else { - if service.Ports[0].Port != 80 { - t.Errorf("The port of %s is wrong.", fooHostName) - } - - if service.Attributes.Namespace != "test" { - t.Errorf("The namespace of %s is wrong.", fooHostName) - } - } - - if service, exist := nameToServiceMap[barHostName]; !exist || service == nil { - t.Errorf("The service of %s not found or should be not nil", barHostName) - } else { - if service.Ports[0].Port != 8080 { - t.Errorf("The port of %s is wrong.", barHostName) - } - - if service.Attributes.Namespace != "default" { - t.Errorf("The namespace of %s is wrong.", barHostName) - } - } - - if service, exist := nameToServiceMap[bazHostName]; !exist || service != nil { - t.Errorf("The value of hostname %s mapping must be exist and it should be nil.", bazHostName) - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/httproute.go b/pilot/pkg/networking/core/v1alpha3/httproute.go deleted file mode 100644 index 142fb31e0..000000000 --- a/pilot/pkg/networking/core/v1alpha3/httproute.go +++ /dev/null @@ -1,736 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "fmt" - "sort" - "strconv" - "strings" -) - -import ( - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "google.golang.org/protobuf/types/known/durationpb" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3/envoyfilter" - istio_route "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3/route" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/telemetry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/proto" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -const ( - wildcardDomainPrefix = "*." - inboundVirtualHostPrefix = string(model.TrafficDirectionInbound) + "|http|" -) - -// BuildHTTPRoutes produces a list of routes for the proxy -func (configgen *ConfigGeneratorImpl) BuildHTTPRoutes( - node *model.Proxy, - req *model.PushRequest, - routeNames []string, -) ([]*discovery.Resource, model.XdsLogDetails) { - var routeConfigurations model.Resources - - efw := req.Push.EnvoyFilters(node) - hit, miss := 0, 0 - switch node.Type { - case model.SidecarProxy: - vHostCache := make(map[int][]*route.VirtualHost) - // dependent envoyfilters' key, calculate in front once to prevent calc for each route. - envoyfilterKeys := efw.Keys() - for _, routeName := range routeNames { - rc, cached := configgen.buildSidecarOutboundHTTPRouteConfig(node, req, routeName, vHostCache, efw, envoyfilterKeys) - if cached && !features.EnableUnsafeAssertions { - hit++ - } else { - miss++ - } - if rc == nil { - emptyRoute := &route.RouteConfiguration{ - Name: routeName, - VirtualHosts: []*route.VirtualHost{}, - ValidateClusters: proto.BoolFalse, - } - rc = &discovery.Resource{ - Name: routeName, - Resource: util.MessageToAny(emptyRoute), - } - } - routeConfigurations = append(routeConfigurations, rc) - } - case model.Router: - for _, routeName := range routeNames { - rc := configgen.buildGatewayHTTPRouteConfig(node, req.Push, routeName) - if rc != nil { - rc = envoyfilter.ApplyRouteConfigurationPatches(networking.EnvoyFilter_GATEWAY, node, efw, rc) - resource := &discovery.Resource{ - Name: routeName, - Resource: util.MessageToAny(rc), - } - routeConfigurations = append(routeConfigurations, resource) - } - } - } - if !features.EnableRDSCaching { - return routeConfigurations, model.DefaultXdsLogDetails - } - return routeConfigurations, model.XdsLogDetails{AdditionalInfo: fmt.Sprintf("cached:%v/%v", hit, hit+miss)} -} - -// buildSidecarInboundHTTPRouteConfig builds the route config with a single wildcard virtual host on the inbound path -// TODO: trace decorators, inbound timeouts -func buildSidecarInboundHTTPRouteConfig(lb *ListenerBuilder, cc inboundChainConfig) *route.RouteConfiguration { - traceOperation := telemetry.TraceOperation(string(cc.telemetryMetadata.InstanceHostname), int(cc.port.Port)) - defaultRoute := istio_route.BuildDefaultHTTPInboundRoute(cc.clusterName, traceOperation) - - inboundVHost := &route.VirtualHost{ - Name: inboundVirtualHostPrefix + strconv.Itoa(int(cc.port.Port)), // Format: "inbound|http|%d" - Domains: []string{"*"}, - Routes: []*route.Route{defaultRoute}, - } - - r := &route.RouteConfiguration{ - Name: cc.clusterName, - VirtualHosts: []*route.VirtualHost{inboundVHost}, - ValidateClusters: proto.BoolFalse, - } - efw := lb.push.EnvoyFilters(lb.node) - r = envoyfilter.ApplyRouteConfigurationPatches(networking.EnvoyFilter_SIDECAR_INBOUND, lb.node, efw, r) - return r -} - -// buildSidecarOutboundHTTPRouteConfig builds an outbound HTTP Route for sidecar. -// Based on port, will determine all virtual hosts that listen on the port. -func (configgen *ConfigGeneratorImpl) buildSidecarOutboundHTTPRouteConfig( - node *model.Proxy, - req *model.PushRequest, - routeName string, - vHostCache map[int][]*route.VirtualHost, - efw *model.EnvoyFilterWrapper, - efKeys []string, -) (*discovery.Resource, bool) { - var virtualHosts []*route.VirtualHost - listenerPort := 0 - useSniffing := false - var err error - if features.EnableProtocolSniffingForOutbound && - !strings.HasPrefix(routeName, model.UnixAddressPrefix) { - index := strings.IndexRune(routeName, ':') - if index != -1 { - useSniffing = true - } - listenerPort, err = strconv.Atoi(routeName[index+1:]) - } else { - listenerPort, err = strconv.Atoi(routeName) - } - - if err != nil { - // we have a port whose name is http_proxy or unix:///foo/bar - // check for both. - if routeName != model.RDSHttpProxy && !strings.HasPrefix(routeName, model.UnixAddressPrefix) { - // TODO: This is potentially one place where envoyFilter ADD operation can be helpful if the - // user wants to ship a custom RDS. But at this point, the match semantics are murky. We have no - // object to match upon. This needs more thought. For now, we will continue to return nil for - // unknown routes - return nil, false - } - } - - var routeCache *istio_route.Cache - var resource *discovery.Resource - - cacheHit := false - if useSniffing && listenerPort != 0 { - // Check if we have already computed the list of all virtual hosts for this port - // If so, then we simply have to return only the relevant virtual hosts for - // this listener's host:port - if vhosts, exists := vHostCache[listenerPort]; exists { - virtualHosts = getVirtualHostsForSniffedServicePort(vhosts, routeName) - cacheHit = true - } - } - if !cacheHit { - virtualHosts, resource, routeCache = BuildSidecarOutboundVirtualHosts(node, req.Push, routeName, listenerPort, efKeys, configgen.Cache) - if resource != nil { - return resource, true - } - if listenerPort > 0 { - // only cache for tcp ports and not for uds - vHostCache[listenerPort] = virtualHosts - } - - // FIXME: This will ignore virtual services with hostnames that do not match any service in the registry - // per api spec, these hostnames + routes should appear in the virtual hosts (think bookinfo.com and - // productpage.ns1.svc.cluster.local). See the TODO in BuildSidecarOutboundVirtualHosts for the right solution - if useSniffing { - virtualHosts = getVirtualHostsForSniffedServicePort(virtualHosts, routeName) - } - } - - util.SortVirtualHosts(virtualHosts) - - if !useSniffing { - virtualHosts = append(virtualHosts, buildCatchAllVirtualHost(node)) - } - - out := &route.RouteConfiguration{ - Name: routeName, - VirtualHosts: virtualHosts, - ValidateClusters: proto.BoolFalse, - } - - // apply envoy filter patches - out = envoyfilter.ApplyRouteConfigurationPatches(networking.EnvoyFilter_SIDECAR_OUTBOUND, node, efw, out) - - resource = &discovery.Resource{ - Name: out.Name, - Resource: util.MessageToAny(out), - } - - if features.EnableRDSCaching && routeCache != nil { - configgen.Cache.Add(routeCache, req, resource) - } - - return resource, false -} - -// TODO: merge with IstioEgressListenerWrapper.selectVirtualServices -// selectVirtualServices selects the virtual services by matching given services' host names. -func selectVirtualServices(virtualServices []config.Config, servicesByName map[host.Name]*model.Service) []config.Config { - out := make([]config.Config, 0) - for _, c := range virtualServices { - rule := c.Spec.(*networking.VirtualService) - var match bool - - // Selection algorithm: - // virtualservices have a list of hosts in the API spec - // if any host in the list matches one service hostname, select the virtual service - // and break out of the loop. - for _, h := range rule.Hosts { - // TODO: This is a bug. VirtualServices can have many hosts - // while the user might be importing only a single host - // We need to generate a new VirtualService with just the matched host - if servicesByName[host.Name(h)] != nil { - match = true - break - } - - for svcHost := range servicesByName { - if host.Name(h).Matches(svcHost) { - match = true - break - } - } - - if match { - break - } - } - - if match { - out = append(out, c) - } - } - - return out -} - -func BuildSidecarOutboundVirtualHosts(node *model.Proxy, push *model.PushContext, - routeName string, - listenerPort int, - efKeys []string, - xdsCache model.XdsCache, -) ([]*route.VirtualHost, *discovery.Resource, *istio_route.Cache) { - var virtualServices []config.Config - var services []*model.Service - - // Get the services from the egress listener. When sniffing is enabled, we send - // route name as foo.bar.com:8080 which is going to match against the wildcard - // egress listener only. A route with sniffing would not have been generated if there - // was a sidecar with explicit port (and hence protocol declaration). A route with - // sniffing is generated only in the case of the catch all egress listener. - egressListener := node.SidecarScope.GetEgressListenerForRDS(listenerPort, routeName) - // We should never be getting a nil egress listener because the code that setup this RDS - // call obviously saw an egress listener - if egressListener == nil { - return nil, nil, nil - } - - services = egressListener.Services() - // To maintain correctness, we should only use the virtualservices for - // this listener and not all virtual services accessible to this proxy. - virtualServices = egressListener.VirtualServices() - - // When generating RDS for ports created via the SidecarScope, we treat ports as HTTP proxy style ports - // if ports protocol is HTTP_PROXY. - if egressListener.IstioListener != nil && egressListener.IstioListener.Port != nil && - protocol.Parse(egressListener.IstioListener.Port.Protocol) == protocol.HTTP_PROXY { - listenerPort = 0 - } - - servicesByName := make(map[host.Name]*model.Service) - for _, svc := range services { - if listenerPort == 0 { - // Take all ports when listen port is 0 (http_proxy or uds) - // Expect virtualServices to resolve to right port - servicesByName[svc.Hostname] = svc - } else if svcPort, exists := svc.Ports.GetByPort(listenerPort); exists { - servicesByName[svc.Hostname] = &model.Service{ - Hostname: svc.Hostname, - DefaultAddress: svc.GetAddressForProxy(node), - MeshExternal: svc.MeshExternal, - Resolution: svc.Resolution, - Ports: []*model.Port{svcPort}, - Attributes: model.ServiceAttributes{ - Namespace: svc.Attributes.Namespace, - ServiceRegistry: svc.Attributes.ServiceRegistry, - }, - } - } - } - - var routeCache *istio_route.Cache - - if listenerPort > 0 { - services = make([]*model.Service, 0, len(servicesByName)) - // sort services - for _, svc := range servicesByName { - services = append(services, svc) - } - sort.SliceStable(services, func(i, j int) bool { - return services[i].Hostname <= services[j].Hostname - }) - - routeCache = &istio_route.Cache{ - RouteName: routeName, - ProxyVersion: node.Metadata.IstioVersion, - ClusterID: string(node.Metadata.ClusterID), - DNSDomain: node.DNSDomain, - DNSCapture: bool(node.Metadata.DNSCapture), - DNSAutoAllocate: bool(node.Metadata.DNSAutoAllocate), - AllowAny: util.IsAllowAnyOutbound(node), - ListenerPort: listenerPort, - Services: services, - VirtualServices: virtualServices, - DelegateVirtualServices: push.DelegateVirtualServicesConfigKey(virtualServices), - EnvoyFilterKeys: efKeys, - } - } - - // This is hack to keep consistent with previous behavior. - if listenerPort != 80 { - // only select virtualServices that matches a service - virtualServices = selectVirtualServices(virtualServices, servicesByName) - } - // Get list of virtual services bound to the mesh gateway - virtualHostWrappers := istio_route.BuildSidecarVirtualHostWrapper(routeCache, node, push, servicesByName, virtualServices, listenerPort) - - resource, exist := xdsCache.Get(routeCache) - if exist && !features.EnableUnsafeAssertions { - return nil, resource, routeCache - } - - vHostPortMap := make(map[int][]*route.VirtualHost) - vhosts := sets.Set{} - vhdomains := sets.Set{} - knownFQDN := sets.Set{} - - buildVirtualHost := func(hostname string, vhwrapper istio_route.VirtualHostWrapper, svc *model.Service) *route.VirtualHost { - name := util.DomainName(hostname, vhwrapper.Port) - if duplicateVirtualHost(name, vhosts) { - // This means this virtual host has caused duplicate virtual host name. - var msg string - if svc == nil { - msg = fmt.Sprintf("duplicate domain from virtual service: %s", name) - } else { - msg = fmt.Sprintf("duplicate domain from service: %s", name) - } - push.AddMetric(model.DuplicatedDomains, name, node.ID, msg) - return nil - } - var domains []string - var altHosts []string - if svc == nil { - domains = []string{util.IPv6Compliant(hostname), name} - } else { - domains, altHosts = generateVirtualHostDomains(svc, vhwrapper.Port, node) - } - dl := len(domains) - domains = dedupeDomains(domains, vhdomains, altHosts, knownFQDN) - if dl != len(domains) { - var msg string - if svc == nil { - msg = fmt.Sprintf("duplicate domain from virtual service: %s", name) - } else { - msg = fmt.Sprintf("duplicate domain from service: %s", name) - } - // This means this virtual host has caused duplicate virtual host domain. - push.AddMetric(model.DuplicatedDomains, name, node.ID, msg) - } - if len(domains) > 0 { - return &route.VirtualHost{ - Name: name, - Domains: domains, - Routes: vhwrapper.Routes, - IncludeRequestAttemptCount: true, - } - } - - return nil - } - - for _, virtualHostWrapper := range virtualHostWrappers { - for _, svc := range virtualHostWrapper.Services { - name := util.DomainName(string(svc.Hostname), virtualHostWrapper.Port) - knownFQDN.InsertAll(name, string(svc.Hostname)) - } - } - - for _, virtualHostWrapper := range virtualHostWrappers { - // If none of the routes matched by source, skip this virtual host - if len(virtualHostWrapper.Routes) == 0 { - continue - } - virtualHosts := make([]*route.VirtualHost, 0, len(virtualHostWrapper.VirtualServiceHosts)+len(virtualHostWrapper.Services)) - - for _, hostname := range virtualHostWrapper.VirtualServiceHosts { - if vhost := buildVirtualHost(hostname, virtualHostWrapper, nil); vhost != nil { - virtualHosts = append(virtualHosts, vhost) - } - } - - for _, svc := range virtualHostWrapper.Services { - if vhost := buildVirtualHost(string(svc.Hostname), virtualHostWrapper, svc); vhost != nil { - virtualHosts = append(virtualHosts, vhost) - } - } - vHostPortMap[virtualHostWrapper.Port] = append(vHostPortMap[virtualHostWrapper.Port], virtualHosts...) - } - - var out []*route.VirtualHost - if listenerPort == 0 { - out = mergeAllVirtualHosts(vHostPortMap) - } else { - out = vHostPortMap[listenerPort] - } - - return out, nil, routeCache -} - -// duplicateVirtualHost checks whether the virtual host with the same name exists in the route. -func duplicateVirtualHost(vhost string, vhosts sets.Set) bool { - if vhosts.Contains(vhost) { - return true - } - vhosts.Insert(vhost) - return false -} - -// dedupeDomains removes the duplicate domains from the passed in domains. -func dedupeDomains(domains []string, vhdomains sets.Set, expandedHosts []string, knownFQDNs sets.Set) []string { - temp := domains[:0] - for _, d := range domains { - if vhdomains.Contains(d) { - continue - } - // Check if the domain is an "expanded" host, and its also a known FQDN - // This prevents a case where a domain like "foo.com.cluster.local" gets expanded to "foo.com", overwriting - // the real "foo.com" - // This works by providing a list of domains that were added as expanding the DNS domain as part of expandedHosts, - // and a list of known unexpanded FQDNs to compare against - if util.ListContains(expandedHosts, d) && knownFQDNs.Contains(d) { // O(n) search, but n is at most 10 - continue - } - temp = append(temp, d) - vhdomains.Insert(d) - } - return temp -} - -// Returns the set of virtual hosts that correspond to the listener that has HTTP protocol detection -// setup. This listener should only get the virtual hosts that correspond to this service+port and not -// all virtual hosts that are usually supplied for 0.0.0.0:PORT. -func getVirtualHostsForSniffedServicePort(vhosts []*route.VirtualHost, routeName string) []*route.VirtualHost { - var virtualHosts []*route.VirtualHost - for _, vh := range vhosts { - for _, domain := range vh.Domains { - if domain == routeName { - virtualHosts = append(virtualHosts, vh) - break - } - } - } - - if len(virtualHosts) == 0 { - return virtualHosts - } - if len(virtualHosts) == 1 { - virtualHosts[0].Domains = []string{"*"} - return virtualHosts - } - if features.EnableUnsafeAssertions { - panic(fmt.Sprintf("unexpectedly matched multiple virtual hosts for %v: %v", routeName, virtualHosts)) - } - return virtualHosts -} - -// generateVirtualHostDomains generates the set of domain matches for a service being accessed from -// a proxy node -func generateVirtualHostDomains(service *model.Service, port int, node *model.Proxy) ([]string, []string) { - altHosts := GenerateAltVirtualHosts(string(service.Hostname), port, node.DNSDomain) - domains := []string{util.IPv6Compliant(string(service.Hostname)), util.DomainName(string(service.Hostname), port)} - domains = append(domains, altHosts...) - - if service.Resolution == model.Passthrough && - service.Attributes.ServiceRegistry == provider.Kubernetes { - for _, domain := range domains { - domains = append(domains, wildcardDomainPrefix+domain) - } - } - - svcAddr := service.GetAddressForProxy(node) - if len(svcAddr) > 0 && svcAddr != constants.UnspecifiedIP { - domains = append(domains, util.IPv6Compliant(svcAddr), util.DomainName(svcAddr, port)) - } - return domains, altHosts -} - -// GenerateAltVirtualHosts given a service and a port, generates all possible HTTP Host headers. -// For example, a service of the form foo.local.campus.net on port 80, with local domain "local.campus.net" -// could be accessed as http://foo:80 within the .local network, as http://foo.local:80 (by other clients -// in the campus.net domain), as http://foo.local.campus:80, etc. -// NOTE: When a sidecar in remote.campus.net domain is talking to foo.local.campus.net, -// we should only generate foo.local, foo.local.campus, etc (and never just "foo"). -// -// - Given foo.local.campus.net on proxy domain local.campus.net, this function generates -// foo:80, foo.local:80, foo.local.campus:80, with and without ports. It will not generate -// foo.local.campus.net (full hostname) since its already added elsewhere. -// -// - Given foo.local.campus.net on proxy domain remote.campus.net, this function generates -// foo.local:80, foo.local.campus:80 -// -// - Given foo.local.campus.net on proxy domain "" or proxy domain example.com, this -// function returns nil -func GenerateAltVirtualHosts(hostname string, port int, proxyDomain string) []string { - if strings.Contains(proxyDomain, ".svc.") { - return generateAltVirtualHostsForKubernetesService(hostname, port, proxyDomain) - } - - var vhosts []string - uniqueHostnameParts, sharedDNSDomainParts := getUniqueAndSharedDNSDomain(hostname, proxyDomain) - - // If there is no shared DNS name (e.g., foobar.com service on local.net proxy domain) - // do not generate any alternate virtual host representations - if len(sharedDNSDomainParts) == 0 { - return nil - } - - uniqueHostname := strings.Join(uniqueHostnameParts, ".") - - // Add the uniqueHost. - vhosts = append(vhosts, uniqueHostname, util.DomainName(uniqueHostname, port)) - if len(uniqueHostnameParts) == 2 { - // This is the case of uniqHostname having namespace already. - dnsHostName := uniqueHostname + "." + sharedDNSDomainParts[0] - vhosts = append(vhosts, dnsHostName, util.DomainName(dnsHostName, port)) - } - return vhosts -} - -func generateAltVirtualHostsForKubernetesService(hostname string, port int, proxyDomain string) []string { - id := strings.Index(proxyDomain, ".svc.") - ih := strings.Index(hostname, ".svc.") - if ih > 0 { // Proxy and service hostname are in kube - ns := strings.Index(hostname, ".") - if ns+1 >= len(hostname) || ns+1 > ih { - // Invalid domain - return nil - } - if hostname[ns+1:ih] == proxyDomain[:id] { - // Same namespace - return []string{ - hostname[:ns], - util.DomainName(hostname[:ns], port), - hostname[:ih] + ".svc", - util.DomainName(hostname[:ih]+".svc", port), - hostname[:ih], - util.DomainName(hostname[:ih], port), - } - } - // Different namespace - return []string{ - hostname[:ih], - util.DomainName(hostname[:ih], port), - hostname[:ih] + ".svc", - util.DomainName(hostname[:ih]+".svc", port), - } - } - // Proxy is in k8s, but service isn't. No alt hosts - return nil -} - -// mergeAllVirtualHosts across all ports. On routes for ports other than port 80, -// virtual hosts without an explicit port suffix (IP:PORT) should not be added -func mergeAllVirtualHosts(vHostPortMap map[int][]*route.VirtualHost) []*route.VirtualHost { - var virtualHosts []*route.VirtualHost - for p, vhosts := range vHostPortMap { - if p == 80 { - virtualHosts = append(virtualHosts, vhosts...) - } else { - for _, vhost := range vhosts { - var newDomains []string - for _, domain := range vhost.Domains { - if strings.Contains(domain, ":") { - newDomains = append(newDomains, domain) - } - } - if len(newDomains) > 0 { - vhost.Domains = newDomains - virtualHosts = append(virtualHosts, vhost) - } - } - } - } - return virtualHosts -} - -// reverseArray returns its argument string array reversed -func reverseArray(r []string) []string { - for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 { - r[i], r[j] = r[j], r[i] - } - return r -} - -func min(a, b int) int { - if a < b { - return a - } - return b -} - -// getUniqueAndSharedDNSDomain computes the unique and shared DNS suffix from a FQDN service name and -// the proxy's local domain with namespace. This is especially useful in Kubernetes environments, where -// a two services can have same name in different namespaces (e.g., foo.ns1.svc.cluster.local, -// foo.ns2.svc.cluster.local). In this case, if the proxy is in ns2.svc.cluster.local, then while -// generating alt virtual hosts for service foo.ns1 for the sidecars in ns2 namespace, we should generate -// foo.ns1, foo.ns1.svc, foo.ns1.svc.cluster.local and should not generate a virtual host called "foo" for -// foo.ns1 service. -// So given foo.ns1.svc.cluster.local and ns2.svc.cluster.local, this function will return -// foo.ns1, and svc.cluster.local. -// When given foo.ns2.svc.cluster.local and ns2.svc.cluster.local, this function will return -// foo, ns2.svc.cluster.local. -func getUniqueAndSharedDNSDomain(fqdnHostname, proxyDomain string) (partsUnique []string, partsShared []string) { - // split them by the dot and reverse the arrays, so that we can - // start collecting the shared bits of DNS suffix. - // E.g., foo.ns1.svc.cluster.local -> local,cluster,svc,ns1,foo - // ns2.svc.cluster.local -> local,cluster,svc,ns2 - partsFQDN := strings.Split(fqdnHostname, ".") - partsProxyDomain := strings.Split(proxyDomain, ".") - partsFQDNInReverse := reverseArray(partsFQDN) - partsProxyDomainInReverse := reverseArray(partsProxyDomain) - var sharedSuffixesInReverse []string // pieces shared between proxy and svc. e.g., local,cluster,svc - - for i := 0; i < min(len(partsFQDNInReverse), len(partsProxyDomainInReverse)); i++ { - if partsFQDNInReverse[i] == partsProxyDomainInReverse[i] { - sharedSuffixesInReverse = append(sharedSuffixesInReverse, partsFQDNInReverse[i]) - } else { - break - } - } - - if len(sharedSuffixesInReverse) == 0 { - partsUnique = partsFQDN - } else { - // get the non shared pieces (ns1, foo) and reverse Array - partsUnique = reverseArray(partsFQDNInReverse[len(sharedSuffixesInReverse):]) - partsShared = reverseArray(sharedSuffixesInReverse) - } - return -} - -func buildCatchAllVirtualHost(node *model.Proxy) *route.VirtualHost { - if util.IsAllowAnyOutbound(node) { - egressCluster := util.PassthroughCluster - notimeout := durationpb.New(0) - - // no need to check for nil value as the previous if check has checked - if node.SidecarScope.OutboundTrafficPolicy.EgressProxy != nil { - // user has provided an explicit destination for all the unknown traffic. - // build a cluster out of this destination - egressCluster = istio_route.GetDestinationCluster(node.SidecarScope.OutboundTrafficPolicy.EgressProxy, - nil, 0) - } - - routeAction := &route.RouteAction{ - ClusterSpecifier: &route.RouteAction_Cluster{Cluster: egressCluster}, - // Disable timeout instead of assuming some defaults. - Timeout: notimeout, - // Use deprecated value for now as the replacement MaxStreamDuration has some regressions. - // nolint: staticcheck - MaxGrpcTimeout: notimeout, - } - - return &route.VirtualHost{ - Name: util.Passthrough, - Domains: []string{"*"}, - Routes: []*route.Route{ - { - Name: util.Passthrough, - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_Prefix{Prefix: "/"}, - }, - Action: &route.Route_Route{ - Route: routeAction, - }, - }, - }, - IncludeRequestAttemptCount: true, - } - } - - return &route.VirtualHost{ - Name: util.BlackHole, - Domains: []string{"*"}, - Routes: []*route.Route{ - { - Name: util.BlackHole, - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_Prefix{Prefix: "/"}, - }, - Action: &route.Route_DirectResponse{ - DirectResponse: &route.DirectResponseAction{ - Status: 502, - }, - }, - }, - }, - IncludeRequestAttemptCount: true, - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/httproute_test.go b/pilot/pkg/networking/core/v1alpha3/httproute_test.go deleted file mode 100644 index b504f1085..000000000 --- a/pilot/pkg/networking/core/v1alpha3/httproute_test.go +++ /dev/null @@ -1,1583 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "fmt" - "reflect" - "testing" - "time" -) - -import ( - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - meshapi "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/config/visibility" -) - -func TestGenerateVirtualHostDomains(t *testing.T) { - cases := []struct { - name string - service *model.Service - port int - node *model.Proxy - want []string - }{ - { - name: "same domain", - service: &model.Service{ - Hostname: "foo.local.campus.net", - MeshExternal: false, - }, - port: 80, - node: &model.Proxy{ - DNSDomain: "local.campus.net", - }, - want: []string{ - "foo.local.campus.net", "foo.local.campus.net:80", - "foo", "foo:80", - }, - }, - { - name: "different domains with some shared dns", - service: &model.Service{ - Hostname: "foo.local.campus.net", - MeshExternal: false, - }, - port: 80, - node: &model.Proxy{ - DNSDomain: "remote.campus.net", - }, - want: []string{ - "foo.local.campus.net", - "foo.local.campus.net:80", - "foo.local", - "foo.local:80", - "foo.local.campus", - "foo.local.campus:80", - }, - }, - { - name: "different domains with no shared dns", - service: &model.Service{ - Hostname: "foo.local.campus.net", - MeshExternal: false, - }, - port: 80, - node: &model.Proxy{ - DNSDomain: "example.com", - }, - want: []string{"foo.local.campus.net", "foo.local.campus.net:80"}, - }, - { - name: "k8s service with default domain", - service: &model.Service{ - Hostname: "echo.default.svc.cluster.local", - MeshExternal: false, - }, - port: 8123, - node: &model.Proxy{ - DNSDomain: "default.svc.cluster.local", - }, - want: []string{ - "echo.default.svc.cluster.local", - "echo.default.svc.cluster.local:8123", - "echo", - "echo:8123", - "echo.default.svc", - "echo.default.svc:8123", - "echo.default", - "echo.default:8123", - }, - }, - { - name: "k8s service with default domain and different namespace", - service: &model.Service{ - Hostname: "echo.default.svc.cluster.local", - MeshExternal: false, - }, - port: 8123, - node: &model.Proxy{ - DNSDomain: "mesh.svc.cluster.local", - }, - want: []string{ - "echo.default.svc.cluster.local", - "echo.default.svc.cluster.local:8123", - "echo.default", - "echo.default:8123", - "echo.default.svc", - "echo.default.svc:8123", - }, - }, - { - name: "k8s service with custom domain 2", - service: &model.Service{ - Hostname: "google.local", - MeshExternal: false, - }, - port: 8123, - node: &model.Proxy{ - DNSDomain: "foo.svc.custom.k8s.local", - }, - want: []string{"google.local", "google.local:8123"}, - }, - { - name: "ipv4 domain", - service: &model.Service{ - Hostname: "1.2.3.4", - MeshExternal: false, - }, - port: 8123, - node: &model.Proxy{ - DNSDomain: "example.com", - }, - want: []string{"1.2.3.4", "1.2.3.4:8123"}, - }, - { - name: "ipv6 domain", - service: &model.Service{ - Hostname: "2406:3003:2064:35b8:864:a648:4b96:e37d", - MeshExternal: false, - }, - port: 8123, - node: &model.Proxy{ - DNSDomain: "example.com", - }, - want: []string{"[2406:3003:2064:35b8:864:a648:4b96:e37d]", "[2406:3003:2064:35b8:864:a648:4b96:e37d]:8123"}, - }, - { - name: "back subset of cluster domain in address", - service: &model.Service{ - Hostname: "aaa.example.local", - MeshExternal: true, - }, - port: 7777, - node: &model.Proxy{ - DNSDomain: "tests.svc.cluster.local", - }, - want: []string{"aaa.example.local", "aaa.example.local:7777"}, - }, - { - name: "front subset of cluster domain in address", - service: &model.Service{ - Hostname: "aaa.example.my", - MeshExternal: true, - }, - port: 7777, - node: &model.Proxy{ - DNSDomain: "tests.svc.my.long.domain.suffix", - }, - want: []string{"aaa.example.my", "aaa.example.my:7777"}, - }, - { - name: "large subset of cluster domain in address", - service: &model.Service{ - Hostname: "aaa.example.my.long", - MeshExternal: true, - }, - port: 7777, - node: &model.Proxy{ - DNSDomain: "tests.svc.my.long.domain.suffix", - }, - want: []string{"aaa.example.my.long", "aaa.example.my.long:7777"}, - }, - { - name: "no overlap of cluster domain in address", - service: &model.Service{ - Hostname: "aaa.example.com", - MeshExternal: true, - }, - port: 7777, - node: &model.Proxy{ - DNSDomain: "tests.svc.cluster.local", - }, - want: []string{"aaa.example.com", "aaa.example.com:7777"}, - }, - } - - testFn := func(service *model.Service, port int, node *model.Proxy, want []string) error { - out, _ := generateVirtualHostDomains(service, port, node) - if !reflect.DeepEqual(out, want) { - return fmt.Errorf("unexpected virtual hosts:\ngot %v\nwant %v", out, want) - } - return nil - } - - for _, c := range cases { - c := c - t.Run(c.name, func(t *testing.T) { - if err := testFn(c.service, c.port, c.node, c.want); err != nil { - t.Error(err) - } - }) - } -} - -func TestSidecarOutboundHTTPRouteConfigWithDuplicateHosts(t *testing.T) { - virtualServiceSpec := &networking.VirtualService{ - Hosts: []string{"test-duplicate-domains.default.svc.cluster.local", "test-duplicate-domains.default"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "test-duplicate-domains.default", - }, - }, - }, - }, - }, - } - virtualServiceSpecDefault := &networking.VirtualService{ - Hosts: []string{"test.default"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "test.default", - }, - }, - }, - }, - }, - } - - cases := []struct { - name string - services []*model.Service - config []config.Config - expectedHosts map[string][]string - expectedDestination map[string]string - }{ - { - "more exact first", - []*model.Service{ - buildHTTPService("test.local", visibility.Public, "", "default", 80), - buildHTTPService("test", visibility.Public, "", "default", 80), - }, - nil, - map[string][]string{ - "allow_any": {"*"}, - // BUG: test should be below - "test.local:80": {"test.local", "test.local:80"}, - "test:80": {"test", "test:80"}, - }, - map[string]string{ - "allow_any": "PassthroughCluster", - "test.local:80": "outbound|80||test.local", - "test:80": "outbound|80||test", - }, - }, - { - "more exact first with full cluster domain", - []*model.Service{ - buildHTTPService("test.default.svc.cluster.local", visibility.Public, "", "default", 80), - buildHTTPService("test", visibility.Public, "", "default", 80), - }, - nil, - map[string][]string{ - "allow_any": {"*"}, - "test.default.svc.cluster.local:80": { - "test.default.svc.cluster.local", "test.default.svc.cluster.local:80", - "test.default.svc", "test.default.svc:80", - "test.default", "test.default:80", - }, - "test:80": {"test", "test:80"}, - }, - map[string]string{ - "allow_any": "PassthroughCluster", - "test.default.svc.cluster.local:80": "outbound|80||test.default.svc.cluster.local", - "test:80": "outbound|80||test", - }, - }, - { - "more exact second", - []*model.Service{ - buildHTTPService("test", visibility.Public, "", "default", 80), - buildHTTPService("test.local", visibility.Public, "", "default", 80), - }, - nil, - map[string][]string{ - "allow_any": {"*"}, - "test.local:80": {"test.local", "test.local:80"}, - "test:80": {"test", "test:80"}, - }, - map[string]string{ - "allow_any": "PassthroughCluster", - "test.local:80": "outbound|80||test.local", - "test:80": "outbound|80||test", - }, - }, - { - "virtual service", - []*model.Service{ - buildHTTPService("test-duplicate-domains.default.svc.cluster.local", visibility.Public, "", "default", 80), - }, - []config.Config{{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: virtualServiceSpec, - }}, - map[string][]string{ - "allow_any": {"*"}, - "test-duplicate-domains.default.svc.cluster.local:80": { - "test-duplicate-domains.default.svc.cluster.local", "test-duplicate-domains.default.svc.cluster.local:80", - "test-duplicate-domains", "test-duplicate-domains:80", - "test-duplicate-domains.default.svc", "test-duplicate-domains.default.svc:80", - }, - "test-duplicate-domains.default:80": {"test-duplicate-domains.default", "test-duplicate-domains.default:80"}, - }, - map[string]string{ - "allow_any": "PassthroughCluster", - // Virtual service destination takes precedence - "test-duplicate-domains.default.svc.cluster.local:80": "outbound|80||test-duplicate-domains.default", - "test-duplicate-domains.default:80": "outbound|80||test-duplicate-domains.default", - }, - }, - { - "virtual service conflict", - []*model.Service{ - buildHTTPService("test.default.svc.cluster.local", visibility.Public, "", "default", 80), - }, - []config.Config{{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: virtualServiceSpecDefault, - }}, - map[string][]string{ - "allow_any": {"*"}, - "test.default.svc.cluster.local:80": { - "test.default.svc.cluster.local", "test.default.svc.cluster.local:80", - "test", "test:80", - "test.default.svc", "test.default.svc:80", - }, - "test.default:80": {"test.default", "test.default:80"}, - }, - map[string]string{ - "allow_any": "PassthroughCluster", - // From the service, go to the service - "test.default.svc.cluster.local:80": "outbound|80||test.default.svc.cluster.local", - "test.default:80": "outbound|80||test.default", - }, - }, - { - "multiple ports", - []*model.Service{ - buildHTTPService("test.local", visibility.Public, "", "default", 70, 80, 90), - }, - nil, - map[string][]string{ - "allow_any": {"*"}, - "test.local:80": {"test.local", "test.local:80"}, - }, - map[string]string{ - "allow_any": "PassthroughCluster", - "test.local:80": "outbound|80||test.local", - }, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - // ensure services are ordered - t0 := time.Now() - for _, svc := range tt.services { - svc.CreationTime = t0 - t0 = t0.Add(time.Minute) - } - cg := NewConfigGenTest(t, TestOptions{ - Services: tt.services, - Configs: tt.config, - }) - - vHostCache := make(map[int][]*route.VirtualHost) - //routeName := "80" - resource, _ := cg.ConfigGen.buildSidecarOutboundHTTPRouteConfig( - cg.SetupProxy(nil), &model.PushRequest{Push: cg.PushContext()}, "80", vHostCache, nil, nil) - routeCfg := route.RouteConfiguration{} - resource.Resource.UnmarshalTo(&routeCfg) - xdstest.ValidateRouteConfiguration(t, &routeCfg) - - got := map[string][]string{} - clusters := map[string]string{} - for _, vh := range routeCfg.VirtualHosts { - got[vh.Name] = vh.Domains - clusters[vh.Name] = vh.GetRoutes()[0].GetRoute().GetCluster() - } - - if !reflect.DeepEqual(tt.expectedHosts, got) { - t.Fatalf("unexpected virtual hosts\n%v, wanted\n%v", got, tt.expectedHosts) - } - - if !reflect.DeepEqual(tt.expectedDestination, clusters) { - t.Fatalf("unexpected destinations\n%v, wanted\n%v", clusters, tt.expectedDestination) - } - }) - } -} - -func TestSidecarOutboundHTTPRouteConfig(t *testing.T) { - services := []*model.Service{ - buildHTTPService("bookinfo.com", visibility.Public, wildcardIP, "default", 9999, 70), - buildHTTPService("private.com", visibility.Private, wildcardIP, "default", 9999, 80), - buildHTTPService("test.com", visibility.Public, "8.8.8.8", "not-default", 8080), - buildHTTPService("test-private.com", visibility.Private, "9.9.9.9", "not-default", 80, 70), - buildHTTPService("test-private-2.com", visibility.Private, "9.9.9.10", "not-default", 60), - buildHTTPService("test-headless.com", visibility.Public, wildcardIP, "not-default", 8888), - buildHTTPService("service-A.default.svc.cluster.local", visibility.Public, "", "default", 7777), - } - - sidecarConfig := &config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "not-default", - GroupVersionKind: gvk.Sidecar, - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - // A port that is not in any of the services - Number: 9000, - Protocol: "HTTP", - Name: "something", - }, - Bind: "1.1.1.1", - Hosts: []string{"*/bookinfo.com"}, - }, - { - Port: &networking.Port{ - // Unix domain socket listener - Number: 0, - Protocol: "HTTP", - Name: "something", - }, - Bind: "unix://foo/bar/baz", - Hosts: []string{"*/bookinfo.com"}, - }, - { - Port: &networking.Port{ - // Unix domain socket listener - Number: 0, - Protocol: "HTTP", - Name: "something", - }, - Bind: "unix://foo/bar/headless", - Hosts: []string{"*/test-headless.com"}, - }, - { - Port: &networking.Port{ - // A port that is in one of the services - Number: 8080, - Protocol: "HTTP", - Name: "foo", - }, - Hosts: []string{"default/bookinfo.com", "not-default/test.com"}, - }, - { - // Wildcard egress importing from all namespaces - Hosts: []string{"*/*"}, - }, - }, - }, - } - sidecarConfigWithWildcard := &config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "not-default", - GroupVersionKind: gvk.Sidecar, - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Number: 7443, - Protocol: "HTTP", - Name: "something", - }, - Hosts: []string{"*/*"}, - }, - }, - }, - } - sidecarConfigWitHTTPProxy := &config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "not-default", - GroupVersionKind: gvk.Sidecar, - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Number: 7443, - Protocol: "HTTP_PROXY", - Name: "something", - }, - Hosts: []string{"*/*"}, - }, - }, - }, - } - sidecarConfigWithRegistryOnly := &config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "not-default", - GroupVersionKind: gvk.Sidecar, - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - // A port that is not in any of the services - Number: 9000, - Protocol: "HTTP", - Name: "something", - }, - Bind: "1.1.1.1", - Hosts: []string{"*/bookinfo.com"}, - }, - { - Port: &networking.Port{ - // Unix domain socket listener - Number: 0, - Protocol: "HTTP", - Name: "something", - }, - Bind: "unix://foo/bar/baz", - Hosts: []string{"*/bookinfo.com"}, - }, - { - Port: &networking.Port{ - Number: 0, - Protocol: "HTTP", - Name: "something", - }, - Bind: "unix://foo/bar/headless", - Hosts: []string{"*/test-headless.com"}, - }, - { - Port: &networking.Port{ - Number: 18888, - Protocol: "HTTP", - Name: "foo", - }, - Hosts: []string{"*/test-headless.com"}, - }, - { - // Wildcard egress importing from all namespaces - Hosts: []string{"*/*"}, - }, - }, - OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_REGISTRY_ONLY, - }, - }, - } - sidecarConfigWithAllowAny := &config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "not-default", - GroupVersionKind: gvk.Sidecar, - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - // A port that is not in any of the services - Number: 9000, - Protocol: "HTTP", - Name: "something", - }, - Bind: "1.1.1.1", - Hosts: []string{"*/bookinfo.com"}, - }, - { - Port: &networking.Port{ - // Unix domain socket listener - Number: 0, - Protocol: "HTTP", - Name: "something", - }, - Bind: "unix://foo/bar/baz", - Hosts: []string{"*/bookinfo.com"}, - }, - { - Port: &networking.Port{ - // A port that is in one of the services - Number: 8080, - Protocol: "HTTP", - Name: "foo", - }, - Hosts: []string{"default/bookinfo.com", "not-default/test.com"}, - }, - { - // Wildcard egress importing from all namespaces - Hosts: []string{"*/*"}, - }, - }, - OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, - }, - }, - } - virtualServiceSpec1 := &networking.VirtualService{ - Hosts: []string{"test-private-2.com"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - // Subset: "some-subset", - Host: "example.org", - Port: &networking.PortSelector{ - Number: 61, - }, - }, - Weight: 100, - }, - }, - }, - }, - } - virtualServiceSpec2 := &networking.VirtualService{ - Hosts: []string{"test-private-2.com"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "test.org", - Port: &networking.PortSelector{ - Number: 62, - }, - }, - Weight: 100, - }, - }, - }, - }, - } - virtualServiceSpec3 := &networking.VirtualService{ - Hosts: []string{"test-private-3.com"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "test.org", - Port: &networking.PortSelector{ - Number: 63, - }, - }, - Weight: 100, - }, - }, - }, - }, - } - virtualServiceSpec4 := &networking.VirtualService{ - Hosts: []string{"test-headless.com", "example.com"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "test.org", - Port: &networking.PortSelector{ - Number: 64, - }, - }, - Weight: 100, - }, - }, - }, - }, - } - virtualServiceSpec5 := &networking.VirtualService{ - Hosts: []string{"test-svc.testns.svc.cluster.local"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "test-svc.testn.svc.cluster.local", - }, - Weight: 100, - }, - }, - }, - }, - } - virtualServiceSpec6 := &networking.VirtualService{ - Hosts: []string{"match-no-service"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "non-exist-service", - }, - Weight: 100, - }, - }, - }, - }, - } - virtualServiceSpec7 := &networking.VirtualService{ - Hosts: []string{"service-A.default.svc.cluster.local", "service-A.v2", "service-A.v3"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Headers: map[string]*networking.StringMatch{":authority": {MatchType: &networking.StringMatch_Exact{Exact: "service-A.v2"}}}, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "service-A", - Subset: "v2", - }, - }, - }, - }, - { - Match: []*networking.HTTPMatchRequest{ - { - Headers: map[string]*networking.StringMatch{":authority": {MatchType: &networking.StringMatch_Exact{Exact: "service-A.v3"}}}, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "service-A", - Subset: "v3", - }, - }, - }, - }, - }, - } - virtualService1 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "acme2-v1", - Namespace: "not-default", - }, - Spec: virtualServiceSpec1, - } - virtualService2 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "acme-v2", - Namespace: "not-default", - }, - Spec: virtualServiceSpec2, - } - virtualService3 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "acme-v3", - Namespace: "not-default", - }, - Spec: virtualServiceSpec3, - } - virtualService4 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "acme-v4", - Namespace: "not-default", - }, - Spec: virtualServiceSpec4, - } - virtualService5 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "acme-v3", - Namespace: "not-default", - }, - Spec: virtualServiceSpec5, - } - virtualService6 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "acme-v3", - Namespace: "not-default", - }, - Spec: virtualServiceSpec6, - } - virtualService7 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "vs-1", - Namespace: "default", - }, - Spec: virtualServiceSpec7, - } - - // With the config above, RDS should return a valid route for the following route names - // port 9000 - [bookinfo.com:9999, *.bookinfo.com:9990], [bookinfo.com:70, *.bookinfo.com:70] but no bookinfo.com - // unix://foo/bar/baz - [bookinfo.com:9999, *.bookinfo.com:9999], [bookinfo.com:70, *.bookinfo.com:70] but no bookinfo.com - // port 8080 - [test.com, test.com:8080, 8.8.8.8, 8.8.8.8:8080], but no bookinfo.com or test.com - // port 9999 - [bookinfo.com, bookinfo.com:9999, *.bookinfo.com, *.bookinfo.com:9999] - // port 80 - [test-private.com, test-private.com:80, 9.9.9.9:80, 9.9.9.9] - // port 70 - [test-private.com, test-private.com:70, 9.9.9.9, 9.9.9.9:70], [bookinfo.com, bookinfo.com:70] - - // Without sidecar config [same as wildcard egress listener], expect routes - // 9999 - [bookinfo.com, bookinfo.com:9999, *.bookinfo.com, *.bookinfo.com:9999], - // 8080 - [test.com, test.com:8080, 8.8.8.8, 8.8.8.8:8080] - // 80 - [test-private.com, test-private.com:80, 9.9.9.9:80, 9.9.9.9] - // 70 - [bookinfo.com, bookinfo.com:70, *.bookinfo.com:70],[test-private.com, test-private.com:70, 9.9.9.9:70, 9.9.9.9] - cases := []struct { - name string - routeName string - sidecarConfig *config.Config - virtualServiceConfigs []*config.Config - // virtualHost Name and domains - expectedHosts map[string]map[string]bool - expectedRoutes int - registryOnly bool - }{ - { - name: "sidecar config port that is not in any service", - routeName: "9000", - sidecarConfig: sidecarConfig, - virtualServiceConfigs: nil, - expectedHosts: map[string]map[string]bool{ - "block_all": { - "*": true, - }, - }, - registryOnly: true, - }, - { - name: "sidecar config with unix domain socket listener", - routeName: "unix://foo/bar/baz", - sidecarConfig: sidecarConfig, - virtualServiceConfigs: nil, - expectedHosts: map[string]map[string]bool{ - "bookinfo.com:9999": {"bookinfo.com:9999": true, "*.bookinfo.com:9999": true}, - "bookinfo.com:70": {"bookinfo.com:70": true, "*.bookinfo.com:70": true}, - "allow_any": { - "*": true, - }, - }, - }, - { - name: "sidecar config port that is in one of the services", - routeName: "8080", - sidecarConfig: sidecarConfig, - virtualServiceConfigs: nil, - expectedHosts: map[string]map[string]bool{ - "test.com:8080": {"test.com": true, "test.com:8080": true, "8.8.8.8": true, "8.8.8.8:8080": true}, - "block_all": { - "*": true, - }, - }, - registryOnly: true, - }, - { - name: "sidecar config with fallthrough and registry only and allow any mesh config", - routeName: "80", - sidecarConfig: sidecarConfigWithRegistryOnly, - virtualServiceConfigs: nil, - expectedHosts: map[string]map[string]bool{ - "test-private.com:80": { - "test-private.com": true, "test-private.com:80": true, "9.9.9.9": true, "9.9.9.9:80": true, - }, - "block_all": { - "*": true, - }, - }, - registryOnly: false, - }, - { - name: "sidecar config with fallthrough and allow any and registry only mesh config", - routeName: "80", - sidecarConfig: sidecarConfigWithAllowAny, - virtualServiceConfigs: nil, - expectedHosts: map[string]map[string]bool{ - "test-private.com:80": { - "test-private.com": true, "test-private.com:80": true, "9.9.9.9": true, "9.9.9.9:80": true, - }, - "allow_any": { - "*": true, - }, - }, - registryOnly: false, - }, - - { - name: "sidecar config with allow any and virtual service includes non existing service", - routeName: "80", - sidecarConfig: sidecarConfigWithAllowAny, - virtualServiceConfigs: []*config.Config{&virtualService6}, - expectedHosts: map[string]map[string]bool{ - // does not include `match-no-service` - "test-private.com:80": { - "test-private.com": true, "test-private.com:80": true, "9.9.9.9": true, "9.9.9.9:80": true, - }, - "match-no-service.not-default:80": {"match-no-service.not-default": true, "match-no-service.not-default:80": true}, - "allow_any": { - "*": true, - }, - }, - registryOnly: false, - }, - - { - name: "sidecar config with allow any and virtual service includes non existing service", - routeName: "80", - sidecarConfig: sidecarConfigWithAllowAny, - virtualServiceConfigs: []*config.Config{&virtualService6}, - expectedHosts: map[string]map[string]bool{ - // does not include `match-no-service` - "test-private.com:80": { - "test-private.com": true, "test-private.com:80": true, "9.9.9.9": true, "9.9.9.9:80": true, - }, - "match-no-service.not-default:80": {"match-no-service.not-default": true, "match-no-service.not-default:80": true}, - "allow_any": { - "*": true, - }, - }, - registryOnly: false, - }, - - { - name: "wildcard egress importing from all namespaces: 9999", - routeName: "9999", - sidecarConfig: sidecarConfig, - virtualServiceConfigs: nil, - expectedHosts: map[string]map[string]bool{ - "bookinfo.com:9999": { - "bookinfo.com:9999": true, "bookinfo.com": true, - "*.bookinfo.com:9999": true, "*.bookinfo.com": true, - }, - "block_all": { - "*": true, - }, - }, - registryOnly: true, - }, - { - name: "wildcard egress importing from all namespaces: 80", - routeName: "80", - sidecarConfig: sidecarConfig, - virtualServiceConfigs: nil, - expectedHosts: map[string]map[string]bool{ - "test-private.com:80": { - "test-private.com": true, "test-private.com:80": true, "9.9.9.9": true, "9.9.9.9:80": true, - }, - "block_all": { - "*": true, - }, - }, - registryOnly: true, - }, - { - name: "wildcard egress importing from all namespaces: 70", - routeName: "70", - sidecarConfig: sidecarConfig, - virtualServiceConfigs: nil, - expectedHosts: map[string]map[string]bool{ - "test-private.com:70": { - "test-private.com": true, "test-private.com:70": true, "9.9.9.9": true, "9.9.9.9:70": true, - }, - "bookinfo.com:70": { - "bookinfo.com": true, "bookinfo.com:70": true, - "*.bookinfo.com": true, "*.bookinfo.com:70": true, - }, - "block_all": { - "*": true, - }, - }, - registryOnly: true, - }, - { - name: "no sidecar config - import public service from other namespaces: 9999", - routeName: "9999", - sidecarConfig: nil, - virtualServiceConfigs: nil, - expectedHosts: map[string]map[string]bool{ - "bookinfo.com:9999": { - "bookinfo.com:9999": true, "bookinfo.com": true, - "*.bookinfo.com:9999": true, "*.bookinfo.com": true, - }, - "block_all": { - "*": true, - }, - }, - registryOnly: true, - }, - { - name: "no sidecar config - import public service from other namespaces: 8080", - routeName: "8080", - sidecarConfig: nil, - virtualServiceConfigs: nil, - expectedHosts: map[string]map[string]bool{ - "test.com:8080": { - "test.com:8080": true, "test.com": true, "8.8.8.8": true, "8.8.8.8:8080": true, - }, - "block_all": { - "*": true, - }, - }, - registryOnly: true, - }, - { - name: "no sidecar config - import public services from other namespaces: 80", - routeName: "80", - sidecarConfig: nil, - virtualServiceConfigs: nil, - expectedHosts: map[string]map[string]bool{ - "test-private.com:80": { - "test-private.com": true, "test-private.com:80": true, "9.9.9.9": true, "9.9.9.9:80": true, - }, - "block_all": { - "*": true, - }, - }, - registryOnly: true, - }, - { - name: "no sidecar config - import public services from other namespaces: 70", - routeName: "70", - sidecarConfig: nil, - virtualServiceConfigs: nil, - expectedHosts: map[string]map[string]bool{ - "test-private.com:70": { - "test-private.com": true, "test-private.com:70": true, "9.9.9.9": true, "9.9.9.9:70": true, - }, - "bookinfo.com:70": { - "bookinfo.com": true, "bookinfo.com:70": true, - "*.bookinfo.com": true, "*.bookinfo.com:70": true, - }, - "block_all": { - "*": true, - }, - }, - registryOnly: true, - }, - { - name: "no sidecar config - import public services from other namespaces: 70 with sniffing", - routeName: "test-private.com:70", - sidecarConfig: nil, - virtualServiceConfigs: nil, - expectedHosts: map[string]map[string]bool{ - "test-private.com:70": { - "*": true, - }, - }, - registryOnly: true, - }, - { - name: "no sidecar config - import public services from other namespaces: 80 with fallthrough", - routeName: "80", - sidecarConfig: nil, - virtualServiceConfigs: nil, - expectedHosts: map[string]map[string]bool{ - "test-private.com:80": { - "test-private.com": true, "test-private.com:80": true, "9.9.9.9": true, "9.9.9.9:80": true, - }, - "allow_any": { - "*": true, - }, - }, - registryOnly: false, - }, - { - name: "no sidecar config - import public services from other namespaces: 80 with fallthrough and registry only", - routeName: "80", - sidecarConfig: nil, - virtualServiceConfigs: nil, - expectedHosts: map[string]map[string]bool{ - "test-private.com:80": { - "test-private.com": true, "test-private.com:80": true, "9.9.9.9": true, "9.9.9.9:80": true, - }, - "block_all": { - "*": true, - }, - }, - registryOnly: true, - }, - { - name: "no sidecar config with virtual services with duplicate entries", - routeName: "60", - sidecarConfig: nil, - virtualServiceConfigs: []*config.Config{&virtualService1, &virtualService2}, - expectedHosts: map[string]map[string]bool{ - "test-private-2.com:60": { - "test-private-2.com": true, "test-private-2.com:60": true, "9.9.9.10": true, "9.9.9.10:60": true, - }, - "block_all": { - "*": true, - }, - }, - registryOnly: true, - }, - { - name: "no sidecar config with virtual services with no service in registry", - routeName: "80", // no service for the host in registry; use port 80 by default - sidecarConfig: nil, - virtualServiceConfigs: []*config.Config{&virtualService3}, - expectedHosts: map[string]map[string]bool{ - "test-private.com:80": { - "test-private.com": true, "test-private.com:80": true, "9.9.9.9": true, "9.9.9.9:80": true, - }, - "test-private-3.com:80": { - "test-private-3.com": true, "test-private-3.com:80": true, - }, - "block_all": { - "*": true, - }, - }, - registryOnly: true, - }, - { - name: "no sidecar config - import headless service from other namespaces: 8888", - routeName: "8888", - sidecarConfig: nil, - virtualServiceConfigs: nil, - expectedHosts: map[string]map[string]bool{ - "test-headless.com:8888": { - "test-headless.com": true, "test-headless.com:8888": true, "*.test-headless.com": true, "*.test-headless.com:8888": true, - }, - "block_all": { - "*": true, - }, - }, - registryOnly: true, - }, - { - name: "no sidecar config with virtual services - import headless service from other namespaces: 8888", - routeName: "8888", - sidecarConfig: nil, - virtualServiceConfigs: []*config.Config{&virtualService4}, - expectedHosts: map[string]map[string]bool{ - "test-headless.com:8888": { - "test-headless.com": true, "test-headless.com:8888": true, "*.test-headless.com": true, "*.test-headless.com:8888": true, - }, - "example.com:8888": { - "example.com": true, "example.com:8888": true, - }, - "block_all": { - "*": true, - }, - }, - registryOnly: true, - }, - { - name: "sidecar config with unix domain socket listener - import headless service", - routeName: "unix://foo/bar/headless", - sidecarConfig: sidecarConfig, - virtualServiceConfigs: nil, - expectedHosts: map[string]map[string]bool{ - "test-headless.com:8888": {"test-headless.com:8888": true, "*.test-headless.com:8888": true}, - "block_all": { - "*": true, - }, - }, - registryOnly: true, - }, - { - name: "sidecar config port - import headless service", - routeName: "18888", - sidecarConfig: sidecarConfigWithRegistryOnly, - virtualServiceConfigs: nil, - expectedHosts: map[string]map[string]bool{ - "block_all": { - "*": true, - }, - }, - registryOnly: true, - }, - { - name: "wild card sidecar config, with non matching virtual service", - routeName: "7443", - sidecarConfig: sidecarConfigWithWildcard, - virtualServiceConfigs: []*config.Config{&virtualService5}, - expectedHosts: map[string]map[string]bool{ - "block_all": { - "*": true, - }, - }, - registryOnly: true, - }, - { - name: "http proxy sidecar config, with non matching virtual service", - routeName: "7443", - sidecarConfig: sidecarConfigWitHTTPProxy, - virtualServiceConfigs: []*config.Config{&virtualService5}, - expectedHosts: map[string]map[string]bool{ - "bookinfo.com:9999": {"bookinfo.com:9999": true, "*.bookinfo.com:9999": true}, - "bookinfo.com:70": {"bookinfo.com:70": true, "*.bookinfo.com:70": true}, - "test-headless.com:8888": {"test-headless.com:8888": true, "*.test-headless.com:8888": true}, - "test-private-2.com:60": { - "test-private-2.com:60": true, "9.9.9.10:60": true, - }, - "test-private.com:70": { - "test-private.com:70": true, "9.9.9.9:70": true, - }, - "test-private.com:80": { - "test-private.com": true, "test-private.com:80": true, "9.9.9.9": true, "9.9.9.9:80": true, - }, - "test.com:8080": { - "test.com:8080": true, "8.8.8.8:8080": true, - }, - "service-A.default.svc.cluster.local:7777": { - "service-A.default.svc.cluster.local:7777": true, - }, - "block_all": { - "*": true, - }, - }, - registryOnly: true, - }, - { - name: "virtual service hosts with subsets and with existing service", - routeName: "7777", - sidecarConfig: sidecarConfigWithAllowAny, - virtualServiceConfigs: []*config.Config{&virtualService7}, - expectedHosts: map[string]map[string]bool{ - "allow_any": { - "*": true, - }, - "service-A.default.svc.cluster.local:7777": { - "service-A.default.svc.cluster.local": true, - "service-A.default.svc.cluster.local:7777": true, - }, - "service-A.v2:7777": { - "service-A.v2": true, - "service-A.v2:7777": true, - }, - "service-A.v3:7777": { - "service-A.v3": true, - "service-A.v3:7777": true, - }, - }, - expectedRoutes: 7, - registryOnly: false, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - testSidecarRDSVHosts(t, services, c.sidecarConfig, c.virtualServiceConfigs, - c.routeName, c.expectedHosts, c.expectedRoutes, c.registryOnly) - }) - } -} - -func TestSelectVirtualService(t *testing.T) { - services := []*model.Service{ - buildHTTPService("bookinfo.com", visibility.Public, wildcardIP, "default", 9999, 70), - buildHTTPService("private.com", visibility.Private, wildcardIP, "default", 9999, 80), - buildHTTPService("test.com", visibility.Public, "8.8.8.8", "not-default", 8080), - buildHTTPService("test-private.com", visibility.Private, "9.9.9.9", "not-default", 80, 70), - buildHTTPService("test-private-2.com", visibility.Private, "9.9.9.10", "not-default", 60), - buildHTTPService("test-headless.com", visibility.Public, wildcardIP, "not-default", 8888), - } - - servicesByName := make(map[host.Name]*model.Service, len(services)) - for _, svc := range services { - servicesByName[svc.Hostname] = svc - } - - virtualServiceSpec1 := &networking.VirtualService{ - Hosts: []string{"test-private-2.com"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - // Subset: "some-subset", - Host: "example.org", - Port: &networking.PortSelector{ - Number: 61, - }, - }, - Weight: 100, - }, - }, - }, - }, - } - virtualServiceSpec2 := &networking.VirtualService{ - Hosts: []string{"test-private-2.com"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "test.org", - Port: &networking.PortSelector{ - Number: 62, - }, - }, - Weight: 100, - }, - }, - }, - }, - } - virtualServiceSpec3 := &networking.VirtualService{ - Hosts: []string{"test-private-3.com"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "test.org", - Port: &networking.PortSelector{ - Number: 63, - }, - }, - Weight: 100, - }, - }, - }, - }, - } - virtualServiceSpec4 := &networking.VirtualService{ - Hosts: []string{"test-headless.com", "example.com"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "test.org", - Port: &networking.PortSelector{ - Number: 64, - }, - }, - Weight: 100, - }, - }, - }, - }, - } - virtualServiceSpec5 := &networking.VirtualService{ - Hosts: []string{"test-svc.testns.svc.cluster.local"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "test-svc.testn.svc.cluster.local", - }, - Weight: 100, - }, - }, - }, - }, - } - virtualServiceSpec6 := &networking.VirtualService{ - Hosts: []string{"match-no-service"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "non-exist-service", - }, - Weight: 100, - }, - }, - }, - }, - } - virtualService1 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "acme2-v1", - Namespace: "not-default", - }, - Spec: virtualServiceSpec1, - } - virtualService2 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "acme-v2", - Namespace: "not-default", - }, - Spec: virtualServiceSpec2, - } - virtualService3 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "acme-v3", - Namespace: "not-default", - }, - Spec: virtualServiceSpec3, - } - virtualService4 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "acme-v4", - Namespace: "not-default", - }, - Spec: virtualServiceSpec4, - } - virtualService5 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "acme-v3", - Namespace: "not-default", - }, - Spec: virtualServiceSpec5, - } - virtualService6 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "acme-v3", - Namespace: "not-default", - }, - Spec: virtualServiceSpec6, - } - configs := selectVirtualServices( - []config.Config{virtualService1, virtualService2, virtualService3, virtualService4, virtualService5, virtualService6}, - servicesByName) - expectedVS := []string{virtualService1.Name, virtualService2.Name, virtualService4.Name} - if len(expectedVS) != len(configs) { - t.Fatalf("Unexpected virtualService, got %d, epxected %d", len(configs), len(expectedVS)) - } - for i, config := range configs { - if config.Name != expectedVS[i] { - t.Fatalf("Unexpected virtualService, got %s, epxected %s", config.Name, expectedVS[i]) - } - } -} - -func testSidecarRDSVHosts(t *testing.T, services []*model.Service, - sidecarConfig *config.Config, virtualServices []*config.Config, routeName string, - expectedHosts map[string]map[string]bool, expectedRoutes int, registryOnly bool, -) { - m := mesh.DefaultMeshConfig() - if registryOnly { - m.OutboundTrafficPolicy = &meshapi.MeshConfig_OutboundTrafficPolicy{Mode: meshapi.MeshConfig_OutboundTrafficPolicy_REGISTRY_ONLY} - } - cg := NewConfigGenTest(t, TestOptions{ - MeshConfig: m, - Services: services, - ConfigPointers: append(virtualServices, sidecarConfig), - }) - - proxy := &model.Proxy{ConfigNamespace: "not-default", DNSDomain: "default.example.org"} - vHostCache := make(map[int][]*route.VirtualHost) - resource, _ := cg.ConfigGen.buildSidecarOutboundHTTPRouteConfig( - cg.SetupProxy(proxy), &model.PushRequest{Push: cg.PushContext()}, routeName, vHostCache, nil, nil) - routeCfg := route.RouteConfiguration{} - resource.Resource.UnmarshalTo(&routeCfg) - xdstest.ValidateRouteConfiguration(t, &routeCfg) - - if expectedRoutes == 0 { - expectedRoutes = len(expectedHosts) - } - numberOfRoutes := 0 - for _, vhost := range routeCfg.VirtualHosts { - numberOfRoutes += len(vhost.Routes) - if _, found := expectedHosts[vhost.Name]; !found { - t.Fatalf("unexpected vhost block %s for route %s", - vhost.Name, routeName) - } - - for _, domain := range vhost.Domains { - if !expectedHosts[vhost.Name][domain] { - t.Fatalf("unexpected vhost domain %s in vhost %s, for route %s", domain, vhost.Name, routeName) - } - } - for want := range expectedHosts[vhost.Name] { - found := false - for _, got := range vhost.Domains { - if got == want { - found = true - } - } - if !found { - t.Fatalf("expected vhost domain %s in vhost %s, for route %s not found. got domains %v", want, vhost.Name, routeName, vhost.Domains) - } - } - - if !vhost.GetIncludeRequestAttemptCount() { - t.Fatal("Expected that include request attempt count is set to true, but set to false") - } - } - if (expectedRoutes >= 0) && (numberOfRoutes != expectedRoutes) { - t.Errorf("Wrong number of routes. expected: %v, Got: %v", expectedRoutes, numberOfRoutes) - } -} - -func buildHTTPService(hostname string, v visibility.Instance, ip, namespace string, ports ...int) *model.Service { - service := &model.Service{ - CreationTime: tnow, - Hostname: host.Name(hostname), - DefaultAddress: ip, - Resolution: model.DNSLB, - Attributes: model.ServiceAttributes{ - ServiceRegistry: provider.Kubernetes, - Namespace: namespace, - ExportTo: map[visibility.Instance]bool{v: true}, - }, - } - if ip == wildcardIP { - service.Resolution = model.Passthrough - } - - Ports := make([]*model.Port, 0) - - for _, p := range ports { - Ports = append(Ports, &model.Port{ - Name: fmt.Sprintf("http-%d", p), - Port: p, - Protocol: protocol.HTTP, - }) - } - - service.Ports = Ports - return service -} diff --git a/pilot/pkg/networking/core/v1alpha3/leak_test.go b/pilot/pkg/networking/core/v1alpha3/leak_test.go deleted file mode 100644 index cc5b1a8f6..000000000 --- a/pilot/pkg/networking/core/v1alpha3/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/networking/core/v1alpha3/listener.go b/pilot/pkg/networking/core/v1alpha3/listener.go deleted file mode 100644 index e1ad3368a..000000000 --- a/pilot/pkg/networking/core/v1alpha3/listener.go +++ /dev/null @@ -1,1726 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "fmt" - "net" - "sort" - "strconv" - "strings" - "time" -) - -import ( - accesslog "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v3" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - envoyquicv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/quic/v3" - auth "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "google.golang.org/protobuf/types/known/durationpb" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - extensions "istio.io/api/extensions/v1alpha1" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" - "istio.io/pkg/log" - "istio.io/pkg/monitoring" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - istionetworking "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3/extension" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - authnmodel "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - xdsfilters "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/filters" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/requestidextension" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/security" - "github.com/apache/dubbo-go-pixiu/pkg/proto" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -const ( - NoConflict = iota - // HTTPOverTCP represents incoming HTTP existing TCP - HTTPOverTCP - // TCPOverHTTP represents incoming TCP existing HTTP - TCPOverHTTP - // TCPOverTCP represents incoming TCP existing TCP - TCPOverTCP - // TCPOverAuto represents incoming TCP existing AUTO - TCPOverAuto - // AutoOverHTTP represents incoming AUTO existing HTTP - AutoOverHTTP - // AutoOverTCP represents incoming AUTO existing TCP - AutoOverTCP -) - -const ( - // ProxyInboundListenPort is the port on which all inbound traffic to the pod/vm will be captured to - // TODO: allow configuration through mesh config - ProxyInboundListenPort = 15006 -) - -// MutableListener represents a listener that is being built. -type MutableListener struct { - istionetworking.MutableObjects -} - -// A set of pre-allocated variables related to protocol sniffing logic for -// propagating the ALPN to upstreams -var ( - // These are sniffed by the HTTP Inspector in the outbound listener - // We need to forward these ALPNs to upstream so that the upstream can - // properly use an HTTP or TCP listener - plaintextHTTPALPNs = func() []string { - if features.HTTP10 { - // If HTTP 1.0 is enabled, we will match it - return []string{"http/1.0", "http/1.1", "h2c"} - } - // Otherwise, matching would just lead to immediate rejection. By not matching, we can let it pass - // through as raw TCP at least. - // NOTE: mtlsHTTPALPNs can always include 1.0, for simplicity, as it will only be sent if a client - return []string{"http/1.1", "h2c"} - }() - mtlsHTTPALPNs = []string{"istio-http/1.0", "istio-http/1.1", "istio-h2"} - - allIstioMtlsALPNs = []string{"istio", "istio-peer-exchange", "istio-http/1.0", "istio-http/1.1", "istio-h2"} - - mtlsTCPWithMxcALPNs = []string{"istio-peer-exchange", "istio"} -) - -// BuildListeners produces a list of listeners and referenced clusters for all proxies -func (configgen *ConfigGeneratorImpl) BuildListeners(node *model.Proxy, - push *model.PushContext) []*listener.Listener { - builder := NewListenerBuilder(node, push) - - switch node.Type { - case model.SidecarProxy: - builder = configgen.buildSidecarListeners(builder) - case model.Router: - builder = configgen.buildGatewayListeners(builder) - } - - builder.patchListeners() - return builder.getListeners() -} - -func BuildListenerTLSContext(serverTLSSettings *networking.ServerTLSSettings, - proxy *model.Proxy, transportProtocol istionetworking.TransportProtocol) *auth.DownstreamTlsContext { - alpnByTransport := util.ALPNHttp - if transportProtocol == istionetworking.TransportProtocolQUIC { - alpnByTransport = util.ALPNHttp3OverQUIC - } - - ctx := &auth.DownstreamTlsContext{ - CommonTlsContext: &auth.CommonTlsContext{ - AlpnProtocols: alpnByTransport, - }, - } - - ctx.RequireClientCertificate = proto.BoolFalse - if serverTLSSettings.Mode == networking.ServerTLSSettings_MUTUAL || - serverTLSSettings.Mode == networking.ServerTLSSettings_ISTIO_MUTUAL { - ctx.RequireClientCertificate = proto.BoolTrue - } - - if features.EnableLegacyIstioMutualCredentialName { - // Legacy code path. Can be removed after a couple releases. - switch { - // If credential name is specified at gateway config, create SDS config for gateway to fetch key/cert from Istiod. - case serverTLSSettings.CredentialName != "": - authnmodel.ApplyCredentialSDSToServerCommonTLSContext(ctx.CommonTlsContext, serverTLSSettings) - case serverTLSSettings.Mode == networking.ServerTLSSettings_ISTIO_MUTUAL: - authnmodel.ApplyToCommonTLSContext(ctx.CommonTlsContext, proxy, serverTLSSettings.SubjectAltNames, []string{}, ctx.RequireClientCertificate.Value) - default: - certProxy := &model.Proxy{} - certProxy.IstioVersion = proxy.IstioVersion - // If certificate files are specified in gateway configuration, use file based SDS. - certProxy.Metadata = &model.NodeMetadata{ - TLSServerCertChain: serverTLSSettings.ServerCertificate, - TLSServerKey: serverTLSSettings.PrivateKey, - TLSServerRootCert: serverTLSSettings.CaCertificates, - } - - authnmodel.ApplyToCommonTLSContext(ctx.CommonTlsContext, certProxy, serverTLSSettings.SubjectAltNames, []string{}, ctx.RequireClientCertificate.Value) - } - } else { - switch { - case serverTLSSettings.Mode == networking.ServerTLSSettings_ISTIO_MUTUAL: - authnmodel.ApplyToCommonTLSContext(ctx.CommonTlsContext, proxy, serverTLSSettings.SubjectAltNames, []string{}, ctx.RequireClientCertificate.Value) - // If credential name is specified at gateway config, create SDS config for gateway to fetch key/cert from Istiod. - case serverTLSSettings.CredentialName != "": - authnmodel.ApplyCredentialSDSToServerCommonTLSContext(ctx.CommonTlsContext, serverTLSSettings) - default: - certProxy := &model.Proxy{} - certProxy.IstioVersion = proxy.IstioVersion - // If certificate files are specified in gateway configuration, use file based SDS. - certProxy.Metadata = &model.NodeMetadata{ - TLSServerCertChain: serverTLSSettings.ServerCertificate, - TLSServerKey: serverTLSSettings.PrivateKey, - TLSServerRootCert: serverTLSSettings.CaCertificates, - } - - authnmodel.ApplyToCommonTLSContext(ctx.CommonTlsContext, certProxy, serverTLSSettings.SubjectAltNames, []string{}, ctx.RequireClientCertificate.Value) - } - } - - // Set TLS parameters if they are non-default - if len(serverTLSSettings.CipherSuites) > 0 || - serverTLSSettings.MinProtocolVersion != networking.ServerTLSSettings_TLS_AUTO || - serverTLSSettings.MaxProtocolVersion != networking.ServerTLSSettings_TLS_AUTO { - ctx.CommonTlsContext.TlsParams = &auth.TlsParameters{ - TlsMinimumProtocolVersion: convertTLSProtocol(serverTLSSettings.MinProtocolVersion), - TlsMaximumProtocolVersion: convertTLSProtocol(serverTLSSettings.MaxProtocolVersion), - CipherSuites: serverTLSSettings.CipherSuites, - } - } - - return ctx -} - -// Invalid cipher suites lead Envoy to NACKing. This filters the list down to just the supported set. -func filteredSidecarCipherSuites(suites []string) []string { - ret := make([]string, 0, len(suites)) - validCiphers := sets.New() - for _, s := range suites { - if security.IsValidCipherSuite(s) { - if !validCiphers.Contains(s) { - ret = append(ret, s) - validCiphers = validCiphers.Insert(s) - } else if log.DebugEnabled() { - log.Debugf("ignoring duplicated cipherSuite: %q", s) - } - } else if log.DebugEnabled() { - log.Debugf("ignoring unsupported cipherSuite: %q", s) - } - } - return ret -} - -// buildSidecarListeners produces a list of listeners for sidecar proxies -func (configgen *ConfigGeneratorImpl) buildSidecarListeners(builder *ListenerBuilder) *ListenerBuilder { - if builder.push.Mesh.ProxyListenPort > 0 { - // Any build order change need a careful code review - builder.appendSidecarInboundListeners(). - appendSidecarOutboundListeners(). - buildHTTPProxyListener(). - buildVirtualOutboundListener() - } - return builder -} - -// if enableFlag is "1" indicates that AcceptHttp_10 is enabled. -func enableHTTP10(enableFlag string) bool { - return enableFlag == "1" -} - -type outboundListenerEntry struct { - services []*model.Service - servicePort *model.Port - bind string - listener *listener.Listener - locked bool - protocol protocol.Instance -} - -func protocolName(p protocol.Instance) string { - switch istionetworking.ModelProtocolToListenerProtocol(p, core.TrafficDirection_OUTBOUND) { - case istionetworking.ListenerProtocolHTTP: - return "HTTP" - case istionetworking.ListenerProtocolTCP: - return "TCP" - default: - return "UNKNOWN" - } -} - -type outboundListenerConflict struct { - metric monitoring.Metric - node *model.Proxy - listenerName string - currentProtocol protocol.Instance - currentServices []*model.Service - newHostname host.Name - newProtocol protocol.Instance -} - -func (c outboundListenerConflict) addMetric(metrics model.Metrics) { - currentHostnames := make([]string, len(c.currentServices)) - for i, s := range c.currentServices { - currentHostnames[i] = string(s.Hostname) - } - concatHostnames := strings.Join(currentHostnames, ",") - metrics.AddMetric(c.metric, - c.listenerName, - c.node.ID, - fmt.Sprintf("Listener=%s Accepted%s=%s Rejected%s=%s %sServices=%d", - c.listenerName, - protocolName(c.currentProtocol), - concatHostnames, - protocolName(c.newProtocol), - c.newHostname, - protocolName(c.currentProtocol), - len(c.currentServices))) -} - -// buildSidecarOutboundListeners generates http and tcp listeners for -// outbound connections from the proxy based on the sidecar scope associated with the proxy. -func (lb *ListenerBuilder) buildSidecarOutboundListeners(node *model.Proxy, - push *model.PushContext) []*listener.Listener { - noneMode := node.GetInterceptionMode() == model.InterceptionNone - - actualWildcard, actualLocalHostAddress := getActualWildcardAndLocalHost(node) - - var tcpListeners, httpListeners []*listener.Listener - // For conflict resolution - listenerMap := make(map[string]*outboundListenerEntry) - - // The sidecarConfig if provided could filter the list of - // services/virtual services that we need to process. It could also - // define one or more listeners with specific ports. Once we generate - // listeners for these user specified ports, we will auto generate - // configs for other ports if and only if the sidecarConfig has an - // egressListener on wildcard port. - // - // Validation will ensure that we have utmost one wildcard egress listener - // occurring in the end - - // Add listeners based on the config in the sidecar.EgressListeners if - // no Sidecar CRD is provided for this config namespace, - // push.SidecarScope will generate a default catch all egress listener. - for _, egressListener := range node.SidecarScope.EgressListeners { - - services := egressListener.Services() - virtualServices := egressListener.VirtualServices() - - // determine the bindToPort setting for listeners - bindToPort := false - if noneMode { - // do not care what the listener's capture mode setting is. The proxy does not use iptables - bindToPort = true - } else if egressListener.IstioListener != nil { - if egressListener.IstioListener.CaptureMode == networking.CaptureMode_NONE { - // proxy uses iptables redirect or tproxy. IF mode is not set - // for older proxies, it defaults to iptables redirect. If the - // listener's capture mode specifies NONE, then the proxy wants - // this listener alone to be on a physical port. If the - // listener's capture mode is default, then its same as - // iptables i.e. bindToPort is false. - bindToPort = true - } else if strings.HasPrefix(egressListener.IstioListener.Bind, model.UnixAddressPrefix) { - // If the bind is a Unix domain socket, set bindtoPort to true as it makes no - // sense to have ORIG_DST listener for unix domain socket listeners. - bindToPort = true - } - } - - if egressListener.IstioListener != nil && - egressListener.IstioListener.Port != nil { - // We have a non catch all listener on some user specified port - // The user specified port may or may not match a service port. - // If it does not match any service port and the service has only - // one port, then we pick a default service port. If service has - // multiple ports, we expect the user to provide a virtualService - // that will route to a proper Service. - - // Skip ports we cannot bind to - if !node.CanBindToPort(bindToPort, egressListener.IstioListener.Port.Number) { - log.Warnf("buildSidecarOutboundListeners: skipping privileged sidecar port %d for node %s as it is an unprivileged proxy", - egressListener.IstioListener.Port.Number, node.ID) - continue - } - - listenPort := &model.Port{ - Port: int(egressListener.IstioListener.Port.Number), - Protocol: protocol.Parse(egressListener.IstioListener.Port.Protocol), - Name: egressListener.IstioListener.Port.Name, - } - - // If capture mode is NONE i.e., bindToPort is true, and - // Bind IP + Port is specified, we will bind to the specified IP and Port. - // This specified IP is ideally expected to be a loopback IP. - // - // If capture mode is NONE i.e., bindToPort is true, and - // only Port is specified, we will bind to the default loopback IP - // 127.0.0.1 and the specified Port. - // - // If capture mode is NONE, i.e., bindToPort is true, and - // only Bind IP is specified, we will bind to the specified IP - // for each port as defined in the service registry. - // - // If captureMode is not NONE, i.e., bindToPort is false, then - // we will bind to user specified IP (if any) or to the VIPs of services in - // this egress listener. - bind := egressListener.IstioListener.Bind - if bind == "" { - if bindToPort { - bind = actualLocalHostAddress - } else { - bind = actualWildcard - } - } - - // Build ListenerOpts and PluginParams once and reuse across all Services to avoid unnecessary allocations. - listenerOpts := buildListenerOpts{ - push: push, - proxy: node, - bind: bind, - port: listenPort, - bindToPort: bindToPort, - } - - for _, service := range services { - listenerOpts.service = service - // Set service specific attributes here. - lb.buildSidecarOutboundListenerForPortOrUDS(listenerOpts, listenerMap, virtualServices, actualWildcard) - } - } else { - // This is a catch all egress listener with no port. This - // should be the last egress listener in the sidecar - // Scope. Construct a listener for each service and service - // port, if and only if this port was not specified in any of - // the preceding listeners from the sidecarScope. This allows - // users to specify a trimmed set of services for one or more - // listeners and then add a catch all egress listener for all - // other ports. Doing so allows people to restrict the set of - // services exposed on one or more listeners, and avoid hard - // port conflicts like tcp taking over http or http taking over - // tcp, or simply specify that of all the listeners that Istio - // generates, the user would like to have only specific sets of - // services exposed on a particular listener. - // - // To ensure that we do not add anything to listeners we have - // already generated, run through the outboundListenerEntry map and set - // the locked bit to true. - // buildSidecarOutboundListenerForPortOrUDS will not add/merge - // any HTTP/TCP listener if there is already a outboundListenerEntry - // with locked bit set to true - for _, e := range listenerMap { - e.locked = true - } - - bind := "" - if egressListener.IstioListener != nil && egressListener.IstioListener.Bind != "" { - bind = egressListener.IstioListener.Bind - } - if bindToPort && bind == "" { - bind = actualLocalHostAddress - } - - // Build ListenerOpts and PluginParams once and reuse across all Services to avoid unnecessary allocations. - listenerOpts := buildListenerOpts{ - push: push, - proxy: node, - bindToPort: bindToPort, - } - - for _, service := range services { - saddress := service.GetAddressForProxy(node) - for _, servicePort := range service.Ports { - // Skip ports we cannot bind to - if !node.CanBindToPort(bindToPort, uint32(servicePort.Port)) { - // here, we log at DEBUG level instead of WARN to avoid noise - // when the catch all egress listener hits ports 80 and 443 - log.Debugf("buildSidecarOutboundListeners: skipping privileged sidecar port %d for node %s as it is an unprivileged proxy", - servicePort.Port, node.ID) - continue - } - - // bind might have been modified by below code, so reset it for every Service. - listenerOpts.bind = bind - // port depends on servicePort. - listenerOpts.port = servicePort - listenerOpts.service = service - - // Support statefulsets/headless services with TCP ports, and empty service address field. - // Instead of generating a single 0.0.0.0:Port listener, generate a listener - // for each instance. HTTP services can happily reside on 0.0.0.0:PORT and use the - // wildcard route match to get to the appropriate IP through original dst clusters. - if features.EnableHeadlessService && bind == "" && service.Resolution == model.Passthrough && - saddress == constants.UnspecifiedIP && (servicePort.Protocol.IsTCP() || servicePort.Protocol.IsUnsupported()) { - instances := push.ServiceInstancesByPort(service, servicePort.Port, nil) - if service.Attributes.ServiceRegistry != provider.Kubernetes && len(instances) == 0 && service.Attributes.LabelSelectors == nil { - // A Kubernetes service with no endpoints means there are no endpoints at - // all, so don't bother sending, as traffic will never work. If we did - // send a wildcard listener, we may get into a situation where a scale - // down leads to a listener conflict. Similarly, if we have a - // labelSelector on the Service, then this may have endpoints not yet - // selected or scaled down, so we skip these as well. This leaves us with - // only a plain ServiceEntry with resolution NONE. In this case, we will - // fallback to a wildcard listener. - lb.buildSidecarOutboundListenerForPortOrUDS(listenerOpts, listenerMap, virtualServices, actualWildcard) - continue - } - for _, instance := range instances { - // Make sure each endpoint address is a valid address - // as service entries could have NONE resolution with label selectors for workload - // entries (which could technically have hostnames). - if net.ParseIP(instance.Endpoint.Address) == nil { - continue - } - // Skip build outbound listener to the node itself, - // as when app access itself by pod ip will not flow through this listener. - // Simultaneously, it will be duplicate with inbound listener. - if instance.Endpoint.Address == node.IPAddresses[0] { - continue - } - listenerOpts.bind = instance.Endpoint.Address - lb.buildSidecarOutboundListenerForPortOrUDS(listenerOpts, listenerMap, virtualServices, actualWildcard) - } - } else { - // Standard logic for headless and non headless services - lb.buildSidecarOutboundListenerForPortOrUDS(listenerOpts, listenerMap, virtualServices, actualWildcard) - } - } - } - } - } - - // Now validate all the listeners. Collate the tcp listeners first and then the HTTP listeners - // TODO: This is going to be bad for caching as the order of listeners in tcpListeners or httpListeners is not - // guaranteed. - for _, l := range listenerMap { - if l.servicePort.Protocol.IsTCP() { - tcpListeners = append(tcpListeners, l.listener) - } else { - httpListeners = append(httpListeners, l.listener) - } - } - tcpListeners = append(tcpListeners, httpListeners...) - // Build pass through filter chains now that all the non-passthrough filter chains are ready. - for _, l := range tcpListeners { - appendListenerFallthroughRouteForCompleteListener(l, node, push) - } - removeListenerFilterTimeout(tcpListeners) - return tcpListeners -} - -func (lb *ListenerBuilder) buildHTTPProxy(node *model.Proxy, - push *model.PushContext) *listener.Listener { - httpProxyPort := push.Mesh.ProxyHttpPort // global - if node.Metadata.HTTPProxyPort != "" { - port, err := strconv.Atoi(node.Metadata.HTTPProxyPort) - if err == nil { - httpProxyPort = int32(port) - } - } - if httpProxyPort == 0 { - return nil - } - - // enable HTTP PROXY port if necessary; this will add an RDS route for this port - _, listenAddress := getActualWildcardAndLocalHost(node) - - httpOpts := &core.Http1ProtocolOptions{ - AllowAbsoluteUrl: proto.BoolTrue, - } - if features.HTTP10 || enableHTTP10(node.Metadata.HTTP10) { - httpOpts.AcceptHttp_10 = true - } - - opts := buildListenerOpts{ - push: push, - proxy: node, - bind: listenAddress, - port: &model.Port{Port: int(httpProxyPort)}, - filterChainOpts: []*filterChainOpts{{ - httpOpts: &httpListenerOpts{ - rds: model.RDSHttpProxy, - useRemoteAddress: false, - connectionManager: &hcm.HttpConnectionManager{ - HttpProtocolOptions: httpOpts, - }, - protocol: protocol.HTTP_PROXY, - class: istionetworking.ListenerClassSidecarOutbound, - }, - }}, - bindToPort: true, - skipUserFilters: true, - } - l := buildListener(opts, core.TrafficDirection_OUTBOUND) - - // TODO: plugins for HTTP_PROXY mode, envoyfilter needs another listener match for SIDECAR_HTTP_PROXY - mutable := &MutableListener{ - MutableObjects: istionetworking.MutableObjects{ - Listener: l, - FilterChains: []istionetworking.FilterChain{{}}, - }, - } - if err := mutable.build(lb, opts); err != nil { - log.Warn("buildHTTPProxy filter chain error ", err.Error()) - return nil - } - return l -} - -func buildSidecarOutboundHTTPListenerOptsForPortOrUDS(listenerMapKey *string, - currentListenerEntry **outboundListenerEntry, listenerOpts *buildListenerOpts, - listenerMap map[string]*outboundListenerEntry, actualWildcard string) (bool, []*filterChainOpts) { - // first identify the bind if its not set. Then construct the key - // used to lookup the listener in the conflict map. - if len(listenerOpts.bind) == 0 { // no user specified bind. Use 0.0.0.0:Port - listenerOpts.bind = actualWildcard - } - *listenerMapKey = listenerOpts.bind + ":" + strconv.Itoa(listenerOpts.port.Port) - - var exists bool - sniffingEnabled := features.EnableProtocolSniffingForOutbound - - // Have we already generated a listener for this Port based on user - // specified listener ports? if so, we should not add any more HTTP - // services to the port. The user could have specified a sidecar - // resource with one or more explicit ports and then added a catch - // all listener, implying add all other ports as usual. When we are - // iterating through the services for a catchAll egress listener, - // the caller would have set the locked bit for each listener Entry - // in the map. - // - // Check if this HTTP listener conflicts with an existing TCP - // listener. We could have listener conflicts occur on unix domain - // sockets, or on IP binds. Specifically, its common to see - // conflicts on binds for wildcard address when a service has NONE - // resolution type, since we collapse all HTTP listeners into a - // single 0.0.0.0:port listener and use vhosts to distinguish - // individual http services in that port - if *currentListenerEntry, exists = listenerMap[*listenerMapKey]; exists { - // NOTE: This is not a conflict. This is simply filtering the - // services for a given listener explicitly. - // When the user declares their own ports in Sidecar.egress - // with some specific services on those ports, we should not - // generate any more listeners on that port as the user does - // not want those listeners. Protocol sniffing is not needed. - if (*currentListenerEntry).locked { - return false, nil - } - - if !sniffingEnabled { - if listenerOpts.service != nil { - if !(*currentListenerEntry).servicePort.Protocol.IsHTTP() { - outboundListenerConflict{ - metric: model.ProxyStatusConflictOutboundListenerTCPOverHTTP, - node: listenerOpts.proxy, - listenerName: *listenerMapKey, - currentServices: (*currentListenerEntry).services, - currentProtocol: (*currentListenerEntry).servicePort.Protocol, - newHostname: listenerOpts.service.Hostname, - newProtocol: listenerOpts.port.Protocol, - }.addMetric(listenerOpts.push) - } - - // Skip building listener for the same http port - (*currentListenerEntry).services = append((*currentListenerEntry).services, listenerOpts.service) - } - return false, nil - } - } - - listenerProtocol := istionetworking.ModelProtocolToListenerProtocol(listenerOpts.port.Protocol, core.TrafficDirection_OUTBOUND) - - // No conflicts. Add a http filter chain option to the listenerOpts - var rdsName string - if listenerOpts.port.Port == 0 { - rdsName = listenerOpts.bind // use the UDS as a rds name - } else { - if listenerProtocol == istionetworking.ListenerProtocolAuto && - sniffingEnabled && listenerOpts.bind != actualWildcard && listenerOpts.service != nil { - rdsName = string(listenerOpts.service.Hostname) + ":" + strconv.Itoa(listenerOpts.port.Port) - } else { - rdsName = strconv.Itoa(listenerOpts.port.Port) - } - } - httpOpts := &httpListenerOpts{ - // Set useRemoteAddress to true for side car outbound listeners so that it picks up the localhost address of the sender, - // which is an internal address, so that trusted headers are not sanitized. This helps to retain the timeout headers - // such as "x-envoy-upstream-rq-timeout-ms" set by the calling application. - useRemoteAddress: features.UseRemoteAddress, - rds: rdsName, - - protocol: listenerOpts.port.Protocol, - class: istionetworking.ListenerClassSidecarOutbound, - } - - if features.HTTP10 || enableHTTP10(listenerOpts.proxy.Metadata.HTTP10) { - httpOpts.connectionManager = &hcm.HttpConnectionManager{ - HttpProtocolOptions: &core.Http1ProtocolOptions{ - AcceptHttp_10: true, - }, - } - } - - return true, []*filterChainOpts{{ - httpOpts: httpOpts, - }} -} - -func buildSidecarOutboundTCPListenerOptsForPortOrUDS(listenerMapKey *string, - currentListenerEntry **outboundListenerEntry, listenerOpts *buildListenerOpts, listenerMap map[string]*outboundListenerEntry, - virtualServices []config.Config, actualWildcard string) (bool, []*filterChainOpts) { - // first identify the bind if its not set. Then construct the key - // used to lookup the listener in the conflict map. - - // Determine the listener address if bind is empty - // we listen on the service VIP if and only - // if the address is an IP address. If its a CIDR, we listen on - // 0.0.0.0, and setup a filter chain match for the CIDR range. - // As a small optimization, CIDRs with /32 prefix will be converted - // into listener address so that there is a dedicated listener for this - // ip:port. This will reduce the impact of a listener reload - var destinationCIDR string - if len(listenerOpts.bind) == 0 { - svcListenAddress := listenerOpts.service.GetAddressForProxy(listenerOpts.proxy) - // We should never get an empty address. - // This is a safety guard, in case some platform adapter isn't doing things - // properly - if len(svcListenAddress) > 0 { - if !strings.Contains(svcListenAddress, "/") { - listenerOpts.bind = svcListenAddress - } else { - // Address is a CIDR. Fall back to 0.0.0.0 and - // filter chain match - destinationCIDR = svcListenAddress - listenerOpts.bind = actualWildcard - } - } - } - - // could be a unix domain socket or an IP bind - *listenerMapKey = listenerKey(listenerOpts.bind, listenerOpts.port.Port) - - var exists bool - - // Have we already generated a listener for this Port based on user - // specified listener ports? if so, we should not add any more - // services to the port. The user could have specified a sidecar - // resource with one or more explicit ports and then added a catch - // all listener, implying add all other ports as usual. When we are - // iterating through the services for a catchAll egress listener, - // the caller would have set the locked bit for each listener Entry - // in the map. - // - // Check if this TCP listener conflicts with an existing HTTP listener - if *currentListenerEntry, exists = listenerMap[*listenerMapKey]; exists { - // NOTE: This is not a conflict. This is simply filtering the - // services for a given listener explicitly. - // When the user declares their own ports in Sidecar.egress - // with some specific services on those ports, we should not - // generate any more listeners on that port as the user does - // not want those listeners. Protocol sniffing is not needed. - if (*currentListenerEntry).locked { - return false, nil - } - - if !features.EnableProtocolSniffingForOutbound { - // Check for port collisions between TCP/TLS and HTTP (or unknown). If - // configured correctly, TCP/TLS ports may not collide. We'll - // need to do additional work to find out if there is a - // collision within TCP/TLS. - // If the service port was defined as unknown. It will conflict with all other - // protocols. - if !(*currentListenerEntry).servicePort.Protocol.IsTCP() { - // NOTE: While pluginParams.Service can be nil, - // this code cannot be reached if Service is nil because a pluginParams.Service can be nil only - // for user defined Egress listeners with ports. And these should occur in the API before - // the wildcard egress listener. the check for the "locked" bit will eliminate the collision. - // User is also not allowed to add duplicate ports in the egress listener - var newHostname host.Name - if listenerOpts.service != nil { - newHostname = listenerOpts.service.Hostname - } else { - // user defined outbound listener via sidecar API - newHostname = "sidecar-config-egress-http-listener" - } - - // We have a collision with another TCP port. This can happen - // for headless services, or non-k8s services that do not have - // a VIP, or when we have two binds on a unix domain socket or - // on same IP. Unfortunately we won't know if this is a real - // conflict or not until we process the VirtualServices, etc. - // The conflict resolution is done later in this code - outboundListenerConflict{ - metric: model.ProxyStatusConflictOutboundListenerHTTPOverTCP, - node: listenerOpts.proxy, - listenerName: *listenerMapKey, - currentServices: (*currentListenerEntry).services, - currentProtocol: (*currentListenerEntry).servicePort.Protocol, - newHostname: newHostname, - newProtocol: listenerOpts.port.Protocol, - }.addMetric(listenerOpts.push) - return false, nil - } - } - } - - meshGateway := map[string]bool{constants.IstioMeshGateway: true} - return true, buildSidecarOutboundTCPTLSFilterChainOpts(listenerOpts.proxy, - listenerOpts.push, virtualServices, - destinationCIDR, listenerOpts.service, - listenerOpts.bind, listenerOpts.port, meshGateway) -} - -// buildSidecarOutboundListenerForPortOrUDS builds a single listener and -// adds it to the listenerMap provided by the caller. Listeners are added -// if one doesn't already exist. HTTP listeners on same port are ignored -// (as vhosts are shipped through RDS). TCP listeners on same port are -// allowed only if they have different CIDR matches. -func (lb *ListenerBuilder) buildSidecarOutboundListenerForPortOrUDS(listenerOpts buildListenerOpts, - listenerMap map[string]*outboundListenerEntry, virtualServices []config.Config, actualWildcard string) { - var listenerMapKey string - var currentListenerEntry *outboundListenerEntry - var ret bool - var opts []*filterChainOpts - - listenerOpts.class = istionetworking.ListenerClassSidecarOutbound - - conflictType := NoConflict - - outboundSniffingEnabled := features.EnableProtocolSniffingForOutbound - listenerPortProtocol := listenerOpts.port.Protocol - listenerProtocol := istionetworking.ModelProtocolToListenerProtocol(listenerOpts.port.Protocol, core.TrafficDirection_OUTBOUND) - - // For HTTP_PROXY protocol defined by sidecars, just create the HTTP listener right away. - if listenerPortProtocol == protocol.HTTP_PROXY { - if ret, opts = buildSidecarOutboundHTTPListenerOptsForPortOrUDS(&listenerMapKey, ¤tListenerEntry, - &listenerOpts, listenerMap, actualWildcard); !ret { - return - } - listenerOpts.filterChainOpts = opts - } else { - switch listenerProtocol { - case istionetworking.ListenerProtocolHTTP: - if ret, opts = buildSidecarOutboundHTTPListenerOptsForPortOrUDS(&listenerMapKey, - ¤tListenerEntry, &listenerOpts, listenerMap, actualWildcard); !ret { - return - } - - // Check if conflict happens - if outboundSniffingEnabled && currentListenerEntry != nil { - // Build HTTP listener. If current listener entry is using HTTP or protocol sniffing, - // append the service. Otherwise (TCP), change current listener to use protocol sniffing. - if currentListenerEntry.protocol.IsHTTP() { - // conflictType is HTTPOverHTTP - // In these cases, we just add the services and exit early rather than recreate an identical listener - currentListenerEntry.services = append(currentListenerEntry.services, listenerOpts.service) - return - } else if currentListenerEntry.protocol.IsTCP() { - conflictType = HTTPOverTCP - } else { - // conflictType is HTTPOverAuto - // In these cases, we just add the services and exit early rather than recreate an identical listener - currentListenerEntry.services = append(currentListenerEntry.services, listenerOpts.service) - return - } - } - // Add application protocol filter chain match to the http filter chain. The application protocol will be set by http inspector - // Since application protocol filter chain match has been added to the http filter chain, a fall through filter chain will be - // appended to the listener later to allow arbitrary egress TCP traffic pass through when its port is conflicted with existing - // HTTP services, which can happen when a pod accesses a non registry service. - if outboundSniffingEnabled { - if listenerOpts.bind == actualWildcard { - for _, opt := range opts { - if opt.match == nil { - opt.match = &listener.FilterChainMatch{} - } - - // Support HTTP/1.0, HTTP/1.1 and HTTP/2 - opt.match.ApplicationProtocols = append(opt.match.ApplicationProtocols, plaintextHTTPALPNs...) - opt.match.TransportProtocol = xdsfilters.RawBufferTransportProtocol - } - - listenerOpts.needHTTPInspector = true - - // if we have a tcp fallthrough filter chain, this is no longer an HTTP listener - it - // is instead "unsupported" (auto detected), as we have a TCP and HTTP filter chain with - // inspection to route between them - listenerPortProtocol = protocol.Unsupported - } - } - listenerOpts.filterChainOpts = opts - - case istionetworking.ListenerProtocolTCP: - if ret, opts = buildSidecarOutboundTCPListenerOptsForPortOrUDS(&listenerMapKey, ¤tListenerEntry, - &listenerOpts, listenerMap, virtualServices, actualWildcard); !ret { - return - } - - // Check if conflict happens - if outboundSniffingEnabled && currentListenerEntry != nil { - // Build TCP listener. If current listener entry is using HTTP, add a new TCP filter chain - // If current listener is using protocol sniffing, merge the TCP filter chains. - if currentListenerEntry.protocol.IsHTTP() { - conflictType = TCPOverHTTP - } else if currentListenerEntry.protocol.IsTCP() { - conflictType = TCPOverTCP - } else { - conflictType = TCPOverAuto - } - } - - listenerOpts.filterChainOpts = opts - - case istionetworking.ListenerProtocolAuto: - // Add tcp filter chain, build TCP filter chain first. - if ret, opts = buildSidecarOutboundTCPListenerOptsForPortOrUDS(&listenerMapKey, ¤tListenerEntry, - &listenerOpts, listenerMap, virtualServices, actualWildcard); !ret { - return - } - listenerOpts.filterChainOpts = append(listenerOpts.filterChainOpts, opts...) - - // Add http filter chain and tcp filter chain to the listener opts - if ret, opts = buildSidecarOutboundHTTPListenerOptsForPortOrUDS(&listenerMapKey, ¤tListenerEntry, - &listenerOpts, listenerMap, actualWildcard); !ret { - return - } - - // Add application protocol filter chain match to the http filter chain. The application protocol will be set by http inspector - for _, opt := range opts { - if opt.match == nil { - opt.match = &listener.FilterChainMatch{} - } - - // Support HTTP/1.0, HTTP/1.1 and HTTP/2 - opt.match.ApplicationProtocols = append(opt.match.ApplicationProtocols, plaintextHTTPALPNs...) - opt.match.TransportProtocol = xdsfilters.RawBufferTransportProtocol - } - - listenerOpts.filterChainOpts = append(listenerOpts.filterChainOpts, opts...) - listenerOpts.needHTTPInspector = true - - if currentListenerEntry != nil { - if currentListenerEntry.protocol.IsHTTP() { - conflictType = AutoOverHTTP - } else if currentListenerEntry.protocol.IsTCP() { - conflictType = AutoOverTCP - } else { - // conflictType is AutoOverAuto - // In these cases, we just add the services and exit early rather than recreate an identical listener - currentListenerEntry.services = append(currentListenerEntry.services, listenerOpts.service) - return - } - } - - default: - // UDP or other protocols: no need to log, it's too noisy - return - } - } - - // Lets build the new listener with the filter chains. In the end, we will - // merge the filter chains with any existing listener on the same port/bind point - l := buildListener(listenerOpts, core.TrafficDirection_OUTBOUND) - - mutable := &MutableListener{ - MutableObjects: istionetworking.MutableObjects{ - Listener: l, - FilterChains: getPluginFilterChain(listenerOpts), - }, - } - - // Filters are serialized one time into an opaque struct once we have the complete list. - if err := mutable.build(lb, listenerOpts); err != nil { - log.Warn("buildSidecarOutboundListeners: ", err.Error()) - return - } - - // If there is a TCP listener on well known port, cannot add any http filter chain - // with the inspector as it will break for server-first protocols. Similarly, - // if there was a HTTP listener on well known port, cannot add a tcp listener - // with the inspector as inspector breaks all server-first protocols. - if currentListenerEntry != nil && - !isConflictWithWellKnownPort(listenerOpts.port.Protocol, currentListenerEntry.protocol, conflictType) { - log.Warnf("conflict happens on a well known port %d, incoming protocol %v, existing protocol %v, conflict type %v", - listenerOpts.port.Port, listenerOpts.port.Protocol, currentListenerEntry.protocol, conflictType) - return - } - - // There are 9 types conflicts - // Incoming Existing - // 1. HTTP -> HTTP - // 2. HTTP -> TCP - // 3. HTTP -> unknown - // 4. TCP -> HTTP - // 5. TCP -> TCP - // 6. TCP -> unknown - // 7. unknown -> HTTP - // 8. unknown -> TCP - // 9. unknown -> unknown - // Type 1 can be resolved by appending service to existing services - // Type 2 can be resolved by merging TCP filter chain with HTTP filter chain - // Type 3 can be resolved by appending service to existing services - // Type 4 can be resolved by merging HTTP filter chain with TCP filter chain - // Type 5 can be resolved by merging TCP filter chains - // Type 6 can be resolved by merging TCP filter chains - // Type 7 can be resolved by appending service to existing services - // Type 8 can be resolved by merging TCP filter chains - // Type 9 can be resolved by merging TCP and HTTP filter chains - - switch conflictType { - case NoConflict: - if currentListenerEntry != nil { - currentListenerEntry.listener.FilterChains = mergeTCPFilterChains(mutable.Listener.FilterChains, - listenerOpts, listenerMapKey, listenerMap) - } else { - listenerMap[listenerMapKey] = &outboundListenerEntry{ - services: []*model.Service{listenerOpts.service}, - servicePort: listenerOpts.port, - bind: listenerOpts.bind, - listener: mutable.Listener, - protocol: listenerPortProtocol, - } - } - case HTTPOverTCP: - // Merge HTTP filter chain to TCP filter chain - currentListenerEntry.listener.FilterChains = mergeFilterChains(mutable.Listener.FilterChains, currentListenerEntry.listener.FilterChains) - currentListenerEntry.protocol = protocol.Unsupported - currentListenerEntry.listener.ListenerFilters = appendListenerFilters(currentListenerEntry.listener.ListenerFilters) - currentListenerEntry.services = append(currentListenerEntry.services, listenerOpts.service) - - case TCPOverHTTP: - // Merge TCP filter chain to HTTP filter chain - currentListenerEntry.listener.FilterChains = mergeFilterChains(currentListenerEntry.listener.FilterChains, mutable.Listener.FilterChains) - currentListenerEntry.protocol = protocol.Unsupported - currentListenerEntry.listener.ListenerFilters = appendListenerFilters(currentListenerEntry.listener.ListenerFilters) - case TCPOverTCP: - // Merge two TCP filter chains. HTTP filter chain will not conflict with TCP filter chain because HTTP filter chain match for - // HTTP filter chain is different from TCP filter chain's. - currentListenerEntry.listener.FilterChains = mergeTCPFilterChains(mutable.Listener.FilterChains, listenerOpts, listenerMapKey, listenerMap) - case TCPOverAuto: - // Merge two TCP filter chains. HTTP filter chain will not conflict with TCP filter chain because HTTP filter chain match for - // HTTP filter chain is different from TCP filter chain's. - currentListenerEntry.listener.FilterChains = mergeTCPFilterChains(mutable.Listener.FilterChains, listenerOpts, listenerMapKey, listenerMap) - - case AutoOverHTTP: - listenerMap[listenerMapKey] = &outboundListenerEntry{ - services: append(currentListenerEntry.services, listenerOpts.service), - servicePort: listenerOpts.port, - bind: listenerOpts.bind, - listener: mutable.Listener, - protocol: protocol.Unsupported, - } - currentListenerEntry.listener.ListenerFilters = appendListenerFilters(currentListenerEntry.listener.ListenerFilters) - - case AutoOverTCP: - // Merge two TCP filter chains. HTTP filter chain will not conflict with TCP filter chain because HTTP filter chain match for - // HTTP filter chain is different from TCP filter chain's. - currentListenerEntry.listener.FilterChains = mergeTCPFilterChains(mutable.Listener.FilterChains, - listenerOpts, listenerMapKey, listenerMap) - currentListenerEntry.protocol = protocol.Unsupported - currentListenerEntry.listener.ListenerFilters = appendListenerFilters(currentListenerEntry.listener.ListenerFilters) - - default: - // Covered previously - in this case we return early to prevent creating listeners that we end up throwing away - // This should never happen - log.Errorf("Got unexpected conflict type %v. This should never happen", conflictType) - } - - if log.DebugEnabled() && len(mutable.Listener.FilterChains) > 1 || currentListenerEntry != nil { - var numChains int - if currentListenerEntry != nil { - numChains = len(currentListenerEntry.listener.FilterChains) - } else { - numChains = len(mutable.Listener.FilterChains) - } - log.Debugf("buildSidecarOutboundListeners: multiple filter chain listener %s with %d chains", mutable.Listener.Name, numChains) - } -} - -// httpListenerOpts are options for an HTTP listener -type httpListenerOpts struct { - routeConfig *route.RouteConfiguration - rds string - // If set, use this as a basis - connectionManager *hcm.HttpConnectionManager - // stat prefix for the http connection manager - // DO not set this field. Will be overridden by buildCompleteFilterChain - statPrefix string - protocol protocol.Instance - useRemoteAddress bool - - // http3Only indicates that the HTTP codec used - // is HTTP/3 over QUIC transport (uses UDP) - http3Only bool - - class istionetworking.ListenerClass -} - -// filterChainOpts describes a filter chain: a set of filters with the same TLS context -type filterChainOpts struct { - filterChainName string - sniHosts []string - destinationCIDRs []string - metadata *core.Metadata - tlsContext *auth.DownstreamTlsContext - httpOpts *httpListenerOpts - match *listener.FilterChainMatch - listenerFilters []*listener.ListenerFilter - networkFilters []*listener.Filter - filterChain istionetworking.FilterChain -} - -// buildListenerOpts are the options required to build a Listener -type buildListenerOpts struct { - // nolint: maligned - push *model.PushContext - proxy *model.Proxy - bind string - port *model.Port - filterChainOpts []*filterChainOpts - bindToPort bool - skipUserFilters bool - needHTTPInspector bool - class istionetworking.ListenerClass - service *model.Service - transport istionetworking.TransportProtocol -} - -func (lb *ListenerBuilder) buildHTTPConnectionManager(httpOpts *httpListenerOpts) *hcm.HttpConnectionManager { - if httpOpts.connectionManager == nil { - httpOpts.connectionManager = &hcm.HttpConnectionManager{} - } - - connectionManager := httpOpts.connectionManager - if httpOpts.http3Only { - connectionManager.CodecType = hcm.HttpConnectionManager_HTTP3 - connectionManager.Http3ProtocolOptions = &core.Http3ProtocolOptions{} - } else { - connectionManager.CodecType = hcm.HttpConnectionManager_AUTO - } - connectionManager.AccessLog = []*accesslog.AccessLog{} - connectionManager.StatPrefix = httpOpts.statPrefix - - // Setup normalization - connectionManager.PathWithEscapedSlashesAction = hcm.HttpConnectionManager_KEEP_UNCHANGED - switch lb.push.Mesh.GetPathNormalization().GetNormalization() { - case meshconfig.MeshConfig_ProxyPathNormalization_NONE: - connectionManager.NormalizePath = proto.BoolFalse - case meshconfig.MeshConfig_ProxyPathNormalization_BASE, meshconfig.MeshConfig_ProxyPathNormalization_DEFAULT: - connectionManager.NormalizePath = proto.BoolTrue - case meshconfig.MeshConfig_ProxyPathNormalization_MERGE_SLASHES: - connectionManager.NormalizePath = proto.BoolTrue - connectionManager.MergeSlashes = true - case meshconfig.MeshConfig_ProxyPathNormalization_DECODE_AND_MERGE_SLASHES: - connectionManager.NormalizePath = proto.BoolTrue - connectionManager.MergeSlashes = true - connectionManager.PathWithEscapedSlashesAction = hcm.HttpConnectionManager_UNESCAPE_AND_FORWARD - } - - if httpOpts.useRemoteAddress { - connectionManager.UseRemoteAddress = proto.BoolTrue - } else { - connectionManager.UseRemoteAddress = proto.BoolFalse - } - - // Allow websocket upgrades - websocketUpgrade := &hcm.HttpConnectionManager_UpgradeConfig{UpgradeType: "websocket"} - connectionManager.UpgradeConfigs = []*hcm.HttpConnectionManager_UpgradeConfig{websocketUpgrade} - - idleTimeout, err := time.ParseDuration(lb.node.Metadata.IdleTimeout) - if err == nil { - connectionManager.CommonHttpProtocolOptions = &core.HttpProtocolOptions{ - IdleTimeout: durationpb.New(idleTimeout), - } - } - - notimeout := durationpb.New(0 * time.Second) - connectionManager.StreamIdleTimeout = notimeout - - if httpOpts.rds != "" { - rds := &hcm.HttpConnectionManager_Rds{ - Rds: &hcm.Rds{ - ConfigSource: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_Ads{ - Ads: &core.AggregatedConfigSource{}, - }, - InitialFetchTimeout: durationpb.New(0), - ResourceApiVersion: core.ApiVersion_V3, - }, - RouteConfigName: httpOpts.rds, - }, - } - connectionManager.RouteSpecifier = rds - } else { - connectionManager.RouteSpecifier = &hcm.HttpConnectionManager_RouteConfig{RouteConfig: httpOpts.routeConfig} - } - - accessLogBuilder.setHTTPAccessLog(lb.push, lb.node, connectionManager, httpOpts.class) - - routerFilterCtx, reqIDExtensionCtx := configureTracing(lb.push, lb.node, connectionManager, httpOpts.class) - - filters := []*hcm.HttpFilter{} - wasm := lb.push.WasmPlugins(lb.node) - // TODO: how to deal with ext-authz? It will be in the ordering twice - filters = append(filters, lb.authzCustomBuilder.BuildHTTP(httpOpts.class)...) - filters = extension.PopAppend(filters, wasm, extensions.PluginPhase_AUTHN) - filters = append(filters, lb.authnBuilder.BuildHTTP(httpOpts.class)...) - filters = extension.PopAppend(filters, wasm, extensions.PluginPhase_AUTHZ) - filters = append(filters, lb.authzBuilder.BuildHTTP(httpOpts.class)...) - - // TODO: these feel like the wrong place to insert, but this retains backwards compatibility with the original implementation - filters = extension.PopAppend(filters, wasm, extensions.PluginPhase_STATS) - filters = extension.PopAppend(filters, wasm, extensions.PluginPhase_UNSPECIFIED_PHASE) - - if features.MetadataExchange { - filters = append(filters, xdsfilters.HTTPMx) - } - - if httpOpts.protocol == protocol.GRPCWeb { - filters = append(filters, xdsfilters.GrpcWeb) - } - - if httpOpts.protocol.IsGRPC() { - filters = append(filters, xdsfilters.GrpcStats) - } - - // append ALPN HTTP filter in HTTP connection manager for outbound listener only. - if features.ALPNFilter { - if httpOpts.class != istionetworking.ListenerClassSidecarInbound { - filters = append(filters, xdsfilters.Alpn) - } - } - - // TypedPerFilterConfig in route needs these filters. - filters = append(filters, xdsfilters.Fault, xdsfilters.Cors) - filters = append(filters, lb.push.Telemetry.HTTPFilters(lb.node, httpOpts.class)...) - filters = append(filters, xdsfilters.BuildRouterFilter(routerFilterCtx)) - - connectionManager.HttpFilters = filters - connectionManager.RequestIdExtension = requestidextension.BuildUUIDRequestIDExtension(reqIDExtensionCtx) - - return connectionManager -} - -// buildListener builds and initializes a Listener proto based on the provided opts. It does not set any filters. -// Optionally for HTTP filters with TLS enabled, HTTP/3 can be supported by generating QUIC Mirror filters for the -// same port (it is fine as QUIC uses UDP) -func buildListener(opts buildListenerOpts, trafficDirection core.TrafficDirection) *listener.Listener { - filterChains := make([]*listener.FilterChain, 0, len(opts.filterChainOpts)) - listenerFiltersMap := make(map[string]bool) - var listenerFilters []*listener.ListenerFilter - - // add a TLS inspector if we need to detect ServerName or ALPN - // (this is not applicable for QUIC listeners) - needTLSInspector := false - if opts.transport == istionetworking.TransportProtocolTCP { - for _, chain := range opts.filterChainOpts { - needsALPN := chain.tlsContext != nil && chain.tlsContext.CommonTlsContext != nil && len(chain.tlsContext.CommonTlsContext.AlpnProtocols) > 0 - if len(chain.sniHosts) > 0 || needsALPN { - needTLSInspector = true - break - } - } - } - - if opts.proxy.GetInterceptionMode() == model.InterceptionTproxy && trafficDirection == core.TrafficDirection_INBOUND { - listenerFiltersMap[wellknown.OriginalSource] = true - listenerFilters = append(listenerFilters, xdsfilters.OriginalSrc) - } - - // We add a TLS inspector when http inspector is needed for outbound only. This - // is because if we ever set ALPN in the match without - // transport_protocol=raw_buffer, Envoy will automatically inject a tls - // inspector: https://github.com/envoyproxy/envoy/issues/13601. This leads to - // excessive logging and loss of control over the config For inbound this is not - // needed, since we are explicitly setting transport protocol in every single - // match. We can do this for outbound as well, at which point this could be - // removed, but have not yet - if opts.transport == istionetworking.TransportProtocolTCP && - (needTLSInspector || (opts.class == istionetworking.ListenerClassSidecarOutbound && opts.needHTTPInspector)) { - listenerFiltersMap[wellknown.TlsInspector] = true - listenerFilters = append(listenerFilters, xdsfilters.TLSInspector) - } - - // TODO: For now we assume that only HTTP/3 is used over QUIC. Revisit this in the future - if opts.needHTTPInspector && opts.transport == istionetworking.TransportProtocolTCP { - listenerFiltersMap[wellknown.HttpInspector] = true - listenerFilters = append(listenerFilters, xdsfilters.HTTPInspector) - } - - for _, chain := range opts.filterChainOpts { - for _, filter := range chain.listenerFilters { - if _, exist := listenerFiltersMap[filter.Name]; !exist { - listenerFiltersMap[filter.Name] = true - listenerFilters = append(listenerFilters, filter) - } - } - match := &listener.FilterChainMatch{} - needMatch := false - if chain.match != nil { - needMatch = true - match = chain.match - } - if len(chain.sniHosts) > 0 { - fullWildcardFound := false - for _, h := range chain.sniHosts { - if h == "*" { - fullWildcardFound = true - // If we have a host with *, it effectively means match anything, i.e. - // no SNI based matching for this host. - break - } - } - if !fullWildcardFound { - chain.sniHosts = append([]string{}, chain.sniHosts...) - sort.Stable(sort.StringSlice(chain.sniHosts)) - match.ServerNames = chain.sniHosts - } - } - if len(chain.destinationCIDRs) > 0 { - chain.destinationCIDRs = append([]string{}, chain.destinationCIDRs...) - sort.Stable(sort.StringSlice(chain.destinationCIDRs)) - for _, d := range chain.destinationCIDRs { - cidr := util.ConvertAddressToCidr(d) - if cidr != nil && cidr.AddressPrefix != constants.UnspecifiedIP { - match.PrefixRanges = append(match.PrefixRanges, cidr) - } - } - } - - if !needMatch && filterChainMatchEmpty(match) { - match = nil - } - var transportSocket *core.TransportSocket - switch opts.transport { - case istionetworking.TransportProtocolTCP: - transportSocket = buildDownstreamTLSTransportSocket(chain.tlsContext) - case istionetworking.TransportProtocolQUIC: - transportSocket = buildDownstreamQUICTransportSocket(chain.tlsContext) - } - filterChains = append(filterChains, &listener.FilterChain{ - FilterChainMatch: match, - TransportSocket: transportSocket, - }) - } - - var res *listener.Listener - switch opts.transport { - case istionetworking.TransportProtocolTCP: - var bindToPort *wrappers.BoolValue - var connectionBalance *listener.Listener_ConnectionBalanceConfig - if !opts.bindToPort { - bindToPort = proto.BoolFalse - } - - // only use to exact_balance for tcp outbound listeners; virtualOutbound listener should - // not have this set per Envoy docs for redirected listeners - if opts.proxy.Metadata.OutboundListenerExactBalance && trafficDirection == core.TrafficDirection_OUTBOUND { - connectionBalance = &listener.Listener_ConnectionBalanceConfig{ - BalanceType: &listener.Listener_ConnectionBalanceConfig_ExactBalance_{ - ExactBalance: &listener.Listener_ConnectionBalanceConfig_ExactBalance{}, - }, - } - } - - res = &listener.Listener{ - // TODO: need to sanitize the opts.bind if its a UDS socket, as it could have colons, that envoy doesn't like - Name: getListenerName(opts.bind, opts.port.Port, istionetworking.TransportProtocolTCP), - Address: util.BuildAddress(opts.bind, uint32(opts.port.Port)), - TrafficDirection: trafficDirection, - ListenerFilters: listenerFilters, - FilterChains: filterChains, - BindToPort: bindToPort, - ConnectionBalanceConfig: connectionBalance, - } - - if opts.proxy.Type != model.Router { - res.ListenerFiltersTimeout = opts.push.Mesh.ProtocolDetectionTimeout - if res.ListenerFiltersTimeout != nil { - res.ContinueOnListenerFiltersTimeout = true - } - } - case istionetworking.TransportProtocolQUIC: - // TODO: switch on TransportProtocolQUIC is in too many places now. Once this is a bit - // mature, refactor some of these to an interface so that they kick off the process - // of building listener, filter chains, serializing etc based on transport protocol - listenerName := getListenerName(opts.bind, opts.port.Port, istionetworking.TransportProtocolQUIC) - log.Debugf("buildListener: building UDP/QUIC listener %s", listenerName) - res = &listener.Listener{ - Name: listenerName, - Address: util.BuildNetworkAddress(opts.bind, uint32(opts.port.Port), istionetworking.TransportProtocolQUIC), - TrafficDirection: trafficDirection, - FilterChains: filterChains, - UdpListenerConfig: &listener.UdpListenerConfig{ - // TODO: Maybe we should add options in MeshConfig to - // configure QUIC options - it should look similar - // to the H2 protocol options. - QuicOptions: &listener.QuicProtocolOptions{}, - DownstreamSocketConfig: &core.UdpSocketConfig{}, - }, - EnableReusePort: proto.BoolTrue, - } - } - - accessLogBuilder.setListenerAccessLog(opts.push, opts.proxy, res, opts.class) - - return res -} - -func getMatchAllFilterChain(l *listener.Listener) (int, *listener.FilterChain) { - for i, fc := range l.FilterChains { - if isMatchAllFilterChain(fc) { - return i, fc - } - } - return 0, nil -} - -// Create pass through filter chain for the listener assuming all the other filter chains are ready. -// The match member of pass through filter chain depends on the existing non-passthrough filter chain. -// TODO(lambdai): Calculate the filter chain match to replace the wildcard and replace appendListenerFallthroughRoute. -func appendListenerFallthroughRouteForCompleteListener(l *listener.Listener, node *model.Proxy, push *model.PushContext) { - matchIndex, matchAll := getMatchAllFilterChain(l) - - fallthroughNetworkFilters := buildOutboundCatchAllNetworkFiltersOnly(push, node) - - outboundPassThroughFilterChain := &listener.FilterChain{ - FilterChainMatch: &listener.FilterChainMatch{}, - Name: util.PassthroughFilterChain, - Filters: fallthroughNetworkFilters, - } - - // Set a default filter chain. This allows us to avoid issues where - // traffic starts to match a filter chain but then doesn't match latter criteria, leading to - // dropped requests. See https://github.com/istio/istio/issues/26079 for details. - // If there are multiple filter chains and a match all chain, move it to DefaultFilterChain - // This ensures it will always be used as the fallback. - if matchAll != nil && len(l.FilterChains) > 1 { - copy(l.FilterChains[matchIndex:], l.FilterChains[matchIndex+1:]) // Shift l.FilterChains[i+1:] left one index. - l.FilterChains[len(l.FilterChains)-1] = nil // Erase last element (write zero value). - l.FilterChains = l.FilterChains[:len(l.FilterChains)-1] // Truncate slice. - l.DefaultFilterChain = matchAll - } else if matchAll == nil { - // Otherwise, if there is no match all already, set a passthrough match all - l.DefaultFilterChain = outboundPassThroughFilterChain - } -} - -// build adds the provided TCP and HTTP filters to the provided Listener and serializes them. -// TODO: given how tightly tied listener.FilterChains, opts.filterChainOpts, and mutable.FilterChains -// are to each other we should encapsulate them some way to ensure they remain consistent (mainly that -// in each an index refers to the same chain). -func (ml *MutableListener) build(builder *ListenerBuilder, opts buildListenerOpts) error { - if len(opts.filterChainOpts) == 0 { - return fmt.Errorf("must have more than 0 chains in listener %q", ml.Listener.Name) - } - httpConnectionManagers := make([]*hcm.HttpConnectionManager, len(ml.FilterChains)) - for i := range ml.FilterChains { - chain := ml.FilterChains[i] - opt := opts.filterChainOpts[i] - ml.Listener.FilterChains[i].Metadata = opt.metadata - ml.Listener.FilterChains[i].Name = opt.filterChainName - if opt.httpOpts == nil { - // we are building a network filter chain (no http connection manager) for this filter chain - // In HTTP, we need to have RBAC, etc. upfront so that they can enforce policies immediately - // For network filters such as mysql, mongo, etc., we need the filter codec upfront. Data from this - // codec is used by RBAC later. - // - // Currently, when transport is QUIC we assume HTTP3. So it should not come here. - // When other protocols are used over QUIC, we have to revisit this assumption. - - if len(opt.networkFilters) > 0 { - // this is the terminating filter - lastNetworkFilter := opt.networkFilters[len(opt.networkFilters)-1] - - for n := 0; n < len(opt.networkFilters)-1; n++ { - ml.Listener.FilterChains[i].Filters = append(ml.Listener.FilterChains[i].Filters, opt.networkFilters[n]) - } - ml.Listener.FilterChains[i].Filters = append(ml.Listener.FilterChains[i].Filters, chain.TCP...) - ml.Listener.FilterChains[i].Filters = append(ml.Listener.FilterChains[i].Filters, lastNetworkFilter) - } else { - ml.Listener.FilterChains[i].Filters = append(ml.Listener.FilterChains[i].Filters, chain.TCP...) - } - log.Debugf("attached %d network filters to listener %q filter chain %d", len(chain.TCP)+len(opt.networkFilters), ml.Listener.Name, i) - } else { - // Add the TCP filters first.. and then the HTTP connection manager. - // Skip adding this if transport is not TCP (could be QUIC) - if chain.TransportProtocol == istionetworking.TransportProtocolTCP { - ml.Listener.FilterChains[i].Filters = append(ml.Listener.FilterChains[i].Filters, chain.TCP...) - } - - // If statPrefix has been set before calling this method, respect that. - if len(opt.httpOpts.statPrefix) == 0 { - opt.httpOpts.statPrefix = strings.ToLower(ml.Listener.TrafficDirection.String()) + "_" + ml.Listener.Name - } - httpConnectionManagers[i] = builder.buildHTTPConnectionManager(opt.httpOpts) - filter := &listener.Filter{ - Name: wellknown.HTTPConnectionManager, - ConfigType: &listener.Filter_TypedConfig{TypedConfig: util.MessageToAny(httpConnectionManagers[i])}, - } - ml.Listener.FilterChains[i].Filters = append(ml.Listener.FilterChains[i].Filters, filter) - log.Debugf("attached HTTP filter with %d http_filter options to listener %q filter chain %d", - len(httpConnectionManagers[i].HttpFilters), ml.Listener.Name, i) - } - } - - return nil -} - -func mergeTCPFilterChains(incoming []*listener.FilterChain, listenerOpts buildListenerOpts, listenerMapKey string, - listenerMap map[string]*outboundListenerEntry) []*listener.FilterChain { - // TODO(rshriram) merge multiple identical filter chains with just a single destination CIDR based - // filter chain match, into a single filter chain and array of destinationcidr matches - - // The code below checks for TCP over TCP conflicts and merges listeners - - // Merge the newly built listener with the existing listener, if and only if the filter chains have distinct conditions. - // Extract the current filter chain matches, for every new filter chain match being added, check if there is a matching - // one in previous filter chains, if so, skip adding this filter chain with a warning. - - currentListenerEntry := listenerMap[listenerMapKey] - mergedFilterChains := make([]*listener.FilterChain, 0, len(currentListenerEntry.listener.FilterChains)+len(incoming)) - // Start with the current listener's filter chains. - mergedFilterChains = append(mergedFilterChains, currentListenerEntry.listener.FilterChains...) - - for _, incomingFilterChain := range incoming { - conflict := false - - for _, existingFilterChain := range mergedFilterChains { - conflict = isConflict(existingFilterChain, incomingFilterChain) - - if conflict { - // NOTE: While pluginParams.Service can be nil, - // this code cannot be reached if Service is nil because a pluginParams.Service can be nil only - // for user defined Egress listeners with ports. And these should occur in the API before - // the wildcard egress listener. the check for the "locked" bit will eliminate the collision. - // User is also not allowed to add duplicate ports in the egress listener - var newHostname host.Name - if listenerOpts.service != nil { - newHostname = listenerOpts.service.Hostname - } else { - // user defined outbound listener via sidecar API - newHostname = "sidecar-config-egress-tcp-listener" - } - - outboundListenerConflict{ - metric: model.ProxyStatusConflictOutboundListenerTCPOverTCP, - node: listenerOpts.proxy, - listenerName: listenerMapKey, - currentServices: currentListenerEntry.services, - currentProtocol: currentListenerEntry.servicePort.Protocol, - newHostname: newHostname, - newProtocol: listenerOpts.port.Protocol, - }.addMetric(listenerOpts.push) - break - } - - } - if !conflict { - // There is no conflict with any filter chain in the existing listener. - // So append the new filter chains to the existing listener's filter chains - mergedFilterChains = append(mergedFilterChains, incomingFilterChain) - if listenerOpts.service != nil { - lEntry := listenerMap[listenerMapKey] - lEntry.services = append(lEntry.services, listenerOpts.service) - } - } - } - return mergedFilterChains -} - -// isConflict determines whether the incoming filter chain has conflict with existing filter chain. -func isConflict(existing, incoming *listener.FilterChain) bool { - return filterChainMatchEqual(existing.FilterChainMatch, incoming.FilterChainMatch) -} - -func filterChainMatchEmpty(fcm *listener.FilterChainMatch) bool { - return fcm == nil || filterChainMatchEqual(fcm, emptyFilterChainMatch) -} - -// filterChainMatchEqual returns true if both filter chains are equal otherwise false. -func filterChainMatchEqual(first *listener.FilterChainMatch, second *listener.FilterChainMatch) bool { - if first == nil || second == nil { - return first == second - } - if first.TransportProtocol != second.TransportProtocol { - return false - } - if !util.StringSliceEqual(first.ApplicationProtocols, second.ApplicationProtocols) { - return false - } - if first.DestinationPort.GetValue() != second.DestinationPort.GetValue() { - return false - } - if !util.CidrRangeSliceEqual(first.PrefixRanges, second.PrefixRanges) { - return false - } - if !util.CidrRangeSliceEqual(first.SourcePrefixRanges, second.SourcePrefixRanges) { - return false - } - if !util.CidrRangeSliceEqual(first.DirectSourcePrefixRanges, second.DirectSourcePrefixRanges) { - return false - } - if first.AddressSuffix != second.AddressSuffix { - return false - } - if first.SuffixLen.GetValue() != second.SuffixLen.GetValue() { - return false - } - if first.SourceType != second.SourceType { - return false - } - if !util.UInt32SliceEqual(first.SourcePorts, second.SourcePorts) { - return false - } - if !util.StringSliceEqual(first.ServerNames, second.ServerNames) { - return false - } - return true -} - -func mergeFilterChains(httpFilterChain, tcpFilterChain []*listener.FilterChain) []*listener.FilterChain { - var newFilterChan []*listener.FilterChain - for _, fc := range httpFilterChain { - if fc.FilterChainMatch == nil { - fc.FilterChainMatch = &listener.FilterChainMatch{} - } - - var missingHTTPALPNs []string - for _, p := range plaintextHTTPALPNs { - if !contains(fc.FilterChainMatch.ApplicationProtocols, p) { - missingHTTPALPNs = append(missingHTTPALPNs, p) - } - } - - fc.FilterChainMatch.ApplicationProtocols = append(fc.FilterChainMatch.ApplicationProtocols, missingHTTPALPNs...) - newFilterChan = append(newFilterChan, fc) - } - return append(tcpFilterChain, newFilterChan...) -} - -// It's fine to use this naive implementation for searching in a very short list like ApplicationProtocols -func contains(s []string, e string) bool { - for _, a := range s { - if a == e { - return true - } - } - return false -} - -func getPluginFilterChain(opts buildListenerOpts) []istionetworking.FilterChain { - filterChain := make([]istionetworking.FilterChain, len(opts.filterChainOpts)) - - for id := range filterChain { - if opts.filterChainOpts[id].httpOpts == nil { - filterChain[id].ListenerProtocol = istionetworking.ListenerProtocolTCP - } else { - filterChain[id].ListenerProtocol = istionetworking.ListenerProtocolHTTP - } - filterChain[id].TCP = opts.filterChainOpts[id].filterChain.TCP - filterChain[id].HTTP = opts.filterChainOpts[id].filterChain.HTTP - } - - return filterChain -} - -// isConflictWithWellKnownPort checks conflicts between incoming protocol and existing protocol. -// Mongo and MySQL are not allowed to co-exist with other protocols in one port. -func isConflictWithWellKnownPort(incoming, existing protocol.Instance, conflict int) bool { - if conflict == NoConflict { - return true - } - - if (incoming == protocol.Mongo || - incoming == protocol.MySQL || - existing == protocol.Mongo || - existing == protocol.MySQL) && incoming != existing { - return false - } - - return true -} - -func appendListenerFilters(filters []*listener.ListenerFilter) []*listener.ListenerFilter { - hasTLSInspector := false - hasHTTPInspector := false - - for _, f := range filters { - hasTLSInspector = hasTLSInspector || f.Name == wellknown.TlsInspector - hasHTTPInspector = hasHTTPInspector || f.Name == wellknown.HttpInspector - } - - if !hasTLSInspector { - filters = append(filters, xdsfilters.TLSInspector) - } - - if !hasHTTPInspector { - filters = append(filters, xdsfilters.HTTPInspector) - } - - return filters -} - -// nolint: interfacer -func buildDownstreamTLSTransportSocket(tlsContext *auth.DownstreamTlsContext) *core.TransportSocket { - if tlsContext == nil { - return nil - } - return &core.TransportSocket{ - Name: util.EnvoyTLSSocketName, - ConfigType: &core.TransportSocket_TypedConfig{TypedConfig: util.MessageToAny(tlsContext)}, - } -} - -func buildDownstreamQUICTransportSocket(tlsContext *auth.DownstreamTlsContext) *core.TransportSocket { - if tlsContext == nil { - return nil - } - return &core.TransportSocket{ - Name: util.EnvoyQUICSocketName, - ConfigType: &core.TransportSocket_TypedConfig{ - TypedConfig: util.MessageToAny(&envoyquicv3.QuicDownstreamTransport{ - DownstreamTlsContext: tlsContext, - }), - }, - } -} - -func isMatchAllFilterChain(fc *listener.FilterChain) bool { - // See if it is empty filter chain. - return filterChainMatchEmpty(fc.FilterChainMatch) -} - -func removeListenerFilterTimeout(listeners []*listener.Listener) { - for _, l := range listeners { - // Remove listener filter timeout for - // 1. outbound listeners AND - // 2. without HTTP inspector - hasHTTPInspector := false - for _, lf := range l.ListenerFilters { - if lf.Name == wellknown.HttpInspector { - hasHTTPInspector = true - break - } - } - - if !hasHTTPInspector && l.TrafficDirection == core.TrafficDirection_OUTBOUND { - l.ListenerFiltersTimeout = nil - l.ContinueOnListenerFiltersTimeout = false - } - } -} - -// listenerKey builds the key for a given bind and port -func listenerKey(bind string, port int) string { - return bind + ":" + strconv.Itoa(port) -} diff --git a/pilot/pkg/networking/core/v1alpha3/listener_address.go b/pilot/pkg/networking/core/v1alpha3/listener_address.go deleted file mode 100644 index 97cd6a4b8..000000000 --- a/pilot/pkg/networking/core/v1alpha3/listener_address.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" -) - -const ( - // WildcardAddress binds to all IP addresses - WildcardAddress = "0.0.0.0" - - // WildcardIPv6Address binds to all IPv6 addresses - WildcardIPv6Address = "::" - - // LocalhostAddress for local binding - LocalhostAddress = "127.0.0.1" - - // LocalhostIPv6Address for local binding - LocalhostIPv6Address = "::1" - - // 6 is the magical number for inbound: 15006, 127.0.0.6, ::6 - InboundPassthroughBindIpv4 = "127.0.0.6" - InboundPassthroughBindIpv6 = "::6" -) - -// getActualWildcardAndLocalHost will return corresponding Wildcard and LocalHost -// depending on value of proxy's IPAddresses. -func getActualWildcardAndLocalHost(node *model.Proxy) (string, string) { - if node.SupportsIPv4() { - return WildcardAddress, LocalhostAddress - } - return WildcardIPv6Address, LocalhostIPv6Address -} - -func getPassthroughBindIP(node *model.Proxy) string { - if node.SupportsIPv4() { - return InboundPassthroughBindIpv4 - } - return InboundPassthroughBindIpv6 -} - -// getSidecarInboundBindIP returns the IP that the proxy can bind to along with the sidecar specified port. -// It looks for an unicast address, if none found, then the default wildcard address is used. -// This will make the inbound listener bind to instance_ip:port instead of 0.0.0.0:port where applicable. -func getSidecarInboundBindIP(node *model.Proxy) string { - // Return the IP if its a global unicast address. - if len(node.GlobalUnicastIP) > 0 { - return node.GlobalUnicastIP - } - defaultInboundIP, _ := getActualWildcardAndLocalHost(node) - return defaultInboundIP -} diff --git a/pilot/pkg/networking/core/v1alpha3/listener_builder.go b/pilot/pkg/networking/core/v1alpha3/listener_builder.go deleted file mode 100644 index 4665a426e..000000000 --- a/pilot/pkg/networking/core/v1alpha3/listener_builder.go +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "time" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - tcp "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "google.golang.org/protobuf/types/known/durationpb" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - networking "istio.io/api/networking/v1alpha3" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - istionetworking "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3/envoyfilter" - istio_route "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3/route" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/plugin/authn" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/plugin/authz" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pkg/proto" -) - -// A stateful listener builder -// Support the below intentions -// 1. Use separate inbound capture listener(:15006) and outbound capture listener(:15001) -// 2. The above listeners use bind_to_port sub listeners or filter chains. -type ListenerBuilder struct { - node *model.Proxy - push *model.PushContext - gatewayListeners []*listener.Listener - inboundListeners []*listener.Listener - outboundListeners []*listener.Listener - // HttpProxyListener is a specialize outbound listener. See MeshConfig.proxyHttpPort - httpProxyListener *listener.Listener - virtualOutboundListener *listener.Listener - virtualInboundListener *listener.Listener - - envoyFilterWrapper *model.EnvoyFilterWrapper - - // authnBuilder provides access to authn (mTLS) configuration for the given proxy. - authnBuilder *authn.Builder - // authzBuilder provides access to authz configuration for the given proxy. - authzBuilder *authz.Builder - // authzCustomBuilder provides access to CUSTOM authz configuration for the given proxy. - authzCustomBuilder *authz.Builder -} - -// enabledInspector captures if for a given listener, listener filter inspectors are added -type enabledInspector struct { - HTTPInspector bool - TLSInspector bool -} - -func NewListenerBuilder(node *model.Proxy, push *model.PushContext) *ListenerBuilder { - builder := &ListenerBuilder{ - node: node, - push: push, - } - builder.authnBuilder = authn.NewBuilder(push, node) - builder.authzBuilder = authz.NewBuilder(authz.Local, push, node) - builder.authzCustomBuilder = authz.NewBuilder(authz.Custom, push, node) - return builder -} - -func (lb *ListenerBuilder) appendSidecarInboundListeners() *ListenerBuilder { - lb.inboundListeners = lb.buildInboundListeners() - return lb -} - -func (lb *ListenerBuilder) appendSidecarOutboundListeners() *ListenerBuilder { - lb.outboundListeners = lb.buildSidecarOutboundListeners(lb.node, lb.push) - return lb -} - -func (lb *ListenerBuilder) buildHTTPProxyListener() *ListenerBuilder { - httpProxy := lb.buildHTTPProxy(lb.node, lb.push) - if httpProxy == nil { - return lb - } - removeListenerFilterTimeout([]*listener.Listener{httpProxy}) - lb.patchOneListener(httpProxy, networking.EnvoyFilter_SIDECAR_OUTBOUND) - lb.httpProxyListener = httpProxy - return lb -} - -func (lb *ListenerBuilder) buildVirtualOutboundListener() *ListenerBuilder { - if lb.node.GetInterceptionMode() == model.InterceptionNone { - // virtual listener is not necessary since workload is not using IPtables for traffic interception - return lb - } - - var isTransparentProxy *wrappers.BoolValue - if lb.node.GetInterceptionMode() == model.InterceptionTproxy { - isTransparentProxy = proto.BoolTrue - } - - filterChains := buildOutboundCatchAllNetworkFilterChains(lb.node, lb.push) - - actualWildcard, _ := getActualWildcardAndLocalHost(lb.node) - - // add an extra listener that binds to the port that is the recipient of the iptables redirect - ipTablesListener := &listener.Listener{ - Name: model.VirtualOutboundListenerName, - Address: util.BuildAddress(actualWildcard, uint32(lb.push.Mesh.ProxyListenPort)), - Transparent: isTransparentProxy, - UseOriginalDst: proto.BoolTrue, - FilterChains: filterChains, - TrafficDirection: core.TrafficDirection_OUTBOUND, - } - class := model.OutboundListenerClass(lb.node.Type) - accessLogBuilder.setListenerAccessLog(lb.push, lb.node, ipTablesListener, class) - lb.virtualOutboundListener = ipTablesListener - return lb -} - -func (lb *ListenerBuilder) patchOneListener(l *listener.Listener, ctx networking.EnvoyFilter_PatchContext) *listener.Listener { - if l == nil { - return nil - } - tempArray := []*listener.Listener{l} - tempArray = envoyfilter.ApplyListenerPatches(ctx, lb.envoyFilterWrapper, tempArray, true) - // temp array will either be empty [if virtual listener was removed] or will have a modified listener - if len(tempArray) == 0 { - return nil - } - return tempArray[0] -} - -func (lb *ListenerBuilder) patchListeners() { - lb.envoyFilterWrapper = lb.push.EnvoyFilters(lb.node) - if lb.envoyFilterWrapper == nil { - return - } - - if lb.node.Type == model.Router { - lb.gatewayListeners = envoyfilter.ApplyListenerPatches(networking.EnvoyFilter_GATEWAY, lb.envoyFilterWrapper, - lb.gatewayListeners, false) - return - } - - lb.virtualOutboundListener = lb.patchOneListener(lb.virtualOutboundListener, networking.EnvoyFilter_SIDECAR_OUTBOUND) - lb.virtualInboundListener = lb.patchOneListener(lb.virtualInboundListener, networking.EnvoyFilter_SIDECAR_INBOUND) - lb.inboundListeners = envoyfilter.ApplyListenerPatches(networking.EnvoyFilter_SIDECAR_INBOUND, lb.envoyFilterWrapper, lb.inboundListeners, false) - lb.outboundListeners = envoyfilter.ApplyListenerPatches(networking.EnvoyFilter_SIDECAR_OUTBOUND, lb.envoyFilterWrapper, lb.outboundListeners, false) -} - -func (lb *ListenerBuilder) getListeners() []*listener.Listener { - if lb.node.Type == model.SidecarProxy { - nInbound, nOutbound := len(lb.inboundListeners), len(lb.outboundListeners) - nHTTPProxy, nVirtual := 0, 0 - if lb.httpProxyListener != nil { - nHTTPProxy = 1 - } - if lb.virtualOutboundListener != nil { - nVirtual = 1 - } - - nListener := nInbound + nOutbound + nHTTPProxy + nVirtual - - listeners := make([]*listener.Listener, 0, nListener) - listeners = append(listeners, lb.outboundListeners...) - if lb.httpProxyListener != nil { - listeners = append(listeners, lb.httpProxyListener) - } - if lb.virtualOutboundListener != nil { - listeners = append(listeners, lb.virtualOutboundListener) - } - listeners = append(listeners, lb.inboundListeners...) - - log.Debugf("Build %d listeners for node %s including %d outbound, %d http proxy, "+ - "%d virtual outbound", - nListener, - lb.node.ID, - nOutbound, - nHTTPProxy, - nVirtual, - ) - return listeners - } - - return lb.gatewayListeners -} - -func buildOutboundCatchAllNetworkFiltersOnly(push *model.PushContext, node *model.Proxy) []*listener.Filter { - var egressCluster string - - if util.IsAllowAnyOutbound(node) { - // We need a passthrough filter to fill in the filter stack for orig_dst listener - egressCluster = util.PassthroughCluster - - // no need to check for nil value as the previous if check has checked - if node.SidecarScope.OutboundTrafficPolicy.EgressProxy != nil { - // user has provided an explicit destination for all the unknown traffic. - // build a cluster out of this destination - egressCluster = istio_route.GetDestinationCluster(node.SidecarScope.OutboundTrafficPolicy.EgressProxy, - nil, 0) - } - } else { - egressCluster = util.BlackHoleCluster - } - idleTimeoutDuration, err := time.ParseDuration(node.Metadata.IdleTimeout) - if err != nil { - idleTimeoutDuration = 0 - } - - tcpProxy := &tcp.TcpProxy{ - StatPrefix: egressCluster, - ClusterSpecifier: &tcp.TcpProxy_Cluster{Cluster: egressCluster}, - IdleTimeout: durationpb.New(idleTimeoutDuration), - } - filterStack := buildMetricsNetworkFilters(push, node, istionetworking.ListenerClassSidecarOutbound) - accessLogBuilder.setTCPAccessLog(push, node, tcpProxy, istionetworking.ListenerClassSidecarOutbound) - filterStack = append(filterStack, &listener.Filter{ - Name: wellknown.TCPProxy, - ConfigType: &listener.Filter_TypedConfig{TypedConfig: util.MessageToAny(tcpProxy)}, - }) - - return filterStack -} - -// TODO: This code is still insufficient. Ideally we should be parsing all the virtual services -// with TLS blocks and build the appropriate filter chain matches and routes here. And then finally -// evaluate the left over unmatched TLS traffic using allow_any or registry_only. -// See https://github.com/istio/istio/issues/21170 -func buildOutboundCatchAllNetworkFilterChains(node *model.Proxy, push *model.PushContext) []*listener.FilterChain { - filterStack := buildOutboundCatchAllNetworkFiltersOnly(push, node) - chains := make([]*listener.FilterChain, 0, 2) - chains = append(chains, blackholeFilterChain(push, node), &listener.FilterChain{ - Name: model.VirtualOutboundCatchAllTCPFilterChainName, - Filters: filterStack, - }) - return chains -} - -func blackholeFilterChain(push *model.PushContext, node *model.Proxy) *listener.FilterChain { - return &listener.FilterChain{ - Name: model.VirtualOutboundBlackholeFilterChainName, - FilterChainMatch: &listener.FilterChainMatch{ - // We should not allow requests to the listen port directly. Requests must be - // sent to some other original port and iptables redirected to 15001. This - // ensures we do not passthrough back to the listen port. - DestinationPort: &wrappers.UInt32Value{Value: uint32(push.Mesh.ProxyListenPort)}, - }, - Filters: append( - buildMetricsNetworkFilters(push, node, istionetworking.ListenerClassSidecarOutbound), - &listener.Filter{ - Name: wellknown.TCPProxy, - ConfigType: &listener.Filter_TypedConfig{TypedConfig: util.MessageToAny(&tcp.TcpProxy{ - StatPrefix: util.BlackHoleCluster, - ClusterSpecifier: &tcp.TcpProxy_Cluster{Cluster: util.BlackHoleCluster}, - })}, - }, - ), - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/listener_builder_test.go b/pilot/pkg/networking/core/v1alpha3/listener_builder_test.go deleted file mode 100644 index 1ef5aaab3..000000000 --- a/pilot/pkg/networking/core/v1alpha3/listener_builder_test.go +++ /dev/null @@ -1,775 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "fmt" - "reflect" - "strings" - "testing" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "github.com/golang/protobuf/jsonpb" // nolint: staticcheck - "google.golang.org/protobuf/types/known/structpb" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -func TestVirtualListenerBuilder(t *testing.T) { - cg := NewConfigGenTest(t, TestOptions{Services: testServices}) - proxy := cg.SetupProxy(nil) - - vo := xdstest.ExtractListener(model.VirtualOutboundListenerName, cg.Listeners(proxy)) - if vo == nil { - t.Fatalf("didn't find virtual outbound listener") - } -} - -var ( - testServices = []*model.Service{buildService("test.com", wildcardIP, protocol.HTTP, tnow)} - testServicesWithQUIC = []*model.Service{ - buildService("test.com", wildcardIP, protocol.HTTP, tnow), - buildService("quick.com", wildcardIP, protocol.UDP, tnow), - } -) - -func buildListeners(t *testing.T, o TestOptions, p *model.Proxy) []*listener.Listener { - cg := NewConfigGenTest(t, o) - // Hack up some instances for each Service - for _, s := range o.Services { - i := &model.ServiceInstance{ - Service: s, - Endpoint: &model.IstioEndpoint{ - Address: "1.1.1.1", - EndpointPort: 8080, - }, - ServicePort: s.Ports[0], - } - cg.MemRegistry.AddInstance(s.Hostname, i) - } - l := cg.Listeners(cg.SetupProxy(p)) - xdstest.ValidateListeners(t, l) - return l -} - -func TestVirtualInboundListenerBuilder(t *testing.T) { - tests := []struct { - useExactBalance bool - }{ - { - useExactBalance: false, - }, - { - useExactBalance: true, - }, - } - - for _, tt := range tests { - proxy := &model.Proxy{ - Metadata: &model.NodeMetadata{ - InboundListenerExactBalance: model.StringBool(tt.useExactBalance), - OutboundListenerExactBalance: model.StringBool(tt.useExactBalance), - }, - } - listeners := buildListeners(t, TestOptions{Services: testServices}, proxy) - if vo := xdstest.ExtractListener(model.VirtualOutboundListenerName, listeners); vo == nil { - t.Fatalf("expect virtual listener, found %s", listeners[0].Name) - } - vi := xdstest.ExtractListener(model.VirtualInboundListenerName, listeners) - if vi == nil { - t.Fatalf("expect virtual inbound listener, found %s", listeners[0].Name) - } - - byListenerName := map[string]int{} - - for _, fc := range vi.FilterChains { - byListenerName[fc.Name]++ - } - - for k, v := range byListenerName { - if k == model.VirtualInboundListenerName && v != 3 { - t.Fatalf("expect virtual listener has 3 passthrough filter chains, found %d", v) - } - if k == model.VirtualInboundCatchAllHTTPFilterChainName && v != 2 { - t.Fatalf("expect virtual listener has 2 passthrough filter chains, found %d", v) - } - } - - if tt.useExactBalance { - if vi.ConnectionBalanceConfig == nil || vi.ConnectionBalanceConfig.GetExactBalance() == nil { - t.Fatal("expected virtual listener to have connection balance config set to exact_balance") - } - } else { - if vi.ConnectionBalanceConfig != nil { - t.Fatal("expected virtual listener to not have connection balance config set") - } - } - } -} - -func TestVirtualInboundHasPassthroughClusters(t *testing.T) { - listeners := buildListeners(t, TestOptions{Services: testServices}, nil) - l := xdstest.ExtractListener(model.VirtualInboundListenerName, listeners) - if l == nil { - t.Fatalf("failed to find virtual inbound listener") - } - sawIpv4PassthroughCluster := 0 - sawIpv6PassthroughCluster := false - sawIpv4PassthroughFilterChainMatchTLSFromFakePlugin := false - for _, fc := range l.FilterChains { - if fc.TransportSocket != nil && fc.FilterChainMatch.TransportProtocol != "tls" { - t.Fatalf("expect passthrough filter chain sets transport protocol to tls if transport socket is set") - } - - if f := getTCPFilter(fc); f != nil && fc.Name == model.VirtualInboundListenerName { - if ipLen := len(fc.FilterChainMatch.PrefixRanges); ipLen != 1 { - t.Fatalf("expect passthrough filter chain has 1 ip address, found %d", ipLen) - } - - if fc.TransportSocket != nil { - sawIpv4PassthroughFilterChainMatchTLSFromFakePlugin = true - } - if fc.FilterChainMatch.PrefixRanges[0].AddressPrefix == util.ConvertAddressToCidr("0.0.0.0/0").AddressPrefix && - fc.FilterChainMatch.PrefixRanges[0].PrefixLen.Value == 0 { - if sawIpv4PassthroughCluster == 3 { - t.Fatalf("duplicated ipv4 passthrough cluster filter chain in listener %v", l) - } - sawIpv4PassthroughCluster++ - } else if fc.FilterChainMatch.PrefixRanges[0].AddressPrefix == util.ConvertAddressToCidr("::0/0").AddressPrefix && - fc.FilterChainMatch.PrefixRanges[0].PrefixLen.Value == 0 { - if sawIpv6PassthroughCluster { - t.Fatalf("duplicated ipv6 passthrough cluster filter chain in listener %v", l) - } - sawIpv6PassthroughCluster = true - } - } - - if f := getHTTPFilter(fc); f != nil && fc.Name == model.VirtualInboundCatchAllHTTPFilterChainName { - if fc.TransportSocket != nil && !reflect.DeepEqual(fc.FilterChainMatch.ApplicationProtocols, mtlsHTTPALPNs) { - t.Fatalf("expect %v application protocols, found %v", mtlsHTTPALPNs, fc.FilterChainMatch.ApplicationProtocols) - } - - if fc.TransportSocket == nil && !reflect.DeepEqual(fc.FilterChainMatch.ApplicationProtocols, plaintextHTTPALPNs) { - t.Fatalf("expect %v application protocols, found %v", plaintextHTTPALPNs, fc.FilterChainMatch.ApplicationProtocols) - } - } - } - - if sawIpv4PassthroughCluster != 3 { - t.Fatalf("fail to find the ipv4 passthrough filter chain in listener, got %v: %v", sawIpv4PassthroughCluster, xdstest.Dump(t, l)) - } - - if !sawIpv4PassthroughFilterChainMatchTLSFromFakePlugin { - t.Fatalf("fail to find the fake plugin filter chain match with TLS in listener %v", l) - } - - if len(l.ListenerFilters) != 3 { - t.Fatalf("expected %d listener filters, found %d", 3, len(l.ListenerFilters)) - } - - if l.ListenerFilters[0].Name != wellknown.OriginalDestination || - l.ListenerFilters[1].Name != wellknown.TlsInspector || - l.ListenerFilters[2].Name != wellknown.HttpInspector { - t.Fatalf("expect listener filters [%q, %q, %q], found [%q, %q, %q]", - wellknown.OriginalDestination, wellknown.TlsInspector, wellknown.HttpInspector, - l.ListenerFilters[0].Name, l.ListenerFilters[1].Name, l.ListenerFilters[2].Name) - } -} - -func TestSidecarInboundListenerWithOriginalSrc(t *testing.T) { - proxy := &model.Proxy{ - Metadata: &model.NodeMetadata{InterceptionMode: model.InterceptionTproxy}, - } - listeners := buildListeners(t, TestOptions{Services: testServices}, proxy) - l := xdstest.ExtractListener(model.VirtualInboundListenerName, listeners) - if l == nil { - t.Fatalf("failed to find virtual inbound listener") - } - if _, f := xdstest.ExtractListenerFilters(l)[wellknown.OriginalSource]; !f { - t.Fatalf("missing %v filter", wellknown.OriginalSource) - } -} - -// TestSidecarInboundListenerWithQUICConnectionBalance should not set -// exact_balance for the virtualInbound listener as QUIC uses UDP -// and this works only over TCP -func TestSidecarInboundListenerWithQUICAndExactBalance(t *testing.T) { - proxy := &model.Proxy{ - Metadata: &model.NodeMetadata{ - InboundListenerExactBalance: true, - OutboundListenerExactBalance: true, - }, - } - listeners := buildListeners(t, TestOptions{Services: testServicesWithQUIC}, proxy) - l := xdstest.ExtractListener(model.VirtualInboundListenerName, listeners) - if l == nil { - t.Fatalf("failed to find virtual inbound listener") - } - if l.ConnectionBalanceConfig == nil || l.ConnectionBalanceConfig.GetExactBalance() == nil { - t.Fatal("expected listener to have exact_balance set, but was empty") - } -} - -func TestListenerBuilderPatchListeners(t *testing.T) { - configPatches := []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_LISTENER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_ADD, - Value: buildPatchStruct(`{"name":"new-outbound-listener"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_LISTENER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_ADD, - Value: buildPatchStruct(`{"name":"new-inbound-listener"}`), - }, - }, - { - ApplyTo: networking.EnvoyFilter_LISTENER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_GATEWAY, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_ADD, - Value: buildPatchStruct(`{"name":"new-gateway-listener"}`), - }, - }, - - { - ApplyTo: networking.EnvoyFilter_LISTENER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_OUTBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 81, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - { - ApplyTo: networking.EnvoyFilter_LISTENER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_SIDECAR_INBOUND, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 82, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - { - ApplyTo: networking.EnvoyFilter_LISTENER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Context: networking.EnvoyFilter_GATEWAY, - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - PortNumber: 83, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - } - cg := NewConfigGenTest(t, TestOptions{Configs: getEnvoyFilterConfigs(configPatches)}) - - gatewayProxy := cg.SetupProxy(&model.Proxy{Type: model.Router, ConfigNamespace: "not-default"}) - sidecarProxy := cg.SetupProxy(&model.Proxy{ConfigNamespace: "not-default"}) - type fields struct { - gatewayListeners []*listener.Listener - inboundListeners []*listener.Listener - outboundListeners []*listener.Listener - httpProxyListener *listener.Listener - virtualOutboundListener *listener.Listener - virtualInboundListener *listener.Listener - } - tests := []struct { - name string - proxy *model.Proxy - fields fields - want fields - }{ - { - name: "patch add inbound and outbound listener", - proxy: sidecarProxy, - fields: fields{ - outboundListeners: []*listener.Listener{ - { - Name: "outbound-listener", - }, - }, - }, - want: fields{ - inboundListeners: []*listener.Listener{ - { - Name: "new-inbound-listener", - }, - }, - - outboundListeners: []*listener.Listener{ - { - Name: "outbound-listener", - }, - { - Name: "new-outbound-listener", - }, - }, - }, - }, - { - name: "patch inbound and outbound listener", - proxy: sidecarProxy, - fields: fields{ - outboundListeners: []*listener.Listener{ - { - Name: "outbound-listener", - }, - { - Name: "remove-outbound", - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 81, - }, - }, - }, - }, - }, - }, - }, - want: fields{ - inboundListeners: []*listener.Listener{ - { - Name: "new-inbound-listener", - }, - }, - - outboundListeners: []*listener.Listener{ - { - Name: "outbound-listener", - }, - { - Name: "new-outbound-listener", - }, - }, - }, - }, - { - name: "patch add gateway listener", - proxy: gatewayProxy, - fields: fields{ - gatewayListeners: []*listener.Listener{ - { - Name: "gateway-listener", - }, - }, - }, - want: fields{ - gatewayListeners: []*listener.Listener{ - { - Name: "gateway-listener", - }, - { - Name: "new-gateway-listener", - }, - }, - }, - }, - - { - name: "patch gateway listener", - proxy: gatewayProxy, - fields: fields{ - gatewayListeners: []*listener.Listener{ - { - Name: "gateway-listener", - }, - { - Name: "remove-gateway", - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 83, - }, - }, - }, - }, - }, - }, - }, - want: fields{ - gatewayListeners: []*listener.Listener{ - { - Name: "gateway-listener", - }, - { - Name: "new-gateway-listener", - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - lb := &ListenerBuilder{ - node: tt.proxy, - push: cg.PushContext(), - gatewayListeners: tt.fields.gatewayListeners, - inboundListeners: tt.fields.inboundListeners, - outboundListeners: tt.fields.outboundListeners, - httpProxyListener: tt.fields.httpProxyListener, - virtualOutboundListener: tt.fields.virtualOutboundListener, - virtualInboundListener: tt.fields.virtualInboundListener, - } - - lb.patchListeners() - got := fields{ - gatewayListeners: lb.gatewayListeners, - inboundListeners: lb.inboundListeners, - outboundListeners: lb.outboundListeners, - httpProxyListener: lb.httpProxyListener, - virtualOutboundListener: lb.virtualOutboundListener, - virtualInboundListener: lb.virtualInboundListener, - } - - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Unexpected default listener, want \n%#v got \n%#v", tt.want, got) - } - }) - } -} - -func buildPatchStruct(config string) *structpb.Struct { - val := &structpb.Struct{} - _ = jsonpb.Unmarshal(strings.NewReader(config), val) - return val -} - -func getEnvoyFilterConfigs(configPatches []*networking.EnvoyFilter_EnvoyConfigObjectPatch) []config.Config { - res := []config.Config{} - for i, cp := range configPatches { - res = append(res, config.Config{ - Meta: config.Meta{ - Name: fmt.Sprintf("test-envoyfilter-%d", i), - Namespace: "not-default", - GroupVersionKind: gvk.EnvoyFilter, - }, - Spec: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{cp}, - }, - }) - } - return res -} - -const strictMode = ` -apiVersion: security.istio.io/v1beta1 -kind: PeerAuthentication -metadata: - name: default - namespace: dubbo-system -spec: - mtls: - mode: STRICT -` - -const disableMode = ` -apiVersion: security.istio.io/v1beta1 -kind: PeerAuthentication -metadata: - name: default - namespace: dubbo-system -spec: - mtls: - mode: DISABLE -` - -func TestInboundListenerFilters(t *testing.T) { - services := []*model.Service{ - buildServiceWithPort("test1.com", 80, protocol.HTTP, tnow), - buildServiceWithPort("test2.com", 81, protocol.Unsupported, tnow), - buildServiceWithPort("test3.com", 82, protocol.TCP, tnow), - } - instances := make([]*model.ServiceInstance, 0, len(services)) - for _, s := range services { - instances = append(instances, &model.ServiceInstance{ - Service: s, - Endpoint: &model.IstioEndpoint{ - EndpointPort: uint32(s.Ports[0].Port), - Address: "1.1.1.1", - }, - ServicePort: s.Ports[0], - }) - } - cases := []struct { - name string - config string - http map[int]bool - tls map[int]bool - }{ - { - name: "permissive", - config: "", - http: map[int]bool{ - // Should not see HTTP inspector if we declare ports - 80: true, - 82: true, - // But should see for passthrough or unnamed ports - 81: false, - 1000: false, - }, - tls: map[int]bool{ - // Permissive mode: inspector is set everywhere - 80: false, - 82: false, - 81: false, - 1000: false, - }, - }, - { - name: "disable", - config: disableMode, - http: map[int]bool{ - // Should not see HTTP inspector if we declare ports - 80: true, - 82: true, - // But should see for passthrough or unnamed ports - 81: false, - 1000: false, - }, - }, - { - name: "strict", - config: strictMode, - http: map[int]bool{ - // Should not see HTTP inspector if we declare ports - 80: true, - 82: true, - // This is 'auto', but for STRICT we always get requests over TLS so HTTP inspector is not in play - 81: true, - // Even for passthrough, we do not need HTTP inspector because it is handled by TLS inspector - 1000: true, - }, - tls: map[int]bool{ - // strict mode: inspector is set everywhere. - 80: false, - 82: false, - 81: false, - 1000: false, - }, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - cg := NewConfigGenTest(t, TestOptions{ - Services: services, - Instances: instances, - ConfigString: tt.config, - }) - listeners := cg.Listeners(cg.SetupProxy(nil)) - virtualInbound := xdstest.ExtractListener("virtualInbound", listeners) - filters := xdstest.ExtractListenerFilters(virtualInbound) - evaluateListenerFilterPredicates(t, filters[wellknown.HttpInspector].GetFilterDisabled(), tt.http) - if filters[wellknown.TlsInspector] == nil { - if len(tt.tls) > 0 { - t.Fatalf("Expected tls inspector, got none") - } - } else { - evaluateListenerFilterPredicates(t, filters[wellknown.TlsInspector].FilterDisabled, tt.tls) - } - }) - } -} - -func evaluateListenerFilterPredicates(t testing.TB, predicate *listener.ListenerFilterChainMatchPredicate, expected map[int]bool) { - t.Helper() - for port, expect := range expected { - got := xdstest.EvaluateListenerFilterPredicates(predicate, port) - if got != expect { - t.Errorf("expected port %v to have match=%v, got match=%v", port, expect, got) - } - } -} - -func TestSidecarInboundListenerFilters(t *testing.T) { - services := []*model.Service{buildServiceWithPort("test.com", 80, protocol.HTTPS, tnow)} - - expectIstioMTLS := func(t test.Failer, filterChain *listener.FilterChain) { - tlsContext := &tls.DownstreamTlsContext{} - if err := filterChain.GetTransportSocket().GetTypedConfig().UnmarshalTo(tlsContext); err != nil { - t.Fatal(err) - } - commonTLSContext := tlsContext.CommonTlsContext - if len(commonTLSContext.TlsCertificateSdsSecretConfigs) == 0 { - t.Fatal("expected tls certificates") - } - if commonTLSContext.TlsCertificateSdsSecretConfigs[0].Name != "default" { - t.Fatalf("expected certificate default, actual %s", - commonTLSContext.TlsCertificates[0].CertificateChain.String()) - } - } - instances := make([]*model.ServiceInstance, 0, len(services)) - for _, s := range services { - instances = append(instances, &model.ServiceInstance{ - Service: s, - Endpoint: &model.IstioEndpoint{ - EndpointPort: uint32(s.Ports[0].Port), - Address: "1.1.1.1", - }, - ServicePort: s.Ports[0], - }) - } - cases := []struct { - name string - sidecarScope *model.SidecarScope - mtlsMode model.MutualTLSMode - expectedResult func(t test.Failer, filterChain *listener.FilterChain) - }{ - { - name: "simulate peer auth disabled on port 80", - sidecarScope: &model.SidecarScope{ - Sidecar: &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{Name: "https-port", Protocol: "https", Number: 80}, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - ServerCertificate: "cert.pem", - PrivateKey: "privatekey.pem", - }, - }, - }, - }, - }, - mtlsMode: model.MTLSDisable, - expectedResult: func(t test.Failer, filterChain *listener.FilterChain) { - tlsContext := &tls.DownstreamTlsContext{} - if err := filterChain.GetTransportSocket().GetTypedConfig().UnmarshalTo(tlsContext); err != nil { - t.Fatal(err) - } - commonTLSContext := tlsContext.CommonTlsContext - if len(commonTLSContext.TlsCertificateSdsSecretConfigs) == 0 { - t.Fatal("expected tls certificates") - } - if commonTLSContext.TlsCertificateSdsSecretConfigs[0].Name != "file-cert:cert.pem~privatekey.pem" { - t.Fatalf("expected certificate httpbin.pem, actual %s", - commonTLSContext.TlsCertificates[0].CertificateChain.String()) - } - if tlsContext.RequireClientCertificate.Value { - t.Fatalf("expected RequireClientCertificate to be false") - } - }, - }, - { - name: "simulate peer auth strict", - sidecarScope: &model.SidecarScope{ - Sidecar: &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{Name: "https-port", Protocol: "https", Number: 80}, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - ServerCertificate: "cert.pem", - PrivateKey: "privatekey.pem", - }, - }, - }, - }, - }, - mtlsMode: model.MTLSStrict, - expectedResult: expectIstioMTLS, - }, - { - name: "simulate peer auth permissive", - sidecarScope: &model.SidecarScope{ - Sidecar: &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{Name: "https-port", Protocol: "https", Number: 80}, - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - ServerCertificate: "cert.pem", - PrivateKey: "privatekey.pem", - }, - }, - }, - }, - }, - mtlsMode: model.MTLSPermissive, - expectedResult: expectIstioMTLS, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - cg := NewConfigGenTest(t, TestOptions{ - Services: services, - Instances: instances, - ConfigString: mtlsMode(tt.mtlsMode.String()), - }) - proxy := cg.SetupProxy(nil) - proxy.Metadata = &model.NodeMetadata{Labels: map[string]string{"app": "foo"}} - proxy.SidecarScope = tt.sidecarScope - test.SetBoolForTest(t, &features.EnableTLSOnSidecarIngress, true) - listeners := cg.Listeners(proxy) - virtualInbound := xdstest.ExtractListener("virtualInbound", listeners) - filterChain := xdstest.ExtractFilterChain("1.1.1.1_80", virtualInbound) - tt.expectedResult(t, filterChain) - }) - } -} - -func mtlsMode(m string) string { - return fmt.Sprintf(`apiVersion: security.istio.io/v1beta1 -kind: PeerAuthentication -metadata: - name: default - namespace: dubbo-system -spec: - mtls: - mode: %s -`, m) -} diff --git a/pilot/pkg/networking/core/v1alpha3/listener_inbound.go b/pilot/pkg/networking/core/v1alpha3/listener_inbound.go deleted file mode 100644 index feeddd8d7..000000000 --- a/pilot/pkg/networking/core/v1alpha3/listener_inbound.go +++ /dev/null @@ -1,709 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "fmt" - "sort" - "time" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - tcp "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3" - envoytype "github.com/envoyproxy/go-control-plane/envoy/type/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "github.com/golang/protobuf/ptypes/duration" - "google.golang.org/protobuf/types/known/durationpb" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - istionetworking "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/plugin" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/telemetry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - xdsfilters "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/filters" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/proto" -) - -// inboundChainConfig defines the configuration for a single inbound filter chain. This may be created -// as a result of a Service, and Sidecar CR, or for the built-in passthrough filter chains. -type inboundChainConfig struct { - // clusterName defines the destination cluster for this chain - clusterName string - // port defines the port configuration for this chain. Note that there is a Port and TargetPort; - // most usages should just use TargetPort. Port is mostly used for legacy compatibility and - // telemetry. - port ServiceInstancePort - // bind determines where (IP) this filter chain should bind. Note: typically we just end up using - // 'virtual' listener and do not literally bind to port; in these cases this just impacts naming - // and telemetry. - bind string - - // tlsSettings defines the *custom* TLS settings for the chain. mTLS settings are orthogonal; this - // only configures TLS overrides. - tlsSettings *networking.ServerTLSSettings - - // passthrough should be set to true for the 'passthrough' chains, which are the chains always - // present to handle all unmatched traffic. These have a few naming and quirks that require - // different configuration. - passthrough bool - - // bindToPort determines if this chain should form a real listener that actually binds to a real port, - // or if it should just be a filter chain part of the 'virtual inbound' listener. - bindToPort bool - - // telemetryMetadata defines additional information about the chain for telemetry purposes. - telemetryMetadata telemetry.FilterChainMetadata -} - -// StatPrefix returns the stat prefix for the config -func (cc inboundChainConfig) StatPrefix() string { - if cc.passthrough { - // A bit arbitrary, but for backwards compatibility just use the cluster name - return cc.clusterName - } - return "inbound_" + cc.Name(istionetworking.ListenerProtocolHTTP) -} - -// Name determines the name for this chain -func (cc inboundChainConfig) Name(protocol istionetworking.ListenerProtocol) string { - if cc.passthrough { - // A bit arbitrary, but for backwards compatibility fixed names are used - // Passthrough chains have fix names based on protocol - if protocol == istionetworking.ListenerProtocolHTTP { - return model.VirtualInboundCatchAllHTTPFilterChainName - } - return model.VirtualInboundListenerName - } - // Everything else derived from bind/port - return getListenerName(cc.bind, int(cc.port.TargetPort), istionetworking.TransportProtocolTCP) -} - -var ( - IPv4PasstrhoughCIDR = []*core.CidrRange{util.ConvertAddressToCidr("0.0.0.0/0")} - IPv6PasstrhoughCIDR = []*core.CidrRange{util.ConvertAddressToCidr("::/0")} -) - -// ToFilterChainMatch builds the FilterChainMatch for the config -func (cc inboundChainConfig) ToFilterChainMatch(opt FilterChainMatchOptions) *listener.FilterChainMatch { - match := &listener.FilterChainMatch{} - match.ApplicationProtocols = opt.ApplicationProtocols - match.TransportProtocol = opt.TransportProtocol - if cc.passthrough { - // Pasthrough listeners do an IP match - but matching all IPs. This is really an IP *version* match, - // but Envoy doesn't explicitly have version check. - if cc.clusterName == util.InboundPassthroughClusterIpv4 { - match.PrefixRanges = IPv4PasstrhoughCIDR - } else { - match.PrefixRanges = IPv6PasstrhoughCIDR - } - } - if cc.port.TargetPort > 0 { - match.DestinationPort = &wrappers.UInt32Value{Value: cc.port.TargetPort} - } - return match -} - -// ServiceInstancePort defines a port that has both a port and targetPort (which distinguishes it from model.Port) -// Note: ServiceInstancePort only makes sense in the context of a specific ServiceInstance, because TargetPort depends on a specific instance. -type ServiceInstancePort struct { - // Name ascribes a human readable name for the port object. When a - // service has multiple ports, the name field is mandatory - Name string `json:"name,omitempty"` - - // Port number where the service can be reached. Does not necessarily - // map to the corresponding port numbers for the instances behind the - // service. - Port uint32 `json:"port"` - - TargetPort uint32 `json:"targetPort"` - - // Protocol to be used for the port. - Protocol protocol.Instance `json:"protocol,omitempty"` -} - -// buildInboundListeners creates inbound listeners. -// Typically, this a single listener with many filter chains for each applicable Service; traffic is redirect with iptables. -// However, explicit listeners can be used in NONE mode or with Sidecar.Ingress configuration. -func (lb *ListenerBuilder) buildInboundListeners() []*listener.Listener { - // All listeners we build - var listeners []*listener.Listener - // virtualInboundFilterChains builds up all of the filter chains for the virtual inbound listener - var virtualInboundFilterChains []*listener.FilterChain - // For each chain config we will build required filter chain(s) - for _, cc := range lb.buildInboundChainConfigs() { - // First, construct our set of filter chain matchers. For a given port, we will have multiple matches - // to handle mTLS vs plaintext and HTTP vs TCP (depending on protocol and PeerAuthentication). - var opts []FilterChainMatchOptions - mtls := lb.authnBuilder.ForPort(cc.port.TargetPort) - // Chain has explicit user TLS config. This can only apply when the mTLS settings are DISABLE to avoid conflicts - if cc.tlsSettings != nil && mtls.Mode == model.MTLSDisable { - // Since we are terminating TLS, we need to treat the protocol as if its terminated. - // Example: user specifies protocol=HTTPS and user TLS, we will use HTTP - cc.port.Protocol = cc.port.Protocol.AfterTLSTermination() - lp := istionetworking.ModelProtocolToListenerProtocol(cc.port.Protocol, core.TrafficDirection_INBOUND) - opts = getTLSFilterChainMatchOptions(lp) - mtls.TCP = BuildListenerTLSContext(cc.tlsSettings, lb.node, istionetworking.TransportProtocolTCP) - mtls.HTTP = mtls.TCP - } else { - lp := istionetworking.ModelProtocolToListenerProtocol(cc.port.Protocol, core.TrafficDirection_INBOUND) - opts = getFilterChainMatchOptions(mtls, lp) - } - // Build the actual chain - chains := lb.inboundChainForOpts(cc, mtls, opts) - - if cc.bindToPort { - // If this config is for bindToPort, we want to actually create a real Listener. - listeners = append(listeners, lb.inboundCustomListener(cc, chains)) - } else { - // Otherwise, just append the filter chain to the virtual inbound chains. - virtualInboundFilterChains = append(virtualInboundFilterChains, chains...) - } - } - - if lb.node.GetInterceptionMode() != model.InterceptionNone { - // Prepend virtual inbound, as long as we are using redirection. - listeners = append([]*listener.Listener{lb.inboundVirtualListener(virtualInboundFilterChains)}, listeners...) - } - - return listeners -} - -// inboundVirtualListener builds the virtual inbound listener. -func (lb *ListenerBuilder) inboundVirtualListener(chains []*listener.FilterChain) *listener.Listener { - actualWildcard, _ := getActualWildcardAndLocalHost(lb.node) - - // Build the "virtual" inbound listener. This will capture all inbound redirected traffic and contains: - // * Passthrough filter chains, matching all unmatched traffic. There are a few of these to handle all cases - // * Service filter chains. These will either be for each Port exposed by a Service OR Sidecar.Ingress configuration. - allChains := buildInboundPassthroughChains(lb) - allChains = append(allChains, chains...) - return lb.buildInboundListener(model.VirtualInboundListenerName, util.BuildAddress(actualWildcard, ProxyInboundListenPort), false, allChains) -} - -// inboundCustomListener build a custom listener that actually binds to a port, rather than relying on redirection. -func (lb *ListenerBuilder) inboundCustomListener(cc inboundChainConfig, chains []*listener.FilterChain) *listener.Listener { - return lb.buildInboundListener(cc.Name(istionetworking.ListenerProtocolTCP), util.BuildAddress(cc.bind, cc.port.TargetPort), true, chains) -} - -func (lb *ListenerBuilder) buildInboundListener(name string, address *core.Address, bindToPort bool, chains []*listener.FilterChain) *listener.Listener { - l := &listener.Listener{ - Name: name, - Address: address, - TrafficDirection: core.TrafficDirection_INBOUND, - } - if lb.node.Metadata.InboundListenerExactBalance { - l.ConnectionBalanceConfig = &listener.Listener_ConnectionBalanceConfig{ - BalanceType: &listener.Listener_ConnectionBalanceConfig_ExactBalance_{ - ExactBalance: &listener.Listener_ConnectionBalanceConfig_ExactBalance{}, - }, - } - } - if !bindToPort && lb.node.GetInterceptionMode() == model.InterceptionTproxy { - l.Transparent = proto.BoolTrue - } - - accessLogBuilder.setListenerAccessLog(lb.push, lb.node, l, istionetworking.ListenerClassSidecarInbound) - l.FilterChains = chains - l.ListenerFilters = populateListenerFilters(lb.node, l, bindToPort) - l.ListenerFiltersTimeout = getProtocolDetectionTimeout(lb.push.Mesh) - l.ContinueOnListenerFiltersTimeout = true - return l -} - -// inboundChainForOpts builds a set of filter chains -func (lb *ListenerBuilder) inboundChainForOpts(cc inboundChainConfig, mtls plugin.MTLSSettings, opts []FilterChainMatchOptions) []*listener.FilterChain { - chains := make([]*listener.FilterChain, 0, len(opts)) - for _, opt := range opts { - switch opt.Protocol { - // Switch on the protocol. Note: we do not need to handle Auto protocol as it will already be split into a TCP and HTTP option. - case istionetworking.ListenerProtocolHTTP: - chains = append(chains, &listener.FilterChain{ - FilterChainMatch: cc.ToFilterChainMatch(opt), - Filters: lb.buildInboundNetworkFiltersForHTTP(cc), - TransportSocket: buildDownstreamTLSTransportSocket(opt.ToTransportSocket(mtls)), - Name: cc.Name(opt.Protocol), - }) - case istionetworking.ListenerProtocolTCP: - chains = append(chains, &listener.FilterChain{ - FilterChainMatch: cc.ToFilterChainMatch(opt), - Filters: lb.buildInboundNetworkFilters(cc), - TransportSocket: buildDownstreamTLSTransportSocket(opt.ToTransportSocket(mtls)), - Name: cc.Name(opt.Protocol), - }) - } - } - return chains -} - -// buildInboundChainConfigs builds all the application chain configs. -func (lb *ListenerBuilder) buildInboundChainConfigs() []inboundChainConfig { - chainsByPort := make(map[uint32]inboundChainConfig) - // No user supplied sidecar scope or the user supplied one has no ingress listeners. - if !lb.node.SidecarScope.HasIngressListener() { - // We will look at all Services that apply to this proxy and build chains for each distinct port. - // Note: this does mean that we may have multiple Services applying to the same port, which introduces a conflict - for _, i := range lb.node.ServiceInstances { - port := ServiceInstancePort{ - Name: i.ServicePort.Name, - Port: uint32(i.ServicePort.Port), - TargetPort: i.Endpoint.EndpointPort, - Protocol: i.ServicePort.Protocol, - } - - cc := inboundChainConfig{ - telemetryMetadata: telemetry.FilterChainMetadata{InstanceHostname: i.Service.Hostname}, - port: port, - clusterName: model.BuildInboundSubsetKey(int(port.TargetPort)), - bind: "0.0.0.0", // TODO ipv6 - bindToPort: getBindToPort(networking.CaptureMode_DEFAULT, lb.node), - } - if i.Service.Attributes.ServiceRegistry == provider.Kubernetes { - cc.telemetryMetadata.KubernetesServiceNamespace = i.Service.Attributes.Namespace - cc.telemetryMetadata.KubernetesServiceName = i.Service.Attributes.Name - } - // First, make sure there is a distinct instance used per port. - // The Service is *almost* not relevant, but some Telemetry is per-service. - // If there is a conflict, we will use the oldest Service. This impacts the protocol used as well. - if old, f := chainsByPort[port.TargetPort]; f { - reportInboundConflict(lb, old, cc) - continue - } - chainsByPort[port.TargetPort] = cc - } - } else { - for _, i := range lb.node.SidecarScope.Sidecar.Ingress { - port := ServiceInstancePort{ - Name: i.Port.Name, - Port: i.Port.Number, - TargetPort: i.Port.Number, // No targetPort support in the API - Protocol: protocol.Parse(i.Port.Protocol), - } - bindtoPort := getBindToPort(i.CaptureMode, lb.node) - // Skip ports we cannot bind to - if !lb.node.CanBindToPort(bindtoPort, port.TargetPort) { - log.Warnf("buildInboundListeners: skipping privileged sidecar port %d for node %s as it is an unprivileged proxy", - i.Port.Number, lb.node.ID) - continue - } - - cc := inboundChainConfig{ - // Sidecar config doesn't have a real hostname. In order to give some telemetry info, make a synthetic hostname. - telemetryMetadata: telemetry.FilterChainMetadata{ - InstanceHostname: host.Name(lb.node.SidecarScope.Name + "." + lb.node.SidecarScope.Namespace), - }, - port: port, - clusterName: model.BuildInboundSubsetKey(int(port.TargetPort)), - bind: i.Bind, - bindToPort: bindtoPort, - } - if cc.bind == "" { - // If user didn't provide, pick one based on IP - cc.bind = getSidecarInboundBindIP(lb.node) - } - // If there is a conflict, we will use the oldest Service. This impacts the protocol used as well. - if old, f := chainsByPort[port.TargetPort]; f { - reportInboundConflict(lb, old, cc) - continue - } - - if i.Tls != nil && features.EnableTLSOnSidecarIngress { - // User provided custom TLS settings - cc.tlsSettings = i.Tls.DeepCopy() - cc.tlsSettings.CipherSuites = filteredSidecarCipherSuites(cc.tlsSettings.CipherSuites) - cc.port.Protocol = cc.port.Protocol.AfterTLSTermination() - } - - chainsByPort[port.TargetPort] = cc - } - } - - chainConfigs := make([]inboundChainConfig, 0, len(chainsByPort)) - for _, cc := range chainsByPort { - chainConfigs = append(chainConfigs, cc) - } - // Give a stable order to the chains - sort.Slice(chainConfigs, func(i, j int) bool { - return chainConfigs[i].port.TargetPort < chainConfigs[j].port.TargetPort - }) - - return chainConfigs -} - -// getBindToPort determines whether we should bind to port based on the chain-specific config and the proxy -func getBindToPort(mode networking.CaptureMode, node *model.Proxy) bool { - if mode == networking.CaptureMode_DEFAULT { - // Chain doesn't specify explicit config, so use the proxy defaults - return node.GetInterceptionMode() == model.InterceptionNone - } - // Explicitly configured in the config, ignore proxy defaults - return mode == networking.CaptureMode_NONE -} - -func getProtocolDetectionTimeout(mesh *meshconfig.MeshConfig) *duration.Duration { - timeout := mesh.GetProtocolDetectionTimeout() - if features.InboundProtocolDetectionTimeoutSet { - timeout = durationpb.New(features.InboundProtocolDetectionTimeout) - } - return timeout -} - -// populateListenerFilters determines the appropriate listener filters based on the listener -// HTTP and TLS inspectors are automatically derived based on FilterChainMatch requirements. -func populateListenerFilters(node *model.Proxy, vi *listener.Listener, bindToPort bool) []*listener.ListenerFilter { - lf := make([]*listener.ListenerFilter, 0, 4) - if !bindToPort { - lf = append(lf, xdsfilters.OriginalDestination) - } - if !bindToPort && node.GetInterceptionMode() == model.InterceptionTproxy { - lf = append(lf, xdsfilters.OriginalSrc) - } - - // inspectors builds up a map of port -> required inspectors (TLS/HTTP) - inspectors := map[int]enabledInspector{} - for _, fc := range vi.FilterChains { - port := fc.GetFilterChainMatch().GetDestinationPort().GetValue() - needsTLS := fc.GetFilterChainMatch().GetTransportProtocol() == xdsfilters.TLSTransportProtocol - needHTTP := false - for _, ap := range fc.GetFilterChainMatch().GetApplicationProtocols() { - // Check for HTTP protocol - these require HTTP inspector - if ap == "http/1.1" || ap == "h2c" { - needHTTP = true - break - } - } - // Port may already have config; we OR them together. If any filter chain on that port is enabled - // we will enable the inspector. - i := inspectors[int(port)] - i.HTTPInspector = i.HTTPInspector || needHTTP - i.TLSInspector = i.TLSInspector || needsTLS - inspectors[int(port)] = i - } - - // Enable TLS inspector on any ports we need it - if needsTLS(inspectors) { - lf = append(lf, buildTLSInspector(inspectors)) - } - - // Note: the HTTP inspector should be after TLS inspector. - // If TLS inspector sets transport protocol to tls, the http inspector - // won't inspect the packet. - if features.EnableProtocolSniffingForInbound && needsHTTP(inspectors) { - lf = append(lf, buildHTTPInspector(inspectors)) - } - - return lf -} - -// listenerPredicateExcludePorts returns a listener filter predicate that will -// match everything except the passed in ports. This is useful, for example, to -// enable protocol sniffing on every port except port X and Y, because X and Y -// are explicitly declared. -func listenerPredicateExcludePorts(ports []int) *listener.ListenerFilterChainMatchPredicate { - ranges := []*listener.ListenerFilterChainMatchPredicate{} - for _, p := range ports { - ranges = append(ranges, &listener.ListenerFilterChainMatchPredicate{Rule: &listener.ListenerFilterChainMatchPredicate_DestinationPortRange{ - // Range is [start, end) - DestinationPortRange: &envoytype.Int32Range{ - Start: int32(p), - End: int32(p + 1), - }, - }}) - } - if len(ranges) > 1 { - return &listener.ListenerFilterChainMatchPredicate{Rule: &listener.ListenerFilterChainMatchPredicate_OrMatch{ - OrMatch: &listener.ListenerFilterChainMatchPredicate_MatchSet{ - Rules: ranges, - }, - }} - } - return &listener.ListenerFilterChainMatchPredicate{Rule: ranges[0].GetRule()} -} - -func listenerPredicateIncludePorts(ports []int) *listener.ListenerFilterChainMatchPredicate { - rule := listenerPredicateExcludePorts(ports) - return &listener.ListenerFilterChainMatchPredicate{Rule: &listener.ListenerFilterChainMatchPredicate_NotMatch{ - NotMatch: rule, - }} -} - -func needsTLS(inspectors map[int]enabledInspector) bool { - for _, i := range inspectors { - if i.TLSInspector { - return true - } - } - return false -} - -func needsHTTP(inspectors map[int]enabledInspector) bool { - for _, i := range inspectors { - if i.HTTPInspector { - return true - } - } - return false -} - -// buildTLSInspector creates a tls inspector filter. Based on the configured ports, this may be enabled -// for only some ports. -func buildTLSInspector(inspectors map[int]enabledInspector) *listener.ListenerFilter { - // TODO share logic with HTTP inspector - defaultEnabled := inspectors[0].TLSInspector - - // We have a split path here based on if the passthrough inspector is enabled - // If it is, then we need to explicitly opt ports out of the inspector - // If it isn't, then we need to explicitly opt ports into the inspector - if defaultEnabled { - ports := make([]int, 0, len(inspectors)) - // Collect all ports where TLS inspector is disabled. - for p, i := range inspectors { - if p == 0 { - continue - } - if !i.TLSInspector { - ports = append(ports, p) - } - } - // No need to filter, return the cached version enabled for all ports - if len(ports) == 0 { - return xdsfilters.TLSInspector - } - // Ensure consistent ordering as we are looping over a map - sort.Ints(ports) - filter := &listener.ListenerFilter{ - Name: wellknown.TlsInspector, - ConfigType: xdsfilters.TLSInspector.ConfigType, - FilterDisabled: listenerPredicateExcludePorts(ports), - } - return filter - } - ports := make([]int, 0, len(inspectors)) - // Collect all ports where TLS inspector is disabled. - for p, i := range inspectors { - if p == 0 { - continue - } - if i.TLSInspector { - ports = append(ports, p) - } - } - // No need to filter, return the cached version enabled for all ports - if len(ports) == 0 { - return xdsfilters.TLSInspector - } - // Ensure consistent ordering as we are looping over a map - sort.Ints(ports) - filter := &listener.ListenerFilter{ - Name: wellknown.TlsInspector, - ConfigType: xdsfilters.TLSInspector.ConfigType, - // Exclude all disabled ports - FilterDisabled: listenerPredicateIncludePorts(ports), - } - return filter -} - -// buildHTTPInspector creates an http inspector filter. Based on the configured ports, this may be enabled -// for only some ports. -func buildHTTPInspector(inspectors map[int]enabledInspector) *listener.ListenerFilter { - ports := make([]int, 0, len(inspectors)) - // Collect all ports where HTTP inspector is disabled. - for p, i := range inspectors { - if !i.HTTPInspector { - ports = append(ports, p) - } - } - // No need to filter, return the cached version enabled for all ports - if len(ports) == 0 { - return xdsfilters.HTTPInspector - } - // Ensure consistent ordering as we are looping over a map - sort.Ints(ports) - filter := &listener.ListenerFilter{ - Name: wellknown.HttpInspector, - ConfigType: xdsfilters.HTTPInspector.ConfigType, - // Exclude all disabled ports - FilterDisabled: listenerPredicateExcludePorts(ports), - } - return filter -} - -func reportInboundConflict(lb *ListenerBuilder, old inboundChainConfig, cc inboundChainConfig) { - // If the protocols and service do not match, we have a real conflict. For example, one Service may - // define TCP and the other HTTP. Report this up to the user. - if old.port.Protocol != cc.port.Protocol && old.telemetryMetadata.InstanceHostname != cc.telemetryMetadata.InstanceHostname { - lb.push.AddMetric(model.ProxyStatusConflictInboundListener, lb.node.ID, lb.node.ID, - fmt.Sprintf("Conflicting inbound listener:%d. existing: %s, incoming: %s", cc.port.TargetPort, - old.telemetryMetadata.InstanceHostname, cc.telemetryMetadata.InstanceHostname)) - return - } - // This can happen if two services select the same pod with same port and protocol - we should skip - // building listener again, but no need to report to the user - if old.telemetryMetadata.InstanceHostname != cc.telemetryMetadata.InstanceHostname { - log.Debugf("skipping inbound listener:%d as we have already build it for existing host: %s, new host: %s", - cc.port.TargetPort, - old.telemetryMetadata.InstanceHostname, cc.telemetryMetadata.InstanceHostname) - } -} - -// buildInboundPassthroughChains builds the passthrough chains. These match any unmatched traffic. -// This allows traffic to ports not exposed by any Service, for example. -func buildInboundPassthroughChains(lb *ListenerBuilder) []*listener.FilterChain { - // ipv4 and ipv6 feature detect - ipVersions := make([]string, 0, 2) - if lb.node.SupportsIPv4() { - ipVersions = append(ipVersions, util.InboundPassthroughClusterIpv4) - } - if lb.node.SupportsIPv6() { - ipVersions = append(ipVersions, util.InboundPassthroughClusterIpv6) - } - - // Setup enough slots for common max size (permissive mode is 5 filter chains). This is not - // exact, just best effort optimization - filterChains := make([]*listener.FilterChain, 0, 1+5*len(ipVersions)) - filterChains = append(filterChains, buildInboundBlackhole(lb)) - - for _, clusterName := range ipVersions { - mtlsOptions := lb.authnBuilder.ForPassthrough() - for _, mtls := range mtlsOptions { - cc := inboundChainConfig{ - port: ServiceInstancePort{ - Name: model.VirtualInboundListenerName, - // Port as 0 doesn't completely make sense here, since we get weird tracing decorators like `:0/*`, - // but this is backwards compatible and there aren't any perfect options. - Port: 0, - Protocol: protocol.Unsupported, - TargetPort: mtls.Port, - }, - clusterName: clusterName, - passthrough: true, - } - opts := getFilterChainMatchOptions(mtls, istionetworking.ListenerProtocolAuto) - filterChains = append(filterChains, lb.inboundChainForOpts(cc, mtls, opts)...) - } - } - - return filterChains -} - -// buildInboundBlackhole builds a special filter chain for the virtual inbound matching traffic to the port the listener is actually on. -// This avoids a possible loop where traffic sent to this port would continually call itself indefinitely. -func buildInboundBlackhole(lb *ListenerBuilder) *listener.FilterChain { - var filters []*listener.Filter - filters = append(filters, buildMetadataExchangeNetworkFilters(istionetworking.ListenerClassSidecarInbound)...) - filters = append(filters, buildMetricsNetworkFilters(lb.push, lb.node, istionetworking.ListenerClassSidecarInbound)...) - filters = append(filters, &listener.Filter{ - Name: wellknown.TCPProxy, - ConfigType: &listener.Filter_TypedConfig{TypedConfig: util.MessageToAny(&tcp.TcpProxy{ - StatPrefix: util.BlackHoleCluster, - ClusterSpecifier: &tcp.TcpProxy_Cluster{Cluster: util.BlackHoleCluster}, - })}, - }) - return &listener.FilterChain{ - Name: model.VirtualInboundBlackholeFilterChainName, - FilterChainMatch: &listener.FilterChainMatch{ - DestinationPort: &wrappers.UInt32Value{Value: ProxyInboundListenPort}, - }, - Filters: filters, - } -} - -// buildSidecarInboundHTTPOpts sets up HTTP options for a given chain. -func buildSidecarInboundHTTPOpts(lb *ListenerBuilder, cc inboundChainConfig) *httpListenerOpts { - httpOpts := &httpListenerOpts{ - routeConfig: buildSidecarInboundHTTPRouteConfig(lb, cc), - rds: "", // no RDS for inbound traffic - useRemoteAddress: false, - connectionManager: &hcm.HttpConnectionManager{ - // Append and forward client cert to backend. - ForwardClientCertDetails: hcm.HttpConnectionManager_APPEND_FORWARD, - SetCurrentClientCertDetails: &hcm.HttpConnectionManager_SetCurrentClientCertDetails{ - Subject: proto.BoolTrue, - Uri: true, - Dns: true, - }, - ServerName: EnvoyServerName, - }, - protocol: cc.port.Protocol, - class: istionetworking.ListenerClassSidecarInbound, - statPrefix: cc.StatPrefix(), - } - // See https://github.com/grpc/grpc-web/tree/master/net/grpc/gateway/examples/helloworld#configure-the-proxy - if cc.port.Protocol.IsHTTP2() { - httpOpts.connectionManager.Http2ProtocolOptions = &core.Http2ProtocolOptions{} - } - - if features.HTTP10 || enableHTTP10(lb.node.Metadata.HTTP10) { - httpOpts.connectionManager.HttpProtocolOptions = &core.Http1ProtocolOptions{ - AcceptHttp_10: true, - } - } - - return httpOpts -} - -// buildInboundNetworkFiltersForHTTP builds the network filters that should be inserted before an HCM. -// This should only be used with HTTP; see buildInboundNetworkFilters for TCP -func (lb *ListenerBuilder) buildInboundNetworkFiltersForHTTP(cc inboundChainConfig) []*listener.Filter { - var filters []*listener.Filter - filters = append(filters, buildMetadataExchangeNetworkFilters(istionetworking.ListenerClassSidecarInbound)...) - - httpOpts := buildSidecarInboundHTTPOpts(lb, cc) - hcm := lb.buildHTTPConnectionManager(httpOpts) - filters = append(filters, &listener.Filter{ - Name: wellknown.HTTPConnectionManager, - ConfigType: &listener.Filter_TypedConfig{TypedConfig: util.MessageToAny(hcm)}, - }) - return filters -} - -// buildInboundNetworkFilters generates a TCP proxy network filter on the inbound path -func (lb *ListenerBuilder) buildInboundNetworkFilters(fcc inboundChainConfig) []*listener.Filter { - statPrefix := fcc.clusterName - // If stat name is configured, build the stat prefix from configured pattern. - if len(lb.push.Mesh.InboundClusterStatName) != 0 { - statPrefix = telemetry.BuildInboundStatPrefix(lb.push.Mesh.InboundClusterStatName, fcc.telemetryMetadata, "", fcc.port.Port, fcc.port.Name) - } - tcpProxy := &tcp.TcpProxy{ - StatPrefix: statPrefix, - ClusterSpecifier: &tcp.TcpProxy_Cluster{Cluster: fcc.clusterName}, - } - idleTimeout, err := time.ParseDuration(lb.node.Metadata.IdleTimeout) - if err == nil { - tcpProxy.IdleTimeout = durationpb.New(idleTimeout) - } - tcpFilter := setAccessLogAndBuildTCPFilter(lb.push, lb.node, tcpProxy, istionetworking.ListenerClassSidecarInbound) - - var filters []*listener.Filter - filters = append(filters, buildMetadataExchangeNetworkFilters(istionetworking.ListenerClassSidecarInbound)...) - filters = append(filters, lb.authzCustomBuilder.BuildTCP()...) - filters = append(filters, lb.authzBuilder.BuildTCP()...) - filters = append(filters, buildMetricsNetworkFilters(lb.push, lb.node, istionetworking.ListenerClassSidecarInbound)...) - filters = append(filters, buildNetworkFiltersStack(fcc.port.Protocol, tcpFilter, statPrefix, fcc.clusterName)...) - - return filters -} diff --git a/pilot/pkg/networking/core/v1alpha3/listener_test.go b/pilot/pkg/networking/core/v1alpha3/listener_test.go deleted file mode 100644 index 1e7d4ff45..000000000 --- a/pilot/pkg/networking/core/v1alpha3/listener_test.go +++ /dev/null @@ -1,2860 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "fmt" - "reflect" - "strconv" - "strings" - "testing" - "time" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - tcp "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3" - tracing "github.com/envoyproxy/go-control-plane/envoy/type/tracing/v3" - xdstype "github.com/envoyproxy/go-control-plane/envoy/type/v3" - "github.com/envoyproxy/go-control-plane/pkg/conversion" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/testing/protocmp" - "google.golang.org/protobuf/types/known/durationpb" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3/listenertest" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - xdsfilters "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/filters" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -const ( - wildcardIP = "0.0.0.0" -) - -func getProxy() *model.Proxy { - pr := &model.Proxy{ - Type: model.SidecarProxy, - IPAddresses: []string{"1.1.1.1"}, - ID: "v0.default", - DNSDomain: "default.example.org", - Metadata: &model.NodeMetadata{ - Namespace: "not-default", - }, - ConfigNamespace: "not-default", - } - pr.DiscoverIPMode() - return pr -} - -var ( - tnow = time.Now() - proxyHTTP10 = model.Proxy{ - Type: model.SidecarProxy, - IPAddresses: []string{"1.1.1.1"}, - ID: "v0.default", - DNSDomain: "default.example.org", - Metadata: &model.NodeMetadata{ - Namespace: "not-default", - HTTP10: "1", - }, - ConfigNamespace: "not-default", - } - proxyGateway = model.Proxy{ - Type: model.Router, - IPAddresses: []string{"1.1.1.1"}, - ID: "v0.default", - DNSDomain: "default.example.org", - Metadata: &proxyGatewayMetadata, - ConfigNamespace: "not-default", - } - proxyGatewayMetadata = model.NodeMetadata{ - Namespace: "not-default", - Labels: map[string]string{ - "istio": "ingressgateway", - }, - } - virtualServiceSpec = &networking.VirtualService{ - Hosts: []string{"test.com"}, - Gateways: []string{"mesh"}, - Tcp: []*networking.TCPRoute{ - { - Match: []*networking.L4MatchAttributes{ - { - DestinationSubnets: []string{"10.10.0.0/24"}, - Port: 8080, - }, - }, - Route: []*networking.RouteDestination{ - { - Destination: &networking.Destination{ - Host: "test.org", - Port: &networking.PortSelector{ - Number: 80, - }, - }, - Weight: 100, - }, - }, - }, - }, - } -) - -func TestInboundListenerConfig(t *testing.T) { - for _, p := range []*model.Proxy{getProxy(), &proxyHTTP10} { - t.Run("multiple services", func(t *testing.T) { - testInboundListenerConfig(t, p, - buildService("test1.com", wildcardIP, protocol.HTTP, tnow.Add(1*time.Second)), - buildService("test2.com", wildcardIP, "unknown", tnow), - buildService("test3.com", wildcardIP, protocol.HTTP, tnow.Add(2*time.Second))) - }) - t.Run("no service", func(t *testing.T) { - testInboundListenerConfigWithoutService(t, p) - }) - t.Run("sidecar", func(t *testing.T) { - testInboundListenerConfigWithSidecar(t, p, - buildService("test.com", wildcardIP, protocol.HTTP, tnow)) - }) - t.Run("sidecar with service", func(t *testing.T) { - testInboundListenerConfigWithSidecarWithoutServices(t, p) - }) - } - - t.Run("grpc", func(t *testing.T) { - testInboundListenerConfigWithGrpc(t, getProxy(), - buildService("test1.com", wildcardIP, protocol.GRPC, tnow.Add(1*time.Second))) - }) -} - -func TestOutboundListenerConflict_HTTPWithCurrentUnknown(t *testing.T) { - test.SetBoolForTest(t, &features.EnableProtocolSniffingForOutbound, true) - - // The oldest service port is unknown. We should encounter conflicts when attempting to add the HTTP ports. Purposely - // storing the services out of time order to test that it's being sorted properly. - testOutboundListenerConflict(t, - buildService("test1.com", wildcardIP, protocol.HTTP, tnow.Add(1*time.Second)), - buildService("test2.com", wildcardIP, "unknown", tnow), - buildService("test3.com", wildcardIP, protocol.HTTP, tnow.Add(2*time.Second))) -} - -func TestOutboundListenerConflict_WellKnowPorts(t *testing.T) { - test.SetBoolForTest(t, &features.EnableProtocolSniffingForOutbound, true) - - // The oldest service port is unknown. We should encounter conflicts when attempting to add the HTTP ports. Purposely - // storing the services out of time order to test that it's being sorted properly. - testOutboundListenerConflict(t, - buildServiceWithPort("test1.com", 3306, protocol.HTTP, tnow.Add(1*time.Second)), - buildServiceWithPort("test2.com", 3306, protocol.MySQL, tnow)) - testOutboundListenerConflict(t, - buildServiceWithPort("test1.com", 9999, protocol.HTTP, tnow.Add(1*time.Second)), - buildServiceWithPort("test2.com", 9999, protocol.MySQL, tnow)) -} - -func TestOutboundListenerConflict_TCPWithCurrentUnknown(t *testing.T) { - test.SetBoolForTest(t, &features.EnableProtocolSniffingForOutbound, true) - - // The oldest service port is unknown. We should encounter conflicts when attempting to add the HTTP ports. Purposely - // storing the services out of time order to test that it's being sorted properly. - testOutboundListenerConflict(t, - buildService("test1.com", wildcardIP, protocol.TCP, tnow.Add(1*time.Second)), - buildService("test2.com", wildcardIP, "unknown", tnow), - buildService("test3.com", wildcardIP, protocol.TCP, tnow.Add(2*time.Second))) -} - -func TestOutboundListenerConflict_UnknownWithCurrentTCP(t *testing.T) { - test.SetBoolForTest(t, &features.EnableProtocolSniffingForOutbound, true) - - // The oldest service port is TCP. We should encounter conflicts when attempting to add the HTTP ports. Purposely - // storing the services out of time order to test that it's being sorted properly. - testOutboundListenerConflict(t, - buildService("test1.com", wildcardIP, "unknown", tnow.Add(1*time.Second)), - buildService("test2.com", wildcardIP, protocol.TCP, tnow), - buildService("test3.com", wildcardIP, "unknown", tnow.Add(2*time.Second))) -} - -func TestOutboundListenerConflict_UnknownWithCurrentHTTP(t *testing.T) { - test.SetBoolForTest(t, &features.EnableProtocolSniffingForOutbound, true) - - // The oldest service port is Auto. We should encounter conflicts when attempting to add the HTTP ports. Purposely - // storing the services out of time order to test that it's being sorted properly. - testOutboundListenerConflict(t, - buildService("test1.com", wildcardIP, "unknown", tnow.Add(1*time.Second)), - buildService("test2.com", wildcardIP, protocol.HTTP, tnow), - buildService("test3.com", wildcardIP, "unknown", tnow.Add(2*time.Second))) -} - -func TestOutboundListenerRoute(t *testing.T) { - test.SetBoolForTest(t, &features.EnableProtocolSniffingForOutbound, true) - - testOutboundListenerRoute(t, - buildService("test1.com", "1.2.3.4", "unknown", tnow.Add(1*time.Second)), - buildService("test2.com", "2.3.4.5", protocol.HTTP, tnow), - buildService("test3.com", "3.4.5.6", "unknown", tnow.Add(2*time.Second))) -} - -func TestOutboundListenerConfig_WithSidecar(t *testing.T) { - // Add a service and verify it's config - services := []*model.Service{ - buildService("test1.com", wildcardIP, protocol.HTTP, tnow.Add(1*time.Second)), - buildService("test2.com", wildcardIP, protocol.TCP, tnow), - buildService("test3.com", wildcardIP, "unknown", tnow.Add(2*time.Second)), - } - service4 := &model.Service{ - CreationTime: tnow.Add(1 * time.Second), - Hostname: host.Name("test4.com"), - DefaultAddress: wildcardIP, - Ports: model.PortList{ - &model.Port{ - Name: "udp", - Port: 9000, - Protocol: protocol.GRPC, - }, - }, - Resolution: model.Passthrough, - Attributes: model.ServiceAttributes{ - Namespace: "default", - }, - } - services = append(services, service4) - service5 := &model.Service{ - CreationTime: tnow.Add(1 * time.Second), - Hostname: host.Name("test5.com"), - DefaultAddress: "8.8.8.8", - Ports: model.PortList{ - &model.Port{ - Name: "MySQL", - Port: 3306, - Protocol: protocol.MySQL, - }, - }, - Resolution: model.Passthrough, - Attributes: model.ServiceAttributes{ - Namespace: "default", - }, - } - services = append(services, service5) - service6 := &model.Service{ - CreationTime: tnow.Add(1 * time.Second), - Hostname: host.Name("test6.com"), - DefaultAddress: "2.2.2.2", - Ports: model.PortList{ - &model.Port{ - Name: "unknown", - Port: 8888, - Protocol: "unknown", - }, - }, - Resolution: model.Passthrough, - Attributes: model.ServiceAttributes{ - Namespace: "default", - }, - } - services = append(services, service6) - testOutboundListenerConfigWithSidecar(t, services...) -} - -func TestOutboundListenerConflict_HTTPWithCurrentTCP(t *testing.T) { - // The oldest service port is TCP. We should encounter conflicts when attempting to add the HTTP ports. Purposely - // storing the services out of time order to test that it's being sorted properly. - testOutboundListenerConflictWithSniffingDisabled(t, - buildService("test1.com", wildcardIP, protocol.HTTP, tnow.Add(1*time.Second)), - buildService("test2.com", wildcardIP, protocol.TCP, tnow), - buildService("test3.com", wildcardIP, protocol.HTTP, tnow.Add(2*time.Second))) -} - -func TestOutboundListenerConflict_TCPWithCurrentHTTP(t *testing.T) { - // The oldest service port is HTTP. We should encounter conflicts when attempting to add the TCP ports. Purposely - // storing the services out of time order to test that it's being sorted properly. - testOutboundListenerConflictWithSniffingDisabled(t, - buildService("test1.com", wildcardIP, protocol.TCP, tnow.Add(1*time.Second)), - buildService("test2.com", wildcardIP, protocol.HTTP, tnow), - buildService("test3.com", wildcardIP, protocol.TCP, tnow.Add(2*time.Second))) -} - -func TestOutboundListenerConflict_TCPWithCurrentTCP(t *testing.T) { - services := []*model.Service{ - buildService("test1.com", "1.2.3.4", protocol.TCP, tnow.Add(1*time.Second)), - buildService("test2.com", "1.2.3.4", protocol.TCP, tnow), - buildService("test3.com", "1.2.3.4", protocol.TCP, tnow.Add(2*time.Second)), - } - listeners := buildOutboundListeners(t, getProxy(), nil, nil, services...) - if len(listeners) != 1 { - t.Fatalf("expected %d listeners, found %d", 1, len(listeners)) - } - // The filter chains should all be merged into one. - if len(listeners[0].FilterChains) != 1 { - t.Fatalf("expected %d filter chains, found %d", 1, len(listeners[0].FilterChains)) - } - - oldestService := getOldestService(services...) - oldestProtocol := oldestService.Ports[0].Protocol - if oldestProtocol != protocol.HTTP && isHTTPListener(listeners[0]) { - t.Fatal("expected TCP listener, found HTTP") - } else if oldestProtocol == protocol.HTTP && !isHTTPListener(listeners[0]) { - t.Fatal("expected HTTP listener, found TCP") - } - - // Validate that listener conflict preserves the listener of oldest service. - verifyOutboundTCPListenerHostname(t, listeners[0], oldestService.Hostname) -} - -func TestOutboundListenerTCPWithVS(t *testing.T) { - tests := []struct { - name string - CIDR string - expectedChains []string - }{ - { - name: "same CIDR", - CIDR: "10.10.0.0/24", - expectedChains: []string{"10.10.0.0"}, - }, - { - name: "different CIDR", - CIDR: "10.10.10.0/24", - expectedChains: []string{"10.10.0.0", "10.10.10.0"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - services := []*model.Service{ - buildService("test.com", tt.CIDR, protocol.TCP, tnow), - } - - virtualService := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "test_vs", - Namespace: "default", - }, - Spec: virtualServiceSpec, - } - listeners := buildOutboundListeners(t, getProxy(), nil, &virtualService, services...) - - if len(listeners) != 1 { - t.Fatalf("expected %d listeners, found %d", 1, len(listeners)) - } - var chains []string - for _, fc := range listeners[0].FilterChains { - for _, cidr := range fc.FilterChainMatch.PrefixRanges { - chains = append(chains, cidr.AddressPrefix) - } - } - // There should not be multiple filter chains with same CIDR match - if !reflect.DeepEqual(chains, tt.expectedChains) { - t.Fatalf("expected filter chains %v, found %v", tt.expectedChains, chains) - } - - if listeners[0].ConnectionBalanceConfig != nil { - t.Fatalf("expected connection balance config to be set to empty, found %v", listeners[0].ConnectionBalanceConfig) - } - - for _, l := range listeners { - for _, fc := range l.GetFilterChains() { - listenertest.VerifyFilterChain(t, fc, listenertest.FilterChainTest{ - NetworkFilters: []string{wellknown.TCPProxy}, - TotalMatch: true, - }) - } - } - }) - } -} - -func TestOutboundListenerTCPWithVSExactBalance(t *testing.T) { - tests := []struct { - name string - CIDR string - expectedChains []string - }{ - { - name: "same CIDR", - CIDR: "10.10.0.0/24", - expectedChains: []string{"10.10.0.0"}, - }, - { - name: "different CIDR", - CIDR: "10.10.10.0/24", - expectedChains: []string{"10.10.0.0", "10.10.10.0"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - services := []*model.Service{ - buildService("test.com", tt.CIDR, protocol.TCP, tnow), - } - - virtualService := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), - Name: "test_vs", - Namespace: "default", - }, - Spec: virtualServiceSpec, - } - proxy := getProxy() - proxy.Metadata.InboundListenerExactBalance = true - proxy.Metadata.OutboundListenerExactBalance = true - listeners := buildOutboundListeners(t, proxy, nil, &virtualService, services...) - - if len(listeners) != 1 { - t.Fatalf("expected %d listeners, found %d", 1, len(listeners)) - } - var chains []string - for _, fc := range listeners[0].FilterChains { - for _, cidr := range fc.FilterChainMatch.PrefixRanges { - chains = append(chains, cidr.AddressPrefix) - } - } - // There should not be multiple filter chains with same CIDR match - if !reflect.DeepEqual(chains, tt.expectedChains) { - t.Fatalf("expected filter chains %v, found %v", tt.expectedChains, chains) - } - - if listeners[0].ConnectionBalanceConfig == nil || listeners[0].ConnectionBalanceConfig.GetExactBalance() == nil { - t.Fatalf("expected connection balance config to be set to exact_balance, found %v", listeners[0].ConnectionBalanceConfig) - } - }) - } -} - -func TestOutboundListenerForHeadlessServices(t *testing.T) { - svc := buildServiceWithPort("test.com", 9999, protocol.TCP, tnow) - svc.Resolution = model.Passthrough - svc.Attributes.ServiceRegistry = provider.Kubernetes - services := []*model.Service{svc} - - autoSvc := buildServiceWithPort("test.com", 9999, protocol.Unsupported, tnow) - autoSvc.Resolution = model.Passthrough - autoSvc.Attributes.ServiceRegistry = provider.Kubernetes - - extSvc := buildServiceWithPort("example1.com", 9999, protocol.TCP, tnow) - extSvc.Resolution = model.Passthrough - extSvc.Attributes.ServiceRegistry = provider.External - - extSvcSelector := buildServiceWithPort("example2.com", 9999, protocol.TCP, tnow) - extSvcSelector.Resolution = model.Passthrough - extSvcSelector.Attributes.ServiceRegistry = provider.External - extSvcSelector.Attributes.LabelSelectors = map[string]string{"foo": "bar"} - - tests := []struct { - name string - instances []*model.ServiceInstance - services []*model.Service - numListenersOnServicePort int - }{ - { - name: "gen a listener per IP instance", - instances: []*model.ServiceInstance{ - // This instance is the proxy itself, will not gen a outbound listener for it. - buildServiceInstance(services[0], "1.1.1.1"), - buildServiceInstance(services[0], "10.10.10.10"), - buildServiceInstance(services[0], "11.11.11.11"), - buildServiceInstance(services[0], "12.11.11.11"), - }, - services: []*model.Service{svc}, - numListenersOnServicePort: 3, - }, - { - name: "no listeners for empty services", - instances: []*model.ServiceInstance{}, - services: []*model.Service{svc}, - numListenersOnServicePort: 0, - }, - { - name: "no listeners for DNS instance", - instances: []*model.ServiceInstance{ - buildServiceInstance([]*model.Service{svc}[0], "example.com"), - }, - services: services, - numListenersOnServicePort: 0, - }, - { - name: "external service", - instances: []*model.ServiceInstance{}, - services: []*model.Service{extSvc}, - numListenersOnServicePort: 1, - }, - { - name: "external service with selector", - instances: []*model.ServiceInstance{}, - services: []*model.Service{extSvcSelector}, - numListenersOnServicePort: 0, - }, - { - name: "external service with selector and endpoints", - instances: []*model.ServiceInstance{ - buildServiceInstance(extSvcSelector, "10.10.10.10"), - buildServiceInstance(extSvcSelector, "11.11.11.11"), - }, - services: []*model.Service{extSvcSelector}, - numListenersOnServicePort: 2, - }, - { - name: "no listeners for empty Kubernetes auto protocol", - instances: []*model.ServiceInstance{}, - services: []*model.Service{autoSvc}, - numListenersOnServicePort: 0, - }, - { - name: "listeners per instance for Kubernetes auto protocol", - instances: []*model.ServiceInstance{ - buildServiceInstance(autoSvc, "10.10.10.10"), - buildServiceInstance(autoSvc, "11.11.11.11"), - }, - services: []*model.Service{autoSvc}, - numListenersOnServicePort: 2, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - cg := NewConfigGenTest(t, TestOptions{ - Services: tt.services, - Instances: tt.instances, - }) - - proxy := cg.SetupProxy(nil) - proxy.Metadata.InboundListenerExactBalance = true - proxy.Metadata.OutboundListenerExactBalance = true - - listeners := NewListenerBuilder(proxy, cg.env.PushContext).buildSidecarOutboundListeners(proxy, cg.env.PushContext) - listenersToCheck := make([]string, 0) - for _, l := range listeners { - if l.Address.GetSocketAddress().GetPortValue() == 9999 { - listenersToCheck = append(listenersToCheck, l.Name) - } - - if l.ConnectionBalanceConfig == nil || l.ConnectionBalanceConfig.GetExactBalance() == nil { - t.Fatalf("expected connection balance config to be set to exact_balance, found %v", listeners[0].ConnectionBalanceConfig) - } - } - - if len(listenersToCheck) != tt.numListenersOnServicePort { - t.Errorf("Expected %d listeners on service port 9999, got %d (%v)", tt.numListenersOnServicePort, len(listenersToCheck), listenersToCheck) - } - }) - } -} - -func TestInboundHTTPListenerConfig(t *testing.T) { - sidecarConfig := config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "not-default", - GroupVersionKind: gvk.Sidecar, - }, - Spec: &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{ - Number: 8080, - Protocol: "HTTP", - Name: "uds", - }, - Bind: "1.1.1.1", - DefaultEndpoint: "127.0.0.1:80", - }, - }, - }, - } - svc := buildService("test.com", wildcardIP, protocol.HTTP, tnow) - for _, p := range []*model.Proxy{getProxy(), &proxyHTTP10} { - cases := []struct { - name string - p *model.Proxy - cfg []config.Config - services []*model.Service - }{ - { - name: "simple", - p: p, - services: []*model.Service{svc}, - }, - { - name: "sidecar with service", - p: p, - services: []*model.Service{svc}, - cfg: []config.Config{sidecarConfig}, - }, - { - name: "sidecar", - p: p, - services: []*model.Service{svc}, - cfg: []config.Config{sidecarConfig}, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - t.Helper() - listeners := buildListeners(t, TestOptions{ - Services: tt.services, - Configs: tt.cfg, - }, tt.p) - l := xdstest.ExtractListener(model.VirtualInboundListenerName, listeners) - listenertest.VerifyListener(t, l, listenertest.ListenerTest{ - FilterChains: []listenertest.FilterChainTest{ - { - TotalMatch: true, - Port: 8080, - HTTPFilters: []string{xdsfilters.MxFilterName, xdsfilters.Fault.Name, xdsfilters.Cors.Name, xdsfilters.Router.Name}, - ValidateHCM: func(t test.Failer, hcm *hcm.HttpConnectionManager) { - assert.Equal(t, "istio-envoy", hcm.GetServerName(), "server name") - if len(tt.cfg) == 0 { - assert.Equal(t, "inbound_0.0.0.0_8080", hcm.GetStatPrefix(), "stat prefix") - } else { - // Sidecar impacts stat prefix - assert.Equal(t, "inbound_1.1.1.1_8080", hcm.GetStatPrefix(), "stat prefix") - } - assert.Equal(t, "APPEND_FORWARD", hcm.GetForwardClientCertDetails().String(), "forward client cert details") - assert.Equal(t, true, hcm.GetSetCurrentClientCertDetails().GetSubject().GetValue(), "subject") - assert.Equal(t, true, hcm.GetSetCurrentClientCertDetails().GetDns(), "dns") - assert.Equal(t, true, hcm.GetSetCurrentClientCertDetails().GetUri(), "uri") - assert.Equal(t, true, hcm.GetNormalizePath().GetValue(), "normalize path") - assert.Equal(t, enableHTTP10(tt.p.Metadata.HTTP10), hcm.GetHttpProtocolOptions().GetAcceptHttp_10(), "http/1.0") - }, - }, - }, - }) - }) - } - } -} - -func TestOutboundListenerConfig_WithDisabledSniffing_WithSidecar(t *testing.T) { - test.SetBoolForTest(t, &features.EnableProtocolSniffingForOutbound, false) - - // Add a service and verify it's config - services := []*model.Service{ - buildService("test1.com", wildcardIP, protocol.HTTP, tnow.Add(1*time.Second)), - buildService("test2.com", wildcardIP, protocol.TCP, tnow), - buildService("test3.com", wildcardIP, protocol.HTTP, tnow.Add(2*time.Second)), - } - service4 := &model.Service{ - CreationTime: tnow.Add(1 * time.Second), - Hostname: host.Name("test4.com"), - DefaultAddress: wildcardIP, - Ports: model.PortList{ - &model.Port{ - Name: "default", - Port: 9090, - Protocol: protocol.HTTP, - }, - }, - Resolution: model.Passthrough, - Attributes: model.ServiceAttributes{ - Namespace: "default", - }, - } - testOutboundListenerConfigWithSidecarWithSniffingDisabled(t, services...) - services = append(services, service4) - testOutboundListenerConfigWithSidecarWithCaptureModeNone(t, services...) - testOutboundListenerConfigWithSidecarWithUseRemoteAddress(t, services...) -} - -func TestOutboundTlsTrafficWithoutTimeout(t *testing.T) { - services := []*model.Service{ - { - CreationTime: tnow, - Hostname: host.Name("test.com"), - DefaultAddress: wildcardIP, - Ports: model.PortList{ - &model.Port{ - Name: "https", - Port: 8080, - Protocol: protocol.HTTPS, - }, - }, - Resolution: model.Passthrough, - Attributes: model.ServiceAttributes{ - Namespace: "default", - }, - }, - { - CreationTime: tnow, - Hostname: host.Name("test1.com"), - DefaultAddress: wildcardIP, - Ports: model.PortList{ - &model.Port{ - Name: "foo", - Port: 9090, - Protocol: "unknown", - }, - }, - Resolution: model.Passthrough, - Attributes: model.ServiceAttributes{ - Namespace: "default", - }, - }, - } - testOutboundListenerFilterTimeout(t, services...) -} - -func TestOutboundTls(t *testing.T) { - services := []*model.Service{ - { - CreationTime: tnow, - Hostname: host.Name("test.com"), - DefaultAddress: wildcardIP, - Ports: model.PortList{ - &model.Port{ - Name: "https", - Port: 8080, - Protocol: protocol.HTTPS, - }, - }, - Resolution: model.Passthrough, - Attributes: model.ServiceAttributes{ - Namespace: "default", - }, - }, - } - virtualService := config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "test", - Namespace: "default", - }, - Spec: &networking.VirtualService{ - Hosts: []string{"test.com"}, - Gateways: []string{"mesh"}, - Tls: []*networking.TLSRoute{ - { - Match: []*networking.TLSMatchAttributes{ - { - DestinationSubnets: []string{"10.10.0.0/24", "11.10.0.0/24"}, - Port: 8080, - SniHosts: []string{"a", "b", "c"}, - }, - }, - Route: []*networking.RouteDestination{ - { - Destination: &networking.Destination{ - Host: "test.org", - Port: &networking.PortSelector{ - Number: 80, - }, - }, - Weight: 100, - }, - }, - }, - }, - }, - } - virtualService2 := config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "test2", - Namespace: "default", - }, - Spec: &networking.VirtualService{ - Hosts: []string{"test.com"}, - Gateways: []string{"mesh"}, - Tls: []*networking.TLSRoute{ - { - Match: []*networking.TLSMatchAttributes{ - { - DestinationSubnets: []string{"12.10.0.0/24", "13.10.0.0/24"}, - Port: 8080, - SniHosts: []string{"e", "f", "g"}, - }, - }, - Route: []*networking.RouteDestination{ - { - Destination: &networking.Destination{ - Host: "test.org", - Port: &networking.PortSelector{ - Number: 80, - }, - }, - Weight: 100, - }, - }, - }, - }, - }, - } - buildOutboundListeners(t, getProxy(), &virtualService2, &virtualService, services...) -} - -func TestOutboundListenerConfigWithSidecarHTTPProxy(t *testing.T) { - sidecarConfig := &config.Config{ - Meta: config.Meta{ - Name: "sidecar-with-http-proxy", - Namespace: "not-default", - GroupVersionKind: gvk.Sidecar, - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"default/*"}, - Port: &networking.Port{ - Number: 15080, - Protocol: "HTTP_PROXY", - Name: "15080", - }, - Bind: "127.0.0.1", - CaptureMode: networking.CaptureMode_NONE, - }, - }, - }, - } - services := []*model.Service{buildService("httpbin.com", wildcardIP, protocol.HTTP, tnow.Add(1*time.Second))} - - listeners := buildOutboundListeners(t, getProxy(), sidecarConfig, nil, services...) - - if expected := 1; len(listeners) != expected { - t.Fatalf("expected %d listeners, found %d", expected, len(listeners)) - } - l := findListenerByPort(listeners, 15080) - if l == nil { - t.Fatalf("expected listener on port %d, but not found", 15080) - } - if len(l.FilterChains) != 1 { - t.Fatalf("expectd %d filter chains, found %d", 1, len(l.FilterChains)) - } else { - if !isHTTPFilterChain(l.FilterChains[0]) { - t.Fatalf("expected http filter chain, found %s", l.FilterChains[1].Filters[0].Name) - } - if len(l.ListenerFilters) > 0 { - t.Fatalf("expected %d listener filter, found %d", 0, len(l.ListenerFilters)) - } - } -} - -func TestGetActualWildcardAndLocalHost(t *testing.T) { - tests := []struct { - name string - proxy *model.Proxy - expected [2]string - }{ - { - name: "ipv4 only", - proxy: &model.Proxy{ - IPAddresses: []string{"1.1.1.1", "127.0.0.1", "2.2.2.2"}, - }, - expected: [2]string{WildcardAddress, LocalhostAddress}, - }, - { - name: "ipv6 only", - proxy: &model.Proxy{ - IPAddresses: []string{"1111:2222::1", "::1", "2222:3333::1"}, - }, - expected: [2]string{WildcardIPv6Address, LocalhostIPv6Address}, - }, - { - name: "mixed ipv4 and ipv6", - proxy: &model.Proxy{ - IPAddresses: []string{"1111:2222::1", "::1", "127.0.0.1", "2.2.2.2", "2222:3333::1"}, - }, - expected: [2]string{WildcardAddress, LocalhostAddress}, - }, - } - for _, tt := range tests { - tt.proxy.DiscoverIPMode() - wm, lh := getActualWildcardAndLocalHost(tt.proxy) - if wm != tt.expected[0] && lh != tt.expected[1] { - t.Errorf("Test %s failed, expected: %s / %s got: %s / %s", tt.name, tt.expected[0], tt.expected[1], wm, lh) - } - } -} - -// Test to catch new fields in FilterChainMatch message. -func TestFilterChainMatchFields(t *testing.T) { - fcm := listener.FilterChainMatch{} - e := reflect.ValueOf(&fcm).Elem() - // If this fails, that means new fields have been added to FilterChainMatch, filterChainMatchEqual function needs to be updated. - if e.NumField() != 14 { - t.Fatalf("Expected 14 fields, got %v. This means we need to update filterChainMatchEqual implementation", e.NumField()) - } -} - -func TestInboundListener_PrivilegedPorts(t *testing.T) { - // Verify that an explicit ingress listener will not bind to privileged ports - // if proxy is not using Iptables and cannot bind to privileged ports (1-1023). - // - // Even if a user explicitly created a Sidecar config with an ingress listener for a privileged port, - // it is still not worth it creating such a listener if we already known that a proxy will end up - // rejecting it. - testPrivilegedPorts(t, func(t *testing.T, proxy *model.Proxy, port uint32) []*listener.Listener { - // simulate user-defined Sidecar config with an ingress listener for a given port - sidecarConfig := config.Config{ - Meta: config.Meta{ - Name: "sidecar-with-ingress-listener", - Namespace: proxy.ConfigNamespace, - GroupVersionKind: gvk.Sidecar, - }, - Spec: &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{ - Number: port, - Protocol: "HTTP", - Name: strconv.Itoa(int(port)), - }, - DefaultEndpoint: "127.0.0.1:8080", - }, - }, - }, - } - return buildListeners(t, TestOptions{ - Configs: []config.Config{sidecarConfig}, - }, proxy) - }) -} - -func TestOutboundListener_PrivilegedPorts(t *testing.T) { - // Verify that an implicit catch all egress listener will not bind to privileged ports - // if proxy is not using Iptables and cannot bind to privileged ports (1-1023). - // - // It is very common for the catch all egress listener to match services on ports 80 and 443. - // Therefore, the default behavior should not force users to start from looking for a workaround. - t.Run("implicit catch all egress listener", func(t *testing.T) { - testPrivilegedPorts(t, func(t *testing.T, proxy *model.Proxy, port uint32) []*listener.Listener { - return buildListeners(t, TestOptions{ - Services: []*model.Service{buildServiceWithPort("httpbin.com", int(port), protocol.HTTP, tnow)}, - }, proxy) - }) - }) - - // Verify that an explicit per-port egress listener will not bind to privileged ports - // if proxy is not using Iptables and cannot bind to privileged ports (1-1023). - // - // Even if a user explicitly created a Sidecar config with an egress listener for a privileged port, - // it is still not worth it creating such a listener if we already known that a proxy will end up - // rejecting it. - t.Run("explicit per-port egress listener", func(t *testing.T) { - testPrivilegedPorts(t, func(t *testing.T, proxy *model.Proxy, port uint32) []*listener.Listener { - // simulate user-defined Sidecar config with an egress listener for a given port - sidecarConfig := config.Config{ - Meta: config.Meta{ - Name: "sidecar-with-per-port-egress-listener", - Namespace: proxy.ConfigNamespace, - GroupVersionKind: gvk.Sidecar, - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"default/*"}, - Port: &networking.Port{ - Number: port, - Protocol: "HTTP", - Name: strconv.Itoa(int(port)), - }, - }, - }, - }, - } - return buildListeners(t, TestOptions{ - Services: []*model.Service{buildServiceWithPort("httpbin.com", int(port), protocol.HTTP, tnow)}, - Configs: []config.Config{sidecarConfig}, - }, proxy) - }) - }) -} - -func testPrivilegedPorts(t *testing.T, buildListeners func(t *testing.T, proxy *model.Proxy, port uint32) []*listener.Listener) { - privilegedPorts := []uint32{1, 80, 443, 1023} - unprivilegedPorts := []uint32{1024, 8080, 8443, 15443} - anyPorts := append(privilegedPorts, unprivilegedPorts...) - - // multiple test cases to ensure that privileged ports get treated differently - // only under certain conditions, namely - // 1) proxy explicitly indicated it is not using Iptables - // 2) proxy explicitly indicated it is not a privileged process (cannot bind to 1-1023) - cases := []struct { - name string - unprivileged bool // whether proxy is unprivileged - mode model.TrafficInterceptionMode - ports []uint32 - expectListener bool // expect listener to be generated - }{ - { - name: "privileged proxy; implicit REDIRECT mode; any ports", - unprivileged: false, - mode: "", - ports: anyPorts, - expectListener: true, - }, - { - name: "privileged proxy; explicit REDIRECT mode; any ports", - unprivileged: false, - mode: model.InterceptionRedirect, - ports: anyPorts, - expectListener: true, - }, - { - name: "privileged proxy; explicit TPROXY mode; any ports", - unprivileged: false, - mode: model.InterceptionTproxy, - ports: anyPorts, - expectListener: true, - }, - { - name: "privileged proxy; explicit NONE mode; any ports", - unprivileged: false, - mode: model.InterceptionNone, - ports: anyPorts, - expectListener: true, - }, - { - name: "unprivileged proxy; implicit REDIRECT mode; any ports", - unprivileged: true, - mode: "", - ports: anyPorts, - expectListener: true, - }, - { - name: "unprivileged proxy; explicit REDIRECT mode; any ports", - unprivileged: true, - mode: model.InterceptionRedirect, - ports: anyPorts, - expectListener: true, - }, - { - name: "unprivileged proxy; explicit TPROXY mode; any ports", - unprivileged: true, - mode: model.InterceptionTproxy, - ports: anyPorts, - expectListener: true, - }, - { - name: "unprivileged proxy; explicit NONE mode; privileged ports", - unprivileged: true, - mode: model.InterceptionNone, - ports: privilegedPorts, - expectListener: false, - }, - { - name: "unprivileged proxy; explicit NONE mode; unprivileged ports", - unprivileged: true, - mode: model.InterceptionNone, - ports: unprivilegedPorts, - expectListener: true, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - for _, port := range tc.ports { - t.Run(strconv.Itoa(int(port)), func(t *testing.T) { - proxy := getProxy() - proxy.Metadata.UnprivilegedPod = strconv.FormatBool(tc.unprivileged) - proxy.Metadata.InterceptionMode = tc.mode - - listeners := buildListeners(t, proxy, port) - found := hasListenerOrFilterChainForPort(listeners, port) - if tc.expectListener { - if !found { - t.Fatalf("expected listener on port %d, but not found", port) - } - } else { - if found { - t.Fatalf("expected no listener on port %d, but found found one", port) - } - } - }) - } - }) - } -} - -func testOutboundListenerConflictWithSniffingDisabled(t *testing.T, services ...*model.Service) { - t.Helper() - - test.SetBoolForTest(t, &features.EnableProtocolSniffingForOutbound, false) - - oldestService := getOldestService(services...) - - listeners := buildOutboundListeners(t, getProxy(), nil, nil, services...) - if len(listeners) != 1 { - t.Fatalf("expected %d listeners, found %d", 1, len(listeners)) - } - - oldestProtocol := oldestService.Ports[0].Protocol - if oldestProtocol != protocol.HTTP && isHTTPListener(listeners[0]) { - t.Fatal("expected TCP listener, found HTTP") - } else if oldestProtocol == protocol.HTTP && !isHTTPListener(listeners[0]) { - t.Fatal("expected HTTP listener, found TCP") - } -} - -func testOutboundListenerRoute(t *testing.T, services ...*model.Service) { - t.Helper() - listeners := buildOutboundListeners(t, getProxy(), nil, nil, services...) - if len(listeners) != 3 { - t.Fatalf("expected %d listeners, found %d", 3, len(listeners)) - } - - l := findListenerByAddress(listeners, wildcardIP) - if l == nil { - t.Fatalf("expect listener %s", "0.0.0.0_8080") - } - - f := l.FilterChains[0].Filters[0] - cfg, _ := conversion.MessageToStruct(f.GetTypedConfig()) - rds := cfg.Fields["rds"].GetStructValue().Fields["route_config_name"].GetStringValue() - if rds != "8080" { - t.Fatalf("expect routes %s, found %s", "8080", rds) - } - - l = findListenerByAddress(listeners, "1.2.3.4") - if l == nil { - t.Fatalf("expect listener %s", "1.2.3.4_8080") - } - f = l.FilterChains[0].Filters[0] - cfg, _ = conversion.MessageToStruct(f.GetTypedConfig()) - rds = cfg.Fields["rds"].GetStructValue().Fields["route_config_name"].GetStringValue() - if rds != "test1.com:8080" { - t.Fatalf("expect routes %s, found %s", "test1.com:8080", rds) - } - - l = findListenerByAddress(listeners, "3.4.5.6") - if l == nil { - t.Fatalf("expect listener %s", "3.4.5.6_8080") - } - f = l.FilterChains[0].Filters[0] - cfg, _ = conversion.MessageToStruct(f.GetTypedConfig()) - rds = cfg.Fields["rds"].GetStructValue().Fields["route_config_name"].GetStringValue() - if rds != "test3.com:8080" { - t.Fatalf("expect routes %s, found %s", "test3.com:8080", rds) - } -} - -func testOutboundListenerFilterTimeout(t *testing.T, services ...*model.Service) { - listeners := buildOutboundListeners(t, getProxy(), nil, nil, services...) - if len(listeners) != 2 { - t.Fatalf("expected %d listeners, found %d", 2, len(listeners)) - } - - if listeners[0].ContinueOnListenerFiltersTimeout { - t.Fatalf("expected timeout disabled, found ContinueOnListenerFiltersTimeout %v", - listeners[0].ContinueOnListenerFiltersTimeout) - } - - if !listeners[1].ContinueOnListenerFiltersTimeout || listeners[1].ListenerFiltersTimeout == nil { - t.Fatalf("expected timeout enabled, found ContinueOnListenerFiltersTimeout %v, ListenerFiltersTimeout %v", - listeners[1].ContinueOnListenerFiltersTimeout, - listeners[1].ListenerFiltersTimeout) - } -} - -func testOutboundListenerConflict(t *testing.T, services ...*model.Service) { - oldestService := getOldestService(services...) - proxy := getProxy() - proxy.DiscoverIPMode() - listeners := buildOutboundListeners(t, getProxy(), nil, nil, services...) - if len(listeners) != 1 { - t.Fatalf("expected %d listeners, found %d", 1, len(listeners)) - } - - oldestProtocol := oldestService.Ports[0].Protocol - if oldestProtocol == protocol.MySQL { - if len(listeners[0].FilterChains) != 1 { - t.Fatalf("expected %d filter chains, found %d", 1, len(listeners[0].FilterChains)) - } else if !isTCPFilterChain(listeners[0].FilterChains[0]) { - t.Fatalf("expected tcp filter chain, found %s", listeners[0].FilterChains[1].Filters[0].Name) - } - } else if oldestProtocol != protocol.HTTP && oldestProtocol != protocol.TCP { - if len(listeners[0].FilterChains) != 1 { - t.Fatalf("expectd %d filter chains, found %d", 1, len(listeners[0].FilterChains)) - } - if !isHTTPFilterChain(listeners[0].FilterChains[0]) { - t.Fatalf("expected http filter chain, found %s", listeners[0].FilterChains[0].Filters[0].Name) - } - - if !isTCPFilterChain(listeners[0].DefaultFilterChain) { - t.Fatalf("expected tcp filter chain, found %s", listeners[0].DefaultFilterChain.Filters[0].Name) - } - - verifyHTTPFilterChainMatch(t, listeners[0].FilterChains[0]) - verifyListenerFilters(t, listeners[0].ListenerFilters) - - if !listeners[0].ContinueOnListenerFiltersTimeout || listeners[0].ListenerFiltersTimeout == nil { - t.Fatalf("exptected timeout, found ContinueOnListenerFiltersTimeout %v, ListenerFiltersTimeout %v", - listeners[0].ContinueOnListenerFiltersTimeout, - listeners[0].ListenerFiltersTimeout) - } - - f := listeners[0].FilterChains[0].Filters[0] - cfg, _ := conversion.MessageToStruct(f.GetTypedConfig()) - rds := cfg.Fields["rds"].GetStructValue().Fields["route_config_name"].GetStringValue() - expect := fmt.Sprintf("%d", oldestService.Ports[0].Port) - if rds != expect { - t.Fatalf("expect routes %s, found %s", expect, rds) - } - } else { - if len(listeners[0].FilterChains) != 1 { - t.Fatalf("expectd %d filter chains, found %d", 1, len(listeners[0].FilterChains)) - } - if listeners[0].DefaultFilterChain == nil { - t.Fatalf("expected default filter chains, found none") - } - - _ = getTCPFilterChain(t, listeners[0]) - http := getHTTPFilterChain(t, listeners[0]) - - verifyHTTPFilterChainMatch(t, http) - verifyListenerFilters(t, listeners[0].ListenerFilters) - - if !listeners[0].ContinueOnListenerFiltersTimeout || listeners[0].ListenerFiltersTimeout == nil { - t.Fatalf("exptected timeout, found ContinueOnListenerFiltersTimeout %v, ListenerFiltersTimeout %v", - listeners[0].ContinueOnListenerFiltersTimeout, - listeners[0].ListenerFiltersTimeout) - } - } -} - -func getFilterChains(l *listener.Listener) []*listener.FilterChain { - res := l.FilterChains - if l.DefaultFilterChain != nil { - res = append(res, l.DefaultFilterChain) - } - return res -} - -func getTCPFilterChain(t *testing.T, l *listener.Listener) *listener.FilterChain { - t.Helper() - for _, fc := range getFilterChains(l) { - for _, f := range fc.Filters { - if f.Name == wellknown.TCPProxy { - return fc - } - } - } - t.Fatalf("tcp filter chain not found") - return nil -} - -func getTCPFilter(fc *listener.FilterChain) *listener.Filter { - for _, f := range fc.Filters { - if f.Name == wellknown.TCPProxy { - return f - } - } - return nil -} - -func getHTTPFilter(fc *listener.FilterChain) *listener.Filter { - for _, f := range fc.Filters { - if f.Name == wellknown.HTTPConnectionManager { - return f - } - } - return nil -} - -func getHTTPFilterChain(t *testing.T, l *listener.Listener) *listener.FilterChain { - t.Helper() - for _, fc := range getFilterChains(l) { - for _, f := range fc.Filters { - if f.Name == wellknown.HTTPConnectionManager { - return fc - } - } - } - t.Fatalf("tcp filter chain not found") - return nil -} - -func testInboundListenerConfig(t *testing.T, proxy *model.Proxy, services ...*model.Service) { - t.Helper() - listeners := buildListeners(t, TestOptions{Services: services}, proxy) - verifyFilterChainMatch(t, xdstest.ExtractListener(model.VirtualInboundListenerName, listeners)) -} - -func testInboundListenerConfigWithGrpc(t *testing.T, proxy *model.Proxy, services ...*model.Service) { - t.Helper() - listeners := buildListeners(t, TestOptions{Services: services}, proxy) - l := xdstest.ExtractListener(model.VirtualInboundListenerName, listeners) - listenertest.VerifyListener(t, l, listenertest.ListenerTest{ - FilterChains: []listenertest.FilterChainTest{ - { - Port: 8080, - HTTPFilters: []string{wellknown.HTTPGRPCStats}, - }, - }, - }) -} - -func testInboundListenerConfigWithSidecar(t *testing.T, proxy *model.Proxy, services ...*model.Service) { - t.Helper() - sidecarConfig := config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "not-default", - GroupVersionKind: gvk.Sidecar, - }, - Spec: &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{ - Number: 8080, - Protocol: "unknown", - Name: "uds", - }, - Bind: "1.1.1.1", - DefaultEndpoint: "127.0.0.1:80", - }, - }, - }, - } - listeners := buildListeners(t, TestOptions{ - Services: services, - Configs: []config.Config{sidecarConfig}, - }, proxy) - l := xdstest.ExtractListener(model.VirtualInboundListenerName, listeners) - verifyFilterChainMatch(t, l) -} - -func testInboundListenerConfigWithSidecarWithoutServices(t *testing.T, proxy *model.Proxy) { - t.Helper() - - sidecarConfig := config.Config{ - Meta: config.Meta{ - Name: "foo-without-service", - Namespace: "not-default", - GroupVersionKind: gvk.Sidecar, - }, - Spec: &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{ - Number: 8080, - Protocol: "unknown", - Name: "uds", - }, - Bind: "1.1.1.1", - DefaultEndpoint: "127.0.0.1:80", - }, - }, - }, - } - listeners := buildListeners(t, TestOptions{ - Configs: []config.Config{sidecarConfig}, - }, proxy) - l := xdstest.ExtractListener(model.VirtualInboundListenerName, listeners) - verifyFilterChainMatch(t, l) -} - -func testInboundListenerConfigWithoutService(t *testing.T, proxy *model.Proxy) { - t.Helper() - listeners := buildListeners(t, TestOptions{}, proxy) - l := xdstest.ExtractListener(model.VirtualInboundListenerName, listeners) - verifyFilterChainMatch(t, l) -} - -func verifyListenerFilters(t *testing.T, lfilters []*listener.ListenerFilter) { - t.Helper() - if len(lfilters) != 2 { - t.Fatalf("expected %d listener filter, found %d", 2, len(lfilters)) - } - if lfilters[0].Name != wellknown.TlsInspector || - lfilters[1].Name != wellknown.HttpInspector { - t.Fatalf("expected listener filters not found, got %v", lfilters) - } -} - -func verifyHTTPFilterChainMatch(t *testing.T, fc *listener.FilterChain) { - t.Helper() - if fc.FilterChainMatch.TransportProtocol != xdsfilters.RawBufferTransportProtocol { - t.Fatalf("exepct %q transport protocol, found %q", xdsfilters.RawBufferTransportProtocol, fc.FilterChainMatch.TransportProtocol) - } - - if !reflect.DeepEqual(plaintextHTTPALPNs, fc.FilterChainMatch.ApplicationProtocols) { - t.Fatalf("expected %d application protocols, %v got %v", - len(plaintextHTTPALPNs), plaintextHTTPALPNs, fc.FilterChainMatch.ApplicationProtocols) - } - - hcm := &hcm.HttpConnectionManager{} - if err := getFilterConfig(getHTTPFilter(fc), hcm); err != nil { - t.Fatalf("failed to get HCM, config %v", hcm) - } - - hasAlpn := hasAlpnFilter(hcm.HttpFilters) - - if !hasAlpn { - t.Fatal("ALPN filter is not found") - } -} - -func hasAlpnFilter(filters []*hcm.HttpFilter) bool { - for _, f := range filters { - if f.Name == xdsfilters.AlpnFilterName { - return true - } - } - return false -} - -func isHTTPFilterChain(fc *listener.FilterChain) bool { - return getHTTPFilter(fc) != nil -} - -func isTCPFilterChain(fc *listener.FilterChain) bool { - return getTCPFilter(fc) != nil -} - -func testOutboundListenerConfigWithSidecar(t *testing.T, services ...*model.Service) { - t.Helper() - sidecarConfig := &config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "not-default", - GroupVersionKind: gvk.Sidecar, - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Number: 9000, - Protocol: "GRPC", - Name: "uds", - }, - Hosts: []string{"*/*"}, - }, - { - Port: &networking.Port{ - Number: 3306, - Protocol: string(protocol.MySQL), - Name: "MySQL", - }, - Bind: "8.8.8.8", - Hosts: []string{"*/*"}, - }, - { - Port: &networking.Port{ - Number: 8888, - Protocol: "unknown", - Name: "unknown", - }, - Bind: "2.2.2.2", - Hosts: []string{"*/*"}, - }, - { - Hosts: []string{"*/*"}, - }, - }, - }, - } - - // enable mysql filter that is used here - test.SetBoolForTest(t, &features.EnableMysqlFilter, true) - - listeners := buildOutboundListeners(t, getProxy(), sidecarConfig, nil, services...) - if len(listeners) != 4 { - t.Fatalf("expected %d listeners, found %d", 4, len(listeners)) - } - - l := findListenerByPort(listeners, 8080) - if len(l.FilterChains) != 1 { - t.Fatalf("expectd %d filter chains, found %d", 1, len(l.FilterChains)) - } - if !isHTTPFilterChain(l.FilterChains[0]) { - t.Fatalf("expected http filter chain, found %s", l.FilterChains[0].Filters[0].Name) - } - - if !isTCPFilterChain(l.DefaultFilterChain) { - t.Fatalf("expected tcp filter chain, found %s", l.DefaultFilterChain.Filters[0].Name) - } - - verifyHTTPFilterChainMatch(t, l.FilterChains[0]) - verifyListenerFilters(t, l.ListenerFilters) - - if l := findListenerByPort(listeners, 3306); !isMysqlListener(l) { - t.Fatalf("expected MySQL listener on port 3306, found %v", l) - } - - if l := findListenerByPort(listeners, 9000); !isHTTPListener(l) { - t.Fatalf("expected HTTP listener on port 9000, found TCP\n%v", l) - } - - l = findListenerByPort(listeners, 8888) - if len(l.FilterChains) != 1 { - t.Fatalf("expected %d filter chains, found %d", 1, len(l.FilterChains)) - } - if !isHTTPFilterChain(l.FilterChains[0]) { - t.Fatalf("expected http filter chain, found %s", l.FilterChains[0].Filters[0].Name) - } - - if !isTCPFilterChain(l.DefaultFilterChain) { - t.Fatalf("expected tcp filter chain, found %s", l.DefaultFilterChain.Filters[0].Name) - } - - verifyHTTPFilterChainMatch(t, l.FilterChains[0]) - verifyListenerFilters(t, l.ListenerFilters) -} - -func testOutboundListenerConfigWithSidecarWithSniffingDisabled(t *testing.T, services ...*model.Service) { - t.Helper() - sidecarConfig := &config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "not-default", - GroupVersionKind: gvk.Sidecar, - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Number: 9000, - Protocol: "HTTP", - Name: "uds", - }, - Bind: "1.1.1.1", - Hosts: []string{"*/*"}, - }, - { - Port: &networking.Port{ - Number: 3306, - Protocol: string(protocol.MySQL), - Name: "MySQL", - }, - Bind: "8.8.8.8", - Hosts: []string{"*/*"}, - }, - { - Hosts: []string{"*/*"}, - }, - }, - }, - } - - // enable mysql filter that is used here - test.SetBoolForTest(t, &features.EnableMysqlFilter, true) - - listeners := buildOutboundListeners(t, getProxy(), sidecarConfig, nil, services...) - if len(listeners) != 1 { - t.Fatalf("expected %d listeners, found %d", 1, len(listeners)) - } - - if l := findListenerByPort(listeners, 8080); isHTTPListener(l) { - t.Fatalf("expected TCP listener on port 8080, found HTTP: %v", l) - } -} - -func testOutboundListenerConfigWithSidecarWithUseRemoteAddress(t *testing.T, services ...*model.Service) { - t.Helper() - sidecarConfig := &config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "not-default", - GroupVersionKind: gvk.Sidecar, - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Number: 9090, - Protocol: "HTTP", - Name: "uds", - }, - Bind: "1.1.1.1", - Hosts: []string{"*/*"}, - }, - }, - }, - } - - // enable use remote address to true - test.SetBoolForTest(t, &features.UseRemoteAddress, true) - - listeners := buildOutboundListeners(t, getProxy(), sidecarConfig, nil, services...) - - if l := findListenerByPort(listeners, 9090); !isHTTPListener(l) { - t.Fatalf("expected HTTP listener on port 9090, found TCP\n%v", l) - } else { - f := l.FilterChains[0].Filters[0] - cfg, _ := conversion.MessageToStruct(f.GetTypedConfig()) - if useRemoteAddress, exists := cfg.Fields["use_remote_address"]; exists { - if !exists || !useRemoteAddress.GetBoolValue() { - t.Fatalf("expected useRemoteAddress true, found false %v", l) - } - } - } -} - -func testOutboundListenerConfigWithSidecarWithCaptureModeNone(t *testing.T, services ...*model.Service) { - t.Helper() - sidecarConfig := &config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "not-default", - GroupVersionKind: gvk.Sidecar, - }, - Spec: &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - // Bind + Port - CaptureMode: networking.CaptureMode_NONE, - Port: &networking.Port{ - Number: 9000, - Protocol: "HTTP", - Name: "grpc", - }, - Bind: "127.1.1.2", - Hosts: []string{"*/*"}, - }, - { - // Bind Only - CaptureMode: networking.CaptureMode_NONE, - Bind: "127.1.1.2", - Hosts: []string{"*/*"}, - }, - { - // Port Only - CaptureMode: networking.CaptureMode_NONE, - Port: &networking.Port{ - Number: 9000, - Protocol: "HTTP", - Name: "grpc", - }, - Hosts: []string{"*/*"}, - }, - { - // None - CaptureMode: networking.CaptureMode_NONE, - Hosts: []string{"*/*"}, - }, - }, - }, - } - listeners := buildOutboundListeners(t, getProxy(), sidecarConfig, nil, services...) - if len(listeners) != 4 { - t.Fatalf("expected %d listeners, found %d", 4, len(listeners)) - } - - expectedListeners := map[string]string{ - "127.1.1.2_9090": "HTTP", - "127.1.1.2_8080": "TCP", - "127.0.0.1_9090": "HTTP", - "127.0.0.1_8080": "TCP", - } - - for _, l := range listeners { - listenerName := l.Name - expectedListenerType := expectedListeners[listenerName] - if expectedListenerType == "" { - t.Fatalf("listener %s not expected", listenerName) - } - if expectedListenerType == "TCP" && isHTTPListener(l) { - t.Fatalf("expected TCP listener %s, but found HTTP", listenerName) - } - if expectedListenerType == "HTTP" && !isHTTPListener(l) { - t.Fatalf("expected HTTP listener %s, but found TCP", listenerName) - } - if l.ConnectionBalanceConfig != nil { - t.Fatalf("expected connection balance config to be nil, found %v", l.ConnectionBalanceConfig) - } - } - - if l := findListenerByPort(listeners, 9090); !isHTTPListener(l) { - t.Fatalf("expected HTTP listener on port 9090, but not found\n%v", l) - } else { - f := l.FilterChains[0].Filters[0] - cfg, _ := conversion.MessageToStruct(f.GetTypedConfig()) - if useRemoteAddress, exists := cfg.Fields["use_remote_address"]; exists { - if exists && useRemoteAddress.GetBoolValue() { - t.Fatalf("expected useRemoteAddress false, found true %v", l) - } - } - } -} - -func TestVirtualListeners_TrafficRedirectionEnabled(t *testing.T) { - cases := []struct { - name string - mode model.TrafficInterceptionMode - }{ - { - name: "empty value", - mode: "", - }, - { - name: "unknown value", - mode: model.TrafficInterceptionMode("UNKNOWN_VALUE"), - }, - { - name: string(model.InterceptionTproxy), - mode: model.InterceptionTproxy, - }, - { - name: string(model.InterceptionRedirect), - mode: model.InterceptionRedirect, - }, - } - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - listeners := buildListeners(t, TestOptions{}, &model.Proxy{Metadata: &model.NodeMetadata{InterceptionMode: tc.mode}}) - - if l := xdstest.ExtractListener(model.VirtualInboundListenerName, listeners); l == nil { - t.Fatalf("did not generate virtual inbound listener") - } - - if l := xdstest.ExtractListener(model.VirtualOutboundListenerName, listeners); l == nil { - t.Fatalf("did not generate virtual outbound listener") - } - }) - } -} - -func TestVirtualListeners_TrafficRedirectionDisabled(t *testing.T) { - listeners := buildListeners(t, TestOptions{}, &model.Proxy{Metadata: &model.NodeMetadata{InterceptionMode: model.InterceptionNone}}) - if l := xdstest.ExtractListener(model.VirtualInboundListenerName, listeners); l != nil { - t.Fatalf("unexpectedly generated virtual inbound listener") - } - - if l := xdstest.ExtractListener(model.VirtualOutboundListenerName, listeners); l != nil { - t.Fatalf("unexpectedly generated virtual outbound listener") - } -} - -func TestOutboundListenerAccessLogs(t *testing.T) { - m := mesh.DefaultMeshConfig() - m.AccessLogFile = "foo" - listeners := buildListeners(t, TestOptions{MeshConfig: m}, nil) - validateAccessLog(t, xdstest.ExtractListener(model.VirtualOutboundListenerName, listeners), "") - - // Update MeshConfig - m.AccessLogFormat = "format modified" - // Trigger MeshConfig change and validate that access log is recomputed. - accessLogBuilder.reset() - listeners = buildListeners(t, TestOptions{MeshConfig: m}, nil) - - // Validate that access log filter uses the new format. - validateAccessLog(t, xdstest.ExtractListener(model.VirtualOutboundListenerName, listeners), "format modified") -} - -func TestListenerAccessLogs(t *testing.T) { - t.Helper() - m := mesh.DefaultMeshConfig() - m.AccessLogFile = "foo" - listeners := buildListeners(t, TestOptions{MeshConfig: m}, nil) - for _, l := range listeners { - if l.AccessLog == nil { - t.Fatalf("expected access log configuration for %v", l) - } - if l.AccessLog[0].Filter == nil { - t.Fatal("expected filter config in listener access log configuration") - } - } -} - -func validateAccessLog(t *testing.T, l *listener.Listener, format string) { - t.Helper() - if l == nil { - t.Fatalf("nil listener") - } - - fc := &tcp.TcpProxy{} - if err := getFilterConfig(xdstest.ExtractFilterChain("virtualOutbound-catchall-tcp", l).Filters[0], fc); err != nil { - t.Fatalf("failed to get TCP Proxy config: %s", err) - } - if fc.AccessLog == nil { - t.Fatal("expected access log configuration") - } - cfg, _ := conversion.MessageToStruct(fc.AccessLog[0].GetTypedConfig()) - textFormat := cfg.GetFields()["log_format"].GetStructValue().GetFields()["text_format_source"].GetStructValue(). - GetFields()["inline_string"].GetStringValue() - if format != "" && textFormat != format { - t.Fatalf("expected format to be %s, but got %s", format, textFormat) - } -} - -func TestHttpProxyListener(t *testing.T) { - m := mesh.DefaultMeshConfig() - m.ProxyHttpPort = 15007 - listeners := buildListeners(t, TestOptions{MeshConfig: m}, nil) - httpProxy := xdstest.ExtractListener("127.0.0.1_15007", listeners) - f := httpProxy.FilterChains[0].Filters[0] - cfg, _ := conversion.MessageToStruct(f.GetTypedConfig()) - - if httpProxy.Address.GetSocketAddress().GetPortValue() != 15007 { - t.Fatalf("expected http proxy is not listening on %d, but on port %d", 15007, - httpProxy.Address.GetSocketAddress().GetPortValue()) - } - if !strings.HasPrefix(cfg.Fields["stat_prefix"].GetStringValue(), "outbound_") { - t.Fatalf("expected http proxy stat prefix to have outbound, %s", cfg.Fields["stat_prefix"].GetStringValue()) - } -} - -func TestHttpProxyListenerPerWorkload(t *testing.T) { - listeners := buildListeners(t, TestOptions{}, &model.Proxy{Metadata: &model.NodeMetadata{HTTPProxyPort: "15007"}}) - httpProxy := xdstest.ExtractListener("127.0.0.1_15007", listeners) - f := httpProxy.FilterChains[0].Filters[0] - cfg, _ := conversion.MessageToStruct(f.GetTypedConfig()) - - if httpProxy.Address.GetSocketAddress().GetPortValue() != 15007 { - t.Fatalf("expected http proxy is not listening on %d, but on port %d", 15007, - httpProxy.Address.GetSocketAddress().GetPortValue()) - } - if !strings.HasPrefix(cfg.Fields["stat_prefix"].GetStringValue(), "outbound_") { - t.Fatalf("expected http proxy stat prefix to have outbound, %s", cfg.Fields["stat_prefix"].GetStringValue()) - } -} - -func TestHttpProxyListener_Tracing(t *testing.T) { - customTagsTest := []struct { - name string - in *meshconfig.Tracing - out *hcm.HttpConnectionManager_Tracing - tproxy *model.Proxy - envPilotSampling float64 - disableIstioTags bool - }{ - { - name: "random-sampling-env", - tproxy: getProxy(), - envPilotSampling: 80.0, - in: &meshconfig.Tracing{ - Tracer: nil, - CustomTags: nil, - MaxPathTagLength: 0, - Sampling: 0, - }, - out: &hcm.HttpConnectionManager_Tracing{ - MaxPathTagLength: nil, - ClientSampling: &xdstype.Percent{ - Value: 100.0, - }, - RandomSampling: &xdstype.Percent{ - Value: 80.0, - }, - OverallSampling: &xdstype.Percent{ - Value: 100.0, - }, - CustomTags: customTracingTags(), - }, - }, - { - name: "random-sampling-env-without-istio-tags", - disableIstioTags: true, - tproxy: getProxy(), - envPilotSampling: 80.0, - in: &meshconfig.Tracing{ - Tracer: nil, - CustomTags: nil, - MaxPathTagLength: 0, - Sampling: 0, - }, - out: &hcm.HttpConnectionManager_Tracing{ - MaxPathTagLength: nil, - ClientSampling: &xdstype.Percent{ - Value: 100.0, - }, - RandomSampling: &xdstype.Percent{ - Value: 80.0, - }, - OverallSampling: &xdstype.Percent{ - Value: 100.0, - }, - }, - }, - { - name: "random-sampling-env-and-meshconfig", - tproxy: getProxy(), - envPilotSampling: 80.0, - in: &meshconfig.Tracing{ - Tracer: nil, - CustomTags: nil, - MaxPathTagLength: 0, - Sampling: 10, - }, - out: &hcm.HttpConnectionManager_Tracing{ - MaxPathTagLength: nil, - ClientSampling: &xdstype.Percent{ - Value: 100.0, - }, - RandomSampling: &xdstype.Percent{ - Value: 10.0, - }, - OverallSampling: &xdstype.Percent{ - Value: 100.0, - }, - CustomTags: customTracingTags(), - }, - }, - { - name: "random-sampling-too-low-env", - tproxy: getProxy(), - envPilotSampling: -1, - in: &meshconfig.Tracing{ - Tracer: nil, - CustomTags: nil, - MaxPathTagLength: 0, - Sampling: 300, - }, - out: &hcm.HttpConnectionManager_Tracing{ - MaxPathTagLength: nil, - ClientSampling: &xdstype.Percent{ - Value: 100.0, - }, - RandomSampling: &xdstype.Percent{ - Value: 1.0, - }, - OverallSampling: &xdstype.Percent{ - Value: 100.0, - }, - CustomTags: customTracingTags(), - }, - }, - { - name: "random-sampling-too-high-meshconfig", - tproxy: getProxy(), - envPilotSampling: 80.0, - in: &meshconfig.Tracing{ - Tracer: nil, - CustomTags: nil, - MaxPathTagLength: 0, - Sampling: 300, - }, - out: &hcm.HttpConnectionManager_Tracing{ - MaxPathTagLength: nil, - ClientSampling: &xdstype.Percent{ - Value: 100.0, - }, - RandomSampling: &xdstype.Percent{ - Value: 1.0, - }, - OverallSampling: &xdstype.Percent{ - Value: 100.0, - }, - CustomTags: customTracingTags(), - }, - }, - { - name: "random-sampling-too-high-env", - tproxy: getProxy(), - envPilotSampling: 2000.0, - in: &meshconfig.Tracing{ - Tracer: nil, - CustomTags: nil, - MaxPathTagLength: 0, - Sampling: 300, - }, - out: &hcm.HttpConnectionManager_Tracing{ - MaxPathTagLength: nil, - ClientSampling: &xdstype.Percent{ - Value: 100.0, - }, - RandomSampling: &xdstype.Percent{ - Value: 1.0, - }, - OverallSampling: &xdstype.Percent{ - Value: 100.0, - }, - CustomTags: customTracingTags(), - }, - }, - { - // upstream will set the default to 256 per - // its documentation - name: "tag-max-path-length-not-set-default", - tproxy: getProxy(), - in: &meshconfig.Tracing{ - Tracer: nil, - CustomTags: nil, - MaxPathTagLength: 0, - Sampling: 0, - }, - out: &hcm.HttpConnectionManager_Tracing{ - MaxPathTagLength: nil, - ClientSampling: &xdstype.Percent{ - Value: 100.0, - }, - RandomSampling: &xdstype.Percent{ - Value: 1.0, - }, - OverallSampling: &xdstype.Percent{ - Value: 100.0, - }, - CustomTags: customTracingTags(), - }, - }, - { - name: "tag-max-path-length-set-to-1024", - tproxy: getProxy(), - in: &meshconfig.Tracing{ - Tracer: nil, - CustomTags: nil, - MaxPathTagLength: 1024, - Sampling: 0, - }, - out: &hcm.HttpConnectionManager_Tracing{ - MaxPathTagLength: &wrappers.UInt32Value{ - Value: 1024, - }, - ClientSampling: &xdstype.Percent{ - Value: 100.0, - }, - RandomSampling: &xdstype.Percent{ - Value: 1.0, - }, - OverallSampling: &xdstype.Percent{ - Value: 100.0, - }, - CustomTags: customTracingTags(), - }, - }, - { - name: "custom-tags-sidecar", - tproxy: getProxy(), - in: &meshconfig.Tracing{ - CustomTags: map[string]*meshconfig.Tracing_CustomTag{ - "custom_tag_env": { - Type: &meshconfig.Tracing_CustomTag_Environment{ - Environment: &meshconfig.Tracing_Environment{ - Name: "custom_tag_env-var", - DefaultValue: "custom-tag-env-default", - }, - }, - }, - "custom_tag_request_header": { - Type: &meshconfig.Tracing_CustomTag_Header{ - Header: &meshconfig.Tracing_RequestHeader{ - Name: "custom_tag_request_header_name", - DefaultValue: "custom-defaulted-value-request-header", - }, - }, - }, - // leave this in non-alphanumeric order to verify - // the stable sorting doing when creating the custom tag filter - "custom_tag_literal": { - Type: &meshconfig.Tracing_CustomTag_Literal{ - Literal: &meshconfig.Tracing_Literal{ - Value: "literal-value", - }, - }, - }, - }, - }, - out: &hcm.HttpConnectionManager_Tracing{ - ClientSampling: &xdstype.Percent{ - Value: 100.0, - }, - RandomSampling: &xdstype.Percent{ - Value: 1.0, - }, - OverallSampling: &xdstype.Percent{ - Value: 100.0, - }, - CustomTags: append([]*tracing.CustomTag{ - { - Tag: "custom_tag_env", - Type: &tracing.CustomTag_Environment_{ - Environment: &tracing.CustomTag_Environment{ - Name: "custom_tag_env-var", - DefaultValue: "custom-tag-env-default", - }, - }, - }, - { - Tag: "custom_tag_literal", - Type: &tracing.CustomTag_Literal_{ - Literal: &tracing.CustomTag_Literal{ - Value: "literal-value", - }, - }, - }, - { - Tag: "custom_tag_request_header", - Type: &tracing.CustomTag_RequestHeader{ - RequestHeader: &tracing.CustomTag_Header{ - Name: "custom_tag_request_header_name", - DefaultValue: "custom-defaulted-value-request-header", - }, - }, - }, - }, customTracingTags()...), - }, - }, - { - name: "custom-tags-sidecar-without-istio-tags", - disableIstioTags: true, - tproxy: getProxy(), - in: &meshconfig.Tracing{ - CustomTags: map[string]*meshconfig.Tracing_CustomTag{ - "custom_tag_env": { - Type: &meshconfig.Tracing_CustomTag_Environment{ - Environment: &meshconfig.Tracing_Environment{ - Name: "custom_tag_env-var", - DefaultValue: "custom-tag-env-default", - }, - }, - }, - "custom_tag_request_header": { - Type: &meshconfig.Tracing_CustomTag_Header{ - Header: &meshconfig.Tracing_RequestHeader{ - Name: "custom_tag_request_header_name", - DefaultValue: "custom-defaulted-value-request-header", - }, - }, - }, - // leave this in non-alphanumeric order to verify - // the stable sorting doing when creating the custom tag filter - "custom_tag_literal": { - Type: &meshconfig.Tracing_CustomTag_Literal{ - Literal: &meshconfig.Tracing_Literal{ - Value: "literal-value", - }, - }, - }, - }, - }, - out: &hcm.HttpConnectionManager_Tracing{ - ClientSampling: &xdstype.Percent{ - Value: 100.0, - }, - RandomSampling: &xdstype.Percent{ - Value: 1.0, - }, - OverallSampling: &xdstype.Percent{ - Value: 100.0, - }, - CustomTags: []*tracing.CustomTag{ - { - Tag: "custom_tag_env", - Type: &tracing.CustomTag_Environment_{ - Environment: &tracing.CustomTag_Environment{ - Name: "custom_tag_env-var", - DefaultValue: "custom-tag-env-default", - }, - }, - }, - { - Tag: "custom_tag_literal", - Type: &tracing.CustomTag_Literal_{ - Literal: &tracing.CustomTag_Literal{ - Value: "literal-value", - }, - }, - }, - { - Tag: "custom_tag_request_header", - Type: &tracing.CustomTag_RequestHeader{ - RequestHeader: &tracing.CustomTag_Header{ - Name: "custom_tag_request_header_name", - DefaultValue: "custom-defaulted-value-request-header", - }, - }, - }, - }, - }, - }, - { - name: "custom-tracing-gateways", - tproxy: &proxyGateway, - in: &meshconfig.Tracing{ - MaxPathTagLength: 100, - CustomTags: map[string]*meshconfig.Tracing_CustomTag{ - "custom_tag_request_header": { - Type: &meshconfig.Tracing_CustomTag_Header{ - Header: &meshconfig.Tracing_RequestHeader{ - Name: "custom_tag_request_header_name", - DefaultValue: "custom-defaulted-value-request-header", - }, - }, - }, - }, - }, - out: &hcm.HttpConnectionManager_Tracing{ - ClientSampling: &xdstype.Percent{ - Value: 100.0, - }, - RandomSampling: &xdstype.Percent{ - Value: 1.0, - }, - OverallSampling: &xdstype.Percent{ - Value: 100.0, - }, - MaxPathTagLength: &wrappers.UInt32Value{ - Value: 100, - }, - CustomTags: append([]*tracing.CustomTag{ - { - Tag: "custom_tag_request_header", - Type: &tracing.CustomTag_RequestHeader{ - RequestHeader: &tracing.CustomTag_Header{ - Name: "custom_tag_request_header_name", - DefaultValue: "custom-defaulted-value-request-header", - }, - }, - }, - }, customTracingTags()...), - }, - }, - } - for _, tc := range customTagsTest { - t.Run(tc.name, func(t *testing.T) { - if tc.envPilotSampling != 0.0 { - test.SetFloatForTest(t, &features.TraceSampling, tc.envPilotSampling) - } - - test.SetBoolForTest(t, &features.EnableIstioTags, !tc.disableIstioTags) - - m := mesh.DefaultMeshConfig() - m.ProxyHttpPort = 15007 - m.EnableTracing = true - m.DefaultConfig = &meshconfig.ProxyConfig{ - Tracing: &meshconfig.Tracing{ - CustomTags: tc.in.CustomTags, - MaxPathTagLength: tc.in.MaxPathTagLength, - Sampling: tc.in.Sampling, - }, - } - listeners := buildListeners(t, TestOptions{MeshConfig: m}, nil) - httpProxy := xdstest.ExtractListener("127.0.0.1_15007", listeners) - f := httpProxy.FilterChains[0].Filters[0] - verifyHTTPConnectionManagerFilter(t, f, tc.out, tc.name) - }) - } -} - -func customTracingTags() []*tracing.CustomTag { - return append(buildOptionalPolicyTags(), - &tracing.CustomTag{ - Tag: "istio.canonical_revision", - Type: &tracing.CustomTag_Literal_{ - Literal: &tracing.CustomTag_Literal{ - Value: "latest", - }, - }, - }, - &tracing.CustomTag{ - Tag: "istio.canonical_service", - Type: &tracing.CustomTag_Literal_{ - Literal: &tracing.CustomTag_Literal{ - Value: "unknown", - }, - }, - }, - &tracing.CustomTag{ - Tag: "istio.mesh_id", - Type: &tracing.CustomTag_Literal_{ - Literal: &tracing.CustomTag_Literal{ - Value: "unknown", - }, - }, - }, - &tracing.CustomTag{ - Tag: "istio.namespace", - Type: &tracing.CustomTag_Literal_{ - Literal: &tracing.CustomTag_Literal{ - Value: "default", - }, - }, - }) -} - -func verifyHTTPConnectionManagerFilter(t *testing.T, f *listener.Filter, expected *hcm.HttpConnectionManager_Tracing, name string) { - t.Helper() - if f.Name == wellknown.HTTPConnectionManager { - cmgr := &hcm.HttpConnectionManager{} - err := getFilterConfig(f, cmgr) - if err != nil { - t.Fatal(err) - } - - if diff := cmp.Diff(cmgr.GetTracing(), expected, protocmp.Transform()); diff != "" { - t.Fatalf("Testcase failure: %s custom tags did match not expected output; diff: %v", name, diff) - } - } -} - -func TestOutboundListenerConfig_TCPFailThrough(t *testing.T) { - // Add a service and verify it's config - services := []*model.Service{ - buildService("test1.com", wildcardIP, protocol.HTTP, tnow), - } - listeners := buildListeners(t, TestOptions{Services: services}, nil) - l := xdstest.ExtractListener("0.0.0.0_8080", listeners) - if l == nil { - t.Fatalf("failed to find listener") - } - if len(l.FilterChains) != 1 { - t.Fatalf("expectd %d filter chains, found %d", 1, len(l.FilterChains)) - } - - verifyHTTPFilterChainMatch(t, l.FilterChains[0]) - verifyPassThroughTCPFilterChain(t, l.DefaultFilterChain) - verifyListenerFilters(t, l.ListenerFilters) -} - -func verifyPassThroughTCPFilterChain(t *testing.T, fc *listener.FilterChain) { - t.Helper() - f := fc.Filters[0] - expectedStatPrefix := util.PassthroughCluster - cfg, _ := conversion.MessageToStruct(f.GetTypedConfig()) - statPrefix := cfg.Fields["stat_prefix"].GetStringValue() - if statPrefix != expectedStatPrefix { - t.Fatalf("expected listener to contain stat_prefix %s, found %s", expectedStatPrefix, statPrefix) - } -} - -func verifyOutboundTCPListenerHostname(t *testing.T, l *listener.Listener, hostname host.Name) { - t.Helper() - if len(l.FilterChains) != 1 { - t.Fatalf("expected %d filter chains, found %d", 1, len(l.FilterChains)) - } - fc := l.FilterChains[0] - f := getTCPFilter(fc) - if f == nil { - t.Fatalf("expected TCP filters, found none") - } - expectedStatPrefix := fmt.Sprintf("outbound|8080||%s", hostname) - cfg, _ := conversion.MessageToStruct(f.GetTypedConfig()) - statPrefix := cfg.Fields["stat_prefix"].GetStringValue() - if statPrefix != expectedStatPrefix { - t.Fatalf("expected listener to contain stat_prefix %s, found %s", expectedStatPrefix, statPrefix) - } -} - -func verifyFilterChainMatch(t *testing.T, listener *listener.Listener) { - httpFilters := []string{ - xdsfilters.MxFilterName, - xdsfilters.Fault.Name, - xdsfilters.Cors.Name, - xdsfilters.Router.Name, - } - listenertest.VerifyListener(t, listener, listenertest.ListenerTest{ - FilterChains: []listenertest.FilterChainTest{ - { - Name: model.VirtualInboundBlackholeFilterChainName, - Port: 15006, - TotalMatch: true, - }, - { - Name: model.VirtualInboundCatchAllHTTPFilterChainName, - Type: listenertest.MTLSHTTP, - HTTPFilters: httpFilters, - NetworkFilters: []string{xdsfilters.MxFilterName, wellknown.HTTPConnectionManager}, - TotalMatch: true, - }, - { - Name: model.VirtualInboundCatchAllHTTPFilterChainName, - Type: listenertest.PlainHTTP, - HTTPFilters: httpFilters, - NetworkFilters: []string{xdsfilters.MxFilterName, wellknown.HTTPConnectionManager}, - TotalMatch: true, - }, - { - Name: model.VirtualInboundListenerName, - Type: listenertest.MTLSTCP, - HTTPFilters: []string{}, - NetworkFilters: []string{xdsfilters.MxFilterName, wellknown.TCPProxy}, - TotalMatch: true, - }, - { - Name: model.VirtualInboundListenerName, - Type: listenertest.PlainTCP, - HTTPFilters: []string{}, - NetworkFilters: []string{xdsfilters.MxFilterName, wellknown.TCPProxy}, - TotalMatch: true, - }, - { - Name: model.VirtualInboundListenerName, - Type: listenertest.StandardTLS, - HTTPFilters: []string{}, - NetworkFilters: []string{xdsfilters.MxFilterName, wellknown.TCPProxy}, - TotalMatch: true, - }, - }, - }) -} - -func getOldestService(services ...*model.Service) *model.Service { - var oldestService *model.Service - for _, s := range services { - if oldestService == nil || s.CreationTime.Before(oldestService.CreationTime) { - oldestService = s - } - } - return oldestService -} - -func getFilterConfig(filter *listener.Filter, out proto.Message) error { - switch c := filter.ConfigType.(type) { - case *listener.Filter_TypedConfig: - if err := c.TypedConfig.UnmarshalTo(out); err != nil { - return err - } - } - return nil -} - -func buildOutboundListeners(t *testing.T, proxy *model.Proxy, sidecarConfig *config.Config, - virtualService *config.Config, services ...*model.Service) []*listener.Listener { - t.Helper() - cg := NewConfigGenTest(t, TestOptions{ - Services: services, - ConfigPointers: []*config.Config{sidecarConfig, virtualService}, - }) - listeners := NewListenerBuilder(proxy, cg.env.PushContext).buildSidecarOutboundListeners(cg.SetupProxy(proxy), cg.env.PushContext) - xdstest.ValidateListeners(t, listeners) - return listeners -} - -func isHTTPListener(listener *listener.Listener) bool { - for _, fc := range listener.GetFilterChains() { - if isHTTPFilterChain(fc) { - return true - } - } - return false -} - -func isMysqlListener(listener *listener.Listener) bool { - if len(listener.FilterChains) > 0 && len(listener.FilterChains[0].Filters) > 0 { - return listener.FilterChains[0].Filters[0].Name == wellknown.MySQLProxy - } - return false -} - -func hasListenerOrFilterChainForPort(listeners []*listener.Listener, port uint32) bool { - for _, l := range listeners { - if port == l.Address.GetSocketAddress().GetPortValue() { - return true - } - for _, fc := range l.GetFilterChains() { - if fc.GetFilterChainMatch().GetDestinationPort().GetValue() == port { - return true - } - } - } - - return false -} - -func findListenerByPort(listeners []*listener.Listener, port uint32) *listener.Listener { - for _, l := range listeners { - if port == l.Address.GetSocketAddress().GetPortValue() { - return l - } - } - - return nil -} - -func findListenerByAddress(listeners []*listener.Listener, address string) *listener.Listener { - for _, l := range listeners { - if address == l.Address.GetSocketAddress().Address { - return l - } - } - - return nil -} - -func buildService(hostname string, ip string, protocol protocol.Instance, creationTime time.Time) *model.Service { - return &model.Service{ - CreationTime: creationTime, - Hostname: host.Name(hostname), - DefaultAddress: ip, - Ports: model.PortList{ - &model.Port{ - Name: "default", - Port: 8080, - Protocol: protocol, - }, - }, - Resolution: model.ClientSideLB, - Attributes: model.ServiceAttributes{ - Namespace: "default", - }, - } -} - -func buildServiceWithPort(hostname string, port int, protocol protocol.Instance, creationTime time.Time) *model.Service { - return &model.Service{ - CreationTime: creationTime, - Hostname: host.Name(hostname), - DefaultAddress: wildcardIP, - Ports: model.PortList{ - &model.Port{ - Name: "default", - Port: port, - Protocol: protocol, - }, - }, - Resolution: model.ClientSideLB, - Attributes: model.ServiceAttributes{ - Namespace: "default", - }, - } -} - -func buildServiceInstance(service *model.Service, instanceIP string) *model.ServiceInstance { - return &model.ServiceInstance{ - Endpoint: &model.IstioEndpoint{ - Address: instanceIP, - }, - ServicePort: service.Ports[0], - Service: service, - } -} - -func TestAppendListenerFallthroughRouteForCompleteListener(t *testing.T) { - tests := []struct { - name string - node *model.Proxy - hostname string - idleTimeout *durationpb.Duration - }{ - { - name: "Registry_Only", - node: &model.Proxy{ - ID: "foo.bar", - Metadata: &model.NodeMetadata{}, - SidecarScope: &model.SidecarScope{ - OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_REGISTRY_ONLY, - }, - }, - }, - hostname: util.BlackHoleCluster, - }, - { - name: "Allow_Any", - node: &model.Proxy{ - ID: "foo.bar", - Metadata: &model.NodeMetadata{}, - SidecarScope: &model.SidecarScope{ - OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, - }, - }, - }, - hostname: util.PassthroughCluster, - }, - { - name: "idle_timeout", - node: &model.Proxy{ - ID: "foo.bar", - Metadata: &model.NodeMetadata{ - IdleTimeout: "15s", - }, - SidecarScope: &model.SidecarScope{ - OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, - }, - }, - }, - hostname: util.PassthroughCluster, - idleTimeout: durationpb.New(15 * time.Second), - }, - { - name: "invalid_idle_timeout", - node: &model.Proxy{ - ID: "foo.bar", - Metadata: &model.NodeMetadata{ - IdleTimeout: "s15s", - }, - SidecarScope: &model.SidecarScope{ - OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, - }, - }, - }, - hostname: util.PassthroughCluster, - idleTimeout: durationpb.New(0 * time.Second), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - cg := NewConfigGenTest(t, TestOptions{}) - l := &listener.Listener{} - appendListenerFallthroughRouteForCompleteListener(l, tt.node, cg.PushContext()) - if len(l.FilterChains) != 0 { - t.Errorf("Expected exactly 0 filter chain") - } - if len(l.DefaultFilterChain.Filters) != 1 { - t.Errorf("Expected exactly 1 network filter in the chain") - } - filter := l.DefaultFilterChain.Filters[0] - var tcpProxy tcp.TcpProxy - cfg := filter.GetTypedConfig() - _ = cfg.UnmarshalTo(&tcpProxy) - if tcpProxy.StatPrefix != tt.hostname { - t.Errorf("Expected stat prefix %s but got %s\n", tt.hostname, tcpProxy.StatPrefix) - } - if tcpProxy.GetCluster() != tt.hostname { - t.Errorf("Expected cluster %s but got %s\n", tt.hostname, tcpProxy.GetCluster()) - } - if tt.idleTimeout != nil && !reflect.DeepEqual(tcpProxy.IdleTimeout, tt.idleTimeout) { - t.Errorf("Expected IdleTimeout %s but got %s\n", tt.idleTimeout, tcpProxy.IdleTimeout) - } - }) - } -} - -func TestMergeTCPFilterChains(t *testing.T) { - cg := NewConfigGenTest(t, TestOptions{}) - - node := &model.Proxy{ - ID: "foo.bar", - Metadata: &model.NodeMetadata{}, - SidecarScope: &model.SidecarScope{ - OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, - }, - }, - } - - tcpProxy := &tcp.TcpProxy{ - StatPrefix: "outbound|443||foo.com", - ClusterSpecifier: &tcp.TcpProxy_Cluster{Cluster: "outbound|443||foo.com"}, - } - - tcpProxyFilter := &listener.Filter{ - Name: wellknown.TCPProxy, - ConfigType: &listener.Filter_TypedConfig{TypedConfig: util.MessageToAny(tcpProxy)}, - } - - tcpProxy = &tcp.TcpProxy{ - StatPrefix: "outbound|443||bar.com", - ClusterSpecifier: &tcp.TcpProxy_Cluster{Cluster: "outbound|443||bar.com"}, - } - - tcpProxyFilter2 := &listener.Filter{ - Name: wellknown.TCPProxy, - ConfigType: &listener.Filter_TypedConfig{TypedConfig: util.MessageToAny(tcpProxy)}, - } - - svcPort := &model.Port{ - Name: "https", - Port: 443, - Protocol: protocol.HTTPS, - } - var l listener.Listener - filterChains := []*listener.FilterChain{ - { - FilterChainMatch: &listener.FilterChainMatch{ - PrefixRanges: []*core.CidrRange{ - { - AddressPrefix: "10.244.0.18", - PrefixLen: &wrappers.UInt32Value{Value: 32}, - }, - { - AddressPrefix: "fe80::1c97:c3ff:fed7:5940", - PrefixLen: &wrappers.UInt32Value{Value: 128}, - }, - }, - }, - Filters: nil, // This is not a valid config, just for test - }, - { - FilterChainMatch: &listener.FilterChainMatch{ - ServerNames: []string{"foo.com"}, - }, - // This is not a valid config, just for test - Filters: []*listener.Filter{tcpProxyFilter}, - }, - { - FilterChainMatch: &listener.FilterChainMatch{}, - // This is not a valid config, just for test - Filters: buildOutboundCatchAllNetworkFiltersOnly(cg.PushContext(), node), - }, - } - l.FilterChains = filterChains - listenerMap := map[string]*outboundListenerEntry{ - "0.0.0.0_443": { - servicePort: svcPort, - services: []*model.Service{{ - CreationTime: tnow, - Hostname: host.Name("foo.com"), - DefaultAddress: "192.168.1.1", - Ports: []*model.Port{svcPort}, - Resolution: model.DNSLB, - }}, - listener: &l, - }, - } - - incomingFilterChains := []*listener.FilterChain{ - { - FilterChainMatch: &listener.FilterChainMatch{ - ServerNames: []string{"bar.com"}, - }, // This is not a valid config, just for test - Filters: []*listener.Filter{tcpProxyFilter2}, - }, - } - - svc := model.Service{ - Hostname: "bar.com", - } - - opts := buildListenerOpts{ - proxy: node, - push: cg.PushContext(), - service: &svc, - } - - out := mergeTCPFilterChains(incomingFilterChains, opts, "0.0.0.0_443", listenerMap) - - if len(out) != 4 { - t.Errorf("Got %d filter chains, expected 3", len(out)) - } - if !isMatchAllFilterChain(out[2]) { - t.Errorf("The last filter chain %#v is not wildcard matching", out[2]) - } - - if !reflect.DeepEqual(out[3].Filters, incomingFilterChains[0].Filters) { - t.Errorf("got %v\nwant %v\ndiff %v", out[2].Filters, incomingFilterChains[0].Filters, cmp.Diff(out[2].Filters, incomingFilterChains[0].Filters)) - } -} - -func TestFilterChainMatchEqual(t *testing.T) { - cases := []struct { - name string - first *listener.FilterChainMatch - second *listener.FilterChainMatch - want bool - }{ - { - name: "both nil", - first: nil, - second: nil, - want: true, - }, - { - name: "one of them nil", - first: nil, - second: &listener.FilterChainMatch{}, - want: false, - }, - { - name: "both empty", - first: &listener.FilterChainMatch{}, - second: &listener.FilterChainMatch{}, - want: true, - }, - { - name: "with equal values", - first: &listener.FilterChainMatch{ - TransportProtocol: "TCP", - ApplicationProtocols: mtlsHTTPALPNs, - }, - second: &listener.FilterChainMatch{ - TransportProtocol: "TCP", - ApplicationProtocols: mtlsHTTPALPNs, - }, - want: true, - }, - { - name: "with not equal values", - first: &listener.FilterChainMatch{ - TransportProtocol: "TCP", - ApplicationProtocols: mtlsHTTPALPNs, - }, - second: &listener.FilterChainMatch{ - TransportProtocol: "TCP", - ApplicationProtocols: plaintextHTTPALPNs, - }, - want: false, - }, - { - name: "equal with all values", - first: &listener.FilterChainMatch{ - TransportProtocol: "TCP", - ApplicationProtocols: mtlsHTTPALPNs, - DestinationPort: &wrappers.UInt32Value{Value: 1999}, - AddressSuffix: "suffix", - SourceType: listener.FilterChainMatch_ANY, - SuffixLen: &wrappers.UInt32Value{Value: 3}, - PrefixRanges: []*core.CidrRange{ - { - AddressPrefix: "10.244.0.18", - PrefixLen: &wrappers.UInt32Value{Value: 32}, - }, - { - AddressPrefix: "fe80::1c97:c3ff:fed7:5940", - PrefixLen: &wrappers.UInt32Value{Value: 128}, - }, - }, - SourcePrefixRanges: []*core.CidrRange{ - { - AddressPrefix: "10.244.0.18", - PrefixLen: &wrappers.UInt32Value{Value: 32}, - }, - { - AddressPrefix: "fe80::1c97:c3ff:fed7:5940", - PrefixLen: &wrappers.UInt32Value{Value: 128}, - }, - }, - SourcePorts: []uint32{2000}, - ServerNames: []string{"foo"}, - }, - second: &listener.FilterChainMatch{ - TransportProtocol: "TCP", - ApplicationProtocols: plaintextHTTPALPNs, - DestinationPort: &wrappers.UInt32Value{Value: 1999}, - AddressSuffix: "suffix", - SourceType: listener.FilterChainMatch_ANY, - SuffixLen: &wrappers.UInt32Value{Value: 3}, - PrefixRanges: []*core.CidrRange{ - { - AddressPrefix: "10.244.0.18", - PrefixLen: &wrappers.UInt32Value{Value: 32}, - }, - { - AddressPrefix: "fe80::1c97:c3ff:fed7:5940", - PrefixLen: &wrappers.UInt32Value{Value: 128}, - }, - }, - SourcePrefixRanges: []*core.CidrRange{ - { - AddressPrefix: "10.244.0.18", - PrefixLen: &wrappers.UInt32Value{Value: 32}, - }, - { - AddressPrefix: "fe80::1c97:c3ff:fed7:5940", - PrefixLen: &wrappers.UInt32Value{Value: 128}, - }, - }, - SourcePorts: []uint32{2000}, - ServerNames: []string{"foo"}, - }, - want: false, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - if got := filterChainMatchEqual(tt.first, tt.second); got != tt.want { - t.Fatalf("Expected filter chain match to return %v, but got %v", tt.want, got) - } - }) - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/listenertest/match.go b/pilot/pkg/networking/core/v1alpha3/listenertest/match.go deleted file mode 100644 index 0839cfa27..000000000 --- a/pilot/pkg/networking/core/v1alpha3/listenertest/match.go +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package listenertest - -import ( - "fmt" -) - -import ( - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" -) - -import ( - xdsfilters "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/filters" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -type ListenersTest struct { - // Match listener by name - Name string - // Match listener by port - Port uint32 - - // Listener assertions - Listener ListenerTest -} - -// ListenerTest provides a struct for defining expectations for a listener -type ListenerTest struct { - // Assert the listener contains these filter chains (in order, if TotalMatch) - FilterChains []FilterChainTest - - // Assert the listener contains these ListenerFilters (in order, if TotalMatch) - Filters []string - - // TotalMatch will require that the all elements exactly match (eg, if I have 3 elements in the - // check, the listener must as well). Otherwise, we only validate the assertions we provided are - // present. - TotalMatch bool -} - -type FilterChainTest struct { - // Match a filter chain by name - Name string - // Match filter chain by 'type'. This can be important since Name is currently not unique - Type FilterChainType - // Port the filter chain matches - Port uint32 - - NetworkFilters []string - HTTPFilters []string - - ValidateHCM func(t test.Failer, hcm *hcm.HttpConnectionManager) - - TotalMatch bool -} - -type FilterChainType string - -const ( - PlainTCP FilterChainType = "plaintext TCP" - PlainHTTP FilterChainType = "plaintext HTTP" - StandardTLS FilterChainType = "TLS" - MTLSTCP FilterChainType = "mTLS TCP" - MTLSHTTP FilterChainType = "mTLS HTTP" - Unknown FilterChainType = "unknown" -) - -func classifyFilterChain(have *listener.FilterChain) FilterChainType { - fcm := have.GetFilterChainMatch() - alpn := sets.New(fcm.GetApplicationProtocols()...) - switch fcm.GetTransportProtocol() { - case xdsfilters.TLSTransportProtocol: - if alpn.Contains("istio-http/1.1") { - return MTLSHTTP - } - if alpn.Contains("istio") { - return MTLSTCP - } - return StandardTLS - case xdsfilters.RawBufferTransportProtocol: - if alpn.Contains("http/1.1") { - return PlainHTTP - } - return PlainTCP - default: - return Unknown - } -} - -func VerifyListeners(t test.Failer, listeners []*listener.Listener, lt ListenersTest) { - t.Helper() - for _, l := range listeners { - if lt.Name != "" && lt.Name != l.Name { - continue - } - if lt.Port != 0 && lt.Port != l.Address.GetSocketAddress().GetPortValue() { - continue - } - // It was a match, run assertions - VerifyListener(t, l, lt.Listener) - } -} - -func VerifyListener(t test.Failer, l *listener.Listener, lt ListenerTest) { - t.Helper() - haveFilters := []string{} - for _, lf := range l.ListenerFilters { - haveFilters = append(haveFilters, lf.Name) - } - - // Check ListenerFilters - - if lt.Filters != nil { - if lt.TotalMatch { - assert.Equal(t, lt.Filters, haveFilters, l.Name+": listener filters should be equal") - } else { - if missing := sets.New(lt.Filters...).Difference(sets.New(haveFilters...)).SortedList(); len(missing) > 0 { - t.Fatalf("%v: missing listener filters: %v", l.Name, missing) - } - } - } - - // Check FilterChains - if lt.FilterChains != nil { - if lt.TotalMatch { - // First check they are the same size - if len(lt.FilterChains) != len(l.FilterChains) { - want := []string{} - for _, n := range lt.FilterChains { - want = append(want, n.Name) - } - t.Fatalf("didn't match filter chains, have names %v, expected %v", xdstest.ExtractFilterChainNames(l), want) - } - // Now check they are equivalent - for i := range lt.FilterChains { - have := l.FilterChains[i] - want := lt.FilterChains[i] - - VerifyFilterChain(t, have, want) - } - } else { - for _, want := range lt.FilterChains { - found := 0 - for _, have := range l.FilterChains { - if want.Name != "" && want.Name != have.Name { - continue - } - haveType := classifyFilterChain(have) - if want.Type != "" && want.Type != haveType { - continue - } - if want.Port != 0 && want.Port != have.GetFilterChainMatch().GetDestinationPort().GetValue() { - continue - } - found++ - VerifyFilterChain(t, have, want) - } - if found == 0 { - t.Fatalf("No matching chain found for %+v", want) - } - if found > 1 { - t.Logf("warning: multiple matching chains found for %+v", want) - } - } - } - } -} - -func VerifyFilterChain(t test.Failer, have *listener.FilterChain, want FilterChainTest) { - t.Helper() - haveType := classifyFilterChain(have) - context := func(s string) string { - return fmt.Sprintf("%v/%v: %v", have.Name, haveType, s) - } - if want.Name != "" { - assert.Equal(t, want.Name, have.Name, context("name should be equal")) - } - if want.Type != "" { - assert.Equal(t, want.Type, haveType, context("type should be equal")) - } - if want.Port != 0 { - assert.Equal(t, want.Port, have.GetFilterChainMatch().GetDestinationPort().GetValue(), context("port should be equal")) - } - haveNetwork, haveHTTP := xdstest.ExtractFilterNames(t, have) - if want.TotalMatch { - if want.NetworkFilters != nil { - assert.Equal(t, want.NetworkFilters, haveNetwork, context("network filters should be equal")) - } - if want.HTTPFilters != nil { - assert.Equal(t, want.HTTPFilters, haveHTTP, context("http should be equal")) - } - } else { - if missing := sets.New(want.NetworkFilters...).Difference(sets.New(haveNetwork...)).SortedList(); len(missing) > 0 { - t.Fatalf("%v/%v: missing network filters: %v, have %v", have.Name, haveType, missing, haveNetwork) - } - if missing := sets.New(want.HTTPFilters...).Difference(sets.New(haveHTTP...)).SortedList(); len(missing) > 0 { - t.Fatalf("%v/%v: missing network filters: %v, have %v", have.Name, haveType, missing, haveHTTP) - } - } - if want.ValidateHCM != nil { - want.ValidateHCM(t, xdstest.ExtractHTTPConnectionManager(t, have)) - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/loadbalancer/loadbalancer.go b/pilot/pkg/networking/core/v1alpha3/loadbalancer/loadbalancer.go deleted file mode 100644 index 78db02657..000000000 --- a/pilot/pkg/networking/core/v1alpha3/loadbalancer/loadbalancer.go +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// packages used for load balancer setting -package loadbalancer - -import ( - "math" - "sort" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" -) - -func GetLocalityLbSetting( - mesh *v1alpha3.LocalityLoadBalancerSetting, - destrule *v1alpha3.LocalityLoadBalancerSetting, -) *v1alpha3.LocalityLoadBalancerSetting { - var enabled bool - // Locality lb is enabled if its not explicitly disabled in mesh global config - if mesh != nil && (mesh.Enabled == nil || mesh.Enabled.Value) { - enabled = true - } - // Unless we explicitly override this in destination rule - if destrule != nil { - if destrule.Enabled != nil && !destrule.Enabled.Value { - enabled = false - } else { - enabled = true - } - } - if !enabled { - return nil - } - - // Destination Rule overrides mesh config. If its defined, use that - if destrule != nil { - return destrule - } - // Otherwise fall back to mesh default - return mesh -} - -func ApplyLocalityLBSetting( - loadAssignment *endpoint.ClusterLoadAssignment, - wrappedLocalityLbEndpoints []*WrappedLocalityLbEndpoints, - locality *core.Locality, - proxyLabels map[string]string, - localityLB *v1alpha3.LocalityLoadBalancerSetting, - enableFailover bool, -) { - if localityLB == nil || loadAssignment == nil { - return - } - - // one of Distribute or Failover settings can be applied. - if localityLB.GetDistribute() != nil { - applyLocalityWeight(locality, loadAssignment, localityLB.GetDistribute()) - // Failover needs outlier detection, otherwise Envoy will never drop down to a lower priority. - // Do not apply default failover when locality LB is disabled. - } else if enableFailover && (localityLB.Enabled == nil || localityLB.Enabled.Value) { - if len(localityLB.FailoverPriority) > 0 { - applyPriorityFailover(loadAssignment, wrappedLocalityLbEndpoints, proxyLabels, localityLB.FailoverPriority) - return - } - applyLocalityFailover(locality, loadAssignment, localityLB.Failover) - } -} - -// set locality loadbalancing weight -func applyLocalityWeight( - locality *core.Locality, - loadAssignment *endpoint.ClusterLoadAssignment, - distribute []*v1alpha3.LocalityLoadBalancerSetting_Distribute) { - if distribute == nil { - return - } - - // Support Locality weighted load balancing - // (https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/locality_weight#locality-weighted-load-balancing) - // by providing weights in LocalityLbEndpoints via load_balancing_weight. - // By setting weights across different localities, it can allow - // Envoy to weight assignments across different zones and geographical locations. - for _, localityWeightSetting := range distribute { - if localityWeightSetting != nil && - util.LocalityMatch(locality, localityWeightSetting.From) { - misMatched := map[int]struct{}{} - for i := range loadAssignment.Endpoints { - misMatched[i] = struct{}{} - } - for locality, weight := range localityWeightSetting.To { - // index -> original weight - destLocMap := map[int]uint32{} - totalWeight := uint32(0) - for i, ep := range loadAssignment.Endpoints { - if _, exist := misMatched[i]; exist { - if util.LocalityMatch(ep.Locality, locality) { - delete(misMatched, i) - if ep.LoadBalancingWeight != nil { - destLocMap[i] = ep.LoadBalancingWeight.Value - } else { - destLocMap[i] = 1 - } - totalWeight += destLocMap[i] - } - } - } - // in case wildcard dest matching multi groups of endpoints - // the load balancing weight for a locality is divided by the sum of the weights of all localities - for index, originalWeight := range destLocMap { - destWeight := float64(originalWeight*weight) / float64(totalWeight) - if destWeight > 0 { - loadAssignment.Endpoints[index].LoadBalancingWeight = &wrappers.UInt32Value{ - Value: uint32(math.Ceil(destWeight)), - } - } - } - } - - // remove groups of endpoints in a locality that miss matched - for i := range misMatched { - loadAssignment.Endpoints[i].LbEndpoints = nil - } - break - } - } -} - -// set locality loadbalancing priority -func applyLocalityFailover( - locality *core.Locality, - loadAssignment *endpoint.ClusterLoadAssignment, - failover []*v1alpha3.LocalityLoadBalancerSetting_Failover) { - // key is priority, value is the index of the LocalityLbEndpoints in ClusterLoadAssignment - priorityMap := map[int][]int{} - - // 1. calculate the LocalityLbEndpoints.Priority compared with proxy locality - for i, localityEndpoint := range loadAssignment.Endpoints { - // if region/zone/subZone all match, the priority is 0. - // if region/zone match, the priority is 1. - // if region matches, the priority is 2. - // if locality not match, the priority is 3. - priority := util.LbPriority(locality, localityEndpoint.Locality) - // region not match, apply failover settings when specified - // update localityLbEndpoints' priority to 4 if failover not match - if priority == 3 { - for _, failoverSetting := range failover { - if failoverSetting.From == locality.Region { - if localityEndpoint.Locality == nil || localityEndpoint.Locality.Region != failoverSetting.To { - priority = 4 - } - break - } - } - } - loadAssignment.Endpoints[i].Priority = uint32(priority) - priorityMap[priority] = append(priorityMap[priority], i) - } - - // since Priorities should range from 0 (highest) to N (lowest) without skipping. - // 2. adjust the priorities in order - // 2.1 sort all priorities in increasing order. - priorities := []int{} - for priority := range priorityMap { - priorities = append(priorities, priority) - } - sort.Ints(priorities) - // 2.2 adjust LocalityLbEndpoints priority - // if the index and value of priorities array is not equal. - for i, priority := range priorities { - if i != priority { - // the LocalityLbEndpoints index in ClusterLoadAssignment.Endpoints - for _, index := range priorityMap[priority] { - loadAssignment.Endpoints[index].Priority = uint32(i) - } - } - } -} - -// WrappedLocalityLbEndpoints contain an envoy LocalityLbEndpoints -// and the original IstioEndpoints used to generate it. -// It is used to do failover priority label match with proxy labels. -type WrappedLocalityLbEndpoints struct { - IstioEndpoints []*model.IstioEndpoint - LocalityLbEndpoints *endpoint.LocalityLbEndpoints -} - -// set loadbalancing priority by failover priority label -func applyPriorityFailover( - loadAssignment *endpoint.ClusterLoadAssignment, - wrappedLocalityLbEndpoints []*WrappedLocalityLbEndpoints, - proxyLabels map[string]string, - failoverPriorities []string) { - if len(proxyLabels) == 0 || len(wrappedLocalityLbEndpoints) == 0 { - return - } - priorityMap := make(map[int][]int, len(failoverPriorities)) - localityLbEndpoints := []*endpoint.LocalityLbEndpoints{} - for _, wrappedLbEndpoint := range wrappedLocalityLbEndpoints { - localityLbEndpointsPerLocality := applyPriorityFailoverPerLocality(proxyLabels, wrappedLbEndpoint, failoverPriorities) - localityLbEndpoints = append(localityLbEndpoints, localityLbEndpointsPerLocality...) - } - for i, ep := range localityLbEndpoints { - priorityMap[int(ep.Priority)] = append(priorityMap[int(ep.Priority)], i) - } - // since Priorities should range from 0 (highest) to N (lowest) without skipping. - // adjust the priorities in order - // 1. sort all priorities in increasing order. - priorities := []int{} - for priority := range priorityMap { - priorities = append(priorities, priority) - } - sort.Ints(priorities) - // 2. adjust LocalityLbEndpoints priority - // if the index and value of priorities array is not equal. - for i, priority := range priorities { - if i != priority { - // the LocalityLbEndpoints index in ClusterLoadAssignment.Endpoints - for _, index := range priorityMap[priority] { - localityLbEndpoints[index].Priority = uint32(i) - } - } - } - loadAssignment.Endpoints = localityLbEndpoints -} - -// set loadbalancing priority by failover priority label. -// split one LocalityLbEndpoints to multiple LocalityLbEndpoints based on failover priorities. -func applyPriorityFailoverPerLocality( - proxyLabels map[string]string, - ep *WrappedLocalityLbEndpoints, - failoverPriorities []string) []*endpoint.LocalityLbEndpoints { - lowestPriority := len(failoverPriorities) - // key is priority, value is the index of LocalityLbEndpoints.LbEndpoints - priorityMap := map[int][]int{} - for i, istioEndpoint := range ep.IstioEndpoints { - var priority int - // failoverPriority labels match - for j, label := range failoverPriorities { - if proxyLabels[label] != istioEndpoint.Labels[label] { - priority = lowestPriority - j - break - } - } - priorityMap[priority] = append(priorityMap[priority], i) - } - - // sort all priorities in increasing order. - priorities := []int{} - for priority := range priorityMap { - priorities = append(priorities, priority) - } - sort.Ints(priorities) - - out := make([]*endpoint.LocalityLbEndpoints, len(priorityMap)) - for i, priority := range priorities { - out[i] = util.CloneLocalityLbEndpoint(ep.LocalityLbEndpoints) - out[i].LbEndpoints = nil - out[i].Priority = uint32(priority) - var weight uint32 - for _, index := range priorityMap[priority] { - out[i].LbEndpoints = append(out[i].LbEndpoints, ep.LocalityLbEndpoints.LbEndpoints[index]) - weight += ep.LocalityLbEndpoints.LbEndpoints[index].GetLoadBalancingWeight().GetValue() - } - // reset weight - out[i].LoadBalancingWeight = &wrappers.UInt32Value{ - Value: weight, - } - } - - return out -} diff --git a/pilot/pkg/networking/core/v1alpha3/loadbalancer/loadbalancer_test.go b/pilot/pkg/networking/core/v1alpha3/loadbalancer/loadbalancer_test.go deleted file mode 100644 index a3ef549af..000000000 --- a/pilot/pkg/networking/core/v1alpha3/loadbalancer/loadbalancer_test.go +++ /dev/null @@ -1,966 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package loadbalancer - -import ( - "reflect" - "testing" -) - -import ( - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" - . "github.com/onsi/gomega" - "google.golang.org/protobuf/types/known/durationpb" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - memregistry "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/memory" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -func TestApplyLocalitySetting(t *testing.T) { - locality := &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - } - - t.Run("Distribute", func(t *testing.T) { - tests := []struct { - name string - distribute []*networking.LocalityLoadBalancerSetting_Distribute - expected []int - }{ - { - name: "distribution between subzones", - distribute: []*networking.LocalityLoadBalancerSetting_Distribute{ - { - From: "region1/zone1/subzone1", - To: map[string]uint32{ - "region1/zone1/subzone1": 80, - "region1/zone1/subzone2": 15, - "region1/zone1/subzone3": 5, - }, - }, - }, - expected: []int{40, 40, 15, 5, 0, 0, 0}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - env := buildEnvForClustersWithDistribute(tt.distribute) - cluster := buildFakeCluster() - ApplyLocalityLBSetting(cluster.LoadAssignment, nil, locality, nil, env.Mesh().LocalityLbSetting, true) - weights := make([]int, 0) - for _, localityEndpoint := range cluster.LoadAssignment.Endpoints { - weights = append(weights, int(localityEndpoint.LoadBalancingWeight.GetValue())) - } - if !reflect.DeepEqual(weights, tt.expected) { - t.Errorf("Got weights %v expected %v", weights, tt.expected) - } - }) - } - }) - - t.Run("Failover: all priorities", func(t *testing.T) { - g := NewWithT(t) - env := buildEnvForClustersWithFailover() - cluster := buildFakeCluster() - ApplyLocalityLBSetting(cluster.LoadAssignment, nil, locality, nil, env.Mesh().LocalityLbSetting, true) - for _, localityEndpoint := range cluster.LoadAssignment.Endpoints { - if localityEndpoint.Locality.Region == locality.Region { - if localityEndpoint.Locality.Zone == locality.Zone { - if localityEndpoint.Locality.SubZone == locality.SubZone { - g.Expect(localityEndpoint.Priority).To(Equal(uint32(0))) - continue - } - g.Expect(localityEndpoint.Priority).To(Equal(uint32(1))) - continue - } - g.Expect(localityEndpoint.Priority).To(Equal(uint32(2))) - continue - } - if localityEndpoint.Locality.Region == "region2" { - g.Expect(localityEndpoint.Priority).To(Equal(uint32(3))) - } else { - g.Expect(localityEndpoint.Priority).To(Equal(uint32(4))) - } - } - }) - - t.Run("Failover: priorities with gaps", func(t *testing.T) { - g := NewWithT(t) - env := buildEnvForClustersWithFailover() - cluster := buildSmallCluster() - ApplyLocalityLBSetting(cluster.LoadAssignment, nil, locality, nil, env.Mesh().LocalityLbSetting, true) - for _, localityEndpoint := range cluster.LoadAssignment.Endpoints { - if localityEndpoint.Locality.Region == locality.Region { - if localityEndpoint.Locality.Zone == locality.Zone { - if localityEndpoint.Locality.SubZone == locality.SubZone { - t.Errorf("Should not exist") - continue - } - g.Expect(localityEndpoint.Priority).To(Equal(uint32(0))) - continue - } - t.Errorf("Should not exist") - continue - } - if localityEndpoint.Locality.Region == "region2" { - g.Expect(localityEndpoint.Priority).To(Equal(uint32(1))) - } else { - t.Errorf("Should not exist") - } - } - }) - - t.Run("Failover: priorities with some nil localities", func(t *testing.T) { - g := NewWithT(t) - env := buildEnvForClustersWithFailover() - cluster := buildSmallClusterWithNilLocalities() - ApplyLocalityLBSetting(cluster.LoadAssignment, nil, locality, nil, env.Mesh().LocalityLbSetting, true) - for _, localityEndpoint := range cluster.LoadAssignment.Endpoints { - if localityEndpoint.Locality == nil { - g.Expect(localityEndpoint.Priority).To(Equal(uint32(2))) - } else if localityEndpoint.Locality.Region == locality.Region { - if localityEndpoint.Locality.Zone == locality.Zone { - if localityEndpoint.Locality.SubZone == locality.SubZone { - t.Errorf("Should not exist") - continue - } - g.Expect(localityEndpoint.Priority).To(Equal(uint32(0))) - continue - } - t.Errorf("Should not exist") - continue - } else if localityEndpoint.Locality.Region == "region2" { - g.Expect(localityEndpoint.Priority).To(Equal(uint32(1))) - } else { - t.Errorf("Should not exist") - } - } - }) - - t.Run("Failover: with locality lb disabled", func(t *testing.T) { - g := NewWithT(t) - cluster := buildSmallClusterWithNilLocalities() - lbsetting := &networking.LocalityLoadBalancerSetting{ - Enabled: &wrappers.BoolValue{Value: false}, - } - ApplyLocalityLBSetting(cluster.LoadAssignment, nil, locality, nil, lbsetting, true) - for _, localityEndpoint := range cluster.LoadAssignment.Endpoints { - g.Expect(localityEndpoint.Priority).To(Equal(uint32(0))) - } - }) - - t.Run("FailoverPriority", func(t *testing.T) { - tests := []struct { - name string - failoverPriority []string - proxyLabels map[string]string - expected []*endpoint.LocalityLbEndpoints - }{ - { - name: "match none label", - failoverPriority: []string{"topology.istio.io/network", "topology.istio.io/cluster"}, - proxyLabels: map[string]string{ - "topology.istio.io/network": "test", - "topology.istio.io/cluster": "test", - }, - expected: []*endpoint.LocalityLbEndpoints{ - { - Locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, - LbEndpoints: []*endpoint.LbEndpoint{ - { - HostIdentifier: buildEndpoint("1.1.1.1"), - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - }, - { - HostIdentifier: buildEndpoint("2.2.2.2"), - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - }, - }, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 2, - }, - Priority: 0, - }, - { - Locality: &core.Locality{ - Region: "region2", - Zone: "zone2", - SubZone: "subzone2", - }, - LbEndpoints: []*endpoint.LbEndpoint{ - { - HostIdentifier: buildEndpoint("3.3.3.3"), - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - }, - { - HostIdentifier: buildEndpoint("4.4.4.4"), - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - }, - }, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 2, - }, - Priority: 0, - }, - }, - }, - { - name: "match network label", - failoverPriority: []string{"topology.istio.io/network", "topology.istio.io/cluster"}, - proxyLabels: map[string]string{ - "topology.istio.io/network": "n1", - "topology.istio.io/cluster": "test", - }, - expected: []*endpoint.LocalityLbEndpoints{ - { - Locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, - LbEndpoints: []*endpoint.LbEndpoint{ - { - HostIdentifier: buildEndpoint("1.1.1.1"), - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - }, - }, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - Priority: 0, - }, - { - Locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, - LbEndpoints: []*endpoint.LbEndpoint{ - { - HostIdentifier: buildEndpoint("2.2.2.2"), - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - }, - }, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - Priority: 1, - }, - { - Locality: &core.Locality{ - Region: "region2", - Zone: "zone2", - SubZone: "subzone2", - }, - LbEndpoints: []*endpoint.LbEndpoint{ - { - HostIdentifier: buildEndpoint("3.3.3.3"), - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - }, - }, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - Priority: 0, - }, - { - Locality: &core.Locality{ - Region: "region2", - Zone: "zone2", - SubZone: "subzone2", - }, - LbEndpoints: []*endpoint.LbEndpoint{ - { - HostIdentifier: buildEndpoint("4.4.4.4"), - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - }, - }, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - Priority: 1, - }, - }, - }, - { - name: "not match the first n label", - failoverPriority: []string{"topology.istio.io/network", "topology.istio.io/cluster"}, - proxyLabels: map[string]string{ - "topology.istio.io/network": "test", - "topology.istio.io/cluster": "c1", - }, - expected: []*endpoint.LocalityLbEndpoints{ - { - Locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, - LbEndpoints: []*endpoint.LbEndpoint{ - { - HostIdentifier: buildEndpoint("1.1.1.1"), - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - }, - { - HostIdentifier: buildEndpoint("2.2.2.2"), - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - }, - }, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 2, - }, - Priority: 0, - }, - { - Locality: &core.Locality{ - Region: "region2", - Zone: "zone2", - SubZone: "subzone2", - }, - LbEndpoints: []*endpoint.LbEndpoint{ - { - HostIdentifier: buildEndpoint("3.3.3.3"), - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - }, - { - HostIdentifier: buildEndpoint("4.4.4.4"), - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - }, - }, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 2, - }, - Priority: 0, - }, - }, - }, - { - name: "match all labels", - failoverPriority: []string{"key", "topology.istio.io/network", "topology.istio.io/cluster"}, - proxyLabels: map[string]string{ - "key": "value", - "topology.istio.io/network": "n2", - "topology.istio.io/cluster": "c2", - }, - expected: []*endpoint.LocalityLbEndpoints{ - { - Locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, - LbEndpoints: []*endpoint.LbEndpoint{ - { - HostIdentifier: buildEndpoint("2.2.2.2"), // match [key, network, cluster] - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - }, - }, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - Priority: 0, - }, - { - Locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, - LbEndpoints: []*endpoint.LbEndpoint{ - { - HostIdentifier: buildEndpoint("1.1.1.1"), // match no label - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - }, - }, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - Priority: 3, - }, - { - Locality: &core.Locality{ - Region: "region2", - Zone: "zone2", - SubZone: "subzone2", - }, - LbEndpoints: []*endpoint.LbEndpoint{ - { - HostIdentifier: buildEndpoint("4.4.4.4"), // match [key, network] - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - }, - }, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - Priority: 1, - }, - { - Locality: &core.Locality{ - Region: "region2", - Zone: "zone2", - SubZone: "subzone2", - }, - LbEndpoints: []*endpoint.LbEndpoint{ - { - HostIdentifier: buildEndpoint("3.3.3.3"), // match [key] - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - }, - }, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - Priority: 2, - }, - }, - }, - } - - wrappedEndpoints := buildWrappedLocalityLbEndpoints() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - env := buildEnvForClustersWithFailoverPriority(tt.failoverPriority) - cluster := buildFakeCluster() - ApplyLocalityLBSetting(cluster.LoadAssignment, wrappedEndpoints, locality, tt.proxyLabels, env.Mesh().LocalityLbSetting, true) - - if len(cluster.LoadAssignment.Endpoints) != len(tt.expected) { - t.Fatalf("expected endpoints %d but got %d", len(cluster.LoadAssignment.Endpoints), len(tt.expected)) - } - for i := range cluster.LoadAssignment.Endpoints { - if cluster.LoadAssignment.Endpoints[i].LoadBalancingWeight.GetValue() != tt.expected[i].LoadBalancingWeight.GetValue() { - t.Errorf("Got unexpected lb weight %v expected %v", cluster.LoadAssignment.Endpoints[i].LoadBalancingWeight, tt.expected[i].LoadBalancingWeight) - } - if cluster.LoadAssignment.Endpoints[i].Priority != tt.expected[i].Priority { - t.Errorf("Got unexpected priority %v expected %v", cluster.LoadAssignment.Endpoints[i].Priority, tt.expected[i].Priority) - } - } - }) - } - }) -} - -func TestGetLocalityLbSetting(t *testing.T) { - // dummy config for test - failover := []*networking.LocalityLoadBalancerSetting_Failover{nil} - cases := []struct { - name string - mesh *networking.LocalityLoadBalancerSetting - dr *networking.LocalityLoadBalancerSetting - expected *networking.LocalityLoadBalancerSetting - }{ - { - "all disabled", - nil, - nil, - nil, - }, - { - "mesh only", - &networking.LocalityLoadBalancerSetting{}, - nil, - &networking.LocalityLoadBalancerSetting{}, - }, - { - "dr only", - nil, - &networking.LocalityLoadBalancerSetting{}, - &networking.LocalityLoadBalancerSetting{}, - }, - { - "dr only override", - nil, - &networking.LocalityLoadBalancerSetting{Enabled: &wrappers.BoolValue{Value: true}}, - &networking.LocalityLoadBalancerSetting{Enabled: &wrappers.BoolValue{Value: true}}, - }, - { - "both", - &networking.LocalityLoadBalancerSetting{}, - &networking.LocalityLoadBalancerSetting{Failover: failover}, - &networking.LocalityLoadBalancerSetting{Failover: failover}, - }, - { - "mesh disabled", - &networking.LocalityLoadBalancerSetting{Enabled: &wrappers.BoolValue{Value: false}}, - nil, - nil, - }, - { - "dr disabled", - &networking.LocalityLoadBalancerSetting{Enabled: &wrappers.BoolValue{Value: true}}, - &networking.LocalityLoadBalancerSetting{Enabled: &wrappers.BoolValue{Value: false}}, - nil, - }, - { - "dr enabled override mesh disabled", - &networking.LocalityLoadBalancerSetting{Enabled: &wrappers.BoolValue{Value: false}}, - &networking.LocalityLoadBalancerSetting{Enabled: &wrappers.BoolValue{Value: true}}, - &networking.LocalityLoadBalancerSetting{Enabled: &wrappers.BoolValue{Value: true}}, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - got := GetLocalityLbSetting(tt.mesh, tt.dr) - if !reflect.DeepEqual(tt.expected, got) { - t.Fatalf("Expected: %v, got: %v", tt.expected, got) - } - }) - } -} - -func buildEnvForClustersWithDistribute(distribute []*networking.LocalityLoadBalancerSetting_Distribute) *model.Environment { - serviceDiscovery := memregistry.NewServiceDiscovery(&model.Service{ - Hostname: "test.example.org", - DefaultAddress: "1.1.1.1", - Ports: model.PortList{ - &model.Port{ - Name: "default", - Port: 8080, - Protocol: protocol.HTTP, - }, - }, - }) - - meshConfig := &meshconfig.MeshConfig{ - ConnectTimeout: &durationpb.Duration{ - Seconds: 10, - Nanos: 1, - }, - LocalityLbSetting: &networking.LocalityLoadBalancerSetting{ - Distribute: distribute, - }, - } - - configStore := model.MakeIstioStore(memory.Make(collections.Pilot)) - - env := &model.Environment{ - ServiceDiscovery: serviceDiscovery, - ConfigStore: configStore, - Watcher: mesh.NewFixedWatcher(meshConfig), - } - - env.PushContext = model.NewPushContext() - env.Init() - _ = env.PushContext.InitContext(env, nil, nil) - env.PushContext.SetDestinationRules([]config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(), - Name: "acme", - }, - Spec: &networking.DestinationRule{ - Host: "test.example.org", - TrafficPolicy: &networking.TrafficPolicy{ - OutlierDetection: &networking.OutlierDetection{}, - }, - }, - }, - }) - - return env -} - -func buildEnvForClustersWithFailover() *model.Environment { - serviceDiscovery := memregistry.NewServiceDiscovery(&model.Service{ - Hostname: "test.example.org", - DefaultAddress: "1.1.1.1", - Ports: model.PortList{ - &model.Port{ - Name: "default", - Port: 8080, - Protocol: protocol.HTTP, - }, - }, - }) - - meshConfig := &meshconfig.MeshConfig{ - ConnectTimeout: &durationpb.Duration{ - Seconds: 10, - Nanos: 1, - }, - LocalityLbSetting: &networking.LocalityLoadBalancerSetting{ - Failover: []*networking.LocalityLoadBalancerSetting_Failover{ - { - From: "region1", - To: "region2", - }, - }, - }, - } - - configStore := model.MakeIstioStore(memory.Make(collections.Pilot)) - - env := &model.Environment{ - ServiceDiscovery: serviceDiscovery, - ConfigStore: configStore, - Watcher: mesh.NewFixedWatcher(meshConfig), - } - - env.PushContext = model.NewPushContext() - env.Init() - _ = env.PushContext.InitContext(env, nil, nil) - env.PushContext.SetDestinationRules([]config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(), - Name: "acme", - }, - Spec: &networking.DestinationRule{ - Host: "test.example.org", - TrafficPolicy: &networking.TrafficPolicy{ - OutlierDetection: &networking.OutlierDetection{}, - }, - }, - }, - }) - - return env -} - -func buildEnvForClustersWithFailoverPriority(failoverPriority []string) *model.Environment { - serviceDiscovery := memregistry.NewServiceDiscovery(&model.Service{ - Hostname: "test.example.org", - DefaultAddress: "1.1.1.1", - Ports: model.PortList{ - &model.Port{ - Name: "default", - Port: 8080, - Protocol: protocol.HTTP, - }, - }, - }) - - meshConfig := &meshconfig.MeshConfig{ - ConnectTimeout: &durationpb.Duration{ - Seconds: 10, - Nanos: 1, - }, - LocalityLbSetting: &networking.LocalityLoadBalancerSetting{ - FailoverPriority: failoverPriority, - }, - } - - configStore := model.MakeIstioStore(memory.Make(collections.Pilot)) - - env := &model.Environment{ - ServiceDiscovery: serviceDiscovery, - ConfigStore: configStore, - Watcher: mesh.NewFixedWatcher(meshConfig), - } - - env.PushContext = model.NewPushContext() - env.Init() - _ = env.PushContext.InitContext(env, nil, nil) - env.PushContext.SetDestinationRules([]config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(), - Name: "acme", - }, - Spec: &networking.DestinationRule{ - Host: "test.example.org", - TrafficPolicy: &networking.TrafficPolicy{ - OutlierDetection: &networking.OutlierDetection{}, - }, - }, - }, - }) - - return env -} - -func buildFakeCluster() *cluster.Cluster { - return &cluster.Cluster{ - Name: "outbound|8080||test.example.org", - LoadAssignment: &endpoint.ClusterLoadAssignment{ - ClusterName: "outbound|8080||test.example.org", - Endpoints: []*endpoint.LocalityLbEndpoints{ - { - Locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, - }, - { - Locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, - }, - { - Locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone2", - }, - }, - { - Locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone3", - }, - }, - { - Locality: &core.Locality{ - Region: "region1", - Zone: "zone2", - SubZone: "", - }, - }, - { - Locality: &core.Locality{ - Region: "region2", - Zone: "", - SubZone: "", - }, - }, - { - Locality: &core.Locality{ - Region: "region3", - Zone: "", - SubZone: "", - }, - }, - }, - }, - } -} - -func buildSmallCluster() *cluster.Cluster { - return &cluster.Cluster{ - Name: "outbound|8080||test.example.org", - LoadAssignment: &endpoint.ClusterLoadAssignment{ - ClusterName: "outbound|8080||test.example.org", - Endpoints: []*endpoint.LocalityLbEndpoints{ - { - Locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone2", - }, - }, - { - Locality: &core.Locality{ - Region: "region2", - Zone: "zone1", - SubZone: "subzone2", - }, - }, - { - Locality: &core.Locality{ - Region: "region2", - Zone: "zone1", - SubZone: "subzone2", - }, - }, - }, - }, - } -} - -func buildSmallClusterWithNilLocalities() *cluster.Cluster { - return &cluster.Cluster{ - Name: "outbound|8080||test.example.org", - LoadAssignment: &endpoint.ClusterLoadAssignment{ - ClusterName: "outbound|8080||test.example.org", - Endpoints: []*endpoint.LocalityLbEndpoints{ - { - Locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone2", - }, - }, - {}, - { - Locality: &core.Locality{ - Region: "region2", - Zone: "zone1", - SubZone: "subzone2", - }, - }, - }, - }, - } -} - -func buildSmallClusterForFailOverPriority() *cluster.Cluster { - return &cluster.Cluster{ - Name: "outbound|8080||test.example.org", - LoadAssignment: &endpoint.ClusterLoadAssignment{ - ClusterName: "outbound|8080||test.example.org", - Endpoints: []*endpoint.LocalityLbEndpoints{ - { - Locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, - LbEndpoints: []*endpoint.LbEndpoint{ - { - HostIdentifier: buildEndpoint("1.1.1.1"), - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - }, - { - HostIdentifier: buildEndpoint("2.2.2.2"), - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - }, - }, - }, - { - Locality: &core.Locality{ - Region: "region2", - Zone: "zone2", - SubZone: "subzone2", - }, - LbEndpoints: []*endpoint.LbEndpoint{ - { - HostIdentifier: buildEndpoint("3.3.3.3"), - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - }, - { - HostIdentifier: buildEndpoint("4.4.4.4"), - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: 1, - }, - }, - }, - }, - }, - }, - } -} - -func buildEndpoint(ip string) *endpoint.LbEndpoint_Endpoint { - return &endpoint.LbEndpoint_Endpoint{ - Endpoint: &endpoint.Endpoint{ - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - Address: ip, - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 10001, - }, - }, - }, - }, - }, - } -} - -func buildWrappedLocalityLbEndpoints() []*WrappedLocalityLbEndpoints { - cluster := buildSmallClusterForFailOverPriority() - return []*WrappedLocalityLbEndpoints{ - { - IstioEndpoints: []*model.IstioEndpoint{ - { - Labels: map[string]string{ - "topology.istio.io/network": "n1", - "topology.istio.io/cluster": "c1", - }, - Address: "1.1.1.1", - }, - { - Labels: map[string]string{ - "key": "value", - "topology.istio.io/network": "n2", - "topology.istio.io/cluster": "c2", - }, - Address: "2.2.2.2", - }, - }, - LocalityLbEndpoints: cluster.LoadAssignment.Endpoints[0], - }, - { - IstioEndpoints: []*model.IstioEndpoint{ - { - Labels: map[string]string{ - "key": "value", - "topology.istio.io/network": "n1", - "topology.istio.io/cluster": "c3", - }, - Address: "3.3.3.3", - }, - { - Labels: map[string]string{ - "key": "value", - "topology.istio.io/network": "n2", - "topology.istio.io/cluster": "c4", - }, - Address: "4.4.4.4", - }, - }, - LocalityLbEndpoints: cluster.LoadAssignment.Endpoints[1], - }, - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/name_table.go b/pilot/pkg/networking/core/v1alpha3/name_table.go deleted file mode 100644 index 06dab948c..000000000 --- a/pilot/pkg/networking/core/v1alpha3/name_table.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - dnsProto "github.com/apache/dubbo-go-pixiu/pkg/dns/proto" - dnsServer "github.com/apache/dubbo-go-pixiu/pkg/dns/server" -) - -// BuildNameTable produces a table of hostnames and their associated IPs that can then -// be used by the agent to resolve DNS. This logic is always active. However, local DNS resolution -// will only be effective if DNS capture is enabled in the proxy -func (configgen *ConfigGeneratorImpl) BuildNameTable(node *model.Proxy, push *model.PushContext) *dnsProto.NameTable { - return dnsServer.BuildNameTable(dnsServer.Config{ - Node: node, - Push: push, - MulticlusterHeadlessEnabled: features.MulticlusterHeadlessEnabled, - }) -} diff --git a/pilot/pkg/networking/core/v1alpha3/networkfilter.go b/pilot/pkg/networking/core/v1alpha3/networkfilter.go deleted file mode 100644 index 95a2e5447..000000000 --- a/pilot/pkg/networking/core/v1alpha3/networkfilter.go +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "time" -) - -import ( - mysql "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/network/mysql_proxy/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - mongo "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/mongo_proxy/v3" - redis "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/redis_proxy/v3" - tcp "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3" - hashpolicy "github.com/envoyproxy/go-control-plane/envoy/type/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "google.golang.org/protobuf/types/known/durationpb" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - istionetworking "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - istioroute "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3/route" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/telemetry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - xdsfilters "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/filters" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" -) - -// redisOpTimeout is the default operation timeout for the Redis proxy filter. -var redisOpTimeout = 5 * time.Second - -func buildMetadataExchangeNetworkFilters(class istionetworking.ListenerClass) []*listener.Filter { - filterstack := make([]*listener.Filter, 0) - // We add metadata exchange on inbound only; outbound is handled in cluster filter - if class == istionetworking.ListenerClassSidecarInbound && features.MetadataExchange { - filterstack = append(filterstack, xdsfilters.TCPListenerMx) - } - - return filterstack -} - -func buildMetricsNetworkFilters(push *model.PushContext, proxy *model.Proxy, class istionetworking.ListenerClass) []*listener.Filter { - return push.Telemetry.TCPFilters(proxy, class) -} - -// setAccessLogAndBuildTCPFilter sets the AccessLog configuration in the given -// TcpProxy instance and builds a TCP filter out of it. -func setAccessLogAndBuildTCPFilter(push *model.PushContext, node *model.Proxy, config *tcp.TcpProxy, class istionetworking.ListenerClass) *listener.Filter { - accessLogBuilder.setTCPAccessLog(push, node, config, class) - - tcpFilter := &listener.Filter{ - Name: wellknown.TCPProxy, - ConfigType: &listener.Filter_TypedConfig{TypedConfig: util.MessageToAny(config)}, - } - return tcpFilter -} - -// buildOutboundNetworkFiltersWithSingleDestination takes a single cluster name -// and builds a stack of network filters. -func buildOutboundNetworkFiltersWithSingleDestination(push *model.PushContext, node *model.Proxy, - statPrefix, clusterName, subsetName string, port *model.Port, destinationRule *networking.DestinationRule) []*listener.Filter { - tcpProxy := &tcp.TcpProxy{ - StatPrefix: statPrefix, - ClusterSpecifier: &tcp.TcpProxy_Cluster{Cluster: clusterName}, - } - - idleTimeout, err := time.ParseDuration(node.Metadata.IdleTimeout) - if err == nil { - tcpProxy.IdleTimeout = durationpb.New(idleTimeout) - } - maybeSetHashPolicy(destinationRule, tcpProxy, subsetName) - class := model.OutboundListenerClass(node.Type) - tcpFilter := setAccessLogAndBuildTCPFilter(push, node, tcpProxy, class) - - var filters []*listener.Filter - filters = append(filters, buildMetadataExchangeNetworkFilters(class)...) - filters = append(filters, buildMetricsNetworkFilters(push, node, class)...) - filters = append(filters, buildNetworkFiltersStack(port.Protocol, tcpFilter, statPrefix, clusterName)...) - return filters -} - -// buildOutboundNetworkFiltersWithWeightedClusters takes a set of weighted -// destination routes and builds a stack of network filters. -func buildOutboundNetworkFiltersWithWeightedClusters(node *model.Proxy, routes []*networking.RouteDestination, - push *model.PushContext, port *model.Port, configMeta config.Meta, destinationRule *networking.DestinationRule) []*listener.Filter { - statPrefix := configMeta.Name + "." + configMeta.Namespace - clusterSpecifier := &tcp.TcpProxy_WeightedClusters{ - WeightedClusters: &tcp.TcpProxy_WeightedCluster{}, - } - - tcpProxy := &tcp.TcpProxy{ - StatPrefix: statPrefix, - ClusterSpecifier: clusterSpecifier, - } - - idleTimeout, err := time.ParseDuration(node.Metadata.IdleTimeout) - if err == nil { - tcpProxy.IdleTimeout = durationpb.New(idleTimeout) - } - - for _, route := range routes { - service := push.ServiceForHostname(node, host.Name(route.Destination.Host)) - if route.Weight > 0 { - clusterName := istioroute.GetDestinationCluster(route.Destination, service, port.Port) - clusterSpecifier.WeightedClusters.Clusters = append(clusterSpecifier.WeightedClusters.Clusters, &tcp.TcpProxy_WeightedCluster_ClusterWeight{ - Name: clusterName, - Weight: uint32(route.Weight), - }) - } - } - - // For weighted clusters set hash policy if any of the upstream destinations have sourceIP. - maybeSetHashPolicy(destinationRule, tcpProxy, "") - - // TODO: Need to handle multiple cluster names for Redis - clusterName := clusterSpecifier.WeightedClusters.Clusters[0].Name - class := model.OutboundListenerClass(node.Type) - tcpFilter := setAccessLogAndBuildTCPFilter(push, node, tcpProxy, class) - - var filters []*listener.Filter - filters = append(filters, buildMetadataExchangeNetworkFilters(class)...) - filters = append(filters, buildMetricsNetworkFilters(push, node, class)...) - filters = append(filters, buildNetworkFiltersStack(port.Protocol, tcpFilter, statPrefix, clusterName)...) - return filters -} - -func maybeSetHashPolicy(destinationRule *networking.DestinationRule, tcpProxy *tcp.TcpProxy, subsetName string) { - if destinationRule != nil { - useSourceIP := destinationRule.GetTrafficPolicy().GetLoadBalancer().GetConsistentHash().GetUseSourceIp() - for _, subset := range destinationRule.Subsets { - if subset.Name != subsetName { - continue - } - // If subset has load balancer - see if it is also consistent hash source IP - if subset.TrafficPolicy != nil && subset.TrafficPolicy.LoadBalancer != nil { - if subset.TrafficPolicy.LoadBalancer.GetConsistentHash() != nil { - useSourceIP = subset.TrafficPolicy.LoadBalancer.GetConsistentHash().GetUseSourceIp() - } else { - // This means that subset has defined non sourceIP consistent hash load balancer. - useSourceIP = false - } - } - break - } - // If destinationrule has consistent hash source ip set, use it for tcp proxy. - if useSourceIP { - tcpProxy.HashPolicy = []*hashpolicy.HashPolicy{{PolicySpecifier: &hashpolicy.HashPolicy_SourceIp_{ - SourceIp: &hashpolicy.HashPolicy_SourceIp{}, - }}} - } - } -} - -// buildNetworkFiltersStack builds a slice of network filters based on -// the protocol in use and the given TCP filter instance. -func buildNetworkFiltersStack(p protocol.Instance, tcpFilter *listener.Filter, statPrefix string, clusterName string) []*listener.Filter { - filterstack := make([]*listener.Filter, 0) - switch p { - case protocol.Mongo: - if features.EnableMongoFilter { - filterstack = append(filterstack, buildMongoFilter(statPrefix), tcpFilter) - } else { - filterstack = append(filterstack, tcpFilter) - } - case protocol.Redis: - if features.EnableRedisFilter { - // redis filter has route config, it is a terminating filter, no need append tcp filter. - filterstack = append(filterstack, buildRedisFilter(statPrefix, clusterName)) - } else { - filterstack = append(filterstack, tcpFilter) - } - case protocol.MySQL: - if features.EnableMysqlFilter { - filterstack = append(filterstack, buildMySQLFilter(statPrefix)) - } - filterstack = append(filterstack, tcpFilter) - default: - filterstack = append(filterstack, tcpFilter) - } - - return filterstack -} - -// buildOutboundNetworkFilters generates a TCP proxy network filter for outbound -// connections. In addition, it generates protocol specific filters (e.g., Mongo -// filter). -func buildOutboundNetworkFilters(node *model.Proxy, - routes []*networking.RouteDestination, push *model.PushContext, - port *model.Port, configMeta config.Meta) []*listener.Filter { - service := push.ServiceForHostname(node, host.Name(routes[0].Destination.Host)) - var destinationRule *networking.DestinationRule - if service != nil { - destinationRule = CastDestinationRule(node.SidecarScope.DestinationRule(model.TrafficDirectionOutbound, node, service.Hostname)) - } - if len(routes) == 1 { - clusterName := istioroute.GetDestinationCluster(routes[0].Destination, service, port.Port) - statPrefix := clusterName - // If stat name is configured, build the stat prefix from configured pattern. - if len(push.Mesh.OutboundClusterStatName) != 0 && service != nil { - statPrefix = telemetry.BuildStatPrefix(push.Mesh.OutboundClusterStatName, routes[0].Destination.Host, - routes[0].Destination.Subset, port, &service.Attributes) - } - - return buildOutboundNetworkFiltersWithSingleDestination(push, node, statPrefix, clusterName, routes[0].Destination.Subset, port, destinationRule) - } - return buildOutboundNetworkFiltersWithWeightedClusters(node, routes, push, port, configMeta, destinationRule) -} - -// buildMongoFilter builds an outbound Envoy MongoProxy filter. -func buildMongoFilter(statPrefix string) *listener.Filter { - // TODO: add a watcher for /var/lib/istio/mongo/certs - // if certs are found use, TLS or mTLS clusters for talking to MongoDB. - // User is responsible for mounting those certs in the pod. - mongoProxy := &mongo.MongoProxy{ - StatPrefix: statPrefix, // mongo stats are prefixed with mongo. by Envoy - // TODO enable faults in mongo - } - - out := &listener.Filter{ - Name: wellknown.MongoProxy, - ConfigType: &listener.Filter_TypedConfig{TypedConfig: util.MessageToAny(mongoProxy)}, - } - - return out -} - -// buildOutboundAutoPassthroughFilterStack builds a filter stack with sni_cluster and tcp -// used by auto_passthrough gateway servers -func buildOutboundAutoPassthroughFilterStack(push *model.PushContext, node *model.Proxy, port *model.Port) []*listener.Filter { - // First build tcp with access logs - // then add sni_cluster to the front - tcpProxy := buildOutboundNetworkFiltersWithSingleDestination(push, node, util.BlackHoleCluster, util.BlackHoleCluster, "", port, nil) - filterstack := make([]*listener.Filter, 0) - filterstack = append(filterstack, &listener.Filter{ - Name: util.SniClusterFilter, - }) - filterstack = append(filterstack, tcpProxy...) - - return filterstack -} - -// buildRedisFilter builds an outbound Envoy RedisProxy filter. -// Currently, if multiple clusters are defined, one of them will be picked for -// configuring the Redis proxy. -func buildRedisFilter(statPrefix, clusterName string) *listener.Filter { - redisProxy := &redis.RedisProxy{ - LatencyInMicros: true, // redis latency stats are captured in micro seconds which is typically the case. - StatPrefix: statPrefix, // redis stats are prefixed with redis. by Envoy - Settings: &redis.RedisProxy_ConnPoolSettings{ - OpTimeout: durationpb.New(redisOpTimeout), - }, - PrefixRoutes: &redis.RedisProxy_PrefixRoutes{ - CatchAllRoute: &redis.RedisProxy_PrefixRoutes_Route{ - Cluster: clusterName, - }, - }, - } - - out := &listener.Filter{ - Name: wellknown.RedisProxy, - ConfigType: &listener.Filter_TypedConfig{TypedConfig: util.MessageToAny(redisProxy)}, - } - - return out -} - -// buildMySQLFilter builds an outbound Envoy MySQLProxy filter. -func buildMySQLFilter(statPrefix string) *listener.Filter { - mySQLProxy := &mysql.MySQLProxy{ - StatPrefix: statPrefix, // MySQL stats are prefixed with mysql. by Envoy. - } - - out := &listener.Filter{ - Name: wellknown.MySQLProxy, - ConfigType: &listener.Filter_TypedConfig{TypedConfig: util.MessageToAny(mySQLProxy)}, - } - - return out -} diff --git a/pilot/pkg/networking/core/v1alpha3/networkfilter_test.go b/pilot/pkg/networking/core/v1alpha3/networkfilter_test.go deleted file mode 100644 index 65db9480c..000000000 --- a/pilot/pkg/networking/core/v1alpha3/networkfilter_test.go +++ /dev/null @@ -1,458 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "reflect" - "testing" - "time" -) - -import ( - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - redis "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/redis_proxy/v3" - tcp "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "google.golang.org/protobuf/types/known/durationpb" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/telemetry" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -func TestBuildRedisFilter(t *testing.T) { - redisFilter := buildRedisFilter("redis", "redis-cluster") - if redisFilter.Name != wellknown.RedisProxy { - t.Errorf("redis filter name is %s not %s", redisFilter.Name, wellknown.RedisProxy) - } - if config, ok := redisFilter.ConfigType.(*listener.Filter_TypedConfig); ok { - redisProxy := redis.RedisProxy{} - if err := config.TypedConfig.UnmarshalTo(&redisProxy); err != nil { - t.Errorf("unmarshal failed: %v", err) - } - if redisProxy.StatPrefix != "redis" { - t.Errorf("redis proxy statPrefix is %s", redisProxy.StatPrefix) - } - if !redisProxy.LatencyInMicros { - t.Errorf("redis proxy latency stat is not configured for microseconds") - } - if redisProxy.PrefixRoutes.CatchAllRoute.Cluster != "redis-cluster" { - t.Errorf("redis proxy's PrefixRoutes.CatchAllCluster is %s", redisProxy.PrefixRoutes.CatchAllRoute.Cluster) - } - } else { - t.Errorf("redis filter type is %T not listener.Filter_TypedConfig ", redisFilter.ConfigType) - } -} - -func TestInboundNetworkFilterStatPrefix(t *testing.T) { - cases := []struct { - name string - statPattern string - expectedStatPrefix string - }{ - { - "no pattern", - "", - "inbound|8888||", - }, - { - "service only pattern", - "%SERVICE%", - "v0.default.example.org", - }, - } - - services := []*model.Service{ - buildService("test.com", "10.10.0.0/24", protocol.TCP, tnow), - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - m := mesh.DefaultMeshConfig() - m.InboundClusterStatName = tt.statPattern - cg := NewConfigGenTest(t, TestOptions{ - Services: services, - MeshConfig: m, - }) - - fcc := inboundChainConfig{ - telemetryMetadata: telemetry.FilterChainMetadata{InstanceHostname: "v0.default.example.org"}, - clusterName: "inbound|8888||", - } - - listenerFilters := NewListenerBuilder(cg.SetupProxy(nil), cg.PushContext()).buildInboundNetworkFilters(fcc) - tcp := &tcp.TcpProxy{} - listenerFilters[len(listenerFilters)-1].GetTypedConfig().UnmarshalTo(tcp) - if tcp.StatPrefix != tt.expectedStatPrefix { - t.Fatalf("Unexpected Stat Prefix, Expecting %s, Got %s", tt.expectedStatPrefix, tcp.StatPrefix) - } - }) - } -} - -func TestInboundNetworkFilterIdleTimeout(t *testing.T) { - cases := []struct { - name string - idleTimeout string - expected *durationpb.Duration - }{ - { - "no idle timeout", - "", - nil, - }, - { - "invalid timeout", - "invalid-30s", - nil, - }, - { - "valid idle timeout 30s", - "30s", - durationpb.New(30 * time.Second), - }, - } - - services := []*model.Service{ - buildService("test.com", "10.10.0.0/24", protocol.TCP, tnow), - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - cg := NewConfigGenTest(t, TestOptions{Services: services}) - - fcc := inboundChainConfig{} - node := &model.Proxy{Metadata: &model.NodeMetadata{IdleTimeout: tt.idleTimeout}} - listenerFilters := NewListenerBuilder(cg.SetupProxy(node), cg.PushContext()).buildInboundNetworkFilters(fcc) - tcp := &tcp.TcpProxy{} - listenerFilters[len(listenerFilters)-1].GetTypedConfig().UnmarshalTo(tcp) - if !reflect.DeepEqual(tcp.IdleTimeout, tt.expected) { - t.Fatalf("Unexpected IdleTimeout, Expecting %s, Got %s", tt.expected, tcp.IdleTimeout) - } - }) - } -} - -func TestOutboundNetworkFilterStatPrefix(t *testing.T) { - cases := []struct { - name string - statPattern string - routes []*networking.RouteDestination - expectedStatPrefix string - }{ - { - "no pattern, single route", - "", - []*networking.RouteDestination{ - { - Destination: &networking.Destination{ - Host: "test.com", - Port: &networking.PortSelector{ - Number: 9999, - }, - }, - }, - }, - "outbound|9999||test.com", - }, - { - "service only pattern, single route", - "%SERVICE%", - []*networking.RouteDestination{ - { - Destination: &networking.Destination{ - Host: "test.com", - Port: &networking.PortSelector{ - Number: 9999, - }, - }, - }, - }, - "test.com", - }, - { - "no pattern, multiple routes", - "", - []*networking.RouteDestination{ - { - Destination: &networking.Destination{ - Host: "test.com", - Port: &networking.PortSelector{ - Number: 9999, - }, - }, - Weight: 50, - }, - { - Destination: &networking.Destination{ - Host: "test.com", - Port: &networking.PortSelector{ - Number: 8888, - }, - }, - Weight: 50, - }, - }, - "test.com.ns", // No stat pattern will be applied for multiple routes, as it will be always be name.namespace. - }, - { - "service pattern, multiple routes", - "%SERVICE%", - []*networking.RouteDestination{ - { - Destination: &networking.Destination{ - Host: "test.com", - Port: &networking.PortSelector{ - Number: 9999, - }, - }, - Weight: 50, - }, - { - Destination: &networking.Destination{ - Host: "test.com", - Port: &networking.PortSelector{ - Number: 8888, - }, - }, - Weight: 50, - }, - }, - "test.com.ns", // No stat pattern will be applied for multiple routes, as it will be always be name.namespace. - }, - } - - services := []*model.Service{ - buildService("test.com", "10.10.0.0/24", protocol.TCP, tnow), - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - m := mesh.DefaultMeshConfig() - m.OutboundClusterStatName = tt.statPattern - cg := NewConfigGenTest(t, TestOptions{MeshConfig: m, Services: services}) - - listeners := buildOutboundNetworkFilters( - cg.SetupProxy(nil), tt.routes, cg.PushContext(), - &model.Port{Port: 9999}, config.Meta{Name: "test.com", Namespace: "ns"}) - tcp := &tcp.TcpProxy{} - listeners[0].GetTypedConfig().UnmarshalTo(tcp) - if tcp.StatPrefix != tt.expectedStatPrefix { - t.Fatalf("Unexpected Stat Prefix, Expecting %s, Got %s", tt.expectedStatPrefix, tcp.StatPrefix) - } - }) - } -} - -func TestOutboundNetworkFilterWithSourceIPHashing(t *testing.T) { - services := []*model.Service{ - buildService("test.com", "10.10.0.0/24", protocol.TCP, tnow), - buildService("testsimple.com", "10.10.0.0/24", protocol.TCP, tnow), - buildService("subsettest.com", "10.10.0.0/24", protocol.TCP, tnow), - buildService("subsettestdifferent.com", "10.10.0.0/24", protocol.TCP, tnow), - } - - simpleDestinationRuleSpec := &networking.DestinationRule{ - Host: "testsimple.com", - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{}, - }, - }, - } - - simpleDestinationRule := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(), - Name: "acme-v3-0", - Namespace: "not-default", - }, - Spec: simpleDestinationRuleSpec, - } - - destinationRuleSpec := &networking.DestinationRule{ - Host: "test.com", - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{ - ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ - HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_UseSourceIp{UseSourceIp: true}, - }, - }, - }, - }, - } - - destinationRule := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(), - Name: "acme-v3-1", - Namespace: "not-default", - }, - Spec: destinationRuleSpec, - } - - subsetdestinationRuleSpec := &networking.DestinationRule{ - Host: "subsettest.com", - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{ - ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ - HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_UseSourceIp{UseSourceIp: true}, - }, - }, - }, - }, - Subsets: []*networking.Subset{{Name: "v1", Labels: map[string]string{"version": "v1"}}}, - } - - subsetdestinationRule := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(), - Name: "acme-v3-2", - Namespace: "not-default", - }, - Spec: subsetdestinationRuleSpec, - } - - subsetdestinationRuleDifferentSpec := &networking.DestinationRule{ - Host: "subsettestdifferent.com", - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{ - ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ - HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_UseSourceIp{UseSourceIp: true}, - }, - }, - }, - }, - Subsets: []*networking.Subset{ - { - Name: "v1", - Labels: map[string]string{"version": "v1"}, - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{}, - }, - }, - }, - }, - } - - subsetDifferentdestinationRule := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(), - Name: "acme-v3-3", - Namespace: "not-default", - }, - Spec: subsetdestinationRuleDifferentSpec, - } - - destinationRules := []*config.Config{&destinationRule, &simpleDestinationRule, &subsetdestinationRule, &subsetDifferentdestinationRule} - - cg := NewConfigGenTest(t, TestOptions{ - ConfigPointers: destinationRules, - Services: services, - }) - - proxy := cg.SetupProxy(&model.Proxy{ConfigNamespace: "not-default"}) - cases := []struct { - name string - routes []*networking.RouteDestination - configMeta config.Meta - useSourceIP bool - }{ - { - "destination rule without sourceip", - []*networking.RouteDestination{ - { - Destination: &networking.Destination{ - Host: "testsimple.com", - Port: &networking.PortSelector{ - Number: 9999, - }, - }, - }, - }, - config.Meta{Name: "testsimple.com", Namespace: "ns"}, - false, - }, - { - "destination rule has sourceip", - []*networking.RouteDestination{ - { - Destination: &networking.Destination{ - Host: "test.com", - Port: &networking.PortSelector{ - Number: 9999, - }, - }, - }, - }, - config.Meta{Name: "test.com", Namespace: "ns"}, - true, - }, - { - "subset destination rule does not have traffic policy", - []*networking.RouteDestination{ - { - Destination: &networking.Destination{ - Host: "subsettest.com", - Port: &networking.PortSelector{ - Number: 9999, - }, - Subset: "v1", - }, - }, - }, - config.Meta{Name: "subsettest.com", Namespace: "ns"}, - true, - }, - { - "subset destination rule overrides traffic policy", - []*networking.RouteDestination{ - { - Destination: &networking.Destination{ - Host: "subsettestdifferent.com", - Port: &networking.PortSelector{ - Number: 9999, - }, - Subset: "v1", - }, - }, - }, - config.Meta{Name: "subsettestdifferent.com", Namespace: "ns"}, - false, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - listeners := buildOutboundNetworkFilters(proxy, tt.routes, cg.PushContext(), &model.Port{Port: 9999}, tt.configMeta) - tcp := &tcp.TcpProxy{} - listeners[0].GetTypedConfig().UnmarshalTo(tcp) - hasSourceIP := tcp.HashPolicy != nil && len(tcp.HashPolicy) == 1 && tcp.HashPolicy[0].GetSourceIp() != nil - if hasSourceIP != tt.useSourceIP { - t.Fatalf("Unexpected SourceIp hash policy. expected: %v, got: %v", tt.useSourceIP, hasSourceIP) - } - }) - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/peer_authentication_simulation_test.go b/pilot/pkg/networking/core/v1alpha3/peer_authentication_simulation_test.go deleted file mode 100644 index b12115ffb..000000000 --- a/pilot/pkg/networking/core/v1alpha3/peer_authentication_simulation_test.go +++ /dev/null @@ -1,619 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3_test - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/simulation" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" -) - -// TestPeerAuthenticationPassthrough tests the PeerAuthentication policy applies correctly on the passthrough filter chain, -// including both global configuration and port level configuration. -func TestPeerAuthenticationPassthrough(t *testing.T) { - paStrict := ` -apiVersion: security.istio.io/v1beta1 -kind: PeerAuthentication -metadata: - name: default -spec: - selector: - matchLabels: - app: foo - mtls: - mode: STRICT ----` - paDisable := ` -apiVersion: security.istio.io/v1beta1 -kind: PeerAuthentication -metadata: - name: default -spec: - selector: - matchLabels: - app: foo - mtls: - mode: DISABLE ----` - paPermissive := ` -apiVersion: security.istio.io/v1beta1 -kind: PeerAuthentication -metadata: - name: default -spec: - selector: - matchLabels: - app: foo - mtls: - mode: PERMISSIVE ----` - paStrictWithDisableOnPort9000 := ` -apiVersion: security.istio.io/v1beta1 -kind: PeerAuthentication -metadata: - name: default -spec: - selector: - matchLabels: - app: foo - mtls: - mode: STRICT - portLevelMtls: - 9000: - mode: DISABLE ----` - paDisableWithStrictOnPort9000 := ` -apiVersion: security.istio.io/v1beta1 -kind: PeerAuthentication -metadata: - name: default -spec: - selector: - matchLabels: - app: foo - mtls: - mode: DISABLE - portLevelMtls: - 9000: - mode: STRICT ----` - paDisableWithPermissiveOnPort9000 := ` -apiVersion: security.istio.io/v1beta1 -kind: PeerAuthentication -metadata: - name: default -spec: - selector: - matchLabels: - app: foo - mtls: - mode: DISABLE - portLevelMtls: - 9000: - mode: PERMISSIVE ----` - sePort8000 := ` -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: se -spec: - hosts: - - foo.bar - endpoints: - - address: 1.1.1.1 - location: MESH_INTERNAL - resolution: STATIC - ports: - - name: http - number: 8000 - protocol: HTTP ----` - mkCall := func(port int, tls simulation.TLSMode) simulation.Call { - r := simulation.Call{Protocol: simulation.HTTP, Port: port, CallMode: simulation.CallModeInbound, TLS: tls} - if tls == simulation.MTLS { - r.Alpn = "istio" - } - return r - } - cases := []struct { - name string - config string - calls []simulation.Expect - }{ - { - name: "global disable", - config: paDisable, - calls: []simulation.Expect{ - { - Name: "mtls", - Call: mkCall(8000, simulation.MTLS), - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - { - Name: "plaintext", - Call: mkCall(8000, simulation.Plaintext), - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - }, - }, - { - name: "global strict", - config: paStrict, - calls: []simulation.Expect{ - { - Name: "plaintext", - Call: mkCall(8000, simulation.Plaintext), - Result: simulation.Result{Error: simulation.ErrNoFilterChain}, - }, - { - Name: "mtls", - Call: mkCall(8000, simulation.MTLS), - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - }, - }, - { - name: "global permissive", - config: paPermissive, - calls: []simulation.Expect{ - { - Name: "plaintext", - Call: mkCall(8000, simulation.Plaintext), - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - { - Name: "mtls", - Call: mkCall(8000, simulation.MTLS), - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - }, - }, - { - name: "global disable and port 9000 strict", - config: paDisableWithStrictOnPort9000, - calls: []simulation.Expect{ - { - Name: "plaintext on port 8000", - Call: mkCall(8000, simulation.Plaintext), - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - { - Name: "mtls on port 8000", - Call: mkCall(8000, simulation.MTLS), - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - { - Name: "plaintext port 9000", - Call: mkCall(9000, simulation.Plaintext), - Result: simulation.Result{Error: simulation.ErrNoFilterChain}, - }, - { - Name: "mtls port 9000", - Call: mkCall(9000, simulation.MTLS), - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - }, - }, - { - name: "global disable and port 9000 strict not in service", - config: paDisableWithStrictOnPort9000 + sePort8000, - calls: []simulation.Expect{ - { - Name: "plaintext on port 8000", - Call: mkCall(8000, simulation.Plaintext), - Result: simulation.Result{ClusterMatched: "inbound|8000||"}, - }, - { - Name: "mtls on port 8000", - Call: mkCall(8000, simulation.MTLS), - // This will send an mTLS request to plaintext HTTP port, which is expected to fail - Result: simulation.Result{Error: simulation.ErrProtocolError}, - }, - { - Name: "plaintext port 9000", - Call: mkCall(9000, simulation.Plaintext), - Result: simulation.Result{Error: simulation.ErrNoFilterChain}, - }, - { - Name: "mtls port 9000", - Call: mkCall(9000, simulation.MTLS), - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - }, - }, - { - name: "global strict and port 9000 plaintext", - config: paStrictWithDisableOnPort9000, - calls: []simulation.Expect{ - { - Name: "plaintext on port 8000", - Call: mkCall(8000, simulation.Plaintext), - Result: simulation.Result{Error: simulation.ErrNoFilterChain}, - }, - { - Name: "mtls on port 8000", - Call: mkCall(8000, simulation.MTLS), - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - { - Name: "plaintext port 9000", - Call: mkCall(9000, simulation.Plaintext), - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - { - Name: "mtls port 9000", - Call: mkCall(9000, simulation.MTLS), - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - }, - }, - { - name: "global strict and port 9000 plaintext not in service", - config: paStrictWithDisableOnPort9000 + sePort8000, - calls: []simulation.Expect{ - { - Name: "plaintext on port 8000", - Call: mkCall(8000, simulation.Plaintext), - Result: simulation.Result{Error: simulation.ErrNoFilterChain}, - }, - { - Name: "mtls on port 8000", - Call: mkCall(8000, simulation.MTLS), - Result: simulation.Result{ClusterMatched: "inbound|8000||"}, - }, - { - Name: "plaintext port 9000", - Call: mkCall(9000, simulation.Plaintext), - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - { - Name: "mtls port 9000", - Call: mkCall(9000, simulation.MTLS), - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - }, - }, - { - name: "global plaintext and port 9000 permissive", - config: paDisableWithPermissiveOnPort9000, - calls: []simulation.Expect{ - { - Name: "plaintext on port 8000", - Call: mkCall(8000, simulation.Plaintext), - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - { - Name: "mtls on port 8000", - Call: mkCall(8000, simulation.MTLS), - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - { - Name: "plaintext port 9000", - Call: mkCall(9000, simulation.Plaintext), - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - { - Name: "mtls port 9000", - Call: mkCall(9000, simulation.MTLS), - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - }, - }, - { - name: "global plaintext and port 9000 permissive not in service", - config: paDisableWithPermissiveOnPort9000 + sePort8000, - calls: []simulation.Expect{ - { - Name: "plaintext on port 8000", - Call: mkCall(8000, simulation.Plaintext), - Result: simulation.Result{ClusterMatched: "inbound|8000||"}, - }, - { - Name: "mtls on port 8000", - Call: mkCall(8000, simulation.MTLS), - // We match the plaintext HTTP filter chain, which is a protocol error (as expected) - Result: simulation.Result{Error: simulation.ErrProtocolError}, - }, - { - Name: "plaintext port 9000", - Call: mkCall(9000, simulation.Plaintext), - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - { - Name: "mtls port 9000", - Call: mkCall(9000, simulation.MTLS), - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - }, - }, - } - proxy := &model.Proxy{Metadata: &model.NodeMetadata{Labels: map[string]string{"app": "foo"}}} - for _, tt := range cases { - runSimulationTest(t, proxy, xds.FakeOptions{}, simulationTest{ - name: tt.name, - config: tt.config, - calls: tt.calls, - }) - } -} - -// TestPeerAuthenticationWithSidecar tests the PeerAuthentication policy applies correctly to filter chain generated from -// either the service or sidecar resource. -func TestPeerAuthenticationWithSidecar(t *testing.T) { - pa := ` -apiVersion: security.istio.io/v1beta1 -kind: PeerAuthentication -metadata: - name: default -spec: - selector: - matchLabels: - app: foo - mtls: - mode: STRICT - portLevelMtls: - 9090: - mode: DISABLE ----` - sidecar := ` -apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - labels: - app: foo - name: sidecar -spec: - ingress: - - defaultEndpoint: 127.0.0.1:8080 - port: - name: tls - number: 8080 - protocol: TCP - - defaultEndpoint: 127.0.0.1:9090 - port: - name: plaintext - number: 9090 - protocol: TCP - egress: - - hosts: - - "*/*" - workloadSelector: - labels: - app: foo ----` - partialSidecar := ` -apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - labels: - app: foo - name: sidecar -spec: - ingress: - - defaultEndpoint: 127.0.0.1:8080 - port: - name: tls - number: 8080 - protocol: TCP - egress: - - hosts: - - "*/*" - workloadSelector: - labels: - app: foo ----` - instancePorts := ` -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: se -spec: - hosts: - - foo.bar - endpoints: - - address: 1.1.1.1 - labels: - app: foo - location: MESH_INTERNAL - resolution: STATIC - ports: - - name: tls - number: 8080 - protocol: TCP - - name: plaintext - number: 9090 - protocol: TCP ----` - instanceNoPorts := ` -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: se -spec: - hosts: - - foo.bar - endpoints: - - address: 1.1.1.1 - labels: - app: foo - location: MESH_INTERNAL - resolution: STATIC - ports: - - name: random - number: 5050 - protocol: TCP ----` - mkCall := func(port int, tls simulation.TLSMode) simulation.Call { - return simulation.Call{Protocol: simulation.TCP, Port: port, CallMode: simulation.CallModeInbound, TLS: tls} - } - cases := []struct { - name string - config string - calls []simulation.Expect - }{ - { - name: "service, no sidecar", - config: pa + instancePorts, - calls: []simulation.Expect{ - { - Name: "plaintext on tls port", - Call: mkCall(8080, simulation.Plaintext), - Result: simulation.Result{Error: simulation.ErrNoFilterChain}, - }, - { - Name: "tls on tls port", - Call: mkCall(8080, simulation.MTLS), - Result: simulation.Result{ClusterMatched: "inbound|8080||"}, - }, - { - Name: "plaintext on plaintext port", - Call: mkCall(9090, simulation.Plaintext), - Result: simulation.Result{ClusterMatched: "inbound|9090||"}, - }, - { - Name: "tls on plaintext port", - Call: mkCall(9090, simulation.MTLS), - // TLS is fine here; we are not sniffing TLS at all so anything is allowed - Result: simulation.Result{ClusterMatched: "inbound|9090||"}, - }, - }, - }, - { - name: "service, full sidecar", - config: pa + sidecar + instancePorts, - calls: []simulation.Expect{ - { - Name: "plaintext on tls port", - Call: mkCall(8080, simulation.Plaintext), - Result: simulation.Result{Error: simulation.ErrNoFilterChain}, - }, - { - Name: "tls on tls port", - Call: mkCall(8080, simulation.MTLS), - Result: simulation.Result{ClusterMatched: "inbound|8080||"}, - }, - { - Name: "plaintext on plaintext port", - Call: mkCall(9090, simulation.Plaintext), - Result: simulation.Result{ClusterMatched: "inbound|9090||"}, - }, - { - Name: "tls on plaintext port", - Call: mkCall(9090, simulation.MTLS), - // TLS is fine here; we are not sniffing TLS at all so anything is allowed - Result: simulation.Result{ClusterMatched: "inbound|9090||"}, - }, - }, - }, - { - name: "no service, no sidecar", - config: pa + instanceNoPorts, - calls: []simulation.Expect{ - { - Name: "plaintext on tls port", - Call: mkCall(8080, simulation.Plaintext), - Result: simulation.Result{Error: simulation.ErrNoFilterChain}, - }, - { - Name: "tls on tls port", - Call: mkCall(8080, simulation.MTLS), - // no ports defined, so we will passthrough - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - { - Name: "plaintext on plaintext port", - Call: mkCall(9090, simulation.Plaintext), - // no ports defined, so we will passthrough - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - { - Name: "tls on plaintext port", - Call: mkCall(9090, simulation.MTLS), - // no ports defined, so we will passthrough - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - }, - }, - { - name: "no service, full sidecar", - config: pa + sidecar + instanceNoPorts, - calls: []simulation.Expect{ - { - Name: "plaintext on tls port", - Call: mkCall(8080, simulation.Plaintext), - Result: simulation.Result{Error: simulation.ErrNoFilterChain}, - }, - { - Name: "tls on tls port", - Call: mkCall(8080, simulation.MTLS), - Result: simulation.Result{ClusterMatched: "inbound|8080||"}, - }, - { - Name: "plaintext on plaintext port", - Call: mkCall(9090, simulation.Plaintext), - Result: simulation.Result{ClusterMatched: "inbound|9090||"}, - }, - { - Name: "tls on plaintext port", - Call: mkCall(9090, simulation.MTLS), - // TLS is fine here; we are not sniffing TLS at all so anything is allowed - Result: simulation.Result{ClusterMatched: "inbound|9090||"}, - }, - }, - }, - { - name: "service, partial sidecar", - config: pa + partialSidecar + instancePorts, - calls: []simulation.Expect{ - { - Name: "plaintext on tls port", - Call: mkCall(8080, simulation.Plaintext), - Result: simulation.Result{Error: simulation.ErrNoFilterChain}, - }, - { - Name: "tls on tls port", - Call: mkCall(8080, simulation.MTLS), - Result: simulation.Result{ClusterMatched: "inbound|8080||"}, - }, - // Despite being defined in the Service, we get no filter chain since its not in Sidecar - { - Name: "plaintext on plaintext port", - Call: mkCall(9090, simulation.Plaintext), - // port 9090 not defined in partialSidecar and will use plain text, plaintext request should pass. - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - { - Name: "tls on plaintext port", - Call: mkCall(9090, simulation.MTLS), - // no ports defined, so we will passthrough - Result: simulation.Result{ClusterMatched: "InboundPassthroughClusterIpv4"}, - }, - }, - }, - } - proxy := &model.Proxy{Metadata: &model.NodeMetadata{Labels: map[string]string{"app": "foo"}}} - for _, tt := range cases { - runSimulationTest(t, proxy, xds.FakeOptions{}, simulationTest{ - name: tt.name, - config: tt.config, - calls: tt.calls, - }) - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/route/leak_test.go b/pilot/pkg/networking/core/v1alpha3/route/leak_test.go deleted file mode 100644 index fe2facf3c..000000000 --- a/pilot/pkg/networking/core/v1alpha3/route/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package route - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/networking/core/v1alpha3/route/retry/retry.go b/pilot/pkg/networking/core/v1alpha3/route/retry/retry.go deleted file mode 100644 index ad6b0c98b..000000000 --- a/pilot/pkg/networking/core/v1alpha3/route/retry/retry.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package retry - -import ( - "net/http" - "strconv" - "strings" -) - -import ( - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - previouspriorities "github.com/envoyproxy/go-control-plane/envoy/extensions/retry/priority/previous_priorities/v3" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - xdsfilters "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/filters" -) - -var defaultRetryPriorityTypedConfig = util.MessageToAny(buildPreviousPrioritiesConfig()) - -// DefaultPolicy gets a copy of the default retry policy. -func DefaultPolicy() *route.RetryPolicy { - policy := route.RetryPolicy{ - NumRetries: &wrappers.UInt32Value{Value: 2}, - RetryOn: "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes", - RetriableStatusCodes: []uint32{http.StatusServiceUnavailable}, - RetryHostPredicate: []*route.RetryPolicy_RetryHostPredicate{ - // to configure retries to prefer hosts that haven’t been attempted already, - // the builtin `envoy.retry_host_predicates.previous_hosts` predicate can be used. - xdsfilters.RetryPreviousHosts, - }, - // TODO: allow this to be configured via API. - HostSelectionRetryMaxAttempts: 5, - } - return &policy -} - -// ConvertPolicy converts the given Istio retry policy to an Envoy policy. -// -// If in is nil, DefaultPolicy is returned. -// -// If in.Attempts == 0, returns nil. -// -// Otherwise, the returned policy is DefaultPolicy with the following overrides: -// -// - NumRetries: set from in.Attempts -// -// - RetryOn, RetriableStatusCodes: set from in.RetryOn (if specified). RetriableStatusCodes -// is appended when encountering parts that are valid HTTP status codes. -// -// - PerTryTimeout: set from in.PerTryTimeout (if specified) -func ConvertPolicy(in *networking.HTTPRetry) *route.RetryPolicy { - if in == nil { - // No policy was set, use a default. - return DefaultPolicy() - } - - if in.Attempts <= 0 { - // Configuration is explicitly disabling the retry policy. - return nil - } - - // A policy was specified. Start with the default and override with user-provided fields where appropriate. - out := DefaultPolicy() - out.NumRetries = &wrappers.UInt32Value{Value: uint32(in.Attempts)} - - if in.RetryOn != "" { - // Allow the incoming configuration to specify both Envoy RetryOn and RetriableStatusCodes. Any integers are - // assumed to be status codes. - out.RetryOn, out.RetriableStatusCodes = parseRetryOn(in.RetryOn) - // If user has just specified HTTP status codes in retryOn but have not specified "retriable-status-codes", let us add it. - if len(out.RetriableStatusCodes) > 0 && !strings.Contains(out.RetryOn, "retriable-status-codes") { - out.RetryOn += ",retriable-status-codes" - } - } - - if in.PerTryTimeout != nil { - out.PerTryTimeout = in.PerTryTimeout - } - - if in.RetryRemoteLocalities != nil && in.RetryRemoteLocalities.GetValue() { - out.RetryPriority = &route.RetryPolicy_RetryPriority{ - Name: "envoy.retry_priorities.previous_priorities", - ConfigType: &route.RetryPolicy_RetryPriority_TypedConfig{ - TypedConfig: defaultRetryPriorityTypedConfig, - }, - } - } - - return out -} - -func parseRetryOn(retryOn string) (string, []uint32) { - codes := make([]uint32, 0) - tojoin := make([]string, 0) - - parts := strings.Split(retryOn, ",") - for _, part := range parts { - part = strings.TrimSpace(part) - if part == "" { - continue - } - - // Try converting it to an integer to see if it's a valid HTTP status code. - i, err := strconv.Atoi(part) - - if err == nil && http.StatusText(i) != "" { - codes = append(codes, uint32(i)) - } else { - tojoin = append(tojoin, part) - } - } - - return strings.Join(tojoin, ","), codes -} - -// buildPreviousPrioritiesConfig builds a PreviousPrioritiesConfig with a default -// value for UpdateFrequency which indicated how often to update the priority. -func buildPreviousPrioritiesConfig() *previouspriorities.PreviousPrioritiesConfig { - return &previouspriorities.PreviousPrioritiesConfig{ - UpdateFrequency: int32(2), - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/route/retry/retry_test.go b/pilot/pkg/networking/core/v1alpha3/route/retry/retry_test.go deleted file mode 100644 index 89bd16233..000000000 --- a/pilot/pkg/networking/core/v1alpha3/route/retry/retry_test.go +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package retry_test - -import ( - "reflect" - "testing" - "time" -) - -import ( - envoyroute "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - previouspriorities "github.com/envoyproxy/go-control-plane/envoy/extensions/retry/priority/previous_priorities/v3" - . "github.com/onsi/gomega" - "google.golang.org/protobuf/types/known/durationpb" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3/route/retry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" -) - -func TestRetry(t *testing.T) { - testCases := []struct { - name string - route *networking.HTTPRoute - assertFunc func(g *WithT, policy *envoyroute.RetryPolicy) - }{ - { - name: "TestNilRetryShouldReturnDefault", - // Create a route where no retry policy has been explicitly set. - route: &networking.HTTPRoute{}, - assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { - g.Expect(policy).To(Not(BeNil())) - g.Expect(policy).To(Equal(retry.DefaultPolicy())) - }, - }, - { - name: "TestZeroAttemptsShouldReturnNilPolicy", - // Create a route with a retry policy with zero attempts configured. - route: &networking.HTTPRoute{ - Retries: &networking.HTTPRetry{ - // Explicitly not retrying. - Attempts: 0, - }, - }, - assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { - g.Expect(policy).To(BeNil()) - }, - }, - { - name: "TestRetryWithAllFieldsSet", - // Create a route with a retry policy with all fields configured. - route: &networking.HTTPRoute{ - Retries: &networking.HTTPRetry{ - Attempts: 2, - RetryOn: "some,fake,conditions", - PerTryTimeout: durationpb.New(time.Second * 3), - }, - }, - assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { - g.Expect(policy).To(Not(BeNil())) - g.Expect(policy.RetryOn).To(Equal("some,fake,conditions")) - g.Expect(policy.PerTryTimeout).To(Equal(durationpb.New(time.Second * 3))) - g.Expect(policy.NumRetries.Value).To(Equal(uint32(2))) - g.Expect(policy.RetriableStatusCodes).To(Equal(make([]uint32, 0))) - g.Expect(policy.RetryPriority).To(BeNil()) - g.Expect(policy.HostSelectionRetryMaxAttempts).To(Equal(retry.DefaultPolicy().HostSelectionRetryMaxAttempts)) - g.Expect(policy.RetryHostPredicate).To(Equal(retry.DefaultPolicy().RetryHostPredicate)) - }, - }, - { - name: "TestRetryOnWithEmptyParts", - // Create a route with a retry policy with empty retry conditions configured. - route: &networking.HTTPRoute{ - Retries: &networking.HTTPRetry{ - // Explicitly not retrying. - Attempts: 2, - RetryOn: "some,fake,conditions,,,", - }, - }, - assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { - g.Expect(policy).To(Not(BeNil())) - g.Expect(policy.RetryOn).To(Equal("some,fake,conditions")) - g.Expect(policy.RetriableStatusCodes).To(Equal([]uint32{})) - }, - }, - { - name: "TestRetryOnWithRetriableStatusCodes", - // Create a route with a retry policy with retriable status code. - route: &networking.HTTPRoute{ - Retries: &networking.HTTPRetry{ - // Explicitly not retrying. - Attempts: 2, - RetryOn: "gateway-error,retriable-status-codes,503", - }, - }, - assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { - g.Expect(policy).To(Not(BeNil())) - g.Expect(policy.RetryOn).To(Equal("gateway-error,retriable-status-codes")) - g.Expect(policy.RetriableStatusCodes).To(Equal([]uint32{503})) - }, - }, - { - name: "TestRetryOnWithWhitespace", - // Create a route with a retry policy with retryOn having white spaces. - route: &networking.HTTPRoute{ - Retries: &networking.HTTPRetry{ - // Explicitly not retrying. - Attempts: 2, - RetryOn: " some, ,fake , conditions, ,", - }, - }, - assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { - g.Expect(policy).To(Not(BeNil())) - g.Expect(policy.RetryOn).To(Equal("some,fake,conditions")) - g.Expect(policy.RetriableStatusCodes).To(Equal([]uint32{})) - }, - }, - { - name: "TestRetryOnContainingStatusCodes", - // Create a route with a retry policy with status codes. - route: &networking.HTTPRoute{ - Retries: &networking.HTTPRetry{ - Attempts: 2, - RetryOn: "some,fake,5xx,404,conditions,503", - }, - }, - assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { - g.Expect(policy).To(Not(BeNil())) - g.Expect(policy.RetryOn).To(Equal("some,fake,5xx,conditions,retriable-status-codes")) - g.Expect(policy.RetriableStatusCodes).To(Equal([]uint32{404, 503})) - }, - }, - { - name: "TestRetryOnWithInvalidStatusCodesShouldAddToRetryOn", - // Create a route with a retry policy with invalid status codes. - route: &networking.HTTPRoute{ - Retries: &networking.HTTPRetry{ - Attempts: 2, - RetryOn: "some,fake,conditions,1000", - }, - }, - assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { - g.Expect(policy).To(Not(BeNil())) - g.Expect(policy.RetryOn).To(Equal("some,fake,conditions,1000")) - g.Expect(policy.RetriableStatusCodes).To(Equal([]uint32{})) - }, - }, - { - name: "TestMissingRetryOnShouldReturnDefaults", - // Create a route with a retry policy with two attempts configured. - route: &networking.HTTPRoute{ - Retries: &networking.HTTPRetry{ - Attempts: 2, - }, - }, - assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { - g.Expect(policy).To(Not(BeNil())) - g.Expect(policy.RetryOn).To(Equal(retry.DefaultPolicy().RetryOn)) - g.Expect(policy.RetriableStatusCodes).To(Equal(retry.DefaultPolicy().RetriableStatusCodes)) - }, - }, - { - name: "TestMissingPerTryTimeoutShouldReturnNil", - // Create a route with a retry policy without per try timeout. - route: &networking.HTTPRoute{ - Retries: &networking.HTTPRetry{ - Attempts: 2, - }, - }, - assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { - g.Expect(policy).To(Not(BeNil())) - g.Expect(policy.PerTryTimeout).To(BeNil()) - }, - }, - { - name: "TestRetryRemoteLocalities", - // Create a route with a retry policy with RetryRemoteLocalities enabled. - route: &networking.HTTPRoute{ - Retries: &networking.HTTPRetry{ - Attempts: 2, - RetryRemoteLocalities: &wrappers.BoolValue{ - Value: true, - }, - }, - }, - assertFunc: func(g *WithT, policy *envoyroute.RetryPolicy) { - g.Expect(policy).To(Not(BeNil())) - g.Expect(policy.RetryOn).To(Equal(retry.DefaultPolicy().RetryOn)) - g.Expect(policy.RetriableStatusCodes).To(Equal(retry.DefaultPolicy().RetriableStatusCodes)) - - previousPrioritiesConfig := &previouspriorities.PreviousPrioritiesConfig{ - UpdateFrequency: int32(2), - } - expected := &envoyroute.RetryPolicy_RetryPriority{ - Name: "envoy.retry_priorities.previous_priorities", - ConfigType: &envoyroute.RetryPolicy_RetryPriority_TypedConfig{ - TypedConfig: util.MessageToAny(previousPrioritiesConfig), - }, - } - if !reflect.DeepEqual(policy.RetryPriority, expected) { - t.Fatalf("Expected %v, actual %v", expected, policy.RetryPriority) - } - }, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - g := NewWithT(t) - policy := retry.ConvertPolicy(tc.route.Retries) - if tc.assertFunc != nil { - tc.assertFunc(g, policy) - } - }) - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/route/route.go b/pilot/pkg/networking/core/v1alpha3/route/route.go deleted file mode 100644 index 820fe25e1..000000000 --- a/pilot/pkg/networking/core/v1alpha3/route/route.go +++ /dev/null @@ -1,1347 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package route - -import ( - "fmt" - "regexp" - "sort" - "strconv" - "strings" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - xdsfault "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/common/fault/v3" - xdshttpfault "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/fault/v3" - matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" - xdstype "github.com/envoyproxy/go-control-plane/envoy/type/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - any "google.golang.org/protobuf/types/known/anypb" - "google.golang.org/protobuf/types/known/durationpb" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3/route/retry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/telemetry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - authz "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/authz/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/util/constant" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/proto" -) - -// Headers with special meaning in Envoy -const ( - HeaderMethod = ":method" - HeaderAuthority = ":authority" - HeaderScheme = ":scheme" -) - -// DefaultRouteName is the name assigned to a route generated by default in absence of a virtual service. -const DefaultRouteName = "default" - -// prefixMatchRegex optionally matches "/..." at the end of a path. -// regex taken from https://github.com/projectcontour/contour/blob/2b3376449bedfea7b8cea5fbade99fb64009c0f6/internal/envoy/v3/route.go#L59 -const prefixMatchRegex = `((\/).*)?` - -// VirtualHostWrapper is a context-dependent virtual host entry with guarded routes. -// Note: Currently we are not fully utilizing this structure. We could invoke this logic -// once for all sidecars in the cluster to compute all RDS for inside the mesh and arrange -// it by listener port. However to properly use such an optimization, we need to have an -// eventing subsystem to invalidate the computed routes if any service changes/virtual Services change. -type VirtualHostWrapper struct { - // Port is the listener port for outbound sidecar (e.g. service port) - Port int - - // Services are the Services from the registry. Each service - // in this list should have a virtual host entry - Services []*model.Service - - // VirtualServiceHosts is a list of hosts defined in the virtual service - // if virtual service hostname is same as a the service registry host, then - // the host would appear in Services as we need to generate all variants of the - // service's hostname within a platform (e.g., foo, foo.default, foo.default.svc, etc.) - VirtualServiceHosts []string - - // Routes in the virtual host - Routes []*route.Route -} - -// BuildSidecarVirtualHostWrapper creates virtual hosts from -// the given set of virtual Services and a list of Services from the -// service registry. Services are indexed by FQDN hostnames. -// The list of Services is also passed to allow maintaining consistent ordering. -func BuildSidecarVirtualHostWrapper(routeCache *Cache, node *model.Proxy, push *model.PushContext, serviceRegistry map[host.Name]*model.Service, - virtualServices []config.Config, listenPort int, -) []VirtualHostWrapper { - out := make([]VirtualHostWrapper, 0) - - // dependentDestinationRules includes all the destinationrules referenced by the virtualservices, which have consistent hash policy. - dependentDestinationRules := []*config.Config{} - // consistent hash policies for the http route destinations - hashByDestination := map[*networking.HTTPRouteDestination]*networking.LoadBalancerSettings_ConsistentHashLB{} - for _, virtualService := range virtualServices { - for _, httpRoute := range virtualService.Spec.(*networking.VirtualService).Http { - for _, destination := range httpRoute.Route { - hostName := destination.Destination.Host - var configNamespace string - if serviceRegistry[host.Name(hostName)] != nil { - configNamespace = serviceRegistry[host.Name(hostName)].Attributes.Namespace - } else { - configNamespace = virtualService.Namespace - } - hash, destinationRule := GetHashForHTTPDestination(push, node, destination, configNamespace) - if hash != nil { - hashByDestination[destination] = hash - dependentDestinationRules = append(dependentDestinationRules, destinationRule) - } - } - } - } - - // translate all virtual service configs into virtual hosts - for _, virtualService := range virtualServices { - wrappers := buildSidecarVirtualHostsForVirtualService(node, virtualService, serviceRegistry, hashByDestination, listenPort, push.Mesh) - out = append(out, wrappers...) - } - - // compute Services missing virtual service configs - for _, wrapper := range out { - for _, service := range wrapper.Services { - delete(serviceRegistry, service.Hostname) - } - } - - hashByService := map[host.Name]map[int]*networking.LoadBalancerSettings_ConsistentHashLB{} - for _, svc := range serviceRegistry { - for _, port := range svc.Ports { - if port.Protocol.IsHTTP() || util.IsProtocolSniffingEnabledForPort(port) { - hash, destinationRule := getHashForService(node, push, svc, port) - if hash != nil { - if _, ok := hashByService[svc.Hostname]; !ok { - hashByService[svc.Hostname] = map[int]*networking.LoadBalancerSettings_ConsistentHashLB{} - } - hashByService[svc.Hostname][port.Port] = hash - dependentDestinationRules = append(dependentDestinationRules, destinationRule) - } - } - } - } - - if routeCache != nil { - routeCache.DestinationRules = dependentDestinationRules - } - - // append default hosts for the service missing virtual Services - out = append(out, buildSidecarVirtualHostsForService(serviceRegistry, hashByService, push.Mesh)...) - return out -} - -// separateVSHostsAndServices splits the virtual service hosts into Services (if they are found in the registry) and -// plain non-registry hostnames -func separateVSHostsAndServices(virtualService config.Config, - serviceRegistry map[host.Name]*model.Service, -) ([]string, []*model.Service) { - rule := virtualService.Spec.(*networking.VirtualService) - hosts := make([]string, 0) - servicesInVirtualService := make([]*model.Service, 0) - wchosts := make([]host.Name, 0) - - // As a performance optimization, process non wildcard hosts first, so that they can be - // looked up directly in the service registry map. - for _, hostname := range rule.Hosts { - vshost := host.Name(hostname) - if !vshost.IsWildCarded() { - if svc, exists := serviceRegistry[vshost]; exists { - servicesInVirtualService = append(servicesInVirtualService, svc) - } else { - hosts = append(hosts, hostname) - } - } else { - // Add it to the wildcard hosts so that they can be processed later. - wchosts = append(wchosts, vshost) - } - } - - // Now process wild card hosts as they need to follow the slow path of looping through all Services in the registry. - for _, hostname := range wchosts { - if model.UseGatewaySemantics(virtualService) { - hosts = append(hosts, string(hostname)) - continue - } - // Say host is *.global - foundSvcMatch := false - // Say we have Services *.foo.global, *.bar.global - for svcHost, svc := range serviceRegistry { - // *.foo.global matches *.global - if svcHost.Matches(hostname) { - servicesInVirtualService = append(servicesInVirtualService, svc) - foundSvcMatch = true - } - } - if !foundSvcMatch { - hosts = append(hosts, string(hostname)) - } - } - - return hosts, servicesInVirtualService -} - -// buildSidecarVirtualHostsForVirtualService creates virtual hosts corresponding to a virtual service. -// Called for each port to determine the list of vhosts on the given port. -// It may return an empty list if no VirtualService rule has a matching service. -func buildSidecarVirtualHostsForVirtualService( - node *model.Proxy, - virtualService config.Config, - serviceRegistry map[host.Name]*model.Service, - hashByDestination map[*networking.HTTPRouteDestination]*networking.LoadBalancerSettings_ConsistentHashLB, - listenPort int, - mesh *meshconfig.MeshConfig, -) []VirtualHostWrapper { - meshGateway := map[string]bool{constants.IstioMeshGateway: true} - routes, err := BuildHTTPRoutesForVirtualService(node, virtualService, serviceRegistry, hashByDestination, - listenPort, meshGateway, false /* isH3DiscoveryNeeded */, mesh) - if err != nil || len(routes) == 0 { - return nil - } - - hosts, servicesInVirtualService := separateVSHostsAndServices(virtualService, serviceRegistry) - - // Now group these Services by port so that we can infer the destination.port if the user - // doesn't specify any port for a multiport service. We need to know the destination port in - // order to build the cluster name (outbound|||) - // If the destination service is being accessed on port X, we set that as the default - // destination port - serviceByPort := make(map[int][]*model.Service) - for _, svc := range servicesInVirtualService { - for _, port := range svc.Ports { - if port.Protocol.IsHTTP() || util.IsProtocolSniffingEnabledForPort(port) { - serviceByPort[port.Port] = append(serviceByPort[port.Port], svc) - } - } - } - - if len(serviceByPort) == 0 { - if listenPort == 80 { - // TODO: This is a gross HACK. Fix me. Its a much bigger surgery though, due to the way - // the current code is written. - serviceByPort[80] = nil - } - } - - out := make([]VirtualHostWrapper, 0, len(serviceByPort)) - for port, services := range serviceByPort { - out = append(out, VirtualHostWrapper{ - Port: port, - Services: services, - VirtualServiceHosts: hosts, - Routes: routes, - }) - } - - return out -} - -func buildSidecarVirtualHostsForService( - serviceRegistry map[host.Name]*model.Service, - hashByService map[host.Name]map[int]*networking.LoadBalancerSettings_ConsistentHashLB, - mesh *meshconfig.MeshConfig, -) []VirtualHostWrapper { - out := make([]VirtualHostWrapper, 0) - for _, svc := range serviceRegistry { - for _, port := range svc.Ports { - if port.Protocol.IsHTTP() || util.IsProtocolSniffingEnabledForPort(port) { - cluster := model.BuildSubsetKey(model.TrafficDirectionOutbound, "", svc.Hostname, port.Port) - traceOperation := telemetry.TraceOperation(string(svc.Hostname), port.Port) - httpRoute := BuildDefaultHTTPOutboundRoute(cluster, traceOperation, mesh) - - // if this host has no virtualservice, the consistentHash on its destinationRule will be useless - if hashByPort, ok := hashByService[svc.Hostname]; ok { - hashPolicy := consistentHashToHashPolicy(hashByPort[port.Port]) - if hashPolicy != nil { - httpRoute.GetRoute().HashPolicy = []*route.RouteAction_HashPolicy{hashPolicy} - } - } - out = append(out, VirtualHostWrapper{ - Port: port.Port, - Services: []*model.Service{svc}, - Routes: []*route.Route{httpRoute}, - }) - } - } - } - return out -} - -// GetDestinationCluster generates a cluster name for the route, or error if no cluster -// can be found. Called by translateRule to determine if -func GetDestinationCluster(destination *networking.Destination, service *model.Service, listenerPort int) string { - port := listenerPort - if destination.GetPort() != nil { - port = int(destination.GetPort().GetNumber()) - } else if service != nil && len(service.Ports) == 1 { - // if service only has one port defined, use that as the port, otherwise use default listenerPort - port = service.Ports[0].Port - - // Do not return blackhole cluster for service==nil case as there is a legitimate use case for - // calling this function with nil service: to route to a pre-defined statically configured cluster - // declared as part of the bootstrap. - // If blackhole cluster is needed, do the check on the caller side. See gateway and tls.go for examples. - } - - return model.BuildSubsetKey(model.TrafficDirectionOutbound, destination.Subset, host.Name(destination.Host), port) -} - -// BuildHTTPRoutesForVirtualService creates data plane HTTP routes from the virtual service spec. -// The rule should be adapted to destination names (outbound clusters). -// Each rule is guarded by source labels. -// -// This is called for each port to compute virtual hosts. -// Each VirtualService is tried, with a list of Services that listen on the port. -// Error indicates the given virtualService can't be used on the port. -// This function is used by both the gateway and the sidecar -func BuildHTTPRoutesForVirtualService( - node *model.Proxy, - virtualService config.Config, - serviceRegistry map[host.Name]*model.Service, - hashByDestination map[*networking.HTTPRouteDestination]*networking.LoadBalancerSettings_ConsistentHashLB, - listenPort int, - gatewayNames map[string]bool, - isHTTP3AltSvcHeaderNeeded bool, - mesh *meshconfig.MeshConfig, -) ([]*route.Route, error) { - vs, ok := virtualService.Spec.(*networking.VirtualService) - if !ok { // should never happen - return nil, fmt.Errorf("in not a virtual service: %#v", virtualService) - } - - out := make([]*route.Route, 0, len(vs.Http)) - - catchall := false - for _, http := range vs.Http { - if len(http.Match) == 0 { - if r := translateRoute(node, http, nil, listenPort, virtualService, serviceRegistry, - hashByDestination, gatewayNames, isHTTP3AltSvcHeaderNeeded, mesh); r != nil { - out = append(out, r) - } - catchall = true - } else { - for _, match := range http.Match { - if r := translateRoute(node, http, match, listenPort, virtualService, serviceRegistry, - hashByDestination, gatewayNames, isHTTP3AltSvcHeaderNeeded, mesh); r != nil { - out = append(out, r) - // This is a catch all path. Routes are matched in order, so we will never go beyond this match - // As an optimization, we can just top sending any more routes here. - if isCatchAllRoute(r) { - catchall = true - break - } - } - } - } - if catchall { - break - } - } - - if len(out) == 0 { - return nil, fmt.Errorf("no routes matched") - } - return out, nil -} - -// sourceMatchHttp checks if the sourceLabels or the gateways in a match condition match with the -// labels for the proxy or the gateway name for which we are generating a route -func sourceMatchHTTP(match *networking.HTTPMatchRequest, proxyLabels labels.Instance, gatewayNames map[string]bool, proxyNamespace string) bool { - if match == nil { - return true - } - - // Trim by source labels or mesh gateway - if len(match.Gateways) > 0 { - for _, g := range match.Gateways { - if gatewayNames[g] { - return true - } - } - } else if labels.Instance(match.GetSourceLabels()).SubsetOf(proxyLabels) { - return match.SourceNamespace == "" || match.SourceNamespace == proxyNamespace - } - - return false -} - -// translateRoute translates HTTP routes -func translateRoute( - node *model.Proxy, - in *networking.HTTPRoute, - match *networking.HTTPMatchRequest, - listenPort int, - virtualService config.Config, - serviceRegistry map[host.Name]*model.Service, - hashByDestination map[*networking.HTTPRouteDestination]*networking.LoadBalancerSettings_ConsistentHashLB, - gatewayNames map[string]bool, - isHTTP3AltSvcHeaderNeeded bool, - mesh *meshconfig.MeshConfig, -) *route.Route { - // When building routes, it's okay if the target cluster cannot be - // resolved Traffic to such clusters will blackhole. - - // Match by the destination port specified in the match condition - if match != nil && match.Port != 0 && match.Port != uint32(listenPort) { - return nil - } - // Match by source labels/gateway names inside the match condition - if !sourceMatchHTTP(match, node.Metadata.Labels, gatewayNames, node.Metadata.Namespace) { - return nil - } - - routeName := in.Name - if match != nil && match.Name != "" { - routeName = routeName + "." + match.Name - } - - out := &route.Route{ - Name: routeName, - Match: translateRouteMatch(node, virtualService, match), - Metadata: util.BuildConfigInfoMetadata(virtualService.Meta), - } - authority := "" - if in.Headers != nil { - operations := translateHeadersOperations(in.Headers) - out.RequestHeadersToAdd = operations.requestHeadersToAdd - out.ResponseHeadersToAdd = operations.responseHeadersToAdd - out.RequestHeadersToRemove = operations.requestHeadersToRemove - out.ResponseHeadersToRemove = operations.responseHeadersToRemove - authority = operations.authority - } - - if in.Redirect != nil { - applyRedirect(out, in.Redirect, listenPort) - } else { - applyHTTPRouteDestination(out, node, in, mesh, authority, serviceRegistry, listenPort, hashByDestination) - } - - out.Decorator = &route.Decorator{ - Operation: getRouteOperation(out, virtualService.Name, listenPort), - } - if in.Fault != nil { - out.TypedPerFilterConfig = make(map[string]*any.Any) - out.TypedPerFilterConfig[wellknown.Fault] = util.MessageToAny(translateFault(in.Fault)) - } - - if isHTTP3AltSvcHeaderNeeded { - http3AltSvcHeader := buildHTTP3AltSvcHeader(listenPort, util.ALPNHttp3OverQUIC) - if out.ResponseHeadersToAdd == nil { - out.ResponseHeadersToAdd = make([]*core.HeaderValueOption, 0) - } - out.ResponseHeadersToAdd = append(out.ResponseHeadersToAdd, http3AltSvcHeader) - } - - return out -} - -func applyHTTPRouteDestination( - out *route.Route, - node *model.Proxy, - in *networking.HTTPRoute, - mesh *meshconfig.MeshConfig, - authority string, - serviceRegistry map[host.Name]*model.Service, - listenerPort int, - hashByDestination map[*networking.HTTPRouteDestination]*networking.LoadBalancerSettings_ConsistentHashLB, -) { - policy := in.Retries - if policy == nil { - // No VS policy set, use mesh defaults - policy = mesh.GetDefaultHttpRetryPolicy() - } - action := &route.RouteAction{ - Cors: translateCORSPolicy(in.CorsPolicy), - RetryPolicy: retry.ConvertPolicy(policy), - } - - // Configure timeouts specified by Virtual Service if they are provided, otherwise set it to defaults. - action.Timeout = features.DefaultRequestTimeout - if in.Timeout != nil { - action.Timeout = in.Timeout - } - if node.IsProxylessGrpc() { - // TODO(stevenctl) merge these paths; grpc's xDS impl will not read the deprecated value - action.MaxStreamDuration = &route.RouteAction_MaxStreamDuration{MaxStreamDuration: action.Timeout} - } else { - // Use deprecated value for now as the replacement MaxStreamDuration has some regressions. - // nolint: staticcheck - action.MaxGrpcTimeout = action.Timeout - } - - out.Action = &route.Route_Route{Route: action} - - if in.Rewrite != nil { - action.PrefixRewrite = in.Rewrite.GetUri() - if in.Rewrite.GetAuthority() != "" { - authority = in.Rewrite.GetAuthority() - } - } - if authority != "" { - action.HostRewriteSpecifier = &route.RouteAction_HostRewriteLiteral{ - HostRewriteLiteral: authority, - } - } - - if in.Mirror != nil { - if mp := mirrorPercent(in); mp != nil { - action.RequestMirrorPolicies = []*route.RouteAction_RequestMirrorPolicy{{ - Cluster: GetDestinationCluster(in.Mirror, serviceRegistry[host.Name(in.Mirror.Host)], listenerPort), - RuntimeFraction: mp, - TraceSampled: &wrappers.BoolValue{Value: false}, - }} - } - } - - // TODO: eliminate this logic and use the total_weight option in envoy route - weighted := make([]*route.WeightedCluster_ClusterWeight, 0) - for _, dst := range in.Route { - weight := &wrappers.UInt32Value{Value: uint32(dst.Weight)} - if dst.Weight == 0 { - // Ignore 0 weighted clusters if there are other clusters in the route. - // But if this is the only cluster in the route, then add it as a cluster with weight 100 - if len(in.Route) == 1 { - weight.Value = uint32(100) - } else { - continue - } - } - hostname := host.Name(dst.GetDestination().GetHost()) - n := GetDestinationCluster(dst.Destination, serviceRegistry[hostname], listenerPort) - clusterWeight := &route.WeightedCluster_ClusterWeight{ - Name: n, - Weight: weight, - } - if dst.Headers != nil { - operations := translateHeadersOperations(dst.Headers) - clusterWeight.RequestHeadersToAdd = operations.requestHeadersToAdd - clusterWeight.RequestHeadersToRemove = operations.requestHeadersToRemove - clusterWeight.ResponseHeadersToAdd = operations.responseHeadersToAdd - clusterWeight.ResponseHeadersToRemove = operations.responseHeadersToRemove - if operations.authority != "" { - clusterWeight.HostRewriteSpecifier = &route.WeightedCluster_ClusterWeight_HostRewriteLiteral{ - HostRewriteLiteral: operations.authority, - } - } - } - - weighted = append(weighted, clusterWeight) - hash := hashByDestination[dst] - hashPolicy := consistentHashToHashPolicy(hash) - if hashPolicy != nil { - action.HashPolicy = append(action.HashPolicy, hashPolicy) - } - } - - // rewrite to a single cluster if there is only weighted cluster - if len(weighted) == 1 { - action.ClusterSpecifier = &route.RouteAction_Cluster{Cluster: weighted[0].Name} - out.RequestHeadersToAdd = append(out.RequestHeadersToAdd, weighted[0].RequestHeadersToAdd...) - out.RequestHeadersToRemove = append(out.RequestHeadersToRemove, weighted[0].RequestHeadersToRemove...) - out.ResponseHeadersToAdd = append(out.ResponseHeadersToAdd, weighted[0].ResponseHeadersToAdd...) - out.ResponseHeadersToRemove = append(out.ResponseHeadersToRemove, weighted[0].ResponseHeadersToRemove...) - if weighted[0].HostRewriteSpecifier != nil && action.HostRewriteSpecifier == nil { - // Ideally, if the weighted cluster overwrites authority, it has precedence. This mirrors behavior of headers, - // because for headers we append the weighted last which allows it to Set and wipe out previous Adds. - // However, Envoy behavior is different when we set at both cluster level and route level, and we want - // behavior to be consistent with a single cluster and multiple clusters. - // As a result, we only override if the top level rewrite is not set - action.HostRewriteSpecifier = &route.RouteAction_HostRewriteLiteral{ - HostRewriteLiteral: weighted[0].GetHostRewriteLiteral(), - } - } - } else { - action.ClusterSpecifier = &route.RouteAction_WeightedClusters{ - WeightedClusters: &route.WeightedCluster{ - Clusters: weighted, - }, - } - } -} - -func applyRedirect(out *route.Route, redirect *networking.HTTPRedirect, port int) { - action := &route.Route_Redirect{ - Redirect: &route.RedirectAction{ - HostRedirect: redirect.Authority, - PathRewriteSpecifier: &route.RedirectAction_PathRedirect{ - PathRedirect: redirect.Uri, - }, - }, - } - - if redirect.Scheme != "" { - action.Redirect.SchemeRewriteSpecifier = &route.RedirectAction_SchemeRedirect{SchemeRedirect: redirect.Scheme} - } - - if redirect.RedirectPort != nil { - switch rp := redirect.RedirectPort.(type) { - case *networking.HTTPRedirect_DerivePort: - if rp.DerivePort == networking.HTTPRedirect_FROM_REQUEST_PORT { - // Envoy doesn't actually support deriving the port from the request dynamically. However, - // we always generate routes in the context of a specific request port. As a result, we can just - // use that port - action.Redirect.PortRedirect = uint32(port) - } - // Otherwise, no port needed; HTTPRedirect_FROM_PROTOCOL_DEFAULT is Envoy's default behavior - case *networking.HTTPRedirect_Port: - action.Redirect.PortRedirect = rp.Port - } - } - - switch redirect.RedirectCode { - case 0, 301: - action.Redirect.ResponseCode = route.RedirectAction_MOVED_PERMANENTLY - case 302: - action.Redirect.ResponseCode = route.RedirectAction_FOUND - case 303: - action.Redirect.ResponseCode = route.RedirectAction_SEE_OTHER - case 307: - action.Redirect.ResponseCode = route.RedirectAction_TEMPORARY_REDIRECT - case 308: - action.Redirect.ResponseCode = route.RedirectAction_PERMANENT_REDIRECT - default: - log.Warnf("Redirect Code %d is not yet supported", redirect.RedirectCode) - action = nil - } - - out.Action = action -} - -func buildHTTP3AltSvcHeader(port int, h3Alpns []string) *core.HeaderValueOption { - // For example, www.cloudflare.com returns the following - // alt-svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400, h3=":443"; ma=86400 - valParts := make([]string, 0, len(h3Alpns)) - for _, alpn := range h3Alpns { - // Max-age is hardcoded to 1 day for now. - valParts = append(valParts, fmt.Sprintf(`%s=":%d"; ma=86400`, alpn, port)) - } - headerVal := strings.Join(valParts, ", ") - return &core.HeaderValueOption{ - Append: proto.BoolTrue, - Header: &core.HeaderValue{ - Key: util.AltSvcHeader, - Value: headerVal, - }, - } -} - -// SortHeaderValueOption type and the functions below (Len, Less and Swap) are for sort.Stable for type HeaderValueOption -type SortHeaderValueOption []*core.HeaderValueOption - -// mirrorPercent computes the mirror percent to be used based on "Mirror" data in route. -func mirrorPercent(in *networking.HTTPRoute) *core.RuntimeFractionalPercent { - switch { - case in.MirrorPercentage != nil: - if in.MirrorPercentage.GetValue() > 0 { - return &core.RuntimeFractionalPercent{ - DefaultValue: translatePercentToFractionalPercent(in.MirrorPercentage), - } - } - // If zero percent is provided explicitly, we should not mirror. - return nil - // nolint: staticcheck - case in.MirrorPercent != nil: - if in.MirrorPercent.GetValue() > 0 { - return &core.RuntimeFractionalPercent{ - DefaultValue: translateIntegerToFractionalPercent((int32(in.MirrorPercent.GetValue()))), - } - } - // If zero percent is provided explicitly, we should not mirror. - return nil - default: - // Default to 100 percent if percent is not given. - return &core.RuntimeFractionalPercent{ - DefaultValue: translateIntegerToFractionalPercent(100), - } - } -} - -// Len is i the sort.Interface for SortHeaderValueOption -func (b SortHeaderValueOption) Len() int { - return len(b) -} - -// Less is in the sort.Interface for SortHeaderValueOption -func (b SortHeaderValueOption) Less(i, j int) bool { - if b[i] == nil || b[i].Header == nil { - return false - } else if b[j] == nil || b[j].Header == nil { - return true - } - return strings.Compare(b[i].Header.Key, b[j].Header.Key) < 0 -} - -// Swap is in the sort.Interface for SortHeaderValueOption -func (b SortHeaderValueOption) Swap(i, j int) { - b[i], b[j] = b[j], b[i] -} - -// translateAppendHeaders translates headers -func translateAppendHeaders(headers map[string]string, appendFlag bool) ([]*core.HeaderValueOption, string) { - if len(headers) == 0 { - return nil, "" - } - authority := "" - headerValueOptionList := make([]*core.HeaderValueOption, 0, len(headers)) - for key, value := range headers { - if isAuthorityHeader(key) { - // If there are multiple, last one wins; validation will reject - authority = value - } - if isInternalHeader(key) { - continue - } - headerValueOptionList = append(headerValueOptionList, &core.HeaderValueOption{ - Header: &core.HeaderValue{ - Key: key, - Value: value, - }, - Append: &wrappers.BoolValue{Value: appendFlag}, - }) - } - sort.Stable(SortHeaderValueOption(headerValueOptionList)) - return headerValueOptionList, authority -} - -type headersOperations struct { - requestHeadersToAdd []*core.HeaderValueOption - responseHeadersToAdd []*core.HeaderValueOption - requestHeadersToRemove []string - responseHeadersToRemove []string - authority string -} - -// isInternalHeader returns true if a header refers to an internal value that cannot be modified by Envoy -func isInternalHeader(headerKey string) bool { - return strings.HasPrefix(headerKey, ":") || strings.EqualFold(headerKey, "host") -} - -// isAuthorityHeader returns true if a header refers to the authority header -func isAuthorityHeader(headerKey string) bool { - return strings.EqualFold(headerKey, ":authority") || strings.EqualFold(headerKey, "host") -} - -func dropInternal(keys []string) []string { - result := make([]string, 0, len(keys)) - for _, k := range keys { - if isInternalHeader(k) { - continue - } - result = append(result, k) - } - return result -} - -// translateHeadersOperations translates headers operations -func translateHeadersOperations(headers *networking.Headers) headersOperations { - req := headers.GetRequest() - resp := headers.GetResponse() - - requestHeadersToAdd, setAuthority := translateAppendHeaders(req.GetSet(), false) - reqAdd, addAuthority := translateAppendHeaders(req.GetAdd(), true) - requestHeadersToAdd = append(requestHeadersToAdd, reqAdd...) - - responseHeadersToAdd, _ := translateAppendHeaders(resp.GetSet(), false) - respAdd, _ := translateAppendHeaders(resp.GetAdd(), true) - responseHeadersToAdd = append(responseHeadersToAdd, respAdd...) - - auth := addAuthority - if setAuthority != "" { - // If authority is set in 'add' and 'set', pick the one from 'set' - auth = setAuthority - } - return headersOperations{ - requestHeadersToAdd: requestHeadersToAdd, - responseHeadersToAdd: responseHeadersToAdd, - requestHeadersToRemove: dropInternal(req.GetRemove()), - responseHeadersToRemove: dropInternal(resp.GetRemove()), - authority: auth, - } -} - -// translateRouteMatch translates match condition -func translateRouteMatch(node *model.Proxy, vs config.Config, in *networking.HTTPMatchRequest) *route.RouteMatch { - out := &route.RouteMatch{PathSpecifier: &route.RouteMatch_Prefix{Prefix: "/"}} - if in == nil { - return out - } - - for name, stringMatch := range in.Headers { - // The metadata matcher takes precedence over the header matcher. - if metadataMatcher := translateMetadataMatch(name, stringMatch); metadataMatcher != nil { - out.DynamicMetadata = append(out.DynamicMetadata, metadataMatcher) - } else { - matcher := translateHeaderMatch(name, stringMatch) - out.Headers = append(out.Headers, matcher) - } - } - - for name, stringMatch := range in.WithoutHeaders { - if metadataMatcher := translateMetadataMatch(name, stringMatch); metadataMatcher != nil { - metadataMatcher.Invert = true - out.DynamicMetadata = append(out.DynamicMetadata, metadataMatcher) - } else { - matcher := translateHeaderMatch(name, stringMatch) - matcher.InvertMatch = true - out.Headers = append(out.Headers, matcher) - } - } - - // guarantee ordering of headers - sort.Slice(out.Headers, func(i, j int) bool { - return out.Headers[i].Name < out.Headers[j].Name - }) - - if in.Uri != nil { - switch m := in.Uri.MatchType.(type) { - case *networking.StringMatch_Exact: - out.PathSpecifier = &route.RouteMatch_Path{Path: m.Exact} - case *networking.StringMatch_Prefix: - if (model.UseIngressSemantics(vs) || model.UseGatewaySemantics(vs)) && m.Prefix != "/" { - path := strings.TrimSuffix(m.Prefix, "/") - if util.IsIstioVersionGE114(node.IstioVersion) { - out.PathSpecifier = &route.RouteMatch_PathSeparatedPrefix{PathSeparatedPrefix: path} - } else { - // For older versions, we have to use the regex hack. - // From the spec: /foo/bar matches /foo/bar/baz, but does not match /foo/barbaz - // and if the prefix is /foo/bar/ we must match /foo/bar and /foo/bar/baz. We cannot simply strip the - // trailing "/" and do a prefix match since we'll match unwanted continuations and we cannot add - // a "/" if not present since we won't match the prefix without trailing "/". Must be smarter and - // use regex. - out.PathSpecifier = &route.RouteMatch_SafeRegex{ - SafeRegex: &matcher.RegexMatcher{ - EngineType: util.RegexEngine, - Regex: regexp.QuoteMeta(path) + prefixMatchRegex, - }, - } - } - } else { - out.PathSpecifier = &route.RouteMatch_Prefix{Prefix: m.Prefix} - } - case *networking.StringMatch_Regex: - out.PathSpecifier = &route.RouteMatch_SafeRegex{ - SafeRegex: &matcher.RegexMatcher{ - EngineType: util.RegexEngine, - Regex: m.Regex, - }, - } - } - } - - out.CaseSensitive = &wrappers.BoolValue{Value: !in.IgnoreUriCase} - - if in.Method != nil { - matcher := translateHeaderMatch(HeaderMethod, in.Method) - out.Headers = append(out.Headers, matcher) - } - - if in.Authority != nil { - matcher := translateHeaderMatch(HeaderAuthority, in.Authority) - out.Headers = append(out.Headers, matcher) - } - - if in.Scheme != nil { - matcher := translateHeaderMatch(HeaderScheme, in.Scheme) - out.Headers = append(out.Headers, matcher) - } - - for name, stringMatch := range in.QueryParams { - matcher := translateQueryParamMatch(name, stringMatch) - out.QueryParameters = append(out.QueryParameters, matcher) - } - - return out -} - -// translateQueryParamMatch translates a StringMatch to a QueryParameterMatcher. -func translateQueryParamMatch(name string, in *networking.StringMatch) *route.QueryParameterMatcher { - out := &route.QueryParameterMatcher{ - Name: name, - } - - switch m := in.MatchType.(type) { - case *networking.StringMatch_Exact: - out.QueryParameterMatchSpecifier = &route.QueryParameterMatcher_StringMatch{ - StringMatch: &matcher.StringMatcher{MatchPattern: &matcher.StringMatcher_Exact{Exact: m.Exact}}, - } - case *networking.StringMatch_Regex: - out.QueryParameterMatchSpecifier = &route.QueryParameterMatcher_StringMatch{ - StringMatch: &matcher.StringMatcher{ - MatchPattern: &matcher.StringMatcher_SafeRegex{ - SafeRegex: &matcher.RegexMatcher{ - EngineType: util.RegexEngine, - Regex: m.Regex, - }, - }, - }, - } - } - - return out -} - -// isCatchAllHeaderMatch determines if the given header is matched with all strings or not. -// Currently, if the regex has "*" value, it returns true -func isCatchAllHeaderMatch(in *networking.StringMatch) bool { - if in == nil { - return true - } - - catchall := false - - switch m := in.MatchType.(type) { - case *networking.StringMatch_Regex: - catchall = m.Regex == "*" - } - - return catchall -} - -// translateMetadataMatch translates a header match to dynamic metadata matcher. Returns nil if the header is not supported -// or the header format is invalid for generating metadata matcher. -// -// The currently only supported header is @request.auth.claims for JWT claims matching. Claims of type string or list of string -// are supported and nested claims are also supported using `.` as a separator for claim names. -// Examples: -// - `@request.auth.claims.admin` matches the claim "admin". -// - `@request.auth.claims.group.id` matches the nested claims "group" and "id". -func translateMetadataMatch(name string, in *networking.StringMatch) *matcher.MetadataMatcher { - if !strings.HasPrefix(strings.ToLower(name), constant.HeaderJWTClaim) { - return nil - } - claims := strings.Split(name[len(constant.HeaderJWTClaim):], ".") - return authz.MetadataMatcherForJWTClaims(claims, util.ConvertToEnvoyMatch(in)) -} - -// translateHeaderMatch translates to HeaderMatcher -func translateHeaderMatch(name string, in *networking.StringMatch) *route.HeaderMatcher { - out := &route.HeaderMatcher{ - Name: name, - } - - if isCatchAllHeaderMatch(in) { - out.HeaderMatchSpecifier = &route.HeaderMatcher_PresentMatch{PresentMatch: true} - return out - } - - if em := util.ConvertToEnvoyMatch(in); em != nil { - out.HeaderMatchSpecifier = &route.HeaderMatcher_StringMatch{ - StringMatch: em, - } - } - - return out -} - -// translateCORSPolicy translates CORS policy -func translateCORSPolicy(in *networking.CorsPolicy) *route.CorsPolicy { - if in == nil { - return nil - } - - // CORS filter is enabled by default - out := route.CorsPolicy{} - // nolint: staticcheck - if in.AllowOrigins != nil { - out.AllowOriginStringMatch = util.ConvertToEnvoyMatches(in.AllowOrigins) - } else if in.AllowOrigin != nil { - out.AllowOriginStringMatch = util.StringToExactMatch(in.AllowOrigin) - } - - out.EnabledSpecifier = &route.CorsPolicy_FilterEnabled{ - FilterEnabled: &core.RuntimeFractionalPercent{ - DefaultValue: &xdstype.FractionalPercent{ - Numerator: 100, - Denominator: xdstype.FractionalPercent_HUNDRED, - }, - }, - } - - out.AllowCredentials = in.AllowCredentials - out.AllowHeaders = strings.Join(in.AllowHeaders, ",") - out.AllowMethods = strings.Join(in.AllowMethods, ",") - out.ExposeHeaders = strings.Join(in.ExposeHeaders, ",") - if in.MaxAge != nil { - out.MaxAge = strconv.FormatInt(in.MaxAge.GetSeconds(), 10) - } - return &out -} - -// getRouteOperation returns readable route description for trace. -func getRouteOperation(in *route.Route, vsName string, port int) string { - path := "/*" - m := in.GetMatch() - ps := m.GetPathSpecifier() - if ps != nil { - switch ps.(type) { - case *route.RouteMatch_Prefix: - path = m.GetPrefix() + "*" - case *route.RouteMatch_Path: - path = m.GetPath() - case *route.RouteMatch_SafeRegex: - path = m.GetSafeRegex().GetRegex() - } - } - - // If there is only one destination cluster in route, return host:port/uri as description of route. - // Otherwise there are multiple destination clusters and destination host is not clear. For that case - // return virtual serivce name:port/uri as substitute. - if c := in.GetRoute().GetCluster(); model.IsValidSubsetKey(c) { - // Parse host and port from cluster name. - _, _, h, p := model.ParseSubsetKey(c) - return string(h) + ":" + strconv.Itoa(p) + path - } - return vsName + ":" + strconv.Itoa(port) + path -} - -// BuildDefaultHTTPInboundRoute builds a default inbound route. -func BuildDefaultHTTPInboundRoute(clusterName string, operation string) *route.Route { - notimeout := durationpb.New(0) - routeAction := &route.RouteAction{ - ClusterSpecifier: &route.RouteAction_Cluster{Cluster: clusterName}, - Timeout: notimeout, - } - routeAction.MaxStreamDuration = &route.RouteAction_MaxStreamDuration{ - MaxStreamDuration: notimeout, - // If not configured at all, the grpc-timeout header is not used and - // gRPC requests time out like any other requests using timeout or its default. - GrpcTimeoutHeaderMax: notimeout, - } - val := &route.Route{ - Match: translateRouteMatch(nil, config.Config{}, nil), - Decorator: &route.Decorator{ - Operation: operation, - }, - Action: &route.Route_Route{ - Route: routeAction, - }, - } - - val.Name = DefaultRouteName - return val -} - -// BuildDefaultHTTPOutboundRoute builds a default outbound route, including a retry policy. -func BuildDefaultHTTPOutboundRoute(clusterName string, operation string, mesh *meshconfig.MeshConfig) *route.Route { - // Start with the same configuration as for inbound. - out := BuildDefaultHTTPInboundRoute(clusterName, operation) - - // Add a default retry policy for outbound routes. - out.GetRoute().RetryPolicy = retry.ConvertPolicy(mesh.GetDefaultHttpRetryPolicy()) - return out -} - -// translatePercentToFractionalPercent translates an v1alpha3 Percent instance -// to an envoy.type.FractionalPercent instance. -func translatePercentToFractionalPercent(p *networking.Percent) *xdstype.FractionalPercent { - return &xdstype.FractionalPercent{ - Numerator: uint32(p.Value * 10000), - Denominator: xdstype.FractionalPercent_MILLION, - } -} - -// translateIntegerToFractionalPercent translates an int32 instance to an -// envoy.type.FractionalPercent instance. -func translateIntegerToFractionalPercent(p int32) *xdstype.FractionalPercent { - return &xdstype.FractionalPercent{ - Numerator: uint32(p), - Denominator: xdstype.FractionalPercent_HUNDRED, - } -} - -// translateFault translates networking.HTTPFaultInjection into Envoy's HTTPFault -func translateFault(in *networking.HTTPFaultInjection) *xdshttpfault.HTTPFault { - if in == nil { - return nil - } - - out := xdshttpfault.HTTPFault{} - if in.Delay != nil { - out.Delay = &xdsfault.FaultDelay{} - if in.Delay.Percentage != nil { - out.Delay.Percentage = translatePercentToFractionalPercent(in.Delay.Percentage) - } else { - out.Delay.Percentage = translateIntegerToFractionalPercent(in.Delay.Percent) // nolint: staticcheck - } - switch d := in.Delay.HttpDelayType.(type) { - case *networking.HTTPFaultInjection_Delay_FixedDelay: - out.Delay.FaultDelaySecifier = &xdsfault.FaultDelay_FixedDelay{ - FixedDelay: d.FixedDelay, - } - default: - log.Warnf("Exponential faults are not yet supported") - out.Delay = nil - } - } - - if in.Abort != nil { - out.Abort = &xdshttpfault.FaultAbort{} - if in.Abort.Percentage != nil { - out.Abort.Percentage = translatePercentToFractionalPercent(in.Abort.Percentage) - } - switch a := in.Abort.ErrorType.(type) { - case *networking.HTTPFaultInjection_Abort_HttpStatus: - out.Abort.ErrorType = &xdshttpfault.FaultAbort_HttpStatus{ - HttpStatus: uint32(a.HttpStatus), - } - default: - log.Warnf("Non-HTTP type abort faults are not yet supported") - out.Abort = nil - } - } - - if out.Delay == nil && out.Abort == nil { - return nil - } - - return &out -} - -func portLevelSettingsConsistentHash(dst *networking.Destination, - pls []*networking.TrafficPolicy_PortTrafficPolicy, -) *networking.LoadBalancerSettings_ConsistentHashLB { - if dst.Port != nil { - portNumber := dst.GetPort().GetNumber() - for _, setting := range pls { - number := setting.GetPort().GetNumber() - if number == portNumber { - return setting.GetLoadBalancer().GetConsistentHash() - } - } - } - - return nil -} - -func consistentHashToHashPolicy(consistentHash *networking.LoadBalancerSettings_ConsistentHashLB) *route.RouteAction_HashPolicy { - switch consistentHash.GetHashKey().(type) { - case *networking.LoadBalancerSettings_ConsistentHashLB_HttpHeaderName: - return &route.RouteAction_HashPolicy{ - PolicySpecifier: &route.RouteAction_HashPolicy_Header_{ - Header: &route.RouteAction_HashPolicy_Header{ - HeaderName: consistentHash.GetHttpHeaderName(), - }, - }, - } - case *networking.LoadBalancerSettings_ConsistentHashLB_HttpCookie: - cookie := consistentHash.GetHttpCookie() - var ttl *durationpb.Duration - if cookie.GetTtl() != nil { - ttl = cookie.GetTtl() - } - return &route.RouteAction_HashPolicy{ - PolicySpecifier: &route.RouteAction_HashPolicy_Cookie_{ - Cookie: &route.RouteAction_HashPolicy_Cookie{ - Name: cookie.GetName(), - Ttl: ttl, - Path: cookie.GetPath(), - }, - }, - } - case *networking.LoadBalancerSettings_ConsistentHashLB_UseSourceIp: - return &route.RouteAction_HashPolicy{ - PolicySpecifier: &route.RouteAction_HashPolicy_ConnectionProperties_{ - ConnectionProperties: &route.RouteAction_HashPolicy_ConnectionProperties{ - SourceIp: consistentHash.GetUseSourceIp(), - }, - }, - } - case *networking.LoadBalancerSettings_ConsistentHashLB_HttpQueryParameterName: - return &route.RouteAction_HashPolicy{ - PolicySpecifier: &route.RouteAction_HashPolicy_QueryParameter_{ - QueryParameter: &route.RouteAction_HashPolicy_QueryParameter{ - Name: consistentHash.GetHttpQueryParameterName(), - }, - }, - } - } - return nil -} - -func getHashForService(node *model.Proxy, push *model.PushContext, - svc *model.Service, port *model.Port, -) (*networking.LoadBalancerSettings_ConsistentHashLB, *config.Config) { - if push == nil { - return nil, nil - } - destinationRule := node.SidecarScope.DestinationRule(model.TrafficDirectionOutbound, node, svc.Hostname) - if destinationRule == nil { - return nil, nil - } - rule := destinationRule.Spec.(*networking.DestinationRule) - consistentHash := rule.GetTrafficPolicy().GetLoadBalancer().GetConsistentHash() - portLevelSettings := rule.GetTrafficPolicy().GetPortLevelSettings() - for _, setting := range portLevelSettings { - number := setting.GetPort().GetNumber() - if int(number) == port.Port { - if setting.GetLoadBalancer().GetConsistentHash() != nil { - consistentHash = setting.GetLoadBalancer().GetConsistentHash() - } - break - } - } - - return consistentHash, destinationRule -} - -func GetConsistentHashForVirtualService(push *model.PushContext, node *model.Proxy, - virtualService config.Config, - serviceRegistry map[host.Name]*model.Service, -) map[*networking.HTTPRouteDestination]*networking.LoadBalancerSettings_ConsistentHashLB { - hashByDestination := map[*networking.HTTPRouteDestination]*networking.LoadBalancerSettings_ConsistentHashLB{} - for _, httpRoute := range virtualService.Spec.(*networking.VirtualService).Http { - for _, destination := range httpRoute.Route { - hostName := destination.Destination.Host - var configNamespace string - if serviceRegistry[host.Name(hostName)] != nil { - configNamespace = serviceRegistry[host.Name(hostName)].Attributes.Namespace - } else { - configNamespace = virtualService.Namespace - } - hash, _ := GetHashForHTTPDestination(push, node, destination, configNamespace) - if hash != nil { - hashByDestination[destination] = hash - } - } - } - - return hashByDestination -} - -// GetHashForHTTPDestination return the ConsistentHashLB and the DestinationRule associated with HTTP route destination. -func GetHashForHTTPDestination(push *model.PushContext, node *model.Proxy, dst *networking.HTTPRouteDestination, - configNamespace string, -) (*networking.LoadBalancerSettings_ConsistentHashLB, *config.Config) { - if push == nil { - return nil, nil - } - - destination := dst.GetDestination() - destinationRule := node.SidecarScope.DestinationRule(model.TrafficDirectionOutbound, node, host.Name(destination.Host)) - if destinationRule == nil { - return nil, nil - } - - rule := destinationRule.Spec.(*networking.DestinationRule) - - consistentHash := rule.GetTrafficPolicy().GetLoadBalancer().GetConsistentHash() - portLevelSettings := rule.GetTrafficPolicy().GetPortLevelSettings() - plsHash := portLevelSettingsConsistentHash(destination, portLevelSettings) - - var subsetHash, subsetPLSHash *networking.LoadBalancerSettings_ConsistentHashLB - for _, subset := range rule.GetSubsets() { - if subset.GetName() == destination.GetSubset() { - subsetPortLevelSettings := subset.GetTrafficPolicy().GetPortLevelSettings() - subsetHash = subset.GetTrafficPolicy().GetLoadBalancer().GetConsistentHash() - subsetPLSHash = portLevelSettingsConsistentHash(destination, subsetPortLevelSettings) - break - } - } - - switch { - case subsetPLSHash != nil: - consistentHash = subsetPLSHash - case subsetHash != nil: - consistentHash = subsetHash - case plsHash != nil: - consistentHash = plsHash - } - return consistentHash, destinationRule -} - -// isCatchAll returns true if HTTPMatchRequest is a catchall match otherwise -// false. Note - this may not be exactly "catch all" as we don't know the full -// class of possible inputs As such, this is used only for optimization. -func isCatchAllMatch(m *networking.HTTPMatchRequest) bool { - catchall := false - if m.Uri != nil { - switch m := m.Uri.MatchType.(type) { - case *networking.StringMatch_Prefix: - catchall = m.Prefix == "/" - case *networking.StringMatch_Regex: - catchall = m.Regex == "*" - } - } - // A Match is catch all if and only if it has no match set - // and URI has a prefix / or regex *. - return catchall && - len(m.Headers) == 0 && - len(m.QueryParams) == 0 && - len(m.SourceLabels) == 0 && - len(m.WithoutHeaders) == 0 && - len(m.Gateways) == 0 && - m.Method == nil && - m.Scheme == nil && - m.Port == 0 && - m.Authority == nil && - m.SourceNamespace == "" -} - -// CombineVHostRoutes semi concatenates Vhost's routes into a single route set. -// Moves the catch all routes alone to the end, while retaining -// the relative order of other routes in the concatenated route. -// Assumes that the virtual Services that generated first and second are ordered by -// time. -func CombineVHostRoutes(routeSets ...[]*route.Route) []*route.Route { - l := 0 - for _, rs := range routeSets { - l += len(rs) - } - allroutes := make([]*route.Route, 0, l) - catchAllRoutes := make([]*route.Route, 0) - for _, routes := range routeSets { - for _, r := range routes { - if isCatchAllRoute(r) { - catchAllRoutes = append(catchAllRoutes, r) - } else { - allroutes = append(allroutes, r) - } - } - } - return append(allroutes, catchAllRoutes...) -} - -// isCatchAllRoute returns true if an Envoy route is a catchall route otherwise false. -func isCatchAllRoute(r *route.Route) bool { - catchall := false - switch ir := r.Match.PathSpecifier.(type) { - case *route.RouteMatch_Prefix: - catchall = ir.Prefix == "/" - case *route.RouteMatch_PathSeparatedPrefix: - catchall = ir.PathSeparatedPrefix == "/" - case *route.RouteMatch_SafeRegex: - catchall = ir.SafeRegex.GetRegex() == "*" - } - // A Match is catch all if and only if it has no header/query param match - // and URI has a prefix / or regex *. - return catchall && len(r.Match.Headers) == 0 && len(r.Match.QueryParameters) == 0 && len(r.Match.DynamicMetadata) == 0 -} diff --git a/pilot/pkg/networking/core/v1alpha3/route/route_cache.go b/pilot/pkg/networking/core/v1alpha3/route/route_cache.go deleted file mode 100644 index 8a2ffb493..000000000 --- a/pilot/pkg/networking/core/v1alpha3/route/route_cache.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package route - -import ( - "crypto/md5" - "encoding/hex" - "strconv" - "strings" -) - -import ( - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -// Cache includes the variables that can influence a Route Configuration. -// Implements XdsCacheEntry interface. -type Cache struct { - RouteName string - - ProxyVersion string - // proxy cluster ID - ClusterID string - // proxy dns domain - DNSDomain string - // DNSCapture indicates whether the workload has enabled dns capture - DNSCapture bool - // DNSAutoAllocate indicates whether the workload should have auto allocated addresses for ServiceEntry - // This allows resolving ServiceEntries, which is especially useful for distinguishing TCP traffic - // This depends on DNSCapture. - DNSAutoAllocate bool - // AllowAny indicates if the proxy should allow all outbound traffic or only known registries - AllowAny bool - - ListenerPort int - Services []*model.Service - VirtualServices []config.Config - DelegateVirtualServices []model.ConfigKey - DestinationRules []*config.Config - EnvoyFilterKeys []string -} - -func (r *Cache) Cacheable() bool { - if r == nil { - return false - } - if r.ListenerPort == 0 { - return false - } - - for _, config := range r.VirtualServices { - vs := config.Spec.(*networking.VirtualService) - for _, httpRoute := range vs.Http { - for _, match := range httpRoute.Match { - // if vs has source match, not cacheable - if len(match.SourceLabels) > 0 || match.SourceNamespace != "" { - return false - } - } - } - } - - return true -} - -func (r *Cache) DependentConfigs() []model.ConfigKey { - configs := make([]model.ConfigKey, 0, len(r.Services)+len(r.VirtualServices)+ - len(r.DelegateVirtualServices)+len(r.DestinationRules)+len(r.EnvoyFilterKeys)) - for _, svc := range r.Services { - configs = append(configs, model.ConfigKey{Kind: gvk.ServiceEntry, Name: string(svc.Hostname), Namespace: svc.Attributes.Namespace}) - } - for _, vs := range r.VirtualServices { - configs = append(configs, model.ConfigKey{Kind: gvk.VirtualService, Name: vs.Name, Namespace: vs.Namespace}) - } - // add delegate virtual services to dependent configs - // so that we can clear the rds cache when delegate virtual services are updated - configs = append(configs, r.DelegateVirtualServices...) - for _, dr := range r.DestinationRules { - configs = append(configs, model.ConfigKey{Kind: gvk.DestinationRule, Name: dr.Name, Namespace: dr.Namespace}) - } - - for _, efKey := range r.EnvoyFilterKeys { - items := strings.Split(efKey, "/") - configs = append(configs, model.ConfigKey{Kind: gvk.EnvoyFilter, Name: items[1], Namespace: items[0]}) - } - return configs -} - -func (r *Cache) DependentTypes() []config.GroupVersionKind { - return nil -} - -func (r *Cache) Key() string { - params := []string{ - r.RouteName, r.ProxyVersion, r.ClusterID, r.DNSDomain, - strconv.FormatBool(r.DNSCapture), strconv.FormatBool(r.DNSAutoAllocate), strconv.FormatBool(r.AllowAny), - } - for _, svc := range r.Services { - params = append(params, string(svc.Hostname)+"/"+svc.Attributes.Namespace) - } - for _, vs := range r.VirtualServices { - params = append(params, vs.Name+"/"+vs.Namespace) - } - for _, dr := range r.DestinationRules { - params = append(params, dr.Name+"/"+dr.Namespace) - } - params = append(params, r.EnvoyFilterKeys...) - - hash := md5.New() - for _, param := range params { - hash.Write([]byte(param)) - } - sum := hash.Sum(nil) - return hex.EncodeToString(sum) -} diff --git a/pilot/pkg/networking/core/v1alpha3/route/route_cache_test.go b/pilot/pkg/networking/core/v1alpha3/route/route_cache_test.go deleted file mode 100644 index 9776885e7..000000000 --- a/pilot/pkg/networking/core/v1alpha3/route/route_cache_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package route - -import ( - "reflect" - "testing" - "time" -) - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -func TestClearRDSCacheOnDelegateUpdate(t *testing.T) { - xdsCache := model.NewXdsCache() - // root virtual service - root := config.Config{ - Meta: config.Meta{Name: "root", Namespace: "default"}, - Spec: &networking.VirtualService{ - Http: []*networking.HTTPRoute{ - { - Name: "route", - Delegate: &networking.Delegate{ - Namespace: "default", - Name: "delegate", - }, - }, - }, - }, - } - // delegate virtual service - delegate := model.ConfigKey{Kind: gvk.VirtualService, Name: "delegate", Namespace: "default"} - // rds cache entry - entry := Cache{ - VirtualServices: []config.Config{root}, - DelegateVirtualServices: []model.ConfigKey{delegate}, - ListenerPort: 8080, - } - resource := &discovery.Resource{Name: "bar"} - - // add resource to cache - xdsCache.Add(&entry, &model.PushRequest{Start: time.Now()}, resource) - if got, found := xdsCache.Get(&entry); !found || !reflect.DeepEqual(got, resource) { - t.Fatalf("rds cache was not updated") - } - - // clear cache when delegate virtual service is updated - // this func is called by `dropCacheForRequest` in `initPushContext` - xdsCache.Clear(map[model.ConfigKey]struct{}{ - delegate: {}, - }) - if _, found := xdsCache.Get(&entry); found { - t.Fatalf("rds cache was not cleared") - } - - // add resource to cache - xdsCache.Add(&entry, &model.PushRequest{Start: time.Now()}, resource) - irrelevantDelegate := model.ConfigKey{Kind: gvk.VirtualService, Name: "foo", Namespace: "default"} - - // don't clear cache when irrelevant delegate virtual service is updated - xdsCache.Clear(map[model.ConfigKey]struct{}{ - irrelevantDelegate: {}, - }) - if got, found := xdsCache.Get(&entry); !found || !reflect.DeepEqual(got, resource) { - t.Fatalf("rds cache was cleared by irrelevant delegate virtual service update") - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/route/route_internal_test.go b/pilot/pkg/networking/core/v1alpha3/route/route_internal_test.go deleted file mode 100644 index f653a49d5..000000000 --- a/pilot/pkg/networking/core/v1alpha3/route/route_internal_test.go +++ /dev/null @@ -1,562 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package route - -import ( - "reflect" - "testing" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" - xdstype "github.com/envoyproxy/go-control-plane/envoy/type/v3" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - authzmatcher "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/authz/matcher" - authz "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/authz/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" -) - -func TestIsCatchAllMatch(t *testing.T) { - cases := []struct { - name string - match *networking.HTTPMatchRequest - want bool - }{ - { - name: "catch all prefix", - match: &networking.HTTPMatchRequest{ - Name: "catch-all", - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{ - Prefix: "/", - }, - }, - }, - want: true, - }, - { - name: "specific prefix match", - match: &networking.HTTPMatchRequest{ - Name: "specific match", - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{ - Prefix: "/a", - }, - }, - }, - want: false, - }, - { - name: "uri regex catch all", - match: &networking.HTTPMatchRequest{ - Name: "regex-catch-all", - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Regex{ - Regex: "*", - }, - }, - }, - want: true, - }, - { - name: "uri regex with headers", - match: &networking.HTTPMatchRequest{ - Name: "regex with headers", - Headers: map[string]*networking.StringMatch{ - "Authentication": { - MatchType: &networking.StringMatch_Regex{ - Regex: "Bearer .+?\\..+?\\..+?", - }, - }, - }, - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Regex{ - Regex: "*", - }, - }, - }, - want: false, - }, - { - name: "uri regex with query params", - match: &networking.HTTPMatchRequest{ - Name: "regex with query params", - QueryParams: map[string]*networking.StringMatch{ - "Authentication": { - MatchType: &networking.StringMatch_Regex{ - Regex: "Bearer .+?\\..+?\\..+?", - }, - }, - }, - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Regex{ - Regex: "*", - }, - }, - }, - want: false, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - match := isCatchAllMatch(tt.match) - if match != tt.want { - t.Errorf("Unexpected catchAllMatch want %v, got %v", tt.want, match) - } - }) - } -} - -func TestIsCatchAllRoute(t *testing.T) { - cases := []struct { - name string - route *route.Route - want bool - }{ - { - name: "catch all prefix", - route: &route.Route{ - Name: "catch-all", - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_Prefix{ - Prefix: "/", - }, - }, - }, - want: true, - }, - { - name: "catch all prefix >= 1.14", - route: &route.Route{ - Name: "catch-all", - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_PathSeparatedPrefix{ - PathSeparatedPrefix: "/", - }, - }, - }, - want: true, - }, - { - name: "catch all regex", - route: &route.Route{ - Name: "catch-all", - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_SafeRegex{ - SafeRegex: &matcher.RegexMatcher{ - EngineType: &matcher.RegexMatcher_GoogleRe2{GoogleRe2: &matcher.RegexMatcher_GoogleRE2{}}, - Regex: "*", - }, - }, - }, - }, - want: true, - }, - { - name: "catch all prefix with headers", - route: &route.Route{ - Name: "catch-all", - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_Prefix{ - Prefix: "/", - }, - Headers: []*route.HeaderMatcher{ - { - Name: "Authentication", - HeaderMatchSpecifier: &route.HeaderMatcher_ExactMatch{ - ExactMatch: "test", - }, - }, - }, - }, - }, - want: false, - }, - { - name: "uri regex with headers", - route: &route.Route{ - Name: "non-catch-all", - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_SafeRegex{ - SafeRegex: &matcher.RegexMatcher{ - // nolint: staticcheck - EngineType: &matcher.RegexMatcher_GoogleRe2{}, - Regex: "*", - }, - }, - Headers: []*route.HeaderMatcher{ - { - Name: "Authentication", - HeaderMatchSpecifier: &route.HeaderMatcher_StringMatch{ - StringMatch: &matcher.StringMatcher{ - MatchPattern: &matcher.StringMatcher_SafeRegex{ - SafeRegex: &matcher.RegexMatcher{ - EngineType: util.RegexEngine, - Regex: "*", - }, - }, - }, - }, - }, - }, - }, - }, - want: false, - }, - { - name: "uri regex with query params", - route: &route.Route{ - Name: "non-catch-all", - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_SafeRegex{ - SafeRegex: &matcher.RegexMatcher{ - // nolint: staticcheck - EngineType: &matcher.RegexMatcher_GoogleRe2{}, - Regex: "*", - }, - }, - QueryParameters: []*route.QueryParameterMatcher{ - { - Name: "Authentication", - QueryParameterMatchSpecifier: &route.QueryParameterMatcher_PresentMatch{ - PresentMatch: true, - }, - }, - }, - }, - }, - want: false, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - catchall := isCatchAllRoute(tt.route) - if catchall != tt.want { - t.Errorf("Unexpected catchAllMatch want %v, got %v", tt.want, catchall) - } - }) - } -} - -func TestCatchAllMatch(t *testing.T) { - cases := []struct { - name string - http *networking.HTTPMatchRequest - match bool - }{ - { - name: "catch all virtual service", - http: &networking.HTTPMatchRequest{ - Name: "catch-all", - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{ - Prefix: "/", - }, - }, - }, - match: true, - }, - { - name: "uri regex", - http: &networking.HTTPMatchRequest{ - Name: "regex-catch-all", - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Regex{ - Regex: "*", - }, - }, - }, - match: true, - }, - { - name: "uri regex with query params", - http: &networking.HTTPMatchRequest{ - Name: "regex-catch-all", - QueryParams: map[string]*networking.StringMatch{ - "Authentication": { - MatchType: &networking.StringMatch_Regex{ - Regex: "Bearer .+?\\..+?\\..+?", - }, - }, - }, - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Regex{ - Regex: "*", - }, - }, - }, - match: false, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - match := isCatchAllMatch(tt.http) - if tt.match != match { - t.Errorf("Expected a match=%v but got match=%v", tt.match, match) - } - }) - } -} - -func TestTranslateCORSPolicy(t *testing.T) { - corsPolicy := &networking.CorsPolicy{ - AllowOrigins: []*networking.StringMatch{ - {MatchType: &networking.StringMatch_Exact{Exact: "exact"}}, - {MatchType: &networking.StringMatch_Prefix{Prefix: "prefix"}}, - {MatchType: &networking.StringMatch_Regex{Regex: "regex"}}, - }, - } - expectedCorsPolicy := &route.CorsPolicy{ - AllowOriginStringMatch: []*matcher.StringMatcher{ - {MatchPattern: &matcher.StringMatcher_Exact{Exact: "exact"}}, - {MatchPattern: &matcher.StringMatcher_Prefix{Prefix: "prefix"}}, - { - MatchPattern: &matcher.StringMatcher_SafeRegex{ - SafeRegex: &matcher.RegexMatcher{ - EngineType: util.RegexEngine, - Regex: "regex", - }, - }, - }, - }, - EnabledSpecifier: &route.CorsPolicy_FilterEnabled{ - FilterEnabled: &core.RuntimeFractionalPercent{ - DefaultValue: &xdstype.FractionalPercent{ - Numerator: 100, - Denominator: xdstype.FractionalPercent_HUNDRED, - }, - }, - }, - } - if got := translateCORSPolicy(corsPolicy); !reflect.DeepEqual(got, expectedCorsPolicy) { - t.Errorf("translateCORSPolicy() = \n%v, want \n%v", got, expectedCorsPolicy) - } -} - -func TestMirrorPercent(t *testing.T) { - cases := []struct { - name string - route *networking.HTTPRoute - want *core.RuntimeFractionalPercent - }{ - { - name: "zero mirror percent", - route: &networking.HTTPRoute{ - Mirror: &networking.Destination{}, - MirrorPercent: &wrappers.UInt32Value{Value: 0.0}, - }, - want: nil, - }, - { - name: "mirror with no value given", - route: &networking.HTTPRoute{ - Mirror: &networking.Destination{}, - }, - want: &core.RuntimeFractionalPercent{ - DefaultValue: &xdstype.FractionalPercent{ - Numerator: 100, - Denominator: xdstype.FractionalPercent_HUNDRED, - }, - }, - }, - { - name: "mirror with actual percent", - route: &networking.HTTPRoute{ - Mirror: &networking.Destination{}, - MirrorPercent: &wrappers.UInt32Value{Value: 50}, - }, - want: &core.RuntimeFractionalPercent{ - DefaultValue: &xdstype.FractionalPercent{ - Numerator: 50, - Denominator: xdstype.FractionalPercent_HUNDRED, - }, - }, - }, - { - name: "zero mirror percentage", - route: &networking.HTTPRoute{ - Mirror: &networking.Destination{}, - MirrorPercentage: &networking.Percent{Value: 0.0}, - }, - want: nil, - }, - { - name: "mirrorpercentage with actual percent", - route: &networking.HTTPRoute{ - Mirror: &networking.Destination{}, - MirrorPercentage: &networking.Percent{Value: 50.0}, - }, - want: &core.RuntimeFractionalPercent{ - DefaultValue: &xdstype.FractionalPercent{ - Numerator: 500000, - Denominator: xdstype.FractionalPercent_MILLION, - }, - }, - }, - { - name: "mirrorpercentage takes precedence when both are given", - route: &networking.HTTPRoute{ - Mirror: &networking.Destination{}, - MirrorPercent: &wrappers.UInt32Value{Value: 40}, - MirrorPercentage: &networking.Percent{Value: 50.0}, - }, - want: &core.RuntimeFractionalPercent{ - DefaultValue: &xdstype.FractionalPercent{ - Numerator: 500000, - Denominator: xdstype.FractionalPercent_MILLION, - }, - }, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - mp := mirrorPercent(tt.route) - if !reflect.DeepEqual(mp, tt.want) { - t.Errorf("Unexpected mirro percent want %v, got %v", tt.want, mp) - } - }) - } -} - -func TestSourceMatchHTTP(t *testing.T) { - type args struct { - match *networking.HTTPMatchRequest - proxyLabels labels.Instance - gatewayNames map[string]bool - proxyNamespace string - } - tests := []struct { - name string - args args - want bool - }{ - { - "source namespace match", - args{ - match: &networking.HTTPMatchRequest{ - SourceNamespace: "foo", - }, - proxyNamespace: "foo", - }, - true, - }, - { - "source namespace not match", - args{ - match: &networking.HTTPMatchRequest{ - SourceNamespace: "foo", - }, - proxyNamespace: "bar", - }, - false, - }, - { - "source namespace not match when empty", - args{ - match: &networking.HTTPMatchRequest{ - SourceNamespace: "foo", - }, - proxyNamespace: "", - }, - false, - }, - { - "source namespace any", - args{ - match: &networking.HTTPMatchRequest{}, - proxyNamespace: "bar", - }, - true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := sourceMatchHTTP(tt.args.match, tt.args.proxyLabels, tt.args.gatewayNames, tt.args.proxyNamespace); got != tt.want { - t.Errorf("sourceMatchHTTP() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestTranslateMetadataMatch(t *testing.T) { - cases := []struct { - name string - in *networking.StringMatch - want *matcher.MetadataMatcher - }{ - { - name: "@request.auth.claims", - }, - { - name: "@request.auth.claims-", - }, - { - name: "request.auth.claims.", - }, - { - name: "@request.auth.claims-", - }, - { - name: "@request.auth.claims-abc", - }, - { - name: "x-some-other-header", - }, - { - name: "@request.auth.claims.key1", - in: &networking.StringMatch{MatchType: &networking.StringMatch_Exact{Exact: "exact"}}, - want: authz.MetadataMatcherForJWTClaims([]string{"key1"}, authzmatcher.StringMatcher("exact")), - }, - { - name: "@request.auth.claims.key1.KEY2", - in: &networking.StringMatch{MatchType: &networking.StringMatch_Exact{Exact: "exact"}}, - want: authz.MetadataMatcherForJWTClaims([]string{"key1", "KEY2"}, authzmatcher.StringMatcher("exact")), - }, - { - name: "@request.auth.claims.key1-key2", - in: &networking.StringMatch{MatchType: &networking.StringMatch_Exact{Exact: "exact"}}, - want: authz.MetadataMatcherForJWTClaims([]string{"key1-key2"}, authzmatcher.StringMatcher("exact")), - }, - { - name: "@request.auth.claims.prefix", - in: &networking.StringMatch{MatchType: &networking.StringMatch_Prefix{Prefix: "prefix"}}, - want: authz.MetadataMatcherForJWTClaims([]string{"prefix"}, authzmatcher.StringMatcher("prefix*")), - }, - { - name: "@request.auth.claims.regex", - in: &networking.StringMatch{MatchType: &networking.StringMatch_Regex{Regex: ".+?\\..+?\\..+?"}}, - want: authz.MetadataMatcherForJWTClaims([]string{"regex"}, authzmatcher.StringMatcherRegex(".+?\\..+?\\..+?")), - }, - } - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - got := translateMetadataMatch(tc.name, tc.in) - if !reflect.DeepEqual(got, tc.want) { - t.Errorf("Unexpected metadata matcher want %v, got %v", tc.want, got) - } - }) - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/route/route_test.go b/pilot/pkg/networking/core/v1alpha3/route/route_test.go deleted file mode 100644 index abbaad721..000000000 --- a/pilot/pkg/networking/core/v1alpha3/route/route_test.go +++ /dev/null @@ -1,1963 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package route_test - -import ( - "os" - "reflect" - "testing" - "time" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - envoyroute "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" - "github.com/onsi/gomega" - "google.golang.org/protobuf/types/known/durationpb" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3/route" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -func TestBuildHTTPRoutes(t *testing.T) { - serviceRegistry := map[host.Name]*model.Service{ - "*.example.org": { - Hostname: "*.example.org", - DefaultAddress: "1.1.1.1", - Ports: model.PortList{ - &model.Port{ - Name: "default", - Port: 8080, - Protocol: protocol.HTTP, - }, - }, - }, - } - - node := func(cg *v1alpha3.ConfigGenTest) *model.Proxy { - return cg.SetupProxy(&model.Proxy{ - Type: model.SidecarProxy, - IPAddresses: []string{"1.1.1.1"}, - ID: "someID", - DNSDomain: "foo.com", - }) - } - - gatewayNames := map[string]bool{"some-gateway": true} - - t.Run("for virtual service", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - os.Setenv("ISTIO_DEFAULT_REQUEST_TIMEOUT", "0ms") - defer os.Unsetenv("ISTIO_DEFAULT_REQUEST_TIMEOUT") - - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServicePlain, serviceRegistry, nil, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - // Validate that when timeout is not specified, we disable it based on default value of flag. - g.Expect(routes[0].GetRoute().Timeout.Seconds).To(gomega.Equal(int64(0))) - // nolint: staticcheck - g.Expect(routes[0].GetRoute().MaxGrpcTimeout.Seconds).To(gomega.Equal(int64(0))) - }) - - t.Run("for virtual service with HTTP/3 discovery enabled", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServicePlain, serviceRegistry, nil, 8080, gatewayNames, true, nil) - xdstest.ValidateRoutes(t, routes) - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(routes[0].GetResponseHeadersToAdd()).To(gomega.Equal([]*core.HeaderValueOption{ - { - Header: &core.HeaderValue{ - Key: util.AltSvcHeader, - Value: `h3=":8080"; ma=86400`, - }, - Append: &wrappers.BoolValue{Value: true}, - }, - })) - }) - - t.Run("for virtual service with changed default timeout", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - dt := features.DefaultRequestTimeout - features.DefaultRequestTimeout = durationpb.New(1 * time.Second) - defer func() { features.DefaultRequestTimeout = dt }() - - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServicePlain, serviceRegistry, nil, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - // Validate that when timeout is not specified, we send what is set in the timeout flag. - g.Expect(routes[0].GetRoute().Timeout.Seconds).To(gomega.Equal(int64(1))) - // nolint: staticcheck - g.Expect(routes[0].GetRoute().MaxGrpcTimeout.Seconds).To(gomega.Equal(int64(1))) - }) - - t.Run("for virtual service with timeout", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithTimeout, serviceRegistry, nil, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - // Validate that when timeout specified, we send the configured timeout to Envoys. - g.Expect(routes[0].GetRoute().Timeout.Seconds).To(gomega.Equal(int64(10))) - // nolint: staticcheck - g.Expect(routes[0].GetRoute().MaxGrpcTimeout.Seconds).To(gomega.Equal(int64(10))) - }) - - t.Run("for virtual service with disabled timeout", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithTimeoutDisabled, serviceRegistry, nil, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - g.Expect(routes[0].GetRoute().Timeout.Seconds).To(gomega.Equal(int64(0))) - // nolint: staticcheck - g.Expect(routes[0].GetRoute().MaxGrpcTimeout.Seconds).To(gomega.Equal(int64(0))) - }) - - t.Run("for virtual service with catch all route", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithCatchAllRoute, - serviceRegistry, nil, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(2)) - g.Expect(routes[0].Name).To(gomega.Equal("route.non-catch-all")) - g.Expect(routes[1].Name).To(gomega.Equal("route.catch-all")) - }) - - t.Run("for virtual service with catch all route:port match", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithCatchAllPort, - serviceRegistry, nil, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - g.Expect(routes[0].Name).To(gomega.Equal("route 1.catch-all for 8080")) - }) - - t.Run("for internally generated virtual service with ingress semantics (istio version<1.14)", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - vs := virtualServiceWithCatchAllRoute - if vs.Annotations == nil { - vs.Annotations = make(map[string]string) - } - vs.Annotations[constants.InternalRouteSemantics] = constants.RouteSemanticsIngress - - proxy := node(cg) - proxy.IstioVersion = &model.IstioVersion{ - Major: 1, - Minor: 13, - } - routes, err := route.BuildHTTPRoutesForVirtualService(proxy, vs, - serviceRegistry, nil, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(routes[0].Match.PathSpecifier).To(gomega.Equal(&envoyroute.RouteMatch_SafeRegex{ - SafeRegex: &matcher.RegexMatcher{ - EngineType: util.RegexEngine, - Regex: `/route/v1((\/).*)?`, - }, - })) - g.Expect(routes[1].Match.PathSpecifier).To(gomega.Equal(&envoyroute.RouteMatch_Prefix{ - Prefix: "/", - })) - }) - - t.Run("for internally generated virtual service with gateway semantics (istio version<1.14)", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - vs := virtualServiceWithCatchAllRoute - if vs.Annotations == nil { - vs.Annotations = make(map[string]string) - } - vs.Annotations[constants.InternalRouteSemantics] = constants.RouteSemanticsGateway - - proxy := node(cg) - proxy.IstioVersion = &model.IstioVersion{ - Major: 1, - Minor: 13, - } - routes, err := route.BuildHTTPRoutesForVirtualService(proxy, vs, - serviceRegistry, nil, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(routes[0].Match.PathSpecifier).To(gomega.Equal(&envoyroute.RouteMatch_SafeRegex{ - SafeRegex: &matcher.RegexMatcher{ - EngineType: util.RegexEngine, - Regex: `/route/v1((\/).*)?`, - }, - })) - g.Expect(routes[1].Match.PathSpecifier).To(gomega.Equal(&envoyroute.RouteMatch_Prefix{ - Prefix: "/", - })) - }) - - t.Run("for internally generated virtual service with ingress semantics (istio version>=1.14)", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - vs := virtualServiceWithCatchAllRoute - if vs.Annotations == nil { - vs.Annotations = make(map[string]string) - } - vs.Annotations[constants.InternalRouteSemantics] = constants.RouteSemanticsIngress - - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), vs, - serviceRegistry, nil, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(routes[0].Match.PathSpecifier).To(gomega.Equal(&envoyroute.RouteMatch_PathSeparatedPrefix{ - PathSeparatedPrefix: "/route/v1", - })) - g.Expect(routes[1].Match.PathSpecifier).To(gomega.Equal(&envoyroute.RouteMatch_Prefix{ - Prefix: "/", - })) - }) - - t.Run("for internally generated virtual service with gateway semantics (istio version>=1.14)", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - vs := virtualServiceWithCatchAllRoute - if vs.Annotations == nil { - vs.Annotations = make(map[string]string) - } - vs.Annotations[constants.InternalRouteSemantics] = constants.RouteSemanticsGateway - - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), vs, - serviceRegistry, nil, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(routes[0].Match.PathSpecifier).To(gomega.Equal(&envoyroute.RouteMatch_PathSeparatedPrefix{ - PathSeparatedPrefix: "/route/v1", - })) - g.Expect(routes[1].Match.PathSpecifier).To(gomega.Equal(&envoyroute.RouteMatch_Prefix{ - Prefix: "/", - })) - }) - - t.Run("for virtual service with top level catch all route", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithCatchAllRouteWeightedDestination, - serviceRegistry, nil, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - }) - - t.Run("for virtual service with multi prefix catch all route", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithCatchAllMultiPrefixRoute, - serviceRegistry, nil, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - }) - - t.Run("for virtual service with regex matching on URI", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithRegexMatchingOnURI, - serviceRegistry, nil, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - g.Expect(routes[0].GetMatch().GetSafeRegex().GetRegex()).To(gomega.Equal("\\/(.?)\\/status")) - }) - - t.Run("for virtual service with exact matching on JWT claims", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithExactMatchingOnHeaderForJWTClaims, - serviceRegistry, nil, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - g.Expect(len(routes[0].GetMatch().GetHeaders())).To(gomega.Equal(0)) - g.Expect(routes[0].GetMatch().GetDynamicMetadata()[0].GetFilter()).To(gomega.Equal("istio_authn")) - g.Expect(routes[0].GetMatch().GetDynamicMetadata()[0].GetInvert()).To(gomega.BeFalse()) - g.Expect(routes[0].GetMatch().GetDynamicMetadata()[1].GetFilter()).To(gomega.Equal("istio_authn")) - g.Expect(routes[0].GetMatch().GetDynamicMetadata()[1].GetInvert()).To(gomega.BeTrue()) - }) - - t.Run("for virtual service with regex matching on header", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithRegexMatchingOnHeader, - serviceRegistry, nil, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - g.Expect(routes[0].GetMatch().GetHeaders()[0].GetStringMatch().GetSafeRegex().GetRegex()).To(gomega.Equal("Bearer .+?\\..+?\\..+?")) - }) - - t.Run("for virtual service with regex matching on without_header", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithRegexMatchingOnWithoutHeader, - serviceRegistry, nil, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - g.Expect(routes[0].GetMatch().GetHeaders()[0].GetStringMatch().GetSafeRegex().GetRegex()).To(gomega.Equal("BAR .+?\\..+?\\..+?")) - g.Expect(routes[0].GetMatch().GetHeaders()[0].GetInvertMatch()).To(gomega.Equal(true)) - }) - - t.Run("for virtual service with presence matching on header", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithPresentMatchingOnHeader, - serviceRegistry, nil, 8080, gatewayNames, false, nil) - g.Expect(err).NotTo(gomega.HaveOccurred()) - xdstest.ValidateRoutes(t, routes) - g.Expect(len(routes)).To(gomega.Equal(1)) - g.Expect(routes[0].GetMatch().GetHeaders()[0].GetName()).To(gomega.Equal("FOO-HEADER")) - g.Expect(routes[0].GetMatch().GetHeaders()[0].GetPresentMatch()).To(gomega.Equal(true)) - g.Expect(routes[0].GetMatch().GetHeaders()[0].GetInvertMatch()).To(gomega.Equal(false)) - }) - - t.Run("for virtual service with presence matching on header and without_header", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithPresentMatchingOnWithoutHeader, - serviceRegistry, nil, 8080, gatewayNames, false, nil) - g.Expect(err).NotTo(gomega.HaveOccurred()) - xdstest.ValidateRoutes(t, routes) - g.Expect(len(routes)).To(gomega.Equal(1)) - g.Expect(routes[0].GetMatch().GetHeaders()[0].GetName()).To(gomega.Equal("FOO-HEADER")) - g.Expect(routes[0].GetMatch().GetHeaders()[0].GetPresentMatch()).To(gomega.Equal(true)) - g.Expect(routes[0].GetMatch().GetHeaders()[0].GetInvertMatch()).To(gomega.Equal(true)) - }) - - t.Run("for virtual service with regex matching for all cases on header", func(t *testing.T) { - cset := createVirtualServiceWithRegexMatchingForAllCasesOnHeader() - - for _, c := range cset { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), *c, serviceRegistry, nil, - 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - g.Expect(routes[0].GetMatch().GetHeaders()[0].GetName()).To(gomega.Equal("FOO-HEADER")) - g.Expect(routes[0].GetMatch().GetHeaders()[0].GetPresentMatch()).To(gomega.Equal(true)) - g.Expect(routes[0].GetMatch().GetHeaders()[0].GetInvertMatch()).To(gomega.Equal(false)) - } - }) - - t.Run("for virtual service with source namespace matching", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - fooNode := cg.SetupProxy(&model.Proxy{ - ConfigNamespace: "foo", - }) - - routes, err := route.BuildHTTPRoutesForVirtualService(fooNode, virtualServiceMatchingOnSourceNamespace, - serviceRegistry, nil, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - g.Expect(routes[0].GetName()).To(gomega.Equal("foo")) - - barNode := cg.SetupProxy(&model.Proxy{ - ConfigNamespace: "bar", - }) - - routes, err = route.BuildHTTPRoutesForVirtualService(barNode, virtualServiceMatchingOnSourceNamespace, - serviceRegistry, nil, 8080, gatewayNames, false, nil) - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - g.Expect(routes[0].GetName()).To(gomega.Equal("bar")) - }) - - t.Run("for virtual service with ring hash", func(t *testing.T) { - g := gomega.NewWithT(t) - ttl := durationpb.Duration{Nanos: 100} - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{ - Services: exampleService, - Configs: []config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: "acme", - Namespace: "dubbo-system", - }, - Spec: &networking.DestinationRule{ - Host: "*.example.org", - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{ - ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ - HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpCookie{ - HttpCookie: &networking.LoadBalancerSettings_ConsistentHashLB_HTTPCookie{ - Name: "hash-cookie", - Ttl: &ttl, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }) - - proxy := node(cg) - hashByDestination := route.GetConsistentHashForVirtualService(cg.PushContext(), proxy, virtualServicePlain, serviceRegistry) - routes, err := route.BuildHTTPRoutesForVirtualService(proxy, virtualServicePlain, serviceRegistry, - hashByDestination, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - - hashPolicy := &envoyroute.RouteAction_HashPolicy{ - PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{ - Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{ - Name: "hash-cookie", - Ttl: &ttl, - }, - }, - } - g.Expect(routes[0].GetRoute().GetHashPolicy()).To(gomega.ConsistOf(hashPolicy)) - }) - - t.Run("for virtual service with query param based ring hash", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{ - Services: exampleService, - Configs: []config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: "acme", - Namespace: "dubbo-system", - }, - Spec: &networking.DestinationRule{ - Host: "*.example.org", - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{ - ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ - HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpQueryParameterName{ - HttpQueryParameterName: "query", - }, - }, - }, - }, - }, - }, - }, - }, - }) - - proxy := node(cg) - hashByDestination := route.GetConsistentHashForVirtualService(cg.PushContext(), proxy, virtualServicePlain, serviceRegistry) - routes, err := route.BuildHTTPRoutesForVirtualService(proxy, virtualServicePlain, serviceRegistry, - hashByDestination, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - - hashPolicy := &envoyroute.RouteAction_HashPolicy{ - PolicySpecifier: &envoyroute.RouteAction_HashPolicy_QueryParameter_{ - QueryParameter: &envoyroute.RouteAction_HashPolicy_QueryParameter{ - Name: "query", - }, - }, - } - g.Expect(routes[0].GetRoute().GetHashPolicy()).To(gomega.ConsistOf(hashPolicy)) - }) - - t.Run("for virtual service with subsets with ring hash", func(t *testing.T) { - g := gomega.NewWithT(t) - virtualService := config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: virtualServiceWithSubset, - } - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{ - Services: exampleService, - Configs: []config.Config{ - virtualService, - { - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: "acme", - Namespace: "dubbo-system", - }, - Spec: &networking.DestinationRule{ - Host: "*.example.org", - Subsets: []*networking.Subset{networkingSubset}, - }, - }, - }, - }) - - proxy := node(cg) - hashByDestination := route.GetConsistentHashForVirtualService(cg.PushContext(), proxy, virtualService, serviceRegistry) - routes, err := route.BuildHTTPRoutesForVirtualService(proxy, virtualService, serviceRegistry, - hashByDestination, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - - hashPolicy := &envoyroute.RouteAction_HashPolicy{ - PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{ - Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{ - Name: "other-cookie", - Ttl: nil, - }, - }, - } - g.Expect(routes[0].GetRoute().GetHashPolicy()).To(gomega.ConsistOf(hashPolicy)) - }) - - t.Run("for virtual service with subsets with port level settings with ring hash", func(t *testing.T) { - g := gomega.NewWithT(t) - virtualService := config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: virtualServiceWithSubsetWithPortLevelSettings, - } - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{ - Services: exampleService, - Configs: []config.Config{ - virtualService, - { - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: "acme", - Namespace: "dubbo-system", - }, - Spec: portLevelDestinationRuleWithSubsetPolicy, - }, - }, - }) - - proxy := node(cg) - hashByDestination := route.GetConsistentHashForVirtualService(cg.PushContext(), proxy, virtualService, serviceRegistry) - routes, err := route.BuildHTTPRoutesForVirtualService(proxy, virtualService, serviceRegistry, - hashByDestination, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - - hashPolicy := &envoyroute.RouteAction_HashPolicy{ - PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{ - Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{ - Name: "port-level-settings-cookie", - Ttl: nil, - }, - }, - } - g.Expect(routes[0].GetRoute().GetHashPolicy()).To(gomega.ConsistOf(hashPolicy)) - }) - - t.Run("for virtual service with subsets and top level traffic policy with ring hash", func(t *testing.T) { - g := gomega.NewWithT(t) - - virtualService := config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: virtualServiceWithSubset, - } - - cnfg := config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: "acme", - Namespace: "dubbo-system", - }, - } - rule := networkingDestinationRule - rule.Subsets = []*networking.Subset{networkingSubset} - cnfg.Spec = networkingDestinationRule - - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{ - Services: exampleService, - Configs: []config.Config{cnfg, virtualService}, - }) - - proxy := node(cg) - hashByDestination := route.GetConsistentHashForVirtualService(cg.PushContext(), proxy, virtualService, serviceRegistry) - routes, err := route.BuildHTTPRoutesForVirtualService(proxy, virtualService, serviceRegistry, - hashByDestination, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - - hashPolicy := &envoyroute.RouteAction_HashPolicy{ - PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{ - Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{ - Name: "other-cookie", - Ttl: nil, - }, - }, - } - g.Expect(routes[0].GetRoute().GetHashPolicy()).To(gomega.ConsistOf(hashPolicy)) - }) - - t.Run("port selector based traffic policy", func(t *testing.T) { - g := gomega.NewWithT(t) - - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{ - Services: exampleService, - Configs: []config.Config{{ - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: "acme", - Namespace: "dubbo-system", - }, - Spec: portLevelDestinationRule, - }}, - }) - - proxy := node(cg) - gatewayNames := map[string]bool{"some-gateway": true} - hashByDestination := route.GetConsistentHashForVirtualService(cg.PushContext(), proxy, virtualServicePlain, serviceRegistry) - routes, err := route.BuildHTTPRoutesForVirtualService(proxy, virtualServicePlain, serviceRegistry, - hashByDestination, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - - hashPolicy := &envoyroute.RouteAction_HashPolicy{ - PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{ - Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{ - Name: "hash-cookie", - Ttl: nil, - }, - }, - } - g.Expect(routes[0].GetRoute().GetHashPolicy()).To(gomega.ConsistOf(hashPolicy)) - }) - - t.Run("for header operations for single cluster", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithHeaderOperationsForSingleCluster, - serviceRegistry, nil, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - - r := routes[0] - g.Expect(len(r.RequestHeadersToAdd)).To(gomega.Equal(4)) - g.Expect(len(r.ResponseHeadersToAdd)).To(gomega.Equal(4)) - g.Expect(len(r.RequestHeadersToRemove)).To(gomega.Equal(2)) - g.Expect(len(r.ResponseHeadersToRemove)).To(gomega.Equal(2)) - - g.Expect(r.RequestHeadersToAdd).To(gomega.Equal([]*core.HeaderValueOption{ - { - Header: &core.HeaderValue{ - Key: "x-req-set", - Value: "v1", - }, - Append: &wrappers.BoolValue{Value: false}, - }, - { - Header: &core.HeaderValue{ - Key: "x-req-add", - Value: "v2", - }, - Append: &wrappers.BoolValue{Value: true}, - }, - { - Header: &core.HeaderValue{ - Key: "x-route-req-set", - Value: "v1", - }, - Append: &wrappers.BoolValue{Value: false}, - }, - { - Header: &core.HeaderValue{ - Key: "x-route-req-add", - Value: "v2", - }, - Append: &wrappers.BoolValue{Value: true}, - }, - })) - g.Expect(r.RequestHeadersToRemove).To(gomega.Equal([]string{"x-req-remove", "x-route-req-remove"})) - - g.Expect(r.ResponseHeadersToAdd).To(gomega.Equal([]*core.HeaderValueOption{ - { - Header: &core.HeaderValue{ - Key: "x-resp-set", - Value: "v1", - }, - Append: &wrappers.BoolValue{Value: false}, - }, - { - Header: &core.HeaderValue{ - Key: "x-resp-add", - Value: "v2", - }, - Append: &wrappers.BoolValue{Value: true}, - }, - { - Header: &core.HeaderValue{ - Key: "x-route-resp-set", - Value: "v1", - }, - Append: &wrappers.BoolValue{Value: false}, - }, - { - Header: &core.HeaderValue{ - Key: "x-route-resp-add", - Value: "v2", - }, - Append: &wrappers.BoolValue{Value: true}, - }, - })) - g.Expect(r.ResponseHeadersToRemove).To(gomega.Equal([]string{"x-resp-remove", "x-route-resp-remove"})) - - routeAction, ok := r.GetAction().(*envoyroute.Route_Route) - g.Expect(ok).NotTo(gomega.BeFalse()) - g.Expect(routeAction.Route.GetHostRewriteLiteral()).To(gomega.Equal("foo.extsvc.com")) - }) - - t.Run("for header operations for weighted cluster", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithHeaderOperationsForWeightedCluster, - serviceRegistry, nil, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - - r := routes[0] - routeAction, ok := r.GetAction().(*envoyroute.Route_Route) - g.Expect(ok).NotTo(gomega.BeFalse()) - - weightedCluster := routeAction.Route.GetWeightedClusters() - g.Expect(weightedCluster).NotTo(gomega.BeNil()) - g.Expect(len(weightedCluster.GetClusters())).To(gomega.Equal(2)) - - expectResults := []struct { - reqAdd []*core.HeaderValueOption - reqRemove []string - respAdd []*core.HeaderValueOption - respRemove []string - authority string - }{ - { - reqAdd: []*core.HeaderValueOption{ - { - Header: &core.HeaderValue{ - Key: "x-route-req-set-blue", - Value: "v1", - }, - Append: &wrappers.BoolValue{Value: false}, - }, - { - Header: &core.HeaderValue{ - Key: "x-route-req-add-blue", - Value: "v2", - }, - Append: &wrappers.BoolValue{Value: true}, - }, - }, - reqRemove: []string{"x-route-req-remove-blue"}, - respAdd: []*core.HeaderValueOption{ - { - Header: &core.HeaderValue{ - Key: "x-route-resp-set-blue", - Value: "v1", - }, - Append: &wrappers.BoolValue{Value: false}, - }, - { - Header: &core.HeaderValue{ - Key: "x-route-resp-add-blue", - Value: "v2", - }, - Append: &wrappers.BoolValue{Value: true}, - }, - }, - respRemove: []string{"x-route-resp-remove-blue"}, - authority: "blue.foo.extsvc.com", - }, - { - reqAdd: []*core.HeaderValueOption{ - { - Header: &core.HeaderValue{ - Key: "x-route-req-set-green", - Value: "v1", - }, - Append: &wrappers.BoolValue{Value: false}, - }, - { - Header: &core.HeaderValue{ - Key: "x-route-req-add-green", - Value: "v2", - }, - Append: &wrappers.BoolValue{Value: true}, - }, - }, - reqRemove: []string{"x-route-req-remove-green"}, - respAdd: []*core.HeaderValueOption{ - { - Header: &core.HeaderValue{ - Key: "x-route-resp-set-green", - Value: "v1", - }, - Append: &wrappers.BoolValue{Value: false}, - }, - { - Header: &core.HeaderValue{ - Key: "x-route-resp-add-green", - Value: "v2", - }, - Append: &wrappers.BoolValue{Value: true}, - }, - }, - respRemove: []string{"x-route-resp-remove-green"}, - authority: "green.foo.extsvc.com", - }, - } - - for i, expectResult := range expectResults { - cluster := weightedCluster.GetClusters()[i] - g.Expect(cluster.RequestHeadersToAdd).To(gomega.Equal(expectResult.reqAdd)) - g.Expect(cluster.RequestHeadersToRemove).To(gomega.Equal(expectResult.reqRemove)) - g.Expect(cluster.ResponseHeadersToAdd).To(gomega.Equal(expectResult.respAdd)) - g.Expect(cluster.RequestHeadersToRemove).To(gomega.Equal(expectResult.reqRemove)) - g.Expect(cluster.GetHostRewriteLiteral()).To(gomega.Equal(expectResult.authority)) - } - }) - - t.Run("for redirect code", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithRedirect, serviceRegistry, nil, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - - redirectAction, ok := routes[0].Action.(*envoyroute.Route_Redirect) - g.Expect(ok).NotTo(gomega.BeFalse()) - g.Expect(redirectAction.Redirect.ResponseCode).To(gomega.Equal(envoyroute.RedirectAction_PERMANENT_REDIRECT)) - }) - - t.Run("for redirect and header manipulation", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{}) - - routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithRedirectAndSetHeader, serviceRegistry, nil, 8080, gatewayNames, false, nil) - xdstest.ValidateRoutes(t, routes) - g.Expect(err).NotTo(gomega.HaveOccurred()) - g.Expect(len(routes)).To(gomega.Equal(1)) - - redirectAction, ok := routes[0].Action.(*envoyroute.Route_Redirect) - g.Expect(ok).NotTo(gomega.BeFalse()) - g.Expect(redirectAction.Redirect.ResponseCode).To(gomega.Equal(envoyroute.RedirectAction_PERMANENT_REDIRECT)) - g.Expect(len(routes[0].ResponseHeadersToAdd)).To(gomega.Equal(1)) - g.Expect(routes[0].ResponseHeadersToAdd[0].Append.Value).To(gomega.BeFalse()) - g.Expect(routes[0].ResponseHeadersToAdd[0].Header.Key).To(gomega.Equal("Strict-Transport-Security")) - g.Expect(routes[0].ResponseHeadersToAdd[0].Header.Value).To(gomega.Equal("max-age=31536000; includeSubDomains; preload")) - }) - - t.Run("for no virtualservice but has destinationrule with consistentHash loadbalancer", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{ - Configs: []config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: "acme", - Namespace: "dubbo-system", - }, - Spec: networkingDestinationRule, - }, - }, - Services: exampleService, - }) - vhosts := route.BuildSidecarVirtualHostWrapper(nil, node(cg), cg.PushContext(), serviceRegistry, []config.Config{}, 8080) - g.Expect(vhosts[0].Routes[0].Action.(*envoyroute.Route_Route).Route.HashPolicy).NotTo(gomega.BeNil()) - }) - t.Run("for no virtualservice but has destinationrule with portLevel consistentHash loadbalancer", func(t *testing.T) { - g := gomega.NewWithT(t) - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{ - Configs: []config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: "acme", - Namespace: "dubbo-system", - }, - Spec: networkingDestinationRuleWithPortLevelTrafficPolicy, - }, - }, - Services: exampleService, - }) - vhosts := route.BuildSidecarVirtualHostWrapper(nil, node(cg), cg.PushContext(), serviceRegistry, []config.Config{}, 8080) - - hashPolicy := &envoyroute.RouteAction_HashPolicy{ - PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{ - Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{ - Name: "hash-cookie-1", - }, - }, - } - g.Expect(vhosts[0].Routes[0].Action.(*envoyroute.Route_Route).Route.HashPolicy).To(gomega.ConsistOf(hashPolicy)) - }) -} - -func loadBalancerPolicy(name string) *networking.LoadBalancerSettings_ConsistentHash { - return &networking.LoadBalancerSettings_ConsistentHash{ - ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ - HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpCookie{ - HttpCookie: &networking.LoadBalancerSettings_ConsistentHashLB_HTTPCookie{ - Name: name, - }, - }, - }, - } -} - -var virtualServiceWithSubset = &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"some-gateway"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Subset: "some-subset", - Host: "*.example.org", - Port: &networking.PortSelector{ - Number: 65000, - }, - }, - Weight: 100, - }, - }, - }, - }, -} - -var virtualServiceWithSubsetWithPortLevelSettings = &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"some-gateway"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Subset: "port-level-settings-subset", - Host: "*.example.org", - Port: &networking.PortSelector{ - Number: 8484, - }, - }, - Weight: 100, - }, - }, - }, - }, -} - -var virtualServicePlain = config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"some-gateway"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "*.example.org", - Port: &networking.PortSelector{ - Number: 8484, - }, - }, - Weight: 100, - }, - }, - }, - }, - }, -} - -var virtualServiceWithTimeout = config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"some-gateway"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "*.example.org", - Port: &networking.PortSelector{ - Number: 8484, - }, - }, - Weight: 100, - }, - }, - Timeout: &durationpb.Duration{ - Seconds: 10, - }, - }, - }, - }, -} - -var virtualServiceWithTimeoutDisabled = config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"some-gateway"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "*.example.org", - Port: &networking.PortSelector{ - Number: 8484, - }, - }, - Weight: 100, - }, - }, - Timeout: &durationpb.Duration{ - Seconds: 0, - }, - }, - }, - }, -} - -var virtualServiceWithCatchAllRoute = config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"some-gateway"}, - Http: []*networking.HTTPRoute{ - { - Name: "route", - Match: []*networking.HTTPMatchRequest{ - { - Name: "non-catch-all", - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{ - Prefix: "/route/v1", - }, - }, - }, - { - Name: "catch-all", - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{ - Prefix: "/", - }, - }, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "*.example.org", - Port: &networking.PortSelector{ - Number: 8484, - }, - }, - Weight: 100, - }, - }, - }, - }, - }, -} - -var virtualServiceWithCatchAllPort = config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"some-gateway"}, - Http: []*networking.HTTPRoute{ - { - Name: "route 1", - Match: []*networking.HTTPMatchRequest{ - { - Name: "catch-all for 8080", - Port: 8080, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "example1.default.svc.cluster.local", - Port: &networking.PortSelector{ - Number: 8484, - }, - }, - }, - }, - }, - { - Name: "route 2", - Match: []*networking.HTTPMatchRequest{ - { - Name: "header match", - Headers: map[string]*networking.StringMatch{ - "cookie": { - MatchType: &networking.StringMatch_Exact{Exact: "canary"}, - }, - }, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "example2.default.svc.cluster.local", - Port: &networking.PortSelector{ - Number: 8484, - }, - }, - }, - }, - }, - { - Name: "route 3", - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "example1.default.svc.cluster.local", - Port: &networking.PortSelector{ - Number: 8484, - }, - }, - }, - }, - }, - }, - }, -} - -var virtualServiceWithCatchAllMultiPrefixRoute = config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"some-gateway"}, - Http: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Name: "catch-all", - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{ - Prefix: "/", - }, - }, - SourceLabels: map[string]string{ - "matchingNoSrc": "xxx", - }, - }, - { - Name: "specific match", - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{ - Prefix: "/a", - }, - }, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "*.example.org", - Port: &networking.PortSelector{ - Number: 8484, - }, - }, - Weight: 100, - }, - }, - }, - }, - }, -} - -var virtualServiceWithCatchAllRouteWeightedDestination = config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: &networking.VirtualService{ - Hosts: []string{"headers.test.istio.io"}, - Gateways: []string{"some-gateway"}, - Http: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Name: "headers-only", - Headers: map[string]*networking.StringMatch{ - "version": { - MatchType: &networking.StringMatch_Exact{ - Exact: "v2", - }, - }, - }, - SourceLabels: map[string]string{ - "version": "v1", - }, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "c-weighted.extsvc.com", - Subset: "v2", - }, - Weight: 100, - }, - }, - }, - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "c-weighted.extsvc.com", - Subset: "v1", - }, - Weight: 100, - }, - }, - }, - }, - }, -} - -var virtualServiceWithHeaderOperationsForSingleCluster = config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: &networking.VirtualService{ - Hosts: []string{"headers.test.istio.io"}, - Gateways: []string{"some-gateway"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "c-weighted.extsvc.com", - Subset: "v1", - }, - Headers: &networking.Headers{ - Request: &networking.Headers_HeaderOperations{ - Set: map[string]string{"x-route-req-set": "v1", ":authority": "internal.foo.extsvc.com"}, - Add: map[string]string{"x-route-req-add": "v2", ":authority": "internal.bar.extsvc.com"}, - Remove: []string{"x-route-req-remove"}, - }, - Response: &networking.Headers_HeaderOperations{ - Set: map[string]string{"x-route-resp-set": "v1"}, - Add: map[string]string{"x-route-resp-add": "v2"}, - Remove: []string{"x-route-resp-remove"}, - }, - }, - Weight: 100, - }, - }, - Headers: &networking.Headers{ - Request: &networking.Headers_HeaderOperations{ - Set: map[string]string{"x-req-set": "v1", ":authority": "foo.extsvc.com"}, - Add: map[string]string{"x-req-add": "v2", ":authority": "bar.extsvc.com"}, - Remove: []string{"x-req-remove"}, - }, - Response: &networking.Headers_HeaderOperations{ - Set: map[string]string{"x-resp-set": "v1"}, - Add: map[string]string{"x-resp-add": "v2"}, - Remove: []string{"x-resp-remove"}, - }, - }, - }, - }, - }, -} - -var virtualServiceWithHeaderOperationsForWeightedCluster = config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: &networking.VirtualService{ - Hosts: []string{"headers.test.istio.io"}, - Gateways: []string{"some-gateway"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "c-weighted.extsvc.com", - Subset: "blue", - }, - Headers: &networking.Headers{ - Request: &networking.Headers_HeaderOperations{ - Set: map[string]string{"x-route-req-set-blue": "v1", ":authority": "blue.foo.extsvc.com"}, - Add: map[string]string{"x-route-req-add-blue": "v2", ":authority": "blue.bar.extsvc.com"}, - Remove: []string{"x-route-req-remove-blue"}, - }, - Response: &networking.Headers_HeaderOperations{ - Set: map[string]string{"x-route-resp-set-blue": "v1"}, - Add: map[string]string{"x-route-resp-add-blue": "v2"}, - Remove: []string{"x-route-resp-remove-blue"}, - }, - }, - Weight: 90, - }, - { - Destination: &networking.Destination{ - Host: "c-weighted.extsvc.com", - Subset: "green", - }, - Headers: &networking.Headers{ - Request: &networking.Headers_HeaderOperations{ - Set: map[string]string{"x-route-req-set-green": "v1", ":authority": "green.foo.extsvc.com"}, - Add: map[string]string{"x-route-req-add-green": "v2", ":authority": "green.bar.extsvc.com"}, - Remove: []string{"x-route-req-remove-green"}, - }, - Response: &networking.Headers_HeaderOperations{ - Set: map[string]string{"x-route-resp-set-green": "v1"}, - Add: map[string]string{"x-route-resp-add-green": "v2"}, - Remove: []string{"x-route-resp-remove-green"}, - }, - }, - Weight: 10, - }, - }, - Headers: &networking.Headers{ - Request: &networking.Headers_HeaderOperations{ - Set: map[string]string{"x-req-set": "v1", ":authority": "foo.extsvc.com"}, - Add: map[string]string{"x-req-add": "v2", ":authority": "bar.extsvc.com"}, - Remove: []string{"x-req-remove"}, - }, - Response: &networking.Headers_HeaderOperations{ - Set: map[string]string{"x-resp-set": "v1"}, - Add: map[string]string{"x-resp-add": "v2"}, - Remove: []string{"x-resp-remove"}, - }, - }, - }, - }, - }, -} - -var virtualServiceWithRedirect = config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"some-gateway"}, - Http: []*networking.HTTPRoute{ - { - Redirect: &networking.HTTPRedirect{ - Uri: "example.org", - Authority: "some-authority.default.svc.cluster.local", - RedirectCode: 308, - }, - }, - }, - }, -} - -var virtualServiceWithRedirectAndSetHeader = config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"some-gateway"}, - Http: []*networking.HTTPRoute{ - { - Redirect: &networking.HTTPRedirect{ - Uri: "example.org", - Authority: "some-authority.default.svc.cluster.local", - RedirectCode: 308, - }, - Headers: &networking.Headers{ - Response: &networking.Headers_HeaderOperations{ - Set: map[string]string{ - "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload", - }, - }, - }, - }, - }, - }, -} - -var virtualServiceWithRegexMatchingOnURI = config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"some-gateway"}, - Http: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Name: "status", - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Regex{ - Regex: "\\/(.?)\\/status", - }, - }, - }, - }, - Redirect: &networking.HTTPRedirect{ - Uri: "example.org", - Authority: "some-authority.default.svc.cluster.local", - RedirectCode: 308, - }, - }, - }, - }, -} - -var virtualServiceWithExactMatchingOnHeaderForJWTClaims = config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"some-gateway"}, - Http: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Name: "auth", - Headers: map[string]*networking.StringMatch{ - "@request.auth.claims.Foo": { - MatchType: &networking.StringMatch_Exact{ - Exact: "Bar", - }, - }, - }, - WithoutHeaders: map[string]*networking.StringMatch{ - "@request.auth.claims.Bla": { - MatchType: &networking.StringMatch_Exact{ - Exact: "Bar", - }, - }, - }, - }, - }, - Redirect: &networking.HTTPRedirect{ - Uri: "example.org", - Authority: "some-authority.default.svc.cluster.local", - RedirectCode: 308, - }, - }, - }, - }, -} - -var virtualServiceWithRegexMatchingOnHeader = config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"some-gateway"}, - Http: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Name: "auth", - Headers: map[string]*networking.StringMatch{ - "Authentication": { - MatchType: &networking.StringMatch_Regex{ - Regex: "Bearer .+?\\..+?\\..+?", - }, - }, - }, - }, - }, - Redirect: &networking.HTTPRedirect{ - Uri: "example.org", - Authority: "some-authority.default.svc.cluster.local", - RedirectCode: 308, - }, - }, - }, - }, -} - -func createVirtualServiceWithRegexMatchingForAllCasesOnHeader() []*config.Config { - ret := []*config.Config{} - regex := "*" - ret = append(ret, &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"some-gateway"}, - Http: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Name: "presence", - Headers: map[string]*networking.StringMatch{ - "FOO-HEADER": { - MatchType: &networking.StringMatch_Regex{ - Regex: regex, - }, - }, - }, - }, - }, - Redirect: &networking.HTTPRedirect{ - Uri: "example.org", - Authority: "some-authority.default.svc.cluster.local", - RedirectCode: 308, - }, - }, - }, - }, - }) - - return ret -} - -var virtualServiceWithRegexMatchingOnWithoutHeader = config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"some-gateway"}, - Http: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Name: "without-test", - WithoutHeaders: map[string]*networking.StringMatch{ - "FOO-HEADER": { - MatchType: &networking.StringMatch_Regex{ - Regex: "BAR .+?\\..+?\\..+?", - }, - }, - }, - }, - }, - Redirect: &networking.HTTPRedirect{ - Uri: "example.org", - Authority: "some-authority.default.svc.cluster.local", - RedirectCode: 308, - }, - }, - }, - }, -} - -var virtualServiceWithPresentMatchingOnHeader = config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"some-gateway"}, - Http: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Name: "presence", - Headers: map[string]*networking.StringMatch{ - "FOO-HEADER": nil, - }, - }, - }, - Redirect: &networking.HTTPRedirect{ - Uri: "example.org", - Authority: "some-authority.default.svc.cluster.local", - RedirectCode: 308, - }, - }, - }, - }, -} - -var virtualServiceWithPresentMatchingOnWithoutHeader = config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"some-gateway"}, - Http: []*networking.HTTPRoute{ - { - Match: []*networking.HTTPMatchRequest{ - { - Name: "presence", - WithoutHeaders: map[string]*networking.StringMatch{ - "FOO-HEADER": nil, - }, - }, - }, - Redirect: &networking.HTTPRedirect{ - Uri: "example.org", - Authority: "some-authority.default.svc.cluster.local", - RedirectCode: 308, - }, - }, - }, - }, -} - -var virtualServiceMatchingOnSourceNamespace = config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: "acme", - }, - Spec: &networking.VirtualService{ - Hosts: []string{}, - Http: []*networking.HTTPRoute{ - { - Name: "foo", - Match: []*networking.HTTPMatchRequest{ - { - SourceNamespace: "foo", - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "foo.example.org", - Port: &networking.PortSelector{ - Number: 8484, - }, - }, - Weight: 100, - }, - }, - }, - { - Name: "bar", - Match: []*networking.HTTPMatchRequest{ - { - SourceNamespace: "bar", - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "bar.example.org", - Port: &networking.PortSelector{ - Number: 8484, - }, - }, - Weight: 100, - }, - }, - }, - }, - }, -} - -var portLevelDestinationRule = &networking.DestinationRule{ - Host: "*.example.org", - Subsets: []*networking.Subset{}, - TrafficPolicy: &networking.TrafficPolicy{ - PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ - { - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: loadBalancerPolicy("hash-cookie"), - }, - Port: &networking.PortSelector{ - Number: 8484, - }, - }, - }, - }, -} - -var portLevelDestinationRuleWithSubsetPolicy = &networking.DestinationRule{ - Host: "*.example.org", - Subsets: []*networking.Subset{networkingSubsetWithPortLevelSettings}, - TrafficPolicy: &networking.TrafficPolicy{ - PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ - { - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: loadBalancerPolicy("hash-cookie"), - }, - Port: &networking.PortSelector{ - Number: 8484, - }, - }, - }, - }, -} - -var networkingDestinationRule = &networking.DestinationRule{ - Host: "*.example.org", - Subsets: []*networking.Subset{}, - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: loadBalancerPolicy("hash-cookie"), - }, - }, -} - -var exampleService = []*model.Service{{Hostname: "*.example.org", Attributes: model.ServiceAttributes{Namespace: "dubbo-system"}}} - -var networkingDestinationRuleWithPortLevelTrafficPolicy = &networking.DestinationRule{ - Host: "*.example.org", - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: loadBalancerPolicy("hash-cookie"), - }, - PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ - { - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: loadBalancerPolicy("hash-cookie-1"), - }, - Port: &networking.PortSelector{ - Number: 8080, - }, - }, - }, - }, -} - -var networkingSubset = &networking.Subset{ - Name: "some-subset", - Labels: map[string]string{}, - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{ - ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ - HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpCookie{ - HttpCookie: &networking.LoadBalancerSettings_ConsistentHashLB_HTTPCookie{ - Name: "other-cookie", - }, - }, - }, - }, - }, - }, -} - -var networkingSubsetWithPortLevelSettings = &networking.Subset{ - Name: "port-level-settings-subset", - Labels: map[string]string{}, - TrafficPolicy: &networking.TrafficPolicy{ - PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ - { - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: loadBalancerPolicy("port-level-settings-cookie"), - }, - Port: &networking.PortSelector{ - Number: 8484, - }, - }, - }, - }, -} - -func TestCombineVHostRoutes(t *testing.T) { - regexEngine := &matcher.RegexMatcher_GoogleRe2{GoogleRe2: &matcher.RegexMatcher_GoogleRE2{ - MaxProgramSize: &wrappers.UInt32Value{ - Value: uint32(10), - }, - }} - first := []*envoyroute.Route{ - {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Path{Path: "/path1"}}}, - {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Prefix{Prefix: "/prefix1"}}}, - {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_SafeRegex{ - SafeRegex: &matcher.RegexMatcher{ - EngineType: regexEngine, - Regex: ".*?regex1", - }, - }}}, - {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Prefix{Prefix: "/"}}}, - } - second := []*envoyroute.Route{ - {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Path{Path: "/path12"}}}, - {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Prefix{Prefix: "/prefix12"}}}, - {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_SafeRegex{ - SafeRegex: &matcher.RegexMatcher{ - EngineType: regexEngine, - Regex: ".*?regex12", - }, - }}}, - {Match: &envoyroute.RouteMatch{ - PathSpecifier: &envoyroute.RouteMatch_SafeRegex{ - SafeRegex: &matcher.RegexMatcher{ - EngineType: regexEngine, - Regex: "*", - }, - }, - Headers: []*envoyroute.HeaderMatcher{ - { - Name: "foo", - HeaderMatchSpecifier: &envoyroute.HeaderMatcher_StringMatch{ - StringMatch: &matcher.StringMatcher{MatchPattern: &matcher.StringMatcher_Exact{Exact: "bar"}}, - }, - InvertMatch: false, - }, - }, - }}, - } - - want := []*envoyroute.Route{ - {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Path{Path: "/path1"}}}, - {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Prefix{Prefix: "/prefix1"}}}, - {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_SafeRegex{ - SafeRegex: &matcher.RegexMatcher{ - EngineType: regexEngine, - Regex: ".*?regex1", - }, - }}}, - {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Path{Path: "/path12"}}}, - {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Prefix{Prefix: "/prefix12"}}}, - {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_SafeRegex{ - SafeRegex: &matcher.RegexMatcher{ - EngineType: regexEngine, - Regex: ".*?regex12", - }, - }}}, - {Match: &envoyroute.RouteMatch{ - PathSpecifier: &envoyroute.RouteMatch_SafeRegex{ - SafeRegex: &matcher.RegexMatcher{ - EngineType: regexEngine, - Regex: "*", - }, - }, - Headers: []*envoyroute.HeaderMatcher{ - { - Name: "foo", - HeaderMatchSpecifier: &envoyroute.HeaderMatcher_StringMatch{ - StringMatch: &matcher.StringMatcher{MatchPattern: &matcher.StringMatcher_Exact{Exact: "bar"}}, - }, - InvertMatch: false, - }, - }, - }}, - {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Prefix{Prefix: "/"}}}, - } - - got := route.CombineVHostRoutes(first, second) - if !reflect.DeepEqual(want, got) { - t.Errorf("CombineVHostRoutes: \n") - t.Errorf("got: \n") - for _, g := range got { - t.Errorf("%v\n", g.Match.PathSpecifier) - } - t.Errorf("want: \n") - for _, g := range want { - t.Errorf("%v\n", g.Match.PathSpecifier) - } - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/serviceentry_simulation_test.go b/pilot/pkg/networking/core/v1alpha3/serviceentry_simulation_test.go deleted file mode 100644 index 948791e89..000000000 --- a/pilot/pkg/networking/core/v1alpha3/serviceentry_simulation_test.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package v1alpha3_test - -import ( - "fmt" - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/simulation" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" -) - -const se = ` -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: se1 -spec: - hosts: - - blah.somedomain - addresses: - - %s - ports: - - number: 9999 - name: TCP-9999 - protocol: TCP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: se2 -spec: - hosts: - - blah.somedomain - addresses: - - %s - ports: - - number: 9999 - name: TCP-9999 - protocol: TCP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: vs1 -spec: - hosts: - - blah.somedomain - tls: - - match: - - port: 9999 - sniHosts: - - blah.somedomain - route: - - destination: - host: blah.somedomain - port: - number: 9999` - -func TestServiceEntry(t *testing.T) { - cases := []simulationTest{ - { - name: "identical CIDR (ignoreing insignificant bits) is dropped", - config: fmt.Sprintf(se, "1234:1f1:123:123:f816:3eff:feb8:2287/32", "1234:1f1:123:123:f816:3eff:febf:57ce/32"), - kubeConfig: "", - calls: []simulation.Expect{{ - // Expect listener, but no routing - Name: "defined port", - Call: simulation.Call{ - Port: 9999, - HostHeader: "blah.somedomain", - Address: "1234:1f1:1:1:1:1:1:1", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - ListenerMatched: "0.0.0.0_9999", - ClusterMatched: "outbound|9999||blah.somedomain", - }, - }}, - }, - { - // TODO(https://github.com/istio/istio/issues/29197) we should probably drop these too - name: "overlapping CIDR causes multiple filter chain match", - config: fmt.Sprintf(se, "1234:1f1:123:123:f816:3eff:feb8:2287/16", "1234:1f1:123:123:f816:3eff:febf:57ce/32"), - kubeConfig: "", - calls: []simulation.Expect{{ - // Expect listener, but no routing - Name: "defined port", - Call: simulation.Call{ - Port: 9999, - HostHeader: "blah.somedomain", - Address: "1234:1f1:1:1:1:1:1:1", - Protocol: simulation.HTTP, - }, - Result: simulation.Result{ - Error: simulation.ErrMultipleFilterChain, - ListenerMatched: "0.0.0.0_9999", - }, - }}, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - proxy := &model.Proxy{Metadata: &model.NodeMetadata{Labels: map[string]string{"app": "foo"}}} - runSimulationTest(t, proxy, xds.FakeOptions{}, simulationTest{ - name: tt.name, - config: tt.config, - calls: tt.calls, - }) - }) - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/sidecar_simulation_test.go b/pilot/pkg/networking/core/v1alpha3/sidecar_simulation_test.go deleted file mode 100644 index 9511534be..000000000 --- a/pilot/pkg/networking/core/v1alpha3/sidecar_simulation_test.go +++ /dev/null @@ -1,2477 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3_test - -import ( - "encoding/json" - "fmt" - "reflect" - "sort" - "strings" - "testing" -) - -import ( - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/simulation" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/tmpl" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -func flattenInstances(il ...[]*model.ServiceInstance) []*model.ServiceInstance { - ret := []*model.ServiceInstance{} - for _, i := range il { - ret = append(ret, i...) - } - return ret -} - -func makeInstances(proxy *model.Proxy, svc *model.Service, servicePort int, targetPort int) []*model.ServiceInstance { - ret := []*model.ServiceInstance{} - for _, p := range svc.Ports { - if p.Port != servicePort { - continue - } - ret = append(ret, &model.ServiceInstance{ - Service: svc, - ServicePort: p, - Endpoint: &model.IstioEndpoint{ - Address: proxy.IPAddresses[0], - ServicePortName: p.Name, - EndpointPort: uint32(targetPort), - }, - }) - } - return ret -} - -func TestInboundClusters(t *testing.T) { - proxy := &model.Proxy{ - IPAddresses: []string{"1.2.3.4"}, - Metadata: &model.NodeMetadata{}, - } - service := &model.Service{ - Hostname: host.Name("backend.default.svc.cluster.local"), - DefaultAddress: "1.1.1.1", - Ports: model.PortList{&model.Port{ - Name: "default", - Port: 80, - Protocol: protocol.HTTP, - }, &model.Port{ - Name: "other", - Port: 81, - Protocol: protocol.HTTP, - }}, - Resolution: model.ClientSideLB, - } - serviceAlt := &model.Service{ - Hostname: host.Name("backend-alt.default.svc.cluster.local"), - DefaultAddress: "1.1.1.2", - Ports: model.PortList{&model.Port{ - Name: "default", - Port: 80, - Protocol: protocol.HTTP, - }, &model.Port{ - Name: "other", - Port: 81, - Protocol: protocol.HTTP, - }}, - Resolution: model.ClientSideLB, - } - - cases := []struct { - name string - configs []config.Config - services []*model.Service - instances []*model.ServiceInstance - // Assertions - clusters map[string][]string - telemetry map[string][]string - proxy *model.Proxy - disableInboundPassthrough bool - }{ - // Proxy 1.8.1+ tests - {name: "empty"}, - {name: "empty service", services: []*model.Service{service}}, - { - name: "single service, partial instance", - services: []*model.Service{service}, - instances: makeInstances(proxy, service, 80, 8080), - clusters: map[string][]string{ - "inbound|8080||": nil, - }, - telemetry: map[string][]string{ - "inbound|8080||": {string(service.Hostname)}, - }, - }, - { - name: "single service, multiple instance", - services: []*model.Service{service}, - instances: flattenInstances( - makeInstances(proxy, service, 80, 8080), - makeInstances(proxy, service, 81, 8081)), - clusters: map[string][]string{ - "inbound|8080||": nil, - "inbound|8081||": nil, - }, - telemetry: map[string][]string{ - "inbound|8080||": {string(service.Hostname)}, - "inbound|8081||": {string(service.Hostname)}, - }, - }, - { - name: "multiple services with same service port, different target", - services: []*model.Service{service, serviceAlt}, - instances: flattenInstances( - makeInstances(proxy, service, 80, 8080), - makeInstances(proxy, service, 81, 8081), - makeInstances(proxy, serviceAlt, 80, 8082), - makeInstances(proxy, serviceAlt, 81, 8083)), - clusters: map[string][]string{ - "inbound|8080||": nil, - "inbound|8081||": nil, - "inbound|8082||": nil, - "inbound|8083||": nil, - }, - telemetry: map[string][]string{ - "inbound|8080||": {string(service.Hostname)}, - "inbound|8081||": {string(service.Hostname)}, - "inbound|8082||": {string(serviceAlt.Hostname)}, - "inbound|8083||": {string(serviceAlt.Hostname)}, - }, - }, - { - name: "multiple services with same service port and target", - services: []*model.Service{service, serviceAlt}, - instances: flattenInstances( - makeInstances(proxy, service, 80, 8080), - makeInstances(proxy, service, 81, 8081), - makeInstances(proxy, serviceAlt, 80, 8080), - makeInstances(proxy, serviceAlt, 81, 8081)), - clusters: map[string][]string{ - "inbound|8080||": nil, - "inbound|8081||": nil, - }, - telemetry: map[string][]string{ - "inbound|8080||": {string(serviceAlt.Hostname), string(service.Hostname)}, - "inbound|8081||": {string(serviceAlt.Hostname), string(service.Hostname)}, - }, - }, - { - name: "ingress to same port", - configs: []config.Config{ - { - Meta: config.Meta{GroupVersionKind: gvk.Sidecar, Namespace: "default", Name: "sidecar"}, - Spec: &networking.Sidecar{Ingress: []*networking.IstioIngressListener{{ - Port: &networking.Port{ - Number: 80, - Protocol: "HTTP", - Name: "http", - }, - DefaultEndpoint: "127.0.0.1:80", - }}}, - }, - }, - clusters: map[string][]string{ - "inbound|80||": {"127.0.0.1:80"}, - }, - }, - { - name: "ingress to different port", - configs: []config.Config{ - { - Meta: config.Meta{GroupVersionKind: gvk.Sidecar, Namespace: "default", Name: "sidecar"}, - Spec: &networking.Sidecar{Ingress: []*networking.IstioIngressListener{{ - Port: &networking.Port{ - Number: 80, - Protocol: "HTTP", - Name: "http", - }, - DefaultEndpoint: "127.0.0.1:8080", - }}}, - }, - }, - clusters: map[string][]string{ - "inbound|80||": {"127.0.0.1:8080"}, - }, - }, - { - name: "ingress to instance IP", - configs: []config.Config{ - { - Meta: config.Meta{GroupVersionKind: gvk.Sidecar, Namespace: "default", Name: "sidecar"}, - Spec: &networking.Sidecar{Ingress: []*networking.IstioIngressListener{{ - Port: &networking.Port{ - Number: 80, - Protocol: "HTTP", - Name: "http", - }, - DefaultEndpoint: "0.0.0.0:8080", - }}}, - }, - }, - clusters: map[string][]string{ - "inbound|80||": {"1.2.3.4:8080"}, - }, - }, - { - name: "ingress without default endpoint", - configs: []config.Config{ - { - Meta: config.Meta{GroupVersionKind: gvk.Sidecar, Namespace: "default", Name: "sidecar"}, - Spec: &networking.Sidecar{Ingress: []*networking.IstioIngressListener{{ - Port: &networking.Port{ - Number: 80, - Protocol: "HTTP", - Name: "http", - }, - }}}, - }, - }, - clusters: map[string][]string{ - "inbound|80||": nil, - }, - }, - { - name: "ingress to socket", - configs: []config.Config{ - { - Meta: config.Meta{GroupVersionKind: gvk.Sidecar, Namespace: "default", Name: "sidecar"}, - Spec: &networking.Sidecar{Ingress: []*networking.IstioIngressListener{{ - Port: &networking.Port{ - Number: 80, - Protocol: "HTTP", - Name: "http", - }, - DefaultEndpoint: "unix:///socket", - }}}, - }, - }, - clusters: map[string][]string{ - "inbound|80||": {"/socket"}, - }, - }, - { - name: "multiple ingress", - configs: []config.Config{ - { - Meta: config.Meta{GroupVersionKind: gvk.Sidecar, Namespace: "default", Name: "sidecar"}, - Spec: &networking.Sidecar{Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{ - Number: 80, - Protocol: "HTTP", - Name: "http", - }, - DefaultEndpoint: "127.0.0.1:8080", - }, - { - Port: &networking.Port{ - Number: 81, - Protocol: "HTTP", - Name: "http", - }, - DefaultEndpoint: "127.0.0.1:8080", - }, - }}, - }, - }, - clusters: map[string][]string{ - "inbound|80||": {"127.0.0.1:8080"}, - "inbound|81||": {"127.0.0.1:8080"}, - }, - }, - - // Disable inbound passthrough - { - name: "single service, partial instance", - services: []*model.Service{service}, - instances: makeInstances(proxy, service, 80, 8080), - clusters: map[string][]string{ - "inbound|8080||": {"127.0.0.1:8080"}, - }, - telemetry: map[string][]string{ - "inbound|8080||": {string(service.Hostname)}, - }, - disableInboundPassthrough: true, - }, - { - name: "single service, multiple instance", - services: []*model.Service{service}, - instances: flattenInstances( - makeInstances(proxy, service, 80, 8080), - makeInstances(proxy, service, 81, 8081)), - clusters: map[string][]string{ - "inbound|8080||": {"127.0.0.1:8080"}, - "inbound|8081||": {"127.0.0.1:8081"}, - }, - telemetry: map[string][]string{ - "inbound|8080||": {string(service.Hostname)}, - "inbound|8081||": {string(service.Hostname)}, - }, - disableInboundPassthrough: true, - }, - { - name: "multiple services with same service port, different target", - services: []*model.Service{service, serviceAlt}, - instances: flattenInstances( - makeInstances(proxy, service, 80, 8080), - makeInstances(proxy, service, 81, 8081), - makeInstances(proxy, serviceAlt, 80, 8082), - makeInstances(proxy, serviceAlt, 81, 8083)), - clusters: map[string][]string{ - "inbound|8080||": {"127.0.0.1:8080"}, - "inbound|8081||": {"127.0.0.1:8081"}, - "inbound|8082||": {"127.0.0.1:8082"}, - "inbound|8083||": {"127.0.0.1:8083"}, - }, - telemetry: map[string][]string{ - "inbound|8080||": {string(service.Hostname)}, - "inbound|8081||": {string(service.Hostname)}, - "inbound|8082||": {string(serviceAlt.Hostname)}, - "inbound|8083||": {string(serviceAlt.Hostname)}, - }, - disableInboundPassthrough: true, - }, - { - name: "multiple services with same service port and target", - services: []*model.Service{service, serviceAlt}, - instances: flattenInstances( - makeInstances(proxy, service, 80, 8080), - makeInstances(proxy, service, 81, 8081), - makeInstances(proxy, serviceAlt, 80, 8080), - makeInstances(proxy, serviceAlt, 81, 8081)), - clusters: map[string][]string{ - "inbound|8080||": {"127.0.0.1:8080"}, - "inbound|8081||": {"127.0.0.1:8081"}, - }, - telemetry: map[string][]string{ - "inbound|8080||": {string(serviceAlt.Hostname), string(service.Hostname)}, - "inbound|8081||": {string(serviceAlt.Hostname), string(service.Hostname)}, - }, - disableInboundPassthrough: true, - }, - } - for _, tt := range cases { - name := tt.name - if tt.proxy == nil { - tt.proxy = proxy - } else { - name += "-" + tt.proxy.Metadata.IstioVersion - } - - if tt.disableInboundPassthrough { - name += "-disableinbound" - } - t.Run(name, func(t *testing.T) { - test.SetBoolForTest(t, &features.EnableInboundPassthrough, !tt.disableInboundPassthrough) - s := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{ - Services: tt.services, - Instances: tt.instances, - Configs: tt.configs, - }) - sim := simulation.NewSimulationFromConfigGen(t, s, s.SetupProxy(tt.proxy)) - - clusters := xdstest.FilterClusters(sim.Clusters, func(c *cluster.Cluster) bool { - return strings.HasPrefix(c.Name, "inbound") - }) - if len(s.PushContext().ProxyStatus) != 0 { - // TODO make this fatal, once inbound conflict is silenced - t.Logf("got unexpected error: %+v", s.PushContext().ProxyStatus) - } - cmap := xdstest.ExtractClusters(clusters) - got := xdstest.MapKeys(cmap) - - // Check we have all expected clusters - if !reflect.DeepEqual(xdstest.MapKeys(tt.clusters), got) { - t.Errorf("expected clusters: %v, got: %v", xdstest.MapKeys(tt.clusters), got) - } - - for cname, c := range cmap { - // Check the upstream endpoints match - got := xdstest.ExtractLoadAssignments([]*endpoint.ClusterLoadAssignment{c.GetLoadAssignment()})[cname] - if !reflect.DeepEqual(tt.clusters[cname], got) { - t.Errorf("%v: expected endpoints %v, got %v", cname, tt.clusters[cname], got) - } - gotTelemetry := extractClusterMetadataServices(t, c) - if !reflect.DeepEqual(tt.telemetry[cname], gotTelemetry) { - t.Errorf("%v: expected telemetry services %v, got %v", cname, tt.telemetry[cname], gotTelemetry) - } - - // simulate an actual call, this ensures we are aligned with the inbound listener configuration - _, _, hostname, port := model.ParseSubsetKey(cname) - if tt.proxy.Metadata.IstioVersion != "" { - // This doesn't work with the legacy proxies which have issues (https://github.com/istio/istio/issues/29199) - for _, i := range tt.instances { - if len(hostname) > 0 && i.Service.Hostname != hostname { - continue - } - if i.ServicePort.Port == port { - port = int(i.Endpoint.EndpointPort) - } - } - } - sim.Run(simulation.Call{ - Port: port, - Protocol: simulation.HTTP, - Address: "1.2.3.4", - CallMode: simulation.CallModeInbound, - }).Matches(t, simulation.Result{ - ClusterMatched: cname, - }) - } - }) - } -} - -type clusterServicesMetadata struct { - Services []struct { - Host string - Name string - Namespace string - } -} - -func extractClusterMetadataServices(t test.Failer, c *cluster.Cluster) []string { - got := c.GetMetadata().GetFilterMetadata()[util.IstioMetadataKey] - if got == nil { - return nil - } - s, err := protomarshal.Marshal(got) - if err != nil { - t.Fatal(err) - } - meta := clusterServicesMetadata{} - if err := json.Unmarshal(s, &meta); err != nil { - t.Fatal(err) - } - res := []string{} - for _, m := range meta.Services { - res = append(res, m.Host) - } - return res -} - -func mtlsMode(m string) string { - return fmt.Sprintf(`apiVersion: security.istio.io/v1beta1 -kind: PeerAuthentication -metadata: - name: default - namespace: dubbo-system -spec: - mtls: - mode: %s -`, m) -} - -func TestInbound(t *testing.T) { - svc := ` -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: se -spec: - hosts: - - foo.bar - endpoints: - - address: 1.1.1.1 - location: MESH_INTERNAL - resolution: STATIC - ports: - - name: tcp - number: 70 - protocol: TCP - - name: http - number: 80 - protocol: HTTP - - name: auto - number: 81 ---- -` - cases := []struct { - Name string - Call simulation.Call - Disabled simulation.Result - Permissive simulation.Result - Strict simulation.Result - }{ - { - Name: "tcp", - Call: simulation.Call{ - Port: 70, - Protocol: simulation.TCP, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - ClusterMatched: "inbound|70||", - }, - Permissive: simulation.Result{ - ClusterMatched: "inbound|70||", - }, - Strict: simulation.Result{ - // Plaintext to strict, should fail - Error: simulation.ErrNoFilterChain, - }, - }, - { - Name: "http to tcp", - Call: simulation.Call{ - Port: 70, - Protocol: simulation.HTTP, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - ClusterMatched: "inbound|70||", - }, - Permissive: simulation.Result{ - ClusterMatched: "inbound|70||", - }, - Strict: simulation.Result{ - // Plaintext to strict, should fail - Error: simulation.ErrNoFilterChain, - }, - }, - { - Name: "tls to tcp", - Call: simulation.Call{ - Port: 70, - Protocol: simulation.TCP, - TLS: simulation.TLS, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - ClusterMatched: "inbound|70||", - }, - Permissive: simulation.Result{ - ClusterMatched: "inbound|70||", - }, - Strict: simulation.Result{ - // TLS, but not mTLS - Error: simulation.ErrMTLSError, - }, - }, - { - Name: "https to tcp", - Call: simulation.Call{ - Port: 70, - Protocol: simulation.HTTP, - TLS: simulation.TLS, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - ClusterMatched: "inbound|70||", - }, - Permissive: simulation.Result{ - ClusterMatched: "inbound|70||", - }, - Strict: simulation.Result{ - // TLS, but not mTLS - Error: simulation.ErrMTLSError, - }, - }, - { - Name: "mtls tcp to tcp", - Call: simulation.Call{ - Port: 70, - Protocol: simulation.TCP, - TLS: simulation.MTLS, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - // This is probably a user error, but there is no reason we should block mTLS traffic - // we just will not terminate it - ClusterMatched: "inbound|70||", - }, - Permissive: simulation.Result{ - ClusterMatched: "inbound|70||", - }, - Strict: simulation.Result{ - ClusterMatched: "inbound|70||", - }, - }, - { - Name: "mtls http to tcp", - Call: simulation.Call{ - Port: 70, - Protocol: simulation.HTTP, - TLS: simulation.MTLS, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - // This is probably a user error, but there is no reason we should block mTLS traffic - // we just will not terminate it - ClusterMatched: "inbound|70||", - }, - Permissive: simulation.Result{ - ClusterMatched: "inbound|70||", - }, - Strict: simulation.Result{ - ClusterMatched: "inbound|70||", - }, - }, - { - Name: "http", - Call: simulation.Call{ - Port: 80, - Protocol: simulation.HTTP, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - VirtualHostMatched: "inbound|http|80", - ClusterMatched: "inbound|80||", - }, - Permissive: simulation.Result{ - VirtualHostMatched: "inbound|http|80", - ClusterMatched: "inbound|80||", - }, - Strict: simulation.Result{ - // Plaintext to strict, should fail - Error: simulation.ErrNoFilterChain, - }, - }, - { - Name: "tls to http", - Call: simulation.Call{ - Port: 80, - Protocol: simulation.TCP, - TLS: simulation.TLS, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - // TLS is not terminated, so we will attempt to decode as HTTP and fail - Error: simulation.ErrProtocolError, - }, - Permissive: simulation.Result{ - // This could also be a protocol error. In the current implementation, we choose not - // to create a match since if we did it would just be rejected in HCM; no match - // is more performant - Error: simulation.ErrNoFilterChain, - }, - Strict: simulation.Result{ - // TLS, but not mTLS - Error: simulation.ErrMTLSError, - }, - }, - { - Name: "https to http", - Call: simulation.Call{ - Port: 80, - Protocol: simulation.HTTP, - TLS: simulation.TLS, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - // TLS is not terminated, so we will attempt to decode as HTTP and fail - Error: simulation.ErrProtocolError, - }, - Permissive: simulation.Result{ - // This could also be a protocol error. In the current implementation, we choose not - // to create a match since if we did it would just be rejected in HCM; no match - // is more performant - Error: simulation.ErrNoFilterChain, - }, - Strict: simulation.Result{ - // TLS, but not mTLS - Error: simulation.ErrMTLSError, - }, - }, - { - Name: "mtls to http", - Call: simulation.Call{ - Port: 80, - Protocol: simulation.HTTP, - TLS: simulation.MTLS, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - // TLS is not terminated, so we will attempt to decode as HTTP and fail - Error: simulation.ErrProtocolError, - }, - Permissive: simulation.Result{ - VirtualHostMatched: "inbound|http|80", - ClusterMatched: "inbound|80||", - }, - Strict: simulation.Result{ - VirtualHostMatched: "inbound|http|80", - ClusterMatched: "inbound|80||", - }, - }, - { - Name: "tcp to http", - Call: simulation.Call{ - Port: 80, - Protocol: simulation.TCP, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - // Expected, the port only supports HTTP - Error: simulation.ErrProtocolError, - }, - Permissive: simulation.Result{ - // Expected, the port only supports HTTP - Error: simulation.ErrProtocolError, - }, - Strict: simulation.Result{ - // Plaintext to strict fails - Error: simulation.ErrNoFilterChain, - }, - }, - { - Name: "auto port http", - Call: simulation.Call{ - Port: 81, - Protocol: simulation.HTTP, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - VirtualHostMatched: "inbound|http|81", - ClusterMatched: "inbound|81||", - }, - Permissive: simulation.Result{ - VirtualHostMatched: "inbound|http|81", - ClusterMatched: "inbound|81||", - }, - Strict: simulation.Result{ - // Plaintext to strict fails - Error: simulation.ErrNoFilterChain, - }, - }, - { - Name: "auto port http2", - Call: simulation.Call{ - Port: 81, - Protocol: simulation.HTTP2, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - VirtualHostMatched: "inbound|http|81", - ClusterMatched: "inbound|81||", - }, - Permissive: simulation.Result{ - VirtualHostMatched: "inbound|http|81", - ClusterMatched: "inbound|81||", - }, - Strict: simulation.Result{ - // Plaintext to strict fails - Error: simulation.ErrNoFilterChain, - }, - }, - { - Name: "auto port tcp", - Call: simulation.Call{ - Port: 81, - Protocol: simulation.TCP, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - ListenerMatched: "virtualInbound", - FilterChainMatched: "0.0.0.0_81", - ClusterMatched: "inbound|81||", - StrictMatch: true, - }, - Permissive: simulation.Result{ - ListenerMatched: "virtualInbound", - FilterChainMatched: "0.0.0.0_81", - ClusterMatched: "inbound|81||", - StrictMatch: true, - }, - Strict: simulation.Result{ - // Plaintext to strict fails - Error: simulation.ErrNoFilterChain, - }, - }, - { - Name: "tls to auto port", - Call: simulation.Call{ - Port: 81, - Protocol: simulation.TCP, - TLS: simulation.TLS, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - // Should go through the TCP chains - ListenerMatched: "virtualInbound", - FilterChainMatched: "0.0.0.0_81", - ClusterMatched: "inbound|81||", - StrictMatch: true, - }, - Permissive: simulation.Result{ - // Should go through the TCP chains - ListenerMatched: "virtualInbound", - FilterChainMatched: "0.0.0.0_81", - ClusterMatched: "inbound|81||", - StrictMatch: true, - }, - Strict: simulation.Result{ - // Tls, but not mTLS - Error: simulation.ErrMTLSError, - }, - }, - { - Name: "https to auto port", - Call: simulation.Call{ - Port: 81, - Protocol: simulation.HTTP, - TLS: simulation.TLS, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - // Should go through the TCP chains - ListenerMatched: "virtualInbound", - FilterChainMatched: "0.0.0.0_81", - ClusterMatched: "inbound|81||", - StrictMatch: true, - }, - Permissive: simulation.Result{ - // Should go through the TCP chains - ListenerMatched: "virtualInbound", - FilterChainMatched: "0.0.0.0_81", - ClusterMatched: "inbound|81||", - StrictMatch: true, - }, - Strict: simulation.Result{ - // Tls, but not mTLS - Error: simulation.ErrMTLSError, - }, - }, - { - Name: "mtls tcp to auto port", - Call: simulation.Call{ - Port: 81, - Protocol: simulation.TCP, - TLS: simulation.MTLS, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - // This is probably a user error, but there is no reason we should block mTLS traffic - // we just will not terminate it - ClusterMatched: "inbound|81||", - }, - Permissive: simulation.Result{ - // Should go through the TCP chains - ListenerMatched: "virtualInbound", - FilterChainMatched: "0.0.0.0_81", - ClusterMatched: "inbound|81||", - StrictMatch: true, - }, - Strict: simulation.Result{ - // Should go through the TCP chains - ListenerMatched: "virtualInbound", - FilterChainMatched: "0.0.0.0_81", - ClusterMatched: "inbound|81||", - StrictMatch: true, - }, - }, - { - Name: "mtls http to auto port", - Call: simulation.Call{ - Port: 81, - Protocol: simulation.HTTP, - TLS: simulation.MTLS, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - // This is probably a user error, but there is no reason we should block mTLS traffic - // we just will not terminate it - ClusterMatched: "inbound|81||", - }, - Permissive: simulation.Result{ - // Should go through the HTTP chains - VirtualHostMatched: "inbound|http|81", - ClusterMatched: "inbound|81||", - }, - Strict: simulation.Result{ - // Should go through the HTTP chains - VirtualHostMatched: "inbound|http|81", - ClusterMatched: "inbound|81||", - }, - }, - { - Name: "passthrough http", - Call: simulation.Call{ - Address: "1.2.3.4", - Port: 82, - Protocol: simulation.HTTP, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - ClusterMatched: "InboundPassthroughClusterIpv4", - FilterChainMatched: "virtualInbound-catchall-http", - }, - Permissive: simulation.Result{ - ClusterMatched: "InboundPassthroughClusterIpv4", - FilterChainMatched: "virtualInbound-catchall-http", - }, - Strict: simulation.Result{ - // Plaintext to strict fails - Error: simulation.ErrNoFilterChain, - }, - }, - { - Name: "passthrough tcp", - Call: simulation.Call{ - Address: "1.2.3.4", - Port: 82, - Protocol: simulation.TCP, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - ClusterMatched: "InboundPassthroughClusterIpv4", - FilterChainMatched: "virtualInbound", - }, - Permissive: simulation.Result{ - ClusterMatched: "InboundPassthroughClusterIpv4", - FilterChainMatched: "virtualInbound", - }, - Strict: simulation.Result{ - // Plaintext to strict fails - Error: simulation.ErrNoFilterChain, - }, - }, - { - Name: "passthrough tls", - Call: simulation.Call{ - Address: "1.2.3.4", - Port: 82, - Protocol: simulation.TCP, - TLS: simulation.TLS, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - ClusterMatched: "InboundPassthroughClusterIpv4", - FilterChainMatched: "virtualInbound", - }, - Permissive: simulation.Result{ - ClusterMatched: "InboundPassthroughClusterIpv4", - }, - Strict: simulation.Result{ - // tls, but not mTLS - Error: simulation.ErrMTLSError, - }, - }, - { - Name: "passthrough https", - Call: simulation.Call{ - Address: "1.2.3.4", - Port: 82, - Protocol: simulation.HTTP, - TLS: simulation.TLS, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - ClusterMatched: "InboundPassthroughClusterIpv4", - }, - Permissive: simulation.Result{ - ClusterMatched: "InboundPassthroughClusterIpv4", - }, - Strict: simulation.Result{ - // tls, but not mTLS - Error: simulation.ErrMTLSError, - }, - }, - { - Name: "passthrough mtls", - Call: simulation.Call{ - Address: "1.2.3.4", - Port: 82, - Protocol: simulation.HTTP, - TLS: simulation.MTLS, - CallMode: simulation.CallModeInbound, - }, - Disabled: simulation.Result{ - ClusterMatched: "InboundPassthroughClusterIpv4", - }, - Permissive: simulation.Result{ - ClusterMatched: "InboundPassthroughClusterIpv4", - }, - Strict: simulation.Result{ - ClusterMatched: "InboundPassthroughClusterIpv4", - }, - }, - } - t.Run("Disable", func(t *testing.T) { - calls := []simulation.Expect{} - for _, c := range cases { - calls = append(calls, simulation.Expect{ - Name: c.Name, - Call: c.Call, - Result: c.Disabled, - }) - } - runSimulationTest(t, nil, xds.FakeOptions{}, simulationTest{ - config: svc + mtlsMode("DISABLE"), - calls: calls, - }) - }) - - t.Run("Permissive", func(t *testing.T) { - calls := []simulation.Expect{} - for _, c := range cases { - calls = append(calls, simulation.Expect{ - Name: c.Name, - Call: c.Call, - Result: c.Permissive, - }) - } - runSimulationTest(t, nil, xds.FakeOptions{}, simulationTest{ - config: svc + mtlsMode("PERMISSIVE"), - calls: calls, - }) - }) - - t.Run("Strict", func(t *testing.T) { - calls := []simulation.Expect{} - for _, c := range cases { - calls = append(calls, simulation.Expect{ - Name: c.Name, - Call: c.Call, - Result: c.Strict, - }) - } - runSimulationTest(t, nil, xds.FakeOptions{}, simulationTest{ - config: svc + mtlsMode("STRICT"), - calls: calls, - }) - }) -} - -func TestHeadlessServices(t *testing.T) { - ports := ` - - name: http - port: 80 - - name: auto - port: 81 - - name: tcp - port: 82 - - name: tls - port: 83 - - name: https - port: 84` - - calls := []simulation.Expect{} - for _, call := range []simulation.Call{ - {Address: "1.2.3.4", Port: 80, Protocol: simulation.HTTP, HostHeader: "headless.default.svc.cluster.local"}, - - // Auto port should support any protocol - {Address: "1.2.3.4", Port: 81, Protocol: simulation.HTTP, HostHeader: "headless.default.svc.cluster.local"}, - {Address: "1.2.3.4", Port: 81, Protocol: simulation.HTTP, TLS: simulation.TLS, HostHeader: "headless.default.svc.cluster.local"}, - {Address: "1.2.3.4", Port: 81, Protocol: simulation.TCP, HostHeader: "headless.default.svc.cluster.local"}, - - {Address: "1.2.3.4", Port: 82, Protocol: simulation.TCP, HostHeader: "headless.default.svc.cluster.local"}, - - // Use short host name - {Address: "1.2.3.4", Port: 83, Protocol: simulation.TCP, TLS: simulation.TLS, HostHeader: "headless.default"}, - {Address: "1.2.3.4", Port: 84, Protocol: simulation.HTTP, TLS: simulation.TLS, HostHeader: "headless.default"}, - } { - calls = append(calls, simulation.Expect{ - Name: fmt.Sprintf("%s-%d", call.Protocol, call.Port), - Call: call, - Result: simulation.Result{ - ClusterMatched: fmt.Sprintf("outbound|%d||headless.default.svc.cluster.local", call.Port), - }, - }) - } - runSimulationTest(t, nil, xds.FakeOptions{}, simulationTest{ - kubeConfig: `apiVersion: v1 -kind: Service -metadata: - name: headless - namespace: default -spec: - clusterIP: None - selector: - app: headless - ports:` + ports + ` ---- -apiVersion: v1 -kind: Endpoints -metadata: - name: headless - namespace: default -subsets: -- addresses: - - ip: 1.2.3.4 - ports: -` + ports, - calls: calls, - }, - ) -} - -func TestPassthroughTraffic(t *testing.T) { - calls := map[string]simulation.Call{} - for port := 80; port < 87; port++ { - for _, call := range []simulation.Call{ - {Port: port, Protocol: simulation.HTTP, TLS: simulation.Plaintext, HostHeader: "foo"}, - {Port: port, Protocol: simulation.HTTP, TLS: simulation.TLS, HostHeader: "foo"}, - {Port: port, Protocol: simulation.HTTP, TLS: simulation.TLS, HostHeader: "foo", Alpn: "http/1.1"}, - {Port: port, Protocol: simulation.TCP, TLS: simulation.Plaintext, HostHeader: "foo"}, - {Port: port, Protocol: simulation.HTTP2, TLS: simulation.TLS, HostHeader: "foo"}, - } { - suffix := "" - if call.Alpn != "" { - suffix = "-" + call.Alpn - } - calls[fmt.Sprintf("%v-%v-%v%v", call.Protocol, call.TLS, port, suffix)] = call - } - } - ports := ` - ports: - - name: http - number: 80 - protocol: HTTP - - name: auto - number: 81 - - name: tcp - number: 82 - protocol: TCP - - name: tls - number: 83 - protocol: TLS - - name: https - number: 84 - protocol: HTTPS - - name: grpc - number: 85 - protocol: GRPC - - name: h2 - number: 86 - protocol: HTTP2` - - isHTTPPort := func(p int) bool { - switch p { - case 80, 85, 86: - return true - default: - return false - } - } - isAutoPort := func(p int) bool { - switch p { - case 81: - return true - default: - return false - } - } - for _, tp := range []meshconfig.MeshConfig_OutboundTrafficPolicy_Mode{ - meshconfig.MeshConfig_OutboundTrafficPolicy_REGISTRY_ONLY, - meshconfig.MeshConfig_OutboundTrafficPolicy_ALLOW_ANY, - } { - t.Run(tp.String(), func(t *testing.T) { - o := xds.FakeOptions{ - MeshConfig: func() *meshconfig.MeshConfig { - m := mesh.DefaultMeshConfig() - m.OutboundTrafficPolicy.Mode = tp - return m - }(), - } - expectedCluster := map[meshconfig.MeshConfig_OutboundTrafficPolicy_Mode]string{ - meshconfig.MeshConfig_OutboundTrafficPolicy_REGISTRY_ONLY: util.BlackHoleCluster, - meshconfig.MeshConfig_OutboundTrafficPolicy_ALLOW_ANY: util.PassthroughCluster, - }[tp] - t.Run("with VIP", func(t *testing.T) { - testCalls := []simulation.Expect{} - for name, call := range calls { - e := simulation.Expect{ - Name: name, - Call: call, - Result: simulation.Result{ - ClusterMatched: expectedCluster, - }, - } - // For blackhole, we will 502 where possible instead of blackhole cluster - // This only works for HTTP on HTTP - if expectedCluster == util.BlackHoleCluster && call.IsHTTP() && isHTTPPort(call.Port) { - e.Result.ClusterMatched = "" - e.Result.VirtualHostMatched = util.BlackHole - } - testCalls = append(testCalls, e) - } - sort.Slice(testCalls, func(i, j int) bool { - return testCalls[i].Name < testCalls[j].Name - }) - runSimulationTest(t, nil, o, - simulationTest{ - config: ` -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: se -spec: - hosts: - - istio.io - addresses: [1.2.3.4] - location: MESH_EXTERNAL - resolution: DNS` + ports, - calls: testCalls, - }) - }) - t.Run("without VIP", func(t *testing.T) { - testCalls := []simulation.Expect{} - for name, call := range calls { - e := simulation.Expect{ - Name: name, - Call: call, - Result: simulation.Result{ - ClusterMatched: expectedCluster, - }, - } - // For blackhole, we will 502 where possible instead of blackhole cluster - // This only works for HTTP on HTTP - if expectedCluster == util.BlackHoleCluster && call.IsHTTP() && (isHTTPPort(call.Port) || isAutoPort(call.Port)) { - e.Result.ClusterMatched = "" - e.Result.VirtualHostMatched = util.BlackHole - } - // TCP without a VIP will capture everything. - // Auto without a VIP is similar, but HTTP happens to work because routing is done on header - if call.Port == 82 || (call.Port == 81 && !call.IsHTTP()) { - e.Result.Error = nil - e.Result.ClusterMatched = "" - } - testCalls = append(testCalls, e) - } - sort.Slice(testCalls, func(i, j int) bool { - return testCalls[i].Name < testCalls[j].Name - }) - runSimulationTest(t, nil, o, - simulationTest{ - config: ` -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: se -spec: - hosts: - - istio.io - location: MESH_EXTERNAL - resolution: DNS` + ports, - calls: testCalls, - }) - }) - }) - } -} - -func TestLoop(t *testing.T) { - runSimulationTest(t, nil, xds.FakeOptions{}, simulationTest{ - calls: []simulation.Expect{ - { - Name: "direct request to outbound port", - Call: simulation.Call{ - Port: 15001, - Protocol: simulation.TCP, - }, - Result: simulation.Result{ - // This request should be blocked - ClusterMatched: "BlackHoleCluster", - }, - }, - { - Name: "direct request to inbound port", - Call: simulation.Call{ - Port: 15006, - Protocol: simulation.TCP, - }, - Result: simulation.Result{ - // This request should be blocked - ClusterMatched: "BlackHoleCluster", - }, - }, - }, - }) -} - -func TestInboundSidecarTLSModes(t *testing.T) { - peerAuthConfig := func(m string) string { - return fmt.Sprintf(`apiVersion: security.istio.io/v1beta1 -kind: PeerAuthentication -metadata: - name: peer-auth - namespace: default -spec: - selector: - matchLabels: - app: foo - mtls: - mode: STRICT - portLevelMtls: - 9080: - mode: %s ---- -`, m) - } - sidecarSimple := func(protocol string) string { - return fmt.Sprintf(` -apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - labels: - app: foo - name: sidecar - namespace: default -spec: - ingress: - - defaultEndpoint: 0.0.0.0:9080 - port: - name: tls - number: 9080 - protocol: %s - tls: - mode: SIMPLE - privateKey: "httpbinkey.pem" - serverCertificate: "httpbin.pem" - workloadSelector: - labels: - app: foo ---- -`, protocol) - } - sidecarMutual := func(protocol string) string { - return fmt.Sprintf(` -apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - labels: - app: foo - name: sidecar - namespace: default -spec: - ingress: - - defaultEndpoint: 0.0.0.0:9080 - port: - name: tls - number: 9080 - protocol: %s - tls: - mode: MUTUAL - privateKey: "httpbinkey.pem" - serverCertificate: "httpbin.pem" - caCertificates: "rootCA.pem" - workloadSelector: - labels: - app: foo ---- -`, protocol) - } - expectedTLSContext := func(filterChain *listener.FilterChain) error { - tlsContext := &tls.DownstreamTlsContext{} - ts := filterChain.GetTransportSocket().GetTypedConfig() - if ts == nil { - return fmt.Errorf("expected transport socket for chain %v", filterChain.GetName()) - } - if err := ts.UnmarshalTo(tlsContext); err != nil { - return err - } - commonTLSContext := tlsContext.CommonTlsContext - if len(commonTLSContext.TlsCertificateSdsSecretConfigs) == 0 { - return fmt.Errorf("expected tls certificates") - } - if commonTLSContext.TlsCertificateSdsSecretConfigs[0].Name != "file-cert:httpbin.pem~httpbinkey.pem" { - return fmt.Errorf("expected certificate httpbin.pem, actual %s", commonTLSContext.TlsCertificates[0].CertificateChain.String()) - } - if tlsContext.RequireClientCertificate.Value { - return fmt.Errorf("expected RequireClientCertificate to be false") - } - return nil - } - - mkCall := func(port int, protocol simulation.Protocol, - tls simulation.TLSMode, validations []simulation.CustomFilterChainValidation, - mTLSSecretConfigName string, - ) simulation.Call { - return simulation.Call{ - Protocol: protocol, - Port: port, - CallMode: simulation.CallModeInbound, - TLS: tls, - CustomListenerValidations: validations, - MtlsSecretConfigName: mTLSSecretConfigName, - } - } - cases := []struct { - name string - config string - calls []simulation.Expect - }{ - { - name: "sidecar http over TLS simple mode with peer auth on port disabled", - config: peerAuthConfig("DISABLE") + sidecarSimple("HTTPS"), - calls: []simulation.Expect{ - { - Name: "http over tls", - Call: mkCall(9080, simulation.HTTP, simulation.TLS, []simulation.CustomFilterChainValidation{expectedTLSContext}, ""), - Result: simulation.Result{ - FilterChainMatched: "1.1.1.1_9080", - ClusterMatched: "inbound|9080||", - VirtualHostMatched: "inbound|http|9080", - RouteMatched: "default", - ListenerMatched: "virtualInbound", - }, - }, - { - Name: "plaintext", - Call: mkCall(9080, simulation.HTTP, simulation.Plaintext, nil, ""), - Result: simulation.Result{ - Error: simulation.ErrNoFilterChain, - }, - }, - { - Name: "http over mTLS", - Call: mkCall(9080, simulation.HTTP, simulation.MTLS, nil, "file-cert:httpbin.pem~httpbinkey.pem"), - Result: simulation.Result{ - Error: simulation.ErrMTLSError, - }, - }, - }, - }, - { - name: "sidecar TCP over TLS simple mode with peer auth on port disabled", - config: peerAuthConfig("DISABLE") + sidecarSimple("TLS"), - calls: []simulation.Expect{ - { - Name: "tcp over tls", - Call: mkCall(9080, simulation.TCP, simulation.TLS, []simulation.CustomFilterChainValidation{expectedTLSContext}, ""), - Result: simulation.Result{ - FilterChainMatched: "1.1.1.1_9080", - ClusterMatched: "inbound|9080||", - ListenerMatched: "virtualInbound", - }, - }, - { - Name: "plaintext", - Call: mkCall(9080, simulation.TCP, simulation.Plaintext, nil, ""), - Result: simulation.Result{ - Error: simulation.ErrNoFilterChain, - }, - }, - { - Name: "tcp over mTLS", - Call: mkCall(9080, simulation.TCP, simulation.MTLS, nil, "file-cert:httpbin.pem~httpbinkey.pem"), - Result: simulation.Result{ - Error: simulation.ErrMTLSError, - }, - }, - }, - }, - { - name: "sidecar http over mTLS mutual mode with peer auth on port disabled", - config: peerAuthConfig("DISABLE") + sidecarMutual("HTTPS"), - calls: []simulation.Expect{ - { - Name: "http over mtls", - Call: mkCall(9080, simulation.HTTP, simulation.MTLS, nil, "file-cert:httpbin.pem~httpbinkey.pem"), - Result: simulation.Result{ - FilterChainMatched: "1.1.1.1_9080", - ClusterMatched: "inbound|9080||", - ListenerMatched: "virtualInbound", - }, - }, - { - Name: "plaintext", - Call: mkCall(9080, simulation.HTTP, simulation.Plaintext, nil, ""), - Result: simulation.Result{ - Error: simulation.ErrNoFilterChain, - }, - }, - { - Name: "http over tls", - Call: mkCall(9080, simulation.HTTP, simulation.TLS, nil, "file-cert:httpbin.pem~httpbinkey.pem"), - Result: simulation.Result{ - Error: simulation.ErrMTLSError, - }, - }, - }, - }, - { - name: "sidecar tcp over mTLS mutual mode with peer auth on port disabled", - config: peerAuthConfig("DISABLE") + sidecarMutual("TLS"), - calls: []simulation.Expect{ - { - Name: "tcp over mtls", - Call: mkCall(9080, simulation.TCP, simulation.MTLS, nil, "file-cert:httpbin.pem~httpbinkey.pem"), - Result: simulation.Result{ - FilterChainMatched: "1.1.1.1_9080", - ClusterMatched: "inbound|9080||", - ListenerMatched: "virtualInbound", - }, - }, - { - Name: "plaintext", - Call: mkCall(9080, simulation.TCP, simulation.Plaintext, nil, ""), - Result: simulation.Result{ - Error: simulation.ErrNoFilterChain, - }, - }, - { - Name: "http over tls", - Call: mkCall(9080, simulation.TCP, simulation.TLS, nil, "file-cert:httpbin.pem~httpbinkey.pem"), - Result: simulation.Result{ - Error: simulation.ErrMTLSError, - }, - }, - }, - }, - { - name: "sidecar http over TLS SIMPLE mode with peer auth on port STRICT", - config: peerAuthConfig("STRICT") + sidecarMutual("TLS"), - calls: []simulation.Expect{ - { - Name: "http over tls", - Call: mkCall(9080, simulation.HTTP, simulation.TLS, nil, ""), - Result: simulation.Result{ - Error: simulation.ErrMTLSError, - }, - }, - { - Name: "plaintext", - Call: mkCall(9080, simulation.HTTP, simulation.Plaintext, nil, ""), - Result: simulation.Result{ - Error: simulation.ErrNoFilterChain, - }, - }, - { - Name: "http over mtls", - Call: mkCall(9080, simulation.HTTP, simulation.MTLS, nil, ""), - Result: simulation.Result{ - FilterChainMatched: "1.1.1.1_9080", - ClusterMatched: "inbound|9080||", - ListenerMatched: "virtualInbound", - }, - }, - }, - }, - } - proxy := &model.Proxy{Metadata: &model.NodeMetadata{Labels: map[string]string{"app": "foo"}}} - test.SetBoolForTest(t, &features.EnableTLSOnSidecarIngress, true) - for _, tt := range cases { - runSimulationTest(t, proxy, xds.FakeOptions{}, simulationTest{ - name: tt.name, - config: tt.config, - calls: tt.calls, - }) - } -} - -const ( - TimeOlder = "2019-01-01T00:00:00Z" - TimeBase = "2020-01-01T00:00:00Z" - TimeNewer = "2021-01-01T00:00:00Z" -) - -type Configer interface { - Config(variant string) string -} - -type vsArgs struct { - Namespace string - Match string - Matches []string - Dest string - Port int - PortMatch int - Time string -} - -func (args vsArgs) Config(variant string) string { - if args.Time == "" { - args.Time = TimeBase - } - - if args.PortMatch != 0 { - // TODO(v0.4.2) test port match - variant = "virtualservice" - } - if args.Matches == nil { - args.Matches = []string{args.Match} - } - switch variant { - case "httproute": - return tmpl.MustEvaluate(`apiVersion: gateway.networking.k8s.io/v1alpha2 -kind: HTTPRoute -metadata: - name: "{{.Namespace}}{{.Match | replace "*" "wild"}}{{.Dest}}" - namespace: {{.Namespace}} - creationTimestamp: "{{.Time}}" -spec: - parentRefs: - - kind: Mesh - name: istio -{{ with .PortMatch }} - port: {{.}} -{{ end }} - hostnames: -{{- range $val := .Matches }} - - "{{$val}}" -{{ end }} - rules: - - backendRefs: - - kind: Hostname - group: networking.istio.io - name: {{.Dest}} - port: {{.Port | default 80}} -`, args) - case "virtualservice": - return tmpl.MustEvaluate(`apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: "{{.Namespace}}{{.Match | replace "*" "wild"}}{{.Dest}}" - namespace: {{.Namespace}} - creationTimestamp: "{{.Time}}" -spec: - hosts: -{{- range $val := .Matches }} - - "{{$val}}" -{{ end }} - http: - - route: - - destination: - host: {{.Dest}} -{{ with .Port }} - port: - number: {{.}} -{{ end }} -{{ with .PortMatch }} - match: - - port: {{.}} -{{ end }} -`, args) - default: - panic(variant + " unknown") - } -} - -type scArgs struct { - Namespace string - Egress []string -} - -func (args scArgs) Config(variant string) string { - return tmpl.MustEvaluate(`apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - name: "{{.Namespace}}" - namespace: "{{.Namespace}}" -spec: - egress: - - hosts: -{{- range $val := .Egress }} - - "{{$val}}" -{{- end }} -`, args) -} - -func TestSidecarRoutes(t *testing.T) { - knownServices := ` -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: known-default.example.com - namespace: default -spec: - hosts: - - known-default.example.com - addresses: - - 2.0.0.0 - endpoints: - - address: 1.0.0.0 - resolution: STATIC - ports: - - name: http - number: 80 - protocol: HTTP - - name: http-other - number: 8080 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: alt-known-default.example.com - namespace: default -spec: - hosts: - - alt-known-default.example.com - addresses: - - 2.0.0.1 - endpoints: - - address: 1.0.0.1 - resolution: STATIC - ports: - - name: http - number: 80 - protocol: HTTP - - name: http-other - number: 8080 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: not-default.example.org - namespace: not-default -spec: - hosts: - - not-default.example.org - addresses: - - 2.0.0.2 - endpoints: - - address: 1.0.0.2 - resolution: STATIC - ports: - - name: http - number: 80 - protocol: HTTP - - name: http-other - number: 8080 - protocol: HTTP ---- -` - proxy := func(ns string) *model.Proxy { - return &model.Proxy{ConfigNamespace: ns} - } - cases := []struct { - name string - cfg []Configer - proxy *model.Proxy - routeName string - expected map[string][]string - expectedGateway map[string][]string - }{ - // Port 80 has special cases as there is defaulting logic around this port - { - name: "simple port 80", - cfg: []Configer{vsArgs{ - Namespace: "default", - Match: "known-default.example.com", - Dest: "alt-known-default.example.com", - }}, - proxy: proxy("default"), - routeName: "80", - expected: map[string][]string{ - "known-default.example.com": {"outbound|80||alt-known-default.example.com"}, - }, - }, - { - name: "simple port 8080", - cfg: []Configer{vsArgs{ - Namespace: "default", - Match: "known-default.example.com", - Dest: "alt-known-default.example.com", - }}, - proxy: proxy("default"), - routeName: "8080", - expected: map[string][]string{ - "known-default.example.com": {"outbound|8080||alt-known-default.example.com"}, - }, - expectedGateway: map[string][]string{ - "known-default.example.com": {"outbound|80||alt-known-default.example.com"}, - }, - }, - { - name: "unknown port 80", - cfg: []Configer{vsArgs{ - Namespace: "default", - Match: "foo.com", - Dest: "foo.com", - }}, - proxy: proxy("default"), - routeName: "80", - expected: map[string][]string{ - "foo.com": {"outbound|80||foo.com"}, - }, - }, - { - name: "unknown port 8080", - cfg: []Configer{vsArgs{ - Namespace: "default", - Match: "foo.com", - Dest: "foo.com", - }}, - proxy: proxy("default"), - routeName: "8080", - // For unknown services, we only will add a route to the port 80 - expected: map[string][]string{ - "default.com": nil, - }, - }, - { - name: "unknown port 8080 match 8080", - cfg: []Configer{vsArgs{ - Namespace: "default", - Match: "foo.com", - Dest: "foo.com", - PortMatch: 8080, - }}, - proxy: proxy("default"), - routeName: "8080", - // For unknown services, we only will add a route to the port 80 - expected: map[string][]string{ - "foo.com": nil, - }, - }, - { - name: "unknown port 8080 dest 8080 ", - cfg: []Configer{vsArgs{ - Namespace: "default", - Match: "foo.com", - Dest: "foo.com", - Port: 8080, - }}, - proxy: proxy("default"), - routeName: "8080", - // For unknown services, we only will add a route to the port 80 - expected: map[string][]string{ - "default.com": nil, - }, - }, - { - name: "producer rule port 80", - cfg: []Configer{vsArgs{ - Namespace: "default", - Match: "known-default.example.com", - Dest: "alt-known-default.example.com", - }}, - proxy: proxy("not-default"), - routeName: "80", - expected: map[string][]string{ - "known-default.example.com": {"outbound|80||alt-known-default.example.com"}, - }, - }, - { - name: "producer rule port 8080", - cfg: []Configer{vsArgs{ - Namespace: "default", - Match: "known-default.example.com", - Dest: "alt-known-default.example.com", - }}, - proxy: proxy("not-default"), - routeName: "8080", - expected: map[string][]string{ - "known-default.example.com": {"outbound|8080||alt-known-default.example.com"}, - }, - expectedGateway: map[string][]string{ // No implicit port matching for gateway - "known-default.example.com": {"outbound|80||alt-known-default.example.com"}, - }, - }, - { - name: "consumer rule port 80", - cfg: []Configer{vsArgs{ - Namespace: "not-default", - Match: "known-default.example.com", - Dest: "alt-known-default.example.com", - }}, - proxy: proxy("not-default"), - routeName: "80", - expected: map[string][]string{ - "known-default.example.com": {"outbound|80||alt-known-default.example.com"}, - }, - }, - { - name: "consumer rule port 8080", - cfg: []Configer{vsArgs{ - Namespace: "not-default", - Match: "known-default.example.com", - Dest: "alt-known-default.example.com", - }}, - proxy: proxy("not-default"), - routeName: "8080", - expected: map[string][]string{ - "known-default.example.com": {"outbound|8080||alt-known-default.example.com"}, - }, - expectedGateway: map[string][]string{ // No implicit port matching for gateway - "known-default.example.com": {"outbound|80||alt-known-default.example.com"}, - }, - }, - { - name: "arbitrary rule port 80", - cfg: []Configer{vsArgs{ - Namespace: "arbitrary", - Match: "known-default.example.com", - Dest: "alt-known-default.example.com", - }}, - proxy: proxy("not-default"), - routeName: "80", - expected: map[string][]string{ - "known-default.example.com": {"outbound|80||alt-known-default.example.com"}, - }, - }, - { - name: "arbitrary rule port 8080", - cfg: []Configer{vsArgs{ - Namespace: "arbitrary", - Match: "known-default.example.com", - Dest: "alt-known-default.example.com", - }}, - proxy: proxy("not-default"), - routeName: "8080", - expected: map[string][]string{ - "known-default.example.com": {"outbound|8080||alt-known-default.example.com"}, - }, - expectedGateway: map[string][]string{ // No implicit port matching for gateway - "known-default.example.com": {"outbound|80||alt-known-default.example.com"}, - }, - }, - { - name: "multiple rules 80", - cfg: []Configer{ - vsArgs{ - Namespace: "arbitrary", - Match: "known-default.example.com", - Dest: "arbitrary.example.com", - Time: TimeOlder, - }, - vsArgs{ - Namespace: "default", - Match: "known-default.example.com", - Dest: "default.example.com", - Time: TimeBase, - }, - vsArgs{ - Namespace: "not-default", - Match: "known-default.example.com", - Dest: "not-default.example.com", - Time: TimeNewer, - }, - }, - proxy: proxy("not-default"), - routeName: "80", - expected: map[string][]string{ - // Oldest wins - "known-default.example.com": {"outbound|80||arbitrary.example.com"}, - }, - expectedGateway: map[string][]string{ - // TODO: consumer namespace wins - "known-default.example.com": {"outbound|80||arbitrary.example.com"}, - }, - }, - { - name: "multiple rules 8080", - cfg: []Configer{ - vsArgs{ - Namespace: "arbitrary", - Match: "known-default.example.com", - Dest: "arbitrary.example.com", - Time: TimeOlder, - }, - vsArgs{ - Namespace: "default", - Match: "known-default.example.com", - Dest: "default.example.com", - Time: TimeBase, - }, - vsArgs{ - Namespace: "not-default", - Match: "known-default.example.com", - Dest: "not-default.example.com", - Time: TimeNewer, - }, - }, - proxy: proxy("not-default"), - routeName: "8080", - expected: map[string][]string{ - // Oldest wins - "known-default.example.com": {"outbound|8080||arbitrary.example.com"}, - }, - expectedGateway: map[string][]string{ - // TODO: Consumer gateway wins. No implicit destination port for Gateway - "known-default.example.com": {"outbound|80||arbitrary.example.com"}, - }, - }, - { - name: "wildcard random", - cfg: []Configer{vsArgs{ - Namespace: "default", - Match: "*.unknown.example.com", - Dest: "arbitrary.example.com", - }}, - proxy: proxy("default"), - routeName: "80", - expected: map[string][]string{ - // match no VS, get default config - "alt-known-default.example.com": {"outbound|80||alt-known-default.example.com"}, - "known-default.example.com": {"outbound|80||known-default.example.com"}, - // Wildcard doesn't match any known services, insert it as-is - "*.unknown.example.com": {"outbound|80||arbitrary.example.com"}, - }, - }, - { - name: "wildcard match with sidecar", - cfg: []Configer{ - vsArgs{ - Namespace: "default", - Match: "*.example.com", - Dest: "arbitrary.example.com", - }, - scArgs{ - Namespace: "default", - Egress: []string{"*/*.example.com"}, - }, - }, - proxy: proxy("default"), - routeName: "80", - expected: map[string][]string{ - "alt-known-default.example.com": {"outbound|80||arbitrary.example.com"}, - "known-default.example.com": {"outbound|80||arbitrary.example.com"}, - // Matched an exact service, so we have no route for the wildcard - "*.example.com": nil, - }, - expectedGateway: map[string][]string{ - // Exact service matches do not get the wildcard applied - "alt-known-default.example.com": {"outbound|80||alt-known-default.example.com"}, - "known-default.example.com": {"outbound|80||known-default.example.com"}, - // The wildcard - "*.example.com": {"outbound|80||arbitrary.example.com"}, - }, - }, - { - name: "wildcard first then explicit", - cfg: []Configer{ - vsArgs{ - Namespace: "default", - Match: "*.example.com", - Dest: "wild.example.com", - Time: TimeOlder, - }, - vsArgs{ - Namespace: "default", - Match: "known-default.example.com", - Dest: "explicit.example.com", - Time: TimeNewer, - }, - }, - proxy: proxy("default"), - routeName: "80", - expected: map[string][]string{ - "alt-known-default.example.com": {"outbound|80||wild.example.com"}, - "known-default.example.com": {"outbound|80||wild.example.com"}, // oldest wins - // Matched an exact service, so we have no route for the wildcard - "*.example.com": nil, - }, - expectedGateway: map[string][]string{ - // No overrides, use default - "alt-known-default.example.com": {"outbound|80||alt-known-default.example.com"}, - // Explicit has precedence - "known-default.example.com": {"outbound|80||explicit.example.com"}, - // Last is our wildcard - "*.example.com": {"outbound|80||wild.example.com"}, - }, - }, - { - name: "explicit first then wildcard", - cfg: []Configer{ - vsArgs{ - Namespace: "default", - Match: "*.example.com", - Dest: "wild.example.com", - Time: TimeNewer, - }, - vsArgs{ - Namespace: "default", - Match: "known-default.example.com", - Dest: "explicit.example.com", - Time: TimeOlder, - }, - }, - proxy: proxy("default"), - routeName: "80", - expected: map[string][]string{ - "alt-known-default.example.com": {"outbound|80||wild.example.com"}, - "known-default.example.com": {"outbound|80||explicit.example.com"}, // oldest wins - // Matched an exact service, so we have no route for the wildcard - "*.example.com": nil, - }, - expectedGateway: map[string][]string{ - // No overrides, use default - "alt-known-default.example.com": {"outbound|80||alt-known-default.example.com"}, - // Explicit has precedence - "known-default.example.com": {"outbound|80||explicit.example.com"}, - // Last is our wildcard - "*.example.com": {"outbound|80||wild.example.com"}, - }, - }, - { - name: "wildcard and explicit with sidecar", - cfg: []Configer{ - vsArgs{ - Namespace: "default", - Match: "*.example.com", - Dest: "wild.example.com", - Time: TimeOlder, - }, - vsArgs{ - Namespace: "default", - Match: "known-default.example.com", - Dest: "explicit.example.com", - Time: TimeNewer, - }, - scArgs{ - Namespace: "default", - Egress: []string{"default/known-default.example.com", "default/alt-known-default.example.com"}, - }, - }, - proxy: proxy("default"), - routeName: "80", - expected: map[string][]string{ - // Even though we did not import `*.example.com`, the VS attaches - "alt-known-default.example.com": {"outbound|80||wild.example.com"}, - "known-default.example.com": {"outbound|80||wild.example.com"}, - // Matched an exact service, so we have no route for the wildcard - "*.example.com": nil, - }, - expectedGateway: map[string][]string{ - // No rule imported - "alt-known-default.example.com": {"outbound|80||alt-known-default.example.com"}, - // Imported rule - "known-default.example.com": {"outbound|80||explicit.example.com"}, - // Not imported - "*.example.com": nil, - }, - }, - { - name: "explicit first then wildcard with sidecar cross namespace", - cfg: []Configer{ - vsArgs{ - Namespace: "not-default", - Match: "*.example.com", - Dest: "wild.example.com", - Time: TimeOlder, - }, - vsArgs{ - Namespace: "default", - Match: "known-default.example.com", - Dest: "explicit.example.com", - Time: TimeNewer, - }, - scArgs{ - Namespace: "default", - Egress: []string{"default/known-default.example.com", "default/alt-known-default.example.com"}, - }, - }, - proxy: proxy("default"), - routeName: "80", - expected: map[string][]string{ - // Similar to above, but now the older wildcard VS is in a complete different namespace which we don't import - "alt-known-default.example.com": {"outbound|80||alt-known-default.example.com"}, - "known-default.example.com": {"outbound|80||explicit.example.com"}, - // Matched an exact service, so we have no route for the wildcard - "*.example.com": nil, - }, - }, - { - name: "wildcard and explicit cross namespace", - cfg: []Configer{ - vsArgs{ - Namespace: "not-default", - Match: "*.com", - Dest: "wild.example.com", - Time: TimeOlder, - }, - vsArgs{ - Namespace: "default", - Match: "known-default.example.com", - Dest: "explicit.example.com", - Time: TimeNewer, - }, - }, - proxy: proxy("default"), - routeName: "80", - expected: map[string][]string{ - // Wildcard is older, so it wins, even though it is cross namespace - "alt-known-default.example.com": {"outbound|80||wild.example.com"}, - "known-default.example.com": {"outbound|80||wild.example.com"}, - // Matched an exact service, so we have no route for the wildcard - "*.com": nil, - }, - expectedGateway: map[string][]string{ - // Exact match wins - "alt-known-default.example.com": {"outbound|80||alt-known-default.example.com"}, - "known-default.example.com": {"outbound|80||explicit.example.com"}, - // Wildcard last - "*.com": {"outbound|80||wild.example.com"}, - }, - }, - { - name: "wildcard and explicit unknown", - cfg: []Configer{ - vsArgs{ - Namespace: "default", - Match: "*.tld", - Dest: "wild.example.com", - Time: TimeOlder, - }, - vsArgs{ - Namespace: "default", - Match: "example.tld", - Dest: "explicit.example.com", - Time: TimeNewer, - }, - }, - proxy: proxy("default"), - routeName: "80", - expected: map[string][]string{ - // wildcard does not match - "known-default.example.com": {"outbound|80||known-default.example.com"}, - // Even though its less exact, this wildcard wins - "*.tld": {"outbound|80||wild.example.com"}, - "*.example.tld": nil, - }, - }, - { - name: "explicit match with wildcard sidecar", - cfg: []Configer{ - vsArgs{ - Namespace: "default", - Match: "arbitrary.example.com", - Dest: "arbitrary.example.com", - }, - scArgs{ - Namespace: "default", - Egress: []string{"*/*.example.com"}, - }, - }, - proxy: proxy("default"), - routeName: "80", - expected: map[string][]string{ - "arbitrary.example.com": {"outbound|80||arbitrary.example.com"}, - }, - }, - { - name: "wildcard match with explicit sidecar", - cfg: []Configer{ - vsArgs{ - Namespace: "default", - Match: "*.example.com", - Dest: "arbitrary.example.com", - }, - scArgs{ - Namespace: "default", - Egress: []string{"*/known-default.example.com"}, - }, - }, - proxy: proxy("default"), - routeName: "80", - expected: map[string][]string{ - "known-default.example.com": {"outbound|80||arbitrary.example.com"}, - "*.example.com": nil, - }, - expectedGateway: map[string][]string{ - "known-default.example.com": {"outbound|80||known-default.example.com"}, - "*.example.com": nil, - }, - }, - { - name: "non-service wildcard match with explicit sidecar", - cfg: []Configer{ - vsArgs{ - Namespace: "default", - Match: "*.example.org", - Dest: "arbitrary.example.com", - }, - scArgs{ - Namespace: "default", - Egress: []string{"*/explicit.example.org", "*/alt-known-default.example.com"}, - }, - }, - proxy: proxy("default"), - routeName: "80", - expected: map[string][]string{ - "known-default.example.com": nil, // Not imported - "alt-known-default.example.com": {"outbound|80||alt-known-default.example.com"}, // No change - "*.example.org": {"outbound|80||arbitrary.example.com"}, - }, - expectedGateway: map[string][]string{ - "known-default.example.com": nil, // Not imported - "alt-known-default.example.com": {"outbound|80||alt-known-default.example.com"}, // No change - "*.example.org": nil, // Not imported - }, - }, - { - name: "sidecar filter", - cfg: []Configer{ - vsArgs{ - Namespace: "not-default", - Match: "*.example.com", - Dest: "arbitrary.example.com", - }, - vsArgs{ - Namespace: "default", - Match: "explicit.example.com", - Dest: "explicit.example.com", - }, - scArgs{ - Namespace: "not-default", - Egress: []string{"not-default/*.example.com", "not-default/not-default.example.org"}, - }, - }, - proxy: proxy("not-default"), - routeName: "80", - expected: map[string][]string{ - // even though there is an *.example.com, since we do not import it we should create a wildcard matcher - "*.example.com": {"outbound|80||arbitrary.example.com"}, - // We did not import this, shouldn't show up - "explicit.example.com": nil, - }, - }, - { - name: "same namespace conflict", - cfg: []Configer{ - vsArgs{ - Namespace: "default", - Match: "known-default.example.com", - Dest: "old.example.com", - Time: TimeOlder, - }, - vsArgs{ - Namespace: "default", - Match: "known-default.example.com", - Dest: "new.example.com", - Time: TimeNewer, - }, - }, - proxy: proxy("default"), - routeName: "80", - expected: map[string][]string{ - "known-default.example.com": {"outbound|80||old.example.com"}, // oldest wins - }, - }, - { - name: "cross namespace conflict", - cfg: []Configer{ - vsArgs{ - Namespace: "not-default", - Match: "known-default.example.com", - Dest: "producer.example.com", - Time: TimeOlder, - }, - vsArgs{ - Namespace: "default", - Match: "known-default.example.com", - Dest: "consumer.example.com", - Time: TimeNewer, - }, - }, - proxy: proxy("default"), - routeName: "80", - expected: map[string][]string{ - // oldest wins - "known-default.example.com": {"outbound|80||producer.example.com"}, - }, - expectedGateway: map[string][]string{ - // TODO: consumer namespace wins - "known-default.example.com": {"outbound|80||producer.example.com"}, - }, - }, - { - name: "import only a unknown service route", - cfg: []Configer{ - vsArgs{ - Namespace: "default", - Match: "a.example.org", - Dest: "example.com", - }, - scArgs{ - Namespace: "default", - Egress: []string{"*/a.example.com"}, - }, - }, - proxy: proxy("default"), - routeName: "80", - expected: nil, // We do not even get a route as there is no service on the port - }, - { - // https://github.com/istio/istio/issues/37087 - name: "multi-host import single", - cfg: []Configer{ - vsArgs{ - Namespace: "default", - Matches: []string{"a.example.org", "b.example.org"}, - Dest: "example.com", - }, - scArgs{ - Namespace: "default", - Egress: []string{"*/known-default.example.com", "*/a.example.org"}, - }, - }, - proxy: proxy("default"), - routeName: "80", - expected: map[string][]string{ - // imported - "a.example.org": {"outbound|80||example.com"}, - // Not imported but we include it anyway - "b.example.org": {"outbound|80||example.com"}, - }, - expectedGateway: map[string][]string{ - // imported - "a.example.org": {"outbound|80||example.com"}, - // Not imported but we include it anyway - "b.example.org": nil, - }, - }, - } - for _, variant := range []string{"httproute", "virtualservice"} { - t.Run(variant, func(t *testing.T) { - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - cfg := knownServices - for _, tc := range tt.cfg { - cfg = cfg + "\n---\n" + tc.Config(variant) - } - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ConfigString: cfg}) - sim := simulation.NewSimulation(t, s, s.SetupProxy(tt.proxy)) - xdstest.ValidateListeners(t, sim.Listeners) - xdstest.ValidateRouteConfigurations(t, sim.Routes) - r := xdstest.ExtractRouteConfigurations(sim.Routes) - vh := r[tt.routeName] - exp := tt.expected - if variant == "httproute" && tt.expectedGateway != nil { - exp = tt.expectedGateway - } - if vh == nil && exp != nil { - t.Fatalf("route %q not found, have %v", tt.routeName, xdstest.MapKeys(r)) - } - gotHosts := xdstest.ExtractVirtualHosts(vh) - - for wk, wv := range exp { - got := gotHosts[wk] - if !reflect.DeepEqual(wv, got) { - t.Errorf("%v: wanted %v, got %v (had %v)", wk, wv, got, xdstest.MapKeys(gotHosts)) - } - } - }) - } - }) - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/tls.go b/pilot/pkg/networking/core/v1alpha3/tls.go deleted file mode 100644 index a9f3a3427..000000000 --- a/pilot/pkg/networking/core/v1alpha3/tls.go +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "sort" - "strings" -) - -import ( - "istio.io/api/networking/v1alpha3" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/telemetry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" -) - -// Match by source labels, the listener port where traffic comes in, the gateway on which the rule is being -// bound, etc. All these can be checked statically, since we are generating the configuration for a proxy -// with predefined labels, on a specific port. -func matchTLS(match *v1alpha3.TLSMatchAttributes, proxyLabels labels.Instance, gateways map[string]bool, port int, proxyNamespace string) bool { - if match == nil { - return true - } - - gatewayMatch := len(match.Gateways) == 0 - for _, gateway := range match.Gateways { - gatewayMatch = gatewayMatch || gateways[gateway] - } - - labelMatch := labels.Instance(match.SourceLabels).SubsetOf(proxyLabels) - - portMatch := match.Port == 0 || match.Port == uint32(port) - - nsMatch := match.SourceNamespace == "" || match.SourceNamespace == proxyNamespace - - return gatewayMatch && labelMatch && portMatch && nsMatch -} - -// Match by source labels, the listener port where traffic comes in, the gateway on which the rule is being -// bound, etc. All these can be checked statically, since we are generating the configuration for a proxy -// with predefined labels, on a specific port. -func matchTCP(match *v1alpha3.L4MatchAttributes, proxyLabels labels.Instance, gateways map[string]bool, port int, proxyNamespace string) bool { - if match == nil { - return true - } - - gatewayMatch := len(match.Gateways) == 0 - for _, gateway := range match.Gateways { - gatewayMatch = gatewayMatch || gateways[gateway] - } - - labelMatch := labels.Instance(match.SourceLabels).SubsetOf(proxyLabels) - - portMatch := match.Port == 0 || match.Port == uint32(port) - - nsMatch := match.SourceNamespace == "" || match.SourceNamespace == proxyNamespace - - return gatewayMatch && labelMatch && portMatch && nsMatch -} - -// Select the config pertaining to the service being processed. -func getConfigsForHost(hostname host.Name, configs []config.Config) []config.Config { - svcConfigs := make([]config.Config, 0) - for index := range configs { - virtualService := configs[index].Spec.(*v1alpha3.VirtualService) - for _, vsHost := range virtualService.Hosts { - if host.Name(vsHost).Matches(hostname) { - svcConfigs = append(svcConfigs, configs[index]) - break - } - } - } - return svcConfigs -} - -// hashRuntimeTLSMatchPredicates hashes runtime predicates of a TLS match -func hashRuntimeTLSMatchPredicates(match *v1alpha3.TLSMatchAttributes) string { - return strings.Join(match.SniHosts, ",") + "|" + strings.Join(match.DestinationSubnets, ",") -} - -func buildSidecarOutboundTLSFilterChainOpts(node *model.Proxy, push *model.PushContext, destinationCIDR string, - service *model.Service, bind string, listenPort *model.Port, - gateways map[string]bool, configs []config.Config) []*filterChainOpts { - if !listenPort.Protocol.IsTLS() { - return nil - } - actualWildcard, _ := getActualWildcardAndLocalHost(node) - // TLS matches are composed of runtime and static predicates. - // Static predicates can be evaluated during the generation of the config. Examples: gateway, source labels, etc. - // Runtime predicates cannot be evaluated during config generation. Instead the proxy must be configured to - // evaluate them. Examples: SNI hosts, source/destination subnets, etc. - // - // A list of matches may contain duplicate runtime matches, but different static matches. For example: - // - // {sni_hosts: A, sourceLabels: X} => destination M - // {sni_hosts: A, sourceLabels: *} => destination N - // - // For a proxy with labels X, we can evaluate the static predicates to get: - // {sni_hosts: A} => destination M - // {sni_hosts: A} => destination N - // - // The matches have the same runtime predicates. Since the second match can never be reached, we only - // want to generate config for the first match. - // - // To achieve this in this function we keep track of which runtime matches we have already generated config for - // and only add config if the we have not already generated config for that set of runtime predicates. - matchHasBeenHandled := make(map[string]bool) // Runtime predicate set -> have we generated config for this set? - - // Is there a virtual service with a TLS block that matches us? - hasTLSMatch := false - - out := make([]*filterChainOpts, 0) - for _, cfg := range configs { - virtualService := cfg.Spec.(*v1alpha3.VirtualService) - for _, tls := range virtualService.Tls { - for _, match := range tls.Match { - if matchTLS(match, node.Metadata.Labels, gateways, listenPort.Port, node.Metadata.Namespace) { - // Use the service's CIDRs. - // But if a virtual service overrides it with its own destination subnet match - // give preference to the user provided one - // destinationCIDR will be empty for services with VIPs - var destinationCIDRs []string - if destinationCIDR != "" { - destinationCIDRs = []string{destinationCIDR} - } - // Only set CIDR match if the listener is bound to an IP. - // If its bound to a unix domain socket, then ignore the CIDR matches - // Unix domain socket bound ports have Port value set to 0 - if len(match.DestinationSubnets) > 0 && listenPort.Port > 0 { - destinationCIDRs = match.DestinationSubnets - } - matchHash := hashRuntimeTLSMatchPredicates(match) - if !matchHasBeenHandled[matchHash] { - out = append(out, &filterChainOpts{ - metadata: util.BuildConfigInfoMetadata(cfg.Meta), - sniHosts: match.SniHosts, - destinationCIDRs: destinationCIDRs, - networkFilters: buildOutboundNetworkFilters(node, tls.Route, push, listenPort, cfg.Meta), - }) - hasTLSMatch = true - } - matchHasBeenHandled[matchHash] = true - } - } - } - } - - // HTTPS or TLS ports without associated virtual service - if !hasTLSMatch { - var sniHosts []string - - // In case of a sidecar config with user defined port, if the user specified port is not the same as the - // service's port, then pick the service port if and only if the service has only one port. If service - // has multiple ports, then route to a cluster with the listener port (i.e. sidecar defined port) - the - // traffic will most likely blackhole. - port := listenPort.Port - if len(service.Ports) == 1 { - port = service.Ports[0].Port - } - - clusterName := model.BuildSubsetKey(model.TrafficDirectionOutbound, "", service.Hostname, port) - statPrefix := clusterName - // If stat name is configured, use it to build the stat prefix. - if len(push.Mesh.OutboundClusterStatName) != 0 { - statPrefix = telemetry.BuildStatPrefix(push.Mesh.OutboundClusterStatName, string(service.Hostname), "", &model.Port{Port: port}, &service.Attributes) - } - // Use the hostname as the SNI value if and only: - // 1) if the destination is a CIDR; - // 2) or if we have an empty destination VIP (i.e. which we should never get in case some platform adapter improper handlings); - // 3) or if the destination is a wildcard destination VIP with the listener bound to the wildcard as well. - // In the above cited cases, the listener will be bound to 0.0.0.0. So SNI match is the only way to distinguish different - // target services. If we have a VIP, then we know the destination. Or if we do not have an VIP, but have - // `PILOT_ENABLE_HEADLESS_SERVICE_POD_LISTENERS` enabled (by default) and applicable to all that's needed, pilot will generate - // an outbound listener for each pod in a headless service. There is thus no need to do a SNI match. It saves us from having to - // generate expensive permutations of the host name just like RDS does.. - // NOTE that we cannot have two services with the same VIP as our listener build logic will treat it as a collision and - // ignore one of the services. - svcListenAddress := service.GetAddressForProxy(node) - if strings.Contains(svcListenAddress, "/") { - // Address is a CIDR, already captured by destinationCIDR parameter. - svcListenAddress = "" - } - - if len(destinationCIDR) > 0 || len(svcListenAddress) == 0 || (svcListenAddress == actualWildcard && bind == actualWildcard) { - sniHosts = []string{string(service.Hostname)} - } - destinationRule := CastDestinationRule(node.SidecarScope.DestinationRule( - model.TrafficDirectionOutbound, node, service.Hostname)) - out = append(out, &filterChainOpts{ - sniHosts: sniHosts, - destinationCIDRs: []string{destinationCIDR}, - networkFilters: buildOutboundNetworkFiltersWithSingleDestination(push, node, statPrefix, clusterName, "", listenPort, destinationRule), - }) - } - - return out -} - -func buildSidecarOutboundTCPFilterChainOpts(node *model.Proxy, push *model.PushContext, destinationCIDR string, - service *model.Service, listenPort *model.Port, - gateways map[string]bool, configs []config.Config) []*filterChainOpts { - if listenPort.Protocol.IsTLS() { - return nil - } - - out := make([]*filterChainOpts, 0) - - // very basic TCP - // break as soon as we add one network filter with no destination addresses to match - // This is the terminating condition in the filter chain match list - defaultRouteAdded := false -TcpLoop: - for _, cfg := range configs { - virtualService := cfg.Spec.(*v1alpha3.VirtualService) - for _, tcp := range virtualService.Tcp { - var destinationCIDRs []string - if destinationCIDR != "" { - destinationCIDRs = []string{destinationCIDR} - } - if len(tcp.Match) == 0 { - // implicit match - out = append(out, &filterChainOpts{ - metadata: util.BuildConfigInfoMetadata(cfg.Meta), - destinationCIDRs: destinationCIDRs, - networkFilters: buildOutboundNetworkFilters(node, tcp.Route, push, listenPort, cfg.Meta), - }) - defaultRouteAdded = true - break TcpLoop - } - - // Use the service's virtual address first. - // But if a virtual service overrides it with its own destination subnet match - // give preference to the user provided one - virtualServiceDestinationSubnets := make([]string, 0) - - for _, match := range tcp.Match { - if matchTCP(match, node.Metadata.Labels, gateways, listenPort.Port, node.Metadata.Namespace) { - // Scan all the match blocks - // if we find any match block without a runtime destination subnet match - // i.e. match any destination address, then we treat it as the terminal match/catch all match - // and break out of the loop. We also treat it as a terminal match if the listener is bound - // to a unix domain socket. - // But if we find only runtime destination subnet matches in all match blocks, collect them - // (this is similar to virtual hosts in http) and create filter chain match accordingly. - if len(match.DestinationSubnets) == 0 || listenPort.Port == 0 { - out = append(out, &filterChainOpts{ - metadata: util.BuildConfigInfoMetadata(cfg.Meta), - destinationCIDRs: destinationCIDRs, - networkFilters: buildOutboundNetworkFilters(node, tcp.Route, push, listenPort, cfg.Meta), - }) - defaultRouteAdded = true - break TcpLoop - } - virtualServiceDestinationSubnets = append(virtualServiceDestinationSubnets, match.DestinationSubnets...) - } - } - - if len(virtualServiceDestinationSubnets) > 0 { - out = append(out, &filterChainOpts{ - destinationCIDRs: virtualServiceDestinationSubnets, - networkFilters: buildOutboundNetworkFilters(node, tcp.Route, push, listenPort, cfg.Meta), - }) - - // If at this point there is a filter chain generated with the same CIDR match as the - // one that may be generated for the service as the default route, do not generate it. - // Otherwise, Envoy will complain about having filter chains with identical matches - // and will reject the config. - sort.Strings(virtualServiceDestinationSubnets) - sort.Strings(destinationCIDRs) - if util.StringSliceEqual(virtualServiceDestinationSubnets, destinationCIDRs) { - log.Warnf("Existing filter chain with same matching CIDR: %v.", destinationCIDRs) - defaultRouteAdded = true - } - } - } - } - - if !defaultRouteAdded { - // In case of a sidecar config with user defined port, if the user specified port is not the same as the - // service's port, then pick the service port if and only if the service has only one port. If service - // has multiple ports, then route to a cluster with the listener port (i.e. sidecar defined port) - the - // traffic will most likely blackhole. - port := listenPort.Port - if len(service.Ports) == 1 { - port = service.Ports[0].Port - } - - clusterName := model.BuildSubsetKey(model.TrafficDirectionOutbound, "", service.Hostname, port) - statPrefix := clusterName - destinationRule := CastDestinationRule(node.SidecarScope.DestinationRule( - model.TrafficDirectionOutbound, node, service.Hostname)) - // If stat name is configured, use it to build the stat prefix. - if len(push.Mesh.OutboundClusterStatName) != 0 { - statPrefix = telemetry.BuildStatPrefix(push.Mesh.OutboundClusterStatName, string(service.Hostname), "", &model.Port{Port: port}, &service.Attributes) - } - out = append(out, &filterChainOpts{ - destinationCIDRs: []string{destinationCIDR}, - networkFilters: buildOutboundNetworkFiltersWithSingleDestination(push, node, statPrefix, clusterName, "", listenPort, destinationRule), - }) - } - - return out -} - -// This function can be called for namespaces with the auto generated sidecar, i.e. once per service and per port. -// OR, it could be called in the context of an egress listener with specific TCP port on a sidecar config. -// In the latter case, there is no service associated with this listen port. So we have to account for this -// missing service throughout this file -func buildSidecarOutboundTCPTLSFilterChainOpts(node *model.Proxy, push *model.PushContext, - configs []config.Config, destinationCIDR string, service *model.Service, bind string, listenPort *model.Port, - gateways map[string]bool) []*filterChainOpts { - out := make([]*filterChainOpts, 0) - var svcConfigs []config.Config - if service != nil { - svcConfigs = getConfigsForHost(service.Hostname, configs) - } else { - svcConfigs = configs - } - - out = append(out, buildSidecarOutboundTLSFilterChainOpts(node, push, destinationCIDR, service, - bind, listenPort, gateways, svcConfigs)...) - out = append(out, buildSidecarOutboundTCPFilterChainOpts(node, push, destinationCIDR, service, - listenPort, gateways, svcConfigs)...) - return out -} diff --git a/pilot/pkg/networking/core/v1alpha3/tls_test.go b/pilot/pkg/networking/core/v1alpha3/tls_test.go deleted file mode 100644 index 7037b23ee..000000000 --- a/pilot/pkg/networking/core/v1alpha3/tls_test.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "testing" -) - -import ( - "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" -) - -func TestMatchTLS(t *testing.T) { - type args struct { - match *v1alpha3.TLSMatchAttributes - proxyLabels labels.Instance - gateways map[string]bool - port int - namespace string - } - tests := []struct { - name string - args args - want bool - }{ - { - "source namespace match", - args{ - match: &v1alpha3.TLSMatchAttributes{ - SourceNamespace: "foo", - }, - namespace: "foo", - }, - true, - }, - { - "source namespace not match", - args{ - match: &v1alpha3.TLSMatchAttributes{ - SourceNamespace: "foo", - }, - namespace: "bar", - }, - false, - }, - { - "source namespace not match when empty", - args{ - match: &v1alpha3.TLSMatchAttributes{ - SourceNamespace: "foo", - }, - namespace: "", - }, - false, - }, - { - "source namespace any", - args{ - match: &v1alpha3.TLSMatchAttributes{}, - namespace: "bar", - }, - true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := matchTLS(tt.args.match, tt.args.proxyLabels, tt.args.gateways, tt.args.port, tt.args.namespace); got != tt.want { - t.Errorf("matchTLS() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestMatchTCP(t *testing.T) { - type args struct { - match *v1alpha3.L4MatchAttributes - proxyLabels labels.Instance - gateways map[string]bool - port int - namespace string - } - tests := []struct { - name string - args args - want bool - }{ - { - "source namespace match", - args{ - match: &v1alpha3.L4MatchAttributes{ - SourceNamespace: "foo", - }, - namespace: "foo", - }, - true, - }, - { - "source namespace not match", - args{ - match: &v1alpha3.L4MatchAttributes{ - SourceNamespace: "foo", - }, - namespace: "bar", - }, - false, - }, - { - "source namespace not match when empty", - args{ - match: &v1alpha3.L4MatchAttributes{ - SourceNamespace: "foo", - }, - namespace: "", - }, - false, - }, - { - "source namespace any", - args{ - match: &v1alpha3.L4MatchAttributes{}, - namespace: "bar", - }, - true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := matchTCP(tt.args.match, tt.args.proxyLabels, tt.args.gateways, tt.args.port, tt.args.namespace); got != tt.want { - t.Errorf("matchTLS() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/pilot/pkg/networking/core/v1alpha3/tracing.go b/pilot/pkg/networking/core/v1alpha3/tracing.go deleted file mode 100644 index 2c4289d3f..000000000 --- a/pilot/pkg/networking/core/v1alpha3/tracing.go +++ /dev/null @@ -1,593 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "fmt" - "sort" - "strconv" -) - -import ( - opb "github.com/census-instrumentation/opencensus-proto/gen-go/trace/v1" - envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - tracingcfg "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3" - hpb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - envoy_type_metadata_v3 "github.com/envoyproxy/go-control-plane/envoy/type/metadata/v3" - tracing "github.com/envoyproxy/go-control-plane/envoy/type/tracing/v3" - xdstype "github.com/envoyproxy/go-control-plane/envoy/type/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "google.golang.org/protobuf/types/known/anypb" - "google.golang.org/protobuf/types/known/wrapperspb" - meshconfig "istio.io/api/mesh/v1alpha1" - telemetrypb "istio.io/api/telemetry/v1alpha1" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/extensionproviders" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - authz_model "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/authz/model" - xdsfilters "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/filters" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/requestidextension" - "github.com/apache/dubbo-go-pixiu/pkg/bootstrap/platform" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" -) - -// this is used for testing. it should not be changed in regular code. -var clusterLookupFn = extensionproviders.LookupCluster - -func configureTracing( - push *model.PushContext, - proxy *model.Proxy, - hcm *hpb.HttpConnectionManager, - class networking.ListenerClass, -) (*xdsfilters.RouterFilterContext, *requestidextension.UUIDRequestIDExtensionContext) { - tracing := push.Telemetry.Tracing(proxy) - return configureTracingFromSpec(tracing, push, proxy, hcm, class) -} - -func configureTracingFromSpec( - tracing *model.TracingConfig, - push *model.PushContext, - proxy *model.Proxy, - hcm *hpb.HttpConnectionManager, - class networking.ListenerClass, -) (*xdsfilters.RouterFilterContext, *requestidextension.UUIDRequestIDExtensionContext) { - meshCfg := push.Mesh - proxyCfg := proxy.Metadata.ProxyConfigOrDefault(push.Mesh.DefaultConfig) - - if tracing == nil { - // No Telemetry config for tracing, fallback to legacy mesh config - if !meshCfg.EnableTracing { - log.Debug("No valid tracing configuration found") - return nil, nil - } - // use the prior configuration bits of sampling and custom tags - hcm.Tracing = &hpb.HttpConnectionManager_Tracing{} - configureSampling(hcm.Tracing, proxyConfigSamplingValue(proxyCfg)) - configureCustomTags(hcm.Tracing, map[string]*telemetrypb.Tracing_CustomTag{}, proxyCfg, proxy.Metadata) - if proxyCfg.GetTracing().GetMaxPathTagLength() != 0 { - hcm.Tracing.MaxPathTagLength = wrapperspb.UInt32(proxyCfg.GetTracing().MaxPathTagLength) - } - return nil, nil - } - - spec := tracing.ServerSpec - if class == networking.ListenerClassSidecarOutbound || class == networking.ListenerClassGateway { - spec = tracing.ClientSpec - } - - if spec.Disabled { - return nil, nil - } - - var routerFilterCtx *xdsfilters.RouterFilterContext - if spec.Provider != nil { - tcfg, rfCtx, err := configureFromProviderConfig(push, proxy.Metadata, spec.Provider) - if err != nil { - log.Warnf("Not able to configure requested tracing provider %q: %v", spec.Provider.Name, err) - return nil, nil - } - hcm.Tracing = tcfg - routerFilterCtx = rfCtx - } else { - // TODO: should this `return nil, nil` instead ? - log.Warnf("Not able to configure tracing provider. Provider lookup failed.") - hcm.Tracing = &hpb.HttpConnectionManager_Tracing{} - // TODO: transition to configuring providers from proxy config here? - // something like: configureFromProxyConfig(tracingCfg, opts.proxy.Metadata.ProxyConfig.Tracing) - } - - // gracefully fallback to MeshConfig configuration. It will act as an implicit - // parent configuration during transition period. - configureSampling(hcm.Tracing, spec.RandomSamplingPercentage) - configureCustomTags(hcm.Tracing, spec.CustomTags, proxyCfg, proxy.Metadata) - - // if there is configured max tag length somewhere, fallback to it. - if hcm.GetTracing().GetMaxPathTagLength() == nil && proxyCfg.GetTracing().GetMaxPathTagLength() != 0 { - hcm.Tracing.MaxPathTagLength = wrapperspb.UInt32(proxyCfg.GetTracing().MaxPathTagLength) - } - - reqIDExtension := &requestidextension.UUIDRequestIDExtensionContext{} - reqIDExtension.UseRequestIDForTraceSampling = spec.UseRequestIDForTraceSampling - return routerFilterCtx, reqIDExtension -} - -// TODO: follow-on work to enable bootstrapping of clusters for $(HOST_IP):PORT addresses. - -func configureFromProviderConfig(pushCtx *model.PushContext, meta *model.NodeMetadata, - providerCfg *meshconfig.MeshConfig_ExtensionProvider) (*hpb.HttpConnectionManager_Tracing, *xdsfilters.RouterFilterContext, error) { - tracing := &hpb.HttpConnectionManager_Tracing{} - var rfCtx *xdsfilters.RouterFilterContext - var err error - - switch provider := providerCfg.Provider.(type) { - case *meshconfig.MeshConfig_ExtensionProvider_Zipkin: - tracing, err = buildHCMTracing(pushCtx, providerCfg.Name, provider.Zipkin.Service, provider.Zipkin.Port, provider.Zipkin.MaxTagLength, zipkinConfigGen) - case *meshconfig.MeshConfig_ExtensionProvider_Datadog: - tracing, err = buildHCMTracing(pushCtx, providerCfg.Name, provider.Datadog.Service, provider.Datadog.Port, provider.Datadog.MaxTagLength, datadogConfigGen) - case *meshconfig.MeshConfig_ExtensionProvider_Lightstep: - tracing, err = buildHCMTracing(pushCtx, providerCfg.Name, provider.Lightstep.Service, provider.Lightstep.Port, provider.Lightstep.MaxTagLength, - func(hostname, clusterName string) (*anypb.Any, error) { - lc := &tracingcfg.LightstepConfig{ - CollectorCluster: clusterName, - AccessTokenFile: provider.Lightstep.AccessToken, - } - return anypb.New(lc) - }) - - case *meshconfig.MeshConfig_ExtensionProvider_Opencensus: - tracing, err = buildHCMTracingOpenCensus(providerCfg.Name, provider.Opencensus.MaxTagLength, func() (*anypb.Any, error) { - oc := &tracingcfg.OpenCensusConfig{ - OcagentAddress: fmt.Sprintf("%s:%d", provider.Opencensus.Service, provider.Opencensus.Port), - OcagentExporterEnabled: true, - IncomingTraceContext: convert(provider.Opencensus.Context), - OutgoingTraceContext: convert(provider.Opencensus.Context), - } - - return anypb.New(oc) - }) - - case *meshconfig.MeshConfig_ExtensionProvider_Skywalking: - tracing, err = buildHCMTracing(pushCtx, providerCfg.Name, provider.Skywalking.Service, - provider.Skywalking.Port, 0, func(hostname, clusterName string) (*anypb.Any, error) { - s := &tracingcfg.SkyWalkingConfig{ - GrpcService: &envoy_config_core_v3.GrpcService{ - TargetSpecifier: &envoy_config_core_v3.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &envoy_config_core_v3.GrpcService_EnvoyGrpc{ - ClusterName: clusterName, - Authority: hostname, - }, - }, - }, - } - - return anypb.New(s) - }) - - rfCtx = &xdsfilters.RouterFilterContext{ - StartChildSpan: true, - } - - case *meshconfig.MeshConfig_ExtensionProvider_Stackdriver: - tracing, err = buildHCMTracingOpenCensus(providerCfg.Name, provider.Stackdriver.MaxTagLength, func() (*anypb.Any, error) { - proj, ok := meta.PlatformMetadata[platform.GCPProject] - if !ok { - proj, ok = meta.PlatformMetadata[platform.GCPProjectNumber] - } - if !ok { - return nil, fmt.Errorf("could not configure Stackdriver tracer - unknown project id") - } - - sd := &tracingcfg.OpenCensusConfig{ - StackdriverExporterEnabled: true, - StackdriverProjectId: proj, - IncomingTraceContext: allContexts, - OutgoingTraceContext: allContexts, - StdoutExporterEnabled: provider.Stackdriver.Debug, - TraceConfig: &opb.TraceConfig{ - MaxNumberOfAnnotations: 200, - MaxNumberOfAttributes: 200, - MaxNumberOfMessageEvents: 200, - }, - } - - if meta.StsPort != "" { - stsPort, err := strconv.Atoi(meta.StsPort) - if err != nil || stsPort < 1 { - return nil, fmt.Errorf("could not configure Stackdriver tracer - bad sts port: %v", err) - } - sd.StackdriverGrpcService = &envoy_config_core_v3.GrpcService{ - InitialMetadata: []*envoy_config_core_v3.HeaderValue{ - { - Key: "x-goog-user-project", - Value: proj, - }, - }, - TargetSpecifier: &envoy_config_core_v3.GrpcService_GoogleGrpc_{ - GoogleGrpc: &envoy_config_core_v3.GrpcService_GoogleGrpc{ - TargetUri: "cloudtrace.googleapis.com", - StatPrefix: "oc_stackdriver_tracer", - ChannelCredentials: &envoy_config_core_v3.GrpcService_GoogleGrpc_ChannelCredentials{ - CredentialSpecifier: &envoy_config_core_v3.GrpcService_GoogleGrpc_ChannelCredentials_SslCredentials{ - SslCredentials: &envoy_config_core_v3.GrpcService_GoogleGrpc_SslCredentials{}, - }, - }, - CallCredentials: []*envoy_config_core_v3.GrpcService_GoogleGrpc_CallCredentials{ - { - CredentialSpecifier: &envoy_config_core_v3.GrpcService_GoogleGrpc_CallCredentials_StsService_{ - StsService: &envoy_config_core_v3.GrpcService_GoogleGrpc_CallCredentials_StsService{ - TokenExchangeServiceUri: fmt.Sprintf("http://localhost:%d/token", stsPort), - SubjectTokenPath: constants.TrustworthyJWTPath, - SubjectTokenType: "urn:ietf:params:oauth:token-type:jwt", - Scope: "https://www.googleapis.com/auth/cloud-platform", - }, - }, - }, - }, - }, - }, - } - } - - if provider.Stackdriver.MaxNumberOfAnnotations != nil { - sd.TraceConfig.MaxNumberOfAnnotations = provider.Stackdriver.MaxNumberOfAnnotations.Value - } - if provider.Stackdriver.MaxNumberOfAttributes != nil { - sd.TraceConfig.MaxNumberOfAttributes = provider.Stackdriver.MaxNumberOfAttributes.Value - } - if provider.Stackdriver.MaxNumberOfMessageEvents != nil { - sd.TraceConfig.MaxNumberOfMessageEvents = provider.Stackdriver.MaxNumberOfMessageEvents.Value - } - return anypb.New(sd) - }) - } - - return tracing, rfCtx, err -} - -type typedConfigGenFromClusterFn func(hostname, clusterName string) (*anypb.Any, error) - -func zipkinConfigGen(hostname, cluster string) (*anypb.Any, error) { - zc := &tracingcfg.ZipkinConfig{ - CollectorCluster: cluster, - CollectorEndpoint: "/api/v2/spans", // envoy deprecated v1 support - CollectorEndpointVersion: tracingcfg.ZipkinConfig_HTTP_JSON, // use v2 JSON for now - CollectorHostname: hostname, // http host header - TraceId_128Bit: true, - SharedSpanContext: wrapperspb.Bool(false), - } - return anypb.New(zc) -} - -func datadogConfigGen(hostname, cluster string) (*anypb.Any, error) { - dc := &tracingcfg.DatadogConfig{ - CollectorCluster: cluster, - } - return anypb.New(dc) -} - -type typedConfigGenFn func() (*anypb.Any, error) - -func buildHCMTracing(pushCtx *model.PushContext, provider, svc string, port, maxTagLen uint32, - anyFn typedConfigGenFromClusterFn) (*hpb.HttpConnectionManager_Tracing, error) { - config := &hpb.HttpConnectionManager_Tracing{} - - hostname, cluster, err := clusterLookupFn(pushCtx, svc, int(port)) - if err != nil { - return config, fmt.Errorf("could not find cluster for tracing provider %q: %v", provider, err) - } - - cfg, err := anyFn(hostname, cluster) - if err != nil { - return config, fmt.Errorf("could not configure tracing provider %q: %v", provider, err) - } - - config.Provider = &tracingcfg.Tracing_Http{ - Name: provider, - ConfigType: &tracingcfg.Tracing_Http_TypedConfig{TypedConfig: cfg}, - } - - if maxTagLen != 0 { - config.MaxPathTagLength = &wrapperspb.UInt32Value{Value: maxTagLen} - } - return config, nil -} - -func buildHCMTracingOpenCensus(provider string, maxTagLen uint32, anyFn typedConfigGenFn) (*hpb.HttpConnectionManager_Tracing, error) { - config := &hpb.HttpConnectionManager_Tracing{} - any, err := anyFn() - if err != nil { - return config, fmt.Errorf("could not configure tracing provider %q: %v", provider, err) - } - - config.Provider = &tracingcfg.Tracing_Http{ - Name: provider, - ConfigType: &tracingcfg.Tracing_Http_TypedConfig{TypedConfig: any}, - } - - if maxTagLen != 0 { - config.MaxPathTagLength = &wrapperspb.UInt32Value{Value: maxTagLen} - } - return config, nil -} - -var allContexts = []tracingcfg.OpenCensusConfig_TraceContext{ - tracingcfg.OpenCensusConfig_B3, - tracingcfg.OpenCensusConfig_CLOUD_TRACE_CONTEXT, - tracingcfg.OpenCensusConfig_GRPC_TRACE_BIN, - tracingcfg.OpenCensusConfig_TRACE_CONTEXT, -} - -func convert(ctxs []meshconfig.MeshConfig_ExtensionProvider_OpenCensusAgentTracingProvider_TraceContext) []tracingcfg.OpenCensusConfig_TraceContext { - if len(ctxs) == 0 { - return allContexts - } - converted := make([]tracingcfg.OpenCensusConfig_TraceContext, 0, len(ctxs)) - for _, c := range ctxs { - switch c { - case meshconfig.MeshConfig_ExtensionProvider_OpenCensusAgentTracingProvider_B3: - converted = append(converted, tracingcfg.OpenCensusConfig_B3) - case meshconfig.MeshConfig_ExtensionProvider_OpenCensusAgentTracingProvider_CLOUD_TRACE_CONTEXT: - converted = append(converted, tracingcfg.OpenCensusConfig_CLOUD_TRACE_CONTEXT) - case meshconfig.MeshConfig_ExtensionProvider_OpenCensusAgentTracingProvider_GRPC_BIN: - converted = append(converted, tracingcfg.OpenCensusConfig_GRPC_TRACE_BIN) - case meshconfig.MeshConfig_ExtensionProvider_OpenCensusAgentTracingProvider_W3C_TRACE_CONTEXT: - converted = append(converted, tracingcfg.OpenCensusConfig_TRACE_CONTEXT) - } - } - return converted -} - -func dryRunPolicyTraceTag(name, key string) *tracing.CustomTag { - // The tag will not be populated when not used as there is no default value set for the tag. - // See https://www.envoyproxy.io/docs/envoy/v1.17.1/configuration/http/http_filters/rbac_filter#dynamic-metadata. - return &tracing.CustomTag{ - Tag: name, - Type: &tracing.CustomTag_Metadata_{ - Metadata: &tracing.CustomTag_Metadata{ - Kind: &envoy_type_metadata_v3.MetadataKind{ - Kind: &envoy_type_metadata_v3.MetadataKind_Request_{ - Request: &envoy_type_metadata_v3.MetadataKind_Request{}, - }, - }, - MetadataKey: &envoy_type_metadata_v3.MetadataKey{ - Key: wellknown.HTTPRoleBasedAccessControl, - Path: []*envoy_type_metadata_v3.MetadataKey_PathSegment{ - { - Segment: &envoy_type_metadata_v3.MetadataKey_PathSegment_Key{ - Key: key, - }, - }, - }, - }, - }, - }, - } -} - -func buildOptionalPolicyTags() []*tracing.CustomTag { - return []*tracing.CustomTag{ - dryRunPolicyTraceTag("istio.authorization.dry_run.allow_policy.name", authz_model.RBACShadowRulesAllowStatPrefix+authz_model.RBACShadowEffectivePolicyID), - dryRunPolicyTraceTag("istio.authorization.dry_run.allow_policy.result", authz_model.RBACShadowRulesAllowStatPrefix+authz_model.RBACShadowEngineResult), - dryRunPolicyTraceTag("istio.authorization.dry_run.deny_policy.name", authz_model.RBACShadowRulesDenyStatPrefix+authz_model.RBACShadowEffectivePolicyID), - dryRunPolicyTraceTag("istio.authorization.dry_run.deny_policy.result", authz_model.RBACShadowRulesDenyStatPrefix+authz_model.RBACShadowEngineResult), - } -} - -func buildServiceTags(metadata *model.NodeMetadata) []*tracing.CustomTag { - var revision, service string - if metadata.Labels != nil { - revision = metadata.Labels["service.istio.io/canonical-revision"] - service = metadata.Labels["service.istio.io/canonical-name"] - } - if revision == "" { - revision = "latest" - } - // TODO: This should have been properly handled with the injector. - if service == "" { - service = "unknown" - } - meshID := metadata.MeshID - if meshID == "" { - meshID = "unknown" - } - namespace := metadata.Namespace - if namespace == "" { - namespace = "default" - } - return []*tracing.CustomTag{ - { - Tag: "istio.canonical_revision", - Type: &tracing.CustomTag_Literal_{ - Literal: &tracing.CustomTag_Literal{ - Value: revision, - }, - }, - }, - { - Tag: "istio.canonical_service", - Type: &tracing.CustomTag_Literal_{ - Literal: &tracing.CustomTag_Literal{ - Value: service, - }, - }, - }, - { - Tag: "istio.mesh_id", - Type: &tracing.CustomTag_Literal_{ - Literal: &tracing.CustomTag_Literal{ - Value: meshID, - }, - }, - }, - { - Tag: "istio.namespace", - Type: &tracing.CustomTag_Literal_{ - Literal: &tracing.CustomTag_Literal{ - Value: namespace, - }, - }, - }, - } -} - -func configureSampling(hcmTracing *hpb.HttpConnectionManager_Tracing, providerPercentage float64) { - hcmTracing.ClientSampling = &xdstype.Percent{ - Value: 100.0, - } - hcmTracing.OverallSampling = &xdstype.Percent{ - Value: 100.0, - } - hcmTracing.RandomSampling = &xdstype.Percent{ - Value: providerPercentage, - } -} - -func proxyConfigSamplingValue(config *meshconfig.ProxyConfig) float64 { - sampling := features.TraceSampling - - if config.Tracing != nil && config.Tracing.Sampling != 0.0 { - sampling = config.Tracing.Sampling - - if sampling > 100.0 { - sampling = 1.0 - } - } - return sampling -} - -func configureCustomTags(hcmTracing *hpb.HttpConnectionManager_Tracing, - providerTags map[string]*telemetrypb.Tracing_CustomTag, proxyCfg *meshconfig.ProxyConfig, metadata *model.NodeMetadata) { - var tags []*tracing.CustomTag - - // TODO(dougreid): remove support for this feature. We don't want this to be - // optional moving forward. And we can add it back in via the Telemetry API - // later, if needed. - // THESE TAGS SHOULD BE ALWAYS ON. - if features.EnableIstioTags { - tags = append(tags, buildOptionalPolicyTags()...) - tags = append(tags, buildServiceTags(metadata)...) - } - - if len(providerTags) == 0 { - tags = append(tags, buildCustomTagsFromProxyConfig(proxyCfg.GetTracing().GetCustomTags())...) - } else { - tags = append(tags, buildCustomTagsFromProvider(providerTags)...) - } - - // looping over customTags, a map, results in the returned value - // being non-deterministic when multiple tags were defined; sort by the tag name - // to rectify this - sort.Slice(tags, func(i, j int) bool { - return tags[i].Tag < tags[j].Tag - }) - - hcmTracing.CustomTags = tags -} - -func buildCustomTagsFromProvider(providerTags map[string]*telemetrypb.Tracing_CustomTag) []*tracing.CustomTag { - var tags []*tracing.CustomTag - for tagName, tagInfo := range providerTags { - if tagInfo == nil { - log.Warnf("while building custom tags from provider, encountered nil custom tag: %s, skipping", tagName) - continue - } - switch tag := tagInfo.Type.(type) { - case *telemetrypb.Tracing_CustomTag_Environment: - env := &tracing.CustomTag{ - Tag: tagName, - Type: &tracing.CustomTag_Environment_{ - Environment: &tracing.CustomTag_Environment{ - Name: tag.Environment.Name, - DefaultValue: tag.Environment.DefaultValue, - }, - }, - } - tags = append(tags, env) - case *telemetrypb.Tracing_CustomTag_Header: - header := &tracing.CustomTag{ - Tag: tagName, - Type: &tracing.CustomTag_RequestHeader{ - RequestHeader: &tracing.CustomTag_Header{ - Name: tag.Header.Name, - DefaultValue: tag.Header.DefaultValue, - }, - }, - } - tags = append(tags, header) - case *telemetrypb.Tracing_CustomTag_Literal: - env := &tracing.CustomTag{ - Tag: tagName, - Type: &tracing.CustomTag_Literal_{ - Literal: &tracing.CustomTag_Literal{ - Value: tag.Literal.Value, - }, - }, - } - tags = append(tags, env) - } - } - return tags -} - -func buildCustomTagsFromProxyConfig(customTags map[string]*meshconfig.Tracing_CustomTag) []*tracing.CustomTag { - var tags []*tracing.CustomTag - - for tagName, tagInfo := range customTags { - if tagInfo == nil { - log.Warnf("while building custom tags from proxyConfig, encountered nil custom tag: %s, skipping", tagName) - continue - } - switch tag := tagInfo.Type.(type) { - case *meshconfig.Tracing_CustomTag_Environment: - env := &tracing.CustomTag{ - Tag: tagName, - Type: &tracing.CustomTag_Environment_{ - Environment: &tracing.CustomTag_Environment{ - Name: tag.Environment.Name, - DefaultValue: tag.Environment.DefaultValue, - }, - }, - } - tags = append(tags, env) - case *meshconfig.Tracing_CustomTag_Header: - header := &tracing.CustomTag{ - Tag: tagName, - Type: &tracing.CustomTag_RequestHeader{ - RequestHeader: &tracing.CustomTag_Header{ - Name: tag.Header.Name, - DefaultValue: tag.Header.DefaultValue, - }, - }, - } - tags = append(tags, header) - case *meshconfig.Tracing_CustomTag_Literal: - env := &tracing.CustomTag{ - Tag: tagName, - Type: &tracing.CustomTag_Literal_{ - Literal: &tracing.CustomTag_Literal{ - Value: tag.Literal.Value, - }, - }, - } - tags = append(tags, env) - } - } - return tags -} diff --git a/pilot/pkg/networking/core/v1alpha3/tracing_test.go b/pilot/pkg/networking/core/v1alpha3/tracing_test.go deleted file mode 100644 index aedcfa39c..000000000 --- a/pilot/pkg/networking/core/v1alpha3/tracing_test.go +++ /dev/null @@ -1,546 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1alpha3 - -import ( - "testing" -) - -import ( - envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - tracingcfg "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3" - hpb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - tracing "github.com/envoyproxy/go-control-plane/envoy/type/tracing/v3" - xdstype "github.com/envoyproxy/go-control-plane/envoy/type/v3" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/testing/protocmp" - "google.golang.org/protobuf/types/known/anypb" - "google.golang.org/protobuf/types/known/wrapperspb" - meshconfig "istio.io/api/mesh/v1alpha1" - tpb "istio.io/api/telemetry/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/extensionproviders" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - xdsfilters "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/filters" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/requestidextension" -) - -func TestConfigureTracing(t *testing.T) { - clusterName := "testcluster" - authority := "testhost" - providerName := "foo" - - clusterLookupFn = func(push *model.PushContext, service string, port int) (hostname string, cluster string, err error) { - return authority, clusterName, nil - } - defer func() { - clusterLookupFn = extensionproviders.LookupCluster - }() - - defaultUUIDExtensionCtx := requestidextension.UUIDRequestIDExtensionContext{ - UseRequestIDForTraceSampling: true, - } - - testcases := []struct { - name string - opts buildListenerOpts - inSpec *model.TracingConfig - want *hpb.HttpConnectionManager_Tracing - wantRfCtx *xdsfilters.RouterFilterContext - wantReqIDExtCtx *requestidextension.UUIDRequestIDExtensionContext - }{ - { - name: "no telemetry api", - opts: fakeOptsNoTelemetryAPI(), - want: fakeTracingConfigNoProvider(55.55, 13, append(defaultTracingTags(), fakeEnvTag)), - wantRfCtx: nil, - wantReqIDExtCtx: nil, - }, - { - name: "no telemetry api and nil custom tag", - opts: fakeOptsNoTelemetryAPIWithNilCustomTag(), - want: fakeTracingConfigNoProvider(55.55, 13, defaultTracingTags()), - wantRfCtx: nil, - wantReqIDExtCtx: nil, - }, - { - name: "only telemetry api (no provider)", - inSpec: fakeTracingSpecNoProvider(99.999, false, true), - opts: fakeOptsOnlyZipkinTelemetryAPI(), - want: fakeTracingConfigNoProvider(99.999, 0, append(defaultTracingTags(), fakeEnvTag)), - wantRfCtx: nil, - wantReqIDExtCtx: &defaultUUIDExtensionCtx, - }, - { - name: "only telemetry api (no provider) with nil custom tag", - inSpec: fakeTracingSpecNoProviderWithNilCustomTag(99.999, false, true), - opts: fakeOptsOnlyZipkinTelemetryAPI(), - want: fakeTracingConfigNoProvider(99.999, 0, defaultTracingTags()), - wantRfCtx: nil, - wantReqIDExtCtx: &defaultUUIDExtensionCtx, - }, - { - name: "only telemetry api (with provider)", - inSpec: fakeTracingSpec(fakeZipkin(), 99.999, false, true), - opts: fakeOptsOnlyZipkinTelemetryAPI(), - want: fakeTracingConfig(fakeZipkinProvider(clusterName, authority, providerName), 99.999, 256, append(defaultTracingTags(), fakeEnvTag)), - wantRfCtx: nil, - wantReqIDExtCtx: &defaultUUIDExtensionCtx, - }, - { - name: "both tracing enabled (no provider)", - inSpec: fakeTracingSpecNoProvider(99.999, false, true), - opts: fakeOptsMeshAndTelemetryAPI(true /* enable tracing */), - want: fakeTracingConfigNoProvider(99.999, 13, append(defaultTracingTags(), fakeEnvTag)), - wantRfCtx: nil, - wantReqIDExtCtx: &defaultUUIDExtensionCtx, - }, - { - name: "both tracing disabled (no provider)", - inSpec: fakeTracingSpecNoProvider(99.999, false, true), - opts: fakeOptsMeshAndTelemetryAPI(false /* no enable tracing */), - want: fakeTracingConfigNoProvider(99.999, 13, append(defaultTracingTags(), fakeEnvTag)), - wantRfCtx: nil, - wantReqIDExtCtx: &defaultUUIDExtensionCtx, - }, - { - name: "both tracing enabled (with provider)", - inSpec: fakeTracingSpec(fakeZipkin(), 99.999, false, true), - opts: fakeOptsMeshAndTelemetryAPI(true /* enable tracing */), - want: fakeTracingConfig(fakeZipkinProvider(clusterName, authority, providerName), 99.999, 256, append(defaultTracingTags(), fakeEnvTag)), - wantRfCtx: nil, - wantReqIDExtCtx: &defaultUUIDExtensionCtx, - }, - { - name: "both tracing disabled (with provider)", - inSpec: fakeTracingSpec(fakeZipkin(), 99.999, false, true), - opts: fakeOptsMeshAndTelemetryAPI(false /* no enable tracing */), - want: fakeTracingConfig(fakeZipkinProvider(clusterName, authority, providerName), 99.999, 256, append(defaultTracingTags(), fakeEnvTag)), - wantRfCtx: nil, - wantReqIDExtCtx: &defaultUUIDExtensionCtx, - }, - { - name: "basic config (with skywalking provider)", - inSpec: fakeTracingSpec(fakeSkywalking(), 99.999, false, false), - opts: fakeOptsOnlySkywalkingTelemetryAPI(), - want: fakeTracingConfig(fakeSkywalkingProvider(clusterName, authority, providerName), 99.999, 0, append(defaultTracingTags(), fakeEnvTag)), - wantRfCtx: &xdsfilters.RouterFilterContext{StartChildSpan: true}, - wantReqIDExtCtx: &requestidextension.UUIDRequestIDExtensionContext{UseRequestIDForTraceSampling: false}, - }, - { - name: "client-only config for server", - inSpec: fakeClientOnlyTracingSpec(fakeSkywalking(), 99.999, false, false), - opts: fakeInboundOptsOnlySkywalkingTelemetryAPI(), - want: nil, - wantRfCtx: nil, - wantReqIDExtCtx: nil, - }, - { - name: "server-only config for server", - inSpec: fakeServerOnlyTracingSpec(fakeSkywalking(), 99.999, false, false), - opts: fakeInboundOptsOnlySkywalkingTelemetryAPI(), - want: fakeTracingConfig(fakeSkywalkingProvider(clusterName, authority, providerName), 99.999, 0, append(defaultTracingTags(), fakeEnvTag)), - wantRfCtx: &xdsfilters.RouterFilterContext{StartChildSpan: true}, - wantReqIDExtCtx: &requestidextension.UUIDRequestIDExtensionContext{UseRequestIDForTraceSampling: false}, - }, - } - - for _, tc := range testcases { - t.Run(tc.name, func(t *testing.T) { - hcm := &hpb.HttpConnectionManager{} - gotRfCtx, gotReqIDExtCtx := configureTracingFromSpec(tc.inSpec, tc.opts.push, tc.opts.proxy, hcm, 0) - if diff := cmp.Diff(tc.want, hcm.Tracing, protocmp.Transform()); diff != "" { - t.Fatalf("configureTracing returned unexpected diff (-want +got):\n%s", diff) - } - if diff := cmp.Diff(gotRfCtx, tc.wantRfCtx); diff != "" { - t.Fatalf("got filter modifier context is unexpected diff (-want +got):\n%s", diff) - } - if diff := cmp.Diff(tc.wantReqIDExtCtx, gotReqIDExtCtx); diff != "" { - t.Fatalf("configureTracingFromSpec returned unexpected diff for request ID extension (-want +got):\n%s", diff) - } - }) - } -} - -func defaultTracingTags() []*tracing.CustomTag { - return append(buildOptionalPolicyTags(), - &tracing.CustomTag{ - Tag: "istio.canonical_revision", - Type: &tracing.CustomTag_Literal_{ - Literal: &tracing.CustomTag_Literal{ - Value: "latest", - }, - }, - }, - &tracing.CustomTag{ - Tag: "istio.canonical_service", - Type: &tracing.CustomTag_Literal_{ - Literal: &tracing.CustomTag_Literal{ - Value: "unknown", - }, - }, - }, - &tracing.CustomTag{ - Tag: "istio.mesh_id", - Type: &tracing.CustomTag_Literal_{ - Literal: &tracing.CustomTag_Literal{ - Value: "unknown", - }, - }, - }, - &tracing.CustomTag{ - Tag: "istio.namespace", - Type: &tracing.CustomTag_Literal_{ - Literal: &tracing.CustomTag_Literal{ - Value: "default", - }, - }, - }) -} - -func fakeOptsNoTelemetryAPI() buildListenerOpts { - var opts buildListenerOpts - opts.push = &model.PushContext{ - Mesh: &meshconfig.MeshConfig{ - EnableTracing: true, - }, - } - opts.proxy = &model.Proxy{ - Metadata: &model.NodeMetadata{ - ProxyConfig: &model.NodeMetaProxyConfig{ - Tracing: &meshconfig.Tracing{ - Sampling: 55.55, - MaxPathTagLength: 13, - CustomTags: map[string]*meshconfig.Tracing_CustomTag{ - "test": { - Type: &meshconfig.Tracing_CustomTag_Environment{ - Environment: &meshconfig.Tracing_Environment{ - Name: "FOO", - }, - }, - }, - }, - }, - }, - }, - } - - return opts -} - -func fakeOptsNoTelemetryAPIWithNilCustomTag() buildListenerOpts { - var opts buildListenerOpts - opts.push = &model.PushContext{ - Mesh: &meshconfig.MeshConfig{ - EnableTracing: true, - }, - } - opts.proxy = &model.Proxy{ - Metadata: &model.NodeMetadata{ - ProxyConfig: &model.NodeMetaProxyConfig{ - Tracing: &meshconfig.Tracing{ - Sampling: 55.55, - MaxPathTagLength: 13, - CustomTags: map[string]*meshconfig.Tracing_CustomTag{ - "test": nil, - }, - }, - }, - }, - } - - return opts -} - -func fakeOptsOnlyZipkinTelemetryAPI() buildListenerOpts { - var opts buildListenerOpts - opts.push = &model.PushContext{ - Mesh: &meshconfig.MeshConfig{ - ExtensionProviders: []*meshconfig.MeshConfig_ExtensionProvider{ - { - Name: "foo", - Provider: &meshconfig.MeshConfig_ExtensionProvider_Zipkin{ - Zipkin: &meshconfig.MeshConfig_ExtensionProvider_ZipkinTracingProvider{ - Service: "zipkin", - Port: 9411, - MaxTagLength: 256, - }, - }, - }, - }, - }, - } - opts.proxy = &model.Proxy{ - Metadata: &model.NodeMetadata{ - ProxyConfig: &model.NodeMetaProxyConfig{}, - }, - } - - return opts -} - -func fakeZipkin() *meshconfig.MeshConfig_ExtensionProvider { - return &meshconfig.MeshConfig_ExtensionProvider{ - Name: "foo", - Provider: &meshconfig.MeshConfig_ExtensionProvider_Zipkin{ - Zipkin: &meshconfig.MeshConfig_ExtensionProvider_ZipkinTracingProvider{ - Service: "zipkin", - Port: 9411, - MaxTagLength: 256, - }, - }, - } -} - -func fakeOptsMeshAndTelemetryAPI(enableTracing bool) buildListenerOpts { - var opts buildListenerOpts - opts.push = &model.PushContext{ - Mesh: &meshconfig.MeshConfig{ - EnableTracing: enableTracing, - ExtensionProviders: []*meshconfig.MeshConfig_ExtensionProvider{ - { - Name: "foo", - Provider: &meshconfig.MeshConfig_ExtensionProvider_Zipkin{ - Zipkin: &meshconfig.MeshConfig_ExtensionProvider_ZipkinTracingProvider{ - Service: "zipkin", - Port: 9411, - MaxTagLength: 256, - }, - }, - }, - }, - }, - } - opts.proxy = &model.Proxy{ - Metadata: &model.NodeMetadata{ - ProxyConfig: &model.NodeMetaProxyConfig{ - Tracing: &meshconfig.Tracing{ - Sampling: 55.55, - MaxPathTagLength: 13, - CustomTags: map[string]*meshconfig.Tracing_CustomTag{ - "test": { - Type: &meshconfig.Tracing_CustomTag_Environment{ - Environment: &meshconfig.Tracing_Environment{ - Name: "FOO", - }, - }, - }, - }, - }, - }, - }, - } - - return opts -} - -func fakeSkywalking() *meshconfig.MeshConfig_ExtensionProvider { - return &meshconfig.MeshConfig_ExtensionProvider{ - Name: "foo", - Provider: &meshconfig.MeshConfig_ExtensionProvider_Skywalking{ - Skywalking: &meshconfig.MeshConfig_ExtensionProvider_SkyWalkingTracingProvider{ - Service: "skywalking-oap.dubbo-system.svc.cluster.local", - Port: 11800, - }, - }, - } -} - -func fakeOptsOnlySkywalkingTelemetryAPI() buildListenerOpts { - var opts buildListenerOpts - opts.push = &model.PushContext{ - Mesh: &meshconfig.MeshConfig{ - ExtensionProviders: []*meshconfig.MeshConfig_ExtensionProvider{ - { - Name: "foo", - Provider: &meshconfig.MeshConfig_ExtensionProvider_Skywalking{ - Skywalking: &meshconfig.MeshConfig_ExtensionProvider_SkyWalkingTracingProvider{ - Service: "skywalking-oap.dubbo-system.svc.cluster.local", - Port: 11800, - }, - }, - }, - }, - }, - } - opts.proxy = &model.Proxy{ - Metadata: &model.NodeMetadata{ - ProxyConfig: &model.NodeMetaProxyConfig{}, - }, - } - - return opts -} - -func fakeInboundOptsOnlySkywalkingTelemetryAPI() buildListenerOpts { - opts := fakeOptsOnlySkywalkingTelemetryAPI() - opts.class = networking.ListenerClassSidecarInbound - return opts -} - -func fakeTracingSpecNoProvider(sampling float64, disableReporting bool, useRequestIDForTraceSampling bool) *model.TracingConfig { - return fakeTracingSpec(nil, sampling, disableReporting, useRequestIDForTraceSampling) -} - -func fakeTracingSpecNoProviderWithNilCustomTag(sampling float64, disableReporting bool, useRequestIDForTraceSampling bool) *model.TracingConfig { - return fakeTracingSpecWithNilCustomTag(nil, sampling, disableReporting, useRequestIDForTraceSampling) -} - -func fakeTracingSpec(provider *meshconfig.MeshConfig_ExtensionProvider, sampling float64, disableReporting bool, - useRequestIDForTraceSampling bool) *model.TracingConfig { - t := &model.TracingConfig{ - ClientSpec: tracingSpec(provider, sampling, disableReporting, useRequestIDForTraceSampling), - ServerSpec: tracingSpec(provider, sampling, disableReporting, useRequestIDForTraceSampling), - } - return t -} - -func fakeClientOnlyTracingSpec(provider *meshconfig.MeshConfig_ExtensionProvider, sampling float64, disableReporting bool, - useRequestIDForTraceSampling bool) *model.TracingConfig { - t := &model.TracingConfig{ - ClientSpec: tracingSpec(provider, sampling, disableReporting, useRequestIDForTraceSampling), - ServerSpec: model.TracingSpec{ - Disabled: true, - }, - } - return t -} - -func fakeServerOnlyTracingSpec(provider *meshconfig.MeshConfig_ExtensionProvider, sampling float64, disableReporting bool, - useRequestIDForTraceSampling bool) *model.TracingConfig { - t := &model.TracingConfig{ - ClientSpec: model.TracingSpec{ - Disabled: true, - }, - ServerSpec: tracingSpec(provider, sampling, disableReporting, useRequestIDForTraceSampling), - } - return t -} - -func tracingSpec(provider *meshconfig.MeshConfig_ExtensionProvider, sampling float64, disableReporting bool, - useRequestIDForTraceSampling bool) model.TracingSpec { - return model.TracingSpec{ - Provider: provider, - Disabled: disableReporting, - RandomSamplingPercentage: sampling, - CustomTags: map[string]*tpb.Tracing_CustomTag{ - "test": { - Type: &tpb.Tracing_CustomTag_Environment{ - Environment: &tpb.Tracing_Environment{ - Name: "FOO", - }, - }, - }, - }, - UseRequestIDForTraceSampling: useRequestIDForTraceSampling, - } -} - -func fakeTracingSpecWithNilCustomTag(provider *meshconfig.MeshConfig_ExtensionProvider, sampling float64, disableReporting bool, - useRequestIDForTraceSampling bool) *model.TracingConfig { - t := &model.TracingConfig{ - ClientSpec: model.TracingSpec{ - Provider: provider, - Disabled: disableReporting, - RandomSamplingPercentage: sampling, - CustomTags: map[string]*tpb.Tracing_CustomTag{ - "test": nil, - }, - UseRequestIDForTraceSampling: useRequestIDForTraceSampling, - }, - ServerSpec: model.TracingSpec{ - Provider: provider, - Disabled: disableReporting, - RandomSamplingPercentage: sampling, - CustomTags: map[string]*tpb.Tracing_CustomTag{ - "test": nil, - }, - UseRequestIDForTraceSampling: useRequestIDForTraceSampling, - }, - } - return t -} - -func fakeTracingConfigNoProvider(randomSampling float64, maxLen uint32, tags []*tracing.CustomTag) *hpb.HttpConnectionManager_Tracing { - return fakeTracingConfig(nil, randomSampling, maxLen, tags) -} - -func fakeTracingConfig(provider *tracingcfg.Tracing_Http, randomSampling float64, maxLen uint32, tags []*tracing.CustomTag) *hpb.HttpConnectionManager_Tracing { - t := &hpb.HttpConnectionManager_Tracing{ - ClientSampling: &xdstype.Percent{ - Value: 100.0, - }, - OverallSampling: &xdstype.Percent{ - Value: 100.0, - }, - RandomSampling: &xdstype.Percent{ - Value: randomSampling, - }, - CustomTags: tags, - } - if maxLen != 0 { - t.MaxPathTagLength = wrapperspb.UInt32(maxLen) - } - if provider != nil { - t.Provider = provider - } - return t -} - -var fakeEnvTag = &tracing.CustomTag{ - Tag: "test", - Type: &tracing.CustomTag_Environment_{ - Environment: &tracing.CustomTag_Environment{ - Name: "FOO", - }, - }, -} - -func fakeZipkinProvider(expectClusterName, expectAuthority, expectProviderName string) *tracingcfg.Tracing_Http { - fakeZipkinProviderConfig := &tracingcfg.ZipkinConfig{ - CollectorCluster: expectClusterName, - CollectorEndpoint: "/api/v2/spans", - CollectorEndpointVersion: tracingcfg.ZipkinConfig_HTTP_JSON, - CollectorHostname: expectAuthority, - TraceId_128Bit: true, - SharedSpanContext: wrapperspb.Bool(false), - } - fakeZipkinAny, _ := anypb.New(fakeZipkinProviderConfig) - return &tracingcfg.Tracing_Http{ - Name: expectProviderName, - ConfigType: &tracingcfg.Tracing_Http_TypedConfig{TypedConfig: fakeZipkinAny}, - } -} - -func fakeSkywalkingProvider(expectClusterName, expectAuthority, expectProviderName string) *tracingcfg.Tracing_Http { - fakeSkywalkingProviderConfig := &tracingcfg.SkyWalkingConfig{ - GrpcService: &envoy_config_core_v3.GrpcService{ - TargetSpecifier: &envoy_config_core_v3.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &envoy_config_core_v3.GrpcService_EnvoyGrpc{ - ClusterName: expectClusterName, - Authority: expectAuthority, - }, - }, - }, - } - fakeSkywalkingAny, _ := anypb.New(fakeSkywalkingProviderConfig) - return &tracingcfg.Tracing_Http{ - Name: expectProviderName, - ConfigType: &tracingcfg.Tracing_Http_TypedConfig{TypedConfig: fakeSkywalkingAny}, - } -} diff --git a/pilot/pkg/networking/dubbo/v1alpha1/debouncehelper.go b/pilot/pkg/networking/dubbo/v1alpha1/debouncehelper.go deleted file mode 100644 index 2e9ff409e..000000000 --- a/pilot/pkg/networking/dubbo/v1alpha1/debouncehelper.go +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 v1alpha1 - -import ( - "fmt" - "time" -) - -import ( - "go.uber.org/atomic" -) - -/** -Copy From pilot/pkg/xds/discovery.go -*/ -type debounceOptions struct { - // debounceAfter is the delay added to events to wait - // after a registry/config event for debouncing. - // This will delay the push by at least this interval, plus - // the time getting subsequent events. If no change is - // detected the push will happen, otherwise we'll keep - // delaying until things settle. - debounceAfter time.Duration - - // debounceMax is the maximum time to wait for events - // while debouncing. Defaults to 10 seconds. If events keep - // showing up with no break for this time, we'll trigger a push. - debounceMax time.Duration - - // enableDebounce indicates whether EDS pushes should be debounced. - enableDebounce bool -} - -type DebounceHelper struct { -} - -func (h *DebounceHelper) Debounce(ch chan *pushRequest, stopCh <-chan struct{}, opts debounceOptions, pushFn func(req *pushRequest), updateSent *atomic.Int64) { - defer func() { - err := recover() - if err != nil { - log.Infof("Debounce panic caused by: {%+v}", err) - } - }() - - var timeChan <-chan time.Time - var startDebounce time.Time - var lastConfigUpdateTime time.Time - - pushCounter := 0 - debouncedEvents := 0 - - // Keeps track of the push requests. If updates are debounce they will be merged. - var req *pushRequest - - free := true - freeCh := make(chan struct{}, 1) - - push := func(req *pushRequest, debouncedEvents int) { - pushFn(req) - updateSent.Add(int64(debouncedEvents)) - freeCh <- struct{}{} - } - - pushWorker := func() { - eventDelay := time.Since(startDebounce) - quietTime := time.Since(lastConfigUpdateTime) - // it has been too long or quiet enough - if eventDelay >= opts.debounceMax || quietTime >= opts.debounceAfter { - if req != nil { - pushCounter++ - if req.ConfigsUpdated == nil { - log.Infof("Push debounce stable[%d] %d : %v since last change, %v since last push", - pushCounter, debouncedEvents, - quietTime, eventDelay) - } else { - log.Infof("Push debounce stable[%d] %d for config %s: %v since last change, %v since last push", - pushCounter, debouncedEvents, PushRequestConfigsUpdated(req), - quietTime, eventDelay) - } - free = false - go push(req, debouncedEvents) - req = nil - debouncedEvents = 0 - } - } else { - timeChan = time.After(opts.debounceAfter - quietTime) - } - } - - for { - select { - case <-freeCh: - free = true - pushWorker() - case r := <-ch: - if !opts.enableDebounce { - // trigger push now, just for EDS - go func(req *pushRequest) { - pushFn(req) - updateSent.Inc() - }(r) - continue - } - - lastConfigUpdateTime = time.Now() - if debouncedEvents == 0 { - timeChan = time.After(opts.debounceAfter) - startDebounce = lastConfigUpdateTime - } - debouncedEvents++ - - req = req.Merge(r) - case <-timeChan: - if free { - pushWorker() - } - case <-stopCh: - return - } - } -} - -func PushRequestConfigsUpdated(req *pushRequest) string { - configs := "" - for key := range req.ConfigsUpdated { - configs += key.String() - break - } - if len(req.ConfigsUpdated) > 1 { - more := fmt.Sprintf(" and %d more configs", len(req.ConfigsUpdated)-1) - configs += more - } - return configs -} diff --git a/pilot/pkg/networking/dubbo/v1alpha1/servicemetadataserver.go b/pilot/pkg/networking/dubbo/v1alpha1/servicemetadataserver.go deleted file mode 100644 index 27061a935..000000000 --- a/pilot/pkg/networking/dubbo/v1alpha1/servicemetadataserver.go +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 v1alpha1 - -import ( - "context" - "fmt" - "strconv" - "time" -) - -import ( - "github.com/pkg/errors" - "go.uber.org/atomic" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - dubbov1alpha1 "istio.io/api/dubbo/v1alpha1" - istioextensionsv1alpha1 "istio.io/api/extensions/v1alpha1" - "istio.io/client-go/pkg/apis/extensions/v1alpha1" - apierror "k8s.io/apimachinery/pkg/api/errors" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -type ServiceMetadataServer struct { - dubbov1alpha1.UnimplementedServiceMetadataServiceServer - - KubeClient kube.Client - - // CommittedUpdates describes the number of configuration updates the discovery server has - // received, process, and stored in the push context. If this number is less than InboundUpdates, - // there are updates we have not yet processed. - // Note: This does not mean that all proxies have received these configurations; it is strictly - // the push context, which means that the next push to a proxy will receive this configuration. - CommittedUpdates *atomic.Int64 - - ch chan *pushRequest - - debounceOptions debounceOptions - debounceHelper DebounceHelper -} - -type pushRequest struct { - // application name + revision => metadata info - ConfigsUpdated map[model.ConfigKey]metadataConfig -} - -type metadataConfig struct { - applicationName string - revision string - metadataInfo string - timestamp time.Time -} - -func (pr *pushRequest) Merge(other *pushRequest) *pushRequest { - if pr == nil { - return other - } - if other == nil { - return pr - } - - // Keep the first (older) start time - - // Do not merge when any one is empty - if len(pr.ConfigsUpdated) == 0 || len(other.ConfigsUpdated) == 0 { - pr.ConfigsUpdated = nil - } else { - for key, value := range other.ConfigsUpdated { - if prValue, ok := pr.ConfigsUpdated[key]; ok { - if value.timestamp.After(prValue.timestamp) { - pr.ConfigsUpdated[key] = value - } - } else { - pr.ConfigsUpdated[key] = value - } - } - } - - return pr -} - -func NewServiceMetadataServer(client kube.Client) *ServiceMetadataServer { - return &ServiceMetadataServer{ - CommittedUpdates: atomic.NewInt64(0), - KubeClient: client, - ch: make(chan *pushRequest, 10), - debounceOptions: debounceOptions{ - debounceAfter: features.SMDebounceAfter, - debounceMax: features.SMDebounceMax, - enableDebounce: features.SMEnableDebounce, - }, - } -} - -func (s *ServiceMetadataServer) Start(stopCh <-chan struct{}) { - if s == nil { - log.Warn("ServiceMetadataServer is not init, skip start") - return - } - go s.handleUpdate(stopCh) - go s.removeOutdatedCRD(stopCh) -} - -func (s *ServiceMetadataServer) Register(rpcs *grpc.Server) { - // Register v3 server - dubbov1alpha1.RegisterServiceMetadataServiceServer(rpcs, s) -} - -func (s *ServiceMetadataServer) Publish(ctx context.Context, request *dubbov1alpha1.PublishServiceMetadataRequest) (*dubbov1alpha1.PublishServiceMetadataResponse, error) { - pushReq := &pushRequest{ConfigsUpdated: map[model.ConfigKey]metadataConfig{}} - - key := model.ConfigKey{ - Name: getConfigKeyName(request.GetApplicationName(), request.GetRevision()), - Namespace: request.GetNamespace(), - } - - pushReq.ConfigsUpdated[key] = metadataConfig{ - applicationName: request.GetApplicationName(), - revision: request.GetRevision(), - metadataInfo: request.GetMetadataInfo(), - timestamp: time.Now(), - } - - s.ch <- pushReq - - return &dubbov1alpha1.PublishServiceMetadataResponse{}, nil -} - -func getConfigKeyName(applicationName, revision string) string { - log.Infof("application name: %s, revision: %s", applicationName, revision) - return fmt.Sprintf("%s-%s", applicationName, revision) -} - -func (s *ServiceMetadataServer) Get(ctx context.Context, request *dubbov1alpha1.GetServiceMetadataRequest) (*dubbov1alpha1.GetServiceMetadataResponse, error) { - metadata, err := s.KubeClient.Istio().ExtensionsV1alpha1().ServiceMetadatas(request.Namespace).Get(ctx, getConfigKeyName(request.GetApplicationName(), request.GetRevision()), v1.GetOptions{}) - if err != nil { - return nil, status.Errorf(codes.Aborted, err.Error()) - } - - return &dubbov1alpha1.GetServiceMetadataResponse{MetadataInfo: metadata.Spec.GetMetadataInfo()}, nil -} - -func (s *ServiceMetadataServer) handleUpdate(stopCh <-chan struct{}) { - s.debounce(stopCh) -} - -func (s *ServiceMetadataServer) Push(req *pushRequest) { - ctx := context.TODO() - - for key, config := range req.ConfigsUpdated { - metadata, err := getOrCreateCRD(s.KubeClient, ctx, key.Namespace, config.applicationName, config.revision, config.metadataInfo, config.timestamp) - if err != nil { - log.Errorf("Failed to getOrCreateCRD, %s", err.Error()) - return - } - - metadata.Spec.MetadataInfo = config.metadataInfo - - _, err = s.KubeClient.Istio().ExtensionsV1alpha1().ServiceMetadatas(metadata.Namespace).Update(ctx, metadata, v1.UpdateOptions{}) - if err != nil { - log.Errorf("Failed to update metadata, %s", err.Error()) - return - } - } -} - -func getOrCreateCRD(kubeClient kube.Client, ctx context.Context, namespace string, applicationName, revision, metadataInfo string, createTime time.Time) (*v1alpha1.ServiceMetadata, error) { - var ( - metadata *v1alpha1.ServiceMetadata - ) - - serviceMetadataInterface := kubeClient.Istio().ExtensionsV1alpha1().ServiceMetadatas(namespace) - crdName := getConfigKeyName(applicationName, revision) - metadata, err := serviceMetadataInterface.Get(ctx, crdName, v1.GetOptions{}) - - log.Infof("metadata: %+v", metadata) - - if err != nil { - if apierror.IsNotFound(err) { - mt := &v1alpha1.ServiceMetadata{ - ObjectMeta: v1.ObjectMeta{ - Name: crdName, - Namespace: namespace, - Annotations: map[string]string{ - "updateTime": strconv.FormatInt(createTime.Unix(), 10), - }, - }, - Spec: istioextensionsv1alpha1.ServiceMetadata{ - ApplicationName: applicationName, - Revision: revision, - MetadataInfo: metadataInfo, - }, - } - - metadata, err = serviceMetadataInterface.Create(ctx, mt, v1.CreateOptions{}) - if err != nil { - return nil, errors.Wrap(err, "Failed to create metadata") - } - } else { - return nil, errors.Wrap(err, "Failed to check metadata exists or not") - } - } else { - // if metadata exist in crd, then update timestamp - if metadata.ObjectMeta.Annotations != nil { - metadata.ObjectMeta.Annotations["updateTime"] = strconv.FormatInt(createTime.Unix(), 10) - } else { - metadata.ObjectMeta.Annotations = map[string]string{ - "updateTime": strconv.FormatInt(createTime.Unix(), 10), - } - } - } - - return metadata, nil -} - -func (s *ServiceMetadataServer) debounce(stopCh <-chan struct{}) { - s.debounceHelper.Debounce(s.ch, stopCh, s.debounceOptions, s.Push, s.CommittedUpdates) -} - -func (s *ServiceMetadataServer) removeOutdatedCRD(stopChan <-chan struct{}) { - ctx, cancel := context.WithCancel(context.TODO()) - ticker := time.NewTicker(2 * 60 * time.Hour) - - for { - select { - case <-stopChan: - cancel() - ticker.Stop() - return - case <-ticker.C: - namespaces, err := s.KubeClient.Kube().CoreV1().Namespaces().List(ctx, v1.ListOptions{}) - if err != nil { - log.Errorf("Failed to get namespaces, %s", err.Error()) - continue - } - - for _, namespace := range namespaces.Items { - metadataList, err := s.KubeClient.Istio().ExtensionsV1alpha1().ServiceMetadatas(namespace.GetNamespace()).List(ctx, v1.ListOptions{}) - if err != nil { - log.Errorf("Failed to get metadata with namespace {%s}, %s", namespace, err.Error()) - continue - } - - for _, metadata := range metadataList.Items { - if metadata.Annotations != nil { - if ts, ok := metadata.Annotations["updateTime"]; ok { - tsInt, err := strconv.ParseInt(ts, 10, 64) - if err != nil { - log.Errorf("Failed to get metadata with namespace {%s}, %s", namespace, err.Error()) - } else { - if time.Now().Sub(time.Unix(tsInt, 0)) > 24*time.Hour { - err := s.KubeClient.Istio().ExtensionsV1alpha1().ServiceMetadatas(namespace.GetNamespace()).Delete(ctx, metadata.GetName(), v1.DeleteOptions{}) - if err != nil { - log.Errorf("Failed to delete outdated metadata with namespace {%s}, metadata {%s}, err {%s}", namespace, metadata.GetName(), err.Error()) - } - } - } - } else { - log.Errorf("Failed to get metadata with namespace {%s}, metadata {%s}", namespace, metadata.GetName()) - } - } - } - } - - } - } -} diff --git a/pilot/pkg/networking/dubbo/v1alpha1/servicenamemappingserver.go b/pilot/pkg/networking/dubbo/v1alpha1/servicenamemappingserver.go deleted file mode 100644 index ddea47465..000000000 --- a/pilot/pkg/networking/dubbo/v1alpha1/servicenamemappingserver.go +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 v1alpha1 - -import ( - "context" - "fmt" - "strings" - "time" -) - -import ( - "github.com/pkg/errors" - "google.golang.org/grpc" - dubbov1alpha1 "istio.io/api/dubbo/v1alpha1" - extensionsv1alpha1 "istio.io/api/extensions/v1alpha1" - "istio.io/client-go/pkg/apis/extensions/v1alpha1" - istiolog "istio.io/pkg/log" - apierror "k8s.io/apimachinery/pkg/api/errors" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -var log = istiolog.RegisterScope("Snp server", "Snp register server for Proxyless dubbo", 0) - -type Snp struct { - dubbov1alpha1.UnimplementedServiceNameMappingServiceServer - - KubeClient kube.Client - queue chan *RegisterRequest - debounceAfter time.Duration - debounceMax time.Duration - enableDebounce bool -} - -func NewSnp(kubeClient kube.Client) *Snp { - return &Snp{ - KubeClient: kubeClient, - queue: make(chan *RegisterRequest, 10), - debounceAfter: features.SNPDebounceAfter, - debounceMax: features.SNPDebounceMax, - enableDebounce: features.SNPEnableDebounce, - } -} - -// RegisterServiceAppMapping register service app mapping. -func (s *Snp) RegisterServiceAppMapping(ctx context.Context, req *dubbov1alpha1.ServiceMappingRequest) (*dubbov1alpha1.ServiceMappingResponse, error) { - namespace := req.GetNamespace() - interfaces := req.GetInterfaceNames() - applicationName := req.GetApplicationName() - - registerReq := &RegisterRequest{ConfigsUpdated: map[model.ConfigKey]map[string]struct{}{}} - for _, interfaceName := range interfaces { - key := model.ConfigKey{ - Name: interfaceName, - Namespace: namespace, - } - if _, ok := registerReq.ConfigsUpdated[key]; !ok { - registerReq.ConfigsUpdated[key] = make(map[string]struct{}) - } - registerReq.ConfigsUpdated[key][applicationName] = struct{}{} - } - s.queue <- registerReq - - return &dubbov1alpha1.ServiceMappingResponse{}, nil -} - -func (s *Snp) Register(server *grpc.Server) { - dubbov1alpha1.RegisterServiceNameMappingServiceServer(server, s) -} - -func (s *Snp) Start(stop <-chan struct{}) { - if s == nil { - log.Warn("Snp server is not init, skip start") - return - } - go s.debounce(stop, s.push) -} - -func (s *Snp) push(req *RegisterRequest) { - for key, m := range req.ConfigsUpdated { - var appNames []string - for app := range m { - appNames = append(appNames, app) - } - for i := 0; i < 3; i++ { - if err := tryRegister(s.KubeClient, key.Namespace, key.Name, appNames); err != nil { - log.Errorf(" register [%v] failed: %v, try again later", key, err) - } else { - break - } - } - } -} - -// Each application registers its services with the Snp server at startup, -// and there are usually multiple copies of a deployment. They make requests -// at the same time. So we need to debounce these requests, -// because only one request is valid for the same deployment! -// So we wait for a while, merge incoming requests, and submit them when -// there is a timeout or a short period of no subsequent requests, -// by default a minimum of 200ms and a maximum of 1s, which is usually acceptable. -// The above can significantly reduce the pressure on the registry. -func (s *Snp) debounce(stopCh <-chan struct{}, pushFn func(req *RegisterRequest)) { - ch := s.queue - var timeChan <-chan time.Time - var startDebounce time.Time - var lastConfigUpdateTime time.Time - - pushCounter := 0 - debouncedEvents := 0 - - var req *RegisterRequest - - free := true - freeCh := make(chan struct{}, 1) - - push := func(req *RegisterRequest) { - pushFn(req) - freeCh <- struct{}{} - } - - pushWorker := func() { - eventDelay := time.Since(startDebounce) - quietTime := time.Since(lastConfigUpdateTime) - // it has been too long or quiet enough - if eventDelay >= s.debounceMax || quietTime >= s.debounceAfter { - if req != nil { - pushCounter++ - - if req.ConfigsUpdated != nil { - log.Infof(" Push debounce stable[%d] %d for config %s: %v since last change, %v since last push", - pushCounter, debouncedEvents, configsUpdated(req), - quietTime, eventDelay) - } - free = false - go push(req) - req = nil - debouncedEvents = 0 - } - } else { - timeChan = time.After(s.debounceAfter - quietTime) - } - } - - for { - select { - case <-freeCh: - free = true - pushWorker() - case r := <-ch: - // if debounce is disabled, push immediately - if !s.enableDebounce { - go push(r) - req = nil - continue - } - - lastConfigUpdateTime = time.Now() - if debouncedEvents == 0 { - timeChan = time.After(200 * time.Millisecond) - startDebounce = lastConfigUpdateTime - } - debouncedEvents++ - - req = req.Merge(r) - case <-timeChan: - if free { - pushWorker() - } - case <-stopCh: - return - } - } -} - -func tryRegister(kubeClient kube.Client, namespace, interfaceName string, newApps []string) error { - log.Debugf("try register [%s] in namespace [%s] with [%v] apps", interfaceName, namespace, len(newApps)) - snp, created, err := getOrCreateSnp(kubeClient, namespace, interfaceName, newApps) - if created { - log.Debugf("register success, revision:%s", snp.ResourceVersion) - return nil - } - if err != nil { - return err - } - - previousLen := len(snp.Spec.ApplicationNames) - previousAppNames := make(map[string]struct{}, previousLen) - for _, name := range snp.Spec.ApplicationNames { - previousAppNames[name] = struct{}{} - } - for _, newApp := range newApps { - previousAppNames[newApp] = struct{}{} - } - if len(previousAppNames) == previousLen { - log.Debugf("[%s] has been registered: %v", interfaceName, newApps) - return nil - } - - mergedApps := make([]string, 0, len(previousAppNames)) - for name := range previousAppNames { - mergedApps = append(mergedApps, name) - } - snp.Spec.ApplicationNames = mergedApps - snpInterface := kubeClient.Istio().ExtensionsV1alpha1().ServiceNameMappings(namespace) - snp, err = snpInterface.Update(context.Background(), snp, v1.UpdateOptions{}) - if err != nil { - return errors.Wrap(err, " update failed") - } - log.Debugf("register update success, revision:%s", snp.ResourceVersion) - return nil -} - -func getOrCreateSnp(kubeClient kube.Client, namespace string, interfaceName string, newApps []string) (*v1alpha1.ServiceNameMapping, bool, error) { - ctx := context.TODO() - // snp name is a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-' - lowerCaseName := strings.ToLower(strings.ReplaceAll(interfaceName, ".", "-")) - snpInterface := kubeClient.Istio().ExtensionsV1alpha1().ServiceNameMappings(namespace) - snp, err := snpInterface.Get(ctx, lowerCaseName, v1.GetOptions{}) - if err != nil { - if apierror.IsNotFound(err) { - //try to create snp, this operation may be conflict with other goroutine - snp, err = snpInterface.Create(ctx, &v1alpha1.ServiceNameMapping{ - ObjectMeta: v1.ObjectMeta{ - Name: lowerCaseName, - Namespace: namespace, - Labels: map[string]string{ - "interface": interfaceName, - }, - }, - Spec: extensionsv1alpha1.ServiceNameMapping{ - InterfaceName: interfaceName, - ApplicationNames: newApps, - }, - }, v1.CreateOptions{}) - // create success - if err == nil { - log.Debugf("create snp %s revision %s", interfaceName, snp.ResourceVersion) - return snp, true, nil - } - // If the creation fails, meaning it already created by other goroutine, then get it - if apierror.IsAlreadyExists(err) { - log.Debugf("[%s] has been exists, err: %v", err) - snp, err = snpInterface.Get(ctx, lowerCaseName, v1.GetOptions{}) - // maybe failed to get snp cause of network issue, just return error - if err != nil { - return nil, false, errors.Wrap(err, "tryRegister retry get snp error") - } - } - } else { - return nil, false, errors.Wrap(err, "tryRegister get snp error") - } - } - return snp, false, nil -} - -type RegisterRequest struct { - ConfigsUpdated map[model.ConfigKey]map[string]struct{} -} - -func (r *RegisterRequest) Merge(req *RegisterRequest) *RegisterRequest { - if r == nil { - return req - } - for key, newApps := range req.ConfigsUpdated { - if _, ok := r.ConfigsUpdated[key]; !ok { - r.ConfigsUpdated[key] = make(map[string]struct{}) - } - for app, _ := range newApps { - r.ConfigsUpdated[key][app] = struct{}{} - } - } - return r -} - -func configsUpdated(req *RegisterRequest) string { - configs := "" - for key := range req.ConfigsUpdated { - configs += key.String() - break - } - if len(req.ConfigsUpdated) > 1 { - more := fmt.Sprintf(" and %d more configs", len(req.ConfigsUpdated)-1) - configs += more - } - return configs -} diff --git a/pilot/pkg/networking/dubbo/v1alpha1/servicenamemappingserver_test.go b/pilot/pkg/networking/dubbo/v1alpha1/servicenamemappingserver_test.go deleted file mode 100644 index 09ce1e89f..000000000 --- a/pilot/pkg/networking/dubbo/v1alpha1/servicenamemappingserver_test.go +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 v1alpha1 - -import ( - "context" - "strconv" - "strings" - "sync" - "testing" - "time" -) - -import ( - dubbov1alpha1 "istio.io/api/dubbo/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -func TestRegisterServiceAppMapping(t *testing.T) { - client := kube.NewFakeClient() - s := NewSnp(client) - stop := make(chan struct{}) - - s.Start(stop) - w := sync.WaitGroup{} - for i := 0; i < 100; i++ { - w.Add(1) - j := i - go func() { - _, _ = s.RegisterServiceAppMapping(context.Background(), &dubbov1alpha1.ServiceMappingRequest{ - Namespace: "default", - ApplicationName: "app" + strconv.Itoa(j), - InterfaceNames: []string{"com.test.TestService"}, - }) - w.Done() - }() - } - - w.Wait() - time.Sleep(s.debounceMax) - stop <- struct{}{} - - lowerCaseName := strings.ToLower(strings.ReplaceAll("com.test.TestService", ".", "-")) - mapping, err := client.Istio().ExtensionsV1alpha1().ServiceNameMappings("default").Get(context.Background(), lowerCaseName, v1.GetOptions{}) - if err != nil { - t.Fatal(err) - } - assert.Equal(t, len(mapping.Spec.ApplicationNames), 100) -} - -func TestReqMerge(t *testing.T) { - tests := []struct { - name string - reqs []*RegisterRequest - want *RegisterRequest - }{ - { - name: "different", - reqs: []*RegisterRequest{ - { - ConfigsUpdated: map[model.ConfigKey]map[string]struct{}{ - model.ConfigKey{ - Name: "a-b-c-demoservice", - Namespace: "default", - }: { - "app1": struct{}{}, - "app2": struct{}{}, - }, - }, - }, - { - ConfigsUpdated: map[model.ConfigKey]map[string]struct{}{ - model.ConfigKey{ - Name: "a-b-c-demoservice", - Namespace: "default", - }: { - "app3": struct{}{}, - }, - }, - }, - }, - want: &RegisterRequest{ - ConfigsUpdated: map[model.ConfigKey]map[string]struct{}{ - model.ConfigKey{ - Name: "a-b-c-demoservice", - Namespace: "default", - }: { - "app1": struct{}{}, - "app2": struct{}{}, - "app3": struct{}{}, - }, - }, - }, - }, - { - name: "same", - reqs: []*RegisterRequest{ - { - ConfigsUpdated: map[model.ConfigKey]map[string]struct{}{ - model.ConfigKey{ - Name: "a-b-c-demoservice", - Namespace: "default", - }: { - "app1": struct{}{}, - }, - }, - }, - { - ConfigsUpdated: map[model.ConfigKey]map[string]struct{}{ - model.ConfigKey{ - Name: "a-b-c-demoservice", - Namespace: "default", - }: { - "app1": struct{}{}, - }, - }, - }, - }, - want: &RegisterRequest{ - ConfigsUpdated: map[model.ConfigKey]map[string]struct{}{ - model.ConfigKey{ - Name: "a-b-c-demoservice", - Namespace: "default", - }: { - "app1": struct{}{}, - }, - }, - }, - }, - } - for _, tt := range tests { - var got *RegisterRequest - t.Run(tt.name, func(t *testing.T) { - for _, r := range tt.reqs { - got = got.Merge(r) - } - assert.Equal(t, got, tt.want) - }) - } -} diff --git a/pilot/pkg/networking/dubbogen/dubbogen.go b/pilot/pkg/networking/dubbogen/dubbogen.go deleted file mode 100644 index 168b28361..000000000 --- a/pilot/pkg/networking/dubbogen/dubbogen.go +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 dubbogen - -import ( - istiolog "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" -) - -var log = istiolog.RegisterScope("dubbogen", "xDS Generator for Proxyless dubbo", 0) - -type DubboConfigGenerator struct{} - -func (d *DubboConfigGenerator) Generate(proxy *model.Proxy, w *model.WatchedResource, req *model.PushRequest) (model.Resources, model.XdsLogDetails, error) { - switch w.TypeUrl { - case v3.DubboServiceNameMappingType: - return d.buildServiceNameMappings(proxy, req, w.ResourceNames), model.DefaultXdsLogDetails, nil - default: - log.Warnf("not support now %s", w.TypeUrl) - return nil, model.DefaultXdsLogDetails, nil - } -} diff --git a/pilot/pkg/networking/dubbogen/servicenamemapping.go b/pilot/pkg/networking/dubbogen/servicenamemapping.go deleted file mode 100644 index 714038dcc..000000000 --- a/pilot/pkg/networking/dubbogen/servicenamemapping.go +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 dubbogen - -import ( - "strings" -) - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - istioioapidubbov1alpha1 "istio.io/api/dubbo/v1alpha1" - istioioapiextensionsv1alpha1 "istio.io/api/extensions/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -// Map of all configs that do not impact LDS -var snpConfigs = map[model.NodeType]map[config.GroupVersionKind]struct{}{ - model.SidecarProxy: { - gvk.ServiceNameMapping: {}, - }, -} - -func snpNeedsPush(proxy *model.Proxy, req *model.PushRequest) bool { - if req == nil { - return true - } - if !req.Full { - // SNP only handles full push - return false - } - // If none set, we will always push - if len(req.ConfigsUpdated) == 0 { - return true - } - for conf := range req.ConfigsUpdated { - if _, f := snpConfigs[proxy.Type][conf.Kind]; f { - return true - } - } - return false -} - -func (d *DubboConfigGenerator) buildServiceNameMappings(node *model.Proxy, req *model.PushRequest, watchedResourceNames []string) model.Resources { - log.Debugf("building snp for %s", node.ID) - if !snpNeedsPush(node, req) { - return nil - } - snps := buildSnp(node, req, watchedResourceNames) - log.Debugf("snp res for %s:\n%v", node.ID, snps) - resources := model.Resources{} - for _, c := range snps { - resources = append(resources, &discovery.Resource{ - Name: c.InterfaceName, - Resource: util.MessageToAny(c), - }) - } - return resources -} - -func buildSnp(node *model.Proxy, req *model.PushRequest, watchedResourceNames []string) []*istioioapidubbov1alpha1.ServiceMappingXdsResponse { - defaultNs := node.ConfigNamespace - res := make([]*istioioapidubbov1alpha1.ServiceMappingXdsResponse, 0, len(watchedResourceNames)) - - updatedMap := map[string]interface{}{} - for update, _ := range req.ConfigsUpdated { - watchedResourceName := buildNameAndNameSpace(update.Name, update.Namespace) - updatedMap[watchedResourceName] = struct{}{} - } - // if req.ConfigsUpdated is empty, it means that all watched resources need to be pushed - for _, w := range watchedResourceNames { - watchedResourceName := buildNameAndNameSpace(w, defaultNs) - // if configsUpdated empty, meaning a full push of watched snp resources. - if _, exists := updatedMap[watchedResourceName]; exists || len(req.ConfigsUpdated) == 0 { - snpName, namespace := extractNameAndNameSpace(watchedResourceName, defaultNs) - if snp := req.Push.ServiceNameMappingsByNameSpaceAndInterfaceName(namespace, snpName); snp != nil { - mapping := snp.Spec.(*istioioapiextensionsv1alpha1.ServiceNameMapping) - res = append(res, &istioioapidubbov1alpha1.ServiceMappingXdsResponse{ - Namespace: snp.Namespace, - InterfaceName: mapping.InterfaceName, - ApplicationNames: mapping.ApplicationNames, - }) - } - } - } - return res -} - -// extractNameAndNameSpace watchedResource should like '[a.b.interface]|[namespace]', if namespace is nil, -// defaultNamespace will be used. -func extractNameAndNameSpace(watchedResource, defaultNamespace string) (string, string) { - split := strings.Split(watchedResource, "|") - if len(split) == 1 || split[1] == "" { - return split[0], defaultNamespace - } - return split[0], split[1] -} - -// buildNameAndNameSpace watchedResource should like '[a.b.interface]|[namespace]', if namespace is nil, -// defaultNamespace will be used. -func buildNameAndNameSpace(watchedResource, defaultNamespace string) string { - split := strings.Split(watchedResource, "|") - if len(split) == 1 || split[1] == "" { - return strings.Join([]string{split[0], defaultNamespace}, "|") - } - return watchedResource -} diff --git a/pilot/pkg/networking/grpcgen/cds.go b/pilot/pkg/networking/grpcgen/cds.go deleted file mode 100644 index 9dea9b678..000000000 --- a/pilot/pkg/networking/grpcgen/cds.go +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package grpcgen - -import ( - "fmt" -) - -import ( - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - corexds "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// BuildClusters handles a gRPC CDS request, used with the 'ApiListener' style of requests. -// The main difference is that the request includes Resources to filter. -func (g *GrpcConfigGenerator) BuildClusters(node *model.Proxy, push *model.PushContext, names []string) model.Resources { - filter := newClusterFilter(names) - clusters := make([]*cluster.Cluster, 0, len(names)) - for defaultClusterName, subsetFilter := range filter { - builder, err := newClusterBuilder(node, push, defaultClusterName, subsetFilter) - if err != nil { - log.Warn(err) - continue - } - clusters = append(clusters, builder.build()...) - } - - resp := make(model.Resources, 0, len(clusters)) - for _, c := range clusters { - resp = append(resp, &discovery.Resource{ - Name: c.Name, - Resource: util.MessageToAny(c), - }) - } - if len(resp) == 0 && len(names) == 0 { - log.Warnf("did not generate any cds for %s; no names provided", node.ID) - } - return resp -} - -// newClusterFilter maps a non-subset cluster name to the list of actual cluster names (default or subset) actually -// requested by the client. gRPC will usually request a group of clusters that are used in the same route; in some -// cases this means subsets associated with the same default cluster aren't all expected in the same CDS response. -func newClusterFilter(names []string) map[string]sets.Set { - filter := map[string]sets.Set{} - for _, name := range names { - dir, _, hn, p := model.ParseSubsetKey(name) - defaultKey := model.BuildSubsetKey(dir, "", hn, p) - if _, ok := filter[defaultKey]; !ok { - filter[defaultKey] = sets.New() - } - filter[defaultKey].Insert(name) - } - return filter -} - -// clusterBuilder is responsible for building a single default and subset clusters for a service -// TODO re-use the v1alpha3.ClusterBuilder: -// Most of the logic is similar, I think we can just share the code if we expose: -// * BuildSubsetCluster -// * BuildDefaultCluster -// * BuildClusterOpts and members -// * Add something to allow us to override how tlscontext is built -type clusterBuilder struct { - push *model.PushContext - node *model.Proxy - - // guaranteed to be set in init - defaultClusterName string - requestedClusterName string - hostname host.Name - portNum int - - // may not be set - svc *model.Service - port *model.Port - filter sets.Set -} - -func newClusterBuilder(node *model.Proxy, push *model.PushContext, defaultClusterName string, filter sets.Set) (*clusterBuilder, error) { - _, _, hostname, portNum := model.ParseSubsetKey(defaultClusterName) - if hostname == "" || portNum == 0 { - return nil, fmt.Errorf("failed parsing subset key: %s", defaultClusterName) - } - - // try to resolve the service and port - var port *model.Port - svc := push.ServiceForHostname(node, hostname) - if svc == nil { - log.Warnf("cds gen for %s: did not find service for cluster %s", node.ID, defaultClusterName) - } else { - var ok bool - port, ok = svc.Ports.GetByPort(portNum) - if !ok { - log.Warnf("cds gen for %s: did not find port %d in service for cluster %s", node.ID, portNum, defaultClusterName) - } - } - - return &clusterBuilder{ - node: node, - push: push, - - defaultClusterName: defaultClusterName, - hostname: hostname, - portNum: portNum, - filter: filter, - - svc: svc, - port: port, - }, nil -} - -// subsetFilter returns the requestedClusterName if it isn't the default cluster -// for subset clusters, gRPC may request them individually -func (b *clusterBuilder) subsetFilter() string { - if b.defaultClusterName == b.requestedClusterName { - return "" - } - return b.requestedClusterName -} - -func (b *clusterBuilder) build() []*cluster.Cluster { - var defaultCluster *cluster.Cluster - if b.filter.Contains(b.defaultClusterName) { - defaultCluster = edsCluster(b.defaultClusterName) - } - - subsetClusters := b.applyDestinationRule(defaultCluster) - out := make([]*cluster.Cluster, 0, 1+len(subsetClusters)) - if defaultCluster != nil { - out = append(out, defaultCluster) - } - return append(out, subsetClusters...) -} - -// edsCluster creates a simple cluster to read endpoints from ads/eds. -func edsCluster(name string) *cluster.Cluster { - return &cluster.Cluster{ - Name: name, - ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS}, - EdsClusterConfig: &cluster.Cluster_EdsClusterConfig{ - ServiceName: name, - EdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_Ads{ - Ads: &core.AggregatedConfigSource{}, - }, - }, - }, - } -} - -// applyDestinationRule mutates the default cluster to reflect traffic policies, and returns a set of additional -// subset clusters if specified by a destination rule -func (b *clusterBuilder) applyDestinationRule(defaultCluster *cluster.Cluster) (subsetClusters []*cluster.Cluster) { - if b.svc == nil || b.port == nil { - return nil - } - - // resolve policy from context - destinationRule := corexds.CastDestinationRule(b.node.SidecarScope.DestinationRule( - model.TrafficDirectionOutbound, b.node, b.svc.Hostname)) - trafficPolicy := corexds.MergeTrafficPolicy(nil, destinationRule.GetTrafficPolicy(), b.port) - - // setup default cluster - b.applyTrafficPolicy(defaultCluster, trafficPolicy) - - // subset clusters - if len(destinationRule.GetSubsets()) > 0 { - subsetClusters = make([]*cluster.Cluster, 0, len(destinationRule.GetSubsets())) - for _, subset := range destinationRule.GetSubsets() { - subsetKey := subsetClusterKey(subset.Name, string(b.hostname), b.portNum) - if !b.filter.Contains(subsetKey) { - continue - } - c := edsCluster(subsetKey) - trafficPolicy := corexds.MergeTrafficPolicy(trafficPolicy, subset.TrafficPolicy, b.port) - b.applyTrafficPolicy(c, trafficPolicy) - subsetClusters = append(subsetClusters, c) - } - } - - return -} - -// applyTrafficPolicy mutates the give cluster (if not-nil) so that the given merged traffic policy applies. -func (b *clusterBuilder) applyTrafficPolicy(c *cluster.Cluster, trafficPolicy *networking.TrafficPolicy) { - // cluster can be nil if it wasn't requested - if c == nil { - return - } - b.applyTLS(c, trafficPolicy) - b.applyLoadBalancing(c, trafficPolicy) - // TODO status or log when unsupported features are included -} - -func (b *clusterBuilder) applyLoadBalancing(c *cluster.Cluster, policy *networking.TrafficPolicy) { - switch policy.GetLoadBalancer().GetSimple() { - case networking.LoadBalancerSettings_ROUND_ROBIN, networking.LoadBalancerSettings_UNSPECIFIED: - // ok - default: - log.Warnf("cannot apply LbPolicy %s to %s", policy.LoadBalancer.GetSimple(), b.node.ID) - } - corexds.ApplyRingHashLoadBalancer(c, policy.GetLoadBalancer()) -} - -func (b *clusterBuilder) applyTLS(c *cluster.Cluster, policy *networking.TrafficPolicy) { - // TODO for now, we leave mTLS *off* by default: - // 1. We don't know if the client uses xds.NewClientCredentials; these settings will be ignored if not - // 2. We cannot reach servers in PERMISSIVE mode; gRPC doesn't allow us to override the alpn to one of Istio's - // 3. Once we support gRPC servers, we have no good way to detect if a server is implemented with xds.NewGrpcServer and will actually support our config - // For these reasons, support only explicit tls configuration. - switch policy.GetTls().GetMode() { - case networking.ClientTLSSettings_DISABLE: - // nothing to do - case networking.ClientTLSSettings_SIMPLE: - // TODO support this - case networking.ClientTLSSettings_MUTUAL: - // TODO support this - case networking.ClientTLSSettings_ISTIO_MUTUAL: - tlsCtx := buildUpstreamTLSContext(b.push.ServiceAccounts[b.hostname][b.portNum]) - c.TransportSocket = &core.TransportSocket{ - Name: transportSocketName, - ConfigType: &core.TransportSocket_TypedConfig{TypedConfig: util.MessageToAny(tlsCtx)}, - } - } -} - -// TransportSocket proto message has a `name` field which is expected to be set to exactly this value by the -// management server (see grpc/xds/internal/client/xds.go securityConfigFromCluster). -const transportSocketName = "envoy.transport_sockets.tls" - -func buildUpstreamTLSContext(sans []string) *tls.UpstreamTlsContext { - return &tls.UpstreamTlsContext{ - CommonTlsContext: buildCommonTLSContext(sans), - } -} diff --git a/pilot/pkg/networking/grpcgen/grpcecho_test.go b/pilot/pkg/networking/grpcgen/grpcecho_test.go deleted file mode 100644 index d1001d9c6..000000000 --- a/pilot/pkg/networking/grpcgen/grpcecho_test.go +++ /dev/null @@ -1,358 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package grpcgen_test - -import ( - "context" - "fmt" - "math" - "net" - "runtime" - "strconv" - "sync" - "testing" - "time" -) - -import ( - "google.golang.org/grpc" - _ "google.golang.org/grpc/xds" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/proto" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/server/endpoint" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -type echoCfg struct { - version string - namespace string - tls bool -} - -type configGenTest struct { - *testing.T - endpoints []endpoint.Instance - ds *xds.FakeDiscoveryServer - xdsPort int -} - -// newConfigGenTest creates a FakeDiscoveryServer that listens for gRPC on grpcXdsAddr -// For each of the given servers, we serve echo (only supporting Echo, no ForwardEcho) and -// create a corresponding WorkloadEntry. The WorkloadEntry will have the given format: -// -// meta: -// name: echo-{generated portnum}-{server.version} -// namespace: {server.namespace or "default"} -// labels: {"app": "grpc", "version": "{server.version}"} -// spec: -// address: {grpcEchoHost} -// ports: -// grpc: {generated portnum} -func newConfigGenTest(t *testing.T, discoveryOpts xds.FakeOptions, servers ...echoCfg) *configGenTest { - if runtime.GOOS == "darwin" && len(servers) > 1 { - // TODO always skip if this breaks anywhere else - t.Skip("cannot use 127.0.0.2-255 on OSX without manual setup") - } - - cgt := &configGenTest{T: t} - wg := sync.WaitGroup{} - cfgs := []config.Config{} - - discoveryOpts.ListenerBuilder = func() (net.Listener, error) { - return net.Listen("tcp", "127.0.0.1:0") - } - // Start XDS server - cgt.ds = xds.NewFakeDiscoveryServer(t, discoveryOpts) - _, xdsPorts, _ := net.SplitHostPort(cgt.ds.Listener.Addr().String()) - xdsPort, _ := strconv.Atoi(xdsPorts) - cgt.xdsPort = xdsPort - for i, s := range servers { - if s.namespace == "" { - s.namespace = "default" - } - // TODO this breaks without extra ifonfig aliases on OSX, and probably elsewhere - ip := fmt.Sprintf("127.0.0.%d", i+1) - - ep, err := endpoint.New(endpoint.Config{ - Port: &common.Port{ - Name: "grpc", - Port: 0, - Protocol: protocol.GRPC, - XDSServer: true, - XDSReadinessTLS: s.tls, - XDSTestBootstrap: GRPCBootstrap("echo-"+s.version, s.namespace, ip, xdsPort), - }, - ListenerIP: ip, - Version: s.version, - }) - if err != nil { - t.Fatal(err) - } - wg.Add(1) - if err := ep.Start(func() { - wg.Done() - }); err != nil { - t.Fatal(err) - } - - cfgs = append(cfgs, makeWE(s, ip, ep.GetConfig().Port.Port)) - cgt.endpoints = append(cgt.endpoints, ep) - t.Cleanup(func() { - if err := ep.Close(); err != nil { - t.Errorf("failed to close endpoint %s: %v", ip, err) - } - }) - } - for _, cfg := range cfgs { - if _, err := cgt.ds.Env().Create(cfg); err != nil { - t.Fatalf("failed to create config %v: %v", cfg.Name, err) - } - } - // we know onReady will get called because there are internal timeouts for this - wg.Wait() - return cgt -} - -func makeWE(s echoCfg, host string, port int) config.Config { - ns := "default" - if s.namespace != "" { - ns = s.namespace - } - return config.Config{ - Meta: config.Meta{ - Name: fmt.Sprintf("echo-%d-%s", port, s.version), - Namespace: ns, - GroupVersionKind: collections.IstioNetworkingV1Alpha3Workloadentries.Resource().GroupVersionKind(), - Labels: map[string]string{ - "app": "echo", - "version": s.version, - }, - }, - Spec: &networking.WorkloadEntry{ - Address: host, - Ports: map[string]uint32{"grpc": uint32(port)}, - }, - } -} - -func (t *configGenTest) dialEcho(addr string) *echo.Client { - resolver := resolverForTest(t, t.xdsPort, "default") - out, err := echo.New(addr, nil, grpc.WithResolvers(resolver)) - if err != nil { - t.Fatal(err) - } - return out -} - -func TestTrafficShifting(t *testing.T) { - tt := newConfigGenTest(t, xds.FakeOptions{ - KubernetesObjectString: ` -apiVersion: v1 -kind: Service -metadata: - labels: - app: echo-app - name: echo-app - namespace: default -spec: - clusterIP: 1.2.3.4 - selector: - app: echo - ports: - - name: grpc - targetPort: grpc - port: 7070 -`, - ConfigString: ` -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: echo-dr - namespace: default -spec: - host: echo-app.default.svc.cluster.local - subsets: - - name: v1 - labels: - version: v1 - - name: v2 - labels: - version: v2 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: echo-vs - namespace: default -spec: - hosts: - - echo-app.default.svc.cluster.local - http: - - route: - - destination: - host: echo-app.default.svc.cluster.local - subset: v1 - weight: 20 - - destination: - host: echo-app.default.svc.cluster.local - subset: v2 - weight: 80 - -`, - }, echoCfg{version: "v1"}, echoCfg{version: "v2"}) - - retry.UntilSuccessOrFail(tt.T, func() error { - cw := tt.dialEcho("xds:///echo-app.default.svc.cluster.local:7070") - distribution := map[string]int{} - for i := 0; i < 100; i++ { - res, err := cw.Echo(context.Background(), &proto.EchoRequest{Message: "needle"}) - if err != nil { - return err - } - distribution[res.Version]++ - } - - if err := expectAlmost(distribution["v1"], 20); err != nil { - return err - } - if err := expectAlmost(distribution["v2"], 80); err != nil { - return err - } - return nil - }, retry.Timeout(5*time.Second), retry.Delay(0)) -} - -func TestMtls(t *testing.T) { - tt := newConfigGenTest(t, xds.FakeOptions{ - KubernetesObjectString: ` -apiVersion: v1 -kind: Service -metadata: - labels: - app: echo-app - name: echo-app - namespace: default -spec: - clusterIP: 1.2.3.4 - selector: - app: echo - ports: - - name: grpc - targetPort: grpc - port: 7070 -`, - ConfigString: ` -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: echo-dr - namespace: default -spec: - host: echo-app.default.svc.cluster.local - trafficPolicy: - tls: - mode: ISTIO_MUTUAL ---- -apiVersion: security.istio.io/v1beta1 -kind: PeerAuthentication -metadata: - name: default - namespace: default -spec: - mtls: - mode: STRICT -`, - }, echoCfg{version: "v1", tls: true}) - - // ensure we can make 10 consecutive successful requests - retry.UntilSuccessOrFail(tt.T, func() error { - cw := tt.dialEcho("xds:///echo-app.default.svc.cluster.local:7070") - for i := 0; i < 10; i++ { - _, err := cw.Echo(context.Background(), &proto.EchoRequest{Message: "needle"}) - if err != nil { - return err - } - } - return nil - }, retry.Timeout(5*time.Second), retry.Delay(0)) -} - -func TestFault(t *testing.T) { - tt := newConfigGenTest(t, xds.FakeOptions{ - KubernetesObjectString: ` -apiVersion: v1 -kind: Service -metadata: - labels: - app: echo-app - name: echo-app - namespace: default -spec: - clusterIP: 1.2.3.4 - selector: - app: echo - ports: - - name: grpc - targetPort: grpc - port: 7071 -`, - ConfigString: ` -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: echo-delay -spec: - hosts: - - echo-app.default.svc.cluster.local - http: - - fault: - delay: - percent: 100 - fixedDelay: 100ms - route: - - destination: - host: echo-app.default.svc.cluster.local -`, - }, echoCfg{version: "v1"}) - c := tt.dialEcho("xds:///echo-app.default.svc.cluster.local:7071") - - // without a delay it usually takes ~500us - st := time.Now() - _, err := c.Echo(context.Background(), &proto.EchoRequest{}) - duration := time.Since(st) - if err != nil { - t.Fatal(err) - } - if duration < time.Millisecond*100 { - t.Fatalf("expected to take over 1s but took %v", duration) - } - - // TODO test timeouts, aborts -} - -func expectAlmost(got, want int) error { - if math.Abs(float64(want-got)) > 10 { - return fmt.Errorf("expected within %d of %d but got %d", 10, want, got) - } - return nil -} diff --git a/pilot/pkg/networking/grpcgen/grpcgen.go b/pilot/pkg/networking/grpcgen/grpcgen.go deleted file mode 100644 index 23401ee88..000000000 --- a/pilot/pkg/networking/grpcgen/grpcgen.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package grpcgen - -import ( - tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" - istiolog "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" -) - -// Support generation of 'ApiListener' LDS responses, used for native support of gRPC. -// The same response can also be used by other apps using XDS directly. -// GRPC proposal: -// https://github.com/grpc/proposal/blob/master/A27-xds-global-load-balancing.md -// -// Note that this implementation is tested against gRPC, but it is generic - any other framework can -// use this XDS mode to get load balancing info from Istio, including MC/VM/etc. -// The corresponding RDS response is also generated - currently gRPC has special differences -// and can't understand normal Istio RDS - in particular expects "" instead of "/" as -// default prefix, and is expects just the route for one host. -// handleAck will detect if the message is an ACK or NACK, and update/log/count -// using the generic structures. "Classical" CDS/LDS/RDS/EDS use separate logic - -// this is used for the API-based LDS and generic messages. -var log = istiolog.RegisterScope("grpcgen", "xDS Generator for Proxyless gRPC", 0) - -type GrpcConfigGenerator struct{} - -func clusterKey(hostname string, port int) string { - return subsetClusterKey("", hostname, port) -} - -func subsetClusterKey(subset, hostname string, port int) string { - return model.BuildSubsetKey(model.TrafficDirectionOutbound, subset, host.Name(hostname), port) -} - -func (g *GrpcConfigGenerator) Generate(proxy *model.Proxy, w *model.WatchedResource, req *model.PushRequest) (model.Resources, model.XdsLogDetails, error) { - switch w.TypeUrl { - case v3.ListenerType: - return g.BuildListeners(proxy, req.Push, w.ResourceNames), model.DefaultXdsLogDetails, nil - case v3.ClusterType: - return g.BuildClusters(proxy, req.Push, w.ResourceNames), model.DefaultXdsLogDetails, nil - case v3.RouteType: - return g.BuildHTTPRoutes(proxy, req.Push, w.ResourceNames), model.DefaultXdsLogDetails, nil - } - - return nil, model.DefaultXdsLogDetails, nil -} - -// buildCommonTLSContext creates a TLS context that assumes 'default' name, and credentials/tls/certprovider/pemfile -// (see grpc/xds/internal/client/xds.go securityConfigFromCluster). -func buildCommonTLSContext(sans []string) *tls.CommonTlsContext { - var sanMatch []*matcher.StringMatcher - if len(sans) > 0 { - sanMatch = util.StringToExactMatch(sans) - } - return &tls.CommonTlsContext{ - TlsCertificateCertificateProviderInstance: &tls.CommonTlsContext_CertificateProviderInstance{ - InstanceName: "default", - CertificateName: "default", - }, - ValidationContextType: &tls.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{ - ValidationContextCertificateProviderInstance: &tls.CommonTlsContext_CertificateProviderInstance{ - InstanceName: "default", - CertificateName: "ROOTCA", - }, - DefaultValidationContext: &tls.CertificateValidationContext{ - MatchSubjectAltNames: sanMatch, - }, - }, - }, - } -} diff --git a/pilot/pkg/networking/grpcgen/grpcgen_test.go b/pilot/pkg/networking/grpcgen/grpcgen_test.go deleted file mode 100644 index bd9051e48..000000000 --- a/pilot/pkg/networking/grpcgen/grpcgen_test.go +++ /dev/null @@ -1,472 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package grpcgen_test - -import ( - "context" - "encoding/json" - "fmt" - "net" - "net/url" - "path" - "strconv" - "testing" - "time" -) - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - xdscreds "google.golang.org/grpc/credentials/xds" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/resolver" - "google.golang.org/grpc/serviceconfig" - "google.golang.org/grpc/status" - xdsgrpc "google.golang.org/grpc/xds" - networking "istio.io/api/networking/v1alpha3" - security "istio.io/api/security/v1beta1" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/istio-agent/grpcxds" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" - echoproto "github.com/apache/dubbo-go-pixiu/pkg/test/echo/proto" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/server/endpoint" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" -) - -// Address of the Istiod gRPC service, used in tests. -var istiodSvcHost = "istiod.dubbo-system.svc.cluster.local" - -// Local integration tests for proxyless gRPC. -// The tests will start an in-process Istiod, using the memory store, and use -// proxyless grpc servers and clients to validate the config generation. -// GRPC project has more extensive tests for each language, we mainly verify that Istiod -// generates the expected XDS, and gRPC tests verify that the XDS is correctly interpreted. -// -// To debug, set GRPC_GO_LOG_SEVERITY_LEVEL=info;GRPC_GO_LOG_VERBOSITY_LEVEL=99 for -// verbose logs from gRPC side. - -// GRPCBootstrap creates the bootstrap bytes dynamically. -// This can be used with NewXDSResolverWithConfigForTesting, and used when creating clients. -// -// See pkg/istio-agent/testdata/grpc-bootstrap.json for a sample bootstrap as expected by Istio agent. -func GRPCBootstrap(app, namespace, ip string, xdsPort int) []byte { - if ip == "" { - ip = "127.0.0.1" - } - if namespace == "" { - namespace = "default" - } - if app == "" { - app = "app" - } - nodeID := "sidecar~" + ip + "~" + app + "." + namespace + "~" + namespace + ".svc.cluster.local" - bootstrap, err := grpcxds.GenerateBootstrap(grpcxds.GenerateBootstrapOptions{ - Node: &model.Node{ - ID: nodeID, - Metadata: &model.BootstrapNodeMetadata{ - NodeMetadata: model.NodeMetadata{ - Namespace: namespace, - Generator: "grpc", - ClusterID: "Kubernetes", - }, - }, - }, - DiscoveryAddress: fmt.Sprintf("127.0.0.1:%d", xdsPort), - CertDir: path.Join(env.IstioSrc, "tests/testdata/certs/default"), - }) - if err != nil { - return []byte{} - } - bootstrapBytes, err := json.Marshal(bootstrap) - if err != nil { - return []byte{} - } - return bootstrapBytes -} - -// resolverForTest creates a resolver for xds:// names using dynamic bootstrap. -func resolverForTest(t test.Failer, xdsPort int, ns string) resolver.Builder { - xdsresolver, err := xdsgrpc.NewXDSResolverWithConfigForTesting( - GRPCBootstrap("foo", ns, "10.0.0.1", xdsPort)) - if err != nil { - t.Fatal(err) - } - return xdsresolver -} - -func init() { - // Setup gRPC logging. Do it once in init to avoid races - o := log.DefaultOptions() - o.LogGrpc = true - log.Configure(o) -} - -func TestGRPC(t *testing.T) { - // Init Istiod in-process server. - ds := xds.NewXDS(make(chan struct{})) - sd := ds.DiscoveryServer.MemRegistry - sd.ClusterID = "Kubernetes" - - lis, err := net.Listen("tcp", ":0") - if err != nil { - t.Fatalf("net.Listen failed: %v", err) - } - lis.Addr() - _, ports, _ := net.SplitHostPort(lis.Addr().String()) - port, _ := strconv.Atoi(ports) - // Echo service - // initRBACTests(sd, store, "echo-rbac-plain", 14058, false) - initRBACTests(sd, ds.MemoryConfigStore, "echo-rbac-mtls", port, true) - - xdsAddr, err := ds.StartGRPC("127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - defer ds.GRPCListener.Close() - - _, xdsPorts, _ := net.SplitHostPort(xdsAddr) - xdsPort, _ := strconv.Atoi(xdsPorts) - - addIstiod(sd, xdsPort) - - env := ds.DiscoveryServer.Env - env.Init() - if err := env.PushContext.InitContext(env, env.PushContext, nil); err != nil { - t.Fatal(err) - } - ds.DiscoveryServer.UpdateServiceShards(env.PushContext) - - // Client bootstrap - will show as "foo.clientns" - xdsresolver := resolverForTest(t, xdsPort, "clientns") - - // Test the xdsresolver - query LDS and RDS for a specific service, wait for the update. - // Should be very fast (~20ms) and validate bootstrap and basic XDS connection. - // Unfortunately we have no way to look at the response except using the logs from XDS. - // This does not attempt to resolve CDS or EDS. - t.Run("gRPC-resolve", func(t *testing.T) { - rb := xdsresolver - stateCh := &Channel{ch: make(chan interface{}, 1)} - errorCh := &Channel{ch: make(chan interface{}, 1)} - _, err := rb.Build(resolver.Target{URL: url.URL{ - Scheme: "xds", - Path: "/" + net.JoinHostPort(istiodSvcHost, xdsPorts), - }}, - &testClientConn{stateCh: stateCh, errorCh: errorCh}, resolver.BuildOptions{}) - if err != nil { - t.Fatal("Failed to resolve XDS ", err) - } - tm := time.After(10 * time.Second) - select { - case s := <-stateCh.ch: - t.Log("Got state ", s) - case e := <-errorCh.ch: - t.Error("Error in resolve", e) - case <-tm: - t.Error("Didn't resolve in time") - } - }) - - t.Run("gRPC-svc", func(t *testing.T) { - t.Run("gRPC-svc-tls", func(t *testing.T) { - // Replaces: insecure.NewCredentials - creds, err := xdscreds.NewServerCredentials(xdscreds.ServerOptions{FallbackCreds: insecure.NewCredentials()}) - if err != nil { - t.Fatal(err) - } - - grpcOptions := []grpc.ServerOption{ - grpc.Creds(creds), - } - - bootstrapB := GRPCBootstrap("echo-rbac-mtls", "test", "127.0.1.1", xdsPort) - grpcOptions = append(grpcOptions, xdsgrpc.BootstrapContentsForTesting(bootstrapB)) - - // Replaces: grpc NewServer - grpcServer := xdsgrpc.NewGRPCServer(grpcOptions...) - - testRBAC(t, grpcServer, xdsresolver, "echo-rbac-mtls", port, lis) - }) - }) - - t.Run("gRPC-dial", func(t *testing.T) { - for _, host := range []string{ - "istiod.dubbo-system.svc.cluster.local", - //"istiod.dubbo-system.svc", - //"istiod.dubbo-system", - //"istiod", - } { - t.Run(host, func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) - defer cancel() - conn, err := grpc.DialContext(ctx, "xds:///"+host+":"+xdsPorts, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock(), - grpc.WithResolvers(xdsresolver)) - if err != nil { - t.Fatal("XDS gRPC", err) - } - defer conn.Close() - s, err := discovery.NewAggregatedDiscoveryServiceClient(conn).StreamAggregatedResources(ctx) - if err != nil { - t.Fatal(err) - } - _ = s.Send(&discovery.DiscoveryRequest{}) - }) - } - }) -} - -func addIstiod(sd *memory.ServiceDiscovery, xdsPort int) { - sd.AddService(&model.Service{ - Attributes: model.ServiceAttributes{ - Name: "istiod", - Namespace: "dubbo-system", - }, - Hostname: host.Name(istiodSvcHost), - DefaultAddress: "127.0.1.12", - Ports: model.PortList{ - { - Name: "grpc-main", - Port: xdsPort, - Protocol: protocol.GRPC, // SetEndpoints hardcodes this - }, - }, - }) - sd.SetEndpoints(istiodSvcHost, "dubbo-system", []*model.IstioEndpoint{ - { - Address: "127.0.0.1", - EndpointPort: uint32(xdsPort), - ServicePortName: "grpc-main", - }, - }) -} - -func initRBACTests(sd *memory.ServiceDiscovery, store model.ConfigStore, svcname string, port int, mtls bool) { - ns := "test" - hn := svcname + "." + ns + ".svc.cluster.local" - // The 'memory' store GetProxyServiceInstances uses the IP address of the node and endpoints to - // identify the service. In k8s store, labels are matched instead. - // For server configs to work, the server XDS bootstrap must match the IP. - sd.AddService(&model.Service{ - // Required: namespace (otherwise DR matching fails) - Attributes: model.ServiceAttributes{ - Name: svcname, - Namespace: ns, - }, - Hostname: host.Name(hn), - DefaultAddress: "127.0.5.1", - Ports: model.PortList{ - { - Name: "grpc-main", - Port: port, - Protocol: protocol.GRPC, - }, - }, - }) - // The address will be matched against the INSTANCE_IPS and id in the node id. If they match, the service is returned. - sd.SetEndpoints(hn, ns, []*model.IstioEndpoint{ - { - Address: "127.0.1.1", - EndpointPort: uint32(port), - ServicePortName: "grpc-main", - }, - }) - - store.Create(config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioSecurityV1Beta1Authorizationpolicies.Resource().GroupVersionKind(), - Name: svcname, - Namespace: ns, - }, - Spec: &security.AuthorizationPolicy{ - Rules: []*security.Rule{ - { - When: []*security.Condition{ - { - Key: "request.headers[echo]", - Values: []string{ - "block", - }, - }, - }, - }, - }, - Action: security.AuthorizationPolicy_DENY, - }, - }) - - store.Create(config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioSecurityV1Beta1Authorizationpolicies.Resource().GroupVersionKind(), - Name: svcname + "-allow", - Namespace: ns, - }, - Spec: &security.AuthorizationPolicy{ - Rules: []*security.Rule{ - { - When: []*security.Condition{ - { - Key: "request.headers[echo]", - Values: []string{ - "allow", - }, - }, - }, - }, - }, - Action: security.AuthorizationPolicy_ALLOW, - }, - }) - if mtls { - // Client side. - _, _ = store.Create(config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(), - Name: svcname, - Namespace: "test", - }, - Spec: &networking.DestinationRule{ - Host: svcname + ".test.svc.cluster.local", - TrafficPolicy: &networking.TrafficPolicy{Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_ISTIO_MUTUAL, - }}, - }, - }) - - // Server side. - _, _ = store.Create(config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioSecurityV1Beta1Peerauthentications.Resource().GroupVersionKind(), - Name: svcname, - Namespace: "test", - }, - Spec: &security.PeerAuthentication{ - Mtls: &security.PeerAuthentication_MutualTLS{Mode: security.PeerAuthentication_MutualTLS_STRICT}, - }, - }) - - _, _ = store.Create(config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioSecurityV1Beta1Authorizationpolicies.Resource().GroupVersionKind(), - Name: svcname, - Namespace: "test", - }, - Spec: &security.AuthorizationPolicy{ - Rules: []*security.Rule{ - { - From: []*security.Rule_From{ - { - Source: &security.Source{ - Principals: []string{"evie"}, - }, - }, - }, - }, - }, - Action: security.AuthorizationPolicy_DENY, - }, - }) - } -} - -func testRBAC(t *testing.T, grpcServer *xdsgrpc.GRPCServer, xdsresolver resolver.Builder, svcname string, port int, lis net.Listener) { - echos := &endpoint.EchoGrpcHandler{Config: endpoint.Config{Port: &common.Port{Port: port}}} - echoproto.RegisterEchoTestServiceServer(grpcServer, echos) - - go func() { - err := grpcServer.Serve(lis) - if err != nil { - log.Errora(err) - } - }() - time.Sleep(3 * time.Second) - ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) - defer cancel() - - creds, _ := xdscreds.NewClientCredentials(xdscreds.ClientOptions{ - FallbackCreds: insecure.NewCredentials(), - }) - - conn, err := grpc.DialContext(ctx, fmt.Sprintf("xds:///%s.test.svc.cluster.local:%d", svcname, port), - grpc.WithTransportCredentials(creds), - grpc.WithBlock(), - grpc.WithResolvers(xdsresolver)) - if err != nil { - t.Fatal("XDS gRPC", err) - } - defer conn.Close() - echoc := echoproto.NewEchoTestServiceClient(conn) - md := metadata.New(map[string]string{"echo": "block"}) - outctx := metadata.NewOutgoingContext(context.Background(), md) - _, err = echoc.Echo(outctx, &echoproto.EchoRequest{}) - if err == nil { - t.Fatal("RBAC rule not enforced") - } - if status.Code(err) != codes.PermissionDenied { - t.Fatal("Unexpected error", err) - } - t.Log(err) -} - -type Channel struct { - ch chan interface{} -} - -// Send sends value on the underlying channel. -func (c *Channel) Send(value interface{}) { - c.ch <- value -} - -// From xds_resolver_test -// testClientConn is a fake implemetation of resolver.ClientConn. All is does -// is to store the state received from the resolver locally and signal that -// event through a channel. -type testClientConn struct { - resolver.ClientConn - stateCh *Channel - errorCh *Channel -} - -func (t *testClientConn) UpdateState(s resolver.State) error { - t.stateCh.Send(s) - return nil -} - -func (t *testClientConn) ReportError(err error) { - t.errorCh.Send(err) -} - -func (t *testClientConn) ParseServiceConfig(jsonSC string) *serviceconfig.ParseResult { - // Will be called with something like: - // {"loadBalancingConfig":[ - // {"xds_cluster_manager_experimental":{ - // "children":{ - // "cluster:outbound|14057||istiod.dubbo-system.svc.cluster.local":{ - // "childPolicy":[ - // {"cds_experimental": - // {"cluster":"outbound|14057||istiod.dubbo-system.svc.cluster.local"}}]}}}}]} - return &serviceconfig.ParseResult{} -} diff --git a/pilot/pkg/networking/grpcgen/lds.go b/pilot/pkg/networking/grpcgen/lds.go deleted file mode 100644 index 579cb4b88..000000000 --- a/pilot/pkg/networking/grpcgen/lds.go +++ /dev/null @@ -1,422 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package grpcgen - -import ( - "fmt" - "net" - "strconv" - "strings" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - rbacpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3" - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - rbachttppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/rbac/v3" - hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - "istio.io/api/label" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/authn" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/authn/factory" - authzmodel "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/authz/model" - xdsfilters "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/filters" - "github.com/apache/dubbo-go-pixiu/pkg/istio-agent/grpcxds" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -var supportedFilters = []*hcm.HttpFilter{ - xdsfilters.Fault, - xdsfilters.Router, -} - -const ( - RBACHTTPFilterName = "envoy.filters.http.rbac" - RBACHTTPFilterNameDeny = "envoy.filters.http.rbac.DENY" -) - -// BuildListeners handles a LDS request, returning listeners of ApiListener type. -// The request may include a list of resource names, using the full_hostname[:port] format to select only -// specific services. -func (g *GrpcConfigGenerator) BuildListeners(node *model.Proxy, push *model.PushContext, names []string) model.Resources { - filter := newListenerNameFilter(names, node) - - log.Debugf("building lds for %s with filter:\n%v", node.ID, filter) - - resp := make(model.Resources, 0, len(filter)) - resp = append(resp, buildOutboundListeners(node, push, filter)...) - resp = append(resp, buildInboundListeners(node, push, filter.inboundNames())...) - - return resp -} - -func buildInboundListeners(node *model.Proxy, push *model.PushContext, names []string) model.Resources { - if len(names) == 0 { - return nil - } - var out model.Resources - policyApplier := factory.NewPolicyApplier(push, node.Metadata.Namespace, node.Metadata.Labels) - serviceInstancesByPort := map[uint32]*model.ServiceInstance{} - for _, si := range node.ServiceInstances { - serviceInstancesByPort[si.Endpoint.EndpointPort] = si - } - - for _, name := range names { - listenAddress := strings.TrimPrefix(name, grpcxds.ServerListenerNamePrefix) - listenHost, listenPortStr, err := net.SplitHostPort(listenAddress) - if err != nil { - log.Errorf("failed parsing address from gRPC listener name %s: %v", name, err) - continue - } - listenPort, err := strconv.Atoi(listenPortStr) - if err != nil { - log.Errorf("failed parsing port from gRPC listener name %s: %v", name, err) - continue - } - si, ok := serviceInstancesByPort[uint32(listenPort)] - if !ok { - log.Warnf("%s has no service instance for port %s", node.ID, listenPortStr) - continue - } - - ll := &listener.Listener{ - Name: name, - Address: &core.Address{Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - Address: listenHost, - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: uint32(listenPort), - }, - }, - }}, - FilterChains: buildInboundFilterChains(node, push, si, policyApplier), - // the following must not be set or the client will NACK - ListenerFilters: nil, - UseOriginalDst: nil, - } - out = append(out, &discovery.Resource{ - Name: ll.Name, - Resource: util.MessageToAny(ll), - }) - } - return out -} - -// nolint: unparam -func buildInboundFilterChains(node *model.Proxy, push *model.PushContext, si *model.ServiceInstance, applier authn.PolicyApplier) []*listener.FilterChain { - mode := applier.GetMutualTLSModeForPort(si.Endpoint.EndpointPort) - - // auto-mtls label is set - clients will attempt to connect using mtls, and - // gRPC doesn't support permissive. - if node.Metadata.Labels[label.SecurityTlsMode.Name] == "istio" && mode == model.MTLSPermissive { - mode = model.MTLSStrict - } - - var tlsContext *tls.DownstreamTlsContext - if mode != model.MTLSDisable && mode != model.MTLSUnknown { - tlsContext = &tls.DownstreamTlsContext{ - CommonTlsContext: buildCommonTLSContext(nil), - // TODO match_subject_alt_names field in validation context is not supported on the server - // CommonTlsContext: buildCommonTLSContext(authnplugin.TrustDomainsForValidation(push.Mesh)), - // TODO plain TLS support - RequireClientCertificate: &wrappers.BoolValue{Value: true}, - } - } - - if mode == model.MTLSUnknown { - log.Warnf("could not find mTLS mode for %s on %s; defaulting to DISABLE", si.Service.Hostname, node.ID) - mode = model.MTLSDisable - } - if mode == model.MTLSPermissive { - // TODO gRPC's filter chain match is super limted - only effective transport_protocol match is "raw_buffer" - // see https://github.com/grpc/proposal/blob/master/A36-xds-for-servers.md for detail - // No need to warn on each push - the behavior is still consistent with auto-mtls, which is the - // replacement for permissive. - mode = model.MTLSDisable - } - - var out []*listener.FilterChain - switch mode { - case model.MTLSDisable: - out = append(out, buildInboundFilterChain(node, push, "plaintext", nil)) - case model.MTLSStrict: - out = append(out, buildInboundFilterChain(node, push, "mtls", tlsContext)) - // TODO permissive builts both plaintext and mtls; when tlsContext is present add a match for protocol - } - - return out -} - -func buildInboundFilterChain(node *model.Proxy, push *model.PushContext, nameSuffix string, tlsContext *tls.DownstreamTlsContext) *listener.FilterChain { - fc := []*hcm.HttpFilter{} - // See security/authz/builder and grpc internal/xds/rbac - // grpc supports ALLOW and DENY actions (fail if it is not one of them), so we can't use the normal generator - policies := push.AuthzPolicies.ListAuthorizationPolicies(node.ConfigNamespace, node.Metadata.Labels) - if len(policies.Deny)+len(policies.Allow) > 0 { - rules := buildRBAC(node, push, nameSuffix, tlsContext, rbacpb.RBAC_DENY, policies.Deny) - if rules != nil && len(rules.Policies) > 0 { - rbac := &rbachttppb.RBAC{ - Rules: rules, - } - fc = append(fc, - &hcm.HttpFilter{ - Name: RBACHTTPFilterNameDeny, - ConfigType: &hcm.HttpFilter_TypedConfig{TypedConfig: util.MessageToAny(rbac)}, - }) - } - arules := buildRBAC(node, push, nameSuffix, tlsContext, rbacpb.RBAC_ALLOW, policies.Allow) - if arules != nil && len(arules.Policies) > 0 { - rbac := &rbachttppb.RBAC{ - Rules: arules, - } - fc = append(fc, - &hcm.HttpFilter{ - Name: RBACHTTPFilterName, - ConfigType: &hcm.HttpFilter_TypedConfig{TypedConfig: util.MessageToAny(rbac)}, - }) - } - } - - // Must be last - fc = append(fc, xdsfilters.Router) - - out := &listener.FilterChain{ - Name: "inbound-" + nameSuffix, - FilterChainMatch: nil, - Filters: []*listener.Filter{{ - Name: "inbound-hcm" + nameSuffix, - ConfigType: &listener.Filter_TypedConfig{ - TypedConfig: util.MessageToAny(&hcm.HttpConnectionManager{ - RouteSpecifier: &hcm.HttpConnectionManager_RouteConfig{ - // https://github.com/grpc/grpc-go/issues/4924 - RouteConfig: &route.RouteConfiguration{ - Name: "inbound", - VirtualHosts: []*route.VirtualHost{{ - Domains: []string{"*"}, - Routes: []*route.Route{{ - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_Prefix{Prefix: "/"}, - }, - Action: &route.Route_NonForwardingAction{}, - }}, - }}, - }, - }, - HttpFilters: fc, - }), - }, - }}, - } - if tlsContext != nil { - out.TransportSocket = &core.TransportSocket{ - Name: transportSocketName, - ConfigType: &core.TransportSocket_TypedConfig{TypedConfig: util.MessageToAny(tlsContext)}, - } - } - return out -} - -// buildRBAC builds the RBAC config expected by gRPC. -// -// See: xds/interal/httpfilter/rbac -// -// TODO: gRPC also supports 'per route override' - not yet clear how to use it, Istio uses path expressions instead and we don't generate -// vhosts or routes for the inbound listener. -// -// For gateways it would make a lot of sense to use this concept, same for moving path prefix at top level ( more scalable, easier for users) -// This should probably be done for the v2 API. -// -// nolint: unparam -func buildRBAC(node *model.Proxy, push *model.PushContext, suffix string, context *tls.DownstreamTlsContext, - a rbacpb.RBAC_Action, policies []model.AuthorizationPolicy) *rbacpb.RBAC { - rules := &rbacpb.RBAC{ - Action: a, - Policies: map[string]*rbacpb.Policy{}, - } - for _, policy := range policies { - for i, rule := range policy.Spec.Rules { - name := fmt.Sprintf("%s-%s-%d", policy.Namespace, policy.Name, i) - m, err := authzmodel.New(rule) - if err != nil { - log.Warn("Invalid rule ", rule, err) - } - generated, _ := m.Generate(false, a) - rules.Policies[name] = generated - } - } - - return rules -} - -// nolint: unparam -func buildOutboundListeners(node *model.Proxy, push *model.PushContext, filter listenerNames) model.Resources { - out := make(model.Resources, 0, len(filter)) - for _, sv := range node.SidecarScope.Services() { - serviceHost := string(sv.Hostname) - match, ok := filter.includes(serviceHost) - if !ok { - continue - } - // we must duplicate the listener for every requested host - grpc may have watches for both foo and foo.ns - for _, matchedHost := range match.RequestedNames.SortedList() { - for _, p := range sv.Ports { - sPort := strconv.Itoa(p.Port) - if !match.includesPort(sPort) { - continue - } - ll := &listener.Listener{ - Name: net.JoinHostPort(matchedHost, sPort), - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - Address: sv.GetAddressForProxy(node), - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: uint32(p.Port), - }, - }, - }, - }, - ApiListener: &listener.ApiListener{ - ApiListener: util.MessageToAny(&hcm.HttpConnectionManager{ - HttpFilters: supportedFilters, - RouteSpecifier: &hcm.HttpConnectionManager_Rds{ - // TODO: for TCP listeners don't generate RDS, but some indication of cluster name. - Rds: &hcm.Rds{ - ConfigSource: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_Ads{ - Ads: &core.AggregatedConfigSource{}, - }, - }, - RouteConfigName: clusterKey(serviceHost, p.Port), - }, - }, - }), - }, - } - out = append(out, &discovery.Resource{ - Name: ll.Name, - Resource: util.MessageToAny(ll), - }) - } - } - } - return out -} - -// map[host] -> map[port] -> exists -// if the map[port] is empty, an exact listener name was provided (non-hostport) -type listenerNames map[string]listenerName - -type listenerName struct { - RequestedNames sets.Set - Ports sets.Set -} - -func (ln *listenerName) includesPort(port string) bool { - if len(ln.Ports) == 0 { - return true - } - _, ok := ln.Ports[port] - return ok -} - -func (f listenerNames) includes(s string) (listenerName, bool) { - if len(f) == 0 { - // filter is empty, include everything - return listenerName{RequestedNames: sets.New(s)}, true - } - n, ok := f[s] - return n, ok -} - -func (f listenerNames) inboundNames() []string { - var out []string - for key := range f { - if strings.HasPrefix(key, grpcxds.ServerListenerNamePrefix) { - out = append(out, key) - } - } - return out -} - -func newListenerNameFilter(names []string, node *model.Proxy) listenerNames { - filter := make(listenerNames, len(names)) - for _, name := range names { - // inbound, create a simple entry and move on - if strings.HasPrefix(name, grpcxds.ServerListenerNamePrefix) { - filter[name] = listenerName{RequestedNames: sets.New(name)} - continue - } - - host, port, err := net.SplitHostPort(name) - hasPort := err == nil - - // attempt to expand shortname to FQDN - requestedName := name - if hasPort { - requestedName = host - } - allNames := []string{requestedName} - if fqdn := tryFindFQDN(requestedName, node); fqdn != "" { - allNames = append(allNames, fqdn) - } - - for _, name := range allNames { - ln, ok := filter[name] - if !ok { - ln = listenerName{RequestedNames: sets.New()} - } - ln.RequestedNames.Insert(requestedName) - - // only build the portmap if we aren't filtering this name yet, or if the existing filter is non-empty - if hasPort && (!ok || len(ln.Ports) != 0) { - if ln.Ports == nil { - ln.Ports = map[string]struct{}{} - } - ln.Ports.Insert(port) - } else if !hasPort { - // if we didn't have a port, we should clear the portmap - ln.Ports = nil - } - filter[name] = ln - } - } - return filter -} - -func tryFindFQDN(name string, node *model.Proxy) string { - // no "." - assuming this is a shortname "foo" -> "foo.ns.svc.cluster.local" - if !strings.Contains(name, ".") { - return fmt.Sprintf("%s.%s", name, node.DNSDomain) - } - for _, suffix := range []string{ - node.Metadata.Namespace, - node.Metadata.Namespace + ".svc", - } { - shortname := strings.TrimSuffix(name, "."+suffix) - if shortname != name && strings.HasPrefix(node.DNSDomain, suffix) { - return fmt.Sprintf("%s.%s", shortname, node.DNSDomain) - } - } - return "" -} diff --git a/pilot/pkg/networking/grpcgen/lds_test.go b/pilot/pkg/networking/grpcgen/lds_test.go deleted file mode 100644 index dc7c19a6f..000000000 --- a/pilot/pkg/networking/grpcgen/lds_test.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package grpcgen - -import ( - "fmt" - "sort" - "testing" -) - -import ( - "github.com/google/go-cmp/cmp" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/istio-agent/grpcxds" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -var node = &model.Proxy{DNSDomain: "ns.svc.cluster.local", Metadata: &model.NodeMetadata{Namespace: "ns"}} - -func TestListenerNameFilter(t *testing.T) { - cases := map[string]struct { - in []string - want listenerNames - wantInbound []string - }{ - "simple": { - in: []string{"foo.com:80", "foo.com:443", "wildcard.com"}, - want: listenerNames{ - "foo.com": { - RequestedNames: sets.New("foo.com"), - Ports: sets.New("80", "443"), - }, - "wildcard.com": {RequestedNames: sets.New("wildcard.com")}, - }, - }, - "plain-host clears port-map": { - in: []string{"foo.com:80", "foo.com"}, - want: listenerNames{"foo.com": {RequestedNames: sets.New("foo.com")}}, - }, - "port-map stays clear": { - in: []string{"foo.com:80", "foo.com", "foo.com:443"}, - want: listenerNames{"foo.com": { - RequestedNames: sets.New("foo.com"), - }}, - }, - "special listeners preserved exactly": { - in: []string{ - "foo.com:80", - fmt.Sprintf(grpcxds.ServerListenerNameTemplate, "foo:1234"), - fmt.Sprintf(grpcxds.ServerListenerNameTemplate, "foo"), - fmt.Sprintf(grpcxds.ServerListenerNameTemplate, "[::]:8076"), - }, - want: listenerNames{ - "foo.com": { - RequestedNames: sets.New("foo.com"), - Ports: sets.New("80"), - }, - fmt.Sprintf(grpcxds.ServerListenerNameTemplate, "foo:1234"): { - RequestedNames: sets.New(fmt.Sprintf(grpcxds.ServerListenerNameTemplate, "foo:1234")), - }, - fmt.Sprintf(grpcxds.ServerListenerNameTemplate, "foo"): { - RequestedNames: sets.New(fmt.Sprintf(grpcxds.ServerListenerNameTemplate, "foo")), - }, - fmt.Sprintf(grpcxds.ServerListenerNameTemplate, "[::]:8076"): { - RequestedNames: sets.New(fmt.Sprintf(grpcxds.ServerListenerNameTemplate, "[::]:8076")), - }, - }, - wantInbound: []string{ - fmt.Sprintf(grpcxds.ServerListenerNameTemplate, "foo:1234"), - fmt.Sprintf(grpcxds.ServerListenerNameTemplate, "foo"), - fmt.Sprintf(grpcxds.ServerListenerNameTemplate, "[::]:8076"), - }, - }, - "expand shortnames": { - in: []string{ - "bar", - "bar.ns", - "bar.ns.svc", - "bar.ns.svc.cluster.local", - "foo:80", - "foo.ns:81", - "foo.ns.svc:82", - "foo.ns.svc.cluster.local:83", - }, - want: listenerNames{ - "bar": {RequestedNames: sets.New("bar")}, - "bar.ns": {RequestedNames: sets.New("bar.ns")}, - "bar.ns.svc": {RequestedNames: sets.New("bar.ns.svc")}, - "bar.ns.svc.cluster.local": {RequestedNames: sets.New( - "bar", - "bar.ns", - "bar.ns.svc", - "bar.ns.svc.cluster.local", - )}, - "foo": {RequestedNames: sets.New("foo"), Ports: sets.New("80")}, - "foo.ns": {RequestedNames: sets.New("foo.ns"), Ports: sets.New("81")}, - "foo.ns.svc": {RequestedNames: sets.New("foo.ns.svc"), Ports: sets.New("82")}, - "foo.ns.svc.cluster.local": { - RequestedNames: sets.New( - "foo", - "foo.ns", - "foo.ns.svc", - "foo.ns.svc.cluster.local", - ), - Ports: sets.New("80", "81", "82", "83"), - }, - }, - }, - } - for name, tt := range cases { - t.Run(name, func(t *testing.T) { - got := newListenerNameFilter(tt.in, node) - if diff := cmp.Diff(got, tt.want); diff != "" { - t.Fatal(diff) - } - gotInbound := got.inboundNames() - sort.Strings(gotInbound) - sort.Strings(tt.wantInbound) - if diff := cmp.Diff(gotInbound, tt.wantInbound); diff != "" { - t.Fatalf(diff) - } - }) - } -} - -func TestTryFindFQDN(t *testing.T) { - cases := []struct { - in string - want string - }{ - {"foo", "foo.ns.svc.cluster.local"}, - {"foo.ns", "foo.ns.svc.cluster.local"}, - {"foo.ns.svc", "foo.ns.svc.cluster.local"}, - {"foo.ns.svc.cluster.local", ""}, - {"foo.com", ""}, - {"foo.ns.com", ""}, - {"foo.ns.svc.notdnsdomain", ""}, - {"foo.ns.svc.cluster.local.extra", ""}, - {"xds.istio.io/grpc/lds/inbound/0.0.0.0:1234", ""}, - } - - for _, tc := range cases { - t.Run(tc.in, func(t *testing.T) { - if got := tryFindFQDN(tc.in, node); got != tc.want { - t.Errorf("want %q but got %q", tc.want, got) - } - }) - } -} diff --git a/pilot/pkg/networking/grpcgen/rds.go b/pilot/pkg/networking/grpcgen/rds.go deleted file mode 100644 index 2ff673932..000000000 --- a/pilot/pkg/networking/grpcgen/rds.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package grpcgen - -import ( - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" -) - -// BuildHTTPRoutes supports per-VIP routes, as used by GRPC. -// This mode is indicated by using names containing full host:port instead of just port. -// Returns true of the request is of this type. -func (g *GrpcConfigGenerator) BuildHTTPRoutes(node *model.Proxy, push *model.PushContext, routeNames []string) model.Resources { - resp := model.Resources{} - for _, routeName := range routeNames { - if rc := buildHTTPRoute(node, push, routeName); rc != nil { - resp = append(resp, &discovery.Resource{ - Name: routeName, - Resource: util.MessageToAny(rc), - }) - } - } - return resp -} - -func buildHTTPRoute(node *model.Proxy, push *model.PushContext, routeName string) *route.RouteConfiguration { - // TODO use route-style naming instead of cluster naming - _, _, hostname, port := model.ParseSubsetKey(routeName) - if hostname == "" || port == 0 { - log.Warn("Failed to parse ", routeName) - return nil - } - - virtualHosts, _, _ := v1alpha3.BuildSidecarOutboundVirtualHosts(node, push, routeName, port, nil, &model.DisabledCache{}) - - // Only generate the required route for grpc. Will need to generate more - // as GRPC adds more features. - return &route.RouteConfiguration{ - Name: routeName, - VirtualHosts: virtualHosts, - } -} diff --git a/pilot/pkg/networking/grpcgen/testdata/xds_bootstrap.json b/pilot/pkg/networking/grpcgen/testdata/xds_bootstrap.json deleted file mode 100644 index afcd82a0d..000000000 --- a/pilot/pkg/networking/grpcgen/testdata/xds_bootstrap.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "xds_servers": [ - { - "server_uri": "localhost:14057", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "sidecar~127.0.1.1~echo.test~cluster.local", - "metadata": { - "INSTANCE_IPS": "127.0.1.1", - "PILOT_SAN": [ - "istiod.dubbo-system.svc" - ], - "GENERATOR": "grpc", - "NAMESPACE": "test" - }, - "localisty": {}, - "UserAgentVersionType": "istiov1" - }, - "certificate_providers": { - "default": { - "plugin_name": "file_watcher", - "config": { - "certificate_file": "../../../../tests/testdata/certs/default/cert-chain.pem", - "private_key_file": "../../../../tests/testdata/certs/default/key.pem", - "ca_certificate_file": "../../../../tests/testdata/certs/default/root-cert.pem", - "refresh_interval": "900s" - } - } - }, - "server_listener_resource_name_template": "xds.istio.io/grpc/lds/inbound/%s" -} diff --git a/pilot/pkg/networking/networking.go b/pilot/pkg/networking/networking.go deleted file mode 100644 index 8d4dae9bd..000000000 --- a/pilot/pkg/networking/networking.go +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package networking - -import ( - "fmt" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - http_conn "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - "google.golang.org/protobuf/encoding/prototext" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/anypb" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" -) - -// ListenerProtocol is the protocol associated with the listener. -type ListenerProtocol int - -const ( - // ListenerProtocolUnknown is an unknown type of listener. - ListenerProtocolUnknown = iota - // ListenerProtocolTCP is a TCP listener. - ListenerProtocolTCP - // ListenerProtocolHTTP is an HTTP listener. - ListenerProtocolHTTP - // ListenerProtocolAuto enables auto protocol detection - ListenerProtocolAuto -) - -// ModelProtocolToListenerProtocol converts from a config.Protocol to its corresponding plugin.ListenerProtocol -func ModelProtocolToListenerProtocol(p protocol.Instance, - trafficDirection core.TrafficDirection) ListenerProtocol { - switch p { - case protocol.HTTP, protocol.HTTP2, protocol.HTTP_PROXY, protocol.GRPC, protocol.GRPCWeb: - return ListenerProtocolHTTP - case protocol.TCP, protocol.HTTPS, protocol.TLS, - protocol.Mongo, protocol.Redis, protocol.MySQL: - return ListenerProtocolTCP - case protocol.UDP: - return ListenerProtocolUnknown - case protocol.Unsupported: - // If protocol sniffing is not enabled, the default value is TCP - switch trafficDirection { - case core.TrafficDirection_INBOUND: - if !features.EnableProtocolSniffingForInbound { - return ListenerProtocolTCP - } - case core.TrafficDirection_OUTBOUND: - if !features.EnableProtocolSniffingForOutbound { - return ListenerProtocolTCP - } - default: - // Should not reach here. - } - return ListenerProtocolAuto - default: - // Should not reach here. - return ListenerProtocolAuto - } -} - -type TransportProtocol uint8 - -const ( - // TransportProtocolTCP is a TCP listener - TransportProtocolTCP = iota - // TransportProtocolQUIC is a QUIC listener - TransportProtocolQUIC -) - -func (tp TransportProtocol) String() string { - switch tp { - case TransportProtocolTCP: - return "tcp" - case TransportProtocolQUIC: - return "quic" - } - return "unknown" -} - -func (tp TransportProtocol) ToEnvoySocketProtocol() core.SocketAddress_Protocol { - if tp == TransportProtocolQUIC { - return core.SocketAddress_UDP - } - return core.SocketAddress_TCP -} - -// FilterChain describes a set of filters (HTTP or TCP) with a shared TLS context. -type FilterChain struct { - // FilterChainMatch is the match used to select the filter chain. - FilterChainMatch *listener.FilterChainMatch - // TLSContext is the TLS settings for this filter chains. - TLSContext *tls.DownstreamTlsContext - // ListenerProtocol indicates whether this filter chain is for HTTP or TCP - // Note that HTTP filter chains can also have network filters - ListenerProtocol ListenerProtocol - // TransportProtocol indicates the type of transport used - TCP, UDP, QUIC - // This would be TCP by default - TransportProtocol TransportProtocol - - // HTTP is the set of HTTP filters for this filter chain - HTTP []*http_conn.HttpFilter - // TCP is the set of network (TCP) filters for this filter chain. - TCP []*listener.Filter -} - -// MutableObjects is a set of objects passed to On*Listener callbacks. Fields may be nil or empty. -// Any lists should not be overridden, but rather only appended to. -// Non-list fields may be mutated; however it's not recommended to do this since it can affect other plugins in the -// chain in unpredictable ways. -// TODO: do we need this now? -type MutableObjects struct { - // Listener is the listener being built. Must be initialized before Plugin methods are called. - Listener *listener.Listener - - // FilterChains is the set of filter chains that will be attached to Listener. - FilterChains []FilterChain -} - -const ( - NoTunnelTypeName = "notunnel" - H2TunnelTypeName = "H2Tunnel" -) - -type ( - TunnelType int - TunnelAbility int -) - -const ( - // Bind the no tunnel support to a name. - NoTunnel TunnelType = 0 - // Enumeration of tunnel type below. Each type should own a unique bit field. - H2Tunnel TunnelType = 1 << 0 -) - -func MakeTunnelAbility(ttypes ...TunnelType) TunnelAbility { - ability := int(NoTunnel) - for _, tunnelType := range ttypes { - ability |= int(tunnelType) - } - return TunnelAbility(ability) -} - -func (t TunnelType) ToString() string { - switch t { - case H2Tunnel: - return H2TunnelTypeName - default: - return NoTunnelTypeName - } -} - -func (t TunnelAbility) SupportH2Tunnel() bool { - return (int(t) & int(H2Tunnel)) != 0 -} - -// ListenerClass defines the class of the listener -type ListenerClass int - -const ( - ListenerClassUndefined ListenerClass = iota - ListenerClassSidecarInbound - ListenerClassSidecarOutbound - ListenerClassGateway -) - -// MessageToAnyWithError converts from proto message to proto Any -func MessageToAnyWithError(msg proto.Message) (*anypb.Any, error) { - b, err := proto.MarshalOptions{Deterministic: true}.Marshal(msg) - if err != nil { - return nil, err - } - return &anypb.Any{ - // nolint: staticcheck - TypeUrl: "type.googleapis.com/" + string(proto.MessageName(msg)), - Value: b, - }, nil -} - -// MessageToAny converts from proto message to proto Any -func MessageToAny(msg proto.Message) *anypb.Any { - out, err := MessageToAnyWithError(msg) - if err != nil { - log.Error(fmt.Sprintf("error marshaling Any %s: %v", prototext.Format(msg), err)) - return nil - } - return out -} diff --git a/pilot/pkg/networking/networking_test.go b/pilot/pkg/networking/networking_test.go deleted file mode 100644 index ab9a0404f..000000000 --- a/pilot/pkg/networking/networking_test.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package networking - -import ( - "testing" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -func TestModelProtocolToListenerProtocol(t *testing.T) { - tests := []struct { - name string - protocol protocol.Instance - direction core.TrafficDirection - sniffingEnabledForInbound bool - sniffingEnabledForOutbound bool - want ListenerProtocol - }{ - { - "TCP to TCP", - protocol.TCP, - core.TrafficDirection_INBOUND, - true, - true, - ListenerProtocolTCP, - }, - { - "HTTP to HTTP", - protocol.HTTP, - core.TrafficDirection_INBOUND, - true, - true, - ListenerProtocolHTTP, - }, - { - "HTTP to HTTP", - protocol.HTTP_PROXY, - core.TrafficDirection_OUTBOUND, - true, - true, - ListenerProtocolHTTP, - }, - { - "MySQL to TCP", - protocol.MySQL, - core.TrafficDirection_INBOUND, - true, - true, - ListenerProtocolTCP, - }, - { - "Inbound unknown to Auto", - protocol.Unsupported, - core.TrafficDirection_INBOUND, - true, - true, - ListenerProtocolAuto, - }, - { - "Outbound unknown to Auto", - protocol.Unsupported, - core.TrafficDirection_OUTBOUND, - true, - true, - ListenerProtocolAuto, - }, - { - "Inbound unknown to TCP", - protocol.Unsupported, - core.TrafficDirection_INBOUND, - false, - true, - ListenerProtocolTCP, - }, - { - "Outbound unknown to Auto (disable sniffing for inbound)", - protocol.Unsupported, - core.TrafficDirection_OUTBOUND, - false, - true, - ListenerProtocolAuto, - }, - { - "Inbound unknown to Auto (disable sniffing for outbound)", - protocol.Unsupported, - core.TrafficDirection_INBOUND, - true, - false, - ListenerProtocolAuto, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - test.SetBoolForTest(t, &features.EnableProtocolSniffingForOutbound, tt.sniffingEnabledForOutbound) - test.SetBoolForTest(t, &features.EnableProtocolSniffingForInbound, tt.sniffingEnabledForInbound) - if got := ModelProtocolToListenerProtocol(tt.protocol, tt.direction); got != tt.want { - t.Errorf("ModelProtocolToListenerProtocol() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/pilot/pkg/networking/plugin/authn/authentication.go b/pilot/pkg/networking/plugin/authn/authentication.go deleted file mode 100644 index 0a1f44018..000000000 --- a/pilot/pkg/networking/plugin/authn/authentication.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package authn - -import ( - httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/plugin" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/authn" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/authn/factory" -) - -var authnLog = log.RegisterScope("authn", "authn debugging", 0) - -type Builder struct { - applier authn.PolicyApplier - trustDomains []string - proxy *model.Proxy -} - -func NewBuilder(push *model.PushContext, proxy *model.Proxy) *Builder { - applier := factory.NewPolicyApplier(push, proxy.Metadata.Namespace, proxy.Metadata.Labels) - trustDomains := TrustDomainsForValidation(push.Mesh) - return &Builder{ - applier: applier, - proxy: proxy, - trustDomains: trustDomains, - } -} - -func (b *Builder) ForPort(port uint32) plugin.MTLSSettings { - if b == nil { - return plugin.MTLSSettings{ - Port: port, - Mode: model.MTLSDisable, - } - } - return b.applier.InboundMTLSSettings(port, b.proxy, b.trustDomains) -} - -func (b *Builder) ForPassthrough() []plugin.MTLSSettings { - if b == nil { - return []plugin.MTLSSettings{{ - Port: 0, - Mode: model.MTLSDisable, - }} - } - // We need to create configuration for the passthrough, - // but also any ports that are not explicitly declared in the Service but are in the mTLS port level settings. - - resp := []plugin.MTLSSettings{ - // Full passthrough - no port match - b.applier.InboundMTLSSettings(0, b.proxy, b.trustDomains), - } - - // Then generate the per-port passthrough filter chains. - for port := range b.applier.PortLevelSetting() { - // Skip the per-port passthrough filterchain if the port is already handled by InboundMTLSConfiguration(). - if !needPerPortPassthroughFilterChain(port, b.proxy) { - continue - } - - authnLog.Debugf("InboundMTLSConfiguration: build extra pass through filter chain for %v:%d", b.proxy.ID, port) - resp = append(resp, b.applier.InboundMTLSSettings(port, b.proxy, b.trustDomains)) - } - return resp -} - -func (b *Builder) BuildHTTP(class networking.ListenerClass) []*httppb.HttpFilter { - if b == nil { - return nil - } - if class == networking.ListenerClassSidecarOutbound { - // Only applies to inbound and gateways - return nil - } - res := []*httppb.HttpFilter{} - if filter := b.applier.JwtFilter(); filter != nil { - res = append(res, filter) - } - forSidecar := b.proxy.Type == model.SidecarProxy - if filter := b.applier.AuthNFilter(forSidecar); filter != nil { - res = append(res, filter) - } - - return res -} - -func needPerPortPassthroughFilterChain(port uint32, node *model.Proxy) bool { - // If there is any Sidecar defined, check if the port is explicitly defined there. - // This means the Sidecar resource takes precedence over the service. A port defined in service but not in Sidecar - // means the port is going to be handled by the pass through filter chain. - if node.SidecarScope.HasIngressListener() { - for _, ingressListener := range node.SidecarScope.Sidecar.Ingress { - if port == ingressListener.Port.Number { - return false - } - } - return true - } - - // If there is no Sidecar, check if the port is appearing in any service. - for _, si := range node.ServiceInstances { - if port == si.Endpoint.EndpointPort { - return false - } - } - return true -} diff --git a/pilot/pkg/networking/plugin/authn/util.go b/pilot/pkg/networking/plugin/authn/util.go deleted file mode 100644 index 5c7ac23b5..000000000 --- a/pilot/pkg/networking/plugin/authn/util.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package authn - -import ( - meshconfig "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -func TrustDomainsForValidation(meshConfig *meshconfig.MeshConfig) []string { - if features.SkipValidateTrustDomain { - return nil - } - - tds := append([]string{meshConfig.TrustDomain}, meshConfig.TrustDomainAliases...) - return dedupTrustDomains(tds) -} - -func dedupTrustDomains(tds []string) []string { - known := sets.New() - deduped := make([]string, 0, len(tds)) - - for _, td := range tds { - if td != "" && !known.Contains(td) { - known.Insert(td) - deduped = append(deduped, td) - } - } - return deduped -} diff --git a/pilot/pkg/networking/plugin/authn/util_test.go b/pilot/pkg/networking/plugin/authn/util_test.go deleted file mode 100644 index 4a0053c30..000000000 --- a/pilot/pkg/networking/plugin/authn/util_test.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package authn - -import ( - "reflect" - "testing" -) - -import ( - meshconfig "istio.io/api/mesh/v1alpha1" -) - -func TestTrustDomainsForValidation(t *testing.T) { - tests := []struct { - name string - meshConfig *meshconfig.MeshConfig - want []string - }{ - { - name: "No duplicated trust domain in mesh config", - meshConfig: &meshconfig.MeshConfig{ - TrustDomain: "cluster.local", - TrustDomainAliases: []string{"alias-1.domain", "some-other-alias-1.domain", "alias-2.domain"}, - }, - want: []string{"cluster.local", "alias-1.domain", "some-other-alias-1.domain", "alias-2.domain"}, - }, - { - name: "Empty mesh config", - meshConfig: &meshconfig.MeshConfig{}, - want: []string{}, - }, - { - name: "Sequential duplicated trust domains in mesh config", - meshConfig: &meshconfig.MeshConfig{ - TrustDomain: "cluster.local", - TrustDomainAliases: []string{ - "alias-1.domain", "alias-1.domain", "some-other-alias-1.domain", "alias-2.domain", "alias-2.domain", - }, - }, - want: []string{"cluster.local", "alias-1.domain", "some-other-alias-1.domain", "alias-2.domain"}, - }, - { - name: "Mixed duplicated trust domains in mesh config", - meshConfig: &meshconfig.MeshConfig{ - TrustDomain: "cluster.local", - TrustDomainAliases: []string{ - "alias-1.domain", "cluster.local", "alias-2.domain", "some-other-alias-1.domain", "alias-2.domain", "alias-1.domain", - }, - }, - want: []string{"cluster.local", "alias-1.domain", "alias-2.domain", "some-other-alias-1.domain"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := TrustDomainsForValidation(tt.meshConfig); !reflect.DeepEqual(got, tt.want) { - t.Errorf("trustDomainsForValidation() = %#v, want %#v", got, tt.want) - } - }) - } -} diff --git a/pilot/pkg/networking/plugin/authz/authorization.go b/pilot/pkg/networking/plugin/authz/authorization.go deleted file mode 100644 index 4a771f710..000000000 --- a/pilot/pkg/networking/plugin/authz/authorization.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package authz - -import ( - tcppb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/authz/builder" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/trustdomain" -) - -type ActionType int - -const ( - // Local for action ALLOW, DENY and AUDIT and is enforced by Envoy RBAC filter. - Local ActionType = iota - // Custom action is enforced by Envoy ext_authz filter. - Custom -) - -type Builder struct { - // Lazy load - httpBuilt, tcpBuilt bool - - httpFilters []*httppb.HttpFilter - tcpFilters []*tcppb.Filter - builder *builder.Builder -} - -func NewBuilder(actionType ActionType, push *model.PushContext, proxy *model.Proxy) *Builder { - tdBundle := trustdomain.NewBundle(push.Mesh.TrustDomain, push.Mesh.TrustDomainAliases) - option := builder.Option{ - IsCustomBuilder: actionType == Custom, - Logger: &builder.AuthzLogger{}, - } - policies := push.AuthzPolicies.ListAuthorizationPolicies(proxy.ConfigNamespace, proxy.Metadata.Labels) - b := builder.New(tdBundle, push, policies, option) - return &Builder{builder: b} -} - -func (b *Builder) BuildTCP() []*tcppb.Filter { - if b.builder == nil { - return nil - } - if b.tcpBuilt { - return b.tcpFilters - } - b.tcpBuilt = true - b.tcpFilters = b.builder.BuildTCP() - - return b.tcpFilters -} - -func (b *Builder) BuildHTTP(class networking.ListenerClass) []*httppb.HttpFilter { - if b.builder == nil { - return nil - } - if class == networking.ListenerClassSidecarOutbound { - // Only applies to inbound and gateways - return nil - } - if b.httpBuilt { - return b.httpFilters - } - b.httpBuilt = true - b.httpFilters = b.builder.BuildHTTP() - - return b.httpFilters -} diff --git a/pilot/pkg/networking/plugin/plugin.go b/pilot/pkg/networking/plugin/plugin.go deleted file mode 100644 index 3d484226b..000000000 --- a/pilot/pkg/networking/plugin/plugin.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package plugin - -import ( - tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" -) - -const ( - // AuthzCustom is the name of the authorization plugin (CUSTOM action) passed through the command line - AuthzCustom = "ext_authz" - // Authn is the name of the authentication plugin passed through the command line - Authn = "authn" - // Authz is the name of the authorization plugin (ALLOW/DENY/AUDIT action) passed through the command line - Authz = "authz" - // MetadataExchange is the name of the telemetry plugin passed through the command line - MetadataExchange = "metadata_exchange" -) - -// InputParams is a set of values passed to Plugin callback methods. Not all fields are guaranteed to -// be set, it's up to the callee to validate required fields are set and emit error if they are not. -// These are for reading only and should not be modified. -type InputParams struct { - // Node is the node the response is for. - Node *model.Proxy - // ServiceInstance is the service instance colocated with the listener (applies to sidecar). - ServiceInstance *model.ServiceInstance - // Push holds stats and other information about the current push. - Push *model.PushContext -} - -// MTLSSettings describes the mTLS options for a filter chain -type MTLSSettings struct { - // Port is the port this option applies for - Port uint32 - // Mode is the mTLS mode to use - Mode model.MutualTLSMode - // TCP describes the tls context to use for TCP filter chains - TCP *tls.DownstreamTlsContext - // HTTP describes the tls context to use for HTTP filter chains - HTTP *tls.DownstreamTlsContext -} diff --git a/pilot/pkg/networking/telemetry/telemetry.go b/pilot/pkg/networking/telemetry/telemetry.go deleted file mode 100644 index 78fcb37a9..000000000 --- a/pilot/pkg/networking/telemetry/telemetry.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package telemetry - -import ( - "strconv" - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" -) - -var ( - // StatName patterns - serviceStatPattern = "%SERVICE%" - serviceFQDNStatPattern = "%SERVICE_FQDN%" - servicePortStatPattern = "%SERVICE_PORT%" - servicePortNameStatPattern = "%SERVICE_PORT_NAME%" - subsetNameStatPattern = "%SUBSET_NAME%" -) - -// BuildStatPrefix builds a stat prefix based on the stat pattern. -func BuildStatPrefix(statPattern string, host string, subset string, port *model.Port, attributes *model.ServiceAttributes) string { - prefix := strings.ReplaceAll(statPattern, serviceStatPattern, shortHostName(host, attributes)) - prefix = strings.ReplaceAll(prefix, serviceFQDNStatPattern, host) - prefix = strings.ReplaceAll(prefix, subsetNameStatPattern, subset) - prefix = strings.ReplaceAll(prefix, servicePortStatPattern, strconv.Itoa(port.Port)) - prefix = strings.ReplaceAll(prefix, servicePortNameStatPattern, port.Name) - return prefix -} - -// BuildInboundStatPrefix builds a stat prefix based on the stat pattern and filter chain telemetry data. -func BuildInboundStatPrefix(statPattern string, tm FilterChainMetadata, subset string, port uint32, portName string) string { - prefix := strings.ReplaceAll(statPattern, serviceStatPattern, tm.ShortHostname()) - prefix = strings.ReplaceAll(prefix, serviceFQDNStatPattern, tm.InstanceHostname.String()) - prefix = strings.ReplaceAll(prefix, subsetNameStatPattern, subset) - prefix = strings.ReplaceAll(prefix, servicePortStatPattern, strconv.Itoa(int(port))) - prefix = strings.ReplaceAll(prefix, servicePortNameStatPattern, portName) - return prefix -} - -// shortHostName constructs the name from kubernetes hosts based on attributes (name and namespace). -// For other hosts like VMs, this method does not do any thing - just returns the passed in host as is. -func shortHostName(host string, attributes *model.ServiceAttributes) string { - if attributes.ServiceRegistry == provider.Kubernetes { - return attributes.Name + "." + attributes.Namespace - } - return host -} - -// TraceOperation builds the string format: "%s:%d/*" for a given host and port -func TraceOperation(host string, port int) string { - // Format : "%s:%d/*" - return util.DomainName(host, port) + "/*" -} - -// FilterChainMetadata defines additional metadata for telemetry use for a filter chain. -type FilterChainMetadata struct { - // InstanceHostname defines the hostname of the service this filter chain is built for. - // Note: This is best effort; this may be empty if generated by Sidecar config, and there may be multiple - // Services that make up the filter chain. - InstanceHostname host.Name - // KubernetesServiceNamespace is the namespace the service is defined in, if it is for a Kubernetes Service. - // Note: This is best effort; this may be empty if generated by Sidecar config, and there may be multiple - // Services that make up the filter chain. - KubernetesServiceNamespace string - // KubernetesServiceName is the name of service, if it is for a Kubernetes Service. - // Note: This is best effort; this may be empty if generated by Sidecar config, and there may be multiple - // Services that make up the filter chain. - KubernetesServiceName string -} - -// ShortHostname constructs the name from kubernetes service name if available or just uses instance host name. -func (tm FilterChainMetadata) ShortHostname() string { - if tm.KubernetesServiceName != "" { - return tm.KubernetesServiceName + "." + tm.KubernetesServiceNamespace - } - return tm.InstanceHostname.String() -} diff --git a/pilot/pkg/networking/telemetry/telemetry_test.go b/pilot/pkg/networking/telemetry/telemetry_test.go deleted file mode 100644 index 80bdae3a1..000000000 --- a/pilot/pkg/networking/telemetry/telemetry_test.go +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package telemetry - -import ( - "fmt" - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" -) - -func TestBuildStatPrefix(t *testing.T) { - tests := []struct { - name string - statPattern string - host string - subsetName string - port *model.Port - attributes *model.ServiceAttributes - want string - }{ - { - "Service only pattern", - "%SERVICE%", - "reviews.default.svc.cluster.local", - "", - &model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"}, - &model.ServiceAttributes{ - ServiceRegistry: provider.Kubernetes, - Name: "reviews", - Namespace: "default", - }, - "reviews.default", - }, - { - "Service only pattern from different namespace", - "%SERVICE%", - "reviews.namespace1.svc.cluster.local", - "", - &model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"}, - &model.ServiceAttributes{ - ServiceRegistry: provider.Kubernetes, - Name: "reviews", - Namespace: "namespace1", - }, - "reviews.namespace1", - }, - { - "Service with port pattern from different namespace", - "%SERVICE%.%SERVICE_PORT%", - "reviews.namespace1.svc.cluster.local", - "", - &model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"}, - &model.ServiceAttributes{ - ServiceRegistry: provider.Kubernetes, - Name: "reviews", - Namespace: "namespace1", - }, - "reviews.namespace1.7443", - }, - { - "Service FQDN only pattern", - "%SERVICE_FQDN%", - "reviews.default.svc.cluster.local", - "", - &model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"}, - &model.ServiceAttributes{ - ServiceRegistry: provider.Kubernetes, - Name: "reviews", - Namespace: "default", - }, - "reviews.default.svc.cluster.local", - }, - { - "Service With Port pattern", - "%SERVICE%_%SERVICE_PORT%", - "reviews.default.svc.cluster.local", - "", - &model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"}, - &model.ServiceAttributes{ - ServiceRegistry: provider.Kubernetes, - Name: "reviews", - Namespace: "default", - }, - "reviews.default_7443", - }, - { - "Service With Port Name pattern", - "%SERVICE%_%SERVICE_PORT_NAME%", - "reviews.default.svc.cluster.local", - "", - &model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"}, - &model.ServiceAttributes{ - ServiceRegistry: provider.Kubernetes, - Name: "reviews", - Namespace: "default", - }, - "reviews.default_grpc-svc", - }, - { - "Service With Port and Port Name pattern", - "%SERVICE%_%SERVICE_PORT_NAME%_%SERVICE_PORT%", - "reviews.default.svc.cluster.local", - "", - &model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"}, - &model.ServiceAttributes{ - ServiceRegistry: provider.Kubernetes, - Name: "reviews", - Namespace: "default", - }, - "reviews.default_grpc-svc_7443", - }, - { - "Service FQDN With Port pattern", - "%SERVICE_FQDN%_%SERVICE_PORT%", - "reviews.default.svc.cluster.local", - "", - &model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"}, - &model.ServiceAttributes{ - ServiceRegistry: provider.Kubernetes, - Name: "reviews", - Namespace: "default", - }, - "reviews.default.svc.cluster.local_7443", - }, - { - "Service FQDN With Port Name pattern", - "%SERVICE_FQDN%_%SERVICE_PORT_NAME%", - "reviews.default.svc.cluster.local", - "", - &model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"}, - &model.ServiceAttributes{ - ServiceRegistry: provider.Kubernetes, - Name: "reviews", - Namespace: "default", - }, - "reviews.default.svc.cluster.local_grpc-svc", - }, - { - "Service FQDN With Port and Port Name pattern", - "%SERVICE_FQDN%_%SERVICE_PORT_NAME%_%SERVICE_PORT%", - "reviews.default.svc.cluster.local", - "", - &model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"}, - &model.ServiceAttributes{ - ServiceRegistry: provider.Kubernetes, - Name: "reviews", - Namespace: "default", - }, - "reviews.default.svc.cluster.local_grpc-svc_7443", - }, - { - "Service FQDN With Empty Subset, Port and Port Name pattern", - "%SERVICE_FQDN%%SUBSET_NAME%_%SERVICE_PORT_NAME%_%SERVICE_PORT%", - "reviews.default.svc.cluster.local", - "", - &model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"}, - &model.ServiceAttributes{ - ServiceRegistry: provider.Kubernetes, - Name: "reviews", - Namespace: "default", - }, - "reviews.default.svc.cluster.local_grpc-svc_7443", - }, - { - "Service FQDN With Subset, Port and Port Name pattern", - "%SERVICE_FQDN%.%SUBSET_NAME%.%SERVICE_PORT_NAME%_%SERVICE_PORT%", - "reviews.default.svc.cluster.local", - "v1", - &model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"}, - &model.ServiceAttributes{ - ServiceRegistry: provider.Kubernetes, - Name: "reviews", - Namespace: "default", - }, - "reviews.default.svc.cluster.local.v1.grpc-svc_7443", - }, - { - "Service FQDN With Unknown Pattern", - "%SERVICE_FQDN%.%DUMMY%", - "reviews.default.svc.cluster.local", - "v1", - &model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"}, - &model.ServiceAttributes{ - ServiceRegistry: provider.Kubernetes, - Name: "reviews", - Namespace: "default", - }, - "reviews.default.svc.cluster.local.%DUMMY%", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := BuildStatPrefix(tt.statPattern, tt.host, tt.subsetName, tt.port, tt.attributes) - if got != tt.want { - t.Errorf("Expected alt statname %s, but got %s", tt.want, got) - } - }) - } -} - -func TestTraceOperation(t *testing.T) { - tests := []struct { - host string - port int - match string - }{ - {"localhost", 3000, "localhost:3000/*"}, - {"127.0.0.1", 3000, "127.0.0.1:3000/*"}, - {"::1", 3000, "[::1]:3000/*"}, - {"2001:4860:0:2001::68", 3000, "[2001:4860:0:2001::68]:3000/*"}, - } - for _, tt := range tests { - t.Run(fmt.Sprint(tt.host), func(t *testing.T) { - if got := TraceOperation(tt.host, tt.port); got != tt.match { - t.Fatalf("got %v wanted %v", got, tt.match) - } - }) - } -} diff --git a/pilot/pkg/networking/util/util.go b/pilot/pkg/networking/util/util.go deleted file mode 100644 index 077c32bb4..000000000 --- a/pilot/pkg/networking/util/util.go +++ /dev/null @@ -1,699 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "fmt" - "net" - "sort" - "strconv" - "strings" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - http_conn "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "google.golang.org/protobuf/encoding/prototext" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/anypb" - "google.golang.org/protobuf/types/known/structpb" - "google.golang.org/protobuf/types/known/wrapperspb" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - istionetworking "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/network" - "github.com/apache/dubbo-go-pixiu/pkg/proto/merge" - "github.com/apache/dubbo-go-pixiu/pkg/util/strcase" -) - -const ( - // BlackHoleCluster to catch traffic from routes with unresolved clusters. Traffic arriving here goes nowhere. - BlackHoleCluster = "BlackHoleCluster" - // BlackHole is the name of the virtual host and route name used to block all traffic - BlackHole = "block_all" - // PassthroughCluster to forward traffic to the original destination requested. This cluster is used when - // traffic does not match any listener in envoy. - PassthroughCluster = "PassthroughCluster" - // Passthrough is the name of the virtual host used to forward traffic to the - // PassthroughCluster - Passthrough = "allow_any" - // PassthroughFilterChain to catch traffic that doesn't match other filter chains. - PassthroughFilterChain = "PassthroughFilterChain" - - // Inbound pass through cluster need to the bind the loopback ip address for the security and loop avoidance. - InboundPassthroughClusterIpv4 = "InboundPassthroughClusterIpv4" - InboundPassthroughClusterIpv6 = "InboundPassthroughClusterIpv6" - - // SniClusterFilter is the name of the sni_cluster envoy filter - SniClusterFilter = "envoy.filters.network.sni_cluster" - - // IstioMetadataKey is the key under which metadata is added to a route or cluster - // regarding the virtual service or destination rule used for each - IstioMetadataKey = "istio" - - // EnvoyTransportSocketMetadataKey is the key under which metadata is added to an endpoint - // which determines the endpoint level transport socket configuration. - EnvoyTransportSocketMetadataKey = "envoy.transport_socket_match" - - // EnvoyRawBufferSocketName matched with hardcoded built-in Envoy transport name which determines - // endpoint level plantext transport socket configuration - EnvoyRawBufferSocketName = wellknown.TransportSocketRawBuffer - - // EnvoyTLSSocketName matched with hardcoded built-in Envoy transport name which determines endpoint - // level tls transport socket configuration - EnvoyTLSSocketName = wellknown.TransportSocketTls - - // EnvoyQUICSocketName matched with hardcoded built-in Envoy transport name which determines endpoint - // level QUIC transport socket configuration - EnvoyQUICSocketName = wellknown.TransportSocketQuic - - // Well-known header names - AltSvcHeader = "alt-svc" -) - -// ALPNH2Only advertises that Proxy is going to use HTTP/2 when talking to the cluster. -var ALPNH2Only = []string{"h2"} - -// ALPNInMeshH2 advertises that Proxy is going to use HTTP/2 when talking to the in-mesh cluster. -// The custom "istio" value indicates in-mesh traffic and it's going to be used for routing decisions. -// Once Envoy supports client-side ALPN negotiation, this should be {"istio", "h2", "http/1.1"}. -var ALPNInMeshH2 = []string{"istio", "h2"} - -// ALPNInMeshH2WithMxc advertises that Proxy is going to use HTTP/2 when talking to the in-mesh cluster. -// The custom "istio" value indicates in-mesh traffic and it's going to be used for routing decisions. -// The custom "istio-peer-exchange" value indicates, metadata exchange is enabled for TCP. -var ALPNInMeshH2WithMxc = []string{"istio-peer-exchange", "istio", "h2"} - -// ALPNInMesh advertises that Proxy is going to talk to the in-mesh cluster. -// The custom "istio" value indicates in-mesh traffic and it's going to be used for routing decisions. -var ALPNInMesh = []string{"istio"} - -// ALPNInMeshWithMxc advertises that Proxy is going to talk to the in-mesh cluster and has metadata exchange enabled for -// TCP. The custom "istio-peer-exchange" value indicates, metadata exchange is enabled for TCP. The custom "istio" value -// indicates in-mesh traffic and it's going to be used for routing decisions. -var ALPNInMeshWithMxc = []string{"istio-peer-exchange", "istio"} - -// ALPNHttp advertises that Proxy is going to talking either http2 or http 1.1. -var ALPNHttp = []string{"h2", "http/1.1"} - -// ALPNHttp3OverQUIC advertises that Proxy is going to talk HTTP/3 over QUIC -var ALPNHttp3OverQUIC = []string{"h3"} - -// ALPNDownstreamWithMxc advertises that Proxy is going to talk either tcp(for metadata exchange), http2 or http 1.1. -var ALPNDownstreamWithMxc = []string{"istio-peer-exchange", "h2", "http/1.1"} - -// ALPNDownstream advertises that Proxy is going to talk http2 or http 1.1. -var ALPNDownstream = []string{"h2", "http/1.1"} - -// RegexEngine is the default google RE2 regex engine. -var RegexEngine = &matcher.RegexMatcher_GoogleRe2{GoogleRe2: &matcher.RegexMatcher_GoogleRE2{}} - -func getMaxCidrPrefix(addr string) uint32 { - ip := net.ParseIP(addr) - if ip.To4() == nil { - // ipv6 address - return 128 - } - // ipv4 address - return 32 -} - -func ListContains(haystack []string, needle string) bool { - for _, n := range haystack { - if needle == n { - return true - } - } - return false -} - -// ConvertAddressToCidr converts from string to CIDR proto -func ConvertAddressToCidr(addr string) *core.CidrRange { - if len(addr) == 0 { - return nil - } - - cidr := &core.CidrRange{ - AddressPrefix: addr, - PrefixLen: &wrapperspb.UInt32Value{ - Value: getMaxCidrPrefix(addr), - }, - } - - if strings.Contains(addr, "/") { - parts := strings.Split(addr, "/") - cidr.AddressPrefix = parts[0] - prefix, _ := strconv.Atoi(parts[1]) - cidr.PrefixLen.Value = uint32(prefix) - } - return cidr -} - -// BuildAddress returns a SocketAddress with the given ip and port or uds. -func BuildAddress(bind string, port uint32) *core.Address { - address := BuildNetworkAddress(bind, port, istionetworking.TransportProtocolTCP) - if address != nil { - return address - } - - return &core.Address{ - Address: &core.Address_Pipe{ - Pipe: &core.Pipe{ - Path: strings.TrimPrefix(bind, model.UnixAddressPrefix), - }, - }, - } -} - -func BuildNetworkAddress(bind string, port uint32, transport istionetworking.TransportProtocol) *core.Address { - if port == 0 { - return nil - } - return &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - Address: bind, - Protocol: transport.ToEnvoySocketProtocol(), - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: port, - }, - }, - }, - } -} - -// MessageToAnyWithError converts from proto message to proto Any -func MessageToAnyWithError(msg proto.Message) (*anypb.Any, error) { - b, err := proto.MarshalOptions{Deterministic: true}.Marshal(msg) - if err != nil { - return nil, err - } - return &anypb.Any{ - // nolint: staticcheck - TypeUrl: "type.googleapis.com/" + string(msg.ProtoReflect().Descriptor().FullName()), - Value: b, - }, nil -} - -// MessageToAny converts from proto message to proto Any -func MessageToAny(msg proto.Message) *anypb.Any { - out, err := MessageToAnyWithError(msg) - if err != nil { - log.Error(fmt.Sprintf("error marshaling Any %s: %v", prototext.Format(msg), err)) - return nil - } - return out -} - -// SortVirtualHosts sorts a slice of virtual hosts by name. -// -// Envoy computes a hash of RDS to see if things have changed - hash is affected by order of elements in the filter. Therefore -// we sort virtual hosts by name before handing them back so the ordering is stable across HTTP Route Configs. -func SortVirtualHosts(hosts []*route.VirtualHost) { - if len(hosts) < 2 { - return - } - sort.SliceStable(hosts, func(i, j int) bool { - return hosts[i].Name < hosts[j].Name - }) -} - -// IsIstioVersionGE114 checks whether the given Istio version is greater than or equals 1.14. -func IsIstioVersionGE114(version *model.IstioVersion) bool { - return version == nil || - version.Compare(&model.IstioVersion{Major: 1, Minor: 14, Patch: -1}) >= 0 -} - -func IsProtocolSniffingEnabledForPort(port *model.Port) bool { - return features.EnableProtocolSniffingForOutbound && port.Protocol.IsUnsupported() -} - -func IsProtocolSniffingEnabledForInboundPort(port *model.Port) bool { - return features.EnableProtocolSniffingForInbound && port.Protocol.IsUnsupported() -} - -func IsProtocolSniffingEnabledForOutboundPort(port *model.Port) bool { - return features.EnableProtocolSniffingForOutbound && port.Protocol.IsUnsupported() -} - -// ConvertLocality converts '/' separated locality string to Locality struct. -func ConvertLocality(locality string) *core.Locality { - if locality == "" { - return &core.Locality{} - } - - region, zone, subzone := model.SplitLocalityLabel(locality) - return &core.Locality{ - Region: region, - Zone: zone, - SubZone: subzone, - } -} - -// LocalityToString converts Locality struct to '/' separated locality string. -func LocalityToString(l *core.Locality) string { - if l == nil { - return "" - } - resp := l.Region - if l.Zone == "" { - return resp - } - resp += "/" + l.Zone - if l.SubZone == "" { - return resp - } - resp += "/" + l.SubZone - return resp -} - -// IsLocalityEmpty checks if a locality is empty (checking region is good enough, based on how its initialized) -func IsLocalityEmpty(locality *core.Locality) bool { - if locality == nil || (len(locality.GetRegion()) == 0) { - return true - } - return false -} - -func LocalityMatch(proxyLocality *core.Locality, ruleLocality string) bool { - ruleRegion, ruleZone, ruleSubzone := model.SplitLocalityLabel(ruleLocality) - regionMatch := ruleRegion == "*" || proxyLocality.GetRegion() == ruleRegion - zoneMatch := ruleZone == "*" || ruleZone == "" || proxyLocality.GetZone() == ruleZone - subzoneMatch := ruleSubzone == "*" || ruleSubzone == "" || proxyLocality.GetSubZone() == ruleSubzone - - if regionMatch && zoneMatch && subzoneMatch { - return true - } - return false -} - -func LbPriority(proxyLocality, endpointsLocality *core.Locality) int { - if proxyLocality.GetRegion() == endpointsLocality.GetRegion() { - if proxyLocality.GetZone() == endpointsLocality.GetZone() { - if proxyLocality.GetSubZone() == endpointsLocality.GetSubZone() { - return 0 - } - return 1 - } - return 2 - } - return 3 -} - -// return a shallow copy ClusterLoadAssignment -func CloneClusterLoadAssignment(original *endpoint.ClusterLoadAssignment) *endpoint.ClusterLoadAssignment { - if original == nil { - return nil - } - out := &endpoint.ClusterLoadAssignment{} - - out.ClusterName = original.ClusterName - out.Endpoints = cloneLocalityLbEndpoints(original.Endpoints) - out.Policy = original.Policy - - return out -} - -// return a shallow copy LocalityLbEndpoints -func cloneLocalityLbEndpoints(endpoints []*endpoint.LocalityLbEndpoints) []*endpoint.LocalityLbEndpoints { - out := make([]*endpoint.LocalityLbEndpoints, 0, len(endpoints)) - for _, ep := range endpoints { - clone := CloneLocalityLbEndpoint(ep) - out = append(out, clone) - } - return out -} - -// return a shallow copy of LocalityLbEndpoints -func CloneLocalityLbEndpoint(ep *endpoint.LocalityLbEndpoints) *endpoint.LocalityLbEndpoints { - clone := &endpoint.LocalityLbEndpoints{} - clone.Locality = ep.Locality - clone.LbEndpoints = ep.LbEndpoints - clone.Proximity = ep.Proximity - clone.Priority = ep.Priority - if ep.LoadBalancingWeight != nil { - clone.LoadBalancingWeight = &wrapperspb.UInt32Value{ - Value: ep.GetLoadBalancingWeight().GetValue(), - } - } - return clone -} - -// BuildConfigInfoMetadata builds core.Metadata struct containing the -// name.namespace of the config, the type, etc. -func BuildConfigInfoMetadata(config config.Meta) *core.Metadata { - return AddConfigInfoMetadata(nil, config) -} - -// AddConfigInfoMetadata adds name.namespace of the config, the type, etc -// to the given core.Metadata struct, if metadata is not initialized, build a new metadata. -func AddConfigInfoMetadata(metadata *core.Metadata, config config.Meta) *core.Metadata { - if metadata == nil { - metadata = &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{}, - } - } - s := "/apis/" + config.GroupVersionKind.Group + "/" + config.GroupVersionKind.Version + "/namespaces/" + config.Namespace + "/" + - strcase.CamelCaseToKebabCase(config.GroupVersionKind.Kind) + "/" + config.Name - if _, ok := metadata.FilterMetadata[IstioMetadataKey]; !ok { - metadata.FilterMetadata[IstioMetadataKey] = &structpb.Struct{ - Fields: map[string]*structpb.Value{}, - } - } - metadata.FilterMetadata[IstioMetadataKey].Fields["config"] = &structpb.Value{ - Kind: &structpb.Value_StringValue{ - StringValue: s, - }, - } - return metadata -} - -// AddSubsetToMetadata will insert the subset name supplied. This should be called after the initial -// "istio" metadata has been created for the cluster. If the "istio" metadata field is not already -// defined, the subset information will not be added (to prevent adding this information where not -// needed). This is used for telemetry reporting. -func AddSubsetToMetadata(md *core.Metadata, subset string) { - if istioMeta, ok := md.FilterMetadata[IstioMetadataKey]; ok { - istioMeta.Fields["subset"] = &structpb.Value{ - Kind: &structpb.Value_StringValue{ - StringValue: subset, - }, - } - } -} - -// IsHTTPFilterChain returns true if the filter chain contains a HTTP connection manager filter -func IsHTTPFilterChain(filterChain *listener.FilterChain) bool { - for _, f := range filterChain.Filters { - if f.Name == wellknown.HTTPConnectionManager { - return true - } - } - return false -} - -// MergeAnyWithAny merges a given any typed message into the given Any typed message by dynamically inferring the -// type of Any -func MergeAnyWithAny(dst *anypb.Any, src *anypb.Any) (*anypb.Any, error) { - // Assuming that Pilot is compiled with this type [which should always be the case] - var err error - - // get an object of type used by this message - dstX, err := dst.UnmarshalNew() - if err != nil { - return nil, err - } - - // get an object of type used by this message - srcX, err := src.UnmarshalNew() - if err != nil { - return nil, err - } - - // Merge the two typed protos - merge.Merge(dstX, srcX) - var retVal *anypb.Any - - // Convert the merged proto back to dst - if retVal, err = anypb.New(dstX); err != nil { - return nil, err - } - - return retVal, nil -} - -// BuildLbEndpointMetadata adds metadata values to a lb endpoint -func BuildLbEndpointMetadata(networkID network.ID, tlsMode, workloadname, namespace string, - clusterID cluster.ID, labels labels.Instance) *core.Metadata { - if networkID == "" && (tlsMode == "" || tlsMode == model.DisabledTLSModeLabel) && - (!features.EndpointTelemetryLabel || !features.EnableTelemetryLabel) { - return nil - } - - metadata := &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{}, - } - - if tlsMode != "" && tlsMode != model.DisabledTLSModeLabel { - metadata.FilterMetadata[EnvoyTransportSocketMetadataKey] = &structpb.Struct{ - Fields: map[string]*structpb.Value{ - model.TLSModeLabelShortname: {Kind: &structpb.Value_StringValue{StringValue: tlsMode}}, - }, - } - } - - // Add compressed telemetry metadata. Note this is a short term solution to make server workload metadata - // available at client sidecar, so that telemetry filter could use for metric labels. This is useful for two cases: - // server does not have sidecar injected, and request fails to reach server and thus metadata exchange does not happen. - // Due to performance concern, telemetry metadata is compressed into a semicolon separted string: - // workload-name;namespace;canonical-service-name;canonical-service-revision;cluster-id. - if features.EndpointTelemetryLabel { - var sb strings.Builder - sb.WriteString(workloadname) - sb.WriteString(";") - sb.WriteString(namespace) - sb.WriteString(";") - if csn, ok := labels[model.IstioCanonicalServiceLabelName]; ok { - sb.WriteString(csn) - } - sb.WriteString(";") - if csr, ok := labels[model.IstioCanonicalServiceRevisionLabelName]; ok { - sb.WriteString(csr) - } - sb.WriteString(";") - sb.WriteString(clusterID.String()) - addIstioEndpointLabel(metadata, "workload", &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: sb.String()}}) - } - - return metadata -} - -// MaybeApplyTLSModeLabel may or may not update the metadata for the Envoy transport socket matches for auto mTLS. -func MaybeApplyTLSModeLabel(ep *endpoint.LbEndpoint, tlsMode string) (*endpoint.LbEndpoint, bool) { - if ep == nil || ep.Metadata == nil { - return nil, false - } - epTLSMode := "" - if ep.Metadata.FilterMetadata != nil { - if v, ok := ep.Metadata.FilterMetadata[EnvoyTransportSocketMetadataKey]; ok { - epTLSMode = v.Fields[model.TLSModeLabelShortname].GetStringValue() - } - } - // Normalize the tls label name before comparison. This ensure we won't falsely cloning - // the endpoint when they are "" and model.DisabledTLSModeLabel. - if epTLSMode == model.DisabledTLSModeLabel { - epTLSMode = "" - } - if tlsMode == model.DisabledTLSModeLabel { - tlsMode = "" - } - if epTLSMode == tlsMode { - return nil, false - } - // We make a copy instead of modifying on existing endpoint pointer directly to avoid data race. - // See https://github.com/istio/istio/issues/34227 for details. - newEndpoint := proto.Clone(ep).(*endpoint.LbEndpoint) - if tlsMode != "" && tlsMode != model.DisabledTLSModeLabel { - newEndpoint.Metadata.FilterMetadata[EnvoyTransportSocketMetadataKey] = &structpb.Struct{ - Fields: map[string]*structpb.Value{ - model.TLSModeLabelShortname: {Kind: &structpb.Value_StringValue{StringValue: tlsMode}}, - }, - } - } else { - delete(newEndpoint.Metadata.FilterMetadata, EnvoyTransportSocketMetadataKey) - } - return newEndpoint, true -} - -func addIstioEndpointLabel(metadata *core.Metadata, key string, val *structpb.Value) { - if _, ok := metadata.FilterMetadata[IstioMetadataKey]; !ok { - metadata.FilterMetadata[IstioMetadataKey] = &structpb.Struct{ - Fields: map[string]*structpb.Value{}, - } - } - - metadata.FilterMetadata[IstioMetadataKey].Fields[key] = val -} - -// IsAllowAnyOutbound checks if allow_any is enabled for outbound traffic -func IsAllowAnyOutbound(node *model.Proxy) bool { - return node.SidecarScope != nil && - node.SidecarScope.OutboundTrafficPolicy != nil && - node.SidecarScope.OutboundTrafficPolicy.Mode == networking.OutboundTrafficPolicy_ALLOW_ANY -} - -func StringToExactMatch(in []string) []*matcher.StringMatcher { - if len(in) == 0 { - return nil - } - res := make([]*matcher.StringMatcher, 0, len(in)) - for _, s := range in { - res = append(res, &matcher.StringMatcher{ - MatchPattern: &matcher.StringMatcher_Exact{Exact: s}, - }) - } - return res -} - -func StringToPrefixMatch(in []string) []*matcher.StringMatcher { - if len(in) == 0 { - return nil - } - res := make([]*matcher.StringMatcher, 0, len(in)) - for _, s := range in { - res = append(res, &matcher.StringMatcher{ - MatchPattern: &matcher.StringMatcher_Prefix{Prefix: s}, - }) - } - return res -} - -func ConvertToEnvoyMatches(in []*networking.StringMatch) []*matcher.StringMatcher { - res := make([]*matcher.StringMatcher, 0, len(in)) - - for _, im := range in { - if em := ConvertToEnvoyMatch(im); em != nil { - res = append(res, em) - } - } - - return res -} - -func ConvertToEnvoyMatch(in *networking.StringMatch) *matcher.StringMatcher { - switch m := in.MatchType.(type) { - case *networking.StringMatch_Exact: - return &matcher.StringMatcher{MatchPattern: &matcher.StringMatcher_Exact{Exact: m.Exact}} - case *networking.StringMatch_Prefix: - return &matcher.StringMatcher{MatchPattern: &matcher.StringMatcher_Prefix{Prefix: m.Prefix}} - case *networking.StringMatch_Regex: - return &matcher.StringMatcher{ - MatchPattern: &matcher.StringMatcher_SafeRegex{ - SafeRegex: &matcher.RegexMatcher{ - EngineType: RegexEngine, - Regex: m.Regex, - }, - }, - } - } - return nil -} - -func StringSliceEqual(a, b []string) bool { - if len(a) != len(b) { - return false - } - - for i := range a { - if a[i] != b[i] { - return false - } - } - - return true -} - -func UInt32SliceEqual(a, b []uint32) bool { - if len(a) != len(b) { - return false - } - - for i := range a { - if a[i] != b[i] { - return false - } - } - - return true -} - -func CidrRangeSliceEqual(a, b []*core.CidrRange) bool { - if len(a) != len(b) { - return false - } - - for i := range a { - netA, err := toIPNet(a[i]) - if err != nil { - return false - } - netB, err := toIPNet(b[i]) - if err != nil { - return false - } - if netA.IP.String() != netB.IP.String() { - return false - } - } - - return true -} - -func toIPNet(c *core.CidrRange) (*net.IPNet, error) { - _, cA, err := net.ParseCIDR(c.AddressPrefix + "/" + strconv.Itoa(int(c.PrefixLen.GetValue()))) - if err != nil { - log.Errorf("failed to parse CidrRange %v as IPNet: %v", c, err) - } - return cA, err -} - -// meshconfig ForwardClientCertDetails and the Envoy config enum are off by 1 -// due to the UNDEFINED in the meshconfig ForwardClientCertDetails -func MeshConfigToEnvoyForwardClientCertDetails(c meshconfig.Topology_ForwardClientCertDetails) http_conn.HttpConnectionManager_ForwardClientCertDetails { - return http_conn.HttpConnectionManager_ForwardClientCertDetails(c - 1) -} - -// ByteCount returns a human readable byte format -// Inspired by https://yourbasic.org/golang/formatting-byte-size-to-human-readable-format/ -func ByteCount(b int) string { - const unit = 1000 - if b < unit { - return fmt.Sprintf("%dB", b) - } - div, exp := int64(unit), 0 - for n := b / unit; n >= unit; n /= unit { - div *= unit - exp++ - } - return fmt.Sprintf("%.1f%cB", - float64(b)/float64(div), "kMGTPE"[exp]) -} - -// IPv6Compliant encloses ipv6 addresses in square brackets followed by port number in Host header/URIs -func IPv6Compliant(host string) string { - if strings.Contains(host, ":") { - return "[" + host + "]" - } - return host -} - -// DomainName builds the domain name for a given host and port -func DomainName(host string, port int) string { - return net.JoinHostPort(host, strconv.Itoa(port)) -} diff --git a/pilot/pkg/networking/util/util_test.go b/pilot/pkg/networking/util/util_test.go deleted file mode 100644 index b57a692a4..000000000 --- a/pilot/pkg/networking/util/util_test.go +++ /dev/null @@ -1,1132 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "fmt" - "reflect" - "testing" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - xdsutil "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/testing/protocmp" - structpb "google.golang.org/protobuf/types/known/structpb" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/network" - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -var testCla = &endpoint.ClusterLoadAssignment{ - ClusterName: "cluster", - Endpoints: []*endpoint.LocalityLbEndpoints{{ - Locality: &core.Locality{Region: "foo", Zone: "bar"}, - LbEndpoints: []*endpoint.LbEndpoint{ - { - HostIdentifier: &endpoint.LbEndpoint_Endpoint{Endpoint: &endpoint.Endpoint{Hostname: "foo", Address: BuildAddress("1.1.1.1", 80)}}, - LoadBalancingWeight: &wrappers.UInt32Value{Value: 100}, - }, - { - HostIdentifier: &endpoint.LbEndpoint_Endpoint{Endpoint: &endpoint.Endpoint{Hostname: "foo", Address: BuildAddress("1.1.1.1", 80)}}, - LoadBalancingWeight: &wrappers.UInt32Value{Value: 100}, - }, - }, - LoadBalancingWeight: &wrappers.UInt32Value{Value: 50}, - Priority: 2, - }}, -} - -func BenchmarkCloneClusterLoadAssignment(b *testing.B) { - for i := 0; i < b.N; i++ { - cpy := CloneClusterLoadAssignment(testCla) - _ = cpy - } -} - -func TestCloneClusterLoadAssignment(t *testing.T) { - cloned := CloneClusterLoadAssignment(testCla) - cloned2 := CloneClusterLoadAssignment(testCla) - if !cmp.Equal(testCla, cloned, protocmp.Transform()) { - t.Fatalf("expected %v to be the same as %v", testCla, cloned) - } - cloned.ClusterName = "foo" - cloned.Endpoints[0].LbEndpoints[0].LoadBalancingWeight.Value = 5 - if cmp.Equal(testCla, cloned, protocmp.Transform()) { - t.Fatalf("expected %v to be the different from %v", testCla, cloned) - } - if !cmp.Equal(testCla, cloned2, protocmp.Transform()) { - t.Fatalf("expected %v to be the same as %v", testCla, cloned) - } -} - -func TestConvertAddressToCidr(t *testing.T) { - tests := []struct { - name string - addr string - want *core.CidrRange - }{ - { - "return nil when the address is empty", - "", - nil, - }, - { - "success case with no PrefixLen", - "1.2.3.4", - &core.CidrRange{ - AddressPrefix: "1.2.3.4", - PrefixLen: &wrappers.UInt32Value{ - Value: 32, - }, - }, - }, - { - "success case with PrefixLen", - "1.2.3.4/16", - &core.CidrRange{ - AddressPrefix: "1.2.3.4", - PrefixLen: &wrappers.UInt32Value{ - Value: 16, - }, - }, - }, - { - "ipv6", - "2001:db8::", - &core.CidrRange{ - AddressPrefix: "2001:db8::", - PrefixLen: &wrappers.UInt32Value{ - Value: 128, - }, - }, - }, - { - "ipv6 with prefix", - "2001:db8::/64", - &core.CidrRange{ - AddressPrefix: "2001:db8::", - PrefixLen: &wrappers.UInt32Value{ - Value: 64, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := ConvertAddressToCidr(tt.addr); !reflect.DeepEqual(got, tt.want) { - t.Errorf("ConvertAddressToCidr() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestConvertLocality(t *testing.T) { - tests := []struct { - name string - locality string - want *core.Locality - reverse string - }{ - { - name: "nil locality", - locality: "", - want: &core.Locality{}, - }, - { - name: "locality with only region", - locality: "region", - want: &core.Locality{ - Region: "region", - }, - }, - { - name: "locality with region and zone", - locality: "region/zone", - want: &core.Locality{ - Region: "region", - Zone: "zone", - }, - }, - { - name: "locality with region zone and subzone", - locality: "region/zone/subzone", - want: &core.Locality{ - Region: "region", - Zone: "zone", - SubZone: "subzone", - }, - }, - { - name: "locality with region zone subzone and rack", - locality: "region/zone/subzone/rack", - want: &core.Locality{ - Region: "region", - Zone: "zone", - SubZone: "subzone", - }, - reverse: "region/zone/subzone", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := ConvertLocality(tt.locality) - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Expected locality %#v, but got %#v", tt.want, got) - } - // Verify we can reverse the conversion back to the original input - reverse := LocalityToString(got) - if tt.reverse != "" { - // Special case, reverse lookup is different than original input - if tt.reverse != reverse { - t.Errorf("Expected locality string %s, got %v", tt.reverse, reverse) - } - } else if tt.locality != reverse { - t.Errorf("Expected locality string %s, got %v", tt.locality, reverse) - } - }) - } -} - -func TestLocalityMatch(t *testing.T) { - tests := []struct { - name string - locality *core.Locality - rule string - match bool - }{ - { - name: "wildcard matching", - locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, - rule: "*", - match: true, - }, - { - name: "wildcard matching", - locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, - rule: "region1/*", - match: true, - }, - { - name: "wildcard matching", - locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, - rule: "region1/zone1/*", - match: true, - }, - { - name: "wildcard not matching", - locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, - rule: "region1/zone2/*", - match: false, - }, - { - name: "region matching", - locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, - rule: "region1", - match: true, - }, - { - name: "region and zone matching", - locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - SubZone: "subzone1", - }, - rule: "region1/zone1", - match: true, - }, - { - name: "zubzone wildcard matching", - locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - }, - rule: "region1/zone1", - match: true, - }, - { - name: "subzone mismatching", - locality: &core.Locality{ - Region: "region1", - Zone: "zone1", - }, - rule: "region1/zone1/subzone2", - match: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - match := LocalityMatch(tt.locality, tt.rule) - if match != tt.match { - t.Errorf("Expected matching result %v, but got %v", tt.match, match) - } - }) - } -} - -func TestIsLocalityEmpty(t *testing.T) { - tests := []struct { - name string - locality *core.Locality - want bool - }{ - { - "non empty locality", - &core.Locality{ - Region: "region", - }, - false, - }, - { - "empty locality", - &core.Locality{ - Region: "", - }, - true, - }, - { - "nil locality", - nil, - true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := IsLocalityEmpty(tt.locality) - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Expected locality empty result %#v, but got %#v", tt.want, got) - } - }) - } -} - -func TestBuildConfigInfoMetadata(t *testing.T) { - cases := []struct { - name string - in config.Meta - want *core.Metadata - }{ - { - "destination-rule", - config.Meta{ - Name: "svcA", - Namespace: "default", - Domain: "svc.cluster.local", - GroupVersionKind: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(), - }, - &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "config": { - Kind: &structpb.Value_StringValue{ - StringValue: "/apis/networking.istio.io/v1alpha3/namespaces/default/destination-rule/svcA", - }, - }, - }, - }, - }, - }, - }, - } - - for _, v := range cases { - t.Run(v.name, func(tt *testing.T) { - got := BuildConfigInfoMetadata(v.in) - if diff := cmp.Diff(got, v.want, protocmp.Transform()); diff != "" { - tt.Errorf("BuildConfigInfoMetadata(%v) produced incorrect result:\ngot: %v\nwant: %v\nDiff: %s", v.in, got, v.want, diff) - } - }) - } -} - -func TestAddConfigInfoMetadata(t *testing.T) { - cases := []struct { - name string - in config.Meta - meta *core.Metadata - want *core.Metadata - }{ - { - "nil metadata", - config.Meta{ - Name: "svcA", - Namespace: "default", - Domain: "svc.cluster.local", - GroupVersionKind: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(), - }, - nil, - &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "config": { - Kind: &structpb.Value_StringValue{ - StringValue: "/apis/networking.istio.io/v1alpha3/namespaces/default/destination-rule/svcA", - }, - }, - }, - }, - }, - }, - }, - { - "empty metadata", - config.Meta{ - Name: "svcA", - Namespace: "default", - Domain: "svc.cluster.local", - GroupVersionKind: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(), - }, - &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{}, - }, - &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "config": { - Kind: &structpb.Value_StringValue{ - StringValue: "/apis/networking.istio.io/v1alpha3/namespaces/default/destination-rule/svcA", - }, - }, - }, - }, - }, - }, - }, - { - "existing istio metadata", - config.Meta{ - Name: "svcA", - Namespace: "default", - Domain: "svc.cluster.local", - GroupVersionKind: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(), - }, - &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "other-config": { - Kind: &structpb.Value_StringValue{ - StringValue: "other-config", - }, - }, - }, - }, - }, - }, - &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "other-config": { - Kind: &structpb.Value_StringValue{ - StringValue: "other-config", - }, - }, - "config": { - Kind: &structpb.Value_StringValue{ - StringValue: "/apis/networking.istio.io/v1alpha3/namespaces/default/destination-rule/svcA", - }, - }, - }, - }, - }, - }, - }, - { - "existing non-istio metadata", - config.Meta{ - Name: "svcA", - Namespace: "default", - Domain: "svc.cluster.local", - GroupVersionKind: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(), - }, - &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - "other-metadata": { - Fields: map[string]*structpb.Value{ - "other-config": { - Kind: &structpb.Value_StringValue{ - StringValue: "other-config", - }, - }, - }, - }, - }, - }, - &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - "other-metadata": { - Fields: map[string]*structpb.Value{ - "other-config": { - Kind: &structpb.Value_StringValue{ - StringValue: "other-config", - }, - }, - }, - }, - IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "config": { - Kind: &structpb.Value_StringValue{ - StringValue: "/apis/networking.istio.io/v1alpha3/namespaces/default/destination-rule/svcA", - }, - }, - }, - }, - }, - }, - }, - } - - for _, v := range cases { - t.Run(v.name, func(tt *testing.T) { - got := AddConfigInfoMetadata(v.meta, v.in) - if diff := cmp.Diff(got, v.want, protocmp.Transform()); diff != "" { - tt.Errorf("AddConfigInfoMetadata(%v) produced incorrect result:\ngot: %v\nwant: %v\nDiff: %s", v.in, got, v.want, diff) - } - }) - } -} - -func TestAddSubsetToMetadata(t *testing.T) { - cases := []struct { - name string - in *core.Metadata - subset string - want *core.Metadata - }{ - { - "simple subset", - &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "config": { - Kind: &structpb.Value_StringValue{ - StringValue: "/apis/networking.istio.io/v1alpha3/namespaces/default/destination-rule/svcA", - }, - }, - }, - }, - }, - }, - "test-subset", - &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "config": { - Kind: &structpb.Value_StringValue{ - StringValue: "/apis/networking.istio.io/v1alpha3/namespaces/default/destination-rule/svcA", - }, - }, - "subset": { - Kind: &structpb.Value_StringValue{ - StringValue: "test-subset", - }, - }, - }, - }, - }, - }, - }, - { - "no metadata", - &core.Metadata{}, - "test-subset", - &core.Metadata{}, - }, - } - - for _, v := range cases { - t.Run(v.name, func(tt *testing.T) { - AddSubsetToMetadata(v.in, v.subset) - got := v.in - if diff := cmp.Diff(got, v.want, protocmp.Transform()); diff != "" { - tt.Errorf("AddSubsetToMetadata(%v, %s) produced incorrect result:\ngot: %v\nwant: %v\nDiff: %s", v.in, v.subset, got, v.want, diff) - } - }) - } -} - -func TestIsHTTPFilterChain(t *testing.T) { - httpFilterChain := &listener.FilterChain{ - Filters: []*listener.Filter{ - { - Name: xdsutil.HTTPConnectionManager, - }, - }, - } - - tcpFilterChain := &listener.FilterChain{ - Filters: []*listener.Filter{ - { - Name: xdsutil.TCPProxy, - }, - }, - } - - if !IsHTTPFilterChain(httpFilterChain) { - t.Errorf("http Filter chain not detected properly") - } - - if IsHTTPFilterChain(tcpFilterChain) { - t.Errorf("tcp filter chain detected as http filter chain") - } -} - -func TestIsAllowAnyOutbound(t *testing.T) { - tests := []struct { - name string - node *model.Proxy - result bool - }{ - { - name: "NilSidecarScope", - node: &model.Proxy{}, - result: false, - }, - { - name: "NilOutboundTrafficPolicy", - node: &model.Proxy{ - SidecarScope: &model.SidecarScope{}, - }, - result: false, - }, - { - name: "OutboundTrafficPolicyRegistryOnly", - node: &model.Proxy{ - SidecarScope: &model.SidecarScope{ - OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_REGISTRY_ONLY, - }, - }, - }, - result: false, - }, - { - name: "OutboundTrafficPolicyAllowAny", - node: &model.Proxy{ - SidecarScope: &model.SidecarScope{ - OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, - }, - }, - }, - result: true, - }, - } - for i := range tests { - t.Run(tests[i].name, func(t *testing.T) { - out := IsAllowAnyOutbound(tests[i].node) - if out != tests[i].result { - t.Errorf("Expected %t but got %t for test case: %v\n", tests[i].result, out, tests[i].node) - } - }) - } -} - -func TestBuildAddress(t *testing.T) { - testCases := []struct { - name string - addr string - port uint32 - expected *core.Address - }{ - { - name: "ipv4", - addr: "172.10.10.1", - port: 8080, - expected: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - Address: "172.10.10.1", - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 8080, - }, - }, - }, - }, - }, - { - name: "ipv6", - addr: "fe80::10e7:52ff:fecd:198b", - port: 8080, - expected: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - Address: "fe80::10e7:52ff:fecd:198b", - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: 8080, - }, - }, - }, - }, - }, - { - name: "uds", - addr: "/var/run/test/socket", - port: 0, - expected: &core.Address{ - Address: &core.Address_Pipe{ - Pipe: &core.Pipe{ - Path: "/var/run/test/socket", - }, - }, - }, - }, - { - name: "uds with unix prefix", - addr: "unix:///var/run/test/socket", - port: 0, - expected: &core.Address{ - Address: &core.Address_Pipe{ - Pipe: &core.Pipe{ - Path: "/var/run/test/socket", - }, - }, - }, - }, - } - - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - addr := BuildAddress(test.addr, test.port) - if !reflect.DeepEqual(addr, test.expected) { - t.Errorf("expected add %v, but got %v", test.expected, addr) - } - }) - } -} - -func TestCidrRangeSliceEqual(t *testing.T) { - tests := []struct { - name string - first []*core.CidrRange - second []*core.CidrRange - want bool - }{ - { - "both nil", - nil, - nil, - true, - }, - { - "unequal length", - []*core.CidrRange{ - { - AddressPrefix: "1.2.3.4", - PrefixLen: &wrappers.UInt32Value{ - Value: 32, - }, - }, - { - AddressPrefix: "1.2.3.5", - PrefixLen: &wrappers.UInt32Value{ - Value: 32, - }, - }, - }, - []*core.CidrRange{ - { - AddressPrefix: "1.2.3.4", - PrefixLen: &wrappers.UInt32Value{ - Value: 32, - }, - }, - }, - false, - }, - { - "equal cidr", - []*core.CidrRange{ - { - AddressPrefix: "1.2.3.4", - PrefixLen: &wrappers.UInt32Value{ - Value: 32, - }, - }, - }, - []*core.CidrRange{ - { - AddressPrefix: "1.2.3.4", - PrefixLen: &wrappers.UInt32Value{ - Value: 32, - }, - }, - }, - true, - }, - { - "equal cidr with different insignificant bits", - []*core.CidrRange{ - { - AddressPrefix: "1.2.3.4", - PrefixLen: &wrappers.UInt32Value{ - Value: 24, - }, - }, - }, - []*core.CidrRange{ - { - AddressPrefix: "1.2.3.5", - PrefixLen: &wrappers.UInt32Value{ - Value: 24, - }, - }, - }, - true, - }, - { - "unequal cidr address prefix mismatch", - []*core.CidrRange{ - { - AddressPrefix: "1.2.3.4", - PrefixLen: &wrappers.UInt32Value{ - Value: 32, - }, - }, - }, - []*core.CidrRange{ - { - AddressPrefix: "1.2.3.5", - PrefixLen: &wrappers.UInt32Value{ - Value: 32, - }, - }, - }, - false, - }, - { - "unequal cidr prefixlen mismatch", - []*core.CidrRange{ - { - AddressPrefix: "1.2.3.4", - PrefixLen: &wrappers.UInt32Value{ - Value: 32, - }, - }, - }, - []*core.CidrRange{ - { - AddressPrefix: "1.2.3.4", - PrefixLen: &wrappers.UInt32Value{ - Value: 16, - }, - }, - }, - false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := CidrRangeSliceEqual(tt.first, tt.second); got != tt.want { - t.Errorf("Unexpected CidrRangeSliceEqual() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestEndpointMetadata(t *testing.T) { - test.SetBoolForTest(t, &features.EndpointTelemetryLabel, true) - cases := []struct { - name string - network network.ID - tlsMode string - workloadName string - clusterID cluster.ID - namespace string - labels labels.Instance - want *core.Metadata - }{ - { - name: "all empty", - tlsMode: model.DisabledTLSModeLabel, - network: "", - workloadName: "", - clusterID: "", - want: &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "workload": { - Kind: &structpb.Value_StringValue{ - StringValue: ";;;;", - }, - }, - }, - }, - }, - }, - }, - { - name: "tls mode", - tlsMode: model.IstioMutualTLSModeLabel, - network: "", - workloadName: "", - clusterID: "", - want: &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - EnvoyTransportSocketMetadataKey: { - Fields: map[string]*structpb.Value{ - model.TLSModeLabelShortname: { - Kind: &structpb.Value_StringValue{ - StringValue: model.IstioMutualTLSModeLabel, - }, - }, - }, - }, - IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "workload": { - Kind: &structpb.Value_StringValue{ - StringValue: ";;;;", - }, - }, - }, - }, - }, - }, - }, - { - name: "network and tls mode", - tlsMode: model.IstioMutualTLSModeLabel, - network: "network", - workloadName: "", - clusterID: "", - want: &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - EnvoyTransportSocketMetadataKey: { - Fields: map[string]*structpb.Value{ - model.TLSModeLabelShortname: { - Kind: &structpb.Value_StringValue{ - StringValue: model.IstioMutualTLSModeLabel, - }, - }, - }, - }, - IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "workload": { - Kind: &structpb.Value_StringValue{ - StringValue: ";;;;", - }, - }, - }, - }, - }, - }, - }, - { - name: "all label", - tlsMode: model.IstioMutualTLSModeLabel, - network: "network", - workloadName: "workload", - clusterID: "cluster", - namespace: "default", - labels: labels.Instance{ - model.IstioCanonicalServiceLabelName: "service", - model.IstioCanonicalServiceRevisionLabelName: "v1", - }, - want: &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - EnvoyTransportSocketMetadataKey: { - Fields: map[string]*structpb.Value{ - model.TLSModeLabelShortname: { - Kind: &structpb.Value_StringValue{ - StringValue: model.IstioMutualTLSModeLabel, - }, - }, - }, - }, - IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "workload": { - Kind: &structpb.Value_StringValue{ - StringValue: "workload;default;service;v1;cluster", - }, - }, - }, - }, - }, - }, - }, - { - name: "miss pod label", - tlsMode: model.IstioMutualTLSModeLabel, - network: "network", - workloadName: "workload", - clusterID: "cluster", - namespace: "default", - want: &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - EnvoyTransportSocketMetadataKey: { - Fields: map[string]*structpb.Value{ - model.TLSModeLabelShortname: { - Kind: &structpb.Value_StringValue{ - StringValue: model.IstioMutualTLSModeLabel, - }, - }, - }, - }, - IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "workload": { - Kind: &structpb.Value_StringValue{ - StringValue: "workload;default;;;cluster", - }, - }, - }, - }, - }, - }, - }, - { - name: "miss workload name", - tlsMode: model.IstioMutualTLSModeLabel, - network: "network", - workloadName: "", - clusterID: "cluster", - namespace: "", - want: &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{ - EnvoyTransportSocketMetadataKey: { - Fields: map[string]*structpb.Value{ - model.TLSModeLabelShortname: { - Kind: &structpb.Value_StringValue{ - StringValue: model.IstioMutualTLSModeLabel, - }, - }, - }, - }, - IstioMetadataKey: { - Fields: map[string]*structpb.Value{ - "workload": { - Kind: &structpb.Value_StringValue{ - StringValue: ";;;;cluster", - }, - }, - }, - }, - }, - }, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - if got := BuildLbEndpointMetadata(tt.network, tt.tlsMode, tt.workloadName, tt.namespace, tt.clusterID, tt.labels); !reflect.DeepEqual(got, tt.want) { - t.Errorf("Unexpected Endpoint metadata got %v, want %v", got, tt.want) - } - }) - } -} - -func TestByteCount(t *testing.T) { - cases := []struct { - in int - out string - }{ - {1, "1B"}, - {1000, "1.0kB"}, - {1_000_000, "1.0MB"}, - {1_500_000, "1.5MB"}, - } - for _, tt := range cases { - t.Run(fmt.Sprint(tt.in), func(t *testing.T) { - if got := ByteCount(tt.in); got != tt.out { - t.Fatalf("got %v wanted %v", got, tt.out) - } - }) - } -} - -func TestIPv6Compliant(t *testing.T) { - tests := []struct { - host string - match string - }{ - {"localhost", "localhost"}, - {"127.0.0.1", "127.0.0.1"}, - {"::1", "[::1]"}, - {"2001:4860:0:2001::68", "[2001:4860:0:2001::68]"}, - } - for _, tt := range tests { - t.Run(fmt.Sprint(tt.host), func(t *testing.T) { - if got := IPv6Compliant(tt.host); got != tt.match { - t.Fatalf("got %v wanted %v", got, tt.match) - } - }) - } -} - -func TestDomainName(t *testing.T) { - tests := []struct { - host string - port int - match string - }{ - {"localhost", 3000, "localhost:3000"}, - {"127.0.0.1", 3000, "127.0.0.1:3000"}, - {"::1", 3000, "[::1]:3000"}, - {"2001:4860:0:2001::68", 3000, "[2001:4860:0:2001::68]:3000"}, - } - for _, tt := range tests { - t.Run(fmt.Sprint(tt.host), func(t *testing.T) { - if got := DomainName(tt.host, tt.port); got != tt.match { - t.Fatalf("got %v wanted %v", got, tt.match) - } - }) - } -} diff --git a/pilot/pkg/request/command.go b/pilot/pkg/request/command.go deleted file mode 100644 index d51bc0d06..000000000 --- a/pilot/pkg/request/command.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package request - -import ( - "bytes" - "fmt" - "io" - "net/http" - "strings" -) - -// Command is a wrapper for making http requests to Pilot or Envoy via a CLI command. -type Command struct { - Address string - Client *http.Client -} - -// Do executes an http request using the specified arguments -func (c *Command) Do(method, path, body string) error { - bodyBuffer := bytes.NewBufferString(body) - path = strings.TrimPrefix(path, "/") - url := fmt.Sprintf("http://%v/%v", c.Address, path) - req, err := http.NewRequest(method, url, bodyBuffer) - if err != nil { - return err - } - resp, err := c.Client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - respBody, err := io.ReadAll(resp.Body) - if err != nil { - return err - } - if resp.StatusCode > 399 && resp.StatusCode != 404 { - return fmt.Errorf("received unsuccessful status code %v: %v", resp.StatusCode, string(respBody)) - } - fmt.Println(string(respBody)) - return nil -} diff --git a/pilot/pkg/request/command_test.go b/pilot/pkg/request/command_test.go deleted file mode 100644 index 449725c7f..000000000 --- a/pilot/pkg/request/command_test.go +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package request - -import ( - "fmt" - "io" - "net/http" - "net/http/httptest" - "net/url" - "sync" - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util" -) - -type pilotStubHandler struct { - sync.Mutex - States []pilotStubState -} - -type pilotStubState struct { - wantMethod string - wantPath string - wantBody []byte - StatusCode int - Response string -} - -func (p *pilotStubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - p.Lock() - if r.Method == p.States[0].wantMethod { - if r.URL.Path == p.States[0].wantPath { - defer r.Body.Close() - body, _ := io.ReadAll(r.Body) - if err := util.Compare(body, p.States[0].wantBody); err == nil { - w.WriteHeader(p.States[0].StatusCode) - w.Write([]byte(p.States[0].Response)) - } else { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(fmt.Sprintf("wanted body %q got %q", string(p.States[0].wantBody), string(body)))) - } - } else { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(fmt.Sprintf("wanted path %q got %q", p.States[0].wantPath, r.URL.Path))) - } - } else { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(fmt.Sprintf("wanted method %q got %q", p.States[0].wantMethod, r.Method))) - } - p.States = p.States[1:] - p.Unlock() -} - -func Test_command_do(t *testing.T) { - tests := []struct { - name string - method string - path string - body string - pilotStates []pilotStubState - pilotNotReachable bool - wantError bool - }{ - { - name: "makes a request using passed method, url and body", - method: "POST", - path: "/want/path", - body: "body", - pilotStates: []pilotStubState{ - {StatusCode: 200, Response: "fine", wantMethod: "POST", wantPath: "/want/path", wantBody: []byte("body")}, - }, - }, - { - name: "adds / prefix to path if required", - method: "POST", - path: "want/path", - body: "body", - pilotStates: []pilotStubState{ - {StatusCode: 200, Response: "fine", wantMethod: "POST", wantPath: "/want/path", wantBody: []byte("body")}, - }, - }, - { - name: "handles empty string body in args", - method: "GET", - path: "/want/path", - body: "", - pilotStates: []pilotStubState{ - {StatusCode: 200, Response: "fine", wantMethod: "GET", wantPath: "/want/path", wantBody: nil}, - }, - }, - { - name: "doesn't error on 404", - method: "GET", - path: "/want/path", - body: "", - pilotStates: []pilotStubState{ - {StatusCode: 404, Response: "not-found", wantMethod: "GET", wantPath: "/want/path", wantBody: nil}, - }, - }, - { - name: "errors if Pilot is unreachable", - method: "GET", - path: "/want/path", - pilotNotReachable: true, - wantError: true, - }, - { - name: "errors if Pilot responds with a non success status", - method: "GET", - path: "/not/wanted/path", - body: "", - pilotStates: []pilotStubState{ - {StatusCode: 200, Response: "fine", wantMethod: "GET", wantPath: "/want/path", wantBody: nil}, - }, - wantError: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - pilotStub := httptest.NewServer( - &pilotStubHandler{States: tt.pilotStates}, - ) - stubURL, _ := url.Parse(pilotStub.URL) - if tt.pilotNotReachable { - stubURL, _ = url.Parse("http://notpilot") - } - c := &Command{ - Address: stubURL.Host, - Client: &http.Client{}, - } - err := c.Do(tt.method, tt.path, tt.body) - if (err == nil) && tt.wantError { - t.Errorf("Expected an error but received none") - } else if (err != nil) && !tt.wantError { - t.Errorf("Unexpected err: %v", err) - } - }) - } -} diff --git a/pilot/pkg/security/authn/factory/factory.go b/pilot/pkg/security/authn/factory/factory.go deleted file mode 100644 index b4e091331..000000000 --- a/pilot/pkg/security/authn/factory/factory.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package factory - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/authn" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/authn/v1beta1" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" -) - -// NewPolicyApplier returns the appropriate (policy) applier, depends on the versions of the policy exists -// for the given service instance. -func NewPolicyApplier(push *model.PushContext, namespace string, labels labels.Instance) authn.PolicyApplier { - return v1beta1.NewPolicyApplier( - push.AuthnPolicies.GetRootNamespace(), - push.AuthnPolicies.GetJwtPoliciesForWorkload(namespace, labels), - push.AuthnPolicies.GetPeerAuthenticationsForWorkload(namespace, labels), push) -} diff --git a/pilot/pkg/security/authn/policy_applier.go b/pilot/pkg/security/authn/policy_applier.go deleted file mode 100644 index 0bfb73420..000000000 --- a/pilot/pkg/security/authn/policy_applier.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package authn - -import ( - http_conn "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - "istio.io/api/security/v1beta1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/plugin" -) - -// PolicyApplier is the interface provides essential functionalities to help config Envoy (xDS) to enforce -// authentication policy. Each version of authentication policy will implement this interface. -type PolicyApplier interface { - // InboundMTLSSettings returns inbound mTLS settings for a given workload port - InboundMTLSSettings(endpointPort uint32, node *model.Proxy, trustDomainAliases []string) plugin.MTLSSettings - - // JwtFilter returns the JWT HTTP filter to enforce the underlying authentication policy. - // It may return nil, if no JWT validation is needed. - JwtFilter() *http_conn.HttpFilter - - // AuthNFilter returns the (authn) HTTP filter to enforce the underlying authentication policy. - // It may return nil, if no authentication is needed. - AuthNFilter(forSidecar bool) *http_conn.HttpFilter - - // PortLevelSetting returns port level mTLS settings. - PortLevelSetting() map[uint32]*v1beta1.PeerAuthentication_MutualTLS - - // GetMutualTLSModeForPort gets the mTLS mode for the given port. If there is no port level setting, it - // returns the inherited namespace/mesh level setting. - GetMutualTLSModeForPort(endpointPort uint32) model.MutualTLSMode -} diff --git a/pilot/pkg/security/authn/utils/utils.go b/pilot/pkg/security/authn/utils/utils.go deleted file mode 100644 index a5e17d092..000000000 --- a/pilot/pkg/security/authn/utils/utils.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package utils - -import ( - tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - meshconfig "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - authn_model "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/model" - protovalue "github.com/apache/dubbo-go-pixiu/pkg/proto" -) - -// SupportedCiphers for server side TLS configuration. -var SupportedCiphers = []string{ - "ECDHE-ECDSA-AES256-GCM-SHA384", - "ECDHE-RSA-AES256-GCM-SHA384", - "ECDHE-ECDSA-AES128-GCM-SHA256", - "ECDHE-RSA-AES128-GCM-SHA256", - "AES256-GCM-SHA384", - "AES128-GCM-SHA256", -} - -// BuildInboundTLS returns the TLS context corresponding to the mTLS mode. -func BuildInboundTLS(mTLSMode model.MutualTLSMode, node *model.Proxy, - protocol networking.ListenerProtocol, trustDomainAliases []string, minTLSVersion tls.TlsParameters_TlsProtocol, -) *tls.DownstreamTlsContext { - if mTLSMode == model.MTLSDisable || mTLSMode == model.MTLSUnknown { - return nil - } - ctx := &tls.DownstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{}, - RequireClientCertificate: protovalue.BoolTrue, - } - if protocol == networking.ListenerProtocolTCP { - // For TCP with mTLS, we advertise "istio-peer-exchange" from client and - // expect the same from server. This is so that secure metadata exchange - // transfer can take place between sidecars for TCP with mTLS. - if features.MetadataExchange { - ctx.CommonTlsContext.AlpnProtocols = util.ALPNDownstreamWithMxc - } else { - ctx.CommonTlsContext.AlpnProtocols = util.ALPNDownstream - } - } else { - // Note that in the PERMISSIVE mode, we match filter chain on "istio" ALPN, - // which is used to differentiate between service mesh and legacy traffic. - // - // Client sidecar outbound cluster's TLSContext.ALPN must include "istio". - // - // Server sidecar filter chain's FilterChainMatch.ApplicationProtocols must - // include "istio" for the secure traffic, but its TLSContext.ALPN must not - // include "istio", which would interfere with negotiation of the underlying - // protocol, e.g. HTTP/2. - ctx.CommonTlsContext.AlpnProtocols = util.ALPNHttp - } - - // Set Minimum TLS version to match the default client version and allowed strong cipher suites for sidecars. - ctx.CommonTlsContext.TlsParams = &tls.TlsParameters{ - CipherSuites: SupportedCiphers, - } - ctx.CommonTlsContext.TlsParams.TlsMinimumProtocolVersion = minTLSVersion - ctx.CommonTlsContext.TlsParams.TlsMaximumProtocolVersion = tls.TlsParameters_TLSv1_3 - authn_model.ApplyToCommonTLSContext(ctx.CommonTlsContext, node, []string{}, /*subjectAltNames*/ - trustDomainAliases, ctx.RequireClientCertificate.Value) - return ctx -} - -// GetMinTLSVersion returns the minimum TLS version for workloads based on the mesh config. -func GetMinTLSVersion(ver meshconfig.MeshConfig_TLSConfig_TLSProtocol) tls.TlsParameters_TlsProtocol { - switch ver { - case meshconfig.MeshConfig_TLSConfig_TLSV1_3: - return tls.TlsParameters_TLSv1_3 - default: - return tls.TlsParameters_TLSv1_2 - } -} diff --git a/pilot/pkg/security/authn/utils/utils_test.go b/pilot/pkg/security/authn/utils/utils_test.go deleted file mode 100644 index 55e084557..000000000 --- a/pilot/pkg/security/authn/utils/utils_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package utils - -import ( - "testing" -) - -import ( - tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - meshconfig "istio.io/api/mesh/v1alpha1" -) - -func TestGetMinTLSVersion(t *testing.T) { - tests := []struct { - name string - minTLSVer meshconfig.MeshConfig_TLSConfig_TLSProtocol - expectedMinTLSVer tls.TlsParameters_TlsProtocol - }{ - { - name: "Default TLS versions", - expectedMinTLSVer: tls.TlsParameters_TLSv1_2, - }, - { - name: "Configure minimum TLS version 1.2", - minTLSVer: meshconfig.MeshConfig_TLSConfig_TLSV1_2, - expectedMinTLSVer: tls.TlsParameters_TLSv1_2, - }, - { - name: "Configure minimum TLS version 1.3", - minTLSVer: meshconfig.MeshConfig_TLSConfig_TLSV1_3, - expectedMinTLSVer: tls.TlsParameters_TLSv1_3, - }, - { - name: "Configure minimum TLS version to be auto", - minTLSVer: meshconfig.MeshConfig_TLSConfig_TLS_AUTO, - expectedMinTLSVer: tls.TlsParameters_TLSv1_2, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - minVersion := GetMinTLSVersion(tt.minTLSVer) - if minVersion != tt.expectedMinTLSVer { - t.Errorf("unexpected result: expected min ver %v got %v", - tt.expectedMinTLSVer, minVersion) - } - }) - } -} diff --git a/pilot/pkg/security/authn/v1beta1/policy_applier.go b/pilot/pkg/security/authn/v1beta1/policy_applier.go deleted file mode 100644 index d45b13d4d..000000000 --- a/pilot/pkg/security/authn/v1beta1/policy_applier.go +++ /dev/null @@ -1,472 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1beta1 - -import ( - "fmt" - "net/url" - "sort" - "strconv" - "strings" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - envoy_jwt "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/jwt_authn/v3" - http_conn "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - "google.golang.org/protobuf/types/known/durationpb" - "google.golang.org/protobuf/types/known/emptypb" - authn_alpha "istio.io/api/authentication/v1alpha1" - authn_filter "istio.io/api/envoy/config/filter/http/authn/v2alpha1" - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/api/security/v1beta1" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/extensionproviders" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/plugin" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/authn" - authn_utils "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/authn/utils" - authn_model "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" -) - -var authnLog = log.RegisterScope("authn", "authn debugging", 0) - -// Implementation of authn.PolicyApplier with v1beta1 API. -type v1beta1PolicyApplier struct { - jwtPolicies []*config.Config - - peerPolices []*config.Config - - // processedJwtRules is the consolidate JWT rules from all jwtPolicies. - processedJwtRules []*v1beta1.JWTRule - - consolidatedPeerPolicy *v1beta1.PeerAuthentication - - push *model.PushContext -} - -func (a *v1beta1PolicyApplier) JwtFilter() *http_conn.HttpFilter { - if len(a.processedJwtRules) == 0 { - return nil - } - - filterConfigProto := convertToEnvoyJwtConfig(a.processedJwtRules, a.push) - - if filterConfigProto == nil { - return nil - } - return &http_conn.HttpFilter{ - Name: authn_model.EnvoyJwtFilterName, - ConfigType: &http_conn.HttpFilter_TypedConfig{TypedConfig: util.MessageToAny(filterConfigProto)}, - } -} - -func defaultAuthnFilter() *authn_filter.FilterConfig { - return &authn_filter.FilterConfig{ - Policy: &authn_alpha.Policy{}, - // we can always set this field, it's no-op if mTLS is not used. - SkipValidateTrustDomain: true, - } -} - -func (a *v1beta1PolicyApplier) setAuthnFilterForRequestAuthn(config *authn_filter.FilterConfig) *authn_filter.FilterConfig { - if len(a.processedJwtRules) == 0 { - // (beta) RequestAuthentication is not set for workload, do nothing. - authnLog.Debug("AuthnFilter: RequestAuthentication (beta policy) not found, keep settings with alpha API") - return config - } - - if config == nil { - config = defaultAuthnFilter() - } - - // This is obsoleted and not needed (payload is extracted from metadata). Reset the field to remove - // any artifacts from alpha applier. - config.JwtOutputPayloadLocations = nil - p := config.Policy - // Reset origins to use with beta API - // nolint: staticcheck - p.Origins = []*authn_alpha.OriginAuthenticationMethod{} - // Always set to true for beta API, as it doesn't doe rejection on missing token. - // nolint: staticcheck - p.OriginIsOptional = true - - // Always bind request.auth.principal from JWT origin. In v2 policy, authorization config specifies what principal to - // choose from instead, rather than in authn config. - // nolint: staticcheck - p.PrincipalBinding = authn_alpha.PrincipalBinding_USE_ORIGIN - // nolint: staticcheck - for _, jwt := range a.processedJwtRules { - p.Origins = append(p.Origins, &authn_alpha.OriginAuthenticationMethod{ - Jwt: &authn_alpha.Jwt{ - // used for getting the filter data, and all other fields are irrelevant. - Issuer: jwt.GetIssuer(), - }, - }) - } - return config -} - -// AuthNFilter returns the Istio authn filter config: -// - If RequestAuthentication is used, it overwrite the settings for request principal validation and extraction based on the new API. -// - If RequestAuthentication is used, principal binding is always set to ORIGIN. -func (a *v1beta1PolicyApplier) AuthNFilter(forSidecar bool) *http_conn.HttpFilter { - var filterConfigProto *authn_filter.FilterConfig - - // Override the config with request authentication, if applicable. - filterConfigProto = a.setAuthnFilterForRequestAuthn(filterConfigProto) - - if filterConfigProto == nil { - return nil - } - // disable clear route cache for sidecars because the JWT claim based routing is only supported on gateways. - filterConfigProto.DisableClearRouteCache = forSidecar - - // Note: in previous Istio versions, the authn filter also handled PeerAuthentication, to extract principal. - // This has been modified to rely on the TCP filter - - return &http_conn.HttpFilter{ - Name: authn_model.AuthnFilterName, - ConfigType: &http_conn.HttpFilter_TypedConfig{TypedConfig: util.MessageToAny(filterConfigProto)}, - } -} - -func (a *v1beta1PolicyApplier) InboundMTLSSettings(endpointPort uint32, node *model.Proxy, trustDomainAliases []string) plugin.MTLSSettings { - effectiveMTLSMode := a.GetMutualTLSModeForPort(endpointPort) - authnLog.Debugf("InboundFilterChain: build inbound filter change for %v:%d in %s mode", node.ID, endpointPort, effectiveMTLSMode) - var mc *meshconfig.MeshConfig - if a.push != nil { - mc = a.push.Mesh - } - // Configure TLS version based on meshconfig TLS API. - minTLSVersion := authn_utils.GetMinTLSVersion(mc.GetMeshMTLS().GetMinProtocolVersion()) - return plugin.MTLSSettings{ - Port: endpointPort, - Mode: effectiveMTLSMode, - TCP: authn_utils.BuildInboundTLS(effectiveMTLSMode, node, networking.ListenerProtocolTCP, - trustDomainAliases, minTLSVersion), - HTTP: authn_utils.BuildInboundTLS(effectiveMTLSMode, node, networking.ListenerProtocolHTTP, - trustDomainAliases, minTLSVersion), - } -} - -// NewPolicyApplier returns new applier for v1beta1 authentication policies. -func NewPolicyApplier(rootNamespace string, - jwtPolicies []*config.Config, - peerPolicies []*config.Config, - push *model.PushContext, -) authn.PolicyApplier { - processedJwtRules := []*v1beta1.JWTRule{} - - // TODO(diemtvu) should we need to deduplicate JWT with the same issuer. - // https://github.com/istio/istio/issues/19245 - for idx := range jwtPolicies { - spec := jwtPolicies[idx].Spec.(*v1beta1.RequestAuthentication) - processedJwtRules = append(processedJwtRules, spec.JwtRules...) - } - - // Sort the jwt rules by the issuer alphabetically to make the later-on generated filter - // config deterministic. - sort.Slice(processedJwtRules, func(i, j int) bool { - return strings.Compare( - processedJwtRules[i].GetIssuer(), processedJwtRules[j].GetIssuer()) < 0 - }) - - return &v1beta1PolicyApplier{ - jwtPolicies: jwtPolicies, - peerPolices: peerPolicies, - processedJwtRules: processedJwtRules, - consolidatedPeerPolicy: ComposePeerAuthentication(rootNamespace, peerPolicies), - push: push, - } -} - -// convertToEnvoyJwtConfig converts a list of JWT rules into Envoy JWT filter config to enforce it. -// Each rule is expected corresponding to one JWT issuer (provider). -// The behavior of the filter should reject all requests with invalid token. On the other hand, -// if no token provided, the request is allowed. -func convertToEnvoyJwtConfig(jwtRules []*v1beta1.JWTRule, push *model.PushContext) *envoy_jwt.JwtAuthentication { - if len(jwtRules) == 0 { - return nil - } - - providers := map[string]*envoy_jwt.JwtProvider{} - // Each element of innerAndList is the requirement for each provider, in the form of - // {provider OR `allow_missing`} - // This list will be ANDed (if have more than one provider) for the final requirement. - innerAndList := []*envoy_jwt.JwtRequirement{} - - // This is an (or) list for all providers. This will be OR with the innerAndList above so - // it can pass the requirement in the case that providers share the same location. - outterOrList := []*envoy_jwt.JwtRequirement{} - - for i, jwtRule := range jwtRules { - provider := &envoy_jwt.JwtProvider{ - Issuer: jwtRule.Issuer, - Audiences: jwtRule.Audiences, - Forward: jwtRule.ForwardOriginalToken, - ForwardPayloadHeader: jwtRule.OutputPayloadToHeader, - PayloadInMetadata: jwtRule.Issuer, - } - - for _, location := range jwtRule.FromHeaders { - provider.FromHeaders = append(provider.FromHeaders, &envoy_jwt.JwtHeader{ - Name: location.Name, - ValuePrefix: location.Prefix, - }) - } - provider.FromParams = jwtRule.FromParams - - if features.EnableRemoteJwks && jwtRule.JwksUri != "" { - // Use remote jwks if jwksUri is non empty. Parse the jwksUri to get the cluster name, - // generate the jwt filter config using remoteJwks. - // If failed to parse the cluster name, fallback to let istiod to fetch the jwksUri. - // TODO: Implement the logic to auto-generate the cluster so that when the flag is enabled, - // it will always let envoy to fetch the jwks for consistent behavior. - u, _ := url.Parse(jwtRule.JwksUri) - hostAndPort := strings.Split(u.Host, ":") - host := hostAndPort[0] - // TODO: Default port based on scheme ? - port := 80 - if len(hostAndPort) == 2 { - var err error - if port, err = strconv.Atoi(hostAndPort[1]); err != nil { - port = 80 // If port is not specified or there is an error in parsing default to 80. - } - } - _, cluster, err := extensionproviders.LookupCluster(push, host, port) - - if err == nil && len(cluster) > 0 { - // This is a case of URI pointing to mesh cluster. Setup Remote Jwks and let Envoy fetch the key. - provider.JwksSourceSpecifier = &envoy_jwt.JwtProvider_RemoteJwks{ - RemoteJwks: &envoy_jwt.RemoteJwks{ - HttpUri: &core.HttpUri{ - Uri: jwtRule.JwksUri, - HttpUpstreamType: &core.HttpUri_Cluster{ - Cluster: cluster, - }, - Timeout: &durationpb.Duration{Seconds: 5}, - }, - CacheDuration: &durationpb.Duration{Seconds: 5 * 60}, - }, - } - } else { - provider.JwksSourceSpecifier = push.JwtKeyResolver.BuildLocalJwks(jwtRule.JwksUri, jwtRule.Issuer, "") - } - } else { - // Use inline jwks as existing flow, either jwtRule.jwks is non empty or let istiod to fetch the jwtRule.jwksUri - provider.JwksSourceSpecifier = push.JwtKeyResolver.BuildLocalJwks(jwtRule.JwksUri, jwtRule.Issuer, jwtRule.Jwks) - } - - name := fmt.Sprintf("origins-%d", i) - providers[name] = provider - innerAndList = append(innerAndList, &envoy_jwt.JwtRequirement{ - RequiresType: &envoy_jwt.JwtRequirement_RequiresAny{ - RequiresAny: &envoy_jwt.JwtRequirementOrList{ - Requirements: []*envoy_jwt.JwtRequirement{ - { - RequiresType: &envoy_jwt.JwtRequirement_ProviderName{ - ProviderName: name, - }, - }, - { - RequiresType: &envoy_jwt.JwtRequirement_AllowMissing{ - AllowMissing: &emptypb.Empty{}, - }, - }, - }, - }, - }, - }) - outterOrList = append(outterOrList, &envoy_jwt.JwtRequirement{ - RequiresType: &envoy_jwt.JwtRequirement_ProviderName{ - ProviderName: name, - }, - }) - } - - // If there is only one provider, simply use an OR of {provider, `allow_missing`}. - if len(innerAndList) == 1 { - return &envoy_jwt.JwtAuthentication{ - Rules: []*envoy_jwt.RequirementRule{ - { - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_Prefix{ - Prefix: "/", - }, - }, - RequirementType: &envoy_jwt.RequirementRule_Requires{ - Requires: innerAndList[0], - }, - }, - }, - Providers: providers, - BypassCorsPreflight: true, - } - } - - // If there are more than one provider, filter should OR of - // {P1, P2 .., AND of {OR{P1, allow_missing}, OR{P2, allow_missing} ...}} - // where the innerAnd enforce a token, if provided, must be valid, and the - // outer OR aids the case where providers share the same location (as - // it will always fail with the innerAND). - outterOrList = append(outterOrList, &envoy_jwt.JwtRequirement{ - RequiresType: &envoy_jwt.JwtRequirement_RequiresAll{ - RequiresAll: &envoy_jwt.JwtRequirementAndList{ - Requirements: innerAndList, - }, - }, - }) - - return &envoy_jwt.JwtAuthentication{ - Rules: []*envoy_jwt.RequirementRule{ - { - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_Prefix{ - Prefix: "/", - }, - }, - RequirementType: &envoy_jwt.RequirementRule_Requires{ - Requires: &envoy_jwt.JwtRequirement{ - RequiresType: &envoy_jwt.JwtRequirement_RequiresAny{ - RequiresAny: &envoy_jwt.JwtRequirementOrList{ - Requirements: outterOrList, - }, - }, - }, - }, - }, - }, - Providers: providers, - BypassCorsPreflight: true, - } -} - -func (a *v1beta1PolicyApplier) PortLevelSetting() map[uint32]*v1beta1.PeerAuthentication_MutualTLS { - return a.consolidatedPeerPolicy.PortLevelMtls -} - -func (a *v1beta1PolicyApplier) GetMutualTLSModeForPort(endpointPort uint32) model.MutualTLSMode { - if a.consolidatedPeerPolicy.PortLevelMtls != nil { - if portMtls, ok := a.consolidatedPeerPolicy.PortLevelMtls[endpointPort]; ok { - return getMutualTLSMode(portMtls) - } - } - - return getMutualTLSMode(a.consolidatedPeerPolicy.Mtls) -} - -// getMutualTLSMode returns the MutualTLSMode enum corresponding peer MutualTLS settings. -// Input cannot be nil. -func getMutualTLSMode(mtls *v1beta1.PeerAuthentication_MutualTLS) model.MutualTLSMode { - return model.ConvertToMutualTLSMode(mtls.Mode) -} - -// ComposePeerAuthentication returns the effective PeerAuthentication given the list of applicable -// configs. This list should contains at most 1 mesh-level and 1 namespace-level configs. -// Workload-level configs should not be in root namespace (this should be guaranteed by the caller, -// though they will be safely ignored in this function). If the input config list is empty, returns -// a default policy set to a PERMISSIVE. -// If there is at least one applicable config, returns should not be nil, and is a combined policy -// based on following rules: -// - It should have the setting from the most narrow scope (i.e workload-level is preferred over -// namespace-level, which is preferred over mesh-level). -// - When there are more than one policy in the same scope (i.e workload-level), the oldest one win. -// - UNSET will be replaced with the setting from the parent. I.e UNSET port-level config will be -// replaced with config from workload-level, UNSET in workload-level config will be replaced with -// one in namespace-level and so on. -func ComposePeerAuthentication(rootNamespace string, configs []*config.Config) *v1beta1.PeerAuthentication { - var meshCfg, namespaceCfg, workloadCfg *config.Config - - // Initial outputPolicy is set to a PERMISSIVE. - outputPolicy := v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_PERMISSIVE, - }, - } - - for _, cfg := range configs { - spec := cfg.Spec.(*v1beta1.PeerAuthentication) - if spec.Selector == nil || len(spec.Selector.MatchLabels) == 0 { - // Namespace-level or mesh-level policy - if cfg.Namespace == rootNamespace { - if meshCfg == nil || cfg.CreationTimestamp.Before(meshCfg.CreationTimestamp) { - authnLog.Debugf("Switch selected mesh policy to %s.%s (%v)", cfg.Name, cfg.Namespace, cfg.CreationTimestamp) - meshCfg = cfg - } - } else { - if namespaceCfg == nil || cfg.CreationTimestamp.Before(namespaceCfg.CreationTimestamp) { - authnLog.Debugf("Switch selected namespace policy to %s.%s (%v)", cfg.Name, cfg.Namespace, cfg.CreationTimestamp) - namespaceCfg = cfg - } - } - } else if cfg.Namespace != rootNamespace { - // Workload-level policy, aka the one with selector and not in root namespace. - if workloadCfg == nil || cfg.CreationTimestamp.Before(workloadCfg.CreationTimestamp) { - authnLog.Debugf("Switch selected workload policy to %s.%s (%v)", cfg.Name, cfg.Namespace, cfg.CreationTimestamp) - workloadCfg = cfg - } - } - } - - // Process in mesh, namespace, workload order to resolve inheritance (UNSET) - - if meshCfg != nil && !isMtlsModeUnset(meshCfg.Spec.(*v1beta1.PeerAuthentication).Mtls) { - // If mesh policy is defined, update parent policy to mesh policy. - outputPolicy.Mtls = meshCfg.Spec.(*v1beta1.PeerAuthentication).Mtls - } - - if namespaceCfg != nil && !isMtlsModeUnset(namespaceCfg.Spec.(*v1beta1.PeerAuthentication).Mtls) { - // If namespace policy is defined, update output policy to namespace policy. This means namespace - // policy overwrite mesh policy. - outputPolicy.Mtls = namespaceCfg.Spec.(*v1beta1.PeerAuthentication).Mtls - } - - var workloadPolicy *v1beta1.PeerAuthentication - if workloadCfg != nil { - workloadPolicy = workloadCfg.Spec.(*v1beta1.PeerAuthentication) - } - - if workloadPolicy != nil && !isMtlsModeUnset(workloadPolicy.Mtls) { - // If workload policy is defined, update parent policy to workload policy. - outputPolicy.Mtls = workloadPolicy.Mtls - } - - if workloadPolicy != nil && workloadPolicy.PortLevelMtls != nil { - outputPolicy.PortLevelMtls = make(map[uint32]*v1beta1.PeerAuthentication_MutualTLS, len(workloadPolicy.PortLevelMtls)) - for port, mtls := range workloadPolicy.PortLevelMtls { - if isMtlsModeUnset(mtls) { - // Inherit from workload level. - outputPolicy.PortLevelMtls[port] = outputPolicy.Mtls - } else { - outputPolicy.PortLevelMtls[port] = mtls - } - } - } - - return &outputPolicy -} - -func isMtlsModeUnset(mtls *v1beta1.PeerAuthentication_MutualTLS) bool { - return mtls == nil || mtls.Mode == v1beta1.PeerAuthentication_MutualTLS_UNSET -} diff --git a/pilot/pkg/security/authn/v1beta1/policy_applier_test.go b/pilot/pkg/security/authn/v1beta1/policy_applier_test.go deleted file mode 100644 index 7830b48aa..000000000 --- a/pilot/pkg/security/authn/v1beta1/policy_applier_test.go +++ /dev/null @@ -1,2116 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v1beta1 - -import ( - "reflect" - "testing" - "time" -) - -import ( - "github.com/davecgh/go-spew/spew" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - envoy_jwt "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/jwt_authn/v3" - http_conn "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/testing/protocmp" - "google.golang.org/protobuf/types/known/durationpb" - "google.golang.org/protobuf/types/known/emptypb" - authn_alpha "istio.io/api/authentication/v1alpha1" - authn_filter "istio.io/api/envoy/config/filter/http/authn/v2alpha1" - "istio.io/api/security/v1beta1" - type_beta "istio.io/api/type/v1beta1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model/test" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/plugin" - pilotutil "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - protovalue "github.com/apache/dubbo-go-pixiu/pkg/proto" - istiotest "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -func TestJwtFilter(t *testing.T) { - ms, err := test.StartNewServer() - if err != nil { - t.Fatal("failed to start a mock server") - } - - jwksURI := ms.URL + "/oauth2/v3/certs" - - cases := []struct { - name string - in []*config.Config - enableRemoteJwks bool - expected *http_conn.HttpFilter - }{ - { - name: "No policy", - in: []*config.Config{}, - expected: nil, - }, - { - name: "Empty policy", - in: []*config.Config{ - { - Spec: &v1beta1.RequestAuthentication{}, - }, - }, - expected: nil, - }, - { - name: "Single JWT policy", - in: []*config.Config{ - { - Spec: &v1beta1.RequestAuthentication{ - JwtRules: []*v1beta1.JWTRule{ - { - Issuer: "https://secret.foo.com", - JwksUri: jwksURI, - }, - }, - }, - }, - }, - expected: &http_conn.HttpFilter{ - Name: "envoy.filters.http.jwt_authn", - ConfigType: &http_conn.HttpFilter_TypedConfig{ - TypedConfig: pilotutil.MessageToAny( - &envoy_jwt.JwtAuthentication{ - Rules: []*envoy_jwt.RequirementRule{ - { - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_Prefix{ - Prefix: "/", - }, - }, - RequirementType: &envoy_jwt.RequirementRule_Requires{ - Requires: &envoy_jwt.JwtRequirement{ - RequiresType: &envoy_jwt.JwtRequirement_RequiresAny{ - RequiresAny: &envoy_jwt.JwtRequirementOrList{ - Requirements: []*envoy_jwt.JwtRequirement{ - { - RequiresType: &envoy_jwt.JwtRequirement_ProviderName{ - ProviderName: "origins-0", - }, - }, - { - RequiresType: &envoy_jwt.JwtRequirement_AllowMissing{ - AllowMissing: &emptypb.Empty{}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - Providers: map[string]*envoy_jwt.JwtProvider{ - "origins-0": { - Issuer: "https://secret.foo.com", - JwksSourceSpecifier: &envoy_jwt.JwtProvider_LocalJwks{ - LocalJwks: &core.DataSource{ - Specifier: &core.DataSource_InlineString{ - InlineString: test.JwtPubKey1, - }, - }, - }, - Forward: false, - PayloadInMetadata: "https://secret.foo.com", - }, - }, - BypassCorsPreflight: true, - }), - }, - }, - }, - { - name: "JWT policy with Mesh cluster as issuer and remote jwks enabled", - in: []*config.Config{ - { - Spec: &v1beta1.RequestAuthentication{ - JwtRules: []*v1beta1.JWTRule{ - { - Issuer: "mesh cluster", - JwksUri: "http://jwt-token-issuer.mesh:7443/jwks", - }, - }, - }, - }, - }, - enableRemoteJwks: true, - expected: &http_conn.HttpFilter{ - Name: "envoy.filters.http.jwt_authn", - ConfigType: &http_conn.HttpFilter_TypedConfig{ - TypedConfig: pilotutil.MessageToAny( - &envoy_jwt.JwtAuthentication{ - Rules: []*envoy_jwt.RequirementRule{ - { - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_Prefix{ - Prefix: "/", - }, - }, - RequirementType: &envoy_jwt.RequirementRule_Requires{ - Requires: &envoy_jwt.JwtRequirement{ - RequiresType: &envoy_jwt.JwtRequirement_RequiresAny{ - RequiresAny: &envoy_jwt.JwtRequirementOrList{ - Requirements: []*envoy_jwt.JwtRequirement{ - { - RequiresType: &envoy_jwt.JwtRequirement_ProviderName{ - ProviderName: "origins-0", - }, - }, - { - RequiresType: &envoy_jwt.JwtRequirement_AllowMissing{ - AllowMissing: &emptypb.Empty{}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - Providers: map[string]*envoy_jwt.JwtProvider{ - "origins-0": { - Issuer: "mesh cluster", - JwksSourceSpecifier: &envoy_jwt.JwtProvider_RemoteJwks{ - RemoteJwks: &envoy_jwt.RemoteJwks{ - HttpUri: &core.HttpUri{ - Uri: "http://jwt-token-issuer.mesh:7443/jwks", - HttpUpstreamType: &core.HttpUri_Cluster{ - Cluster: "outbound|7443||jwt-token-issuer.mesh.svc.cluster.local", - }, - Timeout: &durationpb.Duration{Seconds: 5}, - }, - CacheDuration: &durationpb.Duration{Seconds: 5 * 60}, - }, - }, - Forward: false, - PayloadInMetadata: "mesh cluster", - }, - }, - BypassCorsPreflight: true, - }), - }, - }, - }, - { - name: "JWT policy with non Mesh cluster as issuer and remote jwks enabled", - in: []*config.Config{ - { - Spec: &v1beta1.RequestAuthentication{ - JwtRules: []*v1beta1.JWTRule{ - { - Issuer: "invalid|7443|", - JwksUri: jwksURI, - }, - }, - }, - }, - }, - enableRemoteJwks: true, - expected: &http_conn.HttpFilter{ - Name: "envoy.filters.http.jwt_authn", - ConfigType: &http_conn.HttpFilter_TypedConfig{ - TypedConfig: pilotutil.MessageToAny( - &envoy_jwt.JwtAuthentication{ - Rules: []*envoy_jwt.RequirementRule{ - { - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_Prefix{ - Prefix: "/", - }, - }, - RequirementType: &envoy_jwt.RequirementRule_Requires{ - Requires: &envoy_jwt.JwtRequirement{ - RequiresType: &envoy_jwt.JwtRequirement_RequiresAny{ - RequiresAny: &envoy_jwt.JwtRequirementOrList{ - Requirements: []*envoy_jwt.JwtRequirement{ - { - RequiresType: &envoy_jwt.JwtRequirement_ProviderName{ - ProviderName: "origins-0", - }, - }, - { - RequiresType: &envoy_jwt.JwtRequirement_AllowMissing{ - AllowMissing: &emptypb.Empty{}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - Providers: map[string]*envoy_jwt.JwtProvider{ - "origins-0": { - Issuer: "invalid|7443|", - JwksSourceSpecifier: &envoy_jwt.JwtProvider_LocalJwks{ - LocalJwks: &core.DataSource{ - Specifier: &core.DataSource_InlineString{ - InlineString: test.JwtPubKey2, - }, - }, - }, - Forward: false, - PayloadInMetadata: "invalid|7443|", - }, - }, - BypassCorsPreflight: true, - }), - }, - }, - }, - { - name: "Multi JWTs policy", - in: []*config.Config{ - { - Spec: &v1beta1.RequestAuthentication{ - JwtRules: []*v1beta1.JWTRule{ - { - Issuer: "https://secret.foo.com", - JwksUri: jwksURI, - }, - }, - }, - }, - { - Spec: &v1beta1.RequestAuthentication{}, - }, - { - Spec: &v1beta1.RequestAuthentication{ - JwtRules: []*v1beta1.JWTRule{ - { - Issuer: "https://secret.bar.com", - Jwks: "jwks-inline-data", - }, - }, - }, - }, - }, - expected: &http_conn.HttpFilter{ - Name: "envoy.filters.http.jwt_authn", - ConfigType: &http_conn.HttpFilter_TypedConfig{ - TypedConfig: pilotutil.MessageToAny( - &envoy_jwt.JwtAuthentication{ - Rules: []*envoy_jwt.RequirementRule{ - { - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_Prefix{ - Prefix: "/", - }, - }, - RequirementType: &envoy_jwt.RequirementRule_Requires{ - Requires: &envoy_jwt.JwtRequirement{ - RequiresType: &envoy_jwt.JwtRequirement_RequiresAny{ - RequiresAny: &envoy_jwt.JwtRequirementOrList{ - Requirements: []*envoy_jwt.JwtRequirement{ - { - RequiresType: &envoy_jwt.JwtRequirement_ProviderName{ - ProviderName: "origins-0", - }, - }, - { - RequiresType: &envoy_jwt.JwtRequirement_ProviderName{ - ProviderName: "origins-1", - }, - }, - { - RequiresType: &envoy_jwt.JwtRequirement_RequiresAll{ - RequiresAll: &envoy_jwt.JwtRequirementAndList{ - Requirements: []*envoy_jwt.JwtRequirement{ - { - RequiresType: &envoy_jwt.JwtRequirement_RequiresAny{ - RequiresAny: &envoy_jwt.JwtRequirementOrList{ - Requirements: []*envoy_jwt.JwtRequirement{ - { - RequiresType: &envoy_jwt.JwtRequirement_ProviderName{ - ProviderName: "origins-0", - }, - }, - { - RequiresType: &envoy_jwt.JwtRequirement_AllowMissing{ - AllowMissing: &emptypb.Empty{}, - }, - }, - }, - }, - }, - }, - { - RequiresType: &envoy_jwt.JwtRequirement_RequiresAny{ - RequiresAny: &envoy_jwt.JwtRequirementOrList{ - Requirements: []*envoy_jwt.JwtRequirement{ - { - RequiresType: &envoy_jwt.JwtRequirement_ProviderName{ - ProviderName: "origins-1", - }, - }, - { - RequiresType: &envoy_jwt.JwtRequirement_AllowMissing{ - AllowMissing: &emptypb.Empty{}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - Providers: map[string]*envoy_jwt.JwtProvider{ - "origins-0": { - Issuer: "https://secret.bar.com", - JwksSourceSpecifier: &envoy_jwt.JwtProvider_LocalJwks{ - LocalJwks: &core.DataSource{ - Specifier: &core.DataSource_InlineString{ - InlineString: "jwks-inline-data", - }, - }, - }, - Forward: false, - PayloadInMetadata: "https://secret.bar.com", - }, - "origins-1": { - Issuer: "https://secret.foo.com", - JwksSourceSpecifier: &envoy_jwt.JwtProvider_LocalJwks{ - LocalJwks: &core.DataSource{ - Specifier: &core.DataSource_InlineString{ - InlineString: test.JwtPubKey1, - }, - }, - }, - Forward: false, - PayloadInMetadata: "https://secret.foo.com", - }, - }, - BypassCorsPreflight: true, - }), - }, - }, - }, - { - name: "JWT policy with inline Jwks", - in: []*config.Config{ - { - Spec: &v1beta1.RequestAuthentication{ - JwtRules: []*v1beta1.JWTRule{ - { - Issuer: "https://secret.foo.com", - Jwks: "inline-jwks-data", - }, - }, - }, - }, - }, - expected: &http_conn.HttpFilter{ - Name: "envoy.filters.http.jwt_authn", - ConfigType: &http_conn.HttpFilter_TypedConfig{ - TypedConfig: pilotutil.MessageToAny( - &envoy_jwt.JwtAuthentication{ - Rules: []*envoy_jwt.RequirementRule{ - { - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_Prefix{ - Prefix: "/", - }, - }, - RequirementType: &envoy_jwt.RequirementRule_Requires{ - Requires: &envoy_jwt.JwtRequirement{ - RequiresType: &envoy_jwt.JwtRequirement_RequiresAny{ - RequiresAny: &envoy_jwt.JwtRequirementOrList{ - Requirements: []*envoy_jwt.JwtRequirement{ - { - RequiresType: &envoy_jwt.JwtRequirement_ProviderName{ - ProviderName: "origins-0", - }, - }, - { - RequiresType: &envoy_jwt.JwtRequirement_AllowMissing{ - AllowMissing: &emptypb.Empty{}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - Providers: map[string]*envoy_jwt.JwtProvider{ - "origins-0": { - Issuer: "https://secret.foo.com", - JwksSourceSpecifier: &envoy_jwt.JwtProvider_LocalJwks{ - LocalJwks: &core.DataSource{ - Specifier: &core.DataSource_InlineString{ - InlineString: "inline-jwks-data", - }, - }, - }, - Forward: false, - PayloadInMetadata: "https://secret.foo.com", - }, - }, - BypassCorsPreflight: true, - }), - }, - }, - }, - { - name: "JWT policy with bad Jwks URI", - in: []*config.Config{ - { - Spec: &v1beta1.RequestAuthentication{ - JwtRules: []*v1beta1.JWTRule{ - { - Issuer: "https://secret.foo.com", - JwksUri: "http://site.not.exist", - }, - }, - }, - }, - }, - expected: &http_conn.HttpFilter{ - Name: "envoy.filters.http.jwt_authn", - ConfigType: &http_conn.HttpFilter_TypedConfig{ - TypedConfig: pilotutil.MessageToAny( - &envoy_jwt.JwtAuthentication{ - Rules: []*envoy_jwt.RequirementRule{ - { - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_Prefix{ - Prefix: "/", - }, - }, - RequirementType: &envoy_jwt.RequirementRule_Requires{ - Requires: &envoy_jwt.JwtRequirement{ - RequiresType: &envoy_jwt.JwtRequirement_RequiresAny{ - RequiresAny: &envoy_jwt.JwtRequirementOrList{ - Requirements: []*envoy_jwt.JwtRequirement{ - { - RequiresType: &envoy_jwt.JwtRequirement_ProviderName{ - ProviderName: "origins-0", - }, - }, - { - RequiresType: &envoy_jwt.JwtRequirement_AllowMissing{ - AllowMissing: &emptypb.Empty{}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - Providers: map[string]*envoy_jwt.JwtProvider{ - "origins-0": { - Issuer: "https://secret.foo.com", - JwksSourceSpecifier: &envoy_jwt.JwtProvider_LocalJwks{ - LocalJwks: &core.DataSource{ - Specifier: &core.DataSource_InlineString{ - InlineString: model.CreateFakeJwks("http://site.not.exist"), - }, - }, - }, - Forward: false, - PayloadInMetadata: "https://secret.foo.com", - }, - }, - BypassCorsPreflight: true, - }), - }, - }, - }, - { - name: "Forward original token", - in: []*config.Config{ - { - Spec: &v1beta1.RequestAuthentication{ - JwtRules: []*v1beta1.JWTRule{ - { - Issuer: "https://secret.foo.com", - JwksUri: jwksURI, - ForwardOriginalToken: true, - }, - }, - }, - }, - }, - expected: &http_conn.HttpFilter{ - Name: "envoy.filters.http.jwt_authn", - ConfigType: &http_conn.HttpFilter_TypedConfig{ - TypedConfig: pilotutil.MessageToAny( - &envoy_jwt.JwtAuthentication{ - Rules: []*envoy_jwt.RequirementRule{ - { - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_Prefix{ - Prefix: "/", - }, - }, - RequirementType: &envoy_jwt.RequirementRule_Requires{ - Requires: &envoy_jwt.JwtRequirement{ - RequiresType: &envoy_jwt.JwtRequirement_RequiresAny{ - RequiresAny: &envoy_jwt.JwtRequirementOrList{ - Requirements: []*envoy_jwt.JwtRequirement{ - { - RequiresType: &envoy_jwt.JwtRequirement_ProviderName{ - ProviderName: "origins-0", - }, - }, - { - RequiresType: &envoy_jwt.JwtRequirement_AllowMissing{ - AllowMissing: &emptypb.Empty{}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - Providers: map[string]*envoy_jwt.JwtProvider{ - "origins-0": { - Issuer: "https://secret.foo.com", - JwksSourceSpecifier: &envoy_jwt.JwtProvider_LocalJwks{ - LocalJwks: &core.DataSource{ - Specifier: &core.DataSource_InlineString{ - InlineString: test.JwtPubKey1, - }, - }, - }, - Forward: true, - PayloadInMetadata: "https://secret.foo.com", - }, - }, - BypassCorsPreflight: true, - }), - }, - }, - }, - { - name: "Output payload", - in: []*config.Config{ - { - Spec: &v1beta1.RequestAuthentication{ - JwtRules: []*v1beta1.JWTRule{ - { - Issuer: "https://secret.foo.com", - JwksUri: jwksURI, - ForwardOriginalToken: true, - OutputPayloadToHeader: "x-foo", - }, - }, - }, - }, - }, - expected: &http_conn.HttpFilter{ - Name: "envoy.filters.http.jwt_authn", - ConfigType: &http_conn.HttpFilter_TypedConfig{ - TypedConfig: pilotutil.MessageToAny( - &envoy_jwt.JwtAuthentication{ - Rules: []*envoy_jwt.RequirementRule{ - { - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_Prefix{ - Prefix: "/", - }, - }, - RequirementType: &envoy_jwt.RequirementRule_Requires{ - Requires: &envoy_jwt.JwtRequirement{ - RequiresType: &envoy_jwt.JwtRequirement_RequiresAny{ - RequiresAny: &envoy_jwt.JwtRequirementOrList{ - Requirements: []*envoy_jwt.JwtRequirement{ - { - RequiresType: &envoy_jwt.JwtRequirement_ProviderName{ - ProviderName: "origins-0", - }, - }, - { - RequiresType: &envoy_jwt.JwtRequirement_AllowMissing{ - AllowMissing: &emptypb.Empty{}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - Providers: map[string]*envoy_jwt.JwtProvider{ - "origins-0": { - Issuer: "https://secret.foo.com", - JwksSourceSpecifier: &envoy_jwt.JwtProvider_LocalJwks{ - LocalJwks: &core.DataSource{ - Specifier: &core.DataSource_InlineString{ - InlineString: test.JwtPubKey1, - }, - }, - }, - Forward: true, - ForwardPayloadHeader: "x-foo", - PayloadInMetadata: "https://secret.foo.com", - }, - }, - BypassCorsPreflight: true, - }), - }, - }, - }, - } - - push := model.NewPushContext() - push.JwtKeyResolver = model.NewJwksResolver( - model.JwtPubKeyEvictionDuration, model.JwtPubKeyRefreshInterval, - model.JwtPubKeyRefreshIntervalOnFailure, model.JwtPubKeyRetryInterval) - defer push.JwtKeyResolver.Close() - - push.ServiceIndex.HostnameAndNamespace[host.Name("jwt-token-issuer.mesh")] = map[string]*model.Service{} - push.ServiceIndex.HostnameAndNamespace[host.Name("jwt-token-issuer.mesh")]["mesh"] = &model.Service{ - Hostname: "jwt-token-issuer.mesh.svc.cluster.local", - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - istiotest.SetBoolForTest(t, &features.EnableRemoteJwks, c.enableRemoteJwks) - if got := NewPolicyApplier("root-namespace", c.in, nil, push).JwtFilter(); !reflect.DeepEqual(c.expected, got) { - t.Errorf("got:\n%s\nwanted:\n%s", spew.Sdump(got), spew.Sdump(c.expected)) - } - }) - } -} - -func TestConvertToEnvoyJwtConfig(t *testing.T) { - ms, err := test.StartNewServer() - if err != nil { - t.Fatal("failed to start a mock server") - } - - jwksURI := ms.URL + "/oauth2/v3/certs" - - cases := []struct { - name string - in []*v1beta1.JWTRule - expected *envoy_jwt.JwtAuthentication - }{ - { - name: "No rule", - in: []*v1beta1.JWTRule{}, - expected: nil, - }, - { - name: "Single JWT rule", - in: []*v1beta1.JWTRule{ - { - Issuer: "https://secret.foo.com", - JwksUri: jwksURI, - }, - }, - expected: &envoy_jwt.JwtAuthentication{ - Rules: []*envoy_jwt.RequirementRule{ - { - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_Prefix{ - Prefix: "/", - }, - }, - RequirementType: &envoy_jwt.RequirementRule_Requires{ - Requires: &envoy_jwt.JwtRequirement{ - RequiresType: &envoy_jwt.JwtRequirement_RequiresAny{ - RequiresAny: &envoy_jwt.JwtRequirementOrList{ - Requirements: []*envoy_jwt.JwtRequirement{ - { - RequiresType: &envoy_jwt.JwtRequirement_ProviderName{ - ProviderName: "origins-0", - }, - }, - { - RequiresType: &envoy_jwt.JwtRequirement_AllowMissing{ - AllowMissing: &emptypb.Empty{}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - Providers: map[string]*envoy_jwt.JwtProvider{ - "origins-0": { - Issuer: "https://secret.foo.com", - JwksSourceSpecifier: &envoy_jwt.JwtProvider_LocalJwks{ - LocalJwks: &core.DataSource{ - Specifier: &core.DataSource_InlineString{ - InlineString: test.JwtPubKey1, - }, - }, - }, - Forward: false, - PayloadInMetadata: "https://secret.foo.com", - }, - }, - BypassCorsPreflight: true, - }, - }, - { - name: "Multiple JWT rule", - in: []*v1beta1.JWTRule{ - { - Issuer: "https://secret.foo.com", - JwksUri: jwksURI, - }, - { - Issuer: "https://secret.bar.com", - Jwks: "jwks-inline-data", - }, - }, - expected: &envoy_jwt.JwtAuthentication{ - Rules: []*envoy_jwt.RequirementRule{ - { - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_Prefix{ - Prefix: "/", - }, - }, - RequirementType: &envoy_jwt.RequirementRule_Requires{ - Requires: &envoy_jwt.JwtRequirement{ - RequiresType: &envoy_jwt.JwtRequirement_RequiresAny{ - RequiresAny: &envoy_jwt.JwtRequirementOrList{ - Requirements: []*envoy_jwt.JwtRequirement{ - { - RequiresType: &envoy_jwt.JwtRequirement_ProviderName{ - ProviderName: "origins-0", - }, - }, - { - RequiresType: &envoy_jwt.JwtRequirement_ProviderName{ - ProviderName: "origins-1", - }, - }, - { - RequiresType: &envoy_jwt.JwtRequirement_RequiresAll{ - RequiresAll: &envoy_jwt.JwtRequirementAndList{ - Requirements: []*envoy_jwt.JwtRequirement{ - { - RequiresType: &envoy_jwt.JwtRequirement_RequiresAny{ - RequiresAny: &envoy_jwt.JwtRequirementOrList{ - Requirements: []*envoy_jwt.JwtRequirement{ - { - RequiresType: &envoy_jwt.JwtRequirement_ProviderName{ - ProviderName: "origins-0", - }, - }, - { - RequiresType: &envoy_jwt.JwtRequirement_AllowMissing{ - AllowMissing: &emptypb.Empty{}, - }, - }, - }, - }, - }, - }, - { - RequiresType: &envoy_jwt.JwtRequirement_RequiresAny{ - RequiresAny: &envoy_jwt.JwtRequirementOrList{ - Requirements: []*envoy_jwt.JwtRequirement{ - { - RequiresType: &envoy_jwt.JwtRequirement_ProviderName{ - ProviderName: "origins-1", - }, - }, - { - RequiresType: &envoy_jwt.JwtRequirement_AllowMissing{ - AllowMissing: &emptypb.Empty{}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - Providers: map[string]*envoy_jwt.JwtProvider{ - "origins-0": { - Issuer: "https://secret.foo.com", - JwksSourceSpecifier: &envoy_jwt.JwtProvider_LocalJwks{ - LocalJwks: &core.DataSource{ - Specifier: &core.DataSource_InlineString{ - InlineString: test.JwtPubKey1, - }, - }, - }, - Forward: false, - PayloadInMetadata: "https://secret.foo.com", - }, - "origins-1": { - Issuer: "https://secret.bar.com", - JwksSourceSpecifier: &envoy_jwt.JwtProvider_LocalJwks{ - LocalJwks: &core.DataSource{ - Specifier: &core.DataSource_InlineString{ - InlineString: "jwks-inline-data", - }, - }, - }, - Forward: false, - PayloadInMetadata: "https://secret.bar.com", - }, - }, - BypassCorsPreflight: true, - }, - }, - { - name: "Empty Jwks URI", - in: []*v1beta1.JWTRule{ - { - Issuer: "https://secret.foo.com", - }, - }, - expected: &envoy_jwt.JwtAuthentication{ - Rules: []*envoy_jwt.RequirementRule{ - { - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_Prefix{ - Prefix: "/", - }, - }, - RequirementType: &envoy_jwt.RequirementRule_Requires{ - Requires: &envoy_jwt.JwtRequirement{ - RequiresType: &envoy_jwt.JwtRequirement_RequiresAny{ - RequiresAny: &envoy_jwt.JwtRequirementOrList{ - Requirements: []*envoy_jwt.JwtRequirement{ - { - RequiresType: &envoy_jwt.JwtRequirement_ProviderName{ - ProviderName: "origins-0", - }, - }, - { - RequiresType: &envoy_jwt.JwtRequirement_AllowMissing{ - AllowMissing: &emptypb.Empty{}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - Providers: map[string]*envoy_jwt.JwtProvider{ - "origins-0": { - Issuer: "https://secret.foo.com", - JwksSourceSpecifier: &envoy_jwt.JwtProvider_LocalJwks{ - LocalJwks: &core.DataSource{ - Specifier: &core.DataSource_InlineString{ - InlineString: model.CreateFakeJwks(""), - }, - }, - }, - Forward: false, - PayloadInMetadata: "https://secret.foo.com", - }, - }, - BypassCorsPreflight: true, - }, - }, - { - name: "Unreachable Jwks URI", - in: []*v1beta1.JWTRule{ - { - Issuer: "https://secret.foo.com", - JwksUri: "http://site.not.exist", - }, - }, - expected: &envoy_jwt.JwtAuthentication{ - Rules: []*envoy_jwt.RequirementRule{ - { - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_Prefix{ - Prefix: "/", - }, - }, - RequirementType: &envoy_jwt.RequirementRule_Requires{ - Requires: &envoy_jwt.JwtRequirement{ - RequiresType: &envoy_jwt.JwtRequirement_RequiresAny{ - RequiresAny: &envoy_jwt.JwtRequirementOrList{ - Requirements: []*envoy_jwt.JwtRequirement{ - { - RequiresType: &envoy_jwt.JwtRequirement_ProviderName{ - ProviderName: "origins-0", - }, - }, - { - RequiresType: &envoy_jwt.JwtRequirement_AllowMissing{ - AllowMissing: &emptypb.Empty{}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - Providers: map[string]*envoy_jwt.JwtProvider{ - "origins-0": { - Issuer: "https://secret.foo.com", - JwksSourceSpecifier: &envoy_jwt.JwtProvider_LocalJwks{ - LocalJwks: &core.DataSource{ - Specifier: &core.DataSource_InlineString{ - InlineString: model.CreateFakeJwks("http://site.not.exist"), - }, - }, - }, - Forward: false, - PayloadInMetadata: "https://secret.foo.com", - }, - }, - BypassCorsPreflight: true, - }, - }, - } - - push := &model.PushContext{} - push.JwtKeyResolver = model.NewJwksResolver( - model.JwtPubKeyEvictionDuration, model.JwtPubKeyRefreshInterval, - model.JwtPubKeyRefreshIntervalOnFailure, model.JwtPubKeyRetryInterval) - defer push.JwtKeyResolver.Close() - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - if got := convertToEnvoyJwtConfig(c.in, push); !reflect.DeepEqual(c.expected, got) { - t.Errorf("got:\n%s\nwanted:\n%s\n", spew.Sdump(got), spew.Sdump(c.expected)) - } - }) - } -} - -func humanReadableAuthnFilterDump(filter *http_conn.HttpFilter) string { - if filter == nil { - return "" - } - config := &authn_filter.FilterConfig{} - filter.GetTypedConfig().UnmarshalTo(config) - return spew.Sdump(config) -} - -func TestAuthnFilterConfig(t *testing.T) { - ms, err := test.StartNewServer() - if err != nil { - t.Fatal("failed to start a mock server") - } - jwksURI := ms.URL + "/oauth2/v3/certs" - - cases := []struct { - name string - forSidecar bool - jwtIn []*config.Config - peerIn []*config.Config - expected *http_conn.HttpFilter - }{ - { - name: "no-policy", - expected: nil, - }, - { - name: "beta-jwt", - jwtIn: []*config.Config{ - { - Spec: &v1beta1.RequestAuthentication{ - JwtRules: []*v1beta1.JWTRule{ - { - Issuer: "https://secret.foo.com", - JwksUri: jwksURI, - }, - }, - }, - }, - }, - expected: &http_conn.HttpFilter{ - Name: "istio_authn", - ConfigType: &http_conn.HttpFilter_TypedConfig{ - TypedConfig: pilotutil.MessageToAny(&authn_filter.FilterConfig{ - SkipValidateTrustDomain: true, - Policy: &authn_alpha.Policy{ - Origins: []*authn_alpha.OriginAuthenticationMethod{ - { - Jwt: &authn_alpha.Jwt{ - Issuer: "https://secret.foo.com", - }, - }, - }, - OriginIsOptional: true, - PrincipalBinding: authn_alpha.PrincipalBinding_USE_ORIGIN, - }, - }), - }, - }, - }, - { - name: "beta-jwt-for-sidecar", - forSidecar: true, - jwtIn: []*config.Config{ - { - Spec: &v1beta1.RequestAuthentication{ - JwtRules: []*v1beta1.JWTRule{ - { - Issuer: "https://secret.foo.com", - JwksUri: jwksURI, - }, - }, - }, - }, - }, - expected: &http_conn.HttpFilter{ - Name: "istio_authn", - ConfigType: &http_conn.HttpFilter_TypedConfig{ - TypedConfig: pilotutil.MessageToAny(&authn_filter.FilterConfig{ - SkipValidateTrustDomain: true, - DisableClearRouteCache: true, - Policy: &authn_alpha.Policy{ - Origins: []*authn_alpha.OriginAuthenticationMethod{ - { - Jwt: &authn_alpha.Jwt{ - Issuer: "https://secret.foo.com", - }, - }, - }, - OriginIsOptional: true, - PrincipalBinding: authn_alpha.PrincipalBinding_USE_ORIGIN, - }, - }), - }, - }, - }, - { - name: "multi-beta-jwt", - jwtIn: []*config.Config{ - { - Spec: &v1beta1.RequestAuthentication{ - JwtRules: []*v1beta1.JWTRule{ - { - Issuer: "https://secret.bar.com", - JwksUri: jwksURI, - }, - }, - }, - }, - { - Spec: &v1beta1.RequestAuthentication{}, - }, - { - Spec: &v1beta1.RequestAuthentication{ - JwtRules: []*v1beta1.JWTRule{ - { - Issuer: "https://secret.foo.com", - Jwks: "jwks-inline-data", - }, - }, - }, - }, - }, - expected: &http_conn.HttpFilter{ - Name: "istio_authn", - ConfigType: &http_conn.HttpFilter_TypedConfig{ - TypedConfig: pilotutil.MessageToAny(&authn_filter.FilterConfig{ - SkipValidateTrustDomain: true, - Policy: &authn_alpha.Policy{ - Origins: []*authn_alpha.OriginAuthenticationMethod{ - { - Jwt: &authn_alpha.Jwt{ - Issuer: "https://secret.bar.com", - }, - }, - { - Jwt: &authn_alpha.Jwt{ - Issuer: "https://secret.foo.com", - }, - }, - }, - OriginIsOptional: true, - PrincipalBinding: authn_alpha.PrincipalBinding_USE_ORIGIN, - }, - }), - }, - }, - }, - { - name: "multi-beta-jwt-sort-by-issuer-again", - jwtIn: []*config.Config{ - { - Spec: &v1beta1.RequestAuthentication{ - JwtRules: []*v1beta1.JWTRule{ - { - Issuer: "https://secret.foo.com", - JwksUri: jwksURI, - }, - }, - }, - }, - { - Spec: &v1beta1.RequestAuthentication{}, - }, - { - Spec: &v1beta1.RequestAuthentication{ - JwtRules: []*v1beta1.JWTRule{ - { - Issuer: "https://secret.bar.com", - Jwks: "jwks-inline-data", - }, - }, - }, - }, - }, - expected: &http_conn.HttpFilter{ - Name: "istio_authn", - ConfigType: &http_conn.HttpFilter_TypedConfig{ - TypedConfig: pilotutil.MessageToAny(&authn_filter.FilterConfig{ - SkipValidateTrustDomain: true, - Policy: &authn_alpha.Policy{ - Origins: []*authn_alpha.OriginAuthenticationMethod{ - { - Jwt: &authn_alpha.Jwt{ - Issuer: "https://secret.bar.com", - }, - }, - { - Jwt: &authn_alpha.Jwt{ - Issuer: "https://secret.foo.com", - }, - }, - }, - OriginIsOptional: true, - PrincipalBinding: authn_alpha.PrincipalBinding_USE_ORIGIN, - }, - }), - }, - }, - }, - { - name: "beta-mtls", - peerIn: []*config.Config{ - { - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - }, - expected: nil, - }, - { - name: "beta-mtls-disable", - peerIn: []*config.Config{ - { - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_DISABLE, - }, - }, - }, - }, - expected: nil, - }, - { - name: "beta-mtls-skip-trust-domain", - peerIn: []*config.Config{ - { - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - }, - expected: nil, - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - got := NewPolicyApplier("root-namespace", c.jwtIn, c.peerIn, &model.PushContext{}).AuthNFilter(c.forSidecar) - if !reflect.DeepEqual(c.expected, got) { - t.Errorf("got:\n%v\nwanted:\n%v\n", humanReadableAuthnFilterDump(got), humanReadableAuthnFilterDump(c.expected)) - } - }) - } -} - -func TestInboundMTLSSettings(t *testing.T) { - now := time.Now() - tlsContext := &tls.DownstreamTlsContext{ - CommonTlsContext: &tls.CommonTlsContext{ - TlsCertificateSdsSecretConfigs: []*tls.SdsSecretConfig{ - { - Name: "default", - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"}, - }, - }, - }, - }, - }, - InitialFetchTimeout: durationpb.New(time.Second * 0), - ResourceApiVersion: core.ApiVersion_V3, - }, - }, - }, - ValidationContextType: &tls.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &tls.CertificateValidationContext{}, - ValidationContextSdsSecretConfig: &tls.SdsSecretConfig{ - Name: "ROOTCA", - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "sds-grpc"}, - }, - }, - }, - }, - }, - InitialFetchTimeout: durationpb.New(time.Second * 0), - ResourceApiVersion: core.ApiVersion_V3, - }, - }, - }, - }, - AlpnProtocols: []string{"istio-peer-exchange", "h2", "http/1.1"}, - TlsParams: &tls.TlsParameters{ - TlsMinimumProtocolVersion: tls.TlsParameters_TLSv1_2, - TlsMaximumProtocolVersion: tls.TlsParameters_TLSv1_3, - CipherSuites: []string{ - "ECDHE-ECDSA-AES256-GCM-SHA384", - "ECDHE-RSA-AES256-GCM-SHA384", - "ECDHE-ECDSA-AES128-GCM-SHA256", - "ECDHE-RSA-AES128-GCM-SHA256", - "AES256-GCM-SHA384", - "AES128-GCM-SHA256", - }, - }, - }, - RequireClientCertificate: protovalue.BoolTrue, - } - tlsContextHTTP := proto.Clone(tlsContext).(*tls.DownstreamTlsContext) - tlsContextHTTP.CommonTlsContext.AlpnProtocols = []string{"h2", "http/1.1"} - - expectedStrict := plugin.MTLSSettings{ - Port: 8080, - Mode: model.MTLSStrict, - TCP: tlsContext, - HTTP: tlsContextHTTP, - } - expectedPermissive := plugin.MTLSSettings{ - Port: 8080, - Mode: model.MTLSPermissive, - TCP: tlsContext, - HTTP: tlsContextHTTP, - } - - cases := []struct { - name string - peerPolicies []*config.Config - expected plugin.MTLSSettings - }{ - { - name: "No policy - behave as permissive", - expected: expectedPermissive, - }, - { - name: "Single policy - disable mode", - peerPolicies: []*config.Config{ - { - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_DISABLE, - }, - }, - }, - }, - expected: plugin.MTLSSettings{Port: 8080, Mode: model.MTLSDisable}, - }, - { - name: "Single policy - permissive mode", - peerPolicies: []*config.Config{ - { - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_PERMISSIVE, - }, - }, - }, - }, - expected: expectedPermissive, - }, - { - name: "Single policy - strict mode", - peerPolicies: []*config.Config{ - { - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - }, - expected: expectedStrict, - }, - { - name: "Multiple policies resolved to STRICT", - peerPolicies: []*config.Config{ - { - Meta: config.Meta{ - Name: "now", - Namespace: "my-ns", - CreationTimestamp: now, - }, - Spec: &v1beta1.PeerAuthentication{ - Selector: &type_beta.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "foo", - }, - }, - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - { - Meta: config.Meta{ - Name: "later", - Namespace: "my-ns", - CreationTimestamp: now.Add(time.Second), - }, - Spec: &v1beta1.PeerAuthentication{ - Selector: &type_beta.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "foo", - }, - }, - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_DISABLE, - }, - }, - }, - }, - expected: expectedStrict, - }, - { - name: "Multiple policies resolved to PERMISSIVE", - peerPolicies: []*config.Config{ - { - Meta: config.Meta{ - Name: "now", - Namespace: "my-ns", - CreationTimestamp: now, - }, - Spec: &v1beta1.PeerAuthentication{ - Selector: &type_beta.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "foo", - }, - }, - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_DISABLE, - }, - }, - }, - { - Meta: config.Meta{ - Name: "earlier", - Namespace: "my-ns", - CreationTimestamp: now.Add(time.Second * -1), - }, - Spec: &v1beta1.PeerAuthentication{ - Selector: &type_beta.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "foo", - }, - }, - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_PERMISSIVE, - }, - }, - }, - }, - expected: expectedPermissive, - }, - { - name: "Port level hit", - peerPolicies: []*config.Config{ - { - Spec: &v1beta1.PeerAuthentication{ - Selector: &type_beta.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "foo", - }, - }, - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_DISABLE, - }, - PortLevelMtls: map[uint32]*v1beta1.PeerAuthentication_MutualTLS{ - 8080: { - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - }, - }, - expected: expectedStrict, - }, - { - name: "Port level miss", - peerPolicies: []*config.Config{ - { - Spec: &v1beta1.PeerAuthentication{ - Selector: &type_beta.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "foo", - }, - }, - PortLevelMtls: map[uint32]*v1beta1.PeerAuthentication_MutualTLS{ - 7070: { - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - }, - }, - expected: expectedPermissive, - }, - } - - testNode := &model.Proxy{ - Metadata: &model.NodeMetadata{ - Labels: map[string]string{ - "app": "foo", - }, - }, - } - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - got := NewPolicyApplier("root-namespace", nil, tc.peerPolicies, &model.PushContext{}).InboundMTLSSettings( - 8080, - testNode, - []string{}, - ) - if diff := cmp.Diff(tc.expected, got, protocmp.Transform()); diff != "" { - t.Errorf("unexpected filter chains: %v", diff) - } - }) - } -} - -func TestComposePeerAuthentication(t *testing.T) { - now := time.Now() - tests := []struct { - name string - configs []*config.Config - want *v1beta1.PeerAuthentication - }{ - { - name: "no config", - configs: []*config.Config{}, - want: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_PERMISSIVE, - }, - }, - }, - { - name: "mesh only", - configs: []*config.Config{ - { - Meta: config.Meta{ - Name: "default", - Namespace: "root-namespace", - }, - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - }, - want: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - { - name: "mesh vs namespace", - configs: []*config.Config{ - { - Meta: config.Meta{ - Name: "default", - Namespace: "root-namespace", - }, - Spec: &v1beta1.PeerAuthentication{ - Selector: &type_beta.WorkloadSelector{ - MatchLabels: map[string]string{}, - }, - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - { - Meta: config.Meta{ - Name: "default", - Namespace: "my-ns", - }, - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_PERMISSIVE, - }, - }, - }, - }, - want: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_PERMISSIVE, - }, - }, - }, - { - name: "ignore non-emptypb selector in root namespace", - configs: []*config.Config{ - { - Meta: config.Meta{ - Name: "default", - Namespace: "root-namespace", - }, - Spec: &v1beta1.PeerAuthentication{ - Selector: &type_beta.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "foo", - }, - }, - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - }, - want: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_PERMISSIVE, - }, - }, - }, - { - name: "workload vs namespace config", - configs: []*config.Config{ - { - Meta: config.Meta{ - Name: "default", - Namespace: "my-ns", - }, - Spec: &v1beta1.PeerAuthentication{}, - }, - { - Meta: config.Meta{ - Name: "foo", - Namespace: "my-ns", - }, - Spec: &v1beta1.PeerAuthentication{ - Selector: &type_beta.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "foo", - }, - }, - }, - }, - }, - want: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_PERMISSIVE, - }, - }, - }, - { - name: "workload vs mesh config", - configs: []*config.Config{ - { - Meta: config.Meta{ - Name: "default", - Namespace: "my-ns", - }, - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_PERMISSIVE, - }, - }, - }, - { - Meta: config.Meta{ - Name: "default", - Namespace: "root-namespace", - }, - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_DISABLE, - }, - }, - }, - }, - want: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_PERMISSIVE, - }, - }, - }, - { - name: "multiple mesh policy", - configs: []*config.Config{ - { - Meta: config.Meta{ - Name: "now", - Namespace: "root-namespace", - CreationTimestamp: now, - }, - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_DISABLE, - }, - }, - }, - { - Meta: config.Meta{ - Name: "second ago", - Namespace: "root-namespace", - CreationTimestamp: now.Add(time.Second * -1), - }, - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_PERMISSIVE, - }, - }, - }, - { - Meta: config.Meta{ - Name: "second later", - Namespace: "root-namespace", - CreationTimestamp: now.Add(time.Second * -1), - }, - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - }, - want: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_PERMISSIVE, - }, - }, - }, - { - name: "multiple namespace policy", - configs: []*config.Config{ - { - Meta: config.Meta{ - Name: "now", - Namespace: "my-ns", - CreationTimestamp: now, - }, - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_DISABLE, - }, - }, - }, - { - Meta: config.Meta{ - Name: "second ago", - Namespace: "my-ns", - CreationTimestamp: now.Add(time.Second * -1), - }, - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_PERMISSIVE, - }, - }, - }, - { - Meta: config.Meta{ - Name: "second later", - Namespace: "my-ns", - CreationTimestamp: now.Add(time.Second * -1), - }, - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - }, - want: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_PERMISSIVE, - }, - }, - }, - { - name: "multiple workload policy", - configs: []*config.Config{ - { - Meta: config.Meta{ - Name: "now", - Namespace: "my-ns", - CreationTimestamp: now, - }, - Spec: &v1beta1.PeerAuthentication{ - Selector: &type_beta.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "foo", - }, - }, - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_DISABLE, - }, - }, - }, - { - Meta: config.Meta{ - Name: "second ago", - Namespace: "my-ns", - CreationTimestamp: now.Add(time.Second * -1), - }, - Spec: &v1beta1.PeerAuthentication{ - Selector: &type_beta.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "foo", - }, - }, - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_PERMISSIVE, - }, - }, - }, - { - Meta: config.Meta{ - Name: "second later", - Namespace: "my-ns", - CreationTimestamp: now.Add(time.Second * -1), - }, - Spec: &v1beta1.PeerAuthentication{ - Selector: &type_beta.WorkloadSelector{ - MatchLabels: map[string]string{ - "stage": "prod", - }, - }, - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - }, - want: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_PERMISSIVE, - }, - }, - }, - { - name: "inheritance: default mesh", - configs: []*config.Config{ - { - Meta: config.Meta{ - Name: "default", - Namespace: "root-namespace", - }, - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_UNSET, - }, - }, - }, - }, - want: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_PERMISSIVE, - }, - }, - }, - { - name: "inheritance: mesh to workload", - configs: []*config.Config{ - { - Meta: config.Meta{ - Name: "default", - Namespace: "root-namespace", - }, - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - { - Meta: config.Meta{ - Name: "foo", - Namespace: "my-ns", - }, - Spec: &v1beta1.PeerAuthentication{ - Selector: &type_beta.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "foo", - }, - }, - }, - }, - }, - want: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - { - name: "inheritance: namespace to workload", - configs: []*config.Config{ - { - Meta: config.Meta{ - Name: "default", - Namespace: "my-ns", - }, - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - { - Meta: config.Meta{ - Name: "foo", - Namespace: "my-ns", - }, - Spec: &v1beta1.PeerAuthentication{ - Selector: &type_beta.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "foo", - }, - }, - }, - }, - }, - want: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - { - name: "inheritance: mesh to namespace to workload", - configs: []*config.Config{ - { - Meta: config.Meta{ - Name: "default", - Namespace: "root-namespace", - }, - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - { - Meta: config.Meta{ - Name: "default", - Namespace: "my-ns", - }, - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_UNSET, - }, - }, - }, - { - Meta: config.Meta{ - Name: "foo", - Namespace: "my-ns", - }, - Spec: &v1beta1.PeerAuthentication{ - Selector: &type_beta.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "foo", - }, - }, - }, - }, - }, - want: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - { - name: "port level", - configs: []*config.Config{ - { - Meta: config.Meta{ - Name: "default", - Namespace: "root-namespace", - }, - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - { - Meta: config.Meta{ - Name: "foo", - Namespace: "my-ns", - }, - Spec: &v1beta1.PeerAuthentication{ - Selector: &type_beta.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "foo", - }, - }, - PortLevelMtls: map[uint32]*v1beta1.PeerAuthentication_MutualTLS{ - 80: { - Mode: v1beta1.PeerAuthentication_MutualTLS_DISABLE, - }, - 90: { - Mode: v1beta1.PeerAuthentication_MutualTLS_UNSET, - }, - 100: {}, - }, - }, - }, - }, - want: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - PortLevelMtls: map[uint32]*v1beta1.PeerAuthentication_MutualTLS{ - 80: { - Mode: v1beta1.PeerAuthentication_MutualTLS_DISABLE, - }, - 90: { - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - 100: { - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := ComposePeerAuthentication("root-namespace", tt.configs); !reflect.DeepEqual(got, tt.want) { - t.Errorf("composePeerAuthentication() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestGetMutualTLSMode(t *testing.T) { - tests := []struct { - name string - in *v1beta1.PeerAuthentication_MutualTLS - want model.MutualTLSMode - }{ - { - name: "unset", - in: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_UNSET, - }, - want: model.MTLSUnknown, - }, - { - name: "disable", - in: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_DISABLE, - }, - want: model.MTLSDisable, - }, - { - name: "permissive", - in: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_PERMISSIVE, - }, - want: model.MTLSPermissive, - }, - { - name: "strict", - in: &v1beta1.PeerAuthentication_MutualTLS{ - Mode: v1beta1.PeerAuthentication_MutualTLS_STRICT, - }, - want: model.MTLSStrict, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := getMutualTLSMode(tt.in); !reflect.DeepEqual(got, tt.want) { - t.Errorf("getMutualTLSMode() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/pilot/pkg/security/authz/builder/builder.go b/pilot/pkg/security/authz/builder/builder.go deleted file mode 100644 index b64504009..000000000 --- a/pilot/pkg/security/authz/builder/builder.go +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package builder - -import ( - "fmt" - "strconv" -) - -import ( - tcppb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - rbacpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3" - rbachttppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/rbac/v3" - httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - rbactcppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/rbac/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "github.com/hashicorp/go-multierror" - "istio.io/api/annotation" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - authzmodel "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/authz/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/trustdomain" -) - -var rbacPolicyMatchNever = &rbacpb.Policy{ - Permissions: []*rbacpb.Permission{{Rule: &rbacpb.Permission_NotRule{ - NotRule: &rbacpb.Permission{Rule: &rbacpb.Permission_Any{Any: true}}, - }}}, - Principals: []*rbacpb.Principal{{Identifier: &rbacpb.Principal_NotId{ - NotId: &rbacpb.Principal{Identifier: &rbacpb.Principal_Any{Any: true}}, - }}}, -} - -// General setting to control behavior -type Option struct { - IsCustomBuilder bool - Logger *AuthzLogger -} - -// Builder builds Istio authorization policy to Envoy filters. -type Builder struct { - trustDomainBundle trustdomain.Bundle - option Option - - // populated when building for CUSTOM action. - customPolicies []model.AuthorizationPolicy - extensions map[string]*builtExtAuthz - - // populated when building for ALLOW/DENY/AUDIT action. - denyPolicies []model.AuthorizationPolicy - allowPolicies []model.AuthorizationPolicy - auditPolicies []model.AuthorizationPolicy -} - -// New returns a new builder for the given workload with the authorization policy. -// Returns nil if none of the authorization policies are enabled for the workload. -func New(trustDomainBundle trustdomain.Bundle, push *model.PushContext, policies model.AuthorizationPoliciesResult, option Option) *Builder { - if option.IsCustomBuilder { - option.Logger.AppendDebugf("found %d CUSTOM actions", len(policies.Custom)) - if len(policies.Custom) == 0 { - return nil - } - return &Builder{ - customPolicies: policies.Custom, - extensions: processExtensionProvider(push), - trustDomainBundle: trustDomainBundle, - option: option, - } - } - - option.Logger.AppendDebugf("found %d DENY actions, %d ALLOW actions, %d AUDIT actions", len(policies.Deny), len(policies.Allow), len(policies.Audit)) - if len(policies.Deny) == 0 && len(policies.Allow) == 0 && len(policies.Audit) == 0 { - return nil - } - return &Builder{ - denyPolicies: policies.Deny, - allowPolicies: policies.Allow, - auditPolicies: policies.Audit, - trustDomainBundle: trustDomainBundle, - option: option, - } -} - -// BuildHTTP returns the HTTP filters built from the authorization policy. -func (b Builder) BuildHTTP() []*httppb.HttpFilter { - if b.option.IsCustomBuilder { - // Use the DENY action so that a HTTP rule is properly handled when generating for TCP filter chain. - if configs := b.build(b.customPolicies, rbacpb.RBAC_DENY, false); configs != nil { - b.option.Logger.AppendDebugf("built %d HTTP filters for CUSTOM action", len(configs.http)) - return configs.http - } - return nil - } - - var filters []*httppb.HttpFilter - if configs := b.build(b.auditPolicies, rbacpb.RBAC_LOG, false); configs != nil { - b.option.Logger.AppendDebugf("built %d HTTP filters for AUDIT action", len(configs.http)) - filters = append(filters, configs.http...) - } - if configs := b.build(b.denyPolicies, rbacpb.RBAC_DENY, false); configs != nil { - b.option.Logger.AppendDebugf("built %d HTTP filters for DENY action", len(configs.http)) - filters = append(filters, configs.http...) - } - if configs := b.build(b.allowPolicies, rbacpb.RBAC_ALLOW, false); configs != nil { - b.option.Logger.AppendDebugf("built %d HTTP filters for ALLOW action", len(configs.http)) - filters = append(filters, configs.http...) - } - return filters -} - -// BuildTCP returns the TCP filters built from the authorization policy. -func (b Builder) BuildTCP() []*tcppb.Filter { - if b.option.IsCustomBuilder { - if configs := b.build(b.customPolicies, rbacpb.RBAC_DENY, true); configs != nil { - b.option.Logger.AppendDebugf("built %d TCP filters for CUSTOM action", len(configs.tcp)) - return configs.tcp - } - return nil - } - - var filters []*tcppb.Filter - if configs := b.build(b.auditPolicies, rbacpb.RBAC_LOG, true); configs != nil { - b.option.Logger.AppendDebugf("built %d TCP filters for AUDIT action", len(configs.tcp)) - filters = append(filters, configs.tcp...) - } - if configs := b.build(b.denyPolicies, rbacpb.RBAC_DENY, true); configs != nil { - b.option.Logger.AppendDebugf("built %d TCP filters for DENY action", len(configs.tcp)) - filters = append(filters, configs.tcp...) - } - if configs := b.build(b.allowPolicies, rbacpb.RBAC_ALLOW, true); configs != nil { - b.option.Logger.AppendDebugf("built %d TCP filters for ALLOW action", len(configs.tcp)) - filters = append(filters, configs.tcp...) - } - return filters -} - -type builtConfigs struct { - http []*httppb.HttpFilter - tcp []*tcppb.Filter -} - -func (b Builder) isDryRun(policy model.AuthorizationPolicy) bool { - dryRun := false - if val, ok := policy.Annotations[annotation.IoIstioDryRun.Name]; ok { - var err error - dryRun, err = strconv.ParseBool(val) - if err != nil { - b.option.Logger.AppendError(fmt.Errorf("failed to parse the value of %s: %v", annotation.IoIstioDryRun.Name, err)) - } - } - return dryRun -} - -func shadowRuleStatPrefix(rule *rbacpb.RBAC) string { - switch rule.GetAction() { - case rbacpb.RBAC_ALLOW: - return authzmodel.RBACShadowRulesAllowStatPrefix - case rbacpb.RBAC_DENY: - return authzmodel.RBACShadowRulesDenyStatPrefix - default: - return "" - } -} - -func (b Builder) build(policies []model.AuthorizationPolicy, action rbacpb.RBAC_Action, forTCP bool) *builtConfigs { - if len(policies) == 0 { - return nil - } - - enforceRules := &rbacpb.RBAC{ - Action: action, - Policies: map[string]*rbacpb.Policy{}, - } - shadowRules := &rbacpb.RBAC{ - Action: action, - Policies: map[string]*rbacpb.Policy{}, - } - - var providers []string - filterType := "HTTP" - if forTCP { - filterType = "TCP" - } - hasEnforcePolicy, hasDryRunPolicy := false, false - for _, policy := range policies { - var currentRule *rbacpb.RBAC - if b.isDryRun(policy) { - currentRule = shadowRules - hasDryRunPolicy = true - } else { - currentRule = enforceRules - hasEnforcePolicy = true - } - if b.option.IsCustomBuilder { - providers = append(providers, policy.Spec.GetProvider().GetName()) - } - for i, rule := range policy.Spec.Rules { - // The name will later be used by ext_authz filter to get the evaluation result from dynamic metadata. - name := policyName(policy.Namespace, policy.Name, i, b.option) - if rule == nil { - b.option.Logger.AppendError(fmt.Errorf("skipped nil rule %s", name)) - continue - } - m, err := authzmodel.New(rule) - if err != nil { - b.option.Logger.AppendError(multierror.Prefix(err, fmt.Sprintf("skipped invalid rule %s:", name))) - continue - } - m.MigrateTrustDomain(b.trustDomainBundle) - if len(b.trustDomainBundle.TrustDomains) > 1 { - b.option.Logger.AppendDebugf("patched source principal with trust domain aliases %v", b.trustDomainBundle.TrustDomains) - } - generated, err := m.Generate(forTCP, action) - if err != nil { - b.option.Logger.AppendDebugf("skipped rule %s on TCP filter chain: %v", name, err) - continue - } - if generated != nil { - currentRule.Policies[name] = generated - b.option.Logger.AppendDebugf("generated config from rule %s on %s filter chain successfully", name, filterType) - } - } - if len(policy.Spec.Rules) == 0 { - // Generate an explicit policy that never matches. - name := policyName(policy.Namespace, policy.Name, 0, b.option) - b.option.Logger.AppendDebugf("generated config from policy %s on %s filter chain successfully", name, filterType) - currentRule.Policies[name] = rbacPolicyMatchNever - } - } - - if !hasEnforcePolicy { - enforceRules = nil - } - if !hasDryRunPolicy { - shadowRules = nil - } - if forTCP { - return &builtConfigs{tcp: b.buildTCP(enforceRules, shadowRules, providers)} - } - return &builtConfigs{http: b.buildHTTP(enforceRules, shadowRules, providers)} -} - -func (b Builder) buildHTTP(rules *rbacpb.RBAC, shadowRules *rbacpb.RBAC, providers []string) []*httppb.HttpFilter { - if !b.option.IsCustomBuilder { - rbac := &rbachttppb.RBAC{ - Rules: rules, - ShadowRules: shadowRules, - ShadowRulesStatPrefix: shadowRuleStatPrefix(shadowRules), - } - return []*httppb.HttpFilter{ - { - Name: wellknown.HTTPRoleBasedAccessControl, - ConfigType: &httppb.HttpFilter_TypedConfig{TypedConfig: util.MessageToAny(rbac)}, - }, - } - } - - extauthz, err := getExtAuthz(b.extensions, providers) - if err != nil { - b.option.Logger.AppendError(multierror.Prefix(err, "failed to process CUSTOM action:")) - rbac := &rbachttppb.RBAC{Rules: rbacDefaultDenyAll} - return []*httppb.HttpFilter{ - { - Name: wellknown.HTTPRoleBasedAccessControl, - ConfigType: &httppb.HttpFilter_TypedConfig{TypedConfig: util.MessageToAny(rbac)}, - }, - } - } - // Add the RBAC filter in shadow mode so that it only evaluates the matching rules for CUSTOM action but not enforce it. - // The evaluation result is stored in the dynamic metadata keyed by the policy name. And then the ext_authz filter - // can utilize these metadata to trigger the enforcement conditionally. - // See https://docs.google.com/document/d/1V4mCQCw7mlGp0zSQQXYoBdbKMDnkPOjeyUb85U07iSI/edit#bookmark=kix.jdq8u0an2r6s - // for more details. - rbac := &rbachttppb.RBAC{ - ShadowRules: rules, - ShadowRulesStatPrefix: authzmodel.RBACExtAuthzShadowRulesStatPrefix, - } - return []*httppb.HttpFilter{ - { - Name: wellknown.HTTPRoleBasedAccessControl, - ConfigType: &httppb.HttpFilter_TypedConfig{TypedConfig: util.MessageToAny(rbac)}, - }, - { - Name: wellknown.HTTPExternalAuthorization, - ConfigType: &httppb.HttpFilter_TypedConfig{TypedConfig: util.MessageToAny(extauthz.http)}, - }, - } -} - -func (b Builder) buildTCP(rules *rbacpb.RBAC, shadowRules *rbacpb.RBAC, providers []string) []*tcppb.Filter { - if !b.option.IsCustomBuilder { - rbac := &rbactcppb.RBAC{ - Rules: rules, - StatPrefix: authzmodel.RBACTCPFilterStatPrefix, - ShadowRules: shadowRules, - ShadowRulesStatPrefix: shadowRuleStatPrefix(shadowRules), - } - return []*tcppb.Filter{ - { - Name: wellknown.RoleBasedAccessControl, - ConfigType: &tcppb.Filter_TypedConfig{TypedConfig: util.MessageToAny(rbac)}, - }, - } - } - - if extauthz, err := getExtAuthz(b.extensions, providers); err != nil { - b.option.Logger.AppendError(multierror.Prefix(err, "failed to parse CUSTOM action, will generate a deny all config:")) - rbac := &rbactcppb.RBAC{ - Rules: rbacDefaultDenyAll, - StatPrefix: authzmodel.RBACTCPFilterStatPrefix, - } - return []*tcppb.Filter{ - { - Name: wellknown.RoleBasedAccessControl, - ConfigType: &tcppb.Filter_TypedConfig{TypedConfig: util.MessageToAny(rbac)}, - }, - } - } else if extauthz.tcp == nil { - b.option.Logger.AppendDebugf("ignored CUSTOM action with HTTP provider on TCP filter chain") - return nil - } else { - rbac := &rbactcppb.RBAC{ - ShadowRules: rules, - StatPrefix: authzmodel.RBACTCPFilterStatPrefix, - ShadowRulesStatPrefix: authzmodel.RBACExtAuthzShadowRulesStatPrefix, - } - return []*tcppb.Filter{ - { - Name: wellknown.RoleBasedAccessControl, - ConfigType: &tcppb.Filter_TypedConfig{TypedConfig: util.MessageToAny(rbac)}, - }, - { - Name: wellknown.ExternalAuthorization, - ConfigType: &tcppb.Filter_TypedConfig{TypedConfig: util.MessageToAny(extauthz.tcp)}, - }, - } - } -} - -func policyName(namespace, name string, rule int, option Option) string { - prefix := "" - if option.IsCustomBuilder { - prefix = extAuthzMatchPrefix + "-" - } - return fmt.Sprintf("%sns[%s]-policy[%s]-rule[%d]", prefix, namespace, name, rule) -} diff --git a/pilot/pkg/security/authz/builder/builder_test.go b/pilot/pkg/security/authz/builder/builder_test.go deleted file mode 100644 index 8ccd45949..000000000 --- a/pilot/pkg/security/authz/builder/builder_test.go +++ /dev/null @@ -1,465 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package builder - -import ( - "os" - "testing" -) - -import ( - tcppb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/durationpb" - meshconfig "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/crd" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/plugin" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/trustdomain" - "github.com/apache/dubbo-go-pixiu/pilot/test/util" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -const ( - basePath = "testdata/" -) - -var ( - httpbin = map[string]string{ - "app": "httpbin", - "version": "v1", - } - meshConfigGRPCNoNamespace = &meshconfig.MeshConfig{ - ExtensionProviders: []*meshconfig.MeshConfig_ExtensionProvider{ - { - Name: "default", - Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyExtAuthzGrpc{ - EnvoyExtAuthzGrpc: &meshconfig.MeshConfig_ExtensionProvider_EnvoyExternalAuthorizationGrpcProvider{ - Service: "my-custom-ext-authz.foo.svc.cluster.local", - Port: 9000, - FailOpen: true, - StatusOnError: "403", - }, - }, - }, - }, - } - meshConfigGRPC = &meshconfig.MeshConfig{ - ExtensionProviders: []*meshconfig.MeshConfig_ExtensionProvider{ - { - Name: "default", - Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyExtAuthzGrpc{ - EnvoyExtAuthzGrpc: &meshconfig.MeshConfig_ExtensionProvider_EnvoyExternalAuthorizationGrpcProvider{ - Service: "foo/my-custom-ext-authz.foo.svc.cluster.local", - Port: 9000, - Timeout: &durationpb.Duration{Nanos: 2000 * 1000}, - FailOpen: true, - StatusOnError: "403", - IncludeRequestBodyInCheck: &meshconfig.MeshConfig_ExtensionProvider_EnvoyExternalAuthorizationRequestBody{ - MaxRequestBytes: 4096, - AllowPartialMessage: true, - }, - }, - }, - }, - }, - } - meshConfigHTTP = &meshconfig.MeshConfig{ - ExtensionProviders: []*meshconfig.MeshConfig_ExtensionProvider{ - { - Name: "default", - Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyExtAuthzHttp{ - EnvoyExtAuthzHttp: &meshconfig.MeshConfig_ExtensionProvider_EnvoyExternalAuthorizationHttpProvider{ - Service: "foo/my-custom-ext-authz.foo.svc.cluster.local", - Port: 9000, - Timeout: &durationpb.Duration{Seconds: 10}, - FailOpen: true, - StatusOnError: "403", - PathPrefix: "/check", - IncludeRequestHeadersInCheck: []string{"x-custom-id", "x-prefix-*", "*-suffix"}, - IncludeHeadersInCheck: []string{"should-not-include-when-IncludeRequestHeadersInCheck-is-set"}, - IncludeAdditionalHeadersInCheck: map[string]string{"x-header-1": "value-1", "x-header-2": "value-2"}, - IncludeRequestBodyInCheck: &meshconfig.MeshConfig_ExtensionProvider_EnvoyExternalAuthorizationRequestBody{ - MaxRequestBytes: 2048, - AllowPartialMessage: true, - PackAsBytes: true, - }, - HeadersToUpstreamOnAllow: []string{"Authorization", "x-prefix-*", "*-suffix"}, - HeadersToDownstreamOnDeny: []string{"Set-cookie", "x-prefix-*", "*-suffix"}, - HeadersToDownstreamOnAllow: []string{"Set-cookie", "x-prefix-*", "*-suffix"}, - }, - }, - }, - }, - } - meshConfigInvalid = &meshconfig.MeshConfig{ - ExtensionProviders: []*meshconfig.MeshConfig_ExtensionProvider{ - { - Name: "default", - Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyExtAuthzHttp{ - EnvoyExtAuthzHttp: &meshconfig.MeshConfig_ExtensionProvider_EnvoyExternalAuthorizationHttpProvider{ - Service: "foo/my-custom-ext-authz", - Port: 999999, - PathPrefix: "check", - StatusOnError: "999", - }, - }, - }, - }, - } -) - -func TestGenerator_GenerateHTTP(t *testing.T) { - testCases := []struct { - name string - tdBundle trustdomain.Bundle - meshConfig *meshconfig.MeshConfig - version *model.IstioVersion - input string - want []string - }{ - { - name: "allow-empty-rule", - input: "allow-empty-rule-in.yaml", - want: []string{"allow-empty-rule-out.yaml"}, - }, - { - name: "allow-full-rule", - input: "allow-full-rule-in.yaml", - want: []string{"allow-full-rule-out.yaml"}, - }, - { - name: "allow-nil-rule", - input: "allow-nil-rule-in.yaml", - want: []string{"allow-nil-rule-out.yaml"}, - }, - { - name: "allow-path", - input: "allow-path-in.yaml", - want: []string{"allow-path-out.yaml"}, - }, - { - name: "audit-full-rule", - input: "audit-full-rule-in.yaml", - want: []string{"audit-full-rule-out.yaml"}, - }, - { - name: "custom-grpc-provider-no-namespace", - meshConfig: meshConfigGRPCNoNamespace, - input: "custom-simple-http-in.yaml", - want: []string{"custom-grpc-provider-no-namespace-out1.yaml", "custom-grpc-provider-no-namespace-out2.yaml"}, - }, - { - name: "custom-grpc-provider", - meshConfig: meshConfigGRPC, - input: "custom-simple-http-in.yaml", - want: []string{"custom-grpc-provider-out1.yaml", "custom-grpc-provider-out2.yaml"}, - }, - { - name: "custom-http-provider", - meshConfig: meshConfigHTTP, - input: "custom-simple-http-in.yaml", - want: []string{"custom-http-provider-out1.yaml", "custom-http-provider-out2.yaml"}, - }, - { - name: "custom-bad-multiple-providers", - meshConfig: meshConfigHTTP, - input: "custom-bad-multiple-providers-in.yaml", - want: []string{"custom-bad-out.yaml"}, - }, - { - name: "custom-bad-invalid-config", - meshConfig: meshConfigInvalid, - input: "custom-simple-http-in.yaml", - want: []string{"custom-bad-out.yaml"}, - }, - { - name: "deny-and-allow", - input: "deny-and-allow-in.yaml", - want: []string{"deny-and-allow-out1.yaml", "deny-and-allow-out2.yaml"}, - }, - { - name: "deny-empty-rule", - input: "deny-empty-rule-in.yaml", - want: []string{"deny-empty-rule-out.yaml"}, - }, - { - name: "dry-run-allow-and-deny", - input: "dry-run-allow-and-deny-in.yaml", - want: []string{"dry-run-allow-and-deny-out1.yaml", "dry-run-allow-and-deny-out2.yaml"}, - }, - { - name: "dry-run-allow", - input: "dry-run-allow-in.yaml", - want: []string{"dry-run-allow-out.yaml"}, - }, - { - name: "dry-run-mix", - input: "dry-run-mix-in.yaml", - want: []string{"dry-run-mix-out.yaml"}, - }, - { - name: "multiple-policies", - input: "multiple-policies-in.yaml", - want: []string{"multiple-policies-out.yaml"}, - }, - { - name: "single-policy", - input: "single-policy-in.yaml", - want: []string{"single-policy-out.yaml"}, - }, - { - name: "trust-domain-one-alias", - tdBundle: trustdomain.NewBundle("td1", []string{"cluster.local"}), - input: "simple-policy-td-aliases-in.yaml", - want: []string{"simple-policy-td-aliases-out.yaml"}, - }, - { - name: "trust-domain-multiple-aliases", - tdBundle: trustdomain.NewBundle("td1", []string{"cluster.local", "some-td"}), - input: "simple-policy-multiple-td-aliases-in.yaml", - want: []string{"simple-policy-multiple-td-aliases-out.yaml"}, - }, - { - name: "trust-domain-wildcard-in-principal", - tdBundle: trustdomain.NewBundle("td1", []string{"foobar"}), - input: "simple-policy-principal-with-wildcard-in.yaml", - want: []string{"simple-policy-principal-with-wildcard-out.yaml"}, - }, - { - name: "trust-domain-aliases-in-source-principal", - tdBundle: trustdomain.NewBundle("new-td", []string{"old-td", "some-trustdomain"}), - input: "td-aliases-source-principal-in.yaml", - want: []string{"td-aliases-source-principal-out.yaml"}, - }, - } - - baseDir := "http/" - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - option := Option{ - IsCustomBuilder: tc.meshConfig != nil, - Logger: &AuthzLogger{}, - } - in := inputParams(t, baseDir+tc.input, tc.meshConfig, tc.version) - defer option.Logger.Report(in) - policies := in.Push.AuthzPolicies.ListAuthorizationPolicies(in.Node.ConfigNamespace, in.Node.Metadata.Labels) - g := New(tc.tdBundle, in.Push, policies, option) - if g == nil { - t.Fatalf("failed to create generator") - } - got := g.BuildHTTP() - verify(t, convertHTTP(got), baseDir, tc.want, false /* forTCP */) - }) - } -} - -func TestGenerator_GenerateTCP(t *testing.T) { - testCases := []struct { - name string - tdBundle trustdomain.Bundle - meshConfig *meshconfig.MeshConfig - input string - want []string - }{ - { - name: "allow-both-http-tcp", - input: "allow-both-http-tcp-in.yaml", - want: []string{"allow-both-http-tcp-out.yaml"}, - }, - { - name: "allow-only-http", - input: "allow-only-http-in.yaml", - want: []string{"allow-only-http-out.yaml"}, - }, - { - name: "audit-both-http-tcp", - input: "audit-both-http-tcp-in.yaml", - want: []string{"audit-both-http-tcp-out.yaml"}, - }, - { - name: "custom-both-http-tcp", - meshConfig: meshConfigGRPC, - input: "custom-both-http-tcp-in.yaml", - want: []string{"custom-both-http-tcp-out1.yaml", "custom-both-http-tcp-out2.yaml"}, - }, - { - name: "custom-only-http", - meshConfig: meshConfigHTTP, - input: "custom-only-http-in.yaml", - want: []string{}, - }, - { - name: "deny-both-http-tcp", - input: "deny-both-http-tcp-in.yaml", - want: []string{"deny-both-http-tcp-out.yaml"}, - }, - { - name: "dry-run-mix", - input: "dry-run-mix-in.yaml", - want: []string{"dry-run-mix-out.yaml"}, - }, - } - - baseDir := "tcp/" - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - option := Option{ - IsCustomBuilder: tc.meshConfig != nil, - Logger: &AuthzLogger{}, - } - in := inputParams(t, baseDir+tc.input, tc.meshConfig, nil) - defer option.Logger.Report(in) - policies := in.Push.AuthzPolicies.ListAuthorizationPolicies(in.Node.ConfigNamespace, in.Node.Metadata.Labels) - g := New(tc.tdBundle, in.Push, policies, option) - if g == nil { - t.Fatalf("failed to create generator") - } - got := g.BuildTCP() - verify(t, convertTCP(got), baseDir, tc.want, true /* forTCP */) - }) - } -} - -func verify(t *testing.T, gots []proto.Message, baseDir string, wants []string, forTCP bool) { - t.Helper() - - if len(gots) != len(wants) { - t.Fatalf("got %d configs but want %d", len(gots), len(wants)) - } - for i, got := range gots { - gotYaml, err := protomarshal.ToYAML(got) - if err != nil { - t.Fatalf("failed to convert to YAML: %v", err) - } - - wantFile := basePath + baseDir + wants[i] - want := yamlConfig(t, wantFile, forTCP) - wantYaml, err := protomarshal.ToYAML(want) - if err != nil { - t.Fatalf("failed to convert to YAML: %v", err) - } - - util.RefreshGoldenFile(t, []byte(gotYaml), wantFile) - if err := util.Compare([]byte(gotYaml), []byte(wantYaml)); err != nil { - t.Error(err) - } - } -} - -func yamlPolicy(t *testing.T, filename string) *model.AuthorizationPolicies { - t.Helper() - data, err := os.ReadFile(filename) - if err != nil { - t.Fatalf("failed to read input yaml file: %v", err) - } - c, _, err := crd.ParseInputs(string(data)) - if err != nil { - t.Fatalf("failde to parse CRD: %v", err) - } - var configs []*config.Config - for i := range c { - configs = append(configs, &c[i]) - } - - return newAuthzPolicies(t, configs) -} - -func yamlConfig(t *testing.T, filename string, forTCP bool) proto.Message { - t.Helper() - data, err := os.ReadFile(filename) - if err != nil { - t.Fatalf("failed to read file: %v", err) - } - if forTCP { - out := &tcppb.Filter{} - if err := protomarshal.ApplyYAML(string(data), out); err != nil { - t.Fatalf("failed to parse YAML: %v", err) - } - return out - } - out := &httppb.HttpFilter{} - if err := protomarshal.ApplyYAML(string(data), out); err != nil { - t.Fatalf("failed to parse YAML: %v", err) - } - return out -} - -func convertHTTP(in []*httppb.HttpFilter) []proto.Message { - ret := make([]proto.Message, len(in)) - for i := range in { - ret[i] = in[i] - } - return ret -} - -func convertTCP(in []*tcppb.Filter) []proto.Message { - ret := make([]proto.Message, len(in)) - for i := range in { - ret[i] = in[i] - } - return ret -} - -func newAuthzPolicies(t *testing.T, policies []*config.Config) *model.AuthorizationPolicies { - store := model.MakeIstioStore(memory.Make(collections.Pilot)) - for _, p := range policies { - if _, err := store.Create(*p); err != nil { - t.Fatalf("newAuthzPolicies: %v", err) - } - } - - authzPolicies, err := model.GetAuthorizationPolicies(&model.Environment{ - ConfigStore: store, - }) - if err != nil { - t.Fatalf("newAuthzPolicies: %v", err) - } - return authzPolicies -} - -func inputParams(t *testing.T, input string, mc *meshconfig.MeshConfig, version *model.IstioVersion) *plugin.InputParams { - t.Helper() - ret := &plugin.InputParams{ - Node: &model.Proxy{ - ID: "test-node", - ConfigNamespace: "foo", - Metadata: &model.NodeMetadata{ - Labels: httpbin, - }, - IstioVersion: version, - }, - Push: &model.PushContext{ - AuthzPolicies: yamlPolicy(t, basePath+input), - Mesh: mc, - }, - } - ret.Push.ServiceIndex.HostnameAndNamespace = map[host.Name]map[string]*model.Service{ - "my-custom-ext-authz.foo.svc.cluster.local": { - "foo": &model.Service{ - Hostname: "my-custom-ext-authz.foo.svc.cluster.local", - }, - }, - } - return ret -} diff --git a/pilot/pkg/security/authz/builder/extauthz.go b/pilot/pkg/security/authz/builder/extauthz.go deleted file mode 100644 index 341057099..000000000 --- a/pilot/pkg/security/authz/builder/extauthz.go +++ /dev/null @@ -1,399 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package builder - -import ( - "fmt" - "net/url" - "sort" - "strconv" - "strings" -) - -import ( - "github.com/davecgh/go-spew/spew" - envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - rbacpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3" - extauthzhttp "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v3" - extauthztcp "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/ext_authz/v3" - envoy_type_matcher_v3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" - envoytypev3 "github.com/envoyproxy/go-control-plane/envoy/type/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "github.com/hashicorp/go-multierror" - "google.golang.org/protobuf/types/known/durationpb" - meshconfig "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/extensionproviders" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - authzmodel "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/authz/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/validation" -) - -const ( - extAuthzMatchPrefix = "istio-ext-authz" -) - -var ( - rbacPolicyMatchAll = &rbacpb.Policy{ - Permissions: []*rbacpb.Permission{{Rule: &rbacpb.Permission_Any{Any: true}}}, - Principals: []*rbacpb.Principal{{Identifier: &rbacpb.Principal_Any{Any: true}}}, - } - rbacDefaultDenyAll = &rbacpb.RBAC{ - Action: rbacpb.RBAC_DENY, - Policies: map[string]*rbacpb.Policy{ - "default-deny-all-due-to-bad-CUSTOM-action": rbacPolicyMatchAll, - }, - } - supportedStatus = func() []int { - var supported []int - for code := range envoytypev3.StatusCode_name { - supported = append(supported, int(code)) - } - sort.Ints(supported) - return supported - }() -) - -type builtExtAuthz struct { - http *extauthzhttp.ExtAuthz - tcp *extauthztcp.ExtAuthz - err error -} - -func processExtensionProvider(push *model.PushContext) map[string]*builtExtAuthz { - resolved := map[string]*builtExtAuthz{} - for i, config := range push.Mesh.ExtensionProviders { - var errs error - if config.Name == "" { - errs = multierror.Append(errs, fmt.Errorf("extension provider name must not be empty, found empty at index: %d", i)) - } else if _, found := resolved[config.Name]; found { - errs = multierror.Append(errs, fmt.Errorf("extension provider name must be unique, found duplicate: %s", config.Name)) - } - var parsed *builtExtAuthz - var err error - // TODO(yangminzhu): Refactor and cache the ext_authz config. - switch p := config.Provider.(type) { - case *meshconfig.MeshConfig_ExtensionProvider_EnvoyExtAuthzHttp: - if err = validation.ValidateExtensionProviderEnvoyExtAuthzHTTP(p.EnvoyExtAuthzHttp); err == nil { - parsed, err = buildExtAuthzHTTP(push, p.EnvoyExtAuthzHttp) - } - case *meshconfig.MeshConfig_ExtensionProvider_EnvoyExtAuthzGrpc: - if err = validation.ValidateExtensionProviderEnvoyExtAuthzGRPC(p.EnvoyExtAuthzGrpc); err == nil { - parsed, err = buildExtAuthzGRPC(push, p.EnvoyExtAuthzGrpc) - } - default: - continue - } - if err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, fmt.Sprintf("failed to parse extension provider %q:", config.Name))) - } - if parsed == nil { - parsed = &builtExtAuthz{} - } - parsed.err = errs - resolved[config.Name] = parsed - } - - if authzLog.DebugEnabled() { - authzLog.Debugf("Resolved extension providers: %v", spew.Sdump(resolved)) - } - return resolved -} - -func notAllTheSame(names []string) bool { - for i := 1; i < len(names); i++ { - if names[i-1] != names[i] { - return true - } - } - return false -} - -func getExtAuthz(resolved map[string]*builtExtAuthz, providers []string) (*builtExtAuthz, error) { - if resolved == nil { - return nil, fmt.Errorf("extension provider is either invalid or undefined") - } - if len(providers) < 1 { - return nil, fmt.Errorf("no provider specified in authorization policy") - } - if notAllTheSame(providers) { - return nil, fmt.Errorf("only 1 provider can be used per workload, found multiple providers: %v", providers) - } - - provider := providers[0] - ret, found := resolved[provider] - if !found { - var li []string - for p := range resolved { - li = append(li, p) - } - return nil, fmt.Errorf("available providers are %v but found %q", li, provider) - } else if ret.err != nil { - return nil, fmt.Errorf("found errors in provider %s: %v", provider, ret.err) - } - - return ret, nil -} - -func buildExtAuthzHTTP(push *model.PushContext, - config *meshconfig.MeshConfig_ExtensionProvider_EnvoyExternalAuthorizationHttpProvider) (*builtExtAuthz, error) { - var errs error - port, err := parsePort(config.Port) - if err != nil { - errs = multierror.Append(errs, err) - } - hostname, cluster, err := extensionproviders.LookupCluster(push, config.Service, port) - if err != nil { - errs = multierror.Append(errs, err) - } - status, err := parseStatusOnError(config.StatusOnError) - if err != nil { - errs = multierror.Append(errs, err) - } - if config.PathPrefix != "" { - if _, err := url.Parse(config.PathPrefix); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, fmt.Sprintf("invalid pathPrefix %q:", config.PathPrefix))) - } - if !strings.HasPrefix(config.PathPrefix, "/") { - errs = multierror.Append(errs, fmt.Errorf("pathPrefix must begin with `/`, found: %q", config.PathPrefix)) - } - } - checkWildcard := func(field string, values []string) { - for _, val := range values { - if val == "*" { - errs = multierror.Append(errs, fmt.Errorf("a single wildcard (\"*\") is not supported, change it to either prefix or suffix match: %s", field)) - } - } - } - checkWildcard("IncludeRequestHeadersInCheck", config.IncludeRequestHeadersInCheck) - checkWildcard("IncludeHeadersInCheck", config.IncludeHeadersInCheck) - checkWildcard("HeadersToDownstreamOnDeny", config.HeadersToDownstreamOnDeny) - checkWildcard("HeadersToDownstreamOnAllow", config.HeadersToDownstreamOnAllow) - checkWildcard("HeadersToUpstreamOnAllow", config.HeadersToUpstreamOnAllow) - - if errs != nil { - return nil, errs - } - - return generateHTTPConfig(hostname, cluster, status, config), nil -} - -func buildExtAuthzGRPC(push *model.PushContext, - config *meshconfig.MeshConfig_ExtensionProvider_EnvoyExternalAuthorizationGrpcProvider) (*builtExtAuthz, error) { - var errs error - port, err := parsePort(config.Port) - if err != nil { - errs = multierror.Append(errs, err) - } - _, cluster, err := extensionproviders.LookupCluster(push, config.Service, port) - if err != nil { - errs = multierror.Append(errs, err) - } - status, err := parseStatusOnError(config.StatusOnError) - if err != nil { - errs = multierror.Append(errs, err) - } - if errs != nil { - return nil, errs - } - - return generateGRPCConfig(cluster, config, status), nil -} - -func parsePort(port uint32) (int, error) { - if 1 <= port && port <= 65535 { - return int(port), nil - } - return 0, fmt.Errorf("port must be in the range [1, 65535], found: %d", port) -} - -func parseStatusOnError(status string) (*envoytypev3.HttpStatus, error) { - if status == "" { - return nil, nil - } - code, err := strconv.ParseInt(status, 10, 32) - if err != nil { - return nil, multierror.Prefix(err, fmt.Sprintf("invalid statusOnError %q:", status)) - } - if _, found := envoytypev3.StatusCode_name[int32(code)]; !found { - return nil, fmt.Errorf("unsupported statusOnError %s, supported values: %v", status, supportedStatus) - } - return &envoytypev3.HttpStatus{Code: envoytypev3.StatusCode(code)}, nil -} - -func generateHTTPConfig(hostname, cluster string, status *envoytypev3.HttpStatus, - config *meshconfig.MeshConfig_ExtensionProvider_EnvoyExternalAuthorizationHttpProvider) *builtExtAuthz { - service := &extauthzhttp.HttpService{ - PathPrefix: config.PathPrefix, - ServerUri: &envoy_config_core_v3.HttpUri{ - // Timeout is required. - Timeout: timeoutOrDefault(config.Timeout), - // Uri is required but actually not used in the ext_authz filter. - Uri: fmt.Sprintf("http://%s", hostname), - HttpUpstreamType: &envoy_config_core_v3.HttpUri_Cluster{ - Cluster: cluster, - }, - }, - } - allowedHeaders := generateHeaders(config.IncludeRequestHeadersInCheck) - if allowedHeaders == nil { - // IncludeHeadersInCheck is deprecated, only use it if IncludeRequestHeadersInCheck is not set. - // TODO: Remove the IncludeHeadersInCheck field before promoting to beta. - allowedHeaders = generateHeaders(config.IncludeHeadersInCheck) - } - var headersToAdd []*envoy_config_core_v3.HeaderValue - var additionalHeaders []string - for k := range config.IncludeAdditionalHeadersInCheck { - additionalHeaders = append(additionalHeaders, k) - } - sort.Strings(additionalHeaders) - for _, k := range additionalHeaders { - headersToAdd = append(headersToAdd, &envoy_config_core_v3.HeaderValue{ - Key: k, - Value: config.IncludeAdditionalHeadersInCheck[k], - }) - } - if allowedHeaders != nil || len(headersToAdd) != 0 { - service.AuthorizationRequest = &extauthzhttp.AuthorizationRequest{ - AllowedHeaders: allowedHeaders, - HeadersToAdd: headersToAdd, - } - } - - if len(config.HeadersToUpstreamOnAllow) > 0 || len(config.HeadersToDownstreamOnDeny) > 0 || - len(config.HeadersToDownstreamOnAllow) > 0 { - service.AuthorizationResponse = &extauthzhttp.AuthorizationResponse{ - AllowedUpstreamHeaders: generateHeaders(config.HeadersToUpstreamOnAllow), - AllowedClientHeaders: generateHeaders(config.HeadersToDownstreamOnDeny), - AllowedClientHeadersOnSuccess: generateHeaders(config.HeadersToDownstreamOnAllow), - } - } - http := &extauthzhttp.ExtAuthz{ - StatusOnError: status, - FailureModeAllow: config.FailOpen, - TransportApiVersion: envoy_config_core_v3.ApiVersion_V3, - Services: &extauthzhttp.ExtAuthz_HttpService{ - HttpService: service, - }, - FilterEnabledMetadata: generateFilterMatcher(wellknown.HTTPRoleBasedAccessControl), - WithRequestBody: withBodyRequest(config.IncludeRequestBodyInCheck), - } - return &builtExtAuthz{http: http} -} - -func generateGRPCConfig(cluster string, config *meshconfig.MeshConfig_ExtensionProvider_EnvoyExternalAuthorizationGrpcProvider, - status *envoytypev3.HttpStatus) *builtExtAuthz { - // The cluster includes the character `|` that is invalid in gRPC authority header and will cause the connection - // rejected in the server side, replace it with a valid character and set in authority otherwise ext_authz will - // use the cluster name as default authority. - authority := strings.ReplaceAll(cluster, "|", "_.") - grpc := &envoy_config_core_v3.GrpcService{ - TargetSpecifier: &envoy_config_core_v3.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &envoy_config_core_v3.GrpcService_EnvoyGrpc{ - ClusterName: cluster, - Authority: authority, - }, - }, - Timeout: timeoutOrDefault(config.Timeout), - } - http := &extauthzhttp.ExtAuthz{ - StatusOnError: status, - FailureModeAllow: config.FailOpen, - Services: &extauthzhttp.ExtAuthz_GrpcService{ - GrpcService: grpc, - }, - FilterEnabledMetadata: generateFilterMatcher(wellknown.HTTPRoleBasedAccessControl), - TransportApiVersion: envoy_config_core_v3.ApiVersion_V3, - WithRequestBody: withBodyRequest(config.IncludeRequestBodyInCheck), - } - tcp := &extauthztcp.ExtAuthz{ - StatPrefix: "tcp.", - FailureModeAllow: config.FailOpen, - TransportApiVersion: envoy_config_core_v3.ApiVersion_V3, - GrpcService: grpc, - FilterEnabledMetadata: generateFilterMatcher(wellknown.RoleBasedAccessControl), - } - return &builtExtAuthz{http: http, tcp: tcp} -} - -func generateHeaders(headers []string) *envoy_type_matcher_v3.ListStringMatcher { - if len(headers) == 0 { - return nil - } - var patterns []*envoy_type_matcher_v3.StringMatcher - for _, header := range headers { - pattern := &envoy_type_matcher_v3.StringMatcher{ - IgnoreCase: true, - } - if strings.HasPrefix(header, "*") { - pattern.MatchPattern = &envoy_type_matcher_v3.StringMatcher_Suffix{ - Suffix: strings.TrimPrefix(header, "*"), - } - } else if strings.HasSuffix(header, "*") { - pattern.MatchPattern = &envoy_type_matcher_v3.StringMatcher_Prefix{ - Prefix: strings.TrimSuffix(header, "*"), - } - } else { - pattern.MatchPattern = &envoy_type_matcher_v3.StringMatcher_Exact{ - Exact: header, - } - } - patterns = append(patterns, pattern) - } - return &envoy_type_matcher_v3.ListStringMatcher{Patterns: patterns} -} - -func generateFilterMatcher(name string) *envoy_type_matcher_v3.MetadataMatcher { - return &envoy_type_matcher_v3.MetadataMatcher{ - Filter: name, - Path: []*envoy_type_matcher_v3.MetadataMatcher_PathSegment{ - { - Segment: &envoy_type_matcher_v3.MetadataMatcher_PathSegment_Key{ - Key: authzmodel.RBACExtAuthzShadowRulesStatPrefix + authzmodel.RBACShadowEffectivePolicyID, - }, - }, - }, - Value: &envoy_type_matcher_v3.ValueMatcher{ - MatchPattern: &envoy_type_matcher_v3.ValueMatcher_StringMatch{ - StringMatch: &envoy_type_matcher_v3.StringMatcher{ - MatchPattern: &envoy_type_matcher_v3.StringMatcher_Prefix{ - Prefix: extAuthzMatchPrefix, - }, - }, - }, - }, - } -} - -func timeoutOrDefault(t *durationpb.Duration) *durationpb.Duration { - if t == nil { - // Default timeout is 600s. - return &durationpb.Duration{Seconds: 600} - } - return t -} - -func withBodyRequest(config *meshconfig.MeshConfig_ExtensionProvider_EnvoyExternalAuthorizationRequestBody) *extauthzhttp.BufferSettings { - if config == nil { - return nil - } - return &extauthzhttp.BufferSettings{ - MaxRequestBytes: config.MaxRequestBytes, - AllowPartialMessage: config.AllowPartialMessage, - PackAsBytes: config.PackAsBytes, - } -} diff --git a/pilot/pkg/security/authz/builder/logger.go b/pilot/pkg/security/authz/builder/logger.go deleted file mode 100644 index 437eae581..000000000 --- a/pilot/pkg/security/authz/builder/logger.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package builder - -import ( - "fmt" - "strings" -) - -import ( - "github.com/hashicorp/go-multierror" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/plugin" - "github.com/apache/dubbo-go-pixiu/pkg/util/istiomultierror" -) - -var authzLog = log.RegisterScope("authorization", "Istio Authorization Policy", 0) - -type AuthzLogger struct { - debugMsg []string - errMsg *multierror.Error -} - -func (al *AuthzLogger) AppendDebugf(format string, args ...interface{}) { - al.debugMsg = append(al.debugMsg, fmt.Sprintf(format, args...)) -} - -func (al *AuthzLogger) AppendError(err error) { - al.errMsg = multierror.Append(al.errMsg, err) -} - -func (al *AuthzLogger) Report(in *plugin.InputParams) { - if al.errMsg != nil { - al.errMsg.ErrorFormat = istiomultierror.MultiErrorFormat() - authzLog.Errorf("Processed authorization policy for %s, %s", in.Node.ID, al.errMsg) - } - if authzLog.DebugEnabled() && len(al.debugMsg) != 0 { - out := strings.Join(al.debugMsg, "\n\t* ") - authzLog.Debugf("Processed authorization policy for %s with details:\n\t* %v", in.Node.ID, out) - } else { - authzLog.Debugf("Processed authorization policy for %s", in.Node.ID) - } -} diff --git a/pilot/pkg/security/authz/builder/testdata/http/allow-empty-rule-in.yaml b/pilot/pkg/security/authz/builder/testdata/http/allow-empty-rule-in.yaml deleted file mode 100644 index 04c26ccef..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/allow-empty-rule-in.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: allow-all - namespace: foo -spec: - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - {} diff --git a/pilot/pkg/security/authz/builder/testdata/http/allow-empty-rule-out.yaml b/pilot/pkg/security/authz/builder/testdata/http/allow-empty-rule-out.yaml deleted file mode 100644 index 439627130..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/allow-empty-rule-out.yaml +++ /dev/null @@ -1,15 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - rules: - policies: - ns[foo]-policy[allow-all]-rule[0]: - permissions: - - andRules: - rules: - - any: true - principals: - - andIds: - ids: - - any: true - shadowRulesStatPrefix: istio_dry_run_allow_ diff --git a/pilot/pkg/security/authz/builder/testdata/http/allow-full-rule-in.yaml b/pilot/pkg/security/authz/builder/testdata/http/allow-full-rule-in.yaml deleted file mode 100644 index 55ad3c602..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/allow-full-rule-in.yaml +++ /dev/null @@ -1,76 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-1 - namespace: foo -spec: - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - from: - - source: - principals: ["principal", "principal-prefix-*", "*-suffix-principal", "*"] - requestPrincipals: ["requestPrincipals", "requestPrincipals-prefix-*", "*-suffix-requestPrincipals", "*"] - namespaces: ["ns", "ns-prefix-*", "*-ns-suffix", "*"] - ipBlocks: ["1.2.3.4", "5.6.0.0/16"] - remoteIpBlocks: ["1.2.3.4", "5.6.0.0/16"] - notPrincipals: ["not-principal", "not-principal-prefix-*", "*-not-suffix-principal", "*"] - notRequestPrincipals: ["not-requestPrincipals", "not-requestPrincipals-prefix-*", "*-not-suffix-requestPrincipals", "*"] - notNamespaces: ["not-ns", "not-ns-prefix-*", "*-not-ns-suffix", "*"] - notIpBlocks: ["9.0.0.1", "9.2.0.0/16"] - notRemoteIpBlocks: ["9.0.0.1", "9.2.0.0/16"] - to: - - operation: - methods: ["method", "method-prefix-*", "*-suffix-method", "*"] - hosts: ["exact.com", "*.suffix.com", "prefix.*", "*"] - ports: ["80", "90"] - paths: ["/exact", "/prefix/*", "*/suffix", "*"] - notMethods: ["not-method", "not-method-prefix-*", "*-not-suffix-method", "*"] - notHosts: ["not-exact.com", "*.not-suffix.com", "not-prefix.*", "*"] - notPorts: ["8000", "9000"] - notPaths: ["/not-exact", "/not-prefix/*", "*/not-suffix", "*"] - when: - - key: "request.headers[X-header]" - values: ["header", "header-prefix-*", "*-suffix-header", "*"] - notValues: ["not-header", "not-header-prefix-*", "*-not-suffix-header", "*"] - - key: "source.ip" - values: ["10.10.10.10", "192.168.10.0/24"] - notValues: ["90.10.10.10", "90.168.10.0/24"] - - key: "remote.ip" - values: ["10.10.10.10", "192.168.10.0/24"] - notValues: ["90.10.10.10", "90.168.10.0/24"] - - key: "source.namespace" - values: ["ns", "ns-prefix-*", "*-ns-suffix", "*"] - notValues: ["not-ns", "not-ns-prefix-*", "*-not-ns-suffix", "*"] - - key: "source.principal" - values: ["principal", "principal-prefix-*", "*-suffix-principal", "*"] - notValues: ["not-principal", "not-principal-prefix-*", "*-not-suffix-principal", "*"] - - key: "request.auth.principal" - values: ["requestPrincipals", "requestPrincipals-prefix-*", "*-suffix-requestPrincipals", "*"] - notValues: ["not-requestPrincipals", "not-requestPrincipals-prefix-*", "*-not-suffix-requestPrincipals", "*"] - - key: "request.auth.audiences" - values: ["audiences", "audiences-prefix-*", "*-suffix-audiences", "*"] - notValues: ["not-audiences", "not-audiences-prefix-*", "*-not-suffix-audiences", "*"] - - key: "request.auth.presenter" - values: ["presenter", "presenter-prefix-*", "*-suffix-presenter", "*"] - notValues: ["not-presenter", "not-presenter-prefix-*", "*-not-suffix-presenter", "*"] - - key: "request.auth.claims[iss]" - values: ["iss", "iss-prefix-*", "*-suffix-iss", "*"] - notValues: ["not-iss", "not-iss-prefix-*", "*-not-suffix-iss", "*"] - - key: "request.auth.claims[nested1][nested2]" - values: ["nested", "nested-prefix-*", "*-suffix-nested", "*"] - notValues: ["not-nested", "not-nested-prefix-*", "*-not-suffix-nested", "*"] - - key: "destination.ip" - values: ["10.10.10.10", "192.168.10.0/24"] - notValues: ["90.10.10.10", "90.168.10.0/24"] - - key: "destination.port" - values: ["91", "92"] - notValues: ["9001", "9002"] - - key: "connection.sni" - values: ["exact.com", "*.suffix.com", "prefix.*", "*"] - notValues: ["not-exact.com", "*.not-suffix.com", "not-prefix.*", "*"] - - key: "experimental.envoy.filters.a.b[c]" - values: ["exact", "prefix-*", "*-suffix", "*"] - notValues: ["not-exact", "not-prefix-*", "*-not-suffix", "*"] diff --git a/pilot/pkg/security/authz/builder/testdata/http/allow-full-rule-out.yaml b/pilot/pkg/security/authz/builder/testdata/http/allow-full-rule-out.yaml deleted file mode 100644 index 5fbed4e2e..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/allow-full-rule-out.yaml +++ /dev/null @@ -1,948 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - rules: - policies: - ns[foo]-policy[httpbin-1]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - header: - name: :authority - stringMatch: - exact: exact.com - ignoreCase: true - - header: - name: :authority - stringMatch: - ignoreCase: true - suffix: .suffix.com - - header: - name: :authority - stringMatch: - ignoreCase: true - prefix: prefix. - - header: - name: :authority - presentMatch: true - - notRule: - orRules: - rules: - - header: - name: :authority - stringMatch: - exact: not-exact.com - ignoreCase: true - - header: - name: :authority - stringMatch: - ignoreCase: true - suffix: .not-suffix.com - - header: - name: :authority - stringMatch: - ignoreCase: true - prefix: not-prefix. - - header: - name: :authority - presentMatch: true - - orRules: - rules: - - header: - exactMatch: method - name: :method - - header: - name: :method - prefixMatch: method-prefix- - - header: - name: :method - suffixMatch: -suffix-method - - header: - name: :method - presentMatch: true - - notRule: - orRules: - rules: - - header: - exactMatch: not-method - name: :method - - header: - name: :method - prefixMatch: not-method-prefix- - - header: - name: :method - suffixMatch: -not-suffix-method - - header: - name: :method - presentMatch: true - - orRules: - rules: - - urlPath: - path: - exact: /exact - - urlPath: - path: - prefix: /prefix/ - - urlPath: - path: - suffix: /suffix - - urlPath: - path: - safeRegex: - googleRe2: {} - regex: .+ - - notRule: - orRules: - rules: - - urlPath: - path: - exact: /not-exact - - urlPath: - path: - prefix: /not-prefix/ - - urlPath: - path: - suffix: /not-suffix - - urlPath: - path: - safeRegex: - googleRe2: {} - regex: .+ - - orRules: - rules: - - destinationPort: 80 - - destinationPort: 90 - - notRule: - orRules: - rules: - - destinationPort: 8000 - - destinationPort: 9000 - - orRules: - rules: - - destinationIp: - addressPrefix: 10.10.10.10 - prefixLen: 32 - - destinationIp: - addressPrefix: 192.168.10.0 - prefixLen: 24 - - notRule: - orRules: - rules: - - destinationIp: - addressPrefix: 90.10.10.10 - prefixLen: 32 - - destinationIp: - addressPrefix: 90.168.10.0 - prefixLen: 24 - - orRules: - rules: - - destinationPort: 91 - - destinationPort: 92 - - notRule: - orRules: - rules: - - destinationPort: 9001 - - destinationPort: 9002 - - orRules: - rules: - - requestedServerName: - exact: exact.com - - requestedServerName: - suffix: .suffix.com - - requestedServerName: - prefix: prefix. - - requestedServerName: - safeRegex: - googleRe2: {} - regex: .+ - - notRule: - orRules: - rules: - - requestedServerName: - exact: not-exact.com - - requestedServerName: - suffix: .not-suffix.com - - requestedServerName: - prefix: not-prefix. - - requestedServerName: - safeRegex: - googleRe2: {} - regex: .+ - - orRules: - rules: - - metadata: - filter: envoy.filters.a.b - path: - - key: c - value: - stringMatch: - exact: exact - - metadata: - filter: envoy.filters.a.b - path: - - key: c - value: - stringMatch: - prefix: prefix- - - metadata: - filter: envoy.filters.a.b - path: - - key: c - value: - stringMatch: - suffix: -suffix - - metadata: - filter: envoy.filters.a.b - path: - - key: c - value: - stringMatch: - safeRegex: - googleRe2: {} - regex: .+ - - notRule: - orRules: - rules: - - metadata: - filter: envoy.filters.a.b - path: - - key: c - value: - stringMatch: - exact: not-exact - - metadata: - filter: envoy.filters.a.b - path: - - key: c - value: - stringMatch: - prefix: not-prefix- - - metadata: - filter: envoy.filters.a.b - path: - - key: c - value: - stringMatch: - suffix: -not-suffix - - metadata: - filter: envoy.filters.a.b - path: - - key: c - value: - stringMatch: - safeRegex: - googleRe2: {} - regex: .+ - principals: - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://principal - - authenticated: - principalName: - prefix: spiffe://principal-prefix- - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: spiffe://.*-suffix-principal - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .+ - - notId: - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://not-principal - - authenticated: - principalName: - prefix: spiffe://not-principal-prefix- - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: spiffe://.*-not-suffix-principal - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .+ - - orIds: - ids: - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - exact: requestPrincipals - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - prefix: requestPrincipals-prefix- - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - suffix: -suffix-requestPrincipals - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - safeRegex: - googleRe2: {} - regex: .+ - - notId: - orIds: - ids: - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - exact: not-requestPrincipals - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - prefix: not-requestPrincipals-prefix- - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - suffix: -not-suffix-requestPrincipals - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - safeRegex: - googleRe2: {} - regex: .+ - - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/ns/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/ns-prefix-.*/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/.*-ns-suffix/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/.*/.* - - notId: - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/not-ns/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/not-ns-prefix-.*/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/.*-not-ns-suffix/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/.*/.* - - orIds: - ids: - - remoteIp: - addressPrefix: 1.2.3.4 - prefixLen: 32 - - remoteIp: - addressPrefix: 5.6.0.0 - prefixLen: 16 - - notId: - orIds: - ids: - - remoteIp: - addressPrefix: 9.0.0.1 - prefixLen: 32 - - remoteIp: - addressPrefix: 9.2.0.0 - prefixLen: 16 - - orIds: - ids: - - directRemoteIp: - addressPrefix: 1.2.3.4 - prefixLen: 32 - - directRemoteIp: - addressPrefix: 5.6.0.0 - prefixLen: 16 - - notId: - orIds: - ids: - - directRemoteIp: - addressPrefix: 9.0.0.1 - prefixLen: 32 - - directRemoteIp: - addressPrefix: 9.2.0.0 - prefixLen: 16 - - orIds: - ids: - - header: - exactMatch: header - name: X-header - - header: - name: X-header - prefixMatch: header-prefix- - - header: - name: X-header - suffixMatch: -suffix-header - - header: - name: X-header - presentMatch: true - - notId: - orIds: - ids: - - header: - exactMatch: not-header - name: X-header - - header: - name: X-header - prefixMatch: not-header-prefix- - - header: - name: X-header - suffixMatch: -not-suffix-header - - header: - name: X-header - presentMatch: true - - orIds: - ids: - - directRemoteIp: - addressPrefix: 10.10.10.10 - prefixLen: 32 - - directRemoteIp: - addressPrefix: 192.168.10.0 - prefixLen: 24 - - notId: - orIds: - ids: - - directRemoteIp: - addressPrefix: 90.10.10.10 - prefixLen: 32 - - directRemoteIp: - addressPrefix: 90.168.10.0 - prefixLen: 24 - - orIds: - ids: - - remoteIp: - addressPrefix: 10.10.10.10 - prefixLen: 32 - - remoteIp: - addressPrefix: 192.168.10.0 - prefixLen: 24 - - notId: - orIds: - ids: - - remoteIp: - addressPrefix: 90.10.10.10 - prefixLen: 32 - - remoteIp: - addressPrefix: 90.168.10.0 - prefixLen: 24 - - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/ns/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/ns-prefix-.*/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/.*-ns-suffix/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/.*/.* - - notId: - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/not-ns/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/not-ns-prefix-.*/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/.*-not-ns-suffix/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/.*/.* - - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://principal - - authenticated: - principalName: - prefix: spiffe://principal-prefix- - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: spiffe://.*-suffix-principal - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .+ - - notId: - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://not-principal - - authenticated: - principalName: - prefix: spiffe://not-principal-prefix- - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: spiffe://.*-not-suffix-principal - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .+ - - orIds: - ids: - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - exact: requestPrincipals - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - prefix: requestPrincipals-prefix- - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - suffix: -suffix-requestPrincipals - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - safeRegex: - googleRe2: {} - regex: .+ - - notId: - orIds: - ids: - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - exact: not-requestPrincipals - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - prefix: not-requestPrincipals-prefix- - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - suffix: -not-suffix-requestPrincipals - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - safeRegex: - googleRe2: {} - regex: .+ - - orIds: - ids: - - metadata: - filter: istio_authn - path: - - key: request.auth.audiences - value: - stringMatch: - exact: audiences - - metadata: - filter: istio_authn - path: - - key: request.auth.audiences - value: - stringMatch: - prefix: audiences-prefix- - - metadata: - filter: istio_authn - path: - - key: request.auth.audiences - value: - stringMatch: - suffix: -suffix-audiences - - metadata: - filter: istio_authn - path: - - key: request.auth.audiences - value: - stringMatch: - safeRegex: - googleRe2: {} - regex: .+ - - notId: - orIds: - ids: - - metadata: - filter: istio_authn - path: - - key: request.auth.audiences - value: - stringMatch: - exact: not-audiences - - metadata: - filter: istio_authn - path: - - key: request.auth.audiences - value: - stringMatch: - prefix: not-audiences-prefix- - - metadata: - filter: istio_authn - path: - - key: request.auth.audiences - value: - stringMatch: - suffix: -not-suffix-audiences - - metadata: - filter: istio_authn - path: - - key: request.auth.audiences - value: - stringMatch: - safeRegex: - googleRe2: {} - regex: .+ - - orIds: - ids: - - metadata: - filter: istio_authn - path: - - key: request.auth.presenter - value: - stringMatch: - exact: presenter - - metadata: - filter: istio_authn - path: - - key: request.auth.presenter - value: - stringMatch: - prefix: presenter-prefix- - - metadata: - filter: istio_authn - path: - - key: request.auth.presenter - value: - stringMatch: - suffix: -suffix-presenter - - metadata: - filter: istio_authn - path: - - key: request.auth.presenter - value: - stringMatch: - safeRegex: - googleRe2: {} - regex: .+ - - notId: - orIds: - ids: - - metadata: - filter: istio_authn - path: - - key: request.auth.presenter - value: - stringMatch: - exact: not-presenter - - metadata: - filter: istio_authn - path: - - key: request.auth.presenter - value: - stringMatch: - prefix: not-presenter-prefix- - - metadata: - filter: istio_authn - path: - - key: request.auth.presenter - value: - stringMatch: - suffix: -not-suffix-presenter - - metadata: - filter: istio_authn - path: - - key: request.auth.presenter - value: - stringMatch: - safeRegex: - googleRe2: {} - regex: .+ - - orIds: - ids: - - metadata: - filter: istio_authn - path: - - key: request.auth.claims - - key: iss - value: - listMatch: - oneOf: - stringMatch: - exact: iss - - metadata: - filter: istio_authn - path: - - key: request.auth.claims - - key: iss - value: - listMatch: - oneOf: - stringMatch: - prefix: iss-prefix- - - metadata: - filter: istio_authn - path: - - key: request.auth.claims - - key: iss - value: - listMatch: - oneOf: - stringMatch: - suffix: -suffix-iss - - metadata: - filter: istio_authn - path: - - key: request.auth.claims - - key: iss - value: - listMatch: - oneOf: - stringMatch: - safeRegex: - googleRe2: {} - regex: .+ - - notId: - orIds: - ids: - - metadata: - filter: istio_authn - path: - - key: request.auth.claims - - key: iss - value: - listMatch: - oneOf: - stringMatch: - exact: not-iss - - metadata: - filter: istio_authn - path: - - key: request.auth.claims - - key: iss - value: - listMatch: - oneOf: - stringMatch: - prefix: not-iss-prefix- - - metadata: - filter: istio_authn - path: - - key: request.auth.claims - - key: iss - value: - listMatch: - oneOf: - stringMatch: - suffix: -not-suffix-iss - - metadata: - filter: istio_authn - path: - - key: request.auth.claims - - key: iss - value: - listMatch: - oneOf: - stringMatch: - safeRegex: - googleRe2: {} - regex: .+ - - orIds: - ids: - - metadata: - filter: istio_authn - path: - - key: request.auth.claims - - key: nested1 - - key: nested2 - value: - listMatch: - oneOf: - stringMatch: - exact: nested - - metadata: - filter: istio_authn - path: - - key: request.auth.claims - - key: nested1 - - key: nested2 - value: - listMatch: - oneOf: - stringMatch: - prefix: nested-prefix- - - metadata: - filter: istio_authn - path: - - key: request.auth.claims - - key: nested1 - - key: nested2 - value: - listMatch: - oneOf: - stringMatch: - suffix: -suffix-nested - - metadata: - filter: istio_authn - path: - - key: request.auth.claims - - key: nested1 - - key: nested2 - value: - listMatch: - oneOf: - stringMatch: - safeRegex: - googleRe2: {} - regex: .+ - - notId: - orIds: - ids: - - metadata: - filter: istio_authn - path: - - key: request.auth.claims - - key: nested1 - - key: nested2 - value: - listMatch: - oneOf: - stringMatch: - exact: not-nested - - metadata: - filter: istio_authn - path: - - key: request.auth.claims - - key: nested1 - - key: nested2 - value: - listMatch: - oneOf: - stringMatch: - prefix: not-nested-prefix- - - metadata: - filter: istio_authn - path: - - key: request.auth.claims - - key: nested1 - - key: nested2 - value: - listMatch: - oneOf: - stringMatch: - suffix: -not-suffix-nested - - metadata: - filter: istio_authn - path: - - key: request.auth.claims - - key: nested1 - - key: nested2 - value: - listMatch: - oneOf: - stringMatch: - safeRegex: - googleRe2: {} - regex: .+ - shadowRulesStatPrefix: istio_dry_run_allow_ diff --git a/pilot/pkg/security/authz/builder/testdata/http/allow-host-before-111-in.yaml b/pilot/pkg/security/authz/builder/testdata/http/allow-host-before-111-in.yaml deleted file mode 100644 index 862b239e3..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/allow-host-before-111-in.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-1 - namespace: foo -spec: - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - to: - - operation: - hosts: ["example.com", "prefix.example.*", "*.example.com", "*"] - notHosts: ["not-example.com", "prefix.not-example.*", "*.not-example.com", "*"] diff --git a/pilot/pkg/security/authz/builder/testdata/http/allow-host-before-111-out.yaml b/pilot/pkg/security/authz/builder/testdata/http/allow-host-before-111-out.yaml deleted file mode 100644 index 70496f8f6..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/allow-host-before-111-out.yaml +++ /dev/null @@ -1,55 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - rules: - policies: - ns[foo]-policy[httpbin-1]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - header: - name: :authority - safeRegexMatch: - googleRe2: {} - regex: (?i)example\.com - - header: - name: :authority - safeRegexMatch: - googleRe2: {} - regex: (?i)prefix\.example\..* - - header: - name: :authority - safeRegexMatch: - googleRe2: {} - regex: (?i).*\.example\.com - - header: - name: :authority - presentMatch: true - - notRule: - orRules: - rules: - - header: - name: :authority - safeRegexMatch: - googleRe2: {} - regex: (?i)not-example\.com - - header: - name: :authority - safeRegexMatch: - googleRe2: {} - regex: (?i)prefix\.not-example\..* - - header: - name: :authority - safeRegexMatch: - googleRe2: {} - regex: (?i).*\.not-example\.com - - header: - name: :authority - presentMatch: true - principals: - - andIds: - ids: - - any: true - shadowRulesStatPrefix: istio_dry_run_allow_ diff --git a/pilot/pkg/security/authz/builder/testdata/http/allow-nil-rule-in.yaml b/pilot/pkg/security/authz/builder/testdata/http/allow-nil-rule-in.yaml deleted file mode 100644 index 359416fbd..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/allow-nil-rule-in.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: allow-none - namespace: foo -spec: - selector: - matchLabels: - app: httpbin - version: v1 diff --git a/pilot/pkg/security/authz/builder/testdata/http/allow-nil-rule-out.yaml b/pilot/pkg/security/authz/builder/testdata/http/allow-nil-rule-out.yaml deleted file mode 100644 index 82a918335..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/allow-nil-rule-out.yaml +++ /dev/null @@ -1,13 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - rules: - policies: - ns[foo]-policy[allow-none]-rule[0]: - permissions: - - notRule: - any: true - principals: - - notId: - any: true - shadowRulesStatPrefix: istio_dry_run_allow_ diff --git a/pilot/pkg/security/authz/builder/testdata/http/allow-path-in.yaml b/pilot/pkg/security/authz/builder/testdata/http/allow-path-in.yaml deleted file mode 100644 index 3819ec948..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/allow-path-in.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-1 - namespace: foo -spec: - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - to: - - operation: - paths: ["/exact", "/prefix/*", "*/suffix", "*"] - notPaths: ["/not-exact", "/not-prefix/*", "*/not-suffix", "*"] diff --git a/pilot/pkg/security/authz/builder/testdata/http/allow-path-out.yaml b/pilot/pkg/security/authz/builder/testdata/http/allow-path-out.yaml deleted file mode 100644 index c7270914c..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/allow-path-out.yaml +++ /dev/null @@ -1,47 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - rules: - policies: - ns[foo]-policy[httpbin-1]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - urlPath: - path: - exact: /exact - - urlPath: - path: - prefix: /prefix/ - - urlPath: - path: - suffix: /suffix - - urlPath: - path: - safeRegex: - googleRe2: {} - regex: .+ - - notRule: - orRules: - rules: - - urlPath: - path: - exact: /not-exact - - urlPath: - path: - prefix: /not-prefix/ - - urlPath: - path: - suffix: /not-suffix - - urlPath: - path: - safeRegex: - googleRe2: {} - regex: .+ - principals: - - andIds: - ids: - - any: true - shadowRulesStatPrefix: istio_dry_run_allow_ diff --git a/pilot/pkg/security/authz/builder/testdata/http/audit-full-rule-in.yaml b/pilot/pkg/security/authz/builder/testdata/http/audit-full-rule-in.yaml deleted file mode 100644 index b522665e6..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/audit-full-rule-in.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: audit-all - namespace: foo -spec: - action: AUDIT - rules: - - {} diff --git a/pilot/pkg/security/authz/builder/testdata/http/audit-full-rule-out.yaml b/pilot/pkg/security/authz/builder/testdata/http/audit-full-rule-out.yaml deleted file mode 100644 index 2fa8fb329..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/audit-full-rule-out.yaml +++ /dev/null @@ -1,16 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - rules: - action: LOG - policies: - ns[foo]-policy[audit-all]-rule[0]: - permissions: - - andRules: - rules: - - any: true - principals: - - andIds: - ids: - - any: true - shadowRulesStatPrefix: istio_dry_run_allow_ diff --git a/pilot/pkg/security/authz/builder/testdata/http/custom-bad-multiple-providers-in.yaml b/pilot/pkg/security/authz/builder/testdata/http/custom-bad-multiple-providers-in.yaml deleted file mode 100644 index 56c7453a2..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/custom-bad-multiple-providers-in.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-1 - namespace: foo -spec: - action: CUSTOM - provider: - name: default - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - to: - - operation: - paths: ["/httpbin1"] ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-2 - namespace: foo -spec: - action: CUSTOM - provider: - name: another - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - to: - - operation: - paths: ["/httpbin2"] diff --git a/pilot/pkg/security/authz/builder/testdata/http/custom-bad-out.yaml b/pilot/pkg/security/authz/builder/testdata/http/custom-bad-out.yaml deleted file mode 100644 index a277a4dc3..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/custom-bad-out.yaml +++ /dev/null @@ -1,11 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - rules: - action: DENY - policies: - default-deny-all-due-to-bad-CUSTOM-action: - permissions: - - any: true - principals: - - any: true diff --git a/pilot/pkg/security/authz/builder/testdata/http/custom-grpc-provider-no-namespace-out1.yaml b/pilot/pkg/security/authz/builder/testdata/http/custom-grpc-provider-no-namespace-out1.yaml deleted file mode 100644 index 4bd9692e9..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/custom-grpc-provider-no-namespace-out1.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - shadowRules: - action: DENY - policies: - istio-ext-authz-ns[foo]-policy[httpbin-1]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - urlPath: - path: - exact: /httpbin1 - principals: - - andIds: - ids: - - any: true - istio-ext-authz-ns[foo]-policy[httpbin-2]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - urlPath: - path: - exact: /httpbin2 - principals: - - andIds: - ids: - - any: true - shadowRulesStatPrefix: istio_ext_authz_ diff --git a/pilot/pkg/security/authz/builder/testdata/http/custom-grpc-provider-no-namespace-out2.yaml b/pilot/pkg/security/authz/builder/testdata/http/custom-grpc-provider-no-namespace-out2.yaml deleted file mode 100644 index 4d541e1a2..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/custom-grpc-provider-no-namespace-out2.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: envoy.filters.http.ext_authz -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz - failureModeAllow: true - filterEnabledMetadata: - filter: envoy.filters.http.rbac - path: - - key: istio_ext_authz_shadow_effective_policy_id - value: - stringMatch: - prefix: istio-ext-authz - grpcService: - envoyGrpc: - authority: outbound_.9000_._.my-custom-ext-authz.foo.svc.cluster.local - clusterName: outbound|9000||my-custom-ext-authz.foo.svc.cluster.local - timeout: 600s - statusOnError: - code: Forbidden - transportApiVersion: V3 diff --git a/pilot/pkg/security/authz/builder/testdata/http/custom-grpc-provider-out1.yaml b/pilot/pkg/security/authz/builder/testdata/http/custom-grpc-provider-out1.yaml deleted file mode 100644 index 4bd9692e9..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/custom-grpc-provider-out1.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - shadowRules: - action: DENY - policies: - istio-ext-authz-ns[foo]-policy[httpbin-1]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - urlPath: - path: - exact: /httpbin1 - principals: - - andIds: - ids: - - any: true - istio-ext-authz-ns[foo]-policy[httpbin-2]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - urlPath: - path: - exact: /httpbin2 - principals: - - andIds: - ids: - - any: true - shadowRulesStatPrefix: istio_ext_authz_ diff --git a/pilot/pkg/security/authz/builder/testdata/http/custom-grpc-provider-out2.yaml b/pilot/pkg/security/authz/builder/testdata/http/custom-grpc-provider-out2.yaml deleted file mode 100644 index dbff2fabe..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/custom-grpc-provider-out2.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: envoy.filters.http.ext_authz -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz - failureModeAllow: true - filterEnabledMetadata: - filter: envoy.filters.http.rbac - path: - - key: istio_ext_authz_shadow_effective_policy_id - value: - stringMatch: - prefix: istio-ext-authz - grpcService: - envoyGrpc: - authority: outbound_.9000_._.my-custom-ext-authz.foo.svc.cluster.local - clusterName: outbound|9000||my-custom-ext-authz.foo.svc.cluster.local - timeout: 0.002s - statusOnError: - code: Forbidden - transportApiVersion: V3 - withRequestBody: - allowPartialMessage: true - maxRequestBytes: 4096 diff --git a/pilot/pkg/security/authz/builder/testdata/http/custom-http-provider-out1.yaml b/pilot/pkg/security/authz/builder/testdata/http/custom-http-provider-out1.yaml deleted file mode 100644 index 4bd9692e9..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/custom-http-provider-out1.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - shadowRules: - action: DENY - policies: - istio-ext-authz-ns[foo]-policy[httpbin-1]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - urlPath: - path: - exact: /httpbin1 - principals: - - andIds: - ids: - - any: true - istio-ext-authz-ns[foo]-policy[httpbin-2]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - urlPath: - path: - exact: /httpbin2 - principals: - - andIds: - ids: - - any: true - shadowRulesStatPrefix: istio_ext_authz_ diff --git a/pilot/pkg/security/authz/builder/testdata/http/custom-http-provider-out2.yaml b/pilot/pkg/security/authz/builder/testdata/http/custom-http-provider-out2.yaml deleted file mode 100644 index 0a12b4262..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/custom-http-provider-out2.yaml +++ /dev/null @@ -1,63 +0,0 @@ -name: envoy.filters.http.ext_authz -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz - failureModeAllow: true - filterEnabledMetadata: - filter: envoy.filters.http.rbac - path: - - key: istio_ext_authz_shadow_effective_policy_id - value: - stringMatch: - prefix: istio-ext-authz - httpService: - authorizationRequest: - allowedHeaders: - patterns: - - exact: x-custom-id - ignoreCase: true - - ignoreCase: true - prefix: x-prefix- - - ignoreCase: true - suffix: -suffix - headersToAdd: - - key: x-header-1 - value: value-1 - - key: x-header-2 - value: value-2 - authorizationResponse: - allowedClientHeaders: - patterns: - - exact: Set-cookie - ignoreCase: true - - ignoreCase: true - prefix: x-prefix- - - ignoreCase: true - suffix: -suffix - allowedClientHeadersOnSuccess: - patterns: - - exact: Set-cookie - ignoreCase: true - - ignoreCase: true - prefix: x-prefix- - - ignoreCase: true - suffix: -suffix - allowedUpstreamHeaders: - patterns: - - exact: Authorization - ignoreCase: true - - ignoreCase: true - prefix: x-prefix- - - ignoreCase: true - suffix: -suffix - pathPrefix: /check - serverUri: - cluster: outbound|9000||my-custom-ext-authz.foo.svc.cluster.local - timeout: 10s - uri: http://my-custom-ext-authz.foo.svc.cluster.local - statusOnError: - code: Forbidden - transportApiVersion: V3 - withRequestBody: - allowPartialMessage: true - maxRequestBytes: 2048 - packAsBytes: true diff --git a/pilot/pkg/security/authz/builder/testdata/http/custom-simple-http-in.yaml b/pilot/pkg/security/authz/builder/testdata/http/custom-simple-http-in.yaml deleted file mode 100644 index f5f8361ec..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/custom-simple-http-in.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-1 - namespace: foo -spec: - action: CUSTOM - provider: - name: default - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - to: - - operation: - paths: ["/httpbin1"] ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-2 - namespace: foo -spec: - action: CUSTOM - provider: - name: default - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - to: - - operation: - paths: ["/httpbin2"] diff --git a/pilot/pkg/security/authz/builder/testdata/http/deny-and-allow-in.yaml b/pilot/pkg/security/authz/builder/testdata/http/deny-and-allow-in.yaml deleted file mode 100644 index 554847461..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/deny-and-allow-in.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-deny - namespace: foo -spec: - action: DENY - rules: - - from: - - source: - principals: ["deny"] ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-allow - namespace: foo -spec: - action: ALLOW - rules: - - from: - - source: - principals: ["allow"] diff --git a/pilot/pkg/security/authz/builder/testdata/http/deny-and-allow-out1.yaml b/pilot/pkg/security/authz/builder/testdata/http/deny-and-allow-out1.yaml deleted file mode 100644 index 8c842f15e..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/deny-and-allow-out1.yaml +++ /dev/null @@ -1,20 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - rules: - action: DENY - policies: - ns[foo]-policy[httpbin-deny]-rule[0]: - permissions: - - andRules: - rules: - - any: true - principals: - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://deny - shadowRulesStatPrefix: istio_dry_run_allow_ diff --git a/pilot/pkg/security/authz/builder/testdata/http/deny-and-allow-out2.yaml b/pilot/pkg/security/authz/builder/testdata/http/deny-and-allow-out2.yaml deleted file mode 100644 index a57069718..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/deny-and-allow-out2.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - rules: - policies: - ns[foo]-policy[httpbin-allow]-rule[0]: - permissions: - - andRules: - rules: - - any: true - principals: - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://allow - shadowRulesStatPrefix: istio_dry_run_allow_ diff --git a/pilot/pkg/security/authz/builder/testdata/http/deny-empty-rule-in.yaml b/pilot/pkg/security/authz/builder/testdata/http/deny-empty-rule-in.yaml deleted file mode 100644 index 29be69334..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/deny-empty-rule-in.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: deny-all - namespace: foo -spec: - action: DENY - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - {} diff --git a/pilot/pkg/security/authz/builder/testdata/http/deny-empty-rule-out.yaml b/pilot/pkg/security/authz/builder/testdata/http/deny-empty-rule-out.yaml deleted file mode 100644 index 16768bf98..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/deny-empty-rule-out.yaml +++ /dev/null @@ -1,16 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - rules: - action: DENY - policies: - ns[foo]-policy[deny-all]-rule[0]: - permissions: - - andRules: - rules: - - any: true - principals: - - andIds: - ids: - - any: true - shadowRulesStatPrefix: istio_dry_run_allow_ diff --git a/pilot/pkg/security/authz/builder/testdata/http/dry-run-allow-and-deny-in.yaml b/pilot/pkg/security/authz/builder/testdata/http/dry-run-allow-and-deny-in.yaml deleted file mode 100644 index 57a5c4e8b..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/dry-run-allow-and-deny-in.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-1 - namespace: foo - annotations: - "istio.io/dry-run": "true" -spec: - selector: - matchLabels: - app: httpbin - version: v1 - action: ALLOW - rules: - - to: - - operation: - paths: ["/allow"] ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-2 - namespace: foo - annotations: - "istio.io/dry-run": "true" -spec: - selector: - matchLabels: - app: httpbin - version: v1 - action: DENY - rules: - - to: - - operation: - paths: ["/deny"] diff --git a/pilot/pkg/security/authz/builder/testdata/http/dry-run-allow-and-deny-out1.yaml b/pilot/pkg/security/authz/builder/testdata/http/dry-run-allow-and-deny-out1.yaml deleted file mode 100644 index fc947bd06..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/dry-run-allow-and-deny-out1.yaml +++ /dev/null @@ -1,20 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - shadowRules: - action: DENY - policies: - ns[foo]-policy[httpbin-2]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - urlPath: - path: - exact: /deny - principals: - - andIds: - ids: - - any: true - shadowRulesStatPrefix: istio_dry_run_deny_ diff --git a/pilot/pkg/security/authz/builder/testdata/http/dry-run-allow-and-deny-out2.yaml b/pilot/pkg/security/authz/builder/testdata/http/dry-run-allow-and-deny-out2.yaml deleted file mode 100644 index bd1d1714c..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/dry-run-allow-and-deny-out2.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - shadowRules: - policies: - ns[foo]-policy[httpbin-1]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - urlPath: - path: - exact: /allow - principals: - - andIds: - ids: - - any: true - shadowRulesStatPrefix: istio_dry_run_allow_ diff --git a/pilot/pkg/security/authz/builder/testdata/http/dry-run-allow-in.yaml b/pilot/pkg/security/authz/builder/testdata/http/dry-run-allow-in.yaml deleted file mode 100644 index c0abd6c11..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/dry-run-allow-in.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-1 - namespace: foo - annotations: - "istio.io/dry-run": "true" -spec: - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - to: - - operation: - paths: ["/exact"] diff --git a/pilot/pkg/security/authz/builder/testdata/http/dry-run-allow-out.yaml b/pilot/pkg/security/authz/builder/testdata/http/dry-run-allow-out.yaml deleted file mode 100644 index 899470136..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/dry-run-allow-out.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - shadowRules: - policies: - ns[foo]-policy[httpbin-1]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - urlPath: - path: - exact: /exact - principals: - - andIds: - ids: - - any: true - shadowRulesStatPrefix: istio_dry_run_allow_ diff --git a/pilot/pkg/security/authz/builder/testdata/http/dry-run-mix-in.yaml b/pilot/pkg/security/authz/builder/testdata/http/dry-run-mix-in.yaml deleted file mode 100644 index 401e5f4df..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/dry-run-mix-in.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-1 - namespace: foo - annotations: - "istio.io/dry-run": "true" -spec: - selector: - matchLabels: - app: httpbin - version: v1 - action: ALLOW - rules: - - to: - - operation: - paths: ["/allow"] ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-2 - namespace: foo - annotations: - "istio.io/dry-run": "false" -spec: - selector: - matchLabels: - app: httpbin - version: v1 - action: ALLOW - rules: - - to: - - operation: - paths: ["/another"] diff --git a/pilot/pkg/security/authz/builder/testdata/http/dry-run-mix-out.yaml b/pilot/pkg/security/authz/builder/testdata/http/dry-run-mix-out.yaml deleted file mode 100644 index 78f1fafd6..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/dry-run-mix-out.yaml +++ /dev/null @@ -1,34 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - rules: - policies: - ns[foo]-policy[httpbin-2]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - urlPath: - path: - exact: /another - principals: - - andIds: - ids: - - any: true - shadowRules: - policies: - ns[foo]-policy[httpbin-1]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - urlPath: - path: - exact: /allow - principals: - - andIds: - ids: - - any: true - shadowRulesStatPrefix: istio_dry_run_allow_ diff --git a/pilot/pkg/security/authz/builder/testdata/http/multiple-policies-in.yaml b/pilot/pkg/security/authz/builder/testdata/http/multiple-policies-in.yaml deleted file mode 100644 index af5c742cc..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/multiple-policies-in.yaml +++ /dev/null @@ -1,109 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-1 - namespace: foo -spec: - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - to: - - operation: - methods: ["GET", "POST"] ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-2 - namespace: foo -spec: - selector: - matchLabels: - app: httpbin - rules: - - to: - - operation: - paths: ["/v1", "/v2"] ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-3 - namespace: foo -spec: - selector: - matchLabels: - version: v1 - rules: - - to: - - operation: - hosts: ["google.com", "httpbin.org"] ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-4 - namespace: foo -spec: - rules: - - to: - - operation: - ports: ["80", "90"] ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-5 - namespace: foo -spec: - rules: - - from: - - source: - principals: ["principals1", "principals2"] ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-6 - namespace: foo -spec: - rules: - - from: - - source: - requestPrincipals: ["requestPrincipals1", "requestPrincipals2"] ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-7 - namespace: foo -spec: - rules: - - from: - - source: - namespaces: ["namespaces1", "namespaces2"] ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-8 - namespace: foo -spec: - rules: - - from: - - source: - ipBlocks: ["1.2.3.4", "5.6.7.0/24"] ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-9 - namespace: foo -spec: - rules: - - when: - - key: "request.headers[X-abc]" - values: ["abc1", "abc2"] ---- diff --git a/pilot/pkg/security/authz/builder/testdata/http/multiple-policies-out.yaml b/pilot/pkg/security/authz/builder/testdata/http/multiple-policies-out.yaml deleted file mode 100644 index ba6479769..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/multiple-policies-out.yaml +++ /dev/null @@ -1,162 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - rules: - policies: - ns[foo]-policy[httpbin-1]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - header: - exactMatch: GET - name: :method - - header: - exactMatch: POST - name: :method - principals: - - andIds: - ids: - - any: true - ns[foo]-policy[httpbin-2]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - urlPath: - path: - exact: /v1 - - urlPath: - path: - exact: /v2 - principals: - - andIds: - ids: - - any: true - ns[foo]-policy[httpbin-3]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - header: - name: :authority - stringMatch: - exact: google.com - ignoreCase: true - - header: - name: :authority - stringMatch: - exact: httpbin.org - ignoreCase: true - principals: - - andIds: - ids: - - any: true - ns[foo]-policy[httpbin-4]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - destinationPort: 80 - - destinationPort: 90 - principals: - - andIds: - ids: - - any: true - ns[foo]-policy[httpbin-5]-rule[0]: - permissions: - - andRules: - rules: - - any: true - principals: - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://principals1 - - authenticated: - principalName: - exact: spiffe://principals2 - ns[foo]-policy[httpbin-6]-rule[0]: - permissions: - - andRules: - rules: - - any: true - principals: - - andIds: - ids: - - orIds: - ids: - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - exact: requestPrincipals1 - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - exact: requestPrincipals2 - ns[foo]-policy[httpbin-7]-rule[0]: - permissions: - - andRules: - rules: - - any: true - principals: - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/namespaces1/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/namespaces2/.* - ns[foo]-policy[httpbin-8]-rule[0]: - permissions: - - andRules: - rules: - - any: true - principals: - - andIds: - ids: - - orIds: - ids: - - directRemoteIp: - addressPrefix: 1.2.3.4 - prefixLen: 32 - - directRemoteIp: - addressPrefix: 5.6.7.0 - prefixLen: 24 - ns[foo]-policy[httpbin-9]-rule[0]: - permissions: - - andRules: - rules: - - any: true - principals: - - andIds: - ids: - - orIds: - ids: - - header: - exactMatch: abc1 - name: X-abc - - header: - exactMatch: abc2 - name: X-abc - shadowRulesStatPrefix: istio_dry_run_allow_ diff --git a/pilot/pkg/security/authz/builder/testdata/http/simple-policy-multiple-td-aliases-in.yaml b/pilot/pkg/security/authz/builder/testdata/http/simple-policy-multiple-td-aliases-in.yaml deleted file mode 100644 index 4a81374b4..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/simple-policy-multiple-td-aliases-in.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin - namespace: foo -spec: - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - from: - - source: - principals: ["cluster.local/ns/rule[0]/sa/from[0]-principal[0]"] - - source: - principals: ["some-td/ns/rule[0]/sa/from[1]-principal[0]", "cluster.local/ns/rule[0]/sa/from[1]-principal[1]"] - namespaces: ["rule[0]-from[1]-ns[0]"] \ No newline at end of file diff --git a/pilot/pkg/security/authz/builder/testdata/http/simple-policy-multiple-td-aliases-out.yaml b/pilot/pkg/security/authz/builder/testdata/http/simple-policy-multiple-td-aliases-out.yaml deleted file mode 100644 index 392f3e17d..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/simple-policy-multiple-td-aliases-out.yaml +++ /dev/null @@ -1,54 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - rules: - policies: - ns[foo]-policy[httpbin]-rule[0]: - permissions: - - andRules: - rules: - - any: true - principals: - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://td1/ns/rule[0]/sa/from[0]-principal[0] - - authenticated: - principalName: - exact: spiffe://cluster.local/ns/rule[0]/sa/from[0]-principal[0] - - authenticated: - principalName: - exact: spiffe://some-td/ns/rule[0]/sa/from[0]-principal[0] - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://td1/ns/rule[0]/sa/from[1]-principal[0] - - authenticated: - principalName: - exact: spiffe://cluster.local/ns/rule[0]/sa/from[1]-principal[0] - - authenticated: - principalName: - exact: spiffe://some-td/ns/rule[0]/sa/from[1]-principal[0] - - authenticated: - principalName: - exact: spiffe://td1/ns/rule[0]/sa/from[1]-principal[1] - - authenticated: - principalName: - exact: spiffe://cluster.local/ns/rule[0]/sa/from[1]-principal[1] - - authenticated: - principalName: - exact: spiffe://some-td/ns/rule[0]/sa/from[1]-principal[1] - - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/rule[0]-from[1]-ns[0]/.* - shadowRulesStatPrefix: istio_dry_run_allow_ diff --git a/pilot/pkg/security/authz/builder/testdata/http/simple-policy-principal-with-wildcard-in.yaml b/pilot/pkg/security/authz/builder/testdata/http/simple-policy-principal-with-wildcard-in.yaml deleted file mode 100644 index c4d658ef2..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/simple-policy-principal-with-wildcard-in.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin - namespace: foo -spec: - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - from: - - source: - principals: ["*"] - - source: - principals: ["*/ns/foo/sa/rule[0]-from[1]-principal[0]", "*bar/ns/foo/sa/rule[0]-from[1]-principal[1]"] \ No newline at end of file diff --git a/pilot/pkg/security/authz/builder/testdata/http/simple-policy-principal-with-wildcard-out.yaml b/pilot/pkg/security/authz/builder/testdata/http/simple-policy-principal-with-wildcard-out.yaml deleted file mode 100644 index 61a394818..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/simple-policy-principal-with-wildcard-out.yaml +++ /dev/null @@ -1,38 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - rules: - policies: - ns[foo]-policy[httpbin]-rule[0]: - permissions: - - andRules: - rules: - - any: true - principals: - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .+ - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: spiffe://.*/ns/foo/sa/rule[0]-from[1]-principal[0] - - authenticated: - principalName: - exact: spiffe://td1/ns/foo/sa/rule[0]-from[1]-principal[1] - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: spiffe://.*bar/ns/foo/sa/rule[0]-from[1]-principal[1] - shadowRulesStatPrefix: istio_dry_run_allow_ diff --git a/pilot/pkg/security/authz/builder/testdata/http/simple-policy-td-aliases-in.yaml b/pilot/pkg/security/authz/builder/testdata/http/simple-policy-td-aliases-in.yaml deleted file mode 100644 index c40e1da4d..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/simple-policy-td-aliases-in.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin - namespace: foo -spec: - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - from: - - source: - principals: ["cluster.local/ns/rule[0]/sa/from[0]-principal[0]"] - - source: - principals: ["cluster.local/ns/rule[0]/sa/from[1]-principal[0]", "cluster.local/ns/rule[0]/sa/from[1]-principal[1]"] - namespaces: ["rule[0]-from[1]-ns[0]"] - to: - - operation: - methods: ["rule[0]-to[0]-method[0]"] - - from: - - source: - principals: ["cluster.local/ns/rule[1]/sa/from[0]-principal[0]"] - to: - - operation: - methods: ["rule[1]-to[0]-method[0]"] diff --git a/pilot/pkg/security/authz/builder/testdata/http/simple-policy-td-aliases-out.yaml b/pilot/pkg/security/authz/builder/testdata/http/simple-policy-td-aliases-out.yaml deleted file mode 100644 index 748020c14..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/simple-policy-td-aliases-out.yaml +++ /dev/null @@ -1,69 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - rules: - policies: - ns[foo]-policy[httpbin]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - header: - exactMatch: rule[0]-to[0]-method[0] - name: :method - principals: - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://td1/ns/rule[0]/sa/from[0]-principal[0] - - authenticated: - principalName: - exact: spiffe://cluster.local/ns/rule[0]/sa/from[0]-principal[0] - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://td1/ns/rule[0]/sa/from[1]-principal[0] - - authenticated: - principalName: - exact: spiffe://cluster.local/ns/rule[0]/sa/from[1]-principal[0] - - authenticated: - principalName: - exact: spiffe://td1/ns/rule[0]/sa/from[1]-principal[1] - - authenticated: - principalName: - exact: spiffe://cluster.local/ns/rule[0]/sa/from[1]-principal[1] - - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/rule[0]-from[1]-ns[0]/.* - ns[foo]-policy[httpbin]-rule[1]: - permissions: - - andRules: - rules: - - orRules: - rules: - - header: - exactMatch: rule[1]-to[0]-method[0] - name: :method - principals: - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://td1/ns/rule[1]/sa/from[0]-principal[0] - - authenticated: - principalName: - exact: spiffe://cluster.local/ns/rule[1]/sa/from[0]-principal[0] - shadowRulesStatPrefix: istio_dry_run_allow_ diff --git a/pilot/pkg/security/authz/builder/testdata/http/single-policy-in.yaml b/pilot/pkg/security/authz/builder/testdata/http/single-policy-in.yaml deleted file mode 100644 index 7672fe25e..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/single-policy-in.yaml +++ /dev/null @@ -1,66 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin - namespace: foo -spec: - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - from: - - source: - principals: ["rule[0]-from[0]-principal[1]", "rule[0]-from[0]-principal[2]"] - requestPrincipals: ["rule[0]-from[0]-requestPrincipal[1]", "rule[0]-from[0]-requestPrincipal[2]"] - namespaces: ["rule[0]-from[0]-ns[1]", "rule[0]-from[0]-ns[2]"] - ipBlocks: ["10.0.0.1", "10.0.0.2"] - remoteIpBlocks: ["172.16.10.10"] - - source: - principals: ["rule[0]-from[1]-principal[1]", "rule[0]-from[1]-principal[2]"] - requestPrincipals: ["rule[0]-from[1]-requestPrincipal[1]", "rule[0]-from[1]-requestPrincipal[2]"] - namespaces: ["rule[0]-from[1]-ns[1]", "rule[0]-from[1]-ns[2]"] - ipBlocks: ["10.0.1.1", "192.0.1.2"] - remoteIpBlocks: ["172.17.8.0/24", "172.17.9.4"] - to: - - operation: - methods: ["rule[0]-to[0]-method[1]", "rule[0]-to[0]-method[2]"] - hosts: ["rule[0]-to[0]-host[1]", "rule[0]-to[0]-host[2]"] - ports: ["9001", "9002"] - paths: ["rule[0]-to[0]-path[1]", "rule[0]-to[0]-path[2]"] - - operation: - methods: ["rule[0]-to[1]-method[1]", "rule[0]-to[1]-method[2]"] - hosts: ["rule[0]-to[1]-host[1]", "rule[0]-to[1]-host[2]"] - ports: ["9011", "9012"] - paths: ["rule[0]-to[1]-path[1]", "rule[0]-to[1]-path[2]"] - when: - - key: "request.headers[X-header]" - values: ["header", "header-prefix-*", "*-suffix-header", "*"] - - key: "destination.ip" - values: ["10.10.10.10", "192.168.10.0/24"] - - key: "remote.ip" - values: ["10.99.10.8", "10.80.64.0/18"] - - from: - - source: - principals: ["rule[1]-from[0]-principal[1]", "rule[1]-from[0]-principal[2]"] - requestPrincipals: ["rule[1]-from[0]-requestPrincipal[1]", "rule[1]-from[0]-requestPrincipal[2]"] - namespaces: ["rule[1]-from[0]-ns[1]", "rule[1]-from[0]-ns[2]"] - ipBlocks: ["10.1.0.1", "10.1.0.2"] - remoteIpBlocks: ["172.22.2.0/23", "172.21.234.254"] - - source: - principals: ["rule[1]-from[1]-principal[1]", "rule[1]-from[1]-principal[2]"] - requestPrincipals: ["rule[1]-from[1]-requestPrincipal[1]", "rule[1]-from[1]-requestPrincipal[2]"] - namespaces: ["rule[1]-from[1]-ns[1]", "rule[1]-from[1]-ns[2]"] - ipBlocks: ["10.1.1.1", "192.1.1.2"] - remoteIpBlocks: ["192.168.4.0/24", "192.168.7.8"] - to: - - operation: - methods: ["rule[1]-to[0]-method[1]", "rule[1]-to[0]-method[2]"] - hosts: ["rule[1]-to[0]-host[1]", "rule[1]-to[0]-host[2]"] - ports: ["9101", "9102"] - paths: ["rule[1]-to[0]-path[1]", "rule[1]-to[0]-path[2]"] - - operation: - methods: ["rule[1]-to[1]-method[1]", "rule[1]-to[1]-method[2]"] - hosts: ["rule[1]-to[1]-host[1]", "rule[1]-to[1]-host[2]"] - ports: ["9111", "9112"] - paths: ["rule[1]-to[1]-path[1]", "rule[1]-to[1]-path[2]"] diff --git a/pilot/pkg/security/authz/builder/testdata/http/single-policy-out.yaml b/pilot/pkg/security/authz/builder/testdata/http/single-policy-out.yaml deleted file mode 100644 index 1543d1874..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/single-policy-out.yaml +++ /dev/null @@ -1,421 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - rules: - policies: - ns[foo]-policy[httpbin]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - header: - name: :authority - stringMatch: - exact: rule[0]-to[0]-host[1] - ignoreCase: true - - header: - name: :authority - stringMatch: - exact: rule[0]-to[0]-host[2] - ignoreCase: true - - orRules: - rules: - - header: - exactMatch: rule[0]-to[0]-method[1] - name: :method - - header: - exactMatch: rule[0]-to[0]-method[2] - name: :method - - orRules: - rules: - - urlPath: - path: - exact: rule[0]-to[0]-path[1] - - urlPath: - path: - exact: rule[0]-to[0]-path[2] - - orRules: - rules: - - destinationPort: 9001 - - destinationPort: 9002 - - orRules: - rules: - - destinationIp: - addressPrefix: 10.10.10.10 - prefixLen: 32 - - destinationIp: - addressPrefix: 192.168.10.0 - prefixLen: 24 - - andRules: - rules: - - orRules: - rules: - - header: - name: :authority - stringMatch: - exact: rule[0]-to[1]-host[1] - ignoreCase: true - - header: - name: :authority - stringMatch: - exact: rule[0]-to[1]-host[2] - ignoreCase: true - - orRules: - rules: - - header: - exactMatch: rule[0]-to[1]-method[1] - name: :method - - header: - exactMatch: rule[0]-to[1]-method[2] - name: :method - - orRules: - rules: - - urlPath: - path: - exact: rule[0]-to[1]-path[1] - - urlPath: - path: - exact: rule[0]-to[1]-path[2] - - orRules: - rules: - - destinationPort: 9011 - - destinationPort: 9012 - - orRules: - rules: - - destinationIp: - addressPrefix: 10.10.10.10 - prefixLen: 32 - - destinationIp: - addressPrefix: 192.168.10.0 - prefixLen: 24 - principals: - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://rule[0]-from[0]-principal[1] - - authenticated: - principalName: - exact: spiffe://rule[0]-from[0]-principal[2] - - orIds: - ids: - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - exact: rule[0]-from[0]-requestPrincipal[1] - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - exact: rule[0]-from[0]-requestPrincipal[2] - - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/rule[0]-from[0]-ns[1]/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/rule[0]-from[0]-ns[2]/.* - - orIds: - ids: - - remoteIp: - addressPrefix: 172.16.10.10 - prefixLen: 32 - - orIds: - ids: - - directRemoteIp: - addressPrefix: 10.0.0.1 - prefixLen: 32 - - directRemoteIp: - addressPrefix: 10.0.0.2 - prefixLen: 32 - - orIds: - ids: - - header: - exactMatch: header - name: X-header - - header: - name: X-header - prefixMatch: header-prefix- - - header: - name: X-header - suffixMatch: -suffix-header - - header: - name: X-header - presentMatch: true - - orIds: - ids: - - remoteIp: - addressPrefix: 10.99.10.8 - prefixLen: 32 - - remoteIp: - addressPrefix: 10.80.64.0 - prefixLen: 18 - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://rule[0]-from[1]-principal[1] - - authenticated: - principalName: - exact: spiffe://rule[0]-from[1]-principal[2] - - orIds: - ids: - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - exact: rule[0]-from[1]-requestPrincipal[1] - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - exact: rule[0]-from[1]-requestPrincipal[2] - - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/rule[0]-from[1]-ns[1]/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/rule[0]-from[1]-ns[2]/.* - - orIds: - ids: - - remoteIp: - addressPrefix: 172.17.8.0 - prefixLen: 24 - - remoteIp: - addressPrefix: 172.17.9.4 - prefixLen: 32 - - orIds: - ids: - - directRemoteIp: - addressPrefix: 10.0.1.1 - prefixLen: 32 - - directRemoteIp: - addressPrefix: 192.0.1.2 - prefixLen: 32 - - orIds: - ids: - - header: - exactMatch: header - name: X-header - - header: - name: X-header - prefixMatch: header-prefix- - - header: - name: X-header - suffixMatch: -suffix-header - - header: - name: X-header - presentMatch: true - - orIds: - ids: - - remoteIp: - addressPrefix: 10.99.10.8 - prefixLen: 32 - - remoteIp: - addressPrefix: 10.80.64.0 - prefixLen: 18 - ns[foo]-policy[httpbin]-rule[1]: - permissions: - - andRules: - rules: - - orRules: - rules: - - header: - name: :authority - stringMatch: - exact: rule[1]-to[0]-host[1] - ignoreCase: true - - header: - name: :authority - stringMatch: - exact: rule[1]-to[0]-host[2] - ignoreCase: true - - orRules: - rules: - - header: - exactMatch: rule[1]-to[0]-method[1] - name: :method - - header: - exactMatch: rule[1]-to[0]-method[2] - name: :method - - orRules: - rules: - - urlPath: - path: - exact: rule[1]-to[0]-path[1] - - urlPath: - path: - exact: rule[1]-to[0]-path[2] - - orRules: - rules: - - destinationPort: 9101 - - destinationPort: 9102 - - andRules: - rules: - - orRules: - rules: - - header: - name: :authority - stringMatch: - exact: rule[1]-to[1]-host[1] - ignoreCase: true - - header: - name: :authority - stringMatch: - exact: rule[1]-to[1]-host[2] - ignoreCase: true - - orRules: - rules: - - header: - exactMatch: rule[1]-to[1]-method[1] - name: :method - - header: - exactMatch: rule[1]-to[1]-method[2] - name: :method - - orRules: - rules: - - urlPath: - path: - exact: rule[1]-to[1]-path[1] - - urlPath: - path: - exact: rule[1]-to[1]-path[2] - - orRules: - rules: - - destinationPort: 9111 - - destinationPort: 9112 - principals: - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://rule[1]-from[0]-principal[1] - - authenticated: - principalName: - exact: spiffe://rule[1]-from[0]-principal[2] - - orIds: - ids: - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - exact: rule[1]-from[0]-requestPrincipal[1] - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - exact: rule[1]-from[0]-requestPrincipal[2] - - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/rule[1]-from[0]-ns[1]/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/rule[1]-from[0]-ns[2]/.* - - orIds: - ids: - - remoteIp: - addressPrefix: 172.22.2.0 - prefixLen: 23 - - remoteIp: - addressPrefix: 172.21.234.254 - prefixLen: 32 - - orIds: - ids: - - directRemoteIp: - addressPrefix: 10.1.0.1 - prefixLen: 32 - - directRemoteIp: - addressPrefix: 10.1.0.2 - prefixLen: 32 - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://rule[1]-from[1]-principal[1] - - authenticated: - principalName: - exact: spiffe://rule[1]-from[1]-principal[2] - - orIds: - ids: - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - exact: rule[1]-from[1]-requestPrincipal[1] - - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - exact: rule[1]-from[1]-requestPrincipal[2] - - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/rule[1]-from[1]-ns[1]/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/rule[1]-from[1]-ns[2]/.* - - orIds: - ids: - - remoteIp: - addressPrefix: 192.168.4.0 - prefixLen: 24 - - remoteIp: - addressPrefix: 192.168.7.8 - prefixLen: 32 - - orIds: - ids: - - directRemoteIp: - addressPrefix: 10.1.1.1 - prefixLen: 32 - - directRemoteIp: - addressPrefix: 192.1.1.2 - prefixLen: 32 - shadowRulesStatPrefix: istio_dry_run_allow_ diff --git a/pilot/pkg/security/authz/builder/testdata/http/td-aliases-source-principal-in.yaml b/pilot/pkg/security/authz/builder/testdata/http/td-aliases-source-principal-in.yaml deleted file mode 100644 index d8cf3ef9a..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/td-aliases-source-principal-in.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin - namespace: foo -spec: - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - from: - - source: - namespaces: ["dubbo-system"] - when: - - key: "source.principal" - values: ["*", "*/ns/foo/sa/all-td", "*-td/ns/foo/sa/prefix-td"] \ No newline at end of file diff --git a/pilot/pkg/security/authz/builder/testdata/http/td-aliases-source-principal-out.yaml b/pilot/pkg/security/authz/builder/testdata/http/td-aliases-source-principal-out.yaml deleted file mode 100644 index ae11c473c..000000000 --- a/pilot/pkg/security/authz/builder/testdata/http/td-aliases-source-principal-out.yaml +++ /dev/null @@ -1,41 +0,0 @@ -name: envoy.filters.http.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - rules: - policies: - ns[foo]-policy[httpbin]-rule[0]: - permissions: - - andRules: - rules: - - any: true - principals: - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/dubbo-system/.* - - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .+ - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: spiffe://.*/ns/foo/sa/all-td - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: spiffe://.*-td/ns/foo/sa/prefix-td - - authenticated: - principalName: - exact: spiffe://some-trustdomain/ns/foo/sa/prefix-td - shadowRulesStatPrefix: istio_dry_run_allow_ diff --git a/pilot/pkg/security/authz/builder/testdata/tcp/allow-both-http-tcp-in.yaml b/pilot/pkg/security/authz/builder/testdata/tcp/allow-both-http-tcp-in.yaml deleted file mode 100644 index 8db6a66eb..000000000 --- a/pilot/pkg/security/authz/builder/testdata/tcp/allow-both-http-tcp-in.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-deny - namespace: foo -spec: - action: ALLOW - rules: - - from: - - source: - requestPrincipals: ["id-1"] - to: - - operation: - methods: ["GET"] - - from: - - source: - namespaces: ["ns-1"] - to: - - operation: - ports: ["80"] diff --git a/pilot/pkg/security/authz/builder/testdata/tcp/allow-both-http-tcp-out.yaml b/pilot/pkg/security/authz/builder/testdata/tcp/allow-both-http-tcp-out.yaml deleted file mode 100644 index 1bb8747fa..000000000 --- a/pilot/pkg/security/authz/builder/testdata/tcp/allow-both-http-tcp-out.yaml +++ /dev/null @@ -1,24 +0,0 @@ -name: envoy.filters.network.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC - rules: - policies: - ns[foo]-policy[httpbin-deny]-rule[1]: - permissions: - - andRules: - rules: - - orRules: - rules: - - destinationPort: 80 - principals: - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/ns-1/.* - shadowRulesStatPrefix: istio_dry_run_allow_ - statPrefix: tcp. diff --git a/pilot/pkg/security/authz/builder/testdata/tcp/allow-only-http-in.yaml b/pilot/pkg/security/authz/builder/testdata/tcp/allow-only-http-in.yaml deleted file mode 100644 index 5a0ca258c..000000000 --- a/pilot/pkg/security/authz/builder/testdata/tcp/allow-only-http-in.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-deny - namespace: foo -spec: - action: ALLOW - rules: - - from: - - source: - requestPrincipals: ["id-1"] - to: - - operation: - methods: ["GET"] - - from: - - source: - namespaces: ["ns-1"] - to: - - operation: - hosts: ["example.com"] diff --git a/pilot/pkg/security/authz/builder/testdata/tcp/allow-only-http-out.yaml b/pilot/pkg/security/authz/builder/testdata/tcp/allow-only-http-out.yaml deleted file mode 100644 index 51e2c2241..000000000 --- a/pilot/pkg/security/authz/builder/testdata/tcp/allow-only-http-out.yaml +++ /dev/null @@ -1,6 +0,0 @@ -name: envoy.filters.network.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC - rules: {} - shadowRulesStatPrefix: istio_dry_run_allow_ - statPrefix: tcp. diff --git a/pilot/pkg/security/authz/builder/testdata/tcp/audit-both-http-tcp-in.yaml b/pilot/pkg/security/authz/builder/testdata/tcp/audit-both-http-tcp-in.yaml deleted file mode 100644 index 5a4c32e80..000000000 --- a/pilot/pkg/security/authz/builder/testdata/tcp/audit-both-http-tcp-in.yaml +++ /dev/null @@ -1,71 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-audit - namespace: foo -spec: - action: AUDIT - rules: - # rule[0] `from`: all fields, `to`: all fields, `when`: all fields. - - from: - - source: - principals: ["principal"] - requestPrincipals: ["requestPrincipals"] - namespaces: ["ns"] - ipBlocks: ["1.2.3.4"] - remoteIpBlocks: ["10.250.90.4"] - notPrincipals: ["not-principal"] - notRequestPrincipals: ["not-requestPrincipals"] - notNamespaces: ["not-ns"] - notIpBlocks: ["9.0.0.1"] - notRemoteIpBlocks: ["10.133.154.65"] - to: - - operation: - methods: ["method"] - hosts: ["exact.com"] - ports: ["80"] - paths: ["/exact"] - notMethods: ["not-method"] - notHosts: ["not-exact.com"] - notPorts: ["8000"] - notPaths: ["/not-exact"] - when: - - key: "request.headers[X-header]" - values: ["header"] - notValues: ["not-header"] - - key: "source.ip" - values: ["10.10.10.10"] - notValues: ["90.10.10.10"] - - key: "remote.ip" - values: ["192.168.7.7"] - notValues: ["192.168.10.9"] - - key: "source.namespace" - values: ["ns"] - notValues: ["not-ns"] - - key: "source.principal" - values: ["principal"] - notValues: ["not-principal"] - - key: "request.auth.principal" - values: ["requestPrincipals"] - notValues: ["not-requestPrincipals"] - - key: "request.auth.audiences" - values: ["audiences"] - notValues: ["not-audiences"] - - key: "request.auth.presenter" - values: ["presenter"] - notValues: ["not-presenter"] - - key: "request.auth.claims[iss]" - values: ["iss"] - notValues: ["not-iss"] - - key: "destination.ip" - values: ["10.10.10.10"] - notValues: ["90.10.10.10"] - - key: "destination.port" - values: ["91"] - notValues: ["9001"] - - key: "connection.sni" - values: ["exact.com"] - notValues: ["not-exact.com"] - - key: "experimental.envoy.filters.a.b[c]" - values: ["exact"] - notValues: ["not-exact"] diff --git a/pilot/pkg/security/authz/builder/testdata/tcp/audit-both-http-tcp-out.yaml b/pilot/pkg/security/authz/builder/testdata/tcp/audit-both-http-tcp-out.yaml deleted file mode 100644 index 3b1806625..000000000 --- a/pilot/pkg/security/authz/builder/testdata/tcp/audit-both-http-tcp-out.yaml +++ /dev/null @@ -1,164 +0,0 @@ -name: envoy.filters.network.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC - rules: - action: LOG - policies: - ns[foo]-policy[httpbin-audit]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - destinationPort: 80 - - notRule: - orRules: - rules: - - destinationPort: 8000 - - orRules: - rules: - - destinationIp: - addressPrefix: 10.10.10.10 - prefixLen: 32 - - notRule: - orRules: - rules: - - destinationIp: - addressPrefix: 90.10.10.10 - prefixLen: 32 - - orRules: - rules: - - destinationPort: 91 - - notRule: - orRules: - rules: - - destinationPort: 9001 - - orRules: - rules: - - requestedServerName: - exact: exact.com - - notRule: - orRules: - rules: - - requestedServerName: - exact: not-exact.com - - orRules: - rules: - - metadata: - filter: envoy.filters.a.b - path: - - key: c - value: - stringMatch: - exact: exact - - notRule: - orRules: - rules: - - metadata: - filter: envoy.filters.a.b - path: - - key: c - value: - stringMatch: - exact: not-exact - principals: - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://principal - - notId: - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://not-principal - - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/ns/.* - - notId: - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/not-ns/.* - - orIds: - ids: - - remoteIp: - addressPrefix: 10.250.90.4 - prefixLen: 32 - - notId: - orIds: - ids: - - remoteIp: - addressPrefix: 10.133.154.65 - prefixLen: 32 - - orIds: - ids: - - directRemoteIp: - addressPrefix: 1.2.3.4 - prefixLen: 32 - - notId: - orIds: - ids: - - directRemoteIp: - addressPrefix: 9.0.0.1 - prefixLen: 32 - - orIds: - ids: - - directRemoteIp: - addressPrefix: 10.10.10.10 - prefixLen: 32 - - notId: - orIds: - ids: - - directRemoteIp: - addressPrefix: 90.10.10.10 - prefixLen: 32 - - orIds: - ids: - - remoteIp: - addressPrefix: 192.168.7.7 - prefixLen: 32 - - notId: - orIds: - ids: - - remoteIp: - addressPrefix: 192.168.10.9 - prefixLen: 32 - - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/ns/.* - - notId: - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/not-ns/.* - - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://principal - - notId: - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://not-principal - shadowRulesStatPrefix: istio_dry_run_allow_ - statPrefix: tcp. diff --git a/pilot/pkg/security/authz/builder/testdata/tcp/custom-both-http-tcp-in.yaml b/pilot/pkg/security/authz/builder/testdata/tcp/custom-both-http-tcp-in.yaml deleted file mode 100644 index 279daa373..000000000 --- a/pilot/pkg/security/authz/builder/testdata/tcp/custom-both-http-tcp-in.yaml +++ /dev/null @@ -1,57 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-deny - namespace: foo -spec: - action: CUSTOM - provider: - name: default - rules: - # rule[0] `from`: nil, `to`: HTTP field. - - to: - - operation: - methods: ["GET"] - # rule[1] `from`: TCP field, `to`: HTTP field. - - from: - - source: - ipBlocks: ["1.2.3.4"] - to: - - operation: - methods: ["GET"] - # rule[2] `from`: TCP field, `to`: TCP field. - - from: - - source: - ipBlocks: ["1.2.3.4"] - to: - - operation: - ports: ["80"] - # rule[3] `from`: nil, `to`: nil, `when`: HTTP field. - - when: - - key: "request.headers[:method]" - values: ["GET"] - # rule[4] `from`: nil, `to`: nil, `when`: TCP field. - - when: - - key: "destination.port" - values: ["80"] - # rule[5] `from`: all fields, `to`: all fields, `when`: all fields. - - from: - - source: - ipBlocks: ["1.2.3.4"] - remoteIpBlocks: ["172.18.4.0/22"] - notIpBlocks: ["9.0.0.1"] - notRemoteIpBlocks: ["192.168.244.139"] - to: - - operation: - methods: ["method"] - hosts: ["exact.com"] - ports: ["80"] - paths: ["/exact"] - notMethods: ["not-method"] - notHosts: ["not-exact.com"] - notPorts: ["8000"] - notPaths: ["/not-exact"] - when: - - key: "request.headers[X-header]" - values: ["header"] - notValues: ["not-header"] diff --git a/pilot/pkg/security/authz/builder/testdata/tcp/custom-both-http-tcp-out1.yaml b/pilot/pkg/security/authz/builder/testdata/tcp/custom-both-http-tcp-out1.yaml deleted file mode 100644 index 92014ac3e..000000000 --- a/pilot/pkg/security/authz/builder/testdata/tcp/custom-both-http-tcp-out1.yaml +++ /dev/null @@ -1,101 +0,0 @@ -name: envoy.filters.network.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC - shadowRules: - action: DENY - policies: - istio-ext-authz-ns[foo]-policy[httpbin-deny]-rule[0]: - permissions: - - andRules: - rules: - - any: true - principals: - - andIds: - ids: - - any: true - istio-ext-authz-ns[foo]-policy[httpbin-deny]-rule[1]: - permissions: - - andRules: - rules: - - any: true - principals: - - andIds: - ids: - - orIds: - ids: - - directRemoteIp: - addressPrefix: 1.2.3.4 - prefixLen: 32 - istio-ext-authz-ns[foo]-policy[httpbin-deny]-rule[2]: - permissions: - - andRules: - rules: - - orRules: - rules: - - destinationPort: 80 - principals: - - andIds: - ids: - - orIds: - ids: - - directRemoteIp: - addressPrefix: 1.2.3.4 - prefixLen: 32 - istio-ext-authz-ns[foo]-policy[httpbin-deny]-rule[3]: - permissions: - - andRules: - rules: - - any: true - principals: - - andIds: - ids: - - any: true - istio-ext-authz-ns[foo]-policy[httpbin-deny]-rule[4]: - permissions: - - andRules: - rules: - - orRules: - rules: - - destinationPort: 80 - principals: - - andIds: - ids: - - any: true - istio-ext-authz-ns[foo]-policy[httpbin-deny]-rule[5]: - permissions: - - andRules: - rules: - - orRules: - rules: - - destinationPort: 80 - - notRule: - orRules: - rules: - - destinationPort: 8000 - principals: - - andIds: - ids: - - orIds: - ids: - - remoteIp: - addressPrefix: 172.18.4.0 - prefixLen: 22 - - notId: - orIds: - ids: - - remoteIp: - addressPrefix: 192.168.244.139 - prefixLen: 32 - - orIds: - ids: - - directRemoteIp: - addressPrefix: 1.2.3.4 - prefixLen: 32 - - notId: - orIds: - ids: - - directRemoteIp: - addressPrefix: 9.0.0.1 - prefixLen: 32 - shadowRulesStatPrefix: istio_ext_authz_ - statPrefix: tcp. diff --git a/pilot/pkg/security/authz/builder/testdata/tcp/custom-both-http-tcp-out2.yaml b/pilot/pkg/security/authz/builder/testdata/tcp/custom-both-http-tcp-out2.yaml deleted file mode 100644 index 7e1722e08..000000000 --- a/pilot/pkg/security/authz/builder/testdata/tcp/custom-both-http-tcp-out2.yaml +++ /dev/null @@ -1,18 +0,0 @@ -name: envoy.filters.network.ext_authz -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.network.ext_authz.v3.ExtAuthz - failureModeAllow: true - filterEnabledMetadata: - filter: envoy.filters.network.rbac - path: - - key: istio_ext_authz_shadow_effective_policy_id - value: - stringMatch: - prefix: istio-ext-authz - grpcService: - envoyGrpc: - authority: outbound_.9000_._.my-custom-ext-authz.foo.svc.cluster.local - clusterName: outbound|9000||my-custom-ext-authz.foo.svc.cluster.local - timeout: 0.002s - statPrefix: tcp. - transportApiVersion: V3 diff --git a/pilot/pkg/security/authz/builder/testdata/tcp/custom-only-http-in.yaml b/pilot/pkg/security/authz/builder/testdata/tcp/custom-only-http-in.yaml deleted file mode 100644 index f5f8361ec..000000000 --- a/pilot/pkg/security/authz/builder/testdata/tcp/custom-only-http-in.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-1 - namespace: foo -spec: - action: CUSTOM - provider: - name: default - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - to: - - operation: - paths: ["/httpbin1"] ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-2 - namespace: foo -spec: - action: CUSTOM - provider: - name: default - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - to: - - operation: - paths: ["/httpbin2"] diff --git a/pilot/pkg/security/authz/builder/testdata/tcp/deny-both-http-tcp-in.yaml b/pilot/pkg/security/authz/builder/testdata/tcp/deny-both-http-tcp-in.yaml deleted file mode 100644 index a0048063f..000000000 --- a/pilot/pkg/security/authz/builder/testdata/tcp/deny-both-http-tcp-in.yaml +++ /dev/null @@ -1,115 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-deny - namespace: foo -spec: - action: DENY - rules: - # rule[0] `from`: HTTP field, `to`: HTTP field. - - from: - - source: - requestPrincipals: ["id-1"] - to: - - operation: - methods: ["GET"] - # rule[1] `from`: nil, `to`: HTTP field. - - to: - - operation: - methods: ["GET"] - # rule[2] `from`: HTTP field, `to`: nil. - - from: - - source: - requestPrincipals: ["id-1"] - # rule[3] `from`: TCP field, `to`: HTTP field. - - from: - - source: - namespaces: ["ns-1"] - to: - - operation: - methods: ["GET"] - # rule[4] `from`: HTTP field, `to`: TCP field. - - from: - - source: - requestPrincipals: ["id-1"] - to: - - operation: - ports: ["80"] - # rule[5] `from`: TCP field, `to`: TCP field. - - from: - - source: - namespaces: ["ns-1"] - to: - - operation: - ports: ["80"] - # rule[6] `from`: nil, `to`: nil, `when`: HTTP field. - - when: - - key: "request.headers[:method]" - values: ["GET"] - # rule[7] `from`: nil, `to`: nil, `when`: TCP field. - - when: - - key: "destination.port" - values: ["80"] - # rule[8] `from`: all fields, `to`: all fields, `when`: all fields. - - from: - - source: - principals: ["principal", "*principal-suffix", "principal-prefix*", "*"] - requestPrincipals: ["requestPrincipals"] - namespaces: ["ns", "*ns-suffix", "ns-prefix*", "*"] - ipBlocks: ["1.2.3.4"] - remoteIpBlocks: ["172.18.4.0/22"] - notPrincipals: ["not-principal", "*not-principal-suffix", "not-principal-prefix*", "*"] - notRequestPrincipals: ["not-requestPrincipals"] - notNamespaces: ["not-ns", "*not-ns-suffix", "not-ns-prefix*", "*"] - notIpBlocks: ["9.0.0.1"] - notRemoteIpBlocks: ["192.168.244.139"] - to: - - operation: - methods: ["method"] - hosts: ["exact.com"] - ports: ["80"] - paths: ["/exact"] - notMethods: ["not-method"] - notHosts: ["not-exact.com"] - notPorts: ["8000"] - notPaths: ["/not-exact"] - when: - - key: "request.headers[X-header]" - values: ["header"] - notValues: ["not-header"] - - key: "source.ip" - values: ["10.10.10.10"] - notValues: ["90.10.10.10"] - - key: "remote.ip" - values: ["192.168.3.3"] - notValues: ["172.19.31.3"] - - key: "source.namespace" - values: ["ns", "*ns-suffix", "ns-prefix*", "*"] - notValues: ["not-ns", "*not-ns-suffix", "not-ns-prefix*", "*"] - - key: "source.principal" - values: ["principal", "*principal-suffix", "principal-prefix*", "*"] - notValues: ["not-principal", "*not-principal-suffix", "not-principal-prefix*", "*"] - - key: "request.auth.principal" - values: ["requestPrincipals"] - notValues: ["not-requestPrincipals"] - - key: "request.auth.audiences" - values: ["audiences"] - notValues: ["not-audiences"] - - key: "request.auth.presenter" - values: ["presenter"] - notValues: ["not-presenter"] - - key: "request.auth.claims[iss]" - values: ["iss"] - notValues: ["not-iss"] - - key: "destination.ip" - values: ["10.10.10.10"] - notValues: ["90.10.10.10"] - - key: "destination.port" - values: ["91"] - notValues: ["9001"] - - key: "connection.sni" - values: ["exact.com"] - notValues: ["not-exact.com"] - - key: "experimental.envoy.filters.a.b[c]" - values: ["exact"] - notValues: ["not-exact"] diff --git a/pilot/pkg/security/authz/builder/testdata/tcp/deny-both-http-tcp-out.yaml b/pilot/pkg/security/authz/builder/testdata/tcp/deny-both-http-tcp-out.yaml deleted file mode 100644 index 87fc045af..000000000 --- a/pilot/pkg/security/authz/builder/testdata/tcp/deny-both-http-tcp-out.yaml +++ /dev/null @@ -1,366 +0,0 @@ -name: envoy.filters.network.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC - rules: - action: DENY - policies: - ns[foo]-policy[httpbin-deny]-rule[0]: - permissions: - - andRules: - rules: - - any: true - principals: - - andIds: - ids: - - any: true - ns[foo]-policy[httpbin-deny]-rule[1]: - permissions: - - andRules: - rules: - - any: true - principals: - - andIds: - ids: - - any: true - ns[foo]-policy[httpbin-deny]-rule[2]: - permissions: - - andRules: - rules: - - any: true - principals: - - andIds: - ids: - - any: true - ns[foo]-policy[httpbin-deny]-rule[3]: - permissions: - - andRules: - rules: - - any: true - principals: - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/ns-1/.* - ns[foo]-policy[httpbin-deny]-rule[4]: - permissions: - - andRules: - rules: - - orRules: - rules: - - destinationPort: 80 - principals: - - andIds: - ids: - - any: true - ns[foo]-policy[httpbin-deny]-rule[5]: - permissions: - - andRules: - rules: - - orRules: - rules: - - destinationPort: 80 - principals: - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/ns-1/.* - ns[foo]-policy[httpbin-deny]-rule[6]: - permissions: - - andRules: - rules: - - any: true - principals: - - andIds: - ids: - - any: true - ns[foo]-policy[httpbin-deny]-rule[7]: - permissions: - - andRules: - rules: - - orRules: - rules: - - destinationPort: 80 - principals: - - andIds: - ids: - - any: true - ns[foo]-policy[httpbin-deny]-rule[8]: - permissions: - - andRules: - rules: - - orRules: - rules: - - destinationPort: 80 - - notRule: - orRules: - rules: - - destinationPort: 8000 - - orRules: - rules: - - destinationIp: - addressPrefix: 10.10.10.10 - prefixLen: 32 - - notRule: - orRules: - rules: - - destinationIp: - addressPrefix: 90.10.10.10 - prefixLen: 32 - - orRules: - rules: - - destinationPort: 91 - - notRule: - orRules: - rules: - - destinationPort: 9001 - - orRules: - rules: - - requestedServerName: - exact: exact.com - - notRule: - orRules: - rules: - - requestedServerName: - exact: not-exact.com - - orRules: - rules: - - metadata: - filter: envoy.filters.a.b - path: - - key: c - value: - stringMatch: - exact: exact - - notRule: - orRules: - rules: - - metadata: - filter: envoy.filters.a.b - path: - - key: c - value: - stringMatch: - exact: not-exact - principals: - - andIds: - ids: - - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://principal - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: spiffe://.*principal-suffix - - authenticated: - principalName: - prefix: spiffe://principal-prefix - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .+ - - notId: - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://not-principal - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: spiffe://.*not-principal-suffix - - authenticated: - principalName: - prefix: spiffe://not-principal-prefix - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .+ - - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/ns/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/.*ns-suffix/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/ns-prefix.*/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/.*/.* - - notId: - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/not-ns/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/.*not-ns-suffix/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/not-ns-prefix.*/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/.*/.* - - orIds: - ids: - - remoteIp: - addressPrefix: 172.18.4.0 - prefixLen: 22 - - notId: - orIds: - ids: - - remoteIp: - addressPrefix: 192.168.244.139 - prefixLen: 32 - - orIds: - ids: - - directRemoteIp: - addressPrefix: 1.2.3.4 - prefixLen: 32 - - notId: - orIds: - ids: - - directRemoteIp: - addressPrefix: 9.0.0.1 - prefixLen: 32 - - orIds: - ids: - - directRemoteIp: - addressPrefix: 10.10.10.10 - prefixLen: 32 - - notId: - orIds: - ids: - - directRemoteIp: - addressPrefix: 90.10.10.10 - prefixLen: 32 - - orIds: - ids: - - remoteIp: - addressPrefix: 192.168.3.3 - prefixLen: 32 - - notId: - orIds: - ids: - - remoteIp: - addressPrefix: 172.19.31.3 - prefixLen: 32 - - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/ns/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/.*ns-suffix/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/ns-prefix.*/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/.*/.* - - notId: - orIds: - ids: - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/not-ns/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/.*not-ns-suffix/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/not-ns-prefix.*/.* - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/.*/.* - - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://principal - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: spiffe://.*principal-suffix - - authenticated: - principalName: - prefix: spiffe://principal-prefix - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .+ - - notId: - orIds: - ids: - - authenticated: - principalName: - exact: spiffe://not-principal - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: spiffe://.*not-principal-suffix - - authenticated: - principalName: - prefix: spiffe://not-principal-prefix - - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .+ - shadowRulesStatPrefix: istio_dry_run_allow_ - statPrefix: tcp. diff --git a/pilot/pkg/security/authz/builder/testdata/tcp/dry-run-mix-in.yaml b/pilot/pkg/security/authz/builder/testdata/tcp/dry-run-mix-in.yaml deleted file mode 100644 index 3d7ebc379..000000000 --- a/pilot/pkg/security/authz/builder/testdata/tcp/dry-run-mix-in.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-1 - namespace: foo - annotations: - "istio.io/dry-run": "true" -spec: - selector: - matchLabels: - app: httpbin - version: v1 - action: ALLOW - rules: - - to: - - operation: - ports: ["80"] ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-2 - namespace: foo - annotations: - "istio.io/dry-run": "false" -spec: - selector: - matchLabels: - app: httpbin - version: v1 - action: ALLOW - rules: - - to: - - operation: - ports: ["9000"] diff --git a/pilot/pkg/security/authz/builder/testdata/tcp/dry-run-mix-out.yaml b/pilot/pkg/security/authz/builder/testdata/tcp/dry-run-mix-out.yaml deleted file mode 100644 index ca9eb6690..000000000 --- a/pilot/pkg/security/authz/builder/testdata/tcp/dry-run-mix-out.yaml +++ /dev/null @@ -1,31 +0,0 @@ -name: envoy.filters.network.rbac -typedConfig: - '@type': type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC - rules: - policies: - ns[foo]-policy[httpbin-2]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - destinationPort: 9000 - principals: - - andIds: - ids: - - any: true - shadowRules: - policies: - ns[foo]-policy[httpbin-1]-rule[0]: - permissions: - - andRules: - rules: - - orRules: - rules: - - destinationPort: 80 - principals: - - andIds: - ids: - - any: true - shadowRulesStatPrefix: istio_dry_run_allow_ - statPrefix: tcp. diff --git a/pilot/pkg/security/authz/matcher/cidr.go b/pilot/pkg/security/authz/matcher/cidr.go deleted file mode 100644 index 022a05d02..000000000 --- a/pilot/pkg/security/authz/matcher/cidr.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package matcher - -import ( - "fmt" - "net" - "strings" -) - -import ( - corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" -) - -// CidrRange converts a CIDR or a single IP string to a corresponding CidrRange. For a single IP -// string the converted CidrRange prefix is either 32 (for ipv4) or 128 (for ipv6). -func CidrRange(v string) (*corepb.CidrRange, error) { - var address string - var prefixLen int - - if strings.Contains(v, "/") { - if ip, ipnet, err := net.ParseCIDR(v); err == nil { - address = ip.String() - prefixLen, _ = ipnet.Mask.Size() - } else { - return nil, fmt.Errorf("invalid cidr range: %v", err) - } - } else { - if ip := net.ParseIP(v); ip != nil { - address = ip.String() - if strings.Contains(v, ".") { - // Set the prefixLen to 32 for ipv4 address. - prefixLen = 32 - } else if strings.Contains(v, ":") { - // Set the prefixLen to 128 for ipv6 address. - prefixLen = 128 - } else { - return nil, fmt.Errorf("invalid ip address: %s", v) - } - } else { - return nil, fmt.Errorf("invalid ip address: %s", v) - } - } - - return &corepb.CidrRange{ - AddressPrefix: address, - PrefixLen: &wrappers.UInt32Value{Value: uint32(prefixLen)}, - }, nil -} diff --git a/pilot/pkg/security/authz/matcher/cidr_test.go b/pilot/pkg/security/authz/matcher/cidr_test.go deleted file mode 100644 index 8515e6c9d..000000000 --- a/pilot/pkg/security/authz/matcher/cidr_test.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package matcher - -import ( - "strings" - "testing" -) - -import ( - corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/testing/protocmp" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" -) - -func TestCidrRange(t *testing.T) { - testCases := []struct { - Name string - V string - Expect *corepb.CidrRange - Err string - }{ - { - Name: "cidr with two /", - V: "192.168.0.0//16", - Err: "invalid cidr range", - }, - { - Name: "cidr with invalid prefix length", - V: "192.168.0.0/ab", - Err: "invalid cidr range", - }, - { - Name: "cidr with negative prefix length", - V: "192.168.0.0/-16", - Err: "invalid cidr range", - }, - { - Name: "valid cidr range", - V: "192.168.0.0/16", - Expect: &corepb.CidrRange{ - AddressPrefix: "192.168.0.0", - PrefixLen: &wrappers.UInt32Value{Value: 16}, - }, - }, - { - Name: "invalid ip address", - V: "19216800", - Err: "invalid ip address", - }, - { - Name: "valid ipv4 address", - V: "192.168.0.0", - Expect: &corepb.CidrRange{ - AddressPrefix: "192.168.0.0", - PrefixLen: &wrappers.UInt32Value{Value: 32}, - }, - }, - { - Name: "valid ipv6 address", - V: "2001:abcd:85a3::8a2e:370:1234", - Expect: &corepb.CidrRange{ - AddressPrefix: "2001:abcd:85a3::8a2e:370:1234", - PrefixLen: &wrappers.UInt32Value{Value: 128}, - }, - }, - } - - for _, tc := range testCases { - actual, err := CidrRange(tc.V) - if tc.Err != "" { - if err == nil { - t.Errorf("%s: expecting error: %s but found no error", tc.Name, tc.Err) - } else if !strings.HasPrefix(err.Error(), tc.Err) { - t.Errorf("%s: expecting error: %s, but got: %s", tc.Name, tc.Err, err.Error()) - } - } else if !cmp.Equal(tc.Expect, actual, protocmp.Transform()) { - t.Errorf("%s: expecting %v, but got %v", tc.Name, tc.Expect, actual) - } - } -} diff --git a/pilot/pkg/security/authz/matcher/header.go b/pilot/pkg/security/authz/matcher/header.go deleted file mode 100644 index c7c639e08..000000000 --- a/pilot/pkg/security/authz/matcher/header.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package matcher - -import ( - "regexp" - "strings" -) - -import ( - routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" -) - -// HeaderMatcher converts a key, value string pair to a corresponding HeaderMatcher. -func HeaderMatcher(k, v string) *routepb.HeaderMatcher { - // We must check "*" first to make sure we'll generate a non empty value in the prefix/suffix case. - // Empty prefix/suffix value is invalid in HeaderMatcher. - if v == "*" { - return &routepb.HeaderMatcher{ - Name: k, - HeaderMatchSpecifier: &routepb.HeaderMatcher_PresentMatch{ - PresentMatch: true, - }, - } - } else if strings.HasPrefix(v, "*") { - return &routepb.HeaderMatcher{ - Name: k, - HeaderMatchSpecifier: &routepb.HeaderMatcher_SuffixMatch{ - SuffixMatch: v[1:], - }, - } - } else if strings.HasSuffix(v, "*") { - return &routepb.HeaderMatcher{ - Name: k, - HeaderMatchSpecifier: &routepb.HeaderMatcher_PrefixMatch{ - PrefixMatch: v[:len(v)-1], - }, - } - } - return &routepb.HeaderMatcher{ - Name: k, - HeaderMatchSpecifier: &routepb.HeaderMatcher_ExactMatch{ - ExactMatch: v, - }, - } -} - -// HostMatcherWithRegex creates a host matcher for a host using regex for proxies before 1.11. -func HostMatcherWithRegex(k, v string) *routepb.HeaderMatcher { - var regex string - if v == "*" { - return &routepb.HeaderMatcher{ - Name: k, - HeaderMatchSpecifier: &routepb.HeaderMatcher_PresentMatch{ - PresentMatch: true, - }, - } - } else if strings.HasPrefix(v, "*") { - regex = `.*` + regexp.QuoteMeta(v[1:]) - } else if strings.HasSuffix(v, "*") { - regex = regexp.QuoteMeta(v[:len(v)-1]) + `.*` - } else { - regex = regexp.QuoteMeta(v) - } - return &routepb.HeaderMatcher{ - Name: k, - HeaderMatchSpecifier: &routepb.HeaderMatcher_SafeRegexMatch{ - SafeRegexMatch: &matcherpb.RegexMatcher{ - EngineType: &matcherpb.RegexMatcher_GoogleRe2{ - GoogleRe2: &matcherpb.RegexMatcher_GoogleRE2{}, - }, - Regex: `(?i)` + regex, - }, - }, - } -} - -// HostMatcher creates a host matcher for a host. -func HostMatcher(k, v string) *routepb.HeaderMatcher { - // We must check "*" first to make sure we'll generate a non empty value in the prefix/suffix case. - // Empty prefix/suffix value is invalid in HeaderMatcher. - if v == "*" { - return &routepb.HeaderMatcher{ - Name: k, - HeaderMatchSpecifier: &routepb.HeaderMatcher_PresentMatch{ - PresentMatch: true, - }, - } - } else if strings.HasPrefix(v, "*") { - return &routepb.HeaderMatcher{ - Name: k, - HeaderMatchSpecifier: &routepb.HeaderMatcher_StringMatch{ - StringMatch: &matcherpb.StringMatcher{ - IgnoreCase: true, - MatchPattern: &matcherpb.StringMatcher_Suffix{ - Suffix: v[1:], - }, - }, - }, - } - } else if strings.HasSuffix(v, "*") { - return &routepb.HeaderMatcher{ - Name: k, - HeaderMatchSpecifier: &routepb.HeaderMatcher_StringMatch{ - StringMatch: &matcherpb.StringMatcher{ - IgnoreCase: true, - MatchPattern: &matcherpb.StringMatcher_Prefix{ - Prefix: v[:len(v)-1], - }, - }, - }, - } - } - return &routepb.HeaderMatcher{ - Name: k, - HeaderMatchSpecifier: &routepb.HeaderMatcher_StringMatch{ - StringMatch: &matcherpb.StringMatcher{ - IgnoreCase: true, - MatchPattern: &matcherpb.StringMatcher_Exact{ - Exact: v, - }, - }, - }, - } -} - -// PathMatcher creates a path matcher for a path. -func PathMatcher(path string) *matcherpb.PathMatcher { - return &matcherpb.PathMatcher{ - Rule: &matcherpb.PathMatcher_Path{ - Path: StringMatcher(path), - }, - } -} diff --git a/pilot/pkg/security/authz/matcher/header_test.go b/pilot/pkg/security/authz/matcher/header_test.go deleted file mode 100644 index b12adc549..000000000 --- a/pilot/pkg/security/authz/matcher/header_test.go +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package matcher - -import ( - "regexp" - "testing" -) - -import ( - routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/testing/protocmp" -) - -func TestHeaderMatcher(t *testing.T) { - testCases := []struct { - Name string - K string - V string - Expect *routepb.HeaderMatcher - }{ - { - Name: "exact match", - K: ":path", - V: "/productpage", - Expect: &routepb.HeaderMatcher{ - Name: ":path", - HeaderMatchSpecifier: &routepb.HeaderMatcher_ExactMatch{ - ExactMatch: "/productpage", - }, - }, - }, - { - Name: "suffix match", - K: ":path", - V: "*/productpage*", - Expect: &routepb.HeaderMatcher{ - Name: ":path", - HeaderMatchSpecifier: &routepb.HeaderMatcher_SuffixMatch{ - SuffixMatch: "/productpage*", - }, - }, - }, - } - - for _, tc := range testCases { - actual := HeaderMatcher(tc.K, tc.V) - if !cmp.Equal(tc.Expect, actual, protocmp.Transform()) { - t.Errorf("expecting %v, but got %v", tc.Expect, actual) - } - } -} - -func TestHostMatcherWithRegex(t *testing.T) { - testCases := []struct { - Name string - K string - V string - Expect *routepb.HeaderMatcher - }{ - { - Name: "present match", - K: ":authority", - V: "*", - Expect: &routepb.HeaderMatcher{ - Name: ":authority", - HeaderMatchSpecifier: &routepb.HeaderMatcher_PresentMatch{PresentMatch: true}, - }, - }, - { - Name: "prefix match", - K: ":authority", - V: "*.example.com", - Expect: &routepb.HeaderMatcher{ - Name: ":authority", - HeaderMatchSpecifier: &routepb.HeaderMatcher_SafeRegexMatch{ - SafeRegexMatch: &matcherpb.RegexMatcher{ - EngineType: &matcherpb.RegexMatcher_GoogleRe2{ - GoogleRe2: &matcherpb.RegexMatcher_GoogleRE2{}, - }, - Regex: `(?i).*\.example\.com`, - }, - }, - }, - }, - { - Name: "suffix match", - K: ":authority", - V: "example.*", - Expect: &routepb.HeaderMatcher{ - Name: ":authority", - HeaderMatchSpecifier: &routepb.HeaderMatcher_SafeRegexMatch{ - SafeRegexMatch: &matcherpb.RegexMatcher{ - EngineType: &matcherpb.RegexMatcher_GoogleRe2{ - GoogleRe2: &matcherpb.RegexMatcher_GoogleRE2{}, - }, - Regex: `(?i)example\..*`, - }, - }, - }, - }, - { - Name: "exact match", - K: ":authority", - V: "example.com", - Expect: &routepb.HeaderMatcher{ - Name: ":authority", - HeaderMatchSpecifier: &routepb.HeaderMatcher_SafeRegexMatch{ - SafeRegexMatch: &matcherpb.RegexMatcher{ - EngineType: &matcherpb.RegexMatcher_GoogleRe2{ - GoogleRe2: &matcherpb.RegexMatcher_GoogleRE2{}, - }, - Regex: `(?i)example\.com`, - }, - }, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.Name, func(t *testing.T) { - actual := HostMatcherWithRegex(tc.K, tc.V) - // nolint: staticcheck - // Update to not use the deprecated fields later. - if re := actual.GetSafeRegexMatch().GetRegex(); re != "" { - _, err := regexp.Compile(re) - if err != nil { - t.Errorf("failed to compile regex %s: %v", re, err) - } - } - if !cmp.Equal(tc.Expect, actual, protocmp.Transform()) { - t.Errorf("expecting %v, but got %v", tc.Expect, actual) - } - }) - } -} - -func TestHostMatcher(t *testing.T) { - testCases := []struct { - Name string - K string - V string - Expect *routepb.HeaderMatcher - }{ - { - Name: "present match", - K: ":authority", - V: "*", - Expect: &routepb.HeaderMatcher{ - Name: ":authority", - HeaderMatchSpecifier: &routepb.HeaderMatcher_PresentMatch{PresentMatch: true}, - }, - }, - { - Name: "suffix match", - K: ":authority", - V: "*.example.com", - Expect: &routepb.HeaderMatcher{ - Name: ":authority", - HeaderMatchSpecifier: &routepb.HeaderMatcher_StringMatch{ - StringMatch: &matcherpb.StringMatcher{ - IgnoreCase: true, - MatchPattern: &matcherpb.StringMatcher_Suffix{ - Suffix: ".example.com", - }, - }, - }, - }, - }, - { - Name: "prefix match", - K: ":authority", - V: "example.*", - Expect: &routepb.HeaderMatcher{ - Name: ":authority", - HeaderMatchSpecifier: &routepb.HeaderMatcher_StringMatch{ - StringMatch: &matcherpb.StringMatcher{ - IgnoreCase: true, - MatchPattern: &matcherpb.StringMatcher_Prefix{ - Prefix: "example.", - }, - }, - }, - }, - }, - { - Name: "exact match", - K: ":authority", - V: "example.com", - Expect: &routepb.HeaderMatcher{ - Name: ":authority", - HeaderMatchSpecifier: &routepb.HeaderMatcher_StringMatch{ - StringMatch: &matcherpb.StringMatcher{ - IgnoreCase: true, - MatchPattern: &matcherpb.StringMatcher_Exact{ - Exact: "example.com", - }, - }, - }, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.Name, func(t *testing.T) { - actual := HostMatcher(tc.K, tc.V) - if !cmp.Equal(tc.Expect, actual, protocmp.Transform()) { - t.Errorf("expecting %v, but got %v", tc.Expect, actual) - } - }) - } -} - -func TestPathMatcher(t *testing.T) { - testCases := []struct { - Name string - V string - Expect *matcherpb.PathMatcher - }{ - { - Name: "exact match", - V: "/productpage", - Expect: &matcherpb.PathMatcher{ - Rule: &matcherpb.PathMatcher_Path{ - Path: &matcherpb.StringMatcher{ - MatchPattern: &matcherpb.StringMatcher_Exact{ - Exact: "/productpage", - }, - }, - }, - }, - }, - { - Name: "prefix match", - V: "/prefix*", - Expect: &matcherpb.PathMatcher{ - Rule: &matcherpb.PathMatcher_Path{ - Path: &matcherpb.StringMatcher{ - MatchPattern: &matcherpb.StringMatcher_Prefix{ - Prefix: "/prefix", - }, - }, - }, - }, - }, - { - Name: "suffix match", - V: "*suffix", - Expect: &matcherpb.PathMatcher{ - Rule: &matcherpb.PathMatcher_Path{ - Path: &matcherpb.StringMatcher{ - MatchPattern: &matcherpb.StringMatcher_Suffix{ - Suffix: "suffix", - }, - }, - }, - }, - }, - { - Name: "wildcard match", - V: "*", - Expect: &matcherpb.PathMatcher{ - Rule: &matcherpb.PathMatcher_Path{ - Path: &matcherpb.StringMatcher{ - MatchPattern: &matcherpb.StringMatcher_SafeRegex{ - SafeRegex: &matcherpb.RegexMatcher{ - Regex: ".+", - EngineType: &matcherpb.RegexMatcher_GoogleRe2{ - GoogleRe2: &matcherpb.RegexMatcher_GoogleRE2{}, - }, - }, - }, - }, - }, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.Name, func(t *testing.T) { - actual := PathMatcher(tc.V) - if !cmp.Equal(tc.Expect, actual, protocmp.Transform()) { - t.Errorf("expecting %v, but got %v", tc.Expect, actual) - } - }) - } -} diff --git a/pilot/pkg/security/authz/matcher/leak_test.go b/pilot/pkg/security/authz/matcher/leak_test.go deleted file mode 100644 index 9e2729ca1..000000000 --- a/pilot/pkg/security/authz/matcher/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package matcher - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/security/authz/matcher/metadata.go b/pilot/pkg/security/authz/matcher/metadata.go deleted file mode 100644 index 041b38337..000000000 --- a/pilot/pkg/security/authz/matcher/metadata.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package matcher - -import ( - matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" -) - -// MetadataStringMatcher creates a metadata string matcher for the given filter, key and the -// string matcher. -func MetadataStringMatcher(filter, key string, m *matcherpb.StringMatcher) *matcherpb.MetadataMatcher { - return &matcherpb.MetadataMatcher{ - Filter: filter, - Path: []*matcherpb.MetadataMatcher_PathSegment{ - { - Segment: &matcherpb.MetadataMatcher_PathSegment_Key{ - Key: key, - }, - }, - }, - Value: &matcherpb.ValueMatcher{ - MatchPattern: &matcherpb.ValueMatcher_StringMatch{ - StringMatch: m, - }, - }, - } -} - -// MetadataListMatcher creates a metadata list matcher for the given path keys and value. -func MetadataListMatcher(filter string, keys []string, value *matcherpb.StringMatcher) *matcherpb.MetadataMatcher { - listMatcher := &matcherpb.ListMatcher{ - MatchPattern: &matcherpb.ListMatcher_OneOf{ - OneOf: &matcherpb.ValueMatcher{ - MatchPattern: &matcherpb.ValueMatcher_StringMatch{ - StringMatch: value, - }, - }, - }, - } - - paths := make([]*matcherpb.MetadataMatcher_PathSegment, 0, len(keys)) - for _, k := range keys { - paths = append(paths, &matcherpb.MetadataMatcher_PathSegment{ - Segment: &matcherpb.MetadataMatcher_PathSegment_Key{ - Key: k, - }, - }) - } - - return &matcherpb.MetadataMatcher{ - Filter: filter, - Path: paths, - Value: &matcherpb.ValueMatcher{ - MatchPattern: &matcherpb.ValueMatcher_ListMatch{ - ListMatch: listMatcher, - }, - }, - } -} diff --git a/pilot/pkg/security/authz/matcher/metadata_test.go b/pilot/pkg/security/authz/matcher/metadata_test.go deleted file mode 100644 index 31d6e98bf..000000000 --- a/pilot/pkg/security/authz/matcher/metadata_test.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package matcher - -import ( - "testing" -) - -import ( - matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/testing/protocmp" -) - -func TestMetadataStringMatcher(t *testing.T) { - matcher := &matcherpb.StringMatcher{ - MatchPattern: &matcherpb.StringMatcher_Exact{ - Exact: "exact", - }, - } - actual := MetadataStringMatcher("istio_authn", "key", matcher) - expect := &matcherpb.MetadataMatcher{ - Filter: "istio_authn", - Path: []*matcherpb.MetadataMatcher_PathSegment{ - { - Segment: &matcherpb.MetadataMatcher_PathSegment_Key{ - Key: "key", - }, - }, - }, - Value: &matcherpb.ValueMatcher{ - MatchPattern: &matcherpb.ValueMatcher_StringMatch{ - StringMatch: matcher, - }, - }, - } - - if !cmp.Equal(actual, expect, protocmp.Transform()) { - t.Errorf("want %s, got %s", expect.String(), actual.String()) - } -} - -func TestMetadataListMatcher(t *testing.T) { - getWant := func(regex string) *matcherpb.MetadataMatcher { - return &matcherpb.MetadataMatcher{ - Filter: "istio_authn", - Path: []*matcherpb.MetadataMatcher_PathSegment{ - { - Segment: &matcherpb.MetadataMatcher_PathSegment_Key{ - Key: "key1", - }, - }, - { - Segment: &matcherpb.MetadataMatcher_PathSegment_Key{ - Key: "key2", - }, - }, - }, - Value: &matcherpb.ValueMatcher{ - MatchPattern: &matcherpb.ValueMatcher_ListMatch{ - ListMatch: &matcherpb.ListMatcher{ - MatchPattern: &matcherpb.ListMatcher_OneOf{ - OneOf: &matcherpb.ValueMatcher{ - MatchPattern: &matcherpb.ValueMatcher_StringMatch{ - StringMatch: &matcherpb.StringMatcher{ - MatchPattern: &matcherpb.StringMatcher_SafeRegex{ - SafeRegex: &matcherpb.RegexMatcher{ - EngineType: &matcherpb.RegexMatcher_GoogleRe2{ - GoogleRe2: &matcherpb.RegexMatcher_GoogleRE2{}, - }, - Regex: regex, - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - } - - testCases := []struct { - name string - want string - }{ - { - name: "wildcard", - want: ".+", - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - want := getWant(tc.want) - actual := MetadataListMatcher("istio_authn", []string{"key1", "key2"}, StringMatcher("*")) - if !cmp.Equal(want, actual, protocmp.Transform()) { - t.Errorf("want %s, but got %s", want.String(), actual.String()) - } - }) - } -} diff --git a/pilot/pkg/security/authz/matcher/string.go b/pilot/pkg/security/authz/matcher/string.go deleted file mode 100644 index 5980044de..000000000 --- a/pilot/pkg/security/authz/matcher/string.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package matcher - -import ( - "strings" -) - -import ( - matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" -) - -// StringMatcher creates a string matcher for v. -func StringMatcher(v string) *matcherpb.StringMatcher { - return StringMatcherWithPrefix(v, "") -} - -// StringMatcherRegex creates a regex string matcher for regex. -func StringMatcherRegex(regex string) *matcherpb.StringMatcher { - return &matcherpb.StringMatcher{ - MatchPattern: &matcherpb.StringMatcher_SafeRegex{ - SafeRegex: &matcherpb.RegexMatcher{ - EngineType: &matcherpb.RegexMatcher_GoogleRe2{ - GoogleRe2: &matcherpb.RegexMatcher_GoogleRE2{}, - }, - Regex: regex, - }, - }, - } -} - -// StringMatcherWithPrefix creates a string matcher for v with the extra prefix inserted to the -// created string matcher, note the prefix is ignored if v is wildcard ("*"). -// The wildcard "*" will be generated as ".+" instead of ".*". -func StringMatcherWithPrefix(v, prefix string) *matcherpb.StringMatcher { - switch { - // Check if v is "*" first to make sure we won't generate an empty prefix/suffix StringMatcher, - // the Envoy StringMatcher doesn't allow empty prefix/suffix. - case v == "*": - return StringMatcherRegex(".+") - case strings.HasPrefix(v, "*"): - if prefix == "" { - return &matcherpb.StringMatcher{ - MatchPattern: &matcherpb.StringMatcher_Suffix{ - Suffix: strings.TrimPrefix(v, "*"), - }, - } - } - return StringMatcherRegex(prefix + ".*" + strings.TrimPrefix(v, "*")) - case strings.HasSuffix(v, "*"): - return &matcherpb.StringMatcher{ - MatchPattern: &matcherpb.StringMatcher_Prefix{ - Prefix: prefix + strings.TrimSuffix(v, "*"), - }, - } - default: - return &matcherpb.StringMatcher{ - MatchPattern: &matcherpb.StringMatcher_Exact{ - Exact: prefix + v, - }, - } - } -} diff --git a/pilot/pkg/security/authz/matcher/string_test.go b/pilot/pkg/security/authz/matcher/string_test.go deleted file mode 100644 index e49d39c71..000000000 --- a/pilot/pkg/security/authz/matcher/string_test.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package matcher - -import ( - "testing" -) - -import ( - matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/testing/protocmp" -) - -type testCase struct { - name string - v string - prefix string - want *matcherpb.StringMatcher -} - -func TestStringMatcherWithPrefix(t *testing.T) { - testCases := []testCase{ - { - name: "wildcardAsRequired", - v: "*", - prefix: "abc", - want: StringMatcherRegex(".+"), - }, - { - name: "prefix", - v: "-prefix-*", - prefix: "abc", - want: &matcherpb.StringMatcher{ - MatchPattern: &matcherpb.StringMatcher_Prefix{ - Prefix: "abc-prefix-", - }, - }, - }, - { - name: "suffix-empty-prefix", - v: "*-suffix", - prefix: "", - want: &matcherpb.StringMatcher{ - MatchPattern: &matcherpb.StringMatcher_Suffix{ - Suffix: "-suffix", - }, - }, - }, - { - name: "suffix", - v: "*-suffix", - prefix: "abc", - want: StringMatcherRegex("abc.*-suffix"), - }, - { - name: "exact", - v: "-exact", - prefix: "abc", - want: &matcherpb.StringMatcher{ - MatchPattern: &matcherpb.StringMatcher_Exact{ - Exact: "abc-exact", - }, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - actual := StringMatcherWithPrefix(tc.v, tc.prefix) - if !cmp.Equal(actual, tc.want, protocmp.Transform()) { - t.Errorf("want %s but got %s", tc.want.String(), actual.String()) - } - }) - } -} - -func TestStringMatcherRegex(t *testing.T) { - testCases := []testCase{ - { - name: "wildcardAsRequired", - v: "*", - want: &matcherpb.StringMatcher{ - MatchPattern: &matcherpb.StringMatcher_SafeRegex{ - SafeRegex: &matcherpb.RegexMatcher{ - Regex: "*", - EngineType: &matcherpb.RegexMatcher_GoogleRe2{ - GoogleRe2: &matcherpb.RegexMatcher_GoogleRE2{}, - }, - }, - }, - }, - }, - { - name: "regexExpression", - v: "+?", - want: &matcherpb.StringMatcher{ - MatchPattern: &matcherpb.StringMatcher_SafeRegex{ - SafeRegex: &matcherpb.RegexMatcher{ - Regex: "+?", - EngineType: &matcherpb.RegexMatcher_GoogleRe2{ - GoogleRe2: &matcherpb.RegexMatcher_GoogleRE2{}, - }, - }, - }, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - if actual := StringMatcherRegex(tc.v); !cmp.Equal(actual, tc.want, protocmp.Transform()) { - t.Errorf("want %s but got %s", tc.want.String(), actual.String()) - } - }) - } -} diff --git a/pilot/pkg/security/authz/model/generator.go b/pilot/pkg/security/authz/model/generator.go deleted file mode 100644 index 07a410327..000000000 --- a/pilot/pkg/security/authz/model/generator.go +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "fmt" - "strings" -) - -import ( - rbacpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/authz/matcher" - sm "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/model" - "github.com/apache/dubbo-go-pixiu/pkg/spiffe" -) - -type generator interface { - permission(key, value string, forTCP bool) (*rbacpb.Permission, error) - principal(key, value string, forTCP bool) (*rbacpb.Principal, error) -} - -type destIPGenerator struct{} - -func (destIPGenerator) permission(_, value string, _ bool) (*rbacpb.Permission, error) { - cidrRange, err := matcher.CidrRange(value) - if err != nil { - return nil, err - } - return permissionDestinationIP(cidrRange), nil -} - -func (destIPGenerator) principal(_, _ string, _ bool) (*rbacpb.Principal, error) { - return nil, fmt.Errorf("unimplemented") -} - -type destPortGenerator struct{} - -func (destPortGenerator) permission(_, value string, _ bool) (*rbacpb.Permission, error) { - portValue, err := convertToPort(value) - if err != nil { - return nil, err - } - return permissionDestinationPort(portValue), nil -} - -func (destPortGenerator) principal(_, _ string, _ bool) (*rbacpb.Principal, error) { - return nil, fmt.Errorf("unimplemented") -} - -type connSNIGenerator struct{} - -func (connSNIGenerator) permission(_, value string, _ bool) (*rbacpb.Permission, error) { - m := matcher.StringMatcher(value) - return permissionRequestedServerName(m), nil -} - -func (connSNIGenerator) principal(_, _ string, _ bool) (*rbacpb.Principal, error) { - return nil, fmt.Errorf("unimplemented") -} - -type envoyFilterGenerator struct{} - -func (envoyFilterGenerator) permission(key, value string, _ bool) (*rbacpb.Permission, error) { - // Split key of format "experimental.envoy.filters.a.b[c]" to "envoy.filters.a.b" and "c". - parts := strings.SplitN(strings.TrimSuffix(strings.TrimPrefix(key, "experimental."), "]"), "[", 2) - - if len(parts) != 2 { - return nil, fmt.Errorf("invalid key: %v", key) - } - - // If value is of format [v], create a list matcher. - // Else, if value is of format v, create a string matcher. - if strings.HasPrefix(value, "[") && strings.HasSuffix(value, "]") { - m := matcher.MetadataListMatcher(parts[0], parts[1:], matcher.StringMatcher(strings.Trim(value, "[]"))) - return permissionMetadata(m), nil - } - m := matcher.MetadataStringMatcher(parts[0], parts[1], matcher.StringMatcher(value)) - return permissionMetadata(m), nil -} - -func (envoyFilterGenerator) principal(_, _ string, _ bool) (*rbacpb.Principal, error) { - return nil, fmt.Errorf("unimplemented") -} - -type srcIPGenerator struct{} - -func (srcIPGenerator) permission(_, _ string, _ bool) (*rbacpb.Permission, error) { - return nil, fmt.Errorf("unimplemented") -} - -func (srcIPGenerator) principal(_, value string, _ bool) (*rbacpb.Principal, error) { - cidr, err := matcher.CidrRange(value) - if err != nil { - return nil, err - } - return principalDirectRemoteIP(cidr), nil -} - -type remoteIPGenerator struct{} - -func (remoteIPGenerator) permission(_, _ string, _ bool) (*rbacpb.Permission, error) { - return nil, fmt.Errorf("unimplemented") -} - -func (remoteIPGenerator) principal(_, value string, _ bool) (*rbacpb.Principal, error) { - cidr, err := matcher.CidrRange(value) - if err != nil { - return nil, err - } - return principalRemoteIP(cidr), nil -} - -type srcNamespaceGenerator struct{} - -func (srcNamespaceGenerator) permission(_, _ string, _ bool) (*rbacpb.Permission, error) { - return nil, fmt.Errorf("unimplemented") -} - -func (srcNamespaceGenerator) principal(_, value string, _ bool) (*rbacpb.Principal, error) { - v := strings.Replace(value, "*", ".*", -1) - m := matcher.StringMatcherRegex(fmt.Sprintf(".*/ns/%s/.*", v)) - return principalAuthenticated(m), nil -} - -type srcPrincipalGenerator struct{} - -func (srcPrincipalGenerator) permission(_, _ string, _ bool) (*rbacpb.Permission, error) { - return nil, fmt.Errorf("unimplemented") -} - -func (srcPrincipalGenerator) principal(key, value string, _ bool) (*rbacpb.Principal, error) { - m := matcher.StringMatcherWithPrefix(value, spiffe.URIPrefix) - return principalAuthenticated(m), nil -} - -type requestPrincipalGenerator struct{} - -func (requestPrincipalGenerator) permission(_, _ string, _ bool) (*rbacpb.Permission, error) { - return nil, fmt.Errorf("unimplemented") -} - -func (requestPrincipalGenerator) principal(key, value string, forTCP bool) (*rbacpb.Principal, error) { - if forTCP { - return nil, fmt.Errorf("%q is HTTP only", key) - } - - m := matcher.MetadataStringMatcher(sm.AuthnFilterName, key, matcher.StringMatcher(value)) - return principalMetadata(m), nil -} - -type requestAudiencesGenerator struct{} - -func (requestAudiencesGenerator) permission(key, value string, forTCP bool) (*rbacpb.Permission, error) { - return requestPrincipalGenerator{}.permission(key, value, forTCP) -} - -func (requestAudiencesGenerator) principal(key, value string, forTCP bool) (*rbacpb.Principal, error) { - return requestPrincipalGenerator{}.principal(key, value, forTCP) -} - -type requestPresenterGenerator struct{} - -func (requestPresenterGenerator) permission(key, value string, forTCP bool) (*rbacpb.Permission, error) { - return requestPrincipalGenerator{}.permission(key, value, forTCP) -} - -func (requestPresenterGenerator) principal(key, value string, forTCP bool) (*rbacpb.Principal, error) { - return requestPrincipalGenerator{}.principal(key, value, forTCP) -} - -type requestHeaderGenerator struct{} - -func (requestHeaderGenerator) permission(_, _ string, _ bool) (*rbacpb.Permission, error) { - return nil, fmt.Errorf("unimplemented") -} - -func (requestHeaderGenerator) principal(key, value string, forTCP bool) (*rbacpb.Principal, error) { - if forTCP { - return nil, fmt.Errorf("%q is HTTP only", key) - } - - header, err := extractNameInBrackets(strings.TrimPrefix(key, attrRequestHeader)) - if err != nil { - return nil, err - } - m := matcher.HeaderMatcher(header, value) - return principalHeader(m), nil -} - -type requestClaimGenerator struct{} - -func (requestClaimGenerator) permission(_, _ string, _ bool) (*rbacpb.Permission, error) { - return nil, fmt.Errorf("unimplemented") -} - -func (requestClaimGenerator) principal(key, value string, forTCP bool) (*rbacpb.Principal, error) { - if forTCP { - return nil, fmt.Errorf("%q is HTTP only", key) - } - - claims, err := extractNameInNestedBrackets(strings.TrimPrefix(key, attrRequestClaims)) - if err != nil { - return nil, err - } - // Generate a metadata list matcher for the given path keys and value. - // On proxy side, the value should be of list type. - m := MetadataMatcherForJWTClaims(claims, matcher.StringMatcher(value)) - return principalMetadata(m), nil -} - -type hostGenerator struct{} - -func (hg hostGenerator) permission(key, value string, forTCP bool) (*rbacpb.Permission, error) { - if forTCP { - return nil, fmt.Errorf("%q is HTTP only", key) - } - - return permissionHeader(matcher.HostMatcher(hostHeader, value)), nil -} - -func (hostGenerator) principal(key, value string, forTCP bool) (*rbacpb.Principal, error) { - return nil, fmt.Errorf("unimplemented") -} - -type pathGenerator struct{} - -func (g pathGenerator) permission(key, value string, forTCP bool) (*rbacpb.Permission, error) { - if forTCP { - return nil, fmt.Errorf("%q is HTTP only", key) - } - - m := matcher.PathMatcher(value) - return permissionPath(m), nil -} - -func (pathGenerator) principal(key, value string, forTCP bool) (*rbacpb.Principal, error) { - return nil, fmt.Errorf("unimplemented") -} - -type methodGenerator struct{} - -func (methodGenerator) permission(key, value string, forTCP bool) (*rbacpb.Permission, error) { - if forTCP { - return nil, fmt.Errorf("%q is HTTP only", key) - } - - m := matcher.HeaderMatcher(methodHeader, value) - return permissionHeader(m), nil -} - -func (methodGenerator) principal(key, value string, forTCP bool) (*rbacpb.Principal, error) { - return nil, fmt.Errorf("unimplemented") -} diff --git a/pilot/pkg/security/authz/model/generator_test.go b/pilot/pkg/security/authz/model/generator_test.go deleted file mode 100644 index 778a1f2a2..000000000 --- a/pilot/pkg/security/authz/model/generator_test.go +++ /dev/null @@ -1,335 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "testing" -) - -import ( - rbacpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/testing/protocmp" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -func TestGenerator(t *testing.T) { - cases := []struct { - name string - g generator - key string - value string - forTCP bool - want interface{} - }{ - { - name: "destIPGenerator", - g: destIPGenerator{}, - value: "1.2.3.4", - want: yamlPermission(t, ` - destinationIp: - addressPrefix: 1.2.3.4 - prefixLen: 32`), - }, - { - name: "destPortGenerator", - g: destPortGenerator{}, - value: "80", - want: yamlPermission(t, ` - destinationPort: 80`), - }, - { - name: "connSNIGenerator", - g: connSNIGenerator{}, - value: "exact.com", - want: yamlPermission(t, ` - requestedServerName: - exact: exact.com`), - }, - { - name: "envoyFilterGenerator-string", - g: envoyFilterGenerator{}, - key: "experimental.a.b.c[d]", - value: "val", - want: yamlPermission(t, ` - metadata: - filter: a.b.c - path: - - key: d - value: - stringMatch: - exact: val`), - }, - { - name: "envoyFilterGenerator-invalid", - g: envoyFilterGenerator{}, - key: "experimental.a.b.c]", - value: "val", - }, - { - name: "envoyFilterGenerator-list", - g: envoyFilterGenerator{}, - key: "experimental.a.b.c[d]", - value: "[v1, v2]", - want: yamlPermission(t, ` - metadata: - filter: a.b.c - path: - - key: d - value: - listMatch: - oneOf: - stringMatch: - exact: v1, v2`), - }, - { - name: "srcIPGenerator", - g: srcIPGenerator{}, - value: "1.2.3.4", - want: yamlPrincipal(t, ` - directRemoteIp: - addressPrefix: 1.2.3.4 - prefixLen: 32`), - }, - { - name: "remoteIPGenerator", - g: remoteIPGenerator{}, - value: "1.2.3.4", - want: yamlPrincipal(t, ` - remoteIp: - addressPrefix: 1.2.3.4 - prefixLen: 32`), - }, - { - name: "srcNamespaceGenerator-http", - g: srcNamespaceGenerator{}, - value: "foo", - want: yamlPrincipal(t, ` - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/foo/.*`), - }, - { - name: "srcNamespaceGenerator-tcp", - g: srcNamespaceGenerator{}, - value: "foo", - forTCP: true, - want: yamlPrincipal(t, ` - authenticated: - principalName: - safeRegex: - googleRe2: {} - regex: .*/ns/foo/.*`), - }, - { - name: "srcPrincipalGenerator-http", - g: srcPrincipalGenerator{}, - key: "source.principal", - value: "foo", - want: yamlPrincipal(t, ` - authenticated: - principalName: - exact: spiffe://foo`), - }, - { - name: "srcPrincipalGenerator-tcp", - g: srcPrincipalGenerator{}, - key: "source.principal", - value: "foo", - forTCP: true, - want: yamlPrincipal(t, ` - authenticated: - principalName: - exact: spiffe://foo`), - }, - { - name: "requestPrincipalGenerator", - g: requestPrincipalGenerator{}, - key: "request.auth.principal", - value: "foo", - want: yamlPrincipal(t, ` - metadata: - filter: istio_authn - path: - - key: request.auth.principal - value: - stringMatch: - exact: foo`), - }, - { - name: "requestAudiencesGenerator", - g: requestAudiencesGenerator{}, - key: "request.auth.audiences", - value: "foo", - want: yamlPrincipal(t, ` - metadata: - filter: istio_authn - path: - - key: request.auth.audiences - value: - stringMatch: - exact: foo`), - }, - { - name: "requestPresenterGenerator", - g: requestPresenterGenerator{}, - key: "request.auth.presenter", - value: "foo", - want: yamlPrincipal(t, ` - metadata: - filter: istio_authn - path: - - key: request.auth.presenter - value: - stringMatch: - exact: foo`), - }, - { - name: "requestHeaderGenerator", - g: requestHeaderGenerator{}, - key: "request.headers[x-foo]", - value: "foo", - want: yamlPrincipal(t, ` - header: - exactMatch: foo - name: x-foo`), - }, - { - name: "requestClaimGenerator", - g: requestClaimGenerator{}, - key: "request.auth.claims[bar]", - value: "foo", - want: yamlPrincipal(t, ` - metadata: - filter: istio_authn - path: - - key: request.auth.claims - - key: bar - value: - listMatch: - oneOf: - stringMatch: - exact: foo`), - }, - { - name: "requestNestedClaimsGenerator", - g: requestClaimGenerator{}, - key: "request.auth.claims[bar][baz]", - value: "foo", - want: yamlPrincipal(t, ` - metadata: - filter: istio_authn - path: - - key: request.auth.claims - - key: bar - - key: baz - value: - listMatch: - oneOf: - stringMatch: - exact: foo`), - }, - { - name: "hostGenerator", - g: hostGenerator{}, - value: "foo", - want: yamlPermission(t, ` - header: - stringMatch: - exact: foo - ignoreCase: true - name: :authority`), - }, - { - name: "pathGenerator", - g: pathGenerator{}, - value: "/abc", - want: yamlPermission(t, ` - urlPath: - path: - exact: /abc`), - }, - { - name: "methodGenerator", - g: methodGenerator{}, - value: "GET", - want: yamlPermission(t, ` - header: - exactMatch: GET - name: :method`), - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - var got interface{} - var err error - // nolint: gocritic - if _, ok := tc.want.(*rbacpb.Permission); ok { - got, err = tc.g.permission(tc.key, tc.value, tc.forTCP) - if err != nil { - t.Errorf("both permission and principal returned error") - } - } else if _, ok := tc.want.(*rbacpb.Principal); ok { - got, err = tc.g.principal(tc.key, tc.value, tc.forTCP) - if err != nil { - t.Errorf("both permission and principal returned error") - } - } else { - _, err1 := tc.g.principal(tc.key, tc.value, tc.forTCP) - _, err2 := tc.g.permission(tc.key, tc.value, tc.forTCP) - if err1 == nil || err2 == nil { - t.Fatalf("wanted error") - } - return - } - if diff := cmp.Diff(got, tc.want, protocmp.Transform()); diff != "" { - var gotYaml string - gotProto, ok := got.(proto.Message) - if !ok { - t.Fatal("failed to extract proto") - } - if gotYaml, err = protomarshal.ToYAML(gotProto); err != nil { - t.Fatalf("%s: failed to parse yaml: %s", tc.name, err) - } - t.Errorf("got:\n %v\n but want:\n %v", gotYaml, tc.want) - } - }) - } -} - -func yamlPermission(t *testing.T, yaml string) *rbacpb.Permission { - t.Helper() - p := &rbacpb.Permission{} - if err := protomarshal.ApplyYAML(yaml, p); err != nil { - t.Fatalf("failed to parse yaml: %s", err) - } - return p -} - -func yamlPrincipal(t *testing.T, yaml string) *rbacpb.Principal { - t.Helper() - p := &rbacpb.Principal{} - if err := protomarshal.ApplyYAML(yaml, p); err != nil { - t.Fatalf("failed to parse yaml: %s", err) - } - return p -} diff --git a/pilot/pkg/security/authz/model/leak_test.go b/pilot/pkg/security/authz/model/leak_test.go deleted file mode 100644 index 1665150e5..000000000 --- a/pilot/pkg/security/authz/model/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/security/authz/model/model.go b/pilot/pkg/security/authz/model/model.go deleted file mode 100644 index 088d1205d..000000000 --- a/pilot/pkg/security/authz/model/model.go +++ /dev/null @@ -1,337 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "fmt" - "strings" -) - -import ( - rbacpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3" - authzpb "istio.io/api/security/v1beta1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/trustdomain" -) - -const ( - RBACTCPFilterStatPrefix = "tcp." - RBACShadowEngineResult = "shadow_engine_result" - RBACShadowEffectivePolicyID = "shadow_effective_policy_id" - RBACShadowRulesAllowStatPrefix = "istio_dry_run_allow_" - RBACShadowRulesDenyStatPrefix = "istio_dry_run_deny_" - RBACExtAuthzShadowRulesStatPrefix = "istio_ext_authz_" - - attrRequestHeader = "request.headers" // header name is surrounded by brackets, e.g. "request.headers[User-Agent]". - attrSrcIP = "source.ip" // supports both single ip and cidr, e.g. "10.1.2.3" or "10.1.0.0/16". - attrRemoteIP = "remote.ip" // original client ip determined from x-forwarded-for or proxy protocol. - attrSrcNamespace = "source.namespace" // e.g. "default". - attrSrcPrincipal = "source.principal" // source identity, e,g, "cluster.local/ns/default/sa/productpage". - attrRequestPrincipal = "request.auth.principal" // authenticated principal of the request. - attrRequestAudiences = "request.auth.audiences" // intended audience(s) for this authentication information. - attrRequestPresenter = "request.auth.presenter" // authorized presenter of the credential. - attrRequestClaims = "request.auth.claims" // claim name is surrounded by brackets, e.g. "request.auth.claims[iss]". - attrDestIP = "destination.ip" // supports both single ip and cidr, e.g. "10.1.2.3" or "10.1.0.0/16". - attrDestPort = "destination.port" // must be in the range [0, 65535]. - attrConnSNI = "connection.sni" // server name indication, e.g. "www.example.com". - attrEnvoyFilter = "experimental.envoy.filters." // an experimental attribute for checking Envoy Metadata directly. - - // Internal names used to generate corresponding Envoy matcher. - methodHeader = ":method" - pathMatcher = "path-matcher" - hostHeader = ":authority" -) - -type rule struct { - key string - values []string - notValues []string - g generator -} - -type ruleList struct { - rules []*rule -} - -// Model represents a single rule from an authorization policy. The conditions of the rule are consolidated into -// permission or principal to align with the Envoy RBAC filter API. -type Model struct { - permissions []ruleList - principals []ruleList -} - -// New returns a model representing a single authorization policy. -func New(r *authzpb.Rule) (*Model, error) { - m := Model{} - - basePermission := ruleList{} - basePrincipal := ruleList{} - - // Each condition in the when needs to be consolidated into either permission or principal. - for _, when := range r.When { - k := when.Key - switch { - case k == attrDestIP: - basePermission.appendLast(destIPGenerator{}, k, when.Values, when.NotValues) - case k == attrDestPort: - basePermission.appendLast(destPortGenerator{}, k, when.Values, when.NotValues) - case k == attrConnSNI: - basePermission.appendLast(connSNIGenerator{}, k, when.Values, when.NotValues) - case strings.HasPrefix(k, attrEnvoyFilter): - basePermission.appendLast(envoyFilterGenerator{}, k, when.Values, when.NotValues) - case k == attrSrcIP: - basePrincipal.appendLast(srcIPGenerator{}, k, when.Values, when.NotValues) - case k == attrRemoteIP: - basePrincipal.appendLast(remoteIPGenerator{}, k, when.Values, when.NotValues) - case k == attrSrcNamespace: - basePrincipal.appendLast(srcNamespaceGenerator{}, k, when.Values, when.NotValues) - case k == attrSrcPrincipal: - basePrincipal.appendLast(srcPrincipalGenerator{}, k, when.Values, when.NotValues) - case k == attrRequestPrincipal: - basePrincipal.appendLast(requestPrincipalGenerator{}, k, when.Values, when.NotValues) - case k == attrRequestAudiences: - basePrincipal.appendLast(requestAudiencesGenerator{}, k, when.Values, when.NotValues) - case k == attrRequestPresenter: - basePrincipal.appendLast(requestPresenterGenerator{}, k, when.Values, when.NotValues) - case strings.HasPrefix(k, attrRequestHeader): - basePrincipal.appendLast(requestHeaderGenerator{}, k, when.Values, when.NotValues) - case strings.HasPrefix(k, attrRequestClaims): - basePrincipal.appendLast(requestClaimGenerator{}, k, when.Values, when.NotValues) - default: - return nil, fmt.Errorf("unknown attribute %s", when.Key) - } - } - - for _, from := range r.From { - merged := basePrincipal.copy() - if s := from.Source; s != nil { - merged.insertFront(srcIPGenerator{}, attrSrcIP, s.IpBlocks, s.NotIpBlocks) - merged.insertFront(remoteIPGenerator{}, attrRemoteIP, s.RemoteIpBlocks, s.NotRemoteIpBlocks) - merged.insertFront(srcNamespaceGenerator{}, attrSrcNamespace, s.Namespaces, s.NotNamespaces) - merged.insertFront(requestPrincipalGenerator{}, attrRequestPrincipal, s.RequestPrincipals, s.NotRequestPrincipals) - merged.insertFront(srcPrincipalGenerator{}, attrSrcPrincipal, s.Principals, s.NotPrincipals) - } - m.principals = append(m.principals, merged) - } - if len(r.From) == 0 { - m.principals = append(m.principals, basePrincipal) - } - - for _, to := range r.To { - merged := basePermission.copy() - if o := to.Operation; o != nil { - merged.insertFront(destPortGenerator{}, attrDestPort, o.Ports, o.NotPorts) - merged.insertFront(pathGenerator{}, pathMatcher, o.Paths, o.NotPaths) - merged.insertFront(methodGenerator{}, methodHeader, o.Methods, o.NotMethods) - merged.insertFront(hostGenerator{}, hostHeader, o.Hosts, o.NotHosts) - } - m.permissions = append(m.permissions, merged) - } - if len(r.To) == 0 { - m.permissions = append(m.permissions, basePermission) - } - - return &m, nil -} - -// MigrateTrustDomain replaces the trust domain in source principal based on the trust domain aliases information. -func (m *Model) MigrateTrustDomain(tdBundle trustdomain.Bundle) { - for _, p := range m.principals { - for _, r := range p.rules { - if r.key == attrSrcPrincipal { - if len(r.values) != 0 { - r.values = tdBundle.ReplaceTrustDomainAliases(r.values) - } - if len(r.notValues) != 0 { - r.notValues = tdBundle.ReplaceTrustDomainAliases(r.notValues) - } - } - } - } -} - -// Generate generates the Envoy RBAC config from the model. -func (m Model) Generate(forTCP bool, action rbacpb.RBAC_Action) (*rbacpb.Policy, error) { - var permissions []*rbacpb.Permission - for _, rl := range m.permissions { - permission, err := generatePermission(rl, forTCP, action) - if err != nil { - return nil, err - } - permissions = append(permissions, permission) - } - if len(permissions) == 0 { - return nil, fmt.Errorf("must have at least 1 permission") - } - - var principals []*rbacpb.Principal - for _, rl := range m.principals { - principal, err := generatePrincipal(rl, forTCP, action) - if err != nil { - return nil, err - } - principals = append(principals, principal) - } - if len(principals) == 0 { - return nil, fmt.Errorf("must have at least 1 principal") - } - - return &rbacpb.Policy{ - Permissions: permissions, - Principals: principals, - }, nil -} - -func generatePermission(rl ruleList, forTCP bool, action rbacpb.RBAC_Action) (*rbacpb.Permission, error) { - var and []*rbacpb.Permission - for _, r := range rl.rules { - ret, err := r.permission(forTCP, action) - if err != nil { - return nil, err - } - and = append(and, ret...) - } - if len(and) == 0 { - and = append(and, permissionAny()) - } - return permissionAnd(and), nil -} - -func generatePrincipal(rl ruleList, forTCP bool, action rbacpb.RBAC_Action) (*rbacpb.Principal, error) { - var and []*rbacpb.Principal - for _, r := range rl.rules { - ret, err := r.principal(forTCP, action) - if err != nil { - return nil, err - } - and = append(and, ret...) - } - if len(and) == 0 { - and = append(and, principalAny()) - } - return principalAnd(and), nil -} - -func (r rule) permission(forTCP bool, action rbacpb.RBAC_Action) ([]*rbacpb.Permission, error) { - var permissions []*rbacpb.Permission - var or []*rbacpb.Permission - for _, value := range r.values { - p, err := r.g.permission(r.key, value, forTCP) - if err := r.checkError(action, err); err != nil { - return nil, err - } - if p != nil { - or = append(or, p) - } - } - if len(or) > 0 { - permissions = append(permissions, permissionOr(or)) - } - - or = nil - for _, notValue := range r.notValues { - p, err := r.g.permission(r.key, notValue, forTCP) - if err := r.checkError(action, err); err != nil { - return nil, err - } - if p != nil { - or = append(or, p) - } - } - if len(or) > 0 { - permissions = append(permissions, permissionNot(permissionOr(or))) - } - return permissions, nil -} - -func (r rule) principal(forTCP bool, action rbacpb.RBAC_Action) ([]*rbacpb.Principal, error) { - var principals []*rbacpb.Principal - var or []*rbacpb.Principal - for _, value := range r.values { - p, err := r.g.principal(r.key, value, forTCP) - if err := r.checkError(action, err); err != nil { - return nil, err - } - if p != nil { - or = append(or, p) - } - } - if len(or) > 0 { - principals = append(principals, principalOr(or)) - } - - or = nil - for _, notValue := range r.notValues { - p, err := r.g.principal(r.key, notValue, forTCP) - if err := r.checkError(action, err); err != nil { - return nil, err - } - if p != nil { - or = append(or, p) - } - } - if len(or) > 0 { - principals = append(principals, principalNot(principalOr(or))) - } - return principals, nil -} - -func (r rule) checkError(action rbacpb.RBAC_Action, err error) error { - if action == rbacpb.RBAC_ALLOW { - // Return the error as-is for allow policy. This will make all rules in the current permission ignored, effectively - // result in a smaller allow policy (i.e. less likely to allow a request). - return err - } - - // Ignore the error for a deny or audit policy. This will make the current rule ignored and continue the generation of - // the next rule, effectively resulting in a wider deny or audit policy (i.e. more likely to deny or audit a request). - return nil -} - -func (p *ruleList) copy() ruleList { - r := ruleList{} - r.rules = append([]*rule{}, p.rules...) - return r -} - -func (p *ruleList) insertFront(g generator, key string, values, notValues []string) { - if len(values) == 0 && len(notValues) == 0 { - return - } - r := &rule{ - key: key, - values: values, - notValues: notValues, - g: g, - } - - p.rules = append([]*rule{r}, p.rules...) -} - -func (p *ruleList) appendLast(g generator, key string, values, notValues []string) { - if len(values) == 0 && len(notValues) == 0 { - return - } - r := &rule{ - key: key, - values: values, - notValues: notValues, - g: g, - } - - p.rules = append(p.rules, r) -} diff --git a/pilot/pkg/security/authz/model/model_test.go b/pilot/pkg/security/authz/model/model_test.go deleted file mode 100644 index 3693a6e51..000000000 --- a/pilot/pkg/security/authz/model/model_test.go +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "strings" - "testing" -) - -import ( - "github.com/davecgh/go-spew/spew" - rbacpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3" - authzpb "istio.io/api/security/v1beta1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/trustdomain" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -func TestModel_MigrateTrustDomain(t *testing.T) { - cases := []struct { - name string - tdBundle trustdomain.Bundle - rule *authzpb.Rule - want []string - notWant []string - }{ - { - name: "no-source-principal", - tdBundle: trustdomain.NewBundle("td-1", []string{"td-2"}), - rule: yamlRule(t, ` -from: -- source: - requestPrincipals: ["td-1/ns/foo/sa/sleep"] -`), - want: []string{ - "td-1/ns/foo/sa/sleep", - }, - notWant: []string{ - "td-2/ns/foo/sa/sleep", - }, - }, - { - name: "source-principal-field", - tdBundle: trustdomain.NewBundle("td-1", []string{"td-2"}), - rule: yamlRule(t, ` -from: -- source: - principals: ["td-1/ns/foo/sa/sleep"] -`), - want: []string{ - "td-1/ns/foo/sa/sleep", - "td-2/ns/foo/sa/sleep", - }, - }, - { - name: "source-principal-attribute", - tdBundle: trustdomain.NewBundle("td-1", []string{"td-2"}), - rule: yamlRule(t, ` -when: -- key: source.principal - values: ["td-1/ns/foo/sa/sleep"] -`), - want: []string{ - "td-1/ns/foo/sa/sleep", - "td-2/ns/foo/sa/sleep", - }, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - got, err := New(tc.rule) - if err != nil { - t.Fatal(err) - } - got.MigrateTrustDomain(tc.tdBundle) - gotStr := spew.Sdump(got) - for _, want := range tc.want { - if !strings.Contains(gotStr, want) { - t.Errorf("got %s but not found %s", gotStr, want) - } - } - for _, notWant := range tc.notWant { - if strings.Contains(gotStr, notWant) { - t.Errorf("got %s but not want %s", gotStr, notWant) - } - } - }) - } -} - -func TestModel_Generate(t *testing.T) { - rule := yamlRule(t, ` -from: -- source: - requestPrincipals: ["td-1/ns/foo/sa/sleep-1"] - notRequestPrincipals: ["td-1/ns/foo/sa/sleep-2"] -- source: - requestPrincipals: ["td-1/ns/foo/sa/sleep-3"] - notRequestPrincipals: ["td-1/ns/foo/sa/sleep-4"] -to: -- operation: - ports: ["8001"] - notPorts: ["8002"] -- operation: - ports: ["8003"] - notPorts: ["8004"] -when: -- key: "source.principal" - values: ["td-1/ns/foo/sa/httpbin-1"] - notValues: ["td-1/ns/foo/sa/httpbin-2"] -- key: "destination.ip" - values: ["10.0.0.1"] - notValues: ["10.0.0.2"] -`) - - cases := []struct { - name string - forTCP bool - action rbacpb.RBAC_Action - rule *authzpb.Rule - want []string - notWant []string - }{ - { - name: "allow-http", - action: rbacpb.RBAC_ALLOW, - rule: rule, - want: []string{ - "td-1/ns/foo/sa/sleep-1", - "td-1/ns/foo/sa/sleep-2", - "td-1/ns/foo/sa/sleep-3", - "td-1/ns/foo/sa/sleep-4", - "td-1/ns/foo/sa/httpbin-1", - "td-1/ns/foo/sa/httpbin-2", - "8001", - "8002", - "8003", - "8004", - "10.0.0.1", - "10.0.0.2", - }, - }, - { - name: "allow-tcp", - action: rbacpb.RBAC_ALLOW, - forTCP: true, - rule: rule, - notWant: []string{ - "td-1/ns/foo/sa/sleep-1", - "td-1/ns/foo/sa/sleep-2", - "td-1/ns/foo/sa/sleep-3", - "td-1/ns/foo/sa/sleep-4", - "td-1/ns/foo/sa/httpbin-1", - "td-1/ns/foo/sa/httpbin-2", - "8001", - "8002", - "8003", - "8004", - "10.0.0.1", - "10.0.0.2", - }, - }, - { - name: "deny-http", - action: rbacpb.RBAC_DENY, - rule: rule, - want: []string{ - "td-1/ns/foo/sa/sleep-1", - "td-1/ns/foo/sa/sleep-2", - "td-1/ns/foo/sa/sleep-3", - "td-1/ns/foo/sa/sleep-4", - "td-1/ns/foo/sa/httpbin-1", - "td-1/ns/foo/sa/httpbin-2", - "8001", - "8002", - "8003", - "8004", - "10.0.0.1", - "10.0.0.2", - }, - }, - { - name: "deny-tcp", - action: rbacpb.RBAC_DENY, - forTCP: true, - rule: rule, - want: []string{ - "8001", - "8002", - "8003", - "8004", - "10.0.0.1", - "10.0.0.2", - "td-1/ns/foo/sa/httpbin-1", - "td-1/ns/foo/sa/httpbin-2", - }, - notWant: []string{ - "td-1/ns/foo/sa/sleep-1", - "td-1/ns/foo/sa/sleep-2", - "td-1/ns/foo/sa/sleep-3", - "td-1/ns/foo/sa/sleep-4", - }, - }, - { - name: "audit-http", - action: rbacpb.RBAC_LOG, - rule: rule, - want: []string{ - "td-1/ns/foo/sa/sleep-1", - "td-1/ns/foo/sa/sleep-2", - "td-1/ns/foo/sa/sleep-3", - "td-1/ns/foo/sa/sleep-4", - "td-1/ns/foo/sa/httpbin-1", - "td-1/ns/foo/sa/httpbin-2", - "8001", - "8002", - "8003", - "8004", - "10.0.0.1", - "10.0.0.2", - }, - }, - { - name: "audit-tcp", - action: rbacpb.RBAC_LOG, - forTCP: true, - rule: rule, - want: []string{ - "8001", - "8002", - "8003", - "8004", - "10.0.0.1", - "10.0.0.2", - "td-1/ns/foo/sa/httpbin-1", - "td-1/ns/foo/sa/httpbin-2", - }, - notWant: []string{ - "td-1/ns/foo/sa/sleep-1", - "td-1/ns/foo/sa/sleep-2", - "td-1/ns/foo/sa/sleep-3", - "td-1/ns/foo/sa/sleep-4", - }, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - m, err := New(tc.rule) - if err != nil { - t.Fatal(err) - } - p, _ := m.Generate(tc.forTCP, tc.action) - var gotYaml string - if p != nil { - if gotYaml, err = protomarshal.ToYAML(p); err != nil { - t.Fatalf("%s: failed to parse yaml: %s", tc.name, err) - } - } - - for _, want := range tc.want { - if !strings.Contains(gotYaml, want) { - t.Errorf("got:\n%s but not found %s", gotYaml, want) - } - } - for _, notWant := range tc.notWant { - if strings.Contains(gotYaml, notWant) { - t.Errorf("got:\n%s but not want %s", gotYaml, notWant) - } - } - }) - } -} - -func yamlRule(t *testing.T, yaml string) *authzpb.Rule { - t.Helper() - p := &authzpb.Rule{} - if err := protomarshal.ApplyYAML(yaml, p); err != nil { - t.Fatalf("failed to parse yaml: %s", err) - } - return p -} diff --git a/pilot/pkg/security/authz/model/permission.go b/pilot/pkg/security/authz/model/permission.go deleted file mode 100644 index 6809638bc..000000000 --- a/pilot/pkg/security/authz/model/permission.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - rbacpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3" - routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" -) - -func permissionAny() *rbacpb.Permission { - return &rbacpb.Permission{ - Rule: &rbacpb.Permission_Any{ - Any: true, - }, - } -} - -func permissionAnd(permission []*rbacpb.Permission) *rbacpb.Permission { - return &rbacpb.Permission{ - Rule: &rbacpb.Permission_AndRules{ - AndRules: &rbacpb.Permission_Set{ - Rules: permission, - }, - }, - } -} - -func permissionOr(permission []*rbacpb.Permission) *rbacpb.Permission { - return &rbacpb.Permission{ - Rule: &rbacpb.Permission_OrRules{ - OrRules: &rbacpb.Permission_Set{ - Rules: permission, - }, - }, - } -} - -func permissionNot(permission *rbacpb.Permission) *rbacpb.Permission { - return &rbacpb.Permission{ - Rule: &rbacpb.Permission_NotRule{ - NotRule: permission, - }, - } -} - -func permissionDestinationIP(cidr *core.CidrRange) *rbacpb.Permission { - return &rbacpb.Permission{ - Rule: &rbacpb.Permission_DestinationIp{ - DestinationIp: cidr, - }, - } -} - -func permissionDestinationPort(port uint32) *rbacpb.Permission { - return &rbacpb.Permission{ - Rule: &rbacpb.Permission_DestinationPort{ - DestinationPort: port, - }, - } -} - -func permissionRequestedServerName(name *matcherpb.StringMatcher) *rbacpb.Permission { - return &rbacpb.Permission{ - Rule: &rbacpb.Permission_RequestedServerName{ - RequestedServerName: name, - }, - } -} - -func permissionMetadata(metadata *matcherpb.MetadataMatcher) *rbacpb.Permission { - return &rbacpb.Permission{ - Rule: &rbacpb.Permission_Metadata{ - Metadata: metadata, - }, - } -} - -func permissionHeader(header *routepb.HeaderMatcher) *rbacpb.Permission { - return &rbacpb.Permission{ - Rule: &rbacpb.Permission_Header{ - Header: header, - }, - } -} - -func permissionPath(path *matcherpb.PathMatcher) *rbacpb.Permission { - return &rbacpb.Permission{ - Rule: &rbacpb.Permission_UrlPath{ - UrlPath: path, - }, - } -} diff --git a/pilot/pkg/security/authz/model/principal.go b/pilot/pkg/security/authz/model/principal.go deleted file mode 100644 index a579f7c37..000000000 --- a/pilot/pkg/security/authz/model/principal.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - rbacpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3" - routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" -) - -func principalAny() *rbacpb.Principal { - return &rbacpb.Principal{ - Identifier: &rbacpb.Principal_Any{ - Any: true, - }, - } -} - -func principalOr(principals []*rbacpb.Principal) *rbacpb.Principal { - return &rbacpb.Principal{ - Identifier: &rbacpb.Principal_OrIds{ - OrIds: &rbacpb.Principal_Set{ - Ids: principals, - }, - }, - } -} - -func principalAnd(principals []*rbacpb.Principal) *rbacpb.Principal { - return &rbacpb.Principal{ - Identifier: &rbacpb.Principal_AndIds{ - AndIds: &rbacpb.Principal_Set{ - Ids: principals, - }, - }, - } -} - -func principalNot(principal *rbacpb.Principal) *rbacpb.Principal { - return &rbacpb.Principal{ - Identifier: &rbacpb.Principal_NotId{ - NotId: principal, - }, - } -} - -func principalAuthenticated(name *matcherpb.StringMatcher) *rbacpb.Principal { - return &rbacpb.Principal{ - Identifier: &rbacpb.Principal_Authenticated_{ - Authenticated: &rbacpb.Principal_Authenticated{ - PrincipalName: name, - }, - }, - } -} - -func principalDirectRemoteIP(cidr *corepb.CidrRange) *rbacpb.Principal { - return &rbacpb.Principal{ - Identifier: &rbacpb.Principal_DirectRemoteIp{ - DirectRemoteIp: cidr, - }, - } -} - -func principalRemoteIP(cidr *corepb.CidrRange) *rbacpb.Principal { - return &rbacpb.Principal{ - Identifier: &rbacpb.Principal_RemoteIp{ - RemoteIp: cidr, - }, - } -} - -func principalMetadata(metadata *matcherpb.MetadataMatcher) *rbacpb.Principal { - return &rbacpb.Principal{ - Identifier: &rbacpb.Principal_Metadata{ - Metadata: metadata, - }, - } -} - -func principalHeader(header *routepb.HeaderMatcher) *rbacpb.Principal { - return &rbacpb.Principal{ - Identifier: &rbacpb.Principal_Header{ - Header: header, - }, - } -} diff --git a/pilot/pkg/security/authz/model/util.go b/pilot/pkg/security/authz/model/util.go deleted file mode 100644 index 4c5fd70ca..000000000 --- a/pilot/pkg/security/authz/model/util.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "fmt" - "strconv" - "strings" -) - -import ( - matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/authz/matcher" - authn "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/model" -) - -// convertToPort converts a port string to a uint32. -func convertToPort(v string) (uint32, error) { - p, err := strconv.ParseUint(v, 10, 32) - if err != nil || p > 65535 { - return 0, fmt.Errorf("invalid port %s: %v", v, err) - } - return uint32(p), nil -} - -func extractNameInBrackets(s string) (string, error) { - if !strings.HasPrefix(s, "[") || !strings.HasSuffix(s, "]") { - return "", fmt.Errorf("expecting format [], but found %s", s) - } - return strings.TrimPrefix(strings.TrimSuffix(s, "]"), "["), nil -} - -func extractNameInNestedBrackets(s string) ([]string, error) { - var claims []string - findEndBracket := func(begin int) int { - if begin >= len(s) || s[begin] != '[' { - return -1 - } - for i := begin + 1; i < len(s); i++ { - if s[i] == '[' { - return -1 - } - if s[i] == ']' { - return i - } - } - return -1 - } - for begin := 0; begin < len(s); { - end := findEndBracket(begin) - if end == -1 { - ret, err := extractNameInBrackets(s) - if err != nil { - return nil, err - } - return []string{ret}, nil - } - claims = append(claims, s[begin+1:end]) - begin = end + 1 - } - return claims, nil -} - -// MetadataMatcherForJWTClaims is a convenient method for generating metadata matcher for JWT claims. -func MetadataMatcherForJWTClaims(claims []string, value *matcherpb.StringMatcher) *matcherpb.MetadataMatcher { - return matcher.MetadataListMatcher(authn.AuthnFilterName, append([]string{attrRequestClaims}, claims...), value) -} diff --git a/pilot/pkg/security/authz/model/util_test.go b/pilot/pkg/security/authz/model/util_test.go deleted file mode 100644 index 6d4526894..000000000 --- a/pilot/pkg/security/authz/model/util_test.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "reflect" - "strings" - "testing" -) - -func TestConvertToPort(t *testing.T) { - testCases := []struct { - Name string - V string - Expect uint32 - Err string - }{ - { - Name: "negative port", - V: "-80", - Err: "invalid port -80:", - }, - { - Name: "invalid port", - V: "xyz", - Err: "invalid port xyz:", - }, - { - Name: "port too large", - V: "91234", - Err: "invalid port 91234:", - }, - { - Name: "valid port", - V: "443", - Expect: 443, - }, - } - - for _, tc := range testCases { - actual, err := convertToPort(tc.V) - if tc.Err != "" { - if err == nil { - t.Errorf("%s: expecting error %s but found no error", tc.Name, tc.Err) - } else if !strings.HasPrefix(err.Error(), tc.Err) { - t.Errorf("%s: expecting error %s, but got: %s", tc.Name, tc.Err, err.Error()) - } - } else if tc.Expect != actual { - t.Errorf("%s: expecting %d, but got %d", tc.Name, tc.Expect, actual) - } - } -} - -func TestExtractNameInBrackets(t *testing.T) { - cases := []struct { - s string - want string - err bool - }{ - {s: "[good]", want: "good"}, - {s: "[[good]]", want: "[good]"}, - {s: "[]", want: ""}, - {s: "[bad", err: true}, - {s: "bad]", err: true}, - {s: "bad", err: true}, - } - - for _, c := range cases { - t.Run(c.s, func(t *testing.T) { - s, err := extractNameInBrackets(c.s) - if s != c.want { - t.Errorf("want %s but found %s", c.want, s) - } - if c.err != (err != nil) { - t.Errorf("unexpected error: %v", err) - } - }) - } -} - -func TestExtractNameInNestedBrackets(t *testing.T) { - cases := []struct { - s string - want []string - err bool - }{ - {s: "[good]", want: []string{"good"}}, - {s: "[good][abc][xyz]", want: []string{"good", "abc", "xyz"}}, - {s: "[]", want: []string{""}}, - {s: "[[good]", want: []string{"[good"}}, - {s: "[good]]", want: []string{"good]"}}, - {s: "[[good]]", want: []string{"[good]"}}, - {s: "x[bad]", err: true}, - {s: "[bad", err: true}, - {s: "bad]", err: true}, - {s: "bad", err: true}, - } - - for _, c := range cases { - t.Run(c.s, func(t *testing.T) { - s, err := extractNameInNestedBrackets(c.s) - if !reflect.DeepEqual(s, c.want) { - t.Errorf("want %s but found %s", c.want, s) - } - if c.err != (err != nil) { - t.Errorf("unexpected error: %v", err) - } - }) - } -} diff --git a/pilot/pkg/security/model/authentication.go b/pilot/pkg/security/model/authentication.go deleted file mode 100644 index 7b0aa4317..000000000 --- a/pilot/pkg/security/model/authentication.go +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "time" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - "google.golang.org/protobuf/types/known/durationpb" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model/credentials" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pkg/security" - "github.com/apache/dubbo-go-pixiu/pkg/spiffe" -) - -const ( - // SDSClusterName is the name of the cluster for SDS connections - SDSClusterName = "sds-grpc" - - // SDSDefaultResourceName is the default name in sdsconfig, used for fetching normal key/cert. - SDSDefaultResourceName = "default" - - // SDSRootResourceName is the sdsconfig name for root CA, used for fetching root cert. - SDSRootResourceName = "ROOTCA" - - // K8sSAJwtFileName is the token volume mount file name for k8s jwt token. - K8sSAJwtFileName = "/var/run/secrets/kubernetes.io/serviceaccount/token" - - // K8sSATrustworthyJwtFileName is the token volume mount file name for k8s trustworthy jwt token. - K8sSATrustworthyJwtFileName = "/var/run/secrets/tokens/istio-token" - - // K8sSAJwtTokenHeaderKey is the request header key for k8s jwt token. - // Binary header name must has suffix "-bin", according to https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md. - K8sSAJwtTokenHeaderKey = "istio_sds_credentials_header-bin" - - // SdsCaSuffix is the suffix of the sds resource name for root CA. - SdsCaSuffix = "-cacert" - - // EnvoyJwtFilterName is the name of the Envoy JWT filter. This should be the same as the name defined - // in https://github.com/envoyproxy/envoy/blob/v1.9.1/source/extensions/filters/http/well_known_names.h#L48 - EnvoyJwtFilterName = "envoy.filters.http.jwt_authn" - - // AuthnFilterName is the name for the Istio AuthN filter. This should be the same - // as the name defined in - // https://github.com/istio/proxy/blob/master/src/envoy/http/authn/http_filter_factory.cc#L30 - AuthnFilterName = "istio_authn" -) - -var SDSAdsConfig = &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_Ads{ - Ads: &core.AggregatedConfigSource{}, - }, - // We intentionally do *not* set InitialFetchTimeout to 0s here, as this is used for - // credentialName SDS which may refer to secrets which do not exist. We do not want to block the - // entire listener/cluster in these cases. - ResourceApiVersion: core.ApiVersion_V3, -} - -// ConstructSdsSecretConfigForCredential constructs SDS secret configuration used -// from certificates referenced by credentialName in DestinationRule or Gateway. -// Currently this is served by a local SDS server, but in the future replaced by -// Istiod SDS server. -func ConstructSdsSecretConfigForCredential(name string) *tls.SdsSecretConfig { - if name == "" { - return nil - } - if name == credentials.BuiltinGatewaySecretTypeURI { - return ConstructSdsSecretConfig(SDSDefaultResourceName) - } - if name == credentials.BuiltinGatewaySecretTypeURI+SdsCaSuffix { - return ConstructSdsSecretConfig(SDSRootResourceName) - } - - return &tls.SdsSecretConfig{ - Name: credentials.ToResourceName(name), - SdsConfig: SDSAdsConfig, - } -} - -// Preconfigured SDS configs to avoid excessive memory allocations -var ( - defaultSDSConfig = &tls.SdsSecretConfig{ - Name: SDSDefaultResourceName, - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: SDSClusterName}, - }, - }, - }, - }, - }, - ResourceApiVersion: core.ApiVersion_V3, - InitialFetchTimeout: durationpb.New(time.Second * 0), - }, - } - rootSDSConfig = &tls.SdsSecretConfig{ - Name: SDSRootResourceName, - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: SDSClusterName}, - }, - }, - }, - }, - }, - ResourceApiVersion: core.ApiVersion_V3, - InitialFetchTimeout: durationpb.New(time.Second * 0), - }, - } -) - -// ConstructSdsSecretConfig constructs SDS Secret Configuration for workload proxy. -func ConstructSdsSecretConfig(name string) *tls.SdsSecretConfig { - if name == "" { - return nil - } - - if name == SDSDefaultResourceName { - return defaultSDSConfig - } - if name == SDSRootResourceName { - return rootSDSConfig - } - - cfg := &tls.SdsSecretConfig{ - Name: name, - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - SetNodeOnFirstMessageOnly: true, - ApiType: core.ApiConfigSource_GRPC, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: SDSClusterName}, - }, - }, - }, - }, - }, - ResourceApiVersion: core.ApiVersion_V3, - }, - } - - return cfg -} - -func appendURIPrefixToTrustDomain(trustDomainAliases []string) []string { - var res []string - for _, td := range trustDomainAliases { - res = append(res, spiffe.URIPrefix+td+"/") - } - return res -} - -// ApplyToCommonTLSContext completes the commonTlsContext -func ApplyToCommonTLSContext(tlsContext *tls.CommonTlsContext, proxy *model.Proxy, - subjectAltNames []string, trustDomainAliases []string, validateClient bool) { - // These are certs being mounted from within the pod. Rather than reading directly in Envoy, - // which does not support rotation, we will serve them over SDS by reading the files. - // We should check if these certs have values, if yes we should use them or otherwise fall back to defaults. - res := security.SdsCertificateConfig{ - CertificatePath: proxy.Metadata.TLSServerCertChain, - PrivateKeyPath: proxy.Metadata.TLSServerKey, - CaCertificatePath: proxy.Metadata.TLSServerRootCert, - } - - // TODO: if subjectAltName ends with *, create a prefix match as well. - // TODO: if user explicitly specifies SANs - should we alter his explicit config by adding all spifee aliases? - matchSAN := util.StringToExactMatch(subjectAltNames) - if len(trustDomainAliases) > 0 { - matchSAN = append(matchSAN, util.StringToPrefixMatch(appendURIPrefixToTrustDomain(trustDomainAliases))...) - } - - // configure server listeners with SDS. - if validateClient { - tlsContext.ValidationContextType = &tls.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &tls.CertificateValidationContext{MatchSubjectAltNames: matchSAN}, - ValidationContextSdsSecretConfig: ConstructSdsSecretConfig(model.GetOrDefault(res.GetRootResourceName(), SDSRootResourceName)), - }, - } - } - tlsContext.TlsCertificateSdsSecretConfigs = []*tls.SdsSecretConfig{ - ConstructSdsSecretConfig(model.GetOrDefault(res.GetResourceName(), SDSDefaultResourceName)), - } -} - -// ApplyCustomSDSToClientCommonTLSContext applies the customized sds to CommonTlsContext -// Used for building upstream TLS context for egress gateway's TLS/mTLS origination -func ApplyCustomSDSToClientCommonTLSContext(tlsContext *tls.CommonTlsContext, tlsOpts *networking.ClientTLSSettings) { - if tlsOpts.Mode == networking.ClientTLSSettings_MUTUAL { - // create SDS config for gateway to fetch key/cert from agent. - tlsContext.TlsCertificateSdsSecretConfigs = []*tls.SdsSecretConfig{ - ConstructSdsSecretConfigForCredential(tlsOpts.CredentialName), - } - } - // create SDS config for gateway to fetch certificate validation context - // at gateway agent. - defaultValidationContext := &tls.CertificateValidationContext{ - MatchSubjectAltNames: util.StringToExactMatch(tlsOpts.SubjectAltNames), - } - tlsContext.ValidationContextType = &tls.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: defaultValidationContext, - ValidationContextSdsSecretConfig: ConstructSdsSecretConfigForCredential(tlsOpts.CredentialName + SdsCaSuffix), - }, - } -} - -// ApplyCredentialSDSToServerCommonTLSContext applies the credentialName sds (Gateway/DestinationRule) to CommonTlsContext -// Used for building both gateway/sidecar TLS context -func ApplyCredentialSDSToServerCommonTLSContext(tlsContext *tls.CommonTlsContext, tlsOpts *networking.ServerTLSSettings) { - // create SDS config for gateway/sidecar to fetch key/cert from agent. - tlsContext.TlsCertificateSdsSecretConfigs = []*tls.SdsSecretConfig{ - ConstructSdsSecretConfigForCredential(tlsOpts.CredentialName), - } - // If tls mode is MUTUAL, create SDS config for gateway/sidecar to fetch certificate validation context - // at gateway agent. Otherwise, use the static certificate validation context config. - if tlsOpts.Mode == networking.ServerTLSSettings_MUTUAL { - defaultValidationContext := &tls.CertificateValidationContext{ - MatchSubjectAltNames: util.StringToExactMatch(tlsOpts.SubjectAltNames), - VerifyCertificateSpki: tlsOpts.VerifyCertificateSpki, - VerifyCertificateHash: tlsOpts.VerifyCertificateHash, - } - tlsContext.ValidationContextType = &tls.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: defaultValidationContext, - ValidationContextSdsSecretConfig: ConstructSdsSecretConfigForCredential(tlsOpts.CredentialName + SdsCaSuffix), - }, - } - } else if len(tlsOpts.SubjectAltNames) > 0 { - tlsContext.ValidationContextType = &tls.CommonTlsContext_ValidationContext{ - ValidationContext: &tls.CertificateValidationContext{ - MatchSubjectAltNames: util.StringToExactMatch(tlsOpts.SubjectAltNames), - }, - } - } -} diff --git a/pilot/pkg/security/model/authentication_test.go b/pilot/pkg/security/model/authentication_test.go deleted file mode 100644 index cc26cb254..000000000 --- a/pilot/pkg/security/model/authentication_test.go +++ /dev/null @@ -1,436 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "testing" - "time" -) - -import ( - "github.com/davecgh/go-spew/spew" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - auth "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/testing/protocmp" - "google.golang.org/protobuf/types/known/durationpb" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/spiffe" -) - -func TestConstructSdsSecretConfig(t *testing.T) { - testCases := []struct { - name string - secretName string - expected *auth.SdsSecretConfig - }{ - { - name: "ConstructSdsSecretConfig", - secretName: "spiffe://cluster.local/ns/bar/sa/foo", - expected: &auth.SdsSecretConfig{ - Name: "spiffe://cluster.local/ns/bar/sa/foo", - SdsConfig: &core.ConfigSource{ - ResourceApiVersion: core.ApiVersion_V3, - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - { - name: "ConstructSdsSecretConfig without secretName", - secretName: "", - expected: nil, - }, - } - - for _, c := range testCases { - t.Run(c.name, func(t *testing.T) { - if got := ConstructSdsSecretConfig(c.secretName); !cmp.Equal(got, c.expected, protocmp.Transform()) { - t.Errorf("ConstructSdsSecretConfig: got(%#v), want(%#v)\n", got, c.expected) - } - }) - } -} - -func TestApplyToCommonTLSContext(t *testing.T) { - testCases := []struct { - name string - node *model.Proxy - trustDomainAliases []string - validateClient bool - expected *auth.CommonTlsContext - }{ - { - name: "MTLSStrict using SDS", - node: &model.Proxy{ - Metadata: &model.NodeMetadata{}, - }, - validateClient: true, - expected: &auth.CommonTlsContext{ - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "default", - SdsConfig: &core.ConfigSource{ - InitialFetchTimeout: durationpb.New(time.Second * 0), - ResourceApiVersion: core.ApiVersion_V3, - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{}, - ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ - Name: "ROOTCA", - SdsConfig: &core.ConfigSource{ - InitialFetchTimeout: durationpb.New(time.Second * 0), - ResourceApiVersion: core.ApiVersion_V3, - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - { - name: "MTLSStrict using SDS and SAN aliases", - node: &model.Proxy{ - Metadata: &model.NodeMetadata{}, - }, - validateClient: true, - trustDomainAliases: []string{"alias-1.domain", "some-other-alias-1.domain", "alias-2.domain"}, - expected: &auth.CommonTlsContext{ - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "default", - SdsConfig: &core.ConfigSource{ - InitialFetchTimeout: durationpb.New(time.Second * 0), - ResourceApiVersion: core.ApiVersion_V3, - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{MatchSubjectAltNames: []*matcher.StringMatcher{ - {MatchPattern: &matcher.StringMatcher_Prefix{Prefix: spiffe.URIPrefix + "alias-1.domain" + "/"}}, - {MatchPattern: &matcher.StringMatcher_Prefix{Prefix: spiffe.URIPrefix + "some-other-alias-1.domain" + "/"}}, - {MatchPattern: &matcher.StringMatcher_Prefix{Prefix: spiffe.URIPrefix + "alias-2.domain" + "/"}}, - }}, - ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ - Name: "ROOTCA", - SdsConfig: &core.ConfigSource{ - InitialFetchTimeout: durationpb.New(time.Second * 0), - ResourceApiVersion: core.ApiVersion_V3, - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: SDSClusterName}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - { - name: "MTLS using SDS with custom certs in metadata", - node: &model.Proxy{ - Metadata: &model.NodeMetadata{ - TLSServerCertChain: "serverCertChain", - TLSServerKey: "serverKey", - TLSServerRootCert: "servrRootCert", - }, - }, - validateClient: true, - expected: &auth.CommonTlsContext{ - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "file-cert:serverCertChain~serverKey", - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: SDSClusterName}, - }, - }, - }, - }, - }, - ResourceApiVersion: core.ApiVersion_V3, - }, - }, - }, - ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{}, - ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ - Name: "file-root:servrRootCert", - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: SDSClusterName}, - }, - }, - }, - }, - }, - ResourceApiVersion: core.ApiVersion_V3, - }, - }, - }, - }, - }, - }, - { - name: "ISTIO_MUTUAL SDS without node meta", - node: &model.Proxy{ - Metadata: &model.NodeMetadata{}, - }, - validateClient: true, - expected: &auth.CommonTlsContext{ - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "default", - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: SDSClusterName}, - }, - }, - }, - }, - }, - ResourceApiVersion: core.ApiVersion_V3, - InitialFetchTimeout: durationpb.New(time.Second * 0), - }, - }, - }, - ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{}, - ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ - Name: "ROOTCA", - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: SDSClusterName}, - }, - }, - }, - }, - }, - ResourceApiVersion: core.ApiVersion_V3, - InitialFetchTimeout: durationpb.New(time.Second * 0), - }, - }, - }, - }, - }, - }, - { - name: "ISTIO_MUTUAL with custom cert paths from proxy node metadata", - node: &model.Proxy{ - Metadata: &model.NodeMetadata{ - TLSServerCertChain: "/custom/path/to/cert-chain.pem", - TLSServerKey: "/custom-key.pem", - TLSServerRootCert: "/custom/path/to/root.pem", - }, - }, - validateClient: true, - expected: &auth.CommonTlsContext{ - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "file-cert:/custom/path/to/cert-chain.pem~/custom-key.pem", - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: SDSClusterName}, - }, - }, - }, - }, - }, - ResourceApiVersion: core.ApiVersion_V3, - }, - }, - }, - ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{}, - ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ - Name: "file-root:/custom/path/to/root.pem", - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: SDSClusterName}, - }, - }, - }, - }, - }, - ResourceApiVersion: core.ApiVersion_V3, - }, - }, - }, - }, - }, - }, - { - name: "SIMPLE with custom cert paths from proxy node metadata without cacerts", - node: &model.Proxy{ - Metadata: &model.NodeMetadata{ - TLSServerCertChain: "/custom/path/to/cert-chain.pem", - TLSServerKey: "/custom-key.pem", - }, - }, - validateClient: false, - expected: &auth.CommonTlsContext{ - TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ - { - Name: "file-cert:/custom/path/to/cert-chain.pem~/custom-key.pem", - SdsConfig: &core.ConfigSource{ - ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - TransportApiVersion: core.ApiVersion_V3, - GrpcServices: []*core.GrpcService{ - { - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: SDSClusterName}, - }, - }, - }, - }, - }, - ResourceApiVersion: core.ApiVersion_V3, - }, - }, - }, - }, - }, - } - - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - tlsContext := &auth.CommonTlsContext{} - ApplyToCommonTLSContext(tlsContext, test.node, []string{}, test.trustDomainAliases, test.validateClient) - - if !cmp.Equal(tlsContext, test.expected, protocmp.Transform()) { - t.Errorf("got(%#v), want(%#v)\n", spew.Sdump(tlsContext), spew.Sdump(test.expected)) - } - }) - } -} diff --git a/pilot/pkg/security/trustdomain/bundle.go b/pilot/pkg/security/trustdomain/bundle.go deleted file mode 100644 index f27b45f8a..000000000 --- a/pilot/pkg/security/trustdomain/bundle.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package trustdomain - -import ( - "fmt" - "strings" -) - -import ( - istiolog "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" -) - -var authzLog = istiolog.RegisterScope("authorization", "Istio Authorization Policy", 0) - -type Bundle struct { - // Contain the local trust domain and its aliases. - // The trust domain corresponds to the trust root of a system. - // Refer to [SPIFFE-ID](https://github.com/spiffe/spiffe/blob/master/standards/SPIFFE-ID.md#21-trust-domain) - // The trust domain aliases represent the aliases of `trust_domain`. - // For example, if we have - // trustDomain: td1, trustDomainAliases: ["td2", "td3"] - // Any service with the identity `td1/ns/foo/sa/a-service-account`, `td2/ns/foo/sa/a-service-account`, - // or `td3/ns/foo/sa/a-service-account` will be treated the same in the Istio mesh. - TrustDomains []string -} - -// NewBundle returns a new trust domain bundle. -func NewBundle(trustDomain string, trustDomainAliases []string) Bundle { - return Bundle{ - // Put the new trust domain to the beginning of the list to avoid changing existing tests. - TrustDomains: append([]string{trustDomain}, trustDomainAliases...), - } -} - -// ReplaceTrustDomainAliases checks the existing principals and returns a list of new principals -// with the current trust domain and its aliases. -// For example, for a user "bar" in namespace "foo". -// If the local trust domain is "td2" and its alias is "td1" (migrating from td1 to td2), -// replaceTrustDomainAliases returns ["td2/ns/foo/sa/bar", "td1/ns/foo/sa/bar]]. -func (t Bundle) ReplaceTrustDomainAliases(principals []string) []string { - principalsIncludingAliases := []string{} - for _, principal := range principals { - isTrustDomainBeingEnforced := isTrustDomainBeingEnforced(principal) - // Return the existing principals if the policy doesn't care about the trust domain. - if !isTrustDomainBeingEnforced { - principalsIncludingAliases = append(principalsIncludingAliases, principal) - continue - } - trustDomainFromPrincipal, err := getTrustDomainFromSpiffeIdentity(principal) - if err != nil { - authzLog.Errorf("unexpected incorrect Spiffe format: %s", principal) - principalsIncludingAliases = append(principalsIncludingAliases, principal) - continue - } - // Only generate configuration if the extracted trust domain from the policy is part of the trust domain list, - // or if the extracted/existing trust domain is "cluster.local", which is a pointer to the local trust domain - // and its aliases. - if stringMatch(trustDomainFromPrincipal, t.TrustDomains) || trustDomainFromPrincipal == constants.DefaultKubernetesDomain { - // Generate configuration for trust domain and trust domain aliases. - principalsIncludingAliases = append(principalsIncludingAliases, t.replaceTrustDomains(principal, trustDomainFromPrincipal)...) - } else { - authzLog.Warnf("Trust domain %s from principal %s does not match the current trust "+ - "domain or its aliases", trustDomainFromPrincipal, principal) - // If the trust domain from the existing doesn't match with the new trust domain aliases or "cluster.local", - // keep the policy as it is. - principalsIncludingAliases = append(principalsIncludingAliases, principal) - } - } - return principalsIncludingAliases -} - -// replaceTrustDomains replace the given principal's trust domain with the trust domains from the -// trustDomains list and return the new principals. -func (t Bundle) replaceTrustDomains(principal, trustDomainFromPrincipal string) []string { - principalsForAliases := []string{} - for _, td := range t.TrustDomains { - // If the trust domain has a prefix * (e.g. *local from *local/ns/foo/sa/bar), keep the principal - // as-is for the matched trust domain. For others, replace the trust domain with the new trust domain - // or alias. - var newPrincipal string - var err error - if suffixMatch(td, trustDomainFromPrincipal) { - newPrincipal = principal - } else { - newPrincipal, err = replaceTrustDomainInPrincipal(td, principal) - if err != nil { - authzLog.Errorf("Failed to replace trust domain with %s from principal %s: %v", td, principal, err) - continue - } - } - // Check to make sure we don't generate duplicated principals. This happens when trust domain - // has a * prefix. For example, "*-td" can match with "old-td" and "new-td", but we only want - // to keep the principal as-is in the generated config, .i.e. *-td. - if !isKeyInList(newPrincipal, principalsForAliases) { - principalsForAliases = append(principalsForAliases, newPrincipal) - } - } - return principalsForAliases -} - -// replaceTrustDomainInPrincipal returns a new SPIFFE identity with the new trust domain. -// The trust domain corresponds to the trust root of a system. -// Refer to -// [SPIFFE-ID](https://github.com/spiffe/spiffe/blob/master/standards/SPIFFE-ID.md#21-trust-domain) -// In Istio authorization, an identity is presented in the format: -// /ns//sa/ -func replaceTrustDomainInPrincipal(trustDomain string, principal string) (string, error) { - identityParts := strings.Split(principal, "/") - // A valid SPIFFE identity in authorization has no SPIFFE:// prefix. - // It is presented as /ns//sa/ - if len(identityParts) != 5 { - return "", fmt.Errorf("wrong SPIFFE format: %s", principal) - } - return fmt.Sprintf("%s/%s", trustDomain, strings.Join(identityParts[1:], "/")), nil -} - -// isTrustDomainBeingEnforced checks whether the trust domain is being checked in the filter or not. -// For example, in the principal "*/ns/foo/sa/bar", the trust domain is * and it matches to any trust domain, -// so it won't be checked in the filter. -func isTrustDomainBeingEnforced(principal string) bool { - identityParts := strings.Split(principal, "/") - if len(identityParts) != 5 { - // If a principal is mis-configured and doesn't follow Spiffe format, e.g. "sa/bar", - // there is really no trust domain from the principal, so the trust domain is also considered not being enforced. - return false - } - // Check if the first part of the spiffe string is "*" (as opposed to *-something or ""). - return identityParts[0] != "*" -} - -// getTrustDomainFromSpiffeIdentity gets the trust domain from the given principal and expects -// principal to have the right SPIFFE format. -func getTrustDomainFromSpiffeIdentity(principal string) (string, error) { - identityParts := strings.Split(principal, "/") - // A valid SPIFFE identity in authorization has no SPIFFE:// prefix. - // It is presented as /ns//sa/ - if len(identityParts) != 5 { - return "", fmt.Errorf("wrong SPIFFE format: %s", principal) - } - trustDomain := identityParts[0] - return trustDomain, nil -} diff --git a/pilot/pkg/security/trustdomain/bundle_test.go b/pilot/pkg/security/trustdomain/bundle_test.go deleted file mode 100644 index d8e38a12b..000000000 --- a/pilot/pkg/security/trustdomain/bundle_test.go +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package trustdomain - -import ( - "reflect" - "testing" -) - -func TestReplaceTrustDomainAliases(t *testing.T) { - testCases := []struct { - name string - trustDomainBundle Bundle - principals []string - expect []string - }{ - { - name: "No trust domain aliases (no change in trust domain)", - trustDomainBundle: NewBundle("cluster.local", nil), - principals: []string{"cluster.local/ns/foo/sa/bar"}, - expect: []string{"cluster.local/ns/foo/sa/bar"}, - }, - { - name: "Principal with *", - trustDomainBundle: NewBundle("cluster.local", nil), - principals: []string{"*"}, - expect: []string{"*"}, - }, - { - name: "Principal with * prefix", - trustDomainBundle: NewBundle("cluster.local", nil), - principals: []string{"*/ns/foo/sa/bar"}, - expect: []string{"*/ns/foo/sa/bar"}, - }, - { - name: "One trust domain alias, one principal", - trustDomainBundle: NewBundle("td2", []string{"td1"}), - principals: []string{"td1/ns/foo/sa/bar"}, - expect: []string{"td2/ns/foo/sa/bar", "td1/ns/foo/sa/bar"}, - }, - { - name: "One trust domain alias, two principals", - trustDomainBundle: NewBundle("td1", []string{"cluster.local"}), - principals: []string{"cluster.local/ns/foo/sa/bar", "cluster.local/ns/yyy/sa/zzz"}, - expect: []string{"td1/ns/foo/sa/bar", "cluster.local/ns/foo/sa/bar", "td1/ns/yyy/sa/zzz", "cluster.local/ns/yyy/sa/zzz"}, - }, - { - name: "One trust domain alias, principals with * as-is", - trustDomainBundle: NewBundle("td1", []string{"cluster.local"}), - principals: []string{"*/ns/foo/sa/bar", "*sa/zzz", "*"}, - expect: []string{"*/ns/foo/sa/bar", "*sa/zzz", "*"}, - }, - { - name: "Two trust domain aliases, two principals", - trustDomainBundle: NewBundle("td2", []string{"td1", "cluster.local"}), - principals: []string{"cluster.local/ns/foo/sa/bar", "td1/ns/yyy/sa/zzz"}, - expect: []string{ - "td2/ns/foo/sa/bar", "td1/ns/foo/sa/bar", "cluster.local/ns/foo/sa/bar", - "td2/ns/yyy/sa/zzz", "td1/ns/yyy/sa/zzz", "cluster.local/ns/yyy/sa/zzz", - }, - }, - { - name: "Two trust domain aliases with * prefix in trust domain", - trustDomainBundle: NewBundle("td2", []string{"foo-td1", "cluster.local"}), - principals: []string{"*-td1/ns/foo/sa/bar"}, - expect: []string{"td2/ns/foo/sa/bar", "*-td1/ns/foo/sa/bar", "cluster.local/ns/foo/sa/bar"}, - }, - { - name: "Principals not match any trust domains", - trustDomainBundle: NewBundle("td1", []string{"td2"}), - principals: []string{"some-td/ns/foo/sa/bar"}, - expect: []string{"some-td/ns/foo/sa/bar"}, - }, - { - name: "Principals match one alias", - trustDomainBundle: NewBundle("td1", []string{"td2", "some-td"}), - principals: []string{"some-td/ns/foo/sa/bar"}, - expect: []string{"td1/ns/foo/sa/bar", "td2/ns/foo/sa/bar", "some-td/ns/foo/sa/bar"}, - }, - { - name: "One principal match one alias", - trustDomainBundle: NewBundle("new-td", []string{"td2", "td3"}), - principals: []string{"td1/ns/some-ns/sa/some-sa", "td2/ns/foo/sa/bar"}, - expect: []string{ - "td1/ns/some-ns/sa/some-sa", "new-td/ns/foo/sa/bar", - "td2/ns/foo/sa/bar", "td3/ns/foo/sa/bar", - }, - }, - { - name: "Trust domain is empty string", - trustDomainBundle: NewBundle("new-td", []string{"td2", "td3"}), - principals: []string{"/ns/some-ns/sa/some-sa"}, - expect: []string{"/ns/some-ns/sa/some-sa"}, - }, - { - name: "No duplicated principals for prefix", - trustDomainBundle: NewBundle("new-td", []string{"old-td"}), - principals: []string{"*-td/ns/some-ns/sa/some-sa"}, - // Rather than output *-td/ns/some-ns/sa/some-sa once for each trust domain. - expect: []string{"*-td/ns/some-ns/sa/some-sa"}, - }, - } - - for _, tc := range testCases { - got := tc.trustDomainBundle.ReplaceTrustDomainAliases(tc.principals) - if !reflect.DeepEqual(got, tc.expect) { - t.Errorf("%s failed. Expect: %s. Got: %s", tc.name, tc.expect, got) - } - } -} - -func TestReplaceTrustDomainInPrincipal(t *testing.T) { - cases := []struct { - name string - trustDomainIn string - principal string - out string - expectedError string - }{ - { - name: "Principal in wrong format with SPIFFE:// prefix", - principal: "spiffe://cluster.local/ns/foo/sa/bar", - out: "", - expectedError: "wrong SPIFFE format: spiffe://cluster.local/ns/foo/sa/bar", - }, - { - name: "Principal in wrong format with less components", - principal: "sa/test-sa/ns/default", - out: "", - expectedError: "wrong SPIFFE format: sa/test-sa/ns/default", - }, - { - name: "Replace td with domain name in principal", - trustDomainIn: "td", - principal: "cluster.local/ns/foo/sa/bar", - out: "td/ns/foo/sa/bar", - expectedError: "", - }, - { - name: "Replace td without domain name in principal", - trustDomainIn: "abc", - principal: "xyz/ns/foo/sa/bar", - out: "abc/ns/foo/sa/bar", - expectedError: "", - }, - } - - for _, c := range cases { - got, err := replaceTrustDomainInPrincipal(c.trustDomainIn, c.principal) - if err != nil { - if c.expectedError == "" { - t.Errorf("%s: replace trust domain in principal error: %v", c.name, err) - } - if got != "" { - t.Errorf("%s: Expected empty SPIFFE ID but obtained a non-empty one: %s.", c.name, got) - } - if err.Error() != c.expectedError { - t.Errorf("%s: Expected error: %s but got error: %s.", c.name, c.expectedError, err.Error()) - } - continue - } - - if got != c.out { - t.Errorf("%s failed. Expect %s, but got %s", c.name, c.out, got) - } - } -} - -func TestGetTrustDomainFromSpiffeIdentity(t *testing.T) { - cases := []struct { - principal string - out string - }{ - {principal: "spiffe://cluster.local/ns/foo/sa/bar", out: ""}, - {principal: "sa/test-sa/ns/default", out: ""}, - {principal: "cluster.local/ns/foo/sa/bar", out: "cluster.local"}, - {principal: "xyz/ns/foo/sa/bar", out: "xyz"}, - } - - for _, c := range cases { - got, _ := getTrustDomainFromSpiffeIdentity(c.principal) - if got != c.out { - t.Errorf("expect %s, but got %s", c.out, got) - } - } -} - -func TestIsTrustDomainBeingEnforced(t *testing.T) { - cases := []struct { - principal string - want bool - }{ - {principal: "cluster.local/ns/foo/sa/bar", want: true}, - {principal: "*/ns/foo/sa/bar", want: false}, - {principal: "*-td/ns/foo/sa/bar", want: true}, - {principal: "*/sa/bar", want: false}, - {principal: "*", want: false}, - {principal: "/ns/foo/sa/bar", want: true}, - } - - for _, c := range cases { - got := isTrustDomainBeingEnforced(c.principal) - if got != c.want { - t.Errorf("expect %v, but got %v", c.want, got) - } - } -} diff --git a/pilot/pkg/security/trustdomain/leak_test.go b/pilot/pkg/security/trustdomain/leak_test.go deleted file mode 100644 index 0cab68be5..000000000 --- a/pilot/pkg/security/trustdomain/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package trustdomain - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/security/trustdomain/util.go b/pilot/pkg/security/trustdomain/util.go deleted file mode 100644 index 66488394c..000000000 --- a/pilot/pkg/security/trustdomain/util.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package trustdomain - -import ( - "strings" -) - -// stringMatch checks if a string is in a list, it supports four types of string matches: -// 1. Exact match. -// 2. Wild character match. "*" matches any string. -// 3. Prefix match. For example, "book*" matches "bookstore", "bookshop", etc. -// 4. Suffix match. For example, "*/review" matches "/bookstore/review", "/products/review", etc. -// This is an extensive version of model.stringMatch(). The pattern can be in the string or the list. -func stringMatch(a string, list []string) bool { - for _, s := range list { - if a == s || s == "*" || prefixMatch(a, s) || prefixMatch(s, a) || suffixMatch(a, s) || suffixMatch(s, a) { - return true - } - } - return false -} - -// prefixMatch checks if pattern is a prefix match and if string a has the given prefix. -func prefixMatch(a string, pattern string) bool { - if !strings.HasSuffix(pattern, "*") { - return false - } - pattern = strings.TrimSuffix(pattern, "*") - return strings.HasPrefix(a, pattern) -} - -// suffixMatch checks if pattern is a suffix match and if string a has the given suffix. -func suffixMatch(a string, pattern string) bool { - if !strings.HasPrefix(pattern, "*") { - return false - } - pattern = strings.TrimPrefix(pattern, "*") - return strings.HasSuffix(a, pattern) -} - -// isKeyInList it's fine to use this naive implementation for searching in a very short list. -func isKeyInList(key string, list []string) bool { - for _, l := range list { - if key == l { - return true - } - } - return false -} diff --git a/pilot/pkg/security/trustdomain/util_test.go b/pilot/pkg/security/trustdomain/util_test.go deleted file mode 100644 index 6ba4f7dc7..000000000 --- a/pilot/pkg/security/trustdomain/util_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package trustdomain - -import ( - "testing" -) - -func TestStringMatch(t *testing.T) { - tests := []struct { - desc string - element string - list []string - expected bool - }{ - { - desc: "wildcard", - element: "*", - list: []string{"match-me"}, - expected: true, - }, - { - desc: "suffix=match", - element: "yo*", - list: []string{"yo", "yolo", "yo-yo"}, - expected: true, - }, - { - desc: "no-sub", - element: "foo", - list: []string{"bar"}, - expected: false, - }, - { - desc: "prefix-match", - element: "*yo", - list: []string{"yoyo", "goyo"}, - expected: true, - }, - { - desc: "empty", - element: "", - list: []string{"yo", "tawg"}, - expected: false, - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got := stringMatch(tt.element, tt.list); got != tt.expected { - t.Errorf("%s: expected %v got %v", tt.desc, tt.expected, got) - } - }) - } -} diff --git a/pilot/pkg/server/instance.go b/pilot/pkg/server/instance.go deleted file mode 100644 index 353e5002b..000000000 --- a/pilot/pkg/server/instance.go +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "sync" -) - -import ( - "istio.io/pkg/log" -) - -type Component func(stop <-chan struct{}) error - -// Instance is a server that is composed a number of Component tasks. -type Instance interface { - // Start this Server. Any components that were already added - // will be run immediately. If any error is returned, - // Start will terminate and return the error immediately. - // - // Once all startup components have been run, starts a polling - // loop to continue monitoring for new components and returns nil. - Start(stop <-chan struct{}) error - - // RunComponent adds the given component to the server's run queue. - RunComponent(t Component) - - // RunComponentAsync runs the given component asynchronously. - RunComponentAsync(t Component) - - // RunComponentAsyncAndWait runs the given component asynchronously. When - // the serer Instance is shutting down, it will wait for the component - // to complete before exiting. - // Note: this is best effort; a process can die at any time. - RunComponentAsyncAndWait(t Component) - - // Wait for this server Instance to shutdown. - Wait() -} - -var _ Instance = &instance{} - -// New creates a new server Instance. -func New() Instance { - return &instance{ - done: make(chan struct{}), - components: make(chan Component, 1000), // should be enough? - } -} - -type instance struct { - components chan Component - done chan struct{} - - // requiredTerminations keeps track of tasks that should block instance exit - // if they are not stopped. This allows important cleanup tasks to be completed. - // Note: this is still best effort; a process can die at any time. - requiredTerminations sync.WaitGroup -} - -func (i *instance) Start(stop <-chan struct{}) error { - shutdown := func() { - close(i.done) - } - - // First, drain all startup tasks and immediately return if any fail. - for startupDone := false; !startupDone; { - select { - case next := <-i.components: - if err := next(stop); err != nil { - // Startup error: terminate and return the error. - shutdown() - return err - } - default: - // We've drained all of the initial tasks. - // Break out of the loop and run asynchronously. - startupDone = true - } - } - - // Start the run loop to continue tasks added after the instance is started. - go func() { - for { - select { - case <-stop: - // Wait for any tasks that are required for termination. - i.requiredTerminations.Wait() - - // Indicate that this instance is not terminated. - shutdown() - return - case next := <-i.components: - if err := next(stop); err != nil { - logComponentError(err) - } - } - } - }() - - return nil -} - -func (i *instance) RunComponent(t Component) { - select { - case <-i.done: - log.Warnf("attempting to run a new component after the server was shutdown") - default: - i.components <- t - } -} - -func (i *instance) RunComponentAsync(task Component) { - i.RunComponent(func(stop <-chan struct{}) error { - go func() { - err := task(stop) - if err != nil { - logComponentError(err) - } - }() - return nil - }) -} - -func (i *instance) RunComponentAsyncAndWait(task Component) { - i.RunComponent(func(stop <-chan struct{}) error { - i.requiredTerminations.Add(1) - go func() { - err := task(stop) - if err != nil { - logComponentError(err) - } - i.requiredTerminations.Done() - }() - return nil - }) -} - -func (i *instance) Wait() { - <-i.done -} - -func logComponentError(err error) { - log.Errorf("failure in server component: %v", err) -} diff --git a/pilot/pkg/server/instance_test.go b/pilot/pkg/server/instance_test.go deleted file mode 100644 index 4ca26de68..000000000 --- a/pilot/pkg/server/instance_test.go +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server_test - -import ( - "errors" - "testing" - "time" -) - -import ( - . "github.com/onsi/gomega" - "go.uber.org/atomic" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/server" -) - -func TestStartWithError(t *testing.T) { - g := NewWithT(t) - - inst := server.New() - expected := errors.New("fake") - inst.RunComponent(func(stop <-chan struct{}) error { - return expected - }) - - stop := newReclosableChannel() - t.Cleanup(stop.Close) - g.Expect(inst.Start(stop.c)).To(Equal(expected)) -} - -func TestStartWithNoError(t *testing.T) { - g := NewWithT(t) - - inst := server.New() - c := newFakeComponent(0) - inst.RunComponent(c.Run) - - stop := newReclosableChannel() - t.Cleanup(stop.Close) - g.Expect(inst.Start(stop.c)).To(BeNil()) - g.Expect(c.started.Load()).To(BeTrue()) -} - -func TestRunComponentsAfterStart(t *testing.T) { - longDuration := 10 * time.Second - shortDuration := 1 * time.Second - cases := []struct { - name string - c *fakeComponent - async bool - wait bool - }{ - { - name: "RunComponent", - // Use a large duration - it will not complete before the end of the test. - // This is used to verify that we don't wait for it while shutting down. - c: newFakeComponent(longDuration), - async: false, - wait: false, - }, - { - name: "RunComponentAsync", - // Use a large duration - it will not complete before the end of the test. - // This is used to verify that we don't wait for it while shutting down. - c: newFakeComponent(longDuration), - async: true, - wait: false, - }, - { - name: "RunComponentAsyncAndWait", - c: newFakeComponent(shortDuration), - async: true, - wait: true, - }, - } - for _, c := range cases { - c := c - t.Run(c.name, func(t *testing.T) { - g := NewWithT(t) - - stop := newReclosableChannel() - t.Cleanup(stop.Close) - inst := server.New() - g.Expect(inst.Start(stop.c)).To(BeNil()) - - go func() { - component := c.c.Run - if c.async { - if c.wait { - inst.RunComponentAsyncAndWait(component) - } else { - inst.RunComponentAsync(component) - } - } else { - inst.RunComponent(component) - } - }() - - // Ensure that the component is started. - g.Eventually(func() bool { - return c.c.started.Load() - }).Should(BeTrue()) - - // Stop before the tasks end. - stop.Close() - if c.wait { - // Add a little buffer to the task duration. - totalWaitTime := shortDuration + (1 * time.Second) - g.Eventually(func() bool { - return c.c.completed.Load() - }, totalWaitTime).Should(BeTrue()) - } else { - g.Expect(c.c.completed.Load()).Should(BeFalse()) - } - }) - } -} - -type reclosableChannel struct { - c chan struct{} - closed bool -} - -func newReclosableChannel() *reclosableChannel { - return &reclosableChannel{ - c: make(chan struct{}), - } -} - -func (c *reclosableChannel) Close() { - if !c.closed { - c.closed = true - close(c.c) - } -} - -type fakeComponent struct { - started *atomic.Bool - completed *atomic.Bool - d time.Duration -} - -func newFakeComponent(d time.Duration) *fakeComponent { - return &fakeComponent{ - started: atomic.NewBool(false), - completed: atomic.NewBool(false), - d: d, - } -} - -func (c *fakeComponent) Run(_ <-chan struct{}) error { - c.started.Store(true) - time.Sleep(c.d) - c.completed.Store(true) - return nil -} diff --git a/pilot/pkg/serviceregistry/aggregate/controller.go b/pilot/pkg/serviceregistry/aggregate/controller.go deleted file mode 100644 index 0930b7567..000000000 --- a/pilot/pkg/serviceregistry/aggregate/controller.go +++ /dev/null @@ -1,430 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package aggregate - -import ( - "sort" - "sync" -) - -import ( - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/spiffe" -) - -// The aggregate controller does not implement serviceregistry.Instance since it may be comprised of various -// providers and clusters. -var ( - _ model.ServiceDiscovery = &Controller{} - _ model.AggregateController = &Controller{} -) - -// Controller aggregates data across different registries and monitors for changes -type Controller struct { - meshHolder mesh.Holder - - // The lock is used to protect the registries and controller's running status. - storeLock sync.RWMutex - registries []*registryEntry - // indicates whether the controller has run. - // if true, all the registries added later should be run manually. - running bool - - handlers model.ControllerHandlers - handlersByCluster map[cluster.ID]*model.ControllerHandlers - model.NetworkGatewaysHandler -} - -type registryEntry struct { - serviceregistry.Instance - // stop if not nil is the per-registry stop chan. If null, the server stop chan should be used to Run the registry. - stop <-chan struct{} -} - -type Options struct { - MeshHolder mesh.Holder -} - -// NewController creates a new Aggregate controller -func NewController(opt Options) *Controller { - return &Controller{ - registries: make([]*registryEntry, 0), - meshHolder: opt.MeshHolder, - running: false, - handlersByCluster: map[cluster.ID]*model.ControllerHandlers{}, - } -} - -func (c *Controller) addRegistry(registry serviceregistry.Instance, stop <-chan struct{}) { - c.registries = append(c.registries, ®istryEntry{Instance: registry, stop: stop}) - - // Observe the registry for events. - registry.AppendNetworkGatewayHandler(c.NotifyGatewayHandlers) - registry.AppendServiceHandler(c.handlers.NotifyServiceHandlers) - registry.AppendServiceHandler(func(service *model.Service, event model.Event) { - for _, handlers := range c.getClusterHandlers() { - handlers.NotifyServiceHandlers(service, event) - } - }) -} - -func (c *Controller) getClusterHandlers() []*model.ControllerHandlers { - c.storeLock.Lock() - defer c.storeLock.Unlock() - out := make([]*model.ControllerHandlers, 0, len(c.handlersByCluster)) - for _, handlers := range c.handlersByCluster { - out = append(out, handlers) - } - return out -} - -// AddRegistry adds registries into the aggregated controller. -// If the aggregated controller is already Running, the given registry will never be started. -func (c *Controller) AddRegistry(registry serviceregistry.Instance) { - c.storeLock.Lock() - defer c.storeLock.Unlock() - c.addRegistry(registry, nil) -} - -// AddRegistryAndRun adds registries into the aggregated controller and makes sure it is Run. -// If the aggregated controller is running, the given registry is Run immediately. -// Otherwise, the given registry is Run when the aggregate controller is Run, using the given stop. -func (c *Controller) AddRegistryAndRun(registry serviceregistry.Instance, stop <-chan struct{}) { - if stop == nil { - log.Warnf("nil stop channel passed to AddRegistryAndRun for registry %s/%s", registry.Provider(), registry.Cluster()) - } - c.storeLock.Lock() - defer c.storeLock.Unlock() - c.addRegistry(registry, stop) - if c.running { - go registry.Run(stop) - } -} - -// DeleteRegistry deletes specified registry from the aggregated controller -func (c *Controller) DeleteRegistry(clusterID cluster.ID, providerID provider.ID) { - c.storeLock.Lock() - defer c.storeLock.Unlock() - - if len(c.registries) == 0 { - log.Warnf("Registry list is empty, nothing to delete") - return - } - index, ok := c.getRegistryIndex(clusterID, providerID) - if !ok { - log.Warnf("Registry %s/%s is not found in the registries list, nothing to delete", providerID, clusterID) - return - } - c.registries[index] = nil - c.registries = append(c.registries[:index], c.registries[index+1:]...) - log.Infof("%s registry for the cluster %s has been deleted.", providerID, clusterID) -} - -// GetRegistries returns a copy of all registries -func (c *Controller) GetRegistries() []serviceregistry.Instance { - c.storeLock.RLock() - defer c.storeLock.RUnlock() - - // copy registries to prevent race, no need to deep copy here. - out := make([]serviceregistry.Instance, len(c.registries)) - for i := range c.registries { - out[i] = c.registries[i] - } - return out -} - -func (c *Controller) getRegistryIndex(clusterID cluster.ID, provider provider.ID) (int, bool) { - for i, r := range c.registries { - if r.Cluster().Equals(clusterID) && r.Provider() == provider { - return i, true - } - } - return 0, false -} - -// Services lists services from all platforms -func (c *Controller) Services() []*model.Service { - // smap is a map of hostname (string) to service index, used to identify services that - // are installed in multiple clusters. - smap := make(map[host.Name]int) - index := 0 - services := make([]*model.Service, 0) - // Locking Registries list while walking it to prevent inconsistent results - for _, r := range c.GetRegistries() { - svcs := r.Services() - if r.Provider() != provider.Kubernetes { - index += len(svcs) - services = append(services, svcs...) - } else { - for _, s := range svcs { - previous, ok := smap[s.Hostname] - if !ok { - // First time we see a service. The result will have a single service per hostname - // The first cluster will be listed first, so the services in the primary cluster - // will be used for default settings. If a service appears in multiple clusters, - // the order is less clear. - smap[s.Hostname] = index - index++ - services = append(services, s) - } else { - // We must deepcopy before merge, and after merging, the ClusterVips length will be >= 2. - // This is an optimization to prevent deepcopy multi-times - if len(services[previous].ClusterVIPs.GetAddresses()) < 2 { - // Deep copy before merging, otherwise there is a case - // a service in remote cluster can be deleted, but the ClusterIP left. - services[previous] = services[previous].DeepCopy() - } - // If it is seen second time, that means it is from a different cluster, update cluster VIPs. - mergeService(services[previous], s, r) - } - } - } - } - return services -} - -// GetService retrieves a service by hostname if exists -func (c *Controller) GetService(hostname host.Name) *model.Service { - var out *model.Service - for _, r := range c.GetRegistries() { - service := r.GetService(hostname) - if service == nil { - continue - } - if r.Provider() != provider.Kubernetes { - return service - } - if out == nil { - out = service.DeepCopy() - } else { - // If we are seeing the service for the second time, it means it is available in multiple clusters. - mergeService(out, service, r) - } - } - return out -} - -func mergeService(dst, src *model.Service, srcRegistry serviceregistry.Instance) { - // Prefer the k8s HostVIPs where possible - clusterID := srcRegistry.Cluster() - if srcRegistry.Provider() == provider.Kubernetes || len(dst.ClusterVIPs.GetAddressesFor(clusterID)) == 0 { - newAddresses := src.ClusterVIPs.GetAddressesFor(clusterID) - dst.ClusterVIPs.SetAddressesFor(clusterID, newAddresses) - } -} - -// NetworkGateways merges the service-based cross-network gateways from each registry. -func (c *Controller) NetworkGateways() []model.NetworkGateway { - var gws []model.NetworkGateway - for _, r := range c.GetRegistries() { - gws = append(gws, r.NetworkGateways()...) - } - return gws -} - -func (c *Controller) MCSServices() []model.MCSServiceInfo { - var out []model.MCSServiceInfo - for _, r := range c.GetRegistries() { - out = append(out, r.MCSServices()...) - } - return out -} - -// InstancesByPort retrieves instances for a service on a given port that match -// any of the supplied labels. All instances match an empty label list. -func (c *Controller) InstancesByPort(svc *model.Service, port int, labels labels.Instance) []*model.ServiceInstance { - var instances []*model.ServiceInstance - for _, r := range c.GetRegistries() { - instances = append(instances, r.InstancesByPort(svc, port, labels)...) - } - return instances -} - -func nodeClusterID(node *model.Proxy) cluster.ID { - if node.Metadata == nil || node.Metadata.ClusterID == "" { - return "" - } - return node.Metadata.ClusterID -} - -// Skip the service registry when there won't be a match -// because the proxy is in a different cluster. -func skipSearchingRegistryForProxy(nodeClusterID cluster.ID, r serviceregistry.Instance) bool { - // Always search non-kube (usually serviceentry) registry. - // Check every registry if cluster ID isn't specified. - if r.Provider() != provider.Kubernetes || nodeClusterID == "" { - return false - } - - return !r.Cluster().Equals(nodeClusterID) -} - -// GetProxyServiceInstances lists service instances co-located with a given proxy -func (c *Controller) GetProxyServiceInstances(node *model.Proxy) []*model.ServiceInstance { - out := make([]*model.ServiceInstance, 0) - nodeClusterID := nodeClusterID(node) - for _, r := range c.GetRegistries() { - if skipSearchingRegistryForProxy(nodeClusterID, r) { - log.Debugf("GetProxyServiceInstances(): not searching registry %v: proxy %v CLUSTER_ID is %v", - r.Cluster(), node.ID, nodeClusterID) - continue - } - - instances := r.GetProxyServiceInstances(node) - if len(instances) > 0 { - out = append(out, instances...) - } - } - - return out -} - -func (c *Controller) GetProxyWorkloadLabels(proxy *model.Proxy) labels.Instance { - clusterID := nodeClusterID(proxy) - for _, r := range c.GetRegistries() { - // If proxy clusterID unset, we may find incorrect workload label. - // This can not happen in k8s env. - if clusterID == "" { - lbls := r.GetProxyWorkloadLabels(proxy) - if lbls != nil { - return lbls - } - } else if clusterID == r.Cluster() { - // find proxy in the specified cluster - lbls := r.GetProxyWorkloadLabels(proxy) - if lbls != nil { - return lbls - } - } - } - - return nil -} - -// Run starts all the controllers -func (c *Controller) Run(stop <-chan struct{}) { - c.storeLock.Lock() - for _, r := range c.registries { - // prefer the per-registry stop channel - registryStop := stop - if s := r.stop; s != nil { - registryStop = s - } - go r.Run(registryStop) - } - c.running = true - c.storeLock.Unlock() - - <-stop - log.Info("Registry Aggregator terminated") -} - -// HasSynced returns true when all registries have synced -func (c *Controller) HasSynced() bool { - for _, r := range c.GetRegistries() { - if !r.HasSynced() { - log.Debugf("registry %s is syncing", r.Cluster()) - return false - } - } - return true -} - -func (c *Controller) AppendServiceHandler(f func(*model.Service, model.Event)) { - c.handlers.AppendServiceHandler(f) -} - -func (c *Controller) AppendWorkloadHandler(f func(*model.WorkloadInstance, model.Event)) { - // Currently, it is not used. - // Note: take care when you want to enable it, it will register the handlers to all registries - // c.handlers.AppendWorkloadHandler(f) -} - -func (c *Controller) AppendServiceHandlerForCluster(id cluster.ID, f func(*model.Service, model.Event)) { - c.storeLock.Lock() - defer c.storeLock.Unlock() - handler, ok := c.handlersByCluster[id] - if !ok { - c.handlersByCluster[id] = &model.ControllerHandlers{} - handler = c.handlersByCluster[id] - } - handler.AppendServiceHandler(f) -} - -func (c *Controller) AppendWorkloadHandlerForCluster(id cluster.ID, f func(*model.WorkloadInstance, model.Event)) { - c.storeLock.Lock() - defer c.storeLock.Unlock() - handler, ok := c.handlersByCluster[id] - if !ok { - c.handlersByCluster[id] = &model.ControllerHandlers{} - handler = c.handlersByCluster[id] - } - handler.AppendWorkloadHandler(f) -} - -func (c *Controller) UnRegisterHandlersForCluster(id cluster.ID) { - c.storeLock.Lock() - defer c.storeLock.Unlock() - delete(c.handlersByCluster, id) -} - -// GetIstioServiceAccounts implements model.ServiceAccounts operation. -// The returned list contains all SPIFFE based identities that backs the service. -// This method also expand the results from different registries based on the mesh config trust domain aliases. -// To retain such trust domain expansion behavior, the xDS server implementation should wrap any (even if single) -// service registry by this aggreated one. -// For example, -// - { "spiffe://cluster.local/bar@iam.gserviceaccount.com"}; when annotation is used on corresponding workloads. -// - { "spiffe://cluster.local/ns/default/sa/foo" }; normal kubernetes cases -// - { "spiffe://cluster.local/ns/default/sa/foo", "spiffe://trust-domain-alias/ns/default/sa/foo" }; -// if the trust domain alias is configured. -func (c *Controller) GetIstioServiceAccounts(svc *model.Service, ports []int) []string { - out := map[string]struct{}{} - for _, r := range c.GetRegistries() { - svcAccounts := r.GetIstioServiceAccounts(svc, ports) - for _, sa := range svcAccounts { - out[sa] = struct{}{} - } - } - result := make([]string, 0, len(out)) - for k := range out { - result = append(result, k) - } - tds := make([]string, 0) - if c.meshHolder != nil { - m := c.meshHolder.Mesh() - if m != nil { - tds = m.TrustDomainAliases - } - } - expanded := spiffe.ExpandWithTrustDomains(result, tds) - result = make([]string, 0, len(expanded)) - for k := range expanded { - result = append(result, k) - } - // Sort to make the return result deterministic. - sort.Strings(result) - return result -} diff --git a/pilot/pkg/serviceregistry/aggregate/controller_test.go b/pilot/pkg/serviceregistry/aggregate/controller_test.go deleted file mode 100644 index 77887013e..000000000 --- a/pilot/pkg/serviceregistry/aggregate/controller_test.go +++ /dev/null @@ -1,568 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package aggregate - -import ( - "fmt" - "reflect" - "testing" - "time" -) - -import ( - "github.com/google/go-cmp/cmp" - "go.uber.org/atomic" - meshconfig "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/mock" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -type mockMeshConfigHolder struct { - trustDomainAliases []string -} - -func (mh mockMeshConfigHolder) Mesh() *meshconfig.MeshConfig { - return &meshconfig.MeshConfig{ - TrustDomainAliases: mh.trustDomainAliases, - } -} - -func buildMockController() *Controller { - discovery1 := memory.NewServiceDiscovery(mock.ReplicatedFooServiceV1.DeepCopy(), - mock.HelloService.DeepCopy(), - mock.ExtHTTPService.DeepCopy(), - ) - for _, port := range mock.HelloService.Ports { - discovery1.AddInstance(mock.HelloService.Hostname, mock.MakeServiceInstance(mock.HelloService, port, 0, model.Locality{})) - discovery1.AddInstance(mock.HelloService.Hostname, mock.MakeServiceInstance(mock.HelloService, port, 1, model.Locality{})) - } - - discovery2 := memory.NewServiceDiscovery(mock.ReplicatedFooServiceV2.DeepCopy(), - mock.WorldService.DeepCopy(), - mock.ExtHTTPSService.DeepCopy(), - ) - for _, port := range mock.WorldService.Ports { - discovery2.AddInstance(mock.WorldService.Hostname, mock.MakeServiceInstance(mock.WorldService, port, 0, model.Locality{})) - discovery2.AddInstance(mock.WorldService.Hostname, mock.MakeServiceInstance(mock.WorldService, port, 1, model.Locality{})) - } - registry1 := serviceregistry.Simple{ - ProviderID: provider.ID("mockAdapter1"), - ServiceDiscovery: discovery1, - Controller: &mock.Controller{}, - } - - registry2 := serviceregistry.Simple{ - ProviderID: provider.ID("mockAdapter2"), - ServiceDiscovery: discovery2, - Controller: &mock.Controller{}, - } - - ctls := NewController(Options{&mockMeshConfigHolder{}}) - ctls.AddRegistry(registry1) - ctls.AddRegistry(registry2) - - return ctls -} - -// return aggregator and cluster1 and cluster2 service discovery -func buildMockControllerForMultiCluster() (*Controller, *memory.ServiceDiscovery, *memory.ServiceDiscovery) { - discovery1 := memory.NewServiceDiscovery(mock.HelloService) - - discovery2 := memory.NewServiceDiscovery(mock.MakeService(mock.ServiceArgs{ - Hostname: mock.HelloService.Hostname, - Address: "10.1.2.0", - ServiceAccounts: []string{}, - ClusterID: "cluster-2", - }), mock.WorldService) - - registry1 := serviceregistry.Simple{ - ProviderID: provider.Kubernetes, - ClusterID: "cluster-1", - ServiceDiscovery: discovery1, - Controller: &mock.Controller{}, - } - - registry2 := serviceregistry.Simple{ - ProviderID: provider.Kubernetes, - ClusterID: "cluster-2", - ServiceDiscovery: discovery2, - Controller: &mock.Controller{}, - } - - ctls := NewController(Options{}) - ctls.AddRegistry(registry1) - ctls.AddRegistry(registry2) - - return ctls, discovery1, discovery2 -} - -func TestServicesForMultiCluster(t *testing.T) { - originalHelloService := mock.HelloService.DeepCopy() - aggregateCtl, _, registry2 := buildMockControllerForMultiCluster() - // List Services from aggregate controller - services := aggregateCtl.Services() - - // Set up ground truth hostname values - hosts := map[host.Name]bool{ - mock.HelloService.Hostname: false, - mock.WorldService.Hostname: false, - } - - count := 0 - // Compare return value to ground truth - for _, svc := range services { - if counted, existed := hosts[svc.Hostname]; existed && !counted { - count++ - hosts[svc.Hostname] = true - } - } - - if count != len(hosts) { - t.Fatalf("Cluster local service map expected size %d, actual %v vs %v", count, hosts, services) - } - - // Now verify ClusterVIPs for each service - ClusterVIPs := map[host.Name]map[cluster.ID][]string{ - mock.HelloService.Hostname: { - "cluster-1": []string{"10.1.0.0"}, - "cluster-2": []string{"10.1.2.0"}, - }, - mock.WorldService.Hostname: { - "cluster-2": []string{"10.2.0.0"}, - }, - } - for _, svc := range services { - if !reflect.DeepEqual(svc.ClusterVIPs.Addresses, ClusterVIPs[svc.Hostname]) { - t.Fatalf("Service %s ClusterVIPs actual %v, expected %v", svc.Hostname, - svc.ClusterVIPs.Addresses, ClusterVIPs[svc.Hostname]) - } - } - - registry2.RemoveService(mock.HelloService.Hostname) - // List Services from aggregate controller - services = aggregateCtl.Services() - // Now verify ClusterVIPs for each service - ClusterVIPs = map[host.Name]map[cluster.ID][]string{ - mock.HelloService.Hostname: { - "cluster-1": []string{"10.1.0.0"}, - }, - mock.WorldService.Hostname: { - "cluster-2": []string{"10.2.0.0"}, - }, - } - for _, svc := range services { - if !reflect.DeepEqual(svc.ClusterVIPs.Addresses, ClusterVIPs[svc.Hostname]) { - t.Fatalf("Service %s ClusterVIPs actual %v, expected %v", svc.Hostname, - svc.ClusterVIPs.Addresses, ClusterVIPs[svc.Hostname]) - } - } - - // check HelloService is not mutated - if !reflect.DeepEqual(originalHelloService, mock.HelloService) { - t.Errorf("Original hello service is mutated") - } -} - -func TestServices(t *testing.T) { - aggregateCtl := buildMockController() - // List Services from aggregate controller - services := aggregateCtl.Services() - - // Set up ground truth hostname values - serviceMap := map[host.Name]bool{ - mock.HelloService.Hostname: false, - mock.ExtHTTPService.Hostname: false, - mock.WorldService.Hostname: false, - mock.ExtHTTPSService.Hostname: false, - } - - svcCount := 0 - // Compare return value to ground truth - for _, svc := range services { - if counted, existed := serviceMap[svc.Hostname]; existed && !counted { - svcCount++ - serviceMap[svc.Hostname] = true - } - } - - if svcCount != len(serviceMap) { - t.Fatal("Return services does not match ground truth") - } -} - -func TestGetService(t *testing.T) { - aggregateCtl := buildMockController() - - // Get service from mockAdapter1 - svc := aggregateCtl.GetService(mock.HelloService.Hostname) - if svc == nil { - t.Fatal("Fail to get service") - } - if svc.Hostname != mock.HelloService.Hostname { - t.Fatal("Returned service is incorrect") - } - - // Get service from mockAdapter2 - svc = aggregateCtl.GetService(mock.WorldService.Hostname) - if svc == nil { - t.Fatal("Fail to get service") - } - if svc.Hostname != mock.WorldService.Hostname { - t.Fatal("Returned service is incorrect") - } -} - -func TestGetProxyServiceInstances(t *testing.T) { - aggregateCtl := buildMockController() - - // Get Instances from mockAdapter1 - instances := aggregateCtl.GetProxyServiceInstances(&model.Proxy{IPAddresses: []string{mock.HelloInstanceV0}}) - if len(instances) != 6 { - t.Fatalf("Returned GetProxyServiceInstances' amount %d is not correct", len(instances)) - } - for _, inst := range instances { - if inst.Service.Hostname != mock.HelloService.Hostname { - t.Fatal("Returned Instance is incorrect") - } - } - - // Get Instances from mockAdapter2 - instances = aggregateCtl.GetProxyServiceInstances(&model.Proxy{IPAddresses: []string{mock.MakeIP(mock.WorldService, 1)}}) - if len(instances) != 6 { - t.Fatalf("Returned GetProxyServiceInstances' amount %d is not correct", len(instances)) - } - for _, inst := range instances { - if inst.Service.Hostname != mock.WorldService.Hostname { - t.Fatal("Returned Instance is incorrect") - } - } -} - -func TestGetProxyWorkloadLabels(t *testing.T) { - // If no registries return workload labels, we must return nil, rather than an empty list. - // This ensures callers can distinguish between no labels, and labels not found. - aggregateCtl := buildMockController() - - instances := aggregateCtl.GetProxyWorkloadLabels(&model.Proxy{IPAddresses: []string{mock.HelloInstanceV0}}) - if instances != nil { - t.Fatalf("expected nil workload labels, got: %v", instances) - } -} - -func TestInstances(t *testing.T) { - aggregateCtl := buildMockController() - - // Get Instances from mockAdapter1 - instances := aggregateCtl.InstancesByPort(mock.HelloService, 80, nil) - if len(instances) != 2 { - t.Fatal("Returned wrong number of instances from controller") - } - for _, instance := range instances { - if instance.Service.Hostname != mock.HelloService.Hostname { - t.Fatal("Returned instance's hostname does not match desired value") - } - if _, ok := instance.Service.Ports.Get(mock.PortHTTPName); !ok { - t.Fatal("Returned instance does not contain desired port") - } - } - - // Get Instances from mockAdapter2 - instances = aggregateCtl.InstancesByPort(mock.WorldService, 80, nil) - if len(instances) != 2 { - t.Fatal("Returned wrong number of instances from controller") - } - for _, instance := range instances { - if instance.Service.Hostname != mock.WorldService.Hostname { - t.Fatal("Returned instance's hostname does not match desired value") - } - if _, ok := instance.Service.Ports.Get(mock.PortHTTPName); !ok { - t.Fatal("Returned instance does not contain desired port") - } - } -} - -func TestGetIstioServiceAccounts(t *testing.T) { - aggregateCtl := buildMockController() - testCases := []struct { - name string - svc *model.Service - trustDomainAliases []string - want []string - }{ - { - name: "HelloEmpty", - svc: mock.HelloService, - want: []string{}, - }, - { - name: "World", - svc: mock.WorldService, - want: []string{ - "spiffe://cluster.local/ns/default/sa/world1", - "spiffe://cluster.local/ns/default/sa/world2", - }, - }, - { - name: "ReplicatedFoo", - svc: mock.ReplicatedFooServiceV1, - want: []string{ - "spiffe://cluster.local/ns/default/sa/foo-share", - "spiffe://cluster.local/ns/default/sa/foo1", - "spiffe://cluster.local/ns/default/sa/foo2", - }, - }, - { - name: "ExpansionByTrustDomainAliases", - trustDomainAliases: []string{"cluster.local", "example.com"}, - svc: mock.WorldService, - want: []string{ - "spiffe://cluster.local/ns/default/sa/world1", - "spiffe://cluster.local/ns/default/sa/world2", - "spiffe://example.com/ns/default/sa/world1", - "spiffe://example.com/ns/default/sa/world2", - }, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - aggregateCtl.meshHolder = &mockMeshConfigHolder{trustDomainAliases: tc.trustDomainAliases} - accounts := aggregateCtl.GetIstioServiceAccounts(tc.svc, []int{}) - if diff := cmp.Diff(accounts, tc.want); diff != "" { - t.Errorf("unexpected service account, diff %v, %v", diff, accounts) - } - }) - } -} - -func TestAddRegistry(t *testing.T) { - registries := []serviceregistry.Simple{ - { - ProviderID: "registry1", - ClusterID: "cluster1", - Controller: &mock.Controller{}, - ServiceDiscovery: memory.NewServiceDiscovery(), - }, - { - ProviderID: "registry2", - ClusterID: "cluster2", - Controller: &mock.Controller{}, - ServiceDiscovery: memory.NewServiceDiscovery(), - }, - } - ctrl := NewController(Options{}) - - registry1Counter := atomic.NewInt32(0) - registry2Counter := atomic.NewInt32(0) - - for _, r := range registries { - clusterID := r.Cluster() - counter := registry1Counter - if clusterID == "cluster2" { - counter = registry2Counter - } - ctrl.AppendServiceHandlerForCluster(clusterID, func(service *model.Service, event model.Event) { - counter.Add(1) - }) - ctrl.AddRegistry(r) - } - if l := len(ctrl.registries); l != 2 { - t.Fatalf("Expected length of the registries slice should be 2, got %d", l) - } - - registries[0].Controller.(*mock.Controller).OnServiceEvent(mock.HelloService, model.EventAdd) - registries[1].Controller.(*mock.Controller).OnServiceEvent(mock.WorldService, model.EventAdd) - - ctrl.DeleteRegistry(registries[1].Cluster(), registries[1].Provider()) - ctrl.UnRegisterHandlersForCluster(registries[1].Cluster()) - registries[0].Controller.(*mock.Controller).OnServiceEvent(mock.HelloService, model.EventAdd) - - if registry1Counter.Load() != 3 { - t.Errorf("cluster1 expected 3 event, but got %d", registry1Counter.Load()) - } - if registry2Counter.Load() != 2 { - t.Errorf("cluster2 expected 2 event, but got %d", registry2Counter.Load()) - } -} - -func TestGetDeleteRegistry(t *testing.T) { - registries := []serviceregistry.Simple{ - { - ProviderID: "registry1", - ClusterID: "cluster1", - Controller: &mock.Controller{}, - ServiceDiscovery: memory.NewServiceDiscovery(), - }, - { - ProviderID: "registry2", - ClusterID: "cluster2", - Controller: &mock.Controller{}, - ServiceDiscovery: memory.NewServiceDiscovery(), - }, - { - ProviderID: "registry3", - ClusterID: "cluster3", - Controller: &mock.Controller{}, - ServiceDiscovery: memory.NewServiceDiscovery(), - }, - } - wrapRegistry := func(r serviceregistry.Instance) serviceregistry.Instance { - return ®istryEntry{Instance: r} - } - - ctrl := NewController(Options{}) - for _, r := range registries { - ctrl.AddRegistry(r) - } - - // Test Get - result := ctrl.GetRegistries() - if l := len(result); l != 3 { - t.Fatalf("Expected length of the registries slice should be 3, got %d", l) - } - - // Test Delete cluster2 - ctrl.DeleteRegistry(registries[1].ClusterID, registries[1].ProviderID) - result = ctrl.GetRegistries() - if l := len(result); l != 2 { - t.Fatalf("Expected length of the registries slice should be 2, got %d", l) - } - // check left registries are orders as before - if !reflect.DeepEqual(result[0], wrapRegistry(registries[0])) || !reflect.DeepEqual(result[1], wrapRegistry(registries[2])) { - t.Fatalf("Expected registries order has been changed") - } -} - -func TestSkipSearchingRegistryForProxy(t *testing.T) { - cluster1 := serviceregistry.Simple{ - ClusterID: "cluster-1", - ProviderID: provider.Kubernetes, - Controller: &mock.Controller{}, - ServiceDiscovery: memory.NewServiceDiscovery(), - } - cluster2 := serviceregistry.Simple{ - ClusterID: "cluster-2", - ProviderID: provider.Kubernetes, - Controller: &mock.Controller{}, - ServiceDiscovery: memory.NewServiceDiscovery(), - } - // external registries may eventually be associated with a cluster - external := serviceregistry.Simple{ - ClusterID: "cluster-1", - ProviderID: provider.External, - Controller: &mock.Controller{}, - ServiceDiscovery: memory.NewServiceDiscovery(), - } - - cases := []struct { - nodeClusterID cluster.ID - registry serviceregistry.Instance - want bool - }{ - // matching kube registry - {"cluster-1", cluster1, false}, - // unmatching kube registry - {"cluster-1", cluster2, true}, - // always search external - {"cluster-1", external, false}, - {"cluster-2", external, false}, - {"", external, false}, - // always search for empty node cluster id - {"", cluster1, false}, - {"", cluster2, false}, - {"", external, false}, - } - - for i, c := range cases { - got := skipSearchingRegistryForProxy(c.nodeClusterID, c.registry) - if got != c.want { - t.Errorf("%s: got %v want %v", - fmt.Sprintf("[%v] registry=%v node=%v", i, c.registry, c.nodeClusterID), - got, c.want) - } - } -} - -func runnableRegistry(name string) *RunnableRegistry { - return &RunnableRegistry{ - Instance: serviceregistry.Simple{ - ClusterID: cluster.ID(name), ProviderID: "test", - Controller: &mock.Controller{}, - ServiceDiscovery: memory.NewServiceDiscovery(), - }, - running: atomic.NewBool(false), - } -} - -type RunnableRegistry struct { - serviceregistry.Instance - running *atomic.Bool -} - -func (rr *RunnableRegistry) Run(stop <-chan struct{}) { - if rr.running.Load() { - panic("--- registry has been run twice ---") - } - rr.running.Store(true) - <-stop -} - -func expectRunningOrFail(t *testing.T, ctrl *Controller, want bool) { - // running gets flipped in a goroutine, retry to avoid race - retry.UntilSuccessOrFail(t, func() error { - for _, registry := range ctrl.registries { - if running := registry.Instance.(*RunnableRegistry).running.Load(); running != want { - return fmt.Errorf("%s running is %v but wanted %v", registry.Cluster(), running, want) - } - } - return nil - }, retry.Timeout(50*time.Millisecond), retry.Delay(0)) -} - -func TestDeferredRun(t *testing.T) { - stop := make(chan struct{}) - defer close(stop) - ctrl := NewController(Options{}) - - t.Run("AddRegistry before aggregate Run does not run", func(t *testing.T) { - ctrl.AddRegistry(runnableRegistry("earlyAdd")) - ctrl.AddRegistryAndRun(runnableRegistry("earlyAddAndRun"), nil) - expectRunningOrFail(t, ctrl, false) - }) - t.Run("aggregate Run starts all registries", func(t *testing.T) { - go ctrl.Run(stop) - expectRunningOrFail(t, ctrl, true) - ctrl.DeleteRegistry("earlyAdd", "test") - ctrl.DeleteRegistry("earlyAddAndRun", "test") - }) - t.Run("AddRegistry after aggregate Run does not start registry", func(t *testing.T) { - ctrl.AddRegistry(runnableRegistry("missed")) - expectRunningOrFail(t, ctrl, false) - ctrl.DeleteRegistry("missed", "test") - expectRunningOrFail(t, ctrl, true) - }) - t.Run("AddRegistryAndRun after aggregate Run starts registry", func(t *testing.T) { - ctrl.AddRegistryAndRun(runnableRegistry("late"), nil) - expectRunningOrFail(t, ctrl, true) - }) -} diff --git a/pilot/pkg/serviceregistry/instance.go b/pilot/pkg/serviceregistry/instance.go deleted file mode 100644 index 5c35e6626..000000000 --- a/pilot/pkg/serviceregistry/instance.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package serviceregistry - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" -) - -// Instance of a service registry. A single service registry combines the capabilities of service discovery -// and the controller for managing asynchronous events. -type Instance interface { - model.Controller - model.ServiceDiscovery - - // Provider backing this service registry (i.e. Kubernetes etc.) - Provider() provider.ID - - // Cluster for which the service registry applies. Only needed for multicluster systems. - Cluster() cluster.ID -} - -var _ Instance = &Simple{} - -// Simple Instance implementation, where fields are set individually. -type Simple struct { - ProviderID provider.ID - ClusterID cluster.ID - - model.Controller - model.ServiceDiscovery -} - -func (r Simple) Provider() provider.ID { - return r.ProviderID -} - -func (r Simple) Cluster() cluster.ID { - return r.ClusterID -} diff --git a/pilot/pkg/serviceregistry/kube/controller/autoserviceexportcontroller.go b/pilot/pkg/serviceregistry/kube/controller/autoserviceexportcontroller.go deleted file mode 100644 index 175b1b99c..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/autoserviceexportcontroller.go +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "context" - "time" -) - -import ( - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/tools/cache" - mcsapi "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - serviceRegistryKube "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/mcs" - "github.com/apache/dubbo-go-pixiu/pkg/queue" -) - -type autoServiceExportController struct { - autoServiceExportOptions - - client kube.Client - queue queue.Instance - serviceInformer cache.SharedInformer - - // We use this flag to short-circuit the logic and stop the controller - // if the CRD does not exist (or is deleted) - mcsSupported bool -} - -// autoServiceExportOptions provide options for creating a autoServiceExportController. -type autoServiceExportOptions struct { - Client kube.Client - ClusterID cluster.ID - DomainSuffix string - ClusterLocal model.ClusterLocalProvider -} - -// newAutoServiceExportController creates a new autoServiceExportController. -func newAutoServiceExportController(opts autoServiceExportOptions) *autoServiceExportController { - c := &autoServiceExportController{ - autoServiceExportOptions: opts, - client: opts.Client, - queue: queue.NewQueue(time.Second), - mcsSupported: true, - } - - log.Infof("%s starting controller", c.logPrefix()) - - c.serviceInformer = opts.Client.KubeInformer().Core().V1().Services().Informer() - c.serviceInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { c.onServiceAdd(obj) }, - - // Do nothing on update. The controller only acts on parts of the service - // that are immutable (e.g. name). - - // Do nothing on delete. When we create ServiceExport, we bind its - // lifecycle to the Service so that when the Service is deleted, - // k8s automatically deletes the ServiceExport. - }) - - return c -} - -func (c *autoServiceExportController) onServiceAdd(obj interface{}) { - c.queue.Push(func() error { - if !c.mcsSupported { - // Don't create ServiceExport if MCS is not supported on the cluster. - log.Debugf("%s ignoring added Service, since !mcsSupported", c.logPrefix()) - return nil - } - - svc, err := convertToService(obj) - if err != nil { - log.Warnf("%s failed converting service: %v", c.logPrefix(), err) - return err - } - - if c.isClusterLocalService(svc) { - // Don't create ServiceExport if the service is configured to be - // local to the cluster (i.e. non-exported). - log.Debugf("%s ignoring cluster-local service %s/%s", c.logPrefix(), svc.Namespace, svc.Name) - return nil - } - - return c.createServiceExportIfNotPresent(svc) - }) -} - -func (c *autoServiceExportController) Run(stopCh <-chan struct{}) { - if !cache.WaitForCacheSync(stopCh, c.serviceInformer.HasSynced) { - log.Errorf("%s failed to sync cache", c.logPrefix()) - return - } - log.Infof("%s started", c.logPrefix()) - go c.queue.Run(stopCh) -} - -func (c *autoServiceExportController) logPrefix() string { - return "AutoServiceExport (cluster=" + c.ClusterID.String() + ") " -} - -func (c *autoServiceExportController) createServiceExportIfNotPresent(svc *v1.Service) error { - serviceExport := mcsapi.ServiceExport{ - TypeMeta: metav1.TypeMeta{ - Kind: "ServiceExport", - APIVersion: mcs.MCSSchemeGroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: svc.Namespace, - Name: svc.Name, - - // Bind the lifecycle of the ServiceExport to the Service. We do this by making the Service - // the "owner" of the ServiceExport resource. - OwnerReferences: []metav1.OwnerReference{ - { - APIVersion: v1.SchemeGroupVersion.String(), - Kind: "Service", - Name: svc.Name, - UID: svc.UID, - }, - }, - }, - } - - // Convert to unstructured. - u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&serviceExport) - if err != nil { - log.Warnf("%s failed converting ServiceExport %s/%s to Unstructured: %v", c.logPrefix(), - svc.Namespace, svc.Name, err) - return err - } - - if _, err = c.client.Dynamic().Resource(mcs.ServiceExportGVR).Namespace(serviceExport.Namespace).Create( - context.TODO(), &unstructured.Unstructured{Object: u}, metav1.CreateOptions{}); err != nil { - switch { - case errors.IsAlreadyExists(err): - // The ServiceExport already exists. Nothing to do. - return nil - case errors.IsNotFound(err): - log.Warnf("%s ServiceExport CRD Not found. Shutting down MCS ServiceExport sync. "+ - "Please add the CRD then restart the istiod deployment", c.logPrefix()) - c.mcsSupported = false - - // Do not return the error, so that the queue does not attempt a retry. - return nil - } - } - - if err != nil { - log.Warnf("%s failed creating ServiceExport %s/%s: %v", c.logPrefix(), svc.Namespace, svc.Name, err) - return err - } - - log.Debugf("%s created ServiceExport %s/%s", c.logPrefix(), svc.Namespace, svc.Name) - return nil -} - -func (c *autoServiceExportController) isClusterLocalService(svc *v1.Service) bool { - hostname := serviceRegistryKube.ServiceHostname(svc.Name, svc.Namespace, c.DomainSuffix) - return c.ClusterLocal.GetClusterLocalHosts().IsClusterLocal(hostname) -} diff --git a/pilot/pkg/serviceregistry/kube/controller/autoserviceexportcontroller_test.go b/pilot/pkg/serviceregistry/kube/controller/autoserviceexportcontroller_test.go deleted file mode 100644 index f59ebac49..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/autoserviceexportcontroller_test.go +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "context" - "fmt" - "strings" - "testing" - "time" -) - -import ( - meshconfig "istio.io/api/mesh/v1alpha1" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes" - mcsapi "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/mcs" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -var serviceExportTimeout = retry.Timeout(time.Second * 2) - -func TestServiceExportController(t *testing.T) { - client := kube.NewFakeClient() - - // Configure the environment with cluster-local hosts. - m := meshconfig.MeshConfig{ - ServiceSettings: []*meshconfig.MeshConfig_ServiceSettings{ - { - Settings: &meshconfig.MeshConfig_ServiceSettings_Settings{ - ClusterLocal: true, - }, - Hosts: []string{"*.unexportable-ns.svc.cluster.local", "unexportable-svc.*.svc.cluster.local"}, - }, - }, - } - env := model.Environment{Watcher: mesh.NewFixedWatcher(&m)} - env.Init() - - sc := newAutoServiceExportController(autoServiceExportOptions{ - Client: client, - ClusterID: "", - DomainSuffix: env.DomainSuffix, - ClusterLocal: env.ClusterLocal(), - }) - - stop := make(chan struct{}) - t.Cleanup(func() { - close(stop) - }) - client.RunAndWait(stop) - sc.Run(stop) - - t.Run("exportable", func(t *testing.T) { - createSimpleService(t, client, "exportable-ns", "foo") - assertServiceExport(t, client, "exportable-ns", "foo", true) - }) - - t.Run("unexportable", func(t *testing.T) { - createSimpleService(t, client, "unexportable-ns", "foo") - assertServiceExport(t, client, "unexportable-ns", "foo", false) - }) - - t.Run("no overwrite", func(t *testing.T) { - // manually create serviceexport - export := mcsapi.ServiceExport{ - TypeMeta: metav1.TypeMeta{ - Kind: "ServiceExport", - APIVersion: features.MCSAPIVersion, - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "exportable-ns", - Name: "manual-export", - }, - Status: mcsapi.ServiceExportStatus{ - Conditions: []mcsapi.ServiceExportCondition{ - { - Type: mcsapi.ServiceExportValid, - }, - }, - }, - } - - _, err := client.Dynamic().Resource(mcs.ServiceExportGVR).Namespace("exportable-ns").Create( - context.TODO(), toUnstructured(&export), metav1.CreateOptions{}) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - - // create the associated service - // no need for assertions, just trying to ensure no errors - createSimpleService(t, client, "exportable-ns", "manual-export") - - // assert that we didn't wipe out the pre-existing serviceexport status - assertServiceExportHasCondition(t, client, "exportable-ns", "manual-export", - mcsapi.ServiceExportValid) - }) -} - -func createSimpleService(t *testing.T, client kubernetes.Interface, ns string, name string) { - t.Helper() - if _, err := client.CoreV1().Services(ns).Create(context.TODO(), &v1.Service{ - ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: ns}, - }, metav1.CreateOptions{}); err != nil { - t.Fatal(err) - } -} - -func assertServiceExport(t *testing.T, client kube.Client, ns, name string, shouldBePresent bool) { - t.Helper() - retry.UntilSuccessOrFail(t, func() error { - got, err := client.Dynamic().Resource(mcs.ServiceExportGVR).Namespace(ns).Get(context.TODO(), name, metav1.GetOptions{}) - - if err != nil && !strings.Contains(err.Error(), "not found") { - return fmt.Errorf("unexpected error %v", err) - } - isPresent := got != nil - if isPresent != shouldBePresent { - return fmt.Errorf("unexpected serviceexport state. IsPresent: %v, ShouldBePresent: %v, name: %v, namespace: %v", isPresent, shouldBePresent, name, ns) - } - return nil - }, serviceExportTimeout) -} - -func assertServiceExportHasCondition(t *testing.T, client kube.Client, ns, name string, condition mcsapi.ServiceExportConditionType) { - t.Helper() - retry.UntilSuccessOrFail(t, func() error { - gotU, err := client.Dynamic().Resource(mcs.ServiceExportGVR).Namespace(ns).Get(context.TODO(), name, metav1.GetOptions{}) - if err != nil { - return fmt.Errorf("unexpected error %v", err) - } - - got := &mcsapi.ServiceExport{} - if err := runtime.DefaultUnstructuredConverter.FromUnstructured(gotU.Object, got); err != nil { - return err - } - - if got.Status.Conditions == nil || len(got.Status.Conditions) == 0 || got.Status.Conditions[0].Type != condition { - return fmt.Errorf("condition incorrect or not found") - } - - return nil - }, serviceExportTimeout) -} diff --git a/pilot/pkg/serviceregistry/kube/controller/controller.go b/pilot/pkg/serviceregistry/kube/controller/controller.go deleted file mode 100644 index a3c7a76bc..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/controller.go +++ /dev/null @@ -1,1386 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "fmt" - "net" - "sort" - "sync" - "time" -) - -import ( - "github.com/hashicorp/go-multierror" - "go.uber.org/atomic" - "istio.io/api/label" - istiolog "istio.io/pkg/log" - "istio.io/pkg/monitoring" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - listerv1 "k8s.io/client-go/listers/core/v1" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/aggregate" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube/controller/filter" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/util/workloadinstances" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/util/informermetric" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - kubelib "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/network" - "github.com/apache/dubbo-go-pixiu/pkg/queue" -) - -const ( - // NodeRegionLabel is the well-known label for kubernetes node region in beta - NodeRegionLabel = v1.LabelFailureDomainBetaRegion - // NodeZoneLabel is the well-known label for kubernetes node zone in beta - NodeZoneLabel = v1.LabelFailureDomainBetaZone - // NodeRegionLabelGA is the well-known label for kubernetes node region in ga - NodeRegionLabelGA = v1.LabelTopologyRegion - // NodeZoneLabelGA is the well-known label for kubernetes node zone in ga - NodeZoneLabelGA = v1.LabelTopologyZone - - // DefaultNetworkGatewayPort is the port used by default for cross-network traffic if not otherwise specified - // by meshNetworks or "networking.istio.io/gatewayPort" - DefaultNetworkGatewayPort = 15443 -) - -var log = istiolog.RegisterScope("kube", "kubernetes service registry controller", 0) - -var ( - typeTag = monitoring.MustCreateLabel("type") - eventTag = monitoring.MustCreateLabel("event") - - k8sEvents = monitoring.NewSum( - "pilot_k8s_reg_events", - "Events from k8s registry.", - monitoring.WithLabels(typeTag, eventTag), - ) - - // nolint: gocritic - // This is deprecated in favor of `pilot_k8s_endpoints_pending_pod`, which is a gauge indicating the number of - // currently missing pods. This helps distinguish transient errors from permanent ones - endpointsWithNoPods = monitoring.NewSum( - "pilot_k8s_endpoints_with_no_pods", - "Endpoints that does not have any corresponding pods.") - - endpointsPendingPodUpdate = monitoring.NewGauge( - "pilot_k8s_endpoints_pending_pod", - "Number of endpoints that do not currently have any corresponding pods.", - ) -) - -func init() { - monitoring.MustRegister(k8sEvents) - monitoring.MustRegister(endpointsWithNoPods) - monitoring.MustRegister(endpointsPendingPodUpdate) -} - -func incrementEvent(kind, event string) { - k8sEvents.With(typeTag.Value(kind), eventTag.Value(event)).Increment() -} - -// Options stores the configurable attributes of a Controller. -type Options struct { - SystemNamespace string - - // MeshServiceController is a mesh-wide service Controller. - MeshServiceController *aggregate.Controller - - DomainSuffix string - - // ClusterID identifies the remote cluster in a multicluster env. - ClusterID cluster.ID - - // ClusterAliases are aliase names for cluster. When a proxy connects with a cluster ID - // and if it has a different alias we should use that a cluster ID for proxy. - ClusterAliases map[string]string - - // Metrics for capturing node-based metrics. - Metrics model.Metrics - - // XDSUpdater will push changes to the xDS server. - XDSUpdater model.XDSUpdater - - // NetworksWatcher observes changes to the mesh networks config. - NetworksWatcher mesh.NetworksWatcher - - // MeshWatcher observes changes to the mesh config - MeshWatcher mesh.Watcher - - // EndpointMode decides what source to use to get endpoint information - EndpointMode EndpointMode - - // Maximum QPS when communicating with kubernetes API - KubernetesAPIQPS float32 - - // Maximum burst for throttle when communicating with the kubernetes API - KubernetesAPIBurst int - - // SyncTimeout, if set, causes HasSynced to be returned when marked true. - SyncTimeout *atomic.Bool - - // If meshConfig.DiscoverySelectors are specified, the DiscoveryNamespacesFilter tracks the namespaces this controller watches. - DiscoveryNamespacesFilter filter.DiscoveryNamespacesFilter -} - -// DetectEndpointMode determines whether to use Endpoints or EndpointSlice based on the -// feature flag and/or Kubernetes version -func DetectEndpointMode(kubeClient kubelib.Client) EndpointMode { - useEndpointslice, ok := features.EnableEndpointSliceController() - - // we have a client, and flag wasn't set explicitly, auto-detect - if kubeClient != nil && !ok && kubelib.IsAtLeastVersion(kubeClient, 21) { - useEndpointslice = true - } - - if useEndpointslice { - return EndpointSliceOnly - } - return EndpointsOnly -} - -// EndpointMode decides what source to use to get endpoint information -type EndpointMode int - -const ( - // EndpointsOnly type will use only Kubernetes Endpoints - EndpointsOnly EndpointMode = iota - - // EndpointSliceOnly type will use only Kubernetes EndpointSlices - EndpointSliceOnly - - // TODO: add other modes. Likely want a mode with Endpoints+EndpointSlices that are not controlled by - // Kubernetes Controller (e.g. made by user and not duplicated with Endpoints), or a mode with both that - // does deduping. Simply doing both won't work for now, since not all Kubernetes components support EndpointSlice. -) - -var EndpointModes = []EndpointMode{EndpointsOnly, EndpointSliceOnly} - -var EndpointModeNames = map[EndpointMode]string{ - EndpointsOnly: "EndpointsOnly", - EndpointSliceOnly: "EndpointSliceOnly", -} - -func (m EndpointMode) String() string { - return EndpointModeNames[m] -} - -// kubernetesNode represents a kubernetes node that is reachable externally -type kubernetesNode struct { - address string - labels labels.Instance -} - -// controllerInterface is a simplified interface for the Controller used for testing. -type controllerInterface interface { - getPodLocality(pod *v1.Pod) string - Network(endpointIP string, labels labels.Instance) network.ID - Cluster() cluster.ID -} - -var ( - _ controllerInterface = &Controller{} - _ serviceregistry.Instance = &Controller{} -) - -// Controller is a collection of synchronized resource watchers -// Caches are thread-safe -type Controller struct { - opts Options - - client kubelib.Client - - queue queue.Instance - - nsInformer cache.SharedIndexInformer - nsLister listerv1.NamespaceLister - - serviceInformer filter.FilteredSharedIndexInformer - serviceLister listerv1.ServiceLister - - endpoints kubeEndpointsController - - // Used to watch node accessible from remote cluster. - // In multi-cluster(shared control plane multi-networks) scenario, ingress gateway service can be of nodePort type. - // With this, we can populate mesh's gateway address with the node ips. - nodeInformer cache.SharedIndexInformer - nodeLister listerv1.NodeLister - - exports serviceExportCache - imports serviceImportCache - pods *PodCache - - handlers model.ControllerHandlers - - // This is only used for test - stop chan struct{} - - sync.RWMutex - // servicesMap stores hostname ==> service, it is used to reduce convertService calls. - servicesMap map[host.Name]*model.Service - // hostNamesForNamespacedName returns all possible hostnames for the given service name. - // If Kubernetes Multi-Cluster Services (MCS) is enabled, this will contain the regular - // hostname as well as the MCS hostname (clusterset.local). Otherwise, only the regular - // hostname will be returned. - hostNamesForNamespacedName func(name types.NamespacedName) []host.Name - // servicesForNamespacedName returns all services for the given service name. - // If Kubernetes Multi-Cluster Services (MCS) is enabled, this will contain the regular - // service as well as the MCS service (clusterset.local), if available. Otherwise, - // only the regular service will be returned. - servicesForNamespacedName func(name types.NamespacedName) []*model.Service - // nodeSelectorsForServices stores hostname => label selectors that can be used to - // refine the set of node port IPs for a service. - nodeSelectorsForServices map[host.Name]labels.Instance - // map of node name and its address+labels - this is the only thing we need from nodes - // for vm to k8s or cross cluster. When node port services select specific nodes by labels, - // we run through the label selectors here to pick only ones that we need. - // Only nodes with ExternalIP addresses are included in this map ! - nodeInfoMap map[string]kubernetesNode - // externalNameSvcInstanceMap stores hostname ==> instance, is used to store instances for ExternalName k8s services - externalNameSvcInstanceMap map[host.Name][]*model.ServiceInstance - // index over workload instances from workload entries - workloadInstancesIndex workloadinstances.Index - - multinetwork - // informerInit is set to true once the controller is running successfully. This ensures we do not - // return HasSynced=true before we are running - informerInit *atomic.Bool - // beginSync is set to true when calling SyncAll, it indicates the controller has began sync resources. - beginSync *atomic.Bool - // initialSync is set to true after performing an initial in-order processing of all objects. - initialSync *atomic.Bool -} - -// NewController creates a new Kubernetes controller -// Created by bootstrap and multicluster (see multicluster.Controller). -func NewController(kubeClient kubelib.Client, options Options) *Controller { - c := &Controller{ - opts: options, - client: kubeClient, - queue: queue.NewQueueWithID(1*time.Second, string(options.ClusterID)), - servicesMap: make(map[host.Name]*model.Service), - nodeSelectorsForServices: make(map[host.Name]labels.Instance), - nodeInfoMap: make(map[string]kubernetesNode), - externalNameSvcInstanceMap: make(map[host.Name][]*model.ServiceInstance), - workloadInstancesIndex: workloadinstances.NewIndex(), - informerInit: atomic.NewBool(false), - beginSync: atomic.NewBool(false), - initialSync: atomic.NewBool(false), - - multinetwork: initMultinetwork(), - } - - if features.EnableMCSHost { - c.hostNamesForNamespacedName = func(name types.NamespacedName) []host.Name { - return []host.Name{ - kube.ServiceHostname(name.Name, name.Namespace, c.opts.DomainSuffix), - serviceClusterSetLocalHostname(name), - } - } - c.servicesForNamespacedName = func(name types.NamespacedName) []*model.Service { - out := make([]*model.Service, 0, 2) - - c.RLock() - if svc := c.servicesMap[kube.ServiceHostname(name.Name, name.Namespace, c.opts.DomainSuffix)]; svc != nil { - out = append(out, svc) - } - - if svc := c.servicesMap[serviceClusterSetLocalHostname(name)]; svc != nil { - out = append(out, svc) - } - c.RUnlock() - - return out - } - } else { - c.hostNamesForNamespacedName = func(name types.NamespacedName) []host.Name { - return []host.Name{ - kube.ServiceHostname(name.Name, name.Namespace, c.opts.DomainSuffix), - } - } - c.servicesForNamespacedName = func(name types.NamespacedName) []*model.Service { - if svc := c.GetService(kube.ServiceHostname(name.Name, name.Namespace, c.opts.DomainSuffix)); svc != nil { - return []*model.Service{svc} - } - return nil - } - } - - c.nsInformer = kubeClient.KubeInformer().Core().V1().Namespaces().Informer() - c.nsLister = kubeClient.KubeInformer().Core().V1().Namespaces().Lister() - if c.opts.SystemNamespace != "" { - nsInformer := filter.NewFilteredSharedIndexInformer(func(obj interface{}) bool { - ns, ok := obj.(*v1.Namespace) - if !ok { - log.Warnf("Namespace watch getting wrong type in event: %T", obj) - return false - } - return ns.Name == c.opts.SystemNamespace - }, c.nsInformer) - c.registerHandlers(nsInformer, "Namespaces", c.onSystemNamespaceEvent, nil) - } - - if c.opts.DiscoveryNamespacesFilter == nil { - c.opts.DiscoveryNamespacesFilter = filter.NewDiscoveryNamespacesFilter(c.nsLister, options.MeshWatcher.Mesh().DiscoverySelectors) - } - - c.initDiscoveryHandlers(kubeClient, options.EndpointMode, options.MeshWatcher, c.opts.DiscoveryNamespacesFilter) - - c.serviceInformer = filter.NewFilteredSharedIndexInformer(c.opts.DiscoveryNamespacesFilter.Filter, kubeClient.KubeInformer().Core().V1().Services().Informer()) - c.serviceLister = listerv1.NewServiceLister(c.serviceInformer.GetIndexer()) - - c.registerHandlers(c.serviceInformer, "Services", c.onServiceEvent, nil) - - switch options.EndpointMode { - case EndpointsOnly: - c.endpoints = newEndpointsController(c) - case EndpointSliceOnly: - c.endpoints = newEndpointSliceController(c) - } - - // This is for getting the node IPs of a selected set of nodes - c.nodeInformer = kubeClient.KubeInformer().Core().V1().Nodes().Informer() - c.nodeLister = kubeClient.KubeInformer().Core().V1().Nodes().Lister() - c.registerHandlers(c.nodeInformer, "Nodes", c.onNodeEvent, nil) - - podInformer := filter.NewFilteredSharedIndexInformer(c.opts.DiscoveryNamespacesFilter.Filter, kubeClient.KubeInformer().Core().V1().Pods().Informer()) - c.pods = newPodCache(c, podInformer, func(key string) { - item, exists, err := c.endpoints.getInformer().GetIndexer().GetByKey(key) - if err != nil { - log.Debugf("Endpoint %v lookup failed with error %v, skipping stale endpoint", key, err) - return - } - if !exists { - log.Debugf("Endpoint %v not found, skipping stale endpoint", key) - return - } - if shouldEnqueue("Pods", c.beginSync) { - c.queue.Push(func() error { - return c.endpoints.onEvent(item, model.EventUpdate) - }) - } - }) - c.registerHandlers(c.pods.informer, "Pods", c.pods.onEvent, nil) - - c.exports = newServiceExportCache(c) - c.imports = newServiceImportCache(c) - - return c -} - -func (c *Controller) Provider() provider.ID { - return provider.Kubernetes -} - -func (c *Controller) Cluster() cluster.ID { - return c.opts.ClusterID -} - -func (c *Controller) MCSServices() []model.MCSServiceInfo { - outMap := make(map[types.NamespacedName]*model.MCSServiceInfo) - - // Add the ServiceExport info. - for _, se := range c.exports.ExportedServices() { - mcsService := outMap[se.namespacedName] - if mcsService == nil { - mcsService = &model.MCSServiceInfo{} - outMap[se.namespacedName] = mcsService - } - mcsService.Cluster = c.Cluster() - mcsService.Name = se.namespacedName.Name - mcsService.Namespace = se.namespacedName.Namespace - mcsService.Exported = true - mcsService.Discoverability = se.discoverability - } - - // Add the ServiceImport info. - for _, si := range c.imports.ImportedServices() { - mcsService := outMap[si.namespacedName] - if mcsService == nil { - mcsService = &model.MCSServiceInfo{} - outMap[si.namespacedName] = mcsService - } - mcsService.Cluster = c.Cluster() - mcsService.Name = si.namespacedName.Name - mcsService.Namespace = si.namespacedName.Namespace - mcsService.Imported = true - mcsService.ClusterSetVIP = si.clusterSetVIP - } - - out := make([]model.MCSServiceInfo, 0, len(outMap)) - for _, v := range outMap { - out = append(out, *v) - } - - return out -} - -func (c *Controller) networkFromMeshNetworks(endpointIP string) network.ID { - c.RLock() - defer c.RUnlock() - if c.networkForRegistry != "" { - return c.networkForRegistry - } - - if c.ranger != nil { - entries, err := c.ranger.ContainingNetworks(net.ParseIP(endpointIP)) - if err != nil { - log.Error(err) - return "" - } - if len(entries) > 1 { - log.Warnf("Found multiple networks CIDRs matching the endpoint IP: %s. Using the first match.", endpointIP) - } - if len(entries) > 0 { - return (entries[0].(namedRangerEntry)).name - } - } - return "" -} - -func (c *Controller) networkFromSystemNamespace() network.ID { - c.RLock() - defer c.RUnlock() - return c.network -} - -func (c *Controller) Network(endpointIP string, labels labels.Instance) network.ID { - // 1. check the pod/workloadEntry label - if nw := labels[label.TopologyNetwork.Name]; nw != "" { - return network.ID(nw) - } - - // 2. check the system namespace labels - if nw := c.networkFromSystemNamespace(); nw != "" { - return nw - } - - // 3. check the meshNetworks config - if nw := c.networkFromMeshNetworks(endpointIP); nw != "" { - return nw - } - - return "" -} - -func (c *Controller) Cleanup() error { - if err := queue.WaitForClose(c.queue, 30*time.Second); err != nil { - log.Warnf("queue for removed kube registry %q may not be done processing: %v", c.Cluster(), err) - } - if c.opts.XDSUpdater != nil { - c.opts.XDSUpdater.RemoveShard(model.ShardKeyFromRegistry(c)) - } - return nil -} - -func (c *Controller) onServiceEvent(curr interface{}, event model.Event) error { - svc, err := convertToService(curr) - if err != nil { - log.Errorf(err) - return nil - } - - log.Debugf("Handle event %s for service %s in namespace %s", event, svc.Name, svc.Namespace) - - // Create the standard (cluster.local) service. - svcConv := kube.ConvertService(*svc, c.opts.DomainSuffix, c.Cluster()) - switch event { - case model.EventDelete: - c.deleteService(svcConv) - default: - c.addOrUpdateService(svc, svcConv, event, false) - } - - return nil -} - -func (c *Controller) deleteService(svc *model.Service) { - c.Lock() - delete(c.servicesMap, svc.Hostname) - delete(c.nodeSelectorsForServices, svc.Hostname) - delete(c.externalNameSvcInstanceMap, svc.Hostname) - _, isNetworkGateway := c.networkGatewaysBySvc[svc.Hostname] - delete(c.networkGatewaysBySvc, svc.Hostname) - c.Unlock() - - if isNetworkGateway { - c.NotifyGatewayHandlers() - // TODO trigger push via handler - // networks are different, we need to update all eds endpoints - c.opts.XDSUpdater.ConfigUpdate(&model.PushRequest{Full: true, Reason: []model.TriggerReason{model.NetworksTrigger}}) - } - - shard := model.ShardKeyFromRegistry(c) - event := model.EventDelete - c.opts.XDSUpdater.SvcUpdate(shard, string(svc.Hostname), svc.Attributes.Namespace, event) - - c.handlers.NotifyServiceHandlers(svc, event) -} - -func (c *Controller) addOrUpdateService(svc *v1.Service, svcConv *model.Service, event model.Event, updateEDSCache bool) { - needsFullPush := false - // First, process nodePort gateway service, whose externalIPs specified - // and loadbalancer gateway service - if !svcConv.Attributes.ClusterExternalAddresses.IsEmpty() { - needsFullPush = c.extractGatewaysFromService(svcConv) - } else if isNodePortGatewayService(svc) { - // We need to know which services are using node selectors because during node events, - // we have to update all the node port services accordingly. - nodeSelector := getNodeSelectorsForService(svc) - c.Lock() - // only add when it is nodePort gateway service - c.nodeSelectorsForServices[svcConv.Hostname] = nodeSelector - c.Unlock() - needsFullPush = c.updateServiceNodePortAddresses(svcConv) - } - - // instance conversion is only required when service is added/updated. - instances := kube.ExternalNameServiceInstances(svc, svcConv) - c.Lock() - c.servicesMap[svcConv.Hostname] = svcConv - if len(instances) > 0 { - c.externalNameSvcInstanceMap[svcConv.Hostname] = instances - } - c.Unlock() - - if needsFullPush { - // networks are different, we need to update all eds endpoints - c.opts.XDSUpdater.ConfigUpdate(&model.PushRequest{Full: true, Reason: []model.TriggerReason{model.NetworksTrigger}}) - } - - shard := model.ShardKeyFromRegistry(c) - ns := svcConv.Attributes.Namespace - // We also need to update when the Service changes. For Kubernetes, a service change will result in Endpoint updates, - // but workload entries will also need to be updated. - // TODO(nmittler): Build different sets of endpoints for cluster.local and clusterset.local. - endpoints := c.buildEndpointsForService(svcConv, updateEDSCache) - if len(endpoints) > 0 { - c.opts.XDSUpdater.EDSCacheUpdate(shard, string(svcConv.Hostname), ns, endpoints) - } - - c.opts.XDSUpdater.SvcUpdate(shard, string(svcConv.Hostname), ns, event) - - c.handlers.NotifyServiceHandlers(svcConv, event) -} - -func (c *Controller) buildEndpointsForService(svc *model.Service, updateCache bool) []*model.IstioEndpoint { - endpoints := c.endpoints.buildIstioEndpointsWithService(svc.Attributes.Name, svc.Attributes.Namespace, svc.Hostname, updateCache) - fep := c.collectWorkloadInstanceEndpoints(svc) - endpoints = append(endpoints, fep...) - return endpoints -} - -func (c *Controller) onNodeEvent(obj interface{}, event model.Event) error { - node, ok := obj.(*v1.Node) - if !ok { - tombstone, ok := obj.(cache.DeletedFinalStateUnknown) - if !ok { - log.Errorf("couldn't get object from tombstone %+v", obj) - return nil - } - node, ok = tombstone.Obj.(*v1.Node) - if !ok { - log.Errorf("tombstone contained object that is not a node %#v", obj) - return nil - } - } - var updatedNeeded bool - if event == model.EventDelete { - updatedNeeded = true - c.Lock() - delete(c.nodeInfoMap, node.Name) - c.Unlock() - } else { - k8sNode := kubernetesNode{labels: node.Labels} - for _, address := range node.Status.Addresses { - if address.Type == v1.NodeExternalIP && address.Address != "" { - k8sNode.address = address.Address - break - } - } - if k8sNode.address == "" { - return nil - } - - c.Lock() - // check if the node exists as this add event could be due to controller resync - // if the stored object changes, then fire an update event. Otherwise, ignore this event. - currentNode, exists := c.nodeInfoMap[node.Name] - if !exists || !nodeEquals(currentNode, k8sNode) { - c.nodeInfoMap[node.Name] = k8sNode - updatedNeeded = true - } - c.Unlock() - } - - // update all related services - if updatedNeeded && c.updateServiceNodePortAddresses() { - c.opts.XDSUpdater.ConfigUpdate(&model.PushRequest{ - Full: true, - Reason: []model.TriggerReason{model.ServiceUpdate}, - }) - } - return nil -} - -// FilterOutFunc func for filtering out objects during update callback -type FilterOutFunc func(old, cur interface{}) bool - -func (c *Controller) registerHandlers( - informer filter.FilteredSharedIndexInformer, otype string, - handler func(interface{}, model.Event) error, filter FilterOutFunc, -) { - wrappedHandler := func(obj interface{}, event model.Event) error { - obj = tryGetLatestObject(informer, obj) - return handler(obj, event) - } - if informer, ok := informer.(cache.SharedInformer); ok { - _ = informer.SetWatchErrorHandler(informermetric.ErrorHandlerForCluster(c.Cluster())) - } - informer.AddEventHandler( - cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - incrementEvent(otype, "add") - if !shouldEnqueue(otype, c.beginSync) { - return - } - c.queue.Push(func() error { - return wrappedHandler(obj, model.EventAdd) - }) - }, - UpdateFunc: func(old, cur interface{}) { - if filter != nil { - if filter(old, cur) { - incrementEvent(otype, "updatesame") - return - } - } - - incrementEvent(otype, "update") - if !shouldEnqueue(otype, c.beginSync) { - return - } - c.queue.Push(func() error { - return wrappedHandler(cur, model.EventUpdate) - }) - }, - DeleteFunc: func(obj interface{}) { - incrementEvent(otype, "delete") - if !shouldEnqueue(otype, c.beginSync) { - return - } - c.queue.Push(func() error { - return handler(obj, model.EventDelete) - }) - }, - }) -} - -// tryGetLatestObject attempts to fetch the latest version of the object from the cache. -// Changes may have occurred between queuing and processing. -func tryGetLatestObject(informer filter.FilteredSharedIndexInformer, obj interface{}) interface{} { - key, err := cache.MetaNamespaceKeyFunc(obj) - if err != nil { - log.Warnf("failed creating key for informer object: %v", err) - return obj - } - - latest, exists, err := informer.GetIndexer().GetByKey(key) - if !exists || err != nil { - log.Warnf("couldn't find %q in informer index", key) - return obj - } - - return latest -} - -// HasSynced returns true after the initial state synchronization -func (c *Controller) HasSynced() bool { - return (c.opts.SyncTimeout != nil && c.opts.SyncTimeout.Load()) || c.initialSync.Load() -} - -func (c *Controller) informersSynced() bool { - if !c.informerInit.Load() { - // registration/Run of informers hasn't occurred yet - return false - } - if (c.nsInformer != nil && !c.nsInformer.HasSynced()) || - !c.serviceInformer.HasSynced() || - !c.endpoints.HasSynced() || - !c.pods.informer.HasSynced() || - !c.nodeInformer.HasSynced() || - !c.exports.HasSynced() || - !c.imports.HasSynced() { - return false - } - return true -} - -// SyncAll syncs all the objects node->service->pod->endpoint in order -// TODO: sync same kind of objects in parallel -// This can cause great performance cost in multi clusters scenario. -// Maybe just sync the cache and trigger one push at last. -func (c *Controller) SyncAll() error { - c.beginSync.Store(true) - var err *multierror.Error - err = multierror.Append(err, c.syncDiscoveryNamespaces()) - err = multierror.Append(err, c.syncSystemNamespace()) - err = multierror.Append(err, c.syncNodes()) - err = multierror.Append(err, c.syncServices()) - err = multierror.Append(err, c.syncPods()) - err = multierror.Append(err, c.syncEndpoints()) - - return multierror.Flatten(err.ErrorOrNil()) -} - -func (c *Controller) syncSystemNamespace() error { - var err error - if c.nsLister != nil { - sysNs, _ := c.nsLister.Get(c.opts.SystemNamespace) - log.Debugf("initializing systemNamespace:%s", c.opts.SystemNamespace) - if sysNs != nil { - err = c.onSystemNamespaceEvent(sysNs, model.EventAdd) - } - } - return err -} - -func (c *Controller) syncDiscoveryNamespaces() error { - var err error - if c.nsLister != nil { - err = c.opts.DiscoveryNamespacesFilter.SyncNamespaces() - } - return err -} - -func (c *Controller) syncNodes() error { - var err *multierror.Error - nodes := c.nodeInformer.GetIndexer().List() - log.Debugf("initializing %d nodes", len(nodes)) - for _, s := range nodes { - err = multierror.Append(err, c.onNodeEvent(s, model.EventAdd)) - } - return err.ErrorOrNil() -} - -func (c *Controller) syncServices() error { - var err *multierror.Error - services := c.serviceInformer.GetIndexer().List() - log.Debugf("initializing %d services", len(services)) - for _, s := range services { - err = multierror.Append(err, c.onServiceEvent(s, model.EventAdd)) - } - return err.ErrorOrNil() -} - -func (c *Controller) syncPods() error { - var err *multierror.Error - pods := c.pods.informer.GetIndexer().List() - log.Debugf("initializing %d pods", len(pods)) - for _, s := range pods { - err = multierror.Append(err, c.pods.onEvent(s, model.EventAdd)) - } - return err.ErrorOrNil() -} - -func (c *Controller) syncEndpoints() error { - var err *multierror.Error - endpoints := c.endpoints.getInformer().GetIndexer().List() - log.Debugf("initializing %d endpoints", len(endpoints)) - for _, s := range endpoints { - err = multierror.Append(err, c.endpoints.onEvent(s, model.EventAdd)) - } - return err.ErrorOrNil() -} - -// Run all controllers until a signal is received -func (c *Controller) Run(stop <-chan struct{}) { - st := time.Now() - if c.opts.NetworksWatcher != nil { - c.opts.NetworksWatcher.AddNetworksHandler(c.reloadNetworkLookup) - c.reloadMeshNetworks() - c.reloadNetworkGateways() - } - c.informerInit.Store(true) - - cache.WaitForCacheSync(stop, c.informersSynced) - // after informer caches sync the first time, process resources in order - if err := c.SyncAll(); err != nil { - log.Errorf("one or more errors force-syncing resources: %v", err) - } - c.initialSync.Store(true) - log.Infof("kube controller for %s synced after %v", c.opts.ClusterID, time.Since(st)) - // after the in-order sync we can start processing the queue - c.queue.Run(stop) - log.Infof("Controller terminated") -} - -// Stop the controller. Only for tests, to simplify the code (defer c.Stop()) -func (c *Controller) Stop() { - if c.stop != nil { - close(c.stop) - } -} - -// Services implements a service catalog operation -func (c *Controller) Services() []*model.Service { - c.RLock() - out := make([]*model.Service, 0, len(c.servicesMap)) - for _, svc := range c.servicesMap { - out = append(out, svc) - } - c.RUnlock() - sort.Slice(out, func(i, j int) bool { return out[i].Hostname < out[j].Hostname }) - return out -} - -// GetService implements a service catalog operation by hostname specified. -func (c *Controller) GetService(hostname host.Name) *model.Service { - c.RLock() - svc := c.servicesMap[hostname] - c.RUnlock() - return svc -} - -// getPodLocality retrieves the locality for a pod. -func (c *Controller) getPodLocality(pod *v1.Pod) string { - // if pod has `istio-locality` label, skip below ops - if len(pod.Labels[model.LocalityLabel]) > 0 { - return model.GetLocalityLabelOrDefault(pod.Labels[model.LocalityLabel], "") - } - - // NodeName is set by the scheduler after the pod is created - // https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#late-initialization - raw, err := c.nodeLister.Get(pod.Spec.NodeName) - if err != nil { - if pod.Spec.NodeName != "" { - log.Warnf("unable to get node %q for pod %q/%q: %v", pod.Spec.NodeName, pod.Namespace, pod.Name, err) - } - return "" - } - - nodeMeta, err := meta.Accessor(raw) - if err != nil { - log.Warnf("unable to get node meta: %v", nodeMeta) - return "" - } - - region := getLabelValue(nodeMeta, NodeRegionLabel, NodeRegionLabelGA) - zone := getLabelValue(nodeMeta, NodeZoneLabel, NodeZoneLabelGA) - subzone := getLabelValue(nodeMeta, label.TopologySubzone.Name, "") - - if region == "" && zone == "" && subzone == "" { - return "" - } - - return region + "/" + zone + "/" + subzone // Format: "%s/%s/%s" -} - -// InstancesByPort implements a service catalog operation -func (c *Controller) InstancesByPort(svc *model.Service, reqSvcPort int, labels labels.Instance) []*model.ServiceInstance { - // First get k8s standard service instances and the workload entry instances - outInstances := c.endpoints.InstancesByPort(c, svc, reqSvcPort, labels) - outInstances = append(outInstances, c.serviceInstancesFromWorkloadInstances(svc, reqSvcPort)...) - - // return when instances found or an error occurs - if len(outInstances) > 0 { - return outInstances - } - - // Fall back to external name service since we did not find any instances of normal services - c.RLock() - externalNameInstances := c.externalNameSvcInstanceMap[svc.Hostname] - c.RUnlock() - if externalNameInstances != nil { - inScopeInstances := make([]*model.ServiceInstance, 0) - for _, i := range externalNameInstances { - if i.Service.Attributes.Namespace == svc.Attributes.Namespace && i.ServicePort.Port == reqSvcPort { - inScopeInstances = append(inScopeInstances, i) - } - } - return inScopeInstances - } - return nil -} - -func (c *Controller) serviceInstancesFromWorkloadInstances(svc *model.Service, reqSvcPort int) []*model.ServiceInstance { - // Run through all the workload instances, select ones that match the service labels - // only if this is a kubernetes internal service and of ClientSideLB (eds) type - // as InstancesByPort is called by the aggregate controller. We dont want to include - // workload instances for any other registry - workloadInstancesExist := !c.workloadInstancesIndex.Empty() - c.RLock() - _, inRegistry := c.servicesMap[svc.Hostname] - c.RUnlock() - - // Only select internal Kubernetes services with selectors - if !inRegistry || !workloadInstancesExist || svc.Attributes.ServiceRegistry != provider.Kubernetes || - svc.MeshExternal || svc.Resolution != model.ClientSideLB || svc.Attributes.LabelSelectors == nil { - return nil - } - - selector := labels.Instance(svc.Attributes.LabelSelectors) - - // Get the service port name and target port so that we can construct the service instance - k8sService, err := c.serviceLister.Services(svc.Attributes.Namespace).Get(svc.Attributes.Name) - // We did not find the k8s service. We cannot get the targetPort - if err != nil { - log.Infof("serviceInstancesFromWorkloadInstances(%s.%s) failed to get k8s service => error %v", - svc.Attributes.Name, svc.Attributes.Namespace, err) - return nil - } - - var servicePort *model.Port - for _, p := range svc.Ports { - if p.Port == reqSvcPort { - servicePort = p - break - } - } - if servicePort == nil { - return nil - } - - // Now get the target Port for this service port - targetPort := findServiceTargetPort(servicePort, k8sService) - if targetPort.num == 0 { - targetPort.num = servicePort.Port - } - - out := make([]*model.ServiceInstance, 0) - - c.workloadInstancesIndex.ForEach(func(wi *model.WorkloadInstance) { - if wi.Namespace != svc.Attributes.Namespace { - return - } - if selector.SubsetOf(wi.Endpoint.Labels) { - instance := serviceInstanceFromWorkloadInstance(svc, servicePort, targetPort, wi) - if instance != nil { - out = append(out, instance) - } - } - }) - return out -} - -func serviceInstanceFromWorkloadInstance(svc *model.Service, servicePort *model.Port, - targetPort serviceTargetPort, wi *model.WorkloadInstance) *model.ServiceInstance { - // create an instance with endpoint whose service port name matches - istioEndpoint := *wi.Endpoint - - // by default, use the numbered targetPort - istioEndpoint.EndpointPort = uint32(targetPort.num) - - if targetPort.name != "" { - // This is a named port, find the corresponding port in the port map - matchedPort := wi.PortMap[targetPort.name] - if matchedPort != 0 { - istioEndpoint.EndpointPort = matchedPort - } else if targetPort.explicitName { - // No match found, and we expect the name explicitly in the service, skip this endpoint - return nil - } - } - - istioEndpoint.ServicePortName = servicePort.Name - return &model.ServiceInstance{ - Service: svc, - ServicePort: servicePort, - Endpoint: &istioEndpoint, - } -} - -// convenience function to collect all workload entry endpoints in updateEDS calls. -func (c *Controller) collectWorkloadInstanceEndpoints(svc *model.Service) []*model.IstioEndpoint { - workloadInstancesExist := !c.workloadInstancesIndex.Empty() - - if !workloadInstancesExist || svc.Resolution != model.ClientSideLB || len(svc.Ports) == 0 { - return nil - } - - endpoints := make([]*model.IstioEndpoint, 0) - for _, port := range svc.Ports { - for _, instance := range c.serviceInstancesFromWorkloadInstances(svc, port.Port) { - endpoints = append(endpoints, instance.Endpoint) - } - } - - return endpoints -} - -// GetProxyServiceInstances returns service instances co-located with a given proxy -// TODO: this code does not return k8s service instances when the proxy's IP is a workload entry -// To tackle this, we need a ip2instance map like what we have in service entry. -func (c *Controller) GetProxyServiceInstances(proxy *model.Proxy) []*model.ServiceInstance { - if len(proxy.IPAddresses) > 0 { - proxyIP := proxy.IPAddresses[0] - // look up for a WorkloadEntry; if there are multiple WorkloadEntry(s) - // with the same IP, choose one deterministically - workload := workloadinstances.GetInstanceForProxy(c.workloadInstancesIndex, proxy, proxyIP) - if workload != nil { - return c.serviceInstancesFromWorkloadInstance(workload) - } - pod := c.pods.getPodByProxy(proxy) - if pod != nil && !proxy.IsVM() { - // we don't want to use this block for our test "VM" which is actually a Pod. - - if !c.isControllerForProxy(proxy) { - log.Errorf("proxy is in cluster %v, but controller is for cluster %v", proxy.Metadata.ClusterID, c.Cluster()) - return nil - } - - // 1. find proxy service by label selector, if not any, there may exist headless service without selector - // failover to 2 - if services, err := getPodServices(c.serviceLister, pod); err == nil && len(services) > 0 { - out := make([]*model.ServiceInstance, 0) - for _, svc := range services { - out = append(out, c.getProxyServiceInstancesByPod(pod, svc, proxy)...) - } - return out - } - // 2. Headless service without selector - return c.endpoints.GetProxyServiceInstances(c, proxy) - } - - // 3. The pod is not present when this is called - // due to eventual consistency issues. However, we have a lot of information about the pod from the proxy - // metadata already. Because of this, we can still get most of the information we need. - // If we cannot accurately construct ServiceInstances from just the metadata, this will return an error and we can - // attempt to read the real pod. - out, err := c.getProxyServiceInstancesFromMetadata(proxy) - if err != nil { - log.Warnf("getProxyServiceInstancesFromMetadata for %v failed: %v", proxy.ID, err) - } - return out - } - - // TODO: This could not happen, remove? - if c.opts.Metrics != nil { - c.opts.Metrics.AddMetric(model.ProxyStatusNoService, proxy.ID, proxy.ID, "") - } else { - log.Infof("Missing metrics env, empty list of services for pod %s", proxy.ID) - } - return nil -} - -func (c *Controller) serviceInstancesFromWorkloadInstance(si *model.WorkloadInstance) []*model.ServiceInstance { - out := make([]*model.ServiceInstance, 0) - // find the workload entry's service by label selector - // rather than scanning through our internal map of model.services, get the services via the k8s apis - dummyPod := &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{Namespace: si.Namespace, Labels: si.Endpoint.Labels}, - } - - // find the services that map to this workload entry, fire off eds updates if the service is of type client-side lb - if k8sServices, err := getPodServices(c.serviceLister, dummyPod); err == nil && len(k8sServices) > 0 { - for _, k8sSvc := range k8sServices { - service := c.GetService(kube.ServiceHostname(k8sSvc.Name, k8sSvc.Namespace, c.opts.DomainSuffix)) - // Note that this cannot be an external service because k8s external services do not have label selectors. - if service == nil || service.Resolution != model.ClientSideLB { - // may be a headless service - continue - } - - for _, servicePort := range service.Ports { - if servicePort.Protocol == protocol.UDP { - continue - } - - // Now get the target Port for this service port - targetPort := findServiceTargetPort(servicePort, k8sSvc) - if targetPort.num == 0 { - targetPort.num = servicePort.Port - } - - instance := serviceInstanceFromWorkloadInstance(service, servicePort, targetPort, si) - if instance != nil { - out = append(out, instance) - } - } - } - } - return out -} - -// WorkloadInstanceHandler defines the handler for service instances generated by other registries -func (c *Controller) WorkloadInstanceHandler(si *model.WorkloadInstance, event model.Event) { - // ignore malformed workload entries. And ignore any workload entry that does not have a label - // as there is no way for us to select them - if si.Namespace == "" || len(si.Endpoint.Labels) == 0 { - return - } - - // this is from a workload entry. Store it in separate index so that - // the InstancesByPort can use these as well as the k8s pods. - switch event { - case model.EventDelete: - c.workloadInstancesIndex.Delete(si) - default: // add or update - c.workloadInstancesIndex.Insert(si) - } - - // find the workload entry's service by label selector - // rather than scanning through our internal map of model.services, get the services via the k8s apis - dummyPod := &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{Namespace: si.Namespace, Labels: si.Endpoint.Labels}, - } - - shard := model.ShardKeyFromRegistry(c) - // find the services that map to this workload entry, fire off eds updates if the service is of type client-side lb - if k8sServices, err := getPodServices(c.serviceLister, dummyPod); err == nil && len(k8sServices) > 0 { - for _, k8sSvc := range k8sServices { - service := c.GetService(kube.ServiceHostname(k8sSvc.Name, k8sSvc.Namespace, c.opts.DomainSuffix)) - // Note that this cannot be an external service because k8s external services do not have label selectors. - if service == nil || service.Resolution != model.ClientSideLB { - // may be a headless service - continue - } - - // Get the updated list of endpoints that includes k8s pods and the workload entries for this service - // and then notify the EDS server that endpoints for this service have changed. - // We need one endpoint object for each service port - endpoints := make([]*model.IstioEndpoint, 0) - for _, port := range service.Ports { - if port.Protocol == protocol.UDP { - continue - } - // Similar code as UpdateServiceShards in eds.go - instances := c.InstancesByPort(service, port.Port, nil) - for _, inst := range instances { - endpoints = append(endpoints, inst.Endpoint) - } - } - // fire off eds update - c.opts.XDSUpdater.EDSUpdate(shard, string(service.Hostname), service.Attributes.Namespace, endpoints) - } - } -} - -func (c *Controller) onSystemNamespaceEvent(obj interface{}, ev model.Event) error { - if ev == model.EventDelete { - return nil - } - ns, ok := obj.(*v1.Namespace) - if !ok { - log.Warnf("Namespace watch getting wrong type in event: %T", obj) - return nil - } - if ns == nil { - return nil - } - nw := ns.Labels[label.TopologyNetwork.Name] - c.Lock() - oldDefaultNetwork := c.network - c.network = network.ID(nw) - c.Unlock() - // network changed, rarely happen - if oldDefaultNetwork != c.network { - // refresh pods/endpoints/services - c.onDefaultNetworkChange() - } - return nil -} - -// isControllerForProxy should be used for proxies assumed to be in the kube cluster for this controller. Workload Entries -// may not necessarily pass this check, but we still want to allow kube services to select workload instances. -func (c *Controller) isControllerForProxy(proxy *model.Proxy) bool { - return proxy.Metadata.ClusterID == "" || proxy.Metadata.ClusterID == c.Cluster() -} - -// getProxyServiceInstancesFromMetadata retrieves ServiceInstances using proxy Metadata rather than -// from the Pod. This allows retrieving Instances immediately, regardless of delays in Kubernetes. -// If the proxy doesn't have enough metadata, an error is returned -func (c *Controller) getProxyServiceInstancesFromMetadata(proxy *model.Proxy) ([]*model.ServiceInstance, error) { - if len(proxy.Metadata.Labels) == 0 { - return nil, nil - } - - if !c.isControllerForProxy(proxy) { - return nil, fmt.Errorf("proxy is in cluster %v, but controller is for cluster %v", proxy.Metadata.ClusterID, c.Cluster()) - } - - // Create a pod with just the information needed to find the associated Services - dummyPod := &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: proxy.ConfigNamespace, - Labels: proxy.Metadata.Labels, - }, - } - - // Find the Service associated with the pod. - services, err := getPodServices(c.serviceLister, dummyPod) - if err != nil { - return nil, fmt.Errorf("error getting instances for %s: %v", proxy.ID, err) - } - if len(services) == 0 { - return nil, fmt.Errorf("no instances found for %s: %v", proxy.ID, err) - } - - out := make([]*model.ServiceInstance, 0) - for _, svc := range services { - hostname := kube.ServiceHostname(svc.Name, svc.Namespace, c.opts.DomainSuffix) - modelService := c.GetService(hostname) - if modelService == nil { - return nil, fmt.Errorf("failed to find model service for %v", hostname) - } - - for _, modelService := range c.servicesForNamespacedName(kube.NamespacedNameForK8sObject(svc)) { - discoverabilityPolicy := c.exports.EndpointDiscoverabilityPolicy(modelService) - - tps := make(map[model.Port]*model.Port) - tpsList := make([]model.Port, 0) - for _, port := range svc.Spec.Ports { - svcPort, f := modelService.Ports.Get(port.Name) - if !f { - return nil, fmt.Errorf("failed to get svc port for %v", port.Name) - } - - var portNum int - if len(proxy.Metadata.PodPorts) > 0 { - portNum, err = findPortFromMetadata(port, proxy.Metadata.PodPorts) - if err != nil { - return nil, fmt.Errorf("failed to find target port for %v: %v", proxy.ID, err) - } - } else { - // most likely a VM - we assume the WorkloadEntry won't remap any ports - portNum = port.TargetPort.IntValue() - } - - // Dedupe the target ports here - Service might have configured multiple ports to the same target port, - // we will have to create only one ingress listener per port and protocol so that we do not endup - // complaining about listener conflicts. - targetPort := model.Port{ - Port: portNum, - Protocol: svcPort.Protocol, - } - if _, exists := tps[targetPort]; !exists { - tps[targetPort] = svcPort - tpsList = append(tpsList, targetPort) - } - } - - epBuilder := NewEndpointBuilderFromMetadata(c, proxy) - // Iterate over target ports in the same order as defined in service spec, in case of - // protocol conflict for a port causes unstable protocol selection for a port. - for _, tp := range tpsList { - svcPort := tps[tp] - // consider multiple IP scenarios - for _, ip := range proxy.IPAddresses { - // Construct the ServiceInstance - out = append(out, &model.ServiceInstance{ - Service: modelService, - ServicePort: svcPort, - Endpoint: epBuilder.buildIstioEndpoint(ip, int32(tp.Port), svcPort.Name, discoverabilityPolicy), - }) - } - } - } - } - return out, nil -} - -func (c *Controller) getProxyServiceInstancesByPod(pod *v1.Pod, - service *v1.Service, proxy *model.Proxy) []*model.ServiceInstance { - var out []*model.ServiceInstance - - for _, svc := range c.servicesForNamespacedName(kube.NamespacedNameForK8sObject(service)) { - discoverabilityPolicy := c.exports.EndpointDiscoverabilityPolicy(svc) - - tps := make(map[model.Port]*model.Port) - tpsList := make([]model.Port, 0) - for _, port := range service.Spec.Ports { - svcPort, exists := svc.Ports.Get(port.Name) - if !exists { - continue - } - // find target port - portNum, err := FindPort(pod, &port) - if err != nil { - log.Warnf("Failed to find port for service %s/%s: %v", service.Namespace, service.Name, err) - continue - } - // Dedupe the target ports here - Service might have configured multiple ports to the same target port, - // we will have to create only one ingress listener per port and protocol so that we do not endup - // complaining about listener conflicts. - targetPort := model.Port{ - Port: portNum, - Protocol: svcPort.Protocol, - } - if _, exists := tps[targetPort]; !exists { - tps[targetPort] = svcPort - tpsList = append(tpsList, targetPort) - } - } - - builder := NewEndpointBuilder(c, pod) - // Iterate over target ports in the same order as defined in service spec, in case of - // protocol conflict for a port causes unstable protocol selection for a port. - for _, tp := range tpsList { - svcPort := tps[tp] - // consider multiple IP scenarios - for _, ip := range proxy.IPAddresses { - istioEndpoint := builder.buildIstioEndpoint(ip, int32(tp.Port), svcPort.Name, discoverabilityPolicy) - out = append(out, &model.ServiceInstance{ - Service: svc, - ServicePort: svcPort, - Endpoint: istioEndpoint, - }) - } - } - } - - return out -} - -func (c *Controller) GetProxyWorkloadLabels(proxy *model.Proxy) labels.Instance { - pod := c.pods.getPodByProxy(proxy) - if pod != nil { - return pod.Labels - } - return nil -} - -// GetIstioServiceAccounts returns the Istio service accounts running a service -// hostname. Each service account is encoded according to the SPIFFE VSID spec. -// For example, a service account named "bar" in namespace "foo" is encoded as -// "spiffe://cluster.local/ns/foo/sa/bar". -func (c *Controller) GetIstioServiceAccounts(svc *model.Service, ports []int) []string { - return model.GetServiceAccounts(svc, ports, c) -} - -// AppendServiceHandler implements a service catalog operation -func (c *Controller) AppendServiceHandler(f func(*model.Service, model.Event)) { - c.handlers.AppendServiceHandler(f) -} - -// AppendWorkloadHandler implements a service catalog operation -func (c *Controller) AppendWorkloadHandler(f func(*model.WorkloadInstance, model.Event)) { - c.handlers.AppendWorkloadHandler(f) -} diff --git a/pilot/pkg/serviceregistry/kube/controller/controller_test.go b/pilot/pkg/serviceregistry/kube/controller/controller_test.go deleted file mode 100644 index 45ae92171..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/controller_test.go +++ /dev/null @@ -1,2709 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "context" - "fmt" - "net" - "reflect" - "sort" - "strconv" - "sync" - "testing" - "time" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - "github.com/google/go-cmp/cmp" - "istio.io/api/annotation" - "istio.io/api/label" - meshconfig "istio.io/api/mesh/v1alpha1" - coreV1 "k8s.io/api/core/v1" - discovery "k8s.io/api/discovery/v1" - "k8s.io/apimachinery/pkg/api/errors" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube/controller/filter" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - kubelib "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/network" - "github.com/apache/dubbo-go-pixiu/pkg/spiffe" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -const ( - testService = "test" -) - -// eventually polls cond until it completes (returns true) or times out (resulting in a test failure). -func eventually(t test.Failer, cond func() bool) { - t.Helper() - retry.UntilSuccessOrFail(t, func() error { - if !cond() { - return fmt.Errorf("failed to get positive condition") - } - return nil - }, retry.Timeout(time.Second), retry.Delay(time.Millisecond*10)) -} - -func TestServices(t *testing.T) { - networksWatcher := mesh.NewFixedNetworksWatcher(&meshconfig.MeshNetworks{ - Networks: map[string]*meshconfig.Network{ - "network1": { - Endpoints: []*meshconfig.Network_NetworkEndpoints{ - { - Ne: &meshconfig.Network_NetworkEndpoints_FromCidr{ - FromCidr: "10.10.1.1/24", - }, - }, - }, - }, - "network2": { - Endpoints: []*meshconfig.Network_NetworkEndpoints{ - { - Ne: &meshconfig.Network_NetworkEndpoints_FromCidr{ - FromCidr: "10.11.1.1/24", - }, - }, - }, - }, - }, - }) - for mode, name := range EndpointModeNames { - mode := mode - t.Run(name, func(t *testing.T) { - ctl, fx := NewFakeControllerWithOptions(FakeControllerOptions{NetworksWatcher: networksWatcher, Mode: mode}) - go ctl.Run(ctl.stop) - // Wait for the caches to sync, otherwise we may hit race conditions where events are dropped - cache.WaitForCacheSync(ctl.stop, ctl.HasSynced) - defer ctl.Stop() - t.Parallel() - ns := "ns-test" - - hostname := kube.ServiceHostname(testService, ns, defaultFakeDomainSuffix) - - var sds model.ServiceDiscovery = ctl - // "test", ports: http-example on 80 - makeService(testService, ns, ctl.client, t) - <-fx.Events - - eventually(t, func() bool { - out := sds.Services() - - // Original test was checking for 'protocolTCP' - which is incorrect (the - // port name is 'http'. It was working because the Service was created with - // an invalid protocol, and the code was ignoring that ( not TCP/UDP). - for _, item := range out { - if item.Hostname == hostname && - len(item.Ports) == 1 && - item.Ports[0].Protocol == protocol.HTTP { - return true - } - } - return false - }) - - // 2 ports 1001, 2 IPs - createEndpoints(t, ctl, testService, ns, []string{"http-example", "foo"}, []string{"10.10.1.1", "10.11.1.2"}, nil, nil) - - svc := sds.GetService(hostname) - if svc == nil { - t.Fatalf("GetService(%q) => should exists", hostname) - } - if svc.Hostname != hostname { - t.Fatalf("GetService(%q) => %q", hostname, svc.Hostname) - } - - eventually(t, func() bool { - ep := sds.InstancesByPort(svc, 80, nil) - return len(ep) == 2 - }) - - ep := sds.InstancesByPort(svc, 80, nil) - if len(ep) != 2 { - t.Fatalf("Invalid response for GetInstancesByPort %v", ep) - } - - if ep[0].Endpoint.Address == "10.10.1.1" && ep[0].Endpoint.Network != "network1" { - t.Fatalf("Endpoint with IP 10.10.1.1 is expected to be in network1 but get: %s", ep[0].Endpoint.Network) - } - - if ep[1].Endpoint.Address == "10.11.1.2" && ep[1].Endpoint.Network != "network2" { - t.Fatalf("Endpoint with IP 10.11.1.2 is expected to be in network2 but get: %s", ep[1].Endpoint.Network) - } - - missing := kube.ServiceHostname("does-not-exist", ns, defaultFakeDomainSuffix) - svc = sds.GetService(missing) - if svc != nil { - t.Fatalf("GetService(%q) => %s, should not exist", missing, svc.Hostname) - } - }) - } -} - -func makeService(n, ns string, cl kubernetes.Interface, t *testing.T) { - _, err := cl.CoreV1().Services(ns).Create(context.TODO(), &coreV1.Service{ - ObjectMeta: metaV1.ObjectMeta{Name: n}, - Spec: coreV1.ServiceSpec{ - Ports: []coreV1.ServicePort{ - { - Port: 80, - Name: "http-example", - Protocol: coreV1.ProtocolTCP, // Not added automatically by fake - }, - }, - }, - }, metaV1.CreateOptions{}) - if err != nil { - t.Log("Service already created (rerunning test)") - } - log.Infof("Created service %s", n) -} - -func TestController_GetPodLocality(t *testing.T) { - pod1 := generatePod("128.0.1.1", "pod1", "nsA", "", "node1", map[string]string{"app": "prod-app"}, map[string]string{}) - pod2 := generatePod("128.0.1.2", "pod2", "nsB", "", "node2", map[string]string{"app": "prod-app"}, map[string]string{}) - podOverride := generatePod("128.0.1.2", "pod2", "nsB", "", - "node1", map[string]string{"app": "prod-app", model.LocalityLabel: "regionOverride.zoneOverride.subzoneOverride"}, map[string]string{}) - testCases := []struct { - name string - pods []*coreV1.Pod - nodes []*coreV1.Node - wantAZ map[*coreV1.Pod]string - }{ - { - name: "should return correct az for given address", - pods: []*coreV1.Pod{pod1, pod2}, - nodes: []*coreV1.Node{ - generateNode("node1", map[string]string{NodeZoneLabel: "zone1", NodeRegionLabel: "region1", label.TopologySubzone.Name: "subzone1"}), - generateNode("node2", map[string]string{NodeZoneLabel: "zone2", NodeRegionLabel: "region2", label.TopologySubzone.Name: "subzone2"}), - }, - wantAZ: map[*coreV1.Pod]string{ - pod1: "region1/zone1/subzone1", - pod2: "region2/zone2/subzone2", - }, - }, - { - name: "should return correct az for given address", - pods: []*coreV1.Pod{pod1, pod2}, - nodes: []*coreV1.Node{ - generateNode("node1", map[string]string{NodeZoneLabel: "zone1", NodeRegionLabel: "region1"}), - generateNode("node2", map[string]string{NodeZoneLabel: "zone2", NodeRegionLabel: "region2"}), - }, - wantAZ: map[*coreV1.Pod]string{ - pod1: "region1/zone1/", - pod2: "region2/zone2/", - }, - }, - { - name: "should return false if pod isn't in the cache", - wantAZ: map[*coreV1.Pod]string{ - pod1: "", - pod2: "", - }, - }, - { - name: "should return false if node isn't in the cache", - pods: []*coreV1.Pod{pod1, pod2}, - wantAZ: map[*coreV1.Pod]string{ - pod1: "", - pod2: "", - }, - }, - { - name: "should return correct az if node has only region label", - pods: []*coreV1.Pod{pod1, pod2}, - nodes: []*coreV1.Node{ - generateNode("node1", map[string]string{NodeRegionLabel: "region1"}), - generateNode("node2", map[string]string{NodeRegionLabel: "region2"}), - }, - wantAZ: map[*coreV1.Pod]string{ - pod1: "region1//", - pod2: "region2//", - }, - }, - { - name: "should return correct az if node has only zone label", - pods: []*coreV1.Pod{pod1, pod2}, - nodes: []*coreV1.Node{ - generateNode("node1", map[string]string{NodeZoneLabel: "zone1"}), - generateNode("node2", map[string]string{NodeZoneLabel: "zone2"}), - }, - wantAZ: map[*coreV1.Pod]string{ - pod1: "/zone1/", - pod2: "/zone2/", - }, - }, - { - name: "should return correct az if node has only subzone label", - pods: []*coreV1.Pod{pod1, pod2}, - nodes: []*coreV1.Node{ - generateNode("node1", map[string]string{label.TopologySubzone.Name: "subzone1"}), - generateNode("node2", map[string]string{label.TopologySubzone.Name: "subzone2"}), - }, - wantAZ: map[*coreV1.Pod]string{ - pod1: "//subzone1", - pod2: "//subzone2", - }, - }, - { - name: "should return correct az for given address", - pods: []*coreV1.Pod{podOverride}, - nodes: []*coreV1.Node{ - generateNode("node1", map[string]string{NodeZoneLabel: "zone1", NodeRegionLabel: "region1", label.TopologySubzone.Name: "subzone1"}), - }, - wantAZ: map[*coreV1.Pod]string{ - podOverride: "regionOverride/zoneOverride/subzoneOverride", - }, - }, - } - - for _, tc := range testCases { - // If using t.Parallel() you must copy the iteration to a new local variable - // https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables - tc := tc - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - // Setup kube caches - // Pod locality only matters for Endpoints - controller, fx := NewFakeControllerWithOptions(FakeControllerOptions{Mode: EndpointsOnly}) - go controller.Run(controller.stop) - // Wait for the caches to sync, otherwise we may hit race conditions where events are dropped - cache.WaitForCacheSync(controller.stop, controller.HasSynced) - defer controller.Stop() - addNodes(t, controller, tc.nodes...) - addPods(t, controller, fx, tc.pods...) - - // Verify expected existing pod AZs - for pod, wantAZ := range tc.wantAZ { - az := controller.getPodLocality(pod) - if wantAZ != "" { - if !reflect.DeepEqual(az, wantAZ) { - t.Fatalf("Wanted az: %s, got: %s", wantAZ, az) - } - } else { - if az != "" { - t.Fatalf("Unexpectedly found az: %s for pod: %s", az, pod.ObjectMeta.Name) - } - } - } - }) - } -} - -func TestGetProxyServiceInstances(t *testing.T) { - clusterID := cluster.ID("fakeCluster") - networkID := network.ID("fakeNetwork") - for mode, name := range EndpointModeNames { - mode := mode - t.Run(name, func(t *testing.T) { - controller, fx := NewFakeControllerWithOptions(FakeControllerOptions{ - Mode: mode, - ClusterID: clusterID, - }) - // add a network ID to test endpoints include topology.istio.io/network label - controller.network = networkID - go controller.Run(controller.stop) - // Wait for the caches to sync, otherwise we may hit race conditions where events are dropped - cache.WaitForCacheSync(controller.stop, controller.HasSynced) - defer controller.Stop() - p := generatePod("128.0.0.1", "pod1", "nsa", "foo", "node1", map[string]string{"app": "test-app"}, map[string]string{}) - addPods(t, controller, fx, p) - - k8sSaOnVM := "acct4" - canonicalSaOnVM := "acctvm2@gserviceaccount2.com" - - createService(controller, "svc1", "nsa", - map[string]string{ - annotation.AlphaKubernetesServiceAccounts.Name: k8sSaOnVM, - annotation.AlphaCanonicalServiceAccounts.Name: canonicalSaOnVM, - }, - []int32{8080}, map[string]string{"app": "prod-app"}, t) - ev := fx.Wait("service") - if ev == nil { - t.Fatal("Timeout creating service") - } - - // Endpoints are generated by Kubernetes from pod labels and service selectors. - // Here we manually create them for mocking purpose. - svc1Ips := []string{"128.0.0.1"} - portNames := []string{"tcp-port"} - // Create 1 endpoint that refers to a pod in the same namespace. - createEndpoints(t, controller, "svc1", "nsA", portNames, svc1Ips, nil, nil) - - // Creates 100 endpoints that refers to a pod in a different namespace. - fakeSvcCounts := 100 - for i := 0; i < fakeSvcCounts; i++ { - svcName := fmt.Sprintf("svc-fake-%d", i) - createService(controller, svcName, "nsfake", - map[string]string{ - annotation.AlphaKubernetesServiceAccounts.Name: k8sSaOnVM, - annotation.AlphaCanonicalServiceAccounts.Name: canonicalSaOnVM, - }, - []int32{8080}, map[string]string{"app": "prod-app"}, t) - fx.Wait("service") - - createEndpoints(t, controller, svcName, "nsfake", portNames, svc1Ips, nil, nil) - fx.Wait("eds") - } - - // Create 1 endpoint that refers to a pod in the same namespace. - createEndpoints(t, controller, "svc1", "nsa", portNames, svc1Ips, nil, nil) - fx.Wait("eds") - - // this can test get pod by proxy ID - svcNode := &model.Proxy{ - Type: model.Router, - IPAddresses: []string{"128.0.0.1"}, - ID: "pod1.nsa", - DNSDomain: "nsa.svc.cluster.local", - Metadata: &model.NodeMetadata{Namespace: "nsa", ClusterID: clusterID}, - } - serviceInstances := controller.GetProxyServiceInstances(svcNode) - - if len(serviceInstances) != 1 { - t.Fatalf("GetProxyServiceInstances() expected 1 instance, got %d", len(serviceInstances)) - } - - hostname := kube.ServiceHostname("svc1", "nsa", defaultFakeDomainSuffix) - if serviceInstances[0].Service.Hostname != hostname { - t.Fatalf("GetProxyServiceInstances() wrong service instance returned => hostname %q, want %q", - serviceInstances[0].Service.Hostname, hostname) - } - - // Test that we can look up instances just by Proxy metadata - metaServices := controller.GetProxyServiceInstances(&model.Proxy{ - Type: "sidecar", - IPAddresses: []string{"1.1.1.1"}, - Locality: &core.Locality{Region: "r", Zone: "z"}, - ConfigNamespace: "nsa", - Metadata: &model.NodeMetadata{ - ServiceAccount: "account", - ClusterID: clusterID, - Labels: map[string]string{ - "app": "prod-app", - label.SecurityTlsMode.Name: "mutual", - }, - }, - }) - - expected := &model.ServiceInstance{ - Service: &model.Service{ - Hostname: "svc1.nsa.svc.company.com", - ClusterVIPs: model.AddressMap{ - Addresses: map[cluster.ID][]string{clusterID: {"10.0.0.1"}}, - }, - DefaultAddress: "10.0.0.1", - Ports: []*model.Port{{Name: "tcp-port", Port: 8080, Protocol: protocol.TCP}}, - ServiceAccounts: []string{"acctvm2@gserviceaccount2.com", "spiffe://cluster.local/ns/nsa/sa/acct4"}, - Attributes: model.ServiceAttributes{ - ServiceRegistry: provider.Kubernetes, - Name: "svc1", - Namespace: "nsa", - LabelSelectors: map[string]string{"app": "prod-app"}, - }, - }, - ServicePort: &model.Port{Name: "tcp-port", Port: 8080, Protocol: protocol.TCP}, - Endpoint: &model.IstioEndpoint{ - Labels: labels.Instance{ - "app": "prod-app", - label.SecurityTlsMode.Name: "mutual", - NodeRegionLabelGA: "r", - NodeZoneLabelGA: "z", - label.TopologyCluster.Name: clusterID.String(), - label.TopologyNetwork.Name: networkID.String(), - }, - ServiceAccount: "account", - Address: "1.1.1.1", - Network: networkID, - EndpointPort: 0, - ServicePortName: "tcp-port", - Locality: model.Locality{ - Label: "r/z", - ClusterID: clusterID, - }, - TLSMode: "mutual", - }, - } - - if len(metaServices) != 1 { - t.Fatalf("expected 1 instance, got %v", len(metaServices)) - } - // Remove the discoverability function so that it's ignored by DeepEqual. - clearDiscoverabilityPolicy(metaServices[0].Endpoint) - if !reflect.DeepEqual(expected, metaServices[0]) { - t.Fatalf("expected instance %v, got %v", expected, metaServices[0]) - } - - // Test that we first look up instances by Proxy pod - - node := generateNode("node1", map[string]string{NodeZoneLabel: "zone1", NodeRegionLabel: "region1", label.TopologySubzone.Name: "subzone1"}) - addNodes(t, controller, node) - - // 1. pod without `istio-locality` label, get locality from node label. - p = generatePod("129.0.0.1", "pod2", "nsa", "svcaccount", "node1", - map[string]string{"app": "prod-app"}, nil) - addPods(t, controller, fx, p) - - // this can test get pod by proxy ip address - podServices := controller.GetProxyServiceInstances(&model.Proxy{ - Type: "sidecar", - IPAddresses: []string{"129.0.0.1"}, - Locality: &core.Locality{Region: "r", Zone: "z"}, - ConfigNamespace: "nsa", - Metadata: &model.NodeMetadata{ - ServiceAccount: "account", - ClusterID: clusterID, - Labels: map[string]string{ - "app": "prod-app", - }, - }, - }) - - expected = &model.ServiceInstance{ - Service: &model.Service{ - Hostname: "svc1.nsa.svc.company.com", - ClusterVIPs: model.AddressMap{ - Addresses: map[cluster.ID][]string{clusterID: {"10.0.0.1"}}, - }, - DefaultAddress: "10.0.0.1", - Ports: []*model.Port{{Name: "tcp-port", Port: 8080, Protocol: protocol.TCP}}, - ServiceAccounts: []string{"acctvm2@gserviceaccount2.com", "spiffe://cluster.local/ns/nsa/sa/acct4"}, - Attributes: model.ServiceAttributes{ - ServiceRegistry: provider.Kubernetes, - Name: "svc1", - Namespace: "nsa", - LabelSelectors: map[string]string{"app": "prod-app"}, - }, - }, - ServicePort: &model.Port{Name: "tcp-port", Port: 8080, Protocol: protocol.TCP}, - Endpoint: &model.IstioEndpoint{ - Address: "129.0.0.1", - Network: networkID, - EndpointPort: 0, - ServicePortName: "tcp-port", - Locality: model.Locality{ - Label: "region1/zone1/subzone1", - ClusterID: clusterID, - }, - Labels: labels.Instance{ - "app": "prod-app", - NodeRegionLabelGA: "region1", - NodeZoneLabelGA: "zone1", - label.TopologySubzone.Name: "subzone1", - label.TopologyCluster.Name: clusterID.String(), - label.TopologyNetwork.Name: networkID.String(), - }, - ServiceAccount: "spiffe://cluster.local/ns/nsa/sa/svcaccount", - TLSMode: model.DisabledTLSModeLabel, - WorkloadName: "pod2", - Namespace: "nsa", - }, - } - if len(podServices) != 1 { - t.Fatalf("expected 1 instance, got %v", len(podServices)) - } - clearDiscoverabilityPolicy(podServices[0].Endpoint) - if !reflect.DeepEqual(expected, podServices[0]) { - t.Fatalf("expected instance %v, got %v", expected, podServices[0]) - } - - // 2. pod with `istio-locality` label, ignore node label. - p = generatePod("129.0.0.2", "pod3", "nsa", "svcaccount", "node1", - map[string]string{"app": "prod-app", "istio-locality": "region.zone"}, nil) - addPods(t, controller, fx, p) - - // this can test get pod by proxy ip address - podServices = controller.GetProxyServiceInstances(&model.Proxy{ - Type: "sidecar", - IPAddresses: []string{"129.0.0.2"}, - Locality: &core.Locality{Region: "r", Zone: "z"}, - ConfigNamespace: "nsa", - Metadata: &model.NodeMetadata{ - ServiceAccount: "account", - ClusterID: clusterID, - Labels: map[string]string{ - "app": "prod-app", - }, - }, - }) - - expected = &model.ServiceInstance{ - Service: &model.Service{ - Hostname: "svc1.nsa.svc.company.com", - ClusterVIPs: model.AddressMap{ - Addresses: map[cluster.ID][]string{clusterID: {"10.0.0.1"}}, - }, - DefaultAddress: "10.0.0.1", - Ports: []*model.Port{{Name: "tcp-port", Port: 8080, Protocol: protocol.TCP}}, - ServiceAccounts: []string{"acctvm2@gserviceaccount2.com", "spiffe://cluster.local/ns/nsa/sa/acct4"}, - Attributes: model.ServiceAttributes{ - ServiceRegistry: provider.Kubernetes, - Name: "svc1", - Namespace: "nsa", - LabelSelectors: map[string]string{"app": "prod-app"}, - }, - }, - ServicePort: &model.Port{Name: "tcp-port", Port: 8080, Protocol: protocol.TCP}, - Endpoint: &model.IstioEndpoint{ - Address: "129.0.0.2", - Network: networkID, - EndpointPort: 0, - ServicePortName: "tcp-port", - Locality: model.Locality{ - Label: "region/zone", - ClusterID: clusterID, - }, - Labels: labels.Instance{ - "app": "prod-app", - "istio-locality": "region.zone", - NodeRegionLabelGA: "region", - NodeZoneLabelGA: "zone", - label.TopologyCluster.Name: clusterID.String(), - label.TopologyNetwork.Name: networkID.String(), - }, - ServiceAccount: "spiffe://cluster.local/ns/nsa/sa/svcaccount", - TLSMode: model.DisabledTLSModeLabel, - WorkloadName: "pod3", - Namespace: "nsa", - }, - } - if len(podServices) != 1 { - t.Fatalf("expected 1 instance, got %v", len(podServices)) - } - clearDiscoverabilityPolicy(podServices[0].Endpoint) - if !reflect.DeepEqual(expected, podServices[0]) { - t.Fatalf("expected instance %v, got %v", expected, podServices[0]) - } - }) - } -} - -func TestGetProxyServiceInstancesWithMultiIPsAndTargetPorts(t *testing.T) { - pod1 := generatePod("128.0.0.1", "pod1", "nsa", "foo", "node1", map[string]string{"app": "test-app"}, map[string]string{}) - testCases := []struct { - name string - pods []*coreV1.Pod - ips []string - ports []coreV1.ServicePort - wantEndpoints []model.IstioEndpoint - }{ - { - name: "multiple proxy ips single port", - pods: []*coreV1.Pod{pod1}, - ips: []string{"128.0.0.1", "192.168.2.6"}, - ports: []coreV1.ServicePort{ - { - Name: "tcp-port", - Port: 8080, - Protocol: "http", - TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 8080}, - }, - }, - wantEndpoints: []model.IstioEndpoint{ - { - Address: "128.0.0.1", - ServicePortName: "tcp-port", - EndpointPort: 8080, - }, - { - Address: "192.168.2.6", - ServicePortName: "tcp-port", - EndpointPort: 8080, - }, - }, - }, - { - name: "single proxy ip single port", - pods: []*coreV1.Pod{pod1}, - ips: []string{"128.0.0.1"}, - ports: []coreV1.ServicePort{ - { - Name: "tcp-port", - Port: 8080, - Protocol: "TCP", - TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 8080}, - }, - }, - wantEndpoints: []model.IstioEndpoint{ - { - Address: "128.0.0.1", - ServicePortName: "tcp-port", - EndpointPort: 8080, - }, - }, - }, - { - name: "multiple proxy ips multiple ports", - pods: []*coreV1.Pod{pod1}, - ips: []string{"128.0.0.1", "192.168.2.6"}, - ports: []coreV1.ServicePort{ - { - Name: "tcp-port-1", - Port: 8080, - Protocol: "http", - TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 8080}, - }, - { - Name: "tcp-port-2", - Port: 9090, - Protocol: "http", - TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 9090}, - }, - }, - wantEndpoints: []model.IstioEndpoint{ - { - Address: "128.0.0.1", - ServicePortName: "tcp-port-1", - EndpointPort: 8080, - }, - { - Address: "192.168.2.6", - ServicePortName: "tcp-port-1", - EndpointPort: 8080, - }, - { - Address: "128.0.0.1", - ServicePortName: "tcp-port-2", - EndpointPort: 9090, - }, - { - Address: "192.168.2.6", - ServicePortName: "tcp-port-2", - EndpointPort: 9090, - }, - }, - }, - { - name: "single proxy ip multiple ports same target port with different protocols", - pods: []*coreV1.Pod{pod1}, - ips: []string{"128.0.0.1"}, - ports: []coreV1.ServicePort{ - { - Name: "tcp-port", - Port: 8080, - Protocol: "TCP", - TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 8080}, - }, - { - Name: "http-port", - Port: 9090, - Protocol: "TCP", - TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 8080}, - }, - }, - wantEndpoints: []model.IstioEndpoint{ - { - Address: "128.0.0.1", - ServicePortName: "tcp-port", - EndpointPort: 8080, - }, - { - Address: "128.0.0.1", - ServicePortName: "http-port", - EndpointPort: 8080, - }, - }, - }, - { - name: "single proxy ip multiple ports same target port with overlapping protocols", - pods: []*coreV1.Pod{pod1}, - ips: []string{"128.0.0.1"}, - ports: []coreV1.ServicePort{ - { - Name: "http-7442", - Port: 7442, - Protocol: "TCP", - TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 7442}, - }, - { - Name: "tcp-8443", - Port: 8443, - Protocol: "TCP", - TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 7442}, - }, - { - Name: "http-7557", - Port: 7557, - Protocol: "TCP", - TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 7442}, - }, - }, - wantEndpoints: []model.IstioEndpoint{ - { - Address: "128.0.0.1", - ServicePortName: "http-7442", - EndpointPort: 7442, - }, - { - Address: "128.0.0.1", - ServicePortName: "tcp-8443", - EndpointPort: 7442, - }, - }, - }, - { - name: "single proxy ip multiple ports", - pods: []*coreV1.Pod{pod1}, - ips: []string{"128.0.0.1"}, - ports: []coreV1.ServicePort{ - { - Name: "tcp-port", - Port: 8080, - Protocol: "TCP", - TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 8080}, - }, - { - Name: "http-port", - Port: 9090, - Protocol: "TCP", - TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 9090}, - }, - }, - wantEndpoints: []model.IstioEndpoint{ - { - Address: "128.0.0.1", - ServicePortName: "tcp-port", - EndpointPort: 8080, - }, - { - Address: "128.0.0.1", - ServicePortName: "http-port", - EndpointPort: 9090, - }, - }, - }, - } - - for _, c := range testCases { - for mode, name := range EndpointModeNames { - mode := mode - t.Run(fmt.Sprintf("%s_%s", c.name, name), func(t *testing.T) { - // Setup kube caches - controller, fx := NewFakeControllerWithOptions(FakeControllerOptions{Mode: mode}) - go controller.Run(controller.stop) - // Wait for the caches to sync, otherwise we may hit race conditions where events are dropped - cache.WaitForCacheSync(controller.stop, controller.HasSynced) - defer controller.Stop() - addPods(t, controller, fx, c.pods...) - - createServiceWithTargetPorts(controller, "svc1", "nsa", - map[string]string{ - annotation.AlphaKubernetesServiceAccounts.Name: "acct4", - annotation.AlphaCanonicalServiceAccounts.Name: "acctvm2@gserviceaccount2.com", - }, - c.ports, map[string]string{"app": "test-app"}, t) - - ev := fx.Wait("service") - if ev == nil { - t.Fatal("Timeout creating service") - } - serviceInstances := controller.GetProxyServiceInstances(&model.Proxy{Metadata: &model.NodeMetadata{}, IPAddresses: c.ips}) - - for i, svc := range serviceInstances { - if svc.Endpoint.Address != c.wantEndpoints[i].Address { - t.Errorf("wrong endpoint address at #i endpoint, got %v want %v", svc.Endpoint.Address, c.wantEndpoints[i].Address) - } - if svc.Endpoint.EndpointPort != c.wantEndpoints[i].EndpointPort { - t.Errorf("wrong endpoint port at #i endpoint, got %v want %v", svc.Endpoint.EndpointPort, c.wantEndpoints[i].EndpointPort) - } - if svc.Endpoint.ServicePortName != c.wantEndpoints[i].ServicePortName { - t.Errorf("wrong svc port at #i endpoint, got %v want %v", svc.Endpoint.ServicePortName, c.wantEndpoints[i].ServicePortName) - } - } - }) - } - } -} - -func TestGetProxyServiceInstances_WorkloadInstance(t *testing.T) { - ctl, fx := NewFakeControllerWithOptions(FakeControllerOptions{}) - go ctl.Run(ctl.stop) - // Wait for the caches to sync, otherwise we may hit race conditions where events are dropped - cache.WaitForCacheSync(ctl.stop, ctl.HasSynced) - defer ctl.Stop() - - createService(ctl, "ratings", "bookinfo-ratings", - map[string]string{ - annotation.AlphaKubernetesServiceAccounts.Name: "ratings", - annotation.AlphaCanonicalServiceAccounts.Name: "ratings@gserviceaccount2.com", - }, - []int32{8080}, map[string]string{"app": "ratings"}, t) - fx.WaitOrFail(t, "service") - - createService(ctl, "details", "bookinfo-details", - map[string]string{ - annotation.AlphaKubernetesServiceAccounts.Name: "details", - annotation.AlphaCanonicalServiceAccounts.Name: "details@gserviceaccount2.com", - }, - []int32{9090}, map[string]string{"app": "details"}, t) - fx.WaitOrFail(t, "service") - - createService(ctl, "reviews", "bookinfo-reviews", - map[string]string{ - annotation.AlphaKubernetesServiceAccounts.Name: "reviews", - annotation.AlphaCanonicalServiceAccounts.Name: "reviews@gserviceaccount2.com", - }, - []int32{7070}, map[string]string{"app": "reviews"}, t) - fx.WaitOrFail(t, "service") - - wiRatings1 := &model.WorkloadInstance{ - Name: "ratings-1", - Namespace: "bookinfo-ratings", - Endpoint: &model.IstioEndpoint{ - Labels: labels.Instance{"app": "ratings"}, - Address: "2.2.2.21", - EndpointPort: 8080, - }, - } - - wiDetails1 := &model.WorkloadInstance{ - Name: "details-1", - Namespace: "bookinfo-details", - Endpoint: &model.IstioEndpoint{ - Labels: labels.Instance{"app": "details"}, - Address: "2.2.2.21", - EndpointPort: 9090, - }, - } - - wiReviews1 := &model.WorkloadInstance{ - Name: "reviews-1", - Namespace: "bookinfo-reviews", - Endpoint: &model.IstioEndpoint{ - Labels: labels.Instance{"app": "reviews"}, - Address: "3.3.3.31", - EndpointPort: 7070, - }, - } - - wiReviews2 := &model.WorkloadInstance{ - Name: "reviews-2", - Namespace: "bookinfo-reviews", - Endpoint: &model.IstioEndpoint{ - Labels: labels.Instance{"app": "reviews"}, - Address: "3.3.3.32", - EndpointPort: 7071, - }, - } - - wiProduct1 := &model.WorkloadInstance{ - Name: "productpage-1", - Namespace: "bookinfo-productpage", - Endpoint: &model.IstioEndpoint{ - Labels: labels.Instance{"app": "productpage"}, - Address: "4.4.4.41", - EndpointPort: 6060, - }, - } - - for _, wi := range []*model.WorkloadInstance{wiRatings1, wiDetails1, wiReviews1, wiReviews2, wiProduct1} { - ctl.WorkloadInstanceHandler(wi, model.EventAdd) // simulate adding a workload entry - } - - cases := []struct { - name string - proxy *model.Proxy - want []*model.ServiceInstance - }{ - { - name: "proxy with unspecified IP", - proxy: &model.Proxy{Metadata: &model.NodeMetadata{}, IPAddresses: nil}, - want: nil, - }, - { - name: "proxy with IP not in the registry", - proxy: &model.Proxy{Metadata: &model.NodeMetadata{}, IPAddresses: []string{"1.1.1.1"}}, - want: nil, - }, - { - name: "proxy with IP from the registry, 1 matching WE, but no matching Service", - proxy: &model.Proxy{Metadata: &model.NodeMetadata{}, IPAddresses: []string{"4.4.4.41"}}, - want: nil, - }, - { - name: "proxy with IP from the registry, 1 matching WE, and matching Service", - proxy: &model.Proxy{Metadata: &model.NodeMetadata{}, IPAddresses: []string{"3.3.3.31"}}, - want: []*model.ServiceInstance{{ - Service: &model.Service{ - Hostname: "reviews.bookinfo-reviews.svc.company.com", - }, - Endpoint: &model.IstioEndpoint{ - Labels: map[string]string{"app": "reviews"}, - Address: "3.3.3.31", - ServicePortName: "tcp-port", - EndpointPort: 7070, - }, - }}, - }, - { - name: "proxy with IP from the registry, 2 matching WE, and matching Service", - proxy: &model.Proxy{Metadata: &model.NodeMetadata{}, IPAddresses: []string{"2.2.2.21"}}, - want: []*model.ServiceInstance{{ - Service: &model.Service{ - Hostname: "details.bookinfo-details.svc.company.com", - }, - Endpoint: &model.IstioEndpoint{ - Labels: map[string]string{"app": "details"}, // should pick "details" because of ordering - Address: "2.2.2.21", - ServicePortName: "tcp-port", - EndpointPort: 9090, - }, - }}, - }, - { - name: "proxy with IP from the registry, 2 matching WE, and matching Service, and proxy ID equal to WE with a different address", - proxy: &model.Proxy{ - Metadata: &model.NodeMetadata{}, IPAddresses: []string{"2.2.2.21"}, - ID: "reviews-1.bookinfo-reviews", ConfigNamespace: "bookinfo-reviews", - }, - want: []*model.ServiceInstance{{ - Service: &model.Service{ - Hostname: "details.bookinfo-details.svc.company.com", - }, - Endpoint: &model.IstioEndpoint{ - Labels: map[string]string{"app": "details"}, // should pick "details" because of ordering - Address: "2.2.2.21", - ServicePortName: "tcp-port", - EndpointPort: 9090, - }, - }}, - }, - { - name: "proxy with IP from the registry, 2 matching WE, and matching Service, and proxy ID equal to WE name, but proxy.ID != proxy.ConfigNamespace", - proxy: &model.Proxy{ - Metadata: &model.NodeMetadata{}, IPAddresses: []string{"2.2.2.21"}, - ID: "ratings-1.bookinfo-ratings", ConfigNamespace: "wrong-namespace", - }, - want: []*model.ServiceInstance{{ - Service: &model.Service{ - Hostname: "details.bookinfo-details.svc.company.com", - }, - Endpoint: &model.IstioEndpoint{ - Labels: map[string]string{"app": "details"}, // should pick "details" because of ordering - Address: "2.2.2.21", - ServicePortName: "tcp-port", - EndpointPort: 9090, - }, - }}, - }, - { - name: "proxy with IP from the registry, 2 matching WE, and matching Service, and proxy.ID == WE name", - proxy: &model.Proxy{ - Metadata: &model.NodeMetadata{}, IPAddresses: []string{"2.2.2.21"}, - ID: "ratings-1.bookinfo-ratings", ConfigNamespace: "bookinfo-ratings", - }, - want: []*model.ServiceInstance{{ - Service: &model.Service{ - Hostname: "ratings.bookinfo-ratings.svc.company.com", - }, - Endpoint: &model.IstioEndpoint{ - Labels: map[string]string{"app": "ratings"}, // should pick "ratings" - Address: "2.2.2.21", - ServicePortName: "tcp-port", - EndpointPort: 8080, - }, - }}, - }, - { - name: "proxy with IP from the registry, 2 matching WE, and matching Service, and proxy.ID != WE name, but proxy.ConfigNamespace == WE namespace", - proxy: &model.Proxy{ - Metadata: &model.NodeMetadata{}, IPAddresses: []string{"2.2.2.21"}, - ID: "wrong-name.bookinfo-ratings", ConfigNamespace: "bookinfo-ratings", - }, - want: []*model.ServiceInstance{{ - Service: &model.Service{ - Hostname: "ratings.bookinfo-ratings.svc.company.com", - }, - Endpoint: &model.IstioEndpoint{ - Labels: map[string]string{"app": "ratings"}, // should pick "ratings" - Address: "2.2.2.21", - ServicePortName: "tcp-port", - EndpointPort: 8080, - }, - }}, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - got := ctl.GetProxyServiceInstances(tc.proxy) - - if diff := cmp.Diff(len(tc.want), len(got)); diff != "" { - t.Fatalf("GetProxyServiceInstances() returned unexpected number of service instances (--want/++got): %v", diff) - } - - for i := range tc.want { - if diff := cmp.Diff(tc.want[i].Service.Hostname, got[i].Service.Hostname); diff != "" { - t.Fatalf("GetProxyServiceInstances() returned unexpected value [%d].Service.Hostname (--want/++got): %v", i, diff) - } - if diff := cmp.Diff(tc.want[i].Endpoint, got[i].Endpoint); diff != "" { - t.Fatalf("GetProxyServiceInstances() returned unexpected value [%d].Endpoint (--want/++got): %v", i, diff) - } - } - }) - } -} - -func TestController_GetIstioServiceAccounts(t *testing.T) { - oldTrustDomain := spiffe.GetTrustDomain() - spiffe.SetTrustDomain(defaultFakeDomainSuffix) - defer spiffe.SetTrustDomain(oldTrustDomain) - - for mode, name := range EndpointModeNames { - mode := mode - t.Run(name, func(t *testing.T) { - controller, fx := NewFakeControllerWithOptions(FakeControllerOptions{Mode: mode}) - go controller.Run(controller.stop) - // Wait for the caches to sync, otherwise we may hit race conditions where events are dropped - cache.WaitForCacheSync(controller.stop, controller.HasSynced) - defer controller.Stop() - - sa1 := "acct1" - sa2 := "acct2" - sa3 := "acct3" - k8sSaOnVM := "acct4" - canonicalSaOnVM := "acctvm@gserviceaccount.com" - - pods := []*coreV1.Pod{ - generatePod("128.0.0.1", "pod1", "nsA", sa1, "node1", map[string]string{"app": "test-app"}, map[string]string{}), - generatePod("128.0.0.2", "pod2", "nsA", sa2, "node2", map[string]string{"app": "prod-app"}, map[string]string{}), - generatePod("128.0.0.3", "pod3", "nsB", sa3, "node1", map[string]string{"app": "prod-app"}, map[string]string{}), - } - addPods(t, controller, fx, pods...) - - createService(controller, "svc1", "nsA", - map[string]string{ - annotation.AlphaKubernetesServiceAccounts.Name: k8sSaOnVM, - annotation.AlphaCanonicalServiceAccounts.Name: canonicalSaOnVM, - }, - []int32{8080}, map[string]string{"app": "prod-app"}, t) - fx.Wait("service") - createService(controller, "svc2", "nsA", nil, []int32{8080}, map[string]string{"app": "staging-app"}, t) - fx.Wait("service") - - // Endpoints are generated by Kubernetes from pod labels and service selectors. - // Here we manually create them for mocking purpose. - svc1Ips := []string{"128.0.0.2"} - svc2Ips := make([]string, 0) - portNames := []string{"tcp-port"} - createEndpoints(t, controller, "svc1", "nsA", portNames, svc1Ips, nil, nil) - createEndpoints(t, controller, "svc2", "nsA", portNames, svc2Ips, nil, nil) - - // We expect only one EDS update with Endpoints. - <-fx.Events - - hostname := kube.ServiceHostname("svc1", "nsA", defaultFakeDomainSuffix) - svc := controller.GetService(hostname) - sa := controller.GetIstioServiceAccounts(svc, []int{8080}) - sort.Strings(sa) - expected := []string{ - canonicalSaOnVM, - "spiffe://company.com/ns/nsA/sa/" + sa2, - "spiffe://company.com/ns/nsA/sa/" + k8sSaOnVM, - } - if !reflect.DeepEqual(sa, expected) { - t.Fatalf("Unexpected service accounts %v (expecting %v)", sa, expected) - } - - hostname = kube.ServiceHostname("svc2", "nsA", defaultFakeDomainSuffix) - svc = controller.GetService(hostname) - sa = controller.GetIstioServiceAccounts(svc, []int{}) - if len(sa) != 0 { - t.Fatal("Failure: Expected to resolve 0 service accounts, but got: ", sa) - } - }) - } -} - -func TestController_Service(t *testing.T) { - for mode, name := range EndpointModeNames { - mode := mode - t.Run(name, func(t *testing.T) { - controller, fx := NewFakeControllerWithOptions(FakeControllerOptions{Mode: mode}) - go controller.Run(controller.stop) - // Wait for the caches to sync, otherwise we may hit race conditions where events are dropped - cache.WaitForCacheSync(controller.stop, controller.HasSynced) - defer controller.Stop() - // Use a timeout to keep the test from hanging. - - createService(controller, "svc1", "nsA", - map[string]string{}, - []int32{8080}, map[string]string{"test-app": "test-app-1"}, t) - <-fx.Events - createService(controller, "svc2", "nsA", - map[string]string{}, - []int32{8081}, map[string]string{"test-app": "test-app-2"}, t) - <-fx.Events - createService(controller, "svc3", "nsA", - map[string]string{}, - []int32{8082}, map[string]string{"test-app": "test-app-3"}, t) - <-fx.Events - createService(controller, "svc4", "nsA", - map[string]string{}, - []int32{8083}, map[string]string{"test-app": "test-app-4"}, t) - <-fx.Events - - expectedSvcList := []*model.Service{ - { - Hostname: kube.ServiceHostname("svc1", "nsA", defaultFakeDomainSuffix), - DefaultAddress: "10.0.0.1", - Ports: model.PortList{ - &model.Port{ - Name: "tcp-port", - Port: 8080, - Protocol: protocol.TCP, - }, - }, - }, - { - Hostname: kube.ServiceHostname("svc2", "nsA", defaultFakeDomainSuffix), - DefaultAddress: "10.0.0.1", - Ports: model.PortList{ - &model.Port{ - Name: "tcp-port", - Port: 8081, - Protocol: protocol.TCP, - }, - }, - }, - { - Hostname: kube.ServiceHostname("svc3", "nsA", defaultFakeDomainSuffix), - DefaultAddress: "10.0.0.1", - Ports: model.PortList{ - &model.Port{ - Name: "tcp-port", - Port: 8082, - Protocol: protocol.TCP, - }, - }, - }, - { - Hostname: kube.ServiceHostname("svc4", "nsA", defaultFakeDomainSuffix), - DefaultAddress: "10.0.0.1", - Ports: model.PortList{ - &model.Port{ - Name: "tcp-port", - Port: 8083, - Protocol: protocol.TCP, - }, - }, - }, - } - - svcList := controller.Services() - servicesEqual(svcList, expectedSvcList) - }) - } -} - -func TestController_ServiceWithFixedDiscoveryNamespaces(t *testing.T) { - meshWatcher := mesh.NewFixedWatcher(&meshconfig.MeshConfig{ - DiscoverySelectors: []*metaV1.LabelSelector{ - { - MatchLabels: map[string]string{ - "pilot-discovery": "enabled", - }, - }, - { - MatchExpressions: []metaV1.LabelSelectorRequirement{ - { - Key: "env", - Operator: metaV1.LabelSelectorOpIn, - Values: []string{"test", "dev"}, - }, - }, - }, - }, - }) - - svc1 := &model.Service{ - Hostname: kube.ServiceHostname("svc1", "nsA", defaultFakeDomainSuffix), - DefaultAddress: "10.0.0.1", - Ports: model.PortList{ - &model.Port{ - Name: "tcp-port", - Port: 8080, - Protocol: protocol.TCP, - }, - }, - } - svc2 := &model.Service{ - Hostname: kube.ServiceHostname("svc2", "nsA", defaultFakeDomainSuffix), - DefaultAddress: "10.0.0.1", - Ports: model.PortList{ - &model.Port{ - Name: "tcp-port", - Port: 8081, - Protocol: protocol.TCP, - }, - }, - } - svc3 := &model.Service{ - Hostname: kube.ServiceHostname("svc3", "nsB", defaultFakeDomainSuffix), - DefaultAddress: "10.0.0.1", - Ports: model.PortList{ - &model.Port{ - Name: "tcp-port", - Port: 8082, - Protocol: protocol.TCP, - }, - }, - } - svc4 := &model.Service{ - Hostname: kube.ServiceHostname("svc4", "nsB", defaultFakeDomainSuffix), - DefaultAddress: "10.0.0.1", - Ports: model.PortList{ - &model.Port{ - Name: "tcp-port", - Port: 8083, - Protocol: protocol.TCP, - }, - }, - } - - for mode, name := range EndpointModeNames { - mode := mode - t.Run(name, func(t *testing.T) { - controller, fx := NewFakeControllerWithOptions(FakeControllerOptions{ - Mode: mode, - MeshWatcher: meshWatcher, - }) - go controller.Run(controller.stop) - // Wait for the caches to sync, otherwise we may hit race conditions where events are dropped - cache.WaitForCacheSync(controller.stop, controller.HasSynced) - defer controller.Stop() - - nsA := "nsA" - nsB := "nsB" - - // event handlers should only be triggered for services in namespaces selected for discovery - createNamespace(t, controller.client, nsA, map[string]string{"pilot-discovery": "enabled"}) - createNamespace(t, controller.client, nsB, map[string]string{}) - - // wait for namespaces to be created - eventually(t, func() bool { - list, err := controller.client.CoreV1().Namespaces().List(context.TODO(), metaV1.ListOptions{}) - if err != nil { - t.Fatalf("error listing namespaces: %v", err) - } - return len(list.Items) == 2 - }) - - // service event handlers should trigger for svc1 and svc2 - createService(controller, "svc1", nsA, - map[string]string{}, - []int32{8080}, map[string]string{"test-app": "test-app-1"}, t) - if ev := fx.Wait("service"); ev == nil { - t.Fatal("Timeout creating service") - } - createService(controller, "svc2", nsA, - map[string]string{}, - []int32{8081}, map[string]string{"test-app": "test-app-2"}, t) - if ev := fx.Wait("service"); ev == nil { - t.Fatal("Timeout creating service") - } - // service event handlers should not trigger for svc3 and svc4 - createService(controller, "svc3", nsB, - map[string]string{}, - []int32{8082}, map[string]string{"test-app": "test-app-3"}, t) - createService(controller, "svc4", nsB, - map[string]string{}, - []int32{8083}, map[string]string{"test-app": "test-app-4"}, t) - - expectedSvcList := []*model.Service{svc1, svc2} - eventually(t, func() bool { - svcList := controller.Services() - return servicesEqual(svcList, expectedSvcList) - }) - - // test updating namespace with adding discovery label - updateNamespace(t, controller.client, nsB, map[string]string{"env": "test"}) - // service event handlers should trigger for svc3 and svc4 - if ev := fx.Wait("service"); ev == nil { - t.Fatal("Timeout creating service") - } - if ev := fx.Wait("service"); ev == nil { - t.Fatal("Timeout creating service") - } - expectedSvcList = []*model.Service{svc1, svc2, svc3, svc4} - eventually(t, func() bool { - svcList := controller.Services() - return servicesEqual(svcList, expectedSvcList) - }) - - // test updating namespace by removing discovery label - updateNamespace(t, controller.client, nsA, map[string]string{"pilot-discovery": "disabled"}) - // service event handlers should trigger for svc1 and svc2 - if ev := fx.Wait("service"); ev == nil { - t.Fatal("Timeout creating service") - } - if ev := fx.Wait("service"); ev == nil { - t.Fatal("Timeout creating service") - } - expectedSvcList = []*model.Service{svc3, svc4} - eventually(t, func() bool { - svcList := controller.Services() - return servicesEqual(svcList, expectedSvcList) - }) - }) - } -} - -func TestController_ServiceWithChangingDiscoveryNamespaces(t *testing.T) { - svc1 := &model.Service{ - Hostname: kube.ServiceHostname("svc1", "nsA", defaultFakeDomainSuffix), - DefaultAddress: "10.0.0.1", - Ports: model.PortList{ - &model.Port{ - Name: "tcp-port", - Port: 8080, - Protocol: protocol.TCP, - }, - }, - } - svc2 := &model.Service{ - Hostname: kube.ServiceHostname("svc2", "nsA", defaultFakeDomainSuffix), - DefaultAddress: "10.0.0.1", - Ports: model.PortList{ - &model.Port{ - Name: "tcp-port", - Port: 8081, - Protocol: protocol.TCP, - }, - }, - } - svc3 := &model.Service{ - Hostname: kube.ServiceHostname("svc3", "nsB", defaultFakeDomainSuffix), - DefaultAddress: "10.0.0.1", - Ports: model.PortList{ - &model.Port{ - Name: "tcp-port", - Port: 8082, - Protocol: protocol.TCP, - }, - }, - } - svc4 := &model.Service{ - Hostname: kube.ServiceHostname("svc4", "nsC", defaultFakeDomainSuffix), - DefaultAddress: "10.0.0.1", - Ports: model.PortList{ - &model.Port{ - Name: "tcp-port", - Port: 8083, - Protocol: protocol.TCP, - }, - }, - } - - updateMeshConfig := func( - meshConfig *meshconfig.MeshConfig, - expectedSvcList []*model.Service, - expectedNumSvcEvents int, - testMeshWatcher *mesh.TestWatcher, - fx *FakeXdsUpdater, - controller *FakeController, - ) { - // update meshConfig - if err := testMeshWatcher.Update(meshConfig, 5); err != nil { - t.Fatalf("%v", err) - } - - // assert firing of service events - for i := 0; i < expectedNumSvcEvents; i++ { - if ev := fx.Wait("service"); ev == nil { - t.Fatal("timed out waiting for service event") - } - } - - eventually(t, func() bool { - svcList := controller.Services() - return servicesEqual(svcList, expectedSvcList) - }) - } - - for mode, name := range EndpointModeNames { - mode := mode - t.Run(name, func(t *testing.T) { - client := kubelib.NewFakeClient() - meshWatcher := mesh.NewTestWatcher(&meshconfig.MeshConfig{}) - discoveryNamespacesFilter := filter.NewDiscoveryNamespacesFilter( - client.KubeInformer().Core().V1().Namespaces().Lister(), - meshWatcher.Mesh().DiscoverySelectors, - ) - - controller, fx := NewFakeControllerWithOptions(FakeControllerOptions{ - Client: client, - Mode: mode, - MeshWatcher: meshWatcher, - DiscoveryNamespacesFilter: discoveryNamespacesFilter, - }) - go controller.Run(controller.stop) - // Wait for the caches to sync, otherwise we may hit race conditions where events are dropped - cache.WaitForCacheSync(controller.stop, controller.HasSynced) - defer controller.Stop() - - nsA := "nsA" - nsB := "nsB" - nsC := "nsC" - - createNamespace(t, controller.client, nsA, map[string]string{"app": "foo"}) - createNamespace(t, controller.client, nsB, map[string]string{"app": "bar"}) - createNamespace(t, controller.client, nsC, map[string]string{"app": "baz"}) - - // wait for namespaces to be created - eventually(t, func() bool { - list, err := controller.client.CoreV1().Namespaces().List(context.TODO(), metaV1.ListOptions{}) - if err != nil { - t.Fatalf("error listing namespaces: %v", err) - } - return len(list.Items) == 3 - }) - - // assert that namespace membership has been updated - eventually(t, func() bool { - members := discoveryNamespacesFilter.GetMembers() - return members.Has(nsA) && members.Has(nsB) && members.Has(nsC) - }) - - // service event handlers should trigger for all svcs - createService(controller, "svc1", nsA, - map[string]string{}, - []int32{8080}, map[string]string{"test-app": "test-app-1"}, t) - if ev := fx.Wait("service"); ev == nil { - t.Fatal("Timeout creating service") - } - createService(controller, "svc2", nsA, - map[string]string{}, - []int32{8081}, map[string]string{"test-app": "test-app-2"}, t) - if ev := fx.Wait("service"); ev == nil { - t.Fatal("Timeout creating service") - } - createService(controller, "svc3", nsB, - map[string]string{}, - []int32{8082}, map[string]string{"test-app": "test-app-3"}, t) - if ev := fx.Wait("service"); ev == nil { - t.Fatal("Timeout creating service") - } - createService(controller, "svc4", nsC, - map[string]string{}, - []int32{8083}, map[string]string{"test-app": "test-app-4"}, t) - if ev := fx.Wait("service"); ev == nil { - t.Fatal("Timeout creating service") - } - - expectedSvcList := []*model.Service{svc1, svc2, svc3, svc4} - eventually(t, func() bool { - svcList := controller.Services() - return servicesEqual(svcList, expectedSvcList) - }) - - // restrict namespaces to nsA (expect 2 delete events for svc3 and svc4) - updateMeshConfig( - &meshconfig.MeshConfig{ - DiscoverySelectors: []*metaV1.LabelSelector{ - { - MatchLabels: map[string]string{ - "app": "foo", - }, - }, - }, - }, - []*model.Service{svc1, svc2}, - 2, - meshWatcher, - fx, - controller, - ) - - // restrict namespaces to nsB (1 create event should trigger for nsB service and 2 delete events for nsA services) - updateMeshConfig( - &meshconfig.MeshConfig{ - DiscoverySelectors: []*metaV1.LabelSelector{ - { - MatchLabels: map[string]string{ - "app": "bar", - }, - }, - }, - }, - []*model.Service{svc3}, - 3, - meshWatcher, - fx, - controller, - ) - - // expand namespaces to nsA and nsB with selectors (2 create events should trigger for nsA services) - updateMeshConfig( - &meshconfig.MeshConfig{ - DiscoverySelectors: []*metaV1.LabelSelector{ - { - MatchExpressions: []metaV1.LabelSelectorRequirement{ - { - Key: "app", - Operator: metaV1.LabelSelectorOpIn, - Values: []string{"foo", "bar"}, - }, - }, - }, - }, - }, - []*model.Service{svc1, svc2, svc3}, - 2, - meshWatcher, - fx, - controller, - ) - - // permit all discovery namespaces by omitting discovery selectors (1 create event should trigger for the nsC service) - updateMeshConfig( - &meshconfig.MeshConfig{ - DiscoverySelectors: []*metaV1.LabelSelector{}, - }, - []*model.Service{svc1, svc2, svc3, svc4}, - 1, - meshWatcher, - fx, - controller, - ) - }) - } -} - -func TestInstancesByPort_WorkloadInstances(t *testing.T) { - ctl, fx := NewFakeControllerWithOptions(FakeControllerOptions{}) - go ctl.Run(ctl.stop) - // Wait for the caches to sync, otherwise we may hit race conditions where events are dropped - cache.WaitForCacheSync(ctl.stop, ctl.HasSynced) - defer ctl.Stop() - - createServiceWithTargetPorts(ctl, "ratings", "bookinfo-ratings", - map[string]string{ - annotation.AlphaKubernetesServiceAccounts.Name: "ratings", - annotation.AlphaCanonicalServiceAccounts.Name: "ratings@gserviceaccount2.com", - }, - []coreV1.ServicePort{ - { - Name: "http-port", - Port: 8080, - Protocol: "TCP", - TargetPort: intstr.IntOrString{Type: intstr.String, StrVal: "http"}, - }, - }, - map[string]string{"app": "ratings"}, t) - fx.WaitOrFail(t, "service") - - wiRatings1 := &model.WorkloadInstance{ - Name: "ratings-1", - Namespace: "bookinfo-ratings", - Endpoint: &model.IstioEndpoint{ - Labels: labels.Instance{"app": "ratings"}, - Address: "2.2.2.2", - EndpointPort: 8081, // should be ignored since it doesn't define PortMap - }, - } - - wiRatings2 := &model.WorkloadInstance{ - Name: "ratings-2", - Namespace: "bookinfo-ratings", - Endpoint: &model.IstioEndpoint{ - Labels: labels.Instance{"app": "ratings"}, - Address: "2.2.2.2", - }, - PortMap: map[string]uint32{ - "http": 8082, // should be used - }, - } - - wiRatings3 := &model.WorkloadInstance{ - Name: "ratings-3", - Namespace: "bookinfo-ratings", - Endpoint: &model.IstioEndpoint{ - Labels: labels.Instance{"app": "ratings"}, - Address: "2.2.2.2", - }, - PortMap: map[string]uint32{ - "http": 8083, // should be used - }, - } - - for _, wi := range []*model.WorkloadInstance{wiRatings1, wiRatings2, wiRatings3} { - ctl.WorkloadInstanceHandler(wi, model.EventAdd) // simulate adding a workload entry - } - - // get service object - - svcs := ctl.Services() - if len(svcs) != 1 { - t.Fatalf("failed to get services (%v)", svcs) - } - - // get service instances - - instances := ctl.InstancesByPort(svcs[0], 8080, nil) - - want := []string{"2.2.2.2:8082", "2.2.2.2:8083"} // expect both WorkloadEntries even though they have the same IP - - var got []string - for _, instance := range instances { - got = append(got, net.JoinHostPort(instance.Endpoint.Address, strconv.Itoa(int(instance.Endpoint.EndpointPort)))) - } - sort.Strings(got) - - if diff := cmp.Diff(want, got); diff != "" { - t.Fatalf("InstancesByPort() returned unexpected list of endpoints (--want/++got): %v", diff) - } -} - -func TestExternalNameServiceInstances(t *testing.T) { - for mode, name := range EndpointModeNames { - mode := mode - t.Run(name, func(t *testing.T) { - controller, fx := NewFakeControllerWithOptions(FakeControllerOptions{Mode: mode}) - go controller.Run(controller.stop) - // Wait for the caches to sync, otherwise we may hit race conditions where events are dropped - cache.WaitForCacheSync(controller.stop, controller.HasSynced) - defer controller.Stop() - createExternalNameService(controller, "svc5", "nsA", - []int32{1, 2, 3}, "foo.co", t, fx.Events) - - converted := controller.Services() - if len(converted) != 1 { - t.Fatalf("failed to get services (%v)s", converted) - } - instances := controller.InstancesByPort(converted[0], 1, nil) - if len(instances) != 1 { - t.Fatalf("expected 1 instance, got %v", instances) - } - if instances[0].ServicePort.Port != 1 { - t.Fatalf("expected port 1, got %v", instances[0].ServicePort.Port) - } - }) - } -} - -func TestController_ExternalNameService(t *testing.T) { - for mode, name := range EndpointModeNames { - mode := mode - t.Run(name, func(t *testing.T) { - deleteWg := sync.WaitGroup{} - controller, fx := NewFakeControllerWithOptions(FakeControllerOptions{ - Mode: mode, - ServiceHandler: func(_ *model.Service, e model.Event) { - if e == model.EventDelete { - deleteWg.Done() - } - }, - }) - go controller.Run(controller.stop) - // Wait for the caches to sync, otherwise we may hit race conditions where events are dropped - cache.WaitForCacheSync(controller.stop, controller.HasSynced) - defer controller.Stop() - // Use a timeout to keep the test from hanging. - - k8sSvcs := []*coreV1.Service{ - createExternalNameService(controller, "svc1", "nsA", - []int32{8080}, "test-app-1.test.svc."+defaultFakeDomainSuffix, t, fx.Events), - createExternalNameService(controller, "svc2", "nsA", - []int32{8081}, "test-app-2.test.svc."+defaultFakeDomainSuffix, t, fx.Events), - createExternalNameService(controller, "svc3", "nsA", - []int32{8082}, "test-app-3.test.pod."+defaultFakeDomainSuffix, t, fx.Events), - createExternalNameService(controller, "svc4", "nsA", - []int32{8083}, "g.co", t, fx.Events), - } - - expectedSvcList := []*model.Service{ - { - Hostname: kube.ServiceHostname("svc1", "nsA", defaultFakeDomainSuffix), - Ports: model.PortList{ - &model.Port{ - Name: "tcp-port", - Port: 8080, - Protocol: protocol.TCP, - }, - }, - MeshExternal: true, - Resolution: model.DNSLB, - }, - { - Hostname: kube.ServiceHostname("svc2", "nsA", defaultFakeDomainSuffix), - Ports: model.PortList{ - &model.Port{ - Name: "tcp-port", - Port: 8081, - Protocol: protocol.TCP, - }, - }, - MeshExternal: true, - Resolution: model.DNSLB, - }, - { - Hostname: kube.ServiceHostname("svc3", "nsA", defaultFakeDomainSuffix), - Ports: model.PortList{ - &model.Port{ - Name: "tcp-port", - Port: 8082, - Protocol: protocol.TCP, - }, - }, - MeshExternal: true, - Resolution: model.DNSLB, - }, - { - Hostname: kube.ServiceHostname("svc4", "nsA", defaultFakeDomainSuffix), - Ports: model.PortList{ - &model.Port{ - Name: "tcp-port", - Port: 8083, - Protocol: protocol.TCP, - }, - }, - MeshExternal: true, - Resolution: model.DNSLB, - }, - } - - svcList := controller.Services() - if len(svcList) != len(expectedSvcList) { - t.Fatalf("Expecting %d service but got %d\r\n", len(expectedSvcList), len(svcList)) - } - for i, exp := range expectedSvcList { - if exp.Hostname != svcList[i].Hostname { - t.Fatalf("got hostname of %dst service, got:\n%#v\nwanted:\n%#v\n", i+1, svcList[i].Hostname, exp.Hostname) - } - if !reflect.DeepEqual(exp.Ports, svcList[i].Ports) { - t.Fatalf("got ports of %dst service, got:\n%#v\nwanted:\n%#v\n", i+1, svcList[i].Ports, exp.Ports) - } - if svcList[i].MeshExternal != exp.MeshExternal { - t.Fatalf("i=%v, MeshExternal==%v, should be %v: externalName='%s'", i+1, exp.MeshExternal, svcList[i].MeshExternal, k8sSvcs[i].Spec.ExternalName) - } - if svcList[i].Resolution != exp.Resolution { - t.Fatalf("i=%v, Resolution=='%v', should be '%v'", i+1, svcList[i].Resolution, exp.Resolution) - } - instances := controller.InstancesByPort(svcList[i], svcList[i].Ports[0].Port, nil) - if len(instances) != 1 { - t.Fatalf("should be exactly 1 instance: len(instances) = %v", len(instances)) - } - if instances[0].Endpoint.Address != k8sSvcs[i].Spec.ExternalName { - t.Fatalf("wrong instance endpoint address: '%s' != '%s'", instances[0].Endpoint.Address, k8sSvcs[i].Spec.ExternalName) - } - } - - deleteWg.Add(len(k8sSvcs)) - for _, s := range k8sSvcs { - deleteExternalNameService(controller, s.Name, s.Namespace, t, fx.Events) - } - deleteWg.Wait() - - svcList = controller.Services() - if len(svcList) != 0 { - t.Fatalf("Should have 0 services at this point") - } - for _, exp := range expectedSvcList { - instances := controller.InstancesByPort(exp, exp.Ports[0].Port, nil) - if len(instances) != 0 { - t.Fatalf("should be exactly 0 instance: len(instances) = %v", len(instances)) - } - } - }) - } -} - -func createEndpoints(t *testing.T, controller *FakeController, name, namespace string, - portNames, ips []string, refs []*coreV1.ObjectReference, labels map[string]string) { - if labels == nil { - labels = make(map[string]string) - } - // Add the reference to the service. Used by EndpointSlice logic only. - labels[discovery.LabelServiceName] = name - - if refs == nil { - refs = make([]*coreV1.ObjectReference, len(ips)) - } - var portNum int32 = 1001 - eas := make([]coreV1.EndpointAddress, 0) - for i, ip := range ips { - eas = append(eas, coreV1.EndpointAddress{IP: ip, TargetRef: refs[i]}) - } - - eps := make([]coreV1.EndpointPort, 0) - for _, name := range portNames { - eps = append(eps, coreV1.EndpointPort{Name: name, Port: portNum}) - } - - endpoint := &coreV1.Endpoints{ - ObjectMeta: metaV1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: labels, - }, - Subsets: []coreV1.EndpointSubset{{ - Addresses: eas, - Ports: eps, - }}, - } - if _, err := controller.client.CoreV1().Endpoints(namespace).Create(context.TODO(), endpoint, metaV1.CreateOptions{}); err != nil { - if errors.IsAlreadyExists(err) { - _, err = controller.client.CoreV1().Endpoints(namespace).Update(context.TODO(), endpoint, metaV1.UpdateOptions{}) - } - if err != nil { - t.Fatalf("failed to create endpoints %s in namespace %s (error %v)", name, namespace, err) - } - } - - // Create endpoint slice as well - esps := make([]discovery.EndpointPort, 0) - for _, name := range portNames { - n := name // Create a stable reference to take the pointer from - esps = append(esps, discovery.EndpointPort{Name: &n, Port: &portNum}) - } - - var sliceEndpoint []discovery.Endpoint - for i, ip := range ips { - sliceEndpoint = append(sliceEndpoint, discovery.Endpoint{ - Addresses: []string{ip}, - TargetRef: refs[i], - }) - } - endpointSlice := &discovery.EndpointSlice{ - ObjectMeta: metaV1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: labels, - }, - Endpoints: sliceEndpoint, - Ports: esps, - } - if _, err := controller.client.DiscoveryV1().EndpointSlices(namespace).Create(context.TODO(), endpointSlice, metaV1.CreateOptions{}); err != nil { - if errors.IsAlreadyExists(err) { - _, err = controller.client.DiscoveryV1().EndpointSlices(namespace).Update(context.TODO(), endpointSlice, metaV1.UpdateOptions{}) - } - if err != nil { - t.Fatalf("failed to create endpoint slice %s in namespace %s (error %v)", name, namespace, err) - } - } -} - -func updateEndpoints(controller *FakeController, name, namespace string, portNames, ips []string, t *testing.T) { - var portNum int32 = 1001 - eas := make([]coreV1.EndpointAddress, 0) - for _, ip := range ips { - eas = append(eas, coreV1.EndpointAddress{IP: ip}) - } - - eps := make([]coreV1.EndpointPort, 0) - for _, name := range portNames { - eps = append(eps, coreV1.EndpointPort{Name: name, Port: portNum}) - } - - endpoint := &coreV1.Endpoints{ - ObjectMeta: metaV1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Subsets: []coreV1.EndpointSubset{{ - Addresses: eas, - Ports: eps, - }}, - } - if _, err := controller.client.CoreV1().Endpoints(namespace).Update(context.TODO(), endpoint, metaV1.UpdateOptions{}); err != nil { - t.Fatalf("failed to update endpoints %s in namespace %s (error %v)", name, namespace, err) - } - - // Update endpoint slice as well - esps := make([]discovery.EndpointPort, 0) - for i := range portNames { - esps = append(esps, discovery.EndpointPort{Name: &portNames[i], Port: &portNum}) - } - endpointSlice := &discovery.EndpointSlice{ - ObjectMeta: metaV1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: map[string]string{ - discovery.LabelServiceName: name, - }, - }, - Endpoints: []discovery.Endpoint{ - { - Addresses: ips, - }, - }, - Ports: esps, - } - if _, err := controller.client.DiscoveryV1().EndpointSlices(namespace).Update(context.TODO(), endpointSlice, metaV1.UpdateOptions{}); err != nil { - t.Errorf("failed to create endpoint slice %s in namespace %s (error %v)", name, namespace, err) - } -} - -func createServiceWithTargetPorts(controller *FakeController, name, namespace string, annotations map[string]string, - svcPorts []coreV1.ServicePort, selector map[string]string, t *testing.T) { - service := &coreV1.Service{ - ObjectMeta: metaV1.ObjectMeta{ - Name: name, - Namespace: namespace, - Annotations: annotations, - }, - Spec: coreV1.ServiceSpec{ - ClusterIP: "10.0.0.1", // FIXME: generate? - Ports: svcPorts, - Selector: selector, - Type: coreV1.ServiceTypeClusterIP, - }, - } - - _, err := controller.client.CoreV1().Services(namespace).Create(context.TODO(), service, metaV1.CreateOptions{}) - if err != nil { - t.Fatalf("Cannot create service %s in namespace %s (error: %v)", name, namespace, err) - } -} - -func createService(controller *FakeController, name, namespace string, annotations map[string]string, - ports []int32, selector map[string]string, t *testing.T) { - svcPorts := make([]coreV1.ServicePort, 0) - for _, p := range ports { - svcPorts = append(svcPorts, coreV1.ServicePort{ - Name: "tcp-port", - Port: p, - Protocol: "http", - }) - } - service := &coreV1.Service{ - ObjectMeta: metaV1.ObjectMeta{ - Name: name, - Namespace: namespace, - Annotations: annotations, - }, - Spec: coreV1.ServiceSpec{ - ClusterIP: "10.0.0.1", // FIXME: generate? - Ports: svcPorts, - Selector: selector, - Type: coreV1.ServiceTypeClusterIP, - }, - } - - _, err := controller.client.CoreV1().Services(namespace).Create(context.TODO(), service, metaV1.CreateOptions{}) - if err != nil { - t.Fatalf("Cannot create service %s in namespace %s (error: %v)", name, namespace, err) - } -} - -func getService(controller *FakeController, name, namespace string, t *testing.T) *coreV1.Service { - svc, err := controller.client.CoreV1().Services(namespace).Get(context.TODO(), name, metaV1.GetOptions{}) - if err != nil { - t.Fatalf("Cannot get service %s in namespace %s (error: %v)", name, namespace, err) - } - return svc -} - -func updateService(controller *FakeController, svc *coreV1.Service, t *testing.T) *coreV1.Service { - svc, err := controller.client.CoreV1().Services(svc.Namespace).Update(context.TODO(), svc, metaV1.UpdateOptions{}) - if err != nil { - t.Fatalf("Cannot update service %s in namespace %s (error: %v)", svc.Name, svc.Namespace, err) - } - return svc -} - -func createServiceWithoutClusterIP(controller *FakeController, name, namespace string, annotations map[string]string, - ports []int32, selector map[string]string, t *testing.T) { - svcPorts := make([]coreV1.ServicePort, 0) - for _, p := range ports { - svcPorts = append(svcPorts, coreV1.ServicePort{ - Name: "tcp-port", - Port: p, - Protocol: "http", - }) - } - service := &coreV1.Service{ - ObjectMeta: metaV1.ObjectMeta{ - Name: name, - Namespace: namespace, - Annotations: annotations, - }, - Spec: coreV1.ServiceSpec{ - ClusterIP: coreV1.ClusterIPNone, - Ports: svcPorts, - Selector: selector, - Type: coreV1.ServiceTypeClusterIP, - }, - } - - _, err := controller.client.CoreV1().Services(namespace).Create(context.TODO(), service, metaV1.CreateOptions{}) - if err != nil { - t.Fatalf("Cannot create service %s in namespace %s (error: %v)", name, namespace, err) - } -} - -// nolint: unparam -func createExternalNameService(controller *FakeController, name, namespace string, - ports []int32, externalName string, t *testing.T, xdsEvents <-chan FakeXdsEvent) *coreV1.Service { - defer func() { - <-xdsEvents - }() - - svcPorts := make([]coreV1.ServicePort, 0) - for _, p := range ports { - svcPorts = append(svcPorts, coreV1.ServicePort{ - Name: "tcp-port", - Port: p, - Protocol: "http", - }) - } - service := &coreV1.Service{ - ObjectMeta: metaV1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: coreV1.ServiceSpec{ - Ports: svcPorts, - Type: coreV1.ServiceTypeExternalName, - ExternalName: externalName, - }, - } - - _, err := controller.client.CoreV1().Services(namespace).Create(context.TODO(), service, metaV1.CreateOptions{}) - if err != nil { - t.Fatalf("Cannot create service %s in namespace %s (error: %v)", name, namespace, err) - } - return service -} - -func deleteExternalNameService(controller *FakeController, name, namespace string, t *testing.T, xdsEvents <-chan FakeXdsEvent) { - defer func() { - <-xdsEvents - }() - - err := controller.client.CoreV1().Services(namespace).Delete(context.TODO(), name, metaV1.DeleteOptions{}) - if err != nil { - t.Fatalf("Cannot delete service %s in namespace %s (error: %v)", name, namespace, err) - } -} - -func servicesEqual(svcList, expectedSvcList []*model.Service) bool { - if len(svcList) != len(expectedSvcList) { - return false - } - for i, exp := range expectedSvcList { - if exp.Hostname != svcList[i].Hostname { - return false - } - if exp.DefaultAddress != svcList[i].DefaultAddress { - return false - } - if !reflect.DeepEqual(exp.Ports, svcList[i].Ports) { - return false - } - } - return true -} - -func addPods(t *testing.T, controller *FakeController, fx *FakeXdsUpdater, pods ...*coreV1.Pod) { - for _, pod := range pods { - p, _ := controller.client.CoreV1().Pods(pod.Namespace).Get(context.TODO(), pod.Name, metaV1.GetOptions{}) - var newPod *coreV1.Pod - var err error - if p == nil { - newPod, err = controller.client.CoreV1().Pods(pod.Namespace).Create(context.TODO(), pod, metaV1.CreateOptions{}) - if err != nil { - t.Fatalf("Cannot create %s in namespace %s (error: %v)", pod.ObjectMeta.Name, pod.ObjectMeta.Namespace, err) - } - } else { - newPod, err = controller.client.CoreV1().Pods(pod.Namespace).Update(context.TODO(), pod, metaV1.UpdateOptions{}) - if err != nil { - t.Fatalf("Cannot update %s in namespace %s (error: %v)", pod.ObjectMeta.Name, pod.ObjectMeta.Namespace, err) - } - } - - setPodReady(newPod) - // Apiserver doesn't allow Create/Update to modify the pod status. Creating doesn't result in - // events - since PodIP will be "". - newPod.Status.PodIP = pod.Status.PodIP - newPod.Status.Phase = coreV1.PodRunning - _, _ = controller.client.CoreV1().Pods(pod.Namespace).UpdateStatus(context.TODO(), newPod, metaV1.UpdateOptions{}) - if err := waitForPod(controller, pod.Status.PodIP); err != nil { - t.Fatal(err) - } - // pod first time occur will trigger proxy push - fx.Wait("proxy") - } -} - -func setPodReady(pod *coreV1.Pod) { - pod.Status.Conditions = []coreV1.PodCondition{ - { - Type: coreV1.PodReady, - Status: coreV1.ConditionTrue, - LastTransitionTime: metaV1.Now(), - }, - } -} - -func generatePod(ip, name, namespace, saName, node string, labels map[string]string, annotations map[string]string) *coreV1.Pod { - automount := false - return &coreV1.Pod{ - ObjectMeta: metaV1.ObjectMeta{ - Name: name, - Labels: labels, - Annotations: annotations, - Namespace: namespace, - }, - Spec: coreV1.PodSpec{ - ServiceAccountName: saName, - NodeName: node, - AutomountServiceAccountToken: &automount, - // Validation requires this - Containers: []coreV1.Container{ - { - Name: "test", - Image: "ununtu", - }, - }, - }, - // The cache controller uses this as key, required by our impl. - Status: coreV1.PodStatus{ - Conditions: []coreV1.PodCondition{ - { - Type: coreV1.PodReady, - Status: coreV1.ConditionTrue, - LastTransitionTime: metaV1.Now(), - }, - }, - PodIP: ip, - HostIP: ip, - Phase: coreV1.PodRunning, - }, - } -} - -func generateNode(name string, labels map[string]string) *coreV1.Node { - return &coreV1.Node{ - TypeMeta: metaV1.TypeMeta{ - Kind: "Node", - APIVersion: "v1", - }, - ObjectMeta: metaV1.ObjectMeta{ - Name: name, - Labels: labels, - }, - } -} - -func addNodes(t *testing.T, controller *FakeController, nodes ...*coreV1.Node) { - fakeClient := controller.client - for _, node := range nodes { - _, err := fakeClient.CoreV1().Nodes().Create(context.TODO(), node, metaV1.CreateOptions{}) - if errors.IsAlreadyExists(err) { - if _, err := fakeClient.CoreV1().Nodes().Update(context.TODO(), node, metaV1.UpdateOptions{}); err != nil { - t.Fatal(err) - } - } else if err != nil { - t.Fatal(err) - } - if err := waitForNode(controller, node.Name); err != nil { - t.Fatal(err) - } - } -} - -func TestEndpointUpdate(t *testing.T) { - for mode, name := range EndpointModeNames { - mode := mode - t.Run(name, func(t *testing.T) { - controller, fx := NewFakeControllerWithOptions(FakeControllerOptions{Mode: mode}) - go controller.Run(controller.stop) - // Wait for the caches to sync, otherwise we may hit race conditions where events are dropped - cache.WaitForCacheSync(controller.stop, controller.HasSynced) - defer controller.Stop() - - pod1 := generatePod("128.0.0.1", "pod1", "nsA", "", "node1", map[string]string{"app": "prod-app"}, map[string]string{}) - pods := []*coreV1.Pod{pod1} - addPods(t, controller, fx, pods...) - - // 1. incremental eds for normal service endpoint update - createService(controller, "svc1", "nsa", nil, - []int32{8080}, map[string]string{"app": "prod-app"}, t) - if ev := fx.Wait("service"); ev == nil { - t.Fatal("Timeout creating service") - } - - // Endpoints are generated by Kubernetes from pod labels and service selectors. - // Here we manually create them for mocking purpose. - svc1Ips := []string{"128.0.0.1"} - portNames := []string{"tcp-port"} - // Create 1 endpoint that refers to a pod in the same namespace. - createEndpoints(t, controller, "svc1", "nsa", portNames, svc1Ips, nil, nil) - if ev := fx.Wait("eds"); ev == nil { - t.Fatalf("Timeout incremental eds") - } - - // delete normal service - err := controller.client.CoreV1().Services("nsa").Delete(context.TODO(), "svc1", metaV1.DeleteOptions{}) - if err != nil { - t.Fatalf("Cannot delete service (error: %v)", err) - } - if ev := fx.Wait("service"); ev == nil { - t.Fatalf("Timeout deleting service") - } - - // 2. full xds push request for headless service endpoint update - - // create a headless service - createServiceWithoutClusterIP(controller, "svc1", "nsa", nil, - []int32{8080}, map[string]string{"app": "prod-app"}, t) - if ev := fx.Wait("service"); ev == nil { - t.Fatalf("Timeout creating service") - } - - // Create 1 endpoint that refers to a pod in the same namespace. - svc1Ips = append(svc1Ips, "128.0.0.2") - updateEndpoints(controller, "svc1", "nsa", portNames, svc1Ips, t) - ev := fx.Wait("xds") - if ev == nil { - t.Fatalf("Timeout xds push") - } - if ev.ID != string(kube.ServiceHostname("svc1", "nsa", controller.opts.DomainSuffix)) { - t.Errorf("Expect service %s updated, but got %s", - kube.ServiceHostname("svc1", "nsa", controller.opts.DomainSuffix), ev.ID) - } - }) - } -} - -// Validates that when Pilot sees Endpoint before the corresponding Pod, it triggers endpoint event on pod event. -func TestEndpointUpdateBeforePodUpdate(t *testing.T) { - for mode, name := range EndpointModeNames { - mode := mode - t.Run(name, func(t *testing.T) { - controller, fx := NewFakeControllerWithOptions(FakeControllerOptions{Mode: mode}) - go controller.Run(controller.stop) - // Wait for the caches to sync, otherwise we may hit race conditions where events are dropped - cache.WaitForCacheSync(controller.stop, controller.HasSynced) - // Setup kube caches - defer controller.Stop() - addNodes(t, controller, generateNode("node1", map[string]string{NodeZoneLabel: "zone1", NodeRegionLabel: "region1", label.TopologySubzone.Name: "subzone1"})) - // Setup help functions to make the test more explicit - addPod := func(name, ip string) { - pod := generatePod(ip, name, "nsA", name, "node1", map[string]string{"app": "prod-app"}, map[string]string{}) - addPods(t, controller, fx, pod) - } - deletePod := func(name, ip string) { - if err := controller.client.CoreV1().Pods("nsA").Delete(context.TODO(), name, metaV1.DeleteOptions{}); err != nil { - t.Fatal(err) - } - retry.UntilSuccessOrFail(t, func() error { - controller.pods.RLock() - defer controller.pods.RUnlock() - if _, ok := controller.pods.podsByIP[ip]; ok { - return fmt.Errorf("pod still present") - } - return nil - }, retry.Timeout(time.Second)) - } - addService := func(name string) { - // create service - createService(controller, name, "nsA", nil, - []int32{8080}, map[string]string{"app": "prod-app"}, t) - if ev := fx.Wait("service"); ev == nil { - t.Fatal("Timeout creating service") - } - } - addEndpoint := func(svcName string, ips []string, pods []string) { - var refs []*coreV1.ObjectReference - for _, pod := range pods { - if pod == "" { - refs = append(refs, nil) - } else { - refs = append(refs, &coreV1.ObjectReference{ - Kind: "Pod", - Namespace: "nsA", - Name: pod, - }) - } - } - createEndpoints(t, controller, svcName, "nsA", []string{"tcp-port"}, ips, refs, nil) - } - assertEndpointsEvent := func(ips []string, pods []string) { - t.Helper() - ev := fx.Wait("eds") - if ev == nil { - t.Fatalf("Timeout incremental eds") - } - var gotIps []string - for _, e := range ev.Endpoints { - gotIps = append(gotIps, e.Address) - } - var gotSA []string - var expectedSa []string - for _, e := range pods { - if e == "" { - expectedSa = append(expectedSa, "") - } else { - expectedSa = append(expectedSa, "spiffe://cluster.local/ns/nsA/sa/"+e) - } - } - - for _, e := range ev.Endpoints { - gotSA = append(gotSA, e.ServiceAccount) - } - if !reflect.DeepEqual(gotIps, ips) { - t.Fatalf("expected ips %v, got %v", ips, gotIps) - } - if !reflect.DeepEqual(gotSA, expectedSa) { - t.Fatalf("expected SAs %v, got %v", expectedSa, gotSA) - } - } - assertPendingResync := func(expected int) { - t.Helper() - retry.UntilSuccessOrFail(t, func() error { - controller.pods.RLock() - defer controller.pods.RUnlock() - if len(controller.pods.needResync) != expected { - return fmt.Errorf("expected %d pods needing resync, got %d", expected, len(controller.pods.needResync)) - } - return nil - }, retry.Timeout(time.Second)) - } - - // standard ordering - addService("svc") - addPod("pod1", "172.0.1.1") - addEndpoint("svc", []string{"172.0.1.1"}, []string{"pod1"}) - assertEndpointsEvent([]string{"172.0.1.1"}, []string{"pod1"}) - fx.Clear() - - // Create the endpoint, then later add the pod. Should eventually get an update for the endpoint - addEndpoint("svc", []string{"172.0.1.1", "172.0.1.2"}, []string{"pod1", "pod2"}) - assertEndpointsEvent([]string{"172.0.1.1"}, []string{"pod1"}) - fx.Clear() - addPod("pod2", "172.0.1.2") - assertEndpointsEvent([]string{"172.0.1.1", "172.0.1.2"}, []string{"pod1", "pod2"}) - fx.Clear() - - // Create the endpoint without a pod reference. We should see it immediately - addEndpoint("svc", []string{"172.0.1.1", "172.0.1.2", "172.0.1.3"}, []string{"pod1", "pod2", ""}) - assertEndpointsEvent([]string{"172.0.1.1", "172.0.1.2", "172.0.1.3"}, []string{"pod1", "pod2", ""}) - fx.Clear() - - // Delete a pod before the endpoint - addEndpoint("svc", []string{"172.0.1.1"}, []string{"pod1"}) - deletePod("pod2", "172.0.1.2") - assertEndpointsEvent([]string{"172.0.1.1"}, []string{"pod1"}) - fx.Clear() - - // add another service - addService("other") - // Add endpoints for the new service, and the old one. Both should be missing the last IP - addEndpoint("other", []string{"172.0.1.1", "172.0.1.2"}, []string{"pod1", "pod2"}) - addEndpoint("svc", []string{"172.0.1.1", "172.0.1.2"}, []string{"pod1", "pod2"}) - assertEndpointsEvent([]string{"172.0.1.1"}, []string{"pod1"}) - assertEndpointsEvent([]string{"172.0.1.1"}, []string{"pod1"}) - fx.Clear() - // Add the pod, expect the endpoints update for both - addPod("pod2", "172.0.1.2") - assertEndpointsEvent([]string{"172.0.1.1", "172.0.1.2"}, []string{"pod1", "pod2"}) - assertEndpointsEvent([]string{"172.0.1.1", "172.0.1.2"}, []string{"pod1", "pod2"}) - - // Check for memory leaks - assertPendingResync(0) - addEndpoint("svc", []string{"172.0.1.1", "172.0.1.2", "172.0.1.3"}, []string{"pod1", "pod2", "pod3"}) - // This is really an implementation detail here - but checking to sanity check our test - assertPendingResync(1) - // Remove the endpoint again, with no pod events in between. Should have no memory leaks - addEndpoint("svc", []string{"172.0.1.1", "172.0.1.2"}, []string{"pod1", "pod2"}) - // TODO this case would leak - // assertPendingResync(0) - - // completely remove the endpoint - addEndpoint("svc", []string{"172.0.1.1", "172.0.1.2", "172.0.1.3"}, []string{"pod1", "pod2", "pod3"}) - assertPendingResync(1) - if err := controller.client.CoreV1().Endpoints("nsA").Delete(context.TODO(), "svc", metaV1.DeleteOptions{}); err != nil { - t.Fatal(err) - } - if err := controller.client.DiscoveryV1().EndpointSlices("nsA").Delete(context.TODO(), "svc", metaV1.DeleteOptions{}); err != nil { - t.Fatal(err) - } - assertPendingResync(0) - }) - } -} - -func TestWorkloadInstanceHandlerMultipleEndpoints(t *testing.T) { - controller, fx := NewFakeControllerWithOptions(FakeControllerOptions{}) - go controller.Run(controller.stop) - // Wait for the caches to sync, otherwise we may hit race conditions where events are dropped - cache.WaitForCacheSync(controller.stop, controller.HasSynced) - defer controller.Stop() - - // Create an initial pod with a service, and endpoint. - pod1 := generatePod("172.0.1.1", "pod1", "nsA", "", "node1", map[string]string{"app": "prod-app"}, map[string]string{}) - pod2 := generatePod("172.0.1.2", "pod2", "nsA", "", "node1", map[string]string{"app": "prod-app"}, map[string]string{}) - pods := []*coreV1.Pod{pod1, pod2} - nodes := []*coreV1.Node{ - generateNode("node1", map[string]string{NodeZoneLabel: "zone1", NodeRegionLabel: "region1", label.TopologySubzone.Name: "subzone1"}), - } - addNodes(t, controller, nodes...) - addPods(t, controller, fx, pods...) - createService(controller, "svc1", "nsA", nil, - []int32{8080}, map[string]string{"app": "prod-app"}, t) - if ev := fx.Wait("service"); ev == nil { - t.Fatal("Timeout creating service") - } - pod1Ips := []string{"172.0.1.1"} - portNames := []string{"tcp-port"} - createEndpoints(t, controller, "svc1", "nsA", portNames, pod1Ips, nil, nil) - if ev := fx.Wait("eds"); ev == nil { - t.Fatal("Timeout incremental eds") - } - - // Simulate adding a workload entry (fired through invocation of WorkloadInstanceHandler) - controller.WorkloadInstanceHandler(&model.WorkloadInstance{ - Namespace: "nsA", - Endpoint: &model.IstioEndpoint{ - Labels: labels.Instance{"app": "prod-app"}, - ServiceAccount: "account", - Address: "2.2.2.2", - EndpointPort: 8080, - }, - }, model.EventAdd) - - expectedEndpointIPs := []string{"172.0.1.1", "2.2.2.2"} - // Check if an EDS event is fired - if ev := fx.Wait("eds"); ev == nil { - t.Fatal("Did not get eds event when workload entry was added") - } else { - // check if the hostname matches that of k8s service svc1.nsA - if ev.ID != "svc1.nsA.svc.company.com" { - t.Fatalf("eds event for workload entry addition did not match the expected service. got %s, want %s", - ev.ID, "svc1.nsA.svc.company.com") - } - // we should have the pod IP and the workload Entry's IP in the endpoints.. - // the first endpoint should be that of the k8s pod and the second one should be the workload entry - - var gotEndpointIPs []string - for _, ep := range ev.Endpoints { - gotEndpointIPs = append(gotEndpointIPs, ep.Address) - } - if !reflect.DeepEqual(gotEndpointIPs, expectedEndpointIPs) { - t.Fatalf("eds update after adding workload entry did not match expected list. got %v, want %v", - gotEndpointIPs, expectedEndpointIPs) - } - } - - // Check if InstancesByPort returns the same list - converted := controller.Services() - if len(converted) != 1 { - t.Fatalf("failed to get services (%v), converted", converted) - } - instances := controller.InstancesByPort(converted[0], 8080, labels.Instance{ - "app": "prod-app", - }) - var gotEndpointIPs []string - for _, instance := range instances { - gotEndpointIPs = append(gotEndpointIPs, instance.Endpoint.Address) - } - if !reflect.DeepEqual(gotEndpointIPs, expectedEndpointIPs) { - t.Fatalf("InstancesByPort after adding workload entry did not match expected list. got %v, want %v", - gotEndpointIPs, expectedEndpointIPs) - } - - // Now add a k8s pod to the service and ensure that eds updates contain both pod IPs and workload entry IPs. - updateEndpoints(controller, "svc1", "nsA", portNames, []string{"172.0.1.1", "172.0.1.2"}, t) - if ev := fx.Wait("eds"); ev == nil { - t.Fatal("Timeout incremental eds") - } else { - var gotEndpointIPs []string - for _, ep := range ev.Endpoints { - gotEndpointIPs = append(gotEndpointIPs, ep.Address) - } - expectedEndpointIPs = []string{"172.0.1.1", "172.0.1.2", "2.2.2.2"} - if !reflect.DeepEqual(gotEndpointIPs, expectedEndpointIPs) { - t.Fatalf("eds update after adding pod did not match expected list. got %v, want %v", - gotEndpointIPs, expectedEndpointIPs) - } - } -} - -func TestWorkloadInstanceHandler_WorkloadInstanceIndex(t *testing.T) { - ctl, _ := NewFakeControllerWithOptions(FakeControllerOptions{}) - go ctl.Run(ctl.stop) - // Wait for the caches to sync, otherwise we may hit race conditions where events are dropped - cache.WaitForCacheSync(ctl.stop, ctl.HasSynced) - defer ctl.Stop() - - verifyGetByIP := func(address string, want []*model.WorkloadInstance) { - got := ctl.workloadInstancesIndex.GetByIP(address) - - if diff := cmp.Diff(want, got); diff != "" { - t.Fatalf("workload index is not valid (--want/++got): %v", diff) - } - } - - wi1 := &model.WorkloadInstance{ - Name: "ratings-1", - Namespace: "bookinfo", - Endpoint: &model.IstioEndpoint{ - Labels: labels.Instance{"app": "ratings"}, - Address: "2.2.2.2", - EndpointPort: 8080, - }, - } - - // simulate adding a workload entry - ctl.WorkloadInstanceHandler(wi1, model.EventAdd) - - verifyGetByIP("2.2.2.2", []*model.WorkloadInstance{wi1}) - - wi2 := &model.WorkloadInstance{ - Name: "details-1", - Namespace: "bookinfo", - Endpoint: &model.IstioEndpoint{ - Labels: labels.Instance{"app": "details"}, - Address: "3.3.3.3", - EndpointPort: 9090, - }, - } - - // simulate adding a workload entry - ctl.WorkloadInstanceHandler(wi2, model.EventAdd) - - verifyGetByIP("2.2.2.2", []*model.WorkloadInstance{wi1}) - verifyGetByIP("3.3.3.3", []*model.WorkloadInstance{wi2}) - - wi3 := &model.WorkloadInstance{ - Name: "details-1", - Namespace: "bookinfo", - Endpoint: &model.IstioEndpoint{ - Labels: labels.Instance{"app": "details"}, - Address: "2.2.2.2", // update IP - EndpointPort: 9090, - }, - } - - // simulate updating a workload entry - ctl.WorkloadInstanceHandler(wi3, model.EventUpdate) - - verifyGetByIP("3.3.3.3", nil) - verifyGetByIP("2.2.2.2", []*model.WorkloadInstance{wi3, wi1}) - - // simulate deleting a workload entry - ctl.WorkloadInstanceHandler(wi3, model.EventDelete) - - verifyGetByIP("2.2.2.2", []*model.WorkloadInstance{wi1}) - - // simulate deleting a workload entry - ctl.WorkloadInstanceHandler(wi1, model.EventDelete) - - verifyGetByIP("2.2.2.2", nil) -} - -func TestKubeEndpointsControllerOnEvent(t *testing.T) { - testCases := []struct { - mode EndpointMode - tombstone cache.DeletedFinalStateUnknown - }{ - { - mode: EndpointsOnly, - tombstone: cache.DeletedFinalStateUnknown{ - Key: "namespace/name", - Obj: &coreV1.Endpoints{}, - }, - }, - { - mode: EndpointSliceOnly, - tombstone: cache.DeletedFinalStateUnknown{ - Key: "namespace/name", - Obj: &discovery.EndpointSlice{}, - }, - }, - } - - for _, tc := range testCases { - t.Run(EndpointModeNames[tc.mode], func(t *testing.T) { - controller, _ := NewFakeControllerWithOptions(FakeControllerOptions{Mode: tc.mode}) - go controller.Run(controller.stop) - // Wait for the caches to sync, otherwise we may hit race conditions where events are dropped - cache.WaitForCacheSync(controller.stop, controller.HasSynced) - defer controller.Stop() - - if err := controller.endpoints.onEvent(tc.tombstone, model.EventDelete); err != nil { - t.Errorf("unexpected error: %v", err) - } - }) - } -} - -func TestUpdateEdsCacheOnServiceUpdate(t *testing.T) { - controller, fx := NewFakeControllerWithOptions(FakeControllerOptions{}) - go controller.Run(controller.stop) - // Wait for the caches to sync, otherwise we may hit race conditions where events are dropped - cache.WaitForCacheSync(controller.stop, controller.HasSynced) - defer controller.Stop() - - // Create an initial pod with a service, and endpoint. - pod1 := generatePod("172.0.1.1", "pod1", "nsA", "", "node1", map[string]string{"app": "prod-app"}, map[string]string{}) - pod2 := generatePod("172.0.1.2", "pod2", "nsA", "", "node1", map[string]string{"app": "prod-app"}, map[string]string{}) - pods := []*coreV1.Pod{pod1, pod2} - nodes := []*coreV1.Node{ - generateNode("node1", map[string]string{NodeZoneLabel: "zone1", NodeRegionLabel: "region1", label.TopologySubzone.Name: "subzone1"}), - } - addNodes(t, controller, nodes...) - addPods(t, controller, fx, pods...) - createService(controller, "svc1", "nsA", nil, - []int32{8080}, map[string]string{"app": "prod-app"}, t) - if ev := fx.Wait("service"); ev == nil { - t.Fatal("Timeout creating service") - } - - pod1Ips := []string{"172.0.1.1"} - portNames := []string{"tcp-port"} - createEndpoints(t, controller, "svc1", "nsA", portNames, pod1Ips, nil, nil) - if ev := fx.Wait("eds"); ev == nil { - t.Fatal("Timeout incremental eds") - } - - // update service selector - svc := getService(controller, "svc1", "nsA", t) - svc.Spec.Selector = map[string]string{ - "app": "prod-app", - "foo": "bar", - } - svc.Spec.Selector = map[string]string{ - "app": "prod-app", - } - updateService(controller, svc, t) - if ev := fx.Wait("eds cache"); ev == nil { - t.Fatal("Timeout updating eds cache") - } -} - -func clearDiscoverabilityPolicy(ep *model.IstioEndpoint) { - if ep != nil { - ep.DiscoverabilityPolicy = nil - } -} diff --git a/pilot/pkg/serviceregistry/kube/controller/discoverycontrollers.go b/pilot/pkg/serviceregistry/kube/controller/discoverycontrollers.go deleted file mode 100644 index c8175b970..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/discoverycontrollers.go +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "github.com/hashicorp/go-multierror" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" - listerv1 "k8s.io/client-go/listers/core/v1" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube/controller/filter" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - kubelib "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -// initialize handlers for discovery selection scoping -func (c *Controller) initDiscoveryHandlers( - kubeClient kubelib.Client, - endpointMode EndpointMode, - meshWatcher mesh.Watcher, - discoveryNamespacesFilter filter.DiscoveryNamespacesFilter, -) { - c.initDiscoveryNamespaceHandlers(kubeClient, endpointMode, discoveryNamespacesFilter) - c.initMeshWatcherHandler(kubeClient, endpointMode, meshWatcher, discoveryNamespacesFilter) -} - -// handle discovery namespace membership changes triggered by namespace events, -// which requires triggering create/delete event handlers for services, pods, and endpoints, -// and updating the DiscoveryNamespacesFilter. -func (c *Controller) initDiscoveryNamespaceHandlers( - kubeClient kubelib.Client, - endpointMode EndpointMode, - discoveryNamespacesFilter filter.DiscoveryNamespacesFilter, -) { - otype := "Namespaces" - c.nsInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - incrementEvent(otype, "add") - ns := obj.(*v1.Namespace) - if discoveryNamespacesFilter.NamespaceCreated(ns.ObjectMeta) { - c.queue.Push(func() error { - c.handleSelectedNamespace(endpointMode, ns.Name) - return nil - }) - } - }, - UpdateFunc: func(old, new interface{}) { - incrementEvent(otype, "update") - oldNs := old.(*v1.Namespace) - newNs := new.(*v1.Namespace) - membershipChanged, namespaceAdded := discoveryNamespacesFilter.NamespaceUpdated(oldNs.ObjectMeta, newNs.ObjectMeta) - if membershipChanged { - var handleFunc func() error - if namespaceAdded { - handleFunc = func() error { - c.handleSelectedNamespace(endpointMode, newNs.Name) - return nil - } - } else { - handleFunc = func() error { - c.handleDeselectedNamespace(kubeClient, endpointMode, newNs.Name) - return nil - } - } - c.queue.Push(handleFunc) - } - }, - DeleteFunc: func(obj interface{}) { - incrementEvent(otype, "delete") - ns, ok := obj.(*v1.Namespace) - if !ok { - if tombstone, ok := obj.(cache.DeletedFinalStateUnknown); ok { - if cast, ok := tombstone.Obj.(*v1.Namespace); ok { - ns = cast - } else { - log.Errorf("Failed to convert to tombstoned namespace object: %v", obj) - return - } - } else { - log.Errorf("Failed to convert to namespace object: %v", obj) - return - } - } - discoveryNamespacesFilter.NamespaceDeleted(ns.ObjectMeta) - // no need to invoke object handlers since objects within the namespace will trigger delete events - }, - }) -} - -// handle discovery namespace membership changes triggered by changes to meshConfig's discovery selectors -// which requires updating the DiscoveryNamespaceFilter and triggering create/delete event handlers for services/pods/endpoints -// for membership changes -func (c *Controller) initMeshWatcherHandler( - kubeClient kubelib.Client, - endpointMode EndpointMode, - meshWatcher mesh.Watcher, - discoveryNamespacesFilter filter.DiscoveryNamespacesFilter, -) { - meshWatcher.AddMeshHandler(func() { - newSelectedNamespaces, deselectedNamespaces := discoveryNamespacesFilter.SelectorsChanged(meshWatcher.Mesh().GetDiscoverySelectors()) - - for _, nsName := range newSelectedNamespaces { - nsName := nsName // need to shadow variable to ensure correct value when evaluated inside the closure below - c.queue.Push(func() error { - c.handleSelectedNamespace(endpointMode, nsName) - return nil - }) - } - - for _, nsName := range deselectedNamespaces { - nsName := nsName // need to shadow variable to ensure correct value when evaluated inside the closure below - c.queue.Push(func() error { - c.handleDeselectedNamespace(kubeClient, endpointMode, nsName) - return nil - }) - } - }) -} - -// issue create events for all services, pods, and endpoints in the newly labeled namespace -func (c *Controller) handleSelectedNamespace(endpointMode EndpointMode, ns string) { - var errs *multierror.Error - - // for each resource type, issue create events for objects in the labeled namespace - - services, err := c.serviceLister.Services(ns).List(labels.Everything()) - if err != nil { - log.Errorf("error listing services: %v", err) - return - } - for _, svc := range services { - errs = multierror.Append(errs, c.onServiceEvent(svc, model.EventAdd)) - } - - pods, err := listerv1.NewPodLister(c.pods.informer.GetIndexer()).Pods(ns).List(labels.Everything()) - if err != nil { - log.Errorf("error listing pods: %v", err) - return - } - for _, pod := range pods { - errs = multierror.Append(errs, c.pods.onEvent(pod, model.EventAdd)) - } - - switch endpointMode { - case EndpointsOnly: - endpoints, err := listerv1.NewEndpointsLister(c.endpoints.getInformer().GetIndexer()).Endpoints(ns).List(labels.Everything()) - if err != nil { - log.Errorf("error listing endpoints: %v", err) - return - } - for _, ep := range endpoints { - errs = multierror.Append(errs, c.endpoints.onEvent(ep, model.EventAdd)) - } - case EndpointSliceOnly: - endpointSlices, err := c.endpoints.(*endpointSliceController).listSlices(ns, labels.Everything()) - if err != nil { - log.Errorf("error listing endpoint slices: %v", err) - return - } - for _, ep := range endpointSlices { - errs = multierror.Append(errs, c.endpoints.onEvent(ep, model.EventAdd)) - } - } - - if err := multierror.Flatten(errs.ErrorOrNil()); err != nil { - log.Errorf("one or more errors while handling newly labeled discovery namespace %s: %v", ns, err) - } -} - -// issue delete events for all services, pods, and endpoints in the delabled namespace -// use kubeClient.KubeInformer() to bypass filter in order to list resources from non-labeled namespace, -// which fetches informers from the SharedInformerFactory cache (i.e. does not instantiate a new informer) -func (c *Controller) handleDeselectedNamespace(kubeClient kubelib.Client, endpointMode EndpointMode, ns string) { - var errs *multierror.Error - - // for each resource type, issue delete events for objects in the delabled namespace - - services, err := kubeClient.KubeInformer().Core().V1().Services().Lister().Services(ns).List(labels.Everything()) - if err != nil { - log.Errorf("error listing services: %v", err) - return - } - for _, svc := range services { - errs = multierror.Append(errs, c.onServiceEvent(svc, model.EventDelete)) - } - - pods, err := kubeClient.KubeInformer().Core().V1().Pods().Lister().Pods(ns).List(labels.Everything()) - if err != nil { - log.Errorf("error listing pods: %v", err) - return - } - for _, pod := range pods { - errs = multierror.Append(errs, c.pods.onEvent(pod, model.EventDelete)) - } - - switch endpointMode { - case EndpointsOnly: - endpoints, err := kubeClient.KubeInformer().Core().V1().Endpoints().Lister().Endpoints(ns).List(labels.Everything()) - if err != nil { - log.Errorf("error listing endpoints: %v", err) - return - } - for _, ep := range endpoints { - errs = multierror.Append(errs, c.endpoints.onEvent(ep, model.EventDelete)) - } - case EndpointSliceOnly: - endpointSlices, err := c.endpoints.(*endpointSliceController).listSlices(ns, labels.Everything()) - if err != nil { - log.Errorf("error listing endpoint slices: %v", err) - return - } - for _, ep := range endpointSlices { - errs = multierror.Append(errs, c.endpoints.onEvent(ep, model.EventDelete)) - } - } - - if err := multierror.Flatten(errs.ErrorOrNil()); err != nil { - log.Errorf("one or more errors while handling delabeled discovery namespace %s: %v", ns, err) - } -} diff --git a/pilot/pkg/serviceregistry/kube/controller/endpoint_builder.go b/pilot/pkg/serviceregistry/kube/controller/endpoint_builder.go deleted file mode 100644 index 79b968614..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/endpoint_builder.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "istio.io/api/label" - v1 "k8s.io/api/core/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube" - labelutil "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/util/label" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - kubeUtil "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/network" -) - -// EndpointBuilder is a stateful IstioEndpoint builder with metadata used to build IstioEndpoint -type EndpointBuilder struct { - controller controllerInterface - - labels labels.Instance - metaNetwork network.ID - serviceAccount string - locality model.Locality - tlsMode string - workloadName string - namespace string - - // Values used to build dns name tables per pod. - // The the hostname of the Pod, by default equals to pod name. - hostname string - // If specified, the fully qualified Pod hostname will be "...svc.". - subDomain string -} - -func NewEndpointBuilder(c controllerInterface, pod *v1.Pod) *EndpointBuilder { - locality, sa, namespace, hostname, subdomain, ip := "", "", "", "", "", "" - var podLabels labels.Instance - if pod != nil { - locality = c.getPodLocality(pod) - sa = kube.SecureNamingSAN(pod) - podLabels = pod.Labels - namespace = pod.Namespace - subdomain = pod.Spec.Subdomain - if subdomain != "" { - hostname = pod.Spec.Hostname - if hostname == "" { - hostname = pod.Name - } - } - ip = pod.Status.PodIP - } - dm, _ := kubeUtil.GetDeployMetaFromPod(pod) - out := &EndpointBuilder{ - controller: c, - serviceAccount: sa, - locality: model.Locality{ - Label: locality, - ClusterID: c.Cluster(), - }, - tlsMode: kube.PodTLSMode(pod), - workloadName: dm.Name, - namespace: namespace, - hostname: hostname, - subDomain: subdomain, - } - networkID := out.endpointNetwork(ip) - out.labels = labelutil.AugmentLabels(podLabels, c.Cluster(), locality, networkID) - return out -} - -func NewEndpointBuilderFromMetadata(c controllerInterface, proxy *model.Proxy) *EndpointBuilder { - locality := util.LocalityToString(proxy.Locality) - out := &EndpointBuilder{ - controller: c, - metaNetwork: proxy.Metadata.Network, - serviceAccount: proxy.Metadata.ServiceAccount, - locality: model.Locality{ - Label: locality, - ClusterID: c.Cluster(), - }, - tlsMode: model.GetTLSModeFromEndpointLabels(proxy.Metadata.Labels), - } - var networkID network.ID - if len(proxy.IPAddresses) > 0 { - networkID = out.endpointNetwork(proxy.IPAddresses[0]) - } - out.labels = labelutil.AugmentLabels(proxy.Metadata.Labels, c.Cluster(), locality, networkID) - return out -} - -func (b *EndpointBuilder) buildIstioEndpoint( - endpointAddress string, - endpointPort int32, - svcPortName string, - discoverabilityPolicy model.EndpointDiscoverabilityPolicy) *model.IstioEndpoint { - if b == nil { - return nil - } - - // in case pod is not found when init EndpointBuilder. - networkID := network.ID(b.labels[label.TopologyNetwork.Name]) - if networkID == "" { - networkID = b.endpointNetwork(endpointAddress) - b.labels[label.TopologyNetwork.Name] = string(networkID) - } - - return &model.IstioEndpoint{ - Labels: b.labels, - ServiceAccount: b.serviceAccount, - Locality: b.locality, - TLSMode: b.tlsMode, - Address: endpointAddress, - EndpointPort: uint32(endpointPort), - ServicePortName: svcPortName, - Network: networkID, - WorkloadName: b.workloadName, - Namespace: b.namespace, - HostName: b.hostname, - SubDomain: b.subDomain, - DiscoverabilityPolicy: discoverabilityPolicy, - } -} - -// return the mesh network for the endpoint IP. Empty string if not found. -func (b *EndpointBuilder) endpointNetwork(endpointIP string) network.ID { - // If we're building the endpoint based on proxy meta, prefer the injected ISTIO_META_NETWORK value. - if b.metaNetwork != "" { - return b.metaNetwork - } - - return b.controller.Network(endpointIP, b.labels) -} diff --git a/pilot/pkg/serviceregistry/kube/controller/endpoint_builder_test.go b/pilot/pkg/serviceregistry/kube/controller/endpoint_builder_test.go deleted file mode 100644 index 03f3a047f..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/endpoint_builder_test.go +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "testing" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - . "github.com/onsi/gomega" - "istio.io/api/label" - v1 "k8s.io/api/core/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - cluster2 "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/network" -) - -func TestNewEndpointBuilderTopologyLabels(t *testing.T) { - cases := []struct { - name string - ctl testController - podLabels labels.Instance - expected labels.Instance - }{ - { - name: "empty", - ctl: testController{}, - podLabels: nil, - expected: labels.Instance{}, - }, - { - name: "region only", - ctl: testController{ - locality: "myregion", - }, - podLabels: labels.Instance{ - "k1": "v1", - label.TopologyNetwork.Name: "mynetwork", - }, - expected: labels.Instance{ - "k1": "v1", - NodeRegionLabelGA: "myregion", - label.TopologyNetwork.Name: "mynetwork", - }, - }, - { - name: "region and zone", - ctl: testController{ - locality: "myregion/myzone", - }, - podLabels: labels.Instance{ - "k1": "v1", - label.TopologyNetwork.Name: "mynetwork", - }, - expected: labels.Instance{ - "k1": "v1", - NodeRegionLabelGA: "myregion", - NodeZoneLabelGA: "myzone", - label.TopologyNetwork.Name: "mynetwork", - }, - }, - { - name: "network only ", - ctl: testController{ - network: "mynetwork", - }, - podLabels: labels.Instance{ - "k1": "v1", - }, - expected: labels.Instance{ - "k1": "v1", - label.TopologyNetwork.Name: "mynetwork", - }, - }, - { - name: "all values", - ctl: testController{ - locality: "myregion/myzone/mysubzone", - cluster: "mycluster", - network: "mynetwork", - }, - podLabels: labels.Instance{ - "k1": "v1", - label.TopologyNetwork.Name: "mynetwork", - }, - expected: labels.Instance{ - "k1": "v1", - NodeRegionLabelGA: "myregion", - NodeZoneLabelGA: "myzone", - label.TopologySubzone.Name: "mysubzone", - label.TopologyCluster.Name: "mycluster", - label.TopologyNetwork.Name: "mynetwork", - }, - }, - } - - for _, c := range cases { - c := c - t.Run(c.name, func(t *testing.T) { - pod := v1.Pod{} - pod.Name = "testpod" - pod.Namespace = "testns" - pod.Spec.ServiceAccountName = "testsan" - pod.Labels = c.podLabels - - eb := NewEndpointBuilder(c.ctl, &pod) - - g := NewGomegaWithT(t) - g.Expect(eb.labels).Should(Equal(c.expected)) - }) - } -} - -func TestNewEndpointBuilderFromMetadataTopologyLabels(t *testing.T) { - cases := []struct { - name string - ctl testController - proxy *model.Proxy - expected labels.Instance - }{ - { - name: "empty", - ctl: testController{}, - proxy: &model.Proxy{ - Metadata: &model.NodeMetadata{}, - }, - expected: labels.Instance{}, - }, - { - name: "region only", - ctl: testController{}, - proxy: &model.Proxy{ - Metadata: &model.NodeMetadata{ - Labels: labels.Instance{ - "k1": "v1", - label.TopologyNetwork.Name: "mynetwork", - }, - }, - Locality: &core.Locality{ - Region: "myregion", - }, - }, - expected: labels.Instance{ - "k1": "v1", - NodeRegionLabelGA: "myregion", - label.TopologyNetwork.Name: "mynetwork", - }, - }, - { - name: "region and zone", - ctl: testController{}, - proxy: &model.Proxy{ - Metadata: &model.NodeMetadata{ - Labels: labels.Instance{ - "k1": "v1", - label.TopologyNetwork.Name: "mynetwork", - }, - }, - Locality: &core.Locality{ - Region: "myregion", - Zone: "myzone", - }, - }, - expected: labels.Instance{ - "k1": "v1", - NodeRegionLabelGA: "myregion", - NodeZoneLabelGA: "myzone", - label.TopologyNetwork.Name: "mynetwork", - }, - }, - { - name: "all values set", - ctl: testController{ - cluster: "mycluster", - }, - proxy: &model.Proxy{ - Metadata: &model.NodeMetadata{ - Labels: labels.Instance{ - "k1": "v1", - label.TopologyNetwork.Name: "mynetwork", - }, - }, - Locality: &core.Locality{ - Region: "myregion", - Zone: "myzone", - SubZone: "mysubzone", - }, - }, - expected: labels.Instance{ - "k1": "v1", - NodeRegionLabelGA: "myregion", - NodeZoneLabelGA: "myzone", - label.TopologySubzone.Name: "mysubzone", - label.TopologyCluster.Name: "mycluster", - label.TopologyNetwork.Name: "mynetwork", - }, - }, - } - - for _, c := range cases { - c := c - t.Run(c.name, func(t *testing.T) { - eb := NewEndpointBuilderFromMetadata(c.ctl, c.proxy) - - g := NewGomegaWithT(t) - g.Expect(eb.labels).Should(Equal(c.expected)) - }) - } -} - -var _ controllerInterface = testController{} - -type testController struct { - locality string - cluster cluster2.ID - network network.ID -} - -func (c testController) getPodLocality(*v1.Pod) string { - return c.locality -} - -func (c testController) Network(ip string, instance labels.Instance) network.ID { - return c.network -} - -func (c testController) Cluster() cluster2.ID { - return c.cluster -} diff --git a/pilot/pkg/serviceregistry/kube/controller/endpoint_test.go b/pilot/pkg/serviceregistry/kube/controller/endpoint_test.go deleted file mode 100644 index b9b037ed7..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/endpoint_test.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "testing" -) - -import ( - coreV1 "k8s.io/api/core/v1" -) - -func TestEndpointsEqual(t *testing.T) { - addressA := coreV1.EndpointAddress{IP: "1.2.3.4", Hostname: "a"} - addressB := coreV1.EndpointAddress{IP: "1.2.3.4", Hostname: "b"} - portA := coreV1.EndpointPort{Name: "a"} - portB := coreV1.EndpointPort{Name: "b"} - appProtocolA := "http" - appProtocolB := "tcp" - appProtocolPortA := coreV1.EndpointPort{Name: "a", AppProtocol: &appProtocolA} - appProtocolPortB := coreV1.EndpointPort{Name: "a", AppProtocol: &appProtocolB} - cases := []struct { - name string - a *coreV1.Endpoints - b *coreV1.Endpoints - want bool - }{ - {"both empty", &coreV1.Endpoints{}, &coreV1.Endpoints{}, true}, - { - "just not ready endpoints", - &coreV1.Endpoints{Subsets: []coreV1.EndpointSubset{ - {NotReadyAddresses: []coreV1.EndpointAddress{addressA}}, - }}, - &coreV1.Endpoints{}, - false, - }, - { - "not ready to ready", - &coreV1.Endpoints{Subsets: []coreV1.EndpointSubset{ - {NotReadyAddresses: []coreV1.EndpointAddress{addressA}}, - }}, - &coreV1.Endpoints{Subsets: []coreV1.EndpointSubset{ - {Addresses: []coreV1.EndpointAddress{addressA}}, - }}, - false, - }, - { - "ready and not ready address", - &coreV1.Endpoints{Subsets: []coreV1.EndpointSubset{ - { - NotReadyAddresses: []coreV1.EndpointAddress{addressB}, - Addresses: []coreV1.EndpointAddress{addressA}, - }, - }}, - &coreV1.Endpoints{Subsets: []coreV1.EndpointSubset{ - {Addresses: []coreV1.EndpointAddress{addressA}}, - }}, - true, - }, - { - "different addresses", - &coreV1.Endpoints{Subsets: []coreV1.EndpointSubset{ - {Addresses: []coreV1.EndpointAddress{addressB}}, - }}, - &coreV1.Endpoints{Subsets: []coreV1.EndpointSubset{ - {Addresses: []coreV1.EndpointAddress{addressA}}, - }}, - false, - }, - { - "different ports", - &coreV1.Endpoints{Subsets: []coreV1.EndpointSubset{ - {Addresses: []coreV1.EndpointAddress{addressA}, Ports: []coreV1.EndpointPort{portA}}, - }}, - &coreV1.Endpoints{Subsets: []coreV1.EndpointSubset{ - {Addresses: []coreV1.EndpointAddress{addressA}, Ports: []coreV1.EndpointPort{portB}}, - }}, - false, - }, - { - "same app protocol", - &coreV1.Endpoints{Subsets: []coreV1.EndpointSubset{ - {Addresses: []coreV1.EndpointAddress{addressA}, Ports: []coreV1.EndpointPort{appProtocolPortA}}, - }}, - &coreV1.Endpoints{Subsets: []coreV1.EndpointSubset{ - {Addresses: []coreV1.EndpointAddress{addressA}, Ports: []coreV1.EndpointPort{appProtocolPortA}}, - }}, - true, - }, - { - "different app protocol", - &coreV1.Endpoints{Subsets: []coreV1.EndpointSubset{ - {Addresses: []coreV1.EndpointAddress{addressA}, Ports: []coreV1.EndpointPort{appProtocolPortA}}, - }}, - &coreV1.Endpoints{Subsets: []coreV1.EndpointSubset{ - {Addresses: []coreV1.EndpointAddress{addressA}, Ports: []coreV1.EndpointPort{appProtocolPortB}}, - }}, - false, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - got := endpointsEqual(tt.a, tt.b) - inverse := endpointsEqual(tt.b, tt.a) - if got != tt.want { - t.Fatalf("Compare endpoints got %v, want %v", got, tt.want) - } - if got != inverse { - t.Fatalf("Expected to be commutative, but was not") - } - }) - } -} diff --git a/pilot/pkg/serviceregistry/kube/controller/endpointcontroller.go b/pilot/pkg/serviceregistry/kube/controller/endpointcontroller.go deleted file mode 100644 index 7ff002e7b..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/endpointcontroller.go +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube/controller/filter" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -// Pilot can get EDS information from Kubernetes from two mutually exclusive sources, Endpoints and -// EndpointSlices. The kubeEndpointsController abstracts these details and provides a common interface -// that both sources implement. -type kubeEndpointsController interface { - HasSynced() bool - Run(stopCh <-chan struct{}) - getInformer() filter.FilteredSharedIndexInformer - onEvent(curr interface{}, event model.Event) error - InstancesByPort(c *Controller, svc *model.Service, reqSvcPort int, labelsList labels.Instance) []*model.ServiceInstance - GetProxyServiceInstances(c *Controller, proxy *model.Proxy) []*model.ServiceInstance - buildIstioEndpoints(ep interface{}, host host.Name) []*model.IstioEndpoint - buildIstioEndpointsWithService(name, namespace string, host host.Name, clearCache bool) []*model.IstioEndpoint - // forgetEndpoint does internal bookkeeping on a deleted endpoint - forgetEndpoint(endpoint interface{}) map[host.Name][]*model.IstioEndpoint - getServiceNamespacedName(ep interface{}) types.NamespacedName -} - -// kubeEndpoints abstracts the common behavior across endpoint and endpoint slices. -type kubeEndpoints struct { - c *Controller - informer filter.FilteredSharedIndexInformer -} - -func (e *kubeEndpoints) HasSynced() bool { - return e.informer.HasSynced() -} - -func (e *kubeEndpoints) Run(stopCh <-chan struct{}) { - e.informer.Run(stopCh) -} - -// processEndpointEvent triggers the config update. -func processEndpointEvent(c *Controller, epc kubeEndpointsController, name string, namespace string, event model.Event, ep interface{}) error { - // Update internal endpoint cache no matter what kind of service, even headless service. - // As for gateways, the cluster discovery type is `EDS` for headless service. - updateEDS(c, epc, ep, event) - if features.EnableHeadlessService { - if svc, _ := c.serviceLister.Services(namespace).Get(name); svc != nil { - for _, modelSvc := range c.servicesForNamespacedName(kube.NamespacedNameForK8sObject(svc)) { - // if the service is headless service, trigger a full push. - if svc.Spec.ClusterIP == v1.ClusterIPNone { - c.opts.XDSUpdater.ConfigUpdate(&model.PushRequest{ - Full: true, - // TODO: extend and set service instance type, so no need to re-init push context - ConfigsUpdated: map[model.ConfigKey]struct{}{{ - Kind: gvk.ServiceEntry, - Name: modelSvc.Hostname.String(), - Namespace: svc.Namespace, - }: {}}, - Reason: []model.TriggerReason{model.EndpointUpdate}, - }) - return nil - } - } - } - } - - return nil -} - -func updateEDS(c *Controller, epc kubeEndpointsController, ep interface{}, event model.Event) { - namespacedName := epc.getServiceNamespacedName(ep) - log.Debugf("Handle EDS endpoint %s %s %s in namespace %s", namespacedName.Name, event, namespacedName.Namespace) - var forgottenEndpointsByHost map[host.Name][]*model.IstioEndpoint - if event == model.EventDelete { - forgottenEndpointsByHost = epc.forgetEndpoint(ep) - } - - shard := model.ShardKeyFromRegistry(c) - - for _, hostName := range c.hostNamesForNamespacedName(namespacedName) { - var endpoints []*model.IstioEndpoint - if forgottenEndpointsByHost != nil { - endpoints = forgottenEndpointsByHost[hostName] - } else { - endpoints = epc.buildIstioEndpoints(ep, hostName) - } - - svc := c.GetService(hostName) - if svc != nil { - fep := c.collectWorkloadInstanceEndpoints(svc) - endpoints = append(endpoints, fep...) - } else { - log.Debugf("Handle EDS endpoint: skip collecting workload entry endpoints, service %s/%s has not been populated", - namespacedName.Namespace, namespacedName.Name) - } - - c.opts.XDSUpdater.EDSUpdate(shard, string(hostName), namespacedName.Namespace, endpoints) - } -} - -// getPod fetches a pod by name or IP address. -// A pod may be missing (nil) for two reasons: -// - It is an endpoint without an associated Pod. In this case, expectPod will be false. -// - It is an endpoint with an associate Pod, but its not found. In this case, expectPod will be true. -// this may happen due to eventually consistency issues, out of order events, etc. In this case, the caller -// should not precede with the endpoint, or inaccurate information would be sent which may have impacts on -// correctness and security. -// -// Note: this is only used by endpoints and endpointslice controller -func getPod(c *Controller, ip string, ep *metav1.ObjectMeta, targetRef *v1.ObjectReference, host host.Name) (*v1.Pod, bool) { - var expectPod bool - pod := c.getPod(ip, ep.Namespace, targetRef) - if targetRef != nil && targetRef.Kind == "Pod" { - expectPod = true - if pod == nil { - c.registerEndpointResync(ep, ip, host) - } - } - - return pod, expectPod -} - -func (c *Controller) registerEndpointResync(ep *metav1.ObjectMeta, ip string, host host.Name) { - // This means, the endpoint event has arrived before pod event. - // This might happen because PodCache is eventually consistent. - log.Debugf("Endpoint without pod %s %s.%s", ip, ep.Name, ep.Namespace) - endpointsWithNoPods.Increment() - if c.opts.Metrics != nil { - c.opts.Metrics.AddMetric(model.EndpointNoPod, string(host), "", ip) - } - // Tell pod cache we want to queue the endpoint event when this pod arrives. - epkey := kube.KeyFunc(ep.Name, ep.Namespace) - c.pods.queueEndpointEventOnPodArrival(epkey, ip) -} - -// getPod fetches a pod by name or IP address. -// A pod may be missing (nil) for two reasons: -// * It is an endpoint without an associated Pod. -// * It is an endpoint with an associate Pod, but its not found. -func (c *Controller) getPod(ip string, namespace string, targetRef *v1.ObjectReference) *v1.Pod { - if targetRef != nil && targetRef.Kind == "Pod" { - key := kube.KeyFunc(targetRef.Name, targetRef.Namespace) - pod := c.pods.getPodByKey(key) - return pod - } - - // This means the endpoint is manually controlled - // TODO: this may be not correct because of the hostnetwork pods may have same ip address - // Do we have a way to get the pod from only endpoint? - pod := c.pods.getPodByIP(ip) - if pod != nil { - // This prevents selecting a pod in another different namespace - if pod.Namespace != namespace { - pod = nil - } - } - // There maybe no pod at all - return pod -} diff --git a/pilot/pkg/serviceregistry/kube/controller/endpoints.go b/pilot/pkg/serviceregistry/kube/controller/endpoints.go deleted file mode 100644 index 9ac7ea6d6..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/endpoints.go +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - klabels "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/types" - listerv1 "k8s.io/client-go/listers/core/v1" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube/controller/filter" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" -) - -type endpointsController struct { - kubeEndpoints -} - -var _ kubeEndpointsController = &endpointsController{} - -func newEndpointsController(c *Controller) *endpointsController { - informer := filter.NewFilteredSharedIndexInformer( - c.opts.DiscoveryNamespacesFilter.Filter, - c.client.KubeInformer().Core().V1().Endpoints().Informer(), - ) - out := &endpointsController{ - kubeEndpoints: kubeEndpoints{ - c: c, - informer: informer, - }, - } - c.registerHandlers(informer, "Endpoints", out.onEvent, endpointsEqual) - return out -} - -func (e *endpointsController) GetProxyServiceInstances(c *Controller, proxy *model.Proxy) []*model.ServiceInstance { - eps, err := listerv1.NewEndpointsLister(e.informer.GetIndexer()).Endpoints(proxy.Metadata.Namespace).List(klabels.Everything()) - if err != nil { - log.Errorf("Get endpoints by index failed: %v", err) - return nil - } - var out []*model.ServiceInstance - for _, ep := range eps { - instances := endpointServiceInstances(c, ep, proxy) - out = append(out, instances...) - } - - return out -} - -func endpointServiceInstances(c *Controller, endpoints *v1.Endpoints, proxy *model.Proxy) []*model.ServiceInstance { - var out []*model.ServiceInstance - - for _, svc := range c.servicesForNamespacedName(kube.NamespacedNameForK8sObject(endpoints)) { - pod := c.pods.getPodByProxy(proxy) - builder := NewEndpointBuilder(c, pod) - - discoverabilityPolicy := c.exports.EndpointDiscoverabilityPolicy(svc) - - for _, ss := range endpoints.Subsets { - for _, port := range ss.Ports { - svcPort, exists := svc.Ports.Get(port.Name) - if !exists { - continue - } - - // consider multiple IP scenarios - for _, ip := range proxy.IPAddresses { - if hasProxyIP(ss.Addresses, ip) || hasProxyIP(ss.NotReadyAddresses, ip) { - istioEndpoint := builder.buildIstioEndpoint(ip, port.Port, svcPort.Name, discoverabilityPolicy) - out = append(out, &model.ServiceInstance{ - Endpoint: istioEndpoint, - ServicePort: svcPort, - Service: svc, - }) - } - - if hasProxyIP(ss.NotReadyAddresses, ip) { - if c.opts.Metrics != nil { - c.opts.Metrics.AddMetric(model.ProxyStatusEndpointNotReady, proxy.ID, proxy.ID, "") - } - } - } - } - } - } - - return out -} - -func (e *endpointsController) InstancesByPort(c *Controller, svc *model.Service, reqSvcPort int, labels labels.Instance) []*model.ServiceInstance { - item, exists, err := e.informer.GetIndexer().GetByKey(kube.KeyFunc(svc.Attributes.Name, svc.Attributes.Namespace)) - if err != nil { - log.Infof("get endpoints(%s, %s) => error %v", svc.Attributes.Name, svc.Attributes.Namespace, err) - return nil - } - if !exists { - return nil - } - - discoverabilityPolicy := c.exports.EndpointDiscoverabilityPolicy(svc) - - // Locate all ports in the actual service - svcPort, exists := svc.Ports.GetByPort(reqSvcPort) - if !exists { - return nil - } - ep := item.(*v1.Endpoints) - var out []*model.ServiceInstance - for _, ss := range ep.Subsets { - out = append(out, e.buildServiceInstances(ep, ss, ss.Addresses, svc, discoverabilityPolicy, labels, svcPort, model.Healthy)...) - if features.SendUnhealthyEndpoints { - out = append(out, e.buildServiceInstances(ep, ss, ss.NotReadyAddresses, svc, discoverabilityPolicy, labels, svcPort, model.UnHealthy)...) - } - } - return out -} - -func (e *endpointsController) getInformer() filter.FilteredSharedIndexInformer { - return e.informer -} - -func (e *endpointsController) onEvent(curr interface{}, event model.Event) error { - ep, ok := curr.(*v1.Endpoints) - if !ok { - tombstone, ok := curr.(cache.DeletedFinalStateUnknown) - if !ok { - log.Errorf("Couldn't get object from tombstone %#v", curr) - return nil - } - ep, ok = tombstone.Obj.(*v1.Endpoints) - if !ok { - log.Errorf("Tombstone contained object that is not an endpoints %#v", curr) - return nil - } - } - - return processEndpointEvent(e.c, e, ep.Name, ep.Namespace, event, ep) -} - -func (e *endpointsController) forgetEndpoint(endpoint interface{}) map[host.Name][]*model.IstioEndpoint { - ep := endpoint.(*v1.Endpoints) - key := kube.KeyFunc(ep.Name, ep.Namespace) - for _, ss := range ep.Subsets { - for _, ea := range ss.Addresses { - e.c.pods.endpointDeleted(key, ea.IP) - } - } - return make(map[host.Name][]*model.IstioEndpoint) -} - -func (e *endpointsController) buildIstioEndpoints(endpoint interface{}, host host.Name) []*model.IstioEndpoint { - var endpoints []*model.IstioEndpoint - ep := endpoint.(*v1.Endpoints) - - discoverabilityPolicy := e.c.exports.EndpointDiscoverabilityPolicy(e.c.GetService(host)) - - for _, ss := range ep.Subsets { - endpoints = append(endpoints, e.buildIstioEndpointFromAddress(ep, ss, ss.Addresses, host, discoverabilityPolicy, model.Healthy)...) - if features.SendUnhealthyEndpoints { - endpoints = append(endpoints, e.buildIstioEndpointFromAddress(ep, ss, ss.NotReadyAddresses, host, discoverabilityPolicy, model.UnHealthy)...) - } - } - return endpoints -} - -func (e *endpointsController) buildServiceInstances(ep *v1.Endpoints, ss v1.EndpointSubset, endpoints []v1.EndpointAddress, - svc *model.Service, discoverabilityPolicy model.EndpointDiscoverabilityPolicy, lbls labels.Instance, - svcPort *model.Port, health model.HealthStatus) []*model.ServiceInstance { - var out []*model.ServiceInstance - for _, ea := range endpoints { - var podLabels labels.Instance - pod, expectedPod := getPod(e.c, ea.IP, &metav1.ObjectMeta{Name: ep.Name, Namespace: ep.Namespace}, ea.TargetRef, svc.Hostname) - if pod == nil && expectedPod { - continue - } - if pod != nil { - podLabels = pod.Labels - } - // check that one of the input labels is a subset of the labels - if !lbls.SubsetOf(podLabels) { - continue - } - - builder := NewEndpointBuilder(e.c, pod) - - // identify the port by name. K8S EndpointPort uses the service port name - for _, port := range ss.Ports { - if port.Name == "" || // 'name optional if single port is defined' - svcPort.Name == port.Name { - istioEndpoint := builder.buildIstioEndpoint(ea.IP, port.Port, svcPort.Name, discoverabilityPolicy) - istioEndpoint.HealthStatus = health - out = append(out, &model.ServiceInstance{ - Endpoint: istioEndpoint, - ServicePort: svcPort, - Service: svc, - }) - } - } - } - return out -} - -func (e *endpointsController) buildIstioEndpointFromAddress(ep *v1.Endpoints, ss v1.EndpointSubset, endpoints []v1.EndpointAddress, - host host.Name, discoverabilityPolicy model.EndpointDiscoverabilityPolicy, health model.HealthStatus) []*model.IstioEndpoint { - var istioEndpoints []*model.IstioEndpoint - for _, ea := range endpoints { - pod, expectedPod := getPod(e.c, ea.IP, &metav1.ObjectMeta{Name: ep.Name, Namespace: ep.Namespace}, ea.TargetRef, host) - if pod == nil && expectedPod { - continue - } - builder := NewEndpointBuilder(e.c, pod) - // EDS and ServiceEntry use name for service port - ADS will need to map to numbers. - for _, port := range ss.Ports { - istioEndpoint := builder.buildIstioEndpoint(ea.IP, port.Port, port.Name, discoverabilityPolicy) - istioEndpoint.HealthStatus = health - istioEndpoints = append(istioEndpoints, istioEndpoint) - } - } - return istioEndpoints -} - -func (e *endpointsController) buildIstioEndpointsWithService(name, namespace string, host host.Name, _ bool) []*model.IstioEndpoint { - ep, err := listerv1.NewEndpointsLister(e.informer.GetIndexer()).Endpoints(namespace).Get(name) - if err != nil || ep == nil { - log.Debugf("endpoints(%s, %s) not found => error %v", name, namespace, err) - return nil - } - - return e.buildIstioEndpoints(ep, host) -} - -func (e *endpointsController) getServiceNamespacedName(ep interface{}) types.NamespacedName { - endpoint := ep.(*v1.Endpoints) - return kube.NamespacedNameForK8sObject(endpoint) -} - -// endpointsEqual returns true if the two endpoints are the same in aspects Pilot cares about -// This currently means only looking at "Ready" endpoints -func endpointsEqual(first, second interface{}) bool { - a := first.(*v1.Endpoints) - b := second.(*v1.Endpoints) - if len(a.Subsets) != len(b.Subsets) { - return false - } - for i := range a.Subsets { - if !portsEqual(a.Subsets[i].Ports, b.Subsets[i].Ports) { - return false - } - if !addressesEqual(a.Subsets[i].Addresses, b.Subsets[i].Addresses) { - return false - } - } - return true -} diff --git a/pilot/pkg/serviceregistry/kube/controller/endpointslice.go b/pilot/pkg/serviceregistry/kube/controller/endpointslice.go deleted file mode 100644 index 917375f81..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/endpointslice.go +++ /dev/null @@ -1,532 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "sync" -) - -import ( - "istio.io/api/label" - v1 "k8s.io/api/discovery/v1" - "k8s.io/api/discovery/v1beta1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - klabels "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/selection" - "k8s.io/apimachinery/pkg/types" - listerv1 "k8s.io/client-go/listers/discovery/v1" - listerv1beta1 "k8s.io/client-go/listers/discovery/v1beta1" - "k8s.io/client-go/tools/cache" - mcs "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube/controller/filter" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - kubelib "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -type endpointSliceController struct { - kubeEndpoints - endpointCache *endpointSliceCache - useV1Resource bool -} - -var _ kubeEndpointsController = &endpointSliceController{} - -var ( - endpointSliceRequirement = labelRequirement(mcs.LabelServiceName, selection.DoesNotExist, nil) - endpointSliceSelector = klabels.NewSelector().Add(*endpointSliceRequirement) -) - -func newEndpointSliceController(c *Controller) *endpointSliceController { - // TODO Endpoints has a special cache, to filter out irrelevant updates to kube-system - // Investigate if we need this, or if EndpointSlice is makes this not relevant - useV1Resource := endpointSliceV1Available(c.client) - var informer cache.SharedIndexInformer - if useV1Resource { - informer = c.client.KubeInformer().Discovery().V1().EndpointSlices().Informer() - } else { - informer = c.client.KubeInformer().Discovery().V1beta1().EndpointSlices().Informer() - } - - filteredInformer := filter.NewFilteredSharedIndexInformer( - c.opts.DiscoveryNamespacesFilter.Filter, - informer, - ) - out := &endpointSliceController{ - kubeEndpoints: kubeEndpoints{ - c: c, - informer: filteredInformer, - }, - useV1Resource: useV1Resource, - endpointCache: newEndpointSliceCache(), - } - c.registerHandlers(filteredInformer, "EndpointSlice", out.onEvent, nil) - return out -} - -// TODO use this to automatically switch to EndpointSlice mode -func endpointSliceV1Available(client kubelib.Client) bool { - return client != nil && kubelib.IsAtLeastVersion(client, 21) -} - -func (esc *endpointSliceController) getInformer() filter.FilteredSharedIndexInformer { - return esc.informer -} - -func (esc *endpointSliceController) listSlices(ns string, selector klabels.Selector) (slices []interface{}, err error) { - if esc.useV1Resource { - var eps []*v1.EndpointSlice - eps, err = listerv1.NewEndpointSliceLister(esc.informer.GetIndexer()).EndpointSlices(ns).List(selector) - slices = make([]interface{}, len(eps)) - for i, ep := range eps { - slices[i] = ep - } - } else { - var eps []*v1beta1.EndpointSlice - eps, err = listerv1beta1.NewEndpointSliceLister(esc.informer.GetIndexer()).EndpointSlices(ns).List(selector) - slices = make([]interface{}, len(eps)) - for i, ep := range eps { - slices[i] = ep - } - } - return -} - -func (esc *endpointSliceController) onEvent(curr interface{}, event model.Event) error { - ep, ok := curr.(metav1.Object) - if !ok { - tombstone, ok := curr.(cache.DeletedFinalStateUnknown) - if !ok { - log.Errorf("Couldn't get object from tombstone %#v", curr) - return nil - } - epGvk, ok := tombstone.Obj.(runtime.Object) - if !ok || epGvk.GetObjectKind().GroupVersionKind().Kind != "EndpointSlice" { - log.Errorf("Tombstone contained an object that is not an endpoints slice %#v", curr) - return nil - } - } - - esLabels := ep.GetLabels() - if endpointSliceSelector.Matches(klabels.Set(esLabels)) { - return processEndpointEvent(esc.c, esc, serviceNameForEndpointSlice(esLabels), ep.GetNamespace(), event, ep) - } - return nil -} - -// GetProxyServiceInstances returns service instances co-located with a given proxy -// TODO: this code does not return k8s service instances when the proxy's IP is a workload entry -// To tackle this, we need a ip2instance map like what we have in service entry. -func (esc *endpointSliceController) GetProxyServiceInstances(c *Controller, proxy *model.Proxy) []*model.ServiceInstance { - eps, err := esc.listSlices(proxy.Metadata.Namespace, endpointSliceSelector) - if err != nil { - log.Errorf("Get endpointslice by index failed: %v", err) - return nil - } - var out []*model.ServiceInstance - for _, ep := range eps { - instances := esc.sliceServiceInstances(c, ep, proxy) - out = append(out, instances...) - } - - return out -} - -func serviceNameForEndpointSlice(labels map[string]string) string { - return labels[v1beta1.LabelServiceName] -} - -func (esc *endpointSliceController) sliceServiceInstances(c *Controller, slice interface{}, proxy *model.Proxy) []*model.ServiceInstance { - var out []*model.ServiceInstance - ep := wrapEndpointSlice(slice) - if ep.AddressType() == v1.AddressTypeFQDN { - // TODO(https://github.com/istio/istio/issues/34995) support FQDN endpointslice - return out - } - for _, svc := range c.servicesForNamespacedName(esc.getServiceNamespacedName(ep)) { - pod := c.pods.getPodByProxy(proxy) - builder := NewEndpointBuilder(c, pod) - - discoverabilityPolicy := c.exports.EndpointDiscoverabilityPolicy(svc) - - for _, port := range ep.Ports() { - if port.Name == nil || port.Port == nil { - continue - } - svcPort, exists := svc.Ports.Get(*port.Name) - if !exists { - continue - } - // consider multiple IP scenarios - for _, ip := range proxy.IPAddresses { - for _, ep := range ep.Endpoints() { - for _, a := range ep.Addresses { - if a == ip { - istioEndpoint := builder.buildIstioEndpoint(ip, *port.Port, svcPort.Name, discoverabilityPolicy) - out = append(out, &model.ServiceInstance{ - Endpoint: istioEndpoint, - ServicePort: svcPort, - Service: svc, - }) - // If the endpoint isn't ready, report this - if ep.Conditions.Ready != nil && !*ep.Conditions.Ready && c.opts.Metrics != nil { - c.opts.Metrics.AddMetric(model.ProxyStatusEndpointNotReady, proxy.ID, proxy.ID, "") - } - } - } - } - } - } - } - - return out -} - -func (esc *endpointSliceController) forgetEndpoint(endpoint interface{}) map[host.Name][]*model.IstioEndpoint { - slice := wrapEndpointSlice(endpoint) - key := kube.KeyFunc(slice.Name, slice.Namespace) - for _, e := range slice.Endpoints() { - for _, a := range e.Addresses { - esc.c.pods.endpointDeleted(key, a) - } - } - - out := make(map[host.Name][]*model.IstioEndpoint) - for _, hostName := range esc.c.hostNamesForNamespacedName(esc.getServiceNamespacedName(slice)) { - // endpointSlice cache update - if esc.endpointCache.Has(hostName) { - esc.endpointCache.Delete(hostName, slice.Name) - out[hostName] = esc.endpointCache.Get(hostName) - } - } - return out -} - -func (esc *endpointSliceController) buildIstioEndpoints(es interface{}, hostName host.Name) []*model.IstioEndpoint { - esc.updateEndpointCacheForSlice(hostName, es) - return esc.endpointCache.Get(hostName) -} - -func (esc *endpointSliceController) updateEndpointCacheForSlice(hostName host.Name, ep interface{}) { - var endpoints []*model.IstioEndpoint - slice := wrapEndpointSlice(ep) - if slice.AddressType() == v1.AddressTypeFQDN { - // TODO(https://github.com/istio/istio/issues/34995) support FQDN endpointslice - return - } - discoverabilityPolicy := esc.c.exports.EndpointDiscoverabilityPolicy(esc.c.GetService(hostName)) - - for _, e := range slice.Endpoints() { - if !features.SendUnhealthyEndpoints { - if e.Conditions.Ready != nil && !*e.Conditions.Ready { - // Ignore not ready endpoints - continue - } - } - ready := e.Conditions.Ready == nil || *e.Conditions.Ready - for _, a := range e.Addresses { - pod, expectedPod := getPod(esc.c, a, &metav1.ObjectMeta{Name: slice.Name, Namespace: slice.Namespace}, e.TargetRef, hostName) - if pod == nil && expectedPod { - continue - } - builder := NewEndpointBuilder(esc.c, pod) - // EDS and ServiceEntry use name for service port - ADS will need to map to numbers. - for _, port := range slice.Ports() { - var portNum int32 - if port.Port != nil { - portNum = *port.Port - } - var portName string - if port.Name != nil { - portName = *port.Name - } - - istioEndpoint := builder.buildIstioEndpoint(a, portNum, portName, discoverabilityPolicy) - if ready { - istioEndpoint.HealthStatus = model.Healthy - } else { - istioEndpoint.HealthStatus = model.UnHealthy - } - endpoints = append(endpoints, istioEndpoint) - } - } - } - esc.endpointCache.Update(hostName, slice.Name, endpoints) -} - -func (esc *endpointSliceController) buildIstioEndpointsWithService(name, namespace string, hostName host.Name, updateCache bool) []*model.IstioEndpoint { - esLabelSelector := endpointSliceSelectorForService(name) - slices, err := esc.listSlices(namespace, esLabelSelector) - if err != nil || len(slices) == 0 { - log.Debugf("endpoint slices of (%s, %s) not found => error %v", name, namespace, err) - return nil - } - - if updateCache { - // A cache update was requested. Rebuild the endpoints for these slices. - for _, slice := range slices { - esc.updateEndpointCacheForSlice(hostName, slice) - } - } - - return esc.endpointCache.Get(hostName) -} - -func (esc *endpointSliceController) getServiceNamespacedName(es interface{}) types.NamespacedName { - slice := es.(metav1.Object) - return types.NamespacedName{ - Namespace: slice.GetNamespace(), - Name: serviceNameForEndpointSlice(slice.GetLabels()), - } -} - -func (esc *endpointSliceController) InstancesByPort(c *Controller, svc *model.Service, reqSvcPort int, lbls labels.Instance) []*model.ServiceInstance { - esLabelSelector := endpointSliceSelectorForService(svc.Attributes.Name) - slices, err := esc.listSlices(svc.Attributes.Namespace, esLabelSelector) - if err != nil { - log.Infof("get endpoints(%s, %s) => error %v", svc.Attributes.Name, svc.Attributes.Namespace, err) - return nil - } - if len(slices) == 0 { - return nil - } - - // Locate all ports in the actual service - svcPort, exists := svc.Ports.GetByPort(reqSvcPort) - if !exists { - return nil - } - - discoverabilityPolicy := c.exports.EndpointDiscoverabilityPolicy(svc) - - var out []*model.ServiceInstance - for _, es := range slices { - slice := wrapEndpointSlice(es) - if slice.AddressType() == v1.AddressTypeFQDN { - // TODO(https://github.com/istio/istio/issues/34995) support FQDN endpointslice - continue - } - for _, e := range slice.Endpoints() { - for _, a := range e.Addresses { - var podLabels labels.Instance - pod, expectedPod := getPod(c, a, &metav1.ObjectMeta{Name: slice.Name, Namespace: slice.Namespace}, e.TargetRef, svc.Hostname) - if pod == nil && expectedPod { - continue - } - if pod != nil { - podLabels = pod.Labels - } - // check that one of the input labels is a subset of the labels - if !lbls.SubsetOf(podLabels) { - continue - } - - builder := NewEndpointBuilder(esc.c, pod) - // identify the port by name. K8S EndpointPort uses the service port name - for _, port := range slice.Ports() { - var portNum int32 - if port.Port != nil { - portNum = *port.Port - } - - if port.Name == nil || - svcPort.Name == *port.Name { - istioEndpoint := builder.buildIstioEndpoint(a, portNum, svcPort.Name, discoverabilityPolicy) - out = append(out, &model.ServiceInstance{ - Endpoint: istioEndpoint, - ServicePort: svcPort, - Service: svc, - }) - } - } - } - } - } - return out -} - -// TODO this isn't used now, but we may still want to extract locality from the v1 EnspointSlice instead of node -func getLocalityFromTopology(topology map[string]string) string { - locality := topology[NodeRegionLabelGA] - if _, f := topology[NodeZoneLabelGA]; f { - locality += "/" + topology[NodeZoneLabelGA] - } - if _, f := topology[label.TopologySubzone.Name]; f { - locality += "/" + topology[label.TopologySubzone.Name] - } - return locality -} - -// endpointKey unique identifies an endpoint by IP and port name -// This is used for deduping endpoints across slices. -type endpointKey struct { - ip string - port string -} - -type endpointSliceCache struct { - mu sync.RWMutex - endpointsByServiceAndSlice map[host.Name]map[string][]*model.IstioEndpoint -} - -func newEndpointSliceCache() *endpointSliceCache { - out := &endpointSliceCache{ - endpointsByServiceAndSlice: make(map[host.Name]map[string][]*model.IstioEndpoint), - } - return out -} - -func (e *endpointSliceCache) Update(hostname host.Name, slice string, endpoints []*model.IstioEndpoint) { - e.mu.Lock() - defer e.mu.Unlock() - if len(endpoints) == 0 { - delete(e.endpointsByServiceAndSlice[hostname], slice) - } - if _, f := e.endpointsByServiceAndSlice[hostname]; !f { - e.endpointsByServiceAndSlice[hostname] = make(map[string][]*model.IstioEndpoint) - } - // We will always overwrite. A conflict here means an endpoint is transitioning - // from one slice to another See - // https://github.com/kubernetes/website/blob/master/content/en/docs/concepts/services-networking/endpoint-slices.md#duplicate-endpoints - // In this case, we can always assume and update is fresh, although older slices - // we have not gotten updates may be stale; therefor we always take the new - // update. - e.endpointsByServiceAndSlice[hostname][slice] = endpoints -} - -func (e *endpointSliceCache) Delete(hostname host.Name, slice string) { - e.mu.Lock() - defer e.mu.Unlock() - - delete(e.endpointsByServiceAndSlice[hostname], slice) - if len(e.endpointsByServiceAndSlice[hostname]) == 0 { - delete(e.endpointsByServiceAndSlice, hostname) - } -} - -func (e *endpointSliceCache) Get(hostname host.Name) []*model.IstioEndpoint { - e.mu.RLock() - defer e.mu.RUnlock() - var endpoints []*model.IstioEndpoint - found := map[endpointKey]struct{}{} - for _, eps := range e.endpointsByServiceAndSlice[hostname] { - for _, ep := range eps { - key := endpointKey{ep.Address, ep.ServicePortName} - if _, f := found[key]; f { - // This a duplicate. Update() already handles conflict resolution, so we don't - // need to pick the "right" one here. - continue - } - found[key] = struct{}{} - endpoints = append(endpoints, ep) - } - } - return endpoints -} - -func (e *endpointSliceCache) Has(hostname host.Name) bool { - e.mu.RLock() - defer e.mu.RUnlock() - _, found := e.endpointsByServiceAndSlice[hostname] - return found -} - -func endpointSliceSelectorForService(name string) klabels.Selector { - return klabels.Set(map[string]string{ - v1beta1.LabelServiceName: name, - }).AsSelectorPreValidated().Add(*endpointSliceRequirement) -} - -func wrapEndpointSlice(slice interface{}) *endpointSliceWrapper { - switch es := slice.(type) { - case *v1.EndpointSlice: - return &endpointSliceWrapper{ObjectMeta: es.ObjectMeta, v1: es} - case *v1beta1.EndpointSlice: - return &endpointSliceWrapper{ObjectMeta: es.ObjectMeta, v1beta1: es} - } - return nil -} - -type endpointSliceWrapper struct { - metav1.ObjectMeta - v1beta1 *v1beta1.EndpointSlice - v1 *v1.EndpointSlice -} - -func (esw *endpointSliceWrapper) AddressType() v1.AddressType { - if esw.v1 != nil { - return esw.v1.AddressType - } - return v1.AddressType(esw.v1beta1.AddressType) -} - -func (esw *endpointSliceWrapper) Ports() []v1.EndpointPort { - if esw.v1 != nil { - return esw.v1.Ports - } - out := make([]v1.EndpointPort, len(esw.v1beta1.Ports)) - for i, p := range esw.v1beta1.Ports { - out[i] = v1.EndpointPort{ - Name: p.Name, - Protocol: p.Protocol, - Port: p.Port, - AppProtocol: p.AppProtocol, - } - } - return out -} - -func (esw *endpointSliceWrapper) Endpoints() []v1.Endpoint { - if esw.v1 != nil { - return esw.v1.Endpoints - } - out := make([]v1.Endpoint, len(esw.v1beta1.Endpoints)) - for i, ep := range esw.v1beta1.Endpoints { - zone := ep.Topology[NodeZoneLabelGA] - - var fz []v1.ForZone - if ep.Hints != nil { - fz = make([]v1.ForZone, len(ep.Hints.ForZones)) - for i, el := range fz { - fz[i] = v1.ForZone{Name: el.Name} - } - } - - out[i] = v1.Endpoint{ - Addresses: ep.Addresses, - Conditions: v1.EndpointConditions{ - Ready: ep.Conditions.Ready, - Serving: ep.Conditions.Serving, - Terminating: ep.Conditions.Serving, - }, - Hostname: ep.Hostname, - TargetRef: ep.TargetRef, - DeprecatedTopology: ep.Topology, - NodeName: ep.NodeName, - Zone: &zone, - Hints: &v1.EndpointHints{ - ForZones: fz, - }, - } - } - return out -} diff --git a/pilot/pkg/serviceregistry/kube/controller/endpointslice_test.go b/pilot/pkg/serviceregistry/kube/controller/endpointslice_test.go deleted file mode 100644 index bd6932b4a..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/endpointslice_test.go +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "reflect" - "testing" - "time" -) - -import ( - "istio.io/api/label" - coreV1 "k8s.io/api/core/v1" - "k8s.io/client-go/tools/cache" - mcs "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" -) - -func TestGetLocalityFromTopology(t *testing.T) { - cases := []struct { - name string - topology map[string]string - locality string - }{ - { - "all standard kubernetes labels", - map[string]string{ - NodeRegionLabelGA: "region", - NodeZoneLabelGA: "zone", - }, - "region/zone", - }, - { - "all standard kubernetes labels and Istio custom labels", - map[string]string{ - NodeRegionLabelGA: "region", - NodeZoneLabelGA: "zone", - label.TopologySubzone.Name: "subzone", - }, - "region/zone/subzone", - }, - { - "missing zone", - map[string]string{ - NodeRegionLabelGA: "region", - }, - "region", - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - got := getLocalityFromTopology(tt.topology) - if !reflect.DeepEqual(tt.locality, got) { - t.Fatalf("Expected %v, got %v", tt.topology, got) - } - }) - } -} - -func TestEndpointSliceFromMCSShouldBeIgnored(t *testing.T) { - const ( - ns = "nsa" - svcName = "svc1" - appName = "prod-app" - ) - - controller, fx := NewFakeControllerWithOptions(FakeControllerOptions{Mode: EndpointSliceOnly}) - go controller.Run(controller.stop) - cache.WaitForCacheSync(controller.stop, controller.HasSynced) - defer controller.Stop() - - node := generateNode("node1", map[string]string{ - NodeZoneLabel: "zone1", - NodeRegionLabel: "region1", - label.TopologySubzone.Name: "subzone1", - }) - addNodes(t, controller, node) - - pod := generatePod("128.0.0.1", "pod1", ns, "svcaccount", "node1", - map[string]string{"app": appName}, map[string]string{}) - pods := []*coreV1.Pod{pod} - addPods(t, controller, fx, pods...) - - createService(controller, svcName, ns, nil, - []int32{8080}, map[string]string{"app": appName}, t) - if ev := fx.Wait("service"); ev == nil { - t.Fatal("Timeout creating service") - } - - // Ensure that the service is available. - hostname := kube.ServiceHostname(svcName, ns, controller.opts.DomainSuffix) - svc := controller.GetService(hostname) - if svc == nil { - t.Fatal("failed to get service") - } - - // Create an endpoint that indicates it's an MCS endpoint for the service. - svc1Ips := []string{"128.0.0.1"} - portNames := []string{"tcp-port"} - createEndpoints(t, controller, svcName, ns, portNames, svc1Ips, nil, map[string]string{ - mcs.LabelServiceName: svcName, - }) - if ev := fx.WaitForDuration("eds", 200*time.Millisecond); ev != nil { - t.Fatalf("Received unexpected EDS event") - } - - // Ensure that getting by port returns no ServiceInstances. - instances := controller.InstancesByPort(svc, svc.Ports[0].Port, nil) - if len(instances) != 0 { - t.Fatalf("should be 0 instances: len(instances) = %v", len(instances)) - } -} - -func TestEndpointSliceCache(t *testing.T) { - cache := newEndpointSliceCache() - hostname := host.Name("foo") - - // add a endpoint - ep1 := &model.IstioEndpoint{ - Address: "1.2.3.4", - ServicePortName: "http", - } - cache.Update(hostname, "slice1", []*model.IstioEndpoint{ep1}) - if !testEndpointsEqual(cache.Get(hostname), []*model.IstioEndpoint{ep1}) { - t.Fatalf("unexpected endpoints") - } - if !cache.Has(hostname) { - t.Fatalf("expect to find the host name") - } - // add a new endpoint - ep2 := &model.IstioEndpoint{ - Address: "2.3.4.5", - ServicePortName: "http", - } - cache.Update(hostname, "slice1", []*model.IstioEndpoint{ep1, ep2}) - if !testEndpointsEqual(cache.Get(hostname), []*model.IstioEndpoint{ep1, ep2}) { - t.Fatalf("unexpected endpoints") - } - - // change service port name - ep1 = &model.IstioEndpoint{ - Address: "1.2.3.4", - ServicePortName: "http2", - } - ep2 = &model.IstioEndpoint{ - Address: "2.3.4.5", - ServicePortName: "http2", - } - cache.Update(hostname, "slice1", []*model.IstioEndpoint{ep1, ep2}) - if !testEndpointsEqual(cache.Get(hostname), []*model.IstioEndpoint{ep1, ep2}) { - t.Fatalf("unexpected endpoints") - } - - // add a new slice - ep3 := &model.IstioEndpoint{ - Address: "3.4.5.6", - ServicePortName: "http2", - } - cache.Update(hostname, "slice2", []*model.IstioEndpoint{ep3}) - if !testEndpointsEqual(cache.Get(hostname), []*model.IstioEndpoint{ep1, ep2, ep3}) { - t.Fatalf("unexpected endpoints") - } - - // dedup when transitioning - cache.Update(hostname, "slice2", []*model.IstioEndpoint{ep2, ep3}) - if !testEndpointsEqual(cache.Get(hostname), []*model.IstioEndpoint{ep1, ep2, ep3}) { - t.Fatalf("unexpected endpoints") - } - - cache.Delete(hostname, "slice1") - if !testEndpointsEqual(cache.Get(hostname), []*model.IstioEndpoint{ep2, ep3}) { - t.Fatalf("unexpected endpoints") - } - - cache.Delete(hostname, "slice2") - if cache.Get(hostname) != nil { - t.Fatalf("unexpected endpoints") - } -} - -func testEndpointsEqual(a, b []*model.IstioEndpoint) bool { - if len(a) != len(b) { - return false - } - m1 := make(map[endpointKey]int) - m2 := make(map[endpointKey]int) - for _, i := range a { - m1[endpointKey{i.Address, i.ServicePortName}]++ - } - for _, i := range b { - m2[endpointKey{i.Address, i.ServicePortName}]++ - } - return reflect.DeepEqual(m1, m2) -} diff --git a/pilot/pkg/serviceregistry/kube/controller/fake.go b/pilot/pkg/serviceregistry/kube/controller/fake.go deleted file mode 100644 index 1971ba84c..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/fake.go +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "time" -) - -import ( - meshconfig "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/aggregate" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube/controller/filter" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - kubelib "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -const ( - defaultFakeDomainSuffix = "company.com" -) - -// FakeXdsUpdater is used to test the registry. -type FakeXdsUpdater struct { - // Events tracks notifications received by the updater - Events chan FakeXdsEvent -} - -var _ model.XDSUpdater = &FakeXdsUpdater{} - -func (fx *FakeXdsUpdater) ConfigUpdate(req *model.PushRequest) { - var id string - if req != nil && len(req.ConfigsUpdated) > 0 { - for key := range req.ConfigsUpdated { - id = key.Name - } - } - select { - case fx.Events <- FakeXdsEvent{Type: "xds", ID: id}: - default: - } -} - -func (fx *FakeXdsUpdater) ProxyUpdate(_ cluster.ID, _ string) { - select { - case fx.Events <- FakeXdsEvent{Type: "proxy"}: - default: - } -} - -// FakeXdsEvent is used to watch XdsEvents -type FakeXdsEvent struct { - // Type of the event - Type string - - // The id of the event - ID string - - // The endpoints associated with an EDS push if any - Endpoints []*model.IstioEndpoint -} - -// NewFakeXDS creates a XdsUpdater reporting events via a channel. -func NewFakeXDS() *FakeXdsUpdater { - return &FakeXdsUpdater{ - Events: make(chan FakeXdsEvent, 100), - } -} - -func (fx *FakeXdsUpdater) EDSUpdate(_ model.ShardKey, hostname string, _ string, entry []*model.IstioEndpoint) { - if len(entry) > 0 { - select { - case fx.Events <- FakeXdsEvent{Type: "eds", ID: hostname, Endpoints: entry}: - default: - } - } -} - -func (fx *FakeXdsUpdater) EDSCacheUpdate(_ model.ShardKey, hostname, _ string, entry []*model.IstioEndpoint) { - if len(entry) > 0 { - select { - case fx.Events <- FakeXdsEvent{Type: "eds cache", ID: hostname, Endpoints: entry}: - default: - } - } -} - -// SvcUpdate is called when a service port mapping definition is updated. -// This interface is WIP - labels, annotations and other changes to service may be -// updated to force a EDS and CDS recomputation and incremental push, as it doesn't affect -// LDS/RDS. -func (fx *FakeXdsUpdater) SvcUpdate(_ model.ShardKey, hostname string, _ string, _ model.Event) { - select { - case fx.Events <- FakeXdsEvent{Type: "service", ID: hostname}: - default: - } -} - -func (fx *FakeXdsUpdater) RemoveShard(shardKey model.ShardKey) { - select { - case fx.Events <- FakeXdsEvent{Type: "removeShard", ID: shardKey.String()}: - default: - } -} - -func (fx *FakeXdsUpdater) WaitOrFail(t test.Failer, et string) *FakeXdsEvent { - return fx.WaitForDurationOrFail(t, et, 5*time.Second) -} - -func (fx *FakeXdsUpdater) WaitForDurationOrFail(t test.Failer, et string, d time.Duration) *FakeXdsEvent { - ev := fx.WaitForDuration(et, d) - if ev == nil { - t.Fatalf("Timeout creating %q after %s", et, d) - } - return ev -} - -func (fx *FakeXdsUpdater) Wait(et string) *FakeXdsEvent { - return fx.WaitForDuration(et, 5*time.Second) -} - -func (fx *FakeXdsUpdater) WaitForDuration(et string, d time.Duration) *FakeXdsEvent { - for { - select { - case e := <-fx.Events: - if e.Type == et { - return &e - } - continue - case <-time.After(d): - return nil - } - } -} - -// Clear any pending event -func (fx *FakeXdsUpdater) Clear() { - wait := true - for wait { - select { - case <-fx.Events: - default: - wait = false - } - } -} - -type FakeControllerOptions struct { - Client kubelib.Client - NetworksWatcher mesh.NetworksWatcher - MeshWatcher mesh.Watcher - ServiceHandler func(service *model.Service, event model.Event) - Mode EndpointMode - ClusterID cluster.ID - WatchedNamespaces string - DomainSuffix string - XDSUpdater model.XDSUpdater - DiscoveryNamespacesFilter filter.DiscoveryNamespacesFilter - Stop chan struct{} -} - -type FakeController struct { - *Controller -} - -func NewFakeControllerWithOptions(opts FakeControllerOptions) (*FakeController, *FakeXdsUpdater) { - xdsUpdater := opts.XDSUpdater - if xdsUpdater == nil { - xdsUpdater = NewFakeXDS() - } - - domainSuffix := defaultFakeDomainSuffix - if opts.DomainSuffix != "" { - domainSuffix = opts.DomainSuffix - } - if opts.Client == nil { - opts.Client = kubelib.NewFakeClient() - } - if opts.MeshWatcher == nil { - opts.MeshWatcher = mesh.NewFixedWatcher(&meshconfig.MeshConfig{}) - } - - meshServiceController := aggregate.NewController(aggregate.Options{MeshHolder: opts.MeshWatcher}) - - options := Options{ - DomainSuffix: domainSuffix, - XDSUpdater: xdsUpdater, - Metrics: &model.Environment{}, - NetworksWatcher: opts.NetworksWatcher, - MeshWatcher: opts.MeshWatcher, - EndpointMode: opts.Mode, - ClusterID: opts.ClusterID, - DiscoveryNamespacesFilter: opts.DiscoveryNamespacesFilter, - MeshServiceController: meshServiceController, - } - c := NewController(opts.Client, options) - meshServiceController.AddRegistry(c) - - if opts.ServiceHandler != nil { - c.AppendServiceHandler(opts.ServiceHandler) - } - c.stop = opts.Stop - if c.stop == nil { - c.stop = make(chan struct{}) - } - opts.Client.RunAndWait(c.stop) - var fx *FakeXdsUpdater - if x, ok := xdsUpdater.(*FakeXdsUpdater); ok { - fx = x - } - - return &FakeController{c}, fx -} diff --git a/pilot/pkg/serviceregistry/kube/controller/filter/discoverynamespaces.go b/pilot/pkg/serviceregistry/kube/controller/filter/discoverynamespaces.go deleted file mode 100644 index 6ccf7d182..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/filter/discoverynamespaces.go +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package filter - -import ( - "sync" -) - -import ( - "istio.io/pkg/log" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/util/sets" - listerv1 "k8s.io/client-go/listers/core/v1" - "k8s.io/client-go/tools/cache" -) - -// DiscoveryNamespacesFilter tracks the set of namespaces selected for discovery, which are updated by the discovery namespace controller. -// It exposes a filter function used for filtering out objects that don't reside in namespaces selected for discovery. -type DiscoveryNamespacesFilter interface { - // Filter returns true if the input object resides in a namespace selected for discovery - Filter(obj interface{}) bool - // SelectorsChanged is invoked when meshConfig's discoverySelectors change, returns any newly selected namespaces and deselected namespaces - SelectorsChanged(discoverySelectors []*metav1.LabelSelector) (selectedNamespaces []string, deselectedNamespaces []string) - // SyncNamespaces is invoked when namespace informer hasSynced before other controller SyncAll - SyncNamespaces() error - // NamespaceCreated returns true if the created namespace is selected for discovery - NamespaceCreated(ns metav1.ObjectMeta) (membershipChanged bool) - // NamespaceUpdated : membershipChanged will be true if the updated namespace is newly selected or deselected for discovery - NamespaceUpdated(oldNs, newNs metav1.ObjectMeta) (membershipChanged bool, namespaceAdded bool) - // NamespaceDeleted returns true if the deleted namespace was selected for discovery - NamespaceDeleted(ns metav1.ObjectMeta) (membershipChanged bool) - // GetMembers returns the namespaces selected for discovery - GetMembers() sets.String -} - -type discoveryNamespacesFilter struct { - lock sync.RWMutex - nsLister listerv1.NamespaceLister - discoveryNamespaces sets.String - discoverySelectors []labels.Selector // nil if discovery selectors are not specified, permits all namespaces for discovery -} - -func NewDiscoveryNamespacesFilter( - nsLister listerv1.NamespaceLister, - discoverySelectors []*metav1.LabelSelector, -) DiscoveryNamespacesFilter { - discoveryNamespacesFilter := &discoveryNamespacesFilter{ - nsLister: nsLister, - } - - // initialize discovery namespaces filter - discoveryNamespacesFilter.SelectorsChanged(discoverySelectors) - - return discoveryNamespacesFilter -} - -func (d *discoveryNamespacesFilter) Filter(obj interface{}) bool { - d.lock.RLock() - defer d.lock.RUnlock() - // permit all objects if discovery selectors are not specified - if len(d.discoverySelectors) == 0 { - return true - } - - // When an object is deleted, obj could be a DeletionFinalStateUnknown marker item. - object, ok := obj.(metav1.Object) - if !ok { - tombstone, ok := obj.(cache.DeletedFinalStateUnknown) - if !ok { - return false - } - object, ok = tombstone.Obj.(metav1.Object) - if !ok { - return false - } - } - - // permit if object resides in a namespace labeled for discovery - return d.discoveryNamespaces.Has(object.GetNamespace()) -} - -// SelectorsChanged initializes the discovery filter state with the discovery selectors and selected namespaces -func (d *discoveryNamespacesFilter) SelectorsChanged( - discoverySelectors []*metav1.LabelSelector, -) (selectedNamespaces []string, deselectedNamespaces []string) { - d.lock.Lock() - defer d.lock.Unlock() - - oldDiscoveryNamespaces := d.discoveryNamespaces - - var selectors []labels.Selector - newDiscoveryNamespaces := sets.NewString() - - namespaceList, err := d.nsLister.List(labels.Everything()) - if err != nil { - log.Errorf("error initializing discovery namespaces filter, failed to list namespaces: %v", err) - return - } - - // convert LabelSelectors to Selectors - for _, selector := range discoverySelectors { - ls, err := metav1.LabelSelectorAsSelector(selector) - if err != nil { - log.Errorf("error initializing discovery namespaces filter, invalid discovery selector: %v", err) - return - } - selectors = append(selectors, ls) - } - - // range over all namespaces to get discovery namespaces - for _, ns := range namespaceList { - for _, selector := range selectors { - if selector.Matches(labels.Set(ns.Labels)) { - newDiscoveryNamespaces.Insert(ns.Name) - } - } - // omitting discoverySelectors indicates discovering all namespaces - if len(selectors) == 0 { - for _, ns := range namespaceList { - newDiscoveryNamespaces.Insert(ns.Name) - } - } - } - - selectedNamespaces = newDiscoveryNamespaces.Difference(oldDiscoveryNamespaces).List() - deselectedNamespaces = oldDiscoveryNamespaces.Difference(newDiscoveryNamespaces).List() - - // update filter state - d.discoveryNamespaces = newDiscoveryNamespaces - d.discoverySelectors = selectors - - return -} - -func (d *discoveryNamespacesFilter) SyncNamespaces() error { - d.lock.Lock() - defer d.lock.Unlock() - - newDiscoveryNamespaces := sets.NewString() - - namespaceList, err := d.nsLister.List(labels.Everything()) - if err != nil { - log.Errorf("error initializing discovery namespaces filter, failed to list namespaces: %v", err) - return err - } - - // omitting discoverySelectors indicates discovering all namespaces - if len(d.discoverySelectors) == 0 { - for _, ns := range namespaceList { - newDiscoveryNamespaces.Insert(ns.Name) - } - } - - // range over all namespaces to get discovery namespaces - for _, ns := range namespaceList { - for _, selector := range d.discoverySelectors { - if selector.Matches(labels.Set(ns.Labels)) { - newDiscoveryNamespaces.Insert(ns.Name) - } - } - } - - // update filter state - d.discoveryNamespaces = newDiscoveryNamespaces - - return nil -} - -// NamespaceCreated : if newly created namespace is selected, update namespace membership -func (d *discoveryNamespacesFilter) NamespaceCreated(ns metav1.ObjectMeta) (membershipChanged bool) { - if d.isSelected(ns.Labels) { - d.addNamespace(ns.Name) - return true - } - return false -} - -// NamespaceUpdated : if updated namespace was a member and no longer selected, or was not a member and now selected, update namespace membership -func (d *discoveryNamespacesFilter) NamespaceUpdated(oldNs, newNs metav1.ObjectMeta) (membershipChanged bool, namespaceAdded bool) { - if d.hasNamespace(oldNs.Name) && !d.isSelected(newNs.Labels) { - d.removeNamespace(oldNs.Name) - return true, false - } - if !d.hasNamespace(oldNs.Name) && d.isSelected(newNs.Labels) { - d.addNamespace(oldNs.Name) - return true, true - } - return false, false -} - -// NamespaceDeleted : if deleted namespace was a member, remove it -func (d *discoveryNamespacesFilter) NamespaceDeleted(ns metav1.ObjectMeta) (membershipChanged bool) { - if d.isSelected(ns.Labels) { - d.removeNamespace(ns.Name) - return true - } - return false -} - -// GetMembers returns member namespaces -func (d *discoveryNamespacesFilter) GetMembers() sets.String { - d.lock.RLock() - defer d.lock.RUnlock() - members := sets.NewString() - members.Insert(d.discoveryNamespaces.List()...) - return members -} - -func (d *discoveryNamespacesFilter) addNamespace(ns string) { - d.lock.Lock() - defer d.lock.Unlock() - d.discoveryNamespaces.Insert(ns) -} - -func (d *discoveryNamespacesFilter) hasNamespace(ns string) bool { - d.lock.RLock() - defer d.lock.RUnlock() - return d.discoveryNamespaces.Has(ns) -} - -func (d *discoveryNamespacesFilter) removeNamespace(ns string) { - d.lock.Lock() - defer d.lock.Unlock() - d.discoveryNamespaces.Delete(ns) -} - -func (d *discoveryNamespacesFilter) isSelected(labels labels.Set) bool { - d.lock.RLock() - defer d.lock.RUnlock() - // permit all objects if discovery selectors are not specified - if len(d.discoverySelectors) == 0 { - return true - } - - for _, selector := range d.discoverySelectors { - if selector.Matches(labels) { - return true - } - } - - return false -} diff --git a/pilot/pkg/serviceregistry/kube/controller/filter/informer.go b/pilot/pkg/serviceregistry/kube/controller/filter/informer.go deleted file mode 100644 index 9db34e98a..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/filter/informer.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package filter - -import ( - "k8s.io/client-go/tools/cache" -) - -type FilteredSharedIndexInformer interface { - AddEventHandler(handler cache.ResourceEventHandler) - GetIndexer() cache.Indexer - HasSynced() bool - Run(stopCh <-chan struct{}) -} - -type filteredSharedIndexInformer struct { - filterFunc func(obj interface{}) bool - cache.SharedIndexInformer - filteredIndexer *filteredIndexer -} - -// NewFilteredSharedIndexInformer wraps a SharedIndexInformer's handlers and indexer with a filter predicate, -// which scopes the processed objects to only those that satisfy the predicate -func NewFilteredSharedIndexInformer( - filterFunc func(obj interface{}) bool, - sharedIndexInformer cache.SharedIndexInformer, -) FilteredSharedIndexInformer { - return &filteredSharedIndexInformer{ - filterFunc: filterFunc, - SharedIndexInformer: sharedIndexInformer, - filteredIndexer: newFilteredIndexer(filterFunc, sharedIndexInformer.GetIndexer()), - } -} - -// AddEventHandler filters incoming objects before forwarding to event handler -func (w *filteredSharedIndexInformer) AddEventHandler(handler cache.ResourceEventHandler) { - w.SharedIndexInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - if !w.filterFunc(obj) { - return - } - handler.OnAdd(obj) - }, - UpdateFunc: func(old, new interface{}) { - if !w.filterFunc(new) { - return - } - handler.OnUpdate(old, new) - }, - DeleteFunc: func(obj interface{}) { - if !w.filterFunc(obj) { - return - } - handler.OnDelete(obj) - }, - }) -} - -func (w *filteredSharedIndexInformer) HasSynced() bool { - w.SharedIndexInformer.GetStore() - return w.SharedIndexInformer.HasSynced() -} - -func (w *filteredSharedIndexInformer) Run(stopCh <-chan struct{}) { - w.SharedIndexInformer.Run(stopCh) -} - -func (w *filteredSharedIndexInformer) GetIndexer() cache.Indexer { - return w.filteredIndexer -} - -type filteredIndexer struct { - filterFunc func(obj interface{}) bool - cache.Indexer -} - -func newFilteredIndexer( - filterFunc func(obj interface{}) bool, - indexer cache.Indexer, -) *filteredIndexer { - return &filteredIndexer{ - filterFunc: filterFunc, - Indexer: indexer, - } -} - -func (w filteredIndexer) List() []interface{} { - unfiltered := w.Indexer.List() - var filtered []interface{} - for _, obj := range unfiltered { - if w.filterFunc(obj) { - filtered = append(filtered, obj) - } - } - return filtered -} - -func (w filteredIndexer) GetByKey(key string) (item interface{}, exists bool, err error) { - item, exists, err = w.Indexer.GetByKey(key) - if !exists || err != nil { - return nil, exists, err - } - if w.filterFunc(item) { - return item, true, nil - } - return nil, false, nil -} diff --git a/pilot/pkg/serviceregistry/kube/controller/leak_test.go b/pilot/pkg/serviceregistry/kube/controller/leak_test.go deleted file mode 100644 index 981266f82..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/serviceregistry/kube/controller/multicluster.go b/pilot/pkg/serviceregistry/kube/controller/multicluster.go deleted file mode 100644 index 2797194e2..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/multicluster.go +++ /dev/null @@ -1,323 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "context" - "fmt" - "sync" -) - -import ( - "golang.org/x/sync/errgroup" - "k8s.io/client-go/kubernetes" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/crdclient" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/keycertbundle" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/leaderelection" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/server" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/serviceentry" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - kubelib "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/multicluster" - "github.com/apache/dubbo-go-pixiu/pkg/webhooks" - "github.com/apache/dubbo-go-pixiu/pkg/webhooks/validation/controller" -) - -const ( - // Name of the webhook config in the config - no need to change it. - webhookName = "sidecar-injector.istio.io" -) - -var _ multicluster.ClusterHandler = &Multicluster{} - -type kubeController struct { - *Controller - workloadEntryController *serviceentry.Controller -} - -// Multicluster structure holds the remote kube Controllers and multicluster specific attributes. -type Multicluster struct { - // serverID of this pilot instance used for leader election - serverID string - - // options to use when creating kube controllers - opts Options - - // client for reading remote-secrets to initialize multicluster registries - client kubernetes.Interface - s server.Instance - closing bool - - serviceEntryController *serviceentry.Controller - XDSUpdater model.XDSUpdater - - m sync.Mutex // protects remoteKubeControllers - remoteKubeControllers map[cluster.ID]*kubeController - clusterLocal model.ClusterLocalProvider - - startNsController bool - caBundleWatcher *keycertbundle.Watcher - revision string - - // secretNamespace where we get cluster-access secrets - secretNamespace string -} - -// NewMulticluster initializes data structure to store multicluster information -func NewMulticluster( - serverID string, - kc kubernetes.Interface, - secretNamespace string, - opts Options, - serviceEntryController *serviceentry.Controller, - caBundleWatcher *keycertbundle.Watcher, - revision string, - startNsController bool, - clusterLocal model.ClusterLocalProvider, - s server.Instance) *Multicluster { - remoteKubeController := make(map[cluster.ID]*kubeController) - mc := &Multicluster{ - serverID: serverID, - opts: opts, - serviceEntryController: serviceEntryController, - startNsController: startNsController, - caBundleWatcher: caBundleWatcher, - revision: revision, - XDSUpdater: opts.XDSUpdater, - remoteKubeControllers: remoteKubeController, - clusterLocal: clusterLocal, - secretNamespace: secretNamespace, - client: kc, - s: s, - } - - return mc -} - -func (m *Multicluster) Run(stopCh <-chan struct{}) error { - // Wait for server shutdown. - <-stopCh - return m.close() -} - -func (m *Multicluster) close() (err error) { - m.m.Lock() - m.closing = true - - // Gather all of the member clusters. - var clusterIDs []cluster.ID - for clusterID := range m.remoteKubeControllers { - clusterIDs = append(clusterIDs, clusterID) - } - m.m.Unlock() - - // Remove all of the clusters. - g, _ := errgroup.WithContext(context.Background()) - for _, clusterID := range clusterIDs { - clusterID := clusterID - g.Go(func() error { - return m.ClusterDeleted(clusterID) - }) - } - err = g.Wait() - return -} - -// ClusterAdded is passed to the secret controller as a callback to be called -// when a remote cluster is added. This function needs to set up all the handlers -// to watch for resources being added, deleted or changed on remote clusters. -func (m *Multicluster) ClusterAdded(cluster *multicluster.Cluster, clusterStopCh <-chan struct{}) error { - m.m.Lock() - - if m.closing { - m.m.Unlock() - return fmt.Errorf("failed adding member cluster %s: server shutting down", cluster.ID) - } - - client := cluster.Client - - // clusterStopCh is a channel that will be closed when this cluster removed. - options := m.opts - options.ClusterID = cluster.ID - // the aggregate registry's HasSynced will use the k8s controller's HasSynced, so we reference the same timeout - options.SyncTimeout = cluster.SyncTimeout - // different clusters may have different k8s version, re-apply conditional default - options.EndpointMode = DetectEndpointMode(client) - - log.Infof("Initializing Kubernetes service registry %q", options.ClusterID) - kubeRegistry := NewController(client, options) - m.remoteKubeControllers[cluster.ID] = &kubeController{ - Controller: kubeRegistry, - } - // localCluster may also be the "config" cluster, in an external-istiod setup. - localCluster := m.opts.ClusterID == cluster.ID - - m.m.Unlock() - - // TODO move instance cache out of registries - if m.serviceEntryController != nil && features.EnableServiceEntrySelectPods { - // Add an instance handler in the kubernetes registry to notify service entry store about pod events - kubeRegistry.AppendWorkloadHandler(m.serviceEntryController.WorkloadInstanceHandler) - } - - // TODO implement deduping in aggregate registry to allow multiple k8s registries to handle WorkloadEntry - if m.serviceEntryController != nil && localCluster { - // Add an instance handler in the service entry store to notify kubernetes about workload entry events - m.serviceEntryController.AppendWorkloadHandler(kubeRegistry.WorkloadInstanceHandler) - } else if features.WorkloadEntryCrossCluster { - // TODO only do this for non-remotes, can't guarantee CRDs in remotes (depends on https://github.com/istio/istio/pull/29824) - if configStore, err := createWleConfigStore(client, m.revision, options); err == nil { - m.remoteKubeControllers[cluster.ID].workloadEntryController = serviceentry.NewWorkloadEntryController( - configStore, model.MakeIstioStore(configStore), options.XDSUpdater, - serviceentry.WithClusterID(cluster.ID), - serviceentry.WithNetworkIDCb(kubeRegistry.Network)) - // Services can select WorkloadEntry from the same cluster. We only duplicate the Service to configure kube-dns. - m.remoteKubeControllers[cluster.ID].workloadEntryController.AppendWorkloadHandler(kubeRegistry.WorkloadInstanceHandler) - // ServiceEntry selects WorkloadEntry from remote cluster - m.remoteKubeControllers[cluster.ID].workloadEntryController.AppendWorkloadHandler(m.serviceEntryController.WorkloadInstanceHandler) - m.opts.MeshServiceController.AddRegistryAndRun(m.remoteKubeControllers[cluster.ID].workloadEntryController, clusterStopCh) - go configStore.Run(clusterStopCh) - } else { - return fmt.Errorf("failed creating config configStore for cluster %s: %v", cluster.ID, err) - } - } - - // run after WorkloadHandler is added - m.opts.MeshServiceController.AddRegistryAndRun(kubeRegistry, clusterStopCh) - - if m.startNsController && (features.ExternalIstiod || localCluster) { - // Block server exit on graceful termination of the leader controller. - m.s.RunComponentAsyncAndWait(func(_ <-chan struct{}) error { - log.Infof("joining leader-election for %s in %s on cluster %s", - leaderelection.NamespaceController, options.SystemNamespace, options.ClusterID) - leaderelection. - NewLeaderElectionMulticluster(options.SystemNamespace, m.serverID, leaderelection.NamespaceController, m.revision, !localCluster, client). - AddRunFunction(func(leaderStop <-chan struct{}) { - log.Infof("starting namespace controller for cluster %s", cluster.ID) - nc := NewNamespaceController(client, m.caBundleWatcher) - // Start informers again. This fixes the case where informers for namespace do not start, - // as we create them only after acquiring the leader lock - // Note: stop here should be the overall pilot stop, NOT the leader election stop. We are - // basically lazy loading the informer, if we stop it when we lose the lock we will never - // recreate it again. - client.RunAndWait(clusterStopCh) - nc.Run(leaderStop) - }).Run(clusterStopCh) - return nil - }) - } - - // The local cluster has this patching set-up elsewhere. We may eventually want to move it here. - if features.ExternalIstiod && !localCluster && m.caBundleWatcher != nil { - // Patch injection webhook cert - // This requires RBAC permissions - a low-priv Istiod should not attempt to patch but rely on - // operator or CI/CD - if features.InjectionWebhookConfigName != "" { - // TODO prevent istiods in primary clusters from trying to patch eachother. should we also leader-elect? - log.Infof("initializing webhook cert patch for cluster %s", cluster.ID) - patcher, err := webhooks.NewWebhookCertPatcher(client, m.revision, webhookName, m.caBundleWatcher) - if err != nil { - log.Errorf("could not initialize webhook cert patcher: %v", err) - } else { - go patcher.Run(clusterStopCh) - } - } - // Patch validation webhook cert - go controller.NewValidatingWebhookController(client, m.revision, m.secretNamespace, m.caBundleWatcher).Run(clusterStopCh) - - } - - // setting up the serviceexport controller if and only if it is turned on in the meshconfig. - // TODO(nmittler): Need a better solution. Leader election doesn't take into account locality. - if features.EnableMCSAutoExport { - log.Infof("joining leader-election for %s in %s on cluster %s", - leaderelection.ServiceExportController, options.SystemNamespace, options.ClusterID) - // Block server exit on graceful termination of the leader controller. - m.s.RunComponentAsyncAndWait(func(_ <-chan struct{}) error { - leaderelection. - NewLeaderElection(options.SystemNamespace, m.serverID, leaderelection.ServiceExportController, m.revision, client). - AddRunFunction(func(leaderStop <-chan struct{}) { - serviceExportController := newAutoServiceExportController(autoServiceExportOptions{ - Client: client, - ClusterID: options.ClusterID, - DomainSuffix: options.DomainSuffix, - ClusterLocal: m.clusterLocal, - }) - // Start informers again. This fixes the case where informers do not start, - // as we create them only after acquiring the leader lock - // Note: stop here should be the overall pilot stop, NOT the leader election stop. We are - // basically lazy loading the informer, if we stop it when we lose the lock we will never - // recreate it again. - client.RunAndWait(clusterStopCh) - serviceExportController.Run(leaderStop) - }).Run(clusterStopCh) - return nil - }) - } - - return nil -} - -// ClusterUpdated is passed to the secret controller as a callback to be called -// when a remote cluster is updated. -func (m *Multicluster) ClusterUpdated(cluster *multicluster.Cluster, stop <-chan struct{}) error { - if err := m.ClusterDeleted(cluster.ID); err != nil { - return err - } - return m.ClusterAdded(cluster, stop) -} - -// ClusterDeleted is passed to the secret controller as a callback to be called -// when a remote cluster is deleted. Also must clear the cache so remote resources -// are removed. -func (m *Multicluster) ClusterDeleted(clusterID cluster.ID) error { - m.m.Lock() - defer m.m.Unlock() - m.opts.MeshServiceController.UnRegisterHandlersForCluster(clusterID) - m.opts.MeshServiceController.DeleteRegistry(clusterID, provider.Kubernetes) - kc, ok := m.remoteKubeControllers[clusterID] - if !ok { - log.Infof("cluster %s does not exist, maybe caused by invalid kubeconfig", clusterID) - return nil - } - if kc.workloadEntryController != nil { - m.opts.MeshServiceController.DeleteRegistry(clusterID, provider.External) - } - if err := kc.Cleanup(); err != nil { - log.Warnf("failed cleaning up services in %s: %v", clusterID, err) - } - delete(m.remoteKubeControllers, clusterID) - if m.XDSUpdater != nil { - m.XDSUpdater.ConfigUpdate(&model.PushRequest{Full: true, Reason: []model.TriggerReason{model.ClusterUpdate}}) - } - - return nil -} - -func createWleConfigStore(client kubelib.Client, revision string, opts Options) (model.ConfigStoreController, error) { - log.Infof("Creating WorkloadEntry only config store for %s", opts.ClusterID) - workloadEntriesSchemas := collection.NewSchemasBuilder(). - MustAdd(collections.IstioNetworkingV1Alpha3Workloadentries). - Build() - return crdclient.NewForSchemas(client, revision, opts.DomainSuffix, workloadEntriesSchemas) -} diff --git a/pilot/pkg/serviceregistry/kube/controller/multicluster_test.go b/pilot/pkg/serviceregistry/kube/controller/multicluster_test.go deleted file mode 100644 index ce3fb9dc2..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/multicluster_test.go +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "context" - "testing" - "time" -) - -import ( - meshconfig "istio.io/api/mesh/v1alpha1" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/keycertbundle" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/server" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/aggregate" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/multicluster" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -const ( - testSecretNameSpace = "dubbo-system" - DomainSuffix = "fake_domain" -) - -var mockserviceController = aggregate.NewController(aggregate.Options{}) - -func createMultiClusterSecret(k8s kube.Client, sname, cname string) error { - data := map[string][]byte{} - secret := v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: sname, - Namespace: testSecretNameSpace, - Labels: map[string]string{ - multicluster.MultiClusterSecretLabel: "true", - }, - }, - Data: map[string][]byte{}, - } - - data[cname] = []byte("Test") - secret.Data = data - _, err := k8s.CoreV1().Secrets(testSecretNameSpace).Create(context.TODO(), &secret, metav1.CreateOptions{}) - return err -} - -func deleteMultiClusterSecret(k8s kube.Client, sname string) error { - var immediate int64 - - return k8s.CoreV1().Secrets(testSecretNameSpace).Delete( - context.TODO(), - sname, metav1.DeleteOptions{GracePeriodSeconds: &immediate}) -} - -func verifyControllers(t *testing.T, m *Multicluster, expectedControllerCount int, timeoutName string) { - t.Helper() - retry.UntilOrFail(t, func() bool { - m.m.Lock() - defer m.m.Unlock() - return len(m.remoteKubeControllers) == expectedControllerCount - }, retry.Message(timeoutName), retry.Delay(time.Millisecond*10), retry.Timeout(time.Second*5)) -} - -func initController(client kube.ExtendedClient, ns string, stop <-chan struct{}, mc *Multicluster) { - sc := multicluster.NewController(client, ns, "cluster-1") - sc.AddHandler(mc) - _ = sc.Run(stop) - cache.WaitForCacheSync(stop, sc.HasSynced) -} - -func Test_KubeSecretController(t *testing.T) { - multicluster.BuildClientsFromConfig = func(kubeConfig []byte) (kube.Client, error) { - return kube.NewFakeClient(), nil - } - clientset := kube.NewFakeClient() - stop := make(chan struct{}) - t.Cleanup(func() { - close(stop) - }) - s := server.New() - mc := NewMulticluster( - "pilot-abc-123", - clientset, - testSecretNameSpace, - Options{ - ClusterID: "cluster-1", - DomainSuffix: DomainSuffix, - MeshWatcher: mesh.NewFixedWatcher(&meshconfig.MeshConfig{}), - MeshServiceController: mockserviceController, - }, nil, nil, "default", false, nil, s) - initController(clientset, testSecretNameSpace, stop, mc) - clientset.RunAndWait(stop) - _ = s.Start(stop) - go func() { - _ = mc.Run(stop) - }() - go mockserviceController.Run(stop) - - verifyControllers(t, mc, 1, "create local controller") - - // Create the multicluster secret. Sleep to allow created remote - // controller to start and callback add function to be called. - err := createMultiClusterSecret(clientset, "test-secret-1", "test-remote-cluster-1") - if err != nil { - t.Fatalf("Unexpected error on secret create: %v", err) - } - - // Test - Verify that the remote controller has been added. - verifyControllers(t, mc, 2, "create remote controller") - - // Delete the mulicluster secret. - err = deleteMultiClusterSecret(clientset, "test-secret-1") - if err != nil { - t.Fatalf("Unexpected error on secret delete: %v", err) - } - - // Test - Verify that the remote controller has been removed. - verifyControllers(t, mc, 1, "delete remote controller") -} - -func Test_KubeSecretController_ExternalIstiod_MultipleClusters(t *testing.T) { - test.SetBoolForTest(t, &features.ExternalIstiod, true) - test.SetStringForTest(t, &features.InjectionWebhookConfigName, "") - clientset := kube.NewFakeClient() - multicluster.BuildClientsFromConfig = func(kubeConfig []byte) (kube.Client, error) { - return kube.NewFakeClient(), nil - } - stop := make(chan struct{}) - t.Cleanup(func() { - close(stop) - }) - s := server.New() - certWatcher := keycertbundle.NewWatcher() - mc := NewMulticluster( - "pilot-abc-123", - clientset, - testSecretNameSpace, - Options{ - ClusterID: "cluster-1", - DomainSuffix: DomainSuffix, - MeshWatcher: mesh.NewFixedWatcher(&meshconfig.MeshConfig{}), - MeshServiceController: mockserviceController, - }, nil, certWatcher, "default", false, nil, s) - initController(clientset, testSecretNameSpace, stop, mc) - clientset.RunAndWait(stop) - _ = s.Start(stop) - go func() { - _ = mc.Run(stop) - }() - go mockserviceController.Run(stop) - - // the multicluster controller will register the local cluster - verifyControllers(t, mc, 1, "registered local cluster controller") - - // Create the multicluster secret. Sleep to allow created remote - // controller to start and callback add function to be called. - err := createMultiClusterSecret(clientset, "test-secret-1", "test-remote-cluster-1") - if err != nil { - t.Fatalf("Unexpected error on secret create: %v", err) - } - - // Test - Verify that the remote controller has been added. - verifyControllers(t, mc, 2, "create remote controller 1") - - // Create second multicluster secret. Sleep to allow created remote - // controller to start and callback add function to be called. - err = createMultiClusterSecret(clientset, "test-secret-2", "test-remote-cluster-2") - if err != nil { - t.Fatalf("Unexpected error on secret create: %v", err) - } - - // Test - Verify that the remote controller has been added. - verifyControllers(t, mc, 3, "create remote controller 2") - - // Delete the first mulicluster secret. - err = deleteMultiClusterSecret(clientset, "test-secret-1") - if err != nil { - t.Fatalf("Unexpected error on secret delete: %v", err) - } - - // Test - Verify that the remote controller has been removed. - verifyControllers(t, mc, 2, "delete remote controller 1") - - // Delete the second mulicluster secret. - err = deleteMultiClusterSecret(clientset, "test-secret-2") - if err != nil { - t.Fatalf("Unexpected error on secret delete: %v", err) - } - - // Test - Verify that the remote controller has been removed. - verifyControllers(t, mc, 1, "delete remote controller 2") -} diff --git a/pilot/pkg/serviceregistry/kube/controller/namespacecontroller.go b/pilot/pkg/serviceregistry/kube/controller/namespacecontroller.go deleted file mode 100644 index 895e67444..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/namespacecontroller.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/types" - corev1 "k8s.io/client-go/kubernetes/typed/core/v1" - listerv1 "k8s.io/client-go/listers/core/v1" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/keycertbundle" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/controllers" - "github.com/apache/dubbo-go-pixiu/pkg/kube/inject" - "github.com/apache/dubbo-go-pixiu/security/pkg/k8s" -) - -const ( - // CACertNamespaceConfigMap is the name of the ConfigMap in each namespace storing the root cert of non-Kube CA. - CACertNamespaceConfigMap = "istio-ca-root-cert" -) - -var configMapLabel = map[string]string{"istio.io/config": "true"} - -// NamespaceController manages reconciles a configmap in each namespace with a desired set of data. -type NamespaceController struct { - client corev1.CoreV1Interface - caBundleWatcher *keycertbundle.Watcher - - queue controllers.Queue - namespacesInformer cache.SharedInformer - configMapInformer cache.SharedInformer - namespaceLister listerv1.NamespaceLister - configmapLister listerv1.ConfigMapLister -} - -// NewNamespaceController returns a pointer to a newly constructed NamespaceController instance. -func NewNamespaceController(kubeClient kube.Client, caBundleWatcher *keycertbundle.Watcher) *NamespaceController { - c := &NamespaceController{ - client: kubeClient.CoreV1(), - caBundleWatcher: caBundleWatcher, - } - c.queue = controllers.NewQueue("namespace controller", controllers.WithReconciler(c.insertDataForNamespace)) - - c.configMapInformer = kubeClient.KubeInformer().Core().V1().ConfigMaps().Informer() - c.configmapLister = kubeClient.KubeInformer().Core().V1().ConfigMaps().Lister() - c.namespacesInformer = kubeClient.KubeInformer().Core().V1().Namespaces().Informer() - c.namespaceLister = kubeClient.KubeInformer().Core().V1().Namespaces().Lister() - - c.configMapInformer.AddEventHandler(controllers.FilteredObjectSpecHandler(c.queue.AddObject, func(o controllers.Object) bool { - if o.GetName() != CACertNamespaceConfigMap { - // This is a change to a configmap we don't watch, ignore it - return false - } - if inject.IgnoredNamespaces.Contains(o.GetNamespace()) { - // skip special kubernetes system namespaces - return false - } - return true - })) - c.namespacesInformer.AddEventHandler(controllers.FilteredObjectSpecHandler(c.queue.AddObject, func(o controllers.Object) bool { - return !inject.IgnoredNamespaces.Contains(o.GetName()) - })) - - return c -} - -// Run starts the NamespaceController until a value is sent to stopCh. -func (nc *NamespaceController) Run(stopCh <-chan struct{}) { - if !cache.WaitForCacheSync(stopCh, nc.namespacesInformer.HasSynced, nc.configMapInformer.HasSynced) { - log.Error("Failed to sync namespace controller cache") - return - } - go nc.startCaBundleWatcher(stopCh) - nc.queue.Run(stopCh) -} - -// startCaBundleWatcher listens for updates to the CA bundle and update cm in each namespace -func (nc *NamespaceController) startCaBundleWatcher(stop <-chan struct{}) { - id, watchCh := nc.caBundleWatcher.AddWatcher() - defer nc.caBundleWatcher.RemoveWatcher(id) - for { - select { - case <-watchCh: - namespaceList, _ := nc.namespaceLister.List(labels.Everything()) - for _, ns := range namespaceList { - nc.namespaceChange(ns) - } - case <-stop: - return - } - } -} - -// insertDataForNamespace will add data into the configmap for the specified namespace -// If the configmap is not found, it will be created. -// If you know the current contents of the configmap, using UpdateDataInConfigMap is more efficient. -func (nc *NamespaceController) insertDataForNamespace(o types.NamespacedName) error { - ns := o.Namespace - if ns == "" { - // For Namespace object, it will not have o.Namespace field set - ns = o.Name - } - meta := metav1.ObjectMeta{ - Name: CACertNamespaceConfigMap, - Namespace: ns, - Labels: configMapLabel, - } - return k8s.InsertDataToConfigMap(nc.client, nc.configmapLister, meta, nc.caBundleWatcher.GetCABundle()) -} - -// On namespace change, update the config map. -// If terminating, this will be skipped -func (nc *NamespaceController) namespaceChange(ns *v1.Namespace) { - if ns.Status.Phase != v1.NamespaceTerminating { - nc.syncNamespace(ns.Name) - } -} - -func (nc *NamespaceController) syncNamespace(ns string) { - // skip special kubernetes system namespaces - if inject.IgnoredNamespaces.Contains(ns) { - return - } - nc.queue.Add(types.NamespacedName{Name: ns}) -} diff --git a/pilot/pkg/serviceregistry/kube/controller/namespacecontroller_test.go b/pilot/pkg/serviceregistry/kube/controller/namespacecontroller_test.go deleted file mode 100644 index 5f98291de..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/namespacecontroller_test.go +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "context" - "fmt" - "reflect" - "testing" - "time" -) - -import ( - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - listerv1 "k8s.io/client-go/listers/core/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/keycertbundle" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/inject" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -func TestNamespaceController(t *testing.T) { - client := kube.NewFakeClient() - watcher := keycertbundle.NewWatcher() - caBundle := []byte("caBundle") - watcher.SetAndNotify(nil, nil, caBundle) - nc := NewNamespaceController(client, watcher) - nc.configmapLister = client.KubeInformer().Core().V1().ConfigMaps().Lister() - stop := make(chan struct{}) - t.Cleanup(func() { - close(stop) - }) - client.RunAndWait(stop) - go nc.Run(stop) - retry.UntilOrFail(t, nc.queue.HasSynced) - - expectedData := map[string]string{ - constants.CACertNamespaceConfigMapDataName: string(caBundle), - } - createNamespace(t, client, "foo", nil) - expectConfigMap(t, nc.configmapLister, CACertNamespaceConfigMap, "foo", expectedData) - - // Make sure random configmap does not get updated - cmData := createConfigMap(t, client, "not-root", "foo", "k") - expectConfigMap(t, nc.configmapLister, "not-root", "foo", cmData) - - newCaBundle := []byte("caBundle-new") - watcher.SetAndNotify(nil, nil, newCaBundle) - newData := map[string]string{ - constants.CACertNamespaceConfigMapDataName: string(newCaBundle), - } - expectConfigMap(t, nc.configmapLister, CACertNamespaceConfigMap, "foo", newData) - - deleteConfigMap(t, client, "foo") - expectConfigMap(t, nc.configmapLister, CACertNamespaceConfigMap, "foo", newData) - - for _, namespace := range inject.IgnoredNamespaces.UnsortedList() { - // Create namespace in ignored list, make sure its not created - createNamespace(t, client, namespace, newData) - // Configmap in that namespace should not do anything either - createConfigMap(t, client, "not-root", namespace, "k") - expectConfigMapNotExist(t, nc.configmapLister, namespace) - } -} - -func deleteConfigMap(t *testing.T, client kubernetes.Interface, ns string) { - t.Helper() - _, err := client.CoreV1().ConfigMaps(ns).Get(context.TODO(), CACertNamespaceConfigMap, metav1.GetOptions{}) - if err != nil { - t.Fatal(err) - } - if err := client.CoreV1().ConfigMaps(ns).Delete(context.TODO(), CACertNamespaceConfigMap, metav1.DeleteOptions{}); err != nil { - t.Fatal(err) - } -} - -func createConfigMap(t *testing.T, client kubernetes.Interface, name, ns, key string) map[string]string { - t.Helper() - data := map[string]string{key: "v"} - _, err := client.CoreV1().ConfigMaps(ns).Create(context.Background(), &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Data: data, - }, metav1.CreateOptions{}) - if err != nil { - t.Fatal(err) - } - return data -} - -func createNamespace(t *testing.T, client kubernetes.Interface, ns string, labels map[string]string) { - t.Helper() - if _, err := client.CoreV1().Namespaces().Create(context.TODO(), &v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{Name: ns, Labels: labels}, - }, metav1.CreateOptions{}); err != nil { - t.Fatal(err) - } -} - -func updateNamespace(t *testing.T, client kubernetes.Interface, ns string, labels map[string]string) { - t.Helper() - if _, err := client.CoreV1().Namespaces().Update(context.TODO(), &v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{Name: ns, Labels: labels}, - }, metav1.UpdateOptions{}); err != nil { - t.Fatal(err) - } -} - -// nolint:unparam -func expectConfigMap(t *testing.T, client listerv1.ConfigMapLister, name, ns string, data map[string]string) { - t.Helper() - retry.UntilSuccessOrFail(t, func() error { - cm, err := client.ConfigMaps(ns).Get(name) - if err != nil { - return err - } - if !reflect.DeepEqual(cm.Data, data) { - return fmt.Errorf("data mismatch, expected %+v got %+v", data, cm.Data) - } - return nil - }, retry.Timeout(time.Second*10)) -} - -func expectConfigMapNotExist(t *testing.T, client listerv1.ConfigMapLister, ns string) { - t.Helper() - err := retry.Until(func() bool { - _, err := client.ConfigMaps(ns).Get(CACertNamespaceConfigMap) - return err == nil - }, retry.Timeout(time.Millisecond*25)) - - if err == nil { - t.Fatalf("%s namespace should not have istio-ca-root-cert configmap.", ns) - } -} diff --git a/pilot/pkg/serviceregistry/kube/controller/network.go b/pilot/pkg/serviceregistry/kube/controller/network.go deleted file mode 100644 index 4b2954528..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/network.go +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "net" - "strconv" -) - -import ( - "github.com/yl2chen/cidranger" - "istio.io/api/label" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/network" -) - -type multinetwork struct { - // CIDR ranger based on path-compressed prefix trie - ranger cidranger.Ranger - - // Network name for to be used when the meshNetworks fromRegistry nor network label on pod is specified - // This is defined by a topology.istio.io/network label on the system namespace. - network network.ID - // Network name for the registry as specified by the MeshNetworks configmap - networkForRegistry network.ID - // map of svc fqdn to partially built network gateways; the actual gateways will be built from these into networkGatewaysBySvc - // this map just enumerates which networks/ports each Service is a gateway for - registryServiceNameGateways map[host.Name][]model.NetworkGateway - // gateways for each service - networkGatewaysBySvc map[host.Name]model.NetworkGatewaySet - // implements NetworkGatewaysWatcher; we need to call c.NotifyGatewayHandlers when our gateways change - model.NetworkGatewaysHandler -} - -func initMultinetwork() multinetwork { - return multinetwork{ - // zero values are a workaround structcheck issue: https://github.com/golangci/golangci-lint/issues/826 - ranger: nil, - network: "", - networkForRegistry: "", - registryServiceNameGateways: make(map[host.Name][]model.NetworkGateway), - networkGatewaysBySvc: make(map[host.Name]model.NetworkGatewaySet), - } -} - -// namedRangerEntry for holding network's CIDR and name -type namedRangerEntry struct { - name network.ID - network net.IPNet -} - -// Network returns the IPNet for the network -func (n namedRangerEntry) Network() net.IPNet { - return n.network -} - -// onDefaultNetworkChange is fired if the default network is changed either via the namespace label or mesh-networks -func (c *Controller) onDefaultNetworkChange() { - // the network for endpoints are computed when we process the events; this will fix the cache - // NOTE: this must run before the other network watcher handler that creates a force push - if err := c.syncPods(); err != nil { - log.Errorf("one or more errors force-syncing pods: %v", err) - } - if err := c.syncEndpoints(); err != nil { - log.Errorf("one or more errors force-syncing endpoints: %v", err) - } - c.reloadNetworkGateways() -} - -// reloadNetworkLookup refreshes the meshNetworks configuration, network for each endpoint, and -// recomputes network gateways. -func (c *Controller) reloadNetworkLookup() { - c.reloadMeshNetworks() - c.onDefaultNetworkChange() -} - -// reloadMeshNetworks will read the mesh networks configuration to setup -// fromRegistry and cidr based network lookups for this registry -func (c *Controller) reloadMeshNetworks() { - c.Lock() - defer c.Unlock() - c.networkForRegistry = "" - ranger := cidranger.NewPCTrieRanger() - - c.networkForRegistry = "" - c.registryServiceNameGateways = make(map[host.Name][]model.NetworkGateway) - - meshNetworks := c.opts.NetworksWatcher.Networks() - if meshNetworks == nil || len(meshNetworks.Networks) == 0 { - return - } - for n, v := range meshNetworks.Networks { - // track endpoints items from this registry are a part of this network - fromRegistry := false - for _, ep := range v.Endpoints { - if ep.GetFromCidr() != "" { - _, nw, err := net.ParseCIDR(ep.GetFromCidr()) - if err != nil { - log.Warnf("unable to parse CIDR %q for network %s", ep.GetFromCidr(), n) - continue - } - rangerEntry := namedRangerEntry{ - name: network.ID(n), - network: *nw, - } - _ = ranger.Insert(rangerEntry) - } - if ep.GetFromRegistry() != "" && cluster.ID(ep.GetFromRegistry()) == c.Cluster() { - fromRegistry = true - } - } - - // fromRegistry field specified this cluster - if fromRegistry { - // treat endpoints in this cluster as part of this network - if c.networkForRegistry != "" { - log.Warnf("multiple networks specify %s in fromRegistry; endpoints from %s will continue to be treated as part of %s", - c.Cluster(), c.Cluster(), c.networkForRegistry) - } else { - c.networkForRegistry = network.ID(n) - } - - // services in this registry matching the registryServiceName and port are part of this network - for _, gw := range v.Gateways { - if gwSvcName := gw.GetRegistryServiceName(); gwSvcName != "" { - svc := host.Name(gwSvcName) - c.registryServiceNameGateways[svc] = append(c.registryServiceNameGateways[svc], model.NetworkGateway{ - Network: network.ID(n), - Cluster: c.Cluster(), - Port: gw.GetPort(), - }) - } - } - } - - } - c.ranger = ranger -} - -func (c *Controller) NetworkGateways() []model.NetworkGateway { - c.RLock() - defer c.RUnlock() - - if len(c.networkGatewaysBySvc) == 0 { - return nil - } - - // Merge all the gateways into a single set to eliminate duplicates. - out := make(model.NetworkGatewaySet) - for _, gateways := range c.networkGatewaysBySvc { - out.AddAll(gateways) - } - - return out.ToArray() -} - -// extractGatewaysFromService checks if the service is a cross-network gateway -// and if it is, updates the controller's gateways. -func (c *Controller) extractGatewaysFromService(svc *model.Service) bool { - c.Lock() - changed := c.extractGatewaysInner(svc) - c.Unlock() - if changed { - c.NotifyGatewayHandlers() - } - return changed -} - -// reloadNetworkGateways performs extractGatewaysFromService for all services registered with the controller. -func (c *Controller) reloadNetworkGateways() { - c.Lock() - gwsChanged := false - for _, svc := range c.servicesMap { - if c.extractGatewaysInner(svc) { - gwsChanged = true - break - } - } - c.Unlock() - if gwsChanged { - c.NotifyGatewayHandlers() - // TODO ConfigUpdate via gateway handler - c.opts.XDSUpdater.ConfigUpdate(&model.PushRequest{Full: true, Reason: []model.TriggerReason{model.NetworksTrigger}}) - } -} - -// extractGatewaysInner performs the logic for extractGatewaysFromService without locking the controller. -// Returns true if any gateways changed. -func (c *Controller) extractGatewaysInner(svc *model.Service) bool { - newGateways := make(model.NetworkGatewaySet) - - // check if we have node port mappings - nodePortMap := make(map[uint32]uint32) - if svc.Attributes.ClusterExternalPorts != nil { - if npm, exists := svc.Attributes.ClusterExternalPorts[c.Cluster()]; exists { - nodePortMap = npm - } - } - - gateways := c.getGatewayDetails(svc) - - for _, addr := range svc.Attributes.ClusterExternalAddresses.GetAddressesFor(c.Cluster()) { - for _, gw := range gateways { - // what we now have is a service port. If there is a mapping for cluster external ports, - // look it up and get the node port for the remote port - if nodePort, exists := nodePortMap[gw.Port]; exists { - gw.Port = nodePort - } - - gw.Cluster = c.Cluster() - gw.Addr = addr - newGateways.Add(gw) - } - } - - previousGateways := c.networkGatewaysBySvc[svc.Hostname] - gatewaysChanged := !newGateways.Equals(previousGateways) - - if len(newGateways) > 0 { - c.networkGatewaysBySvc[svc.Hostname] = newGateways - } else { - delete(c.networkGatewaysBySvc, svc.Hostname) - } - - return gatewaysChanged -} - -// getGatewayDetails returns gateways without the address populated, only the network and (unmapped) port for a given service. -func (c *Controller) getGatewayDetails(svc *model.Service) []model.NetworkGateway { - // TODO should we start checking if svc's Ports contain the gateway port? - - // label based gateways - // TODO label based gateways could support being the gateway for multiple networks - if nw := svc.Attributes.Labels[label.TopologyNetwork.Name]; nw != "" { - if gwPortStr := svc.Attributes.Labels[label.NetworkingGatewayPort.Name]; gwPortStr != "" { - if gwPort, err := strconv.Atoi(gwPortStr); err == nil { - return []model.NetworkGateway{{Port: uint32(gwPort), Network: network.ID(nw)}} - } - log.Warnf("could not parse %q for %s on %s/%s; defaulting to %d", - gwPortStr, label.NetworkingGatewayPort.Name, svc.Attributes.Namespace, svc.Attributes.Name, DefaultNetworkGatewayPort) - } - return []model.NetworkGateway{{Port: DefaultNetworkGatewayPort, Network: network.ID(nw)}} - } - - // meshNetworks registryServiceName+fromRegistry - if gws, ok := c.registryServiceNameGateways[svc.Hostname]; ok { - out := append(make([]model.NetworkGateway, 0, len(gws)), gws...) - return out - } - - return nil -} - -// updateServiceNodePortAddresses updates ClusterExternalAddresses for Services of nodePort type -func (c *Controller) updateServiceNodePortAddresses(svcs ...*model.Service) bool { - // node event, update all nodePort gateway services - if len(svcs) == 0 { - svcs = c.getNodePortGatewayServices() - } - // no nodePort gateway service found, no update - if len(svcs) == 0 { - return false - } - for _, svc := range svcs { - c.RLock() - nodeSelector := c.nodeSelectorsForServices[svc.Hostname] - c.RUnlock() - // update external address - var nodeAddresses []string - for _, n := range c.nodeInfoMap { - if nodeSelector.SubsetOf(n.labels) { - nodeAddresses = append(nodeAddresses, n.address) - } - } - svc.Attributes.ClusterExternalAddresses.SetAddressesFor(c.Cluster(), nodeAddresses) - // update gateways that use the service - c.extractGatewaysFromService(svc) - } - return true -} - -// getNodePortServices returns nodePort type gateway service -func (c *Controller) getNodePortGatewayServices() []*model.Service { - c.RLock() - defer c.RUnlock() - out := make([]*model.Service, 0, len(c.nodeSelectorsForServices)) - for hostname := range c.nodeSelectorsForServices { - svc := c.servicesMap[hostname] - if svc != nil { - out = append(out, svc) - } - } - - return out -} diff --git a/pilot/pkg/serviceregistry/kube/controller/network_test.go b/pilot/pkg/serviceregistry/kube/controller/network_test.go deleted file mode 100644 index 214eda412..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/network_test.go +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "context" - "fmt" - "sync" - "testing" - "time" -) - -import ( - "go.uber.org/atomic" - "istio.io/api/label" - meshconfig "istio.io/api/mesh/v1alpha1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -func TestNetworkUpdateTriggers(t *testing.T) { - meshNetworks := mesh.NewFixedNetworksWatcher(nil) - c, _ := NewFakeControllerWithOptions(FakeControllerOptions{ClusterID: "Kubernetes", NetworksWatcher: meshNetworks, DomainSuffix: "cluster.local"}) - defer close(c.stop) - go func() { - c.Run(c.stop) - }() - - if len(c.NetworkGateways()) != 0 { - t.Fatal("did not expect any gateways yet") - } - - notified := atomic.NewBool(false) - var ( - gwMu sync.Mutex - gws []model.NetworkGateway - ) - setGws := func(v []model.NetworkGateway) { - gwMu.Lock() - defer gwMu.Unlock() - gws = v - } - getGws := func() []model.NetworkGateway { - gwMu.Lock() - defer gwMu.Unlock() - return gws - } - - c.AppendNetworkGatewayHandler(func() { - notified.Store(true) - setGws(c.NetworkGateways()) - }) - expectGateways := func(t *testing.T, expectedGws int) { - defer notified.Store(false) - // 1. wait for a notification - retry.UntilSuccessOrFail(t, func() error { - if !notified.Load() { - return fmt.Errorf("no gateway notify") - } - if n := len(getGws()); n != expectedGws { - return fmt.Errorf("expected %d gateways but got %d", expectedGws, n) - } - return nil - }, retry.Timeout(5*time.Second), retry.Delay(500*time.Millisecond)) - } - - t.Run("add meshnetworks", func(t *testing.T) { - addMeshNetworksFromRegistryGateway(t, c, meshNetworks) - expectGateways(t, 2) - }) - fmt.Println(c.NetworkGateways()) - t.Run("add labeled service", func(t *testing.T) { - addLabeledServiceGateway(t, c, "nw0") - expectGateways(t, 3) - }) - t.Run("update labeled service network", func(t *testing.T) { - addLabeledServiceGateway(t, c, "nw1") - expectGateways(t, 3) - }) - t.Run("remove labeled service", func(t *testing.T) { - removeLabeledServiceGateway(t, c) - expectGateways(t, 2) - }) - t.Run("remove meshnetworks", func(t *testing.T) { - meshNetworks.SetNetworks(nil) - expectGateways(t, 0) - }) -} - -func addLabeledServiceGateway(t *testing.T, c *FakeController, nw string) { - ctx := context.TODO() - - svc := &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{Name: "istio-labeled-gw", Namespace: "arbitrary-ns", Labels: map[string]string{ - label.TopologyNetwork.Name: nw, - }}, - Spec: corev1.ServiceSpec{ - Type: corev1.ServiceTypeLoadBalancer, - Ports: []corev1.ServicePort{{Port: 15443, Protocol: corev1.ProtocolTCP}}, - }, - Status: corev1.ServiceStatus{LoadBalancer: corev1.LoadBalancerStatus{Ingress: []corev1.LoadBalancerIngress{{ - IP: "2.3.4.6", - Ports: []corev1.PortStatus{{Port: 15443, Protocol: corev1.ProtocolTCP}}, - }}}}, - } - - if _, err := c.client.CoreV1().Services("arbitrary-ns").Get(ctx, "istio-labeled-gw", metav1.GetOptions{}); err == nil { - // update - if _, err := c.client.CoreV1().Services("arbitrary-ns").Update(context.TODO(), svc, metav1.UpdateOptions{}); err != nil { - t.Fatal(err) - } - } else if errors.IsNotFound(err) { - // create - if _, err := c.client.CoreV1().Services("arbitrary-ns").Create(context.TODO(), svc, metav1.CreateOptions{}); err != nil { - t.Fatal(err) - } - } else { - t.Fatal(err) - } -} - -func removeLabeledServiceGateway(t *testing.T, c *FakeController) { - err := c.client.CoreV1().Services("arbitrary-ns").Delete(context.TODO(), "istio-labeled-gw", metav1.DeleteOptions{}) - if err != nil { - t.Fatal(err) - } -} - -func addMeshNetworksFromRegistryGateway(t *testing.T, c *FakeController, watcher mesh.NetworksWatcher) { - _, err := c.client.CoreV1().Services("dubbo-system").Create(context.TODO(), &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{Name: "istio-meshnetworks-gw", Namespace: "dubbo-system"}, - Spec: corev1.ServiceSpec{ - Type: corev1.ServiceTypeLoadBalancer, - Ports: []corev1.ServicePort{{Port: 15443, Protocol: corev1.ProtocolTCP}}, - }, - Status: corev1.ServiceStatus{LoadBalancer: corev1.LoadBalancerStatus{Ingress: []corev1.LoadBalancerIngress{{ - IP: "1.2.3.4", - Ports: []corev1.PortStatus{{Port: 15443, Protocol: corev1.ProtocolTCP}}, - }}}}, - }, metav1.CreateOptions{}) - if err != nil { - t.Fatal(err) - } - watcher.SetNetworks(&meshconfig.MeshNetworks{Networks: map[string]*meshconfig.Network{ - "nw0": { - Endpoints: []*meshconfig.Network_NetworkEndpoints{{ - Ne: &meshconfig.Network_NetworkEndpoints_FromRegistry{FromRegistry: "Kubernetes"}, - }}, - Gateways: []*meshconfig.Network_IstioNetworkGateway{{ - Port: 15443, - Gw: &meshconfig.Network_IstioNetworkGateway_RegistryServiceName{RegistryServiceName: "istio-meshnetworks-gw.dubbo-system.svc.cluster.local"}, - }}, - }, - "nw1": { - Endpoints: []*meshconfig.Network_NetworkEndpoints{{ - Ne: &meshconfig.Network_NetworkEndpoints_FromRegistry{FromRegistry: "Kubernetes"}, - }}, - Gateways: []*meshconfig.Network_IstioNetworkGateway{{ - Port: 15443, - Gw: &meshconfig.Network_IstioNetworkGateway_RegistryServiceName{RegistryServiceName: "istio-meshnetworks-gw.dubbo-system.svc.cluster.local"}, - }}, - }, - }}) -} diff --git a/pilot/pkg/serviceregistry/kube/controller/pod.go b/pilot/pkg/serviceregistry/kube/controller/pod.go deleted file mode 100644 index 5f8e1d0e4..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/pod.go +++ /dev/null @@ -1,311 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "fmt" - "sync" -) - -import ( - v1 "k8s.io/api/core/v1" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube/controller/filter" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// PodCache is an eventually consistent pod cache -type PodCache struct { - informer filter.FilteredSharedIndexInformer - - sync.RWMutex - // podsByIP maintains stable pod IP to name key mapping - // this allows us to retrieve the latest status by pod IP. - // This should only contain RUNNING or PENDING pods with an allocated IP. - podsByIP map[string]string - // IPByPods is a reverse map of podsByIP. This exists to allow us to prune stale entries in the - // pod cache if a pod changes IP. - IPByPods map[string]string - - // needResync is map of IP to endpoint namespace/name. This is used to requeue endpoint - // events when pod event comes. This typically happens when pod is not available - // in podCache when endpoint event comes. - needResync map[string]sets.Set - queueEndpointEvent func(string) - - c *Controller -} - -func newPodCache(c *Controller, informer filter.FilteredSharedIndexInformer, queueEndpointEvent func(string)) *PodCache { - out := &PodCache{ - informer: informer, - c: c, - podsByIP: make(map[string]string), - IPByPods: make(map[string]string), - needResync: make(map[string]sets.Set), - queueEndpointEvent: queueEndpointEvent, - } - - return out -} - -// Copied from kubernetes/pkg/controller/endpoint/endpoints_controller.go -func shouldPodBeInEndpoints(pod *v1.Pod) bool { - switch pod.Spec.RestartPolicy { - case v1.RestartPolicyNever: - return pod.Status.Phase != v1.PodFailed && pod.Status.Phase != v1.PodSucceeded - case v1.RestartPolicyOnFailure: - return pod.Status.Phase != v1.PodSucceeded - default: - return true - } -} - -// IsPodReady is copied from kubernetes/pkg/api/v1/pod/utils.go -func IsPodReady(pod *v1.Pod) bool { - return IsPodReadyConditionTrue(pod.Status) -} - -// IsPodReadyConditionTrue returns true if a pod is ready; false otherwise. -func IsPodReadyConditionTrue(status v1.PodStatus) bool { - condition := GetPodReadyCondition(status) - return condition != nil && condition.Status == v1.ConditionTrue -} - -func GetPodReadyCondition(status v1.PodStatus) *v1.PodCondition { - _, condition := GetPodCondition(&status, v1.PodReady) - return condition -} - -func GetPodCondition(status *v1.PodStatus, conditionType v1.PodConditionType) (int, *v1.PodCondition) { - if status == nil { - return -1, nil - } - return GetPodConditionFromList(status.Conditions, conditionType) -} - -// GetPodConditionFromList extracts the provided condition from the given list of condition and -// returns the index of the condition and the condition. Returns -1 and nil if the condition is not present. -func GetPodConditionFromList(conditions []v1.PodCondition, conditionType v1.PodConditionType) (int, *v1.PodCondition) { - if conditions == nil { - return -1, nil - } - for i := range conditions { - if conditions[i].Type == conditionType { - return i, &conditions[i] - } - } - return -1, nil -} - -// onEvent updates the IP-based index (pc.podsByIP). -func (pc *PodCache) onEvent(curr interface{}, ev model.Event) error { - // When a pod is deleted obj could be an *v1.Pod or a DeletionFinalStateUnknown marker item. - pod, ok := curr.(*v1.Pod) - if !ok { - tombstone, ok := curr.(cache.DeletedFinalStateUnknown) - if !ok { - return fmt.Errorf("couldn't get object from tombstone %+v", curr) - } - pod, ok = tombstone.Obj.(*v1.Pod) - if !ok { - return fmt.Errorf("tombstone contained object that is not a pod %#v", curr) - } - } - - ip := pod.Status.PodIP - // PodIP will be empty when pod is just created, but before the IP is assigned - // via UpdateStatus. - if len(ip) == 0 { - return nil - } - - key := kube.KeyFunc(pod.Name, pod.Namespace) - switch ev { - case model.EventAdd: - // can happen when istiod just starts - if pod.DeletionTimestamp != nil || !IsPodReady(pod) { - return nil - } else if shouldPodBeInEndpoints(pod) { - pc.update(ip, key) - } else { - return nil - } - case model.EventUpdate: - if pod.DeletionTimestamp != nil || !IsPodReady(pod) { - // delete only if this pod was in the cache - pc.deleteIP(ip, key) - ev = model.EventDelete - } else if shouldPodBeInEndpoints(pod) { - pc.update(ip, key) - } else { - return nil - } - case model.EventDelete: - // delete only if this pod was in the cache, - // in most case it has already been deleted in `UPDATE` with `DeletionTimestamp` set. - if !pc.deleteIP(ip, key) { - return nil - } - } - pc.notifyWorkloadHandlers(pod, ev) - return nil -} - -// notifyWorkloadHandlers fire workloadInstance handlers for pod -func (pc *PodCache) notifyWorkloadHandlers(pod *v1.Pod, ev model.Event) { - // fire instance handles for workload - ep := NewEndpointBuilder(pc.c, pod).buildIstioEndpoint(pod.Status.PodIP, 0, "", model.AlwaysDiscoverable) - workloadInstance := &model.WorkloadInstance{ - Name: pod.Name, - Namespace: pod.Namespace, - Kind: model.PodKind, - Endpoint: ep, - PortMap: getPortMap(pod), - } - pc.c.handlers.NotifyWorkloadHandlers(workloadInstance, ev) -} - -func getPortMap(pod *v1.Pod) map[string]uint32 { - pmap := map[string]uint32{} - for _, c := range pod.Spec.Containers { - for _, port := range c.Ports { - if port.Name == "" || port.Protocol != v1.ProtocolTCP { - continue - } - // First port wins, per Kubernetes (https://github.com/kubernetes/kubernetes/issues/54213) - if _, f := pmap[port.Name]; !f { - pmap[port.Name] = uint32(port.ContainerPort) - } - } - } - return pmap -} - -// deleteIP returns true if the pod and ip are really deleted. -func (pc *PodCache) deleteIP(ip string, podKey string) bool { - pc.Lock() - defer pc.Unlock() - if pc.podsByIP[ip] == podKey { - delete(pc.podsByIP, ip) - delete(pc.IPByPods, podKey) - return true - } - return false -} - -func (pc *PodCache) update(ip, key string) { - pc.Lock() - // if the pod has been cached, return - if key == pc.podsByIP[ip] { - pc.Unlock() - return - } - if current, f := pc.IPByPods[key]; f { - // The pod already exists, but with another IP Address. We need to clean up that - delete(pc.podsByIP, current) - } - pc.podsByIP[ip] = key - pc.IPByPods[key] = ip - - if endpointsToUpdate, f := pc.needResync[ip]; f { - delete(pc.needResync, ip) - for epKey := range endpointsToUpdate { - pc.queueEndpointEvent(epKey) - } - endpointsPendingPodUpdate.Record(float64(len(pc.needResync))) - } - pc.Unlock() - - pc.proxyUpdates(ip) -} - -// queueEndpointEventOnPodArrival registers this endpoint and queues endpoint event -// when the corresponding pod arrives. -func (pc *PodCache) queueEndpointEventOnPodArrival(key, ip string) { - pc.Lock() - defer pc.Unlock() - if _, f := pc.needResync[ip]; !f { - pc.needResync[ip] = sets.New(key) - } else { - pc.needResync[ip].Insert(key) - } - endpointsPendingPodUpdate.Record(float64(len(pc.needResync))) -} - -// endpointDeleted cleans up endpoint from resync endpoint list. -func (pc *PodCache) endpointDeleted(key string, ip string) { - pc.Lock() - defer pc.Unlock() - delete(pc.needResync[ip], key) - if len(pc.needResync[ip]) == 0 { - delete(pc.needResync, ip) - } - endpointsPendingPodUpdate.Record(float64(len(pc.needResync))) -} - -func (pc *PodCache) proxyUpdates(ip string) { - if pc.c != nil && pc.c.opts.XDSUpdater != nil { - pc.c.opts.XDSUpdater.ProxyUpdate(pc.c.Cluster(), ip) - } -} - -func (pc *PodCache) getPodKey(addr string) (string, bool) { - pc.RLock() - defer pc.RUnlock() - key, exists := pc.podsByIP[addr] - return key, exists -} - -// getPodByIp returns the pod or nil if pod not found or an error occurred -func (pc *PodCache) getPodByIP(addr string) *v1.Pod { - key, exists := pc.getPodKey(addr) - if !exists { - return nil - } - return pc.getPodByKey(key) -} - -// getPodByKey returns the pod by key formatted `ns/name` -func (pc *PodCache) getPodByKey(key string) *v1.Pod { - item, _, _ := pc.informer.GetIndexer().GetByKey(key) - if item != nil { - return item.(*v1.Pod) - } - return nil -} - -// getPodByKey returns the pod of the proxy -func (pc *PodCache) getPodByProxy(proxy *model.Proxy) *v1.Pod { - var pod *v1.Pod - key := podKeyByProxy(proxy) - if key != "" { - pod = pc.getPodByKey(key) - if pod != nil { - return pod - } - } - - // only need to fetch the corresponding pod through the first IP, although there are multiple IP scenarios, - // because multiple ips belong to the same pod - proxyIP := proxy.IPAddresses[0] - // just in case the proxy ID is bad formatted - return pc.getPodByIP(proxyIP) -} diff --git a/pilot/pkg/serviceregistry/kube/controller/pod_test.go b/pilot/pkg/serviceregistry/kube/controller/pod_test.go deleted file mode 100644 index 7212d44f4..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/pod_test.go +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "context" - "reflect" - "testing" - "time" -) - -import ( - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -// Prepare k8s. This can be used in multiple tests, to -// avoid duplicating creation, which can be tricky. It can be used with the fake or -// standalone apiserver. -func initTestEnv(t *testing.T, ki kubernetes.Interface, fx *FakeXdsUpdater) { - cleanup(ki) - for _, n := range []string{"nsa", "nsb"} { - _, err := ki.CoreV1().Namespaces().Create(context.TODO(), &v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: n, - Labels: map[string]string{ - "istio-injection": "enabled", - }, - }, - }, metav1.CreateOptions{}) - if err != nil { - t.Fatalf("failed creating test namespace: %v", err) - } - - // K8S 1.10 also checks if service account exists - _, err = ki.CoreV1().ServiceAccounts(n).Create(context.TODO(), &v1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: "default", - Annotations: map[string]string{ - "kubernetes.io/enforce-mountable-secrets": "false", - }, - }, - Secrets: []v1.ObjectReference{ - { - Name: "default-token-2", - UID: "1", - }, - }, - }, metav1.CreateOptions{}) - if err != nil { - t.Fatalf("failed creating test service account: %v", err) - } - - _, err = ki.CoreV1().Secrets(n).Create(context.TODO(), &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "default-token-2", - Annotations: map[string]string{ - "kubernetes.io/service-account.name": "default", - "kubernetes.io/service-account.uid": "1", - }, - }, - Type: v1.SecretTypeServiceAccountToken, - Data: map[string][]byte{ - "token": []byte("1"), - }, - }, metav1.CreateOptions{}) - if err != nil { - t.Fatalf("failed creating test secret: %v", err) - } - } - fx.Clear() -} - -func cleanup(ki kubernetes.Interface) { - for _, n := range []string{"nsa", "nsb"} { - n := n - pods, err := ki.CoreV1().Pods(n).List(context.TODO(), metav1.ListOptions{}) - if err == nil { - // Make sure the pods don't exist - for _, pod := range pods.Items { - _ = ki.CoreV1().Pods(pod.Namespace).Delete(context.TODO(), pod.Name, metav1.DeleteOptions{}) - } - } - } -} - -func TestPodCache(t *testing.T) { - t.Run("fakeApiserver", func(t *testing.T) { - t.Parallel() - testPodCache(t) - }) -} - -func TestHostNetworkPod(t *testing.T) { - c, fx := NewFakeControllerWithOptions(FakeControllerOptions{Mode: EndpointsOnly}) - go c.Run(c.stop) - cache.WaitForCacheSync(c.stop, c.HasSynced) - defer c.Stop() - initTestEnv(t, c.client, fx) - createPod := func(ip, name string) { - addPods(t, c, fx, generatePod(ip, name, "ns", "1", "", map[string]string{}, map[string]string{})) - } - - createPod("128.0.0.1", "pod1") - if p, f := c.pods.getPodKey("128.0.0.1"); !f || p != "ns/pod1" { - t.Fatalf("unexpected pod: %v", p) - } - - createPod("128.0.0.1", "pod2") - if p, f := c.pods.getPodKey("128.0.0.1"); !f || p != "ns/pod2" { - t.Fatalf("unexpected pod: %v", p) - } - - p := c.pods.getPodByKey("ns/pod1") - if p == nil || p.Name != "pod1" { - t.Fatalf("unexpected pod: %v", p) - } -} - -// Regression test for https://github.com/istio/istio/issues/20676 -func TestIPReuse(t *testing.T) { - c, fx := NewFakeControllerWithOptions(FakeControllerOptions{Mode: EndpointsOnly}) - go c.Run(c.stop) - cache.WaitForCacheSync(c.stop, c.HasSynced) - defer c.Stop() - initTestEnv(t, c.client, fx) - - createPod := func(ip, name string) { - addPods(t, c, fx, generatePod(ip, name, "ns", "1", "", map[string]string{}, map[string]string{})) - } - - createPod("128.0.0.1", "pod") - if p, f := c.pods.getPodKey("128.0.0.1"); !f || p != "ns/pod" { - t.Fatalf("unexpected pod: %v", p) - } - - // Change the pod IP. This can happen if the pod moves to another node, for example. - createPod("128.0.0.2", "pod") - if p, f := c.pods.getPodKey("128.0.0.2"); !f || p != "ns/pod" { - t.Fatalf("unexpected pod: %v", p) - } - if p, f := c.pods.getPodKey("128.0.0.1"); f { - t.Fatalf("expected no pod, got pod: %v", p) - } - - // A new pod is created with the old IP. We should get new-pod, not pod - createPod("128.0.0.1", "new-pod") - if p, f := c.pods.getPodKey("128.0.0.1"); !f || p != "ns/new-pod" { - t.Fatalf("unexpected pod: %v", p) - } - - // A new pod is created with the same IP. In theory this should never happen, but maybe we miss an update somehow. - createPod("128.0.0.1", "another-pod") - if p, f := c.pods.getPodKey("128.0.0.1"); !f || p != "ns/another-pod" { - t.Fatalf("unexpected pod: %v", p) - } - - err := c.client.CoreV1().Pods("ns").Delete(context.TODO(), "another-pod", metav1.DeleteOptions{}) - if err != nil { - t.Fatalf("Cannot delete pod: %v", err) - } - if err := wait.Poll(10*time.Millisecond, 5*time.Second, func() (bool, error) { - if _, ok := c.pods.getPodKey("128.0.0.1"); ok { - return false, nil - } - return true, nil - }); err != nil { - t.Fatalf("delete failed: %v", err) - } -} - -func waitForPod(c *FakeController, ip string) error { - return wait.Poll(5*time.Millisecond, 1*time.Second, func() (bool, error) { - c.pods.RLock() - defer c.pods.RUnlock() - if _, ok := c.pods.podsByIP[ip]; ok { - return true, nil - } - return false, nil - }) -} - -func waitForNode(c *FakeController, name string) error { - return retry.UntilSuccess(func() error { - _, err := c.nodeLister.Get(name) - return err - }, retry.Timeout(time.Second*1), retry.Delay(time.Millisecond*5)) -} - -func testPodCache(t *testing.T) { - c, fx := NewFakeControllerWithOptions(FakeControllerOptions{ - Mode: EndpointsOnly, - WatchedNamespaces: "nsa,nsb", - }) - go c.Run(c.stop) - cache.WaitForCacheSync(c.stop, c.HasSynced) - defer c.Stop() - - initTestEnv(t, c.client, fx) - - // Namespace must be lowercase (nsA doesn't work) - pods := []*v1.Pod{ - generatePod("128.0.0.1", "cpod1", "nsa", "", "", map[string]string{"app": "test-app"}, map[string]string{}), - generatePod("128.0.0.2", "cpod2", "nsa", "", "", map[string]string{"app": "prod-app-1"}, map[string]string{}), - generatePod("128.0.0.3", "cpod3", "nsb", "", "", map[string]string{"app": "prod-app-2"}, map[string]string{}), - } - - addPods(t, c, fx, pods...) - - // Verify podCache - wantLabels := map[string]labels.Instance{ - "128.0.0.1": {"app": "test-app"}, - "128.0.0.2": {"app": "prod-app-1"}, - "128.0.0.3": {"app": "prod-app-2"}, - } - for addr, wantTag := range wantLabels { - pod := c.pods.getPodByIP(addr) - if pod == nil { - t.Error("Not found ", addr) - continue - } - if !reflect.DeepEqual(wantTag, labels.Instance(pod.Labels)) { - t.Errorf("Expected %v got %v", wantTag, labels.Instance(pod.Labels)) - } - } - - // This pod exists, but should not be in the cache because it is in a - // namespace not watched by the controller. - pod := c.pods.getPodByIP("128.0.0.4") - if pod != nil { - t.Error("Expected not found but was found") - } - - // This pod should not be in the cache because it never existed. - pod = c.pods.getPodByIP("128.0.0.128") - if pod != nil { - t.Error("Expected not found but was found") - } -} - -// Checks that events from the watcher create the proper internal structures -func TestPodCacheEvents(t *testing.T) { - t.Parallel() - c, _ := NewFakeControllerWithOptions(FakeControllerOptions{Mode: EndpointsOnly}) - go c.Run(c.stop) - cache.WaitForCacheSync(c.stop, c.HasSynced) - defer c.Stop() - - ns := "default" - podCache := c.pods - - f := podCache.onEvent - - ip := "172.0.3.35" - pod1 := metav1.ObjectMeta{Name: "pod1", Namespace: ns} - if err := f(&v1.Pod{ObjectMeta: pod1}, model.EventAdd); err != nil { - t.Error(err) - } - - podCondition := []v1.PodCondition{{Type: v1.PodReady, Status: v1.ConditionTrue}} - - if err := f(&v1.Pod{ObjectMeta: pod1, Status: v1.PodStatus{Conditions: podCondition, PodIP: ip, Phase: v1.PodPending}}, model.EventUpdate); err != nil { - t.Error(err) - } - - if pod, exists := podCache.getPodKey(ip); !exists || pod != "default/pod1" { - t.Errorf("getPodKey => got %s, pod1 not found or incorrect", pod) - } - - pod2 := metav1.ObjectMeta{Name: "pod2", Namespace: ns} - if err := f( - &v1.Pod{ObjectMeta: pod1, Status: v1.PodStatus{Conditions: podCondition, PodIP: ip, Phase: v1.PodFailed}}, model.EventUpdate); err != nil { - t.Error(err) - } - if err := f(&v1.Pod{ObjectMeta: pod2, Status: v1.PodStatus{Conditions: podCondition, PodIP: ip, Phase: v1.PodRunning}}, model.EventAdd); err != nil { - t.Error(err) - } - - if pod, exists := podCache.getPodKey(ip); !exists || pod != "default/pod2" { - t.Errorf("getPodKey => got %s, pod2 not found or incorrect", pod) - } - - if err := f(&v1.Pod{ObjectMeta: pod1, Status: v1.PodStatus{PodIP: ip, Phase: v1.PodFailed}}, model.EventDelete); err != nil { - t.Error(err) - } - - if pod, exists := podCache.getPodKey(ip); !exists || pod != "default/pod2" { - t.Errorf("getPodKey => got %s, pod2 not found or incorrect", pod) - } - - if err := f(&v1.Pod{ObjectMeta: pod2, Spec: v1.PodSpec{ - RestartPolicy: v1.RestartPolicyOnFailure, - }, Status: v1.PodStatus{Conditions: podCondition, PodIP: ip, Phase: v1.PodFailed}}, model.EventUpdate); err != nil { - t.Error(err) - } - - if pod, exists := podCache.getPodKey(ip); !exists || pod != "default/pod2" { - t.Errorf("getPodKey => got %s, pod2 not found or incorrect", pod) - } - - if err := f(&v1.Pod{ObjectMeta: pod2, Status: v1.PodStatus{Conditions: podCondition, PodIP: ip, Phase: v1.PodFailed}}, model.EventDelete); err != nil { - t.Error(err) - } - - if pod, exists := podCache.getPodKey(ip); exists { - t.Errorf("getPodKey => got %s, want none", pod) - } -} diff --git a/pilot/pkg/serviceregistry/kube/controller/serviceexportcache.go b/pilot/pkg/serviceregistry/kube/controller/serviceexportcache.go deleted file mode 100644 index f9a7529aa..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/serviceexportcache.go +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "fmt" - "strings" -) - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - klabels "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - kubesr "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/kube/mcs" -) - -type exportedService struct { - namespacedName types.NamespacedName - discoverability map[host.Name]string -} - -// serviceExportCache reads Kubernetes Multi-Cluster Services (MCS) ServiceExport resources in the -// cluster and generates discoverability policies for the endpoints. -type serviceExportCache interface { - // EndpointDiscoverabilityPolicy returns the policy for Service endpoints residing within the current cluster. - EndpointDiscoverabilityPolicy(svc *model.Service) model.EndpointDiscoverabilityPolicy - - // ExportedServices returns the list of services that are exported in this cluster. Used for debugging. - ExportedServices() []exportedService - - // HasSynced indicates whether the kube createClient has synced for the watched resources. - HasSynced() bool -} - -// newServiceExportCache creates a new serviceExportCache that observes the given cluster. -func newServiceExportCache(c *Controller) serviceExportCache { - if features.EnableMCSServiceDiscovery { - dInformer := c.client.DynamicInformer().ForResource(mcs.ServiceExportGVR) - ec := &serviceExportCacheImpl{ - Controller: c, - informer: dInformer.Informer(), - lister: dInformer.Lister(), - } - - // Set the discoverability policy for the clusterset.local host. - ec.clusterSetLocalPolicySelector = func(svc *model.Service) (policy model.EndpointDiscoverabilityPolicy) { - // If the service is exported in this cluster, allow the endpoints in this cluster to be discoverable - // anywhere in the mesh. - if ec.isExported(namespacedNameForService(svc)) { - return model.AlwaysDiscoverable - } - - // Otherwise, endpoints are only discoverable from within the same cluster. - return model.DiscoverableFromSameCluster - } - - // Set the discoverability policy for the cluster.local host. - if features.EnableMCSClusterLocal { - // MCS cluster.local mode is enabled. Allow endpoints for the cluster.local host to be - // discoverable only from within the same cluster. - ec.clusterLocalPolicySelector = func(svc *model.Service) (policy model.EndpointDiscoverabilityPolicy) { - return model.DiscoverableFromSameCluster - } - } else { - // MCS cluster.local mode is not enabled, so requests to the cluster.local host are not confined - // to the same cluster. Use the same discoverability policy as for clusterset.local. - ec.clusterLocalPolicySelector = ec.clusterSetLocalPolicySelector - } - - // Register callbacks for events. - c.registerHandlers(ec.informer, "ServiceExports", ec.onServiceExportEvent, nil) - return ec - } - - // MCS Service discovery is disabled. Use a placeholder cache. - return disabledServiceExportCache{} -} - -type discoverabilityPolicySelector func(*model.Service) model.EndpointDiscoverabilityPolicy - -// serviceExportCache reads ServiceExport resources for a single cluster. -type serviceExportCacheImpl struct { - *Controller - - informer cache.SharedIndexInformer - lister cache.GenericLister - - // clusterLocalPolicySelector selects an appropriate EndpointDiscoverabilityPolicy for the cluster.local host. - clusterLocalPolicySelector discoverabilityPolicySelector - - // clusterSetLocalPolicySelector selects an appropriate EndpointDiscoverabilityPolicy for the clusterset.local host. - clusterSetLocalPolicySelector discoverabilityPolicySelector -} - -func (ec *serviceExportCacheImpl) onServiceExportEvent(obj interface{}, event model.Event) error { - se, ok := obj.(*unstructured.Unstructured) - if !ok { - tombstone, ok := obj.(cache.DeletedFinalStateUnknown) - if !ok { - return fmt.Errorf("couldn't get object from tombstone %#v", obj) - } - se, ok = tombstone.Obj.(*unstructured.Unstructured) - if !ok { - return fmt.Errorf("tombstone contained object that is not a ServiceExport %#v", obj) - } - } - - switch event { - case model.EventAdd, model.EventDelete: - ec.updateXDS(se) - default: - // Don't care about updates. - } - return nil -} - -func (ec *serviceExportCacheImpl) updateXDS(se metav1.Object) { - for _, svc := range ec.servicesForNamespacedName(kubesr.NamespacedNameForK8sObject(se)) { - // Re-build the endpoints for this service with a new discoverability policy. - // Also update any internal caching. - endpoints := ec.buildEndpointsForService(svc, true) - shard := model.ShardKeyFromRegistry(ec) - ec.opts.XDSUpdater.EDSUpdate(shard, svc.Hostname.String(), se.GetNamespace(), endpoints) - } -} - -func (ec *serviceExportCacheImpl) EndpointDiscoverabilityPolicy(svc *model.Service) model.EndpointDiscoverabilityPolicy { - if svc == nil { - // Default policy when the service doesn't exist. - return model.DiscoverableFromSameCluster - } - - if strings.HasSuffix(svc.Hostname.String(), "."+constants.DefaultClusterSetLocalDomain) { - return ec.clusterSetLocalPolicySelector(svc) - } - - return ec.clusterLocalPolicySelector(svc) -} - -func (ec *serviceExportCacheImpl) isExported(name types.NamespacedName) bool { - _, err := ec.lister.ByNamespace(name.Namespace).Get(name.Name) - return err == nil -} - -func (ec *serviceExportCacheImpl) ExportedServices() []exportedService { - // List all exports in this cluster. - exports, err := ec.lister.List(klabels.Everything()) - if err != nil { - return make([]exportedService, 0) - } - - ec.RLock() - - out := make([]exportedService, 0, len(exports)) - for _, export := range exports { - uExport := export.(*unstructured.Unstructured) - es := exportedService{ - namespacedName: kubesr.NamespacedNameForK8sObject(uExport), - discoverability: make(map[host.Name]string), - } - - // Generate the map of all hosts for this service to their discoverability policies. - clusterLocalHost := kubesr.ServiceHostname(uExport.GetName(), uExport.GetNamespace(), ec.opts.DomainSuffix) - clusterSetLocalHost := serviceClusterSetLocalHostname(es.namespacedName) - for _, hostName := range []host.Name{clusterLocalHost, clusterSetLocalHost} { - if svc := ec.servicesMap[hostName]; svc != nil { - es.discoverability[hostName] = ec.EndpointDiscoverabilityPolicy(svc).String() - } - } - - out = append(out, es) - } - - ec.RUnlock() - - return out -} - -func (ec *serviceExportCacheImpl) HasSynced() bool { - return ec.informer.HasSynced() -} - -type disabledServiceExportCache struct{} - -var _ serviceExportCache = disabledServiceExportCache{} - -func (c disabledServiceExportCache) EndpointDiscoverabilityPolicy(*model.Service) model.EndpointDiscoverabilityPolicy { - return model.AlwaysDiscoverable -} - -func (c disabledServiceExportCache) HasSynced() bool { - return true -} - -func (c disabledServiceExportCache) ExportedServices() []exportedService { - // MCS is disabled - returning `nil`, which is semantically different here than an empty list. - return nil -} diff --git a/pilot/pkg/serviceregistry/kube/controller/serviceexportcache_test.go b/pilot/pkg/serviceregistry/kube/controller/serviceexportcache_test.go deleted file mode 100644 index 587892d6b..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/serviceexportcache_test.go +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "context" - "errors" - "fmt" - "testing" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - "istio.io/api/label" - v12 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/tools/cache" - mcsapi "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/kube/mcs" - istiotest "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -const ( - serviceExportName = "test-svc" - serviceExportNamespace = "test-ns" - serviceExportPodIP = "128.0.0.2" - testCluster = "test-cluster" -) - -var serviceExportNamespacedName = types.NamespacedName{ - Namespace: serviceExportNamespace, - Name: serviceExportName, -} - -type ClusterLocalMode string - -func (m ClusterLocalMode) String() string { - return string(m) -} - -const ( - alwaysClusterLocal ClusterLocalMode = "always cluster local" - meshWide ClusterLocalMode = "mesh wide" -) - -var ClusterLocalModes = []ClusterLocalMode{alwaysClusterLocal, meshWide} - -func TestServiceNotExported(t *testing.T) { - for _, clusterLocalMode := range ClusterLocalModes { - t.Run(clusterLocalMode.String(), func(t *testing.T) { - for _, endpointMode := range EndpointModes { - t.Run(endpointMode.String(), func(t *testing.T) { - // Create and run the controller. - ec := newTestServiceExportCache(t, clusterLocalMode, endpointMode) - - // Check that the endpoint is cluster-local - ec.checkServiceInstancesOrFail(t, false) - }) - } - }) - } -} - -func TestServiceExported(t *testing.T) { - for _, clusterLocalMode := range ClusterLocalModes { - t.Run(clusterLocalMode.String(), func(t *testing.T) { - for _, endpointMode := range EndpointModes { - t.Run(endpointMode.String(), func(t *testing.T) { - // Create and run the controller. - ec := newTestServiceExportCache(t, clusterLocalMode, endpointMode) - - // Export the service. - ec.export(t) - - // Check that the endpoint is mesh-wide - ec.checkServiceInstancesOrFail(t, true) - }) - } - }) - } -} - -func TestServiceUnexported(t *testing.T) { - for _, clusterLocalMode := range ClusterLocalModes { - t.Run(clusterLocalMode.String(), func(t *testing.T) { - for _, endpointMode := range EndpointModes { - t.Run(endpointMode.String(), func(t *testing.T) { - // Create and run the controller. - ec := newTestServiceExportCache(t, clusterLocalMode, endpointMode) - - // Export the service and then unexport it immediately. - ec.export(t) - ec.unExport(t) - - // Check that the endpoint is cluster-local - ec.checkServiceInstancesOrFail(t, false) - }) - } - }) - } -} - -func newServiceExport() *unstructured.Unstructured { - se := &mcsapi.ServiceExport{ - TypeMeta: v12.TypeMeta{ - Kind: "ServiceExport", - APIVersion: mcs.MCSSchemeGroupVersion.String(), - }, - ObjectMeta: v12.ObjectMeta{ - Name: serviceExportName, - Namespace: serviceExportNamespace, - }, - } - return toUnstructured(se) -} - -func newTestServiceExportCache(t *testing.T, clusterLocalMode ClusterLocalMode, endpointMode EndpointMode) (ec *serviceExportCacheImpl) { - t.Helper() - - stopCh := make(chan struct{}) - istiotest.SetBoolForTest(t, &features.EnableMCSServiceDiscovery, true) - istiotest.SetBoolForTest(t, &features.EnableMCSClusterLocal, clusterLocalMode == alwaysClusterLocal) - t.Cleanup(func() { - close(stopCh) - }) - - c, _ := NewFakeControllerWithOptions(FakeControllerOptions{ - Stop: stopCh, - ClusterID: testCluster, - Mode: endpointMode, - }) - go c.Run(c.stop) - cache.WaitForCacheSync(c.stop, c.HasSynced) - - // Create the test service and endpoints. - createService(c, serviceExportName, serviceExportNamespace, map[string]string{}, - []int32{8080}, map[string]string{"app": "prod-app"}, t) - createEndpoints(t, c, serviceExportName, serviceExportNamespace, []string{"tcp-port"}, []string{serviceExportPodIP}, nil, nil) - - ec = c.exports.(*serviceExportCacheImpl) - - // Wait for the resources to be processed by the controller. - retry.UntilOrFail(t, func() bool { - if svc := ec.GetService(ec.serviceHostname()); svc == nil { - return false - } - inst := ec.getProxyServiceInstances() - return len(inst) == 1 && inst[0].Service != nil && inst[0].Endpoint != nil - }, serviceExportTimeout) - return -} - -func (ec *serviceExportCacheImpl) serviceHostname() host.Name { - return kube.ServiceHostname(serviceExportName, serviceExportNamespace, ec.opts.DomainSuffix) -} - -func (ec *serviceExportCacheImpl) export(t *testing.T) { - t.Helper() - - _, err := ec.client.Dynamic().Resource(mcs.ServiceExportGVR).Namespace(serviceExportNamespace).Create(context.TODO(), - newServiceExport(), - v12.CreateOptions{}) - if err != nil { - t.Fatal(err) - } - - // Wait for the export to be processed by the controller. - retry.UntilOrFail(t, func() bool { - return ec.isExported(serviceExportNamespacedName) - }, serviceExportTimeout) - - // Wait for the XDS event. - ec.waitForXDS(t, true) -} - -func (ec *serviceExportCacheImpl) unExport(t *testing.T) { - t.Helper() - - _ = ec.client.Dynamic().Resource(mcs.ServiceExportGVR).Namespace(serviceExportNamespace).Delete( - context.TODO(), - serviceExportName, - v12.DeleteOptions{}) - - // Wait for the delete to be processed by the controller. - retry.UntilOrFail(t, func() bool { - return !ec.isExported(serviceExportNamespacedName) - }, serviceExportTimeout) - - // Wait for the XDS event. - ec.waitForXDS(t, false) -} - -func (ec *serviceExportCacheImpl) waitForXDS(t *testing.T, exported bool) { - t.Helper() - retry.UntilSuccessOrFail(t, func() error { - event := ec.opts.XDSUpdater.(*FakeXdsUpdater).Wait("eds") - if event == nil { - return errors.New("failed waiting for XDS event") - } - if len(event.Endpoints) != 1 { - return fmt.Errorf("waitForXDS failed: expected 1 endpoint, found %d", len(event.Endpoints)) - } - - hostName := host.Name(event.ID) - svc := ec.GetService(hostName) - if svc == nil { - return fmt.Errorf("unable to find service for host %s", hostName) - } - si := &model.ServiceInstance{ - Service: svc, - Endpoint: event.Endpoints[0], - } - return ec.checkServiceInstance(exported, si) - }, serviceExportTimeout) -} - -func (ec *serviceExportCacheImpl) getProxyServiceInstances() []*model.ServiceInstance { - return ec.GetProxyServiceInstances(&model.Proxy{ - Type: model.SidecarProxy, - IPAddresses: []string{serviceExportPodIP}, - Locality: &core.Locality{Region: "r", Zone: "z"}, - ConfigNamespace: serviceExportNamespace, - Metadata: &model.NodeMetadata{ - ServiceAccount: "account", - ClusterID: ec.Cluster(), - Labels: map[string]string{ - "app": "prod-app", - label.SecurityTlsMode.Name: "mutual", - }, - }, - }) -} - -func (ec *serviceExportCacheImpl) checkServiceInstancesOrFail(t *testing.T, exported bool) { - t.Helper() - if err := ec.checkServiceInstances(exported); err != nil { - t.Fatal(err) - } -} - -func (ec *serviceExportCacheImpl) checkServiceInstances(exported bool) error { - sis := ec.getProxyServiceInstances() - if len(sis) != 1 { - return fmt.Errorf("expected 1 ServiceInstance, found %d", len(sis)) - } - return ec.checkServiceInstance(exported, sis[0]) -} - -func (ec *serviceExportCacheImpl) checkServiceInstance(exported bool, si *model.ServiceInstance) error { - ep := si.Endpoint - - // Should always be discoverable from the same cluster. - if err := ec.checkDiscoverableFromSameCluster(ep); err != nil { - return err - } - - if exported && !features.EnableMCSClusterLocal { - return ec.checkDiscoverableFromDifferentCluster(ep) - } - - return ec.checkNotDiscoverableFromDifferentCluster(ep) -} - -func (ec *serviceExportCacheImpl) checkDiscoverableFromSameCluster(ep *model.IstioEndpoint) error { - if !ec.isDiscoverableFromSameCluster(ep) { - return fmt.Errorf("endpoint was not discoverable from the same cluster") - } - return nil -} - -func (ec *serviceExportCacheImpl) checkDiscoverableFromDifferentCluster(ep *model.IstioEndpoint) error { - if !ec.isDiscoverableFromDifferentCluster(ep) { - return fmt.Errorf("endpoint was not discoverable from a different cluster") - } - return nil -} - -func (ec *serviceExportCacheImpl) checkNotDiscoverableFromDifferentCluster(ep *model.IstioEndpoint) error { - if ec.isDiscoverableFromDifferentCluster(ep) { - return fmt.Errorf("endpoint was discoverable from a different cluster") - } - return nil -} - -func (ec *serviceExportCacheImpl) isDiscoverableFromSameCluster(ep *model.IstioEndpoint) bool { - return ep.IsDiscoverableFromProxy(&model.Proxy{ - Metadata: &model.NodeMetadata{ - ClusterID: ec.Cluster(), - }, - }) -} - -func (ec *serviceExportCacheImpl) isDiscoverableFromDifferentCluster(ep *model.IstioEndpoint) bool { - return ep.IsDiscoverableFromProxy(&model.Proxy{ - Metadata: &model.NodeMetadata{ - ClusterID: "some-other-cluster", - }, - }) -} diff --git a/pilot/pkg/serviceregistry/kube/controller/serviceimportcache.go b/pilot/pkg/serviceregistry/kube/controller/serviceimportcache.go deleted file mode 100644 index 03b165353..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/serviceimportcache.go +++ /dev/null @@ -1,317 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "fmt" - "net" - "sort" - "strings" -) - -import ( - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - klabels "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/kube/mcs" -) - -const ( - mcsDomainSuffix = "." + constants.DefaultClusterSetLocalDomain -) - -type importedService struct { - namespacedName types.NamespacedName - clusterSetVIP string -} - -// serviceImportCache reads and processes Kubernetes Multi-Cluster Services (MCS) ServiceImport -// resources. -// -// An MCS controller is responsible for reading ServiceExport resources in one cluster and generating -// ServiceImport in all clusters of the ClusterSet (i.e. mesh). While the serviceExportCache reads -// ServiceExport to control the discoverability policy for individual endpoints, this controller -// reads ServiceImport in the cluster in order to extract the ClusterSet VIP and generate a -// synthetic service for the MCS host (i.e. clusterset.local). The aggregate.Controller will then -// merge together the MCS services from all the clusters, filling out the full map of Cluster IPs. -// -// The synthetic MCS service is a copy of the real k8s Service (e.g. cluster.local) with the same -// namespaced name, but with the hostname and VIPs changed to the appropriate ClusterSet values. -// The real k8s Service can live anywhere in the mesh and does not have to reside in the same -// cluster as the ServiceImport. -type serviceImportCache interface { - HasSynced() bool - ImportedServices() []importedService -} - -// newServiceImportCache creates a new cache of ServiceImport resources in the cluster. -func newServiceImportCache(c *Controller) serviceImportCache { - if features.EnableMCSHost { - dInformer := c.client.DynamicInformer().ForResource(mcs.ServiceImportGVR) - sic := &serviceImportCacheImpl{ - Controller: c, - informer: dInformer.Informer(), - lister: dInformer.Lister(), - } - - // Register callbacks for Service events anywhere in the mesh. - c.opts.MeshServiceController.AppendServiceHandlerForCluster(c.Cluster(), sic.onServiceEvent) - - // Register callbacks for ServiceImport events in this cluster only. - c.registerHandlers(sic.informer, "ServiceImports", sic.onServiceImportEvent, nil) - return sic - } - - // MCS Service discovery is disabled. Use a placeholder cache. - return disabledServiceImportCache{} -} - -// serviceImportCacheImpl reads ServiceImport resources for a single cluster. -type serviceImportCacheImpl struct { - *Controller - informer cache.SharedIndexInformer - lister cache.GenericLister -} - -// onServiceEvent is called when the controller receives an event for the kube Service (i.e. cluster.local). -// When this happens, we need to update the state of the associated synthetic MCS service. -func (ic *serviceImportCacheImpl) onServiceEvent(svc *model.Service, event model.Event) { - if strings.HasSuffix(svc.Hostname.String(), mcsDomainSuffix) { - // Ignore events for MCS services that were triggered by this controller. - return - } - - // This method is called concurrently from each cluster's queue. Process it in `this` cluster's queue - // in order to synchronize event processing. - ic.queue.Push(func() error { - namespacedName := namespacedNameForService(svc) - - // Lookup the previous MCS service if there was one. - mcsHost := serviceClusterSetLocalHostname(namespacedName) - prevMcsService := ic.GetService(mcsHost) - - // Get the ClusterSet VIPs for this service in this cluster. Will only be populated if the - // service has a ServiceImport in this cluster. - vips := ic.getClusterSetIPs(namespacedName) - name := namespacedName.Name - ns := namespacedName.Namespace - - if len(vips) == 0 || (event == model.EventDelete && - ic.opts.MeshServiceController.GetService(kube.ServiceHostname(name, ns, ic.opts.DomainSuffix)) == nil) { - if prevMcsService != nil { - // There are no vips in this cluster. Just delete the MCS service now. - ic.deleteService(prevMcsService) - } - return nil - } - - if prevMcsService != nil { - event = model.EventUpdate - } else { - event = model.EventAdd - } - - mcsService := ic.genMCSService(svc, mcsHost, vips) - ic.addOrUpdateService(nil, mcsService, event, false) - return nil - }) -} - -func (ic *serviceImportCacheImpl) onServiceImportEvent(obj interface{}, event model.Event) error { - si, ok := obj.(*unstructured.Unstructured) - if !ok { - tombstone, ok := obj.(cache.DeletedFinalStateUnknown) - if !ok { - return fmt.Errorf("couldn't get object from tombstone %#v", obj) - } - si, ok = tombstone.Obj.(*unstructured.Unstructured) - if !ok { - return fmt.Errorf("tombstone contained object that is not a ServiceImport %#v", obj) - } - } - - // We need a full push if the cluster VIP changes. - needsFullPush := false - - // Get the updated MCS service. - mcsHost := serviceClusterSetLocalHostnameForKR(si) - mcsService := ic.GetService(mcsHost) - - ips := GetServiceImportIPs(si) - if mcsService == nil { - if event == model.EventDelete || len(ips) == 0 { - // We never created the service. Nothing to delete. - return nil - } - - // The service didn't exist prior. Treat it as an add. - event = model.EventAdd - - // Create the MCS service, based on the cluster.local service. We get the merged, mesh-wide service - // from the aggregate controller so that we don't rely on the service existing in this cluster. - realService := ic.opts.MeshServiceController.GetService(kube.ServiceHostnameForKR(si, ic.opts.DomainSuffix)) - if realService == nil { - log.Warnf("failed processing %s event for ServiceImport %s/%s in cluster %s. No matching service found in cluster", - event, si.GetNamespace(), si.GetName(), ic.Cluster()) - return nil - } - - // Create the MCS service from the cluster.local service. - mcsService = ic.genMCSService(realService, mcsHost, ips) - } else { - if event == model.EventDelete || len(ips) == 0 { - ic.deleteService(mcsService) - return nil - } - - // The service already existed. Treat it as an update. - event = model.EventUpdate - - if ic.updateIPs(mcsService, ips) { - needsFullPush = true - } - } - - // Always force a rebuild of the endpoint cache in case this import caused - // a change to the discoverability policy. - ic.addOrUpdateService(nil, mcsService, event, true) - - if needsFullPush { - ic.doFullPush(mcsHost, si.GetNamespace()) - } - - return nil -} - -func (ic *serviceImportCacheImpl) updateIPs(mcsService *model.Service, ips []string) (updated bool) { - prevIPs := mcsService.ClusterVIPs.GetAddressesFor(ic.Cluster()) - if !util.StringSliceEqual(prevIPs, ips) { - // Update the VIPs - mcsService.ClusterVIPs.SetAddressesFor(ic.Cluster(), ips) - updated = true - } - return -} - -func (ic *serviceImportCacheImpl) doFullPush(mcsHost host.Name, ns string) { - pushReq := &model.PushRequest{ - Full: true, - ConfigsUpdated: map[model.ConfigKey]struct{}{{ - Kind: gvk.ServiceEntry, - Name: mcsHost.String(), - Namespace: ns, - }: {}}, - Reason: []model.TriggerReason{model.ServiceUpdate}, - } - ic.opts.XDSUpdater.ConfigUpdate(pushReq) -} - -// GetServiceImportIPs returns the list of ClusterSet IPs for the ServiceImport. -// Exported for testing only. -func GetServiceImportIPs(si *unstructured.Unstructured) []string { - var ips []string - if spec, ok := si.Object["spec"].(map[string]interface{}); ok { - if rawIPs, ok := spec["ips"].([]interface{}); ok { - for _, rawIP := range rawIPs { - ip := rawIP.(string) - if net.ParseIP(ip) != nil { - ips = append(ips, ip) - } - } - } - } - sort.Strings(ips) - return ips -} - -// genMCSService generates an MCS service based on the given real k8s service. The list of vips must be non-empty. -func (ic *serviceImportCacheImpl) genMCSService(realService *model.Service, mcsHost host.Name, vips []string) *model.Service { - mcsService := realService.DeepCopy() - mcsService.Hostname = mcsHost - mcsService.DefaultAddress = vips[0] - mcsService.ClusterVIPs.Addresses = map[cluster.ID][]string{ - ic.Cluster(): vips, - } - - return mcsService -} - -func (ic *serviceImportCacheImpl) getClusterSetIPs(name types.NamespacedName) []string { - if si, err := ic.lister.ByNamespace(name.Namespace).Get(name.Name); err == nil { - return GetServiceImportIPs(si.(*unstructured.Unstructured)) - } - return nil -} - -func (ic *serviceImportCacheImpl) ImportedServices() []importedService { - sis, err := ic.lister.List(klabels.Everything()) - if err != nil { - return make([]importedService, 0) - } - - // Iterate over the ServiceImport resources in this cluster. - out := make([]importedService, 0, len(sis)) - - ic.RLock() - for _, si := range sis { - usi := si.(*unstructured.Unstructured) - info := importedService{ - namespacedName: kube.NamespacedNameForK8sObject(usi), - } - - // Lookup the synthetic MCS service. - hostName := serviceClusterSetLocalHostnameForKR(usi) - svc := ic.servicesMap[hostName] - if svc != nil { - if vips := svc.ClusterVIPs.GetAddressesFor(ic.Cluster()); len(vips) > 0 { - info.clusterSetVIP = vips[0] - } - } - - out = append(out, info) - } - ic.RUnlock() - - return out -} - -func (ic *serviceImportCacheImpl) HasSynced() bool { - return ic.informer.HasSynced() -} - -type disabledServiceImportCache struct{} - -var _ serviceImportCache = disabledServiceImportCache{} - -func (c disabledServiceImportCache) HasSynced() bool { - return true -} - -func (c disabledServiceImportCache) ImportedServices() []importedService { - // MCS is disabled - returning `nil`, which is semantically different here than an empty list. - return nil -} diff --git a/pilot/pkg/serviceregistry/kube/controller/serviceimportcache_test.go b/pilot/pkg/serviceregistry/kube/controller/serviceimportcache_test.go deleted file mode 100644 index f88797ce3..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/serviceimportcache_test.go +++ /dev/null @@ -1,568 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "context" - "errors" - "fmt" - "reflect" - "testing" - "time" -) - -import ( - envoyCore "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - . "github.com/onsi/gomega" - "istio.io/api/label" - kubeMeta "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/tools/cache" - mcsapi "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/kube/mcs" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -const ( - serviceImportName = "test-svc" - serviceImportNamespace = "test-ns" - serviceImportPodIP = "128.0.0.2" - serviceImportCluster = "test-cluster" -) - -var ( - serviceImportNamespacedName = types.NamespacedName{ - Namespace: serviceImportNamespace, - Name: serviceImportName, - } - serviceImportClusterSetHost = serviceClusterSetLocalHostname(serviceImportNamespacedName) - serviceImportVIPs = []string{"1.1.1.1"} - serviceImportTimeout = retry.Timeout(2 * time.Second) -) - -func TestServiceNotImported(t *testing.T) { - for _, mode := range []EndpointMode{EndpointsOnly, EndpointSliceOnly} { - t.Run(mode.String(), func(t *testing.T) { - c, ic := newTestServiceImportCache(t, mode) - - ic.createKubeService(t, c) - - // Check that the service does not have ClusterSet IPs. - ic.checkServiceInstances(t) - }) - } -} - -func TestServiceImportedAfterCreated(t *testing.T) { - for _, mode := range []EndpointMode{EndpointsOnly, EndpointSliceOnly} { - t.Run(mode.String(), func(t *testing.T) { - c, ic := newTestServiceImportCache(t, mode) - - ic.createKubeService(t, c) - ic.createServiceImport(t, mcsapi.ClusterSetIP, serviceImportVIPs) - - // Check that the service has been assigned ClusterSet IPs. - ic.checkServiceInstances(t) - }) - } -} - -func TestServiceCreatedAfterImported(t *testing.T) { - for _, mode := range []EndpointMode{EndpointsOnly, EndpointSliceOnly} { - t.Run(mode.String(), func(t *testing.T) { - c, ic := newTestServiceImportCache(t, mode) - - ic.createServiceImport(t, mcsapi.ClusterSetIP, serviceImportVIPs) - ic.createKubeService(t, c) - - // Check that the service has been assigned ClusterSet IPs. - ic.checkServiceInstances(t) - }) - } -} - -func TestUpdateImportedService(t *testing.T) { - for _, mode := range []EndpointMode{EndpointsOnly, EndpointSliceOnly} { - t.Run(mode.String(), func(t *testing.T) { - c, ic := newTestServiceImportCache(t, mode) - - ic.createKubeService(t, c) - ic.createServiceImport(t, mcsapi.ClusterSetIP, serviceImportVIPs) - ic.checkServiceInstances(t) - - // Update the k8s service and verify that both services are updated. - ic.updateKubeService(t) - }) - } -} - -func TestHeadlessServiceImported(t *testing.T) { - for _, mode := range []EndpointMode{EndpointsOnly, EndpointSliceOnly} { - t.Run(mode.String(), func(t *testing.T) { - // Create and run the controller. - c, ic := newTestServiceImportCache(t, mode) - - ic.createKubeService(t, c) - ic.createServiceImport(t, mcsapi.Headless, nil) - - // Verify that we did not generate the synthetic service for the headless service. - ic.checkServiceInstances(t) - }) - } -} - -func TestDeleteImportedService(t *testing.T) { - for _, mode := range []EndpointMode{EndpointsOnly, EndpointSliceOnly} { - t.Run(mode.String(), func(t *testing.T) { - // Create and run the controller. - c1, ic := newTestServiceImportCache(t, mode) - - // Create and run another controller. - c2, _ := NewFakeControllerWithOptions(FakeControllerOptions{ - Stop: c1.stop, - ClusterID: "test-cluster2", - Mode: mode, - }) - go c2.Run(c2.stop) - cache.WaitForCacheSync(c2.stop, c2.HasSynced) - - c1.opts.MeshServiceController.AddRegistryAndRun(c2, c2.stop) - - ic.createKubeService(t, c1) - ic.createServiceImport(t, mcsapi.ClusterSetIP, serviceImportVIPs) - ic.checkServiceInstances(t) - - // create the same service in cluster2 - createService(c2, serviceImportName, serviceImportNamespace, map[string]string{}, - []int32{8080}, map[string]string{"app": "prod-app"}, t) - - // Delete the k8s service and verify that all internal services are removed. - ic.deleteKubeService(t, c2) - }) - } -} - -func TestUnimportService(t *testing.T) { - for _, mode := range []EndpointMode{EndpointsOnly, EndpointSliceOnly} { - t.Run(mode.String(), func(t *testing.T) { - // Create and run the controller. - c, ic := newTestServiceImportCache(t, mode) - - ic.createKubeService(t, c) - ic.createServiceImport(t, mcsapi.ClusterSetIP, serviceImportVIPs) - ic.checkServiceInstances(t) - - ic.unimportService(t) - }) - } -} - -func TestAddServiceImportVIPs(t *testing.T) { - for _, mode := range []EndpointMode{EndpointsOnly, EndpointSliceOnly} { - t.Run(mode.String(), func(t *testing.T) { - // Create and run the controller. - c, ic := newTestServiceImportCache(t, mode) - - ic.createKubeService(t, c) - ic.createServiceImport(t, mcsapi.ClusterSetIP, nil) - ic.checkServiceInstances(t) - - ic.setServiceImportVIPs(t, serviceImportVIPs) - }) - } -} - -func TestUpdateServiceImportVIPs(t *testing.T) { - for _, mode := range []EndpointMode{EndpointsOnly, EndpointSliceOnly} { - t.Run(mode.String(), func(t *testing.T) { - // Create and run the controller. - c, ic := newTestServiceImportCache(t, mode) - - ic.createKubeService(t, c) - ic.createServiceImport(t, mcsapi.ClusterSetIP, serviceImportVIPs) - ic.checkServiceInstances(t) - - updatedVIPs := []string{"1.1.1.1", "1.1.1.2"} - ic.setServiceImportVIPs(t, updatedVIPs) - }) - } -} - -func newTestServiceImportCache(t test.Failer, mode EndpointMode) (c *FakeController, ic *serviceImportCacheImpl) { - stopCh := make(chan struct{}) - test.SetBoolForTest(t, &features.EnableMCSHost, true) - t.Cleanup(func() { - close(stopCh) - }) - - c, _ = NewFakeControllerWithOptions(FakeControllerOptions{ - Stop: stopCh, - ClusterID: serviceImportCluster, - Mode: mode, - }) - go c.Run(c.stop) - cache.WaitForCacheSync(c.stop, c.HasSynced) - - ic = c.imports.(*serviceImportCacheImpl) - return -} - -func (ic *serviceImportCacheImpl) createKubeService(t *testing.T, c *FakeController) { - t.Helper() - - // Create the test service and endpoints. - createService(c, serviceImportName, serviceImportNamespace, map[string]string{}, - []int32{8080}, map[string]string{"app": "prod-app"}, t) - createEndpoints(t, c, serviceImportName, serviceImportNamespace, []string{"tcp-port"}, []string{serviceImportPodIP}, nil, nil) - - isImported := ic.isImported(serviceImportNamespacedName) - - // Wait for the resources to be processed by the controller. - retry.UntilSuccessOrFail(t, func() error { - clusterLocalHost := ic.clusterLocalHost() - if svc := c.GetService(clusterLocalHost); svc == nil { - return fmt.Errorf("failed looking up service for host %s", clusterLocalHost) - } - - var expectedHosts map[host.Name]struct{} - if isImported { - expectedHosts = map[host.Name]struct{}{ - clusterLocalHost: {}, - serviceImportClusterSetHost: {}, - } - } else { - expectedHosts = map[host.Name]struct{}{ - clusterLocalHost: {}, - } - } - - instances := ic.getProxyServiceInstances() - if len(instances) != len(expectedHosts) { - return fmt.Errorf("expected 1 service instance, found %d", len(instances)) - } - for _, si := range instances { - if si.Service == nil { - return fmt.Errorf("proxy ServiceInstance has nil service") - } - if si.Endpoint == nil { - return fmt.Errorf("proxy ServiceInstance has nil endpoint") - } - if _, found := expectedHosts[si.Service.Hostname]; !found { - return fmt.Errorf("found proxy ServiceInstance for unexpected host: %s", si.Service.Hostname) - } - delete(expectedHosts, si.Service.Hostname) - } - - if len(expectedHosts) > 0 { - return fmt.Errorf("failed to find proxy ServiceInstances for hosts: %v", expectedHosts) - } - - return nil - }, serviceImportTimeout) -} - -func (ic *serviceImportCacheImpl) updateKubeService(t *testing.T) { - t.Helper() - svc, _ := ic.client.CoreV1().Services(serviceImportNamespace).Get(context.TODO(), serviceImportName, kubeMeta.GetOptions{}) - if svc == nil { - t.Fatalf("failed to find k8s service: %s/%s", serviceImportNamespace, serviceImportName) - } - - // Just add a new label. - svc.Labels = map[string]string{ - "foo": "bar", - } - if _, err := ic.client.CoreV1().Services(serviceImportNamespace).Update(context.TODO(), svc, kubeMeta.UpdateOptions{}); err != nil { - t.Fatal(err) - } - - hostNames := []host.Name{ - ic.clusterLocalHost(), - serviceImportClusterSetHost, - } - - // Wait for the services to pick up the label. - retry.UntilSuccessOrFail(t, func() error { - for _, hostName := range hostNames { - svc := ic.GetService(hostName) - if svc == nil { - return fmt.Errorf("failed to find service for host %s", hostName) - } - if svc.Attributes.Labels["foo"] != "bar" { - return fmt.Errorf("service not updated for %s", hostName) - } - } - - return nil - }, serviceImportTimeout) -} - -func (ic *serviceImportCacheImpl) deleteKubeService(t *testing.T, anotherCluster *FakeController) { - t.Helper() - - if err := anotherCluster.client.CoreV1().Services(serviceImportNamespace).Delete(context.TODO(), serviceImportName, kubeMeta.DeleteOptions{}); err != nil { - t.Fatal(err) - } - // Wait for the resources to be processed by the controller. - if err := ic.client.CoreV1().Services(serviceImportNamespace).Delete(context.TODO(), serviceImportName, kubeMeta.DeleteOptions{}); err != nil { - t.Fatal(err) - } - - // Wait for the resources to be processed by the controller. - retry.UntilSuccessOrFail(t, func() error { - if svc := ic.GetService(ic.clusterLocalHost()); svc != nil { - return fmt.Errorf("found deleted service for host %s", ic.clusterLocalHost()) - } - if svc := ic.GetService(serviceImportClusterSetHost); svc != nil { - return fmt.Errorf("found deleted service for host %s", serviceImportClusterSetHost) - } - - instances := ic.getProxyServiceInstances() - if len(instances) != 0 { - return fmt.Errorf("expected 0 service instance, found %d", len(instances)) - } - - return nil - }, serviceImportTimeout) -} - -func (ic *serviceImportCacheImpl) getProxyServiceInstances() []*model.ServiceInstance { - return ic.GetProxyServiceInstances(&model.Proxy{ - Type: model.SidecarProxy, - IPAddresses: []string{serviceImportPodIP}, - Locality: &envoyCore.Locality{Region: "r", Zone: "z"}, - ConfigNamespace: serviceImportNamespace, - Metadata: &model.NodeMetadata{ - ServiceAccount: "account", - ClusterID: ic.Cluster(), - Labels: map[string]string{ - "app": "prod-app", - label.SecurityTlsMode.Name: "mutual", - }, - }, - }) -} - -func (ic *serviceImportCacheImpl) getServiceImport(t *testing.T) *mcsapi.ServiceImport { - t.Helper() - - // Get the ServiceImport as unstructured - u, err := ic.client.Dynamic().Resource(mcs.ServiceImportGVR).Namespace(serviceImportNamespace).Get( - context.TODO(), serviceImportName, kubeMeta.GetOptions{}) - if err != nil { - return nil - } - - // Convert to ServiceImport - si := &mcsapi.ServiceImport{} - if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, si); err != nil { - t.Fatal(err) - } - return si -} - -func (ic *serviceImportCacheImpl) checkServiceInstances(t *testing.T) { - t.Helper() - g := NewWithT(t) - - si := ic.getServiceImport(t) - - var expectedIPs []string - expectedServiceCount := 1 - expectMCSService := false - if si != nil && si.Spec.Type == mcsapi.ClusterSetIP && len(si.Spec.IPs) > 0 { - expectedIPs = si.Spec.IPs - expectedServiceCount = 2 - expectMCSService = true - } - - instances := ic.getProxyServiceInstances() - g.Expect(instances).To(HaveLen(expectedServiceCount)) - - for _, inst := range instances { - svc := inst.Service - if svc.Hostname == serviceImportClusterSetHost { - if !expectMCSService { - t.Fatalf("found ServiceInstance for unimported service %s", serviceImportClusterSetHost) - } - // Check the ClusterSet IPs. - g.Expect(svc.ClusterVIPs.GetAddressesFor(ic.Cluster())).To(Equal(expectedIPs)) - return - } - } - - if expectMCSService { - t.Fatalf("failed finding ServiceInstance for %s", serviceImportClusterSetHost) - } -} - -func (ic *serviceImportCacheImpl) createServiceImport(t *testing.T, importType mcsapi.ServiceImportType, vips []string) { - t.Helper() - - // Create the ServiceImport resource in the cluster. - _, err := ic.client.Dynamic().Resource(mcs.ServiceImportGVR).Namespace(serviceImportNamespace).Create(context.TODO(), - newServiceImport(importType, vips), - kubeMeta.CreateOptions{}) - if err != nil { - t.Fatal(err) - } - - shouldCreateMCSService := importType == mcsapi.ClusterSetIP && len(vips) > 0 && - ic.GetService(ic.clusterLocalHost()) != nil - - // Wait for the import to be processed by the controller. - retry.UntilSuccessOrFail(t, func() error { - if !ic.isImported(serviceImportNamespacedName) { - return fmt.Errorf("serviceImport not found for %s", serviceImportClusterSetHost) - } - if shouldCreateMCSService && ic.GetService(serviceImportClusterSetHost) == nil { - return fmt.Errorf("failed to find service for %s", serviceImportClusterSetHost) - } - return nil - }, serviceImportTimeout) - - if shouldCreateMCSService { - // Wait for the XDS event. - ic.waitForXDS(t) - } -} - -func (ic *serviceImportCacheImpl) setServiceImportVIPs(t *testing.T, vips []string) { - t.Helper() - - // Get the ServiceImport - si := ic.getServiceImport(t) - - // Apply the ClusterSet IPs. - si.Spec.IPs = vips - if _, err := ic.client.Dynamic().Resource(mcs.ServiceImportGVR).Namespace(serviceImportNamespace).Update( - context.TODO(), toUnstructured(si), kubeMeta.UpdateOptions{}); err != nil { - t.Fatal(err) - } - - if len(vips) > 0 { - // Wait for the import to be processed by the controller. - retry.UntilSuccessOrFail(t, func() error { - svc := ic.GetService(serviceImportClusterSetHost) - if svc == nil { - return fmt.Errorf("failed to find service for %s", serviceImportClusterSetHost) - } - - actualVIPs := svc.ClusterVIPs.GetAddressesFor(ic.Cluster()) - if !reflect.DeepEqual(vips, actualVIPs) { - return fmt.Errorf("expected ClusterSet VIPs %v, but found %v", vips, actualVIPs) - } - return nil - }, serviceImportTimeout) - - // Wait for the XDS event. - ic.waitForXDS(t) - } else { - // Wait for the import to be processed by the controller. - retry.UntilSuccessOrFail(t, func() error { - if svc := ic.GetService(serviceImportClusterSetHost); svc != nil { - return fmt.Errorf("found unexpected service for %s", serviceImportClusterSetHost) - } - return nil - }, serviceImportTimeout) - } -} - -func (ic *serviceImportCacheImpl) unimportService(t *testing.T) { - t.Helper() - - if err := ic.client.Dynamic().Resource(mcs.ServiceImportGVR).Namespace(serviceImportNamespace).Delete( - context.TODO(), serviceImportName, kubeMeta.DeleteOptions{}); err != nil { - t.Fatal(err) - } - - // Wait for the import to be processed by the controller. - retry.UntilSuccessOrFail(t, func() error { - if ic.isImported(serviceImportNamespacedName) { - return fmt.Errorf("serviceImport found for %s", serviceImportClusterSetHost) - } - if ic.GetService(serviceImportClusterSetHost) != nil { - return fmt.Errorf("found MCS service for unimported service %s", serviceImportClusterSetHost) - } - return nil - }, serviceImportTimeout) -} - -func (ic *serviceImportCacheImpl) isImported(name types.NamespacedName) bool { - _, err := ic.lister.ByNamespace(name.Namespace).Get(name.Name) - return err == nil -} - -func (ic *serviceImportCacheImpl) waitForXDS(t *testing.T) { - t.Helper() - - retry.UntilSuccessOrFail(t, func() error { - return ic.checkXDS() - }, serviceImportTimeout) -} - -func (ic *serviceImportCacheImpl) checkXDS() error { - event := ic.opts.XDSUpdater.(*FakeXdsUpdater).Wait("service") - if event == nil { - return errors.New("failed waiting for XDS event") - } - - // The name of the event will be the cluster-local hostname. - eventID := serviceImportClusterSetHost.String() - if event.ID != eventID { - return fmt.Errorf("waitForXDS failed: expected event id=%s, but found %s", eventID, event.ID) - } - return nil -} - -func (ic *serviceImportCacheImpl) clusterLocalHost() host.Name { - return kube.ServiceHostname(serviceImportName, serviceImportNamespace, ic.opts.DomainSuffix) -} - -func newServiceImport(importType mcsapi.ServiceImportType, vips []string) *unstructured.Unstructured { - si := &mcsapi.ServiceImport{ - TypeMeta: kubeMeta.TypeMeta{ - Kind: "ServiceImport", - APIVersion: "multicluster.x-k8s.io/v1alpha1", - }, - ObjectMeta: kubeMeta.ObjectMeta{ - Name: serviceImportName, - Namespace: serviceImportNamespace, - }, - Spec: mcsapi.ServiceImportSpec{ - Type: importType, - IPs: vips, - }, - } - return toUnstructured(si) -} - -func toUnstructured(o interface{}) *unstructured.Unstructured { - u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(o) - if err != nil { - panic(err) - } - return &unstructured.Unstructured{Object: u} -} diff --git a/pilot/pkg/serviceregistry/kube/controller/sync.go b/pilot/pkg/serviceregistry/kube/controller/sync.go deleted file mode 100644 index 6e467797f..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/sync.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "go.uber.org/atomic" -) - -var SyncAllKinds = map[string]struct{}{ - "Services": {}, - "Nodes": {}, - "Pods": {}, - "Endpoints": {}, - "EndpointSlice": {}, -} - -func isSyncAllKind(kind string) bool { - if _, ok := SyncAllKinds[kind]; ok { - return true - } - return false -} - -func shouldEnqueue(kind string, beginSync *atomic.Bool) bool { - if isSyncAllKind(kind) && !beginSync.Load() { - return false - } - return true -} diff --git a/pilot/pkg/serviceregistry/kube/controller/util.go b/pilot/pkg/serviceregistry/kube/controller/util.go deleted file mode 100644 index 91e483c2f..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/util.go +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "encoding/json" - "fmt" - "strings" -) - -import ( - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - klabels "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/selection" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/apimachinery/pkg/util/validation/field" - listerv1 "k8s.io/client-go/listers/core/v1" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" -) - -func hasProxyIP(addresses []v1.EndpointAddress, proxyIP string) bool { - for _, addr := range addresses { - if addr.IP == proxyIP { - return true - } - } - return false -} - -func getLabelValue(metadata metav1.Object, label string, fallBackLabel string) string { - metaLabels := metadata.GetLabels() - val := metaLabels[label] - if val != "" { - return val - } - - return metaLabels[fallBackLabel] -} - -// Forked from Kubernetes k8s.io/kubernetes/pkg/api/v1/pod -// FindPort locates the container port for the given pod and portName. If the -// targetPort is a number, use that. If the targetPort is a string, look that -// string up in all named ports in all containers in the target pod. If no -// match is found, fail. -func FindPort(pod *v1.Pod, svcPort *v1.ServicePort) (int, error) { - portName := svcPort.TargetPort - switch portName.Type { - case intstr.String: - name := portName.StrVal - for _, container := range pod.Spec.Containers { - for _, port := range container.Ports { - if port.Name == name && port.Protocol == svcPort.Protocol { - return int(port.ContainerPort), nil - } - } - } - case intstr.Int: - return portName.IntValue(), nil - } - - return 0, fmt.Errorf("no suitable port for manifest: %s", pod.UID) -} - -// findPortFromMetadata resolves the TargetPort of a Service Port, by reading the Pod spec. -func findPortFromMetadata(svcPort v1.ServicePort, podPorts []model.PodPort) (int, error) { - target := svcPort.TargetPort - - switch target.Type { - case intstr.String: - name := target.StrVal - for _, port := range podPorts { - if port.Name == name && port.Protocol == string(svcPort.Protocol) { - return port.ContainerPort, nil - } - } - case intstr.Int: - // For a direct reference we can just return the port number - return target.IntValue(), nil - } - - return 0, fmt.Errorf("no matching port found for %+v", svcPort) -} - -type serviceTargetPort struct { - // the mapped port number, or 0 if unspecified - num int - // the mapped port name - name string - // a bool indicating if the mapped port name was explicitly set on the TargetPort field, or inferred from k8s' port.Name - explicitName bool -} - -func findServiceTargetPort(servicePort *model.Port, k8sService *v1.Service) serviceTargetPort { - for _, p := range k8sService.Spec.Ports { - // TODO(@hzxuzhonghu): check protocol as well as port - if p.Name == servicePort.Name || p.Port == int32(servicePort.Port) { - if p.TargetPort.Type == intstr.Int && p.TargetPort.IntVal > 0 { - return serviceTargetPort{num: int(p.TargetPort.IntVal), name: p.Name, explicitName: false} - } - return serviceTargetPort{num: 0, name: p.TargetPort.StrVal, explicitName: true} - } - } - // should never happen - log.Debugf("did not find matching target port for %v on service %s", servicePort, k8sService.Name) - return serviceTargetPort{num: 0, name: "", explicitName: false} -} - -func getPodServices(s listerv1.ServiceLister, pod *v1.Pod) ([]*v1.Service, error) { - allServices, err := s.Services(pod.Namespace).List(klabels.Everything()) - if err != nil { - return nil, err - } - - var services []*v1.Service - for _, service := range allServices { - if service.Spec.Selector == nil { - // services with nil selectors match nothing, not everything. - continue - } - selector := klabels.Set(service.Spec.Selector).AsSelectorPreValidated() - if selector.Matches(klabels.Set(pod.Labels)) { - services = append(services, service) - } - } - - return services, nil -} - -func portsEqual(a, b []v1.EndpointPort) bool { - if len(a) != len(b) { - return false - } - - for i := range a { - if a[i].Name != b[i].Name || a[i].Port != b[i].Port || a[i].Protocol != b[i].Protocol || - ptrValueOrEmpty(a[i].AppProtocol) != ptrValueOrEmpty(b[i].AppProtocol) { - return false - } - } - - return true -} - -func addressesEqual(a, b []v1.EndpointAddress) bool { - if len(a) != len(b) { - return false - } - - for i := range a { - if a[i].IP != b[i].IP || a[i].Hostname != b[i].Hostname || - ptrValueOrEmpty(a[i].NodeName) != ptrValueOrEmpty(b[i].NodeName) { - return false - } - } - - return true -} - -func ptrValueOrEmpty(ptr *string) string { - if ptr != nil { - return *ptr - } - return "" -} - -func getNodeSelectorsForService(svc *v1.Service) labels.Instance { - if nodeSelector := svc.Annotations[kube.NodeSelectorAnnotation]; nodeSelector != "" { - var nodeSelectorKV map[string]string - if err := json.Unmarshal([]byte(nodeSelector), &nodeSelectorKV); err != nil { - log.Debugf("failed to unmarshal node selector annotation value for service %s.%s: %v", - svc.Name, svc.Namespace, err) - } - return nodeSelectorKV - } - return nil -} - -func nodeEquals(a, b kubernetesNode) bool { - return a.address == b.address && a.labels.Equals(b.labels) -} - -func isNodePortGatewayService(svc *v1.Service) bool { - if svc == nil { - return false - } - _, ok := svc.Annotations[kube.NodeSelectorAnnotation] - return ok && svc.Spec.Type == v1.ServiceTypeNodePort -} - -// Get the pod key of the proxy which can be used to get pod from the informer cache -func podKeyByProxy(proxy *model.Proxy) string { - parts := strings.Split(proxy.ID, ".") - if len(parts) == 2 && proxy.Metadata.Namespace == parts[1] { - return kube.KeyFunc(parts[0], parts[1]) - } - - return "" -} - -func convertToService(obj interface{}) (*v1.Service, error) { - cm, ok := obj.(*v1.Service) - if !ok { - tombstone, ok := obj.(cache.DeletedFinalStateUnknown) - if !ok { - return nil, fmt.Errorf("couldn't get object from tombstone %#v", obj) - } - cm, ok = tombstone.Obj.(*v1.Service) - if !ok { - return nil, fmt.Errorf("tombstone contained object that is not a Service %#v", obj) - } - } - return cm, nil -} - -func namespacedNameForService(svc *model.Service) types.NamespacedName { - return types.NamespacedName{ - Namespace: svc.Attributes.Namespace, - Name: svc.Attributes.Name, - } -} - -// serviceClusterSetLocalHostname produces Kubernetes Multi-Cluster Services (MCS) ClusterSet FQDN for a k8s service -func serviceClusterSetLocalHostname(nn types.NamespacedName) host.Name { - return host.Name(nn.Name + "." + nn.Namespace + "." + "svc" + "." + constants.DefaultClusterSetLocalDomain) -} - -// serviceClusterSetLocalHostnameForKR calls serviceClusterSetLocalHostname with the name and namespace of the given kubernetes resource. -func serviceClusterSetLocalHostnameForKR(obj metav1.Object) host.Name { - return serviceClusterSetLocalHostname(types.NamespacedName{Name: obj.GetName(), Namespace: obj.GetNamespace()}) -} - -func labelRequirement(key string, op selection.Operator, vals []string, opts ...field.PathOption) *klabels.Requirement { - out, err := klabels.NewRequirement(key, op, vals, opts...) - if err != nil { - panic(fmt.Sprintf("failed creating requirements for Service: %v", err)) - } - return out -} diff --git a/pilot/pkg/serviceregistry/kube/controller/util_test.go b/pilot/pkg/serviceregistry/kube/controller/util_test.go deleted file mode 100644 index d75001cd3..000000000 --- a/pilot/pkg/serviceregistry/kube/controller/util_test.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "reflect" - "testing" -) - -import ( - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" -) - -func TestHasProxyIP(t *testing.T) { - tests := []struct { - name string - addresses []v1.EndpointAddress - proxyIP string - expected bool - }{ - { - "has proxy ip", - []v1.EndpointAddress{{IP: "172.17.0.1"}, {IP: "172.17.0.2"}}, - "172.17.0.1", - true, - }, - { - "has no proxy ip", - []v1.EndpointAddress{{IP: "172.17.0.1"}, {IP: "172.17.0.2"}}, - "172.17.0.100", - false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := hasProxyIP(test.addresses, test.proxyIP) - if test.expected != got { - t.Errorf("Expected %v, but got %v", test.expected, got) - } - }) - } -} - -func TestGetLabelValue(t *testing.T) { - tests := []struct { - name string - node *v1.Node - expectedLabelValue string - }{ - { - "Chooses beta label", - &v1.Node{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{NodeRegionLabel: "beta-region", NodeRegionLabelGA: "ga-region"}}}, - "beta-region", - }, - { - "Fallback no beta label defined", - &v1.Node{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{NodeRegionLabelGA: "ga-region"}}}, - "ga-region", - }, - { - "Only beta label specified", - &v1.Node{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{NodeRegionLabel: "beta-region"}}}, - "beta-region", - }, - { - "No label defined at all", - &v1.Node{}, - "", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := getLabelValue(test.node, NodeRegionLabel, NodeRegionLabelGA) - if test.expectedLabelValue != got { - t.Errorf("Expected %v, but got %v", test.expectedLabelValue, got) - } - }) - } -} - -func TestPodKeyByProxy(t *testing.T) { - testCases := []struct { - name string - proxy *model.Proxy - expectedKey string - }{ - { - name: "invalid id: bad format", - proxy: &model.Proxy{ - ID: "invalid", - Metadata: &model.NodeMetadata{ - Namespace: "default", - }, - }, - expectedKey: "", - }, - { - name: "invalid id: namespace mismatch", - proxy: &model.Proxy{ - ID: "pod1.ns1", - Metadata: &model.NodeMetadata{ - Namespace: "default", - }, - }, - expectedKey: "", - }, - { - name: "invalid id: namespace mismatch", - proxy: &model.Proxy{ - ID: "pod1.ns1", - Metadata: &model.NodeMetadata{ - Namespace: "ns1", - }, - }, - expectedKey: "ns1/pod1", - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - key := podKeyByProxy(tc.proxy) - if key != tc.expectedKey { - t.Errorf("expected key %s != %s", tc.expectedKey, key) - } - }) - } -} - -func TestGetNodeSelectorsForService(t *testing.T) { - testCases := []struct { - name string - svc *v1.Service - expectedLabelSelector labels.Instance - }{ - { - name: "empty selector", - svc: makeFakeSvc(""), - expectedLabelSelector: nil, - }, - { - name: "invalid selector", - svc: makeFakeSvc("invalid value"), - expectedLabelSelector: nil, - }, - { - name: "wildcard match", - svc: makeFakeSvc("{}"), - expectedLabelSelector: labels.Instance{}, - }, - { - name: "specific match", - svc: makeFakeSvc(`{"kubernetes.io/hostname": "node1"}`), - expectedLabelSelector: labels.Instance{"kubernetes.io/hostname": "node1"}, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - selector := getNodeSelectorsForService(tc.svc) - if !reflect.DeepEqual(selector, tc.expectedLabelSelector) { - t.Errorf("expected selector %v != %v", tc.expectedLabelSelector, selector) - } - }) - } -} - -func makeFakeSvc(nodeSelector string) *v1.Service { - svc := &v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "service", - Namespace: "ns", - }, - Spec: v1.ServiceSpec{ - Ports: []v1.ServicePort{{ - Name: "http", - Port: 80, - }}, - Selector: map[string]string{"app": "helloworld"}, - ClusterIP: "9.9.9.9", - }, - } - - if nodeSelector != "" { - svc.Annotations = map[string]string{ - "traffic.istio.io/nodeSelector": nodeSelector, - } - } - return svc -} diff --git a/pilot/pkg/serviceregistry/kube/conversion.go b/pilot/pkg/serviceregistry/kube/conversion.go deleted file mode 100644 index ca101c712..000000000 --- a/pilot/pkg/serviceregistry/kube/conversion.go +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "strings" -) - -import ( - "istio.io/api/annotation" - coreV1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/kube" - "github.com/apache/dubbo-go-pixiu/pkg/config/visibility" - "github.com/apache/dubbo-go-pixiu/pkg/spiffe" -) - -const ( - // IngressClassAnnotation is the annotation on ingress resources for the class of controllers - // responsible for it - IngressClassAnnotation = "kubernetes.io/ingress.class" - - // NodeSelectorAnnotation is the value for this annotation is a set of key value pairs (node labels) - // that can be used to select a subset of nodes from the pool of k8s nodes - // It is used for multi-cluster scenario, and with nodePort type gateway service. - // TODO: move to API - NodeSelectorAnnotation = "traffic.istio.io/nodeSelector" -) - -func convertPort(port coreV1.ServicePort) *model.Port { - return &model.Port{ - Name: port.Name, - Port: int(port.Port), - Protocol: kube.ConvertProtocol(port.Port, port.Name, port.Protocol, port.AppProtocol), - } -} - -func ConvertService(svc coreV1.Service, domainSuffix string, clusterID cluster.ID) *model.Service { - addr := constants.UnspecifiedIP - resolution := model.ClientSideLB - meshExternal := false - - if svc.Spec.Type == coreV1.ServiceTypeExternalName && svc.Spec.ExternalName != "" { - resolution = model.DNSLB - meshExternal = true - } - - if svc.Spec.ClusterIP == coreV1.ClusterIPNone { // headless services should not be load balanced - resolution = model.Passthrough - } else if svc.Spec.ClusterIP != "" { - addr = svc.Spec.ClusterIP - } - - ports := make([]*model.Port, 0, len(svc.Spec.Ports)) - for _, port := range svc.Spec.Ports { - ports = append(ports, convertPort(port)) - } - - var exportTo map[visibility.Instance]bool - serviceaccounts := make([]string, 0) - if svc.Annotations[annotation.AlphaCanonicalServiceAccounts.Name] != "" { - serviceaccounts = append(serviceaccounts, strings.Split(svc.Annotations[annotation.AlphaCanonicalServiceAccounts.Name], ",")...) - } - if svc.Annotations[annotation.AlphaKubernetesServiceAccounts.Name] != "" { - for _, ksa := range strings.Split(svc.Annotations[annotation.AlphaKubernetesServiceAccounts.Name], ",") { - serviceaccounts = append(serviceaccounts, kubeToIstioServiceAccount(ksa, svc.Namespace)) - } - } - if svc.Annotations[annotation.NetworkingExportTo.Name] != "" { - namespaces := strings.Split(svc.Annotations[annotation.NetworkingExportTo.Name], ",") - exportTo = make(map[visibility.Instance]bool, len(namespaces)) - for _, ns := range namespaces { - exportTo[visibility.Instance(ns)] = true - } - } - - istioService := &model.Service{ - Hostname: ServiceHostname(svc.Name, svc.Namespace, domainSuffix), - ClusterVIPs: model.AddressMap{ - Addresses: map[cluster.ID][]string{ - clusterID: {addr}, - }, - }, - Ports: ports, - DefaultAddress: addr, - ServiceAccounts: serviceaccounts, - MeshExternal: meshExternal, - Resolution: resolution, - CreationTime: svc.CreationTimestamp.Time, - ResourceVersion: svc.ResourceVersion, - Attributes: model.ServiceAttributes{ - ServiceRegistry: provider.Kubernetes, - Name: svc.Name, - Namespace: svc.Namespace, - Labels: svc.Labels, - ExportTo: exportTo, - LabelSelectors: svc.Spec.Selector, - }, - } - - switch svc.Spec.Type { - case coreV1.ServiceTypeNodePort: - if _, ok := svc.Annotations[NodeSelectorAnnotation]; !ok { - // only do this for istio ingress-gateway services - break - } - // store the service port to node port mappings - portMap := make(map[uint32]uint32) - for _, p := range svc.Spec.Ports { - portMap[uint32(p.Port)] = uint32(p.NodePort) - } - istioService.Attributes.ClusterExternalPorts = map[cluster.ID]map[uint32]uint32{clusterID: portMap} - // address mappings will be done elsewhere - case coreV1.ServiceTypeLoadBalancer: - if len(svc.Status.LoadBalancer.Ingress) > 0 { - var lbAddrs []string - for _, ingress := range svc.Status.LoadBalancer.Ingress { - if len(ingress.IP) > 0 { - lbAddrs = append(lbAddrs, ingress.IP) - } else if len(ingress.Hostname) > 0 { - // DO NOT resolve the DNS here. In environments like AWS, the ELB hostname - // does not have a repeatable DNS address and IPs resolved at an earlier point - // in time may not work. So, when we get just hostnames instead of IPs, we need - // to smartly switch from EDS to strict_dns rather than doing the naive thing of - // resolving the DNS name and hoping the resolution is one-time task. - lbAddrs = append(lbAddrs, ingress.Hostname) - } - } - if len(lbAddrs) > 0 { - istioService.Attributes.ClusterExternalAddresses.SetAddressesFor(clusterID, lbAddrs) - } - } - } - - istioService.Attributes.ClusterExternalAddresses.AddAddressesFor(clusterID, svc.Spec.ExternalIPs) - - return istioService -} - -func ExternalNameServiceInstances(k8sSvc *coreV1.Service, svc *model.Service) []*model.ServiceInstance { - if k8sSvc == nil || k8sSvc.Spec.Type != coreV1.ServiceTypeExternalName || k8sSvc.Spec.ExternalName == "" { - return nil - } - out := make([]*model.ServiceInstance, 0, len(svc.Ports)) - - discoverabilityPolicy := model.AlwaysDiscoverable - if features.EnableMCSServiceDiscovery { - // MCS spec does not allow export of external name services. - // See https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#exporting-services. - discoverabilityPolicy = model.DiscoverableFromSameCluster - } - for _, portEntry := range svc.Ports { - out = append(out, &model.ServiceInstance{ - Service: svc, - ServicePort: portEntry, - Endpoint: &model.IstioEndpoint{ - Address: k8sSvc.Spec.ExternalName, - EndpointPort: uint32(portEntry.Port), - ServicePortName: portEntry.Name, - Labels: k8sSvc.Labels, - DiscoverabilityPolicy: discoverabilityPolicy, - }, - }) - } - return out -} - -// NamespacedNameForK8sObject is a helper that creates a NamespacedName for the given K8s Object. -func NamespacedNameForK8sObject(obj metav1.Object) types.NamespacedName { - return types.NamespacedName{ - Namespace: obj.GetNamespace(), - Name: obj.GetName(), - } -} - -// ServiceHostname produces FQDN for a k8s service -func ServiceHostname(name, namespace, domainSuffix string) host.Name { - return host.Name(name + "." + namespace + "." + "svc" + "." + domainSuffix) // Format: "%s.%s.svc.%s" -} - -// ServiceHostnameForKR calls ServiceHostname with the name and namespace of the given kubernetes resource. -func ServiceHostnameForKR(obj metav1.Object, domainSuffix string) host.Name { - return ServiceHostname(obj.GetName(), obj.GetNamespace(), domainSuffix) -} - -// kubeToIstioServiceAccount converts a K8s service account to an Istio service account -func kubeToIstioServiceAccount(saname string, ns string) string { - return spiffe.MustGenSpiffeURI(ns, saname) -} - -// SecureNamingSAN creates the secure naming used for SAN verification from pod metadata -func SecureNamingSAN(pod *coreV1.Pod) string { - return spiffe.MustGenSpiffeURI(pod.Namespace, pod.Spec.ServiceAccountName) -} - -// PodTLSMode returns the tls mode associated with the pod if pod has been injected with sidecar -func PodTLSMode(pod *coreV1.Pod) string { - if pod == nil { - return model.DisabledTLSModeLabel - } - return model.GetTLSModeFromEndpointLabels(pod.Labels) -} - -// KeyFunc is the internal API key function that returns "namespace"/"name" or -// "name" if "namespace" is empty -func KeyFunc(name, namespace string) string { - if len(namespace) == 0 { - return name - } - return namespace + "/" + name -} diff --git a/pilot/pkg/serviceregistry/kube/conversion_test.go b/pilot/pkg/serviceregistry/kube/conversion_test.go deleted file mode 100644 index 045195871..000000000 --- a/pilot/pkg/serviceregistry/kube/conversion_test.go +++ /dev/null @@ -1,443 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "fmt" - "reflect" - "strings" - "testing" - "time" -) - -import ( - "istio.io/api/annotation" - coreV1 "k8s.io/api/core/v1" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/kube" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/spiffe" -) - -var ( - domainSuffix = "company.com" - clusterID = cluster.ID("test-cluster") -) - -func TestConvertProtocol(t *testing.T) { - http := "http" - type protocolCase struct { - port int32 - name string - appProtocol *string - proto coreV1.Protocol - out protocol.Instance - } - protocols := []protocolCase{ - {8888, "", nil, coreV1.ProtocolTCP, protocol.Unsupported}, - {25, "", nil, coreV1.ProtocolTCP, protocol.TCP}, - {53, "", nil, coreV1.ProtocolTCP, protocol.TCP}, - {3306, "", nil, coreV1.ProtocolTCP, protocol.TCP}, - {27017, "", nil, coreV1.ProtocolTCP, protocol.TCP}, - {8888, "http", nil, coreV1.ProtocolTCP, protocol.HTTP}, - {8888, "http-test", nil, coreV1.ProtocolTCP, protocol.HTTP}, - {8888, "http", nil, coreV1.ProtocolUDP, protocol.UDP}, - {8888, "httptest", nil, coreV1.ProtocolTCP, protocol.Unsupported}, - {25, "httptest", nil, coreV1.ProtocolTCP, protocol.TCP}, - {53, "httptest", nil, coreV1.ProtocolTCP, protocol.TCP}, - {3306, "httptest", nil, coreV1.ProtocolTCP, protocol.TCP}, - {27017, "httptest", nil, coreV1.ProtocolTCP, protocol.TCP}, - {8888, "https", nil, coreV1.ProtocolTCP, protocol.HTTPS}, - {8888, "https-test", nil, coreV1.ProtocolTCP, protocol.HTTPS}, - {8888, "http2", nil, coreV1.ProtocolTCP, protocol.HTTP2}, - {8888, "http2-test", nil, coreV1.ProtocolTCP, protocol.HTTP2}, - {8888, "grpc", nil, coreV1.ProtocolTCP, protocol.GRPC}, - {8888, "grpc-test", nil, coreV1.ProtocolTCP, protocol.GRPC}, - {8888, "grpc-web", nil, coreV1.ProtocolTCP, protocol.GRPCWeb}, - {8888, "grpc-web-test", nil, coreV1.ProtocolTCP, protocol.GRPCWeb}, - {8888, "mongo", nil, coreV1.ProtocolTCP, protocol.Mongo}, - {8888, "mongo-test", nil, coreV1.ProtocolTCP, protocol.Mongo}, - {8888, "redis", nil, coreV1.ProtocolTCP, protocol.Redis}, - {8888, "redis-test", nil, coreV1.ProtocolTCP, protocol.Redis}, - {8888, "mysql", nil, coreV1.ProtocolTCP, protocol.MySQL}, - {8888, "mysql-test", nil, coreV1.ProtocolTCP, protocol.MySQL}, - {8888, "tcp", &http, coreV1.ProtocolTCP, protocol.HTTP}, - } - - // Create the list of cases for all of the names in both upper and lowercase. - cases := make([]protocolCase, 0, len(protocols)*2) - for _, p := range protocols { - name := p.name - - p.name = strings.ToLower(name) - cases = append(cases, p) - - // Don't bother adding uppercase version for empty string. - if name != "" { - p.name = strings.ToUpper(name) - cases = append(cases, p) - } - } - - for _, c := range cases { - testName := strings.Replace(fmt.Sprintf("%s_%s_%d", c.name, c.proto, c.port), "-", "_", -1) - t.Run(testName, func(t *testing.T) { - out := kube.ConvertProtocol(c.port, c.name, c.proto, c.appProtocol) - if out != c.out { - t.Fatalf("convertProtocol(%d, %q, %q) => %q, want %q", c.port, c.name, c.proto, out, c.out) - } - }) - } -} - -func BenchmarkConvertProtocol(b *testing.B) { - cases := []struct { - name string - proto coreV1.Protocol - out protocol.Instance - }{ - {"grpc-web-lowercase", coreV1.ProtocolTCP, protocol.GRPCWeb}, - {"GRPC-WEB-mixedcase", coreV1.ProtocolTCP, protocol.GRPCWeb}, - {"https-lowercase", coreV1.ProtocolTCP, protocol.HTTPS}, - {"HTTPS-mixedcase", coreV1.ProtocolTCP, protocol.HTTPS}, - } - - for _, c := range cases { - testName := strings.Replace(c.name, "-", "_", -1) - b.Run(testName, func(b *testing.B) { - for i := 0; i < b.N; i++ { - out := kube.ConvertProtocol(8888, c.name, c.proto, nil) - if out != c.out { - b.Fatalf("convertProtocol(%q, %q) => %q, want %q", c.name, c.proto, out, c.out) - } - } - }) - } -} - -func TestServiceConversion(t *testing.T) { - serviceName := "service1" - namespace := "default" - saA := "serviceaccountA" - saB := "serviceaccountB" - saC := "spiffe://accounts.google.com/serviceaccountC@cloudservices.gserviceaccount.com" - saD := "spiffe://accounts.google.com/serviceaccountD@developer.gserviceaccount.com" - - oldTrustDomain := spiffe.GetTrustDomain() - spiffe.SetTrustDomain(domainSuffix) - defer spiffe.SetTrustDomain(oldTrustDomain) - - ip := "10.0.0.1" - - tnow := time.Now() - localSvc := coreV1.Service{ - ObjectMeta: metaV1.ObjectMeta{ - Name: serviceName, - Namespace: namespace, - Annotations: map[string]string{ - annotation.AlphaKubernetesServiceAccounts.Name: saA + "," + saB, - annotation.AlphaCanonicalServiceAccounts.Name: saC + "," + saD, - "other/annotation": "test", - }, - CreationTimestamp: metaV1.Time{Time: tnow}, - }, - Spec: coreV1.ServiceSpec{ - ClusterIP: ip, - Selector: map[string]string{"foo": "bar"}, - Ports: []coreV1.ServicePort{ - { - Name: "http", - Port: 8080, - Protocol: coreV1.ProtocolTCP, - }, - { - Name: "https", - Protocol: coreV1.ProtocolTCP, - Port: 443, - }, - }, - }, - } - - service := ConvertService(localSvc, domainSuffix, clusterID) - if service == nil { - t.Fatalf("could not convert service") - } - - if service.CreationTime != tnow { - t.Fatalf("incorrect creation time => %v, want %v", service.CreationTime, tnow) - } - - if len(service.Ports) != len(localSvc.Spec.Ports) { - t.Fatalf("incorrect number of ports => %v, want %v", - len(service.Ports), len(localSvc.Spec.Ports)) - } - - if service.External() { - t.Fatal("service should not be external") - } - - if service.Hostname != ServiceHostname(serviceName, namespace, domainSuffix) { - t.Fatalf("service hostname incorrect => %q, want %q", - service.Hostname, ServiceHostname(serviceName, namespace, domainSuffix)) - } - - ips := service.ClusterVIPs.GetAddressesFor(clusterID) - if len(ips) != 1 { - t.Fatalf("number of ips incorrect => %q, want 1", len(ips)) - } - - if ips[0] != ip { - t.Fatalf("service IP incorrect => %q, want %q", ips[0], ip) - } - - actualIPs := service.ClusterVIPs.GetAddressesFor(clusterID) - expectedIPs := []string{ip} - if !reflect.DeepEqual(actualIPs, expectedIPs) { - t.Fatalf("service IPs incorrect => %q, want %q", actualIPs, expectedIPs) - } - - if !reflect.DeepEqual(service.Attributes.LabelSelectors, localSvc.Spec.Selector) { - t.Fatalf("service label selectors incorrect => %q, want %q", service.Attributes.LabelSelectors, - localSvc.Spec.Selector) - } - - sa := service.ServiceAccounts - if sa == nil || len(sa) != 4 { - t.Fatalf("number of service accounts is incorrect") - } - expected := []string{ - saC, saD, - "spiffe://company.com/ns/default/sa/" + saA, - "spiffe://company.com/ns/default/sa/" + saB, - } - if !reflect.DeepEqual(sa, expected) { - t.Fatalf("Unexpected service accounts %v (expecting %v)", sa, expected) - } -} - -func TestServiceConversionWithEmptyServiceAccountsAnnotation(t *testing.T) { - serviceName := "service1" - namespace := "default" - - ip := "10.0.0.1" - - localSvc := coreV1.Service{ - ObjectMeta: metaV1.ObjectMeta{ - Name: serviceName, - Namespace: namespace, - Annotations: map[string]string{}, - }, - Spec: coreV1.ServiceSpec{ - ClusterIP: ip, - Ports: []coreV1.ServicePort{ - { - Name: "http", - Port: 8080, - Protocol: coreV1.ProtocolTCP, - }, - { - Name: "https", - Protocol: coreV1.ProtocolTCP, - Port: 443, - }, - }, - }, - } - - service := ConvertService(localSvc, domainSuffix, clusterID) - if service == nil { - t.Fatalf("could not convert service") - } - - sa := service.ServiceAccounts - if len(sa) != 0 { - t.Fatalf("number of service accounts is incorrect: %d, expected 0", len(sa)) - } -} - -func TestExternalServiceConversion(t *testing.T) { - serviceName := "service1" - namespace := "default" - - extSvc := coreV1.Service{ - ObjectMeta: metaV1.ObjectMeta{ - Name: serviceName, - Namespace: namespace, - }, - Spec: coreV1.ServiceSpec{ - Ports: []coreV1.ServicePort{ - { - Name: "http", - Port: 80, - Protocol: coreV1.ProtocolTCP, - }, - }, - Type: coreV1.ServiceTypeExternalName, - ExternalName: "google.com", - }, - } - - service := ConvertService(extSvc, domainSuffix, clusterID) - if service == nil { - t.Fatalf("could not convert external service") - } - - if len(service.Ports) != len(extSvc.Spec.Ports) { - t.Fatalf("incorrect number of ports => %v, want %v", - len(service.Ports), len(extSvc.Spec.Ports)) - } - - if !service.External() { - t.Fatal("service should be external") - } - - if service.Hostname != ServiceHostname(serviceName, namespace, domainSuffix) { - t.Fatalf("service hostname incorrect => %q, want %q", - service.Hostname, ServiceHostname(serviceName, namespace, domainSuffix)) - } -} - -func TestExternalClusterLocalServiceConversion(t *testing.T) { - serviceName := "service1" - namespace := "default" - - extSvc := coreV1.Service{ - ObjectMeta: metaV1.ObjectMeta{ - Name: serviceName, - Namespace: namespace, - }, - Spec: coreV1.ServiceSpec{ - Ports: []coreV1.ServicePort{ - { - Name: "http", - Port: 80, - Protocol: coreV1.ProtocolTCP, - }, - }, - Type: coreV1.ServiceTypeExternalName, - ExternalName: "some.test.svc.cluster.local", - }, - } - - domainSuffix := "cluster.local" - - service := ConvertService(extSvc, domainSuffix, clusterID) - if service == nil { - t.Fatalf("could not convert external service") - } - - if len(service.Ports) != len(extSvc.Spec.Ports) { - t.Fatalf("incorrect number of ports => %v, want %v", - len(service.Ports), len(extSvc.Spec.Ports)) - } - - if !service.External() { - t.Fatal("ExternalName service (even if .cluster.local) should be external") - } - - if service.Hostname != ServiceHostname(serviceName, namespace, domainSuffix) { - t.Fatalf("service hostname incorrect => %q, want %q", - service.Hostname, ServiceHostname(serviceName, namespace, domainSuffix)) - } -} - -func TestLBServiceConversion(t *testing.T) { - serviceName := "service1" - namespace := "default" - - addresses := []coreV1.LoadBalancerIngress{ - { - IP: "127.68.32.112", - }, - { - IP: "127.68.32.113", - }, - { - Hostname: "127.68.32.114", - }, - { - Hostname: "127.68.32.115", - }, - } - - extSvc := coreV1.Service{ - ObjectMeta: metaV1.ObjectMeta{ - Name: serviceName, - Namespace: namespace, - }, - Spec: coreV1.ServiceSpec{ - Ports: []coreV1.ServicePort{ - { - Name: "http", - Port: 80, - Protocol: coreV1.ProtocolTCP, - }, - }, - Type: coreV1.ServiceTypeLoadBalancer, - }, - Status: coreV1.ServiceStatus{ - LoadBalancer: coreV1.LoadBalancerStatus{ - Ingress: addresses, - }, - }, - } - - service := ConvertService(extSvc, domainSuffix, clusterID) - if service == nil { - t.Fatalf("could not convert external service") - } - - gotAddresses := service.Attributes.ClusterExternalAddresses.GetAddressesFor(clusterID) - if len(gotAddresses) == 0 { - t.Fatalf("no load balancer addresses found") - } - - for i, addr := range addresses { - var want string - if len(addr.IP) > 0 { - want = addr.IP - } else { - want = addr.Hostname - } - got := gotAddresses[i] - if got != want { - t.Fatalf("Expected address %s but got %s", want, got) - } - } -} - -func TestSecureNamingSAN(t *testing.T) { - pod := &coreV1.Pod{} - - pod.Annotations = make(map[string]string) - - ns := "anything" - sa := "foo" - pod.Namespace = ns - pod.Spec.ServiceAccountName = sa - - san := SecureNamingSAN(pod) - - expectedSAN := fmt.Sprintf("spiffe://%v/ns/%v/sa/%v", spiffe.GetTrustDomain(), ns, sa) - - if san != expectedSAN { - t.Fatalf("SAN match failed, SAN:%v expectedSAN:%v", san, expectedSAN) - } -} diff --git a/pilot/pkg/serviceregistry/kube/leak_test.go b/pilot/pkg/serviceregistry/kube/leak_test.go deleted file mode 100644 index 3d83f6726..000000000 --- a/pilot/pkg/serviceregistry/kube/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/serviceregistry/kube/testdata/cert.crt b/pilot/pkg/serviceregistry/kube/testdata/cert.crt deleted file mode 100644 index 6f75754b6..000000000 --- a/pilot/pkg/serviceregistry/kube/testdata/cert.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDeTCCAmGgAwIBAgIJAIsoa+W2cm1/MA0GCSqGSIb3DQEBCwUAMFMxCzAJBgNV -BAYTAlVTMRAwDgYDVQQIDAdTZWF0dGxlMRMwEQYDVQQKDApTdGFja3BvaW50MR0w -GwYDVQQDDBRzZWN1cmUuc3RhY2twb2ludC5pbzAeFw0xNzAzMTYxNzEwNTZaFw0x -NzA0MTUxNzEwNTZaMFMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdTZWF0dGxlMRMw -EQYDVQQKDApTdGFja3BvaW50MR0wGwYDVQQDDBRzZWN1cmUuc3RhY2twb2ludC5p -bzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK2DJwIeu2eRvXB1oCUr -gaMsAOXO0nXnIBGMlfDea75ShcOqEQUYP/cz+Jm3PBoJDuJEJhhin7IoYeBAlOW9 -tp5WYXyLtgmuLBRqk4fEVcNJh/x0x5h35Cgxi1LNegcbAqfVDPBs4uOheYKCXCk/ -QMWAKPfnnwEltJTbrywqMufP6Hzi47qCHMnC+E8SLfVry1sdW86BgnQq3/oZWxyH -m5+9wi3vZaTQJgpqoLzqa78WpYWhwgjXhzvOVPbglAsexhlGp0WcBQqc/COnBuEC -ZJPA+OTSPet1K0+vgLvYpdK/qoEGiUaPUnhaNDrDvLLCdiIvrf8YSDVbzx0gWhVf -fAECAwEAAaNQME4wHQYDVR0OBBYEFPD6y3GxXKCUzm728qtMZ/1XZ81SMB8GA1Ud -IwQYMBaAFPD6y3GxXKCUzm728qtMZ/1XZ81SMAwGA1UdEwQFMAMBAf8wDQYJKoZI -hvcNAQELBQADggEBABtdi6yKD685ioCnF/VgUIHwBgY5VZ8VJ5g8q/qWgvEGq2ym -qqoPpHqVFVPkm9UzWdJjeUS5SsOMA7Sw3jV7+dkstrtCIc9VEobRq3n2+Z/qExcn -mLnWcTi8WSujSOxDVChi/DoLPhLGmGGEHFuiUWPp3jPG8MiXJEiXek5X8UXEwHuD -9QbJserc8ZZkjjZD8h5d18zBLpLKH6I4NmjBjCYvNjazl8EEYNB/LbltuSmdOfYD -hc1CUdl1vWW/WD4ps6HEEPQsaiu2o37gaKxxjn8HiLzVI3xNVu3CLLlhVi1OwOhp -o5Ijut3z9/eioNNwVvmSbdCpzwWmgqBAjX0vygE= ------END CERTIFICATE----- diff --git a/pilot/pkg/serviceregistry/kube/testdata/cert.key b/pilot/pkg/serviceregistry/kube/testdata/cert.key deleted file mode 100644 index 735d81985..000000000 --- a/pilot/pkg/serviceregistry/kube/testdata/cert.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCtgycCHrtnkb1w -daAlK4GjLADlztJ15yARjJXw3mu+UoXDqhEFGD/3M/iZtzwaCQ7iRCYYYp+yKGHg -QJTlvbaeVmF8i7YJriwUapOHxFXDSYf8dMeYd+QoMYtSzXoHGwKn1QzwbOLjoXmC -glwpP0DFgCj3558BJbSU268sKjLnz+h84uO6ghzJwvhPEi31a8tbHVvOgYJ0Kt/6 -GVsch5ufvcIt72Wk0CYKaqC86mu/FqWFocII14c7zlT24JQLHsYZRqdFnAUKnPwj -pwbhAmSTwPjk0j3rdStPr4C72KXSv6qBBolGj1J4WjQ6w7yywnYiL63/GEg1W88d -IFoVX3wBAgMBAAECggEAdGOKchCdUv7e3SX505UYc5Tb0Utnv3DXAQ6VsanxCgye -774PhqRam/6/npTb+vbihgKCr07QopgV93A6sNUOP63f3MR2yo2LwfIvnh2kDcgw -MLdA/9RENnXtaBCu7z8+C8iM0mWn2FuDf3jkS0LDtveeA5TaAtIBDO1D1tNDrFye -ZzWrt6NKrifvvfXoTGqWMuW/YJnoLvmmAAE2hy0ic1XKDel+rGheoZkolYnN9bvx -tFE8qHBEj/l8KTkt7Skd8VwQBpbiyml7mM0/EoNX8Ips4dIyF5zfr23kP/kI5Stn -I6AQ2NjCC7+2pS7W/YlnyEI2nGNd8cPoO9HM5pX8AQKBgQDVnl4p1GkoW/THUS2i -PMelZrQaE0G93b8/pqdEcQFUn51uodf0mmdRv+D7frca17nEaL7CIGM/Mt/zGiIz -BiYc1baHObVLMWL2oyIdzQEbo7Zd/k2gWU8ARuBJvk9Q4mbP6lyhAjwIuynyGjYt -z18j5i+h15g3YMyTzsbdB4broQKBgQDP78u/gr7QO/c2K1iTC438oqJe2hl5kO4T -O/P7H5L2d3xnHYs5Q+cyEgpzcYlqxhO/S4+n9b/VXDXl4RGEmpiXx4sP/Nwu+j/N -1Eb5X4394ZptdCKt1K5VZz5EkLy9tJeOzan68V/GH2gLLEFLba2tefph0uYbM28s -lhNTHLO0YQKBgQCfWrGR8MO1eMukOHmbUk34pCHsZbsgISB/IhobY1WDxEJT+mW4 -McqrYEE8O23ql00cKKtM84414gTQPAmRGKAr2H1+aN1GR4Q0ysMzie+up7TubzuH -R7g4U6cTO+W9R/c8WO9aqOkR9lU3JriN+elWRWv5BnSlsw9Jn/IXhnruoQKBgQC9 -WOEyTT6wpEpi+2m2zQjIithMg1I7Tuxce3WasY/D+94+j7qdtOWsxJzbrwGxRxno -UkhIbBH5mfRCloa7N4PIgp5xOpLN/HdpWOogXxEPpQZYsS57GHZ/snoTObGFFhQE -p+cnafEKmsLFmdEDfKZwl+iWd9Ot5VJRSX/jaPmDQQKBgD52uG7ljk0Ivkwice/V -ycvv54TbndvSy1K5asia/d2jYPqQeJl9SF9S+qtQ+q5btGeIsFj/fvfbmyXy15Pi -hklQXW/IckivPDSGYEjwDT+j9me6kEomCesaU1k4Ht9lFl5qWyfTA6QoWzRObUB+ -0JRou6isycdVHKOy7I3fz5if ------END PRIVATE KEY----- diff --git a/pilot/pkg/serviceregistry/leak_test.go b/pilot/pkg/serviceregistry/leak_test.go deleted file mode 100644 index 0a5bc1950..000000000 --- a/pilot/pkg/serviceregistry/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package serviceregistry - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/serviceregistry/memory/discovery.go b/pilot/pkg/serviceregistry/memory/discovery.go deleted file mode 100644 index fa12bc3d3..000000000 --- a/pilot/pkg/serviceregistry/memory/discovery.go +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package memory - -import ( - "fmt" - "sync" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" -) - -// ServiceController is a mock service controller -type ServiceController struct { - svcHandlers []func(*model.Service, model.Event) - - sync.RWMutex -} - -var _ model.Controller = &ServiceController{} - -// Memory does not support workload handlers; everything is done in terms of instances -func (c *ServiceController) AppendWorkloadHandler(func(*model.WorkloadInstance, model.Event)) {} - -// AppendServiceHandler appends a service handler to the controller -func (c *ServiceController) AppendServiceHandler(f func(*model.Service, model.Event)) { - c.Lock() - c.svcHandlers = append(c.svcHandlers, f) - c.Unlock() -} - -// Run will run the controller -func (c *ServiceController) Run(<-chan struct{}) {} - -// HasSynced always returns true -func (c *ServiceController) HasSynced() bool { return true } - -// ServiceDiscovery is a mock discovery interface -type ServiceDiscovery struct { - services map[host.Name]*model.Service - - networkGateways []model.NetworkGateway - model.NetworkGatewaysHandler - - // EndpointShards table. Key is the fqdn of the service, ':', port - instancesByPortNum map[string][]*model.ServiceInstance - instancesByPortName map[string][]*model.ServiceInstance - - // Used by GetProxyServiceInstance, used to configure inbound (list of services per IP) - // We generally expect a single instance - conflicting services need to be reported. - ip2instance map[string][]*model.ServiceInstance - WantGetProxyServiceInstances []*model.ServiceInstance - InstancesError error - Controller model.Controller - ClusterID cluster.ID - - // Used by GetProxyWorkloadLabels - ip2workloadLabels map[string]labels.Instance - - // XDSUpdater will push EDS changes to the ADS model. - EDSUpdater model.XDSUpdater - - // Single mutex for now - it's for debug only. - mutex sync.Mutex -} - -var _ model.ServiceDiscovery = &ServiceDiscovery{} - -// NewServiceDiscovery builds an in-memory ServiceDiscovery -func NewServiceDiscovery(services ...*model.Service) *ServiceDiscovery { - svcs := map[host.Name]*model.Service{} - for _, svc := range services { - svcs[svc.Hostname] = svc - } - return &ServiceDiscovery{ - services: svcs, - Controller: &ServiceController{}, - instancesByPortNum: map[string][]*model.ServiceInstance{}, - instancesByPortName: map[string][]*model.ServiceInstance{}, - ip2instance: map[string][]*model.ServiceInstance{}, - ip2workloadLabels: map[string]labels.Instance{}, - } -} - -func (sd *ServiceDiscovery) shardKey() model.ShardKey { - return model.ShardKey{Cluster: sd.ClusterID, Provider: provider.Mock} -} - -func (sd *ServiceDiscovery) AddWorkload(ip string, labels labels.Instance) { - sd.ip2workloadLabels[ip] = labels -} - -// AddHTTPService is a helper to add a service of type http, named 'http-main', with the -// specified vip and port. -func (sd *ServiceDiscovery) AddHTTPService(name, vip string, port int) { - sd.AddService(&model.Service{ - Hostname: host.Name(name), - DefaultAddress: vip, - Ports: model.PortList{ - { - Name: "http-main", - Port: port, - Protocol: protocol.HTTP, - }, - }, - }) -} - -// AddService adds an in-memory service. -func (sd *ServiceDiscovery) AddService(svc *model.Service) { - sd.mutex.Lock() - svc.Attributes.ServiceRegistry = provider.Mock - sd.services[svc.Hostname] = svc - sd.mutex.Unlock() - // TODO: notify listeners -} - -// RemoveService removes an in-memory service. -func (sd *ServiceDiscovery) RemoveService(name host.Name) { - sd.mutex.Lock() - delete(sd.services, name) - sd.mutex.Unlock() - if sd.EDSUpdater != nil { - sd.EDSUpdater.SvcUpdate(sd.shardKey(), string(name), "", model.EventDelete) - } -} - -// AddInstance adds an in-memory instance. -func (sd *ServiceDiscovery) AddInstance(service host.Name, instance *model.ServiceInstance) { - // WIP: add enough code to allow tests and load tests to work - sd.mutex.Lock() - defer sd.mutex.Unlock() - svc := sd.services[service] - if svc == nil { - return - } - instance.Service = svc - sd.ip2instance[instance.Endpoint.Address] = append(sd.ip2instance[instance.Endpoint.Address], instance) - - key := fmt.Sprintf("%s:%d", service, instance.ServicePort.Port) - instanceList := sd.instancesByPortNum[key] - sd.instancesByPortNum[key] = append(instanceList, instance) - - key = fmt.Sprintf("%s:%s", service, instance.ServicePort.Name) - instanceList = sd.instancesByPortName[key] - sd.instancesByPortName[key] = append(instanceList, instance) -} - -// AddEndpoint adds an endpoint to a service. -func (sd *ServiceDiscovery) AddEndpoint(service host.Name, servicePortName string, servicePort int, address string, port int) *model.ServiceInstance { - instance := &model.ServiceInstance{ - Endpoint: &model.IstioEndpoint{ - Address: address, - ServicePortName: servicePortName, - EndpointPort: uint32(port), - }, - ServicePort: &model.Port{ - Name: servicePortName, - Port: servicePort, - Protocol: protocol.HTTP, - }, - } - sd.AddInstance(service, instance) - return instance -} - -// SetEndpoints update the list of endpoints for a service, similar with K8S controller. -func (sd *ServiceDiscovery) SetEndpoints(service string, namespace string, endpoints []*model.IstioEndpoint) { - sh := host.Name(service) - - sd.mutex.Lock() - svc := sd.services[sh] - if svc == nil { - sd.mutex.Unlock() - return - } - - // remove old entries - for k, v := range sd.ip2instance { - if len(v) > 0 && v[0].Service.Hostname == sh { - delete(sd.ip2instance, k) - } - } - for k, v := range sd.instancesByPortNum { - if len(v) > 0 && v[0].Service.Hostname == sh { - delete(sd.instancesByPortNum, k) - } - } - for k, v := range sd.instancesByPortName { - if len(v) > 0 && v[0].Service.Hostname == sh { - delete(sd.instancesByPortName, k) - } - } - - for _, e := range endpoints { - // servicePortName string, servicePort int, address string, port int - p, _ := svc.Ports.Get(e.ServicePortName) - - instance := &model.ServiceInstance{ - Service: svc, - ServicePort: &model.Port{ - Name: e.ServicePortName, - Port: p.Port, - Protocol: p.Protocol, - }, - Endpoint: e, - } - sd.ip2instance[instance.Endpoint.Address] = []*model.ServiceInstance{instance} - - key := fmt.Sprintf("%s:%d", service, instance.ServicePort.Port) - - instanceList := sd.instancesByPortNum[key] - sd.instancesByPortNum[key] = append(instanceList, instance) - - key = fmt.Sprintf("%s:%s", service, instance.ServicePort.Name) - instanceList = sd.instancesByPortName[key] - sd.instancesByPortName[key] = append(instanceList, instance) - - } - sd.mutex.Unlock() - sd.EDSUpdater.EDSUpdate(sd.shardKey(), service, namespace, endpoints) -} - -// Services implements discovery interface -// Each call to Services() should return a list of new *model.Service -func (sd *ServiceDiscovery) Services() []*model.Service { - sd.mutex.Lock() - defer sd.mutex.Unlock() - out := make([]*model.Service, 0, len(sd.services)) - for _, service := range sd.services { - out = append(out, service) - } - return out -} - -// GetService implements discovery interface -// Each call to GetService() should return a new *model.Service -func (sd *ServiceDiscovery) GetService(hostname host.Name) *model.Service { - sd.mutex.Lock() - defer sd.mutex.Unlock() - return sd.services[hostname] -} - -// InstancesByPort filters the service instances by labels. This assumes single port, as is -// used by EDS/ADS. -func (sd *ServiceDiscovery) InstancesByPort(svc *model.Service, port int, labels labels.Instance) []*model.ServiceInstance { - sd.mutex.Lock() - defer sd.mutex.Unlock() - if sd.InstancesError != nil { - return nil - } - key := fmt.Sprintf("%s:%d", string(svc.Hostname), port) - instances, ok := sd.instancesByPortNum[key] - if !ok { - return nil - } - return instances -} - -// GetProxyServiceInstances returns service instances associated with a node, resulting in -// 'in' services. -func (sd *ServiceDiscovery) GetProxyServiceInstances(node *model.Proxy) []*model.ServiceInstance { - sd.mutex.Lock() - defer sd.mutex.Unlock() - if sd.WantGetProxyServiceInstances != nil { - return sd.WantGetProxyServiceInstances - } - out := make([]*model.ServiceInstance, 0) - for _, ip := range node.IPAddresses { - si, found := sd.ip2instance[ip] - if found { - out = append(out, si...) - } - } - return out -} - -func (sd *ServiceDiscovery) GetProxyWorkloadLabels(proxy *model.Proxy) labels.Instance { - sd.mutex.Lock() - defer sd.mutex.Unlock() - - for _, ip := range proxy.IPAddresses { - if l, found := sd.ip2workloadLabels[ip]; found { - return l - } - } - return nil -} - -// GetIstioServiceAccounts gets the Istio service accounts for a service hostname. -func (sd *ServiceDiscovery) GetIstioServiceAccounts(svc *model.Service, _ []int) []string { - sd.mutex.Lock() - defer sd.mutex.Unlock() - for h, s := range sd.services { - if h == svc.Hostname { - return s.ServiceAccounts - } - } - return make([]string, 0) -} - -func (sd *ServiceDiscovery) AddGateways(gws ...model.NetworkGateway) { - sd.networkGateways = append(sd.networkGateways, gws...) - sd.NotifyGatewayHandlers() -} - -func (sd *ServiceDiscovery) NetworkGateways() []model.NetworkGateway { - return sd.networkGateways -} - -func (sd *ServiceDiscovery) MCSServices() []model.MCSServiceInfo { - return nil -} diff --git a/pilot/pkg/serviceregistry/mock/discovery.go b/pilot/pkg/serviceregistry/mock/discovery.go deleted file mode 100644 index 6402e6090..000000000 --- a/pilot/pkg/serviceregistry/mock/discovery.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mock - -import ( - "fmt" - "net" - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" -) - -// PortHTTPName is the HTTP port name -var PortHTTPName = "http" - -type ServiceArgs struct { - Hostname host.Name - Address string - ServiceAccounts []string - ClusterID cluster.ID -} - -func MakeServiceInstance(service *model.Service, port *model.Port, version int, locality model.Locality) *model.ServiceInstance { - if service.External() { - return nil - } - - // we make port 80 same as endpoint port, otherwise, it's distinct - target := port.Port - if target != 80 { - target += 1000 - } - - return &model.ServiceInstance{ - Endpoint: &model.IstioEndpoint{ - Address: MakeIP(service, version), - EndpointPort: uint32(target), - ServicePortName: port.Name, - Labels: map[string]string{"version": fmt.Sprintf("v%d", version)}, - Locality: locality, - }, - Service: service, - ServicePort: port, - } -} - -// MakeService creates a memory service -func MakeService(args ServiceArgs) *model.Service { - return &model.Service{ - CreationTime: time.Now(), - Hostname: args.Hostname, - ClusterVIPs: model.AddressMap{ - Addresses: map[cluster.ID][]string{args.ClusterID: {args.Address}}, - }, - DefaultAddress: args.Address, - ServiceAccounts: args.ServiceAccounts, - Ports: []*model.Port{ - { - Name: PortHTTPName, - Port: 80, // target port 80 - Protocol: protocol.HTTP, - }, { - Name: "http-status", - Port: 81, // target port 1081 - Protocol: protocol.HTTP, - }, { - Name: "custom", - Port: 90, // target port 1090 - Protocol: protocol.TCP, - }, { - Name: "mongo", - Port: 100, // target port 1100 - Protocol: protocol.Mongo, - }, { - Name: "redis", - Port: 110, // target port 1110 - Protocol: protocol.Redis, - }, { - Name: "mysql", - Port: 120, // target port 1120 - Protocol: protocol.MySQL, - }, - }, - } -} - -// MakeExternalHTTPService creates memory external service -func MakeExternalHTTPService(hostname host.Name, isMeshExternal bool, address string) *model.Service { - return &model.Service{ - CreationTime: time.Now(), - Hostname: hostname, - DefaultAddress: address, - MeshExternal: isMeshExternal, - Ports: []*model.Port{{ - Name: "http", - Port: 80, - Protocol: protocol.HTTP, - }}, - } -} - -// MakeExternalHTTPSService creates memory external service -func MakeExternalHTTPSService(hostname host.Name, isMeshExternal bool, address string) *model.Service { - return &model.Service{ - CreationTime: time.Now(), - Hostname: hostname, - DefaultAddress: address, - MeshExternal: isMeshExternal, - Ports: []*model.Port{{ - Name: "https", - Port: 443, - Protocol: protocol.HTTPS, - }}, - } -} - -// MakeIP creates a fake IP address for a service and instance version -func MakeIP(service *model.Service, version int) string { - // external services have no instances - if service.External() { - return "" - } - ip := net.ParseIP(service.DefaultAddress).To4() - ip[2] = byte(1) - ip[3] = byte(version) - return ip.String() -} - -type Controller struct { - serviceHandler model.ControllerHandlers -} - -func (c *Controller) AppendServiceHandler(f func(*model.Service, model.Event)) { - c.serviceHandler.AppendServiceHandler(f) -} - -func (c *Controller) AppendWorkloadHandler(func(*model.WorkloadInstance, model.Event)) {} - -func (c *Controller) Run(<-chan struct{}) {} - -func (c *Controller) HasSynced() bool { return true } - -func (c *Controller) OnServiceEvent(s *model.Service, e model.Event) { - for _, h := range c.serviceHandler.GetServiceHandlers() { - h(s, e) - } -} diff --git a/pilot/pkg/serviceregistry/mock/discovery_mock.go b/pilot/pkg/serviceregistry/mock/discovery_mock.go deleted file mode 100644 index 65778c8a0..000000000 --- a/pilot/pkg/serviceregistry/mock/discovery_mock.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mock - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" -) - -var ( - // HelloService is a mock service with `hello.default.svc.cluster.local` as - // a hostname and `10.1.0.0` for ip - HelloService = MakeService(ServiceArgs{ - Hostname: "hello.default.svc.cluster.local", - Address: "10.1.0.0", - ServiceAccounts: []string{}, - ClusterID: "cluster-1", - }) - - // ReplicatedFooServiceName is a service replicated in all clusters. - ReplicatedFooServiceName = host.Name("foo.default.svc.cluster.local") - ReplicatedFooServiceV1 = MakeService(ServiceArgs{ - Hostname: ReplicatedFooServiceName, - Address: "10.3.0.0", - ServiceAccounts: []string{ - "spiffe://cluster.local/ns/default/sa/foo1", - "spiffe://cluster.local/ns/default/sa/foo-share", - }, - ClusterID: "", - }) - ReplicatedFooServiceV2 = MakeService(ServiceArgs{ - Hostname: ReplicatedFooServiceName, - Address: "10.3.0.1", - ServiceAccounts: []string{ - "spiffe://cluster.local/ns/default/sa/foo2", - "spiffe://cluster.local/ns/default/sa/foo-share", - }, - ClusterID: "", - }) - - // WorldService is a mock service with `world.default.svc.cluster.local` as - // a hostname and `10.2.0.0` for ip - WorldService = MakeService(ServiceArgs{ - Hostname: "world.default.svc.cluster.local", - Address: "10.2.0.0", - ServiceAccounts: []string{ - "spiffe://cluster.local/ns/default/sa/world1", - "spiffe://cluster.local/ns/default/sa/world2", - }, - ClusterID: "cluster-2", - }) - - // ExtHTTPService is a mock external HTTP service - ExtHTTPService = MakeExternalHTTPService("httpbin.default.svc.cluster.local", - true, "") - - // ExtHTTPSService is a mock external HTTPS service - ExtHTTPSService = MakeExternalHTTPSService("httpsbin.default.svc.cluster.local", - true, "") - - // HelloInstanceV0 is a mock IP address for v0 of HelloService - HelloInstanceV0 = MakeIP(HelloService, 0) - - // HelloProxyV0 is a mock proxy v0 of HelloService - HelloProxyV0 = model.Proxy{ - Type: model.SidecarProxy, - IPAddresses: []string{HelloInstanceV0}, - ID: "v0.default", - DNSDomain: "default.svc.cluster.local", - IstioVersion: model.MaxIstioVersion, - Metadata: &model.NodeMetadata{}, - } -) diff --git a/pilot/pkg/serviceregistry/provider/providers.go b/pilot/pkg/serviceregistry/provider/providers.go deleted file mode 100644 index 7c38b85d4..000000000 --- a/pilot/pkg/serviceregistry/provider/providers.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package provider - -// ID defines underlying platform supporting service registry -type ID string - -const ( - // Mock is a service registry that contains 2 hard-coded test services - Mock ID = "Mock" - // Kubernetes is a service registry backed by k8s API server - Kubernetes ID = "Kubernetes" - // External is a service registry for externally provided ServiceEntries - External ID = "External" -) diff --git a/pilot/pkg/serviceregistry/serviceentry/conversion.go b/pilot/pkg/serviceregistry/serviceentry/conversion.go deleted file mode 100644 index fcce3d2a3..000000000 --- a/pilot/pkg/serviceregistry/serviceentry/conversion.go +++ /dev/null @@ -1,446 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package serviceentry - -import ( - "net" - "strings" - "time" -) - -import ( - "istio.io/api/label" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - labelutil "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/util/label" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/config/visibility" - "github.com/apache/dubbo-go-pixiu/pkg/kube/labels" - "github.com/apache/dubbo-go-pixiu/pkg/network" - "github.com/apache/dubbo-go-pixiu/pkg/spiffe" -) - -func convertPort(port *networking.Port) *model.Port { - return &model.Port{ - Name: port.Name, - Port: int(port.Number), - Protocol: protocol.Parse(port.Protocol), - } -} - -type HostAddress struct { - host string - address string -} - -// ServiceToServiceEntry converts from internal Service representation to ServiceEntry -// This does not include endpoints - they'll be represented as EndpointSlice or EDS. -// -// See convertServices() for the reverse conversion, used by Istio to handle ServiceEntry configs. -// See kube.ConvertService for the conversion from K8S to internal Service. -func ServiceToServiceEntry(svc *model.Service, proxy *model.Proxy) *config.Config { - gvk := gvk.ServiceEntry - se := &networking.ServiceEntry{ - // Host is fully qualified: name, namespace, domainSuffix - Hosts: []string{string(svc.Hostname)}, - - // Internal Service and K8S Service have a single Address. - // ServiceEntry can represent multiple - but we are not using that. SE may be merged. - // Will be 0.0.0.0 if not specified as ClusterIP or ClusterIP==None. In such case resolution is Passthrough. - // - Addresses: svc.GetAddresses(proxy), - - // Location: 0, - - // Internal resolution: - // - Passthrough - for ClusterIP=None and no ExternalName - // - ClientSideLB - regular ClusterIP clusters (VIP, resolved via EDS) - // - DNSLB - if ExternalName is specified. Also meshExternal is set. - - WorkloadSelector: &networking.WorkloadSelector{Labels: svc.Attributes.LabelSelectors}, - - // This is based on alpha.istio.io/canonical-serviceaccounts and - // alpha.istio.io/kubernetes-serviceaccounts. - SubjectAltNames: svc.ServiceAccounts, - } - - // Based on networking.istio.io/exportTo annotation - for k, v := range svc.Attributes.ExportTo { - if v { - // k is Private or Public - se.ExportTo = append(se.ExportTo, string(k)) - } - } - - if svc.MeshExternal { - se.Location = networking.ServiceEntry_MESH_EXTERNAL // 0 - default - } else { - se.Location = networking.ServiceEntry_MESH_INTERNAL - } - - // Reverse in convertServices. Note that enum values are different - // TODO: make the enum match, should be safe (as long as they're used as enum) - var resolution networking.ServiceEntry_Resolution - switch svc.Resolution { - case model.Passthrough: // 2 - resolution = networking.ServiceEntry_NONE // 0 - case model.DNSLB: // 1 - resolution = networking.ServiceEntry_DNS // 2 - case model.DNSRoundRobinLB: // 3 - resolution = networking.ServiceEntry_DNS_ROUND_ROBIN // 3 - case model.ClientSideLB: // 0 - resolution = networking.ServiceEntry_STATIC // 1 - } - se.Resolution = resolution - - // Port is mapped from ServicePort - for _, p := range svc.Ports { - se.Ports = append(se.Ports, &networking.Port{ - Number: uint32(p.Port), - Name: p.Name, - // Protocol is converted to protocol.Instance - reverse conversion will use the name. - Protocol: string(p.Protocol), - // TODO: target port - }) - } - - cfg := &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk, - Name: "synthetic-" + svc.Attributes.Name, - Namespace: svc.Attributes.Namespace, - CreationTimestamp: svc.CreationTime, - ResourceVersion: svc.ResourceVersion, - }, - Spec: se, - } - - // TODO: WorkloadSelector - - // TODO: preserve ServiceRegistry. The reverse conversion sets it to 'external' - // TODO: preserve UID ? It seems MCP didn't preserve it - but that code path was not used much. - - // TODO: ClusterExternalPorts map - for NodePort services, with "traffic.istio.io/nodeSelector" ann - // It's a per-cluster map - - // TODO: ClusterExternalAddresses - for LB types, per cluster. Populated from K8S, missing - // in SE. Used for multi-network support. - return cfg -} - -// convertServices transforms a ServiceEntry config to a list of internal Service objects. -func convertServices(cfg config.Config) []*model.Service { - serviceEntry := cfg.Spec.(*networking.ServiceEntry) - creationTime := cfg.CreationTimestamp - - var resolution model.Resolution - switch serviceEntry.Resolution { - case networking.ServiceEntry_NONE: - resolution = model.Passthrough - case networking.ServiceEntry_DNS: - resolution = model.DNSLB - case networking.ServiceEntry_DNS_ROUND_ROBIN: - resolution = model.DNSRoundRobinLB - case networking.ServiceEntry_STATIC: - resolution = model.ClientSideLB - } - - svcPorts := make(model.PortList, 0, len(serviceEntry.Ports)) - for _, port := range serviceEntry.Ports { - svcPorts = append(svcPorts, convertPort(port)) - } - - var exportTo map[visibility.Instance]bool - if len(serviceEntry.ExportTo) > 0 { - exportTo = make(map[visibility.Instance]bool) - for _, e := range serviceEntry.ExportTo { - exportTo[visibility.Instance(e)] = true - } - } - - var labelSelectors map[string]string - if serviceEntry.WorkloadSelector != nil { - labelSelectors = serviceEntry.WorkloadSelector.Labels - } - hostAddresses := []*HostAddress{} - for _, hostname := range serviceEntry.Hosts { - if len(serviceEntry.Addresses) > 0 { - for _, address := range serviceEntry.Addresses { - // Check if address is an IP first because that is the most common case. - if net.ParseIP(address) != nil { - hostAddresses = append(hostAddresses, &HostAddress{hostname, address}) - } else if ip, network, cidrErr := net.ParseCIDR(address); cidrErr == nil { - newAddress := address - ones, zeroes := network.Mask.Size() - if ones == zeroes { - // /32 mask. Remove the /32 and make it a normal IP address - newAddress = ip.String() - } - hostAddresses = append(hostAddresses, &HostAddress{hostname, newAddress}) - } - } - } else { - hostAddresses = append(hostAddresses, &HostAddress{hostname, constants.UnspecifiedIP}) - } - } - - return buildServices(hostAddresses, cfg.Name, cfg.Namespace, svcPorts, serviceEntry.Location, resolution, - exportTo, labelSelectors, serviceEntry.SubjectAltNames, creationTime, cfg.Labels) -} - -func buildServices(hostAddresses []*HostAddress, name, namespace string, ports model.PortList, location networking.ServiceEntry_Location, - resolution model.Resolution, exportTo map[visibility.Instance]bool, selectors map[string]string, saccounts []string, - ctime time.Time, labels map[string]string) []*model.Service { - out := make([]*model.Service, 0, len(hostAddresses)) - lbls := labels - if features.CanonicalServiceForMeshExternalServiceEntry && location == networking.ServiceEntry_MESH_EXTERNAL { - lbls = ensureCanonicalServiceLabels(name, labels) - } - for _, ha := range hostAddresses { - out = append(out, &model.Service{ - CreationTime: ctime, - MeshExternal: location == networking.ServiceEntry_MESH_EXTERNAL, - Hostname: host.Name(ha.host), - DefaultAddress: ha.address, - Ports: ports, - Resolution: resolution, - Attributes: model.ServiceAttributes{ - ServiceRegistry: provider.External, - Name: ha.host, - Namespace: namespace, - Labels: lbls, - ExportTo: exportTo, - LabelSelectors: selectors, - }, - ServiceAccounts: saccounts, - }) - } - return out -} - -func ensureCanonicalServiceLabels(name string, srcLabels map[string]string) map[string]string { - if srcLabels == nil { - srcLabels = make(map[string]string) - } - _, svcLabelFound := srcLabels[model.IstioCanonicalServiceLabelName] - _, revLabelFound := srcLabels[model.IstioCanonicalServiceRevisionLabelName] - if svcLabelFound && revLabelFound { - return srcLabels - } - - srcLabels[model.IstioCanonicalServiceLabelName], srcLabels[model.IstioCanonicalServiceRevisionLabelName] = labels.CanonicalService(srcLabels, name) - return srcLabels -} - -func (s *Controller) convertEndpoint(service *model.Service, servicePort *networking.Port, - wle *networking.WorkloadEntry, configKey *configKey, clusterID cluster.ID) *model.ServiceInstance { - var instancePort uint32 - addr := wle.GetAddress() - // priority level: unixAddress > we.ports > se.port.targetPort > se.port.number - if strings.HasPrefix(addr, model.UnixAddressPrefix) { - instancePort = 0 - addr = strings.TrimPrefix(addr, model.UnixAddressPrefix) - } else if port, ok := wle.Ports[servicePort.Name]; ok && port > 0 { - instancePort = port - } else if servicePort.TargetPort > 0 { - instancePort = servicePort.TargetPort - } else { - // final fallback is to the service port value - instancePort = servicePort.Number - } - - tlsMode := getTLSModeFromWorkloadEntry(wle) - sa := "" - if wle.ServiceAccount != "" { - sa = spiffe.MustGenSpiffeURI(service.Attributes.Namespace, wle.ServiceAccount) - } - networkID := s.workloadEntryNetwork(wle) - labels := labelutil.AugmentLabels(wle.Labels, clusterID, wle.Locality, networkID) - return &model.ServiceInstance{ - Endpoint: &model.IstioEndpoint{ - Address: addr, - EndpointPort: instancePort, - ServicePortName: servicePort.Name, - Network: network.ID(wle.Network), - Locality: model.Locality{ - Label: wle.Locality, - ClusterID: clusterID, - }, - LbWeight: wle.Weight, - Labels: labels, - TLSMode: tlsMode, - ServiceAccount: sa, - // Workload entry config name is used as workload name, which will appear in metric label. - // After VM auto registry is introduced, workload group annotation should be used for workload name. - WorkloadName: configKey.name, - Namespace: configKey.namespace, - }, - Service: service, - ServicePort: convertPort(servicePort), - } -} - -// convertWorkloadEntryToServiceInstances translates a WorkloadEntry into ServiceInstances. This logic is largely the -// same as the ServiceEntry convertServiceEntryToInstances. -func (s *Controller) convertWorkloadEntryToServiceInstances(wle *networking.WorkloadEntry, services []*model.Service, - se *networking.ServiceEntry, configKey *configKey, clusterID cluster.ID) []*model.ServiceInstance { - out := make([]*model.ServiceInstance, 0) - for _, service := range services { - for _, port := range se.Ports { - out = append(out, s.convertEndpoint(service, port, wle, configKey, clusterID)) - } - } - return out -} - -func (s *Controller) convertServiceEntryToInstances(cfg config.Config, services []*model.Service) []*model.ServiceInstance { - out := make([]*model.ServiceInstance, 0) - serviceEntry := cfg.Spec.(*networking.ServiceEntry) - if serviceEntry == nil { - return nil - } - if services == nil { - services = convertServices(cfg) - } - for _, service := range services { - for _, serviceEntryPort := range serviceEntry.Ports { - if len(serviceEntry.Endpoints) == 0 && serviceEntry.WorkloadSelector == nil && - (serviceEntry.Resolution == networking.ServiceEntry_DNS || serviceEntry.Resolution == networking.ServiceEntry_DNS_ROUND_ROBIN) { - // Note: only convert the hostname to service instance if WorkloadSelector is not set - // when service entry has discovery type DNS and no endpoints - // we create endpoints from service's host - // Do not use serviceentry.hosts as a service entry is converted into - // multiple services (one for each host) - endpointPort := serviceEntryPort.Number - if serviceEntryPort.TargetPort > 0 { - endpointPort = serviceEntryPort.TargetPort - } - out = append(out, &model.ServiceInstance{ - Endpoint: &model.IstioEndpoint{ - Address: string(service.Hostname), - EndpointPort: endpointPort, - ServicePortName: serviceEntryPort.Name, - Labels: nil, - TLSMode: model.DisabledTLSModeLabel, - }, - Service: service, - ServicePort: convertPort(serviceEntryPort), - }) - } else { - for _, endpoint := range serviceEntry.Endpoints { - out = append(out, s.convertEndpoint(service, serviceEntryPort, endpoint, &configKey{}, s.clusterID)) - } - } - } - } - return out -} - -func getTLSModeFromWorkloadEntry(wle *networking.WorkloadEntry) string { - // * Use security.istio.io/tlsMode if its present - // * If not, set TLS mode if ServiceAccount is specified - tlsMode := model.DisabledTLSModeLabel - if val, exists := wle.Labels[label.SecurityTlsMode.Name]; exists { - tlsMode = val - } else if wle.ServiceAccount != "" { - tlsMode = model.IstioMutualTLSModeLabel - } - - return tlsMode -} - -// The workload instance has pointer to the service and its service port. -// We need to create our own but we can retain the endpoint already created. -func convertWorkloadInstanceToServiceInstance(workloadInstance *model.IstioEndpoint, serviceEntryServices []*model.Service, - serviceEntry *networking.ServiceEntry) []*model.ServiceInstance { - out := make([]*model.ServiceInstance, 0) - for _, service := range serviceEntryServices { - for _, serviceEntryPort := range serviceEntry.Ports { - ep := *workloadInstance - ep.ServicePortName = serviceEntryPort.Name - // if target port is set, use the target port else fallback to the service port - // TODO: we need a way to get the container port map from k8s - if serviceEntryPort.TargetPort > 0 { - ep.EndpointPort = serviceEntryPort.TargetPort - } else { - ep.EndpointPort = serviceEntryPort.Number - } - ep.EnvoyEndpoint = nil - out = append(out, &model.ServiceInstance{ - Endpoint: &ep, - Service: service, - ServicePort: convertPort(serviceEntryPort), - }) - } - } - return out -} - -// Convenience function to convert a workloadEntry into a WorkloadInstance object encoding the endpoint (without service -// port names) and the namespace - k8s will consume this workload instance when selecting workload entries -func (s *Controller) convertWorkloadEntryToWorkloadInstance(cfg config.Config, clusterID cluster.ID) *model.WorkloadInstance { - we := convertWorkloadEntry(cfg) - addr := we.GetAddress() - dnsServiceEntryOnly := false - if strings.HasPrefix(addr, model.UnixAddressPrefix) { - // k8s can't use uds for service objects - dnsServiceEntryOnly = true - } - if net.ParseIP(addr) == nil { - // k8s can't use workloads with hostnames in the address field. - dnsServiceEntryOnly = true - } - tlsMode := getTLSModeFromWorkloadEntry(we) - sa := "" - if we.ServiceAccount != "" { - sa = spiffe.MustGenSpiffeURI(cfg.Namespace, we.ServiceAccount) - } - networkID := s.workloadEntryNetwork(we) - labels := labelutil.AugmentLabels(we.Labels, clusterID, we.Locality, networkID) - return &model.WorkloadInstance{ - Endpoint: &model.IstioEndpoint{ - Address: addr, - // Not setting ports here as its done by k8s controller - Network: network.ID(we.Network), - Locality: model.Locality{ - Label: we.Locality, - ClusterID: clusterID, - }, - LbWeight: we.Weight, - Namespace: cfg.Namespace, - // Workload entry config name is used as workload name, which will appear in metric label. - // After VM auto registry is introduced, workload group annotation should be used for workload name. - WorkloadName: cfg.Name, - Labels: labels, - TLSMode: tlsMode, - ServiceAccount: sa, - }, - PortMap: we.Ports, - Namespace: cfg.Namespace, - Name: cfg.Name, - Kind: model.WorkloadEntryKind, - DNSServiceEntryOnly: dnsServiceEntryOnly, - } -} diff --git a/pilot/pkg/serviceregistry/serviceentry/conversion_test.go b/pilot/pkg/serviceregistry/serviceentry/conversion_test.go deleted file mode 100644 index 5dbb20d4d..000000000 --- a/pilot/pkg/serviceregistry/serviceentry/conversion_test.go +++ /dev/null @@ -1,1245 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package serviceentry - -import ( - "encoding/json" - "strings" - "testing" - "time" -) - -import ( - "istio.io/api/label" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - labelutil "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/util/label" - "github.com/apache/dubbo-go-pixiu/pilot/test/util" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/network" - "github.com/apache/dubbo-go-pixiu/pkg/spiffe" - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -var ( - GlobalTime = time.Now() - httpNone = &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.ServiceEntry, - Name: "httpNone", - Namespace: "httpNone", - Domain: "svc.cluster.local", - CreationTimestamp: GlobalTime, - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"*.google.com"}, - Ports: []*networking.Port{ - {Number: 80, Name: "http-number", Protocol: "http"}, - {Number: 8080, Name: "http2-number", Protocol: "http2"}, - }, - Location: networking.ServiceEntry_MESH_EXTERNAL, - Resolution: networking.ServiceEntry_NONE, - }, - } -) - -var tcpNone = &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.ServiceEntry, - Name: "tcpNone", - Namespace: "tcpNone", - CreationTimestamp: GlobalTime, - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"tcpnone.com"}, - Addresses: []string{"172.217.0.0/16"}, - Ports: []*networking.Port{ - {Number: 444, Name: "tcp-444", Protocol: "tcp"}, - }, - Location: networking.ServiceEntry_MESH_EXTERNAL, - Resolution: networking.ServiceEntry_NONE, - }, -} - -var httpStatic = &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.ServiceEntry, - Name: "httpStatic", - Namespace: "httpStatic", - CreationTimestamp: GlobalTime, - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"*.google.com"}, - Ports: []*networking.Port{ - {Number: 80, Name: "http-port", Protocol: "http"}, - {Number: 8080, Name: "http-alt-port", Protocol: "http"}, - }, - Endpoints: []*networking.WorkloadEntry{ - { - Address: "2.2.2.2", - Ports: map[string]uint32{"http-port": 7080, "http-alt-port": 18080}, - Labels: map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}, - }, - { - Address: "3.3.3.3", - Ports: map[string]uint32{"http-port": 1080}, - Labels: map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}, - }, - { - Address: "4.4.4.4", - Ports: map[string]uint32{"http-port": 1080}, - Labels: map[string]string{"foo": "bar"}, - }, - }, - Location: networking.ServiceEntry_MESH_EXTERNAL, - Resolution: networking.ServiceEntry_STATIC, - }, -} - -// Shares the same host as httpStatic, but adds some endpoints. We expect these to be merge -var httpStaticOverlay = &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.ServiceEntry, - Name: "httpStaticOverlay", - Namespace: "httpStatic", - CreationTimestamp: GlobalTime, - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"*.google.com"}, - Ports: []*networking.Port{ - {Number: 4567, Name: "http-port", Protocol: "http"}, - }, - Endpoints: []*networking.WorkloadEntry{ - { - Address: "5.5.5.5", - Labels: map[string]string{"overlay": "bar"}, - }, - }, - Location: networking.ServiceEntry_MESH_EXTERNAL, - Resolution: networking.ServiceEntry_STATIC, - }, -} - -var httpDNSnoEndpoints = &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.ServiceEntry, - Name: "httpDNSnoEndpoints", - Namespace: "httpDNSnoEndpoints", - CreationTimestamp: GlobalTime, - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"google.com", "www.wikipedia.org"}, - Ports: []*networking.Port{ - {Number: 80, Name: "http-port", Protocol: "http"}, - {Number: 8080, Name: "http-alt-port", Protocol: "http"}, - }, - Location: networking.ServiceEntry_MESH_EXTERNAL, - Resolution: networking.ServiceEntry_DNS, - SubjectAltNames: []string{"google.com"}, - }, -} - -var httpDNSRRnoEndpoints = &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.ServiceEntry, - Name: "httpDNSRRnoEndpoints", - Namespace: "httpDNSRRnoEndpoints", - CreationTimestamp: GlobalTime, - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"api.istio.io"}, - Ports: []*networking.Port{ - {Number: 80, Name: "http-port", Protocol: "http"}, - {Number: 8080, Name: "http-alt-port", Protocol: "http"}, - }, - Location: networking.ServiceEntry_MESH_EXTERNAL, - Resolution: networking.ServiceEntry_DNS_ROUND_ROBIN, - SubjectAltNames: []string{"api.istio.io"}, - }, -} - -var dnsTargetPort = &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.ServiceEntry, - Name: "dnsTargetPort", - Namespace: "dnsTargetPort", - CreationTimestamp: GlobalTime, - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"google.com"}, - Ports: []*networking.Port{ - {Number: 80, Name: "http-port", Protocol: "http", TargetPort: 8080}, - }, - Location: networking.ServiceEntry_MESH_EXTERNAL, - Resolution: networking.ServiceEntry_DNS, - }, -} - -var httpDNS = &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.ServiceEntry, - Name: "httpDNS", - Namespace: "httpDNS", - CreationTimestamp: GlobalTime, - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"*.google.com"}, - Ports: []*networking.Port{ - {Number: 80, Name: "http-port", Protocol: "http"}, - {Number: 8080, Name: "http-alt-port", Protocol: "http"}, - }, - Endpoints: []*networking.WorkloadEntry{ - { - Address: "us.google.com", - Ports: map[string]uint32{"http-port": 7080, "http-alt-port": 18080}, - Labels: map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}, - }, - { - Address: "uk.google.com", - Ports: map[string]uint32{"http-port": 1080}, - Labels: map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}, - }, - { - Address: "de.google.com", - Labels: map[string]string{"foo": "bar", label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}, - }, - }, - Location: networking.ServiceEntry_MESH_EXTERNAL, - Resolution: networking.ServiceEntry_DNS, - }, -} - -var httpDNSRR = &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.ServiceEntry, - Name: "httpDNSRR", - Namespace: "httpDNSRR", - CreationTimestamp: GlobalTime, - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"*.istio.io"}, - Ports: []*networking.Port{ - {Number: 80, Name: "http-port", Protocol: "http"}, - {Number: 8080, Name: "http-alt-port", Protocol: "http"}, - }, - Endpoints: []*networking.WorkloadEntry{ - { - Address: "api-v1.istio.io", - Ports: map[string]uint32{"http-port": 7080, "http-alt-port": 18080}, - Labels: map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}, - }, - { - Address: "api-v2.istio.io", - Ports: map[string]uint32{"http-port": 1080}, - Labels: map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}, - }, - { - Address: "api-v3.istio.io", - Labels: map[string]string{"foo": "bar", label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}, - }, - }, - Location: networking.ServiceEntry_MESH_EXTERNAL, - Resolution: networking.ServiceEntry_DNS_ROUND_ROBIN, - }, -} - -var tcpDNS = &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.ServiceEntry, - Name: "tcpDNS", - Namespace: "tcpDNS", - CreationTimestamp: GlobalTime, - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"tcpdns.com"}, - Ports: []*networking.Port{ - {Number: 444, Name: "tcp-444", Protocol: "tcp"}, - }, - Endpoints: []*networking.WorkloadEntry{ - { - Address: "lon.google.com", - Labels: map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}, - }, - { - Address: "in.google.com", - Labels: map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}, - }, - }, - Location: networking.ServiceEntry_MESH_EXTERNAL, - Resolution: networking.ServiceEntry_DNS, - }, -} - -var tcpStatic = &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.ServiceEntry, - Name: "tcpStatic", - Namespace: "tcpStatic", - CreationTimestamp: GlobalTime, - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"tcpstatic.com"}, - Addresses: []string{"172.217.0.1"}, - Ports: []*networking.Port{ - {Number: 444, Name: "tcp-444", Protocol: "tcp"}, - }, - Endpoints: []*networking.WorkloadEntry{ - { - Address: "1.1.1.1", - Labels: map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}, - }, - { - Address: "2.2.2.2", - Labels: map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}, - }, - }, - Location: networking.ServiceEntry_MESH_EXTERNAL, - Resolution: networking.ServiceEntry_STATIC, - }, -} - -var httpNoneInternal = &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.ServiceEntry, - Name: "httpNoneInternal", - Namespace: "httpNoneInternal", - CreationTimestamp: GlobalTime, - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"*.google.com"}, - Ports: []*networking.Port{ - {Number: 80, Name: "http-number", Protocol: "http"}, - {Number: 8080, Name: "http2-number", Protocol: "http2"}, - }, - Location: networking.ServiceEntry_MESH_INTERNAL, - Resolution: networking.ServiceEntry_NONE, - }, -} - -var tcpNoneInternal = &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.ServiceEntry, - Name: "tcpNoneInternal", - Namespace: "tcpNoneInternal", - CreationTimestamp: GlobalTime, - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"tcpinternal.com"}, - Addresses: []string{"172.217.0.0/16"}, - Ports: []*networking.Port{ - {Number: 444, Name: "tcp-444", Protocol: "tcp"}, - }, - Location: networking.ServiceEntry_MESH_INTERNAL, - Resolution: networking.ServiceEntry_NONE, - }, -} - -var multiAddrInternal = &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.ServiceEntry, - Name: "multiAddrInternal", - Namespace: "multiAddrInternal", - CreationTimestamp: GlobalTime, - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"tcp1.com", "tcp2.com"}, - Addresses: []string{"1.1.1.0/16", "2.2.2.0/16"}, - Ports: []*networking.Port{ - {Number: 444, Name: "tcp-444", Protocol: "tcp"}, - }, - Location: networking.ServiceEntry_MESH_INTERNAL, - Resolution: networking.ServiceEntry_NONE, - }, -} - -var udsLocal = &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.ServiceEntry, - Name: "udsLocal", - Namespace: "udsLocal", - CreationTimestamp: GlobalTime, - Labels: map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}, - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"uds.cluster.local"}, - Ports: []*networking.Port{ - {Number: 6553, Name: "grpc-1", Protocol: "grpc"}, - }, - Endpoints: []*networking.WorkloadEntry{ - {Address: "unix:///test/sock", Labels: map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}}, - }, - Resolution: networking.ServiceEntry_STATIC, - }, -} - -// ServiceEntry DNS with a selector -var selectorDNS = &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.ServiceEntry, - Name: "selector", - Namespace: "selector", - CreationTimestamp: GlobalTime, - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"selector.com"}, - Ports: []*networking.Port{ - {Number: 444, Name: "tcp-444", Protocol: "tcp"}, - {Number: 445, Name: "http-445", Protocol: "http"}, - }, - WorkloadSelector: &networking.WorkloadSelector{ - Labels: map[string]string{"app": "wle"}, - }, - Resolution: networking.ServiceEntry_DNS, - }, -} - -// ServiceEntry with a selector -var selector = &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.ServiceEntry, - Name: "selector", - Namespace: "selector", - CreationTimestamp: GlobalTime, - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"selector.com"}, - Ports: []*networking.Port{ - {Number: 444, Name: "tcp-444", Protocol: "tcp"}, - {Number: 445, Name: "http-445", Protocol: "http"}, - }, - WorkloadSelector: &networking.WorkloadSelector{ - Labels: map[string]string{"app": "wle"}, - }, - Resolution: networking.ServiceEntry_STATIC, - }, -} - -// DNS ServiceEntry with a selector -var dnsSelector = &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.ServiceEntry, - Name: "dns-selector", - Namespace: "dns-selector", - CreationTimestamp: GlobalTime, - Labels: map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}, - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"dns.selector.com"}, - Ports: []*networking.Port{ - {Number: 444, Name: "tcp-444", Protocol: "tcp"}, - {Number: 445, Name: "http-445", Protocol: "http"}, - }, - WorkloadSelector: &networking.WorkloadSelector{ - Labels: map[string]string{"app": "dns-wle"}, - }, - Resolution: networking.ServiceEntry_DNS, - }, -} - -func createWorkloadEntry(name, namespace string, spec *networking.WorkloadEntry) *config.Config { - return &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.WorkloadEntry, - Name: name, - Namespace: namespace, - CreationTimestamp: GlobalTime, - }, - Spec: spec, - } -} - -func convertPortNameToProtocol(name string) protocol.Instance { - prefix := name - i := strings.Index(name, "-") - if i >= 0 { - prefix = name[:i] - } - return protocol.Parse(prefix) -} - -func makeService(hostname host.Name, configNamespace, address string, ports map[string]int, - external bool, resolution model.Resolution, serviceAccounts ...string) *model.Service { - svc := &model.Service{ - CreationTime: GlobalTime, - Hostname: hostname, - DefaultAddress: address, - MeshExternal: external, - Resolution: resolution, - ServiceAccounts: serviceAccounts, - Attributes: model.ServiceAttributes{ - ServiceRegistry: provider.External, - Name: string(hostname), - Namespace: configNamespace, - }, - } - - if external && features.CanonicalServiceForMeshExternalServiceEntry { - if svc.Attributes.Labels == nil { - svc.Attributes.Labels = make(map[string]string) - } - svc.Attributes.Labels["service.istio.io/canonical-name"] = configNamespace - svc.Attributes.Labels["service.istio.io/canonical-revision"] = "latest" - } - - svcPorts := make(model.PortList, 0, len(ports)) - for name, port := range ports { - svcPort := &model.Port{ - Name: name, - Port: port, - Protocol: convertPortNameToProtocol(name), - } - svcPorts = append(svcPorts, svcPort) - } - - sortPorts(svcPorts) - svc.Ports = svcPorts - return svc -} - -// MTLSMode is the expected instance mtls settings. This is for test setup only -type MTLSMode int - -const ( - MTLS = iota - // Like mTLS, but no label is defined on the endpoint - MTLSUnlabelled - PlainText -) - -// nolint: unparam -func makeInstanceWithServiceAccount(cfg *config.Config, address string, port int, - svcPort *networking.Port, svcLabels map[string]string, serviceAccount string) *model.ServiceInstance { - i := makeInstance(cfg, address, port, svcPort, svcLabels, MTLSUnlabelled) - i.Endpoint.ServiceAccount = spiffe.MustGenSpiffeURI(i.Service.Attributes.Namespace, serviceAccount) - return i -} - -// nolint: unparam -func makeInstance(cfg *config.Config, address string, port int, - svcPort *networking.Port, svcLabels map[string]string, mtlsMode MTLSMode) *model.ServiceInstance { - services := convertServices(*cfg) - svc := services[0] // default - for _, s := range services { - if string(s.Hostname) == address { - svc = s - break - } - } - tlsMode := model.DisabledTLSModeLabel - if mtlsMode == MTLS || mtlsMode == MTLSUnlabelled { - tlsMode = model.IstioMutualTLSModeLabel - } - if mtlsMode == MTLS { - if svcLabels == nil { - svcLabels = map[string]string{} - } - svcLabels[label.SecurityTlsMode.Name] = model.IstioMutualTLSModeLabel - } - return &model.ServiceInstance{ - Service: svc, - Endpoint: &model.IstioEndpoint{ - Address: address, - EndpointPort: uint32(port), - ServicePortName: svcPort.Name, - Labels: svcLabels, - TLSMode: tlsMode, - }, - ServicePort: &model.Port{ - Name: svcPort.Name, - Port: int(svcPort.Number), - Protocol: protocol.Parse(svcPort.Protocol), - }, - } -} - -func TestConvertService(t *testing.T) { - testConvertServiceBody(t) - test.SetBoolForTest(t, &features.CanonicalServiceForMeshExternalServiceEntry, true) - testConvertServiceBody(t) -} - -func testConvertServiceBody(t *testing.T) { - t.Helper() - - serviceTests := []struct { - externalSvc *config.Config - services []*model.Service - }{ - { - // service entry http - externalSvc: httpNone, - services: []*model.Service{ - makeService("*.google.com", "httpNone", constants.UnspecifiedIP, - map[string]int{"http-number": 80, "http2-number": 8080}, true, model.Passthrough), - }, - }, - { - // service entry tcp - externalSvc: tcpNone, - services: []*model.Service{ - makeService("tcpnone.com", "tcpNone", "172.217.0.0/16", - map[string]int{"tcp-444": 444}, true, model.Passthrough), - }, - }, - { - // service entry http static - externalSvc: httpStatic, - services: []*model.Service{ - makeService("*.google.com", "httpStatic", constants.UnspecifiedIP, - map[string]int{"http-port": 80, "http-alt-port": 8080}, true, model.ClientSideLB), - }, - }, - { - // service entry DNS with no endpoints - externalSvc: httpDNSnoEndpoints, - services: []*model.Service{ - makeService("google.com", "httpDNSnoEndpoints", constants.UnspecifiedIP, - map[string]int{"http-port": 80, "http-alt-port": 8080}, true, model.DNSLB, "google.com"), - makeService("www.wikipedia.org", "httpDNSnoEndpoints", constants.UnspecifiedIP, - map[string]int{"http-port": 80, "http-alt-port": 8080}, true, model.DNSLB, "google.com"), - }, - }, - { - // service entry dns - externalSvc: httpDNS, - services: []*model.Service{ - makeService("*.google.com", "httpDNS", constants.UnspecifiedIP, - map[string]int{"http-port": 80, "http-alt-port": 8080}, true, model.DNSLB), - }, - }, - { - // service entry dns with target port - externalSvc: dnsTargetPort, - services: []*model.Service{ - makeService("google.com", "dnsTargetPort", constants.UnspecifiedIP, - map[string]int{"http-port": 80}, true, model.DNSLB), - }, - }, - { - // service entry tcp DNS - externalSvc: tcpDNS, - services: []*model.Service{ - makeService("tcpdns.com", "tcpDNS", constants.UnspecifiedIP, - map[string]int{"tcp-444": 444}, true, model.DNSLB), - }, - }, - { - // service entry tcp static - externalSvc: tcpStatic, - services: []*model.Service{ - makeService("tcpstatic.com", "tcpStatic", "172.217.0.1", - map[string]int{"tcp-444": 444}, true, model.ClientSideLB), - }, - }, - { - // service entry http internal - externalSvc: httpNoneInternal, - services: []*model.Service{ - makeService("*.google.com", "httpNoneInternal", constants.UnspecifiedIP, - map[string]int{"http-number": 80, "http2-number": 8080}, false, model.Passthrough), - }, - }, - { - // service entry tcp internal - externalSvc: tcpNoneInternal, - services: []*model.Service{ - makeService("tcpinternal.com", "tcpNoneInternal", "172.217.0.0/16", - map[string]int{"tcp-444": 444}, false, model.Passthrough), - }, - }, - { - // service entry multiAddrInternal - externalSvc: multiAddrInternal, - services: []*model.Service{ - makeService("tcp1.com", "multiAddrInternal", "1.1.1.0/16", - map[string]int{"tcp-444": 444}, false, model.Passthrough), - makeService("tcp1.com", "multiAddrInternal", "2.2.2.0/16", - map[string]int{"tcp-444": 444}, false, model.Passthrough), - makeService("tcp2.com", "multiAddrInternal", "1.1.1.0/16", - map[string]int{"tcp-444": 444}, false, model.Passthrough), - makeService("tcp2.com", "multiAddrInternal", "2.2.2.0/16", - map[string]int{"tcp-444": 444}, false, model.Passthrough), - }, - }, - } - - selectorSvc := makeService("selector.com", "selector", "0.0.0.0", - map[string]int{"tcp-444": 444, "http-445": 445}, true, model.ClientSideLB) - selectorSvc.Attributes.LabelSelectors = map[string]string{"app": "wle"} - - serviceTests = append(serviceTests, struct { - externalSvc *config.Config - services []*model.Service - }{ - externalSvc: selector, - services: []*model.Service{selectorSvc}, - }) - - for _, tt := range serviceTests { - services := convertServices(*tt.externalSvc) - if err := compare(t, services, tt.services); err != nil { - t.Errorf("testcase: %v\n%v ", tt.externalSvc.Name, err) - } - } -} - -func TestConvertInstances(t *testing.T) { - serviceInstanceTests := []struct { - externalSvc *config.Config - out []*model.ServiceInstance - }{ - { - // single instance with multiple ports - externalSvc: httpNone, - // DNS type none means service should not have a registered instance - out: []*model.ServiceInstance{}, - }, - { - // service entry tcp - externalSvc: tcpNone, - // DNS type none means service should not have a registered instance - out: []*model.ServiceInstance{}, - }, - { - // service entry static - externalSvc: httpStatic, - out: []*model.ServiceInstance{ - makeInstance(httpStatic, "2.2.2.2", 7080, httpStatic.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS), - makeInstance(httpStatic, "2.2.2.2", 18080, httpStatic.Spec.(*networking.ServiceEntry).Ports[1], nil, MTLS), - makeInstance(httpStatic, "3.3.3.3", 1080, httpStatic.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS), - makeInstance(httpStatic, "3.3.3.3", 8080, httpStatic.Spec.(*networking.ServiceEntry).Ports[1], nil, MTLS), - makeInstance(httpStatic, "4.4.4.4", 1080, httpStatic.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"foo": "bar"}, PlainText), - makeInstance(httpStatic, "4.4.4.4", 8080, httpStatic.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"foo": "bar"}, PlainText), - }, - }, - { - // service entry DNS with no endpoints - externalSvc: httpDNSnoEndpoints, - out: []*model.ServiceInstance{ - makeInstance(httpDNSnoEndpoints, "google.com", 80, httpDNSnoEndpoints.Spec.(*networking.ServiceEntry).Ports[0], nil, PlainText), - makeInstance(httpDNSnoEndpoints, "google.com", 8080, httpDNSnoEndpoints.Spec.(*networking.ServiceEntry).Ports[1], nil, PlainText), - makeInstance(httpDNSnoEndpoints, "www.wikipedia.org", 80, httpDNSnoEndpoints.Spec.(*networking.ServiceEntry).Ports[0], nil, PlainText), - makeInstance(httpDNSnoEndpoints, "www.wikipedia.org", 8080, httpDNSnoEndpoints.Spec.(*networking.ServiceEntry).Ports[1], nil, PlainText), - }, - }, - { - // service entry DNS with no endpoints using round robin - externalSvc: httpDNSRRnoEndpoints, - out: []*model.ServiceInstance{ - makeInstance(httpDNSRRnoEndpoints, "api.istio.io", 80, httpDNSnoEndpoints.Spec.(*networking.ServiceEntry).Ports[0], nil, PlainText), - makeInstance(httpDNSRRnoEndpoints, "api.istio.io", 8080, httpDNSnoEndpoints.Spec.(*networking.ServiceEntry).Ports[1], nil, PlainText), - }, - }, - { - // service entry DNS with workload selector and no endpoints - externalSvc: selectorDNS, - out: []*model.ServiceInstance{}, - }, - { - // service entry dns - externalSvc: httpDNS, - out: []*model.ServiceInstance{ - makeInstance(httpDNS, "us.google.com", 7080, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS), - makeInstance(httpDNS, "us.google.com", 18080, httpDNS.Spec.(*networking.ServiceEntry).Ports[1], nil, MTLS), - makeInstance(httpDNS, "uk.google.com", 1080, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS), - makeInstance(httpDNS, "uk.google.com", 8080, httpDNS.Spec.(*networking.ServiceEntry).Ports[1], nil, MTLS), - makeInstance(httpDNS, "de.google.com", 80, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"foo": "bar"}, MTLS), - makeInstance(httpDNS, "de.google.com", 8080, httpDNS.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"foo": "bar"}, MTLS), - }, - }, - { - // service entry dns with target port - externalSvc: dnsTargetPort, - out: []*model.ServiceInstance{ - makeInstance(dnsTargetPort, "google.com", 8080, dnsTargetPort.Spec.(*networking.ServiceEntry).Ports[0], nil, PlainText), - }, - }, - { - // service entry tcp DNS - externalSvc: tcpDNS, - out: []*model.ServiceInstance{ - makeInstance(tcpDNS, "lon.google.com", 444, tcpDNS.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS), - makeInstance(tcpDNS, "in.google.com", 444, tcpDNS.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS), - }, - }, - { - // service entry tcp static - externalSvc: tcpStatic, - out: []*model.ServiceInstance{ - makeInstance(tcpStatic, "1.1.1.1", 444, tcpStatic.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS), - makeInstance(tcpStatic, "2.2.2.2", 444, tcpStatic.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS), - }, - }, - { - // service entry unix domain socket static - externalSvc: udsLocal, - out: []*model.ServiceInstance{ - makeInstance(udsLocal, "/test/sock", 0, udsLocal.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS), - }, - }, - } - - for _, tt := range serviceInstanceTests { - t.Run(strings.Join(tt.externalSvc.Spec.(*networking.ServiceEntry).Hosts, "_"), func(t *testing.T) { - s := &Controller{} - instances := s.convertServiceEntryToInstances(*tt.externalSvc, nil) - sortServiceInstances(instances) - sortServiceInstances(tt.out) - if err := compare(t, instances, tt.out); err != nil { - t.Fatalf("testcase: %v\n%v", tt.externalSvc.Name, err) - } - }) - } -} - -func TestConvertWorkloadEntryToServiceInstances(t *testing.T) { - labels := map[string]string{ - "app": "wle", - } - serviceInstanceTests := []struct { - name string - wle *networking.WorkloadEntry - se *config.Config - clusterID cluster.ID - out []*model.ServiceInstance - }{ - { - name: "simple", - wle: &networking.WorkloadEntry{ - Address: "1.1.1.1", - Labels: labels, - }, - se: selector, - out: []*model.ServiceInstance{ - makeInstance(selector, "1.1.1.1", 444, selector.Spec.(*networking.ServiceEntry).Ports[0], labels, PlainText), - makeInstance(selector, "1.1.1.1", 445, selector.Spec.(*networking.ServiceEntry).Ports[1], labels, PlainText), - }, - }, - { - name: "mtls", - wle: &networking.WorkloadEntry{ - Address: "1.1.1.1", - Labels: labels, - ServiceAccount: "default", - }, - se: selector, - out: []*model.ServiceInstance{ - makeInstanceWithServiceAccount(selector, "1.1.1.1", 444, selector.Spec.(*networking.ServiceEntry).Ports[0], labels, "default"), - makeInstanceWithServiceAccount(selector, "1.1.1.1", 445, selector.Spec.(*networking.ServiceEntry).Ports[1], labels, "default"), - }, - }, - { - name: "replace-port", - wle: &networking.WorkloadEntry{ - Address: "1.1.1.1", - Labels: labels, - Ports: map[string]uint32{ - "http-445": 8080, - }, - }, - se: selector, - out: []*model.ServiceInstance{ - makeInstance(selector, "1.1.1.1", 444, selector.Spec.(*networking.ServiceEntry).Ports[0], labels, PlainText), - makeInstance(selector, "1.1.1.1", 8080, selector.Spec.(*networking.ServiceEntry).Ports[1], labels, PlainText), - }, - }, - { - name: "augment label", - wle: &networking.WorkloadEntry{ - Address: "1.1.1.1", - Labels: labels, - Locality: "region1/zone1/sunzone1", - Network: "network1", - ServiceAccount: "default", - }, - se: selector, - clusterID: "fakeCluster", - out: []*model.ServiceInstance{ - makeInstanceWithServiceAccount(selector, "1.1.1.1", 444, selector.Spec.(*networking.ServiceEntry).Ports[0], labels, "default"), - makeInstanceWithServiceAccount(selector, "1.1.1.1", 445, selector.Spec.(*networking.ServiceEntry).Ports[1], labels, "default"), - }, - }, - } - - for _, tt := range serviceInstanceTests { - t.Run(tt.name, func(t *testing.T) { - services := convertServices(*tt.se) - s := &Controller{} - instances := s.convertWorkloadEntryToServiceInstances(tt.wle, services, tt.se.Spec.(*networking.ServiceEntry), &configKey{}, tt.clusterID) - sortServiceInstances(instances) - sortServiceInstances(tt.out) - - if tt.wle.Locality != "" || tt.clusterID != "" || tt.wle.Network != "" { - for _, serviceInstance := range tt.out { - serviceInstance.Endpoint.Locality = model.Locality{ - Label: tt.wle.Locality, - ClusterID: tt.clusterID, - } - serviceInstance.Endpoint.Network = network.ID(tt.wle.Network) - serviceInstance.Endpoint.Labels = labelutil.AugmentLabels(serviceInstance.Endpoint.Labels, - tt.clusterID, tt.wle.Locality, network.ID(tt.wle.Network)) - } - } - - if err := compare(t, instances, tt.out); err != nil { - t.Fatal(err) - } - }) - } -} - -func TestConvertWorkloadEntryToWorkloadInstance(t *testing.T) { - workloadLabel := map[string]string{ - "app": "wle", - } - - clusterID := "fakeCluster" - expectedLabel := map[string]string{ - "app": "wle", - "topology.istio.io/cluster": clusterID, - } - - workloadInstanceTests := []struct { - name string - getNetworkIDCb func(IP string, labels labels.Instance) network.ID - wle config.Config - out *model.WorkloadInstance - }{ - { - name: "simple", - wle: config.Config{ - Meta: config.Meta{ - Namespace: "ns1", - }, - Spec: &networking.WorkloadEntry{ - Address: "1.1.1.1", - Labels: workloadLabel, - Ports: map[string]uint32{ - "http": 80, - }, - ServiceAccount: "scooby", - }, - }, - out: &model.WorkloadInstance{ - Namespace: "ns1", - Kind: model.WorkloadEntryKind, - Endpoint: &model.IstioEndpoint{ - Labels: expectedLabel, - Address: "1.1.1.1", - ServiceAccount: "spiffe://cluster.local/ns/ns1/sa/scooby", - TLSMode: "istio", - Namespace: "ns1", - Locality: model.Locality{ - ClusterID: cluster.ID(clusterID), - }, - }, - PortMap: map[string]uint32{ - "http": 80, - }, - }, - }, - { - name: "simple - tls mode disabled", - wle: config.Config{ - Meta: config.Meta{ - Namespace: "ns1", - }, - Spec: &networking.WorkloadEntry{ - Address: "1.1.1.1", - Labels: map[string]string{ - "security.istio.io/tlsMode": "disabled", - }, - Ports: map[string]uint32{ - "http": 80, - }, - ServiceAccount: "scooby", - }, - }, - out: &model.WorkloadInstance{ - Namespace: "ns1", - Kind: model.WorkloadEntryKind, - Endpoint: &model.IstioEndpoint{ - Labels: map[string]string{ - "security.istio.io/tlsMode": "disabled", - "topology.istio.io/cluster": clusterID, - }, - Address: "1.1.1.1", - ServiceAccount: "spiffe://cluster.local/ns/ns1/sa/scooby", - TLSMode: "disabled", - Namespace: "ns1", - Locality: model.Locality{ - ClusterID: cluster.ID(clusterID), - }, - }, - PortMap: map[string]uint32{ - "http": 80, - }, - }, - }, - { - name: "unix domain socket", - wle: config.Config{ - Meta: config.Meta{ - Namespace: "ns1", - }, - Spec: &networking.WorkloadEntry{ - Address: "unix://foo/bar", - ServiceAccount: "scooby", - }, - }, - out: &model.WorkloadInstance{ - Namespace: "ns1", - Kind: model.WorkloadEntryKind, - Endpoint: &model.IstioEndpoint{ - Labels: map[string]string{ - "topology.istio.io/cluster": clusterID, - }, - Address: "unix://foo/bar", - ServiceAccount: "spiffe://cluster.local/ns/ns1/sa/scooby", - TLSMode: "istio", - Namespace: "ns1", - Locality: model.Locality{ - ClusterID: cluster.ID(clusterID), - }, - }, - DNSServiceEntryOnly: true, - }, - }, - { - name: "DNS address", - wle: config.Config{ - Meta: config.Meta{ - Namespace: "ns1", - }, - Spec: &networking.WorkloadEntry{ - Address: "scooby.com", - ServiceAccount: "scooby", - }, - }, - out: &model.WorkloadInstance{ - Namespace: "ns1", - Kind: model.WorkloadEntryKind, - Endpoint: &model.IstioEndpoint{ - Labels: map[string]string{ - "topology.istio.io/cluster": clusterID, - }, - Address: "scooby.com", - ServiceAccount: "spiffe://cluster.local/ns/ns1/sa/scooby", - TLSMode: "istio", - Namespace: "ns1", - Locality: model.Locality{ - ClusterID: cluster.ID(clusterID), - }, - }, - DNSServiceEntryOnly: true, - }, - }, - { - name: "metadata labels only", - wle: config.Config{ - Meta: config.Meta{ - Labels: workloadLabel, - Namespace: "ns1", - }, - Spec: &networking.WorkloadEntry{ - Address: "1.1.1.1", - Ports: map[string]uint32{ - "http": 80, - }, - ServiceAccount: "scooby", - }, - }, - out: &model.WorkloadInstance{ - Namespace: "ns1", - Kind: model.WorkloadEntryKind, - Endpoint: &model.IstioEndpoint{ - Labels: expectedLabel, - Address: "1.1.1.1", - ServiceAccount: "spiffe://cluster.local/ns/ns1/sa/scooby", - TLSMode: "istio", - Namespace: "ns1", - Locality: model.Locality{ - ClusterID: cluster.ID(clusterID), - }, - }, - PortMap: map[string]uint32{ - "http": 80, - }, - }, - }, - { - name: "labels merge", - wle: config.Config{ - Meta: config.Meta{ - Namespace: "ns1", - Labels: map[string]string{ - "my-label": "bar", - }, - }, - Spec: &networking.WorkloadEntry{ - Address: "1.1.1.1", - Labels: workloadLabel, - Ports: map[string]uint32{ - "http": 80, - }, - ServiceAccount: "scooby", - }, - }, - out: &model.WorkloadInstance{ - Namespace: "ns1", - Kind: model.WorkloadEntryKind, - Endpoint: &model.IstioEndpoint{ - Labels: map[string]string{ - "my-label": "bar", - "app": "wle", - "topology.istio.io/cluster": clusterID, - }, - Address: "1.1.1.1", - ServiceAccount: "spiffe://cluster.local/ns/ns1/sa/scooby", - TLSMode: "istio", - Namespace: "ns1", - Locality: model.Locality{ - ClusterID: cluster.ID(clusterID), - }, - }, - PortMap: map[string]uint32{ - "http": 80, - }, - }, - }, - { - name: "augment labels", - wle: config.Config{ - Meta: config.Meta{ - Namespace: "ns1", - }, - Spec: &networking.WorkloadEntry{ - Address: "1.1.1.1", - Labels: workloadLabel, - Ports: map[string]uint32{ - "http": 80, - }, - Locality: "region1/zone1/subzone1", - Network: "network1", - ServiceAccount: "scooby", - }, - }, - out: &model.WorkloadInstance{ - Namespace: "ns1", - Kind: model.WorkloadEntryKind, - Endpoint: &model.IstioEndpoint{ - Labels: map[string]string{ - "app": "wle", - "topology.kubernetes.io/region": "region1", - "topology.kubernetes.io/zone": "zone1", - "topology.istio.io/subzone": "subzone1", - "topology.istio.io/network": "network1", - "topology.istio.io/cluster": clusterID, - }, - Address: "1.1.1.1", - Network: "network1", - Locality: model.Locality{ - Label: "region1/zone1/subzone1", - ClusterID: cluster.ID(clusterID), - }, - ServiceAccount: "spiffe://cluster.local/ns/ns1/sa/scooby", - TLSMode: "istio", - Namespace: "ns1", - }, - PortMap: map[string]uint32{ - "http": 80, - }, - }, - }, - { - name: "augment labels: networkID get from cb", - wle: config.Config{ - Meta: config.Meta{ - Namespace: "ns1", - }, - Spec: &networking.WorkloadEntry{ - Address: "1.1.1.1", - Labels: workloadLabel, - Ports: map[string]uint32{ - "http": 80, - }, - Locality: "region1/zone1/subzone1", - ServiceAccount: "scooby", - }, - }, - getNetworkIDCb: func(IP string, labels labels.Instance) network.ID { - return "cb-network1" - }, - out: &model.WorkloadInstance{ - Namespace: "ns1", - Kind: model.WorkloadEntryKind, - Endpoint: &model.IstioEndpoint{ - Labels: map[string]string{ - "app": "wle", - "topology.kubernetes.io/region": "region1", - "topology.kubernetes.io/zone": "zone1", - "topology.istio.io/subzone": "subzone1", - "topology.istio.io/network": "cb-network1", - "topology.istio.io/cluster": clusterID, - }, - Address: "1.1.1.1", - Locality: model.Locality{ - Label: "region1/zone1/subzone1", - ClusterID: cluster.ID(clusterID), - }, - ServiceAccount: "spiffe://cluster.local/ns/ns1/sa/scooby", - TLSMode: "istio", - Namespace: "ns1", - }, - PortMap: map[string]uint32{ - "http": 80, - }, - }, - }, - } - - for _, tt := range workloadInstanceTests { - t.Run(tt.name, func(t *testing.T) { - s := &Controller{networkIDCallback: tt.getNetworkIDCb} - instance := s.convertWorkloadEntryToWorkloadInstance(tt.wle, cluster.ID(clusterID)) - if err := compare(t, instance, tt.out); err != nil { - t.Fatal(err) - } - }) - } -} - -func compare(t testing.TB, actual, expected interface{}) error { - return util.Compare(jsonBytes(t, actual), jsonBytes(t, expected)) -} - -func jsonBytes(t testing.TB, v interface{}) []byte { - data, err := json.MarshalIndent(v, "", " ") - if err != nil { - t.Fatal(t) - } - return data -} diff --git a/pilot/pkg/serviceregistry/serviceentry/leak_test.go b/pilot/pkg/serviceregistry/serviceentry/leak_test.go deleted file mode 100644 index 332699ab4..000000000 --- a/pilot/pkg/serviceregistry/serviceentry/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package serviceentry - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/serviceregistry/serviceentry/servicediscovery.go b/pilot/pkg/serviceregistry/serviceentry/servicediscovery.go deleted file mode 100644 index fc7a24dbe..000000000 --- a/pilot/pkg/serviceregistry/serviceentry/servicediscovery.go +++ /dev/null @@ -1,955 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package serviceentry - -import ( - "fmt" - "reflect" - "strconv" - "sync" - "time" -) - -import ( - networking "istio.io/api/networking/v1alpha3" - istiolog "istio.io/pkg/log" - "k8s.io/apimachinery/pkg/types" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model/status" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/util/workloadinstances" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/util/informermetric" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/network" - "github.com/apache/dubbo-go-pixiu/pkg/queue" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -var ( - _ serviceregistry.Instance = &Controller{} - log = istiolog.RegisterScope("serviceentry", "ServiceEntry registry", 0) -) - -// instancesKey acts as a key to identify all instances for a given hostname/namespace pair -// This is mostly used as an index -type instancesKey struct { - hostname host.Name - namespace string -} - -func makeInstanceKey(i *model.ServiceInstance) instancesKey { - return instancesKey{i.Service.Hostname, i.Service.Attributes.Namespace} -} - -type externalConfigType int - -const ( - serviceEntryConfigType externalConfigType = iota - workloadEntryConfigType - podConfigType -) - -// configKey unique identifies a config object managed by this registry (ServiceEntry and WorkloadEntry) -type configKey struct { - kind externalConfigType - name string - namespace string -} - -// Controller communicates with ServiceEntry CRDs and monitors for changes. -type Controller struct { - XdsUpdater model.XDSUpdater - - store model.ConfigStore - clusterID cluster.ID - - // This lock is to make multi ops on the below stores. For example, in some case, - // it requires delete all instances and then update new ones. - mutex sync.RWMutex - - serviceInstances serviceInstancesStore - // NOTE: historically, one index for both WorkloadEntry(s) and Pod(s); - // beware of naming collisions - workloadInstances workloadinstances.Index - services serviceStore - - // to make sure the eds update run in serial to prevent stale ones can override new ones - // There are multiple threads calling edsUpdate. - // If all share one lock, then all the threads can have an obvious performance downgrade. - edsQueue queue.Instance - - workloadHandlers []func(*model.WorkloadInstance, model.Event) - - // callback function used to get the networkID according to workload ip and labels. - networkIDCallback func(IP string, labels labels.Instance) network.ID - - processServiceEntry bool - - model.NetworkGatewaysHandler -} - -type Option func(*Controller) - -func WithClusterID(clusterID cluster.ID) Option { - return func(o *Controller) { - o.clusterID = clusterID - } -} - -func WithNetworkIDCb(cb func(endpointIP string, labels labels.Instance) network.ID) Option { - return func(o *Controller) { - o.networkIDCallback = cb - } -} - -// NewController creates a new ServiceEntry discovery service. -func NewController(configController model.ConfigStoreController, store model.ConfigStore, xdsUpdater model.XDSUpdater, - options ...Option) *Controller { - s := newController(store, xdsUpdater, options...) - if configController != nil { - configController.RegisterEventHandler(gvk.ServiceEntry, s.serviceEntryHandler) - configController.RegisterEventHandler(gvk.WorkloadEntry, s.workloadEntryHandler) - _ = configController.SetWatchErrorHandler(informermetric.ErrorHandlerForCluster(s.clusterID)) - } - return s -} - -// NewWorkloadEntryController creates a new WorkloadEntry discovery service. -func NewWorkloadEntryController(configController model.ConfigStoreController, store model.ConfigStore, xdsUpdater model.XDSUpdater, - options ...Option) *Controller { - s := newController(store, xdsUpdater, options...) - // Disable service entry processing for workload entry controller. - s.processServiceEntry = false - for _, o := range options { - o(s) - } - - if configController != nil { - configController.RegisterEventHandler(gvk.WorkloadEntry, s.workloadEntryHandler) - _ = configController.SetWatchErrorHandler(informermetric.ErrorHandlerForCluster(s.clusterID)) - } - return s -} - -func newController(store model.ConfigStore, xdsUpdater model.XDSUpdater, options ...Option) *Controller { - s := &Controller{ - XdsUpdater: xdsUpdater, - store: store, - serviceInstances: serviceInstancesStore{ - ip2instance: map[string][]*model.ServiceInstance{}, - instances: map[instancesKey]map[configKey][]*model.ServiceInstance{}, - instancesBySE: map[types.NamespacedName]map[configKey][]*model.ServiceInstance{}, - }, - workloadInstances: workloadinstances.NewIndex(), - services: serviceStore{ - servicesBySE: map[types.NamespacedName][]*model.Service{}, - }, - edsQueue: queue.NewQueue(time.Second), - processServiceEntry: true, - } - for _, o := range options { - o(s) - } - return s -} - -// convertWorkloadEntry convert wle from Config.Spec and populate the metadata labels into it. -func convertWorkloadEntry(cfg config.Config) *networking.WorkloadEntry { - wle := cfg.Spec.(*networking.WorkloadEntry) - if wle == nil { - return nil - } - - labels := make(map[string]string, len(wle.Labels)+len(cfg.Labels)) - for k, v := range wle.Labels { - labels[k] = v - } - // we will merge labels from metadata with spec, with precedence to the metadata - for k, v := range cfg.Labels { - labels[k] = v - } - // shallow copy - copied := &networking.WorkloadEntry{} - protomarshal.ShallowCopy(copied, wle) - copied.Labels = labels - return copied -} - -// workloadEntryHandler defines the handler for workload entries -func (s *Controller) workloadEntryHandler(old, curr config.Config, event model.Event) { - log.Debugf("Handle event %s for workload entry %s/%s", event, curr.Namespace, curr.Name) - var oldWle *networking.WorkloadEntry - if old.Spec != nil { - oldWle = convertWorkloadEntry(old) - } - wle := convertWorkloadEntry(curr) - curr.Spec = wle - key := configKey{ - kind: workloadEntryConfigType, - name: curr.Name, - namespace: curr.Namespace, - } - - // If an entry is unhealthy, we will mark this as a delete instead - // This ensures we do not track unhealthy endpoints - if features.WorkloadEntryHealthChecks && !isHealthy(curr) { - event = model.EventDelete - } - - wi := s.convertWorkloadEntryToWorkloadInstance(curr, s.Cluster()) - if wi != nil && !wi.DNSServiceEntryOnly { - // fire off the k8s handlers - for _, h := range s.workloadHandlers { - h(wi, event) - } - } - - // includes instances new updated or unchanged, in other word it is the current state. - instancesUpdated := []*model.ServiceInstance{} - instancesDeleted := []*model.ServiceInstance{} - fullPush := false - configsUpdated := map[model.ConfigKey]struct{}{} - - addConfigs := func(se *networking.ServiceEntry, services []*model.Service) { - // If serviceentry's resolution is DNS, make a full push - // TODO: maybe cds? - if se.Resolution == networking.ServiceEntry_DNS || se.Resolution == networking.ServiceEntry_DNS_ROUND_ROBIN { - fullPush = true - for key, value := range getUpdatedConfigs(services) { - configsUpdated[key] = value - } - } - } - - cfgs, _ := s.store.List(gvk.ServiceEntry, curr.Namespace) - currSes := getWorkloadServiceEntries(cfgs, wle) - var oldSes map[types.NamespacedName]*config.Config - if oldWle != nil { - if labels.Instance(oldWle.Labels).Equals(curr.Labels) { - oldSes = currSes - } else { - oldSes = getWorkloadServiceEntries(cfgs, oldWle) - } - } - unSelected := difference(oldSes, currSes) - log.Debugf("workloadEntry %s/%s selected %v, unSelected %v serviceEntry", curr.Namespace, curr.Name, currSes, unSelected) - s.mutex.Lock() - for namespacedName, cfg := range currSes { - services := s.services.getServices(namespacedName) - se := cfg.Spec.(*networking.ServiceEntry) - if wi.DNSServiceEntryOnly && se.Resolution != networking.ServiceEntry_DNS && - se.Resolution != networking.ServiceEntry_DNS_ROUND_ROBIN { - log.Debugf("skip selecting workload instance %v/%v for DNS service entry %v", wi.Namespace, wi.Name, se.Hosts) - continue - } - instance := s.convertWorkloadEntryToServiceInstances(wle, services, se, &key, s.Cluster()) - instancesUpdated = append(instancesUpdated, instance...) - addConfigs(se, services) - } - - for _, namespacedName := range unSelected { - services := s.services.getServices(namespacedName) - cfg := oldSes[namespacedName] - se := cfg.Spec.(*networking.ServiceEntry) - if wi.DNSServiceEntryOnly && se.Resolution != networking.ServiceEntry_DNS && - se.Resolution != networking.ServiceEntry_DNS_ROUND_ROBIN { - log.Debugf("skip selecting workload instance %v/%v for DNS service entry %v", wi.Namespace, wi.Name, se.Hosts) - continue - } - instance := s.convertWorkloadEntryToServiceInstances(wle, services, se, &key, s.Cluster()) - instancesDeleted = append(instancesDeleted, instance...) - addConfigs(se, services) - } - - s.serviceInstances.deleteInstances(key, instancesDeleted) - if event == model.EventDelete { - s.workloadInstances.Delete(wi) - s.serviceInstances.deleteInstances(key, instancesUpdated) - } else { - s.workloadInstances.Insert(wi) - s.serviceInstances.updateInstances(key, instancesUpdated) - } - s.mutex.Unlock() - - allInstances := append(instancesUpdated, instancesDeleted...) - if !fullPush { - // trigger full xds push to the related sidecar proxy - if event == model.EventAdd { - s.XdsUpdater.ProxyUpdate(s.Cluster(), wle.Address) - } - s.edsUpdate(allInstances) - return - } - - // update eds cache only - s.edsCacheUpdate(allInstances) - - pushReq := &model.PushRequest{ - Full: true, - ConfigsUpdated: configsUpdated, - Reason: []model.TriggerReason{model.EndpointUpdate}, - } - // trigger a full push - s.XdsUpdater.ConfigUpdate(pushReq) -} - -// getUpdatedConfigs returns related service entries when full push -func getUpdatedConfigs(services []*model.Service) map[model.ConfigKey]struct{} { - configsUpdated := map[model.ConfigKey]struct{}{} - for _, svc := range services { - configsUpdated[model.ConfigKey{ - Kind: gvk.ServiceEntry, - Name: string(svc.Hostname), - Namespace: svc.Attributes.Namespace, - }] = struct{}{} - } - return configsUpdated -} - -// serviceEntryHandler defines the handler for service entries -func (s *Controller) serviceEntryHandler(_, curr config.Config, event model.Event) { - currentServiceEntry := curr.Spec.(*networking.ServiceEntry) - cs := convertServices(curr) - configsUpdated := map[model.ConfigKey]struct{}{} - key := types.NamespacedName{Namespace: curr.Namespace, Name: curr.Name} - - s.mutex.Lock() - // If it is add/delete event we should always do a full push. If it is update event, we should do full push, - // only when services have changed - otherwise, just push endpoint updates. - var addedSvcs, deletedSvcs, updatedSvcs, unchangedSvcs []*model.Service - switch event { - case model.EventUpdate: - addedSvcs, deletedSvcs, updatedSvcs, unchangedSvcs = servicesDiff(s.services.getServices(key), cs) - s.services.updateServices(key, cs) - case model.EventDelete: - deletedSvcs = cs - s.services.deleteServices(key) - case model.EventAdd: - addedSvcs = cs - s.services.updateServices(key, cs) - default: - // this should not happen - unchangedSvcs = cs - } - - serviceInstancesByConfig, serviceInstances := s.buildServiceInstances(curr, cs) - oldInstances := s.serviceInstances.getServiceEntryInstances(key) - for configKey, old := range oldInstances { - s.serviceInstances.deleteInstances(configKey, old) - } - if event == model.EventDelete { - s.serviceInstances.deleteAllServiceEntryInstances(key) - } else { - // Update the indexes with new instances. - for ckey, value := range serviceInstancesByConfig { - s.serviceInstances.addInstances(ckey, value) - } - s.serviceInstances.updateServiceEntryInstances(key, serviceInstancesByConfig) - } - - shard := model.ShardKeyFromRegistry(s) - - for _, svc := range addedSvcs { - s.XdsUpdater.SvcUpdate(shard, string(svc.Hostname), svc.Attributes.Namespace, model.EventAdd) - configsUpdated[makeConfigKey(svc)] = struct{}{} - } - - for _, svc := range updatedSvcs { - s.XdsUpdater.SvcUpdate(shard, string(svc.Hostname), svc.Attributes.Namespace, model.EventUpdate) - configsUpdated[makeConfigKey(svc)] = struct{}{} - } - // If service entry is deleted, call SvcUpdate to cleanup endpoint shards for services. - for _, svc := range deletedSvcs { - instanceKey := instancesKey{namespace: svc.Attributes.Namespace, hostname: svc.Hostname} - // There can be multiple service entries of same host reside in same namespace. - // Delete endpoint shards only if there are no service instances. - if len(s.serviceInstances.getByKey(instanceKey)) == 0 { - s.XdsUpdater.SvcUpdate(shard, string(svc.Hostname), svc.Attributes.Namespace, model.EventDelete) - } - configsUpdated[makeConfigKey(svc)] = struct{}{} - } - - // If a service is updated and is not part of updatedSvcs, that means its endpoints might have changed. - // If this service entry had endpoints with IPs (i.e. resolution STATIC), then we do EDS update. - // If the service entry had endpoints with FQDNs (i.e. resolution DNS), then we need to do - // full push (as fqdn endpoints go via strict_dns clusters in cds). - if len(unchangedSvcs) > 0 { - if currentServiceEntry.Resolution == networking.ServiceEntry_DNS || currentServiceEntry.Resolution == networking.ServiceEntry_DNS_ROUND_ROBIN { - for _, svc := range unchangedSvcs { - configsUpdated[makeConfigKey(svc)] = struct{}{} - } - } - } - s.mutex.Unlock() - - fullPush := len(configsUpdated) > 0 - // if not full push needed, at least one service unchanged - if !fullPush { - s.edsUpdate(serviceInstances) - return - } - - // When doing a full push, the non DNS added, updated, unchanged services trigger an eds update - // so that endpoint shards are updated. - allServices := make([]*model.Service, 0, len(addedSvcs)+len(updatedSvcs)+len(unchangedSvcs)) - nonDNSServices := make([]*model.Service, 0, len(addedSvcs)+len(updatedSvcs)+len(unchangedSvcs)) - allServices = append(allServices, addedSvcs...) - allServices = append(allServices, updatedSvcs...) - allServices = append(allServices, unchangedSvcs...) - for _, svc := range allServices { - if !(svc.Resolution == model.DNSLB || svc.Resolution == model.DNSRoundRobinLB) { - nonDNSServices = append(nonDNSServices, svc) - } - } - // non dns service instances - keys := map[instancesKey]struct{}{} - for _, svc := range nonDNSServices { - keys[instancesKey{hostname: svc.Hostname, namespace: curr.Namespace}] = struct{}{} - } - - s.queueEdsEvent(keys, s.doEdsCacheUpdate) - - pushReq := &model.PushRequest{ - Full: true, - ConfigsUpdated: configsUpdated, - Reason: []model.TriggerReason{model.ServiceUpdate}, - } - s.XdsUpdater.ConfigUpdate(pushReq) -} - -// WorkloadInstanceHandler defines the handler for service instances generated by other registries -func (s *Controller) WorkloadInstanceHandler(wi *model.WorkloadInstance, event model.Event) { - log.Debugf("Handle event %s for workload instance (%s/%s) in namespace %s", event, - wi.Kind, wi.Endpoint.Address, wi.Namespace) - key := configKey{ - kind: podConfigType, - name: wi.Name, - namespace: wi.Namespace, - } - // Used to indicate if this event was fired for a pod->workloadentry conversion - // and that the event can be ignored due to no relevant change in the workloadentry - redundantEventForPod := false - - var addressToDelete string - s.mutex.Lock() - // this is from a pod. Store it in separate map so that - // the refreshIndexes function can use these as well as the store ones. - switch event { - case model.EventDelete: - redundantEventForPod = s.workloadInstances.Delete(wi) == nil - default: // add or update - if old := s.workloadInstances.Insert(wi); old != nil { - if old.Endpoint.Address != wi.Endpoint.Address { - addressToDelete = old.Endpoint.Address - } - // If multiple k8s services select the same pod or a service has multiple ports, - // we may be getting multiple events ignore them as we only care about the Endpoint IP itself. - if model.WorkloadInstancesEqual(old, wi) { - // ignore the update as nothing has changed - redundantEventForPod = true - } - } - } - - if redundantEventForPod { - s.mutex.Unlock() - return - } - - // We will only select entries in the same namespace - cfgs, _ := s.store.List(gvk.ServiceEntry, wi.Namespace) - if len(cfgs) == 0 { - s.mutex.Unlock() - return - } - - instances := []*model.ServiceInstance{} - instancesDeleted := []*model.ServiceInstance{} - for _, cfg := range cfgs { - se := cfg.Spec.(*networking.ServiceEntry) - if se.WorkloadSelector == nil || !labels.Instance(se.WorkloadSelector.Labels).SubsetOf(wi.Endpoint.Labels) { - // Not a match, skip this one - continue - } - seNamespacedName := types.NamespacedName{Namespace: cfg.Namespace, Name: cfg.Name} - services := s.services.getServices(seNamespacedName) - instance := convertWorkloadInstanceToServiceInstance(wi.Endpoint, services, se) - instances = append(instances, instance...) - if addressToDelete != "" { - for _, i := range instance { - di := i.DeepCopy() - di.Endpoint.Address = addressToDelete - instancesDeleted = append(instancesDeleted, di) - } - s.serviceInstances.deleteServiceEntryInstances(seNamespacedName, key) - } else if event == model.EventDelete { - s.serviceInstances.deleteServiceEntryInstances(seNamespacedName, key) - } else { - s.serviceInstances.updateServiceEntryInstancesPerConfig(seNamespacedName, key, instance) - } - } - if len(instancesDeleted) > 0 { - s.serviceInstances.deleteInstances(key, instancesDeleted) - } - - if event == model.EventDelete { - s.serviceInstances.deleteInstances(key, instances) - } else { - s.serviceInstances.updateInstances(key, instances) - } - s.mutex.Unlock() - - s.edsUpdate(instances) -} - -func (s *Controller) Provider() provider.ID { - return provider.External -} - -func (s *Controller) Cluster() cluster.ID { - return s.clusterID -} - -// AppendServiceHandler adds service resource event handler. Service Entries does not use these handlers. -func (s *Controller) AppendServiceHandler(_ func(*model.Service, model.Event)) {} - -// AppendWorkloadHandler adds instance event handler. Service Entries does not use these handlers. -func (s *Controller) AppendWorkloadHandler(h func(*model.WorkloadInstance, model.Event)) { - s.workloadHandlers = append(s.workloadHandlers, h) -} - -// Run is used by some controllers to execute background jobs after init is done. -func (s *Controller) Run(stopCh <-chan struct{}) { - s.edsQueue.Run(stopCh) -} - -// HasSynced always returns true for SE -func (s *Controller) HasSynced() bool { - return true -} - -// Services list declarations of all services in the system -func (s *Controller) Services() []*model.Service { - s.mutex.Lock() - allServices := s.services.getAllServices() - out := make([]*model.Service, 0, len(allServices)) - if s.services.allocateNeeded { - autoAllocateIPs(allServices) - s.services.allocateNeeded = false - } - s.mutex.Unlock() - for _, svc := range allServices { - // shallow copy, copy `AutoAllocatedIPv4Address` and `AutoAllocatedIPv6Address` - // if return the pointer directly, there will be a race with `BuildNameTable` - // nolint: govet - shallowSvc := *svc - out = append(out, &shallowSvc) - } - return out -} - -// GetService retrieves a service by host name if it exists. -// NOTE: The service entry implementation is used only for tests. -func (s *Controller) GetService(hostname host.Name) *model.Service { - if !s.processServiceEntry { - return nil - } - // TODO(@hzxuzhonghu): only get the specific service instead of converting all the serviceEntries - services := s.Services() - for _, service := range services { - if service.Hostname == hostname { - return service - } - } - - return nil -} - -// InstancesByPort retrieves instances for a service on the given ports with labels that -// match any of the supplied labels. All instances match an empty tag list. -func (s *Controller) InstancesByPort(svc *model.Service, port int, labels labels.Instance) []*model.ServiceInstance { - out := make([]*model.ServiceInstance, 0) - s.mutex.RLock() - instanceLists := s.serviceInstances.getByKey(instancesKey{svc.Hostname, svc.Attributes.Namespace}) - s.mutex.RUnlock() - for _, instance := range instanceLists { - if labels.SubsetOf(instance.Endpoint.Labels) && - portMatchSingle(instance, port) { - out = append(out, instance) - } - } - - return out -} - -// ResyncEDS will do a full EDS update. This is needed for some tests where we have many configs loaded without calling -// the config handlers. -// This should probably not be used in production code. -func (s *Controller) ResyncEDS() { - s.mutex.RLock() - allInstances := s.serviceInstances.getAll() - s.mutex.RUnlock() - s.edsUpdate(allInstances) -} - -// edsUpdate triggers an EDS push serially such that we can prevent all instances -// got at t1 can accidentally override that got at t2 if multiple threads are -// running this function. Queueing ensures latest updated wins. -func (s *Controller) edsUpdate(instances []*model.ServiceInstance) { - // Find all keys we need to lookup - keys := map[instancesKey]struct{}{} - for _, i := range instances { - keys[makeInstanceKey(i)] = struct{}{} - } - s.queueEdsEvent(keys, s.doEdsUpdate) -} - -// edsCacheUpdate upates eds cache serially such that we can prevent allinstances -// got at t1 can accidentally override that got at t2 if multiple threads are -// running this function. Queueing ensures latest updated wins. -func (s *Controller) edsCacheUpdate(instances []*model.ServiceInstance) { - // Find all keys we need to lookup - keys := map[instancesKey]struct{}{} - for _, i := range instances { - keys[makeInstanceKey(i)] = struct{}{} - } - s.queueEdsEvent(keys, s.doEdsCacheUpdate) -} - -// queueEdsEvent processes eds events sequentially for the passed keys and invokes the passed function. -func (s *Controller) queueEdsEvent(keys map[instancesKey]struct{}, edsFn func(keys map[instancesKey]struct{})) { - // wait for the cache update finished - waitCh := make(chan struct{}) - // trigger update eds endpoint shards - s.edsQueue.Push(func() error { - defer close(waitCh) - edsFn(keys) - return nil - }) - select { - case <-waitCh: - return - // To prevent goroutine leak in tests - // in case the queue is stopped but the task has not been executed.. - case <-s.edsQueue.Closed(): - return - } -} - -// doEdsCacheUpdate invokes XdsUpdater's EDSCacheUpdate to update endpoint shards. -func (s *Controller) doEdsCacheUpdate(keys map[instancesKey]struct{}) { - endpoints := s.buildEndpoints(keys) - shard := model.ShardKeyFromRegistry(s) - // This is delete. - if len(endpoints) == 0 { - for k := range keys { - s.XdsUpdater.EDSCacheUpdate(shard, string(k.hostname), k.namespace, nil) - } - } else { - for k, eps := range endpoints { - s.XdsUpdater.EDSCacheUpdate(shard, string(k.hostname), k.namespace, eps) - } - } -} - -// doEdsUpdate invokes XdsUpdater's eds update to trigger eds push. -func (s *Controller) doEdsUpdate(keys map[instancesKey]struct{}) { - endpoints := s.buildEndpoints(keys) - shard := model.ShardKeyFromRegistry(s) - // This is delete. - if len(endpoints) == 0 { - for k := range keys { - s.XdsUpdater.EDSUpdate(shard, string(k.hostname), k.namespace, nil) - } - } else { - for k, eps := range endpoints { - s.XdsUpdater.EDSUpdate(shard, string(k.hostname), k.namespace, eps) - } - } -} - -// buildEndpoints builds endpoints for the instance keys. -func (s *Controller) buildEndpoints(keys map[instancesKey]struct{}) map[instancesKey][]*model.IstioEndpoint { - var endpoints map[instancesKey][]*model.IstioEndpoint - allInstances := []*model.ServiceInstance{} - s.mutex.RLock() - for key := range keys { - i := s.serviceInstances.getByKey(key) - allInstances = append(allInstances, i...) - } - s.mutex.RUnlock() - - if len(allInstances) > 0 { - endpoints = make(map[instancesKey][]*model.IstioEndpoint) - for _, instance := range allInstances { - port := instance.ServicePort - key := makeInstanceKey(instance) - endpoints[key] = append(endpoints[key], - &model.IstioEndpoint{ - Address: instance.Endpoint.Address, - EndpointPort: instance.Endpoint.EndpointPort, - ServicePortName: port.Name, - Labels: instance.Endpoint.Labels, - ServiceAccount: instance.Endpoint.ServiceAccount, - Network: instance.Endpoint.Network, - Locality: instance.Endpoint.Locality, - LbWeight: instance.Endpoint.LbWeight, - TLSMode: instance.Endpoint.TLSMode, - WorkloadName: instance.Endpoint.WorkloadName, - Namespace: instance.Endpoint.Namespace, - }) - } - - } - return endpoints -} - -// returns true if an instance's port matches with any in the provided list -func portMatchSingle(instance *model.ServiceInstance, port int) bool { - return port == 0 || port == instance.ServicePort.Port -} - -// GetProxyServiceInstances lists service instances co-located with a given proxy -// NOTE: The service objects in these instances do not have the auto allocated IP set. -func (s *Controller) GetProxyServiceInstances(node *model.Proxy) []*model.ServiceInstance { - out := make([]*model.ServiceInstance, 0) - s.mutex.RLock() - defer s.mutex.RUnlock() - for _, ip := range node.IPAddresses { - instances := s.serviceInstances.getByIP(ip) - for _, i := range instances { - // Insert all instances for this IP for services within the same namespace This ensures we - // match Kubernetes logic where Services do not cross namespace boundaries and avoids - // possibility of other namespaces inserting service instances into namespaces they do not - // control. - if node.Metadata.Namespace == "" || i.Service.Attributes.Namespace == node.Metadata.Namespace { - out = append(out, i) - } - } - } - return out -} - -func (s *Controller) GetProxyWorkloadLabels(proxy *model.Proxy) labels.Instance { - s.mutex.RLock() - defer s.mutex.RUnlock() - for _, ip := range proxy.IPAddresses { - instances := s.serviceInstances.getByIP(ip) - for _, instance := range instances { - return instance.Endpoint.Labels - } - } - return nil -} - -// GetIstioServiceAccounts implements model.ServiceAccounts operation -// For service entries using workload entries or mix of workload entries and pods, -// this function returns the appropriate service accounts used by these. -func (s *Controller) GetIstioServiceAccounts(svc *model.Service, ports []int) []string { - // service entries with built in endpoints have SANs as a dedicated field. - // Those with selector labels will have service accounts embedded inside workloadEntries and pods as well. - return model.GetServiceAccounts(svc, ports, s) -} - -func (s *Controller) NetworkGateways() []model.NetworkGateway { - // TODO implement mesh networks loading logic from kube controller if needed - return nil -} - -func (s *Controller) MCSServices() []model.MCSServiceInfo { - return nil -} - -func servicesDiff(os []*model.Service, ns []*model.Service) ([]*model.Service, []*model.Service, []*model.Service, []*model.Service) { - var added, deleted, updated, unchanged []*model.Service - - oldServiceHosts := make(map[host.Name]*model.Service, len(os)) - newServiceHosts := make(map[host.Name]*model.Service, len(ns)) - for _, s := range os { - oldServiceHosts[s.Hostname] = s - } - for _, s := range ns { - newServiceHosts[s.Hostname] = s - } - - for _, s := range os { - newSvc, f := newServiceHosts[s.Hostname] - if !f { - deleted = append(deleted, s) - } else if !reflect.DeepEqual(s, newSvc) { - updated = append(updated, newSvc) - } else { - unchanged = append(unchanged, newSvc) - } - } - - for _, s := range ns { - if _, f := oldServiceHosts[s.Hostname]; !f { - added = append(added, s) - } - } - - return added, deleted, updated, unchanged -} - -// Automatically allocates IPs for service entry services WITHOUT an -// address field if the hostname is not a wildcard, or when resolution -// is not NONE. The IPs are allocated from the reserved Class E subnet -// (240.240.0.0/16) that is not reachable outside the pod or reserved -// Benchmarking IP range (2001:2::/48) in RFC5180. When DNS -// capture is enabled, Envoy will resolve the DNS to these IPs. The -// listeners for TCP services will also be set up on these IPs. The -// IPs allocated to a service entry may differ from istiod to istiod -// but it does not matter because these IPs only affect the listener -// IPs on a given proxy managed by a given istiod. -// -// NOTE: If DNS capture is not enabled by the proxy, the automatically -// allocated IP addresses do not take effect. -// -// The current algorithm to allocate IPs is deterministic across all istiods. -// At stable state, given two istiods with exact same set of services, there should -// be no change in XDS as the algorithm is just a dumb iterative one that allocates sequentially. -// -// TODO: Rather than sequentially allocate IPs, switch to a hash based allocation mechanism so that -// deletion of the oldest service entry does not cause change of IPs for all other service entries. -// Currently, the sequential allocation will result in unnecessary XDS reloads (lds/rds) when a -// service entry with auto allocated IP is deleted. We are trading off a perf problem (xds reload) -// for a usability problem (e.g., multiple cloud SQL or AWS RDS tcp services with no VIPs end up having -// the same port, causing traffic to go to the wrong place). Once we move to a deterministic hash-based -// allocation with deterministic collision resolution, the perf problem will go away. If the collision guarantee -// cannot be made within the IP address space we have (which is about 64K services), then we may need to -// have the sequential allocation algorithm as a fallback when too many collisions take place. -func autoAllocateIPs(services []*model.Service) []*model.Service { - // i is everything from 240.240.0.(j) to 240.240.255.(j) - // j is everything from 240.240.(i).1 to 240.240.(i).254 - // we can capture this in one integer variable. - // given X, we can compute i by X/255, and j is X%255 - // To avoid allocating 240.240.(i).255, if X % 255 is 0, increment X. - // For example, when X=510, the resulting IP would be 240.240.2.0 (invalid) - // So we bump X to 511, so that the resulting IP is 240.240.2.1 - maxIPs := 255 * 255 // are we going to exceed this limit by processing 64K services? - x := 0 - for _, svc := range services { - // we can allocate IPs only if - // 1. the service has resolution set to static/dns. We cannot allocate - // for NONE because we will not know the original DST IP that the application requested. - // 2. the address is not set (0.0.0.0) - // 3. the hostname is not a wildcard - if svc.DefaultAddress == constants.UnspecifiedIP && !svc.Hostname.IsWildCarded() && - svc.Resolution != model.Passthrough { - x++ - if x%255 == 0 { - x++ - } - if x >= maxIPs { - log.Errorf("out of IPs to allocate for service entries") - return services - } - thirdOctet := x / 255 - fourthOctet := x % 255 - - svc.AutoAllocatedIPv4Address = fmt.Sprintf("240.240.%d.%d", thirdOctet, fourthOctet) - // if the service of service entry has IPv6 address, then allocate the IPv4-Mapped IPv6 Address for it - if thirdOctet == 0 { - svc.AutoAllocatedIPv6Address = fmt.Sprintf("2001:2::f0f0:%x", fourthOctet) - } else { - svc.AutoAllocatedIPv6Address = fmt.Sprintf("2001:2::f0f0:%x%x", thirdOctet, fourthOctet) - } - } - } - return services -} - -func makeConfigKey(svc *model.Service) model.ConfigKey { - return model.ConfigKey{ - Kind: gvk.ServiceEntry, - Name: string(svc.Hostname), - Namespace: svc.Attributes.Namespace, - } -} - -// isHealthy checks that the provided WorkloadEntry is healthy. If health checks are not enabled, -// it is assumed to always be healthy -func isHealthy(cfg config.Config) bool { - if parseHealthAnnotation(cfg.Annotations[status.WorkloadEntryHealthCheckAnnotation]) { - // We default to false if the condition is not set. This ensures newly created WorkloadEntries - // are treated as unhealthy until we prove they are healthy by probe success. - return status.GetBoolConditionFromSpec(cfg, status.ConditionHealthy, false) - } - // If health check is not enabled, assume its healthy - return true -} - -func parseHealthAnnotation(s string) bool { - if s == "" { - return false - } - p, err := strconv.ParseBool(s) - if err != nil { - return false - } - return p -} - -func (s *Controller) buildServiceInstances( - curr config.Config, - services []*model.Service, -) (map[configKey][]*model.ServiceInstance, []*model.ServiceInstance) { - currentServiceEntry := curr.Spec.(*networking.ServiceEntry) - var serviceInstances []*model.ServiceInstance - serviceInstancesByConfig := map[configKey][]*model.ServiceInstance{} - // for service entry with labels - if currentServiceEntry.WorkloadSelector != nil { - selector := workloadinstances.ByServiceSelector(curr.Namespace, currentServiceEntry.WorkloadSelector.Labels) - workloadInstances := workloadinstances.FindAllInIndex(s.workloadInstances, selector) - for _, wi := range workloadInstances { - if wi.DNSServiceEntryOnly && currentServiceEntry.Resolution != networking.ServiceEntry_DNS && - currentServiceEntry.Resolution != networking.ServiceEntry_DNS_ROUND_ROBIN { - log.Debugf("skip selecting workload instance %v/%v for DNS service entry %v", wi.Namespace, wi.Name, - currentServiceEntry.Hosts) - continue - } - instances := convertWorkloadInstanceToServiceInstance(wi.Endpoint, services, currentServiceEntry) - serviceInstances = append(serviceInstances, instances...) - ckey := configKey{namespace: wi.Namespace, name: wi.Name} - if wi.Kind == model.PodKind { - ckey.kind = podConfigType - } else { - ckey.kind = workloadEntryConfigType - } - serviceInstancesByConfig[ckey] = instances - } - } else { - serviceInstances = s.convertServiceEntryToInstances(curr, services) - ckey := configKey{ - kind: serviceEntryConfigType, - name: curr.Name, - namespace: curr.Namespace, - } - serviceInstancesByConfig[ckey] = serviceInstances - } - - return serviceInstancesByConfig, serviceInstances -} diff --git a/pilot/pkg/serviceregistry/serviceentry/servicediscovery_test.go b/pilot/pkg/serviceregistry/serviceentry/servicediscovery_test.go deleted file mode 100644 index cdc7c8724..000000000 --- a/pilot/pkg/serviceregistry/serviceentry/servicediscovery_test.go +++ /dev/null @@ -1,1702 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package serviceentry - -import ( - "fmt" - "reflect" - "sort" - "strings" - "testing" - "time" -) - -import ( - "istio.io/api/label" - networking "istio.io/api/networking/v1alpha3" - "k8s.io/apimachinery/pkg/types" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/spiffe" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -func createConfigs(configs []*config.Config, store model.ConfigStore, t testing.TB) { - t.Helper() - for _, cfg := range configs { - _, err := store.Create(*cfg) - if err != nil && strings.Contains(err.Error(), "item already exists") { - _, err := store.Update(*cfg) - if err != nil { - t.Fatalf("error occurred updating ServiceEntry config: %v", err) - } - } else if err != nil { - t.Fatalf("error occurred creating ServiceEntry config: %v", err) - } - } -} - -func callInstanceHandlers(instances []*model.WorkloadInstance, sd *Controller, ev model.Event, t testing.TB) { - t.Helper() - for _, instance := range instances { - sd.WorkloadInstanceHandler(instance, ev) - } -} - -func deleteConfigs(configs []*config.Config, store model.ConfigStore, t testing.TB) { - t.Helper() - for _, cfg := range configs { - err := store.Delete(cfg.GroupVersionKind, cfg.Name, cfg.Namespace, nil) - if err != nil { - t.Errorf("error occurred crearting ServiceEntry config: %v", err) - } - } -} - -type Event struct { - kind string - host string - namespace string - proxyIP string - endpoints int - pushReq *model.PushRequest -} - -type FakeXdsUpdater struct { - // Events tracks notifications received by the updater - Events chan Event -} - -var _ model.XDSUpdater = &FakeXdsUpdater{} - -func (fx *FakeXdsUpdater) EDSUpdate(_ model.ShardKey, hostname string, namespace string, entry []*model.IstioEndpoint) { - fx.Events <- Event{kind: "eds", host: hostname, namespace: namespace, endpoints: len(entry)} -} - -func (fx *FakeXdsUpdater) EDSCacheUpdate(_ model.ShardKey, _, _ string, _ []*model.IstioEndpoint) { -} - -func (fx *FakeXdsUpdater) ConfigUpdate(req *model.PushRequest) { - fx.Events <- Event{kind: "xds", pushReq: req} -} - -func (fx *FakeXdsUpdater) ProxyUpdate(_ cluster.ID, ip string) { - fx.Events <- Event{kind: "xds", proxyIP: ip} -} - -func (fx *FakeXdsUpdater) SvcUpdate(_ model.ShardKey, hostname string, namespace string, _ model.Event) { - fx.Events <- Event{kind: "svcupdate", host: hostname, namespace: namespace} -} - -func (fx *FakeXdsUpdater) RemoveShard(_ model.ShardKey) { - fx.Events <- Event{kind: "removeshard"} -} - -func waitUntilEvent(t testing.TB, ch chan Event, event Event) { - t.Helper() - for { - select { - case e := <-ch: - if e == event { - return - } - case <-time.After(2 * time.Second): - t.Fatalf("timed out waiting for event %v", event) - return - } - } -} - -func waitForEvent(t testing.TB, ch chan Event) Event { - t.Helper() - select { - case e := <-ch: - return e - case <-time.After(2 * time.Second): - t.Fatalf("timed out waiting for event") - return Event{} - } -} - -func initServiceDiscovery() (model.ConfigStore, *Controller, chan Event, func()) { - return initServiceDiscoveryWithOpts(false) -} - -// initServiceDiscoveryWithoutEvents initializes a test setup with no events. This avoids excessive attempts to push -// EDS updates to a full queue -func initServiceDiscoveryWithoutEvents(t test.Failer) (model.ConfigStore, *Controller) { - store := memory.Make(collections.Pilot) - configController := memory.NewController(store) - - stop := make(chan struct{}) - go configController.Run(stop) - - eventch := make(chan Event, 100) - xdsUpdater := &FakeXdsUpdater{ - Events: eventch, - } - go func() { - for { - select { - case <-stop: - return - case <-eventch: // drain - } - } - }() - - istioStore := model.MakeIstioStore(configController) - serviceController := NewController(configController, istioStore, xdsUpdater) - t.Cleanup(func() { - close(stop) - }) - return istioStore, serviceController -} - -func initServiceDiscoveryWithOpts(workloadOnly bool, opts ...Option) (model.ConfigStore, *Controller, chan Event, func()) { - store := memory.Make(collections.Pilot) - configController := memory.NewController(store) - - stop := make(chan struct{}) - go configController.Run(stop) - - eventch := make(chan Event, 100) - xdsUpdater := &FakeXdsUpdater{ - Events: eventch, - } - - istioStore := model.MakeIstioStore(configController) - var controller *Controller - if !workloadOnly { - controller = NewController(configController, istioStore, xdsUpdater, opts...) - } else { - controller = NewWorkloadEntryController(configController, istioStore, xdsUpdater, opts...) - } - go controller.Run(stop) - return istioStore, controller, eventch, func() { - close(stop) - } -} - -func TestServiceDiscoveryServices(t *testing.T) { - store, sd, eventCh, stopFn := initServiceDiscovery() - defer stopFn() - expectedServices := []*model.Service{ - makeService("*.istio.io", "httpDNSRR", constants.UnspecifiedIP, map[string]int{"http-port": 80, "http-alt-port": 8080}, true, model.DNSRoundRobinLB), - makeService("*.google.com", "httpDNS", constants.UnspecifiedIP, map[string]int{"http-port": 80, "http-alt-port": 8080}, true, model.DNSLB), - makeService("tcpstatic.com", "tcpStatic", "172.217.0.1", map[string]int{"tcp-444": 444}, true, model.ClientSideLB), - } - - createConfigs([]*config.Config{httpDNS, httpDNSRR, tcpStatic}, store, t) - - waitUntilEvent(t, eventCh, Event{ - kind: "svcupdate", - host: "*.google.com", - namespace: httpDNS.Namespace, - }) - - waitUntilEvent(t, eventCh, Event{ - kind: "svcupdate", - host: "*.istio.io", - namespace: httpDNSRR.Namespace, - }) - - waitUntilEvent(t, eventCh, Event{ - kind: "svcupdate", - host: "tcpstatic.com", - namespace: tcpStatic.Namespace, - }) - - services := sd.Services() - sortServices(services) - sortServices(expectedServices) - if err := compare(t, services, expectedServices); err != nil { - t.Error(err) - } -} - -func TestServiceDiscoveryGetService(t *testing.T) { - hostname := "*.google.com" - hostDNE := "does.not.exist.local" - - store, sd, eventCh, stopFn := initServiceDiscovery() - defer stopFn() - - createConfigs([]*config.Config{httpDNS, tcpStatic}, store, t) - waitForEvent(t, eventCh) - waitForEvent(t, eventCh) - service := sd.GetService(host.Name(hostDNE)) - if service != nil { - t.Errorf("GetService(%q) => should not exist, got %s", hostDNE, service.Hostname) - } - - service = sd.GetService(host.Name(hostname)) - if service == nil { - t.Fatalf("GetService(%q) => should exist", hostname) - } - if service.Hostname != host.Name(hostname) { - t.Errorf("GetService(%q) => %q, want %q", hostname, service.Hostname, hostname) - } -} - -// TestServiceDiscoveryServiceUpdate test various add/update/delete events for ServiceEntry -// nolint: lll -func TestServiceDiscoveryServiceUpdate(t *testing.T) { - store, sd, events, stopFn := initServiceDiscovery() - defer stopFn() - // httpStaticOverlayUpdated is the same as httpStaticOverlay but with an extra endpoint added to test updates - httpStaticOverlayUpdated := func() *config.Config { - c := httpStaticOverlay.DeepCopy() - se := c.Spec.(*networking.ServiceEntry) - se.Endpoints = append(se.Endpoints, &networking.WorkloadEntry{ - Address: "6.6.6.6", - Labels: map[string]string{"other": "bar"}, - }) - return &c - }() - // httpStaticOverlayUpdatedInstance is the same as httpStaticOverlayUpdated but with an extra endpoint added that has the same address - httpStaticOverlayUpdatedInstance := func() *config.Config { - c := httpStaticOverlayUpdated.DeepCopy() - se := c.Spec.(*networking.ServiceEntry) - se.Endpoints = append(se.Endpoints, &networking.WorkloadEntry{ - Address: "6.6.6.6", - Labels: map[string]string{"some-new-label": "bar"}, - }) - return &c - }() - - // httpStaticOverlayUpdatedNop is the same as httpStaticOverlayUpdated but with a NOP change - httpStaticOverlayUpdatedNop := func() *config.Config { - c := httpStaticOverlayUpdated.DeepCopy() - return &c - }() - - // httpStaticOverlayUpdatedNs is the same as httpStaticOverlay but with an extra endpoint and different namespace added to test updates - httpStaticOverlayUpdatedNs := func() *config.Config { - c := httpStaticOverlay.DeepCopy() - c.Namespace = "other" - se := c.Spec.(*networking.ServiceEntry) - se.Endpoints = append(se.Endpoints, &networking.WorkloadEntry{ - Address: "7.7.7.7", - Labels: map[string]string{"namespace": "bar"}, - }) - return &c - }() - - // Setup the expected instances for `httpStatic`. This will be added/removed from as we add various configs - baseInstances := []*model.ServiceInstance{ - makeInstance(httpStatic, "2.2.2.2", 7080, httpStatic.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS), - makeInstance(httpStatic, "2.2.2.2", 18080, httpStatic.Spec.(*networking.ServiceEntry).Ports[1], nil, MTLS), - makeInstance(httpStatic, "3.3.3.3", 1080, httpStatic.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS), - makeInstance(httpStatic, "3.3.3.3", 8080, httpStatic.Spec.(*networking.ServiceEntry).Ports[1], nil, MTLS), - makeInstance(httpStatic, "4.4.4.4", 1080, httpStatic.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"foo": "bar"}, PlainText), - makeInstance(httpStatic, "4.4.4.4", 8080, httpStatic.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"foo": "bar"}, PlainText), - } - - t.Run("simple entry", func(t *testing.T) { - // Create a SE, expect the base instances - createConfigs([]*config.Config{httpStatic}, store, t) - instances := baseInstances - expectServiceInstances(t, sd, httpStatic, 0, instances) - expectEvents(t, events, - Event{kind: "svcupdate", host: "*.google.com", namespace: httpStatic.Namespace}, - Event{kind: "xds", pushReq: &model.PushRequest{ConfigsUpdated: map[model.ConfigKey]struct{}{{Kind: gvk.ServiceEntry, Name: httpStatic.Spec.(*networking.ServiceEntry).Hosts[0], Namespace: httpStatic.Namespace}: {}}}}) - }) - - t.Run("add entry", func(t *testing.T) { - // Create another SE for the same host, expect these instances to get added - createConfigs([]*config.Config{httpStaticOverlay}, store, t) - instances := append(baseInstances, - makeInstance(httpStaticOverlay, "5.5.5.5", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText)) - expectServiceInstances(t, sd, httpStatic, 0, instances) - expectEvents(t, events, - Event{kind: "svcupdate", host: "*.google.com", namespace: httpStaticOverlay.Namespace}, - Event{kind: "xds", pushReq: &model.PushRequest{ConfigsUpdated: map[model.ConfigKey]struct{}{{Kind: gvk.ServiceEntry, Name: httpStaticOverlay.Spec.(*networking.ServiceEntry).Hosts[0], Namespace: httpStaticOverlay.Namespace}: {}}}}) - }) - - t.Run("add endpoint", func(t *testing.T) { - // Update the SE for the same host, expect these instances to get added - createConfigs([]*config.Config{httpStaticOverlayUpdated}, store, t) - instances := append(baseInstances, - makeInstance(httpStaticOverlay, "5.5.5.5", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText), - makeInstance(httpStaticOverlay, "6.6.6.6", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"other": "bar"}, PlainText)) - expectServiceInstances(t, sd, httpStatic, 0, instances) - expectEvents(t, events, Event{kind: "eds", host: "*.google.com", namespace: httpStaticOverlay.Namespace, endpoints: len(instances)}) - - // Make a NOP change, expect that there are no changes - createConfigs([]*config.Config{httpStaticOverlayUpdatedNop}, store, t) - expectServiceInstances(t, sd, httpStaticOverlayUpdatedNop, 0, instances) - // TODO this could trigger no changes - expectEvents(t, events, Event{kind: "eds", host: "*.google.com", namespace: httpStaticOverlay.Namespace, endpoints: len(instances)}) - }) - - t.Run("overlapping address", func(t *testing.T) { - // Add another SE with an additional endpoint with a matching address - createConfigs([]*config.Config{httpStaticOverlayUpdatedInstance}, store, t) - instances := append(baseInstances, - makeInstance(httpStaticOverlay, "5.5.5.5", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText), - makeInstance(httpStaticOverlay, "6.6.6.6", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"other": "bar"}, PlainText), - makeInstance(httpStaticOverlay, "6.6.6.6", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"some-new-label": "bar"}, PlainText)) - expectServiceInstances(t, sd, httpStaticOverlayUpdatedInstance, 0, instances) - proxyInstances := []*model.ServiceInstance{ - makeInstance(httpStaticOverlay, "6.6.6.6", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"other": "bar"}, PlainText), - makeInstance(httpStaticOverlay, "6.6.6.6", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"some-new-label": "bar"}, PlainText), - } - expectProxyInstances(t, sd, proxyInstances, "6.6.6.6") - // TODO 45 is wrong - expectEvents(t, events, Event{kind: "eds", host: "*.google.com", namespace: httpStaticOverlay.Namespace, endpoints: len(instances)}) - - // Remove the additional endpoint - createConfigs([]*config.Config{httpStaticOverlayUpdated}, store, t) - instances = append(baseInstances, - makeInstance(httpStaticOverlay, "5.5.5.5", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText), - makeInstance(httpStaticOverlay, "6.6.6.6", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"other": "bar"}, PlainText)) - expectServiceInstances(t, sd, httpStatic, 0, instances) - proxyInstances = []*model.ServiceInstance{ - makeInstance(httpStaticOverlay, "6.6.6.6", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"other": "bar"}, PlainText), - } - expectProxyInstances(t, sd, proxyInstances, "6.6.6.6") - expectEvents(t, events, Event{kind: "eds", host: "*.google.com", namespace: httpStaticOverlay.Namespace, endpoints: len(instances)}) - }) - - t.Run("update removes endpoint", func(t *testing.T) { - // Update the SE for the same host to remove the endpoint - createConfigs([]*config.Config{httpStaticOverlay}, store, t) - instances := append(baseInstances, - makeInstance(httpStaticOverlay, "5.5.5.5", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText)) - expectServiceInstances(t, sd, httpStaticOverlay, 0, instances) - expectEvents(t, events, - Event{kind: "eds", host: "*.google.com", namespace: httpStaticOverlay.Namespace, endpoints: len(instances)}) - }) - - t.Run("different namespace", func(t *testing.T) { - // Update the SE for the same host in a different ns, expect these instances to get added - createConfigs([]*config.Config{httpStaticOverlayUpdatedNs}, store, t) - instances := []*model.ServiceInstance{ - makeInstance(httpStaticOverlayUpdatedNs, "5.5.5.5", 4567, httpStaticOverlayUpdatedNs.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText), - makeInstance(httpStaticOverlayUpdatedNs, "7.7.7.7", 4567, httpStaticOverlayUpdatedNs.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"namespace": "bar"}, PlainText), - } - // This lookup is per-namespace, so we should only see the objects in the same namespace - expectServiceInstances(t, sd, httpStaticOverlayUpdatedNs, 0, instances) - // Expect a full push, as the Service has changed - expectEvents(t, events, - Event{kind: "svcupdate", host: "*.google.com", namespace: "other"}, - Event{kind: "xds", pushReq: &model.PushRequest{ConfigsUpdated: map[model.ConfigKey]struct{}{{Kind: gvk.ServiceEntry, Name: httpStaticOverlayUpdatedNs.Spec.(*networking.ServiceEntry).Hosts[0], Namespace: httpStaticOverlayUpdatedNs.Namespace}: {}}}}) - }) - - t.Run("delete entry", func(t *testing.T) { - // Delete the additional SE in same namespace , expect it to get removed - deleteConfigs([]*config.Config{httpStaticOverlayUpdated}, store, t) - expectServiceInstances(t, sd, httpStatic, 0, baseInstances) - // Check the other namespace is untouched - instances := []*model.ServiceInstance{ - makeInstance(httpStaticOverlayUpdatedNs, "5.5.5.5", 4567, httpStaticOverlayUpdatedNs.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText), - makeInstance(httpStaticOverlayUpdatedNs, "7.7.7.7", 4567, httpStaticOverlayUpdatedNs.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"namespace": "bar"}, PlainText), - } - expectServiceInstances(t, sd, httpStaticOverlayUpdatedNs, 0, instances) - // svcUpdate is not triggered since `httpStatic` is there and has instances, so we should - // not delete the endpoints shards of "*.google.com". We xpect a full push as the service has changed. - expectEvents(t, events, - Event{kind: "xds", pushReq: &model.PushRequest{ConfigsUpdated: map[model.ConfigKey]struct{}{{Kind: gvk.ServiceEntry, Name: "*.google.com", Namespace: httpStaticOverlayUpdated.Namespace}: {}}}}, - ) - - // delete httpStatic, no "*.google.com" service exists now. - deleteConfigs([]*config.Config{httpStatic}, store, t) - // svcUpdate is triggered since "*.google.com" in same namespace is deleted and - // we need to delete endpoint shards. We expect a full push as the service has changed. - expectEvents(t, events, - Event{kind: "svcupdate", host: "*.google.com", namespace: httpStatic.Namespace}, - Event{kind: "xds", pushReq: &model.PushRequest{ConfigsUpdated: map[model.ConfigKey]struct{}{{Kind: gvk.ServiceEntry, Name: "*.google.com", Namespace: httpStaticOverlayUpdated.Namespace}: {}}}}, - ) - - // add back httpStatic - createConfigs([]*config.Config{httpStatic}, store, t) - instances = baseInstances - expectServiceInstances(t, sd, httpStatic, 0, instances) - expectEvents(t, events, - Event{kind: "svcupdate", host: "*.google.com", namespace: httpStatic.Namespace}, - Event{kind: "xds", pushReq: &model.PushRequest{ConfigsUpdated: map[model.ConfigKey]struct{}{{Kind: gvk.ServiceEntry, Name: httpStatic.Spec.(*networking.ServiceEntry).Hosts[0], Namespace: httpStatic.Namespace}: {}}}}) - - // Add back the ServiceEntry, expect these instances to get added - createConfigs([]*config.Config{httpStaticOverlayUpdated}, store, t) - instances = append(baseInstances, - makeInstance(httpStaticOverlay, "5.5.5.5", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText), - makeInstance(httpStaticOverlay, "6.6.6.6", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"other": "bar"}, PlainText)) - expectServiceInstances(t, sd, httpStatic, 0, instances) - // Service change, so we need a full push - expectEvents(t, events, - Event{kind: "svcupdate", host: "*.google.com", namespace: httpStaticOverlay.Namespace}, - Event{kind: "xds", pushReq: &model.PushRequest{ConfigsUpdated: map[model.ConfigKey]struct{}{{Kind: gvk.ServiceEntry, Name: "*.google.com", Namespace: httpStaticOverlayUpdated.Namespace}: {}}}}) - }) - - t.Run("change host", func(t *testing.T) { - // same as httpStaticOverlayUpdated but with an additional host - httpStaticHost := func() *config.Config { - c := httpStaticOverlayUpdated.DeepCopy() - se := c.Spec.(*networking.ServiceEntry) - se.Hosts = append(se.Hosts, "other.com") - return &c - }() - createConfigs([]*config.Config{httpStaticHost}, store, t) - instances := append(baseInstances, - makeInstance(httpStaticOverlay, "5.5.5.5", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText), - makeInstance(httpStaticOverlay, "6.6.6.6", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"other": "bar"}, PlainText)) - // This is not applied, just to make makeInstance pick the right service. - otherHost := func() *config.Config { - c := httpStaticOverlayUpdated.DeepCopy() - se := c.Spec.(*networking.ServiceEntry) - se.Hosts = []string{"other.com"} - return &c - }() - instances2 := []*model.ServiceInstance{ - makeInstance(otherHost, "5.5.5.5", 4567, httpStaticHost.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText), - makeInstance(otherHost, "6.6.6.6", 4567, httpStaticHost.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"other": "bar"}, PlainText), - } - expectServiceInstances(t, sd, httpStaticHost, 0, instances, instances2) - // Service change, so we need a full push - expectEvents(t, events, - Event{kind: "svcupdate", host: "other.com", namespace: httpStaticOverlayUpdated.Namespace}, - Event{kind: "xds", pushReq: &model.PushRequest{ConfigsUpdated: map[model.ConfigKey]struct{}{{Kind: gvk.ServiceEntry, Name: "other.com", Namespace: httpStaticOverlayUpdated.Namespace}: {}}}}) // service added - - // restore this config and remove the added host. - createConfigs([]*config.Config{httpStaticOverlayUpdated}, store, t) - expectEvents(t, events, - Event{kind: "svcupdate", host: "other.com", namespace: httpStatic.Namespace}, - Event{kind: "xds", pushReq: &model.PushRequest{ConfigsUpdated: map[model.ConfigKey]struct{}{{Kind: gvk.ServiceEntry, Name: "other.com", Namespace: httpStaticOverlayUpdated.Namespace}: {}}}}) // service deleted - }) - - t.Run("change dns endpoints", func(t *testing.T) { - // Setup the expected instances for DNS. This will be added/removed from as we add various configs - instances1 := []*model.ServiceInstance{ - makeInstance(tcpDNS, "lon.google.com", 444, tcpDNS.Spec.(*networking.ServiceEntry).Ports[0], - nil, MTLS), - makeInstance(tcpDNS, "in.google.com", 444, tcpDNS.Spec.(*networking.ServiceEntry).Ports[0], - nil, MTLS), - } - - // This is not applied, just to make makeInstance pick the right service. - tcpDNSUpdated := func() *config.Config { - c := tcpDNS.DeepCopy() - se := c.Spec.(*networking.ServiceEntry) - se.Endpoints = []*networking.WorkloadEntry{ - { - Address: "lon.google.com", - Labels: map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}, - }, - } - return &c - }() - - instances2 := []*model.ServiceInstance{ - makeInstance(tcpDNS, "lon.google.com", 444, tcpDNS.Spec.(*networking.ServiceEntry).Ports[0], - nil, MTLS), - } - - createConfigs([]*config.Config{tcpDNS}, store, t) - expectServiceInstances(t, sd, tcpDNS, 0, instances1) - // Service change, so we need a full push - expectEvents(t, events, - Event{kind: "svcupdate", host: "tcpdns.com", namespace: tcpDNS.Namespace}, - Event{kind: "xds", pushReq: &model.PushRequest{ConfigsUpdated: map[model.ConfigKey]struct{}{{Kind: gvk.ServiceEntry, Name: "tcpdns.com", Namespace: tcpDNS.Namespace}: {}}}}) // service added - - // now update the config - createConfigs([]*config.Config{tcpDNSUpdated}, store, t) - expectEvents(t, events, - Event{kind: "xds", pushReq: &model.PushRequest{ConfigsUpdated: map[model.ConfigKey]struct{}{{ - Kind: gvk.ServiceEntry, Name: "tcpdns.com", - Namespace: tcpDNSUpdated.Namespace, - }: {}}}}) // service deleted - expectServiceInstances(t, sd, tcpDNS, 0, instances2) - }) - - t.Run("change workload selector", func(t *testing.T) { - // same as selector but with an additional host - selector1 := func() *config.Config { - c := httpStaticOverlay.DeepCopy() - se := c.Spec.(*networking.ServiceEntry) - se.Hosts = append(se.Hosts, "selector1.com") - se.Endpoints = nil - se.WorkloadSelector = &networking.WorkloadSelector{ - Labels: map[string]string{"app": "wle"}, - } - return &c - }() - createConfigs([]*config.Config{selector1}, store, t) - // Service change, so we need a full push - expectEvents(t, events, - Event{kind: "svcupdate", host: "selector1.com", namespace: httpStaticOverlay.Namespace}, - Event{kind: "svcupdate", host: "*.google.com", namespace: httpStaticOverlay.Namespace}, - - Event{kind: "xds", pushReq: &model.PushRequest{ConfigsUpdated: map[model.ConfigKey]struct{}{ - {Kind: gvk.ServiceEntry, Name: "*.google.com", Namespace: selector1.Namespace}: {}, - {Kind: gvk.ServiceEntry, Name: "selector1.com", Namespace: selector1.Namespace}: {}, - }}}) // service added - - selector1Updated := func() *config.Config { - c := selector1.DeepCopy() - se := c.Spec.(*networking.ServiceEntry) - se.WorkloadSelector = &networking.WorkloadSelector{ - Labels: map[string]string{"app": "wle1"}, - } - return &c - }() - createConfigs([]*config.Config{selector1Updated}, store, t) - expectEvents(t, events, - Event{kind: "svcupdate", host: "*.google.com", namespace: httpStaticOverlay.Namespace}, - Event{kind: "svcupdate", host: "selector1.com", namespace: httpStaticOverlay.Namespace}, - - Event{kind: "xds", pushReq: &model.PushRequest{ConfigsUpdated: map[model.ConfigKey]struct{}{ - {Kind: gvk.ServiceEntry, Name: "*.google.com", Namespace: selector1.Namespace}: {}, - {Kind: gvk.ServiceEntry, Name: "selector1.com", Namespace: selector1.Namespace}: {}, - }}}) // service updated - }) -} - -func TestServiceDiscoveryWorkloadUpdate(t *testing.T) { - store, sd, events, stopFn := initServiceDiscovery() - defer stopFn() - - // Setup a couple workload entries for test. These will be selected by the `selector` SE - wle := createWorkloadEntry("wl", selector.Name, - &networking.WorkloadEntry{ - Address: "2.2.2.2", - Labels: map[string]string{"app": "wle"}, - ServiceAccount: "default", - }) - wle2 := createWorkloadEntry("wl2", selector.Name, - &networking.WorkloadEntry{ - Address: "3.3.3.3", - Labels: map[string]string{"app": "wle"}, - ServiceAccount: "default", - }) - wle3 := createWorkloadEntry("wl3", selector.Name, - &networking.WorkloadEntry{ - Address: "abc.def", - Labels: map[string]string{"app": "wle"}, - ServiceAccount: "default", - }) - dnsWle := createWorkloadEntry("dnswl", dnsSelector.Namespace, - &networking.WorkloadEntry{ - Address: "4.4.4.4", - Labels: map[string]string{"app": "dns-wle"}, - ServiceAccount: "default", - }) - - t.Run("service entry", func(t *testing.T) { - // Add just the ServiceEntry with selector. We should see no instances - createConfigs([]*config.Config{selector}, store, t) - instances := []*model.ServiceInstance{} - expectProxyInstances(t, sd, instances, "2.2.2.2") - expectServiceInstances(t, sd, selector, 0, instances) - expectEvents(t, events, - Event{kind: "svcupdate", host: "selector.com", namespace: selector.Namespace}, - Event{kind: "xds"}) - }) - - t.Run("add workload", func(t *testing.T) { - // Add a WLE, we expect this to update - createConfigs([]*config.Config{wle}, store, t) - - instances := []*model.ServiceInstance{ - makeInstanceWithServiceAccount(selector, "2.2.2.2", 444, - selector.Spec.(*networking.ServiceEntry).Ports[0], - map[string]string{"app": "wle"}, "default"), - makeInstanceWithServiceAccount(selector, "2.2.2.2", 445, - selector.Spec.(*networking.ServiceEntry).Ports[1], - map[string]string{"app": "wle"}, "default"), - } - for _, i := range instances { - i.Endpoint.WorkloadName = "wl" - i.Endpoint.Namespace = selector.Name - } - expectProxyInstances(t, sd, instances, "2.2.2.2") - expectServiceInstances(t, sd, selector, 0, instances) - expectEvents(t, events, - Event{kind: "xds", proxyIP: "2.2.2.2"}, - Event{kind: "eds", host: "selector.com", namespace: selector.Namespace, endpoints: 2}, - ) - }) - - t.Run("add dns service entry", func(t *testing.T) { - // Add just the ServiceEntry with selector. We should see no instances - createConfigs([]*config.Config{dnsSelector}, store, t) - instances := []*model.ServiceInstance{} - expectProxyInstances(t, sd, instances, "4.4.4.4") - expectServiceInstances(t, sd, dnsSelector, 0, instances) - expectEvents(t, events, - Event{kind: "svcupdate", host: "dns.selector.com", namespace: dnsSelector.Namespace}, - Event{kind: "xds"}) - }) - - t.Run("add dns workload", func(t *testing.T) { - // Add a WLE, we expect this to update - createConfigs([]*config.Config{dnsWle}, store, t) - instances := []*model.ServiceInstance{ - makeInstanceWithServiceAccount(dnsSelector, "4.4.4.4", 444, - selector.Spec.(*networking.ServiceEntry).Ports[0], - map[string]string{"app": "dns-wle"}, "default"), - makeInstanceWithServiceAccount(dnsSelector, "4.4.4.4", 445, - selector.Spec.(*networking.ServiceEntry).Ports[1], - map[string]string{"app": "dns-wle"}, "default"), - } - for _, i := range instances { - i.Endpoint.WorkloadName = "dnswl" - i.Endpoint.Namespace = dnsSelector.Namespace - } - expectProxyInstances(t, sd, instances, "4.4.4.4") - expectServiceInstances(t, sd, dnsSelector, 0, instances) - expectEvents(t, events, Event{kind: "xds"}) - }) - - t.Run("another workload", func(t *testing.T) { - // Add a different WLE - createConfigs([]*config.Config{wle2}, store, t) - instances := []*model.ServiceInstance{ - makeInstanceWithServiceAccount(selector, "2.2.2.2", 444, - selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"), - makeInstanceWithServiceAccount(selector, "2.2.2.2", 445, - selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"), - } - for _, i := range instances { - i.Endpoint.WorkloadName = "wl" - i.Endpoint.Namespace = selector.Name - } - expectProxyInstances(t, sd, instances, "2.2.2.2") - instances = append(instances, - makeInstanceWithServiceAccount(selector, "3.3.3.3", 444, - selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"), - makeInstanceWithServiceAccount(selector, "3.3.3.3", 445, - selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default")) - for _, i := range instances[2:] { - i.Endpoint.WorkloadName = "wl2" - i.Endpoint.Namespace = selector.Name - } - expectServiceInstances(t, sd, selector, 0, instances) - expectEvents(t, events, - Event{kind: "xds", proxyIP: "3.3.3.3"}, - Event{kind: "eds", host: "selector.com", namespace: selector.Namespace, endpoints: 4}, - ) - }) - - t.Run("ignore host workload", func(t *testing.T) { - // Add a WLE with host address. Should be ignored by static service entry. - createConfigs([]*config.Config{wle3}, store, t) - instances := []*model.ServiceInstance{ - makeInstanceWithServiceAccount(selector, "2.2.2.2", 444, - selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"), - makeInstanceWithServiceAccount(selector, "2.2.2.2", 445, - selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"), - } - for _, i := range instances { - i.Endpoint.WorkloadName = "wl" - i.Endpoint.Namespace = selector.Name - } - expectProxyInstances(t, sd, instances, "2.2.2.2") - instances = append(instances, - makeInstanceWithServiceAccount(selector, "3.3.3.3", 444, - selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"), - makeInstanceWithServiceAccount(selector, "3.3.3.3", 445, - selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default")) - for _, i := range instances[2:] { - i.Endpoint.WorkloadName = "wl2" - i.Endpoint.Namespace = selector.Name - } - expectServiceInstances(t, sd, selector, 0, instances) - expectEvents(t, events, - Event{kind: "xds", proxyIP: "abc.def"}, - ) - }) - - t.Run("deletion", func(t *testing.T) { - // Delete the configs, it should be gone - deleteConfigs([]*config.Config{wle2}, store, t) - instances := []*model.ServiceInstance{ - makeInstanceWithServiceAccount(selector, "2.2.2.2", 444, - selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"), - makeInstanceWithServiceAccount(selector, "2.2.2.2", 445, - selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"), - } - for _, i := range instances { - i.Endpoint.WorkloadName = "wl" - i.Endpoint.Namespace = selector.Name - } - expectProxyInstances(t, sd, instances, "2.2.2.2") - expectServiceInstances(t, sd, selector, 0, instances) - expectEvents(t, events, Event{kind: "eds", host: "selector.com", namespace: selector.Namespace, endpoints: 2}) - - // Delete the other config - deleteConfigs([]*config.Config{wle}, store, t) - instances = []*model.ServiceInstance{} - expectServiceInstances(t, sd, selector, 0, instances) - expectProxyInstances(t, sd, instances, "2.2.2.2") - expectEvents(t, events, Event{kind: "eds", host: "selector.com", namespace: selector.Namespace, endpoints: 0}) - - // Add the config back - createConfigs([]*config.Config{wle}, store, t) - instances = []*model.ServiceInstance{ - makeInstanceWithServiceAccount(selector, "2.2.2.2", 444, - selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"), - makeInstanceWithServiceAccount(selector, "2.2.2.2", 445, - selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"), - } - for _, i := range instances { - i.Endpoint.WorkloadName = "wl" - i.Endpoint.Namespace = selector.Name - } - expectProxyInstances(t, sd, instances, "2.2.2.2") - expectServiceInstances(t, sd, selector, 0, instances) - expectEvents(t, events, - Event{kind: "xds", proxyIP: "2.2.2.2"}, - Event{kind: "eds", host: "selector.com", namespace: selector.Namespace, endpoints: 2}, - ) - }) -} - -func TestServiceDiscoveryWorkloadChangeLabel(t *testing.T) { - store, sd, events, stopFn := initServiceDiscovery() - defer stopFn() - - wle := createWorkloadEntry("wl", selector.Name, - &networking.WorkloadEntry{ - Address: "2.2.2.2", - Labels: map[string]string{"app": "wle"}, - ServiceAccount: "default", - }) - - wle2 := createWorkloadEntry("wl", selector.Name, - &networking.WorkloadEntry{ - Address: "2.2.2.2", - Labels: map[string]string{"app": "wle2"}, - ServiceAccount: "default", - }) - wle3 := createWorkloadEntry("wl3", selector.Name, - &networking.WorkloadEntry{ - Address: "3.3.3.3", - Labels: map[string]string{"app": "wle"}, - ServiceAccount: "default", - }) - - t.Run("service entry", func(t *testing.T) { - // Add just the ServiceEntry with selector. We should see no instances - createConfigs([]*config.Config{selector}, store, t) - instances := []*model.ServiceInstance{} - expectProxyInstances(t, sd, instances, "2.2.2.2") - expectServiceInstances(t, sd, selector, 0, instances) - expectEvents(t, events, - Event{kind: "svcupdate", host: "selector.com", namespace: selector.Namespace}, - Event{kind: "xds"}) - }) - - t.Run("change label removing all", func(t *testing.T) { - // Add a WLE, we expect this to update - createConfigs([]*config.Config{wle}, store, t) - instances := []*model.ServiceInstance{ - makeInstanceWithServiceAccount(selector, "2.2.2.2", 444, - selector.Spec.(*networking.ServiceEntry).Ports[0], - map[string]string{"app": "wle"}, "default"), - makeInstanceWithServiceAccount(selector, "2.2.2.2", 445, - selector.Spec.(*networking.ServiceEntry).Ports[1], - map[string]string{"app": "wle"}, "default"), - } - for _, i := range instances { - i.Endpoint.WorkloadName = "wl" - i.Endpoint.Namespace = selector.Name - } - expectProxyInstances(t, sd, instances, "2.2.2.2") - expectServiceInstances(t, sd, selector, 0, instances) - expectEvents(t, events, - Event{kind: "xds", proxyIP: "2.2.2.2"}, - Event{kind: "eds", host: "selector.com", namespace: selector.Namespace, endpoints: 2}, - ) - - createConfigs([]*config.Config{wle2}, store, t) - instances = []*model.ServiceInstance{} - expectServiceInstances(t, sd, selector, 0, instances) - expectProxyInstances(t, sd, instances, "2.2.2.2") - expectEvents(t, events, Event{kind: "eds", host: "selector.com", namespace: selector.Namespace, endpoints: 0}) - }) - - t.Run("change label removing one", func(t *testing.T) { - // Add a WLE, we expect this to update - createConfigs([]*config.Config{wle}, store, t) - expectEvents(t, events, - Event{kind: "eds", host: "selector.com", namespace: selector.Namespace, endpoints: 2}, - ) - // add a wle, expect this to be an add - createConfigs([]*config.Config{wle3}, store, t) - instances := []*model.ServiceInstance{ - makeInstanceWithServiceAccount(selector, "2.2.2.2", 444, - selector.Spec.(*networking.ServiceEntry).Ports[0], - map[string]string{"app": "wle"}, "default"), - makeInstanceWithServiceAccount(selector, "2.2.2.2", 445, - selector.Spec.(*networking.ServiceEntry).Ports[1], - map[string]string{"app": "wle"}, "default"), - makeInstanceWithServiceAccount(selector, "3.3.3.3", 444, - selector.Spec.(*networking.ServiceEntry).Ports[0], - map[string]string{"app": "wle"}, "default"), - makeInstanceWithServiceAccount(selector, "3.3.3.3", 445, - selector.Spec.(*networking.ServiceEntry).Ports[1], - map[string]string{"app": "wle"}, "default"), - } - for _, i := range instances[:2] { - i.Endpoint.WorkloadName = "wl" - i.Endpoint.Namespace = selector.Name - } - for _, i := range instances[2:] { - i.Endpoint.WorkloadName = "wl3" - i.Endpoint.Namespace = selector.Name - } - expectProxyInstances(t, sd, instances[:2], "2.2.2.2") - expectProxyInstances(t, sd, instances[2:], "3.3.3.3") - expectServiceInstances(t, sd, selector, 0, instances) - expectEvents(t, events, - Event{kind: "xds", proxyIP: "3.3.3.3"}, - Event{kind: "eds", host: "selector.com", namespace: selector.Namespace, endpoints: 4}, - ) - - createConfigs([]*config.Config{wle2}, store, t) - instances = []*model.ServiceInstance{ - makeInstanceWithServiceAccount(selector, "3.3.3.3", 444, - selector.Spec.(*networking.ServiceEntry).Ports[0], - map[string]string{"app": "wle"}, "default"), - makeInstanceWithServiceAccount(selector, "3.3.3.3", 445, - selector.Spec.(*networking.ServiceEntry).Ports[1], - map[string]string{"app": "wle"}, "default"), - } - for _, i := range instances { - i.Endpoint.WorkloadName = "wl3" - i.Endpoint.Namespace = selector.Name - } - expectServiceInstances(t, sd, selector, 0, instances) - expectProxyInstances(t, sd, instances, "3.3.3.3") - expectEvents(t, events, Event{kind: "eds", host: "selector.com", namespace: selector.Namespace, endpoints: 2}) - }) -} - -func TestServiceDiscoveryWorkloadInstance(t *testing.T) { - store, sd, events, stopFn := initServiceDiscovery() - defer stopFn() - - // Setup a couple of workload instances for test. These will be selected by the `selector` SE - fi1 := &model.WorkloadInstance{ - Name: selector.Name, - Namespace: selector.Namespace, - Endpoint: &model.IstioEndpoint{ - Address: "2.2.2.2", - Labels: map[string]string{"app": "wle"}, - ServiceAccount: spiffe.MustGenSpiffeURI(selector.Name, "default"), - TLSMode: model.IstioMutualTLSModeLabel, - }, - } - - fi2 := &model.WorkloadInstance{ - Name: "some-other-name", - Namespace: selector.Namespace, - Endpoint: &model.IstioEndpoint{ - Address: "3.3.3.3", - Labels: map[string]string{"app": "wle"}, - ServiceAccount: spiffe.MustGenSpiffeURI(selector.Name, "default"), - TLSMode: model.IstioMutualTLSModeLabel, - }, - } - - fi3 := &model.WorkloadInstance{ - Name: "another-name", - Namespace: dnsSelector.Namespace, - Endpoint: &model.IstioEndpoint{ - Address: "2.2.2.2", - Labels: map[string]string{"app": "dns-wle"}, - ServiceAccount: spiffe.MustGenSpiffeURI(dnsSelector.Name, "default"), - TLSMode: model.IstioMutualTLSModeLabel, - }, - } - - t.Run("service entry", func(t *testing.T) { - // Add just the ServiceEntry with selector. We should see no instances - createConfigs([]*config.Config{selector}, store, t) - instances := []*model.ServiceInstance{} - expectProxyInstances(t, sd, instances, "2.2.2.2") - expectServiceInstances(t, sd, selector, 0, instances) - expectEvents(t, events, - Event{kind: "svcupdate", host: "selector.com", namespace: selector.Namespace}, - Event{kind: "xds"}) - }) - - t.Run("add another service entry", func(t *testing.T) { - createConfigs([]*config.Config{dnsSelector}, store, t) - instances := []*model.ServiceInstance{} - expectProxyInstances(t, sd, instances, "2.2.2.2") - expectServiceInstances(t, sd, dnsSelector, 0, instances) - expectEvents(t, events, - Event{kind: "svcupdate", host: "dns.selector.com", namespace: dnsSelector.Namespace}, - Event{kind: "xds"}) - }) - - t.Run("add workload instance", func(t *testing.T) { - // Add a workload instance, we expect this to update - callInstanceHandlers([]*model.WorkloadInstance{fi1}, sd, model.EventAdd, t) - instances := []*model.ServiceInstance{ - makeInstanceWithServiceAccount(selector, "2.2.2.2", 444, - selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"), - makeInstanceWithServiceAccount(selector, "2.2.2.2", 445, - selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"), - } - expectProxyInstances(t, sd, instances, "2.2.2.2") - expectServiceInstances(t, sd, selector, 0, instances) - expectEvents(t, events, Event{kind: "eds", host: "selector.com", namespace: selector.Namespace, endpoints: 2}) - }) - - t.Run("another workload instance", func(t *testing.T) { - // Add a different instance - callInstanceHandlers([]*model.WorkloadInstance{fi2}, sd, model.EventAdd, t) - instances := []*model.ServiceInstance{ - makeInstanceWithServiceAccount(selector, "2.2.2.2", 444, - selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"), - makeInstanceWithServiceAccount(selector, "2.2.2.2", 445, - selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"), - } - expectProxyInstances(t, sd, instances, "2.2.2.2") - instances = append(instances, - makeInstanceWithServiceAccount(selector, "3.3.3.3", 444, - selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"), - makeInstanceWithServiceAccount(selector, "3.3.3.3", 445, - selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default")) - expectServiceInstances(t, sd, selector, 0, instances) - expectEvents(t, events, Event{kind: "eds", host: "selector.com", namespace: selector.Namespace, endpoints: 4}) - }) - - t.Run("delete workload instance", func(t *testing.T) { - // Delete the instances, it should be gone - callInstanceHandlers([]*model.WorkloadInstance{fi2}, sd, model.EventDelete, t) - instances := []*model.ServiceInstance{ - makeInstanceWithServiceAccount(selector, "2.2.2.2", 444, - selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"), - makeInstanceWithServiceAccount(selector, "2.2.2.2", 445, - selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"), - } - expectProxyInstances(t, sd, instances, "2.2.2.2") - expectServiceInstances(t, sd, selector, 0, instances) - expectEvents(t, events, Event{kind: "eds", host: "selector.com", namespace: selector.Namespace, endpoints: 2}) - - key := instancesKey{namespace: selector.Namespace, hostname: "selector.com"} - namespacedName := types.NamespacedName{Namespace: selector.Namespace, Name: selector.Name} - if len(sd.serviceInstances.ip2instance) != 1 { - t.Fatalf("service instances store `ip2instance` memory leak, expect 1, got %d", len(sd.serviceInstances.ip2instance)) - } - if len(sd.serviceInstances.instances[key]) != 1 { - t.Fatalf("service instances store `instances` memory leak, expect 1, got %d", len(sd.serviceInstances.instances[key])) - } - if len(sd.serviceInstances.instancesBySE[namespacedName]) != 1 { - t.Fatalf("service instances store `instancesBySE` memory leak, expect 1, got %d", len(sd.serviceInstances.instancesBySE[namespacedName])) - } - - // The following sections mimic this scenario: - // f1 starts terminating, f3 picks up the IP, f3 delete event (pod - // not ready yet) comes before f1 - // - // Delete f3 event - callInstanceHandlers([]*model.WorkloadInstance{fi3}, sd, model.EventDelete, t) - expectProxyInstances(t, sd, instances, "2.2.2.2") - expectServiceInstances(t, sd, selector, 0, instances) - - // Delete f1 event - callInstanceHandlers([]*model.WorkloadInstance{fi1}, sd, model.EventDelete, t) - instances = []*model.ServiceInstance{} - expectProxyInstances(t, sd, instances, "2.2.2.2") - expectServiceInstances(t, sd, selector, 0, instances) - expectEvents(t, events, Event{kind: "eds", host: "selector.com", namespace: selector.Namespace, endpoints: 0}) - - if len(sd.serviceInstances.ip2instance) != 0 { - t.Fatalf("service instances store `ip2instance` memory leak, expect 0, got %d", len(sd.serviceInstances.ip2instance)) - } - if len(sd.serviceInstances.instances[key]) != 0 { - t.Fatalf("service instances store `instances` memory leak, expect 0, got %d", len(sd.serviceInstances.instances[key])) - } - if len(sd.serviceInstances.instancesBySE[namespacedName]) != 0 { - t.Fatalf("service instances store `instancesBySE` memory leak, expect 0, got %d", len(sd.serviceInstances.instancesBySE[namespacedName])) - } - - // Add f3 event - callInstanceHandlers([]*model.WorkloadInstance{fi3}, sd, model.EventAdd, t) - instances = []*model.ServiceInstance{ - makeInstanceWithServiceAccount(dnsSelector, "2.2.2.2", 444, - dnsSelector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "dns-wle"}, "default"), - makeInstanceWithServiceAccount(dnsSelector, "2.2.2.2", 445, - dnsSelector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "dns-wle"}, "default"), - } - expectProxyInstances(t, sd, instances, "2.2.2.2") - expectServiceInstances(t, sd, dnsSelector, 0, instances) - expectEvents(t, events, Event{kind: "eds", host: "dns.selector.com", namespace: dnsSelector.Namespace, endpoints: 2}) - }) -} - -func expectProxyInstances(t testing.TB, sd *Controller, expected []*model.ServiceInstance, ip string) { - t.Helper() - // The system is eventually consistent, so add some retries - retry.UntilSuccessOrFail(t, func() error { - instances := sd.GetProxyServiceInstances(&model.Proxy{IPAddresses: []string{ip}, Metadata: &model.NodeMetadata{}}) - sortServiceInstances(instances) - sortServiceInstances(expected) - if err := compare(t, instances, expected); err != nil { - return err - } - return nil - }, retry.Converge(2), retry.Timeout(time.Second*5)) -} - -func expectEvents(t testing.TB, ch chan Event, events ...Event) { - cmpPushRequest := func(expectReq, gotReq *model.PushRequest) bool { - var expectConfigs, gotConfigs map[model.ConfigKey]struct{} - if expectReq != nil { - expectConfigs = expectReq.ConfigsUpdated - } - if gotReq != nil { - gotConfigs = gotReq.ConfigsUpdated - } - - return reflect.DeepEqual(expectConfigs, gotConfigs) - } - - t.Helper() - for _, event := range events { - got := waitForEvent(t, ch) - if event.pushReq != nil { - if !cmpPushRequest(event.pushReq, got.pushReq) { - t.Fatalf("expected event %+v %+v, got %+v %+v", event, event.pushReq, got, got.pushReq) - } - } - - event.pushReq, got.pushReq = nil, nil - if event != got { - t.Fatalf("expected event %+v, got %+v", event, got) - } - } - // Drain events - for { - select { - case e := <-ch: - t.Logf("ignoring event %+v", e) - if len(events) == 0 { - t.Fatalf("got unexpected event %+v", e) - } - default: - return - } - } -} - -func expectServiceInstances(t testing.TB, sd *Controller, cfg *config.Config, port int, expected ...[]*model.ServiceInstance) { - t.Helper() - svcs := convertServices(*cfg) - if len(svcs) != len(expected) { - t.Fatalf("got more services than expected: %v vs %v", len(svcs), len(expected)) - } - // The system is eventually consistent, so add some retries - retry.UntilSuccessOrFail(t, func() error { - for i, svc := range svcs { - instances := sd.InstancesByPort(svc, port, nil) - sortServiceInstances(instances) - sortServiceInstances(expected[i]) - if err := compare(t, instances, expected[i]); err != nil { - return fmt.Errorf("%d: %v", i, err) - } - } - return nil - }, retry.Converge(2), retry.Timeout(time.Second*1)) -} - -func TestServiceDiscoveryGetProxyServiceInstances(t *testing.T) { - store, sd, _, stopFn := initServiceDiscovery() - defer stopFn() - - createConfigs([]*config.Config{httpStatic, tcpStatic}, store, t) - - expectProxyInstances(t, sd, []*model.ServiceInstance{ - makeInstance(httpStatic, "2.2.2.2", 7080, httpStatic.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS), - makeInstance(httpStatic, "2.2.2.2", 18080, httpStatic.Spec.(*networking.ServiceEntry).Ports[1], nil, MTLS), - makeInstance(tcpStatic, "2.2.2.2", 444, tcpStatic.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS), - }, "2.2.2.2") -} - -// Keeping this test for legacy - but it never happens in real life. -func TestServiceDiscoveryInstances(t *testing.T) { - store, sd, _, stopFn := initServiceDiscovery() - defer stopFn() - - createConfigs([]*config.Config{httpDNS, tcpStatic}, store, t) - - expectServiceInstances(t, sd, httpDNS, 0, []*model.ServiceInstance{ - makeInstance(httpDNS, "us.google.com", 7080, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS), - makeInstance(httpDNS, "us.google.com", 18080, httpDNS.Spec.(*networking.ServiceEntry).Ports[1], nil, MTLS), - makeInstance(httpDNS, "uk.google.com", 1080, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS), - makeInstance(httpDNS, "uk.google.com", 8080, httpDNS.Spec.(*networking.ServiceEntry).Ports[1], nil, MTLS), - makeInstance(httpDNS, "de.google.com", 80, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"foo": "bar"}, MTLS), - makeInstance(httpDNS, "de.google.com", 8080, httpDNS.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"foo": "bar"}, MTLS), - }) -} - -// Keeping this test for legacy - but it never happens in real life. -func TestServiceDiscoveryInstances1Port(t *testing.T) { - store, sd, _, stopFn := initServiceDiscovery() - defer stopFn() - - createConfigs([]*config.Config{httpDNS, tcpStatic}, store, t) - - expectServiceInstances(t, sd, httpDNS, 80, []*model.ServiceInstance{ - makeInstance(httpDNS, "us.google.com", 7080, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS), - makeInstance(httpDNS, "uk.google.com", 1080, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS), - makeInstance(httpDNS, "de.google.com", 80, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"foo": "bar"}, MTLS), - }) -} - -func TestNonServiceConfig(t *testing.T) { - store, sd, _, stopFn := initServiceDiscovery() - defer stopFn() - - // Create a non-service configuration element. This should not affect the service registry at all. - cfg := config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(), - Name: "fakeDestinationRule", - Namespace: "default", - Domain: "cluster.local", - CreationTimestamp: GlobalTime, - }, - Spec: &networking.DestinationRule{ - Host: "fakehost", - }, - } - _, err := store.Create(cfg) - if err != nil { - t.Errorf("error occurred crearting ServiceEntry config: %v", err) - } - - // Now create some service entries and verify that it's added to the registry. - createConfigs([]*config.Config{httpDNS, tcpStatic}, store, t) - expectServiceInstances(t, sd, httpDNS, 80, []*model.ServiceInstance{ - makeInstance(httpDNS, "us.google.com", 7080, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS), - makeInstance(httpDNS, "uk.google.com", 1080, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS), - makeInstance(httpDNS, "de.google.com", 80, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"foo": "bar"}, MTLS), - }) -} - -// nolint: lll -func TestServicesDiff(t *testing.T) { - updatedHTTPDNS := &config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Serviceentries.Resource().GroupVersionKind(), - Name: "httpDNS", - Namespace: "httpDNS", - CreationTimestamp: GlobalTime, - Labels: map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}, - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"*.google.com", "*.mail.com"}, - Ports: []*networking.Port{ - {Number: 80, Name: "http-port", Protocol: "http"}, - {Number: 8080, Name: "http-alt-port", Protocol: "http"}, - }, - Endpoints: []*networking.WorkloadEntry{ - { - Address: "us.google.com", - Ports: map[string]uint32{"http-port": 7080, "http-alt-port": 18080}, - Labels: map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}, - }, - { - Address: "uk.google.com", - Ports: map[string]uint32{"http-port": 1080}, - Labels: map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}, - }, - { - Address: "de.google.com", - Labels: map[string]string{"foo": "bar", label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}, - }, - }, - Location: networking.ServiceEntry_MESH_EXTERNAL, - Resolution: networking.ServiceEntry_DNS, - }, - } - - updatedHTTPDNSPort := func() *config.Config { - c := updatedHTTPDNS.DeepCopy() - se := c.Spec.(*networking.ServiceEntry) - var ports []*networking.Port - ports = append(ports, se.Ports...) - ports = append(ports, &networking.Port{Number: 9090, Name: "http-new-port", Protocol: "http"}) - se.Ports = ports - return &c - }() - - updatedEndpoint := func() *config.Config { - c := updatedHTTPDNS.DeepCopy() - se := c.Spec.(*networking.ServiceEntry) - var endpoints []*networking.WorkloadEntry - endpoints = append(endpoints, se.Endpoints...) - endpoints = append(endpoints, &networking.WorkloadEntry{ - Address: "in.google.com", - Labels: map[string]string{"foo": "bar", label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}, - }) - se.Endpoints = endpoints - return &c - }() - - stringsToHosts := func(hosts []string) []host.Name { - ret := make([]host.Name, len(hosts)) - for i, hostname := range hosts { - ret[i] = host.Name(hostname) - } - return ret - } - - cases := []struct { - name string - current *config.Config - new *config.Config - - added []host.Name - deleted []host.Name - updated []host.Name - unchanged []host.Name - }{ - { - name: "same config", - current: updatedHTTPDNS, - new: updatedHTTPDNS, - unchanged: stringsToHosts(updatedHTTPDNS.Spec.(*networking.ServiceEntry).Hosts), - }, - { - name: "same config with different name", - current: updatedHTTPDNS, - new: func() *config.Config { - c := updatedHTTPDNS.DeepCopy() - c.Name = "httpDNS1" - return &c - }(), - unchanged: stringsToHosts(updatedHTTPDNS.Spec.(*networking.ServiceEntry).Hosts), - }, - { - name: "different resolution", - current: updatedHTTPDNS, - new: func() *config.Config { - c := updatedHTTPDNS.DeepCopy() - c.Spec.(*networking.ServiceEntry).Resolution = networking.ServiceEntry_NONE - return &c - }(), - updated: stringsToHosts(updatedHTTPDNS.Spec.(*networking.ServiceEntry).Hosts), - }, - { - name: "config modified with added/deleted host", - current: updatedHTTPDNS, - new: func() *config.Config { - c := updatedHTTPDNS.DeepCopy() - se := c.Spec.(*networking.ServiceEntry) - se.Hosts = []string{"*.google.com", "host.com"} - return &c - }(), - added: []host.Name{"host.com"}, - deleted: []host.Name{"*.mail.com"}, - unchanged: []host.Name{"*.google.com"}, - }, - { - name: "config modified with additional port", - current: updatedHTTPDNS, - new: updatedHTTPDNSPort, - updated: stringsToHosts(updatedHTTPDNS.Spec.(*networking.ServiceEntry).Hosts), - }, - { - name: "same config with additional endpoint", - current: updatedHTTPDNS, - new: updatedEndpoint, - unchanged: stringsToHosts(updatedHTTPDNS.Spec.(*networking.ServiceEntry).Hosts), - }, - } - - servicesHostnames := func(services []*model.Service) []host.Name { - if len(services) == 0 { - return nil - } - ret := make([]host.Name, len(services)) - for i, svc := range services { - ret[i] = svc.Hostname - } - return ret - } - - for _, tt := range cases { - if tt.name != "same config with additional endpoint" { - continue - } - t.Run(tt.name, func(t *testing.T) { - as := convertServices(*tt.current) - bs := convertServices(*tt.new) - added, deleted, updated, unchanged := servicesDiff(as, bs) - for i, item := range []struct { - hostnames []host.Name - services []*model.Service - }{ - {tt.added, added}, - {tt.deleted, deleted}, - {tt.updated, updated}, - {tt.unchanged, unchanged}, - } { - if !reflect.DeepEqual(servicesHostnames(item.services), item.hostnames) { - t.Errorf("ServicesChanged %d got %v, want %v", i, servicesHostnames(item.services), item.hostnames) - } - } - }) - } -} - -func sortServices(services []*model.Service) { - sort.Slice(services, func(i, j int) bool { return services[i].Hostname < services[j].Hostname }) - for _, service := range services { - sortPorts(service.Ports) - } -} - -func sortServiceInstances(instances []*model.ServiceInstance) { - labelsToSlice := func(labels labels.Instance) []string { - out := make([]string, 0, len(labels)) - for k, v := range labels { - out = append(out, fmt.Sprintf("%s=%s", k, v)) - } - sort.Strings(out) - return out - } - - sort.Slice(instances, func(i, j int) bool { - if instances[i].Service.Hostname == instances[j].Service.Hostname { - if instances[i].Endpoint.EndpointPort == instances[j].Endpoint.EndpointPort { - if instances[i].Endpoint.Address == instances[j].Endpoint.Address { - if len(instances[i].Endpoint.Labels) == len(instances[j].Endpoint.Labels) { - iLabels := labelsToSlice(instances[i].Endpoint.Labels) - jLabels := labelsToSlice(instances[j].Endpoint.Labels) - for k := range iLabels { - if iLabels[k] < jLabels[k] { - return true - } - } - } - return len(instances[i].Endpoint.Labels) < len(instances[j].Endpoint.Labels) - } - return instances[i].Endpoint.Address < instances[j].Endpoint.Address - } - return instances[i].Endpoint.EndpointPort < instances[j].Endpoint.EndpointPort - } - return instances[i].Service.Hostname < instances[j].Service.Hostname - }) -} - -func sortPorts(ports []*model.Port) { - sort.Slice(ports, func(i, j int) bool { - if ports[i].Port == ports[j].Port { - if ports[i].Name == ports[j].Name { - return ports[i].Protocol < ports[j].Protocol - } - return ports[i].Name < ports[j].Name - } - return ports[i].Port < ports[j].Port - }) -} - -func Test_autoAllocateIP_conditions(t *testing.T) { - tests := []struct { - name string - inServices []*model.Service - wantServices []*model.Service - }{ - { - name: "no allocation for passthrough", - inServices: []*model.Service{ - { - Hostname: "foo.com", - Resolution: model.Passthrough, - DefaultAddress: "0.0.0.0", - }, - }, - wantServices: []*model.Service{ - { - Hostname: "foo.com", - Resolution: model.Passthrough, - DefaultAddress: "0.0.0.0", - }, - }, - }, - { - name: "no allocation if address exists", - inServices: []*model.Service{ - { - Hostname: "foo.com", - Resolution: model.ClientSideLB, - DefaultAddress: "1.1.1.1", - }, - }, - wantServices: []*model.Service{ - { - Hostname: "foo.com", - Resolution: model.ClientSideLB, - DefaultAddress: "1.1.1.1", - }, - }, - }, - { - name: "no allocation if hostname is wildcard", - inServices: []*model.Service{ - { - Hostname: "*.foo.com", - Resolution: model.ClientSideLB, - DefaultAddress: "1.1.1.1", - }, - }, - wantServices: []*model.Service{ - { - Hostname: "*.foo.com", - Resolution: model.ClientSideLB, - DefaultAddress: "1.1.1.1", - }, - }, - }, - { - name: "allocate IP for clientside lb", - inServices: []*model.Service{ - { - Hostname: "foo.com", - Resolution: model.ClientSideLB, - DefaultAddress: "0.0.0.0", - }, - }, - wantServices: []*model.Service{ - { - Hostname: "foo.com", - Resolution: model.ClientSideLB, - DefaultAddress: "0.0.0.0", - AutoAllocatedIPv4Address: "240.240.0.1", - AutoAllocatedIPv6Address: "2001:2::f0f0:1", - }, - }, - }, - { - name: "allocate IP for dns lb", - inServices: []*model.Service{ - { - Hostname: "foo.com", - Resolution: model.DNSLB, - DefaultAddress: "0.0.0.0", - }, - }, - wantServices: []*model.Service{ - { - Hostname: "foo.com", - Resolution: model.DNSLB, - DefaultAddress: "0.0.0.0", - AutoAllocatedIPv4Address: "240.240.0.1", - AutoAllocatedIPv6Address: "2001:2::f0f0:1", - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := autoAllocateIPs(tt.inServices) - if got[0].AutoAllocatedIPv4Address != tt.wantServices[0].AutoAllocatedIPv4Address { - t.Errorf("autoAllocateIPs() AutoAllocatedIPv4Address = %v, want %v", - got[0].AutoAllocatedIPv4Address, tt.wantServices[0].AutoAllocatedIPv4Address) - } - if got[0].AutoAllocatedIPv6Address != tt.wantServices[0].AutoAllocatedIPv6Address { - t.Errorf("autoAllocateIPs() AutoAllocatedIPv4Address = %v, want %v", - got[0].AutoAllocatedIPv6Address, tt.wantServices[0].AutoAllocatedIPv6Address) - } - }) - } -} - -func Test_autoAllocateIP_values(t *testing.T) { - inServices := make([]*model.Service, 512) - for i := 0; i < 512; i++ { - temp := model.Service{ - Hostname: "foo.com", - Resolution: model.ClientSideLB, - DefaultAddress: constants.UnspecifiedIP, - } - inServices[i] = &temp - } - gotServices := autoAllocateIPs(inServices) - - // out of the 512 IPs, we dont expect the following IPs - // 240.240.0.0 - // 240.240.0.255 - // 240.240.1.0 - // 240.240.1.255 - // 240.240.2.0 - // 240.240.2.255 - // The last IP should be 240.240.2.4 - doNotWant := map[string]bool{ - "240.240.0.0": true, - "240.240.0.255": true, - "240.240.1.0": true, - "240.240.1.255": true, - "240.240.2.0": true, - "240.240.2.255": true, - } - expectedLastIP := "240.240.2.4" - if gotServices[len(gotServices)-1].AutoAllocatedIPv4Address != expectedLastIP { - t.Errorf("expected last IP address to be %s, got %s", expectedLastIP, gotServices[len(gotServices)-1].AutoAllocatedIPv4Address) - } - - gotIPMap := make(map[string]bool) - for _, svc := range gotServices { - if svc.AutoAllocatedIPv4Address == "" || doNotWant[svc.AutoAllocatedIPv4Address] { - t.Errorf("unexpected value for auto allocated IP address %s", svc.AutoAllocatedIPv4Address) - } - if gotIPMap[svc.AutoAllocatedIPv4Address] { - t.Errorf("multiple allocations of same IP address to different services: %s", svc.AutoAllocatedIPv4Address) - } - gotIPMap[svc.AutoAllocatedIPv4Address] = true - } -} - -func TestWorkloadEntryOnlyMode(t *testing.T) { - store, registry, _, cleanup := initServiceDiscoveryWithOpts(true) - defer cleanup() - createConfigs([]*config.Config{httpStatic}, store, t) - svcs := registry.Services() - if len(svcs) > 0 { - t.Fatalf("expected 0 services, got %d", len(svcs)) - } - svc := registry.GetService("*.google.com") - if svc != nil { - t.Fatalf("expected nil, got %v", svc) - } -} - -func BenchmarkServiceEntryHandler(b *testing.B) { - _, sd := initServiceDiscoveryWithoutEvents(b) - stopCh := make(chan struct{}) - go sd.Run(stopCh) - defer close(stopCh) - for i := 0; i < b.N; i++ { - sd.serviceEntryHandler(config.Config{}, *httpDNS, model.EventAdd) - sd.serviceEntryHandler(config.Config{}, *httpDNSRR, model.EventAdd) - sd.serviceEntryHandler(config.Config{}, *tcpDNS, model.EventAdd) - sd.serviceEntryHandler(config.Config{}, *tcpStatic, model.EventAdd) - - sd.serviceEntryHandler(config.Config{}, *httpDNS, model.EventDelete) - sd.serviceEntryHandler(config.Config{}, *httpDNSRR, model.EventDelete) - sd.serviceEntryHandler(config.Config{}, *tcpDNS, model.EventDelete) - sd.serviceEntryHandler(config.Config{}, *tcpStatic, model.EventDelete) - } -} - -func BenchmarkWorkloadInstanceHandler(b *testing.B) { - store, sd := initServiceDiscoveryWithoutEvents(b) - stopCh := make(chan struct{}) - go sd.Run(stopCh) - defer close(stopCh) - // Add just the ServiceEntry with selector. We should see no instances - createConfigs([]*config.Config{selector, dnsSelector}, store, b) - - // Setup a couple of workload instances for test. These will be selected by the `selector` SE - fi1 := &model.WorkloadInstance{ - Name: selector.Name, - Namespace: selector.Namespace, - Endpoint: &model.IstioEndpoint{ - Address: "2.2.2.2", - Labels: map[string]string{"app": "wle"}, - ServiceAccount: spiffe.MustGenSpiffeURI(selector.Name, "default"), - TLSMode: model.IstioMutualTLSModeLabel, - }, - } - - fi2 := &model.WorkloadInstance{ - Name: "some-other-name", - Namespace: selector.Namespace, - Endpoint: &model.IstioEndpoint{ - Address: "3.3.3.3", - Labels: map[string]string{"app": "wle"}, - ServiceAccount: spiffe.MustGenSpiffeURI(selector.Name, "default"), - TLSMode: model.IstioMutualTLSModeLabel, - }, - } - - fi3 := &model.WorkloadInstance{ - Name: "another-name", - Namespace: dnsSelector.Namespace, - Endpoint: &model.IstioEndpoint{ - Address: "2.2.2.2", - Labels: map[string]string{"app": "dns-wle"}, - ServiceAccount: spiffe.MustGenSpiffeURI(dnsSelector.Name, "default"), - TLSMode: model.IstioMutualTLSModeLabel, - }, - } - for i := 0; i < b.N; i++ { - sd.WorkloadInstanceHandler(fi1, model.EventAdd) - sd.WorkloadInstanceHandler(fi2, model.EventAdd) - sd.WorkloadInstanceHandler(fi3, model.EventDelete) - - sd.WorkloadInstanceHandler(fi2, model.EventDelete) - sd.WorkloadInstanceHandler(fi1, model.EventDelete) - sd.WorkloadInstanceHandler(fi3, model.EventDelete) - } -} - -func BenchmarkWorkloadEntryHandler(b *testing.B) { - // Setup a couple workload entries for test. These will be selected by the `selector` SE - wle := createWorkloadEntry("wl", selector.Name, - &networking.WorkloadEntry{ - Address: "2.2.2.2", - Labels: map[string]string{"app": "wle"}, - ServiceAccount: "default", - }) - wle2 := createWorkloadEntry("wl2", selector.Name, - &networking.WorkloadEntry{ - Address: "3.3.3.3", - Labels: map[string]string{"app": "wle"}, - ServiceAccount: "default", - }) - dnsWle := createWorkloadEntry("dnswl", dnsSelector.Namespace, - &networking.WorkloadEntry{ - Address: "4.4.4.4", - Labels: map[string]string{"app": "dns-wle"}, - ServiceAccount: "default", - }) - - store, sd := initServiceDiscoveryWithoutEvents(b) - stopCh := make(chan struct{}) - go sd.Run(stopCh) - defer close(stopCh) - // Add just the ServiceEntry with selector. We should see no instances - createConfigs([]*config.Config{selector}, store, b) - - for i := 0; i < b.N; i++ { - sd.workloadEntryHandler(config.Config{}, *wle, model.EventAdd) - sd.workloadEntryHandler(config.Config{}, *dnsWle, model.EventAdd) - sd.workloadEntryHandler(config.Config{}, *wle2, model.EventAdd) - - sd.workloadEntryHandler(config.Config{}, *wle, model.EventDelete) - sd.workloadEntryHandler(config.Config{}, *dnsWle, model.EventDelete) - sd.workloadEntryHandler(config.Config{}, *wle2, model.EventDelete) - } -} diff --git a/pilot/pkg/serviceregistry/serviceentry/store.go b/pilot/pkg/serviceregistry/serviceentry/store.go deleted file mode 100644 index 019c64533..000000000 --- a/pilot/pkg/serviceregistry/serviceentry/store.go +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package serviceentry - -import ( - "k8s.io/apimachinery/pkg/types" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" -) - -// stores all the service instances from SE, WLE and pods -type serviceInstancesStore struct { - ip2instance map[string][]*model.ServiceInstance - // service instances by hostname -> config - instances map[instancesKey]map[configKey][]*model.ServiceInstance - // instances only for serviceentry - instancesBySE map[types.NamespacedName]map[configKey][]*model.ServiceInstance -} - -func (s *serviceInstancesStore) getByIP(ip string) []*model.ServiceInstance { - return s.ip2instance[ip] -} - -func (s *serviceInstancesStore) getAll() []*model.ServiceInstance { - all := []*model.ServiceInstance{} - for _, instances := range s.ip2instance { - all = append(all, instances...) - } - return all -} - -func (s *serviceInstancesStore) getByKey(key instancesKey) []*model.ServiceInstance { - all := []*model.ServiceInstance{} - for _, instances := range s.instances[key] { - all = append(all, instances...) - } - return all -} - -func (s *serviceInstancesStore) deleteInstances(key configKey, instances []*model.ServiceInstance) { - for _, i := range instances { - delete(s.instances[makeInstanceKey(i)], key) - delete(s.ip2instance, i.Endpoint.Address) - } -} - -// addInstances add the instances to the store. -func (s *serviceInstancesStore) addInstances(key configKey, instances []*model.ServiceInstance) { - for _, instance := range instances { - ikey := makeInstanceKey(instance) - if _, f := s.instances[ikey]; !f { - s.instances[ikey] = map[configKey][]*model.ServiceInstance{} - } - s.instances[ikey][key] = append(s.instances[ikey][key], instance) - s.ip2instance[instance.Endpoint.Address] = append(s.ip2instance[instance.Endpoint.Address], instance) - } -} - -func (s *serviceInstancesStore) updateInstances(key configKey, instances []*model.ServiceInstance) { - // first delete - s.deleteInstances(key, instances) - - // second add - s.addInstances(key, instances) -} - -func (s *serviceInstancesStore) getServiceEntryInstances(key types.NamespacedName) map[configKey][]*model.ServiceInstance { - return s.instancesBySE[key] -} - -func (s *serviceInstancesStore) updateServiceEntryInstances(key types.NamespacedName, instances map[configKey][]*model.ServiceInstance) { - s.instancesBySE[key] = instances -} - -func (s *serviceInstancesStore) updateServiceEntryInstancesPerConfig(key types.NamespacedName, cKey configKey, instances []*model.ServiceInstance) { - if s.instancesBySE[key] == nil { - s.instancesBySE[key] = map[configKey][]*model.ServiceInstance{} - } - s.instancesBySE[key][cKey] = instances -} - -func (s *serviceInstancesStore) deleteServiceEntryInstances(key types.NamespacedName, cKey configKey) { - delete(s.instancesBySE[key], cKey) -} - -func (s *serviceInstancesStore) deleteAllServiceEntryInstances(key types.NamespacedName) { - delete(s.instancesBySE, key) -} - -// stores all the services converted from serviceEntries -type serviceStore struct { - // services keeps track of all services - mainly used to return from Services() to avoid reconversion. - servicesBySE map[types.NamespacedName][]*model.Service - allocateNeeded bool -} - -// getAllServices return all the services. -func (s *serviceStore) getAllServices() []*model.Service { - var out []*model.Service - for _, svcs := range s.servicesBySE { - out = append(out, svcs...) - } - return model.SortServicesByCreationTime(out) -} - -func (s *serviceStore) getServices(key types.NamespacedName) []*model.Service { - return s.servicesBySE[key] -} - -func (s *serviceStore) deleteServices(key types.NamespacedName) { - delete(s.servicesBySE, key) -} - -func (s *serviceStore) updateServices(key types.NamespacedName, services []*model.Service) { - s.servicesBySE[key] = services - s.allocateNeeded = true -} diff --git a/pilot/pkg/serviceregistry/serviceentry/store_test.go b/pilot/pkg/serviceregistry/serviceentry/store_test.go deleted file mode 100644 index 3e85f24c7..000000000 --- a/pilot/pkg/serviceregistry/serviceentry/store_test.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package serviceentry - -import ( - "reflect" - "testing" -) - -import ( - networking "istio.io/api/networking/v1alpha3" - "k8s.io/apimachinery/pkg/types" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" -) - -func TestServiceInstancesStore(t *testing.T) { - store := serviceInstancesStore{ - ip2instance: map[string][]*model.ServiceInstance{}, - instances: map[instancesKey]map[configKey][]*model.ServiceInstance{}, - instancesBySE: map[types.NamespacedName]map[configKey][]*model.ServiceInstance{}, - } - instances := []*model.ServiceInstance{ - makeInstance(selector, "1.1.1.1", 444, selector.Spec.(*networking.ServiceEntry).Ports[0], nil, PlainText), - makeInstance(selector, "1.1.1.1", 445, selector.Spec.(*networking.ServiceEntry).Ports[1], nil, PlainText), - makeInstance(dnsSelector, "1.1.1.1", 444, dnsSelector.Spec.(*networking.ServiceEntry).Ports[0], nil, PlainText), - } - cKey := configKey{ - namespace: "default", - name: "test-wle", - } - store.addInstances(cKey, instances) - - // 1. test getByIP - gotInstances := store.getByIP("1.1.1.1") - if !reflect.DeepEqual(instances, gotInstances) { - t.Errorf("got unexpected instances : %v", gotInstances) - } - - // 2. test getAll - gotInstances = store.getAll() - if !reflect.DeepEqual(instances, gotInstances) { - t.Errorf("got unexpected instances : %v", gotInstances) - } - - // 3. test getByKey - gotInstances = store.getByKey(instancesKey{ - hostname: "selector.com", - namespace: "selector", - }) - expected := []*model.ServiceInstance{ - makeInstance(selector, "1.1.1.1", 444, selector.Spec.(*networking.ServiceEntry).Ports[0], nil, PlainText), - makeInstance(selector, "1.1.1.1", 445, selector.Spec.(*networking.ServiceEntry).Ports[1], nil, PlainText), - } - if !reflect.DeepEqual(gotInstances, expected) { - t.Errorf("got unexpected instances : %v", gotInstances) - } - - // 4. test getServiceEntryInstances - expectedSeInstances := map[configKey][]*model.ServiceInstance{cKey: { - makeInstance(selector, "1.1.1.1", 444, selector.Spec.(*networking.ServiceEntry).Ports[0], nil, PlainText), - makeInstance(selector, "1.1.1.1", 445, selector.Spec.(*networking.ServiceEntry).Ports[1], nil, PlainText), - }} - key := types.NamespacedName{Namespace: selector.Namespace, Name: selector.Name} - store.updateServiceEntryInstances(key, expectedSeInstances) - - gotSeInstances := store.getServiceEntryInstances(key) - if !reflect.DeepEqual(gotSeInstances, expectedSeInstances) { - t.Errorf("got unexpected se instances : %v", gotSeInstances) - } - - // 5. test deleteServiceEntryInstances - store.deleteServiceEntryInstances(key, cKey) - gotSeInstances = store.getServiceEntryInstances(key) - if len(gotSeInstances) != 0 { - t.Errorf("got unexpected instances %v", gotSeInstances) - } - - // 6. test deleteAllServiceEntryInstances - store.deleteAllServiceEntryInstances(key) - gotSeInstances = store.getServiceEntryInstances(key) - if len(gotSeInstances) != 0 { - t.Errorf("got unexpected instances %v", gotSeInstances) - } - - // 7. test deleteInstances - store.deleteInstances(cKey, instances) - gotInstances = store.getAll() - if len(gotInstances) != 0 { - t.Errorf("got unexpected instances %v", gotSeInstances) - } -} - -func TestServiceStore(t *testing.T) { - store := serviceStore{ - servicesBySE: map[types.NamespacedName][]*model.Service{}, - } - - expectedServices := []*model.Service{ - makeService("*.istio.io", "httpDNSRR", constants.UnspecifiedIP, map[string]int{"http-port": 80, "http-alt-port": 8080}, true, model.DNSRoundRobinLB), - makeService("*.istio.io", "httpDNSRR", constants.UnspecifiedIP, map[string]int{"http-port": 80, "http-alt-port": 8080}, true, model.DNSLB), - } - - store.updateServices(types.NamespacedName{Namespace: httpDNSRR.Namespace, Name: httpDNSRR.Name}, expectedServices) - got := store.getServices(types.NamespacedName{Namespace: httpDNSRR.Namespace, Name: httpDNSRR.Name}) - if !reflect.DeepEqual(got, expectedServices) { - t.Errorf("got unexpected services %v", got) - } - - got = store.getAllServices() - if !reflect.DeepEqual(got, expectedServices) { - t.Errorf("got unexpected services %v", got) - } - if !store.allocateNeeded { - t.Errorf("expected allocate needed") - } - store.allocateNeeded = false - store.deleteServices(types.NamespacedName{Namespace: httpDNSRR.Namespace, Name: httpDNSRR.Name}) - got = store.getAllServices() - if got != nil { - t.Errorf("got unexpected services %v", got) - } - if store.allocateNeeded { - t.Errorf("expected no allocate needed") - } -} diff --git a/pilot/pkg/serviceregistry/serviceentry/util.go b/pilot/pkg/serviceregistry/serviceentry/util.go deleted file mode 100644 index 44b8b8098..000000000 --- a/pilot/pkg/serviceregistry/serviceentry/util.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package serviceentry - -import ( - networking "istio.io/api/networking/v1alpha3" - "k8s.io/apimachinery/pkg/types" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" -) - -func getWorkloadServiceEntries(ses []config.Config, wle *networking.WorkloadEntry) map[types.NamespacedName]*config.Config { - out := make(map[types.NamespacedName]*config.Config) - for i, cfg := range ses { - se := cfg.Spec.(*networking.ServiceEntry) - if se.WorkloadSelector != nil && labels.Instance(se.WorkloadSelector.Labels).SubsetOf(wle.Labels) { - out[types.NamespacedName{Name: cfg.Name, Namespace: cfg.Namespace}] = &ses[i] - } - } - - return out -} - -// returns a set of objects that are in `old` but not in `curr` -// For example: -// old = {a1, a2, a3} -// curr = {a1, a2, a4, a5} -// difference(old, curr) = {a3} -func difference(old, curr map[types.NamespacedName]*config.Config) []types.NamespacedName { - var out []types.NamespacedName - for key := range old { - if _, ok := curr[key]; !ok { - out = append(out, key) - } - } - - return out -} diff --git a/pilot/pkg/serviceregistry/serviceentry/util_test.go b/pilot/pkg/serviceregistry/serviceentry/util_test.go deleted file mode 100644 index 43ce24389..000000000 --- a/pilot/pkg/serviceregistry/serviceentry/util_test.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package serviceentry - -import ( - "reflect" - "testing" -) - -import ( - networking "istio.io/api/networking/v1alpha3" - "k8s.io/apimachinery/pkg/types" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -func TestGetWorkloadServiceEntries(t *testing.T) { - se1 := config.Config{ - Meta: config.Meta{GroupVersionKind: gvk.ServiceEntry, Namespace: "default", Name: "se-1"}, - Spec: &networking.ServiceEntry{ - Hosts: []string{"*.google.com"}, - Ports: []*networking.Port{ - {Number: 80, Name: "http-number", Protocol: "http"}, - {Number: 8080, Name: "http2-number", Protocol: "http2"}, - }, - WorkloadSelector: &networking.WorkloadSelector{ - Labels: map[string]string{"app": "foo"}, - }, - }, - } - se2 := config.Config{ - Meta: config.Meta{GroupVersionKind: gvk.ServiceEntry, Namespace: "default", Name: "se-2"}, - Spec: &networking.ServiceEntry{ - Hosts: []string{"*.google.com"}, - Ports: []*networking.Port{ - {Number: 80, Name: "http-number", Protocol: "http"}, - {Number: 8080, Name: "http2-number", Protocol: "http2"}, - }, - WorkloadSelector: &networking.WorkloadSelector{ - Labels: map[string]string{"app": "bar"}, - }, - }, - } - - se3 := config.Config{ - Meta: config.Meta{GroupVersionKind: gvk.ServiceEntry, Namespace: "default", Name: "se-3"}, - Spec: &networking.ServiceEntry{ - Hosts: []string{"www.wikipedia.org"}, - Ports: []*networking.Port{ - {Number: 80, Name: "http-number", Protocol: "http"}, - {Number: 8080, Name: "http2-number", Protocol: "http2"}, - }, - WorkloadSelector: &networking.WorkloadSelector{ - Labels: map[string]string{"app": "foo"}, - }, - }, - } - ses := []config.Config{se1, se2, se3} - - wle := &networking.WorkloadEntry{ - Address: "2.3.4.5", - Labels: map[string]string{ - "app": "foo", - "version": "v1", - }, - Ports: map[string]uint32{ - "http-number": 8081, - "http2-number": 8088, - }, - } - - expected := map[types.NamespacedName]*config.Config{ - {Namespace: "default", Name: "se-1"}: &se1, - {Namespace: "default", Name: "se-3"}: &se3, - } - got := getWorkloadServiceEntries(ses, wle) - if !reflect.DeepEqual(got, expected) { - t.Errorf("recv unexpected se: %v", got) - } -} - -func TestCompareServiceEntries(t *testing.T) { - oldSes := map[types.NamespacedName]*config.Config{ - {Namespace: "default", Name: "se-1"}: {}, - {Namespace: "default", Name: "se-2"}: {}, - {Namespace: "default", Name: "se-3"}: {}, - } - currSes := map[types.NamespacedName]*config.Config{ - {Namespace: "default", Name: "se-2"}: {}, - {Namespace: "default", Name: "se-4"}: {}, - {Namespace: "default", Name: "se-5"}: {}, - } - - expectedUnselected := map[types.NamespacedName]*config.Config{ - {Namespace: "default", Name: "se-1"}: {}, - {Namespace: "default", Name: "se-3"}: {}, - } - unSelected := difference(oldSes, currSes) - - if len(unSelected) != len(expectedUnselected) { - t.Errorf("got unexpected unSelected ses %v", unSelected) - } - for _, se := range unSelected { - if _, ok := expectedUnselected[se]; !ok { - t.Errorf("got unexpected unSelected se %v", se) - } - } -} diff --git a/pilot/pkg/serviceregistry/serviceentry/workloadentry.go b/pilot/pkg/serviceregistry/serviceentry/workloadentry.go deleted file mode 100644 index 48a0a2de7..000000000 --- a/pilot/pkg/serviceregistry/serviceentry/workloadentry.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package serviceentry - -import ( - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/network" -) - -// return the mesh network for the workload entry. Empty string if not found. -func (s *Controller) workloadEntryNetwork(wle *networking.WorkloadEntry) network.ID { - if s == nil { - return "" - } - // 1. first check the wle.Network - if wle.Network != "" { - return network.ID(wle.Network) - } - - // 2. fall back to the passed in getNetworkCb func. - if s.networkIDCallback != nil { - return s.networkIDCallback(wle.Address, wle.Labels) - } - return "" -} diff --git a/pilot/pkg/serviceregistry/serviceregistry_test.go b/pilot/pkg/serviceregistry/serviceregistry_test.go deleted file mode 100644 index f3d9e3159..000000000 --- a/pilot/pkg/serviceregistry/serviceregistry_test.go +++ /dev/null @@ -1,1346 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package serviceregistry_test - -import ( - "context" - "encoding/json" - "fmt" - "reflect" - "sort" - "testing" - "time" -) - -import ( - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/api/meta/v1alpha1" - networking "istio.io/api/networking/v1alpha3" - v1 "k8s.io/api/core/v1" - discovery "k8s.io/api/discovery/v1" - kerrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/kubernetes" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model/status" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/aggregate" - kubecontroller "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube/controller" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/serviceentry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - "github.com/apache/dubbo-go-pixiu/pilot/test/util" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - kubeclient "github.com/apache/dubbo-go-pixiu/pkg/kube" - istiotest "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -func setupTest(t *testing.T) ( - *kubecontroller.Controller, - *serviceentry.Controller, - model.ConfigStoreController, - kubernetes.Interface, - *xds.FakeXdsUpdater) { - t.Helper() - client := kubeclient.NewFakeClient() - - eventch := make(chan xds.FakeXdsEvent, 100) - - xdsUpdater := &xds.FakeXdsUpdater{ - Events: eventch, - } - meshWatcher := mesh.NewFixedWatcher(&meshconfig.MeshConfig{}) - kc := kubecontroller.NewController( - client, - kubecontroller.Options{ - XDSUpdater: xdsUpdater, - DomainSuffix: "cluster.local", - MeshWatcher: meshWatcher, - MeshServiceController: aggregate.NewController(aggregate.Options{MeshHolder: meshWatcher}), - }, - ) - configController := memory.NewController(memory.Make(collections.Pilot)) - - stop := make(chan struct{}) - t.Cleanup(func() { - close(stop) - }) - go configController.Run(stop) - - istioStore := model.MakeIstioStore(configController) - se := serviceentry.NewController(configController, istioStore, xdsUpdater) - client.RunAndWait(stop) - - kc.AppendWorkloadHandler(se.WorkloadInstanceHandler) - se.AppendWorkloadHandler(kc.WorkloadInstanceHandler) - - go kc.Run(stop) - go se.Run(stop) - - return kc, se, configController, client.Kube(), xdsUpdater -} - -// TestWorkloadInstances is effectively an integration test of composing the Kubernetes service registry with the -// external service registry, which have cross-references by workload instances. -func TestWorkloadInstances(t *testing.T) { - istiotest.SetBoolForTest(t, &features.WorkloadEntryHealthChecks, true) - port := &networking.Port{ - Name: "http", - Number: 80, - Protocol: "http", - } - labels := map[string]string{ - "app": "foo", - } - namespace := "namespace" - serviceEntry := config.Config{ - Meta: config.Meta{ - Name: "service-entry", - Namespace: namespace, - GroupVersionKind: gvk.ServiceEntry, - Domain: "cluster.local", - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"service.namespace.svc.cluster.local"}, - Ports: []*networking.Port{port}, - WorkloadSelector: &networking.WorkloadSelector{ - Labels: labels, - }, - Resolution: networking.ServiceEntry_STATIC, - }, - } - service := &v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "service", - Namespace: namespace, - }, - Spec: v1.ServiceSpec{ - Ports: []v1.ServicePort{{ - Name: "http", - Port: 80, - }}, - Selector: labels, - ClusterIP: "9.9.9.9", - }, - } - headlessService := &v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "service", - Namespace: namespace, - }, - Spec: v1.ServiceSpec{ - Ports: []v1.ServicePort{{ - Name: "http", - Port: 80, - }}, - Selector: labels, - ClusterIP: v1.ClusterIPNone, - }, - } - pod := &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "pod", - Namespace: namespace, - Labels: labels, - }, - Status: v1.PodStatus{ - PodIP: "1.2.3.4", - Phase: v1.PodPending, - }, - } - workloadEntry := config.Config{ - Meta: config.Meta{ - Name: "workload", - Namespace: namespace, - GroupVersionKind: gvk.WorkloadEntry, - Domain: "cluster.local", - }, - Spec: &networking.WorkloadEntry{ - Address: "2.3.4.5", - Labels: labels, - }, - } - expectedSvc := &model.Service{ - Hostname: "service.namespace.svc.cluster.local", - Ports: []*model.Port{{ - Name: "http", - Port: 80, - Protocol: "http", - }, { - Name: "http2", - Port: 90, - Protocol: "http", - }}, - Attributes: model.ServiceAttributes{ - Namespace: namespace, - Name: "service", - LabelSelectors: labels, - }, - } - - t.Run("Kubernetes only", func(t *testing.T) { - kc, _, _, kube, _ := setupTest(t) - makeService(t, kube, service) - makePod(t, kube, pod) - createEndpoints(t, kube, service.Name, namespace, []v1.EndpointPort{{Name: "http", Port: 80}}, []string{pod.Status.PodIP}) - - instances := []ServiceInstanceResponse{{ - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: pod.Status.PodIP, - Port: 80, - }} - expectServiceInstances(t, kc, expectedSvc, 80, instances) - }) - - t.Run("Kubernetes only: headless service", func(t *testing.T) { - kc, _, _, kube, xdsUpdater := setupTest(t) - makeService(t, kube, headlessService) - xdsUpdater.WaitOrFail(t, "svcupdate") - makePod(t, kube, pod) - createEndpoints(t, kube, service.Name, namespace, []v1.EndpointPort{{Name: "http", Port: 80}}, []string{pod.Status.PodIP}) - xdsUpdater.WaitOrFail(t, "eds") - xdsUpdater.WaitOrFail(t, "xds") - instances := []ServiceInstanceResponse{{ - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: pod.Status.PodIP, - Port: 80, - }} - expectServiceInstances(t, kc, expectedSvc, 80, instances) - }) - - t.Run("Kubernetes only: endpoint occur earlier", func(t *testing.T) { - kc, _, _, kube, xdsUpdater := setupTest(t) - makePod(t, kube, pod) - - createEndpoints(t, kube, service.Name, namespace, []v1.EndpointPort{{Name: "http", Port: 80}}, []string{pod.Status.PodIP}) - waitForEdsUpdate(t, xdsUpdater, 1) - - // make service populated later than endpoint - makeService(t, kube, service) - waitForEdsUpdate(t, xdsUpdater, 1) - - instances := []ServiceInstanceResponse{{ - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: pod.Status.PodIP, - Port: 80, - }} - expectServiceInstances(t, kc, expectedSvc, 80, instances) - }) - - t.Run("External only: workLoadEntry port and serviceEntry target port is not set, use serviceEntry port.number", func(t *testing.T) { - _, wc, store, _, _ := setupTest(t) - makeIstioObject(t, store, serviceEntry) - makeIstioObject(t, store, workloadEntry) - - instances := []ServiceInstanceResponse{{ - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: workloadEntry.Spec.(*networking.WorkloadEntry).Address, - Port: 80, - }} - expectServiceInstances(t, wc, expectedSvc, 80, instances) - }) - - t.Run("External only: the port name of the workloadEntry and serviceEntry does match, use workloadEntry port to override", func(t *testing.T) { - _, wc, store, _, _ := setupTest(t) - makeIstioObject(t, store, serviceEntry) - makeIstioObject(t, store, config.Config{ - Meta: config.Meta{ - Name: "workload", - Namespace: namespace, - GroupVersionKind: gvk.WorkloadEntry, - Domain: "cluster.local", - }, - Spec: &networking.WorkloadEntry{ - Address: "2.3.4.5", - Labels: labels, - Ports: map[string]uint32{ - serviceEntry.Spec.(*networking.ServiceEntry).Ports[0].Name: 8080, - }, - }, - }) - - instances := []ServiceInstanceResponse{{ - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: workloadEntry.Spec.(*networking.WorkloadEntry).Address, - Port: 8080, - }} - expectServiceInstances(t, wc, expectedSvc, 80, instances) - }) - - t.Run("External only: workloadEntry port is not set, use target port", func(t *testing.T) { - _, wc, store, _, _ := setupTest(t) - makeIstioObject(t, store, config.Config{ - Meta: config.Meta{ - Name: "service-entry", - Namespace: namespace, - GroupVersionKind: gvk.ServiceEntry, - Domain: "cluster.local", - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"service.namespace.svc.cluster.local"}, - Ports: []*networking.Port{{ - Name: "http", - Number: 80, - Protocol: "http", - TargetPort: 8080, - }}, - WorkloadSelector: &networking.WorkloadSelector{ - Labels: labels, - }, - }, - }) - makeIstioObject(t, store, workloadEntry) - - instances := []ServiceInstanceResponse{{ - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: workloadEntry.Spec.(*networking.WorkloadEntry).Address, - Port: 8080, - }} - expectServiceInstances(t, wc, expectedSvc, 80, instances) - }) - - t.Run("External only: the port name of the workloadEntry and serviceEntry does not match, use target port", func(t *testing.T) { - _, wc, store, _, _ := setupTest(t) - makeIstioObject(t, store, config.Config{ - Meta: config.Meta{ - Name: "service-entry", - Namespace: namespace, - GroupVersionKind: gvk.ServiceEntry, - Domain: "cluster.local", - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"service.namespace.svc.cluster.local"}, - Ports: []*networking.Port{{ - Name: "http", - Number: 80, - Protocol: "http", - TargetPort: 8080, - }}, - WorkloadSelector: &networking.WorkloadSelector{ - Labels: labels, - }, - }, - }) - makeIstioObject(t, store, config.Config{ - Meta: config.Meta{ - Name: "workload", - Namespace: namespace, - GroupVersionKind: gvk.WorkloadEntry, - Domain: "cluster.local", - }, - Spec: &networking.WorkloadEntry{ - Address: "2.3.4.5", - Labels: labels, - Ports: map[string]uint32{ - "different-port-name": 8081, - }, - }, - }) - - instances := []ServiceInstanceResponse{{ - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: workloadEntry.Spec.(*networking.WorkloadEntry).Address, - Port: 8080, - }} - expectServiceInstances(t, wc, expectedSvc, 80, instances) - }) - - t.Run("External only: the port name of the workloadEntry and serviceEntry does not match, "+ - "and the serivceEntry target port is not set, use serviceEntry port.number", func(t *testing.T) { - _, wc, store, _, _ := setupTest(t) - makeIstioObject(t, store, serviceEntry) - makeIstioObject(t, store, config.Config{ - Meta: config.Meta{ - Name: "workload", - Namespace: namespace, - GroupVersionKind: gvk.WorkloadEntry, - Domain: "cluster.local", - }, - Spec: &networking.WorkloadEntry{ - Address: "2.3.4.5", - Labels: labels, - Ports: map[string]uint32{ - "different-port-name": 8081, - }, - }, - }) - - instances := []ServiceInstanceResponse{{ - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: workloadEntry.Spec.(*networking.WorkloadEntry).Address, - Port: 80, - }} - expectServiceInstances(t, wc, expectedSvc, 80, instances) - }) - - t.Run("Service selects WorkloadEntry", func(t *testing.T) { - kc, _, store, kube, _ := setupTest(t) - makeService(t, kube, service) - makeIstioObject(t, store, workloadEntry) - - instances := []ServiceInstanceResponse{{ - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: workloadEntry.Spec.(*networking.WorkloadEntry).Address, - Port: 80, - }} - expectServiceInstances(t, kc, expectedSvc, 80, instances) - }) - - t.Run("Service selects WorkloadEntry: wle occur earlier", func(t *testing.T) { - kc, _, store, kube, xdsUpdater := setupTest(t) - makeIstioObject(t, store, workloadEntry) - - // Wait no event pushed when workload entry created as no service entry - select { - case ev := <-xdsUpdater.Events: - t.Fatalf("Got %s event, expect none", ev.Kind) - case <-time.After(40 * time.Millisecond): - } - - makeService(t, kube, service) - event := xdsUpdater.WaitOrFail(t, "edscache") - if event.Endpoints != 1 { - t.Errorf("expecting 1 endpoints, but got %d ", event.Endpoints) - } - - instances := []ServiceInstanceResponse{{ - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: workloadEntry.Spec.(*networking.WorkloadEntry).Address, - Port: 80, - }} - expectServiceInstances(t, kc, expectedSvc, 80, instances) - }) - - t.Run("Service selects both pods and WorkloadEntry", func(t *testing.T) { - kc, _, store, kube, xdsUpdater := setupTest(t) - makeService(t, kube, service) - xdsUpdater.WaitOrFail(t, "svcupdate") - - makeIstioObject(t, store, workloadEntry) - xdsUpdater.WaitOrFail(t, "eds") - - makePod(t, kube, pod) - createEndpoints(t, kube, service.Name, namespace, []v1.EndpointPort{{Name: "http", Port: 80}}, []string{pod.Status.PodIP}) - waitForEdsUpdate(t, xdsUpdater, 2) - - instances := []ServiceInstanceResponse{ - { - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: pod.Status.PodIP, - Port: 80, - }, - { - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: workloadEntry.Spec.(*networking.WorkloadEntry).Address, - Port: 80, - }, - } - expectServiceInstances(t, kc, expectedSvc, 80, instances) - }) - - t.Run("Service selects both pods and WorkloadEntry: wle occur earlier", func(t *testing.T) { - kc, _, store, kube, xdsUpdater := setupTest(t) - makeIstioObject(t, store, workloadEntry) - - // Wait no event pushed when workload entry created as no service entry - select { - case ev := <-xdsUpdater.Events: - t.Fatalf("Got %s event, expect none", ev.Kind) - case <-time.After(200 * time.Millisecond): - } - - makePod(t, kube, pod) - createEndpoints(t, kube, service.Name, namespace, []v1.EndpointPort{{Name: "http", Port: 80}}, []string{pod.Status.PodIP}) - waitForEdsUpdate(t, xdsUpdater, 1) - - makeService(t, kube, service) - waitForEdsUpdate(t, xdsUpdater, 2) - - instances := []ServiceInstanceResponse{ - { - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: pod.Status.PodIP, - Port: 80, - }, - { - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: workloadEntry.Spec.(*networking.WorkloadEntry).Address, - Port: 80, - }, - } - expectServiceInstances(t, kc, expectedSvc, 80, instances) - }) - - t.Run("Service selects WorkloadEntry with port name", func(t *testing.T) { - kc, _, store, kube, _ := setupTest(t) - makeService(t, kube, &v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "service", - Namespace: namespace, - }, - Spec: v1.ServiceSpec{ - Ports: []v1.ServicePort{{ - Name: "my-port", - Port: 80, - }}, - Selector: labels, - ClusterIP: "9.9.9.9", - }, - }) - makeIstioObject(t, store, config.Config{ - Meta: config.Meta{ - Name: "workload", - Namespace: namespace, - GroupVersionKind: gvk.WorkloadEntry, - Domain: "cluster.local", - }, - Spec: &networking.WorkloadEntry{ - Address: "2.3.4.5", - Labels: labels, - Ports: map[string]uint32{ - "my-port": 8080, - }, - }, - }) - - instances := []ServiceInstanceResponse{{ - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: workloadEntry.Spec.(*networking.WorkloadEntry).Address, - Port: 8080, - }} - expectServiceInstances(t, kc, expectedSvc, 80, instances) - }) - - t.Run("Service selects WorkloadEntry with targetPort name", func(t *testing.T) { - kc, _, store, kube, _ := setupTest(t) - makeService(t, kube, &v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "service", - Namespace: namespace, - }, - Spec: v1.ServiceSpec{ - Ports: []v1.ServicePort{{ - Name: "http", - Port: 80, - TargetPort: intstr.Parse("my-port"), - }}, - Selector: labels, - ClusterIP: "9.9.9.9", - }, - }) - makeIstioObject(t, store, config.Config{ - Meta: config.Meta{ - Name: "workload", - Namespace: namespace, - GroupVersionKind: gvk.WorkloadEntry, - Domain: "cluster.local", - }, - Spec: &networking.WorkloadEntry{ - Address: "2.3.4.5", - Labels: labels, - Ports: map[string]uint32{ - "my-port": 8080, - }, - }, - }) - - instances := []ServiceInstanceResponse{{ - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: workloadEntry.Spec.(*networking.WorkloadEntry).Address, - Port: 8080, - }} - expectServiceInstances(t, kc, expectedSvc, 80, instances) - }) - - t.Run("Service selects WorkloadEntry with targetPort number", func(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - makeService(t, s.KubeClient(), &v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "service", - Namespace: namespace, - }, - Spec: v1.ServiceSpec{ - Ports: []v1.ServicePort{ - { - Name: "http", - Port: 80, - TargetPort: intstr.FromInt(8080), - }, - { - Name: "http2", - Port: 90, - TargetPort: intstr.FromInt(9090), - }, - }, - Selector: labels, - ClusterIP: "9.9.9.9", - }, - }) - makeIstioObject(t, s.Store(), config.Config{ - Meta: config.Meta{ - Name: "workload", - Namespace: namespace, - GroupVersionKind: gvk.WorkloadEntry, - Domain: "cluster.local", - }, - Spec: &networking.WorkloadEntry{ - Address: "2.3.4.5", - Labels: labels, - }, - }) - - instances := []ServiceInstanceResponse{{ - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: workloadEntry.Spec.(*networking.WorkloadEntry).Address, - Port: 8080, - }} - expectServiceInstances(t, s.KubeRegistry, expectedSvc, 80, instances) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", []string{"2.3.4.5:8080"}) - instances = []ServiceInstanceResponse{{ - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: workloadEntry.Spec.(*networking.WorkloadEntry).Address, - Port: 9090, - }} - expectServiceInstances(t, s.KubeRegistry, expectedSvc, 90, instances) - expectEndpoints(t, s, "outbound|90||service.namespace.svc.cluster.local", []string{"2.3.4.5:9090"}) - }) - - t.Run("ServiceEntry selects Pod", func(t *testing.T) { - _, wc, store, kube, _ := setupTest(t) - makeIstioObject(t, store, serviceEntry) - makePod(t, kube, pod) - - instances := []ServiceInstanceResponse{{ - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: pod.Status.PodIP, - Port: 80, - }} - expectServiceInstances(t, wc, expectedSvc, 80, instances) - }) - - t.Run("ServiceEntry selects Pod that is in transit states", func(t *testing.T) { - _, wc, store, kube, _ := setupTest(t) - makeIstioObject(t, store, serviceEntry) - makePod(t, kube, pod) - - instances := []ServiceInstanceResponse{{ - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: pod.Status.PodIP, - Port: 80, - }} - expectServiceInstances(t, wc, expectedSvc, 80, instances) - - // when pods become unready, we should see the instances being removed from the registry - setPodUnready(pod) - _, err := kube.CoreV1().Pods(pod.Namespace).UpdateStatus(context.TODO(), pod, metav1.UpdateOptions{}) - if err != nil { - t.Fatal(err) - } - expectServiceInstances(t, wc, expectedSvc, 80, []ServiceInstanceResponse{}) - - setPodReady(pod) - _, err = kube.CoreV1().Pods(pod.Namespace).UpdateStatus(context.TODO(), pod, metav1.UpdateOptions{}) - if err != nil { - t.Fatal(err) - } - expectServiceInstances(t, wc, expectedSvc, 80, instances) - }) - - t.Run("ServiceEntry selects Pod with targetPort number", func(t *testing.T) { - _, wc, store, kube, _ := setupTest(t) - makeIstioObject(t, store, config.Config{ - Meta: config.Meta{ - Name: "service-entry", - Namespace: namespace, - GroupVersionKind: gvk.ServiceEntry, - Domain: "cluster.local", - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"service.namespace.svc.cluster.local"}, - Ports: []*networking.Port{{ - Name: "http", - Number: 80, - Protocol: "http", - TargetPort: 8080, - }}, - WorkloadSelector: &networking.WorkloadSelector{ - Labels: labels, - }, - }, - }) - makePod(t, kube, pod) - - instances := []ServiceInstanceResponse{{ - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: pod.Status.PodIP, - Port: 8080, - }} - expectServiceInstances(t, wc, expectedSvc, 80, instances) - }) - - t.Run("All directions", func(t *testing.T) { - kc, wc, store, kube, _ := setupTest(t) - makeService(t, kube, service) - makeIstioObject(t, store, serviceEntry) - - makePod(t, kube, pod) - createEndpoints(t, kube, service.Name, namespace, []v1.EndpointPort{{Name: "http", Port: 80}}, []string{pod.Status.PodIP}) - makeIstioObject(t, store, workloadEntry) - - instances := []ServiceInstanceResponse{ - { - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: pod.Status.PodIP, - Port: 80, - }, - { - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: workloadEntry.Spec.(*networking.WorkloadEntry).Address, - Port: 80, - }, - } - - expectServiceInstances(t, wc, expectedSvc, 80, instances) - expectServiceInstances(t, kc, expectedSvc, 80, instances) - }) - - t.Run("All directions with deletion", func(t *testing.T) { - kc, wc, store, kube, _ := setupTest(t) - makeService(t, kube, service) - makeIstioObject(t, store, serviceEntry) - - makePod(t, kube, pod) - createEndpoints(t, kube, service.Name, namespace, []v1.EndpointPort{{Name: "http", Port: 80}}, []string{pod.Status.PodIP}) - makeIstioObject(t, store, workloadEntry) - - instances := []ServiceInstanceResponse{ - { - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: pod.Status.PodIP, - Port: 80, - }, - { - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: workloadEntry.Spec.(*networking.WorkloadEntry).Address, - Port: 80, - }, - } - expectServiceInstances(t, wc, expectedSvc, 80, instances) - expectServiceInstances(t, kc, expectedSvc, 80, instances) - - _ = kube.CoreV1().Pods(pod.Namespace).Delete(context.TODO(), pod.Name, metav1.DeleteOptions{}) - _ = kube.CoreV1().Endpoints(pod.Namespace).Delete(context.TODO(), "service", metav1.DeleteOptions{}) - _ = store.Delete(gvk.WorkloadEntry, workloadEntry.Name, workloadEntry.Namespace, nil) - expectServiceInstances(t, wc, expectedSvc, 80, []ServiceInstanceResponse{}) - expectServiceInstances(t, kc, expectedSvc, 80, []ServiceInstanceResponse{}) - }) - - t.Run("Service selects WorkloadEntry: update service", func(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - makeService(t, s.KubeClient(), service) - makeIstioObject(t, s.Store(), workloadEntry) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", []string{"2.3.4.5:80"}) - - newSvc := service.DeepCopy() - newSvc.Spec.Ports[0].Port = 8080 - makeService(t, s.KubeClient(), newSvc) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", nil) - expectEndpoints(t, s, "outbound|8080||service.namespace.svc.cluster.local", []string{"2.3.4.5:8080"}) - - newSvc.Spec.Ports[0].TargetPort = intstr.IntOrString{IntVal: 9090} - makeService(t, s.KubeClient(), newSvc) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", nil) - expectEndpoints(t, s, "outbound|8080||service.namespace.svc.cluster.local", []string{"2.3.4.5:9090"}) - - if err := s.KubeClient().CoreV1().Services(newSvc.Namespace).Delete(context.Background(), newSvc.Name, metav1.DeleteOptions{}); err != nil { - t.Fatal(err) - } - expectEndpoints(t, s, "outbound|8080||service.namespace.svc.cluster.local", nil) - }) - - t.Run("Service selects WorkloadEntry: update workloadEntry", func(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - makeService(t, s.KubeClient(), service) - makeIstioObject(t, s.Store(), workloadEntry) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", []string{"2.3.4.5:80"}) - - newWE := workloadEntry.DeepCopy() - newWE.Spec.(*networking.WorkloadEntry).Address = "3.4.5.6" - makeIstioObject(t, s.Store(), newWE) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", []string{"3.4.5.6:80"}) - - if err := s.Store().Delete(gvk.WorkloadEntry, newWE.Name, newWE.Namespace, nil); err != nil { - t.Fatal(err) - } - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", nil) - }) - - t.Run("ServiceEntry selects Pod: update service entry", func(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - makeIstioObject(t, s.Store(), serviceEntry) - makePod(t, s.KubeClient(), pod) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", []string{"1.2.3.4:80"}) - - newSE := serviceEntry.DeepCopy() - newSE.Spec.(*networking.ServiceEntry).Ports = []*networking.Port{{ - Name: "http", - Number: 80, - Protocol: "http", - TargetPort: 8080, - }} - makeIstioObject(t, s.Store(), newSE) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", []string{"1.2.3.4:8080"}) - - newSE = newSE.DeepCopy() - newSE.Spec.(*networking.ServiceEntry).Ports = []*networking.Port{{ - Name: "http", - Number: 9090, - Protocol: "http", - TargetPort: 9091, - }} - makeIstioObject(t, s.Store(), newSE) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", nil) - expectEndpoints(t, s, "outbound|9090||service.namespace.svc.cluster.local", []string{"1.2.3.4:9091"}) - - if err := s.Store().Delete(gvk.ServiceEntry, newSE.Name, newSE.Namespace, nil); err != nil { - t.Fatal(err) - } - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", nil) - expectEndpoints(t, s, "outbound|9090||service.namespace.svc.cluster.local", nil) - }) - - t.Run("ServiceEntry selects Pod: update pod", func(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - makeIstioObject(t, s.Store(), serviceEntry) - makePod(t, s.KubeClient(), pod) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", []string{"1.2.3.4:80"}) - - newPod := pod.DeepCopy() - newPod.Status.PodIP = "2.3.4.5" - makePod(t, s.KubeClient(), newPod) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", []string{"2.3.4.5:80"}) - - if err := s.KubeClient().CoreV1().Pods(newPod.Namespace).Delete(context.Background(), newPod.Name, metav1.DeleteOptions{}); err != nil { - t.Fatal(err) - } - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", nil) - }) - - t.Run("ServiceEntry selects Pod: deleting pod", func(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - makeIstioObject(t, s.Store(), serviceEntry) - makePod(t, s.KubeClient(), pod) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", []string{"1.2.3.4:80"}) - - // Simulate pod being deleted by setting deletion timestamp - newPod := pod.DeepCopy() - newPod.DeletionTimestamp = &metav1.Time{Time: time.Now()} - makePod(t, s.KubeClient(), newPod) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", nil) - - if err := s.KubeClient().CoreV1().Pods(newPod.Namespace).Delete(context.Background(), newPod.Name, metav1.DeleteOptions{}); err != nil { - t.Fatal(err) - } - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", nil) - }) - - t.Run("Service selects WorkloadEntry: health status", func(t *testing.T) { - kc, _, store, kube, _ := setupTest(t) - makeService(t, kube, service) - - // Start as unhealthy, should have no instances - makeIstioObject(t, store, setHealth(workloadEntry, false)) - instances := []ServiceInstanceResponse{} - expectServiceInstances(t, kc, expectedSvc, 80, instances) - - // Mark healthy, get instances - makeIstioObject(t, store, setHealth(workloadEntry, true)) - instances = []ServiceInstanceResponse{{ - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: workloadEntry.Spec.(*networking.WorkloadEntry).Address, - Port: 80, - }} - expectServiceInstances(t, kc, expectedSvc, 80, instances) - - // Set back to unhealthy - makeIstioObject(t, store, setHealth(workloadEntry, false)) - instances = []ServiceInstanceResponse{} - expectServiceInstances(t, kc, expectedSvc, 80, instances) - - // Remove health status entirely - makeIstioObject(t, store, workloadEntry) - instances = []ServiceInstanceResponse{{ - Hostname: expectedSvc.Hostname, - Namestring: expectedSvc.Attributes.Namespace, - Address: workloadEntry.Spec.(*networking.WorkloadEntry).Address, - Port: 80, - }} - expectServiceInstances(t, kc, expectedSvc, 80, instances) - }) -} - -func setHealth(cfg config.Config, healthy bool) config.Config { - cfg = cfg.DeepCopy() - if cfg.Annotations == nil { - cfg.Annotations = map[string]string{} - } - cfg.Annotations[status.WorkloadEntryHealthCheckAnnotation] = "true" - if healthy { - return status.UpdateConfigCondition(cfg, &v1alpha1.IstioCondition{ - Type: status.ConditionHealthy, - Status: status.StatusTrue, - }) - } - return status.UpdateConfigCondition(cfg, &v1alpha1.IstioCondition{ - Type: status.ConditionHealthy, - Status: status.StatusFalse, - }) -} - -func waitForEdsUpdate(t *testing.T, xdsUpdater *xds.FakeXdsUpdater, expected int) { - t.Helper() - retry.UntilSuccessOrFail(t, func() error { - event := xdsUpdater.WaitOrFail(t, "eds", "edscache") - if event.Endpoints != expected { - return fmt.Errorf("expecting %d endpoints, but got %d", expected, event.Endpoints) - } - return nil - }, retry.Delay(time.Millisecond*10), retry.Timeout(time.Second)) -} - -func TestEndpointsDeduping(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ - KubernetesEndpointMode: kubecontroller.EndpointSliceOnly, - }) - namespace := "namespace" - labels := map[string]string{ - "app": "bar", - } - makeService(t, s.KubeClient(), &v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "service", - Namespace: namespace, - }, - Spec: v1.ServiceSpec{ - Ports: []v1.ServicePort{{ - Name: "http", - Port: 80, - }, { - Name: "http-other", - Port: 90, - }}, - Selector: labels, - ClusterIP: "9.9.9.9", - }, - }) - // Create an expect endpoint - createEndpointSlice(t, s.KubeClient(), "slice1", "service", namespace, []v1.EndpointPort{{Name: "http", Port: 80}}, []string{"1.2.3.4"}) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", []string{"1.2.3.4:80"}) - - // create an FQDN endpoint that should be ignored - createEndpointSliceWithType(t, s.KubeClient(), "slice1", "service", - namespace, []v1.EndpointPort{{Name: "http", Port: 80}}, []string{"foo.com"}, discovery.AddressTypeFQDN) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", []string{"1.2.3.4:80"}) - - // Add another port endpoint - createEndpointSlice(t, s.KubeClient(), "slice1", "service", namespace, - []v1.EndpointPort{{Name: "http-other", Port: 90}, {Name: "http", Port: 80}}, []string{"1.2.3.4", "2.3.4.5"}) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", []string{"1.2.3.4:80", "2.3.4.5:80"}) - expectEndpoints(t, s, "outbound|90||service.namespace.svc.cluster.local", []string{"1.2.3.4:90", "2.3.4.5:90"}) - - // Move the endpoint to another slice - transition phase where its duplicated - createEndpointSlice(t, s.KubeClient(), "slice1", "service", namespace, []v1.EndpointPort{{Name: "http", Port: 80}}, []string{"1.2.3.5", "2.3.4.5"}) - createEndpointSlice(t, s.KubeClient(), "slice2", "service", namespace, []v1.EndpointPort{{Name: "http", Port: 80}}, []string{"2.3.4.5"}) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", []string{"1.2.3.5:80", "2.3.4.5:80"}) - - // Move the endpoint to another slice - completed - createEndpointSlice(t, s.KubeClient(), "slice1", "service", namespace, []v1.EndpointPort{{Name: "http", Port: 80}}, []string{"1.2.3.4"}) - createEndpointSlice(t, s.KubeClient(), "slice2", "service", namespace, []v1.EndpointPort{{Name: "http", Port: 80}}, []string{"2.3.4.5"}) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", []string{"1.2.3.4:80", "2.3.4.5:80"}) - - // Delete endpoint - createEndpointSlice(t, s.KubeClient(), "slice1", "service", namespace, []v1.EndpointPort{{Name: "http", Port: 80}}, []string{"1.2.3.4"}) - createEndpointSlice(t, s.KubeClient(), "slice2", "service", namespace, []v1.EndpointPort{{Name: "http", Port: 80}}, []string{}) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", []string{"1.2.3.4:80"}) - - _ = s.KubeClient().DiscoveryV1().EndpointSlices(namespace).Delete(context.TODO(), "slice1", metav1.DeleteOptions{}) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", nil) - - // Ensure there is nothing is left over - expectServiceInstances(t, s.KubeRegistry, &model.Service{ - Hostname: "service.namespace.svc.cluster.local", - Ports: []*model.Port{{ - Name: "http", - Port: 80, - Protocol: "http", - }}, - Attributes: model.ServiceAttributes{ - Namespace: namespace, - Name: "service", - LabelSelectors: labels, - }, - }, 80, []ServiceInstanceResponse{}) -} - -// TestEndpointSlicingServiceUpdate is a regression test to ensure we do not end up with duplicate endpoints when a service changes. -func TestEndpointSlicingServiceUpdate(t *testing.T) { - for _, version := range []string{"latest", "20"} { - t.Run("kuberentes 1."+version, func(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ - KubernetesEndpointMode: kubecontroller.EndpointSliceOnly, - KubernetesVersion: version, - EnableFakeXDSUpdater: true, - }) - namespace := "namespace" - labels := map[string]string{ - "app": "bar", - } - makeService(t, s.KubeClient(), &v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "service", - Namespace: namespace, - }, - Spec: v1.ServiceSpec{ - Ports: []v1.ServicePort{{ - Name: "http", - Port: 80, - }, { - Name: "http-other", - Port: 90, - }}, - Selector: labels, - ClusterIP: "9.9.9.9", - }, - }) - xdsUpdater := s.XdsUpdater.(*xds.FakeXdsUpdater) - createEndpointSlice(t, s.KubeClient(), "slice1", "service", namespace, []v1.EndpointPort{{Name: "http", Port: 80}}, []string{"1.2.3.4"}) - createEndpointSlice(t, s.KubeClient(), "slice2", "service", namespace, []v1.EndpointPort{{Name: "http", Port: 80}}, []string{"1.2.3.4"}) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", []string{"1.2.3.4:80"}) - xdsUpdater.WaitOrFail(t, "svcupdate") - - // Trigger a service updates - makeService(t, s.KubeClient(), &v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "service", - Namespace: namespace, - Labels: map[string]string{"foo": "bar"}, - }, - Spec: v1.ServiceSpec{ - Ports: []v1.ServicePort{{ - Name: "http", - Port: 80, - }, { - Name: "http-other", - Port: 90, - }}, - Selector: labels, - ClusterIP: "9.9.9.9", - }, - }) - xdsUpdater.WaitOrFail(t, "svcupdate") - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", []string{"1.2.3.4:80"}) - }) - } -} - -func TestSameIPEndpointSlicing(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ - KubernetesEndpointMode: kubecontroller.EndpointSliceOnly, - EnableFakeXDSUpdater: true, - }) - namespace := "namespace" - labels := map[string]string{ - "app": "bar", - } - makeService(t, s.KubeClient(), &v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "service", - Namespace: namespace, - }, - Spec: v1.ServiceSpec{ - Ports: []v1.ServicePort{{ - Name: "http", - Port: 80, - }, { - Name: "http-other", - Port: 90, - }}, - Selector: labels, - ClusterIP: "9.9.9.9", - }, - }) - xdsUpdater := s.XdsUpdater.(*xds.FakeXdsUpdater) - - // Delete endpoints with same IP - createEndpointSlice(t, s.KubeClient(), "slice1", "service", namespace, []v1.EndpointPort{{Name: "http", Port: 80}}, []string{"1.2.3.4"}) - createEndpointSlice(t, s.KubeClient(), "slice2", "service", namespace, []v1.EndpointPort{{Name: "http", Port: 80}}, []string{"1.2.3.4"}) - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", []string{"1.2.3.4:80"}) - - // delete slice 1, it should still exist - _ = s.KubeClient().DiscoveryV1().EndpointSlices(namespace).Delete(context.TODO(), "slice1", metav1.DeleteOptions{}) - xdsUpdater.WaitOrFail(t, "eds") - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", []string{"1.2.3.4:80"}) - _ = s.KubeClient().DiscoveryV1().EndpointSlices(namespace).Delete(context.TODO(), "slice2", metav1.DeleteOptions{}) - xdsUpdater.WaitOrFail(t, "eds") - expectEndpoints(t, s, "outbound|80||service.namespace.svc.cluster.local", nil) -} - -type ServiceInstanceResponse struct { - Hostname host.Name - Namestring string - Address string - Port uint32 -} - -func expectEndpoints(t *testing.T, s *xds.FakeDiscoveryServer, cluster string, expected []string) { - t.Helper() - retry.UntilSuccessOrFail(t, func() error { - got := xdstest.ExtractLoadAssignments(s.Endpoints(s.SetupProxy(nil))) - sort.Strings(got[cluster]) - sort.Strings(expected) - if !reflect.DeepEqual(got[cluster], expected) { - return fmt.Errorf("wanted %v got %v. All endpoints: %+v", expected, got[cluster], got) - } - return nil - }, retry.Converge(2), retry.Timeout(time.Second*2), retry.Delay(time.Millisecond*10)) -} - -// nolint: unparam -func expectServiceInstances(t *testing.T, sd serviceregistry.Instance, svc *model.Service, port int, expected []ServiceInstanceResponse) { - t.Helper() - svc.Attributes.ServiceRegistry = sd.Provider() - // The system is eventually consistent, so add some retries - retry.UntilSuccessOrFail(t, func() error { - instances := sd.InstancesByPort(svc, port, nil) - sortServiceInstances(instances) - got := []ServiceInstanceResponse{} - for _, i := range instances { - got = append(got, ServiceInstanceResponse{ - Hostname: i.Service.Hostname, - Namestring: i.Service.Attributes.Namespace, - Address: i.Endpoint.Address, - Port: i.Endpoint.EndpointPort, - }) - } - if err := compare(t, got, expected); err != nil { - return fmt.Errorf("%v", err) - } - return nil - }, retry.Converge(2), retry.Timeout(time.Second*2), retry.Delay(time.Millisecond*10)) -} - -func compare(t *testing.T, actual, expected interface{}) error { - return util.Compare(jsonBytes(t, actual), jsonBytes(t, expected)) -} - -func sortServiceInstances(instances []*model.ServiceInstance) { - sort.Slice(instances, func(i, j int) bool { - if instances[i].Service.Hostname == instances[j].Service.Hostname { - return instances[i].Endpoint.Address < instances[j].Endpoint.Address - } - return instances[i].Service.Hostname < instances[j].Service.Hostname - }) -} - -func jsonBytes(t *testing.T, v interface{}) []byte { - data, err := json.MarshalIndent(v, "", " ") - if err != nil { - t.Fatal(t) - } - return data -} - -func setPodReady(pod *v1.Pod) { - pod.Status.Conditions = []v1.PodCondition{ - { - Type: v1.PodReady, - Status: v1.ConditionTrue, - LastTransitionTime: metav1.Now(), - }, - } -} - -func setPodUnready(pod *v1.Pod) { - pod.Status.Conditions = []v1.PodCondition{ - { - Type: v1.PodReady, - Status: v1.ConditionFalse, - LastTransitionTime: metav1.Now(), - }, - } -} - -func makePod(t *testing.T, c kubernetes.Interface, pod *v1.Pod) { - t.Helper() - newPod, err := c.CoreV1().Pods(pod.Namespace).Create(context.Background(), pod, metav1.CreateOptions{}) - if kerrors.IsAlreadyExists(err) { - newPod, err = c.CoreV1().Pods(pod.Namespace).Update(context.Background(), pod, metav1.UpdateOptions{}) - } - if err != nil { - t.Fatal(err) - } - // Apiserver doesn't allow Create/Update to modify the pod status. Creating doesn't result in - // events - since PodIP will be "". - newPod.Status.PodIP = pod.Status.PodIP - newPod.Status.Phase = v1.PodRunning - - // Also need to sets the pod to be ready as now we only add pod into service entry endpoint when it's ready - setPodReady(newPod) - _, err = c.CoreV1().Pods(pod.Namespace).UpdateStatus(context.TODO(), newPod, metav1.UpdateOptions{}) - if err != nil { - t.Fatal(err) - } -} - -func makeService(t *testing.T, c kubernetes.Interface, svc *v1.Service) { - t.Helper() - // avoid mutating input - svc = svc.DeepCopy() - // simulate actual k8s behavior - for i, port := range svc.Spec.Ports { - if port.TargetPort.IntVal == 0 && port.TargetPort.StrVal == "" { - svc.Spec.Ports[i].TargetPort.IntVal = port.Port - } - } - - _, err := c.CoreV1().Services(svc.Namespace).Create(context.Background(), svc, metav1.CreateOptions{}) - if kerrors.IsAlreadyExists(err) { - _, err = c.CoreV1().Services(svc.Namespace).Update(context.Background(), svc, metav1.UpdateOptions{}) - } - if err != nil { - t.Fatal(err) - } -} - -func makeIstioObject(t *testing.T, c model.ConfigStore, svc config.Config) { - t.Helper() - _, err := c.Create(svc) - if err != nil && err.Error() == "item already exists" { - _, err = c.Update(svc) - } - if err != nil { - t.Fatal(err) - } -} - -func createEndpoints(t *testing.T, c kubernetes.Interface, name, namespace string, ports []v1.EndpointPort, ips []string) { - eas := make([]v1.EndpointAddress, 0) - for _, ip := range ips { - eas = append(eas, v1.EndpointAddress{IP: ip, TargetRef: &v1.ObjectReference{ - Kind: "Pod", - Name: "pod", - Namespace: namespace, - }}) - } - - endpoint := &v1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Subsets: []v1.EndpointSubset{{ - Addresses: eas, - Ports: ports, - }}, - } - if _, err := c.CoreV1().Endpoints(namespace).Create(context.TODO(), endpoint, metav1.CreateOptions{}); err != nil { - t.Fatalf("failed to create endpoints %s in namespace %s (error %v)", name, namespace, err) - } -} - -// nolint: unparam -func createEndpointSlice(t *testing.T, c kubernetes.Interface, name, serviceName, namespace string, ports []v1.EndpointPort, addrs []string) { - createEndpointSliceWithType(t, c, name, serviceName, namespace, ports, addrs, discovery.AddressTypeIPv4) -} - -// nolint: unparam -func createEndpointSliceWithType(t *testing.T, c kubernetes.Interface, name, serviceName, namespace string, - ports []v1.EndpointPort, ips []string, addrType discovery.AddressType) { - esps := make([]discovery.EndpointPort, 0) - for _, name := range ports { - n := name // Create a stable reference to take the pointer from - esps = append(esps, discovery.EndpointPort{ - Name: &n.Name, - Protocol: &n.Protocol, - Port: &n.Port, - AppProtocol: n.AppProtocol, - }) - } - - sliceEndpoint := []discovery.Endpoint{} - for _, ip := range ips { - sliceEndpoint = append(sliceEndpoint, discovery.Endpoint{ - Addresses: []string{ip}, - }) - } - - endpointSlice := &discovery.EndpointSlice{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: map[string]string{ - discovery.LabelServiceName: serviceName, - }, - }, - AddressType: addrType, - Endpoints: sliceEndpoint, - Ports: esps, - } - if _, err := c.DiscoveryV1().EndpointSlices(namespace).Create(context.TODO(), endpointSlice, metav1.CreateOptions{}); err != nil { - if kerrors.IsAlreadyExists(err) { - _, err = c.DiscoveryV1().EndpointSlices(namespace).Update(context.TODO(), endpointSlice, metav1.UpdateOptions{}) - } - if err != nil { - t.Fatalf("failed to create endpoint slice %s in namespace %s (error %v)", name, namespace, err) - } - } -} diff --git a/pilot/pkg/serviceregistry/util/label/label.go b/pilot/pkg/serviceregistry/util/label/label.go deleted file mode 100644 index 30c7d5593..000000000 --- a/pilot/pkg/serviceregistry/util/label/label.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package label - -import ( - "istio.io/api/label" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/network" -) - -// copied from https://github.com/kubernetes/api/blob/master/core/v1/well_known_labels.go -// It is to remove dependency on k8s.io/api/core/v1 -const ( - LabelTopologyZone = "topology.kubernetes.io/zone" - LabelTopologyRegion = "topology.kubernetes.io/region" -) - -// AugmentLabels adds additional labels to the those provided. -func AugmentLabels(in labels.Instance, clusterID cluster.ID, locality string, networkID network.ID) labels.Instance { - // Copy the original labels to a new map. - out := make(labels.Instance, len(in)+5) - for k, v := range in { - out[k] = v - } - - region, zone, subzone := model.SplitLocalityLabel(locality) - if len(region) > 0 { - out[LabelTopologyRegion] = region - } - if len(zone) > 0 { - out[LabelTopologyZone] = zone - } - if len(subzone) > 0 { - out[label.TopologySubzone.Name] = subzone - } - if len(clusterID) > 0 { - out[label.TopologyCluster.Name] = clusterID.String() - } - if len(networkID) > 0 { - out[label.TopologyNetwork.Name] = networkID.String() - } - return out -} diff --git a/pilot/pkg/serviceregistry/util/workloadinstances/index.go b/pilot/pkg/serviceregistry/util/workloadinstances/index.go deleted file mode 100644 index c839a196b..000000000 --- a/pilot/pkg/serviceregistry/util/workloadinstances/index.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package workloadinstances - -import ( - "sync" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" -) - -// Index reprensents an index over workload instances from workload entries. -// -// Indexes are thread-safe. -type Index interface { - // Insert adds/updates given workload instance to the index. - // - // Returns previous value in the index, or nil otherwise. - Insert(*model.WorkloadInstance) *model.WorkloadInstance - // Delete removes given workload instance from the index. - // - // Returns value removed from the index, or nil otherwise. - Delete(*model.WorkloadInstance) *model.WorkloadInstance - // GetByIP returns a list of all workload instances associated with a - // given IP address. The list is ordered by namespace/name. - // - // There are several use cases where multiple workload instances might - // have the same IP address: - // 1) there are multiple Istio Proxies running on a single host, e.g. - // in 'router' mode or even in 'sidecar' mode. - // 2) workload instances have the same IP but different networks - GetByIP(string) []*model.WorkloadInstance - // Empty returns whether the index is empty. - Empty() bool - // ForEach iterates over all workload instances in the index. - ForEach(func(*model.WorkloadInstance)) -} - -// indexKey returns index key for a given workload instance. -func indexKey(wi *model.WorkloadInstance) string { - return wi.Namespace + "/" + wi.Name -} - -// NewIndex returns a new Index instance. -func NewIndex() Index { - return &index{ - keyFunc: indexKey, - keyToInstance: make(map[string]*model.WorkloadInstance), - ipToKeys: make(MultiValueMap), - } -} - -// index implements Index. -type index struct { - mu sync.RWMutex - // key function - keyFunc func(*model.WorkloadInstance) string - // map of namespace/name -> workload instance - keyToInstance map[string]*model.WorkloadInstance - // map of ip -> set of namespace/name - ipToKeys MultiValueMap -} - -// Insert implements Index. -func (i *index) Insert(wi *model.WorkloadInstance) *model.WorkloadInstance { - i.mu.Lock() - defer i.mu.Unlock() - - key := i.keyFunc(wi) - // Check to see if the workload entry changed. If it did, clear the old entry - previous := i.keyToInstance[key] - if previous != nil && previous.Endpoint.Address != wi.Endpoint.Address { - i.ipToKeys.Delete(previous.Endpoint.Address, key) - } - i.keyToInstance[key] = wi - i.ipToKeys.Insert(wi.Endpoint.Address, key) - return previous -} - -// Delete implements Index. -func (i *index) Delete(wi *model.WorkloadInstance) *model.WorkloadInstance { - i.mu.Lock() - defer i.mu.Unlock() - - key := i.keyFunc(wi) - previous := i.keyToInstance[key] - if previous != nil { - i.ipToKeys.Delete(previous.Endpoint.Address, key) - } - i.ipToKeys.Delete(wi.Endpoint.Address, key) - delete(i.keyToInstance, key) - return previous -} - -// GetByIP implements Index. -func (i *index) GetByIP(ip string) []*model.WorkloadInstance { - i.mu.RLock() - defer i.mu.RUnlock() - - keys := i.ipToKeys[ip] - if len(keys) == 0 { - return nil - } - instances := make([]*model.WorkloadInstance, 0, len(keys)) - for _, key := range keys.SortedList() { - if instance, exists := i.keyToInstance[key]; exists { - instances = append(instances, instance) - } - } - return instances -} - -// Empty implements Index. -func (i *index) Empty() bool { - i.mu.RLock() - defer i.mu.RUnlock() - - return len(i.keyToInstance) == 0 -} - -// ForEach iterates over all workload instances in the index. -func (i *index) ForEach(fn func(*model.WorkloadInstance)) { - i.mu.RLock() - defer i.mu.RUnlock() - - for _, instance := range i.keyToInstance { - fn(instance) - } -} diff --git a/pilot/pkg/serviceregistry/util/workloadinstances/index_test.go b/pilot/pkg/serviceregistry/util/workloadinstances/index_test.go deleted file mode 100644 index ba2b60fb1..000000000 --- a/pilot/pkg/serviceregistry/util/workloadinstances/index_test.go +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package workloadinstances - -import ( - "testing" - "time" -) - -import ( - "github.com/google/go-cmp/cmp" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/spiffe" -) - -var GlobalTime = time.Now() - -// ServiceEntry with a selector -var selector = &config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.ServiceEntry, - Name: "selector", - Namespace: "selector", - CreationTimestamp: GlobalTime, - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"selector.com"}, - Ports: []*networking.Port{ - {Number: 444, Name: "tcp-444", Protocol: "tcp"}, - {Number: 445, Name: "http-445", Protocol: "http"}, - }, - WorkloadSelector: &networking.WorkloadSelector{ - Labels: map[string]string{"app": "wle"}, - }, - Resolution: networking.ServiceEntry_STATIC, - }, -} - -func TestIndex(t *testing.T) { - // Setup a couple of workload instances for test - wi1 := &model.WorkloadInstance{ - Name: selector.Name, - Namespace: selector.Namespace, - Endpoint: &model.IstioEndpoint{ - Address: "2.2.2.2", - Labels: map[string]string{"app": "wle"}, - ServiceAccount: spiffe.MustGenSpiffeURI(selector.Name, "default"), - TLSMode: model.IstioMutualTLSModeLabel, - }, - } - - wi2 := &model.WorkloadInstance{ - Name: "some-other-name", - Namespace: selector.Namespace, - Endpoint: &model.IstioEndpoint{ - Address: "3.3.3.3", - Labels: map[string]string{"app": "wle"}, - ServiceAccount: spiffe.MustGenSpiffeURI(selector.Name, "default"), - TLSMode: model.IstioMutualTLSModeLabel, - }, - } - - wi3 := &model.WorkloadInstance{ - Name: "another-name", - Namespace: "dns-selector", - Endpoint: &model.IstioEndpoint{ - Address: "2.2.2.2", - Labels: map[string]string{"app": "dns-wle"}, - ServiceAccount: spiffe.MustGenSpiffeURI("dns-selector", "default"), - TLSMode: model.IstioMutualTLSModeLabel, - }, - } - - index := NewIndex() - - // test update - index.Insert(wi1) - index.Insert(wi2) - index.Insert(wi3) - - verifyGetByIP := func(ip string, expected []*model.WorkloadInstance) { - actual := index.GetByIP(ip) - - if diff := cmp.Diff(len(expected), len(actual)); diff != "" { - t.Errorf("GetByIP() returned unexpected number of workload instances (--want/++got): %v", diff) - } - - for i := range expected { - if diff := cmp.Diff(expected[i], actual[i]); diff != "" { - t.Errorf("GetByIP() returned unexpected workload instance %d (--want/++got): %v", i, diff) - } - } - } - - // GetByIP should return 2 workload instances - - verifyGetByIP("2.2.2.2", []*model.WorkloadInstance{wi3, wi1}) - - // Delete should return previously inserted value - - deleted := index.Delete(wi1) - if diff := cmp.Diff(wi1, deleted); diff != "" { - t.Errorf("1st Delete() returned unexpected value (--want/++got): %v", diff) - } - - // GetByIP should return 1 workload instance - - verifyGetByIP("2.2.2.2", []*model.WorkloadInstance{wi3}) - - // Delete should return nil since there is no such element in the index - - deleted = index.Delete(wi1) - if diff := cmp.Diff((*model.WorkloadInstance)(nil), deleted); diff != "" { - t.Errorf("2nd Delete() returned unexpected value (--want/++got): %v", diff) - } - - // GetByIP should return nil - - verifyGetByIP("1.1.1.1", nil) -} - -func TestIndex_FindAll(t *testing.T) { - // Setup a couple of workload instances for test. These will be selected by the `selector` SE - wi1 := &model.WorkloadInstance{ - Name: selector.Name, - Namespace: selector.Namespace, - Endpoint: &model.IstioEndpoint{ - Address: "2.2.2.2", - Labels: map[string]string{"app": "wle"}, // should match - }, - } - - wi2 := &model.WorkloadInstance{ - Name: "same-ip", - Namespace: selector.Namespace, - Endpoint: &model.IstioEndpoint{ - Address: "2.2.2.2", - Labels: map[string]string{"app": "wle"}, // should match - }, - } - - wi3 := &model.WorkloadInstance{ - Name: "another-ip", - Namespace: selector.Namespace, - Endpoint: &model.IstioEndpoint{ - Address: "3.3.3.3", - Labels: map[string]string{"app": "another-wle"}, // should not match because of another label - }, - } - - wi4 := &model.WorkloadInstance{ - Name: "another-name", - Namespace: "another-namespace", // should not match because of another namespace - Endpoint: &model.IstioEndpoint{ - Address: "2.2.2.2", - Labels: map[string]string{"app": "wle"}, - }, - } - - index := NewIndex() - - // test update - index.Insert(wi1) - index.Insert(wi2) - index.Insert(wi3) - index.Insert(wi4) - - // test search by service selector - actual := FindAllInIndex(index, ByServiceSelector(selector.Namespace, labels.Instance{"app": "wle"})) - want := []*model.WorkloadInstance{wi1, wi2} - - if diff := cmp.Diff(len(want), len(actual)); diff != "" { - t.Errorf("FindAllInIndex() returned unexpected number of workload instances (--want/++got): %v", diff) - } - - got := map[string]*model.WorkloadInstance{} - for _, instance := range actual { - got[instance.Name] = instance - } - - for _, expected := range want { - if diff := cmp.Diff(expected, got[expected.Name]); diff != "" { - t.Fatalf("FindAllInIndex() returned unexpected workload instance %q (--want/++got): %v", expected.Name, diff) - } - } -} diff --git a/pilot/pkg/serviceregistry/util/workloadinstances/map.go b/pilot/pkg/serviceregistry/util/workloadinstances/map.go deleted file mode 100644 index c45d14424..000000000 --- a/pilot/pkg/serviceregistry/util/workloadinstances/map.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package workloadinstances - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// MultiValueMap represents a map where each key might be associated with -// multiple values. -type MultiValueMap map[string]sets.Set - -// Insert adds given (key, value) pair into the map. -func (m MultiValueMap) Insert(key, value string) MultiValueMap { - if values, exists := m[key]; exists { - values.Insert(value) - return m - } - m[key] = sets.New(value) - return m -} - -// Delete removes given (key, value) pair out of the map. -func (m MultiValueMap) Delete(key, value string) MultiValueMap { - if values, exists := m[key]; exists { - values.Delete(value) - if values.IsEmpty() { - delete(m, key) - } - } - return m -} diff --git a/pilot/pkg/serviceregistry/util/workloadinstances/selector.go b/pilot/pkg/serviceregistry/util/workloadinstances/selector.go deleted file mode 100644 index 398fb32a2..000000000 --- a/pilot/pkg/serviceregistry/util/workloadinstances/selector.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package workloadinstances - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" -) - -// ByServiceSelector returns a predicate that matches workload instances -// of a given service. -func ByServiceSelector(namespace string, selector labels.Instance) func(*model.WorkloadInstance) bool { - return func(wi *model.WorkloadInstance) bool { - return wi.Namespace == namespace && selector.SubsetOf(wi.Endpoint.Labels) - } -} - -// FindAllInIndex returns a list of workload instances in the index -// that match given predicate. -// -// The returned list is not ordered. -func FindAllInIndex(index Index, predicate func(*model.WorkloadInstance) bool) []*model.WorkloadInstance { - var instances []*model.WorkloadInstance - index.ForEach(func(instance *model.WorkloadInstance) { - if predicate(instance) { - instances = append(instances, instance) - } - }) - return instances -} diff --git a/pilot/pkg/serviceregistry/util/workloadinstances/util.go b/pilot/pkg/serviceregistry/util/workloadinstances/util.go deleted file mode 100644 index 793827d32..000000000 --- a/pilot/pkg/serviceregistry/util/workloadinstances/util.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package workloadinstances - -import ( - "strings" -) - -import ( - "k8s.io/apimachinery/pkg/types" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/util/slices" -) - -// FindInstance returns the first workload instance matching given predicate. -func FindInstance(instances []*model.WorkloadInstance, predicate func(*model.WorkloadInstance) bool) *model.WorkloadInstance { - for _, instance := range instances { - if predicate(instance) { - return instance - } - } - return nil -} - -// InstanceNameForProxy returns a name of the workload instance that -// corresponds to a given proxy, if any. -func InstanceNameForProxy(proxy *model.Proxy) types.NamespacedName { - parts := strings.Split(proxy.ID, ".") - if len(parts) == 2 && proxy.ConfigNamespace == parts[1] { - return types.NamespacedName{Name: parts[0], Namespace: parts[1]} - } - return types.NamespacedName{} -} - -// GetInstanceForProxy returns a workload instance that -// corresponds to a given proxy, if any. -func GetInstanceForProxy(index Index, proxy *model.Proxy, proxyIP string) *model.WorkloadInstance { - if !slices.ContainsString(proxy.IPAddresses, proxyIP) { - return nil - } - instances := index.GetByIP(proxyIP) // list is ordered by namespace/name - if len(instances) == 0 { - return nil - } - if len(instances) == 1 { // dominant use case - // NOTE: for the sake of backwards compatibility, we don't enforce - // instance.Namespace == proxy.ConfigNamespace - return instances[0] - } - - // try to find workload instance with the same name as proxy - proxyName := InstanceNameForProxy(proxy) - if proxyName != (types.NamespacedName{}) { - instance := FindInstance(instances, func(wi *model.WorkloadInstance) bool { - return wi.Name == proxyName.Name && wi.Namespace == proxyName.Namespace - }) - if instance != nil { - return instance - } - } - - // try to find workload instance in the same namespace as proxy - instance := FindInstance(instances, func(wi *model.WorkloadInstance) bool { - // TODO: take auto-registration group into account once it's included into workload instance - return wi.Namespace == proxy.ConfigNamespace - }) - if instance != nil { - return instance - } - - // fall back to choosing one of the workload instances - - // NOTE: for the sake of backwards compatibility, we don't enforce - // instance.Namespace == proxy.ConfigNamespace - return instances[0] -} diff --git a/pilot/pkg/simulation/traffic.go b/pilot/pkg/simulation/traffic.go deleted file mode 100644 index eeb6ef5b8..000000000 --- a/pilot/pkg/simulation/traffic.go +++ /dev/null @@ -1,684 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package simulation - -import ( - "errors" - "fmt" - "net" - "net/http" - "regexp" - "strings" - "testing" -) - -import ( - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/yl2chen/cidranger" - istiolog "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - xdsfilters "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/filters" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -var log = istiolog.RegisterScope("simulation", "", 0) - -type Protocol string - -const ( - HTTP Protocol = "http" - HTTP2 Protocol = "http2" - TCP Protocol = "tcp" -) - -type TLSMode string - -const ( - Plaintext TLSMode = "plaintext" - TLS TLSMode = "tls" - MTLS TLSMode = "mtls" -) - -func (c Call) IsHTTP() bool { - return httpProtocols.Contains(string(c.Protocol)) && (c.TLS == Plaintext || c.TLS == "") -} - -var httpProtocols = sets.New(string(HTTP), string(HTTP2)) - -var ( - ErrNoListener = errors.New("no listener matched") - ErrNoFilterChain = errors.New("no filter chains matched") - ErrNoRoute = errors.New("no route matched") - ErrTLSRedirect = errors.New("tls required, sending 301") - ErrNoVirtualHost = errors.New("no virtual host matched") - ErrMultipleFilterChain = errors.New("multiple filter chains matched") - // ErrProtocolError happens when sending TLS/TCP request to HCM, for example - ErrProtocolError = errors.New("protocol error") - ErrTLSError = errors.New("invalid TLS") - ErrMTLSError = errors.New("invalid mTLS") -) - -type Expect struct { - Name string - Call Call - Result Result -} - -type CallMode string - -type CustomFilterChainValidation func(filterChain *listener.FilterChain) error - -var ( - // CallModeGateway simulate no iptables - CallModeGateway CallMode = "gateway" - // CallModeOutbound simulate iptables redirect to 15001 - CallModeOutbound CallMode = "outbound" - // CallModeInbound simulate iptables redirect to 15006 - CallModeInbound CallMode = "inbound" -) - -type Call struct { - Address string - Port int - Path string - - // Protocol describes the protocol type. TLS encapsulation is separate - Protocol Protocol - // TLS describes the connection tls parameters - // TODO: currently this does not verify TLS vs mTLS - TLS TLSMode - Alpn string - - // HostHeader is a convenience field for Headers - HostHeader string - Headers http.Header - - Sni string - - // CallMode describes the type of call to make. - CallMode CallMode - - CustomListenerValidations []CustomFilterChainValidation - - MtlsSecretConfigName string -} - -func (c Call) FillDefaults() Call { - if c.Headers == nil { - c.Headers = http.Header{} - } - if c.HostHeader != "" { - c.Headers["Host"] = []string{c.HostHeader} - } - // For simplicity, set SNI automatically for TLS traffic. - if c.Sni == "" && (c.TLS == TLS) { - c.Sni = c.HostHeader - } - if c.Path == "" { - c.Path = "/" - } - if c.TLS == "" { - c.TLS = Plaintext - } - if c.Address == "" { - // pick a random address, assumption is the test does not care - c.Address = "1.3.3.7" - } - if c.TLS == MTLS && c.Alpn == "" { - c.Alpn = protocolToMTLSAlpn(c.Protocol) - } - if c.TLS == TLS && c.Alpn == "" { - c.Alpn = protocolToTLSAlpn(c.Protocol) - } - return c -} - -type Result struct { - Error error - ListenerMatched string - FilterChainMatched string - RouteMatched string - RouteConfigMatched string - VirtualHostMatched string - ClusterMatched string - // StrictMatch controls whether we will strictly match the result. If unset, empty fields will - // be ignored, allowing testing only fields we care about This allows asserting that the result - // is *exactly* equal, allowing asserting a field is empty - StrictMatch bool - // If set, this will mark a test as skipped. Note the result is still checked first - we skip only - // if we pass the test. This is to ensure that if the behavior changes, we still capture it; the skip - // just ensures we notice a test is wrong - Skip string - t test.Failer -} - -func (r Result) Matches(t *testing.T, want Result) { - t.Helper() - r.StrictMatch = want.StrictMatch // to make diff pass - r.Skip = want.Skip // to make diff pass - diff := cmp.Diff(want, r, cmpopts.IgnoreUnexported(Result{}), cmpopts.EquateErrors()) - if want.StrictMatch && diff != "" { - t.Errorf("Diff: %v", diff) - return - } - if want.Error != r.Error { - t.Errorf("want error %v got %v", want.Error, r.Error) - } - if want.ListenerMatched != "" && want.ListenerMatched != r.ListenerMatched { - t.Errorf("want listener matched %q got %q", want.ListenerMatched, r.ListenerMatched) - } else { - // Populate each field in case we did not care about it. This avoids confusing errors when we have fields - // we don't care about in the test that are present in the result. - want.ListenerMatched = r.ListenerMatched - } - if want.FilterChainMatched != "" && want.FilterChainMatched != r.FilterChainMatched { - t.Errorf("want filter chain matched %q got %q", want.FilterChainMatched, r.FilterChainMatched) - } else { - want.FilterChainMatched = r.FilterChainMatched - } - if want.RouteMatched != "" && want.RouteMatched != r.RouteMatched { - t.Errorf("want route matched %q got %q", want.RouteMatched, r.RouteMatched) - } else { - want.RouteMatched = r.RouteMatched - } - if want.RouteConfigMatched != "" && want.RouteConfigMatched != r.RouteConfigMatched { - t.Errorf("want route config matched %q got %q", want.RouteConfigMatched, r.RouteConfigMatched) - } else { - want.RouteConfigMatched = r.RouteConfigMatched - } - if want.VirtualHostMatched != "" && want.VirtualHostMatched != r.VirtualHostMatched { - t.Errorf("want virtual host matched %q got %q", want.VirtualHostMatched, r.VirtualHostMatched) - } else { - want.VirtualHostMatched = r.VirtualHostMatched - } - if want.ClusterMatched != "" && want.ClusterMatched != r.ClusterMatched { - t.Errorf("want cluster matched %q got %q", want.ClusterMatched, r.ClusterMatched) - } else { - want.ClusterMatched = r.ClusterMatched - } - if t.Failed() { - t.Logf("Diff: %+v", diff) - t.Logf("Full Diff: %+v", cmp.Diff(want, r, cmpopts.IgnoreUnexported(Result{}), cmpopts.EquateErrors())) - } else if want.Skip != "" { - t.Skip(fmt.Sprintf("Known bug: %v", r.Skip)) - } -} - -type Simulation struct { - t *testing.T - Listeners []*listener.Listener - Clusters []*cluster.Cluster - Routes []*route.RouteConfiguration -} - -func NewSimulationFromConfigGen(t *testing.T, s *v1alpha3.ConfigGenTest, proxy *model.Proxy) *Simulation { - l := s.Listeners(proxy) - sim := &Simulation{ - t: t, - Listeners: l, - Clusters: s.Clusters(proxy), - Routes: s.RoutesFromListeners(proxy, l), - } - return sim -} - -func NewSimulation(t *testing.T, s *xds.FakeDiscoveryServer, proxy *model.Proxy) *Simulation { - return NewSimulationFromConfigGen(t, s.ConfigGenTest, proxy) -} - -// withT swaps out the testing struct. This allows executing sub tests. -func (sim *Simulation) withT(t *testing.T) *Simulation { - cpy := *sim - cpy.t = t - return &cpy -} - -func (sim *Simulation) RunExpectations(es []Expect) { - for _, e := range es { - sim.t.Run(e.Name, func(t *testing.T) { - sim.withT(t).Run(e.Call).Matches(t, e.Result) - }) - } -} - -func hasFilterOnPort(l *listener.Listener, filter string, port int) bool { - got, f := xdstest.ExtractListenerFilters(l)[filter] - if !f { - return false - } - if got.FilterDisabled == nil { - return true - } - return !xdstest.EvaluateListenerFilterPredicates(got.FilterDisabled, port) -} - -func (sim *Simulation) Run(input Call) (result Result) { - result = Result{t: sim.t} - input = input.FillDefaults() - if input.Alpn != "" && input.TLS == Plaintext { - result.Error = fmt.Errorf("invalid call, ALPN can only be sent in TLS requests") - return result - } - - // First we will match a listener - l := matchListener(sim.Listeners, input) - if l == nil { - result.Error = ErrNoListener - return - } - result.ListenerMatched = l.Name - - hasTLSInspector := hasFilterOnPort(l, xdsfilters.TLSInspector.Name, input.Port) - if !hasTLSInspector { - // Without tls inspector, Envoy would not read the ALPN in the TLS handshake - // HTTP inspector still may set it though - input.Alpn = "" - } - - // Apply listener filters - if hasFilterOnPort(l, xdsfilters.HTTPInspector.Name, input.Port) { - if alpn := protocolToAlpn(input.Protocol); alpn != "" && input.TLS == Plaintext { - input.Alpn = alpn - } - } - - fc, err := sim.matchFilterChain(l.FilterChains, l.DefaultFilterChain, input, hasTLSInspector) - if err != nil { - result.Error = err - return - } - result.FilterChainMatched = fc.Name - // Plaintext to TLS is an error - if fc.TransportSocket != nil && input.TLS == Plaintext { - result.Error = ErrTLSError - return - } - - mTLSSecretConfigName := "default" - if input.MtlsSecretConfigName != "" { - mTLSSecretConfigName = input.MtlsSecretConfigName - } - - // mTLS listener will only accept mTLS traffic - if fc.TransportSocket != nil && sim.requiresMTLS(fc, mTLSSecretConfigName) != (input.TLS == MTLS) { - // If there is no tls inspector, then - result.Error = ErrMTLSError - return - } - - if len(input.CustomListenerValidations) > 0 { - for _, validation := range input.CustomListenerValidations { - if err := validation(fc); err != nil { - result.Error = err - } - } - } - - if hcm := xdstest.ExtractHTTPConnectionManager(sim.t, fc); hcm != nil { - // We matched HCM and didn't terminate TLS, but we are sending TLS traffic - decoding will fail - if input.TLS != Plaintext && fc.TransportSocket == nil { - result.Error = ErrProtocolError - return - } - // TCP to HCM is invalid - if input.Protocol != HTTP && input.Protocol != HTTP2 { - result.Error = ErrProtocolError - return - } - - // Fetch inline route - rc := hcm.GetRouteConfig() - if rc == nil { - // If not set, fallback to RDS - routeName := hcm.GetRds().RouteConfigName - result.RouteConfigMatched = routeName - rc = xdstest.ExtractRouteConfigurations(sim.Routes)[routeName] - } - hostHeader := "" - if len(input.Headers["Host"]) > 0 { - hostHeader = input.Headers["Host"][0] - } - vh := sim.matchVirtualHost(rc, hostHeader) - if vh == nil { - result.Error = ErrNoVirtualHost - return - } - result.VirtualHostMatched = vh.Name - if vh.RequireTls == route.VirtualHost_ALL && input.TLS == Plaintext { - result.Error = ErrTLSRedirect - return - } - - r := sim.matchRoute(vh, input) - if r == nil { - result.Error = ErrNoRoute - return - } - result.RouteMatched = r.Name - switch t := r.GetAction().(type) { - case *route.Route_Route: - result.ClusterMatched = t.Route.GetCluster() - } - } else if tcp := xdstest.ExtractTCPProxy(sim.t, fc); tcp != nil { - result.ClusterMatched = tcp.GetCluster() - } - return -} - -func (sim *Simulation) requiresMTLS(fc *listener.FilterChain, mTLSSecretConfigName string) bool { - if fc.TransportSocket == nil { - return false - } - t := &tls.DownstreamTlsContext{} - if err := fc.GetTransportSocket().GetTypedConfig().UnmarshalTo(t); err != nil { - sim.t.Fatal(err) - } - - if len(t.GetCommonTlsContext().GetTlsCertificateSdsSecretConfigs()) == 0 { - return false - } - // This is a lazy heuristic, we could check for explicit default resource or spiffe if it becomes necessary - if t.GetCommonTlsContext().GetTlsCertificateSdsSecretConfigs()[0].Name != mTLSSecretConfigName { - return false - } - if !t.RequireClientCertificate.Value { - return false - } - return true -} - -func (sim *Simulation) matchRoute(vh *route.VirtualHost, input Call) *route.Route { - for _, r := range vh.Routes { - // check path - switch pt := r.Match.GetPathSpecifier().(type) { - case *route.RouteMatch_Prefix: - if !strings.HasPrefix(input.Path, pt.Prefix) { - continue - } - case *route.RouteMatch_Path: - if input.Path != pt.Path { - continue - } - case *route.RouteMatch_SafeRegex: - r, err := regexp.Compile(pt.SafeRegex.GetRegex()) - if err != nil { - sim.t.Fatalf("invalid regex %v: %v", pt.SafeRegex.GetRegex(), err) - } - if !r.MatchString(input.Path) { - continue - } - default: - sim.t.Fatalf("unknown route path type") - } - - // TODO this only handles path - we need to add headers, query params, etc to be complete. - - return r - } - return nil -} - -func (sim *Simulation) matchVirtualHost(rc *route.RouteConfiguration, host string) *route.VirtualHost { - // Exact match - for _, vh := range rc.VirtualHosts { - for _, d := range vh.Domains { - if d == host { - return vh - } - } - } - // prefix match - var bestMatch *route.VirtualHost - longest := 0 - for _, vh := range rc.VirtualHosts { - for _, d := range vh.Domains { - if d[0] != '*' { - continue - } - if len(host) >= len(d) && strings.HasSuffix(host, d[1:]) && len(d) > longest { - bestMatch = vh - longest = len(d) - } - } - } - if bestMatch != nil { - return bestMatch - } - // Suffix match - longest = 0 - for _, vh := range rc.VirtualHosts { - for _, d := range vh.Domains { - if d[len(d)-1] != '*' { - continue - } - if len(host) >= len(d) && strings.HasPrefix(host, d[:len(d)-1]) && len(d) > longest { - bestMatch = vh - longest = len(d) - } - } - } - if bestMatch != nil { - return bestMatch - } - // wildcard match - for _, vh := range rc.VirtualHosts { - for _, d := range vh.Domains { - if d == "*" { - return vh - } - } - } - return nil -} - -// Follow the 8 step Sieve as in -// https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/listener/v3/listener_components.proto.html#config-listener-v3-filterchainmatch -// The implementation may initially be confusing because of a property of the -// Envoy algorithm - at each level we will filter out all FilterChains that do -// not match. This means an empty match (`{}`) may not match if another chain -// matches one criteria but not another. -func (sim *Simulation) matchFilterChain(chains []*listener.FilterChain, defaultChain *listener.FilterChain, - input Call, hasTLSInspector bool) (*listener.FilterChain, error) { - chains = filter("DestinationPort", chains, func(fc *listener.FilterChainMatch) bool { - return fc.GetDestinationPort() == nil - }, func(fc *listener.FilterChainMatch) bool { - return int(fc.GetDestinationPort().GetValue()) == input.Port - }) - chains = filter("PrefixRanges", chains, func(fc *listener.FilterChainMatch) bool { - return fc.GetPrefixRanges() == nil - }, func(fc *listener.FilterChainMatch) bool { - ranger := cidranger.NewPCTrieRanger() - for _, a := range fc.GetPrefixRanges() { - s := fmt.Sprintf("%s/%d", a.AddressPrefix, a.GetPrefixLen().GetValue()) - _, cidr, err := net.ParseCIDR(s) - if err != nil { - sim.t.Fatalf("failed to parse cidr %v: %v", s, err) - } - if err := ranger.Insert(cidranger.NewBasicRangerEntry(*cidr)); err != nil { - sim.t.Fatalf("failed to insert cidr %v: %v", cidr, err) - } - } - f, err := ranger.Contains(net.ParseIP(input.Address)) - if err != nil { - sim.t.Fatalf("cidr containers %v failed: %v", input.Address, err) - } - return f - }) - chains = filter("ServerNames", chains, func(fc *listener.FilterChainMatch) bool { - return fc.GetServerNames() == nil - }, func(fc *listener.FilterChainMatch) bool { - sni := host.Name(input.Sni) - for _, s := range fc.GetServerNames() { - if sni.SubsetOf(host.Name(s)) { - return true - } - } - return false - }) - chains = filter("TransportProtocol", chains, func(fc *listener.FilterChainMatch) bool { - return fc.GetTransportProtocol() == "" - }, func(fc *listener.FilterChainMatch) bool { - if !hasTLSInspector { - // Without tls inspector, transport protocol will always be raw buffer - return fc.GetTransportProtocol() == xdsfilters.RawBufferTransportProtocol - } - switch fc.GetTransportProtocol() { - case xdsfilters.TLSTransportProtocol: - return input.TLS == TLS || input.TLS == MTLS - case xdsfilters.RawBufferTransportProtocol: - return input.TLS == Plaintext - } - return false - }) - chains = filter("ApplicationProtocols", chains, func(fc *listener.FilterChainMatch) bool { - return fc.GetApplicationProtocols() == nil - }, func(fc *listener.FilterChainMatch) bool { - return sets.New(fc.GetApplicationProtocols()...).Contains(input.Alpn) - }) - // We do not implement the "source" based filters as we do not use them - if len(chains) > 1 { - for _, c := range chains { - log.Warnf("Matched chain %v", c.Name) - } - return nil, ErrMultipleFilterChain - } - if len(chains) == 0 { - if defaultChain != nil { - return defaultChain, nil - } - return nil, ErrNoFilterChain - } - return chains[0], nil -} - -func filter(desc string, chains []*listener.FilterChain, - empty func(fc *listener.FilterChainMatch) bool, - match func(fc *listener.FilterChainMatch) bool) []*listener.FilterChain { - res := []*listener.FilterChain{} - anySet := false - for _, c := range chains { - if !empty(c.GetFilterChainMatch()) { - anySet = true - break - } - } - if !anySet { - log.Debugf("%v: none set, skipping", desc) - return chains - } - for i, c := range chains { - if match(c.GetFilterChainMatch()) { - log.Debugf("%v: matched chain %v/%v", desc, i, c.GetName()) - res = append(res, c) - } - } - // Return all matching filter chains - if len(res) > 0 { - return res - } - // Unless there were no matches - in which case we return all filter chains that did not have a - // match set - for i, c := range chains { - if empty(c.GetFilterChainMatch()) { - log.Debugf("%v: no matches, found empty chain match %v/%v", desc, i, c.GetName()) - res = append(res, c) - } - } - return res -} - -func protocolToMTLSAlpn(s Protocol) string { - switch s { - case HTTP: - return "istio-http/1.1" - case HTTP2: - return "istio-h2" - default: - return "istio" - } -} - -func protocolToTLSAlpn(s Protocol) string { - switch s { - case HTTP: - return "http/1.1" - case HTTP2: - return "h2" - default: - return "" - } -} - -func protocolToAlpn(s Protocol) string { - switch s { - case HTTP: - return "http/1.1" - case HTTP2: - return "h2c" - default: - return "" - } -} - -func matchListener(listeners []*listener.Listener, input Call) *listener.Listener { - if input.CallMode == CallModeInbound { - return xdstest.ExtractListener(model.VirtualInboundListenerName, listeners) - } - // First find exact match for the IP/Port, then fallback to wildcard IP/Port - // There is no wildcard port - for _, l := range listeners { - if matchAddress(l.GetAddress(), input.Address, input.Port) { - return l - } - } - for _, l := range listeners { - if matchAddress(l.GetAddress(), "0.0.0.0", input.Port) { - return l - } - } - - // Fallback to the outbound listener - // TODO - support inbound - for _, l := range listeners { - if l.Name == model.VirtualOutboundListenerName { - return l - } - } - return nil -} - -func matchAddress(a *core.Address, address string, port int) bool { - if a.GetSocketAddress().GetAddress() != address { - return false - } - if int(a.GetSocketAddress().GetPortValue()) != port { - return false - } - return true -} diff --git a/pilot/pkg/status/distribution/leak_test.go b/pilot/pkg/status/distribution/leak_test.go deleted file mode 100644 index 477822e53..000000000 --- a/pilot/pkg/status/distribution/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package distribution - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/status/distribution/ledger.go b/pilot/pkg/status/distribution/ledger.go deleted file mode 100644 index 5bc7284cf..000000000 --- a/pilot/pkg/status/distribution/ledger.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package distribution - -import ( - "strconv" -) - -import ( - "istio.io/pkg/ledger" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" -) - -func tryLedgerPut(configLedger ledger.Ledger, obj config.Config) { - key := obj.Key() - if _, err := configLedger.Put(key, strconv.FormatInt(obj.Generation, 10)); err != nil { - scope.Errorf("Failed to update %s in ledger, status will be out of date.", key) - } -} - -func tryLedgerDelete(configLedger ledger.Ledger, obj config.Config) { - key := obj.Key() - if err := configLedger.Delete(key); err != nil { - scope.Errorf("Failed to delete %s in ledger, status will be out of date.", key) - } -} diff --git a/pilot/pkg/status/distribution/report.go b/pilot/pkg/status/distribution/report.go deleted file mode 100644 index 33a26af53..000000000 --- a/pilot/pkg/status/distribution/report.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package distribution - -import ( - "gopkg.in/yaml.v2" -) - -type Report struct { - Reporter string `json:"reporter"` - DataPlaneCount int `json:"dataPlaneCount"` - InProgressResources map[string]int `json:"inProgressResources"` -} - -func ReportFromYaml(content []byte) (Report, error) { - out := Report{} - err := yaml.Unmarshal(content, &out) - return out, err -} diff --git a/pilot/pkg/status/distribution/report_test.go b/pilot/pkg/status/distribution/report_test.go deleted file mode 100644 index 0273b4f14..000000000 --- a/pilot/pkg/status/distribution/report_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package distribution - -import ( - "reflect" - "testing" -) - -import ( - "github.com/onsi/gomega" - "gopkg.in/yaml.v2" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/status" -) - -func TestReportSerialization(t *testing.T) { - in := Report{ - Reporter: "Me", - DataPlaneCount: 10, - InProgressResources: map[string]int{ - (&status.Resource{ - Name: "water", - Namespace: "default", - }).String(): 1, - }, - } - outbytes, err := yaml.Marshal(in) - gomega.RegisterTestingT(t) - gomega.Expect(err).To(gomega.BeNil()) - out := Report{} - err = yaml.Unmarshal(outbytes, &out) - gomega.Expect(err).To(gomega.BeNil()) - if !reflect.DeepEqual(out, in) { - t.Errorf("Report Serialization mutated the Report. got = %v, want %v", out, in) - } -} diff --git a/pilot/pkg/status/distribution/reporter.go b/pilot/pkg/status/distribution/reporter.go deleted file mode 100644 index f36988c7c..000000000 --- a/pilot/pkg/status/distribution/reporter.go +++ /dev/null @@ -1,355 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package distribution - -import ( - "context" - "fmt" - "sync" - "time" -) - -import ( - "gopkg.in/yaml.v2" - "istio.io/pkg/ledger" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - v1 "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/utils/clock" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/status" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -func GenStatusReporterMapKey(conID string, distributionType xds.EventType) string { - key := conID + "~" + distributionType - return key -} - -type inProgressEntry struct { - // the resource, including resourceVersion, we are currently tracking - status.Resource - // the number of reports we have written with this resource at 100% - completedIterations int -} - -type Reporter struct { - mu sync.RWMutex - // map from connection id to latest nonce - status map[string]string - // map from nonce to connection ids for which it is current - // using map[string]struct to approximate a hashset - reverseStatus map[string]sets.Set - inProgressResources map[string]*inProgressEntry - client v1.ConfigMapInterface - cm *corev1.ConfigMap - UpdateInterval time.Duration - PodName string - clock clock.Clock - ledger ledger.Ledger - distributionEventQueue chan distributionEvent - controller *Controller -} - -var _ xds.DistributionStatusCache = &Reporter{} - -const ( - labelKey = "internal.istio.io/distribution-report" - dataField = "distribution-report" -) - -// Init starts all the read only features of the reporter, used for nonce generation -// and responding to istioctl wait. -func (r *Reporter) Init(ledger ledger.Ledger, stop <-chan struct{}) { - r.ledger = ledger - if r.clock == nil { - r.clock = clock.RealClock{} - } - r.distributionEventQueue = make(chan distributionEvent, 100_000) - r.status = make(map[string]string) - r.reverseStatus = make(map[string]sets.Set) - r.inProgressResources = make(map[string]*inProgressEntry) - go r.readFromEventQueue(stop) -} - -// Start starts the reporter, which watches dataplane ack's and resource changes so that it can update status leader -// with distribution information. -func (r *Reporter) Start(clientSet kubernetes.Interface, namespace string, podname string, stop <-chan struct{}) { - scope.Info("Starting status follower controller") - r.client = clientSet.CoreV1().ConfigMaps(namespace) - r.cm = &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: r.PodName + "-distribution", - Labels: map[string]string{labelKey: "true"}, - }, - Data: make(map[string]string), - } - t := r.clock.Tick(r.UpdateInterval) - ctx := status.NewIstioContext(stop) - x, err := clientSet.CoreV1().Pods(namespace).Get(ctx, podname, metav1.GetOptions{}) - if err != nil { - scope.Errorf("can't identify pod %s context: %s", podname, err) - } else { - r.cm.OwnerReferences = []metav1.OwnerReference{ - *metav1.NewControllerRef(x, metav1.SchemeGroupVersion.WithKind("Pod")), - } - } - go func() { - for { - select { - case <-ctx.Done(): - if r.cm != nil { - // TODO: is the use of a cancelled context here a problem? Maybe set a short timeout context? - if err := r.client.Delete(context.Background(), r.cm.Name, metav1.DeleteOptions{}); err != nil { - scope.Errorf("failed to properly clean up distribution report: %v", err) - } - } - close(r.distributionEventQueue) - return - case <-t: - // TODO, check if report is necessary? May already be handled by client - r.writeReport(ctx) - } - } - }() -} - -// build a distribution report to send to status leader -func (r *Reporter) buildReport() (Report, []status.Resource) { - r.mu.RLock() - defer r.mu.RUnlock() - var finishedResources []status.Resource - out := Report{ - Reporter: r.PodName, - DataPlaneCount: len(r.status), - InProgressResources: map[string]int{}, - } - // for every resource in flight - for _, ipr := range r.inProgressResources { - res := ipr.Resource - key := res.String() - // for every version (nonce) of the config currently in play - for nonce, dataplanes := range r.reverseStatus { - - // check to see if this version of the config contains this version of the resource - // it might be more optimal to provide for a full dump of the config at a certain version? - dpVersion, err := r.ledger.GetPreviousValue(nonce, res.ToModelKey()) - if err == nil && dpVersion == res.Generation { - if _, ok := out.InProgressResources[key]; !ok { - out.InProgressResources[key] = len(dataplanes) - } else { - out.InProgressResources[key] += len(dataplanes) - } - } else if err != nil { - scope.Errorf("Encountered error retrieving version %s of key %s from Store: %v", nonce, key, err) - continue - } else if nonce == r.ledger.RootHash() { - scope.Warnf("Cache appears to be missing latest version of %s", key) - } - if out.InProgressResources[key] >= out.DataPlaneCount { - // if this resource is done reconciling, let's not worry about it anymore - finishedResources = append(finishedResources, res) - // deleting it here doesn't work because we have a read lock and are inside an iterator. - // TODO: this will leak when a resource never reaches 100% before it is replaced. - // TODO: do deletes propagate through this thing? - } - } - } - return out, finishedResources -} - -// For efficiency, we don't want to be checking on resources that have already reached 100% distribution. -// When this happens, we remove them from our watch list. -func (r *Reporter) removeCompletedResource(completedResources []status.Resource) { - r.mu.Lock() - defer r.mu.Unlock() - var toDelete []status.Resource - for _, item := range completedResources { - // TODO: handle cache miss - // if cache miss, need to skip current loop, otherwise is will cause errors like - // invalid memory address or nil pointer dereference - if _, ok := r.inProgressResources[item.ToModelKey()]; !ok { - continue - } - total := r.inProgressResources[item.ToModelKey()].completedIterations + 1 - if int64(total) > (time.Minute.Milliseconds() / r.UpdateInterval.Milliseconds()) { - // remove from inProgressResources - // TODO: cleanup completedResources - toDelete = append(toDelete, item) - } else { - r.inProgressResources[item.ToModelKey()].completedIterations = total - } - } - for _, resource := range toDelete { - delete(r.inProgressResources, resource.ToModelKey()) - } -} - -// AddInProgressResource must be called every time a resource change is detected by pilot. This allows us to lookup -// only the resources we expect to be in flight, not the ones that have already distributed -func (r *Reporter) AddInProgressResource(res config.Config) { - tryLedgerPut(r.ledger, res) - myRes := status.ResourceFromModelConfig(res) - if myRes == (status.Resource{}) { - scope.Errorf("Unable to locate schema for %v, will not update status.", res) - return - } - r.mu.Lock() - defer r.mu.Unlock() - r.inProgressResources[myRes.ToModelKey()] = &inProgressEntry{ - Resource: myRes, - completedIterations: 0, - } -} - -func (r *Reporter) DeleteInProgressResource(res config.Config) { - tryLedgerDelete(r.ledger, res) - if r.controller != nil { - r.controller.configDeleted(res) - } - r.mu.Lock() - defer r.mu.Unlock() - delete(r.inProgressResources, res.Key()) -} - -// generate a distribution report and write it to a ConfigMap for the leader to read. -func (r *Reporter) writeReport(ctx context.Context) { - report, finishedResources := r.buildReport() - go r.removeCompletedResource(finishedResources) - // write to kubernetes here. - reportbytes, err := yaml.Marshal(report) - if err != nil { - scope.Errorf("Error serializing Distribution Report: %v", err) - return - } - r.cm.Data[dataField] = string(reportbytes) - // TODO: short circuit this write in the leader - _, err = CreateOrUpdateConfigMap(ctx, r.cm, r.client) - if err != nil { - scope.Errorf("Error writing Distribution Report: %v", err) - } -} - -// CreateOrUpdateConfigMap is lifted with few modifications from kubeadm's apiclient -func CreateOrUpdateConfigMap(ctx context.Context, cm *corev1.ConfigMap, client v1.ConfigMapInterface) (res *corev1.ConfigMap, err error) { - if res, err = client.Create(ctx, cm, metav1.CreateOptions{}); err != nil { - if !apierrors.IsAlreadyExists(err) { - scope.Errorf("%v", err) - return nil, fmt.Errorf("unable to create ConfigMap: %w", err) - } - - if res, err = client.Update(context.TODO(), cm, metav1.UpdateOptions{}); err != nil { - return nil, fmt.Errorf("unable to update ConfigMap: %w", err) - } - } - return res, nil -} - -type distributionEvent struct { - conID string - distributionType xds.EventType - nonce string -} - -func (r *Reporter) QueryLastNonce(conID string, distributionType xds.EventType) (noncePrefix string) { - key := GenStatusReporterMapKey(conID, distributionType) - r.mu.RLock() - defer r.mu.RUnlock() - return r.status[key] -} - -// Register that a dataplane has acknowledged a new version of the config. -// Theoretically, we could use the ads connections themselves to harvest this data, -// but the mutex there is pretty hot, and it seems best to trade memory for time. -func (r *Reporter) RegisterEvent(conID string, distributionType xds.EventType, nonce string) { - // Skip unsupported event types. This ensures we do not leak memory for types - // which may not be handled properly. For example, a type not in AllEventTypes - // will not be properly unregistered. - if _, f := xds.AllEventTypes[distributionType]; !f { - return - } - d := distributionEvent{nonce: nonce, distributionType: distributionType, conID: conID} - select { - case r.distributionEventQueue <- d: - return - default: - scope.Errorf("Distribution Event Queue overwhelmed, status will be invalid.") - } -} - -func (r *Reporter) readFromEventQueue(stop <-chan struct{}) { - for { - select { - case ev := <-r.distributionEventQueue: - // TODO might need to batch this to prevent lock contention - r.processEvent(ev.conID, ev.distributionType, ev.nonce) - case <-stop: - return - } - } -} - -func (r *Reporter) processEvent(conID string, distributionType xds.EventType, nonce string) { - r.mu.Lock() - defer r.mu.Unlock() - key := GenStatusReporterMapKey(conID, distributionType) - r.deleteKeyFromReverseMap(key) - var version string - if len(nonce) > 12 { - version = nonce[:xds.VersionLen] - } else { - version = nonce - } - // touch - r.status[key] = version - if _, ok := r.reverseStatus[version]; !ok { - r.reverseStatus[version] = sets.New() - } - r.reverseStatus[version].Insert(key) -} - -// This is a helper function for keeping our reverseStatus map in step with status. -// must have write lock before calling. -func (r *Reporter) deleteKeyFromReverseMap(key string) { - if old, ok := r.status[key]; ok { - if keys, ok := r.reverseStatus[old]; ok { - keys.Delete(key) - if keys.IsEmpty() { - delete(r.reverseStatus, old) - } - } - } -} - -// RegisterDisconnect : when a dataplane disconnects, we should no longer count it, nor expect it to ack config. -func (r *Reporter) RegisterDisconnect(conID string, types []xds.EventType) { - r.mu.Lock() - defer r.mu.Unlock() - for _, xdsType := range types { - key := GenStatusReporterMapKey(conID, xdsType) - r.deleteKeyFromReverseMap(key) - delete(r.status, key) - } -} - -func (r *Reporter) SetController(controller *Controller) { - r.controller = controller -} diff --git a/pilot/pkg/status/distribution/reporter_test.go b/pilot/pkg/status/distribution/reporter_test.go deleted file mode 100644 index bba4b41ca..000000000 --- a/pilot/pkg/status/distribution/reporter_test.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package distribution - -import ( - "testing" - "time" -) - -import ( - . "github.com/onsi/gomega" - "istio.io/pkg/ledger" - "k8s.io/utils/clock" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/status" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -func TestStatusMaps(t *testing.T) { - r := initReporterWithoutStarting() - typ := "" - r.processEvent("conA", typ, "a") - r.processEvent("conB", typ, "a") - r.processEvent("conC", typ, "c") - r.processEvent("conD", typ, "d") - RegisterTestingT(t) - x := struct{}{} - Expect(r.status).To(Equal(map[string]string{"conA~": "a", "conB~": "a", "conC~": "c", "conD~": "d"})) - Expect(r.reverseStatus).To(Equal(map[string]sets.Set{"a": {"conA~": x, "conB~": x}, "c": {"conC~": x}, "d": {"conD~": x}})) - r.processEvent("conA", typ, "d") - Expect(r.status).To(Equal(map[string]string{"conA~": "d", "conB~": "a", "conC~": "c", "conD~": "d"})) - Expect(r.reverseStatus).To(Equal(map[string]sets.Set{"a": {"conB~": x}, "c": {"conC~": x}, "d": {"conD~": x, "conA~": x}})) - r.RegisterDisconnect("conA", []xds.EventType{typ}) - Expect(r.status).To(Equal(map[string]string{"conB~": "a", "conC~": "c", "conD~": "d"})) - Expect(r.reverseStatus).To(Equal(map[string]sets.Set{"a": {"conB~": x}, "c": {"conC~": x}, "d": {"conD~": x}})) -} - -func initReporterWithoutStarting() (out Reporter) { - out.PodName = "tespod" - out.inProgressResources = map[string]*inProgressEntry{} - out.client = nil // TODO - out.clock = clock.RealClock{} // TODO - out.UpdateInterval = 300 * time.Millisecond - out.cm = nil // TODO - out.reverseStatus = make(map[string]sets.Set) - out.status = make(map[string]string) - return -} - -func TestBuildReport(t *testing.T) { - RegisterTestingT(t) - r := initReporterWithoutStarting() - r.ledger = ledger.Make(time.Minute) - resources := []*config.Config{ - { - Meta: config.Meta{ - Namespace: "default", - Name: "foo", - ResourceVersion: "1", - }, - }, - { - Meta: config.Meta{ - Namespace: "default", - Name: "bar", - ResourceVersion: "1", - }, - }, - { - Meta: config.Meta{ - Namespace: "alternate", - Name: "boo", - ResourceVersion: "1", - }, - }, - } - // cast our model.Configs to Resource because these types aren't compatible - var myResources []status.Resource - col := collections.IstioNetworkingV1Alpha3Virtualservices.Resource() - for _, res := range resources { - // Set Group Version and GroupVersionKind to real world values from VS - res.GroupVersionKind = col.GroupVersionKind() - myResources = append(myResources, status.ResourceFromModelConfig(*res)) - // Add each resource to our ledger for tracking history - // mark each of our resources as in flight so they are included in the report. - r.AddInProgressResource(*res) - } - firstNoncePrefix := r.ledger.RootHash() - connections := []string{ - "conA", "conB", "conC", - } - // mark each fake connection as having acked version 1 of all resources - for _, con := range connections { - r.processEvent(con, "", firstNoncePrefix) - } - // modify one resource to version 2 - resources[1].Generation = int64(2) - myResources[1].Generation = "2" - // notify the ledger of the new version - r.AddInProgressResource(*resources[1]) - // mark only one connection as having acked version 2 - r.processEvent(connections[1], "", r.ledger.RootHash()) - // mark one connection as having disconnected. - r.RegisterDisconnect(connections[2], []xds.EventType{""}) - // build a report, which should have only two dataplanes, with 50% acking v2 of config - rpt, prunes := r.buildReport() - r.removeCompletedResource(prunes) - Expect(rpt.DataPlaneCount).To(Equal(2)) - Expect(rpt.InProgressResources).To(Equal(map[string]int{ - myResources[0].String(): 2, - myResources[1].String(): 1, - myResources[2].String(): 2, - })) - Expect(r.inProgressResources).NotTo(ContainElement(resources[0])) -} diff --git a/pilot/pkg/status/distribution/resourcelock.go b/pilot/pkg/status/distribution/resourcelock.go deleted file mode 100644 index c66b82ad0..000000000 --- a/pilot/pkg/status/distribution/resourcelock.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package distribution - -type ResourceStatus interface{} diff --git a/pilot/pkg/status/distribution/state.go b/pilot/pkg/status/distribution/state.go deleted file mode 100644 index e817106c3..000000000 --- a/pilot/pkg/status/distribution/state.go +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package distribution - -import ( - "fmt" - "strings" - "sync" - "time" -) - -import ( - "google.golang.org/protobuf/types/known/timestamppb" - "istio.io/api/meta/v1alpha1" - "istio.io/pkg/log" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/cache" - "k8s.io/utils/clock" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/status" - "github.com/apache/dubbo-go-pixiu/pkg/config" -) - -var scope = log.RegisterScope("status", - "CRD distribution status debugging", 0) - -type Progress struct { - AckedInstances int - TotalInstances int -} - -func (p *Progress) PlusEquals(p2 Progress) { - p.TotalInstances += p2.TotalInstances - p.AckedInstances += p2.AckedInstances -} - -type Controller struct { - configStore model.ConfigStore - mu sync.RWMutex - CurrentState map[status.Resource]map[string]Progress - ObservationTime map[string]time.Time - UpdateInterval time.Duration - dynamicClient dynamic.Interface - clock clock.Clock - workers *status.Controller - StaleInterval time.Duration - cmInformer cache.SharedIndexInformer -} - -func NewController(restConfig *rest.Config, namespace string, cs model.ConfigStore, m *status.Manager) *Controller { - c := &Controller{ - CurrentState: make(map[status.Resource]map[string]Progress), - ObservationTime: make(map[string]time.Time), - UpdateInterval: 200 * time.Millisecond, - StaleInterval: time.Minute, - clock: clock.RealClock{}, - configStore: cs, - workers: m.CreateIstioStatusController(func(status *v1alpha1.IstioStatus, context interface{}) *v1alpha1.IstioStatus { - if status == nil { - return nil - } - distributionState := context.(Progress) - if needsReconcile, desiredStatus := ReconcileStatuses(status, distributionState); needsReconcile { - return desiredStatus - } - return status - }), - } - - // client-go defaults to 5 QPS, with 10 Boost, which is insufficient for updating status on all the config - // in the mesh. These values can be configured using environment variables for tuning (see pilot/pkg/features) - restConfig.QPS = float32(features.StatusQPS) - restConfig.Burst = features.StatusBurst - var err error - if c.dynamicClient, err = dynamic.NewForConfig(restConfig); err != nil { - scope.Fatalf("Could not connect to kubernetes: %s", err) - } - - // configmap informer - i := informers.NewSharedInformerFactoryWithOptions(kubernetes.NewForConfigOrDie(restConfig), 1*time.Minute, - informers.WithNamespace(namespace), - informers.WithTweakListOptions(func(listOptions *metav1.ListOptions) { - listOptions.LabelSelector = labels.Set(map[string]string{labelKey: "true"}).AsSelector().String() - })). - Core().V1().ConfigMaps() - c.cmInformer = i.Informer() - i.Informer().AddEventHandler(&DistroReportHandler{dc: c}) - - return c -} - -func (c *Controller) Start(stop <-chan struct{}) { - scope.Info("Starting status leader controller") - - // this will list all existing configmaps, as well as updates, right? - ctx := status.NewIstioContext(stop) - go c.cmInformer.Run(ctx.Done()) - - // create Status Writer - t := c.clock.Tick(c.UpdateInterval) - - go func() { - for { - select { - case <-ctx.Done(): - return - case <-t: - staleReporters := c.writeAllStatus() - if len(staleReporters) > 0 { - c.removeStaleReporters(staleReporters) - } - } - } - }() -} - -func (c *Controller) handleReport(d Report) { - defer c.mu.Unlock() - c.mu.Lock() - for resstr := range d.InProgressResources { - res := *status.ResourceFromString(resstr) - if _, ok := c.CurrentState[res]; !ok { - c.CurrentState[res] = make(map[string]Progress) - } - c.CurrentState[res][d.Reporter] = Progress{d.InProgressResources[resstr], d.DataPlaneCount} - } - c.ObservationTime[d.Reporter] = c.clock.Now() -} - -func (c *Controller) writeAllStatus() (staleReporters []string) { - defer c.mu.RUnlock() - c.mu.RLock() - for config, fractions := range c.CurrentState { - if !strings.HasSuffix(config.Group, "istio.io") { - // don't try to write status for non-istio types - continue - } - var distributionState Progress - for reporter, w := range fractions { - // check for stale data here - if c.clock.Since(c.ObservationTime[reporter]) > c.StaleInterval { - scope.Warnf("Status reporter %s has not been heard from since %v, deleting report.", - reporter, c.ObservationTime[reporter]) - staleReporters = append(staleReporters, reporter) - } else { - distributionState.PlusEquals(w) - } - } - if distributionState.TotalInstances > 0 { // this is necessary when all reports are stale. - c.queueWriteStatus(config, distributionState) - } - } - return -} - -func (c *Controller) pruneOldVersion(config status.Resource) { - defer c.mu.Unlock() - c.mu.Lock() - delete(c.CurrentState, config) -} - -func (c *Controller) removeStaleReporters(staleReporters []string) { - defer c.mu.Unlock() - c.mu.Lock() - for key, fractions := range c.CurrentState { - for _, staleReporter := range staleReporters { - delete(fractions, staleReporter) - } - c.CurrentState[key] = fractions - } -} - -func (c *Controller) queueWriteStatus(config status.Resource, state Progress) { - c.workers.EnqueueStatusUpdateResource(state, config) -} - -func (c *Controller) configDeleted(res config.Config) { - r := status.ResourceFromModelConfig(res) - c.workers.Delete(r) -} - -func boolToConditionStatus(b bool) string { - if b { - return "True" - } - return "False" -} - -func ReconcileStatuses(current *v1alpha1.IstioStatus, desired Progress) (bool, *v1alpha1.IstioStatus) { - needsReconcile := false - desiredCondition := v1alpha1.IstioCondition{ - Type: "Reconciled", - Status: boolToConditionStatus(desired.AckedInstances == desired.TotalInstances), - LastProbeTime: timestamppb.Now(), - LastTransitionTime: timestamppb.Now(), - Message: fmt.Sprintf("%d/%d proxies up to date.", desired.AckedInstances, desired.TotalInstances), - } - current = current.DeepCopy() - var currentCondition *v1alpha1.IstioCondition - conditionIndex := -1 - for i, c := range current.Conditions { - if c.Type == "Reconciled" { - currentCondition = current.Conditions[i] - conditionIndex = i - break - } - } - if currentCondition == nil || - currentCondition.Message != desiredCondition.Message || - currentCondition.Status != desiredCondition.Status { - needsReconcile = true - } - if conditionIndex > -1 { - current.Conditions[conditionIndex] = &desiredCondition - } else { - current.Conditions = append(current.Conditions, &desiredCondition) - } - return needsReconcile, current -} - -type DistroReportHandler struct { - dc *Controller -} - -func (drh *DistroReportHandler) OnAdd(obj interface{}) { - drh.HandleNew(obj) -} - -func (drh *DistroReportHandler) OnUpdate(oldObj, newObj interface{}) { - drh.HandleNew(newObj) -} - -func (drh *DistroReportHandler) HandleNew(obj interface{}) { - cm, ok := obj.(*v1.ConfigMap) - if !ok { - scope.Warnf("expected configmap, but received %v, discarding", obj) - return - } - rptStr := cm.Data[dataField] - scope.Debugf("using report: %s", rptStr) - dr, err := ReportFromYaml([]byte(cm.Data[dataField])) - if err != nil { - scope.Warnf("received malformed distributionReport %s, discarding: %v", cm.Name, err) - return - } - drh.dc.handleReport(dr) -} - -func (drh *DistroReportHandler) OnDelete(obj interface{}) { - // TODO: what do we do here? will these ever be deleted? -} diff --git a/pilot/pkg/status/distribution/state_test.go b/pilot/pkg/status/distribution/state_test.go deleted file mode 100644 index bbc1e8114..000000000 --- a/pilot/pkg/status/distribution/state_test.go +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package distribution - -import ( - "encoding/json" - "reflect" - "testing" -) - -import ( - "istio.io/api/meta/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/status" - "github.com/apache/dubbo-go-pixiu/pkg/config" -) - -var statusStillPropagating = &v1alpha1.IstioStatus{ - Conditions: []*v1alpha1.IstioCondition{ - { - Type: "PassedValidation", - Status: "True", - Message: "just a test, here", - }, - { - Type: "Reconciled", - Status: "False", - Message: "1/2 proxies up to date.", - }, - }, - ValidationMessages: nil, -} - -func TestReconcileStatuses(t *testing.T) { - type args struct { - current *config.Config - desired Progress - } - tests := []struct { - name string - args args - want bool - want1 *v1alpha1.IstioStatus - }{ - { - name: "Don't Reconcile when other fields are the only diff", - args: args{ - current: &config.Config{Status: statusStillPropagating}, - desired: Progress{1, 2}, - }, - want: false, - }, { - name: "Simple Reconcile to true", - args: args{ - current: &config.Config{Status: statusStillPropagating}, - desired: Progress{1, 3}, - }, - want: true, - want1: &v1alpha1.IstioStatus{ - Conditions: []*v1alpha1.IstioCondition{ - { - Type: "PassedValidation", - Status: "True", - Message: "just a test, here", - }, - { - Type: "Reconciled", - Status: "False", - Message: "1/3 proxies up to date.", - }, - }, - ValidationMessages: nil, - }, - }, { - name: "Simple Reconcile to false", - args: args{ - current: &config.Config{Status: statusStillPropagating}, - desired: Progress{2, 2}, - }, - want: true, - want1: &v1alpha1.IstioStatus{ - Conditions: []*v1alpha1.IstioCondition{ - { - Type: "PassedValidation", - Status: "True", - Message: "just a test, here", - }, - { - Type: "Reconciled", - Status: "True", - Message: "2/2 proxies up to date.", - }, - }, - ValidationMessages: nil, - }, - }, { - name: "Reconcile for message difference", - args: args{ - current: &config.Config{Status: statusStillPropagating}, - desired: Progress{2, 3}, - }, - want: true, - want1: &v1alpha1.IstioStatus{ - Conditions: []*v1alpha1.IstioCondition{ - { - Type: "PassedValidation", - Status: "True", - Message: "just a test, here", - }, - { - Type: "Reconciled", - Status: "False", - Message: "2/3 proxies up to date.", - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, got1 := ReconcileStatuses(tt.args.current.Status.(*v1alpha1.IstioStatus), tt.args.desired) - if got != tt.want { - t.Errorf("ReconcileStatuses() got = %v, want %v", got, tt.want) - } - if tt.want1 != nil { - for i := range tt.want1.Conditions { - if got1 != nil && i < len(got1.Conditions) { - tt.want1.Conditions[i].LastTransitionTime = got1.Conditions[i].LastTransitionTime - tt.want1.Conditions[i].LastProbeTime = got1.Conditions[i].LastProbeTime - } - } - if !reflect.DeepEqual(got1, tt.want1) { - t.Errorf("ReconcileStatuses() got1 = %v, want %v", got1, tt.want1) - } - } - }) - } -} - -func Test_getTypedStatus(t *testing.T) { - x := v1alpha1.IstioStatus{} - b, _ := json.Marshal(statusStillPropagating) - _ = json.Unmarshal(b, &x) - type args struct { - in interface{} - } - tests := []struct { - name string - args args - wantOut *v1alpha1.IstioStatus - wantErr bool - }{ - { - name: "Nondestructive cast", - args: args{in: statusStillPropagating}, - wantOut: statusStillPropagating, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotOut, err := status.GetTypedStatus(tt.args.in) - if (err != nil) != tt.wantErr { - t.Errorf("GetTypedStatus() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(gotOut, tt.wantOut) { - t.Errorf("GetTypedStatus() gotOut = %v, want %v", gotOut, tt.wantOut) - } - }) - } -} diff --git a/pilot/pkg/status/manager.go b/pilot/pkg/status/manager.go deleted file mode 100644 index 187a6f3d1..000000000 --- a/pilot/pkg/status/manager.go +++ /dev/null @@ -1,128 +0,0 @@ -/* - Copyright Istio Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package status - -import ( - "istio.io/api/meta/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// Manager allows multiple controllers to provide input into configuration -// status without needlessly doubling the number of writes, or overwriting -// one another. Each status controller calls newController, passing in -// an arbitrary status modification function, and then calls EnqueueStatusUpdate -// when an individual resource is ready to be updated with the relevant data. -type Manager struct { - // TODO: is Resource the right abstraction? - store model.ConfigStore - workers WorkerQueue -} - -func NewManager(store model.ConfigStore) *Manager { - writeFunc := func(m *config.Config, istatus interface{}) { - scope.Debugf("writing status for resource %s/%s", m.Namespace, m.Name) - status := istatus.(GenerationProvider) - m.Status = status.Unwrap() - _, err := store.UpdateStatus(*m) - if err != nil { - // TODO: need better error handling - scope.Errorf("Encountered unexpected error updating status for %v, will try again later: %s", m, err) - return - } - } - retrieveFunc := func(resource Resource) *config.Config { - scope.Debugf("retrieving config for status update: %s/%s", resource.Namespace, resource.Name) - schema, _ := collections.All.FindByGroupVersionResource(resource.GroupVersionResource) - if schema == nil { - scope.Warnf("schema %v could not be identified", schema) - return nil - } - - current := store.Get(schema.Resource().GroupVersionKind(), resource.Name, resource.Namespace) - return current - } - return &Manager{ - store: store, - workers: NewWorkerPool(writeFunc, retrieveFunc, uint(features.StatusMaxWorkers)), - } -} - -func (m *Manager) Start(stop <-chan struct{}) { - scope.Info("Starting status manager") - - ctx := NewIstioContext(stop) - m.workers.Run(ctx) -} - -// CreateGenericController provides an interface for a status update function to be -// called in series with other controllers, minimizing the number of actual -// api server writes sent from various status controllers. The UpdateFunc -// must take the target resrouce status and arbitrary context information as -// parameters, and return the updated status value. Multiple controllers -// will be called in series, so the input status may not have been written -// to the API server yet, and the output status may be modified by other -// controllers before it is written to the server. -func (m *Manager) CreateGenericController(fn UpdateFunc) *Controller { - result := &Controller{ - fn: fn, - workers: m.workers, - } - return result -} - -func (m *Manager) CreateIstioStatusController(fn func(status *v1alpha1.IstioStatus, context interface{}) *v1alpha1.IstioStatus) *Controller { - wrapper := func(status interface{}, context interface{}) GenerationProvider { - var input *v1alpha1.IstioStatus - if status != nil { - converted := status.(*IstioGenerationProvider) - input = converted.IstioStatus - } - result := fn(input, context) - return &IstioGenerationProvider{result} - } - result := &Controller{ - fn: wrapper, - workers: m.workers, - } - return result -} - -type UpdateFunc func(status interface{}, context interface{}) GenerationProvider - -type Controller struct { - fn UpdateFunc - workers WorkerQueue -} - -// EnqueueStatusUpdateResource informs the manager that this controller would like to -// update the status of target, using the information in context. Once the status -// workers are ready to perform this update, the controller's UpdateFunc -// will be called with target and context as input. -func (c *Controller) EnqueueStatusUpdateResource(context interface{}, target Resource) { - // TODO: buffer this with channel - c.workers.Push(target, c, context) -} - -func (c *Controller) Delete(r Resource) { - c.workers.Delete(r) -} diff --git a/pilot/pkg/status/resource.go b/pilot/pkg/status/resource.go deleted file mode 100644 index 0e8800f81..000000000 --- a/pilot/pkg/status/resource.go +++ /dev/null @@ -1,157 +0,0 @@ -/* - Copyright Istio Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package status - -import ( - "context" - "fmt" - "strconv" - "strings" -) - -import ( - "istio.io/api/meta/v1alpha1" - "istio.io/pkg/log" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -var scope = log.RegisterScope("status", - "status controller for istio", 0) - -func ResourceFromString(s string) *Resource { - pieces := strings.Split(s, "/") - if len(pieces) != 6 { - scope.Errorf("cannot unmarshal %s into resource identifier", s) - return nil - } - return &Resource{ - GroupVersionResource: schema.GroupVersionResource{ - Group: pieces[0], - Version: pieces[1], - Resource: pieces[2], - }, - Namespace: pieces[3], - Name: pieces[4], - Generation: pieces[5], - } -} - -// TODO: maybe replace with a kubernetes resource identifier, if that's a thing -type Resource struct { - schema.GroupVersionResource - Namespace string - Name string - Generation string -} - -func (r Resource) String() string { - return strings.Join([]string{r.Group, r.Version, r.GroupVersionResource.Resource, r.Namespace, r.Name, r.Generation}, "/") -} - -func (r *Resource) ToModelKey() string { - // we have a resource here, but model keys use kind. Use the schema to find the correct kind. - found, _ := collections.All.FindByPlural(r.Group, r.Version, r.Resource) - return config.Key( - found.Resource().Group(), found.Resource().Version(), found.Resource().Kind(), - r.Name, r.Namespace) -} - -func ResourceFromMetadata(i resource.Metadata) Resource { - return Resource{ - GroupVersionResource: i.Schema.GroupVersionResource(), - Namespace: i.FullName.Namespace.String(), - Name: i.FullName.Name.String(), - Generation: strconv.FormatInt(i.Generation, 10), - } -} - -func ResourceFromModelConfig(c config.Config) Resource { - gvr := GVKtoGVR(c.GroupVersionKind) - if gvr == nil { - return Resource{} - } - return Resource{ - GroupVersionResource: *gvr, - Namespace: c.Namespace, - Name: c.Name, - Generation: strconv.FormatInt(c.Generation, 10), - } -} - -func ResourceToModelConfig(c Resource) config.Meta { - gvk := GVRtoGVK(c.GroupVersionResource) - gen, err := strconv.Atoi(c.Generation) - if err != nil { - log.Errorf("failed to convert resource generation %s to int: %s", c.Generation, err) - return config.Meta{} - } - return config.Meta{ - GroupVersionKind: gvk, - Namespace: c.Namespace, - Name: c.Name, - Generation: int64(gen), - } -} - -func GetTypedStatus(in interface{}) (out *v1alpha1.IstioStatus, err error) { - if ret, ok := in.(*v1alpha1.IstioStatus); ok { - return ret, nil - } - return nil, fmt.Errorf("cannot cast %T: %v to IstioStatus", in, in) -} - -func GetOGProvider(in interface{}) (out GenerationProvider, err error) { - if ret, ok := in.(*v1alpha1.IstioStatus); ok { - return &IstioGenerationProvider{ret}, nil - } - return nil, fmt.Errorf("cannot cast %T: %v to GenerationProvider", in, in) -} - -func GVKtoGVR(in config.GroupVersionKind) *schema.GroupVersionResource { - found, ok := collections.All.FindByGroupVersionKind(in) - if !ok { - return nil - } - return &schema.GroupVersionResource{ - Group: in.Group, - Version: in.Version, - Resource: found.Resource().Plural(), - } -} - -func GVRtoGVK(in schema.GroupVersionResource) config.GroupVersionKind { - found, ok := collections.All.FindByGroupVersionResource(in) - if !ok { - return config.GroupVersionKind{} - } - return found.Resource().GroupVersionKind() -} - -func NewIstioContext(stop <-chan struct{}) context.Context { - ctx, cancel := context.WithCancel(context.Background()) - go func() { - <-stop - cancel() - }() - return ctx -} diff --git a/pilot/pkg/status/resourcelock.go b/pilot/pkg/status/resourcelock.go deleted file mode 100644 index b724e3b44..000000000 --- a/pilot/pkg/status/resourcelock.go +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package status - -import ( - "context" - "strconv" - "sync" -) - -import ( - "istio.io/api/meta/v1alpha1" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" -) - -// Task to be performed. -type Task func(entry cacheEntry) - -// WorkerQueue implements an expandable goroutine pool which executes at most one concurrent routine per target -// resource. Multiple calls to Push() will not schedule multiple executions per target resource, but will ensure that -// the single execution uses the latest value. -type WorkerQueue interface { - // Push a task. - Push(target Resource, controller *Controller, context interface{}) - // Run the loop until a signal on the context - Run(ctx context.Context) - // Delete a task - Delete(target Resource) -} - -type cacheEntry struct { - // the cacheVale represents the latest version of the resource, including ResourceVersion - cacheResource Resource - // the perControllerStatus represents the latest version of the ResourceStatus - perControllerStatus map[*Controller]interface{} -} - -type lockResource struct { - schema.GroupVersionResource - Namespace string - Name string -} - -func convert(i Resource) lockResource { - return lockResource{ - GroupVersionResource: i.GroupVersionResource, - Namespace: i.Namespace, - Name: i.Name, - } -} - -type WorkQueue struct { - // tasks which are not currently executing but need to run - tasks []lockResource - // a lock to govern access to data in the cache - lock sync.Mutex - // for each task, a cacheEntry which can be updated before the task is run so that execution will have latest values - cache map[lockResource]cacheEntry - - OnPush func() -} - -func (wq *WorkQueue) Push(target Resource, ctl *Controller, progress interface{}) { - wq.lock.Lock() - key := convert(target) - if item, inqueue := wq.cache[key]; inqueue { - item.perControllerStatus[ctl] = progress - wq.cache[key] = item - } else { - wq.cache[key] = cacheEntry{ - cacheResource: target, - perControllerStatus: map[*Controller]interface{}{ctl: progress}, - } - wq.tasks = append(wq.tasks, key) - } - wq.lock.Unlock() - if wq.OnPush != nil { - wq.OnPush() - } -} - -// Pop returns the first item in the queue not in exclusion, along with it's latest progress -func (wq *WorkQueue) Pop(exclusion map[lockResource]struct{}) (target Resource, progress map[*Controller]interface{}) { - wq.lock.Lock() - defer wq.lock.Unlock() - for i := 0; i < len(wq.tasks); i++ { - if _, ok := exclusion[wq.tasks[i]]; !ok { - // remove from tasks - t, ok := wq.cache[wq.tasks[i]] - wq.tasks = append(wq.tasks[:i], wq.tasks[i+1:]...) - if !ok { - return Resource{}, nil - } - return t.cacheResource, t.perControllerStatus - } - } - return Resource{}, nil -} - -func (wq *WorkQueue) Length() int { - wq.lock.Lock() - defer wq.lock.Unlock() - return len(wq.tasks) -} - -func (wq *WorkQueue) Delete(target Resource) { - wq.lock.Lock() - defer wq.lock.Unlock() - delete(wq.cache, convert(target)) -} - -type WorkerPool struct { - q WorkQueue - // indicates the queue is closing - closing bool - // the function which will be run for each task in queue - write func(*config.Config, interface{}) - // the function to retrieve the initial status - get func(Resource) *config.Config - // current worker routine count - workerCount uint - // maximum worker routine count - maxWorkers uint - currentlyWorking map[lockResource]struct{} - lock sync.Mutex -} - -func NewWorkerPool(write func(*config.Config, interface{}), get func(Resource) *config.Config, maxWorkers uint) WorkerQueue { - return &WorkerPool{ - write: write, - get: get, - maxWorkers: maxWorkers, - currentlyWorking: make(map[lockResource]struct{}), - q: WorkQueue{ - tasks: make([]lockResource, 0), - cache: make(map[lockResource]cacheEntry), - OnPush: nil, - }, - } -} - -func (wp *WorkerPool) Delete(target Resource) { - wp.q.Delete(target) -} - -func (wp *WorkerPool) Push(target Resource, controller *Controller, context interface{}) { - wp.q.Push(target, controller, context) - wp.maybeAddWorker() -} - -func (wp *WorkerPool) Run(ctx context.Context) { - go func() { - <-ctx.Done() - wp.lock.Lock() - wp.closing = true - wp.lock.Unlock() - }() -} - -// maybeAddWorker adds a worker unless we are at maxWorkers. Workers exit when there are no more tasks, except for the -// last worker, which stays alive indefinitely. -func (wp *WorkerPool) maybeAddWorker() { - wp.lock.Lock() - if wp.workerCount >= wp.maxWorkers || wp.q.Length() == 0 { - wp.lock.Unlock() - return - } - wp.workerCount++ - wp.lock.Unlock() - go func() { - for { - wp.lock.Lock() - if wp.closing || wp.q.Length() == 0 { - wp.workerCount-- - wp.lock.Unlock() - return - } - - target, perControllerWork := wp.q.Pop(wp.currentlyWorking) - - if target == (Resource{}) { - // continue or return? - // could have been deleted, or could be no items in queue not currently worked on. need a way to differentiate. - wp.lock.Unlock() - continue - } - wp.q.Delete(target) - wp.currentlyWorking[convert(target)] = struct{}{} - wp.lock.Unlock() - // work should be done without holding the lock - cfg := wp.get(target) - if cfg != nil { - // Check that generation matches - if strconv.FormatInt(cfg.Generation, 10) == target.Generation { - x, err := GetOGProvider(cfg.Status) - if err == nil { - // Not all controllers user generation, so we can ignore errors - x.SetObservedGeneration(cfg.Generation) - } - for c, i := range perControllerWork { - // TODO: this does not guarantee controller order. perhaps it should? - x = c.fn(x, i) - } - wp.write(cfg, x) - } - } - wp.lock.Lock() - delete(wp.currentlyWorking, convert(target)) - wp.lock.Unlock() - } - }() -} - -type GenerationProvider interface { - SetObservedGeneration(int64) - Unwrap() interface{} -} - -type IstioGenerationProvider struct { - *v1alpha1.IstioStatus -} - -func (i *IstioGenerationProvider) SetObservedGeneration(in int64) { - i.ObservedGeneration = in -} - -func (i *IstioGenerationProvider) Unwrap() interface{} { - return i.IstioStatus -} diff --git a/pilot/pkg/status/resourcelock_test.go b/pilot/pkg/status/resourcelock_test.go deleted file mode 100644 index ad3544ae7..000000000 --- a/pilot/pkg/status/resourcelock_test.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package status - -import ( - "context" - "sync/atomic" - "testing" -) - -import ( - . "github.com/onsi/gomega" - "istio.io/api/meta/v1alpha1" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" -) - -func TestResourceLock_Lock(t *testing.T) { - g := NewGomegaWithT(t) - r1 := Resource{ - GroupVersionResource: schema.GroupVersionResource{ - Group: "r1", - Version: "r1", - }, - Namespace: "r1", - Name: "r1", - Generation: "11", - } - r1a := Resource{ - GroupVersionResource: schema.GroupVersionResource{ - Group: "r1", - Version: "r1", - }, - Namespace: "r1", - Name: "r1", - Generation: "12", - } - var runCount int32 - x := make(chan struct{}) - y := make(chan struct{}) - mgr := NewManager(nil) - fakefunc := func(status *v1alpha1.IstioStatus, context interface{}) *v1alpha1.IstioStatus { - x <- struct{}{} - atomic.AddInt32(&runCount, 1) - y <- struct{}{} - return nil - } - c1 := mgr.CreateIstioStatusController(fakefunc) - c2 := mgr.CreateIstioStatusController(fakefunc) - workers := NewWorkerPool(func(_ *config.Config, _ interface{}) { - }, func(resource Resource) *config.Config { - return &config.Config{ - Meta: config.Meta{Generation: 11}, - } - }, 10) - ctx, cancel := context.WithCancel(context.Background()) - workers.Run(ctx) - workers.Push(r1, c1, nil) - workers.Push(r1, c2, nil) - workers.Push(r1, c1, nil) - <-x - <-y - <-x - workers.Push(r1, c1, nil) - workers.Push(r1a, c1, nil) - <-y - <-x - select { - case <-x: - t.FailNow() - default: - } - <-y - result := atomic.LoadInt32(&runCount) - g.Expect(result).To(Equal(int32(3))) - cancel() -} diff --git a/pilot/pkg/trustbundle/leak_test.go b/pilot/pkg/trustbundle/leak_test.go deleted file mode 100644 index 3eb5cf8b9..000000000 --- a/pilot/pkg/trustbundle/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package trustbundle - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/trustbundle/trustbundle.go b/pilot/pkg/trustbundle/trustbundle.go deleted file mode 100644 index f459b93a0..000000000 --- a/pilot/pkg/trustbundle/trustbundle.go +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package trustbundle - -import ( - "crypto/x509" - "encoding/pem" - "fmt" - "sort" - "strings" - "sync" - "time" -) - -import ( - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/spiffe" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// Source is all possible sources of MeshConfig -type Source int - -type TrustAnchorConfig struct { - Certs []string -} - -type TrustAnchorUpdate struct { - TrustAnchorConfig - Source Source -} - -type TrustBundle struct { - sourceConfig map[Source]TrustAnchorConfig - mutex sync.RWMutex - mergedCerts []string - updatecb func() - endpointMutex sync.RWMutex - endpoints []string - endpointUpdateChan chan struct{} - remoteCaCertPool *x509.CertPool -} - -var ( - trustBundleLog = log.RegisterScope("trustBundle", "Workload mTLS trust bundle logs", 0) - remoteTimeout = 10 * time.Second -) - -const ( - SourceIstioCA Source = iota - SourceMeshConfig - SourceIstioRA - sourceSpiffeEndpoints - - RemoteDefaultPollPeriod = 30 * time.Minute -) - -func isEqSliceStr(certs1 []string, certs2 []string) bool { - if len(certs1) != len(certs2) { - return false - } - for i := range certs1 { - if certs1[i] != certs2[i] { - return false - } - } - return true -} - -// NewTrustBundle returns a new trustbundle -func NewTrustBundle(remoteCaCertPool *x509.CertPool) *TrustBundle { - var err error - tb := &TrustBundle{ - sourceConfig: map[Source]TrustAnchorConfig{ - SourceIstioCA: {Certs: []string{}}, - SourceMeshConfig: {Certs: []string{}}, - SourceIstioRA: {Certs: []string{}}, - sourceSpiffeEndpoints: {Certs: []string{}}, - }, - mergedCerts: []string{}, - updatecb: nil, - endpointUpdateChan: make(chan struct{}, 1), - endpoints: []string{}, - } - if remoteCaCertPool == nil { - tb.remoteCaCertPool, err = x509.SystemCertPool() - if err != nil { - trustBundleLog.Errorf("failed to initialize remote Cert pool: %v", err) - } - } else { - tb.remoteCaCertPool = remoteCaCertPool - } - return tb -} - -func (tb *TrustBundle) UpdateCb(updatecb func()) { - tb.updatecb = updatecb -} - -// GetTrustBundle : Retrieves all the trustAnchors for current Spiffee Trust Domain -func (tb *TrustBundle) GetTrustBundle() []string { - tb.mutex.RLock() - defer tb.mutex.RUnlock() - trustedCerts := make([]string, len(tb.mergedCerts)) - copy(trustedCerts, tb.mergedCerts) - return trustedCerts -} - -func verifyTrustAnchor(trustAnchor string) error { - block, _ := pem.Decode([]byte(trustAnchor)) - if block == nil { - return fmt.Errorf("failed to decode pem certificate") - } - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return fmt.Errorf("failed to parse X.509 certificate: %v", err) - } - if !cert.IsCA { - return fmt.Errorf("certificate is not a CA certificate") - } - return nil -} - -func (tb *TrustBundle) mergeInternal() { - var mergeCerts []string - certMap := sets.New() - - tb.mutex.Lock() - defer tb.mutex.Unlock() - - for _, configSource := range tb.sourceConfig { - for _, cert := range configSource.Certs { - if !certMap.Contains(cert) { - certMap.Insert(cert) - mergeCerts = append(mergeCerts, cert) - } - } - } - tb.mergedCerts = mergeCerts - sort.Strings(tb.mergedCerts) -} - -// UpdateTrustAnchor : External Function to merge a TrustAnchor config with the existing TrustBundle -func (tb *TrustBundle) UpdateTrustAnchor(anchorConfig *TrustAnchorUpdate) error { - var ok bool - var err error - - tb.mutex.RLock() - cachedConfig, ok := tb.sourceConfig[anchorConfig.Source] - tb.mutex.RUnlock() - if !ok { - return fmt.Errorf("invalid source of TrustBundle configuration %v", anchorConfig.Source) - } - - // Check if anything needs to be changed at all - if isEqSliceStr(anchorConfig.Certs, cachedConfig.Certs) { - trustBundleLog.Debugf("no change to trustAnchor configuration after recent update") - return nil - } - - for _, cert := range anchorConfig.Certs { - err = verifyTrustAnchor(cert) - if err != nil { - return err - } - } - tb.mutex.Lock() - tb.sourceConfig[anchorConfig.Source] = anchorConfig.TrustAnchorConfig - tb.mutex.Unlock() - tb.mergeInternal() - - trustBundleLog.Infof("updating Source %v with certs %v", - anchorConfig.Source, - strings.Join(anchorConfig.TrustAnchorConfig.Certs, "\n")) - - if tb.updatecb != nil { - tb.updatecb() - } - return nil -} - -func (tb *TrustBundle) updateRemoteEndpoint(spiffeEndpoints []string) { - tb.endpointMutex.RLock() - remoteEndpoints := tb.endpoints - tb.endpointMutex.RUnlock() - - if isEqSliceStr(spiffeEndpoints, remoteEndpoints) { - return - } - trustBundleLog.Infof("updated remote endpoints :%v", spiffeEndpoints) - tb.endpointMutex.Lock() - tb.endpoints = spiffeEndpoints - tb.endpointMutex.Unlock() - tb.endpointUpdateChan <- struct{}{} -} - -// AddMeshConfigUpdate : Update trustAnchor configurations from meshConfig -func (tb *TrustBundle) AddMeshConfigUpdate(cfg *meshconfig.MeshConfig) error { - var err error - if cfg != nil { - certs := []string{} - endpoints := []string{} - for _, pemCert := range cfg.GetCaCertificates() { - cert := pemCert.GetPem() - if cert != "" { - certs = append(certs, cert) - } else if pemCert.GetSpiffeBundleUrl() != "" { - endpoints = append(endpoints, pemCert.GetSpiffeBundleUrl()) - } - } - - err = tb.UpdateTrustAnchor(&TrustAnchorUpdate{ - TrustAnchorConfig: TrustAnchorConfig{Certs: certs}, - Source: SourceMeshConfig, - }) - if err != nil { - trustBundleLog.Errorf("failed to update meshConfig PEM trustAnchors: %v", err) - return err - } - - tb.updateRemoteEndpoint(endpoints) - } - return nil -} - -func (tb *TrustBundle) fetchRemoteTrustAnchors() { - var err error - - tb.endpointMutex.RLock() - remoteEndpoints := tb.endpoints - tb.endpointMutex.RUnlock() - remoteCerts := []string{} - - currentTrustDomain := spiffe.GetTrustDomain() - for _, endpoint := range remoteEndpoints { - trustDomainAnchorMap, err := spiffe.RetrieveSpiffeBundleRootCerts( - map[string]string{currentTrustDomain: endpoint}, tb.remoteCaCertPool, remoteTimeout) - if err != nil { - trustBundleLog.Errorf("unable to fetch trust Anchors from endpoint %s: %s", endpoint, err) - continue - } - certs := trustDomainAnchorMap[currentTrustDomain] - for _, cert := range certs { - certStr := string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})) - trustBundleLog.Debugf("from endpoint %v, fetched trust anchor cert: %v", endpoint, certStr) - remoteCerts = append(remoteCerts, certStr) - } - } - err = tb.UpdateTrustAnchor(&TrustAnchorUpdate{ - TrustAnchorConfig: TrustAnchorConfig{Certs: remoteCerts}, - Source: sourceSpiffeEndpoints, - }) - if err != nil { - trustBundleLog.Errorf("failed to update meshConfig Spiffe trustAnchors: %v", err) - } -} - -func (tb *TrustBundle) ProcessRemoteTrustAnchors(stop <-chan struct{}, pollInterval time.Duration) { - ticker := time.NewTicker(pollInterval) - defer ticker.Stop() - for { - select { - case <-ticker.C: - trustBundleLog.Infof("waking up to perform periodic checks") - tb.fetchRemoteTrustAnchors() - case <-stop: - trustBundleLog.Infof("stop processing endpoint trustAnchor updates") - return - case <-tb.endpointUpdateChan: - tb.fetchRemoteTrustAnchors() - trustBundleLog.Infof("processing endpoint trustAnchor Updates for config change") - } - } -} diff --git a/pilot/pkg/trustbundle/trustbundle_test.go b/pilot/pkg/trustbundle/trustbundle_test.go deleted file mode 100644 index e55d459a9..000000000 --- a/pilot/pkg/trustbundle/trustbundle_test.go +++ /dev/null @@ -1,340 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package trustbundle - -import ( - "crypto/x509" - "fmt" - "net/http" - "net/http/httptest" - "os" - "path" - "sort" - "testing" - "time" -) - -import ( - meshconfig "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -func readCertFromFile(filename string) string { - csrBytes, err := os.ReadFile(filename) - if err != nil { - return "" - } - return string(csrBytes) -} - -var ( - malformedCert = "Malformed" - rootCACert = readCertFromFile(path.Join(env.IstioSrc, "samples/certs", "root-cert.pem")) - nonCaCert = readCertFromFile(path.Join(env.IstioSrc, "samples/certs", "workload-bar-cert.pem")) - intermediateCACert = readCertFromFile(path.Join(env.IstioSrc, "samples/certs", "ca-cert.pem")) - - // borrowed from the spiffe package, spiffe_test.go - validSpiffeX509Bundle = ` -{ - "spiffe_sequence": 1, - "spiffe_refresh_hint": 450000, - "keys": [ - { - "kty": "RSA", - "use": "x509-svid", - "n": "r10W2IcjT-vvSTpaFsS4OAcPOX87kw-zKZuJgXhxDhkOQyBdPZpUfK4H8yZ2q14Laym4bmiMLocIeGP70k` + - `UXcp9T4SP-P0DmBTPx3hVgP3YteHzaKsja056VtDs9kAufmFGemTSCenMt7aSlryUbLRO0H-__fTeNkCXR7uIoq` + - `RfU6jL0nN4EBh02q724iGuX6dpJcQam5bEJjq6Kn4Ry4qn1xHXqQXM4o2f6xDT13sp4U32stpmKh0HOd1WWKr0W` + - `RYnAh4GnToKr21QySZi9QWTea3zqeFmti-Isji1dKZkgZA2S89BdTWSLe6S_9lV0mtdXvDaT8RmaIX72jE_Abhn` + - `bUYV84pNYv-T2LtIKoi5PjWk0raaYoexAjtCWiu3PnizxjYOnNwpzgQN9Qh_rY2jv74cgzG50_Ft1B7XUiakNFx` + - `AiD1k6pNuiu4toY0Es7qt1yeqaC2zcIuuV7HUv1AbFBkIdF5quJHVtZ5AE1MCh1ipLPq-lIjmFdQKSRdbssVw8y` + - `q9FtFVyVqTz9GnQtoctCIPGQqmJDWmt8E7gjFhweUQo-fGgGuTlZRl9fiPQ6luPyGQ1WL6wH79G9eu4UtmgUDNw` + - `q7kpYq0_NQ5vw_1WQSY3LsPclfKzkZ-Lw2RVef-SFVVvUFMcd_3ALeeEnnSe4GSY-7vduPUAE5qMH7M", - "e": "AQAB", - "x5c": ["MIIGlDCCBHygAwIBAgIQEW25APa7S9Sj/Nj6V6GxQTANBgkqhkiG9w0BAQsFADCBwTELMAkGA1UEBhM` + - `CVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxEzARBgNVBAoTCkdvb2dsZS` + - `BMTEMxDjAMBgNVBAsTBUNsb3VkMWAwXgYDVQQDDFdpc3Rpb192MV9jbG91ZF93b3JrbG9hZF9yb290LXNpZ25lc` + - `i0wLTIwMTgtMDQtMjVUMTQ6MTE6MzMtMDc6MDAgSzoxLCAxOkg1MnZnd0VtM3RjOjA6MTgwIBcNMTgwNDI1MjEx` + - `MTMzWhgPMjExODA0MjUyMjExMzNaMIHBMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1U` + - `EBxMNTW91bnRhaW4gVmlldzETMBEGA1UEChMKR29vZ2xlIExMQzEOMAwGA1UECxMFQ2xvdWQxYDBeBgNVBAMMV2` + - `lzdGlvX3YxX2Nsb3VkX3dvcmtsb2FkX3Jvb3Qtc2lnbmVyLTAtMjAxOC0wNC0yNVQxNDoxMTozMy0wNzowMCBLO` + - `jEsIDE6SDUydmd3RW0zdGM6MDoxODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK9dFtiHI0/r70k6` + - `WhbEuDgHDzl/O5MPsymbiYF4cQ4ZDkMgXT2aVHyuB/MmdqteC2spuG5ojC6HCHhj+9JFF3KfU+Ej/j9A5gUz8d4` + - `VYD92LXh82irI2tOelbQ7PZALn5hRnpk0gnpzLe2kpa8lGy0TtB/v/303jZAl0e7iKKkX1Ooy9JzeBAYdNqu9uI` + - `hrl+naSXEGpuWxCY6uip+EcuKp9cR16kFzOKNn+sQ09d7KeFN9rLaZiodBzndVliq9FkWJwIeBp06Cq9tUMkmYv` + - `UFk3mt86nhZrYviLI4tXSmZIGQNkvPQXU1ki3ukv/ZVdJrXV7w2k/EZmiF+9oxPwG4Z21GFfOKTWL/k9i7SCqIu` + - `T41pNK2mmKHsQI7Qlortz54s8Y2DpzcKc4EDfUIf62No7++HIMxudPxbdQe11ImpDRcQIg9ZOqTboruLaGNBLO6` + - `rdcnqmgts3CLrlex1L9QGxQZCHReariR1bWeQBNTAodYqSz6vpSI5hXUCkkXW7LFcPMqvRbRVclak8/Rp0LaHLQ` + - `iDxkKpiQ1prfBO4IxYcHlEKPnxoBrk5WUZfX4j0Opbj8hkNVi+sB+/RvXruFLZoFAzcKu5KWKtPzUOb8P9VkEmN` + - `y7D3JXys5Gfi8NkVXn/khVVb1BTHHf9wC3nhJ50nuBkmPu73bj1ABOajB+zAgMBAAGjgYMwgYAwDgYDVR0PAQH/` + - `BAQDAgEGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQ` + - `/VsuyjgRDAEmcZjyJ77619Js9ijAfBgNVHSMEGDAWgBQ/VsuyjgRDAEmcZjyJ77619Js9ijANBgkqhkiG9w0BAQ` + - `sFAAOCAgEAUc5QJOqxmMJY0E2rcHEWQYRah1vat3wuIHtEZ3SkSumyj+y9eyIHb9XTTyc4SyGyX1n8Rary8oSgQ` + - `V4cbyJTFXEEQOGLHB9/98EKThgJtfPsos2WKe/59S8yN05onpxcaL9y4S295Kv9kcSQxLm5UfjlqsKeHJZymvxi` + - `YzmBox7LA1zqcLYZvslJNkJxKAk5JA66iyDSQqOK7jIixn8pi305dFGCZglUFStwWqY6Rc9rR8EycVhSx2AhrvT` + - `7OQTVdKLfoKA84D8JZJPB7hrxqKf7JJFs87Kjt7c/5bXPFJ2osmjoNYnbHjiq64bh20sSCd630qvhhePLwjjOlB` + - `PiFyK36o/hQN871AEm1SCHy+aQcfJqF5KTgPnZQy5D+D/CGau+BfkO+WCGDVxRleYBJ4g2NbATolygB2KWXrj07` + - `U/WaWqV2hERbkmxXFh6cUdlkX2MeoG4v6ZD2OKAPx5DpJCfp0TEq6PznP+Z1mLd/ZjGsOF8R2WGQJEuU8HRzvsr` + - `0wsX9UyLMqf5XViDK11V/W+dcIvjHCayBpX2se3dfex5jFht+JcQc+iwB8caSXkR6tGSiargEtSJODORacO9IB8` + - `b6W8Sm//JWf/8zyiCcMm1i2yVVphwE1kczFwunAh0JB896VaXGVxXeKEAMQoXHjgDdCYp8/Etxjb8UkCmyjU="] - } - ] -}` -) - -func TestIsEqSpliceStr(t *testing.T) { - testCases := []struct { - certs1 []string - certs2 []string - expSame bool - }{ - { - certs1: []string{"a", "b"}, - certs2: []string{}, - expSame: false, - }, - { - certs1: []string{"a", "b"}, - certs2: []string{"b"}, - expSame: false, - }, - { - certs1: []string{"a", "b"}, - certs2: []string{"a", "b"}, - expSame: true, - }, - } - for _, tc := range testCases { - certSame := isEqSliceStr(tc.certs1, tc.certs2) - if (certSame && !tc.expSame) || (!certSame && tc.expSame) { - t.Errorf("cert compare testcase failed. tc: %v", tc) - } - } -} - -func TestVerifyTrustAnchor(t *testing.T) { - testCases := []struct { - errExp bool - cert string - }{ - { - cert: rootCACert, - errExp: false, - }, - { - cert: malformedCert, - errExp: true, - }, - { - cert: nonCaCert, - errExp: true, - }, - { - cert: intermediateCACert, - errExp: false, - }, - } - for i, tc := range testCases { - err := verifyTrustAnchor(tc.cert) - if tc.errExp && err == nil { - t.Errorf("test case %v failed. Expected Error but got none", i) - } else if !tc.errExp && err != nil { - t.Errorf("test case %v failed. Expected no error but got %v", i, err) - } - } -} - -func TestUpdateTrustAnchor(t *testing.T) { - cbCounter := 0 - tb := NewTrustBundle(nil) - tb.UpdateCb(func() { cbCounter++ }) - - var trustedCerts []string - var err error - - // Add First Cert update - err = tb.UpdateTrustAnchor(&TrustAnchorUpdate{ - TrustAnchorConfig: TrustAnchorConfig{Certs: []string{rootCACert}}, - Source: SourceMeshConfig, - }) - if err != nil { - t.Errorf("Basic trustbundle update test failed. Error: %v", err) - } - trustedCerts = tb.GetTrustBundle() - if !isEqSliceStr(trustedCerts, []string{rootCACert}) || cbCounter != 1 { - t.Errorf("Basic trustbundle update test failed. Callback value is %v", cbCounter) - } - - // Add Second Cert update - // ensure intermediate CA certs accepted, it replaces the first completely, and lib dedupes duplicate cert - err = tb.UpdateTrustAnchor(&TrustAnchorUpdate{ - TrustAnchorConfig: TrustAnchorConfig{Certs: []string{intermediateCACert, intermediateCACert}}, - Source: SourceMeshConfig, - }) - if err != nil { - t.Errorf("trustbundle intermediate cert update test failed. Error: %v", err) - } - trustedCerts = tb.GetTrustBundle() - if !isEqSliceStr(trustedCerts, []string{intermediateCACert}) || cbCounter != 2 { - t.Errorf("trustbundle intermediate cert update test failed. Callback value is %v", cbCounter) - } - - // Try adding one more cert to a different source - // Ensure both certs are not present - err = tb.UpdateTrustAnchor(&TrustAnchorUpdate{ - TrustAnchorConfig: TrustAnchorConfig{Certs: []string{rootCACert}}, - Source: SourceIstioCA, - }) - if err != nil { - t.Errorf("multicert update failed. Error: %v", err) - } - trustedCerts = tb.GetTrustBundle() - result := []string{intermediateCACert, rootCACert} - sort.Strings(result) - if !isEqSliceStr(trustedCerts, result) || cbCounter != 3 { - t.Errorf("multicert update failed. Callback value is %v", cbCounter) - } - - // Try added same cert again. Ensure cb doesn't increment - err = tb.UpdateTrustAnchor(&TrustAnchorUpdate{ - TrustAnchorConfig: TrustAnchorConfig{Certs: []string{rootCACert}}, - Source: SourceIstioCA, - }) - if err != nil { - t.Errorf("duplicate multicert update failed. Error: %v", err) - } - trustedCerts = tb.GetTrustBundle() - if !isEqSliceStr(trustedCerts, result) || cbCounter != 3 { - t.Errorf("duplicate multicert update failed. Callback value is %v", cbCounter) - } - - // Try added one good cert, one bogus Cert - // Verify Update should not go through and no change to cb - err = tb.UpdateTrustAnchor(&TrustAnchorUpdate{ - TrustAnchorConfig: TrustAnchorConfig{Certs: []string{malformedCert}}, - Source: SourceIstioCA, - }) - if err == nil { - t.Errorf("bad cert update failed. Expected error") - } - trustedCerts = tb.GetTrustBundle() - if !isEqSliceStr(trustedCerts, result) || cbCounter != 3 { - t.Errorf("bad cert update failed. Callback value is %v", cbCounter) - } - - // Finally, remove all certs and ensure trustBundle is clean - err = tb.UpdateTrustAnchor(&TrustAnchorUpdate{ - TrustAnchorConfig: TrustAnchorConfig{Certs: []string{}}, - Source: SourceIstioCA, - }) - if err != nil { - t.Errorf("clear cert update failed. Error: %v", err) - } - err = tb.UpdateTrustAnchor(&TrustAnchorUpdate{ - TrustAnchorConfig: TrustAnchorConfig{Certs: []string{}}, - Source: SourceMeshConfig, - }) - if err != nil { - t.Errorf("clear cert update failed. Error: %v", err) - } - trustedCerts = tb.GetTrustBundle() - if !isEqSliceStr(trustedCerts, []string{}) || cbCounter != 5 { - t.Errorf("cert removal update failed. Callback value is %v", cbCounter) - } -} - -func expectTbCount(t *testing.T, tb *TrustBundle, expAnchorCount int, ti time.Duration, strPrefix string) { - t.Helper() - retry.UntilSuccessOrFail(t, func() error { - certs := tb.GetTrustBundle() - if len(certs) != expAnchorCount { - return fmt.Errorf("%s. Got %v, expected %v", strPrefix, len(certs), expAnchorCount) - } - return nil - }, retry.Timeout(ti)) -} - -func TestAddMeshConfigUpdate(t *testing.T) { - caCertPool, err := x509.SystemCertPool() - if err != nil { - t.Fatalf("failed to get SystemCertPool: %v", err) - } - stop := make(chan struct{}) - t.Cleanup(func() { close(stop) }) - - // Mock response from TLS Spiffe Server - validHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(validSpiffeX509Bundle)) - }) - - server1 := httptest.NewTLSServer(validHandler) - caCertPool.AddCert(server1.Certificate()) - defer server1.Close() - - server2 := httptest.NewTLSServer(validHandler) - caCertPool.AddCert(server2.Certificate()) - defer server2.Close() - - tb := NewTrustBundle(caCertPool) - - // Change global remote timeout interval for the duration of the unit test - remoteTimeout = 30 * time.Millisecond - - // Test1: Ensure that MeshConfig PEM certs are updated correctly - tb.AddMeshConfigUpdate(&meshconfig.MeshConfig{CaCertificates: []*meshconfig.MeshConfig_CertificateData{ - {CertificateData: &meshconfig.MeshConfig_CertificateData_Pem{Pem: rootCACert}}, - }}) - expectTbCount(t, tb, 1, 1*time.Second, "meshConfig pem trustAnchor not updated in bundle") - - // Test2: Append server1 as spiffe endpoint to existing MeshConfig - - // Start processing remote anchor update with poll frequency. - go tb.ProcessRemoteTrustAnchors(stop, 200*time.Millisecond) - tb.AddMeshConfigUpdate(&meshconfig.MeshConfig{CaCertificates: []*meshconfig.MeshConfig_CertificateData{ - {CertificateData: &meshconfig.MeshConfig_CertificateData_SpiffeBundleUrl{SpiffeBundleUrl: server1.Listener.Addr().String()}}, - {CertificateData: &meshconfig.MeshConfig_CertificateData_Pem{Pem: rootCACert}}, - }}) - if !isEqSliceStr(tb.endpoints, []string{server1.Listener.Addr().String()}) { - t.Errorf("server1 endpoint not correctly updated in trustbundle. Trustbundle endpoints: %v", tb.endpoints) - } - // Check server1's anchor has been added along with meshConfig pem cert - expectTbCount(t, tb, 2, 3*time.Second, "server1(running) trustAnchor not updated in bundle") - - // Test3: Stop server1 - server1.Close() - // Check server1's valid trustAnchor is no longer in the trustbundle within poll frequency window - expectTbCount(t, tb, 1, 6*time.Second, "server1(stopped) trustAnchor not removed from bundle") - - // Test4: Update with server1, server2 and mesh pem ca - tb.AddMeshConfigUpdate(&meshconfig.MeshConfig{CaCertificates: []*meshconfig.MeshConfig_CertificateData{ - {CertificateData: &meshconfig.MeshConfig_CertificateData_SpiffeBundleUrl{SpiffeBundleUrl: server2.Listener.Addr().String()}}, - {CertificateData: &meshconfig.MeshConfig_CertificateData_SpiffeBundleUrl{SpiffeBundleUrl: server1.Listener.Addr().String()}}, - {CertificateData: &meshconfig.MeshConfig_CertificateData_Pem{Pem: rootCACert}}, - }}) - if !isEqSliceStr(tb.endpoints, []string{server2.Listener.Addr().String(), server1.Listener.Addr().String()}) { - t.Errorf("server2 endpoint not correctly updated in trustbundle. Trustbundle endpoints: %v", tb.endpoints) - } - // Check only server 2's trustanchor is present along with meshConfig pem and not server 1 (since it is down) - expectTbCount(t, tb, 2, 3*time.Second, "server2(running) trustAnchor not updated in bundle") - - // Test5. remove everything - tb.AddMeshConfigUpdate(&meshconfig.MeshConfig{CaCertificates: []*meshconfig.MeshConfig_CertificateData{}}) - expectTbCount(t, tb, 0, 3*time.Second, "trustAnchor not updated in bundle after meshConfig cleared") -} diff --git a/pilot/pkg/util/constant/constant.go b/pilot/pkg/util/constant/constant.go deleted file mode 100644 index 1f6f1f25f..000000000 --- a/pilot/pkg/util/constant/constant.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package constant - -const ( - // HeaderJWTClaim is the special header name used in virtual service for routing based on JWT claims. - HeaderJWTClaim = "@request.auth.claims." -) diff --git a/pilot/pkg/util/informermetric/informerutil.go b/pilot/pkg/util/informermetric/informerutil.go deleted file mode 100644 index 69553b4e9..000000000 --- a/pilot/pkg/util/informermetric/informerutil.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package informermetric - -import ( - "sync" -) - -import ( - "istio.io/pkg/log" - "istio.io/pkg/monitoring" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/cluster" -) - -var ( - clusterLabel = monitoring.MustCreateLabel("cluster") - - errorMetric = monitoring.NewSum( - "controller_sync_errors_total", - "Total number of errorMetric syncing controllers.", - ) - - mu sync.RWMutex - handlers = map[cluster.ID]cache.WatchErrorHandler{} -) - -func init() { - monitoring.MustRegister(errorMetric) -} - -// ErrorHandlerForCluster fetches or creates an ErrorHandler that emits a metric -// and logs when a watch error occurs. For use with SetWatchErrorHandler on SharedInformer. -func ErrorHandlerForCluster(clusterID cluster.ID) cache.WatchErrorHandler { - mu.RLock() - handler, ok := handlers[clusterID] - mu.RUnlock() - if ok { - return handler - } - - mu.Lock() - defer mu.Unlock() - clusterMetric := errorMetric.With(clusterLabel.Value(clusterID.String())) - h := func(_ *cache.Reflector, err error) { - clusterMetric.Increment() - log.Errorf("watch error in cluster %s: %v", clusterID, err) - } - handlers[clusterID] = h - return h -} diff --git a/pilot/pkg/util/network/ip.go b/pilot/pkg/util/network/ip.go deleted file mode 100644 index d6700f890..000000000 --- a/pilot/pkg/util/network/ip.go +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package network - -import ( - "context" - "fmt" - "net" - "time" -) - -import ( - "istio.io/pkg/log" -) - -// Network-related utility functions -const ( - waitInterval = 100 * time.Millisecond - waitTimeout = 2 * time.Minute -) - -type lookupIPAddrType = func(ctx context.Context, addr string) ([]net.IPAddr, error) - -// ErrResolveNoAddress error occurs when IP address resolution is attempted, -// but no address was provided. -var ErrResolveNoAddress = fmt.Errorf("no address specified") - -// GetPrivateIPs blocks until private IP addresses are available, or a timeout is reached. -func GetPrivateIPs(ctx context.Context) ([]string, bool) { - if _, ok := ctx.Deadline(); !ok { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, waitTimeout) - defer cancel() - } - - for { - select { - case <-ctx.Done(): - return getPrivateIPsIfAvailable() - default: - addr, ok := getPrivateIPsIfAvailable() - if ok { - return addr, true - } - time.Sleep(waitInterval) - } - } -} - -// Returns all the private IP addresses -func getPrivateIPsIfAvailable() ([]string, bool) { - ok := true - ipAddresses := make([]string, 0) - - ifaces, _ := net.Interfaces() - - for _, iface := range ifaces { - if iface.Flags&net.FlagUp == 0 { - continue // interface down - } - if iface.Flags&net.FlagLoopback != 0 { - continue // loopback interface - } - addrs, _ := iface.Addrs() - - for _, addr := range addrs { - var ip net.IP - switch v := addr.(type) { - case *net.IPNet: - ip = v.IP - case *net.IPAddr: - ip = v.IP - } - if ip == nil || ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() { - continue - } - if ip.IsUnspecified() { - ok = false - continue - } - ipAddresses = append(ipAddresses, ip.String()) - } - } - return ipAddresses, ok -} - -// ResolveAddr resolves an authority address to an IP address. Incoming -// addr can be an IP address or hostname. If addr is an IPv6 address, the IP -// part must be enclosed in square brackets. -// -// LookupIPAddr() may return multiple IP addresses, of which this function returns -// the first IPv4 entry. To use this function in an IPv6 only environment, either -// provide an IPv6 address or ensure the hostname resolves to only IPv6 addresses. -func ResolveAddr(addr string, lookupIPAddr ...lookupIPAddrType) (string, error) { - if addr == "" { - return "", ErrResolveNoAddress - } - host, port, err := net.SplitHostPort(addr) - if err != nil { - return "", err - } - log.Infof("Attempting to lookup address: %s", host) - defer log.Infof("Finished lookup of address: %s", host) - // lookup the udp address with a timeout of 15 seconds. - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() - var addrs []net.IPAddr - var lookupErr error - if (len(lookupIPAddr) > 0) && (lookupIPAddr[0] != nil) { - // if there are more than one lookup function, ignore all but first - addrs, lookupErr = lookupIPAddr[0](ctx, host) - } else { - addrs, lookupErr = net.DefaultResolver.LookupIPAddr(ctx, host) - } - - if lookupErr != nil || len(addrs) == 0 { - return "", fmt.Errorf("lookup failed for IP address: %w", lookupErr) - } - var resolvedAddr string - - for _, address := range addrs { - ip := address.IP - if ip.To4() == nil { - resolvedAddr = fmt.Sprintf("[%s]:%s", ip, port) - } else { - resolvedAddr = fmt.Sprintf("%s:%s", ip, port) - break - } - } - log.Infof("Addr resolved to: %s", resolvedAddr) - return resolvedAddr, nil -} - -// AllIPv6 checks the addresses slice and returns true if all addresses -// are valid IPv6 address, for all other cases it returns false. -func AllIPv6(ipAddrs []string) bool { - for i := 0; i < len(ipAddrs); i++ { - addr := net.ParseIP(ipAddrs[i]) - if addr == nil { - // Should not happen, invalid IP in proxy's IPAddresses slice should have been caught earlier, - // skip it to prevent a panic. - continue - } - if addr.To4() != nil { - return false - } - } - return true -} - -// AllIPv4 checks the addresses slice and returns true if all addresses -// are valid IPv4 address, for all other cases it returns false. -func AllIPv4(ipAddrs []string) bool { - for i := 0; i < len(ipAddrs); i++ { - addr := net.ParseIP(ipAddrs[i]) - if addr == nil { - // Should not happen, invalid IP in proxy's IPAddresses slice should have been caught earlier, - // skip it to prevent a panic. - continue - } - if addr.To4() == nil && addr.To16() != nil { - return false - } - } - return true -} - -// GlobalUnicastIP returns the first global unicast address in the passed in addresses. -func GlobalUnicastIP(ipAddrs []string) string { - for i := 0; i < len(ipAddrs); i++ { - addr := net.ParseIP(ipAddrs[i]) - if addr == nil { - // Should not happen, invalid IP in proxy's IPAddresses slice should have been caught earlier, - // skip it to prevent a panic. - continue - } - if addr.IsGlobalUnicast() { - return addr.String() - } - } - return "" -} diff --git a/pilot/pkg/util/network/ip_test.go b/pilot/pkg/util/network/ip_test.go deleted file mode 100644 index f1b7359a0..000000000 --- a/pilot/pkg/util/network/ip_test.go +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package network - -import ( - "context" - "fmt" - "net" - "strings" - "testing" -) - -// The test may run on a system with localhost = 127.0.0.1 or ::1, so we -// determine that value and use it in the "expected" results for the test -// cases in TestResolveAddr(). Need to wrap IPv6 addresses in square -// brackets. -func determineLocalHostIPString(t *testing.T) string { - ips, err := net.LookupIP("localhost") - if err != nil || len(ips) == 0 { - t.Fatalf("Test setup failure - unable to determine IP of localhost: %v", err) - } - var ret string - for _, ip := range ips { - if ip.To4() == nil { - ret = fmt.Sprintf("[%s]", ip.String()) - } else { - return ip.String() - } - } - return ret -} - -func MockLookupIPAddr(_ context.Context, _ string) ([]net.IPAddr, error) { - ret := []net.IPAddr{ - {IP: net.ParseIP("2001:db8::68")}, - {IP: net.IPv4(1, 2, 3, 4)}, - {IP: net.IPv4(1, 2, 3, 5)}, - } - return ret, nil -} - -func MockLookupIPAddrIPv6(_ context.Context, _ string) ([]net.IPAddr, error) { - ret := []net.IPAddr{ - {IP: net.ParseIP("2001:db8::68")}, - } - return ret, nil -} - -func TestResolveAddr(t *testing.T) { - localIP := determineLocalHostIPString(t) - - testCases := []struct { - name string - input string - expected string - errStr string - lookup func(ctx context.Context, addr string) ([]net.IPAddr, error) - }{ - { - name: "Host by name", - input: "localhost:9080", - expected: fmt.Sprintf("%s:9080", localIP), - errStr: "", - lookup: nil, - }, - { - name: "Host by name w/brackets", - input: "[localhost]:9080", - expected: fmt.Sprintf("%s:9080", localIP), - errStr: "", - lookup: nil, - }, - { - name: "Host by IPv4", - input: "127.0.0.1:9080", - expected: "127.0.0.1:9080", - errStr: "", - lookup: nil, - }, - { - name: "Host by IPv6", - input: "[::1]:9080", - expected: "[::1]:9080", - errStr: "", - lookup: nil, - }, - { - name: "Bad IPv4", - input: "127.0.0.1.1:9080", - expected: "", - errStr: "lookup failed for IP address: lookup 127.0.0.1.1: no such host", - lookup: nil, - }, - { - name: "Bad IPv6", - input: "[2001:db8::bad::1]:9080", - expected: "", - errStr: "lookup failed for IP address: lookup 2001:db8::bad::1: no such host", - lookup: nil, - }, - { - name: "Empty host", - input: "", - expected: "", - errStr: ErrResolveNoAddress.Error(), - lookup: nil, - }, - { - name: "IPv6 missing brackets", - input: "2001:db8::20:9080", - expected: "", - errStr: "address 2001:db8::20:9080: too many colons in address", - lookup: nil, - }, - { - name: "Colon, but no port", - input: "localhost:", - expected: fmt.Sprintf("%s:", localIP), - errStr: "", - lookup: nil, - }, - { - name: "Missing port", - input: "localhost", - expected: "", - errStr: "address localhost: missing port in address", - lookup: nil, - }, - { - name: "Missing host", - input: ":9080", - expected: "", - errStr: "lookup failed for IP address: lookup : no such host", - lookup: nil, - }, - { - name: "Host by name - non local", - input: "www.foo.com:9080", - expected: "1.2.3.4:9080", - errStr: "", - lookup: MockLookupIPAddr, - }, - { - name: "Host by name - non local 0 IPv6 only address", - input: "www.foo.com:9080", - expected: "[2001:db8::68]:9080", - errStr: "", - lookup: MockLookupIPAddrIPv6, - }, - } - - for _, tc := range testCases { - actual, err := ResolveAddr(tc.input, tc.lookup) - if err != nil { - if tc.errStr == "" { - t.Errorf("[%s] expected success, but saw error: %v", tc.name, err) - } else if err.Error() != tc.errStr { - if strings.Contains(err.Error(), "Temporary failure in name resolution") { - t.Logf("[%s] expected error %q, got %q", tc.name, tc.errStr, err.Error()) - continue - } - t.Errorf("[%s] expected error %q, got %q", tc.name, tc.errStr, err.Error()) - } - } else { - if tc.errStr != "" { - t.Errorf("[%s] no error seen, but expected failure: %s", tc.name, tc.errStr) - } else if actual != tc.expected { - t.Errorf("[%s] expected address %q, got %q", tc.name, tc.expected, actual) - } - } - } -} - -func TestAllIPv6(t *testing.T) { - tests := []struct { - name string - addrs []string - expected bool - }{ - { - name: "ipv4 only", - addrs: []string{"1.1.1.1", "127.0.0.1", "2.2.2.2"}, - expected: false, - }, - { - name: "ipv6 only", - addrs: []string{"1111:2222::1", "::1", "2222:3333::1"}, - expected: true, - }, - { - name: "mixed ipv4 and ipv6", - addrs: []string{"1111:2222::1", "::1", "127.0.0.1", "2.2.2.2", "2222:3333::1"}, - expected: false, - }, - } - for _, tt := range tests { - result := AllIPv6(tt.addrs) - if result != tt.expected { - t.Errorf("Test %s failed, expected: %t got: %t", tt.name, tt.expected, result) - } - } -} - -func TestAllIPv4(t *testing.T) { - tests := []struct { - name string - addrs []string - expected bool - }{ - { - name: "ipv4 only", - addrs: []string{"1.1.1.1", "127.0.0.1", "2.2.2.2"}, - expected: true, - }, - { - name: "ipv6 only", - addrs: []string{"1111:2222::1", "::1", "2222:3333::1"}, - expected: false, - }, - { - name: "mixed ipv4 and ipv6", - addrs: []string{"1111:2222::1", "::1", "127.0.0.1", "2.2.2.2", "2222:3333::1"}, - expected: false, - }, - } - for _, tt := range tests { - result := AllIPv4(tt.addrs) - if result != tt.expected { - t.Errorf("Test %s failed, expected: %t got: %t", tt.name, tt.expected, result) - } - } -} diff --git a/pilot/pkg/util/runtime/leak_test.go b/pilot/pkg/util/runtime/leak_test.go deleted file mode 100644 index a24aec1a6..000000000 --- a/pilot/pkg/util/runtime/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package runtime - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/util/runtime/runtime.go b/pilot/pkg/util/runtime/runtime.go deleted file mode 100644 index a1bf80931..000000000 --- a/pilot/pkg/util/runtime/runtime.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package runtime - -import ( - "runtime" -) - -import ( - "istio.io/pkg/log" -) - -// LogPanic logs the caller tree when a panic occurs. -func LogPanic(r interface{}) { - // Same as stdlib http server code. Manually allocate stack trace buffer size - // to prevent excessively large logs - const size = 64 << 10 - stacktrace := make([]byte, size) - stacktrace = stacktrace[:runtime.Stack(stacktrace, false)] - log.Errorf("Observed a panic: %#v (%v)\n%s", r, r, stacktrace) -} - -// HandleCrash catches the crash and calls additional handlers. -func HandleCrash(handlers ...func(interface{})) { - if r := recover(); r != nil { - for _, handler := range handlers { - handler(r) - } - } -} diff --git a/pilot/pkg/util/runtime/runtime_test.go b/pilot/pkg/util/runtime/runtime_test.go deleted file mode 100644 index d3bf1a0ed..000000000 --- a/pilot/pkg/util/runtime/runtime_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package runtime - -import ( - "testing" - "time" -) - -func TestHandleCrash(t *testing.T) { - defer func() { - if x := recover(); x != nil { - t.Errorf("Expected no panic ") - } - }() - - defer HandleCrash() - panic("test") -} - -func TestCustomHandleCrash(t *testing.T) { - ch := make(chan struct{}, 1) - defer func() { - select { - case <-ch: - t.Logf("crash handler called") - case <-time.After(1 * time.Second): - t.Errorf("Custom handler not called") - } - }() - - defer HandleCrash(func(interface{}) { - ch <- struct{}{} - }) - - panic("test") -} diff --git a/pilot/pkg/util/slices/string.go b/pilot/pkg/util/slices/string.go deleted file mode 100644 index 5bde4e6c0..000000000 --- a/pilot/pkg/util/slices/string.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package slices - -// ContainsString returns true if a given slice of strings -// contains given value. -func ContainsString(values []string, match string) bool { - for _, value := range values { - if value == match { - return true - } - } - return false -} diff --git a/pilot/pkg/xds/README.md b/pilot/pkg/xds/README.md deleted file mode 100644 index 2b05cdff2..000000000 --- a/pilot/pkg/xds/README.md +++ /dev/null @@ -1,141 +0,0 @@ -# Debug interface - -The debug handlers are configured on the monitoring port (default 15014) as well -as on the http port (8080). - -```bash -PILOT=istiod.dubbo-system:15014 - -# What is sent to envoy -# Listeners and routes -curl $PILOT/debug/adsz - -# Endpoints -curl $PILOT/debug/edsz - -# Clusters -curl $PILOT/debug/cdsz -``` - -Each handler takes an extra parameter, "debug=0|1" which flips the verbosity of the -messages for that component (similar with envoy). - -An extra parameter "push=1" triggers a config push to all connected endpoints. - -Handlers should list, in json format: - -- one entry for each connected envoy -- the timestamp of the connection - -In addition, Pilot debug interface can show pilot's internal view of the config: - -```bash -# General metrics -curl $PILOT/metrics - -# All services/external services from all registries -curl $PILOT/debug/registryz - -# All endpoints -curl $PILOT/debug/endpointz[?brief=1] - -# All configs. -curl $PILOT/debug/configz - -``` - -Example for EDS: - -```json -{ - - // Cluster - "echosrv.dubbo-system.svc.cluster.local|grpc-ping": { - "EdsClients": { - // One for each connected envoy. - "sidecar~172.17.0.8~echosrv-deployment-5b7878cc9-dlm8j.dubbo-system~dubbo-system.svc.cluster.local-116": { - // Should match the info in the node (this is the real remote address) - "PeerAddr": "172.17.0.8:42044", - "Clusters": [ - // Should match the cluster, this is what is monitored - "echosrv.dubbo-system.svc.cluster.local|grpc-ping" - ], - // Time the sidecar connected to pilot - "Connect": "2018-03-22T15:01:07.527304202Z" - }, - "sidecar~172.17.0.9~echosrv-deployment-5b7878cc9-wb9b7.dubbo-system~dubbo-system.svc.cluster.local-75": { - "PeerAddr": "172.17.0.9:47182", - "Clusters": [ - "echosrv.dubbo-system.svc.cluster.local|grpc-ping" - ], - "Connect": "2018-03-22T15:01:00.465066249Z" - } - }, - // The info pushed to each connected sidecar watching the cluster. - "LoadAssignment": { - "cluster_name": "echosrv.dubbo-system.svc.cluster.local|grpc-ping", - "endpoints": [ - { - "locality": {}, - "lb_endpoints": [ - { - // Should match the endpoint and port from 'kubectl get ep' - "endpoint": { - "address": { - "Address": { - "SocketAddress": { - "address": "172.17.0.8", - "PortSpecifier": { - "PortValue": 8079 - }, - "ipv4_compat": true - } - } - } - } - }, - ] - } - ] - } - } -``` - -## Log messages - -Verbose messages for v2 is controlled by env variables PILOT_DEBUG_{EDS,CDS,LDS}. -Setting it to "0" disables debug, setting it to "1" enables - debug is currently -enabled by default, since it is not very verbose. - -Messages are prefixed with EDS/LDS/CDS. - -What we log and how to use it: - -- sidecar connecting to pilot: "EDS/CDS/LDS: REQ ...". This includes the node, IP and the discovery -request proto. Should show up when the sidecar starts up. - -- sidecar disconnecting from pilot: xDS: close. This happens when a pod is stopped. - -- push events - whenever we push a config to the sidecar. - -- "XDS: Registry event..." - indicates a registry event, should be followed by PUSH messages for -each endpoint. - -- "EDS: no instances": pay close attention to this event, it indicates that Envoy asked for -a cluster but pilot doesn't have any valid instance. At some point after, when the instance eventually -shows up you should see an EDS PUSH message. - -In addition, the registry has slightly more verbose messages about the events, so it is -possible to map an event in the registry to config pushes. - -## Example requests and responses - -EDS: - -```plain -node: -resource_names:"echosrv.dubbo-system.svc.cluster.local|http-echo" -type_url:"type.googleapis.com/envoy.api.v2.ClusterLoadAssignment" -``` diff --git a/pilot/pkg/xds/ads.go b/pilot/pkg/xds/ads.go deleted file mode 100644 index 5527feb4f..000000000 --- a/pilot/pkg/xds/ads.go +++ /dev/null @@ -1,980 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "strconv" - "strings" - "sync/atomic" - "time" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - uatomic "go.uber.org/atomic" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/peer" - "google.golang.org/grpc/status" - "istio.io/pkg/env" - istiolog "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/controller/workloadentry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - istiogrpc "github.com/apache/dubbo-go-pixiu/pilot/pkg/grpc" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - labelutil "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/util/label" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -var ( - log = istiolog.RegisterScope("ads", "ads debugging", 0) - - // Tracks connections, increment on each new connection. - connectionNumber = int64(0) -) - -// Used only when running in KNative, to handle the load balancing behavior. -var firstRequest = uatomic.NewBool(true) - -var knativeEnv = env.RegisterStringVar("K_REVISION", "", - "KNative revision, set if running in knative").Get() - -// DiscoveryStream is a server interface for XDS. -type DiscoveryStream = discovery.AggregatedDiscoveryService_StreamAggregatedResourcesServer - -// DeltaDiscoveryStream is a server interface for Delta XDS. -type DeltaDiscoveryStream = discovery.AggregatedDiscoveryService_DeltaAggregatedResourcesServer - -// DiscoveryClient is a client interface for XDS. -type DiscoveryClient = discovery.AggregatedDiscoveryService_StreamAggregatedResourcesClient - -// DeltaDiscoveryClient is a client interface for Delta XDS. -type DeltaDiscoveryClient = discovery.AggregatedDiscoveryService_DeltaAggregatedResourcesClient - -// Connection holds information about connected client. -type Connection struct { - // peerAddr is the address of the client, from network layer. - peerAddr string - - // Time of connection, for debugging - connectedAt time.Time - - // conID is the connection conID, used as a key in the connection table. - // Currently based on the node name and a counter. - conID string - - // proxy is the client to which this connection is established. - proxy *model.Proxy - - // Sending on this channel results in a push. - pushChannel chan *Event - - // Both ADS and SDS streams implement this interface - stream DiscoveryStream - // deltaStream is used for Delta XDS. Only one of deltaStream or stream will be set - deltaStream DeltaDiscoveryStream - - // Original node metadata, to avoid unmarshal/marshal. - // This is included in internal events. - node *core.Node - - // initialized channel will be closed when proxy is initialized. Pushes, or anything accessing - // the proxy, should not be started until this channel is closed. - initialized chan struct{} - - // stop can be used to end the connection manually via debug endpoints. Only to be used for testing. - stop chan struct{} - - // reqChan is used to receive discovery requests for this connection. - reqChan chan *discovery.DiscoveryRequest - deltaReqChan chan *discovery.DeltaDiscoveryRequest - - // errorChan is used to process error during discovery request processing. - errorChan chan error -} - -// Event represents a config or registry event that results in a push. -type Event struct { - // pushRequest PushRequest to use for the push. - pushRequest *model.PushRequest - - // function to call once a push is finished. This must be called or future changes may be blocked. - done func() -} - -func newConnection(peerAddr string, stream DiscoveryStream) *Connection { - return &Connection{ - pushChannel: make(chan *Event), - initialized: make(chan struct{}), - stop: make(chan struct{}), - reqChan: make(chan *discovery.DiscoveryRequest, 1), - errorChan: make(chan error, 1), - peerAddr: peerAddr, - connectedAt: time.Now(), - stream: stream, - } -} - -func (s *DiscoveryServer) receive(con *Connection, identities []string) { - defer func() { - close(con.errorChan) - close(con.reqChan) - // Close the initialized channel, if its not already closed, to prevent blocking the stream. - select { - case <-con.initialized: - default: - close(con.initialized) - } - }() - - firstRequest := true - for { - req, err := con.stream.Recv() - if err != nil { - if istiogrpc.IsExpectedGRPCError(err) { - log.Infof("ADS: %q %s terminated", con.peerAddr, con.conID) - return - } - con.errorChan <- err - log.Errorf("ADS: %q %s terminated with error: %v", con.peerAddr, con.conID, err) - totalXDSInternalErrors.Increment() - return - } - // This should be only set for the first request. The node id may not be set - for example malicious clients. - if firstRequest { - // probe happens before envoy sends first xDS request - if req.TypeUrl == v3.HealthInfoType { - log.Warnf("ADS: %q %s send health check probe before normal xDS request", con.peerAddr, con.conID) - continue - } - firstRequest = false - if req.Node == nil || req.Node.Id == "" { - con.errorChan <- status.New(codes.InvalidArgument, "missing node information").Err() - return - } - if err := s.initConnection(req.Node, con, identities); err != nil { - con.errorChan <- err - return - } - defer s.closeConnection(con) - log.Infof("ADS: new connection for node:%s", con.conID) - } - - select { - case con.reqChan <- req: - case <-con.stream.Context().Done(): - log.Infof("ADS: %q %s terminated with stream closed", con.peerAddr, con.conID) - return - } - } -} - -// processRequest handles one discovery request. This is currently called from the 'main' thread, which also -// handles 'push' requests and close - the code will eventually call the 'push' code, and it needs more mutex -// protection. Original code avoided the mutexes by doing both 'push' and 'process requests' in same thread. -func (s *DiscoveryServer) processRequest(req *discovery.DiscoveryRequest, con *Connection) error { - if req.TypeUrl == v3.HealthInfoType { - s.handleWorkloadHealthcheck(con.proxy, req) - return nil - } - - // For now, don't let xDS piggyback debug requests start watchers. - if strings.HasPrefix(req.TypeUrl, v3.DebugType) { - return s.pushXds(con, - &model.WatchedResource{TypeUrl: req.TypeUrl, ResourceNames: req.ResourceNames}, - &model.PushRequest{Full: true, Push: con.proxy.LastPushContext}) - } - if s.StatusReporter != nil { - s.StatusReporter.RegisterEvent(con.conID, req.TypeUrl, req.ResponseNonce) - } - shouldRespond, delta := s.shouldRespond(con, req) - if !shouldRespond { - return nil - } - - request := &model.PushRequest{ - Full: true, - Push: con.proxy.LastPushContext, - Reason: []model.TriggerReason{model.ProxyRequest}, - - // The usage of LastPushTime (rather than time.Now()), is critical here for correctness; This time - // is used by the XDS cache to determine if a entry is stale. If we use Now() with an old push context, - // we may end up overriding active cache entries with stale ones. - Start: con.proxy.LastPushTime, - Delta: delta, - } - - // SidecarScope for the proxy may not have been updated based on this pushContext. - // It can happen when `processRequest` comes after push context has been updated(s.initPushContext), - // but proxy's SidecarScope has been updated(s.updateProxy) due to optimizations that skip sidecar scope - // computation. - if con.proxy.SidecarScope != nil && con.proxy.SidecarScope.Version != request.Push.PushVersion { - s.computeProxyState(con.proxy, request) - } - return s.pushXds(con, con.Watched(req.TypeUrl), request) -} - -// StreamAggregatedResources implements the ADS interface. -func (s *DiscoveryServer) StreamAggregatedResources(stream discovery.AggregatedDiscoveryService_StreamAggregatedResourcesServer) error { - return s.Stream(stream) -} - -func (s *DiscoveryServer) Stream(stream DiscoveryStream) error { - if knativeEnv != "" && firstRequest.Load() { - // How scaling works in knative is the first request is the "loading" request. During - // loading request, concurrency=1. Once that request is done, concurrency is enabled. - // However, the XDS stream is long lived, so the first request would block all others. As a - // result, we should exit the first request immediately; clients will retry. - firstRequest.Store(false) - return status.Error(codes.Unavailable, "server warmup not complete; try again") - } - // Check if server is ready to accept clients and process new requests. - // Currently ready means caches have been synced and hence can build - // clusters correctly. Without this check, InitContext() call below would - // initialize with empty config, leading to reconnected Envoys loosing - // configuration. This is an additional safety check inaddition to adding - // cachesSynced logic to readiness probe to handle cases where kube-proxy - // ip tables update latencies. - // See https://github.com/istio/istio/issues/25495. - if !s.IsServerReady() { - return status.Error(codes.Unavailable, "server is not ready to serve discovery information") - } - - ctx := stream.Context() - peerAddr := "0.0.0.0" - if peerInfo, ok := peer.FromContext(ctx); ok { - peerAddr = peerInfo.Addr.String() - } - - if err := s.WaitForRequestLimit(stream.Context()); err != nil { - log.Warnf("ADS: %q exceeded rate limit: %v", peerAddr, err) - return status.Errorf(codes.ResourceExhausted, "request rate limit exceeded: %v", err) - } - - ids, err := s.authenticate(ctx) - if err != nil { - return status.Error(codes.Unauthenticated, err.Error()) - } - if ids != nil { - log.Debugf("Authenticated XDS: %v with identity %v", peerAddr, ids) - } else { - log.Debugf("Unauthenticated XDS: %s", peerAddr) - } - - // InitContext returns immediately if the context was already initialized. - if err = s.globalPushContext().InitContext(s.Env, nil, nil); err != nil { - // Error accessing the data - log and close, maybe a different pilot replica - // has more luck - log.Warnf("Error reading config %v", err) - return status.Error(codes.Unavailable, "error reading config") - } - con := newConnection(peerAddr, stream) - - // Do not call: defer close(con.pushChannel). The push channel will be garbage collected - // when the connection is no longer used. Closing the channel can cause subtle race conditions - // with push. According to the spec: "It's only necessary to close a channel when it is important - // to tell the receiving goroutines that all data have been sent." - - // Block until either a request is received or a push is triggered. - // We need 2 go routines because 'read' blocks in Recv(). - go s.receive(con, ids) - - // Wait for the proxy to be fully initialized before we start serving traffic. Because - // initialization doesn't have dependencies that will block, there is no need to add any timeout - // here. Prior to this explicit wait, we were implicitly waiting by receive() not sending to - // reqChannel and the connection not being enqueued for pushes to pushChannel until the - // initialization is complete. - <-con.initialized - - for { - select { - case req, ok := <-con.reqChan: - if ok { - if err := s.processRequest(req, con); err != nil { - return err - } - } else { - // Remote side closed connection or error processing the request. - return <-con.errorChan - } - case pushEv := <-con.pushChannel: - err := s.pushConnection(con, pushEv) - pushEv.done() - if err != nil { - return err - } - case <-con.stop: - return nil - } - } -} - -var emptyResourceDelta = model.ResourceDelta{} - -// shouldRespond determines whether this request needs to be responded back. It applies the ack/nack rules as per xds protocol -// using WatchedResource for previous state and discovery request for the current state. -func (s *DiscoveryServer) shouldRespond(con *Connection, request *discovery.DiscoveryRequest) (bool, model.ResourceDelta) { - stype := v3.GetShortType(request.TypeUrl) - - // If there is an error in request that means previous response is erroneous. - // We do not have to respond in that case. In this case request's version info - // will be different from the version sent. But it is fragile to rely on that. - if request.ErrorDetail != nil { - errCode := codes.Code(request.ErrorDetail.Code) - log.Warnf("ADS:%s: ACK ERROR %s %s:%s", stype, con.conID, errCode.String(), request.ErrorDetail.GetMessage()) - incrementXDSRejects(request.TypeUrl, con.proxy.ID, errCode.String()) - if s.StatusGen != nil { - s.StatusGen.OnNack(con.proxy, request) - } - con.proxy.Lock() - if w, f := con.proxy.WatchedResources[request.TypeUrl]; f { - w.NonceNacked = request.ResponseNonce - } - con.proxy.Unlock() - return false, emptyResourceDelta - } - - if shouldUnsubscribe(request) { - log.Debugf("ADS:%s: UNSUBSCRIBE %s %s %s", stype, con.conID, request.VersionInfo, request.ResponseNonce) - con.proxy.Lock() - delete(con.proxy.WatchedResources, request.TypeUrl) - con.proxy.Unlock() - return false, emptyResourceDelta - } - - con.proxy.RLock() - previousInfo := con.proxy.WatchedResources[request.TypeUrl] - con.proxy.RUnlock() - - // This can happen in two cases: - // 1. Envoy initially send request to Istiod - // 2. Envoy reconnect to Istiod i.e. Istiod does not have - // information about this typeUrl, but Envoy sends response nonce - either - // because Istiod is restarted or Envoy disconnects and reconnects. - // We should always respond with the current resource names. - if request.ResponseNonce == "" || previousInfo == nil { - log.Debugf("ADS:%s: INIT/RECONNECT %s %s %s", stype, con.conID, request.VersionInfo, request.ResponseNonce) - con.proxy.Lock() - con.proxy.WatchedResources[request.TypeUrl] = &model.WatchedResource{TypeUrl: request.TypeUrl, ResourceNames: request.ResourceNames} - con.proxy.Unlock() - return true, emptyResourceDelta - } - - // If there is mismatch in the nonce, that is a case of expired/stale nonce. - // A nonce becomes stale following a newer nonce being sent to Envoy. - if request.ResponseNonce != previousInfo.NonceSent { - log.Debugf("ADS:%s: REQ %s Expired nonce received %s, sent %s", stype, - con.conID, request.ResponseNonce, previousInfo.NonceSent) - xdsExpiredNonce.With(typeTag.Value(v3.GetMetricType(request.TypeUrl))).Increment() - con.proxy.Lock() - con.proxy.WatchedResources[request.TypeUrl].NonceNacked = "" - con.proxy.Unlock() - return false, emptyResourceDelta - } - - // If it comes here, that means nonce match. This an ACK. We should record - // the ack details and respond if there is a change in resource names. - con.proxy.Lock() - previousResources := con.proxy.WatchedResources[request.TypeUrl].ResourceNames - con.proxy.WatchedResources[request.TypeUrl].NonceAcked = request.ResponseNonce - con.proxy.WatchedResources[request.TypeUrl].NonceNacked = "" - con.proxy.WatchedResources[request.TypeUrl].ResourceNames = request.ResourceNames - con.proxy.Unlock() - - prev := sets.New(previousResources...) - cur := sets.New(request.ResourceNames...) - removed := prev.Difference(cur) - added := cur.Difference(prev) - // Envoy can send two DiscoveryRequests with same version and nonce - // when it detects a new resource. We should respond if they change. - if len(removed) == 0 && len(added) == 0 { - log.Debugf("ADS:%s: ACK %s %s %s", stype, con.conID, request.VersionInfo, request.ResponseNonce) - return false, emptyResourceDelta - } - log.Debugf("ADS:%s: RESOURCE CHANGE added %v removed %v %s %s %s", stype, - added, removed, con.conID, request.VersionInfo, request.ResponseNonce) - - return true, model.ResourceDelta{ - Subscribed: added, - Unsubscribed: removed, - } -} - -// shouldUnsubscribe checks if we should unsubscribe. This is done when Envoy is -// no longer watching. For example, we remove all RDS references, we will -// unsubscribe from RDS. NOTE: This may happen as part of the initial request. If -// there are no routes needed, Envoy will send an empty request, which this -// properly handles by not adding it to the watched resource list. -func shouldUnsubscribe(request *discovery.DiscoveryRequest) bool { - return len(request.ResourceNames) == 0 && !isWildcardTypeURL(request.TypeUrl) -} - -// isWildcardTypeURL checks whether a given type is a wildcard type -// https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol#how-the-client-specifies-what-resources-to-return -// If the list of resource names becomes empty, that means that the client is no -// longer interested in any resources of the specified type. For Listener and -// Cluster resource types, there is also a “wildcard†mode, which is triggered -// when the initial request on the stream for that resource type contains no -// resource names. -func isWildcardTypeURL(typeURL string) bool { - switch typeURL { - case v3.SecretType, v3.EndpointType, v3.RouteType, v3.ExtensionConfigurationType, v3.DubboServiceNameMappingType: - // By XDS spec, these are not wildcard - return false - case v3.ClusterType, v3.ListenerType: - // By XDS spec, these are wildcard - return true - default: - // All of our internal types use wildcard semantics - return true - } -} - -// listEqualUnordered checks that two lists contain all the same elements -func listEqualUnordered(a []string, b []string) bool { - if len(a) != len(b) { - return false - } - first := make(map[string]struct{}, len(a)) - for _, c := range a { - first[c] = struct{}{} - } - for _, c := range b { - _, f := first[c] - if !f { - return false - } - } - return true -} - -// update the node associated with the connection, after receiving a packet from envoy, also adds the connection -// to the tracking map. -func (s *DiscoveryServer) initConnection(node *core.Node, con *Connection, identities []string) error { - // Setup the initial proxy metadata - proxy, err := s.initProxyMetadata(node) - if err != nil { - return err - } - // Check if proxy cluster has an alias configured, if yes use that as cluster ID for this proxy. - if alias, exists := s.ClusterAliases[proxy.Metadata.ClusterID]; exists { - proxy.Metadata.ClusterID = alias - } - // To ensure push context is monotonically increasing, setup LastPushContext before we addCon. This - // way only new push contexts will be registered for this proxy. - proxy.LastPushContext = s.globalPushContext() - // First request so initialize connection id and start tracking it. - con.conID = connectionID(proxy.ID) - con.node = node - con.proxy = proxy - - // Authorize xds clients - if err := s.authorize(con, identities); err != nil { - return err - } - - // Register the connection. this allows pushes to be triggered for the proxy. Note: the timing of - // this and initializeProxy important. While registering for pushes *after* initialization is complete seems like - // a better choice, it introduces a race condition; If we complete initialization of a new push - // context between initializeProxy and addCon, we would not get any pushes triggered for the new - // push context, leading the proxy to have a stale state until the next full push. - s.addCon(con.conID, con) - // Register that initialization is complete. This triggers to calls that it is safe to access the - // proxy - defer close(con.initialized) - - // Complete full initialization of the proxy - if err := s.initializeProxy(node, con); err != nil { - s.closeConnection(con) - return err - } - - if s.StatusGen != nil { - s.StatusGen.OnConnect(con) - } - return nil -} - -func (s *DiscoveryServer) closeConnection(con *Connection) { - if con.conID == "" { - return - } - s.removeCon(con.conID) - if s.StatusGen != nil { - s.StatusGen.OnDisconnect(con) - } - if s.StatusReporter != nil { - s.StatusReporter.RegisterDisconnect(con.conID, AllEventTypesList) - } - s.WorkloadEntryController.QueueUnregisterWorkload(con.proxy, con.connectedAt) -} - -func connectionID(node string) string { - id := atomic.AddInt64(&connectionNumber, 1) - return node + "-" + strconv.FormatInt(id, 10) -} - -// initProxyMetadata initializes just the basic metadata of a proxy. This is decoupled from -// initProxyState such that we can perform authorization before attempting expensive computations to -// fully initialize the proxy. -func (s *DiscoveryServer) initProxyMetadata(node *core.Node) (*model.Proxy, error) { - meta, err := model.ParseMetadata(node.Metadata) - if err != nil { - return nil, err - } - proxy, err := model.ParseServiceNodeWithMetadata(node.Id, meta) - if err != nil { - return nil, err - } - // Update the config namespace associated with this proxy - proxy.ConfigNamespace = model.GetProxyConfigNamespace(proxy) - proxy.XdsNode = node - return proxy, nil -} - -// initializeProxy completes the initialization of a proxy. It is expected to be called only after -// initProxyMetadata. -func (s *DiscoveryServer) initializeProxy(node *core.Node, con *Connection) error { - proxy := con.proxy - // this should be done before we look for service instances, but after we load metadata - // TODO fix check in kubecontroller treat echo VMs like there isn't a pod - if err := s.WorkloadEntryController.RegisterWorkload(proxy, con.connectedAt); err != nil { - return err - } - s.computeProxyState(proxy, nil) - - // Get the locality from the proxy's service instances. - // We expect all instances to have the same IP and therefore the same locality. - // So its enough to look at the first instance. - if len(proxy.ServiceInstances) > 0 { - proxy.Locality = util.ConvertLocality(proxy.ServiceInstances[0].Endpoint.Locality.Label) - } - - // If there is no locality in the registry then use the one sent as part of the discovery request. - // This is not preferable as only the connected Pilot is aware of this proxies location, but it - // can still help provide some client-side Envoy context when load balancing based on location. - if util.IsLocalityEmpty(proxy.Locality) { - proxy.Locality = &core.Locality{ - Region: node.Locality.GetRegion(), - Zone: node.Locality.GetZone(), - SubZone: node.Locality.GetSubZone(), - } - } - - locality := util.LocalityToString(proxy.Locality) - // add topology labels to proxy metadata labels - proxy.Metadata.Labels = labelutil.AugmentLabels(proxy.Metadata.Labels, proxy.Metadata.ClusterID, locality, proxy.Metadata.Network) - // Discover supported IP Versions of proxy so that appropriate config can be delivered. - proxy.DiscoverIPMode() - - proxy.WatchedResources = map[string]*model.WatchedResource{} - // Based on node metadata and version, we can associate a different generator. - if proxy.Metadata.Generator != "" { - proxy.XdsResourceGenerator = s.Generators[proxy.Metadata.Generator] - } - - return nil -} - -func (s *DiscoveryServer) updateProxy(proxy *model.Proxy, request *model.PushRequest) { - s.computeProxyState(proxy, request) - if util.IsLocalityEmpty(proxy.Locality) { - // Get the locality from the proxy's service instances. - // We expect all instances to have the same locality. - // So its enough to look at the first instance. - if len(proxy.ServiceInstances) > 0 { - proxy.Locality = util.ConvertLocality(proxy.ServiceInstances[0].Endpoint.Locality.Label) - locality := proxy.ServiceInstances[0].Endpoint.Locality.Label - // add topology labels to proxy metadata labels - proxy.Metadata.Labels = labelutil.AugmentLabels(proxy.Metadata.Labels, proxy.Metadata.ClusterID, locality, proxy.Metadata.Network) - } - } -} - -func (s *DiscoveryServer) computeProxyState(proxy *model.Proxy, request *model.PushRequest) { - proxy.SetWorkloadLabels(s.Env) - proxy.SetServiceInstances(s.Env.ServiceDiscovery) - // Precompute the sidecar scope and merged gateways associated with this proxy. - // Saves compute cycles in networking code. Though this might be redundant sometimes, we still - // have to compute this because as part of a config change, a new Sidecar could become - // applicable to this proxy - var sidecar, gateway bool - push := proxy.LastPushContext - if request == nil { - sidecar = true - gateway = true - } else { - push = request.Push - if len(request.ConfigsUpdated) == 0 { - sidecar = true - gateway = true - } - for conf := range request.ConfigsUpdated { - switch conf.Kind { - case gvk.ServiceEntry, gvk.DestinationRule, gvk.VirtualService, gvk.Sidecar, gvk.HTTPRoute, gvk.TCPRoute, gvk.ServiceNameMapping: - sidecar = true - case gvk.Gateway, gvk.KubernetesGateway, gvk.GatewayClass: - gateway = true - case gvk.Ingress: - sidecar = true - gateway = true - } - if sidecar && gateway { - break - } - } - } - // compute the sidecarscope for both proxy type whenever it changes. - if sidecar { - proxy.SetSidecarScope(push) - } - // only compute gateways for "router" type proxy. - if gateway && proxy.Type == model.Router { - proxy.SetGatewaysForProxy(push) - } - proxy.LastPushContext = push - if request != nil { - proxy.LastPushTime = request.Start - } -} - -// handleWorkloadHealthcheck processes HealthInformation type Url. -func (s *DiscoveryServer) handleWorkloadHealthcheck(proxy *model.Proxy, req *discovery.DiscoveryRequest) { - if features.WorkloadEntryHealthChecks { - event := workloadentry.HealthEvent{} - event.Healthy = req.ErrorDetail == nil - if !event.Healthy { - event.Message = req.ErrorDetail.Message - } - s.WorkloadEntryController.QueueWorkloadEntryHealth(proxy, event) - } -} - -// DeltaAggregatedResources is not implemented. -// Instead, Generators may send only updates/add, with Delete indicated by an empty spec. -// This works if both ends follow this model. For example EDS and the API generator follow this -// pattern. -// -// The delta protocol changes the request, adding unsubscribe/subscribe instead of sending full -// list of resources. On the response it adds 'removed resources' and sends changes for everything. -func (s *DiscoveryServer) DeltaAggregatedResources(stream discovery.AggregatedDiscoveryService_DeltaAggregatedResourcesServer) error { - return s.StreamDeltas(stream) -} - -// Compute and send the new configuration for a connection. -func (s *DiscoveryServer) pushConnection(con *Connection, pushEv *Event) error { - pushRequest := pushEv.pushRequest - - if pushRequest.Full { - // Update Proxy with current information. - s.updateProxy(con.proxy, pushRequest) - } - - if !s.ProxyNeedsPush(con.proxy, pushRequest) { - log.Debugf("Skipping push to %v, no updates required", con.conID) - if pushRequest.Full { - // Only report for full versions, incremental pushes do not have a new version. - reportAllEvents(s.StatusReporter, con.conID, pushRequest.Push.LedgerVersion, nil) - } - return nil - } - - // Send pushes to all generators - // Each Generator is responsible for determining if the push event requires a push - wrl, ignoreEvents := con.pushDetails() - for _, w := range wrl { - if err := s.pushXds(con, w, pushRequest); err != nil { - return err - } - } - if pushRequest.Full { - // Report all events for unwatched resources. Watched resources will be reported in pushXds or on ack. - reportAllEvents(s.StatusReporter, con.conID, pushRequest.Push.LedgerVersion, ignoreEvents) - } - - proxiesConvergeDelay.Record(time.Since(pushRequest.Start).Seconds()) - return nil -} - -// PushOrder defines the order that updates will be pushed in. Any types not listed here will be pushed in random -// order after the types listed here -var PushOrder = []string{v3.ClusterType, v3.EndpointType, v3.ListenerType, v3.RouteType, v3.SecretType} - -// KnownOrderedTypeUrls has typeUrls for which we know the order of push. -var KnownOrderedTypeUrls = map[string]struct{}{ - v3.ClusterType: {}, - v3.EndpointType: {}, - v3.ListenerType: {}, - v3.RouteType: {}, - v3.SecretType: {}, -} - -func reportAllEvents(s DistributionStatusCache, id, version string, ignored sets.Set) { - if s == nil { - return - } - // this version of the config will never be distributed to this envoy because it is not a relevant diff. - // inform distribution status reporter that this connection has been updated, because it effectively has - for distributionType := range AllEventTypes { - if ignored.Contains(distributionType) { - // Skip this type - continue - } - s.RegisterEvent(id, distributionType, version) - } -} - -func (s *DiscoveryServer) adsClientCount() int { - s.adsClientsMutex.RLock() - defer s.adsClientsMutex.RUnlock() - return len(s.adsClients) -} - -func (s *DiscoveryServer) ProxyUpdate(clusterID cluster.ID, ip string) { - var connection *Connection - - for _, v := range s.Clients() { - if v.proxy.Metadata.ClusterID == clusterID && v.proxy.IPAddresses[0] == ip { - connection = v - break - } - } - - // It is possible that the envoy has not connected to this pilot, maybe connected to another pilot - if connection == nil { - return - } - if log.DebugEnabled() { - currentlyPending := s.pushQueue.Pending() - if currentlyPending != 0 { - log.Debugf("Starting new push while %v were still pending", currentlyPending) - } - } - - s.pushQueue.Enqueue(connection, &model.PushRequest{ - Full: true, - Push: s.globalPushContext(), - Start: time.Now(), - Reason: []model.TriggerReason{model.ProxyUpdate}, - }) -} - -// AdsPushAll will send updates to all nodes, for a full config or incremental EDS. -func AdsPushAll(s *DiscoveryServer) { - s.AdsPushAll(versionInfo(), &model.PushRequest{ - Full: true, - Push: s.globalPushContext(), - Reason: []model.TriggerReason{model.DebugTrigger}, - }) -} - -// AdsPushAll implements old style invalidation, generated when any rule or endpoint changes. -// Primary code path is from v1 discoveryService.clearCache(), which is added as a handler -// to the model ConfigStorageCache and Controller. -func (s *DiscoveryServer) AdsPushAll(version string, req *model.PushRequest) { - if !req.Full { - log.Infof("XDS: Incremental Pushing:%s ConnectedEndpoints:%d Version:%s", - version, s.adsClientCount(), req.Push.PushVersion) - } else { - totalService := len(req.Push.GetAllServices()) - log.Infof("XDS: Pushing:%s Services:%d ConnectedEndpoints:%d Version:%s", - version, totalService, s.adsClientCount(), req.Push.PushVersion) - monServices.Record(float64(totalService)) - - // Make sure the ConfigsUpdated map exists - if req.ConfigsUpdated == nil { - req.ConfigsUpdated = make(map[model.ConfigKey]struct{}) - } - } - - s.startPush(req) -} - -// Send a signal to all connections, with a push event. -func (s *DiscoveryServer) startPush(req *model.PushRequest) { - // Push config changes, iterating over connected envoys. - if log.DebugEnabled() { - currentlyPending := s.pushQueue.Pending() - if currentlyPending != 0 { - log.Debugf("Starting new push while %v were still pending", currentlyPending) - } - } - req.Start = time.Now() - for _, p := range s.AllClients() { - s.pushQueue.Enqueue(p, req) - } -} - -func (s *DiscoveryServer) addCon(conID string, con *Connection) { - s.adsClientsMutex.Lock() - defer s.adsClientsMutex.Unlock() - s.adsClients[conID] = con - recordXDSClients(con.proxy.Metadata.IstioVersion, 1) -} - -func (s *DiscoveryServer) removeCon(conID string) { - s.adsClientsMutex.Lock() - defer s.adsClientsMutex.Unlock() - - if con, exist := s.adsClients[conID]; !exist { - log.Errorf("ADS: Removing connection for non-existing node:%v.", conID) - totalXDSInternalErrors.Increment() - } else { - delete(s.adsClients, conID) - recordXDSClients(con.proxy.Metadata.IstioVersion, -1) - } -} - -// Send with timeout if configured. -func (conn *Connection) send(res *discovery.DiscoveryResponse) error { - sendHandler := func() error { - start := time.Now() - defer func() { recordSendTime(time.Since(start)) }() - return conn.stream.Send(res) - } - err := istiogrpc.Send(conn.stream.Context(), sendHandler) - if err == nil { - sz := 0 - for _, rc := range res.Resources { - sz += len(rc.Value) - } - if res.Nonce != "" && !strings.HasPrefix(res.TypeUrl, v3.DebugType) { - conn.proxy.Lock() - if conn.proxy.WatchedResources[res.TypeUrl] == nil { - conn.proxy.WatchedResources[res.TypeUrl] = &model.WatchedResource{TypeUrl: res.TypeUrl} - } - conn.proxy.WatchedResources[res.TypeUrl].NonceSent = res.Nonce - conn.proxy.WatchedResources[res.TypeUrl].VersionSent = res.VersionInfo - conn.proxy.WatchedResources[res.TypeUrl].LastSent = time.Now() - conn.proxy.Unlock() - } - } else if status.Convert(err).Code() == codes.DeadlineExceeded { - log.Infof("Timeout writing %s", conn.conID) - xdsResponseWriteTimeouts.Increment() - } - return err -} - -// nolint -// Synced checks if the type has been synced, meaning the most recent push was ACKed -func (conn *Connection) Synced(typeUrl string) (bool, bool) { - conn.proxy.RLock() - defer conn.proxy.RUnlock() - acked := conn.proxy.WatchedResources[typeUrl].NonceAcked - sent := conn.proxy.WatchedResources[typeUrl].NonceSent - nacked := conn.proxy.WatchedResources[typeUrl].NonceNacked != "" - sendTime := conn.proxy.WatchedResources[typeUrl].LastSent - return nacked || acked == sent, time.Since(sendTime) > features.FlowControlTimeout -} - -// nolint -func (conn *Connection) NonceAcked(typeUrl string) string { - conn.proxy.RLock() - defer conn.proxy.RUnlock() - if conn.proxy.WatchedResources != nil && conn.proxy.WatchedResources[typeUrl] != nil { - return conn.proxy.WatchedResources[typeUrl].NonceAcked - } - return "" -} - -// nolint -func (conn *Connection) NonceSent(typeUrl string) string { - conn.proxy.RLock() - defer conn.proxy.RUnlock() - if conn.proxy.WatchedResources != nil && conn.proxy.WatchedResources[typeUrl] != nil { - return conn.proxy.WatchedResources[typeUrl].NonceSent - } - return "" -} - -func (conn *Connection) Clusters() []string { - conn.proxy.RLock() - defer conn.proxy.RUnlock() - if conn.proxy.WatchedResources != nil && conn.proxy.WatchedResources[v3.EndpointType] != nil { - return conn.proxy.WatchedResources[v3.EndpointType].ResourceNames - } - return []string{} -} - -func (conn *Connection) Routes() []string { - conn.proxy.RLock() - defer conn.proxy.RUnlock() - if conn.proxy.WatchedResources != nil && conn.proxy.WatchedResources[v3.RouteType] != nil { - return conn.proxy.WatchedResources[v3.RouteType].ResourceNames - } - return []string{} -} - -// nolint -func (conn *Connection) Watching(typeUrl string) bool { - conn.proxy.RLock() - defer conn.proxy.RUnlock() - if conn.proxy.WatchedResources != nil && conn.proxy.WatchedResources[typeUrl] != nil { - return true - } - return false -} - -// nolint -func (conn *Connection) Watched(typeUrl string) *model.WatchedResource { - conn.proxy.RLock() - defer conn.proxy.RUnlock() - if conn.proxy.WatchedResources != nil && conn.proxy.WatchedResources[typeUrl] != nil { - return conn.proxy.WatchedResources[typeUrl] - } - return nil -} - -// pushDetails returns the details needed for current push. It returns ordered list of -// watched resources for the proxy, ordered in accordance with known push order. -// It also returns the lis of typeUrls. -// nolint -func (conn *Connection) pushDetails() ([]*model.WatchedResource, sets.Set) { - conn.proxy.RLock() - defer conn.proxy.RUnlock() - typeUrls := sets.New() - for k := range conn.proxy.WatchedResources { - typeUrls.Insert(k) - } - return orderWatchedResources(conn.proxy.WatchedResources), typeUrls -} - -func orderWatchedResources(resources map[string]*model.WatchedResource) []*model.WatchedResource { - wr := make([]*model.WatchedResource, 0, len(resources)) - // first add all known types, in order - for _, tp := range PushOrder { - if w, f := resources[tp]; f { - wr = append(wr, w) - } - } - // Then add any undeclared types - for tp, w := range resources { - if _, f := KnownOrderedTypeUrls[tp]; !f { - wr = append(wr, w) - } - } - return wr -} - -func (conn *Connection) Stop() { - close(conn.stop) -} diff --git a/pilot/pkg/xds/ads_test.go b/pilot/pkg/xds/ads_test.go deleted file mode 100644 index c26507d0c..000000000 --- a/pilot/pkg/xds/ads_test.go +++ /dev/null @@ -1,953 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package xds_test - -import ( - "fmt" - "reflect" - "testing" - "time" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/adsc" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -const ( - routeA = "http.80" - routeB = "https.443.https.my-gateway.testns" -) - -func TestStatusEvents(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - - ads := s.Connect( - &model.Proxy{ - Metadata: &model.NodeMetadata{ - Generator: "event", - }, - }, - []string{xds.TypeURLConnect}, - []string{}, - ) - defer ads.Close() - - dr, err := ads.WaitVersion(5*time.Second, xds.TypeURLConnect, "") - if err != nil { - t.Fatal(err) - } - - if dr.Resources == nil || len(dr.Resources) == 0 { - t.Error("Expected connections, but not found") - } - - // Create a second connection - we should get an event. - ads2 := s.Connect(nil, nil, nil) - defer ads2.Close() - - dr, err = ads.WaitVersion(5*time.Second, xds.TypeURLConnect, - dr.VersionInfo) - if err != nil { - t.Fatal(err) - } - if dr.Resources == nil || len(dr.Resources) == 0 { - t.Error("Expected connections, but not found") - } -} - -func TestAdsReconnectAfterRestart(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - - ads := s.ConnectADS().WithType(v3.EndpointType) - res := ads.RequestResponseAck(t, &discovery.DiscoveryRequest{ResourceNames: []string{"fake-cluster"}}) - // Close the connection and reconnect - ads.Cleanup() - - ads = s.ConnectADS().WithType(v3.EndpointType) - - // Reconnect with the same resources - ads.RequestResponseAck(t, &discovery.DiscoveryRequest{ - ResourceNames: []string{"fake-cluster"}, - ResponseNonce: res.Nonce, - VersionInfo: res.VersionInfo, - }) -} - -func TestAdsUnsubscribe(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - - ads := s.ConnectADS().WithType(v3.EndpointType) - res := ads.RequestResponseAck(t, &discovery.DiscoveryRequest{ResourceNames: []string{"fake-cluster"}}) - - ads.Request(t, &discovery.DiscoveryRequest{ - ResourceNames: nil, - ResponseNonce: res.Nonce, - VersionInfo: res.VersionInfo, - }) - ads.ExpectNoResponse(t) -} - -// Regression for envoy restart and overlapping connections -func TestAdsReconnect(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - ads := s.ConnectADS().WithType(v3.ClusterType) - ads.RequestResponseAck(t, nil) - - // envoy restarts and reconnects - ads2 := s.ConnectADS().WithType(v3.ClusterType) - ads2.RequestResponseAck(t, nil) - - // closes old process - ads.Cleanup() - - // event happens, expect push to the remaining connection - xds.AdsPushAll(s.Discovery) - ads2.ExpectResponse(t) -} - -// Regression for connection with a bad ID -func TestAdsBadId(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - ads := s.ConnectADS().WithID("").WithType(v3.ClusterType) - xds.AdsPushAll(s.Discovery) - ads.ExpectNoResponse(t) -} - -func TestAdsClusterUpdate(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - ads := s.ConnectADS().WithType(v3.EndpointType) - - version := "" - nonce := "" - sendEDSReqAndVerify := func(clusterName string) { - res := ads.RequestResponseAck(t, &discovery.DiscoveryRequest{ - ResourceNames: []string{clusterName}, - VersionInfo: version, - ResponseNonce: nonce, - }) - version = res.VersionInfo - nonce = res.Nonce - got := xdstest.MapKeys(xdstest.ExtractLoadAssignments(xdstest.UnmarshalClusterLoadAssignment(t, res.Resources))) - if len(got) != 1 { - t.Fatalf("expected 1 response, got %v", len(got)) - } - if got[0] != clusterName { - t.Fatalf("expected cluster %v got %v", clusterName, got[0]) - } - } - - cluster1 := "outbound|80||local.default.svc.cluster.local" - sendEDSReqAndVerify(cluster1) - cluster2 := "outbound|80||hello.default.svc.cluster.local" - sendEDSReqAndVerify(cluster2) -} - -// nolint: lll -func TestAdsPushScoping(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - - const ( - svcSuffix = ".testPushScoping.com" - ns1 = "ns1" - ) - - removeServiceByNames := func(ns string, names ...string) { - configsUpdated := map[model.ConfigKey]struct{}{} - - for _, name := range names { - hostname := host.Name(name) - s.Discovery.MemRegistry.RemoveService(hostname) - configsUpdated[model.ConfigKey{ - Kind: gvk.ServiceEntry, - Name: string(hostname), - Namespace: ns, - }] = struct{}{} - } - - s.Discovery.ConfigUpdate(&model.PushRequest{Full: true, ConfigsUpdated: configsUpdated}) - } - removeService := func(ns string, indexes ...int) { - var names []string - - for _, i := range indexes { - names = append(names, fmt.Sprintf("svc%d%s", i, svcSuffix)) - } - - removeServiceByNames(ns, names...) - } - addServiceByNames := func(ns string, names ...string) { - configsUpdated := map[model.ConfigKey]struct{}{} - - for _, name := range names { - hostname := host.Name(name) - configsUpdated[model.ConfigKey{ - Kind: gvk.ServiceEntry, - Name: string(hostname), - Namespace: ns, - }] = struct{}{} - - s.Discovery.MemRegistry.AddService(&model.Service{ - Hostname: hostname, - DefaultAddress: "10.11.0.1", - Ports: []*model.Port{ - { - Name: "http-main", - Port: 2080, - Protocol: protocol.HTTP, - }, - }, - Attributes: model.ServiceAttributes{ - Namespace: ns, - }, - }) - } - - s.Discovery.ConfigUpdate(&model.PushRequest{Full: true, ConfigsUpdated: configsUpdated}) - } - addService := func(ns string, indexes ...int) { - var hostnames []string - for _, i := range indexes { - hostnames = append(hostnames, fmt.Sprintf("svc%d%s", i, svcSuffix)) - } - addServiceByNames(ns, hostnames...) - } - - addServiceInstance := func(hostname host.Name, indexes ...int) { - for _, i := range indexes { - s.Discovery.MemRegistry.AddEndpoint(hostname, "http-main", 2080, "192.168.1.10", i) - } - - s.Discovery.ConfigUpdate(&model.PushRequest{Full: false, ConfigsUpdated: map[model.ConfigKey]struct{}{ - {Kind: gvk.ServiceEntry, Name: string(hostname), Namespace: model.IstioDefaultConfigNamespace}: {}, - }}) - } - - addVirtualService := func(i int, hosts []string, dest string) { - if _, err := s.Store().Create(config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: fmt.Sprintf("vs%d", i), Namespace: model.IstioDefaultConfigNamespace, - }, - Spec: &networking.VirtualService{ - Hosts: hosts, - Http: []*networking.HTTPRoute{{ - Name: "dest-foo", - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{ - Host: dest, - }, - }}, - }}, - ExportTo: nil, - }, - }); err != nil { - t.Fatal(err) - } - } - removeVirtualService := func(i int) { - s.Store().Delete(gvk.VirtualService, fmt.Sprintf("vs%d", i), model.IstioDefaultConfigNamespace, nil) - } - - addDelegateVirtualService := func(i int, hosts []string, dest string) { - if _, err := s.Store().Create(config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: fmt.Sprintf("rootvs%d", i), Namespace: model.IstioDefaultConfigNamespace, - }, - Spec: &networking.VirtualService{ - Hosts: hosts, - - Http: []*networking.HTTPRoute{{ - Name: "dest-foo", - Delegate: &networking.Delegate{ - Name: fmt.Sprintf("delegatevs%d", i), - Namespace: model.IstioDefaultConfigNamespace, - }, - }}, - ExportTo: nil, - }, - }); err != nil { - t.Fatal(err) - } - - if _, err := s.Store().Create(config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: fmt.Sprintf("delegatevs%d", i), Namespace: model.IstioDefaultConfigNamespace, - }, - Spec: &networking.VirtualService{ - Http: []*networking.HTTPRoute{{ - Name: "dest-foo", - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{ - Host: dest, - }, - }}, - }}, - ExportTo: nil, - }, - }); err != nil { - t.Fatal(err) - } - } - - updateDelegateVirtualService := func(i int, dest string) { - if _, err := s.Store().Update(config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.VirtualService, - Name: fmt.Sprintf("delegatevs%d", i), Namespace: model.IstioDefaultConfigNamespace, - }, - Spec: &networking.VirtualService{ - Http: []*networking.HTTPRoute{{ - Name: "dest-foo", - Headers: &networking.Headers{ - Request: &networking.Headers_HeaderOperations{ - Remove: []string{"any-string"}, - }, - }, - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: dest, - }, - }, - }, - }}, - ExportTo: nil, - }, - }); err != nil { - t.Fatal(err) - } - } - - removeDelegateVirtualService := func(i int) { - s.Store().Delete(gvk.VirtualService, fmt.Sprintf("rootvs%d", i), model.IstioDefaultConfigNamespace, nil) - s.Store().Delete(gvk.VirtualService, fmt.Sprintf("delegatevs%d", i), model.IstioDefaultConfigNamespace, nil) - } - - addDestinationRule := func(i int, host string) { - if _, err := s.Store().Create(config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: fmt.Sprintf("dr%d", i), Namespace: model.IstioDefaultConfigNamespace, - }, - Spec: &networking.DestinationRule{ - Host: host, - ExportTo: nil, - }, - }); err != nil { - t.Fatal(err) - } - } - removeDestinationRule := func(i int) { - s.Store().Delete(gvk.DestinationRule, fmt.Sprintf("dr%d", i), model.IstioDefaultConfigNamespace, nil) - } - - sc := &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{model.IstioDefaultConfigNamespace + "/*" + svcSuffix}, - }, - }, - } - scc := config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.Sidecar, - Name: "sc", Namespace: model.IstioDefaultConfigNamespace, - }, - Spec: sc, - } - notMatchedScc := config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.Sidecar, - Name: "notMatchedSc", Namespace: model.IstioDefaultConfigNamespace, - }, - Spec: &networking.Sidecar{ - WorkloadSelector: &networking.WorkloadSelector{ - Labels: map[string]string{"notMatched": "notMatched"}, - }, - }, - } - if _, err := s.Store().Create(scc); err != nil { - t.Fatal(err) - } - addService(model.IstioDefaultConfigNamespace, 1, 2, 3) - - adscConn := s.Connect(nil, nil, nil) - defer adscConn.Close() - type svcCase struct { - desc string - - ev model.Event - svcIndexes []int - svcNames []string - ns string - instIndexes []struct { - name string - indexes []int - } - vsIndexes []struct { - index int - hosts []string - dest string - } - delegatevsIndexes []struct { - index int - hosts []string - dest string - } - drIndexes []struct { - index int - host string - } - cfgs []config.Config - - expectedUpdates []string - unexpectedUpdates []string - } - svcCases := []svcCase{ - { - desc: "Add a scoped service", - ev: model.EventAdd, - svcIndexes: []int{4}, - ns: model.IstioDefaultConfigNamespace, - expectedUpdates: []string{v3.ListenerType}, - }, // then: default 1,2,3,4 - { - desc: "Add instances to a scoped service", - ev: model.EventAdd, - instIndexes: []struct { - name string - indexes []int - }{{fmt.Sprintf("svc%d%s", 4, svcSuffix), []int{1, 2}}}, - ns: model.IstioDefaultConfigNamespace, - expectedUpdates: []string{v3.EndpointType}, - }, // then: default 1,2,3,4 - { - desc: "Add virtual service to a scoped service", - ev: model.EventAdd, - vsIndexes: []struct { - index int - hosts []string - dest string - }{{index: 4, hosts: []string{fmt.Sprintf("svc%d%s", 4, svcSuffix)}, dest: "unknown-svc"}}, - expectedUpdates: []string{v3.ListenerType}, - }, - { - desc: "Delete virtual service of a scoped service", - ev: model.EventDelete, - vsIndexes: []struct { - index int - hosts []string - dest string - }{{index: 4}}, - expectedUpdates: []string{v3.ListenerType}, - }, - { - desc: "Add destination rule to a scoped service", - ev: model.EventAdd, - drIndexes: []struct { - index int - host string - }{{4, fmt.Sprintf("svc%d%s", 4, svcSuffix)}}, - expectedUpdates: []string{v3.ClusterType}, - }, - { - desc: "Delete destination rule of a scoped service", - ev: model.EventDelete, - drIndexes: []struct { - index int - host string - }{{index: 4}}, - expectedUpdates: []string{v3.ClusterType}, - }, - { - desc: "Add a unscoped(name not match) service", - ev: model.EventAdd, - svcNames: []string{"foo.com"}, - ns: model.IstioDefaultConfigNamespace, - unexpectedUpdates: []string{v3.ClusterType}, - }, // then: default 1,2,3,4, foo.com; ns1: 11 - { - desc: "Add instances to an unscoped service", - ev: model.EventAdd, - instIndexes: []struct { - name string - indexes []int - }{{"foo.com", []int{1, 2}}}, - ns: model.IstioDefaultConfigNamespace, - unexpectedUpdates: []string{v3.EndpointType}, - }, // then: default 1,2,3,4 - { - desc: "Add a unscoped(ns not match) service", - ev: model.EventAdd, - svcIndexes: []int{11}, - ns: ns1, - unexpectedUpdates: []string{v3.ClusterType}, - }, // then: default 1,2,3,4, foo.com; ns1: 11 - { - desc: "Add virtual service to an unscoped service", - ev: model.EventAdd, - vsIndexes: []struct { - index int - hosts []string - dest string - }{{index: 0, hosts: []string{"foo.com"}, dest: "unknown-service"}}, - unexpectedUpdates: []string{v3.ClusterType}, - }, - { - desc: "Delete virtual service of a unscoped service", - ev: model.EventDelete, - vsIndexes: []struct { - index int - hosts []string - dest string - }{{index: 0}}, - unexpectedUpdates: []string{v3.ClusterType}, - }, - { - desc: "Add destination rule to an unscoped service", - ev: model.EventAdd, - drIndexes: []struct { - index int - host string - }{{0, "foo.com"}}, - unexpectedUpdates: []string{v3.ClusterType}, - }, - { - desc: "Delete destination rule of a unscoped service", - ev: model.EventDelete, - drIndexes: []struct { - index int - host string - }{{index: 0}}, - unexpectedUpdates: []string{v3.ClusterType}, - }, - { - desc: "Add virtual service for scoped service with transitively scoped dest svc", - ev: model.EventAdd, - vsIndexes: []struct { - index int - hosts []string - dest string - }{{index: 4, hosts: []string{fmt.Sprintf("svc%d%s", 4, svcSuffix)}, dest: "foo.com"}}, - expectedUpdates: []string{v3.ClusterType, v3.EndpointType}, - }, - { - desc: "Add instances for transitively scoped svc", - ev: model.EventAdd, - instIndexes: []struct { - name string - indexes []int - }{{"foo.com", []int{1, 2}}}, - ns: model.IstioDefaultConfigNamespace, - expectedUpdates: []string{v3.EndpointType}, - }, - { - desc: "Delete virtual service for scoped service with transitively scoped dest svc", - ev: model.EventDelete, - vsIndexes: []struct { - index int - hosts []string - dest string - }{{index: 4}}, - expectedUpdates: []string{v3.ClusterType}, - }, - { - desc: "Add delegation virtual service for scoped service with transitively scoped dest svc", - ev: model.EventAdd, - delegatevsIndexes: []struct { - index int - hosts []string - dest string - }{{index: 4, hosts: []string{fmt.Sprintf("svc%d%s", 4, svcSuffix)}, dest: "foo.com"}}, - expectedUpdates: []string{v3.ListenerType, v3.RouteType, v3.ClusterType, v3.EndpointType}, - }, - { - desc: "Update delegate virtual service should trigger full push", - ev: model.EventUpdate, - delegatevsIndexes: []struct { - index int - hosts []string - dest string - }{{index: 4, hosts: []string{fmt.Sprintf("svc%d%s", 4, svcSuffix)}, dest: "foo.com"}}, - expectedUpdates: []string{v3.ListenerType, v3.RouteType, v3.ClusterType}, - }, - { - desc: "Delete delegate virtual service for scoped service with transitively scoped dest svc", - ev: model.EventDelete, - delegatevsIndexes: []struct { - index int - hosts []string - dest string - }{{index: 4}}, - expectedUpdates: []string{v3.ListenerType, v3.RouteType, v3.ClusterType}, - }, - { - desc: "Remove a scoped service", - ev: model.EventDelete, - svcIndexes: []int{4}, - ns: model.IstioDefaultConfigNamespace, - expectedUpdates: []string{v3.ListenerType}, - }, // then: default 1,2,3, foo.com; ns: 11 - { - desc: "Remove a unscoped(name not match) service", - ev: model.EventDelete, - svcNames: []string{"foo.com"}, - ns: model.IstioDefaultConfigNamespace, - unexpectedUpdates: []string{v3.ClusterType}, - }, // then: default 1,2,3; ns1: 11 - { - desc: "Remove a unscoped(ns not match) service", - ev: model.EventDelete, - svcIndexes: []int{11}, - ns: ns1, - unexpectedUpdates: []string{v3.ClusterType}, - }, // then: default 1,2,3 - { - desc: "Add an unmatched Sidecar config", - ev: model.EventAdd, - cfgs: []config.Config{notMatchedScc}, - ns: model.IstioDefaultConfigNamespace, - unexpectedUpdates: []string{v3.ListenerType, v3.RouteType, v3.ClusterType, v3.EndpointType}, - }, - { - desc: "Update the Sidecar config", - ev: model.EventUpdate, - cfgs: []config.Config{scc}, - ns: model.IstioDefaultConfigNamespace, - expectedUpdates: []string{v3.ListenerType, v3.RouteType, v3.ClusterType, v3.EndpointType}, - }, - } - - for _, c := range svcCases { - t.Run(c.desc, func(t *testing.T) { - // Let events from previous tests complete - time.Sleep(time.Millisecond * 50) - adscConn.WaitClear() - var wantUpdates []string - wantUpdates = append(wantUpdates, c.expectedUpdates...) - wantUpdates = append(wantUpdates, c.unexpectedUpdates...) - - switch c.ev { - case model.EventAdd: - if len(c.svcIndexes) > 0 { - addService(c.ns, c.svcIndexes...) - } - if len(c.svcNames) > 0 { - addServiceByNames(c.ns, c.svcNames...) - } - if len(c.instIndexes) > 0 { - for _, instIndex := range c.instIndexes { - addServiceInstance(host.Name(instIndex.name), instIndex.indexes...) - } - } - if len(c.vsIndexes) > 0 { - for _, vsIndex := range c.vsIndexes { - addVirtualService(vsIndex.index, vsIndex.hosts, vsIndex.dest) - } - } - if len(c.delegatevsIndexes) > 0 { - for _, vsIndex := range c.delegatevsIndexes { - addDelegateVirtualService(vsIndex.index, vsIndex.hosts, vsIndex.dest) - } - } - if len(c.drIndexes) > 0 { - for _, drIndex := range c.drIndexes { - addDestinationRule(drIndex.index, drIndex.host) - } - } - if len(c.cfgs) > 0 { - for _, cfg := range c.cfgs { - if _, err := s.Store().Create(cfg); err != nil { - t.Fatal(err) - } - } - } - case model.EventUpdate: - if len(c.delegatevsIndexes) > 0 { - for _, vsIndex := range c.delegatevsIndexes { - updateDelegateVirtualService(vsIndex.index, vsIndex.dest) - } - } - if len(c.cfgs) > 0 { - for _, cfg := range c.cfgs { - if _, err := s.Store().Update(cfg); err != nil { - t.Fatal(err) - } - } - } - case model.EventDelete: - if len(c.svcIndexes) > 0 { - removeService(c.ns, c.svcIndexes...) - } - if len(c.svcNames) > 0 { - removeServiceByNames(c.ns, c.svcNames...) - } - if len(c.vsIndexes) > 0 { - for _, vsIndex := range c.vsIndexes { - removeVirtualService(vsIndex.index) - } - } - if len(c.delegatevsIndexes) > 0 { - for _, vsIndex := range c.delegatevsIndexes { - removeDelegateVirtualService(vsIndex.index) - } - } - if len(c.drIndexes) > 0 { - for _, drIndex := range c.drIndexes { - removeDestinationRule(drIndex.index) - } - } - default: - t.Fatalf("wrong event for case %v", c) - } - - timeout := time.Millisecond * 200 - upd, _ := adscConn.Wait(timeout, wantUpdates...) - for _, expect := range c.expectedUpdates { - if !contains(upd, expect) { - t.Fatalf("expected update %s not in updates %v", expect, upd) - } - } - for _, unexpect := range c.unexpectedUpdates { - if contains(upd, unexpect) { - t.Fatalf("expected to not get update %s, but it is in updates %v", unexpect, upd) - } - } - }) - } -} - -func TestAdsUpdate(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - ads := s.ConnectADS() - - s.Discovery.MemRegistry.AddService(&model.Service{ - Hostname: "adsupdate.default.svc.cluster.local", - DefaultAddress: "10.11.0.1", - Ports: []*model.Port{ - { - Name: "http-main", - Port: 2080, - Protocol: protocol.HTTP, - }, - }, - Attributes: model.ServiceAttributes{ - Name: "adsupdate", - Namespace: "default", - }, - }) - s.Discovery.ConfigUpdate(&model.PushRequest{Full: true}) - time.Sleep(time.Millisecond * 200) - s.Discovery.MemRegistry.SetEndpoints("adsupdate.default.svc.cluster.local", "default", - newEndpointWithAccount("10.2.0.1", "hello-sa", "v1")) - - cluster := "outbound|2080||adsupdate.default.svc.cluster.local" - res := ads.RequestResponseAck(t, &discovery.DiscoveryRequest{ - ResourceNames: []string{cluster}, - TypeUrl: v3.EndpointType, - }) - eps, f := xdstest.ExtractLoadAssignments(xdstest.UnmarshalClusterLoadAssignment(t, res.GetResources()))[cluster] - if !f { - t.Fatalf("did not find cluster %v", cluster) - } - if !reflect.DeepEqual(eps, []string{"10.2.0.1:80"}) { - t.Fatalf("expected endpoints [10.2.0.1:80] got %v", eps) - } - - _ = s.Discovery.MemRegistry.AddEndpoint("adsupdate.default.svc.cluster.local", - "http-main", 2080, "10.1.7.1", 1080) - - // will trigger recompute and push for all clients - including some that may be closing - // This reproduced the 'push on closed connection' bug. - xds.AdsPushAll(s.Discovery) - res1 := ads.ExpectResponse(t) - xdstest.UnmarshalClusterLoadAssignment(t, res1.GetResources()) -} - -func TestEnvoyRDSProtocolError(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - ads := s.ConnectADS().WithType(v3.RouteType) - ads.RequestResponseAck(t, &discovery.DiscoveryRequest{ResourceNames: []string{routeA}}) - - xds.AdsPushAll(s.Discovery) - res := ads.ExpectResponse(t) - - // send empty response and validate no response is returned. - ads.Request(t, &discovery.DiscoveryRequest{ - ResourceNames: nil, - VersionInfo: res.VersionInfo, - ResponseNonce: res.Nonce, - }) - ads.ExpectNoResponse(t) - - // Refresh routes - ads.Request(t, &discovery.DiscoveryRequest{ - ResourceNames: []string{routeA, routeB}, - VersionInfo: res.VersionInfo, - ResponseNonce: res.Nonce, - }) -} - -func TestEnvoyRDSUpdatedRouteRequest(t *testing.T) { - expectRoutes := func(resp *discovery.DiscoveryResponse, expected ...string) { - t.Helper() - got := xdstest.MapKeys(xdstest.ExtractRouteConfigurations(xdstest.UnmarshalRouteConfiguration(t, resp.Resources))) - if !reflect.DeepEqual(expected, got) { - t.Fatalf("expected routes %v got %v", expected, got) - } - } - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - ads := s.ConnectADS().WithType(v3.RouteType) - resp := ads.RequestResponseAck(t, &discovery.DiscoveryRequest{ResourceNames: []string{routeA}}) - expectRoutes(resp, routeA) - - xds.AdsPushAll(s.Discovery) - resp = ads.ExpectResponse(t) - expectRoutes(resp, routeA) - - // Test update from A -> B - resp = ads.RequestResponseAck(t, &discovery.DiscoveryRequest{ResourceNames: []string{routeB}}) - expectRoutes(resp, routeB) - - // Test update from B -> A, B - resp = ads.RequestResponseAck(t, &discovery.DiscoveryRequest{ResourceNames: []string{routeA, routeB}}) - expectRoutes(resp, routeA, routeB) - - // Test update from B, B -> A - resp = ads.RequestResponseAck(t, &discovery.DiscoveryRequest{ResourceNames: []string{routeA}}) - expectRoutes(resp, routeA) -} - -func TestEdsCache(t *testing.T) { - makeEndpoint := func(addr []*networking.WorkloadEntry) config.Config { - return config.Config{ - Meta: config.Meta{ - Name: "service", - Namespace: "default", - GroupVersionKind: gvk.ServiceEntry, - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{"foo.com"}, - Ports: []*networking.Port{{ - Number: 80, - Protocol: "HTTP", - Name: "http", - }}, - Resolution: networking.ServiceEntry_STATIC, - Endpoints: addr, - }, - } - } - assertEndpoints := func(a *adsc.ADSC, addr ...string) { - t.Helper() - retry.UntilSuccessOrFail(t, func() error { - got := sets.New(xdstest.ExtractEndpoints(a.GetEndpoints()["outbound|80||foo.com"])...) - want := sets.New(addr...) - - if !got.Equals(want) { - return fmt.Errorf("invalid endpoints, got %v want %v", got, addr) - } - return nil - }, retry.Timeout(time.Second*5)) - } - - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ - Configs: []config.Config{ - makeEndpoint([]*networking.WorkloadEntry{ - {Address: "1.2.3.4", Locality: "region/zone"}, - {Address: "1.2.3.5", Locality: "notmatch"}, - }), - }, - }) - ads := s.Connect(&model.Proxy{Locality: &core.Locality{Region: "region"}}, nil, watchAll) - - assertEndpoints(ads, "1.2.3.4:80", "1.2.3.5:80") - t.Logf("endpoints: %+v", xdstest.ExtractEndpoints(ads.GetEndpoints()["outbound|80||foo.com"])) - - if _, err := s.Store().Update(makeEndpoint([]*networking.WorkloadEntry{ - {Address: "1.2.3.6", Locality: "region/zone"}, - {Address: "1.2.3.5", Locality: "notmatch"}, - })); err != nil { - t.Fatal(err) - } - if _, err := ads.Wait(time.Second*5, v3.EndpointType); err != nil { - t.Fatal(err) - } - assertEndpoints(ads, "1.2.3.6:80", "1.2.3.5:80") - t.Logf("endpoints: %+v", xdstest.ExtractEndpoints(ads.GetEndpoints()["outbound|80||foo.com"])) - - ads.WaitClear() - if _, err := s.Store().Create(config.Config{ - Meta: config.Meta{ - Name: "service", - Namespace: "default", - GroupVersionKind: gvk.DestinationRule, - }, - Spec: &networking.DestinationRule{ - Host: "foo.com", - TrafficPolicy: &networking.TrafficPolicy{ - OutlierDetection: &networking.OutlierDetection{}, - }, - }, - }); err != nil { - t.Fatal(err) - } - if _, err := ads.Wait(time.Second*5, v3.EndpointType); err != nil { - t.Fatal(err) - } - assertEndpoints(ads, "1.2.3.6:80", "1.2.3.5:80") - retry.UntilSuccessOrFail(t, func() error { - found := false - for _, ep := range ads.GetEndpoints()["outbound|80||foo.com"].Endpoints { - if ep.Priority == 1 { - found = true - } - } - if !found { - return fmt.Errorf("locality did not update") - } - return nil - }, retry.Timeout(time.Second*5)) - - ads.WaitClear() - - ep := makeEndpoint([]*networking.WorkloadEntry{{Address: "1.2.3.6", Locality: "region/zone"}, {Address: "1.2.3.5", Locality: "notmatch"}}) - ep.Spec.(*networking.ServiceEntry).Resolution = networking.ServiceEntry_DNS - if _, err := s.Store().Update(ep); err != nil { - t.Fatal(err) - } - if _, err := ads.Wait(time.Second*5, v3.EndpointType); err != nil { - t.Fatal(err) - } - assertEndpoints(ads) - t.Logf("endpoints: %+v", ads.GetEndpoints()) -} diff --git a/pilot/pkg/xds/adstest.go b/pilot/pkg/xds/adstest.go deleted file mode 100644 index 3cfb23161..000000000 --- a/pilot/pkg/xds/adstest.go +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "context" - "sync" - "time" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - sds "github.com/envoyproxy/go-control-plane/envoy/service/secret/v3" - "google.golang.org/genproto/googleapis/rpc/status" - "google.golang.org/grpc" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -func NewAdsTest(t test.Failer, conn *grpc.ClientConn) *AdsTest { - return NewXdsTest(t, conn, func(conn *grpc.ClientConn) (DiscoveryClient, error) { - xds := discovery.NewAggregatedDiscoveryServiceClient(conn) - return xds.StreamAggregatedResources(context.Background()) - }) -} - -func NewSdsTest(t test.Failer, conn *grpc.ClientConn) *AdsTest { - return NewXdsTest(t, conn, func(conn *grpc.ClientConn) (DiscoveryClient, error) { - xds := sds.NewSecretDiscoveryServiceClient(conn) - return xds.StreamSecrets(context.Background()) - }).WithType(v3.SecretType) -} - -func NewXdsTest(t test.Failer, conn *grpc.ClientConn, getClient func(conn *grpc.ClientConn) (DiscoveryClient, error)) *AdsTest { - ctx, cancel := context.WithCancel(context.Background()) - - cl, err := getClient(conn) - if err != nil { - t.Fatal(err) - } - resp := &AdsTest{ - client: cl, - conn: conn, - context: ctx, - cancelContext: cancel, - ID: "sidecar~1.1.1.1~test.default~default.svc.cluster.local", - timeout: time.Second, - Type: v3.ClusterType, - responses: make(chan *discovery.DiscoveryResponse), - error: make(chan error), - } - t.Cleanup(resp.Cleanup) - - go resp.adsReceiveChannel() - - return resp -} - -type AdsTest struct { - client DiscoveryClient - responses chan *discovery.DiscoveryResponse - error chan error - conn *grpc.ClientConn - metadata model.NodeMetadata - - ID string - Type string - - cancelOnce sync.Once - context context.Context - cancelContext context.CancelFunc - timeout time.Duration -} - -func (a *AdsTest) Cleanup() { - // Place in once to avoid race when two callers attempt to cleanup - a.cancelOnce.Do(func() { - a.cancelContext() - _ = a.client.CloseSend() - if a.conn != nil { - _ = a.conn.Close() - } - }) -} - -func (a *AdsTest) adsReceiveChannel() { - go func() { - <-a.context.Done() - a.Cleanup() - }() - for { - resp, err := a.client.Recv() - if err != nil { - if isUnexpectedError(err) { - log.Warnf("ads received error: %v", err) - } - select { - case a.error <- err: - case <-a.context.Done(): - } - return - } - select { - case a.responses <- resp: - case <-a.context.Done(): - return - } - } -} - -// DrainResponses reads all responses, but does nothing to them -func (a *AdsTest) DrainResponses() { - for { - select { - case <-a.context.Done(): - return - case r := <-a.responses: - log.Infof("drained response %v", r.TypeUrl) - } - } -} - -// ExpectResponse waits until a response is received and returns it -func (a *AdsTest) ExpectResponse(t test.Failer) *discovery.DiscoveryResponse { - t.Helper() - select { - case <-time.After(a.timeout): - t.Fatalf("did not get response in time") - case resp := <-a.responses: - if resp == nil || len(resp.Resources) == 0 { - t.Fatalf("got empty response") - } - return resp - case err := <-a.error: - t.Fatalf("got error: %v", err) - } - return nil -} - -// ExpectError waits until an error is received and returns it -func (a *AdsTest) ExpectError(t test.Failer) error { - t.Helper() - select { - case <-time.After(a.timeout): - t.Fatalf("did not get error in time") - case err := <-a.error: - return err - } - return nil -} - -// ExpectNoResponse waits a short period of time and ensures no response is received -func (a *AdsTest) ExpectNoResponse(t test.Failer) { - t.Helper() - select { - case <-time.After(time.Millisecond * 50): - return - case resp := <-a.responses: - t.Fatalf("got unexpected response: %v", resp) - } -} - -func (a *AdsTest) fillInRequestDefaults(req *discovery.DiscoveryRequest) *discovery.DiscoveryRequest { - if req == nil { - req = &discovery.DiscoveryRequest{} - } - if req.TypeUrl == "" { - req.TypeUrl = a.Type - } - if req.Node == nil { - req.Node = &core.Node{ - Id: a.ID, - Metadata: a.metadata.ToStruct(), - } - } - return req -} - -func (a *AdsTest) Request(t test.Failer, req *discovery.DiscoveryRequest) { - t.Helper() - req = a.fillInRequestDefaults(req) - if err := a.client.Send(req); err != nil { - t.Fatal(err) - } -} - -// RequestResponseAck does a full XDS exchange: Send a request, get a response, and ACK the response -func (a *AdsTest) RequestResponseAck(t test.Failer, req *discovery.DiscoveryRequest) *discovery.DiscoveryResponse { - t.Helper() - req = a.fillInRequestDefaults(req) - a.Request(t, req) - resp := a.ExpectResponse(t) - req.ResponseNonce = resp.Nonce - req.VersionInfo = resp.VersionInfo - a.Request(t, req) - return resp -} - -// RequestResponseAck does a full XDS exchange with an error: Send a request, get a response, and NACK the response -func (a *AdsTest) RequestResponseNack(t test.Failer, req *discovery.DiscoveryRequest) *discovery.DiscoveryResponse { - t.Helper() - if req == nil { - req = &discovery.DiscoveryRequest{} - } - a.Request(t, req) - resp := a.ExpectResponse(t) - req.ResponseNonce = resp.Nonce - req.ErrorDetail = &status.Status{Message: "Test request NACK"} - a.Request(t, req) - return resp -} - -func (a *AdsTest) WithID(id string) *AdsTest { - a.ID = id - return a -} - -func (a *AdsTest) WithType(typeURL string) *AdsTest { - a.Type = typeURL - return a -} - -func (a *AdsTest) WithMetadata(m model.NodeMetadata) *AdsTest { - a.metadata = m - return a -} - -func (a *AdsTest) WithTimeout(t time.Duration) *AdsTest { - a.timeout = t - return a -} diff --git a/pilot/pkg/xds/auth.go b/pilot/pkg/xds/auth.go deleted file mode 100644 index 116c6984a..000000000 --- a/pilot/pkg/xds/auth.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "context" - "errors" - "fmt" - "strings" -) - -import ( - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/peer" - "google.golang.org/grpc/status" - "istio.io/pkg/env" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/spiffe" -) - -var AuthPlaintext = env.RegisterBoolVar("XDS_AUTH_PLAINTEXT", false, - "Authenticate plain text requests - used if Istiod is behind a gateway handling TLS").Get() - -// authenticate authenticates the ADS request using the configured authenticators. -// Returns the validated principals or an error. -// If no authenticators are configured, or if the request is on a non-secure -// stream ( 15010 ) - returns an empty list of principals and no errors. -func (s *DiscoveryServer) authenticate(ctx context.Context) ([]string, error) { - if !features.XDSAuth { - return nil, nil - } - - // Authenticate - currently just checks that request has a certificate signed with the our key. - // Protected by flag to avoid breaking upgrades - should be enabled in multi-cluster/meshexpansion where - // XDS is exposed. - peerInfo, ok := peer.FromContext(ctx) - if !ok { - return nil, errors.New("invalid context") - } - // Not a TLS connection, we will not perform authentication - // TODO: add a flag to prevent unauthenticated requests ( 15010 ) - // request not over TLS on the insecure port - if _, ok := peerInfo.AuthInfo.(credentials.TLSInfo); !ok && !AuthPlaintext { - return nil, nil - } - authFailMsgs := []string{} - for _, authn := range s.Authenticators { - u, err := authn.Authenticate(ctx) - // If one authenticator passes, return - if u != nil && u.Identities != nil && err == nil { - return u.Identities, nil - } - authFailMsgs = append(authFailMsgs, fmt.Sprintf("Authenticator %s: %v", authn.AuthenticatorType(), err)) - } - - log.Errorf("Failed to authenticate client from %s: %s", peerInfo.Addr.String(), strings.Join(authFailMsgs, "; ")) - return nil, errors.New("authentication failure") -} - -func (s *DiscoveryServer) authorize(con *Connection, identities []string) error { - if con == nil || con.proxy == nil { - return nil - } - - if features.EnableXDSIdentityCheck && identities != nil { - // TODO: allow locking down, rejecting unauthenticated requests. - id, err := checkConnectionIdentity(con.proxy, identities) - if err != nil { - log.Warnf("Unauthorized XDS: %v with identity %v: %v", con.peerAddr, identities, err) - return status.Newf(codes.PermissionDenied, "authorization failed: %v", err).Err() - } - con.proxy.VerifiedIdentity = id - } - return nil -} - -func checkConnectionIdentity(proxy *model.Proxy, identities []string) (*spiffe.Identity, error) { - for _, rawID := range identities { - spiffeID, err := spiffe.ParseIdentity(rawID) - if err != nil { - continue - } - if proxy.ConfigNamespace != "" && spiffeID.Namespace != proxy.ConfigNamespace { - continue - } - if proxy.Metadata.ServiceAccount != "" && spiffeID.ServiceAccount != proxy.Metadata.ServiceAccount { - continue - } - return &spiffeID, nil - } - return nil, fmt.Errorf("no identities (%v) matched %v/%v", identities, proxy.ConfigNamespace, proxy.Metadata.ServiceAccount) -} diff --git a/pilot/pkg/xds/bench_test.go b/pilot/pkg/xds/bench_test.go deleted file mode 100644 index f5dde8735..000000000 --- a/pilot/pkg/xds/bench_test.go +++ /dev/null @@ -1,560 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "bytes" - "fmt" - "os" - "path" - "strings" - "testing" - "text/template" - "time" -) - -import ( - "github.com/Masterminds/sprig/v3" - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - any "google.golang.org/protobuf/types/known/anypb" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" - "istio.io/pkg/env" - istiolog "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/crd" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/spiffe" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/yml" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -// ConfigInput defines inputs passed to the test config templates -// This allows tests to do things like create a virtual service for each service, for example -type ConfigInput struct { - // Name of the test - Name string - // Name of the test config file to use. If not set, is used - ConfigName string - // Number of services to make - Services int - // Number of instances to make - Instances int - // Type of proxy to generate configs for - ProxyType model.NodeType -} - -var testCases = []ConfigInput{ - { - // Gateways provides an example config for a large Ingress deployment. This will create N - // virtual services and gateways, where routing is determined by hostname, meaning we generate N routes for HTTPS. - Name: "gateways", - Services: 1000, - ProxyType: model.Router, - }, - { - // Gateways-shared provides an example config for a large Ingress deployment. This will create N - // virtual services and gateways, where routing is determined by path. This means there will be a single large route. - Name: "gateways-shared", - Services: 1000, - ProxyType: model.Router, - }, - { - Name: "empty", - Services: 100, - ProxyType: model.SidecarProxy, - }, - { - Name: "tls", - Services: 100, - }, - { - Name: "telemetry", - Services: 100, - }, - { - Name: "telemetry-api", - Services: 100, - }, - { - Name: "virtualservice", - Services: 100, - }, - { - Name: "authorizationpolicy", - Services: 100, - }, - { - Name: "peerauthentication", - Services: 100, - }, - { - Name: "knative-gateway", - Services: 100, - ProxyType: model.Router, - }, - { - Name: "serviceentry-workloadentry", - Services: 100, - Instances: 1000, - ProxyType: model.SidecarProxy, - }, -} - -var sidecarTestCases = func() (res []ConfigInput) { - for _, c := range testCases { - if c.ProxyType == model.Router { - continue - } - res = append(res, c) - } - return res -}() - -func configureBenchmark(t test.Failer) { - for _, s := range istiolog.Scopes() { - if s.Name() == benchmarkScope.Name() { - continue - } - s.SetOutputLevel(istiolog.NoneLevel) - } - test.SetBoolForTest(t, &features.EnableXDSCaching, false) -} - -func BenchmarkInitPushContext(b *testing.B) { - configureBenchmark(b) - for _, tt := range testCases { - b.Run(tt.Name, func(b *testing.B) { - s, proxy := setupTest(b, tt) - b.ResetTimer() - for n := 0; n < b.N; n++ { - s.Env().PushContext.InitDone.Store(false) - initPushContext(s.Env(), proxy) - } - }) - } -} - -// Do a quick sanity tests to make sure telemetry v2 filters are applying. This ensures as they -// update our benchmark doesn't become useless. -func TestValidateTelemetry(t *testing.T) { - s, proxy := setupAndInitializeTest(t, ConfigInput{Name: "telemetry", Services: 1}) - c, _, _ := s.Discovery.Generators[v3.ClusterType].Generate(proxy, nil, &model.PushRequest{Full: true, Push: s.PushContext()}) - if len(c) == 0 { - t.Fatal("Got no clusters!") - } - for _, r := range c { - cls := &cluster.Cluster{} - if err := r.GetResource().UnmarshalTo(cls); err != nil { - t.Fatal(err) - } - for _, ff := range cls.Filters { - if ff.Name == "istio.metadata_exchange" { - return - } - } - } - t.Fatalf("telemetry v2 filters not found") -} - -func BenchmarkRouteGeneration(b *testing.B) { - runBenchmark(b, v3.RouteType, testCases) -} - -func TestRouteGeneration(t *testing.T) { - testBenchmark(t, v3.RouteType, testCases) -} - -func BenchmarkClusterGeneration(b *testing.B) { - runBenchmark(b, v3.ClusterType, testCases) -} - -func TestClusterGeneration(t *testing.T) { - testBenchmark(t, v3.ClusterType, testCases) -} - -func BenchmarkListenerGeneration(b *testing.B) { - runBenchmark(b, v3.ListenerType, testCases) -} - -func TestListenerGeneration(t *testing.T) { - testBenchmark(t, v3.ListenerType, testCases) -} - -func BenchmarkNameTableGeneration(b *testing.B) { - runBenchmark(b, v3.NameTableType, sidecarTestCases) -} - -func TestNameTableGeneration(t *testing.T) { - testBenchmark(t, v3.NameTableType, sidecarTestCases) -} - -var secretCases = []ConfigInput{ - { - Name: "secrets", - Services: 10, - ProxyType: model.Router, - }, - { - Name: "secrets", - Services: 1000, - ProxyType: model.Router, - }, -} - -func TestSecretGeneration(t *testing.T) { - testBenchmark(t, v3.SecretType, secretCases) -} - -func BenchmarkSecretGeneration(b *testing.B) { - runBenchmark(b, v3.SecretType, secretCases) -} - -func createGateways(n int) map[string]*meshconfig.Network { - out := make(map[string]*meshconfig.Network, n) - for i := 0; i < n; i++ { - out[fmt.Sprintf("network-%d", i)] = &meshconfig.Network{ - Gateways: []*meshconfig.Network_IstioNetworkGateway{{ - Gw: &meshconfig.Network_IstioNetworkGateway_Address{Address: fmt.Sprintf("35.0.0.%d", i)}, - Port: 15443, - }}, - } - } - return out -} - -// BenchmarkEDS measures performance of EDS config generation -// TODO Add more variables, such as different services -func BenchmarkEndpointGeneration(b *testing.B) { - configureBenchmark(b) - - const numNetworks = 4 - tests := []struct { - endpoints int - services int - }{ - {1, 100}, - {10, 10}, - {100, 10}, - {1000, 1}, - } - - var response *discovery.DiscoveryResponse - for _, tt := range tests { - b.Run(fmt.Sprintf("%d/%d", tt.endpoints, tt.services), func(b *testing.B) { - s := NewFakeDiscoveryServer(b, FakeOptions{ - Configs: createEndpoints(tt.endpoints, tt.services, numNetworks), - NetworksWatcher: mesh.NewFixedNetworksWatcher(&meshconfig.MeshNetworks{ - Networks: createGateways(numNetworks), - }), - }) - proxy := &model.Proxy{ - Type: model.SidecarProxy, - IPAddresses: []string{"10.3.3.3"}, - ID: "random", - ConfigNamespace: "default", - Metadata: &model.NodeMetadata{}, - } - push := s.PushContext() - proxy.SetSidecarScope(push) - b.ResetTimer() - for n := 0; n < b.N; n++ { - loadAssignments := make([]*any.Any, 0) - for svc := 0; svc < tt.services; svc++ { - l := s.Discovery.generateEndpoints(NewEndpointBuilder(fmt.Sprintf("outbound|80||foo-%d.com", svc), proxy, push)) - loadAssignments = append(loadAssignments, util.MessageToAny(l)) - } - response = endpointDiscoveryResponse(loadAssignments, version, push.LedgerVersion) - } - logDebug(b, model.AnyToUnnamedResources(response.GetResources())) - }) - } -} - -func runBenchmark(b *testing.B, tpe string, testCases []ConfigInput) { - configureBenchmark(b) - for _, tt := range testCases { - b.Run(tt.Name, func(b *testing.B) { - s, proxy := setupAndInitializeTest(b, tt) - wr := getWatchedResources(tpe, tt, s, proxy) - b.ResetTimer() - var c model.Resources - for n := 0; n < b.N; n++ { - c, _, _ = s.Discovery.Generators[tpe].Generate(proxy, wr, &model.PushRequest{Full: true, Push: s.PushContext()}) - if len(c) == 0 { - b.Fatalf("Got no %v's!", tpe) - } - } - logDebug(b, c) - }) - } -} - -func testBenchmark(t *testing.T, tpe string, testCases []ConfigInput) { - for _, tt := range testCases { - t.Run(tt.Name, func(t *testing.T) { - // No need for large test here - tt.Services = 1 - tt.Instances = 1 - s, proxy := setupAndInitializeTest(t, tt) - wr := getWatchedResources(tpe, tt, s, proxy) - c, _, _ := s.Discovery.Generators[tpe].Generate(proxy, wr, &model.PushRequest{Full: true, Push: s.PushContext()}) - if len(c) == 0 { - t.Fatalf("Got no %v's!", tpe) - } - }) - } -} - -func getWatchedResources(tpe string, tt ConfigInput, s *FakeDiscoveryServer, proxy *model.Proxy) *model.WatchedResource { - switch tpe { - case v3.SecretType: - watchedResources := []string{} - for i := 0; i < tt.Services; i++ { - watchedResources = append(watchedResources, fmt.Sprintf("kubernetes://default/sds-credential-%d", i)) - } - return &model.WatchedResource{ResourceNames: watchedResources} - case v3.RouteType: - l := s.Discovery.ConfigGenerator.BuildListeners(proxy, s.PushContext()) - routeNames := xdstest.ExtractRoutesFromListeners(l) - return &model.WatchedResource{ResourceNames: routeNames} - } - return nil -} - -// Setup test builds a mock test environment. Note: push context is not initialized, to be able to benchmark separately -// most should just call setupAndInitializeTest -func setupTest(t testing.TB, config ConfigInput) (*FakeDiscoveryServer, *model.Proxy) { - proxyType := config.ProxyType - if proxyType == "" { - proxyType = model.SidecarProxy - } - proxy := &model.Proxy{ - Type: proxyType, - IPAddresses: []string{"1.1.1.1"}, - ID: "v0.default", - DNSDomain: "default.example.org", - Metadata: &model.NodeMetadata{ - Namespace: "default", - Labels: map[string]string{ - "istio.io/benchmark": "true", - }, - ClusterID: "Kubernetes", - IstioVersion: "1.14.0", - }, - ConfigNamespace: "default", - VerifiedIdentity: &spiffe.Identity{Namespace: "default"}, - } - proxy.IstioVersion = model.ParseIstioVersion(proxy.Metadata.IstioVersion) - - configs, k8sConfig := getConfigsWithCache(t, config) - s := NewFakeDiscoveryServer(t, FakeOptions{ - Configs: configs, - KubernetesObjectString: k8sConfig, - // Allow debounce to avoid overwhelming with writes - DebounceTime: time.Millisecond * 10, - DisableSecretAuthorization: true, - }) - - return s, proxy -} - -var ( - configCache = map[ConfigInput][]config.Config{} - k8sConfigCache = map[ConfigInput]string{} -) - -func getConfigsWithCache(t testing.TB, input ConfigInput) ([]config.Config, string) { - // Config setup is slow for large tests. Cache this and return from Cache. - // This improves even running a single test, as go will run the full test (including setup) at least twice. - if cached, f := configCache[input]; f { - return cached, k8sConfigCache[input] - } - configName := input.ConfigName - if configName == "" { - configName = input.Name - } - tmpl := template.Must(template.New("").Funcs(sprig.TxtFuncMap()).ParseFiles(path.Join("testdata", "benchmarks", configName+".yaml"))) - var buf bytes.Buffer - if err := tmpl.ExecuteTemplate(&buf, configName+".yaml", input); err != nil { - t.Fatalf("failed to execute template: %v", err) - } - extra := path.Join("testdata", "benchmarks", configName+".extra.yaml") - inputYAML := buf.String() - if _, err := os.Stat(extra); err == nil { - bdata, err := os.ReadFile(extra) - if err != nil { - t.Fatal(err) - } - - inputYAML += "\n---\n" + yml.SplitYamlByKind(string(bdata))[gvk.EnvoyFilter.Kind] - } - - configs, badKinds, err := crd.ParseInputs(inputYAML) - if err != nil { - t.Fatalf("failed to read config: %v", err) - } - scrt, count := parseSecrets(inputYAML) - if len(badKinds) != count { - t.Fatalf("Got unknown resources: %v", badKinds) - } - // setup default namespace if not defined - for i, c := range configs { - if c.Namespace == "" { - c.Namespace = "default" - } - configs[i] = c - } - configCache[input] = configs - k8sConfigCache[input] = scrt - return configs, scrt -} - -func parseSecrets(inputs string) (string, int) { - matches := 0 - sb := strings.Builder{} - for _, text := range strings.Split(inputs, "\n---") { - if strings.Contains(text, "kind: Secret") { - sb.WriteString(text + "\n---\n") - matches++ - } - } - return sb.String(), matches -} - -func setupAndInitializeTest(t testing.TB, config ConfigInput) (*FakeDiscoveryServer, *model.Proxy) { - s, proxy := setupTest(t, config) - initPushContext(s.Env(), proxy) - return s, proxy -} - -func initPushContext(env *model.Environment, proxy *model.Proxy) { - env.PushContext.InitContext(env, nil, nil) - proxy.SetSidecarScope(env.PushContext) - proxy.SetGatewaysForProxy(env.PushContext) - proxy.SetServiceInstances(env.ServiceDiscovery) -} - -var debugGeneration = env.RegisterBoolVar("DEBUG_CONFIG_DUMP", false, "if enabled, print a full config dump of the generated config") - -var benchmarkScope = istiolog.RegisterScope("benchmark", "", 0) - -// Add additional debug info for a test -func logDebug(b *testing.B, m model.Resources) { - b.Helper() - b.StopTimer() - - if debugGeneration.Get() { - for i, r := range m { - s, err := protomarshal.MarshalIndent(r, " ") - if err != nil { - b.Fatal(err) - } - // Cannot use b.Logf, it truncates - benchmarkScope.Infof("Generated: %d %s", i, s) - } - } - bytes := 0 - for _, r := range m { - bytes += len(r.GetResource().Value) - } - b.ReportMetric(float64(bytes)/1000, "kb/msg") - b.ReportMetric(float64(len(m)), "resources/msg") - b.StartTimer() -} - -func createEndpoints(numEndpoints, numServices, numNetworks int) []config.Config { - result := make([]config.Config, 0, numServices) - for s := 0; s < numServices; s++ { - endpoints := make([]*networking.WorkloadEntry, 0, numEndpoints) - for e := 0; e < numEndpoints; e++ { - endpoints = append(endpoints, &networking.WorkloadEntry{ - Address: fmt.Sprintf("111.%d.%d.%d", e/(256*256), (e/256)%256, e%256), - Network: fmt.Sprintf("network-%d", e%numNetworks), - }) - } - result = append(result, config.Config{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Serviceentries.Resource().GroupVersionKind(), - Name: fmt.Sprintf("foo-%d", s), - Namespace: "default", - CreationTimestamp: time.Now(), - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{fmt.Sprintf("foo-%d.com", s)}, - Ports: []*networking.Port{ - {Number: 80, Name: "http-port", Protocol: "http"}, - }, - Endpoints: endpoints, - Resolution: networking.ServiceEntry_STATIC, - }, - }) - } - return result -} - -func BenchmarkPushRequest(b *testing.B) { - // allTriggers contains all triggers, so we can pick one at random. - // It is not a big issue if it falls out of sync, as we are just trying to generate test data - allTriggers := []model.TriggerReason{ - model.EndpointUpdate, - model.ConfigUpdate, - model.ServiceUpdate, - model.ProxyUpdate, - model.GlobalUpdate, - model.UnknownTrigger, - model.DebugTrigger, - model.SecretTrigger, - model.NetworksTrigger, - model.ProxyRequest, - model.NamespaceUpdate, - } - // Number of (simulated) proxies - proxies := 500 - // Number of (simulated) pushes merged - pushesMerged := 10 - // Number of configs per push - configs := 1 - - for n := 0; n < b.N; n++ { - var req *model.PushRequest - for i := 0; i < pushesMerged; i++ { - trigger := allTriggers[i%len(allTriggers)] - nreq := &model.PushRequest{ - ConfigsUpdated: map[model.ConfigKey]struct{}{}, - Reason: []model.TriggerReason{trigger}, - } - for c := 0; c < configs; c++ { - nreq.ConfigsUpdated[model.ConfigKey{Kind: gvk.ServiceEntry, Name: fmt.Sprintf("%d", c), Namespace: "default"}] = struct{}{} - } - req = req.Merge(nreq) - } - for p := 0; p < proxies; p++ { - recordPushTriggers(req.Reason...) - } - } -} diff --git a/pilot/pkg/xds/bootstrapds.go b/pilot/pkg/xds/bootstrapds.go deleted file mode 100644 index efa95da70..000000000 --- a/pilot/pkg/xds/bootstrapds.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "bytes" - "fmt" - "io" -) - -import ( - bootstrapv3 "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "google.golang.org/protobuf/proto" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3/envoyfilter" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/util/runtime" - "github.com/apache/dubbo-go-pixiu/pkg/bootstrap" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -// Bootstrap generator produces an Envoy bootstrap from node descriptors. -type BootstrapGenerator struct { - Server *DiscoveryServer -} - -var _ model.XdsResourceGenerator = &BootstrapGenerator{} - -// Generate returns a bootstrap discovery response. -func (e *BootstrapGenerator) Generate(proxy *model.Proxy, w *model.WatchedResource, req *model.PushRequest) (model.Resources, model.XdsLogDetails, error) { - // The model.Proxy information is incomplete, re-parse the discovery request. - node := bootstrap.ConvertXDSNodeToNode(proxy.XdsNode) - - var buf bytes.Buffer - templateFile := bootstrap.GetEffectiveTemplatePath(node.Metadata.ProxyConfig) - err := bootstrap.New(bootstrap.Config{ - Node: node, - }).WriteTo(templateFile, io.Writer(&buf)) - if err != nil { - return nil, model.DefaultXdsLogDetails, fmt.Errorf("failed to generate bootstrap config: %v", err) - } - - bs := &bootstrapv3.Bootstrap{} - if err = protomarshal.Unmarshal(buf.Bytes(), bs); err != nil { - log.Warnf("failed to unmarshal bootstrap from JSON %q: %v", buf.String(), err) - } - bs = e.applyPatches(bs, proxy, req.Push) - return model.Resources{ - &discovery.Resource{ - Resource: util.MessageToAny(bs), - }, - }, model.DefaultXdsLogDetails, nil -} - -func (e *BootstrapGenerator) applyPatches(bs *bootstrapv3.Bootstrap, proxy *model.Proxy, push *model.PushContext) *bootstrapv3.Bootstrap { - patches := push.EnvoyFilters(proxy) - if patches == nil { - return bs - } - defer runtime.HandleCrash(runtime.LogPanic, func(interface{}) { - envoyfilter.IncrementEnvoyFilterErrorMetric(envoyfilter.Bootstrap) - log.Errorf("bootstrap patch caused panic, so the patches did not take effect") - }) - for _, patch := range patches.Patches[networking.EnvoyFilter_BOOTSTRAP] { - if patch.Operation == networking.EnvoyFilter_Patch_MERGE { - proto.Merge(bs, patch.Value) - envoyfilter.IncrementEnvoyFilterMetric(patch.Key(), envoyfilter.Bootstrap, true) - } else { - envoyfilter.IncrementEnvoyFilterErrorMetric(envoyfilter.Bootstrap) - } - } - return bs -} diff --git a/pilot/pkg/xds/cds.go b/pilot/pkg/xds/cds.go deleted file mode 100644 index 6d7961ca3..000000000 --- a/pilot/pkg/xds/cds.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -type CdsGenerator struct { - Server *DiscoveryServer -} - -var _ model.XdsDeltaResourceGenerator = &CdsGenerator{} - -// Map of all configs that do not impact CDS -var skippedCdsConfigs = map[config.GroupVersionKind]struct{}{ - gvk.Gateway: {}, - gvk.WorkloadEntry: {}, - gvk.WorkloadGroup: {}, - gvk.AuthorizationPolicy: {}, - gvk.RequestAuthentication: {}, - gvk.Secret: {}, - gvk.Telemetry: {}, - gvk.WasmPlugin: {}, - gvk.ProxyConfig: {}, -} - -// Map all configs that impacts CDS for gateways. -var pushCdsGatewayConfig = map[config.GroupVersionKind]struct{}{ - gvk.VirtualService: {}, - gvk.Gateway: {}, -} - -func cdsNeedsPush(req *model.PushRequest, proxy *model.Proxy) bool { - if req == nil { - return true - } - if !req.Full { - // CDS only handles full push - return false - } - // If none set, we will always push - if len(req.ConfigsUpdated) == 0 { - return true - } - for config := range req.ConfigsUpdated { - if proxy.Type == model.Router { - if _, f := pushCdsGatewayConfig[config.Kind]; f { - return true - } - } - - if _, f := skippedCdsConfigs[config.Kind]; !f { - return true - } - } - return false -} - -func (c CdsGenerator) Generate(proxy *model.Proxy, w *model.WatchedResource, req *model.PushRequest) (model.Resources, model.XdsLogDetails, error) { - if !cdsNeedsPush(req, proxy) { - return nil, model.DefaultXdsLogDetails, nil - } - clusters, logs := c.Server.ConfigGenerator.BuildClusters(proxy, req) - return clusters, logs, nil -} - -// GenerateDeltas for CDS currently only builds deltas when services change. todo implement changes for DestinationRule, etc -func (c CdsGenerator) GenerateDeltas(proxy *model.Proxy, req *model.PushRequest, - w *model.WatchedResource) (model.Resources, model.DeletedResources, model.XdsLogDetails, bool, error) { - if !cdsNeedsPush(req, proxy) { - return nil, nil, model.DefaultXdsLogDetails, false, nil - } - updatedClusters, removedClusters, logs, usedDelta := c.Server.ConfigGenerator.BuildDeltaClusters(proxy, req, w) - return updatedClusters, removedClusters, logs, usedDelta, nil -} diff --git a/pilot/pkg/xds/cds_test.go b/pilot/pkg/xds/cds_test.go deleted file mode 100644 index e37b7f0f9..000000000 --- a/pilot/pkg/xds/cds_test.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package xds_test - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" -) - -func TestCDS(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - ads := s.ConnectADS().WithType(v3.ClusterType) - ads.RequestResponseAck(t, nil) -} diff --git a/pilot/pkg/xds/debug.go b/pilot/pkg/xds/debug.go deleted file mode 100644 index 8b6feb384..000000000 --- a/pilot/pkg/xds/debug.go +++ /dev/null @@ -1,1044 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "encoding/json" - "fmt" - "html/template" - "net" - "net/http" - "net/http/pprof" - "sort" - "strings" - "time" -) - -import ( - adminapi "github.com/envoyproxy/go-control-plane/envoy/admin/v3" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - wasm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/wasm/v3" - tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "google.golang.org/protobuf/proto" - any "google.golang.org/protobuf/types/known/anypb" - istiolog "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/crd" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/aggregate" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/xds" - "github.com/apache/dubbo-go-pixiu/pkg/network" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -var indexTmpl = template.Must(template.New("index").Parse(` - -Pilot Debug Console - - - -
- - -{{range .}} - - - -{{end}} -
EndpointDescription
{{.Name}}{{.Help}}
-
- - -`)) - -// AdsClient defines the data that is displayed on "/adsz" endpoint. -type AdsClient struct { - ConnectionID string `json:"connectionId"` - ConnectedAt time.Time `json:"connectedAt"` - PeerAddress string `json:"address"` - Metadata *model.NodeMetadata `json:"metadata,omitempty"` - Watches map[string][]string `json:"watches,omitempty"` -} - -// AdsClients is collection of AdsClient connected to this Istiod. -type AdsClients struct { - Total int `json:"totalClients"` - Connected []AdsClient `json:"clients,omitempty"` -} - -// SyncStatus is the synchronization status between Pilot and a given Envoy -type SyncStatus struct { - ClusterID string `json:"cluster_id,omitempty"` - ProxyID string `json:"proxy,omitempty"` - ProxyVersion string `json:"proxy_version,omitempty"` - IstioVersion string `json:"istio_version,omitempty"` - ClusterSent string `json:"cluster_sent,omitempty"` - ClusterAcked string `json:"cluster_acked,omitempty"` - ListenerSent string `json:"listener_sent,omitempty"` - ListenerAcked string `json:"listener_acked,omitempty"` - RouteSent string `json:"route_sent,omitempty"` - RouteAcked string `json:"route_acked,omitempty"` - EndpointSent string `json:"endpoint_sent,omitempty"` - EndpointAcked string `json:"endpoint_acked,omitempty"` - ExtensionConfigSent string `json:"extensionconfig_sent,omitempty"` - ExtensionConfigAcked string `json:"extensionconfig_acked,omitempty"` -} - -// SyncedVersions shows what resourceVersion of a given resource has been acked by Envoy. -type SyncedVersions struct { - ProxyID string `json:"proxy,omitempty"` - ClusterVersion string `json:"cluster_acked,omitempty"` - ListenerVersion string `json:"listener_acked,omitempty"` - RouteVersion string `json:"route_acked,omitempty"` -} - -// InitDebug initializes the debug handlers and adds a debug in-memory registry. -func (s *DiscoveryServer) InitDebug(mux *http.ServeMux, sctl *aggregate.Controller, enableProfiling bool, - fetchWebhook func() map[string]string) { - // For debugging and load testing v2 we add an memory registry. - s.MemRegistry = memory.NewServiceDiscovery() - s.MemRegistry.EDSUpdater = s - s.MemRegistry.ClusterID = "v2-debug" - - sctl.AddRegistry(serviceregistry.Simple{ - ClusterID: "v2-debug", - ProviderID: provider.Mock, - ServiceDiscovery: s.MemRegistry, - Controller: s.MemRegistry.Controller, - }) - internalMux := http.NewServeMux() - s.AddDebugHandlers(mux, internalMux, enableProfiling, fetchWebhook) - debugGen, ok := (s.Generators[TypeDebug]).(*DebugGen) - if ok { - debugGen.DebugMux = internalMux - } -} - -func (s *DiscoveryServer) AddDebugHandlers(mux, internalMux *http.ServeMux, enableProfiling bool, webhook func() map[string]string) { - // Debug handlers on HTTP ports are added for backward compatibility. - // They will be exposed on XDS-over-TLS in future releases. - if !features.EnableDebugOnHTTP { - return - } - - if enableProfiling { - s.addDebugHandler(mux, internalMux, "/debug/pprof/", "Displays pprof index", pprof.Index) - s.addDebugHandler(mux, internalMux, "/debug/pprof/cmdline", "The command line invocation of the current program", pprof.Cmdline) - s.addDebugHandler(mux, internalMux, "/debug/pprof/profile", "CPU profile", pprof.Profile) - s.addDebugHandler(mux, internalMux, "/debug/pprof/symbol", "Symbol looks up the program counters listed in the request", pprof.Symbol) - s.addDebugHandler(mux, internalMux, "/debug/pprof/trace", "A trace of execution of the current program.", pprof.Trace) - } - - mux.HandleFunc("/debug", s.Debug) - - if features.EnableUnsafeAdminEndpoints { - s.addDebugHandler(mux, internalMux, "/debug/force_disconnect", "Disconnects a proxy from this Pilot", s.forceDisconnect) - } - - s.addDebugHandler(mux, internalMux, "/debug/ecdsz", "Status and debug interface for ECDS", s.ecdsz) - s.addDebugHandler(mux, internalMux, "/debug/edsz", "Status and debug interface for EDS", s.Edsz) - s.addDebugHandler(mux, internalMux, "/debug/ndsz", "Status and debug interface for NDS", s.ndsz) - s.addDebugHandler(mux, internalMux, "/debug/adsz", "Status and debug interface for ADS", s.adsz) - s.addDebugHandler(mux, internalMux, "/debug/adsz?push=true", "Initiates push of the current state to all connected endpoints", s.adsz) - - s.addDebugHandler(mux, internalMux, "/debug/syncz", "Synchronization status of all Envoys connected to this Pilot instance", s.Syncz) - s.addDebugHandler(mux, internalMux, "/debug/config_distribution", "Version status of all Envoys connected to this Pilot instance", s.distributedVersions) - - s.addDebugHandler(mux, internalMux, "/debug/registryz", "Debug support for registry", s.registryz) - s.addDebugHandler(mux, internalMux, "/debug/endpointz", "Debug support for endpoints", s.endpointz) - s.addDebugHandler(mux, internalMux, "/debug/endpointShardz", "Info about the endpoint shards", s.endpointShardz) - s.addDebugHandler(mux, internalMux, "/debug/cachez", "Info about the internal XDS caches", s.cachez) - s.addDebugHandler(mux, internalMux, "/debug/cachez?sizes=true", "Info about the size of the internal XDS caches", s.cachez) - s.addDebugHandler(mux, internalMux, "/debug/cachez?clear=true", "Clear the XDS caches", s.cachez) - s.addDebugHandler(mux, internalMux, "/debug/configz", "Debug support for config", s.configz) - s.addDebugHandler(mux, internalMux, "/debug/sidecarz", "Debug sidecar scope for a proxy", s.sidecarz) - s.addDebugHandler(mux, internalMux, "/debug/resourcesz", "Debug support for watched resources", s.resourcez) - s.addDebugHandler(mux, internalMux, "/debug/instancesz", "Debug support for service instances", s.instancesz) - - s.addDebugHandler(mux, internalMux, "/debug/authorizationz", "Internal authorization policies", s.authorizationz) - s.addDebugHandler(mux, internalMux, "/debug/telemetryz", "Debug Telemetry configuration", s.telemetryz) - s.addDebugHandler(mux, internalMux, "/debug/config_dump", "ConfigDump in the form of the Envoy admin config dump API for passed in proxyID", s.ConfigDump) - s.addDebugHandler(mux, internalMux, "/debug/push_status", "Last PushContext Details", s.pushStatusHandler) - s.addDebugHandler(mux, internalMux, "/debug/pushcontext", "Debug support for current push context", s.pushContextHandler) - s.addDebugHandler(mux, internalMux, "/debug/connections", "Info about the connected XDS clients", s.connectionsHandler) - - s.addDebugHandler(mux, internalMux, "/debug/inject", "Active inject template", s.injectTemplateHandler(webhook)) - s.addDebugHandler(mux, internalMux, "/debug/mesh", "Active mesh config", s.meshHandler) - s.addDebugHandler(mux, internalMux, "/debug/clusterz", "List remote clusters where istiod reads endpoints", s.clusterz) - s.addDebugHandler(mux, internalMux, "/debug/networkz", "List cross-network gateways", s.networkz) - s.addDebugHandler(mux, internalMux, "/debug/mcsz", "List information about Kubernetes MCS services", s.mcsz) - - s.addDebugHandler(mux, internalMux, "/debug/list", "List all supported debug commands in json", s.List) -} - -func (s *DiscoveryServer) addDebugHandler(mux *http.ServeMux, internalMux *http.ServeMux, - path string, help string, handler func(http.ResponseWriter, *http.Request)) { - s.debugHandlers[path] = help - // Add handler without auth. This mux is never exposed on an HTTP server and only used internally - if internalMux != nil { - internalMux.HandleFunc(path, handler) - } - // Add handler with auth; this is expose on an HTTP server - mux.HandleFunc(path, s.allowAuthenticatedOrLocalhost(http.HandlerFunc(handler))) -} - -func (s *DiscoveryServer) allowAuthenticatedOrLocalhost(next http.Handler) http.HandlerFunc { - return func(w http.ResponseWriter, req *http.Request) { - // Request is from localhost, no need to authenticate - if isRequestFromLocalhost(req) { - next.ServeHTTP(w, req) - return - } - // Authenticate request with the same method as XDS - authFailMsgs := make([]string, 0) - var ids []string - for _, authn := range s.Authenticators { - u, err := authn.AuthenticateRequest(req) - // If one authenticator passes, return - if u != nil && u.Identities != nil && err == nil { - ids = u.Identities - break - } - authFailMsgs = append(authFailMsgs, fmt.Sprintf("Authenticator %s: %v", authn.AuthenticatorType(), err)) - } - if ids == nil { - istiolog.Errorf("Failed to authenticate %s %v", req.URL, authFailMsgs) - // Not including detailed info in the response, XDS doesn't either (returns a generic "authentication failure). - w.WriteHeader(http.StatusUnauthorized) - return - } - // TODO: Check that the identity contains dubbo-system namespace, else block or restrict to only info that - // is visible to the authenticated SA. Will require changes in docs and istioctl too. - next.ServeHTTP(w, req) - } -} - -func isRequestFromLocalhost(r *http.Request) bool { - ip, _, err := net.SplitHostPort(r.RemoteAddr) - if err != nil { - return false - } - - userIP := net.ParseIP(ip) - return userIP.IsLoopback() -} - -// Syncz dumps the synchronization status of all Envoys connected to this Pilot instance -func (s *DiscoveryServer) Syncz(w http.ResponseWriter, _ *http.Request) { - syncz := make([]SyncStatus, 0) - for _, con := range s.Clients() { - node := con.proxy - if node != nil { - syncz = append(syncz, SyncStatus{ - ProxyID: node.ID, - ClusterID: node.Metadata.ClusterID.String(), - IstioVersion: node.Metadata.IstioVersion, - ClusterSent: con.NonceSent(v3.ClusterType), - ClusterAcked: con.NonceAcked(v3.ClusterType), - ListenerSent: con.NonceSent(v3.ListenerType), - ListenerAcked: con.NonceAcked(v3.ListenerType), - RouteSent: con.NonceSent(v3.RouteType), - RouteAcked: con.NonceAcked(v3.RouteType), - EndpointSent: con.NonceSent(v3.EndpointType), - EndpointAcked: con.NonceAcked(v3.EndpointType), - ExtensionConfigSent: con.NonceSent(v3.ExtensionConfigurationType), - ExtensionConfigAcked: con.NonceAcked(v3.ExtensionConfigurationType), - }) - } - } - writeJSON(w, syncz) -} - -// registryz providees debug support for registry - adding and listing model items. -// Can be combined with the push debug interface to reproduce changes. -func (s *DiscoveryServer) registryz(w http.ResponseWriter, req *http.Request) { - all := s.Env.ServiceDiscovery.Services() - writeJSON(w, all) -} - -// Dumps info about the endpoint shards, tracked using the new direct interface. -// Legacy registry provides are synced to the new data structure as well, during -// the full push. -func (s *DiscoveryServer) endpointShardz(w http.ResponseWriter, req *http.Request) { - w.Header().Add("Content-Type", "application/json") - out, _ := json.MarshalIndent(s.EndpointIndex.Shardz(), " ", " ") - _, _ = w.Write(out) -} - -func (s *DiscoveryServer) cachez(w http.ResponseWriter, req *http.Request) { - if err := req.ParseForm(); err != nil { - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte("Failed to parse request\n")) - return - } - if req.Form.Get("clear") != "" { - s.Cache.ClearAll() - _, _ = w.Write([]byte("Cache cleared\n")) - return - } - if req.Form.Get("sizes") != "" { - snapshot := s.Cache.Snapshot() - res := make(map[string]string, len(snapshot)) - totalSize := 0 - for _, resource := range snapshot { - if resource == nil { - continue - } - resourceType := resource.Resource.TypeUrl - sz := len(resource.Resource.GetValue()) - res[resourceType] += util.ByteCount(sz) - totalSize += sz - } - res["total"] = util.ByteCount(totalSize) - writeJSON(w, res) - return - } - snapshot := s.Cache.Snapshot() - resources := make(map[string][]string, len(snapshot)) // Key is typeUrl and value is resource names. - for key, resource := range snapshot { - if resource == nil { - continue - } - resourceType := resource.Resource.TypeUrl - resources[resourceType] = append(resources[resourceType], resource.Name+"/"+key) - } - writeJSON(w, resources) -} - -type endpointzResponse struct { - Service string `json:"svc"` - Endpoints []*model.ServiceInstance `json:"ep"` -} - -// Endpoint debugging -func (s *DiscoveryServer) endpointz(w http.ResponseWriter, req *http.Request) { - if _, f := req.URL.Query()["brief"]; f { - svc := s.Env.ServiceDiscovery.Services() - for _, ss := range svc { - for _, p := range ss.Ports { - all := s.Env.ServiceDiscovery.InstancesByPort(ss, p.Port, nil) - for _, svc := range all { - _, _ = fmt.Fprintf(w, "%s:%s %s:%d %v %s\n", ss.Hostname, - p.Name, svc.Endpoint.Address, svc.Endpoint.EndpointPort, svc.Endpoint.Labels, - svc.Endpoint.ServiceAccount) - } - } - } - return - } - - svc := s.Env.ServiceDiscovery.Services() - resp := make([]endpointzResponse, 0) - for _, ss := range svc { - for _, p := range ss.Ports { - all := s.Env.ServiceDiscovery.InstancesByPort(ss, p.Port, nil) - resp = append(resp, endpointzResponse{ - Service: fmt.Sprintf("%s:%s", ss.Hostname, p.Name), - Endpoints: all, - }) - } - } - writeJSON(w, resp) -} - -func (s *DiscoveryServer) distributedVersions(w http.ResponseWriter, req *http.Request) { - if !features.EnableDistributionTracking { - w.WriteHeader(http.StatusConflict) - _, _ = fmt.Fprint(w, "Pilot Version tracking is disabled. Please set the "+ - "PILOT_ENABLE_CONFIG_DISTRIBUTION_TRACKING environment variable to true to enable.") - return - } - if resourceID := req.URL.Query().Get("resource"); resourceID != "" { - proxyNamespace := req.URL.Query().Get("proxy_namespace") - knownVersions := make(map[string]string) - var results []SyncedVersions - for _, con := range s.Clients() { - // wrap this in independent scope so that panic's don't bypass Unlock... - con.proxy.RLock() - - if con.proxy != nil && (proxyNamespace == "" || proxyNamespace == con.proxy.ConfigNamespace) { - // read nonces from our statusreporter to allow for skipped nonces, etc. - results = append(results, SyncedVersions{ - ProxyID: con.proxy.ID, - ClusterVersion: s.getResourceVersion(s.StatusReporter.QueryLastNonce(con.conID, v3.ClusterType), - resourceID, knownVersions), - ListenerVersion: s.getResourceVersion(s.StatusReporter.QueryLastNonce(con.conID, v3.ListenerType), - resourceID, knownVersions), - RouteVersion: s.getResourceVersion(s.StatusReporter.QueryLastNonce(con.conID, v3.RouteType), - resourceID, knownVersions), - }) - } - con.proxy.RUnlock() - } - - writeJSON(w, results) - } else { - w.WriteHeader(http.StatusUnprocessableEntity) - _, _ = fmt.Fprintf(w, "querystring parameter 'resource' is required\n") - } -} - -// VersionLen is the Config Version and is only used as the nonce prefix, but we can reconstruct -// it because is is a b64 encoding of a 64 bit array, which will always be 12 chars in length. -// len = ceil(bitlength/(2^6))+1 -const VersionLen = 12 - -func (s *DiscoveryServer) getResourceVersion(nonce, key string, cache map[string]string) string { - if len(nonce) < VersionLen { - return "" - } - configVersion := nonce[:VersionLen] - result, ok := cache[configVersion] - if !ok { - lookupResult, err := s.Env.GetLedger().GetPreviousValue(configVersion, key) - if err != nil { - istiolog.Errorf("Unable to retrieve resource %s at version %s: %v", key, configVersion, err) - lookupResult = "" - } - // update the cache even on an error, because errors will not resolve themselves, and we don't want to - // repeat the same error for many s.adsClients. - cache[configVersion] = lookupResult - return lookupResult - } - return result -} - -// kubernetesConfig wraps a config.Config with a custom marshaling method that matches a Kubernetes -// object structure. -type kubernetesConfig struct { - config.Config -} - -func (k kubernetesConfig) MarshalJSON() ([]byte, error) { - cfg, err := crd.ConvertConfig(k.Config) - if err != nil { - return nil, err - } - return json.Marshal(cfg) -} - -// Config debugging. -func (s *DiscoveryServer) configz(w http.ResponseWriter, req *http.Request) { - configs := make([]kubernetesConfig, 0) - s.Env.ConfigStore.Schemas().ForEach(func(schema collection.Schema) bool { - cfg, _ := s.Env.ConfigStore.List(schema.Resource().GroupVersionKind(), "") - for _, c := range cfg { - configs = append(configs, kubernetesConfig{c}) - } - return false - }) - writeJSON(w, configs) -} - -// SidecarScope debugging -func (s *DiscoveryServer) sidecarz(w http.ResponseWriter, req *http.Request) { - proxyID, con := s.getDebugConnection(req) - if con == nil { - s.errorHandler(w, proxyID, con) - return - } - writeJSON(w, con.proxy.SidecarScope) -} - -// Resource debugging. -func (s *DiscoveryServer) resourcez(w http.ResponseWriter, _ *http.Request) { - schemas := make([]config.GroupVersionKind, 0) - s.Env.Schemas().ForEach(func(schema collection.Schema) bool { - schemas = append(schemas, schema.Resource().GroupVersionKind()) - return false - }) - - writeJSON(w, schemas) -} - -// AuthorizationDebug holds debug information for authorization policy. -type AuthorizationDebug struct { - AuthorizationPolicies *model.AuthorizationPolicies `json:"authorization_policies"` -} - -// authorizationz dumps the internal authorization policies. -func (s *DiscoveryServer) authorizationz(w http.ResponseWriter, req *http.Request) { - info := AuthorizationDebug{ - AuthorizationPolicies: s.globalPushContext().AuthzPolicies, - } - writeJSON(w, info) -} - -// AuthorizationDebug holds debug information for authorization policy. -type TelemetryDebug struct { - Telemetries *model.Telemetries `json:"telemetries"` -} - -func (s *DiscoveryServer) telemetryz(w http.ResponseWriter, req *http.Request) { - info := TelemetryDebug{ - Telemetries: s.globalPushContext().Telemetry, - } - writeJSON(w, info) -} - -// connectionsHandler implements interface for displaying current connections. -// It is mapped to /debug/connections. -func (s *DiscoveryServer) connectionsHandler(w http.ResponseWriter, req *http.Request) { - adsClients := &AdsClients{} - connections := s.Clients() - adsClients.Total = len(connections) - - for _, c := range connections { - adsClient := AdsClient{ - ConnectionID: c.conID, - ConnectedAt: c.connectedAt, - PeerAddress: c.peerAddr, - } - adsClients.Connected = append(adsClients.Connected, adsClient) - } - writeJSON(w, adsClients) -} - -// adsz implements a status and debug interface for ADS. -// It is mapped to /debug/adsz -func (s *DiscoveryServer) adsz(w http.ResponseWriter, req *http.Request) { - if s.handlePushRequest(w, req) { - return - } - proxyID, con := s.getDebugConnection(req) - if proxyID != "" && con == nil { - // We can't guarantee the Pilot we are connected to has a connection to the proxy we requested - // There isn't a great way around this, but for debugging purposes its suitable to have the caller retry. - w.WriteHeader(http.StatusNotFound) - _, _ = w.Write([]byte("Proxy not connected to this Pilot instance. It may be connected to another instance.\n")) - return - } - var connections []*Connection - if con != nil { - connections = []*Connection{con} - } else { - connections = s.Clients() - } - - adsClients := &AdsClients{} - adsClients.Total = len(connections) - for _, c := range connections { - adsClient := AdsClient{ - ConnectionID: c.conID, - ConnectedAt: c.connectedAt, - PeerAddress: c.peerAddr, - Metadata: c.proxy.Metadata, - Watches: map[string][]string{}, - } - c.proxy.RLock() - for k, wr := range c.proxy.WatchedResources { - r := wr.ResourceNames - if r == nil { - r = []string{} - } - adsClient.Watches[k] = r - } - c.proxy.RUnlock() - adsClients.Connected = append(adsClients.Connected, adsClient) - } - sort.Slice(adsClients.Connected, func(i, j int) bool { - return adsClients.Connected[i].ConnectionID < adsClients.Connected[j].ConnectionID - }) - writeJSON(w, adsClients) -} - -// ecdsz implements a status and debug interface for ECDS. -// It is mapped to /debug/ecdsz -func (s *DiscoveryServer) ecdsz(w http.ResponseWriter, req *http.Request) { - if s.handlePushRequest(w, req) { - return - } - proxyID, con := s.getDebugConnection(req) - if con == nil { - s.errorHandler(w, proxyID, con) - return - } - - if s.Generators[v3.ExtensionConfigurationType] != nil { - r, ok := con.proxy.WatchedResources[v3.ExtensionConfigurationType] - if !ok { - w.WriteHeader(http.StatusNotFound) - _, _ = w.Write([]byte(fmt.Sprintf("no watched ExtensionConfigurationType found, proxyID: %s\n", proxyID))) - return - } - - resource, _, _ := s.Generators[v3.ExtensionConfigurationType].Generate(con.proxy, r, &model.PushRequest{ - Full: true, - Push: con.proxy.LastPushContext, - }) - if len(resource) == 0 { - w.WriteHeader(http.StatusNotFound) - _, _ = w.Write([]byte(fmt.Sprintf("ExtensionConfigurationType not found, proxyID: %s\n", proxyID))) - return - } - - wasmCfgs := make([]interface{}, 0, len(resource)) - for _, rr := range resource { - if w, err := unmarshalToWasm(rr); err != nil { - istiolog.Warnf("failed to unmarshal wasm: %v", err) - } else { - wasmCfgs = append(wasmCfgs, w) - } - } - - writeJSON(w, wasmCfgs) - } -} - -func unmarshalToWasm(r *discovery.Resource) (interface{}, error) { - tce := &core.TypedExtensionConfig{} - if err := r.GetResource().UnmarshalTo(tce); err != nil { - return nil, err - } - - switch tce.TypedConfig.TypeUrl { - case xds.WasmHTTPFilterType: - w := &wasm.Wasm{} - if err := tce.TypedConfig.UnmarshalTo(w); err != nil { - return nil, err - } - // Redact Wasm secret env variable. - vmenvs := w.GetConfig().GetVmConfig().EnvironmentVariables - if vmenvs != nil { - if _, found := vmenvs.KeyValues[model.WasmSecretEnv]; found { - vmenvs.KeyValues[model.WasmSecretEnv] = "" - } - } - return w, nil - } - - return tce, nil -} - -// ConfigDump returns information in the form of the Envoy admin API config dump for the specified proxy -// The dump will only contain dynamic listeners/clusters/routes and can be used to compare what an Envoy instance -// should look like according to Pilot vs what it currently does look like. -func (s *DiscoveryServer) ConfigDump(w http.ResponseWriter, req *http.Request) { - proxyID, con := s.getDebugConnection(req) - if con == nil { - s.errorHandler(w, proxyID, con) - return - } - dump, err := s.configDump(con) - if err != nil { - handleHTTPError(w, err) - return - } - writeJSON(w, dump) -} - -// configDump converts the connection internal state into an Envoy Admin API config dump proto -// It is used in debugging to create a consistent object for comparison between Envoy and Pilot outputs -func (s *DiscoveryServer) configDump(conn *Connection) (*adminapi.ConfigDump, error) { - dynamicActiveClusters := make([]*adminapi.ClustersConfigDump_DynamicCluster, 0) - req := &model.PushRequest{Push: conn.proxy.LastPushContext, Start: time.Now()} - clusters, _ := s.ConfigGenerator.BuildClusters(conn.proxy, req) - - for _, cs := range clusters { - dynamicActiveClusters = append(dynamicActiveClusters, &adminapi.ClustersConfigDump_DynamicCluster{Cluster: cs.Resource}) - } - clustersAny, err := util.MessageToAnyWithError(&adminapi.ClustersConfigDump{ - VersionInfo: versionInfo(), - DynamicActiveClusters: dynamicActiveClusters, - }) - if err != nil { - return nil, err - } - - dynamicActiveListeners := make([]*adminapi.ListenersConfigDump_DynamicListener, 0) - listeners := s.ConfigGenerator.BuildListeners(conn.proxy, req.Push) - for _, cs := range listeners { - listener, err := any.New(cs) - if err != nil { - return nil, err - } - dynamicActiveListeners = append(dynamicActiveListeners, &adminapi.ListenersConfigDump_DynamicListener{ - Name: cs.Name, - ActiveState: &adminapi.ListenersConfigDump_DynamicListenerState{Listener: listener}, - }) - } - listenersAny, err := util.MessageToAnyWithError(&adminapi.ListenersConfigDump{ - VersionInfo: versionInfo(), - DynamicListeners: dynamicActiveListeners, - }) - if err != nil { - return nil, err - } - - routes, _ := s.ConfigGenerator.BuildHTTPRoutes(conn.proxy, req, conn.Routes()) - routeConfigAny := util.MessageToAny(&adminapi.RoutesConfigDump{}) - if len(routes) > 0 { - dynamicRouteConfig := make([]*adminapi.RoutesConfigDump_DynamicRouteConfig, 0) - for _, rs := range routes { - dynamicRouteConfig = append(dynamicRouteConfig, &adminapi.RoutesConfigDump_DynamicRouteConfig{RouteConfig: rs.Resource}) - } - routeConfigAny, err = util.MessageToAnyWithError(&adminapi.RoutesConfigDump{DynamicRouteConfigs: dynamicRouteConfig}) - if err != nil { - return nil, err - } - } - - secretsDump := &adminapi.SecretsConfigDump{} - if s.Generators[v3.SecretType] != nil { - secrets, _, _ := s.Generators[v3.SecretType].Generate(conn.proxy, conn.Watched(v3.SecretType), nil) - if len(secrets) > 0 { - for _, secretAny := range secrets { - secret := &tls.Secret{} - if err := secretAny.GetResource().UnmarshalTo(secret); err != nil { - istiolog.Warnf("failed to unmarshal secret: %v", err) - } - if secret.GetTlsCertificate() != nil { - secret.GetTlsCertificate().PrivateKey = &core.DataSource{ - Specifier: &core.DataSource_InlineBytes{ - InlineBytes: []byte("[redacted]"), - }, - } - } - secretsDump.DynamicActiveSecrets = append(secretsDump.DynamicActiveSecrets, &adminapi.SecretsConfigDump_DynamicSecret{ - Name: secret.Name, - Secret: util.MessageToAny(secret), - }) - } - } - } - - bootstrapAny := util.MessageToAny(&adminapi.BootstrapConfigDump{}) - scopedRoutesAny := util.MessageToAny(&adminapi.ScopedRoutesConfigDump{}) - // The config dump must have all configs with connections specified in - // https://www.envoyproxy.io/docs/envoy/latest/api-v2/admin/v2alpha/config_dump.proto - configDump := &adminapi.ConfigDump{ - Configs: []*any.Any{ - bootstrapAny, - clustersAny, listenersAny, - scopedRoutesAny, - routeConfigAny, - util.MessageToAny(secretsDump), - }, - } - return configDump, nil -} - -// injectTemplateHandler dumps the injection template -// Replaces dumping the template at startup. -func (s *DiscoveryServer) injectTemplateHandler(webhook func() map[string]string) func(http.ResponseWriter, *http.Request) { - return func(w http.ResponseWriter, req *http.Request) { - // TODO: we should split the inject template into smaller modules (separate one for dump core, etc), - // and allow pods to select which patches will be selected. When this happen, this should return - // all inject templates or take a param to select one. - if webhook == nil { - w.WriteHeader(http.StatusNotFound) - return - } - - writeJSON(w, webhook()) - } -} - -// meshHandler dumps the mesh config -func (s *DiscoveryServer) meshHandler(w http.ResponseWriter, r *http.Request) { - writeJSON(w, s.Env.Mesh()) -} - -// pushStatusHandler dumps the last PushContext -func (s *DiscoveryServer) pushStatusHandler(w http.ResponseWriter, req *http.Request) { - model.LastPushMutex.Lock() - defer model.LastPushMutex.Unlock() - if model.LastPushStatus == nil { - return - } - out, err := model.LastPushStatus.StatusJSON() - if err != nil { - handleHTTPError(w, err) - return - } - w.Header().Add("Content-Type", "application/json") - - _, _ = w.Write(out) -} - -// PushContextDebug holds debug information for push context. -type PushContextDebug struct { - AuthorizationPolicies *model.AuthorizationPolicies - NetworkGateways map[network.ID][]model.NetworkGateway -} - -// pushContextHandler dumps the current PushContext -func (s *DiscoveryServer) pushContextHandler(w http.ResponseWriter, _ *http.Request) { - push := PushContextDebug{ - AuthorizationPolicies: s.globalPushContext().AuthzPolicies, - NetworkGateways: s.globalPushContext().NetworkManager().GatewaysByNetwork(), - } - - writeJSON(w, push) -} - -// Debug lists all the supported debug endpoints. -func (s *DiscoveryServer) Debug(w http.ResponseWriter, req *http.Request) { - type debugEndpoint struct { - Name string - Href string - Help string - } - var deps []debugEndpoint - - for k, v := range s.debugHandlers { - deps = append(deps, debugEndpoint{ - Name: k, - Href: k, - Help: v, - }) - } - - sort.Slice(deps, func(i, j int) bool { - return deps[i].Name < deps[j].Name - }) - - if err := indexTmpl.Execute(w, deps); err != nil { - istiolog.Errorf("Error in rendering index template %v", err) - w.WriteHeader(http.StatusInternalServerError) - } -} - -// List all the supported debug commands in json. -func (s *DiscoveryServer) List(w http.ResponseWriter, req *http.Request) { - var cmdNames []string - for k := range s.debugHandlers { - key := strings.Replace(k, "/debug/", "", -1) - // exclude current list command - if key == "list" { - continue - } - // can not support pprof commands - if strings.Contains(key, "pprof") { - continue - } - cmdNames = append(cmdNames, key) - } - sort.Strings(cmdNames) - writeJSON(w, cmdNames) -} - -// ndsz implements a status and debug interface for NDS. -// It is mapped to /debug/ndsz on the monitor port (15014). -func (s *DiscoveryServer) ndsz(w http.ResponseWriter, req *http.Request) { - if s.handlePushRequest(w, req) { - return - } - proxyID, con := s.getDebugConnection(req) - if con == nil { - s.errorHandler(w, proxyID, con) - return - } - if !con.proxy.Metadata.DNSCapture { - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte("DNS capture is not enabled in the proxy\n")) - return - } - - if s.Generators[v3.NameTableType] != nil { - nds, _, _ := s.Generators[v3.NameTableType].Generate(con.proxy, nil, &model.PushRequest{ - Push: con.proxy.LastPushContext, - }) - if len(nds) == 0 { - return - } - writeJSON(w, nds[0]) - } -} - -// Edsz implements a status and debug interface for EDS. -// It is mapped to /debug/edsz on the monitor port (15014). -func (s *DiscoveryServer) Edsz(w http.ResponseWriter, req *http.Request) { - if s.handlePushRequest(w, req) { - return - } - - proxyID, con := s.getDebugConnection(req) - if con == nil { - s.errorHandler(w, proxyID, con) - return - } - - clusters := con.Clusters() - eps := make([]jsonMarshalProto, 0, len(clusters)) - for _, clusterName := range clusters { - eps = append(eps, jsonMarshalProto{s.generateEndpoints(NewEndpointBuilder(clusterName, con.proxy, con.proxy.LastPushContext))}) - } - writeJSON(w, eps) -} - -func (s *DiscoveryServer) forceDisconnect(w http.ResponseWriter, req *http.Request) { - proxyID, con := s.getDebugConnection(req) - if con == nil { - s.errorHandler(w, proxyID, con) - return - } - con.Stop() - _, _ = w.Write([]byte("OK")) -} - -func (s *DiscoveryServer) getProxyConnection(proxyID string) *Connection { - for _, con := range s.Clients() { - if strings.Contains(con.conID, proxyID) { - return con - } - } - - return nil -} - -func (s *DiscoveryServer) instancesz(w http.ResponseWriter, req *http.Request) { - instances := map[string][]*model.ServiceInstance{} - for _, con := range s.Clients() { - con.proxy.RLock() - if con.proxy != nil { - instances[con.proxy.ID] = con.proxy.ServiceInstances - } - con.proxy.RUnlock() - } - writeJSON(w, instances) -} - -func (s *DiscoveryServer) networkz(w http.ResponseWriter, _ *http.Request) { - writeJSON(w, s.Env.NetworkManager.AllGateways()) -} - -func (s *DiscoveryServer) mcsz(w http.ResponseWriter, _ *http.Request) { - svcs := sortMCSServices(s.Env.MCSServices()) - writeJSON(w, svcs) -} - -func sortMCSServices(svcs []model.MCSServiceInfo) []model.MCSServiceInfo { - sort.Slice(svcs, func(i, j int) bool { - if strings.Compare(svcs[i].Cluster.String(), svcs[j].Cluster.String()) < 0 { - return true - } - if strings.Compare(svcs[i].Namespace, svcs[j].Namespace) < 0 { - return true - } - return strings.Compare(svcs[i].Name, svcs[j].Name) < 0 - }) - return svcs -} - -func (s *DiscoveryServer) clusterz(w http.ResponseWriter, _ *http.Request) { - if s.ListRemoteClusters == nil { - w.WriteHeader(400) - return - } - writeJSON(w, s.ListRemoteClusters()) -} - -// handlePushRequest handles a ?push=true query param and triggers a push. -// A boolean response is returned to indicate if the caller should continue -func (s *DiscoveryServer) handlePushRequest(w http.ResponseWriter, req *http.Request) bool { - if err := req.ParseForm(); err != nil { - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte("Failed to parse request\n")) - return true - } - if req.Form.Get("push") != "" { - AdsPushAll(s) - _, _ = fmt.Fprintf(w, "Pushed to %d servers\n", s.adsClientCount()) - return true - } - return false -} - -// getDebugConnection fetches the Connection requested by proxyID -func (s *DiscoveryServer) getDebugConnection(req *http.Request) (string, *Connection) { - if proxyID := req.URL.Query().Get("proxyID"); proxyID != "" { - return proxyID, s.getProxyConnection(proxyID) - } - return "", nil -} - -func (s *DiscoveryServer) errorHandler(w http.ResponseWriter, proxyID string, con *Connection) { - if proxyID == "" { - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte("You must provide a proxyID in the query string\n")) - return - } - if con == nil { - // We can't guarantee the Pilot we are connected to has a connection to the proxy we requested - // There isn't a great way around this, but for debugging purposes its suitable to have the caller retry. - w.WriteHeader(http.StatusNotFound) - _, _ = w.Write([]byte("Proxy not connected to this Pilot instance. It may be connected to another instance.\n")) - return - } -} - -// jsonMarshalProto wraps a proto.Message so it can be marshaled with the standard encoding/json library -type jsonMarshalProto struct { - proto.Message -} - -func (p jsonMarshalProto) MarshalJSON() ([]byte, error) { - return protomarshal.Marshal(p.Message) -} - -// writeJSON writes a json payload, handling content type, marshaling, and errors -func writeJSON(w http.ResponseWriter, obj interface{}) { - w.Header().Set("Content-Type", "application/json") - b, err := config.ToJSON(obj) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - _, _ = w.Write([]byte(err.Error())) - return - } - _, err = w.Write(b) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - } -} - -// handleHTTPError writes an error message to the response -func handleHTTPError(w http.ResponseWriter, err error) { - w.WriteHeader(http.StatusInternalServerError) - _, _ = w.Write([]byte(err.Error())) -} diff --git a/pilot/pkg/xds/debug_test.go b/pilot/pkg/xds/debug_test.go deleted file mode 100644 index 10b6d5db1..000000000 --- a/pilot/pkg/xds/debug_test.go +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds_test - -import ( - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" - "testing" -) - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/pkg/util/configdump" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" -) - -func TestSyncz(t *testing.T) { - t.Run("return the sent and ack status of adsClient connections", func(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - ads := s.ConnectADS() - - ads.RequestResponseAck(t, &discovery.DiscoveryRequest{TypeUrl: v3.ClusterType}) - ads.RequestResponseAck(t, &discovery.DiscoveryRequest{TypeUrl: v3.ListenerType}) - ads.RequestResponseAck(t, &discovery.DiscoveryRequest{ - TypeUrl: v3.EndpointType, - ResourceNames: []string{"outbound|9080||app2.default.svc.cluster.local"}, - }) - ads.RequestResponseAck(t, &discovery.DiscoveryRequest{ - TypeUrl: v3.RouteType, - ResourceNames: []string{"80", "8080"}, - }) - - node, _ := model.ParseServiceNodeWithMetadata(ads.ID, &model.NodeMetadata{}) - verifySyncStatus(t, s.Discovery, node.ID, true, true) - }) - t.Run("sync status not set when Nackd", func(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - ads := s.ConnectADS() - - ads.RequestResponseNack(t, &discovery.DiscoveryRequest{TypeUrl: v3.ClusterType}) - ads.RequestResponseNack(t, &discovery.DiscoveryRequest{TypeUrl: v3.ListenerType}) - ads.RequestResponseNack(t, &discovery.DiscoveryRequest{ - TypeUrl: v3.EndpointType, - ResourceNames: []string{"outbound|9080||app2.default.svc.cluster.local"}, - }) - ads.RequestResponseNack(t, &discovery.DiscoveryRequest{ - TypeUrl: v3.RouteType, - ResourceNames: []string{"80", "8080"}, - }) - node, _ := model.ParseServiceNodeWithMetadata(ads.ID, &model.NodeMetadata{}) - verifySyncStatus(t, s.Discovery, node.ID, true, false) - }) - t.Run("sync ecds", func(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ - ConfigString: mustReadFile(t, "./testdata/ecds.yaml"), - }) - ads := s.ConnectADS() - - ads.RequestResponseNack(t, &discovery.DiscoveryRequest{ - TypeUrl: v3.ExtensionConfigurationType, - ResourceNames: []string{"extension-config"}, - }) - node, _ := model.ParseServiceNodeWithMetadata(ads.ID, &model.NodeMetadata{}) - verifySyncStatus(t, s.Discovery, node.ID, true, true) - }) -} - -func getSyncStatus(t *testing.T, server *xds.DiscoveryServer) []xds.SyncStatus { - req, err := http.NewRequest("GET", "/debug", nil) - if err != nil { - t.Fatal(err) - } - rr := httptest.NewRecorder() - syncz := http.HandlerFunc(server.Syncz) - syncz.ServeHTTP(rr, req) - got := []xds.SyncStatus{} - if err := json.Unmarshal(rr.Body.Bytes(), &got); err != nil { - t.Error(err) - } - return got -} - -func verifySyncStatus(t *testing.T, s *xds.DiscoveryServer, nodeID string, wantSent, wantAcked bool) { - // This is a mostly horrible hack because the single pilot instance is shared across multiple tests - // This makes this test contaminated by others and gives it horrible timing windows - attempts := 5 - for i := 0; i < attempts; i++ { - gotStatus := getSyncStatus(t, s) - var errorHandler func(string, ...interface{}) - if i == attempts-1 { - errorHandler = t.Errorf - } else { - errorHandler = t.Logf - } - for _, ss := range gotStatus { - if ss.ProxyID == nodeID { - if ss.ProxyVersion == "" { - errorHandler("ProxyVersion should always be set for %v", nodeID) - } - if (ss.ClusterSent != "") != wantSent { - errorHandler("wanted ClusterSent set %v got %v for %v", wantSent, ss.ClusterSent, nodeID) - } - if (ss.ClusterAcked != "") != wantAcked { - errorHandler("wanted ClusterAcked set %v got %v for %v", wantAcked, ss.ClusterAcked, nodeID) - } - if (ss.ListenerSent != "") != wantSent { - errorHandler("wanted ListenerSent set %v got %v for %v", wantSent, ss.ListenerSent, nodeID) - } - if (ss.ListenerAcked != "") != wantAcked { - errorHandler("wanted ListenerAcked set %v got %v for %v", wantAcked, ss.ListenerAcked, nodeID) - } - if (ss.RouteSent != "") != wantSent { - errorHandler("wanted RouteSent set %v got %v for %v", wantSent, ss.RouteSent, nodeID) - } - if (ss.RouteAcked != "") != wantAcked { - errorHandler("wanted RouteAcked set %v got %v for %v", wantAcked, ss.RouteAcked, nodeID) - } - if (ss.EndpointSent != "") != wantSent { - errorHandler("wanted EndpointSent set %v got %v for %v", wantSent, ss.EndpointSent, nodeID) - } - if (ss.EndpointAcked != "") != wantAcked { - errorHandler("wanted EndpointAcked set %v got %v for %v", wantAcked, ss.EndpointAcked, nodeID) - } - if (ss.ExtensionConfigSent != "") != wantSent { - errorHandler("wanted ExtesionConfigSent set %v got %v for %v", wantSent, ss.ExtensionConfigSent, nodeID) - } - if (ss.ExtensionConfigAcked != "") != wantAcked { - errorHandler("wanted ExtensionConfigAcked set %v got %v for %v", wantAcked, ss.ExtensionConfigAcked, nodeID) - } - return - } - } - errorHandler("node id %v not found", nodeID) - } -} - -func TestConfigDump(t *testing.T) { - tests := []struct { - name string - wantCode int - proxyID string - }{ - { - name: "dumps most recent proxy with 200", - proxyID: "test.default", - wantCode: 200, - }, - { - name: "returns 404 if proxy not found", - proxyID: "not-found", - wantCode: 404, - }, - { - name: "returns 400 if no proxyID", - proxyID: "", - wantCode: 400, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - ads := s.ConnectADS() - ads.RequestResponseAck(t, &discovery.DiscoveryRequest{TypeUrl: v3.ClusterType}) - ads.RequestResponseAck(t, &discovery.DiscoveryRequest{TypeUrl: v3.ListenerType}) - ads.RequestResponseAck(t, &discovery.DiscoveryRequest{ - TypeUrl: v3.RouteType, - ResourceNames: []string{"80", "8080"}, - }) - - wrapper := getConfigDump(t, s.Discovery, tt.proxyID, tt.wantCode) - if wrapper != nil { - if rs, err := wrapper.GetDynamicRouteDump(false); err != nil || len(rs.DynamicRouteConfigs) == 0 { - t.Errorf("routes were present, must have received an older connection's dump, err: %v", err) - } - } else if tt.wantCode < 400 { - t.Error("expected a non-nil wrapper with successful status code") - } - }) - } -} - -func getConfigDump(t *testing.T, s *xds.DiscoveryServer, proxyID string, wantCode int) *configdump.Wrapper { - path := "/config_dump" - if proxyID != "" { - path += fmt.Sprintf("?proxyID=%v", proxyID) - } - req, err := http.NewRequest("GET", path, nil) - if err != nil { - t.Fatal(err) - } - rr := httptest.NewRecorder() - syncz := http.HandlerFunc(s.ConfigDump) - syncz.ServeHTTP(rr, req) - if rr.Code != wantCode { - t.Errorf("wanted response code %v, got %v", wantCode, rr.Code) - } - if wantCode > 399 { - return nil - } - got := &configdump.Wrapper{} - if err := got.UnmarshalJSON(rr.Body.Bytes()); err != nil { - t.Fatalf(err.Error()) - } - return got -} - -func TestDebugHandlers(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - req, err := http.NewRequest("GET", "/debug", nil) - if err != nil { - t.Fatal(err) - } - rr := httptest.NewRecorder() - debug := http.HandlerFunc(s.Discovery.Debug) - debug.ServeHTTP(rr, req) - if rr.Code != 200 { - t.Errorf("Error in generatating debug endpoint list") - } -} diff --git a/pilot/pkg/xds/debuggen.go b/pilot/pkg/xds/debuggen.go deleted file mode 100644 index 6631dc14e..000000000 --- a/pilot/pkg/xds/debuggen.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - "net/url" - "strconv" -) - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - any "google.golang.org/protobuf/types/known/anypb" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" -) - -const ( - // TypeDebug requests debug info from istio, a secured implementation for istio debug interface - TypeDebug = "istio.io/debug" -) - -var activeNamespaceDebuggers = map[string]struct{}{ - "config_dump": {}, - "ndsz": {}, - "edsz": {}, -} - -// DebugGen is a Generator for istio debug info -type DebugGen struct { - Server *DiscoveryServer - SystemNamespace string - DebugMux *http.ServeMux -} - -type ResponseCapture struct { - body *bytes.Buffer - header map[string]string - wroteHeader bool -} - -func (r ResponseCapture) Header() http.Header { - header := make(http.Header) - for k, v := range r.header { - header.Set(k, v) - } - return header -} - -func (r ResponseCapture) Write(i []byte) (int, error) { - return r.body.Write(i) -} - -func (r ResponseCapture) WriteHeader(statusCode int) { - r.header["statusCode"] = strconv.Itoa(statusCode) -} - -func NewResponseCapture() *ResponseCapture { - return &ResponseCapture{ - header: make(map[string]string), - body: new(bytes.Buffer), - wroteHeader: false, - } -} - -func NewDebugGen(s *DiscoveryServer, systemNamespace string) *DebugGen { - return &DebugGen{ - Server: s, - SystemNamespace: systemNamespace, - } -} - -// Generate XDS debug responses according to the incoming debug request -func (dg *DebugGen) Generate(proxy *model.Proxy, w *model.WatchedResource, req *model.PushRequest) (model.Resources, model.XdsLogDetails, error) { - res := model.Resources{} - var buffer bytes.Buffer - if proxy.VerifiedIdentity == nil { - log.Warnf("proxy %s is not authorized to receive debug. Ensure you are connecting over TLS port and are authenticated.", proxy.ID) - return nil, model.DefaultXdsLogDetails, fmt.Errorf("authentication required") - } - if w.ResourceNames == nil { - return res, model.DefaultXdsLogDetails, fmt.Errorf("debug type is required") - } - if len(w.ResourceNames) != 1 { - return res, model.DefaultXdsLogDetails, fmt.Errorf("only one debug request is allowed") - } - resourceName := w.ResourceNames[0] - u, _ := url.Parse(resourceName) - debugType := u.Path - identity := proxy.VerifiedIdentity - if identity.Namespace != dg.SystemNamespace { - shouldAllow := false - if _, ok := activeNamespaceDebuggers[debugType]; ok { - shouldAllow = true - } - if !shouldAllow { - return res, model.DefaultXdsLogDetails, fmt.Errorf("the debug info is not available for current identity: %q", identity) - } - } - debugURL := "/debug/" + resourceName - hreq, _ := http.NewRequest(http.MethodGet, debugURL, nil) - handler, _ := dg.DebugMux.Handler(hreq) - response := NewResponseCapture() - handler.ServeHTTP(response, hreq) - if response.wroteHeader && len(response.header) >= 1 { - header, _ := json.Marshal(response.header) - buffer.Write(header) - } - buffer.Write(response.body.Bytes()) - res = append(res, &discovery.Resource{ - Name: resourceName, - Resource: &any.Any{ - TypeUrl: TypeDebug, - Value: buffer.Bytes(), - }, - }) - return res, model.DefaultXdsLogDetails, nil -} diff --git a/pilot/pkg/xds/delta.go b/pilot/pkg/xds/delta.go deleted file mode 100644 index d7e4d7edd..000000000 --- a/pilot/pkg/xds/delta.go +++ /dev/null @@ -1,545 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "errors" - "fmt" - "strconv" - "strings" - "time" -) - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/peer" - "google.golang.org/grpc/status" - istiolog "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - istiogrpc "github.com/apache/dubbo-go-pixiu/pilot/pkg/grpc" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -var deltaLog = istiolog.RegisterScope("delta", "delta xds debugging", 0) - -func (s *DiscoveryServer) StreamDeltas(stream DeltaDiscoveryStream) error { - if knativeEnv != "" && firstRequest.Load() { - // How scaling works in knative is the first request is the "loading" request. During - // loading request, concurrency=1. Once that request is done, concurrency is enabled. - // However, the XDS stream is long lived, so the first request would block all others. As a - // result, we should exit the first request immediately; clients will retry. - firstRequest.Store(false) - return status.Error(codes.Unavailable, "server warmup not complete; try again") - } - // Check if server is ready to accept clients and process new requests. - // Currently ready means caches have been synced and hence can build - // clusters correctly. Without this check, InitContext() call below would - // initialize with empty config, leading to reconnected Envoys loosing - // configuration. This is an additional safety check inaddition to adding - // cachesSynced logic to readiness probe to handle cases where kube-proxy - // ip tables update latencies. - // See https://github.com/istio/istio/issues/25495. - if !s.IsServerReady() { - return errors.New("server is not ready to serve discovery information") - } - - ctx := stream.Context() - peerAddr := "0.0.0.0" - if peerInfo, ok := peer.FromContext(ctx); ok { - peerAddr = peerInfo.Addr.String() - } - - if err := s.WaitForRequestLimit(stream.Context()); err != nil { - deltaLog.Warnf("ADS: %q exceeded rate limit: %v", peerAddr, err) - return status.Errorf(codes.ResourceExhausted, "request rate limit exceeded: %v", err) - } - - ids, err := s.authenticate(ctx) - if err != nil { - return status.Error(codes.Unauthenticated, err.Error()) - } - if ids != nil { - deltaLog.Debugf("Authenticated XDS: %v with identity %v", peerAddr, ids) - } else { - deltaLog.Debugf("Unauthenticated XDS: %v", peerAddr) - } - - // InitContext returns immediately if the context was already initialized. - if err = s.globalPushContext().InitContext(s.Env, nil, nil); err != nil { - // Error accessing the data - log and close, maybe a different pilot replica - // has more luck - deltaLog.Warnf("Error reading config %v", err) - return status.Error(codes.Unavailable, "error reading config") - } - con := newDeltaConnection(peerAddr, stream) - - // Do not call: defer close(con.pushChannel). The push channel will be garbage collected - // when the connection is no longer used. Closing the channel can cause subtle race conditions - // with push. According to the spec: "It's only necessary to close a channel when it is important - // to tell the receiving goroutines that all data have been sent." - - // Block until either a request is received or a push is triggered. - // We need 2 go routines because 'read' blocks in Recv(). - go s.receiveDelta(con, ids) - - // Wait for the proxy to be fully initialized before we start serving traffic. Because - // initialization doesn't have dependencies that will block, there is no need to add any timeout - // here. Prior to this explicit wait, we were implicitly waiting by receive() not sending to - // reqChannel and the connection not being enqueued for pushes to pushChannel until the - // initialization is complete. - <-con.initialized - - for { - select { - case req, ok := <-con.deltaReqChan: - if ok { - deltaLog.Debugf("ADS: got Delta Request for: %s", req.TypeUrl) - if err := s.processDeltaRequest(req, con); err != nil { - return err - } - } else { - // Remote side closed connection or error processing the request. - return <-con.errorChan - } - case pushEv := <-con.pushChannel: - err := s.pushConnectionDelta(con, pushEv) - pushEv.done() - if err != nil { - return err - } - case <-con.stop: - return nil - } - } -} - -// Compute and send the new configuration for a connection. This is blocking and may be slow -// for large configs. The method will hold a lock on con.pushMutex. -func (s *DiscoveryServer) pushConnectionDelta(con *Connection, pushEv *Event) error { - pushRequest := pushEv.pushRequest - - if pushRequest.Full { - // Update Proxy with current information. - s.updateProxy(con.proxy, pushRequest) - } - - if !s.ProxyNeedsPush(con.proxy, pushRequest) { - deltaLog.Debugf("Skipping push to %v, no updates required", con.conID) - if pushRequest.Full { - // Only report for full versions, incremental pushes do not have a new version - reportAllEvents(s.StatusReporter, con.conID, pushRequest.Push.LedgerVersion, nil) - } - return nil - } - - // Send pushes to all generators - // Each Generator is responsible for determining if the push event requires a push - wrl, ignoreEvents := con.pushDetails() - for _, w := range wrl { - if err := s.pushDeltaXds(con, w, pushRequest); err != nil { - return err - } - } - if pushRequest.Full { - // Report all events for unwatched resources. Watched resources will be reported in pushXds or on ack. - reportAllEvents(s.StatusReporter, con.conID, pushRequest.Push.LedgerVersion, ignoreEvents) - } - - proxiesConvergeDelay.Record(time.Since(pushRequest.Start).Seconds()) - return nil -} - -func (s *DiscoveryServer) receiveDelta(con *Connection, identities []string) { - defer func() { - close(con.deltaReqChan) - close(con.errorChan) - // Close the initialized channel, if its not already closed, to prevent blocking the stream - select { - case <-con.initialized: - default: - close(con.initialized) - } - }() - firstRequest := true - for { - req, err := con.deltaStream.Recv() - if err != nil { - if istiogrpc.IsExpectedGRPCError(err) { - deltaLog.Infof("ADS: %q %s terminated", con.peerAddr, con.conID) - return - } - con.errorChan <- err - deltaLog.Errorf("ADS: %q %s terminated with error: %v", con.peerAddr, con.conID, err) - totalXDSInternalErrors.Increment() - return - } - // This should be only set for the first request. The node id may not be set - for example malicious clients. - if firstRequest { - firstRequest = false - if req.Node == nil || req.Node.Id == "" { - con.errorChan <- status.New(codes.InvalidArgument, "missing node information").Err() - return - } - if err := s.initConnection(req.Node, con, identities); err != nil { - con.errorChan <- err - return - } - defer s.closeConnection(con) - deltaLog.Infof("ADS: new delta connection for node:%s", con.conID) - } - - select { - case con.deltaReqChan <- req: - case <-con.deltaStream.Context().Done(): - deltaLog.Infof("ADS: %q %s terminated with stream closed", con.peerAddr, con.conID) - return - } - } -} - -func (conn *Connection) sendDelta(res *discovery.DeltaDiscoveryResponse) error { - sendHandler := func() error { - start := time.Now() - defer func() { recordSendTime(time.Since(start)) }() - return conn.deltaStream.Send(res) - } - err := istiogrpc.Send(conn.deltaStream.Context(), sendHandler) - if err == nil { - sz := 0 - for _, rc := range res.Resources { - sz += len(rc.Resource.Value) - } - if res.Nonce != "" && !strings.HasPrefix(res.TypeUrl, v3.DebugType) { - conn.proxy.Lock() - if conn.proxy.WatchedResources[res.TypeUrl] == nil { - conn.proxy.WatchedResources[res.TypeUrl] = &model.WatchedResource{TypeUrl: res.TypeUrl} - } - conn.proxy.WatchedResources[res.TypeUrl].NonceSent = res.Nonce - conn.proxy.WatchedResources[res.TypeUrl].VersionSent = res.SystemVersionInfo - conn.proxy.WatchedResources[res.TypeUrl].LastSent = time.Now() - if features.EnableUnsafeDeltaTest { - conn.proxy.WatchedResources[res.TypeUrl].LastResources = applyDelta(conn.proxy.WatchedResources[res.TypeUrl].LastResources, res) - } - conn.proxy.Unlock() - } - } else { - deltaLog.Infof("Timeout writing %s", conn.conID) - xdsResponseWriteTimeouts.Increment() - } - return err -} - -// processDeltaRequest is handling one request. This is currently called from the 'main' thread, which also -// handles 'push' requests and close - the code will eventually call the 'push' code, and it needs more mutex -// protection. Original code avoided the mutexes by doing both 'push' and 'process requests' in same thread. -func (s *DiscoveryServer) processDeltaRequest(req *discovery.DeltaDiscoveryRequest, con *Connection) error { - if req.TypeUrl == v3.HealthInfoType { - s.handleWorkloadHealthcheck(con.proxy, deltaToSotwRequest(req)) - return nil - } - if strings.HasPrefix(req.TypeUrl, v3.DebugType) { - return s.pushXds(con, - &model.WatchedResource{TypeUrl: req.TypeUrl, ResourceNames: req.ResourceNamesSubscribe}, - &model.PushRequest{Full: true, Push: con.proxy.LastPushContext}) - } - if s.StatusReporter != nil { - s.StatusReporter.RegisterEvent(con.conID, req.TypeUrl, req.ResponseNonce) - } - shouldRespond := s.shouldRespondDelta(con, req) - if !shouldRespond { - return nil - } - - request := &model.PushRequest{ - Full: true, - Push: con.proxy.LastPushContext, - Reason: []model.TriggerReason{model.ProxyRequest}, - - // The usage of LastPushTime (rather than time.Now()), is critical here for correctness; This time - // is used by the XDS cache to determine if a entry is stale. If we use Now() with an old push context, - // we may end up overriding active cache entries with stale ones. - Start: con.proxy.LastPushTime, - Delta: model.ResourceDelta{ - Subscribed: sets.New(req.ResourceNamesSubscribe...), - Unsubscribed: sets.New(req.ResourceNamesUnsubscribe...), - }, - } - // SidecarScope for the proxy may has not been updated based on this pushContext. - // It can happen when `processRequest` comes after push context has been updated(s.initPushContext), - // but before proxy's SidecarScope has been updated(s.updateProxy). - if con.proxy.SidecarScope != nil && con.proxy.SidecarScope.Version != request.Push.PushVersion { - s.computeProxyState(con.proxy, request) - } - return s.pushDeltaXds(con, con.Watched(req.TypeUrl), request) -} - -// shouldRespondDelta determines whether this request needs to be responded back. It applies the ack/nack rules as per xds protocol -// using WatchedResource for previous state and discovery request for the current state. -func (s *DiscoveryServer) shouldRespondDelta(con *Connection, request *discovery.DeltaDiscoveryRequest) bool { - stype := v3.GetShortType(request.TypeUrl) - - // If there is an error in request that means previous response is erroneous. - // We do not have to respond in that case. In this case request's version info - // will be different from the version sent. But it is fragile to rely on that. - if request.ErrorDetail != nil { - errCode := codes.Code(request.ErrorDetail.Code) - deltaLog.Warnf("ADS:%s: ACK ERROR %s %s:%s", stype, con.conID, errCode.String(), request.ErrorDetail.GetMessage()) - incrementXDSRejects(request.TypeUrl, con.proxy.ID, errCode.String()) - if s.StatusGen != nil { - s.StatusGen.OnNack(con.proxy, deltaToSotwRequest(request)) - } - con.proxy.Lock() - if w, f := con.proxy.WatchedResources[request.TypeUrl]; f { - w.NonceNacked = request.ResponseNonce - } - con.proxy.Unlock() - return false - } - - con.proxy.RLock() - previousInfo := con.proxy.WatchedResources[request.TypeUrl] - con.proxy.RUnlock() - - // This can happen in two cases: - // 1. Envoy initially send request to Istiod - // 2. Envoy reconnect to Istiod i.e. Istiod does not have - // information about this typeUrl, but Envoy sends response nonce - either - // because Istiod is restarted or Envoy disconnects and reconnects. - // We should always respond with the current resource names. - if previousInfo == nil { - // TODO: can we distinguish init and reconnect? Do we care? - deltaLog.Debugf("ADS:%s: INIT/RECONNECT %s %s", stype, con.conID, request.ResponseNonce) - con.proxy.Lock() - con.proxy.WatchedResources[request.TypeUrl] = &model.WatchedResource{ - TypeUrl: request.TypeUrl, - ResourceNames: deltaWatchedResources(nil, request), - } - con.proxy.Unlock() - return true - } - - // If there is mismatch in the nonce, that is a case of expired/stale nonce. - // A nonce becomes stale following a newer nonce being sent to Envoy. - // TODO: due to concurrent unsubscribe, this probably doesn't make sense. Do we need any logic here? - if request.ResponseNonce != "" && request.ResponseNonce != previousInfo.NonceSent { - deltaLog.Debugf("ADS:%s: REQ %s Expired nonce received %s, sent %s", stype, - con.conID, request.ResponseNonce, previousInfo.NonceSent) - xdsExpiredNonce.With(typeTag.Value(v3.GetMetricType(request.TypeUrl))).Increment() - con.proxy.Lock() - con.proxy.WatchedResources[request.TypeUrl].NonceNacked = "" - con.proxy.Unlock() - return false - } - - // If it comes here, that means nonce match. This an ACK. We should record - // the ack details and respond if there is a change in resource names. - con.proxy.Lock() - previousResources := con.proxy.WatchedResources[request.TypeUrl].ResourceNames - deltaResources := deltaWatchedResources(previousResources, request) - con.proxy.WatchedResources[request.TypeUrl].NonceAcked = request.ResponseNonce - con.proxy.WatchedResources[request.TypeUrl].NonceNacked = "" - con.proxy.WatchedResources[request.TypeUrl].ResourceNames = deltaResources - con.proxy.Unlock() - - oldAck := listEqualUnordered(previousResources, deltaResources) - // Spontaneous DeltaDiscoveryRequests from the client. - // This can be done to dynamically add or remove elements from the tracked resource_names set. - // In this case response_nonce is empty. - newAck := request.ResponseNonce != "" - if newAck != oldAck { - // Not sure which is better, lets just log if they don't match for now and compare. - deltaLog.Errorf("ADS:%s: New ACK and old ACK check mismatch: %v vs %v", stype, newAck, oldAck) - if features.EnableUnsafeAssertions { - panic(fmt.Sprintf("ADS:%s: New ACK and old ACK check mismatch: %v vs %v", stype, newAck, oldAck)) - } - } - // Envoy can send two DiscoveryRequests with same version and nonce - // when it detects a new resource. We should respond if they change. - if oldAck { - deltaLog.Debugf("ADS:%s: ACK %s %s", stype, con.conID, request.ResponseNonce) - return false - } - deltaLog.Debugf("ADS:%s: RESOURCE CHANGE previous resources: %v, new resources: %v %s %s", stype, - previousResources, deltaResources, con.conID, request.ResponseNonce) - - return true -} - -// Push an Delta XDS resource for the given connection. Configuration will be generated -// based on the passed in generator. -func (s *DiscoveryServer) pushDeltaXds(con *Connection, - w *model.WatchedResource, req *model.PushRequest) error { - if w == nil { - return nil - } - gen := s.findGenerator(w.TypeUrl, con) - if gen == nil { - return nil - } - t0 := time.Now() - - originalW := w - // If delta is set, client is requesting new resources or removing old ones. We should just generate the - // new resources it needs, rather than the entire set of known resources. - // Note: we do not need to account for unsubscribed resources as these are handled by parent removal; - // See https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol#deleting-resources. - // This means if there are only removals, we will not respond. - var logFiltered string - if !req.Delta.IsEmpty() { - logFiltered = " filtered:" + strconv.Itoa(len(w.ResourceNames)-len(req.Delta.Subscribed)) - w = &model.WatchedResource{ - TypeUrl: w.TypeUrl, - ResourceNames: req.Delta.Subscribed.UnsortedList(), - } - } - - var res model.Resources - var deletedRes model.DeletedResources - var logdata model.XdsLogDetails - var usedDelta bool - var err error - switch g := gen.(type) { - case model.XdsDeltaResourceGenerator: - res, deletedRes, logdata, usedDelta, err = g.GenerateDeltas(con.proxy, req, w) - if features.EnableUnsafeDeltaTest { - fullRes, _, _ := g.Generate(con.proxy, originalW, req) - s.compareDiff(con, originalW, fullRes, res, deletedRes, usedDelta, req.Delta) - } - case model.XdsResourceGenerator: - res, logdata, err = g.Generate(con.proxy, w, req) - } - if err != nil || (res == nil && deletedRes == nil) { - // If we have nothing to send, report that we got an ACK for this version. - if s.StatusReporter != nil { - s.StatusReporter.RegisterEvent(con.conID, w.TypeUrl, req.Push.LedgerVersion) - } - return err - } - defer func() { recordPushTime(w.TypeUrl, time.Since(t0)) }() - resp := &discovery.DeltaDiscoveryResponse{ - ControlPlane: ControlPlane(), - TypeUrl: w.TypeUrl, - // TODO: send different version for incremental eds - SystemVersionInfo: req.Push.PushVersion, - Nonce: nonce(req.Push.LedgerVersion), - Resources: res, - } - currentResources := extractNames(res) - if usedDelta { - resp.RemovedResources = deletedRes - } else if req.Full { - // similar to sotw - subscribed := sets.New(w.ResourceNames...) - subscribed.DeleteAll(currentResources...) - resp.RemovedResources = subscribed.SortedList() - } - if len(resp.RemovedResources) > 0 { - deltaLog.Debugf("ADS:%v %s REMOVE %v", v3.GetShortType(w.TypeUrl), con.conID, resp.RemovedResources) - } - // normally wildcard xds `subscribe` is always nil, just in case there are some extended type not handled correctly. - if req.Delta.Subscribed == nil && isWildcardTypeURL(w.TypeUrl) { - // this is probably a bad idea... - con.proxy.Lock() - w.ResourceNames = currentResources - con.proxy.Unlock() - } - - configSize := ResourceSize(res) - configSizeBytes.With(typeTag.Value(w.TypeUrl)).Record(float64(configSize)) - - ptype := "PUSH" - info := "" - if logdata.Incremental { - ptype = "PUSH INC" - } - if len(logdata.AdditionalInfo) > 0 { - info = " " + logdata.AdditionalInfo - } - if len(logFiltered) > 0 { - info += logFiltered - } - - if err := con.sendDelta(resp); err != nil { - if recordSendError(w.TypeUrl, err) { - deltaLog.Warnf("%s: Send failure for node:%s resources:%d size:%s%s: %v", - v3.GetShortType(w.TypeUrl), con.proxy.ID, len(res), util.ByteCount(configSize), info, err) - } - return err - } - - switch { - case logdata.Incremental: - if deltaLog.DebugEnabled() { - deltaLog.Debugf("%s: %s%s for node:%s resources:%d size:%s%s", - v3.GetShortType(w.TypeUrl), ptype, req.PushReason(), con.proxy.ID, len(res), util.ByteCount(configSize), info) - } - default: - debug := "" - if deltaLog.DebugEnabled() { - // Add additional information to logs when debug mode enabled. - debug = " nonce:" + resp.Nonce + " version:" + resp.SystemVersionInfo - } - deltaLog.Infof("%s: %s%s for node:%s resources:%d size:%v%s%s", v3.GetShortType(w.TypeUrl), ptype, req.PushReason(), con.proxy.ID, len(res), - util.ByteCount(ResourceSize(res)), info, debug) - } - - return nil -} - -func newDeltaConnection(peerAddr string, stream DeltaDiscoveryStream) *Connection { - return &Connection{ - pushChannel: make(chan *Event), - initialized: make(chan struct{}), - stop: make(chan struct{}), - peerAddr: peerAddr, - connectedAt: time.Now(), - deltaStream: stream, - deltaReqChan: make(chan *discovery.DeltaDiscoveryRequest, 1), - errorChan: make(chan error, 1), - } -} - -// To satisfy methods that need DiscoveryRequest. Not suitable for real usage -func deltaToSotwRequest(request *discovery.DeltaDiscoveryRequest) *discovery.DiscoveryRequest { - return &discovery.DiscoveryRequest{ - Node: request.Node, - ResourceNames: request.ResourceNamesSubscribe, - TypeUrl: request.TypeUrl, - ResponseNonce: request.ResponseNonce, - ErrorDetail: request.ErrorDetail, - } -} - -func deltaWatchedResources(existing []string, request *discovery.DeltaDiscoveryRequest) []string { - res := sets.New(existing...) - res.InsertAll(request.ResourceNamesSubscribe...) - res.DeleteAll(request.ResourceNamesUnsubscribe...) - return res.SortedList() -} - -func extractNames(res []*discovery.Resource) []string { - names := []string{} - for _, r := range res { - names = append(names, r.Name) - } - return names -} diff --git a/pilot/pkg/xds/delta_test.go b/pilot/pkg/xds/delta_test.go deleted file mode 100644 index 66cda8155..000000000 --- a/pilot/pkg/xds/delta_test.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds_test - -import ( - "reflect" - "testing" -) - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -func TestDeltaAds(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - ads := s.ConnectDeltaADS().WithType(v3.ClusterType) - ads.RequestResponseAck(nil) -} - -func TestDeltaAdsClusterUpdate(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - ads := s.ConnectDeltaADS().WithType(v3.EndpointType) - nonce := "" - sendEDSReqAndVerify := func(add, remove, expect []string) { - t.Helper() - res := ads.RequestResponseAck(&discovery.DeltaDiscoveryRequest{ - ResponseNonce: nonce, - ResourceNamesSubscribe: add, - ResourceNamesUnsubscribe: remove, - }) - nonce = res.Nonce - got := xdstest.MapKeys(xdstest.ExtractLoadAssignments(xdstest.UnmarshalClusterLoadAssignment(t, model.ResourcesToAny(res.Resources)))) - if !reflect.DeepEqual(expect, got) { - t.Fatalf("expected clusters %v got %v", expect, got) - } - } - - sendEDSReqAndVerify([]string{"outbound|80||local.default.svc.cluster.local"}, nil, []string{"outbound|80||local.default.svc.cluster.local"}) - // Only send the one that is requested - sendEDSReqAndVerify([]string{"outbound|81||local.default.svc.cluster.local"}, nil, []string{"outbound|81||local.default.svc.cluster.local"}) - ads.Request(&discovery.DeltaDiscoveryRequest{ - ResponseNonce: nonce, - ResourceNamesUnsubscribe: []string{"outbound|81||local.default.svc.cluster.local"}, - }) - ads.ExpectNoResponse() -} - -func TestDeltaEDS(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ - ConfigString: mustReadFile(t, "tests/testdata/config/destination-rule-locality.yaml"), - DiscoveryServerModifier: func(s *xds.DiscoveryServer) { - addTestClientEndpoints(s) - s.MemRegistry.AddHTTPService(edsIncSvc, edsIncVip, 8080) - s.MemRegistry.SetEndpoints(edsIncSvc, "", - newEndpointWithAccount("127.0.0.1", "hello-sa", "v1")) - }, - }) - - ads := s.ConnectDeltaADS().WithType(v3.EndpointType) - ads.Request(&discovery.DeltaDiscoveryRequest{ - ResourceNamesSubscribe: []string{"outbound|80||test-1.default"}, - }) - resp := ads.ExpectResponse() - if len(resp.Resources) != 1 || resp.Resources[0].Name != "outbound|80||test-1.default" { - t.Fatalf("received unexpected eds resource %v", resp.Resources) - } - if len(resp.RemovedResources) != 0 { - t.Fatalf("received unexpected removed eds resource %v", resp.RemovedResources) - } - - ads.Request(&discovery.DeltaDiscoveryRequest{ - ResourceNamesSubscribe: []string{"outbound|8080||" + edsIncSvc}, - }) - resp = ads.ExpectResponse() - if len(resp.Resources) != 1 || resp.Resources[0].Name != "outbound|8080||"+edsIncSvc { - t.Fatalf("received unexpected eds resource %v", resp.Resources) - } - if len(resp.RemovedResources) != 0 { - t.Fatalf("received unexpected removed eds resource %v", resp.RemovedResources) - } - - // update endpoint - s.MemRegistry.SetEndpoints(edsIncSvc, "", - newEndpointWithAccount("127.0.0.2", "hello-sa", "v1")) - resp = ads.ExpectResponse() - if len(resp.Resources) != 1 || resp.Resources[0].Name != "outbound|8080||"+edsIncSvc { - t.Fatalf("received unexpected eds resource %v", resp.Resources) - } - if len(resp.RemovedResources) != 0 { - t.Fatalf("received unexpected removed eds resource %v", resp.RemovedResources) - } - - // update svc, only send the eds for this service - s.Discovery.MemRegistry.AddHTTPService(edsIncSvc, "10.10.1.3", 8080) - s.Discovery.ConfigUpdate(&model.PushRequest{Full: true, ConfigsUpdated: map[model.ConfigKey]struct{}{{ - Kind: gvk.ServiceEntry, - Name: edsIncSvc, - Namespace: "", - }: {}}}) - - resp = ads.ExpectResponse() - if len(resp.Resources) != 1 || resp.Resources[0].Name != "outbound|8080||"+edsIncSvc { - t.Fatalf("received unexpected eds resource %v", resp.Resources) - } - if len(resp.RemovedResources) != 0 { - t.Fatalf("received unexpected removed eds resource %v", resp.RemovedResources) - } - - // delete svc, only send eds fot this service - s.Discovery.MemRegistry.RemoveService(edsIncSvc) - s.Discovery.ConfigUpdate(&model.PushRequest{Full: true, ConfigsUpdated: map[model.ConfigKey]struct{}{{ - Kind: gvk.ServiceEntry, - Name: edsIncSvc, - Namespace: "", - }: {}}}) - - resp = ads.ExpectResponse() - if len(resp.RemovedResources) != 1 || resp.RemovedResources[0] != "outbound|8080||"+edsIncSvc { - t.Fatalf("received unexpected removed eds resource %v", resp.RemovedResources) - } - if len(resp.Resources) != 0 { - t.Fatalf("received unexpected eds resource %v", resp.Resources) - } -} diff --git a/pilot/pkg/xds/deltaadstest.go b/pilot/pkg/xds/deltaadstest.go deleted file mode 100644 index 352584fc6..000000000 --- a/pilot/pkg/xds/deltaadstest.go +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "context" - "sync" - "time" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "google.golang.org/genproto/googleapis/rpc/status" - "google.golang.org/grpc" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -func NewDeltaAdsTest(t test.Failer, conn *grpc.ClientConn) *DeltaAdsTest { - test.SetBoolForTest(t, &features.DeltaXds, true) - return NewDeltaXdsTest(t, conn, func(conn *grpc.ClientConn) (DeltaDiscoveryClient, error) { - xds := discovery.NewAggregatedDiscoveryServiceClient(conn) - return xds.DeltaAggregatedResources(context.Background()) - }) -} - -func NewDeltaXdsTest(t test.Failer, conn *grpc.ClientConn, - getClient func(conn *grpc.ClientConn) (DeltaDiscoveryClient, error)) *DeltaAdsTest { - ctx, cancel := context.WithCancel(context.Background()) - - cl, err := getClient(conn) - if err != nil { - t.Fatal(err) - } - resp := &DeltaAdsTest{ - client: cl, - conn: conn, - context: ctx, - cancelContext: cancel, - t: t, - ID: "sidecar~1.1.1.1~test.default~default.svc.cluster.local", - timeout: time.Second, - Type: v3.ClusterType, - responses: make(chan *discovery.DeltaDiscoveryResponse), - error: make(chan error), - } - t.Cleanup(resp.Cleanup) - - go resp.adsReceiveChannel() - - return resp -} - -type DeltaAdsTest struct { - client DeltaDiscoveryClient - responses chan *discovery.DeltaDiscoveryResponse - error chan error - t test.Failer - conn *grpc.ClientConn - metadata model.NodeMetadata - - ID string - Type string - - cancelOnce sync.Once - context context.Context - cancelContext context.CancelFunc - timeout time.Duration -} - -func (a *DeltaAdsTest) Cleanup() { - // Place in once to avoid race when two callers attempt to cleanup - a.cancelOnce.Do(func() { - a.cancelContext() - _ = a.client.CloseSend() - if a.conn != nil { - _ = a.conn.Close() - } - }) -} - -func (a *DeltaAdsTest) adsReceiveChannel() { - go func() { - <-a.context.Done() - a.Cleanup() - }() - for { - resp, err := a.client.Recv() - if err != nil { - if isUnexpectedError(err) { - log.Warnf("ads received error: %v", err) - } - select { - case a.error <- err: - case <-a.context.Done(): - } - return - } - select { - case a.responses <- resp: - case <-a.context.Done(): - return - } - } -} - -// DrainResponses reads all responses, but does nothing to them -func (a *DeltaAdsTest) DrainResponses() { - a.t.Helper() - for { - select { - case <-a.context.Done(): - return - case r := <-a.responses: - log.Infof("drained response %v", r.TypeUrl) - } - } -} - -// ExpectResponse waits until a response is received and returns it -func (a *DeltaAdsTest) ExpectResponse() *discovery.DeltaDiscoveryResponse { - a.t.Helper() - select { - case <-time.After(a.timeout): - a.t.Fatalf("did not get response in time") - case resp := <-a.responses: - if resp == nil || (len(resp.Resources) == 0 && len(resp.RemovedResources) == 0) { - a.t.Fatalf("got empty response") - } - return resp - case err := <-a.error: - a.t.Fatalf("got error: %v", err) - } - return nil -} - -// ExpectError waits until an error is received and returns it -func (a *DeltaAdsTest) ExpectError() error { - a.t.Helper() - select { - case <-time.After(a.timeout): - a.t.Fatalf("did not get error in time") - case err := <-a.error: - return err - } - return nil -} - -// ExpectNoResponse waits a short period of time and ensures no response is received -func (a *DeltaAdsTest) ExpectNoResponse() { - a.t.Helper() - select { - case <-time.After(time.Millisecond * 50): - return - case resp := <-a.responses: - a.t.Fatalf("got unexpected response: %v", resp) - } -} - -func (a *DeltaAdsTest) fillInRequestDefaults(req *discovery.DeltaDiscoveryRequest) *discovery.DeltaDiscoveryRequest { - if req == nil { - req = &discovery.DeltaDiscoveryRequest{} - } - if req.TypeUrl == "" { - req.TypeUrl = a.Type - } - if req.Node == nil { - req.Node = &core.Node{ - Id: a.ID, - Metadata: a.metadata.ToStruct(), - } - } - return req -} - -func (a *DeltaAdsTest) Request(req *discovery.DeltaDiscoveryRequest) { - req = a.fillInRequestDefaults(req) - if err := a.client.Send(req); err != nil { - a.t.Fatal(err) - } -} - -// RequestResponseAck does a full XDS exchange: Send a request, get a response, and ACK the response -func (a *DeltaAdsTest) RequestResponseAck(req *discovery.DeltaDiscoveryRequest) *discovery.DeltaDiscoveryResponse { - a.t.Helper() - req = a.fillInRequestDefaults(req) - a.Request(req) - resp := a.ExpectResponse() - req.ResponseNonce = resp.Nonce - a.Request(&discovery.DeltaDiscoveryRequest{ - Node: req.Node, - TypeUrl: req.TypeUrl, - ResponseNonce: req.ResponseNonce, - }) - return resp -} - -// RequestResponseNack does a full XDS exchange with an error: Send a request, get a response, and NACK the response -func (a *DeltaAdsTest) RequestResponseNack(req *discovery.DeltaDiscoveryRequest) *discovery.DeltaDiscoveryResponse { - a.t.Helper() - if req == nil { - req = &discovery.DeltaDiscoveryRequest{} - } - a.Request(req) - resp := a.ExpectResponse() - a.Request(&discovery.DeltaDiscoveryRequest{ - Node: req.Node, - TypeUrl: req.TypeUrl, - ResponseNonce: req.ResponseNonce, - ErrorDetail: &status.Status{Message: "Test request NACK"}, - }) - return resp -} - -func (a *DeltaAdsTest) WithID(id string) *DeltaAdsTest { - a.ID = id - return a -} - -func (a *DeltaAdsTest) WithType(typeURL string) *DeltaAdsTest { - a.Type = typeURL - return a -} - -func (a *DeltaAdsTest) WithMetadata(m model.NodeMetadata) *DeltaAdsTest { - a.metadata = m - return a -} - -func (a *DeltaAdsTest) WithTimeout(t time.Duration) *DeltaAdsTest { - a.timeout = t - return a -} diff --git a/pilot/pkg/xds/deltatest.go b/pilot/pkg/xds/deltatest.go deleted file mode 100644 index a624b36f3..000000000 --- a/pilot/pkg/xds/deltatest.go +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "fmt" -) - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/testing/protocmp" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -var knownOptimizationGaps = sets.New( - "BlackHoleCluster", - "InboundPassthroughClusterIpv4", - "InboundPassthroughClusterIpv6", - "PassthroughCluster", -) - -// compareDiff compares a Delta and SotW XDS response. This allows checking that the Delta XDS -// response returned the optimal result. Checks include correctness checks (e.g. if a config changed, -// we must include it) and possible optimizations (e.g. we sent a config, but it was not changed). -func (s *DiscoveryServer) compareDiff( - con *Connection, - w *model.WatchedResource, - full model.Resources, - resp model.Resources, - deleted model.DeletedResources, - usedDelta bool, - delta model.ResourceDelta, -) { - current := con.Watched(w.TypeUrl).LastResources - if current == nil { - log.Debugf("ADS:%s: resources initialized", v3.GetShortType(w.TypeUrl)) - return - } - if resp == nil && deleted == nil && len(full) == 0 { - // TODO: it suspicious full is never nil - are there case where we should be deleting everything? - // Both SotW and Delta did not respond, nothing to compare - return - } - newByName := map[string]*discovery.Resource{} - for _, v := range full { - newByName[v.Name] = v - } - curByName := map[string]*discovery.Resource{} - for _, v := range current { - curByName[v.Name] = v - } - - watched := sets.New(w.ResourceNames...) - - details := fmt.Sprintf("last:%v sotw:%v delta:%v-%v", len(current), len(full), len(resp), len(deleted)) - wantDeleted := sets.New() - wantChanged := sets.New() - wantUnchanged := sets.New() - for _, c := range current { - n := newByName[c.Name] - if n == nil { - // We had a resource, but SotW didn't generate it. - if watched.Contains(c.Name) { - // We only need to delete it if Envoy is watching. Otherwise, it may have simply unsubscribed - wantDeleted.Insert(c.Name) - } - } else if diff := cmp.Diff(c.Resource, n.Resource, protocmp.Transform()); diff != "" { - // Resource was modified - wantChanged.Insert(c.Name) - } else { - // No diff. Ideally delta doesn't send any update here - wantUnchanged.Insert(c.Name) - } - } - for _, v := range full { - if _, f := curByName[v.Name]; !f { - // Resource is added. Delta doesn't distinguish add vs update, so just put it with changed - wantChanged.Insert(v.Name) - } - } - - gotDeleted := sets.New() - if usedDelta { - gotDeleted.InsertAll(deleted...) - } - gotChanged := sets.New() - for _, v := range resp { - gotChanged.Insert(v.Name) - } - - // BUGS - extraDeletes := gotDeleted.Difference(wantDeleted).SortedList() - missedDeletes := wantDeleted.Difference(gotDeleted).SortedList() - missedChanges := wantChanged.Difference(gotChanged).SortedList() - - // Optimization Potential - extraChanges := gotChanged.Difference(wantChanged).Difference(knownOptimizationGaps).SortedList() - if len(delta.Subscribed) > 0 { - // Delta is configured to build only the request resources. Make sense we didn't build anything extra - if !wantChanged.SupersetOf(gotChanged) { - log.Errorf("%s: TEST for node:%s unexpected resources: %v %v", v3.GetShortType(w.TypeUrl), con.proxy.ID, details, wantChanged.Difference(gotChanged)) - } - // Still make sure we didn't delete anything extra - if len(extraDeletes) > 0 { - log.Errorf("%s: TEST for node:%s unexpected deletions: %v %v", v3.GetShortType(w.TypeUrl), con.proxy.ID, details, extraDeletes) - } - } else { - if len(extraDeletes) > 0 { - log.Errorf("%s: TEST for node:%s unexpected deletions: %v %v", v3.GetShortType(w.TypeUrl), con.proxy.ID, details, extraDeletes) - } - if len(missedDeletes) > 0 { - log.Errorf("%s: TEST for node:%s missed deletions: %v %v", v3.GetShortType(w.TypeUrl), con.proxy.ID, details, missedDeletes) - } - if len(missedChanges) > 0 { - log.Errorf("%s: TEST for node:%s missed changes: %v %v", v3.GetShortType(w.TypeUrl), con.proxy.ID, details, missedChanges) - } - if len(extraChanges) > 0 { - if usedDelta { - log.Infof("%s: TEST for node:%s missed possible optimization: %v. deleted:%v changed:%v", - v3.GetShortType(w.TypeUrl), con.proxy.ID, extraChanges, len(gotDeleted), len(gotChanged)) - } else { - log.Debugf("%s: TEST for node:%s missed possible optimization: %v. deleted:%v changed:%v", - v3.GetShortType(w.TypeUrl), con.proxy.ID, extraChanges, len(gotDeleted), len(gotChanged)) - } - } - } -} - -func applyDelta(message model.Resources, resp *discovery.DeltaDiscoveryResponse) model.Resources { - deleted := sets.New(resp.RemovedResources...) - byName := map[string]*discovery.Resource{} - for _, v := range resp.Resources { - byName[v.Name] = v - } - res := model.Resources{} - for _, m := range message { - if deleted.Contains(m.Name) { - continue - } - if replaced := byName[m.Name]; replaced != nil { - res = append(res, replaced) - delete(byName, m.Name) - continue - } - res = append(res, m) - } - for _, v := range byName { - res = append(res, v) - } - return res -} diff --git a/pilot/pkg/xds/discovery.go b/pilot/pkg/xds/discovery.go deleted file mode 100644 index b80a6b583..000000000 --- a/pilot/pkg/xds/discovery.go +++ /dev/null @@ -1,684 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "context" - "fmt" - "strconv" - "sync" - "time" -) - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "github.com/google/uuid" - "go.uber.org/atomic" - "golang.org/x/time/rate" - "google.golang.org/grpc" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/controller/workloadentry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/apigen" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3/envoyfilter" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/dubbogen" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/grpcgen" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/aggregate" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/security" -) - -var ( - versionMutex sync.RWMutex - // version is the timestamp of the last registry event. - version = "0" - // versionNum counts versions - versionNum = atomic.NewUint64(0) - - periodicRefreshMetrics = 10 * time.Second -) - -type debounceOptions struct { - // debounceAfter is the delay added to events to wait - // after a registry/config event for debouncing. - // This will delay the push by at least this interval, plus - // the time getting subsequent events. If no change is - // detected the push will happen, otherwise we'll keep - // delaying until things settle. - debounceAfter time.Duration - - // debounceMax is the maximum time to wait for events - // while debouncing. Defaults to 10 seconds. If events keep - // showing up with no break for this time, we'll trigger a push. - debounceMax time.Duration - - // enableEDSDebounce indicates whether EDS pushes should be debounced. - enableEDSDebounce bool -} - -// DiscoveryServer is Pilot's gRPC implementation for Envoy's xds APIs -type DiscoveryServer struct { - // Env is the model environment. - Env *model.Environment - - // MemRegistry is used for debug and load testing, allow adding services. Visible for testing. - MemRegistry *memory.ServiceDiscovery - - // ConfigGenerator is responsible for generating data plane configuration using Istio networking - // APIs and service registry info - ConfigGenerator core.ConfigGenerator - - // Generators allow customizing the generated config, based on the client metadata. - // Key is the generator type - will match the Generator metadata to set the per-connection - // default generator, or the combination of Generator metadata and TypeUrl to select a - // different generator for a type. - // Normal istio clients use the default generator - will not be impacted by this. - Generators map[string]model.XdsResourceGenerator - - // ProxyNeedsPush is a function that determines whether a push can be completely skipped. Individual generators - // may also choose to not send any updates. - ProxyNeedsPush func(proxy *model.Proxy, req *model.PushRequest) bool - - // concurrentPushLimit is a semaphore that limits the amount of concurrent XDS pushes. - concurrentPushLimit chan struct{} - // requestRateLimit limits the number of new XDS requests allowed. This helps prevent thundering hurd of incoming requests. - requestRateLimit *rate.Limiter - - // InboundUpdates describes the number of configuration updates the discovery server has received - InboundUpdates *atomic.Int64 - // CommittedUpdates describes the number of configuration updates the discovery server has - // received, process, and stored in the push context. If this number is less than InboundUpdates, - // there are updates we have not yet processed. - // Note: This does not mean that all proxies have received these configurations; it is strictly - // the push context, which means that the next push to a proxy will receive this configuration. - CommittedUpdates *atomic.Int64 - - // EndpointShards for a service. This is a global (per-server) list, built from - // incremental updates. This is keyed by service and namespace - EndpointIndex *model.EndpointIndex - - // pushChannel is the buffer used for debouncing. - // after debouncing the pushRequest will be sent to pushQueue - pushChannel chan *model.PushRequest - - // mutex used for protecting Environment.PushContext - updateMutex sync.RWMutex - - // pushQueue is the buffer that used after debounce and before the real xds push. - pushQueue *PushQueue - - // debugHandlers is the list of all the supported debug handlers. - debugHandlers map[string]string - - // adsClients reflect active gRPC channels, for both ADS and EDS. - adsClients map[string]*Connection - adsClientsMutex sync.RWMutex - - StatusReporter DistributionStatusCache - - // Authenticators for XDS requests. Should be same/subset of the CA authenticators. - Authenticators []security.Authenticator - - // StatusGen is notified of connect/disconnect/nack on all connections - StatusGen *StatusGen - WorkloadEntryController *workloadentry.Controller - - // serverReady indicates caches have been synced up and server is ready to process requests. - serverReady atomic.Bool - - debounceOptions debounceOptions - - instanceID string - - // Cache for XDS resources - Cache model.XdsCache - - // JwtKeyResolver holds a reference to the JWT key resolver instance. - JwtKeyResolver *model.JwksResolver - - // ListRemoteClusters collects debug information about other clusters this istiod reads from. - ListRemoteClusters func() []cluster.DebugInfo - - // ClusterAliases are aliase names for cluster. When a proxy connects with a cluster ID - // and if it has a different alias we should use that a cluster ID for proxy. - ClusterAliases map[cluster.ID]cluster.ID -} - -// NewDiscoveryServer creates DiscoveryServer that sources data from Pilot's internal mesh data structures -func NewDiscoveryServer(env *model.Environment, instanceID string, clusterAliases map[string]string) *DiscoveryServer { - out := &DiscoveryServer{ - Env: env, - Generators: map[string]model.XdsResourceGenerator{}, - ProxyNeedsPush: DefaultProxyNeedsPush, - EndpointIndex: model.NewEndpointIndex(), - concurrentPushLimit: make(chan struct{}, features.PushThrottle), - requestRateLimit: rate.NewLimiter(rate.Limit(features.RequestLimit), 1), - InboundUpdates: atomic.NewInt64(0), - CommittedUpdates: atomic.NewInt64(0), - pushChannel: make(chan *model.PushRequest, 10), - pushQueue: NewPushQueue(), - debugHandlers: map[string]string{}, - adsClients: map[string]*Connection{}, - debounceOptions: debounceOptions{ - debounceAfter: features.DebounceAfter, - debounceMax: features.DebounceMax, - enableEDSDebounce: features.EnableEDSDebounce, - }, - Cache: model.DisabledCache{}, - instanceID: instanceID, - } - - out.ClusterAliases = make(map[cluster.ID]cluster.ID) - for alias := range clusterAliases { - out.ClusterAliases[cluster.ID(alias)] = cluster.ID(clusterAliases[alias]) - } - - out.initJwksResolver() - - if features.EnableXDSCaching { - out.Cache = model.NewXdsCache() - // clear the cache as endpoint shards are modified to avoid cache write race - out.EndpointIndex.SetCache(out.Cache) - } - - out.ConfigGenerator = core.NewConfigGenerator(out.Cache) - - return out -} - -// initJwkResolver initializes the JWT key resolver to be used. -func (s *DiscoveryServer) initJwksResolver() { - if s.JwtKeyResolver != nil { - s.closeJwksResolver() - } - s.JwtKeyResolver = model.NewJwksResolver( - model.JwtPubKeyEvictionDuration, model.JwtPubKeyRefreshInterval, - model.JwtPubKeyRefreshIntervalOnFailure, model.JwtPubKeyRetryInterval) - - // Flush cached discovery responses when detecting jwt public key change. - s.JwtKeyResolver.PushFunc = func() { - s.ConfigUpdate(&model.PushRequest{Full: true, Reason: []model.TriggerReason{model.UnknownTrigger}}) - } -} - -// closeJwksResolver shuts down the JWT key resolver used. -func (s *DiscoveryServer) closeJwksResolver() { - if s.JwtKeyResolver != nil { - s.JwtKeyResolver.Close() - } - s.JwtKeyResolver = nil -} - -// Register adds the ADS handler to the grpc server -func (s *DiscoveryServer) Register(rpcs *grpc.Server) { - // Register v3 server - discovery.RegisterAggregatedDiscoveryServiceServer(rpcs, s) -} - -var processStartTime = time.Now() - -// CachesSynced is called when caches have been synced so that server can accept connections. -func (s *DiscoveryServer) CachesSynced() { - log.Infof("All caches have been synced up in %v, marking server ready", time.Since(processStartTime)) - s.serverReady.Store(true) -} - -func (s *DiscoveryServer) IsServerReady() bool { - return s.serverReady.Load() -} - -func (s *DiscoveryServer) Start(stopCh <-chan struct{}) { - go s.WorkloadEntryController.Run(stopCh) - go s.handleUpdates(stopCh) - go s.periodicRefreshMetrics(stopCh) - go s.sendPushes(stopCh) -} - -func (s *DiscoveryServer) getNonK8sRegistries() []serviceregistry.Instance { - var registries []serviceregistry.Instance - var nonK8sRegistries []serviceregistry.Instance - - if agg, ok := s.Env.ServiceDiscovery.(*aggregate.Controller); ok { - registries = agg.GetRegistries() - } else { - registries = []serviceregistry.Instance{ - serviceregistry.Simple{ - ServiceDiscovery: s.Env.ServiceDiscovery, - }, - } - } - - for _, registry := range registries { - if registry.Provider() != provider.Kubernetes && registry.Provider() != provider.External { - nonK8sRegistries = append(nonK8sRegistries, registry) - } - } - return nonK8sRegistries -} - -// Push metrics are updated periodically (10s default) -func (s *DiscoveryServer) periodicRefreshMetrics(stopCh <-chan struct{}) { - ticker := time.NewTicker(periodicRefreshMetrics) - defer ticker.Stop() - for { - select { - case <-ticker.C: - push := s.globalPushContext() - model.LastPushMutex.Lock() - if model.LastPushStatus != push { - model.LastPushStatus = push - push.UpdateMetrics() - out, _ := model.LastPushStatus.StatusJSON() - if string(out) != "{}" { - log.Infof("Push Status: %s", string(out)) - } - } - model.LastPushMutex.Unlock() - case <-stopCh: - return - } - } -} - -// dropCacheForRequest clears the cache in response to a push request -func (s *DiscoveryServer) dropCacheForRequest(req *model.PushRequest) { - // If we don't know what updated, cannot safely cache. Clear the whole cache - if len(req.ConfigsUpdated) == 0 { - s.Cache.ClearAll() - } else { - // Otherwise, just clear the updated configs - s.Cache.Clear(req.ConfigsUpdated) - } -} - -// Push is called to push changes on config updates using ADS. This is set in DiscoveryService.Push, -// to avoid direct dependencies. -func (s *DiscoveryServer) Push(req *model.PushRequest) { - if !req.Full { - req.Push = s.globalPushContext() - s.dropCacheForRequest(req) - s.AdsPushAll(versionInfo(), req) - return - } - // Reset the status during the push. - oldPushContext := s.globalPushContext() - if oldPushContext != nil { - oldPushContext.OnConfigChange() - // Push the previous push Envoy metrics. - envoyfilter.RecordMetrics() - } - // PushContext is reset after a config change. Previous status is - // saved. - t0 := time.Now() - - versionLocal := time.Now().Format(time.RFC3339) + "/" + strconv.FormatUint(versionNum.Inc(), 10) - push, err := s.initPushContext(req, oldPushContext, versionLocal) - if err != nil { - return - } - initContextTime := time.Since(t0) - log.Debugf("InitContext %v for push took %s", versionLocal, initContextTime) - pushContextInitTime.Record(initContextTime.Seconds()) - - versionMutex.Lock() - version = versionLocal - versionMutex.Unlock() - - req.Push = push - s.AdsPushAll(versionLocal, req) -} - -func nonce(noncePrefix string) string { - return noncePrefix + uuid.New().String() -} - -func versionInfo() string { - versionMutex.RLock() - defer versionMutex.RUnlock() - return version -} - -// Returns the global push context. This should be used with caution; generally the proxy-specific -// PushContext should be used to get the current state in the context of a single proxy. This should -// only be used for "global" lookups, such as initiating a new push to all proxies. -func (s *DiscoveryServer) globalPushContext() *model.PushContext { - s.updateMutex.RLock() - defer s.updateMutex.RUnlock() - return s.Env.PushContext -} - -// ConfigUpdate implements ConfigUpdater interface, used to request pushes. -// It replaces the 'clear cache' from v1. -func (s *DiscoveryServer) ConfigUpdate(req *model.PushRequest) { - inboundConfigUpdates.Increment() - s.InboundUpdates.Inc() - s.pushChannel <- req -} - -// Debouncing and push request happens in a separate thread, it uses locks -// and we want to avoid complications, ConfigUpdate may already hold other locks. -// handleUpdates processes events from pushChannel -// It ensures that at minimum minQuiet time has elapsed since the last event before processing it. -// It also ensures that at most maxDelay is elapsed between receiving an event and processing it. -func (s *DiscoveryServer) handleUpdates(stopCh <-chan struct{}) { - debounce(s.pushChannel, stopCh, s.debounceOptions, s.Push, s.CommittedUpdates) -} - -// The debounce helper function is implemented to enable mocking -func debounce(ch chan *model.PushRequest, stopCh <-chan struct{}, opts debounceOptions, pushFn func(req *model.PushRequest), updateSent *atomic.Int64) { - var timeChan <-chan time.Time - var startDebounce time.Time - var lastConfigUpdateTime time.Time - - pushCounter := 0 - debouncedEvents := 0 - - // Keeps track of the push requests. If updates are debounce they will be merged. - var req *model.PushRequest - - free := true - freeCh := make(chan struct{}, 1) - - push := func(req *model.PushRequest, debouncedEvents int) { - pushFn(req) - updateSent.Add(int64(debouncedEvents)) - freeCh <- struct{}{} - } - - pushWorker := func() { - eventDelay := time.Since(startDebounce) - quietTime := time.Since(lastConfigUpdateTime) - // it has been too long or quiet enough - if eventDelay >= opts.debounceMax || quietTime >= opts.debounceAfter { - if req != nil { - pushCounter++ - if req.ConfigsUpdated == nil { - log.Infof("Push debounce stable[%d] %d for reason %s: %v since last change, %v since last push, full=%v", - pushCounter, debouncedEvents, reasonsUpdated(req), - quietTime, eventDelay, req.Full) - } else { - log.Infof("Push debounce stable[%d] %d for config %s: %v since last change, %v since last push, full=%v", - pushCounter, debouncedEvents, configsUpdated(req), - quietTime, eventDelay, req.Full) - } - free = false - go push(req, debouncedEvents) - req = nil - debouncedEvents = 0 - } - } else { - timeChan = time.After(opts.debounceAfter - quietTime) - } - } - - for { - select { - case <-freeCh: - free = true - pushWorker() - case r := <-ch: - // If reason is not set, record it as an unknown reason - if len(r.Reason) == 0 { - r.Reason = []model.TriggerReason{model.UnknownTrigger} - } - if !opts.enableEDSDebounce && !r.Full { - // trigger push now, just for EDS - go func(req *model.PushRequest) { - pushFn(req) - updateSent.Inc() - }(r) - continue - } - - lastConfigUpdateTime = time.Now() - if debouncedEvents == 0 { - timeChan = time.After(opts.debounceAfter) - startDebounce = lastConfigUpdateTime - } - debouncedEvents++ - - req = req.Merge(r) - case <-timeChan: - if free { - pushWorker() - } - case <-stopCh: - return - } - } -} - -func configsUpdated(req *model.PushRequest) string { - configs := "" - for key := range req.ConfigsUpdated { - configs += key.String() - break - } - if len(req.ConfigsUpdated) > 1 { - more := fmt.Sprintf(" and %d more configs", len(req.ConfigsUpdated)-1) - configs += more - } - return configs -} - -func reasonsUpdated(req *model.PushRequest) string { - switch len(req.Reason) { - case 0: - return "unknown" - case 1: - return string(req.Reason[0]) - default: - return fmt.Sprintf("%s and %d more reasons", req.Reason[0], len(req.Reason)-1) - } -} - -func doSendPushes(stopCh <-chan struct{}, semaphore chan struct{}, queue *PushQueue) { - for { - select { - case <-stopCh: - return - default: - // We can send to it until it is full, then it will block until a pushes finishes and reads from it. - // This limits the number of pushes that can happen concurrently - semaphore <- struct{}{} - - // Get the next proxy to push. This will block if there are no updates required. - client, push, shuttingdown := queue.Dequeue() - if shuttingdown { - return - } - recordPushTriggers(push.Reason...) - // Signals that a push is done by reading from the semaphore, allowing another send on it. - doneFunc := func() { - queue.MarkDone(client) - <-semaphore - } - - proxiesQueueTime.Record(time.Since(push.Start).Seconds()) - var closed <-chan struct{} - if client.stream != nil { - closed = client.stream.Context().Done() - } else { - closed = client.deltaStream.Context().Done() - } - go func() { - pushEv := &Event{ - pushRequest: push, - done: doneFunc, - } - - select { - case client.pushChannel <- pushEv: - return - case <-closed: // grpc stream was closed - doneFunc() - log.Infof("Client closed connection %v", client.conID) - } - }() - } - } -} - -// initPushContext creates a global push context and stores it on the environment. Note: while this -// method is technically thread safe (there are no data races), it should not be called in parallel; -// if it is, then we may start two push context creations (say A, and B), but then write them in -// reverse order, leaving us with a final version of A, which may be incomplete. -func (s *DiscoveryServer) initPushContext(req *model.PushRequest, oldPushContext *model.PushContext, version string) (*model.PushContext, error) { - push := model.NewPushContext() - push.PushVersion = version - push.JwtKeyResolver = s.JwtKeyResolver - if err := push.InitContext(s.Env, oldPushContext, req); err != nil { - log.Errorf("XDS: failed to init push context: %v", err) - // We can't push if we can't read the data - stick with previous version. - pushContextErrors.Increment() - return nil, err - } - - if err := s.UpdateServiceShards(push); err != nil { - return nil, err - } - - s.updateMutex.Lock() - s.Env.PushContext = push - // Ensure we drop the cache in the lock to avoid races, where we drop the cache, fill it back up, then update push context - s.dropCacheForRequest(req) - s.updateMutex.Unlock() - - return push, nil -} - -func (s *DiscoveryServer) sendPushes(stopCh <-chan struct{}) { - doSendPushes(stopCh, s.concurrentPushLimit, s.pushQueue) -} - -// InitGenerators initializes generators to be used by XdsServer. -func (s *DiscoveryServer) InitGenerators(env *model.Environment, systemNameSpace string) { - edsGen := &EdsGenerator{Server: s} - s.StatusGen = NewStatusGen(s) - s.Generators[v3.ClusterType] = &CdsGenerator{Server: s} - s.Generators[v3.ListenerType] = &LdsGenerator{Server: s} - s.Generators[v3.RouteType] = &RdsGenerator{Server: s} - s.Generators[v3.EndpointType] = edsGen - s.Generators[v3.NameTableType] = &NdsGenerator{Server: s} - s.Generators[v3.ExtensionConfigurationType] = &EcdsGenerator{Server: s} - s.Generators[v3.ProxyConfigType] = &PcdsGenerator{Server: s, TrustBundle: env.TrustBundle} - - s.Generators["grpc"] = &grpcgen.GrpcConfigGenerator{} - s.Generators["grpc/"+v3.EndpointType] = edsGen - s.Generators["grpc/"+v3.ListenerType] = s.Generators["grpc"] - s.Generators["grpc/"+v3.RouteType] = s.Generators["grpc"] - s.Generators["grpc/"+v3.ClusterType] = s.Generators["grpc"] - - s.Generators["dubbo"] = &dubbogen.DubboConfigGenerator{} - s.Generators[v3.DubboServiceNameMappingType] = s.Generators["dubbo"] - - s.Generators["api"] = apigen.NewGenerator(env.ConfigStore) - s.Generators["api/"+v3.EndpointType] = edsGen - - s.Generators["api/"+TypeURLConnect] = s.StatusGen - - s.Generators["event"] = s.StatusGen - s.Generators[TypeDebug] = NewDebugGen(s, systemNameSpace) - s.Generators[v3.BootstrapType] = &BootstrapGenerator{Server: s} -} - -// shutdown shuts down DiscoveryServer components. -func (s *DiscoveryServer) Shutdown() { - s.closeJwksResolver() - s.pushQueue.ShutDown() -} - -// Clients returns all currently connected clients. This method can be safely called concurrently, -// but care should be taken with the underlying objects (ie model.Proxy) to ensure proper locking. -// This method returns only fully initialized connections; for all connections, use AllClients -func (s *DiscoveryServer) Clients() []*Connection { - s.adsClientsMutex.RLock() - defer s.adsClientsMutex.RUnlock() - clients := make([]*Connection, 0, len(s.adsClients)) - for _, con := range s.adsClients { - select { - case <-con.initialized: - default: - // Initialization not complete, skip - continue - } - clients = append(clients, con) - } - return clients -} - -// AllClients returns all connected clients, per Clients, but additionally includes unintialized connections -// Warning: callers must take care not to rely on the con.proxy field being set -func (s *DiscoveryServer) AllClients() []*Connection { - s.adsClientsMutex.RLock() - defer s.adsClientsMutex.RUnlock() - clients := make([]*Connection, 0, len(s.adsClients)) - for _, con := range s.adsClients { - clients = append(clients, con) - } - return clients -} - -// SendResponse will immediately send the response to all connections. -// TODO: additional filters can be added, for example namespace. -func (s *DiscoveryServer) SendResponse(connections []*Connection, res *discovery.DiscoveryResponse) { - for _, p := range connections { - // p.send() waits for an ACK - which is reasonable for normal push, - // but in this case we want to sync fast and not bother with stuck connections. - // This is expecting a relatively small number of watchers - each other istiod - // plus few admin tools or bridges to real message brokers. The normal - // push expects 1000s of envoy connections. - con := p - go func() { - err := con.stream.Send(res) - if err != nil { - log.Errorf("Failed to send internal event %s: %v", con.conID, err) - } - }() - } -} - -// nolint -// ClientsOf returns the clients that are watching the given resource. -func (s *DiscoveryServer) ClientsOf(typeUrl string) []*Connection { - pending := []*Connection{} - for _, v := range s.Clients() { - if v.Watching(typeUrl) { - pending = append(pending, v) - } - } - - return pending -} - -func (s *DiscoveryServer) WaitForRequestLimit(ctx context.Context) error { - if s.requestRateLimit.Limit() == 0 { - // Allow opt out when rate limiting is set to 0qps - return nil - } - // Give a bit of time for queue to clear out, but if not fail fast. Client will connect to another - // instance in best case, or retry with backoff. - wait, cancel := context.WithTimeout(ctx, time.Second) - defer cancel() - return s.requestRateLimit.Wait(wait) -} diff --git a/pilot/pkg/xds/discovery_test.go b/pilot/pkg/xds/discovery_test.go deleted file mode 100644 index 42e55f676..000000000 --- a/pilot/pkg/xds/discovery_test.go +++ /dev/null @@ -1,545 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "context" - "fmt" - "reflect" - "sync" - "sync/atomic" - "testing" - "time" -) - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - uatomic "go.uber.org/atomic" - "google.golang.org/grpc" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -func createProxies(n int) []*Connection { - proxies := make([]*Connection, 0, n) - for p := 0; p < n; p++ { - proxies = append(proxies, &Connection{ - conID: fmt.Sprintf("proxy-%v", p), - pushChannel: make(chan *Event), - stream: &fakeStream{}, - }) - } - return proxies -} - -func wgDoneOrTimeout(wg *sync.WaitGroup, timeout time.Duration) bool { - c := make(chan struct{}) - go func() { - wg.Wait() - c <- struct{}{} - }() - select { - case <-c: - return true - case <-time.After(timeout): - return false - } -} - -func TestSendPushesManyPushes(t *testing.T) { - stopCh := make(chan struct{}) - defer close(stopCh) - - semaphore := make(chan struct{}, 2) - queue := NewPushQueue() - defer queue.ShutDown() - - proxies := createProxies(5) - - pushes := make(map[string]int) - pushesMu := &sync.Mutex{} - - for _, proxy := range proxies { - proxy := proxy - // Start receive thread - go func() { - for { - select { - case p := <-proxy.pushChannel: - p.done() - pushesMu.Lock() - pushes[proxy.conID]++ - pushesMu.Unlock() - case <-stopCh: - return - } - } - }() - } - go doSendPushes(stopCh, semaphore, queue) - - for push := 0; push < 100; push++ { - for _, proxy := range proxies { - queue.Enqueue(proxy, &model.PushRequest{Push: &model.PushContext{}}) - } - time.Sleep(time.Millisecond * 10) - } - for queue.Pending() > 0 { - time.Sleep(time.Millisecond) - } - pushesMu.Lock() - defer pushesMu.Unlock() - for proxy, numPushes := range pushes { - if numPushes == 0 { - t.Fatalf("Proxy %v had 0 pushes", proxy) - } - } -} - -func TestSendPushesSinglePush(t *testing.T) { - stopCh := make(chan struct{}) - defer close(stopCh) - - semaphore := make(chan struct{}, 2) - queue := NewPushQueue() - defer queue.ShutDown() - - proxies := createProxies(5) - - wg := &sync.WaitGroup{} - wg.Add(5) - - pushes := make(map[string]int) - pushesMu := &sync.Mutex{} - - for _, proxy := range proxies { - proxy := proxy - // Start receive thread - go func() { - for { - select { - case p := <-proxy.pushChannel: - p.done() - pushesMu.Lock() - pushes[proxy.conID]++ - pushesMu.Unlock() - wg.Done() - case <-stopCh: - return - } - } - }() - } - go doSendPushes(stopCh, semaphore, queue) - - for _, proxy := range proxies { - queue.Enqueue(proxy, &model.PushRequest{Push: &model.PushContext{}}) - } - - if !wgDoneOrTimeout(wg, time.Second) { - t.Fatalf("Expected 5 pushes but got %v", len(pushes)) - } - expected := map[string]int{ - "proxy-0": 1, - "proxy-1": 1, - "proxy-2": 1, - "proxy-3": 1, - "proxy-4": 1, - } - if !reflect.DeepEqual(expected, pushes) { - t.Fatalf("Expected pushes %+v, got %+v", expected, pushes) - } -} - -type fakeStream struct { - grpc.ServerStream -} - -func (h *fakeStream) Send(*discovery.DiscoveryResponse) error { - return nil -} - -func (h *fakeStream) Recv() (*discovery.DiscoveryRequest, error) { - return nil, nil -} - -func (h *fakeStream) Context() context.Context { - return context.Background() -} - -func TestDebounce(t *testing.T) { - // This test tests the timeout and debouncing of config updates - // If it is flaking, DebounceAfter may need to be increased, or the code refactored to mock time. - // For now, this seems to work well - opts := debounceOptions{ - debounceAfter: time.Millisecond * 50, - debounceMax: time.Millisecond * 100, - enableEDSDebounce: false, - } - - tests := []struct { - name string - test func(updateCh chan *model.PushRequest, expect func(partial, full int32)) - }{ - { - name: "Should not debounce partial pushes", - test: func(updateCh chan *model.PushRequest, expect func(partial, full int32)) { - updateCh <- &model.PushRequest{Full: false} - expect(1, 0) - updateCh <- &model.PushRequest{Full: false} - expect(2, 0) - updateCh <- &model.PushRequest{Full: false} - expect(3, 0) - updateCh <- &model.PushRequest{Full: false} - expect(4, 0) - updateCh <- &model.PushRequest{Full: false} - expect(5, 0) - }, - }, - { - name: "Should debounce full pushes", - test: func(updateCh chan *model.PushRequest, expect func(partial, full int32)) { - updateCh <- &model.PushRequest{Full: true} - expect(0, 0) - }, - }, - { - name: "Should send full updates in batches", - test: func(updateCh chan *model.PushRequest, expect func(partial, full int32)) { - updateCh <- &model.PushRequest{Full: true} - updateCh <- &model.PushRequest{Full: true} - expect(0, 1) - }, - }, - { - name: "Should send full updates in batches, partial updates immediately", - test: func(updateCh chan *model.PushRequest, expect func(partial, full int32)) { - updateCh <- &model.PushRequest{Full: true} - updateCh <- &model.PushRequest{Full: true} - updateCh <- &model.PushRequest{Full: false} - updateCh <- &model.PushRequest{Full: false} - expect(2, 1) - updateCh <- &model.PushRequest{Full: false} - expect(3, 1) - }, - }, - { - name: "Should force a push after DebounceMax", - test: func(updateCh chan *model.PushRequest, expect func(partial, full int32)) { - // Send many requests within debounce window - updateCh <- &model.PushRequest{Full: true} - time.Sleep(opts.debounceAfter / 2) - updateCh <- &model.PushRequest{Full: true} - time.Sleep(opts.debounceAfter / 2) - updateCh <- &model.PushRequest{Full: true} - time.Sleep(opts.debounceAfter / 2) - updateCh <- &model.PushRequest{Full: true} - time.Sleep(opts.debounceAfter / 2) - expect(0, 1) - }, - }, - { - name: "Should push synchronously after debounce", - test: func(updateCh chan *model.PushRequest, expect func(partial, full int32)) { - updateCh <- &model.PushRequest{Full: true} - time.Sleep(opts.debounceAfter + 10*time.Millisecond) - updateCh <- &model.PushRequest{Full: true} - expect(0, 2) - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - stopCh := make(chan struct{}) - updateCh := make(chan *model.PushRequest) - pushingCh := make(chan struct{}, 1) - errCh := make(chan error, 1) - - var partialPushes int32 - var fullPushes int32 - - wg := sync.WaitGroup{} - - fakePush := func(req *model.PushRequest) { - if req.Full { - select { - case pushingCh <- struct{}{}: - default: - errCh <- fmt.Errorf("multiple pushes happen simultaneously") - return - } - atomic.AddInt32(&fullPushes, 1) - time.Sleep(opts.debounceMax * 2) - <-pushingCh - } else { - atomic.AddInt32(&partialPushes, 1) - } - } - updateSent := uatomic.NewInt64(0) - - wg.Add(1) - go func() { - debounce(updateCh, stopCh, opts, fakePush, updateSent) - wg.Done() - }() - - expect := func(expectedPartial, expectedFull int32) { - t.Helper() - err := retry.UntilSuccess(func() error { - select { - case err := <-errCh: - t.Error(err) - return err - default: - partial := atomic.LoadInt32(&partialPushes) - full := atomic.LoadInt32(&fullPushes) - if partial != expectedPartial || full != expectedFull { - return fmt.Errorf("got %v full and %v partial, expected %v full and %v partial", full, partial, expectedFull, expectedPartial) - } - return nil - } - }, retry.Timeout(opts.debounceAfter*8), retry.Delay(opts.debounceAfter/2)) - if err != nil { - t.Error(err) - } - } - - // Send updates - tt.test(updateCh, expect) - - close(stopCh) - wg.Wait() - }) - } -} - -func TestShouldRespond(t *testing.T) { - tests := []struct { - name string - connection *Connection - request *discovery.DiscoveryRequest - response bool - }{ - { - name: "initial request", - connection: &Connection{ - proxy: &model.Proxy{ - WatchedResources: map[string]*model.WatchedResource{}, - }, - }, - request: &discovery.DiscoveryRequest{ - TypeUrl: v3.ClusterType, - }, - response: true, - }, - { - name: "ack", - connection: &Connection{ - proxy: &model.Proxy{ - WatchedResources: map[string]*model.WatchedResource{ - v3.ClusterType: { - VersionSent: "v1", - NonceSent: "nonce", - }, - }, - }, - }, - request: &discovery.DiscoveryRequest{ - TypeUrl: v3.ClusterType, - VersionInfo: "v1", - ResponseNonce: "nonce", - }, - response: false, - }, - { - name: "nack", - connection: &Connection{ - proxy: &model.Proxy{ - WatchedResources: map[string]*model.WatchedResource{ - v3.ClusterType: { - VersionSent: "v1", - NonceSent: "nonce", - }, - }, - }, - }, - request: &discovery.DiscoveryRequest{ - TypeUrl: v3.ClusterType, - VersionInfo: "v1", - ResponseNonce: "stale nonce", - }, - response: false, - }, - { - name: "reconnect", - connection: &Connection{ - proxy: &model.Proxy{ - WatchedResources: map[string]*model.WatchedResource{}, - }, - }, - request: &discovery.DiscoveryRequest{ - TypeUrl: v3.ClusterType, - VersionInfo: "v1", - ResponseNonce: "reconnect nonce", - }, - response: true, - }, - { - name: "resources change", - connection: &Connection{ - proxy: &model.Proxy{ - WatchedResources: map[string]*model.WatchedResource{ - v3.EndpointType: { - VersionSent: "v1", - NonceSent: "nonce", - ResourceNames: []string{"cluster1"}, - }, - }, - }, - }, - request: &discovery.DiscoveryRequest{ - TypeUrl: v3.EndpointType, - VersionInfo: "v1", - ResponseNonce: "nonce", - ResourceNames: []string{"cluster1", "cluster2"}, - }, - response: true, - }, - { - name: "ack with same resources", - connection: &Connection{ - proxy: &model.Proxy{ - WatchedResources: map[string]*model.WatchedResource{ - v3.EndpointType: { - VersionSent: "v1", - NonceSent: "nonce", - ResourceNames: []string{"cluster2", "cluster1"}, - }, - }, - }, - }, - request: &discovery.DiscoveryRequest{ - TypeUrl: v3.EndpointType, - VersionInfo: "v1", - ResponseNonce: "nonce", - ResourceNames: []string{"cluster1", "cluster2"}, - }, - response: false, - }, - { - name: "unsubscribe EDS", - connection: &Connection{ - proxy: &model.Proxy{ - WatchedResources: map[string]*model.WatchedResource{ - v3.EndpointType: { - VersionSent: "v1", - NonceSent: "nonce", - ResourceNames: []string{"cluster2", "cluster1"}, - }, - }, - }, - }, - request: &discovery.DiscoveryRequest{ - TypeUrl: v3.EndpointType, - VersionInfo: "v1", - ResponseNonce: "nonce", - ResourceNames: []string{}, - }, - response: false, - }, - { - name: "resources change", - connection: &Connection{ - proxy: &model.Proxy{ - WatchedResources: map[string]*model.WatchedResource{ - v3.DubboServiceNameMappingType: { - VersionSent: "v1", - NonceSent: "nonce", - ResourceNames: []string{"dubbo.demo.hello1"}, - }, - }, - }, - }, - request: &discovery.DiscoveryRequest{ - TypeUrl: v3.DubboServiceNameMappingType, - VersionInfo: "v1", - ResponseNonce: "nonce", - ResourceNames: []string{"dubbo.demo.hello1", "dubbo.demo.hello2"}, - }, - response: true, - }, - { - name: "ack with same resources", - connection: &Connection{ - proxy: &model.Proxy{ - WatchedResources: map[string]*model.WatchedResource{ - v3.DubboServiceNameMappingType: { - VersionSent: "v1", - NonceSent: "nonce", - ResourceNames: []string{"dubbo.demo.hello1", "dubbo.demo.hello2"}, - }, - }, - }, - }, - request: &discovery.DiscoveryRequest{ - TypeUrl: v3.DubboServiceNameMappingType, - VersionInfo: "v1", - ResponseNonce: "nonce", - ResourceNames: []string{"dubbo.demo.hello1", "dubbo.demo.hello2"}, - }, - response: false, - }, - { - name: "unsubscribe snp", - connection: &Connection{ - proxy: &model.Proxy{ - WatchedResources: map[string]*model.WatchedResource{ - v3.DubboServiceNameMappingType: { - VersionSent: "v1", - NonceSent: "nonce", - ResourceNames: []string{"dubbo.demo.hello1", "dubbo.demo.hello2"}, - }, - }, - }, - }, - request: &discovery.DiscoveryRequest{ - TypeUrl: v3.DubboServiceNameMappingType, - VersionInfo: "v1", - ResponseNonce: "nonce", - ResourceNames: []string{}, - }, - response: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := NewFakeDiscoveryServer(t, FakeOptions{}) - if response, _ := s.Discovery.shouldRespond(tt.connection, tt.request); response != tt.response { - t.Fatalf("Unexpected value for response, expected %v, got %v", tt.response, response) - } - if tt.name != "reconnect" && tt.response { - if tt.connection.proxy.WatchedResources[tt.request.TypeUrl].NonceAcked != tt.request.ResponseNonce { - t.Fatalf("Version & Nonce not updated properly") - } - } - }) - } -} diff --git a/pilot/pkg/xds/ecds.go b/pilot/pkg/xds/ecds.go deleted file mode 100644 index 98a6180d7..000000000 --- a/pilot/pkg/xds/ecds.go +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "fmt" - "strings" -) - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" -) - -import ( - credscontroller "github.com/apache/dubbo-go-pixiu/pilot/pkg/credentials" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model/credentials" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// EcdsGenerator generates ECDS configuration. -type EcdsGenerator struct { - Server *DiscoveryServer - secretController credscontroller.MulticlusterController -} - -var _ model.XdsResourceGenerator = &EcdsGenerator{} - -func ecdsNeedsPush(req *model.PushRequest) bool { - if req == nil { - return true - } - // If none set, we will always push - if len(req.ConfigsUpdated) == 0 { - return true - } - // Only push if config updates is triggered by EnvoyFilter, WasmPlugin, or Secret. - for config := range req.ConfigsUpdated { - switch config.Kind { - case gvk.EnvoyFilter: - return true - case gvk.WasmPlugin: - return true - case gvk.Secret: - return true - } - } - return false -} - -// Generate returns ECDS resources for a given proxy. -func (e *EcdsGenerator) Generate(proxy *model.Proxy, w *model.WatchedResource, req *model.PushRequest) (model.Resources, model.XdsLogDetails, error) { - if !ecdsNeedsPush(req) { - return nil, model.DefaultXdsLogDetails, nil - } - - secretResources := referencedSecrets(proxy, req.Push, w.ResourceNames) - // Check if the secret updates is relevant to Wasm image pull. If not relevant, skip pushing ECDS. - if !model.ConfigsHaveKind(req.ConfigsUpdated, gvk.WasmPlugin) && !model.ConfigsHaveKind(req.ConfigsUpdated, gvk.EnvoyFilter) && - model.ConfigsHaveKind(req.ConfigsUpdated, gvk.Secret) { - // Get the updated secrets - updatedSecrets := model.ConfigsOfKind(req.ConfigsUpdated, gvk.Secret) - needsPush := false - for _, sr := range secretResources { - if _, found := updatedSecrets[model.ConfigKey{Kind: gvk.Secret, Name: sr.Name, Namespace: sr.Namespace}]; found { - needsPush = true - break - } - } - if !needsPush { - return nil, model.DefaultXdsLogDetails, nil - } - } - - var secrets map[string][]byte - if len(secretResources) > 0 { - // Generate the pull secrets first, which will be used when populating the extension config. - var secretController credscontroller.Controller - if e.secretController != nil { - var err error - secretController, err = e.secretController.ForCluster(proxy.Metadata.ClusterID) - if err != nil { - log.Warnf("proxy %s is from an unknown cluster, cannot retrieve certificates for Wasm image pull: %v", proxy.ID, err) - return nil, model.DefaultXdsLogDetails, nil - } - } - // Inserts Wasm pull secrets in ECDS response, which will be used at xds proxy for image pull. - // Before forwarding to Envoy, xds proxy will remove the secret from ECDS response. - secrets = e.GeneratePullSecrets(proxy, secretResources, secretController) - } - - ec := e.Server.ConfigGenerator.BuildExtensionConfiguration(proxy, req.Push, w.ResourceNames, secrets) - - if ec == nil { - return nil, model.DefaultXdsLogDetails, nil - } - - resources := make(model.Resources, 0, len(ec)) - for _, c := range ec { - resources = append(resources, &discovery.Resource{ - Name: c.Name, - Resource: util.MessageToAny(c), - }) - } - - return resources, model.DefaultXdsLogDetails, nil -} - -func (e *EcdsGenerator) GeneratePullSecrets(proxy *model.Proxy, secretResources []SecretResource, - secretController credscontroller.Controller, -) map[string][]byte { - if proxy.VerifiedIdentity == nil { - log.Warnf("proxy %s is not authorized to receive secret. Ensure you are connecting over TLS port and are authenticated.", proxy.ID) - return nil - } - - results := make(map[string][]byte) - for _, sr := range secretResources { - cred, err := secretController.GetDockerCredential(sr.Name, sr.Namespace) - if err != nil { - log.Warnf("Failed to fetch docker credential %s: %v", sr.ResourceName, err) - } else { - results[sr.ResourceName] = cred - } - } - return results -} - -func (e *EcdsGenerator) SetCredController(creds credscontroller.MulticlusterController) { - e.secretController = creds -} - -func referencedSecrets(proxy *model.Proxy, push *model.PushContext, resourceNames []string) []SecretResource { - // The requirement for the Wasm pull secret: - // * Wasm pull secrets must be of type `kubernetes.io/dockerconfigjson`. - // * Secret are referenced by a WasmPlugin which applies to this proxy. - // TODO: we get the WasmPlugins here to get the secrets reference in order to decide whether ECDS push is needed, - // and we will get it again at extension config build. Avoid getting it twice if this becomes a problem. - watched := sets.New(resourceNames...) - wasmPlugins := push.WasmPlugins(proxy) - referencedSecrets := sets.Set{} - for _, wps := range wasmPlugins { - for _, wp := range wps { - if watched.Contains(wp.ResourceName) && wp.ImagePullSecret != "" { - referencedSecrets.Insert(wp.ImagePullSecret) - } - } - } - var filtered []SecretResource - for rn := range referencedSecrets { - sr, err := parseSecretName(rn, proxy.Metadata.ClusterID) - if err != nil { - log.Warnf("Failed to parse secret resource name %v: %v", rn, err) - continue - } - filtered = append(filtered, sr) - } - - return filtered -} - -// parseSecretName parses secret resource name from WasmPlugin env variable. -// See toSecretResourceName at model/extensions.go about how secret resource name is generated. -func parseSecretName(resourceName string, proxyCluster cluster.ID) (SecretResource, error) { - // The secret resource name must be formatted as kubernetes://secret-namespace/secret-name. - if !strings.HasPrefix(resourceName, credentials.KubernetesSecretTypeURI) { - return SecretResource{}, fmt.Errorf("misformed Wasm pull secret resource name %v", resourceName) - } - res := strings.TrimPrefix(resourceName, credentials.KubernetesSecretTypeURI) - sep := "/" - split := strings.Split(res, sep) - if len(split) != 2 { - return SecretResource{}, fmt.Errorf("misformed Wasm pull secret resource name %v", resourceName) - } - return SecretResource{ - SecretResource: credentials.SecretResource{ - Type: credentials.KubernetesSecretType, - Name: split[1], - Namespace: split[0], - ResourceName: resourceName, - Cluster: proxyCluster, - }, - }, nil -} diff --git a/pilot/pkg/xds/ecds_test.go b/pilot/pkg/xds/ecds_test.go deleted file mode 100644 index 965d6a3eb..000000000 --- a/pilot/pkg/xds/ecds_test.go +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package xds_test - -import ( - "reflect" - "testing" - "time" -) - -import ( - corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - extensionsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/wasm/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - extensions "istio.io/api/extensions/v1alpha1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/spiffe" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -func TestECDS(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ - ConfigString: mustReadFile(t, "./testdata/ecds.yaml"), - }) - - ads := s.ConnectADS().WithType(v3.ExtensionConfigurationType) - wantExtensionConfigName := "extension-config" - md := model.NodeMetadata{ - ClusterID: "Kubernetes", - } - res := ads.RequestResponseAck(t, &discovery.DiscoveryRequest{ - Node: &corev3.Node{ - Id: ads.ID, - Metadata: md.ToStruct(), - }, - ResourceNames: []string{wantExtensionConfigName}, - }) - - var ec corev3.TypedExtensionConfig - err := res.Resources[0].UnmarshalTo(&ec) - if err != nil { - t.Fatal("Failed to unmarshal extension config", err) - return - } - if ec.Name != wantExtensionConfigName { - t.Errorf("extension config name got %v want %v", ec.Name, wantExtensionConfigName) - } -} - -func makeDockerCredentials(name, namespace string, data map[string]string, secretType corev1.SecretType) *corev1.Secret { - bdata := map[string][]byte{} - for k, v := range data { - bdata[k] = []byte(v) - } - return &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Data: bdata, - Type: secretType, - } -} - -func makeWasmPlugin(name, namespace, secret string) config.Config { - spec := &extensions.WasmPlugin{ - Phase: extensions.PluginPhase_AUTHN, - } - if secret != "" { - spec.ImagePullSecret = secret - } - return config.Config{ - Meta: config.Meta{ - Name: name, - Namespace: namespace, - GroupVersionKind: gvk.WasmPlugin, - }, - Spec: spec, - } -} - -var ( - // Secrets - defaultPullSecret = makeDockerCredentials("default-pull-secret", "default", map[string]string{ - corev1.DockerConfigJsonKey: "default-docker-credential", - }, corev1.SecretTypeDockerConfigJson) - rootPullSecret = makeDockerCredentials("root-pull-secret", "dubbo-system", map[string]string{ - corev1.DockerConfigJsonKey: "root-docker-credential", - }, corev1.SecretTypeDockerConfigJson) - wrongTypeSecret = makeDockerCredentials("wrong-type-pull-secret", "default", map[string]string{ - corev1.DockerConfigJsonKey: "wrong-type-docker-credential", - }, corev1.SecretTypeTLS) - - wasmPlugin = makeWasmPlugin("default-plugin", "default", "") - wasmPluginWithSec = makeWasmPlugin("default-plugin-with-sec", "default", "default-pull-secret") - wasmPluginWrongSec = makeWasmPlugin("default-plugin-wrong-sec", "default", "wrong-secret") - wasmPluginWrongSecType = makeWasmPlugin("default-plugin-wrong-sec-type", "default", "wrong-type-pull-secret") - rootWasmPluginWithSec = makeWasmPlugin("root-plugin", "dubbo-system", "root-pull-secret") -) - -func TestECDSGenerate(t *testing.T) { - cases := []struct { - name string - proxyNamespace string - request *model.PushRequest - watchedResources []string - wantExtensions sets.Set - wantSecrets sets.Set - }{ - { - name: "simple", - proxyNamespace: "default", - request: &model.PushRequest{Full: true}, - watchedResources: []string{"default.default-plugin"}, - wantExtensions: sets.Set{"default.default-plugin": {}}, - wantSecrets: sets.Set{}, - }, - { - name: "simple_with_secret", - proxyNamespace: "default", - request: &model.PushRequest{Full: true}, - watchedResources: []string{"default.default-plugin-with-sec"}, - wantExtensions: sets.Set{"default.default-plugin-with-sec": {}}, - wantSecrets: sets.Set{"default-docker-credential": {}}, - }, - { - name: "miss_secret", - proxyNamespace: "default", - request: &model.PushRequest{Full: true}, - watchedResources: []string{"default.default-plugin-wrong-sec"}, - wantExtensions: sets.Set{"default.default-plugin-wrong-sec": {}}, - wantSecrets: sets.Set{}, - }, - { - name: "wrong_secret_type", - proxyNamespace: "default", - request: &model.PushRequest{Full: true}, - watchedResources: []string{"default.default-plugin-wrong-sec-type"}, - wantExtensions: sets.Set{"default.default-plugin-wrong-sec-type": {}}, - wantSecrets: sets.Set{}, - }, - { - name: "root_and_default", - proxyNamespace: "default", - request: &model.PushRequest{Full: true}, - watchedResources: []string{"default.default-plugin-with-sec", "dubbo-system.root-plugin"}, - wantExtensions: sets.Set{"default.default-plugin-with-sec": {}, "dubbo-system.root-plugin": {}}, - wantSecrets: sets.Set{"default-docker-credential": {}, "root-docker-credential": {}}, - }, - { - name: "only_root", - proxyNamespace: "somenamespace", - request: &model.PushRequest{Full: true}, - watchedResources: []string{"dubbo-system.root-plugin"}, - wantExtensions: sets.Set{"dubbo-system.root-plugin": {}}, - wantSecrets: sets.Set{"root-docker-credential": {}}, - }, - { - name: "no_relevant_config_update", - proxyNamespace: "default", - request: &model.PushRequest{ - Full: true, - ConfigsUpdated: map[model.ConfigKey]struct{}{ - {Kind: gvk.AuthorizationPolicy}: {}, - }, - }, - watchedResources: []string{"default.default-plugin-with-sec", "dubbo-system.root-plugin"}, - wantExtensions: sets.Set{}, - wantSecrets: sets.Set{}, - }, - { - name: "has_relevant_config_update", - proxyNamespace: "default", - request: &model.PushRequest{ - Full: true, - ConfigsUpdated: map[model.ConfigKey]struct{}{ - {Kind: gvk.AuthorizationPolicy}: {}, - {Kind: gvk.WasmPlugin}: {}, - }, - }, - watchedResources: []string{"default.default-plugin-with-sec"}, - wantExtensions: sets.Set{"default.default-plugin-with-sec": {}}, - wantSecrets: sets.Set{"default-docker-credential": {}}, - }, - { - name: "non_relevant_secret_update", - proxyNamespace: "default", - request: &model.PushRequest{ - Full: true, - ConfigsUpdated: map[model.ConfigKey]struct{}{ - {Kind: gvk.AuthorizationPolicy}: {}, - {Kind: gvk.Secret}: {}, - }, - }, - watchedResources: []string{"default.default-plugin-with-sec"}, - wantExtensions: sets.Set{}, - wantSecrets: sets.Set{}, - }, - { - name: "relevant_secret_update", - proxyNamespace: "default", - request: &model.PushRequest{ - Full: true, - ConfigsUpdated: map[model.ConfigKey]struct{}{ - {Kind: gvk.Secret, Name: "default-pull-secret", Namespace: "default"}: {}, - }, - }, - watchedResources: []string{"default.default-plugin-with-sec"}, - wantExtensions: sets.Set{"default.default-plugin-with-sec": {}}, - wantSecrets: sets.Set{"default-docker-credential": {}}, - }, - { - name: "relevant_secret_update_non_full_push", - proxyNamespace: "default", - request: &model.PushRequest{ - Full: false, - ConfigsUpdated: map[model.ConfigKey]struct{}{ - {Kind: gvk.Secret, Name: "default-pull-secret", Namespace: "default"}: {}, - }, - }, - watchedResources: []string{"default.default-plugin-with-sec"}, - wantExtensions: sets.Set{"default.default-plugin-with-sec": {}}, - wantSecrets: sets.Set{"default-docker-credential": {}}, - }, - // All the credentials should be sent to istio-agent even if one of them is only updated, - // because `istio-agent` does not keep the credentials. - { - name: "multi_wasmplugin_update_secret", - proxyNamespace: "default", - request: &model.PushRequest{ - Full: false, - ConfigsUpdated: map[model.ConfigKey]struct{}{ - {Kind: gvk.Secret, Name: "default-pull-secret", Namespace: "default"}: {}, - }, - }, - watchedResources: []string{"default.default-plugin-with-sec", "dubbo-system.root-plugin"}, - wantExtensions: sets.Set{"default.default-plugin-with-sec": {}, "dubbo-system.root-plugin": {}}, - wantSecrets: sets.Set{"default-docker-credential": {}, "root-docker-credential": {}}, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ - KubernetesObjects: []runtime.Object{defaultPullSecret, rootPullSecret, wrongTypeSecret}, - Configs: []config.Config{wasmPlugin, wasmPluginWithSec, wasmPluginWrongSec, wasmPluginWrongSecType, rootWasmPluginWithSec}, - }) - - gen := s.Discovery.Generators[v3.ExtensionConfigurationType] - tt.request.Start = time.Now() - proxy := &model.Proxy{ - VerifiedIdentity: &spiffe.Identity{Namespace: tt.proxyNamespace}, - Type: model.Router, - Metadata: &model.NodeMetadata{ - ClusterID: "Kubernetes", - }, - } - tt.request.Push = s.PushContext() - tt.request.Push.Mesh.RootNamespace = "dubbo-system" - resources, _, _ := gen.Generate(s.SetupProxy(proxy), - &model.WatchedResource{ResourceNames: tt.watchedResources}, tt.request) - gotExtensions := sets.Set{} - gotSecrets := sets.Set{} - for _, res := range resources { - gotExtensions.Insert(res.Name) - ec := &corev3.TypedExtensionConfig{} - res.Resource.UnmarshalTo(ec) - wasm := &extensionsv3.Wasm{} - ec.TypedConfig.UnmarshalTo(wasm) - gotsecret := wasm.GetConfig().GetVmConfig().GetEnvironmentVariables().GetKeyValues()[model.WasmSecretEnv] - if gotsecret != "" { - gotSecrets.Insert(gotsecret) - } - } - if !reflect.DeepEqual(gotSecrets, tt.wantSecrets) { - t.Errorf("got secrets %v, want secrets %v", gotSecrets, tt.wantSecrets) - } - if !reflect.DeepEqual(gotExtensions, tt.wantExtensions) { - t.Errorf("got extensions %v, want extensions %v", gotExtensions, tt.wantExtensions) - } - }) - } -} diff --git a/pilot/pkg/xds/eds.go b/pilot/pkg/xds/eds.go deleted file mode 100644 index 58c1e17ed..000000000 --- a/pilot/pkg/xds/eds.go +++ /dev/null @@ -1,586 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "fmt" -) - -import ( - endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - any "google.golang.org/protobuf/types/known/anypb" - networkingapi "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - networking "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3/loadbalancer" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// PushType is an enumeration that decides what type push we should do when we get EDS update. -type PushType int - -const ( - // NoPush does not push any thing. - NoPush PushType = iota - // IncrementalPush just pushes endpoints. - IncrementalPush - // FullPush triggers full push - typically used for new services. - FullPush -) - -// UpdateServiceShards will list the endpoints and create the shards. -// This is used to reconcile and to support non-k8s registries (until they migrate). -// Note that aggregated list is expensive (for large numbers) - we want to replace -// it with a model where DiscoveryServer keeps track of all endpoint registries -// directly, and calls them one by one. -func (s *DiscoveryServer) UpdateServiceShards(push *model.PushContext) error { - registries := s.getNonK8sRegistries() - // Short circuit now to avoid the call to Services - if len(registries) == 0 { - return nil - } - // Each registry acts as a shard - we don't want to combine them because some - // may individually update their endpoints incrementally - for _, svc := range push.GetAllServices() { - for _, registry := range registries { - // skip the service in case this svc does not belong to the registry. - if svc.Attributes.ServiceRegistry != registry.Provider() { - continue - } - endpoints := make([]*model.IstioEndpoint, 0) - for _, port := range svc.Ports { - if port.Protocol == protocol.UDP { - continue - } - - // This loses track of grouping (shards) - for _, inst := range registry.InstancesByPort(svc, port.Port, nil) { - endpoints = append(endpoints, inst.Endpoint) - } - } - shard := model.ShardKeyFromRegistry(registry) - s.edsCacheUpdate(shard, string(svc.Hostname), svc.Attributes.Namespace, endpoints) - } - } - - return nil -} - -// SvcUpdate is a callback from service discovery when service info changes. -func (s *DiscoveryServer) SvcUpdate(shard model.ShardKey, hostname string, namespace string, event model.Event) { - // When a service deleted, we should cleanup the endpoint shards and also remove keys from EndpointIndex to - // prevent memory leaks. - if event == model.EventDelete { - inboundServiceDeletes.Increment() - s.EndpointIndex.DeleteServiceShard(shard, hostname, namespace, false) - } else { - inboundServiceUpdates.Increment() - } -} - -// EDSUpdate computes destination address membership across all clusters and networks. -// This is the main method implementing EDS. -// It replaces InstancesByPort in model - instead of iterating over all endpoints it uses -// the hostname-keyed map. And it avoids the conversion from Endpoint to ServiceEntry to envoy -// on each step: instead the conversion happens once, when an endpoint is first discovered. -func (s *DiscoveryServer) EDSUpdate(shard model.ShardKey, serviceName string, namespace string, - istioEndpoints []*model.IstioEndpoint) { - inboundEDSUpdates.Increment() - // Update the endpoint shards - pushType := s.edsCacheUpdate(shard, serviceName, namespace, istioEndpoints) - if pushType == IncrementalPush || pushType == FullPush { - // Trigger a push - s.ConfigUpdate(&model.PushRequest{ - Full: pushType == FullPush, - ConfigsUpdated: map[model.ConfigKey]struct{}{{ - Kind: gvk.ServiceEntry, - Name: serviceName, - Namespace: namespace, - }: {}}, - Reason: []model.TriggerReason{model.EndpointUpdate}, - }) - } -} - -// EDSCacheUpdate computes destination address membership across all clusters and networks. -// This is the main method implementing EDS. -// It replaces InstancesByPort in model - instead of iterating over all endpoints it uses -// the hostname-keyed map. And it avoids the conversion from Endpoint to ServiceEntry to envoy -// on each step: instead the conversion happens once, when an endpoint is first discovered. -// -// Note: the difference with `EDSUpdate` is that it only update the cache rather than requesting a push -func (s *DiscoveryServer) EDSCacheUpdate(shard model.ShardKey, serviceName string, namespace string, - istioEndpoints []*model.IstioEndpoint) { - inboundEDSUpdates.Increment() - // Update the endpoint shards - s.edsCacheUpdate(shard, serviceName, namespace, istioEndpoints) -} - -// edsCacheUpdate updates EndpointShards data by clusterID, hostname, IstioEndpoints. -// It also tracks the changes to ServiceAccounts. It returns whether endpoints need to be pushed and -// it also returns if they need to be pushed whether a full push is needed or incremental push is sufficient. -func (s *DiscoveryServer) edsCacheUpdate(shard model.ShardKey, hostname string, namespace string, - istioEndpoints []*model.IstioEndpoint) PushType { - if len(istioEndpoints) == 0 { - // Should delete the service EndpointShards when endpoints become zero to prevent memory leak, - // but we should not delete the keys from EndpointIndex map - that will trigger - // unnecessary full push which can become a real problem if a pod is in crashloop and thus endpoints - // flip flopping between 1 and 0. - s.EndpointIndex.DeleteServiceShard(shard, hostname, namespace, true) - log.Infof("Incremental push, service %s at shard %v has no endpoints", hostname, shard) - return IncrementalPush - } - - pushType := IncrementalPush - // Find endpoint shard for this service, if it is available - otherwise create a new one. - ep, created := s.EndpointIndex.GetOrCreateEndpointShard(hostname, namespace) - // If we create a new endpoint shard, that means we have not seen the service earlier. We should do a full push. - if created { - log.Infof("Full push, new service %s/%s", namespace, hostname) - pushType = FullPush - } - - ep.Lock() - defer ep.Unlock() - newIstioEndpoints := istioEndpoints - if features.SendUnhealthyEndpoints { - oldIstioEndpoints := ep.Shards[shard] - newIstioEndpoints = make([]*model.IstioEndpoint, 0, len(istioEndpoints)) - - // Check if new Endpoints are ready to be pushed. This check - // will ensure that if a new pod comes with a non ready endpoint, - // we do not unnecessarily push that config to Envoy. - // Please note that address is not a unique key. So this may not accurately - // identify based on health status and push too many times - which is ok since its an optimization. - emap := make(map[string]*model.IstioEndpoint, len(oldIstioEndpoints)) - nmap := make(map[string]*model.IstioEndpoint, len(newIstioEndpoints)) - // Add new endpoints only if they are ever ready once to shards - // so that full push does not send them from shards. - for _, oie := range oldIstioEndpoints { - emap[oie.Address] = oie - } - for _, nie := range istioEndpoints { - nmap[nie.Address] = nie - } - needPush := false - for _, nie := range istioEndpoints { - if oie, exists := emap[nie.Address]; exists { - // If endpoint exists already, we should push if it's health status changes. - if oie.HealthStatus != nie.HealthStatus { - needPush = true - } - newIstioEndpoints = append(newIstioEndpoints, nie) - } else if nie.HealthStatus == model.Healthy { - // If the endpoint does not exist in shards that means it is a - // new endpoint. Only send if it is healthy to avoid pushing endpoints - // that are not ready to start with. - needPush = true - newIstioEndpoints = append(newIstioEndpoints, nie) - } - } - // Next, check for endpoints that were in old but no longer exist. If there are any, there is a - // removal so we need to push an update. - for _, oie := range oldIstioEndpoints { - if _, f := nmap[oie.Address]; !f { - needPush = true - } - } - - if pushType != FullPush && !needPush { - log.Debugf("No push, either old endpoint health status did not change or new endpoint came with unhealthy status, %v", hostname) - pushType = NoPush - } - - } - - ep.Shards[shard] = newIstioEndpoints - - // Check if ServiceAccounts have changed. We should do a full push if they have changed. - saUpdated := s.UpdateServiceAccount(ep, hostname) - - // For existing endpoints, we need to do full push if service accounts change. - if saUpdated && pushType != FullPush { - // Avoid extra logging if already a full push - log.Infof("Full push, service accounts changed, %v", hostname) - pushType = FullPush - } - - // Clear the cache here. While it would likely be cleared later when we trigger a push, a race - // condition is introduced where an XDS response may be generated before the update, but not - // completed until after a response after the update. Essentially, we transition from v0 -> v1 -> - // v0 -> invalidate -> v1. Reverting a change we pushed violates our contract of monotonically - // moving forward in version. In practice, this is pretty rare and self corrects nearly - // immediately. However, clearing the cache here has almost no impact on cache performance as we - // would clear it shortly after anyways. - s.Cache.Clear(map[model.ConfigKey]struct{}{{ - Kind: gvk.ServiceEntry, - Name: hostname, - Namespace: namespace, - }: {}}) - - return pushType -} - -func (s *DiscoveryServer) RemoveShard(shardKey model.ShardKey) { - s.EndpointIndex.DeleteShard(shardKey) -} - -// UpdateServiceAccount updates the service endpoints' sa when service/endpoint event happens. -// Note: it is not concurrent safe. -func (s *DiscoveryServer) UpdateServiceAccount(shards *model.EndpointShards, serviceName string) bool { - oldServiceAccount := shards.ServiceAccounts - serviceAccounts := sets.Set{} - for _, epShards := range shards.Shards { - for _, ep := range epShards { - if ep.ServiceAccount != "" { - serviceAccounts.Insert(ep.ServiceAccount) - } - } - } - - if !oldServiceAccount.Equals(serviceAccounts) { - shards.ServiceAccounts = serviceAccounts - log.Debugf("Updating service accounts now, svc %v, before service account %v, after %v", - serviceName, oldServiceAccount, serviceAccounts) - return true - } - - return false -} - -// llbEndpointAndOptionsForCluster return the endpoints for a cluster -// Initial implementation is computing the endpoints on the flight - caching will be added as needed, based on -// perf tests. -func (s *DiscoveryServer) llbEndpointAndOptionsForCluster(b EndpointBuilder) ([]*LocLbEndpointsAndOptions, error) { - if b.service == nil { - // Shouldn't happen here - log.Debugf("can not find the service for cluster %s", b.clusterName) - return nil, nil - } - - // Service resolution type might have changed and Cluster may be still in the EDS cluster list of "Connection.Clusters". - // This can happen if a ServiceEntry's resolution is changed from STATIC to DNS which changes the Envoy cluster type from - // EDS to STRICT_DNS or LOGICAL_DNS. When pushEds is called before Envoy sends the updated cluster list via Endpoint request which in turn - // will update "Connection.Clusters", we might accidentally send EDS updates for STRICT_DNS cluster. This check guards - // against such behavior and returns nil. When the updated cluster warms up in Envoy, it would update with new endpoints - // automatically. - // Gateways use EDS for Passthrough cluster. So we should allow Passthrough here. - if b.service.Resolution == model.DNSLB || b.service.Resolution == model.DNSRoundRobinLB { - log.Infof("cluster %s in eds cluster, but its resolution now is updated to %v, skipping it.", b.clusterName, b.service.Resolution) - return nil, fmt.Errorf("cluster %s in eds cluster", b.clusterName) - } - - svcPort, f := b.service.Ports.GetByPort(b.port) - if !f { - // Shouldn't happen here - log.Debugf("can not find the service port %d for cluster %s", b.port, b.clusterName) - return nil, nil - } - - epShards, f := s.EndpointIndex.ShardsForService(string(b.hostname), b.service.Attributes.Namespace) - if !f { - // Shouldn't happen here - log.Debugf("can not find the endpointShards for cluster %s", b.clusterName) - return nil, nil - } - - return b.buildLocalityLbEndpointsFromShards(epShards, svcPort), nil -} - -func (s *DiscoveryServer) generateEndpoints(b EndpointBuilder) *endpoint.ClusterLoadAssignment { - llbOpts, err := s.llbEndpointAndOptionsForCluster(b) - if err != nil { - return buildEmptyClusterLoadAssignment(b.clusterName) - } - - // Apply the Split Horizon EDS filter, if applicable. - llbOpts = b.EndpointsByNetworkFilter(llbOpts) - - if model.IsDNSSrvSubsetKey(b.clusterName) { - // For the SNI-DNAT clusters, we are using AUTO_PASSTHROUGH gateway. AUTO_PASSTHROUGH is intended - // to passthrough mTLS requests. However, at the gateway we do not actually have any way to tell if the - // request is a valid mTLS request or not, since its passthrough TLS. - // To ensure we allow traffic only to mTLS endpoints, we filter out non-mTLS endpoints for these cluster types. - llbOpts = b.EndpointsWithMTLSFilter(llbOpts) - } - llbOpts = b.ApplyTunnelSetting(llbOpts, b.tunnelType) - - l := b.createClusterLoadAssignment(llbOpts) - - // If locality aware routing is enabled, prioritize endpoints or set their lb weight. - // Failover should only be enabled when there is an outlier detection, otherwise Envoy - // will never detect the hosts are unhealthy and redirect traffic. - enableFailover, lb := getOutlierDetectionAndLoadBalancerSettings(b.DestinationRule(), b.port, b.subsetName) - lbSetting := loadbalancer.GetLocalityLbSetting(b.push.Mesh.GetLocalityLbSetting(), lb.GetLocalityLbSetting()) - if lbSetting != nil { - // Make a shallow copy of the cla as we are mutating the endpoints with priorities/weights relative to the calling proxy - l = util.CloneClusterLoadAssignment(l) - wrappedLocalityLbEndpoints := make([]*loadbalancer.WrappedLocalityLbEndpoints, len(llbOpts)) - for i := range llbOpts { - wrappedLocalityLbEndpoints[i] = &loadbalancer.WrappedLocalityLbEndpoints{ - IstioEndpoints: llbOpts[i].istioEndpoints, - LocalityLbEndpoints: l.Endpoints[i], - } - } - loadbalancer.ApplyLocalityLBSetting(l, wrappedLocalityLbEndpoints, b.locality, b.proxy.Metadata.Labels, lbSetting, enableFailover) - } - return l -} - -// EdsGenerator implements the new Generate method for EDS, using the in-memory, optimized endpoint -// storage in DiscoveryServer. -type EdsGenerator struct { - Server *DiscoveryServer -} - -var _ model.XdsDeltaResourceGenerator = &EdsGenerator{} - -// Map of all configs that do not impact EDS -var skippedEdsConfigs = map[config.GroupVersionKind]struct{}{ - gvk.Gateway: {}, - gvk.VirtualService: {}, - gvk.WorkloadGroup: {}, - gvk.AuthorizationPolicy: {}, - gvk.RequestAuthentication: {}, - gvk.Secret: {}, - gvk.Telemetry: {}, - gvk.WasmPlugin: {}, - gvk.ProxyConfig: {}, -} - -func edsNeedsPush(updates model.XdsUpdates) bool { - // If none set, we will always push - if len(updates) == 0 { - return true - } - for config := range updates { - if _, f := skippedEdsConfigs[config.Kind]; !f { - return true - } - } - return false -} - -func (eds *EdsGenerator) Generate(proxy *model.Proxy, w *model.WatchedResource, req *model.PushRequest) (model.Resources, model.XdsLogDetails, error) { - if !edsNeedsPush(req.ConfigsUpdated) { - return nil, model.DefaultXdsLogDetails, nil - } - resources, logDetails := eds.buildEndpoints(proxy, req, w) - return resources, logDetails, nil -} - -func getOutlierDetectionAndLoadBalancerSettings( - destinationRule *networkingapi.DestinationRule, - portNumber int, - subsetName string) (bool, *networkingapi.LoadBalancerSettings) { - if destinationRule == nil { - return false, nil - } - outlierDetectionEnabled := false - var lbSettings *networkingapi.LoadBalancerSettings - - port := &model.Port{Port: portNumber} - policy := networking.MergeTrafficPolicy(nil, destinationRule.TrafficPolicy, port) - - for _, subset := range destinationRule.Subsets { - if subset.Name == subsetName { - policy = networking.MergeTrafficPolicy(policy, subset.TrafficPolicy, port) - break - } - } - - if policy != nil { - lbSettings = policy.LoadBalancer - if policy.OutlierDetection != nil { - outlierDetectionEnabled = true - } - } - - return outlierDetectionEnabled, lbSettings -} - -func endpointDiscoveryResponse(loadAssignments []*any.Any, version, noncePrefix string) *discovery.DiscoveryResponse { - out := &discovery.DiscoveryResponse{ - TypeUrl: v3.EndpointType, - // Pilot does not really care for versioning. It always supplies what's currently - // available to it, irrespective of whether Envoy chooses to accept or reject EDS - // responses. Pilot believes in eventual consistency and that at some point, Envoy - // will begin seeing results it deems to be good. - VersionInfo: version, - Nonce: nonce(noncePrefix), - Resources: loadAssignments, - } - - return out -} - -// cluster with no endpoints -func buildEmptyClusterLoadAssignment(clusterName string) *endpoint.ClusterLoadAssignment { - return &endpoint.ClusterLoadAssignment{ - ClusterName: clusterName, - } -} - -func (eds *EdsGenerator) GenerateDeltas(proxy *model.Proxy, req *model.PushRequest, - w *model.WatchedResource) (model.Resources, model.DeletedResources, model.XdsLogDetails, bool, error) { - if !edsNeedsPush(req.ConfigsUpdated) { - return nil, nil, model.DefaultXdsLogDetails, false, nil - } - if !shouldUseDeltaEds(req) { - resources, logDetails := eds.buildEndpoints(proxy, req, w) - return resources, nil, logDetails, false, nil - } - - resources, removed, logs := eds.buildDeltaEndpoints(proxy, req, w) - return resources, removed, logs, true, nil -} - -func shouldUseDeltaEds(req *model.PushRequest) bool { - if !req.Full { - return false - } - return onlyEndpointsChanged(req) -} - -// onlyEndpointsChanged checks if a request contains *only* endpoints updates. This allows us to perform more efficient pushes -// where we only update the endpoints that did change. -func onlyEndpointsChanged(req *model.PushRequest) bool { - if len(req.ConfigsUpdated) > 0 { - for k := range req.ConfigsUpdated { - if k.Kind != gvk.ServiceEntry { - return false - } - } - return true - } - return false -} - -func (eds *EdsGenerator) buildEndpoints(proxy *model.Proxy, - req *model.PushRequest, - w *model.WatchedResource) (model.Resources, model.XdsLogDetails) { - var edsUpdatedServices map[string]struct{} - // canSendPartialFullPushes determines if we can send a partial push (ie a subset of known CLAs). - // This is safe when only Services has changed, as this implies that only the CLAs for the - // associated Service changed. Note when a multi-network Service changes it triggers a push with - // ConfigsUpdated=ALL, so in this case we would not enable a partial push. - // Despite this code existing on the SotW code path, sending these partial pushes is still allowed; - // see https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol#grouping-resources-into-responses - if !req.Full || (features.PartialFullPushes && onlyEndpointsChanged(req)) { - edsUpdatedServices = model.ConfigNamesOfKind(req.ConfigsUpdated, gvk.ServiceEntry) - } - var resources model.Resources - empty := 0 - cached := 0 - regenerated := 0 - for _, clusterName := range w.ResourceNames { - if edsUpdatedServices != nil { - _, _, hostname, _ := model.ParseSubsetKey(clusterName) - if _, ok := edsUpdatedServices[string(hostname)]; !ok { - // Cluster was not updated, skip recomputing. This happens when we get an incremental update for a - // specific Hostname. On connect or for full push edsUpdatedServices will be empty. - continue - } - } - builder := NewEndpointBuilder(clusterName, proxy, req.Push) - if marshalledEndpoint, f := eds.Server.Cache.Get(builder); f && !features.EnableUnsafeAssertions { - // We skip cache if assertions are enabled, so that the cache will assert our eviction logic is correct - resources = append(resources, marshalledEndpoint) - cached++ - } else { - l := eds.Server.generateEndpoints(builder) - if l == nil { - continue - } - regenerated++ - - if len(l.Endpoints) == 0 { - empty++ - } - resource := &discovery.Resource{ - Name: l.ClusterName, - Resource: util.MessageToAny(l), - } - resources = append(resources, resource) - eds.Server.Cache.Add(builder, req, resource) - } - } - return resources, model.XdsLogDetails{ - Incremental: len(edsUpdatedServices) != 0, - AdditionalInfo: fmt.Sprintf("empty:%v cached:%v/%v", empty, cached, cached+regenerated), - } -} - -// TODO(@hzxuzhonghu): merge with buildEndpoints -func (eds *EdsGenerator) buildDeltaEndpoints(proxy *model.Proxy, - req *model.PushRequest, - w *model.WatchedResource) (model.Resources, []string, model.XdsLogDetails) { - edsUpdatedServices := model.ConfigNamesOfKind(req.ConfigsUpdated, gvk.ServiceEntry) - var resources model.Resources - var removed []string - empty := 0 - cached := 0 - regenerated := 0 - - for _, clusterName := range w.ResourceNames { - // filter out eds that are not updated for clusters - _, _, hostname, _ := model.ParseSubsetKey(clusterName) - if _, ok := edsUpdatedServices[string(hostname)]; !ok { - continue - } - - builder := NewEndpointBuilder(clusterName, proxy, req.Push) - // if a service is not found, it means the cluster is removed - if builder.service == nil { - removed = append(removed, clusterName) - continue - } - if marshalledEndpoint, f := eds.Server.Cache.Get(builder); f && !features.EnableUnsafeAssertions { - // We skip cache if assertions are enabled, so that the cache will assert our eviction logic is correct - resources = append(resources, marshalledEndpoint) - cached++ - } else { - l := eds.Server.generateEndpoints(builder) - if l == nil { - removed = append(removed, clusterName) - continue - } - regenerated++ - if len(l.Endpoints) == 0 { - empty++ - } - resource := &discovery.Resource{ - Name: l.ClusterName, - Resource: util.MessageToAny(l), - } - resources = append(resources, resource) - eds.Server.Cache.Add(builder, req, resource) - } - } - return resources, removed, model.XdsLogDetails{ - Incremental: len(edsUpdatedServices) != 0, - AdditionalInfo: fmt.Sprintf("empty:%v cached:%v/%v", empty, cached, cached+regenerated), - } -} diff --git a/pilot/pkg/xds/eds_sh_test.go b/pilot/pkg/xds/eds_sh_test.go deleted file mode 100644 index c59903e6c..000000000 --- a/pilot/pkg/xds/eds_sh_test.go +++ /dev/null @@ -1,351 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package xds_test - -import ( - "fmt" - "sort" - "testing" - "time" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "google.golang.org/protobuf/types/known/structpb" - meshconfig "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/aggregate" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/network" -) - -// Testing the Split Horizon EDS. -type expectedResults struct { - weights map[string]uint32 -} - -func (r expectedResults) getAddrs() []string { - var out []string - for addr := range r.weights { - out = append(out, addr) - } - sort.Strings(out) - return out -} - -// The test will setup 3 networks with various number of endpoints for the same service within -// each network. It creates an instance of memory registry for each cluster and populate it -// with Service, Instances and an ingress gateway service. -// It then conducts an EDS query from each network expecting results to match the design of -// the Split Horizon EDS - all local endpoints + endpoint per remote network that also has -// endpoints for the service. -func TestSplitHorizonEds(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{NetworksWatcher: mesh.NewFixedNetworksWatcher(nil)}) - - // Set up a cluster registry for network 1 with 1 instance for the service 'service5' - // Network has 1 gateway - initRegistry(s, 1, []string{"159.122.219.1"}, 1) - // Set up a cluster registry for network 2 with 2 instances for the service 'service5' - // Network has 1 gateway - initRegistry(s, 2, []string{"159.122.219.2"}, 2) - // Set up a cluster registry for network 3 with 3 instances for the service 'service5' - // Network has 2 gateways - initRegistry(s, 3, []string{"159.122.219.3", "179.114.119.3"}, 3) - // Set up a cluster registry for network 4 with 4 instances for the service 'service5' - // but without any gateway, which is treated as accessible directly. - initRegistry(s, 4, []string{}, 4) - - // Push contexts needs to be updated - s.Discovery.ConfigUpdate(&model.PushRequest{Full: true}) - time.Sleep(time.Millisecond * 200) // give time for cache to clear - - fmt.Println("gateways", s.Env().NetworkManager.AllGateways()) - - tests := []struct { - network string - sidecarID string - want expectedResults - }{ - { - // Verify that EDS from network1 will return 1 local endpoint with local VIP + 2 remote - // endpoints weighted accordingly with the IP of the ingress gateway. - network: "network1", - sidecarID: sidecarID("10.1.0.1", "app3"), - want: expectedResults{ - weights: map[string]uint32{ - // 1 local endpoint - "10.1.0.1": 2, - - // 2 endopints on network 2, go through single gateway. - "159.122.219.2": 4, - - // 3 endpoints on network 3, weights split across 2 gateways. - "159.122.219.3": 3, - "179.114.119.3": 3, - - // no gateway defined for network 4 - treat as directly reachable. - "10.4.0.1": 2, - "10.4.0.2": 2, - "10.4.0.3": 2, - "10.4.0.4": 2, - }, - }, - }, - { - // Verify that EDS from network2 will return 2 local endpoints with local VIPs + 2 remote - // endpoints weighted accordingly with the IP of the ingress gateway. - network: "network2", - sidecarID: sidecarID("10.2.0.1", "app3"), - want: expectedResults{ - weights: map[string]uint32{ - // 2 local endpoints - "10.2.0.1": 2, - "10.2.0.2": 2, - - // 1 endpoint on network 1, accessed via gateway. - "159.122.219.1": 2, - - // 3 endpoints on network 3, weights split across 2 gateways. - "159.122.219.3": 3, - "179.114.119.3": 3, - - // no gateway defined for network 4 - treat as directly reachable. - "10.4.0.1": 2, - "10.4.0.2": 2, - "10.4.0.3": 2, - "10.4.0.4": 2, - }, - }, - }, - { - // Verify that EDS from network3 will return 3 local endpoints with local VIPs + 2 remote - // endpoints weighted accordingly with the IP of the ingress gateway. - network: "network3", - sidecarID: sidecarID("10.3.0.1", "app3"), - want: expectedResults{ - weights: map[string]uint32{ - // 3 local endpoints. - "10.3.0.1": 2, - "10.3.0.2": 2, - "10.3.0.3": 2, - - // 1 endpoint on network 1, accessed via gateway. - "159.122.219.1": 2, - - // 2 endpoint on network 2, accessed via gateway. - "159.122.219.2": 4, - - // no gateway defined for network 4 - treat as directly reachable. - "10.4.0.1": 2, - "10.4.0.2": 2, - "10.4.0.3": 2, - "10.4.0.4": 2, - }, - }, - }, - { - // Verify that EDS from network4 will return 4 local endpoint with local VIP + 4 remote - // endpoints weighted accordingly with the IP of the ingress gateway. - network: "network4", - sidecarID: sidecarID("10.4.0.1", "app3"), - want: expectedResults{ - weights: map[string]uint32{ - // 4 local endpoints. - "10.4.0.1": 2, - "10.4.0.2": 2, - "10.4.0.3": 2, - "10.4.0.4": 2, - - // 1 endpoint on network 1, accessed via gateway. - "159.122.219.1": 2, - - // 2 endpoint on network 2, accessed via gateway. - "159.122.219.2": 4, - - // 3 endpoints on network 3, weights split across 2 gateways. - "159.122.219.3": 3, - "179.114.119.3": 3, - }, - }, - }, - } - for _, tt := range tests { - t.Run("from "+tt.network, func(t *testing.T) { - verifySplitHorizonResponse(t, s, tt.network, tt.sidecarID, tt.want) - }) - } -} - -// Tests whether an EDS response from the provided network matches the expected results -func verifySplitHorizonResponse(t *testing.T, s *xds.FakeDiscoveryServer, network string, sidecarID string, expected expectedResults) { - t.Helper() - ads := s.ConnectADS().WithID(sidecarID) - - metadata := &structpb.Struct{Fields: map[string]*structpb.Value{ - "ISTIO_VERSION": {Kind: &structpb.Value_StringValue{StringValue: "1.3"}}, - "NETWORK": {Kind: &structpb.Value_StringValue{StringValue: network}}, - }} - - ads.RequestResponseAck(t, &discovery.DiscoveryRequest{ - Node: &core.Node{ - Id: ads.ID, - Metadata: metadata, - }, - TypeUrl: v3.ClusterType, - }) - - clusterName := "outbound|1080||service5.default.svc.cluster.local" - res := ads.RequestResponseAck(t, &discovery.DiscoveryRequest{ - Node: &core.Node{ - Id: ads.ID, - Metadata: metadata, - }, - TypeUrl: v3.EndpointType, - ResourceNames: []string{clusterName}, - }) - cla := xdstest.UnmarshalClusterLoadAssignment(t, res.Resources)[0] - eps := cla.Endpoints - - if len(eps) != 1 { - t.Fatalf("expecting 1 locality endpoint but got %d", len(eps)) - } - - lbEndpoints := eps[0].LbEndpoints - if len(lbEndpoints) != len(expected.weights) { - t.Fatalf("unexpected number of endpoints.\nWant:\n%v\nGot:\n%v", expected.getAddrs(), getLbEndpointAddrs(lbEndpoints)) - } - - for addr, weight := range expected.weights { - var match *endpoint.LbEndpoint - for _, ep := range lbEndpoints { - if ep.GetEndpoint().Address.GetSocketAddress().Address == addr { - match = ep - break - } - } - if match == nil { - t.Fatalf("couldn't find endpoint with address %s: found %v", addr, getLbEndpointAddrs(lbEndpoints)) - } - if match.LoadBalancingWeight.Value != weight { - t.Errorf("weight for endpoint %s is expected to be %d but got %d", addr, weight, match.LoadBalancingWeight.Value) - } - } -} - -// initRegistry creates and initializes a memory registry that holds a single -// service with the provided amount of endpoints. It also creates a service for -// the ingress with the provided external IP -func initRegistry(server *xds.FakeDiscoveryServer, networkNum int, gatewaysIP []string, numOfEndpoints int) { - clusterID := cluster.ID(fmt.Sprintf("cluster%d", networkNum)) - networkID := network.ID(fmt.Sprintf("network%d", networkNum)) - memRegistry := memory.NewServiceDiscovery() - memRegistry.EDSUpdater = server.Discovery - - server.Env().ServiceDiscovery.(*aggregate.Controller).AddRegistry(serviceregistry.Simple{ - ClusterID: clusterID, - ProviderID: provider.Mock, - ServiceDiscovery: memRegistry, - Controller: &memory.ServiceController{}, - }) - - gws := make([]*meshconfig.Network_IstioNetworkGateway, 0) - for _, gatewayIP := range gatewaysIP { - if gatewayIP != "" { - gw := &meshconfig.Network_IstioNetworkGateway{ - Gw: &meshconfig.Network_IstioNetworkGateway_Address{ - Address: gatewayIP, - }, - Port: 80, - } - gws = append(gws, gw) - } - } - - if len(gws) != 0 { - addNetwork(server, networkID, &meshconfig.Network{ - Gateways: gws, - }) - } - - svcLabels := map[string]string{ - "version": "v1.1", - } - - // Explicit test service, in the v2 memory registry. Similar with mock.MakeService, - // but easier to read. - memRegistry.AddService(&model.Service{ - Hostname: "service5.default.svc.cluster.local", - DefaultAddress: "10.10.0.1", - Ports: []*model.Port{ - { - Name: "http-main", - Port: 1080, - Protocol: protocol.HTTP, - }, - }, - }) - istioEndpoints := make([]*model.IstioEndpoint, numOfEndpoints) - for i := 0; i < numOfEndpoints; i++ { - addr := fmt.Sprintf("10.%d.0.%d", networkNum, i+1) - istioEndpoints[i] = &model.IstioEndpoint{ - Address: addr, - EndpointPort: 2080, - ServicePortName: "http-main", - Network: networkID, - Locality: model.Locality{ - Label: "az", - ClusterID: clusterID, - }, - Labels: svcLabels, - TLSMode: model.IstioMutualTLSModeLabel, - } - } - memRegistry.SetEndpoints("service5.default.svc.cluster.local", "default", istioEndpoints) -} - -func addNetwork(server *xds.FakeDiscoveryServer, id network.ID, network *meshconfig.Network) { - meshNetworks := server.Env().NetworksWatcher.Networks() - // copy old networks if they exist - c := map[string]*meshconfig.Network{} - if meshNetworks != nil { - for k, v := range meshNetworks.Networks { - c[k] = v - } - } - // add the new one - c[string(id)] = network - server.Env().NetworksWatcher.SetNetworks(&meshconfig.MeshNetworks{Networks: c}) -} - -func getLbEndpointAddrs(eps []*endpoint.LbEndpoint) []string { - addrs := make([]string, 0) - for _, lbEp := range eps { - addrs = append(addrs, lbEp.GetEndpoint().Address.GetSocketAddress().Address) - } - sort.Strings(addrs) - return addrs -} diff --git a/pilot/pkg/xds/eds_test.go b/pilot/pkg/xds/eds_test.go deleted file mode 100644 index 4a031b22e..000000000 --- a/pilot/pkg/xds/eds_test.go +++ /dev/null @@ -1,1373 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package xds_test - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "net/http/httptest" - "os" - "path/filepath" - "reflect" - "runtime" - "sort" - "strconv" - "strings" - "sync" - "testing" - "time" -) - -import ( - endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" - uatomic "go.uber.org/atomic" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/adsc" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// The connect and reconnect tests are removed - ADS already has coverage, and the -// StreamEndpoints is not used in 1.0+ -const ( - asdcLocality = "region1/zone1/subzone1" - asdc2Locality = "region2/zone2/subzone2" - - edsIncSvc = "eds.test.svc.cluster.local" - edsIncVip = "10.10.1.2" -) - -func TestIncrementalPush(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ - ConfigString: mustReadFile(t, "tests/testdata/config/destination-rule-all.yaml") + - mustReadFile(t, "tests/testdata/config/static-weighted-se.yaml"), - }) - ads := s.Connect(nil, nil, watchAll) - t.Run("Full Push", func(t *testing.T) { - s.Discovery.Push(&model.PushRequest{Full: true}) - if _, err := ads.Wait(time.Second*5, watchAll...); err != nil { - t.Fatal(err) - } - }) - t.Run("Incremental Push with updated services", func(t *testing.T) { - ads.WaitClear() - s.Discovery.Push(&model.PushRequest{ - Full: false, - ConfigsUpdated: map[model.ConfigKey]struct{}{ - {Name: "destall.default.svc.cluster.local", Namespace: "testns", Kind: gvk.ServiceEntry}: {}, - }, - }) - if err := ads.WaitSingle(time.Second*5, v3.EndpointType, v3.ClusterType); err != nil { - t.Fatal(err) - } - }) - t.Run("Full Push with updated services", func(t *testing.T) { - ads.WaitClear() - - s.Discovery.Push(&model.PushRequest{ - Full: true, - ConfigsUpdated: map[model.ConfigKey]struct{}{ - {Name: "weighted.static.svc.cluster.local", Namespace: "default", Kind: gvk.ServiceEntry}: {}, - }, - }) - if _, err := ads.Wait(time.Second*5, watchAll...); err != nil { - t.Fatal(err) - } - if len(ads.GetEndpoints()) != 4 { - t.Fatalf("Expected a partial EDS update, but got: %v", xdstest.MapKeys(ads.GetEndpoints())) - } - }) - t.Run("Full Push with multiple updates", func(t *testing.T) { - ads.WaitClear() - s.Discovery.Push(&model.PushRequest{ - Full: true, - ConfigsUpdated: map[model.ConfigKey]struct{}{ - {Name: "foo.bar", Namespace: "default", Kind: gvk.ServiceEntry}: {}, - {Name: "destall", Namespace: "testns", Kind: gvk.DestinationRule}: {}, - }, - }) - if _, err := ads.Wait(time.Second*5, watchAll...); err != nil { - t.Fatal(err) - } - if len(ads.GetEndpoints()) != 4 { - t.Fatalf("Expected a full EDS update, but got: %v", xdstest.MapKeys(ads.GetEndpoints())) - } - }) - t.Run("Full Push without updated services", func(t *testing.T) { - ads.WaitClear() - s.Discovery.Push(&model.PushRequest{ - Full: true, - ConfigsUpdated: map[model.ConfigKey]struct{}{ - {Name: "destall", Namespace: "testns", Kind: gvk.DestinationRule}: {}, - }, - }) - if _, err := ads.Wait(time.Second*5, v3.ClusterType, v3.EndpointType); err != nil { - t.Fatal(err) - } - if len(ads.GetEndpoints()) < 3 { - t.Fatalf("Expected a full EDS update, but got: %v", ads.GetEndpoints()) - } - }) -} - -func TestEds(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ - ConfigString: mustReadFile(t, "tests/testdata/config/destination-rule-locality.yaml"), - DiscoveryServerModifier: func(s *xds.DiscoveryServer) { - addUdsEndpoint(s) - - // enable locality load balancing and add relevant endpoints in order to test - addLocalityEndpoints(s, "locality.cluster.local") - addLocalityEndpoints(s, "locality-no-outlier-detection.cluster.local") - - // Add the test ads clients to list of service instances in order to test the context dependent locality coloring. - addTestClientEndpoints(s) - - s.MemRegistry.AddHTTPService(edsIncSvc, edsIncVip, 8080) - s.MemRegistry.SetEndpoints(edsIncSvc, "", - newEndpointWithAccount("127.0.0.1", "hello-sa", "v1")) - }, - }) - - adscConn := s.Connect(&model.Proxy{IPAddresses: []string{"10.10.10.10"}}, nil, watchAll) - adscConn2 := s.Connect(&model.Proxy{IPAddresses: []string{"10.10.10.11"}}, nil, watchAll) - - t.Run("TCPEndpoints", func(t *testing.T) { - testTCPEndpoints("127.0.0.1", adscConn, t) - }) - t.Run("edsz", func(t *testing.T) { - testEdsz(t, s, "test-1.default") - }) - t.Run("LocalityPrioritizedEndpoints", func(t *testing.T) { - testLocalityPrioritizedEndpoints(adscConn, adscConn2, t) - }) - t.Run("UDSEndpoints", func(t *testing.T) { - testUdsEndpoints(adscConn, t) - }) - t.Run("PushIncremental", func(t *testing.T) { - edsUpdateInc(s, adscConn, t) - }) - t.Run("Push", func(t *testing.T) { - edsUpdates(s, adscConn, t) - }) - t.Run("MultipleRequest", func(t *testing.T) { - multipleRequest(s, false, 20, 5, 25*time.Second, nil, t) - }) - // 5 pushes for 100 clients, using EDS incremental only. - t.Run("MultipleRequestIncremental", func(t *testing.T) { - multipleRequest(s, true, 20, 5, 25*time.Second, nil, t) - }) - t.Run("CDSSave", func(t *testing.T) { - // Moved from cds_test, using new client - clusters := adscConn.GetClusters() - if len(clusters) == 0 { - t.Error("No clusters in ADS response") - } - strResponse, _ := json.MarshalIndent(clusters, " ", " ") - _ = os.WriteFile(env.IstioOut+"/cdsv2_sidecar.json", strResponse, 0o644) - }) -} - -// newEndpointWithAccount is a helper for IstioEndpoint creation. Creates endpoints with -// port name "http", with the given IP, service account and a 'version' label. -// nolint: unparam -func newEndpointWithAccount(ip, account, version string) []*model.IstioEndpoint { - return []*model.IstioEndpoint{ - { - Address: ip, - ServicePortName: "http-main", - EndpointPort: 80, - Labels: map[string]string{"version": version}, - ServiceAccount: account, - }, - } -} - -func TestTunnelServerEndpointEds(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - s.Discovery.MemRegistry.AddHTTPService(edsIncSvc, edsIncVip, 8080) - s.Discovery.MemRegistry.SetEndpoints(edsIncSvc, "", - []*model.IstioEndpoint{ - { - Address: "127.0.0.1", - ServicePortName: "http-main", - EndpointPort: 80, - // Labels: map[string]string{"version": version}, - ServiceAccount: "hello-sa", - TunnelAbility: networking.MakeTunnelAbility(networking.H2Tunnel), - }, - }) - - t.Run("TestClientWantsTunnelEndpoints", func(t *testing.T) { - t.Helper() - adscConn1 := s.Connect(&model.Proxy{IPAddresses: []string{"10.10.10.10"}, Metadata: &model.NodeMetadata{ - ProxyConfig: &model.NodeMetaProxyConfig{ - ProxyMetadata: map[string]string{ - "tunnel": networking.H2TunnelTypeName, - }, - }, - }}, nil, watchAll) - testTunnelEndpoints("127.0.0.1", 15009, adscConn1, t) - }) - t.Run("TestClientWantsNoTunnelEndpoints", func(t *testing.T) { - t.Helper() - adscConn2 := s.Connect(&model.Proxy{IPAddresses: []string{"10.10.10.11"}, Metadata: &model.NodeMetadata{ - ProxyConfig: &model.NodeMetaProxyConfig{}, - }}, nil, watchAll) - testTunnelEndpoints("127.0.0.1", 80, adscConn2, t) - }) -} - -func TestNoTunnelServerEndpointEds(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - - // Add the test ads clients to list of service instances in order to test the context dependent locality coloring. - addTestClientEndpoints(s.Discovery) - - s.Discovery.MemRegistry.AddHTTPService(edsIncSvc, edsIncVip, 8080) - s.Discovery.MemRegistry.SetEndpoints(edsIncSvc, "", - []*model.IstioEndpoint{ - { - Address: "127.0.0.1", - ServicePortName: "http-main", - EndpointPort: 80, - // Labels: map[string]string{"version": version}, - ServiceAccount: "hello-sa", - // No Tunnel Support at this endpoint. - TunnelAbility: networking.MakeTunnelAbility(), - }, - }) - - t.Run("TestClientWantsTunnelEndpoints", func(t *testing.T) { - adscConn := s.Connect(&model.Proxy{IPAddresses: []string{"10.10.10.10"}, Metadata: &model.NodeMetadata{ - ProxyConfig: &model.NodeMetaProxyConfig{ - ProxyMetadata: map[string]string{ - "tunnel": networking.H2TunnelTypeName, - }, - }, - }}, nil, watchAll) - testTunnelEndpoints("127.0.0.1", 80, adscConn, t) - }) - - t.Run("TestClientWantsNoTunnelEndpoints", func(t *testing.T) { - adscConn := s.Connect(&model.Proxy{IPAddresses: []string{"10.10.10.11"}, Metadata: &model.NodeMetadata{}}, nil, watchAll) - testTunnelEndpoints("127.0.0.1", 80, adscConn, t) - }) -} - -func mustReadFile(t *testing.T, fpaths ...string) string { - result := "" - for _, fpath := range fpaths { - if !strings.HasPrefix(fpath, ".") { - fpath = filepath.Join(env.IstioSrc, fpath) - } - bytes, err := os.ReadFile(fpath) - if err != nil { - t.Fatal(err) - } - result += "---\n" - result += string(bytes) - } - return result -} - -func mustReadfolder(t *testing.T, folder string) string { - result := "" - fpathRoot := folder - if !strings.HasPrefix(fpathRoot, ".") { - fpathRoot = filepath.Join(env.IstioSrc, folder) - } - f, err := os.ReadDir(fpathRoot) - if err != nil { - t.Fatal(err) - } - for _, fpath := range f { - bytes, err := os.ReadFile(filepath.Join(fpathRoot, fpath.Name())) - if err != nil { - t.Fatal(err) - } - result += "---\n" - result += string(bytes) - } - return result -} - -func TestEdsWeightedServiceEntry(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ConfigString: mustReadFile(t, "tests/testdata/config/static-weighted-se.yaml")}) - adscConn := s.Connect(nil, nil, watchEds) - endpoints := adscConn.GetEndpoints() - lbe, f := endpoints["outbound|80||weighted.static.svc.cluster.local"] - if !f || len(lbe.Endpoints) == 0 { - t.Fatalf("No lb endpoints for %v, %v", "outbound|80||weighted.static.svc.cluster.local", adscConn.EndpointsJSON()) - } - expected := map[string]uint32{ - "a": 9, // sum of 1 and 8 - "b": 3, - "3.3.3.3": 1, // no weight provided is normalized to 1 - "2.2.2.2": 8, - "1.1.1.1": 3, - } - got := make(map[string]uint32) - for _, lbe := range lbe.Endpoints { - got[lbe.Locality.Region] = lbe.LoadBalancingWeight.Value - for _, e := range lbe.LbEndpoints { - got[e.GetEndpoint().Address.GetSocketAddress().Address] = e.LoadBalancingWeight.Value - } - } - if !reflect.DeepEqual(expected, got) { - t.Errorf("Expected LB weights %v got %v", expected, got) - } -} - -var ( - watchEds = []string{v3.ClusterType, v3.EndpointType} - watchAll = []string{v3.ClusterType, v3.EndpointType, v3.ListenerType, v3.RouteType} -) - -func TestEDSOverlapping(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - addOverlappingEndpoints(s) - adscon := s.Connect(nil, nil, watchEds) - testOverlappingPorts(s, adscon, t) -} - -func TestEDSUnhealthyEndpoints(t *testing.T) { - test.SetBoolForTest(t, &features.SendUnhealthyEndpoints, true) - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - addUnhealthyCluster(s) - adscon := s.Connect(nil, nil, watchEds) - _, err := adscon.Wait(5 * time.Second) - if err != nil { - t.Fatalf("Error in push %v", err) - } - - validateEndpoints := func(expectPush bool, healthy []string, unhealthy []string) { - t.Helper() - // Normalize lists to make comparison easier - if healthy == nil { - healthy = []string{} - } - if unhealthy == nil { - unhealthy = []string{} - } - sort.Strings(healthy) - sort.Strings(unhealthy) - if expectPush { - upd, _ := adscon.Wait(5*time.Second, v3.EndpointType) - - if len(upd) > 0 && !contains(upd, v3.EndpointType) { - t.Fatalf("Expecting EDS push as endpoint health is changed. But received %v", upd) - } - } else { - upd, _ := adscon.Wait(50*time.Millisecond, v3.EndpointType) - if contains(upd, v3.EndpointType) { - t.Fatalf("Expected no EDS push, got %v", upd) - } - } - - // Validate that endpoints are pushed. - lbe := adscon.GetEndpoints()["outbound|53||unhealthy.svc.cluster.local"] - eh, euh := xdstest.ExtractHealthEndpoints(lbe) - gotHealthy := sets.New(eh...).SortedList() - gotUnhealthy := sets.New(euh...).SortedList() - if !reflect.DeepEqual(gotHealthy, healthy) { - t.Fatalf("did not get expected endpoints: got %v, want %v", gotHealthy, healthy) - } - if !reflect.DeepEqual(gotUnhealthy, unhealthy) { - t.Fatalf("did not get expected unhealthy endpoints: got %v, want %v", gotUnhealthy, unhealthy) - } - } - - // Validate that we do not send initial unhealthy endpoints. - validateEndpoints(false, nil, nil) - adscon.WaitClear() - - // Set additional unhealthy endpoint and validate Eds update is not triggered. - s.Discovery.MemRegistry.SetEndpoints("unhealthy.svc.cluster.local", "", - []*model.IstioEndpoint{ - { - Address: "10.0.0.53", - EndpointPort: 53, - ServicePortName: "tcp-dns", - HealthStatus: model.UnHealthy, - }, - { - Address: "10.0.0.54", - EndpointPort: 53, - ServicePortName: "tcp-dns", - HealthStatus: model.UnHealthy, - }, - }) - - // Validate that endpoint is not pushed. - validateEndpoints(false, nil, nil) - - // Change the status of endpoint to Healthy and validate Eds is pushed. - s.Discovery.MemRegistry.SetEndpoints("unhealthy.svc.cluster.local", "", - []*model.IstioEndpoint{ - { - Address: "10.0.0.53", - EndpointPort: 53, - ServicePortName: "tcp-dns", - HealthStatus: model.Healthy, - }, - { - Address: "10.0.0.54", - EndpointPort: 53, - ServicePortName: "tcp-dns", - HealthStatus: model.Healthy, - }, - }) - - // Validate that endpoints are pushed. - validateEndpoints(true, []string{"10.0.0.53:53", "10.0.0.54:53"}, nil) - - // Set to exact same endpoints - s.Discovery.MemRegistry.SetEndpoints("unhealthy.svc.cluster.local", "", - []*model.IstioEndpoint{ - { - Address: "10.0.0.53", - EndpointPort: 53, - ServicePortName: "tcp-dns", - HealthStatus: model.Healthy, - }, - { - Address: "10.0.0.54", - EndpointPort: 53, - ServicePortName: "tcp-dns", - HealthStatus: model.Healthy, - }, - }) - // Validate that endpoint is not pushed. - validateEndpoints(false, []string{"10.0.0.53:53", "10.0.0.54:53"}, nil) - - // Now change the status of endpoint to UnHealthy and validate Eds is pushed. - s.Discovery.MemRegistry.SetEndpoints("unhealthy.svc.cluster.local", "", - []*model.IstioEndpoint{ - { - Address: "10.0.0.53", - EndpointPort: 53, - ServicePortName: "tcp-dns", - HealthStatus: model.UnHealthy, - }, - { - Address: "10.0.0.54", - EndpointPort: 53, - ServicePortName: "tcp-dns", - HealthStatus: model.Healthy, - }, - }) - - // Validate that endpoints are pushed. - validateEndpoints(true, []string{"10.0.0.54:53"}, []string{"10.0.0.53:53"}) - - // Change the status of endpoint to Healthy and validate Eds is pushed. - s.Discovery.MemRegistry.SetEndpoints("unhealthy.svc.cluster.local", "", - []*model.IstioEndpoint{ - { - Address: "10.0.0.53", - EndpointPort: 53, - ServicePortName: "tcp-dns", - HealthStatus: model.Healthy, - }, - { - Address: "10.0.0.54", - EndpointPort: 53, - ServicePortName: "tcp-dns", - HealthStatus: model.Healthy, - }, - }) - - validateEndpoints(true, []string{"10.0.0.54:53", "10.0.0.53:53"}, nil) - - // Remove a healthy endpoint - s.Discovery.MemRegistry.SetEndpoints("unhealthy.svc.cluster.local", "", - []*model.IstioEndpoint{ - { - Address: "10.0.0.53", - EndpointPort: 53, - ServicePortName: "tcp-dns", - HealthStatus: model.Healthy, - }, - }) - - validateEndpoints(true, []string{"10.0.0.53:53"}, nil) - - // Remove last healthy endpoint - s.Discovery.MemRegistry.SetEndpoints("unhealthy.svc.cluster.local", "", []*model.IstioEndpoint{}) - validateEndpoints(true, nil, nil) -} - -// Validates the behavior when Service resolution type is updated after initial EDS push. -// See https://github.com/istio/istio/issues/18355 for more details. -func TestEDSServiceResolutionUpdate(t *testing.T) { - for _, resolution := range []model.Resolution{model.DNSLB, model.DNSRoundRobinLB} { - t.Run(fmt.Sprintf("resolution_%s", resolution), func(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - addEdsCluster(s, "edsdns.svc.cluster.local", "http", "10.0.0.53", 8080) - addEdsCluster(s, "other.local", "http", "1.1.1.1", 8080) - - adscConn := s.Connect(nil, nil, watchAll) - - // Validate that endpoints are pushed correctly. - testEndpoints("10.0.0.53", "outbound|8080||edsdns.svc.cluster.local", adscConn, t) - - // Now update the service resolution to DNSLB/DNSRRLB with a DNS endpoint. - updateServiceResolution(s, resolution) - - if _, err := adscConn.Wait(5*time.Second, v3.EndpointType); err != nil { - t.Fatal(err) - } - - // Validate that endpoints are skipped. - lbe := adscConn.GetEndpoints()["outbound|8080||edsdns.svc.cluster.local"] - if lbe != nil && len(lbe.Endpoints) > 0 { - t.Fatalf("endpoints not expected for %s, but got %v", "edsdns.svc.cluster.local", adscConn.EndpointsJSON()) - } - }) - } -} - -// Validate that when endpoints of a service flipflop between 1 and 0 does not trigger a full push. -func TestEndpointFlipFlops(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - addEdsCluster(s, "flipflop.com", "http", "10.0.0.53", 8080) - - adscConn := s.Connect(nil, nil, watchAll) - - // Validate that endpoints are pushed correctly. - testEndpoints("10.0.0.53", "outbound|8080||flipflop.com", adscConn, t) - - // Clear the endpoint and validate it does not trigger a full push. - s.Discovery.MemRegistry.SetEndpoints("flipflop.com", "", []*model.IstioEndpoint{}) - - upd, _ := adscConn.Wait(5*time.Second, v3.EndpointType) - - if contains(upd, "cds") { - t.Fatalf("Expecting only EDS update as part of a partial push. But received CDS also %v", upd) - } - - if len(upd) > 0 && !contains(upd, v3.EndpointType) { - t.Fatalf("Expecting EDS push as part of a partial push. But received %v", upd) - } - - lbe := adscConn.GetEndpoints()["outbound|8080||flipflop.com"] - if len(lbe.Endpoints) != 0 { - t.Fatalf("There should be no endpoints for outbound|8080||flipflop.com. Endpoints:\n%v", adscConn.EndpointsJSON()) - } - - // Validate that keys in service still exist in EndpointIndex - this prevents full push. - if _, ok := s.Discovery.EndpointIndex.ShardsForService("flipflop.com", ""); !ok { - t.Fatalf("Expected service key %s to be present in EndpointIndex. But missing %v", "flipflop.com", s.Discovery.EndpointIndex.Shardz()) - } - - // Set the endpoints again and validate it does not trigger full push. - s.Discovery.MemRegistry.SetEndpoints("flipflop.com", "", - []*model.IstioEndpoint{ - { - Address: "10.10.1.1", - ServicePortName: "http", - EndpointPort: 8080, - }, - }) - - upd, _ = adscConn.Wait(5*time.Second, v3.EndpointType) - - if contains(upd, v3.ClusterType) { - t.Fatalf("expecting only EDS update as part of a partial push. But received CDS also %+v", upd) - } - - if len(upd) > 0 && !contains(upd, v3.EndpointType) { - t.Fatalf("expecting EDS push as part of a partial push. But did not receive %+v", upd) - } - - testEndpoints("10.10.1.1", "outbound|8080||flipflop.com", adscConn, t) -} - -// Validate that deleting a service clears entries from EndpointIndex. -func TestDeleteService(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - addEdsCluster(s, "removeservice.com", "http", "10.0.0.53", 8080) - - adscConn := s.Connect(nil, nil, watchEds) - - // Validate that endpoints are pushed correctly. - testEndpoints("10.0.0.53", "outbound|8080||removeservice.com", adscConn, t) - - s.Discovery.MemRegistry.RemoveService("removeservice.com") - - if _, ok := s.Discovery.EndpointIndex.ShardsForService("removeservice.com", ""); ok { - t.Fatalf("Expected service key %s to be deleted in EndpointIndex. But is still there %v", - "removeservice.com", s.Discovery.EndpointIndex.Shardz()) - } -} - -var ( - c1Key = model.ShardKey{Cluster: "c1"} - c2Key = model.ShardKey{Cluster: "c2"} -) - -func TestUpdateServiceAccount(t *testing.T) { - cluster1Endppoints := []*model.IstioEndpoint{ - {Address: "10.172.0.1", ServiceAccount: "sa1"}, - {Address: "10.172.0.2", ServiceAccount: "sa-vm1"}, - } - - testCases := []struct { - name string - shardKey model.ShardKey - endpoints []*model.IstioEndpoint - expect bool - }{ - { - name: "added new endpoint", - shardKey: c1Key, - endpoints: append(cluster1Endppoints, &model.IstioEndpoint{Address: "10.172.0.3", ServiceAccount: "sa1"}), - expect: false, - }, - { - name: "added new sa", - shardKey: c1Key, - endpoints: append(cluster1Endppoints, &model.IstioEndpoint{Address: "10.172.0.3", ServiceAccount: "sa2"}), - expect: true, - }, - { - name: "updated endpoints address", - shardKey: c1Key, - endpoints: []*model.IstioEndpoint{ - {Address: "10.172.0.5", ServiceAccount: "sa1"}, - {Address: "10.172.0.2", ServiceAccount: "sa-vm1"}, - }, - expect: false, - }, - { - name: "deleted one endpoint with unique sa", - shardKey: c1Key, - endpoints: []*model.IstioEndpoint{ - {Address: "10.172.0.1", ServiceAccount: "sa1"}, - }, - expect: true, - }, - { - name: "deleted one endpoint with duplicate sa", - shardKey: c1Key, - endpoints: []*model.IstioEndpoint{ - {Address: "10.172.0.2", ServiceAccount: "sa-vm1"}, - }, - expect: false, - }, - { - name: "deleted endpoints", - shardKey: c1Key, - endpoints: nil, - expect: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - s := new(xds.DiscoveryServer) - originalEndpointsShard := &model.EndpointShards{ - Shards: map[model.ShardKey][]*model.IstioEndpoint{ - c1Key: cluster1Endppoints, - c2Key: {{Address: "10.244.0.1", ServiceAccount: "sa1"}, {Address: "10.244.0.2", ServiceAccount: "sa-vm2"}}, - }, - ServiceAccounts: map[string]struct{}{ - "sa1": {}, - "sa-vm1": {}, - "sa-vm2": {}, - }, - } - originalEndpointsShard.Shards[tc.shardKey] = tc.endpoints - ret := s.UpdateServiceAccount(originalEndpointsShard, "test-svc") - if ret != tc.expect { - t.Errorf("expect UpdateServiceAccount %v, but got %v", tc.expect, ret) - } - }) - } -} - -func TestZeroEndpointShardSA(t *testing.T) { - cluster1Endppoints := []*model.IstioEndpoint{ - {Address: "10.172.0.1", ServiceAccount: "sa1"}, - } - s := new(xds.DiscoveryServer) - s.Cache = model.DisabledCache{} - s.EndpointIndex = model.NewEndpointIndex() - originalEndpointsShard, _ := s.EndpointIndex.GetOrCreateEndpointShard("test", "test") - originalEndpointsShard.Shards = map[model.ShardKey][]*model.IstioEndpoint{ - c1Key: cluster1Endppoints, - } - originalEndpointsShard.ServiceAccounts = map[string]struct{}{ - "sa1": {}, - } - s.EDSCacheUpdate(c1Key, "test", "test", []*model.IstioEndpoint{}) - modifiedShard, _ := s.EndpointIndex.GetOrCreateEndpointShard("test", "test") - if len(modifiedShard.ServiceAccounts) != 0 { - t.Errorf("endpoint shard service accounts got %v want 0", len(modifiedShard.ServiceAccounts)) - } -} - -func fullPush(s *xds.FakeDiscoveryServer) { - s.Discovery.Push(&model.PushRequest{Full: true}) -} - -func addTestClientEndpoints(server *xds.DiscoveryServer) { - server.MemRegistry.AddService(&model.Service{ - Hostname: "test-1.default", - Ports: model.PortList{ - { - Name: "http", - Port: 80, - Protocol: protocol.HTTP, - }, - }, - }) - server.MemRegistry.AddInstance("test-1.default", &model.ServiceInstance{ - Endpoint: &model.IstioEndpoint{ - Address: "10.10.10.10", - ServicePortName: "http", - EndpointPort: 80, - Locality: model.Locality{Label: asdcLocality}, - }, - ServicePort: &model.Port{ - Name: "http", - Port: 80, - Protocol: protocol.HTTP, - }, - }) - server.MemRegistry.AddInstance("test-1.default", &model.ServiceInstance{ - Endpoint: &model.IstioEndpoint{ - Address: "10.10.10.11", - ServicePortName: "http", - EndpointPort: 80, - Locality: model.Locality{Label: asdc2Locality}, - }, - ServicePort: &model.Port{ - Name: "http", - Port: 80, - Protocol: protocol.HTTP, - }, - }) -} - -// Verify server sends the endpoint. This check for a single endpoint with the given -// address. -func testTCPEndpoints(expected string, adsc *adsc.ADSC, t *testing.T) { - t.Helper() - testEndpoints(expected, "outbound|8080||eds.test.svc.cluster.local", adsc, t) -} - -// Verify server sends the endpoint. This check for a single endpoint with the given -// address. -func testEndpoints(expected string, cluster string, adsc *adsc.ADSC, t *testing.T) { - t.Helper() - lbe, f := adsc.GetEndpoints()[cluster] - if !f || len(lbe.Endpoints) == 0 { - t.Fatalf("No lb endpoints for %v, %v", cluster, adsc.EndpointsJSON()) - } - var found []string - for _, lbe := range lbe.Endpoints { - for _, e := range lbe.LbEndpoints { - addr := e.GetEndpoint().Address.GetSocketAddress().Address - found = append(found, addr) - if expected == addr { - return - } - } - } - t.Fatalf("Expecting %s got %v", expected, found) -} - -// Verify server sends the tunneled endpoints. -// nolint: unparam -func testTunnelEndpoints(expectIP string, expectPort uint32, adsc *adsc.ADSC, t *testing.T) { - t.Helper() - cluster := "outbound|8080||eds.test.svc.cluster.local" - allClusters := adsc.GetEndpoints() - cla, f := allClusters[cluster] - if !f || len(cla.Endpoints) == 0 { - t.Fatalf("No lb endpoints for %v, %v", cluster, adsc.EndpointsJSON()) - } - var found []string - for _, lbe := range cla.Endpoints { - for _, e := range lbe.LbEndpoints { - addr := e.GetEndpoint().Address.GetSocketAddress().Address - port := e.GetEndpoint().Address.GetSocketAddress().GetPortValue() - found = append(found, fmt.Sprintf("%s:%d", addr, port)) - if expectIP == addr && expectPort == port { - return - } - } - } - t.Errorf("REACH HERE cannot find %s:%d", expectIP, expectPort) - t.Fatalf("Expecting address %s:%d got %v", expectIP, expectPort, found) -} - -func testLocalityPrioritizedEndpoints(adsc *adsc.ADSC, adsc2 *adsc.ADSC, t *testing.T) { - endpoints1 := adsc.GetEndpoints() - endpoints2 := adsc2.GetEndpoints() - - verifyLocalityPriorities(asdcLocality, endpoints1["outbound|80||locality.cluster.local"].GetEndpoints(), t) - verifyLocalityPriorities(asdc2Locality, endpoints2["outbound|80||locality.cluster.local"].GetEndpoints(), t) - - // No outlier detection specified for this cluster, so we shouldn't apply priority. - verifyNoLocalityPriorities(endpoints1["outbound|80||locality-no-outlier-detection.cluster.local"].GetEndpoints(), t) - verifyNoLocalityPriorities(endpoints2["outbound|80||locality-no-outlier-detection.cluster.local"].GetEndpoints(), t) -} - -// Tests that Services with multiple ports sharing the same port number are properly sent endpoints. -// Real world use case for this is kube-dns, which uses port 53 for TCP and UDP. -func testOverlappingPorts(s *xds.FakeDiscoveryServer, adsc *adsc.ADSC, t *testing.T) { - // Test initial state - testEndpoints("10.0.0.53", "outbound|53||overlapping.cluster.local", adsc, t) - - s.Discovery.Push(&model.PushRequest{ - Full: true, - ConfigsUpdated: map[model.ConfigKey]struct{}{{ - Kind: gvk.ServiceEntry, - Name: "overlapping.cluster.local", - }: {}}, - }) - _, _ = adsc.Wait(5 * time.Second) - - // After the incremental push, we should still see the endpoint - testEndpoints("10.0.0.53", "outbound|53||overlapping.cluster.local", adsc, t) -} - -func verifyNoLocalityPriorities(eps []*endpoint.LocalityLbEndpoints, t *testing.T) { - for _, ep := range eps { - if ep.GetPriority() != 0 { - t.Errorf("expected no locality priorities to apply, got priority %v.", ep.GetPriority()) - } - } -} - -func verifyLocalityPriorities(proxyLocality string, eps []*endpoint.LocalityLbEndpoints, t *testing.T) { - items := strings.SplitN(proxyLocality, "/", 3) - region, zone, subzone := items[0], items[1], items[2] - for _, ep := range eps { - if ep.GetLocality().Region == region { - if ep.GetLocality().Zone == zone { - if ep.GetLocality().SubZone == subzone { - if ep.GetPriority() != 0 { - t.Errorf("expected endpoint pool from same locality to have priority of 0, got %v", ep.GetPriority()) - } - } else if ep.GetPriority() != 1 { - t.Errorf("expected endpoint pool from a different subzone to have priority of 1, got %v", ep.GetPriority()) - } - } else { - if ep.GetPriority() != 2 { - t.Errorf("expected endpoint pool from a different zone to have priority of 2, got %v", ep.GetPriority()) - } - } - } else { - if ep.GetPriority() != 3 { - t.Errorf("expected endpoint pool from a different region to have priority of 3, got %v", ep.GetPriority()) - } - } - } -} - -// Verify server sends UDS endpoints -func testUdsEndpoints(adsc *adsc.ADSC, t *testing.T) { - // Check the UDS endpoint ( used to be separate test - but using old unused GRPC method) - // The new test also verifies CDS is pusing the UDS cluster, since adsc.eds is - // populated using CDS response - lbe, f := adsc.GetEndpoints()["outbound|0||localuds.cluster.local"] - if !f || len(lbe.Endpoints) == 0 { - t.Error("No UDS lb endpoints") - } else { - ep0 := lbe.Endpoints[0] - if len(ep0.LbEndpoints) != 1 { - t.Fatalf("expected 1 LB endpoint but got %d", len(ep0.LbEndpoints)) - } - lbep := ep0.LbEndpoints[0] - path := lbep.GetEndpoint().GetAddress().GetPipe().GetPath() - if path != udsPath { - t.Fatalf("expected Pipe to %s, got %s", udsPath, path) - } - } -} - -// Update -func edsUpdates(s *xds.FakeDiscoveryServer, adsc *adsc.ADSC, t *testing.T) { - // Old style (non-incremental) - s.Discovery.MemRegistry.SetEndpoints(edsIncSvc, "", - newEndpointWithAccount("127.0.0.3", "hello-sa", "v1")) - - xds.AdsPushAll(s.Discovery) - - // will trigger recompute and push - - if _, err := adsc.Wait(5*time.Second, v3.EndpointType); err != nil { - t.Fatal("EDS push failed", err) - } - testTCPEndpoints("127.0.0.3", adsc, t) -} - -// edsFullUpdateCheck checks for updates required in a full push after the CDS update -func edsFullUpdateCheck(adsc *adsc.ADSC, t *testing.T) { - t.Helper() - if upd, err := adsc.Wait(15*time.Second, watchAll...); err != nil { - t.Fatal("Expecting CDS, EDS, LDS, and RDS update as part of a full push", err, upd) - } -} - -// This test must be run in isolation, can't be parallelized with any other v2 test. -// It makes different kind of updates, and checks that incremental or full push happens. -// In particular: -// - just endpoint changes -> incremental -// - service account changes -> full ( in future: CDS only ) -// - label changes -> full -func edsUpdateInc(s *xds.FakeDiscoveryServer, adsc *adsc.ADSC, t *testing.T) { - // TODO: set endpoints for a different cluster (new shard) - - // Verify initial state - testTCPEndpoints("127.0.0.1", adsc, t) - - adsc.WaitClear() // make sure there are no pending pushes. - - // Equivalent with the event generated by K8S watching the Service. - // Will trigger a push. - s.Discovery.MemRegistry.SetEndpoints(edsIncSvc, "", - newEndpointWithAccount("127.0.0.2", "hello-sa", "v1")) - - upd, err := adsc.Wait(5*time.Second, v3.EndpointType) - if err != nil { - t.Fatal("Incremental push failed", err) - } - if contains(upd, v3.ClusterType) { - t.Fatal("Expecting EDS only update, got", upd) - } - - testTCPEndpoints("127.0.0.2", adsc, t) - - // Update the endpoint with different SA - expect full - s.Discovery.MemRegistry.SetEndpoints(edsIncSvc, "", - newEndpointWithAccount("127.0.0.2", "account2", "v1")) - - edsFullUpdateCheck(adsc, t) - testTCPEndpoints("127.0.0.2", adsc, t) - - // Update the endpoint again, no SA change - expect incremental - s.Discovery.MemRegistry.SetEndpoints(edsIncSvc, "", - newEndpointWithAccount("127.0.0.4", "account2", "v1")) - - upd, err = adsc.Wait(5 * time.Second) - if err != nil { - t.Fatal("Incremental push failed", err) - } - if !reflect.DeepEqual(upd, []string{v3.EndpointType}) { - t.Fatal("Expecting EDS only update, got", upd) - } - testTCPEndpoints("127.0.0.4", adsc, t) - - // Update the endpoint to original SA - expect full - s.Discovery.MemRegistry.SetEndpoints(edsIncSvc, "", - newEndpointWithAccount("127.0.0.2", "hello-sa", "v1")) - edsFullUpdateCheck(adsc, t) - testTCPEndpoints("127.0.0.2", adsc, t) - - // Update the endpoint again, no label change - expect incremental - s.Discovery.MemRegistry.SetEndpoints(edsIncSvc, "", - newEndpointWithAccount("127.0.0.5", "hello-sa", "v1")) - - upd, err = adsc.Wait(5 * time.Second) - if err != nil { - t.Fatal("Incremental push failed", err) - } - if !reflect.DeepEqual(upd, []string{v3.EndpointType}) { - t.Fatal("Expecting EDS only update, got", upd) - } - testTCPEndpoints("127.0.0.5", adsc, t) - - // Wipe out all endpoints - expect full - s.Discovery.MemRegistry.SetEndpoints(edsIncSvc, "", []*model.IstioEndpoint{}) - - if upd, err := adsc.Wait(15*time.Second, v3.EndpointType); err != nil { - t.Fatal("Expecting EDS update as part of a partial push", err, upd) - } - - lbe := adsc.GetEndpoints()["outbound|8080||eds.test.svc.cluster.local"] - if len(lbe.Endpoints) != 0 { - t.Fatalf("There should be no endpoints for outbound|8080||eds.test.svc.cluster.local. Endpoints:\n%v", adsc.EndpointsJSON()) - } -} - -// Make a direct EDS grpc request to pilot, verify the result is as expected. -// This test includes a 'bad client' regression test, which fails to read on the -// stream. -func multipleRequest(s *xds.FakeDiscoveryServer, inc bool, nclients, - nPushes int, to time.Duration, _ map[string]string, t *testing.T, -) { - wgConnect := &sync.WaitGroup{} - wg := &sync.WaitGroup{} - errChan := make(chan error, nclients) - - // Bad client - will not read any response. This triggers Write to block, which should - // be detected - // This is not using adsc, which consumes the events automatically. - ads := s.ConnectADS() - ads.Request(t, nil) - - n := nclients - wg.Add(n) - wgConnect.Add(n) - rcvPush := uatomic.NewInt32(0) - rcvClients := uatomic.NewInt32(0) - for i := 0; i < n; i++ { - current := i - go func(id int) { - defer wg.Done() - // Connect and get initial response - adscConn := s.Connect(&model.Proxy{IPAddresses: []string{fmt.Sprintf("1.1.1.%d", id)}}, nil, nil) - _, err := adscConn.Wait(15*time.Second, v3.RouteType) - if err != nil { - errChan <- errors.New("failed to get initial rds: " + err.Error()) - wgConnect.Done() - return - } - - if len(adscConn.GetEndpoints()) == 0 { - errChan <- errors.New("no endpoints") - wgConnect.Done() - return - } - - wgConnect.Done() - - // Check we received all pushes - log.Info("Waiting for pushes ", id) - - // Pushes may be merged so we may not get nPushes pushes - got, err := adscConn.Wait(15*time.Second, v3.EndpointType) - - // If in incremental mode, shouldn't receive cds|rds|lds here - if inc { - for _, g := range got { - if g == "cds" || g == "rds" || g == "lds" { - errChan <- fmt.Errorf("should be eds incremental but received cds. %v %v", - err, id) - return - } - } - } - - rcvPush.Inc() - if err != nil { - log.Info("Recv failed", err, id) - errChan <- fmt.Errorf("failed to receive a response in 15 s %v %v", - err, id) - return - } - - log.Info("Received all pushes ", id) - rcvClients.Inc() - - adscConn.Close() - }(current) - } - ok := waitTimeout(wgConnect, to) - if !ok { - t.Fatal("Failed to connect") - } - log.Info("Done connecting") - - // All clients are connected - this can start pushing changes. - for j := 0; j < nPushes; j++ { - if inc { - // This will be throttled - we want to trigger a single push - s.Discovery.AdsPushAll(strconv.Itoa(j), &model.PushRequest{ - Full: false, - ConfigsUpdated: map[model.ConfigKey]struct{}{{ - Kind: gvk.ServiceEntry, - Name: edsIncSvc, - }: {}}, - Push: s.Discovery.Env.PushContext, - }) - } else { - xds.AdsPushAll(s.Discovery) - } - log.Info("Push done ", j) - } - - ok = waitTimeout(wg, to) - if !ok { - t.Errorf("Failed to receive all responses %d %d", rcvClients.Load(), rcvPush.Load()) - buf := make([]byte, 1<<16) - runtime.Stack(buf, true) - fmt.Printf("%s", buf) - } - - close(errChan) - - // moved from ads_test, which had a duplicated test. - for e := range errChan { - t.Error(e) - } -} - -func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool { - c := make(chan struct{}) - go func() { - defer close(c) - wg.Wait() - }() - select { - case <-c: - return true - case <-time.After(timeout): - return false - } -} - -const udsPath = "/var/run/test/socket" - -func addUdsEndpoint(s *xds.DiscoveryServer) { - s.MemRegistry.AddService(&model.Service{ - Hostname: "localuds.cluster.local", - Ports: model.PortList{ - { - Name: "grpc", - Port: 0, - Protocol: protocol.GRPC, - }, - }, - MeshExternal: true, - Resolution: model.ClientSideLB, - }) - s.MemRegistry.AddInstance("localuds.cluster.local", &model.ServiceInstance{ - Endpoint: &model.IstioEndpoint{ - Address: udsPath, - EndpointPort: 0, - ServicePortName: "grpc", - Locality: model.Locality{Label: "localhost"}, - Labels: map[string]string{"socket": "unix"}, - }, - ServicePort: &model.Port{ - Name: "grpc", - Port: 0, - Protocol: protocol.GRPC, - }, - }) - - pushReq := &model.PushRequest{ - Full: true, - Reason: []model.TriggerReason{model.ConfigUpdate}, - } - s.ConfigUpdate(pushReq) -} - -func addLocalityEndpoints(server *xds.DiscoveryServer, hostname host.Name) { - server.MemRegistry.AddService(&model.Service{ - Hostname: hostname, - Ports: model.PortList{ - { - Name: "http", - Port: 80, - Protocol: protocol.HTTP, - }, - }, - }) - localities := []string{ - "region1/zone1/subzone1", - "region1/zone1/subzone2", - "region1/zone2/subzone1", - "region2/zone1/subzone1", - "region2/zone1/subzone2", - "region2/zone2/subzone1", - "region2/zone2/subzone2", - } - for i, locality := range localities { - server.MemRegistry.AddInstance(hostname, &model.ServiceInstance{ - Endpoint: &model.IstioEndpoint{ - Address: fmt.Sprintf("10.0.0.%v", i), - EndpointPort: 80, - ServicePortName: "http", - Locality: model.Locality{Label: locality}, - }, - ServicePort: &model.Port{ - Name: "http", - Port: 80, - Protocol: protocol.HTTP, - }, - }) - } -} - -// nolint: unparam -func addEdsCluster(s *xds.FakeDiscoveryServer, hostName string, portName string, address string, port int) { - s.Discovery.MemRegistry.AddService(&model.Service{ - Hostname: host.Name(hostName), - Ports: model.PortList{ - { - Name: portName, - Port: port, - Protocol: protocol.HTTP, - }, - }, - }) - - s.Discovery.MemRegistry.AddInstance(host.Name(hostName), &model.ServiceInstance{ - Endpoint: &model.IstioEndpoint{ - Address: address, - EndpointPort: uint32(port), - ServicePortName: portName, - }, - ServicePort: &model.Port{ - Name: portName, - Port: port, - Protocol: protocol.HTTP, - }, - }) - fullPush(s) -} - -func updateServiceResolution(s *xds.FakeDiscoveryServer, resolution model.Resolution) { - s.Discovery.MemRegistry.AddService(&model.Service{ - Hostname: "edsdns.svc.cluster.local", - Ports: model.PortList{ - { - Name: "http", - Port: 8080, - Protocol: protocol.HTTP, - }, - }, - Resolution: resolution, - }) - - s.Discovery.MemRegistry.AddInstance("edsdns.svc.cluster.local", &model.ServiceInstance{ - Endpoint: &model.IstioEndpoint{ - Address: "somevip.com", - EndpointPort: 8080, - ServicePortName: "http", - }, - ServicePort: &model.Port{ - Name: "http", - Port: 8080, - Protocol: protocol.HTTP, - }, - }) - - fullPush(s) -} - -func addOverlappingEndpoints(s *xds.FakeDiscoveryServer) { - s.Discovery.MemRegistry.AddService(&model.Service{ - Hostname: "overlapping.cluster.local", - Ports: model.PortList{ - { - Name: "dns", - Port: 53, - Protocol: protocol.UDP, - }, - { - Name: "tcp-dns", - Port: 53, - Protocol: protocol.TCP, - }, - }, - }) - s.Discovery.MemRegistry.AddInstance("overlapping.cluster.local", &model.ServiceInstance{ - Endpoint: &model.IstioEndpoint{ - Address: "10.0.0.53", - EndpointPort: 53, - ServicePortName: "tcp-dns", - }, - ServicePort: &model.Port{ - Name: "tcp-dns", - Port: 53, - Protocol: protocol.TCP, - }, - }) - fullPush(s) -} - -func addUnhealthyCluster(s *xds.FakeDiscoveryServer) { - s.Discovery.MemRegistry.AddService(&model.Service{ - Hostname: "unhealthy.svc.cluster.local", - Ports: model.PortList{ - { - Name: "tcp-dns", - Port: 53, - Protocol: protocol.TCP, - }, - }, - }) - s.Discovery.MemRegistry.AddInstance("unhealthy.svc.cluster.local", &model.ServiceInstance{ - Endpoint: &model.IstioEndpoint{ - Address: "10.0.0.53", - EndpointPort: 53, - ServicePortName: "tcp-dns", - HealthStatus: model.UnHealthy, - }, - ServicePort: &model.Port{ - Name: "tcp-dns", - Port: 53, - Protocol: protocol.TCP, - }, - }) - fullPush(s) -} - -// Verify the endpoint debug interface is installed and returns some string. -// TODO: parse response, check if data captured matches what we expect. -// TODO: use this in integration tests. -// TODO: refine the output -// TODO: dump the ServiceInstances as well -func testEdsz(t *testing.T, s *xds.FakeDiscoveryServer, proxyID string) { - req, err := http.NewRequest("GET", "/debug/edsz?proxyID="+proxyID, nil) - if err != nil { - t.Fatal(err) - } - rr := httptest.NewRecorder() - debug := http.HandlerFunc(s.Discovery.Edsz) - debug.ServeHTTP(rr, req) - - data, err := io.ReadAll(rr.Body) - if err != nil { - t.Fatalf("Failed to read /edsz") - } - statusStr := string(data) - - if !strings.Contains(statusStr, "\"outbound|8080||eds.test.svc.cluster.local\"") { - t.Fatal("Mock eds service not found ", statusStr) - } -} - -func contains(s []string, e string) bool { - for _, a := range s { - if a == e { - return true - } - } - return false -} diff --git a/pilot/pkg/xds/endpoint_builder.go b/pilot/pkg/xds/endpoint_builder.go deleted file mode 100644 index a011276cf..000000000 --- a/pilot/pkg/xds/endpoint_builder.go +++ /dev/null @@ -1,558 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "crypto/md5" - "encoding/hex" - "sort" - "strconv" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" - "google.golang.org/protobuf/proto" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - networkingapi "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/authn/factory" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/network" -) - -// Return the tunnel type for this endpoint builder. If the endpoint builder builds h2tunnel, the final endpoint -// collection includes only the endpoints which support H2 tunnel and the non-tunnel endpoints. The latter case is to -// support multi-cluster service. -// Revisit non-tunnel endpoint decision once the gateways supports tunnel. -// TODO(lambdai): Propose to istio api. -func GetTunnelBuilderType(_ string, proxy *model.Proxy, _ *model.PushContext) networking.TunnelType { - if proxy == nil || proxy.Metadata == nil || proxy.Metadata.ProxyConfig == nil { - return networking.NoTunnel - } - if outTunnel, ok := proxy.Metadata.ProxyConfig.ProxyMetadata["tunnel"]; ok { - switch outTunnel { - case networking.H2TunnelTypeName: - return networking.H2Tunnel - default: - // passthrough - } - } - return networking.NoTunnel -} - -type EndpointBuilder struct { - // These fields define the primary key for an endpoint, and can be used as a cache key - clusterName string - network network.ID - proxyView model.ProxyView - clusterID cluster.ID - locality *core.Locality - destinationRule *config.Config - service *model.Service - clusterLocal bool - tunnelType networking.TunnelType - - // These fields are provided for convenience only - subsetName string - hostname host.Name - port int - push *model.PushContext - proxy *model.Proxy - - mtlsChecker *mtlsChecker -} - -func NewEndpointBuilder(clusterName string, proxy *model.Proxy, push *model.PushContext) EndpointBuilder { - _, subsetName, hostname, port := model.ParseSubsetKey(clusterName) - svc := push.ServiceForHostname(proxy, hostname) - - var dr *config.Config - if svc != nil { - dr = proxy.SidecarScope.DestinationRule(model.TrafficDirectionOutbound, proxy, svc.Hostname) - } - b := EndpointBuilder{ - clusterName: clusterName, - network: proxy.Metadata.Network, - proxyView: proxy.GetView(), - clusterID: proxy.Metadata.ClusterID, - locality: proxy.Locality, - service: svc, - clusterLocal: push.IsClusterLocal(svc), - destinationRule: dr, - tunnelType: GetTunnelBuilderType(clusterName, proxy, push), - - push: push, - proxy: proxy, - subsetName: subsetName, - hostname: hostname, - port: port, - } - - // We need this for multi-network, or for clusters meant for use with AUTO_PASSTHROUGH. - if features.EnableAutomTLSCheckPolicies || - b.push.NetworkManager().IsMultiNetworkEnabled() || model.IsDNSSrvSubsetKey(clusterName) { - b.mtlsChecker = newMtlsChecker(push, port, dr) - } - return b -} - -func (b EndpointBuilder) DestinationRule() *networkingapi.DestinationRule { - if b.destinationRule == nil { - return nil - } - return b.destinationRule.Spec.(*networkingapi.DestinationRule) -} - -// Key provides the eds cache key and should include any information that could change the way endpoints are generated. -func (b EndpointBuilder) Key() string { - params := []string{ - b.clusterName, - string(b.network), - string(b.clusterID), - strconv.FormatBool(b.clusterLocal), - util.LocalityToString(b.locality), - b.tunnelType.ToString(), - } - if b.push != nil && b.push.AuthnPolicies != nil { - params = append(params, b.push.AuthnPolicies.GetVersion()) - } - if b.destinationRule != nil { - params = append(params, b.destinationRule.Name+"/"+b.destinationRule.Namespace) - } - if b.service != nil { - params = append(params, string(b.service.Hostname)+"/"+b.service.Attributes.Namespace) - } - if b.proxyView != nil { - params = append(params, b.proxyView.String()) - } - hash := md5.New() - for _, param := range params { - hash.Write([]byte(param)) - } - sum := hash.Sum(nil) - return hex.EncodeToString(sum) -} - -func (b EndpointBuilder) Cacheable() bool { - // If service is not defined, we cannot do any caching as we will not have a way to - // invalidate the results. - // Service being nil means the EDS will be empty anyways, so not much lost here. - return b.service != nil -} - -func (b EndpointBuilder) DependentConfigs() []model.ConfigKey { - configs := []model.ConfigKey{} - if b.destinationRule != nil { - configs = append(configs, model.ConfigKey{Kind: gvk.DestinationRule, Name: b.destinationRule.Name, Namespace: b.destinationRule.Namespace}) - } - if b.service != nil { - configs = append(configs, model.ConfigKey{Kind: gvk.ServiceEntry, Name: string(b.service.Hostname), Namespace: b.service.Attributes.Namespace}) - } - return configs -} - -var edsDependentTypes = []config.GroupVersionKind{gvk.PeerAuthentication} - -func (b EndpointBuilder) DependentTypes() []config.GroupVersionKind { - return edsDependentTypes -} - -// TODO(lambdai): Receive port value(15009 by default), builder to cover wide cases. -type EndpointTunnelApplier interface { - // Mutate LbEndpoint in place. Return non-nil on failure. - ApplyTunnel(lep *endpoint.LbEndpoint, tunnelType networking.TunnelType) (*endpoint.LbEndpoint, error) -} - -type EndpointNoTunnelApplier struct{} - -// Note that this will not return error if another tunnel typs requested. -func (t *EndpointNoTunnelApplier) ApplyTunnel(lep *endpoint.LbEndpoint, _ networking.TunnelType) (*endpoint.LbEndpoint, error) { - return lep, nil -} - -type EndpointH2TunnelApplier struct{} - -// TODO(lambdai): Set original port if the default cluster original port is not the same. -func (t *EndpointH2TunnelApplier) ApplyTunnel(lep *endpoint.LbEndpoint, tunnelType networking.TunnelType) (*endpoint.LbEndpoint, error) { - switch tunnelType { - case networking.H2Tunnel: - if ep := lep.GetEndpoint(); ep != nil { - if ep.Address.GetSocketAddress().GetPortValue() != 0 { - newEp := proto.Clone(lep).(*endpoint.LbEndpoint) - newEp.GetEndpoint().Address.GetSocketAddress().PortSpecifier = &core.SocketAddress_PortValue{ - PortValue: 15009, - } - return newEp, nil - } - } - return lep, nil - case networking.NoTunnel: - return lep, nil - default: - panic("supported tunnel type") - } -} - -type LocLbEndpointsAndOptions struct { - istioEndpoints []*model.IstioEndpoint - // The protobuf message which contains LbEndpoint slice. - llbEndpoints endpoint.LocalityLbEndpoints - // The runtime information of the LbEndpoint slice. Each LbEndpoint has individual metadata at the same index. - tunnelMetadata []EndpointTunnelApplier -} - -// Return prefer H2 tunnel metadata. -func MakeTunnelApplier(_ *endpoint.LbEndpoint, tunnelOpt networking.TunnelAbility) EndpointTunnelApplier { - if tunnelOpt.SupportH2Tunnel() { - return &EndpointH2TunnelApplier{} - } - return &EndpointNoTunnelApplier{} -} - -func (e *LocLbEndpointsAndOptions) append(ep *model.IstioEndpoint, le *endpoint.LbEndpoint, tunnelOpt networking.TunnelAbility) { - e.istioEndpoints = append(e.istioEndpoints, ep) - e.llbEndpoints.LbEndpoints = append(e.llbEndpoints.LbEndpoints, le) - e.tunnelMetadata = append(e.tunnelMetadata, MakeTunnelApplier(le, tunnelOpt)) -} - -func (e *LocLbEndpointsAndOptions) refreshWeight() { - var weight *wrappers.UInt32Value - if len(e.llbEndpoints.LbEndpoints) == 0 { - weight = nil - } else { - weight = &wrappers.UInt32Value{} - for _, lbEp := range e.llbEndpoints.LbEndpoints { - weight.Value += lbEp.GetLoadBalancingWeight().Value - } - } - e.llbEndpoints.LoadBalancingWeight = weight -} - -func (e *LocLbEndpointsAndOptions) AssertInvarianceInTest() { - if len(e.llbEndpoints.LbEndpoints) != len(e.tunnelMetadata) { - panic(" len(e.llbEndpoints.LbEndpoints) != len(e.tunnelMetadata)") - } -} - -// build LocalityLbEndpoints for a cluster from existing EndpointShards. -func (b *EndpointBuilder) buildLocalityLbEndpointsFromShards( - shards *model.EndpointShards, - svcPort *model.Port, -) []*LocLbEndpointsAndOptions { - localityEpMap := make(map[string]*LocLbEndpointsAndOptions) - // get the subset labels - epLabels := getSubSetLabels(b.DestinationRule(), b.subsetName) - - // Determine whether or not the target service is considered local to the cluster - // and should, therefore, not be accessed from outside the cluster. - isClusterLocal := b.clusterLocal - - shards.Lock() - // Extract shard keys so we can iterate in order. This ensures a stable EDS output. - keys := shards.Keys() - // The shards are updated independently, now need to filter and merge for this cluster - for _, shardKey := range keys { - endpoints := shards.Shards[shardKey] - // If the downstream service is configured as cluster-local, only include endpoints that - // reside in the same cluster. - if isClusterLocal && (shardKey.Cluster != b.clusterID) { - continue - } - for _, ep := range endpoints { - // TODO(nmittler): Consider merging discoverability policy with cluster-local - if !ep.IsDiscoverableFromProxy(b.proxy) { - continue - } - if svcPort.Name != ep.ServicePortName { - continue - } - // Port labels - if !epLabels.SubsetOf(ep.Labels) { - continue - } - - locLbEps, found := localityEpMap[ep.Locality.Label] - if !found { - locLbEps = &LocLbEndpointsAndOptions{ - llbEndpoints: endpoint.LocalityLbEndpoints{ - Locality: util.ConvertLocality(ep.Locality.Label), - LbEndpoints: make([]*endpoint.LbEndpoint, 0, len(endpoints)), - }, - tunnelMetadata: make([]EndpointTunnelApplier, 0, len(endpoints)), - } - localityEpMap[ep.Locality.Label] = locLbEps - } - if ep.EnvoyEndpoint == nil { - ep.EnvoyEndpoint = buildEnvoyLbEndpoint(ep) - } - // detect if mTLS is possible for this endpoint, used later during ep filtering - // this must be done while converting IstioEndpoints because we still have workload labels - if b.mtlsChecker != nil { - b.mtlsChecker.computeForEndpoint(ep) - if features.EnableAutomTLSCheckPolicies { - tlsMode := ep.TLSMode - if b.mtlsChecker.isMtlsDisabled(ep.EnvoyEndpoint) { - tlsMode = "" - } - if nep, modified := util.MaybeApplyTLSModeLabel(ep.EnvoyEndpoint, tlsMode); modified { - ep.EnvoyEndpoint = nep - } - } - } - locLbEps.append(ep, ep.EnvoyEndpoint, ep.TunnelAbility) - } - } - shards.Unlock() - - locEps := make([]*LocLbEndpointsAndOptions, 0, len(localityEpMap)) - locs := make([]string, 0, len(localityEpMap)) - for k := range localityEpMap { - locs = append(locs, k) - } - if len(locs) >= 2 { - sort.Strings(locs) - } - for _, k := range locs { - locLbEps := localityEpMap[k] - var weight uint32 - for _, ep := range locLbEps.llbEndpoints.LbEndpoints { - weight += ep.LoadBalancingWeight.GetValue() - } - locLbEps.llbEndpoints.LoadBalancingWeight = &wrappers.UInt32Value{ - Value: weight, - } - locEps = append(locEps, locLbEps) - } - - if len(locEps) == 0 { - b.push.AddMetric(model.ProxyStatusClusterNoInstances, b.clusterName, "", "") - } - - return locEps -} - -// TODO(lambdai): Handle ApplyTunnel error return value by filter out the failed endpoint. -func (b *EndpointBuilder) ApplyTunnelSetting(llbOpts []*LocLbEndpointsAndOptions, tunnelType networking.TunnelType) []*LocLbEndpointsAndOptions { - for _, llb := range llbOpts { - for i, ep := range llb.llbEndpoints.LbEndpoints { - newEp, err := llb.tunnelMetadata[i].ApplyTunnel(ep, tunnelType) - if err != nil { - panic("not implemented yet on failing to apply tunnel") - } else { - llb.llbEndpoints.LbEndpoints[i] = newEp - } - } - } - return llbOpts -} - -// Create the CLusterLoadAssignment. At this moment the options must have been applied to the locality lb endpoints. -func (b *EndpointBuilder) createClusterLoadAssignment(llbOpts []*LocLbEndpointsAndOptions) *endpoint.ClusterLoadAssignment { - llbEndpoints := make([]*endpoint.LocalityLbEndpoints, 0, len(llbOpts)) - for _, l := range llbOpts { - llbEndpoints = append(llbEndpoints, &l.llbEndpoints) - } - return &endpoint.ClusterLoadAssignment{ - ClusterName: b.clusterName, - Endpoints: llbEndpoints, - } -} - -// buildEnvoyLbEndpoint packs the endpoint based on istio info. -func buildEnvoyLbEndpoint(e *model.IstioEndpoint) *endpoint.LbEndpoint { - addr := util.BuildAddress(e.Address, e.EndpointPort) - healthStatus := core.HealthStatus_HEALTHY - if e.HealthStatus == model.UnHealthy { - healthStatus = core.HealthStatus_UNHEALTHY - } - - ep := &endpoint.LbEndpoint{ - HealthStatus: healthStatus, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: e.GetLoadBalancingWeight(), - }, - HostIdentifier: &endpoint.LbEndpoint_Endpoint{ - Endpoint: &endpoint.Endpoint{ - Address: addr, - }, - }, - } - - // Istio telemetry depends on the metadata value being set for endpoints in the mesh. - // Istio endpoint level tls transport socket configuration depends on this logic - // Do not remove pilot/pkg/xds/fake.go - ep.Metadata = util.BuildLbEndpointMetadata(e.Network, e.TLSMode, e.WorkloadName, e.Namespace, e.Locality.ClusterID, e.Labels) - - return ep -} - -// TODO this logic is probably done elsewhere in XDS, possible code-reuse + perf improvements -type mtlsChecker struct { - push *model.PushContext - svcPort int - destinationRule *networkingapi.DestinationRule - - // cache of host identifiers that have mTLS disabled - mtlsDisabledHosts map[string]struct{} - - // cache of labels/port that have mTLS disabled by peerAuthn - peerAuthDisabledMTLS map[string]bool - // cache of labels that have mTLS modes set for subset policies - subsetPolicyMode map[string]*networkingapi.ClientTLSSettings_TLSmode - // the tlsMode of the root traffic policy if it's set - rootPolicyMode *networkingapi.ClientTLSSettings_TLSmode -} - -func newMtlsChecker(push *model.PushContext, svcPort int, dr *config.Config) *mtlsChecker { - var drSpec *networkingapi.DestinationRule - if dr != nil { - drSpec = dr.Spec.(*networkingapi.DestinationRule) - } - return &mtlsChecker{ - push: push, - svcPort: svcPort, - destinationRule: drSpec, - mtlsDisabledHosts: map[string]struct{}{}, - peerAuthDisabledMTLS: map[string]bool{}, - subsetPolicyMode: map[string]*networkingapi.ClientTLSSettings_TLSmode{}, - rootPolicyMode: mtlsModeForDefaultTrafficPolicy(dr, svcPort), - } -} - -// mTLSDisabled returns true if the given lbEp has mTLS disabled due to any of: -// - disabled tlsMode -// - DestinationRule disabling mTLS on the entire host or the port -// - PeerAuthentication disabling mTLS at any applicable level (mesh, ns, workload, port) -func (c *mtlsChecker) isMtlsDisabled(lbEp *endpoint.LbEndpoint) bool { - if c == nil { - return false - } - _, ok := c.mtlsDisabledHosts[lbEpKey(lbEp)] - return ok -} - -// computeForEndpoint checks destination rule, peer authentication and tls mode labels to determine if mTLS was turned off. -func (c *mtlsChecker) computeForEndpoint(ep *model.IstioEndpoint) { - if drMode := c.mtlsModeForDestinationRule(ep); drMode != nil { - switch *drMode { - case networkingapi.ClientTLSSettings_DISABLE: - c.mtlsDisabledHosts[lbEpKey(ep.EnvoyEndpoint)] = struct{}{} - return - case networkingapi.ClientTLSSettings_ISTIO_MUTUAL: - // don't mark this EP disabled, even if PA or tlsMode meta mark disabled - return - } - } - - // if endpoint has no sidecar or explicitly tls disabled by "security.istio.io/tlsMode" label. - if ep.TLSMode != model.IstioMutualTLSModeLabel { - c.mtlsDisabledHosts[lbEpKey(ep.EnvoyEndpoint)] = struct{}{} - return - } - - mtlsDisabledByPeerAuthentication := func(ep *model.IstioEndpoint) bool { - // apply any matching peer authentications - peerAuthnKey := ep.Labels.String() + ":" + strconv.Itoa(int(ep.EndpointPort)) - if value, ok := c.peerAuthDisabledMTLS[peerAuthnKey]; ok { - // avoid recomputing since most EPs will have the same labels/port - return value - } - c.peerAuthDisabledMTLS[peerAuthnKey] = factory. - NewPolicyApplier(c.push, ep.Namespace, ep.Labels). - GetMutualTLSModeForPort(ep.EndpointPort) == model.MTLSDisable - return c.peerAuthDisabledMTLS[peerAuthnKey] - } - - // mtls disabled by PeerAuthentication - if mtlsDisabledByPeerAuthentication(ep) { - c.mtlsDisabledHosts[lbEpKey(ep.EnvoyEndpoint)] = struct{}{} - } -} - -func (c *mtlsChecker) mtlsModeForDestinationRule(ep *model.IstioEndpoint) *networkingapi.ClientTLSSettings_TLSmode { - if c.destinationRule == nil || len(c.destinationRule.Subsets) == 0 { - return c.rootPolicyMode - } - - drSubsetKey := ep.Labels.String() - if value, ok := c.subsetPolicyMode[drSubsetKey]; ok { - // avoid recomputing since most EPs will have the same labels/port - return value - } - - subsetValue := c.rootPolicyMode - for _, subset := range c.destinationRule.Subsets { - if labels.Instance(subset.Labels).SubsetOf(ep.Labels) { - mode := trafficPolicyTLSModeForPort(subset.TrafficPolicy, c.svcPort) - if mode != nil { - subsetValue = mode - } - break - } - } - c.subsetPolicyMode[drSubsetKey] = subsetValue - return subsetValue -} - -// mtlsModeForDefaultTrafficPolicy returns true if the default traffic policy on a given dr disables mTLS -func mtlsModeForDefaultTrafficPolicy(destinationRule *config.Config, port int) *networkingapi.ClientTLSSettings_TLSmode { - if destinationRule == nil { - return nil - } - dr, ok := destinationRule.Spec.(*networkingapi.DestinationRule) - if !ok || dr == nil { - return nil - } - return trafficPolicyTLSModeForPort(dr.GetTrafficPolicy(), port) -} - -func trafficPolicyTLSModeForPort(tp *networkingapi.TrafficPolicy, port int) *networkingapi.ClientTLSSettings_TLSmode { - if tp == nil { - return nil - } - var mode *networkingapi.ClientTLSSettings_TLSmode - if tp.Tls != nil { - mode = &tp.Tls.Mode - } - // if there is a port-level setting matching this cluster - for _, portSettings := range tp.GetPortLevelSettings() { - if int(portSettings.Port.Number) == port && portSettings.Tls != nil { - mode = &portSettings.Tls.Mode - break - } - } - return mode -} - -func lbEpKey(b *endpoint.LbEndpoint) string { - if addr := b.GetEndpoint().GetAddress().GetSocketAddress(); addr != nil { - return addr.Address + ":" + strconv.Itoa(int(addr.GetPortValue())) - } - if addr := b.GetEndpoint().GetAddress().GetPipe(); addr != nil { - return addr.GetPath() + ":" + strconv.Itoa(int(addr.GetMode())) - } - return "" -} diff --git a/pilot/pkg/xds/ep_filters.go b/pilot/pkg/xds/ep_filters.go deleted file mode 100644 index 0f5a9c58e..000000000 --- a/pilot/pkg/xds/ep_filters.go +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "math" -) - -import ( - endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" - "google.golang.org/protobuf/proto" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - labelutil "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/util/label" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/network" -) - -// EndpointsByNetworkFilter is a network filter function to support Split Horizon EDS - filter the endpoints based on the network -// of the connected sidecar. The filter will filter out all endpoints which are not present within the -// sidecar network and add a gateway endpoint to remote networks that have endpoints -// (if gateway exists and its IP is an IP and not a dns name). -// Information for the mesh networks is provided as a MeshNetwork config map. -func (b *EndpointBuilder) EndpointsByNetworkFilter(endpoints []*LocLbEndpointsAndOptions) []*LocLbEndpointsAndOptions { - if !b.push.NetworkManager().IsMultiNetworkEnabled() { - // Multi-network is not configured (this is the case by default). Just access all endpoints directly. - return endpoints - } - - // A new array of endpoints to be returned that will have both local and - // remote gateways (if any) - filtered := make([]*LocLbEndpointsAndOptions, 0) - - // Scale all weights by the lcm of gateways per network and gateways per cluster. - // This will allow us to more easily spread traffic to the endpoint across multiple - // network gateways, increasing reliability of the endpoint. - scaleFactor := b.push.NetworkManager().GetLBWeightScaleFactor() - - // Go through all cluster endpoints and add those with the same network as the sidecar - // to the result. Also count the number of endpoints per each remote network while - // iterating so that it can be used as the weight for the gateway endpoint - for _, ep := range endpoints { - lbEndpoints := &LocLbEndpointsAndOptions{ - llbEndpoints: endpoint.LocalityLbEndpoints{ - Locality: ep.llbEndpoints.Locality, - Priority: ep.llbEndpoints.Priority, - // Endpoints and weight will be reset below. - }, - } - - // Create a map to keep track of the gateways used and their aggregate weights. - gatewayWeights := make(map[model.NetworkGateway]uint32) - - // Process all of the endpoints. - for i, lbEp := range ep.llbEndpoints.LbEndpoints { - istioEndpoint := ep.istioEndpoints[i] - - // If the proxy can't view the network for this endpoint, exclude it entirely. - if !b.proxyView.IsVisible(istioEndpoint) { - continue - } - - // Copy the endpoint in order to expand the load balancing weight. - // When multiplying, be careful to avoid overflow - clipping the - // result at the maximum value for uint32. - weight := b.scaleEndpointLBWeight(lbEp, scaleFactor) - if lbEp.GetLoadBalancingWeight().GetValue() != weight { - lbEp = proto.Clone(lbEp).(*endpoint.LbEndpoint) - lbEp.LoadBalancingWeight = &wrappers.UInt32Value{ - Value: weight, - } - } - - epNetwork := istioEndpoint.Network - epCluster := istioEndpoint.Locality.ClusterID - gateways := b.selectNetworkGateways(epNetwork, epCluster) - - // Check if the endpoint is directly reachable. It's considered directly reachable if - // the endpoint is either on the local network or on a remote network that can be reached - // directly from the local network. - if b.proxy.InNetwork(epNetwork) || len(gateways) == 0 { - // The endpoint is directly reachable - just add it. - lbEndpoints.append(ep.istioEndpoints[i], lbEp, ep.istioEndpoints[i].TunnelAbility) - continue - } - - // Cross-network traffic relies on mTLS to be enabled for SNI routing - // TODO BTS may allow us to work around this - if b.mtlsChecker.isMtlsDisabled(lbEp) { - continue - } - - // Apply the weight for this endpoint to the network gateways. - splitWeightAmongGateways(weight, gateways, gatewayWeights) - } - - // Sort the gateways into an ordered list so that the generated endpoints are deterministic. - gateways := make([]model.NetworkGateway, 0, len(gatewayWeights)) - for gw := range gatewayWeights { - gateways = append(gateways, gw) - } - gateways = model.SortGateways(gateways) - - // Create endpoints for the gateways. - for _, gw := range gateways { - epWeight := gatewayWeights[gw] - if epWeight == 0 { - log.Warnf("gateway weight must be greater than 0, scaleFactor is %d", scaleFactor) - epWeight = 1 - } - epAddr := util.BuildAddress(gw.Addr, gw.Port) - - // Generate a fake IstioEndpoint to carry network and cluster information. - gwIstioEp := &model.IstioEndpoint{ - Network: gw.Network, - Locality: model.Locality{ - ClusterID: gw.Cluster, - }, - Labels: labelutil.AugmentLabels(nil, gw.Cluster, "", gw.Network), - } - - // Generate the EDS endpoint for this gateway. - gwEp := &endpoint.LbEndpoint{ - HostIdentifier: &endpoint.LbEndpoint_Endpoint{ - Endpoint: &endpoint.Endpoint{ - Address: epAddr, - }, - }, - LoadBalancingWeight: &wrappers.UInt32Value{ - Value: epWeight, - }, - } - // TODO: figure out a way to extract locality data from the gateway public endpoints in meshNetworks - gwEp.Metadata = util.BuildLbEndpointMetadata(gw.Network, model.IstioMutualTLSModeLabel, - "", "", b.clusterID, labels.Instance{}) - // Currently gateway endpoint does not support tunnel. - lbEndpoints.append(gwIstioEp, gwEp, networking.MakeTunnelAbility()) - } - - // Endpoint members could be stripped or aggregated by network. Adjust weight value here. - lbEndpoints.refreshWeight() - filtered = append(filtered, lbEndpoints) - } - - return filtered -} - -// selectNetworkGateways chooses the gateways that best match the network and cluster. If there is -// no match for the network+cluster, then all gateways matching the network are returned. Preferring -// gateways that match against cluster has the following advantages: -// -// 1. Potentially reducing extra latency incurred when the gateway and endpoint reside in different -// clusters. -// -// 2. Enables Kubernetes MCS use cases, where endpoints for a service might be exported in one -// cluster but not another within the same network. By targeting the gateway for the cluster -// where the exported endpoints reside, we ensure that we only send traffic to exported endpoints. -func (b *EndpointBuilder) selectNetworkGateways(nw network.ID, c cluster.ID) []model.NetworkGateway { - // Get the gateways for this network+cluster combination. - gws := b.push.NetworkManager().GatewaysForNetworkAndCluster(nw, c) - if len(gws) == 0 { - // No match for network+cluster, just match the network. - gws = b.push.NetworkManager().GatewaysForNetwork(nw) - } - return gws -} - -func (b *EndpointBuilder) scaleEndpointLBWeight(ep *endpoint.LbEndpoint, scaleFactor uint32) uint32 { - if ep.GetLoadBalancingWeight() == nil || ep.GetLoadBalancingWeight().Value == 0 { - return scaleFactor - } - weight := uint32(math.MaxUint32) - if ep.GetLoadBalancingWeight().Value < math.MaxUint32/scaleFactor { - weight = ep.GetLoadBalancingWeight().Value * scaleFactor - } - return weight -} - -// Apply the weight for this endpoint to the network gateways. -func splitWeightAmongGateways(weight uint32, gateways []model.NetworkGateway, gatewayWeights map[model.NetworkGateway]uint32) { - // Spread the weight across the gateways. - weightPerGateway := weight / uint32(len(gateways)) - for _, gateway := range gateways { - gatewayWeights[gateway] += weightPerGateway - } -} - -// EndpointsWithMTLSFilter removes all endpoints that do not handle mTLS. This is determined by looking at -// auto-mTLS, DestinationRule, and PeerAuthentication to determine if we would send mTLS to these endpoints. -// Note there is no guarantee these destinations *actually* handle mTLS; just that we are configured to send mTLS to them. -func (b *EndpointBuilder) EndpointsWithMTLSFilter(endpoints []*LocLbEndpointsAndOptions) []*LocLbEndpointsAndOptions { - // A new array of endpoints to be returned that will have both local and - // remote gateways (if any) - filtered := make([]*LocLbEndpointsAndOptions, 0) - - // Go through all cluster endpoints and add those with mTLS enabled - for _, ep := range endpoints { - lbEndpoints := &LocLbEndpointsAndOptions{ - llbEndpoints: endpoint.LocalityLbEndpoints{ - Locality: ep.llbEndpoints.Locality, - Priority: ep.llbEndpoints.Priority, - // Endpoints and will be reset below. - }, - } - - for i, lbEp := range ep.llbEndpoints.LbEndpoints { - if b.mtlsChecker.isMtlsDisabled(lbEp) { - // no mTLS, skip it - continue - } - lbEndpoints.append(ep.istioEndpoints[i], lbEp, ep.istioEndpoints[i].TunnelAbility) - } - - filtered = append(filtered, lbEndpoints) - } - - return filtered -} diff --git a/pilot/pkg/xds/ep_filters_test.go b/pilot/pkg/xds/ep_filters_test.go deleted file mode 100644 index cc0b5e84c..000000000 --- a/pilot/pkg/xds/ep_filters_test.go +++ /dev/null @@ -1,792 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "reflect" - "sort" - "testing" -) - -import ( - endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" - networking "istio.io/api/networking/v1alpha3" - security "istio.io/api/security/v1beta1" - "istio.io/api/type/v1beta1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/network" - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -type LbEpInfo struct { - address string - // nolint: structcheck - weight uint32 -} - -type LocLbEpInfo struct { - lbEps []LbEpInfo - weight uint32 -} - -func (i LocLbEpInfo) getAddrs() []string { - addrs := make([]string, 0) - for _, ep := range i.lbEps { - addrs = append(addrs, ep.address) - } - return addrs -} - -var networkFiltered = []networkFilterCase{ - { - name: "from_network1_cluster1a", - conn: xdsConnection("network1", "cluster1a"), - want: []LocLbEpInfo{ - { - lbEps: []LbEpInfo{ - // 2 local endpoints on network1 - {address: "10.0.0.1", weight: 6}, - {address: "10.0.0.2", weight: 6}, - // 1 endpoint on network2, cluster2a - {address: "2.2.2.2", weight: 6}, - // 2 endpoints on network2, cluster2b - {address: "2.2.2.20", weight: 6}, - {address: "2.2.2.21", weight: 6}, - // 1 endpoint on network4 with no gateway (i.e. directly accessible) - {address: "40.0.0.1", weight: 6}, - }, - weight: 36, - }, - }, - }, - { - name: "from_network1_cluster1b", - conn: xdsConnection("network1", "cluster1b"), - want: []LocLbEpInfo{ - { - lbEps: []LbEpInfo{ - // 2 local endpoints on network1 - {address: "10.0.0.1", weight: 6}, - {address: "10.0.0.2", weight: 6}, - // 1 endpoint on network2, cluster2a - {address: "2.2.2.2", weight: 6}, - // 2 endpoints on network2, cluster2b - {address: "2.2.2.20", weight: 6}, - {address: "2.2.2.21", weight: 6}, - // 1 endpoint on network4 with no gateway (i.e. directly accessible) - {address: "40.0.0.1", weight: 6}, - }, - weight: 36, - }, - }, - }, - { - name: "from_network2_cluster2a", - conn: xdsConnection("network2", "cluster2a"), - want: []LocLbEpInfo{ - { - lbEps: []LbEpInfo{ - // 3 local endpoints in network2 - {address: "20.0.0.1", weight: 6}, - {address: "20.0.0.2", weight: 6}, - {address: "20.0.0.3", weight: 6}, - // 2 endpoint on network1 with weight aggregated at the gateway - {address: "1.1.1.1", weight: 12}, - // 1 endpoint on network4 with no gateway (i.e. directly accessible) - {address: "40.0.0.1", weight: 6}, - }, - weight: 36, - }, - }, - }, - { - name: "from_network2_cluster2b", - conn: xdsConnection("network2", "cluster2b"), - want: []LocLbEpInfo{ - { - lbEps: []LbEpInfo{ - // 3 local endpoints in network2 - {address: "20.0.0.1", weight: 6}, - {address: "20.0.0.2", weight: 6}, - {address: "20.0.0.3", weight: 6}, - // 2 endpoint on network1 with weight aggregated at the gateway - {address: "1.1.1.1", weight: 12}, - // 1 endpoint on network4 with no gateway (i.e. directly accessible) - {address: "40.0.0.1", weight: 6}, - }, - weight: 36, - }, - }, - }, - { - name: "from_network3_cluster3", - conn: xdsConnection("network3", "cluster3"), - want: []LocLbEpInfo{ - { - lbEps: []LbEpInfo{ - // 2 endpoint on network2 with weight aggregated at the gateway - {address: "1.1.1.1", weight: 12}, - // 1 endpoint on network2, cluster2a - {address: "2.2.2.2", weight: 6}, - // 2 endpoints on network2, cluster2b - {address: "2.2.2.20", weight: 6}, - {address: "2.2.2.21", weight: 6}, - // 1 endpoint on network4 with no gateway (i.e. directly accessible) - {address: "40.0.0.1", weight: 6}, - }, - weight: 36, - }, - }, - }, - { - name: "from_network4_cluster4", - conn: xdsConnection("network4", "cluster4"), - want: []LocLbEpInfo{ - { - lbEps: []LbEpInfo{ - // 1 local endpoint on network4 - {address: "40.0.0.1", weight: 6}, - // 2 endpoint on network2 with weight aggregated at the gateway - {address: "1.1.1.1", weight: 12}, - // 1 endpoint on network2, cluster2a - {address: "2.2.2.2", weight: 6}, - // 2 endpoints on network2, cluster2b - {address: "2.2.2.20", weight: 6}, - {address: "2.2.2.21", weight: 6}, - }, - weight: 36, - }, - }, - }, -} - -func TestEndpointsByNetworkFilter(t *testing.T) { - env := environment(t) - env.Env().InitNetworksManager(env.Discovery) - // The tests below are calling the endpoints filter from each one of the - // networks and examines the returned filtered endpoints - runNetworkFilterTest(t, env, networkFiltered) -} - -func TestEndpointsByNetworkFilter_WithConfig(t *testing.T) { - noCrossNetwork := []networkFilterCase{ - { - name: "from_network1_cluster1a", - conn: xdsConnection("network1", "cluster1a"), - want: []LocLbEpInfo{ - { - lbEps: []LbEpInfo{ - // 2 local endpoints on network1 - {address: "10.0.0.1", weight: 6}, - {address: "10.0.0.2", weight: 6}, - // 1 endpoint on network4 with no gateway (i.e. directly accessible) - {address: "40.0.0.1", weight: 6}, - }, - weight: 18, - }, - }, - }, - { - name: "from_network1_cluster1b", - conn: xdsConnection("network1", "cluster1b"), - want: []LocLbEpInfo{ - { - lbEps: []LbEpInfo{ - // 2 local endpoints on network1 - {address: "10.0.0.1", weight: 6}, - {address: "10.0.0.2", weight: 6}, - // 1 endpoint on network4 with no gateway (i.e. directly accessible) - {address: "40.0.0.1", weight: 6}, - }, - weight: 18, - }, - }, - }, - { - name: "from_network2_cluster2a", - conn: xdsConnection("network2", "cluster2a"), - want: []LocLbEpInfo{ - { - lbEps: []LbEpInfo{ - // 1 local endpoint on network2 - {address: "20.0.0.1", weight: 6}, - {address: "20.0.0.2", weight: 6}, - {address: "20.0.0.3", weight: 6}, - // 1 endpoint on network4 with no gateway (i.e. directly accessible) - {address: "40.0.0.1", weight: 6}, - }, - weight: 24, - }, - }, - }, - { - name: "from_network2_cluster2b", - conn: xdsConnection("network2", "cluster2b"), - want: []LocLbEpInfo{ - { - lbEps: []LbEpInfo{ - // 1 local endpoint on network2 - {address: "20.0.0.1", weight: 6}, - {address: "20.0.0.2", weight: 6}, - {address: "20.0.0.3", weight: 6}, - // 1 endpoint on network4 with no gateway (i.e. directly accessible) - {address: "40.0.0.1", weight: 6}, - }, - weight: 24, - }, - }, - }, - { - name: "from_network3_cluster3", - conn: xdsConnection("network3", "cluster3"), - want: []LocLbEpInfo{ - { - lbEps: []LbEpInfo{ - // 1 endpoint on network4 with no gateway (i.e. directly accessible) - {address: "40.0.0.1", weight: 6}, - }, - weight: 6, - }, - }, - }, - { - name: "from_network4_cluster4", - conn: xdsConnection("network4", "cluster4"), - want: []LocLbEpInfo{ - { - lbEps: []LbEpInfo{ - // 1 local endpoint on network4 - {address: "40.0.0.1", weight: 6}, - }, - weight: 6, - }, - }, - }, - } - - cases := map[string]map[string]struct { - Config config.Config - Configs []config.Config - Tests []networkFilterCase - }{ - gvk.PeerAuthentication.String(): { - "mtls-off-ineffective": { - Config: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - Name: "mtls-partial", - Namespace: "dubbo-system", - }, - Spec: &security.PeerAuthentication{ - Selector: &v1beta1.WorkloadSelector{ - // shouldn't affect our test workload - MatchLabels: map[string]string{"app": "b"}, - }, - Mtls: &security.PeerAuthentication_MutualTLS{Mode: security.PeerAuthentication_MutualTLS_DISABLE}, - }, - }, - Tests: networkFiltered, - }, - "mtls-on-strict": { - Config: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - Name: "mtls-on", - Namespace: "dubbo-system", - }, - Spec: &security.PeerAuthentication{ - Mtls: &security.PeerAuthentication_MutualTLS{Mode: security.PeerAuthentication_MutualTLS_STRICT}, - }, - }, - Tests: networkFiltered, - }, - "mtls-off-global": { - Config: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - Name: "mtls-off", - Namespace: "dubbo-system", - }, - Spec: &security.PeerAuthentication{ - Mtls: &security.PeerAuthentication_MutualTLS{Mode: security.PeerAuthentication_MutualTLS_DISABLE}, - }, - }, - Tests: noCrossNetwork, - }, - "mtls-off-namespace": { - Config: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - Name: "mtls-off", - Namespace: "ns", - }, - Spec: &security.PeerAuthentication{ - Mtls: &security.PeerAuthentication_MutualTLS{Mode: security.PeerAuthentication_MutualTLS_DISABLE}, - }, - }, - Tests: noCrossNetwork, - }, - "mtls-off-workload": { - Config: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - Name: "mtls-off", - Namespace: "ns", - }, - Spec: &security.PeerAuthentication{ - Selector: &v1beta1.WorkloadSelector{ - MatchLabels: map[string]string{"app": "example"}, - }, - Mtls: &security.PeerAuthentication_MutualTLS{Mode: security.PeerAuthentication_MutualTLS_DISABLE}, - }, - }, - Tests: noCrossNetwork, - }, - "mtls-off-port": { - Config: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - Name: "mtls-off", - Namespace: "ns", - }, - Spec: &security.PeerAuthentication{ - Selector: &v1beta1.WorkloadSelector{ - MatchLabels: map[string]string{"app": "example"}, - }, - PortLevelMtls: map[uint32]*security.PeerAuthentication_MutualTLS{ - 8080: {Mode: security.PeerAuthentication_MutualTLS_DISABLE}, - }, - }, - }, - Tests: noCrossNetwork, - }, - }, - gvk.DestinationRule.String(): { - "mtls-on-override-pa": { - Configs: []config.Config{ - { - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - Name: "mtls-off", - Namespace: "ns", - }, - Spec: &security.PeerAuthentication{ - Mtls: &security.PeerAuthentication_MutualTLS{Mode: security.PeerAuthentication_MutualTLS_DISABLE}, - }, - }, - { - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: "mtls-on", - Namespace: "ns", - }, - Spec: &networking.DestinationRule{ - Host: "example.ns.svc.cluster.local", - TrafficPolicy: &networking.TrafficPolicy{ - Tls: &networking.ClientTLSSettings{Mode: networking.ClientTLSSettings_ISTIO_MUTUAL}, - }, - }, - }, - }, - Tests: networkFiltered, - }, - "mtls-off-innefective": { - Config: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: "mtls-off", - Namespace: "ns", - }, - Spec: &networking.DestinationRule{ - Host: "other.ns.svc.cluster.local", - TrafficPolicy: &networking.TrafficPolicy{ - Tls: &networking.ClientTLSSettings{Mode: networking.ClientTLSSettings_DISABLE}, - }, - }, - }, - Tests: networkFiltered, - }, - "mtls-on-destination-level": { - Config: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: "mtls-on", - Namespace: "ns", - }, - Spec: &networking.DestinationRule{ - Host: "example.ns.svc.cluster.local", - TrafficPolicy: &networking.TrafficPolicy{ - Tls: &networking.ClientTLSSettings{Mode: networking.ClientTLSSettings_ISTIO_MUTUAL}, - }, - }, - }, - Tests: networkFiltered, - }, - "mtls-on-port-level": { - Config: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: "mtls-on", - Namespace: "ns", - }, - Spec: &networking.DestinationRule{ - Host: "example.ns.svc.cluster.local", - TrafficPolicy: &networking.TrafficPolicy{ - PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{{ - Port: &networking.PortSelector{Number: 80}, - Tls: &networking.ClientTLSSettings{Mode: networking.ClientTLSSettings_ISTIO_MUTUAL}, - }}, - }, - }, - }, - Tests: networkFiltered, - }, - "mtls-off-destination-level": { - Config: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: "mtls-off", - Namespace: "ns", - }, - Spec: &networking.DestinationRule{ - Host: "example.ns.svc.cluster.local", - TrafficPolicy: &networking.TrafficPolicy{ - Tls: &networking.ClientTLSSettings{Mode: networking.ClientTLSSettings_DISABLE}, - }, - }, - }, - Tests: noCrossNetwork, - }, - "mtls-off-port-level": { - Config: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: "mtls-off", - Namespace: "ns", - }, - Spec: &networking.DestinationRule{ - Host: "example.ns.svc.cluster.local", - TrafficPolicy: &networking.TrafficPolicy{ - PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{{ - Port: &networking.PortSelector{Number: 80}, - Tls: &networking.ClientTLSSettings{Mode: networking.ClientTLSSettings_DISABLE}, - }}, - }, - }, - }, - Tests: noCrossNetwork, - }, - "mtls-off-subset-level": { - Config: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: "mtls-off", - Namespace: "ns", - }, - Spec: &networking.DestinationRule{ - Host: "example.ns.svc.cluster.local", - TrafficPolicy: &networking.TrafficPolicy{ - // should be overridden by subset - Tls: &networking.ClientTLSSettings{Mode: networking.ClientTLSSettings_ISTIO_MUTUAL}, - }, - Subsets: []*networking.Subset{{ - Name: "disable-tls", - Labels: map[string]string{"app": "example"}, - TrafficPolicy: &networking.TrafficPolicy{ - Tls: &networking.ClientTLSSettings{Mode: networking.ClientTLSSettings_DISABLE}, - }, - }}, - }, - }, - Tests: noCrossNetwork, - }, - "mtls-on-subset-level": { - Config: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.DestinationRule, - Name: "mtls-on", - Namespace: "ns", - }, - Spec: &networking.DestinationRule{ - Host: "example.ns.svc.cluster.local", - TrafficPolicy: &networking.TrafficPolicy{ - // should be overridden by subset - Tls: &networking.ClientTLSSettings{Mode: networking.ClientTLSSettings_DISABLE}, - }, - Subsets: []*networking.Subset{{ - Name: "disable-tls", - Labels: map[string]string{"app": "example"}, - TrafficPolicy: &networking.TrafficPolicy{ - Tls: &networking.ClientTLSSettings{Mode: networking.ClientTLSSettings_ISTIO_MUTUAL}, - }, - }}, - }, - }, - Tests: networkFiltered, - }, - }, - } - - for configType, cases := range cases { - t.Run(configType, func(t *testing.T) { - for name, pa := range cases { - t.Run(name, func(t *testing.T) { - cfgs := pa.Configs - if pa.Config.Name != "" { - cfgs = append(cfgs, pa.Config) - } - env := environment(t, cfgs...) - runNetworkFilterTest(t, env, pa.Tests) - }) - } - }) - } -} - -func TestEndpointsByNetworkFilter_SkipLBWithHostname(t *testing.T) { - // - 1 IP gateway for network1 - // - 1 DNS gateway for network2 - // - 1 IP gateway for network3 - // - 0 gateways for network4 - ds := environment(t) - origServices := ds.Env().Services() - origGateways := ds.Env().NetworkGateways() - ds.MemRegistry.AddService(&model.Service{ - Hostname: "istio-ingressgateway.dubbo-system.svc.cluster.local", - Attributes: model.ServiceAttributes{ - ClusterExternalAddresses: model.AddressMap{ - Addresses: map[cluster.ID][]string{ - "cluster2a": {""}, - "cluster2b": {""}, - }, - }, - }, - }) - for _, svc := range origServices { - ds.MemRegistry.AddService(svc) - } - ds.MemRegistry.AddGateways(origGateways...) - // Also add a hostname-based Gateway, which will be rejected. - ds.MemRegistry.AddGateways(model.NetworkGateway{ - Network: "network2", - Addr: "aeiou.scooby.do", - Port: 80, - }) - - // Run the tests and ensure that the new gateway is never used. - runNetworkFilterTest(t, ds, networkFiltered) -} - -type networkFilterCase struct { - name string - conn *Connection - want []LocLbEpInfo -} - -// runNetworkFilterTest calls the endpoints filter from each one of the -// networks and examines the returned filtered endpoints -func runNetworkFilterTest(t *testing.T, ds *FakeDiscoveryServer, tests []networkFilterCase) { - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - proxy := ds.SetupProxy(tt.conn.proxy) - b := NewEndpointBuilder("outbound|80||example.ns.svc.cluster.local", proxy, ds.PushContext()) - testEndpoints := b.buildLocalityLbEndpointsFromShards(testShards(), &model.Port{Name: "http", Port: 80, Protocol: protocol.HTTP}) - filtered := b.EndpointsByNetworkFilter(testEndpoints) - for _, e := range testEndpoints { - e.AssertInvarianceInTest() - } - if len(filtered) != len(tt.want) { - t.Errorf("Unexpected number of filtered endpoints: got %v, want %v", len(filtered), len(tt.want)) - return - } - - sort.Slice(filtered, func(i, j int) bool { - addrI := filtered[i].llbEndpoints.LbEndpoints[0].GetEndpoint().Address.GetSocketAddress().Address - addrJ := filtered[j].llbEndpoints.LbEndpoints[0].GetEndpoint().Address.GetSocketAddress().Address - return addrI < addrJ - }) - - for i, ep := range filtered { - if len(ep.llbEndpoints.LbEndpoints) != len(tt.want[i].lbEps) { - t.Errorf("Unexpected number of LB endpoints within endpoint %d: %v, want %v", - i, getLbEndpointAddrs(&ep.llbEndpoints), tt.want[i].getAddrs()) - } - - if ep.llbEndpoints.LoadBalancingWeight.GetValue() != tt.want[i].weight { - t.Errorf("Unexpected weight for endpoint %d: got %v, want %v", i, ep.llbEndpoints.LoadBalancingWeight.GetValue(), tt.want[i].weight) - } - - for _, lbEp := range ep.llbEndpoints.LbEndpoints { - addr := lbEp.GetEndpoint().Address.GetSocketAddress().Address - found := false - for _, wantLbEp := range tt.want[i].lbEps { - if addr == wantLbEp.address { - found = true - - // Now compare the weight. - if lbEp.GetLoadBalancingWeight().Value != wantLbEp.weight { - t.Errorf("Unexpected weight for endpoint %s: got %v, want %v", - addr, lbEp.GetLoadBalancingWeight().Value, wantLbEp.weight) - } - break - } - } - if !found { - t.Errorf("Unexpected address for endpoint %d: %v", i, addr) - } - } - } - - b2 := NewEndpointBuilder("outbound|80||example.ns.svc.cluster.local", proxy, ds.PushContext()) - testEndpoints2 := b2.buildLocalityLbEndpointsFromShards(testShards(), &model.Port{Name: "http", Port: 80, Protocol: protocol.HTTP}) - filtered2 := b2.EndpointsByNetworkFilter(testEndpoints2) - if !reflect.DeepEqual(filtered2, filtered) { - t.Fatalf("output of EndpointsByNetworkFilter is non-deterministic") - } - }) - } -} - -func xdsConnection(nw network.ID, c cluster.ID) *Connection { - return &Connection{ - proxy: &model.Proxy{ - Metadata: &model.NodeMetadata{ - Network: nw, - ClusterID: c, - }, - }, - } -} - -// environment defines the networks with: -// - 1 gateway for network1 -// - 3 gateway for network2 -// - 1 gateway for network3 -// - 0 gateways for network4 -func environment(t test.Failer, c ...config.Config) *FakeDiscoveryServer { - ds := NewFakeDiscoveryServer(t, FakeOptions{ - Configs: c, - Services: []*model.Service{{ - Hostname: "example.ns.svc.cluster.local", - Attributes: model.ServiceAttributes{Name: "example", Namespace: "ns"}, - }}, - Gateways: []model.NetworkGateway{ - // network1 has only 1 gateway in cluster1a, which will be used for the endpoints - // in both cluster1a and cluster1b. - { - Network: "network1", - Cluster: "cluster1a", - Addr: "1.1.1.1", - Port: 80, - }, - - // network2 has one gateway in each cluster2a and cluster2b. When targeting a particular - // endpoint, only the gateway for its cluster will be selected. Since the clusters do not - // have the same number of endpoints, the weights for the gateways will be different. - { - Network: "network2", - Cluster: "cluster2a", - Addr: "2.2.2.2", - Port: 80, - }, - { - Network: "network2", - Cluster: "cluster2b", - Addr: "2.2.2.20", - Port: 80, - }, - { - Network: "network2", - Cluster: "cluster2b", - Addr: "2.2.2.21", - Port: 80, - }, - - // network3 has a gateway in cluster3, but no endpoints. - { - Network: "network3", - Cluster: "cluster3", - Addr: "3.3.3.3", - Port: 443, - }, - }, - }) - return ds -} - -// testShards creates endpoints to be handed to the filter: -// - 2 endpoints in network1 -// - 1 endpoints in network2 -// - 0 endpoints in network3 -// - 1 endpoints in network4 -// -// All endpoints are part of service example.ns.svc.cluster.local on port 80 (http). -func testShards() *model.EndpointShards { - shards := &model.EndpointShards{Shards: map[model.ShardKey][]*model.IstioEndpoint{ - // network1 has one endpoint in each cluster - {Cluster: "cluster1a"}: { - {Network: "network1", Address: "10.0.0.1"}, - }, - {Cluster: "cluster1b"}: { - {Network: "network1", Address: "10.0.0.2"}, - }, - - // network2 has an imbalance of endpoints between its clusters - {Cluster: "cluster2a"}: { - {Network: "network2", Address: "20.0.0.1"}, - }, - {Cluster: "cluster2b"}: { - {Network: "network2", Address: "20.0.0.2"}, - {Network: "network2", Address: "20.0.0.3"}, - }, - - // network3 has no endpoints. - - // network4 has a single endpoint, but not gateway so it will always - // be considered directly reachable. - {Cluster: "cluster4"}: { - {Network: "network4", Address: "40.0.0.1"}, - }, - }} - // apply common properties - for sk, shard := range shards.Shards { - for i, ep := range shard { - ep.ServicePortName = "http" - ep.Namespace = "ns" - ep.HostName = "example.ns.svc.cluster.local" - ep.EndpointPort = 8080 - ep.TLSMode = "istio" - ep.Labels = map[string]string{"app": "example"} - ep.Locality.ClusterID = sk.Cluster - shards.Shards[sk][i] = ep - } - } - return shards -} - -func getLbEndpointAddrs(ep *endpoint.LocalityLbEndpoints) []string { - addrs := make([]string, 0) - for _, lbEp := range ep.LbEndpoints { - addrs = append(addrs, lbEp.GetEndpoint().Address.GetSocketAddress().Address) - } - return addrs -} diff --git a/pilot/pkg/xds/eventhandler.go b/pilot/pkg/xds/eventhandler.go deleted file mode 100644 index cc353ef82..000000000 --- a/pilot/pkg/xds/eventhandler.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" -) - -// EventType represents the type of object we are tracking, mapping to envoy TypeUrl. -type EventType = string - -var AllEventTypes = map[EventType]struct{}{ - v3.ClusterType: {}, - v3.ListenerType: {}, - v3.RouteType: {}, - v3.EndpointType: {}, -} - -// AllEventTypesList is AllEventTypes in list form, for convenience -var AllEventTypesList = []EventType{v3.ClusterType, v3.ListenerType, v3.RouteType, v3.EndpointType} - -// EventHandler allows for generic monitoring of xDS ACKS and disconnects, for the purpose of tracking -// Config distribution through the mesh. -type DistributionStatusCache interface { - // RegisterEvent notifies the implementer of an xDS ACK, and must be non-blocking - RegisterEvent(conID string, eventType EventType, nonce string) - RegisterDisconnect(s string, types []EventType) - QueryLastNonce(conID string, eventType EventType) (noncePrefix string) -} diff --git a/pilot/pkg/xds/fake.go b/pilot/pkg/xds/fake.go deleted file mode 100644 index f60e11435..000000000 --- a/pilot/pkg/xds/fake.go +++ /dev/null @@ -1,595 +0,0 @@ -//go:build !agent -// +build !agent - -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "context" - "fmt" - "net" - "strings" - "time" -) - -import ( - endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/test/bufconn" - meshconfig "istio.io/api/mesh/v1alpha1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes/fake" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/gateway" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/ingress" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/controller/workloadentry" - kubesecrets "github.com/apache/dubbo-go-pixiu/pilot/pkg/credentials/kube" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/core/v1alpha3" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry" - kube "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube/controller" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/adsc" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/keepalive" - kubelib "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/multicluster" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -type FakeOptions struct { - // If provided, sets the name of the "default" or local cluster to the similaed pilots. (Defaults to opts.DefaultClusterName) - DefaultClusterName cluster.ID - // If provided, the minor version will be overridden for calls to GetKubernetesVersion to 1.minor - KubernetesVersion string - // If provided, a service registry with the name of each map key will be created with the given objects. - KubernetesObjectsByCluster map[cluster.ID][]runtime.Object - // If provided, these objects will be used directly for the default cluster ("Kubernetes" or DefaultClusterName) - KubernetesObjects []runtime.Object - // If provided, a service registry with the name of each map key will be created with the given objects. - KubernetesObjectStringByCluster map[cluster.ID]string - // If provided, the yaml string will be parsed and used as objects for the default cluster ("Kubernetes" or DefaultClusterName) - KubernetesObjectString string - // Endpoint mode for the Kubernetes service registry - KubernetesEndpointMode kube.EndpointMode - // If provided, these configs will be used directly - Configs []config.Config - // If provided, the yaml string will be parsed and used as configs - ConfigString string - // If provided, the ConfigString will be treated as a go template, with this as input params - ConfigTemplateInput interface{} - // If provided, this mesh config will be used - MeshConfig *meshconfig.MeshConfig - NetworksWatcher mesh.NetworksWatcher - - // Callback to modify the server before it is started - DiscoveryServerModifier func(s *DiscoveryServer) - // Callback to modify the kube client before it is started - KubeClientModifier func(c kubelib.Client) - - // ListenerBuilder, if specified, allows making the server use the given - // listener instead of a buffered conn. - ListenerBuilder func() (net.Listener, error) - - // Time to debounce - // By default, set to 0s to speed up tests - DebounceTime time.Duration - - // EnableFakeXDSUpdater will use a XDSUpdater that can be used to watch events - EnableFakeXDSUpdater bool - DisableSecretAuthorization bool - Services []*model.Service - Gateways []model.NetworkGateway -} - -type FakeDiscoveryServer struct { - *v1alpha3.ConfigGenTest - t test.Failer - Discovery *DiscoveryServer - Listener net.Listener - BufListener *bufconn.Listener - kubeClient kubelib.Client - KubeRegistry *kube.FakeController - XdsUpdater model.XDSUpdater -} - -func NewFakeDiscoveryServer(t test.Failer, opts FakeOptions) *FakeDiscoveryServer { - stop := make(chan struct{}) - t.Cleanup(func() { - close(stop) - }) - - m := opts.MeshConfig - if m == nil { - m = mesh.DefaultMeshConfig() - } - - // Init with a dummy environment, since we have a circular dependency with the env creation. - s := NewDiscoveryServer(&model.Environment{PushContext: model.NewPushContext()}, "pilot-123", map[string]string{}) - s.InitGenerators(s.Env, "dubbo-system") - t.Cleanup(func() { - s.JwtKeyResolver.Close() - s.pushQueue.ShutDown() - }) - - serviceHandler := func(svc *model.Service, _ model.Event) { - pushReq := &model.PushRequest{ - Full: true, - ConfigsUpdated: map[model.ConfigKey]struct{}{{ - Kind: gvk.ServiceEntry, - Name: string(svc.Hostname), - Namespace: svc.Attributes.Namespace, - }: {}}, - Reason: []model.TriggerReason{model.ServiceUpdate}, - } - s.ConfigUpdate(pushReq) - } - - if opts.DefaultClusterName == "" { - opts.DefaultClusterName = "Kubernetes" - } - k8sObjects := getKubernetesObjects(t, opts) - var defaultKubeClient kubelib.Client - var defaultKubeController *kube.FakeController - var registries []serviceregistry.Instance - if opts.NetworksWatcher != nil { - opts.NetworksWatcher.AddNetworksHandler(func() { - s.ConfigUpdate(&model.PushRequest{ - Full: true, - Reason: []model.TriggerReason{model.NetworksTrigger}, - }) - }) - } - var xdsUpdater model.XDSUpdater = s - if opts.EnableFakeXDSUpdater { - evChan := make(chan FakeXdsEvent, 1000) - xdsUpdater = &FakeXdsUpdater{ - Events: evChan, - Delegate: s, - } - } - creds := kubesecrets.NewMulticluster(opts.DefaultClusterName) - s.Generators[v3.SecretType] = NewSecretGen(creds, s.Cache, opts.DefaultClusterName, nil) - s.Generators[v3.ExtensionConfigurationType].(*EcdsGenerator).SetCredController(creds) - for k8sCluster, objs := range k8sObjects { - client := kubelib.NewFakeClientWithVersion(opts.KubernetesVersion, objs...) - if opts.KubeClientModifier != nil { - opts.KubeClientModifier(client) - } - k8s, _ := kube.NewFakeControllerWithOptions(kube.FakeControllerOptions{ - ServiceHandler: serviceHandler, - Client: client, - ClusterID: k8sCluster, - DomainSuffix: "cluster.local", - XDSUpdater: xdsUpdater, - NetworksWatcher: opts.NetworksWatcher, - Mode: opts.KubernetesEndpointMode, - Stop: stop, - }) - // start default client informers after creating ingress/secret controllers - if defaultKubeClient == nil || k8sCluster == opts.DefaultClusterName { - defaultKubeClient = client - defaultKubeController = k8s - } else { - client.RunAndWait(stop) - } - registries = append(registries, k8s) - if err := creds.ClusterAdded(&multicluster.Cluster{ID: k8sCluster, Client: client}, nil); err != nil { - t.Fatal(err) - } - } - - if opts.DisableSecretAuthorization { - kubesecrets.DisableAuthorizationForTest(defaultKubeClient.Kube().(*fake.Clientset)) - } - ingr := ingress.NewController(defaultKubeClient, mesh.NewFixedWatcher(m), kube.Options{ - DomainSuffix: "cluster.local", - }) - defaultKubeClient.RunAndWait(stop) - - var gwc *gateway.Controller - cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{ - Configs: opts.Configs, - ConfigString: opts.ConfigString, - ConfigTemplateInput: opts.ConfigTemplateInput, - MeshConfig: opts.MeshConfig, - NetworksWatcher: opts.NetworksWatcher, - ServiceRegistries: registries, - PushContextLock: &s.updateMutex, - ConfigStoreCaches: []model.ConfigStoreController{ingr}, - CreateConfigStore: func(c model.ConfigStoreController) model.ConfigStoreController { - g := gateway.NewController(defaultKubeClient, c, kube.Options{ - DomainSuffix: "cluster.local", - }) - gwc = g - return gwc - }, - SkipRun: true, - ClusterID: defaultKubeController.Cluster(), - Services: opts.Services, - Gateways: opts.Gateways, - }) - cg.ServiceEntryRegistry.AppendServiceHandler(serviceHandler) - s.updateMutex.Lock() - s.Env = cg.Env() - s.Env.GatewayAPIController = gwc - if err := s.Env.InitNetworksManager(s); err != nil { - t.Fatal(err) - } - // Disable debounce to reduce test times - s.debounceOptions.debounceAfter = opts.DebounceTime - s.MemRegistry = cg.MemRegistry - s.MemRegistry.EDSUpdater = s - s.updateMutex.Unlock() - - // Setup config handlers - // TODO code re-use from server.go - configHandler := func(_, curr config.Config, event model.Event) { - pushReq := &model.PushRequest{ - Full: true, - ConfigsUpdated: map[model.ConfigKey]struct{}{{ - Kind: curr.GroupVersionKind, - Name: curr.Name, - Namespace: curr.Namespace, - }: {}}, - Reason: []model.TriggerReason{model.ConfigUpdate}, - } - s.ConfigUpdate(pushReq) - } - schemas := collections.Pilot.All() - if features.EnableGatewayAPI { - schemas = collections.PilotGatewayAPI.All() - } - for _, schema := range schemas { - // This resource type was handled in external/servicediscovery.go, no need to rehandle here. - if schema.Resource().GroupVersionKind() == collections.IstioNetworkingV1Alpha3Serviceentries. - Resource().GroupVersionKind() { - continue - } - if schema.Resource().GroupVersionKind() == collections.IstioNetworkingV1Alpha3Workloadentries. - Resource().GroupVersionKind() { - continue - } - - cg.Store().RegisterEventHandler(schema.Resource().GroupVersionKind(), configHandler) - } - for _, registry := range registries { - k8s, ok := registry.(*kube.FakeController) - // this closely matches what we do in serviceregistry/kube/controller/multicluster.go - if !ok || k8s.Cluster() != cg.ServiceEntryRegistry.Cluster() { - continue - } - cg.ServiceEntryRegistry.AppendWorkloadHandler(k8s.WorkloadInstanceHandler) - k8s.AppendWorkloadHandler(cg.ServiceEntryRegistry.WorkloadInstanceHandler) - } - s.WorkloadEntryController = workloadentry.NewController(cg.Store(), "test", keepalive.Infinity) - - if opts.DiscoveryServerModifier != nil { - opts.DiscoveryServerModifier(s) - } - - var listener net.Listener - if opts.ListenerBuilder != nil { - var err error - if listener, err = opts.ListenerBuilder(); err != nil { - t.Fatal(err) - } - } else { - // Start in memory gRPC listener - buffer := 1024 * 1024 - listener = bufconn.Listen(buffer) - } - - grpcServer := grpc.NewServer() - s.Register(grpcServer) - go func() { - if err := grpcServer.Serve(listener); err != nil && !(err == grpc.ErrServerStopped || err.Error() == "closed") { - t.Fatal(err) - } - }() - t.Cleanup(func() { - grpcServer.Stop() - _ = listener.Close() - }) - // Start the discovery server - s.Start(stop) - cg.ServiceEntryRegistry.XdsUpdater = s - // Now that handlers are added, get everything started - cg.Run() - cache.WaitForCacheSync(stop, - cg.Registry.HasSynced, - cg.Store().HasSynced) - cg.ServiceEntryRegistry.ResyncEDS() - - // Send an update. This ensures that even if there are no configs provided, the push context is - // initialized. - s.ConfigUpdate(&model.PushRequest{Full: true}) - - processStartTime = time.Now() - - // Wait until initial updates are committed - c := s.InboundUpdates.Load() - retry.UntilOrFail(t, func() bool { - return s.CommittedUpdates.Load() >= c - }, retry.Delay(time.Millisecond)) - - // Mark ourselves ready - s.CachesSynced() - - bufListener, _ := listener.(*bufconn.Listener) - fake := &FakeDiscoveryServer{ - t: t, - Discovery: s, - Listener: listener, - BufListener: bufListener, - ConfigGenTest: cg, - kubeClient: defaultKubeClient, - KubeRegistry: defaultKubeController, - XdsUpdater: xdsUpdater, - } - - return fake -} - -func (f *FakeDiscoveryServer) KubeClient() kubelib.Client { - return f.kubeClient -} - -func (f *FakeDiscoveryServer) PushContext() *model.PushContext { - f.Discovery.updateMutex.RLock() - defer f.Discovery.updateMutex.RUnlock() - return f.Env().PushContext -} - -// ConnectADS starts an ADS connection to the server. It will automatically be cleaned up when the test ends -func (f *FakeDiscoveryServer) ConnectADS() *AdsTest { - conn, err := grpc.Dial("buffcon", - grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.WithBlock(), - grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { - return f.BufListener.Dial() - })) - if err != nil { - f.t.Fatalf("failed to connect: %v", err) - } - return NewAdsTest(f.t, conn) -} - -// ConnectDeltaADS starts a Delta ADS connection to the server. It will automatically be cleaned up when the test ends -func (f *FakeDiscoveryServer) ConnectDeltaADS() *DeltaAdsTest { - conn, err := grpc.Dial("buffcon", - grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.WithBlock(), - grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { - return f.BufListener.Dial() - })) - if err != nil { - f.t.Fatalf("failed to connect: %v", err) - } - return NewDeltaAdsTest(f.t, conn) -} - -// Connect starts an ADS connection to the server using adsc. It will automatically be cleaned up when the test ends -// watch can be configured to determine the resources to watch initially, and wait can be configured to determine what -// resources we should initially wait for. -func (f *FakeDiscoveryServer) Connect(p *model.Proxy, watch []string, wait []string) *adsc.ADSC { - f.t.Helper() - p = f.SetupProxy(p) - initialWatch := []*discovery.DiscoveryRequest{} - if watch == nil { - initialWatch = []*discovery.DiscoveryRequest{{TypeUrl: v3.ClusterType}} - } else { - for _, typeURL := range watch { - initialWatch = append(initialWatch, &discovery.DiscoveryRequest{TypeUrl: typeURL}) - } - } - if wait == nil { - initialWatch = []*discovery.DiscoveryRequest{{TypeUrl: v3.ClusterType}} - } - adscConn, err := adsc.New("buffcon", &adsc.Config{ - IP: p.IPAddresses[0], - NodeType: string(p.Type), - Meta: p.Metadata.ToStruct(), - Locality: p.Locality, - Namespace: p.ConfigNamespace, - InitialDiscoveryRequests: initialWatch, - GrpcOpts: []grpc.DialOption{ - grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { - return f.BufListener.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - }, - }) - if err != nil { - f.t.Fatalf("Error connecting: %v", err) - } - if err := adscConn.Run(); err != nil { - f.t.Fatalf("ADSC: failed running: %v", err) - } - - if len(wait) > 0 { - _, err = adscConn.Wait(10*time.Second, wait...) - if err != nil { - f.t.Fatalf("Error getting initial for %v config: %v", wait, err) - } - } - f.t.Cleanup(func() { - adscConn.Close() - }) - return adscConn -} - -func (f *FakeDiscoveryServer) Endpoints(p *model.Proxy) []*endpoint.ClusterLoadAssignment { - loadAssignments := make([]*endpoint.ClusterLoadAssignment, 0) - for _, c := range xdstest.ExtractEdsClusterNames(f.Clusters(p)) { - loadAssignments = append(loadAssignments, f.Discovery.generateEndpoints(NewEndpointBuilder(c, p, f.PushContext()))) - } - return loadAssignments -} - -func getKubernetesObjects(t test.Failer, opts FakeOptions) map[cluster.ID][]runtime.Object { - objects := map[cluster.ID][]runtime.Object{} - - if len(opts.KubernetesObjects) > 0 { - objects[opts.DefaultClusterName] = append(objects[opts.DefaultClusterName], opts.KubernetesObjects...) - } - if len(opts.KubernetesObjectString) > 0 { - parsed, err := kubernetesObjectsFromString(opts.KubernetesObjectString) - if err != nil { - t.Fatalf("failed parsing KubernetesObjectString: %v", err) - } - objects[opts.DefaultClusterName] = append(objects[opts.DefaultClusterName], parsed...) - } - for k8sCluster, objectStr := range opts.KubernetesObjectStringByCluster { - parsed, err := kubernetesObjectsFromString(objectStr) - if err != nil { - t.Fatalf("failed parsing KubernetesObjectStringByCluster for %s: %v", k8sCluster, err) - } - objects[k8sCluster] = append(objects[k8sCluster], parsed...) - } - for k8sCluster, clusterObjs := range opts.KubernetesObjectsByCluster { - objects[k8sCluster] = append(objects[k8sCluster], clusterObjs...) - } - - if len(objects) == 0 { - return map[cluster.ID][]runtime.Object{opts.DefaultClusterName: {}} - } - - return objects -} - -func kubernetesObjectsFromString(s string) ([]runtime.Object, error) { - var objects []runtime.Object - decode := scheme.Codecs.UniversalDeserializer().Decode - objectStrs := strings.Split(s, "---") - for _, s := range objectStrs { - if len(strings.TrimSpace(s)) == 0 { - continue - } - o, _, err := decode([]byte(s), nil, nil) - if err != nil { - return nil, fmt.Errorf("failed deserializing kubernetes object: %v", err) - } - objects = append(objects, o) - } - return objects, nil -} - -type FakeXdsEvent struct { - Kind string - Host string - Namespace string - Endpoints int - PushReq *model.PushRequest -} - -type FakeXdsUpdater struct { - // Events tracks notifications received by the updater - Events chan FakeXdsEvent - Delegate model.XDSUpdater -} - -var _ model.XDSUpdater = &FakeXdsUpdater{} - -func (fx *FakeXdsUpdater) EDSUpdate(s model.ShardKey, hostname string, namespace string, entry []*model.IstioEndpoint) { - fx.Events <- FakeXdsEvent{Kind: "eds", Host: hostname, Namespace: namespace, Endpoints: len(entry)} - if fx.Delegate != nil { - fx.Delegate.EDSUpdate(s, hostname, namespace, entry) - } -} - -func (fx *FakeXdsUpdater) EDSCacheUpdate(s model.ShardKey, hostname string, namespace string, entry []*model.IstioEndpoint) { - fx.Events <- FakeXdsEvent{Kind: "edscache", Host: hostname, Namespace: namespace, Endpoints: len(entry)} - if fx.Delegate != nil { - fx.Delegate.EDSCacheUpdate(s, hostname, namespace, entry) - } -} - -func (fx *FakeXdsUpdater) ConfigUpdate(req *model.PushRequest) { - fx.Events <- FakeXdsEvent{Kind: "xds", PushReq: req} - if fx.Delegate != nil { - fx.Delegate.ConfigUpdate(req) - } -} - -func (fx *FakeXdsUpdater) ProxyUpdate(c cluster.ID, p string) { - if fx.Delegate != nil { - fx.Delegate.ProxyUpdate(c, p) - } -} - -func (fx *FakeXdsUpdater) SvcUpdate(s model.ShardKey, hostname string, namespace string, e model.Event) { - fx.Events <- FakeXdsEvent{Kind: "svcupdate", Host: hostname, Namespace: namespace} - if fx.Delegate != nil { - fx.Delegate.SvcUpdate(s, hostname, namespace, e) - } -} - -func (fx *FakeXdsUpdater) RemoveShard(_ model.ShardKey) { - fx.Events <- FakeXdsEvent{Kind: "removeshard"} - fx.ConfigUpdate(&model.PushRequest{Full: true}) -} - -func (fx *FakeXdsUpdater) WaitDurationOrFail(t test.Failer, duration time.Duration, types ...string) *FakeXdsEvent { - t.Helper() - got := fx.WaitDuration(duration, types...) - if got == nil { - t.Fatal("missing event") - } - return got -} - -func (fx *FakeXdsUpdater) WaitOrFail(t test.Failer, types ...string) *FakeXdsEvent { - t.Helper() - got := fx.Wait(types...) - if got == nil { - t.Fatal("missing event") - } - return got -} - -func (fx *FakeXdsUpdater) WaitDuration(duration time.Duration, types ...string) *FakeXdsEvent { - for { - select { - case e := <-fx.Events: - for _, et := range types { - if e.Kind == et { - return &e - } - } - continue - case <-time.After(duration): - return nil - } - } -} - -func (fx *FakeXdsUpdater) Wait(types ...string) *FakeXdsEvent { - return fx.WaitDuration(1*time.Second, types...) -} diff --git a/pilot/pkg/xds/filters/context.go b/pilot/pkg/xds/filters/context.go deleted file mode 100644 index 8c2b6343d..000000000 --- a/pilot/pkg/xds/filters/context.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package filters - -type RouterFilterContext struct { - StartChildSpan bool -} diff --git a/pilot/pkg/xds/filters/filters.go b/pilot/pkg/xds/filters/filters.go deleted file mode 100644 index 557151d86..000000000 --- a/pilot/pkg/xds/filters/filters.go +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package filters - -import ( - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - cors "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cors/v3" - fault "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/fault/v3" - grpcstats "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_stats/v3" - grpcweb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_web/v3" - router "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3" - httpwasm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/wasm/v3" - httpinspector "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/http_inspector/v3" - originaldst "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/original_dst/v3" - originalsrc "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/original_src/v3" - tlsinspector "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/tls_inspector/v3" - hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - previoushost "github.com/envoyproxy/go-control-plane/envoy/extensions/retry/host/previous_hosts/v3" - rawbuffer "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/raw_buffer/v3" - wasm "github.com/envoyproxy/go-control-plane/envoy/extensions/wasm/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "google.golang.org/protobuf/types/known/wrapperspb" - alpn "istio.io/api/envoy/config/filter/http/alpn/v2alpha1" - "istio.io/api/envoy/config/filter/network/metadata_exchange" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" -) - -const ( - // Alpn HTTP filter name which will override the ALPN for upstream TLS connection. - AlpnFilterName = "istio.alpn" - - TLSTransportProtocol = "tls" - RawBufferTransportProtocol = "raw_buffer" - - MxFilterName = "istio.metadata_exchange" -) - -// Define static filters to be reused across the codebase. This avoids duplicate marshaling/unmarshaling -// This should not be used for filters that will be mutated -var ( - RetryPreviousHosts = &route.RetryPolicy_RetryHostPredicate{ - Name: "envoy.retry_host_predicates.previous_hosts", - ConfigType: &route.RetryPolicy_RetryHostPredicate_TypedConfig{ - TypedConfig: util.MessageToAny(&previoushost.PreviousHostsPredicate{}), - }, - } - RawBufferTransportSocket = &core.TransportSocket{ - Name: util.EnvoyRawBufferSocketName, - ConfigType: &core.TransportSocket_TypedConfig{ - TypedConfig: util.MessageToAny(&rawbuffer.RawBuffer{}), - }, - } - Cors = &hcm.HttpFilter{ - Name: wellknown.CORS, - ConfigType: &hcm.HttpFilter_TypedConfig{ - TypedConfig: util.MessageToAny(&cors.Cors{}), - }, - } - Fault = &hcm.HttpFilter{ - Name: wellknown.Fault, - ConfigType: &hcm.HttpFilter_TypedConfig{ - TypedConfig: util.MessageToAny(&fault.HTTPFault{}), - }, - } - Router = &hcm.HttpFilter{ - Name: wellknown.Router, - ConfigType: &hcm.HttpFilter_TypedConfig{ - TypedConfig: util.MessageToAny(&router.Router{}), - }, - } - GrpcWeb = &hcm.HttpFilter{ - Name: wellknown.GRPCWeb, - ConfigType: &hcm.HttpFilter_TypedConfig{ - TypedConfig: util.MessageToAny(&grpcweb.GrpcWeb{}), - }, - } - GrpcStats = &hcm.HttpFilter{ - Name: wellknown.HTTPGRPCStats, - ConfigType: &hcm.HttpFilter_TypedConfig{ - TypedConfig: util.MessageToAny(&grpcstats.FilterConfig{ - EmitFilterState: true, - PerMethodStatSpecifier: &grpcstats.FilterConfig_StatsForAllMethods{ - StatsForAllMethods: &wrapperspb.BoolValue{Value: false}, - }, - }), - }, - } - TLSInspector = &listener.ListenerFilter{ - Name: wellknown.TlsInspector, - ConfigType: &listener.ListenerFilter_TypedConfig{ - TypedConfig: util.MessageToAny(&tlsinspector.TlsInspector{}), - }, - } - HTTPInspector = &listener.ListenerFilter{ - Name: wellknown.HttpInspector, - ConfigType: &listener.ListenerFilter_TypedConfig{ - TypedConfig: util.MessageToAny(&httpinspector.HttpInspector{}), - }, - } - OriginalDestination = &listener.ListenerFilter{ - Name: wellknown.OriginalDestination, - ConfigType: &listener.ListenerFilter_TypedConfig{ - TypedConfig: util.MessageToAny(&originaldst.OriginalDst{}), - }, - } - OriginalSrc = &listener.ListenerFilter{ - Name: wellknown.OriginalSource, - ConfigType: &listener.ListenerFilter_TypedConfig{ - TypedConfig: util.MessageToAny(&originalsrc.OriginalSrc{ - Mark: 1337, - }), - }, - } - Alpn = &hcm.HttpFilter{ - Name: AlpnFilterName, - ConfigType: &hcm.HttpFilter_TypedConfig{ - TypedConfig: util.MessageToAny(&alpn.FilterConfig{ - AlpnOverride: []*alpn.FilterConfig_AlpnOverride{ - { - UpstreamProtocol: alpn.FilterConfig_HTTP10, - AlpnOverride: mtlsHTTP10ALPN, - }, - { - UpstreamProtocol: alpn.FilterConfig_HTTP11, - AlpnOverride: mtlsHTTP11ALPN, - }, - { - UpstreamProtocol: alpn.FilterConfig_HTTP2, - AlpnOverride: mtlsHTTP2ALPN, - }, - }, - }), - }, - } - - tcpMx = util.MessageToAny(&metadata_exchange.MetadataExchange{Protocol: "istio-peer-exchange"}) - - TCPListenerMx = &listener.Filter{ - Name: MxFilterName, - ConfigType: &listener.Filter_TypedConfig{TypedConfig: tcpMx}, - } - - TCPClusterMx = &cluster.Filter{ - Name: MxFilterName, - TypedConfig: tcpMx, - } - - HTTPMx = buildHTTPMxFilter() -) - -func BuildRouterFilter(ctx *RouterFilterContext) *hcm.HttpFilter { - if ctx == nil { - return Router - } - - return &hcm.HttpFilter{ - Name: wellknown.Router, - ConfigType: &hcm.HttpFilter_TypedConfig{ - TypedConfig: util.MessageToAny(&router.Router{ - StartChildSpan: ctx.StartChildSpan, - }), - }, - } -} - -var ( - // These ALPNs are injected in the client side by the ALPN filter. - // "istio" is added for each upstream protocol in order to make it - // backward compatible. e.g., 1.4 proxy -> 1.3 proxy. - // Non istio-* variants are added to ensure that traffic sent out of the mesh has a valid ALPN; - // ideally this would not be added, but because the override filter is in the HCM, rather than cluster, - // we do not yet know the upstream so we cannot determine if its in or out of the mesh - mtlsHTTP10ALPN = []string{"istio-http/1.0", "istio", "http/1.0"} - mtlsHTTP11ALPN = []string{"istio-http/1.1", "istio", "http/1.1"} - mtlsHTTP2ALPN = []string{"istio-h2", "istio", "h2"} -) - -func buildHTTPMxFilter() *hcm.HttpFilter { - httpMxConfigProto := &httpwasm.Wasm{ - Config: &wasm.PluginConfig{ - Vm: model.ConstructVMConfig("/etc/istio/extensions/metadata-exchange-filter.compiled.wasm", "envoy.wasm.metadata_exchange"), - Configuration: util.MessageToAny(&metadata_exchange.MetadataExchange{}), - }, - } - return &hcm.HttpFilter{ - Name: MxFilterName, - ConfigType: &hcm.HttpFilter_TypedConfig{TypedConfig: util.MessageToAny(httpMxConfigProto)}, - } -} diff --git a/pilot/pkg/xds/lds.go b/pilot/pkg/xds/lds.go deleted file mode 100644 index dd6c8ab79..000000000 --- a/pilot/pkg/xds/lds.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -type LdsGenerator struct { - Server *DiscoveryServer -} - -var _ model.XdsResourceGenerator = &LdsGenerator{} - -// Map of all configs that do not impact LDS -var skippedLdsConfigs = map[model.NodeType]map[config.GroupVersionKind]struct{}{ - model.Router: { - // for autopassthrough gateways, we build filterchains per-dr subset - gvk.WorkloadGroup: {}, - gvk.WorkloadEntry: {}, - gvk.Secret: {}, - gvk.ProxyConfig: {}, - }, - model.SidecarProxy: { - gvk.Gateway: {}, - gvk.WorkloadGroup: {}, - gvk.WorkloadEntry: {}, - gvk.Secret: {}, - gvk.ProxyConfig: {}, - }, -} - -func ldsNeedsPush(proxy *model.Proxy, req *model.PushRequest) bool { - if req == nil { - return true - } - if !req.Full { - // LDS only handles full push - return false - } - // If none set, we will always push - if len(req.ConfigsUpdated) == 0 { - return true - } - for config := range req.ConfigsUpdated { - if _, f := skippedLdsConfigs[proxy.Type][config.Kind]; !f { - return true - } - } - return false -} - -func (l LdsGenerator) Generate(proxy *model.Proxy, w *model.WatchedResource, req *model.PushRequest) (model.Resources, model.XdsLogDetails, error) { - if !ldsNeedsPush(proxy, req) { - return nil, model.DefaultXdsLogDetails, nil - } - listeners := l.Server.ConfigGenerator.BuildListeners(proxy, req.Push) - resources := model.Resources{} - for _, c := range listeners { - resources = append(resources, &discovery.Resource{ - Name: c.Name, - Resource: util.MessageToAny(c), - }) - } - return resources, model.DefaultXdsLogDetails, nil -} diff --git a/pilot/pkg/xds/lds_test.go b/pilot/pkg/xds/lds_test.go deleted file mode 100644 index a71f01b5f..000000000 --- a/pilot/pkg/xds/lds_test.go +++ /dev/null @@ -1,359 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package xds_test - -import ( - "os" - "testing" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - meshconfig "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/tests/util" -) - -// TestLDS using isolated namespaces -func TestLDSIsolated(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ConfigString: mustReadfolder(t, "tests/testdata/config")}) - - // Sidecar in 'none' mode - t.Run("sidecar_none", func(t *testing.T) { - adscon := s.Connect(&model.Proxy{ - Metadata: &model.NodeMetadata{ - InterceptionMode: model.InterceptionNone, - HTTP10: "1", - }, - IPAddresses: []string{"10.11.0.1"}, // matches none.yaml s1tcp.none - ConfigNamespace: "none", - }, nil, watchAll) - - err := adscon.Save(env.IstioOut + "/none") - if err != nil { - t.Fatal(err) - } - - // 7071 (inbound), 2001 (service - also as http proxy), 18010 (fortio) - if len(adscon.GetHTTPListeners()) != 3 { - t.Error("HTTP listeners, expecting 3 got", len(adscon.GetHTTPListeners()), xdstest.MapKeys(adscon.GetHTTPListeners())) - } - - // s1tcp:2000 outbound, bind=true (to reach other instances of the service) - // s1:5005 outbound, bind=true - // :443 - https external, bind=false - // 10.11.0.1_7070, bind=true -> inbound|2000|s1 - on port 7070, fwd to 37070 - // virtual - if len(adscon.GetTCPListeners()) == 0 { - t.Fatal("No response") - } - - for _, s := range []string{"lds_tcp", "lds_http", "rds", "cds", "ecds"} { - want, err := os.ReadFile(env.IstioOut + "/none_" + s + ".json") - if err != nil { - t.Fatal(err) - } - got, err := os.ReadFile("testdata/none_" + s + ".json") - if err != nil { - t.Fatal(err) - } - - if err = util.Compare(got, want); err != nil { - // Just log for now - golden changes every time there is a config generation update. - // It is mostly intended as a reference for what is generated - we need to add explicit checks - // for things we need, like the number of expected listeners. - // This is mainly using for debugging what changed from the snapshot in the golden files. - if os.Getenv("CONFIG_DIFF") == "1" { - t.Logf("error in golden file %s %v", s, err) - } - } - } - }) - - // Test for the examples in the ServiceEntry doc - t.Run("se_example", func(t *testing.T) { - // TODO: add a Service with EDS resolution in the none ns. - // The ServiceEntry only allows STATIC - both STATIC and EDS should generated TCP listeners on :port - // while DNS and NONE should generate old-style bind ports. - // Right now 'STATIC' and 'EDS' result in ClientSideLB in the internal object, so listener test is valid. - - s.Connect(&model.Proxy{ - IPAddresses: []string{"10.12.0.1"}, // matches none.yaml s1tcp.none - ConfigNamespace: "seexamples", - }, nil, watchAll) - }) - - // Test for the examples in the ServiceEntry doc - t.Run("se_examplegw", func(t *testing.T) { - // TODO: add a Service with EDS resolution in the none ns. - // The ServiceEntry only allows STATIC - both STATIC and EDS should generated TCP listeners on :port - // while DNS and NONE should generate old-style bind ports. - // Right now 'STATIC' and 'EDS' result in ClientSideLB in the internal object, so listener test is valid. - - s.Connect(&model.Proxy{ - IPAddresses: []string{"10.13.0.1"}, // matches none.yaml s1tcp.none - ConfigNamespace: "exampleegressgw", - }, nil, watchAll) - }) -} - -// TestLDS using default sidecar in root namespace -func TestLDSWithDefaultSidecar(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ - ConfigString: mustReadfolder(t, "tests/testdata/networking/sidecar-ns-scope"), - MeshConfig: func() *meshconfig.MeshConfig { - m := mesh.DefaultMeshConfig() - m.RootNamespace = "istio-config" - return m - }(), - }) - adsc := s.Connect(&model.Proxy{ConfigNamespace: "ns1", IPAddresses: []string{"100.1.1.2"}}, nil, watchAll) - - // Expect 2 listeners : 2 orig_dst, 2 outbound (http, tcp1) - if (len(adsc.GetHTTPListeners()) + len(adsc.GetTCPListeners())) != 4 { - t.Fatalf("Expected 4 listeners, got %d\n", len(adsc.GetHTTPListeners())+len(adsc.GetTCPListeners())) - } - - // Expect 9 CDS clusters: - // 2 inbound(http, inbound passthroughipv4) notes: no passthroughipv6 - // 9 outbound (2 http services, 1 tcp service, - // and 2 subsets of http1, 1 blackhole, 1 passthrough) - if (len(adsc.GetClusters()) + len(adsc.GetEdsClusters())) != 9 { - t.Fatalf("Expected 9 clusters in CDS output. Got %d", len(adsc.GetClusters())+len(adsc.GetEdsClusters())) - } - - // Expect two vhost blocks in RDS output for 8080 (one for http1, another for http2) - // plus one extra due to mem registry - if len(adsc.GetRoutes()["8080"].VirtualHosts) != 3 { - t.Fatalf("Expected 3 VirtualHosts in RDS output. Got %d", len(adsc.GetRoutes()["8080"].VirtualHosts)) - } -} - -// TestLDS using gateways -func TestLDSWithIngressGateway(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ - ConfigString: mustReadfolder(t, "tests/testdata/networking/ingress-gateway"), - MeshConfig: func() *meshconfig.MeshConfig { - m := mesh.DefaultMeshConfig() - m.RootNamespace = "istio-config" - return m - }(), - }) - labels := labels.Instance{"istio": "ingressgateway"} - adsc := s.Connect(&model.Proxy{ - ConfigNamespace: "dubbo-system", - Metadata: &model.NodeMetadata{Labels: labels}, - IPAddresses: []string{"99.1.1.1"}, - Type: model.Router, - }, nil, []string{v3.ClusterType, v3.EndpointType, v3.ListenerType}) - - // Expect 2 listeners : 1 for 80, 1 for 443 - // where 443 listener has 3 filter chains - if (len(adsc.GetHTTPListeners()) + len(adsc.GetTCPListeners())) != 2 { - t.Fatalf("Expected 2 listeners, got %d\n", len(adsc.GetHTTPListeners())+len(adsc.GetTCPListeners())) - } - - // TODO: This is flimsy. The ADSC code treats any listener with http connection manager as a HTTP listener - // instead of looking at it as a listener with multiple filter chains - l := adsc.GetHTTPListeners()["0.0.0.0_443"] - - if l != nil { - if len(l.FilterChains) != 3 { - t.Fatalf("Expected 3 filter chains, got %d\n", len(l.FilterChains)) - } - } -} - -// TestLDS is running LDS tests. -func TestLDS(t *testing.T) { - t.Run("sidecar", func(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - ads := s.ConnectADS().WithType(v3.ListenerType) - ads.RequestResponseAck(t, nil) - }) - - // 'router' or 'gateway' type of listener - t.Run("gateway", func(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ConfigString: mustReadfolder(t, "tests/testdata/config")}) - // Matches Gateway config in test data - labels := map[string]string{"version": "v2", "app": "my-gateway-controller"} - ads := s.ConnectADS().WithType(v3.ListenerType).WithID(gatewayID(gatewayIP)) - ads.RequestResponseAck(t, &discovery.DiscoveryRequest{ - Node: &core.Node{ - Id: ads.ID, - Metadata: model.NodeMetadata{Labels: labels}.ToStruct(), - }, - }) - }) -} - -// TestLDS using sidecar scoped on workload without Service -func TestLDSWithSidecarForWorkloadWithoutService(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ - ConfigString: mustReadfolder(t, "tests/testdata/networking/sidecar-without-service"), - MeshConfig: func() *meshconfig.MeshConfig { - m := mesh.DefaultMeshConfig() - m.RootNamespace = "istio-config" - return m - }(), - }) - labels := labels.Instance{"app": "consumeronly"} - s.MemRegistry.AddWorkload("98.1.1.1", labels) // These labels must match the sidecars workload selector - adsc := s.Connect(&model.Proxy{ - ConfigNamespace: "consumerns", - Metadata: &model.NodeMetadata{Labels: labels}, - IPAddresses: []string{"98.1.1.1"}, - }, nil, watchAll) - - // Expect 2 HTTP listeners for outbound 8081 and one virtualInbound which has the same inbound 9080 - // as a filter chain. Since the adsclient code treats any listener with a HTTP connection manager filter in ANY - // filter chain, as a HTTP listener, we end up getting both 9080 and virtualInbound. - if len(adsc.GetHTTPListeners()) != 2 { - t.Fatalf("Expected 2 http listeners, got %d", len(adsc.GetHTTPListeners())) - } - - // TODO: This is flimsy. The ADSC code treats any listener with http connection manager as a HTTP listener - // instead of looking at it as a listener with multiple filter chains - if l := adsc.GetHTTPListeners()["0.0.0.0_8081"]; l != nil { - expected := 1 - if len(l.FilterChains) != expected { - t.Fatalf("Expected %d filter chains, got %d", expected, len(l.FilterChains)) - } - } else { - t.Fatal("Expected listener for 0.0.0.0_8081") - } - - if l := adsc.GetHTTPListeners()["virtualInbound"]; l == nil { - t.Fatal("Expected listener virtualInbound") - } - - // Expect only one eds cluster for http1.ns1.svc.cluster.local - if len(adsc.GetEdsClusters()) != 1 { - t.Fatalf("Expected 1 eds cluster, got %d", len(adsc.GetEdsClusters())) - } - if cluster, ok := adsc.GetEdsClusters()["outbound|8081||http1.ns1.svc.cluster.local"]; !ok { - t.Fatalf("Expected eds cluster outbound|8081||http1.ns1.svc.cluster.local, got %v", cluster.Name) - } -} - -// TestLDS using default sidecar in root namespace -func TestLDSEnvoyFilterWithWorkloadSelector(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ - ConfigString: mustReadfolder(t, "tests/testdata/networking/envoyfilter-without-service"), - }) - // The labels of 98.1.1.1 must match the envoyfilter workload selector - s.MemRegistry.AddWorkload("98.1.1.1", labels.Instance{"app": "envoyfilter-test-app", "some": "otherlabel"}) - s.MemRegistry.AddWorkload("98.1.1.2", labels.Instance{"app": "no-envoyfilter-test-app"}) - s.MemRegistry.AddWorkload("98.1.1.3", labels.Instance{}) - - tests := []struct { - name string - ip string - labels labels.Instance - expectLuaFilter bool - }{ - { - name: "Add filter with matching labels to sidecar", - ip: "98.1.1.1", - labels: labels.Instance{"app": "envoyfilter-test-app", "some": "otherlabel"}, - expectLuaFilter: true, - }, - { - name: "Ignore filter with not matching labels to sidecar", - ip: "98.1.1.2", - labels: labels.Instance{"app": "no-envoyfilter-test-app"}, - expectLuaFilter: false, - }, - { - name: "Ignore filter with empty labels to sidecar", - ip: "98.1.1.3", - labels: labels.Instance{}, - expectLuaFilter: false, - }, - } - - for _, test := range tests { - test := test - t.Run(test.name, func(t *testing.T) { - adsc := s.Connect(&model.Proxy{ - ConfigNamespace: "consumerns", - Metadata: &model.NodeMetadata{Labels: test.labels}, - IPAddresses: []string{test.ip}, - }, nil, watchAll) - - // Expect 1 HTTP listeners for 8081 - if len(adsc.GetHTTPListeners()) != 1 { - t.Fatalf("Expected 2 http listeners, got %v", xdstest.MapKeys(adsc.GetHTTPListeners())) - } - // TODO: This is flimsy. The ADSC code treats any listener with http connection manager as a HTTP listener - // instead of looking at it as a listener with multiple filter chains - l := adsc.GetHTTPListeners()["0.0.0.0_8081"] - - expectLuaFilter(t, l, test.expectLuaFilter) - }) - } -} - -func expectLuaFilter(t *testing.T, l *listener.Listener, expected bool) { - t.Helper() - if l != nil { - var chain *listener.FilterChain - for _, fc := range l.FilterChains { - if len(fc.Filters) == 1 && fc.Filters[0].Name == wellknown.HTTPConnectionManager { - chain = fc - } - } - if chain == nil { - t.Fatalf("Failed to find http_connection_manager") - } - if len(chain.Filters) != 1 { - t.Fatalf("Expected 1 filter in first filter chain, got %d", len(l.FilterChains)) - } - filter := chain.Filters[0] - if filter.Name != wellknown.HTTPConnectionManager { - t.Fatalf("Expected HTTP connection, found %v", chain.Filters[0].Name) - } - httpCfg, ok := filter.ConfigType.(*listener.Filter_TypedConfig) - if !ok { - t.Fatalf("Expected Http Connection Manager Config Filter_TypedConfig, found %T", filter.ConfigType) - } - connectionManagerCfg := hcm.HttpConnectionManager{} - err := httpCfg.TypedConfig.UnmarshalTo(&connectionManagerCfg) - if err != nil { - t.Fatalf("Could not deserialize http connection manager config: %v", err) - } - found := false - for _, filter := range connectionManagerCfg.HttpFilters { - if filter.Name == "envoy.lua" { - found = true - } - } - if expected != found { - t.Fatalf("Expected Lua filter: %v, found: %v", expected, found) - } - } -} diff --git a/pilot/pkg/xds/leak_test.go b/pilot/pkg/xds/leak_test.go deleted file mode 100644 index 0441ef052..000000000 --- a/pilot/pkg/xds/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pilot/pkg/xds/mesh_network_test.go b/pilot/pkg/xds/mesh_network_test.go deleted file mode 100644 index 66041014d..000000000 --- a/pilot/pkg/xds/mesh_network_test.go +++ /dev/null @@ -1,571 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "context" - "fmt" - "strings" - "testing" - "time" -) - -import ( - "istio.io/api/label" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" - "istio.io/api/security/v1beta1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/rand" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/kube" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/network" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -func TestNetworkGatewayUpdates(t *testing.T) { - pod := &workload{ - kind: Pod, - name: "app", namespace: "pod", - ip: "10.10.10.10", port: 8080, - metaNetwork: "network-1", - labels: map[string]string{label.TopologyNetwork.Name: "network-1"}, - } - vm := &workload{ - kind: VirtualMachine, - name: "vm", namespace: "default", - ip: "10.10.10.30", port: 9090, - metaNetwork: "vm", - } - workloads := []*workload{pod, vm} - - kubeObjects := []runtime.Object{} - var configObjects []config.Config - for _, w := range workloads { - _, objs := w.kubeObjects() - kubeObjects = append(kubeObjects, objs...) - configObjects = append(configObjects, w.configs()...) - } - meshNetworks := mesh.NewFixedNetworksWatcher(nil) - s := NewFakeDiscoveryServer(t, FakeOptions{ - KubernetesObjects: kubeObjects, - Configs: configObjects, - NetworksWatcher: meshNetworks, - }) - for _, w := range workloads { - w.setupProxy(s) - } - - t.Run("no gateway", func(t *testing.T) { - vm.Expect(pod, "10.10.10.10:8080") - vm.Test(t, s) - }) - t.Run("gateway added via label", func(t *testing.T) { - _, err := s.KubeClient().CoreV1().Services("dubbo-system").Create(context.TODO(), &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "istio-ingressgateway", - Namespace: "dubbo-system", - Labels: map[string]string{ - label.TopologyNetwork.Name: "network-1", - }, - }, - Spec: corev1.ServiceSpec{ - Type: corev1.ServiceTypeLoadBalancer, - Ports: []corev1.ServicePort{{Port: 15443, Protocol: corev1.ProtocolTCP}}, - }, - Status: corev1.ServiceStatus{ - LoadBalancer: corev1.LoadBalancerStatus{Ingress: []corev1.LoadBalancerIngress{{IP: "3.3.3.3"}}}, - }, - }, metav1.CreateOptions{}) - if err != nil { - t.Fatal(err) - } - if err := retry.Until(func() bool { - return len(s.PushContext().NetworkManager().GatewaysForNetwork("network-1")) == 1 - }); err != nil { - t.Fatal("push context did not reinitialize with gateways; xds event may not have been triggered") - } - vm.Expect(pod, "3.3.3.3:15443") - vm.Test(t, s) - }) - - t.Run("gateway added via meshconfig", func(t *testing.T) { - _, err := s.KubeClient().CoreV1().Services("dubbo-system").Create(context.TODO(), &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "istio-meshnetworks-gateway", - Namespace: "dubbo-system", - }, - Spec: corev1.ServiceSpec{Type: corev1.ServiceTypeLoadBalancer}, - Status: corev1.ServiceStatus{ - LoadBalancer: corev1.LoadBalancerStatus{Ingress: []corev1.LoadBalancerIngress{{IP: "4.4.4.4"}}}, - }, - }, metav1.CreateOptions{}) - meshNetworks.SetNetworks(&meshconfig.MeshNetworks{Networks: map[string]*meshconfig.Network{ - "network-1": { - Endpoints: []*meshconfig.Network_NetworkEndpoints{ - { - Ne: &meshconfig.Network_NetworkEndpoints_FromRegistry{FromRegistry: "Kubernetes"}, - }, - }, - Gateways: []*meshconfig.Network_IstioNetworkGateway{{ - Gw: &meshconfig.Network_IstioNetworkGateway_RegistryServiceName{ - RegistryServiceName: "istio-meshnetworks-gateway.dubbo-system.svc.cluster.local", - }, - Port: 15443, - }}, - }, - }}) - if err != nil { - t.Fatal(err) - } - if err := retry.Until(func() bool { - return len(s.PushContext().NetworkManager().GatewaysForNetwork("network-1")) == 2 - }); err != nil { - t.Fatal("push context did not reinitialize with gateways; xds event may not have been triggered") - } - vm.Expect(pod, "3.3.3.3:15443", "4.4.4.4:15443") - vm.Test(t, s) - }) -} - -func TestMeshNetworking(t *testing.T) { - ingressServiceScenarios := map[corev1.ServiceType]map[cluster.ID][]runtime.Object{ - corev1.ServiceTypeLoadBalancer: { - // cluster/network 1's ingress can be found up by registry service name in meshNetworks - "cluster-1": {&corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "istio-ingressgateway", - Namespace: "dubbo-system", - }, - Spec: corev1.ServiceSpec{Type: corev1.ServiceTypeLoadBalancer}, - Status: corev1.ServiceStatus{ - LoadBalancer: corev1.LoadBalancerStatus{Ingress: []corev1.LoadBalancerIngress{{IP: "2.2.2.2"}}}, - }, - }}, - // cluster/network 2's ingress can be found by it's network label - "cluster-2": {&corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "istio-ingressgateway", - Namespace: "dubbo-system", - Labels: map[string]string{ - label.TopologyNetwork.Name: "network-2", - }, - }, - Spec: corev1.ServiceSpec{Type: corev1.ServiceTypeLoadBalancer}, - Status: corev1.ServiceStatus{ - LoadBalancer: corev1.LoadBalancerStatus{Ingress: []corev1.LoadBalancerIngress{{IP: "3.3.3.3"}}}, - }, - }}, - }, - corev1.ServiceTypeClusterIP: { - // cluster/network 1's ingress can be found up by registry service name in meshNetworks - "cluster-1": {&corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "istio-ingressgateway", - Namespace: "dubbo-system", - }, - Spec: corev1.ServiceSpec{ - Type: corev1.ServiceTypeClusterIP, - ExternalIPs: []string{"2.2.2.2"}, - }, - }}, - // cluster/network 2's ingress can be found by it's network label - "cluster-2": {&corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "istio-ingressgateway", - Namespace: "dubbo-system", - Labels: map[string]string{ - label.TopologyNetwork.Name: "network-2", - }, - }, - Spec: corev1.ServiceSpec{ - Type: corev1.ServiceTypeClusterIP, - ExternalIPs: []string{"3.3.3.3"}, - }, - }}, - }, - corev1.ServiceTypeNodePort: { - "cluster-1": { - &corev1.Node{Status: corev1.NodeStatus{Addresses: []corev1.NodeAddress{{Type: corev1.NodeExternalIP, Address: "2.2.2.2"}}}}, - &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "istio-ingressgateway", - Namespace: "dubbo-system", - Annotations: map[string]string{kube.NodeSelectorAnnotation: "{}"}, - }, - Spec: corev1.ServiceSpec{Type: corev1.ServiceTypeNodePort, Ports: []corev1.ServicePort{{Port: 15443, NodePort: 25443}}}, - }, - }, - "cluster-2": { - &corev1.Node{Status: corev1.NodeStatus{Addresses: []corev1.NodeAddress{{Type: corev1.NodeExternalIP, Address: "3.3.3.3"}}}}, - &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "istio-ingressgateway", - Namespace: "dubbo-system", - Annotations: map[string]string{kube.NodeSelectorAnnotation: "{}"}, - Labels: map[string]string{ - label.TopologyNetwork.Name: "network-2", - // set the label here to test it = expectation doesn't change since we map back to that via NodePort - label.NetworkingGatewayPort.Name: "443", - }, - }, - Spec: corev1.ServiceSpec{Type: corev1.ServiceTypeNodePort, Ports: []corev1.ServicePort{{Port: 443, NodePort: 25443}}}, - }, - }, - }, - } - - // network-2 does not need to be specified, gateways and endpoints are found by labels - meshNetworkConfigs := map[string]*meshconfig.MeshNetworks{ - "gateway address": {Networks: map[string]*meshconfig.Network{ - "network-1": { - Endpoints: []*meshconfig.Network_NetworkEndpoints{{ - Ne: &meshconfig.Network_NetworkEndpoints_FromRegistry{FromRegistry: "cluster-1"}, - }}, - Gateways: []*meshconfig.Network_IstioNetworkGateway{{ - Gw: &meshconfig.Network_IstioNetworkGateway_Address{Address: "2.2.2.2"}, Port: 15443, - }}, - }, - }}, - "gateway fromRegistry": {Networks: map[string]*meshconfig.Network{ - "network-1": { - Endpoints: []*meshconfig.Network_NetworkEndpoints{{ - Ne: &meshconfig.Network_NetworkEndpoints_FromRegistry{FromRegistry: "cluster-1"}, - }}, - Gateways: []*meshconfig.Network_IstioNetworkGateway{{ - Gw: &meshconfig.Network_IstioNetworkGateway_RegistryServiceName{ - RegistryServiceName: "istio-ingressgateway.dubbo-system.svc.cluster.local", - }, - Port: 15443, - }}, - }, - }}, - } - - type trafficConfig struct { - config.Config - allowCrossNetwork bool - } - var trafficConfigs []trafficConfig - for name, mode := range map[string]v1beta1.PeerAuthentication_MutualTLS_Mode{ - "strict": v1beta1.PeerAuthentication_MutualTLS_STRICT, - "permissive": v1beta1.PeerAuthentication_MutualTLS_PERMISSIVE, - "disable": v1beta1.PeerAuthentication_MutualTLS_DISABLE, - } { - trafficConfigs = append(trafficConfigs, trafficConfig{ - Config: config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.PeerAuthentication, - Namespace: "dubbo-system", - Name: "peer-authn-mtls-" + name, - }, - Spec: &v1beta1.PeerAuthentication{ - Mtls: &v1beta1.PeerAuthentication_MutualTLS{Mode: mode}, - }, - }, - allowCrossNetwork: mode != v1beta1.PeerAuthentication_MutualTLS_DISABLE, - }) - } - - for ingrType, ingressObjects := range ingressServiceScenarios { - ingrType, ingressObjects := ingrType, ingressObjects - t.Run(string(ingrType), func(t *testing.T) { - for name, networkConfig := range meshNetworkConfigs { - name, networkConfig := name, networkConfig - t.Run(name, func(t *testing.T) { - for _, cfg := range trafficConfigs { - cfg := cfg - t.Run(cfg.Meta.Name, func(t *testing.T) { - pod := &workload{ - kind: Pod, - name: "unlabeled", namespace: "pod", - ip: "10.10.10.10", port: 8080, - metaNetwork: "network-1", clusterID: "cluster-1", - } - labeledPod := &workload{ - kind: Pod, - name: "labeled", namespace: "pod", - ip: "10.10.10.20", port: 9090, - metaNetwork: "network-2", clusterID: "cluster-2", - labels: map[string]string{label.TopologyNetwork.Name: "network-2"}, - } - vm := &workload{ - kind: VirtualMachine, - name: "vm", namespace: "default", - ip: "10.10.10.30", port: 9090, - metaNetwork: "vm", - } - // gw does not have endpoints, it's just some proxy used to test REQUESTED_NETWORK_VIEW - gw := &workload{ - kind: Other, - name: "gw", ip: "2.2.2.2", - networkView: []string{"vm"}, - } - - net1gw, net1GwPort := "2.2.2.2", "15443" - net2gw, net2GwPort := "3.3.3.3", "15443" - if ingrType == corev1.ServiceTypeNodePort { - if name == "gateway fromRegistry" { - net1GwPort = "25443" - } - // network 2 gateway uses the labels approach - always does nodeport mapping - net2GwPort = "25443" - } - net1gw += ":" + net1GwPort - net2gw += ":" + net2GwPort - - // local ip for self - pod.Expect(pod, "10.10.10.10:8080") - labeledPod.Expect(labeledPod, "10.10.10.20:9090") - - // vm has no gateway, use the original IP - pod.Expect(vm, "10.10.10.30:9090") - labeledPod.Expect(vm, "10.10.10.30:9090") - - vm.Expect(vm, "10.10.10.30:9090") - - if cfg.allowCrossNetwork { - // pod in network-1 uses gateway to reach pod labeled with network-2 - pod.Expect(labeledPod, net2gw) - // pod labeled as network-2 should use gateway for network-1 - labeledPod.Expect(pod, net1gw) - // vm uses gateway to get to pods - vm.Expect(pod, net1gw) - vm.Expect(labeledPod, net2gw) - } - - runMeshNetworkingTest(t, meshNetworkingTest{ - workloads: []*workload{pod, labeledPod, vm, gw}, - meshNetworkConfig: networkConfig, - kubeObjects: ingressObjects, - }, cfg.Config) - }) - } - }) - } - }) - } -} - -type meshNetworkingTest struct { - workloads []*workload - meshNetworkConfig *meshconfig.MeshNetworks - kubeObjects map[cluster.ID][]runtime.Object -} - -func runMeshNetworkingTest(t *testing.T, tt meshNetworkingTest, configs ...config.Config) { - kubeObjects := map[cluster.ID][]runtime.Object{} - for k, v := range tt.kubeObjects { - kubeObjects[k] = v - } - configObjects := configs - for _, w := range tt.workloads { - k8sCluster, objs := w.kubeObjects() - if k8sCluster != "" { - kubeObjects[k8sCluster] = append(kubeObjects[k8sCluster], objs...) - } - configObjects = append(configObjects, w.configs()...) - } - s := NewFakeDiscoveryServer(t, FakeOptions{ - KubernetesObjectsByCluster: kubeObjects, - Configs: configObjects, - NetworksWatcher: mesh.NewFixedNetworksWatcher(tt.meshNetworkConfig), - }) - for _, w := range tt.workloads { - w.setupProxy(s) - } - for _, w := range tt.workloads { - w.Test(t, s) - } -} - -type workloadKind int - -const ( - Other workloadKind = iota - Pod - VirtualMachine -) - -type workload struct { - kind workloadKind - - name string - namespace string - - ip string - port int32 - - clusterID cluster.ID - metaNetwork network.ID - networkView []string - - labels map[string]string - - proxy *model.Proxy - - expectations map[string][]string -} - -func (w *workload) Expect(target *workload, ips ...string) { - if w.expectations == nil { - w.expectations = map[string][]string{} - } - w.expectations[target.clusterName()] = ips -} - -func (w *workload) Test(t *testing.T, s *FakeDiscoveryServer) { - if w.expectations == nil { - return - } - - t.Run(fmt.Sprintf("from %s", w.proxy.ID), func(t *testing.T) { - // wait for eds cache update - retry.UntilSuccessOrFail(t, func() error { - eps := xdstest.ExtractLoadAssignments(s.Endpoints(w.proxy)) - for c, ips := range w.expectations { - if !listEqualUnordered(eps[c], ips) { - err := fmt.Errorf("cluster %s, expected ips %v ,but got %v", c, ips, eps[c]) - fmt.Println(err) - return err - } - } - return nil - }) - }) -} - -func (w *workload) clusterName() string { - name := w.name - if w.kind == Pod { - name = fmt.Sprintf("%s.%s.svc.cluster.local", w.name, w.namespace) - } - return fmt.Sprintf("outbound|%d||%s", w.port, name) -} - -func (w *workload) kubeObjects() (cluster.ID, []runtime.Object) { - if w.kind == Pod { - return w.clusterID, w.buildPodService() - } - return "", nil -} - -func (w *workload) configs() []config.Config { - if w.kind == VirtualMachine { - return []config.Config{{ - Meta: config.Meta{ - GroupVersionKind: collections.IstioNetworkingV1Alpha3Serviceentries.Resource().GroupVersionKind(), - Name: w.name, - Namespace: w.namespace, - CreationTimestamp: time.Now(), - }, - Spec: &networking.ServiceEntry{ - Hosts: []string{w.name}, - Ports: []*networking.Port{ - {Number: uint32(w.port), Name: "http", Protocol: "HTTP"}, - }, - Endpoints: []*networking.WorkloadEntry{{ - Address: w.ip, - Labels: w.labels, - }}, - Resolution: networking.ServiceEntry_STATIC, - Location: networking.ServiceEntry_MESH_INTERNAL, - }, - }} - } - return nil -} - -func (w *workload) setupProxy(s *FakeDiscoveryServer) { - p := &model.Proxy{ - ID: strings.Join([]string{w.name, w.namespace}, "."), - Metadata: &model.NodeMetadata{ - Network: w.metaNetwork, - Labels: w.labels, - RequestedNetworkView: w.networkView, - }, - } - if w.kind == Pod { - p.Metadata.ClusterID = w.clusterID - } else { - p.Metadata.InterceptionMode = "NONE" - } - w.proxy = s.SetupProxy(p) -} - -func (w *workload) buildPodService() []runtime.Object { - baseMeta := metav1.ObjectMeta{ - Name: w.name, - Labels: labels.Instance{ - "app": w.name, - label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel, - }, - Namespace: w.namespace, - } - podMeta := baseMeta - podMeta.Name = w.name + "-" + rand.String(4) - for k, v := range w.labels { - podMeta.Labels[k] = v - } - - return []runtime.Object{ - &corev1.Pod{ - ObjectMeta: podMeta, - }, - &corev1.Service{ - ObjectMeta: baseMeta, - Spec: corev1.ServiceSpec{ - ClusterIP: "1.2.3.4", // just can't be 0.0.0.0/ClusterIPNone, not used in eds - Selector: baseMeta.Labels, - Ports: []corev1.ServicePort{{ - Port: w.port, - Name: "http", - Protocol: corev1.ProtocolTCP, - }}, - }, - }, - &corev1.Endpoints{ - ObjectMeta: baseMeta, - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{ - IP: w.ip, - TargetRef: &corev1.ObjectReference{ - APIVersion: "v1", - Kind: "Pod", - Name: podMeta.Name, - Namespace: podMeta.Namespace, - }, - }}, - Ports: []corev1.EndpointPort{{ - Name: "http", - Port: w.port, - Protocol: corev1.ProtocolTCP, - }}, - }}, - }, - } -} diff --git a/pilot/pkg/xds/monitoring.go b/pilot/pkg/xds/monitoring.go deleted file mode 100644 index f3075a56f..000000000 --- a/pilot/pkg/xds/monitoring.go +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package xds - -import ( - "sync" - "time" -) - -import ( - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "istio.io/pkg/monitoring" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" -) - -var ( - errTag = monitoring.MustCreateLabel("err") - nodeTag = monitoring.MustCreateLabel("node") - typeTag = monitoring.MustCreateLabel("type") - versionTag = monitoring.MustCreateLabel("version") - - // pilot_total_xds_rejects should be used instead. This is for backwards compatibility - cdsReject = monitoring.NewGauge( - "pilot_xds_cds_reject", - "Pilot rejected CDS configs.", - monitoring.WithLabels(nodeTag, errTag), - ) - - // pilot_total_xds_rejects should be used instead. This is for backwards compatibility - edsReject = monitoring.NewGauge( - "pilot_xds_eds_reject", - "Pilot rejected EDS.", - monitoring.WithLabels(nodeTag, errTag), - ) - - // pilot_total_xds_rejects should be used instead. This is for backwards compatibility - ldsReject = monitoring.NewGauge( - "pilot_xds_lds_reject", - "Pilot rejected LDS.", - monitoring.WithLabels(nodeTag, errTag), - ) - - // pilot_total_xds_rejects should be used instead. This is for backwards compatibility - rdsReject = monitoring.NewGauge( - "pilot_xds_rds_reject", - "Pilot rejected RDS.", - monitoring.WithLabels(nodeTag, errTag), - ) - - totalXDSRejects = monitoring.NewSum( - "pilot_total_xds_rejects", - "Total number of XDS responses from pilot rejected by proxy.", - monitoring.WithLabels(typeTag), - ) - - // Number of delayed pushes. Currently this happens only when the last push has not been ACKed - totalDelayedPushes = monitoring.NewSum( - "pilot_xds_delayed_pushes_total", - "Total number of XDS pushes that are delayed.", - monitoring.WithLabels(typeTag), - ) - - // Number of delayed pushes that we pushed prematurely as a failsafe. - // This indicates that either the failsafe timeout is too aggressive or there is a deadlock - totalDelayedPushTimeouts = monitoring.NewSum( - "pilot_xds_delayed_push_timeouts_total", - "Total number of XDS pushes that are delayed and timed out", - monitoring.WithLabels(typeTag), - ) - - xdsExpiredNonce = monitoring.NewSum( - "pilot_xds_expired_nonce", - "Total number of XDS requests with an expired nonce.", - monitoring.WithLabels(typeTag), - ) - - monServices = monitoring.NewGauge( - "pilot_services", - "Total services known to pilot.", - ) - - // TODO: Update all the resource stats in separate routine - // virtual services, destination rules, gateways, etc. - xdsClients = monitoring.NewGauge( - "pilot_xds", - "Number of endpoints connected to this pilot using XDS.", - monitoring.WithLabels(versionTag), - ) - xdsClientTrackerMutex = &sync.Mutex{} - xdsClientTracker = make(map[string]float64) - - xdsResponseWriteTimeouts = monitoring.NewSum( - "pilot_xds_write_timeout", - "Pilot XDS response write timeouts.", - ) - - // Covers xds_builderr and xds_senderr for xds in {lds, rds, cds, eds}. - pushes = monitoring.NewSum( - "pilot_xds_pushes", - "Pilot build and send errors for lds, rds, cds and eds.", - monitoring.WithLabels(typeTag), - ) - - cdsSendErrPushes = pushes.With(typeTag.Value("cds_senderr")) - edsSendErrPushes = pushes.With(typeTag.Value("eds_senderr")) - ldsSendErrPushes = pushes.With(typeTag.Value("lds_senderr")) - rdsSendErrPushes = pushes.With(typeTag.Value("rds_senderr")) - - pushContextInitTime = monitoring.NewDistribution( - "pilot_pushcontext_init_seconds", - "Total time in seconds Pilot takes to init pushContext.", - []float64{.01, .1, 0.5, 1, 3, 5}, - ) - - pushTime = monitoring.NewDistribution( - "pilot_xds_push_time", - "Total time in seconds Pilot takes to push lds, rds, cds and eds.", - []float64{.01, .1, 1, 3, 5, 10, 20, 30}, - monitoring.WithLabels(typeTag), - ) - - sendTime = monitoring.NewDistribution( - "pilot_xds_send_time", - "Total time in seconds Pilot takes to send generated configuration.", - []float64{.01, .1, 1, 3, 5, 10, 20, 30}, - ) - - // only supported dimension is millis, unfortunately. default to unitdimensionless. - proxiesQueueTime = monitoring.NewDistribution( - "pilot_proxy_queue_time", - "Time in seconds, a proxy is in the push queue before being dequeued.", - []float64{.1, .5, 1, 3, 5, 10, 20, 30}, - ) - - pushTriggers = monitoring.NewSum( - "pilot_push_triggers", - "Total number of times a push was triggered, labeled by reason for the push.", - monitoring.WithLabels(typeTag), - ) - - // only supported dimension is millis, unfortunately. default to unitdimensionless. - proxiesConvergeDelay = monitoring.NewDistribution( - "pilot_proxy_convergence_time", - "Delay in seconds between config change and a proxy receiving all required configuration.", - []float64{.1, .5, 1, 3, 5, 10, 20, 30}, - ) - - pushContextErrors = monitoring.NewSum( - "pilot_xds_push_context_errors", - "Number of errors (timeouts) initiating push context.", - ) - - totalXDSInternalErrors = monitoring.NewSum( - "pilot_total_xds_internal_errors", - "Total number of internal XDS errors in pilot.", - ) - - inboundUpdates = monitoring.NewSum( - "pilot_inbound_updates", - "Total number of updates received by pilot.", - monitoring.WithLabels(typeTag), - ) - - pilotSDSCertificateErrors = monitoring.NewSum( - "pilot_sds_certificate_errors_total", - "Total number of failures to fetch SDS key and certificate.", - ) - - inboundConfigUpdates = inboundUpdates.With(typeTag.Value("config")) - inboundEDSUpdates = inboundUpdates.With(typeTag.Value("eds")) - inboundServiceUpdates = inboundUpdates.With(typeTag.Value("svc")) - inboundServiceDeletes = inboundUpdates.With(typeTag.Value("svcdelete")) - - configSizeBytes = monitoring.NewDistribution( - "pilot_xds_config_size_bytes", - "Distribution of configuration sizes pushed to clients", - // Important boundaries: 10K, 1M, 4M, 10M, 40M - // 4M default limit for gRPC, 10M config will start to strain system, - // 40M is likely upper-bound on config sizes supported. - []float64{1, 10000, 1000000, 4000000, 10000000, 40000000}, - monitoring.WithLabels(typeTag), - monitoring.WithUnit(monitoring.Bytes), - ) -) - -func recordXDSClients(version string, delta float64) { - xdsClientTrackerMutex.Lock() - defer xdsClientTrackerMutex.Unlock() - xdsClientTracker[version] += delta - xdsClients.With(versionTag.Value(version)).Record(xdsClientTracker[version]) -} - -// triggerMetric is a precomputed monitoring.Metric for each trigger type. This saves on a lot of allocations -var triggerMetric = map[model.TriggerReason]monitoring.Metric{ - model.EndpointUpdate: pushTriggers.With(typeTag.Value(string(model.EndpointUpdate))), - model.ConfigUpdate: pushTriggers.With(typeTag.Value(string(model.ConfigUpdate))), - model.ServiceUpdate: pushTriggers.With(typeTag.Value(string(model.ServiceUpdate))), - model.ProxyUpdate: pushTriggers.With(typeTag.Value(string(model.ProxyUpdate))), - model.GlobalUpdate: pushTriggers.With(typeTag.Value(string(model.GlobalUpdate))), - model.UnknownTrigger: pushTriggers.With(typeTag.Value(string(model.UnknownTrigger))), - model.DebugTrigger: pushTriggers.With(typeTag.Value(string(model.DebugTrigger))), - model.SecretTrigger: pushTriggers.With(typeTag.Value(string(model.SecretTrigger))), - model.NetworksTrigger: pushTriggers.With(typeTag.Value(string(model.NetworksTrigger))), - model.ProxyRequest: pushTriggers.With(typeTag.Value(string(model.ProxyRequest))), - model.NamespaceUpdate: pushTriggers.With(typeTag.Value(string(model.NamespaceUpdate))), - model.ClusterUpdate: pushTriggers.With(typeTag.Value(string(model.ClusterUpdate))), -} - -func recordPushTriggers(reasons ...model.TriggerReason) { - for _, r := range reasons { - t, f := triggerMetric[r] - if f { - t.Increment() - } else { - pushTriggers.With(typeTag.Value(string(r))).Increment() - } - } -} - -func isUnexpectedError(err error) bool { - s, ok := status.FromError(err) - // Unavailable or canceled code will be sent when a connection is closing down. This is very normal, - // due to the XDS connection being dropped every 30 minutes, or a pod shutting down. - isError := s.Code() != codes.Unavailable && s.Code() != codes.Canceled - return !ok || isError -} - -// recordSendError records a metric indicating that a push failed. It returns true if this was an unexpected -// error -func recordSendError(xdsType string, err error) bool { - if isUnexpectedError(err) { - // TODO use a single metric with a type tag - switch xdsType { - case v3.ListenerType: - ldsSendErrPushes.Increment() - case v3.ClusterType: - cdsSendErrPushes.Increment() - case v3.EndpointType: - edsSendErrPushes.Increment() - case v3.RouteType: - rdsSendErrPushes.Increment() - } - return true - } - return false -} - -func incrementXDSRejects(xdsType string, node, errCode string) { - totalXDSRejects.With(typeTag.Value(v3.GetMetricType(xdsType))).Increment() - switch xdsType { - case v3.ListenerType: - ldsReject.With(nodeTag.Value(node), errTag.Value(errCode)).Increment() - case v3.ClusterType: - cdsReject.With(nodeTag.Value(node), errTag.Value(errCode)).Increment() - case v3.EndpointType: - edsReject.With(nodeTag.Value(node), errTag.Value(errCode)).Increment() - case v3.RouteType: - rdsReject.With(nodeTag.Value(node), errTag.Value(errCode)).Increment() - } -} - -func recordSendTime(duration time.Duration) { - sendTime.Record(duration.Seconds()) -} - -func recordPushTime(xdsType string, duration time.Duration) { - pushTime.With(typeTag.Value(v3.GetMetricType(xdsType))).Record(duration.Seconds()) - pushes.With(typeTag.Value(v3.GetMetricType(xdsType))).Increment() -} - -func init() { - monitoring.MustRegister( - cdsReject, - edsReject, - ldsReject, - rdsReject, - xdsExpiredNonce, - totalXDSRejects, - monServices, - xdsClients, - xdsResponseWriteTimeouts, - pushes, - pushTime, - proxiesConvergeDelay, - proxiesQueueTime, - pushContextErrors, - totalXDSInternalErrors, - inboundUpdates, - pushTriggers, - sendTime, - totalDelayedPushes, - totalDelayedPushTimeouts, - pilotSDSCertificateErrors, - configSizeBytes, - ) -} diff --git a/pilot/pkg/xds/nds.go b/pilot/pkg/xds/nds.go deleted file mode 100644 index 5e94af915..000000000 --- a/pilot/pkg/xds/nds.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -// NdsGenerator Nds stands for Name Discovery Service. Istio agents send NDS requests to istiod -// istiod responds with a list of service entries and their associated IPs (including k8s services) -// The agent then updates its internal DNS based on this data. If DNS capture is enabled in the pod -// the agent will capture all DNS requests and attempt to resolve locally before forwarding to upstream -// dns servers/ -type NdsGenerator struct { - Server *DiscoveryServer -} - -var _ model.XdsResourceGenerator = &NdsGenerator{} - -// Map of all configs that do not impact NDS -var skippedNdsConfigs = map[config.GroupVersionKind]struct{}{ - gvk.Gateway: {}, - gvk.VirtualService: {}, - gvk.DestinationRule: {}, - gvk.EnvoyFilter: {}, - gvk.WorkloadEntry: {}, - gvk.WorkloadGroup: {}, - gvk.AuthorizationPolicy: {}, - gvk.RequestAuthentication: {}, - gvk.PeerAuthentication: {}, - gvk.WasmPlugin: {}, -} - -func ndsNeedsPush(req *model.PushRequest) bool { - if req == nil { - return true - } - if !req.Full { - // NDS only handles full push - return false - } - // If none set, we will always push - if len(req.ConfigsUpdated) == 0 { - return true - } - for config := range req.ConfigsUpdated { - if _, f := skippedNdsConfigs[config.Kind]; !f { - return true - } - } - return false -} - -func (n NdsGenerator) Generate(proxy *model.Proxy, _ *model.WatchedResource, req *model.PushRequest) (model.Resources, model.XdsLogDetails, error) { - if !ndsNeedsPush(req) { - return nil, model.DefaultXdsLogDetails, nil - } - nt := n.Server.ConfigGenerator.BuildNameTable(proxy, req.Push) - if nt == nil { - return nil, model.DefaultXdsLogDetails, nil - } - resources := model.Resources{&discovery.Resource{Resource: util.MessageToAny(nt)}} - return resources, model.DefaultXdsLogDetails, nil -} diff --git a/pilot/pkg/xds/nds_test.go b/pilot/pkg/xds/nds_test.go deleted file mode 100644 index fbd0f34b5..000000000 --- a/pilot/pkg/xds/nds_test.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package xds_test - -import ( - "testing" -) - -import ( - corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/testing/protocmp" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - dnsProto "github.com/apache/dubbo-go-pixiu/pkg/dns/proto" -) - -func TestNDS(t *testing.T) { - cases := []struct { - name string - meta model.NodeMetadata - expected *dnsProto.NameTable - }{ - { - name: "auto allocate", - meta: model.NodeMetadata{ - DNSCapture: true, - DNSAutoAllocate: true, - }, - expected: &dnsProto.NameTable{ - Table: map[string]*dnsProto.NameTable_NameInfo{ - "random-1.host.example": { - Ips: []string{"240.240.0.1"}, - Registry: "External", - }, - "random-2.host.example": { - Ips: []string{"9.9.9.9"}, - Registry: "External", - }, - "random-3.host.example": { - Ips: []string{"240.240.0.2"}, - Registry: "External", - }, - }, - }, - }, - { - name: "just capture", - meta: model.NodeMetadata{ - DNSCapture: true, - }, - expected: &dnsProto.NameTable{ - Table: map[string]*dnsProto.NameTable_NameInfo{ - "random-2.host.example": { - Ips: []string{"9.9.9.9"}, - Registry: "External", - }, - }, - }, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ - ConfigString: mustReadFile(t, "./testdata/nds-se.yaml"), - }) - - ads := s.ConnectADS().WithType(v3.NameTableType) - res := ads.RequestResponseAck(t, &discovery.DiscoveryRequest{ - Node: &corev3.Node{ - Id: ads.ID, - Metadata: tt.meta.ToStruct(), - }, - }) - - nt := &dnsProto.NameTable{} - err := res.Resources[0].UnmarshalTo(nt) - if err != nil { - t.Fatal("Failed to unmarshal name table", err) - return - } - if len(nt.Table) == 0 { - t.Fatalf("expected more than 0 entries in name table") - } - if diff := cmp.Diff(nt, tt.expected, protocmp.Transform()); diff != "" { - t.Fatalf("name table does not match expected value:\n %v", diff) - } - }) - } -} diff --git a/pilot/pkg/xds/pcds.go b/pilot/pkg/xds/pcds.go deleted file mode 100644 index 92d957b81..000000000 --- a/pilot/pkg/xds/pcds.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - mesh "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - tb "github.com/apache/dubbo-go-pixiu/pilot/pkg/trustbundle" -) - -// PcdsGenerator generates proxy configuration for proxies to consume -type PcdsGenerator struct { - Server *DiscoveryServer - TrustBundle *tb.TrustBundle -} - -var _ model.XdsResourceGenerator = &PcdsGenerator{} - -func pcdsNeedsPush(req *model.PushRequest) bool { - if !features.MultiRootMesh { - return false - } - - if req == nil { - return true - } - - if !req.Full { - return false - } - - if len(req.ConfigsUpdated) == 0 { - // This needs to be better optimized - return true - } - - return false -} - -// Generate returns ProxyConfig protobuf containing TrustBundle for given proxy -func (e *PcdsGenerator) Generate(proxy *model.Proxy, w *model.WatchedResource, req *model.PushRequest) (model.Resources, model.XdsLogDetails, error) { - if !pcdsNeedsPush(req) { - return nil, model.DefaultXdsLogDetails, nil - } - if e.TrustBundle == nil { - return nil, model.DefaultXdsLogDetails, nil - } - // TODO: For now, only TrustBundle updates are pushed. Eventually, this should push entire Proxy Configuration - pc := &mesh.ProxyConfig{ - CaCertificatesPem: e.TrustBundle.GetTrustBundle(), - } - return model.Resources{&discovery.Resource{Resource: util.MessageToAny(pc)}}, model.DefaultXdsLogDetails, nil -} diff --git a/pilot/pkg/xds/proxy_dependencies.go b/pilot/pkg/xds/proxy_dependencies.go deleted file mode 100644 index dbf1dd675..000000000 --- a/pilot/pkg/xds/proxy_dependencies.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -// configKindAffectedProxyTypes contains known config types which may affect certain node types. -var configKindAffectedProxyTypes = map[config.GroupVersionKind][]model.NodeType{ - gvk.Gateway: {model.Router}, - gvk.Sidecar: {model.SidecarProxy}, -} - -// ConfigAffectsProxy checks if a pushEv will affect a specified proxy. That means whether the push will be performed -// towards the proxy. -func ConfigAffectsProxy(req *model.PushRequest, proxy *model.Proxy) bool { - // Empty changes means "all" to get a backward compatibility. - if len(req.ConfigsUpdated) == 0 { - return true - } - - for config := range req.ConfigsUpdated { - affected := true - - // Some configKinds only affect specific proxy types - if kindAffectedTypes, f := configKindAffectedProxyTypes[config.Kind]; f { - affected = false - for _, t := range kindAffectedTypes { - if t == proxy.Type { - affected = true - break - } - } - } - - if affected && checkProxyDependencies(proxy, config) { - return true - } - } - - return false -} - -func checkProxyDependencies(proxy *model.Proxy, config model.ConfigKey) bool { - // Detailed config dependencies check. - switch proxy.Type { - case model.SidecarProxy: - if proxy.SidecarScope.DependsOnConfig(config) { - return true - } else if proxy.PrevSidecarScope != nil && proxy.PrevSidecarScope.DependsOnConfig(config) { - return true - } - default: - // TODO We'll add the check for other proxy types later. - return true - } - return false -} - -// DefaultProxyNeedsPush check if a proxy needs push for this push event. -func DefaultProxyNeedsPush(proxy *model.Proxy, req *model.PushRequest) bool { - if ConfigAffectsProxy(req, proxy) { - return true - } - - // If the proxy's service updated, need push for it. - if len(proxy.ServiceInstances) > 0 && req.ConfigsUpdated != nil { - svc := proxy.ServiceInstances[0].Service - if _, ok := req.ConfigsUpdated[model.ConfigKey{ - Kind: gvk.ServiceEntry, - Name: string(svc.Hostname), - Namespace: svc.Attributes.Namespace, - }]; ok { - return true - } - } - - return false -} diff --git a/pilot/pkg/xds/proxy_dependencies_test.go b/pilot/pkg/xds/proxy_dependencies_test.go deleted file mode 100644 index 3c2381a97..000000000 --- a/pilot/pkg/xds/proxy_dependencies_test.go +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "fmt" - "strconv" - "testing" -) - -import ( - model "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/spiffe" -) - -func TestProxyNeedsPush(t *testing.T) { - const ( - invalidKind = "INVALID_KIND" - svcName = "svc1.com" - drName = "dr1" - vsName = "vs1" - scName = "sc1" - nsName = "ns1" - nsRoot = "rootns" - generalName = "name1" - - invalidNameSuffix = "invalid" - ) - - type Case struct { - name string - proxy *model.Proxy - configs map[model.ConfigKey]struct{} - want bool - } - - sidecar := &model.Proxy{ - Type: model.SidecarProxy, IPAddresses: []string{"127.0.0.1"}, Metadata: &model.NodeMetadata{}, - SidecarScope: &model.SidecarScope{Name: generalName, Namespace: nsName, RootNamespace: nsRoot}, - } - gateway := &model.Proxy{Type: model.Router} - - sidecarScopeKindNames := map[config.GroupVersionKind]string{ - gvk.ServiceEntry: svcName, gvk.VirtualService: vsName, gvk.DestinationRule: drName, gvk.Sidecar: scName, - } - for kind, name := range sidecarScopeKindNames { - sidecar.SidecarScope.AddConfigDependencies(model.ConfigKey{Kind: kind, Name: name, Namespace: nsName}) - } - for kind, types := range configKindAffectedProxyTypes { - for _, nodeType := range types { - if nodeType == model.SidecarProxy { - sidecar.SidecarScope.AddConfigDependencies(model.ConfigKey{ - Kind: kind, - Name: generalName, - Namespace: nsName, - }) - } - } - } - - cases := []Case{ - {"no namespace or configs", sidecar, nil, true}, - {"gateway config for sidecar", sidecar, map[model.ConfigKey]struct{}{ - { - Kind: gvk.Gateway, - Name: generalName, Namespace: nsName, - }: {}, - }, false}, - {"gateway config for gateway", gateway, map[model.ConfigKey]struct{}{ - { - Kind: gvk.Gateway, - Name: generalName, Namespace: nsName, - }: {}, - }, true}, - {"sidecar config for gateway", gateway, map[model.ConfigKey]struct{}{ - { - Kind: gvk.Sidecar, - Name: scName, Namespace: nsName, - }: {}, - }, false}, - { - "invalid config for sidecar", sidecar, - map[model.ConfigKey]struct{}{ - { - Kind: config.GroupVersionKind{Kind: invalidKind}, Name: generalName, Namespace: nsName, - }: {}, - }, - true, - }, - {"mixture matched and unmatched config for sidecar", sidecar, map[model.ConfigKey]struct{}{ - {Kind: gvk.DestinationRule, Name: drName, Namespace: nsName}: {}, - {Kind: gvk.ServiceEntry, Name: svcName + invalidNameSuffix, Namespace: nsName}: {}, - }, true}, - {"mixture unmatched and unmatched config for sidecar", sidecar, map[model.ConfigKey]struct{}{ - {Kind: gvk.DestinationRule, Name: drName + invalidNameSuffix, Namespace: nsName}: {}, - {Kind: gvk.ServiceEntry, Name: svcName + invalidNameSuffix, Namespace: nsName}: {}, - }, false}, - {"empty configsUpdated for sidecar", sidecar, nil, true}, - } - - for kind, name := range sidecarScopeKindNames { - cases = append(cases, Case{ // valid name - name: fmt.Sprintf("%s config for sidecar", kind.Kind), - proxy: sidecar, - configs: map[model.ConfigKey]struct{}{{Kind: kind, Name: name, Namespace: nsName}: {}}, - want: true, - }, Case{ // invalid name - name: fmt.Sprintf("%s unmatched config for sidecar", kind.Kind), - proxy: sidecar, - configs: map[model.ConfigKey]struct{}{{Kind: kind, Name: name + invalidNameSuffix, Namespace: nsName}: {}}, - want: false, - }) - } - - sidecarNamespaceScopeTypes := []config.GroupVersionKind{ - gvk.EnvoyFilter, gvk.AuthorizationPolicy, gvk.RequestAuthentication, - } - for _, kind := range sidecarNamespaceScopeTypes { - cases = append(cases, - Case{ - name: fmt.Sprintf("%s config for sidecar in same namespace", kind.Kind), - proxy: sidecar, - configs: map[model.ConfigKey]struct{}{{Kind: kind, Name: generalName, Namespace: nsName}: {}}, - want: true, - }, - Case{ - name: fmt.Sprintf("%s config for sidecar in different namespace", kind.Kind), - proxy: sidecar, - configs: map[model.ConfigKey]struct{}{{Kind: kind, Name: generalName, Namespace: "invalid-namespace"}: {}}, - want: false, - }, - ) - } - - // tests for kind-affect-proxy. - for kind, types := range configKindAffectedProxyTypes { - for _, nodeType := range model.NodeTypes { - affected := false - for _, affectedType := range types { - if nodeType == affectedType { - affected = true - break - } - } - - if !affected { - proxy := gateway - if nodeType == model.SidecarProxy { - proxy = sidecar - } - cases = append(cases, Case{ - name: fmt.Sprintf("kind %s not affect %s", kind, nodeType), - proxy: proxy, - configs: map[model.ConfigKey]struct{}{ - {Kind: kind, Name: generalName + invalidNameSuffix, Namespace: nsName}: {}, - }, - want: false, - }) - } - } - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - got := DefaultProxyNeedsPush(tt.proxy, &model.PushRequest{ConfigsUpdated: tt.configs}) - if got != tt.want { - t.Fatalf("Got needs push = %v, expected %v", got, tt.want) - } - }) - } -} - -func BenchmarkListEquals(b *testing.B) { - size := 100 - var l []string - for i := 0; i < size; i++ { - l = append(l, strconv.Itoa(i)) - } - var equal []string - for i := 0; i < size; i++ { - equal = append(equal, strconv.Itoa(i)) - } - var notEqual []string - for i := 0; i < size; i++ { - notEqual = append(notEqual, strconv.Itoa(i)) - } - notEqual[size-1] = "z" - - for n := 0; n < b.N; n++ { - listEqualUnordered(l, equal) - listEqualUnordered(l, notEqual) - } -} - -func TestCheckConnectionIdentity(t *testing.T) { - cases := []struct { - name string - identity []string - sa string - namespace string - success bool - }{ - { - name: "single match", - identity: []string{spiffe.Identity{TrustDomain: "cluster.local", Namespace: "namespace", ServiceAccount: "serviceaccount"}.String()}, - sa: "serviceaccount", - namespace: "namespace", - success: true, - }, - { - name: "second match", - identity: []string{ - spiffe.Identity{TrustDomain: "cluster.local", Namespace: "bad", ServiceAccount: "serviceaccount"}.String(), - spiffe.Identity{TrustDomain: "cluster.local", Namespace: "namespace", ServiceAccount: "serviceaccount"}.String(), - }, - sa: "serviceaccount", - namespace: "namespace", - success: true, - }, - { - name: "no match namespace", - identity: []string{ - spiffe.Identity{TrustDomain: "cluster.local", Namespace: "bad", ServiceAccount: "serviceaccount"}.String(), - }, - sa: "serviceaccount", - namespace: "namespace", - success: false, - }, - { - name: "no match service account", - identity: []string{ - spiffe.Identity{TrustDomain: "cluster.local", Namespace: "namespace", ServiceAccount: "bad"}.String(), - }, - sa: "serviceaccount", - namespace: "namespace", - success: false, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - proxy := &model.Proxy{ConfigNamespace: tt.namespace, Metadata: &model.NodeMetadata{ServiceAccount: tt.sa}} - if _, err := checkConnectionIdentity(proxy, tt.identity); (err == nil) != tt.success { - t.Fatalf("expected success=%v, got err=%v", tt.success, err) - } - }) - } -} diff --git a/pilot/pkg/xds/pushqueue.go b/pilot/pkg/xds/pushqueue.go deleted file mode 100644 index fb491f083..000000000 --- a/pilot/pkg/xds/pushqueue.go +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "sync" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" -) - -type PushQueue struct { - cond *sync.Cond - - // pending stores all connections in the queue. If the same connection is enqueued again, - // the PushRequest will be merged. - pending map[*Connection]*model.PushRequest - - // queue maintains ordering of the queue - queue []*Connection - - // processing stores all connections that have been Dequeue(), but not MarkDone(). - // The value stored will be initially be nil, but may be populated if the connection is Enqueue(). - // If model.PushRequest is not nil, it will be Enqueued again once MarkDone has been called. - processing map[*Connection]*model.PushRequest - - shuttingDown bool -} - -func NewPushQueue() *PushQueue { - return &PushQueue{ - pending: make(map[*Connection]*model.PushRequest), - processing: make(map[*Connection]*model.PushRequest), - cond: sync.NewCond(&sync.Mutex{}), - } -} - -// Enqueue will mark a proxy as pending a push. If it is already pending, pushInfo will be merged. -// ServiceEntry updates will be added together, and full will be set if either were full -func (p *PushQueue) Enqueue(con *Connection, pushRequest *model.PushRequest) { - p.cond.L.Lock() - defer p.cond.L.Unlock() - - if p.shuttingDown { - return - } - - // If its already in progress, merge the info and return - if request, f := p.processing[con]; f { - p.processing[con] = request.CopyMerge(pushRequest) - return - } - - if request, f := p.pending[con]; f { - p.pending[con] = request.CopyMerge(pushRequest) - return - } - - p.pending[con] = pushRequest - p.queue = append(p.queue, con) - // Signal waiters on Dequeue that a new item is available - p.cond.Signal() -} - -// Remove a proxy from the queue. If there are no proxies ready to be removed, this will block -func (p *PushQueue) Dequeue() (con *Connection, request *model.PushRequest, shutdown bool) { - p.cond.L.Lock() - defer p.cond.L.Unlock() - - // Block until there is one to remove. Enqueue will signal when one is added. - for len(p.queue) == 0 && !p.shuttingDown { - p.cond.Wait() - } - - if len(p.queue) == 0 { - // We must be shutting down. - return nil, nil, true - } - - con = p.queue[0] - // The underlying array will still exist, despite the slice changing, so the object may not GC without this - // See https://github.com/grpc/grpc-go/issues/4758 - p.queue[0] = nil - p.queue = p.queue[1:] - - request = p.pending[con] - delete(p.pending, con) - - // Mark the connection as in progress - p.processing[con] = nil - - return con, request, false -} - -func (p *PushQueue) MarkDone(con *Connection) { - p.cond.L.Lock() - defer p.cond.L.Unlock() - request := p.processing[con] - delete(p.processing, con) - - // If the info is present, that means Enqueue was called while connection was not yet marked done. - // This means we need to add it back to the queue. - if request != nil { - p.pending[con] = request - p.queue = append(p.queue, con) - p.cond.Signal() - } -} - -// Get number of pending proxies -func (p *PushQueue) Pending() int { - p.cond.L.Lock() - defer p.cond.L.Unlock() - return len(p.queue) -} - -// ShutDown will cause queue to ignore all new items added to it. As soon as the -// worker goroutines have drained the existing items in the queue, they will be -// instructed to exit. -func (p *PushQueue) ShutDown() { - p.cond.L.Lock() - defer p.cond.L.Unlock() - p.shuttingDown = true - p.cond.Broadcast() -} diff --git a/pilot/pkg/xds/pushqueue_test.go b/pilot/pkg/xds/pushqueue_test.go deleted file mode 100644 index 29232abac..000000000 --- a/pilot/pkg/xds/pushqueue_test.go +++ /dev/null @@ -1,386 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "fmt" - "reflect" - "sort" - "strconv" - "sync" - "testing" - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -// Helper function to remove an item or timeout and return nil if there are no pending pushes -func getWithTimeout(p *PushQueue) *Connection { - done := make(chan *Connection, 1) - go func() { - con, _, _ := p.Dequeue() - done <- con - }() - select { - case ret := <-done: - return ret - case <-time.After(time.Millisecond * 500): - return nil - } -} - -func ExpectTimeout(t *testing.T, p *PushQueue) { - t.Helper() - done := make(chan struct{}, 1) - go func() { - p.Dequeue() - done <- struct{}{} - }() - select { - case <-done: - t.Fatalf("Expected timeout") - case <-time.After(time.Millisecond * 500): - } -} - -func ExpectDequeue(t *testing.T, p *PushQueue, expected *Connection) { - t.Helper() - result := make(chan *Connection, 1) - go func() { - con, _, _ := p.Dequeue() - result <- con - }() - select { - case got := <-result: - if got != expected { - t.Fatalf("Expected proxy %v, got %v", expected, got) - } - case <-time.After(time.Millisecond * 500): - t.Fatalf("Timed out") - } -} - -func TestProxyQueue(t *testing.T) { - proxies := make([]*Connection, 0, 100) - for p := 0; p < 100; p++ { - proxies = append(proxies, &Connection{conID: fmt.Sprintf("proxy-%d", p)}) - } - - t.Run("simple add and remove", func(t *testing.T) { - t.Parallel() - p := NewPushQueue() - defer p.ShutDown() - p.Enqueue(proxies[0], &model.PushRequest{}) - p.Enqueue(proxies[1], &model.PushRequest{}) - - ExpectDequeue(t, p, proxies[0]) - ExpectDequeue(t, p, proxies[1]) - }) - - t.Run("remove too many", func(t *testing.T) { - t.Parallel() - p := NewPushQueue() - defer p.ShutDown() - - p.Enqueue(proxies[0], &model.PushRequest{}) - - ExpectDequeue(t, p, proxies[0]) - ExpectTimeout(t, p) - }) - - t.Run("add multiple times", func(t *testing.T) { - t.Parallel() - p := NewPushQueue() - defer p.ShutDown() - - p.Enqueue(proxies[0], &model.PushRequest{}) - p.Enqueue(proxies[1], &model.PushRequest{}) - p.Enqueue(proxies[0], &model.PushRequest{}) - - ExpectDequeue(t, p, proxies[0]) - ExpectDequeue(t, p, proxies[1]) - ExpectTimeout(t, p) - }) - - t.Run("add and remove and markdone", func(t *testing.T) { - t.Parallel() - p := NewPushQueue() - defer p.ShutDown() - - p.Enqueue(proxies[0], &model.PushRequest{}) - ExpectDequeue(t, p, proxies[0]) - p.MarkDone(proxies[0]) - p.Enqueue(proxies[0], &model.PushRequest{}) - ExpectDequeue(t, p, proxies[0]) - ExpectTimeout(t, p) - }) - - t.Run("add and remove and add and markdone", func(t *testing.T) { - t.Parallel() - p := NewPushQueue() - defer p.ShutDown() - - p.Enqueue(proxies[0], &model.PushRequest{}) - ExpectDequeue(t, p, proxies[0]) - p.Enqueue(proxies[0], &model.PushRequest{}) - p.Enqueue(proxies[0], &model.PushRequest{}) - p.MarkDone(proxies[0]) - - ExpectDequeue(t, p, proxies[0]) - ExpectTimeout(t, p) - }) - - t.Run("remove should block", func(t *testing.T) { - t.Parallel() - p := NewPushQueue() - defer p.ShutDown() - - wg := &sync.WaitGroup{} - wg.Add(1) - go func() { - ExpectDequeue(t, p, proxies[0]) - wg.Done() - }() - time.Sleep(time.Millisecond * 50) - p.Enqueue(proxies[0], &model.PushRequest{}) - wg.Wait() - }) - - t.Run("should merge model.PushRequest", func(t *testing.T) { - t.Parallel() - p := NewPushQueue() - defer p.ShutDown() - - firstTime := time.Now() - p.Enqueue(proxies[0], &model.PushRequest{ - Full: false, - ConfigsUpdated: map[model.ConfigKey]struct{}{{ - Kind: gvk.ServiceEntry, - Name: "foo", - }: {}}, - Start: firstTime, - }) - - p.Enqueue(proxies[0], &model.PushRequest{ - Full: false, - ConfigsUpdated: map[model.ConfigKey]struct{}{{ - Kind: gvk.ServiceEntry, - Name: "bar", - Namespace: "ns1", - }: {}}, - Start: firstTime.Add(time.Second), - }) - _, info, _ := p.Dequeue() - - if info.Start != firstTime { - t.Errorf("Expected start time to be %v, got %v", firstTime, info.Start) - } - expectedEds := map[model.ConfigKey]struct{}{{ - Kind: gvk.ServiceEntry, - Name: "foo", - Namespace: "", - }: {}, { - Kind: gvk.ServiceEntry, - Name: "bar", - Namespace: "ns1", - }: {}} - if !reflect.DeepEqual(model.ConfigsOfKind(info.ConfigsUpdated, gvk.ServiceEntry), expectedEds) { - t.Errorf("Expected EdsUpdates to be %v, got %v", expectedEds, model.ConfigsOfKind(info.ConfigsUpdated, gvk.ServiceEntry)) - } - if info.Full { - t.Errorf("Expected full to be false, got true") - } - }) - - t.Run("two removes, one should block one should return", func(t *testing.T) { - t.Parallel() - p := NewPushQueue() - defer p.ShutDown() - - wg := &sync.WaitGroup{} - wg.Add(2) - respChannel := make(chan *Connection, 2) - go func() { - respChannel <- getWithTimeout(p) - wg.Done() - }() - time.Sleep(time.Millisecond * 50) - p.Enqueue(proxies[0], &model.PushRequest{}) - go func() { - respChannel <- getWithTimeout(p) - wg.Done() - }() - - wg.Wait() - timeouts := 0 - close(respChannel) - for resp := range respChannel { - if resp == nil { - timeouts++ - } - } - if timeouts != 1 { - t.Fatalf("Expected 1 timeout, got %v", timeouts) - } - }) - - t.Run("concurrent", func(t *testing.T) { - t.Parallel() - p := NewPushQueue() - defer p.ShutDown() - - key := func(p *Connection, eds string) string { return fmt.Sprintf("%s~%s", p.conID, eds) } - - // We will trigger many pushes for eds services to each proxy. In the end we will expect - // all of these to be dequeue, but order is not deterministic. - expected := map[string]struct{}{} - for eds := 0; eds < 100; eds++ { - for _, pr := range proxies { - expected[key(pr, fmt.Sprintf("%d", eds))] = struct{}{} - } - } - go func() { - for eds := 0; eds < 100; eds++ { - for _, pr := range proxies { - p.Enqueue(pr, &model.PushRequest{ - ConfigsUpdated: map[model.ConfigKey]struct{}{{ - Kind: gvk.ServiceEntry, - Name: fmt.Sprintf("%d", eds), - }: {}}, - }) - } - } - }() - - done := make(chan struct{}) - mu := sync.RWMutex{} - go func() { - for { - con, info, shuttingdown := p.Dequeue() - if shuttingdown { - return - } - for eds := range model.ConfigNamesOfKind(info.ConfigsUpdated, gvk.ServiceEntry) { - mu.Lock() - delete(expected, key(con, eds)) - mu.Unlock() - } - p.MarkDone(con) - if len(expected) == 0 { - done <- struct{}{} - } - } - }() - - select { - case <-done: - case <-time.After(time.Second * 10): - mu.RLock() - defer mu.RUnlock() - t.Fatalf("failed to get all updates, still pending: %v", len(expected)) - } - }) - - t.Run("concurrent with deterministic order", func(t *testing.T) { - t.Parallel() - p := NewPushQueue() - defer p.ShutDown() - con := &Connection{conID: "proxy-test"} - - // We will trigger many pushes for eds services to the proxy. In the end we will expect - // all of these to be dequeue, but order is deterministic. - expected := make([]string, 100) - for eds := 0; eds < 100; eds++ { - expected[eds] = fmt.Sprintf("%d", eds) - } - go func() { - // send to pushQueue - for eds := 0; eds < 100; eds++ { - p.Enqueue(con, &model.PushRequest{ - ConfigsUpdated: map[model.ConfigKey]struct{}{{ - Kind: config.GroupVersionKind{Group: "networking.istio.io", Version: "v1alpha3", Kind: fmt.Sprintf("%d", eds)}, - Name: fmt.Sprintf("%d", eds), - }: {}}, - }) - } - }() - - processed := make([]string, 0, 100) - done := make(chan struct{}) - pushChannel := make(chan *model.PushRequest) - go func() { - // dequeue pushQueue and send to pushChannel - for { - _, request, shuttingdown := p.Dequeue() - if shuttingdown { - close(pushChannel) - return - } - pushChannel <- request - } - }() - - go func() { - // recv from pushChannel and simulate push - for { - request := <-pushChannel - if request == nil { - return - } - updated := make([]string, 0, len(request.ConfigsUpdated)) - for configkey := range request.ConfigsUpdated { - updated = append(updated, configkey.Kind.Kind) - } - sort.Slice(updated, func(i, j int) bool { - l, _ := strconv.Atoi(updated[i]) - r, _ := strconv.Atoi(updated[j]) - return l < r - }) - processed = append(processed, updated...) - if len(processed) == 100 { - done <- struct{}{} - } - p.MarkDone(con) - } - }() - - select { - case <-done: - case <-time.After(time.Second * 10): - t.Fatalf("failed to get all updates, still pending: got %v", len(processed)) - } - - if !reflect.DeepEqual(expected, processed) { - t.Fatalf("expected order %v, but got %v", expected, processed) - } - }) -} - -// TestPushQueueLeak is a regression test for https://github.com/grpc/grpc-go/issues/4758 -func TestPushQueueLeak(t *testing.T) { - ds := NewFakeDiscoveryServer(t, FakeOptions{}) - p := ds.ConnectADS() - p.RequestResponseAck(t, nil) - for _, c := range ds.Discovery.AllClients() { - leak.MustGarbageCollect(t, c) - } - ds.Discovery.startPush(&model.PushRequest{}) - p.Cleanup() -} diff --git a/pilot/pkg/xds/rds.go b/pilot/pkg/xds/rds.go deleted file mode 100644 index 85977f424..000000000 --- a/pilot/pkg/xds/rds.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -type RdsGenerator struct { - Server *DiscoveryServer -} - -var _ model.XdsResourceGenerator = &RdsGenerator{} - -// Map of all configs that do not impact RDS -var skippedRdsConfigs = map[config.GroupVersionKind]struct{}{ - gvk.WorkloadEntry: {}, - gvk.WorkloadGroup: {}, - gvk.AuthorizationPolicy: {}, - gvk.RequestAuthentication: {}, - gvk.PeerAuthentication: {}, - gvk.Secret: {}, - gvk.WasmPlugin: {}, - gvk.Telemetry: {}, - gvk.ProxyConfig: {}, -} - -func rdsNeedsPush(req *model.PushRequest) bool { - if req == nil { - return true - } - if !req.Full { - // RDS only handles full push - return false - } - // If none set, we will always push - if len(req.ConfigsUpdated) == 0 { - return true - } - for config := range req.ConfigsUpdated { - if _, f := skippedRdsConfigs[config.Kind]; !f { - return true - } - } - return false -} - -func (c RdsGenerator) Generate(proxy *model.Proxy, w *model.WatchedResource, req *model.PushRequest) (model.Resources, model.XdsLogDetails, error) { - if !rdsNeedsPush(req) { - return nil, model.DefaultXdsLogDetails, nil - } - resources, logDetails := c.Server.ConfigGenerator.BuildHTTPRoutes(proxy, req, w.ResourceNames) - return resources, logDetails, nil -} diff --git a/pilot/pkg/xds/rds_test.go b/pilot/pkg/xds/rds_test.go deleted file mode 100644 index aa4f917e3..000000000 --- a/pilot/pkg/xds/rds_test.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package xds_test - -import ( - "fmt" - "testing" -) - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" -) - -func TestRDS(t *testing.T) { - tests := []struct { - name string - node string - routes []string - }{ - { - "sidecar_new", - sidecarID(app3Ip, "app3"), - []string{"80", "8080"}, - }, - { - "gateway_new", - gatewayID(gatewayIP), - []string{"http.80", "https.443.https.my-gateway.testns"}, - }, - { - // Even if we get a bad route, we should still send Envoy an empty response, rather than - // ignore it. If we ignore the route, the listeners can get stuck waiting forever. - "sidecar_badroute", - sidecarID(app3Ip, "app3"), - []string{"ht&p"}, - }, - } - - s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ads := s.ConnectADS().WithType(v3.RouteType).WithID(tt.node) - ads.RequestResponseAck(t, &discovery.DiscoveryRequest{ResourceNames: tt.routes}) - }) - } -} - -const ( - app3Ip = "10.2.0.1" - gatewayIP = "10.3.0.1" -) - -// Common code for the xds testing. -// The tests in this package use an in-process pilot using mock service registry and -// envoy. - -func sidecarID(ip, deployment string) string { // nolint: unparam - return fmt.Sprintf("sidecar~%s~%s-644fc65469-96dza.testns~testns.svc.cluster.local", ip, deployment) -} - -func gatewayID(ip string) string { //nolint: unparam - return fmt.Sprintf("router~%s~istio-gateway-644fc65469-96dzt.dubbo-system~dubbo-system.svc.cluster.local", ip) -} diff --git a/pilot/pkg/xds/requestidextension/context.go b/pilot/pkg/xds/requestidextension/context.go deleted file mode 100644 index 02ef1be6b..000000000 --- a/pilot/pkg/xds/requestidextension/context.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package requestidextension - -type UUIDRequestIDExtensionContext struct { - UseRequestIDForTraceSampling bool -} diff --git a/pilot/pkg/xds/requestidextension/uuid_extension.go b/pilot/pkg/xds/requestidextension/uuid_extension.go deleted file mode 100644 index 38cd8341d..000000000 --- a/pilot/pkg/xds/requestidextension/uuid_extension.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package requestidextension - -import ( - hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - uuid_extension "github.com/envoyproxy/go-control-plane/envoy/extensions/request_id/uuid/v3" - "google.golang.org/protobuf/types/known/wrapperspb" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" -) - -var UUIDRequestIDExtension = &hcm.RequestIDExtension{ - TypedConfig: util.MessageToAny(&uuid_extension.UuidRequestIdConfig{ - UseRequestIdForTraceSampling: &wrapperspb.BoolValue{ - Value: true, - }, - }), -} - -func BuildUUIDRequestIDExtension(ctx *UUIDRequestIDExtensionContext) *hcm.RequestIDExtension { - if ctx == nil { - return UUIDRequestIDExtension - } - return &hcm.RequestIDExtension{ - TypedConfig: util.MessageToAny(&uuid_extension.UuidRequestIdConfig{ - UseRequestIdForTraceSampling: &wrapperspb.BoolValue{ - Value: ctx.UseRequestIDForTraceSampling, - }, - }), - } -} diff --git a/pilot/pkg/xds/sds.go b/pilot/pkg/xds/sds.go deleted file mode 100644 index b86672001..000000000 --- a/pilot/pkg/xds/sds.go +++ /dev/null @@ -1,417 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "crypto/x509" - "encoding/pem" - "fmt" - "strings" - "time" -) - -import ( - cryptomb "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/private_key_providers/cryptomb/v3alpha" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - envoytls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "google.golang.org/protobuf/types/known/anypb" - "google.golang.org/protobuf/types/known/durationpb" - mesh "istio.io/api/mesh/v1alpha1" -) - -import ( - credscontroller "github.com/apache/dubbo-go-pixiu/pilot/pkg/credentials" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model/credentials" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - securitymodel "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/model" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" -) - -// SecretResource wraps the authnmodel type with cache functions implemented -type SecretResource struct { - credentials.SecretResource -} - -var _ model.XdsCacheEntry = SecretResource{} - -// DependentTypes is not needed; we know exactly which configs impact SDS, so we can scope at DependentConfigs level -func (sr SecretResource) DependentTypes() []config.GroupVersionKind { - return nil -} - -func (sr SecretResource) DependentConfigs() []model.ConfigKey { - return relatedConfigs(model.ConfigKey{Kind: gvk.Secret, Name: sr.Name, Namespace: sr.Namespace}) -} - -func (sr SecretResource) Cacheable() bool { - return true -} - -func sdsNeedsPush(updates model.XdsUpdates) bool { - if len(updates) == 0 { - return true - } - if len(model.ConfigNamesOfKind(updates, gvk.Secret)) > 0 { - return true - } - return false -} - -// parseResources parses a list of resource names to SecretResource types, for a given proxy. -// Invalid resource names are ignored -func (s *SecretGen) parseResources(names []string, proxy *model.Proxy) []SecretResource { - res := make([]SecretResource, 0, len(names)) - for _, resource := range names { - sr, err := credentials.ParseResourceName(resource, proxy.VerifiedIdentity.Namespace, proxy.Metadata.ClusterID, s.configCluster) - if err != nil { - pilotSDSCertificateErrors.Increment() - log.Warnf("error parsing resource name: %v", err) - continue - } - res = append(res, SecretResource{sr}) - } - return res -} - -func (s *SecretGen) Generate(proxy *model.Proxy, w *model.WatchedResource, req *model.PushRequest) (model.Resources, model.XdsLogDetails, error) { - if proxy.VerifiedIdentity == nil { - log.Warnf("proxy %s is not authorized to receive credscontroller. Ensure you are connecting over TLS port and are authenticated.", proxy.ID) - return nil, model.DefaultXdsLogDetails, nil - } - if req == nil || !sdsNeedsPush(req.ConfigsUpdated) { - return nil, model.DefaultXdsLogDetails, nil - } - var updatedSecrets map[model.ConfigKey]struct{} - if !req.Full { - updatedSecrets = model.ConfigsOfKind(req.ConfigsUpdated, gvk.Secret) - } - - // TODO: For the new gateway-api, we should always search the config namespace and stop reading across all clusters - proxyClusterSecrets, err := s.secrets.ForCluster(proxy.Metadata.ClusterID) - if err != nil { - log.Warnf("proxy %s is from an unknown cluster, cannot retrieve certificates: %v", proxy.ID, err) - pilotSDSCertificateErrors.Increment() - return nil, model.DefaultXdsLogDetails, nil - } - configClusterSecrets, err := s.secrets.ForCluster(s.configCluster) - if err != nil { - log.Warnf("config cluster %s not found, cannot retrieve certificates: %v", s.configCluster, err) - pilotSDSCertificateErrors.Increment() - return nil, model.DefaultXdsLogDetails, nil - } - - // Filter down to resources we can access. We do not return an error if they attempt to access a Secret - // they cannot; instead we just exclude it. This ensures that a single bad reference does not break the whole - // SDS flow. The pilotSDSCertificateErrors metric and logs handle visibility into invalid references. - resources := filterAuthorizedResources(s.parseResources(w.ResourceNames, proxy), proxy, proxyClusterSecrets) - - results := model.Resources{} - cached, regenerated := 0, 0 - for _, sr := range resources { - if updatedSecrets != nil { - if !containsAny(updatedSecrets, relatedConfigs(model.ConfigKey{Kind: gvk.Secret, Name: sr.Name, Namespace: sr.Namespace})) { - // This is an incremental update, filter out secrets that are not updated. - continue - } - } - - cachedItem, f := s.cache.Get(sr) - if f && !features.EnableUnsafeAssertions { - // If it is in the Cache, add it and continue - // We skip cache if assertions are enabled, so that the cache will assert our eviction logic is correct - results = append(results, cachedItem) - cached++ - continue - } - regenerated++ - res := s.generate(sr, configClusterSecrets, proxyClusterSecrets, proxy) - if res != nil { - s.cache.Add(sr, req, res) - results = append(results, res) - } - } - return results, model.XdsLogDetails{AdditionalInfo: fmt.Sprintf("cached:%v/%v", cached, cached+regenerated)}, nil -} - -func (s *SecretGen) generate(sr SecretResource, configClusterSecrets, proxyClusterSecrets credscontroller.Controller, proxy *model.Proxy) *discovery.Resource { - // Fetch the appropriate cluster's secret, based on the credential type - var secretController credscontroller.Controller - switch sr.Type { - case credentials.KubernetesGatewaySecretType: - secretController = configClusterSecrets - default: - secretController = proxyClusterSecrets - } - - isCAOnlySecret := strings.HasSuffix(sr.Name, securitymodel.SdsCaSuffix) - if isCAOnlySecret { - caCert, err := secretController.GetCaCert(sr.Name, sr.Namespace) - if err != nil { - pilotSDSCertificateErrors.Increment() - log.Warnf("failed to fetch ca certificate for %s: %v", sr.ResourceName, err) - return nil - } - if features.VerifySDSCertificate { - if err := validateCertificate(caCert); err != nil { - recordInvalidCertificate(sr.ResourceName, err) - return nil - } - } - res := toEnvoyCaSecret(sr.ResourceName, caCert) - return res - } - - key, cert, err := secretController.GetKeyAndCert(sr.Name, sr.Namespace) - if err != nil { - pilotSDSCertificateErrors.Increment() - log.Warnf("failed to fetch key and certificate for %s: %v", sr.ResourceName, err) - return nil - } - if features.VerifySDSCertificate { - if err := validateCertificate(cert); err != nil { - recordInvalidCertificate(sr.ResourceName, err) - return nil - } - } - res := toEnvoyKeyCertSecret(sr.ResourceName, key, cert, proxy, s.meshConfig) - return res -} - -func validateCertificate(data []byte) error { - block, _ := pem.Decode(data) - if block == nil { - return fmt.Errorf("pem decode failed") - } - _, err := x509.ParseCertificates(block.Bytes) - return err -} - -func recordInvalidCertificate(name string, err error) { - pilotSDSCertificateErrors.Increment() - log.Warnf("invalid certificates: %q: %v", name, err) -} - -// filterAuthorizedResources takes a list of SecretResource and filters out resources that proxy cannot access -func filterAuthorizedResources(resources []SecretResource, proxy *model.Proxy, secrets credscontroller.Controller) []SecretResource { - var authzResult *bool - var authzError error - // isAuthorized is a small wrapper around credscontroller.Authorize so we only call it once instead of each time in the loop - isAuthorized := func() bool { - if authzResult != nil { - return *authzResult - } - res := false - if err := secrets.Authorize(proxy.VerifiedIdentity.ServiceAccount, proxy.VerifiedIdentity.Namespace); err == nil { - res = true - } else { - authzError = err - } - authzResult = &res - return res - } - - // There are 4 cases of secret reference - // Verified cross namespace (by ReferencePolicy). No Authz needed. - // Verified same namespace (implicit). No Authz needed. - // Unverified cross namespace. Never allowed. - // Unverified same namespace. Allowed if authorized. - allowedResources := make([]SecretResource, 0, len(resources)) - deniedResources := make([]string, 0) - for _, r := range resources { - sameNamespace := r.Namespace == proxy.VerifiedIdentity.Namespace - verified := proxy.MergedGateway != nil && proxy.MergedGateway.VerifiedCertificateReferences.Contains(r.ResourceName) - switch r.Type { - case credentials.KubernetesGatewaySecretType: - // For KubernetesGateway, we only allow VerifiedCertificateReferences. - // This means a Secret in the same namespace as the Gateway (which also must be in the same namespace - // as the proxy), or a ReferencePolicy allowing the reference. - if verified { - allowedResources = append(allowedResources, r) - } else { - deniedResources = append(deniedResources, r.Name) - } - case credentials.KubernetesSecretType: - // For Kubernetes, we require the secret to be in the same namespace as the proxy and for it to be - // authorized for access. - if sameNamespace && isAuthorized() { - allowedResources = append(allowedResources, r) - } else { - deniedResources = append(deniedResources, r.Name) - } - default: - // Should never happen - log.Warnf("unknown credential type %q", r.Type) - pilotSDSCertificateErrors.Increment() - } - } - - // If we filtered any out, report an error. We aggregate errors in one place here, rather than in the loop, - // to avoid excessive logs. - if len(deniedResources) > 0 { - errMessage := authzError - if errMessage == nil { - errMessage = fmt.Errorf("cross namespace secret reference requires ReferencePolicy") - } - log.Warnf("proxy %s attempted to access unauthorized certificates %s: %v", proxy.ID, atMostNJoin(deniedResources, 3), errMessage) - pilotSDSCertificateErrors.Increment() - } - - return allowedResources -} - -func atMostNJoin(data []string, limit int) string { - if limit == 0 || limit == 1 { - // Assume limit >1, but make sure we dpn't crash if someone does pass those - return strings.Join(data, ", ") - } - if len(data) == 0 { - return "" - } - if len(data) < limit { - return strings.Join(data, ", ") - } - return strings.Join(data[:limit-1], ", ") + fmt.Sprintf(", and %d others", len(data)-limit+1) -} - -func toEnvoyCaSecret(name string, cert []byte) *discovery.Resource { - res := util.MessageToAny(&envoytls.Secret{ - Name: name, - Type: &envoytls.Secret_ValidationContext{ - ValidationContext: &envoytls.CertificateValidationContext{ - TrustedCa: &core.DataSource{ - Specifier: &core.DataSource_InlineBytes{ - InlineBytes: cert, - }, - }, - }, - }, - }) - return &discovery.Resource{ - Name: name, - Resource: res, - } -} - -func toEnvoyKeyCertSecret(name string, key, cert []byte, proxy *model.Proxy, meshConfig *mesh.MeshConfig) *discovery.Resource { - var res *anypb.Any - pkpConf := proxy.Metadata.ProxyConfigOrDefault(meshConfig.GetDefaultConfig()).GetPrivateKeyProvider() - switch pkpConf.GetProvider().(type) { - case *mesh.PrivateKeyProvider_Cryptomb: - crypto := pkpConf.GetCryptomb() - msg := util.MessageToAny(&cryptomb.CryptoMbPrivateKeyMethodConfig{ - PollDelay: durationpb.New(time.Duration(crypto.GetPollDelay().Nanos)), - PrivateKey: &core.DataSource{ - Specifier: &core.DataSource_InlineBytes{ - InlineBytes: key, - }, - }, - }) - res = util.MessageToAny(&envoytls.Secret{ - Name: name, - Type: &envoytls.Secret_TlsCertificate{ - TlsCertificate: &envoytls.TlsCertificate{ - CertificateChain: &core.DataSource{ - Specifier: &core.DataSource_InlineBytes{ - InlineBytes: cert, - }, - }, - PrivateKeyProvider: &envoytls.PrivateKeyProvider{ - ProviderName: "cryptomb", - ConfigType: &envoytls.PrivateKeyProvider_TypedConfig{ - TypedConfig: msg, - }, - }, - }, - }, - }) - default: - res = util.MessageToAny(&envoytls.Secret{ - Name: name, - Type: &envoytls.Secret_TlsCertificate{ - TlsCertificate: &envoytls.TlsCertificate{ - CertificateChain: &core.DataSource{ - Specifier: &core.DataSource_InlineBytes{ - InlineBytes: cert, - }, - }, - PrivateKey: &core.DataSource{ - Specifier: &core.DataSource_InlineBytes{ - InlineBytes: key, - }, - }, - }, - }, - }) - } - return &discovery.Resource{ - Name: name, - Resource: res, - } -} - -func containsAny(mp map[model.ConfigKey]struct{}, keys []model.ConfigKey) bool { - for _, k := range keys { - if _, f := mp[k]; f { - return true - } - } - return false -} - -// relatedConfigs maps a single resource to a list of relevant resources. This is used for cache invalidation -// and push skipping. This is because an secret potentially has a dependency on the same secret with or without -// the -cacert suffix. By including this dependency we ensure we do not miss any updates. -// This is important for cases where we have a compound secret. In this case, the `foo` secret may update, -// but we need to push both the `foo` and `foo-cacert` resource name, or they will fall out of sync. -func relatedConfigs(k model.ConfigKey) []model.ConfigKey { - related := []model.ConfigKey{k} - // For secret without -cacert suffix, add the suffix - if !strings.HasSuffix(k.Name, securitymodel.SdsCaSuffix) { - k.Name += securitymodel.SdsCaSuffix - related = append(related, k) - } else { - // For secret with -cacert suffix, remove the suffix - k.Name = strings.TrimSuffix(k.Name, securitymodel.SdsCaSuffix) - related = append(related, k) - } - return related -} - -type SecretGen struct { - secrets credscontroller.MulticlusterController - // Cache for XDS resources - cache model.XdsCache - configCluster cluster.ID - meshConfig *mesh.MeshConfig -} - -var _ model.XdsResourceGenerator = &SecretGen{} - -func NewSecretGen(sc credscontroller.MulticlusterController, cache model.XdsCache, configCluster cluster.ID, - meshConfig *mesh.MeshConfig) *SecretGen { - // TODO: Currently we only have a single credentials controller (Kubernetes). In the future, we will need a mapping - // of resource type to secret controller (ie kubernetes:// -> KubernetesController, vault:// -> VaultController) - return &SecretGen{ - secrets: sc, - cache: cache, - configCluster: configCluster, - meshConfig: meshConfig, - } -} diff --git a/pilot/pkg/xds/sds_test.go b/pilot/pkg/xds/sds_test.go deleted file mode 100644 index c67f084ad..000000000 --- a/pilot/pkg/xds/sds_test.go +++ /dev/null @@ -1,392 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "errors" - "fmt" - "os" - "path/filepath" - "strings" - "testing" - "time" -) - -import ( - "github.com/google/go-cmp/cmp" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes/fake" - k8stesting "k8s.io/client-go/testing" -) - -import ( - credentials "github.com/apache/dubbo-go-pixiu/pilot/pkg/credentials/kube" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/spiffe" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" -) - -func makeSecret(name string, data map[string]string) *corev1.Secret { - bdata := map[string][]byte{} - for k, v := range data { - bdata[k] = []byte(v) - } - return &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: "dubbo-system", - }, - Data: bdata, - } -} - -var ( - certDir = filepath.Join(env.IstioSrc, "./tests/testdata/certs") - genericCert = makeSecret("generic", map[string]string{ - credentials.GenericScrtCert: readFile(filepath.Join(certDir, "default/cert-chain.pem")), - credentials.GenericScrtKey: readFile(filepath.Join(certDir, "default/key.pem")), - }) - genericMtlsCert = makeSecret("generic-mtls", map[string]string{ - credentials.GenericScrtCert: readFile(filepath.Join(certDir, "dns/cert-chain.pem")), - credentials.GenericScrtKey: readFile(filepath.Join(certDir, "dns/key.pem")), - credentials.GenericScrtCaCert: readFile(filepath.Join(certDir, "dns/root-cert.pem")), - }) - genericMtlsCertSplit = makeSecret("generic-mtls-split", map[string]string{ - credentials.GenericScrtCert: readFile(filepath.Join(certDir, "mountedcerts-client/cert-chain.pem")), - credentials.GenericScrtKey: readFile(filepath.Join(certDir, "mountedcerts-client/key.pem")), - }) - genericMtlsCertSplitCa = makeSecret("generic-mtls-split-cacert", map[string]string{ - credentials.GenericScrtCaCert: readFile(filepath.Join(certDir, "mountedcerts-client/root-cert.pem")), - }) -) - -func readFile(name string) string { - cacert, _ := os.ReadFile(name) - return string(cacert) -} - -func TestGenerate(t *testing.T) { - type Expected struct { - Key string - Cert string - CaCert string - } - allResources := []string{ - "kubernetes://generic", "kubernetes://generic-mtls", "kubernetes://generic-mtls-cacert", - "kubernetes://generic-mtls-split", "kubernetes://generic-mtls-split-cacert", - } - cases := []struct { - name string - proxy *model.Proxy - resources []string - request *model.PushRequest - expect map[string]Expected - accessReviewResponse func(action k8stesting.Action) (bool, runtime.Object, error) - }{ - { - name: "simple", - proxy: &model.Proxy{VerifiedIdentity: &spiffe.Identity{Namespace: "dubbo-system"}, Type: model.Router}, - resources: []string{"kubernetes://generic"}, - request: &model.PushRequest{Full: true}, - expect: map[string]Expected{ - "kubernetes://generic": { - Key: string(genericCert.Data[credentials.GenericScrtKey]), - Cert: string(genericCert.Data[credentials.GenericScrtCert]), - }, - }, - }, - { - name: "sidecar", - proxy: &model.Proxy{VerifiedIdentity: &spiffe.Identity{Namespace: "dubbo-system"}}, - resources: []string{"kubernetes://generic"}, - request: &model.PushRequest{Full: true}, - expect: map[string]Expected{ - "kubernetes://generic": { - Key: string(genericCert.Data[credentials.GenericScrtKey]), - Cert: string(genericCert.Data[credentials.GenericScrtCert]), - }, - }, - }, - { - name: "unauthenticated", - proxy: &model.Proxy{Type: model.Router}, - resources: []string{"kubernetes://generic"}, - request: &model.PushRequest{Full: true}, - expect: map[string]Expected{}, - }, - { - name: "multiple", - proxy: &model.Proxy{VerifiedIdentity: &spiffe.Identity{Namespace: "dubbo-system"}, Type: model.Router}, - resources: allResources, - request: &model.PushRequest{Full: true}, - expect: map[string]Expected{ - "kubernetes://generic": { - Key: string(genericCert.Data[credentials.GenericScrtKey]), - Cert: string(genericCert.Data[credentials.GenericScrtCert]), - }, - "kubernetes://generic-mtls": { - Key: string(genericMtlsCert.Data[credentials.GenericScrtKey]), - Cert: string(genericMtlsCert.Data[credentials.GenericScrtCert]), - }, - "kubernetes://generic-mtls-cacert": { - CaCert: string(genericMtlsCert.Data[credentials.GenericScrtCaCert]), - }, - "kubernetes://generic-mtls-split": { - Key: string(genericMtlsCertSplit.Data[credentials.GenericScrtKey]), - Cert: string(genericMtlsCertSplit.Data[credentials.GenericScrtCert]), - }, - "kubernetes://generic-mtls-split-cacert": { - CaCert: string(genericMtlsCertSplitCa.Data[credentials.GenericScrtCaCert]), - }, - }, - }, - { - name: "full push with updates", - proxy: &model.Proxy{VerifiedIdentity: &spiffe.Identity{Namespace: "dubbo-system"}, Type: model.Router}, - resources: []string{"kubernetes://generic", "kubernetes://generic-mtls", "kubernetes://generic-mtls-cacert"}, - request: &model.PushRequest{Full: true, ConfigsUpdated: map[model.ConfigKey]struct{}{ - {Name: "generic-mtls", Namespace: "dubbo-system", Kind: gvk.Secret}: {}, - }}, - expect: map[string]Expected{ - "kubernetes://generic": { - Key: string(genericCert.Data[credentials.GenericScrtKey]), - Cert: string(genericCert.Data[credentials.GenericScrtCert]), - }, - "kubernetes://generic-mtls": { - Key: string(genericMtlsCert.Data[credentials.GenericScrtKey]), - Cert: string(genericMtlsCert.Data[credentials.GenericScrtCert]), - }, - "kubernetes://generic-mtls-cacert": { - CaCert: string(genericMtlsCert.Data[credentials.GenericScrtCaCert]), - }, - }, - }, - { - name: "incremental push with updates", - proxy: &model.Proxy{VerifiedIdentity: &spiffe.Identity{Namespace: "dubbo-system"}, Type: model.Router}, - resources: allResources, - request: &model.PushRequest{Full: false, ConfigsUpdated: map[model.ConfigKey]struct{}{ - {Name: "generic", Namespace: "dubbo-system", Kind: gvk.Secret}: {}, - }}, - expect: map[string]Expected{ - "kubernetes://generic": { - Key: string(genericCert.Data[credentials.GenericScrtKey]), - Cert: string(genericCert.Data[credentials.GenericScrtCert]), - }, - }, - }, - { - name: "incremental push with updates - mtls", - proxy: &model.Proxy{VerifiedIdentity: &spiffe.Identity{Namespace: "dubbo-system"}, Type: model.Router}, - resources: allResources, - request: &model.PushRequest{Full: false, ConfigsUpdated: map[model.ConfigKey]struct{}{ - {Name: "generic-mtls", Namespace: "dubbo-system", Kind: gvk.Secret}: {}, - }}, - expect: map[string]Expected{ - "kubernetes://generic-mtls": { - Key: string(genericMtlsCert.Data[credentials.GenericScrtKey]), - Cert: string(genericMtlsCert.Data[credentials.GenericScrtCert]), - }, - "kubernetes://generic-mtls-cacert": { - CaCert: string(genericMtlsCert.Data[credentials.GenericScrtCaCert]), - }, - }, - }, - { - name: "incremental push with updates - mtls split", - proxy: &model.Proxy{VerifiedIdentity: &spiffe.Identity{Namespace: "dubbo-system"}, Type: model.Router}, - resources: allResources, - request: &model.PushRequest{Full: false, ConfigsUpdated: map[model.ConfigKey]struct{}{ - {Name: "generic-mtls-split", Namespace: "dubbo-system", Kind: gvk.Secret}: {}, - }}, - expect: map[string]Expected{ - "kubernetes://generic-mtls-split": { - Key: string(genericMtlsCertSplit.Data[credentials.GenericScrtKey]), - Cert: string(genericMtlsCertSplit.Data[credentials.GenericScrtCert]), - }, - "kubernetes://generic-mtls-split-cacert": { - CaCert: string(genericMtlsCertSplitCa.Data[credentials.GenericScrtCaCert]), - }, - }, - }, - { - name: "incremental push with updates - mtls split ca update", - proxy: &model.Proxy{VerifiedIdentity: &spiffe.Identity{Namespace: "dubbo-system"}, Type: model.Router}, - resources: allResources, - request: &model.PushRequest{Full: false, ConfigsUpdated: map[model.ConfigKey]struct{}{ - {Name: "generic-mtls-split-cacert", Namespace: "dubbo-system", Kind: gvk.Secret}: {}, - }}, - expect: map[string]Expected{ - "kubernetes://generic-mtls-split": { - Key: string(genericMtlsCertSplit.Data[credentials.GenericScrtKey]), - Cert: string(genericMtlsCertSplit.Data[credentials.GenericScrtCert]), - }, - "kubernetes://generic-mtls-split-cacert": { - CaCert: string(genericMtlsCertSplitCa.Data[credentials.GenericScrtCaCert]), - }, - }, - }, - { - // If an unknown resource is request, we return all the ones we do know about - name: "unknown", - proxy: &model.Proxy{VerifiedIdentity: &spiffe.Identity{Namespace: "dubbo-system"}, Type: model.Router}, - resources: []string{"kubernetes://generic", "foo://invalid", "kubernetes://not-found", "default", "builtin://"}, - request: &model.PushRequest{Full: true}, - expect: map[string]Expected{ - "kubernetes://generic": { - Key: string(genericCert.Data[credentials.GenericScrtKey]), - Cert: string(genericCert.Data[credentials.GenericScrtCert]), - }, - }, - }, - { - // proxy without authorization - name: "unauthorized", - proxy: &model.Proxy{VerifiedIdentity: &spiffe.Identity{Namespace: "dubbo-system"}, Type: model.Router}, - resources: []string{"kubernetes://generic"}, - request: &model.PushRequest{Full: true}, - // Should get a response, but it will be empty - expect: map[string]Expected{}, - accessReviewResponse: func(action k8stesting.Action) (bool, runtime.Object, error) { - return true, nil, errors.New("not authorized") - }, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - if tt.proxy.Metadata == nil { - tt.proxy.Metadata = &model.NodeMetadata{} - } - tt.proxy.Metadata.ClusterID = "Kubernetes" - s := NewFakeDiscoveryServer(t, FakeOptions{ - KubernetesObjects: []runtime.Object{genericCert, genericMtlsCert, genericMtlsCertSplit, genericMtlsCertSplitCa}, - }) - cc := s.KubeClient().Kube().(*fake.Clientset) - - cc.Fake.Lock() - if tt.accessReviewResponse != nil { - cc.Fake.PrependReactor("create", "subjectaccessreviews", tt.accessReviewResponse) - } else { - credentials.DisableAuthorizationForTest(cc) - } - cc.Fake.Unlock() - - gen := s.Discovery.Generators[v3.SecretType] - tt.request.Start = time.Now() - secrets, _, _ := gen.Generate(s.SetupProxy(tt.proxy), &model.WatchedResource{ResourceNames: tt.resources}, tt.request) - raw := xdstest.ExtractTLSSecrets(t, model.ResourcesToAny(secrets)) - - got := map[string]Expected{} - for _, scrt := range raw { - got[scrt.Name] = Expected{ - Key: string(scrt.GetTlsCertificate().GetPrivateKey().GetInlineBytes()), - Cert: string(scrt.GetTlsCertificate().GetCertificateChain().GetInlineBytes()), - CaCert: string(scrt.GetValidationContext().GetTrustedCa().GetInlineBytes()), - } - } - if diff := cmp.Diff(got, tt.expect); diff != "" { - t.Fatal(diff) - } - }) - } -} - -// TestCaching ensures we don't have cross-proxy cache generation issues. This is split from TestGenerate -// since it is order dependant. -// Regression test for https://github.com/istio/istio/issues/33368 -func TestCaching(t *testing.T) { - s := NewFakeDiscoveryServer(t, FakeOptions{ - KubernetesObjects: []runtime.Object{genericCert}, - KubeClientModifier: func(c kube.Client) { - cc := c.Kube().(*fake.Clientset) - credentials.DisableAuthorizationForTest(cc) - }, - }) - gen := s.Discovery.Generators[v3.SecretType] - - fullPush := &model.PushRequest{Full: true, Start: time.Now()} - istiosystem := &model.Proxy{ - Metadata: &model.NodeMetadata{ClusterID: "Kubernetes"}, - VerifiedIdentity: &spiffe.Identity{Namespace: "dubbo-system"}, - Type: model.Router, - ConfigNamespace: "dubbo-system", - } - otherNamespace := &model.Proxy{ - Metadata: &model.NodeMetadata{ClusterID: "Kubernetes"}, - VerifiedIdentity: &spiffe.Identity{Namespace: "other-namespace"}, - Type: model.Router, - ConfigNamespace: "other-namespace", - } - - secrets, _, _ := gen.Generate(s.SetupProxy(istiosystem), &model.WatchedResource{ResourceNames: []string{"kubernetes://generic"}}, fullPush) - raw := xdstest.ExtractTLSSecrets(t, model.ResourcesToAny(secrets)) - if len(raw) != 1 { - t.Fatalf("failed to get expected secrets for authorized proxy: %v", raw) - } - - // We should not get secret returned, even though we are asking for the same one - secrets, _, _ = gen.Generate(s.SetupProxy(otherNamespace), &model.WatchedResource{ResourceNames: []string{"kubernetes://generic"}}, fullPush) - raw = xdstest.ExtractTLSSecrets(t, model.ResourcesToAny(secrets)) - if len(raw) != 0 { - t.Fatalf("failed to get expected secrets for unauthorized proxy: %v", raw) - } -} - -func TestAtMostNJoin(t *testing.T) { - tests := []struct { - data []string - limit int - want string - }{ - { - []string{"a", "b", "c"}, - 2, - "a, and 2 others", - }, - { - []string{"a", "b", "c"}, - 4, - "a, b, c", - }, - { - []string{"a", "b", "c"}, - 1, - "a, b, c", - }, - { - []string{"a", "b", "c"}, - 0, - "a, b, c", - }, - { - []string{}, - 3, - "", - }, - } - for _, tt := range tests { - t.Run(fmt.Sprintf("%s-%d", strings.Join(tt.data, "-"), tt.limit), func(t *testing.T) { - if got := atMostNJoin(tt.data, tt.limit); got != tt.want { - t.Errorf("got %v, want %v", got, tt.want) - } - }) - } -} diff --git a/pilot/pkg/xds/simple.go b/pilot/pkg/xds/simple.go deleted file mode 100644 index 4104014cb..000000000 --- a/pilot/pkg/xds/simple.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package xds - -import ( - "net" -) - -import ( - "google.golang.org/grpc" - "google.golang.org/grpc/reflection" -) - -import ( - configaggregate "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/aggregate" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/aggregate" - controllermemory "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/serviceentry" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// Server represents the XDS serving feature of Istiod (pilot). -// Unlike bootstrap/, this packet has no dependencies on K8S, CA, -// and other features. It'll be used initially in the istio-agent, -// to provide a minimal proxy while reusing the same code as istiod. -// Portions of the code will also be used in istiod - after it becomes -// stable the plan is to refactor bootstrap to use this code instead -// of directly bootstrapping XDS. -// -// The server support proxy/federation of multiple sources - last part -// or parity with MCP/Galley and MCP-over-XDS. -type SimpleServer struct { - // DiscoveryServer is the gRPC XDS implementation - // Env and MemRegistry are available as fields, as well as the default - // PushContext. - DiscoveryServer *DiscoveryServer - - // MemoryStore is an in-memory config store, part of the aggregate store - // used by the discovery server. - MemoryConfigStore model.ConfigStore - - // GRPCListener is the listener used for GRPC. For agent it is - // an insecure port, bound to 127.0.0.1 - GRPCListener net.Listener - - // syncCh is used for detecting if the stores have synced, - // which needs to happen before serving requests. - syncCh chan string - - ConfigStoreCache model.ConfigStoreController -} - -// Creates an basic, functional discovery server, using the same code as Istiod, but -// backed by an in-memory config and endpoint stores. -// -// Can be used in tests, or as a minimal XDS discovery server with no dependency on K8S or -// the complex bootstrap used by Istiod. A memory registry and memory config store are used to -// generate the configs - they can be programmatically updated. -func NewXDS(stop chan struct{}) *SimpleServer { - // Prepare a working XDS server, with aggregate config and registry stores and a memory store for each. - // TODO: refactor bootstrap code to use this server, and add more registries. - - env := &model.Environment{ - PushContext: model.NewPushContext(), - } - env.Watcher = mesh.NewFixedWatcher(mesh.DefaultMeshConfig()) - env.PushContext.Mesh = env.Watcher.Mesh() - env.Init() - - ds := NewDiscoveryServer(env, "istiod", map[string]string{}) - ds.InitGenerators(env, "dubbo-system") - ds.CachesSynced() - - // Config will have a fixed format: - // - aggregate store - // - one primary (local) memory config - // Additional stores can be added dynamically - for example by push or reference from a server. - // This is used to implement and test XDS federation (which is not yet final). - - // In-memory config store, controller and istioConfigStore - schemas := collections.Pilot - - store := memory.Make(schemas) - s := &SimpleServer{ - DiscoveryServer: ds, - } - s.syncCh = make(chan string, len(schemas.All())) - configController := memory.NewController(store) - s.MemoryConfigStore = model.MakeIstioStore(configController) - - // Endpoints/Clusters - using the config store for ServiceEntries - serviceControllers := aggregate.NewController(aggregate.Options{}) - - serviceEntryController := serviceentry.NewController(configController, s.MemoryConfigStore, ds) - serviceControllers.AddRegistry(serviceEntryController) - - sd := controllermemory.NewServiceDiscovery() - sd.EDSUpdater = ds - ds.MemRegistry = sd - serviceControllers.AddRegistry(serviceregistry.Simple{ - ProviderID: "Mem", - ServiceDiscovery: sd, - Controller: sd.Controller, - }) - env.ServiceDiscovery = serviceControllers - - go configController.Run(stop) - - // configStoreCache - with HasSync interface - aggregateConfigController, err := configaggregate.MakeCache([]model.ConfigStoreController{ - configController, - }) - if err != nil { - log.Fatalf("Creating aggregate config: %v", err) - } - - // TODO: fix the mess of store interfaces - most are too generic for their own good. - s.ConfigStoreCache = aggregateConfigController - env.ConfigStore = model.MakeIstioStore(aggregateConfigController) - - return s -} - -func (s *SimpleServer) StartGRPC(addr string) (string, error) { - lis, err := net.Listen("tcp", addr) - if err != nil { - return "", err - } - gs := grpc.NewServer() - s.DiscoveryServer.Register(gs) - reflection.Register(gs) - s.GRPCListener = lis - go func() { - err = gs.Serve(lis) - if err != nil { - log.Infof("Serve done with %v", err) - } - }() - return lis.Addr().String(), nil -} diff --git a/pilot/pkg/xds/statusgen.go b/pilot/pkg/xds/statusgen.go deleted file mode 100644 index ae741f0ff..000000000 --- a/pilot/pkg/xds/statusgen.go +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "fmt" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - status "github.com/envoyproxy/go-control-plane/envoy/service/status/v3" - "google.golang.org/protobuf/proto" - any "google.golang.org/protobuf/types/known/anypb" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" -) - -const ( - // TypeURLConnect generate connect event. - TypeURLConnect = "istio.io/connect" - - // TypeURLDisconnect generate disconnect event. - TypeURLDisconnect = "istio.io/disconnect" - - // TypeURLNACK will receive messages of type DiscoveryRequest, containing - // the 'NACK' from envoy on rejected configs. Only ID is set in metadata. - // This includes all the info that envoy (client) provides. - TypeURLNACK = "istio.io/nack" - - // TypeDebugSyncronization requests Envoy CSDS for proxy sync status - TypeDebugSyncronization = "istio.io/debug/syncz" - - // TypeDebugConfigDump requests Envoy configuration for a proxy without creating one - TypeDebugConfigDump = "istio.io/debug/config_dump" - - // TODO: TypeURLReady - readiness events for endpoints, agent can propagate -) - -// StatusGen is a Generator for XDS status: connections, syncz, configdump -type StatusGen struct { - Server *DiscoveryServer - - // TODO: track last N Nacks and connection events, with 'version' based on timestamp. - // On new connect, use version to send recent events since last update. -} - -func NewStatusGen(s *DiscoveryServer) *StatusGen { - return &StatusGen{ - Server: s, - } -} - -// Generate XDS responses about internal events: -// - connection status -// - NACKs -// We can also expose ACKS. -func (sg *StatusGen) Generate(proxy *model.Proxy, w *model.WatchedResource, req *model.PushRequest) (model.Resources, model.XdsLogDetails, error) { - res := model.Resources{} - - switch w.TypeUrl { - case TypeURLConnect: - for _, v := range sg.Server.Clients() { - res = append(res, &discovery.Resource{ - Name: v.node.Id, - Resource: util.MessageToAny(v.node), - }) - } - case TypeDebugSyncronization: - res = sg.debugSyncz() - case TypeDebugConfigDump: - if len(w.ResourceNames) == 0 || len(w.ResourceNames) > 1 { - // Malformed request from client - log.Infof("%s with %d ResourceNames", TypeDebugConfigDump, len(w.ResourceNames)) - break - } - var err error - res, err = sg.debugConfigDump(w.ResourceNames[0]) - if err != nil { - log.Infof("%s failed: %v", TypeDebugConfigDump, err) - break - } - } - return res, model.DefaultXdsLogDetails, nil -} - -// isSidecar ad-hoc method to see if connection represents a sidecar -func isProxy(con *Connection) bool { - return con != nil && - con.proxy != nil && - con.proxy.Metadata != nil && - con.proxy.Metadata.ProxyConfig != nil -} - -func (sg *StatusGen) debugSyncz() model.Resources { - res := model.Resources{} - - stypes := []string{ - v3.ListenerType, - v3.RouteType, - v3.EndpointType, - v3.ClusterType, - v3.ExtensionConfigurationType, - } - - for _, con := range sg.Server.Clients() { - con.proxy.RLock() - // Skip "nodes" without metdata (they are probably istioctl queries!) - if isProxy(con) { - xdsConfigs := make([]*status.ClientConfig_GenericXdsConfig, 0) - for _, stype := range stypes { - pxc := &status.ClientConfig_GenericXdsConfig{} - if watchedResource, ok := con.proxy.WatchedResources[stype]; ok { - pxc.ConfigStatus = debugSyncStatus(watchedResource) - } else { - pxc.ConfigStatus = status.ConfigStatus_NOT_SENT - } - - pxc.TypeUrl = stype - - xdsConfigs = append(xdsConfigs, pxc) - } - clientConfig := &status.ClientConfig{ - Node: &core.Node{ - Id: con.proxy.ID, - Metadata: con.proxy.Metadata.ToStruct(), - }, - GenericXdsConfigs: xdsConfigs, - } - res = append(res, &discovery.Resource{ - Name: clientConfig.Node.Id, - Resource: util.MessageToAny(clientConfig), - }) - } - con.proxy.RUnlock() - } - - return res -} - -func debugSyncStatus(wr *model.WatchedResource) status.ConfigStatus { - if wr.NonceSent == "" { - return status.ConfigStatus_NOT_SENT - } - if wr.NonceAcked == wr.NonceSent { - return status.ConfigStatus_SYNCED - } - return status.ConfigStatus_STALE -} - -func (sg *StatusGen) debugConfigDump(proxyID string) (model.Resources, error) { - conn := sg.Server.getProxyConnection(proxyID) - if conn == nil { - // This is "like" a 404. The error is the client's. However, this endpoint - // only tracks a single "shard" of connections. The client may try another instance. - return nil, fmt.Errorf("config dump could not find connection for proxyID %q", proxyID) - } - - dump, err := sg.Server.configDump(conn) - if err != nil { - return nil, err - } - - return model.AnyToUnnamedResources(dump.Configs), nil -} - -func (sg *StatusGen) OnConnect(con *Connection) { - sg.pushStatusEvent(TypeURLConnect, []proto.Message{con.node}) -} - -func (sg *StatusGen) OnDisconnect(con *Connection) { - sg.pushStatusEvent(TypeURLDisconnect, []proto.Message{con.node}) -} - -func (sg *StatusGen) OnNack(node *model.Proxy, dr *discovery.DiscoveryRequest) { - // Make sure we include the ID - the DR may not include metadata - if dr.Node == nil { - dr.Node = &core.Node{} - } - dr.Node.Id = node.ID - sg.pushStatusEvent(TypeURLNACK, []proto.Message{dr}) -} - -// pushStatusEvent is similar with DiscoveryServer.pushStatusEvent() - but called directly, -// since status discovery is not driven by config change events. -// We also want connection events to be dispatched as soon as possible, -// they may be consumed by other instances of Istiod to update internal state. -func (sg *StatusGen) pushStatusEvent(typeURL string, data []proto.Message) { - clients := sg.Server.ClientsOf(typeURL) - if len(clients) == 0 { - return - } - - resources := make([]*any.Any, 0, len(data)) - for _, v := range data { - resources = append(resources, util.MessageToAny(v)) - } - dr := &discovery.DiscoveryResponse{ - TypeUrl: typeURL, - Resources: resources, - } - - sg.Server.SendResponse(clients, dr) -} diff --git a/pilot/pkg/xds/testdata/benchmarks/authorizationpolicy.yaml b/pilot/pkg/xds/testdata/benchmarks/authorizationpolicy.yaml deleted file mode 100644 index d0a2a73ec..000000000 --- a/pilot/pkg/xds/testdata/benchmarks/authorizationpolicy.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# Set up a Service associated with our proxy, which will run as 1.1.1.1 IP -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: proxy-service-instance -spec: - hosts: - - example.com - ports: - - number: 80 - name: http - protocol: HTTP - - number: 443 - name: https - protocol: HTTPS - resolution: STATIC - endpoints: - - address: 1.1.1.1 - labels: - istio.io/benchmark: "true" ---- - {{- range $i := until .Services }} -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: authn-{{$i}} -spec: - action: DENY - rules: - - from: - - source: - namespaces: ["default"] - to: - - operation: - methods: ["POST"] ---- - {{- end }} diff --git a/pilot/pkg/xds/testdata/benchmarks/empty.yaml b/pilot/pkg/xds/testdata/benchmarks/empty.yaml deleted file mode 100644 index 776b91e4f..000000000 --- a/pilot/pkg/xds/testdata/benchmarks/empty.yaml +++ /dev/null @@ -1,57 +0,0 @@ -# Set up a Service associated with our proxy, which will run as 1.1.1.1 IP -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: proxy-service-instance -spec: - hosts: - - example.com - ports: - - number: 80 - name: http - protocol: HTTP - - number: 7070 - name: tcp - protocol: TCP - - number: 443 - name: https - protocol: HTTPS - - number: 9090 - name: auto - protocol: "" - resolution: STATIC - location: MESH_INTERNAL - endpoints: - - address: 1.1.1.1 - labels: - security.istio.io/tlsMode: istio ---- -# Set up .Services number of services. Each will have 4 ports (one for each protocol) -{{- range $i := until .Services }} -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-{{$i}} -spec: - hosts: - - random-{{$i}}.host.example - ports: - - number: 80 - name: http - protocol: HTTP - - number: 7070 - name: tcp - protocol: TCP - - number: 443 - name: https - protocol: HTTPS - - number: 9090 - name: auto - resolution: STATIC - location: MESH_INTERNAL - endpoints: - - address: 1.2.3.4 - labels: - security.istio.io/tlsMode: istio ---- -{{- end }} \ No newline at end of file diff --git a/pilot/pkg/xds/testdata/benchmarks/gateways-shared.yaml b/pilot/pkg/xds/testdata/benchmarks/gateways-shared.yaml deleted file mode 100644 index ccd4936b5..000000000 --- a/pilot/pkg/xds/testdata/benchmarks/gateways-shared.yaml +++ /dev/null @@ -1,96 +0,0 @@ -# Set up a Service associated with our proxy, which will run as 1.1.1.1 IP -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: proxy-service-instance -spec: - hosts: - - example.com - ports: - - number: 80 - name: http - protocol: HTTP - - number: 443 - name: https - protocol: HTTPS - resolution: STATIC - endpoints: - - address: 1.1.1.1 - labels: - istio.io/benchmark: "true" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: gateway - namespace: gateway -spec: - selector: - istio.io/benchmark: "true" - servers: - - port: - number: 80 - name: http - protocol: HTTP - hosts: - - random-1.host.example - - random-2.host.example - - random-3.host.example - - port: - number: 443 - name: https - protocol: HTTPS - hosts: - - random-1.host.example - - random-2.host.example - - random-3.host.example - tls: - mode: ISTIO_MUTUAL ---- -{{- range $i := until .Services }} -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: vs-{{$i}} - namespace: gateway -spec: - hosts: - - random-1.host.example - - random-2.host.example - - random-3.host.example - gateways: - - gateway/gateway - http: - - match: - - uri: - prefix: "/route-a-{{$i}}" - - uri: - prefix: "/route-b-{{$i}}" - - uri: - prefix: "/route-c-{{$i}}" - - uri: - prefix: "/route-d-{{$i}}" - - uri: - prefix: "/route-e-{{$i}}" - - uri: - prefix: "/route-f-{{$i}}" - route: - - destination: - host: random-{{$i}}.host.example ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-{{$i}} -spec: - hosts: - - random-{{$i}}.host.example - ports: - - number: 80 - name: http - protocol: HTTP - resolution: STATIC - endpoints: - - address: 1.2.3.4 ---- -{{- end }} \ No newline at end of file diff --git a/pilot/pkg/xds/testdata/benchmarks/gateways.yaml b/pilot/pkg/xds/testdata/benchmarks/gateways.yaml deleted file mode 100644 index 7ea527128..000000000 --- a/pilot/pkg/xds/testdata/benchmarks/gateways.yaml +++ /dev/null @@ -1,86 +0,0 @@ -# Set up a Service associated with our proxy, which will run as 1.1.1.1 IP -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: proxy-service-instance -spec: - hosts: - - example.com - ports: - - number: 80 - name: http - protocol: HTTP - - number: 443 - name: https - protocol: HTTPS - resolution: STATIC - endpoints: - - address: 1.1.1.1 - labels: - istio.io/benchmark: "true" ---- -{{- range $i := until .Services }} -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: vs-{{$i}} - namespace: gateway -spec: - hosts: - - random-a-{{$i}}.host.example - - random-b-{{$i}}.host.example - - random-c-{{$i}}.host.example - gateways: - - gateway/gateway-{{$i}} - http: - - match: - - uri: - prefix: "/route-{{$i}}" - route: - - destination: - host: random-{{$i}}.host.example ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: gateway-{{$i}} - namespace: gateway -spec: - selector: - istio.io/benchmark: "true" - servers: - - port: - number: 80 - name: http - protocol: HTTP - hosts: - - random-a-{{$i}}.host.example - - random-b-{{$i}}.host.example - - random-c-{{$i}}.host.example - - port: - number: 443 - name: https - protocol: HTTPS - hosts: - - random-a-{{$i}}.host.example - - random-b-{{$i}}.host.example - - random-c-{{$i}}.host.example - tls: - mode: ISTIO_MUTUAL ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-{{$i}} -spec: - hosts: - - random-{{$i}}.host.example - ports: - - number: 80 - name: http - protocol: HTTP - resolution: STATIC - endpoints: - - address: 1.2.3.4 ---- -{{- end }} \ No newline at end of file diff --git a/pilot/pkg/xds/testdata/benchmarks/knative-gateway.yaml b/pilot/pkg/xds/testdata/benchmarks/knative-gateway.yaml deleted file mode 100644 index 48279ab0e..000000000 --- a/pilot/pkg/xds/testdata/benchmarks/knative-gateway.yaml +++ /dev/null @@ -1,220 +0,0 @@ -# Simulate the same configuration knative would generate from some basic KServices -# Set up a Service associated with our proxy, which will run as 1.1.1.1 IP -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: external - namespace: dubbo-system -spec: - hosts: - - istio-ingressgateway.dubbo-system.svc.cluster.local - ports: - - number: 80 - targetPort: 8080 - name: http - protocol: HTTP - resolution: STATIC - endpoints: - - address: 1.1.1.1 - labels: - istio.io/benchmark: "true" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: internal - namespace: dubbo-system -spec: - hosts: - - knative-local-gateway.dubbo-system.svc.cluster.local - ports: - - number: 80 - targetPort: 8081 - name: http - protocol: HTTP - resolution: STATIC - endpoints: - - address: 1.1.1.1 - labels: - istio.io/benchmark: "true" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: knative-ingress-gateway - namespace: knative-serving -spec: - selector: - istio.io/benchmark: "true" - servers: - - hosts: - - '*' - port: - name: http - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: knative-local-gateway - namespace: knative-serving -spec: - selector: - istio.io/benchmark: "true" - servers: - - hosts: - - '*' - port: - name: http - number: 8081 - protocol: HTTP ---- -{{- range $i := until .Services }} -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: hello-ingress-{{$i}} - namespace: default -spec: - gateways: - - knative-serving/knative-ingress-gateway - - knative-serving/knative-local-gateway - hosts: - - hello.default - - hello.default.external.domain - - hello.default.svc - - hello.default.svc.cluster.local - http: - - headers: - request: - set: - K-Network-Hash: 0647dfaebda7111f09cd1ee30dfb4cbdf540bcd47575c5f948106757b7110384 - match: - - authority: - prefix: hello.default - gateways: - - knative-serving/knative-local-gateway - headers: - K-Network-Hash: - exact: override - route: - - destination: - host: hello-{{$i}}.default.svc.cluster.local - port: - number: 80 - headers: - request: - set: - Knative-Serving-Namespace: default - Knative-Serving-Revision: hello-{{$i}} - weight: 100 - - match: - - authority: - prefix: hello.default - gateways: - - knative-serving/knative-local-gateway - route: - - destination: - host: hello-{{$i}}.default.svc.cluster.local - port: - number: 80 - headers: - request: - set: - Knative-Serving-Namespace: default - Knative-Serving-Revision: hello-{{$i}} - weight: 100 - - headers: - request: - set: - K-Network-Hash: 0647dfaebda7111f09cd1ee30dfb4cbdf540bcd47575c5f948106757b7110384 - match: - - authority: - prefix: hello.default.external.domain - gateways: - - knative-serving/knative-ingress-gateway - headers: - K-Network-Hash: - exact: override - route: - - destination: - host: hello-{{$i}}.default.svc.cluster.local - port: - number: 80 - headers: - request: - set: - Knative-Serving-Namespace: default - Knative-Serving-Revision: hello-{{$i}} - weight: 100 - - match: - - authority: - prefix: hello.default.external.domain - gateways: - - knative-serving/knative-ingress-gateway - route: - - destination: - host: hello-{{$i}}.default.svc.cluster.local - port: - number: 80 - headers: - request: - set: - Knative-Serving-Namespace: default - Knative-Serving-Revision: hello-{{$i}} - weight: 100 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: hello-private-ingress-{{$i}} - namespace: default -spec: - gateways: - - knative-serving/knative-local-gateway - hosts: - - hello-private.default - - hello-private.default.svc - - hello-private.default.svc.cluster.local - http: - - headers: - request: - set: - K-Network-Hash: 1235d057c5abf876f0b1fa3cb9e5d04730d98fb236badc4705aecc1159309b2b - match: - - authority: - prefix: hello-private.default - gateways: - - knative-serving/knative-local-gateway - headers: - K-Network-Hash: - exact: override - route: - - destination: - host: hello-private-{{$i}}.default.svc.cluster.local - port: - number: 80 - headers: - request: - set: - Knative-Serving-Namespace: default - Knative-Serving-Revision: hello-private-{{$i}} - weight: 100 - - match: - - authority: - prefix: hello-private.default - gateways: - - knative-serving/knative-local-gateway - route: - - destination: - host: hello-private-{{$i}}.default.svc.cluster.local - port: - number: 80 - headers: - request: - set: - Knative-Serving-Namespace: default - Knative-Serving-Revision: hello-private-{{$i}} - weight: 100 -{{- end }} diff --git a/pilot/pkg/xds/testdata/benchmarks/peerauthentication.yaml b/pilot/pkg/xds/testdata/benchmarks/peerauthentication.yaml deleted file mode 100644 index f8a904b69..000000000 --- a/pilot/pkg/xds/testdata/benchmarks/peerauthentication.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# Set up a Service associated with our proxy, which will run as 1.1.1.1 IP -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: proxy-service-instance -spec: - hosts: - - example.com - ports: - - number: 80 - name: http - protocol: HTTP - - number: 443 - name: https - protocol: HTTPS - resolution: STATIC - endpoints: - - address: 1.1.1.1 - labels: - istio.io/benchmark: "true" ---- - {{- range $i := until .Services }} -apiVersion: security.istio.io/v1beta1 -kind: PeerAuthentication -metadata: - name: authz-{{$i}} -spec: - mtls: - mode: STRICT ---- - {{- end }} diff --git a/pilot/pkg/xds/testdata/benchmarks/secrets.yaml b/pilot/pkg/xds/testdata/benchmarks/secrets.yaml deleted file mode 100644 index c84f17f6c..000000000 --- a/pilot/pkg/xds/testdata/benchmarks/secrets.yaml +++ /dev/null @@ -1,12 +0,0 @@ -{{- range $i := until .Services }} -apiVersion: v1 -kind: Secret -metadata: - name: sds-credential-{{$i}} - namespace: default -type: kubernetes.io/tls -data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURrVENDQW5tZ0F3SUJBZ0lKQU5tdzVmRUlJS3lVTUEwR0NTcUdTSWIzRFFFQkN3VUFNRjh4Q3pBSkJnTlYKQkFZVEFrRlZNUk13RVFZRFZRUUlEQXBUYjIxbExWTjBZWFJsTVNFd0h3WURWUVFLREJoSmJuUmxjbTVsZENCWAphV1JuYVhSeklGQjBlU0JNZEdReEdEQVdCZ05WQkFNTUQyRndhUzVqYjIxd1lXNTVMbU52YlRBZUZ3MHhOekE0Ck1EWXlNVEkwTXpKYUZ3MHlOekE0TURReU1USTBNekphTUY4eEN6QUpCZ05WQkFZVEFrRlZNUk13RVFZRFZRUUkKREFwVGIyMWxMVk4wWVhSbE1TRXdId1lEVlFRS0RCaEpiblJsY201bGRDQlhhV1JuYVhSeklGQjBlU0JNZEdReApHREFXQmdOVkJBTU1EMkZ3YVM1amIyMXdZVzU1TG1OdmJUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQCkFEQ0NBUW9DZ2dFQkFNODNjb2JEOWsrazg1UlRkSTFxQ1pxanBGSnpkV1oxR3Zib1BNVUttOFpHZmVjZEFuU2kKUjVHODVZUnhhS0c5RFVxL3kwaHR6YmlpNVdLaFE2eDN4TmtybDhYaVdROHhCbHdjdzljL3ZXS3hMem9jcURTTgpCTktIOWVHcjNRbFZ3MkI5OE5rT0IxRDlIVHNSb2pHTFJnZElRUkgrREJEOWdteVlsUGJybWZKZXhqUzAvbmp5CkRLb0h5YzlHdlZ0UEFTMFFVZ2NNUllYenhWMnloSXFsTzNWQ0M1RlRHVmZxMWp2SGMvMmJoZ3VhTDJhYXNNMU8KNVF4K2VPbE43Ynh5ZWo3cmN4bXlnTGVqUHpWWWpkdlQzemNtOTBSREx4ck50SHo2NDl2ZGl4bGszNkJmL1ZpRAp1S3F3QlZtU2pESkFhT1FxUFZtMWtPa0VpcGowU3N4V3I0a0NBd0VBQWFOUU1FNHdIUVlEVlIwT0JCWUVGSUgzCm44T3hzV2IrWFg3Y2dDcXIyVkE4YzgvMU1COEdBMVVkSXdRWU1CYUFGSUgzbjhPeHNXYitYWDdjZ0NxcjJWQTgKYzgvMU1Bd0dBMVVkRXdRRk1BTUJBZjh3RFFZSktvWklodmNOQVFFTEJRQURnZ0VCQUJ1QzBJcnhkTDRaemNEawpEZktJYU9OTXdlTTk1cmoxaWViWmU5Vm13WmZUeGl4S0djVG1LNEpZTUM0bmZHY2g0Ny8rSFVrcU9PQXZjWEJVCmpzaU9kbW5mOU9jNmtWdjc5RndzQzVzYUlwOWZCRXE3OHR6bnNnOEdNT3R6c29nY3VPMEFONkxaUWJYQTR4dnMKNGZ6MDUwVDkwTW5MQTFkNWtCTUZGOFAyMU5MRWZNSy80bndxV3FoRkVGZ3Q2ajFVWklFUllSVGxkSm9CR2tpUAp6ZGova2tBTnhwTnlxWXhMbkJBZUdXbzV0cWpBeU1jcjZyQXoyZmh3Nm1rMEltaUxjM09MWmcxYm1oY0VBekNVCitKZENIekhrS3pDdUVBekNScHdZZFM3Yy9BQUwxSGFZaGlldkNQZnpmUmpNTU0vVzkreUJJdFRMeU0vTVlVbVAKMnRvVDVuOD0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= - tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBenpkeWhzUDJUNlR6bEZOMGpXb0ptcU9rVW5OMVpuVWE5dWc4eFFxYnhrWjk1eDBDCmRLSkhrYnpsaEhGb29iME5Tci9MU0czTnVLTGxZcUZEckhmRTJTdVh4ZUpaRHpFR1hCekQxeis5WXJFdk9oeW8KTkkwRTBvZjE0YXZkQ1ZYRFlIM3cyUTRIVVAwZE94R2lNWXRHQjBoQkVmNE1FUDJDYkppVTl1dVo4bDdHTkxUKwplUElNcWdmSnowYTlXMDhCTFJCU0J3eEZoZlBGWGJLRWlxVTdkVUlMa1ZNWlYrcldPOGR6L1p1R0M1b3ZacHF3CnpVN2xESDU0NlUzdHZISjZQdXR6R2JLQXQ2TS9OVmlOMjlQZk55YjNSRU12R3MyMGZQcmoyOTJMR1dUZm9GLzkKV0lPNHFyQUZXWktNTWtCbzVDbzlXYldRNlFTS21QUkt6RmF2aVFJREFRQUJBb0lCQUNSUjVLb0lhUURXdWJieQoxY2Yvb1FWUXozbUFNVUN2SC9YTkNQSEVoVDlBbGNyUGcrR3JtLzNJYlRaRXBvRks0S3lNWjNZZmdPSnU4dVBSCnZrblppRkJFV3NyZGZKeTBEQmhURm1TQkVKSGUycGRGOUptWmFoSDRzTGxJWldyQWRJbFNLY2Z4dElpV2hPd1kKa0NRODlCNU1wTk1oZ3ozcklWUWxmbDYxTnZ1TEhrbmw2c3krMGw0OUMyWnptSit0YWNoTUFpUDllUmRQdXA0MgprM1hXZ3pFOUd6RVdieTd0ek1jY1p6OXFoT1lWcGM1eW15RmFrdVB1WEs0TWpNRHRwMzBxQm91QU53a2tWSTFqCjFDUlY4TlJKU0h2d0RUSHJZNk5IRUdUTFh6S2hIbVZyY1h1K0dQSmNIMUNNVUllYnJqQWRoV0VDYXdhUU8wM2MKUnNWTTZYMENnWUVBN3JkUDFaS3JVeWlSaVdwaC82YmdCMUNYSzM2Q0t2QnU1QUNIUXVGNml6MU9aTTNOb2FLMgprdFE2L2VSd1FialA4eVpIRGFxL2VoeTk5K3RMTnJ1TEIzT0RRbG03bGZkdEZKWmZ1YXM3aGJjSXBIK1BUSWZ2CmpXTjYxNzc4TlRmd3dTQ2x5OVcyRGtkbDNKcGlvR0xiWUVwMzhzZGJtUWVDaWxaWmM2NkR3MU1DZ1lFQTNqaEkKRDVjT3lMbkt1VWp5WnBVZTZndHBrQlNHM3R5Nkg1RE5MQWk2NE9XWkhhZlQvMlpLSWVrWDhSbGY4YklNL0FzQwpYby9LY3JQL1ZrSzhWd0c5NjVFWVhrVEFEeDBkTU16cTEvbUw5SE5OTEhuYXFPdVpEYk52eVNzcnVmMVpRNHgwCnkvaTB2QUdFNlpod28xd2dYL0dxZ0tBM3g5cldxekUxY21wTFlqTUNnWUE1elY5aWFxSmJmMzVHRk9GbjR3TnEKSWdTSXZwaE1SMjNDZmJKQzZwQWV1UmlMWmgzOW5vV3c1ZnptejNLekowb0xLV0NaR1poRnZFSHZqeVRtT3VFKwpTNlVqNHRCK1RxdzJDUGRpNE9pSHh6c3JnY3UwRDFKZEhSSjR2VUVhcmRINUlhdWp3THJWbUVvODhaRWlIdTNaCjBnNWJWaFNDNklPZWRhd3hTN2VTQ3dLQmdRRFY0cWdCVVd5cWFLRWwrMzlNbTBVWkVnajE0N3Y0cjh6NWF0Ny8KL2hzWk1nUlJGZU1uMU9XUGhCSkdQaDBwdmkxZlBwMTJOTUl2NnUzZHNmZ2phb3JKUEd1TytHOC9YTTltMUNWSgo0V2dDemlPK3BqNS9EZHpQNGlDN0tMRTZvQTRWeFEvNTd4VE9URXdJcG0vcjNGVlE0NE12c0laZjkxTmRqTXliCnBwR09Id0tCZ1FDQTE2NUhpaE5zUVVITlJnUmtmR292WENEc0UyNllpRnFyYjNVYzJPOEhodEk1M2MzZ0N6NTQKT25uZ2x2YzlXVGNrV2RHdHBTTWJjdHRPTjJrYUFvTTVwcUJyMll0dWJ4MDZ2V04vcDVva0JhcWJWTkFNWVZsNQpqY2Q5R2xEeWdGLytEZlhEQUNoYTJrOURzRDBqSWJOMW9BSVk0SGFzOE12dTlSdWdmdHFSYlE9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo= ---- -{{- end }} diff --git a/pilot/pkg/xds/testdata/benchmarks/serviceentry-workloadentry.yaml b/pilot/pkg/xds/testdata/benchmarks/serviceentry-workloadentry.yaml deleted file mode 100644 index 6c9595ad2..000000000 --- a/pilot/pkg/xds/testdata/benchmarks/serviceentry-workloadentry.yaml +++ /dev/null @@ -1,71 +0,0 @@ -# Set up a Service associated with our proxy, which will run as 1.1.1.1 IP -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: proxy-service-instance -spec: - hosts: - - example.com - ports: - - number: 80 - name: http - protocol: HTTP - - number: 7070 - name: tcp - protocol: TCP - - number: 443 - name: https - protocol: HTTPS - - number: 9090 - name: auto - protocol: "" - resolution: STATIC - location: MESH_INTERNAL - endpoints: - - address: 1.1.1.1 - labels: - security.istio.io/tlsMode: istio ---- -# Set up .Services number of services. Each will have 4 ports (one for each protocol) -{{- range $i := until .Services }} -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-{{$i}} -spec: - hosts: - - random-{{$i}}.host.example - ports: - - number: 80 - name: http - protocol: HTTP - - number: 7070 - name: tcp - protocol: TCP - - number: 443 - name: https - protocol: HTTPS - - number: 9090 - name: auto - resolution: STATIC - location: MESH_INTERNAL - workloadSelector: - labels: - app: random-{{$i}} - ---- -{{- end }} - ---- -{{- range $j := until .Instances }} -apiVersion: networking.istio.io/v1alpha3 -kind: WorkloadEntry -metadata: - name: random-{{$j}} -spec: - serviceAccount: random - address: 10.10.10.10 - labels: - app: random-{{mod $j $.Services}} ---- -{{- end }} \ No newline at end of file diff --git a/pilot/pkg/xds/testdata/benchmarks/telemetry-api.yaml b/pilot/pkg/xds/testdata/benchmarks/telemetry-api.yaml deleted file mode 100644 index 691a0f5d0..000000000 --- a/pilot/pkg/xds/testdata/benchmarks/telemetry-api.yaml +++ /dev/null @@ -1,61 +0,0 @@ -apiVersion: telemetry.istio.io/v1alpha1 -kind: Telemetry -metadata: - name: metrics - namespace: dubbo-system -spec: - metrics: - - providers: - - name: prometheus ---- -# Set up a Service associated with our proxy, which will run as 1.1.1.1 IP -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: proxy-service-instance -spec: - hosts: - - example.com - ports: - - number: 80 - name: http - protocol: HTTP - - number: 7070 - name: tcp - protocol: TCP - - number: 443 - name: https - protocol: HTTPS - - number: 9090 - name: auto - protocol: "" - resolution: STATIC - endpoints: - - address: 1.1.1.1 ---- -# Set up .Services number of services. Each will have 4 ports (one for each protocol) - {{- range $i := until .Services }} -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-{{$i}} -spec: - hosts: - - random-{{$i}}.host.example - ports: - - number: 80 - name: http - protocol: HTTP - - number: 7070 - name: tcp - protocol: TCP - - number: 443 - name: https - protocol: HTTPS - - number: 9090 - name: auto - resolution: STATIC - endpoints: - - address: 1.2.3.4 ---- -{{- end }} diff --git a/pilot/pkg/xds/testdata/benchmarks/telemetry.extra.yaml b/pilot/pkg/xds/testdata/benchmarks/telemetry.extra.yaml deleted file mode 100644 index 594e61639..000000000 --- a/pilot/pkg/xds/testdata/benchmarks/telemetry.extra.yaml +++ /dev/null @@ -1,3049 +0,0 @@ ---- -# Source: istiod/templates/poddisruptionbudget.yaml -apiVersion: policy/v1 -kind: PodDisruptionBudget -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - release: istio - istio: pilot -spec: - minAvailable: 1 - selector: - matchLabels: - app: istiod - istio: pilot ---- -# Source: istiod/templates/serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - release: istio ---- -# Source: istiod/templates/configmap.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: istio - namespace: dubbo-system - labels: - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - release: istio -data: - - # Configuration file for the mesh networks to be used by the Split Horizon EDS. - meshNetworks: |- - networks: {} - - mesh: |- - defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - tracing: - zipkin: - address: zipkin.dubbo-system:9411 - enablePrometheusMerge: true - rootNamespace: null - trustDomain: cluster.local ---- -# Source: istiod/templates/istiod-injector-configmap.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: istio-sidecar-injector - namespace: dubbo-system - labels: - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - release: istio -data: - - values: |- - { - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "externalIstiod": false, - "hub": "gcr.io/istio-testing", - "imagePullPolicy": "", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "proxyv2", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "proxyv2", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } - } - - # To disable injection: use omitSidecarInjectorConfigMap, which disables the webhook patching - # and istiod webhook functionality. - # - # New fields should not use Values - it is a 'primary' config object, users should be able - # to fine tune it or use it with kube-inject. - config: |- - # defaultTemplates defines the default template to use for pods that do not explicitly specify a template - defaultTemplates: [sidecar] - policy: enabled - alwaysInjectSelector: - [] - neverInjectSelector: - [] - injectedAnnotations: - template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" - templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} ---- -# Source: istiod/templates/clusterrole.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istiod-clusterrole-dubbo-system - labels: - app: istiod - release: istio -rules: - # sidecar injection controller - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["mutatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update", "patch"] - - # configuration validation webhook controller - - apiGroups: ["admissionregistration.k8s.io"] - resources: ["validatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "update"] - - # istio configuration - # removing CRD permissions can break older versions of Istio running alongside this control plane (https://github.com/istio/istio/issues/29382) - # please proceed with caution - - apiGroups: ["config.istio.io", "security.istio.io", "networking.istio.io", "authentication.istio.io", "rbac.istio.io", "telemetry.istio.io", "extensions.istio.io"] - verbs: ["get", "watch", "list"] - resources: ["*"] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "workloadentries" ] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "workloadentries/status" ] - - # auto-detect installed CRD definitions - - apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "list", "watch"] - - # discovery and routing - - apiGroups: [""] - resources: ["pods", "nodes", "services", "namespaces", "endpoints"] - verbs: ["get", "list", "watch"] - - apiGroups: ["discovery.k8s.io"] - resources: ["endpointslices"] - verbs: ["get", "list", "watch"] - - # ingress controller - - apiGroups: ["networking.k8s.io"] - resources: ["ingresses", "ingressclasses"] - verbs: ["get", "list", "watch"] - - apiGroups: ["networking.k8s.io"] - resources: ["ingresses/status"] - verbs: ["*"] - - # required for CA's namespace controller - - apiGroups: [""] - resources: ["configmaps"] - verbs: ["create", "get", "list", "watch", "update"] - - # Istiod and bootstrap. - - apiGroups: ["certificates.k8s.io"] - resources: - - "certificatesigningrequests" - - "certificatesigningrequests/approval" - - "certificatesigningrequests/status" - verbs: ["update", "create", "get", "delete", "watch"] - - apiGroups: ["certificates.k8s.io"] - resources: - - "signers" - resourceNames: - - "kubernetes.io/legacy-unknown" - verbs: ["approve"] - - # Used by Istiod to verify the JWT tokens - - apiGroups: ["authentication.k8s.io"] - resources: ["tokenreviews"] - verbs: ["create"] - - # Used by Istiod to verify gateway SDS - - apiGroups: ["authorization.k8s.io"] - resources: ["subjectaccessreviews"] - verbs: ["create"] - - # Use for Kubernetes Service APIs - - apiGroups: ["networking.x-k8s.io", "gateway.networking.k8s.io"] - resources: ["*"] - verbs: ["get", "watch", "list"] - - apiGroups: ["networking.x-k8s.io", "gateway.networking.k8s.io"] - resources: ["*"] # TODO: should be on just */status but wildcard is not supported - verbs: ["update", "patch"] - - apiGroups: ["gateway.networking.k8s.io"] - resources: ["gatewayclasses"] - verbs: ["create", "update", "patch", "delete"] - - # Needed for multicluster secret reading, possibly ingress certs in the future - - apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "watch", "list"] - - # Used for MCS serviceexport management - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceexports"] - verbs: [ "get", "watch", "list", "create", "delete"] - - # Used for MCS serviceimport management - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceimports"] - verbs: ["get", "watch", "list"] ---- -# Source: istiod/templates/clusterrole.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istiod-gateway-controller-dubbo-system - labels: - app: istiod - release: istio -rules: - - apiGroups: ["apps"] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "deployments" ] - - apiGroups: [""] - verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ] - resources: [ "services" ] ---- -# Source: istiod/templates/reader-clusterrole.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: istio-reader-clusterrole-dubbo-system - labels: - app: istio-reader - release: istio -rules: - - apiGroups: - - "config.istio.io" - - "security.istio.io" - - "networking.istio.io" - - "authentication.istio.io" - - "rbac.istio.io" - resources: ["*"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["endpoints", "pods", "services", "nodes", "replicationcontrollers", "namespaces", "secrets"] - verbs: ["get", "list", "watch"] - - apiGroups: ["networking.istio.io"] - verbs: [ "get", "watch", "list" ] - resources: [ "workloadentries" ] - - apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "list", "watch"] - - apiGroups: ["discovery.k8s.io"] - resources: ["endpointslices"] - verbs: ["get", "list", "watch"] - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceexports"] - verbs: ["get", "list", "watch", "create", "delete"] - - apiGroups: ["multicluster.x-k8s.io"] - resources: ["serviceimports"] - verbs: ["get", "list", "watch"] - - apiGroups: ["apps"] - resources: ["replicasets"] - verbs: ["get", "list", "watch"] - - apiGroups: ["authentication.k8s.io"] - resources: ["tokenreviews"] - verbs: ["create"] - - apiGroups: ["authorization.k8s.io"] - resources: ["subjectaccessreviews"] - verbs: ["create"] ---- -# Source: istiod/templates/clusterrolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istiod-clusterrole-dubbo-system - labels: - app: istiod - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istiod-clusterrole-dubbo-system -subjects: - - kind: ServiceAccount - name: istiod - namespace: dubbo-system ---- -# Source: istiod/templates/clusterrolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istiod-gateway-controller-dubbo-system - labels: - app: istiod - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istiod-gateway-controller-dubbo-system -subjects: -- kind: ServiceAccount - name: istiod - namespace: dubbo-system ---- -# Source: istiod/templates/reader-clusterrolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: istio-reader-clusterrole-dubbo-system - labels: - app: istio-reader - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: istio-reader-clusterrole-dubbo-system -subjects: - - kind: ServiceAccount - name: istio-reader-service-account - namespace: dubbo-system ---- -# Source: istiod/templates/role.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - release: istio -rules: -# permissions to verify the webhook is ready and rejecting -# invalid config. We use --server-dry-run so no config is persisted. -- apiGroups: ["networking.istio.io"] - verbs: ["create"] - resources: ["gateways"] - -# For storing CA secret -- apiGroups: [""] - resources: ["secrets"] - # TODO lock this down to istio-ca-cert if not using the DNS cert mesh config - verbs: ["create", "get", "watch", "list", "update", "delete"] ---- -# Source: istiod/templates/rolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: istiod -subjects: - - kind: ServiceAccount - name: istiod - namespace: dubbo-system ---- -# Source: istiod/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: istiod - namespace: dubbo-system - labels: - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - app: istiod - istio: pilot - release: istio -spec: - ports: - - port: 15010 - name: grpc-xds # plaintext - protocol: TCP - - port: 15012 - name: https-dns # mTLS with k8s-signed cert - protocol: TCP - - port: 443 - name: https-webhook # validation and injection - targetPort: 15017 - protocol: TCP - - port: 15014 - name: http-monitoring # prometheus stats - protocol: TCP - selector: - app: istiod - # Label used by the 'default' service. For versioned deployments we match with app and version. - # This avoids default deployment picking the canary - istio: pilot ---- -# Source: istiod/templates/deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - istio: pilot - release: istio -spec: - strategy: - rollingUpdate: - maxSurge: 100% - maxUnavailable: 25% - selector: - matchLabels: - istio: pilot - template: - metadata: - labels: - app: istiod - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - sidecar.istio.io/inject: "false" - operator.istio.io/component: "Pilot" - istio: pilot - annotations: - prometheus.io/port: "15014" - prometheus.io/scrape: "true" - sidecar.istio.io/inject: "false" - spec: - serviceAccountName: istiod - securityContext: - fsGroup: 1337 - containers: - - name: discovery - image: "gcr.io/istio-testing/pilot:latest" - args: - - "discovery" - - --monitoringAddr=:15014 - - --log_output_level=default:info - - --domain - - cluster.local - - --keepaliveMaxServerConnectionAge - - "30m" - ports: - - containerPort: 8080 - protocol: TCP - - containerPort: 15010 - protocol: TCP - - containerPort: 15017 - protocol: TCP - readinessProbe: - httpGet: - path: /ready - port: 8080 - initialDelaySeconds: 1 - periodSeconds: 3 - timeoutSeconds: 5 - env: - - name: REVISION - value: "default" - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.serviceAccountName - - name: KUBECONFIG - value: /var/run/secrets/remote/config - - name: PILOT_TRACE_SAMPLING - value: "1" - - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_OUTBOUND - value: "true" - - name: PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_INBOUND - value: "true" - - name: ISTIOD_ADDR - value: istiod.dubbo-system.svc:15012 - - name: PILOT_ENABLE_ANALYSIS - value: "false" - - name: CLUSTER_ID - value: "Kubernetes" - resources: - requests: - cpu: 500m - memory: 2048Mi - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsUser: 1337 - runAsGroup: 1337 - runAsNonRoot: true - capabilities: - drop: - - ALL - volumeMounts: - - name: istio-token - mountPath: /var/run/secrets/tokens - readOnly: true - - name: local-certs - mountPath: /var/run/secrets/istio-dns - - name: cacerts - mountPath: /etc/cacerts - readOnly: true - - name: istio-kubeconfig - mountPath: /var/run/secrets/remote - readOnly: true - volumes: - # Technically not needed on this pod - but it helps debugging/testing SDS - # Should be removed after everything works. - - emptyDir: - medium: Memory - name: local-certs - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - # Optional: user-generated root - - name: cacerts - secret: - secretName: cacerts - optional: true - - name: istio-kubeconfig - secret: - secretName: istio-kubeconfig - optional: true ---- -# Source: istiod/templates/autoscale.yaml -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: istiod - namespace: dubbo-system - labels: - app: istiod - release: istio - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" -spec: - maxReplicas: 5 - minReplicas: 1 - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: istiod - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: 80 ---- -# Source: istiod/templates/revision-tags.yaml -# Adapted from istio-discovery/templates/mutatingwebhook.yaml -# Removed paths for legacy and default selectors since a revision tag -# is inherently created from a specific revision ---- -# Source: istiod/templates/telemetryv2_1.11.yaml -# Note: http stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.11 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - priority: -1 - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats ---- -# Source: istiod/templates/telemetryv2_1.11.yaml -# Note: tcp stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.11 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - priority: -1 - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: tcp_stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.11.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" ---- -# Source: istiod/templates/telemetryv2_1.12.yaml -# Note: http stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.12 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - priority: -1 - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats ---- -# Source: istiod/templates/telemetryv2_1.12.yaml -# Note: tcp stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.12 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - priority: -1 - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: tcp_stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.12.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" ---- -# Source: istiod/templates/telemetryv2_1.13.yaml -# Note: http stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.13 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - priority: -1 - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats ---- -# Source: istiod/templates/telemetryv2_1.13.yaml -# Note: tcp stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.13 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - priority: -1 - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: tcp_stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.13.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" ---- -# Source: istiod/templates/telemetryv2_1.14.yaml -# Note: http stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.14 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - priority: -1 - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats ---- -# Source: istiod/templates/telemetryv2_1.14.yaml -# Note: tcp stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.14 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - priority: -1 - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: tcp_stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.14.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" ---- -# Source: istiod/templates/telemetryv2_1.15.yaml -# Note: http stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: stats-filter-1.15 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - priority: -1 - configPatches: - - applyTo: HTTP_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true, - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats - - applyTo: HTTP_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "disable_host_header_fallback": true - } - vm_config: - vm_id: stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: envoy.wasm.stats ---- -# Source: istiod/templates/telemetryv2_1.15.yaml -# Note: tcp stats filter is wasm enabled only in sidecars. -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: tcp-stats-filter-1.15 - namespace: dubbo-system - labels: - istio.io/rev: default -spec: - priority: -1 - configPatches: - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_INBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_inbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio", - "metrics": [ - { - "dimensions": { - "destination_cluster": "node.metadata['CLUSTER_ID']", - "source_cluster": "downstream_peer.cluster_id" - } - } - ] - } - vm_config: - vm_id: tcp_stats_inbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: SIDECAR_OUTBOUND - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" - - applyTo: NETWORK_FILTER - match: - context: GATEWAY - proxy: - proxyVersion: '^1\.15.*' - listener: - filterChain: - filter: - name: "envoy.filters.network.tcp_proxy" - patch: - operation: INSERT_BEFORE - value: - name: istio.stats - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm - value: - config: - root_id: stats_outbound - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - { - "debug": "false", - "stat_prefix": "istio" - } - vm_config: - vm_id: tcp_stats_outbound - runtime: envoy.wasm.runtime.null - code: - local: - inline_string: "envoy.wasm.stats" ---- -# Source: istiod/templates/mutatingwebhook.yaml -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - name: istio-sidecar-injector - labels: - istio.io/rev: default - install.operator.istio.io/owning-resource: unknown - operator.istio.io/component: "Pilot" - app: sidecar-injector - release: istio -webhooks: -- name: rev.namespace.sidecar-injector.istio.io - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: "/inject" - port: 443 - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] - namespaceSelector: - matchExpressions: - - key: istio.io/rev - operator: In - values: - - "default" - - key: istio-injection - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" -- name: rev.object.sidecar-injector.istio.io - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: "/inject" - port: 443 - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] - namespaceSelector: - matchExpressions: - - key: istio.io/rev - operator: DoesNotExist - - key: istio-injection - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" - - key: istio.io/rev - operator: In - values: - - "default" -- name: namespace.sidecar-injector.istio.io - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: "/inject" - port: 443 - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: In - values: - - enabled - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: NotIn - values: - - "false" -- name: object.sidecar-injector.istio.io - clientConfig: - service: - name: istiod - namespace: dubbo-system - path: "/inject" - port: 443 - sideEffects: None - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Fail - admissionReviewVersions: ["v1beta1", "v1"] - namespaceSelector: - matchExpressions: - - key: istio-injection - operator: DoesNotExist - - key: istio.io/rev - operator: DoesNotExist - objectSelector: - matchExpressions: - - key: sidecar.istio.io/inject - operator: In - values: - - "true" - - key: istio.io/rev - operator: DoesNotExist diff --git a/pilot/pkg/xds/testdata/benchmarks/telemetry.yaml b/pilot/pkg/xds/testdata/benchmarks/telemetry.yaml deleted file mode 100644 index 6462c4595..000000000 --- a/pilot/pkg/xds/testdata/benchmarks/telemetry.yaml +++ /dev/null @@ -1,51 +0,0 @@ -# Set up a Service associated with our proxy, which will run as 1.1.1.1 IP -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: proxy-service-instance -spec: - hosts: - - example.com - ports: - - number: 80 - name: http - protocol: HTTP - - number: 7070 - name: tcp - protocol: TCP - - number: 443 - name: https - protocol: HTTPS - - number: 9090 - name: auto - protocol: "" - resolution: STATIC - endpoints: - - address: 1.1.1.1 ---- -# Set up .Services number of services. Each will have 4 ports (one for each protocol) - {{- range $i := until .Services }} -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-{{$i}} -spec: - hosts: - - random-{{$i}}.host.example - ports: - - number: 80 - name: http - protocol: HTTP - - number: 7070 - name: tcp - protocol: TCP - - number: 443 - name: https - protocol: HTTPS - - number: 9090 - name: auto - resolution: STATIC - endpoints: - - address: 1.2.3.4 ---- -{{- end }} diff --git a/pilot/pkg/xds/testdata/benchmarks/tls.yaml b/pilot/pkg/xds/testdata/benchmarks/tls.yaml deleted file mode 100644 index f2f33e63b..000000000 --- a/pilot/pkg/xds/testdata/benchmarks/tls.yaml +++ /dev/null @@ -1,69 +0,0 @@ -# Set up a Service associated with our proxy, which will run as 1.1.1.1 IP -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: proxy-service-instance -spec: - hosts: - - example.com - ports: - - number: 80 - name: http - protocol: HTTP - - number: 7070 - name: tcp - protocol: TCP - - number: 443 - name: https - protocol: HTTPS - - number: 9090 - name: auto - protocol: "" - resolution: STATIC - endpoints: - - address: 1.1.1.1 ---- -apiVersion: security.istio.io/v1beta1 -kind: PeerAuthentication -metadata: - name: default -spec: - mtls: - mode: STRICT ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: use-mtls -spec: - host: "*.host.example" - trafficPolicy: - tls: - mode: ISTIO_MUTUAL ---- -# Set up .Services number of services. Each will have 4 ports (one for each protocol) -{{- range $i := until .Services }} -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-{{$i}} -spec: - hosts: - - random-{{$i}}.host.example - ports: - - number: 80 - name: http - protocol: HTTP - - number: 7070 - name: tcp - protocol: TCP - - number: 443 - name: https - protocol: HTTPS - - number: 9090 - name: auto - resolution: STATIC - endpoints: - - address: 1.2.3.4 ---- -{{- end }} \ No newline at end of file diff --git a/pilot/pkg/xds/testdata/benchmarks/virtualservice.yaml b/pilot/pkg/xds/testdata/benchmarks/virtualservice.yaml deleted file mode 100644 index 322b6495e..000000000 --- a/pilot/pkg/xds/testdata/benchmarks/virtualservice.yaml +++ /dev/null @@ -1,54 +0,0 @@ -# Set up a Service associated with our proxy, which will run as 1.1.1.1 IP -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: proxy-service-instance -spec: - hosts: - - example.com - ports: - - number: 80 - name: http - protocol: HTTP - resolution: STATIC - endpoints: - - address: 1.1.1.1 ---- -# Set up .Services VirtualServices, each pointing to a different Service -{{- range $i := until .Services }} -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: vs-{{$i}} -spec: - hosts: - - random-{{$i}}.host.example - http: - - name: "match-route" - match: - - uri: - prefix: "/foo" - - uri: - regex: "/bar" - rewrite: - uri: "/new-url" - route: - - destination: - host: random-{{$i}}.host.example ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-{{$i}} -spec: - hosts: - - random-{{$i}}.host.example - ports: - - number: 80 - name: http - protocol: HTTP - resolution: STATIC - endpoints: - - address: 1.2.3.4 ---- -{{- end }} \ No newline at end of file diff --git a/pilot/pkg/xds/testdata/ecds.yaml b/pilot/pkg/xds/testdata/ecds.yaml deleted file mode 100644 index 6e875250b..000000000 --- a/pilot/pkg/xds/testdata/ecds.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: test -spec: - configPatches: - - applyTo: EXTENSION_CONFIG - match: - context: SIDECAR_INBOUND - patch: - operation: ADD - value: - name: extension-config - typed_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - value: - config: - vm_config: - code: - remote: - http_uri: - uri: https://test-url diff --git a/pilot/pkg/xds/testdata/nds-se.yaml b/pilot/pkg/xds/testdata/nds-se.yaml deleted file mode 100644 index c58184097..000000000 --- a/pilot/pkg/xds/testdata/nds-se.yaml +++ /dev/null @@ -1,95 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-dns-no-addr - namespace: ns2 -spec: - hosts: - - random-1.host.example - # expect address to be auto allocated - ports: - - number: 80 - name: http - protocol: HTTP - resolution: DNS ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-dns-with-addr - namespace: ns2 -spec: - hosts: - - random-2.host.example - addresses: - - 9.9.9.9 - ports: - - number: 80 - name: http - protocol: HTTP - resolution: DNS ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-static-no-addr - namespace: ns2 -spec: - hosts: - - random-3.host.example - # expect address to be auto allocated - ports: - - number: 80 - name: http - protocol: HTTP - resolution: STATIC - location: MESH_INTERNAL - endpoints: - - address: 1.2.3.4 - labels: - security.istio.io/tlsMode: istio ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-none-wildcard - namespace: ns2 -spec: - hosts: - - "*.random-4.host.example" - # expect no address to be auto allocated - ports: - - number: 80 - name: http - protocol: HTTP - resolution: NONE ---- -# this should not have any name table entry -# as we dont auto allocate for none mode services -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-none-no-wildcard - namespace: ns2 -spec: - hosts: - - random-5.host.example - ports: - - number: 80 - name: http - protocol: HTTP - resolution: NONE ---- -apiVersion: networking.istio.io/v1beta1 -kind: ServiceEntry -metadata: - name: cidr -spec: - addresses: - - 198.51.100.0/31 - hosts: - - address.internal - ports: - - name: tcp - number: 8888 - protocol: TCP diff --git a/pilot/pkg/xds/testdata/none_cds.json b/pilot/pkg/xds/testdata/none_cds.json deleted file mode 100644 index f3f0b3832..000000000 --- a/pilot/pkg/xds/testdata/none_cds.json +++ /dev/null @@ -1,260 +0,0 @@ -{ - "BlackHoleCluster": { - "name": "BlackHoleCluster", - "connect_timeout": 1000000000, - "LbConfig": null - }, - "PassthroughCluster": { - "name": "PassthroughCluster", - "type": 4, - "connect_timeout": 1000000000, - "lb_policy": 4, - "LbConfig": null - }, - "inbound|7070|tcplocal|s1tcp.none": { - "name": "inbound|7070|tcplocal|s1tcp.none", - "connect_timeout": 1000000000, - "load_assignment": { - "cluster_name": "inbound|7070|tcplocal|s1tcp.none", - "endpoints": [ - { - "lb_endpoints": [ - { - "endpoint": { - "address": { - "Address": { - "SocketAddress": { - "address": "127.0.0.1", - "PortSpecifier": { - "PortValue": 17070 - } - } - } - } - } - } - ] - } - ] - }, - "circuit_breakers": { - "thresholds": [ - {} - ] - }, - "LbConfig": null - }, - "inbound|7071|httplocal|s1http.none": { - "name": "inbound|7071|httplocal|s1http.none", - "connect_timeout": 1000000000, - "load_assignment": { - "cluster_name": "inbound|7071|httplocal|s1http.none", - "endpoints": [ - { - "lb_endpoints": [ - { - "endpoint": { - "address": { - "Address": { - "SocketAddress": { - "address": "127.0.0.1", - "PortSpecifier": { - "PortValue": 17071 - } - } - } - } - } - } - ] - } - ] - }, - "circuit_breakers": { - "thresholds": [ - {} - ] - }, - "LbConfig": null - }, - "outbound|2006||s2dns.external.test.istio.io": { - "name": "outbound|2006||s2dns.external.test.istio.io", - "type": 1, - "connect_timeout": 1000000000, - "load_assignment": { - "cluster_name": "outbound|2006||s2dns.external.test.istio.io", - "endpoints": [ - { - "lb_endpoints": null - }, - { - "lb_endpoints": [ - { - "endpoint": { - "address": { - "Address": { - "SocketAddress": { - "address": "s2dns.external.test.istio.io", - "PortSpecifier": { - "PortValue": 2006 - } - } - } - } - }, - "load_balancing_weight": { - "value": 1 - } - } - ] - } - ] - }, - "circuit_breakers": { - "thresholds": [ - { - "max_retries": { - "value": 1024 - } - } - ] - }, - "dns_lookup_family": 1, - "LbConfig": null - }, - "outbound|2007||tcpmeshdns.seexamples.svc": { - "name": "outbound|2007||tcpmeshdns.seexamples.svc", - "type": 1, - "connect_timeout": 1000000000, - "load_assignment": { - "cluster_name": "outbound|2007||tcpmeshdns.seexamples.svc", - "endpoints": [ - { - "lb_endpoints": null - }, - { - "lb_endpoints": [ - { - "endpoint": { - "address": { - "Address": { - "SocketAddress": { - "address": "tcpmeshdns.seexamples.svc", - "PortSpecifier": { - "PortValue": 2007 - } - } - } - } - }, - "load_balancing_weight": { - "value": 1 - } - } - ] - } - ] - }, - "circuit_breakers": { - "thresholds": [ - { - "max_retries": { - "value": 1024 - } - } - ] - }, - "dns_lookup_family": 1, - "LbConfig": null - }, - "outbound|2443||api1.facebook.com": { - "name": "outbound|2443||api1.facebook.com", - "type": 1, - "connect_timeout": 1000000000, - "load_assignment": { - "cluster_name": "outbound|2443||api1.facebook.com", - "endpoints": [ - { - "lb_endpoints": null - }, - { - "lb_endpoints": [ - { - "endpoint": { - "address": { - "Address": { - "SocketAddress": { - "address": "api1.facebook.com", - "PortSpecifier": { - "PortValue": 2443 - } - } - } - } - }, - "load_balancing_weight": { - "value": 1 - } - } - ] - } - ] - }, - "circuit_breakers": { - "thresholds": [ - { - "max_retries": { - "value": 1024 - } - } - ] - }, - "dns_lookup_family": 1, - "LbConfig": null - }, - "outbound|2443||www1.googleapis.com": { - "name": "outbound|2443||www1.googleapis.com", - "type": 1, - "connect_timeout": 1000000000, - "load_assignment": { - "cluster_name": "outbound|2443||www1.googleapis.com", - "endpoints": [ - { - "lb_endpoints": null - }, - { - "lb_endpoints": [ - { - "endpoint": { - "address": { - "Address": { - "SocketAddress": { - "address": "www1.googleapis.com", - "PortSpecifier": { - "PortValue": 2443 - } - } - } - } - }, - "load_balancing_weight": { - "value": 1 - } - } - ] - } - ] - }, - "circuit_breakers": { - "thresholds": [ - { - "max_retries": { - "value": 1024 - } - } - ] - }, - "dns_lookup_family": 1, - "LbConfig": null - } - } \ No newline at end of file diff --git a/pilot/pkg/xds/testdata/none_ecds.json b/pilot/pkg/xds/testdata/none_ecds.json deleted file mode 100644 index 70262df26..000000000 --- a/pilot/pkg/xds/testdata/none_ecds.json +++ /dev/null @@ -1,117 +0,0 @@ -{ - "outbound|2000||s1tcp.none": { - "name": "outbound|2000||s1tcp.none", - "type": 3, - "eds_cluster_config": { - "eds_config": { - "ConfigSourceSpecifier": { - "Ads": {} - } - }, - "service_name": "outbound|2000||s1tcp.none" - }, - "connect_timeout": 1000000000, - "circuit_breakers": { - "thresholds": [ - { - "max_retries": { - "value": 1024 - } - } - ] - }, - "LbConfig": null - }, - "outbound|2001||s1http.none": { - "name": "outbound|2001||s1http.none", - "type": 3, - "eds_cluster_config": { - "eds_config": { - "ConfigSourceSpecifier": { - "Ads": {} - } - }, - "service_name": "outbound|2001||s1http.none" - }, - "connect_timeout": 1000000000, - "circuit_breakers": { - "thresholds": [ - { - "max_retries": { - "value": 1024 - } - } - ] - }, - "LbConfig": null - }, - "outbound|2005||s2.external.test.istio.io": { - "name": "outbound|2005||s2.external.test.istio.io", - "type": 3, - "eds_cluster_config": { - "eds_config": { - "ConfigSourceSpecifier": { - "Ads": {} - } - }, - "service_name": "outbound|2005||s2.external.test.istio.io" - }, - "connect_timeout": 1000000000, - "circuit_breakers": { - "thresholds": [ - { - "max_retries": { - "value": 1024 - } - } - ] - }, - "LbConfig": null - }, - "outbound|2008||tcpmeshstatic.seexamples.svc": { - "name": "outbound|2008||tcpmeshstatic.seexamples.svc", - "type": 3, - "eds_cluster_config": { - "eds_config": { - "ConfigSourceSpecifier": { - "Ads": {} - } - }, - "service_name": "outbound|2008||tcpmeshstatic.seexamples.svc" - }, - "connect_timeout": 1000000000, - "circuit_breakers": { - "thresholds": [ - { - "max_retries": { - "value": 1024 - } - } - ] - }, - "LbConfig": null - }, - "outbound|2009||tcpmeshstaticint.seexamples.svc": { - "name": "outbound|2009||tcpmeshstaticint.seexamples.svc", - "type": 3, - "eds_cluster_config": { - "eds_config": { - "ConfigSourceSpecifier": { - "Ads": {} - } - }, - "service_name": "outbound|2009||tcpmeshstaticint.seexamples.svc" - }, - "connect_timeout": 1000000000, - "circuit_breakers": { - "thresholds": [ - { - "max_retries": { - "value": 1024 - } - } - ] - }, - "LbConfig": null - } - } \ No newline at end of file diff --git a/pilot/pkg/xds/testdata/none_eds.json b/pilot/pkg/xds/testdata/none_eds.json deleted file mode 100644 index 7e4710773..000000000 --- a/pilot/pkg/xds/testdata/none_eds.json +++ /dev/null @@ -1,146 +0,0 @@ -{ - "outbound|2000||s1tcp.none": { - "cluster_name": "outbound|2000||s1tcp.none", - "endpoints": [ - { - "lb_endpoints": [ - { - "endpoint": { - "address": { - "Address": { - "SocketAddress": { - "address": "10.11.0.1", - "PortSpecifier": { - "PortValue": 7070 - } - } - } - } - } - } - ], - "load_balancing_weight": { - "value": 1 - } - } - ] - }, - "outbound|2001||s1http.none": { - "cluster_name": "outbound|2001||s1http.none", - "endpoints": [ - { - "lb_endpoints": [ - { - "endpoint": { - "address": { - "Address": { - "SocketAddress": { - "address": "10.11.0.1", - "PortSpecifier": { - "PortValue": 7071 - } - } - } - } - } - } - ], - "load_balancing_weight": { - "value": 1 - } - } - ] - }, - "outbound|2005||s2.external.test.istio.io": { - "cluster_name": "outbound|2005||s2.external.test.istio.io", - "endpoints": [ - { - "lb_endpoints": [ - { - "endpoint": { - "address": { - "Address": { - "SocketAddress": { - "address": "10.11.0.2", - "PortSpecifier": { - "PortValue": 7071 - } - } - } - } - } - }, - { - "endpoint": { - "address": { - "Address": { - "SocketAddress": { - "address": "10.11.0.3", - "PortSpecifier": { - "PortValue": 7072 - } - } - } - } - } - } - ], - "load_balancing_weight": { - "value": 2 - } - } - ] - }, - "outbound|2008||tcpmeshstatic.seexamples.svc": { - "cluster_name": "outbound|2008||tcpmeshstatic.seexamples.svc", - "endpoints": [ - { - "lb_endpoints": [ - { - "endpoint": { - "address": { - "Address": { - "SocketAddress": { - "address": "10.11.0.8", - "PortSpecifier": { - "PortValue": 7070 - } - } - } - } - } - } - ], - "load_balancing_weight": { - "value": 1 - } - } - ] - }, - "outbound|2009||tcpmeshstaticint.seexamples.svc": { - "cluster_name": "outbound|2009||tcpmeshstaticint.seexamples.svc", - "endpoints": [ - { - "lb_endpoints": [ - { - "endpoint": { - "address": { - "Address": { - "SocketAddress": { - "address": "10.11.0.9", - "PortSpecifier": { - "PortValue": 7070 - } - } - } - } - } - } - ], - "load_balancing_weight": { - "value": 1 - } - } - ] - } - } \ No newline at end of file diff --git a/pilot/pkg/xds/testdata/none_lds_http.json b/pilot/pkg/xds/testdata/none_lds_http.json deleted file mode 100644 index 8b4360121..000000000 --- a/pilot/pkg/xds/testdata/none_lds_http.json +++ /dev/null @@ -1,830 +0,0 @@ -{ - "0.0.0.0_7071": { - "name": "0.0.0.0_7071", - "address": { - "Address": { - "SocketAddress": { - "address": "0.0.0.0", - "PortSpecifier": { - "PortValue": 7071 - } - } - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.http_connection_manager", - "ConfigType": { - "Config": { - "fields": { - "access_log": { - "Kind": { - "ListValue": { - "values": [ - { - "Kind": { - "StructValue": { - "fields": { - "config": { - "Kind": { - "StructValue": { - "fields": { - "path": { - "Kind": { - "StringValue": "/dev/stdout" - } - } - } - } - } - }, - "name": { - "Kind": { - "StringValue": "envoy.file_access_log" - } - } - } - } - } - } - ] - } - } - }, - "forward_client_cert_details": { - "Kind": { - "StringValue": "APPEND_FORWARD" - } - }, - "generate_request_id": { - "Kind": { - "BoolValue": true - } - }, - "http_filters": { - "Kind": { - "ListValue": { - "values": [ - { - "Kind": { - "StructValue": { - "fields": { - "name": { - "Kind": { - "StringValue": "envoy.cors" - } - } - } - } - } - }, - { - "Kind": { - "StructValue": { - "fields": { - "name": { - "Kind": { - "StringValue": "envoy.fault" - } - } - } - } - } - }, - { - "Kind": { - "StructValue": { - "fields": { - "name": { - "Kind": { - "StringValue": "envoy.filters.http.router" - } - } - } - } - } - } - ] - } - } - }, - "route_config": { - "Kind": { - "StructValue": { - "fields": { - "name": { - "Kind": { - "StringValue": "inbound|7071|httplocal|s1http.none" - } - }, - "validate_clusters": { - "Kind": { - "BoolValue": false - } - }, - "virtual_hosts": { - "Kind": { - "ListValue": { - "values": [ - { - "Kind": { - "StructValue": { - "fields": { - "domains": { - "Kind": { - "ListValue": { - "values": [ - { - "Kind": { - "StringValue": "*" - } - } - ] - } - } - }, - "name": { - "Kind": { - "StringValue": "inbound|http|7071" - } - }, - "routes": { - "Kind": { - "ListValue": { - "values": [ - { - "Kind": { - "StructValue": { - "fields": { - "decorator": { - "Kind": { - "StructValue": { - "fields": { - "operation": { - "Kind": { - "StringValue": "s1http.none:7071/*" - } - } - } - } - } - }, - "match": { - "Kind": { - "StructValue": { - "fields": { - "prefix": { - "Kind": { - "StringValue": "/" - } - } - } - } - } - }, - "per_filter_config": { - "Kind": { - "StructValue": { - "fields": { - } - } - } - } - }, - "route": { - "Kind": { - "StructValue": { - "fields": { - "cluster": { - "Kind": { - "StringValue": "inbound|7071|httplocal|s1http.none" - } - }, - "max_grpc_timeout": { - "Kind": { - "StringValue": "0s" - } - }, - "timeout": { - "Kind": { - "StringValue": "0s" - } - } - } - } - } - } - } - } - } - } - ] - } - } - } - } - } - } - } - ] - } - } - } - } - } - } - }, - "server_name": { - "Kind": { - "StringValue": "istio-envoy" - } - }, - "set_current_client_cert_details": { - "Kind": { - "StructValue": { - "fields": { - "dns": { - "Kind": { - "BoolValue": true - } - }, - "subject": { - "Kind": { - "BoolValue": true - } - }, - "uri": { - "Kind": { - "BoolValue": true - } - } - } - } - } - }, - "stat_prefix": { - "Kind": { - "StringValue": "0.0.0.0_7071" - } - }, - "stream_idle_timeout": { - "Kind": { - "StringValue": "0s" - } - }, - "tracing": { - "Kind": { - "StructValue": { - "fields": { - "client_sampling": { - "Kind": { - "StructValue": { - "fields": { - "value": { - "Kind": { - "NumberValue": 100 - } - } - } - } - } - }, - "overall_sampling": { - "Kind": { - "StructValue": { - "fields": { - "value": { - "Kind": { - "NumberValue": 100 - } - } - } - } - } - }, - "random_sampling": { - "Kind": { - "StructValue": { - "fields": { - "value": { - "Kind": { - "NumberValue": 100 - } - } - } - } - } - } - } - } - } - }, - "upgrade_configs": { - "Kind": { - "ListValue": { - "values": [ - { - "Kind": { - "StructValue": { - "fields": { - "upgrade_type": { - "Kind": { - "StringValue": "websocket" - } - } - } - } - } - } - ] - } - } - }, - "use_remote_address": { - "Kind": { - "BoolValue": false - } - } - } - } - } - } - ] - } - ], - "listener_filters": null - }, - "127.0.0.1_15002": { - "name": "127.0.0.1_15002", - "address": { - "Address": { - "SocketAddress": { - "address": "127.0.0.1", - "PortSpecifier": { - "PortValue": 15002 - } - } - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.http_connection_manager", - "ConfigType": { - "Config": { - "fields": { - "access_log": { - "Kind": { - "ListValue": { - "values": [ - { - "Kind": { - "StructValue": { - "fields": { - "config": { - "Kind": { - "StructValue": { - "fields": { - "path": { - "Kind": { - "StringValue": "/dev/stdout" - } - } - } - } - } - }, - "name": { - "Kind": { - "StringValue": "envoy.file_access_log" - } - } - } - } - } - } - ] - } - } - }, - "generate_request_id": { - "Kind": { - "BoolValue": true - } - }, - "http_filters": { - "Kind": { - "ListValue": { - "values": [ - { - "Kind": { - "StructValue": { - "fields": { - "name": { - "Kind": { - "StringValue": "envoy.cors" - } - } - } - } - } - }, - { - "Kind": { - "StructValue": { - "fields": { - "name": { - "Kind": { - "StringValue": "envoy.fault" - } - } - } - } - } - }, - { - "Kind": { - "StructValue": { - "fields": { - "name": { - "Kind": { - "StringValue": "envoy.filters.http.router" - } - } - } - } - } - } - ] - } - } - }, - "http_protocol_options": { - "Kind": { - "StructValue": { - "fields": { - "allow_absolute_url": { - "Kind": { - "BoolValue": true - } - } - } - } - } - }, - "rds": { - "Kind": { - "StructValue": { - "fields": { - "config_source": { - "Kind": { - "StructValue": { - "fields": { - "ads": { - "Kind": { - "StructValue": {} - } - } - } - } - } - }, - "route_config_name": { - "Kind": { - "StringValue": "http_proxy" - } - } - } - } - } - }, - "stat_prefix": { - "Kind": { - "StringValue": "127.0.0.1_15002" - } - }, - "stream_idle_timeout": { - "Kind": { - "StringValue": "0s" - } - }, - "tracing": { - "Kind": { - "StructValue": { - "fields": { - "client_sampling": { - "Kind": { - "StructValue": { - "fields": { - "value": { - "Kind": { - "NumberValue": 100 - } - } - } - } - } - }, - "operation_name": { - "Kind": { - "StringValue": "EGRESS" - } - }, - "overall_sampling": { - "Kind": { - "StructValue": { - "fields": { - "value": { - "Kind": { - "NumberValue": 100 - } - } - } - } - } - }, - "random_sampling": { - "Kind": { - "StructValue": { - "fields": { - "value": { - "Kind": { - "NumberValue": 100 - } - } - } - } - } - } - } - } - } - }, - "upgrade_configs": { - "Kind": { - "ListValue": { - "values": [ - { - "Kind": { - "StructValue": { - "fields": { - "upgrade_type": { - "Kind": { - "StringValue": "websocket" - } - } - } - } - } - } - ] - } - } - }, - "use_remote_address": { - "Kind": { - "BoolValue": false - } - } - } - } - } - } - ] - } - ], - "listener_filters": null - }, - "127.0.0.1_2001": { - "name": "127.0.0.1_2001", - "address": { - "Address": { - "SocketAddress": { - "address": "127.0.0.1", - "PortSpecifier": { - "PortValue": 2001 - } - } - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.http_connection_manager", - "ConfigType": { - "Config": { - "fields": { - "access_log": { - "Kind": { - "ListValue": { - "values": [ - { - "Kind": { - "StructValue": { - "fields": { - "config": { - "Kind": { - "StructValue": { - "fields": { - "path": { - "Kind": { - "StringValue": "/dev/stdout" - } - } - } - } - } - }, - "name": { - "Kind": { - "StringValue": "envoy.file_access_log" - } - } - } - } - } - } - ] - } - } - }, - "generate_request_id": { - "Kind": { - "BoolValue": true - } - }, - "http_filters": { - "Kind": { - "ListValue": { - "values": [ - { - "Kind": { - "StructValue": { - "fields": { - "name": { - "Kind": { - "StringValue": "envoy.cors" - } - } - } - } - } - }, - { - "Kind": { - "StructValue": { - "fields": { - "name": { - "Kind": { - "StringValue": "envoy.fault" - } - } - } - } - } - }, - { - "Kind": { - "StructValue": { - "fields": { - "name": { - "Kind": { - "StringValue": "envoy.filters.http.router" - } - } - } - } - } - } - ] - } - } - }, - "rds": { - "Kind": { - "StructValue": { - "fields": { - "config_source": { - "Kind": { - "StructValue": { - "fields": { - "ads": { - "Kind": { - "StructValue": {} - } - } - } - } - } - }, - "route_config_name": { - "Kind": { - "StringValue": "2001" - } - } - } - } - } - }, - "stat_prefix": { - "Kind": { - "StringValue": "127.0.0.1_2001" - } - }, - "stream_idle_timeout": { - "Kind": { - "StringValue": "0s" - } - }, - "tracing": { - "Kind": { - "StructValue": { - "fields": { - "client_sampling": { - "Kind": { - "StructValue": { - "fields": { - "value": { - "Kind": { - "NumberValue": 100 - } - } - } - } - } - }, - "operation_name": { - "Kind": { - "StringValue": "EGRESS" - } - }, - "overall_sampling": { - "Kind": { - "StructValue": { - "fields": { - "value": { - "Kind": { - "NumberValue": 100 - } - } - } - } - } - }, - "random_sampling": { - "Kind": { - "StructValue": { - "fields": { - "value": { - "Kind": { - "NumberValue": 100 - } - } - } - } - } - } - } - } - } - }, - "upgrade_configs": { - "Kind": { - "ListValue": { - "values": [ - { - "Kind": { - "StructValue": { - "fields": { - "upgrade_type": { - "Kind": { - "StringValue": "websocket" - } - } - } - } - } - } - ] - } - } - }, - "use_remote_address": { - "Kind": { - "BoolValue": false - } - } - } - } - } - } - ] - } - ], - "listener_filters": null - } - } \ No newline at end of file diff --git a/pilot/pkg/xds/testdata/none_lds_tcp.json b/pilot/pkg/xds/testdata/none_lds_tcp.json deleted file mode 100644 index ba67b142b..000000000 --- a/pilot/pkg/xds/testdata/none_lds_tcp.json +++ /dev/null @@ -1,700 +0,0 @@ -{ - "0.0.0.0_7070": { - "name": "0.0.0.0_7070", - "address": { - "Address": { - "SocketAddress": { - "address": "0.0.0.0", - "PortSpecifier": { - "PortValue": 7070 - } - } - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.tcp_proxy", - "ConfigType": { - "Config": { - "fields": { - "access_log": { - "Kind": { - "ListValue": { - "values": [ - { - "Kind": { - "StructValue": { - "fields": { - "config": { - "Kind": { - "StructValue": { - "fields": { - "path": { - "Kind": { - "StringValue": "/dev/stdout" - } - } - } - } - } - }, - "name": { - "Kind": { - "StringValue": "envoy.file_access_log" - } - } - } - } - } - } - ] - } - } - }, - "cluster": { - "Kind": { - "StringValue": "inbound|7070|tcplocal|s1tcp.none" - } - }, - "stat_prefix": { - "Kind": { - "StringValue": "inbound|7070|tcplocal|s1tcp.none" - } - } - } - } - } - } - ] - } - ], - "listener_filters": null - }, - "127.0.0.1_2000": { - "name": "127.0.0.1_2000", - "address": { - "Address": { - "SocketAddress": { - "address": "127.0.0.1", - "PortSpecifier": { - "PortValue": 2000 - } - } - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.tcp_proxy", - "ConfigType": { - "Config": { - "fields": { - "access_log": { - "Kind": { - "ListValue": { - "values": [ - { - "Kind": { - "StructValue": { - "fields": { - "config": { - "Kind": { - "StructValue": { - "fields": { - "path": { - "Kind": { - "StringValue": "/dev/stdout" - } - } - } - } - } - }, - "name": { - "Kind": { - "StringValue": "envoy.file_access_log" - } - } - } - } - } - } - ] - } - } - }, - "cluster": { - "Kind": { - "StringValue": "outbound|2000||s1tcp.none" - } - }, - "stat_prefix": { - "Kind": { - "StringValue": "outbound|2000||s1tcp.none" - } - } - } - } - } - } - ] - } - ], - "listener_filters": null - }, - "127.0.0.1_2005": { - "name": "127.0.0.1_2005", - "address": { - "Address": { - "SocketAddress": { - "address": "127.0.0.1", - "PortSpecifier": { - "PortValue": 2005 - } - } - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.tcp_proxy", - "ConfigType": { - "Config": { - "fields": { - "access_log": { - "Kind": { - "ListValue": { - "values": [ - { - "Kind": { - "StructValue": { - "fields": { - "config": { - "Kind": { - "StructValue": { - "fields": { - "path": { - "Kind": { - "StringValue": "/dev/stdout" - } - } - } - } - } - }, - "name": { - "Kind": { - "StringValue": "envoy.file_access_log" - } - } - } - } - } - } - ] - } - } - }, - "cluster": { - "Kind": { - "StringValue": "outbound|2005||s2.external.test.istio.io" - } - }, - "stat_prefix": { - "Kind": { - "StringValue": "outbound|2005||s2.external.test.istio.io" - } - } - } - } - } - } - ] - } - ], - "listener_filters": null - }, - "127.0.0.1_2006": { - "name": "127.0.0.1_2006", - "address": { - "Address": { - "SocketAddress": { - "address": "127.0.0.1", - "PortSpecifier": { - "PortValue": 2006 - } - } - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.tcp_proxy", - "ConfigType": { - "Config": { - "fields": { - "access_log": { - "Kind": { - "ListValue": { - "values": [ - { - "Kind": { - "StructValue": { - "fields": { - "config": { - "Kind": { - "StructValue": { - "fields": { - "path": { - "Kind": { - "StringValue": "/dev/stdout" - } - } - } - } - } - }, - "name": { - "Kind": { - "StringValue": "envoy.file_access_log" - } - } - } - } - } - } - ] - } - } - }, - "cluster": { - "Kind": { - "StringValue": "outbound|2006||s2dns.external.test.istio.io" - } - }, - "stat_prefix": { - "Kind": { - "StringValue": "outbound|2006||s2dns.external.test.istio.io" - } - } - } - } - } - } - ] - } - ], - "listener_filters": null - }, - "127.0.0.1_2007": { - "name": "127.0.0.1_2007", - "address": { - "Address": { - "SocketAddress": { - "address": "127.0.0.1", - "PortSpecifier": { - "PortValue": 2007 - } - } - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.tcp_proxy", - "ConfigType": { - "Config": { - "fields": { - "access_log": { - "Kind": { - "ListValue": { - "values": [ - { - "Kind": { - "StructValue": { - "fields": { - "config": { - "Kind": { - "StructValue": { - "fields": { - "path": { - "Kind": { - "StringValue": "/dev/stdout" - } - } - } - } - } - }, - "name": { - "Kind": { - "StringValue": "envoy.file_access_log" - } - } - } - } - } - } - ] - } - } - }, - "cluster": { - "Kind": { - "StringValue": "outbound|2007||tcpmeshdns.seexamples.svc" - } - }, - "stat_prefix": { - "Kind": { - "StringValue": "outbound|2007||tcpmeshdns.seexamples.svc" - } - } - } - } - } - } - ] - } - ], - "listener_filters": null - }, - "127.0.0.1_2008": { - "name": "127.0.0.1_2008", - "address": { - "Address": { - "SocketAddress": { - "address": "127.0.0.1", - "PortSpecifier": { - "PortValue": 2008 - } - } - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.tcp_proxy", - "ConfigType": { - "Config": { - "fields": { - "access_log": { - "Kind": { - "ListValue": { - "values": [ - { - "Kind": { - "StructValue": { - "fields": { - "config": { - "Kind": { - "StructValue": { - "fields": { - "path": { - "Kind": { - "StringValue": "/dev/stdout" - } - } - } - } - } - }, - "name": { - "Kind": { - "StringValue": "envoy.file_access_log" - } - } - } - } - } - } - ] - } - } - }, - "cluster": { - "Kind": { - "StringValue": "outbound|2008||tcpmeshstatic.seexamples.svc" - } - }, - "stat_prefix": { - "Kind": { - "StringValue": "outbound|2008||tcpmeshstatic.seexamples.svc" - } - } - } - } - } - } - ] - } - ], - "listener_filters": null - }, - "127.0.0.1_2009": { - "name": "127.0.0.1_2009", - "address": { - "Address": { - "SocketAddress": { - "address": "127.0.0.1", - "PortSpecifier": { - "PortValue": 2009 - } - } - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.tcp_proxy", - "ConfigType": { - "Config": { - "fields": { - "access_log": { - "Kind": { - "ListValue": { - "values": [ - { - "Kind": { - "StructValue": { - "fields": { - "config": { - "Kind": { - "StructValue": { - "fields": { - "path": { - "Kind": { - "StringValue": "/dev/stdout" - } - } - } - } - } - }, - "name": { - "Kind": { - "StringValue": "envoy.file_access_log" - } - } - } - } - } - } - ] - } - } - }, - "cluster": { - "Kind": { - "StringValue": "outbound|2009||tcpmeshstaticint.seexamples.svc" - } - }, - "stat_prefix": { - "Kind": { - "StringValue": "outbound|2009||tcpmeshstaticint.seexamples.svc" - } - } - } - } - } - } - ] - } - ], - "listener_filters": null - }, - "127.0.0.1_2443": { - "name": "127.0.0.1_2443", - "address": { - "Address": { - "SocketAddress": { - "address": "127.0.0.1", - "PortSpecifier": { - "PortValue": 2443 - } - } - } - }, - "filter_chains": [ - { - "filter_chain_match": { - "server_names": [ - "www1.googleapis.com" - ] - }, - "filters": [ - { - "name": "envoy.tcp_proxy", - "ConfigType": { - "Config": { - "fields": { - "access_log": { - "Kind": { - "ListValue": { - "values": [ - { - "Kind": { - "StructValue": { - "fields": { - "config": { - "Kind": { - "StructValue": { - "fields": { - "path": { - "Kind": { - "StringValue": "/dev/stdout" - } - } - } - } - } - }, - "name": { - "Kind": { - "StringValue": "envoy.file_access_log" - } - } - } - } - } - } - ] - } - } - }, - "cluster": { - "Kind": { - "StringValue": "outbound|2443||www1.googleapis.com" - } - }, - "stat_prefix": { - "Kind": { - "StringValue": "outbound|2443||www1.googleapis.com" - } - } - } - } - } - } - ] - }, - { - "filter_chain_match": { - "server_names": [ - "api1.facebook.com" - ] - }, - "filters": [ - { - "name": "envoy.tcp_proxy", - "ConfigType": { - "Config": { - "fields": { - "access_log": { - "Kind": { - "ListValue": { - "values": [ - { - "Kind": { - "StructValue": { - "fields": { - "config": { - "Kind": { - "StructValue": { - "fields": { - "path": { - "Kind": { - "StringValue": "/dev/stdout" - } - } - } - } - } - }, - "name": { - "Kind": { - "StringValue": "envoy.file_access_log" - } - } - } - } - } - } - ] - } - } - }, - "cluster": { - "Kind": { - "StringValue": "outbound|2443||api1.facebook.com" - } - }, - "stat_prefix": { - "Kind": { - "StringValue": "outbound|2443||api1.facebook.com" - } - } - } - } - } - } - ] - } - ], - "listener_filters": [ - { - "name": "envoy.listener.tls_inspector", - "ConfigType": null - } - ] - }, - "virtual": { - "name": "virtual", - "address": { - "Address": { - "SocketAddress": { - "address": "0.0.0.0", - "PortSpecifier": { - "PortValue": 15001 - } - } - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.tcp_proxy", - "ConfigType": { - "Config": { - "fields": { - "cluster": { - "Kind": { - "StringValue": "BlackHoleCluster" - } - }, - "stat_prefix": { - "Kind": { - "StringValue": "BlackHoleCluster" - } - } - } - } - } - } - ] - } - ], - "use_original_dst": { - "value": true - }, - "listener_filters": null - } - } \ No newline at end of file diff --git a/pilot/pkg/xds/testdata/none_rds.json b/pilot/pkg/xds/testdata/none_rds.json deleted file mode 100644 index e707e3a41..000000000 --- a/pilot/pkg/xds/testdata/none_rds.json +++ /dev/null @@ -1,112 +0,0 @@ -{ - "2001": { - "name": "2001", - "virtual_hosts": [ - { - "name": "s1http.none:2001", - "domains": [ - "s1http.none", - "s1http.none:2001" - ], - "routes": [ - { - "match": { - "PathSpecifier": { - "Prefix": "/" - } - }, - "Action": { - "Route": { - "ClusterSpecifier": { - "Cluster": "outbound|2001||s1http.none" - }, - "HostRewriteSpecifier": null, - "timeout": 0, - "retry_policy": { - "retry_on": "connect-failure,refused-stream,unavailable,cancelled,resource-exhausted,retriable-status-codes", - "num_retries": { - "value": 2 - }, - "retry_host_predicate": [ - { - "name": "envoy.retry_host_predicates.previous_hosts", - "ConfigType": null - } - ], - "host_selection_retry_max_attempts": 3, - "retriable_status_codes": [ - 503 - ] - }, - "max_grpc_timeout": 0 - } - }, - "decorator": { - "operation": "s1http.none:2001/*" - }, - "per_filter_config": { - } - } - ] - } - ], - "validate_clusters": {} - }, - "7071": { - "name": "7071", - "virtual_hosts": null, - "validate_clusters": {} - }, - "http_proxy": { - "name": "http_proxy", - "virtual_hosts": [ - { - "name": "s1http.none:2001", - "domains": [ - "s1http.none:2001" - ], - "routes": [ - { - "match": { - "PathSpecifier": { - "Prefix": "/" - } - }, - "Action": { - "Route": { - "ClusterSpecifier": { - "Cluster": "outbound|2001||s1http.none" - }, - "HostRewriteSpecifier": null, - "timeout": 0, - "retry_policy": { - "retry_on": "connect-failure,refused-stream,unavailable,cancelled,resource-exhausted,retriable-status-codes", - "num_retries": { - "value": 2 - }, - "retry_host_predicate": [ - { - "name": "envoy.retry_host_predicates.previous_hosts", - "ConfigType": null - } - ], - "host_selection_retry_max_attempts": 3, - "retriable_status_codes": [ - 503 - ] - }, - "max_grpc_timeout": 0 - } - }, - "decorator": { - "operation": "s1http.none:2001/*" - }, - "per_filter_config": { - } - } - ] - } - ], - "validate_clusters": {} - } - } \ No newline at end of file diff --git a/pilot/pkg/xds/util.go b/pilot/pkg/xds/util.go deleted file mode 100644 index 4f941b095..000000000 --- a/pilot/pkg/xds/util.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - networkingapi "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" -) - -// getSubSetLabels returns the labels associated with a subset of a given service. -func getSubSetLabels(dr *networkingapi.DestinationRule, subsetName string) labels.Instance { - // empty subset - if subsetName == "" { - return nil - } - - if dr == nil { - return nil - } - - for _, subset := range dr.Subsets { - if subset.Name == subsetName { - if len(subset.Labels) == 0 { - return nil - } - return subset.Labels - } - } - - return nil -} diff --git a/pilot/pkg/xds/v3/model.go b/pilot/pkg/xds/v3/model.go deleted file mode 100644 index a3bab3f02..000000000 --- a/pilot/pkg/xds/v3/model.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package v3 - -import ( - "strings" -) - -import ( - resource "github.com/envoyproxy/go-control-plane/pkg/resource/v3" -) - -const ( - envoyTypePrefix = resource.APITypePrefix + "envoy." - - ClusterType = resource.ClusterType - EndpointType = resource.EndpointType - ListenerType = resource.ListenerType - RouteType = resource.RouteType - SecretType = resource.SecretType - ExtensionConfigurationType = resource.ExtensionConfigType - - NameTableType = resource.APITypePrefix + "istio.networking.nds.v1.NameTable" - HealthInfoType = resource.APITypePrefix + "istio.v1.HealthInformation" - ProxyConfigType = resource.APITypePrefix + "istio.mesh.v1alpha1.ProxyConfig" - // DebugType requests debug info from istio, a secured implementation for istio debug interface. - DebugType = "istio.io/debug" - BootstrapType = resource.APITypePrefix + "envoy.config.bootstrap.v3.Bootstrap" - - // nolint - HttpProtocolOptionsType = "envoy.extensions.upstreams.http.v3.HttpProtocolOptions" - DubboServiceNameMappingType = "dubbo.networking.v1alpha1.v1.servicenamemapping" -) - -// GetShortType returns an abbreviated form of a type, useful for logging or human friendly messages -func GetShortType(typeURL string) string { - switch typeURL { - case ClusterType: - return "CDS" - case ListenerType: - return "LDS" - case RouteType: - return "RDS" - case EndpointType: - return "EDS" - case SecretType: - return "SDS" - case NameTableType: - return "NDS" - case ProxyConfigType: - return "PCDS" - case ExtensionConfigurationType: - return "ECDS" - default: - return typeURL - } -} - -// GetMetricType returns the form of a type reported for metrics -func GetMetricType(typeURL string) string { - switch typeURL { - case ClusterType: - return "cds" - case ListenerType: - return "lds" - case RouteType: - return "rds" - case EndpointType: - return "eds" - case SecretType: - return "sds" - case NameTableType: - return "nds" - case ProxyConfigType: - return "pcds" - case ExtensionConfigurationType: - return "ecds" - case BootstrapType: - return "bds" - default: - return typeURL - } -} - -// IsEnvoyType checks whether the typeURL is a valid Envoy type. -func IsEnvoyType(typeURL string) bool { - return strings.HasPrefix(typeURL, envoyTypePrefix) -} diff --git a/pilot/pkg/xds/xds_cache_test.go b/pilot/pkg/xds/xds_cache_test.go deleted file mode 100644 index 3c9fe6899..000000000 --- a/pilot/pkg/xds/xds_cache_test.go +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "fmt" - "math/rand" - "reflect" - "testing" - "time" -) - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "go.uber.org/atomic" - any "google.golang.org/protobuf/types/known/anypb" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -var ( - any1 = &discovery.Resource{Resource: &any.Any{TypeUrl: "foo"}} - any2 = &discovery.Resource{Resource: &any.Any{TypeUrl: "bar"}} -) - -// TestXdsCacheToken is a regression test to ensure that we do not write -func TestXdsCacheToken(t *testing.T) { - c := model.NewXdsCache() - n := atomic.NewInt32(0) - mkv := func(n int32) *discovery.Resource { - return &discovery.Resource{Resource: &any.Any{TypeUrl: fmt.Sprint(n)}} - } - k := EndpointBuilder{clusterName: "key", service: &model.Service{ - Hostname: "foo.com", - }} - work := func(start time.Time, n int32) { - v := mkv(n) - time.Sleep(time.Millisecond * time.Duration(rand.Intn(100))) - req := &model.PushRequest{Start: start} - c.Add(k, req, v) - } - // 5 round of xds push - for vals := 0; vals < 5; vals++ { - c.ClearAll() - n.Inc() - start := time.Now() - for i := 0; i < 5; i++ { - go work(start, n.Load()) - } - retry.UntilOrFail(t, func() bool { - val, f := c.Get(k) - return f && val.Resource.TypeUrl == fmt.Sprint(n.Load()) - }) - for i := 0; i < 5; i++ { - val, f := c.Get(k) - if !f { - t.Fatalf("no cache found") - } - if f && val.Resource.TypeUrl != fmt.Sprint(n.Load()) { - t.Fatalf("got bad write: %v", val.Resource.TypeUrl) - } - time.Sleep(time.Millisecond * time.Duration(rand.Intn(20))) - } - } -} - -func TestXdsCache(t *testing.T) { - ep1 := EndpointBuilder{ - clusterName: "outbound|1||foo.com", - service: &model.Service{ - Hostname: "foo.com", - }, - } - ep2 := EndpointBuilder{ - clusterName: "outbound|2||foo.com", - service: &model.Service{ - Hostname: "foo.com", - }, - } - t.Run("simple", func(t *testing.T) { - c := model.NewLenientXdsCache() - c.Add(ep1, &model.PushRequest{Start: time.Now()}, any1) - if !reflect.DeepEqual(c.Keys(), []string{ep1.Key()}) { - t.Fatalf("unexpected keys: %v, want %v", c.Keys(), ep1.Key()) - } - if got, _ := c.Get(ep1); got != any1 { - t.Fatalf("unexpected result: %v, want %v", got, any1) - } - c.Add(ep1, &model.PushRequest{Start: time.Now()}, any2) - if got, _ := c.Get(ep1); got != any2 { - t.Fatalf("unexpected result: %v, want %v", got, any2) - } - - c.Clear(map[model.ConfigKey]struct{}{{Kind: gvk.ServiceEntry, Name: "foo.com"}: {}}) - if _, f := c.Get(ep1); f { - t.Fatalf("unexpected result, found key when not expected: %v", c.Keys()) - } - }) - - t.Run("multiple hostnames", func(t *testing.T) { - c := model.NewLenientXdsCache() - start := time.Now() - c.Add(ep1, &model.PushRequest{Start: start}, any1) - c.Add(ep2, &model.PushRequest{Start: start}, any2) - - if got, _ := c.Get(ep1); got != any1 { - t.Fatalf("unexpected result: %v, want %v", got, any1) - } - if got, _ := c.Get(ep2); got != any2 { - t.Fatalf("unexpected result: %v, want %v", got, any2) - } - c.Clear(map[model.ConfigKey]struct{}{{Kind: gvk.ServiceEntry, Name: "foo.com"}: {}}) - if _, f := c.Get(ep1); f { - t.Fatalf("unexpected result, found key when not expected: %v", c.Keys()) - } - if _, f := c.Get(ep2); f { - t.Fatalf("unexpected result, found key when not expected: %v", c.Keys()) - } - }) - - t.Run("multiple destinationRules", func(t *testing.T) { - c := model.NewLenientXdsCache() - - ep1 := ep1 - ep1.destinationRule = &config.Config{Meta: config.Meta{Name: "a", Namespace: "b"}} - ep2 := ep2 - ep2.destinationRule = &config.Config{Meta: config.Meta{Name: "b", Namespace: "b"}} - start := time.Now() - c.Add(ep1, &model.PushRequest{Start: start}, any1) - c.Add(ep2, &model.PushRequest{Start: start}, any2) - if got, _ := c.Get(ep1); got != any1 { - t.Fatalf("unexpected result: %v, want %v", got, any1) - } - if got, _ := c.Get(ep2); got != any2 { - t.Fatalf("unexpected result: %v, want %v", got, any2) - } - c.Clear(map[model.ConfigKey]struct{}{{Kind: gvk.DestinationRule, Name: "a", Namespace: "b"}: {}}) - if _, f := c.Get(ep1); f { - t.Fatalf("unexpected result, found key when not expected: %v", c.Keys()) - } - if got, _ := c.Get(ep2); got != any2 { - t.Fatalf("unexpected result: %v, want %v", got, any2) - } - c.Clear(map[model.ConfigKey]struct{}{{Kind: gvk.DestinationRule, Name: "b", Namespace: "b"}: {}}) - if _, f := c.Get(ep1); f { - t.Fatalf("unexpected result, found key when not expected: %v", c.Keys()) - } - if _, f := c.Get(ep2); f { - t.Fatalf("unexpected result, found key when not expected: %v", c.Keys()) - } - }) - - t.Run("clear all", func(t *testing.T) { - c := model.NewLenientXdsCache() - start := time.Now() - c.Add(ep1, &model.PushRequest{Start: start}, any1) - c.Add(ep2, &model.PushRequest{Start: start}, any2) - - c.ClearAll() - if len(c.Keys()) != 0 { - t.Fatalf("expected no keys, got: %v", c.Keys()) - } - if _, f := c.Get(ep1); f { - t.Fatalf("unexpected result, found key when not expected: %v", c.Keys()) - } - if _, f := c.Get(ep2); f { - t.Fatalf("unexpected result, found key when not expected: %v", c.Keys()) - } - }) - - t.Run("dependent type clears all", func(t *testing.T) { - c := model.NewLenientXdsCache() - start := time.Now() - c.Add(ep1, &model.PushRequest{Start: start}, any1) - c.Add(ep2, &model.PushRequest{Start: start}, any2) - - c.Clear(map[model.ConfigKey]struct{}{{Kind: gvk.PeerAuthentication}: {}}) - if len(c.Keys()) != 0 { - t.Fatalf("expected no keys, got: %v", c.Keys()) - } - if _, f := c.Get(ep1); f { - t.Fatalf("unexpected result, found key when not expected: %v", c.Keys()) - } - if _, f := c.Get(ep2); f { - t.Fatalf("unexpected result, found key when not expected: %v", c.Keys()) - } - }) - - t.Run("write without token does nothing", func(t *testing.T) { - c := model.NewLenientXdsCache() - c.Add(ep1, &model.PushRequest{}, any1) - if got, f := c.Get(ep1); f { - t.Fatalf("unexpected result: %v, want none", got) - } - }) - - t.Run("write with evicted token", func(t *testing.T) { - c := model.NewLenientXdsCache() - t1 := time.Now() - t2 := t1.Add(1 * time.Nanosecond) - c.Add(ep1, &model.PushRequest{Start: t2}, any1) - c.Add(ep1, &model.PushRequest{Start: t1}, any2) - if len(c.Keys()) != 1 { - t.Fatalf("expected 1 keys, got: %v", c.Keys()) - } - if got, _ := c.Get(ep1); got != any1 { - t.Fatalf("unexpected result: %v, want %v", got, any1) - } - }) - - t.Run("write with expired token", func(t *testing.T) { - c := model.NewLenientXdsCache() - t1 := time.Now() - t2 := t1.Add(-1 * time.Nanosecond) - - c.Add(ep1, &model.PushRequest{Start: t1}, any1) - c.ClearAll() - // prevented, this is stale token - c.Add(ep1, &model.PushRequest{Start: t2}, any2) - if got, _ := c.Get(ep1); got != nil { - t.Fatalf("expected no cache, but got %v", got) - } - }) - - t.Run("disallow write with stale token after clear", func(t *testing.T) { - c := model.NewLenientXdsCache() - t1 := time.Now() - - c.Add(ep1, &model.PushRequest{Start: t1}, any1) - c.ClearAll() - // prevented, this can be stale data after `disallowCacheSameToken` - c.Add(ep1, &model.PushRequest{Start: t1}, any2) - if got, _ := c.Get(ep1); got != nil { - t.Fatalf("expected no cache, but got %v", got) - } - - // cache with newer token - c.Add(ep1, &model.PushRequest{Start: time.Now()}, any1) - if got, _ := c.Get(ep1); got != any1 { - t.Fatalf("unexpected result: %v, want %v", got, any1) - } - }) -} diff --git a/pilot/pkg/xds/xds_test.go b/pilot/pkg/xds/xds_test.go deleted file mode 100644 index ff418e63a..000000000 --- a/pilot/pkg/xds/xds_test.go +++ /dev/null @@ -1,559 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "fmt" - "os" - "path" - "testing" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/structpath" -) - -type SidecarTestConfig struct { - ImportedNamespaces []string - Resolution string - IngressListener bool -} - -var scopeConfig = ` -apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - name: sidecar - namespace: app -spec: -{{- if .IngressListener }} - ingress: - - port: - number: 9080 - protocol: HTTP - name: custom-http - defaultEndpoint: unix:///var/run/someuds.sock -{{- end }} - egress: - - hosts: -{{ range $i, $ns := .ImportedNamespaces }} - - {{$ns}} -{{ end }} ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: app - namespace: app -spec: - hosts: - - app.com - ports: - - number: 80 - name: http - protocol: HTTP - resolution: {{.Resolution}} - endpoints: -{{- if eq .Resolution "DNS" }} - - address: app.com -{{- else }} - - address: 1.1.1.1 -{{- end }} ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: excluded - namespace: excluded -spec: - hosts: - - app.com - ports: - - number: 80 - name: http - protocol: HTTP - resolution: {{.Resolution}} - endpoints: -{{- if eq .Resolution "DNS" }} - - address: excluded.com -{{- else }} - - address: 9.9.9.9 -{{- end }} ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: included - namespace: included -spec: - hosts: - - app.com - ports: - - number: 80 - name: http - protocol: HTTP - resolution: {{.Resolution}} - endpoints: -{{- if eq .Resolution "DNS" }} - - address: included.com -{{- else }} - - address: 2.2.2.2 -{{- end }} ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: app-https - namespace: app -spec: - hosts: - - app.cluster.local - addresses: - - 5.5.5.5 - ports: - - number: 443 - name: https - protocol: HTTPS - resolution: {{.Resolution}} - endpoints: -{{- if eq .Resolution "DNS" }} - - address: app.com -{{- else }} - - address: 10.10.10.10 -{{- end }} ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: excluded-https - namespace: excluded -spec: - hosts: - - app.cluster.local - addresses: - - 5.5.5.5 - ports: - - number: 4431 - name: https - protocol: HTTPS - resolution: {{.Resolution}} - endpoints: -{{- if eq .Resolution "DNS" }} - - address: app.com -{{- else }} - - address: 10.10.10.10 -{{- end }} -` - -// TestServiceScoping is a high level test ensuring the Sidecar scoping works correctly, especially when -// there are multiple hostnames that are in different namespaces. -func TestServiceScoping(t *testing.T) { - baseProxy := func() *model.Proxy { - return &model.Proxy{ - Metadata: &model.NodeMetadata{}, - ID: "app.app", - Type: model.SidecarProxy, - IPAddresses: []string{"1.1.1.1"}, - ConfigNamespace: "app", - } - } - - t.Run("STATIC", func(t *testing.T) { - s := NewFakeDiscoveryServer(t, FakeOptions{ - ConfigString: scopeConfig, - ConfigTemplateInput: SidecarTestConfig{ - ImportedNamespaces: []string{"./*", "included/*"}, - Resolution: "STATIC", - }, - }) - proxy := s.SetupProxy(baseProxy()) - - endpoints := xdstest.ExtractLoadAssignments(s.Endpoints(proxy)) - if !listEqualUnordered(endpoints["outbound|80||app.com"], []string{"1.1.1.1:80"}) { - t.Fatalf("expected 1.1.1.1, got %v", endpoints["outbound|80||app.com"]) - } - - assertListEqual(t, xdstest.ExtractListenerNames(s.Listeners(proxy)), []string{ - "0.0.0.0_80", - "5.5.5.5_443", - "virtualInbound", - "virtualOutbound", - }) - }) - - t.Run("Ingress Listener", func(t *testing.T) { - s := NewFakeDiscoveryServer(t, FakeOptions{ - ConfigString: scopeConfig, - ConfigTemplateInput: SidecarTestConfig{ - ImportedNamespaces: []string{"./*", "included/*"}, - Resolution: "STATIC", - IngressListener: true, - }, - }) - p := baseProxy() - // Change the node's IP so that it does not match with any service entry - p.IPAddresses = []string{"100.100.100.100"} - proxy := s.SetupProxy(p) - - endpoints := xdstest.ExtractClusterEndpoints(s.Clusters(proxy)) - eps := endpoints["inbound|9080||"] - if !listEqualUnordered(eps, []string{"/var/run/someuds.sock"}) { - t.Fatalf("expected /var/run/someuds.sock, got %v", eps) - } - - assertListEqual(t, xdstest.ExtractListenerNames(s.Listeners(proxy)), []string{ - "0.0.0.0_80", - "5.5.5.5_443", - "virtualInbound", - "virtualOutbound", - }) - }) - - t.Run("DNS", func(t *testing.T) { - s := NewFakeDiscoveryServer(t, FakeOptions{ - ConfigString: scopeConfig, - ConfigTemplateInput: SidecarTestConfig{ - ImportedNamespaces: []string{"./*", "included/*"}, - Resolution: "DNS", - }, - }) - proxy := s.SetupProxy(baseProxy()) - - assertListEqual(t, xdstest.ExtractClusterEndpoints(s.Clusters(proxy))["outbound|80||app.com"], []string{"app.com:80"}) - }) - - t.Run("DNS no self import", func(t *testing.T) { - s := NewFakeDiscoveryServer(t, FakeOptions{ - ConfigString: scopeConfig, - ConfigTemplateInput: SidecarTestConfig{ - ImportedNamespaces: []string{"included/*"}, - Resolution: "DNS", - }, - }) - proxy := s.SetupProxy(baseProxy()) - - assertListEqual(t, xdstest.ExtractClusterEndpoints(s.Clusters(proxy))["outbound|80||app.com"], []string{"included.com:80"}) - }) -} - -func TestSidecarListeners(t *testing.T) { - t.Run("empty", func(t *testing.T) { - s := NewFakeDiscoveryServer(t, FakeOptions{}) - proxy := s.SetupProxy(&model.Proxy{ - IPAddresses: []string{"10.2.0.1"}, - ID: "app3.testns", - }) - structpath.ForProto(xdstest.ToDiscoveryResponse(s.Listeners(proxy))). - Exists("{.resources[?(@.address.socketAddress.portValue==15001)]}"). - Select("{.resources[?(@.address.socketAddress.portValue==15001)]}"). - Equals("virtualOutbound", "{.name}"). - Equals("0.0.0.0", "{.address.socketAddress.address}"). - Equals(wellknown.TCPProxy, "{.filterChains[1].filters[0].name}"). - Equals("PassthroughCluster", "{.filterChains[1].filters[0].typedConfig.cluster}"). - Equals("PassthroughCluster", "{.filterChains[1].filters[0].typedConfig.statPrefix}"). - Equals(true, "{.useOriginalDst}"). - CheckOrFail(t) - }) - - t.Run("mongo", func(t *testing.T) { - s := NewFakeDiscoveryServer(t, FakeOptions{ - ConfigString: mustReadFile(t, "./tests/testdata/config/se-example.yaml"), - }) - proxy := s.SetupProxy(&model.Proxy{ - IPAddresses: []string{"10.2.0.1"}, - ID: "app3.testns", - }) - structpath.ForProto(xdstest.ToDiscoveryResponse(s.Listeners(proxy))). - Exists("{.resources[?(@.address.socketAddress.portValue==27018)]}"). - Select("{.resources[?(@.address.socketAddress.portValue==27018)]}"). - Equals("0.0.0.0", "{.address.socketAddress.address}"). - // Example doing a struct comparison, note the pain with oneofs.... - Equals(&core.SocketAddress{ - Address: "0.0.0.0", - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: uint32(27018), - }, - }, "{.address.socketAddress}"). - Select("{.filterChains[0].filters[0]}"). - Equals("envoy.mongo_proxy", "{.name}"). - Select("{.typedConfig}"). - Exists("{.statPrefix}"). - CheckOrFail(t) - }) -} - -func TestEgressProxy(t *testing.T) { - s := NewFakeDiscoveryServer(t, FakeOptions{ - ConfigString: ` -# Add a random endpoint, otherwise there will be no routes to check -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: pod -spec: - hosts: - - pod.pod.svc.cluster.local - ports: - - number: 80 - name: http - protocol: HTTP - resolution: STATIC - location: MESH_INTERNAL - endpoints: - - address: 10.10.10.20 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - name: sidecar-with-egressproxy - namespace: app -spec: - outboundTrafficPolicy: - mode: ALLOW_ANY - egressProxy: - host: foo.bar - subset: shiny - port: - number: 5000 - egress: - - hosts: - - "*/*" -`, - }) - proxy := s.SetupProxy(&model.Proxy{ - ConfigNamespace: "app", - }) - - listeners := s.Listeners(proxy) - assertListEqual(t, xdstest.ExtractListenerNames(listeners), []string{ - "0.0.0.0_80", - "virtualInbound", - "virtualOutbound", - }) - - expectedEgressCluster := "outbound|5000|shiny|foo.bar" - - found := false - for _, f := range xdstest.ExtractListener("virtualOutbound", listeners).FilterChains { - // We want to check the match all filter chain, as this is testing the fallback logic - if f.FilterChainMatch != nil { - continue - } - tcp := xdstest.ExtractTCPProxy(t, f) - if tcp.GetCluster() != expectedEgressCluster { - t.Fatalf("got unexpected fallback destination: %v, want %v", tcp.GetCluster(), expectedEgressCluster) - } - found = true - } - if !found { - t.Fatalf("failed to find tcp proxy") - } - - found = false - routes := s.Routes(proxy) - for _, rc := range routes { - for _, vh := range rc.GetVirtualHosts() { - if vh.GetName() == "allow_any" { - for _, r := range vh.GetRoutes() { - if expectedEgressCluster == r.GetRoute().GetCluster() { - found = true - break - } - } - break - } - } - } - if !found { - t.Fatalf("failed to find expected fallthrough route") - } -} - -func assertListEqual(t test.Failer, a, b []string) { - t.Helper() - if !listEqualUnordered(a, b) { - t.Fatalf("Expected list %v to be equal to %v", a, b) - } -} - -func mustReadFile(t *testing.T, f string) string { - b, err := os.ReadFile(path.Join(env.IstioSrc, f)) - if err != nil { - t.Fatalf("failed to read %v: %v", f, err) - } - return string(b) -} - -func TestClusterLocal(t *testing.T) { - tests := map[string]struct { - fakeOpts FakeOptions - serviceCluster string - wantClusterLocal map[cluster.ID][]string - wantNonClusterLocal map[cluster.ID][]string - }{ - // set up a k8s service in each cluster, with a pod in each cluster and a workloadentry in cluster-1 - "k8s service with pod and workloadentry": { - fakeOpts: func() FakeOptions { - k8sObjects := map[cluster.ID]string{ - "cluster-1": "", - "cluster-2": "", - } - i := 1 - for range k8sObjects { - clusterID := fmt.Sprintf("cluster-%d", i) - k8sObjects[cluster.ID(clusterID)] = fmt.Sprintf(` -apiVersion: v1 -kind: Service -metadata: - labels: - app: echo-app - name: echo-app - namespace: default -spec: - clusterIP: 1.2.3.4 - selector: - app: echo-app - ports: - - name: grpc - port: 7070 ---- -apiVersion: v1 -kind: Pod -metadata: - labels: - app: echo-app - name: echo-app-%s - namespace: default ---- -apiVersion: v1 -kind: Endpoints -metadata: - name: echo-app - namespace: default - labels: - app: echo-app -subsets: -- addresses: - - ip: 10.0.0.%d - ports: - - name: grpc - port: 7070 -`, clusterID, i) - i++ - } - return FakeOptions{ - DefaultClusterName: "cluster-1", - KubernetesObjectStringByCluster: k8sObjects, - ConfigString: ` -apiVersion: networking.istio.io/v1alpha3 -kind: WorkloadEntry -metadata: - name: echo-app - namespace: default -spec: - address: 10.1.1.1 - labels: - app: echo-app -`, - } - }(), - serviceCluster: "outbound|7070||echo-app.default.svc.cluster.local", - wantClusterLocal: map[cluster.ID][]string{ - "cluster-1": {"10.0.0.1:7070", "10.1.1.1:7070"}, - "cluster-2": {"10.0.0.2:7070"}, - }, - wantNonClusterLocal: map[cluster.ID][]string{ - "cluster-1": {"10.0.0.1:7070", "10.1.1.1:7070", "10.0.0.2:7070"}, - "cluster-2": {"10.0.0.1:7070", "10.1.1.1:7070", "10.0.0.2:7070"}, - }, - }, - "serviceentry": { - fakeOpts: FakeOptions{ - ConfigString: ` -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: external-svc-mongocluster -spec: - hosts: - - mymongodb.somedomain - addresses: - - 192.192.192.192/24 # VIPs - ports: - - number: 27018 - name: mongodb - protocol: MONGO - location: MESH_INTERNAL - resolution: STATIC - endpoints: - - address: 2.2.2.2 - - address: 3.3.3.3 -`, - }, - serviceCluster: "outbound|27018||mymongodb.somedomain", - wantClusterLocal: map[cluster.ID][]string{ - "Kubernetes": {"2.2.2.2:27018", "3.3.3.3:27018"}, - "other": {}, - }, - wantNonClusterLocal: map[cluster.ID][]string{ - "Kubernetes": {"2.2.2.2:27018", "3.3.3.3:27018"}, - "other": {"2.2.2.2:27018", "3.3.3.3:27018"}, - }, - }, - } - - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - for _, local := range []bool{true, false} { - name := "cluster-local" - want := tt.wantClusterLocal - if !local { - name = "non-cluster-local" - want = tt.wantNonClusterLocal - } - t.Run(name, func(t *testing.T) { - meshConfig := mesh.DefaultMeshConfig() - meshConfig.ServiceSettings = []*v1alpha1.MeshConfig_ServiceSettings{ - {Hosts: []string{"*"}, Settings: &v1alpha1.MeshConfig_ServiceSettings_Settings{ - ClusterLocal: local, - }}, - } - fakeOpts := tt.fakeOpts - fakeOpts.MeshConfig = meshConfig - s := NewFakeDiscoveryServer(t, fakeOpts) - for clusterID := range want { - p := s.SetupProxy(&model.Proxy{Metadata: &model.NodeMetadata{ClusterID: clusterID}}) - eps := xdstest.ExtractLoadAssignments(s.Endpoints(p))[tt.serviceCluster] - if want := want[clusterID]; !listEqualUnordered(eps, want) { - t.Errorf("got %v but want %v for %s", eps, want, clusterID) - } - } - }) - } - }) - } -} diff --git a/pilot/pkg/xds/xdsgen.go b/pilot/pkg/xds/xdsgen.go deleted file mode 100644 index 46e91f1d6..000000000 --- a/pilot/pkg/xds/xdsgen.go +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "encoding/json" - "strconv" - "strings" - "time" -) - -import ( - corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "istio.io/pkg/env" - istioversion "istio.io/pkg/version" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" -) - -// IstioControlPlaneInstance defines the format Istio uses for when creating Envoy config.core.v3.ControlPlane.identifier -type IstioControlPlaneInstance struct { - // The Istio component type (e.g. "istiod") - Component string - // The ID of the component instance - ID string - // The Istio version - Info istioversion.BuildInfo -} - -var controlPlane *corev3.ControlPlane - -// ControlPlane identifies the instance and Istio version. -func ControlPlane() *corev3.ControlPlane { - return controlPlane -} - -func init() { - // The Pod Name (instance identity) is in PilotArgs, but not reachable globally nor from DiscoveryServer - podName := env.RegisterStringVar("POD_NAME", "", "").Get() - byVersion, err := json.Marshal(IstioControlPlaneInstance{ - Component: "istiod", - ID: podName, - Info: istioversion.Info, - }) - if err != nil { - log.Warnf("XDS: Could not serialize control plane id: %v", err) - } - controlPlane = &corev3.ControlPlane{Identifier: string(byVersion)} -} - -func (s *DiscoveryServer) findGenerator(typeURL string, con *Connection) model.XdsResourceGenerator { - if g, f := s.Generators[con.proxy.Metadata.Generator+"/"+typeURL]; f { - return g - } - - if g, f := s.Generators[typeURL]; f { - return g - } - - // XdsResourceGenerator is the default generator for this connection. We want to allow - // some types to use custom generators - for example EDS. - g := con.proxy.XdsResourceGenerator - if g == nil { - if strings.HasPrefix(typeURL, "istio.io/debug/") { - g = s.Generators["event"] - } else { - // TODO move this to just directly using the resource TypeUrl - g = s.Generators["api"] // default to "MCP" generators - any type supported by store - } - } - return g -} - -// Push an XDS resource for the given connection. Configuration will be generated -// based on the passed in generator. Based on the updates field, generators may -// choose to send partial or even no response if there are no changes. -func (s *DiscoveryServer) pushXds(con *Connection, w *model.WatchedResource, req *model.PushRequest) error { - if w == nil { - return nil - } - gen := s.findGenerator(w.TypeUrl, con) - if gen == nil { - return nil - } - - t0 := time.Now() - - // If delta is set, client is requesting new resources or removing old ones. We should just generate the - // new resources it needs, rather than the entire set of known resources. - // Note: we do not need to account for unsubscribed resources as these are handled by parent removal; - // See https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol#deleting-resources. - // This means if there are only removals, we will not respond. - var logFiltered string - if !req.Delta.IsEmpty() && features.PartialFullPushes && - !con.proxy.IsProxylessGrpc() { - logFiltered = " filtered:" + strconv.Itoa(len(w.ResourceNames)-len(req.Delta.Subscribed)) - w = &model.WatchedResource{ - TypeUrl: w.TypeUrl, - ResourceNames: req.Delta.Subscribed.UnsortedList(), - } - } - - res, logdata, err := gen.Generate(con.proxy, w, req) - if err != nil || res == nil { - // If we have nothing to send, report that we got an ACK for this version. - if s.StatusReporter != nil { - s.StatusReporter.RegisterEvent(con.conID, w.TypeUrl, req.Push.LedgerVersion) - } - return err - } - defer func() { recordPushTime(w.TypeUrl, time.Since(t0)) }() - - resp := &discovery.DiscoveryResponse{ - ControlPlane: ControlPlane(), - TypeUrl: w.TypeUrl, - // TODO: send different version for incremental eds - VersionInfo: req.Push.PushVersion, - Nonce: nonce(req.Push.LedgerVersion), - Resources: model.ResourcesToAny(res), - } - - configSize := ResourceSize(res) - configSizeBytes.With(typeTag.Value(w.TypeUrl)).Record(float64(configSize)) - - ptype := "PUSH" - info := "" - if logdata.Incremental { - ptype = "PUSH INC" - } - if len(logdata.AdditionalInfo) > 0 { - info = " " + logdata.AdditionalInfo - } - if len(logFiltered) > 0 { - info += logFiltered - } - - if err := con.send(resp); err != nil { - if recordSendError(w.TypeUrl, err) { - log.Warnf("%s: Send failure for node:%s resources:%d size:%s%s: %v", - v3.GetShortType(w.TypeUrl), con.proxy.ID, len(res), util.ByteCount(configSize), info, err) - } - return err - } - - switch { - case logdata.Incremental: - if log.DebugEnabled() { - log.Debugf("%s: %s%s for node:%s resources:%d size:%s%s", - v3.GetShortType(w.TypeUrl), ptype, req.PushReason(), con.proxy.ID, len(res), util.ByteCount(configSize), info) - } - default: - debug := "" - if log.DebugEnabled() { - // Add additional information to logs when debug mode enabled. - debug = " nonce:" + resp.Nonce + " version:" + resp.VersionInfo - } - log.Infof("%s: %s%s for node:%s resources:%d size:%v%s%s", v3.GetShortType(w.TypeUrl), ptype, req.PushReason(), con.proxy.ID, len(res), - util.ByteCount(ResourceSize(res)), info, debug) - } - - return nil -} - -func ResourceSize(r model.Resources) int { - // Approximate size by looking at the Any marshaled size. This avoids high cost - // proto.Size, at the expense of slightly under counting. - size := 0 - for _, r := range r { - size += len(r.Resource.Value) - } - return size -} diff --git a/pilot/test/mock/config.go b/pilot/test/mock/config.go deleted file mode 100644 index 05c374517..000000000 --- a/pilot/test/mock/config.go +++ /dev/null @@ -1,488 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mock - -import ( - "fmt" - "reflect" - "strconv" - "testing" - "time" -) - -import ( - "go.uber.org/atomic" - networking "istio.io/api/networking/v1alpha3" - authz "istio.io/api/security/v1beta1" - api "istio.io/api/type/v1beta1" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - config2 "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/test/config" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -var ( - // ExampleVirtualService is an example V2 route rule - ExampleVirtualService = &networking.VirtualService{ - Hosts: []string{"prod", "test"}, - Http: []*networking.HTTPRoute{ - { - Route: []*networking.HTTPRouteDestination{ - { - Destination: &networking.Destination{ - Host: "job", - }, - Weight: 80, - }, - }, - }, - }, - } - - ExampleServiceEntry = &networking.ServiceEntry{ - Hosts: []string{"*.google.com"}, - Resolution: networking.ServiceEntry_NONE, - Ports: []*networking.Port{ - {Number: 80, Name: "http-name", Protocol: "http"}, - {Number: 8080, Name: "http2-name", Protocol: "http2"}, - }, - } - - ExampleGateway = &networking.Gateway{ - Servers: []*networking.Server{ - { - Hosts: []string{"google.com"}, - Port: &networking.Port{Name: "http", Protocol: "http", Number: 10080}, - }, - }, - } - - // ExampleDestinationRule is an example destination rule - ExampleDestinationRule = &networking.DestinationRule{ - Host: "ratings", - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: new(networking.LoadBalancerSettings_Simple), - }, - }, - } - - // ExampleAuthorizationPolicy is an example AuthorizationPolicy - ExampleAuthorizationPolicy = &authz.AuthorizationPolicy{ - Selector: &api.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - "version": "v1", - }, - }, - } - - mockGvk = collections.Mock.Resource().GroupVersionKind() -) - -// Make creates a mock config indexed by a number -func Make(namespace string, i int) config2.Config { - name := fmt.Sprintf("%s%d", "mock-config", i) - return config2.Config{ - Meta: config2.Meta{ - GroupVersionKind: mockGvk, - Name: name, - Namespace: namespace, - Labels: map[string]string{ - "key": name, - }, - Annotations: map[string]string{ - "annotationkey": name, - }, - }, - Spec: &config.MockConfig{ - Key: name, - Pairs: []*config.ConfigPair{ - {Key: "key", Value: strconv.Itoa(i)}, - }, - }, - } -} - -// Compare checks two configs ignoring revisions and creation time -func Compare(a, b config2.Config) bool { - a.ResourceVersion = "" - b.ResourceVersion = "" - a.CreationTimestamp = time.Time{} - b.CreationTimestamp = time.Time{} - return reflect.DeepEqual(a, b) -} - -// CheckMapInvariant validates operational invariants of an empty config registry -func CheckMapInvariant(r model.ConfigStore, t *testing.T, namespace string, n int) { - // check that the config descriptor is the mock config descriptor - _, contains := r.Schemas().FindByGroupVersionKind(mockGvk) - if !contains { - t.Fatal("expected config mock types") - } - log.Info("Created mock descriptor") - - // create configuration objects - elts := make(map[int]config2.Config) - for i := 0; i < n; i++ { - elts[i] = Make(namespace, i) - } - log.Info("Make mock objects") - - // post all elements - for _, elt := range elts { - if _, err := r.Create(elt); err != nil { - t.Error(err) - } - } - log.Info("Created mock objects") - - revs := make(map[int]string) - - // check that elements are stored - for i, elt := range elts { - v1 := r.Get(mockGvk, elt.Name, elt.Namespace) - if v1 == nil || !Compare(elt, *v1) { - t.Errorf("wanted %v, got %v", elt, v1) - } else { - revs[i] = v1.ResourceVersion - } - } - - log.Info("Got stored elements") - - if _, err := r.Create(elts[0]); err == nil { - t.Error("expected error posting twice") - } - - invalid := config2.Config{ - Meta: config2.Meta{ - GroupVersionKind: mockGvk, - Name: "invalid", - ResourceVersion: revs[0], - }, - Spec: &config.MockConfig{}, - } - - missing := config2.Config{ - Meta: config2.Meta{ - GroupVersionKind: mockGvk, - Name: "missing", - ResourceVersion: revs[0], - }, - Spec: &config.MockConfig{Key: "missing"}, - } - - if _, err := r.Create(config2.Config{}); err == nil { - t.Error("expected error posting empty object") - } - - if _, err := r.Create(invalid); err == nil { - t.Error("expected error posting invalid object") - } - - if _, err := r.Update(config2.Config{}); err == nil { - t.Error("expected error updating empty object") - } - - if _, err := r.Update(invalid); err == nil { - t.Error("expected error putting invalid object") - } - - if _, err := r.Update(missing); err == nil { - t.Error("expected error putting missing object with a missing key") - } - - // check for missing type - if l, _ := r.List(config2.GroupVersionKind{}, namespace); len(l) > 0 { - t.Errorf("unexpected objects for missing type") - } - - // check for missing element - if cfg := r.Get(mockGvk, "missing", ""); cfg != nil { - t.Error("unexpected configuration object found") - } - - // check for missing element - if cfg := r.Get(config2.GroupVersionKind{}, "missing", ""); cfg != nil { - t.Error("unexpected configuration object found") - } - - // delete missing elements - if err := r.Delete(config2.GroupVersionKind{}, "missing", "", nil); err == nil { - t.Error("expected error on deletion of missing type") - } - - // delete missing elements - if err := r.Delete(mockGvk, "missing", "", nil); err == nil { - t.Error("expected error on deletion of missing element") - } - if err := r.Delete(mockGvk, "missing", "unknown", nil); err == nil { - t.Error("expected error on deletion of missing element in unknown namespace") - } - - // list elements - l, err := r.List(mockGvk, namespace) - if err != nil { - t.Errorf("List error %#v, %v", l, err) - } - if len(l) != n { - t.Errorf("wanted %d element(s), got %d in %v", n, len(l), l) - } - - // update all elements - for i := 0; i < n; i++ { - elt := Make(namespace, i) - elt.Spec.(*config.MockConfig).Pairs[0].Value += "(updated)" - elt.ResourceVersion = revs[i] - elts[i] = elt - if _, err = r.Update(elt); err != nil { - t.Error(err) - } - } - - // check that elements are stored - for i, elt := range elts { - v1 := r.Get(mockGvk, elts[i].Name, elts[i].Namespace) - if v1 == nil || !Compare(elt, *v1) { - t.Errorf("wanted %v, got %v", elt, v1) - } - } - - // delete all elements - for i := range elts { - if err = r.Delete(mockGvk, elts[i].Name, elts[i].Namespace, nil); err != nil { - t.Error(err) - } - } - log.Info("Delete elements") - - l, err = r.List(mockGvk, namespace) - if err != nil { - t.Error(err) - } - if len(l) != 0 { - t.Errorf("wanted 0 element(s), got %d in %v", len(l), l) - } - log.Info("Test done, deleting namespace") -} - -// CheckIstioConfigTypes validates that an empty store can ingest Istio config objects -func CheckIstioConfigTypes(store model.ConfigStore, namespace string, t *testing.T) { - configName := "example" - // Global scoped policies like MeshPolicy are not isolated, can't be - // run as part of the normal test suites - if needed they should - // be run in separate environment. The test suites are setting cluster - // scoped policies that may interfere and would require serialization - - cases := []struct { - name string - configName string - schema collection.Schema - spec config2.Spec - }{ - {"VirtualService", configName, collections.IstioNetworkingV1Alpha3Virtualservices, ExampleVirtualService}, - {"DestinationRule", configName, collections.IstioNetworkingV1Alpha3Destinationrules, ExampleDestinationRule}, - {"ServiceEntry", configName, collections.IstioNetworkingV1Alpha3Serviceentries, ExampleServiceEntry}, - {"Gateway", configName, collections.IstioNetworkingV1Alpha3Gateways, ExampleGateway}, - {"AuthorizationPolicy", configName, collections.IstioSecurityV1Beta1Authorizationpolicies, ExampleAuthorizationPolicy}, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - configMeta := config2.Meta{ - GroupVersionKind: c.schema.Resource().GroupVersionKind(), - Name: c.configName, - } - if !c.schema.Resource().IsClusterScoped() { - configMeta.Namespace = namespace - } - - if _, err := store.Create(config2.Config{ - Meta: configMeta, - Spec: c.spec, - }); err != nil { - t.Errorf("Post(%v) => got %v", c.name, err) - } - }) - } -} - -// CheckCacheEvents validates operational invariants of a cache -func CheckCacheEvents(store model.ConfigStore, cache model.ConfigStoreController, namespace string, n int, t *testing.T) { - n64 := int64(n) - stop := make(chan struct{}) - defer close(stop) - added, deleted := atomic.NewInt64(0), atomic.NewInt64(0) - cache.RegisterEventHandler(mockGvk, func(_, _ config2.Config, ev model.Event) { - switch ev { - case model.EventAdd: - if deleted.Load() != 0 { - t.Errorf("Events are not serialized (add)") - } - added.Inc() - case model.EventDelete: - if added.Load() != n64 { - t.Errorf("Events are not serialized (delete)") - } - deleted.Inc() - } - log.Infof("Added %d, deleted %d", added.Load(), deleted.Load()) - }) - go cache.Run(stop) - - // run map invariant sequence - CheckMapInvariant(store, t, namespace, n) - - log.Infof("Waiting till all events are received") - retry.UntilOrFail(t, func() bool { - return added.Load() == n64 && deleted.Load() == n64 - }, retry.Message("receive events"), retry.Delay(time.Millisecond*500), retry.Timeout(time.Minute)) -} - -// CheckCacheFreshness validates operational invariants of a cache -func CheckCacheFreshness(cache model.ConfigStoreController, namespace string, t *testing.T) { - stop := make(chan struct{}) - done := make(chan bool) - o := Make(namespace, 0) - - // validate cache consistency - cache.RegisterEventHandler(mockGvk, func(_, config config2.Config, ev model.Event) { - elts, _ := cache.List(mockGvk, namespace) - elt := cache.Get(o.GroupVersionKind, o.Name, o.Namespace) - switch ev { - case model.EventAdd: - if len(elts) != 1 { - t.Errorf("Got %#v, expected %d element(s) on Add event", elts, 1) - } - if elt == nil || !reflect.DeepEqual(elt.Spec, o.Spec) { - t.Errorf("Got %#v, expected %#v", elt, o) - } - - log.Infof("Calling Update(%s)", config.Key()) - revised := Make(namespace, 1) - revised.Meta = elt.Meta - if _, err := cache.Update(revised); err != nil { - t.Error(err) - } - case model.EventUpdate: - if len(elts) != 1 { - t.Errorf("Got %#v, expected %d element(s) on Update event", elts, 1) - } - if elt == nil { - t.Errorf("Got %#v, expected nonempty", elt) - } - - log.Infof("Calling Delete(%s)", config.Key()) - if err := cache.Delete(mockGvk, config.Name, config.Namespace, nil); err != nil { - t.Error(err) - } - case model.EventDelete: - if len(elts) != 0 { - t.Errorf("Got %#v, expected zero elements on Delete event", elts) - } - log.Infof("Stopping channel for (%#v)", config.Key()) - close(stop) - done <- true - } - }) - - go cache.Run(stop) - - // try warm-up with empty Get - if cfg := cache.Get(config2.GroupVersionKind{}, "example", namespace); cfg != nil { - t.Error("unexpected result for unknown type") - } - - // add and remove - log.Infof("Calling Create(%#v)", o) - if _, err := cache.Create(o); err != nil { - t.Error(err) - } - - timeout := time.After(10 * time.Second) - select { - case <-timeout: - t.Fatalf("timeout waiting to be done") - case <-done: - return - } -} - -// CheckCacheSync validates operational invariants of a cache against the -// non-cached client. -func CheckCacheSync(store model.ConfigStore, cache model.ConfigStoreController, namespace string, n int, t *testing.T) { - keys := make(map[int]config2.Config) - // add elements directly through client - for i := 0; i < n; i++ { - keys[i] = Make(namespace, i) - if _, err := store.Create(keys[i]); err != nil { - t.Error(err) - } - } - - // check in the controller cache - stop := make(chan struct{}) - defer close(stop) - go cache.Run(stop) - retry.UntilOrFail(t, cache.HasSynced, retry.Message("HasSynced")) - os, _ := cache.List(mockGvk, namespace) - if len(os) != n { - t.Errorf("cache.List => Got %d, expected %d", len(os), n) - } - - // remove elements directly through client - for i := 0; i < n; i++ { - if err := store.Delete(mockGvk, keys[i].Name, keys[i].Namespace, nil); err != nil { - t.Error(err) - } - } - - // check again in the controller cache - retry.UntilOrFail(t, func() bool { - os, _ = cache.List(mockGvk, namespace) - log.Infof("cache.List => Got %d, expected %d", len(os), 0) - return len(os) == 0 - }, retry.Message("no elements in cache")) - - // now add through the controller - for i := 0; i < n; i++ { - if _, err := cache.Create(Make(namespace, i)); err != nil { - t.Error(err) - } - } - - // check directly through the client - retry.UntilOrFail(t, func() bool { - cs, _ := cache.List(mockGvk, namespace) - os, _ := store.List(mockGvk, namespace) - log.Infof("cache.List => Got %d, expected %d", len(cs), n) - log.Infof("store.List => Got %d, expected %d", len(os), n) - return len(os) == n && len(cs) == n - }, retry.Message("cache and backing store match")) - - // remove elements directly through the client - for i := 0; i < n; i++ { - if err := store.Delete(mockGvk, keys[i].Name, keys[i].Namespace, nil); err != nil { - t.Error(err) - } - } -} diff --git a/pilot/test/util/diff.go b/pilot/test/util/diff.go deleted file mode 100644 index 25783b3d1..000000000 --- a/pilot/test/util/diff.go +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "errors" - "os" - "regexp" - "strings" -) - -import ( - "github.com/pmezard/go-difflib/difflib" - "istio.io/pkg/env" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/file" - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -const ( - statusReplacement = "sidecar.istio.io/status: '{\"version\":\"\"," -) - -var statusPattern = regexp.MustCompile("sidecar.istio.io/status: '{\"version\":\"([0-9a-f]+)\",") - -// Refresh controls whether to update the golden artifacts instead. -// It is set using the environment variable REFRESH_GOLDEN. -func Refresh() bool { - return env.RegisterBoolVar("REFRESH_GOLDEN", false, "").Get() -} - -// Compare compares two byte slices. It returns an error with a -// contextual diff if they are not equal. -func Compare(content, golden []byte) error { - data := strings.TrimSpace(string(content)) - expected := strings.TrimSpace(string(golden)) - - if data != expected { - diff := difflib.UnifiedDiff{ - A: difflib.SplitLines(expected), - B: difflib.SplitLines(data), - Context: 2, - } - text, err := difflib.GetUnifiedDiffString(diff) - if err != nil { - return err - } - return errors.New(text) - } - - return nil -} - -// CompareYAML compares a file "x" against a golden file "x.golden" -func CompareYAML(t test.Failer, filename string) { - t.Helper() - content, err := os.ReadFile(filename) - if err != nil { - t.Fatalf(err.Error()) - } - goldenFile := filename + ".golden" - if Refresh() { - t.Logf("Refreshing golden file for %s", filename) - if err = os.WriteFile(goldenFile, content, 0o644); err != nil { - t.Fatal(err.Error()) - } - } - - golden, err := os.ReadFile(goldenFile) - if err != nil { - t.Fatalf(err.Error()) - } - if err = Compare(content, golden); err != nil { - t.Fatalf("Failed validating artifact %s:\n%v", filename, err) - } -} - -// CompareContent compares the content value against the golden file and fails the test if they differ -func CompareContent(t test.Failer, content []byte, goldenFile string) { - t.Helper() - golden := ReadGoldenFile(t, content, goldenFile) - CompareBytes(t, content, golden, goldenFile) -} - -// ReadGoldenFile reads the content of the golden file and fails the test if an error is encountered -func ReadGoldenFile(t test.Failer, content []byte, goldenFile string) []byte { - t.Helper() - RefreshGoldenFile(t, content, goldenFile) - - return ReadFile(t, goldenFile) -} - -// StripVersion strips the version fields of a YAML content. -func StripVersion(yaml []byte) []byte { - return statusPattern.ReplaceAllLiteral(yaml, []byte(statusReplacement)) -} - -// RefreshGoldenFile updates the golden file with the given content -func RefreshGoldenFile(t test.Failer, content []byte, goldenFile string) { - if Refresh() { - t.Logf("Refreshing golden file %s", goldenFile) - if err := file.AtomicWrite(goldenFile, content, os.FileMode(0o644)); err != nil { - t.Fatal(err.Error()) - } - } -} - -// ReadFile reads the content of the given file or fails the test if an error is encountered. -func ReadFile(t test.Failer, file string) []byte { - t.Helper() - golden, err := os.ReadFile(file) - if err != nil { - t.Fatal(err.Error()) - } - return golden -} - -// CompareBytes compares the content value against the golden bytes and fails the test if they differ -func CompareBytes(t test.Failer, content []byte, golden []byte, name string) { - t.Helper() - if err := Compare(content, golden); err != nil { - t.Fatalf("Failed validating golden file %s:\n%v", name, err) - } -} diff --git a/pilot/test/xdstest/extract.go b/pilot/test/xdstest/extract.go deleted file mode 100644 index 5f248b5d1..000000000 --- a/pilot/test/xdstest/extract.go +++ /dev/null @@ -1,433 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xdstest - -import ( - "fmt" - "reflect" - "sort" -) - -import ( - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - tcpproxy "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3" - tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "google.golang.org/protobuf/proto" - any "google.golang.org/protobuf/types/known/anypb" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -func ExtractRoutesFromListeners(ll []*listener.Listener) []string { - routes := []string{} - for _, l := range ll { - for _, fc := range l.FilterChains { - for _, filter := range fc.Filters { - if filter.Name == wellknown.HTTPConnectionManager { - hcon := &hcm.HttpConnectionManager{} - if err := filter.GetTypedConfig().UnmarshalTo(hcon); err != nil { - continue - } - switch r := hcon.GetRouteSpecifier().(type) { - case *hcm.HttpConnectionManager_Rds: - routes = append(routes, r.Rds.RouteConfigName) - } - } - } - } - } - return routes -} - -// ExtractSecretResources fetches all referenced SDS resource names from a list of clusters and listeners -func ExtractSecretResources(t test.Failer, rs []*any.Any) []string { - resourceNames := sets.New() - for _, r := range rs { - switch r.TypeUrl { - case v3.ClusterType: - c := &cluster.Cluster{} - if err := r.UnmarshalTo(c); err != nil { - t.Fatal(err) - } - sockets := []*core.TransportSocket{} - if c.TransportSocket != nil { - sockets = append(sockets, c.TransportSocket) - } - for _, ts := range c.TransportSocketMatches { - sockets = append(sockets, ts.TransportSocket) - } - for _, s := range sockets { - tl := &tls.UpstreamTlsContext{} - if err := s.GetTypedConfig().UnmarshalTo(tl); err != nil { - t.Fatal(err) - } - resourceNames.Insert(tl.GetCommonTlsContext().GetCombinedValidationContext().GetValidationContextSdsSecretConfig().GetName()) - for _, s := range tl.GetCommonTlsContext().GetTlsCertificateSdsSecretConfigs() { - resourceNames.Insert(s.GetName()) - } - } - case v3.ListenerType: - l := &listener.Listener{} - if err := r.UnmarshalTo(l); err != nil { - t.Fatal(err) - } - sockets := []*core.TransportSocket{} - for _, fc := range l.GetFilterChains() { - if fc.GetTransportSocket() != nil { - sockets = append(sockets, fc.GetTransportSocket()) - } - } - if ts := l.GetDefaultFilterChain().GetTransportSocket(); ts != nil { - sockets = append(sockets, ts) - } - for _, s := range sockets { - tl := &tls.DownstreamTlsContext{} - if err := s.GetTypedConfig().UnmarshalTo(tl); err != nil { - t.Fatal(err) - } - resourceNames.Insert(tl.GetCommonTlsContext().GetCombinedValidationContext().GetValidationContextSdsSecretConfig().GetName()) - for _, s := range tl.GetCommonTlsContext().GetTlsCertificateSdsSecretConfigs() { - resourceNames.Insert(s.GetName()) - } - } - } - } - resourceNames.Delete("") - ls := resourceNames.UnsortedList() - sort.Sort(sort.Reverse(sort.StringSlice(ls))) - return ls -} - -func ExtractListenerNames(ll []*listener.Listener) []string { - res := []string{} - for _, l := range ll { - res = append(res, l.Name) - } - return res -} - -func ExtractListener(name string, ll []*listener.Listener) *listener.Listener { - for _, l := range ll { - if l.Name == name { - return l - } - } - return nil -} - -func ExtractVirtualHosts(rc *route.RouteConfiguration) map[string][]string { - res := map[string][]string{} - for _, vh := range rc.GetVirtualHosts() { - var dests []string - for _, r := range vh.Routes { - if dc := r.GetRoute().GetCluster(); dc != "" { - dests = append(dests, dc) - } - } - sort.Strings(dests) - for _, d := range vh.Domains { - res[d] = dests - } - } - return res -} - -func ExtractRouteConfigurations(rc []*route.RouteConfiguration) map[string]*route.RouteConfiguration { - res := map[string]*route.RouteConfiguration{} - for _, l := range rc { - res[l.Name] = l - } - return res -} - -func ExtractListenerFilters(l *listener.Listener) map[string]*listener.ListenerFilter { - res := map[string]*listener.ListenerFilter{} - for _, lf := range l.ListenerFilters { - res[lf.Name] = lf - } - return res -} - -func ExtractFilterChain(name string, l *listener.Listener) *listener.FilterChain { - for _, f := range l.GetFilterChains() { - if f.GetName() == name { - return f - } - } - return nil -} - -func ExtractFilterChainNames(l *listener.Listener) []string { - res := []string{} - for _, f := range l.GetFilterChains() { - res = append(res, f.GetName()) - } - return res -} - -func ExtractFilterNames(t test.Failer, fcs *listener.FilterChain) ([]string, []string) { - nwFilters := []string{} - httpFilters := []string{} - for _, fc := range fcs.Filters { - if fc.Name == wellknown.HTTPConnectionManager { - h := &hcm.HttpConnectionManager{} - if fc.GetTypedConfig() != nil { - if err := fc.GetTypedConfig().UnmarshalTo(h); err != nil { - t.Fatalf("failed to unmarshal hcm: %v", err) - } - } - for _, hf := range h.HttpFilters { - httpFilters = append(httpFilters, hf.Name) - } - } - nwFilters = append(nwFilters, fc.Name) - } - return nwFilters, httpFilters -} - -func ExtractTCPProxy(t test.Failer, fcs *listener.FilterChain) *tcpproxy.TcpProxy { - for _, fc := range fcs.Filters { - if fc.Name == wellknown.TCPProxy { - tcpProxy := &tcpproxy.TcpProxy{} - if fc.GetTypedConfig() != nil { - if err := fc.GetTypedConfig().UnmarshalTo(tcpProxy); err != nil { - t.Fatalf("failed to unmarshal tcp proxy: %v", err) - } - } - return tcpProxy - } - } - return nil -} - -func ExtractHTTPConnectionManager(t test.Failer, fcs *listener.FilterChain) *hcm.HttpConnectionManager { - for _, fc := range fcs.Filters { - if fc.Name == wellknown.HTTPConnectionManager { - h := &hcm.HttpConnectionManager{} - if fc.GetTypedConfig() != nil { - if err := fc.GetTypedConfig().UnmarshalTo(h); err != nil { - t.Fatalf("failed to unmarshal hcm: %v", err) - } - } - return h - } - } - return nil -} - -func ExtractLoadAssignments(cla []*endpoint.ClusterLoadAssignment) map[string][]string { - got := map[string][]string{} - for _, cla := range cla { - if cla == nil { - continue - } - got[cla.ClusterName] = append(got[cla.ClusterName], ExtractEndpoints(cla)...) - } - return got -} - -// ExtractHealthEndpoints returns all health and unhealth endpoints -func ExtractHealthEndpoints(cla *endpoint.ClusterLoadAssignment) ([]string, []string) { - if cla == nil { - return nil, nil - } - healthy := []string{} - unhealthy := []string{} - for _, ep := range cla.Endpoints { - for _, lb := range ep.LbEndpoints { - if lb.HealthStatus == core.HealthStatus_HEALTHY { - if lb.GetEndpoint().Address.GetSocketAddress() != nil { - healthy = append(healthy, fmt.Sprintf("%s:%d", - lb.GetEndpoint().Address.GetSocketAddress().Address, lb.GetEndpoint().Address.GetSocketAddress().GetPortValue())) - } else { - healthy = append(healthy, lb.GetEndpoint().Address.GetPipe().Path) - } - } else { - if lb.GetEndpoint().Address.GetSocketAddress() != nil { - unhealthy = append(unhealthy, fmt.Sprintf("%s:%d", - lb.GetEndpoint().Address.GetSocketAddress().Address, lb.GetEndpoint().Address.GetSocketAddress().GetPortValue())) - } else { - unhealthy = append(unhealthy, lb.GetEndpoint().Address.GetPipe().Path) - } - } - } - } - return healthy, unhealthy -} - -// ExtractEndpoints returns all endpoints in the load assignment (including unhealthy endpoints) -func ExtractEndpoints(cla *endpoint.ClusterLoadAssignment) []string { - h, uh := ExtractHealthEndpoints(cla) - h = append(h, uh...) - return h -} - -func ExtractClusters(cc []*cluster.Cluster) map[string]*cluster.Cluster { - res := map[string]*cluster.Cluster{} - for _, c := range cc { - res[c.Name] = c - } - return res -} - -func ExtractCluster(name string, cc []*cluster.Cluster) *cluster.Cluster { - return ExtractClusters(cc)[name] -} - -func ExtractClusterEndpoints(clusters []*cluster.Cluster) map[string][]string { - cla := []*endpoint.ClusterLoadAssignment{} - for _, c := range clusters { - cla = append(cla, c.LoadAssignment) - } - return ExtractLoadAssignments(cla) -} - -func ExtractEdsClusterNames(cl []*cluster.Cluster) []string { - res := []string{} - for _, c := range cl { - switch v := c.ClusterDiscoveryType.(type) { - case *cluster.Cluster_Type: - if v.Type != cluster.Cluster_EDS { - continue - } - } - res = append(res, c.Name) - } - return res -} - -func ExtractTLSSecrets(t test.Failer, secrets []*any.Any) map[string]*tls.Secret { - res := map[string]*tls.Secret{} - for _, a := range secrets { - scrt := &tls.Secret{} - if err := a.UnmarshalTo(scrt); err != nil { - t.Fatal(err) - } - res[scrt.Name] = scrt - } - return res -} - -func UnmarshalRouteConfiguration(t test.Failer, resp []*any.Any) []*route.RouteConfiguration { - un := make([]*route.RouteConfiguration, 0, len(resp)) - for _, r := range resp { - u := &route.RouteConfiguration{} - if err := r.UnmarshalTo(u); err != nil { - t.Fatal(err) - } - un = append(un, u) - } - return un -} - -func UnmarshalClusterLoadAssignment(t test.Failer, resp []*any.Any) []*endpoint.ClusterLoadAssignment { - un := make([]*endpoint.ClusterLoadAssignment, 0, len(resp)) - for _, r := range resp { - u := &endpoint.ClusterLoadAssignment{} - if err := r.UnmarshalTo(u); err != nil { - t.Fatal(err) - } - un = append(un, u) - } - return un -} - -func FilterClusters(cl []*cluster.Cluster, f func(c *cluster.Cluster) bool) []*cluster.Cluster { - res := make([]*cluster.Cluster, 0, len(cl)) - for _, c := range cl { - if f(c) { - res = append(res, c) - } - } - return res -} - -func ToDiscoveryResponse(p interface{}) *discovery.DiscoveryResponse { - slice := InterfaceSlice(p) - if len(slice) == 0 { - return &discovery.DiscoveryResponse{} - } - resources := make([]*any.Any, 0, len(slice)) - for _, v := range slice { - resources = append(resources, util.MessageToAny(v.(proto.Message))) - } - return &discovery.DiscoveryResponse{ - Resources: resources, - TypeUrl: resources[0].TypeUrl, - } -} - -func InterfaceSlice(slice interface{}) []interface{} { - s := reflect.ValueOf(slice) - if s.Kind() != reflect.Slice { - panic("InterfaceSlice() given a non-slice type") - } - - ret := make([]interface{}, s.Len()) - - for i := 0; i < s.Len(); i++ { - ret[i] = s.Index(i).Interface() - } - - return ret -} - -// DumpList will dump a list of protos. To workaround go type issues, call DumpList(t, InterfaceSlice([]proto.Message)) -func DumpList(t test.Failer, protoList []interface{}) []string { - res := []string{} - for _, i := range protoList { - p, ok := i.(proto.Message) - if !ok { - t.Fatalf("expected proto, got %T", i) - } - res = append(res, Dump(t, p)) - } - return res -} - -func Dump(t test.Failer, p proto.Message) string { - v := reflect.ValueOf(p) - if p == nil || (v.Kind() == reflect.Ptr && v.IsNil()) { - return "nil" - } - s, err := protomarshal.ToJSONWithIndent(p, " ") - if err != nil { - t.Fatal(err) - } - return s -} - -func MapKeys(mp interface{}) []string { - keys := reflect.ValueOf(mp).MapKeys() - res := []string{} - for _, k := range keys { - res = append(res, k.String()) - } - sort.Strings(res) - return res -} diff --git a/pilot/test/xdstest/grpc.go b/pilot/test/xdstest/grpc.go deleted file mode 100644 index 458679ff5..000000000 --- a/pilot/test/xdstest/grpc.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xdstest - -import ( - "context" - "time" -) - -import ( - "google.golang.org/grpc" - "istio.io/pkg/log" -) - -func safeSleep(ctx context.Context, t time.Duration) { - select { - case <-time.After(t): - case <-ctx.Done(): - } -} - -type slowClientStream struct { - grpc.ClientStream - recv, send time.Duration -} - -func (w *slowClientStream) RecvMsg(m interface{}) error { - if w.recv > 0 { - safeSleep(w.Context(), w.recv) - log.Infof("delayed recv for %v", w.recv) - } - return w.ClientStream.RecvMsg(m) -} - -func (w *slowClientStream) SendMsg(m interface{}) error { - if w.send > 0 { - safeSleep(w.Context(), w.send) - log.Infof("delayed send for %v", w.send) - } - return w.ClientStream.SendMsg(m) -} - -// SlowClientInterceptor is an interceptor that allows injecting delays on Send and Recv -func SlowClientInterceptor(recv, send time.Duration) grpc.StreamClientInterceptor { - return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, - method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { - clientStream, err := streamer(ctx, desc, cc, method, opts...) - return &slowClientStream{clientStream, recv, send}, err - } -} - -type slowServerStream struct { - grpc.ServerStream - recv, send time.Duration -} - -func (w *slowServerStream) RecvMsg(m interface{}) error { - if w.recv > 0 { - safeSleep(w.Context(), w.recv) - log.Infof("delayed recv for %v", w.recv) - } - return w.ServerStream.RecvMsg(m) -} - -func (w *slowServerStream) SendMsg(m interface{}) error { - if w.send > 0 { - safeSleep(w.Context(), w.send) - log.Infof("delayed send for %v", w.send) - } - return w.ServerStream.SendMsg(m) -} - -// SlowServerInterceptor is an interceptor that allows injecting delays on Send and Recv -func SlowServerInterceptor(recv, send time.Duration) grpc.StreamServerInterceptor { - return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { - return handler(srv, &slowServerStream{ss, recv, send}) - } -} diff --git a/pilot/test/xdstest/mock_discovery.go b/pilot/test/xdstest/mock_discovery.go deleted file mode 100644 index 8bba17019..000000000 --- a/pilot/test/xdstest/mock_discovery.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xdstest - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "google.golang.org/grpc" - "google.golang.org/grpc/test/bufconn" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -// MockDiscovery is a DiscoveryServer that allows users full control over responses. -type MockDiscovery struct { - Listener *bufconn.Listener - responses chan *discovery.DiscoveryResponse - deltaResponses chan *discovery.DeltaDiscoveryResponse - close chan struct{} -} - -func NewMockServer(t test.Failer) *MockDiscovery { - s := &MockDiscovery{ - close: make(chan struct{}), - responses: make(chan *discovery.DiscoveryResponse), - deltaResponses: make(chan *discovery.DeltaDiscoveryResponse), - } - - buffer := 1024 * 1024 - listener := bufconn.Listen(buffer) - grpcServer := grpc.NewServer() - discovery.RegisterAggregatedDiscoveryServiceServer(grpcServer, s) - go func() { - if err := grpcServer.Serve(listener); err != nil && !(err == grpc.ErrServerStopped || err.Error() == "closed") { - t.Fatal(err) - } - }() - t.Cleanup(func() { - grpcServer.Stop() - close(s.close) - }) - s.Listener = listener - return s -} - -func (f *MockDiscovery) StreamAggregatedResources(server discovery.AggregatedDiscoveryService_StreamAggregatedResourcesServer) error { - numberOfSends := 0 - for { - select { - case <-f.close: - return nil - case resp := <-f.responses: - numberOfSends++ - log.Infof("sending response from mock: %v", numberOfSends) - if err := server.Send(resp); err != nil { - return err - } - } - } -} - -func (f *MockDiscovery) DeltaAggregatedResources(server discovery.AggregatedDiscoveryService_DeltaAggregatedResourcesServer) error { - numberOfSends := 0 - for { - select { - case <-f.close: - return nil - case resp := <-f.deltaResponses: - numberOfSends++ - log.Infof("sending delta response from mock: %v", numberOfSends) - if err := server.Send(resp); err != nil { - return err - } - } - } -} - -// SendResponse sends a response to a (random) client. This can block if sends are blocked. -func (f *MockDiscovery) SendResponse(dr *discovery.DiscoveryResponse) { - f.responses <- dr -} - -// SendDeltaResponse sends a response to a (random) client. This can block if sends are blocked. -func (f *MockDiscovery) SendDeltaResponse(dr *discovery.DeltaDiscoveryResponse) { - f.deltaResponses <- dr -} - -var _ discovery.AggregatedDiscoveryServiceServer = &MockDiscovery{} diff --git a/pilot/test/xdstest/test.go b/pilot/test/xdstest/test.go deleted file mode 100644 index f6df8bb69..000000000 --- a/pilot/test/xdstest/test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xdstest - -import ( - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" -) - -// EvaluateListenerFilterPredicates runs through the ListenerFilterChainMatchPredicate logic -// This is exposed for testing only, and should not be used in XDS generation code -func EvaluateListenerFilterPredicates(predicate *listener.ListenerFilterChainMatchPredicate, port int) bool { - if predicate == nil { - return true - } - switch r := predicate.Rule.(type) { - case *listener.ListenerFilterChainMatchPredicate_NotMatch: - return !EvaluateListenerFilterPredicates(r.NotMatch, port) - case *listener.ListenerFilterChainMatchPredicate_OrMatch: - matches := false - for _, r := range r.OrMatch.Rules { - matches = matches || EvaluateListenerFilterPredicates(r, port) - } - return matches - case *listener.ListenerFilterChainMatchPredicate_DestinationPortRange: - return int32(port) >= r.DestinationPortRange.GetStart() && int32(port) < r.DestinationPortRange.GetEnd() - default: - panic("unsupported predicate") - } -} diff --git a/pilot/test/xdstest/validate.go b/pilot/test/xdstest/validate.go deleted file mode 100644 index e4d7ab66b..000000000 --- a/pilot/test/xdstest/validate.go +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xdstest - -import ( - "strings" - "testing" -) - -import ( - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/testing/protocmp" - "k8s.io/apimachinery/pkg/util/sets" -) - -import ( - xdsfilters "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/filters" -) - -func ValidateListeners(t testing.TB, ls []*listener.Listener) { - t.Helper() - found := sets.String{} - for _, l := range ls { - if found.Has(l.Name) { - t.Errorf("duplicate listener name %v", l.Name) - } - found.Insert(l.Name) - ValidateListener(t, l) - } -} - -func ValidateListener(t testing.TB, l *listener.Listener) { - t.Helper() - if err := l.Validate(); err != nil { - t.Errorf("listener %v is invalid: %v", l.Name, err) - } - validateInspector(t, l) - validateListenerTLS(t, l) - validateFilterChainMatch(t, l) - validateInboundListener(t, l) -} - -func validateInboundListener(t testing.TB, l *listener.Listener) { - if l.GetAddress().GetSocketAddress().GetPortValue() != 15006 { - // Not an inbound port - return - } - for i, fc := range l.GetFilterChains() { - if fc.FilterChainMatch == nil { - t.Errorf("nil filter chain %d", i) - continue - } - if fc.FilterChainMatch.TransportProtocol == "" && fc.FilterChainMatch.GetDestinationPort().GetValue() != 15006 { - // Not setting transport protocol may lead to unexpected matching behavior due to https://github.com/istio/istio/issues/26079 - // This is not *always* a bug, just a guideline - the 15006 blocker filter chain doesn't follow this rule and is exluced. - t.Errorf("filter chain %d had no transport protocol set", i) - } - } -} - -func validateFilterChainMatch(t testing.TB, l *listener.Listener) { - t.Helper() - - // Check for duplicate filter chains, to avoid "multiple filter chains with the same matching rules are defined" error - for i1, l1 := range l.FilterChains { - for i2, l2 := range l.FilterChains { - if i1 == i2 { - continue - } - // We still create virtual inbound listeners before merging into single inbound - // This hack skips these ones, as they will be processed later - if hcm := ExtractHTTPConnectionManager(t, l1); strings.HasPrefix(hcm.GetStatPrefix(), "inbound_") && l.Name != "virtualInbound" { - continue - } - if cmp.Equal(l1.FilterChainMatch, l2.FilterChainMatch, protocmp.Transform()) { - fcms := []string{} - for _, fc := range l.FilterChains { - fcms = append(fcms, Dump(t, fc.GetFilterChainMatch())) - } - t.Errorf("overlapping filter chains %d and %d:\n%v\n Full listener: %v", i1, i2, strings.Join(fcms, ",\n"), Dump(t, l)) - } - } - } - - // Due to the trie based logic of FCM, an unset field is only a wildcard if no - // other FCM sets it. Therefore, we should ensure we explicitly set the FCM on - // all match clauses if its set on any other match clause See - // https://github.com/envoyproxy/envoy/issues/12572 for details - destPorts := sets.NewInt() - for _, fc := range l.FilterChains { - if fc.GetFilterChainMatch().GetDestinationPort() != nil { - destPorts.Insert(int(fc.GetFilterChainMatch().GetDestinationPort().GetValue())) - } - } - for _, p := range destPorts.List() { - hasTLSInspector := false - for _, fc := range l.FilterChains { - if p == int(fc.GetFilterChainMatch().GetDestinationPort().GetValue()) && fc.GetFilterChainMatch().GetTransportProtocol() != "" { - hasTLSInspector = true - } - } - if hasTLSInspector { - for _, fc := range l.FilterChains { - if p == int(fc.GetFilterChainMatch().GetDestinationPort().GetValue()) && fc.GetFilterChainMatch().GetTransportProtocol() == "" { - // Note: matches [{transport=tls},{}] and [{transport=tls},{transport=buffer}] - // are equivalent, so technically this error is overly sensitive. However, for - // more complicated use cases its generally best to be explicit rather than - // assuming that {} will be treated as wildcard when in reality it may not be. - // Instead, we should explicitly double the filter chain (one for raw buffer, one - // for TLS) - t.Errorf("filter chain should have transport protocol set for port %v: %v", p, Dump(t, fc)) - } - } - } - } -} - -func validateListenerTLS(t testing.TB, l *listener.Listener) { - t.Helper() - for _, fc := range l.FilterChains { - m := fc.FilterChainMatch - if m == nil { - continue - } - // if we are matching TLS traffic and doing HTTP traffic, we must terminate the TLS - if m.TransportProtocol == xdsfilters.TLSTransportProtocol && fc.TransportSocket == nil && ExtractHTTPConnectionManager(t, fc) != nil { - t.Errorf("listener %v is invalid: tls traffic may not be terminated: %v", l.Name, Dump(t, fc)) - } - } -} - -// Validate a tls inspect filter is added whenever it is needed -// matches logic in https://github.com/envoyproxy/envoy/blob/22683a0a24ffbb0cdeb4111eec5ec90246bec9cb/source/server/listener_impl.cc#L41 -func validateInspector(t testing.TB, l *listener.Listener) { - t.Helper() - for _, lf := range l.ListenerFilters { - if lf.Name == xdsfilters.TLSInspector.Name { - return - } - } - for _, fc := range l.FilterChains { - m := fc.FilterChainMatch - if fc.FilterChainMatch == nil { - continue - } - if m.TransportProtocol == xdsfilters.TLSTransportProtocol { - t.Errorf("transport protocol set, but missing tls inspector: %v", Dump(t, l)) - } - if m.TransportProtocol == "" && len(m.ServerNames) > 0 { - t.Errorf("server names set, but missing tls inspector: %v", Dump(t, l)) - } - // This is a bit suspect; I suspect this could be done with just http inspector without tls inspector, - // but this mirrors Envoy validation logic - if m.TransportProtocol == "" && len(m.ApplicationProtocols) > 0 { - t.Errorf("application protocol set, but missing tls inspector: %v", Dump(t, l)) - } - } -} - -func ValidateClusters(t testing.TB, ls []*cluster.Cluster) { - found := sets.String{} - for _, l := range ls { - if found.Has(l.Name) { - t.Errorf("duplicate cluster name %v", l.Name) - } - found.Insert(l.Name) - ValidateCluster(t, l) - } -} - -func ValidateCluster(t testing.TB, c *cluster.Cluster) { - if err := c.Validate(); err != nil { - t.Errorf("cluster %v is invalid: %v", c.Name, err) - } - validateClusterTLS(t, c) -} - -func validateClusterTLS(t testing.TB, c *cluster.Cluster) { - if c.TransportSocket != nil && c.TransportSocketMatches != nil { - t.Errorf("both transport_socket and transport_socket_matches set for %v", c) - } -} - -func ValidateRoutes(t testing.TB, ls []*route.Route) { - for _, l := range ls { - ValidateRoute(t, l) - } -} - -func ValidateRoute(t testing.TB, r *route.Route) { - if err := r.Validate(); err != nil { - t.Errorf("route %v is invalid: %v", r.Name, err) - } -} - -func ValidateRouteConfigurations(t testing.TB, ls []*route.RouteConfiguration) { - found := sets.String{} - for _, l := range ls { - if found.Has(l.Name) { - t.Errorf("duplicate route config name %v", l.Name) - } - found.Insert(l.Name) - ValidateRouteConfiguration(t, l) - } -} - -func ValidateRouteConfiguration(t testing.TB, l *route.RouteConfiguration) { - t.Helper() - if err := l.Validate(); err != nil { - t.Errorf("route configuration %v is invalid: %v", l.Name, err) - } - validateRouteConfigurationDomains(t, l) -} - -func validateRouteConfigurationDomains(t testing.TB, l *route.RouteConfiguration) { - t.Helper() - - vhosts := sets.String{} - domains := sets.String{} - for _, vhost := range l.VirtualHosts { - if vhosts.Has(vhost.Name) { - t.Errorf("duplicate virtual host found %s", vhost.Name) - } - vhosts.Insert(vhost.Name) - for _, domain := range vhost.Domains { - if domains.Has(domain) { - t.Errorf("duplicate virtual host domain found %s", domain) - } - domains.Insert(domain) - } - } -} - -func ValidateClusterLoadAssignments(t testing.TB, ls []*endpoint.ClusterLoadAssignment) { - for _, l := range ls { - ValidateClusterLoadAssignment(t, l) - } -} - -func ValidateClusterLoadAssignment(t testing.TB, l *endpoint.ClusterLoadAssignment) { - if err := l.Validate(); err != nil { - t.Errorf("cluster load assignment %v is invalid: %v", l.ClusterName, err) - } -} diff --git a/pixiu/pkg/adapter/dubboregistry/registry/nacos/interface_listener.go b/pixiu/pkg/adapter/dubboregistry/registry/nacos/interface_listener.go deleted file mode 100644 index 1f6df9a45..000000000 --- a/pixiu/pkg/adapter/dubboregistry/registry/nacos/interface_listener.go +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 nacos - -import ( - "bytes" - "fmt" - "strings" - "sync" - "time" -) - -import ( - dubboCommon "dubbo.apache.org/dubbo-go/v3/common" - "dubbo.apache.org/dubbo-go/v3/common/constant" - "github.com/nacos-group/nacos-sdk-go/clients/naming_client" - "github.com/nacos-group/nacos-sdk-go/vo" -) - -import ( - common2 "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/common" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/registry" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/remoting/zookeeper" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -const ( - MaxFailTimes = 2 - ConnDelay = 3 * time.Second -) - -var _ registry.Listener = new(nacosIntfListener) - -type nacosIntfListener struct { - exit chan struct{} - client naming_client.INamingClient - regConf *model.Registry - reg *NacosRegistry - wg sync.WaitGroup - addr string - adapterListener common2.RegistryEventListener - serviceInfoMap map[string]*serviceInfo -} - -// newNacosIntfListener returns a new nacosIntfListener with pre-defined path according to the registered type. -func newNacosIntfListener(client naming_client.INamingClient, reg *NacosRegistry, regConf *model.Registry, adapterListener common2.RegistryEventListener) registry.Listener { - return &nacosIntfListener{ - exit: make(chan struct{}), - client: client, - regConf: regConf, - reg: reg, - addr: regConf.Address, - adapterListener: adapterListener, - serviceInfoMap: map[string]*serviceInfo{}, - } -} - -func (z *nacosIntfListener) Close() { - close(z.exit) - z.wg.Wait() -} - -func (z *nacosIntfListener) WatchAndHandle() { - z.wg.Add(1) - go z.watch() -} - -func (z *nacosIntfListener) watch() { - defer z.wg.Done() - var ( - failTimes int64 = 0 - delayTimer = time.NewTimer(ConnDelay * time.Duration(failTimes)) - ) - defer delayTimer.Stop() - for { - serviceList, err := z.client.GetAllServicesInfo(vo.GetAllServiceInfoParam{ - GroupName: z.regConf.Group, - NameSpace: z.regConf.Namespace, - PageSize: 100, - }) - // error handling - if err != nil { - failTimes++ - logger.Infof("watching nacos interface with error{%v}", err) - // Exit the watch if root node is in error - if err == zookeeper.ErrNilNode { - logger.Errorf("watching nacos services got errNilNode,so exit listen") - return - } - if failTimes > MaxFailTimes { - logger.Errorf("Error happens on nacos exceed max fail times: %s,so exit listen", MaxFailTimes) - return - } - delayTimer.Reset(ConnDelay * time.Duration(failTimes)) - <-delayTimer.C - continue - } - failTimes = 0 - if err := z.updateServiceList(serviceList.Doms); err != nil { - logger.Errorf("update service list failed %s", err) - } - time.Sleep(time.Second * 5) - } -} - -type serviceInfo struct { - interfaceName string - version string - group string - listener *serviceListener -} - -func (s *serviceInfo) String() string { - return fmt.Sprintf("%s:%s:%s", s.interfaceName, s.version, s.group) -} - -func fromServiceFullKey(fullKey string) *serviceInfo { - serviceInfoStrs := strings.Split(fullKey, ":") - if len(serviceInfoStrs) != 4 { - return nil - } - return &serviceInfo{ - interfaceName: serviceInfoStrs[1], - version: serviceInfoStrs[2], - group: serviceInfoStrs[3], - } -} - -func (z *nacosIntfListener) updateServiceList(serviceList []string) error { - // add new service info and watch - - newServiceMap := make(map[string]bool) - - for _, v := range serviceList { - svcInfo := fromServiceFullKey(v) - if svcInfo == nil { - // invalid nacos dubbo service key - continue - } - key := svcInfo.String() - newServiceMap[key] = true - if _, ok := z.serviceInfoMap[key]; !ok { - - url, _ := dubboCommon.NewURL("mock://localhost:8848") - url.SetParam(constant.InterfaceKey, svcInfo.interfaceName) - url.SetParam(constant.GroupKey, svcInfo.group) - url.SetParam(constant.VersionKey, svcInfo.version) - l := newNacosSrvListener(url, z.client, z.adapterListener) - l.wg.Add(1) - - svcInfo.listener = l - z.serviceInfoMap[key] = svcInfo - - go func(v *serviceInfo) { - defer l.wg.Done() - - sub := &vo.SubscribeParam{ - ServiceName: getSubscribeName(url), - SubscribeCallback: l.Callback, - GroupName: z.regConf.Group, - } - - if err := z.client.Subscribe(sub); err != nil { - logger.Errorf("subscribe listener with interfaceKey = %s, error = %s", l, err) - } - }(svcInfo) - } - } - - // handle deleted service - for k, v := range z.serviceInfoMap { - if _, ok := newServiceMap[k]; !ok { - delete(z.serviceInfoMap, k) - v.listener.Close() - } - } - - return nil - -} - -func getSubscribeName(url *dubboCommon.URL) string { - var buffer bytes.Buffer - buffer.Write([]byte(dubboCommon.DubboNodes[dubboCommon.PROVIDER])) - appendParam(&buffer, url, constant.InterfaceKey) - appendParam(&buffer, url, constant.VersionKey) - appendParam(&buffer, url, constant.GroupKey) - return buffer.String() -} - -func appendParam(target *bytes.Buffer, url *dubboCommon.URL, key string) { - value := url.GetParam(key, "") - target.Write([]byte(constant.NacosServiceNameSeparator)) - if strings.TrimSpace(value) != "" { - target.Write([]byte(value)) - } -} diff --git a/pixiu/pkg/adapter/dubboregistry/registry/nacos/registry.go b/pixiu/pkg/adapter/dubboregistry/registry/nacos/registry.go deleted file mode 100644 index 51d63f116..000000000 --- a/pixiu/pkg/adapter/dubboregistry/registry/nacos/registry.go +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 nacos - -import ( - "github.com/nacos-group/nacos-sdk-go/clients" - "github.com/nacos-group/nacos-sdk-go/clients/naming_client" - nacosConstant "github.com/nacos-group/nacos-sdk-go/common/constant" - "github.com/nacos-group/nacos-sdk-go/vo" - "github.com/pkg/errors" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/common" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/registry" - baseRegistry "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/registry/base" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/util/stringutil" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -func init() { - registry.SetRegistry(constant.Nacos, newNacosRegistry) -} - -type NacosRegistry struct { - *baseRegistry.BaseRegistry - nacosListeners map[registry.RegisteredType]registry.Listener - client naming_client.INamingClient -} - -func (n *NacosRegistry) DoSubscribe() error { - intfListener, ok := n.nacosListeners[registry.RegisteredTypeInterface] - if !ok { - return errors.New("Listener for interface level registration does not initialized") - } - go intfListener.WatchAndHandle() - return nil -} - -func (n *NacosRegistry) DoUnsubscribe() error { - panic("implement me") -} - -var _ registry.Registry = new(NacosRegistry) - -func newNacosRegistry(regConfig model.Registry, adapterListener common.RegistryEventListener) (registry.Registry, error) { - addrs, err := stringutil.GetIPAndPort(regConfig.Address) - if err != nil { - return nil, err - } - - scs := make([]nacosConstant.ServerConfig, 0) - for _, addr := range addrs { - scs = append(scs, nacosConstant.ServerConfig{ - IpAddr: addr.IP.String(), - Port: uint64(addr.Port), - }) - } - - ccs := nacosConstant.NewClientConfig( - nacosConstant.WithNamespaceId(regConfig.Namespace), - nacosConstant.WithUsername(regConfig.Username), - nacosConstant.WithPassword(regConfig.Password), - nacosConstant.WithNotLoadCacheAtStart(true), - nacosConstant.WithUpdateCacheWhenEmpty(true)) - client, err := clients.NewNamingClient(vo.NacosClientParam{ - ServerConfigs: scs, - ClientConfig: ccs, - }) - if err != nil { - return nil, err - } - - nacosRegistry := &NacosRegistry{ - client: client, - nacosListeners: make(map[registry.RegisteredType]registry.Listener), - } - nacosRegistry.nacosListeners[registry.RegisteredTypeInterface] = newNacosIntfListener(client, nacosRegistry, ®Config, adapterListener) - - baseReg := baseRegistry.NewBaseRegistry(nacosRegistry, adapterListener) - nacosRegistry.BaseRegistry = baseReg - return baseReg, nil -} diff --git a/pixiu/pkg/adapter/dubboregistry/registry/nacos/service_listener.go b/pixiu/pkg/adapter/dubboregistry/registry/nacos/service_listener.go deleted file mode 100644 index cc0d1e8fc..000000000 --- a/pixiu/pkg/adapter/dubboregistry/registry/nacos/service_listener.go +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 nacos - -import ( - "net/url" - "reflect" - "strconv" - "sync" -) - -import ( - dubboCommon "dubbo.apache.org/dubbo-go/v3/common" - dubboRegistry "dubbo.apache.org/dubbo-go/v3/registry" - _ "dubbo.apache.org/dubbo-go/v3/registry/nacos" - "dubbo.apache.org/dubbo-go/v3/remoting" - "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" - "github.com/nacos-group/nacos-sdk-go/clients/naming_client" - nacosModel "github.com/nacos-group/nacos-sdk-go/model" -) - -import ( - common2 "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/common" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/registry" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" -) - -// serviceListener normally monitors the /dubbo/[:url.service()]/providers -type serviceListener struct { - url *dubboCommon.URL - client naming_client.INamingClient - instanceMap map[string]nacosModel.Instance - cacheLock sync.Mutex - - exit chan struct{} - wg sync.WaitGroup - adapterListener common2.RegistryEventListener -} - -// WatchAndHandle todo WatchAndHandle is useless for service listener -func (z *serviceListener) WatchAndHandle() { - panic("implement me") -} - -// newNacosSrvListener creates a new zk service listener -func newNacosSrvListener(url *dubboCommon.URL, client naming_client.INamingClient, adapterListener common2.RegistryEventListener) *serviceListener { - return &serviceListener{ - url: url, - client: client, - exit: make(chan struct{}), - adapterListener: adapterListener, - instanceMap: map[string]nacosModel.Instance{}, - } -} - -func (z *serviceListener) Callback(services []nacosModel.SubscribeService, err error) { - if err != nil { - logger.Errorf("nacos subscribe callback error:%s", err.Error()) - return - } - - addInstances := make([]nacosModel.Instance, 0, len(services)) - delInstances := make([]nacosModel.Instance, 0, len(services)) - updateInstances := make([]nacosModel.Instance, 0, len(services)) - newInstanceMap := make(map[string]nacosModel.Instance, len(services)) - - z.cacheLock.Lock() - defer z.cacheLock.Unlock() - for i := range services { - if !services[i].Enable { - // instance is not available,so ignore it - continue - } - host := services[i].Ip + ":" + strconv.Itoa(int(services[i].Port)) - instance := generateInstance(services[i]) - newInstanceMap[host] = instance - if old, ok := z.instanceMap[host]; !ok { - // instance does not exist in cache, add it to cache - addInstances = append(addInstances, instance) - } else { - // instance is not different from cache, update it to cache - if !reflect.DeepEqual(old, instance) { - updateInstances = append(updateInstances, instance) - } - } - } - - for host, inst := range z.instanceMap { - if _, ok := newInstanceMap[host]; !ok { - // cache instance does not exist in new instance list, remove it from cache - delInstances = append(delInstances, inst) - } - } - - z.instanceMap = newInstanceMap - for i := range addInstances { - newUrl := generateURL(addInstances[i]) - if newUrl != nil { - z.handle(newUrl, remoting.EventTypeAdd) - } - } - for i := range delInstances { - newUrl := generateURL(delInstances[i]) - if newUrl != nil { - z.handle(newUrl, remoting.EventTypeDel) - } - } - - for i := range updateInstances { - newUrl := generateURL(updateInstances[i]) - if newUrl != nil { - z.handle(newUrl, remoting.EventTypeUpdate) - } - } -} - -func (z *serviceListener) handle(url *dubboCommon.URL, action remoting.EventType) { - - logger.Infof("update begin, service event: %v %v", action, url) - - bkConfig, methods, location, err := registry.ParseDubboString(url.String()) - if err != nil { - logger.Errorf("parse dubbo string error = %s", err) - return - } - - mappingParams := []config.MappingParam{ - { - Name: "requestBody.values", - MapTo: "opt.values", - }, - { - Name: "requestBody.types", - MapTo: "opt.types", - }, - } - apiPattern := registry.GetAPIPattern(bkConfig) - for i := range methods { - api := registry.CreateAPIConfig(apiPattern, location, bkConfig, methods[i], mappingParams) - if action == remoting.EventTypeDel { - if err := z.adapterListener.OnRemoveAPI(api); err != nil { - logger.Errorf("Error={%s} happens when try to remove api %s", err.Error(), api.Path) - continue - } - } else { - if err := z.adapterListener.OnAddAPI(api); err != nil { - logger.Errorf("Error={%s} happens when try to add api %s", err.Error(), api.Path) - continue - } - } - - } -} - -func (z *serviceListener) NotifyAll(e []*dubboRegistry.ServiceEvent, f func()) { -} - -// Close closes this listener -func (zkl *serviceListener) Close() { - close(zkl.exit) - zkl.wg.Wait() -} - -func generateURL(instance nacosModel.Instance) *dubboCommon.URL { - if instance.Metadata == nil { - logger.Errorf("nacos instance metadata is empty,instance:%+v", instance) - return nil - } - path := instance.Metadata["path"] - myInterface := instance.Metadata["interface"] - if len(path) == 0 && len(myInterface) == 0 { - logger.Errorf("nacos instance metadata does not have both path key and interface key,instance:%+v", instance) - return nil - } - if len(path) == 0 && len(myInterface) != 0 { - path = "/" + myInterface - } - protocol := instance.Metadata["protocol"] - if len(protocol) == 0 { - logger.Errorf("nacos instance metadata does not have protocol key,instance:%+v", instance) - return nil - } - urlMap := url.Values{} - for k, v := range instance.Metadata { - urlMap.Set(k, v) - } - return dubboCommon.NewURLWithOptions( - dubboCommon.WithIp(instance.Ip), - dubboCommon.WithPort(strconv.Itoa(int(instance.Port))), - dubboCommon.WithProtocol(protocol), - dubboCommon.WithParams(urlMap), - dubboCommon.WithPath(path), - ) -} - -func generateInstance(ss nacosModel.SubscribeService) nacosModel.Instance { - return nacosModel.Instance{ - InstanceId: ss.InstanceId, - Ip: ss.Ip, - Port: ss.Port, - ServiceName: ss.ServiceName, - Valid: ss.Valid, - Enable: ss.Enable, - Weight: ss.Weight, - Metadata: ss.Metadata, - ClusterName: ss.ClusterName, - } -} diff --git a/pixiu/pkg/adapter/dubboregistry/registry/registry.go b/pixiu/pkg/adapter/dubboregistry/registry/registry.go deleted file mode 100644 index 3d8d3b12c..000000000 --- a/pixiu/pkg/adapter/dubboregistry/registry/registry.go +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 registry - -import ( - "strings" - "time" -) - -import ( - "dubbo.apache.org/dubbo-go/v3/common" - "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" - "github.com/dubbo-go-pixiu/pixiu-api/pkg/router" - "github.com/pkg/errors" -) - -import ( - common2 "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/common" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -type RegisteredType int8 - -const ( - RegisteredTypeApplication RegisteredType = iota - RegisteredTypeInterface -) - -var registryMap = make(map[string]func(model.Registry, common2.RegistryEventListener) (Registry, error), 8) - -func (t *RegisteredType) String() string { - return []string{"application", "interface"}[*t] -} - -// Registry interface defines the basic features of a registry -type Registry interface { - // Subscribe monitors the target registry. - Subscribe() error - // Unsubscribe stops monitoring the target registry. - Unsubscribe() error -} - -// SetRegistry will store the registry by name -func SetRegistry(name string, newRegFunc func(model.Registry, common2.RegistryEventListener) (Registry, error)) { - registryMap[name] = newRegFunc -} - -// GetRegistry will return the registry -// if not found, it will panic -func GetRegistry(name string, regConfig model.Registry, listener common2.RegistryEventListener) (Registry, error) { - if registry, ok := registryMap[regConfig.Protocol]; ok { - reg, err := registry(regConfig, listener) - if err != nil { - panic("Initialize Registry" + name + "failed due to: " + err.Error()) - } - return reg, nil - } - return nil, errors.New("Registry " + name + " does not support yet") -} - -// CreateAPIConfig returns router.API struct base on the input -func CreateAPIConfig(urlPattern, location string, dboBackendConfig config.DubboBackendConfig, methodString string, mappingParams []config.MappingParam) router.API { - dboBackendConfig.Method = methodString - url := strings.Join([]string{urlPattern, methodString}, constant.PathSlash) - var requestType config.RequestType - switch dboBackendConfig.Protocol { - case string(config.DubboRequest): - requestType = config.DubboRequest - case "tri": - requestType = "triple" - default: - requestType = config.DubboRequest - } - method := config.Method{ - Enable: true, - Timeout: 3 * time.Second, - Mock: false, - HTTPVerb: config.MethodPost, - InboundRequest: config.InboundRequest{ - RequestType: config.HTTPRequest, - }, - IntegrationRequest: config.IntegrationRequest{ - RequestType: requestType, - DubboBackendConfig: dboBackendConfig, - MappingParams: mappingParams, - HTTPBackendConfig: config.HTTPBackendConfig{ - URL: location, - }, - }, - } - return router.API{ - URLPattern: url, - Method: method, - } -} - -// ParseDubboString parse the dubbo urls -// dubbo://192.168.3.46:20002/org.apache.dubbo.UserProvider2?anyhost=true&app.version=0.0.1&application=UserInfoServer&bean.name=UserProvider -// &cluster=failover&environment=dev&export=true&interface=org.apache.dubbo.UserProvider2&ip=192.168.3.46&loadbalance=random&message_size=4 -// &methods=GetUser&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbo-go user-info server -// &name=UserInfoServer&organization=dubbo.io&pid=11037®istry.role=3&release=dubbo-golang-1.5.6 -// &service.filter=echo,token,accesslog,tps,generic_service,execute,pshutdown&side=provider&ssl-enabled=false×tamp=1624716984&warmup=100 -func ParseDubboString(urlString string) (config.DubboBackendConfig, []string, string, error) { - url, err := common.NewURL(urlString) - if err != nil { - return config.DubboBackendConfig{}, nil, "", errors.WithStack(err) - } - return config.DubboBackendConfig{ - ClusterName: url.GetParam(constant.ClusterKey, ""), - ApplicationName: url.GetParam(constant.ApplicationKey, ""), - Version: url.GetParam(constant.VersionKey, ""), - Protocol: url.Protocol, - Group: url.GetParam(constant.GroupKey, ""), - Interface: url.GetParam(constant.InterfaceKey, ""), - Retries: url.GetParam(constant.RetriesKey, ""), - }, strings.Split(url.GetParam(constant.MethodsKey, ""), constant.StringSeparator), url.Location, nil -} - -// GetAPIPattern generate the API path pattern. /application/interface/version -func GetAPIPattern(bkConfig config.DubboBackendConfig) string { - if bkConfig.Version == "" { - // if the version is empty, make sure the url path is valid. - return strings.Join([]string{"/" + bkConfig.ApplicationName, bkConfig.Interface}, constant.PathSlash) - } - return strings.Join([]string{"/" + bkConfig.ApplicationName, bkConfig.Interface, bkConfig.Version}, constant.PathSlash) -} - -func GetRouter() model.Router { - return model.Router{ - Match: model.RouterMatch{ - Prefix: "", - Path: "", - Methods: nil, - }, - } -} diff --git a/pixiu/pkg/adapter/dubboregistry/registry/util.go b/pixiu/pkg/adapter/dubboregistry/registry/util.go deleted file mode 100644 index c3e239650..000000000 --- a/pixiu/pkg/adapter/dubboregistry/registry/util.go +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 registry - -import ( - "dubbo.apache.org/dubbo-go/v3/common" - "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" -) - -// TransferURL2Api transfer url and clusterName to IntegrationRequest -func TransferURL2Api(url *common.URL, clusterName string) []config.IntegrationRequest { - var irs []config.IntegrationRequest - for _, method := range url.Methods { - irs = append(irs, config.IntegrationRequest{ - RequestType: config.RequestType(url.Protocol), - DubboBackendConfig: config.DubboBackendConfig{ - ApplicationName: url.GetParam(constant.NameKey, ""), - Group: url.GetParam(constant.GroupKey, ""), - Version: url.GetParam(constant.VersionKey, ""), - Interface: url.GetParam(constant.InterfaceKey, ""), - Method: method, - Retries: url.GetParam(constant.RetriesKey, ""), - ClusterName: clusterName, - }, - }) - } - return irs -} diff --git a/pixiu/pkg/adapter/dubboregistry/registry/zookeeper/application_listener.go b/pixiu/pkg/adapter/dubboregistry/registry/zookeeper/application_listener.go deleted file mode 100644 index 3c86411cc..000000000 --- a/pixiu/pkg/adapter/dubboregistry/registry/zookeeper/application_listener.go +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 zookeeper - -import ( - "strings" - "sync" - "time" -) - -import ( - "github.com/dubbogo/go-zookeeper/zk" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/common" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/registry" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/remoting/zookeeper" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" -) - -const ( - defaultServicesPath = "/services" - methodsRootPath = "/dubbo/metadata" -) - -var _ registry.Listener = new(zkAppListener) - -type zkAppListener struct { - servicesPath string - exit chan struct{} - client *zookeeper.ZooKeeperClient - reg *ZKRegistry - wg sync.WaitGroup - adapterListener common.RegistryEventListener -} - -// newZkAppListener returns a new newZkAppListener with pre-defined servicesPath according to the registered type. -func newZkAppListener(client *zookeeper.ZooKeeperClient, reg *ZKRegistry, adapterListener common.RegistryEventListener) registry.Listener { - p := defaultServicesPath - return &zkAppListener{ - servicesPath: p, - exit: make(chan struct{}), - client: client, - reg: reg, - adapterListener: adapterListener, - } -} - -func (z *zkAppListener) Close() { - close(z.exit) - z.wg.Wait() -} - -func (z *zkAppListener) WatchAndHandle() { - z.wg.Add(1) - go z.watch() -} - -func (z *zkAppListener) watch() { - defer z.wg.Done() - - var ( - failTimes int64 = 0 - delayTimer = time.NewTimer(ConnDelay * time.Duration(failTimes)) - ) - defer delayTimer.Stop() - for { - children, e, err := z.client.GetChildrenW(z.servicesPath) - // error handling - if err != nil { - failTimes++ - logger.Infof("watching (path{%s}) = error{%v}", z.servicesPath, err) - // Exit the watch if root node is in error - if err == zookeeper.ErrNilNode { - logger.Errorf("watching (path{%s}) got errNilNode,so exit listen", z.servicesPath) - return - } - if failTimes > MaxFailTimes { - logger.Errorf("Error happens on (path{%s}) exceed max fail times: %s,so exit listen", - z.servicesPath, MaxFailTimes) - return - } - delayTimer.Reset(ConnDelay * time.Duration(failTimes)) - <-delayTimer.C - continue - } - failTimes = 0 - if continueLoop := z.waitEventAndHandlePeriod(children, e); !continueLoop { - return - } - } -} - -func (z *zkAppListener) waitEventAndHandlePeriod(children []string, e <-chan zk.Event) bool { - tickerTTL := defaultTTL - ticker := time.NewTicker(tickerTTL) - defer ticker.Stop() - z.handleEvent(children) - for { - select { - case <-ticker.C: - z.handleEvent(children) - case zkEvent := <-e: - logger.Warnf("get a zookeeper e{type:%s, server:%s, path:%s, state:%d-%s, err:%s}", - zkEvent.Type.String(), zkEvent.Server, zkEvent.Path, zkEvent.State, zookeeper.StateToString(zkEvent.State), zkEvent.Err) - if zkEvent.Type != zk.EventNodeChildrenChanged { - return true - } - z.handleEvent(children) - return true - case <-z.exit: - logger.Warnf("listen(path{%s}) goroutine exit now...", z.servicesPath) - return false - } - } -} - -func (z *zkAppListener) handleEvent(children []string) { - fetchChildren, err := z.client.GetChildren(z.servicesPath) - if err != nil { - logger.Warnf("Error when retrieving newChildren in path: %s, Error:%s", z.servicesPath, err.Error()) - } - for _, path := range fetchChildren { - serviceName := strings.Join([]string{z.servicesPath, path}, constant.PathSlash) - if z.reg.GetSvcListener(serviceName) != nil { - continue - } - l := newApplicationServiceListener(serviceName, z.client, z.adapterListener) - l.wg.Add(1) - go l.WatchAndHandle() - z.reg.SetSvcListener(serviceName, l) - } -} diff --git a/pixiu/pkg/adapter/dubboregistry/registry/zookeeper/interface_listener.go b/pixiu/pkg/adapter/dubboregistry/registry/zookeeper/interface_listener.go deleted file mode 100644 index 3edb6332a..000000000 --- a/pixiu/pkg/adapter/dubboregistry/registry/zookeeper/interface_listener.go +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 zookeeper - -import ( - "path" - "sync" - "time" -) - -import ( - "dubbo.apache.org/dubbo-go/v3/common" - "github.com/dubbogo/go-zookeeper/zk" -) - -import ( - common2 "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/common" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/registry" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/remoting/zookeeper" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" -) - -const ( - rootPath = "/dubbo" - providerCategory = "providers" -) - -var _ registry.Listener = new(zkIntfListener) - -type zkIntfListener struct { - path string - exit chan struct{} - client *zookeeper.ZooKeeperClient - reg *ZKRegistry - wg sync.WaitGroup - adapterListener common2.RegistryEventListener -} - -// newZKIntfListener returns a new zkIntfListener with pre-defined path according to the registered type. -func newZKIntfListener(client *zookeeper.ZooKeeperClient, reg *ZKRegistry, adapterListener common2.RegistryEventListener) registry.Listener { - p := rootPath - return &zkIntfListener{ - path: p, - exit: make(chan struct{}), - client: client, - reg: reg, - adapterListener: adapterListener, - } -} - -func (z *zkIntfListener) Close() { - close(z.exit) - z.wg.Wait() -} - -func (z *zkIntfListener) WatchAndHandle() { - z.wg.Add(1) - go z.watch() -} - -func (z *zkIntfListener) watch() { - defer z.wg.Done() - var ( - failTimes int64 = 0 - delayTimer = time.NewTimer(ConnDelay * time.Duration(failTimes)) - ) - defer delayTimer.Stop() - for { - _, e, err := z.client.GetChildrenW(z.path) - // error handling - if err != nil { - failTimes++ - logger.Infof("watching (path{%s}) = error{%v}", z.path, err) - // Exit the watch if root node is in error - if err == zookeeper.ErrNilNode { - logger.Errorf("watching (path{%s}) got errNilNode,so exit listen", z.path) - return - } - if failTimes > MaxFailTimes { - logger.Errorf("Error happens on (path{%s}) exceed max fail times: %s,so exit listen", - z.path, MaxFailTimes) - return - } - delayTimer.Reset(ConnDelay * time.Duration(failTimes)) - <-delayTimer.C - continue - } - failTimes = 0 - if continueLoop := z.waitEventAndHandlePeriod(z.path, e); !continueLoop { - return - } - } -} - -func (z *zkIntfListener) waitEventAndHandlePeriod(path string, e <-chan zk.Event) bool { - tickerTTL := defaultTTL - ticker := time.NewTicker(tickerTTL) - defer ticker.Stop() - z.handleEvent(z.path) - for { - select { - case <-ticker.C: - z.handleEvent(z.path) - case zkEvent := <-e: - logger.Warnf("get a zookeeper e{type:%s, server:%s, path:%s, state:%d-%s, err:%s}", - zkEvent.Type.String(), zkEvent.Server, zkEvent.Path, zkEvent.State, zookeeper.StateToString(zkEvent.State), zkEvent.Err) - if zkEvent.Type != zk.EventNodeChildrenChanged { - return true - } - z.handleEvent(zkEvent.Path) - return true - case <-z.exit: - logger.Warnf("listen(path{%s}) goroutine exit now...", z.path) - return false - } - } -} - -func (z *zkIntfListener) handleEvent(basePath string) { - newChildren, err := z.client.GetChildren(basePath) - if err != nil { - logger.Errorf("Error when retrieving newChildren in path: %s, Error:%s", basePath, err.Error()) - } - for i := range newChildren { - if newChildren[i] == "metadata" { - continue - } - providerPath := path.Join(basePath, newChildren[i], providerCategory) - // TO-DO: modify here to only handle child that changed - providers, err := z.client.GetChildren(providerPath) - if err != nil { - logger.Warnf("Get provider %s failed due to %s", providerPath, err.Error()) - continue - } - srvUrl, err := common.NewURL(providers[0]) - if err != nil { - logger.Warnf("Parse provider service url %s failed due to %s", providers[0], err.Error()) - continue - } - if z.reg.GetSvcListener(srvUrl.ServiceKey()) != nil { - continue - } - l := newZkSrvListener(srvUrl, providerPath, z.client, z.adapterListener) - l.wg.Add(1) - go l.WatchAndHandle() - z.reg.SetSvcListener(srvUrl.ServiceKey(), l) - } -} diff --git a/pixiu/pkg/adapter/dubboregistry/registry/zookeeper/registry.go b/pixiu/pkg/adapter/dubboregistry/registry/zookeeper/registry.go deleted file mode 100644 index e0512e768..000000000 --- a/pixiu/pkg/adapter/dubboregistry/registry/zookeeper/registry.go +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 zookeeper - -import ( - "strings" - "time" -) - -import ( - "github.com/pkg/errors" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/common" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/registry" - baseRegistry "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/registry/base" - zk "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/remoting/zookeeper" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -var ( - _ baseRegistry.FacadeRegistry = new(ZKRegistry) -) - -const ( - // RegistryZkClient zk client name - RegistryZkClient = "zk registry" - MaxFailTimes = 2 - ConnDelay = 3 * time.Second - defaultTTL = 10 * time.Minute -) - -func init() { - registry.SetRegistry(constant.Zookeeper, newZKRegistry) -} - -type ZKRegistry struct { - *baseRegistry.BaseRegistry - zkListeners map[registry.RegisteredType]registry.Listener - client *zk.ZooKeeperClient -} - -var _ registry.Registry = new(ZKRegistry) - -func newZKRegistry(regConfig model.Registry, adapterListener common.RegistryEventListener) (registry.Registry, error) { - var zkReg = &ZKRegistry{} - baseReg := baseRegistry.NewBaseRegistry(zkReg, adapterListener) - timeout, err := time.ParseDuration(regConfig.Timeout) - if err != nil { - return nil, errors.Errorf("Incorrect timeout configuration: %s", regConfig.Timeout) - } - client, eventChan, err := zk.NewZooKeeperClient(RegistryZkClient, strings.Split(regConfig.Address, ","), timeout) - if err != nil { - return nil, errors.Errorf("Initialize zookeeper client failed: %s", err.Error()) - } - client.RegisterHandler(eventChan) - zkReg.BaseRegistry = baseReg - zkReg.client = client - initZKListeners(zkReg) - return zkReg, nil -} - -func initZKListeners(reg *ZKRegistry) { - reg.zkListeners = make(map[registry.RegisteredType]registry.Listener) - reg.zkListeners[registry.RegisteredTypeInterface] = newZKIntfListener(reg.client, reg, reg.AdapterListener) -} - -func (r *ZKRegistry) GetClient() *zk.ZooKeeperClient { - return r.client -} - -// DoSubscribe is the implementation of subscription on the target registry. -func (r *ZKRegistry) DoSubscribe() error { - if err := r.interfaceSubscribe(); err != nil { - return err - } - return nil -} - -// To subscribe service level service discovery -func (r *ZKRegistry) interfaceSubscribe() error { - intfListener, ok := r.zkListeners[registry.RegisteredTypeInterface] - if !ok { - return errors.New("Listener for interface level registration does not initialized") - } - go intfListener.WatchAndHandle() - return nil -} - -// DoUnsubscribe stops monitoring the target registry. -func (r *ZKRegistry) DoUnsubscribe() error { - intfListener, ok := r.zkListeners[registry.RegisteredTypeInterface] - if !ok { - return errors.New("Listener for interface level registration does not initialized") - } - intfListener.Close() - for k, l := range r.GetAllSvcListener() { - l.Close() - r.RemoveSvcListener(k) - } - return nil -} diff --git a/pixiu/pkg/adapter/dubboregistry/registry/zookeeper/service_listener.go b/pixiu/pkg/adapter/dubboregistry/registry/zookeeper/service_listener.go deleted file mode 100644 index 93d874486..000000000 --- a/pixiu/pkg/adapter/dubboregistry/registry/zookeeper/service_listener.go +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 zookeeper - -import ( - "strings" - "sync" - "time" -) - -import ( - "dubbo.apache.org/dubbo-go/v3/common" - "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" - "github.com/dubbogo/go-zookeeper/zk" -) - -import ( - common2 "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/common" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/registry" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/remoting/zookeeper" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" -) - -var _ registry.Listener = new(serviceListener) - -// serviceListener normally monitors the /dubbo/[:url.service()]/providers -type serviceListener struct { - url *common.URL - path string - client *zookeeper.ZooKeeperClient - - exit chan struct{} - wg sync.WaitGroup - adapterListener common2.RegistryEventListener - registryMethod map[string]*config.Method - mutex sync.Mutex -} - -// newZkSrvListener creates a new zk service listener -func newZkSrvListener(url *common.URL, path string, client *zookeeper.ZooKeeperClient, adapterListener common2.RegistryEventListener) *serviceListener { - return &serviceListener{ - url: url, - path: path, - client: client, - exit: make(chan struct{}), - adapterListener: adapterListener, - registryMethod: make(map[string]*config.Method), - } -} - -func (zkl *serviceListener) WatchAndHandle() { - defer zkl.wg.Done() - - var ( - failTimes int64 = 0 - delayTimer = time.NewTimer(ConnDelay * time.Duration(failTimes)) - ) - - for { - children, e, err := zkl.client.GetChildrenW(zkl.path) - // error handling - if err != nil { - failTimes++ - logger.Infof("watching (path{%s}) = error{%v}", zkl.path, err) - // Exit the watch if root node is in error - if err == zookeeper.ErrNilNode { - logger.Errorf("watching (path{%s}) got errNilNode,so exit listen", zkl.path) - return - } - if failTimes > MaxFailTimes { - logger.Errorf("Error happens on (path{%s}) exceed max fail times: %s,so exit listen", - zkl.path, MaxFailTimes) - return - } - delayTimer.Reset(ConnDelay * time.Duration(failTimes)) - <-delayTimer.C - continue - } - failTimes = 0 - if continueLoop := zkl.waitEventAndHandlePeriod(children, e); !continueLoop { - return - } - } -} - -func (zkl *serviceListener) waitEventAndHandlePeriod(children []string, e <-chan zk.Event) bool { - tickerTTL := defaultTTL - ticker := time.NewTicker(tickerTTL) - defer ticker.Stop() - zkl.handleEvent() - - for { - select { - case <-ticker.C: - zkl.handleEvent() - case zkEvent := <-e: - logger.Warnf("get a zookeeper childEventCh{type:%s, server:%s, path:%s, state:%d-%s, err:%s}", - zkEvent.Type.String(), zkEvent.Server, zkEvent.Path, zkEvent.State, zookeeper.StateToString(zkEvent.State), zkEvent.Err) - ticker.Stop() - if zkEvent.Type != zk.EventNodeChildrenChanged { - return true - } - zkl.handleEvent() - return true - case <-zkl.exit: - logger.Warnf("listen(path{%s}) goroutine exit now...", zkl.path) - ticker.Stop() - return false - } - } -} - -// whenever it is called, the children node changed and refresh the api configuration. -func (zkl *serviceListener) handleEvent() { - children, err := zkl.client.GetChildren(zkl.path) - if err != nil { - // disable the API - bkConf, methods, _, _ := registry.ParseDubboString(zkl.url.String()) - apiPattern := registry.GetAPIPattern(bkConf) - for i := range methods { - path := strings.Join([]string{apiPattern, methods[i]}, constant.PathSlash) - if err := zkl.adapterListener.OnDeleteRouter(config.Resource{Path: path}); err != nil { - logger.Errorf("Error={%s} when try to remove API by path: %s", err.Error(), path) - } - } - return - } - zkl.url, err = common.NewURL(children[0]) - if err != nil { - logger.Warnf("Parse service path failed: %s", children[0]) - } - bkConfig, methods, location, err := registry.ParseDubboString(children[0]) - if err != nil { - logger.Warnf("Parse dubbo interface provider %s failed; due to \n %s", children[0], err.Error()) - return - } - if len(bkConfig.ApplicationName) == 0 || len(bkConfig.Interface) == 0 { - return - } - - mappingParams := []config.MappingParam{ - { - Name: "requestBody.values", - MapTo: "opt.values", - }, - { - Name: "requestBody.types", - MapTo: "opt.types", - }, - } - apiPattern := registry.GetAPIPattern(bkConfig) - zkl.mutex.Lock() - defer zkl.mutex.Unlock() - for i := range methods { - api := registry.CreateAPIConfig(apiPattern, location, bkConfig, methods[i], mappingParams) - key := api.URLPattern + ":" + string(api.Method.HTTPVerb) - if _, ok := zkl.registryMethod[key]; ok { - return - } - if err := zkl.adapterListener.OnAddAPI(api); err != nil { - logger.Errorf("Error={%s} happens when try to add api %s", err.Error(), api.Path) - } else { - zkl.registryMethod[key] = &api.Method - } - } -} - -// Close closes this listener -func (zkl *serviceListener) Close() { - close(zkl.exit) - zkl.wg.Wait() -} diff --git a/pixiu/pkg/adapter/dubboregistry/remoting/zookeeper/client.go b/pixiu/pkg/adapter/dubboregistry/remoting/zookeeper/client.go deleted file mode 100644 index f1ce33641..000000000 --- a/pixiu/pkg/adapter/dubboregistry/remoting/zookeeper/client.go +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 zookeeper - -import ( - "strings" - "sync" - "time" -) - -import ( - "github.com/dubbogo/go-zookeeper/zk" - "github.com/pkg/errors" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" -) - -var ( - // ErrNilZkClientConn no conn error - ErrNilZkClientConn = errors.New("zookeeper Client{conn} is nil") - // ErrNilChildren no children error - ErrNilChildren = errors.Errorf("has none children") - // ErrNilNode no node error - ErrNilNode = errors.Errorf("node does not exist") -) - -// Options defines the client option. -type Options struct { - zkName string - ts *zk.TestCluster -} - -// Option defines the function to load the options -type Option func(*Options) - -// WithZkName sets zk client name -func WithZkName(name string) Option { - return func(opt *Options) { - opt.zkName = name - } -} - -// ZooKeeperClient represents zookeeper client Configuration -type ZooKeeperClient struct { - name string - ZkAddrs []string - sync.RWMutex // for conn - conn *zk.Conn - Timeout time.Duration - exit chan struct{} - Wait sync.WaitGroup - - eventRegistry map[string][]chan zk.Event - eventRegistryLock sync.RWMutex -} - -func NewZooKeeperClient(name string, zkAddrs []string, timeout time.Duration) (*ZooKeeperClient, <-chan zk.Event, error) { - var ( - err error - event <-chan zk.Event - z *ZooKeeperClient - ) - - z = &ZooKeeperClient{ - name: name, - ZkAddrs: zkAddrs, - Timeout: timeout, - exit: make(chan struct{}), - eventRegistry: make(map[string][]chan zk.Event), - } - // connect to zookeeper - z.conn, event, err = zk.Connect(zkAddrs, timeout) - if err != nil { - return nil, nil, errors.WithMessagef(err, "zk.Connect(zkAddrs:%+v)", zkAddrs) - } - - return z, event, nil -} - -// StateToString will transfer zk state to string -func StateToString(state zk.State) string { - switch state { - case zk.StateDisconnected: - return "zookeeper disconnected" - case zk.StateConnecting: - return "zookeeper connecting" - case zk.StateAuthFailed: - return "zookeeper auth failed" - case zk.StateConnectedReadOnly: - return "zookeeper connect readonly" - case zk.StateSaslAuthenticated: - return "zookeeper sasl authenticated" - case zk.StateExpired: - return "zookeeper connection expired" - case zk.StateConnected: - return "zookeeper connected" - case zk.StateHasSession: - return "zookeeper has Session" - case zk.StateUnknown: - return "zookeeper unknown state" - default: - return state.String() - } -} - -func (z *ZooKeeperClient) RegisterHandler(event <-chan zk.Event) { - z.Wait.Add(1) - go z.HandleZkEvent(event) -} - -// ExistW is a wrapper to the zk connection conn.ExistsW -func (z *ZooKeeperClient) ExistW(zkPath string) (<-chan zk.Event, error) { - conn := z.getConn() - if conn == nil { - return nil, ErrNilZkClientConn - } - exist, _, watcher, err := conn.ExistsW(zkPath) - if err != nil { - return nil, errors.WithMessagef(err, "zk.ExistsW(path:%s)", zkPath) - } - if !exist { - return nil, errors.WithMessagef(ErrNilNode, "zkClient{%s} App zk path{%s} does not exist.", z.name, zkPath) - } - return watcher.EvtCh, nil -} - -// HandleZkEvent handles zookeeper events -func (z *ZooKeeperClient) HandleZkEvent(s <-chan zk.Event) { - var ( - state int - event zk.Event - ) - - defer func() { - z.Wait.Done() - logger.Infof("zk{path:%v, name:%s} connection goroutine game over.", z.ZkAddrs, z.name) - }() - - for { - select { - case <-z.exit: - return - case event = <-s: - logger.Infof("client{%s} get a zookeeper event{type:%s, server:%s, path:%s, state:%d-%s, err:%v}", - z.name, event.Type, event.Server, event.Path, event.State, StateToString(event.State), event.Err) - switch event.State { - case zk.StateDisconnected: - logger.Warnf("zk{addr:%s} state is StateDisconnected, so close the zk client{name:%s}.", z.ZkAddrs, z.name) - z.Destroy() - return - case zk.StateConnected: - logger.Infof("zkClient{%s} get zk node changed event{path:%s}", z.name, event.Path) - z.eventRegistryLock.RLock() - for path, a := range z.eventRegistry { - if strings.HasPrefix(event.Path, path) { - logger.Infof("send event{state:zk.EventNodeDataChange, Path:%s} notify event to path{%s} related listener", - event.Path, path) - for _, e := range a { - e <- event - } - } - } - z.eventRegistryLock.RUnlock() - case zk.StateConnecting, zk.StateHasSession: - if state == (int)(zk.StateHasSession) { - continue - } - z.eventRegistryLock.RLock() - if a, ok := z.eventRegistry[event.Path]; ok && 0 < len(a) { - for _, e := range a { - e <- event - } - } - z.eventRegistryLock.RUnlock() - } - state = (int)(event.State) - } - } -} - -// getConn gets zookeeper connection safely -func (z *ZooKeeperClient) getConn() *zk.Conn { - z.RLock() - defer z.RUnlock() - return z.conn -} - -func (z *ZooKeeperClient) stop() bool { - select { - case <-z.exit: - return true - default: - close(z.exit) - } - - return false -} - -// GetChildren gets children by @path -func (z *ZooKeeperClient) GetChildren(path string) ([]string, error) { - conn := z.getConn() - if conn == nil { - return nil, ErrNilZkClientConn - } - children, stat, err := conn.Children(path) - if err != nil { - if err == zk.ErrNoNode { - return nil, errors.WithMessagef(ErrNilNode, "path{%s} does not exist", path) - } - logger.Errorf("zk.Children(path{%s}) = error(%v)", path, errors.WithStack(err)) - return nil, errors.WithMessagef(err, "zk.Children(path:%s)", path) - } - if stat.NumChildren == 0 { - return nil, ErrNilChildren - } - return children, nil -} - -// GetChildrenW gets children watch by @path -func (z *ZooKeeperClient) GetChildrenW(path string) ([]string, <-chan zk.Event, error) { - conn := z.getConn() - if conn == nil { - return nil, nil, ErrNilZkClientConn - } - children, stat, watcher, err := conn.ChildrenW(path) - if err != nil { - if err == zk.ErrNoChildrenForEphemerals { - return nil, nil, ErrNilChildren - } - if err == zk.ErrNoNode { - return nil, nil, ErrNilNode - } - return nil, nil, errors.WithMessagef(err, "zk.ChildrenW(path:%s)", path) - } - if stat == nil { - return nil, nil, errors.Errorf("path{%s} get stat is nil", path) - } - //if len(children) == 0 { - // return nil, nil, ErrNilChildren - //} - - return children, watcher.EvtCh, nil -} - -// GetContent gets content by @path -func (z *ZooKeeperClient) GetContent(path string) ([]byte, error) { - var ( - data []byte - ) - conn := z.getConn() - if conn == nil { - return nil, errors.New("ZooKeeper client has no connection") - } - data, _, err := conn.Get(path) - if err != nil { - if err == zk.ErrNoNode { - return nil, errors.Errorf("path{%s} does not exist", path) - } - logger.Errorf("zk.Data(path{%s}) = error(%v)", path, errors.WithStack(err)) - return nil, errors.WithMessagef(err, "zk.Data(path:%s)", path) - } - return data, nil -} - -func (z *ZooKeeperClient) GetConnState() zk.State { - conn := z.getConn() - if conn != nil { - return conn.State() - } - return zk.StateExpired -} - -func (z *ZooKeeperClient) Destroy() { - z.stop() - z.Lock() - conn := z.conn - z.conn = nil - z.Unlock() - if conn != nil { - conn.Close() - } -} diff --git a/pixiu/pkg/adapter/springcloud/servicediscovery/servicediscovery.go b/pixiu/pkg/adapter/springcloud/servicediscovery/servicediscovery.go deleted file mode 100644 index 1f06ef41a..000000000 --- a/pixiu/pkg/adapter/springcloud/servicediscovery/servicediscovery.go +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 servicediscovery - -import ( - "fmt" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -type ( - // ServiceInstance the service instance info fetched from registry such as nacos consul - ServiceInstance struct { - ID string - ServiceName string - // host:port - Host string - Port int - Healthy bool - CLusterName string - Enable bool - // extra info such as label or other meta data - Metadata map[string]string - } - - ServiceEventListener interface { - OnAddServiceInstance(r *ServiceInstance) - OnDeleteServiceInstance(r *ServiceInstance) - OnUpdateServiceInstance(r *ServiceInstance) - GetServiceNames() []string - } - - ServiceDiscovery interface { - - // QueryAllServices get all service from remote registry center - QueryAllServices() ([]ServiceInstance, error) - - // QueryServicesByName get service by serviceName from remote registry center - QueryServicesByName(serviceNames []string) ([]ServiceInstance, error) - - // Register register to remote registry center - Register() error - - // UnRegister unregister to remote registry center - UnRegister() error - - // Subscribe subscribe the service event from remote registry center - Subscribe() error - - // Unsubscribe unsubscribe from remote registry center - Unsubscribe() error - } -) - -func (i *ServiceInstance) GetUniqKey() string { - return i.ServiceName + i.Host + fmt.Sprint(i.Port) -} - -// ToEndpoint -func (i *ServiceInstance) ToEndpoint() *model.Endpoint { - a := model.SocketAddress{Address: i.Host, Port: i.Port} - return &model.Endpoint{ID: i.ID, Address: a, Name: i.ServiceName, Metadata: i.Metadata} -} - -// ToRoute route ID is cluster name, so equal with endpoint name and routerMatch prefix is also service name -func (i *ServiceInstance) ToRoute() *model.Router { - rm := model.NewRouterMatchPrefix(i.ServiceName) - ra := model.RouteAction{Cluster: i.ServiceName} - return &model.Router{ID: i.ServiceName, Match: rm, Route: ra} -} diff --git a/pixiu/pkg/adapter/springcloud/servicediscovery/zookeeper/application_listener.go b/pixiu/pkg/adapter/springcloud/servicediscovery/zookeeper/application_listener.go deleted file mode 100644 index 7a78a45bd..000000000 --- a/pixiu/pkg/adapter/springcloud/servicediscovery/zookeeper/application_listener.go +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 zookeeper - -import ( - "strings" - "sync" - "time" -) - -import ( - "github.com/dubbogo/go-zookeeper/zk" - gzk "github.com/dubbogo/gost/database/kv/zk" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/springcloud/common" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/remote/zookeeper" -) - -type zkAppListener struct { - servicesPath string - exit chan struct{} - svcListeners *SvcListeners - wg sync.WaitGroup - - ds *zookeeperDiscovery -} - -func newZkAppListener(ds *zookeeperDiscovery) zookeeper.Listener { - return &zkAppListener{ - servicesPath: ds.basePath, - exit: make(chan struct{}), - svcListeners: &SvcListeners{listeners: make(map[string]zookeeper.Listener), listenerLock: sync.Mutex{}}, - ds: ds, - } -} - -func (z *zkAppListener) Close() { - for k, listener := range z.svcListeners.GetAllListener() { - z.svcListeners.RemoveListener(k) - listener.Close() - } - close(z.exit) - z.wg.Wait() -} - -func (z *zkAppListener) WatchAndHandle() { - z.wg.Add(1) - go z.watch() -} - -func (z *zkAppListener) watch() { - defer z.wg.Done() - - var ( - failTimes int64 = 0 - delayTimer = time.NewTimer(ConnDelay * time.Duration(failTimes)) - ) - defer delayTimer.Stop() - for { - children, e, err := z.ds.getClient().GetChildrenW(z.servicesPath) - if err != nil { - failTimes++ - logger.Infof("watching (path{%s}) = error{%v}", z.servicesPath, err) - if err == zk.ErrNoNode { - logger.Errorf("watching (path{%s}) got errNilNode,so exit listen", z.servicesPath) - return - } - if failTimes > MaxFailTimes { - logger.Errorf("Error happens on (path{%s}) exceed max fail times: %s,so exit listen", - z.servicesPath, MaxFailTimes) - return - } - - delayTimer.Reset(ConnDelay * time.Duration(failTimes)) - <-delayTimer.C - continue - } - failTimes = 0 - if continueLoop := z.watchEventHandle(children, e); !continueLoop { - return - } - } -} - -func (z *zkAppListener) watchEventHandle(children []string, e <-chan zk.Event) bool { - tickerTTL := defaultTTL - ticker := time.NewTicker(tickerTTL) - defer ticker.Stop() - z.handleEvent(children) - for { - select { - case <-ticker.C: - z.handleEvent(children) - case zkEvent := <-e: - logger.Debugf("get a zookeeper e{type:%s, server:%s, path:%s, state:%d-%s, err:%s}", - zkEvent.Type.String(), zkEvent.Server, zkEvent.Path, zkEvent.State, gzk.StateToString(zkEvent.State), zkEvent.Err) - z.handleEvent(children) - return true - case <-z.exit: - logger.Warnf("listen(path{%s}) goroutine exit now...", z.servicesPath) - return false - } - } -} - -func (z *zkAppListener) handleEvent(children []string) { - - fetchChildren, err := z.ds.getClient().GetChildren(z.servicesPath) - - if err != nil { - // todo refactor gost zk, make it return the definite err - if strings.Contains(err.Error(), "none children") { - logger.Debugf("%s get nodes from zookeeper fail: %s", common.ZKLogDiscovery, err.Error()) - } else { - logger.Warnf("Error when retrieving newChildren in path: %s, Error:%s", z.servicesPath, err.Error()) - } - } - - discovery := z.ds - serviceMap := discovery.getServiceMap() - - // del services - for sn := range serviceMap { - - if !contains(fetchChildren, sn) { - - // service zk event listener - serviceNodePath := strings.Join([]string{z.servicesPath, sn}, constant.PathSlash) - z.svcListeners.RemoveListener(serviceNodePath) - - // service cluster - for _, instance := range serviceMap[sn] { - _, _ = discovery.delServiceInstance(instance) - } - } - } - - for _, serviceName := range fetchChildren { - serviceNodePath := strings.Join([]string{z.servicesPath, serviceName}, constant.PathSlash) - instances := serviceMap[serviceName] - if len(instances) == 0 { - z.svcListeners.RemoveListener(serviceNodePath) - } - if z.svcListeners.GetListener(serviceNodePath) != nil { - continue - } - l := newApplicationServiceListener(serviceNodePath, serviceName, discovery) - l.wg.Add(1) - go l.WatchAndHandle() - z.svcListeners.SetListener(serviceNodePath, l) - } -} - -type SvcListeners struct { - listeners map[string]zookeeper.Listener - listenerLock sync.Mutex -} - -func (s *SvcListeners) GetListener(id string) zookeeper.Listener { - s.listenerLock.Lock() - defer s.listenerLock.Unlock() - listener, ok := s.listeners[id] - if !ok { - return nil - } - return listener -} - -func (s *SvcListeners) SetListener(id string, listener zookeeper.Listener) { - s.listenerLock.Lock() - defer s.listenerLock.Unlock() - s.listeners[id] = listener -} - -func (s *SvcListeners) RemoveListener(id string) { - s.listenerLock.Lock() - defer s.listenerLock.Unlock() - delete(s.listeners, id) -} - -func (s *SvcListeners) GetAllListener() map[string]zookeeper.Listener { - s.listenerLock.Lock() - defer s.listenerLock.Unlock() - return s.listeners -} - -func contains(elems []string, v string) bool { - for _, s := range elems { - if v == s { - return true - } - } - return false -} diff --git a/pixiu/pkg/adapter/springcloud/servicediscovery/zookeeper/service_listener.go b/pixiu/pkg/adapter/springcloud/servicediscovery/zookeeper/service_listener.go deleted file mode 100644 index 7291a1b3d..000000000 --- a/pixiu/pkg/adapter/springcloud/servicediscovery/zookeeper/service_listener.go +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 zookeeper - -import ( - "strings" - "sync" - "time" -) - -import ( - "github.com/dubbogo/go-zookeeper/zk" - gzk "github.com/dubbogo/gost/database/kv/zk" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/springcloud/common" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" -) - -type applicationServiceListener struct { - servicePath string - serviceName string - exit chan struct{} - wg sync.WaitGroup - - ds *zookeeperDiscovery -} - -func newApplicationServiceListener(path string, serviceName string, ds *zookeeperDiscovery) *applicationServiceListener { - return &applicationServiceListener{ - servicePath: path, - exit: make(chan struct{}), - ds: ds, - serviceName: serviceName, - } -} - -func (asl *applicationServiceListener) WatchAndHandle() { - defer asl.wg.Done() - - var ( - failTimes int64 = 0 - delayTimer = time.NewTimer(ConnDelay * time.Duration(failTimes)) - ) - defer delayTimer.Stop() - for { - children, e, err := asl.ds.getClient().GetChildrenW(asl.servicePath) - if err != nil { - failTimes++ - logger.Infof("watching (path{%s}) = error{%v}", asl.servicePath, err) - if err == zk.ErrNoChildrenForEphemerals { - return - } - if err == zk.ErrNoNode { - logger.Errorf("watching (path{%s}) got errNilNode,so exit listen", asl.servicePath) - return - } - if failTimes > MaxFailTimes { - logger.Errorf("Error happens on (path{%s}) exceed max fail times: %v,so exit listen", asl.servicePath, MaxFailTimes) - return - } - delayTimer.Reset(ConnDelay * time.Duration(failTimes)) - <-delayTimer.C - continue - } - failTimes = 0 - if continueLoop := asl.watchEventHandle(children, e); !continueLoop { - return - } - } -} - -func (asl *applicationServiceListener) watchEventHandle(children []string, e <-chan zk.Event) bool { - tickerTTL := defaultTTL - ticker := time.NewTicker(tickerTTL) - defer ticker.Stop() - asl.handleEvent(children) - for { - select { - case <-ticker.C: - asl.handleEvent(children) - case zkEvent := <-e: - logger.Debugf("get a zookeeper e{type:%s, server:%s, path:%s, state:%d-%s, err:%s}", - zkEvent.Type.String(), zkEvent.Server, zkEvent.Path, zkEvent.State, gzk.StateToString(zkEvent.State), zkEvent.Err) - asl.handleEvent(children) - return true - case <-asl.exit: - logger.Warnf("listen(path{%s}) goroutine exit now...", asl.servicePath) - return false - } - } -} - -func (asl *applicationServiceListener) handleEvent(children []string) { - - fetchChildren, err := asl.ds.getClient().GetChildren(asl.servicePath) - if err != nil { - // todo refactor gost zk, make it return the definite err - if strings.Contains(err.Error(), "none children") { - logger.Debugf("%s get nodes from zookeeper fail: %s", common.ZKLogDiscovery, err.Error()) - } else { - logger.Warnf("%s Error when retrieving service node [%s] in path: %s, Error:%s", common.ZKLogDiscovery, asl.serviceName, asl.servicePath, err.Error()) - } - } - discovery := asl.ds - serviceMap := discovery.getServiceMap() - instanceMap := discovery.instanceMap - - func() { - for _, id := range fetchChildren { - serviceInstance, err := discovery.queryForInstance(asl.serviceName, id) - if err != nil { - logger.Errorf("add service: %s, instance: %s has error: ", asl.serviceName, id, err.Error()) - continue - } - if instanceMap[id] == nil { - _, _ = discovery.addServiceInstance(serviceInstance) - } else { - _, _ = discovery.updateServiceInstance(serviceInstance) - } - } - }() - - func() { - serviceInstances := serviceMap[asl.serviceName] - oldInsId := []string{} - for _, instance := range serviceInstances { - oldInsId = append(oldInsId, instance.ID) - } - if delInstanceIds := Diff(oldInsId, fetchChildren); delInstanceIds != nil { - for _, id := range delInstanceIds { - _, _ = discovery.delServiceInstance(instanceMap[id]) - } - } - }() -} - -// Close closes this listener -func (asl *applicationServiceListener) Close() { - close(asl.exit) - asl.wg.Wait() -} diff --git a/pixiu/pkg/client/dubbo/config.go b/pixiu/pkg/client/dubbo/config.go deleted file mode 100644 index a5d498ff2..000000000 --- a/pixiu/pkg/client/dubbo/config.go +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 dubbo - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -// DubboProxyConfig the config for dubbo proxy -type DubboProxyConfig struct { - // Registries such as zk,nacos or etcd - Registries map[string]model.Registry `yaml:"registries" json:"registries"` - // Timeout - Timeout *model.TimeoutConfig `yaml:"timeout_config" json:"timeout_config"` - // IsDefaultMap whether to use DefaultMap role - IsDefaultMap bool - // AutoResolve whether to resolve api config from request - AutoResolve bool `yaml:"auto_resolve" json:"auto_resolve,omitempty"` - // Protoset path to load protoset files - Protoset []string `yaml:"protoset" json:"protoset,omitempty"` - // Load - LoadBalance string `yaml:"load_balance" json:"load_balance,omitempty"` -} diff --git a/pixiu/pkg/client/dubbo/dubbo.go b/pixiu/pkg/client/dubbo/dubbo.go deleted file mode 100644 index 0998e3d5b..000000000 --- a/pixiu/pkg/client/dubbo/dubbo.go +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 dubbo - -import ( - "context" - "encoding/json" - "strings" - "sync" - "time" -) - -import ( - _ "dubbo.apache.org/dubbo-go/v3/cluster/cluster/failover" - _ "dubbo.apache.org/dubbo-go/v3/cluster/loadbalance/consistenthashing" - _ "dubbo.apache.org/dubbo-go/v3/cluster/loadbalance/leastactive" - _ "dubbo.apache.org/dubbo-go/v3/cluster/loadbalance/p2c" - _ "dubbo.apache.org/dubbo-go/v3/cluster/loadbalance/random" - _ "dubbo.apache.org/dubbo-go/v3/cluster/loadbalance/ringhash" - _ "dubbo.apache.org/dubbo-go/v3/cluster/loadbalance/roundrobin" - "dubbo.apache.org/dubbo-go/v3/common/constant" - _ "dubbo.apache.org/dubbo-go/v3/common/proxy/proxy_factory" - dg "dubbo.apache.org/dubbo-go/v3/config" - "dubbo.apache.org/dubbo-go/v3/config/generic" - _ "dubbo.apache.org/dubbo-go/v3/filter/generic" - _ "dubbo.apache.org/dubbo-go/v3/filter/graceful_shutdown" - _ "dubbo.apache.org/dubbo-go/v3/metadata/service/local" - "dubbo.apache.org/dubbo-go/v3/protocol/dubbo" - _ "dubbo.apache.org/dubbo-go/v3/registry/protocol" - _ "dubbo.apache.org/dubbo-go/v3/registry/zookeeper" - hessian "github.com/apache/dubbo-go-hessian2" - fc "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" - "github.com/pkg/errors" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/propagation" - "go.opentelemetry.io/otel/trace" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client" - cst "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" -) - -// TODO java class name elem -const ( - JavaStringClassName = "java.lang.String" - JavaLangClassName = "java.lang.Long" -) - -const ( - defaultDubboProtocol = "zookeeper" - - traceNameDubbogoClient = "dubbogo-client" - spanNameDubbogoClient = "DUBBOGO CLIENT" - - spanTagMethod = "method" - spanTagType = "type" - spanTagValues = "values" -) - -var ( - dubboClient *Client - onceClient = sync.Once{} - defaultApplication = &dg.ApplicationConfig{ - Organization: "dubbo-go-pixiu", - Name: "Dubbogo Pixiu", - Module: "dubbogo Pixiu", - Version: config.Version, - Owner: "Dubbogo Pixiu", - Environment: "dev", - } -) - -// Client client to generic invoke dubbo -type Client struct { - lock sync.RWMutex - GenericServicePool map[string]*generic.GenericService - dubboProxyConfig *DubboProxyConfig - rootConfig *dg.RootConfig -} - -// SingletonDubboClient singleton dubbo clent -func SingletonDubboClient() *Client { - if dubboClient == nil { - onceClient.Do(func() { - dubboClient = NewDubboClient() - }) - } - - return dubboClient -} - -// InitDefaultDubboClient init default dubbo client -func InitDefaultDubboClient(dpc *DubboProxyConfig) { - dubboClient = NewDubboClient() - dubboClient.SetConfig(dpc) - if err := dubboClient.Apply(); err != nil { - logger.Warnf("dubbo client apply error %s", err) - } -} - -// NewDubboClient create dubbo client -func NewDubboClient() *Client { - return &Client{ - lock: sync.RWMutex{}, - GenericServicePool: make(map[string]*generic.GenericService, 4), - } -} - -// SetConfig set config -func (dc *Client) SetConfig(dpc *DubboProxyConfig) { - dc.dubboProxyConfig = dpc -} - -// Apply init dubbo, config mapping can do here -func (dc *Client) Apply() error { - - rootConfigBuilder := dg.NewRootConfigBuilder() - if dc.dubboProxyConfig != nil && dc.dubboProxyConfig.Registries != nil { - for k, v := range dc.dubboProxyConfig.Registries { - if len(v.Protocol) == 0 { - logger.Warnf("can not find registry protocol config, use default type 'zookeeper'") - v.Protocol = defaultDubboProtocol - } - rootConfigBuilder.AddRegistry(k, &dg.RegistryConfig{ - Protocol: v.Protocol, - Address: v.Address, - Timeout: v.Timeout, - Username: v.Username, - Password: v.Password, - Namespace: v.Namespace, - Group: v.Group, - }) - } - } - rootConfigBuilder.SetApplication(defaultApplication) - rootConfig := rootConfigBuilder.Build() - - if err := dg.Load(dg.WithRootConfig(rootConfig)); err != nil { - panic(err) - } - dc.rootConfig = rootConfig - return nil -} - -// Close clear GenericServicePool. -func (dc *Client) Close() error { - dc.lock.Lock() - defer dc.lock.Unlock() - for k := range dc.GenericServicePool { - delete(dc.GenericServicePool, k) - } - return nil -} - -// Call invoke service -func (dc *Client) Call(req *client.Request) (res interface{}, err error) { - // if GET with no args, values would be nil - values, err := dc.genericArgs(req) - if err != nil { - return nil, err - } - target, ok := values.(*dubboTarget) - if !ok { - return nil, errors.New("map parameters failed") - } - - dm := req.API.Method.IntegrationRequest - method := dm.Method - types := []string{} - vals := []hessian.Object{} - finalValues := []byte{} - - if target != nil { - logger.Debugf("[dubbo-go-pixiu] dubbo invoke, method:%s, types:%s, reqData:%v", method, target.Types, target.Values) - types = target.Types - vals = make([]hessian.Object, len(target.Values)) - for i, v := range target.Values { - vals[i] = v - } - var err error - finalValues, err = json.Marshal(vals) - if err != nil { - logger.Warnf("[dubbo-go-pixiu] reqData convert to string failed: %v", err) - } - } else { - logger.Debugf("[dubbo-go-pixiu] dubbo invoke, method:%s, types:%s, reqData:%v", method, nil, nil) - } - - gs := dc.Get(dm) - tr := otel.Tracer(traceNameDubbogoClient) - ctx, span := tr.Start(req.Context, spanNameDubbogoClient) - trace.SpanFromContext(req.Context).SpanContext() - span.SetAttributes(attribute.Key(spanTagMethod).String(method)) - span.SetAttributes(attribute.Key(spanTagType).StringSlice(types)) - span.SetAttributes(attribute.Key(spanTagValues).String(string(finalValues))) - defer span.End() - - // tracing inject manually; - carrier := propagation.MapCarrier{} - otel.GetTextMapPropagator().Inject(ctx, carrier) - ctxWithAttachment := context.WithValue(ctx, constant.AttachmentKey, map[string]string(carrier)) - - rst, err := gs.Invoke(ctxWithAttachment, method, types, vals) - if err != nil { - // TODO statusCode I don’t know what dubbo will return when it times out, so I will return it directly. I will judge it when I call it. - span.RecordError(err) - return nil, err - } - - logger.Debugf("[dubbo-go-pixiu] dubbo client resp:%v", rst) - - return rst, nil -} - -func (dc *Client) genericArgs(req *client.Request) (interface{}, error) { - values, err := dc.MapParams(req) - if err != nil { - return nil, err - } - - return values, nil -} - -// MapParams params mapping to api. -func (dc *Client) MapParams(req *client.Request) (interface{}, error) { - r := req.API.Method.IntegrationRequest - values := newDubboTarget(r.MappingParams) - if dc.dubboProxyConfig != nil && dc.dubboProxyConfig.IsDefaultMap { - values = newDubboTarget(defaultMappingParams) - } - for _, mappingParam := range r.MappingParams { - source, _, err := client.ParseMapSource(mappingParam.Name) - if err != nil { - return nil, err - } - if mapper, ok := mappers[source]; ok { - if err := mapper.Map(mappingParam, req, values, buildOption(mappingParam)); err != nil { - return nil, err - } - } - } - return values, nil -} - -func buildOption(conf fc.MappingParam) client.RequestOption { - var opt client.RequestOption - isGeneric, mapToType := getGenericMapTo(conf.MapTo) - if isGeneric { - opt = DefaultMapOption[mapToType] - } - return opt -} - -func (dc *Client) get(key string) *generic.GenericService { - dc.lock.RLock() - defer dc.lock.RUnlock() - return dc.GenericServicePool[key] -} - -func (dc *Client) check(key string) bool { - dc.lock.RLock() - defer dc.lock.RUnlock() - if _, ok := dc.GenericServicePool[key]; ok { - return true - } - return false -} - -// Get find a dubbo GenericService -func (dc *Client) Get(ir fc.IntegrationRequest) *generic.GenericService { - key := apiKey(&ir) - if dc.check(key) { - return dc.get(key) - } - - return dc.create(key, ir) -} - -func apiKey(ir *fc.IntegrationRequest) string { - dbc := ir.DubboBackendConfig - return strings.Join([]string{dbc.ClusterName, dbc.ApplicationName, dbc.Interface, dbc.Version, dbc.Group}, "_") -} - -func (dc *Client) create(key string, irequest fc.IntegrationRequest) *generic.GenericService { - useNacosRegister := false - registerIds := make([]string, 0) - for k, v := range dc.rootConfig.Registries { - registerIds = append(registerIds, k) - if v.Protocol == "nacos" { - useNacosRegister = true - } - } - - refConf := dg.ReferenceConfig{ - InterfaceName: irequest.Interface, - Cluster: constant.ClusterKeyFailover, - RegistryIDs: registerIds, - Protocol: dubbo.DUBBO, - Generic: "true", - Version: irequest.DubboBackendConfig.Version, - Group: irequest.Group, - Loadbalance: dc.dubboProxyConfig.LoadBalance, - } - - if len(irequest.DubboBackendConfig.Retries) == 0 { - refConf.Retries = "3" - } else { - refConf.Retries = irequest.DubboBackendConfig.Retries - } - - if dc.dubboProxyConfig.Timeout != nil { - refConf.RequestTimeout = dc.dubboProxyConfig.Timeout.RequestTimeoutStr - } else { - refConf.RequestTimeout = cst.DefaultReqTimeout.String() - } - logger.Debugf("[dubbo-go-pixiu] client dubbo timeout val %v", refConf.RequestTimeout) - dc.lock.Lock() - defer dc.lock.Unlock() - - if service, ok := dc.GenericServicePool[key]; ok { - return service - } - - if err := dg.Load(dg.WithRootConfig(dc.rootConfig)); err != nil { - panic(err) - } - - _ = refConf.Init(dc.rootConfig) - refConf.GenericLoad(key) - - // sleep when first call to fetch enough service meta data from nacos - // todo: GenericLoad should guarantee it - if useNacosRegister { - time.Sleep(1000 * time.Millisecond) - } - - clientService := refConf.GetRPCService().(*generic.GenericService) - dc.GenericServicePool[key] = clientService - - return clientService -} diff --git a/pixiu/pkg/client/dubbo/mapper.go b/pixiu/pkg/client/dubbo/mapper.go deleted file mode 100644 index 2ba5fb248..000000000 --- a/pixiu/pkg/client/dubbo/mapper.go +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 dubbo - -import ( - "bytes" - "encoding/json" - "io" - "net/url" - "reflect" - "strconv" - "strings" - "time" -) - -import ( - "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" - "github.com/pkg/errors" - "github.com/spf13/cast" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/router" -) - -var mappers = map[string]client.ParamMapper{ - constant.QueryStrings: queryStringsMapper{}, - constant.Headers: headerMapper{}, - constant.RequestBody: bodyMapper{}, - constant.RequestURI: uriMapper{}, -} - -type dubboTarget struct { - Values []interface{} // the slice contains the parameters. - Types []string // the slice contains the parameters' types. It should match the values one by one. -} - -// pre-allocate proper memory according to the params' usability. -func newDubboTarget(mps []config.MappingParam) *dubboTarget { - length := 0 - - for i := 0; i < len(mps); i++ { - isGeneric, v := getGenericMapTo(mps[i].MapTo) - if isGeneric && v != optionKeyValues { - continue - } - length++ - } - - if length > 0 { - val := make([]interface{}, length) - target := &dubboTarget{ - Values: val, - Types: make([]string, length), - } - return target - } - return nil -} - -type queryStringsMapper struct{} - -// nolint -func (qm queryStringsMapper) Map(mp config.MappingParam, c *client.Request, target interface{}, option client.RequestOption) error { - t, err := validateTarget(target) - if err != nil { - return err - } - queryValues, err := url.ParseQuery(c.IngressRequest.URL.RawQuery) - if err != nil { - return errors.Wrap(err, "Error happened when parsing the query paramters") - } - _, key, err := client.ParseMapSource(mp.Name) - if err != nil { - return err - } - pos, err := strconv.Atoi(mp.MapTo) - if err != nil && option == nil { - return errors.Errorf("Parameter mapping %v incorrect", mp) - } - qValue := queryValues.Get(key[0]) - if len(qValue) == 0 { - return errors.Errorf("Query parameter %s does not exist", key) - } - - return setTargetWithOpt(c, option, t, pos, qValue, mp.MapType) -} - -type headerMapper struct{} - -// nolint -func (hm headerMapper) Map(mp config.MappingParam, c *client.Request, target interface{}, option client.RequestOption) error { - rv, err := validateTarget(target) - if err != nil { - return err - } - _, key, err := client.ParseMapSource(mp.Name) - pos, err := strconv.Atoi(mp.MapTo) - if err != nil && option == nil { - return errors.Errorf("Parameter mapping %+v incorrect", mp) - } - header := c.IngressRequest.Header.Get(key[0]) - if len(header) == 0 { - return errors.Errorf("Header %s not found", key[0]) - } - - return setTargetWithOpt(c, option, rv, pos, header, mp.MapType) -} - -type bodyMapper struct{} - -// nolint -func (bm bodyMapper) Map(mp config.MappingParam, c *client.Request, target interface{}, option client.RequestOption) error { - // TO-DO: add support for content-type other than application/json - rv, err := validateTarget(target) - if err != nil { - return err - } - _, keys, err := client.ParseMapSource(mp.Name) - if err != nil { - return err - } - pos, err := strconv.Atoi(mp.MapTo) - if err != nil && option == nil { - return errors.Errorf("Parameter mapping %v incorrect, parameters for Dubbo backend must be mapped to an int to represent position", mp) - } - - rawBody, err := io.ReadAll(c.IngressRequest.Body) - defer func() { - c.IngressRequest.Body = io.NopCloser(bytes.NewReader(rawBody)) - }() - if err != nil { - return err - } - mapBody := map[string]interface{}{} - json.Unmarshal(rawBody, &mapBody) - val, err := client.GetMapValue(mapBody, keys) - - if err := setTargetWithOpt(c, option, rv, pos, val, mp.MapType); err != nil { - return errors.Wrap(err, "set target fail") - } - - c.IngressRequest.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - return nil -} - -type uriMapper struct{} - -// nolint -func (um uriMapper) Map(mp config.MappingParam, c *client.Request, target interface{}, option client.RequestOption) error { - rv, err := validateTarget(target) - if err != nil { - return err - } - _, keys, err := client.ParseMapSource(mp.Name) - if err != nil { - return err - } - pos, err := strconv.Atoi(mp.MapTo) - if err != nil && option == nil { - return errors.Errorf("Parameter mapping %v incorrect", mp) - } - uriValues := router.GetURIParams(&c.API, *c.IngressRequest.URL) - - return setTargetWithOpt(c, option, rv, pos, uriValues.Get(keys[0]), mp.MapType) -} - -// validateTarget verify if the incoming target for the Map function -// can be processed as expected. -func validateTarget(target interface{}) (*dubboTarget, error) { - val, ok := target.(*dubboTarget) - if !ok { - return nil, errors.New("Target params for dubbo backend must be *dubbogoTarget") - } - return val, nil -} - -func setTargetWithOpt(req *client.Request, option client.RequestOption, - target *dubboTarget, pos int, value interface{}, targetType string) error { - if option != nil { - return setGenericTarget(req, option, target, value, targetType) - } - value, err := mapTypes(targetType, value) - if err != nil { - return err - } - setCommonTarget(target, pos, value, targetType) - return nil -} - -func setGenericTarget(req *client.Request, option client.RequestOption, - target *dubboTarget, value interface{}, targetType string) error { - var err error - switch option.(type) { - case *groupOpt, *versionOpt, *interfaceOpt, *applicationOpt, *methodOpt: - err = option.Action(req, value) - case *valuesOpt: - err = option.Action(target, [2]interface{}{value, targetType}) - case *paramTypesOpt: - err = option.Action(target, value) - } - return err -} - -func setCommonTarget(target *dubboTarget, pos int, value interface{}, targetType string) { - // if the mapTo position is greater than the numbers of usable parameters, - // extend the values and types slices. It changes the address of the the target. - if cap(target.Values) <= pos { - list := make([]interface{}, pos+1-len(target.Values)) - typeList := make([]string, pos+1-len(target.Types)) - target.Values = append(target.Values, list...) - target.Types = append(target.Types, typeList...) - } - target.Values[pos] = value - target.Types[pos] = targetType -} - -func mapTypes(jType string, originVal interface{}) (interface{}, error) { - targetType, ok := constant.JTypeMapper[jType] - if !ok { - return nil, errors.Errorf("Invalid parameter type: %s", jType) - } - switch targetType { - case reflect.TypeOf(""): - return cast.ToStringE(originVal) - case reflect.TypeOf(int(0)): - return cast.ToIntE(originVal) - case reflect.TypeOf(int8(0)): - return cast.ToInt8E(originVal) - case reflect.TypeOf(int16(16)): - return cast.ToInt16E(originVal) - case reflect.TypeOf(int32(0)): - return cast.ToInt32E(originVal) - case reflect.TypeOf(int64(0)): - return cast.ToInt64E(originVal) - case reflect.TypeOf(float32(0)): - return cast.ToFloat32E(originVal) - case reflect.TypeOf(float64(0)): - return cast.ToFloat64E(originVal) - case reflect.TypeOf(true): - return cast.ToBoolE(originVal) - case reflect.TypeOf(time.Time{}): - return cast.ToTimeE(originVal) - default: - return originVal, nil - } -} - -// getGenericMapTo will parse the mapTo field, if the mapTo value is -// opt.xxx, the "opt." prefix will identify the param mapTo generic field, -// supporting generic fields: interface, group, application, method, version, -// values, types -func getGenericMapTo(mapTo string) (isGeneric bool, genericField string) { - fields := strings.Split(mapTo, ".") - if len(fields) != 2 || fields[0] != "opt" { - return false, "" - } - if _, ok := DefaultMapOption[fields[1]]; !ok { - return false, "" - } - return true, fields[1] -} diff --git a/pixiu/pkg/client/dubbo/mapper_test.go b/pixiu/pkg/client/dubbo/mapper_test.go deleted file mode 100644 index f0a431b49..000000000 --- a/pixiu/pkg/client/dubbo/mapper_test.go +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 dubbo - -import ( - "bytes" - "context" - "net/http" - "testing" -) - -import ( - "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" - "github.com/stretchr/testify/assert" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/mock" -) - -func TestQueryStringsMapper(t *testing.T) { - r, _ := http.NewRequest("GET", "/mock/test?id=12345&age=19", bytes.NewReader([]byte(""))) - api := mock.GetMockAPI(config.MethodGet, "/mock/test") - api.IntegrationRequest.MappingParams = []config.MappingParam{ - { - Name: "queryStrings.id", - MapTo: "0", - MapType: "string", - }, - { - Name: "queryStrings.name", - MapTo: "1", - MapType: "string", - }, - { - Name: "queryStrings.age", - MapTo: "jk", - MapType: "int", - }, - } - - req := client.NewReq(context.TODO(), r, api) - - params := newDubboTarget(api.IntegrationRequest.MappingParams) - qs := queryStringsMapper{} - // Giving valid mapping params - err := qs.Map(api.IntegrationRequest.MappingParams[0], req, params, nil) - // it should not return error - assert.Nil(t, err) - // it should update the target value in target position from corresponding query value in request. - assert.Equal(t, params.Values[0], "12345") - assert.Equal(t, params.Types[0], "string") - // Giving valid mapping params and same target - err = qs.Map(api.IntegrationRequest.MappingParams[1], req, params, nil) - // it should return error when request does not contain the source parameter - assert.EqualError(t, err, "Query parameter [name] does not exist") - // Giving invalid mapping params that is not a number and same target - err = qs.Map(api.IntegrationRequest.MappingParams[2], req, params, nil) - // it should return error that points out the mapping param - assert.EqualError(t, err, "Parameter mapping {queryStrings.age jk int} incorrect") - - r, _ = http.NewRequest("GET", "/mock/test?id=12345&age=19", bytes.NewReader([]byte(""))) - api = mock.GetMockAPI(config.MethodGet, "/mock/test") - api.IntegrationRequest.MappingParams = []config.MappingParam{ - { - Name: "queryStrings.id", - MapTo: "1", - MapType: "string", - }, - { - Name: "queryStrings.age", - MapTo: "0", - MapType: "int", - }, - } - - req = client.NewReq(context.TODO(), r, api) - params = newDubboTarget(api.IntegrationRequest.MappingParams) - err = qs.Map(api.IntegrationRequest.MappingParams[0], req, params, nil) - assert.Nil(t, err) - assert.Equal(t, params.Values[1], "12345") - assert.Equal(t, params.Types[1], "string") - assert.Nil(t, params.Values[0]) - err = qs.Map(api.IntegrationRequest.MappingParams[1], req, params, nil) - assert.Nil(t, err) - assert.Equal(t, params.Types[0], "int") - assert.Equal(t, params.Values[0], 19) -} - -func TestHeaderMapper(t *testing.T) { - r, _ := http.NewRequest("GET", "/mock/test?id=12345&age=19", bytes.NewReader([]byte(""))) - r.Header.Set("Auth", "1234567") - api := mock.GetMockAPI(config.MethodGet, "/mock/test") - api.IntegrationRequest.MappingParams = []config.MappingParam{ - { - Name: "headers.Auth", - MapTo: "0", - MapType: "string", - }, - } - hm := headerMapper{} - target := newDubboTarget(api.IntegrationRequest.MappingParams) - req := client.NewReq(context.TODO(), r, api) - - err := hm.Map(api.IntegrationRequest.MappingParams[0], req, target, nil) - assert.Nil(t, err) - assert.Equal(t, target.Values[0], "1234567") - assert.Equal(t, target.Types[0], "string") - - err = hm.Map(config.MappingParam{Name: "headers.Test", MapTo: "0"}, req, target, nil) - assert.EqualError(t, err, "Header Test not found") -} - -func TestBodyMapper(t *testing.T) { - r, _ := http.NewRequest("POST", "/mock/test?id=12345&age=19", bytes.NewReader([]byte(`{"sex": "male", "name":{"firstName": "Joe", "lastName": "Biden"}}`))) - r.Header.Set("Auth", "1234567") - api := mock.GetMockAPI(config.MethodGet, "/mock/test") - api.IntegrationRequest.MappingParams = []config.MappingParam{ - { - Name: "requestBody.sex", - MapTo: "0", - MapType: "string", - }, - { - Name: "requestBody.name.lastName", - MapTo: "1", - MapType: "string", - }, - { - Name: "requestBody.name", - MapTo: "2", - MapType: "object", - }, - } - bm := bodyMapper{} - target := newDubboTarget(api.IntegrationRequest.MappingParams) - req := client.NewReq(context.TODO(), r, api) - - err := bm.Map(api.IntegrationRequest.MappingParams[0], req, target, nil) - assert.Nil(t, err) - assert.Equal(t, target.Values[0], "male") - assert.Equal(t, target.Types[0], "string") - - err = bm.Map(api.IntegrationRequest.MappingParams[1], req, target, nil) - assert.Nil(t, err) - assert.Equal(t, target.Values[1], "Biden") - assert.Equal(t, target.Types[1], "string") - - err = bm.Map(api.IntegrationRequest.MappingParams[2], req, target, nil) - assert.Nil(t, err) - assert.Equal(t, target.Types[2], "object") - assert.Equal(t, target.Values[2], map[string]interface{}(map[string]interface{}{ - "firstName": "Joe", "lastName": "Biden", - })) -} - -func TestURIMapper(t *testing.T) { - r, _ := http.NewRequest("POST", "/mock/12345/joe?age=19", bytes.NewReader([]byte( - `{"sex": "male", "name":{"firstName": "Joe", "lastName": "Biden"}}`))) - r.Header.Set("Auth", "1234567") - api := mock.GetMockAPI(config.MethodGet, "/mock/:id/:name") - api.IntegrationRequest.MappingParams = []config.MappingParam{ - { - Name: "requestBody.sex", - MapTo: "0", - MapType: "string", - }, - { - Name: "requestBody.name.lastName", - MapTo: "1", - MapType: "string", - }, - { - Name: "uri.name", - MapTo: "2", - MapType: "object", - }, - { - Name: "uri.id", - MapTo: "3", - MapType: "string", - }, - } - - um := uriMapper{} - target := newDubboTarget(api.IntegrationRequest.MappingParams) - req := client.NewReq(context.TODO(), r, api) - err := um.Map(api.IntegrationRequest.MappingParams[3], req, target, nil) - assert.Nil(t, err) - err = um.Map(api.IntegrationRequest.MappingParams[2], req, target, nil) - assert.Nil(t, err) - assert.Equal(t, target.Values[2], "joe") - assert.Equal(t, target.Types[2], "object") - assert.Equal(t, target.Values[3], "12345") - assert.Equal(t, target.Types[3], "string") -} - -func TestValidateTarget(t *testing.T) { - target := newDubboTarget([]config.MappingParam{ - { - Name: "requestBody.sex", - MapTo: "0", - MapType: "string", - }, - }) - val, err := validateTarget(target) - assert.Nil(t, err) - assert.NotNil(t, val) - _, err = validateTarget(*target) - assert.EqualError(t, err, "Target params for dubbo backend must be *dubbogoTarget") - target2 := "" - _, err = validateTarget(target2) - assert.EqualError(t, err, "Target params for dubbo backend must be *dubbogoTarget") -} - -func TestMapType(t *testing.T) { - _, err := mapTypes("strings", 123) - assert.EqualError(t, err, "Invalid parameter type: strings") - - val, err := mapTypes("string", 123) - assert.Nil(t, err) - assert.Equal(t, val, "123") - _, err = mapTypes("string", []int{123, 222}) - assert.EqualError(t, err, "unable to cast []int{123, 222} of type []int to string") - - val, err = mapTypes("int", "123") - assert.Nil(t, err) - assert.Equal(t, val, 123) - val, err = mapTypes("int", 123.6) - assert.Nil(t, err) - assert.Equal(t, val, 123) - _, err = mapTypes("int", "123a") - assert.EqualError(t, err, "unable to cast \"123a\" of type string to int64") - - val, err = mapTypes("object", map[string]string{"abc": "123"}) - assert.Nil(t, err) - assert.Equal(t, val, map[string]string{"abc": "123"}) - val, err = mapTypes("object", struct{ Abc string }{Abc: "123"}) - assert.Nil(t, err) - assert.Equal(t, val, struct{ Abc string }{Abc: "123"}) - val, err = mapTypes("object", 123.6) - assert.Nil(t, err) - assert.Equal(t, val, 123.6) -} - -func TestNewDubboTarget(t *testing.T) { - mps := []config.MappingParam{ - { - Name: "string1", - MapTo: "0", - }, - { - Name: "string2", - MapTo: "opt.values", - }, - } - target := newDubboTarget(mps) - assert.NotNil(t, target) - assert.Equal(t, len(target.Values), 2) - - mps = []config.MappingParam{ - { - Name: "string1", - MapTo: "opt.interface", - }, - } - target = newDubboTarget(mps) - assert.Nil(t, target) -} - -func TestSetCommonTarget(t *testing.T) { - vals := make([]interface{}, 10) - types := make([]string, 10) - target := &dubboTarget{ - Values: vals, - Types: types, - } - setCommonTarget(target, 1, 123, "int") - assert.Equal(t, target.Values[1], 123) - assert.Equal(t, target.Types[1], "int") - assert.Nil(t, target.Values[0]) - assert.Equal(t, target.Types[0], "") - setCommonTarget(target, 10, "123", "string") - assert.Equal(t, target.Values[10], "123") - assert.Equal(t, target.Types[10], "string") -} - -func TestSetGenericTarget(t *testing.T) { - api := mock.GetMockAPI(config.MethodGet, "/mock/test") - r, _ := http.NewRequest("GET", "/mock/test?id=12345&age=19", bytes.NewReader([]byte(""))) - req := client.NewReq(context.TODO(), r, api) - - target := &dubboTarget{ - Values: make([]interface{}, 3), - Types: make([]string, 3), - } - - opt := DefaultMapOption[optionKeyValues] - err := setGenericTarget(req, opt, target, []interface{}{1, "abc", struct{ Name string }{"joe"}}, "int, string, object") - assert.Nil(t, err) - assert.Equal(t, target.Values[0], 1) - assert.Equal(t, target.Values[1], "abc") - assert.Equal(t, target.Values[2], struct{ Name string }{"joe"}) - assert.Equal(t, target.Types[0], "int") - assert.Equal(t, target.Types[1], "string") - assert.Equal(t, target.Types[2], "object") - - opt = DefaultMapOption[optionKeyTypes] - err = setGenericTarget(req, opt, target, "int, object, object", "") - assert.Nil(t, err) - assert.Equal(t, target.Types[0], "int") - assert.Equal(t, target.Types[1], "object") - assert.Equal(t, target.Types[2], "object") - - opt = DefaultMapOption[optionKeyInterface] - err = setGenericTarget(req, opt, target, "testingInterface", "") - assert.Nil(t, err) - assert.Equal(t, req.API.IntegrationRequest.Interface, "testingInterface") - - opt = DefaultMapOption[optionKeyApplication] - err = setGenericTarget(req, opt, target, "testingApplication", "") - assert.Nil(t, err) - assert.Equal(t, req.API.IntegrationRequest.ApplicationName, "testingApplication") -} - -func TestGetGenericMapTo(t *testing.T) { - isGeneric, gMapTo := getGenericMapTo("1") - assert.False(t, isGeneric) - assert.Equal(t, gMapTo, "") - - isGeneric, gMapTo = getGenericMapTo("opt.interface") - assert.True(t, isGeneric) - assert.Equal(t, gMapTo, "interface") - - isGeneric, gMapTo = getGenericMapTo("opt.whatever") - assert.False(t, isGeneric) - assert.Equal(t, gMapTo, "") -} diff --git a/pixiu/pkg/client/http/http.go b/pixiu/pkg/client/http/http.go deleted file mode 100644 index dc48169c9..000000000 --- a/pixiu/pkg/client/http/http.go +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 http - -import ( - "net/http" - "net/url" - "strings" - "sync" -) - -import ( - "github.com/pkg/errors" - "go.opentelemetry.io/otel" - semconv "go.opentelemetry.io/otel/semconv/v1.4.0" - "go.opentelemetry.io/otel/trace" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/router" -) - -// RestMetadata http metadata, api config -type RestMetadata struct { - ApplicationName string `yaml:"application_name" json:"application_name" mapstructure:"application_name"` - Group string `yaml:"group" json:"group" mapstructure:"group"` - Version string `yaml:"version" json:"version" mapstructure:"version"` - Interface string `yaml:"interface" json:"interface" mapstructure:"interface"` - Method string `yaml:"method" json:"method" mapstructure:"method"` - Types []string `yaml:"type" json:"types" mapstructure:"types"` - Retries string `yaml:"retries" json:"retries,omitempty" property:"retries"` - ClusterName string `yaml:"cluster_name" json:"cluster_name,omitempty" property:"cluster_name"` - ProtocolTypeStr string `yaml:"protocol_type" json:"protocol_type,omitempty" property:"protocol_type"` - SerializationTypeStr string `yaml:"serialization_type" json:"serialization_type,omitempty" property:"serialization_type"` -} - -var ( - httpClient *Client - countDown = sync.Once{} -) - -const ( - traceNameHTTPClient = "http-client" - jaegerTraceIDInHeader = "uber-trace-id" -) - -// Client client to generic invoke dubbo -type Client struct{} - -// SingletonHTTPClient singleton HTTP Client -func SingletonHTTPClient() *Client { - if httpClient == nil { - countDown.Do(func() { - httpClient = NewHTTPClient() - }) - } - return httpClient -} - -// NewHTTPClient create dubbo client -func NewHTTPClient() *Client { - return &Client{} -} - -// Apply only init dubbo, config mapping can do here -func (dc *Client) Apply() error { - return nil -} - -// Close close -func (dc *Client) Close() error { - return nil -} - -// Call invoke service -func (dc *Client) Call(req *client.Request) (resp interface{}, err error) { - // Map the origin parameters to backend parameters according to the API configure - transformedParams, err := dc.MapParams(req) - if err != nil { - return nil, err - } - params, _ := transformedParams.(*requestParams) - - targetURL, err := dc.parseURL(req, *params) - if err != nil { - return nil, err - } - - newReq, _ := http.NewRequest(req.IngressRequest.Method, targetURL, params.Body) - newReq.Header = params.Header - httpClient := &http.Client{Timeout: req.Timeout} - - tr := otel.Tracer(traceNameHTTPClient) - _, span := tr.Start(req.Context, "HTTP "+newReq.Method, trace.WithSpanKind(trace.SpanKindClient)) - trace.SpanFromContext(req.Context).SpanContext() - span.SetAttributes(semconv.HTTPMethodKey.String(newReq.Method)) - span.SetAttributes(semconv.HTTPTargetKey.String(targetURL)) - span.SetAttributes(semconv.HTTPFlavorKey.String(newReq.Proto)) - newReq.Header.Set(jaegerTraceIDInHeader, span.SpanContext().TraceID().String()) - defer span.End() - - tmpRet, err := httpClient.Do(newReq) - if tmpRet != nil { - span.SetAttributes(semconv.HTTPStatusCodeKey.Int(tmpRet.StatusCode)) - } - if err != nil { - span.AddEvent(semconv.ExceptionEventName, trace.WithAttributes(semconv.ExceptionMessageKey.String(err.Error()))) - urlErr, ok := err.(*url.Error) - if ok && urlErr.Timeout() { - err = errors.Errorf("http req call timeout err: %s", err.Error()) - } - } - - return tmpRet, err -} - -// MapParams param mapping to api. -func (dc *Client) MapParams(req *client.Request) (reqData interface{}, err error) { - mp := req.API.IntegrationRequest.MappingParams - r := newRequestParams() - if len(mp) == 0 { - r.Body = req.IngressRequest.Body - r.Header = req.IngressRequest.Header.Clone() - queryValues, err := url.ParseQuery(req.IngressRequest.URL.RawQuery) - if err != nil { - return nil, errors.New("Retrieve request query parameters failed") - } - r.Query = queryValues - if router.IsWildCardBackendPath(&req.API) { - r.URIParams = router.GetURIParams(&req.API, *req.IngressRequest.URL) - } - return r, nil - } - for i := 0; i < len(mp); i++ { - source, _, err := client.ParseMapSource(mp[i].Name) - if err != nil { - return nil, err - } - if mapper, ok := mappers[source]; ok { - if err := mapper.Map(mp[i], req, r, nil); err != nil { - return nil, err - } - } - } - return r, nil -} - -// ParseURL returns the actual target url. Supports wildcard target path value mapping. -func (dc *Client) parseURL(req *client.Request, params requestParams) (string, error) { - var schema string - if len(req.API.IntegrationRequest.HTTPBackendConfig.Schema) == 0 { - schema = "http" - } else { - schema = req.API.IntegrationRequest.HTTPBackendConfig.Schema - } - - rawPath := req.API.IntegrationRequest.HTTPBackendConfig.Path - if router.IsWildCardBackendPath(&req.API) { - paths := strings.Split( - strings.TrimLeft(req.API.IntegrationRequest.HTTPBackendConfig.Path, constant.PathSlash), - constant.PathSlash) - for i := 0; i < len(paths); i++ { - if strings.HasPrefix(paths[i], constant.PathParamIdentifier) { - uriParam := string(paths[i][1:len(paths[i])]) - uriValue := params.URIParams.Get(uriParam) - if len(uriValue) == 0 { - return "", errors.New("No value for target URI") - } - paths[i] = uriValue - } - } - rawPath = strings.Join(paths, constant.PathSlash) - } - - parsedURL := url.URL{ - Host: req.API.IntegrationRequest.HTTPBackendConfig.Host, - Scheme: schema, - Path: rawPath, - RawQuery: params.Query.Encode(), - } - return parsedURL.String(), nil -} diff --git a/pixiu/pkg/client/http/mapper.go b/pixiu/pkg/client/http/mapper.go deleted file mode 100644 index 5765ddb34..000000000 --- a/pixiu/pkg/client/http/mapper.go +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 http - -import ( - "bytes" - "encoding/json" - "io" - "net/http" - "net/url" - "reflect" - "strings" -) - -import ( - "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" - "github.com/pkg/errors" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/router" -) - -var mappers = map[string]client.ParamMapper{ - constant.QueryStrings: queryStringsMapper{}, - constant.Headers: headerMapper{}, - constant.RequestBody: bodyMapper{}, - constant.RequestURI: uriMapper{}, -} - -type requestParams struct { - Header http.Header - Query url.Values - Body io.ReadCloser - URIParams url.Values -} - -func newRequestParams() *requestParams { - return &requestParams{ - Header: http.Header{}, - Query: url.Values{}, - Body: io.NopCloser(bytes.NewReader([]byte(""))), - URIParams: url.Values{}, - } -} - -type queryStringsMapper struct{} - -// nolint -func (qs queryStringsMapper) Map(mp config.MappingParam, c *client.Request, rawTarget interface{}, option client.RequestOption) error { - target, fromKey, to, toKey, err := mapPrepare(mp, rawTarget) - if err != nil { - return err - } - queryValues, err := url.ParseQuery(c.IngressRequest.URL.RawQuery) - if err != nil { - return errors.Wrap(err, "Error happened when parsing the query paramters") - } - rawValue := queryValues.Get(fromKey[0]) - if len(rawValue) == 0 { - return errors.Errorf("%s in query parameters not found", fromKey[0]) - } - setTarget(target, to, toKey[0], rawValue) - return nil -} - -type headerMapper struct{} - -// nolint -func (hm headerMapper) Map(mp config.MappingParam, c *client.Request, rawTarget interface{}, option client.RequestOption) error { - target, fromKey, to, toKey, err := mapPrepare(mp, rawTarget) - if err != nil { - return err - } - - rawHeader := c.IngressRequest.Header.Get(fromKey[0]) - if len(rawHeader) == 0 { - return errors.Errorf("Header %s not found", fromKey[0]) - } - setTarget(target, to, toKey[0], rawHeader) - return nil -} - -type bodyMapper struct{} - -// nolint -func (bm bodyMapper) Map(mp config.MappingParam, c *client.Request, rawTarget interface{}, option client.RequestOption) error { - // TO-DO: add support for content-type other than application/json - target, fromKey, to, toKey, err := mapPrepare(mp, rawTarget) - if err != nil { - return err - } - - rawBody, err := io.ReadAll(c.IngressRequest.Body) - defer func() { - c.IngressRequest.Body = io.NopCloser(bytes.NewReader(rawBody)) - }() - if err != nil { - return err - } - mapBody := map[string]interface{}{} - json.Unmarshal(rawBody, &mapBody) - val, err := client.GetMapValue(mapBody, fromKey) - if err != nil { - return errors.Wrapf(err, "Error when get body value from key %s", fromKey) - } - setTarget(target, to, strings.Join(toKey, constant.Dot), val) - return nil -} - -type uriMapper struct{} - -// nolint -func (um uriMapper) Map(mp config.MappingParam, c *client.Request, rawTarget interface{}, option client.RequestOption) error { - target, fromKey, to, toKey, err := mapPrepare(mp, rawTarget) - if err != nil { - return err - } - if to == constant.RequestURI { - } - uriValues := router.GetURIParams(&c.API, *c.IngressRequest.URL) - if uriValues == nil { - return errors.New("No URI parameters found") - } - setTarget(target, to, strings.Join(toKey, constant.Dot), uriValues.Get(fromKey[0])) - return nil -} - -func validateTarget(target interface{}) (*requestParams, error) { - val, ok := target.(*requestParams) - if !ok || val == nil { - return nil, errors.New("Target params must be a requestParams pointer") - } - return val, nil -} - -func setTarget(target *requestParams, to string, key string, val interface{}) error { - valType := reflect.TypeOf(val) - if (to == constant.Headers || to == constant.QueryStrings) && valType.Kind() != reflect.String { - return errors.Errorf("%s only accepts string", to) - } - switch to { - case constant.Headers: - target.Header.Set(key, val.(string)) - case constant.RequestURI: - target.URIParams.Set(key, val.(string)) - case constant.QueryStrings: - target.Query.Set(key, val.(string)) - case constant.RequestBody: - rawBody, err := io.ReadAll(target.Body) - defer func() { - target.Body = io.NopCloser(bytes.NewReader(rawBody)) - }() - if err != nil { - return errors.New("Raw body read failed") - } - mapBody := map[string]interface{}{} - if len(rawBody) != 0 { - err = json.Unmarshal(rawBody, &mapBody) - if err != nil { - return errors.New("Raw body parse failed") - } - } - setMapWithPath(mapBody, key, val) - rawBody, err = json.Marshal(mapBody) - if err != nil { - return errors.New("Stringify map to body failed") - } - default: - return errors.Errorf("Mapping target to %s does not support", to) - } - return nil -} - -// setMapWithPath set the value to the target map. If the origin targetMap has -// {"abc": "cde": {"f":1, "g":2}} and the path is abc, value is "123", then the -// targetMap will be updated to {"abc", "123"} -func setMapWithPath(targetMap map[string]interface{}, path string, val interface{}) map[string]interface{} { - keys := strings.Split(path, constant.Dot) - - _, ok := targetMap[keys[0]] - if len(keys) == 1 { - targetMap[keys[0]] = val - return targetMap - } - if !ok && len(keys) != 1 { - targetMap[keys[0]] = make(map[string]interface{}) - targetMap[keys[0]] = setMapWithPath(targetMap[keys[0]].(map[string]interface{}), strings.Join(keys[1:], constant.Dot), val) - } - return targetMap -} - -func mapPrepare(mp config.MappingParam, rawTarget interface{}) (target *requestParams, fromKey []string, to string, toKey []string, err error) { - // ensure the target is a pointer and type is requestParams - target, err = validateTarget(rawTarget) - if err != nil { - return nil, nil, "", nil, err - } - // retrieve the mapping values' origin param name - _, fromKey, err = client.ParseMapSource(mp.Name) - if err != nil { - return nil, nil, "", nil, err - } - // retrieve the mapping values' target param name and param types(header/uri/query/request body) - to, toKey, err = client.ParseMapSource(mp.MapTo) - if err != nil { - return nil, nil, "", nil, err - } - return target, fromKey, to, toKey, nil -} diff --git a/pixiu/pkg/client/http/mapper_test.go b/pixiu/pkg/client/http/mapper_test.go deleted file mode 100644 index 251f477d6..000000000 --- a/pixiu/pkg/client/http/mapper_test.go +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 http - -import ( - "bytes" - "context" - "io" - "net/http" - "testing" -) - -import ( - "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" - "github.com/stretchr/testify/assert" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/mock" -) - -func TestQueryMapper(t *testing.T) { - qs := queryStringsMapper{} - r, _ := http.NewRequest("GET", "/mock/test?id=12345&age=19&name=joe&nickName=trump", bytes.NewReader([]byte(""))) - api := mock.GetMockAPI(config.MethodGet, "/mock/test") - api.IntegrationRequest.MappingParams = []config.MappingParam{ - { - Name: "queryStrings.id", - MapTo: "headers.Id", - }, - { - Name: "queryStrings.name", - MapTo: "queryStrings.name", - }, - { - Name: "queryStrings.age", - MapTo: "requestBody.age", - }, - { - Name: "queryStrings.nickName", - MapTo: "requestBody.nickName", - }, - } - req := client.NewReq(context.TODO(), r, api) - - target := newRequestParams() - err := qs.Map(api.IntegrationRequest.MappingParams[0], req, target, nil) - assert.Nil(t, err) - assert.Equal(t, target.Header.Get("Id"), "12345") - - err = qs.Map(api.IntegrationRequest.MappingParams[1], req, target, nil) - assert.Nil(t, err) - assert.Equal(t, target.Query.Get("name"), "joe") - - err = qs.Map(api.IntegrationRequest.MappingParams[2], req, target, nil) - assert.Nil(t, err) - err = qs.Map(api.IntegrationRequest.MappingParams[3], req, target, nil) - assert.Nil(t, err) - rawBody, _ := io.ReadAll(target.Body) - assert.Equal(t, string(rawBody), "{\"age\":\"19\",\"nickName\":\"trump\"}") - - err = qs.Map(config.MappingParam{Name: "queryStrings.doesNotExistField", MapTo: "queryStrings.whatever"}, req, target, nil) - assert.EqualError(t, err, "doesNotExistField in query parameters not found") -} - -func TestHeaderMapper(t *testing.T) { - hm := headerMapper{} - r, _ := http.NewRequest("GET", "/mock/test?id=12345&age=19&name=joe&nickName=trump", bytes.NewReader([]byte(""))) - r.Header.Set("Auth", "xxxx12345xxx") - r.Header.Set("Token", "ttttt12345ttt") - r.Header.Set("Origin-Passcode", "whoseyourdaddy") - r.Header.Set("Pokemon-Name", "Pika") - api := mock.GetMockAPI(config.MethodGet, "/mock/test") - api.IntegrationRequest.MappingParams = []config.MappingParam{ - { - Name: "headers.Auth", - MapTo: "headers.Auth", - }, - { - Name: "headers.Token", - MapTo: "headers.Token", - }, - { - Name: "headers.Origin-Passcode", - MapTo: "queryStrings.originPasscode", - }, - { - Name: "headers.Pokemon-Name", - MapTo: "requestBody.pokeMonName", - }, - } - req := client.NewReq(context.TODO(), r, api) - - target := newRequestParams() - err := hm.Map(api.IntegrationRequest.MappingParams[0], req, target, nil) - assert.Nil(t, err) - assert.Equal(t, target.Header.Get("Auth"), "xxxx12345xxx") - err = hm.Map(api.IntegrationRequest.MappingParams[1], req, target, nil) - assert.Nil(t, err) - assert.Equal(t, target.Header.Get("Token"), "ttttt12345ttt") - - err = hm.Map(api.IntegrationRequest.MappingParams[2], req, target, nil) - assert.Nil(t, err) - assert.Equal(t, target.Query.Get("originPasscode"), "whoseyourdaddy") - - err = hm.Map(api.IntegrationRequest.MappingParams[3], req, target, nil) - assert.Nil(t, err) - rawBody, err := io.ReadAll(target.Body) - assert.Nil(t, err) - assert.Equal(t, string(rawBody), "{\"pokeMonName\":\"Pika\"}") - - err = hm.Map(config.MappingParam{Name: "headers.doesNotExistField", MapTo: "headers.whatever"}, req, target, nil) - assert.EqualError(t, err, "Header doesNotExistField not found") -} - -func TestBodyMapper(t *testing.T) { - bm := bodyMapper{} - r, _ := http.NewRequest("POST", "/mock/test", bytes.NewReader([]byte("{\"id\":\"12345\",\"age\":\"19\",\"testStruct\":{\"name\":\"mock\",\"test\":\"happy\",\"nickName\":\"trump\"}}"))) - api := mock.GetMockAPI(config.MethodGet, "/mock/test") - api.IntegrationRequest.MappingParams = []config.MappingParam{ - { - Name: "requestBody.id", - MapTo: "headers.Id", - }, - { - Name: "requestBody.age", - MapTo: "requestBody.age", - }, - { - Name: "requestBody.testStruct", - MapTo: "requestBody.testStruct", - }, - { - Name: "requestBody.testStruct.nickName", - MapTo: "requestBody.nickName", - }, - } - req := client.NewReq(context.TODO(), r, api) - - target := newRequestParams() - err := bm.Map(api.IntegrationRequest.MappingParams[0], req, target, nil) - assert.Nil(t, err) - assert.Equal(t, target.Header.Get("Id"), "12345") - - target = newRequestParams() - err = bm.Map(api.IntegrationRequest.MappingParams[1], req, target, nil) - assert.Nil(t, err) - - err = bm.Map(api.IntegrationRequest.MappingParams[2], req, target, nil) - assert.Nil(t, err) - - err = bm.Map(api.IntegrationRequest.MappingParams[3], req, target, nil) - assert.Nil(t, err) - rawBody, err := io.ReadAll(target.Body) - assert.Nil(t, err) - assert.Equal(t, string(rawBody), "{\"age\":\"19\",\"nickName\":\"trump\",\"testStruct\":{\"name\":\"mock\",\"nickName\":\"trump\",\"test\":\"happy\"}}") -} - -func TestURIMap(t *testing.T) { - um := uriMapper{} - r, _ := http.NewRequest("POST", "/mock/test/12345", bytes.NewReader([]byte("{\"age\":\"19\",\"testStruct\":{\"name\":\"mock\",\"test\":\"happy\",\"nickName\":\"trump\"}}"))) - api := mock.GetMockAPI(config.MethodGet, "/mock/test/:id") - api.IntegrationRequest.MappingParams = []config.MappingParam{ - { - Name: "uri.id", - MapTo: "headers.id", - }, - } - req := client.NewReq(context.TODO(), r, api) - - target := newRequestParams() - err := um.Map(api.IntegrationRequest.MappingParams[0], req, target, nil) - assert.Nil(t, err) - assert.Equal(t, target.Header.Get("Id"), "12345") - - target = newRequestParams() - err = um.Map(config.MappingParam{ - Name: "uri.id", - MapTo: "uri.id", - }, req, target, nil) - assert.Nil(t, err) - assert.Equal(t, target.URIParams.Get("id"), "12345") -} - -func TestSetTarget(t *testing.T) { - emptyRequestParams := newRequestParams() - err := setTarget(emptyRequestParams, constant.Headers, "Auth", "1234565") - assert.Nil(t, err) - assert.Equal(t, emptyRequestParams.Header.Get("Auth"), "1234565") - err = setTarget(emptyRequestParams, constant.Headers, "Auth", 1234565) - assert.EqualError(t, err, "headers only accepts string") - - err = setTarget(emptyRequestParams, constant.QueryStrings, "id", "123") - assert.Nil(t, err) - assert.Equal(t, emptyRequestParams.Query.Get("id"), "123") - err = setTarget(emptyRequestParams, constant.QueryStrings, "id", 123) - assert.EqualError(t, err, "queryStrings only accepts string") - - err = setTarget(emptyRequestParams, constant.RequestBody, "testStruct", map[string]interface{}{"test": "happy", "name": "mock"}) - assert.Nil(t, err) - rawBody, err := io.ReadAll(emptyRequestParams.Body) - assert.Nil(t, err) - assert.Equal(t, string(rawBody), "{\"testStruct\":{\"name\":\"mock\",\"test\":\"happy\"}}") - - err = setTarget(emptyRequestParams, constant.RequestBody, "testStruct.secondLayer", map[string]interface{}{"test": "happy", "name": "mock"}) - assert.Nil(t, err) - rawBody, err = io.ReadAll(emptyRequestParams.Body) - assert.Nil(t, err) - assert.Equal(t, string(rawBody), "{\"testStruct\":{\"secondLayer\":{\"name\":\"mock\",\"test\":\"happy\"}}}") - - err = setTarget(emptyRequestParams, constant.RequestBody, "testStruct.secondLayer.thirdLayer", map[string]interface{}{"test": "happy", "name": "mock"}) - assert.Nil(t, err) - rawBody, err = io.ReadAll(emptyRequestParams.Body) - assert.Nil(t, err) - assert.Equal(t, string(rawBody), "{\"testStruct\":{\"secondLayer\":{\"thirdLayer\":{\"name\":\"mock\",\"test\":\"happy\"}}}}") - - err = setTarget(emptyRequestParams, constant.RequestURI, "id", "12345") - assert.Nil(t, err) - assert.Equal(t, emptyRequestParams.URIParams.Get("id"), "12345") - - nonEmptyRequestParams := newRequestParams() - nonEmptyRequestParams.Body = io.NopCloser(bytes.NewReader([]byte("{\"testStruct\":\"abcde\"}"))) - err = setTarget(nonEmptyRequestParams, constant.RequestBody, "testStruct", map[string]interface{}{"test": "happy", "name": "mock"}) - assert.Nil(t, err) - rawBody, err = io.ReadAll(nonEmptyRequestParams.Body) - assert.Nil(t, err) - assert.Equal(t, string(rawBody), "{\"testStruct\":{\"name\":\"mock\",\"test\":\"happy\"}}") - - nonEmptyRequestParams = newRequestParams() - nonEmptyRequestParams.Body = io.NopCloser(bytes.NewReader([]byte("{\"otherStructure\":\"abcde\"}"))) - err = setTarget(nonEmptyRequestParams, constant.RequestBody, "testStruct", map[string]interface{}{"test": "happy", "name": "mock"}) - assert.Nil(t, err) - rawBody, err = io.ReadAll(nonEmptyRequestParams.Body) - assert.Nil(t, err) - assert.Equal(t, string(rawBody), "{\"otherStructure\":\"abcde\",\"testStruct\":{\"name\":\"mock\",\"test\":\"happy\"}}") -} - -func TestValidateTarget(t *testing.T) { - requestP := newRequestParams() - p, e := validateTarget(requestP) - assert.NotNil(t, p) - assert.Nil(t, e) - - requestP = nil - p, e = validateTarget(requestP) - assert.Nil(t, p) - assert.NotNil(t, e) - - requestP2 := []int{} - p, e = validateTarget(requestP2) - assert.Nil(t, p) - assert.NotNil(t, e) - - requestP3 := struct{}{} - p, e = validateTarget(requestP3) - assert.Nil(t, p) - assert.NotNil(t, e) -} diff --git a/pixiu/pkg/client/mapper.go b/pixiu/pkg/client/mapper.go deleted file mode 100644 index 03b6bf3c4..000000000 --- a/pixiu/pkg/client/mapper.go +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 client - -import ( - "reflect" - "regexp" - "strings" -) - -import ( - "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" - "github.com/pkg/errors" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" -) - -// ParamMapper defines the interface about how to map the params in the inbound request. -type ParamMapper interface { - // Map implements how the request parameters map to the target parameters described by config.MappingParam - Map(config.MappingParam, *Request, interface{}, RequestOption) error -} - -// ParseMapSource parses the source parameter config in the mappingParams -// the source parameter in config could be queryStrings.*, headers.*, requestBody.* -func ParseMapSource(source string) (from string, params []string, err error) { - reg := regexp.MustCompile(`^([uri|queryStrings|headers|requestBody][\w|\d]+)\.([\w|\d|\.|\-]+)$`) - if !reg.MatchString(source) { - return "", nil, errors.New("Parameter mapping config incorrect. Please fix it") - } - ps := reg.FindStringSubmatch(source) - return ps[1], strings.Split(ps[2], "."), nil -} - -// GetMapValue return the value from map base on the path -func GetMapValue(sourceMap map[string]interface{}, keys []string) (interface{}, error) { - if len(keys) > 0 && keys[0] == constant.DefaultBodyAll { - return sourceMap, nil - } - _, ok := sourceMap[keys[0]] - if !ok { - return nil, errors.Errorf("%s does not exist in request body", keys[0]) - } - rvalue := reflect.ValueOf(sourceMap[keys[0]]) - if ok && len(keys) == 1 { - return rvalue.Interface(), nil - } - if rvalue.Type().Kind() != reflect.Map && len(keys) == 1 { - return rvalue.Interface(), nil - } - deeperStruct, ok := sourceMap[keys[0]].(map[string]interface{}) - if !ok { - return nil, errors.Errorf("%s is not a map structure. It contains %v", keys[0], sourceMap[keys[0]]) - } - return GetMapValue(deeperStruct, keys[1:]) -} diff --git a/pixiu/pkg/client/proxy/descriptor_source.go b/pixiu/pkg/client/proxy/descriptor_source.go deleted file mode 100644 index 3240ef02a..000000000 --- a/pixiu/pkg/client/proxy/descriptor_source.go +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ - -// This file contains code that is copied and modified from: -// https://github.com/fullstorydev/grpcurl/blob/v1.8.7/desc_source.go - -package proxy - -import ( - "os" - "sync" -) - -import ( - "github.com/jhump/protoreflect/desc" - "github.com/jhump/protoreflect/dynamic" - "github.com/pkg/errors" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/descriptorpb" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/http/grpcproxy" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" -) - -var ( - sourceOnce sync.Once - protosetSource grpcproxy.DescriptorSource -) - -func InitProtosetSource(protoset []string) { - sourceOnce.Do(func() { - var err error - protosetSource, err = DescriptorSourceFromProtoset(protoset) - if err != nil { - logger.Infof("[dubbo-go-pixiu] could not load protoset files: %v", err) - } - }) -} - -// DescriptorSourceFromProtoset creates a DescriptorSource that is backed by the named files, -// whose contents are Protocol Buffer source files. The given importPaths are used to locate -// any imported files. -func DescriptorSourceFromProtoset(filenames []string) (grpcproxy.DescriptorSource, error) { - if len(filenames) < 1 { - return nil, errors.New("no protoset files provided") - } - files := &descriptorpb.FileDescriptorSet{} - for _, filename := range filenames { - b, err := os.ReadFile(filename) - if err != nil { - return nil, errors.Errorf("wrong path to load protoset file %q: %v", filename, err) - } - var fs descriptorpb.FileDescriptorSet - err = proto.Unmarshal(b, &fs) - if err != nil { - return nil, errors.Errorf("could not parse contents of protoset file %q: %v", filename, err) - } - files.File = append(files.File, fs.File...) - } - return DescriptorSourceFromFileDescriptorSet(files) -} - -// DescriptorSourceFromFileDescriptorSet creates a DescriptorSource that is backed by the FileDescriptorSet. -func DescriptorSourceFromFileDescriptorSet(files *descriptorpb.FileDescriptorSet) (grpcproxy.DescriptorSource, error) { - unresolved := map[string]*descriptorpb.FileDescriptorProto{} - for _, fd := range files.File { - unresolved[fd.GetName()] = fd - } - resolved := map[string]*desc.FileDescriptor{} - for _, fd := range files.File { - _, err := resolveFileDescriptor(unresolved, resolved, fd.GetName()) - if err != nil { - return nil, err - } - } - return &protosetFileSource{files: resolved}, nil -} - -func resolveFileDescriptor(unresolved map[string]*descriptorpb.FileDescriptorProto, resolved map[string]*desc.FileDescriptor, filename string) (*desc.FileDescriptor, error) { - if r, ok := resolved[filename]; ok { - return r, nil - } - fd, ok := unresolved[filename] - if !ok { - return nil, errors.Errorf("no descriptor found for %q", filename) - } - deps := make([]*desc.FileDescriptor, 0, len(fd.GetDependency())) - for _, dep := range fd.GetDependency() { - depFd, err := resolveFileDescriptor(unresolved, resolved, dep) - if err != nil { - return nil, err - } - deps = append(deps, depFd) - } - result, err := desc.CreateFileDescriptor(fd, deps...) - if err != nil { - return nil, err - } - resolved[filename] = result - return result, nil -} - -type protosetFileSource struct { - files map[string]*desc.FileDescriptor - er *dynamic.ExtensionRegistry - erInit sync.Once -} - -func (fs *protosetFileSource) ListServices() ([]string, error) { - set := map[string]bool{} - for _, fd := range fs.files { - for _, svc := range fd.GetServices() { - set[svc.GetFullyQualifiedName()] = true - } - } - sl := make([]string, 0, len(set)) - for svc := range set { - sl = append(sl, svc) - } - return sl, nil -} - -func (fs *protosetFileSource) FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) { - for _, fd := range fs.files { - if dsc := fd.FindSymbol(fullyQualifiedName); dsc != nil { - return dsc, nil - } - } - return nil, errors.Errorf("Symbol not found: %s", fullyQualifiedName) -} - -func (fs *protosetFileSource) AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) { - fs.erInit.Do(func() { - fs.er = &dynamic.ExtensionRegistry{} - for _, fd := range fs.files { - fs.er.AddExtensionsFromFile(fd) - } - }) - return fs.er.AllExtensionsForType(typeName), nil -} diff --git a/pixiu/pkg/cluster/cluster.go b/pixiu/pkg/cluster/cluster.go deleted file mode 100644 index f5f6dc42b..000000000 --- a/pixiu/pkg/cluster/cluster.go +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 cluster - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/cluster/healthcheck" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -type Cluster struct { - HealthCheck *healthcheck.HealthChecker - Config *model.ClusterConfig -} - -func NewCluster(clusterConfig *model.ClusterConfig) *Cluster { - c := &Cluster{ - Config: clusterConfig, - } - - // only handle one health checker - if len(c.Config.HealthChecks) != 0 { - c.HealthCheck = healthcheck.CreateHealthCheck(clusterConfig, c.Config.HealthChecks[0]) - c.HealthCheck.Start() - } - return c -} - -func (c *Cluster) Stop() { - if c.HealthCheck != nil { - c.HealthCheck.Stop() - } -} - -func (c *Cluster) RemoveEndpoint(endpoint *model.Endpoint) { - if c.HealthCheck != nil { - c.HealthCheck.StopOne(endpoint) - } -} - -func (c *Cluster) AddEndpoint(endpoint *model.Endpoint) { - if c.HealthCheck != nil { - c.HealthCheck.StartOne(endpoint) - } -} diff --git a/pixiu/pkg/cluster/healthcheck/tcp.go b/pixiu/pkg/cluster/healthcheck/tcp.go deleted file mode 100644 index dbcfad969..000000000 --- a/pixiu/pkg/cluster/healthcheck/tcp.go +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 healthcheck - -import ( - "net" - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" -) - -type TCPChecker struct { - addr string - timeout time.Duration -} - -func (s *TCPChecker) CheckHealth() bool { - conn, err := net.DialTimeout("tcp", s.addr, s.timeout) - if err != nil { - logger.Infof("[health check] tcp checker for host %s error: %v", s.addr, err) - return false - } - conn.Close() - return true -} - -func (s *TCPChecker) OnTimeout() {} diff --git a/pixiu/pkg/cmd/gateway.go b/pixiu/pkg/cmd/gateway.go deleted file mode 100644 index 111a1d969..000000000 --- a/pixiu/pkg/cmd/gateway.go +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 cmd - -import ( - "fmt" - "os" - "runtime" - "strconv" -) - -import ( - "github.com/spf13/cobra" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - pxruntime "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/runtime" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server" -) - -var ( - flagToLogLevel = map[string]string{ - "trace": "TRACE", - "debug": "DEBUG", - "info": "INFO", - "warning": "WARN", - "error": "ERROR", - "critical": "FATAL", - } - - configPath string - apiConfigPath string - logConfigPath string - logLevel string - - // CURRENTLY USELESS - logFormat string - - limitCpus string - - // Currently default set to false, wait for up coming support - initFromRemote = false -) - -var ( - GatewayCmd = &cobra.Command{ - Use: "gateway", - Short: "Run dubbo go pixiu in gateway mode", - } - - deploy = &DefaultDeployer{ - configManger: config.NewConfigManger(), - } - - startGatewayCmd = &cobra.Command{ - Use: "start", - Short: "Start gateway", - PreRun: func(cmd *cobra.Command, args []string) { - initDefaultValue() - - err := deploy.initialize() - if err != nil { - panic(err) - } - }, - Run: func(cmd *cobra.Command, args []string) { - - err := deploy.start() - if err != nil { - panic(err) - } - }, - } -) - -// init Init startCmd -func init() { - startGatewayCmd.PersistentFlags().StringVarP(&configPath, constant.ConfigPathKey, "c", os.Getenv(constant.EnvDubbogoPixiuConfig), "Load configuration from `FILE`") - startGatewayCmd.PersistentFlags().StringVarP(&apiConfigPath, constant.ApiConfigPathKey, "a", os.Getenv(constant.EnvDubbogoPixiuApiConfig), "Load api configuration from `FILE`") - startGatewayCmd.PersistentFlags().StringVarP(&logConfigPath, constant.LogConfigPathKey, "g", os.Getenv(constant.EnvDubbogoPixiuLogConfig), "Load log configuration from `FILE`") - startGatewayCmd.PersistentFlags().StringVarP(&logLevel, constant.LogLevelKey, "l", os.Getenv(constant.EnvDubbogoPixiuLogLevel), "dubbogo pixiu log level, trace|debug|info|warning|error|critical") - startGatewayCmd.PersistentFlags().StringVarP(&limitCpus, constant.LimitCpusKey, "m", os.Getenv(constant.EnvDubbogoPixiuLimitCpus), "dubbogo pixiu schedule threads count") - startGatewayCmd.PersistentFlags().StringVarP(&logFormat, constant.LogFormatKey, "f", os.Getenv(constant.EnvDubbogoPixiuLogFormat), "dubbogo pixiu log format, currently useless") - - GatewayCmd.AddCommand(startGatewayCmd) -} - -type DefaultDeployer struct { - bootstrap *model.Bootstrap - configManger *config.ConfigManager -} - -func (d *DefaultDeployer) initialize() error { - err := initLog() - if err != nil { - logger.Warnf("[startGatewayCmd] failed to init logger, %s", err.Error()) - } - - // load Bootstrap config - d.bootstrap = d.configManger.LoadBootConfig(configPath) - - err = initLimitCpus() - if err != nil { - logger.Errorf("[startCmd] failed to get limit cpu number, %s", err.Error()) - } - - return err -} - -func (d *DefaultDeployer) start() error { - server.Start(d.bootstrap) - return nil -} - -func (d *DefaultDeployer) stop() error { - //TODO implement me - panic("implement me") -} - -// initDefaultValue If not set both in args and env, set default values -func initDefaultValue() { - if configPath == "" { - configPath = constant.DefaultConfigPath - } - - if apiConfigPath == "" { - apiConfigPath = constant.DefaultApiConfigPath - } - - if logConfigPath == "" { - logConfigPath = constant.DefaultLogConfigPath - } - - if logLevel == "" { - logLevel = constant.DefaultLogLevel - } - - if limitCpus == "" { - limitCpus = constant.DefaultLimitCpus - } - - if logFormat == "" { - logFormat = constant.DefaultLogFormat - } -} - -// initLog -func initLog() error { - err := logger.InitLog(logConfigPath) - if err != nil { - // cause `logger.InitLog` already handle init failed, so just use logger to log - return err - } - - if level, ok := flagToLogLevel[logLevel]; ok { - logger.SetLoggerLevel(level) - } else { - logger.SetLoggerLevel(flagToLogLevel[constant.DefaultLogLevel]) - return fmt.Errorf("logLevel is invalid, set log level to default: %s", constant.DefaultLogLevel) - } - return nil -} - -// initApiConfig return value of the bool is for the judgment of whether is a api meta data error, a kind of silly (?) -func initApiConfig() (*model.Bootstrap, error) { - bootstrap := config.Load(configPath) - return bootstrap, nil -} - -func initLimitCpus() error { - limitCpuNumberFromEnv, err := strconv.ParseInt(limitCpus, 10, 64) - if err != nil { - return err - } - limitCpuNumber := int(limitCpuNumberFromEnv) - if limitCpuNumber <= 0 { - limitCpuNumber = pxruntime.GetCPUNum() - } - runtime.GOMAXPROCS(limitCpuNumber) - logger.Infof("GOMAXPROCS set to %v", limitCpuNumber) - return nil -} diff --git a/pixiu/pkg/common/constant/http.go b/pixiu/pkg/common/constant/http.go deleted file mode 100644 index 1a17040c3..000000000 --- a/pixiu/pkg/common/constant/http.go +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 constant - -const ( - HeaderKeyContextType = "Content-Type" - - HeaderKeyAccessControlAllowOrigin = "Access-Control-Allow-Origin" - HeaderKeyAccessControlAllowHeaders = "Access-Control-Allow-Headers" - HeaderKeyAccessControlExposeHeaders = "Access-Control-Expose-Headers" - HeaderKeyAccessControlAllowMethods = "Access-Control-Allow-Methods" - HeaderKeyAccessControlMaxAge = "Access-Control-Max-Age" - HeaderKeyAccessControlAllowCredentials = "Access-Control-Allow-Credentials" - - HeaderValueJsonUtf8 = "application/json;charset=UTF-8" - HeaderValueTextPlain = "text/plain" - HeaderValueApplicationJson = "application/json" - - HeaderValueAll = "*" - - PathSlash = "/" - ProtocolSlash = "://" - PathParamIdentifier = ":" -) - -const ( - Http1HeaderKeyHost = "Host" - Http2HeaderKeyHost = ":authority" -) - -const ( - PprofDefaultAddress = "0.0.0.0" - PprofDefaultPort = 7070 -) - -const ( - Get = "GET" - Put = "PUT" - Post = "POST" - Delete = "DELETE" - Options = "OPTIONS" -) - -const ( - DubboHttpDubboVersion = "x-dubbo-http1.1-dubbo-version" - DubboServiceProtocol = "x-dubbo-service-protocol" - DubboServiceVersion = "x-dubbo-service-version" - DubboGroup = "x-dubbo-service-group" - DubboServiceMethodTypes = "x-dubbo-service-method-overloading" -) diff --git a/pixiu/pkg/common/extension/adapter/adapter.go b/pixiu/pkg/common/extension/adapter/adapter.go deleted file mode 100644 index 8d9d893b0..000000000 --- a/pixiu/pkg/common/extension/adapter/adapter.go +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 adapter - -import ( - "fmt" -) - -import ( - "github.com/pkg/errors" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -type ( - // AdapterPlugin plugin for adapter - AdapterPlugin interface { - // Kind returns the unique kind name to represent itself. - Kind() string - - // CreateAdapter return the Adapter callback - CreateAdapter(ad *model.Adapter) (Adapter, error) - } - - // Adapter adapter interface - Adapter interface { - // Start start adapter lifetime - Start() - // Stop stop adapter lifetime - Stop() - // Apply init - Apply() error - // Config get config for Adapter - Config() interface{} - } -) - -var ( - adapterPlugins = map[string]AdapterPlugin{} -) - -// RegisterAdapterPlugin registers adapter plugin -func RegisterAdapterPlugin(p AdapterPlugin) { - if p.Kind() == "" { - panic(fmt.Errorf("%T: empty kind", p)) - } - - existedPlugin, existed := adapterPlugins[p.Kind()] - if existed { - panic(fmt.Errorf("%T and %T got same kind: %s", p, existedPlugin, p.Kind())) - } - - adapterPlugins[p.Kind()] = p -} - -// GetAdapterPlugin get plugin by kind -func GetAdapterPlugin(kind string) (AdapterPlugin, error) { - existedAdapter, existed := adapterPlugins[kind] - if existed { - return existedAdapter, nil - } - return nil, errors.Errorf("plugin not found %s", kind) -} diff --git a/pixiu/pkg/common/extension/filter/filter.go b/pixiu/pkg/common/extension/filter/filter.go deleted file mode 100644 index b95873b0f..000000000 --- a/pixiu/pkg/common/extension/filter/filter.go +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 filter - -import ( - "context" - "fmt" - stdHttp "net/http" -) - -import ( - "github.com/pkg/errors" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/dubbo" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" -) - -type ( - // HttpFilterPlugin describe plugin - HttpFilterPlugin interface { - // Kind returns the unique kind name to represent itself. - Kind() string - - // CreateFilterFactory return the filter factory - CreateFilterFactory() (HttpFilterFactory, error) - } - - // HttpFilterFactory describe http filter - HttpFilterFactory interface { - // Config Expose the config so that Filter Manger can inject it, so it must be a pointer - Config() interface{} - - // Apply After the config is injected, check it or make it to default - Apply() error - - // PrepareFilterChain create filter and append it to FilterChain - // - // Be Careful !!! Do not pass the Factory's config pointer to the Filter instance, - // Factory's config may be updated by FilterManager - PrepareFilterChain(ctx *http.HttpContext, chain FilterChain) error - } - - // HttpDecodeFilter before invoke upstream, like add/remove Header, route mutation etc.. - // - // if config like this: - // - A - // - B - // - C - // decode filters will be invoked in the config order: Aã€Bã€C, and decode filters will be - // invoked in the reverse order: Cã€Bã€A - HttpDecodeFilter interface { - Decode(ctx *http.HttpContext) FilterStatus - } - - // HttpEncodeFilter after invoke upstream, - // decode filters will be invoked in the reverse order - HttpEncodeFilter interface { - Encode(ctx *http.HttpContext) FilterStatus - } - - // NetworkFilter describe network filter plugin - NetworkFilterPlugin interface { - // Kind returns the unique kind name to represent itself. - Kind() string - // CreateFilterFactory return the filter callback - CreateFilter(config interface{}) (NetworkFilter, error) - // Config Expose the config so that Filter Manger can inject it, so it must be a pointer - Config() interface{} - } - - // NetworkFilter describe network filter - NetworkFilter interface { - // ServeHTTP handle http request and response - ServeHTTP(w stdHttp.ResponseWriter, r *stdHttp.Request) - // OnDecode decode bytes received from getty server - OnDecode(data []byte) (interface{}, int, error) - // OnEncode encode interface sent to getty server - OnEncode(p interface{}) ([]byte, error) - // OnData dubbo rpc invocation from getty server - OnData(data interface{}) (interface{}, error) - // OnTripleData triple rpc invocation from triple-server - OnTripleData(ctx context.Context, methodName string, arguments []interface{}) (interface{}, error) - } - - // EmptyNetworkFilter default empty network filter adapter which offers empty function implements - EmptyNetworkFilter struct{} - - // DubboFilter describe dubbo filter - DubboFilter interface { - // Handle rpc invocation - Handle(ctx *dubbo.RpcContext) FilterStatus - } - - // NetworkFilter describe dubbo filter plugin - DubboFilterPlugin interface { - // Kind returns the unique kind name to represent itself. - Kind() string - // CreateFilter return the filter callback - CreateFilter(config interface{}) (DubboFilter, error) - // Config Expose the config so that Filter Manger can inject it, so it must be a pointer - Config() interface{} - } -) - -var ( - httpFilterPluginRegistry = map[string]HttpFilterPlugin{} - networkFilterPluginRegistry = map[string]NetworkFilterPlugin{} - dubboFilterPluginRegistry = map[string]DubboFilterPlugin{} -) - -// OnDecode empty implement -func (enf *EmptyNetworkFilter) OnDecode(data []byte) (interface{}, int, error) { - panic("OnDecode is not implemented") -} - -// OnEncode empty implement -func (enf *EmptyNetworkFilter) OnEncode(p interface{}) ([]byte, error) { - panic("OnEncode is not implemented") -} - -// OnData receive data from listener -func (enf *EmptyNetworkFilter) OnData(data interface{}) (interface{}, error) { - panic("OnData is not implemented") -} - -// OnTripleData empty implement -func (enf *EmptyNetworkFilter) OnTripleData(ctx context.Context, methodName string, arguments []interface{}) (interface{}, error) { - panic("OnTripleData is not implemented") -} - -// ServeHTTP empty implement -func (enf *EmptyNetworkFilter) ServeHTTP(w stdHttp.ResponseWriter, r *stdHttp.Request) { - panic("ServeHTTP is not implemented") -} - -// Register registers filter plugin. -func RegisterHttpFilter(f HttpFilterPlugin) { - if f.Kind() == "" { - panic(fmt.Errorf("%T: empty kind", f)) - } - - existedPlugin, existed := httpFilterPluginRegistry[f.Kind()] - if existed { - panic(fmt.Errorf("%T and %T got same kind: %s", f, existedPlugin, f.Kind())) - } - - httpFilterPluginRegistry[f.Kind()] = f -} - -// GetHttpFilterPlugin get plugin by kind -func GetHttpFilterPlugin(kind string) (HttpFilterPlugin, error) { - existedFilter, existed := httpFilterPluginRegistry[kind] - if existed { - return existedFilter, nil - } - return nil, errors.Errorf("plugin not found %s", kind) -} - -// RegisterNetworkFilter registers network filter. -func RegisterNetworkFilterPlugin(f NetworkFilterPlugin) { - if f.Kind() == "" { - panic(fmt.Errorf("%T: empty kind", f)) - } - - existedFilter, existed := networkFilterPluginRegistry[f.Kind()] - if existed { - panic(fmt.Errorf("%T and %T got same kind: %s", f, existedFilter, f.Kind())) - } - - networkFilterPluginRegistry[f.Kind()] = f -} - -// GetNetworkFilterPlugin get plugin by kind -func GetNetworkFilterPlugin(kind string) (NetworkFilterPlugin, error) { - existedFilter, existed := networkFilterPluginRegistry[kind] - if existed { - return existedFilter, nil - } - return nil, errors.Errorf("plugin not found %s", kind) -} - -// RegisterDubboFilterPlugin registers dubbo filter. -func RegisterDubboFilterPlugin(f DubboFilterPlugin) { - if f.Kind() == "" { - panic(fmt.Errorf("%T: empty kind", f)) - } - - existedFilter, existed := dubboFilterPluginRegistry[f.Kind()] - if existed { - panic(fmt.Errorf("%T and %T got same kind: %s", f, existedFilter, f.Kind())) - } - - dubboFilterPluginRegistry[f.Kind()] = f -} - -// GetDubboFilterPlugin get plugin by kind -func GetDubboFilterPlugin(kind string) (DubboFilterPlugin, error) { - existedFilter, existed := dubboFilterPluginRegistry[kind] - if existed { - return existedFilter, nil - } - return nil, errors.Errorf("plugin not found %s", kind) -} diff --git a/pixiu/pkg/common/grpc/manager.go b/pixiu/pkg/common/grpc/manager.go deleted file mode 100644 index fec3c8913..000000000 --- a/pixiu/pkg/common/grpc/manager.go +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 grpc - -import ( - "context" - "crypto/tls" - "encoding/base64" - "fmt" - "io" - "net" - stdHttp "net/http" -) - -import ( - "github.com/dubbogo/grpc-go/codes" - "github.com/dubbogo/grpc-go/status" - "golang.org/x/net/http2" - "google.golang.org/protobuf/proto" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - router2 "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/router" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server" -) - -// GrpcConnectionManager network filter for grpc -type GrpcConnectionManager struct { - filter.EmptyNetworkFilter - config *model.GRPCConnectionManagerConfig - routerCoordinator *router2.RouterCoordinator -} - -// CreateGrpcConnectionManager create grpc connection manager -func CreateGrpcConnectionManager(hcmc *model.GRPCConnectionManagerConfig) *GrpcConnectionManager { - hcm := &GrpcConnectionManager{config: hcmc} - hcm.routerCoordinator = router2.CreateRouterCoordinator(&hcmc.RouteConfig) - return hcm -} - -// ServeHTTP handle request and response -func (gcm *GrpcConnectionManager) ServeHTTP(w stdHttp.ResponseWriter, r *stdHttp.Request) { - - ra, err := gcm.routerCoordinator.RouteByPathAndName(r.RequestURI, r.Method) - if err != nil { - logger.Infof("GrpcConnectionManager can't find route %v", err) - gcm.writeStatus(w, status.New(codes.NotFound, fmt.Sprintf("proxy can't find route error = %v", err))) - return - } - - logger.Debugf("[dubbo-go-pixiu] client choose endpoint from cluster :%v", ra.Cluster) - - clusterName := ra.Cluster - clusterManager := server.GetClusterManager() - endpoint := clusterManager.PickEndpoint(clusterName, &http.HttpContext{Request: r}) - if endpoint == nil { - logger.Infof("GrpcConnectionManager can't find endpoint in cluster") - gcm.writeStatus(w, status.New(codes.Unknown, "can't find endpoint in cluster")) - return - } - ctx := context.Background() - // timeout - ctx, cancel := context.WithTimeout(ctx, gcm.config.Timeout) - defer cancel() - newReq := r.Clone(ctx) - newReq.URL.Scheme = "http" - newReq.URL.Host = endpoint.Address.GetAddress() - - // todo: need cache? - forwarder := gcm.newHttpForwarder() - res, err := forwarder.Forward(newReq) - - if err != nil { - logger.Infof("GrpcConnectionManager forward request error %v", err) - if err == context.DeadlineExceeded { - gcm.writeStatus(w, status.New(codes.DeadlineExceeded, fmt.Sprintf("forward timeout error = %v", err))) - return - } - gcm.writeStatus(w, status.New(codes.Unknown, fmt.Sprintf("forward error not = %v", err))) - return - } - - if err := gcm.response(w, res); err != nil { - logger.Infof("GrpcConnectionManager response error %v", err) - } -} - -func (gcm *GrpcConnectionManager) writeStatus(w stdHttp.ResponseWriter, status *status.Status) { - w.Header().Set("Grpc-Status", fmt.Sprintf("%d", status.Code())) - w.Header().Set("Grpc-Message", status.Message()) - w.Header().Set("Content-Type", "application/grpc") - - if p := status.Proto(); p != nil && len(p.Details) > 0 { - stBytes, err := proto.Marshal(p) - if err != nil { - logger.Warnf("GrpcConnectionManager writeStatus status proto marshal error: %s", err.Error()) - } else { - w.Header().Set("Grpc-Status-Details-Bin", base64.RawStdEncoding.EncodeToString(stBytes)) - } - } - - w.WriteHeader(stdHttp.StatusOK) -} - -func (gcm *GrpcConnectionManager) response(w stdHttp.ResponseWriter, res *stdHttp.Response) error { - defer res.Body.Close() - - copyHeader(w.Header(), res.Header) - w.WriteHeader(res.StatusCode) - - bytes, err := io.ReadAll(res.Body) - if err != nil { - return err - } - w.Write(bytes) - - for k, vv := range res.Trailer { - k = stdHttp.TrailerPrefix + k - for _, v := range vv { - w.Header().Add(k, v) - } - } - return nil -} - -func (gcm *GrpcConnectionManager) newHttpForwarder() *HttpForwarder { - transport := &http2.Transport{ - DialTLS: func(network, addr string, _ *tls.Config) (net.Conn, error) { - return net.Dial(network, addr) - }, - AllowHTTP: true, - } - return &HttpForwarder{transport: transport} -} - -func copyHeader(dst, src stdHttp.Header) { - for k, vv := range src { - for _, v := range vv { - dst.Add(k, v) - } - } -} diff --git a/pixiu/pkg/common/http/manager.go b/pixiu/pkg/common/http/manager.go deleted file mode 100644 index 47d02fbad..000000000 --- a/pixiu/pkg/common/http/manager.go +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 http - -import ( - "context" - "encoding/json" - "fmt" - "io" - stdHttp "net/http" - "sync" -) - -import ( - "github.com/pkg/errors" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - router2 "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/router" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/util" - pch "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -// HttpConnectionManager network filter for http -type HttpConnectionManager struct { - filter.EmptyNetworkFilter - config *model.HttpConnectionManagerConfig - routerCoordinator *router2.RouterCoordinator - filterManager *filter.FilterManager - pool sync.Pool -} - -// CreateHttpConnectionManager create http connection manager -func CreateHttpConnectionManager(hcmc *model.HttpConnectionManagerConfig) *HttpConnectionManager { - hcm := &HttpConnectionManager{config: hcmc} - hcm.pool.New = func() interface{} { - return hcm.allocateContext() - } - hcm.routerCoordinator = router2.CreateRouterCoordinator(&hcmc.RouteConfig) - hcm.filterManager = filter.NewFilterManager(hcmc.HTTPFilters) - hcm.filterManager.Load() - return hcm -} - -func (hcm *HttpConnectionManager) allocateContext() *pch.HttpContext { - return &pch.HttpContext{ - Params: make(map[string]interface{}), - } -} - -func (hcm *HttpConnectionManager) Handle(hc *pch.HttpContext) error { - hc.Ctx = context.Background() - err := hcm.findRoute(hc) - if err != nil { - return err - } - hcm.handleHTTPRequest(hc) - return nil -} - -func (hcm *HttpConnectionManager) ServeHTTP(w stdHttp.ResponseWriter, r *stdHttp.Request) { - hc := hcm.pool.Get().(*pch.HttpContext) - defer hcm.pool.Put(hc) - - hc.Writer = w - hc.Request = r - hc.Reset() - hc.Timeout = hcm.config.Timeout - err := hcm.Handle(hc) - if err != nil { - logger.Errorf("ServeHTTP %v", err) - } -} - -// handleHTTPRequest handle http request -func (hcm *HttpConnectionManager) handleHTTPRequest(c *pch.HttpContext) { - filterChain := hcm.filterManager.CreateFilterChain(c) - - // recover any err when filterChain run - defer func() { - if err := recover(); err != nil { - logger.Warnf("[dubbopixiu go] Occur An Unexpected Err: %+v", err) - c.SendLocalReply(stdHttp.StatusInternalServerError, []byte(fmt.Sprintf("Occur An Unexpected Err: %v", err))) - } - }() - - //todo timeout - filterChain.OnDecode(c) - hcm.buildTargetResponse(c) - filterChain.OnEncode(c) - hcm.writeResponse(c) -} - -func (hcm *HttpConnectionManager) writeResponse(c *pch.HttpContext) { - if !c.LocalReply() { - writer := c.Writer - writer.WriteHeader(c.GetStatusCode()) - if _, err := writer.Write(c.TargetResp.Data); err != nil { - logger.Errorf("write response error: %s", err) - } - } -} - -func (hcm *HttpConnectionManager) buildTargetResponse(c *pch.HttpContext) { - if c.LocalReply() { - return - } - - switch res := c.SourceResp.(type) { - case *stdHttp.Response: - body, err := io.ReadAll(res.Body) - if err != nil { - panic(err) - } - //close body - _ = res.Body.Close() - //Merge header - remoteHeader := res.Header - for k := range remoteHeader { - c.AddHeader(k, remoteHeader.Get(k)) - } - //status code - c.StatusCode(res.StatusCode) - c.TargetResp = &client.Response{Data: body} - case []byte: - c.StatusCode(stdHttp.StatusOK) - if json.Valid(res) { - c.AddHeader(constant.HeaderKeyContextType, constant.HeaderValueApplicationJson) - } else { - c.AddHeader(constant.HeaderKeyContextType, constant.HeaderValueTextPlain) - } - c.TargetResp = &client.Response{Data: res} - default: - //dubbo go generic invoke - response := util.NewDubboResponse(res, false) - c.StatusCode(stdHttp.StatusOK) - c.AddHeader(constant.HeaderKeyContextType, constant.HeaderValueJsonUtf8) - c.TargetResp = response - } -} - -func (hcm *HttpConnectionManager) findRoute(hc *pch.HttpContext) error { - ra, err := hcm.routerCoordinator.Route(hc) - if err != nil { - hc.SendLocalReply(stdHttp.StatusNotFound, constant.Default404Body) - - e := errors.Errorf("Requested URL %s not found", hc.GetUrl()) - logger.Debug(e.Error()) - return e - // return 404 - } - hc.RouteEntry(ra) - return nil -} diff --git a/pixiu/pkg/common/router/router.go b/pixiu/pkg/common/router/router.go deleted file mode 100644 index d8cbc37d5..000000000 --- a/pixiu/pkg/common/router/router.go +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 router - -import ( - stdHttp "net/http" - "strings" - "sync" -) - -import ( - "github.com/pkg/errors" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/router/trie" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/util/stringutil" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server" -) - -type ( - // RouterCoordinator the router coordinator for http connection manager - RouterCoordinator struct { - activeConfig *model.RouteConfiguration - rw sync.RWMutex - } -) - -// CreateRouterCoordinator create coordinator for http connection manager -func CreateRouterCoordinator(routeConfig *model.RouteConfiguration) *RouterCoordinator { - rc := &RouterCoordinator{activeConfig: routeConfig} - if routeConfig.Dynamic { - server.GetRouterManager().AddRouterListener(rc) - } - rc.initTrie() - rc.initRegex() - return rc -} - -// Route find routeAction for request -func (rm *RouterCoordinator) Route(hc *http.HttpContext) (*model.RouteAction, error) { - rm.rw.RLock() - defer rm.rw.RUnlock() - - return rm.route(hc.Request) -} - -func (rm *RouterCoordinator) RouteByPathAndName(path, method string) (*model.RouteAction, error) { - rm.rw.RLock() - defer rm.rw.RUnlock() - - return rm.activeConfig.RouteByPathAndMethod(path, method) -} - -func (rm *RouterCoordinator) route(req *stdHttp.Request) (*model.RouteAction, error) { - // match those route that only contains headers first - var matched []*model.Router - for _, route := range rm.activeConfig.Routes { - if len(route.Match.Prefix) > 0 { - continue - } - if route.Match.MatchHeader(req) { - matched = append(matched, route) - } - } - - // always return the first match of header if got any - if len(matched) > 0 { - if len(matched[0].Route.Cluster) == 0 { - return nil, errors.New("action is nil. please check your configuration.") - } - return &matched[0].Route, nil - } - - // match those route that only contains prefix - // TODO: may consider implementing both prefix and header in the future - return rm.activeConfig.Route(req) -} - -func getTrieKey(method string, path string, isPrefix bool) string { - if isPrefix { - if !strings.HasSuffix(path, constant.PathSlash) { - path = path + constant.PathSlash - } - path = path + "**" - } - return stringutil.GetTrieKey(method, path) -} - -func (rm *RouterCoordinator) initTrie() { - if rm.activeConfig.RouteTrie.IsEmpty() { - rm.activeConfig.RouteTrie = trie.NewTrie() - } - for _, router := range rm.activeConfig.Routes { - rm.OnAddRouter(router) - } -} - -func (rm *RouterCoordinator) initRegex() { - for _, router := range rm.activeConfig.Routes { - headers := router.Match.Headers - for i := range headers { - if headers[i].Regex && len(headers[i].Values) > 0 { - // regexp always use first value of header - err := headers[i].SetValueRegex(headers[i].Values[0]) - if err != nil { - logger.Errorf("invalid regexp in headers[%d]: %v", i, err) - panic(err) - } - } - } - } -} - -// OnAddRouter add router -func (rm *RouterCoordinator) OnAddRouter(r *model.Router) { - //TODO: lock move to trie node - rm.rw.Lock() - defer rm.rw.Unlock() - if r.Match.Methods == nil { - r.Match.Methods = []string{constant.Get, constant.Put, constant.Delete, constant.Post, constant.Options} - } - isPrefix := r.Match.Prefix != "" - for _, method := range r.Match.Methods { - var key string - if isPrefix { - key = getTrieKey(method, r.Match.Prefix, isPrefix) - } else { - key = getTrieKey(method, r.Match.Path, isPrefix) - } - _, _ = rm.activeConfig.RouteTrie.Put(key, r.Route) - } -} - -// OnDeleteRouter delete router -func (rm *RouterCoordinator) OnDeleteRouter(r *model.Router) { - rm.rw.Lock() - defer rm.rw.Unlock() - - if r.Match.Methods == nil { - r.Match.Methods = []string{constant.Get, constant.Put, constant.Delete, constant.Post} - } - isPrefix := r.Match.Prefix != "" - for _, method := range r.Match.Methods { - var key string - if isPrefix { - key = getTrieKey(method, r.Match.Prefix, isPrefix) - } else { - key = getTrieKey(method, r.Match.Path, isPrefix) - } - _, _ = rm.activeConfig.RouteTrie.Remove(key) - } -} diff --git a/pixiu/pkg/common/util/response.go b/pixiu/pkg/common/util/response.go deleted file mode 100644 index a5a28c5d3..000000000 --- a/pixiu/pkg/common/util/response.go +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 util - -import ( - "encoding/json" - "fmt" - "reflect" - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client" -) - -func NewDubboResponse(data interface{}, hump bool) *client.Response { - r, _ := dealResp(data, hump) - bytes, _ := json.Marshal(r) - return &client.Response{Data: bytes} -} - -func dealResp(in interface{}, HumpToLine bool) (interface{}, error) { - if in == nil { - return in, nil - } - switch reflect.TypeOf(in).Kind() { - case reflect.Map: - if _, ok := in.(map[interface{}]interface{}); ok { - m := mapIItoMapSI(in) - if HumpToLine { - m = humpToLine(m) - } - return m, nil - } else if inm, ok := in.(map[string]interface{}); ok { - if HumpToLine { - m := humpToLine(in) - return m, nil - } - return inm, nil - } - case reflect.Slice: - if data, ok := in.([]byte); ok { - // raw bytes data should return directly - return data, nil - } - value := reflect.ValueOf(in) - newTemps := make([]interface{}, 0, value.Len()) - for i := 0; i < value.Len(); i++ { - if value.Index(i).CanInterface() { - newTemp, e := dealResp(value.Index(i).Interface(), HumpToLine) - if e != nil { - return nil, e - } - newTemps = append(newTemps, newTemp) - } else { - return nil, fmt.Errorf("unexpect err,value:%v", value) - } - } - return newTemps, nil - default: - return in, nil - } - return in, nil -} - -func mapIItoMapSI(in interface{}) interface{} { - var inMap map[interface{}]interface{} - if v, ok := in.(map[interface{}]interface{}); !ok { - return in - } else { - inMap = v - } - outMap := make(map[string]interface{}, len(inMap)) - - for k, v := range inMap { - if v == nil { - continue - } - s := fmt.Sprint(k) - if s == "class" { - // ignore the "class" field - continue - } - vt := reflect.TypeOf(v) - switch vt.Kind() { - case reflect.Map: - if _, ok := v.(map[interface{}]interface{}); ok { - v = mapIItoMapSI(v) - } - case reflect.Slice: - vl := reflect.ValueOf(v) - os := make([]interface{}, 0, vl.Len()) - for i := 0; i < vl.Len(); i++ { - if vl.Index(i).CanInterface() { - osv := mapIItoMapSI(vl.Index(i).Interface()) - os = append(os, osv) - } - } - v = os - } - outMap[s] = v - - } - return outMap -} - -// traverse all the keys in the map and change the hump to underline -func humpToLine(in interface{}) interface{} { - var m map[string]interface{} - if v, ok := in.(map[string]interface{}); ok { - m = v - } else { - return in - } - - out := make(map[string]interface{}, len(m)) - for k1, v1 := range m { - x := humpToUnderline(k1) - - if v1 == nil { - out[x] = v1 - } else if reflect.TypeOf(v1).Kind() == reflect.Struct { - out[x] = humpToLine(struct2Map(v1)) - } else if reflect.TypeOf(v1).Kind() == reflect.Slice { - value := reflect.ValueOf(v1) - newTemps := make([]interface{}, 0, value.Len()) - for i := 0; i < value.Len(); i++ { - newTemp := humpToLine(value.Index(i).Interface()) - newTemps = append(newTemps, newTemp) - } - out[x] = newTemps - } else if reflect.TypeOf(v1).Kind() == reflect.Map { - out[x] = humpToLine(v1) - } else { - out[x] = v1 - } - } - return out -} - -func humpToUnderline(s string) string { - data := make([]byte, 0, len(s)*2) - j := false - num := len(s) - for i := 0; i < num; i++ { - d := s[i] - if i > 0 && d >= 'A' && d <= 'Z' && j { - data = append(data, '_') - } - if d != '_' { - j = true - } - data = append(data, d) - } - return strings.ToLower(string(data[:])) -} - -func struct2Map(obj interface{}) map[string]interface{} { - t := reflect.TypeOf(obj) - v := reflect.ValueOf(obj) - - data := make(map[string]interface{}) - for i := 0; i < t.NumField(); i++ { - data[t.Field(i).Name] = v.Field(i).Interface() - } - return data -} diff --git a/pixiu/pkg/config/api_config.go b/pixiu/pkg/config/api_config.go deleted file mode 100644 index 946026dc1..000000000 --- a/pixiu/pkg/config/api_config.go +++ /dev/null @@ -1,464 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 config - -import ( - "regexp" - "strconv" - "strings" - "sync" - "time" -) - -import ( - fc "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" - etcdv3 "github.com/dubbogo/gost/database/kv/etcd/v3" - perrors "github.com/pkg/errors" - "go.etcd.io/etcd/api/v3/mvccpb" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/yaml" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -var ( - apiConfig *fc.APIConfig - client *etcdv3.Client - listener APIConfigResourceListener - lock sync.RWMutex -) - -var ( - BASE_INFO_NAME = "name" - BASE_INFO_DESC = "description" -) - -// APIConfigResourceListener defines api resource and method config listener interface -type APIConfigResourceListener interface { - // ResourceChange handle modify resource event - ResourceChange(new fc.Resource, old fc.Resource) bool // bool is return for interface implement is interesting - // ResourceAdd handle add resource event - ResourceAdd(res fc.Resource) bool - // ResourceDelete handle delete resource event - ResourceDelete(deleted fc.Resource) bool - // MethodChange handle modify method event - MethodChange(res fc.Resource, method fc.Method, old fc.Method) bool - // MethodAdd handle add method below one resource event - MethodAdd(res fc.Resource, method fc.Method) bool - // MethodDelete handle delete method event - MethodDelete(res fc.Resource, method fc.Method) bool -} - -// LoadAPIConfigFromFile load the api config from file -func LoadAPIConfigFromFile(path string) (*fc.APIConfig, error) { - if len(path) == 0 { - return nil, perrors.Errorf("Config file not specified") - } - logger.Infof("Load API configuration file form %s", path) - apiConf := &fc.APIConfig{} - err := yaml.UnmarshalYMLConfig(path, apiConf) - if err != nil { - return nil, perrors.Errorf("unmarshalYmlConfig error %s", perrors.WithStack(err)) - } - apiConfig = apiConf - return apiConf, nil -} - -// LoadAPIConfig load the api config from config center -func LoadAPIConfig(metaConfig *model.APIMetaConfig) (*fc.APIConfig, error) { - tmpClient, err := etcdv3.NewConfigClientWithErr( - etcdv3.WithName(etcdv3.RegistryETCDV3Client), - etcdv3.WithTimeout(10*time.Second), - etcdv3.WithEndpoints(strings.Split(metaConfig.Address, ",")...), - ) - if err != nil { - return nil, perrors.Errorf("Init etcd client fail error %s", err) - } - - client = tmpClient - kList, vList, err := client.GetChildren(metaConfig.APIConfigPath) - if err != nil { - return nil, perrors.Errorf("Get remote config fail error %s", err) - } - if err = initAPIConfigFromKVList(kList, vList); err != nil { - return nil, err - } - // TODO: init other setting which need fetch from remote - go listenResourceAndMethodEvent(metaConfig.APIConfigPath) - // TODO: watch other setting which need fetch from remote - return apiConfig, nil -} - -func initAPIConfigFromKVList(kList, vList []string) error { - var skList, svList, mkList, mvList []string - var baseInfo string - - for i, k := range kList { - v := vList[i] - //handle base info - re := getCheckBaseInfoRegexp() - if m := re.Match([]byte(k)); m { - baseInfo = v - continue - } - - // handle resource - re = getCheckResourceRegexp() - if m := re.Match([]byte(k)); m { - skList = append(skList, k) - svList = append(svList, v) - continue - } - // handle method - re = getExtractMethodRegexp() - if m := re.Match([]byte(k)); m { - mkList = append(mkList, k) - mvList = append(mvList, v) - continue - } - } - - lock.Lock() - defer lock.Unlock() - - tmpApiConf := &fc.APIConfig{} - if err := initBaseInfoFromString(tmpApiConf, baseInfo); err != nil { - logger.Errorf("initBaseInfoFromString error %s", err) - return err - } - if err := initAPIConfigServiceFromKvList(tmpApiConf, skList, svList); err != nil { - logger.Errorf("initAPIConfigServiceFromKvList error %s", err) - return err - } - if err := initAPIConfigMethodFromKvList(tmpApiConf, mkList, mvList); err != nil { - logger.Errorf("initAPIConfigMethodFromKvList error %s", err) - return err - } - - apiConfig = tmpApiConf - return nil -} - -func initBaseInfoFromString(conf *fc.APIConfig, str string) error { - properties := make(map[string]string, 8) - if err := yaml.UnmarshalYML([]byte(str), properties); err != nil { - logger.Errorf("unmarshalYmlConfig error %s", err) - return err - } - if v, ok := properties[BASE_INFO_NAME]; ok { - conf.Name = v - } - if v, ok := properties[BASE_INFO_DESC]; ok { - conf.Description = v - } - return nil -} - -func initAPIConfigMethodFromKvList(config *fc.APIConfig, kList, vList []string) error { - for i := range kList { - v := vList[i] - method := &fc.Method{} - err := yaml.UnmarshalYML([]byte(v), method) - if err != nil { - logger.Errorf("unmarshalYmlConfig error %s", err) - return err - } - - found := false - for r, resource := range config.Resources { - if method.ResourcePath != resource.Path { - continue - } - - for j, old := range resource.Methods { - if old.HTTPVerb == method.HTTPVerb { - // modify one method - resource.Methods[j] = *method - found = true - } - } - if !found { - resource.Methods = append(resource.Methods, *method) - config.Resources[r] = resource - found = true - } - } - - // not found one resource, so need add empty resource first - if !found { - resource := &fc.Resource{} - resource.Methods = append(resource.Methods, *method) - resource.Path = method.ResourcePath - config.Resources = append(config.Resources, *resource) - } - } - return nil -} - -func initAPIConfigServiceFromKvList(config *fc.APIConfig, kList, vList []string) error { - for i := range kList { - v := vList[i] - resource := &fc.Resource{} - err := yaml.UnmarshalYML([]byte(v), resource) - if err != nil { - logger.Errorf("unmarshalYmlConfig error %s", err) - return err - } - - found := false - if config.Resources == nil { - config.Resources = make([]fc.Resource, 0) - } - - for i, old := range config.Resources { - if old.Path != resource.Path { - continue - } - // replace old with new one except method list - resource.Methods = old.Methods - config.Resources[i] = *resource - found = true - } - - if !found { - config.Resources = append(config.Resources, *resource) - } - continue - } - return nil -} - -func listenResourceAndMethodEvent(key string) bool { - for { - wc, err := client.WatchWithPrefix(key) - if err != nil { - logger.Warnf("Watch api config {key:%s} = error{%s}", key, err) - return false - } - - select { - - // client stopped - case <-client.Done(): - logger.Warnf("client stopped") - return false - // client ctx stop - // handle etcd events - case e, ok := <-wc: - if !ok { - logger.Warnf("watch-chan closed") - return false - } - - if e.Err() != nil { - logger.Errorf("watch ERR {err: %s}", e.Err()) - continue - } - for _, event := range e.Events { - switch event.Type { - case mvccpb.PUT: - logger.Infof("get event (key{%s}) = event{EventNodePut}", event.Kv.Key) - handlePutEvent(event.Kv.Key, event.Kv.Value) - case mvccpb.DELETE: - logger.Infof("get event (key{%s}) = event{EventNodeDeleted}", event.Kv.Key) - handleDeleteEvent(event.Kv.Key, event.Kv.Value) - default: - logger.Infof("get event (key{%s}) = event{%d}", event.Kv.Key, event.Type) - } - } - } - } -} - -func handleDeleteEvent(key, val []byte) { - lock.Lock() - defer lock.Unlock() - - keyStr := string(key) - keyStr = strings.TrimSuffix(keyStr, "/") - - re := getCheckResourceRegexp() - if m := re.Match(key); m { - pathArray := strings.Split(keyStr, "/") - if len(pathArray) == 0 { - logger.Errorf("handleDeleteEvent key format error") - return - } - resourceIdStr := pathArray[len(pathArray)-1] - ID, err := strconv.Atoi(resourceIdStr) - if err != nil { - logger.Errorf("handleDeleteEvent ID is not int error %s", err) - return - } - deleteApiConfigResource(ID) - return - } - - re = getExtractMethodRegexp() - if m := re.Match(key); m { - pathArray := strings.Split(keyStr, "/") - if len(pathArray) < 3 { - logger.Errorf("handleDeleteEvent key format error") - return - } - resourceIdStr := pathArray[len(pathArray)-3] - resourceId, err := strconv.Atoi(resourceIdStr) - if err != nil { - logger.Errorf("handleDeleteEvent ID is not int error %s", err) - return - } - - methodIdStr := pathArray[len(pathArray)-1] - methodId, err := strconv.Atoi(methodIdStr) - if err != nil { - logger.Errorf("handleDeleteEvent ID is not int error %s", err) - return - } - deleteApiConfigMethod(resourceId, methodId) - } -} - -func handlePutEvent(key, val []byte) { - lock.Lock() - defer lock.Unlock() - - re := getCheckResourceRegexp() - if m := re.Match(key); m { - res := &fc.Resource{} - err := yaml.UnmarshalYML(val, res) - if err != nil { - logger.Errorf("handlePutEvent UnmarshalYML error %s", err) - return - } - mergeApiConfigResource(*res) - return - } - - re = getExtractMethodRegexp() - if m := re.Match(key); m { - res := &fc.Method{} - err := yaml.UnmarshalYML(val, res) - if err != nil { - logger.Errorf("handlePutEvent UnmarshalYML error %s", err) - return - } - mergeApiConfigMethod(res.ResourcePath, *res) - return - } - - //handle base info - re = getCheckBaseInfoRegexp() - if m := re.Match(key); m { - mergeBaseInfo(val) - return - } -} - -func deleteApiConfigResource(resourceId int) { - for i := 0; i < len(apiConfig.Resources); i++ { - itr := apiConfig.Resources[i] - if itr.ID == resourceId { - apiConfig.Resources = append(apiConfig.Resources[:i], apiConfig.Resources[i+1:]...) - listener.ResourceDelete(itr) - return - } - } -} - -func mergeApiConfigResource(val fc.Resource) { - for i, resource := range apiConfig.Resources { - if val.ID != resource.ID { - continue - } - // modify one resource - val.Methods = resource.Methods - apiConfig.Resources[i] = val - listener.ResourceChange(val, resource) - return - } - // add one resource - apiConfig.Resources = append(apiConfig.Resources, val) - listener.ResourceAdd(val) -} - -func mergeBaseInfo(val []byte) { - _ = initBaseInfoFromString(apiConfig, string(val)) -} - -func deleteApiConfigMethod(resourceId, methodId int) { - for _, resource := range apiConfig.Resources { - if resource.ID != resourceId { - continue - } - - for i := 0; i < len(resource.Methods); i++ { - method := resource.Methods[i] - - if method.ID == methodId { - resource.Methods = append(resource.Methods[:i], resource.Methods[i+1:]...) - listener.MethodDelete(resource, method) - return - } - } - } -} - -func mergeApiConfigMethod(path string, val fc.Method) { - for i, resource := range apiConfig.Resources { - if path != resource.Path { - continue - } - - for j, method := range resource.Methods { - if method.ID == val.ID { - // modify one method - resource.Methods[j] = val - listener.MethodChange(resource, val, method) - apiConfig.Resources[i] = resource - return - } - } - // add one method - resource.Methods = append(resource.Methods, val) - apiConfig.Resources[i] = resource - listener.MethodAdd(resource, val) - } -} - -func getCheckBaseInfoRegexp() *regexp.Regexp { - return regexp.MustCompile(".+/base$") -} - -func getCheckResourceRegexp() *regexp.Regexp { - return regexp.MustCompile(".+/resources/[^/]+/?$") -} - -func getExtractMethodRegexp() *regexp.Regexp { - return regexp.MustCompile(".+/resources/([^/]+)/method/[^/]+/?$") -} - -func getCheckRatelimitRegexp() *regexp.Regexp { - return regexp.MustCompile(".+/filter/ratelimit") -} - -// RegisterConfigListener register APIConfigListener -func RegisterConfigListener(li APIConfigResourceListener) { - listener = li -} diff --git a/pixiu/pkg/config/xds/apiclient/grpc.go b/pixiu/pkg/config/xds/apiclient/grpc.go deleted file mode 100644 index f7e169830..000000000 --- a/pixiu/pkg/config/xds/apiclient/grpc.go +++ /dev/null @@ -1,383 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 apiclient - -import ( - "context" - stderr "errors" - "sync" - "time" -) - -import ( - envoyconfigcorev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - discoverypb "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - extensionpb "github.com/envoyproxy/go-control-plane/envoy/service/extension/v3" - "github.com/envoyproxy/go-control-plane/pkg/resource/v3" - "github.com/pkg/errors" - "google.golang.org/grpc" - "google.golang.org/grpc/connectivity" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/protobuf/types/known/anypb" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server/controls" -) - -// agent name to talk with xDS server -const xdsAgentName = "dubbo-go-pixiu" - -var ( - grpcMg *GRPCClusterManager - ErrClusterNotFound = stderr.New("can not find cluster") -) - -type ( - GrpcExtensionApiClient struct { - config model.ApiConfigSource - grpcMg *GRPCClusterManager - node *model.Node - xDSExtensionClient extensionpb.ExtensionConfigDiscoveryServiceClient - resourceNames []ResourceTypeName - typeUrl string - exitCh chan struct{} - } - xdsState struct { - nonce string - deltaVersion map[string]string - versionInfo string - } -) - -func Init(clusterMg controls.ClusterManager) { - grpcMg = &GRPCClusterManager{ - clusters: &sync.Map{}, - clusterMg: clusterMg, - } -} - -func Stop() { - if err := grpcMg.Close(); err != nil { //todo - logger.Errorf("grpcClusterManager close failed. %v", err) - } -} - -// CreateGrpExtensionApiClient create Grpc type ApiClient -func CreateGrpExtensionApiClient(config *model.ApiConfigSource, node *model.Node, - exitCh chan struct{}, - typeName ResourceTypeName) *GrpcExtensionApiClient { - v := &GrpcExtensionApiClient{ - config: *config, - node: node, - typeUrl: typeName, - grpcMg: grpcMg, - exitCh: exitCh, - } - v.init() - return v -} - -// Fetch get config data from discovery service and return Any type. -func (g *GrpcExtensionApiClient) Fetch(localVersion string) ([]*ProtoAny, error) { - clsRsp, err := g.xDSExtensionClient.FetchExtensionConfigs(context.Background(), &discoverypb.DiscoveryRequest{ - VersionInfo: localVersion, - Node: g.makeNode(), - ResourceNames: g.resourceNames, - TypeUrl: resource.ExtensionConfigType, //"type.googleapis.com/pixiu.config.listener.v3.Listener", //resource.ListenerType, - }) - if err != nil { - return nil, errors.Wrapf(err, "fetch dynamic resource from remote error. %s", g.resourceNames) - } - logger.Infof("init the from xds server typeUrl=%s version=%s", clsRsp.TypeUrl, clsRsp.VersionInfo) - extensions := make([]*ProtoAny, 0, len(clsRsp.Resources)) - for _, clsResource := range clsRsp.Resources { - elems, err := g.decodeSource(clsResource) - if err != nil { - return nil, err - } - extensions = append(extensions, elems) - } - return extensions, nil -} - -func (g *GrpcExtensionApiClient) decodeSource(resource *anypb.Any) (*ProtoAny, error) { - extension := envoyconfigcorev3.TypedExtensionConfig{} - err := resource.UnmarshalTo(&extension) - if err != nil { - return nil, errors.Wrapf(err, "typed extension as expected.(%s)", g.resourceNames) - } - elems := &ProtoAny{typeConfig: &extension} - return elems, nil -} - -func (g *GrpcExtensionApiClient) makeNode() *envoyconfigcorev3.Node { - return &envoyconfigcorev3.Node{ - Id: g.node.Id, - Cluster: g.node.Cluster, - UserAgentName: xdsAgentName, - } -} - -func (g *GrpcExtensionApiClient) Delta() (chan *DeltaResources, error) { - outputCh := make(chan *DeltaResources) - return outputCh, g.runDelta(outputCh) -} - -func (g *GrpcExtensionApiClient) runDelta(output chan<- *DeltaResources) error { - var delta extensionpb.ExtensionConfigDiscoveryService_DeltaExtensionConfigsClient - var cancel context.CancelFunc - var xState xdsState - backoff := func() { - for { - //back off - var err error - var ctx context.Context // context to sync exitCh - ctx, cancel = context.WithCancel(context.TODO()) - delta, err = g.sendInitDeltaRequest(ctx, &xState) - if err != nil { - logger.Error("can not receive delta discovery request, will back off 1 sec later", err) - select { - case <-time.After(1 * time.Second): - case <-g.exitCh: - logger.Infof("get close single.") - return - } - - continue //backoff - } - return //success - } - } - - backoff() - if delta == nil { // delta instance not created because exitCh - return nil - } - go func() { - //waiting exitCh close - for range g.exitCh { - } - cancel() - }() - //get message - go func() { - for { // delta response backoff. - for { //loop consume recv data form xds server(sendInitDeltaRequest) - resp, err := delta.Recv() - if err != nil { //todo backoff retry - logger.Error("can not receive delta discovery request", err) - break - } - g.handleDeltaResponse(resp, &xState, output) - - err = g.subscribeOnGoingChang(delta, &xState) - if err != nil { - logger.Error("can not recv delta discovery request", err) - break - } - } - backoff() - } - }() - - return nil -} - -func (g *GrpcExtensionApiClient) handleDeltaResponse(resp *discoverypb.DeltaDiscoveryResponse, xState *xdsState, output chan<- *DeltaResources) { - // save the xds state - xState.deltaVersion = make(map[string]string, 1) - xState.nonce = resp.Nonce - - resources := &DeltaResources{ - NewResources: make([]*ProtoAny, 0, 1), - RemovedResource: make([]string, 0, 1), - } - logger.Infof("get xDS message nonce, %s", resp.Nonce) - for _, res := range resp.RemovedResources { - logger.Infof("remove resource found ", res) - resources.RemovedResource = append(resources.RemovedResource, res) - } - - for _, res := range resp.Resources { - logger.Infof("new resource found %s version=%s", res.Name, res.Version) - xState.deltaVersion[res.Name] = res.Version - elems, err := g.decodeSource(res.Resource) - if err != nil { - logger.Infof("can not decode source %s version=%s", res.Name, res.Version, err) - } - resources.NewResources = append(resources.NewResources, elems) - } - //notify the resource change handler - output <- resources -} - -func (g *GrpcExtensionApiClient) subscribeOnGoingChang(delta extensionpb.ExtensionConfigDiscoveryService_DeltaExtensionConfigsClient, xState *xdsState) error { - err := delta.Send(&discoverypb.DeltaDiscoveryRequest{ - Node: g.makeNode(), - TypeUrl: resource.ExtensionConfigType, - InitialResourceVersions: xState.deltaVersion, - ResponseNonce: xState.nonce, - }) - return err -} - -func (g *GrpcExtensionApiClient) sendInitDeltaRequest(ctx context.Context, xState *xdsState) (extensionpb.ExtensionConfigDiscoveryService_DeltaExtensionConfigsClient, error) { - delta, err := g.xDSExtensionClient.DeltaExtensionConfigs(ctx) - if err != nil { - return nil, errors.Wrapf(err, "can not start delta stream with xds server ") - } - err = delta.Send(&discoverypb.DeltaDiscoveryRequest{ - Node: g.makeNode(), - TypeUrl: resource.ExtensionConfigType, - ResourceNamesSubscribe: []string{g.typeUrl}, - ResourceNamesUnsubscribe: nil, - InitialResourceVersions: xState.deltaVersion, - ResponseNonce: xState.nonce, - ErrorDetail: nil, - }) - if err != nil { - return nil, errors.Wrapf(err, "can not send delta discovery request") - } - return delta, nil -} - -func (g *GrpcExtensionApiClient) init() { - if len(g.config.ClusterName) == 0 { - panic("should config one cluster at least") - } - //todo implement multiple grpc api services - if len(g.config.ClusterName) > 1 { - logger.Warnf("defined multiple cluster for xDS api services but only one support.") - } - cluster, err := g.grpcMg.GetGrpcCluster(g.config.ClusterName[0]) - - if err != nil { - logger.Errorf("get cluster for init error. error=%v", err) - panic(err) - } - conn, err := cluster.GetConnection() - if err != nil { - panic(err) - } - g.xDSExtensionClient = extensionpb.NewExtensionConfigDiscoveryServiceClient(conn) -} - -type GRPCClusterManager struct { - clusters *sync.Map // map[clusterName]*grpcCluster - clusterMg controls.ClusterManager -} - -type GRPCCluster struct { - name string //cluster name - config *model.ClusterConfig - once sync.Once - conn *grpc.ClientConn -} - -// GetGrpcCluster get the cluster or create it first time. -func (g *GRPCClusterManager) GetGrpcCluster(name string) (*GRPCCluster, error) { - if !g.clusterMg.HasCluster(name) { - return nil, errors.Wrapf(ErrClusterNotFound, "name = %s", name) - } - - if load, ok := g.clusters.Load(name); ok { - grpcCluster := load.(*GRPCCluster) // grpcClusterManager only - return grpcCluster, nil - } - - store, err := g.clusterMg.CloneXdsControlStore() - if err != nil { - return nil, errors.WithMessagef(err, "clone cluster store failed") - } - - var clusterCfg *model.ClusterConfig - for _, cfg := range store.Config() { - if cfg.Name == name { - clusterCfg = cfg - break - } - } - if clusterCfg == nil { - return nil, errors.Wrapf(ErrClusterNotFound, "name of %s", name) - } - newCluster := &GRPCCluster{ - name: name, - config: clusterCfg, - } - g.clusters.Store(name, newCluster) - return newCluster, nil -} - -func (g *GRPCClusterManager) Close() (err error) { - //todo enhance the close process when concurrent - g.clusters.Range(func(_, value interface{}) bool { - if conn := value.(*grpc.ClientConn); conn != nil { - if err = conn.Close(); err != nil { - logger.Errorf("can not close grpc connection.", err) - } - } - return true - }) - return nil -} - -func (g *GRPCCluster) GetConnection() (conn *grpc.ClientConn, err error) { - g.once.Do(func() { - creds := insecure.NewCredentials() - //if *xdsCreds { // todo - // log.Println("Using xDS credentials...") - // var err error - // if creds, err = xdscreds.NewClientCredentials(xdscreds.ClientOptions{FallbackCreds: insecure.NewCredentials()}); err != nil { - // log.Fatalf("failed to create client-side xDS credentials: %v", err) - // } - //} - if len(g.config.Endpoints) == 0 { - err = errors.Errorf("expect endpoint.") - return - } - endpoint := g.config.Endpoints[0].Address.GetAddress() - logger.Infof("to connect xds server %s ...", endpoint) - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) //todo fix timeout cancel warning - defer cancel() - conn, err = grpc.DialContext(ctx, endpoint, - grpc.WithTransportCredentials(creds), - grpc.WithBlock(), - ) - if err != nil { - err = errors.Errorf("grpc.Dial(%s) failed: %v", endpoint, err) - return - } - logger.Infof("connected xds server (%s)", endpoint) - g.conn = conn - }) - return g.conn, nil -} - -func (g *GRPCCluster) IsAlive() bool { - return g.conn.GetState() == connectivity.Ready -} - -func (g *GRPCCluster) Close() error { - if err := g.conn.Close(); err != nil { - return errors.Wrapf(err, "can not close. %v", g.config) - } - return nil -} diff --git a/pixiu/pkg/config/xds/apiclient/grpc_test.go b/pixiu/pkg/config/xds/apiclient/grpc_test.go deleted file mode 100644 index 665cccb73..000000000 --- a/pixiu/pkg/config/xds/apiclient/grpc_test.go +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 apiclient - -import ( - "context" - "sync" - "testing" -) - -import ( - "github.com/cch123/supermonkey" - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/connectivity" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server/controls" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server/controls/mocks" -) - -func TestGRPCClusterManager_GetGrpcCluster(t *testing.T) { - cluster := &model.ClusterConfig{ - Name: "cluster-1", - TypeStr: "GRPC", - Endpoints: []*model.Endpoint{ - { - Address: model.SocketAddress{ - Address: "localhost", - Port: 18000, - }, - }, - }, - } - type fields struct { - clusters *sync.Map - clusterMg controls.ClusterManager - } - type args struct { - name string - } - ctrl := gomock.NewController(t) - clusterMg := mocks.NewMockClusterManager(ctrl) - clusterMg.EXPECT(). - HasCluster("cluster-1").AnyTimes(). - Return(true) - clusterMg.EXPECT().CloneXdsControlStore().DoAndReturn(func() (controls.ClusterStore, error) { - store := mocks.NewMockClusterStore(ctrl) - store.EXPECT().Config().Return([]*model.ClusterConfig{cluster}) - return store, nil - }) - clusterMg.EXPECT().HasCluster("cluster-2").Return(false) - tests := []struct { - name string - fields fields - args args - wantErr error - }{ - {"test-simple", fields{ - clusters: &sync.Map{}, - clusterMg: clusterMg, - }, args{name: "cluster-1"}, nil}, - {"test-not-exist", fields{ - clusters: &sync.Map{}, - clusterMg: clusterMg, - }, args{name: "cluster-2"}, ErrClusterNotFound}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := &GRPCClusterManager{ - clusters: tt.fields.clusters, - clusterMg: tt.fields.clusterMg, - } - got, err := g.GetGrpcCluster(tt.args.name) - assert := require.New(t) - if tt.wantErr != nil { - assert.ErrorIs(err, tt.wantErr) - return - } - assert.NotNil(got) - //run two times. - got, err = g.GetGrpcCluster(tt.args.name) - assert.NoError(err) - assert.NotNil(got) - - }) - } -} - -func TestGRPCCluster_GetConnect(t *testing.T) { - cluster := &model.ClusterConfig{ - Name: "cluster-1", - TypeStr: "GRPC", - Endpoints: []*model.Endpoint{ - { - Address: model.SocketAddress{ - Address: "localhost", - Port: 18000, - }, - }, - }, - } - g := GRPCCluster{ - name: "name", - config: cluster, - once: sync.Once{}, - conn: nil, - } - - gconn := &grpc.ClientConn{} - var state = connectivity.Ready - supermonkey.Patch(grpc.DialContext, func(ctx context.Context, target string, opts ...grpc.DialOption) (conn *grpc.ClientConn, err error) { - return gconn, nil - }) - supermonkey.Patch((*grpc.ClientConn).Close, func(_ *grpc.ClientConn) error { - return nil - }) - supermonkey.Patch((*grpc.ClientConn).GetState, func(_ *grpc.ClientConn) connectivity.State { - return state - }) - - defer supermonkey.UnpatchAll() - assert := require.New(t) - conn, err := g.GetConnection() - assert.NoError(err) - assert.NotNil(conn) - assert.NotNil(g.conn) - assert.True(g.IsAlive()) - err = g.Close() - assert.NoError(err) - state = connectivity.Shutdown - assert.False(g.IsAlive()) -} diff --git a/pixiu/pkg/config/xds/cds.go b/pixiu/pkg/config/xds/cds.go deleted file mode 100644 index 0ebf47881..000000000 --- a/pixiu/pkg/config/xds/cds.go +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 xds - -import ( - "github.com/dubbo-go-pixiu/pixiu-api/pkg/api" - xdspb "github.com/dubbo-go-pixiu/pixiu-api/pkg/xds/model" - "github.com/pkg/errors" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/config/xds/apiclient" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server/controls" -) - -type CdsManager struct { - DiscoverApi - clusterMg controls.ClusterManager -} - -// Fetch overwrite DiscoverApi.Fetch. -func (c *CdsManager) Fetch() error { - r, err := c.DiscoverApi.Fetch("") //todo use local version - if err != nil { - return err - } - clusters := make([]*xdspb.Cluster, 0, len(r)) - for _, one := range r { - extClusters := &xdspb.PixiuExtensionClusters{} - if err := one.To(extClusters); err != nil { - logger.Errorf("unknown resource of %s, expect Listener", one.GetName()) - continue - } - logger.Infof("clusters from xds server %v", extClusters) - clusters = append(clusters, extClusters.Clusters...) - } - - return c.setupCluster(clusters) -} - -func (c *CdsManager) Delta() error { - readCh, err := c.DiscoverApi.Delta() - if err != nil { - return err - } - go c.asyncHandler(readCh) - return nil -} - -func (c *CdsManager) asyncHandler(read chan *apiclient.DeltaResources) { - for one := range read { - clusters := make([]*xdspb.Cluster, 0, len(one.NewResources)) - for _, one := range one.NewResources { - cluster := &xdspb.PixiuExtensionClusters{} - if err := one.To(cluster); err != nil { - logger.Errorf("unknown resource of %s, expect Listener", one.GetName()) - continue - } - logger.Infof("clusters from xds server %v", cluster) - clusters = append(clusters, cluster.Clusters...) - - } - if err := c.setupCluster(clusters); err != nil { - logger.Errorf("can not setup cluster.", err) - } - } -} - -func (c *CdsManager) removeCluster(clusterNames []string) { - c.clusterMg.RemoveCluster(clusterNames) -} - -func (c *CdsManager) setupCluster(clusters []*xdspb.Cluster) error { - - laterApplies := make([]func() error, 0, len(clusters)) - toRemoveHash := make(map[string]struct{}, len(clusters)) - - store, err := c.clusterMg.CloneXdsControlStore() - if err != nil { - return errors.WithMessagef(err, "can not clone cluster store when update cluster") - } - //todo this will remove the cluster which defined locally. - for _, cluster := range store.Config() { - toRemoveHash[cluster.Name] = struct{}{} - } - for _, cluster := range clusters { - delete(toRemoveHash, cluster.Name) - - makeCluster := c.makeCluster(cluster) - switch { - case c.clusterMg.HasCluster(cluster.Name): - laterApplies = append(laterApplies, func() error { - c.clusterMg.UpdateCluster(makeCluster) - return nil - }) - default: - laterApplies = append(laterApplies, func() error { - c.clusterMg.AddCluster(makeCluster) - return nil - }) - } - } - - c.removeClusters(toRemoveHash) - for _, fn := range laterApplies { //do update and add new cluster. - if err := fn(); err != nil { - logger.Errorf("can not modify cluster", err) - } - } - return nil -} - -func (c *CdsManager) removeClusters(toRemoveList map[string]struct{}) { - removeClusters := make([]string, 0, len(toRemoveList)) - for clusterName := range toRemoveList { - removeClusters = append(removeClusters, clusterName) - } - if len(toRemoveList) == 0 { - return - } - c.removeCluster(removeClusters) -} - -func (c *CdsManager) makeCluster(cluster *xdspb.Cluster) *model.ClusterConfig { - return &model.ClusterConfig{ - Name: cluster.Name, - TypeStr: cluster.TypeStr, - Type: c.makeClusterType(cluster), - EdsClusterConfig: c.makeEdsClusterConfig(cluster.EdsClusterConfig), - LbStr: c.makeLoadBalancePolicy(cluster.LbStr), - HealthChecks: c.makeHealthChecks(cluster.HealthChecks), - Endpoints: c.makeEndpoints(cluster.Endpoints), - } -} - -func (c *CdsManager) makeLoadBalancePolicy(lb string) model.LbPolicyType { - return model.LbPolicyTypeValue[lb] -} - -func (c *CdsManager) makeClusterType(cluster *xdspb.Cluster) model.DiscoveryType { - return model.DiscoveryTypeValue[cluster.TypeStr] -} - -func (c *CdsManager) makeEndpoints(endpoints []*xdspb.Endpoint) []*model.Endpoint { - r := make([]*model.Endpoint, len(endpoints)) - for i, endpoint := range endpoints { - r[i] = &model.Endpoint{ - ID: endpoint.Id, - Name: endpoint.Name, - Address: c.makeAddress(endpoint), - Metadata: endpoint.Metadata, - } - } - return r -} - -func (c *CdsManager) makeAddress(endpoint *xdspb.Endpoint) model.SocketAddress { - if endpoint == nil || endpoint.Address == nil { - return model.SocketAddress{} - } - return model.SocketAddress{ - Address: endpoint.Address.Address, - Port: int(endpoint.Address.Port), - ResolverName: endpoint.Address.ResolverName, - Domains: endpoint.Address.Domains, - CertsDir: endpoint.Address.CertsDir, - } -} - -func (c *CdsManager) makeHealthChecks(checks []*xdspb.HealthCheck) (result []model.HealthCheckConfig) { - //todo implement me after fix model.HealthCheck type define - //result = make([]model.HealthCheck, 0, len(checks)) - //for _, check := range checks { - // switch one := check.GetChecker().(type) { - // case *xdspb.HealthCheck_HttpChecker: - // result = append(result, model.HttpHealthCheck{ - // Host: one.HttpChecker.Host, - // Path: one.HttpChecker.Path, - // UseHttp2: one.HttpChecker.UseHttp2, - // ExpectedStatuses: one.HttpChecker.ExpectedStatuses, - // }) - // case *xdspb.HealthCheck_GrpcChecker: - // result = append(result, model.GrpcHealthCheck{ - // ServiceName: one.GrpcChecker.ServiceName, - // Authority: one.GrpcChecker.Authority, - // }) - // case *xdspb.HealthCheck_CustomChecker: - // result = append(result, model.CustomHealthCheck{ - // Name: one.CustomChecker.Name, - // Config: func() interface{} { - // if one.CustomChecker.Config == nil { - // return nil - // } - // return one.CustomChecker.Config.AsMap() - // }(), - // }) - // } - //} - return -} - -func (c *CdsManager) makeEdsClusterConfig(edsConfig *xdspb.EdsClusterConfig) model.EdsClusterConfig { - if edsConfig == nil { - return model.EdsClusterConfig{} - } - return model.EdsClusterConfig{ - EdsConfig: model.ConfigSource{ - Path: edsConfig.EdsConfig.Path, - ApiConfigSource: c.makeApiConfigSource(edsConfig.EdsConfig.ApiConfigSource), - }, - ServiceName: edsConfig.ServiceName, - } -} - -func (c *CdsManager) makeApiConfigSource(apiConfig *xdspb.ApiConfigSource) (result model.ApiConfigSource) { - apiType, ok := model.ApiTypeValue[apiConfig.APITypeStr] - if !ok { - logger.Errorf("unknown apiType %s", apiConfig.APITypeStr) - return - } - - return model.ApiConfigSource{ - APIType: api.ApiType(apiType), - APITypeStr: apiConfig.APITypeStr, - ClusterName: apiConfig.ClusterName, - RefreshDelay: apiConfig.RefreshDelay, - RequestTimeout: apiConfig.RequestTimeout, - GrpcServices: nil, //todo create node of pb - } -} diff --git a/pixiu/pkg/config/xds/cds_test.go b/pixiu/pkg/config/xds/cds_test.go deleted file mode 100644 index 0937c8ebd..000000000 --- a/pixiu/pkg/config/xds/cds_test.go +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 xds - -import ( - stderr "errors" - "testing" -) - -import ( - "github.com/cch123/supermonkey" - "github.com/dubbo-go-pixiu/pixiu-api/pkg/xds" - pixiupb "github.com/dubbo-go-pixiu/pixiu-api/pkg/xds/model" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/types/known/anypb" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/config/xds/apiclient" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server/controls" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server/controls/mocks" -) - -func makeClusters() *pixiupb.PixiuExtensionClusters { - return &pixiupb.PixiuExtensionClusters{ - Clusters: []*pixiupb.Cluster{ - { - Name: "http-baidu", - TypeStr: "http", - Endpoints: []*pixiupb.Endpoint{{ - Id: "backend", - Address: &pixiupb.SocketAddress{ - Address: "httpbin.org", - Port: 80, - }, - }}, - }, - }, - } -} - -func getCdsConfig() *core.TypedExtensionConfig { - makeClusters := func() *pixiupb.PixiuExtensionClusters { - return &pixiupb.PixiuExtensionClusters{ - Clusters: []*pixiupb.Cluster{ - { - Name: "http-baidu", - TypeStr: "http", - Endpoints: []*pixiupb.Endpoint{{ - Id: "backend", - Address: &pixiupb.SocketAddress{ - Address: "httpbin.org", - Port: 80, - }, - }}, - }, - }, - } - } - cdsResource, _ := anypb.New(makeClusters()) - return &core.TypedExtensionConfig{ - Name: xds.ClusterType, - TypedConfig: cdsResource, - } -} - -func TestCdsManager_Fetch(t *testing.T) { - var fetchResult []*apiclient.ProtoAny - var fetchError error - var cluster = map[string]struct{}{} - var updateCluster *model.ClusterConfig - var addCluster *model.ClusterConfig - xdsConfig := getCdsConfig() - - ctrl := gomock.NewController(t) - clusterMg := mocks.NewMockClusterManager(ctrl) - //var deltaResult chan *apiclient.DeltaResources - //var deltaErr error - supermonkey.Patch((*apiclient.GrpcExtensionApiClient).Fetch, func(_ *apiclient.GrpcExtensionApiClient, localVersion string) ([]*apiclient.ProtoAny, error) { - return fetchResult, fetchError - }) - //supermonkey.Patch(server.GetClusterManager, func() *server.ClusterManager { - // return nil - //}) - clusterMg.EXPECT().HasCluster(gomock.Any()).DoAndReturn(func(clusterName string) bool { - _, ok := cluster[clusterName] - return ok - }) - - clusterMg.EXPECT().UpdateCluster(gomock.Any()).AnyTimes().Do(func(new *model.ClusterConfig) { - updateCluster = new - }) - clusterMg.EXPECT().AddCluster(gomock.Any()).AnyTimes().Do(func(c *model.ClusterConfig) { - addCluster = c - }) - clusterMg.EXPECT().RemoveCluster(gomock.Any()).AnyTimes() - clusterMg.EXPECT().CloneXdsControlStore().AnyTimes().DoAndReturn(func() (controls.ClusterStore, error) { - store := mocks.NewMockClusterStore(ctrl) - store.EXPECT().Config().AnyTimes() - return store, nil - }) - //supermonkey.Patch((*server.ClusterManager).HasCluster, func(_ *server.ClusterManager, clusterName string) bool { - // _, ok := cluster[clusterName] - // return ok - //}) - //supermonkey.Patch((*server.ClusterManager).UpdateCluster, func(_ *server.ClusterManager, new *model.Cluster) { - // updateCluster = new - //}) - //supermonkey.Patch((*server.ClusterManager).AddCluster, func(_ *server.ClusterManager, c *model.Cluster) { - // addCluster = c - //}) - //supermonkey.Patch((*server.ClusterManager).RemoveCluster, func(_ *server.ClusterManager, names []string) { - // //do nothing. - //}) - //supermonkey.Patch((*server.ClusterManager).CloneStore, func(_ *server.ClusterManager) (*server.ClusterStore, error) { - // return &server.ClusterStore{}, nil - //}) - //supermonkey.Patch((*apiclient.GrpcExtensionApiClient).Delta, func(_ *apiclient.GrpcExtensionApiClient) (chan *apiclient.DeltaResources, error) { - // return deltaResult, deltaErr - //}) - defer supermonkey.UnpatchAll() - - tests := []struct { - name string - mockResult []*apiclient.ProtoAny - mockError error - wantErr bool - wantNewCluster bool - wantUpdateCluster bool - }{ - {"error", nil, stderr.New("error test"), true, false, false}, - {"simple", nil, nil, false, false, false}, - {"withValue", func() []*apiclient.ProtoAny { - return []*apiclient.ProtoAny{ - apiclient.NewProtoAny(xdsConfig), - } - }(), nil, false, true, false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := &CdsManager{ - DiscoverApi: &apiclient.GrpcExtensionApiClient{}, - clusterMg: clusterMg, - } - //reset context value. - fetchError = tt.mockError - fetchResult = tt.mockResult - updateCluster = nil - addCluster = nil - - err := c.Fetch() - assert := require.New(t) - if tt.wantErr { - assert.Error(err) - return - } - assert.NoError(err) - if tt.wantUpdateCluster { - assert.NotNil(updateCluster) - } else { - assert.Nil(updateCluster) - } - if tt.wantNewCluster { - assert.NotNil(addCluster) - } else { - assert.Nil(addCluster) - } - }) - } -} - -func TestCdsManager_makeCluster(t *testing.T) { - c := &CdsManager{} - cluster := makeClusters().Clusters[0] - modelCluster := c.makeCluster(cluster) - assert := require.New(t) - assert.Equal(cluster.Name, modelCluster.Name) - assert.Equal(cluster.TypeStr, modelCluster.TypeStr) - assert.Equal(cluster.Endpoints[0].Name, modelCluster.Endpoints[0].Name) - assert.Equal(cluster.Endpoints[0].Address.Address, modelCluster.Endpoints[0].Address.Address) - assert.Equal(cluster.Endpoints[0].Address.Port, int64(modelCluster.Endpoints[0].Address.Port)) -} diff --git a/pixiu/pkg/config/xds/lds.go b/pixiu/pkg/config/xds/lds.go deleted file mode 100644 index f9b795461..000000000 --- a/pixiu/pkg/config/xds/lds.go +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 xds - -import ( - "encoding/json" - "strconv" -) - -import ( - xdsModel "github.com/dubbo-go-pixiu/pixiu-api/pkg/xds/model" - "gopkg.in/yaml.v2" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/config/xds/apiclient" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server/controls" -) - -type LdsManager struct { - DiscoverApi - listenerMg controls.ListenerManager -} - -// Fetch overwrite DiscoverApi.Fetch. -func (l *LdsManager) Fetch() error { - r, err := l.DiscoverApi.Fetch("") //todo use local version - if err != nil { - return err - } - listeners := make([]*xdsModel.Listener, 0, len(r)) - for _, one := range r { - listener := &xdsModel.PixiuExtensionListeners{} - if err := one.To(listener); err != nil { - logger.Errorf("unknown resource of %s, expect Listener", one.GetName()) - continue - } - logger.Infof("listener xds server %v", listener) - listeners = append(listeners, listener.Listeners...) - } - l.setupListeners(listeners) - return nil -} - -func (l *LdsManager) Delta() error { - readCh, err := l.DiscoverApi.Delta() - if err != nil { - return err - } - go l.asyncHandler(readCh) - return nil -} - -func (l *LdsManager) asyncHandler(read chan *apiclient.DeltaResources) { - for delta := range read { - listeners := make([]*xdsModel.Listener, 0, len(delta.NewResources)) - for _, one := range delta.NewResources { - listener := &xdsModel.PixiuExtensionListeners{} - if err := one.To(listener); err != nil { - logger.Errorf("unknown resource of %s, expect Listener", one.GetName()) - continue - } - logger.Infof("listener xds server %v", listener) - listeners = append(listeners, listener.Listeners...) - } - - l.setupListeners(listeners) - } -} - -func (l *LdsManager) makeSocketAddress(address *xdsModel.SocketAddress) model.SocketAddress { - if address == nil { - return model.SocketAddress{} - } - return model.SocketAddress{ - Address: address.Address, - Port: int(address.Port), - ResolverName: address.ResolverName, - //Domains: _l.Address.do, todo add the domains - //CertsDir: _l.Address.SocketAddress"", //todo add the domains - } -} - -func (l *LdsManager) removeListeners(toRemoveHash map[string]struct{}) { - names := make([]string, 0, len(toRemoveHash)) - for name := range toRemoveHash { - names = append(names, name) - } - l.listenerMg.RemoveListener(names) -} - -// setupListeners setup listeners accord to dynamic resource -func (l *LdsManager) setupListeners(listeners []*xdsModel.Listener) { - //Make sure each one has a unique name like "host-port-protocol" - for _, v := range listeners { - v.Name = resolveListenerName(v.Address.SocketAddress.Address, int(v.Address.SocketAddress.Port), v.Protocol.String()) - } - - laterApplies := make([]func() error, 0, len(listeners)) - toRemoveHash := make(map[string]struct{}, len(listeners)) - - lm := l.listenerMg - activeListeners, err := lm.CloneXdsControlListener() - if err != nil { - logger.Errorf("Clone Xds Control Listener fail: %s", err) - return - } - //put all current listeners to $toRemoveHash - for _, v := range activeListeners { - //Make sure each one has a unique name like "host-port-protocol" - v.Name = resolveListenerName(v.Address.SocketAddress.Address, v.Address.SocketAddress.Port, v.ProtocolStr) - toRemoveHash[v.Name] = struct{}{} - } - - for _, listener := range listeners { - delete(toRemoveHash, listener.Name) - - modelListener := l.makeListener(listener) - // add or update later after removes - switch { - case lm.HasListener(modelListener.Name): - laterApplies = append(laterApplies, func() error { - return lm.UpdateListener(&modelListener) - }) - default: - laterApplies = append(laterApplies, func() error { - return lm.AddListener(&modelListener) - }) - } - } - // remove the listeners first to prevent tcp port conflict - l.removeListeners(toRemoveHash) - //do update and add new cluster. - for _, fn := range laterApplies { - if err := fn(); err != nil { - logger.Errorf("can not modify listener", err) - } - } -} - -func resolveListenerName(host string, port int, protocol string) string { - return host + "-" + strconv.Itoa(port) + "-" + protocol -} - -func (l *LdsManager) makeListener(listener *xdsModel.Listener) model.Listener { - return model.Listener{ - Name: listener.Name, - ProtocolStr: listener.Protocol.String(), - Protocol: model.ProtocolType(model.ProtocolTypeValue[listener.Protocol.String()]), - Address: l.makeAddress(listener.Address), - FilterChain: l.makeFilterChain(listener.FilterChain), - Config: nil, // todo set the additional config - } -} - -func (l *LdsManager) makeFilterChain(fChain *xdsModel.FilterChain) model.FilterChain { - return model.FilterChain{ - Filters: l.makeFilters(fChain.Filters), - } -} - -func (l *LdsManager) makeFilters(filters []*xdsModel.NetworkFilter) []model.NetworkFilter { - result := make([]model.NetworkFilter, 0, len(filters)) - for _, filter := range filters { - result = append(result, model.NetworkFilter{ - Name: filter.Name, - //Config: filter., todo define the config of filter - Config: l.makeConfig(filter), - }) - } - return result -} - -func (l *LdsManager) makeConfig(filter *xdsModel.NetworkFilter) (m map[string]interface{}) { - switch cfg := filter.Config.(type) { - case *xdsModel.NetworkFilter_Yaml: - if err := yaml.Unmarshal([]byte(cfg.Yaml.Content), &m); err != nil { - logger.Errorf("can not make yaml from filter.Config: %s", cfg.Yaml.Content, err) - } - case *xdsModel.NetworkFilter_Json: - if err := json.Unmarshal([]byte(cfg.Json.Content), &m); err != nil { - logger.Errorf("can not make json from filter.Config: %s", cfg.Json.Content, err) - } - case *xdsModel.NetworkFilter_Struct: - m = cfg.Struct.AsMap() - default: - logger.Errorf("can not get filter config of %s", filter.Name) - } - return -} - -func (l *LdsManager) makeAddress(addr *xdsModel.Address) model.Address { - if addr == nil { - return model.Address{} - } - return model.Address{ - SocketAddress: l.makeSocketAddress(addr.SocketAddress), - Name: addr.Name, - } -} diff --git a/pixiu/pkg/config/xds/lds_test.go b/pixiu/pkg/config/xds/lds_test.go deleted file mode 100644 index 734cb4c53..000000000 --- a/pixiu/pkg/config/xds/lds_test.go +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 xds - -import ( - "testing" -) - -import ( - pixiupb "github.com/dubbo-go-pixiu/pixiu-api/pkg/xds/model" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/encoding/protojson" - structpb2 "google.golang.org/protobuf/types/known/structpb" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -func TestLdsManager_makeConfig(t *testing.T) { - var httpManagerConfigYaml = ` -route_config: - routes: - - match: - prefix: "/" - route: - cluster: "http_bin" - cluster_not_found_response_code: "505" -http_filters: - - name: dgp.filter.http.httpproxy - config: - - name: dgp.filter.http.response - config: -` - configMap := map[string]interface{}{ - "route_config": map[string]interface{}{ - "routes": []interface{}{ - map[string]interface{}{ - "match": map[string]interface{}{ - "prefix": "/", - }, - "route": map[string]interface{}{ - "cluster": "http_bin", - "cluster_not_found_response_code": "505", - }, - }, - }, - }, - "http_filters": []interface{}{ - map[string]interface{}{ - "name": "dgp.filter.http.httpproxy", - "config": nil, - }, - map[string]interface{}{ - "name": "dgp.filter.http.response", - "config": nil, - }, - }, - } - httpManagerConfigStruct, _ := structpb2.NewStruct(configMap) - - oneConfigMap := map[string]interface{}{ - "route_config": map[interface{}]interface{}{ - "routes": []interface{}{ - map[interface{}]interface{}{ - "match": map[interface{}]interface{}{ - "prefix": "/", - }, - "route": map[interface{}]interface{}{ - "cluster": "http_bin", - "cluster_not_found_response_code": "505", - }, - }, - }, - }, - "http_filters": []interface{}{ - map[interface{}]interface{}{ - "name": "dgp.filter.http.httpproxy", - "config": nil, - }, - map[interface{}]interface{}{ - "name": "dgp.filter.http.response", - "config": nil, - }, - }, - } - - type args struct { - filter *pixiupb.NetworkFilter - } - tests := []struct { - name string - args args - wantM map[string]interface{} - }{ - { - name: "yaml", - args: args{ - filter: &pixiupb.NetworkFilter{ - Name: "", - Config: &pixiupb.NetworkFilter_Yaml{ - Yaml: &pixiupb.Config{ - Content: httpManagerConfigYaml, - }}, - }, - }, - wantM: oneConfigMap, - }, - { - name: "struct", - args: args{ - filter: &pixiupb.NetworkFilter{ - Name: "", - Config: &pixiupb.NetworkFilter_Struct{Struct: httpManagerConfigStruct}, - }, - }, - wantM: configMap, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - l := &LdsManager{} - r := l.makeConfig(tt.args.filter) - assert := require.New(t) - assert.Equal(tt.wantM, r) - }) - } -} - -func TestMakeListener(t *testing.T) { - lm := &LdsManager{} - json := ` -{ - "name": "net/http", - "address": { - "socketAddress": { - "address": "0.0.0.0", - "port": "8080" - } - }, - "filterChain": { - "filters": [ - { - "name": "dgp.filter.httpconnectionmanager", - "struct": { - "http_filters": [ - { - "config": null, - "name": "dgp.filter.http.httpproxy" - } - ], - "route_config": { - "routes": [ - { - "match": { - "prefix": "/" - }, - "route": { - "cluster": "http_bin", - "cluster_not_found_response_code": 503 - } - } - ] - } - } - } - ] - } - } -` - l := &pixiupb.Listener{} - if err := protojson.Unmarshal([]byte(json), l); err != nil { - t.Fatal(err) - } - listener := lm.makeListener(l) - assert.NotNil(t, listener) - assert.Equal(t, "net/http", listener.Name) - assert.Equal(t, "0.0.0.0", listener.Address.SocketAddress.Address) - assert.Equal(t, 8080, listener.Address.SocketAddress.Port) - assert.Equal(t, 1, len(listener.FilterChain.Filters)) -} - -type mockListenerManager struct { - m map[string]*model.Listener -} - -func (m *mockListenerManager) AddListener(l *model.Listener) error { - m.m[l.Name] = l - return nil -} - -func (m *mockListenerManager) UpdateListener(l *model.Listener) error { - m.m[l.Name] = l - return nil -} - -func (m *mockListenerManager) RemoveListener(names []string) { - for _, name := range names { - delete(m.m, name) - } -} - -func (m *mockListenerManager) HasListener(name string) bool { - _, ok := m.m[name] - return ok -} - -func (m *mockListenerManager) CloneXdsControlListener() ([]*model.Listener, error) { - var res []*model.Listener - for _, v := range m.m { - res = append(res, v) - } - return res, nil -} - -func TestSetupListeners(t *testing.T) { - mock := &mockListenerManager{m: map[string]*model.Listener{}} - lm := &LdsManager{listenerMg: mock} - - listeners := []*pixiupb.Listener{ - { - Protocol: pixiupb.Listener_HTTP, - Address: &pixiupb.Address{ - SocketAddress: &pixiupb.SocketAddress{ - Address: "0.0.0.0", - Port: 8080, - }, - }, - FilterChain: &pixiupb.FilterChain{}, - }, - { - Protocol: pixiupb.Listener_TRIPLE, - Address: &pixiupb.Address{ - SocketAddress: &pixiupb.SocketAddress{ - Address: "0.0.0.0", - Port: 8081, - }, - }, - FilterChain: &pixiupb.FilterChain{}, - }, - } - lm.setupListeners(listeners) - for _, v := range listeners { - assert.Equal(t, v.Protocol.String(), model.ProtocolTypeName[int32(mock.m[v.Name].Protocol)]) - assert.Equal(t, v.Address.SocketAddress.Address, mock.m[v.Name].Address.SocketAddress.Address) - assert.Equal(t, int(v.Address.SocketAddress.Port), mock.m[v.Name].Address.SocketAddress.Port) - } - - newListeners := []*pixiupb.Listener{ - { - Protocol: pixiupb.Listener_HTTP, - Address: &pixiupb.Address{ - SocketAddress: &pixiupb.SocketAddress{ - Address: "0.0.0.0", - Port: 8080, - }, - }, - FilterChain: &pixiupb.FilterChain{}, - }, - } - lm.setupListeners(newListeners) - assert.Equal(t, 1, len(mock.m)) -} diff --git a/pixiu/pkg/config/xds/xds.go b/pixiu/pkg/config/xds/xds.go deleted file mode 100644 index 55f15dfab..000000000 --- a/pixiu/pkg/config/xds/xds.go +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 xds - -import ( - "sync" -) - -import ( - "github.com/dubbo-go-pixiu/pixiu-api/pkg/xds" - "github.com/mitchellh/mapstructure" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/config/xds/apiclient" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server/controls" -) - -type ( - DiscoverApi interface { - Fetch(localVersion string) ([]*apiclient.ProtoAny, error) - Delta() (chan *apiclient.DeltaResources, error) - } - - AdapterConfig struct { - } - - Xds struct { - //ads DiscoverApi //aggregate discover service manager todo to implement - cds *CdsManager //cluster discover service manager - lds *LdsManager //listener discover service manager - exitCh chan struct{} - listenerMg controls.ListenerManager - clusterMg controls.ClusterManager - dynamicResourceMg controls.DynamicResourceManager - } -) - -func (a *Xds) createApiManager(config *model.ApiConfigSource, - node *model.Node, - resourceType apiclient.ResourceTypeName) DiscoverApi { - if config == nil { - return nil - } - - switch config.APIType { - case model.ApiTypeGRPC: - return apiclient.CreateGrpExtensionApiClient(config, node, a.exitCh, resourceType) - case model.ApiTypeIstioGRPC: - dubboServices, err := a.readDubboServiceFromListener() - if err != nil { - logger.Errorf("can not read listener. %v", err) - return nil - } - return apiclient.CreateEnvoyGrpcApiClient(config, node, a.exitCh, resourceType, apiclient.WithIstioService(dubboServices...)) - default: - logger.Errorf("un-support the api type %s", config.APITypeStr) - return nil - } -} - -func (a *Xds) readDubboServiceFromListener() ([]string, error) { - dubboServices := make([]string, 0) - listeners, err := a.listenerMg.CloneXdsControlListener() - if err != nil { - return nil, err - } - - for _, l := range listeners { - for _, filter := range l.FilterChain.Filters { - if filter.Name != constant.HTTPConnectManagerFilter { - continue - } - var cfg *model.HttpConnectionManagerConfig - if filter.Config != nil { - if err := mapstructure.Decode(filter.Config, &cfg); err != nil { - logger.Error("read listener config error when init xds", err) - continue - } - } - for _, httpFilter := range cfg.HTTPFilters { - if httpFilter.Name == constant.HTTPDirectDubboProxyFilter { - for _, route := range cfg.RouteConfig.Routes { - dubboServices = append(dubboServices, route.Route.Cluster) - } - } - } - } - } - return dubboServices, nil -} - -func (a *Xds) Start() { - if a.dynamicResourceMg == nil { // if dm is nil, then config not initialized. - logger.Infof("can not get dynamic resource manager. maybe the config has not initialized") - return - } - apiclient.Init(a.clusterMg) - - // lds fetch just run on init phase. - if a.dynamicResourceMg.GetLds() != nil { - a.lds = &LdsManager{ - DiscoverApi: a.createApiManager(a.dynamicResourceMg.GetLds(), a.dynamicResourceMg.GetNode(), xds.ListenerType), - listenerMg: a.listenerMg, - } - if err := a.lds.Delta(); err != nil { - logger.Errorf("can not fetch lds err is %+v", err) - } - } - // catch the ongoing cds config change. - if a.dynamicResourceMg.GetCds() != nil { - a.cds = &CdsManager{ - DiscoverApi: a.createApiManager(a.dynamicResourceMg.GetCds(), a.dynamicResourceMg.GetNode(), xds.ClusterType), - clusterMg: a.clusterMg, - } - if err := a.cds.Delta(); err != nil { - logger.Errorf("can not fetch lds") - } - } - -} - -func (a *Xds) Stop() { - apiclient.Stop() - close(a.exitCh) -} - -var ( - client Client - once sync.Once -) - -// Client xds client -type Client interface { - Stop() -} - -// StartXdsClient create XdsClient and run. only one xds client create at first(singleton) -func StartXdsClient(listenerMg controls.ListenerManager, clusterMg controls.ClusterManager, drm controls.DynamicResourceManager) Client { - once.Do(func() { - xdsClient := &Xds{ - listenerMg: listenerMg, - clusterMg: clusterMg, - dynamicResourceMg: drm, - exitCh: make(chan struct{}), - } - xdsClient.Start() - client = xdsClient - }) - - return client -} diff --git a/pixiu/pkg/config/xds/xds_test.go b/pixiu/pkg/config/xds/xds_test.go deleted file mode 100644 index fce48b457..000000000 --- a/pixiu/pkg/config/xds/xds_test.go +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 xds - -import ( - "context" - "fmt" - "testing" -) - -import ( - monkey "github.com/cch123/supermonkey" - "github.com/dubbo-go-pixiu/pixiu-api/pkg/xds" - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/connectivity" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/config/xds/apiclient" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server/controls" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server/controls/mocks" -) - -func TestAdapter_createApiManager(t *testing.T) { - cluster := &model.ClusterConfig{ - Name: "cluster-1", - TypeStr: "GRPC", - Endpoints: []*model.Endpoint{ - { - Address: model.SocketAddress{ - Address: "localhost", - Port: 18000, - }, - }, - }, - } - - node := model.Node{ - Cluster: "test-cluster", - Id: "node-test-1", - } - - apiConfig := model.ApiConfigSource{ - APIType: model.ApiTypeGRPC, - APITypeStr: "GRPC", - ClusterName: []string{"cluster-1"}, - } - - var state = connectivity.Ready - gconn := &grpc.ClientConn{} - monkey.Patch(grpc.DialContext, func(ctx context.Context, target string, opts ...grpc.DialOption) (conn *grpc.ClientConn, err error) { - fmt.Println("***** DialContext") - return gconn, nil - }) - monkey.Patch((*grpc.ClientConn).Close, func(_ *grpc.ClientConn) error { - return nil - }) - monkey.Patch((*grpc.ClientConn).GetState, func(_ *grpc.ClientConn) connectivity.State { - return state - }) - //monkey.Patch(server.GetDynamicResourceManager, func() server.DynamicResourceManager { - // return &server.DynamicResourceManagerImpl{} - //}) - //monkey.Patch((*server.DynamicResourceManagerImpl).GetLds, func(_ *server.DynamicResourceManagerImpl) *model.ApiConfigSource { - // return &apiConfig - //}) - //monkey.Patch((*server.DynamicResourceManagerImpl).GetCds, func(_ *server.DynamicResourceManagerImpl) *model.ApiConfigSource { - // return &apiConfig - //}) - //monkey.Patch(server.GetClusterManager, func() *server.ClusterManager { - // return nil - //}) - //monkey.Patch((*server.ClusterManager).CloneStore, func(_ *server.ClusterManager) (*server.ClusterStore, error) { - // return &server.ClusterStore{ - // Config: []*model.Cluster{cluster}, - // Version: 1, - // }, nil - //}) - - monkey.Patch((*apiclient.GrpcExtensionApiClient).Fetch, func(_ *apiclient.GrpcExtensionApiClient, localVersion string) ([]*apiclient.ProtoAny, error) { - return nil, nil - }) - monkey.Patch((*apiclient.GrpcExtensionApiClient).Delta, func(_ *apiclient.GrpcExtensionApiClient) (chan *apiclient.DeltaResources, error) { - ch := make(chan *apiclient.DeltaResources) - close(ch) - return ch, nil - }) - - //init cluster manager - ctrl := gomock.NewController(t) - clusterMg := mocks.NewMockClusterManager(ctrl) - { - clusterMg.EXPECT(). - HasCluster("cluster-1").AnyTimes(). - Return(true) - clusterMg.EXPECT().CloneXdsControlStore().DoAndReturn(func() (controls.ClusterStore, error) { - store := mocks.NewMockClusterStore(ctrl) - store.EXPECT().Config().Return([]*model.ClusterConfig{cluster}) - return store, nil - }) - // delete this stub by #https://github.com/golang/mock/issues/530 - //clusterMg.EXPECT().HasCluster("cluster-2").Return(false) - apiclient.Init(clusterMg) - } - - defer monkey.UnpatchAll() - - ada := Xds{ - clusterMg: clusterMg, - } - ada.Start() - api := ada.createApiManager(&apiConfig, &node, xds.ClusterType) - assert := require.New(t) - assert.NotNil(api) -} diff --git a/pixiu/pkg/context/dubbo/context.go b/pixiu/pkg/context/dubbo/context.go deleted file mode 100644 index fd6c264cf..000000000 --- a/pixiu/pkg/context/dubbo/context.go +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 dubbo - -import ( - "context" -) - -import ( - "dubbo.apache.org/dubbo-go/v3/protocol" - "dubbo.apache.org/dubbo-go/v3/protocol/invocation" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -// RpcContext the rpc invocation context -type RpcContext struct { - RpcInvocation *invocation.RPCInvocation - RpcResult protocol.Result - Route *model.RouteAction - Ctx context.Context -} - -// SetError set error in RpcResult -func (c *RpcContext) SetError(err error) { - if c.RpcResult == nil { - c.RpcResult = &protocol.RPCResult{} - } - c.RpcResult.SetError(err) -} - -// SetResult set successful result in RpcResult -func (c *RpcContext) SetResult(result protocol.Result) { - c.RpcResult = result -} - -// SetInvocation set invocation -func (c *RpcContext) SetInvocation(invocation *invocation.RPCInvocation) { - c.RpcInvocation = invocation -} - -// SetRoute set route -func (c *RpcContext) SetRoute(route *model.RouteAction) { - c.Route = route -} - -func (c *RpcContext) GenerateHash() string { - req := c.RpcInvocation - return req.MethodName() + "." + req.Invoker().GetURL().String() -} diff --git a/pixiu/pkg/context/http/context.go b/pixiu/pkg/context/http/context.go deleted file mode 100644 index 734113dca..000000000 --- a/pixiu/pkg/context/http/context.go +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 http - -import ( - "context" - "encoding/json" - "math" - "net" - "net/http" - "net/url" - "strings" - "time" -) - -import ( - "github.com/dubbo-go-pixiu/pixiu-api/pkg/router" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -const abortIndex int8 = math.MaxInt8 / 2 - -// HttpContext http context -type HttpContext struct { - //Deprecated: waiting to delete - Index int8 - //Deprecated: waiting to delete - Filters FilterChain - Timeout time.Duration - Ctx context.Context - Params map[string]interface{} - - // localReply Means that the request was interrupted, - // which may occur in the Decode or Encode stage - localReply bool - // statusCode code will be return - statusCode int - // localReplyBody: happen error - localReplyBody []byte - // the response context will return. - TargetResp *client.Response - // client call response. - SourceResp interface{} - - HttpConnectionManager model.HttpConnectionManagerConfig - Route *model.RouteAction - Api *router.API - - Request *http.Request - Writer http.ResponseWriter -} - -type ( - // ErrResponse err response. - ErrResponse struct { - Message string `json:"message"` - } - - // FilterFunc filter func, filter - FilterFunc func(c *HttpContext) - - // FilterChain filter chain - FilterChain []FilterFunc -) - -// Deprecated: Next logic for lookup filter -func (hc *HttpContext) Next() { -} - -// Reset reset http context -func (hc *HttpContext) Reset() { - hc.Ctx = nil - hc.Index = -1 - hc.Filters = []FilterFunc{} - hc.Route = nil - hc.Api = nil - - hc.TargetResp = nil - hc.SourceResp = nil - hc.statusCode = 0 - hc.localReply = false - hc.localReplyBody = nil -} - -// RouteEntry set route -func (hc *HttpContext) RouteEntry(r *model.RouteAction) { - hc.Route = r -} - -// GetRouteEntry get route -func (hc *HttpContext) GetRouteEntry() *model.RouteAction { - return hc.Route -} - -// AddHeader add header -func (hc *HttpContext) AddHeader(k, v string) { - hc.Writer.Header().Add(k, v) -} - -// GetHeader get header -func (hc *HttpContext) GetHeader(k string) string { - return hc.Request.Header.Get(k) -} - -// AllHeaders get all headers -func (hc *HttpContext) AllHeaders() http.Header { - return hc.Request.Header -} - -// GetUrl get http request url -func (hc *HttpContext) GetUrl() string { - return hc.Request.URL.Path -} - -func (hc *HttpContext) SetUrl(url string) { - hc.Request.URL.Path = url -} - -// GetMethod get method, POST/GET ... -func (hc *HttpContext) GetMethod() string { - return hc.Request.Method -} - -// GetClientIP get client IP -func (hc *HttpContext) GetClientIP() string { - xForwardedFor := hc.Request.Header.Get("X-Forwarded-For") - ip := strings.TrimSpace(strings.Split(xForwardedFor, ",")[0]) - if len(ip) != 0 { - return ip - } - - ip = strings.TrimSpace(hc.Request.Header.Get("X-Real-Ip")) - if len(ip) != 0 { - return ip - } - - if ip, _, err := net.SplitHostPort(strings.TrimSpace(hc.Request.RemoteAddr)); err == nil && len(ip) != 0 { - return ip - } - - return "" -} - -// GetApplicationName get application name -func (hc *HttpContext) GetApplicationName() string { - if u, err := url.Parse(hc.Request.RequestURI); err == nil { - return strings.Split(u.Path, "/")[0] - } - - return "" -} - -// SendLocalReply Means that the request was interrupted and Response will be sent directly -// Even if it’s currently in to Decode stage -func (hc *HttpContext) SendLocalReply(status int, body []byte) { - hc.localReply = true - hc.statusCode = status - hc.localReplyBody = body - hc.TargetResp = &client.Response{Data: body} - if json.Valid(body) { - hc.AddHeader(constant.HeaderKeyContextType, constant.HeaderValueApplicationJson) - } else { - hc.AddHeader(constant.HeaderKeyContextType, constant.HeaderValueTextPlain) - } - writer := hc.Writer - writer.WriteHeader(status) - _, err := writer.Write(body) - if err != nil { - logger.Errorf("write fail: %v", err) - } -} - -func (hc *HttpContext) GetLocalReplyBody() []byte { - return hc.localReplyBody -} - -func (hc *HttpContext) GetStatusCode() int { - return hc.statusCode -} - -func (hc *HttpContext) StatusCode(code int) { - hc.statusCode = code -} - -func (hc *HttpContext) LocalReply() bool { - return hc.localReply -} - -// API sets the API to http context -func (hc *HttpContext) API(api router.API) { - if hc.Timeout > api.Timeout { - hc.Timeout = api.Timeout - } - hc.Api = &api -} - -// GetAPI get api -func (hc *HttpContext) GetAPI() *router.API { - return hc.Api -} - -// Deprecated: Abort filter chain break , filter after the current filter will not executed. -func (hc *HttpContext) Abort() { - hc.Index = abortIndex -} - -// Deprecated: AppendFilterFunc append filter func. -func (hc *HttpContext) AppendFilterFunc(ff ...FilterFunc) { - for _, v := range ff { - hc.Filters = append(hc.Filters, v) - } -} - -func (hc *HttpContext) GenerateHash() string { - req := hc.Request - return req.Method + "." + req.RequestURI -} diff --git a/pixiu/pkg/context/mock/context.go b/pixiu/pkg/context/mock/context.go deleted file mode 100644 index 52b657eea..000000000 --- a/pixiu/pkg/context/mock/context.go +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 mock - -import ( - "context" - "fmt" - "net/http" -) - -import ( - contexthttp "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" -) - -// GetMockHTTPContext mock context for test. -func GetMockHTTPContext(r *http.Request) *contexthttp.HttpContext { - result := &contexthttp.HttpContext{ - Index: -1, - Request: r, - } - - w := mockWriter{header: map[string][]string{}} - result.Writer = &w - result.Reset() - result.Ctx = context.Background() - return result -} - -type mockWriter struct { - header http.Header -} - -func (w *mockWriter) Header() http.Header { - return w.header -} - -func (w *mockWriter) Write(b []byte) (int, error) { - fmt.Println(string(b)) - return -1, nil -} - -func (w *mockWriter) WriteHeader(statusCode int) { - fmt.Println(statusCode) -} diff --git a/pixiu/pkg/filter/accesslog/log.go b/pixiu/pkg/filter/accesslog/log.go deleted file mode 100644 index 2c312863e..000000000 --- a/pixiu/pkg/filter/accesslog/log.go +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 accesslog - -import ( - "os" - "path/filepath" - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" -) - -// access log config, enable default value true, outputpath default value console -// AccessLogConfig access log will out put into console -type AccessLogConfig struct { - OutPutPath string `yaml:"outPutPath" json:"outPutPath" mapstructure:"outPutPath" default:"console"` -} - -// AccessLogWriter access log chan -type AccessLogWriter struct { - AccessLogDataChan chan AccessLogData -} - -// AccessLogData access log data -type AccessLogData struct { - AccessLogMsg string - AccessLogConfig AccessLogConfig -} - -// Writer writer msg into chan -func (alw *AccessLogWriter) Writer(accessLogData AccessLogData) { - select { - case alw.AccessLogDataChan <- accessLogData: - return - default: - logger.Warn("the channel is full and the access logIntoChannel data will be dropped") - return - } -} - -// Write write log into out put path -func (alw *AccessLogWriter) Write() { - go func() { - for accessLogData := range alw.AccessLogDataChan { - alw.writeLogToFile(accessLogData) - } - }() -} - -// write log to file or console -func (alw *AccessLogWriter) writeLogToFile(ald AccessLogData) { - alc := ald.AccessLogConfig - alm := ald.AccessLogMsg - if len(alc.OutPutPath) == 0 || alc.OutPutPath == constant.Console { - logger.Info(alm) - return - } - _ = WriteToFile(alm, alc.OutPutPath) -} - -// WriteToFile write message to access log file -func WriteToFile(accessLogMsg string, filePath string) error { - pd := filepath.Dir(filePath) - if _, err := os.Stat(pd); err != nil { - if os.IsExist(err) { - logger.Warnf("can not open log dir: %s, %v", filePath, err) - } - err = os.MkdirAll(pd, os.ModePerm) - if err != nil { - logger.Warnf("can not create log dir: %s, %v", filePath, err) - return err - } - } - logFile, err := os.OpenFile(filePath, os.O_CREATE|os.O_APPEND|os.O_RDWR, constant.LogFileMode) - if err != nil { - logger.Warnf("can not open the access log file: %s, %v", filePath, err) - return err - } - now := time.Now().Format(constant.FileDateFormat) - fileInfo, err := logFile.Stat() - if err != nil { - logger.Warnf("can not get the info of access log file: %s, %v", filePath, err) - return err - } - last := fileInfo.ModTime().Format(constant.FileDateFormat) - - // this is confused. - // for example, if the last = '2020-03-04' - // and today is '2020-03-05' - // we will create one new file to log access data - // By this way, we can split the access log based on days. - if now != last { - err = os.Rename(fileInfo.Name(), fileInfo.Name()+"."+now) - if err != nil { - logger.Warnf("can not rename access log file: %s, %v", fileInfo.Name(), err) - return err - } - logFile, err = os.OpenFile(fileInfo.Name(), os.O_CREATE|os.O_APPEND|os.O_RDWR, constant.LogFileMode) - if err != nil { - logger.Warnf("can not open access log file: %s, %v", fileInfo.Name(), err) - return err - } - } - _, err = logFile.WriteString(accessLogMsg + "\n") - if err != nil { - logger.Warnf("can not write to access log file: %s, v%", fileInfo.Name(), err) - return err - } - return nil -} diff --git a/pixiu/pkg/filter/auth/jwt/jwt.go b/pixiu/pkg/filter/auth/jwt/jwt.go deleted file mode 100644 index adde1b58b..000000000 --- a/pixiu/pkg/filter/auth/jwt/jwt.go +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 jwt - -import ( - "encoding/json" - "fmt" - stdHttp "net/http" - "strings" - "time" -) - -import ( - "github.com/MicahParks/keyfunc" - jwt4 "github.com/golang-jwt/jwt/v4" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" -) - -const ( - Kind = constant.HTTPAuthJwtFilter -) - -func init() { - filter.RegisterHttpFilter(&Plugin{}) -} - -type ( - // Plugin is http filter plugin. - Plugin struct { - } - - // FilterFactory is http filter instance - FilterFactory struct { - cfg *Config - errMsg []byte - providerJwks map[string]Provider - } - - Filter struct { - cfg *Config - errMsg []byte - providerJwks map[string]Provider - } - - // Config describe the config of FilterFactory - Config struct { - ErrMsg string `yaml:"err_msg" json:"err_msg" mapstructure:"err_msg"` - Rules []Rules `yaml:"rules" json:"rules" mapstructure:"rules"` - Providers []Providers `yaml:"providers" json:"providers" mapstructure:"providers"` - } -) - -func (p Plugin) Kind() string { - return Kind -} - -func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { - return &FilterFactory{cfg: &Config{}, providerJwks: map[string]Provider{}}, nil -} - -func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error { - f := &Filter{cfg: factory.cfg, errMsg: factory.errMsg, providerJwks: factory.providerJwks} - chain.AppendDecodeFilters(f) - return nil -} - -func (f *Filter) Decode(ctx *http.HttpContext) filter.FilterStatus { - - path := ctx.Request.RequestURI - - router := false - - for _, rule := range f.cfg.Rules { - if strings.HasPrefix(path, rule.Match.Prefix) { - router = true - if f.validAny(rule, ctx) || f.validAll(rule, ctx) { - router = false - break - } - } - } - - if router { - ctx.SendLocalReply(stdHttp.StatusUnauthorized, f.errMsg) - return filter.Stop - } - return filter.Continue -} - -// validAny route single provider verification -func (f *Filter) validAny(rule Rules, ctx *http.HttpContext) bool { - - providerName := rule.Requires.RequiresAny.ProviderName - - if provider, ok := f.providerJwks[providerName]; ok { - ctx.Request.Header.Set(provider.forwardPayloadHeader, provider.issuer) - if key := ctx.Request.Header.Get(provider.headers.Name); key != "" { - return checkToken(key, provider.headers.ValuePrefix, providerName, provider) - } - } - - return false -} - -// validAll route multiple provider verification -func (f *Filter) validAll(rule Rules, ctx *http.HttpContext) bool { - - for _, requirement := range rule.Requires.RequiresAll { - if provider, ok := f.providerJwks[requirement.ProviderName]; ok { - ctx.Request.Header.Set(provider.forwardPayloadHeader, provider.issuer) - if key := ctx.Request.Header.Get(provider.headers.Name); key != "" { - if checkToken(key, provider.headers.ValuePrefix, requirement.ProviderName, provider) { - return true - } - } - } - } - - return false -} - -func (factory *FilterFactory) Apply() error { - - if len(factory.cfg.Providers) == 0 { - return fmt.Errorf("providers is null") - } - - for _, provider := range factory.cfg.Providers { - - if provider.Local != nil { - jwksJSON := json.RawMessage(provider.Local.InlineString) - jwks, err := keyfunc.NewJSON(jwksJSON) - if err != nil { - logger.Warnf("failed to create JWKs from JSON. provider:%s Error: %s", provider.Name, err.Error()) - } else { - provider.FromHeaders.setDefault() - factory.providerJwks[provider.Name] = Provider{jwk: jwks, headers: provider.FromHeaders, - issuer: provider.Issuer, forwardPayloadHeader: provider.ForwardPayloadHeader} - continue - } - } - - if provider.Remote != nil { - uri := provider.Remote.HttpURI - timeout, err := time.ParseDuration(uri.TimeOut) - if err != nil { - logger.Warnf("jwt provides timeout parse fail: %s", err.Error()) - continue - } - - options := keyfunc.Options{RefreshTimeout: timeout} - jwks, err := keyfunc.Get(uri.Uri, options) - if err != nil { - logger.Warnf("failed to create JWKs from resource at the given URL. provider:%s Error: %s", provider.Name, err.Error()) - } else { - provider.FromHeaders.setDefault() - factory.providerJwks[provider.Name] = Provider{jwk: jwks, headers: provider.FromHeaders, - issuer: provider.Issuer, forwardPayloadHeader: provider.ForwardPayloadHeader} - } - } - } - - if len(factory.providerJwks) == 0 { - return fmt.Errorf("providers is null") - } - - if factory.cfg.ErrMsg == "" { - factory.cfg.ErrMsg = "token invalid" - } - - errMsg, _ := json.Marshal(http.ErrResponse{Message: factory.cfg.ErrMsg}) - factory.errMsg = errMsg - - return nil -} - -func (h *FromHeaders) setDefault() { - if h.Name == "" { - h.Name = "Authorization" - } - - if h.ValuePrefix == "" { - h.ValuePrefix = "Bearer " - } -} - -func checkToken(value, prefix, providerName string, provider Provider) bool { - if !strings.HasPrefix(value, prefix) { - logger.Warn("header value prefix mismatch provider:", providerName) - return false - } - - token, err := jwt4.Parse(value[len(prefix):], provider.jwk.Keyfunc) - if err != nil { - logger.Warnf("failed to parse JWKs from JSON. provider:%s Error: %s", providerName, err.Error()) - return false - } - - return token.Valid -} - -func (factory *FilterFactory) Config() interface{} { - return factory.cfg -} diff --git a/pixiu/pkg/filter/authority/authority.go b/pixiu/pkg/filter/authority/authority.go deleted file mode 100644 index ad7a9fd35..000000000 --- a/pixiu/pkg/filter/authority/authority.go +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 authority - -import ( - nh "net/http" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" -) - -const ( - Kind = constant.HTTPAuthorityFilter -) - -func init() { - filter.RegisterHttpFilter(&Plugin{}) -} - -type ( - // AuthorityPlugin is http filter plugin. - Plugin struct { - } - // FilterFactory is http filter instance - FilterFactory struct { - cfg *AuthorityConfiguration - } - Filter struct { - cfg *AuthorityConfiguration - } -) - -func (p *Plugin) Kind() string { - return Kind -} - -func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { - return &FilterFactory{cfg: &AuthorityConfiguration{}}, nil -} - -func (factory *FilterFactory) Config() interface{} { - return factory.cfg -} - -func (factory *FilterFactory) Apply() error { - return nil -} - -func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error { - f := &Filter{cfg: factory.cfg} - chain.AppendDecodeFilters(f) - return nil -} - -func (f *Filter) Decode(c *http.HttpContext) filter.FilterStatus { - for _, r := range f.cfg.Rules { - item := c.GetClientIP() - if r.Limit == App { - item = c.GetApplicationName() - } - - result := passCheck(item, r) - if !result { - c.SendLocalReply(nh.StatusForbidden, constant.Default403Body) - return filter.Stop - } - } - return filter.Continue -} - -func passCheck(item string, rule AuthorityRule) bool { - result := false - for _, it := range rule.Items { - if it == item { - result = true - break - } - } - - if (rule.Strategy == Blacklist && result) || (rule.Strategy == Whitelist && !result) { - return false - } - - return true -} diff --git a/pixiu/pkg/filter/failinject/filter.go b/pixiu/pkg/filter/failinject/filter.go deleted file mode 100644 index aff5c9ccb..000000000 --- a/pixiu/pkg/filter/failinject/filter.go +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 failinject - -import ( - "math/rand" - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - contextHttp "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" -) - -const ( - Kind = constant.HTTPFailInjectFilter -) - -func init() { - filter.RegisterHttpFilter(&Plugin{}) -} - -type ( - Plugin struct { - } - - FilterFactory struct { - cfg *Config - } - Filter struct { - cfg *Config - } -) - -func (factory *FilterFactory) Apply() error { - return nil -} - -func (factory *FilterFactory) Config() interface{} { - return factory.cfg -} - -func (factory *FilterFactory) PrepareFilterChain(ctx *contextHttp.HttpContext, chain filter.FilterChain) error { - f := Filter{ - cfg: factory.cfg, - } - chain.AppendDecodeFilters(f) - return nil -} - -func (p *Plugin) Kind() string { - return Kind -} - -func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { - return &FilterFactory{ - cfg: &Config{ - Rules: make(map[URI]*Rule), - }, - }, nil -} - -func (f Filter) Decode(ctx *contextHttp.HttpContext) filter.FilterStatus { - uriCfg := &Rule{} - if v, ok := f.cfg.Rules[URI(ctx.Request.RequestURI)]; ok { - uriCfg = v - } - if uriCfg == nil { - return filter.Continue - } - - matched := f.match(uriCfg) - if !matched { - return filter.Continue - } - - if uriCfg.Type == TypeAbort { - ctx.SendLocalReply(uriCfg.StatusCode, []byte(uriCfg.Body)) - return filter.Stop - } - - if uriCfg.Type == TypeDelay { - time.Sleep(uriCfg.Delay) - return filter.Stop - } - - return filter.Continue -} - -// match determines whether a given request hits the error injection based on the rule's trigger type. -// -// - If the rule is nil, the request doesn't hit the error injection. -// - If the rule's trigger type is "Always", the request always hits the error injection. -// - If the rule's trigger type is "Percentage", the request hits the error injection based on a specified percentage chance. -// - If the rule's trigger type is "Random", the request hits the error injection based on a random chance. -// -// Returns: -// - true if the request hits the error injection -// - false otherwise -func (f Filter) match(rule *Rule) (matched bool) { - if rule == nil { - return false - } - if rule.TriggerType == TriggerTypeAlways { - return true - } - if rule.TriggerType == TriggerTypePercentage { - return percentage(rule.Odds) - } - if rule.TriggerType == TriggerTypeRandom { - return random() - } - return false -} - -// random if the current request is matched -func random() bool { - return (rand.Intn(1000)+1)%2 == 0 -} - -// percentage checks if the current request matches based on a given odds percentage. -// The function returns true with a probability equal to the odds percentage. -// -// Parameters: -// - odds: Percentage chance (0-100) of the function returning true. -// -// Returns: -// - true if a generated random number between 1 and 100 (inclusive) is less than or equal to the odds. -// - false otherwise. -func percentage(odds int) bool { - if odds <= 0 { - return false - } - // generate rand number in 1-100 - num := rand.Intn(100) + 1 - return num <= odds -} diff --git a/pixiu/pkg/filter/header/header.go b/pixiu/pkg/filter/header/header.go deleted file mode 100644 index 6957168e3..000000000 --- a/pixiu/pkg/filter/header/header.go +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 header - -import ( - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" -) - -const ( - Kind = constant.HTTPHeaderFilter -) - -func init() { - filter.RegisterHttpFilter(&Plugin{}) -} - -type ( - // Plugin is http filter plugin. - Plugin struct { - } - // FilterFactory is http filter instance - FilterFactory struct { - cfg *Config - } - // Filter is http filter instance - Filter struct { - cfg *Config - } - // Config describe the config of FilterFactory - Config struct{} -) - -func (p *Plugin) Kind() string { - return Kind -} - -func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { - return &FilterFactory{}, nil -} - -func (factory *FilterFactory) Config() interface{} { - return factory.cfg -} - -func (factory *FilterFactory) Apply() error { - return nil -} - -func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error { - f := &Filter{cfg: factory.cfg} - chain.AppendDecodeFilters(f) - return nil -} - -func (f *Filter) Decode(hc *http.HttpContext) filter.FilterStatus { - api := hc.GetAPI() - headers := api.Headers - if len(headers) == 0 { - return filter.Continue - } - - urlHeaders := hc.AllHeaders() - if len(urlHeaders) == 0 { - return filter.Continue - } - - for headerName, headerValue := range headers { - urlHeaderValues := urlHeaders.Values(strings.ToLower(headerName)) - if urlHeaderValues == nil { - return filter.Continue - } - for _, urlHeaderValue := range urlHeaderValues { - if urlHeaderValue == headerValue { - goto FOUND - } - } - FOUND: - continue - } - return filter.Continue -} diff --git a/pixiu/pkg/filter/header/header_test.go b/pixiu/pkg/filter/header/header_test.go deleted file mode 100644 index 620ac9963..000000000 --- a/pixiu/pkg/filter/header/header_test.go +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 header - -import ( - "bytes" - "net/http" - "testing" -) - -import ( - "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" - "github.com/dubbo-go-pixiu/pixiu-api/pkg/router" - "github.com/stretchr/testify/assert" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/mock" -) - -func TestHeader(t *testing.T) { - p := Plugin{} - - filter, _ := p.CreateFilterFactory() - err := filter.Apply() - assert.Nil(t, err) - - api := router.API{ - URLPattern: "/mock/:id/:name", - Method: getMockMethod(config.MethodGet), - Headers: map[string]string{}, - } - request, err := http.NewRequest("POST", "http://www.dubbogopixiu.com/mock/test?name=tc", bytes.NewReader([]byte("{\"id\":\"12345\"}"))) - assert.NoError(t, err) - c := mock.GetMockHTTPContext(request) - c.API(api) - - request.Header.Set("filter", "test") - api.Headers["filter"] = "test" - c1 := mock.GetMockHTTPContext(request) - c1.API(api) - - request.Header.Set("filter", "test1") - c2 := mock.GetMockHTTPContext(request) - c2.API(api) -} - -func getMockMethod(verb config.HTTPVerb) config.Method { - inbound := config.InboundRequest{} - integration := config.IntegrationRequest{} - return config.Method{ - Enable: true, - HTTPVerb: verb, - InboundRequest: inbound, - IntegrationRequest: integration, - } -} diff --git a/pixiu/pkg/filter/http/apiconfig/api_config.go b/pixiu/pkg/filter/http/apiconfig/api_config.go deleted file mode 100644 index d77cf2735..000000000 --- a/pixiu/pkg/filter/http/apiconfig/api_config.go +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 apiconfig - -import ( - "net/http" -) - -import ( - fc "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" - "github.com/dubbo-go-pixiu/pixiu-api/pkg/router" - "github.com/pkg/errors" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/config" - contexthttp "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/http/apiconfig/api" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server" -) - -const ( - // Kind is the kind of Fallback. - Kind = constant.HTTPApiConfigFilter -) - -func init() { - filter.RegisterHttpFilter(&Plugin{}) -} - -type ( - Plugin struct { - } - - FilterFactory struct { - cfg *ApiConfigConfig - apiService api.APIDiscoveryService - } - Filter struct { - apiService api.APIDiscoveryService - } -) - -func (p *Plugin) Kind() string { - return Kind -} - -func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { - return &FilterFactory{cfg: &ApiConfigConfig{}}, nil -} - -func (factory *FilterFactory) Config() interface{} { - return factory.cfg -} - -func (factory *FilterFactory) Apply() error { - factory.apiService = api.NewLocalMemoryAPIDiscoveryService() - - if factory.cfg.Dynamic { - server.GetApiConfigManager().AddApiConfigListener(factory.cfg.DynamicAdapter, factory) - return nil - } - - config, err := initApiConfig(factory.cfg) - if err != nil { - logger.Errorf("Get ApiConfig fail: %v", err) - } - if err := factory.apiService.InitAPIsFromConfig(*config); err != nil { - logger.Errorf("InitAPIsFromConfig fail: %v", err) - } - - return nil -} - -func (factory *FilterFactory) OnAddAPI(r router.API) error { - return factory.apiService.AddOrUpdateAPI(r) -} -func (factory *FilterFactory) OnRemoveAPI(r router.API) error { - return factory.apiService.RemoveAPIByIntance(r) -} - -func (factory *FilterFactory) OnDeleteRouter(r fc.Resource) error { - return factory.apiService.RemoveAPIByPath(r) -} - -func (factory *FilterFactory) GetAPIService() api.APIDiscoveryService { - return factory.apiService -} - -func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error { - f := &Filter{apiService: factory.apiService} - chain.AppendDecodeFilters(f) - return nil -} - -func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus { - req := ctx.Request - v, err := f.apiService.MatchAPI(req.URL.Path, fc.HTTPVerb(req.Method)) - if err != nil { - ctx.SendLocalReply(http.StatusNotFound, constant.Default404Body) - e := errors.Errorf("Requested URL %s not found", req.URL.Path) - logger.Debug(e.Error()) - return filter.Stop - } - - if !v.Method.Enable { - ctx.SendLocalReply(http.StatusNotAcceptable, constant.Default406Body) - e := errors.Errorf("Requested API %s %s does not online", req.Method, req.URL.Path) - logger.Debug(e.Error()) - return filter.Stop - } - ctx.API(v) - return filter.Continue -} - -func (factory *FilterFactory) GetApiService() api.APIDiscoveryService { - return factory.apiService -} - -// initApiConfig return value of the bool is for the judgment of whether is a api meta data error, a kind of silly (?) -func initApiConfig(cf *ApiConfigConfig) (*fc.APIConfig, error) { - if cf.APIMetaConfig != nil { - a, err := config.LoadAPIConfig(cf.APIMetaConfig) - if err != nil { - logger.Warnf("load api config from etcd error:%+v", err) - return nil, err - } - return a, nil - } - - a, err := config.LoadAPIConfigFromFile(cf.Path) - if err != nil { - logger.Errorf("load api config error:%+v", err) - return nil, err - } - return a, nil -} - -var _ filter.HttpFilterFactory = new(FilterFactory) diff --git a/pixiu/pkg/filter/http/apiconfig/config.go b/pixiu/pkg/filter/http/apiconfig/config.go deleted file mode 100644 index 302264342..000000000 --- a/pixiu/pkg/filter/http/apiconfig/config.go +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 apiconfig - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -// ApiConfigConfig the config for api_config filter -type ApiConfigConfig struct { - APIMetaConfig *model.APIMetaConfig `yaml:"api_meta_config" json:"api_meta_config,omitempty"` - Path string `yaml:"path" json:"path,omitempty"` - Dynamic bool `yaml:"dynamic" json:"dynamic,omitempty"` - DynamicAdapter string `yaml:"dynamic_adapter" json:"dynamic_adapter,omitempty"` -} diff --git a/pixiu/pkg/filter/http/dubboproxy/dubbo.go b/pixiu/pkg/filter/http/dubboproxy/dubbo.go deleted file mode 100644 index b49eeefea..000000000 --- a/pixiu/pkg/filter/http/dubboproxy/dubbo.go +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 dubboproxy - -import ( - "context" - "encoding/json" - "fmt" - "io" - "net/http" - "reflect" - "strings" -) - -import ( - "dubbo.apache.org/dubbo-go/v3/common" - dubboConstant "dubbo.apache.org/dubbo-go/v3/common/constant" - "dubbo.apache.org/dubbo-go/v3/protocol/dubbo" - "dubbo.apache.org/dubbo-go/v3/protocol/invocation" - hessian "github.com/apache/dubbo-go-hessian2" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - pixiuHttp "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server" -) - -const ( - // Kind is the kind of plugin. - Kind = constant.HTTPDirectDubboProxyFilter -) - -func init() { - filter.RegisterHttpFilter(&Plugin{}) -} - -type ( - - // Plugin is http to dubbo with direct generic call filter plugin. - Plugin struct{} - - // FilterFactory is http to dubbo with direct generic call filter instance - FilterFactory struct { - cfg *Config - } - - // Filter http to dubbo with direct generic call - Filter struct{} - - // Config http to dubbo with direct generic call config - Config struct{} -) - -// Kind return plugin kind -func (p *Plugin) Kind() string { - return Kind -} - -// CreateFilterFactory create filter factory instance -func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { - return &FilterFactory{cfg: &Config{}}, nil -} - -// Config return filter facotry config, now is empty -func (factory *FilterFactory) Config() interface{} { - return factory.cfg -} - -// Apply init filter factory, now is empty -func (factory *FilterFactory) Apply() error { - return nil -} - -// PrepareFilterChain prepare filter chain -func (factory *FilterFactory) PrepareFilterChain(ctx *pixiuHttp.HttpContext, chain filter.FilterChain) error { - f := &Filter{} - chain.AppendDecodeFilters(f) - return nil -} - -// Decode handle http request to dubbo direct generic call and return http response -func (f *Filter) Decode(hc *pixiuHttp.HttpContext) filter.FilterStatus { - rEntry := hc.GetRouteEntry() - if rEntry == nil { - logger.Info("[dubbo-go-pixiu] http not match route") - bt, _ := json.Marshal(pixiuHttp.ErrResponse{Message: "not match route"}) - hc.SendLocalReply(http.StatusNotFound, bt) - return filter.Stop - } - logger.Debugf("[dubbo-go-pixiu] client choose endpoint from cluster :%v", rEntry.Cluster) - - clusterName := rEntry.Cluster - clusterManager := server.GetClusterManager() - endpoint := clusterManager.PickEndpoint(clusterName, hc) - if endpoint == nil { - logger.Info("[dubbo-go-pixiu] cluster not found endpoint") - bt, _ := json.Marshal(pixiuHttp.ErrResponse{Message: "cluster not found endpoint"}) - hc.SendLocalReply(http.StatusServiceUnavailable, bt) - return filter.Stop - } - - // http://host/{application}/{service}/{method} or https://host/{application}/{service}/{method} - rawPath := hc.Request.URL.Path - rawPath = strings.Trim(rawPath, "/") - splits := strings.Split(rawPath, "/") - - if len(splits) != 3 { - logger.Info("[dubbo-go-pixiu] http path pattern error. path pattern should be http://127.0.0.1/{application}/{service}/{method}") - bt, _ := json.Marshal(pixiuHttp.ErrResponse{Message: "http path pattern error"}) - hc.SendLocalReply(http.StatusBadRequest, bt) - return filter.Stop - } - - service := splits[1] - method := splits[2] - interfaceKey := service - - groupKey := hc.Request.Header.Get(constant.DubboGroup) - versionKey := hc.Request.Header.Get(constant.DubboServiceVersion) - types := hc.Request.Header.Get(constant.DubboServiceMethodTypes) - - rawBody, err := io.ReadAll(hc.Request.Body) - if err != nil { - logger.Infof("[dubbo-go-pixiu] read request body error %v", err) - bt, _ := json.Marshal(pixiuHttp.ErrResponse{Message: fmt.Sprintf("read request body error %v", err)}) - hc.SendLocalReply(http.StatusBadRequest, bt) - return filter.Stop - } - - var body interface{} - if err := json.Unmarshal(rawBody, &body); err != nil { - logger.Infof("[dubbo-go-pixiu] unmarshal request body error %v", err) - bt, _ := json.Marshal(pixiuHttp.ErrResponse{Message: fmt.Sprintf("unmarshal request body error %v", err)}) - hc.SendLocalReply(http.StatusBadRequest, bt) - return filter.Stop - } - - inIArr := make([]interface{}, 3) - inVArr := make([]reflect.Value, 3) - inIArr[0] = method - - var ( - typesList []string - valuesList []hessian.Object - ) - - if types != "" { - typesList = strings.Split(types, ",") - } - - values := body - if _, ok := values.([]interface{}); ok { - for _, v := range values.([]interface{}) { - valuesList = append(valuesList, v) - } - } else { - valuesList = append(valuesList, values) - } - - inIArr[1] = typesList - inIArr[2] = valuesList - - inVArr[0] = reflect.ValueOf(inIArr[0]) - inVArr[1] = reflect.ValueOf(inIArr[1]) - inVArr[2] = reflect.ValueOf(inIArr[2]) - - invoc := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("$invoke"), - invocation.WithArguments(inIArr), - invocation.WithParameterValues(inVArr)) - - url, err := common.NewURL(endpoint.Address.GetAddress(), - common.WithProtocol(dubbo.DUBBO), common.WithParamsValue(dubboConstant.SerializationKey, dubboConstant.Hessian2Serialization), - common.WithParamsValue(dubboConstant.GenericFilterKey, "true"), - common.WithParamsValue(dubboConstant.InterfaceKey, interfaceKey), - common.WithParamsValue(dubboConstant.ReferenceFilterKey, "generic,filter"), - // dubboAttachment must contains group and version info - common.WithParamsValue(dubboConstant.GroupKey, groupKey), - common.WithParamsValue(dubboConstant.VersionKey, versionKey), - common.WithPath(interfaceKey), - ) - if err != nil { - logger.Infof("[dubbo-go-pixiu] newURL error %v", err) - bt, _ := json.Marshal(pixiuHttp.ErrResponse{Message: fmt.Sprintf("newURL error %v", err)}) - hc.SendLocalReply(http.StatusServiceUnavailable, bt) - return filter.Stop - } - - dubboProtocol := dubbo.NewDubboProtocol() - - // TODO: will print many Error when failed to connect server - invoker := dubboProtocol.Refer(url) - if invoker == nil { - logger.Info("[dubbo-go-pixiu] dubbo protocol refer error") - bt, _ := json.Marshal(pixiuHttp.ErrResponse{Message: "dubbo protocol refer error"}) - hc.SendLocalReply(http.StatusServiceUnavailable, bt) - return filter.Stop - } - - var resp interface{} - invoc.SetReply(&resp) - - invCtx, cancel := context.WithTimeout(context.Background(), hc.Timeout) - defer cancel() - result := invoker.Invoke(invCtx, invoc) - result.SetAttachments(invoc.Attachments()) - - if result.Error() != nil { - logger.Debugf("[dubbo-go-pixiu] invoke result error %v", result.Error()) - bt, _ := json.Marshal(pixiuHttp.ErrResponse{Message: fmt.Sprintf("invoke result error %v", result.Error())}) - // TODO statusCode I don't know what dubbo returns when it times out, first use the string to judge - if strings.Contains(result.Error().Error(), "timeout") { - hc.SendLocalReply(http.StatusGatewayTimeout, bt) - } - hc.SendLocalReply(http.StatusServiceUnavailable, bt) - return filter.Stop - } - - value := reflect.ValueOf(result.Result()) - result.SetResult(value.Elem().Interface()) - hc.SourceResp = resp - invoker.Destroy() - // response write in hcm - return filter.Continue -} diff --git a/pixiu/pkg/filter/http/grpcproxy/descriptor_source.go b/pixiu/pkg/filter/http/grpcproxy/descriptor_source.go deleted file mode 100644 index 7ab8f2324..000000000 --- a/pixiu/pkg/filter/http/grpcproxy/descriptor_source.go +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 grpcproxy - -import ( - "fmt" - "sync" -) - -import ( - "github.com/jhump/protoreflect/desc" - "github.com/jhump/protoreflect/dynamic" - "github.com/jhump/protoreflect/grpcreflect" - "github.com/pkg/errors" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" -) - -type DescriptorSource interface { - // ListServices returns a list of fully-qualified service names. It will be all services in a set of - // descriptor files or the set of all services exposed by a gRPC server. - ListServices() ([]string, error) - // FindSymbol returns a descriptor for the given fully-qualified symbol name. - FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) - // AllExtensionsForType returns all known extension fields that extend the given message type name. - AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) -} - -var ErrReflectionNotSupported = errors.New("server does not support the reflection API") - -// serverSource by gRPC server reflection -type serverSource struct { - client *grpcreflect.Client -} - -func (s *serverSource) ListServices() ([]string, error) { - svcs, err := s.client.ListServices() - return svcs, reflectionSupport(err) -} - -func (s *serverSource) FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) { - file, err := s.client.FileContainingSymbol(fullyQualifiedName) - if err != nil { - return nil, reflectionSupport(err) - } - d := file.FindSymbol(fullyQualifiedName) - if d == nil { - return nil, errors.New(fmt.Sprintf("%s not found: %s", "Symbol", fullyQualifiedName)) - } - return d, nil -} - -func (s *serverSource) AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) { - var exts []*desc.FieldDescriptor - nums, err := s.client.AllExtensionNumbersForType(typeName) - if err != nil { - return nil, reflectionSupport(err) - } - for _, fieldNum := range nums { - ext, err := s.client.ResolveExtension(typeName, fieldNum) - if err != nil { - return nil, reflectionSupport(err) - } - exts = append(exts, ext) - } - return exts, nil -} - -func reflectionSupport(err error) error { - if err == nil { - return nil - } - if stat, ok := status.FromError(err); ok && stat.Code() == codes.Unimplemented { - return ErrReflectionNotSupported - } - return err -} - -// fileSource by gRPC proto file -type fileSource struct { - files map[string]*desc.FileDescriptor - er *dynamic.ExtensionRegistry - erInit sync.Once -} - -func (fs *fileSource) ListServices() ([]string, error) { - set := map[string]bool{} - for _, fd := range fs.files { - for _, svc := range fd.GetServices() { - set[svc.GetFullyQualifiedName()] = true - } - } - sl := make([]string, 0, len(set)) - for svc := range set { - sl = append(sl, svc) - } - return sl, nil -} - -func (fs *fileSource) FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) { - for _, fd := range fs.files { - if dsc := fd.FindSymbol(fullyQualifiedName); dsc != nil { - return dsc, nil - } - } - return nil, fmt.Errorf("could not found symbol %v", fullyQualifiedName) -} - -func (fs *fileSource) AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) { - fs.erInit.Do(func() { - fs.er = &dynamic.ExtensionRegistry{} - for _, fd := range fs.files { - fs.er.AddExtensionsFromFile(fd) - } - }) - return fs.er.AllExtensionsForType(typeName), nil -} - -// compositeSource by fileSource and serverSource -type compositeSource struct { - reflection DescriptorSource - file DescriptorSource -} - -func (cs *compositeSource) ListServices() ([]string, error) { - if cs.reflection == nil { - return nil, ErrReflectionNotSupported - } - return cs.reflection.ListServices() -} - -func (cs *compositeSource) FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) { - if cs.reflection != nil { - descriptor, err := cs.reflection.FindSymbol(fullyQualifiedName) - if err == nil { - logger.Debugf("%s find symbol by reflection : %v", loggerHeader, descriptor) - return descriptor, nil - } - } - - return cs.file.FindSymbol(fullyQualifiedName) -} - -func (cs *compositeSource) AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) { - - if cs.reflection == nil { - fileExts, err := cs.file.AllExtensionsForType(typeName) - if err != nil { - return fileExts, nil - } - } else { - exts, err := cs.reflection.AllExtensionsForType(typeName) - if err != nil { - return cs.file.AllExtensionsForType(typeName) - } - tags := make(map[int32]bool) - for _, ext := range exts { - tags[ext.GetNumber()] = true - } - - fileExts, err := cs.file.AllExtensionsForType(typeName) - if err != nil { - return exts, nil - } - for _, ext := range fileExts { - if !tags[ext.GetNumber()] { - exts = append(exts, ext) - } - } - return exts, nil - } - return nil, nil -} diff --git a/pixiu/pkg/filter/http/grpcproxy/grpc.go b/pixiu/pkg/filter/http/grpcproxy/grpc.go deleted file mode 100644 index 56bc264a9..000000000 --- a/pixiu/pkg/filter/http/grpcproxy/grpc.go +++ /dev/null @@ -1,404 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 grpcproxy - -import ( - "context" - "errors" - "fmt" - "io" - stdHttp "net/http" - "strings" - "sync" - "time" -) - -import ( - "github.com/golang/protobuf/jsonpb" //nolint - "github.com/golang/protobuf/proto" //nolint - "github.com/jhump/protoreflect/desc" - "github.com/jhump/protoreflect/dynamic" - "github.com/jhump/protoreflect/dynamic/grpcdynamic" - perrors "github.com/pkg/errors" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - ct "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server" -) - -const ( - // Kind is the kind of Fallback. - Kind = constant.HTTPGrpcProxyFilter - - loggerHeader = "[grpc-proxy]" - - // DescriptorSourceKey current ds - DescriptorSourceKey = "DescriptorSource" - - // GrpcClientConnKey the grpc-client-conn by the coroutine local - GrpcClientConnKey = "GrpcClientConn" -) - -func init() { - filter.RegisterHttpFilter(&Plugin{}) -} - -const ( - NONE = "none" - AUTO = "auto" - LOCAL = "local" - REMOTE = "remote" -) - -type ( - DescriptorSourceStrategy string - - // Plugin is grpc filter plugin. - Plugin struct { - } - - // FilterFactory is grpc filter instance - FilterFactory struct { - cfg *Config - // grpc descriptor source factory - descriptor *Descriptor - // hold grpc.ClientConns, key format: cluster name + "." + endpoint - pools map[string]*sync.Pool - - extReg *dynamic.ExtensionRegistry - registered map[string]bool - } - Filter struct { - cfg *Config - // grpc descriptor source factory - descriptor *Descriptor - // hold grpc.ClientConns, key format: cluster name + "." + endpoint - pools map[string]*sync.Pool - - extReg *dynamic.ExtensionRegistry - registered map[string]bool - } - - // Config describe the config of AccessFilter - Config struct { - DescriptorSourceStrategy DescriptorSourceStrategy `yaml:"descriptor_source_strategy" json:"descriptor_source_strategy" default:"auto"` - Path string `yaml:"path" json:"path"` - Rules []*Rule `yaml:"rules" json:"rules"` //nolint - Timeout time.Duration `yaml:"timeout" json:"timeout"` //nolint - } - - Rule struct { - Selector string `yaml:"selector" json:"selector"` - Match Match `yaml:"match" json:"match"` - } - - Match struct { - Method string `yaml:"method" json:"method"` //nolint - } -) - -func (c DescriptorSourceStrategy) String() string { - return string(c) -} - -func (c DescriptorSourceStrategy) Val() DescriptorSourceStrategy { - switch c { - case NONE: - return NONE - case AUTO: - return AUTO - case LOCAL: - return LOCAL - case REMOTE: - return REMOTE - } - return "" -} - -func (p *Plugin) Kind() string { - return Kind -} - -func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { - return &FilterFactory{cfg: &Config{DescriptorSourceStrategy: AUTO}, descriptor: &Descriptor{}}, nil -} - -func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error { - f := &Filter{cfg: factory.cfg, descriptor: factory.descriptor, pools: factory.pools, extReg: factory.extReg, registered: factory.registered} - chain.AppendDecodeFilters(f) - return nil -} - -// getServiceAndMethod first return value is package.service, second one is method name -func getServiceAndMethod(path string) (string, string) { - pos := strings.LastIndex(path, "/") - if pos < 0 { - return "", "" - } - - mth := path[pos+1:] - prefix := strings.TrimSuffix(path, "/"+mth) - - pos = strings.LastIndex(prefix, "/") - if pos < 0 { - return "", "" - } - - svc := prefix[pos+1:] - return svc, mth -} - -// Decode use the default http to grpc transcoding strategy https://cloud.google.com/endpoints/docs/grpc/transcoding -func (f *Filter) Decode(c *http.HttpContext) filter.FilterStatus { - svc, mth := getServiceAndMethod(c.GetUrl()) - - var clientConn *grpc.ClientConn - var err error - - re := c.GetRouteEntry() - logger.Debugf("%s client choose endpoint from cluster :%v", loggerHeader, re.Cluster) - - e := server.GetClusterManager().PickEndpoint(re.Cluster, c) - if e == nil { - logger.Errorf("%s err {cluster not exists}", loggerHeader) - c.SendLocalReply(stdHttp.StatusServiceUnavailable, []byte("cluster not exists")) - return filter.Stop - } - // timeout for Dial and Invoke - ctx, cancel := context.WithTimeout(c.Ctx, c.Timeout) - defer cancel() - ep := e.Address.GetAddress() - - p, ok := f.pools[strings.Join([]string{re.Cluster, ep}, ".")] - if !ok { - p = &sync.Pool{} - } - - clientConn, ok = p.Get().(*grpc.ClientConn) - if !ok || clientConn == nil { - // TODO(Kenway): Support Credential and TLS - clientConn, err = grpc.DialContext(ctx, ep, grpc.WithTransportCredentials(insecure.NewCredentials())) - if err != nil || clientConn == nil { - logger.Errorf("%s err {failed to connect to grpc service provider}", loggerHeader) - c.SendLocalReply(stdHttp.StatusServiceUnavailable, []byte((fmt.Sprintf("%s", err)))) - return filter.Stop - } - } - - // get DescriptorSource, contain file and reflection - source, err := f.descriptor.getDescriptorSource(context.WithValue(ctx, ct.ContextKey(GrpcClientConnKey), clientConn), f.cfg) - if err != nil { - logger.Errorf("%s err %s : %s ", loggerHeader, "get desc source fail", err) - c.SendLocalReply(stdHttp.StatusInternalServerError, []byte("service not config proto file or the server not support reflection API")) - return filter.Stop - } - //put DescriptorSource concurrent, del if no need - ctx = context.WithValue(ctx, ct.ContextKey(DescriptorSourceKey), source) - - dscp, err := source.FindSymbol(svc) - if err != nil { - logger.Errorf("%s err {%s}", loggerHeader, "request path invalid") - c.SendLocalReply(stdHttp.StatusBadRequest, []byte("method not allow")) - return filter.Stop - } - - svcDesc, ok := dscp.(*desc.ServiceDescriptor) - if !ok { - logger.Errorf("%s err {service not expose, %s}", loggerHeader, svc) - c.SendLocalReply(stdHttp.StatusBadRequest, []byte(fmt.Sprintf("service not expose, %s", svc))) - return filter.Stop - } - - mthDesc := svcDesc.FindMethodByName(mth) - - err = f.registerExtension(source, mthDesc) - if err != nil { - logger.Errorf("%s err {%s}", loggerHeader, "register extension failed") - c.SendLocalReply(stdHttp.StatusInternalServerError, []byte(fmt.Sprintf("%s", err))) - return filter.Stop - } - - msgFac := dynamic.NewMessageFactoryWithExtensionRegistry(f.extReg) - grpcReq := msgFac.NewMessage(mthDesc.GetInputType()) - - err = jsonToProtoMsg(c.Request.Body, grpcReq) - if err != nil && !errors.Is(err, io.EOF) { - logger.Errorf("%s err {failed to convert json to proto msg, %s}", loggerHeader, err.Error()) - c.SendLocalReply(stdHttp.StatusInternalServerError, []byte(fmt.Sprintf("%s", err))) - return filter.Stop - } - - stub := grpcdynamic.NewStubWithMessageFactory(clientConn, msgFac) - - // metadata in grpc has the same feature in http - md := mapHeaderToMetadata(c.AllHeaders()) - ctx = metadata.NewOutgoingContext(ctx, md) - - md = metadata.MD{} - t := metadata.MD{} - - resp, err := Invoke(ctx, stub, mthDesc, grpcReq, grpc.Header(&md), grpc.Trailer(&t)) - // judge err is server side error or not - if st, ok := status.FromError(err); !ok || isServerError(st) { - if isServerTimeout(st) { - logger.Errorf("%s err {failed to invoke grpc service provider because timeout, err:%s}", loggerHeader, err.Error()) - c.SendLocalReply(stdHttp.StatusGatewayTimeout, []byte(fmt.Sprintf("%s", err))) - return filter.Stop - } - logger.Errorf("%s err {failed to invoke grpc service provider, %s}", loggerHeader, err.Error()) - c.SendLocalReply(stdHttp.StatusServiceUnavailable, []byte(fmt.Sprintf("%s", err))) - return filter.Stop - } - - res, err := protoMsgToJson(resp) - if err != nil { - logger.Errorf("%s err {failed to convert proto msg to json, %s}", loggerHeader, err.Error()) - c.SendLocalReply(stdHttp.StatusInternalServerError, []byte(fmt.Sprintf("%s", err))) - return filter.Stop - } - - h := mapMetadataToHeader(md) - th := mapMetadataToHeader(t) - - // let response filter handle resp - c.SourceResp = &stdHttp.Response{ - StatusCode: stdHttp.StatusOK, - Header: h, - Body: io.NopCloser(strings.NewReader(res)), - Trailer: th, - Request: c.Request, - } - p.Put(clientConn) - return filter.Continue -} - -func (f *Filter) registerExtension(source DescriptorSource, mthDesc *desc.MethodDescriptor) error { - err := RegisterExtension(source, f.extReg, mthDesc.GetInputType(), f.registered) - if err != nil { - return perrors.New("register extension failed") - } - - err = RegisterExtension(source, f.extReg, mthDesc.GetOutputType(), f.registered) - if err != nil { - return perrors.New("register extension failed") - } - return nil -} - -func RegisterExtension(source DescriptorSource, extReg *dynamic.ExtensionRegistry, msgDesc *desc.MessageDescriptor, registered map[string]bool) error { - msgType := msgDesc.GetFullyQualifiedName() - if _, ok := registered[msgType]; ok { - return nil - } - - if len(msgDesc.GetExtensionRanges()) > 0 { - fds, err := source.AllExtensionsForType(msgType) - if err != nil { - return fmt.Errorf("failed to find msg type {%s} in file source", msgType) - } - - err = extReg.AddExtension(fds...) - if err != nil { - return fmt.Errorf("failed to register extensions of msgType {%s}, err is {%s}", msgType, err.Error()) - } - } - - for _, fd := range msgDesc.GetFields() { - if fd.GetMessageType() != nil { - err := RegisterExtension(source, extReg, fd.GetMessageType(), registered) - if err != nil { - return err - } - } - } - - return nil -} - -func mapHeaderToMetadata(header stdHttp.Header) metadata.MD { - md := metadata.MD{} - for key, val := range header { - md.Append(key, val...) - } - return md -} - -func mapMetadataToHeader(md metadata.MD) stdHttp.Header { - h := stdHttp.Header{} - for key, val := range md { - for _, v := range val { - h.Add(key, v) - } - } - return h -} - -func jsonToProtoMsg(reader io.Reader, msg proto.Message) error { - body, err := io.ReadAll(reader) - if err != nil { - return err - } - return jsonpb.UnmarshalString(string(body), msg) -} - -func protoMsgToJson(msg proto.Message) (string, error) { - m := jsonpb.Marshaler{} - return m.MarshalToString(msg) -} - -func isServerError(st *status.Status) bool { - return st.Code() == codes.DeadlineExceeded || st.Code() == codes.ResourceExhausted || st.Code() == codes.Internal || - st.Code() == codes.Unavailable -} - -func isServerTimeout(st *status.Status) bool { - return st.Code() == codes.DeadlineExceeded || st.Code() == codes.Canceled -} - -func (factory *FilterFactory) Config() interface{} { - return factory.cfg -} - -func (factory *FilterFactory) Apply() error { - - err := configCheck(factory.cfg) - if err != nil { - return err - } - - factory.descriptor.initDescriptorSource(factory.cfg) - - return nil -} - -func configCheck(cfg *Config) error { - if len(cfg.DescriptorSourceStrategy.Val()) == 0 { - return perrors.Errorf("grpc descriptor source config `descriptor_source_strategy` is `%s`, maybe set it `%s`", cfg.DescriptorSourceStrategy.String(), AUTO) - } - return nil -} diff --git a/pixiu/pkg/filter/http/loadbalancer/loadbalancer.go b/pixiu/pkg/filter/http/loadbalancer/loadbalancer.go deleted file mode 100644 index 776897443..000000000 --- a/pixiu/pkg/filter/http/loadbalancer/loadbalancer.go +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 loadbalancer - -import ( - "math/rand" - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - contexthttp "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" -) - -const ( - Kind = constant.HTTPLoadBalanceFilter -) - -func init() { - filter.RegisterHttpFilter(&Plugin{}) -} - -type ( - // Plugin is http filter plugin. - Plugin struct { - } - - // FilterFactory is http filter instance - FilterFactory struct { - cfg *Config - } - // Filter is http filter instance - Filter struct { - cfg *Config - } - Config struct{} -) - -func (p *Plugin) Kind() string { - return Kind -} - -func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { - return &FilterFactory{cfg: &Config{}}, nil -} - -func (factory *FilterFactory) Config() interface{} { - return factory.cfg -} - -func (factory *FilterFactory) Apply() error { - return nil -} - -func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error { - f := &Filter{cfg: factory.cfg} - chain.AppendDecodeFilters(f) - return nil -} - -func (f *Filter) Decode(c *contexthttp.HttpContext) filter.FilterStatus { - allInstances := strings.Split(c.GetAPI().IntegrationRequest.HTTPBackendConfig.URL, ",") - idx := rand.Int31n(int32(len(allInstances))) - c.Api.IntegrationRequest.HTTPBackendConfig.URL = strings.TrimSpace(allInstances[idx]) - return filter.Continue -} diff --git a/pixiu/pkg/filter/http/proxywasm/filter.go b/pixiu/pkg/filter/http/proxywasm/filter.go deleted file mode 100644 index fb13f46b9..000000000 --- a/pixiu/pkg/filter/http/proxywasm/filter.go +++ /dev/null @@ -1,126 +0,0 @@ -//go:build !windows - -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 proxywasm - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/wasm" -) - -func init() { - filter.RegisterHttpFilter(&Plugin{}) -} - -type ( - Plugin struct { - } - - WasmFilterFactory struct { - cfg *Config - } - - WasmFilter struct { - abiContextWrappers map[string]*wasm.ABIContextWrapper - } - - Config struct { - WasmServices []*Service `yaml:"wasm_services" json:"wasm_services" mapstructure:"wasm_services"` - } - - Service struct { - Name string `yaml:"name" json:"name" mapstructure:"name"` - } -) - -func (w *WasmFilter) Decode(ctx *http.HttpContext) filter.FilterStatus { - - // sample: display http header - if wrapper := w.abiContextWrappers[wasm.HeaderLevel]; wrapper != nil { - wrapper.Context.Instance.Lock(wrapper.Context) - defer wrapper.Context.Instance.Unlock() - - err := wrapper.Context.GetExports().ProxyOnContextCreate(wrapper.ContextID, wasm.GetServiceRootID(wasm.HeaderLevel)) - if err != nil { - logger.Warnf("[dubbo-go-pixiu] wasmFilter call ProxyOnContextCreate failed: %v", err) - return filter.Continue - } - - _, err = wrapper.Context.GetExports().ProxyOnRequestHeaders(wrapper.ContextID, int32(len(ctx.Request.Header)), 1) - if err != nil { - logger.Warnf("[dubbo-go-pixiu] wasmFilter call ProxyOnRequestHeaders failed: %v", err) - } - - err = wrapper.Context.GetExports().ProxyOnDelete(wrapper.ContextID) - if err != nil { - logger.Warnf("[dubbo-go-pixiu] wasmFilter call ProxyOnDelete failed: %v", err) - } - } - return filter.Continue -} - -func (w *WasmFilter) Encode(ctx *http.HttpContext) filter.FilterStatus { - for _, wrapper := range w.abiContextWrappers { - if err := wasm.ContextDone(wrapper); err != nil { - logger.Warnf("[dubbo-go-pixiu] wasmFilter call contextDone failed: %v", err) - } - } - - if val := ctx.GetHeader("go-wasm-header"); val != "" { - if _, err := ctx.Writer.Write([]byte(val)); err != nil { - logger.Errorf("write response error: %s", err) - } - } - return filter.Continue -} - -func (factory *WasmFilterFactory) Config() interface{} { - return factory.cfg -} - -func (factory *WasmFilterFactory) Apply() error { - return nil -} - -func (factory *WasmFilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error { - filter := &WasmFilter{ - abiContextWrappers: make(map[string]*wasm.ABIContextWrapper), - } - for _, service := range factory.cfg.WasmServices { - if abiContext := wasm.CreateABIContextByName(service.Name, ctx); abiContext != nil { - filter.abiContextWrappers[service.Name] = abiContext - } - } - chain.AppendDecodeFilters(filter) - chain.AppendEncodeFilters(filter) - return nil -} - -func (p *Plugin) Kind() string { - return constant.HTTPWasmFilter -} - -func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { - return &WasmFilterFactory{ - cfg: &Config{}, - }, nil -} diff --git a/pixiu/pkg/filter/http/remote/call.go b/pixiu/pkg/filter/http/remote/call.go deleted file mode 100644 index 505252c84..000000000 --- a/pixiu/pkg/filter/http/remote/call.go +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 remote - -import ( - "errors" - "fmt" - "net/http" - "os" - "strconv" - "strings" -) - -import ( - apiConf "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" - "github.com/dubbo-go-pixiu/pixiu-api/pkg/router" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client/dubbo" - clienthttp "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client/triple" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - contexthttp "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" -) - -const ( - OPEN = iota - CLOSE - ALL -) - -const ( - Kind = constant.HTTPDubboProxyFilter -) - -func init() { - filter.RegisterHttpFilter(&Plugin{}) -} - -type ( - mockLevel int8 - - Plugin struct { - } - - FilterFactory struct { - conf *config - } - - Filter struct { - conf config - } - - config struct { - Level mockLevel `yaml:"level,omitempty" json:"level,omitempty"` - Dpc *dubbo.DubboProxyConfig `yaml:"dubboProxyConfig,omitempty" json:"dubboProxyConfig,omitempty"` - } -) - -func (p *Plugin) Kind() string { - return Kind -} - -func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { - return &FilterFactory{conf: &config{}}, nil -} - -func (factory *FilterFactory) Config() interface{} { - return factory.conf -} - -func (factory *FilterFactory) Apply() error { - mock := 1 - mockStr := os.Getenv(constant.EnvMock) - if len(mockStr) > 0 { - i, err := strconv.Atoi(mockStr) - if err == nil { - mock = i - } - } - level := mockLevel(mock) - if level < 0 || level > 2 { - level = CLOSE - } - factory.conf.Level = level - // must init it at apply function - if factory.conf.Dpc == nil { - return errors.New("expect the dubboProxyConfig config the registries") - } - dubbo.InitDefaultDubboClient(factory.conf.Dpc) - triple.InitDefaultTripleClient(factory.conf.Dpc.Protoset) - return nil -} - -func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error { - f := &Filter{conf: *factory.conf} - chain.AppendDecodeFilters(f) - return nil -} - -func (f *Filter) Decode(c *contexthttp.HttpContext) filter.FilterStatus { - if f.conf.Dpc != nil && f.conf.Dpc.AutoResolve { - if err := f.resolve(c); err != nil { - c.SendLocalReply(http.StatusInternalServerError, []byte(fmt.Sprintf("auto resolve err: %s", err))) - return filter.Stop - } - } - - api := c.GetAPI() - - if (f.conf.Level == OPEN && api.Mock) || (f.conf.Level == ALL) { - c.SourceResp = &contexthttp.ErrResponse{ - Message: "mock success", - } - return filter.Continue - } - - typ := api.Method.IntegrationRequest.RequestType - - cli, err := f.matchClient(typ) - if err != nil { - panic(err) - } - - req := client.NewReq(c.Request.Context(), c.Request, *api) - req.Timeout = c.Timeout - resp, err := cli.Call(req) - if err != nil { - logger.Errorf("[dubbo-go-pixiu] client call err: %v!", err) - if strings.Contains(strings.ToLower(err.Error()), "timeout") { - c.SendLocalReply(http.StatusGatewayTimeout, []byte(fmt.Sprintf("client call timeout err: %s", err))) - return filter.Stop - } - c.SendLocalReply(http.StatusInternalServerError, []byte(fmt.Sprintf("client call err: %s", err))) - return filter.Stop - } - - logger.Debugf("[dubbo-go-pixiu] client call resp: %v", resp) - - c.SourceResp = resp - return filter.Continue -} - -func (f *Filter) matchClient(typ apiConf.RequestType) (client.Client, error) { - switch strings.ToLower(string(typ)) { - case string(apiConf.DubboRequest): - return dubbo.SingletonDubboClient(), nil - // todo @(laurence) add triple to apiConf - case "triple": - return triple.SingletonTripleClient(f.conf.Dpc.Protoset), nil - case string(apiConf.HTTPRequest): - return clienthttp.SingletonHTTPClient(), nil - default: - return nil, errors.New("not support") - } -} - -func (f *Filter) resolve(ctx *contexthttp.HttpContext) error { - // method must be post - req := ctx.Request - if req.Method != http.MethodPost { - return errors.New("http request method must be post when trying to auto resolve") - } - // header must has x-dubbo-http1.1-dubbo-version to declare using auto resolve rule - version := req.Header.Get(constant.DubboHttpDubboVersion) - if version == "" { - return errors.New("http request must has x-dubbo-http1.1-dubbo-version header when trying to auto resolve") - } - - // http://host/{application}/{service}/{method} or https://host/{application}/{service}/{method} - rawPath := req.URL.Path - rawPath = strings.Trim(rawPath, "/") - splits := strings.Split(rawPath, "/") - if len(splits) != 3 { - return errors.New("http request path must meet {application}/{service}/{method} format when trying to auto resolve") - } - - integrationRequest := apiConf.IntegrationRequest{} - resolveProtocol := req.Header.Get(constant.DubboServiceProtocol) - if resolveProtocol == string(apiConf.HTTPRequest) { - integrationRequest.RequestType = apiConf.HTTPRequest - } else if resolveProtocol == string(apiConf.DubboRequest) { - integrationRequest.RequestType = apiConf.DubboRequest - } else if resolveProtocol == "triple" { - integrationRequest.RequestType = "triple" - } else { - return errors.New("http request has unknown protocol in x-dubbo-service-protocol when trying to auto resolve") - } - - dubboBackendConfig := apiConf.DubboBackendConfig{} - dubboBackendConfig.Version = req.Header.Get(constant.DubboServiceVersion) - dubboBackendConfig.Group = req.Header.Get(constant.DubboGroup) - integrationRequest.DubboBackendConfig = dubboBackendConfig - - defaultMappingParams := []apiConf.MappingParam{ - { - Name: "requestBody.values", - MapTo: "opt.values", - }, { - Name: "requestBody.types", - MapTo: "opt.types", - }, { - Name: "uri.application", - MapTo: "opt.application", - }, { - Name: "uri.interface", - MapTo: "opt.interface", - }, { - Name: "uri.method", - MapTo: "opt.method", - }, - } - integrationRequest.MappingParams = defaultMappingParams - - method := apiConf.Method{ - Enable: true, - Mock: false, - HTTPVerb: http.MethodPost, - } - method.IntegrationRequest = integrationRequest - - inboundRequest := apiConf.InboundRequest{} - inboundRequest.RequestType = apiConf.HTTPRequest - method.InboundRequest = inboundRequest - - api := router.API{} - api.URLPattern = "/:application/:interface/:method" - api.Method = method - ctx.API(api) - return nil -} diff --git a/pixiu/pkg/filter/metric/metric.go b/pixiu/pkg/filter/metric/metric.go deleted file mode 100644 index 13d6b8480..000000000 --- a/pixiu/pkg/filter/metric/metric.go +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 metric - -import ( - "fmt" - stdhttp "net/http" - "time" -) - -import ( - "github.com/pkg/errors" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/metric/global" - "go.opentelemetry.io/otel/metric/instrument" - "go.opentelemetry.io/otel/metric/instrument/syncint64" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" -) - -const ( - Kind = constant.HTTPMetricFilter -) - -var ( - totalElapsed syncint64.Counter - totalCount syncint64.Counter - totalError syncint64.Counter - - sizeRequest syncint64.Counter - sizeResponse syncint64.Counter - durationHist syncint64.Histogram -) - -func init() { - filter.RegisterHttpFilter(&Plugin{}) -} - -type ( - // FilterFactory is http filter plugin. - Plugin struct { - } - // FilterFactory is http filter instance - FilterFactory struct { - } - Filter struct { - start time.Time - } - // Config describe the config of FilterFactory - Config struct{} -) - -func (p *Plugin) Kind() string { - return Kind -} - -func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { - return &FilterFactory{}, nil -} - -func (factory *FilterFactory) Config() interface{} { - return &struct{}{} -} - -func (factory *FilterFactory) Apply() error { - // init - err := registerOtelMetric() - return err -} - -func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error { - f := &Filter{} - chain.AppendDecodeFilters(f) - chain.AppendEncodeFilters(f) - return nil -} - -func (f *Filter) Decode(c *http.HttpContext) filter.FilterStatus { - f.start = time.Now() - return filter.Continue -} - -func (f *Filter) Encode(c *http.HttpContext) filter.FilterStatus { - - commonAttrs := []attribute.KeyValue{ - attribute.String("code", fmt.Sprintf("%d", c.GetStatusCode())), - attribute.String("method", c.Request.Method), - attribute.String("url", c.GetUrl()), - attribute.String("host", c.Request.Host), - } - - latency := time.Since(f.start) - totalCount.Add(c.Ctx, 1, commonAttrs...) - latencyMilli := latency.Milliseconds() - totalElapsed.Add(c.Ctx, latencyMilli, commonAttrs...) - if c.LocalReply() { - totalError.Add(c.Ctx, 1) - } - - durationHist.Record(c.Ctx, latencyMilli, commonAttrs...) - size, err := computeApproximateRequestSize(c.Request) - if err != nil { - logger.Warn("can not compute request size", err) - } else { - sizeRequest.Add(c.Ctx, int64(size), commonAttrs...) - } - - size, err = computeApproximateResponseSize(c.TargetResp) - if err != nil { - logger.Warn("can not compute response size", err) - } else { - sizeResponse.Add(c.Ctx, int64(size), commonAttrs...) - } - - logger.Debugf("[Metric] [UPSTREAM] receive request | %d | %s | %s | %s | ", c.GetStatusCode(), latency, c.GetMethod(), c.GetUrl()) - return filter.Continue -} - -func computeApproximateResponseSize(res *client.Response) (int, error) { - if res == nil { - return 0, errors.New("client.Response is null pointer ") - } - return len(res.Data), nil -} - -func computeApproximateRequestSize(r *stdhttp.Request) (int, error) { - if r == nil { - return 0, errors.New("http.Request is null pointer ") - } - s := 0 - if r.URL != nil { - s = len(r.URL.Path) - } - s += len(r.Method) - s += len(r.Proto) - for name, values := range r.Header { - s += len(name) - for _, value := range values { - s += len(value) - } - } - s += len(r.Host) - if r.ContentLength != -1 { - s += int(r.ContentLength) - } - return s, nil -} - -func registerOtelMetric() error { - meter := global.MeterProvider().Meter("pixiu") - - elapsedCounter, err := meter.SyncInt64().Counter("pixiu_request_elapsed", instrument.WithDescription("request total elapsed in pixiu")) - if err != nil { - logger.Errorf("register pixiu_request_elapsed metric failed, err: %v", err) - return err - } - totalElapsed = elapsedCounter - - count, err := meter.SyncInt64().Counter("pixiu_request_count", instrument.WithDescription("request total count in pixiu")) - if err != nil { - logger.Errorf("register pixiu_request_count metric failed, err: %v", err) - return err - } - totalCount = count - - errorCounter, err := meter.SyncInt64().Counter("pixiu_request_error_count", instrument.WithDescription("request error total count in pixiu")) - if err != nil { - logger.Errorf("register pixiu_request_error_count metric failed, err: %v", err) - return err - } - totalError = errorCounter - - sizeRequest, err = meter.SyncInt64().Counter("pixiu_request_content_length", instrument.WithDescription("request total content length in pixiu")) - if err != nil { - logger.Errorf("register pixiu_request_content_length metric failed, err: %v", err) - return err - } - - sizeResponse, err = meter.SyncInt64().Counter("pixiu_response_content_length", instrument.WithDescription("request total content length response in pixiu")) - if err != nil { - logger.Errorf("register pixiu_response_content_length metric failed, err: %v", err) - return err - } - - durationHist, err = meter.SyncInt64().Histogram( - "pixiu_process_time_millicec", - instrument.WithDescription("request process time response in pixiu"), - ) - if err != nil { - logger.Errorf("register pixiu_process_time_millisec metric failed, err: %v", err) - return err - } - - return nil -} diff --git a/pixiu/pkg/filter/metric/metric_test.go b/pixiu/pkg/filter/metric/metric_test.go deleted file mode 100644 index e76bb405a..000000000 --- a/pixiu/pkg/filter/metric/metric_test.go +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 metric - -import ( - "bytes" - "net/http" - "testing" -) - -import ( - "github.com/stretchr/testify/assert" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/mock" -) - -func TestMetric(t *testing.T) { - filter := &Filter{} - err := registerOtelMetric() - if err != nil { - t.Fatal(err) - return - } - - request, err := http.NewRequest("POST", "http://www.dubbogopixiu.com/mock/test?name=tc", bytes.NewReader([]byte("{\"id\":\"12345\"}"))) - assert.NoError(t, err) - c := mock.GetMockHTTPContext(request) - filter.Decode(c) - filter.Encode(c) - t.Log("log filter test is finished") -} diff --git a/pixiu/pkg/filter/network/dubboproxy/manager.go b/pixiu/pkg/filter/network/dubboproxy/manager.go deleted file mode 100644 index ff81afb81..000000000 --- a/pixiu/pkg/filter/network/dubboproxy/manager.go +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 dubboproxy - -import ( - "context" - "reflect" -) - -import ( - dubboConstant "dubbo.apache.org/dubbo-go/v3/common/constant" - "dubbo.apache.org/dubbo-go/v3/protocol/dubbo" - "dubbo.apache.org/dubbo-go/v3/protocol/invocation" - "dubbo.apache.org/dubbo-go/v3/remoting" - hessian "github.com/apache/dubbo-go-hessian2" - "github.com/dubbogo/grpc-go/metadata" - "github.com/go-errors/errors" - perrors "github.com/pkg/errors" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - router2 "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/router" - dubbo2 "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/dubbo" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -// DubboProxyConnectionManager network filter for dubbo -type DubboProxyConnectionManager struct { - filter.EmptyNetworkFilter - config *model.DubboProxyConnectionManagerConfig - routerCoordinator *router2.RouterCoordinator - codec *dubbo.DubboCodec - filterManager *DubboFilterManager -} - -// CreateDubboProxyConnectionManager create dubbo proxy connection manager -func CreateDubboProxyConnectionManager(config *model.DubboProxyConnectionManagerConfig) *DubboProxyConnectionManager { - filterManager := NewDubboFilterManager(config.DubboFilters) - hcm := &DubboProxyConnectionManager{config: config, codec: &dubbo.DubboCodec{}, filterManager: filterManager} - hcm.routerCoordinator = router2.CreateRouterCoordinator(&config.RouteConfig) - return hcm -} - -// OnDecode decode bytes to DecodeResult -func (dcm *DubboProxyConnectionManager) OnDecode(data []byte) (interface{}, int, error) { - - resp, length, err := dcm.codec.Decode(data) - // err := pkg.Unmarshal(buf, p.client) - if err != nil { - if perrors.Is(err, hessian.ErrHeaderNotEnough) || perrors.Is(err, hessian.ErrBodyNotEnough) { - return nil, 0, nil - } - logger.Errorf("pkg.Unmarshal error:%+v", err) - return nil, length, err - } - return resp, length, nil -} - -// OnEncode encode Response to bytes -func (dcm *DubboProxyConnectionManager) OnEncode(pkg interface{}) ([]byte, error) { - res, ok := pkg.(*remoting.Response) - if ok { - buf, err := (dcm.codec).EncodeResponse(res) - if err != nil { - logger.Warnf("binary.Write(res{%#v}) = err{%#v}", res, perrors.WithStack(err)) - return nil, perrors.WithStack(err) - } - return buf.Bytes(), nil - } - - req, ok := pkg.(*remoting.Request) - if ok { - buf, err := (dcm.codec).EncodeRequest(req) - if err != nil { - logger.Warnf("binary.Write(req{%#v}) = err{%#v}", res, perrors.WithStack(err)) - return nil, perrors.WithStack(err) - } - return buf.Bytes(), nil - } - - logger.Errorf("illegal pkg:%+v\n, it is %+v", pkg, reflect.TypeOf(pkg)) - return nil, perrors.New("invalid rpc response") -} - -// OnTripleData handle triple rpc invocation -func (dcm *DubboProxyConnectionManager) OnTripleData(ctx context.Context, methodName string, arguments []interface{}) (interface{}, error) { - dubboAttachment := make(map[string]interface{}) - md, ok := metadata.FromIncomingContext(ctx) - if ok { - for k := range md { - dubboAttachment[k] = md.Get(k)[0] - } - } - interfaceName := dubboAttachment[constant.InterfaceKey].(string) - - ra, err := dcm.routerCoordinator.RouteByPathAndName(interfaceName, methodName) - - if err != nil { - return nil, errors.Errorf("Requested dubbo rpc invocation route not found") - } - - len := len(arguments) - inVArr := make([]reflect.Value, len) - for i := 0; i < len; i++ { - inVArr[i] = reflect.ValueOf(arguments[i]) - } - // old invocation can't set parameterValues - invoc := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName(methodName), - invocation.WithArguments(arguments), - invocation.WithParameterValues(inVArr), - invocation.WithAttachments(dubboAttachment)) - - rpcContext := &dubbo2.RpcContext{} - rpcContext.SetInvocation(invoc) - rpcContext.SetRoute(ra) - dcm.handleRpcInvocation(rpcContext) - result := rpcContext.RpcResult - return result, result.Error() -} - -// OnData handle dubbo rpc invocation -func (dcm *DubboProxyConnectionManager) OnData(data interface{}) (interface{}, error) { - old_invoc, ok := data.(*invocation.RPCInvocation) - if !ok { - panic("create invocation occur some exception for the type is not suitable one.") - } - // need reconstruct RPCInvocation ParameterValues witch is same with arguments. refer to dubbogo/common/proxy/proxy.makeDubboCallProxy - arguments := old_invoc.Arguments() - len := len(arguments) - inVArr := make([]reflect.Value, len) - for i := 0; i < len; i++ { - inVArr[i] = reflect.ValueOf(arguments[i]) - } - //old invocation can't set parameterValues - invoc := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName(old_invoc.MethodName()), - invocation.WithArguments(old_invoc.Arguments()), - invocation.WithCallBack(old_invoc.CallBack()), invocation.WithParameterValues(inVArr), - invocation.WithAttachments(old_invoc.Attachments())) - - path, _ := old_invoc.GetAttachmentInterface(dubboConstant.PathKey).(string) - ra, err := dcm.routerCoordinator.RouteByPathAndName(path, old_invoc.MethodName()) - - if err != nil { - return nil, errors.Errorf("Requested dubbo rpc invocation route not found") - } - - ctx := &dubbo2.RpcContext{} - ctx.SetRoute(ra) - ctx.SetInvocation(invoc) - dcm.handleRpcInvocation(ctx) - result := ctx.RpcResult - return result, nil -} - -// handleRpcInvocation handle rpc request -func (dcm *DubboProxyConnectionManager) handleRpcInvocation(c *dubbo2.RpcContext) { - filterChain := dcm.filterManager.filters - - // recover any err when filterChain run - defer func() { - if err := recover(); err != nil { - logger.Warnf("[dubbopixiu go] Occur An Unexpected Err: %+v", err) - c.SetError(errors.Errorf("Occur An Unexpected Err: %v", err)) - } - }() - - for _, f := range filterChain { - status := f.Handle(c) - switch status { - case filter.Continue: - continue - case filter.Stop: - return - } - } -} diff --git a/pixiu/pkg/filter/network/dubboproxy/plugin.go b/pixiu/pkg/filter/network/dubboproxy/plugin.go deleted file mode 100644 index a003e5dbf..000000000 --- a/pixiu/pkg/filter/network/dubboproxy/plugin.go +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 dubboproxy - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/util/stringutil" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -const ( - Kind = constant.DubboConnectManagerFilter -) - -func init() { - filter.RegisterNetworkFilterPlugin(&Plugin{}) -} - -type ( - // Plugin the dubbo networkfilter plugin - Plugin struct{} -) - -// Kind kind -func (p *Plugin) Kind() string { - return Kind -} - -// CreateFilter create dubbo networkfilter -func (p *Plugin) CreateFilter(config interface{}) (filter.NetworkFilter, error) { - hcmc, ok := config.(*model.DubboProxyConnectionManagerConfig) - hcmc.Timeout = stringutil.ResolveTimeStr2Time(hcmc.TimeoutStr, constant.DefaultReqTimeout) - if !ok { - panic("CreateFilter occur some exception for the type is not suitable one.") - } - return CreateDubboProxyConnectionManager(hcmc), nil -} - -// Config return DubboProxyConnectionManagerConfig -func (p *Plugin) Config() interface{} { - return &model.DubboProxyConnectionManagerConfig{} -} diff --git a/pixiu/pkg/filter/network/grpcconnectionmanager/plugin.go b/pixiu/pkg/filter/network/grpcconnectionmanager/plugin.go deleted file mode 100644 index 20ce5689a..000000000 --- a/pixiu/pkg/filter/network/grpcconnectionmanager/plugin.go +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 grpcconnectionmanager - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/grpc" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/util/stringutil" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -const ( - Kind = constant.GRPCConnectManagerFilter -) - -func init() { - filter.RegisterNetworkFilterPlugin(&Plugin{}) -} - -type ( - Plugin struct{} -) - -// Kind kind -func (p *Plugin) Kind() string { - return Kind -} - -// CreateFilter create grpc network filter -func (p *Plugin) CreateFilter(config interface{}) (filter.NetworkFilter, error) { - hcmc := config.(*model.GRPCConnectionManagerConfig) - hcmc.Timeout = stringutil.ResolveTimeStr2Time(hcmc.TimeoutStr, constant.DefaultReqTimeout) - return grpc.CreateGrpcConnectionManager(hcmc), nil -} - -// Config return GRPCConnectionManagerConfig -func (p *Plugin) Config() interface{} { - return &model.GRPCConnectionManagerConfig{} -} diff --git a/pixiu/pkg/filter/network/httpconnectionmanager/plugin.go b/pixiu/pkg/filter/network/httpconnectionmanager/plugin.go deleted file mode 100644 index 3c0758ef4..000000000 --- a/pixiu/pkg/filter/network/httpconnectionmanager/plugin.go +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 httpconnectionmanager - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/util/stringutil" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -const ( - Kind = constant.HTTPConnectManagerFilter -) - -func init() { - filter.RegisterNetworkFilterPlugin(&Plugin{}) -} - -type ( - Plugin struct{} -) - -// Kind -func (p *Plugin) Kind() string { - return Kind -} - -// CreateFilter create http network filter -func (p *Plugin) CreateFilter(config interface{}) (filter.NetworkFilter, error) { - hcmc := config.(*model.HttpConnectionManagerConfig) - hcmc.Timeout = stringutil.ResolveTimeStr2Time(hcmc.TimeoutStr, constant.DefaultReqTimeout) - return http.CreateHttpConnectionManager(hcmc), nil -} - -// Config return HttpConnectionManagerConfig -func (p *Plugin) Config() interface{} { - return &model.HttpConnectionManagerConfig{} -} diff --git a/pixiu/pkg/filter/prometheus/metric.go b/pixiu/pkg/filter/prometheus/metric.go deleted file mode 100644 index a4ce04014..000000000 --- a/pixiu/pkg/filter/prometheus/metric.go +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 prometheus - -import ( - stdHttp "net/http" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - contextHttp "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - prom "github.com/apache/dubbo-go-pixiu/pixiu/pkg/prometheus" -) - -const ( - Kind = constant.HTTPPrometheusMetricFilter -) - -func init() { - filter.RegisterHttpFilter(&Plugin{}) -} - -type ( - Plugin struct { - } - - FilterFactory struct { - Cfg *MetricCollectConfiguration - Prom *prom.Prometheus - } - - Filter struct { - Cfg *MetricCollectConfiguration - Prom *prom.Prometheus - } -) - -func (p Plugin) Kind() string { - return Kind -} - -func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { - - return &FilterFactory{ - Cfg: &MetricCollectConfiguration{}, - Prom: prom.NewPrometheus(), - }, nil -} - -func (factory *FilterFactory) Config() interface{} { - - return factory.Cfg -} - -func (factory *FilterFactory) Apply() error { - - return nil -} - -func (factory *FilterFactory) PrepareFilterChain(ctx *contextHttp.HttpContext, chain filter.FilterChain) error { - - f := &Filter{ - Cfg: factory.Cfg, - Prom: factory.Prom, - } - f.Prom.SetPushGatewayUrl(f.Cfg.Rules.PushGatewayURL, f.Cfg.Rules.MetricPath) - f.Prom.SetPushIntervalThreshold(f.Cfg.Rules.CounterPush, f.Cfg.Rules.PushIntervalThreshold) - f.Prom.SetPushGatewayJob(f.Cfg.Rules.PushJobName) - chain.AppendDecodeFilters(f) - return nil -} - -func (f *Filter) Decode(ctx *contextHttp.HttpContext) filter.FilterStatus { - - if f.Cfg == nil { - logger.Errorf("Message:Filter Metric Collect Configuration is null") - ctx.SendLocalReply(stdHttp.StatusForbidden, constant.Default403Body) - return filter.Continue - } - if f.Prom == nil { - logger.Errorf("Message:Prometheus Collector is not initialized") - ctx.SendLocalReply(stdHttp.StatusForbidden, constant.Default403Body) - return filter.Continue - } - if f.Cfg.Rules.CounterPush && f.Cfg.Rules.PushIntervalThreshold == 0 { - ctx.SendLocalReply(stdHttp.StatusForbidden, constant.Default403Body) - return filter.Continue - } - start := f.Prom.HandlerFunc() - err := start(ctx) - if err != nil { - logger.Errorf("Message:Context HandlerFunc error") - ctx.SendLocalReply(stdHttp.StatusForbidden, constant.Default403Body) - } - return filter.Continue -} diff --git a/pixiu/pkg/filter/prometheus/metric_test.go b/pixiu/pkg/filter/prometheus/metric_test.go deleted file mode 100644 index c92949fa2..000000000 --- a/pixiu/pkg/filter/prometheus/metric_test.go +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 prometheus - -import ( - "bytes" - "encoding/json" - "net/http" - "testing" - "time" -) - -import ( - "github.com/stretchr/testify/assert" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/yaml" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/mock" -) - -func TestCounterExporterApiMetric(t *testing.T) { - rules := MetricCollectConfiguration{ - MetricCollectRule{ - MetricPath: "/metrics", - PushGatewayURL: "http://127.0.0.1:9091", - PushJobName: "pixiu", - CounterPush: true, - PushIntervalThreshold: 100, - }, - } - _, err := yaml.MarshalYML(rules) - assert.Nil(t, err) - config := &rules - p := Plugin{} - msg := "this is test msg" - metricFilterFactory, _ := p.CreateFilterFactory() - if factory, ok := metricFilterFactory.(*FilterFactory); ok { - factory.Cfg = config - err = factory.Apply() - assert.Nil(t, err) - chain := filter.NewDefaultFilterChain() - for i := 0; i < 1000; i++ { - data := GetApiStatsResponse() - body, _ := json.Marshal(&data) - request, _ := http.NewRequest("POST", "/_api/health", bytes.NewBuffer(body)) - ctx := mock.GetMockHTTPContext(request) - ctx.TargetResp = client.NewResponse([]byte(msg)) - err := factory.PrepareFilterChain(ctx, chain) - assert.Nil(t, err) - chain.OnDecode(ctx) - } - } - time.Sleep(20 * time.Second) -} - -func GetApiStatsResponse() ApiStatsResponse { - return ApiStatsResponse{ - ApiStats: []ApiStat{ - { - ApiName: "api1", - ApiRequests: 1000, - }, - }, - } -} - -type ApiStatsResponse struct { - ApiStats []ApiStat `json:"api_stats"` -} - -type ApiStat struct { - ApiName string `json:"api_name"` - ApiRequests int64 `json:"api_requests"` -} diff --git a/pixiu/pkg/filter/seata/context.go b/pixiu/pkg/filter/seata/context.go deleted file mode 100644 index 947ef354f..000000000 --- a/pixiu/pkg/filter/seata/context.go +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 seata - -import ( - "bytes" - "encoding/json" - "net/http" -) - -import ( - "vimagination.zapto.org/byteio" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" -) - -var ( - VarHost = "host" - VarQueryString = "queryString" -) - -type RequestContext struct { - ActionContext map[string]string - Headers http.Header - Body []byte - Trailers http.Header -} - -func (ctx *RequestContext) Encode() ([]byte, error) { - var ( - err error - actionContextData []byte - headersData []byte - trailersData []byte - b bytes.Buffer - ) - - w := byteio.BigEndianWriter{Writer: &b} - - if ctx.ActionContext == nil || len(ctx.ActionContext) == 0 { - w.WriteUint32(0) - } else { - actionContextData, err = json.Marshal(ctx.ActionContext) - if err != nil { - return nil, err - } - - w.WriteUint32(uint32(len(actionContextData))) - w.Write(actionContextData) - } - - if ctx.Headers == nil || len(ctx.Headers) == 0 { - w.WriteUint32(0) - } else { - headersData, err = json.Marshal(ctx.Headers) - if err != nil { - return nil, err - } - w.WriteUint32(uint32(len(headersData))) - w.Write(headersData) - } - - if ctx.Trailers == nil || len(ctx.Trailers) == 0 { - w.WriteUint32(0) - } else { - trailersData, err = json.Marshal(ctx.Trailers) - if err != nil { - return nil, err - } - w.WriteUint32(uint32(len(trailersData))) - w.Write(trailersData) - } - - w.WriteUint32(uint32(len(ctx.Body))) - b.Write(ctx.Body) - - return b.Bytes(), nil -} - -func (ctx *RequestContext) Decode(b []byte) error { - var ( - actionContextData []byte - headersData []byte - bodyData []byte - trailersData []byte - ) - r := byteio.BigEndianReader{Reader: bytes.NewReader(b)} - - contextLength, _, err := r.ReadUint32() - if err != nil { - return err - } - if contextLength > 0 { - actionContextData = make([]byte, contextLength, contextLength) - r.Read(actionContextData) - } - - headerLength, _, err := r.ReadUint32() - if err != nil { - return err - } - if headerLength > 0 { - headersData = make([]byte, headerLength, headerLength) - r.Read(headersData) - } - - trailersLength, _, err := r.ReadUint32() - if err != nil { - return err - } - if trailersLength > 0 { - trailersData = make([]byte, trailersLength, trailersLength) - r.Read(trailersData) - } - - bodyLength, _, err := r.ReadUint32() - if err != nil { - return err - } - if bodyLength > 0 { - bodyData = make([]byte, bodyLength, bodyLength) - r.Read(bodyData) - } - - if actionContextData != nil { - err = json.Unmarshal(actionContextData, &(ctx.ActionContext)) - if err != nil { - logger.Errorf("unmarshal action context failed, %v", err) - } - } - - if headersData != nil { - err = json.Unmarshal(headersData, &(ctx.Headers)) - if err != nil { - logger.Errorf("unmarshal headers failed, %v", err) - } - } - - if trailersData != nil { - err = json.Unmarshal(trailersData, &(ctx.ActionContext)) - if err != nil { - logger.Errorf("unmarshal trailers failed, %v", err) - } - } - - ctx.Body = bodyData - return nil -} diff --git a/pixiu/pkg/filter/seata/filter.go b/pixiu/pkg/filter/seata/filter.go deleted file mode 100644 index 264b5c67a..000000000 --- a/pixiu/pkg/filter/seata/filter.go +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 seata - -import ( - netHttp "net/http" - "strings" -) - -import ( - "github.com/opentrx/seata-golang/v2/pkg/apis" - "github.com/opentrx/seata-golang/v2/pkg/util/runtime" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" -) - -const ( - Kind = "dgp.filter.http.seata" - - SEATA = "seata" - XID = "x_seata_xid" - BranchID = "x_seata_branch_id" -) - -func init() { - filter.RegisterHttpFilter(&Plugin{}) -} - -type ( - // MetricFilter is http Filter plugin. - Plugin struct { - } - // FilterFactory is http Filter instance - FilterFactory struct { - conf *Seata - transactionInfos map[string]*TransactionInfo - tccResources map[string]*TCCResource - transactionClient apis.TransactionManagerServiceClient - resourceClient apis.ResourceManagerServiceClient - branchMessages chan *apis.BranchMessage - } - // Filter is http Filter instance - Filter struct { - conf *Seata - transactionInfos map[string]*TransactionInfo - tccResources map[string]*TCCResource - transactionClient apis.TransactionManagerServiceClient - resourceClient apis.ResourceManagerServiceClient - branchMessages chan *apis.BranchMessage - - globalResult bool - branchRegisterResult bool - } -) - -func (ap *Plugin) Kind() string { - return Kind -} - -func (ap *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { - return &FilterFactory{ - conf: &Seata{}, - transactionInfos: make(map[string]*TransactionInfo), - tccResources: make(map[string]*TCCResource), - branchMessages: make(chan *apis.BranchMessage), - }, nil -} - -func (factory *FilterFactory) Config() interface{} { - return factory.conf -} - -func (factory *FilterFactory) Apply() error { - conn, err := grpc.Dial(factory.conf.ServerAddressing, - grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.WithKeepaliveParams(factory.conf.GetClientParameters())) - if err != nil { - return err - } - - factory.transactionClient = apis.NewTransactionManagerServiceClient(conn) - factory.resourceClient = apis.NewResourceManagerServiceClient(conn) - - runtime.GoWithRecover(func() { - factory.branchCommunicate() - }, nil) - - for _, ti := range factory.conf.TransactionInfos { - factory.transactionInfos[ti.RequestPath] = ti - } - - for _, r := range factory.conf.TCCResources { - factory.tccResources[r.PrepareRequestPath] = r - } - return nil -} - -func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error { - f := &Filter{ - conf: factory.conf, - transactionInfos: factory.transactionInfos, - tccResources: factory.tccResources, - transactionClient: factory.transactionClient, - resourceClient: factory.resourceClient, - branchMessages: factory.branchMessages, - } - chain.AppendDecodeFilters(f) - return nil -} - -func (f *Filter) Decode(ctx *http.HttpContext) filter.FilterStatus { - path := ctx.Request.URL.Path - method := ctx.Request.Method - - if method != netHttp.MethodPost { - return filter.Continue - } - - transactionInfo, found := f.transactionInfos[strings.ToLower(path)] - if found { - f.globalResult = f.handleHttp1GlobalBegin(ctx, transactionInfo) - } - - tccResource, exists := f.tccResources[strings.ToLower(path)] - if exists { - f.branchRegisterResult = f.handleHttp1BranchRegister(ctx, tccResource) - } - return filter.Continue -} - -func (f *Filter) Encode(ctx *http.HttpContext) filter.FilterStatus { - if f.globalResult { - f.handleHttp1GlobalEnd(ctx) - } - - if f.branchRegisterResult { - f.handleHttp1BranchEnd(ctx) - } - return filter.Continue -} diff --git a/pixiu/pkg/filter/sentinel/ratelimit/config.go b/pixiu/pkg/filter/sentinel/ratelimit/config.go deleted file mode 100644 index 3241c2316..000000000 --- a/pixiu/pkg/filter/sentinel/ratelimit/config.go +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 ratelimit - -import ( - "github.com/alibaba/sentinel-golang/core/flow" -) - -import ( - pkgs "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/sentinel" -) - -type ( - // Config rate limit config - Config struct { - Resources []*pkgs.Resource `json:"resources,omitempty" yaml:"resources,omitempty"` - Rules []*Rule `json:"rules,omitempty" yaml:"rules,omitempty"` - LogPath string `json:"logPath,omitempty" yaml:"logPath,omitempty"` - } - - // Rule api group 's rate-limit rule - Rule struct { - ID int64 `json:"id,omitempty" yaml:"id,omitempty"` - FlowRule flow.Rule `json:"flowRule,omitempty" yaml:"flowRule,omitempty"` - Enable bool `json:"enable,omitempty" yaml:"enable,omitempty"` - } -) diff --git a/pixiu/pkg/filter/sentinel/ratelimit/matcher_test.go b/pixiu/pkg/filter/sentinel/ratelimit/matcher_test.go deleted file mode 100644 index f3555f71d..000000000 --- a/pixiu/pkg/filter/sentinel/ratelimit/matcher_test.go +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 ratelimit - -import ( - "testing" -) - -import ( - "github.com/alibaba/sentinel-golang/core/flow" - "github.com/stretchr/testify/assert" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/sentinel" -) - -func TestMatch(t *testing.T) { - config := mockConfig() - m := sentinel.NewMatcher() - m.Load(config.Resources) - - tests := []struct { - give string - matched bool - res string - }{ - { - give: "/api/v1/test-dubbo/user", - matched: true, - res: "test-dubbo", - }, - { - give: "/api/v1/test-dubbo/user/getInfo", - matched: true, - res: "test-dubbo", - }, - { - give: "/api/v1/test-dubbo/A", - matched: false, - res: "", - }, - { - give: "/api/v1/http/foo", - matched: true, - res: "test-http", - }, - { - give: "/api/v1/http/bar/1.json", - matched: true, - res: "test-http", - }, - { - give: "/api/v1/http", - matched: false, - res: "", - }, - } - for _, tt := range tests { - t.Run(tt.give, func(t *testing.T) { - res, ok := m.Match(tt.give) - assert.Equal(t, res, tt.res) - assert.Equal(t, ok, tt.matched) - }) - } -} - -func mockConfig() *Config { - c := Config{ - Resources: []*sentinel.Resource{ - { - Name: "test-dubbo", - Items: []*sentinel.Item{ - {MatchStrategy: sentinel.EXACT, Pattern: "/api/v1/test-dubbo/user"}, - {MatchStrategy: sentinel.REGEX, Pattern: "/api/v1/test-dubbo/user/*"}, - }, - }, - { - Name: "test-http", - Items: []*sentinel.Item{ - {MatchStrategy: sentinel.EXACT, Pattern: "/api/v1/http/foo"}, - {MatchStrategy: sentinel.EXACT, Pattern: "/api/v1/http/bar"}, - - {MatchStrategy: sentinel.REGEX, Pattern: "/api/v1/http/foo/*"}, - {MatchStrategy: sentinel.REGEX, Pattern: "/api/v1/http/bar/*"}, - }, - }, - }, - Rules: []*Rule{ - { - Enable: true, - FlowRule: flow.Rule{ - Threshold: 100, - StatIntervalInMs: 1000, - }, - }, - }, - } - return &c -} diff --git a/pixiu/pkg/filter/sentinel/ratelimit/mock.go b/pixiu/pkg/filter/sentinel/ratelimit/mock.go deleted file mode 100644 index de40224c7..000000000 --- a/pixiu/pkg/filter/sentinel/ratelimit/mock.go +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 ratelimit - -import ( - "github.com/alibaba/sentinel-golang/core/flow" -) - -import ( - pkgs "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/sentinel" -) - -func GetMockedRateLimitConfig() *Config { - c := Config{ - Resources: []*pkgs.Resource{ - { - Name: "test-dubbo", - Items: []*pkgs.Item{ - {MatchStrategy: pkgs.EXACT, Pattern: "/api/v1/test-dubbo/user"}, - {MatchStrategy: pkgs.REGEX, Pattern: "/api/v1/test-dubbo/user/*"}, - }, - }, - { - Name: "test-http", - Items: []*pkgs.Item{ - {MatchStrategy: pkgs.EXACT, Pattern: "/api/v1/http/foo"}, - {MatchStrategy: pkgs.EXACT, Pattern: "/api/v1/http/bar"}, - - {MatchStrategy: pkgs.REGEX, Pattern: "/api/v1/http/foo/*"}, - {MatchStrategy: pkgs.REGEX, Pattern: "/api/v1/http/bar/*"}, - }, - }, - }, - Rules: []*Rule{ - { - Enable: true, - FlowRule: flow.Rule{ - Threshold: 100, - StatIntervalInMs: 1000, - }, - }, - }, - } - return &c -} diff --git a/pixiu/pkg/filter/tracing/tracing.go b/pixiu/pkg/filter/tracing/tracing.go deleted file mode 100644 index 8bec53e69..000000000 --- a/pixiu/pkg/filter/tracing/tracing.go +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 tracing - -import ( - "go.opentelemetry.io/otel/trace" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - contexthttp "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server" -) - -const TraceIDInHeader = "dubbo-go-pixiu-trace-id" - -// nolint -func init() { - filter.RegisterHttpFilter(&Plugin{}) -} - -// tracerFilter is a filter for tracer -type ( - Plugin struct { - } - - TraceFilterFactory struct { - } - - TraceFilter struct { - span trace.Span - } - - Config struct{} -) - -func (ap *Plugin) Kind() string { - return constant.TracingFilter -} - -func (ap *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { - return &TraceFilterFactory{}, nil -} - -func (m *TraceFilterFactory) Config() interface{} { - return &Config{} -} - -func (m *TraceFilterFactory) Apply() error { - return nil -} - -func (mf *TraceFilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error { - t := &TraceFilter{} - chain.AppendDecodeFilters(t) - chain.AppendEncodeFilters(t) - return nil -} - -// Docode execute tracerFilter filter logic. -func (f *TraceFilter) Decode(hc *contexthttp.HttpContext) filter.FilterStatus { - spanName := "HTTP-" + hc.Request.Method - tr := server.GetTraceDriverManager().GetDriver().Tracer("tracing.HTTPProtocol") - ctxWithId, span := tr.Start(hc.Ctx, spanName) - - hc.Request = hc.Request.WithContext(ctxWithId) - f.span = span - return filter.Continue -} - -func (f *TraceFilter) Encode(hc *contexthttp.HttpContext) filter.FilterStatus { - f.span.End() - return filter.Continue -} diff --git a/pixiu/pkg/filter/traffic/traffic.go b/pixiu/pkg/filter/traffic/traffic.go deleted file mode 100644 index bb3553c7c..000000000 --- a/pixiu/pkg/filter/traffic/traffic.go +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 traffic - -import ( - "errors" - "fmt" - "math/rand" - "strings" - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" -) - -const ( - canaryByHeader CanaryHeaders = "canary-by-header" - unInitialize int = -1 -) - -func init() { - filter.RegisterHttpFilter(&Plugin{}) -} - -type ( - CanaryHeaders string - - Plugin struct { - } - - FilterFactory struct { - cfg *Config - record map[string]struct{} - rulesMap map[string][]*ClusterWrapper - } - - Filter struct { - weight int - Rules []*ClusterWrapper - } - - Config struct { - Traffics []*Cluster `yaml:"traffics" json:"traffics" mapstructure:"traffics"` - } - - ClusterWrapper struct { - weightCeil int - weightFloor int - header string - Cluster *Cluster - } - - Cluster struct { - Name string `yaml:"name" json:"name" mapstructure:"name"` - Router string `yaml:"router" json:"router" mapstructure:"router"` - CanaryByHeader string `yaml:"canary-by-header" json:"canary-by-header" mapstructure:"canary-by-header"` - CanaryWeight int `yaml:"canary-weight" json:"canary-weight" mapstructure:"canary-weight"` - } -) - -func (p *Plugin) Kind() string { - return constant.HTTPTrafficFilter -} - -func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { - return &FilterFactory{ - cfg: &Config{}, - record: map[string]struct{}{}, - rulesMap: map[string][]*ClusterWrapper{}, - }, nil -} - -func (factory *FilterFactory) Config() interface{} { - return factory.cfg -} - -func (factory *FilterFactory) Apply() error { - var ( - router string - up int - ) - - for _, cluster := range factory.cfg.Traffics { - up = 0 - router = cluster.Router - if len(factory.rulesMap[router]) != 0 { - lastCeil := factory.rulesMap[router][len(factory.rulesMap[router])-1].weightCeil - if lastCeil != -1 { - up = lastCeil - } - } - - wp := &ClusterWrapper{ - Cluster: cluster, - weightCeil: -1, - weightFloor: -1, - } - if cluster.CanaryByHeader != "" { - if _, ok := factory.record[cluster.CanaryByHeader]; ok { - return errors.New("duplicate canary-by-header values") - } else { - factory.record[cluster.CanaryByHeader] = struct{}{} - wp.header = cluster.CanaryByHeader - } - } - if cluster.CanaryWeight > 0 && cluster.CanaryWeight <= 100 { - if up+cluster.CanaryWeight > 100 { - return fmt.Errorf("[dubbo-go-pixiu] clusters' weight sum more than 100 in %v service!", cluster.Router) - } else { - wp.weightFloor = up - up += cluster.CanaryWeight - wp.weightCeil = up - } - } - factory.rulesMap[router] = append(factory.rulesMap[router], wp) - } - return nil -} - -func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error { - f := &Filter{ - weight: unInitialize, - } - f.Rules = factory.rulesMatch(ctx.Request.RequestURI) - chain.AppendDecodeFilters(f) - return nil -} - -func (f *Filter) Decode(ctx *http.HttpContext) filter.FilterStatus { - if f.Rules != nil { - var cluster string - for _, wp := range f.Rules { - if f.trafficHeader(wp, ctx) { - cluster = wp.Cluster.Name - logger.Debugf("[dubbo-go-pixiu] execute traffic split to cluster %s", wp.Cluster.Name) - break - } - } - if cluster != "" { - ctx.Route.Cluster = cluster - } else if cluster == "" { - for _, wp := range f.Rules { - if f.trafficWeight(wp) { - ctx.Route.Cluster = wp.Cluster.Name - cluster = wp.Cluster.Name - logger.Debugf("[dubbo-go-pixiu] execute traffic split to cluster %s", cluster) - break - } - } - } - } else { - logger.Warnf("[dubbo-go-pixiu] execute traffic split fail because of empty rules.") - } - return filter.Continue -} - -func (f *Filter) trafficHeader(c *ClusterWrapper, ctx *http.HttpContext) bool { - return spiltHeader(ctx.Request, c.header) -} - -func (f *Filter) trafficWeight(c *ClusterWrapper) bool { - if f.weight == unInitialize { - rand.Seed(time.Now().UnixNano()) - f.weight = rand.Intn(100) + 1 - } - - return spiltWeight(f.weight, c.weightFloor, c.weightCeil) -} - -func (factory *FilterFactory) rulesMatch(path string) []*ClusterWrapper { - for key := range factory.rulesMap { - if strings.HasPrefix(path, key) { - return factory.rulesMap[key] - } - } - return nil -} diff --git a/pixiu/pkg/listener/listener.go b/pixiu/pkg/listener/listener.go deleted file mode 100644 index 01b784601..000000000 --- a/pixiu/pkg/listener/listener.go +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 listener - -import ( - "sync/atomic" -) - -import ( - "github.com/pkg/errors" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filterchain" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -var factoryMap = make(map[model.ProtocolType]func(lc *model.Listener, bs *model.Bootstrap) (ListenerService, error), 8) - -type ( - ListenerService interface { - // Start the listener service - Start() error - // Close the listener service forcefully - Close() error - // ShutDown gracefully shuts down the listener. - ShutDown(interface{}) error - // Refresh config - Refresh(model.Listener) error - } - - BaseListenerService struct { - Config *model.Listener - FilterChain *filterchain.NetworkFilterChain - } - - ListenerGracefulShutdownConfig struct { - ActiveCount int32 - RejectRequest bool - } -) - -// SetListenerServiceFactory will store the listenerService factory by name -func SetListenerServiceFactory(protocol model.ProtocolType, newRegFunc func(lc *model.Listener, bs *model.Bootstrap) (ListenerService, error)) { - factoryMap[protocol] = newRegFunc -} - -// CreateListenerService create listener service -func CreateListenerService(lc *model.Listener, bs *model.Bootstrap) (ListenerService, error) { - if registry, ok := factoryMap[lc.Protocol]; ok { - reg, err := registry(lc, bs) - if err != nil { - panic("Initialize ListenerService " + lc.Name + "failed due to: " + err.Error()) - } - return reg, nil - } - return nil, errors.New("Registry " + lc.ProtocolStr + " does not support yet") -} - -func (lgsc *ListenerGracefulShutdownConfig) AddActiveCount(num int32) { - atomic.AddInt32(&lgsc.ActiveCount, num) -} diff --git a/pixiu/pkg/logger/logger.go b/pixiu/pkg/logger/logger.go deleted file mode 100644 index bbd5d4913..000000000 --- a/pixiu/pkg/logger/logger.go +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 logger - -import ( - "fmt" - "os" - "path" - "sync" -) - -import ( - perrors "github.com/pkg/errors" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/yaml" -) - -var logger Logger - -// DubbogoPXLogger is logger struct -type DubbogoPXLogger struct { - mutex sync.Mutex - Logger - dynamicLevel zap.AtomicLevel - // disable presents the logger state. if disable is true, the logger will write nothing - // the default value is false - disable bool -} - -// Logger -type Logger interface { - Info(args ...interface{}) - Warn(args ...interface{}) - Error(args ...interface{}) - Debug(args ...interface{}) - - Infof(fmt string, args ...interface{}) - Warnf(fmt string, args ...interface{}) - Errorf(fmt string, args ...interface{}) - Debugf(fmt string, args ...interface{}) -} - -func init() { - // only use in test case, so just load default config - if logger == nil { - InitLogger(nil) - } -} - -// InitLog load from config path -func InitLog(logConfFile string) error { - if logConfFile == "" { - InitLogger(nil) - return perrors.New("log configure file name is nil") - } - if path.Ext(logConfFile) != ".yml" { - InitLogger(nil) - return perrors.New(fmt.Sprintf("log configure file name %s suffix must be .yml", logConfFile)) - } - - confFileStream, err := os.ReadFile(logConfFile) - if err != nil { - InitLogger(nil) - return perrors.New(fmt.Sprintf("os.ReadFile file:%s, error:%v", logConfFile, err)) - } - - conf := &zap.Config{} - err = yaml.UnmarshalYML(confFileStream, conf) - if err != nil { - InitLogger(nil) - return perrors.New(fmt.Sprintf("[Unmarshal]init logger error: %v", err)) - } - - InitLogger(conf) - - return nil -} - -func InitLogger(conf *zap.Config) { - var zapLoggerConfig zap.Config - if conf == nil { - zapLoggerConfig = zap.NewDevelopmentConfig() - zapLoggerEncoderConfig := zapcore.EncoderConfig{ - TimeKey: "time", - LevelKey: "level", - NameKey: "logger", - CallerKey: "caller", - MessageKey: "message", - StacktraceKey: "stacktrace", - EncodeLevel: zapcore.CapitalColorLevelEncoder, - EncodeTime: zapcore.ISO8601TimeEncoder, - EncodeDuration: zapcore.SecondsDurationEncoder, - EncodeCaller: zapcore.ShortCallerEncoder, - } - zapLoggerConfig.EncoderConfig = zapLoggerEncoderConfig - } else { - zapLoggerConfig = *conf - } - zapLogger, _ := zapLoggerConfig.Build(zap.AddCallerSkip(1)) - // logger = zapLogger.Sugar() - logger = &DubbogoPXLogger{Logger: zapLogger.Sugar(), dynamicLevel: zapLoggerConfig.Level} -} - -func SetLogger(log Logger) { - logger = log -} - -func GetLogger() Logger { - return logger -} - -func SetLoggerLevel(level string) bool { - if l, ok := logger.(OpsLogger); ok { - l.SetLoggerLevel(level) - return true - } - return false -} - -type OpsLogger interface { - Logger - // SetLoggerLevel function as name - SetLoggerLevel(level string) -} - -// SetLoggerLevel ... -func (dpl *DubbogoPXLogger) SetLoggerLevel(level string) { - l := new(zapcore.Level) - l.Set(level) - dpl.dynamicLevel.SetLevel(*l) -} diff --git a/pixiu/pkg/model/bootstrap.go b/pixiu/pkg/model/bootstrap.go deleted file mode 100644 index 1714f6852..000000000 --- a/pixiu/pkg/model/bootstrap.go +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 model - -import ( - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" -) - -// Bootstrap the door -type Bootstrap struct { - StaticResources StaticResources `yaml:"static_resources" json:"static_resources" mapstructure:"static_resources"` - DynamicResources *DynamicResources `yaml:"dynamic_resources" json:"dynamic_resources" mapstructure:"dynamic_resources"` - Metric Metric `yaml:"metric" json:"metric" mapstructure:"metric"` - Node *Node `yaml:"node" json:"node" mapstructure:"node"` - Trace *TracerConfig `yaml:"tracing" json:"tracing" mapstructure:"tracing"` - Wasm *WasmConfig `yaml:"wasm" json:"wasm" mapstructure:"wasm"` - Config *ConfigCenter `yaml:"config-center" json:"config-center" mapstructure:"config-center"` - // Third party dependency - Nacos *Nacos `yaml:"nacos" json:"nacos" mapstructure:"nacos"` -} - -// Node node info for dynamic identifier -type Node struct { - Cluster string `yaml:"cluster" json:"cluster" mapstructure:"cluster"` - Id string `yaml:"id" json:"id" mapstructure:"id"` -} - -// GetListeners -func (bs *Bootstrap) GetListeners() []*Listener { - return bs.StaticResources.Listeners -} - -func (bs *Bootstrap) GetStaticListeners() []*Listener { - return bs.StaticResources.Listeners -} - -// GetShutdownConfig -func (bs *Bootstrap) GetShutdownConfig() *ShutdownConfig { - if bs.StaticResources.ShutdownConfig == nil { - bs.StaticResources.ShutdownConfig = &ShutdownConfig{ - Timeout: "0s", - StepTimeout: "0s", - RejectPolicy: "immediacy", - } - } - return bs.StaticResources.ShutdownConfig -} - -// GetPprof -func (bs *Bootstrap) GetPprof() PprofConf { - return bs.StaticResources.PprofConf -} - -// ExistCluster -func (bs *Bootstrap) ExistCluster(name string) bool { - if len(bs.StaticResources.Clusters) > 0 { - for _, v := range bs.StaticResources.Clusters { - if v.Name == name { - return true - } - } - } - - return false -} - -// StaticResources -type StaticResources struct { - Listeners []*Listener `yaml:"listeners" json:"listeners" mapstructure:"listeners"` - Clusters []*ClusterConfig `yaml:"clusters" json:"clusters" mapstructure:"clusters"` - Adapters []*Adapter `yaml:"adapters" json:"adapters" mapstructure:"adapters"` - ShutdownConfig *ShutdownConfig `yaml:"shutdown_config" json:"shutdown_config" mapstructure:"shutdown_config"` - PprofConf PprofConf `yaml:"pprofConf" json:"pprofConf" mapstructure:"pprofConf"` -} - -// DynamicResources config the dynamic resource source -// -// "lds_config": "{...}", # config lister load source -// "cds_config": "{...}", # config cluster load source -// "ads_config": "{...}" -// "ada_config": "{...}" # config adaptor load source -type DynamicResources struct { - LdsConfig *ApiConfigSource `yaml:"lds_config" json:"lds_config" mapstructure:"lds_config"` - CdsConfig *ApiConfigSource `yaml:"cds_config" json:"cds_config" mapstructure:"cds_config"` - AdsConfig *ApiConfigSource `yaml:"ads_config" json:"ads_config" mapstructure:"ads_config"` -} - -// ShutdownConfig how to shutdown server. -type ShutdownConfig struct { - Timeout string `default:"0s" yaml:"timeout" json:"timeout,omitempty"` - StepTimeout string `default:"0s" yaml:"step_timeout" json:"step_timeout,omitempty"` - RejectPolicy string `default:"immediacy" yaml:"reject_policy" json:"reject_policy,omitempty"` -} - -// GetTimeoutOfShutdown -func (sdc *ShutdownConfig) GetTimeout() time.Duration { - result, err := time.ParseDuration(sdc.Timeout) - if err != nil { - defaultTimeout := 60 * time.Second - logger.Errorf("The Timeout configuration is invalid: %s, and we will use the default value: %s, err: %v", - sdc.Timeout, defaultTimeout.String(), err) - return defaultTimeout - } - return result -} - -// APIMetaConfig how to find api config, file or etcd etc. -type APIMetaConfig struct { - Address string `yaml:"address" json:"address,omitempty"` - APIConfigPath string `default:"/pixiu/config/api" yaml:"api_config_path" json:"api_config_path,omitempty" mapstructure:"api_config_path"` -} - -// TimeoutConfig the config of ConnectTimeout and RequestTimeout -type TimeoutConfig struct { - ConnectTimeoutStr string `default:"5s" yaml:"connect_timeout" json:"connect_timeout,omitempty"` // ConnectTimeout timeout for connect to cluster node - RequestTimeoutStr string `default:"10s" yaml:"request_timeout" json:"request_timeout,omitempty"` -} - -type Config struct { - Listeners []*Listener `yaml:"listeners" json:"listeners" mapstructure:"listeners"` - Clusters []*ClusterConfig `yaml:"clusters" json:"clusters" mapstructure:"clusters"` - Adapters []*Adapter `yaml:"adapters" json:"adapters" mapstructure:"adapters"` - ShutdownConfig *ShutdownConfig `yaml:"shutdown_config" json:"shutdown_config" mapstructure:"shutdown_config"` - PprofConf PprofConf `yaml:"pprofConf" json:"pprofConf" mapstructure:"pprofConf"` -} - -type Nacos struct { - ServerConfigs []*NacosServerConfig `yaml:"server_configs" json:"server-configs" mapstructure:"server_configs"` - ClientConfig *NacosClientConfig `yaml:"client-config" json:"client-config" mapstructure:"client_config"` - DataId string `yaml:"data-id" json:"data-id" mapstructure:"data_id" default:"pixiu.yaml"` - Group string `yaml:"group" json:"group" mapstructure:"group" default:"DEFAULT_GROUP"` -} - -type NacosServerConfig struct { - IpAddr string `json:"ip_addr,omitempty" yaml:"ip_addr" mapstructure:"ip_addr"` - Port uint64 `json:"port,omitempty" yaml:"port" mapstructure:"port"` - Scheme string `json:"scheme" yaml:"scheme" mapstructure:"scheme"` - ContextPath string `json:"contextPath" yaml:"contextPath" mapstructure:"contextPath"` -} - -type NacosClientConfig struct { - TimeoutMs uint64 `json:"timeout_ms,omitempty" yaml:"timeout_ms" mapstructure:"timeout_ms"` // timeout for requesting Nacos server, default value is 10000ms - ListenInterval uint64 `json:"listen_interval,omitempty" yaml:"listen_interval" mapstructure:"listen_interval"` // Deprecated - BeatInterval int64 `json:"beat_interval,omitempty" yaml:"beat_interval" mapstructure:"beat_interval"` // the time interval for sending beat to server,default value is 5000ms - NamespaceId string `json:"namespace_id,omitempty" yaml:"namespace_id" mapstructure:"namespace_id"` // the namespaceId of Nacos.When namespace is public, fill in the blank string here. - AppName string `json:"app_name,omitempty" yaml:"app_name" mapstructure:"app_name"` // the appName - Endpoint string `json:"endpoint,omitempty" yaml:"endpoint" mapstructure:"endpoint"` // the endpoint for get Nacos server addresses - RegionId string `json:"region_id,omitempty" yaml:"region_id" mapstructure:"region_id"` // the regionId for kms - AccessKey string `json:"access_key,omitempty" yaml:"access_key" mapstructure:"access_key"` // the AccessKey for kms - SecretKey string `json:"secret_key,omitempty" yaml:"secret_key" mapstructure:"secret_key"` // the SecretKey for kms - OpenKMS bool `json:"open_kms,omitempty" yaml:"open_kms" mapstructure:"open_kms"` // it's to open kms,default is false. https://help.aliyun.com/product/28933.html - CacheDir string `json:"cache_dir,omitempty" yaml:"cache_dir" mapstructure:"cache_dir" default:"/tmp/nacos/cache"` // the directory for persist nacos service info,default value is current path - UpdateThreadNum int `json:"update_thread_num,omitempty" yaml:"update_thread_num" mapstructure:"update_thread_num"` // the number of gorutine for update nacos service info,default value is 20 - NotLoadCacheAtStart bool `json:"not_load_cache_at_start,omitempty" yaml:"not_load_cache_at_start" mapstructure:"not_load_cache_at_start"` // not to load persistent nacos service info in CacheDir at start time - UpdateCacheWhenEmpty bool `json:"update_cache_when_empty,omitempty" yaml:"update_cache_when_empty" mapstructure:"update_cache_when_empty"` // update cache when get empty service instance from server - Username string `json:"username,omitempty" yaml:"username" mapstructure:"username"` // the username for nacos auth - Password string `json:"password,omitempty" yaml:"password" mapstructure:"password"` // the password for nacos auth - LogDir string `json:"log_dir,omitempty" yaml:"log_dir" mapstructure:"log_dir" default:"/tmp/nacos/log"` // the directory for log, default is current path - LogLevel string `json:"log_level,omitempty" yaml:"log_level" mapstructure:"log_level"` // the level of log, it's must be debug,info,warn,error, default value is info - //LogSampling *ClientLogSamplingConfig // the sampling config of log - ContextPath string `json:"context_path,omitempty" yaml:"context_path" mapstructure:"context_path"` // the nacos server contextpath - //LogRollingConfig *ClientLogRollingConfig // the log rolling config -} - -type ConfigCenter struct { - Type string `json:"type,omitempty" yaml:"type"` - Enable string `json:"enable" yaml:"enable"` -} diff --git a/pixiu/pkg/model/http.go b/pixiu/pkg/model/http.go deleted file mode 100644 index c72e469c4..000000000 --- a/pixiu/pkg/model/http.go +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 model - -import ( - "time" -) - -import ( - "github.com/mitchellh/mapstructure" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" -) - -// HttpConnectionManagerConfig -type HttpConnectionManagerConfig struct { - RouteConfig RouteConfiguration `yaml:"route_config" json:"route_config" mapstructure:"route_config"` - HTTPFilters []*HTTPFilter `yaml:"http_filters" json:"http_filters" mapstructure:"http_filters"` - ServerName string `yaml:"server_name" json:"server_name" mapstructure:"server_name"` - IdleTimeoutStr string `yaml:"idle_timeout" json:"idle_timeout" mapstructure:"idle_timeout"` - GenerateRequestID bool `yaml:"generate_request_id" json:"generate_request_id" mapstructure:"generate_request_id"` - TimeoutStr string `yaml:"timeout" json:"timeout" mapstructure:"timeout"` - Timeout time.Duration `yaml:"-" json:"-" mapstructure:"-"` -} - -// GRPCConnectionManagerConfig -type GRPCConnectionManagerConfig struct { - RouteConfig RouteConfiguration `yaml:"route_config" json:"route_config" mapstructure:"route_config"` - TimeoutStr string `yaml:"timeout" json:"timeout" mapstructure:"timeout"` - Timeout time.Duration `yaml:"-" json:"-" mapstructure:"-"` -} - -// DubboProxyConnectionManagerConfig -type DubboProxyConnectionManagerConfig struct { - RouteConfig RouteConfiguration `yaml:"route_config" json:"route_config" mapstructure:"route_config"` - DubboFilters []*DubboFilter `yaml:"dubbo_filters" json:"dubbo_filters" mapstructure:"dubbo_filters"` - TimeoutStr string `yaml:"timeout" json:"timeout" mapstructure:"timeout"` - Timeout time.Duration `yaml:"-" json:"-" mapstructure:"-"` -} - -// HTTPFilter http filter -type HTTPFilter struct { - Name string `yaml:"name" json:"name" mapstructure:"name"` - Config map[string]interface{} `yaml:"config" json:"config" mapstructure:"config"` -} - -// DubboFilter dubbo filter -type DubboFilter struct { - Name string `yaml:"name" json:"name" mapstructure:"name"` - Config map[string]interface{} `yaml:"config" json:"config" mapstructure:"config"` -} - -type RequestMethod int32 - -const ( - METHOD_UNSPECIFIED = 0 + iota // (DEFAULT) - GET - HEAD - POST - PUT - DELETE - CONNECT - OPTIONS - TRACE -) - -var RequestMethodName = map[int32]string{ - 0: "METHOD_UNSPECIFIED", - 1: "GET", - 2: "HEAD", - 3: "POST", - 4: "PUT", - 5: "DELETE", - 6: "CONNECT", - 7: "OPTIONS", - 8: "TRACE", -} - -var RequestMethodValue = map[string]int32{ - "METHOD_UNSPECIFIED": 0, - "GET": 1, - "HEAD": 2, - "POST": 3, - "PUT": 4, - "DELETE": 5, - "CONNECT": 6, - "OPTIONS": 7, - "TRACE": 8, -} - -// HttpConfig the http config -type HttpConfig struct { - IdleTimeoutStr string `yaml:"idle_timeout" json:"idle_timeout" mapstructure:"idle_timeout"` - ReadTimeoutStr string `json:"read_timeout,omitempty" yaml:"read_timeout,omitempty" mapstructure:"read_timeout"` - WriteTimeoutStr string `json:"write_timeout,omitempty" yaml:"write_timeout,omitempty" mapstructure:"write_timeout"` - MaxHeaderBytes int `json:"max_header_bytes,omitempty" yaml:"max_header_bytes,omitempty" mapstructure:"max_header_bytes"` -} - -func MapInStruct(cfg interface{}) *HttpConfig { - var hc *HttpConfig - if cfg != nil { - if ok := mapstructure.Decode(cfg, &hc); ok != nil { - logger.Error("Config error", ok) - } - } - return hc -} diff --git a/pixiu/pkg/model/router.go b/pixiu/pkg/model/router.go deleted file mode 100644 index ce41669de..000000000 --- a/pixiu/pkg/model/router.go +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 model - -import ( - stdHttp "net/http" - "regexp" -) - -import ( - "github.com/pkg/errors" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/router/trie" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/util/stringutil" -) - -// Router struct -type ( - Router struct { - ID string `yaml:"id" json:"id" mapstructure:"id"` - Match RouterMatch `yaml:"match" json:"match" mapstructure:"match"` - Route RouteAction `yaml:"route" json:"route" mapstructure:"route"` - } - - // RouterMatch - RouterMatch struct { - Prefix string `yaml:"prefix" json:"prefix" mapstructure:"prefix"` - Path string `yaml:"path" json:"path" mapstructure:"path"` - // Regex string `yaml:"regex" json:"regex" mapstructure:"regex"` TODO: next version - Methods []string `yaml:"methods" json:"methods" mapstructure:"methods"` - Headers []HeaderMatcher `yaml:"headers,omitempty" json:"headers,omitempty" mapstructure:"headers"` - // pathRE *regexp.Regexp - } - - // RouteAction match route should do - RouteAction struct { - Cluster string `yaml:"cluster" json:"cluster" mapstructure:"cluster"` - ClusterNotFoundResponseCode int `yaml:"cluster_not_found_response_code" json:"cluster_not_found_response_code" mapstructure:"cluster_not_found_response_code"` - } - - // RouteConfiguration - RouteConfiguration struct { - RouteTrie trie.Trie `yaml:"-" json:"-" mapstructure:"-"` - Routes []*Router `yaml:"routes" json:"routes" mapstructure:"routes"` - Dynamic bool `yaml:"dynamic" json:"dynamic" mapstructure:"dynamic"` - } - - // HeaderMatcher include Name header key, Values header value, Regex regex value - HeaderMatcher struct { - Name string `yaml:"name" json:"name" mapstructure:"name"` - Values []string `yaml:"values" json:"values" mapstructure:"values"` - Regex bool `yaml:"regex" json:"regex" mapstructure:"regex"` - valueRE *regexp.Regexp - } -) - -func NewRouterMatchPrefix(name string) RouterMatch { - return RouterMatch{Prefix: "/" + name + "/"} -} - -func (rc *RouteConfiguration) RouteByPathAndMethod(path, method string) (*RouteAction, error) { - if rc.RouteTrie.IsEmpty() { - return nil, errors.Errorf("router configuration is empty") - } - - node, _, _ := rc.RouteTrie.Match(stringutil.GetTrieKey(method, path)) - if node == nil { - return nil, errors.Errorf("route failed for %s, no rules matched.", stringutil.GetTrieKey(method, path)) - } - if node.GetBizInfo() == nil { - return nil, errors.Errorf("action is nil. please check your configuration.") - } - ret := (node.GetBizInfo()).(RouteAction) - - return &ret, nil -} - -func (rc *RouteConfiguration) Route(req *stdHttp.Request) (*RouteAction, error) { - return rc.RouteByPathAndMethod(req.URL.Path, req.Method) -} - -// MatchHeader used when there's only headers to match -func (rm *RouterMatch) MatchHeader(req *stdHttp.Request) bool { - if len(rm.Methods) > 0 { - for _, method := range rm.Methods { - if method == req.Method { - goto HEADER - } - } - return false - } -HEADER: - for _, header := range rm.Headers { - if val := req.Header.Get(header.Name); len(val) > 0 { - if header.MatchValues(val) { - return true - } - } - } - return false -} - -// MatchValues match values in header, including regex type -func (hm *HeaderMatcher) MatchValues(dst string) bool { - if hm.Regex && hm.valueRE != nil { - return hm.valueRE.MatchString(dst) - } - - for _, src := range hm.Values { - if src == dst { - return true - } - } - return false -} - -// SetValueRegex compile the regex, disable regex if it failed -func (hm *HeaderMatcher) SetValueRegex(regex string) error { - r, err := regexp.Compile(regex) - if err == nil { - hm.valueRE = r - return nil - } - hm.Regex = false - return err -} diff --git a/pixiu/pkg/pluginregistry/registry.go b/pixiu/pkg/pluginregistry/registry.go deleted file mode 100644 index 08e67722b..000000000 --- a/pixiu/pkg/pluginregistry/registry.go +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 pluginregistry - -import ( - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/springcloud" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/cluster/loadbalancer/maglev" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/cluster/loadbalancer/rand" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/cluster/loadbalancer/ringhash" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/cluster/loadbalancer/roundrobin" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/accesslog" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/auth/jwt" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/authority" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/cors" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/csrf" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/failinject" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/header" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/host" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/http/apiconfig" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/http/dubboproxy" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/http/grpcproxy" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/http/httpproxy" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/http/loadbalancer" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/http/proxyrewrite" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/http/remote" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/metric" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/network/dubboproxy" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/network/dubboproxy/filter/http" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/network/dubboproxy/filter/proxy" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/network/grpcconnectionmanager" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/network/httpconnectionmanager" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/prometheus" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/seata" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/tracing" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/traffic" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/listener/http" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/listener/http2" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/listener/tcp" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/listener/triple" -) diff --git a/pixiu/pkg/pool/pool.go b/pixiu/pkg/pool/pool.go deleted file mode 100644 index 4b18acf43..000000000 --- a/pixiu/pkg/pool/pool.go +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 pool - -import ( - "errors" - "sync" -) - -import ( - "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client/dubbo" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client/http" -) - -// ClientPool a pool of client. -type ClientPool struct { - poolMap map[config.RequestType]*sync.Pool -} - -var ( - clientPool *ClientPool - once = sync.Once{} -) - -func newClientPool() *ClientPool { - clientPool := &ClientPool{ - poolMap: make(map[config.RequestType]*sync.Pool, 4), - } - // init default support request type - clientPool.poolMap[config.DubboRequest] = &sync.Pool{ - New: func() interface{} { - return dubbo.NewDubboClient() - }, - } - clientPool.poolMap[config.HTTPRequest] = &sync.Pool{ - New: func() interface{} { - return http.NewHTTPClient() - }, - } - return clientPool -} - -// SingletonPool singleton pool -func SingletonPool() *ClientPool { - if clientPool == nil { - once.Do(func() { - clientPool = newClientPool() - }) - } - - return clientPool -} - -// GetClient a factory method to get a client according to apiType . -func (pool *ClientPool) GetClient(t config.RequestType) (client.Client, error) { - if pool.poolMap[t] != nil { - return pool.poolMap[t].Get().(client.Client), nil - } - return nil, errors.New("protocol not supported yet") -} - -// Put put client to pool. -func (pool *ClientPool) Put(t config.RequestType, c client.Client) error { - if pool.poolMap[t] != nil { - pool.poolMap[t].Put(c) - return nil - } - - return errors.New("protocol not supported yet") -} diff --git a/pixiu/pkg/prometheus/prometheus.go b/pixiu/pkg/prometheus/prometheus.go deleted file mode 100644 index 9ef02904d..000000000 --- a/pixiu/pkg/prometheus/prometheus.go +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 prometheus - -import ( - "bytes" - "errors" - "net/http" - "os" - "strconv" - "sync" - "time" -) - -import ( - "github.com/dubbo-go-pixiu/pixiu-api/pkg/context" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/expfmt" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client" - contextHttp "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" -) - -var defaultSubsystem = "pixiu" - -type ContextHandlerFunc func(c *contextHttp.HttpContext) error - -const ( - _ = iota // ignore first value by assigning to blank identifier - KB float64 = 1 << (10 * iota) - MB - GB - TB -) - -type FavContextKeyType string - -type Metric struct { - MetricCollector prometheus.Collector - ID string - Name string - Description string - Type string - Args []string - Buckets []float64 -} - -// reqDurBuckets is the buckets for request duration. Here, we use the prometheus defaults -// which are for ~10s request length max: []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10} -var reqDurBuckets = prometheus.DefBuckets - -// reqSzBuckets is the buckets for request size. Here we define a spectrom from 1KB thru 1NB up to 10MB. -var reqSzBuckets = []float64{1.0 * KB, 2.0 * KB, 5.0 * KB, 10.0 * KB, 100 * KB, 500 * KB, 1.0 * MB, 2.5 * MB, 5.0 * MB, 10.0 * MB} - -// resSzBuckets is the buckets for response size. Here we define a spectrom from 1KB thru 1NB up to 10MB. -var resSzBuckets = []float64{1.0 * KB, 2.0 * KB, 5.0 * KB, 10.0 * KB, 100 * KB, 500 * KB, 1.0 * MB, 2.5 * MB, 5.0 * MB, 10.0 * MB} - -// Standard default metrics -// counter, counter_vec, gauge, gauge_vec, -// histogram, histogram_vec, summary, summary_vec - -var reqCnt = &Metric{ - ID: "reqCnt", - Name: "requests_total", - Description: "How many HTTP requests processed, partitioned by status code and HTTP method.", - Type: "counter_vec", - Args: []string{"code", "method", "host", "url"}, -} - -var reqDur = &Metric{ - ID: "reqDur", - Name: "request_duration_seconds", - Description: "The HTTP request latencies in seconds.", - Args: []string{"code", "method", "url"}, - Type: "histogram_vec", - Buckets: reqDurBuckets, -} - -var resSz = &Metric{ - ID: "resSz", - Name: "response_size_bytes", - Description: "The HTTP response sizes in bytes.", - Args: []string{"code", "method", "url"}, - Type: "histogram_vec", - Buckets: resSzBuckets, -} - -var reqSz = &Metric{ - ID: "reqSz", - Name: "request_size_bytes", - Description: "The HTTP request sizes in bytes.", - Args: []string{"code", "method", "url"}, - Type: "histogram_vec", - Buckets: reqSzBuckets, -} - -var standardMetrics = []*Metric{ - reqCnt, - reqDur, - resSz, - reqSz, -} - -// NewMetric associates prometheus.Collector based on Metric.Type -func NewMetric(m *Metric, subsystem string) prometheus.Collector { - var metric prometheus.Collector - switch m.Type { - case "counter_vec": - metric = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Subsystem: subsystem, - Name: m.Name, - Help: m.Description, - }, - m.Args, - ) - case "counter": - metric = prometheus.NewCounter( - prometheus.CounterOpts{ - Subsystem: subsystem, - Name: m.Name, - Help: m.Description, - }, - ) - case "gauge_vec": - metric = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Subsystem: subsystem, - Name: m.Name, - Help: m.Description, - }, - m.Args, - ) - case "gauge": - metric = prometheus.NewGauge( - prometheus.GaugeOpts{ - Subsystem: subsystem, - Name: m.Name, - Help: m.Description, - }, - ) - case "histogram_vec": - metric = prometheus.NewHistogramVec( - prometheus.HistogramOpts{ - Subsystem: subsystem, - Name: m.Name, - Help: m.Description, - Buckets: m.Buckets, - }, - m.Args, - ) - case "histogram": - metric = prometheus.NewHistogram( - prometheus.HistogramOpts{ - Subsystem: subsystem, - Name: m.Name, - Help: m.Description, - Buckets: m.Buckets, - }, - ) - case "summary_vec": - metric = prometheus.NewSummaryVec( - prometheus.SummaryOpts{ - Subsystem: subsystem, - Name: m.Name, - Help: m.Description, - }, - m.Args, - ) - case "summary": - metric = prometheus.NewSummary( - prometheus.SummaryOpts{ - Subsystem: subsystem, - Name: m.Name, - Help: m.Description, - }, - ) - } - return metric -} - -type RequestCounterLabelMappingFunc func(c *contextHttp.HttpContext) string - -type Prometheus struct { - reqCnt *prometheus.CounterVec - reqDur, reqSz, resSz *prometheus.HistogramVec - Ppg PushGateway - - MetricsList []*Metric - MetricsPath string - Subsystem string - - RequestCounterURLLabelMappingFunc RequestCounterLabelMappingFunc - RequestCounterHostLabelMappingFunc RequestCounterLabelMappingFunc - - URLLabelFromContext string - Datacontext context.Context -} - -// PushGateway contains the configuration for pushing to a Prometheus pushgateway (optional) -type PushGateway struct { - CounterPush bool - PushIntervalSeconds time.Duration - PushIntervalThreshold int - PushGatewayURL string - Job string - counter int - mutex sync.RWMutex -} - -// NewPrometheus generates a new set of metrics with a certain subsystem name -func NewPrometheus() *Prometheus { - var metricsList []*Metric - metricsList = append(metricsList, standardMetrics...) - p := &Prometheus{ - MetricsList: metricsList, - Subsystem: defaultSubsystem, - RequestCounterURLLabelMappingFunc: func(c *contextHttp.HttpContext) string { - return c.GetUrl() - }, - RequestCounterHostLabelMappingFunc: func(c *contextHttp.HttpContext) string { - return c.Request.Host - }, - } - p.registerMetrics() - return p -} - -func (p *Prometheus) registerMetrics() { - for _, metricDef := range p.MetricsList { - metric := NewMetric(metricDef, p.Subsystem) - if err := prometheus.Register(metric); err != nil { - logger.Errorf("%s could not be registered in Prometheus: %v", metricDef.Name, err) - } - switch metricDef { - - case reqCnt: - p.reqCnt = metric.(*prometheus.CounterVec) - case reqDur: - p.reqDur = metric.(*prometheus.HistogramVec) - case resSz: - p.resSz = metric.(*prometheus.HistogramVec) - case reqSz: - p.reqSz = metric.(*prometheus.HistogramVec) - } - metricDef.MetricCollector = metric - } -} - -func (p *Prometheus) SetPushGatewayUrl(pushGatewayURL, metricspath string) { - - p.Ppg.PushGatewayURL = pushGatewayURL - p.MetricsPath = metricspath -} - -func (p *Prometheus) SetPushIntervalThreshold(isTurn bool, pushIntervalThreshold int) { - p.Ppg.CounterPush = isTurn - p.Ppg.PushIntervalThreshold = pushIntervalThreshold -} - -func (p *Prometheus) SetPushGatewayJob(j string) { - p.Ppg.Job = j -} - -func (p *Prometheus) startPushCounter() { - if p.Ppg.counter >= p.Ppg.PushIntervalThreshold { - go p.sendMetricsToPushGateway(p.getMetrics()) - p.Ppg.counter = 0 - } -} - -func (p *Prometheus) SetPushGateway() { - if p.Ppg.CounterPush { - p.startPushCounter() - } -} - -func (p *Prometheus) getMetrics() []byte { - out := &bytes.Buffer{} - metricFamilies, err := prometheus.DefaultGatherer.Gather() - if err != nil { - logger.Errorf("prometheus.DefaultGatherer.Gather error: %v", err) - return []byte{} - } - for i := range metricFamilies { - _, err := expfmt.MetricFamilyToText(out, metricFamilies[i]) - if err != nil { - logger.Errorf("failed to converts a MetricFamily proto message into text format %v", err) - } - } - return out.Bytes() -} - -func (p *Prometheus) sendMetricsToPushGateway(metrics []byte) { - req, err := http.NewRequest(http.MethodPost, p.getPushGatewayURL(), bytes.NewBuffer(metrics)) - if err != nil { - logger.Errorf("failed to create push gateway request: %v", err) - return - } - if _, err = (&http.Client{}).Do(req); err != nil { - logger.Errorf("Error sending to push gateway: %v", err) - } -} - -func (p *Prometheus) getPushGatewayURL() string { - h, _ := os.Hostname() - if p.Ppg.Job == "" { - p.Ppg.Job = "pixiu" - } - return p.Ppg.PushGatewayURL + p.MetricsPath + "/job/" + p.Ppg.Job + "/instance/" + h -} - -// HandlerFunc defines handler function for middleware -func (p *Prometheus) HandlerFunc() ContextHandlerFunc { - return func(c *contextHttp.HttpContext) error { - start := time.Now() - reqSz, err1 := computeApproximateRequestSize(c.Request) - //fmt.Println("reqSz", reqSz) - elapsed := float64(time.Since(start)) / float64(time.Second) - //fmt.Println("elapsed ", elapsed) - url := p.RequestCounterURLLabelMappingFunc(c) - //fmt.Println("url ", url) - statusStr := strconv.Itoa(c.GetStatusCode()) - //fmt.Println("statusStr", statusStr) - method := c.GetMethod() - //fmt.Println("method ", method) - p.reqDur.WithLabelValues(statusStr, method, url).Observe(elapsed) - p.reqCnt.WithLabelValues(statusStr, method, p.RequestCounterHostLabelMappingFunc(c), url).Inc() - if err1 == nil { - p.reqSz.WithLabelValues(statusStr, method, url).Observe(float64(reqSz)) - } - resSz, err2 := computeApproximateResponseSize(c.TargetResp) - if err2 == nil { - p.resSz.WithLabelValues(statusStr, method, url).Observe(float64(resSz)) - } - p.Ppg.mutex.Lock() - p.Ppg.counter = p.Ppg.counter + 1 - defer p.Ppg.mutex.Unlock() - p.SetPushGateway() - return nil - } -} - -func computeApproximateRequestSize(r *http.Request) (int, error) { - if r == nil { - return 0, errors.New("http.Request is null pointer ") - } - s := 0 - if r.URL != nil { - s = len(r.URL.Path) - } - s += len(r.Method) - s += len(r.Proto) - for name, values := range r.Header { - s += len(name) - for _, value := range values { - s += len(value) - } - } - s += len(r.Host) - if r.ContentLength != -1 { - s += int(r.ContentLength) - } - return s, nil -} - -func computeApproximateResponseSize(res *client.Response) (int, error) { - if res == nil { - return 0, errors.New("client.Response is null pointer ") - } - return len(res.Data), nil -} diff --git a/pixiu/pkg/remote/nacos/client.go b/pixiu/pkg/remote/nacos/client.go deleted file mode 100644 index 2d75beed1..000000000 --- a/pixiu/pkg/remote/nacos/client.go +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 nacos - -import ( - "net" - "strconv" - "strings" - "time" -) - -import ( - "github.com/nacos-group/nacos-sdk-go/clients" - "github.com/nacos-group/nacos-sdk-go/clients/naming_client" - "github.com/nacos-group/nacos-sdk-go/common/constant" - model2 "github.com/nacos-group/nacos-sdk-go/model" - "github.com/nacos-group/nacos-sdk-go/vo" - perrors "github.com/pkg/errors" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -type NacosClient struct { - namingClient naming_client.INamingClient -} - -func (client *NacosClient) GetAllServicesInfo(param vo.GetAllServiceInfoParam) (model2.ServiceList, error) { - return client.namingClient.GetAllServicesInfo(param) -} - -func (client *NacosClient) SelectInstances(param vo.SelectInstancesParam) ([]model2.Instance, error) { - return client.namingClient.SelectInstances(param) -} - -func (client *NacosClient) Subscribe(param *vo.SubscribeParam) error { - return client.namingClient.Subscribe(param) -} - -func (client *NacosClient) Unsubscribe(param *vo.SubscribeParam) error { - return client.namingClient.Unsubscribe(param) -} - -func NewNacosClient(config *model.RemoteConfig) (*NacosClient, error) { - configMap := make(map[string]interface{}, 2) - addresses := strings.Split(config.Address, ",") - serverConfigs := make([]constant.ServerConfig, 0, len(addresses)) - for _, addr := range addresses { - ip, portStr, err := net.SplitHostPort(addr) - if err != nil { - return nil, perrors.WithMessagef(err, "split [%s] ", addr) - } - port, _ := strconv.Atoi(portStr) - serverConfigs = append(serverConfigs, constant.ServerConfig{ - IpAddr: ip, - Port: uint64(port), - }) - } - configMap["serverConfigs"] = serverConfigs - - duration, _ := time.ParseDuration(config.Timeout) - client, err := clients.NewNamingClient( - vo.NacosClientParam{ - ClientConfig: constant.NewClientConfig( - constant.WithTimeoutMs(uint64(duration.Milliseconds())), - constant.WithNotLoadCacheAtStart(true), - constant.WithUpdateCacheWhenEmpty(true), - ), - ServerConfigs: serverConfigs, - }, - ) - - if err != nil { - return nil, perrors.WithMessagef(err, "nacos client create error") - } - return &NacosClient{client}, nil -} diff --git a/pixiu/pkg/remote/zookeeper/client.go b/pixiu/pkg/remote/zookeeper/client.go deleted file mode 100644 index 1d42b084a..000000000 --- a/pixiu/pkg/remote/zookeeper/client.go +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 zookeeper - -import ( - "strings" - "sync" - "time" -) - -import ( - gxzookeeper "github.com/dubbogo/gost/database/kv/zk" - perrors "github.com/pkg/errors" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -func ValidateZookeeperClient(container ZkClientFacade, zkName string) error { - lock := container.ZkClientLock() - config := container.GetConfig() - - lock.Lock() - defer lock.Unlock() - - if container.ZkClient() == nil { - newClient, cltErr := NewZkClient(zkName, config) - if cltErr != nil { - return perrors.WithMessagef(cltErr, "create zookeeper client (address:%+v)", config.Address) - } - container.SetZkClient(newClient) - } - return nil -} - -func NewZkClient(zkName string, config *model.RemoteConfig) (*gxzookeeper.ZookeeperClient, error) { - var ( - zkAddresses = strings.Split(config.Address, ",") - ) - timeout, _ := time.ParseDuration(config.Timeout) - - newClient, cltErr := gxzookeeper.NewZookeeperClient(zkName, zkAddresses, true, gxzookeeper.WithZkTimeOut(timeout)) - if cltErr != nil { - logger.Warnf("newZookeeperClient(name{%s}, zk address{%v}, timeout{%d}) = error{%v}", - zkName, config.Address, timeout.String(), cltErr) - return nil, perrors.WithMessagef(cltErr, "newZookeeperClient(address:%+v)", config.Address) - } - return newClient, nil -} - -type Listener interface { - // Close closes this listener - Close() - // WatchAndHandle watch the target path and handle the event - WatchAndHandle() -} - -type ZkClientFacade interface { - Name() string - ZkClient() *gxzookeeper.ZookeeperClient - SetZkClient(*gxzookeeper.ZookeeperClient) - ZkClientLock() *sync.Mutex - WaitGroup() *sync.WaitGroup // for wait group control, zk client listener & zk client container - Done() chan struct{} // for registry destroy - RestartCallBack() bool - GetConfig() *model.RemoteConfig -} - -// HandleClientRestart keeps the connection between client and server -// This method should be used only once. You can use handleClientRestart() in package registry. -func HandleClientRestart(r ZkClientFacade) { - defer r.WaitGroup().Done() - for { - select { - case <-r.ZkClient().Reconnect(): - r.RestartCallBack() - time.Sleep(10 * time.Microsecond) - case <-r.Done(): - logger.Warnf("receive registry destroy event, quit client restart handler") - return - } - } -} diff --git a/pixiu/pkg/router/api.go b/pixiu/pkg/router/api.go deleted file mode 100644 index ae4ea5181..000000000 --- a/pixiu/pkg/router/api.go +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 router - -import ( - "net/url" - "strings" -) - -import ( - "github.com/dubbo-go-pixiu/pixiu-api/pkg/router" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" -) - -// GetURIParams returns the values retrieved from the rawURL -func GetURIParams(api *router.API, rawURL url.URL) url.Values { - return wildcardMatch(api.URLPattern, rawURL.Path) -} - -// IsWildCardBackendPath checks whether the configured path of -// the upstream restful service contains parameters -func IsWildCardBackendPath(api *router.API) bool { - if len(api.IntegrationRequest.Path) == 0 { - return false - } - return strings.Contains(api.IntegrationRequest.Path, constant.PathParamIdentifier) -} diff --git a/pixiu/pkg/router/route.go b/pixiu/pkg/router/route.go deleted file mode 100644 index 5079d0272..000000000 --- a/pixiu/pkg/router/route.go +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 router - -import ( - "net/url" - "strings" - "sync" -) - -import ( - "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" - "github.com/dubbo-go-pixiu/pixiu-api/pkg/router" - "github.com/pkg/errors" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/router/trie" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/util/stringutil" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" -) - -// Node defines the single method of the router configured API -type Node struct { - fullPath string - filters []string - method *config.Method - headers map[string]string -} - -// Route defines the tree of router APIs -type Route struct { - lock sync.RWMutex - tree trie.Trie -} - -// ClearAPI clear the api -func (rt *Route) ClearAPI() error { - rt.lock.Lock() - defer rt.lock.Unlock() - rt.tree.Clear() - return nil -} - -func (rt *Route) RemoveAPI(api router.API) { - lowerCasePath := strings.ToLower(api.URLPattern) - key := getTrieKey(api.Method.HTTPVerb, lowerCasePath, false) - - rt.lock.Lock() - defer rt.lock.Unlock() - // if api is exists - if exists, err := rt.tree.Contains(key); err != nil { - logger.Errorf("rt.tree.Get(key) err: %s", err.Error()) - return - } else if exists { - // append new cluster to the node - if node, _, exists, err := rt.tree.Get(key); err != nil { - logger.Errorf("rt.tree.Get(key) err: %s", err.Error()) - return - } else if exists { - bizInfoInterface := node.GetBizInfo() - bizInfo, ok := bizInfoInterface.(*Node) - if bizInfo == nil || !ok { - logger.Error("bizInfoInterface.(*Node) failed") - return - } - // avoid thread safe problem - clusters := strings.Split(bizInfo.method.HTTPBackendConfig.URL, ",") - if len(clusters) > 1 { - var i int - for i = 0; i < len(clusters); i++ { - if clusters[i] == api.URL { - break - } - } - - if i == len(clusters) { - return - } - - // operate pointer has no necessary to call update api - bizInfo.method.HTTPBackendConfig.URL = strings.Join(append(clusters[:i], clusters[i+1:]...), ",") - } else { - // double check, avoid removing api that does not exists - if !strings.Contains(bizInfo.method.HTTPBackendConfig.URL, api.URL) { - return - } - // if backend has only one node, then just directly remove - _, _ = rt.tree.Remove(key) - } - } - } -} - -func getTrieKey(method config.HTTPVerb, path string, isPrefix bool) string { - if isPrefix { - if !strings.HasSuffix(path, constant.PathSlash) { - path = path + constant.PathSlash - } - path = path + "**" - } - return stringutil.GetTrieKey(string(method), path) -} - -// PutAPI puts an api into the resource -func (rt *Route) PutAPI(api router.API) error { - lowerCasePath := strings.ToLower(api.URLPattern) - key := getTrieKey(api.Method.HTTPVerb, lowerCasePath, false) - node, ok := rt.getNode(key) - if !ok { - rn := &Node{ - fullPath: lowerCasePath, - method: &api.Method, - headers: api.Headers, - } - rt.lock.Lock() - defer rt.lock.Unlock() - _, _ = rt.tree.Put(key, rn) - return nil - } - return errors.Errorf("Method %s with address %s already exists in path %s", - api.Method.HTTPVerb, lowerCasePath, node.fullPath) -} - -// PutOrUpdateAPI puts or updates an api into the resource -func (rt *Route) PutOrUpdateAPI(api router.API) error { - lowerCasePath := strings.ToLower(api.URLPattern) - key := getTrieKey(api.Method.HTTPVerb, lowerCasePath, false) - rn := &Node{ - fullPath: lowerCasePath, - method: &api.Method, - headers: api.Headers, - } - rt.lock.Lock() - defer rt.lock.Unlock() - - // if api is exists - if exists, err := rt.tree.Contains(key); err != nil { - return err - } else if exists { - // append new cluster to the node - if node, _, exists, err := rt.tree.Get(key); err != nil { - return err - } else if exists { - bizInfoInterface := node.GetBizInfo() - bizInfo, ok := bizInfoInterface.(*Node) - if bizInfo == nil || !ok { - return errors.New("bizInfoInterface.(*Node) failed") - } - if !strings.Contains(bizInfo.method.HTTPBackendConfig.URL, api.URL) { - // operate pointer has no necessary to call update api - bizInfo.method.HTTPBackendConfig.URL = bizInfo.method.HTTPBackendConfig.URL + "," + api.URL - } - } - } else { - if ok, err := rt.tree.PutOrUpdate(key, rn); !ok { - return err - } - } - - return nil -} - -// FindAPI return if api has path in trie,or nil -func (rt *Route) FindAPI(fullPath string, httpverb config.HTTPVerb) (*router.API, bool) { - lowerCasePath := strings.ToLower(fullPath) - key := getTrieKey(httpverb, lowerCasePath, false) - if n, found := rt.getNode(key); found { - rt.lock.RLock() - defer rt.lock.RUnlock() - return &router.API{ - URLPattern: n.fullPath, - Method: *n.method, - Headers: n.headers, - }, found - } - return nil, false -} - -// MatchAPI FindAPI returns the api that meets the rule -func (rt *Route) MatchAPI(fullPath string, httpverb config.HTTPVerb) (*router.API, bool) { - lowerCasePath := strings.ToLower(fullPath) - key := getTrieKey(httpverb, lowerCasePath, false) - if n, found := rt.matchNode(key); found { - rt.lock.RLock() - defer rt.lock.RUnlock() - return &router.API{ - URLPattern: n.fullPath, - Method: *n.method, - Headers: n.headers, - }, found - } - return nil, false -} - -// DeleteNode delete node by fullPath -func (rt *Route) DeleteNode(fullPath string) bool { - rt.lock.RLock() - defer rt.lock.RUnlock() - methodList := [8]config.HTTPVerb{"ANY", "GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"} - for _, v := range methodList { - key := getTrieKey(v, fullPath, false) - _, _ = rt.tree.Remove(key) - } - return true -} - -// DeleteAPI delete api by fullPath and http verb -func (rt *Route) DeleteAPI(fullPath string, httpverb config.HTTPVerb) bool { - lowerCasePath := strings.ToLower(fullPath) - key := getTrieKey(httpverb, lowerCasePath, false) - if _, found := rt.getNode(key); found { - rt.lock.RLock() - defer rt.lock.RUnlock() - _, _ = rt.tree.Remove(key) - return true - } - return false -} - -func (rt *Route) getNode(fullPath string) (*Node, bool) { - var n interface{} - var found bool - rt.lock.RLock() - defer rt.lock.RUnlock() - trieNode, _, _, _ := rt.tree.Get(fullPath) - found = trieNode != nil - if !found { - return nil, false - } - n = trieNode.GetBizInfo() - if n == nil { - return nil, false - } - return n.(*Node), found -} - -func (rt *Route) matchNode(fullPath string) (*Node, bool) { - var n interface{} - var found bool - rt.lock.RLock() - defer rt.lock.RUnlock() - trieNode, _, _ := rt.tree.Match(fullPath) - found = trieNode != nil - if !found { - return nil, false - } - n = trieNode.GetBizInfo() - if n == nil { - return nil, false - } - return n.(*Node), found -} - -func wildcardMatch(wildcardPath string, checkPath string) url.Values { - - cPaths := strings.Split(strings.TrimLeft(checkPath, constant.PathSlash), constant.PathSlash) - wPaths := strings.Split(strings.TrimLeft(wildcardPath, constant.PathSlash), constant.PathSlash) - result := url.Values{} - if len(cPaths) == 0 || len(wPaths) == 0 || len(cPaths) != len(wPaths) { - return nil - } - for i := 0; i < len(cPaths); i++ { - if !strings.EqualFold(cPaths[i], wPaths[i]) && !strings.HasPrefix(wPaths[i], constant.PathParamIdentifier) { - return nil - } - if strings.HasPrefix(wPaths[i], constant.PathParamIdentifier) { - result.Add(strings.TrimPrefix(wPaths[i], constant.PathParamIdentifier), cPaths[i]) - } - } - return result -} - -// NewRoute returns an empty router tree -func NewRoute() *Route { - return &Route{ - tree: trie.NewTrie(), - } -} diff --git a/pixiu/pkg/router/route_test.go b/pixiu/pkg/router/route_test.go deleted file mode 100644 index 8581d3c67..000000000 --- a/pixiu/pkg/router/route_test.go +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 router - -import ( - "testing" -) - -import ( - "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" - "github.com/dubbo-go-pixiu/pixiu-api/pkg/router" - "github.com/stretchr/testify/assert" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/router/trie" -) - -func getMockMethod(verb config.HTTPVerb) config.Method { - inbound := config.InboundRequest{} - integration := config.IntegrationRequest{} - return config.Method{ - Enable: true, - HTTPVerb: verb, - InboundRequest: inbound, - IntegrationRequest: integration, - } -} - -func TestPut(t *testing.T) { - rt := &Route{ - tree: trie.NewTrie(), - } - n0 := getMockMethod(config.MethodGet) - _ = rt.PutAPI(router.API{URLPattern: "/", Method: n0}) - _, ok := rt.FindAPI("/", n0.HTTPVerb) - assert.True(t, ok) - - err := rt.PutAPI(router.API{URLPattern: "/", Method: n0}) - assert.Error(t, err, "Method GET already exists in path /") - - n1 := getMockMethod(config.MethodPost) - err = rt.PutAPI(router.API{URLPattern: "/mock", Method: n0}) - assert.Nil(t, err) - err = rt.PutAPI(router.API{URLPattern: "/mock", Method: n1}) - assert.Nil(t, err) - _, ok = rt.FindAPI("/mock", n0.HTTPVerb) - assert.True(t, ok) - _, ok = rt.FindAPI("/mock", n1.HTTPVerb) - assert.True(t, ok) - - err = rt.PutAPI(router.API{URLPattern: "/mock/test", Method: n0}) - assert.Nil(t, err) - _, ok = rt.FindAPI("/mock/test", n0.HTTPVerb) - assert.True(t, ok) - - _ = rt.PutAPI(router.API{URLPattern: "/test/:id", Method: n0}) - _, ok = rt.FindAPI("/test/:id", n0.HTTPVerb) - assert.True(t, ok) - - err = rt.PutAPI(router.API{URLPattern: "/test/:id", Method: n1}) - assert.Nil(t, err) - err = rt.PutAPI(router.API{URLPattern: "/test/js", Method: n0}) - assert.Nil(t, err) - err = rt.PutAPI(router.API{URLPattern: "/test/:id/mock", Method: n0}) - _, ok = rt.FindAPI("/test/:id/mock", n0.HTTPVerb) - assert.True(t, ok) - assert.Nil(t, err) -} - -func TestMatchMethod(t *testing.T) { - rt := &Route{ - tree: trie.NewTrie(), - } - n0 := getMockMethod(config.MethodGet) - n1 := getMockMethod(config.MethodPost) - e := rt.PutAPI(router.API{URLPattern: "/theboys", Method: n0}) - assert.Nil(t, e) - e = rt.PutAPI(router.API{URLPattern: "/theboys/:id", Method: n0}) - assert.Nil(t, e) - e = rt.PutAPI(router.API{URLPattern: "/vought/:id/supe/:name", Method: n1}) - assert.Nil(t, e) - - m, ok := rt.MatchAPI("/theboys", config.MethodGet) - assert.True(t, ok) - assert.NotNil(t, m) - assert.Equal(t, m.URLPattern, "/theboys") - - m, ok = rt.MatchAPI("/theboys", config.MethodPost) - assert.False(t, ok) - assert.Nil(t, m) - - m, ok = rt.MatchAPI("/vought/123/supe/startlight", config.MethodPost) - assert.True(t, ok) - assert.NotNil(t, m) - assert.Equal(t, m.URLPattern, "/vought/:id/supe/:name") - - m, ok = rt.MatchAPI("/vought/123/supe/startlight", config.MethodPost) - assert.True(t, ok) - assert.NotNil(t, m) - assert.Equal(t, m.URLPattern, "/vought/:id/supe/:name") -} - -// -//func TestUpdateMethod(t *testing.T) { -// m0 := getMockMethod(config.MethodGet) -// m1 := getMockMethod(config.MethodGet) -// m0.DubboBackendConfig.Version = "1.0.0" -// m1.DubboBackendConfig.Version = "2.0.0" -// -// rt := NewRoute() -// rt.PutAPI(router.API{URLPattern: "/marvel", Method: m0}) -// m, _ := rt.FindAPI("/marvel", config.MethodGet) -// assert.Equal(t, m.DubboBackendConfig.Version, "1.0.0") -// rt.UpdateAPI(router.API{URLPattern: "/marvel", Method: m1}) -// m, ok := rt.FindAPI("/marvel", config.MethodGet) -// assert.True(t, ok) -// assert.Equal(t, m.DubboBackendConfig.Version, "2.0.0") -// -// rt.PutAPI(router.API{URLPattern: "/theboys/:id", Method: m0}) -// m, _ = rt.FindAPI("/theBoys/12345", config.MethodGet) -// assert.Equal(t, m.DubboBackendConfig.Version, "1.0.0") -// rt.UpdateAPI(router.API{URLPattern: "/theBoys/:id", Method: m1}) -// m, ok = rt.FindAPI("/theBoys/12345", config.MethodGet) -// assert.True(t, ok) -// assert.Equal(t, m.DubboBackendConfig.Version, "2.0.0") -//} - -//func TestSearchWildcard(t *testing.T) { -// rt := &Route{ -// tree: avltree.NewWithStringComparator(), -// wildcardTree: avltree.NewWithStringComparator(), -// } -// n0 := getMockMethod(config.MethodGet) -// e := rt.PutAPI(router.API{URLPattern: "/theboys", Method: n0}) -// assert.Nil(t, e) -// e = rt.PutAPI(router.API{URLPattern: "/theboys/:id", Method: n0}) -// assert.Nil(t, e) -// e = rt.PutAPI(router.API{URLPattern: "/vought/:id/supe/:name", Method: n0}) -// assert.Nil(t, e) -// -// _, ok := rt.searchWildcard("/marvel") -// assert.False(t, ok) -// _, ok = rt.searchWildcard("/theboys/:id/age") -// assert.False(t, ok) -// _, ok = rt.searchWildcard("/theboys/butcher") -// assert.True(t, ok) -// _, ok = rt.searchWildcard("/vought/:id/supe/homelander") -// assert.True(t, ok) -//} - -func TestWildcardMatch(t *testing.T) { - vals := wildcardMatch("/vought/:id", "/vought/12345") - assert.NotNil(t, vals) - assert.Equal(t, vals.Get("id"), "12345") - vals = wildcardMatch("/vought/:id", "/vought/125abc") - assert.NotNil(t, vals) - assert.Equal(t, vals.Get("id"), "125abc") - vals = wildcardMatch("/vought/:id", "/vought/1234abcd/status") - assert.Nil(t, vals) - vals = wildcardMatch("/voughT/:id/:action", "/Vought/1234abcd/attack") - assert.NotNil(t, vals) - assert.Equal(t, vals.Get("id"), "1234abcd") - assert.Equal(t, vals.Get("action"), "attack") - vals = wildcardMatch("/voughT/:id/status", "/Vought/1234abcd/status") - assert.NotNil(t, vals) - assert.Equal(t, vals.Get("id"), "1234abcd") -} - -func TestGetFilters(t *testing.T) { - rt := NewRoute() - n0 := getMockMethod(config.MethodGet) - n1 := getMockMethod(config.MethodPost) - e := rt.PutAPI(router.API{URLPattern: "/theboys", Method: n0}) - assert.Nil(t, e) - e = rt.PutAPI(router.API{URLPattern: "/theboys/:id", Method: n0}) - assert.Nil(t, e) - e = rt.PutAPI(router.API{URLPattern: "/vought/:id/supe/:name", Method: n1}) - assert.Nil(t, e) -} diff --git a/pixiu/pkg/server/http.go b/pixiu/pkg/server/http.go deleted file mode 100644 index 606f861a9..000000000 --- a/pixiu/pkg/server/http.go +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 server - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/router/trie" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -// DefaultHttpConnectionManager -func DefaultHttpConnectionManager() *model.HttpConnectionManagerConfig { - return &model.HttpConnectionManagerConfig{ - RouteConfig: model.RouteConfiguration{ - RouteTrie: trie.NewTrieWithDefault("/api/v1/**", model.RouteAction{ - Cluster: constant.HeaderValueAll, - }), - }, - HTTPFilters: []*model.HTTPFilter{ - { - Name: constant.HTTPProxyFilter, - }, - }, - } -} diff --git a/pixiu/pkg/wasm/manager.go b/pixiu/pkg/wasm/manager.go deleted file mode 100644 index 0d4748042..000000000 --- a/pixiu/pkg/wasm/manager.go +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 wasm - -import ( - "sync" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -type WasmManager struct { - model *model.WasmConfig - serviceMap map[string]*WasmService -} - -var manager *WasmManager -var once sync.Once - -// InitWasmManager loads config in conf.yaml -func InitWasmManager(model *model.WasmConfig) { - manager = &WasmManager{ - model: model, - serviceMap: make(map[string]*WasmService), - } - - for _, service := range model.Services { - if !contains(service.Name) { - logger.Warnf("[dubbo-go-pixiu] wasm config error: service name isn't in pixiu keys.") - continue - } - wasmService, err := createWasmService(service) - if err != nil { - logger.Warnf("[dubbo-go-pixiu] wasm config error: %v", err) - continue - } - manager.serviceMap[service.Name] = wasmService - } -} - -// GetWasmServiceByName get WasmService By Name -func getWasmServiceByName(name string) *WasmService { - once.Do(func() { - bs := config.GetBootstrap() - InitWasmManager(bs.Wasm) - }) - if wasmService, exist := manager.serviceMap[name]; exist { - return wasmService - } - logger.Warnf("[dubbo-go-pixiu] no wasmService named %v, check pixiu conf.yaml", name) - - return nil -} - -func GetServiceRootID(name string) int32 { - wasmService := getWasmServiceByName(name) - return wasmService.rootContextID -} - -// CreateAbIContextByName create abiContext according to every request -func CreateABIContextByName(name string, ctx *http.HttpContext) *ABIContextWrapper { - wasmService := getWasmServiceByName(name) - - if wasmService != nil { - return wasmService.createABIContextWrapper(ctx) - } - return nil -} - -// ContextDone put wasmInstance back to Pool. -func ContextDone(wrapper *ABIContextWrapper) error { - wasmService := getWasmServiceByName(wrapper.name) - return wasmService.putWasmInstance(wrapper.Context.Instance) -} diff --git a/pixiu/pkg/wasm/service.go b/pixiu/pkg/wasm/service.go deleted file mode 100644 index 0571cdf08..000000000 --- a/pixiu/pkg/wasm/service.go +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 wasm - -import ( - "math" - "os" - "path/filepath" - "sync" - "sync/atomic" -) - -import ( - "github.com/mitchellh/mapstructure" - "mosn.io/proxy-wasm-go-host/common" - "mosn.io/proxy-wasm-go-host/proxywasm" - "mosn.io/proxy-wasm-go-host/wasmer" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" -) - -// WasmService manages a collection of WasmInstances of the same type. -type WasmService struct { - rootContextID int32 - contextIDGenerator int32 - name string - path string - mutex sync.Mutex - model model.WasmService - instancePool sync.Pool -} - -// WasmConfig contains config about every wasmFile. -type WasmConfig struct { - Path string `yaml:"path" json:"path,omitempty"` -} - -// ABIContextWrapper is a request wrapper which indicates request was handled in the wasmInstance. -type ABIContextWrapper struct { - ContextID int32 - name string - Context *proxywasm.ABIContext -} - -func createWasmService(service model.WasmService) (*WasmService, error) { - var cfg WasmConfig - if err := mapstructure.Decode(service.Config, &cfg); err != nil { - return nil, err - } - - wasmService := &WasmService{ - contextIDGenerator: 0, - name: service.Name, - path: cfg.Path, - mutex: sync.Mutex{}, - model: service, - } - wasmService.rootContextID = atomic.AddInt32(&wasmService.contextIDGenerator, 1) - - wasmService.instancePool = sync.Pool{ - New: func() interface{} { - pwd, _ := os.Getwd() - path := filepath.Join(pwd, cfg.Path) - if _, err := os.Stat(path); err != nil { - logger.Warnf("[dubbo-go-pixiu] wasmFile path:%v error: %v", path, err) - return nil - } - instance := wasmer.NewWasmerInstanceFromFile(path) - proxywasm.RegisterImports(instance) - _ = instance.Start() - return instance - }, - } - return wasmService, nil -} - -func (ws *WasmService) getWasmInstance() common.WasmInstance { - return ws.instancePool.Get().(common.WasmInstance) -} - -func (ws *WasmService) putWasmInstance(instance common.WasmInstance) error { - ws.instancePool.Put(instance) - return nil -} - -func (ws *WasmService) createABIContextWrapper(ctx *http.HttpContext) *ABIContextWrapper { - - contextWrapper := new(ABIContextWrapper) - contextWrapper.ContextID = atomic.AddInt32(&ws.contextIDGenerator, 1) - atomic.CompareAndSwapInt32(&ws.contextIDGenerator, math.MaxInt32, 1) - - contextWrapper.name = ws.name - contextWrapper.Context = &proxywasm.ABIContext{ - Imports: &importHandler{ - ctx: ctx, - reqHeader: &headerWrapper{ - header: ctx.Request.Header, - }, - }, - Instance: ws.getWasmInstance(), - } - _ = contextWrapper.Context.GetExports().ProxyOnContextCreate(ws.rootContextID, 0) - - return contextWrapper -} diff --git a/pixiu/pkg/wasm/utils.go b/pixiu/pkg/wasm/utils.go deleted file mode 100644 index 23182fd79..000000000 --- a/pixiu/pkg/wasm/utils.go +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 wasm - -import ( - "net/http" -) - -import ( - "mosn.io/proxy-wasm-go-host/common" - "mosn.io/proxy-wasm-go-host/proxywasm" -) - -import ( - contexthttp "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" -) - -type ( - headerWrapper struct { - header http.Header - } - - importHandler struct { - ctx *contexthttp.HttpContext - reqHeader common.HeaderMap - proxywasm.DefaultImportsHandler - } -) - -func (h *headerWrapper) Get(key string) (string, bool) { - value := h.header.Get(key) - if value == "" { - return "", false - } - return value, true -} - -func (h *headerWrapper) Set(key, value string) { - h.header.Set(key, value) -} - -func (h *headerWrapper) Add(key, value string) { - h.header.Add(key, value) -} - -func (h *headerWrapper) Del(key string) { - h.header.Del(key) -} - -func (h *headerWrapper) Range(f func(key string, value string) bool) { - for k := range h.header { - v := h.header.Get(k) - f(k, v) - } -} - -func (h *headerWrapper) Clone() common.HeaderMap { - copy := &headerWrapper{ - header: h.header.Clone(), - } - return copy -} - -func (h *headerWrapper) ByteSize() uint64 { - var size uint64 - - for k := range h.header { - v := h.header.Get(k) - size += uint64(len(k) + len(v)) - } - return size -} - -func (im *importHandler) GetHttpRequestHeader() common.HeaderMap { - return im.reqHeader -} - -func (im *importHandler) Log(level proxywasm.LogLevel, msg string) proxywasm.WasmResult { - switch level { - case proxywasm.LogLevelDebug: - logger.Debugf(msg) - case proxywasm.LogLevelInfo: - logger.Infof(msg) - case proxywasm.LogLevelWarn: - logger.Warnf(msg) - case proxywasm.LogLevelError: - logger.Errorf(msg) - default: - logger.Infof(msg) - } - return proxywasm.WasmResultOk -} diff --git a/pixiu/pkg/adapter/dubboregistry/common/common.go b/pkg/adapter/dubboregistry/common/common.go similarity index 100% rename from pixiu/pkg/adapter/dubboregistry/common/common.go rename to pkg/adapter/dubboregistry/common/common.go diff --git a/pixiu/pkg/adapter/dubboregistry/registry/base/baseregistry.go b/pkg/adapter/dubboregistry/registry/base/baseregistry.go similarity index 95% rename from pixiu/pkg/adapter/dubboregistry/registry/base/baseregistry.go rename to pkg/adapter/dubboregistry/registry/base/baseregistry.go index 3dd6377cc..731312fc9 100644 --- a/pixiu/pkg/adapter/dubboregistry/registry/base/baseregistry.go +++ b/pkg/adapter/dubboregistry/registry/base/baseregistry.go @@ -22,8 +22,8 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/common" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/registry" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/common" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/registry" ) type FacadeRegistry interface { diff --git a/pixiu/pkg/adapter/dubboregistry/registry/base/baseregistry_test.go b/pkg/adapter/dubboregistry/registry/base/baseregistry_test.go similarity index 95% rename from pixiu/pkg/adapter/dubboregistry/registry/base/baseregistry_test.go rename to pkg/adapter/dubboregistry/registry/base/baseregistry_test.go index 7acd556f6..bdd4c3e20 100644 --- a/pixiu/pkg/adapter/dubboregistry/registry/base/baseregistry_test.go +++ b/pkg/adapter/dubboregistry/registry/base/baseregistry_test.go @@ -27,7 +27,7 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/registry" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/registry" ) var _ registry.Registry = new(mockRegFacade) diff --git a/pixiu/pkg/adapter/dubboregistry/registry/listener.go b/pkg/adapter/dubboregistry/registry/listener.go similarity index 100% rename from pixiu/pkg/adapter/dubboregistry/registry/listener.go rename to pkg/adapter/dubboregistry/registry/listener.go diff --git a/pkg/adapter/dubboregistry/registry/nacos/interface_listener.go b/pkg/adapter/dubboregistry/registry/nacos/interface_listener.go new file mode 100644 index 000000000..3588f33fd --- /dev/null +++ b/pkg/adapter/dubboregistry/registry/nacos/interface_listener.go @@ -0,0 +1,213 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 nacos + +import ( + "bytes" + "fmt" + "strings" + "sync" + "time" +) + +import ( + dubboCommon "dubbo.apache.org/dubbo-go/v3/common" + "dubbo.apache.org/dubbo-go/v3/common/constant" + "github.com/nacos-group/nacos-sdk-go/clients/naming_client" + "github.com/nacos-group/nacos-sdk-go/vo" +) + +import ( + common2 "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/common" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/registry" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/remoting/zookeeper" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +const ( + MaxFailTimes = 2 + ConnDelay = 3 * time.Second +) + +var _ registry.Listener = new(nacosIntfListener) + +type nacosIntfListener struct { + exit chan struct{} + client naming_client.INamingClient + regConf *model.Registry + reg *NacosRegistry + wg sync.WaitGroup + addr string + adapterListener common2.RegistryEventListener + serviceInfoMap map[string]*serviceInfo +} + +// newNacosIntfListener returns a new nacosIntfListener with pre-defined path according to the registered type. +func newNacosIntfListener(client naming_client.INamingClient, reg *NacosRegistry, regConf *model.Registry, adapterListener common2.RegistryEventListener) registry.Listener { + return &nacosIntfListener{ + exit: make(chan struct{}), + client: client, + regConf: regConf, + reg: reg, + addr: regConf.Address, + adapterListener: adapterListener, + serviceInfoMap: map[string]*serviceInfo{}, + } +} + +func (z *nacosIntfListener) Close() { + close(z.exit) + z.wg.Wait() +} + +func (z *nacosIntfListener) WatchAndHandle() { + z.wg.Add(1) + go z.watch() +} + +func (z *nacosIntfListener) watch() { + defer z.wg.Done() + var ( + failTimes int64 = 0 + delayTimer = time.NewTimer(ConnDelay * time.Duration(failTimes)) + ) + defer delayTimer.Stop() + for { + serviceList, err := z.client.GetAllServicesInfo(vo.GetAllServiceInfoParam{ + GroupName: z.regConf.Group, + NameSpace: z.regConf.Namespace, + PageSize: 100, + }) + // error handling + if err != nil { + failTimes++ + logger.Infof("watching nacos interface with error{%v}", err) + // Exit the watch if root node is in error + if err == zookeeper.ErrNilNode { + logger.Errorf("watching nacos services got errNilNode,so exit listen") + return + } + if failTimes > MaxFailTimes { + logger.Errorf("Error happens on nacos exceed max fail times: %s,so exit listen", MaxFailTimes) + return + } + delayTimer.Reset(ConnDelay * time.Duration(failTimes)) + <-delayTimer.C + continue + } + failTimes = 0 + if err := z.updateServiceList(serviceList.Doms); err != nil { + logger.Errorf("update service list failed %s", err) + } + time.Sleep(time.Second * 5) + } +} + +type serviceInfo struct { + interfaceName string + version string + group string + listener *serviceListener +} + +func (s *serviceInfo) String() string { + return fmt.Sprintf("%s:%s:%s", s.interfaceName, s.version, s.group) +} + +func fromServiceFullKey(fullKey string) *serviceInfo { + serviceInfoStrs := strings.Split(fullKey, ":") + if len(serviceInfoStrs) != 4 { + return nil + } + return &serviceInfo{ + interfaceName: serviceInfoStrs[1], + version: serviceInfoStrs[2], + group: serviceInfoStrs[3], + } +} + +func (z *nacosIntfListener) updateServiceList(serviceList []string) error { + // add new service info and watch + + newServiceMap := make(map[string]bool) + + for _, v := range serviceList { + svcInfo := fromServiceFullKey(v) + if svcInfo == nil { + // invalid nacos dubbo service key + continue + } + key := svcInfo.String() + newServiceMap[key] = true + if _, ok := z.serviceInfoMap[key]; !ok { + + url, _ := dubboCommon.NewURL("mock://localhost:8848") + url.SetParam(constant.InterfaceKey, svcInfo.interfaceName) + url.SetParam(constant.GroupKey, svcInfo.group) + url.SetParam(constant.VersionKey, svcInfo.version) + l := newNacosSrvListener(url, z.client, z.adapterListener) + l.wg.Add(1) + + svcInfo.listener = l + z.serviceInfoMap[key] = svcInfo + + go func(v *serviceInfo) { + defer l.wg.Done() + + sub := &vo.SubscribeParam{ + ServiceName: getSubscribeName(url), + SubscribeCallback: l.Callback, + GroupName: z.regConf.Group, + } + + if err := z.client.Subscribe(sub); err != nil { + logger.Errorf("subscribe listener with interfaceKey = %s, error = %s", l, err) + } + }(svcInfo) + } + } + + // handle deleted service + for k, v := range z.serviceInfoMap { + if _, ok := newServiceMap[k]; !ok { + delete(z.serviceInfoMap, k) + v.listener.Close() + } + } + + return nil + +} + +func getSubscribeName(url *dubboCommon.URL) string { + var buffer bytes.Buffer + buffer.Write([]byte(dubboCommon.DubboNodes[dubboCommon.PROVIDER])) + appendParam(&buffer, url, constant.InterfaceKey) + appendParam(&buffer, url, constant.VersionKey) + appendParam(&buffer, url, constant.GroupKey) + return buffer.String() +} + +func appendParam(target *bytes.Buffer, url *dubboCommon.URL, key string) { + value := url.GetParam(key, "") + target.Write([]byte(constant.NacosServiceNameSeparator)) + if strings.TrimSpace(value) != "" { + target.Write([]byte(value)) + } +} diff --git a/pkg/adapter/dubboregistry/registry/nacos/registry.go b/pkg/adapter/dubboregistry/registry/nacos/registry.go new file mode 100644 index 000000000..8d1356a25 --- /dev/null +++ b/pkg/adapter/dubboregistry/registry/nacos/registry.go @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 nacos + +import ( + "github.com/nacos-group/nacos-sdk-go/clients" + "github.com/nacos-group/nacos-sdk-go/clients/naming_client" + nacosConstant "github.com/nacos-group/nacos-sdk-go/common/constant" + "github.com/nacos-group/nacos-sdk-go/vo" + "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/common" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/registry" + baseRegistry "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/registry/base" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/util/stringutil" + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +func init() { + registry.SetRegistry(constant.Nacos, newNacosRegistry) +} + +type NacosRegistry struct { + *baseRegistry.BaseRegistry + nacosListeners map[registry.RegisteredType]registry.Listener + client naming_client.INamingClient +} + +func (n *NacosRegistry) DoSubscribe() error { + intfListener, ok := n.nacosListeners[registry.RegisteredTypeInterface] + if !ok { + return errors.New("Listener for interface level registration does not initialized") + } + go intfListener.WatchAndHandle() + return nil +} + +func (n *NacosRegistry) DoUnsubscribe() error { + panic("implement me") +} + +var _ registry.Registry = new(NacosRegistry) + +func newNacosRegistry(regConfig model.Registry, adapterListener common.RegistryEventListener) (registry.Registry, error) { + addrs, err := stringutil.GetIPAndPort(regConfig.Address) + if err != nil { + return nil, err + } + + scs := make([]nacosConstant.ServerConfig, 0) + for _, addr := range addrs { + scs = append(scs, nacosConstant.ServerConfig{ + IpAddr: addr.IP.String(), + Port: uint64(addr.Port), + }) + } + + ccs := nacosConstant.NewClientConfig( + nacosConstant.WithNamespaceId(regConfig.Namespace), + nacosConstant.WithUsername(regConfig.Username), + nacosConstant.WithPassword(regConfig.Password), + nacosConstant.WithNotLoadCacheAtStart(true), + nacosConstant.WithUpdateCacheWhenEmpty(true)) + client, err := clients.NewNamingClient(vo.NacosClientParam{ + ServerConfigs: scs, + ClientConfig: ccs, + }) + if err != nil { + return nil, err + } + + nacosRegistry := &NacosRegistry{ + client: client, + nacosListeners: make(map[registry.RegisteredType]registry.Listener), + } + nacosRegistry.nacosListeners[registry.RegisteredTypeInterface] = newNacosIntfListener(client, nacosRegistry, ®Config, adapterListener) + + baseReg := baseRegistry.NewBaseRegistry(nacosRegistry, adapterListener) + nacosRegistry.BaseRegistry = baseReg + return baseReg, nil +} diff --git a/pkg/adapter/dubboregistry/registry/nacos/service_listener.go b/pkg/adapter/dubboregistry/registry/nacos/service_listener.go new file mode 100644 index 000000000..b24bef320 --- /dev/null +++ b/pkg/adapter/dubboregistry/registry/nacos/service_listener.go @@ -0,0 +1,223 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 nacos + +import ( + "net/url" + "reflect" + "strconv" + "sync" +) + +import ( + dubboCommon "dubbo.apache.org/dubbo-go/v3/common" + dubboRegistry "dubbo.apache.org/dubbo-go/v3/registry" + _ "dubbo.apache.org/dubbo-go/v3/registry/nacos" + "dubbo.apache.org/dubbo-go/v3/remoting" + "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" + "github.com/nacos-group/nacos-sdk-go/clients/naming_client" + nacosModel "github.com/nacos-group/nacos-sdk-go/model" +) + +import ( + common2 "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/common" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/registry" + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +// serviceListener normally monitors the /dubbo/[:url.service()]/providers +type serviceListener struct { + url *dubboCommon.URL + client naming_client.INamingClient + instanceMap map[string]nacosModel.Instance + cacheLock sync.Mutex + + exit chan struct{} + wg sync.WaitGroup + adapterListener common2.RegistryEventListener +} + +// WatchAndHandle todo WatchAndHandle is useless for service listener +func (z *serviceListener) WatchAndHandle() { + panic("implement me") +} + +// newNacosSrvListener creates a new zk service listener +func newNacosSrvListener(url *dubboCommon.URL, client naming_client.INamingClient, adapterListener common2.RegistryEventListener) *serviceListener { + return &serviceListener{ + url: url, + client: client, + exit: make(chan struct{}), + adapterListener: adapterListener, + instanceMap: map[string]nacosModel.Instance{}, + } +} + +func (z *serviceListener) Callback(services []nacosModel.SubscribeService, err error) { + if err != nil { + logger.Errorf("nacos subscribe callback error:%s", err.Error()) + return + } + + addInstances := make([]nacosModel.Instance, 0, len(services)) + delInstances := make([]nacosModel.Instance, 0, len(services)) + updateInstances := make([]nacosModel.Instance, 0, len(services)) + newInstanceMap := make(map[string]nacosModel.Instance, len(services)) + + z.cacheLock.Lock() + defer z.cacheLock.Unlock() + for i := range services { + if !services[i].Enable { + // instance is not available,so ignore it + continue + } + host := services[i].Ip + ":" + strconv.Itoa(int(services[i].Port)) + instance := generateInstance(services[i]) + newInstanceMap[host] = instance + if old, ok := z.instanceMap[host]; !ok { + // instance does not exist in cache, add it to cache + addInstances = append(addInstances, instance) + } else { + // instance is not different from cache, update it to cache + if !reflect.DeepEqual(old, instance) { + updateInstances = append(updateInstances, instance) + } + } + } + + for host, inst := range z.instanceMap { + if _, ok := newInstanceMap[host]; !ok { + // cache instance does not exist in new instance list, remove it from cache + delInstances = append(delInstances, inst) + } + } + + z.instanceMap = newInstanceMap + for i := range addInstances { + newUrl := generateURL(addInstances[i]) + if newUrl != nil { + z.handle(newUrl, remoting.EventTypeAdd) + } + } + for i := range delInstances { + newUrl := generateURL(delInstances[i]) + if newUrl != nil { + z.handle(newUrl, remoting.EventTypeDel) + } + } + + for i := range updateInstances { + newUrl := generateURL(updateInstances[i]) + if newUrl != nil { + z.handle(newUrl, remoting.EventTypeUpdate) + } + } +} + +func (z *serviceListener) handle(url *dubboCommon.URL, action remoting.EventType) { + + logger.Infof("update begin, service event: %v %v", action, url) + + bkConfig, methods, location, err := registry.ParseDubboString(url.String()) + if err != nil { + logger.Errorf("parse dubbo string error = %s", err) + return + } + + mappingParams := []config.MappingParam{ + { + Name: "requestBody.values", + MapTo: "opt.values", + }, + { + Name: "requestBody.types", + MapTo: "opt.types", + }, + } + apiPattern := registry.GetAPIPattern(bkConfig) + for i := range methods { + api := registry.CreateAPIConfig(apiPattern, location, bkConfig, methods[i], mappingParams) + if action == remoting.EventTypeDel { + if err := z.adapterListener.OnRemoveAPI(api); err != nil { + logger.Errorf("Error={%s} happens when try to remove api %s", err.Error(), api.Path) + continue + } + } else { + if err := z.adapterListener.OnAddAPI(api); err != nil { + logger.Errorf("Error={%s} happens when try to add api %s", err.Error(), api.Path) + continue + } + } + + } +} + +func (z *serviceListener) NotifyAll(e []*dubboRegistry.ServiceEvent, f func()) { +} + +// Close closes this listener +func (zkl *serviceListener) Close() { + close(zkl.exit) + zkl.wg.Wait() +} + +func generateURL(instance nacosModel.Instance) *dubboCommon.URL { + if instance.Metadata == nil { + logger.Errorf("nacos instance metadata is empty,instance:%+v", instance) + return nil + } + path := instance.Metadata["path"] + myInterface := instance.Metadata["interface"] + if len(path) == 0 && len(myInterface) == 0 { + logger.Errorf("nacos instance metadata does not have both path key and interface key,instance:%+v", instance) + return nil + } + if len(path) == 0 && len(myInterface) != 0 { + path = "/" + myInterface + } + protocol := instance.Metadata["protocol"] + if len(protocol) == 0 { + logger.Errorf("nacos instance metadata does not have protocol key,instance:%+v", instance) + return nil + } + urlMap := url.Values{} + for k, v := range instance.Metadata { + urlMap.Set(k, v) + } + return dubboCommon.NewURLWithOptions( + dubboCommon.WithIp(instance.Ip), + dubboCommon.WithPort(strconv.Itoa(int(instance.Port))), + dubboCommon.WithProtocol(protocol), + dubboCommon.WithParams(urlMap), + dubboCommon.WithPath(path), + ) +} + +func generateInstance(ss nacosModel.SubscribeService) nacosModel.Instance { + return nacosModel.Instance{ + InstanceId: ss.InstanceId, + Ip: ss.Ip, + Port: ss.Port, + ServiceName: ss.ServiceName, + Valid: ss.Valid, + Enable: ss.Enable, + Weight: ss.Weight, + Metadata: ss.Metadata, + ClusterName: ss.ClusterName, + } +} diff --git a/pkg/adapter/dubboregistry/registry/registry.go b/pkg/adapter/dubboregistry/registry/registry.go new file mode 100644 index 000000000..f9a64de2e --- /dev/null +++ b/pkg/adapter/dubboregistry/registry/registry.go @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 registry + +import ( + "strings" + "time" +) + +import ( + "dubbo.apache.org/dubbo-go/v3/common" + "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" + "github.com/dubbo-go-pixiu/pixiu-api/pkg/router" + "github.com/pkg/errors" +) + +import ( + common2 "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/common" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +type RegisteredType int8 + +const ( + RegisteredTypeApplication RegisteredType = iota + RegisteredTypeInterface +) + +var registryMap = make(map[string]func(model.Registry, common2.RegistryEventListener) (Registry, error), 8) + +func (t *RegisteredType) String() string { + return []string{"application", "interface"}[*t] +} + +// Registry interface defines the basic features of a registry +type Registry interface { + // Subscribe monitors the target registry. + Subscribe() error + // Unsubscribe stops monitoring the target registry. + Unsubscribe() error +} + +// SetRegistry will store the registry by name +func SetRegistry(name string, newRegFunc func(model.Registry, common2.RegistryEventListener) (Registry, error)) { + registryMap[name] = newRegFunc +} + +// GetRegistry will return the registry +// if not found, it will panic +func GetRegistry(name string, regConfig model.Registry, listener common2.RegistryEventListener) (Registry, error) { + if registry, ok := registryMap[regConfig.Protocol]; ok { + reg, err := registry(regConfig, listener) + if err != nil { + panic("Initialize Registry" + name + "failed due to: " + err.Error()) + } + return reg, nil + } + return nil, errors.New("Registry " + name + " does not support yet") +} + +// CreateAPIConfig returns router.API struct base on the input +func CreateAPIConfig(urlPattern, location string, dboBackendConfig config.DubboBackendConfig, methodString string, mappingParams []config.MappingParam) router.API { + dboBackendConfig.Method = methodString + url := strings.Join([]string{urlPattern, methodString}, constant.PathSlash) + var requestType config.RequestType + switch dboBackendConfig.Protocol { + case string(config.DubboRequest): + requestType = config.DubboRequest + case "tri": + requestType = "triple" + default: + requestType = config.DubboRequest + } + method := config.Method{ + Enable: true, + Timeout: 3 * time.Second, + Mock: false, + HTTPVerb: config.MethodPost, + InboundRequest: config.InboundRequest{ + RequestType: config.HTTPRequest, + }, + IntegrationRequest: config.IntegrationRequest{ + RequestType: requestType, + DubboBackendConfig: dboBackendConfig, + MappingParams: mappingParams, + HTTPBackendConfig: config.HTTPBackendConfig{ + URL: location, + }, + }, + } + return router.API{ + URLPattern: url, + Method: method, + } +} + +// ParseDubboString parse the dubbo urls +// dubbo://192.168.3.46:20002/org.apache.dubbo.UserProvider2?anyhost=true&app.version=0.0.1&application=UserInfoServer&bean.name=UserProvider +// &cluster=failover&environment=dev&export=true&interface=org.apache.dubbo.UserProvider2&ip=192.168.3.46&loadbalance=random&message_size=4 +// &methods=GetUser&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbo-go user-info server +// &name=UserInfoServer&organization=dubbo.io&pid=11037®istry.role=3&release=dubbo-golang-1.5.6 +// &service.filter=echo,token,accesslog,tps,generic_service,execute,pshutdown&side=provider&ssl-enabled=false×tamp=1624716984&warmup=100 +func ParseDubboString(urlString string) (config.DubboBackendConfig, []string, string, error) { + url, err := common.NewURL(urlString) + if err != nil { + return config.DubboBackendConfig{}, nil, "", errors.WithStack(err) + } + return config.DubboBackendConfig{ + ClusterName: url.GetParam(constant.ClusterKey, ""), + ApplicationName: url.GetParam(constant.ApplicationKey, ""), + Version: url.GetParam(constant.VersionKey, ""), + Protocol: url.Protocol, + Group: url.GetParam(constant.GroupKey, ""), + Interface: url.GetParam(constant.InterfaceKey, ""), + Retries: url.GetParam(constant.RetriesKey, ""), + }, strings.Split(url.GetParam(constant.MethodsKey, ""), constant.StringSeparator), url.Location, nil +} + +// GetAPIPattern generate the API path pattern. /application/interface/version +func GetAPIPattern(bkConfig config.DubboBackendConfig) string { + if bkConfig.Version == "" { + // if the version is empty, make sure the url path is valid. + return strings.Join([]string{"/" + bkConfig.ApplicationName, bkConfig.Interface}, constant.PathSlash) + } + return strings.Join([]string{"/" + bkConfig.ApplicationName, bkConfig.Interface, bkConfig.Version}, constant.PathSlash) +} + +func GetRouter() model.Router { + return model.Router{ + Match: model.RouterMatch{ + Prefix: "", + Path: "", + Methods: nil, + }, + } +} diff --git a/pkg/adapter/dubboregistry/registry/util.go b/pkg/adapter/dubboregistry/registry/util.go new file mode 100644 index 000000000..9dc3694dc --- /dev/null +++ b/pkg/adapter/dubboregistry/registry/util.go @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 registry + +import ( + "dubbo.apache.org/dubbo-go/v3/common" + "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" +) + +// TransferURL2Api transfer url and clusterName to IntegrationRequest +func TransferURL2Api(url *common.URL, clusterName string) []config.IntegrationRequest { + var irs []config.IntegrationRequest + for _, method := range url.Methods { + irs = append(irs, config.IntegrationRequest{ + RequestType: config.RequestType(url.Protocol), + DubboBackendConfig: config.DubboBackendConfig{ + ApplicationName: url.GetParam(constant.NameKey, ""), + Group: url.GetParam(constant.GroupKey, ""), + Version: url.GetParam(constant.VersionKey, ""), + Interface: url.GetParam(constant.InterfaceKey, ""), + Method: method, + Retries: url.GetParam(constant.RetriesKey, ""), + ClusterName: clusterName, + }, + }) + } + return irs +} diff --git a/pkg/adapter/dubboregistry/registry/zookeeper/application_listener.go b/pkg/adapter/dubboregistry/registry/zookeeper/application_listener.go new file mode 100644 index 000000000..910b2c53c --- /dev/null +++ b/pkg/adapter/dubboregistry/registry/zookeeper/application_listener.go @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 zookeeper + +import ( + "strings" + "sync" + "time" +) + +import ( + "github.com/dubbogo/go-zookeeper/zk" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/common" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/registry" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/remoting/zookeeper" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +const ( + defaultServicesPath = "/services" + methodsRootPath = "/dubbo/metadata" +) + +var _ registry.Listener = new(zkAppListener) + +type zkAppListener struct { + servicesPath string + exit chan struct{} + client *zookeeper.ZooKeeperClient + reg *ZKRegistry + wg sync.WaitGroup + adapterListener common.RegistryEventListener +} + +// newZkAppListener returns a new newZkAppListener with pre-defined servicesPath according to the registered type. +func newZkAppListener(client *zookeeper.ZooKeeperClient, reg *ZKRegistry, adapterListener common.RegistryEventListener) registry.Listener { + p := defaultServicesPath + return &zkAppListener{ + servicesPath: p, + exit: make(chan struct{}), + client: client, + reg: reg, + adapterListener: adapterListener, + } +} + +func (z *zkAppListener) Close() { + close(z.exit) + z.wg.Wait() +} + +func (z *zkAppListener) WatchAndHandle() { + z.wg.Add(1) + go z.watch() +} + +func (z *zkAppListener) watch() { + defer z.wg.Done() + + var ( + failTimes int64 = 0 + delayTimer = time.NewTimer(ConnDelay * time.Duration(failTimes)) + ) + defer delayTimer.Stop() + for { + children, e, err := z.client.GetChildrenW(z.servicesPath) + // error handling + if err != nil { + failTimes++ + logger.Infof("watching (path{%s}) = error{%v}", z.servicesPath, err) + // Exit the watch if root node is in error + if err == zookeeper.ErrNilNode { + logger.Errorf("watching (path{%s}) got errNilNode,so exit listen", z.servicesPath) + return + } + if failTimes > MaxFailTimes { + logger.Errorf("Error happens on (path{%s}) exceed max fail times: %s,so exit listen", + z.servicesPath, MaxFailTimes) + return + } + delayTimer.Reset(ConnDelay * time.Duration(failTimes)) + <-delayTimer.C + continue + } + failTimes = 0 + if continueLoop := z.waitEventAndHandlePeriod(children, e); !continueLoop { + return + } + } +} + +func (z *zkAppListener) waitEventAndHandlePeriod(children []string, e <-chan zk.Event) bool { + tickerTTL := defaultTTL + ticker := time.NewTicker(tickerTTL) + defer ticker.Stop() + z.handleEvent(children) + for { + select { + case <-ticker.C: + z.handleEvent(children) + case zkEvent := <-e: + logger.Warnf("get a zookeeper e{type:%s, server:%s, path:%s, state:%d-%s, err:%s}", + zkEvent.Type.String(), zkEvent.Server, zkEvent.Path, zkEvent.State, zookeeper.StateToString(zkEvent.State), zkEvent.Err) + if zkEvent.Type != zk.EventNodeChildrenChanged { + return true + } + z.handleEvent(children) + return true + case <-z.exit: + logger.Warnf("listen(path{%s}) goroutine exit now...", z.servicesPath) + return false + } + } +} + +func (z *zkAppListener) handleEvent(children []string) { + fetchChildren, err := z.client.GetChildren(z.servicesPath) + if err != nil { + logger.Warnf("Error when retrieving newChildren in path: %s, Error:%s", z.servicesPath, err.Error()) + } + for _, path := range fetchChildren { + serviceName := strings.Join([]string{z.servicesPath, path}, constant.PathSlash) + if z.reg.GetSvcListener(serviceName) != nil { + continue + } + l := newApplicationServiceListener(serviceName, z.client, z.adapterListener) + l.wg.Add(1) + go l.WatchAndHandle() + z.reg.SetSvcListener(serviceName, l) + } +} diff --git a/pixiu/pkg/adapter/dubboregistry/registry/zookeeper/application_service_listener.go b/pkg/adapter/dubboregistry/registry/zookeeper/application_service_listener.go similarity index 95% rename from pixiu/pkg/adapter/dubboregistry/registry/zookeeper/application_service_listener.go rename to pkg/adapter/dubboregistry/registry/zookeeper/application_service_listener.go index 671e801b9..aeb5fc603 100644 --- a/pixiu/pkg/adapter/dubboregistry/registry/zookeeper/application_service_listener.go +++ b/pkg/adapter/dubboregistry/registry/zookeeper/application_service_listener.go @@ -36,11 +36,11 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/common" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/registry" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/remoting/zookeeper" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/common" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/registry" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/remoting/zookeeper" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/logger" ) var _ registry.Listener = new(applicationServiceListener) diff --git a/pkg/adapter/dubboregistry/registry/zookeeper/interface_listener.go b/pkg/adapter/dubboregistry/registry/zookeeper/interface_listener.go new file mode 100644 index 000000000..78f16dfbd --- /dev/null +++ b/pkg/adapter/dubboregistry/registry/zookeeper/interface_listener.go @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 zookeeper + +import ( + "path" + "sync" + "time" +) + +import ( + "dubbo.apache.org/dubbo-go/v3/common" + "github.com/dubbogo/go-zookeeper/zk" +) + +import ( + common2 "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/common" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/registry" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/remoting/zookeeper" + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +const ( + rootPath = "/dubbo" + providerCategory = "providers" +) + +var _ registry.Listener = new(zkIntfListener) + +type zkIntfListener struct { + path string + exit chan struct{} + client *zookeeper.ZooKeeperClient + reg *ZKRegistry + wg sync.WaitGroup + adapterListener common2.RegistryEventListener +} + +// newZKIntfListener returns a new zkIntfListener with pre-defined path according to the registered type. +func newZKIntfListener(client *zookeeper.ZooKeeperClient, reg *ZKRegistry, adapterListener common2.RegistryEventListener) registry.Listener { + p := rootPath + return &zkIntfListener{ + path: p, + exit: make(chan struct{}), + client: client, + reg: reg, + adapterListener: adapterListener, + } +} + +func (z *zkIntfListener) Close() { + close(z.exit) + z.wg.Wait() +} + +func (z *zkIntfListener) WatchAndHandle() { + z.wg.Add(1) + go z.watch() +} + +func (z *zkIntfListener) watch() { + defer z.wg.Done() + var ( + failTimes int64 = 0 + delayTimer = time.NewTimer(ConnDelay * time.Duration(failTimes)) + ) + defer delayTimer.Stop() + for { + _, e, err := z.client.GetChildrenW(z.path) + // error handling + if err != nil { + failTimes++ + logger.Infof("watching (path{%s}) = error{%v}", z.path, err) + // Exit the watch if root node is in error + if err == zookeeper.ErrNilNode { + logger.Errorf("watching (path{%s}) got errNilNode,so exit listen", z.path) + return + } + if failTimes > MaxFailTimes { + logger.Errorf("Error happens on (path{%s}) exceed max fail times: %s,so exit listen", + z.path, MaxFailTimes) + return + } + delayTimer.Reset(ConnDelay * time.Duration(failTimes)) + <-delayTimer.C + continue + } + failTimes = 0 + if continueLoop := z.waitEventAndHandlePeriod(z.path, e); !continueLoop { + return + } + } +} + +func (z *zkIntfListener) waitEventAndHandlePeriod(path string, e <-chan zk.Event) bool { + tickerTTL := defaultTTL + ticker := time.NewTicker(tickerTTL) + defer ticker.Stop() + z.handleEvent(z.path) + for { + select { + case <-ticker.C: + z.handleEvent(z.path) + case zkEvent := <-e: + logger.Warnf("get a zookeeper e{type:%s, server:%s, path:%s, state:%d-%s, err:%s}", + zkEvent.Type.String(), zkEvent.Server, zkEvent.Path, zkEvent.State, zookeeper.StateToString(zkEvent.State), zkEvent.Err) + if zkEvent.Type != zk.EventNodeChildrenChanged { + return true + } + z.handleEvent(zkEvent.Path) + return true + case <-z.exit: + logger.Warnf("listen(path{%s}) goroutine exit now...", z.path) + return false + } + } +} + +func (z *zkIntfListener) handleEvent(basePath string) { + newChildren, err := z.client.GetChildren(basePath) + if err != nil { + logger.Errorf("Error when retrieving newChildren in path: %s, Error:%s", basePath, err.Error()) + } + for i := range newChildren { + if newChildren[i] == "metadata" { + continue + } + providerPath := path.Join(basePath, newChildren[i], providerCategory) + // TO-DO: modify here to only handle child that changed + providers, err := z.client.GetChildren(providerPath) + if err != nil { + logger.Warnf("Get provider %s failed due to %s", providerPath, err.Error()) + continue + } + srvUrl, err := common.NewURL(providers[0]) + if err != nil { + logger.Warnf("Parse provider service url %s failed due to %s", providers[0], err.Error()) + continue + } + if z.reg.GetSvcListener(srvUrl.ServiceKey()) != nil { + continue + } + l := newZkSrvListener(srvUrl, providerPath, z.client, z.adapterListener) + l.wg.Add(1) + go l.WatchAndHandle() + z.reg.SetSvcListener(srvUrl.ServiceKey(), l) + } +} diff --git a/pkg/adapter/dubboregistry/registry/zookeeper/registry.go b/pkg/adapter/dubboregistry/registry/zookeeper/registry.go new file mode 100644 index 000000000..1a2ff1860 --- /dev/null +++ b/pkg/adapter/dubboregistry/registry/zookeeper/registry.go @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 zookeeper + +import ( + "strings" + "time" +) + +import ( + "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/common" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/registry" + baseRegistry "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/registry/base" + zk "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/remoting/zookeeper" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +var ( + _ baseRegistry.FacadeRegistry = new(ZKRegistry) +) + +const ( + // RegistryZkClient zk client name + RegistryZkClient = "zk registry" + MaxFailTimes = 2 + ConnDelay = 3 * time.Second + defaultTTL = 10 * time.Minute +) + +func init() { + registry.SetRegistry(constant.Zookeeper, newZKRegistry) +} + +type ZKRegistry struct { + *baseRegistry.BaseRegistry + zkListeners map[registry.RegisteredType]registry.Listener + client *zk.ZooKeeperClient +} + +var _ registry.Registry = new(ZKRegistry) + +func newZKRegistry(regConfig model.Registry, adapterListener common.RegistryEventListener) (registry.Registry, error) { + var zkReg = &ZKRegistry{} + baseReg := baseRegistry.NewBaseRegistry(zkReg, adapterListener) + timeout, err := time.ParseDuration(regConfig.Timeout) + if err != nil { + return nil, errors.Errorf("Incorrect timeout configuration: %s", regConfig.Timeout) + } + client, eventChan, err := zk.NewZooKeeperClient(RegistryZkClient, strings.Split(regConfig.Address, ","), timeout) + if err != nil { + return nil, errors.Errorf("Initialize zookeeper client failed: %s", err.Error()) + } + client.RegisterHandler(eventChan) + zkReg.BaseRegistry = baseReg + zkReg.client = client + initZKListeners(zkReg) + return zkReg, nil +} + +func initZKListeners(reg *ZKRegistry) { + reg.zkListeners = make(map[registry.RegisteredType]registry.Listener) + reg.zkListeners[registry.RegisteredTypeInterface] = newZKIntfListener(reg.client, reg, reg.AdapterListener) +} + +func (r *ZKRegistry) GetClient() *zk.ZooKeeperClient { + return r.client +} + +// DoSubscribe is the implementation of subscription on the target registry. +func (r *ZKRegistry) DoSubscribe() error { + if err := r.interfaceSubscribe(); err != nil { + return err + } + return nil +} + +// To subscribe service level service discovery +func (r *ZKRegistry) interfaceSubscribe() error { + intfListener, ok := r.zkListeners[registry.RegisteredTypeInterface] + if !ok { + return errors.New("Listener for interface level registration does not initialized") + } + go intfListener.WatchAndHandle() + return nil +} + +// DoUnsubscribe stops monitoring the target registry. +func (r *ZKRegistry) DoUnsubscribe() error { + intfListener, ok := r.zkListeners[registry.RegisteredTypeInterface] + if !ok { + return errors.New("Listener for interface level registration does not initialized") + } + intfListener.Close() + for k, l := range r.GetAllSvcListener() { + l.Close() + r.RemoveSvcListener(k) + } + return nil +} diff --git a/pixiu/pkg/adapter/dubboregistry/registry/zookeeper/registry_test.go b/pkg/adapter/dubboregistry/registry/zookeeper/registry_test.go similarity index 98% rename from pixiu/pkg/adapter/dubboregistry/registry/zookeeper/registry_test.go rename to pkg/adapter/dubboregistry/registry/zookeeper/registry_test.go index 1df7636da..4a56f1c16 100644 --- a/pixiu/pkg/adapter/dubboregistry/registry/zookeeper/registry_test.go +++ b/pkg/adapter/dubboregistry/registry/zookeeper/registry_test.go @@ -28,7 +28,7 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) type DemoListener struct { diff --git a/pkg/adapter/dubboregistry/registry/zookeeper/service_listener.go b/pkg/adapter/dubboregistry/registry/zookeeper/service_listener.go new file mode 100644 index 000000000..779fa0844 --- /dev/null +++ b/pkg/adapter/dubboregistry/registry/zookeeper/service_listener.go @@ -0,0 +1,188 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 zookeeper + +import ( + "strings" + "sync" + "time" +) + +import ( + "dubbo.apache.org/dubbo-go/v3/common" + "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" + "github.com/dubbogo/go-zookeeper/zk" +) + +import ( + common2 "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/common" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/registry" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/remoting/zookeeper" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +var _ registry.Listener = new(serviceListener) + +// serviceListener normally monitors the /dubbo/[:url.service()]/providers +type serviceListener struct { + url *common.URL + path string + client *zookeeper.ZooKeeperClient + + exit chan struct{} + wg sync.WaitGroup + adapterListener common2.RegistryEventListener + registryMethod map[string]*config.Method + mutex sync.Mutex +} + +// newZkSrvListener creates a new zk service listener +func newZkSrvListener(url *common.URL, path string, client *zookeeper.ZooKeeperClient, adapterListener common2.RegistryEventListener) *serviceListener { + return &serviceListener{ + url: url, + path: path, + client: client, + exit: make(chan struct{}), + adapterListener: adapterListener, + registryMethod: make(map[string]*config.Method), + } +} + +func (zkl *serviceListener) WatchAndHandle() { + defer zkl.wg.Done() + + var ( + failTimes int64 = 0 + delayTimer = time.NewTimer(ConnDelay * time.Duration(failTimes)) + ) + + for { + children, e, err := zkl.client.GetChildrenW(zkl.path) + // error handling + if err != nil { + failTimes++ + logger.Infof("watching (path{%s}) = error{%v}", zkl.path, err) + // Exit the watch if root node is in error + if err == zookeeper.ErrNilNode { + logger.Errorf("watching (path{%s}) got errNilNode,so exit listen", zkl.path) + return + } + if failTimes > MaxFailTimes { + logger.Errorf("Error happens on (path{%s}) exceed max fail times: %s,so exit listen", + zkl.path, MaxFailTimes) + return + } + delayTimer.Reset(ConnDelay * time.Duration(failTimes)) + <-delayTimer.C + continue + } + failTimes = 0 + if continueLoop := zkl.waitEventAndHandlePeriod(children, e); !continueLoop { + return + } + } +} + +func (zkl *serviceListener) waitEventAndHandlePeriod(children []string, e <-chan zk.Event) bool { + tickerTTL := defaultTTL + ticker := time.NewTicker(tickerTTL) + defer ticker.Stop() + zkl.handleEvent() + + for { + select { + case <-ticker.C: + zkl.handleEvent() + case zkEvent := <-e: + logger.Warnf("get a zookeeper childEventCh{type:%s, server:%s, path:%s, state:%d-%s, err:%s}", + zkEvent.Type.String(), zkEvent.Server, zkEvent.Path, zkEvent.State, zookeeper.StateToString(zkEvent.State), zkEvent.Err) + ticker.Stop() + if zkEvent.Type != zk.EventNodeChildrenChanged { + return true + } + zkl.handleEvent() + return true + case <-zkl.exit: + logger.Warnf("listen(path{%s}) goroutine exit now...", zkl.path) + ticker.Stop() + return false + } + } +} + +// whenever it is called, the children node changed and refresh the api configuration. +func (zkl *serviceListener) handleEvent() { + children, err := zkl.client.GetChildren(zkl.path) + if err != nil { + // disable the API + bkConf, methods, _, _ := registry.ParseDubboString(zkl.url.String()) + apiPattern := registry.GetAPIPattern(bkConf) + for i := range methods { + path := strings.Join([]string{apiPattern, methods[i]}, constant.PathSlash) + if err := zkl.adapterListener.OnDeleteRouter(config.Resource{Path: path}); err != nil { + logger.Errorf("Error={%s} when try to remove API by path: %s", err.Error(), path) + } + } + return + } + zkl.url, err = common.NewURL(children[0]) + if err != nil { + logger.Warnf("Parse service path failed: %s", children[0]) + } + bkConfig, methods, location, err := registry.ParseDubboString(children[0]) + if err != nil { + logger.Warnf("Parse dubbo interface provider %s failed; due to \n %s", children[0], err.Error()) + return + } + if len(bkConfig.ApplicationName) == 0 || len(bkConfig.Interface) == 0 { + return + } + + mappingParams := []config.MappingParam{ + { + Name: "requestBody.values", + MapTo: "opt.values", + }, + { + Name: "requestBody.types", + MapTo: "opt.types", + }, + } + apiPattern := registry.GetAPIPattern(bkConfig) + zkl.mutex.Lock() + defer zkl.mutex.Unlock() + for i := range methods { + api := registry.CreateAPIConfig(apiPattern, location, bkConfig, methods[i], mappingParams) + key := api.URLPattern + ":" + string(api.Method.HTTPVerb) + if _, ok := zkl.registryMethod[key]; ok { + return + } + if err := zkl.adapterListener.OnAddAPI(api); err != nil { + logger.Errorf("Error={%s} happens when try to add api %s", err.Error(), api.Path) + } else { + zkl.registryMethod[key] = &api.Method + } + } +} + +// Close closes this listener +func (zkl *serviceListener) Close() { + close(zkl.exit) + zkl.wg.Wait() +} diff --git a/pixiu/pkg/adapter/dubboregistry/registrycenter.go b/pkg/adapter/dubboregistry/registrycenter.go similarity index 86% rename from pixiu/pkg/adapter/dubboregistry/registrycenter.go rename to pkg/adapter/dubboregistry/registrycenter.go index 7b6472749..3d24f9aec 100644 --- a/pixiu/pkg/adapter/dubboregistry/registrycenter.go +++ b/pkg/adapter/dubboregistry/registrycenter.go @@ -27,14 +27,14 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/registry" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/registry/nacos" - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/dubboregistry/registry/zookeeper" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/adapter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/registry" + _ "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/registry/nacos" + _ "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry/registry/zookeeper" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/adapter" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/server" ) func init() { diff --git a/pkg/adapter/dubboregistry/remoting/zookeeper/client.go b/pkg/adapter/dubboregistry/remoting/zookeeper/client.go new file mode 100644 index 000000000..d616b08dc --- /dev/null +++ b/pkg/adapter/dubboregistry/remoting/zookeeper/client.go @@ -0,0 +1,299 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 zookeeper + +import ( + "strings" + "sync" + "time" +) + +import ( + "github.com/dubbogo/go-zookeeper/zk" + "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +var ( + // ErrNilZkClientConn no conn error + ErrNilZkClientConn = errors.New("zookeeper Client{conn} is nil") + // ErrNilChildren no children error + ErrNilChildren = errors.Errorf("has none children") + // ErrNilNode no node error + ErrNilNode = errors.Errorf("node does not exist") +) + +// Options defines the client option. +type Options struct { + zkName string + ts *zk.TestCluster +} + +// Option defines the function to load the options +type Option func(*Options) + +// WithZkName sets zk client name +func WithZkName(name string) Option { + return func(opt *Options) { + opt.zkName = name + } +} + +// ZooKeeperClient represents zookeeper client Configuration +type ZooKeeperClient struct { + name string + ZkAddrs []string + sync.RWMutex // for conn + conn *zk.Conn + Timeout time.Duration + exit chan struct{} + Wait sync.WaitGroup + + eventRegistry map[string][]chan zk.Event + eventRegistryLock sync.RWMutex +} + +func NewZooKeeperClient(name string, zkAddrs []string, timeout time.Duration) (*ZooKeeperClient, <-chan zk.Event, error) { + var ( + err error + event <-chan zk.Event + z *ZooKeeperClient + ) + + z = &ZooKeeperClient{ + name: name, + ZkAddrs: zkAddrs, + Timeout: timeout, + exit: make(chan struct{}), + eventRegistry: make(map[string][]chan zk.Event), + } + // connect to zookeeper + z.conn, event, err = zk.Connect(zkAddrs, timeout) + if err != nil { + return nil, nil, errors.WithMessagef(err, "zk.Connect(zkAddrs:%+v)", zkAddrs) + } + + return z, event, nil +} + +// StateToString will transfer zk state to string +func StateToString(state zk.State) string { + switch state { + case zk.StateDisconnected: + return "zookeeper disconnected" + case zk.StateConnecting: + return "zookeeper connecting" + case zk.StateAuthFailed: + return "zookeeper auth failed" + case zk.StateConnectedReadOnly: + return "zookeeper connect readonly" + case zk.StateSaslAuthenticated: + return "zookeeper sasl authenticated" + case zk.StateExpired: + return "zookeeper connection expired" + case zk.StateConnected: + return "zookeeper connected" + case zk.StateHasSession: + return "zookeeper has Session" + case zk.StateUnknown: + return "zookeeper unknown state" + default: + return state.String() + } +} + +func (z *ZooKeeperClient) RegisterHandler(event <-chan zk.Event) { + z.Wait.Add(1) + go z.HandleZkEvent(event) +} + +// ExistW is a wrapper to the zk connection conn.ExistsW +func (z *ZooKeeperClient) ExistW(zkPath string) (<-chan zk.Event, error) { + conn := z.getConn() + if conn == nil { + return nil, ErrNilZkClientConn + } + exist, _, watcher, err := conn.ExistsW(zkPath) + if err != nil { + return nil, errors.WithMessagef(err, "zk.ExistsW(path:%s)", zkPath) + } + if !exist { + return nil, errors.WithMessagef(ErrNilNode, "zkClient{%s} App zk path{%s} does not exist.", z.name, zkPath) + } + return watcher.EvtCh, nil +} + +// HandleZkEvent handles zookeeper events +func (z *ZooKeeperClient) HandleZkEvent(s <-chan zk.Event) { + var ( + state int + event zk.Event + ) + + defer func() { + z.Wait.Done() + logger.Infof("zk{path:%v, name:%s} connection goroutine game over.", z.ZkAddrs, z.name) + }() + + for { + select { + case <-z.exit: + return + case event = <-s: + logger.Infof("client{%s} get a zookeeper event{type:%s, server:%s, path:%s, state:%d-%s, err:%v}", + z.name, event.Type, event.Server, event.Path, event.State, StateToString(event.State), event.Err) + switch event.State { + case zk.StateDisconnected: + logger.Warnf("zk{addr:%s} state is StateDisconnected, so close the zk client{name:%s}.", z.ZkAddrs, z.name) + z.Destroy() + return + case zk.StateConnected: + logger.Infof("zkClient{%s} get zk node changed event{path:%s}", z.name, event.Path) + z.eventRegistryLock.RLock() + for path, a := range z.eventRegistry { + if strings.HasPrefix(event.Path, path) { + logger.Infof("send event{state:zk.EventNodeDataChange, Path:%s} notify event to path{%s} related listener", + event.Path, path) + for _, e := range a { + e <- event + } + } + } + z.eventRegistryLock.RUnlock() + case zk.StateConnecting, zk.StateHasSession: + if state == (int)(zk.StateHasSession) { + continue + } + z.eventRegistryLock.RLock() + if a, ok := z.eventRegistry[event.Path]; ok && 0 < len(a) { + for _, e := range a { + e <- event + } + } + z.eventRegistryLock.RUnlock() + } + state = (int)(event.State) + } + } +} + +// getConn gets zookeeper connection safely +func (z *ZooKeeperClient) getConn() *zk.Conn { + z.RLock() + defer z.RUnlock() + return z.conn +} + +func (z *ZooKeeperClient) stop() bool { + select { + case <-z.exit: + return true + default: + close(z.exit) + } + + return false +} + +// GetChildren gets children by @path +func (z *ZooKeeperClient) GetChildren(path string) ([]string, error) { + conn := z.getConn() + if conn == nil { + return nil, ErrNilZkClientConn + } + children, stat, err := conn.Children(path) + if err != nil { + if err == zk.ErrNoNode { + return nil, errors.WithMessagef(ErrNilNode, "path{%s} does not exist", path) + } + logger.Errorf("zk.Children(path{%s}) = error(%v)", path, errors.WithStack(err)) + return nil, errors.WithMessagef(err, "zk.Children(path:%s)", path) + } + if stat.NumChildren == 0 { + return nil, ErrNilChildren + } + return children, nil +} + +// GetChildrenW gets children watch by @path +func (z *ZooKeeperClient) GetChildrenW(path string) ([]string, <-chan zk.Event, error) { + conn := z.getConn() + if conn == nil { + return nil, nil, ErrNilZkClientConn + } + children, stat, watcher, err := conn.ChildrenW(path) + if err != nil { + if err == zk.ErrNoChildrenForEphemerals { + return nil, nil, ErrNilChildren + } + if err == zk.ErrNoNode { + return nil, nil, ErrNilNode + } + return nil, nil, errors.WithMessagef(err, "zk.ChildrenW(path:%s)", path) + } + if stat == nil { + return nil, nil, errors.Errorf("path{%s} get stat is nil", path) + } + //if len(children) == 0 { + // return nil, nil, ErrNilChildren + //} + + return children, watcher.EvtCh, nil +} + +// GetContent gets content by @path +func (z *ZooKeeperClient) GetContent(path string) ([]byte, error) { + var ( + data []byte + ) + conn := z.getConn() + if conn == nil { + return nil, errors.New("ZooKeeper client has no connection") + } + data, _, err := conn.Get(path) + if err != nil { + if err == zk.ErrNoNode { + return nil, errors.Errorf("path{%s} does not exist", path) + } + logger.Errorf("zk.Data(path{%s}) = error(%v)", path, errors.WithStack(err)) + return nil, errors.WithMessagef(err, "zk.Data(path:%s)", path) + } + return data, nil +} + +func (z *ZooKeeperClient) GetConnState() zk.State { + conn := z.getConn() + if conn != nil { + return conn.State() + } + return zk.StateExpired +} + +func (z *ZooKeeperClient) Destroy() { + z.stop() + z.Lock() + conn := z.conn + z.conn = nil + z.Unlock() + if conn != nil { + conn.Close() + } +} diff --git a/pixiu/pkg/adapter/springcloud/cloud.go b/pkg/adapter/springcloud/cloud.go similarity index 94% rename from pixiu/pkg/adapter/springcloud/cloud.go rename to pkg/adapter/springcloud/cloud.go index 3a6f6a4db..467a9cdde 100644 --- a/pixiu/pkg/adapter/springcloud/cloud.go +++ b/pkg/adapter/springcloud/cloud.go @@ -28,14 +28,14 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/springcloud/servicediscovery" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/springcloud/servicediscovery/nacos" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/springcloud/servicediscovery/zookeeper" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/adapter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/springcloud/servicediscovery" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/springcloud/servicediscovery/nacos" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/springcloud/servicediscovery/zookeeper" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/adapter" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/server" ) const ( diff --git a/pixiu/pkg/adapter/springcloud/common/common.go b/pkg/adapter/springcloud/common/common.go similarity index 100% rename from pixiu/pkg/adapter/springcloud/common/common.go rename to pkg/adapter/springcloud/common/common.go diff --git a/pixiu/pkg/adapter/springcloud/servicediscovery/nacos/nacos.go b/pkg/adapter/springcloud/servicediscovery/nacos/nacos.go similarity index 96% rename from pixiu/pkg/adapter/springcloud/servicediscovery/nacos/nacos.go rename to pkg/adapter/springcloud/servicediscovery/nacos/nacos.go index e25f988c6..16a796977 100644 --- a/pixiu/pkg/adapter/springcloud/servicediscovery/nacos/nacos.go +++ b/pkg/adapter/springcloud/servicediscovery/nacos/nacos.go @@ -33,10 +33,10 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/springcloud/servicediscovery" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/remote/nacos" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/springcloud/servicediscovery" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/remote/nacos" ) type nacosServiceDiscovery struct { diff --git a/pkg/adapter/springcloud/servicediscovery/servicediscovery.go b/pkg/adapter/springcloud/servicediscovery/servicediscovery.go new file mode 100644 index 000000000..d85bc7f80 --- /dev/null +++ b/pkg/adapter/springcloud/servicediscovery/servicediscovery.go @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 servicediscovery + +import ( + "fmt" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +type ( + // ServiceInstance the service instance info fetched from registry such as nacos consul + ServiceInstance struct { + ID string + ServiceName string + // host:port + Host string + Port int + Healthy bool + CLusterName string + Enable bool + // extra info such as label or other meta data + Metadata map[string]string + } + + ServiceEventListener interface { + OnAddServiceInstance(r *ServiceInstance) + OnDeleteServiceInstance(r *ServiceInstance) + OnUpdateServiceInstance(r *ServiceInstance) + GetServiceNames() []string + } + + ServiceDiscovery interface { + + // QueryAllServices get all service from remote registry center + QueryAllServices() ([]ServiceInstance, error) + + // QueryServicesByName get service by serviceName from remote registry center + QueryServicesByName(serviceNames []string) ([]ServiceInstance, error) + + // Register register to remote registry center + Register() error + + // UnRegister unregister to remote registry center + UnRegister() error + + // Subscribe subscribe the service event from remote registry center + Subscribe() error + + // Unsubscribe unsubscribe from remote registry center + Unsubscribe() error + } +) + +func (i *ServiceInstance) GetUniqKey() string { + return i.ServiceName + i.Host + fmt.Sprint(i.Port) +} + +// ToEndpoint +func (i *ServiceInstance) ToEndpoint() *model.Endpoint { + a := model.SocketAddress{Address: i.Host, Port: i.Port} + return &model.Endpoint{ID: i.ID, Address: a, Name: i.ServiceName, Metadata: i.Metadata} +} + +// ToRoute route ID is cluster name, so equal with endpoint name and routerMatch prefix is also service name +func (i *ServiceInstance) ToRoute() *model.Router { + rm := model.NewRouterMatchPrefix(i.ServiceName) + ra := model.RouteAction{Cluster: i.ServiceName} + return &model.Router{ID: i.ServiceName, Match: rm, Route: ra} +} diff --git a/pkg/adapter/springcloud/servicediscovery/zookeeper/application_listener.go b/pkg/adapter/springcloud/servicediscovery/zookeeper/application_listener.go new file mode 100644 index 000000000..faa22c9ad --- /dev/null +++ b/pkg/adapter/springcloud/servicediscovery/zookeeper/application_listener.go @@ -0,0 +1,213 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 zookeeper + +import ( + "strings" + "sync" + "time" +) + +import ( + "github.com/dubbogo/go-zookeeper/zk" + gzk "github.com/dubbogo/gost/database/kv/zk" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/adapter/springcloud/common" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/remote/zookeeper" +) + +type zkAppListener struct { + servicesPath string + exit chan struct{} + svcListeners *SvcListeners + wg sync.WaitGroup + + ds *zookeeperDiscovery +} + +func newZkAppListener(ds *zookeeperDiscovery) zookeeper.Listener { + return &zkAppListener{ + servicesPath: ds.basePath, + exit: make(chan struct{}), + svcListeners: &SvcListeners{listeners: make(map[string]zookeeper.Listener), listenerLock: sync.Mutex{}}, + ds: ds, + } +} + +func (z *zkAppListener) Close() { + for k, listener := range z.svcListeners.GetAllListener() { + z.svcListeners.RemoveListener(k) + listener.Close() + } + close(z.exit) + z.wg.Wait() +} + +func (z *zkAppListener) WatchAndHandle() { + z.wg.Add(1) + go z.watch() +} + +func (z *zkAppListener) watch() { + defer z.wg.Done() + + var ( + failTimes int64 = 0 + delayTimer = time.NewTimer(ConnDelay * time.Duration(failTimes)) + ) + defer delayTimer.Stop() + for { + children, e, err := z.ds.getClient().GetChildrenW(z.servicesPath) + if err != nil { + failTimes++ + logger.Infof("watching (path{%s}) = error{%v}", z.servicesPath, err) + if err == zk.ErrNoNode { + logger.Errorf("watching (path{%s}) got errNilNode,so exit listen", z.servicesPath) + return + } + if failTimes > MaxFailTimes { + logger.Errorf("Error happens on (path{%s}) exceed max fail times: %s,so exit listen", + z.servicesPath, MaxFailTimes) + return + } + + delayTimer.Reset(ConnDelay * time.Duration(failTimes)) + <-delayTimer.C + continue + } + failTimes = 0 + if continueLoop := z.watchEventHandle(children, e); !continueLoop { + return + } + } +} + +func (z *zkAppListener) watchEventHandle(children []string, e <-chan zk.Event) bool { + tickerTTL := defaultTTL + ticker := time.NewTicker(tickerTTL) + defer ticker.Stop() + z.handleEvent(children) + for { + select { + case <-ticker.C: + z.handleEvent(children) + case zkEvent := <-e: + logger.Debugf("get a zookeeper e{type:%s, server:%s, path:%s, state:%d-%s, err:%s}", + zkEvent.Type.String(), zkEvent.Server, zkEvent.Path, zkEvent.State, gzk.StateToString(zkEvent.State), zkEvent.Err) + z.handleEvent(children) + return true + case <-z.exit: + logger.Warnf("listen(path{%s}) goroutine exit now...", z.servicesPath) + return false + } + } +} + +func (z *zkAppListener) handleEvent(children []string) { + + fetchChildren, err := z.ds.getClient().GetChildren(z.servicesPath) + + if err != nil { + // todo refactor gost zk, make it return the definite err + if strings.Contains(err.Error(), "none children") { + logger.Debugf("%s get nodes from zookeeper fail: %s", common.ZKLogDiscovery, err.Error()) + } else { + logger.Warnf("Error when retrieving newChildren in path: %s, Error:%s", z.servicesPath, err.Error()) + } + } + + discovery := z.ds + serviceMap := discovery.getServiceMap() + + // del services + for sn := range serviceMap { + + if !contains(fetchChildren, sn) { + + // service zk event listener + serviceNodePath := strings.Join([]string{z.servicesPath, sn}, constant.PathSlash) + z.svcListeners.RemoveListener(serviceNodePath) + + // service cluster + for _, instance := range serviceMap[sn] { + _, _ = discovery.delServiceInstance(instance) + } + } + } + + for _, serviceName := range fetchChildren { + serviceNodePath := strings.Join([]string{z.servicesPath, serviceName}, constant.PathSlash) + instances := serviceMap[serviceName] + if len(instances) == 0 { + z.svcListeners.RemoveListener(serviceNodePath) + } + if z.svcListeners.GetListener(serviceNodePath) != nil { + continue + } + l := newApplicationServiceListener(serviceNodePath, serviceName, discovery) + l.wg.Add(1) + go l.WatchAndHandle() + z.svcListeners.SetListener(serviceNodePath, l) + } +} + +type SvcListeners struct { + listeners map[string]zookeeper.Listener + listenerLock sync.Mutex +} + +func (s *SvcListeners) GetListener(id string) zookeeper.Listener { + s.listenerLock.Lock() + defer s.listenerLock.Unlock() + listener, ok := s.listeners[id] + if !ok { + return nil + } + return listener +} + +func (s *SvcListeners) SetListener(id string, listener zookeeper.Listener) { + s.listenerLock.Lock() + defer s.listenerLock.Unlock() + s.listeners[id] = listener +} + +func (s *SvcListeners) RemoveListener(id string) { + s.listenerLock.Lock() + defer s.listenerLock.Unlock() + delete(s.listeners, id) +} + +func (s *SvcListeners) GetAllListener() map[string]zookeeper.Listener { + s.listenerLock.Lock() + defer s.listenerLock.Unlock() + return s.listeners +} + +func contains(elems []string, v string) bool { + for _, s := range elems { + if v == s { + return true + } + } + return false +} diff --git a/pkg/adapter/springcloud/servicediscovery/zookeeper/service_listener.go b/pkg/adapter/springcloud/servicediscovery/zookeeper/service_listener.go new file mode 100644 index 000000000..728726f82 --- /dev/null +++ b/pkg/adapter/springcloud/servicediscovery/zookeeper/service_listener.go @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 zookeeper + +import ( + "strings" + "sync" + "time" +) + +import ( + "github.com/dubbogo/go-zookeeper/zk" + gzk "github.com/dubbogo/gost/database/kv/zk" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/adapter/springcloud/common" + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +type applicationServiceListener struct { + servicePath string + serviceName string + exit chan struct{} + wg sync.WaitGroup + + ds *zookeeperDiscovery +} + +func newApplicationServiceListener(path string, serviceName string, ds *zookeeperDiscovery) *applicationServiceListener { + return &applicationServiceListener{ + servicePath: path, + exit: make(chan struct{}), + ds: ds, + serviceName: serviceName, + } +} + +func (asl *applicationServiceListener) WatchAndHandle() { + defer asl.wg.Done() + + var ( + failTimes int64 = 0 + delayTimer = time.NewTimer(ConnDelay * time.Duration(failTimes)) + ) + defer delayTimer.Stop() + for { + children, e, err := asl.ds.getClient().GetChildrenW(asl.servicePath) + if err != nil { + failTimes++ + logger.Infof("watching (path{%s}) = error{%v}", asl.servicePath, err) + if err == zk.ErrNoChildrenForEphemerals { + return + } + if err == zk.ErrNoNode { + logger.Errorf("watching (path{%s}) got errNilNode,so exit listen", asl.servicePath) + return + } + if failTimes > MaxFailTimes { + logger.Errorf("Error happens on (path{%s}) exceed max fail times: %v,so exit listen", asl.servicePath, MaxFailTimes) + return + } + delayTimer.Reset(ConnDelay * time.Duration(failTimes)) + <-delayTimer.C + continue + } + failTimes = 0 + if continueLoop := asl.watchEventHandle(children, e); !continueLoop { + return + } + } +} + +func (asl *applicationServiceListener) watchEventHandle(children []string, e <-chan zk.Event) bool { + tickerTTL := defaultTTL + ticker := time.NewTicker(tickerTTL) + defer ticker.Stop() + asl.handleEvent(children) + for { + select { + case <-ticker.C: + asl.handleEvent(children) + case zkEvent := <-e: + logger.Debugf("get a zookeeper e{type:%s, server:%s, path:%s, state:%d-%s, err:%s}", + zkEvent.Type.String(), zkEvent.Server, zkEvent.Path, zkEvent.State, gzk.StateToString(zkEvent.State), zkEvent.Err) + asl.handleEvent(children) + return true + case <-asl.exit: + logger.Warnf("listen(path{%s}) goroutine exit now...", asl.servicePath) + return false + } + } +} + +func (asl *applicationServiceListener) handleEvent(children []string) { + + fetchChildren, err := asl.ds.getClient().GetChildren(asl.servicePath) + if err != nil { + // todo refactor gost zk, make it return the definite err + if strings.Contains(err.Error(), "none children") { + logger.Debugf("%s get nodes from zookeeper fail: %s", common.ZKLogDiscovery, err.Error()) + } else { + logger.Warnf("%s Error when retrieving service node [%s] in path: %s, Error:%s", common.ZKLogDiscovery, asl.serviceName, asl.servicePath, err.Error()) + } + } + discovery := asl.ds + serviceMap := discovery.getServiceMap() + instanceMap := discovery.instanceMap + + func() { + for _, id := range fetchChildren { + serviceInstance, err := discovery.queryForInstance(asl.serviceName, id) + if err != nil { + logger.Errorf("add service: %s, instance: %s has error: ", asl.serviceName, id, err.Error()) + continue + } + if instanceMap[id] == nil { + _, _ = discovery.addServiceInstance(serviceInstance) + } else { + _, _ = discovery.updateServiceInstance(serviceInstance) + } + } + }() + + func() { + serviceInstances := serviceMap[asl.serviceName] + oldInsId := []string{} + for _, instance := range serviceInstances { + oldInsId = append(oldInsId, instance.ID) + } + if delInstanceIds := Diff(oldInsId, fetchChildren); delInstanceIds != nil { + for _, id := range delInstanceIds { + _, _ = discovery.delServiceInstance(instanceMap[id]) + } + } + }() +} + +// Close closes this listener +func (asl *applicationServiceListener) Close() { + close(asl.exit) + asl.wg.Wait() +} diff --git a/pixiu/pkg/adapter/springcloud/servicediscovery/zookeeper/zk_discovery.go b/pkg/adapter/springcloud/servicediscovery/zookeeper/zk_discovery.go similarity index 96% rename from pixiu/pkg/adapter/springcloud/servicediscovery/zookeeper/zk_discovery.go rename to pkg/adapter/springcloud/servicediscovery/zookeeper/zk_discovery.go index 901e6aff9..844fac0f4 100644 --- a/pixiu/pkg/adapter/springcloud/servicediscovery/zookeeper/zk_discovery.go +++ b/pkg/adapter/springcloud/servicediscovery/zookeeper/zk_discovery.go @@ -30,11 +30,11 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/springcloud/common" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/adapter/springcloud/servicediscovery" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/remote/zookeeper" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/springcloud/common" + "github.com/apache/dubbo-go-pixiu/pkg/adapter/springcloud/servicediscovery" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/remote/zookeeper" ) const ( diff --git a/pkg/adsc/adsc.go b/pkg/adsc/adsc.go deleted file mode 100644 index 12ec6c7e5..000000000 --- a/pkg/adsc/adsc.go +++ /dev/null @@ -1,1299 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package adsc - -import ( - "context" - "crypto/tls" - "crypto/x509" - "encoding/json" - "fmt" - "math" - "net" - "os" - "sort" - "strings" - "sync" - "time" -) - -import ( - "github.com/cenkalti/backoff/v4" - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "github.com/envoyproxy/go-control-plane/pkg/conversion" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/protobuf/proto" - any "google.golang.org/protobuf/types/known/anypb" - pstruct "google.golang.org/protobuf/types/known/structpb" - mcp "istio.io/api/mcp/v1alpha1" - "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/log" -) - -import ( - mem "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/memory" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/security" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -const ( - defaultClientMaxReceiveMessageSize = math.MaxInt32 - defaultInitialConnWindowSize = 1024 * 1024 // default gRPC InitialWindowSize - defaultInitialWindowSize = 1024 * 1024 // default gRPC ConnWindowSize -) - -// Config for the ADS connection. -type Config struct { - // Namespace defaults to 'default' - Namespace string - - // Workload defaults to 'test' - Workload string - - // Revision for this control plane instance. We will only read configs that match this revision. - Revision string - - // Meta includes additional metadata for the node - Meta *pstruct.Struct - - Locality *core.Locality - - // NodeType defaults to sidecar. "ingress" and "router" are also supported. - NodeType string - - // IP is currently the primary key used to locate inbound configs. It is sent by client, - // must match a known endpoint IP. Tests can use a ServiceEntry to register fake IPs. - IP string - - // CertDir is the directory where mTLS certs are configured. - // If CertDir and Secret are empty, an insecure connection will be used. - // TODO: implement SecretManager for cert dir - CertDir string - - // Secrets is the interface used for getting keys and rootCA. - SecretManager security.SecretManager - - // For getting the certificate, using same code as SDS server. - // Either the JWTPath or the certs must be present. - JWTPath string - - // XDSSAN is the expected SAN of the XDS server. If not set, the ProxyConfig.DiscoveryAddress is used. - XDSSAN string - - // XDSRootCAFile explicitly set the root CA to be used for the XDS connection. - // Mirrors Envoy file. - XDSRootCAFile string - - // RootCert contains the XDS root certificate. Used mainly for tests, apps will normally use - // XDSRootCAFile - RootCert []byte - - // InsecureSkipVerify skips client verification the server's certificate chain and host name. - InsecureSkipVerify bool - - // InitialDiscoveryRequests is a list of resources to watch at first, represented as URLs (for new XDS resource naming) - // or type URLs. - InitialDiscoveryRequests []*discovery.DiscoveryRequest - - // BackoffPolicy determines the reconnect policy. Based on MCP client. - BackoffPolicy backoff.BackOff - - // ResponseHandler will be called on each DiscoveryResponse. - // TODO: mirror Generator, allow adding handler per type - ResponseHandler ResponseHandler - - GrpcOpts []grpc.DialOption -} - -func DefaultGrpcDialOptions() []grpc.DialOption { - return []grpc.DialOption{ - // TODO(SpecialYang) maybe need to make it configurable. - grpc.WithInitialWindowSize(int32(defaultInitialWindowSize)), - grpc.WithInitialConnWindowSize(int32(defaultInitialConnWindowSize)), - grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaultClientMaxReceiveMessageSize)), - } -} - -// ADSC implements a basic client for ADS, for use in stress tests and tools -// or libraries that need to connect to Istio pilot or other ADS servers. -type ADSC struct { - // Stream is the GRPC connection stream, allowing direct GRPC send operations. - // Set after Dial is called. - stream discovery.AggregatedDiscoveryService_StreamAggregatedResourcesClient - // xds client used to create a stream - client discovery.AggregatedDiscoveryServiceClient - conn *grpc.ClientConn - - // Indicates if the ADSC client is closed - closed bool - - // NodeID is the node identity sent to Pilot. - nodeID string - - url string - - watchTime time.Time - - // InitialLoad tracks the time to receive the initial configuration. - InitialLoad time.Duration - - // httpListeners contains received listeners with a http_connection_manager filter. - httpListeners map[string]*listener.Listener - - // tcpListeners contains all listeners of type TCP (not-HTTP) - tcpListeners map[string]*listener.Listener - - // All received clusters of type eds, keyed by name - edsClusters map[string]*cluster.Cluster - - // All received clusters of no-eds type, keyed by name - clusters map[string]*cluster.Cluster - - // All received routes, keyed by route name - routes map[string]*route.RouteConfiguration - - // All received endpoints, keyed by cluster name - eds map[string]*endpoint.ClusterLoadAssignment - - // Metadata has the node metadata to send to pilot. - // If nil, the defaults will be used. - Metadata *pstruct.Struct - - // Updates includes the type of the last update received from the server. - Updates chan string - errChan chan error - XDSUpdates chan *discovery.DiscoveryResponse - VersionInfo map[string]string - - // Last received message, by type - Received map[string]*discovery.DiscoveryResponse - - mutex sync.RWMutex - - Mesh *v1alpha1.MeshConfig - - // Retrieved configurations can be stored using the common istio model interface. - Store model.ConfigStore - - // Retrieved endpoints can be stored in the memory registry. This is used for CDS and EDS responses. - Registry *memory.ServiceDiscovery - - // LocalCacheDir is set to a base name used to save fetched resources. - // If set, each update will be saved. - // TODO: also load at startup - so we can support warm up in init-container, and survive - // restarts. - LocalCacheDir string - - // RecvWg is for letting goroutines know when the goroutine handling the ADS stream finishes. - RecvWg sync.WaitGroup - - cfg *Config - - // sendNodeMeta is set to true if the connection is new - and we need to send node meta., - sendNodeMeta bool - - sync map[string]time.Time - Locality *core.Locality -} - -type ResponseHandler interface { - HandleResponse(con *ADSC, response *discovery.DiscoveryResponse) -} - -// jsonMarshalProtoWithName wraps a proto.Message with name so it can be marshaled with the standard encoding/json library -type jsonMarshalProtoWithName struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Message proto.Message -} - -func (p jsonMarshalProtoWithName) MarshalJSON() ([]byte, error) { - strSer, serr := protomarshal.ToJSONWithIndent(p.Message, " ") - if serr != nil { - adscLog.Warnf("Error for marshaling [%s]: %v", p.Name, serr) - return []byte(""), serr - } - serialItem := []byte("{\"" + p.Name + "\":" + strSer + "}") - return serialItem, nil -} - -var adscLog = log.RegisterScope("adsc", "adsc debugging", 0) - -func NewWithBackoffPolicy(discoveryAddr string, opts *Config, backoffPolicy backoff.BackOff) (*ADSC, error) { - adsc, err := New(discoveryAddr, opts) - if err != nil { - return nil, err - } - adsc.cfg.BackoffPolicy = backoffPolicy - return adsc, err -} - -// New creates a new ADSC, maintaining a connection to an XDS server. -// Will: -// - get certificate using the Secret provider, if CertRequired -// - connect to the XDS server specified in ProxyConfig -// - send initial request for watched resources -// - wait for response from XDS server -// - on success, start a background thread to maintain the connection, with exp. backoff. -func New(discoveryAddr string, opts *Config) (*ADSC, error) { - if opts == nil { - opts = &Config{} - } - // We want to recreate stream - if opts.BackoffPolicy == nil { - opts.BackoffPolicy = backoff.NewExponentialBackOff() - } - adsc := &ADSC{ - Updates: make(chan string, 100), - XDSUpdates: make(chan *discovery.DiscoveryResponse, 100), - VersionInfo: map[string]string{}, - url: discoveryAddr, - Received: map[string]*discovery.DiscoveryResponse{}, - RecvWg: sync.WaitGroup{}, - cfg: opts, - sync: map[string]time.Time{}, - errChan: make(chan error, 10), - } - - if opts.Namespace == "" { - opts.Namespace = "default" - } - if opts.NodeType == "" { - opts.NodeType = "sidecar" - } - if opts.IP == "" { - opts.IP = getPrivateIPIfAvailable().String() - } - if opts.Workload == "" { - opts.Workload = "test-1" - } - adsc.Metadata = opts.Meta - adsc.Locality = opts.Locality - - adsc.nodeID = fmt.Sprintf("%s~%s~%s.%s~%s.svc.%s", opts.NodeType, opts.IP, - opts.Workload, opts.Namespace, opts.Namespace, constants.DefaultKubernetesDomain) - - if err := adsc.Dial(); err != nil { - return nil, err - } - - return adsc, nil -} - -// Dial connects to a ADS server, with optional MTLS authentication if a cert dir is specified. -func (a *ADSC) Dial() error { - opts := a.cfg - - defaultGrpcDialOptions := DefaultGrpcDialOptions() - var grpcDialOptions []grpc.DialOption - grpcDialOptions = append(grpcDialOptions, defaultGrpcDialOptions...) - grpcDialOptions = append(grpcDialOptions, opts.GrpcOpts...) - - var err error - // If we need MTLS - CertDir or Secrets provider is set. - if len(opts.CertDir) > 0 || opts.SecretManager != nil { - tlsCfg, err := a.tlsConfig() - if err != nil { - return err - } - creds := credentials.NewTLS(tlsCfg) - grpcDialOptions = append(grpcDialOptions, grpc.WithTransportCredentials(creds)) - } - - if len(grpcDialOptions) == len(defaultGrpcDialOptions) { - // Only disable transport security if the user didn't supply custom dial options - grpcDialOptions = append(grpcDialOptions, grpc.WithTransportCredentials(insecure.NewCredentials())) - } - - a.conn, err = grpc.Dial(a.url, grpcDialOptions...) - if err != nil { - return err - } - return nil -} - -// Returns a private IP address, or unspecified IP (0.0.0.0) if no IP is available -func getPrivateIPIfAvailable() net.IP { - addrs, _ := net.InterfaceAddrs() - for _, addr := range addrs { - var ip net.IP - switch v := addr.(type) { - case *net.IPNet: - ip = v.IP - case *net.IPAddr: - ip = v.IP - default: - continue - } - if !ip.IsLoopback() { - return ip - } - } - return net.IPv4zero -} - -func (a *ADSC) tlsConfig() (*tls.Config, error) { - var clientCerts []tls.Certificate - var serverCABytes []byte - var err error - - getClientCertificate := getClientCertFn(a.cfg) - - // Load the root CAs - if a.cfg.RootCert != nil { - serverCABytes = a.cfg.RootCert - } else if a.cfg.XDSRootCAFile != "" { - serverCABytes, err = os.ReadFile(a.cfg.XDSRootCAFile) - } else if a.cfg.SecretManager != nil { - // This is a bit crazy - we could just use the file - rootCA, err := a.cfg.SecretManager.GenerateSecret(security.RootCertReqResourceName) - if err != nil { - return nil, err - } - - serverCABytes = rootCA.RootCert - } else if a.cfg.CertDir != "" { - serverCABytes, err = os.ReadFile(a.cfg.CertDir + "/root-cert.pem") - if err != nil { - return nil, err - } - } - - serverCAs := x509.NewCertPool() - if ok := serverCAs.AppendCertsFromPEM(serverCABytes); !ok { - return nil, err - } - - shost, _, _ := net.SplitHostPort(a.url) - if a.cfg.XDSSAN != "" { - shost = a.cfg.XDSSAN - } - - return &tls.Config{ - GetClientCertificate: getClientCertificate, - Certificates: clientCerts, - RootCAs: serverCAs, - ServerName: shost, - InsecureSkipVerify: a.cfg.InsecureSkipVerify, - }, nil -} - -// Close the stream. -func (a *ADSC) Close() { - a.mutex.Lock() - _ = a.conn.Close() - a.closed = true - a.mutex.Unlock() -} - -// Run will create a new stream using the existing grpc client connection and send the initial xds requests. -// And then it will run a go routine receiving and handling xds response. -// Note: it is non blocking -func (a *ADSC) Run() error { - var err error - a.client = discovery.NewAggregatedDiscoveryServiceClient(a.conn) - a.stream, err = a.client.StreamAggregatedResources(context.Background()) - if err != nil { - return err - } - a.sendNodeMeta = true - a.InitialLoad = 0 - // Send the initial requests - for _, r := range a.cfg.InitialDiscoveryRequests { - if r.TypeUrl == v3.ClusterType { - a.watchTime = time.Now() - } - _ = a.Send(r) - } - // by default, we assume 1 goroutine decrements the waitgroup (go a.handleRecv()). - // for synchronizing when the goroutine finishes reading from the gRPC stream. - - a.RecvWg.Add(1) - - go a.handleRecv() - return nil -} - -// HasSynced returns true if MCP configs have synced -func (a *ADSC) HasSynced() bool { - if a.cfg == nil || len(a.cfg.InitialDiscoveryRequests) == 0 { - return true - } - - a.mutex.RLock() - defer a.mutex.RUnlock() - - for _, req := range a.cfg.InitialDiscoveryRequests { - if strings.Count(req.TypeUrl, "/") != 3 { - continue - } - - if _, ok := a.sync[req.TypeUrl]; !ok { - return false - } - } - - return true -} - -// reconnect will create a new stream -func (a *ADSC) reconnect() { - a.mutex.RLock() - if a.closed { - a.mutex.RUnlock() - return - } - a.mutex.RUnlock() - - err := a.Run() - if err == nil { - a.cfg.BackoffPolicy.Reset() - } else { - time.AfterFunc(a.cfg.BackoffPolicy.NextBackOff(), a.reconnect) - } -} - -func (a *ADSC) handleRecv() { - for { - var err error - msg, err := a.stream.Recv() - if err != nil { - a.RecvWg.Done() - adscLog.Infof("Connection closed for node %v with err: %v", a.nodeID, err) - select { - case a.errChan <- err: - default: - } - // if 'reconnect' enabled - schedule a new Run - if a.cfg.BackoffPolicy != nil { - time.AfterFunc(a.cfg.BackoffPolicy.NextBackOff(), a.reconnect) - } else { - a.Close() - a.WaitClear() - a.Updates <- "" - a.XDSUpdates <- nil - close(a.errChan) - } - return - } - - // Group-value-kind - used for high level api generator. - gvk := strings.SplitN(msg.TypeUrl, "/", 3) - - adscLog.Info("Received ", a.url, " type ", msg.TypeUrl, - " cnt=", len(msg.Resources), " nonce=", msg.Nonce) - if a.cfg.ResponseHandler != nil { - a.cfg.ResponseHandler.HandleResponse(a, msg) - } - - if msg.TypeUrl == collections.IstioMeshV1Alpha1MeshConfig.Resource().GroupVersionKind().String() && - len(msg.Resources) > 0 { - rsc := msg.Resources[0] - m := &v1alpha1.MeshConfig{} - err = proto.Unmarshal(rsc.Value, m) - if err != nil { - adscLog.Warn("Failed to unmarshal mesh config", err) - } - a.Mesh = m - if a.LocalCacheDir != "" { - strResponse, err := protomarshal.ToJSONWithIndent(m, " ") - if err != nil { - continue - } - err = os.WriteFile(a.LocalCacheDir+"_mesh.json", []byte(strResponse), 0o644) - if err != nil { - continue - } - } - continue - } - - // Process the resources. - a.VersionInfo[msg.TypeUrl] = msg.VersionInfo - switch msg.TypeUrl { - case v3.ListenerType: - listeners := make([]*listener.Listener, 0, len(msg.Resources)) - for _, rsc := range msg.Resources { - valBytes := rsc.Value - ll := &listener.Listener{} - _ = proto.Unmarshal(valBytes, ll) - listeners = append(listeners, ll) - } - a.handleLDS(listeners) - case v3.ClusterType: - clusters := make([]*cluster.Cluster, 0, len(msg.Resources)) - for _, rsc := range msg.Resources { - valBytes := rsc.Value - cl := &cluster.Cluster{} - _ = proto.Unmarshal(valBytes, cl) - clusters = append(clusters, cl) - } - a.handleCDS(clusters) - case v3.EndpointType: - eds := make([]*endpoint.ClusterLoadAssignment, 0, len(msg.Resources)) - for _, rsc := range msg.Resources { - valBytes := rsc.Value - el := &endpoint.ClusterLoadAssignment{} - _ = proto.Unmarshal(valBytes, el) - eds = append(eds, el) - } - a.handleEDS(eds) - case v3.RouteType: - routes := make([]*route.RouteConfiguration, 0, len(msg.Resources)) - for _, rsc := range msg.Resources { - valBytes := rsc.Value - rl := &route.RouteConfiguration{} - _ = proto.Unmarshal(valBytes, rl) - routes = append(routes, rl) - } - a.handleRDS(routes) - default: - a.handleMCP(gvk, msg.Resources) - } - - // If we got no resource - still save to the store with empty name/namespace, to notify sync - // This scheme also allows us to chunk large responses ! - - // TODO: add hook to inject nacks - - a.mutex.Lock() - if len(gvk) == 3 { - gt := config.GroupVersionKind{Group: gvk[0], Version: gvk[1], Kind: gvk[2]} - if _, exist := a.sync[gt.String()]; !exist { - a.sync[gt.String()] = time.Now() - } - } - a.Received[msg.TypeUrl] = msg - a.ack(msg) - a.mutex.Unlock() - - select { - case a.XDSUpdates <- msg: - default: - } - } -} - -func (a *ADSC) mcpToPilot(m *mcp.Resource) (*config.Config, error) { - if m == nil || m.Metadata == nil { - return &config.Config{}, nil - } - c := &config.Config{ - Meta: config.Meta{ - ResourceVersion: m.Metadata.Version, - Labels: m.Metadata.Labels, - Annotations: m.Metadata.Annotations, - }, - } - - if !config.ObjectInRevision(c, a.cfg.Revision) { // In case upstream does not support rev in node meta. - return nil, nil - } - - if c.Meta.Annotations == nil { - c.Meta.Annotations = make(map[string]string) - } - nsn := strings.Split(m.Metadata.Name, "/") - if len(nsn) != 2 { - return nil, fmt.Errorf("invalid name %s", m.Metadata.Name) - } - c.Namespace = nsn[0] - c.Name = nsn[1] - var err error - c.CreationTimestamp = m.Metadata.CreateTime.AsTime() - - pb, err := m.Body.UnmarshalNew() - if err != nil { - return nil, err - } - c.Spec = pb - return c, nil -} - -// nolint: staticcheck -func (a *ADSC) handleLDS(ll []*listener.Listener) { - lh := map[string]*listener.Listener{} - lt := map[string]*listener.Listener{} - - routes := []string{} - ldsSize := 0 - - for _, l := range ll { - ldsSize += proto.Size(l) - - // The last filter is the actual destination for inbound listener - if l.ApiListener != nil { - // This is an API Listener - // TODO: extract VIP and RDS or cluster - continue - } - fc := l.FilterChains[len(l.FilterChains)-1] - // Find the terminal filter - filter := fc.Filters[len(fc.Filters)-1] - - // The actual destination will be the next to the last if the last filter is a passthrough filter - if fc.GetName() == util.PassthroughFilterChain { - fc = l.FilterChains[len(l.FilterChains)-2] - filter = fc.Filters[len(fc.Filters)-1] - } - - switch filter.Name { - case wellknown.TCPProxy: - lt[l.Name] = l - config, _ := conversion.MessageToStruct(filter.GetTypedConfig()) - c := config.Fields["cluster"].GetStringValue() - adscLog.Debugf("TCP: %s -> %s", l.Name, c) - case wellknown.HTTPConnectionManager: - lh[l.Name] = l - - // Getting from config is too painful.. - port := l.Address.GetSocketAddress().GetPortValue() - if port == 15002 { - routes = append(routes, "http_proxy") - } else { - routes = append(routes, fmt.Sprintf("%d", port)) - } - case wellknown.MongoProxy: - // ignore for now - case wellknown.RedisProxy: - // ignore for now - case wellknown.MySQLProxy: - // ignore for now - default: - adscLog.Infof(protomarshal.ToJSONWithIndent(l, " ")) - } - } - - adscLog.Infof("LDS: http=%d tcp=%d size=%d", len(lh), len(lt), ldsSize) - if adscLog.DebugEnabled() { - b, _ := json.MarshalIndent(ll, " ", " ") - adscLog.Debugf(string(b)) - } - a.mutex.Lock() - defer a.mutex.Unlock() - if len(routes) > 0 { - a.sendRsc(v3.RouteType, routes) - } - a.httpListeners = lh - a.tcpListeners = lt - - select { - case a.Updates <- v3.ListenerType: - default: - } -} - -// Save will save the json configs to files, using the base directory -func (a *ADSC) Save(base string) error { - a.mutex.Lock() - defer a.mutex.Unlock() - - // guarrante the persistence order for each element in tcpListeners - var sortTCPListeners []string - for key := range a.tcpListeners { - sortTCPListeners = append(sortTCPListeners, key) - } - sort.Strings(sortTCPListeners) - arrTCPListenersJSONProto := make([]jsonMarshalProtoWithName, 0, len(sortTCPListeners)) - for _, element := range sortTCPListeners { - sliceItem := &jsonMarshalProtoWithName{element, a.tcpListeners[element]} - arrTCPListenersJSONProto = append(arrTCPListenersJSONProto, *sliceItem) - } - byteJSONResponse, err := json.MarshalIndent(arrTCPListenersJSONProto, "", " ") - if err != nil { - adscLog.Warnf("Error for marshaling TCPListeners: %v", err) - } - err = os.WriteFile(base+"_lds_tcp.json", byteJSONResponse, 0o644) - if err != nil { - return err - } - - // guarrante the persistence order for each element in httpListeners - var sortHTTPListeners []string - for key := range a.httpListeners { - sortHTTPListeners = append(sortHTTPListeners, key) - } - sort.Strings(sortHTTPListeners) - arrHTTPListenersJSONProto := make([]jsonMarshalProtoWithName, 0, len(sortHTTPListeners)) - for _, element := range sortHTTPListeners { - sliceItem := &jsonMarshalProtoWithName{element, a.httpListeners[element]} - arrHTTPListenersJSONProto = append(arrHTTPListenersJSONProto, *sliceItem) - } - byteJSONResponse, err = json.MarshalIndent(arrHTTPListenersJSONProto, "", " ") - if err != nil { - return err - } - err = os.WriteFile(base+"_lds_http.json", byteJSONResponse, 0o644) - if err != nil { - return err - } - - // guarrante the persistence order for each element in routes - var sortRoutes []string - for key := range a.routes { - sortRoutes = append(sortRoutes, key) - } - sort.Strings(sortRoutes) - arrRoutesJSONProto := make([]jsonMarshalProtoWithName, 0, len(sortRoutes)) - for _, element := range sortRoutes { - sliceItem := &jsonMarshalProtoWithName{element, a.routes[element]} - arrRoutesJSONProto = append(arrRoutesJSONProto, *sliceItem) - } - byteJSONResponse, err = json.MarshalIndent(arrRoutesJSONProto, "", " ") - if err != nil { - return err - } - err = os.WriteFile(base+"_rds.json", byteJSONResponse, 0o644) - if err != nil { - return err - } - - // guarrante the persistence order for each element in edsClusters - var sortEdsClusters []string - for key := range a.edsClusters { - sortEdsClusters = append(sortEdsClusters, key) - } - sort.Strings(sortEdsClusters) - arrEdsClustersJSONProto := make([]jsonMarshalProtoWithName, 0, len(sortEdsClusters)) - for _, element := range sortEdsClusters { - sliceItem := &jsonMarshalProtoWithName{element, a.edsClusters[element]} - arrEdsClustersJSONProto = append(arrEdsClustersJSONProto, *sliceItem) - } - byteJSONResponse, err = json.MarshalIndent(arrEdsClustersJSONProto, "", " ") - if err != nil { - return err - } - err = os.WriteFile(base+"_ecds.json", byteJSONResponse, 0o644) - if err != nil { - return err - } - - // guarrante the persistence order for each element in clusters - var sortClusters []string - for key := range a.clusters { - sortClusters = append(sortClusters, key) - } - sort.Strings(sortClusters) - arrClustersJSONProto := make([]jsonMarshalProtoWithName, 0, len(sortClusters)) - for _, element := range sortClusters { - sliceItem := &jsonMarshalProtoWithName{element, a.clusters[element]} - arrClustersJSONProto = append(arrClustersJSONProto, *sliceItem) - } - byteJSONResponse, err = json.MarshalIndent(arrClustersJSONProto, "", " ") - if err != nil { - return err - } - err = os.WriteFile(base+"_cds.json", byteJSONResponse, 0o644) - if err != nil { - return err - } - - // guarrante the persistence order for each element in eds - var sortEds []string - for key := range a.eds { - sortEds = append(sortEds, key) - } - sort.Strings(sortEds) - arrEdsJSONProto := make([]jsonMarshalProtoWithName, 0, len(sortEds)) - for _, element := range sortEds { - sliceItem := &jsonMarshalProtoWithName{element, a.eds[element]} - arrEdsJSONProto = append(arrEdsJSONProto, *sliceItem) - } - byteJSONResponse, err = json.MarshalIndent(arrEdsJSONProto, "", " ") - if err != nil { - return err - } - err = os.WriteFile(base+"_eds.json", byteJSONResponse, 0o644) - if err != nil { - return err - } - - return err -} - -func (a *ADSC) handleCDS(ll []*cluster.Cluster) { - cn := make([]string, 0, len(ll)) - cdsSize := 0 - edscds := map[string]*cluster.Cluster{} - cds := map[string]*cluster.Cluster{} - for _, c := range ll { - cdsSize += proto.Size(c) - switch v := c.ClusterDiscoveryType.(type) { - case *cluster.Cluster_Type: - if v.Type != cluster.Cluster_EDS { - cds[c.Name] = c - continue - } - } - cn = append(cn, c.Name) - edscds[c.Name] = c - } - - adscLog.Infof("CDS: %d size=%d", len(cn), cdsSize) - - if len(cn) > 0 { - a.sendRsc(v3.EndpointType, cn) - } - if adscLog.DebugEnabled() { - b, _ := json.MarshalIndent(ll, " ", " ") - adscLog.Debugf(string(b)) - } - - a.mutex.Lock() - defer a.mutex.Unlock() - a.edsClusters = edscds - a.clusters = cds - - select { - case a.Updates <- v3.ClusterType: - default: - } -} - -func (a *ADSC) node() *core.Node { - n := &core.Node{ - Id: a.nodeID, - Locality: a.Locality, - } - if a.Metadata == nil { - n.Metadata = &pstruct.Struct{ - Fields: map[string]*pstruct.Value{ - "ISTIO_VERSION": {Kind: &pstruct.Value_StringValue{StringValue: "65536.65536.65536"}}, - }, - } - } else { - n.Metadata = a.Metadata - if a.Metadata.Fields["ISTIO_VERSION"] == nil { - a.Metadata.Fields["ISTIO_VERSION"] = &pstruct.Value{Kind: &pstruct.Value_StringValue{StringValue: "65536.65536.65536"}} - } - } - return n -} - -// Raw send of a request. -func (a *ADSC) Send(req *discovery.DiscoveryRequest) error { - if a.sendNodeMeta { - req.Node = a.node() - a.sendNodeMeta = false - } - req.ResponseNonce = time.Now().String() - if adscLog.DebugEnabled() { - strReq, _ := protomarshal.ToJSONWithIndent(req, " ") - adscLog.Debugf("Sending Discovery Request to istiod: %s", strReq) - } - return a.stream.Send(req) -} - -func (a *ADSC) handleEDS(eds []*endpoint.ClusterLoadAssignment) { - la := map[string]*endpoint.ClusterLoadAssignment{} - edsSize := 0 - ep := 0 - for _, cla := range eds { - edsSize += proto.Size(cla) - la[cla.ClusterName] = cla - ep += len(cla.Endpoints) - } - - adscLog.Infof("eds: %d size=%d ep=%d", len(eds), edsSize, ep) - if adscLog.DebugEnabled() { - b, _ := json.MarshalIndent(eds, " ", " ") - adscLog.Debugf(string(b)) - } - if a.InitialLoad == 0 { - // first load - Envoy loads listeners after endpoints - _ = a.stream.Send(&discovery.DiscoveryRequest{ - Node: a.node(), - TypeUrl: v3.ListenerType, - }) - } - - a.mutex.Lock() - defer a.mutex.Unlock() - a.eds = la - - select { - case a.Updates <- v3.EndpointType: - default: - } -} - -func (a *ADSC) handleRDS(configurations []*route.RouteConfiguration) { - vh := 0 - rcount := 0 - size := 0 - - rds := map[string]*route.RouteConfiguration{} - - for _, r := range configurations { - for _, h := range r.VirtualHosts { - vh++ - for _, rt := range h.Routes { - rcount++ - // Example: match: route: %v expected err %v", err, tt.err) - } - if ldsTCP := readFile(base+"_lds_tcp.json", t); ldsTCP != tt.expectedJSON["_lds_tcp"] { - t.Errorf("AdscSave() => %s expected ldsTcp %s\n%v", ldsTCP, tt.expectedJSON["_lds_tcp"], cmp.Diff(ldsTCP, tt.expectedJSON["_lds_tcp"])) - } - if ldsHTTP := readFile(base+"_lds_http.json", t); ldsHTTP != tt.expectedJSON["_lds_http"] { - t.Errorf("AdscSave() => %s expected ldsHttp %s", ldsHTTP, tt.expectedJSON["_lds_http"]) - } - if rds := readFile(base+"_rds.json", t); rds != tt.expectedJSON["_rds"] { - t.Errorf("AdscSave() => %s expected rds %s", rds, tt.expectedJSON["_rds"]) - } - if ecds := readFile(base+"_ecds.json", t); ecds != tt.expectedJSON["_ecds"] { - t.Errorf("AdscSave() => %s expected ecds %s", ecds, tt.expectedJSON["_ecds"]) - } - if cds := readFile(base+"_cds.json", t); cds != tt.expectedJSON["_cds"] { - t.Errorf("AdscSave() => %s expected cds %s", cds, tt.expectedJSON["_cds"]) - } - if eds := readFile(base+"_eds.json", t); eds != tt.expectedJSON["_eds"] { - t.Errorf("AdscSave() => %s expected eds %s", eds, tt.expectedJSON["_eds"]) - } - saveTeardown(base, t) - }) - } -} - -func saveTeardown(base string, t *testing.T) { - if err := os.Remove(base + "_lds_tcp.json"); err != nil { - t.Errorf("Unable to cleanup: %v", err) - } - if err := os.Remove(base + "_lds_http.json"); err != nil { - t.Errorf("Unable to cleanup: %v", err) - } - if err := os.Remove(base + "_cds.json"); err != nil { - t.Errorf("Unable to cleanup: %v", err) - } - if err := os.Remove(base + "_rds.json"); err != nil { - t.Errorf("Unable to cleanup: %v", err) - } - if err := os.Remove(base + "_ecds.json"); err != nil { - t.Errorf("Unable to cleanup: %v", err) - } - if err := os.Remove(base + "_eds.json"); err != nil { - t.Errorf("Unable to cleanup: %v", err) - } -} - -func readFile(dir string, t *testing.T) string { - dat, err := os.ReadFile(dir) - if err != nil { - t.Fatalf("file %s issue: %v", dat, err) - } - return string(dat) -} - -func TestADSC_handleMCP(t *testing.T) { - rev := "test-rev" - adsc := &ADSC{ - VersionInfo: map[string]string{}, - Store: model.MakeIstioStore(memory.Make(collections.Pilot)), - cfg: &Config{Revision: rev}, - } - - patchLabel := func(lbls map[string]string, name, value string) map[string]string { - if lbls == nil { - lbls = map[string]string{} - } - lbls[name] = value - return lbls - } - - tests := []struct { - desc string - resources []*any.Any - expectedResources [][]string - }{ - { - desc: "create-resources", - resources: []*any.Any{ - constructResource("foo1", "foo1.bar.com", "192.1.1.1", "1"), - constructResource("foo2", "foo2.bar.com", "192.1.1.2", "1"), - }, - expectedResources: [][]string{ - {"foo1", "foo1.bar.com", "192.1.1.1"}, - {"foo2", "foo2.bar.com", "192.1.1.2"}, - }, - }, - { - desc: "create-resources-rev-1", - resources: []*any.Any{ - constructResource("foo1", "foo1.bar.com", "192.1.1.1", "1"), - constructResourceWithOptions("foo2", "foo2.bar.com", "192.1.1.2", "1", func(resource *mcp.Resource) { - resource.Metadata.Labels = patchLabel(resource.Metadata.Labels, label.IoIstioRev.Name, rev+"wrong") // to del - }), - constructResourceWithOptions("foo3", "foo3.bar.com", "192.1.1.3", "1", func(resource *mcp.Resource) { - resource.Metadata.Labels = patchLabel(resource.Metadata.Labels, label.IoIstioRev.Name, rev) // to add - }), - }, - expectedResources: [][]string{ - {"foo1", "foo1.bar.com", "192.1.1.1"}, - {"foo3", "foo3.bar.com", "192.1.1.3"}, - }, - }, - { - desc: "create-resources-rev-2", - resources: []*any.Any{ - constructResource("foo1", "foo1.bar.com", "192.1.1.1", "1"), - constructResourceWithOptions("foo2", "foo2.bar.com", "192.1.1.2", "1", func(resource *mcp.Resource) { - resource.Metadata.Labels = patchLabel(resource.Metadata.Labels, label.IoIstioRev.Name, rev) // to add back - }), - constructResourceWithOptions("foo3", "foo3.bar.com", "192.1.1.3", "1", func(resource *mcp.Resource) { - resource.Metadata.Labels = patchLabel(resource.Metadata.Labels, label.IoIstioRev.Name, rev+"wrong") // to del - }), - }, - expectedResources: [][]string{ - {"foo1", "foo1.bar.com", "192.1.1.1"}, - {"foo2", "foo2.bar.com", "192.1.1.2"}, - }, - }, - { - desc: "update-and-create-resources", - resources: []*any.Any{ - constructResource("foo1", "foo1.bar.com", "192.1.1.11", "2"), - constructResource("foo2", "foo2.bar.com", "192.1.1.22", "1"), - constructResource("foo3", "foo3.bar.com", "192.1.1.3", ""), - }, - expectedResources: [][]string{ - {"foo1", "foo1.bar.com", "192.1.1.11"}, - {"foo2", "foo2.bar.com", "192.1.1.2"}, - {"foo3", "foo3.bar.com", "192.1.1.3"}, - }, - }, - { - desc: "update-delete-and-create-resources", - resources: []*any.Any{ - constructResource("foo2", "foo2.bar.com", "192.1.1.222", "4"), - constructResource("foo4", "foo4.bar.com", "192.1.1.4", "1"), - }, - expectedResources: [][]string{ - {"foo2", "foo2.bar.com", "192.1.1.222"}, - {"foo4", "foo4.bar.com", "192.1.1.4"}, - }, - }, - { - desc: "update-and-delete-resources", - resources: []*any.Any{ - constructResource("foo2", "foo2.bar.com", "192.2.2.22", "3"), - constructResource("foo3", "foo3.bar.com", "192.1.1.33", ""), - }, - expectedResources: [][]string{ - {"foo2", "foo2.bar.com", "192.2.2.22"}, - {"foo3", "foo3.bar.com", "192.1.1.33"}, - }, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - gvk := []string{"networking.istio.io", "v1alpha3", "ServiceEntry"} - adsc.handleMCP(gvk, tt.resources) - configs, _ := adsc.Store.List(collections.IstioNetworkingV1Alpha3Serviceentries.Resource().GroupVersionKind(), "") - if len(configs) != len(tt.expectedResources) { - t.Errorf("expected %v got %v", len(tt.expectedResources), len(configs)) - } - configMap := make(map[string][]string) - for _, conf := range configs { - service, _ := conf.Spec.(*networking.ServiceEntry) - configMap[conf.Name] = []string{conf.Name, service.Hosts[0], service.Addresses[0]} - } - for _, expected := range tt.expectedResources { - got, ok := configMap[expected[0]] - if !ok { - t.Errorf("expected %v got none", expected) - } else { - for i, value := range expected { - if value != got[i] { - t.Errorf("expected %v got %v", value, got[i]) - } - } - } - } - }) - } -} - -func constructResourceWithOptions(name string, host string, address, version string, options ...func(resource *mcp.Resource)) *any.Any { - service := &networking.ServiceEntry{ - Hosts: []string{host}, - Addresses: []string{address}, - } - seAny, _ := any.New(service) - resource := &mcp.Resource{ - Metadata: &mcp.Metadata{ - Name: "default/" + name, - CreateTime: timestamppb.Now(), - Version: version, - }, - Body: seAny, - } - - for _, o := range options { - o(resource) - } - - resAny, _ := any.New(resource) - return &any.Any{ - TypeUrl: resAny.TypeUrl, - Value: resAny.Value, - } -} - -func constructResource(name string, host string, address, version string) *any.Any { - return constructResourceWithOptions(name, host, address, version) -} diff --git a/pkg/adsc/util.go b/pkg/adsc/util.go deleted file mode 100644 index b426bdf70..000000000 --- a/pkg/adsc/util.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package adsc - -import ( - "crypto/tls" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/security" -) - -func getClientCertFn(config *Config) func(requestInfo *tls.CertificateRequestInfo) (*tls.Certificate, error) { - if config.SecretManager != nil { - return func(requestInfo *tls.CertificateRequestInfo) (*tls.Certificate, error) { - key, err := config.SecretManager.GenerateSecret(security.WorkloadKeyCertResourceName) - if err != nil { - return nil, err - } - clientCert, err := tls.X509KeyPair(key.CertificateChain, key.PrivateKey) - if err != nil { - return nil, err - } - return &clientCert, nil - } - } - if config.CertDir != "" { - return func(requestInfo *tls.CertificateRequestInfo) (*tls.Certificate, error) { - certName := config.CertDir + "/cert-chain.pem" - clientCert, err := tls.LoadX509KeyPair(certName, config.CertDir+"/key.pem") - if err != nil { - return nil, err - } - return &clientCert, nil - } - } - - return nil -} diff --git a/pkg/bootstrap/config.go b/pkg/bootstrap/config.go deleted file mode 100644 index a9d82d795..000000000 --- a/pkg/bootstrap/config.go +++ /dev/null @@ -1,746 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bootstrap - -import ( - "encoding/json" - "errors" - "fmt" - "os" - "path" - "strconv" - "strings" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - "google.golang.org/protobuf/types/known/structpb" - "google.golang.org/protobuf/types/known/wrapperspb" - "istio.io/api/annotation" - meshAPI "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/env" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/util/network" - "github.com/apache/dubbo-go-pixiu/pkg/bootstrap/option" - "github.com/apache/dubbo-go-pixiu/pkg/bootstrap/platform" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/kube/labels" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -const ( - // IstioMetaPrefix is used to pass env vars as node metadata. - IstioMetaPrefix = "ISTIO_META_" - - // IstioMetaJSONPrefix is used to pass annotations and similar environment info. - IstioMetaJSONPrefix = "ISTIO_METAJSON_" - - lightstepAccessTokenBase = "lightstep_access_token.txt" - - // required stats are used by readiness checks. - requiredEnvoyStatsMatcherInclusionPrefixes = "cluster_manager,listener_manager,server,cluster.xds-grpc,wasm" - - rbacEnvoyStatsMatcherInclusionSuffix = "rbac.allowed,rbac.denied,shadow_allowed,shadow_denied" - - requiredEnvoyStatsMatcherInclusionSuffixes = rbacEnvoyStatsMatcherInclusionSuffix + ",downstream_cx_active" // Needed for draining. - - // Prefixes of V2 metrics. - // "reporter" prefix is for istio standard metrics. - // "component" suffix is for istio_build metric. - v2Prefixes = "reporter=," - v2Suffix = ",component" -) - -// Config for creating a bootstrap file. -type Config struct { - *model.Node -} - -// toTemplateParams creates a new template configuration for the given configuration. -func (cfg Config) toTemplateParams() (map[string]interface{}, error) { - opts := make([]option.Instance, 0) - - discHost := strings.Split(cfg.Metadata.ProxyConfig.DiscoveryAddress, ":")[0] - - xdsType := "GRPC" - if features.DeltaXds { - xdsType = "DELTA_GRPC" - } - - opts = append(opts, - option.NodeID(cfg.ID), - option.NodeType(cfg.ID), - option.PilotSubjectAltName(cfg.Metadata.PilotSubjectAltName), - option.OutlierLogPath(cfg.Metadata.OutlierLogPath), - option.ProvCert(cfg.Metadata.ProvCert), - option.DiscoveryHost(discHost), - option.Metadata(cfg.Metadata), - option.XdsType(xdsType)) - - // Add GCPProjectNumber to access in bootstrap template. - md := cfg.Metadata.PlatformMetadata - if projectNumber, found := md[platform.GCPProjectNumber]; found { - opts = append(opts, option.GCPProjectNumber(projectNumber)) - } - - if cfg.Metadata.StsPort != "" { - stsPort, err := strconv.Atoi(cfg.Metadata.StsPort) - if err == nil && stsPort > 0 { - opts = append(opts, - option.STSEnabled(true), - option.STSPort(stsPort)) - md := cfg.Metadata.PlatformMetadata - if projectID, found := md[platform.GCPProject]; found { - opts = append(opts, option.GCPProjectID(projectID)) - } - } - } - - // Support passing extra info from node environment as metadata - opts = append(opts, getNodeMetadataOptions(cfg.Node)...) - - // Check if nodeIP carries IPv4 or IPv6 and set up proxy accordingly - if network.AllIPv6(cfg.Metadata.InstanceIPs) { - opts = append(opts, - option.Localhost(option.LocalhostIPv6), - option.Wildcard(option.WildcardIPv6), - option.DNSLookupFamily(option.DNSLookupFamilyIPv6)) - } else { - opts = append(opts, - option.Localhost(option.LocalhostIPv4), - option.Wildcard(option.WildcardIPv4), - option.DNSLookupFamily(option.DNSLookupFamilyIPv4)) - } - - proxyOpts, err := getProxyConfigOptions(cfg.Metadata) - if err != nil { - return nil, err - } - opts = append(opts, proxyOpts...) - - // TODO: allow reading a file with additional metadata (for example if created with - // 'envref'. This will allow Istio to generate the right config even if the pod info - // is not available (in particular in some multi-cluster cases) - return option.NewTemplateParams(opts...) -} - -// substituteValues substitutes variables known to the bootstrap like pod_ip. -// "http.{pod_ip}_" with pod_id = [10.3.3.3,10.4.4.4] --> [http.10.3.3.3_,http.10.4.4.4_] -func substituteValues(patterns []string, varName string, values []string) []string { - ret := make([]string, 0, len(patterns)) - for _, pattern := range patterns { - if !strings.Contains(pattern, varName) { - ret = append(ret, pattern) - continue - } - - for _, val := range values { - ret = append(ret, strings.Replace(pattern, varName, val, -1)) - } - } - return ret -} - -// DefaultStatTags for telemetry v2 tag extraction. -var DefaultStatTags = []string{ - "reporter", - "source_namespace", - "source_workload", - "source_workload_namespace", - "source_principal", - "source_app", - "source_version", - "source_cluster", - "destination_namespace", - "destination_workload", - "destination_workload_namespace", - "destination_principal", - "destination_app", - "destination_version", - "destination_service", - "destination_service_name", - "destination_service_namespace", - "destination_port", - "destination_cluster", - "request_protocol", - "request_operation", - "request_host", - "response_flags", - "grpc_response_status", - "connection_security_policy", - "source_canonical_service", - "destination_canonical_service", - "source_canonical_revision", - "destination_canonical_revision", -} - -func getStatsOptions(meta *model.BootstrapNodeMetadata) []option.Instance { - nodeIPs := meta.InstanceIPs - config := meta.ProxyConfig - - tagAnno := meta.Annotations[annotation.SidecarExtraStatTags.Name] - prefixAnno := meta.Annotations[annotation.SidecarStatsInclusionPrefixes.Name] - RegexAnno := meta.Annotations[annotation.SidecarStatsInclusionRegexps.Name] - suffixAnno := meta.Annotations[annotation.SidecarStatsInclusionSuffixes.Name] - - parseOption := func(metaOption string, required string, proxyConfigOption []string) []string { - var inclusionOption []string - if len(metaOption) > 0 { - inclusionOption = strings.Split(metaOption, ",") - } else if proxyConfigOption != nil { - // In case user relies on mixed usage of annotation and proxy config, - // only consider proxy config if annotation is not set instead of merging. - inclusionOption = proxyConfigOption - } - - if len(required) > 0 { - inclusionOption = append(inclusionOption, strings.Split(required, ",")...) - } - - // At the sidecar we can limit downstream metrics collection to the inbound listener. - // Inbound downstream metrics are named as: http.{pod_ip}_{port}.downstream_rq_* - // Other outbound downstream metrics are numerous and not very interesting for a sidecar. - // specifying http.{pod_ip}_ as a prefix will capture these downstream metrics. - return substituteValues(inclusionOption, "{pod_ip}", nodeIPs) - } - - extraStatTags := make([]string, 0, len(DefaultStatTags)) - extraStatTags = append(extraStatTags, - DefaultStatTags...) - for _, tag := range config.ExtraStatTags { - if tag != "" { - extraStatTags = append(extraStatTags, tag) - } - } - for _, tag := range strings.Split(tagAnno, ",") { - if tag != "" { - extraStatTags = append(extraStatTags, tag) - } - } - extraStatTags = removeDuplicates(extraStatTags) - - var proxyConfigPrefixes, proxyConfigSuffixes, proxyConfigRegexps []string - if config.ProxyStatsMatcher != nil { - proxyConfigPrefixes = config.ProxyStatsMatcher.InclusionPrefixes - proxyConfigSuffixes = config.ProxyStatsMatcher.InclusionSuffixes - proxyConfigRegexps = config.ProxyStatsMatcher.InclusionRegexps - } - inclusionSuffixes := rbacEnvoyStatsMatcherInclusionSuffix - if meta.ExitOnZeroActiveConnections { - inclusionSuffixes = requiredEnvoyStatsMatcherInclusionSuffixes - } - - return []option.Instance{ - option.EnvoyStatsMatcherInclusionPrefix(parseOption(prefixAnno, - requiredEnvoyStatsMatcherInclusionPrefixes, proxyConfigPrefixes)), - option.EnvoyStatsMatcherInclusionSuffix(parseOption(suffixAnno, - inclusionSuffixes, proxyConfigSuffixes)), - option.EnvoyStatsMatcherInclusionRegexp(parseOption(RegexAnno, "", proxyConfigRegexps)), - option.EnvoyExtraStatTags(extraStatTags), - } -} - -func lightstepAccessTokenFile(config string) string { - return path.Join(config, lightstepAccessTokenBase) -} - -func getNodeMetadataOptions(node *model.Node) []option.Instance { - // Add locality options. - opts := getLocalityOptions(node.Locality) - - opts = append(opts, getStatsOptions(node.Metadata)...) - - opts = append(opts, - option.NodeMetadata(node.Metadata, node.RawMetadata), - option.RuntimeFlags(extractRuntimeFlags(node.Metadata.ProxyConfig)), - option.EnvoyStatusPort(node.Metadata.EnvoyStatusPort), - option.EnvoyPrometheusPort(node.Metadata.EnvoyPrometheusPort)) - return opts -} - -var StripFragment = env.RegisterBoolVar("HTTP_STRIP_FRAGMENT_FROM_PATH_UNSAFE_IF_DISABLED", true, "").Get() - -func extractRuntimeFlags(cfg *model.NodeMetaProxyConfig) map[string]string { - // Setup defaults - runtimeFlags := map[string]string{ - "overload.global_downstream_max_connections": "2147483647", - "envoy.deprecated_features:envoy.config.listener.v3.Listener.hidden_envoy_deprecated_use_original_dst": "true", - "envoy.reloadable_features.require_strict_1xx_and_204_response_headers": "false", - "re2.max_program_size.error_level": "32768", - "envoy.reloadable_features.http_reject_path_with_fragment": "false", - "envoy.reloadable_features.no_extension_lookup_by_name": "false", - } - if !StripFragment { - // Note: the condition here is basically backwards. This was a mistake in the initial commit and cannot be reverted - runtimeFlags["envoy.reloadable_features.http_strip_fragment_from_path_unsafe_if_disabled"] = "false" - } - for k, v := range cfg.RuntimeValues { - if v == "" { - // Envoy runtime doesn't see "" as a special value, so we use it to mean 'unset default flag' - delete(runtimeFlags, k) - continue - } - runtimeFlags[k] = v - } - return runtimeFlags -} - -func getLocalityOptions(l *core.Locality) []option.Instance { - return []option.Instance{option.Region(l.Region), option.Zone(l.Zone), option.SubZone(l.SubZone)} -} - -func getServiceCluster(metadata *model.BootstrapNodeMetadata) string { - switch name := metadata.ProxyConfig.ClusterName.(type) { - case *meshAPI.ProxyConfig_ServiceCluster: - return serviceClusterOrDefault(name.ServiceCluster, metadata) - - case *meshAPI.ProxyConfig_TracingServiceName_: - workloadName := metadata.WorkloadName - if workloadName == "" { - workloadName = "istio-proxy" - } - - switch name.TracingServiceName { - case meshAPI.ProxyConfig_APP_LABEL_AND_NAMESPACE: - return serviceClusterOrDefault("istio-proxy", metadata) - case meshAPI.ProxyConfig_CANONICAL_NAME_ONLY: - cs, _ := labels.CanonicalService(metadata.Labels, workloadName) - return serviceClusterOrDefault(cs, metadata) - case meshAPI.ProxyConfig_CANONICAL_NAME_AND_NAMESPACE: - cs, _ := labels.CanonicalService(metadata.Labels, workloadName) - if metadata.Namespace != "" { - return cs + "." + metadata.Namespace - } - return serviceClusterOrDefault(cs, metadata) - default: - return serviceClusterOrDefault("istio-proxy", metadata) - } - - default: - return serviceClusterOrDefault("istio-proxy", metadata) - } -} - -func serviceClusterOrDefault(name string, metadata *model.BootstrapNodeMetadata) string { - if name != "" && name != "istio-proxy" { - return name - } - if app, ok := metadata.Labels["app"]; ok { - return app + "." + metadata.Namespace - } - if metadata.WorkloadName != "" { - return metadata.WorkloadName + "." + metadata.Namespace - } - if metadata.Namespace != "" { - return "istio-proxy." + metadata.Namespace - } - return "istio-proxy" -} - -func getProxyConfigOptions(metadata *model.BootstrapNodeMetadata) ([]option.Instance, error) { - config := metadata.ProxyConfig - - // Add a few misc options. - opts := make([]option.Instance, 0) - - opts = append(opts, option.ProxyConfig(config), - option.Cluster(getServiceCluster(metadata)), - option.PilotGRPCAddress(config.DiscoveryAddress), - option.DiscoveryAddress(config.DiscoveryAddress), - option.StatsdAddress(config.StatsdUdpAddress), - option.XDSRootCert(metadata.XDSRootCert)) - - // Add tracing options. - if config.Tracing != nil { - isH2 := false - switch tracer := config.Tracing.Tracer.(type) { - case *meshAPI.Tracing_Zipkin_: - opts = append(opts, option.ZipkinAddress(tracer.Zipkin.Address)) - case *meshAPI.Tracing_Lightstep_: - isH2 = true - // Create the token file. - lightstepAccessTokenPath := lightstepAccessTokenFile(config.ConfigPath) - lsConfigOut, err := os.Create(lightstepAccessTokenPath) - if err != nil { - return nil, err - } - _, err = lsConfigOut.WriteString(tracer.Lightstep.AccessToken) - if err != nil { - return nil, err - } - - opts = append(opts, option.LightstepAddress(tracer.Lightstep.Address), - option.LightstepToken(lightstepAccessTokenPath)) - case *meshAPI.Tracing_Datadog_: - opts = append(opts, option.DataDogAddress(tracer.Datadog.Address)) - case *meshAPI.Tracing_Stackdriver_: - projectID, projFound := metadata.PlatformMetadata[platform.GCPProject] - if !projFound { - return nil, errors.New("unable to process Stackdriver tracer: missing GCP Project") - } - - opts = append(opts, option.StackDriverEnabled(true), - option.StackDriverProjectID(projectID), - option.StackDriverDebug(tracer.Stackdriver.Debug), - option.StackDriverMaxAnnotations(getInt64ValueOrDefault(tracer.Stackdriver.MaxNumberOfAnnotations, 200)), - option.StackDriverMaxAttributes(getInt64ValueOrDefault(tracer.Stackdriver.MaxNumberOfAttributes, 200)), - option.StackDriverMaxEvents(getInt64ValueOrDefault(tracer.Stackdriver.MaxNumberOfMessageEvents, 200))) - case *meshAPI.Tracing_OpenCensusAgent_: - c := tracer.OpenCensusAgent.Context - opts = append(opts, option.OpenCensusAgentAddress(tracer.OpenCensusAgent.Address), - option.OpenCensusAgentContexts(c)) - } - - opts = append(opts, option.TracingTLS(config.Tracing.TlsSettings, metadata, isH2)) - } - - // Add options for Envoy metrics. - if config.EnvoyMetricsService != nil && config.EnvoyMetricsService.Address != "" { - opts = append(opts, option.EnvoyMetricsServiceAddress(config.EnvoyMetricsService.Address), - option.EnvoyMetricsServiceTLS(config.EnvoyMetricsService.TlsSettings, metadata), - option.EnvoyMetricsServiceTCPKeepalive(config.EnvoyMetricsService.TcpKeepalive)) - } else if config.EnvoyMetricsServiceAddress != "" { // nolint: staticcheck - opts = append(opts, option.EnvoyMetricsServiceAddress(config.EnvoyMetricsService.Address)) - } - - // Add options for Envoy access log. - if config.EnvoyAccessLogService != nil && config.EnvoyAccessLogService.Address != "" { - opts = append(opts, option.EnvoyAccessLogServiceAddress(config.EnvoyAccessLogService.Address), - option.EnvoyAccessLogServiceTLS(config.EnvoyAccessLogService.TlsSettings, metadata), - option.EnvoyAccessLogServiceTCPKeepalive(config.EnvoyAccessLogService.TcpKeepalive)) - } - - return opts, nil -} - -func getInt64ValueOrDefault(src *wrapperspb.Int64Value, defaultVal int64) int64 { - val := defaultVal - if src != nil { - val = src.Value - } - return val -} - -type setMetaFunc func(m map[string]interface{}, key string, val string) - -func extractMetadata(envs []string, prefix string, set setMetaFunc, meta map[string]interface{}) { - metaPrefixLen := len(prefix) - for _, e := range envs { - if !shouldExtract(e, prefix) { - continue - } - v := e[metaPrefixLen:] - if !isEnvVar(v) { - continue - } - metaKey, metaVal := parseEnvVar(v) - set(meta, metaKey, metaVal) - } -} - -func shouldExtract(envVar, prefix string) bool { - return strings.HasPrefix(envVar, prefix) -} - -func isEnvVar(str string) bool { - return strings.Contains(str, "=") -} - -func parseEnvVar(varStr string) (string, string) { - parts := strings.SplitN(varStr, "=", 2) - if len(parts) != 2 { - return varStr, "" - } - return parts[0], parts[1] -} - -func jsonStringToMap(jsonStr string) (m map[string]string) { - err := json.Unmarshal([]byte(jsonStr), &m) - if err != nil { - log.Warnf("Env variable with value %q failed json unmarshal: %v", jsonStr, err) - } - return -} - -func extractAttributesMetadata(envVars []string, plat platform.Environment, meta *model.BootstrapNodeMetadata) { - for _, varStr := range envVars { - name, val := parseEnvVar(varStr) - switch name { - case "ISTIO_METAJSON_LABELS": - m := jsonStringToMap(val) - if len(m) > 0 { - meta.Labels = m - } - case "POD_NAME": - meta.InstanceName = val - case "POD_NAMESPACE": - meta.Namespace = val - case "SERVICE_ACCOUNT": - meta.ServiceAccount = val - } - } - if plat != nil && len(plat.Metadata()) > 0 { - meta.PlatformMetadata = plat.Metadata() - } -} - -// MetadataOptions for constructing node metadata. -type MetadataOptions struct { - Envs []string - Platform platform.Environment - InstanceIPs []string - StsPort int - ID string - ProxyConfig *meshAPI.ProxyConfig - PilotSubjectAltName []string - XDSRootCert string - OutlierLogPath string - ProvCert string - annotationFilePath string - EnvoyStatusPort int - EnvoyPrometheusPort int - ExitOnZeroActiveConnections bool -} - -// GetNodeMetaData function uses an environment variable contract -// ISTIO_METAJSON_* env variables contain json_string in the value. -// The name of variable is ignored. -// ISTIO_META_* env variables are passed thru -func GetNodeMetaData(options MetadataOptions) (*model.Node, error) { - meta := &model.BootstrapNodeMetadata{} - untypedMeta := map[string]interface{}{} - - extractMetadata(options.Envs, IstioMetaPrefix, func(m map[string]interface{}, key string, val string) { - m[key] = val - }, untypedMeta) - - extractMetadata(options.Envs, IstioMetaJSONPrefix, func(m map[string]interface{}, key string, val string) { - err := json.Unmarshal([]byte(val), &m) - if err != nil { - log.Warnf("Env variable %s [%s] failed json unmarshal: %v", key, val, err) - } - }, untypedMeta) - - j, err := json.Marshal(untypedMeta) - if err != nil { - return nil, err - } - - if err := json.Unmarshal(j, meta); err != nil { - return nil, err - } - extractAttributesMetadata(options.Envs, options.Platform, meta) - - // Support multiple network interfaces, removing duplicates. - meta.InstanceIPs = removeDuplicates(options.InstanceIPs) - - // Add STS port into node metadata if it is not 0. This is read by envoy telemetry filters - if options.StsPort != 0 { - meta.StsPort = strconv.Itoa(options.StsPort) - } - meta.EnvoyStatusPort = options.EnvoyStatusPort - meta.EnvoyPrometheusPort = options.EnvoyPrometheusPort - meta.ExitOnZeroActiveConnections = model.StringBool(options.ExitOnZeroActiveConnections) - - meta.ProxyConfig = (*model.NodeMetaProxyConfig)(options.ProxyConfig) - - // Add all instance labels with lower precedence than pod labels - extractInstanceLabels(options.Platform, meta) - - // Add all pod labels found from filesystem - // These are typically volume mounted by the downward API - lbls, err := readPodLabels() - if err == nil { - if meta.Labels == nil { - meta.Labels = map[string]string{} - } - for k, v := range lbls { - meta.Labels[k] = v - } - } else { - if os.IsNotExist(err) { - log.Debugf("failed to read pod labels: %v", err) - } else { - log.Warnf("failed to read pod labels: %v", err) - } - } - - // Add all pod annotations found from filesystem - // These are typically volume mounted by the downward API - annos, err := ReadPodAnnotations(options.annotationFilePath) - if err == nil { - if meta.Annotations == nil { - meta.Annotations = map[string]string{} - } - for k, v := range annos { - meta.Annotations[k] = v - } - } else { - if os.IsNotExist(err) { - log.Debugf("failed to read pod annotations: %v", err) - } else { - log.Warnf("failed to read pod annotations: %v", err) - } - } - - var l *core.Locality - if meta.Labels[model.LocalityLabel] == "" && options.Platform != nil { - // The locality string was not set, try to get locality from platform - l = options.Platform.Locality() - } else { - localityString := model.GetLocalityLabelOrDefault(meta.Labels[model.LocalityLabel], "") - l = util.ConvertLocality(localityString) - } - - meta.PilotSubjectAltName = options.PilotSubjectAltName - meta.XDSRootCert = options.XDSRootCert - meta.OutlierLogPath = options.OutlierLogPath - meta.ProvCert = options.ProvCert - - return &model.Node{ - ID: options.ID, - Metadata: meta, - RawMetadata: untypedMeta, - Locality: l, - }, nil -} - -// ConvertNodeToXDSNode creates an Envoy node descriptor from Istio node descriptor. -func ConvertNodeToXDSNode(node *model.Node) *core.Node { - // First pass translates typed metadata - js, err := json.Marshal(node.Metadata) - if err != nil { - log.Warnf("Failed to marshal node metadata to JSON %#v: %v", node.Metadata, err) - } - pbst := &structpb.Struct{} - if err = protomarshal.Unmarshal(js, pbst); err != nil { - log.Warnf("Failed to unmarshal node metadata from JSON %#v: %v", node.Metadata, err) - } - // Second pass translates untyped metadata for "unknown" fields - for k, v := range node.RawMetadata { - if _, f := pbst.Fields[k]; !f { - fjs, err := json.Marshal(v) - if err != nil { - log.Warnf("Failed to marshal field metadata to JSON %#v: %v", k, err) - } - pbv := &structpb.Value{} - if err = protomarshal.Unmarshal(fjs, pbv); err != nil { - log.Warnf("Failed to unmarshal field metadata from JSON %#v: %v", k, err) - } - pbst.Fields[k] = pbv - } - } - return &core.Node{ - Id: node.ID, - Cluster: getServiceCluster(node.Metadata), - Locality: node.Locality, - Metadata: pbst, - } -} - -// ConvertXDSNodeToNode parses Istio node descriptor from an Envoy node descriptor, using only typed metadata. -func ConvertXDSNodeToNode(node *core.Node) *model.Node { - b, err := protomarshal.MarshalProtoNames(node.Metadata) - if err != nil { - log.Warnf("Failed to marshal node metadata to JSON %q: %v", node.Metadata, err) - } - metadata := &model.BootstrapNodeMetadata{} - err = json.Unmarshal(b, metadata) - if err != nil { - log.Warnf("Failed to unmarshal node metadata from JSON %q: %v", node.Metadata, err) - } - if metadata.ProxyConfig == nil { - metadata.ProxyConfig = &model.NodeMetaProxyConfig{} - metadata.ProxyConfig.ClusterName = &meshAPI.ProxyConfig_ServiceCluster{ServiceCluster: node.Cluster} - } - - return &model.Node{ - ID: node.Id, - Locality: node.Locality, - Metadata: metadata, - } -} - -// Extracts instance labels for the platform into model.NodeMetadata.Labels -// only if not running on Kubernetes -func extractInstanceLabels(plat platform.Environment, meta *model.BootstrapNodeMetadata) { - if plat == nil || meta == nil || plat.IsKubernetes() { - return - } - instanceLabels := plat.Labels() - if meta.Labels == nil { - meta.Labels = map[string]string{} - } - for k, v := range instanceLabels { - meta.Labels[k] = v - } -} - -func readPodLabels() (map[string]string, error) { - b, err := os.ReadFile(constants.PodInfoLabelsPath) - if err != nil { - return nil, err - } - return ParseDownwardAPI(string(b)) -} - -func ReadPodAnnotations(path string) (map[string]string, error) { - if path == "" { - path = constants.PodInfoAnnotationsPath - } - b, err := os.ReadFile(path) - if err != nil { - return nil, err - } - return ParseDownwardAPI(string(b)) -} - -// ParseDownwardAPI parses fields which are stored as format `%s=%q` back to a map -func ParseDownwardAPI(i string) (map[string]string, error) { - res := map[string]string{} - for _, line := range strings.Split(i, "\n") { - sl := strings.SplitN(line, "=", 2) - if len(sl) != 2 { - continue - } - key := sl[0] - // Strip the leading/trailing quotes - val, err := strconv.Unquote(sl[1]) - if err != nil { - return nil, fmt.Errorf("failed to unquote %v: %v", sl[1], err) - } - res[key] = val - } - return res, nil -} - -func removeDuplicates(values []string) []string { - set := sets.New() - newValues := make([]string, 0, len(values)) - for _, v := range values { - if !set.Contains(v) { - set.Insert(v) - newValues = append(newValues, v) - } - } - return newValues -} diff --git a/pkg/bootstrap/config_test.go b/pkg/bootstrap/config_test.go deleted file mode 100644 index a74c49e61..000000000 --- a/pkg/bootstrap/config_test.go +++ /dev/null @@ -1,279 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bootstrap - -import ( - "encoding/json" - "os" - "reflect" - "testing" -) - -import ( - . "github.com/onsi/gomega" - "istio.io/api/mesh/v1alpha1" - "k8s.io/kubectl/pkg/util/fieldpath" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/bootstrap/option" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -func TestParseDownwardApi(t *testing.T) { - cases := []struct { - name string - m map[string]string - }{ - { - "empty", - map[string]string{}, - }, - { - "single", - map[string]string{"foo": "bar"}, - }, - { - "multi", - map[string]string{ - "app": "istio-ingressgateway", - "chart": "gateways", - "heritage": "Tiller", - "istio": "ingressgateway", - "pod-template-hash": "54756dbcf9", - }, - }, - { - "multi line", - map[string]string{ - "config": `foo: bar -other: setting`, - "istio": "ingressgateway", - }, - }, - { - "weird values", - map[string]string{ - "foo": `a1_-.as1`, - "bar": `a=b`, - }, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - // Using the function kubernetes actually uses to write this, we do a round trip of - // map -> file -> map and ensure the input and output are the same - got, err := ParseDownwardAPI(fieldpath.FormatMap(tt.m)) - if !reflect.DeepEqual(got, tt.m) { - t.Fatalf("expected %v, got %v with err: %v", tt.m, got, err) - } - }) - } -} - -func TestGetNodeMetaData(t *testing.T) { - inputOwner := "test" - inputWorkloadName := "workload" - - expectOwner := "test" - expectWorkloadName := "workload" - expectExitOnZeroActiveConnections := model.StringBool(true) - - os.Setenv(IstioMetaPrefix+"OWNER", inputOwner) - os.Setenv(IstioMetaPrefix+"WORKLOAD_NAME", inputWorkloadName) - - node, err := GetNodeMetaData(MetadataOptions{ - ID: "test", - Envs: os.Environ(), - ExitOnZeroActiveConnections: true, - }) - - g := NewWithT(t) - g.Expect(err).Should(BeNil()) - g.Expect(node.Metadata.Owner).To(Equal(expectOwner)) - g.Expect(node.Metadata.WorkloadName).To(Equal(expectWorkloadName)) - g.Expect(node.Metadata.ExitOnZeroActiveConnections).To(Equal(expectExitOnZeroActiveConnections)) - g.Expect(node.RawMetadata["OWNER"]).To(Equal(expectOwner)) - g.Expect(node.RawMetadata["WORKLOAD_NAME"]).To(Equal(expectWorkloadName)) -} - -func TestConvertNodeMetadata(t *testing.T) { - node := &model.Node{ - ID: "test", - Metadata: &model.BootstrapNodeMetadata{ - NodeMetadata: model.NodeMetadata{ - ProxyConfig: &model.NodeMetaProxyConfig{ - ClusterName: &v1alpha1.ProxyConfig_ServiceCluster{ - ServiceCluster: "cluster", - }, - }, - }, - Owner: "real-owner", - }, - RawMetadata: map[string]interface{}{}, - } - node.Metadata.Owner = "real-owner" - node.RawMetadata["OWNER"] = "fake-owner" - node.RawMetadata["UNKNOWN"] = "new-field" - node.RawMetadata["A"] = 1 - node.RawMetadata["B"] = map[string]interface{}{"b": 1} - - out := ConvertNodeToXDSNode(node) - { - b, err := protomarshal.MarshalProtoNames(out) - if err != nil { - t.Fatalf("failed to marshal: %v", err) - } - // nolint: lll - want := `{"id":"test","cluster":"cluster","metadata":{"A":1,"B":{"b":1},"OWNER":"real-owner","PROXY_CONFIG":{"serviceCluster":"cluster"},"UNKNOWN":"new-field"}}` - test.JSONEquals(t, want, string(b)) - } - - node2 := ConvertXDSNodeToNode(out) - { - got, err := json.Marshal(node2) - if err != nil { - t.Fatalf("failed to marshal: %v", err) - } - // nolint: lll - want := `{"ID":"test","Metadata":{"PROXY_CONFIG":{"serviceCluster":"cluster"},"OWNER":"real-owner"},"RawMetadata":null,"Locality":null}` - if want != string(got) { - t.Fatalf("ConvertXDSNodeToNode: got %q, want %q", string(got), want) - } - } -} - -func TestConvertNodeServiceClusterNaming(t *testing.T) { - cases := []struct { - name string - proxyCfg *model.NodeMetaProxyConfig - labels map[string]string - wantCluster string - }{ - { - name: "no cluster name (no labels)", - proxyCfg: &model.NodeMetaProxyConfig{}, - wantCluster: "istio-proxy.bar", - }, - { - name: "no cluster name (defaults)", - proxyCfg: &model.NodeMetaProxyConfig{}, - labels: map[string]string{"app": "foo"}, - wantCluster: "foo.bar", - }, - { - name: "service cluster", - proxyCfg: &model.NodeMetaProxyConfig{ - ClusterName: &v1alpha1.ProxyConfig_ServiceCluster{ - ServiceCluster: "foo", - }, - }, - wantCluster: "foo", - }, - { - name: "trace service name (app label and namespace)", - proxyCfg: &model.NodeMetaProxyConfig{ - ClusterName: &v1alpha1.ProxyConfig_TracingServiceName_{ - TracingServiceName: v1alpha1.ProxyConfig_APP_LABEL_AND_NAMESPACE, - }, - }, - labels: map[string]string{"app": "foo"}, - wantCluster: "foo.bar", - }, - { - name: "trace service name (canonical name)", - proxyCfg: &model.NodeMetaProxyConfig{ - ClusterName: &v1alpha1.ProxyConfig_TracingServiceName_{ - TracingServiceName: v1alpha1.ProxyConfig_CANONICAL_NAME_ONLY, - }, - }, - labels: map[string]string{"service.istio.io/canonical-name": "foo"}, - wantCluster: "foo", - }, - { - name: "trace service name (canonical name and namespace)", - proxyCfg: &model.NodeMetaProxyConfig{ - ClusterName: &v1alpha1.ProxyConfig_TracingServiceName_{ - TracingServiceName: v1alpha1.ProxyConfig_CANONICAL_NAME_AND_NAMESPACE, - }, - }, - labels: map[string]string{"service.istio.io/canonical-name": "foo"}, - wantCluster: "foo.bar", - }, - } - - for _, v := range cases { - t.Run(v.name, func(tt *testing.T) { - node := &model.Node{ - ID: "test", - Metadata: &model.BootstrapNodeMetadata{ - NodeMetadata: model.NodeMetadata{ - ProxyConfig: v.proxyCfg, - Labels: v.labels, - Namespace: "bar", - }, - }, - } - out := ConvertNodeToXDSNode(node) - if got, want := out.Cluster, v.wantCluster; got != want { - tt.Errorf("ConvertNodeToXDSNode(%#v) => cluster = %s; want %s", node, got, want) - } - }) - } -} - -func TestGetStatOptions(t *testing.T) { - cases := []struct { - name string - metadataOptions MetadataOptions - // TODO(ramaraochavali): Add validation for prefix and tags also. - wantInclusionSuffixes []string - }{ - { - name: "with exit on zero connections enabled", - metadataOptions: MetadataOptions{ - ID: "test", - Envs: os.Environ(), - ProxyConfig: &v1alpha1.ProxyConfig{}, - ExitOnZeroActiveConnections: true, - }, - wantInclusionSuffixes: []string{"rbac.allowed", "rbac.denied", "shadow_allowed", "shadow_denied", "downstream_cx_active"}, - }, - { - name: "with exit on zero connections disabled", - metadataOptions: MetadataOptions{ - ID: "test", - Envs: os.Environ(), - ProxyConfig: &v1alpha1.ProxyConfig{}, - ExitOnZeroActiveConnections: false, - }, - wantInclusionSuffixes: []string{"rbac.allowed", "rbac.denied", "shadow_allowed", "shadow_denied"}, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(tt *testing.T) { - node, _ := GetNodeMetaData(tc.metadataOptions) - options := getStatsOptions(node.Metadata) - templateParams, _ := option.NewTemplateParams(options...) - inclusionSuffixes := templateParams["inclusionSuffix"] - if !reflect.DeepEqual(inclusionSuffixes, tc.wantInclusionSuffixes) { - tt.Errorf("unexpected inclusion suffixes. want: %v, got: %v", tc.wantInclusionSuffixes, inclusionSuffixes) - } - }) - } -} diff --git a/pkg/bootstrap/instance.go b/pkg/bootstrap/instance.go deleted file mode 100644 index 9cd16b2fe..000000000 --- a/pkg/bootstrap/instance.go +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bootstrap - -import ( - "encoding/json" - "fmt" - "io" - "os" - "path" - "strings" - "text/template" -) - -import ( - "github.com/Masterminds/sprig/v3" - "istio.io/pkg/env" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" -) - -const ( - // EpochFileTemplate is a template for the root config JSON - EpochFileTemplate = "envoy-rev%d.%s" - DefaultCfgDir = "./var/lib/istio/envoy/envoy_bootstrap_tmpl.json" -) - -// TODO(nmittler): Move this to application code. This shouldn't be declared in a library. -var overrideVar = env.RegisterStringVar("ISTIO_BOOTSTRAP", "", "") - -// Instance of a configured Envoy bootstrap writer. -type Instance interface { - // WriteTo writes the content of the Envoy bootstrap to the given writer. - WriteTo(templateFile string, w io.Writer) error - - // CreateFileForEpoch generates an Envoy bootstrap file for a particular epoch. - CreateFileForEpoch(epoch int) (string, error) -} - -// New creates a new Instance of an Envoy bootstrap writer. -func New(cfg Config) Instance { - return &instance{ - Config: cfg, - } -} - -type instance struct { - Config -} - -func (i *instance) WriteTo(templateFile string, w io.Writer) error { - // Get the input bootstrap template. - t, err := newTemplate(templateFile) - if err != nil { - return err - } - - // Create the parameters for the template. - templateParams, err := i.toTemplateParams() - if err != nil { - return err - } - - // Execute the template. - return t.Execute(w, templateParams) -} - -func toJSON(i interface{}) string { - if i == nil { - return "{}" - } - - ba, err := json.Marshal(i) - if err != nil { - log.Warnf("Unable to marshal %v: %v", i, err) - return "{}" - } - - return string(ba) -} - -// GetEffectiveTemplatePath gets the template file that should be used for bootstrap -func GetEffectiveTemplatePath(pc *model.NodeMetaProxyConfig) string { - var templateFilePath string - switch { - case pc.CustomConfigFile != "": - templateFilePath = pc.CustomConfigFile - case pc.ProxyBootstrapTemplatePath != "": - templateFilePath = pc.ProxyBootstrapTemplatePath - default: - templateFilePath = DefaultCfgDir - } - override := overrideVar.Get() - if len(override) > 0 { - templateFilePath = override - } - return templateFilePath -} - -func (i *instance) CreateFileForEpoch(epoch int) (string, error) { - // Create the output file. - if err := os.MkdirAll(i.Metadata.ProxyConfig.ConfigPath, 0o700); err != nil { - return "", err - } - - templateFile := GetEffectiveTemplatePath(i.Metadata.ProxyConfig) - - outputFilePath := configFile(i.Metadata.ProxyConfig.ConfigPath, templateFile, epoch) - outputFile, err := os.Create(outputFilePath) - if err != nil { - return "", err - } - defer func() { _ = outputFile.Close() }() - - // Write the content of the file. - if err := i.WriteTo(templateFile, outputFile); err != nil { - return "", err - } - - return outputFilePath, err -} - -func configFile(config string, templateFile string, epoch int) string { - suffix := "json" - // Envoy will interpret the file extension to determine the type. We should detect yaml inputs - if strings.HasSuffix(templateFile, ".yaml.tmpl") || strings.HasSuffix(templateFile, ".yaml") { - suffix = "yaml" - } - return path.Join(config, fmt.Sprintf(EpochFileTemplate, epoch, suffix)) -} - -func newTemplate(templateFilePath string) (*template.Template, error) { - cfgTmpl, err := os.ReadFile(templateFilePath) - if err != nil { - return nil, err - } - - funcMap := template.FuncMap{ - "toJSON": toJSON, - } - return template.New("bootstrap").Funcs(funcMap).Funcs(sprig.GenericFuncMap()).Parse(string(cfgTmpl)) -} diff --git a/pkg/bootstrap/instance_test.go b/pkg/bootstrap/instance_test.go deleted file mode 100644 index 06ef05aec..000000000 --- a/pkg/bootstrap/instance_test.go +++ /dev/null @@ -1,684 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package bootstrap - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" - "net/url" - "os" - "path" - "path/filepath" - "reflect" - "regexp" - "strings" - "testing" -) - -import ( - v1 "github.com/census-instrumentation/opencensus-proto/gen-go/trace/v1" - bootstrap "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v3" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - trace "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3" - matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/encoding/prototext" - "google.golang.org/protobuf/testing/protocmp" - "istio.io/api/annotation" - meshconfig "istio.io/api/mesh/v1alpha1" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/test/util" - "github.com/apache/dubbo-go-pixiu/pkg/bootstrap/platform" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -type stats struct { - prefixes string - suffixes string - regexps string -} - -var ( - // The following set of inclusions add minimal upstream and downstream metrics. - // Upstream metrics record client side measurements. - // Downstream metrics record server side measurements. - upstreamStatsSuffixes = "upstream_rq_1xx,upstream_rq_2xx,upstream_rq_3xx,upstream_rq_4xx,upstream_rq_5xx," + - "upstream_rq_time,upstream_cx_tx_bytes_total,upstream_cx_rx_bytes_total,upstream_cx_total" - - // example downstream metric: http.10.16.48.230_8080.downstream_rq_2xx - // http._.downstream_rq_2xx - // This metric is collected at the inbound listener at a sidecar. - // All the other downstream metrics at a sidecar are from the application to the local sidecar. - downstreamStatsSuffixes = "downstream_rq_1xx,downstream_rq_2xx,downstream_rq_3xx,downstream_rq_4xx,downstream_rq_5xx," + - "downstream_rq_time,downstream_cx_tx_bytes_total,downstream_cx_rx_bytes_total,downstream_cx_total" -) - -// Generate configs for the default configs used by istio. -// If the template is updated, refresh golden files using: -// REFRESH_GOLDEN=true go test ./pkg/bootstrap/... -func TestGolden(t *testing.T) { - var ts *httptest.Server - - cases := []struct { - base string - envVars map[string]string - annotations map[string]string - sdsUDSPath string - sdsTokenPath string - expectLightstepAccessToken bool - stats stats - checkLocality bool - stsPort int - platformMeta map[string]string - setup func() - teardown func() - check func(got *bootstrap.Bootstrap, t *testing.T) - }{ - { - base: "xdsproxy", - }, - { - base: "auth", - }, - { - base: "authsds", - sdsUDSPath: "udspath", - sdsTokenPath: "/var/run/secrets/tokens/istio-token", - }, - { - base: "default", - }, - { - base: "running", - envVars: map[string]string{ - "ISTIO_META_ISTIO_PROXY_SHA": "istio-proxy:sha", - "ISTIO_META_INTERCEPTION_MODE": "REDIRECT", - "ISTIO_META_ISTIO_VERSION": "release-3.1", - "ISTIO_META_POD_NAME": "svc-0-0-0-6944fb884d-4pgx8", - "POD_NAME": "svc-0-0-0-6944fb884d-4pgx8", - "POD_NAMESPACE": "test", - "INSTANCE_IP": "10.10.10.1", - "ISTIO_METAJSON_LABELS": `{"version": "v1alpha1", "app": "test", "istio-locality":"regionA.zoneB.sub_zoneC"}`, - }, - annotations: map[string]string{ - "istio.io/insecurepath": "{\"paths\":[\"/metrics\",\"/live\"]}", - }, - checkLocality: true, - }, - { - base: "runningsds", - envVars: map[string]string{ - "ISTIO_META_ISTIO_PROXY_SHA": "istio-proxy:sha", - "ISTIO_META_INTERCEPTION_MODE": "REDIRECT", - "ISTIO_META_ISTIO_VERSION": "release-3.1", - "ISTIO_META_POD_NAME": "svc-0-0-0-6944fb884d-4pgx8", - "POD_NAME": "svc-0-0-0-6944fb884d-4pgx8", - "POD_NAMESPACE": "test", - "INSTANCE_IP": "10.10.10.1", - "ISTIO_METAJSON_LABELS": `{"version": "v1alpha1", "app": "test", "istio-locality":"regionA.zoneB.sub_zoneC"}`, - }, - annotations: map[string]string{ - "istio.io/insecurepath": "{\"paths\":[\"/metrics\",\"/live\"]}", - }, - sdsUDSPath: "udspath", - sdsTokenPath: "/var/run/secrets/tokens/istio-token", - checkLocality: true, - }, - { - base: "tracing_lightstep", - expectLightstepAccessToken: true, - }, - { - base: "tracing_zipkin", - }, - { - base: "tracing_datadog", - }, - { - base: "metrics_no_statsd", - }, - { - base: "tracing_stackdriver", - stsPort: 15463, - platformMeta: map[string]string{ - "gcp_project": "my-sd-project", - }, - setup: func() { - ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, "my-sd-project") - })) - - u, err := url.Parse(ts.URL) - if err != nil { - t.Fatalf("Unable to parse mock server url: %v", err) - } - _ = os.Setenv("GCE_METADATA_HOST", u.Host) - }, - teardown: func() { - if ts != nil { - ts.Close() - } - _ = os.Unsetenv("GCE_METADATA_HOST") - }, - check: func(got *bootstrap.Bootstrap, t *testing.T) { - // nolint: staticcheck - cfg := got.Tracing.Http.GetTypedConfig() - sdMsg := &trace.OpenCensusConfig{} - if err := cfg.UnmarshalTo(sdMsg); err != nil { - t.Fatalf("unable to parse: %v %v", cfg, err) - } - - want := &trace.OpenCensusConfig{ - TraceConfig: &v1.TraceConfig{ - Sampler: &v1.TraceConfig_ConstantSampler{ - ConstantSampler: &v1.ConstantSampler{ - Decision: v1.ConstantSampler_ALWAYS_PARENT, - }, - }, - MaxNumberOfAttributes: 200, - MaxNumberOfAnnotations: 201, - MaxNumberOfMessageEvents: 201, - MaxNumberOfLinks: 200, - }, - StackdriverExporterEnabled: true, - StdoutExporterEnabled: true, - StackdriverProjectId: "my-sd-project", - StackdriverGrpcService: &core.GrpcService{ - TargetSpecifier: &core.GrpcService_GoogleGrpc_{ - GoogleGrpc: &core.GrpcService_GoogleGrpc{ - TargetUri: "cloudtrace.googleapis.com", - StatPrefix: "oc_stackdriver_tracer", - ChannelCredentials: &core.GrpcService_GoogleGrpc_ChannelCredentials{ - CredentialSpecifier: &core.GrpcService_GoogleGrpc_ChannelCredentials_SslCredentials{ - SslCredentials: &core.GrpcService_GoogleGrpc_SslCredentials{}, - }, - }, - CallCredentials: []*core.GrpcService_GoogleGrpc_CallCredentials{ - { - CredentialSpecifier: &core.GrpcService_GoogleGrpc_CallCredentials_StsService_{ - StsService: &core.GrpcService_GoogleGrpc_CallCredentials_StsService{ - TokenExchangeServiceUri: "http://localhost:15463/token", - SubjectTokenPath: "./var/run/secrets/tokens/istio-token", - SubjectTokenType: "urn:ietf:params:oauth:token-type:jwt", - Scope: "https://www.googleapis.com/auth/cloud-platform", - }, - }, - }, - }, - }, - }, - InitialMetadata: []*core.HeaderValue{ - { - Key: "x-goog-user-project", - Value: "my-sd-project", - }, - }, - }, - IncomingTraceContext: []trace.OpenCensusConfig_TraceContext{ - trace.OpenCensusConfig_CLOUD_TRACE_CONTEXT, - trace.OpenCensusConfig_TRACE_CONTEXT, - trace.OpenCensusConfig_GRPC_TRACE_BIN, - trace.OpenCensusConfig_B3, - }, - OutgoingTraceContext: []trace.OpenCensusConfig_TraceContext{ - trace.OpenCensusConfig_CLOUD_TRACE_CONTEXT, - trace.OpenCensusConfig_TRACE_CONTEXT, - trace.OpenCensusConfig_GRPC_TRACE_BIN, - trace.OpenCensusConfig_B3, - }, - } - - if diff := cmp.Diff(sdMsg, want, protocmp.Transform()); diff != "" { - t.Fatalf("got unexpected diff: %v", diff) - } - }, - }, - { - base: "tracing_opencensusagent", - }, - { - // Specify zipkin/statsd address, similar with the default config in v1 tests - base: "all", - }, - { - base: "stats_inclusion", - annotations: map[string]string{ - "sidecar.istio.io/statsInclusionPrefixes": "prefix1,prefix2,http.{pod_ip}_", - "sidecar.istio.io/statsInclusionSuffixes": "suffix1,suffix2" + "," + upstreamStatsSuffixes + "," + downstreamStatsSuffixes, - "sidecar.istio.io/statsInclusionRegexps": "http.[0-9]*\\.[0-9]*\\.[0-9]*\\.[0-9]*_8080.downstream_rq_time", - "sidecar.istio.io/extraStatTags": "dlp_status,dlp_error", - }, - stats: stats{ - prefixes: "prefix1,prefix2,http.10.3.3.3_,http.10.4.4.4_,http.10.5.5.5_,http.10.6.6.6_", - suffixes: "suffix1,suffix2," + upstreamStatsSuffixes + "," + downstreamStatsSuffixes, - regexps: "http.[0-9]*\\.[0-9]*\\.[0-9]*\\.[0-9]*_8080.downstream_rq_time", - }, - }, - { - base: "tracing_tls", - }, - { - base: "tracing_tls_custom_sni", - }, - } - - for _, c := range cases { - t.Run("Bootstrap-"+c.base, func(t *testing.T) { - out := t.TempDir() - if c.setup != nil { - c.setup() - } - if c.teardown != nil { - defer c.teardown() - } - - proxyConfig, err := loadProxyConfig(c.base, out, t) - if err != nil { - t.Fatalf("unable to load proxy config: %s\n%v", c.base, err) - } - - _, localEnv := createEnv(t, map[string]string{}, c.annotations) - for k, v := range c.envVars { - localEnv = append(localEnv, k+"="+v) - } - - plat := &fakePlatform{ - meta: c.platformMeta, - } - - annoFile, err := os.CreateTemp("", "annotations") - if err != nil { - t.Fatal(err) - } - defer os.Remove(annoFile.Name()) - for k, v := range c.annotations { - annoFile.Write([]byte(fmt.Sprintf("%s=%q\n", k, v))) - } - - node, err := GetNodeMetaData(MetadataOptions{ - ID: "sidecar~1.2.3.4~foo~bar", - Envs: localEnv, - Platform: plat, - InstanceIPs: []string{"10.3.3.3", "10.4.4.4", "10.5.5.5", "10.6.6.6", "10.4.4.4"}, - StsPort: c.stsPort, - ProxyConfig: proxyConfig, - PilotSubjectAltName: []string{ - "spiffe://cluster.local/ns/dubbo-system/sa/istio-pilot-service-account", - }, - OutlierLogPath: "/dev/stdout", - annotationFilePath: annoFile.Name(), - EnvoyPrometheusPort: 15090, - EnvoyStatusPort: 15021, - }) - if err != nil { - t.Fatal(err) - } - fn, err := New(Config{ - Node: node, - }).CreateFileForEpoch(0) - if err != nil { - t.Fatal(err) - } - - read, err := os.ReadFile(fn) - if err != nil { - t.Error("Error reading generated file ", err) - return - } - - // apply minor modifications for the generated file so that tests are consistent - // across different env setups - err = os.WriteFile(fn, correctForEnvDifference(read, !c.checkLocality, out), 0o700) - if err != nil { - t.Error("Error modifying generated file ", err) - return - } - - // re-read generated file with the changes having been made - read, err = os.ReadFile(fn) - if err != nil { - t.Error("Error reading generated file ", err) - return - } - - goldenFile := "testdata/" + c.base + "_golden.json" - util.RefreshGoldenFile(t, read, goldenFile) - - golden, err := os.ReadFile(goldenFile) - if err != nil { - golden = []byte{} - } - - realM := &bootstrap.Bootstrap{} - goldenM := &bootstrap.Bootstrap{} - - jgolden, err := yaml.YAMLToJSON(golden) - if err != nil { - t.Fatalf("unable to convert: %s %v", c.base, err) - } - - if err = protomarshal.Unmarshal(jgolden, goldenM); err != nil { - t.Fatalf("invalid json %s %s\n%v", c.base, err, string(jgolden)) - } - - if err = goldenM.Validate(); err != nil { - t.Fatalf("invalid golden %s: %v", c.base, err) - } - - if err = protomarshal.Unmarshal(read, realM); err != nil { - t.Fatalf("invalid json %v\n%s", err, string(read)) - } - - if err = realM.Validate(); err != nil { - t.Fatalf("invalid generated file %s: %v", c.base, err) - } - - checkStatsMatcher(t, realM, goldenM, c.stats) - - if c.check != nil { - c.check(realM, t) - } - - checkOpencensusConfig(t, realM, goldenM) - - if diff := cmp.Diff(goldenM, realM, protocmp.Transform()); diff != "" { - t.Logf("difference: %s", diff) - t.Fatalf("\n got: %s\nwant: %s", prettyPrint(read), prettyPrint(jgolden)) - } - - // Check if the LightStep access token file exists - _, err = os.Stat(lightstepAccessTokenFile(path.Dir(fn))) - if c.expectLightstepAccessToken { - if os.IsNotExist(err) { - t.Error("expected to find a LightStep access token file but none found") - } else if err != nil { - t.Error("error running Stat on file: ", err) - } - } else { - if err == nil { - t.Error("found a LightStep access token file but none was expected") - } else if !os.IsNotExist(err) { - t.Error("error running Stat on file: ", err) - } - } - }) - } -} - -func prettyPrint(b []byte) []byte { - var out bytes.Buffer - _ = json.Indent(&out, b, "", " ") - return out.Bytes() -} - -func checkListStringMatcher(t *testing.T, got *matcher.ListStringMatcher, want string, typ string) { - var patterns []string - for _, pattern := range got.GetPatterns() { - var pat string - switch typ { - case "prefix": - pat = pattern.GetPrefix() - case "suffix": - pat = pattern.GetSuffix() - case "regexp": - // Migration tracked in https://github.com/istio/istio/issues/17127 - //nolint: staticcheck - pat = pattern.GetSafeRegex().GetRegex() - } - - if pat != "" { - patterns = append(patterns, pat) - } - } - gotPattern := strings.Join(patterns, ",") - if want != gotPattern { - t.Fatalf("%s mismatch:\ngot: %s\nwant: %s", typ, gotPattern, want) - } -} - -// nolint: staticcheck -func checkOpencensusConfig(t *testing.T, got, want *bootstrap.Bootstrap) { - if want.Tracing == nil { - return - } - - if want.Tracing.Http.Name != "envoy.tracers.opencensus" { - return - } - - if diff := cmp.Diff(got.Tracing.Http, want.Tracing.Http, protocmp.Transform()); diff != "" { - t.Fatalf("t diff: %v\ngot:\n %v\nwant:\n %v\n", diff, got.Tracing.Http, want.Tracing.Http) - } -} - -func checkStatsMatcher(t *testing.T, got, want *bootstrap.Bootstrap, stats stats) { - gsm := got.GetStatsConfig().GetStatsMatcher() - - if stats.prefixes == "" { - stats.prefixes = v2Prefixes + requiredEnvoyStatsMatcherInclusionPrefixes + v2Suffix - } else { - stats.prefixes = v2Prefixes + stats.prefixes + "," + requiredEnvoyStatsMatcherInclusionPrefixes + v2Suffix - } - if stats.suffixes == "" { - stats.suffixes = rbacEnvoyStatsMatcherInclusionSuffix - } else { - stats.suffixes += "," + rbacEnvoyStatsMatcherInclusionSuffix - } - - if err := gsm.Validate(); err != nil { - t.Fatalf("Generated invalid matcher: %v", err) - } - - checkListStringMatcher(t, gsm.GetInclusionList(), stats.prefixes, "prefix") - checkListStringMatcher(t, gsm.GetInclusionList(), stats.suffixes, "suffix") - checkListStringMatcher(t, gsm.GetInclusionList(), stats.regexps, "regexp") - - // remove StatsMatcher for general matching - got.StatsConfig.StatsMatcher = nil - want.StatsConfig.StatsMatcher = nil - - // remove StatsMatcher metadata from matching - delete(got.Node.Metadata.Fields, annotation.SidecarStatsInclusionPrefixes.Name) - delete(want.Node.Metadata.Fields, annotation.SidecarStatsInclusionPrefixes.Name) - delete(got.Node.Metadata.Fields, annotation.SidecarStatsInclusionSuffixes.Name) - delete(want.Node.Metadata.Fields, annotation.SidecarStatsInclusionSuffixes.Name) - delete(got.Node.Metadata.Fields, annotation.SidecarStatsInclusionRegexps.Name) - delete(want.Node.Metadata.Fields, annotation.SidecarStatsInclusionRegexps.Name) -} - -type regexReplacement struct { - pattern *regexp.Regexp - replacement []byte -} - -// correctForEnvDifference corrects the portions of a generated bootstrap config that vary depending on the environment -// so that they match the golden file's expected value. -func correctForEnvDifference(in []byte, excludeLocality bool, tmpDir string) []byte { - replacements := []regexReplacement{ - // Lightstep access tokens are written to a file and that path is dependent upon the environment variables that - // are set. Standardize the path so that golden files can be properly checked. - { - pattern: regexp.MustCompile(`("access_token_file": ").*(lightstep_access_token.txt")`), - replacement: []byte("$1/test-path/$2"), - }, - { - // Example: "customConfigFile":"../../tools/packaging/common/envoy_bootstrap.json" - // The path may change in CI/other machines - pattern: regexp.MustCompile(`("customConfigFile":").*(envoy_bootstrap.json")`), - replacement: []byte(`"customConfigFile":"envoy_bootstrap.json"`), - }, - { - pattern: regexp.MustCompile(tmpDir), - replacement: []byte(`/tmp`), - }, - { - pattern: regexp.MustCompile(`("path": ".*/XDS")`), - replacement: []byte(`"path": "/tmp/XDS"`), - }, - } - if excludeLocality { - // zone and region can vary based on the environment, so it shouldn't be considered in the diff. - replacements = append(replacements, - regexReplacement{ - pattern: regexp.MustCompile(`"zone": ".+"`), - replacement: []byte("\"zone\": \"\""), - }, - regexReplacement{ - pattern: regexp.MustCompile(`"region": ".+"`), - replacement: []byte("\"region\": \"\""), - }) - } - - out := in - for _, r := range replacements { - out = r.pattern.ReplaceAll(out, r.replacement) - } - return out -} - -func loadProxyConfig(base, out string, _ *testing.T) (*meshconfig.ProxyConfig, error) { - content, err := os.ReadFile("testdata/" + base + ".proxycfg") - if err != nil { - return nil, err - } - cfg := &meshconfig.ProxyConfig{} - - err = prototext.Unmarshal(content, cfg) - if err != nil { - return nil, err - } - - // Exported from makefile or env - cfg.ConfigPath = filepath.Join(out, "/bootstrap/", base) - cfg.CustomConfigFile = filepath.Join(env.IstioSrc, "/tools/packaging/common/envoy_bootstrap.json") - if cfg.StatusPort == 0 { - cfg.StatusPort = 15020 - } - return cfg, nil -} - -// createEnv takes labels and annotations are returns environment in go format. -func createEnv(t *testing.T, labels map[string]string, anno map[string]string) (map[string]string, []string) { - merged := map[string]string{} - mergeMap(merged, labels) - mergeMap(merged, anno) - - envs := make([]string, 0) - - if labels != nil { - envs = append(envs, encodeAsJSON(t, labels, "LABELS")) - } - - if anno != nil { - envs = append(envs, encodeAsJSON(t, anno, "ANNOTATIONS")) - } - return merged, envs -} - -func encodeAsJSON(t *testing.T, data map[string]string, name string) string { - jsonStr, err := json.Marshal(data) - if err != nil { - t.Fatalf("failed to marshal %s %v: %v", name, data, err) - } - return IstioMetaJSONPrefix + name + "=" + string(jsonStr) -} - -func TestNodeMetadataEncodeEnvWithIstioMetaPrefix(t *testing.T) { - originalKey := "foo" - notIstioMetaKey := "NOT_AN_" + IstioMetaPrefix + originalKey - anIstioMetaKey := IstioMetaPrefix + originalKey - envs := []string{ - notIstioMetaKey + "=bar", - anIstioMetaKey + "=baz", - } - node, err := GetNodeMetaData(MetadataOptions{ - ID: "test", - Envs: envs, - ProxyConfig: &meshconfig.ProxyConfig{}, - }) - nm := node.Metadata - if err != nil { - t.Fatal(err) - } - if _, ok := nm.Raw[notIstioMetaKey]; ok { - t.Fatalf("%s should not be encoded in node metadata", notIstioMetaKey) - } - - if _, ok := nm.Raw[anIstioMetaKey]; ok { - t.Fatalf("%s should not be encoded in node metadata. The prefix '%s' should be stripped", anIstioMetaKey, IstioMetaPrefix) - } - if val, ok := nm.Raw[originalKey]; !ok { - t.Fatalf("%s has the prefix %s and it should be encoded in the node metadata", originalKey, IstioMetaPrefix) - } else if val != "baz" { - t.Fatalf("unexpected value node metadata %s. got %s, want: %s", originalKey, val, "baz") - } -} - -func TestNodeMetadata(t *testing.T) { - envs := []string{ - "ISTIO_META_ISTIO_VERSION=1.0.0", - `ISTIO_METAJSON_LABELS={"foo":"bar"}`, - } - node, err := GetNodeMetaData(MetadataOptions{ - ID: "test", - Envs: envs, - ProxyConfig: &meshconfig.ProxyConfig{}, - }) - nm := node.Metadata - if err != nil { - t.Fatal(err) - } - if nm.IstioVersion != "1.0.0" { - t.Fatalf("Expected IstioVersion 1.0.0, got %v", nm.IstioVersion) - } - if !reflect.DeepEqual(nm.Labels, map[string]string{"foo": "bar"}) { - t.Fatalf("Expected Labels foo: bar, got %v", nm.Labels) - } -} - -func mergeMap(to map[string]string, from map[string]string) { - for k, v := range from { - to[k] = v - } -} - -type fakePlatform struct { - platform.Environment - - meta map[string]string - labels map[string]string -} - -func (f *fakePlatform) Metadata() map[string]string { - return f.meta -} - -func (f *fakePlatform) Locality() *core.Locality { - return &core.Locality{} -} - -func (f *fakePlatform) Labels() map[string]string { - return f.labels -} - -func (f *fakePlatform) IsKubernetes() bool { - return true -} diff --git a/pkg/bootstrap/option/convert.go b/pkg/bootstrap/option/convert.go deleted file mode 100644 index 7f3008a79..000000000 --- a/pkg/bootstrap/option/convert.go +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package option - -import ( - "encoding/json" - "fmt" - "net" - "os" - "strings" -) - -import ( - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - auth "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - "github.com/envoyproxy/go-control-plane/pkg/conversion" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "google.golang.org/protobuf/types/known/durationpb" - pstruct "google.golang.org/protobuf/types/known/structpb" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - meshAPI "istio.io/api/mesh/v1alpha1" - networkingAPI "istio.io/api/networking/v1alpha3" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - authn_model "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/model" - "github.com/apache/dubbo-go-pixiu/pkg/security" -) - -// TransportSocket wraps UpstreamTLSContext -type TransportSocket struct { - Name string `json:"name,omitempty"` - TypedConfig *pstruct.Struct `json:"typed_config,omitempty"` -} - -func keepaliveConverter(value *networkingAPI.ConnectionPoolSettings_TCPSettings_TcpKeepalive) convertFunc { - return func(*instance) (interface{}, error) { - upstreamConnectionOptions := &cluster.UpstreamConnectionOptions{ - TcpKeepalive: &core.TcpKeepalive{}, - } - - if value.Probes > 0 { - upstreamConnectionOptions.TcpKeepalive.KeepaliveProbes = &wrappers.UInt32Value{Value: value.Probes} - } - - if value.Time != nil && value.Time.Seconds > 0 { - upstreamConnectionOptions.TcpKeepalive.KeepaliveTime = &wrappers.UInt32Value{Value: uint32(value.Time.Seconds)} - } - - if value.Interval != nil && value.Interval.Seconds > 0 { - upstreamConnectionOptions.TcpKeepalive.KeepaliveInterval = &wrappers.UInt32Value{Value: uint32(value.Interval.Seconds)} - } - return convertToJSON(upstreamConnectionOptions), nil - } -} - -func transportSocketConverter(tls *networkingAPI.ClientTLSSettings, sniName string, metadata *model.BootstrapNodeMetadata, isH2 bool) convertFunc { - return func(*instance) (interface{}, error) { - tlsContext := tlsContextConvert(tls, sniName, metadata) - if tlsContext == nil { - return "", nil - } - if !isH2 { - tlsContext.CommonTlsContext.AlpnProtocols = nil - } - // This double conversion is to encode the typed config and get it out as struct - // so that convertToJSON properly encodes the structure. Since this is just for - // bootstrap generation this is better than having our custom structs. - tlsContextStruct, _ := conversion.MessageToStruct(util.MessageToAny(tlsContext)) - transportSocket := &TransportSocket{ - Name: wellknown.TransportSocketTls, - TypedConfig: tlsContextStruct, - } - return convertToJSON(transportSocket), nil - } -} - -// TODO(ramaraochavali): Unify this code with cluster upstream TLS settings logic. -func tlsContextConvert(tls *networkingAPI.ClientTLSSettings, sniName string, metadata *model.BootstrapNodeMetadata) *auth.UpstreamTlsContext { - tlsContext := &auth.UpstreamTlsContext{ - CommonTlsContext: &auth.CommonTlsContext{}, - } - - switch tls.Mode { - case networkingAPI.ClientTLSSettings_SIMPLE: - res := security.SdsCertificateConfig{ - CaCertificatePath: model.GetOrDefault(metadata.TLSClientRootCert, tls.CaCertificates), - } - - tlsContext.CommonTlsContext.ValidationContextType = &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{MatchSubjectAltNames: util.StringToExactMatch(tls.SubjectAltNames)}, - ValidationContextSdsSecretConfig: authn_model.ConstructSdsSecretConfig(res.GetRootResourceName()), - }, - } - tlsContext.CommonTlsContext.AlpnProtocols = util.ALPNH2Only - tlsContext.Sni = tls.Sni - case networkingAPI.ClientTLSSettings_MUTUAL: - res := security.SdsCertificateConfig{ - CertificatePath: model.GetOrDefault(metadata.TLSClientCertChain, tls.ClientCertificate), - PrivateKeyPath: model.GetOrDefault(metadata.TLSClientKey, tls.PrivateKey), - CaCertificatePath: model.GetOrDefault(metadata.TLSClientRootCert, tls.CaCertificates), - } - if len(res.GetResourceName()) > 0 { - tlsContext.CommonTlsContext.TlsCertificateSdsSecretConfigs = append(tlsContext.CommonTlsContext.TlsCertificateSdsSecretConfigs, - authn_model.ConstructSdsSecretConfig(res.GetResourceName())) - } - - tlsContext.CommonTlsContext.ValidationContextType = &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{MatchSubjectAltNames: util.StringToExactMatch(tls.SubjectAltNames)}, - ValidationContextSdsSecretConfig: authn_model.ConstructSdsSecretConfig(res.GetRootResourceName()), - }, - } - tlsContext.CommonTlsContext.AlpnProtocols = util.ALPNH2Only - tlsContext.Sni = tls.Sni - case networkingAPI.ClientTLSSettings_ISTIO_MUTUAL: - tlsContext.CommonTlsContext.TlsCertificateSdsSecretConfigs = append(tlsContext.CommonTlsContext.TlsCertificateSdsSecretConfigs, - authn_model.ConstructSdsSecretConfig(authn_model.SDSDefaultResourceName)) - - tlsContext.CommonTlsContext.ValidationContextType = &auth.CommonTlsContext_CombinedValidationContext{ - CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ - DefaultValidationContext: &auth.CertificateValidationContext{MatchSubjectAltNames: util.StringToExactMatch(tls.SubjectAltNames)}, - ValidationContextSdsSecretConfig: authn_model.ConstructSdsSecretConfig(authn_model.SDSRootResourceName), - }, - } - tlsContext.CommonTlsContext.AlpnProtocols = util.ALPNInMeshH2 - tlsContext.Sni = tls.Sni - // For ISTIO_MUTUAL if custom SNI is not provided, use the default SNI name. - if len(tls.Sni) == 0 { - tlsContext.Sni = sniName - } - default: - // No TLS. - return nil - } - return tlsContext -} - -func nodeMetadataConverter(metadata *model.BootstrapNodeMetadata, rawMeta map[string]interface{}) convertFunc { - return func(*instance) (interface{}, error) { - marshalString, err := marshalMetadata(metadata, rawMeta) - if err != nil { - return "", err - } - return marshalString, nil - } -} - -func sanConverter(sans []string) convertFunc { - return func(*instance) (interface{}, error) { - matchers := []string{} - for _, s := range sans { - matchers = append(matchers, fmt.Sprintf(`{"exact":"%s"}`, s)) - } - return "[" + strings.Join(matchers, ",") + "]", nil - } -} - -func addressConverter(addr string) convertFunc { - return func(o *instance) (interface{}, error) { - host, port, err := net.SplitHostPort(addr) - if err != nil { - return nil, fmt.Errorf("unable to parse %s address %q: %v", o.name, addr, err) - } - if host == "$(HOST_IP)" { - // Replace host with HOST_IP env var if it is "$(HOST_IP)". - // This is to support some tracer setting (Datadog, Zipkin), where "$(HOST_IP)"" is used for address. - // Tracer address used to be specified within proxy container params, and thus could be interpreted with pod HOST_IP env var. - // Now tracer config is passed in with mesh config volumn at gateway, k8s env var interpretation does not work. - // This is to achieve the same interpretation as k8s. - hostIPEnv := os.Getenv("HOST_IP") - if hostIPEnv != "" { - host = hostIPEnv - } - } - - return fmt.Sprintf("{\"address\": \"%s\", \"port_value\": %s}", host, port), nil - } -} - -func jsonConverter(d interface{}) convertFunc { - return func(o *instance) (interface{}, error) { - b, err := json.Marshal(d) - return string(b), err - } -} - -func durationConverter(value *durationpb.Duration) convertFunc { - return func(*instance) (interface{}, error) { - return value.AsDuration().String(), nil - } -} - -// openCensusAgentContextConverter returns a converter that returns the list of -// distributed trace contexts to propagate with envoy. -func openCensusAgentContextConverter(contexts []meshAPI.Tracing_OpenCensusAgent_TraceContext) convertFunc { - allContexts := `["TRACE_CONTEXT","GRPC_TRACE_BIN","CLOUD_TRACE_CONTEXT","B3"]` - return func(*instance) (interface{}, error) { - if len(contexts) == 0 { - return allContexts, nil - } - - var envoyContexts []string - for _, c := range contexts { - switch c { - // Ignore UNSPECIFIED - case meshAPI.Tracing_OpenCensusAgent_W3C_TRACE_CONTEXT: - envoyContexts = append(envoyContexts, "TRACE_CONTEXT") - case meshAPI.Tracing_OpenCensusAgent_GRPC_BIN: - envoyContexts = append(envoyContexts, "GRPC_TRACE_BIN") - case meshAPI.Tracing_OpenCensusAgent_CLOUD_TRACE_CONTEXT: - envoyContexts = append(envoyContexts, "CLOUD_TRACE_CONTEXT") - case meshAPI.Tracing_OpenCensusAgent_B3: - envoyContexts = append(envoyContexts, "B3") - } - } - return convertToJSON(envoyContexts), nil - } -} - -func convertToJSON(v interface{}) string { - if v == nil { - return "" - } - b, err := json.Marshal(v) - if err != nil { - log.Error(err.Error()) - return "" - } - return string(b) -} - -// marshalMetadata combines type metadata and untyped metadata and marshals to json -// This allows passing arbitrary metadata to Envoy, while still supported typed metadata for known types -func marshalMetadata(metadata *model.BootstrapNodeMetadata, rawMeta map[string]interface{}) (string, error) { - b, err := json.Marshal(metadata) - if err != nil { - return "", err - } - var output map[string]interface{} - if err := json.Unmarshal(b, &output); err != nil { - return "", err - } - // Add all untyped metadata - for k, v := range rawMeta { - // Do not override fields, as we may have made modifications to the type metadata - // This means we will only add "unknown" fields here - if _, f := output[k]; !f { - output[k] = v - } - } - res, err := json.Marshal(output) - if err != nil { - return "", err - } - return string(res), nil -} diff --git a/pkg/bootstrap/option/convert_test.go b/pkg/bootstrap/option/convert_test.go deleted file mode 100644 index f8a324290..000000000 --- a/pkg/bootstrap/option/convert_test.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package option - -import ( - "reflect" - "testing" -) - -import ( - networkingAPI "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" -) - -// nolint: lll -func TestTlsContextConvert(t *testing.T) { - tests := []struct { - desc string - tls *networkingAPI.ClientTLSSettings - sni string - meta *model.BootstrapNodeMetadata - expectTLSCtx string - }{ - { - desc: "no-tls", - tls: &networkingAPI.ClientTLSSettings{}, - sni: "", - meta: &model.BootstrapNodeMetadata{}, - expectTLSCtx: "null", - }, - { - desc: "tls-simple-no-cert", - tls: &networkingAPI.ClientTLSSettings{ - Mode: networkingAPI.ClientTLSSettings_SIMPLE, - }, - sni: "", - meta: &model.BootstrapNodeMetadata{}, - expectTLSCtx: "{\"common_tls_context\":{\"ValidationContextType\":{\"CombinedValidationContext\":{\"default_validation_context\":{}}},\"alpn_protocols\":[\"h2\"]}}", - }, - { - desc: "tls-simple-cert-cli", - tls: &networkingAPI.ClientTLSSettings{ - Mode: networkingAPI.ClientTLSSettings_SIMPLE, - CaCertificates: "foo.pem", - Sni: "foo", - }, - sni: "", - meta: &model.BootstrapNodeMetadata{}, - expectTLSCtx: `{"common_tls_context":{"ValidationContextType":{"CombinedValidationContext":{"default_validation_context":{},"validation_context_sds_secret_config":{"name":"file-root:foo.pem","sds_config":{"ConfigSourceSpecifier":{"ApiConfigSource":{"api_type":2,"transport_api_version":2,"grpc_services":[{"TargetSpecifier":{"EnvoyGrpc":{"cluster_name":"sds-grpc"}}}],"set_node_on_first_message_only":true}},"resource_api_version":2}}}},"alpn_protocols":["h2"]},"sni":"foo"}`, - }, - { - desc: "tls-simple-cert-cli-meta", - tls: &networkingAPI.ClientTLSSettings{ - Mode: networkingAPI.ClientTLSSettings_SIMPLE, - CaCertificates: "foo.pem", - Sni: "foo", - }, - sni: "", - meta: &model.BootstrapNodeMetadata{ - NodeMetadata: model.NodeMetadata{ - TLSClientRootCert: "/foo/bar/baz.pem", - }, - }, - expectTLSCtx: `{"common_tls_context":{"ValidationContextType":{"CombinedValidationContext":{"default_validation_context":{},"validation_context_sds_secret_config":{"name":"file-root:/foo/bar/baz.pem","sds_config":{"ConfigSourceSpecifier":{"ApiConfigSource":{"api_type":2,"transport_api_version":2,"grpc_services":[{"TargetSpecifier":{"EnvoyGrpc":{"cluster_name":"sds-grpc"}}}],"set_node_on_first_message_only":true}},"resource_api_version":2}}}},"alpn_protocols":["h2"]},"sni":"foo"}`, - }, - { - desc: "tls-cli-mutual-missing-certs", - meta: &model.BootstrapNodeMetadata{}, - tls: &networkingAPI.ClientTLSSettings{ - Mode: networkingAPI.ClientTLSSettings_MUTUAL, - }, - expectTLSCtx: `{"common_tls_context":{"ValidationContextType":{"CombinedValidationContext":{"default_validation_context":{}}},"alpn_protocols":["h2"]}}`, - }, - { - desc: "tls-cli-mutual", - tls: &networkingAPI.ClientTLSSettings{ - Mode: networkingAPI.ClientTLSSettings_MUTUAL, - ClientCertificate: "foo", - PrivateKey: "im-private-foo", - Sni: "bar", - }, - sni: "", - meta: &model.BootstrapNodeMetadata{}, - expectTLSCtx: `{"common_tls_context":{"tls_certificate_sds_secret_configs":[{"name":"file-cert:foo~im-private-foo","sds_config":{"ConfigSourceSpecifier":{"ApiConfigSource":{"api_type":2,"transport_api_version":2,"grpc_services":[{"TargetSpecifier":{"EnvoyGrpc":{"cluster_name":"sds-grpc"}}}],"set_node_on_first_message_only":true}},"resource_api_version":2}}],"ValidationContextType":{"CombinedValidationContext":{"default_validation_context":{}}},"alpn_protocols":["h2"]},"sni":"bar"}`, - }, - { - desc: "tls-istio-mutual-no-certs", - tls: &networkingAPI.ClientTLSSettings{ - Mode: networkingAPI.ClientTLSSettings_ISTIO_MUTUAL, - }, - sni: "i-should-be-sni", - meta: &model.BootstrapNodeMetadata{}, - expectTLSCtx: `{"common_tls_context":{"tls_certificate_sds_secret_configs":[{"name":"default","sds_config":{"ConfigSourceSpecifier":{"ApiConfigSource":{"api_type":2,"transport_api_version":2,"grpc_services":[{"TargetSpecifier":{"EnvoyGrpc":{"cluster_name":"sds-grpc"}}}],"set_node_on_first_message_only":true}},"initial_fetch_timeout":{},"resource_api_version":2}}],"ValidationContextType":{"CombinedValidationContext":{"default_validation_context":{},"validation_context_sds_secret_config":{"name":"ROOTCA","sds_config":{"ConfigSourceSpecifier":{"ApiConfigSource":{"api_type":2,"transport_api_version":2,"grpc_services":[{"TargetSpecifier":{"EnvoyGrpc":{"cluster_name":"sds-grpc"}}}],"set_node_on_first_message_only":true}},"initial_fetch_timeout":{},"resource_api_version":2}}}},"alpn_protocols":["istio","h2"]},"sni":"i-should-be-sni"}`, - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if got := convertToJSON(tlsContextConvert(tt.tls, tt.sni, tt.meta)); !reflect.DeepEqual(tt.expectTLSCtx, got) { - t.Errorf("%s: expected TLS ctx \n%v got \n%v", tt.desc, tt.expectTLSCtx, got) - } - }) - } -} diff --git a/pkg/bootstrap/option/instance.go b/pkg/bootstrap/option/instance.go deleted file mode 100644 index 8028a08e4..000000000 --- a/pkg/bootstrap/option/instance.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package option - -import ( - "reflect" -) - -import ( - "google.golang.org/protobuf/types/known/durationpb" - networkingAPI "istio.io/api/networking/v1alpha3" -) - -// NewTemplateParams creates a new golang template parameter map from the given list of options. -func NewTemplateParams(is ...Instance) (map[string]interface{}, error) { - params := make(map[string]interface{}) - - for _, i := range is { - if err := i.apply(params); err != nil { - return nil, err - } - } - - return params, nil -} - -// Name unique name for an option. -type Name string - -func (n Name) String() string { - return string(n) -} - -// Instance of a bootstrap option. -type Instance interface { - Name() Name - - // apply this option to the given template parameter map. - apply(map[string]interface{}) error -} - -var _ Instance = &instance{} - -type ( - convertFunc func(*instance) (interface{}, error) - applyFunc func(map[string]interface{}, *instance) error -) - -type instance struct { - name Name - convertFn convertFunc - applyFn applyFunc -} - -func (i *instance) Name() Name { - return i.name -} - -func (i *instance) withConvert(fn convertFunc) *instance { - out := *i - out.convertFn = fn - return &out -} - -func (i *instance) apply(params map[string]interface{}) error { - return i.applyFn(params, i) -} - -func newOption(name Name, value interface{}) *instance { - return &instance{ - name: name, - convertFn: func(i *instance) (interface{}, error) { - return value, nil - }, - applyFn: func(params map[string]interface{}, o *instance) error { - convertedValue, err := o.convertFn(o) - if err != nil { - return err - } - params[o.name.String()] = convertedValue - return nil - }, - } -} - -// skipOption creates a placeholder option that will not be applied to the output template map. -func skipOption(name Name) *instance { - return &instance{ - name: name, - convertFn: func(*instance) (interface{}, error) { - return nil, nil - }, - applyFn: func(map[string]interface{}, *instance) error { - // Don't apply the option. - return nil - }, - } -} - -func newStringArrayOptionOrSkipIfEmpty(name Name, value []string) *instance { - if len(value) == 0 { - return skipOption(name) - } - return newOption(name, value) -} - -func newOptionOrSkipIfZero(name Name, value interface{}) *instance { - v := reflect.ValueOf(value) - if v.IsZero() { - return skipOption(name) - } - return newOption(name, value) -} - -func newDurationOption(name Name, value *durationpb.Duration) *instance { - return newOptionOrSkipIfZero(name, value).withConvert(durationConverter(value)) -} - -func newTCPKeepaliveOption(name Name, value *networkingAPI.ConnectionPoolSettings_TCPSettings_TcpKeepalive) *instance { - return newOptionOrSkipIfZero(name, value).withConvert(keepaliveConverter(value)) -} diff --git a/pkg/bootstrap/option/instances.go b/pkg/bootstrap/option/instances.go deleted file mode 100644 index b59870bd8..000000000 --- a/pkg/bootstrap/option/instances.go +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package option - -import ( - "strings" -) - -import ( - "google.golang.org/protobuf/types/known/durationpb" - meshAPI "istio.io/api/mesh/v1alpha1" - networkingAPI "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" -) - -type ( - LocalhostValue string - WildcardValue string - DNSLookupFamilyValue string -) - -const ( - LocalhostIPv4 LocalhostValue = "127.0.0.1" - LocalhostIPv6 LocalhostValue = "::1" - WildcardIPv4 WildcardValue = "0.0.0.0" - WildcardIPv6 WildcardValue = "::" - DNSLookupFamilyIPv4 DNSLookupFamilyValue = "V4_ONLY" - DNSLookupFamilyIPv6 DNSLookupFamilyValue = "AUTO" -) - -func ProxyConfig(value *model.NodeMetaProxyConfig) Instance { - return newOption("config", value) -} - -func PilotSubjectAltName(value []string) Instance { - return newOption("pilot_SAN", value).withConvert(sanConverter(value)) -} - -func ConnectTimeout(value *durationpb.Duration) Instance { - return newDurationOption("connect_timeout", value) -} - -func Cluster(value string) Instance { - return newOption("cluster", value) -} - -func NodeID(value string) Instance { - return newOption("nodeID", value) -} - -func NodeType(value string) Instance { - ntype := strings.Split(value, "~")[0] - return newOption("nodeType", ntype) -} - -func XdsType(value string) Instance { - return newOption("xds_type", value) -} - -func Region(value string) Instance { - return newOptionOrSkipIfZero("region", value) -} - -func Zone(value string) Instance { - return newOptionOrSkipIfZero("zone", value) -} - -func SubZone(value string) Instance { - return newOptionOrSkipIfZero("sub_zone", value) -} - -func NodeMetadata(meta *model.BootstrapNodeMetadata, rawMeta map[string]interface{}) Instance { - return newOptionOrSkipIfZero("meta_json_str", meta).withConvert(nodeMetadataConverter(meta, rawMeta)) -} - -func RuntimeFlags(flags map[string]string) Instance { - return newOptionOrSkipIfZero("runtime_flags", flags).withConvert(jsonConverter(flags)) -} - -func DiscoveryAddress(value string) Instance { - return newOption("discovery_address", value) -} - -func XDSRootCert(value string) Instance { - return newOption("xds_root_cert", value) -} - -func Localhost(value LocalhostValue) Instance { - return newOption("localhost", value) -} - -func Wildcard(value WildcardValue) Instance { - return newOption("wildcard", value) -} - -func DNSLookupFamily(value DNSLookupFamilyValue) Instance { - return newOption("dns_lookup_family", value) -} - -func OutlierLogPath(value string) Instance { - return newOptionOrSkipIfZero("outlier_log_path", value) -} - -func LightstepAddress(value string) Instance { - return newOptionOrSkipIfZero("lightstep", value).withConvert(addressConverter(value)) -} - -func LightstepToken(value string) Instance { - return newOption("lightstepToken", value) -} - -func OpenCensusAgentAddress(value string) Instance { - return newOptionOrSkipIfZero("openCensusAgent", value) -} - -func OpenCensusAgentContexts(value []meshAPI.Tracing_OpenCensusAgent_TraceContext) Instance { - return newOption("openCensusAgentContexts", value). - withConvert(openCensusAgentContextConverter(value)) -} - -func StackDriverEnabled(value bool) Instance { - return newOption("stackdriver", value) -} - -func StackDriverProjectID(value string) Instance { - return newOption("stackdriverProjectID", value) -} - -func StackDriverDebug(value bool) Instance { - return newOption("stackdriverDebug", value) -} - -func StackDriverMaxAnnotations(value int64) Instance { - return newOption("stackdriverMaxAnnotations", value) -} - -func StackDriverMaxAttributes(value int64) Instance { - return newOption("stackdriverMaxAttributes", value) -} - -func StackDriverMaxEvents(value int64) Instance { - return newOption("stackdriverMaxEvents", value) -} - -func PilotGRPCAddress(value string) Instance { - return newOptionOrSkipIfZero("pilot_grpc_address", value).withConvert(addressConverter(value)) -} - -func ZipkinAddress(value string) Instance { - return newOptionOrSkipIfZero("zipkin", value).withConvert(addressConverter(value)) -} - -func DataDogAddress(value string) Instance { - return newOptionOrSkipIfZero("datadog", value).withConvert(addressConverter(value)) -} - -func StatsdAddress(value string) Instance { - return newOptionOrSkipIfZero("statsd", value).withConvert(addressConverter(value)) -} - -func TracingTLS(value *networkingAPI.ClientTLSSettings, metadata *model.BootstrapNodeMetadata, isH2 bool) Instance { - return newOptionOrSkipIfZero("tracing_tls", value). - withConvert(transportSocketConverter(value, "tracer", metadata, isH2)) -} - -func EnvoyMetricsServiceAddress(value string) Instance { - return newOptionOrSkipIfZero("envoy_metrics_service_address", value).withConvert(addressConverter(value)) -} - -func EnvoyMetricsServiceTLS(value *networkingAPI.ClientTLSSettings, metadata *model.BootstrapNodeMetadata) Instance { - return newOptionOrSkipIfZero("envoy_metrics_service_tls", value). - withConvert(transportSocketConverter(value, "envoy_metrics_service", metadata, true)) -} - -func EnvoyMetricsServiceTCPKeepalive(value *networkingAPI.ConnectionPoolSettings_TCPSettings_TcpKeepalive) Instance { - return newTCPKeepaliveOption("envoy_metrics_service_tcp_keepalive", value) -} - -func EnvoyAccessLogServiceAddress(value string) Instance { - return newOptionOrSkipIfZero("envoy_accesslog_service_address", value).withConvert(addressConverter(value)) -} - -func EnvoyAccessLogServiceTLS(value *networkingAPI.ClientTLSSettings, metadata *model.BootstrapNodeMetadata) Instance { - return newOptionOrSkipIfZero("envoy_accesslog_service_tls", value). - withConvert(transportSocketConverter(value, "envoy_accesslog_service", metadata, true)) -} - -func EnvoyAccessLogServiceTCPKeepalive(value *networkingAPI.ConnectionPoolSettings_TCPSettings_TcpKeepalive) Instance { - return newTCPKeepaliveOption("envoy_accesslog_service_tcp_keepalive", value) -} - -func EnvoyExtraStatTags(value []string) Instance { - return newStringArrayOptionOrSkipIfEmpty("extraStatTags", value) -} - -func EnvoyStatsMatcherInclusionPrefix(value []string) Instance { - return newStringArrayOptionOrSkipIfEmpty("inclusionPrefix", value) -} - -func EnvoyStatsMatcherInclusionSuffix(value []string) Instance { - return newStringArrayOptionOrSkipIfEmpty("inclusionSuffix", value) -} - -func EnvoyStatsMatcherInclusionRegexp(value []string) Instance { - return newStringArrayOptionOrSkipIfEmpty("inclusionRegexps", value) -} - -func EnvoyStatusPort(value int) Instance { - return newOption("envoy_status_port", value) -} - -func EnvoyPrometheusPort(value int) Instance { - return newOption("envoy_prometheus_port", value) -} - -func STSPort(value int) Instance { - return newOption("sts_port", value) -} - -func GCPProjectID(value string) Instance { - return newOption("gcp_project_id", value) -} - -func GCPProjectNumber(value string) Instance { - return newOption("gcp_project_number", value) -} - -func Metadata(meta *model.BootstrapNodeMetadata) Instance { - return newOption("metadata", meta) -} - -func STSEnabled(value bool) Instance { - return newOption("sts", value) -} - -func ProvCert(value string) Instance { - return newOption("provisioned_cert", value) -} - -func DiscoveryHost(value string) Instance { - return newOption("discovery_host", value) -} diff --git a/pkg/bootstrap/option/instances_test.go b/pkg/bootstrap/option/instances_test.go deleted file mode 100644 index 818d6fdc9..000000000 --- a/pkg/bootstrap/option/instances_test.go +++ /dev/null @@ -1,694 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package option_test - -import ( - "testing" - "time" -) - -import ( - . "github.com/onsi/gomega" - "google.golang.org/protobuf/types/known/durationpb" - meshAPI "istio.io/api/mesh/v1alpha1" - networkingAPI "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/bootstrap/option" -) - -// nolint: lll -func TestOptions(t *testing.T) { - cases := []struct { - testName string - key string - option option.Instance - expectError bool - expected interface{} - }{ - { - testName: "proxy config", - key: "config", - option: option.ProxyConfig(&model.NodeMetaProxyConfig{DiscoveryAddress: "fake"}), - expected: &model.NodeMetaProxyConfig{DiscoveryAddress: "fake"}, - }, - { - testName: "pilotSAN", - key: "pilot_SAN", - option: option.PilotSubjectAltName([]string{"fake"}), - expected: `[{"exact":"fake"}]`, - }, - { - testName: "pilotSAN multi", - key: "pilot_SAN", - option: option.PilotSubjectAltName([]string{"fake", "other"}), - expected: `[{"exact":"fake"},{"exact":"other"}]`, - }, - { - testName: "nil connect timeout", - key: "connect_timeout", - option: option.ConnectTimeout(nil), - expected: nil, - }, - { - testName: "connect timeout", - key: "connect_timeout", - option: option.ConnectTimeout(durationpb.New(time.Second)), - expected: "1s", - }, - { - testName: "cluster", - key: "cluster", - option: option.Cluster("fake"), - expected: "fake", - }, - { - testName: "nodeID", - key: "nodeID", - option: option.NodeID("fake"), - expected: "fake", - }, - { - testName: "region", - key: "region", - option: option.Region("fake"), - expected: "fake", - }, - { - testName: "zone", - key: "zone", - option: option.Zone("fake"), - expected: "fake", - }, - { - testName: "sub zone", - key: "sub_zone", - option: option.SubZone("fake"), - expected: "fake", - }, - { - testName: "node metadata nil", - key: "meta_json_str", - option: option.NodeMetadata(nil, nil), - expected: nil, - }, - { - testName: "node metadata", - key: "meta_json_str", - option: option.NodeMetadata(&model.BootstrapNodeMetadata{ - NodeMetadata: model.NodeMetadata{ - IstioVersion: "fake", - }, - }, map[string]interface{}{ - "key": "value", - }), - expected: "{\"ISTIO_VERSION\":\"fake\",\"key\":\"value\"}", - }, - { - testName: "discovery address", - key: "discovery_address", - option: option.DiscoveryAddress("fake"), - expected: "fake", - }, - { - testName: "localhost v4", - key: "localhost", - option: option.Localhost(option.LocalhostIPv4), - expected: option.LocalhostValue("127.0.0.1"), - }, - { - testName: "localhost v6", - key: "localhost", - option: option.Localhost(option.LocalhostIPv6), - expected: option.LocalhostValue("::1"), - }, - { - testName: "wildcard v4", - key: "wildcard", - option: option.Wildcard(option.WildcardIPv4), - expected: option.WildcardValue("0.0.0.0"), - }, - { - testName: "wildcard v6", - key: "wildcard", - option: option.Wildcard(option.WildcardIPv6), - expected: option.WildcardValue("::"), - }, - { - testName: "dns lookup family v4", - key: "dns_lookup_family", - option: option.DNSLookupFamily(option.DNSLookupFamilyIPv4), - expected: option.DNSLookupFamilyValue("V4_ONLY"), - }, - { - testName: "dns lookup family v6", - key: "dns_lookup_family", - option: option.DNSLookupFamily(option.DNSLookupFamilyIPv6), - expected: option.DNSLookupFamilyValue("AUTO"), - }, - { - testName: "lightstep address empty", - key: "lightstep", - option: option.LightstepAddress(""), - expected: nil, - }, - { - testName: "lightstep address ipv4", - key: "lightstep", - option: option.LightstepAddress("127.0.0.1:80"), - expected: "{\"address\": \"127.0.0.1\", \"port_value\": 80}", - }, - { - testName: "lightstep address ipv6", - key: "lightstep", - option: option.LightstepAddress("[2001:db8::100]:80"), - expected: "{\"address\": \"2001:db8::100\", \"port_value\": 80}", - }, - { - testName: "lightstep address ipv6 missing brackets", - key: "lightstep", - option: option.LightstepAddress("2001:db8::100:80"), - expectError: true, - }, - { - testName: "lightstep address host port", - key: "lightstep", - option: option.LightstepAddress("fake:80"), - expected: "{\"address\": \"fake\", \"port_value\": 80}", - }, - { - testName: "lightstep address no port", - key: "lightstep", - option: option.LightstepAddress("fake:"), - expected: "{\"address\": \"fake\", \"port_value\": }", - }, - { - testName: "lightstep address missing port", - key: "lightstep", - option: option.LightstepAddress("127.0.0.1"), - expectError: true, - }, - { - testName: "lightstep token", - key: "lightstepToken", - option: option.LightstepToken("fake"), - expected: "fake", - }, - { - testName: "openCensusAgent address", - key: "openCensusAgent", - option: option.OpenCensusAgentAddress("fake-ocagent"), - expected: "fake-ocagent", - }, - { - testName: "openCensusAgent empty context", - key: "openCensusAgentContexts", - option: option.OpenCensusAgentContexts([]meshAPI.Tracing_OpenCensusAgent_TraceContext{}), - expected: `["TRACE_CONTEXT","GRPC_TRACE_BIN","CLOUD_TRACE_CONTEXT","B3"]`, - }, - { - testName: "openCensusAgent order context", - key: "openCensusAgentContexts", - option: option.OpenCensusAgentContexts([]meshAPI.Tracing_OpenCensusAgent_TraceContext{ - meshAPI.Tracing_OpenCensusAgent_CLOUD_TRACE_CONTEXT, - meshAPI.Tracing_OpenCensusAgent_B3, - meshAPI.Tracing_OpenCensusAgent_GRPC_BIN, - meshAPI.Tracing_OpenCensusAgent_W3C_TRACE_CONTEXT, - }), - expected: `["CLOUD_TRACE_CONTEXT","B3","GRPC_TRACE_BIN","TRACE_CONTEXT"]`, - }, - { - testName: "openCensusAgent one context", - key: "openCensusAgentContexts", - option: option.OpenCensusAgentContexts([]meshAPI.Tracing_OpenCensusAgent_TraceContext{ - meshAPI.Tracing_OpenCensusAgent_B3, - }), - expected: `["B3"]`, - }, - { - testName: "stackdriver enabled", - key: "stackdriver", - option: option.StackDriverEnabled(true), - expected: true, - }, - { - testName: "stackdriver project ID", - key: "stackdriverProjectID", - option: option.StackDriverProjectID("fake"), - expected: "fake", - }, - { - testName: "stackdriver debug", - key: "stackdriverDebug", - option: option.StackDriverDebug(true), - expected: true, - }, - { - testName: "stackdriver max annotations", - key: "stackdriverMaxAnnotations", - option: option.StackDriverMaxAnnotations(100), - expected: int64(100), - }, - { - testName: "stackdriver max attributes", - key: "stackdriverMaxAttributes", - option: option.StackDriverMaxAttributes(100), - expected: int64(100), - }, - { - testName: "stackdriver max events", - key: "stackdriverMaxEvents", - option: option.StackDriverMaxEvents(100), - expected: int64(100), - }, - { - testName: "pilot grpc address empty", - key: "pilot_grpc_address", - option: option.PilotGRPCAddress(""), - expected: nil, - }, - { - testName: "pilot grpc address ipv4", - key: "pilot_grpc_address", - option: option.PilotGRPCAddress("127.0.0.1:80"), - expected: "{\"address\": \"127.0.0.1\", \"port_value\": 80}", - }, - { - testName: "pilot grpc address ipv6", - key: "pilot_grpc_address", - option: option.PilotGRPCAddress("[2001:db8::100]:80"), - expected: "{\"address\": \"2001:db8::100\", \"port_value\": 80}", - }, - { - testName: "pilot grpc address ipv6 missing brackets", - key: "pilot_grpc_address", - option: option.PilotGRPCAddress("2001:db8::100:80"), - expectError: true, - }, - { - testName: "pilot grpc address host port", - key: "pilot_grpc_address", - option: option.PilotGRPCAddress("fake:80"), - expected: "{\"address\": \"fake\", \"port_value\": 80}", - }, - { - testName: "pilot grpc address no port", - key: "pilot_grpc_address", - option: option.PilotGRPCAddress("fake:"), - expected: "{\"address\": \"fake\", \"port_value\": }", - }, - { - testName: "pilot grpc address missing port", - key: "pilot_grpc_address", - option: option.PilotGRPCAddress("127.0.0.1"), - expectError: true, - }, - { - testName: "zipkin address empty", - key: "zipkin", - option: option.ZipkinAddress(""), - expected: nil, - }, - { - testName: "zipkin address ipv4", - key: "zipkin", - option: option.ZipkinAddress("127.0.0.1:80"), - expected: "{\"address\": \"127.0.0.1\", \"port_value\": 80}", - }, - { - testName: "zipkin address ipv6", - key: "zipkin", - option: option.ZipkinAddress("[2001:db8::100]:80"), - expected: "{\"address\": \"2001:db8::100\", \"port_value\": 80}", - }, - { - testName: "zipkin address ipv6 missing brackets", - key: "zipkin", - option: option.ZipkinAddress("2001:db8::100:80"), - expectError: true, - }, - { - testName: "zipkin address host port", - key: "zipkin", - option: option.ZipkinAddress("fake:80"), - expected: "{\"address\": \"fake\", \"port_value\": 80}", - }, - { - testName: "zipkin address no port", - key: "zipkin", - option: option.ZipkinAddress("fake:"), - expected: "{\"address\": \"fake\", \"port_value\": }", - }, - { - testName: "zipkin address missing port", - key: "zipkin", - option: option.ZipkinAddress("127.0.0.1"), - expectError: true, - }, - { - testName: "datadog address empty", - key: "datadog", - option: option.DataDogAddress(""), - expected: nil, - }, - { - testName: "datadog address ipv4", - key: "datadog", - option: option.DataDogAddress("127.0.0.1:80"), - expected: "{\"address\": \"127.0.0.1\", \"port_value\": 80}", - }, - { - testName: "datadog address ipv6", - key: "datadog", - option: option.DataDogAddress("[2001:db8::100]:80"), - expected: "{\"address\": \"2001:db8::100\", \"port_value\": 80}", - }, - { - testName: "datadog address ipv6 missing brackets", - key: "datadog", - option: option.DataDogAddress("2001:db8::100:80"), - expectError: true, - }, - { - testName: "datadog address host port", - key: "datadog", - option: option.DataDogAddress("fake:80"), - expected: "{\"address\": \"fake\", \"port_value\": 80}", - }, - { - testName: "datadog address no port", - key: "datadog", - option: option.DataDogAddress("fake:"), - expected: "{\"address\": \"fake\", \"port_value\": }", - }, - { - testName: "datadog address missing port", - key: "datadog", - option: option.DataDogAddress("127.0.0.1"), - expectError: true, - }, - { - testName: "statsd address empty", - key: "statsd", - option: option.StatsdAddress(""), - expected: nil, - }, - { - testName: "statsd address ipv4", - key: "statsd", - option: option.StatsdAddress("127.0.0.1:80"), - expected: "{\"address\": \"127.0.0.1\", \"port_value\": 80}", - }, - { - testName: "statsd address ipv6", - key: "statsd", - option: option.StatsdAddress("[2001:db8::100]:80"), - expected: "{\"address\": \"2001:db8::100\", \"port_value\": 80}", - }, - { - testName: "statsd address ipv6 missing brackets", - key: "statsd", - option: option.StatsdAddress("2001:db8::100:80"), - expectError: true, - }, - { - testName: "statsd address host port", - key: "statsd", - option: option.StatsdAddress("fake:80"), - expected: "{\"address\": \"fake\", \"port_value\": 80}", - }, - { - testName: "statsd address no port", - key: "statsd", - option: option.StatsdAddress("fake:"), - expected: "{\"address\": \"fake\", \"port_value\": }", - }, - { - testName: "statsd address missing port", - key: "statsd", - option: option.StatsdAddress("127.0.0.1"), - expectError: true, - }, - { - testName: "envoy metrics address empty", - key: "envoy_metrics_service_address", - option: option.EnvoyMetricsServiceAddress(""), - expected: nil, - }, - { - testName: "envoy metrics address ipv4", - key: "envoy_metrics_service_address", - option: option.EnvoyMetricsServiceAddress("127.0.0.1:80"), - expected: "{\"address\": \"127.0.0.1\", \"port_value\": 80}", - }, - { - testName: "envoy metrics address ipv6", - key: "envoy_metrics_service_address", - option: option.EnvoyMetricsServiceAddress("[2001:db8::100]:80"), - expected: "{\"address\": \"2001:db8::100\", \"port_value\": 80}", - }, - { - testName: "envoy metrics address ipv6 missing brackets", - key: "envoy_metrics_service_address", - option: option.EnvoyMetricsServiceAddress("2001:db8::100:80"), - expectError: true, - }, - { - testName: "envoy metrics address host port", - key: "envoy_metrics_service_address", - option: option.EnvoyMetricsServiceAddress("fake:80"), - expected: "{\"address\": \"fake\", \"port_value\": 80}", - }, - { - testName: "envoy metrics address no port", - key: "envoy_metrics_service_address", - option: option.EnvoyMetricsServiceAddress("fake:"), - expected: "{\"address\": \"fake\", \"port_value\": }", - }, - { - testName: "envoy metrics address missing port", - key: "envoy_metrics_service_address", - option: option.EnvoyMetricsServiceAddress("127.0.0.1"), - expectError: true, - }, - { - testName: "envoy metrics tls nil", - key: "envoy_metrics_service_tls", - option: option.EnvoyMetricsServiceTLS(nil, &model.BootstrapNodeMetadata{}), - expected: nil, - }, - { - testName: "envoy metrics tls", - key: "envoy_metrics_service_tls", - option: option.EnvoyMetricsServiceTLS(&networkingAPI.ClientTLSSettings{ - Mode: networkingAPI.ClientTLSSettings_ISTIO_MUTUAL, - }, &model.BootstrapNodeMetadata{}), - expected: `{"name":"envoy.transport_sockets.tls","typed_config":{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext","common_tls_context":{"alpn_protocols":["istio","h2"],"combined_validation_context":{"default_validation_context":{},"validation_context_sds_secret_config":{"name":"ROOTCA","sds_config":{"api_config_source":{"api_type":"GRPC","grpc_services":[{"envoy_grpc":{"cluster_name":"sds-grpc"}}],"set_node_on_first_message_only":true,"transport_api_version":"V3"},"initial_fetch_timeout":"0s","resource_api_version":"V3"}}},"tls_certificate_sds_secret_configs":[{"name":"default","sds_config":{"api_config_source":{"api_type":"GRPC","grpc_services":[{"envoy_grpc":{"cluster_name":"sds-grpc"}}],"set_node_on_first_message_only":true,"transport_api_version":"V3"},"initial_fetch_timeout":"0s","resource_api_version":"V3"}}]},"sni":"envoy_metrics_service"}}`, - }, - { - testName: "envoy metrics keepalive nil", - key: "envoy_metrics_service_tcp_keepalive", - option: option.EnvoyMetricsServiceTCPKeepalive(nil), - expected: nil, - }, - { - testName: "envoy metrics keepalive", - key: "envoy_metrics_service_tcp_keepalive", - option: option.EnvoyMetricsServiceTCPKeepalive(&networkingAPI.ConnectionPoolSettings_TCPSettings_TcpKeepalive{ - Time: durationpb.New(time.Second), - }), - expected: "{\"tcp_keepalive\":{\"keepalive_time\":{\"value\":1}}}", - }, - { - testName: "envoy accesslog address empty", - key: "envoy_accesslog_service_address", - option: option.EnvoyAccessLogServiceAddress(""), - expected: nil, - }, - { - testName: "envoy accesslog address ipv4", - key: "envoy_accesslog_service_address", - option: option.EnvoyAccessLogServiceAddress("127.0.0.1:80"), - expected: "{\"address\": \"127.0.0.1\", \"port_value\": 80}", - }, - { - testName: "envoy accesslog address ipv6", - key: "envoy_accesslog_service_address", - option: option.EnvoyAccessLogServiceAddress("[2001:db8::100]:80"), - expected: "{\"address\": \"2001:db8::100\", \"port_value\": 80}", - }, - { - testName: "envoy accesslog address ipv6 missing brackets", - key: "envoy_accesslog_service_address", - option: option.EnvoyAccessLogServiceAddress("2001:db8::100:80"), - expectError: true, - }, - { - testName: "envoy accesslog address host port", - key: "envoy_accesslog_service_address", - option: option.EnvoyAccessLogServiceAddress("fake:80"), - expected: "{\"address\": \"fake\", \"port_value\": 80}", - }, - { - testName: "envoy accesslog address no port", - key: "envoy_accesslog_service_address", - option: option.EnvoyAccessLogServiceAddress("fake:"), - expected: "{\"address\": \"fake\", \"port_value\": }", - }, - { - testName: "envoy accesslog address missing port", - key: "envoy_accesslog_service_address", - option: option.EnvoyAccessLogServiceAddress("127.0.0.1"), - expectError: true, - }, - { - testName: "envoy accesslog tls nil", - key: "envoy_accesslog_service_tls", - option: option.EnvoyAccessLogServiceTLS(nil, &model.BootstrapNodeMetadata{}), - expected: nil, - }, - { - testName: "envoy access log tls", - key: "envoy_accesslog_service_tls", - option: option.EnvoyAccessLogServiceTLS(&networkingAPI.ClientTLSSettings{ - Mode: networkingAPI.ClientTLSSettings_ISTIO_MUTUAL, - }, &model.BootstrapNodeMetadata{}), - expected: `{"name":"envoy.transport_sockets.tls","typed_config":{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext","common_tls_context":{"alpn_protocols":["istio","h2"],"combined_validation_context":{"default_validation_context":{},"validation_context_sds_secret_config":{"name":"ROOTCA","sds_config":{"api_config_source":{"api_type":"GRPC","grpc_services":[{"envoy_grpc":{"cluster_name":"sds-grpc"}}],"set_node_on_first_message_only":true,"transport_api_version":"V3"},"initial_fetch_timeout":"0s","resource_api_version":"V3"}}},"tls_certificate_sds_secret_configs":[{"name":"default","sds_config":{"api_config_source":{"api_type":"GRPC","grpc_services":[{"envoy_grpc":{"cluster_name":"sds-grpc"}}],"set_node_on_first_message_only":true,"transport_api_version":"V3"},"initial_fetch_timeout":"0s","resource_api_version":"V3"}}]},"sni":"envoy_accesslog_service"}}`, - }, - { - testName: "envoy access log keepalive nil", - key: "envoy_accesslog_service_tcp_keepalive", - option: option.EnvoyAccessLogServiceTCPKeepalive(nil), - expected: nil, - }, - { - testName: "envoy access log keepalive", - key: "envoy_accesslog_service_tcp_keepalive", - option: option.EnvoyAccessLogServiceTCPKeepalive(&networkingAPI.ConnectionPoolSettings_TCPSettings_TcpKeepalive{ - Time: durationpb.New(time.Second), - }), - expected: "{\"tcp_keepalive\":{\"keepalive_time\":{\"value\":1}}}", - }, - { - testName: "envoy stats matcher inclusion prefix nil", - key: "inclusionPrefix", - option: option.EnvoyStatsMatcherInclusionPrefix(nil), - expected: nil, - }, - { - testName: "envoy stats matcher inclusion prefix empty", - key: "inclusionPrefix", - option: option.EnvoyStatsMatcherInclusionPrefix(make([]string, 0)), - expected: nil, - }, - { - testName: "envoy stats matcher inclusion prefix", - key: "inclusionPrefix", - option: option.EnvoyStatsMatcherInclusionPrefix([]string{"fake"}), - expected: []string{"fake"}, - }, - { - testName: "envoy stats matcher inclusion suffix nil", - key: "inclusionSuffix", - option: option.EnvoyStatsMatcherInclusionSuffix(nil), - expected: nil, - }, - { - testName: "envoy stats matcher inclusion suffix empty", - key: "inclusionSuffix", - option: option.EnvoyStatsMatcherInclusionSuffix(make([]string, 0)), - expected: nil, - }, - { - testName: "envoy stats matcher inclusion suffix", - key: "inclusionSuffix", - option: option.EnvoyStatsMatcherInclusionSuffix([]string{"fake"}), - expected: []string{"fake"}, - }, - { - testName: "envoy stats matcher inclusion regexp nil", - key: "inclusionRegexps", - option: option.EnvoyStatsMatcherInclusionRegexp(nil), - expected: nil, - }, - { - testName: "envoy stats matcher inclusion regexp empty", - key: "inclusionRegexps", - option: option.EnvoyStatsMatcherInclusionRegexp(make([]string, 0)), - expected: nil, - }, - { - testName: "envoy stats matcher inclusion regexp", - key: "inclusionRegexps", - option: option.EnvoyStatsMatcherInclusionRegexp([]string{"fake"}), - expected: []string{"fake"}, - }, - { - testName: "sts enabled", - key: "sts", - option: option.STSEnabled(true), - expected: true, - }, - { - testName: "sts port", - key: "sts_port", - option: option.STSPort(5555), - expected: 5555, - }, - { - testName: "project id", - key: "gcp_project_id", - option: option.GCPProjectID("project"), - expected: "project", - }, - { - testName: "tracing tls nil", - key: "tracing_tls", - option: option.TracingTLS(nil, &model.BootstrapNodeMetadata{}, false), - expected: nil, - }, - { - testName: "tracing tls", - key: "tracing_tls", - option: option.TracingTLS(&networkingAPI.ClientTLSSettings{ - Mode: networkingAPI.ClientTLSSettings_SIMPLE, - CaCertificates: "/etc/tracing/ca.pem", - }, &model.BootstrapNodeMetadata{}, false), - expected: `{"name":"envoy.transport_sockets.tls","typed_config":{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext","common_tls_context":{"combined_validation_context":{"default_validation_context":{},"validation_context_sds_secret_config":{"name":"file-root:/etc/tracing/ca.pem","sds_config":{"api_config_source":{"api_type":"GRPC","grpc_services":[{"envoy_grpc":{"cluster_name":"sds-grpc"}}],"set_node_on_first_message_only":true,"transport_api_version":"V3"},"resource_api_version":"V3"}}}}}}`, - }, - } - - for _, c := range cases { - t.Run(c.testName, func(t *testing.T) { - g := NewWithT(t) - - params, err := option.NewTemplateParams(c.option) - if c.expectError { - g.Expect(err).ToNot(BeNil()) - } else { - g.Expect(err).To(BeNil()) - actual, ok := params[c.key] - if c.expected == nil { - g.Expect(ok).To(BeFalse()) - } else { - g.Expect(ok).To(BeTrue()) - g.Expect(actual).To(Equal(c.expected)) - } - } - }) - } -} diff --git a/pkg/bootstrap/platform/aws.go b/pkg/bootstrap/platform/aws.go deleted file mode 100644 index efcf914ef..000000000 --- a/pkg/bootstrap/platform/aws.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package platform - -import ( - "strings" - "time" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/http" -) - -const ( - AWSRegion = "aws_region" - AWSAvailabilityZone = "aws_availability_zone" - AWSInstanceID = "aws_instance_id" -) - -var ( - awsMetadataIPv4URL = "http://169.254.169.254/latest/meta-data" - awsMetadataIPv6URL = "http://[fd00:ec2::254]/latest/meta-data" -) - -// Approach derived from the following: -// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/identify_ec2_instances.html -// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html - -// IsAWS returns whether or not the platform for bootstrapping is Amazon Web Services. -func IsAWS(ipv6 bool) bool { - info, err := getAWSInfo("iam/info", ipv6) - return err == nil && strings.Contains(info, "arn:aws:iam") -} - -type awsEnv struct { - region string - availabilityzone string - instanceID string -} - -// NewAWS returns a platform environment customized for AWS. -// Metadata returned by the AWS Environment is taken link-local address running on each node. -func NewAWS(ipv6 bool) Environment { - return &awsEnv{ - region: getRegion(ipv6), - availabilityzone: getAvailabilityZone(ipv6), - instanceID: getInstanceID(ipv6), - } -} - -func (a *awsEnv) Metadata() map[string]string { - md := map[string]string{} - if len(a.availabilityzone) > 0 { - md[AWSAvailabilityZone] = a.availabilityzone - } - if len(a.region) > 0 { - md[AWSRegion] = a.region - } - if len(a.instanceID) > 0 { - md[AWSInstanceID] = a.instanceID - } - return md -} - -func (a *awsEnv) Locality() *core.Locality { - return &core.Locality{ - Zone: a.availabilityzone, - Region: a.region, - } -} - -func (a *awsEnv) Labels() map[string]string { - return map[string]string{} -} - -func (a *awsEnv) IsKubernetes() bool { - return true -} - -func getAWSInfo(path string, ipv6 bool) (string, error) { - url := awsMetadataIPv4URL + "/" + path - if ipv6 { - url = awsMetadataIPv6URL + "/" + path - } - - resp, err := http.DoHTTPGetWithTimeout(url, time.Millisecond*100) - if err != nil { - log.Debugf("error in getting aws info for %s : %v", path, err) - return "", err - } - return resp.String(), nil -} - -// getRegion returns the Region that the instance is running in. -func getRegion(ipv6 bool) string { - region, _ := getAWSInfo("placement/region", ipv6) - return region -} - -// getAvailabilityZone returns the AvailabilityZone that the instance is running in. -func getAvailabilityZone(ipv6 bool) string { - az, _ := getAWSInfo("placement/availability-zone", ipv6) - return az -} - -func getInstanceID(ipv6 bool) string { - instance, _ := getAWSInfo("instance-id", ipv6) - return instance -} diff --git a/pkg/bootstrap/platform/aws_test.go b/pkg/bootstrap/platform/aws_test.go deleted file mode 100644 index 8d99395b9..000000000 --- a/pkg/bootstrap/platform/aws_test.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package platform - -import ( - "net/http" - "net/http/httptest" - "net/url" - "reflect" - "testing" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" -) - -type handlerFunc func(http.ResponseWriter, *http.Request) - -func TestAWSLocality(t *testing.T) { - cases := []struct { - name string - handlers map[string]handlerFunc - want *core.Locality - }{ - { - "error", - map[string]handlerFunc{"/placement/region": errorHandler, "/placement/availability-zone": errorHandler}, - &core.Locality{}, - }, - { - "locality", - map[string]handlerFunc{"/placement/region": regionHandler, "/placement/availability-zone": zoneHandler}, - &core.Locality{Region: "us-west-2", Zone: "us-west-2b"}, - }, - } - - for _, v := range cases { - t.Run(v.name, func(tt *testing.T) { - server, url := setupHTTPServer(v.handlers) - defer server.Close() - awsMetadataIPv4URL = url.String() - locality := NewAWS(false).Locality() - if !reflect.DeepEqual(locality, v.want) { - t.Errorf("unexpected locality. want :%v, got :%v", v.want, locality) - } - }) - } -} - -func TestIsAWS(t *testing.T) { - cases := []struct { - name string - handlers map[string]handlerFunc - want bool - }{ - {"not aws", map[string]handlerFunc{"/iam/info": errorHandler}, false}, - {"aws", map[string]handlerFunc{"/iam/info": iamInfoHandler}, true}, - } - - for _, v := range cases { - t.Run(v.name, func(tt *testing.T) { - server, url := setupHTTPServer(v.handlers) - defer server.Close() - awsMetadataIPv4URL = url.String() - aws := IsAWS(false) - if !reflect.DeepEqual(aws, v.want) { - t.Errorf("unexpected iam info. want :%v, got :%v", v.want, aws) - } - }) - } -} - -func errorHandler(writer http.ResponseWriter, _ *http.Request) { - writer.WriteHeader(http.StatusInternalServerError) -} - -func regionHandler(writer http.ResponseWriter, _ *http.Request) { - writer.WriteHeader(http.StatusOK) - writer.Write([]byte("us-west-2")) -} - -func zoneHandler(writer http.ResponseWriter, _ *http.Request) { - writer.WriteHeader(http.StatusOK) - writer.Write([]byte("us-west-2b")) -} - -func iamInfoHandler(writer http.ResponseWriter, _ *http.Request) { - writer.WriteHeader(http.StatusOK) - // nolint: lll - writer.Write([]byte("{\n\"Code\" : \"Success\",\n\"LastUpdated\" : \"2022-03-18T05:04:31Z\",\n\"InstanceProfileArn\" : \"arn:aws:iam::614624372165:instance-profile/sam-processing0000120190916053337315200000004\",\n\"InstanceProfileId\" : \"AIPAY6GTXUXC3LLJY7OG7\"\n\t }")) -} - -func setupHTTPServer(handlers map[string]handlerFunc) (*httptest.Server, *url.URL) { - handler := http.NewServeMux() - for path, handle := range handlers { - handler.HandleFunc(path, handle) - } - server := httptest.NewServer(handler) - url, _ := url.Parse(server.URL) - return server, url -} diff --git a/pkg/bootstrap/platform/azure.go b/pkg/bootstrap/platform/azure.go deleted file mode 100644 index 02662048b..000000000 --- a/pkg/bootstrap/platform/azure.go +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package platform - -import ( - "encoding/json" - "fmt" - "io" - "net/http" - "os" - "strings" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - "istio.io/pkg/log" -) - -const ( - AzureMetadataEndpoint = "http://169.254.169.254" - AzureInstanceURL = AzureMetadataEndpoint + "/metadata/instance" - AzureDefaultAPIVersion = "2019-08-15" - SysVendorPath = "/sys/class/dmi/id/sys_vendor" - MicrosoftIdentifier = "Microsoft Corporation" -) - -var ( - azureAPIVersionsFn = func() string { - return metadataRequest("") - } - azureMetadataFn = func(version string) string { - return metadataRequest(fmt.Sprintf("api-version=%s", version)) - } -) - -type azureEnv struct { - APIVersion string - prefix string - computeMetadata map[string]interface{} - networkMetadata map[string]interface{} -} - -// IsAzure returns whether or not the platform for bootstrapping is Azure -// Checks the system vendor file (similar to https://github.com/banzaicloud/satellite/blob/master/providers/azure.go) -func IsAzure() bool { - sysVendor, err := os.ReadFile(SysVendorPath) - if err != nil { - log.Debugf("Error reading sys_vendor in Azure platform detection: %v", err) - } - return strings.Contains(string(sysVendor), MicrosoftIdentifier) -} - -// Attempts to update the API version. -// Newer API versions can contain additional metadata fields -func (e *azureEnv) updateAPIVersion() { - bodyJSON := stringToJSON(azureAPIVersionsFn()) - if newestVersions, ok := bodyJSON["newest-versions"]; ok { - for _, version := range newestVersions.([]interface{}) { - if strings.Compare(version.(string), e.APIVersion) > 0 { - e.APIVersion = version.(string) - } - } - } -} - -// NewAzure returns a platform environment for Azure -// Default prefix is azure_ -func NewAzure() Environment { - return NewAzureWithPrefix("azure_") -} - -func NewAzureWithPrefix(prefix string) Environment { - e := &azureEnv{APIVersion: AzureDefaultAPIVersion} - e.updateAPIVersion() - e.parseMetadata(e.azureMetadata()) - e.prefix = prefix - return e -} - -// Returns the name with the prefix attached -func (e *azureEnv) prefixName(name string) string { - return e.prefix + name -} - -// Retrieves Azure instance metadata response body stores it in the Azure environment -func (e *azureEnv) parseMetadata(metadata string) { - bodyJSON := stringToJSON(metadata) - if computeMetadata, ok := bodyJSON["compute"]; ok { - e.computeMetadata = computeMetadata.(map[string]interface{}) - } - if networkMetadata, ok := bodyJSON["network"]; ok { - e.networkMetadata = networkMetadata.(map[string]interface{}) - } -} - -// Generic Azure metadata GET request helper for the response body -// Uses the default timeout for the HTTP get request -func metadataRequest(query string) string { - client := http.Client{Timeout: defaultTimeout} - req, err := http.NewRequest("GET", fmt.Sprintf("%s?%s", AzureInstanceURL, query), nil) - if err != nil { - log.Warnf("Failed to create HTTP request: %v", err) - return "" - } - req.Header.Add("Metadata", "True") - - response, err := client.Do(req) - if err != nil { - log.Warnf("HTTP request failed: %v", err) - return "" - } - if response.StatusCode != http.StatusOK { - log.Warnf("HTTP request unsuccessful with status: %v", response.Status) - } - defer response.Body.Close() - body, err := io.ReadAll(response.Body) - if err != nil { - log.Warnf("Could not read response body: %v", err) - return "" - } - return string(body) -} - -func stringToJSON(s string) map[string]interface{} { - var stringJSON map[string]interface{} - if err := json.Unmarshal([]byte(s), &stringJSON); err != nil { - log.Warnf("Could not unmarshal response: %v:", err) - } - return stringJSON -} - -// Returns Azure instance metadata. Must be run on an Azure VM -func (e *azureEnv) Metadata() map[string]string { - md := map[string]string{} - if an := e.azureName(); an != "" { - md[e.prefixName("name")] = an - } - if al := e.azureLocation(); al != "" { - md[e.prefixName("location")] = al - } - if aid := e.azureVMID(); aid != "" { - md[e.prefixName("vmId")] = aid - } - for k, v := range e.azureTags() { - md[k] = v - } - return md -} - -// Locality returns the region and zone -func (e *azureEnv) Locality() *core.Locality { - var l core.Locality - l.Region = e.azureLocation() - l.Zone = e.azureZone() - return &l -} - -func (e *azureEnv) Labels() map[string]string { - return map[string]string{} -} - -func (e *azureEnv) IsKubernetes() bool { - return true -} - -func (e *azureEnv) azureMetadata() string { - return azureMetadataFn(e.APIVersion) -} - -func (e *azureEnv) azureName() string { - if an, ok := e.computeMetadata["name"]; ok { - return an.(string) - } - return "" -} - -// Returns the Azure tags -func (e *azureEnv) azureTags() map[string]string { - tags := map[string]string{} - if tl, ok := e.computeMetadata["tagsList"]; ok { - tlByte, err := json.Marshal(tl) - if err != nil { - return tags - } - var atl []azureTag - err = json.Unmarshal(tlByte, &atl) - if err != nil { - return tags - } - for _, tag := range atl { - tags[e.prefixName(tag.Name)] = tag.Value - } - return tags - } - // fall back to tags if tagsList is not available - if at, ok := e.computeMetadata["tags"]; ok && len(at.(string)) > 0 { - for _, tag := range strings.Split(at.(string), ";") { - kv := strings.SplitN(tag, ":", 2) - switch len(kv) { - case 2: - tags[e.prefixName(kv[0])] = kv[1] - case 1: - tags[e.prefixName(kv[0])] = "" - } - } - } - return tags -} - -func (e *azureEnv) azureLocation() string { - if al, ok := e.computeMetadata["location"]; ok { - return al.(string) - } - return "" -} - -func (e *azureEnv) azureZone() string { - if az, ok := e.computeMetadata["zone"]; ok { - return az.(string) - } - return "" -} - -func (e *azureEnv) azureVMID() string { - if aid, ok := e.computeMetadata["vmId"]; ok { - return aid.(string) - } - return "" -} - -// used for simpler JSON parsing -type azureTag struct { - Name string `json:"name"` - Value string `json:"value"` -} diff --git a/pkg/bootstrap/platform/azure_test.go b/pkg/bootstrap/platform/azure_test.go deleted file mode 100644 index 55284ca21..000000000 --- a/pkg/bootstrap/platform/azure_test.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package platform - -import ( - "fmt" - "reflect" - "testing" -) - -// Mock responses for Azure Metadata (based on Microsoft API documentation samples) -// https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service -const ( - MockVersionsTemplate = `{"error": "Bad request. api-version was not specified in the request","newest-versions": ["%s"]}` - MockMetadata = `{"compute": {"location": "centralus", "name": "negasonic", "tags": "Department:IT;Environment:Prod;Role:WorkerRole", ` + - `"vmId": "13f56399-bd52-4150-9748-7190aae1ff21", "zone": "1"}}` - MockMetadataWithValuelessTag = `{"compute": {"location": "centralus", "name": "negasonic", "tags": "Department", ` + - `"vmId": "13f56399-bd52-4150-9748-7190aae1ff21", "zone": "1"}}` - MockMetadataTagsList = `{"compute": {"location": "centralus", "name": "negasonic", ` + - `"tagsList": [{"name": "Department", "value":"IT"}, {"name": "Environment", "value": "Prod"}, {"name": "Role", "value": "WorkerRole; OtherWorker"}], ` + - `"vmId": "13f56399-bd52-4150-9748-7190aae1ff21", "zone": "1"}}` -) - -func TestAzureVersionUpdate(t *testing.T) { - oldAzureAPIVersionsFn := azureAPIVersionsFn - defer func() { azureAPIVersionsFn = oldAzureAPIVersionsFn }() - MinDate, MaxDate := "0000-00-00", "9999-12-31" - tests := []struct { - name string - response string - version string - }{ - {"ignore empty response", "", AzureDefaultAPIVersion}, - {"ignore smaller version", fmt.Sprintf(MockVersionsTemplate, MinDate), AzureDefaultAPIVersion}, - {"ignore no version", fmt.Sprintf(MockVersionsTemplate, ""), AzureDefaultAPIVersion}, - {"use larger version", fmt.Sprintf(MockVersionsTemplate, MaxDate), MaxDate}, - } - - for idx, tt := range tests { - t.Run(fmt.Sprintf("[%d] %s", idx, tt.name), func(t *testing.T) { - azureAPIVersionsFn = func() string { return tt.response } - e := &azureEnv{APIVersion: AzureDefaultAPIVersion} - e.updateAPIVersion() - if e.APIVersion != tt.version { - t.Errorf("updateAPIVersion() => '%v'; want '%v'", e.APIVersion, tt.version) - } - }) - } -} - -func TestAzureMetadata(t *testing.T) { - oldGetAPIVersions, oldGetAzureMetadata := azureAPIVersionsFn, azureMetadataFn - defer func() { azureAPIVersionsFn, azureMetadataFn = oldGetAPIVersions, oldGetAzureMetadata }() - tests := []struct { - name string - response string - metadata map[string]string - }{ - {"ignore empty response", "", map[string]string{}}, - { - "parse fields", MockMetadata, - map[string]string{ - "azure_Department": "IT", "azure_Environment": "Prod", "azure_Role": "WorkerRole", - "azure_name": "negasonic", "azure_location": "centralus", "azure_vmId": "13f56399-bd52-4150-9748-7190aae1ff21", - }, - }, - { - "handle tags without values", MockMetadataWithValuelessTag, - map[string]string{ - "azure_Department": "", "azure_name": "negasonic", "azure_location": "centralus", "azure_vmId": "13f56399-bd52-4150-9748-7190aae1ff21", - }, - }, - { - "handle tagsList", MockMetadataTagsList, - map[string]string{ - "azure_Department": "IT", "azure_Environment": "Prod", "azure_Role": "WorkerRole; OtherWorker", - "azure_name": "negasonic", "azure_location": "centralus", "azure_vmId": "13f56399-bd52-4150-9748-7190aae1ff21", - }, - }, - } - - // Prevent actual requests to metadata server for updating the API version - azureAPIVersionsFn = func() string { return "" } - for idx, tt := range tests { - t.Run(fmt.Sprintf("[%d] %s", idx, tt.name), func(t *testing.T) { - azureMetadataFn = func(string) string { return tt.response } - e := NewAzure() - if metadata := e.Metadata(); !reflect.DeepEqual(metadata, tt.metadata) { - t.Errorf("Metadata() => '%v'; want '%v'", metadata, tt.metadata) - } - }) - } -} diff --git a/pkg/bootstrap/platform/discovery.go b/pkg/bootstrap/platform/discovery.go deleted file mode 100644 index fd9729fdc..000000000 --- a/pkg/bootstrap/platform/discovery.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package platform - -import ( - "strings" - "sync" - "time" -) - -import ( - "istio.io/pkg/env" - "istio.io/pkg/log" -) - -const ( - defaultTimeout = 5 * time.Second - numPlatforms = 3 -) - -var CloudPlatform = env.RegisterStringVar("CLOUD_PLATFORM", "", "Cloud Platform on which proxy is running, if not specified, "+ - "Istio will try to discover the platform. Valid platform values are aws, azure, gcp, none").Get() - -// Discover attempts to discover the host platform, defaulting to -// `Unknown` if a platform cannot be discovered. -func Discover(ipv6 bool) Environment { - // First check if user has specified platform - use it if provided. - if len(CloudPlatform) > 0 { - switch strings.ToLower(CloudPlatform) { - case "aws": - return NewAWS(ipv6) - case "azure": - return NewAzure() - case "gcp": - return NewGCP() - case "none": - return &Unknown{} - } - } - // Discover the platform if user has not specified. - return DiscoverWithTimeout(defaultTimeout, ipv6) -} - -// DiscoverWithTimeout attempts to discover the host platform, defaulting to -// `Unknown` after the provided timeout. -func DiscoverWithTimeout(timeout time.Duration, ipv6 bool) Environment { - plat := make(chan Environment, numPlatforms) // sized to match number of platform goroutines - done := make(chan bool) - - var wg sync.WaitGroup - wg.Add(numPlatforms) // check GCP, AWS, and Azure - - go func() { - if IsGCP() { - log.Info("platform detected is GCP") - plat <- NewGCP() - } - wg.Done() - }() - - go func() { - if IsAWS(ipv6) { - log.Info("platform detected is AWS") - plat <- NewAWS(ipv6) - } - wg.Done() - }() - - go func() { - if IsAzure() { - log.Info("platform detected is Azure") - plat <- NewAzure() - } - wg.Done() - }() - - go func() { - wg.Wait() - close(done) - }() - - timer := time.NewTimer(timeout) - defer timer.Stop() - select { - case p := <-plat: - return p - case <-done: - select { - case p := <-plat: - return p - default: - return &Unknown{} - } - case <-timer.C: - log.Info("timed out waiting for platform detection, treating it as Unknown") - return &Unknown{} - } -} diff --git a/pkg/bootstrap/platform/gcp.go b/pkg/bootstrap/platform/gcp.go deleted file mode 100644 index 766ce30ee..000000000 --- a/pkg/bootstrap/platform/gcp.go +++ /dev/null @@ -1,317 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package platform - -import ( - "context" - "encoding/json" - "fmt" - "os" - "regexp" - "strings" - "sync" -) - -import ( - "cloud.google.com/go/compute/metadata" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - "golang.org/x/oauth2" - "golang.org/x/oauth2/google" - "istio.io/pkg/env" - "istio.io/pkg/log" -) - -const ( - GCPProject = "gcp_project" - GCPProjectNumber = "gcp_project_number" - GCPCluster = "gcp_gke_cluster_name" - GCPClusterURL = "gcp_gke_cluster_url" - GCPLocation = "gcp_location" - GCEInstance = "gcp_gce_instance" - GCEInstanceID = "gcp_gce_instance_id" - GCEInstanceTemplate = "gcp_gce_instance_template" - GCEInstanceCreatedBy = "gcp_gce_instance_created_by" - GCPQuotaProject = "gcp_quota_project" -) - -var ( - GCPMetadata = env.RegisterStringVar("GCP_METADATA", "", "Pipe separated GCP metadata, schemed as PROJECT_ID|PROJECT_NUMBER|CLUSTER_NAME|CLUSTER_ZONE").Get() - - // GCPQuotaProjectVar holds the value of the `GCP_QUOTA_PROJECT` environment variable. - GCPQuotaProjectVar = env.RegisterStringVar("GCP_QUOTA_PROJECT", "", "Allows specification of a quota project to be used in requests to GCP APIs.").Get() -) - -var ( - shouldFillMetadata = metadata.OnGCE - projectIDFn = metadata.ProjectID - numericProjectIDFn = metadata.NumericProjectID - instanceNameFn = metadata.InstanceName - instanceIDFn = metadata.InstanceID - - clusterNameFn = func() (string, error) { - cn, err := metadata.InstanceAttributeValue("cluster-name") - if err != nil { - return "", err - } - return cn, nil - } - clusterLocationFn = func() (string, error) { - cl, err := metadata.InstanceAttributeValue("cluster-location") - if err == nil { - return cl, nil - } - return metadata.Zone() - } - instanceTemplateFn = func() (string, error) { - it, err := metadata.InstanceAttributeValue("instance-template") - if err != nil { - return "", err - } - return it, nil - } - createdByFn = func() (string, error) { - cb, err := metadata.InstanceAttributeValue("created-by") - if err != nil { - return "", err - } - return cb, nil - } - - constructGKEClusterURL = func(md map[string]string) (string, error) { - projectID, found := md[GCPProject] - if !found { - return "", fmt.Errorf("error constructing GKE cluster url: %s not found in GCP Metadata", GCPProject) - } - clusterLocation, found := md[GCPLocation] - if !found { - return "", fmt.Errorf("error constructing GKE cluster url: %s not found in GCP Metadata", GCPLocation) - } - clusterName, found := md[GCPCluster] - if !found { - return "", fmt.Errorf("error constructing GKE cluster url: %s not found in GCP Metadata", GCPCluster) - } - return fmt.Sprintf("https://container.googleapis.com/v1/projects/%s/locations/%s/clusters/%s", - projectID, clusterLocation, clusterName), nil - } -) - -type ( - shouldFillFn func() bool - metadataFn func() (string, error) -) - -type gcpEnv struct { - sync.Mutex - metadata map[string]string -} - -// IsGCP returns whether or not the platform for bootstrapping is Google Cloud Platform. -func IsGCP() bool { - if GCPMetadata != "" { - // Assume this is running on GCP if GCP project env variable is set. - return true - } - return metadata.OnGCE() -} - -// NewGCP returns a platform environment customized for Google Cloud Platform. -// Metadata returned by the GCP Environment is taken from the GCE metadata -// service. -func NewGCP() Environment { - return &gcpEnv{} -} - -// Metadata returns GCP environmental data, including project, cluster name, and -// location information. -func (e *gcpEnv) Metadata() map[string]string { - md := map[string]string{} - if e == nil { - return md - } - if GCPMetadata == "" && !shouldFillMetadata() { - return md - } - - e.Lock() - defer e.Unlock() - if e.metadata != nil { - return e.metadata - } - envPid, envNPid, envCN, envLoc := parseGCPMetadata() - if envPid != "" { - md[GCPProject] = envPid - } else if pid, err := projectIDFn(); err == nil { - md[GCPProject] = pid - } - if envNPid != "" { - md[GCPProjectNumber] = envNPid - } else if npid, err := numericProjectIDFn(); err == nil { - md[GCPProjectNumber] = npid - } - if envLoc != "" { - md[GCPLocation] = envLoc - } else if l, err := clusterLocationFn(); err == nil { - md[GCPLocation] = l - } - if envCN != "" { - md[GCPCluster] = envCN - } else if cn, err := clusterNameFn(); err == nil { - md[GCPCluster] = cn - } - if GCPQuotaProjectVar != "" { - md[GCPQuotaProject] = GCPQuotaProjectVar - } - // Exit early now if not on GCE. This allows setting env var when not on GCE. - if !shouldFillMetadata() { - e.metadata = md - return md - } - if in, err := instanceNameFn(); err == nil { - md[GCEInstance] = in - } - if id, err := instanceIDFn(); err == nil { - md[GCEInstanceID] = id - } - if it, err := instanceTemplateFn(); err == nil { - md[GCEInstanceTemplate] = it - } - if cb, err := createdByFn(); err == nil { - md[GCEInstanceCreatedBy] = cb - } - if clusterURL, err := constructGKEClusterURL(md); err == nil { - md[GCPClusterURL] = clusterURL - } - e.metadata = md - return md -} - -var ( - envOnce sync.Once - envPid string - envNpid string - envCluster string - envLocation string -) - -func parseGCPMetadata() (pid, npid, cluster, location string) { - envOnce.Do(func() { - gcpmd := GCPMetadata - if len(gcpmd) > 0 { - log.Infof("Extract GCP metadata from env variable GCP_METADATA: %v", gcpmd) - parts := strings.Split(gcpmd, "|") - if len(parts) == 4 { - envPid = parts[0] - envNpid = parts[1] - envCluster = parts[2] - envLocation = parts[3] - } - } - }) - return envPid, envNpid, envCluster, envLocation -} - -// Converts a GCP zone into a region. -func zoneToRegion(z string) (string, error) { - // Zones are in the form -, so capture everything but the suffix. - re := regexp.MustCompile("(.*)-.*") - m := re.FindStringSubmatch(z) - if len(m) != 2 { - return "", fmt.Errorf("unable to extract region from GCP zone: %s", z) - } - return m[1], nil -} - -// Locality returns the GCP-specific region and zone. -func (e *gcpEnv) Locality() *core.Locality { - var l core.Locality - if metadata.OnGCE() { - z, zerr := metadata.Zone() - if zerr != nil { - log.Warnf("Error fetching GCP zone: %v", zerr) - return &l - } - r, rerr := zoneToRegion(z) - if rerr != nil { - log.Warnf("Error fetching GCP region: %v", rerr) - return &l - } - l.Region = r - l.Zone = z - } - - return &l -} - -const ComputeReadonlyScope = "https://www.googleapis.com/auth/compute.readonly" - -// Labels attempts to retrieve the GCE instance labels within the timeout -// Requires read access to the Compute API (compute.instances.get) -func (e *gcpEnv) Labels() map[string]string { - md := e.Metadata() - ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) - defer cancel() - - success := make(chan bool) - labels := map[string]string{} - var instanceLabels map[string]string - go func() { - // use explicit credentials with compute.instances.get IAM permissions - creds, err := google.FindDefaultCredentials(ctx, ComputeReadonlyScope) - if err != nil { - log.Warnf("failed to find default credentials: %v", err) - success <- false - return - } - url := fmt.Sprintf("https://compute.googleapis.com/compute/v1/projects/%s/zones/%s/instances/%s", md[GCPProject], md[GCPLocation], md[GCEInstance]) - resp, err := oauth2.NewClient(ctx, creds.TokenSource).Get(url) - if err != nil { - log.Warnf("unable to retrieve instance labels: %v", err) - success <- false - return - - } - defer resp.Body.Close() - instance := &GcpInstance{} - if err := json.NewDecoder(resp.Body).Decode(instance); err != nil { - log.Warnf("failed to decode response: %v", err) - success <- false - return - } - instanceLabels = instance.Labels - success <- true - }() - select { - case <-ctx.Done(): - log.Warnf("context deadline exceeded for instance get request: %v", ctx.Err()) - case ok := <-success: - if ok && instanceLabels != nil { - labels = instanceLabels - } - } - return labels -} - -// GcpInstance the instances response. Only contains fields we care about, rest are ignored -type GcpInstance struct { - // Labels: Labels to apply to this instance. - Labels map[string]string `json:"labels,omitempty"` -} - -// Checks metadata to see if GKE metadata or Kubernetes env vars exist -func (e *gcpEnv) IsKubernetes() bool { - md := e.Metadata() - _, onKubernetes := os.LookupEnv(KubernetesServiceHost) - return md[GCPCluster] != "" || onKubernetes -} diff --git a/pkg/bootstrap/platform/gcp_test.go b/pkg/bootstrap/platform/gcp_test.go deleted file mode 100644 index 45170d70f..000000000 --- a/pkg/bootstrap/platform/gcp_test.go +++ /dev/null @@ -1,360 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package platform - -import ( - "errors" - "fmt" - "os" - "reflect" - "sync" - "testing" -) - -import ( - "cloud.google.com/go/compute/metadata" -) - -func TestGCPMetadata(t *testing.T) { - tests := []struct { - name string - shouldFill shouldFillFn - projectIDFn metadataFn - numericProjectIDFn metadataFn - locationFn metadataFn - clusterNameFn metadataFn - instanceNameFn metadataFn - instanceIDFn metadataFn - instanceTemplateFn metadataFn - instanceCreatedByFn metadataFn - env map[string]string - want map[string]string - }{ - { - "should not fill", - func() bool { return false }, - func() (string, error) { return "pid", nil }, - func() (string, error) { return "npid", nil }, - func() (string, error) { return "location", nil }, - func() (string, error) { return "cluster", nil }, - func() (string, error) { return "instanceName", nil }, - func() (string, error) { return "instance", nil }, - func() (string, error) { return "instanceTemplate", nil }, - func() (string, error) { return "createdBy", nil }, - map[string]string{}, - map[string]string{}, - }, - { - "should fill", - func() bool { return true }, - func() (string, error) { return "pid", nil }, - func() (string, error) { return "npid", nil }, - func() (string, error) { return "location", nil }, - func() (string, error) { return "cluster", nil }, - func() (string, error) { return "instanceName", nil }, - func() (string, error) { return "instance", nil }, - func() (string, error) { return "instanceTemplate", nil }, - func() (string, error) { return "createdBy", nil }, - map[string]string{}, - map[string]string{ - GCPProject: "pid", GCPProjectNumber: "npid", GCPLocation: "location", GCPCluster: "cluster", GCEInstance: "instanceName", - GCEInstanceID: "instance", GCEInstanceTemplate: "instanceTemplate", GCEInstanceCreatedBy: "createdBy", - GCPClusterURL: "https://container.googleapis.com/v1/projects/pid/locations/location/clusters/cluster", - }, - }, - { - "project id error", - func() bool { return true }, - func() (string, error) { return "", errors.New("error") }, - func() (string, error) { return "npid", nil }, - func() (string, error) { return "location", nil }, - func() (string, error) { return "cluster", nil }, - func() (string, error) { return "instanceName", nil }, - func() (string, error) { return "instance", nil }, - func() (string, error) { return "instanceTemplate", nil }, - func() (string, error) { return "createdBy", nil }, - map[string]string{}, - map[string]string{ - GCPLocation: "location", GCPProjectNumber: "npid", GCPCluster: "cluster", GCEInstance: "instanceName", GCEInstanceID: "instance", - GCEInstanceTemplate: "instanceTemplate", GCEInstanceCreatedBy: "createdBy", - }, - }, - { - "numeric project id error", - func() bool { return true }, - func() (string, error) { return "pid", nil }, - func() (string, error) { return "", errors.New("error") }, - func() (string, error) { return "location", nil }, - func() (string, error) { return "cluster", nil }, - func() (string, error) { return "instanceName", nil }, - func() (string, error) { return "instance", nil }, - func() (string, error) { return "instanceTemplate", nil }, - func() (string, error) { return "createdBy", nil }, - map[string]string{}, - map[string]string{ - GCPLocation: "location", GCPProject: "pid", GCPCluster: "cluster", GCEInstance: "instanceName", GCEInstanceID: "instance", - GCEInstanceTemplate: "instanceTemplate", GCEInstanceCreatedBy: "createdBy", - GCPClusterURL: "https://container.googleapis.com/v1/projects/pid/locations/location/clusters/cluster", - }, - }, - { - "location error", - func() bool { return true }, - func() (string, error) { return "pid", nil }, - func() (string, error) { return "npid", nil }, - func() (string, error) { return "location", errors.New("error") }, - func() (string, error) { return "cluster", nil }, - func() (string, error) { return "instanceName", nil }, - func() (string, error) { return "instance", nil }, - func() (string, error) { return "instanceTemplate", nil }, - func() (string, error) { return "createdBy", nil }, - map[string]string{}, - map[string]string{ - GCPProject: "pid", GCPProjectNumber: "npid", GCPCluster: "cluster", GCEInstance: "instanceName", GCEInstanceID: "instance", - GCEInstanceTemplate: "instanceTemplate", GCEInstanceCreatedBy: "createdBy", - }, - }, - { - "cluster name error", - func() bool { return true }, - func() (string, error) { return "pid", nil }, - func() (string, error) { return "npid", nil }, - func() (string, error) { return "location", nil }, - func() (string, error) { return "cluster", errors.New("error") }, - func() (string, error) { return "instanceName", nil }, - func() (string, error) { return "instance", nil }, - func() (string, error) { return "instanceTemplate", nil }, - func() (string, error) { return "createdBy", nil }, - map[string]string{}, - map[string]string{ - GCPProject: "pid", GCPProjectNumber: "npid", GCPLocation: "location", GCEInstance: "instanceName", GCEInstanceID: "instance", - GCEInstanceTemplate: "instanceTemplate", GCEInstanceCreatedBy: "createdBy", - }, - }, - { - "instance name error", - func() bool { return true }, - func() (string, error) { return "pid", nil }, - func() (string, error) { return "npid", nil }, - func() (string, error) { return "location", nil }, - func() (string, error) { return "cluster", nil }, - func() (string, error) { return "instanceName", errors.New("error") }, - func() (string, error) { return "instance", nil }, - func() (string, error) { return "instanceTemplate", nil }, - func() (string, error) { return "createdBy", nil }, - map[string]string{}, - map[string]string{ - GCPProject: "pid", GCPProjectNumber: "npid", GCPLocation: "location", GCPCluster: "cluster", GCEInstanceID: "instance", - GCEInstanceTemplate: "instanceTemplate", GCEInstanceCreatedBy: "createdBy", - GCPClusterURL: "https://container.googleapis.com/v1/projects/pid/locations/location/clusters/cluster", - }, - }, - { - "instance id error", - func() bool { return true }, - func() (string, error) { return "pid", nil }, - func() (string, error) { return "npid", nil }, - func() (string, error) { return "location", nil }, - func() (string, error) { return "cluster", nil }, - func() (string, error) { return "instanceName", nil }, - func() (string, error) { return "", errors.New("error") }, - func() (string, error) { return "instanceTemplate", nil }, - func() (string, error) { return "createdBy", nil }, - map[string]string{}, - map[string]string{ - GCPProject: "pid", GCPProjectNumber: "npid", GCPLocation: "location", GCPCluster: "cluster", GCEInstance: "instanceName", - GCEInstanceTemplate: "instanceTemplate", GCEInstanceCreatedBy: "createdBy", - GCPClusterURL: "https://container.googleapis.com/v1/projects/pid/locations/location/clusters/cluster", - }, - }, - { - "instance template error", - func() bool { return true }, - func() (string, error) { return "pid", nil }, - func() (string, error) { return "npid", nil }, - func() (string, error) { return "location", nil }, - func() (string, error) { return "cluster", nil }, - func() (string, error) { return "instanceName", nil }, - func() (string, error) { return "instance", nil }, - func() (string, error) { return "", errors.New("error") }, - func() (string, error) { return "createdBy", nil }, - map[string]string{}, - map[string]string{ - GCPProject: "pid", GCPProjectNumber: "npid", GCPLocation: "location", GCPCluster: "cluster", GCEInstance: "instanceName", - GCEInstanceID: "instance", GCEInstanceCreatedBy: "createdBy", - GCPClusterURL: "https://container.googleapis.com/v1/projects/pid/locations/location/clusters/cluster", - }, - }, - { - "instance created by error", - func() bool { return true }, - func() (string, error) { return "pid", nil }, - func() (string, error) { return "npid", nil }, - func() (string, error) { return "location", nil }, - func() (string, error) { return "cluster", nil }, - func() (string, error) { return "instanceName", nil }, - func() (string, error) { return "instance", nil }, - func() (string, error) { return "instanceTemplate", nil }, - func() (string, error) { return "", errors.New("error") }, - map[string]string{}, - map[string]string{ - GCPProject: "pid", GCPProjectNumber: "npid", GCPLocation: "location", GCPCluster: "cluster", GCEInstance: "instanceName", - GCEInstanceID: "instance", GCEInstanceTemplate: "instanceTemplate", - GCPClusterURL: "https://container.googleapis.com/v1/projects/pid/locations/location/clusters/cluster", - }, - }, - { - "use env variable", - func() bool { return true }, - func() (string, error) { return "pid", nil }, - func() (string, error) { return "npid", nil }, - func() (string, error) { return "location", nil }, - func() (string, error) { return "cluster", nil }, - func() (string, error) { return "instanceName", nil }, - func() (string, error) { return "instance", nil }, - func() (string, error) { return "instanceTemplate", nil }, - func() (string, error) { return "createdBy", nil }, - map[string]string{"GCP_METADATA": "env_pid|env_pn|env_cluster|env_location"}, - map[string]string{ - GCPProject: "env_pid", GCPProjectNumber: "env_pn", GCPLocation: "env_location", GCPCluster: "env_cluster", - GCEInstance: "instanceName", GCEInstanceID: "instance", GCEInstanceTemplate: "instanceTemplate", GCEInstanceCreatedBy: "createdBy", - GCPClusterURL: "https://container.googleapis.com/v1/projects/env_pid/locations/env_location/clusters/env_cluster", - }, - }, - } - - for idx, tt := range tests { - t.Run(fmt.Sprintf("[%d] %s", idx, tt.name), func(t *testing.T) { - for e, v := range tt.env { - os.Setenv(e, v) - if e == "GCP_METADATA" { - GCPMetadata = v - } - } - shouldFillMetadata, projectIDFn, numericProjectIDFn, clusterLocationFn, clusterNameFn, - instanceNameFn, instanceIDFn, instanceTemplateFn, createdByFn = tt.shouldFill, tt.projectIDFn, tt.numericProjectIDFn, tt.locationFn, tt.clusterNameFn, - tt.instanceNameFn, tt.instanceIDFn, tt.instanceTemplateFn, tt.instanceCreatedByFn - e := NewGCP() - got := e.Metadata() - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("gcpEnv.Metadata() => '%v'; want '%v'", got, tt.want) - } - for e := range tt.env { - os.Unsetenv(e) - if e == "GCP_METADATA" { - GCPMetadata = "" - } - } - envOnce, envPid, envNpid, envCluster, envLocation = sync.Once{}, "", "", "", "" - }) - } -} - -func TestGCPQuotaProject(t *testing.T) { - cases := []struct { - name string - quotaProject string - wantFound bool - wantProject string - }{ - {"no value set", "", false, ""}, - {"value set", "234234323", true, "234234323"}, - } - for _, v := range cases { - t.Run(v.name, func(tt *testing.T) { - shouldFillMetadata = func() bool { return true } - tmpQuotaProject := GCPQuotaProjectVar - GCPQuotaProjectVar = v.quotaProject - defer func() { - GCPQuotaProjectVar = tmpQuotaProject - shouldFillMetadata = metadata.OnGCE - }() - meta := NewGCP().Metadata() - val, found := meta[GCPQuotaProject] - if got, want := found, v.wantFound; got != want { - tt.Errorf("Metadata() returned an unexpected value for GCPQuotaProject; found value: %t, want %t", got, want) - } - if got, want := val, v.wantProject; got != want { - tt.Errorf("Incorrect value for GCPQuotaProject; got = %q, want = %q", got, want) - } - }) - } -} - -func TestMetadataCache(t *testing.T) { - tests := []struct { - name string - shouldFill shouldFillFn - projectIDFn metadataFn - numericProjectIDFn metadataFn - locationFn metadataFn - clusterNameFn metadataFn - instanceNameFn metadataFn - instanceIDFn metadataFn - instanceTemplateFn metadataFn - instanceCreatedByFn metadataFn - env map[string]string - want map[string]string - }{ - { - "should cache", - func() bool { return true }, - func() (string, error) { return "pid", nil }, - func() (string, error) { return "npid", nil }, - func() (string, error) { return "location", nil }, - func() (string, error) { return "cluster", nil }, - func() (string, error) { return "instanceName", nil }, - func() (string, error) { return "instance", nil }, - func() (string, error) { return "instanceTemplate", nil }, - func() (string, error) { return "createdBy", nil }, - map[string]string{}, - map[string]string{ - GCPProject: "pid", GCPProjectNumber: "npid", GCPLocation: "location", GCPCluster: "cluster", GCEInstance: "instanceName", - GCEInstanceID: "instance", GCEInstanceTemplate: "instanceTemplate", GCEInstanceCreatedBy: "createdBy", - GCPClusterURL: "https://container.googleapis.com/v1/projects/pid/locations/location/clusters/cluster", - }, - }, { - "should ignore", - func() bool { return true }, - func() (string, error) { return "newPid", nil }, - func() (string, error) { return "newNpid", nil }, - func() (string, error) { return "newLocation", nil }, - func() (string, error) { return "newCluster", nil }, - func() (string, error) { return "newInstanceName", nil }, - func() (string, error) { return "newInstance", nil }, - func() (string, error) { return "newInstanceTemplate", nil }, - func() (string, error) { return "newCreatedBy", nil }, - map[string]string{}, - map[string]string{ - GCPProject: "pid", GCPProjectNumber: "npid", GCPLocation: "location", GCPCluster: "cluster", GCEInstance: "instanceName", - GCEInstanceID: "instance", GCEInstanceTemplate: "instanceTemplate", GCEInstanceCreatedBy: "createdBy", - GCPClusterURL: "https://container.googleapis.com/v1/projects/pid/locations/location/clusters/cluster", - }, - }, - } - gcpEnv := NewGCP() - for idx, tt := range tests { - t.Run(fmt.Sprintf("[%d] %s", idx, tt.name), func(t *testing.T) { - shouldFillMetadata, projectIDFn, numericProjectIDFn, clusterLocationFn, clusterNameFn, - instanceNameFn, instanceIDFn, instanceTemplateFn, createdByFn = tt.shouldFill, tt.projectIDFn, tt.numericProjectIDFn, tt.locationFn, tt.clusterNameFn, - tt.instanceNameFn, tt.instanceIDFn, tt.instanceTemplateFn, tt.instanceCreatedByFn - got := gcpEnv.Metadata() - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("gcpEnv.Metadata() => '%v'; want '%v'", got, tt.want) - } - envOnce, envPid, envNpid, envCluster, envLocation = sync.Once{}, "", "", "", "" - }) - } -} diff --git a/pkg/bootstrap/platform/platform.go b/pkg/bootstrap/platform/platform.go deleted file mode 100644 index 06b7caab3..000000000 --- a/pkg/bootstrap/platform/platform.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package platform - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" -) - -const ( - KubernetesServiceHost = "KUBERNETES_SERVICE_HOST" -) - -// Environment provides information for the platform on which the bootstrapping -// is taking place. -type Environment interface { - // Metadata returns a collection of environmental metadata, structured - // as a map for metadata names to values. An example for GCP would be a - // mapping from "gcp_project" to "2344534543". Keys should be prefixed - // by the short name for the platform (example: "gcp_"). - Metadata() map[string]string - - // Locality returns the run location for the bootstrap transformed from the - // platform-specific representation into the Envoy Locality schema. - Locality() *core.Locality - - // Labels returns a collection of labels that exist on the underlying - // instance, structured as a map for label name to values. - Labels() map[string]string - - // IsKubernetes determines if running on Kubernetes - IsKubernetes() bool -} - -// Unknown provides a default platform environment for cases in which the platform -// on which the bootstrapping is taking place cannot be determined. -type Unknown struct{} - -// Metadata returns an empty map. -func (*Unknown) Metadata() map[string]string { - return map[string]string{} -} - -// Locality returns an empty core.Locality struct. -func (*Unknown) Locality() *core.Locality { - return &core.Locality{} -} - -// Labels returns an empty map. -func (*Unknown) Labels() map[string]string { - return map[string]string{} -} - -// IsKubernetes is true to avoid label collisions -func (*Unknown) IsKubernetes() bool { - return true -} diff --git a/pkg/bootstrap/testdata/all.proxycfg b/pkg/bootstrap/testdata/all.proxycfg deleted file mode 100644 index 815e72180..000000000 --- a/pkg/bootstrap/testdata/all.proxycfg +++ /dev/null @@ -1,15 +0,0 @@ -config_path: "/etc/istio/proxy" -binary_path: "/usr/local/bin/envoy" -service_cluster: "istio-proxy" -drain_duration: {seconds: 5} -parent_shutdown_duration: {seconds: 6} -discovery_address: "mypilot:15011" -statsd_udp_address: "10.1.1.1:9125" -envoy_metrics_service: {address: "metrics-service:15000", tls_settings: { mode: MUTUAL, client_certificate: "/etc/istio/ms/client.pem", private_key: "/etc/istio/ms/key.pem", ca_certificates: "/etc/istio/ms/ca.pem"}} -envoy_access_log_service: {address: "accesslog-service:15000"} -proxy_admin_port: 15005 -control_plane_auth_policy: MUTUAL_TLS -stat_name_length: 200 -tracing: { zipkin: { address: "localhost:6000" } } - -# Sets all relevant options to values different than default diff --git a/pkg/bootstrap/testdata/all_golden.json b/pkg/bootstrap/testdata/all_golden.json deleted file mode 100644 index 38b6a5212..000000000 --- a/pkg/bootstrap/testdata/all_golden.json +++ /dev/null @@ -1,642 +0,0 @@ -{ - "node": { - "id": "sidecar~1.2.3.4~foo~bar", - "cluster": "istio-proxy", - "locality": { - }, - "metadata": {"ENVOY_PROMETHEUS_PORT":15090,"ENVOY_STATUS_PORT":15021,"INSTANCE_IPS":"10.3.3.3,10.4.4.4,10.5.5.5,10.6.6.6","OUTLIER_LOG_PATH":"/dev/stdout","PILOT_SAN":["spiffe://cluster.local/ns/dubbo-system/sa/istio-pilot-service-account"],"PROXY_CONFIG":{"binaryPath":"/usr/local/bin/envoy","configPath":"/tmp/bootstrap/all","controlPlaneAuthPolicy":"MUTUAL_TLS","customConfigFile":"envoy_bootstrap.json","discoveryAddress":"mypilot:15011","drainDuration":"5s","envoyAccessLogService":{"address":"accesslog-service:15000"},"envoyMetricsService":{"address":"metrics-service:15000","tlsSettings":{"caCertificates":"/etc/istio/ms/ca.pem","clientCertificate":"/etc/istio/ms/client.pem","mode":"MUTUAL","privateKey":"/etc/istio/ms/key.pem"}},"parentShutdownDuration":"6s","proxyAdminPort":15005,"serviceCluster":"istio-proxy","statNameLength":200,"statsdUdpAddress":"10.1.1.1:9125","statusPort":15020,"tracing":{"zipkin":{"address":"localhost:6000"}}}} - }, - "layered_runtime": { - "layers": [ - { - "name": "global config", - "static_layer": {"envoy.deprecated_features:envoy.config.listener.v3.Listener.hidden_envoy_deprecated_use_original_dst":"true","envoy.reloadable_features.http_reject_path_with_fragment":"false","envoy.reloadable_features.no_extension_lookup_by_name":"false","envoy.reloadable_features.require_strict_1xx_and_204_response_headers":"false","overload.global_downstream_max_connections":"2147483647","re2.max_program_size.error_level":"32768"} - }, - { - "name": "admin", - "admin_layer": {} - } - ] - }, - "stats_config": { - "use_all_default_tags": false, - "stats_tags": [ - { - "tag_name": "cluster_name", - "regex": "^cluster\\.((.+?(\\..+?\\.svc\\.cluster\\.local)?)\\.)" - }, - { - "tag_name": "tcp_prefix", - "regex": "^tcp\\.((.*?)\\.)\\w+?$" - }, - { - "regex": "(response_code=\\.=(.+?);\\.;)|_rq(_(\\.d{3}))$", - "tag_name": "response_code" - }, - { - "tag_name": "response_code_class", - "regex": "_rq(_(\\dxx))$" - }, - { - "tag_name": "http_conn_manager_listener_prefix", - "regex": "^listener(?=\\.).*?\\.http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "http_conn_manager_prefix", - "regex": "^http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "listener_address", - "regex": "^listener\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "mongo_prefix", - "regex": "^mongo\\.(.+?)\\.(collection|cmd|cx_|op_|delays_|decoding_)(.*?)$" - }, - { - "regex": "(reporter=\\.=(.*?);\\.;)", - "tag_name": "reporter" - }, - { - "regex": "(source_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_namespace" - }, - { - "regex": "(source_workload=\\.=(.*?);\\.;)", - "tag_name": "source_workload" - }, - { - "regex": "(source_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_workload_namespace" - }, - { - "regex": "(source_principal=\\.=(.*?);\\.;)", - "tag_name": "source_principal" - }, - { - "regex": "(source_app=\\.=(.*?);\\.;)", - "tag_name": "source_app" - }, - { - "regex": "(source_version=\\.=(.*?);\\.;)", - "tag_name": "source_version" - }, - { - "regex": "(source_cluster=\\.=(.*?);\\.;)", - "tag_name": "source_cluster" - }, - { - "regex": "(destination_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_namespace" - }, - { - "regex": "(destination_workload=\\.=(.*?);\\.;)", - "tag_name": "destination_workload" - }, - { - "regex": "(destination_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_workload_namespace" - }, - { - "regex": "(destination_principal=\\.=(.*?);\\.;)", - "tag_name": "destination_principal" - }, - { - "regex": "(destination_app=\\.=(.*?);\\.;)", - "tag_name": "destination_app" - }, - { - "regex": "(destination_version=\\.=(.*?);\\.;)", - "tag_name": "destination_version" - }, - { - "regex": "(destination_service=\\.=(.*?);\\.;)", - "tag_name": "destination_service" - }, - { - "regex": "(destination_service_name=\\.=(.*?);\\.;)", - "tag_name": "destination_service_name" - }, - { - "regex": "(destination_service_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_service_namespace" - }, - { - "regex": "(destination_port=\\.=(.*?);\\.;)", - "tag_name": "destination_port" - }, - { - "regex": "(destination_cluster=\\.=(.*?);\\.;)", - "tag_name": "destination_cluster" - }, - { - "regex": "(request_protocol=\\.=(.*?);\\.;)", - "tag_name": "request_protocol" - }, - { - "regex": "(request_operation=\\.=(.*?);\\.;)", - "tag_name": "request_operation" - }, - { - "regex": "(request_host=\\.=(.*?);\\.;)", - "tag_name": "request_host" - }, - { - "regex": "(response_flags=\\.=(.*?);\\.;)", - "tag_name": "response_flags" - }, - { - "regex": "(grpc_response_status=\\.=(.*?);\\.;)", - "tag_name": "grpc_response_status" - }, - { - "regex": "(connection_security_policy=\\.=(.*?);\\.;)", - "tag_name": "connection_security_policy" - }, - { - "regex": "(source_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_service" - }, - { - "regex": "(destination_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_service" - }, - { - "regex": "(source_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_revision" - }, - { - "regex": "(destination_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_revision" - }, - { - "regex": "(cache\\.(.+?)\\.)", - "tag_name": "cache" - }, - { - "regex": "(component\\.(.+?)\\.)", - "tag_name": "component" - }, - { - "regex": "(tag\\.(.+?);\\.)", - "tag_name": "tag" - }, - { - "regex": "(wasm_filter\\.(.+?)\\.)", - "tag_name": "wasm_filter" - }, - { - "tag_name": "authz_enforce_result", - "regex": "rbac(\\.(allowed|denied))" - }, - { - "tag_name": "authz_dry_run_action", - "regex": "(\\.istio_dry_run_(allow|deny)_)" - }, - { - "tag_name": "authz_dry_run_result", - "regex": "(\\.shadow_(allowed|denied))" - } - ], - "stats_matcher": { - "inclusion_list": { - "patterns": [ - { - "prefix": "reporter=" - }, - { - "prefix": "cluster_manager" - }, - { - "prefix": "listener_manager" - }, - { - "prefix": "server" - }, - { - "prefix": "cluster.xds-grpc" - }, - { - "prefix": "wasm" - }, - { - "suffix": "rbac.allowed" - }, - { - "suffix": "rbac.denied" - }, - { - "suffix": "shadow_allowed" - }, - { - "suffix": "shadow_denied" - }, - { - "prefix": "component" - } - ] - } - } - }, - "admin": { - "access_log_path": "/dev/null", - "profile_path": "/var/lib/istio/data/envoy.prof", - "address": { - "socket_address": { - "address": "127.0.0.1", - "port_value": 15005 - } - } - }, - "dynamic_resources": { - "lds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "cds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "ads_config": { - "api_type": "GRPC", - "set_node_on_first_message_only": true, - "transport_api_version": "V3", - "grpc_services": [ - { - "envoy_grpc": { - "cluster_name": "xds-grpc" - } - } - ] - } - }, - "static_resources": { - "clusters": [ - { - "name": "prometheus_stats", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "prometheus_stats", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15005 - } - } - } - }] - }] - } - }, - { - "name": "agent", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "agent", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15020 - } - } - } - }] - }] - } - }, - { - "name": "sds-grpc", - "type": "STATIC", - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - }, - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "sds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "./var/run/secrets/workload-spiffe-uds/socket" - } - } - } - }] - }] - } - }, - { - "name": "xds-grpc", - "type" : "STATIC", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "xds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "/tmp/XDS" - } - } - } - }] - }] - }, - "circuit_breakers": { - "thresholds": [ - { - "priority": "DEFAULT", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - }, - { - "priority": "HIGH", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - } - ] - }, - "upstream_connection_options": { - "tcp_keepalive": { - "keepalive_time": 300 - } - }, - "max_requests_per_connection": 1, - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - } - } - - , - { - "name": "zipkin", - "type": "STRICT_DNS", - "respect_dns_ttl": true, - "dns_lookup_family": "V4_ONLY", - "dns_refresh_rate": "30s", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "zipkin", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": {"address": "localhost", "port_value": 6000} - } - } - }] - }] - } - } - - , - { - "name": "envoy_metrics_service", - "type": "STRICT_DNS", - "transport_socket": {"name":"envoy.transport_sockets.tls","typed_config":{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext","common_tls_context":{"alpn_protocols":["h2"],"combined_validation_context":{"default_validation_context":{},"validation_context_sds_secret_config":{"name":"file-root:/etc/istio/ms/ca.pem","sds_config":{"api_config_source":{"api_type":"GRPC","grpc_services":[{"envoy_grpc":{"cluster_name":"sds-grpc"}}],"set_node_on_first_message_only":true,"transport_api_version":"V3"},"resource_api_version":"V3"}}},"tls_certificate_sds_secret_configs":[{"name":"file-cert:/etc/istio/ms/client.pem~/etc/istio/ms/key.pem","sds_config":{"api_config_source":{"api_type":"GRPC","grpc_services":[{"envoy_grpc":{"cluster_name":"sds-grpc"}}],"set_node_on_first_message_only":true,"transport_api_version":"V3"},"resource_api_version":"V3"}}]}}}, - "respect_dns_ttl": true, - "dns_lookup_family": "V4_ONLY", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - }, - "load_assignment": { - "cluster_name": "envoy_metrics_service", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": {"address": "metrics-service", "port_value": 15000} - } - } - }] - }] - } - } - - - , - { - "name": "envoy_accesslog_service", - "type": "STRICT_DNS", - "respect_dns_ttl": true, - "dns_lookup_family": "V4_ONLY", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - }, - "load_assignment": { - "cluster_name": "envoy_accesslog_service", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": {"address": "accesslog-service", "port_value": 15000} - } - } - }] - }] - } - } - - ], - "listeners":[ - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15090 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "stats", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/stats/prometheus" - }, - "route": { - "cluster": "prometheus_stats" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - }, - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15021 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "agent", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/healthz/ready" - }, - "route": { - "cluster": "agent" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - } - ] - } - , - "tracing": { - "http": { - "name": "envoy.tracers.zipkin", - "typed_config": { - "@type": "type.googleapis.com/envoy.config.trace.v3.ZipkinConfig", - "collector_cluster": "zipkin", - "collector_endpoint": "/api/v2/spans", - "collector_endpoint_version": "HTTP_JSON", - "trace_id_128bit": true, - "shared_span_context": false - } - } - } - - , - "stats_sinks": [ - - { - "name": "envoy.stat_sinks.metrics_service", - "typed_config": { - "@type": "type.googleapis.com/envoy.config.metrics.v3.MetricsServiceConfig", - "transport_api_version": "V3", - "grpc_service": { - "envoy_grpc": { - "cluster_name": "envoy_metrics_service" - } - } - } - } - - - , - - - { - "name": "envoy.stat_sinks.statsd", - "typed_config": { - "@type": "type.googleapis.com/envoy.config.metrics.v3.StatsdSink", - "address": { - "socket_address": {"address": "10.1.1.1", "port_value": 9125} - } - } - } - - ] - - - , - "cluster_manager": { - "outlier_detection": { - "event_log_path": "/dev/stdout" - } - } - -} diff --git a/pkg/bootstrap/testdata/auth.proxycfg b/pkg/bootstrap/testdata/auth.proxycfg deleted file mode 100644 index 8ddc86e5f..000000000 --- a/pkg/bootstrap/testdata/auth.proxycfg +++ /dev/null @@ -1,10 +0,0 @@ -config_path: "/etc/istio/proxy" -binary_path: "/usr/local/bin/envoy" -service_cluster: "istio-proxy" -drain_duration: {seconds: 2} -parent_shutdown_duration: {seconds: 3} -discovery_address: "istio-pilot:15011" -proxy_admin_port: 15000 -control_plane_auth_policy: MUTUAL_TLS - -# Same as default, but with MUTUAL_TLS enabled diff --git a/pkg/bootstrap/testdata/auth_golden.json b/pkg/bootstrap/testdata/auth_golden.json deleted file mode 100644 index eb5d918fc..000000000 --- a/pkg/bootstrap/testdata/auth_golden.json +++ /dev/null @@ -1,512 +0,0 @@ -{ - "node": { - "id": "sidecar~1.2.3.4~foo~bar", - "cluster": "istio-proxy", - "locality": { - }, - "metadata": {"ENVOY_PROMETHEUS_PORT":15090,"ENVOY_STATUS_PORT":15021,"INSTANCE_IPS":"10.3.3.3,10.4.4.4,10.5.5.5,10.6.6.6","OUTLIER_LOG_PATH":"/dev/stdout","PILOT_SAN":["spiffe://cluster.local/ns/dubbo-system/sa/istio-pilot-service-account"],"PROXY_CONFIG":{"binaryPath":"/usr/local/bin/envoy","configPath":"/tmp/bootstrap/auth","controlPlaneAuthPolicy":"MUTUAL_TLS","customConfigFile":"envoy_bootstrap.json","discoveryAddress":"istio-pilot:15011","drainDuration":"2s","parentShutdownDuration":"3s","proxyAdminPort":15000,"serviceCluster":"istio-proxy","statusPort":15020}} - }, - "layered_runtime": { - "layers": [ - { - "name": "global config", - "static_layer": {"envoy.deprecated_features:envoy.config.listener.v3.Listener.hidden_envoy_deprecated_use_original_dst":"true","envoy.reloadable_features.http_reject_path_with_fragment":"false","envoy.reloadable_features.no_extension_lookup_by_name":"false","envoy.reloadable_features.require_strict_1xx_and_204_response_headers":"false","overload.global_downstream_max_connections":"2147483647","re2.max_program_size.error_level":"32768"} - }, - { - "name": "admin", - "admin_layer": {} - } - ] - }, - "stats_config": { - "use_all_default_tags": false, - "stats_tags": [ - { - "tag_name": "cluster_name", - "regex": "^cluster\\.((.+?(\\..+?\\.svc\\.cluster\\.local)?)\\.)" - }, - { - "tag_name": "tcp_prefix", - "regex": "^tcp\\.((.*?)\\.)\\w+?$" - }, - { - "regex": "(response_code=\\.=(.+?);\\.;)|_rq(_(\\.d{3}))$", - "tag_name": "response_code" - }, - { - "tag_name": "response_code_class", - "regex": "_rq(_(\\dxx))$" - }, - { - "tag_name": "http_conn_manager_listener_prefix", - "regex": "^listener(?=\\.).*?\\.http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "http_conn_manager_prefix", - "regex": "^http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "listener_address", - "regex": "^listener\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "mongo_prefix", - "regex": "^mongo\\.(.+?)\\.(collection|cmd|cx_|op_|delays_|decoding_)(.*?)$" - }, - { - "regex": "(reporter=\\.=(.*?);\\.;)", - "tag_name": "reporter" - }, - { - "regex": "(source_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_namespace" - }, - { - "regex": "(source_workload=\\.=(.*?);\\.;)", - "tag_name": "source_workload" - }, - { - "regex": "(source_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_workload_namespace" - }, - { - "regex": "(source_principal=\\.=(.*?);\\.;)", - "tag_name": "source_principal" - }, - { - "regex": "(source_app=\\.=(.*?);\\.;)", - "tag_name": "source_app" - }, - { - "regex": "(source_version=\\.=(.*?);\\.;)", - "tag_name": "source_version" - }, - { - "regex": "(source_cluster=\\.=(.*?);\\.;)", - "tag_name": "source_cluster" - }, - { - "regex": "(destination_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_namespace" - }, - { - "regex": "(destination_workload=\\.=(.*?);\\.;)", - "tag_name": "destination_workload" - }, - { - "regex": "(destination_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_workload_namespace" - }, - { - "regex": "(destination_principal=\\.=(.*?);\\.;)", - "tag_name": "destination_principal" - }, - { - "regex": "(destination_app=\\.=(.*?);\\.;)", - "tag_name": "destination_app" - }, - { - "regex": "(destination_version=\\.=(.*?);\\.;)", - "tag_name": "destination_version" - }, - { - "regex": "(destination_service=\\.=(.*?);\\.;)", - "tag_name": "destination_service" - }, - { - "regex": "(destination_service_name=\\.=(.*?);\\.;)", - "tag_name": "destination_service_name" - }, - { - "regex": "(destination_service_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_service_namespace" - }, - { - "regex": "(destination_port=\\.=(.*?);\\.;)", - "tag_name": "destination_port" - }, - { - "regex": "(destination_cluster=\\.=(.*?);\\.;)", - "tag_name": "destination_cluster" - }, - { - "regex": "(request_protocol=\\.=(.*?);\\.;)", - "tag_name": "request_protocol" - }, - { - "regex": "(request_operation=\\.=(.*?);\\.;)", - "tag_name": "request_operation" - }, - { - "regex": "(request_host=\\.=(.*?);\\.;)", - "tag_name": "request_host" - }, - { - "regex": "(response_flags=\\.=(.*?);\\.;)", - "tag_name": "response_flags" - }, - { - "regex": "(grpc_response_status=\\.=(.*?);\\.;)", - "tag_name": "grpc_response_status" - }, - { - "regex": "(connection_security_policy=\\.=(.*?);\\.;)", - "tag_name": "connection_security_policy" - }, - { - "regex": "(source_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_service" - }, - { - "regex": "(destination_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_service" - }, - { - "regex": "(source_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_revision" - }, - { - "regex": "(destination_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_revision" - }, - { - "regex": "(cache\\.(.+?)\\.)", - "tag_name": "cache" - }, - { - "regex": "(component\\.(.+?)\\.)", - "tag_name": "component" - }, - { - "regex": "(tag\\.(.+?);\\.)", - "tag_name": "tag" - }, - { - "regex": "(wasm_filter\\.(.+?)\\.)", - "tag_name": "wasm_filter" - }, - { - "tag_name": "authz_enforce_result", - "regex": "rbac(\\.(allowed|denied))" - }, - { - "tag_name": "authz_dry_run_action", - "regex": "(\\.istio_dry_run_(allow|deny)_)" - }, - { - "tag_name": "authz_dry_run_result", - "regex": "(\\.shadow_(allowed|denied))" - } - ], - "stats_matcher": { - "inclusion_list": { - "patterns": [ - { - "prefix": "reporter=" - }, - { - "prefix": "cluster_manager" - }, - { - "prefix": "listener_manager" - }, - { - "prefix": "server" - }, - { - "prefix": "cluster.xds-grpc" - }, - { - "prefix": "wasm" - }, - { - "suffix": "rbac.allowed" - }, - { - "suffix": "rbac.denied" - }, - { - "suffix": "shadow_allowed" - }, - { - "suffix": "shadow_denied" - }, - { - "prefix": "component" - } - ] - } - } - }, - "admin": { - "access_log_path": "/dev/null", - "profile_path": "/var/lib/istio/data/envoy.prof", - "address": { - "socket_address": { - "address": "127.0.0.1", - "port_value": 15000 - } - } - }, - "dynamic_resources": { - "lds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "cds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "ads_config": { - "api_type": "GRPC", - "set_node_on_first_message_only": true, - "transport_api_version": "V3", - "grpc_services": [ - { - "envoy_grpc": { - "cluster_name": "xds-grpc" - } - } - ] - } - }, - "static_resources": { - "clusters": [ - { - "name": "prometheus_stats", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "prometheus_stats", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15000 - } - } - } - }] - }] - } - }, - { - "name": "agent", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "agent", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15020 - } - } - } - }] - }] - } - }, - { - "name": "sds-grpc", - "type": "STATIC", - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - }, - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "sds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "./var/run/secrets/workload-spiffe-uds/socket" - } - } - } - }] - }] - } - }, - { - "name": "xds-grpc", - "type" : "STATIC", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "xds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "/tmp/XDS" - } - } - } - }] - }] - }, - "circuit_breakers": { - "thresholds": [ - { - "priority": "DEFAULT", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - }, - { - "priority": "HIGH", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - } - ] - }, - "upstream_connection_options": { - "tcp_keepalive": { - "keepalive_time": 300 - } - }, - "max_requests_per_connection": 1, - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - } - } - - - ], - "listeners":[ - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15090 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "stats", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/stats/prometheus" - }, - "route": { - "cluster": "prometheus_stats" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - }, - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15021 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "agent", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/healthz/ready" - }, - "route": { - "cluster": "agent" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - } - ] - } - - - , - "cluster_manager": { - "outlier_detection": { - "event_log_path": "/dev/stdout" - } - } - -} diff --git a/pkg/bootstrap/testdata/authsds.proxycfg b/pkg/bootstrap/testdata/authsds.proxycfg deleted file mode 100644 index 8ddc86e5f..000000000 --- a/pkg/bootstrap/testdata/authsds.proxycfg +++ /dev/null @@ -1,10 +0,0 @@ -config_path: "/etc/istio/proxy" -binary_path: "/usr/local/bin/envoy" -service_cluster: "istio-proxy" -drain_duration: {seconds: 2} -parent_shutdown_duration: {seconds: 3} -discovery_address: "istio-pilot:15011" -proxy_admin_port: 15000 -control_plane_auth_policy: MUTUAL_TLS - -# Same as default, but with MUTUAL_TLS enabled diff --git a/pkg/bootstrap/testdata/authsds_golden.json b/pkg/bootstrap/testdata/authsds_golden.json deleted file mode 100644 index 7e0390dec..000000000 --- a/pkg/bootstrap/testdata/authsds_golden.json +++ /dev/null @@ -1,512 +0,0 @@ -{ - "node": { - "id": "sidecar~1.2.3.4~foo~bar", - "cluster": "istio-proxy", - "locality": { - }, - "metadata": {"ENVOY_PROMETHEUS_PORT":15090,"ENVOY_STATUS_PORT":15021,"INSTANCE_IPS":"10.3.3.3,10.4.4.4,10.5.5.5,10.6.6.6","OUTLIER_LOG_PATH":"/dev/stdout","PILOT_SAN":["spiffe://cluster.local/ns/dubbo-system/sa/istio-pilot-service-account"],"PROXY_CONFIG":{"binaryPath":"/usr/local/bin/envoy","configPath":"/tmp/bootstrap/authsds","controlPlaneAuthPolicy":"MUTUAL_TLS","customConfigFile":"envoy_bootstrap.json","discoveryAddress":"istio-pilot:15011","drainDuration":"2s","parentShutdownDuration":"3s","proxyAdminPort":15000,"serviceCluster":"istio-proxy","statusPort":15020}} - }, - "layered_runtime": { - "layers": [ - { - "name": "global config", - "static_layer": {"envoy.deprecated_features:envoy.config.listener.v3.Listener.hidden_envoy_deprecated_use_original_dst":"true","envoy.reloadable_features.http_reject_path_with_fragment":"false","envoy.reloadable_features.no_extension_lookup_by_name":"false","envoy.reloadable_features.require_strict_1xx_and_204_response_headers":"false","overload.global_downstream_max_connections":"2147483647","re2.max_program_size.error_level":"32768"} - }, - { - "name": "admin", - "admin_layer": {} - } - ] - }, - "stats_config": { - "use_all_default_tags": false, - "stats_tags": [ - { - "tag_name": "cluster_name", - "regex": "^cluster\\.((.+?(\\..+?\\.svc\\.cluster\\.local)?)\\.)" - }, - { - "tag_name": "tcp_prefix", - "regex": "^tcp\\.((.*?)\\.)\\w+?$" - }, - { - "regex": "(response_code=\\.=(.+?);\\.;)|_rq(_(\\.d{3}))$", - "tag_name": "response_code" - }, - { - "tag_name": "response_code_class", - "regex": "_rq(_(\\dxx))$" - }, - { - "tag_name": "http_conn_manager_listener_prefix", - "regex": "^listener(?=\\.).*?\\.http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "http_conn_manager_prefix", - "regex": "^http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "listener_address", - "regex": "^listener\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "mongo_prefix", - "regex": "^mongo\\.(.+?)\\.(collection|cmd|cx_|op_|delays_|decoding_)(.*?)$" - }, - { - "regex": "(reporter=\\.=(.*?);\\.;)", - "tag_name": "reporter" - }, - { - "regex": "(source_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_namespace" - }, - { - "regex": "(source_workload=\\.=(.*?);\\.;)", - "tag_name": "source_workload" - }, - { - "regex": "(source_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_workload_namespace" - }, - { - "regex": "(source_principal=\\.=(.*?);\\.;)", - "tag_name": "source_principal" - }, - { - "regex": "(source_app=\\.=(.*?);\\.;)", - "tag_name": "source_app" - }, - { - "regex": "(source_version=\\.=(.*?);\\.;)", - "tag_name": "source_version" - }, - { - "regex": "(source_cluster=\\.=(.*?);\\.;)", - "tag_name": "source_cluster" - }, - { - "regex": "(destination_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_namespace" - }, - { - "regex": "(destination_workload=\\.=(.*?);\\.;)", - "tag_name": "destination_workload" - }, - { - "regex": "(destination_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_workload_namespace" - }, - { - "regex": "(destination_principal=\\.=(.*?);\\.;)", - "tag_name": "destination_principal" - }, - { - "regex": "(destination_app=\\.=(.*?);\\.;)", - "tag_name": "destination_app" - }, - { - "regex": "(destination_version=\\.=(.*?);\\.;)", - "tag_name": "destination_version" - }, - { - "regex": "(destination_service=\\.=(.*?);\\.;)", - "tag_name": "destination_service" - }, - { - "regex": "(destination_service_name=\\.=(.*?);\\.;)", - "tag_name": "destination_service_name" - }, - { - "regex": "(destination_service_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_service_namespace" - }, - { - "regex": "(destination_port=\\.=(.*?);\\.;)", - "tag_name": "destination_port" - }, - { - "regex": "(destination_cluster=\\.=(.*?);\\.;)", - "tag_name": "destination_cluster" - }, - { - "regex": "(request_protocol=\\.=(.*?);\\.;)", - "tag_name": "request_protocol" - }, - { - "regex": "(request_operation=\\.=(.*?);\\.;)", - "tag_name": "request_operation" - }, - { - "regex": "(request_host=\\.=(.*?);\\.;)", - "tag_name": "request_host" - }, - { - "regex": "(response_flags=\\.=(.*?);\\.;)", - "tag_name": "response_flags" - }, - { - "regex": "(grpc_response_status=\\.=(.*?);\\.;)", - "tag_name": "grpc_response_status" - }, - { - "regex": "(connection_security_policy=\\.=(.*?);\\.;)", - "tag_name": "connection_security_policy" - }, - { - "regex": "(source_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_service" - }, - { - "regex": "(destination_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_service" - }, - { - "regex": "(source_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_revision" - }, - { - "regex": "(destination_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_revision" - }, - { - "regex": "(cache\\.(.+?)\\.)", - "tag_name": "cache" - }, - { - "regex": "(component\\.(.+?)\\.)", - "tag_name": "component" - }, - { - "regex": "(tag\\.(.+?);\\.)", - "tag_name": "tag" - }, - { - "regex": "(wasm_filter\\.(.+?)\\.)", - "tag_name": "wasm_filter" - }, - { - "tag_name": "authz_enforce_result", - "regex": "rbac(\\.(allowed|denied))" - }, - { - "tag_name": "authz_dry_run_action", - "regex": "(\\.istio_dry_run_(allow|deny)_)" - }, - { - "tag_name": "authz_dry_run_result", - "regex": "(\\.shadow_(allowed|denied))" - } - ], - "stats_matcher": { - "inclusion_list": { - "patterns": [ - { - "prefix": "reporter=" - }, - { - "prefix": "cluster_manager" - }, - { - "prefix": "listener_manager" - }, - { - "prefix": "server" - }, - { - "prefix": "cluster.xds-grpc" - }, - { - "prefix": "wasm" - }, - { - "suffix": "rbac.allowed" - }, - { - "suffix": "rbac.denied" - }, - { - "suffix": "shadow_allowed" - }, - { - "suffix": "shadow_denied" - }, - { - "prefix": "component" - } - ] - } - } - }, - "admin": { - "access_log_path": "/dev/null", - "profile_path": "/var/lib/istio/data/envoy.prof", - "address": { - "socket_address": { - "address": "127.0.0.1", - "port_value": 15000 - } - } - }, - "dynamic_resources": { - "lds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "cds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "ads_config": { - "api_type": "GRPC", - "set_node_on_first_message_only": true, - "transport_api_version": "V3", - "grpc_services": [ - { - "envoy_grpc": { - "cluster_name": "xds-grpc" - } - } - ] - } - }, - "static_resources": { - "clusters": [ - { - "name": "prometheus_stats", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "prometheus_stats", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15000 - } - } - } - }] - }] - } - }, - { - "name": "agent", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "agent", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15020 - } - } - } - }] - }] - } - }, - { - "name": "sds-grpc", - "type": "STATIC", - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - }, - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "sds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "./var/run/secrets/workload-spiffe-uds/socket" - } - } - } - }] - }] - } - }, - { - "name": "xds-grpc", - "type" : "STATIC", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "xds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "/tmp/XDS" - } - } - } - }] - }] - }, - "circuit_breakers": { - "thresholds": [ - { - "priority": "DEFAULT", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - }, - { - "priority": "HIGH", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - } - ] - }, - "upstream_connection_options": { - "tcp_keepalive": { - "keepalive_time": 300 - } - }, - "max_requests_per_connection": 1, - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - } - } - - - ], - "listeners":[ - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15090 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "stats", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/stats/prometheus" - }, - "route": { - "cluster": "prometheus_stats" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - }, - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15021 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "agent", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/healthz/ready" - }, - "route": { - "cluster": "agent" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - } - ] - } - - - , - "cluster_manager": { - "outlier_detection": { - "event_log_path": "/dev/stdout" - } - } - -} diff --git a/pkg/bootstrap/testdata/default.proxycfg b/pkg/bootstrap/testdata/default.proxycfg deleted file mode 100644 index 3c62902a1..000000000 --- a/pkg/bootstrap/testdata/default.proxycfg +++ /dev/null @@ -1,12 +0,0 @@ -config_path: "/etc/istio/proxy" -binary_path: "/usr/local/bin/envoy" -service_cluster: "istio-proxy" -drain_duration: {seconds: 2} -parent_shutdown_duration: {seconds: 3} -discovery_address: "istio-pilot:15010" -proxy_admin_port: 15000 -control_plane_auth_policy: NONE - -# -# This matches the default configuration hardcoded in model.DefaultProxyConfig -# Flags may override this configuration, as specified by the injector configs. diff --git a/pkg/bootstrap/testdata/default_golden.json b/pkg/bootstrap/testdata/default_golden.json deleted file mode 100644 index af17e571a..000000000 --- a/pkg/bootstrap/testdata/default_golden.json +++ /dev/null @@ -1,512 +0,0 @@ -{ - "node": { - "id": "sidecar~1.2.3.4~foo~bar", - "cluster": "istio-proxy", - "locality": { - }, - "metadata": {"ENVOY_PROMETHEUS_PORT":15090,"ENVOY_STATUS_PORT":15021,"INSTANCE_IPS":"10.3.3.3,10.4.4.4,10.5.5.5,10.6.6.6","OUTLIER_LOG_PATH":"/dev/stdout","PILOT_SAN":["spiffe://cluster.local/ns/dubbo-system/sa/istio-pilot-service-account"],"PROXY_CONFIG":{"binaryPath":"/usr/local/bin/envoy","configPath":"/tmp/bootstrap/default","customConfigFile":"envoy_bootstrap.json","discoveryAddress":"istio-pilot:15010","drainDuration":"2s","parentShutdownDuration":"3s","proxyAdminPort":15000,"serviceCluster":"istio-proxy","statusPort":15020}} - }, - "layered_runtime": { - "layers": [ - { - "name": "global config", - "static_layer": {"envoy.deprecated_features:envoy.config.listener.v3.Listener.hidden_envoy_deprecated_use_original_dst":"true","envoy.reloadable_features.http_reject_path_with_fragment":"false","envoy.reloadable_features.no_extension_lookup_by_name":"false","envoy.reloadable_features.require_strict_1xx_and_204_response_headers":"false","overload.global_downstream_max_connections":"2147483647","re2.max_program_size.error_level":"32768"} - }, - { - "name": "admin", - "admin_layer": {} - } - ] - }, - "stats_config": { - "use_all_default_tags": false, - "stats_tags": [ - { - "tag_name": "cluster_name", - "regex": "^cluster\\.((.+?(\\..+?\\.svc\\.cluster\\.local)?)\\.)" - }, - { - "tag_name": "tcp_prefix", - "regex": "^tcp\\.((.*?)\\.)\\w+?$" - }, - { - "regex": "(response_code=\\.=(.+?);\\.;)|_rq(_(\\.d{3}))$", - "tag_name": "response_code" - }, - { - "tag_name": "response_code_class", - "regex": "_rq(_(\\dxx))$" - }, - { - "tag_name": "http_conn_manager_listener_prefix", - "regex": "^listener(?=\\.).*?\\.http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "http_conn_manager_prefix", - "regex": "^http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "listener_address", - "regex": "^listener\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "mongo_prefix", - "regex": "^mongo\\.(.+?)\\.(collection|cmd|cx_|op_|delays_|decoding_)(.*?)$" - }, - { - "regex": "(reporter=\\.=(.*?);\\.;)", - "tag_name": "reporter" - }, - { - "regex": "(source_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_namespace" - }, - { - "regex": "(source_workload=\\.=(.*?);\\.;)", - "tag_name": "source_workload" - }, - { - "regex": "(source_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_workload_namespace" - }, - { - "regex": "(source_principal=\\.=(.*?);\\.;)", - "tag_name": "source_principal" - }, - { - "regex": "(source_app=\\.=(.*?);\\.;)", - "tag_name": "source_app" - }, - { - "regex": "(source_version=\\.=(.*?);\\.;)", - "tag_name": "source_version" - }, - { - "regex": "(source_cluster=\\.=(.*?);\\.;)", - "tag_name": "source_cluster" - }, - { - "regex": "(destination_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_namespace" - }, - { - "regex": "(destination_workload=\\.=(.*?);\\.;)", - "tag_name": "destination_workload" - }, - { - "regex": "(destination_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_workload_namespace" - }, - { - "regex": "(destination_principal=\\.=(.*?);\\.;)", - "tag_name": "destination_principal" - }, - { - "regex": "(destination_app=\\.=(.*?);\\.;)", - "tag_name": "destination_app" - }, - { - "regex": "(destination_version=\\.=(.*?);\\.;)", - "tag_name": "destination_version" - }, - { - "regex": "(destination_service=\\.=(.*?);\\.;)", - "tag_name": "destination_service" - }, - { - "regex": "(destination_service_name=\\.=(.*?);\\.;)", - "tag_name": "destination_service_name" - }, - { - "regex": "(destination_service_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_service_namespace" - }, - { - "regex": "(destination_port=\\.=(.*?);\\.;)", - "tag_name": "destination_port" - }, - { - "regex": "(destination_cluster=\\.=(.*?);\\.;)", - "tag_name": "destination_cluster" - }, - { - "regex": "(request_protocol=\\.=(.*?);\\.;)", - "tag_name": "request_protocol" - }, - { - "regex": "(request_operation=\\.=(.*?);\\.;)", - "tag_name": "request_operation" - }, - { - "regex": "(request_host=\\.=(.*?);\\.;)", - "tag_name": "request_host" - }, - { - "regex": "(response_flags=\\.=(.*?);\\.;)", - "tag_name": "response_flags" - }, - { - "regex": "(grpc_response_status=\\.=(.*?);\\.;)", - "tag_name": "grpc_response_status" - }, - { - "regex": "(connection_security_policy=\\.=(.*?);\\.;)", - "tag_name": "connection_security_policy" - }, - { - "regex": "(source_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_service" - }, - { - "regex": "(destination_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_service" - }, - { - "regex": "(source_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_revision" - }, - { - "regex": "(destination_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_revision" - }, - { - "regex": "(cache\\.(.+?)\\.)", - "tag_name": "cache" - }, - { - "regex": "(component\\.(.+?)\\.)", - "tag_name": "component" - }, - { - "regex": "(tag\\.(.+?);\\.)", - "tag_name": "tag" - }, - { - "regex": "(wasm_filter\\.(.+?)\\.)", - "tag_name": "wasm_filter" - }, - { - "tag_name": "authz_enforce_result", - "regex": "rbac(\\.(allowed|denied))" - }, - { - "tag_name": "authz_dry_run_action", - "regex": "(\\.istio_dry_run_(allow|deny)_)" - }, - { - "tag_name": "authz_dry_run_result", - "regex": "(\\.shadow_(allowed|denied))" - } - ], - "stats_matcher": { - "inclusion_list": { - "patterns": [ - { - "prefix": "reporter=" - }, - { - "prefix": "cluster_manager" - }, - { - "prefix": "listener_manager" - }, - { - "prefix": "server" - }, - { - "prefix": "cluster.xds-grpc" - }, - { - "prefix": "wasm" - }, - { - "suffix": "rbac.allowed" - }, - { - "suffix": "rbac.denied" - }, - { - "suffix": "shadow_allowed" - }, - { - "suffix": "shadow_denied" - }, - { - "prefix": "component" - } - ] - } - } - }, - "admin": { - "access_log_path": "/dev/null", - "profile_path": "/var/lib/istio/data/envoy.prof", - "address": { - "socket_address": { - "address": "127.0.0.1", - "port_value": 15000 - } - } - }, - "dynamic_resources": { - "lds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "cds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "ads_config": { - "api_type": "GRPC", - "set_node_on_first_message_only": true, - "transport_api_version": "V3", - "grpc_services": [ - { - "envoy_grpc": { - "cluster_name": "xds-grpc" - } - } - ] - } - }, - "static_resources": { - "clusters": [ - { - "name": "prometheus_stats", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "prometheus_stats", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15000 - } - } - } - }] - }] - } - }, - { - "name": "agent", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "agent", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15020 - } - } - } - }] - }] - } - }, - { - "name": "sds-grpc", - "type": "STATIC", - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - }, - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "sds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "./var/run/secrets/workload-spiffe-uds/socket" - } - } - } - }] - }] - } - }, - { - "name": "xds-grpc", - "type" : "STATIC", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "xds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "/tmp/XDS" - } - } - } - }] - }] - }, - "circuit_breakers": { - "thresholds": [ - { - "priority": "DEFAULT", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - }, - { - "priority": "HIGH", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - } - ] - }, - "upstream_connection_options": { - "tcp_keepalive": { - "keepalive_time": 300 - } - }, - "max_requests_per_connection": 1, - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - } - } - - - ], - "listeners":[ - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15090 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "stats", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/stats/prometheus" - }, - "route": { - "cluster": "prometheus_stats" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - }, - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15021 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "agent", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/healthz/ready" - }, - "route": { - "cluster": "agent" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - } - ] - } - - - , - "cluster_manager": { - "outlier_detection": { - "event_log_path": "/dev/stdout" - } - } - -} diff --git a/pkg/bootstrap/testdata/metrics_no_statsd.proxycfg b/pkg/bootstrap/testdata/metrics_no_statsd.proxycfg deleted file mode 100644 index ec031e2e5..000000000 --- a/pkg/bootstrap/testdata/metrics_no_statsd.proxycfg +++ /dev/null @@ -1,12 +0,0 @@ -config_path: "/etc/istio/proxy" -binary_path: "/usr/local/bin/envoy" -service_cluster: "istio-proxy" -drain_duration: {seconds: 5} -parent_shutdown_duration: {seconds: 6} -discovery_address: "mypilot:15011" -envoy_metrics_service: {address: "metrics-service:15000", tls_settings: { mode: MUTUAL, client_certificate: "/etc/istio/ms/client.pem", private_key: "/etc/istio/ms/key.pem", ca_certificates: "/etc/istio/ms/ca.pem"}} -proxy_admin_port: 15000 -control_plane_auth_policy: MUTUAL_TLS -stat_name_length: 200 - -# Sets all relevant options to values different than default diff --git a/pkg/bootstrap/testdata/metrics_no_statsd_golden.json b/pkg/bootstrap/testdata/metrics_no_statsd_golden.json deleted file mode 100644 index e08247e19..000000000 --- a/pkg/bootstrap/testdata/metrics_no_statsd_golden.json +++ /dev/null @@ -1,563 +0,0 @@ -{ - "node": { - "id": "sidecar~1.2.3.4~foo~bar", - "cluster": "istio-proxy", - "locality": { - }, - "metadata": {"ENVOY_PROMETHEUS_PORT":15090,"ENVOY_STATUS_PORT":15021,"INSTANCE_IPS":"10.3.3.3,10.4.4.4,10.5.5.5,10.6.6.6","OUTLIER_LOG_PATH":"/dev/stdout","PILOT_SAN":["spiffe://cluster.local/ns/dubbo-system/sa/istio-pilot-service-account"],"PROXY_CONFIG":{"binaryPath":"/usr/local/bin/envoy","configPath":"/tmp/bootstrap/metrics_no_statsd","controlPlaneAuthPolicy":"MUTUAL_TLS","customConfigFile":"envoy_bootstrap.json","discoveryAddress":"mypilot:15011","drainDuration":"5s","envoyMetricsService":{"address":"metrics-service:15000","tlsSettings":{"caCertificates":"/etc/istio/ms/ca.pem","clientCertificate":"/etc/istio/ms/client.pem","mode":"MUTUAL","privateKey":"/etc/istio/ms/key.pem"}},"parentShutdownDuration":"6s","proxyAdminPort":15000,"serviceCluster":"istio-proxy","statNameLength":200,"statusPort":15020}} - }, - "layered_runtime": { - "layers": [ - { - "name": "global config", - "static_layer": {"envoy.deprecated_features:envoy.config.listener.v3.Listener.hidden_envoy_deprecated_use_original_dst":"true","envoy.reloadable_features.http_reject_path_with_fragment":"false","envoy.reloadable_features.no_extension_lookup_by_name":"false","envoy.reloadable_features.require_strict_1xx_and_204_response_headers":"false","overload.global_downstream_max_connections":"2147483647","re2.max_program_size.error_level":"32768"} - }, - { - "name": "admin", - "admin_layer": {} - } - ] - }, - "stats_config": { - "use_all_default_tags": false, - "stats_tags": [ - { - "tag_name": "cluster_name", - "regex": "^cluster\\.((.+?(\\..+?\\.svc\\.cluster\\.local)?)\\.)" - }, - { - "tag_name": "tcp_prefix", - "regex": "^tcp\\.((.*?)\\.)\\w+?$" - }, - { - "regex": "(response_code=\\.=(.+?);\\.;)|_rq(_(\\.d{3}))$", - "tag_name": "response_code" - }, - { - "tag_name": "response_code_class", - "regex": "_rq(_(\\dxx))$" - }, - { - "tag_name": "http_conn_manager_listener_prefix", - "regex": "^listener(?=\\.).*?\\.http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "http_conn_manager_prefix", - "regex": "^http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "listener_address", - "regex": "^listener\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "mongo_prefix", - "regex": "^mongo\\.(.+?)\\.(collection|cmd|cx_|op_|delays_|decoding_)(.*?)$" - }, - { - "regex": "(reporter=\\.=(.*?);\\.;)", - "tag_name": "reporter" - }, - { - "regex": "(source_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_namespace" - }, - { - "regex": "(source_workload=\\.=(.*?);\\.;)", - "tag_name": "source_workload" - }, - { - "regex": "(source_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_workload_namespace" - }, - { - "regex": "(source_principal=\\.=(.*?);\\.;)", - "tag_name": "source_principal" - }, - { - "regex": "(source_app=\\.=(.*?);\\.;)", - "tag_name": "source_app" - }, - { - "regex": "(source_version=\\.=(.*?);\\.;)", - "tag_name": "source_version" - }, - { - "regex": "(source_cluster=\\.=(.*?);\\.;)", - "tag_name": "source_cluster" - }, - { - "regex": "(destination_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_namespace" - }, - { - "regex": "(destination_workload=\\.=(.*?);\\.;)", - "tag_name": "destination_workload" - }, - { - "regex": "(destination_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_workload_namespace" - }, - { - "regex": "(destination_principal=\\.=(.*?);\\.;)", - "tag_name": "destination_principal" - }, - { - "regex": "(destination_app=\\.=(.*?);\\.;)", - "tag_name": "destination_app" - }, - { - "regex": "(destination_version=\\.=(.*?);\\.;)", - "tag_name": "destination_version" - }, - { - "regex": "(destination_service=\\.=(.*?);\\.;)", - "tag_name": "destination_service" - }, - { - "regex": "(destination_service_name=\\.=(.*?);\\.;)", - "tag_name": "destination_service_name" - }, - { - "regex": "(destination_service_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_service_namespace" - }, - { - "regex": "(destination_port=\\.=(.*?);\\.;)", - "tag_name": "destination_port" - }, - { - "regex": "(destination_cluster=\\.=(.*?);\\.;)", - "tag_name": "destination_cluster" - }, - { - "regex": "(request_protocol=\\.=(.*?);\\.;)", - "tag_name": "request_protocol" - }, - { - "regex": "(request_operation=\\.=(.*?);\\.;)", - "tag_name": "request_operation" - }, - { - "regex": "(request_host=\\.=(.*?);\\.;)", - "tag_name": "request_host" - }, - { - "regex": "(response_flags=\\.=(.*?);\\.;)", - "tag_name": "response_flags" - }, - { - "regex": "(grpc_response_status=\\.=(.*?);\\.;)", - "tag_name": "grpc_response_status" - }, - { - "regex": "(connection_security_policy=\\.=(.*?);\\.;)", - "tag_name": "connection_security_policy" - }, - { - "regex": "(source_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_service" - }, - { - "regex": "(destination_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_service" - }, - { - "regex": "(source_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_revision" - }, - { - "regex": "(destination_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_revision" - }, - { - "regex": "(cache\\.(.+?)\\.)", - "tag_name": "cache" - }, - { - "regex": "(component\\.(.+?)\\.)", - "tag_name": "component" - }, - { - "regex": "(tag\\.(.+?);\\.)", - "tag_name": "tag" - }, - { - "regex": "(wasm_filter\\.(.+?)\\.)", - "tag_name": "wasm_filter" - }, - { - "tag_name": "authz_enforce_result", - "regex": "rbac(\\.(allowed|denied))" - }, - { - "tag_name": "authz_dry_run_action", - "regex": "(\\.istio_dry_run_(allow|deny)_)" - }, - { - "tag_name": "authz_dry_run_result", - "regex": "(\\.shadow_(allowed|denied))" - } - ], - "stats_matcher": { - "inclusion_list": { - "patterns": [ - { - "prefix": "reporter=" - }, - { - "prefix": "cluster_manager" - }, - { - "prefix": "listener_manager" - }, - { - "prefix": "server" - }, - { - "prefix": "cluster.xds-grpc" - }, - { - "prefix": "wasm" - }, - { - "suffix": "rbac.allowed" - }, - { - "suffix": "rbac.denied" - }, - { - "suffix": "shadow_allowed" - }, - { - "suffix": "shadow_denied" - }, - { - "prefix": "component" - } - ] - } - } - }, - "admin": { - "access_log_path": "/dev/null", - "profile_path": "/var/lib/istio/data/envoy.prof", - "address": { - "socket_address": { - "address": "127.0.0.1", - "port_value": 15000 - } - } - }, - "dynamic_resources": { - "lds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "cds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "ads_config": { - "api_type": "GRPC", - "set_node_on_first_message_only": true, - "transport_api_version": "V3", - "grpc_services": [ - { - "envoy_grpc": { - "cluster_name": "xds-grpc" - } - } - ] - } - }, - "static_resources": { - "clusters": [ - { - "name": "prometheus_stats", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "prometheus_stats", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15000 - } - } - } - }] - }] - } - }, - { - "name": "agent", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "agent", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15020 - } - } - } - }] - }] - } - }, - { - "name": "sds-grpc", - "type": "STATIC", - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - }, - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "sds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "./var/run/secrets/workload-spiffe-uds/socket" - } - } - } - }] - }] - } - }, - { - "name": "xds-grpc", - "type" : "STATIC", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "xds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "/tmp/XDS" - } - } - } - }] - }] - }, - "circuit_breakers": { - "thresholds": [ - { - "priority": "DEFAULT", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - }, - { - "priority": "HIGH", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - } - ] - }, - "upstream_connection_options": { - "tcp_keepalive": { - "keepalive_time": 300 - } - }, - "max_requests_per_connection": 1, - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - } - } - - , - { - "name": "envoy_metrics_service", - "type": "STRICT_DNS", - "transport_socket": {"name":"envoy.transport_sockets.tls","typed_config":{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext","common_tls_context":{"alpn_protocols":["h2"],"combined_validation_context":{"default_validation_context":{},"validation_context_sds_secret_config":{"name":"file-root:/etc/istio/ms/ca.pem","sds_config":{"api_config_source":{"api_type":"GRPC","grpc_services":[{"envoy_grpc":{"cluster_name":"sds-grpc"}}],"set_node_on_first_message_only":true,"transport_api_version":"V3"},"resource_api_version":"V3"}}},"tls_certificate_sds_secret_configs":[{"name":"file-cert:/etc/istio/ms/client.pem~/etc/istio/ms/key.pem","sds_config":{"api_config_source":{"api_type":"GRPC","grpc_services":[{"envoy_grpc":{"cluster_name":"sds-grpc"}}],"set_node_on_first_message_only":true,"transport_api_version":"V3"},"resource_api_version":"V3"}}]}}}, - "respect_dns_ttl": true, - "dns_lookup_family": "V4_ONLY", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - }, - "load_assignment": { - "cluster_name": "envoy_metrics_service", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": {"address": "metrics-service", "port_value": 15000} - } - } - }] - }] - } - } - - - ], - "listeners":[ - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15090 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "stats", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/stats/prometheus" - }, - "route": { - "cluster": "prometheus_stats" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - }, - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15021 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "agent", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/healthz/ready" - }, - "route": { - "cluster": "agent" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - } - ] - } - - , - "stats_sinks": [ - - { - "name": "envoy.stat_sinks.metrics_service", - "typed_config": { - "@type": "type.googleapis.com/envoy.config.metrics.v3.MetricsServiceConfig", - "transport_api_version": "V3", - "grpc_service": { - "envoy_grpc": { - "cluster_name": "envoy_metrics_service" - } - } - } - } - - - - ] - - - , - "cluster_manager": { - "outlier_detection": { - "event_log_path": "/dev/stdout" - } - } - -} diff --git a/pkg/bootstrap/testdata/running.proxycfg b/pkg/bootstrap/testdata/running.proxycfg deleted file mode 100644 index df9d5fd36..000000000 --- a/pkg/bootstrap/testdata/running.proxycfg +++ /dev/null @@ -1,13 +0,0 @@ -config_path: "/etc/istio/proxy" -binary_path: "/usr/local/bin/envoy" -service_cluster: "istio-proxy" -drain_duration: {seconds: 5} -parent_shutdown_duration: {seconds: 6} -discovery_address: "mypilot:1001" -statsd_udp_address: "10.1.1.1:9125" -proxy_admin_port: 15005 -control_plane_auth_policy: MUTUAL_TLS -stat_name_length: 200 -tracing: { zipkin: { address: "localhost:6000" } } - -# Sets all relevant options to values different than default diff --git a/pkg/bootstrap/testdata/running_golden.json b/pkg/bootstrap/testdata/running_golden.json deleted file mode 100644 index 5b7f14d06..000000000 --- a/pkg/bootstrap/testdata/running_golden.json +++ /dev/null @@ -1,571 +0,0 @@ -{ - "node": { - "id": "sidecar~1.2.3.4~foo~bar", - "cluster": "test.test", - "locality": { - "region": "regionA" - , - "zone": "zoneB" - , - "sub_zone": "sub_zoneC" - }, - "metadata": {"ANNOTATIONS":{"istio.io/insecurepath":"{\"paths\":[\"/metrics\",\"/live\"]}"},"ENVOY_PROMETHEUS_PORT":15090,"ENVOY_STATUS_PORT":15021,"INSTANCE_IPS":"10.3.3.3,10.4.4.4,10.5.5.5,10.6.6.6","INTERCEPTION_MODE":"REDIRECT","ISTIO_PROXY_SHA":"istio-proxy:sha","ISTIO_VERSION":"release-3.1","LABELS":{"app":"test","istio-locality":"regionA.zoneB.sub_zoneC","version":"v1alpha1"},"NAME":"svc-0-0-0-6944fb884d-4pgx8","NAMESPACE":"test","OUTLIER_LOG_PATH":"/dev/stdout","PILOT_SAN":["spiffe://cluster.local/ns/dubbo-system/sa/istio-pilot-service-account"],"POD_NAME":"svc-0-0-0-6944fb884d-4pgx8","PROXY_CONFIG":{"binaryPath":"/usr/local/bin/envoy","configPath":"/tmp/bootstrap/running","controlPlaneAuthPolicy":"MUTUAL_TLS","customConfigFile":"envoy_bootstrap.json","discoveryAddress":"mypilot:1001","drainDuration":"5s","parentShutdownDuration":"6s","proxyAdminPort":15005,"serviceCluster":"istio-proxy","statNameLength":200,"statsdUdpAddress":"10.1.1.1:9125","statusPort":15020,"tracing":{"zipkin":{"address":"localhost:6000"}}},"app":"test","istio-locality":"regionA.zoneB.sub_zoneC","istio.io/insecurepath":"{\"paths\":[\"/metrics\",\"/live\"]}","version":"v1alpha1"} - }, - "layered_runtime": { - "layers": [ - { - "name": "global config", - "static_layer": {"envoy.deprecated_features:envoy.config.listener.v3.Listener.hidden_envoy_deprecated_use_original_dst":"true","envoy.reloadable_features.http_reject_path_with_fragment":"false","envoy.reloadable_features.no_extension_lookup_by_name":"false","envoy.reloadable_features.require_strict_1xx_and_204_response_headers":"false","overload.global_downstream_max_connections":"2147483647","re2.max_program_size.error_level":"32768"} - }, - { - "name": "admin", - "admin_layer": {} - } - ] - }, - "stats_config": { - "use_all_default_tags": false, - "stats_tags": [ - { - "tag_name": "cluster_name", - "regex": "^cluster\\.((.+?(\\..+?\\.svc\\.cluster\\.local)?)\\.)" - }, - { - "tag_name": "tcp_prefix", - "regex": "^tcp\\.((.*?)\\.)\\w+?$" - }, - { - "regex": "(response_code=\\.=(.+?);\\.;)|_rq(_(\\.d{3}))$", - "tag_name": "response_code" - }, - { - "tag_name": "response_code_class", - "regex": "_rq(_(\\dxx))$" - }, - { - "tag_name": "http_conn_manager_listener_prefix", - "regex": "^listener(?=\\.).*?\\.http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "http_conn_manager_prefix", - "regex": "^http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "listener_address", - "regex": "^listener\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "mongo_prefix", - "regex": "^mongo\\.(.+?)\\.(collection|cmd|cx_|op_|delays_|decoding_)(.*?)$" - }, - { - "regex": "(reporter=\\.=(.*?);\\.;)", - "tag_name": "reporter" - }, - { - "regex": "(source_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_namespace" - }, - { - "regex": "(source_workload=\\.=(.*?);\\.;)", - "tag_name": "source_workload" - }, - { - "regex": "(source_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_workload_namespace" - }, - { - "regex": "(source_principal=\\.=(.*?);\\.;)", - "tag_name": "source_principal" - }, - { - "regex": "(source_app=\\.=(.*?);\\.;)", - "tag_name": "source_app" - }, - { - "regex": "(source_version=\\.=(.*?);\\.;)", - "tag_name": "source_version" - }, - { - "regex": "(source_cluster=\\.=(.*?);\\.;)", - "tag_name": "source_cluster" - }, - { - "regex": "(destination_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_namespace" - }, - { - "regex": "(destination_workload=\\.=(.*?);\\.;)", - "tag_name": "destination_workload" - }, - { - "regex": "(destination_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_workload_namespace" - }, - { - "regex": "(destination_principal=\\.=(.*?);\\.;)", - "tag_name": "destination_principal" - }, - { - "regex": "(destination_app=\\.=(.*?);\\.;)", - "tag_name": "destination_app" - }, - { - "regex": "(destination_version=\\.=(.*?);\\.;)", - "tag_name": "destination_version" - }, - { - "regex": "(destination_service=\\.=(.*?);\\.;)", - "tag_name": "destination_service" - }, - { - "regex": "(destination_service_name=\\.=(.*?);\\.;)", - "tag_name": "destination_service_name" - }, - { - "regex": "(destination_service_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_service_namespace" - }, - { - "regex": "(destination_port=\\.=(.*?);\\.;)", - "tag_name": "destination_port" - }, - { - "regex": "(destination_cluster=\\.=(.*?);\\.;)", - "tag_name": "destination_cluster" - }, - { - "regex": "(request_protocol=\\.=(.*?);\\.;)", - "tag_name": "request_protocol" - }, - { - "regex": "(request_operation=\\.=(.*?);\\.;)", - "tag_name": "request_operation" - }, - { - "regex": "(request_host=\\.=(.*?);\\.;)", - "tag_name": "request_host" - }, - { - "regex": "(response_flags=\\.=(.*?);\\.;)", - "tag_name": "response_flags" - }, - { - "regex": "(grpc_response_status=\\.=(.*?);\\.;)", - "tag_name": "grpc_response_status" - }, - { - "regex": "(connection_security_policy=\\.=(.*?);\\.;)", - "tag_name": "connection_security_policy" - }, - { - "regex": "(source_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_service" - }, - { - "regex": "(destination_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_service" - }, - { - "regex": "(source_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_revision" - }, - { - "regex": "(destination_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_revision" - }, - { - "regex": "(cache\\.(.+?)\\.)", - "tag_name": "cache" - }, - { - "regex": "(component\\.(.+?)\\.)", - "tag_name": "component" - }, - { - "regex": "(tag\\.(.+?);\\.)", - "tag_name": "tag" - }, - { - "regex": "(wasm_filter\\.(.+?)\\.)", - "tag_name": "wasm_filter" - }, - { - "tag_name": "authz_enforce_result", - "regex": "rbac(\\.(allowed|denied))" - }, - { - "tag_name": "authz_dry_run_action", - "regex": "(\\.istio_dry_run_(allow|deny)_)" - }, - { - "tag_name": "authz_dry_run_result", - "regex": "(\\.shadow_(allowed|denied))" - } - ], - "stats_matcher": { - "inclusion_list": { - "patterns": [ - { - "prefix": "reporter=" - }, - { - "prefix": "cluster_manager" - }, - { - "prefix": "listener_manager" - }, - { - "prefix": "server" - }, - { - "prefix": "cluster.xds-grpc" - }, - { - "prefix": "wasm" - }, - { - "suffix": "rbac.allowed" - }, - { - "suffix": "rbac.denied" - }, - { - "suffix": "shadow_allowed" - }, - { - "suffix": "shadow_denied" - }, - { - "prefix": "component" - } - ] - } - } - }, - "admin": { - "access_log_path": "/dev/null", - "profile_path": "/var/lib/istio/data/envoy.prof", - "address": { - "socket_address": { - "address": "127.0.0.1", - "port_value": 15005 - } - } - }, - "dynamic_resources": { - "lds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "cds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "ads_config": { - "api_type": "GRPC", - "set_node_on_first_message_only": true, - "transport_api_version": "V3", - "grpc_services": [ - { - "envoy_grpc": { - "cluster_name": "xds-grpc" - } - } - ] - } - }, - "static_resources": { - "clusters": [ - { - "name": "prometheus_stats", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "prometheus_stats", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15005 - } - } - } - }] - }] - } - }, - { - "name": "agent", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "agent", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15020 - } - } - } - }] - }] - } - }, - { - "name": "sds-grpc", - "type": "STATIC", - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - }, - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "sds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "./var/run/secrets/workload-spiffe-uds/socket" - } - } - } - }] - }] - } - }, - { - "name": "xds-grpc", - "type" : "STATIC", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "xds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "/tmp/XDS" - } - } - } - }] - }] - }, - "circuit_breakers": { - "thresholds": [ - { - "priority": "DEFAULT", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - }, - { - "priority": "HIGH", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - } - ] - }, - "upstream_connection_options": { - "tcp_keepalive": { - "keepalive_time": 300 - } - }, - "max_requests_per_connection": 1, - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - } - } - - , - { - "name": "zipkin", - "type": "STRICT_DNS", - "respect_dns_ttl": true, - "dns_lookup_family": "V4_ONLY", - "dns_refresh_rate": "30s", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "zipkin", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": {"address": "localhost", "port_value": 6000} - } - } - }] - }] - } - } - - - ], - "listeners":[ - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15090 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "stats", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/stats/prometheus" - }, - "route": { - "cluster": "prometheus_stats" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - }, - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15021 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "agent", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/healthz/ready" - }, - "route": { - "cluster": "agent" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - } - ] - } - , - "tracing": { - "http": { - "name": "envoy.tracers.zipkin", - "typed_config": { - "@type": "type.googleapis.com/envoy.config.trace.v3.ZipkinConfig", - "collector_cluster": "zipkin", - "collector_endpoint": "/api/v2/spans", - "collector_endpoint_version": "HTTP_JSON", - "trace_id_128bit": true, - "shared_span_context": false - } - } - } - - , - "stats_sinks": [ - - - - { - "name": "envoy.stat_sinks.statsd", - "typed_config": { - "@type": "type.googleapis.com/envoy.config.metrics.v3.StatsdSink", - "address": { - "socket_address": {"address": "10.1.1.1", "port_value": 9125} - } - } - } - - ] - - - , - "cluster_manager": { - "outlier_detection": { - "event_log_path": "/dev/stdout" - } - } - -} diff --git a/pkg/bootstrap/testdata/runningsds.proxycfg b/pkg/bootstrap/testdata/runningsds.proxycfg deleted file mode 100644 index df9d5fd36..000000000 --- a/pkg/bootstrap/testdata/runningsds.proxycfg +++ /dev/null @@ -1,13 +0,0 @@ -config_path: "/etc/istio/proxy" -binary_path: "/usr/local/bin/envoy" -service_cluster: "istio-proxy" -drain_duration: {seconds: 5} -parent_shutdown_duration: {seconds: 6} -discovery_address: "mypilot:1001" -statsd_udp_address: "10.1.1.1:9125" -proxy_admin_port: 15005 -control_plane_auth_policy: MUTUAL_TLS -stat_name_length: 200 -tracing: { zipkin: { address: "localhost:6000" } } - -# Sets all relevant options to values different than default diff --git a/pkg/bootstrap/testdata/runningsds_golden.json b/pkg/bootstrap/testdata/runningsds_golden.json deleted file mode 100644 index 4a8827816..000000000 --- a/pkg/bootstrap/testdata/runningsds_golden.json +++ /dev/null @@ -1,571 +0,0 @@ -{ - "node": { - "id": "sidecar~1.2.3.4~foo~bar", - "cluster": "test.test", - "locality": { - "region": "regionA" - , - "zone": "zoneB" - , - "sub_zone": "sub_zoneC" - }, - "metadata": {"ANNOTATIONS":{"istio.io/insecurepath":"{\"paths\":[\"/metrics\",\"/live\"]}"},"ENVOY_PROMETHEUS_PORT":15090,"ENVOY_STATUS_PORT":15021,"INSTANCE_IPS":"10.3.3.3,10.4.4.4,10.5.5.5,10.6.6.6","INTERCEPTION_MODE":"REDIRECT","ISTIO_PROXY_SHA":"istio-proxy:sha","ISTIO_VERSION":"release-3.1","LABELS":{"app":"test","istio-locality":"regionA.zoneB.sub_zoneC","version":"v1alpha1"},"NAME":"svc-0-0-0-6944fb884d-4pgx8","NAMESPACE":"test","OUTLIER_LOG_PATH":"/dev/stdout","PILOT_SAN":["spiffe://cluster.local/ns/dubbo-system/sa/istio-pilot-service-account"],"POD_NAME":"svc-0-0-0-6944fb884d-4pgx8","PROXY_CONFIG":{"binaryPath":"/usr/local/bin/envoy","configPath":"/tmp/bootstrap/runningsds","controlPlaneAuthPolicy":"MUTUAL_TLS","customConfigFile":"envoy_bootstrap.json","discoveryAddress":"mypilot:1001","drainDuration":"5s","parentShutdownDuration":"6s","proxyAdminPort":15005,"serviceCluster":"istio-proxy","statNameLength":200,"statsdUdpAddress":"10.1.1.1:9125","statusPort":15020,"tracing":{"zipkin":{"address":"localhost:6000"}}},"app":"test","istio-locality":"regionA.zoneB.sub_zoneC","istio.io/insecurepath":"{\"paths\":[\"/metrics\",\"/live\"]}","version":"v1alpha1"} - }, - "layered_runtime": { - "layers": [ - { - "name": "global config", - "static_layer": {"envoy.deprecated_features:envoy.config.listener.v3.Listener.hidden_envoy_deprecated_use_original_dst":"true","envoy.reloadable_features.http_reject_path_with_fragment":"false","envoy.reloadable_features.no_extension_lookup_by_name":"false","envoy.reloadable_features.require_strict_1xx_and_204_response_headers":"false","overload.global_downstream_max_connections":"2147483647","re2.max_program_size.error_level":"32768"} - }, - { - "name": "admin", - "admin_layer": {} - } - ] - }, - "stats_config": { - "use_all_default_tags": false, - "stats_tags": [ - { - "tag_name": "cluster_name", - "regex": "^cluster\\.((.+?(\\..+?\\.svc\\.cluster\\.local)?)\\.)" - }, - { - "tag_name": "tcp_prefix", - "regex": "^tcp\\.((.*?)\\.)\\w+?$" - }, - { - "regex": "(response_code=\\.=(.+?);\\.;)|_rq(_(\\.d{3}))$", - "tag_name": "response_code" - }, - { - "tag_name": "response_code_class", - "regex": "_rq(_(\\dxx))$" - }, - { - "tag_name": "http_conn_manager_listener_prefix", - "regex": "^listener(?=\\.).*?\\.http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "http_conn_manager_prefix", - "regex": "^http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "listener_address", - "regex": "^listener\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "mongo_prefix", - "regex": "^mongo\\.(.+?)\\.(collection|cmd|cx_|op_|delays_|decoding_)(.*?)$" - }, - { - "regex": "(reporter=\\.=(.*?);\\.;)", - "tag_name": "reporter" - }, - { - "regex": "(source_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_namespace" - }, - { - "regex": "(source_workload=\\.=(.*?);\\.;)", - "tag_name": "source_workload" - }, - { - "regex": "(source_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_workload_namespace" - }, - { - "regex": "(source_principal=\\.=(.*?);\\.;)", - "tag_name": "source_principal" - }, - { - "regex": "(source_app=\\.=(.*?);\\.;)", - "tag_name": "source_app" - }, - { - "regex": "(source_version=\\.=(.*?);\\.;)", - "tag_name": "source_version" - }, - { - "regex": "(source_cluster=\\.=(.*?);\\.;)", - "tag_name": "source_cluster" - }, - { - "regex": "(destination_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_namespace" - }, - { - "regex": "(destination_workload=\\.=(.*?);\\.;)", - "tag_name": "destination_workload" - }, - { - "regex": "(destination_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_workload_namespace" - }, - { - "regex": "(destination_principal=\\.=(.*?);\\.;)", - "tag_name": "destination_principal" - }, - { - "regex": "(destination_app=\\.=(.*?);\\.;)", - "tag_name": "destination_app" - }, - { - "regex": "(destination_version=\\.=(.*?);\\.;)", - "tag_name": "destination_version" - }, - { - "regex": "(destination_service=\\.=(.*?);\\.;)", - "tag_name": "destination_service" - }, - { - "regex": "(destination_service_name=\\.=(.*?);\\.;)", - "tag_name": "destination_service_name" - }, - { - "regex": "(destination_service_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_service_namespace" - }, - { - "regex": "(destination_port=\\.=(.*?);\\.;)", - "tag_name": "destination_port" - }, - { - "regex": "(destination_cluster=\\.=(.*?);\\.;)", - "tag_name": "destination_cluster" - }, - { - "regex": "(request_protocol=\\.=(.*?);\\.;)", - "tag_name": "request_protocol" - }, - { - "regex": "(request_operation=\\.=(.*?);\\.;)", - "tag_name": "request_operation" - }, - { - "regex": "(request_host=\\.=(.*?);\\.;)", - "tag_name": "request_host" - }, - { - "regex": "(response_flags=\\.=(.*?);\\.;)", - "tag_name": "response_flags" - }, - { - "regex": "(grpc_response_status=\\.=(.*?);\\.;)", - "tag_name": "grpc_response_status" - }, - { - "regex": "(connection_security_policy=\\.=(.*?);\\.;)", - "tag_name": "connection_security_policy" - }, - { - "regex": "(source_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_service" - }, - { - "regex": "(destination_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_service" - }, - { - "regex": "(source_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_revision" - }, - { - "regex": "(destination_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_revision" - }, - { - "regex": "(cache\\.(.+?)\\.)", - "tag_name": "cache" - }, - { - "regex": "(component\\.(.+?)\\.)", - "tag_name": "component" - }, - { - "regex": "(tag\\.(.+?);\\.)", - "tag_name": "tag" - }, - { - "regex": "(wasm_filter\\.(.+?)\\.)", - "tag_name": "wasm_filter" - }, - { - "tag_name": "authz_enforce_result", - "regex": "rbac(\\.(allowed|denied))" - }, - { - "tag_name": "authz_dry_run_action", - "regex": "(\\.istio_dry_run_(allow|deny)_)" - }, - { - "tag_name": "authz_dry_run_result", - "regex": "(\\.shadow_(allowed|denied))" - } - ], - "stats_matcher": { - "inclusion_list": { - "patterns": [ - { - "prefix": "reporter=" - }, - { - "prefix": "cluster_manager" - }, - { - "prefix": "listener_manager" - }, - { - "prefix": "server" - }, - { - "prefix": "cluster.xds-grpc" - }, - { - "prefix": "wasm" - }, - { - "suffix": "rbac.allowed" - }, - { - "suffix": "rbac.denied" - }, - { - "suffix": "shadow_allowed" - }, - { - "suffix": "shadow_denied" - }, - { - "prefix": "component" - } - ] - } - } - }, - "admin": { - "access_log_path": "/dev/null", - "profile_path": "/var/lib/istio/data/envoy.prof", - "address": { - "socket_address": { - "address": "127.0.0.1", - "port_value": 15005 - } - } - }, - "dynamic_resources": { - "lds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "cds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "ads_config": { - "api_type": "GRPC", - "set_node_on_first_message_only": true, - "transport_api_version": "V3", - "grpc_services": [ - { - "envoy_grpc": { - "cluster_name": "xds-grpc" - } - } - ] - } - }, - "static_resources": { - "clusters": [ - { - "name": "prometheus_stats", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "prometheus_stats", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15005 - } - } - } - }] - }] - } - }, - { - "name": "agent", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "agent", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15020 - } - } - } - }] - }] - } - }, - { - "name": "sds-grpc", - "type": "STATIC", - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - }, - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "sds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "./var/run/secrets/workload-spiffe-uds/socket" - } - } - } - }] - }] - } - }, - { - "name": "xds-grpc", - "type" : "STATIC", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "xds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "/tmp/XDS" - } - } - } - }] - }] - }, - "circuit_breakers": { - "thresholds": [ - { - "priority": "DEFAULT", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - }, - { - "priority": "HIGH", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - } - ] - }, - "upstream_connection_options": { - "tcp_keepalive": { - "keepalive_time": 300 - } - }, - "max_requests_per_connection": 1, - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - } - } - - , - { - "name": "zipkin", - "type": "STRICT_DNS", - "respect_dns_ttl": true, - "dns_lookup_family": "V4_ONLY", - "dns_refresh_rate": "30s", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "zipkin", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": {"address": "localhost", "port_value": 6000} - } - } - }] - }] - } - } - - - ], - "listeners":[ - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15090 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "stats", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/stats/prometheus" - }, - "route": { - "cluster": "prometheus_stats" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - }, - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15021 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "agent", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/healthz/ready" - }, - "route": { - "cluster": "agent" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - } - ] - } - , - "tracing": { - "http": { - "name": "envoy.tracers.zipkin", - "typed_config": { - "@type": "type.googleapis.com/envoy.config.trace.v3.ZipkinConfig", - "collector_cluster": "zipkin", - "collector_endpoint": "/api/v2/spans", - "collector_endpoint_version": "HTTP_JSON", - "trace_id_128bit": true, - "shared_span_context": false - } - } - } - - , - "stats_sinks": [ - - - - { - "name": "envoy.stat_sinks.statsd", - "typed_config": { - "@type": "type.googleapis.com/envoy.config.metrics.v3.StatsdSink", - "address": { - "socket_address": {"address": "10.1.1.1", "port_value": 9125} - } - } - } - - ] - - - , - "cluster_manager": { - "outlier_detection": { - "event_log_path": "/dev/stdout" - } - } - -} diff --git a/pkg/bootstrap/testdata/stats_inclusion.proxycfg b/pkg/bootstrap/testdata/stats_inclusion.proxycfg deleted file mode 100644 index b8baba222..000000000 --- a/pkg/bootstrap/testdata/stats_inclusion.proxycfg +++ /dev/null @@ -1,13 +0,0 @@ -config_path: "/etc/istio/proxy" -binary_path: "/usr/local/bin/envoy" -service_cluster: "istio-proxy" -drain_duration: {seconds: 2} -parent_shutdown_duration: {seconds: 3} -discovery_address: "istio-pilot:15010" -proxy_admin_port: 15000 -control_plane_auth_policy: NONE -extra_stat_tags: ["dlp_success"] - -# -# This matches the default configuration hardcoded in model.DefaultProxyConfig -# Flags may override this configuration, as specified by the injector configs. diff --git a/pkg/bootstrap/testdata/stats_inclusion_golden.json b/pkg/bootstrap/testdata/stats_inclusion_golden.json deleted file mode 100644 index 349e13d59..000000000 --- a/pkg/bootstrap/testdata/stats_inclusion_golden.json +++ /dev/null @@ -1,605 +0,0 @@ -{ - "node": { - "id": "sidecar~1.2.3.4~foo~bar", - "cluster": "istio-proxy", - "locality": { - }, - "metadata": {"ANNOTATIONS":{"sidecar.istio.io/extraStatTags":"dlp_status,dlp_error","sidecar.istio.io/statsInclusionPrefixes":"prefix1,prefix2,http.{pod_ip}_","sidecar.istio.io/statsInclusionRegexps":"http.[0-9]*\\.[0-9]*\\.[0-9]*\\.[0-9]*_8080.downstream_rq_time","sidecar.istio.io/statsInclusionSuffixes":"suffix1,suffix2,upstream_rq_1xx,upstream_rq_2xx,upstream_rq_3xx,upstream_rq_4xx,upstream_rq_5xx,upstream_rq_time,upstream_cx_tx_bytes_total,upstream_cx_rx_bytes_total,upstream_cx_total,downstream_rq_1xx,downstream_rq_2xx,downstream_rq_3xx,downstream_rq_4xx,downstream_rq_5xx,downstream_rq_time,downstream_cx_tx_bytes_total,downstream_cx_rx_bytes_total,downstream_cx_total"},"ENVOY_PROMETHEUS_PORT":15090,"ENVOY_STATUS_PORT":15021,"INSTANCE_IPS":"10.3.3.3,10.4.4.4,10.5.5.5,10.6.6.6","OUTLIER_LOG_PATH":"/dev/stdout","PILOT_SAN":["spiffe://cluster.local/ns/dubbo-system/sa/istio-pilot-service-account"],"PROXY_CONFIG":{"binaryPath":"/usr/local/bin/envoy","configPath":"/tmp/bootstrap/stats_inclusion","customConfigFile":"envoy_bootstrap.json","discoveryAddress":"istio-pilot:15010","drainDuration":"2s","extraStatTags":["dlp_success"],"parentShutdownDuration":"3s","proxyAdminPort":15000,"serviceCluster":"istio-proxy","statusPort":15020},"sidecar.istio.io/extraStatTags":"dlp_status,dlp_error","sidecar.istio.io/statsInclusionPrefixes":"prefix1,prefix2,http.{pod_ip}_","sidecar.istio.io/statsInclusionRegexps":"http.[0-9]*\\.[0-9]*\\.[0-9]*\\.[0-9]*_8080.downstream_rq_time","sidecar.istio.io/statsInclusionSuffixes":"suffix1,suffix2,upstream_rq_1xx,upstream_rq_2xx,upstream_rq_3xx,upstream_rq_4xx,upstream_rq_5xx,upstream_rq_time,upstream_cx_tx_bytes_total,upstream_cx_rx_bytes_total,upstream_cx_total,downstream_rq_1xx,downstream_rq_2xx,downstream_rq_3xx,downstream_rq_4xx,downstream_rq_5xx,downstream_rq_time,downstream_cx_tx_bytes_total,downstream_cx_rx_bytes_total,downstream_cx_total"} - }, - "layered_runtime": { - "layers": [ - { - "name": "global config", - "static_layer": {"envoy.deprecated_features:envoy.config.listener.v3.Listener.hidden_envoy_deprecated_use_original_dst":"true","envoy.reloadable_features.http_reject_path_with_fragment":"false","envoy.reloadable_features.no_extension_lookup_by_name":"false","envoy.reloadable_features.require_strict_1xx_and_204_response_headers":"false","overload.global_downstream_max_connections":"2147483647","re2.max_program_size.error_level":"32768"} - }, - { - "name": "admin", - "admin_layer": {} - } - ] - }, - "stats_config": { - "use_all_default_tags": false, - "stats_tags": [ - { - "tag_name": "cluster_name", - "regex": "^cluster\\.((.+?(\\..+?\\.svc\\.cluster\\.local)?)\\.)" - }, - { - "tag_name": "tcp_prefix", - "regex": "^tcp\\.((.*?)\\.)\\w+?$" - }, - { - "regex": "(response_code=\\.=(.+?);\\.;)|_rq(_(\\.d{3}))$", - "tag_name": "response_code" - }, - { - "tag_name": "response_code_class", - "regex": "_rq(_(\\dxx))$" - }, - { - "tag_name": "http_conn_manager_listener_prefix", - "regex": "^listener(?=\\.).*?\\.http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "http_conn_manager_prefix", - "regex": "^http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "listener_address", - "regex": "^listener\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "mongo_prefix", - "regex": "^mongo\\.(.+?)\\.(collection|cmd|cx_|op_|delays_|decoding_)(.*?)$" - }, - { - "regex": "(reporter=\\.=(.*?);\\.;)", - "tag_name": "reporter" - }, - { - "regex": "(source_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_namespace" - }, - { - "regex": "(source_workload=\\.=(.*?);\\.;)", - "tag_name": "source_workload" - }, - { - "regex": "(source_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_workload_namespace" - }, - { - "regex": "(source_principal=\\.=(.*?);\\.;)", - "tag_name": "source_principal" - }, - { - "regex": "(source_app=\\.=(.*?);\\.;)", - "tag_name": "source_app" - }, - { - "regex": "(source_version=\\.=(.*?);\\.;)", - "tag_name": "source_version" - }, - { - "regex": "(source_cluster=\\.=(.*?);\\.;)", - "tag_name": "source_cluster" - }, - { - "regex": "(destination_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_namespace" - }, - { - "regex": "(destination_workload=\\.=(.*?);\\.;)", - "tag_name": "destination_workload" - }, - { - "regex": "(destination_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_workload_namespace" - }, - { - "regex": "(destination_principal=\\.=(.*?);\\.;)", - "tag_name": "destination_principal" - }, - { - "regex": "(destination_app=\\.=(.*?);\\.;)", - "tag_name": "destination_app" - }, - { - "regex": "(destination_version=\\.=(.*?);\\.;)", - "tag_name": "destination_version" - }, - { - "regex": "(destination_service=\\.=(.*?);\\.;)", - "tag_name": "destination_service" - }, - { - "regex": "(destination_service_name=\\.=(.*?);\\.;)", - "tag_name": "destination_service_name" - }, - { - "regex": "(destination_service_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_service_namespace" - }, - { - "regex": "(destination_port=\\.=(.*?);\\.;)", - "tag_name": "destination_port" - }, - { - "regex": "(destination_cluster=\\.=(.*?);\\.;)", - "tag_name": "destination_cluster" - }, - { - "regex": "(request_protocol=\\.=(.*?);\\.;)", - "tag_name": "request_protocol" - }, - { - "regex": "(request_operation=\\.=(.*?);\\.;)", - "tag_name": "request_operation" - }, - { - "regex": "(request_host=\\.=(.*?);\\.;)", - "tag_name": "request_host" - }, - { - "regex": "(response_flags=\\.=(.*?);\\.;)", - "tag_name": "response_flags" - }, - { - "regex": "(grpc_response_status=\\.=(.*?);\\.;)", - "tag_name": "grpc_response_status" - }, - { - "regex": "(connection_security_policy=\\.=(.*?);\\.;)", - "tag_name": "connection_security_policy" - }, - { - "regex": "(source_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_service" - }, - { - "regex": "(destination_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_service" - }, - { - "regex": "(source_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_revision" - }, - { - "regex": "(destination_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_revision" - }, - { - "regex": "(dlp_success=\\.=(.*?);\\.;)", - "tag_name": "dlp_success" - }, - { - "regex": "(dlp_status=\\.=(.*?);\\.;)", - "tag_name": "dlp_status" - }, - { - "regex": "(dlp_error=\\.=(.*?);\\.;)", - "tag_name": "dlp_error" - }, - { - "regex": "(cache\\.(.+?)\\.)", - "tag_name": "cache" - }, - { - "regex": "(component\\.(.+?)\\.)", - "tag_name": "component" - }, - { - "regex": "(tag\\.(.+?);\\.)", - "tag_name": "tag" - }, - { - "regex": "(wasm_filter\\.(.+?)\\.)", - "tag_name": "wasm_filter" - }, - { - "tag_name": "authz_enforce_result", - "regex": "rbac(\\.(allowed|denied))" - }, - { - "tag_name": "authz_dry_run_action", - "regex": "(\\.istio_dry_run_(allow|deny)_)" - }, - { - "tag_name": "authz_dry_run_result", - "regex": "(\\.shadow_(allowed|denied))" - } - ], - "stats_matcher": { - "inclusion_list": { - "patterns": [ - { - "prefix": "reporter=" - }, - { - "prefix": "prefix1" - }, - { - "prefix": "prefix2" - }, - { - "prefix": "http.10.3.3.3_" - }, - { - "prefix": "http.10.4.4.4_" - }, - { - "prefix": "http.10.5.5.5_" - }, - { - "prefix": "http.10.6.6.6_" - }, - { - "prefix": "cluster_manager" - }, - { - "prefix": "listener_manager" - }, - { - "prefix": "server" - }, - { - "prefix": "cluster.xds-grpc" - }, - { - "prefix": "wasm" - }, - { - "suffix": "suffix1" - }, - { - "suffix": "suffix2" - }, - { - "suffix": "upstream_rq_1xx" - }, - { - "suffix": "upstream_rq_2xx" - }, - { - "suffix": "upstream_rq_3xx" - }, - { - "suffix": "upstream_rq_4xx" - }, - { - "suffix": "upstream_rq_5xx" - }, - { - "suffix": "upstream_rq_time" - }, - { - "suffix": "upstream_cx_tx_bytes_total" - }, - { - "suffix": "upstream_cx_rx_bytes_total" - }, - { - "suffix": "upstream_cx_total" - }, - { - "suffix": "downstream_rq_1xx" - }, - { - "suffix": "downstream_rq_2xx" - }, - { - "suffix": "downstream_rq_3xx" - }, - { - "suffix": "downstream_rq_4xx" - }, - { - "suffix": "downstream_rq_5xx" - }, - { - "suffix": "downstream_rq_time" - }, - { - "suffix": "downstream_cx_tx_bytes_total" - }, - { - "suffix": "downstream_cx_rx_bytes_total" - }, - { - "suffix": "downstream_cx_total" - }, - { - "suffix": "rbac.allowed" - }, - { - "suffix": "rbac.denied" - }, - { - "suffix": "shadow_allowed" - }, - { - "suffix": "shadow_denied" - }, - { - "safe_regex": {"google_re2":{}, "regex":"http.[0-9]*\\.[0-9]*\\.[0-9]*\\.[0-9]*_8080.downstream_rq_time"} - }, - { - "prefix": "component" - } - ] - } - } - }, - "admin": { - "access_log_path": "/dev/null", - "profile_path": "/var/lib/istio/data/envoy.prof", - "address": { - "socket_address": { - "address": "127.0.0.1", - "port_value": 15000 - } - } - }, - "dynamic_resources": { - "lds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "cds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "ads_config": { - "api_type": "GRPC", - "set_node_on_first_message_only": true, - "transport_api_version": "V3", - "grpc_services": [ - { - "envoy_grpc": { - "cluster_name": "xds-grpc" - } - } - ] - } - }, - "static_resources": { - "clusters": [ - { - "name": "prometheus_stats", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "prometheus_stats", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15000 - } - } - } - }] - }] - } - }, - { - "name": "agent", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "agent", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15020 - } - } - } - }] - }] - } - }, - { - "name": "sds-grpc", - "type": "STATIC", - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - }, - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "sds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "./var/run/secrets/workload-spiffe-uds/socket" - } - } - } - }] - }] - } - }, - { - "name": "xds-grpc", - "type" : "STATIC", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "xds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "/tmp/XDS" - } - } - } - }] - }] - }, - "circuit_breakers": { - "thresholds": [ - { - "priority": "DEFAULT", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - }, - { - "priority": "HIGH", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - } - ] - }, - "upstream_connection_options": { - "tcp_keepalive": { - "keepalive_time": 300 - } - }, - "max_requests_per_connection": 1, - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - } - } - - - ], - "listeners":[ - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15090 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "stats", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/stats/prometheus" - }, - "route": { - "cluster": "prometheus_stats" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - }, - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15021 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "agent", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/healthz/ready" - }, - "route": { - "cluster": "agent" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - } - ] - } - - - , - "cluster_manager": { - "outlier_detection": { - "event_log_path": "/dev/stdout" - } - } - -} diff --git a/pkg/bootstrap/testdata/tracing_datadog.proxycfg b/pkg/bootstrap/testdata/tracing_datadog.proxycfg deleted file mode 100644 index 306331a67..000000000 --- a/pkg/bootstrap/testdata/tracing_datadog.proxycfg +++ /dev/null @@ -1,9 +0,0 @@ -config_path: "/etc/istio/proxy" -binary_path: "/usr/local/bin/envoy" -service_cluster: "istio-proxy" -drain_duration: {seconds: 2} -parent_shutdown_duration: {seconds: 3} -discovery_address: "istio-pilot:15010" -proxy_admin_port: 15000 -control_plane_auth_policy: NONE -tracing: { datadog: { address: "localhost:8126" } } diff --git a/pkg/bootstrap/testdata/tracing_datadog_golden.json b/pkg/bootstrap/testdata/tracing_datadog_golden.json deleted file mode 100644 index 80200a6ad..000000000 --- a/pkg/bootstrap/testdata/tracing_datadog_golden.json +++ /dev/null @@ -1,545 +0,0 @@ -{ - "node": { - "id": "sidecar~1.2.3.4~foo~bar", - "cluster": "istio-proxy", - "locality": { - }, - "metadata": {"ENVOY_PROMETHEUS_PORT":15090,"ENVOY_STATUS_PORT":15021,"INSTANCE_IPS":"10.3.3.3,10.4.4.4,10.5.5.5,10.6.6.6","OUTLIER_LOG_PATH":"/dev/stdout","PILOT_SAN":["spiffe://cluster.local/ns/dubbo-system/sa/istio-pilot-service-account"],"PROXY_CONFIG":{"binaryPath":"/usr/local/bin/envoy","configPath":"/tmp/bootstrap/tracing_datadog","customConfigFile":"envoy_bootstrap.json","discoveryAddress":"istio-pilot:15010","drainDuration":"2s","parentShutdownDuration":"3s","proxyAdminPort":15000,"serviceCluster":"istio-proxy","statusPort":15020,"tracing":{"datadog":{"address":"localhost:8126"}}}} - }, - "layered_runtime": { - "layers": [ - { - "name": "global config", - "static_layer": {"envoy.deprecated_features:envoy.config.listener.v3.Listener.hidden_envoy_deprecated_use_original_dst":"true","envoy.reloadable_features.http_reject_path_with_fragment":"false","envoy.reloadable_features.no_extension_lookup_by_name":"false","envoy.reloadable_features.require_strict_1xx_and_204_response_headers":"false","overload.global_downstream_max_connections":"2147483647","re2.max_program_size.error_level":"32768"} - }, - { - "name": "admin", - "admin_layer": {} - } - ] - }, - "stats_config": { - "use_all_default_tags": false, - "stats_tags": [ - { - "tag_name": "cluster_name", - "regex": "^cluster\\.((.+?(\\..+?\\.svc\\.cluster\\.local)?)\\.)" - }, - { - "tag_name": "tcp_prefix", - "regex": "^tcp\\.((.*?)\\.)\\w+?$" - }, - { - "regex": "(response_code=\\.=(.+?);\\.;)|_rq(_(\\.d{3}))$", - "tag_name": "response_code" - }, - { - "tag_name": "response_code_class", - "regex": "_rq(_(\\dxx))$" - }, - { - "tag_name": "http_conn_manager_listener_prefix", - "regex": "^listener(?=\\.).*?\\.http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "http_conn_manager_prefix", - "regex": "^http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "listener_address", - "regex": "^listener\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "mongo_prefix", - "regex": "^mongo\\.(.+?)\\.(collection|cmd|cx_|op_|delays_|decoding_)(.*?)$" - }, - { - "regex": "(reporter=\\.=(.*?);\\.;)", - "tag_name": "reporter" - }, - { - "regex": "(source_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_namespace" - }, - { - "regex": "(source_workload=\\.=(.*?);\\.;)", - "tag_name": "source_workload" - }, - { - "regex": "(source_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_workload_namespace" - }, - { - "regex": "(source_principal=\\.=(.*?);\\.;)", - "tag_name": "source_principal" - }, - { - "regex": "(source_app=\\.=(.*?);\\.;)", - "tag_name": "source_app" - }, - { - "regex": "(source_version=\\.=(.*?);\\.;)", - "tag_name": "source_version" - }, - { - "regex": "(source_cluster=\\.=(.*?);\\.;)", - "tag_name": "source_cluster" - }, - { - "regex": "(destination_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_namespace" - }, - { - "regex": "(destination_workload=\\.=(.*?);\\.;)", - "tag_name": "destination_workload" - }, - { - "regex": "(destination_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_workload_namespace" - }, - { - "regex": "(destination_principal=\\.=(.*?);\\.;)", - "tag_name": "destination_principal" - }, - { - "regex": "(destination_app=\\.=(.*?);\\.;)", - "tag_name": "destination_app" - }, - { - "regex": "(destination_version=\\.=(.*?);\\.;)", - "tag_name": "destination_version" - }, - { - "regex": "(destination_service=\\.=(.*?);\\.;)", - "tag_name": "destination_service" - }, - { - "regex": "(destination_service_name=\\.=(.*?);\\.;)", - "tag_name": "destination_service_name" - }, - { - "regex": "(destination_service_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_service_namespace" - }, - { - "regex": "(destination_port=\\.=(.*?);\\.;)", - "tag_name": "destination_port" - }, - { - "regex": "(destination_cluster=\\.=(.*?);\\.;)", - "tag_name": "destination_cluster" - }, - { - "regex": "(request_protocol=\\.=(.*?);\\.;)", - "tag_name": "request_protocol" - }, - { - "regex": "(request_operation=\\.=(.*?);\\.;)", - "tag_name": "request_operation" - }, - { - "regex": "(request_host=\\.=(.*?);\\.;)", - "tag_name": "request_host" - }, - { - "regex": "(response_flags=\\.=(.*?);\\.;)", - "tag_name": "response_flags" - }, - { - "regex": "(grpc_response_status=\\.=(.*?);\\.;)", - "tag_name": "grpc_response_status" - }, - { - "regex": "(connection_security_policy=\\.=(.*?);\\.;)", - "tag_name": "connection_security_policy" - }, - { - "regex": "(source_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_service" - }, - { - "regex": "(destination_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_service" - }, - { - "regex": "(source_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_revision" - }, - { - "regex": "(destination_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_revision" - }, - { - "regex": "(cache\\.(.+?)\\.)", - "tag_name": "cache" - }, - { - "regex": "(component\\.(.+?)\\.)", - "tag_name": "component" - }, - { - "regex": "(tag\\.(.+?);\\.)", - "tag_name": "tag" - }, - { - "regex": "(wasm_filter\\.(.+?)\\.)", - "tag_name": "wasm_filter" - }, - { - "tag_name": "authz_enforce_result", - "regex": "rbac(\\.(allowed|denied))" - }, - { - "tag_name": "authz_dry_run_action", - "regex": "(\\.istio_dry_run_(allow|deny)_)" - }, - { - "tag_name": "authz_dry_run_result", - "regex": "(\\.shadow_(allowed|denied))" - } - ], - "stats_matcher": { - "inclusion_list": { - "patterns": [ - { - "prefix": "reporter=" - }, - { - "prefix": "cluster_manager" - }, - { - "prefix": "listener_manager" - }, - { - "prefix": "server" - }, - { - "prefix": "cluster.xds-grpc" - }, - { - "prefix": "wasm" - }, - { - "suffix": "rbac.allowed" - }, - { - "suffix": "rbac.denied" - }, - { - "suffix": "shadow_allowed" - }, - { - "suffix": "shadow_denied" - }, - { - "prefix": "component" - } - ] - } - } - }, - "admin": { - "access_log_path": "/dev/null", - "profile_path": "/var/lib/istio/data/envoy.prof", - "address": { - "socket_address": { - "address": "127.0.0.1", - "port_value": 15000 - } - } - }, - "dynamic_resources": { - "lds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "cds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "ads_config": { - "api_type": "GRPC", - "set_node_on_first_message_only": true, - "transport_api_version": "V3", - "grpc_services": [ - { - "envoy_grpc": { - "cluster_name": "xds-grpc" - } - } - ] - } - }, - "static_resources": { - "clusters": [ - { - "name": "prometheus_stats", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "prometheus_stats", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15000 - } - } - } - }] - }] - } - }, - { - "name": "agent", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "agent", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15020 - } - } - } - }] - }] - } - }, - { - "name": "sds-grpc", - "type": "STATIC", - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - }, - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "sds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "./var/run/secrets/workload-spiffe-uds/socket" - } - } - } - }] - }] - } - }, - { - "name": "xds-grpc", - "type" : "STATIC", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "xds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "/tmp/XDS" - } - } - } - }] - }] - }, - "circuit_breakers": { - "thresholds": [ - { - "priority": "DEFAULT", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - }, - { - "priority": "HIGH", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - } - ] - }, - "upstream_connection_options": { - "tcp_keepalive": { - "keepalive_time": 300 - } - }, - "max_requests_per_connection": 1, - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - } - } - - , - { - "name": "datadog_agent", - "connect_timeout": "1s", - "type": "STRICT_DNS", - "respect_dns_ttl": true, - "dns_lookup_family": "V4_ONLY", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "datadog_agent", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": {"address": "localhost", "port_value": 8126} - } - } - }] - }] - } - } - - - ], - "listeners":[ - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15090 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "stats", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/stats/prometheus" - }, - "route": { - "cluster": "prometheus_stats" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - }, - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15021 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "agent", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/healthz/ready" - }, - "route": { - "cluster": "agent" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - } - ] - } - , - "tracing": { - "http": { - "name": "envoy.tracers.datadog", - "typed_config": { - "@type": "type.googleapis.com/envoy.config.trace.v3.DatadogConfig", - "collector_cluster": "datadog_agent", - "service_name": "istio-proxy" - } - } - } - - - , - "cluster_manager": { - "outlier_detection": { - "event_log_path": "/dev/stdout" - } - } - -} diff --git a/pkg/bootstrap/testdata/tracing_lightstep.proxycfg b/pkg/bootstrap/testdata/tracing_lightstep.proxycfg deleted file mode 100644 index 7e87094ba..000000000 --- a/pkg/bootstrap/testdata/tracing_lightstep.proxycfg +++ /dev/null @@ -1,9 +0,0 @@ -config_path: "/etc/istio/proxy" -binary_path: "/usr/local/bin/envoy" -service_cluster: "istio-proxy" -drain_duration: {seconds: 2} -parent_shutdown_duration: {seconds: 3} -discovery_address: "istio-pilot:15010" -proxy_admin_port: 15000 -control_plane_auth_policy: NONE -tracing: { lightstep: { address: "lightstep-satellite:8080", access_token: "abcdefg1234567" } } diff --git a/pkg/bootstrap/testdata/tracing_lightstep_golden.json b/pkg/bootstrap/testdata/tracing_lightstep_golden.json deleted file mode 100644 index 6c49375cb..000000000 --- a/pkg/bootstrap/testdata/tracing_lightstep_golden.json +++ /dev/null @@ -1,553 +0,0 @@ -{ - "node": { - "id": "sidecar~1.2.3.4~foo~bar", - "cluster": "istio-proxy", - "locality": { - }, - "metadata": {"ENVOY_PROMETHEUS_PORT":15090,"ENVOY_STATUS_PORT":15021,"INSTANCE_IPS":"10.3.3.3,10.4.4.4,10.5.5.5,10.6.6.6","OUTLIER_LOG_PATH":"/dev/stdout","PILOT_SAN":["spiffe://cluster.local/ns/dubbo-system/sa/istio-pilot-service-account"],"PROXY_CONFIG":{"binaryPath":"/usr/local/bin/envoy","configPath":"/tmp/bootstrap/tracing_lightstep","customConfigFile":"envoy_bootstrap.json","discoveryAddress":"istio-pilot:15010","drainDuration":"2s","parentShutdownDuration":"3s","proxyAdminPort":15000,"serviceCluster":"istio-proxy","statusPort":15020,"tracing":{"lightstep":{"accessToken":"abcdefg1234567","address":"lightstep-satellite:8080"}}}} - }, - "layered_runtime": { - "layers": [ - { - "name": "global config", - "static_layer": {"envoy.deprecated_features:envoy.config.listener.v3.Listener.hidden_envoy_deprecated_use_original_dst":"true","envoy.reloadable_features.http_reject_path_with_fragment":"false","envoy.reloadable_features.no_extension_lookup_by_name":"false","envoy.reloadable_features.require_strict_1xx_and_204_response_headers":"false","overload.global_downstream_max_connections":"2147483647","re2.max_program_size.error_level":"32768"} - }, - { - "name": "admin", - "admin_layer": {} - } - ] - }, - "stats_config": { - "use_all_default_tags": false, - "stats_tags": [ - { - "tag_name": "cluster_name", - "regex": "^cluster\\.((.+?(\\..+?\\.svc\\.cluster\\.local)?)\\.)" - }, - { - "tag_name": "tcp_prefix", - "regex": "^tcp\\.((.*?)\\.)\\w+?$" - }, - { - "regex": "(response_code=\\.=(.+?);\\.;)|_rq(_(\\.d{3}))$", - "tag_name": "response_code" - }, - { - "tag_name": "response_code_class", - "regex": "_rq(_(\\dxx))$" - }, - { - "tag_name": "http_conn_manager_listener_prefix", - "regex": "^listener(?=\\.).*?\\.http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "http_conn_manager_prefix", - "regex": "^http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "listener_address", - "regex": "^listener\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "mongo_prefix", - "regex": "^mongo\\.(.+?)\\.(collection|cmd|cx_|op_|delays_|decoding_)(.*?)$" - }, - { - "regex": "(reporter=\\.=(.*?);\\.;)", - "tag_name": "reporter" - }, - { - "regex": "(source_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_namespace" - }, - { - "regex": "(source_workload=\\.=(.*?);\\.;)", - "tag_name": "source_workload" - }, - { - "regex": "(source_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_workload_namespace" - }, - { - "regex": "(source_principal=\\.=(.*?);\\.;)", - "tag_name": "source_principal" - }, - { - "regex": "(source_app=\\.=(.*?);\\.;)", - "tag_name": "source_app" - }, - { - "regex": "(source_version=\\.=(.*?);\\.;)", - "tag_name": "source_version" - }, - { - "regex": "(source_cluster=\\.=(.*?);\\.;)", - "tag_name": "source_cluster" - }, - { - "regex": "(destination_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_namespace" - }, - { - "regex": "(destination_workload=\\.=(.*?);\\.;)", - "tag_name": "destination_workload" - }, - { - "regex": "(destination_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_workload_namespace" - }, - { - "regex": "(destination_principal=\\.=(.*?);\\.;)", - "tag_name": "destination_principal" - }, - { - "regex": "(destination_app=\\.=(.*?);\\.;)", - "tag_name": "destination_app" - }, - { - "regex": "(destination_version=\\.=(.*?);\\.;)", - "tag_name": "destination_version" - }, - { - "regex": "(destination_service=\\.=(.*?);\\.;)", - "tag_name": "destination_service" - }, - { - "regex": "(destination_service_name=\\.=(.*?);\\.;)", - "tag_name": "destination_service_name" - }, - { - "regex": "(destination_service_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_service_namespace" - }, - { - "regex": "(destination_port=\\.=(.*?);\\.;)", - "tag_name": "destination_port" - }, - { - "regex": "(destination_cluster=\\.=(.*?);\\.;)", - "tag_name": "destination_cluster" - }, - { - "regex": "(request_protocol=\\.=(.*?);\\.;)", - "tag_name": "request_protocol" - }, - { - "regex": "(request_operation=\\.=(.*?);\\.;)", - "tag_name": "request_operation" - }, - { - "regex": "(request_host=\\.=(.*?);\\.;)", - "tag_name": "request_host" - }, - { - "regex": "(response_flags=\\.=(.*?);\\.;)", - "tag_name": "response_flags" - }, - { - "regex": "(grpc_response_status=\\.=(.*?);\\.;)", - "tag_name": "grpc_response_status" - }, - { - "regex": "(connection_security_policy=\\.=(.*?);\\.;)", - "tag_name": "connection_security_policy" - }, - { - "regex": "(source_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_service" - }, - { - "regex": "(destination_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_service" - }, - { - "regex": "(source_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_revision" - }, - { - "regex": "(destination_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_revision" - }, - { - "regex": "(cache\\.(.+?)\\.)", - "tag_name": "cache" - }, - { - "regex": "(component\\.(.+?)\\.)", - "tag_name": "component" - }, - { - "regex": "(tag\\.(.+?);\\.)", - "tag_name": "tag" - }, - { - "regex": "(wasm_filter\\.(.+?)\\.)", - "tag_name": "wasm_filter" - }, - { - "tag_name": "authz_enforce_result", - "regex": "rbac(\\.(allowed|denied))" - }, - { - "tag_name": "authz_dry_run_action", - "regex": "(\\.istio_dry_run_(allow|deny)_)" - }, - { - "tag_name": "authz_dry_run_result", - "regex": "(\\.shadow_(allowed|denied))" - } - ], - "stats_matcher": { - "inclusion_list": { - "patterns": [ - { - "prefix": "reporter=" - }, - { - "prefix": "cluster_manager" - }, - { - "prefix": "listener_manager" - }, - { - "prefix": "server" - }, - { - "prefix": "cluster.xds-grpc" - }, - { - "prefix": "wasm" - }, - { - "suffix": "rbac.allowed" - }, - { - "suffix": "rbac.denied" - }, - { - "suffix": "shadow_allowed" - }, - { - "suffix": "shadow_denied" - }, - { - "prefix": "component" - } - ] - } - } - }, - "admin": { - "access_log_path": "/dev/null", - "profile_path": "/var/lib/istio/data/envoy.prof", - "address": { - "socket_address": { - "address": "127.0.0.1", - "port_value": 15000 - } - } - }, - "dynamic_resources": { - "lds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "cds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "ads_config": { - "api_type": "GRPC", - "set_node_on_first_message_only": true, - "transport_api_version": "V3", - "grpc_services": [ - { - "envoy_grpc": { - "cluster_name": "xds-grpc" - } - } - ] - } - }, - "static_resources": { - "clusters": [ - { - "name": "prometheus_stats", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "prometheus_stats", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15000 - } - } - } - }] - }] - } - }, - { - "name": "agent", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "agent", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15020 - } - } - } - }] - }] - } - }, - { - "name": "sds-grpc", - "type": "STATIC", - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - }, - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "sds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "./var/run/secrets/workload-spiffe-uds/socket" - } - } - } - }] - }] - } - }, - { - "name": "xds-grpc", - "type" : "STATIC", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "xds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "/tmp/XDS" - } - } - } - }] - }] - }, - "circuit_breakers": { - "thresholds": [ - { - "priority": "DEFAULT", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - }, - { - "priority": "HIGH", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - } - ] - }, - "upstream_connection_options": { - "tcp_keepalive": { - "keepalive_time": 300 - } - }, - "max_requests_per_connection": 1, - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - } - } - - , - { - "name": "lightstep", - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - }, - "type": "STRICT_DNS", - "respect_dns_ttl": true, - "dns_lookup_family": "V4_ONLY", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "lightstep", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": {"address": "lightstep-satellite", "port_value": 8080} - } - } - }] - }] - } - } - - - ], - "listeners":[ - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15090 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "stats", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/stats/prometheus" - }, - "route": { - "cluster": "prometheus_stats" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - }, - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15021 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "agent", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/healthz/ready" - }, - "route": { - "cluster": "agent" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - } - ] - } - , - "tracing": { - "http": { - "name": "envoy.tracers.lightstep", - "typed_config": { - "@type": "type.googleapis.com/envoy.config.trace.v3.LightstepConfig", - "collector_cluster": "lightstep", - "access_token_file": "/test-path/lightstep_access_token.txt" - } - } - } - - - , - "cluster_manager": { - "outlier_detection": { - "event_log_path": "/dev/stdout" - } - } - -} diff --git a/pkg/bootstrap/testdata/tracing_opencensusagent.proxycfg b/pkg/bootstrap/testdata/tracing_opencensusagent.proxycfg deleted file mode 100644 index 90a476140..000000000 --- a/pkg/bootstrap/testdata/tracing_opencensusagent.proxycfg +++ /dev/null @@ -1,12 +0,0 @@ -config_path: "/etc/istio/proxy" -binary_path: "/usr/local/bin/envoy" -service_cluster: "istio-proxy" -drain_duration: {seconds: 2} -parent_shutdown_duration: {seconds: 3} -discovery_address: "istio-pilot:15010" -proxy_admin_port: 15000 -control_plane_auth_policy: NONE -tracing: { open_census_agent: { address: "dns://my-oca/endpoint", context: [1] }} - -# Sets all relevant options to values different than default - diff --git a/pkg/bootstrap/testdata/tracing_opencensusagent_golden.json b/pkg/bootstrap/testdata/tracing_opencensusagent_golden.json deleted file mode 100644 index 3fcbace4c..000000000 --- a/pkg/bootstrap/testdata/tracing_opencensusagent_golden.json +++ /dev/null @@ -1,534 +0,0 @@ -{ - "node": { - "id": "sidecar~1.2.3.4~foo~bar", - "cluster": "istio-proxy", - "locality": { - }, - "metadata": {"ENVOY_PROMETHEUS_PORT":15090,"ENVOY_STATUS_PORT":15021,"INSTANCE_IPS":"10.3.3.3,10.4.4.4,10.5.5.5,10.6.6.6","OUTLIER_LOG_PATH":"/dev/stdout","PILOT_SAN":["spiffe://cluster.local/ns/dubbo-system/sa/istio-pilot-service-account"],"PROXY_CONFIG":{"binaryPath":"/usr/local/bin/envoy","configPath":"/tmp/bootstrap/tracing_opencensusagent","customConfigFile":"envoy_bootstrap.json","discoveryAddress":"istio-pilot:15010","drainDuration":"2s","parentShutdownDuration":"3s","proxyAdminPort":15000,"serviceCluster":"istio-proxy","statusPort":15020,"tracing":{"openCensusAgent":{"address":"dns://my-oca/endpoint","context":["W3C_TRACE_CONTEXT"]}}}} - }, - "layered_runtime": { - "layers": [ - { - "name": "global config", - "static_layer": {"envoy.deprecated_features:envoy.config.listener.v3.Listener.hidden_envoy_deprecated_use_original_dst":"true","envoy.reloadable_features.http_reject_path_with_fragment":"false","envoy.reloadable_features.no_extension_lookup_by_name":"false","envoy.reloadable_features.require_strict_1xx_and_204_response_headers":"false","overload.global_downstream_max_connections":"2147483647","re2.max_program_size.error_level":"32768"} - }, - { - "name": "admin", - "admin_layer": {} - } - ] - }, - "stats_config": { - "use_all_default_tags": false, - "stats_tags": [ - { - "tag_name": "cluster_name", - "regex": "^cluster\\.((.+?(\\..+?\\.svc\\.cluster\\.local)?)\\.)" - }, - { - "tag_name": "tcp_prefix", - "regex": "^tcp\\.((.*?)\\.)\\w+?$" - }, - { - "regex": "(response_code=\\.=(.+?);\\.;)|_rq(_(\\.d{3}))$", - "tag_name": "response_code" - }, - { - "tag_name": "response_code_class", - "regex": "_rq(_(\\dxx))$" - }, - { - "tag_name": "http_conn_manager_listener_prefix", - "regex": "^listener(?=\\.).*?\\.http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "http_conn_manager_prefix", - "regex": "^http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "listener_address", - "regex": "^listener\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "mongo_prefix", - "regex": "^mongo\\.(.+?)\\.(collection|cmd|cx_|op_|delays_|decoding_)(.*?)$" - }, - { - "regex": "(reporter=\\.=(.*?);\\.;)", - "tag_name": "reporter" - }, - { - "regex": "(source_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_namespace" - }, - { - "regex": "(source_workload=\\.=(.*?);\\.;)", - "tag_name": "source_workload" - }, - { - "regex": "(source_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_workload_namespace" - }, - { - "regex": "(source_principal=\\.=(.*?);\\.;)", - "tag_name": "source_principal" - }, - { - "regex": "(source_app=\\.=(.*?);\\.;)", - "tag_name": "source_app" - }, - { - "regex": "(source_version=\\.=(.*?);\\.;)", - "tag_name": "source_version" - }, - { - "regex": "(source_cluster=\\.=(.*?);\\.;)", - "tag_name": "source_cluster" - }, - { - "regex": "(destination_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_namespace" - }, - { - "regex": "(destination_workload=\\.=(.*?);\\.;)", - "tag_name": "destination_workload" - }, - { - "regex": "(destination_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_workload_namespace" - }, - { - "regex": "(destination_principal=\\.=(.*?);\\.;)", - "tag_name": "destination_principal" - }, - { - "regex": "(destination_app=\\.=(.*?);\\.;)", - "tag_name": "destination_app" - }, - { - "regex": "(destination_version=\\.=(.*?);\\.;)", - "tag_name": "destination_version" - }, - { - "regex": "(destination_service=\\.=(.*?);\\.;)", - "tag_name": "destination_service" - }, - { - "regex": "(destination_service_name=\\.=(.*?);\\.;)", - "tag_name": "destination_service_name" - }, - { - "regex": "(destination_service_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_service_namespace" - }, - { - "regex": "(destination_port=\\.=(.*?);\\.;)", - "tag_name": "destination_port" - }, - { - "regex": "(destination_cluster=\\.=(.*?);\\.;)", - "tag_name": "destination_cluster" - }, - { - "regex": "(request_protocol=\\.=(.*?);\\.;)", - "tag_name": "request_protocol" - }, - { - "regex": "(request_operation=\\.=(.*?);\\.;)", - "tag_name": "request_operation" - }, - { - "regex": "(request_host=\\.=(.*?);\\.;)", - "tag_name": "request_host" - }, - { - "regex": "(response_flags=\\.=(.*?);\\.;)", - "tag_name": "response_flags" - }, - { - "regex": "(grpc_response_status=\\.=(.*?);\\.;)", - "tag_name": "grpc_response_status" - }, - { - "regex": "(connection_security_policy=\\.=(.*?);\\.;)", - "tag_name": "connection_security_policy" - }, - { - "regex": "(source_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_service" - }, - { - "regex": "(destination_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_service" - }, - { - "regex": "(source_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_revision" - }, - { - "regex": "(destination_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_revision" - }, - { - "regex": "(cache\\.(.+?)\\.)", - "tag_name": "cache" - }, - { - "regex": "(component\\.(.+?)\\.)", - "tag_name": "component" - }, - { - "regex": "(tag\\.(.+?);\\.)", - "tag_name": "tag" - }, - { - "regex": "(wasm_filter\\.(.+?)\\.)", - "tag_name": "wasm_filter" - }, - { - "tag_name": "authz_enforce_result", - "regex": "rbac(\\.(allowed|denied))" - }, - { - "tag_name": "authz_dry_run_action", - "regex": "(\\.istio_dry_run_(allow|deny)_)" - }, - { - "tag_name": "authz_dry_run_result", - "regex": "(\\.shadow_(allowed|denied))" - } - ], - "stats_matcher": { - "inclusion_list": { - "patterns": [ - { - "prefix": "reporter=" - }, - { - "prefix": "cluster_manager" - }, - { - "prefix": "listener_manager" - }, - { - "prefix": "server" - }, - { - "prefix": "cluster.xds-grpc" - }, - { - "prefix": "wasm" - }, - { - "suffix": "rbac.allowed" - }, - { - "suffix": "rbac.denied" - }, - { - "suffix": "shadow_allowed" - }, - { - "suffix": "shadow_denied" - }, - { - "prefix": "component" - } - ] - } - } - }, - "admin": { - "access_log_path": "/dev/null", - "profile_path": "/var/lib/istio/data/envoy.prof", - "address": { - "socket_address": { - "address": "127.0.0.1", - "port_value": 15000 - } - } - }, - "dynamic_resources": { - "lds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "cds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "ads_config": { - "api_type": "GRPC", - "set_node_on_first_message_only": true, - "transport_api_version": "V3", - "grpc_services": [ - { - "envoy_grpc": { - "cluster_name": "xds-grpc" - } - } - ] - } - }, - "static_resources": { - "clusters": [ - { - "name": "prometheus_stats", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "prometheus_stats", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15000 - } - } - } - }] - }] - } - }, - { - "name": "agent", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "agent", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15020 - } - } - } - }] - }] - } - }, - { - "name": "sds-grpc", - "type": "STATIC", - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - }, - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "sds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "./var/run/secrets/workload-spiffe-uds/socket" - } - } - } - }] - }] - } - }, - { - "name": "xds-grpc", - "type" : "STATIC", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "xds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "/tmp/XDS" - } - } - } - }] - }] - }, - "circuit_breakers": { - "thresholds": [ - { - "priority": "DEFAULT", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - }, - { - "priority": "HIGH", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - } - ] - }, - "upstream_connection_options": { - "tcp_keepalive": { - "keepalive_time": 300 - } - }, - "max_requests_per_connection": 1, - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - } - } - - - ], - "listeners":[ - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15090 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "stats", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/stats/prometheus" - }, - "route": { - "cluster": "prometheus_stats" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - }, - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15021 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "agent", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/healthz/ready" - }, - "route": { - "cluster": "agent" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - } - ] - } - , - "tracing": { - "http": { - "name": "envoy.tracers.opencensus", - "typed_config": { - "@type": "type.googleapis.com/envoy.config.trace.v3.OpenCensusConfig", - "ocagent_exporter_enabled": true, - "ocagent_address": "dns://my-oca/endpoint", - "incoming_trace_context": ["TRACE_CONTEXT"], - "outgoing_trace_context": ["TRACE_CONTEXT"], - "trace_config": { - "constant_sampler": { - "decision": "ALWAYS_PARENT" - }, - "max_number_of_annotations": 200, - "max_number_of_attributes": 200, - "max_number_of_message_events": 200, - "max_number_of_links": 200 - } - } - } - } - - - , - "cluster_manager": { - "outlier_detection": { - "event_log_path": "/dev/stdout" - } - } - -} diff --git a/pkg/bootstrap/testdata/tracing_stackdriver.proxycfg b/pkg/bootstrap/testdata/tracing_stackdriver.proxycfg deleted file mode 100644 index da95069ac..000000000 --- a/pkg/bootstrap/testdata/tracing_stackdriver.proxycfg +++ /dev/null @@ -1,11 +0,0 @@ -config_path: "/etc/istio/proxy" -binary_path: "/usr/local/bin/envoy" -service_cluster: "istio-proxy" -drain_duration: {seconds: 2} -parent_shutdown_duration: {seconds: 3} -discovery_address: "istio-pilot:15010" -proxy_admin_port: 15000 -control_plane_auth_policy: NONE -tracing: { stackdriver: { debug: true, max_number_of_annotations: {value: 201}, max_number_of_message_events: {value:201}} } - -# Sets all relevant options to values different than default diff --git a/pkg/bootstrap/testdata/tracing_stackdriver_golden.json b/pkg/bootstrap/testdata/tracing_stackdriver_golden.json deleted file mode 100644 index f5aaa3da5..000000000 --- a/pkg/bootstrap/testdata/tracing_stackdriver_golden.json +++ /dev/null @@ -1,562 +0,0 @@ -{ - "node": { - "id": "sidecar~1.2.3.4~foo~bar", - "cluster": "istio-proxy", - "locality": { - }, - "metadata": {"ENVOY_PROMETHEUS_PORT":15090,"ENVOY_STATUS_PORT":15021,"INSTANCE_IPS":"10.3.3.3,10.4.4.4,10.5.5.5,10.6.6.6","OUTLIER_LOG_PATH":"/dev/stdout","PILOT_SAN":["spiffe://cluster.local/ns/dubbo-system/sa/istio-pilot-service-account"],"PLATFORM_METADATA":{"gcp_project":"my-sd-project"},"PROXY_CONFIG":{"binaryPath":"/usr/local/bin/envoy","configPath":"/tmp/bootstrap/tracing_stackdriver","customConfigFile":"envoy_bootstrap.json","discoveryAddress":"istio-pilot:15010","drainDuration":"2s","parentShutdownDuration":"3s","proxyAdminPort":15000,"serviceCluster":"istio-proxy","statusPort":15020,"tracing":{"stackdriver":{"debug":true,"maxNumberOfAnnotations":"201","maxNumberOfMessageEvents":"201"}}},"STS_PORT":"15463"} - }, - "layered_runtime": { - "layers": [ - { - "name": "global config", - "static_layer": {"envoy.deprecated_features:envoy.config.listener.v3.Listener.hidden_envoy_deprecated_use_original_dst":"true","envoy.reloadable_features.http_reject_path_with_fragment":"false","envoy.reloadable_features.no_extension_lookup_by_name":"false","envoy.reloadable_features.require_strict_1xx_and_204_response_headers":"false","overload.global_downstream_max_connections":"2147483647","re2.max_program_size.error_level":"32768"} - }, - { - "name": "admin", - "admin_layer": {} - } - ] - }, - "stats_config": { - "use_all_default_tags": false, - "stats_tags": [ - { - "tag_name": "cluster_name", - "regex": "^cluster\\.((.+?(\\..+?\\.svc\\.cluster\\.local)?)\\.)" - }, - { - "tag_name": "tcp_prefix", - "regex": "^tcp\\.((.*?)\\.)\\w+?$" - }, - { - "regex": "(response_code=\\.=(.+?);\\.;)|_rq(_(\\.d{3}))$", - "tag_name": "response_code" - }, - { - "tag_name": "response_code_class", - "regex": "_rq(_(\\dxx))$" - }, - { - "tag_name": "http_conn_manager_listener_prefix", - "regex": "^listener(?=\\.).*?\\.http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "http_conn_manager_prefix", - "regex": "^http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "listener_address", - "regex": "^listener\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "mongo_prefix", - "regex": "^mongo\\.(.+?)\\.(collection|cmd|cx_|op_|delays_|decoding_)(.*?)$" - }, - { - "regex": "(reporter=\\.=(.*?);\\.;)", - "tag_name": "reporter" - }, - { - "regex": "(source_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_namespace" - }, - { - "regex": "(source_workload=\\.=(.*?);\\.;)", - "tag_name": "source_workload" - }, - { - "regex": "(source_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_workload_namespace" - }, - { - "regex": "(source_principal=\\.=(.*?);\\.;)", - "tag_name": "source_principal" - }, - { - "regex": "(source_app=\\.=(.*?);\\.;)", - "tag_name": "source_app" - }, - { - "regex": "(source_version=\\.=(.*?);\\.;)", - "tag_name": "source_version" - }, - { - "regex": "(source_cluster=\\.=(.*?);\\.;)", - "tag_name": "source_cluster" - }, - { - "regex": "(destination_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_namespace" - }, - { - "regex": "(destination_workload=\\.=(.*?);\\.;)", - "tag_name": "destination_workload" - }, - { - "regex": "(destination_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_workload_namespace" - }, - { - "regex": "(destination_principal=\\.=(.*?);\\.;)", - "tag_name": "destination_principal" - }, - { - "regex": "(destination_app=\\.=(.*?);\\.;)", - "tag_name": "destination_app" - }, - { - "regex": "(destination_version=\\.=(.*?);\\.;)", - "tag_name": "destination_version" - }, - { - "regex": "(destination_service=\\.=(.*?);\\.;)", - "tag_name": "destination_service" - }, - { - "regex": "(destination_service_name=\\.=(.*?);\\.;)", - "tag_name": "destination_service_name" - }, - { - "regex": "(destination_service_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_service_namespace" - }, - { - "regex": "(destination_port=\\.=(.*?);\\.;)", - "tag_name": "destination_port" - }, - { - "regex": "(destination_cluster=\\.=(.*?);\\.;)", - "tag_name": "destination_cluster" - }, - { - "regex": "(request_protocol=\\.=(.*?);\\.;)", - "tag_name": "request_protocol" - }, - { - "regex": "(request_operation=\\.=(.*?);\\.;)", - "tag_name": "request_operation" - }, - { - "regex": "(request_host=\\.=(.*?);\\.;)", - "tag_name": "request_host" - }, - { - "regex": "(response_flags=\\.=(.*?);\\.;)", - "tag_name": "response_flags" - }, - { - "regex": "(grpc_response_status=\\.=(.*?);\\.;)", - "tag_name": "grpc_response_status" - }, - { - "regex": "(connection_security_policy=\\.=(.*?);\\.;)", - "tag_name": "connection_security_policy" - }, - { - "regex": "(source_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_service" - }, - { - "regex": "(destination_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_service" - }, - { - "regex": "(source_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_revision" - }, - { - "regex": "(destination_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_revision" - }, - { - "regex": "(cache\\.(.+?)\\.)", - "tag_name": "cache" - }, - { - "regex": "(component\\.(.+?)\\.)", - "tag_name": "component" - }, - { - "regex": "(tag\\.(.+?);\\.)", - "tag_name": "tag" - }, - { - "regex": "(wasm_filter\\.(.+?)\\.)", - "tag_name": "wasm_filter" - }, - { - "tag_name": "authz_enforce_result", - "regex": "rbac(\\.(allowed|denied))" - }, - { - "tag_name": "authz_dry_run_action", - "regex": "(\\.istio_dry_run_(allow|deny)_)" - }, - { - "tag_name": "authz_dry_run_result", - "regex": "(\\.shadow_(allowed|denied))" - } - ], - "stats_matcher": { - "inclusion_list": { - "patterns": [ - { - "prefix": "reporter=" - }, - { - "prefix": "cluster_manager" - }, - { - "prefix": "listener_manager" - }, - { - "prefix": "server" - }, - { - "prefix": "cluster.xds-grpc" - }, - { - "prefix": "wasm" - }, - { - "suffix": "rbac.allowed" - }, - { - "suffix": "rbac.denied" - }, - { - "suffix": "shadow_allowed" - }, - { - "suffix": "shadow_denied" - }, - { - "prefix": "component" - } - ] - } - } - }, - "admin": { - "access_log_path": "/dev/null", - "profile_path": "/var/lib/istio/data/envoy.prof", - "address": { - "socket_address": { - "address": "127.0.0.1", - "port_value": 15000 - } - } - }, - "dynamic_resources": { - "lds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "cds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "ads_config": { - "api_type": "GRPC", - "set_node_on_first_message_only": true, - "transport_api_version": "V3", - "grpc_services": [ - { - "envoy_grpc": { - "cluster_name": "xds-grpc" - } - } - ] - } - }, - "static_resources": { - "clusters": [ - { - "name": "prometheus_stats", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "prometheus_stats", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15000 - } - } - } - }] - }] - } - }, - { - "name": "agent", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "agent", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15020 - } - } - } - }] - }] - } - }, - { - "name": "sds-grpc", - "type": "STATIC", - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - }, - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "sds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "./var/run/secrets/workload-spiffe-uds/socket" - } - } - } - }] - }] - } - }, - { - "name": "xds-grpc", - "type" : "STATIC", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "xds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "/tmp/XDS" - } - } - } - }] - }] - }, - "circuit_breakers": { - "thresholds": [ - { - "priority": "DEFAULT", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - }, - { - "priority": "HIGH", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - } - ] - }, - "upstream_connection_options": { - "tcp_keepalive": { - "keepalive_time": 300 - } - }, - "max_requests_per_connection": 1, - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - } - } - - - ], - "listeners":[ - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15090 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "stats", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/stats/prometheus" - }, - "route": { - "cluster": "prometheus_stats" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - }, - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15021 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "agent", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/healthz/ready" - }, - "route": { - "cluster": "agent" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - } - ] - } - , - "tracing": { - "http": { - "name": "envoy.tracers.opencensus", - "typed_config": { - "@type": "type.googleapis.com/envoy.config.trace.v3.OpenCensusConfig", - "stackdriver_exporter_enabled": true, - "stackdriver_project_id": "my-sd-project", - - "stackdriver_grpc_service": { - "google_grpc": { - "target_uri": "cloudtrace.googleapis.com", - "stat_prefix": "oc_stackdriver_tracer", - "channel_credentials": { - "ssl_credentials": {} - }, - "call_credentials": [{ - "sts_service": { - "token_exchange_service_uri": "http://localhost:15463/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": "my-sd-project" - } - - ] - }, - - "stdout_exporter_enabled": true, - "incoming_trace_context": ["CLOUD_TRACE_CONTEXT", "TRACE_CONTEXT", "GRPC_TRACE_BIN", "B3"], - "outgoing_trace_context": ["CLOUD_TRACE_CONTEXT", "TRACE_CONTEXT", "GRPC_TRACE_BIN", "B3"], - "trace_config":{ - "constant_sampler":{ - "decision": "ALWAYS_PARENT" - }, - "max_number_of_annotations": 201, - "max_number_of_attributes": 200, - "max_number_of_message_events": 201, - "max_number_of_links": 200 - } - } - }} - - - - , - "cluster_manager": { - "outlier_detection": { - "event_log_path": "/dev/stdout" - } - } - -} diff --git a/pkg/bootstrap/testdata/tracing_tls.proxycfg b/pkg/bootstrap/testdata/tracing_tls.proxycfg deleted file mode 100644 index b827d5135..000000000 --- a/pkg/bootstrap/testdata/tracing_tls.proxycfg +++ /dev/null @@ -1,11 +0,0 @@ -config_path: "/etc/istio/proxy" -binary_path: "/usr/local/bin/envoy" -service_cluster: "istio-proxy" -drain_duration: {seconds: 2} -parent_shutdown_duration: {seconds: 3} -discovery_address: "istio-pilot:15010" -proxy_admin_port: 15000 -control_plane_auth_policy: NONE -tracing: { zipkin: { address: "localhost:6000" }, tls_settings: { mode: SIMPLE, ca_certificates: "/etc/zipkin/ca.pem"}} - -# Sets all relevant options to values different than default diff --git a/pkg/bootstrap/testdata/tracing_tls_custom_sni.proxycfg b/pkg/bootstrap/testdata/tracing_tls_custom_sni.proxycfg deleted file mode 100644 index 024a1eb03..000000000 --- a/pkg/bootstrap/testdata/tracing_tls_custom_sni.proxycfg +++ /dev/null @@ -1,11 +0,0 @@ -config_path: "/etc/istio/proxy" -binary_path: "/usr/local/bin/envoy" -service_cluster: "istio-proxy" -drain_duration: {seconds: 2} -parent_shutdown_duration: {seconds: 3} -discovery_address: "istio-pilot:15010" -proxy_admin_port: 15000 -control_plane_auth_policy: NONE -tracing: { zipkin: { address: "localhost:6000" }, tls_settings: { mode: SIMPLE, ca_certificates: "/etc/zipkin/ca.pem", sni: "zipkin-custom-sni"}} - -# Sets all relevant options to values different than default diff --git a/pkg/bootstrap/testdata/tracing_tls_custom_sni_golden.json b/pkg/bootstrap/testdata/tracing_tls_custom_sni_golden.json deleted file mode 100644 index 0dcb650cc..000000000 --- a/pkg/bootstrap/testdata/tracing_tls_custom_sni_golden.json +++ /dev/null @@ -1,550 +0,0 @@ -{ - "node": { - "id": "sidecar~1.2.3.4~foo~bar", - "cluster": "istio-proxy", - "locality": { - }, - "metadata": {"ENVOY_PROMETHEUS_PORT":15090,"ENVOY_STATUS_PORT":15021,"INSTANCE_IPS":"10.3.3.3,10.4.4.4,10.5.5.5,10.6.6.6","OUTLIER_LOG_PATH":"/dev/stdout","PILOT_SAN":["spiffe://cluster.local/ns/dubbo-system/sa/istio-pilot-service-account"],"PROXY_CONFIG":{"binaryPath":"/usr/local/bin/envoy","configPath":"/tmp/bootstrap/tracing_tls_custom_sni","customConfigFile":"envoy_bootstrap.json","discoveryAddress":"istio-pilot:15010","drainDuration":"2s","parentShutdownDuration":"3s","proxyAdminPort":15000,"serviceCluster":"istio-proxy","statusPort":15020,"tracing":{"tlsSettings":{"caCertificates":"/etc/zipkin/ca.pem","mode":"SIMPLE","sni":"zipkin-custom-sni"},"zipkin":{"address":"localhost:6000"}}}} - }, - "layered_runtime": { - "layers": [ - { - "name": "global config", - "static_layer": {"envoy.deprecated_features:envoy.config.listener.v3.Listener.hidden_envoy_deprecated_use_original_dst":"true","envoy.reloadable_features.http_reject_path_with_fragment":"false","envoy.reloadable_features.no_extension_lookup_by_name":"false","envoy.reloadable_features.require_strict_1xx_and_204_response_headers":"false","overload.global_downstream_max_connections":"2147483647","re2.max_program_size.error_level":"32768"} - }, - { - "name": "admin", - "admin_layer": {} - } - ] - }, - "stats_config": { - "use_all_default_tags": false, - "stats_tags": [ - { - "tag_name": "cluster_name", - "regex": "^cluster\\.((.+?(\\..+?\\.svc\\.cluster\\.local)?)\\.)" - }, - { - "tag_name": "tcp_prefix", - "regex": "^tcp\\.((.*?)\\.)\\w+?$" - }, - { - "regex": "(response_code=\\.=(.+?);\\.;)|_rq(_(\\.d{3}))$", - "tag_name": "response_code" - }, - { - "tag_name": "response_code_class", - "regex": "_rq(_(\\dxx))$" - }, - { - "tag_name": "http_conn_manager_listener_prefix", - "regex": "^listener(?=\\.).*?\\.http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "http_conn_manager_prefix", - "regex": "^http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "listener_address", - "regex": "^listener\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "mongo_prefix", - "regex": "^mongo\\.(.+?)\\.(collection|cmd|cx_|op_|delays_|decoding_)(.*?)$" - }, - { - "regex": "(reporter=\\.=(.*?);\\.;)", - "tag_name": "reporter" - }, - { - "regex": "(source_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_namespace" - }, - { - "regex": "(source_workload=\\.=(.*?);\\.;)", - "tag_name": "source_workload" - }, - { - "regex": "(source_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_workload_namespace" - }, - { - "regex": "(source_principal=\\.=(.*?);\\.;)", - "tag_name": "source_principal" - }, - { - "regex": "(source_app=\\.=(.*?);\\.;)", - "tag_name": "source_app" - }, - { - "regex": "(source_version=\\.=(.*?);\\.;)", - "tag_name": "source_version" - }, - { - "regex": "(source_cluster=\\.=(.*?);\\.;)", - "tag_name": "source_cluster" - }, - { - "regex": "(destination_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_namespace" - }, - { - "regex": "(destination_workload=\\.=(.*?);\\.;)", - "tag_name": "destination_workload" - }, - { - "regex": "(destination_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_workload_namespace" - }, - { - "regex": "(destination_principal=\\.=(.*?);\\.;)", - "tag_name": "destination_principal" - }, - { - "regex": "(destination_app=\\.=(.*?);\\.;)", - "tag_name": "destination_app" - }, - { - "regex": "(destination_version=\\.=(.*?);\\.;)", - "tag_name": "destination_version" - }, - { - "regex": "(destination_service=\\.=(.*?);\\.;)", - "tag_name": "destination_service" - }, - { - "regex": "(destination_service_name=\\.=(.*?);\\.;)", - "tag_name": "destination_service_name" - }, - { - "regex": "(destination_service_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_service_namespace" - }, - { - "regex": "(destination_port=\\.=(.*?);\\.;)", - "tag_name": "destination_port" - }, - { - "regex": "(destination_cluster=\\.=(.*?);\\.;)", - "tag_name": "destination_cluster" - }, - { - "regex": "(request_protocol=\\.=(.*?);\\.;)", - "tag_name": "request_protocol" - }, - { - "regex": "(request_operation=\\.=(.*?);\\.;)", - "tag_name": "request_operation" - }, - { - "regex": "(request_host=\\.=(.*?);\\.;)", - "tag_name": "request_host" - }, - { - "regex": "(response_flags=\\.=(.*?);\\.;)", - "tag_name": "response_flags" - }, - { - "regex": "(grpc_response_status=\\.=(.*?);\\.;)", - "tag_name": "grpc_response_status" - }, - { - "regex": "(connection_security_policy=\\.=(.*?);\\.;)", - "tag_name": "connection_security_policy" - }, - { - "regex": "(source_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_service" - }, - { - "regex": "(destination_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_service" - }, - { - "regex": "(source_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_revision" - }, - { - "regex": "(destination_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_revision" - }, - { - "regex": "(cache\\.(.+?)\\.)", - "tag_name": "cache" - }, - { - "regex": "(component\\.(.+?)\\.)", - "tag_name": "component" - }, - { - "regex": "(tag\\.(.+?);\\.)", - "tag_name": "tag" - }, - { - "regex": "(wasm_filter\\.(.+?)\\.)", - "tag_name": "wasm_filter" - }, - { - "tag_name": "authz_enforce_result", - "regex": "rbac(\\.(allowed|denied))" - }, - { - "tag_name": "authz_dry_run_action", - "regex": "(\\.istio_dry_run_(allow|deny)_)" - }, - { - "tag_name": "authz_dry_run_result", - "regex": "(\\.shadow_(allowed|denied))" - } - ], - "stats_matcher": { - "inclusion_list": { - "patterns": [ - { - "prefix": "reporter=" - }, - { - "prefix": "cluster_manager" - }, - { - "prefix": "listener_manager" - }, - { - "prefix": "server" - }, - { - "prefix": "cluster.xds-grpc" - }, - { - "prefix": "wasm" - }, - { - "suffix": "rbac.allowed" - }, - { - "suffix": "rbac.denied" - }, - { - "suffix": "shadow_allowed" - }, - { - "suffix": "shadow_denied" - }, - { - "prefix": "component" - } - ] - } - } - }, - "admin": { - "access_log_path": "/dev/null", - "profile_path": "/var/lib/istio/data/envoy.prof", - "address": { - "socket_address": { - "address": "127.0.0.1", - "port_value": 15000 - } - } - }, - "dynamic_resources": { - "lds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "cds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "ads_config": { - "api_type": "GRPC", - "set_node_on_first_message_only": true, - "transport_api_version": "V3", - "grpc_services": [ - { - "envoy_grpc": { - "cluster_name": "xds-grpc" - } - } - ] - } - }, - "static_resources": { - "clusters": [ - { - "name": "prometheus_stats", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "prometheus_stats", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15000 - } - } - } - }] - }] - } - }, - { - "name": "agent", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "agent", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15020 - } - } - } - }] - }] - } - }, - { - "name": "sds-grpc", - "type": "STATIC", - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - }, - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "sds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "./var/run/secrets/workload-spiffe-uds/socket" - } - } - } - }] - }] - } - }, - { - "name": "xds-grpc", - "type" : "STATIC", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "xds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "/tmp/XDS" - } - } - } - }] - }] - }, - "circuit_breakers": { - "thresholds": [ - { - "priority": "DEFAULT", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - }, - { - "priority": "HIGH", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - } - ] - }, - "upstream_connection_options": { - "tcp_keepalive": { - "keepalive_time": 300 - } - }, - "max_requests_per_connection": 1, - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - } - } - - , - { - "name": "zipkin", - "transport_socket": {"name":"envoy.transport_sockets.tls","typed_config":{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext","common_tls_context":{"combined_validation_context":{"default_validation_context":{},"validation_context_sds_secret_config":{"name":"file-root:/etc/zipkin/ca.pem","sds_config":{"api_config_source":{"api_type":"GRPC","grpc_services":[{"envoy_grpc":{"cluster_name":"sds-grpc"}}],"set_node_on_first_message_only":true,"transport_api_version":"V3"},"resource_api_version":"V3"}}}},"sni":"zipkin-custom-sni"}}, - "type": "STRICT_DNS", - "respect_dns_ttl": true, - "dns_lookup_family": "V4_ONLY", - "dns_refresh_rate": "30s", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "zipkin", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": {"address": "localhost", "port_value": 6000} - } - } - }] - }] - } - } - - - ], - "listeners":[ - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15090 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "stats", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/stats/prometheus" - }, - "route": { - "cluster": "prometheus_stats" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - }, - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15021 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "agent", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/healthz/ready" - }, - "route": { - "cluster": "agent" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - } - ] - } - , - "tracing": { - "http": { - "name": "envoy.tracers.zipkin", - "typed_config": { - "@type": "type.googleapis.com/envoy.config.trace.v3.ZipkinConfig", - "collector_cluster": "zipkin", - "collector_endpoint": "/api/v2/spans", - "collector_endpoint_version": "HTTP_JSON", - "trace_id_128bit": true, - "shared_span_context": false - } - } - } - - - , - "cluster_manager": { - "outlier_detection": { - "event_log_path": "/dev/stdout" - } - } - -} diff --git a/pkg/bootstrap/testdata/tracing_tls_golden.json b/pkg/bootstrap/testdata/tracing_tls_golden.json deleted file mode 100644 index 91738b156..000000000 --- a/pkg/bootstrap/testdata/tracing_tls_golden.json +++ /dev/null @@ -1,550 +0,0 @@ -{ - "node": { - "id": "sidecar~1.2.3.4~foo~bar", - "cluster": "istio-proxy", - "locality": { - }, - "metadata": {"ENVOY_PROMETHEUS_PORT":15090,"ENVOY_STATUS_PORT":15021,"INSTANCE_IPS":"10.3.3.3,10.4.4.4,10.5.5.5,10.6.6.6","OUTLIER_LOG_PATH":"/dev/stdout","PILOT_SAN":["spiffe://cluster.local/ns/dubbo-system/sa/istio-pilot-service-account"],"PROXY_CONFIG":{"binaryPath":"/usr/local/bin/envoy","configPath":"/tmp/bootstrap/tracing_tls","customConfigFile":"envoy_bootstrap.json","discoveryAddress":"istio-pilot:15010","drainDuration":"2s","parentShutdownDuration":"3s","proxyAdminPort":15000,"serviceCluster":"istio-proxy","statusPort":15020,"tracing":{"tlsSettings":{"caCertificates":"/etc/zipkin/ca.pem","mode":"SIMPLE"},"zipkin":{"address":"localhost:6000"}}}} - }, - "layered_runtime": { - "layers": [ - { - "name": "global config", - "static_layer": {"envoy.deprecated_features:envoy.config.listener.v3.Listener.hidden_envoy_deprecated_use_original_dst":"true","envoy.reloadable_features.http_reject_path_with_fragment":"false","envoy.reloadable_features.no_extension_lookup_by_name":"false","envoy.reloadable_features.require_strict_1xx_and_204_response_headers":"false","overload.global_downstream_max_connections":"2147483647","re2.max_program_size.error_level":"32768"} - }, - { - "name": "admin", - "admin_layer": {} - } - ] - }, - "stats_config": { - "use_all_default_tags": false, - "stats_tags": [ - { - "tag_name": "cluster_name", - "regex": "^cluster\\.((.+?(\\..+?\\.svc\\.cluster\\.local)?)\\.)" - }, - { - "tag_name": "tcp_prefix", - "regex": "^tcp\\.((.*?)\\.)\\w+?$" - }, - { - "regex": "(response_code=\\.=(.+?);\\.;)|_rq(_(\\.d{3}))$", - "tag_name": "response_code" - }, - { - "tag_name": "response_code_class", - "regex": "_rq(_(\\dxx))$" - }, - { - "tag_name": "http_conn_manager_listener_prefix", - "regex": "^listener(?=\\.).*?\\.http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "http_conn_manager_prefix", - "regex": "^http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "listener_address", - "regex": "^listener\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "mongo_prefix", - "regex": "^mongo\\.(.+?)\\.(collection|cmd|cx_|op_|delays_|decoding_)(.*?)$" - }, - { - "regex": "(reporter=\\.=(.*?);\\.;)", - "tag_name": "reporter" - }, - { - "regex": "(source_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_namespace" - }, - { - "regex": "(source_workload=\\.=(.*?);\\.;)", - "tag_name": "source_workload" - }, - { - "regex": "(source_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_workload_namespace" - }, - { - "regex": "(source_principal=\\.=(.*?);\\.;)", - "tag_name": "source_principal" - }, - { - "regex": "(source_app=\\.=(.*?);\\.;)", - "tag_name": "source_app" - }, - { - "regex": "(source_version=\\.=(.*?);\\.;)", - "tag_name": "source_version" - }, - { - "regex": "(source_cluster=\\.=(.*?);\\.;)", - "tag_name": "source_cluster" - }, - { - "regex": "(destination_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_namespace" - }, - { - "regex": "(destination_workload=\\.=(.*?);\\.;)", - "tag_name": "destination_workload" - }, - { - "regex": "(destination_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_workload_namespace" - }, - { - "regex": "(destination_principal=\\.=(.*?);\\.;)", - "tag_name": "destination_principal" - }, - { - "regex": "(destination_app=\\.=(.*?);\\.;)", - "tag_name": "destination_app" - }, - { - "regex": "(destination_version=\\.=(.*?);\\.;)", - "tag_name": "destination_version" - }, - { - "regex": "(destination_service=\\.=(.*?);\\.;)", - "tag_name": "destination_service" - }, - { - "regex": "(destination_service_name=\\.=(.*?);\\.;)", - "tag_name": "destination_service_name" - }, - { - "regex": "(destination_service_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_service_namespace" - }, - { - "regex": "(destination_port=\\.=(.*?);\\.;)", - "tag_name": "destination_port" - }, - { - "regex": "(destination_cluster=\\.=(.*?);\\.;)", - "tag_name": "destination_cluster" - }, - { - "regex": "(request_protocol=\\.=(.*?);\\.;)", - "tag_name": "request_protocol" - }, - { - "regex": "(request_operation=\\.=(.*?);\\.;)", - "tag_name": "request_operation" - }, - { - "regex": "(request_host=\\.=(.*?);\\.;)", - "tag_name": "request_host" - }, - { - "regex": "(response_flags=\\.=(.*?);\\.;)", - "tag_name": "response_flags" - }, - { - "regex": "(grpc_response_status=\\.=(.*?);\\.;)", - "tag_name": "grpc_response_status" - }, - { - "regex": "(connection_security_policy=\\.=(.*?);\\.;)", - "tag_name": "connection_security_policy" - }, - { - "regex": "(source_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_service" - }, - { - "regex": "(destination_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_service" - }, - { - "regex": "(source_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_revision" - }, - { - "regex": "(destination_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_revision" - }, - { - "regex": "(cache\\.(.+?)\\.)", - "tag_name": "cache" - }, - { - "regex": "(component\\.(.+?)\\.)", - "tag_name": "component" - }, - { - "regex": "(tag\\.(.+?);\\.)", - "tag_name": "tag" - }, - { - "regex": "(wasm_filter\\.(.+?)\\.)", - "tag_name": "wasm_filter" - }, - { - "tag_name": "authz_enforce_result", - "regex": "rbac(\\.(allowed|denied))" - }, - { - "tag_name": "authz_dry_run_action", - "regex": "(\\.istio_dry_run_(allow|deny)_)" - }, - { - "tag_name": "authz_dry_run_result", - "regex": "(\\.shadow_(allowed|denied))" - } - ], - "stats_matcher": { - "inclusion_list": { - "patterns": [ - { - "prefix": "reporter=" - }, - { - "prefix": "cluster_manager" - }, - { - "prefix": "listener_manager" - }, - { - "prefix": "server" - }, - { - "prefix": "cluster.xds-grpc" - }, - { - "prefix": "wasm" - }, - { - "suffix": "rbac.allowed" - }, - { - "suffix": "rbac.denied" - }, - { - "suffix": "shadow_allowed" - }, - { - "suffix": "shadow_denied" - }, - { - "prefix": "component" - } - ] - } - } - }, - "admin": { - "access_log_path": "/dev/null", - "profile_path": "/var/lib/istio/data/envoy.prof", - "address": { - "socket_address": { - "address": "127.0.0.1", - "port_value": 15000 - } - } - }, - "dynamic_resources": { - "lds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "cds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "ads_config": { - "api_type": "GRPC", - "set_node_on_first_message_only": true, - "transport_api_version": "V3", - "grpc_services": [ - { - "envoy_grpc": { - "cluster_name": "xds-grpc" - } - } - ] - } - }, - "static_resources": { - "clusters": [ - { - "name": "prometheus_stats", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "prometheus_stats", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15000 - } - } - } - }] - }] - } - }, - { - "name": "agent", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "agent", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15020 - } - } - } - }] - }] - } - }, - { - "name": "sds-grpc", - "type": "STATIC", - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - }, - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "sds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "./var/run/secrets/workload-spiffe-uds/socket" - } - } - } - }] - }] - } - }, - { - "name": "xds-grpc", - "type" : "STATIC", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "xds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "/tmp/XDS" - } - } - } - }] - }] - }, - "circuit_breakers": { - "thresholds": [ - { - "priority": "DEFAULT", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - }, - { - "priority": "HIGH", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - } - ] - }, - "upstream_connection_options": { - "tcp_keepalive": { - "keepalive_time": 300 - } - }, - "max_requests_per_connection": 1, - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - } - } - - , - { - "name": "zipkin", - "transport_socket": {"name":"envoy.transport_sockets.tls","typed_config":{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext","common_tls_context":{"combined_validation_context":{"default_validation_context":{},"validation_context_sds_secret_config":{"name":"file-root:/etc/zipkin/ca.pem","sds_config":{"api_config_source":{"api_type":"GRPC","grpc_services":[{"envoy_grpc":{"cluster_name":"sds-grpc"}}],"set_node_on_first_message_only":true,"transport_api_version":"V3"},"resource_api_version":"V3"}}}}}}, - "type": "STRICT_DNS", - "respect_dns_ttl": true, - "dns_lookup_family": "V4_ONLY", - "dns_refresh_rate": "30s", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "zipkin", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": {"address": "localhost", "port_value": 6000} - } - } - }] - }] - } - } - - - ], - "listeners":[ - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15090 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "stats", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/stats/prometheus" - }, - "route": { - "cluster": "prometheus_stats" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - }, - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15021 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "agent", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/healthz/ready" - }, - "route": { - "cluster": "agent" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - } - ] - } - , - "tracing": { - "http": { - "name": "envoy.tracers.zipkin", - "typed_config": { - "@type": "type.googleapis.com/envoy.config.trace.v3.ZipkinConfig", - "collector_cluster": "zipkin", - "collector_endpoint": "/api/v2/spans", - "collector_endpoint_version": "HTTP_JSON", - "trace_id_128bit": true, - "shared_span_context": false - } - } - } - - - , - "cluster_manager": { - "outlier_detection": { - "event_log_path": "/dev/stdout" - } - } - -} diff --git a/pkg/bootstrap/testdata/tracing_zipkin.proxycfg b/pkg/bootstrap/testdata/tracing_zipkin.proxycfg deleted file mode 100644 index 2c8eb6642..000000000 --- a/pkg/bootstrap/testdata/tracing_zipkin.proxycfg +++ /dev/null @@ -1,11 +0,0 @@ -config_path: "/etc/istio/proxy" -binary_path: "/usr/local/bin/envoy" -service_cluster: "istio-proxy" -drain_duration: {seconds: 2} -parent_shutdown_duration: {seconds: 3} -discovery_address: "istio-pilot:15010" -proxy_admin_port: 15000 -control_plane_auth_policy: NONE -tracing: { zipkin: { address: "localhost:6000" } } - -# Sets all relevant options to values different than default diff --git a/pkg/bootstrap/testdata/tracing_zipkin_golden.json b/pkg/bootstrap/testdata/tracing_zipkin_golden.json deleted file mode 100644 index ba54abe5f..000000000 --- a/pkg/bootstrap/testdata/tracing_zipkin_golden.json +++ /dev/null @@ -1,549 +0,0 @@ -{ - "node": { - "id": "sidecar~1.2.3.4~foo~bar", - "cluster": "istio-proxy", - "locality": { - }, - "metadata": {"ENVOY_PROMETHEUS_PORT":15090,"ENVOY_STATUS_PORT":15021,"INSTANCE_IPS":"10.3.3.3,10.4.4.4,10.5.5.5,10.6.6.6","OUTLIER_LOG_PATH":"/dev/stdout","PILOT_SAN":["spiffe://cluster.local/ns/dubbo-system/sa/istio-pilot-service-account"],"PROXY_CONFIG":{"binaryPath":"/usr/local/bin/envoy","configPath":"/tmp/bootstrap/tracing_zipkin","customConfigFile":"envoy_bootstrap.json","discoveryAddress":"istio-pilot:15010","drainDuration":"2s","parentShutdownDuration":"3s","proxyAdminPort":15000,"serviceCluster":"istio-proxy","statusPort":15020,"tracing":{"zipkin":{"address":"localhost:6000"}}}} - }, - "layered_runtime": { - "layers": [ - { - "name": "global config", - "static_layer": {"envoy.deprecated_features:envoy.config.listener.v3.Listener.hidden_envoy_deprecated_use_original_dst":"true","envoy.reloadable_features.http_reject_path_with_fragment":"false","envoy.reloadable_features.no_extension_lookup_by_name":"false","envoy.reloadable_features.require_strict_1xx_and_204_response_headers":"false","overload.global_downstream_max_connections":"2147483647","re2.max_program_size.error_level":"32768"} - }, - { - "name": "admin", - "admin_layer": {} - } - ] - }, - "stats_config": { - "use_all_default_tags": false, - "stats_tags": [ - { - "tag_name": "cluster_name", - "regex": "^cluster\\.((.+?(\\..+?\\.svc\\.cluster\\.local)?)\\.)" - }, - { - "tag_name": "tcp_prefix", - "regex": "^tcp\\.((.*?)\\.)\\w+?$" - }, - { - "regex": "(response_code=\\.=(.+?);\\.;)|_rq(_(\\.d{3}))$", - "tag_name": "response_code" - }, - { - "tag_name": "response_code_class", - "regex": "_rq(_(\\dxx))$" - }, - { - "tag_name": "http_conn_manager_listener_prefix", - "regex": "^listener(?=\\.).*?\\.http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "http_conn_manager_prefix", - "regex": "^http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "listener_address", - "regex": "^listener\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "mongo_prefix", - "regex": "^mongo\\.(.+?)\\.(collection|cmd|cx_|op_|delays_|decoding_)(.*?)$" - }, - { - "regex": "(reporter=\\.=(.*?);\\.;)", - "tag_name": "reporter" - }, - { - "regex": "(source_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_namespace" - }, - { - "regex": "(source_workload=\\.=(.*?);\\.;)", - "tag_name": "source_workload" - }, - { - "regex": "(source_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_workload_namespace" - }, - { - "regex": "(source_principal=\\.=(.*?);\\.;)", - "tag_name": "source_principal" - }, - { - "regex": "(source_app=\\.=(.*?);\\.;)", - "tag_name": "source_app" - }, - { - "regex": "(source_version=\\.=(.*?);\\.;)", - "tag_name": "source_version" - }, - { - "regex": "(source_cluster=\\.=(.*?);\\.;)", - "tag_name": "source_cluster" - }, - { - "regex": "(destination_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_namespace" - }, - { - "regex": "(destination_workload=\\.=(.*?);\\.;)", - "tag_name": "destination_workload" - }, - { - "regex": "(destination_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_workload_namespace" - }, - { - "regex": "(destination_principal=\\.=(.*?);\\.;)", - "tag_name": "destination_principal" - }, - { - "regex": "(destination_app=\\.=(.*?);\\.;)", - "tag_name": "destination_app" - }, - { - "regex": "(destination_version=\\.=(.*?);\\.;)", - "tag_name": "destination_version" - }, - { - "regex": "(destination_service=\\.=(.*?);\\.;)", - "tag_name": "destination_service" - }, - { - "regex": "(destination_service_name=\\.=(.*?);\\.;)", - "tag_name": "destination_service_name" - }, - { - "regex": "(destination_service_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_service_namespace" - }, - { - "regex": "(destination_port=\\.=(.*?);\\.;)", - "tag_name": "destination_port" - }, - { - "regex": "(destination_cluster=\\.=(.*?);\\.;)", - "tag_name": "destination_cluster" - }, - { - "regex": "(request_protocol=\\.=(.*?);\\.;)", - "tag_name": "request_protocol" - }, - { - "regex": "(request_operation=\\.=(.*?);\\.;)", - "tag_name": "request_operation" - }, - { - "regex": "(request_host=\\.=(.*?);\\.;)", - "tag_name": "request_host" - }, - { - "regex": "(response_flags=\\.=(.*?);\\.;)", - "tag_name": "response_flags" - }, - { - "regex": "(grpc_response_status=\\.=(.*?);\\.;)", - "tag_name": "grpc_response_status" - }, - { - "regex": "(connection_security_policy=\\.=(.*?);\\.;)", - "tag_name": "connection_security_policy" - }, - { - "regex": "(source_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_service" - }, - { - "regex": "(destination_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_service" - }, - { - "regex": "(source_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_revision" - }, - { - "regex": "(destination_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_revision" - }, - { - "regex": "(cache\\.(.+?)\\.)", - "tag_name": "cache" - }, - { - "regex": "(component\\.(.+?)\\.)", - "tag_name": "component" - }, - { - "regex": "(tag\\.(.+?);\\.)", - "tag_name": "tag" - }, - { - "regex": "(wasm_filter\\.(.+?)\\.)", - "tag_name": "wasm_filter" - }, - { - "tag_name": "authz_enforce_result", - "regex": "rbac(\\.(allowed|denied))" - }, - { - "tag_name": "authz_dry_run_action", - "regex": "(\\.istio_dry_run_(allow|deny)_)" - }, - { - "tag_name": "authz_dry_run_result", - "regex": "(\\.shadow_(allowed|denied))" - } - ], - "stats_matcher": { - "inclusion_list": { - "patterns": [ - { - "prefix": "reporter=" - }, - { - "prefix": "cluster_manager" - }, - { - "prefix": "listener_manager" - }, - { - "prefix": "server" - }, - { - "prefix": "cluster.xds-grpc" - }, - { - "prefix": "wasm" - }, - { - "suffix": "rbac.allowed" - }, - { - "suffix": "rbac.denied" - }, - { - "suffix": "shadow_allowed" - }, - { - "suffix": "shadow_denied" - }, - { - "prefix": "component" - } - ] - } - } - }, - "admin": { - "access_log_path": "/dev/null", - "profile_path": "/var/lib/istio/data/envoy.prof", - "address": { - "socket_address": { - "address": "127.0.0.1", - "port_value": 15000 - } - } - }, - "dynamic_resources": { - "lds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "cds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "ads_config": { - "api_type": "GRPC", - "set_node_on_first_message_only": true, - "transport_api_version": "V3", - "grpc_services": [ - { - "envoy_grpc": { - "cluster_name": "xds-grpc" - } - } - ] - } - }, - "static_resources": { - "clusters": [ - { - "name": "prometheus_stats", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "prometheus_stats", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15000 - } - } - } - }] - }] - } - }, - { - "name": "agent", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "agent", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15020 - } - } - } - }] - }] - } - }, - { - "name": "sds-grpc", - "type": "STATIC", - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - }, - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "sds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "./var/run/secrets/workload-spiffe-uds/socket" - } - } - } - }] - }] - } - }, - { - "name": "xds-grpc", - "type" : "STATIC", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "xds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "/tmp/XDS" - } - } - } - }] - }] - }, - "circuit_breakers": { - "thresholds": [ - { - "priority": "DEFAULT", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - }, - { - "priority": "HIGH", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - } - ] - }, - "upstream_connection_options": { - "tcp_keepalive": { - "keepalive_time": 300 - } - }, - "max_requests_per_connection": 1, - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - } - } - - , - { - "name": "zipkin", - "type": "STRICT_DNS", - "respect_dns_ttl": true, - "dns_lookup_family": "V4_ONLY", - "dns_refresh_rate": "30s", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "zipkin", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": {"address": "localhost", "port_value": 6000} - } - } - }] - }] - } - } - - - ], - "listeners":[ - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15090 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "stats", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/stats/prometheus" - }, - "route": { - "cluster": "prometheus_stats" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - }, - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15021 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "agent", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/healthz/ready" - }, - "route": { - "cluster": "agent" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - } - ] - } - , - "tracing": { - "http": { - "name": "envoy.tracers.zipkin", - "typed_config": { - "@type": "type.googleapis.com/envoy.config.trace.v3.ZipkinConfig", - "collector_cluster": "zipkin", - "collector_endpoint": "/api/v2/spans", - "collector_endpoint_version": "HTTP_JSON", - "trace_id_128bit": true, - "shared_span_context": false - } - } - } - - - , - "cluster_manager": { - "outlier_detection": { - "event_log_path": "/dev/stdout" - } - } - -} diff --git a/pkg/bootstrap/testdata/xdsproxy.proxycfg b/pkg/bootstrap/testdata/xdsproxy.proxycfg deleted file mode 100644 index 3c62902a1..000000000 --- a/pkg/bootstrap/testdata/xdsproxy.proxycfg +++ /dev/null @@ -1,12 +0,0 @@ -config_path: "/etc/istio/proxy" -binary_path: "/usr/local/bin/envoy" -service_cluster: "istio-proxy" -drain_duration: {seconds: 2} -parent_shutdown_duration: {seconds: 3} -discovery_address: "istio-pilot:15010" -proxy_admin_port: 15000 -control_plane_auth_policy: NONE - -# -# This matches the default configuration hardcoded in model.DefaultProxyConfig -# Flags may override this configuration, as specified by the injector configs. diff --git a/pkg/bootstrap/testdata/xdsproxy_golden.json b/pkg/bootstrap/testdata/xdsproxy_golden.json deleted file mode 100644 index c2837c3c9..000000000 --- a/pkg/bootstrap/testdata/xdsproxy_golden.json +++ /dev/null @@ -1,512 +0,0 @@ -{ - "node": { - "id": "sidecar~1.2.3.4~foo~bar", - "cluster": "istio-proxy", - "locality": { - }, - "metadata": {"ENVOY_PROMETHEUS_PORT":15090,"ENVOY_STATUS_PORT":15021,"INSTANCE_IPS":"10.3.3.3,10.4.4.4,10.5.5.5,10.6.6.6","OUTLIER_LOG_PATH":"/dev/stdout","PILOT_SAN":["spiffe://cluster.local/ns/dubbo-system/sa/istio-pilot-service-account"],"PROXY_CONFIG":{"binaryPath":"/usr/local/bin/envoy","configPath":"/tmp/bootstrap/xdsproxy","customConfigFile":"envoy_bootstrap.json","discoveryAddress":"istio-pilot:15010","drainDuration":"2s","parentShutdownDuration":"3s","proxyAdminPort":15000,"serviceCluster":"istio-proxy","statusPort":15020}} - }, - "layered_runtime": { - "layers": [ - { - "name": "global config", - "static_layer": {"envoy.deprecated_features:envoy.config.listener.v3.Listener.hidden_envoy_deprecated_use_original_dst":"true","envoy.reloadable_features.http_reject_path_with_fragment":"false","envoy.reloadable_features.no_extension_lookup_by_name":"false","envoy.reloadable_features.require_strict_1xx_and_204_response_headers":"false","overload.global_downstream_max_connections":"2147483647","re2.max_program_size.error_level":"32768"} - }, - { - "name": "admin", - "admin_layer": {} - } - ] - }, - "stats_config": { - "use_all_default_tags": false, - "stats_tags": [ - { - "tag_name": "cluster_name", - "regex": "^cluster\\.((.+?(\\..+?\\.svc\\.cluster\\.local)?)\\.)" - }, - { - "tag_name": "tcp_prefix", - "regex": "^tcp\\.((.*?)\\.)\\w+?$" - }, - { - "regex": "(response_code=\\.=(.+?);\\.;)|_rq(_(\\.d{3}))$", - "tag_name": "response_code" - }, - { - "tag_name": "response_code_class", - "regex": "_rq(_(\\dxx))$" - }, - { - "tag_name": "http_conn_manager_listener_prefix", - "regex": "^listener(?=\\.).*?\\.http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "http_conn_manager_prefix", - "regex": "^http\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "listener_address", - "regex": "^listener\\.(((?:[_.[:digit:]]*|[_\\[\\]aAbBcCdDeEfF[:digit:]]*))\\.)" - }, - { - "tag_name": "mongo_prefix", - "regex": "^mongo\\.(.+?)\\.(collection|cmd|cx_|op_|delays_|decoding_)(.*?)$" - }, - { - "regex": "(reporter=\\.=(.*?);\\.;)", - "tag_name": "reporter" - }, - { - "regex": "(source_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_namespace" - }, - { - "regex": "(source_workload=\\.=(.*?);\\.;)", - "tag_name": "source_workload" - }, - { - "regex": "(source_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "source_workload_namespace" - }, - { - "regex": "(source_principal=\\.=(.*?);\\.;)", - "tag_name": "source_principal" - }, - { - "regex": "(source_app=\\.=(.*?);\\.;)", - "tag_name": "source_app" - }, - { - "regex": "(source_version=\\.=(.*?);\\.;)", - "tag_name": "source_version" - }, - { - "regex": "(source_cluster=\\.=(.*?);\\.;)", - "tag_name": "source_cluster" - }, - { - "regex": "(destination_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_namespace" - }, - { - "regex": "(destination_workload=\\.=(.*?);\\.;)", - "tag_name": "destination_workload" - }, - { - "regex": "(destination_workload_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_workload_namespace" - }, - { - "regex": "(destination_principal=\\.=(.*?);\\.;)", - "tag_name": "destination_principal" - }, - { - "regex": "(destination_app=\\.=(.*?);\\.;)", - "tag_name": "destination_app" - }, - { - "regex": "(destination_version=\\.=(.*?);\\.;)", - "tag_name": "destination_version" - }, - { - "regex": "(destination_service=\\.=(.*?);\\.;)", - "tag_name": "destination_service" - }, - { - "regex": "(destination_service_name=\\.=(.*?);\\.;)", - "tag_name": "destination_service_name" - }, - { - "regex": "(destination_service_namespace=\\.=(.*?);\\.;)", - "tag_name": "destination_service_namespace" - }, - { - "regex": "(destination_port=\\.=(.*?);\\.;)", - "tag_name": "destination_port" - }, - { - "regex": "(destination_cluster=\\.=(.*?);\\.;)", - "tag_name": "destination_cluster" - }, - { - "regex": "(request_protocol=\\.=(.*?);\\.;)", - "tag_name": "request_protocol" - }, - { - "regex": "(request_operation=\\.=(.*?);\\.;)", - "tag_name": "request_operation" - }, - { - "regex": "(request_host=\\.=(.*?);\\.;)", - "tag_name": "request_host" - }, - { - "regex": "(response_flags=\\.=(.*?);\\.;)", - "tag_name": "response_flags" - }, - { - "regex": "(grpc_response_status=\\.=(.*?);\\.;)", - "tag_name": "grpc_response_status" - }, - { - "regex": "(connection_security_policy=\\.=(.*?);\\.;)", - "tag_name": "connection_security_policy" - }, - { - "regex": "(source_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_service" - }, - { - "regex": "(destination_canonical_service=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_service" - }, - { - "regex": "(source_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "source_canonical_revision" - }, - { - "regex": "(destination_canonical_revision=\\.=(.*?);\\.;)", - "tag_name": "destination_canonical_revision" - }, - { - "regex": "(cache\\.(.+?)\\.)", - "tag_name": "cache" - }, - { - "regex": "(component\\.(.+?)\\.)", - "tag_name": "component" - }, - { - "regex": "(tag\\.(.+?);\\.)", - "tag_name": "tag" - }, - { - "regex": "(wasm_filter\\.(.+?)\\.)", - "tag_name": "wasm_filter" - }, - { - "tag_name": "authz_enforce_result", - "regex": "rbac(\\.(allowed|denied))" - }, - { - "tag_name": "authz_dry_run_action", - "regex": "(\\.istio_dry_run_(allow|deny)_)" - }, - { - "tag_name": "authz_dry_run_result", - "regex": "(\\.shadow_(allowed|denied))" - } - ], - "stats_matcher": { - "inclusion_list": { - "patterns": [ - { - "prefix": "reporter=" - }, - { - "prefix": "cluster_manager" - }, - { - "prefix": "listener_manager" - }, - { - "prefix": "server" - }, - { - "prefix": "cluster.xds-grpc" - }, - { - "prefix": "wasm" - }, - { - "suffix": "rbac.allowed" - }, - { - "suffix": "rbac.denied" - }, - { - "suffix": "shadow_allowed" - }, - { - "suffix": "shadow_denied" - }, - { - "prefix": "component" - } - ] - } - } - }, - "admin": { - "access_log_path": "/dev/null", - "profile_path": "/var/lib/istio/data/envoy.prof", - "address": { - "socket_address": { - "address": "127.0.0.1", - "port_value": 15000 - } - } - }, - "dynamic_resources": { - "lds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "cds_config": { - "ads": {}, - "initial_fetch_timeout": "0s", - "resource_api_version": "V3" - }, - "ads_config": { - "api_type": "GRPC", - "set_node_on_first_message_only": true, - "transport_api_version": "V3", - "grpc_services": [ - { - "envoy_grpc": { - "cluster_name": "xds-grpc" - } - } - ] - } - }, - "static_resources": { - "clusters": [ - { - "name": "prometheus_stats", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "prometheus_stats", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15000 - } - } - } - }] - }] - } - }, - { - "name": "agent", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "agent", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "protocol": "TCP", - "address": "127.0.0.1", - "port_value": 15020 - } - } - } - }] - }] - } - }, - { - "name": "sds-grpc", - "type": "STATIC", - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - }, - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "sds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "./var/run/secrets/workload-spiffe-uds/socket" - } - } - } - }] - }] - } - }, - { - "name": "xds-grpc", - "type" : "STATIC", - "connect_timeout": "1s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "xds-grpc", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "pipe": { - "path": "/tmp/XDS" - } - } - } - }] - }] - }, - "circuit_breakers": { - "thresholds": [ - { - "priority": "DEFAULT", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - }, - { - "priority": "HIGH", - "max_connections": 100000, - "max_pending_requests": 100000, - "max_requests": 100000 - } - ] - }, - "upstream_connection_options": { - "tcp_keepalive": { - "keepalive_time": 300 - } - }, - "max_requests_per_connection": 1, - "typed_extension_protocol_options": { - "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { - "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", - "explicit_http_config": { - "http2_protocol_options": {} - } - } - } - } - - - ], - "listeners":[ - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15090 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "stats", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/stats/prometheus" - }, - "route": { - "cluster": "prometheus_stats" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - }, - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": 15021 - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "agent", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/healthz/ready" - }, - "route": { - "cluster": "agent" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - } - ] - } - - - , - "cluster_manager": { - "outlier_detection": { - "event_log_path": "/dev/stdout" - } - } - -} diff --git a/pkg/channels/unbounded.go b/pkg/channels/unbounded.go deleted file mode 100644 index 67b218126..000000000 --- a/pkg/channels/unbounded.go +++ /dev/null @@ -1,91 +0,0 @@ -// Package buffer provides an implementation of an unbounded buffer. -package channels - -// Heavily inspired by the private library from gRPC (https://raw.githubusercontent.com/grpc/grpc-go/master/internal/buffer/unbounded.go) -// Since it cannot be imported directly it is mirror here. Original license: -/* - * Copyright 2019 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import ( - "sync" -) - -// Unbounded is an implementation of an unbounded buffer which does not use -// extra goroutines. This is typically used for passing updates from one entity -// to another within gRPC. -// -// All methods on this type are thread-safe and don't block on anything except -// the underlying mutex used for synchronization. -// -// Unbounded supports values of any type to be stored in it by using a channel -// of `interface{}`. This means that a call to Put() incurs an extra memory -// allocation, and also that users need a type assertion while reading. For -// performance critical code paths, using Unbounded is strongly discouraged and -// defining a new type specific implementation of this buffer is preferred. See -// internal/transport/transport.go for an example of this. -type Unbounded struct { - c chan any - mu sync.Mutex - backlog []any -} - -// NewUnbounded returns a new instance of Unbounded. -func NewUnbounded() *Unbounded { - return &Unbounded{c: make(chan any, 1)} -} - -// Put adds t to the unbounded buffer. -// Put will never block -func (b *Unbounded) Put(t any) { - b.mu.Lock() - if len(b.backlog) == 0 { - select { - case b.c <- t: - b.mu.Unlock() - return - default: - } - } - b.backlog = append(b.backlog, t) - b.mu.Unlock() -} - -// Load sends the earliest buffered data, if any, onto the read channel -// returned by Get(). Users are expected to call this every time they read a -// value from the read channel. -func (b *Unbounded) Load() { - b.mu.Lock() - if len(b.backlog) > 0 { - n := new(any) - select { - case b.c <- b.backlog[0]: - b.backlog[0] = *n - b.backlog = b.backlog[1:] - default: - } - } - b.mu.Unlock() -} - -// Get returns a read channel on which values added to the buffer, via Put(), -// are sent on. -// -// Upon reading a value from this channel, users are expected to call Load() to -// send the next buffered value onto the channel if there is any. -func (b *Unbounded) Get() <-chan any { - return b.c -} diff --git a/pkg/channels/unbounded_test.go b/pkg/channels/unbounded_test.go deleted file mode 100644 index 4c4d0d242..000000000 --- a/pkg/channels/unbounded_test.go +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2019 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package channels - -import ( - "reflect" - "sort" - "sync" - "testing" -) - -const ( - numWriters = 10 - numWrites = 10 -) - -// wantReads contains the set of values expected to be read by the reader -// goroutine in the tests. -var wantReads []int - -func init() { - for i := 0; i < numWriters; i++ { - for j := 0; j < numWrites; j++ { - wantReads = append(wantReads, i) - } - } -} - -// TestSingleWriter starts one reader and one writer goroutine and makes sure -// that the reader gets all the value added to the buffer by the writer. -func TestSingleWriter(t *testing.T) { - ub := NewUnbounded() - reads := []int{} - - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - ch := ub.Get() - for i := 0; i < numWriters*numWrites; i++ { - r := <-ch - reads = append(reads, r.(int)) - ub.Load() - } - }() - - wg.Add(1) - go func() { - defer wg.Done() - for i := 0; i < numWriters; i++ { - for j := 0; j < numWrites; j++ { - ub.Put(i) - } - } - }() - - wg.Wait() - if !reflect.DeepEqual(reads, wantReads) { - t.Errorf("reads: %#v, wantReads: %#v", reads, wantReads) - } -} - -// TestMultipleWriters starts multiple writers and one reader goroutine and -// makes sure that the reader gets all the data written by all writers. -func TestMultipleWriters(t *testing.T) { - ub := NewUnbounded() - reads := []int{} - - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - ch := ub.Get() - for i := 0; i < numWriters*numWrites; i++ { - r := <-ch - reads = append(reads, r.(int)) - ub.Load() - } - }() - - wg.Add(numWriters) - for i := 0; i < numWriters; i++ { - go func(index int) { - defer wg.Done() - for j := 0; j < numWrites; j++ { - ub.Put(index) - } - }(i) - } - - wg.Wait() - sort.Ints(reads) - if !reflect.DeepEqual(reads, wantReads) { - t.Errorf("reads: %#v, wantReads: %#v", reads, wantReads) - } -} diff --git a/pixiu/pkg/client/client.go b/pkg/client/client.go similarity index 100% rename from pixiu/pkg/client/client.go rename to pkg/client/client.go diff --git a/pkg/client/dubbo/config.go b/pkg/client/dubbo/config.go new file mode 100644 index 000000000..c4055913c --- /dev/null +++ b/pkg/client/dubbo/config.go @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 dubbo + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +// DubboProxyConfig the config for dubbo proxy +type DubboProxyConfig struct { + // Registries such as zk,nacos or etcd + Registries map[string]model.Registry `yaml:"registries" json:"registries"` + // Timeout + Timeout *model.TimeoutConfig `yaml:"timeout_config" json:"timeout_config"` + // IsDefaultMap whether to use DefaultMap role + IsDefaultMap bool + // AutoResolve whether to resolve api config from request + AutoResolve bool `yaml:"auto_resolve" json:"auto_resolve,omitempty"` + // Protoset path to load protoset files + Protoset []string `yaml:"protoset" json:"protoset,omitempty"` + // Load + LoadBalance string `yaml:"load_balance" json:"load_balance,omitempty"` +} diff --git a/pixiu/pkg/client/dubbo/default.go b/pkg/client/dubbo/default.go similarity index 100% rename from pixiu/pkg/client/dubbo/default.go rename to pkg/client/dubbo/default.go diff --git a/pkg/client/dubbo/dubbo.go b/pkg/client/dubbo/dubbo.go new file mode 100644 index 000000000..ad323caf2 --- /dev/null +++ b/pkg/client/dubbo/dubbo.go @@ -0,0 +1,360 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 dubbo + +import ( + "context" + "encoding/json" + "strings" + "sync" + "time" +) + +import ( + _ "dubbo.apache.org/dubbo-go/v3/cluster/cluster/failover" + _ "dubbo.apache.org/dubbo-go/v3/cluster/loadbalance/consistenthashing" + _ "dubbo.apache.org/dubbo-go/v3/cluster/loadbalance/leastactive" + _ "dubbo.apache.org/dubbo-go/v3/cluster/loadbalance/p2c" + _ "dubbo.apache.org/dubbo-go/v3/cluster/loadbalance/random" + _ "dubbo.apache.org/dubbo-go/v3/cluster/loadbalance/ringhash" + _ "dubbo.apache.org/dubbo-go/v3/cluster/loadbalance/roundrobin" + "dubbo.apache.org/dubbo-go/v3/common/constant" + _ "dubbo.apache.org/dubbo-go/v3/common/proxy/proxy_factory" + dg "dubbo.apache.org/dubbo-go/v3/config" + "dubbo.apache.org/dubbo-go/v3/config/generic" + _ "dubbo.apache.org/dubbo-go/v3/filter/generic" + _ "dubbo.apache.org/dubbo-go/v3/filter/graceful_shutdown" + _ "dubbo.apache.org/dubbo-go/v3/metadata/service/local" + "dubbo.apache.org/dubbo-go/v3/protocol/dubbo" + _ "dubbo.apache.org/dubbo-go/v3/registry/protocol" + _ "dubbo.apache.org/dubbo-go/v3/registry/zookeeper" + hessian "github.com/apache/dubbo-go-hessian2" + fc "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" + "github.com/pkg/errors" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/trace" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/client" + cst "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/config" + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +// TODO java class name elem +const ( + JavaStringClassName = "java.lang.String" + JavaLangClassName = "java.lang.Long" +) + +const ( + defaultDubboProtocol = "zookeeper" + + traceNameDubbogoClient = "dubbogo-client" + spanNameDubbogoClient = "DUBBOGO CLIENT" + + spanTagMethod = "method" + spanTagType = "type" + spanTagValues = "values" +) + +var ( + dubboClient *Client + onceClient = sync.Once{} + defaultApplication = &dg.ApplicationConfig{ + Organization: "dubbo-go-pixiu", + Name: "Dubbogo Pixiu", + Module: "dubbogo Pixiu", + Version: config.Version, + Owner: "Dubbogo Pixiu", + Environment: "dev", + } +) + +// Client client to generic invoke dubbo +type Client struct { + lock sync.RWMutex + GenericServicePool map[string]*generic.GenericService + dubboProxyConfig *DubboProxyConfig + rootConfig *dg.RootConfig +} + +// SingletonDubboClient singleton dubbo clent +func SingletonDubboClient() *Client { + if dubboClient == nil { + onceClient.Do(func() { + dubboClient = NewDubboClient() + }) + } + + return dubboClient +} + +// InitDefaultDubboClient init default dubbo client +func InitDefaultDubboClient(dpc *DubboProxyConfig) { + dubboClient = NewDubboClient() + dubboClient.SetConfig(dpc) + if err := dubboClient.Apply(); err != nil { + logger.Warnf("dubbo client apply error %s", err) + } +} + +// NewDubboClient create dubbo client +func NewDubboClient() *Client { + return &Client{ + lock: sync.RWMutex{}, + GenericServicePool: make(map[string]*generic.GenericService, 4), + } +} + +// SetConfig set config +func (dc *Client) SetConfig(dpc *DubboProxyConfig) { + dc.dubboProxyConfig = dpc +} + +// Apply init dubbo, config mapping can do here +func (dc *Client) Apply() error { + + rootConfigBuilder := dg.NewRootConfigBuilder() + if dc.dubboProxyConfig != nil && dc.dubboProxyConfig.Registries != nil { + for k, v := range dc.dubboProxyConfig.Registries { + if len(v.Protocol) == 0 { + logger.Warnf("can not find registry protocol config, use default type 'zookeeper'") + v.Protocol = defaultDubboProtocol + } + rootConfigBuilder.AddRegistry(k, &dg.RegistryConfig{ + Protocol: v.Protocol, + Address: v.Address, + Timeout: v.Timeout, + Username: v.Username, + Password: v.Password, + Namespace: v.Namespace, + Group: v.Group, + }) + } + } + rootConfigBuilder.SetApplication(defaultApplication) + rootConfig := rootConfigBuilder.Build() + + if err := dg.Load(dg.WithRootConfig(rootConfig)); err != nil { + panic(err) + } + dc.rootConfig = rootConfig + return nil +} + +// Close clear GenericServicePool. +func (dc *Client) Close() error { + dc.lock.Lock() + defer dc.lock.Unlock() + for k := range dc.GenericServicePool { + delete(dc.GenericServicePool, k) + } + return nil +} + +// Call invoke service +func (dc *Client) Call(req *client.Request) (res interface{}, err error) { + // if GET with no args, values would be nil + values, err := dc.genericArgs(req) + if err != nil { + return nil, err + } + target, ok := values.(*dubboTarget) + if !ok { + return nil, errors.New("map parameters failed") + } + + dm := req.API.Method.IntegrationRequest + method := dm.Method + types := []string{} + vals := []hessian.Object{} + finalValues := []byte{} + + if target != nil { + logger.Debugf("[dubbo-go-pixiu] dubbo invoke, method:%s, types:%s, reqData:%v", method, target.Types, target.Values) + types = target.Types + vals = make([]hessian.Object, len(target.Values)) + for i, v := range target.Values { + vals[i] = v + } + var err error + finalValues, err = json.Marshal(vals) + if err != nil { + logger.Warnf("[dubbo-go-pixiu] reqData convert to string failed: %v", err) + } + } else { + logger.Debugf("[dubbo-go-pixiu] dubbo invoke, method:%s, types:%s, reqData:%v", method, nil, nil) + } + + gs := dc.Get(dm) + tr := otel.Tracer(traceNameDubbogoClient) + ctx, span := tr.Start(req.Context, spanNameDubbogoClient) + trace.SpanFromContext(req.Context).SpanContext() + span.SetAttributes(attribute.Key(spanTagMethod).String(method)) + span.SetAttributes(attribute.Key(spanTagType).StringSlice(types)) + span.SetAttributes(attribute.Key(spanTagValues).String(string(finalValues))) + defer span.End() + + // tracing inject manually; + carrier := propagation.MapCarrier{} + otel.GetTextMapPropagator().Inject(ctx, carrier) + ctxWithAttachment := context.WithValue(ctx, constant.AttachmentKey, map[string]string(carrier)) + + rst, err := gs.Invoke(ctxWithAttachment, method, types, vals) + if err != nil { + // TODO statusCode I don’t know what dubbo will return when it times out, so I will return it directly. I will judge it when I call it. + span.RecordError(err) + return nil, err + } + + logger.Debugf("[dubbo-go-pixiu] dubbo client resp:%v", rst) + + return rst, nil +} + +func (dc *Client) genericArgs(req *client.Request) (interface{}, error) { + values, err := dc.MapParams(req) + if err != nil { + return nil, err + } + + return values, nil +} + +// MapParams params mapping to api. +func (dc *Client) MapParams(req *client.Request) (interface{}, error) { + r := req.API.Method.IntegrationRequest + values := newDubboTarget(r.MappingParams) + if dc.dubboProxyConfig != nil && dc.dubboProxyConfig.IsDefaultMap { + values = newDubboTarget(defaultMappingParams) + } + for _, mappingParam := range r.MappingParams { + source, _, err := client.ParseMapSource(mappingParam.Name) + if err != nil { + return nil, err + } + if mapper, ok := mappers[source]; ok { + if err := mapper.Map(mappingParam, req, values, buildOption(mappingParam)); err != nil { + return nil, err + } + } + } + return values, nil +} + +func buildOption(conf fc.MappingParam) client.RequestOption { + var opt client.RequestOption + isGeneric, mapToType := getGenericMapTo(conf.MapTo) + if isGeneric { + opt = DefaultMapOption[mapToType] + } + return opt +} + +func (dc *Client) get(key string) *generic.GenericService { + dc.lock.RLock() + defer dc.lock.RUnlock() + return dc.GenericServicePool[key] +} + +func (dc *Client) check(key string) bool { + dc.lock.RLock() + defer dc.lock.RUnlock() + if _, ok := dc.GenericServicePool[key]; ok { + return true + } + return false +} + +// Get find a dubbo GenericService +func (dc *Client) Get(ir fc.IntegrationRequest) *generic.GenericService { + key := apiKey(&ir) + if dc.check(key) { + return dc.get(key) + } + + return dc.create(key, ir) +} + +func apiKey(ir *fc.IntegrationRequest) string { + dbc := ir.DubboBackendConfig + return strings.Join([]string{dbc.ClusterName, dbc.ApplicationName, dbc.Interface, dbc.Version, dbc.Group}, "_") +} + +func (dc *Client) create(key string, irequest fc.IntegrationRequest) *generic.GenericService { + useNacosRegister := false + registerIds := make([]string, 0) + for k, v := range dc.rootConfig.Registries { + registerIds = append(registerIds, k) + if v.Protocol == "nacos" { + useNacosRegister = true + } + } + + refConf := dg.ReferenceConfig{ + InterfaceName: irequest.Interface, + Cluster: constant.ClusterKeyFailover, + RegistryIDs: registerIds, + Protocol: dubbo.DUBBO, + Generic: "true", + Version: irequest.DubboBackendConfig.Version, + Group: irequest.Group, + Loadbalance: dc.dubboProxyConfig.LoadBalance, + } + + if len(irequest.DubboBackendConfig.Retries) == 0 { + refConf.Retries = "3" + } else { + refConf.Retries = irequest.DubboBackendConfig.Retries + } + + if dc.dubboProxyConfig.Timeout != nil { + refConf.RequestTimeout = dc.dubboProxyConfig.Timeout.RequestTimeoutStr + } else { + refConf.RequestTimeout = cst.DefaultReqTimeout.String() + } + logger.Debugf("[dubbo-go-pixiu] client dubbo timeout val %v", refConf.RequestTimeout) + dc.lock.Lock() + defer dc.lock.Unlock() + + if service, ok := dc.GenericServicePool[key]; ok { + return service + } + + if err := dg.Load(dg.WithRootConfig(dc.rootConfig)); err != nil { + panic(err) + } + + _ = refConf.Init(dc.rootConfig) + refConf.GenericLoad(key) + + // sleep when first call to fetch enough service meta data from nacos + // todo: GenericLoad should guarantee it + if useNacosRegister { + time.Sleep(1000 * time.Millisecond) + } + + clientService := refConf.GetRPCService().(*generic.GenericService) + dc.GenericServicePool[key] = clientService + + return clientService +} diff --git a/pixiu/pkg/client/dubbo/dubbo_test.go b/pkg/client/dubbo/dubbo_test.go similarity index 98% rename from pixiu/pkg/client/dubbo/dubbo_test.go rename to pkg/client/dubbo/dubbo_test.go index 5bee2c973..8dd2d16c1 100644 --- a/pixiu/pkg/client/dubbo/dubbo_test.go +++ b/pkg/client/dubbo/dubbo_test.go @@ -31,8 +31,8 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/mock" + "github.com/apache/dubbo-go-pixiu/pkg/client" + "github.com/apache/dubbo-go-pixiu/pkg/common/mock" ) func TestReg(t *testing.T) { diff --git a/pkg/client/dubbo/mapper.go b/pkg/client/dubbo/mapper.go new file mode 100644 index 000000000..d6ed7fc36 --- /dev/null +++ b/pkg/client/dubbo/mapper.go @@ -0,0 +1,279 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 dubbo + +import ( + "bytes" + "encoding/json" + "io" + "net/url" + "reflect" + "strconv" + "strings" + "time" +) + +import ( + "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" + "github.com/pkg/errors" + "github.com/spf13/cast" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/client" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/router" +) + +var mappers = map[string]client.ParamMapper{ + constant.QueryStrings: queryStringsMapper{}, + constant.Headers: headerMapper{}, + constant.RequestBody: bodyMapper{}, + constant.RequestURI: uriMapper{}, +} + +type dubboTarget struct { + Values []interface{} // the slice contains the parameters. + Types []string // the slice contains the parameters' types. It should match the values one by one. +} + +// pre-allocate proper memory according to the params' usability. +func newDubboTarget(mps []config.MappingParam) *dubboTarget { + length := 0 + + for i := 0; i < len(mps); i++ { + isGeneric, v := getGenericMapTo(mps[i].MapTo) + if isGeneric && v != optionKeyValues { + continue + } + length++ + } + + if length > 0 { + val := make([]interface{}, length) + target := &dubboTarget{ + Values: val, + Types: make([]string, length), + } + return target + } + return nil +} + +type queryStringsMapper struct{} + +// nolint +func (qm queryStringsMapper) Map(mp config.MappingParam, c *client.Request, target interface{}, option client.RequestOption) error { + t, err := validateTarget(target) + if err != nil { + return err + } + queryValues, err := url.ParseQuery(c.IngressRequest.URL.RawQuery) + if err != nil { + return errors.Wrap(err, "Error happened when parsing the query paramters") + } + _, key, err := client.ParseMapSource(mp.Name) + if err != nil { + return err + } + pos, err := strconv.Atoi(mp.MapTo) + if err != nil && option == nil { + return errors.Errorf("Parameter mapping %v incorrect", mp) + } + qValue := queryValues.Get(key[0]) + if len(qValue) == 0 { + return errors.Errorf("Query parameter %s does not exist", key) + } + + return setTargetWithOpt(c, option, t, pos, qValue, mp.MapType) +} + +type headerMapper struct{} + +// nolint +func (hm headerMapper) Map(mp config.MappingParam, c *client.Request, target interface{}, option client.RequestOption) error { + rv, err := validateTarget(target) + if err != nil { + return err + } + _, key, err := client.ParseMapSource(mp.Name) + pos, err := strconv.Atoi(mp.MapTo) + if err != nil && option == nil { + return errors.Errorf("Parameter mapping %+v incorrect", mp) + } + header := c.IngressRequest.Header.Get(key[0]) + if len(header) == 0 { + return errors.Errorf("Header %s not found", key[0]) + } + + return setTargetWithOpt(c, option, rv, pos, header, mp.MapType) +} + +type bodyMapper struct{} + +// nolint +func (bm bodyMapper) Map(mp config.MappingParam, c *client.Request, target interface{}, option client.RequestOption) error { + // TO-DO: add support for content-type other than application/json + rv, err := validateTarget(target) + if err != nil { + return err + } + _, keys, err := client.ParseMapSource(mp.Name) + if err != nil { + return err + } + pos, err := strconv.Atoi(mp.MapTo) + if err != nil && option == nil { + return errors.Errorf("Parameter mapping %v incorrect, parameters for Dubbo backend must be mapped to an int to represent position", mp) + } + + rawBody, err := io.ReadAll(c.IngressRequest.Body) + defer func() { + c.IngressRequest.Body = io.NopCloser(bytes.NewReader(rawBody)) + }() + if err != nil { + return err + } + mapBody := map[string]interface{}{} + json.Unmarshal(rawBody, &mapBody) + val, err := client.GetMapValue(mapBody, keys) + + if err := setTargetWithOpt(c, option, rv, pos, val, mp.MapType); err != nil { + return errors.Wrap(err, "set target fail") + } + + c.IngressRequest.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + return nil +} + +type uriMapper struct{} + +// nolint +func (um uriMapper) Map(mp config.MappingParam, c *client.Request, target interface{}, option client.RequestOption) error { + rv, err := validateTarget(target) + if err != nil { + return err + } + _, keys, err := client.ParseMapSource(mp.Name) + if err != nil { + return err + } + pos, err := strconv.Atoi(mp.MapTo) + if err != nil && option == nil { + return errors.Errorf("Parameter mapping %v incorrect", mp) + } + uriValues := router.GetURIParams(&c.API, *c.IngressRequest.URL) + + return setTargetWithOpt(c, option, rv, pos, uriValues.Get(keys[0]), mp.MapType) +} + +// validateTarget verify if the incoming target for the Map function +// can be processed as expected. +func validateTarget(target interface{}) (*dubboTarget, error) { + val, ok := target.(*dubboTarget) + if !ok { + return nil, errors.New("Target params for dubbo backend must be *dubbogoTarget") + } + return val, nil +} + +func setTargetWithOpt(req *client.Request, option client.RequestOption, + target *dubboTarget, pos int, value interface{}, targetType string) error { + if option != nil { + return setGenericTarget(req, option, target, value, targetType) + } + value, err := mapTypes(targetType, value) + if err != nil { + return err + } + setCommonTarget(target, pos, value, targetType) + return nil +} + +func setGenericTarget(req *client.Request, option client.RequestOption, + target *dubboTarget, value interface{}, targetType string) error { + var err error + switch option.(type) { + case *groupOpt, *versionOpt, *interfaceOpt, *applicationOpt, *methodOpt: + err = option.Action(req, value) + case *valuesOpt: + err = option.Action(target, [2]interface{}{value, targetType}) + case *paramTypesOpt: + err = option.Action(target, value) + } + return err +} + +func setCommonTarget(target *dubboTarget, pos int, value interface{}, targetType string) { + // if the mapTo position is greater than the numbers of usable parameters, + // extend the values and types slices. It changes the address of the the target. + if cap(target.Values) <= pos { + list := make([]interface{}, pos+1-len(target.Values)) + typeList := make([]string, pos+1-len(target.Types)) + target.Values = append(target.Values, list...) + target.Types = append(target.Types, typeList...) + } + target.Values[pos] = value + target.Types[pos] = targetType +} + +func mapTypes(jType string, originVal interface{}) (interface{}, error) { + targetType, ok := constant.JTypeMapper[jType] + if !ok { + return nil, errors.Errorf("Invalid parameter type: %s", jType) + } + switch targetType { + case reflect.TypeOf(""): + return cast.ToStringE(originVal) + case reflect.TypeOf(int(0)): + return cast.ToIntE(originVal) + case reflect.TypeOf(int8(0)): + return cast.ToInt8E(originVal) + case reflect.TypeOf(int16(16)): + return cast.ToInt16E(originVal) + case reflect.TypeOf(int32(0)): + return cast.ToInt32E(originVal) + case reflect.TypeOf(int64(0)): + return cast.ToInt64E(originVal) + case reflect.TypeOf(float32(0)): + return cast.ToFloat32E(originVal) + case reflect.TypeOf(float64(0)): + return cast.ToFloat64E(originVal) + case reflect.TypeOf(true): + return cast.ToBoolE(originVal) + case reflect.TypeOf(time.Time{}): + return cast.ToTimeE(originVal) + default: + return originVal, nil + } +} + +// getGenericMapTo will parse the mapTo field, if the mapTo value is +// opt.xxx, the "opt." prefix will identify the param mapTo generic field, +// supporting generic fields: interface, group, application, method, version, +// values, types +func getGenericMapTo(mapTo string) (isGeneric bool, genericField string) { + fields := strings.Split(mapTo, ".") + if len(fields) != 2 || fields[0] != "opt" { + return false, "" + } + if _, ok := DefaultMapOption[fields[1]]; !ok { + return false, "" + } + return true, fields[1] +} diff --git a/pkg/client/dubbo/mapper_test.go b/pkg/client/dubbo/mapper_test.go new file mode 100644 index 000000000..baa61ae3c --- /dev/null +++ b/pkg/client/dubbo/mapper_test.go @@ -0,0 +1,354 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 dubbo + +import ( + "bytes" + "context" + "net/http" + "testing" +) + +import ( + "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/client" + "github.com/apache/dubbo-go-pixiu/pkg/common/mock" +) + +func TestQueryStringsMapper(t *testing.T) { + r, _ := http.NewRequest("GET", "/mock/test?id=12345&age=19", bytes.NewReader([]byte(""))) + api := mock.GetMockAPI(config.MethodGet, "/mock/test") + api.IntegrationRequest.MappingParams = []config.MappingParam{ + { + Name: "queryStrings.id", + MapTo: "0", + MapType: "string", + }, + { + Name: "queryStrings.name", + MapTo: "1", + MapType: "string", + }, + { + Name: "queryStrings.age", + MapTo: "jk", + MapType: "int", + }, + } + + req := client.NewReq(context.TODO(), r, api) + + params := newDubboTarget(api.IntegrationRequest.MappingParams) + qs := queryStringsMapper{} + // Giving valid mapping params + err := qs.Map(api.IntegrationRequest.MappingParams[0], req, params, nil) + // it should not return error + assert.Nil(t, err) + // it should update the target value in target position from corresponding query value in request. + assert.Equal(t, params.Values[0], "12345") + assert.Equal(t, params.Types[0], "string") + // Giving valid mapping params and same target + err = qs.Map(api.IntegrationRequest.MappingParams[1], req, params, nil) + // it should return error when request does not contain the source parameter + assert.EqualError(t, err, "Query parameter [name] does not exist") + // Giving invalid mapping params that is not a number and same target + err = qs.Map(api.IntegrationRequest.MappingParams[2], req, params, nil) + // it should return error that points out the mapping param + assert.EqualError(t, err, "Parameter mapping {queryStrings.age jk int} incorrect") + + r, _ = http.NewRequest("GET", "/mock/test?id=12345&age=19", bytes.NewReader([]byte(""))) + api = mock.GetMockAPI(config.MethodGet, "/mock/test") + api.IntegrationRequest.MappingParams = []config.MappingParam{ + { + Name: "queryStrings.id", + MapTo: "1", + MapType: "string", + }, + { + Name: "queryStrings.age", + MapTo: "0", + MapType: "int", + }, + } + + req = client.NewReq(context.TODO(), r, api) + params = newDubboTarget(api.IntegrationRequest.MappingParams) + err = qs.Map(api.IntegrationRequest.MappingParams[0], req, params, nil) + assert.Nil(t, err) + assert.Equal(t, params.Values[1], "12345") + assert.Equal(t, params.Types[1], "string") + assert.Nil(t, params.Values[0]) + err = qs.Map(api.IntegrationRequest.MappingParams[1], req, params, nil) + assert.Nil(t, err) + assert.Equal(t, params.Types[0], "int") + assert.Equal(t, params.Values[0], 19) +} + +func TestHeaderMapper(t *testing.T) { + r, _ := http.NewRequest("GET", "/mock/test?id=12345&age=19", bytes.NewReader([]byte(""))) + r.Header.Set("Auth", "1234567") + api := mock.GetMockAPI(config.MethodGet, "/mock/test") + api.IntegrationRequest.MappingParams = []config.MappingParam{ + { + Name: "headers.Auth", + MapTo: "0", + MapType: "string", + }, + } + hm := headerMapper{} + target := newDubboTarget(api.IntegrationRequest.MappingParams) + req := client.NewReq(context.TODO(), r, api) + + err := hm.Map(api.IntegrationRequest.MappingParams[0], req, target, nil) + assert.Nil(t, err) + assert.Equal(t, target.Values[0], "1234567") + assert.Equal(t, target.Types[0], "string") + + err = hm.Map(config.MappingParam{Name: "headers.Test", MapTo: "0"}, req, target, nil) + assert.EqualError(t, err, "Header Test not found") +} + +func TestBodyMapper(t *testing.T) { + r, _ := http.NewRequest("POST", "/mock/test?id=12345&age=19", bytes.NewReader([]byte(`{"sex": "male", "name":{"firstName": "Joe", "lastName": "Biden"}}`))) + r.Header.Set("Auth", "1234567") + api := mock.GetMockAPI(config.MethodGet, "/mock/test") + api.IntegrationRequest.MappingParams = []config.MappingParam{ + { + Name: "requestBody.sex", + MapTo: "0", + MapType: "string", + }, + { + Name: "requestBody.name.lastName", + MapTo: "1", + MapType: "string", + }, + { + Name: "requestBody.name", + MapTo: "2", + MapType: "object", + }, + } + bm := bodyMapper{} + target := newDubboTarget(api.IntegrationRequest.MappingParams) + req := client.NewReq(context.TODO(), r, api) + + err := bm.Map(api.IntegrationRequest.MappingParams[0], req, target, nil) + assert.Nil(t, err) + assert.Equal(t, target.Values[0], "male") + assert.Equal(t, target.Types[0], "string") + + err = bm.Map(api.IntegrationRequest.MappingParams[1], req, target, nil) + assert.Nil(t, err) + assert.Equal(t, target.Values[1], "Biden") + assert.Equal(t, target.Types[1], "string") + + err = bm.Map(api.IntegrationRequest.MappingParams[2], req, target, nil) + assert.Nil(t, err) + assert.Equal(t, target.Types[2], "object") + assert.Equal(t, target.Values[2], map[string]interface{}(map[string]interface{}{ + "firstName": "Joe", "lastName": "Biden", + })) +} + +func TestURIMapper(t *testing.T) { + r, _ := http.NewRequest("POST", "/mock/12345/joe?age=19", bytes.NewReader([]byte( + `{"sex": "male", "name":{"firstName": "Joe", "lastName": "Biden"}}`))) + r.Header.Set("Auth", "1234567") + api := mock.GetMockAPI(config.MethodGet, "/mock/:id/:name") + api.IntegrationRequest.MappingParams = []config.MappingParam{ + { + Name: "requestBody.sex", + MapTo: "0", + MapType: "string", + }, + { + Name: "requestBody.name.lastName", + MapTo: "1", + MapType: "string", + }, + { + Name: "uri.name", + MapTo: "2", + MapType: "object", + }, + { + Name: "uri.id", + MapTo: "3", + MapType: "string", + }, + } + + um := uriMapper{} + target := newDubboTarget(api.IntegrationRequest.MappingParams) + req := client.NewReq(context.TODO(), r, api) + err := um.Map(api.IntegrationRequest.MappingParams[3], req, target, nil) + assert.Nil(t, err) + err = um.Map(api.IntegrationRequest.MappingParams[2], req, target, nil) + assert.Nil(t, err) + assert.Equal(t, target.Values[2], "joe") + assert.Equal(t, target.Types[2], "object") + assert.Equal(t, target.Values[3], "12345") + assert.Equal(t, target.Types[3], "string") +} + +func TestValidateTarget(t *testing.T) { + target := newDubboTarget([]config.MappingParam{ + { + Name: "requestBody.sex", + MapTo: "0", + MapType: "string", + }, + }) + val, err := validateTarget(target) + assert.Nil(t, err) + assert.NotNil(t, val) + _, err = validateTarget(*target) + assert.EqualError(t, err, "Target params for dubbo backend must be *dubbogoTarget") + target2 := "" + _, err = validateTarget(target2) + assert.EqualError(t, err, "Target params for dubbo backend must be *dubbogoTarget") +} + +func TestMapType(t *testing.T) { + _, err := mapTypes("strings", 123) + assert.EqualError(t, err, "Invalid parameter type: strings") + + val, err := mapTypes("string", 123) + assert.Nil(t, err) + assert.Equal(t, val, "123") + _, err = mapTypes("string", []int{123, 222}) + assert.EqualError(t, err, "unable to cast []int{123, 222} of type []int to string") + + val, err = mapTypes("int", "123") + assert.Nil(t, err) + assert.Equal(t, val, 123) + val, err = mapTypes("int", 123.6) + assert.Nil(t, err) + assert.Equal(t, val, 123) + _, err = mapTypes("int", "123a") + assert.EqualError(t, err, "unable to cast \"123a\" of type string to int64") + + val, err = mapTypes("object", map[string]string{"abc": "123"}) + assert.Nil(t, err) + assert.Equal(t, val, map[string]string{"abc": "123"}) + val, err = mapTypes("object", struct{ Abc string }{Abc: "123"}) + assert.Nil(t, err) + assert.Equal(t, val, struct{ Abc string }{Abc: "123"}) + val, err = mapTypes("object", 123.6) + assert.Nil(t, err) + assert.Equal(t, val, 123.6) +} + +func TestNewDubboTarget(t *testing.T) { + mps := []config.MappingParam{ + { + Name: "string1", + MapTo: "0", + }, + { + Name: "string2", + MapTo: "opt.values", + }, + } + target := newDubboTarget(mps) + assert.NotNil(t, target) + assert.Equal(t, len(target.Values), 2) + + mps = []config.MappingParam{ + { + Name: "string1", + MapTo: "opt.interface", + }, + } + target = newDubboTarget(mps) + assert.Nil(t, target) +} + +func TestSetCommonTarget(t *testing.T) { + vals := make([]interface{}, 10) + types := make([]string, 10) + target := &dubboTarget{ + Values: vals, + Types: types, + } + setCommonTarget(target, 1, 123, "int") + assert.Equal(t, target.Values[1], 123) + assert.Equal(t, target.Types[1], "int") + assert.Nil(t, target.Values[0]) + assert.Equal(t, target.Types[0], "") + setCommonTarget(target, 10, "123", "string") + assert.Equal(t, target.Values[10], "123") + assert.Equal(t, target.Types[10], "string") +} + +func TestSetGenericTarget(t *testing.T) { + api := mock.GetMockAPI(config.MethodGet, "/mock/test") + r, _ := http.NewRequest("GET", "/mock/test?id=12345&age=19", bytes.NewReader([]byte(""))) + req := client.NewReq(context.TODO(), r, api) + + target := &dubboTarget{ + Values: make([]interface{}, 3), + Types: make([]string, 3), + } + + opt := DefaultMapOption[optionKeyValues] + err := setGenericTarget(req, opt, target, []interface{}{1, "abc", struct{ Name string }{"joe"}}, "int, string, object") + assert.Nil(t, err) + assert.Equal(t, target.Values[0], 1) + assert.Equal(t, target.Values[1], "abc") + assert.Equal(t, target.Values[2], struct{ Name string }{"joe"}) + assert.Equal(t, target.Types[0], "int") + assert.Equal(t, target.Types[1], "string") + assert.Equal(t, target.Types[2], "object") + + opt = DefaultMapOption[optionKeyTypes] + err = setGenericTarget(req, opt, target, "int, object, object", "") + assert.Nil(t, err) + assert.Equal(t, target.Types[0], "int") + assert.Equal(t, target.Types[1], "object") + assert.Equal(t, target.Types[2], "object") + + opt = DefaultMapOption[optionKeyInterface] + err = setGenericTarget(req, opt, target, "testingInterface", "") + assert.Nil(t, err) + assert.Equal(t, req.API.IntegrationRequest.Interface, "testingInterface") + + opt = DefaultMapOption[optionKeyApplication] + err = setGenericTarget(req, opt, target, "testingApplication", "") + assert.Nil(t, err) + assert.Equal(t, req.API.IntegrationRequest.ApplicationName, "testingApplication") +} + +func TestGetGenericMapTo(t *testing.T) { + isGeneric, gMapTo := getGenericMapTo("1") + assert.False(t, isGeneric) + assert.Equal(t, gMapTo, "") + + isGeneric, gMapTo = getGenericMapTo("opt.interface") + assert.True(t, isGeneric) + assert.Equal(t, gMapTo, "interface") + + isGeneric, gMapTo = getGenericMapTo("opt.whatever") + assert.False(t, isGeneric) + assert.Equal(t, gMapTo, "") +} diff --git a/pixiu/pkg/client/dubbo/option.go b/pkg/client/dubbo/option.go similarity index 97% rename from pixiu/pkg/client/dubbo/option.go rename to pkg/client/dubbo/option.go index 1d1aae2ec..78b0e18e9 100644 --- a/pixiu/pkg/client/dubbo/option.go +++ b/pkg/client/dubbo/option.go @@ -26,8 +26,8 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/client" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" ) // option keys diff --git a/pixiu/pkg/client/dubbo/option_test.go b/pkg/client/dubbo/option_test.go similarity index 100% rename from pixiu/pkg/client/dubbo/option_test.go rename to pkg/client/dubbo/option_test.go diff --git a/pkg/client/http/http.go b/pkg/client/http/http.go new file mode 100644 index 000000000..875dd33ea --- /dev/null +++ b/pkg/client/http/http.go @@ -0,0 +1,199 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 http + +import ( + "net/http" + "net/url" + "strings" + "sync" +) + +import ( + "github.com/pkg/errors" + "go.opentelemetry.io/otel" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + "go.opentelemetry.io/otel/trace" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/client" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/router" +) + +// RestMetadata http metadata, api config +type RestMetadata struct { + ApplicationName string `yaml:"application_name" json:"application_name" mapstructure:"application_name"` + Group string `yaml:"group" json:"group" mapstructure:"group"` + Version string `yaml:"version" json:"version" mapstructure:"version"` + Interface string `yaml:"interface" json:"interface" mapstructure:"interface"` + Method string `yaml:"method" json:"method" mapstructure:"method"` + Types []string `yaml:"type" json:"types" mapstructure:"types"` + Retries string `yaml:"retries" json:"retries,omitempty" property:"retries"` + ClusterName string `yaml:"cluster_name" json:"cluster_name,omitempty" property:"cluster_name"` + ProtocolTypeStr string `yaml:"protocol_type" json:"protocol_type,omitempty" property:"protocol_type"` + SerializationTypeStr string `yaml:"serialization_type" json:"serialization_type,omitempty" property:"serialization_type"` +} + +var ( + httpClient *Client + countDown = sync.Once{} +) + +const ( + traceNameHTTPClient = "http-client" + jaegerTraceIDInHeader = "uber-trace-id" +) + +// Client client to generic invoke dubbo +type Client struct{} + +// SingletonHTTPClient singleton HTTP Client +func SingletonHTTPClient() *Client { + if httpClient == nil { + countDown.Do(func() { + httpClient = NewHTTPClient() + }) + } + return httpClient +} + +// NewHTTPClient create dubbo client +func NewHTTPClient() *Client { + return &Client{} +} + +// Apply only init dubbo, config mapping can do here +func (dc *Client) Apply() error { + return nil +} + +// Close close +func (dc *Client) Close() error { + return nil +} + +// Call invoke service +func (dc *Client) Call(req *client.Request) (resp interface{}, err error) { + // Map the origin parameters to backend parameters according to the API configure + transformedParams, err := dc.MapParams(req) + if err != nil { + return nil, err + } + params, _ := transformedParams.(*requestParams) + + targetURL, err := dc.parseURL(req, *params) + if err != nil { + return nil, err + } + + newReq, _ := http.NewRequest(req.IngressRequest.Method, targetURL, params.Body) + newReq.Header = params.Header + httpClient := &http.Client{Timeout: req.Timeout} + + tr := otel.Tracer(traceNameHTTPClient) + _, span := tr.Start(req.Context, "HTTP "+newReq.Method, trace.WithSpanKind(trace.SpanKindClient)) + trace.SpanFromContext(req.Context).SpanContext() + span.SetAttributes(semconv.HTTPMethodKey.String(newReq.Method)) + span.SetAttributes(semconv.HTTPTargetKey.String(targetURL)) + span.SetAttributes(semconv.HTTPFlavorKey.String(newReq.Proto)) + newReq.Header.Set(jaegerTraceIDInHeader, span.SpanContext().TraceID().String()) + defer span.End() + + tmpRet, err := httpClient.Do(newReq) + if tmpRet != nil { + span.SetAttributes(semconv.HTTPStatusCodeKey.Int(tmpRet.StatusCode)) + } + if err != nil { + span.AddEvent(semconv.ExceptionEventName, trace.WithAttributes(semconv.ExceptionMessageKey.String(err.Error()))) + urlErr, ok := err.(*url.Error) + if ok && urlErr.Timeout() { + err = errors.Errorf("http req call timeout err: %s", err.Error()) + } + } + + return tmpRet, err +} + +// MapParams param mapping to api. +func (dc *Client) MapParams(req *client.Request) (reqData interface{}, err error) { + mp := req.API.IntegrationRequest.MappingParams + r := newRequestParams() + if len(mp) == 0 { + r.Body = req.IngressRequest.Body + r.Header = req.IngressRequest.Header.Clone() + queryValues, err := url.ParseQuery(req.IngressRequest.URL.RawQuery) + if err != nil { + return nil, errors.New("Retrieve request query parameters failed") + } + r.Query = queryValues + if router.IsWildCardBackendPath(&req.API) { + r.URIParams = router.GetURIParams(&req.API, *req.IngressRequest.URL) + } + return r, nil + } + for i := 0; i < len(mp); i++ { + source, _, err := client.ParseMapSource(mp[i].Name) + if err != nil { + return nil, err + } + if mapper, ok := mappers[source]; ok { + if err := mapper.Map(mp[i], req, r, nil); err != nil { + return nil, err + } + } + } + return r, nil +} + +// ParseURL returns the actual target url. Supports wildcard target path value mapping. +func (dc *Client) parseURL(req *client.Request, params requestParams) (string, error) { + var schema string + if len(req.API.IntegrationRequest.HTTPBackendConfig.Schema) == 0 { + schema = "http" + } else { + schema = req.API.IntegrationRequest.HTTPBackendConfig.Schema + } + + rawPath := req.API.IntegrationRequest.HTTPBackendConfig.Path + if router.IsWildCardBackendPath(&req.API) { + paths := strings.Split( + strings.TrimLeft(req.API.IntegrationRequest.HTTPBackendConfig.Path, constant.PathSlash), + constant.PathSlash) + for i := 0; i < len(paths); i++ { + if strings.HasPrefix(paths[i], constant.PathParamIdentifier) { + uriParam := string(paths[i][1:len(paths[i])]) + uriValue := params.URIParams.Get(uriParam) + if len(uriValue) == 0 { + return "", errors.New("No value for target URI") + } + paths[i] = uriValue + } + } + rawPath = strings.Join(paths, constant.PathSlash) + } + + parsedURL := url.URL{ + Host: req.API.IntegrationRequest.HTTPBackendConfig.Host, + Scheme: schema, + Path: rawPath, + RawQuery: params.Query.Encode(), + } + return parsedURL.String(), nil +} diff --git a/pixiu/pkg/client/http/http_test.go b/pkg/client/http/http_test.go similarity index 97% rename from pixiu/pkg/client/http/http_test.go rename to pkg/client/http/http_test.go index 3d915c461..ec725edb6 100644 --- a/pixiu/pkg/client/http/http_test.go +++ b/pkg/client/http/http_test.go @@ -31,8 +31,8 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/mock" + "github.com/apache/dubbo-go-pixiu/pkg/client" + "github.com/apache/dubbo-go-pixiu/pkg/common/mock" ) func TestMapParams(t *testing.T) { diff --git a/pkg/client/http/mapper.go b/pkg/client/http/mapper.go new file mode 100644 index 000000000..3e90ad77c --- /dev/null +++ b/pkg/client/http/mapper.go @@ -0,0 +1,227 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 http + +import ( + "bytes" + "encoding/json" + "io" + "net/http" + "net/url" + "reflect" + "strings" +) + +import ( + "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" + "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/client" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/router" +) + +var mappers = map[string]client.ParamMapper{ + constant.QueryStrings: queryStringsMapper{}, + constant.Headers: headerMapper{}, + constant.RequestBody: bodyMapper{}, + constant.RequestURI: uriMapper{}, +} + +type requestParams struct { + Header http.Header + Query url.Values + Body io.ReadCloser + URIParams url.Values +} + +func newRequestParams() *requestParams { + return &requestParams{ + Header: http.Header{}, + Query: url.Values{}, + Body: io.NopCloser(bytes.NewReader([]byte(""))), + URIParams: url.Values{}, + } +} + +type queryStringsMapper struct{} + +// nolint +func (qs queryStringsMapper) Map(mp config.MappingParam, c *client.Request, rawTarget interface{}, option client.RequestOption) error { + target, fromKey, to, toKey, err := mapPrepare(mp, rawTarget) + if err != nil { + return err + } + queryValues, err := url.ParseQuery(c.IngressRequest.URL.RawQuery) + if err != nil { + return errors.Wrap(err, "Error happened when parsing the query paramters") + } + rawValue := queryValues.Get(fromKey[0]) + if len(rawValue) == 0 { + return errors.Errorf("%s in query parameters not found", fromKey[0]) + } + setTarget(target, to, toKey[0], rawValue) + return nil +} + +type headerMapper struct{} + +// nolint +func (hm headerMapper) Map(mp config.MappingParam, c *client.Request, rawTarget interface{}, option client.RequestOption) error { + target, fromKey, to, toKey, err := mapPrepare(mp, rawTarget) + if err != nil { + return err + } + + rawHeader := c.IngressRequest.Header.Get(fromKey[0]) + if len(rawHeader) == 0 { + return errors.Errorf("Header %s not found", fromKey[0]) + } + setTarget(target, to, toKey[0], rawHeader) + return nil +} + +type bodyMapper struct{} + +// nolint +func (bm bodyMapper) Map(mp config.MappingParam, c *client.Request, rawTarget interface{}, option client.RequestOption) error { + // TO-DO: add support for content-type other than application/json + target, fromKey, to, toKey, err := mapPrepare(mp, rawTarget) + if err != nil { + return err + } + + rawBody, err := io.ReadAll(c.IngressRequest.Body) + defer func() { + c.IngressRequest.Body = io.NopCloser(bytes.NewReader(rawBody)) + }() + if err != nil { + return err + } + mapBody := map[string]interface{}{} + json.Unmarshal(rawBody, &mapBody) + val, err := client.GetMapValue(mapBody, fromKey) + if err != nil { + return errors.Wrapf(err, "Error when get body value from key %s", fromKey) + } + setTarget(target, to, strings.Join(toKey, constant.Dot), val) + return nil +} + +type uriMapper struct{} + +// nolint +func (um uriMapper) Map(mp config.MappingParam, c *client.Request, rawTarget interface{}, option client.RequestOption) error { + target, fromKey, to, toKey, err := mapPrepare(mp, rawTarget) + if err != nil { + return err + } + if to == constant.RequestURI { + } + uriValues := router.GetURIParams(&c.API, *c.IngressRequest.URL) + if uriValues == nil { + return errors.New("No URI parameters found") + } + setTarget(target, to, strings.Join(toKey, constant.Dot), uriValues.Get(fromKey[0])) + return nil +} + +func validateTarget(target interface{}) (*requestParams, error) { + val, ok := target.(*requestParams) + if !ok || val == nil { + return nil, errors.New("Target params must be a requestParams pointer") + } + return val, nil +} + +func setTarget(target *requestParams, to string, key string, val interface{}) error { + valType := reflect.TypeOf(val) + if (to == constant.Headers || to == constant.QueryStrings) && valType.Kind() != reflect.String { + return errors.Errorf("%s only accepts string", to) + } + switch to { + case constant.Headers: + target.Header.Set(key, val.(string)) + case constant.RequestURI: + target.URIParams.Set(key, val.(string)) + case constant.QueryStrings: + target.Query.Set(key, val.(string)) + case constant.RequestBody: + rawBody, err := io.ReadAll(target.Body) + defer func() { + target.Body = io.NopCloser(bytes.NewReader(rawBody)) + }() + if err != nil { + return errors.New("Raw body read failed") + } + mapBody := map[string]interface{}{} + if len(rawBody) != 0 { + err = json.Unmarshal(rawBody, &mapBody) + if err != nil { + return errors.New("Raw body parse failed") + } + } + setMapWithPath(mapBody, key, val) + rawBody, err = json.Marshal(mapBody) + if err != nil { + return errors.New("Stringify map to body failed") + } + default: + return errors.Errorf("Mapping target to %s does not support", to) + } + return nil +} + +// setMapWithPath set the value to the target map. If the origin targetMap has +// {"abc": "cde": {"f":1, "g":2}} and the path is abc, value is "123", then the +// targetMap will be updated to {"abc", "123"} +func setMapWithPath(targetMap map[string]interface{}, path string, val interface{}) map[string]interface{} { + keys := strings.Split(path, constant.Dot) + + _, ok := targetMap[keys[0]] + if len(keys) == 1 { + targetMap[keys[0]] = val + return targetMap + } + if !ok && len(keys) != 1 { + targetMap[keys[0]] = make(map[string]interface{}) + targetMap[keys[0]] = setMapWithPath(targetMap[keys[0]].(map[string]interface{}), strings.Join(keys[1:], constant.Dot), val) + } + return targetMap +} + +func mapPrepare(mp config.MappingParam, rawTarget interface{}) (target *requestParams, fromKey []string, to string, toKey []string, err error) { + // ensure the target is a pointer and type is requestParams + target, err = validateTarget(rawTarget) + if err != nil { + return nil, nil, "", nil, err + } + // retrieve the mapping values' origin param name + _, fromKey, err = client.ParseMapSource(mp.Name) + if err != nil { + return nil, nil, "", nil, err + } + // retrieve the mapping values' target param name and param types(header/uri/query/request body) + to, toKey, err = client.ParseMapSource(mp.MapTo) + if err != nil { + return nil, nil, "", nil, err + } + return target, fromKey, to, toKey, nil +} diff --git a/pkg/client/http/mapper_test.go b/pkg/client/http/mapper_test.go new file mode 100644 index 000000000..c1311683a --- /dev/null +++ b/pkg/client/http/mapper_test.go @@ -0,0 +1,275 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 http + +import ( + "bytes" + "context" + "io" + "net/http" + "testing" +) + +import ( + "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/client" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/mock" +) + +func TestQueryMapper(t *testing.T) { + qs := queryStringsMapper{} + r, _ := http.NewRequest("GET", "/mock/test?id=12345&age=19&name=joe&nickName=trump", bytes.NewReader([]byte(""))) + api := mock.GetMockAPI(config.MethodGet, "/mock/test") + api.IntegrationRequest.MappingParams = []config.MappingParam{ + { + Name: "queryStrings.id", + MapTo: "headers.Id", + }, + { + Name: "queryStrings.name", + MapTo: "queryStrings.name", + }, + { + Name: "queryStrings.age", + MapTo: "requestBody.age", + }, + { + Name: "queryStrings.nickName", + MapTo: "requestBody.nickName", + }, + } + req := client.NewReq(context.TODO(), r, api) + + target := newRequestParams() + err := qs.Map(api.IntegrationRequest.MappingParams[0], req, target, nil) + assert.Nil(t, err) + assert.Equal(t, target.Header.Get("Id"), "12345") + + err = qs.Map(api.IntegrationRequest.MappingParams[1], req, target, nil) + assert.Nil(t, err) + assert.Equal(t, target.Query.Get("name"), "joe") + + err = qs.Map(api.IntegrationRequest.MappingParams[2], req, target, nil) + assert.Nil(t, err) + err = qs.Map(api.IntegrationRequest.MappingParams[3], req, target, nil) + assert.Nil(t, err) + rawBody, _ := io.ReadAll(target.Body) + assert.Equal(t, string(rawBody), "{\"age\":\"19\",\"nickName\":\"trump\"}") + + err = qs.Map(config.MappingParam{Name: "queryStrings.doesNotExistField", MapTo: "queryStrings.whatever"}, req, target, nil) + assert.EqualError(t, err, "doesNotExistField in query parameters not found") +} + +func TestHeaderMapper(t *testing.T) { + hm := headerMapper{} + r, _ := http.NewRequest("GET", "/mock/test?id=12345&age=19&name=joe&nickName=trump", bytes.NewReader([]byte(""))) + r.Header.Set("Auth", "xxxx12345xxx") + r.Header.Set("Token", "ttttt12345ttt") + r.Header.Set("Origin-Passcode", "whoseyourdaddy") + r.Header.Set("Pokemon-Name", "Pika") + api := mock.GetMockAPI(config.MethodGet, "/mock/test") + api.IntegrationRequest.MappingParams = []config.MappingParam{ + { + Name: "headers.Auth", + MapTo: "headers.Auth", + }, + { + Name: "headers.Token", + MapTo: "headers.Token", + }, + { + Name: "headers.Origin-Passcode", + MapTo: "queryStrings.originPasscode", + }, + { + Name: "headers.Pokemon-Name", + MapTo: "requestBody.pokeMonName", + }, + } + req := client.NewReq(context.TODO(), r, api) + + target := newRequestParams() + err := hm.Map(api.IntegrationRequest.MappingParams[0], req, target, nil) + assert.Nil(t, err) + assert.Equal(t, target.Header.Get("Auth"), "xxxx12345xxx") + err = hm.Map(api.IntegrationRequest.MappingParams[1], req, target, nil) + assert.Nil(t, err) + assert.Equal(t, target.Header.Get("Token"), "ttttt12345ttt") + + err = hm.Map(api.IntegrationRequest.MappingParams[2], req, target, nil) + assert.Nil(t, err) + assert.Equal(t, target.Query.Get("originPasscode"), "whoseyourdaddy") + + err = hm.Map(api.IntegrationRequest.MappingParams[3], req, target, nil) + assert.Nil(t, err) + rawBody, err := io.ReadAll(target.Body) + assert.Nil(t, err) + assert.Equal(t, string(rawBody), "{\"pokeMonName\":\"Pika\"}") + + err = hm.Map(config.MappingParam{Name: "headers.doesNotExistField", MapTo: "headers.whatever"}, req, target, nil) + assert.EqualError(t, err, "Header doesNotExistField not found") +} + +func TestBodyMapper(t *testing.T) { + bm := bodyMapper{} + r, _ := http.NewRequest("POST", "/mock/test", bytes.NewReader([]byte("{\"id\":\"12345\",\"age\":\"19\",\"testStruct\":{\"name\":\"mock\",\"test\":\"happy\",\"nickName\":\"trump\"}}"))) + api := mock.GetMockAPI(config.MethodGet, "/mock/test") + api.IntegrationRequest.MappingParams = []config.MappingParam{ + { + Name: "requestBody.id", + MapTo: "headers.Id", + }, + { + Name: "requestBody.age", + MapTo: "requestBody.age", + }, + { + Name: "requestBody.testStruct", + MapTo: "requestBody.testStruct", + }, + { + Name: "requestBody.testStruct.nickName", + MapTo: "requestBody.nickName", + }, + } + req := client.NewReq(context.TODO(), r, api) + + target := newRequestParams() + err := bm.Map(api.IntegrationRequest.MappingParams[0], req, target, nil) + assert.Nil(t, err) + assert.Equal(t, target.Header.Get("Id"), "12345") + + target = newRequestParams() + err = bm.Map(api.IntegrationRequest.MappingParams[1], req, target, nil) + assert.Nil(t, err) + + err = bm.Map(api.IntegrationRequest.MappingParams[2], req, target, nil) + assert.Nil(t, err) + + err = bm.Map(api.IntegrationRequest.MappingParams[3], req, target, nil) + assert.Nil(t, err) + rawBody, err := io.ReadAll(target.Body) + assert.Nil(t, err) + assert.Equal(t, string(rawBody), "{\"age\":\"19\",\"nickName\":\"trump\",\"testStruct\":{\"name\":\"mock\",\"nickName\":\"trump\",\"test\":\"happy\"}}") +} + +func TestURIMap(t *testing.T) { + um := uriMapper{} + r, _ := http.NewRequest("POST", "/mock/test/12345", bytes.NewReader([]byte("{\"age\":\"19\",\"testStruct\":{\"name\":\"mock\",\"test\":\"happy\",\"nickName\":\"trump\"}}"))) + api := mock.GetMockAPI(config.MethodGet, "/mock/test/:id") + api.IntegrationRequest.MappingParams = []config.MappingParam{ + { + Name: "uri.id", + MapTo: "headers.id", + }, + } + req := client.NewReq(context.TODO(), r, api) + + target := newRequestParams() + err := um.Map(api.IntegrationRequest.MappingParams[0], req, target, nil) + assert.Nil(t, err) + assert.Equal(t, target.Header.Get("Id"), "12345") + + target = newRequestParams() + err = um.Map(config.MappingParam{ + Name: "uri.id", + MapTo: "uri.id", + }, req, target, nil) + assert.Nil(t, err) + assert.Equal(t, target.URIParams.Get("id"), "12345") +} + +func TestSetTarget(t *testing.T) { + emptyRequestParams := newRequestParams() + err := setTarget(emptyRequestParams, constant.Headers, "Auth", "1234565") + assert.Nil(t, err) + assert.Equal(t, emptyRequestParams.Header.Get("Auth"), "1234565") + err = setTarget(emptyRequestParams, constant.Headers, "Auth", 1234565) + assert.EqualError(t, err, "headers only accepts string") + + err = setTarget(emptyRequestParams, constant.QueryStrings, "id", "123") + assert.Nil(t, err) + assert.Equal(t, emptyRequestParams.Query.Get("id"), "123") + err = setTarget(emptyRequestParams, constant.QueryStrings, "id", 123) + assert.EqualError(t, err, "queryStrings only accepts string") + + err = setTarget(emptyRequestParams, constant.RequestBody, "testStruct", map[string]interface{}{"test": "happy", "name": "mock"}) + assert.Nil(t, err) + rawBody, err := io.ReadAll(emptyRequestParams.Body) + assert.Nil(t, err) + assert.Equal(t, string(rawBody), "{\"testStruct\":{\"name\":\"mock\",\"test\":\"happy\"}}") + + err = setTarget(emptyRequestParams, constant.RequestBody, "testStruct.secondLayer", map[string]interface{}{"test": "happy", "name": "mock"}) + assert.Nil(t, err) + rawBody, err = io.ReadAll(emptyRequestParams.Body) + assert.Nil(t, err) + assert.Equal(t, string(rawBody), "{\"testStruct\":{\"secondLayer\":{\"name\":\"mock\",\"test\":\"happy\"}}}") + + err = setTarget(emptyRequestParams, constant.RequestBody, "testStruct.secondLayer.thirdLayer", map[string]interface{}{"test": "happy", "name": "mock"}) + assert.Nil(t, err) + rawBody, err = io.ReadAll(emptyRequestParams.Body) + assert.Nil(t, err) + assert.Equal(t, string(rawBody), "{\"testStruct\":{\"secondLayer\":{\"thirdLayer\":{\"name\":\"mock\",\"test\":\"happy\"}}}}") + + err = setTarget(emptyRequestParams, constant.RequestURI, "id", "12345") + assert.Nil(t, err) + assert.Equal(t, emptyRequestParams.URIParams.Get("id"), "12345") + + nonEmptyRequestParams := newRequestParams() + nonEmptyRequestParams.Body = io.NopCloser(bytes.NewReader([]byte("{\"testStruct\":\"abcde\"}"))) + err = setTarget(nonEmptyRequestParams, constant.RequestBody, "testStruct", map[string]interface{}{"test": "happy", "name": "mock"}) + assert.Nil(t, err) + rawBody, err = io.ReadAll(nonEmptyRequestParams.Body) + assert.Nil(t, err) + assert.Equal(t, string(rawBody), "{\"testStruct\":{\"name\":\"mock\",\"test\":\"happy\"}}") + + nonEmptyRequestParams = newRequestParams() + nonEmptyRequestParams.Body = io.NopCloser(bytes.NewReader([]byte("{\"otherStructure\":\"abcde\"}"))) + err = setTarget(nonEmptyRequestParams, constant.RequestBody, "testStruct", map[string]interface{}{"test": "happy", "name": "mock"}) + assert.Nil(t, err) + rawBody, err = io.ReadAll(nonEmptyRequestParams.Body) + assert.Nil(t, err) + assert.Equal(t, string(rawBody), "{\"otherStructure\":\"abcde\",\"testStruct\":{\"name\":\"mock\",\"test\":\"happy\"}}") +} + +func TestValidateTarget(t *testing.T) { + requestP := newRequestParams() + p, e := validateTarget(requestP) + assert.NotNil(t, p) + assert.Nil(t, e) + + requestP = nil + p, e = validateTarget(requestP) + assert.Nil(t, p) + assert.NotNil(t, e) + + requestP2 := []int{} + p, e = validateTarget(requestP2) + assert.Nil(t, p) + assert.NotNil(t, e) + + requestP3 := struct{}{} + p, e = validateTarget(requestP3) + assert.Nil(t, p) + assert.NotNil(t, e) +} diff --git a/pkg/client/mapper.go b/pkg/client/mapper.go new file mode 100644 index 000000000..df745f9ad --- /dev/null +++ b/pkg/client/mapper.go @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 client + +import ( + "reflect" + "regexp" + "strings" +) + +import ( + "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" + "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" +) + +// ParamMapper defines the interface about how to map the params in the inbound request. +type ParamMapper interface { + // Map implements how the request parameters map to the target parameters described by config.MappingParam + Map(config.MappingParam, *Request, interface{}, RequestOption) error +} + +// ParseMapSource parses the source parameter config in the mappingParams +// the source parameter in config could be queryStrings.*, headers.*, requestBody.* +func ParseMapSource(source string) (from string, params []string, err error) { + reg := regexp.MustCompile(`^([uri|queryStrings|headers|requestBody][\w|\d]+)\.([\w|\d|\.|\-]+)$`) + if !reg.MatchString(source) { + return "", nil, errors.New("Parameter mapping config incorrect. Please fix it") + } + ps := reg.FindStringSubmatch(source) + return ps[1], strings.Split(ps[2], "."), nil +} + +// GetMapValue return the value from map base on the path +func GetMapValue(sourceMap map[string]interface{}, keys []string) (interface{}, error) { + if len(keys) > 0 && keys[0] == constant.DefaultBodyAll { + return sourceMap, nil + } + _, ok := sourceMap[keys[0]] + if !ok { + return nil, errors.Errorf("%s does not exist in request body", keys[0]) + } + rvalue := reflect.ValueOf(sourceMap[keys[0]]) + if ok && len(keys) == 1 { + return rvalue.Interface(), nil + } + if rvalue.Type().Kind() != reflect.Map && len(keys) == 1 { + return rvalue.Interface(), nil + } + deeperStruct, ok := sourceMap[keys[0]].(map[string]interface{}) + if !ok { + return nil, errors.Errorf("%s is not a map structure. It contains %v", keys[0], sourceMap[keys[0]]) + } + return GetMapValue(deeperStruct, keys[1:]) +} diff --git a/pixiu/pkg/client/mapper_test.go b/pkg/client/mapper_test.go similarity index 100% rename from pixiu/pkg/client/mapper_test.go rename to pkg/client/mapper_test.go diff --git a/pixiu/pkg/client/metadata.go b/pkg/client/metadata.go similarity index 100% rename from pixiu/pkg/client/metadata.go rename to pkg/client/metadata.go diff --git a/pixiu/pkg/client/mq/config.go b/pkg/client/mq/config.go similarity index 100% rename from pixiu/pkg/client/mq/config.go rename to pkg/client/mq/config.go diff --git a/pixiu/pkg/client/mq/facade.go b/pkg/client/mq/facade.go similarity index 100% rename from pixiu/pkg/client/mq/facade.go rename to pkg/client/mq/facade.go diff --git a/pixiu/pkg/client/mq/kafka_facade.go b/pkg/client/mq/kafka_facade.go similarity index 99% rename from pixiu/pkg/client/mq/kafka_facade.go rename to pkg/client/mq/kafka_facade.go index f1336df65..58cc86468 100644 --- a/pixiu/pkg/client/mq/kafka_facade.go +++ b/pkg/client/mq/kafka_facade.go @@ -34,7 +34,7 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/logger" ) type kafkaErrors struct { diff --git a/pixiu/pkg/client/mq/mq.go b/pkg/client/mq/mq.go similarity index 96% rename from pixiu/pkg/client/mq/mq.go rename to pkg/client/mq/mq.go index b5130dd7d..5a98ba10b 100644 --- a/pixiu/pkg/client/mq/mq.go +++ b/pkg/client/mq/mq.go @@ -30,9 +30,9 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/client" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/logger" ) var ( diff --git a/pixiu/pkg/client/mq/msg.go b/pkg/client/mq/msg.go similarity index 100% rename from pixiu/pkg/client/mq/msg.go rename to pkg/client/mq/msg.go diff --git a/pkg/client/proxy/descriptor_source.go b/pkg/client/proxy/descriptor_source.go new file mode 100644 index 000000000..96babec7b --- /dev/null +++ b/pkg/client/proxy/descriptor_source.go @@ -0,0 +1,156 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +// This file contains code that is copied and modified from: +// https://github.com/fullstorydev/grpcurl/blob/v1.8.7/desc_source.go + +package proxy + +import ( + "os" + "sync" +) + +import ( + "github.com/jhump/protoreflect/desc" + "github.com/jhump/protoreflect/dynamic" + "github.com/pkg/errors" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/descriptorpb" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/filter/http/grpcproxy" + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +var ( + sourceOnce sync.Once + protosetSource grpcproxy.DescriptorSource +) + +func InitProtosetSource(protoset []string) { + sourceOnce.Do(func() { + var err error + protosetSource, err = DescriptorSourceFromProtoset(protoset) + if err != nil { + logger.Infof("[dubbo-go-pixiu] could not load protoset files: %v", err) + } + }) +} + +// DescriptorSourceFromProtoset creates a DescriptorSource that is backed by the named files, +// whose contents are Protocol Buffer source files. The given importPaths are used to locate +// any imported files. +func DescriptorSourceFromProtoset(filenames []string) (grpcproxy.DescriptorSource, error) { + if len(filenames) < 1 { + return nil, errors.New("no protoset files provided") + } + files := &descriptorpb.FileDescriptorSet{} + for _, filename := range filenames { + b, err := os.ReadFile(filename) + if err != nil { + return nil, errors.Errorf("wrong path to load protoset file %q: %v", filename, err) + } + var fs descriptorpb.FileDescriptorSet + err = proto.Unmarshal(b, &fs) + if err != nil { + return nil, errors.Errorf("could not parse contents of protoset file %q: %v", filename, err) + } + files.File = append(files.File, fs.File...) + } + return DescriptorSourceFromFileDescriptorSet(files) +} + +// DescriptorSourceFromFileDescriptorSet creates a DescriptorSource that is backed by the FileDescriptorSet. +func DescriptorSourceFromFileDescriptorSet(files *descriptorpb.FileDescriptorSet) (grpcproxy.DescriptorSource, error) { + unresolved := map[string]*descriptorpb.FileDescriptorProto{} + for _, fd := range files.File { + unresolved[fd.GetName()] = fd + } + resolved := map[string]*desc.FileDescriptor{} + for _, fd := range files.File { + _, err := resolveFileDescriptor(unresolved, resolved, fd.GetName()) + if err != nil { + return nil, err + } + } + return &protosetFileSource{files: resolved}, nil +} + +func resolveFileDescriptor(unresolved map[string]*descriptorpb.FileDescriptorProto, resolved map[string]*desc.FileDescriptor, filename string) (*desc.FileDescriptor, error) { + if r, ok := resolved[filename]; ok { + return r, nil + } + fd, ok := unresolved[filename] + if !ok { + return nil, errors.Errorf("no descriptor found for %q", filename) + } + deps := make([]*desc.FileDescriptor, 0, len(fd.GetDependency())) + for _, dep := range fd.GetDependency() { + depFd, err := resolveFileDescriptor(unresolved, resolved, dep) + if err != nil { + return nil, err + } + deps = append(deps, depFd) + } + result, err := desc.CreateFileDescriptor(fd, deps...) + if err != nil { + return nil, err + } + resolved[filename] = result + return result, nil +} + +type protosetFileSource struct { + files map[string]*desc.FileDescriptor + er *dynamic.ExtensionRegistry + erInit sync.Once +} + +func (fs *protosetFileSource) ListServices() ([]string, error) { + set := map[string]bool{} + for _, fd := range fs.files { + for _, svc := range fd.GetServices() { + set[svc.GetFullyQualifiedName()] = true + } + } + sl := make([]string, 0, len(set)) + for svc := range set { + sl = append(sl, svc) + } + return sl, nil +} + +func (fs *protosetFileSource) FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) { + for _, fd := range fs.files { + if dsc := fd.FindSymbol(fullyQualifiedName); dsc != nil { + return dsc, nil + } + } + return nil, errors.Errorf("Symbol not found: %s", fullyQualifiedName) +} + +func (fs *protosetFileSource) AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) { + fs.erInit.Do(func() { + fs.er = &dynamic.ExtensionRegistry{} + for _, fd := range fs.files { + fs.er.AddExtensionsFromFile(fd) + } + }) + return fs.er.AllExtensionsForType(typeName), nil +} diff --git a/pixiu/pkg/client/proxy/proxy.go b/pkg/client/proxy/proxy.go similarity index 100% rename from pixiu/pkg/client/proxy/proxy.go rename to pkg/client/proxy/proxy.go diff --git a/pixiu/pkg/client/proxy/reflection.go b/pkg/client/proxy/reflection.go similarity index 100% rename from pixiu/pkg/client/proxy/reflection.go rename to pkg/client/proxy/reflection.go diff --git a/pixiu/pkg/client/request.go b/pkg/client/request.go similarity index 100% rename from pixiu/pkg/client/request.go rename to pkg/client/request.go diff --git a/pixiu/pkg/client/response.go b/pkg/client/response.go similarity index 100% rename from pixiu/pkg/client/response.go rename to pkg/client/response.go diff --git a/pixiu/pkg/client/triple/triple.go b/pkg/client/triple/triple.go similarity index 96% rename from pixiu/pkg/client/triple/triple.go rename to pkg/client/triple/triple.go index 58578f9fb..70710bfd2 100644 --- a/pixiu/pkg/client/triple/triple.go +++ b/pkg/client/triple/triple.go @@ -32,8 +32,8 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client/proxy" + "github.com/apache/dubbo-go-pixiu/pkg/client" + "github.com/apache/dubbo-go-pixiu/pkg/client/proxy" ) // InitDefaultTripleClient init default dubbo client diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go new file mode 100644 index 000000000..173cd26e5 --- /dev/null +++ b/pkg/cluster/cluster.go @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 cluster + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/cluster/healthcheck" + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +type Cluster struct { + HealthCheck *healthcheck.HealthChecker + Config *model.ClusterConfig +} + +func NewCluster(clusterConfig *model.ClusterConfig) *Cluster { + c := &Cluster{ + Config: clusterConfig, + } + + // only handle one health checker + if len(c.Config.HealthChecks) != 0 { + c.HealthCheck = healthcheck.CreateHealthCheck(clusterConfig, c.Config.HealthChecks[0]) + c.HealthCheck.Start() + } + return c +} + +func (c *Cluster) Stop() { + if c.HealthCheck != nil { + c.HealthCheck.Stop() + } +} + +func (c *Cluster) RemoveEndpoint(endpoint *model.Endpoint) { + if c.HealthCheck != nil { + c.HealthCheck.StopOne(endpoint) + } +} + +func (c *Cluster) AddEndpoint(endpoint *model.Endpoint) { + if c.HealthCheck != nil { + c.HealthCheck.StartOne(endpoint) + } +} diff --git a/pkg/cluster/debug.go b/pkg/cluster/debug.go deleted file mode 100644 index f993dc9db..000000000 --- a/pkg/cluster/debug.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cluster - -// DebugInfo contains minimal information about remote clusters. -// This struct is defined here, in a package that avoids many imports, since xds/debug usually -// affects agent binary size. We avoid embedding other parts of a "remote cluster" struct like kube clients. -type DebugInfo struct { - ID ID `json:"id"` - SecretName string `json:"secretName"` - SyncStatus string `json:"syncStatus"` -} diff --git a/pixiu/pkg/cluster/healthcheck/healthcheck.go b/pkg/cluster/healthcheck/healthcheck.go similarity index 98% rename from pixiu/pkg/cluster/healthcheck/healthcheck.go rename to pkg/cluster/healthcheck/healthcheck.go index 354958071..6d87e7aeb 100644 --- a/pixiu/pkg/cluster/healthcheck/healthcheck.go +++ b/pkg/cluster/healthcheck/healthcheck.go @@ -29,8 +29,8 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) const ( diff --git a/pkg/cluster/healthcheck/tcp.go b/pkg/cluster/healthcheck/tcp.go new file mode 100644 index 000000000..7d12670a6 --- /dev/null +++ b/pkg/cluster/healthcheck/tcp.go @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 healthcheck + +import ( + "net" + "time" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +type TCPChecker struct { + addr string + timeout time.Duration +} + +func (s *TCPChecker) CheckHealth() bool { + conn, err := net.DialTimeout("tcp", s.addr, s.timeout) + if err != nil { + logger.Infof("[health check] tcp checker for host %s error: %v", s.addr, err) + return false + } + conn.Close() + return true +} + +func (s *TCPChecker) OnTimeout() {} diff --git a/pkg/cluster/id.go b/pkg/cluster/id.go deleted file mode 100644 index 7d934b3b7..000000000 --- a/pkg/cluster/id.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cluster - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/util/identifier" -) - -// ID is the unique identifier for a k8s cluster. -type ID string - -func (id ID) Equals(other ID) bool { - return identifier.IsSameOrEmpty(string(id), string(other)) -} - -func (id ID) String() string { - return string(id) -} diff --git a/pixiu/pkg/cluster/loadbalancer/load_balancer.go b/pkg/cluster/loadbalancer/load_balancer.go similarity index 96% rename from pixiu/pkg/cluster/loadbalancer/load_balancer.go rename to pkg/cluster/loadbalancer/load_balancer.go index 3c40f7591..081939c76 100644 --- a/pixiu/pkg/cluster/loadbalancer/load_balancer.go +++ b/pkg/cluster/loadbalancer/load_balancer.go @@ -18,7 +18,7 @@ package loadbalancer import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) type LoadBalancer interface { diff --git a/pixiu/pkg/cluster/loadbalancer/maglev/maglev_hash.go b/pkg/cluster/loadbalancer/maglev/maglev_hash.go similarity index 89% rename from pixiu/pkg/cluster/loadbalancer/maglev/maglev_hash.go rename to pkg/cluster/loadbalancer/maglev/maglev_hash.go index 72d7b10ff..9a881d456 100644 --- a/pixiu/pkg/cluster/loadbalancer/maglev/maglev_hash.go +++ b/pkg/cluster/loadbalancer/maglev/maglev_hash.go @@ -18,10 +18,10 @@ package maglev import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/cluster/loadbalancer" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/cluster/loadbalancer/ringhash" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer" + "github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer/ringhash" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) func init() { diff --git a/pixiu/pkg/cluster/loadbalancer/maglev/maglev_hash_test.go b/pkg/cluster/loadbalancer/maglev/maglev_hash_test.go similarity index 93% rename from pixiu/pkg/cluster/loadbalancer/maglev/maglev_hash_test.go rename to pkg/cluster/loadbalancer/maglev/maglev_hash_test.go index a28124ff1..9d725ffe7 100644 --- a/pixiu/pkg/cluster/loadbalancer/maglev/maglev_hash_test.go +++ b/pkg/cluster/loadbalancer/maglev/maglev_hash_test.go @@ -25,8 +25,8 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) func TestMaglevHash(t *testing.T) { diff --git a/pixiu/pkg/cluster/loadbalancer/maglev/permutation.go b/pkg/cluster/loadbalancer/maglev/permutation.go similarity index 100% rename from pixiu/pkg/cluster/loadbalancer/maglev/permutation.go rename to pkg/cluster/loadbalancer/maglev/permutation.go diff --git a/pixiu/pkg/cluster/loadbalancer/maglev/permutation_test.go b/pkg/cluster/loadbalancer/maglev/permutation_test.go similarity index 100% rename from pixiu/pkg/cluster/loadbalancer/maglev/permutation_test.go rename to pkg/cluster/loadbalancer/maglev/permutation_test.go diff --git a/pixiu/pkg/cluster/loadbalancer/rand/load_balancer_rand.go b/pkg/cluster/loadbalancer/rand/load_balancer_rand.go similarity index 90% rename from pixiu/pkg/cluster/loadbalancer/rand/load_balancer_rand.go rename to pkg/cluster/loadbalancer/rand/load_balancer_rand.go index d9cb45519..06417442f 100644 --- a/pixiu/pkg/cluster/loadbalancer/rand/load_balancer_rand.go +++ b/pkg/cluster/loadbalancer/rand/load_balancer_rand.go @@ -22,8 +22,8 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/cluster/loadbalancer" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) func init() { diff --git a/pixiu/pkg/cluster/loadbalancer/ringhash/ring_hash.go b/pkg/cluster/loadbalancer/ringhash/ring_hash.go similarity index 92% rename from pixiu/pkg/cluster/loadbalancer/ringhash/ring_hash.go rename to pkg/cluster/loadbalancer/ringhash/ring_hash.go index 13b59ab70..ed3cc6267 100644 --- a/pixiu/pkg/cluster/loadbalancer/ringhash/ring_hash.go +++ b/pkg/cluster/loadbalancer/ringhash/ring_hash.go @@ -26,9 +26,9 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/cluster/loadbalancer" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) func init() { diff --git a/pixiu/pkg/cluster/loadbalancer/ringhash/ring_hash_test.go b/pkg/cluster/loadbalancer/ringhash/ring_hash_test.go similarity index 93% rename from pixiu/pkg/cluster/loadbalancer/ringhash/ring_hash_test.go rename to pkg/cluster/loadbalancer/ringhash/ring_hash_test.go index ea8ceded4..a0ad3011e 100644 --- a/pixiu/pkg/cluster/loadbalancer/ringhash/ring_hash_test.go +++ b/pkg/cluster/loadbalancer/ringhash/ring_hash_test.go @@ -25,8 +25,8 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) func TestHashRing(t *testing.T) { diff --git a/pixiu/pkg/cluster/loadbalancer/roundrobin/round_robin.go b/pkg/cluster/loadbalancer/roundrobin/round_robin.go similarity index 91% rename from pixiu/pkg/cluster/loadbalancer/roundrobin/round_robin.go rename to pkg/cluster/loadbalancer/roundrobin/round_robin.go index 64451f044..a14e8f717 100644 --- a/pixiu/pkg/cluster/loadbalancer/roundrobin/round_robin.go +++ b/pkg/cluster/loadbalancer/roundrobin/round_robin.go @@ -18,8 +18,8 @@ package roundrobin import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/cluster/loadbalancer" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) func init() { diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go deleted file mode 100644 index edcd1d403..000000000 --- a/pkg/cmd/cmd.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "flag" - "os" - "os/signal" - "syscall" -) - -import ( - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "istio.io/pkg/log" -) - -// WaitSignal awaits for SIGINT or SIGTERM and closes the channel -func WaitSignal(stop chan struct{}) { - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) - <-sigs - close(stop) - _ = log.Sync() -} - -// WaitSignalFunc awaits for SIGINT or SIGTERM and calls the cancel function -func WaitSignalFunc(cancel func()) { - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) - <-sigs - cancel() - _ = log.Sync() -} - -// AddFlags adds all command line flags to the given command. -func AddFlags(rootCmd *cobra.Command) { - rootCmd.PersistentFlags().AddGoFlagSet(flag.CommandLine) -} - -// PrintFlags logs the flags in the flagset -func PrintFlags(flags *pflag.FlagSet) { - flags.VisitAll(func(flag *pflag.Flag) { - log.Infof("FLAG: --%s=%q", flag.Name, flag.Value) - }) -} diff --git a/pixiu/pkg/cmd/deployer.go b/pkg/cmd/deployer.go similarity index 100% rename from pixiu/pkg/cmd/deployer.go rename to pkg/cmd/deployer.go diff --git a/pkg/cmd/flag_test.go b/pkg/cmd/flag_test.go deleted file mode 100644 index 14e0033cc..000000000 --- a/pkg/cmd/flag_test.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "flag" - "testing" - "time" -) - -import ( - "github.com/spf13/cobra" - "github.com/spf13/pflag" -) - -const ( - defaultInt = 100 - - defaultString = "my default string" - - defaultBool = true - - defaultDuration = 24 * time.Hour -) - -func TestInitializeIntFlag(t *testing.T) { - cmd := &cobra.Command{} - var testInt int - flag.IntVar(&testInt, "testint", defaultInt, "test int flag") - AddFlags(cmd) - - testName := "Initialize int Flag" - if !flag.Parsed() { - t.Errorf("%s: flag.Parsed() returns false, should be true", testName) - } - - cmd.Flags().VisitAll(func(f *pflag.Flag) { - if f.Name != "testint" { - t.Errorf("%s: pflag name error. Actual %s, Expected %s", testName, f.Name, "testint") - } - }) - - _ = cmd.Flags().Parse([]string{}) - if testInt != defaultInt { - t.Errorf("%s: pflag parse error. Actual %d, Expected %d", testName, testInt, defaultInt) - } -} - -func TestInitializeStringFlag(t *testing.T) { - cmd := &cobra.Command{} - var testString string - flag.StringVar(&testString, "teststring", defaultString, "test string flag") - AddFlags(cmd) - - testName := "Initialize String Flag" - if !flag.Parsed() { - t.Errorf("%s: flag.Parsed() returns false, should be true", testName) - } - - cmd.Flags().VisitAll(func(f *pflag.Flag) { - if f.Name != "teststring" { - t.Errorf("%s: pflag name error. Actual %s, Expected %s", testName, f.Name, "teststring") - } - }) - - _ = cmd.Flags().Parse([]string{}) - if testString != defaultString { - t.Errorf("%s: pflag parse error. Actual %s, Expected %s", testName, testString, defaultString) - } -} - -func TestInitializeBoolFlag(t *testing.T) { - cmd := &cobra.Command{} - var testBool bool - flag.BoolVar(&testBool, "testbool", defaultBool, "test bool flag") - AddFlags(cmd) - - testName := "Initialize bool Flag" - if !flag.Parsed() { - t.Errorf("%s: flag.Parsed() returns false, should be true", testName) - } - - cmd.Flags().VisitAll(func(f *pflag.Flag) { - if f.Name != "testbool" { - t.Errorf("%s: pflag name error. Actual %s, Expected %s", testName, f.Name, "testbool") - } - }) - - _ = cmd.Flags().Parse([]string{}) - if testBool != defaultBool { // nolint: megacheck - t.Errorf("%s: pflag parse error. Actual %t, Expected %t", testName, testBool, defaultBool) - } -} - -func TestInitializeDurationFlag(t *testing.T) { - cmd := &cobra.Command{} - var testDuration time.Duration - flag.DurationVar(&testDuration, "testduration", defaultDuration, "test duration flag") - AddFlags(cmd) - - testName := "Initialize duration flag" - if !flag.Parsed() { - t.Errorf("%s: flag.Parsed() returns false, should be true", testName) - } - - cmd.Flags().VisitAll(func(f *pflag.Flag) { - if f.Name != "testduration" { - t.Errorf("%s: pflag name error. Actual %s, Expected %s", testName, f.Name, "testduration") - } - }) - - _ = cmd.Flags().Parse([]string{}) - if testDuration != defaultDuration { - t.Errorf("%s: pflag parse error. Actual %d, Expected %d", testName, testDuration, defaultDuration) - } -} diff --git a/pkg/cmd/gateway.go b/pkg/cmd/gateway.go new file mode 100644 index 000000000..68eae8523 --- /dev/null +++ b/pkg/cmd/gateway.go @@ -0,0 +1,201 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 cmd + +import ( + "fmt" + "os" + "runtime" + "strconv" +) + +import ( + "github.com/spf13/cobra" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + pxruntime "github.com/apache/dubbo-go-pixiu/pkg/common/runtime" + "github.com/apache/dubbo-go-pixiu/pkg/config" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/server" +) + +var ( + flagToLogLevel = map[string]string{ + "trace": "TRACE", + "debug": "DEBUG", + "info": "INFO", + "warning": "WARN", + "error": "ERROR", + "critical": "FATAL", + } + + configPath string + apiConfigPath string + logConfigPath string + logLevel string + + // CURRENTLY USELESS + logFormat string + + limitCpus string + + // Currently default set to false, wait for up coming support + initFromRemote = false +) + +var ( + GatewayCmd = &cobra.Command{ + Use: "gateway", + Short: "Run dubbo go pixiu in gateway mode", + } + + deploy = &DefaultDeployer{ + configManger: config.NewConfigManger(), + } + + startGatewayCmd = &cobra.Command{ + Use: "start", + Short: "Start gateway", + PreRun: func(cmd *cobra.Command, args []string) { + initDefaultValue() + + err := deploy.initialize() + if err != nil { + panic(err) + } + }, + Run: func(cmd *cobra.Command, args []string) { + + err := deploy.start() + if err != nil { + panic(err) + } + }, + } +) + +// init Init startCmd +func init() { + startGatewayCmd.PersistentFlags().StringVarP(&configPath, constant.ConfigPathKey, "c", os.Getenv(constant.EnvDubbogoPixiuConfig), "Load configuration from `FILE`") + startGatewayCmd.PersistentFlags().StringVarP(&apiConfigPath, constant.ApiConfigPathKey, "a", os.Getenv(constant.EnvDubbogoPixiuApiConfig), "Load api configuration from `FILE`") + startGatewayCmd.PersistentFlags().StringVarP(&logConfigPath, constant.LogConfigPathKey, "g", os.Getenv(constant.EnvDubbogoPixiuLogConfig), "Load log configuration from `FILE`") + startGatewayCmd.PersistentFlags().StringVarP(&logLevel, constant.LogLevelKey, "l", os.Getenv(constant.EnvDubbogoPixiuLogLevel), "dubbogo pixiu log level, trace|debug|info|warning|error|critical") + startGatewayCmd.PersistentFlags().StringVarP(&limitCpus, constant.LimitCpusKey, "m", os.Getenv(constant.EnvDubbogoPixiuLimitCpus), "dubbogo pixiu schedule threads count") + startGatewayCmd.PersistentFlags().StringVarP(&logFormat, constant.LogFormatKey, "f", os.Getenv(constant.EnvDubbogoPixiuLogFormat), "dubbogo pixiu log format, currently useless") + + GatewayCmd.AddCommand(startGatewayCmd) +} + +type DefaultDeployer struct { + bootstrap *model.Bootstrap + configManger *config.ConfigManager +} + +func (d *DefaultDeployer) initialize() error { + err := initLog() + if err != nil { + logger.Warnf("[startGatewayCmd] failed to init logger, %s", err.Error()) + } + + // load Bootstrap config + d.bootstrap = d.configManger.LoadBootConfig(configPath) + + err = initLimitCpus() + if err != nil { + logger.Errorf("[startCmd] failed to get limit cpu number, %s", err.Error()) + } + + return err +} + +func (d *DefaultDeployer) start() error { + server.Start(d.bootstrap) + return nil +} + +func (d *DefaultDeployer) stop() error { + //TODO implement me + panic("implement me") +} + +// initDefaultValue If not set both in args and env, set default values +func initDefaultValue() { + if configPath == "" { + configPath = constant.DefaultConfigPath + } + + if apiConfigPath == "" { + apiConfigPath = constant.DefaultApiConfigPath + } + + if logConfigPath == "" { + logConfigPath = constant.DefaultLogConfigPath + } + + if logLevel == "" { + logLevel = constant.DefaultLogLevel + } + + if limitCpus == "" { + limitCpus = constant.DefaultLimitCpus + } + + if logFormat == "" { + logFormat = constant.DefaultLogFormat + } +} + +// initLog +func initLog() error { + err := logger.InitLog(logConfigPath) + if err != nil { + // cause `logger.InitLog` already handle init failed, so just use logger to log + return err + } + + if level, ok := flagToLogLevel[logLevel]; ok { + logger.SetLoggerLevel(level) + } else { + logger.SetLoggerLevel(flagToLogLevel[constant.DefaultLogLevel]) + return fmt.Errorf("logLevel is invalid, set log level to default: %s", constant.DefaultLogLevel) + } + return nil +} + +// initApiConfig return value of the bool is for the judgment of whether is a api meta data error, a kind of silly (?) +func initApiConfig() (*model.Bootstrap, error) { + bootstrap := config.Load(configPath) + return bootstrap, nil +} + +func initLimitCpus() error { + limitCpuNumberFromEnv, err := strconv.ParseInt(limitCpus, 10, 64) + if err != nil { + return err + } + limitCpuNumber := int(limitCpuNumberFromEnv) + if limitCpuNumber <= 0 { + limitCpuNumber = pxruntime.GetCPUNum() + } + runtime.GOMAXPROCS(limitCpuNumber) + logger.Infof("GOMAXPROCS set to %v", limitCpuNumber) + return nil +} diff --git a/pixiu/pkg/cmd/sidecar.go b/pkg/cmd/sidecar.go similarity index 100% rename from pixiu/pkg/cmd/sidecar.go rename to pkg/cmd/sidecar.go diff --git a/pixiu/pkg/common/constant/env.go b/pkg/common/constant/env.go similarity index 100% rename from pixiu/pkg/common/constant/env.go rename to pkg/common/constant/env.go diff --git a/pixiu/pkg/common/constant/filter.go b/pkg/common/constant/filter.go similarity index 100% rename from pixiu/pkg/common/constant/filter.go rename to pkg/common/constant/filter.go diff --git a/pkg/common/constant/http.go b/pkg/common/constant/http.go new file mode 100644 index 000000000..d76536cf7 --- /dev/null +++ b/pkg/common/constant/http.go @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 constant + +const ( + HeaderKeyContextType = "Content-Type" + + HeaderKeyAccessControlAllowOrigin = "Access-Control-Allow-Origin" + HeaderKeyAccessControlAllowHeaders = "Access-Control-Allow-Headers" + HeaderKeyAccessControlExposeHeaders = "Access-Control-Expose-Headers" + HeaderKeyAccessControlAllowMethods = "Access-Control-Allow-Methods" + HeaderKeyAccessControlMaxAge = "Access-Control-Max-Age" + HeaderKeyAccessControlAllowCredentials = "Access-Control-Allow-Credentials" + + HeaderValueJsonUtf8 = "application/json;charset=UTF-8" + HeaderValueTextPlain = "text/plain" + HeaderValueApplicationJson = "application/json" + + HeaderValueAll = "*" + + PathSlash = "/" + ProtocolSlash = "://" + PathParamIdentifier = ":" +) + +const ( + Http1HeaderKeyHost = "Host" + Http2HeaderKeyHost = ":authority" +) + +const ( + PprofDefaultAddress = "0.0.0.0" + PprofDefaultPort = 7070 +) + +const ( + Get = "GET" + Put = "PUT" + Post = "POST" + Delete = "DELETE" + Options = "OPTIONS" +) + +const ( + DubboHttpDubboVersion = "x-dubbo-http1.1-dubbo-version" + DubboServiceProtocol = "x-dubbo-service-protocol" + DubboServiceVersion = "x-dubbo-service-version" + DubboGroup = "x-dubbo-service-group" + DubboServiceMethodTypes = "x-dubbo-service-method-overloading" +) + +const ( + Host = "Host" + Authorization = "Authorization" + XForwardedFor = "X-Forwarded-For" + AccessControlRequestMethod = "Access-Control-Request-Method" + Origin = "Origin" + XForwardedProto = "X-Forwarded-Proto" +) diff --git a/pixiu/pkg/common/constant/jtypes.go b/pkg/common/constant/jtypes.go similarity index 100% rename from pixiu/pkg/common/constant/jtypes.go rename to pkg/common/constant/jtypes.go diff --git a/pixiu/pkg/common/constant/key.go b/pkg/common/constant/key.go similarity index 100% rename from pixiu/pkg/common/constant/key.go rename to pkg/common/constant/key.go diff --git a/pkg/common/constant/log.go b/pkg/common/constant/log.go deleted file mode 100644 index 54bb539cb..000000000 --- a/pkg/common/constant/log.go +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 constant - -// Log scene or topic -const ( - LogPixiu = "[Pixiu]" -) diff --git a/pixiu/pkg/common/constant/pixiu.go b/pkg/common/constant/pixiu.go similarity index 100% rename from pixiu/pkg/common/constant/pixiu.go rename to pkg/common/constant/pixiu.go diff --git a/pixiu/pkg/common/constant/remote.go b/pkg/common/constant/remote.go similarity index 100% rename from pixiu/pkg/common/constant/remote.go rename to pkg/common/constant/remote.go diff --git a/pixiu/pkg/common/constant/url.go b/pkg/common/constant/url.go similarity index 100% rename from pixiu/pkg/common/constant/url.go rename to pkg/common/constant/url.go diff --git a/pkg/common/extension/adapter/adapter.go b/pkg/common/extension/adapter/adapter.go new file mode 100644 index 000000000..365dd36b5 --- /dev/null +++ b/pkg/common/extension/adapter/adapter.go @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 adapter + +import ( + "fmt" +) + +import ( + "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +type ( + // AdapterPlugin plugin for adapter + AdapterPlugin interface { + // Kind returns the unique kind name to represent itself. + Kind() string + + // CreateAdapter return the Adapter callback + CreateAdapter(ad *model.Adapter) (Adapter, error) + } + + // Adapter adapter interface + Adapter interface { + // Start start adapter lifetime + Start() + // Stop stop adapter lifetime + Stop() + // Apply init + Apply() error + // Config get config for Adapter + Config() interface{} + } +) + +var ( + adapterPlugins = map[string]AdapterPlugin{} +) + +// RegisterAdapterPlugin registers adapter plugin +func RegisterAdapterPlugin(p AdapterPlugin) { + if p.Kind() == "" { + panic(fmt.Errorf("%T: empty kind", p)) + } + + existedPlugin, existed := adapterPlugins[p.Kind()] + if existed { + panic(fmt.Errorf("%T and %T got same kind: %s", p, existedPlugin, p.Kind())) + } + + adapterPlugins[p.Kind()] = p +} + +// GetAdapterPlugin get plugin by kind +func GetAdapterPlugin(kind string) (AdapterPlugin, error) { + existedAdapter, existed := adapterPlugins[kind] + if existed { + return existedAdapter, nil + } + return nil, errors.Errorf("plugin not found %s", kind) +} diff --git a/pixiu/pkg/common/extension/adapter/adapter_test.go b/pkg/common/extension/adapter/adapter_test.go similarity index 96% rename from pixiu/pkg/common/extension/adapter/adapter_test.go rename to pkg/common/extension/adapter/adapter_test.go index 9a7b44967..c0f16977a 100644 --- a/pixiu/pkg/common/extension/adapter/adapter_test.go +++ b/pkg/common/extension/adapter/adapter_test.go @@ -26,7 +26,7 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) type ( diff --git a/pkg/common/extension/filter/filter.go b/pkg/common/extension/filter/filter.go new file mode 100644 index 000000000..de19ab237 --- /dev/null +++ b/pkg/common/extension/filter/filter.go @@ -0,0 +1,220 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 filter + +import ( + "context" + "fmt" + stdHttp "net/http" +) + +import ( + "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/context/dubbo" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" +) + +type ( + // HttpFilterPlugin describe plugin + HttpFilterPlugin interface { + // Kind returns the unique kind name to represent itself. + Kind() string + + // CreateFilterFactory return the filter factory + CreateFilterFactory() (HttpFilterFactory, error) + } + + // HttpFilterFactory describe http filter + HttpFilterFactory interface { + // Config Expose the config so that Filter Manger can inject it, so it must be a pointer + Config() interface{} + + // Apply After the config is injected, check it or make it to default + Apply() error + + // PrepareFilterChain create filter and append it to FilterChain + // + // Be Careful !!! Do not pass the Factory's config pointer to the Filter instance, + // Factory's config may be updated by FilterManager + PrepareFilterChain(ctx *http.HttpContext, chain FilterChain) error + } + + // HttpDecodeFilter before invoke upstream, like add/remove Header, route mutation etc.. + // + // if config like this: + // - A + // - B + // - C + // decode filters will be invoked in the config order: Aã€Bã€C, and decode filters will be + // invoked in the reverse order: Cã€Bã€A + HttpDecodeFilter interface { + Decode(ctx *http.HttpContext) FilterStatus + } + + // HttpEncodeFilter after invoke upstream, + // decode filters will be invoked in the reverse order + HttpEncodeFilter interface { + Encode(ctx *http.HttpContext) FilterStatus + } + + // NetworkFilter describe network filter plugin + NetworkFilterPlugin interface { + // Kind returns the unique kind name to represent itself. + Kind() string + // CreateFilterFactory return the filter callback + CreateFilter(config interface{}) (NetworkFilter, error) + // Config Expose the config so that Filter Manger can inject it, so it must be a pointer + Config() interface{} + } + + // NetworkFilter describe network filter + NetworkFilter interface { + // ServeHTTP handle http request and response + ServeHTTP(w stdHttp.ResponseWriter, r *stdHttp.Request) + // OnDecode decode bytes received from getty server + OnDecode(data []byte) (interface{}, int, error) + // OnEncode encode interface sent to getty server + OnEncode(p interface{}) ([]byte, error) + // OnData dubbo rpc invocation from getty server + OnData(data interface{}) (interface{}, error) + // OnTripleData triple rpc invocation from triple-server + OnTripleData(ctx context.Context, methodName string, arguments []interface{}) (interface{}, error) + } + + // EmptyNetworkFilter default empty network filter adapter which offers empty function implements + EmptyNetworkFilter struct{} + + // DubboFilter describe dubbo filter + DubboFilter interface { + // Handle rpc invocation + Handle(ctx *dubbo.RpcContext) FilterStatus + } + + // NetworkFilter describe dubbo filter plugin + DubboFilterPlugin interface { + // Kind returns the unique kind name to represent itself. + Kind() string + // CreateFilter return the filter callback + CreateFilter(config interface{}) (DubboFilter, error) + // Config Expose the config so that Filter Manger can inject it, so it must be a pointer + Config() interface{} + } +) + +var ( + httpFilterPluginRegistry = map[string]HttpFilterPlugin{} + networkFilterPluginRegistry = map[string]NetworkFilterPlugin{} + dubboFilterPluginRegistry = map[string]DubboFilterPlugin{} +) + +// OnDecode empty implement +func (enf *EmptyNetworkFilter) OnDecode(data []byte) (interface{}, int, error) { + panic("OnDecode is not implemented") +} + +// OnEncode empty implement +func (enf *EmptyNetworkFilter) OnEncode(p interface{}) ([]byte, error) { + panic("OnEncode is not implemented") +} + +// OnData receive data from listener +func (enf *EmptyNetworkFilter) OnData(data interface{}) (interface{}, error) { + panic("OnData is not implemented") +} + +// OnTripleData empty implement +func (enf *EmptyNetworkFilter) OnTripleData(ctx context.Context, methodName string, arguments []interface{}) (interface{}, error) { + panic("OnTripleData is not implemented") +} + +// ServeHTTP empty implement +func (enf *EmptyNetworkFilter) ServeHTTP(w stdHttp.ResponseWriter, r *stdHttp.Request) { + panic("ServeHTTP is not implemented") +} + +// Register registers filter plugin. +func RegisterHttpFilter(f HttpFilterPlugin) { + if f.Kind() == "" { + panic(fmt.Errorf("%T: empty kind", f)) + } + + existedPlugin, existed := httpFilterPluginRegistry[f.Kind()] + if existed { + panic(fmt.Errorf("%T and %T got same kind: %s", f, existedPlugin, f.Kind())) + } + + httpFilterPluginRegistry[f.Kind()] = f +} + +// GetHttpFilterPlugin get plugin by kind +func GetHttpFilterPlugin(kind string) (HttpFilterPlugin, error) { + existedFilter, existed := httpFilterPluginRegistry[kind] + if existed { + return existedFilter, nil + } + return nil, errors.Errorf("plugin not found %s", kind) +} + +// RegisterNetworkFilter registers network filter. +func RegisterNetworkFilterPlugin(f NetworkFilterPlugin) { + if f.Kind() == "" { + panic(fmt.Errorf("%T: empty kind", f)) + } + + existedFilter, existed := networkFilterPluginRegistry[f.Kind()] + if existed { + panic(fmt.Errorf("%T and %T got same kind: %s", f, existedFilter, f.Kind())) + } + + networkFilterPluginRegistry[f.Kind()] = f +} + +// GetNetworkFilterPlugin get plugin by kind +func GetNetworkFilterPlugin(kind string) (NetworkFilterPlugin, error) { + existedFilter, existed := networkFilterPluginRegistry[kind] + if existed { + return existedFilter, nil + } + return nil, errors.Errorf("plugin not found %s", kind) +} + +// RegisterDubboFilterPlugin registers dubbo filter. +func RegisterDubboFilterPlugin(f DubboFilterPlugin) { + if f.Kind() == "" { + panic(fmt.Errorf("%T: empty kind", f)) + } + + existedFilter, existed := dubboFilterPluginRegistry[f.Kind()] + if existed { + panic(fmt.Errorf("%T and %T got same kind: %s", f, existedFilter, f.Kind())) + } + + dubboFilterPluginRegistry[f.Kind()] = f +} + +// GetDubboFilterPlugin get plugin by kind +func GetDubboFilterPlugin(kind string) (DubboFilterPlugin, error) { + existedFilter, existed := dubboFilterPluginRegistry[kind] + if existed { + return existedFilter, nil + } + return nil, errors.Errorf("plugin not found %s", kind) +} diff --git a/pixiu/pkg/common/extension/filter/filter_chain.go b/pkg/common/extension/filter/filter_chain.go similarity index 97% rename from pixiu/pkg/common/extension/filter/filter_chain.go rename to pkg/common/extension/filter/filter_chain.go index fbf2bf5ec..07d8108da 100644 --- a/pixiu/pkg/common/extension/filter/filter_chain.go +++ b/pkg/common/extension/filter/filter_chain.go @@ -18,7 +18,7 @@ package filter import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" ) // FilterChain diff --git a/pixiu/pkg/common/extension/filter/filter_manager.go b/pkg/common/extension/filter/filter_manager.go similarity index 93% rename from pixiu/pkg/common/extension/filter/filter_manager.go rename to pkg/common/extension/filter/filter_manager.go index a63e31b02..01b0fe03a 100644 --- a/pixiu/pkg/common/extension/filter/filter_manager.go +++ b/pkg/common/extension/filter/filter_manager.go @@ -27,10 +27,10 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/yaml" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/common/yaml" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) // FilterManager manage filters diff --git a/pixiu/pkg/common/extension/filter/filter_manager_test.go b/pkg/common/extension/filter/filter_manager_test.go similarity index 95% rename from pixiu/pkg/common/extension/filter/filter_manager_test.go rename to pkg/common/extension/filter/filter_manager_test.go index e4b37c9a1..63f333847 100644 --- a/pixiu/pkg/common/extension/filter/filter_manager_test.go +++ b/pkg/common/extension/filter/filter_manager_test.go @@ -27,9 +27,9 @@ import ( ) import ( - contexthttp "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) const ( diff --git a/pixiu/pkg/common/extension/filter/filter_status.go b/pkg/common/extension/filter/filter_status.go similarity index 100% rename from pixiu/pkg/common/extension/filter/filter_status.go rename to pkg/common/extension/filter/filter_status.go diff --git a/pixiu/pkg/common/grpc/RoundTripper.go b/pkg/common/grpc/RoundTripper.go similarity index 100% rename from pixiu/pkg/common/grpc/RoundTripper.go rename to pkg/common/grpc/RoundTripper.go diff --git a/pkg/common/grpc/manager.go b/pkg/common/grpc/manager.go new file mode 100644 index 000000000..732946611 --- /dev/null +++ b/pkg/common/grpc/manager.go @@ -0,0 +1,161 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 grpc + +import ( + "context" + "crypto/tls" + "encoding/base64" + "fmt" + "io" + "net" + stdHttp "net/http" +) + +import ( + "github.com/dubbogo/grpc-go/codes" + "github.com/dubbogo/grpc-go/status" + "golang.org/x/net/http2" + "google.golang.org/protobuf/proto" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + router2 "github.com/apache/dubbo-go-pixiu/pkg/common/router" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/server" +) + +// GrpcConnectionManager network filter for grpc +type GrpcConnectionManager struct { + filter.EmptyNetworkFilter + config *model.GRPCConnectionManagerConfig + routerCoordinator *router2.RouterCoordinator +} + +// CreateGrpcConnectionManager create grpc connection manager +func CreateGrpcConnectionManager(hcmc *model.GRPCConnectionManagerConfig) *GrpcConnectionManager { + hcm := &GrpcConnectionManager{config: hcmc} + hcm.routerCoordinator = router2.CreateRouterCoordinator(&hcmc.RouteConfig) + return hcm +} + +// ServeHTTP handle request and response +func (gcm *GrpcConnectionManager) ServeHTTP(w stdHttp.ResponseWriter, r *stdHttp.Request) { + + ra, err := gcm.routerCoordinator.RouteByPathAndName(r.RequestURI, r.Method) + if err != nil { + logger.Infof("GrpcConnectionManager can't find route %v", err) + gcm.writeStatus(w, status.New(codes.NotFound, fmt.Sprintf("proxy can't find route error = %v", err))) + return + } + + logger.Debugf("[dubbo-go-pixiu] client choose endpoint from cluster :%v", ra.Cluster) + + clusterName := ra.Cluster + clusterManager := server.GetClusterManager() + endpoint := clusterManager.PickEndpoint(clusterName, &http.HttpContext{Request: r}) + if endpoint == nil { + logger.Infof("GrpcConnectionManager can't find endpoint in cluster") + gcm.writeStatus(w, status.New(codes.Unknown, "can't find endpoint in cluster")) + return + } + ctx := context.Background() + // timeout + ctx, cancel := context.WithTimeout(ctx, gcm.config.Timeout) + defer cancel() + newReq := r.Clone(ctx) + newReq.URL.Scheme = "http" + newReq.URL.Host = endpoint.Address.GetAddress() + + // todo: need cache? + forwarder := gcm.newHttpForwarder() + res, err := forwarder.Forward(newReq) + + if err != nil { + logger.Infof("GrpcConnectionManager forward request error %v", err) + if err == context.DeadlineExceeded { + gcm.writeStatus(w, status.New(codes.DeadlineExceeded, fmt.Sprintf("forward timeout error = %v", err))) + return + } + gcm.writeStatus(w, status.New(codes.Unknown, fmt.Sprintf("forward error not = %v", err))) + return + } + + if err := gcm.response(w, res); err != nil { + logger.Infof("GrpcConnectionManager response error %v", err) + } +} + +func (gcm *GrpcConnectionManager) writeStatus(w stdHttp.ResponseWriter, status *status.Status) { + w.Header().Set("Grpc-Status", fmt.Sprintf("%d", status.Code())) + w.Header().Set("Grpc-Message", status.Message()) + w.Header().Set("Content-Type", "application/grpc") + + if p := status.Proto(); p != nil && len(p.Details) > 0 { + stBytes, err := proto.Marshal(p) + if err != nil { + logger.Warnf("GrpcConnectionManager writeStatus status proto marshal error: %s", err.Error()) + } else { + w.Header().Set("Grpc-Status-Details-Bin", base64.RawStdEncoding.EncodeToString(stBytes)) + } + } + + w.WriteHeader(stdHttp.StatusOK) +} + +func (gcm *GrpcConnectionManager) response(w stdHttp.ResponseWriter, res *stdHttp.Response) error { + defer res.Body.Close() + + copyHeader(w.Header(), res.Header) + w.WriteHeader(res.StatusCode) + + bytes, err := io.ReadAll(res.Body) + if err != nil { + return err + } + w.Write(bytes) + + for k, vv := range res.Trailer { + k = stdHttp.TrailerPrefix + k + for _, v := range vv { + w.Header().Add(k, v) + } + } + return nil +} + +func (gcm *GrpcConnectionManager) newHttpForwarder() *HttpForwarder { + transport := &http2.Transport{ + DialTLS: func(network, addr string, _ *tls.Config) (net.Conn, error) { + return net.Dial(network, addr) + }, + AllowHTTP: true, + } + return &HttpForwarder{transport: transport} +} + +func copyHeader(dst, src stdHttp.Header) { + for k, vv := range src { + for _, v := range vv { + dst.Add(k, v) + } + } +} diff --git a/pkg/common/http/manager.go b/pkg/common/http/manager.go new file mode 100644 index 000000000..8b3ee7bdd --- /dev/null +++ b/pkg/common/http/manager.go @@ -0,0 +1,174 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 http + +import ( + "context" + "encoding/json" + "fmt" + "io" + stdHttp "net/http" + "sync" +) + +import ( + "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/client" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + router2 "github.com/apache/dubbo-go-pixiu/pkg/common/router" + "github.com/apache/dubbo-go-pixiu/pkg/common/util" + pch "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +// HttpConnectionManager network filter for http +type HttpConnectionManager struct { + filter.EmptyNetworkFilter + config *model.HttpConnectionManagerConfig + routerCoordinator *router2.RouterCoordinator + filterManager *filter.FilterManager + pool sync.Pool +} + +// CreateHttpConnectionManager create http connection manager +func CreateHttpConnectionManager(hcmc *model.HttpConnectionManagerConfig) *HttpConnectionManager { + hcm := &HttpConnectionManager{config: hcmc} + hcm.pool.New = func() interface{} { + return hcm.allocateContext() + } + hcm.routerCoordinator = router2.CreateRouterCoordinator(&hcmc.RouteConfig) + hcm.filterManager = filter.NewFilterManager(hcmc.HTTPFilters) + hcm.filterManager.Load() + return hcm +} + +func (hcm *HttpConnectionManager) allocateContext() *pch.HttpContext { + return &pch.HttpContext{ + Params: make(map[string]interface{}), + } +} + +func (hcm *HttpConnectionManager) Handle(hc *pch.HttpContext) error { + hc.Ctx = context.Background() + err := hcm.findRoute(hc) + if err != nil { + return err + } + hcm.handleHTTPRequest(hc) + return nil +} + +func (hcm *HttpConnectionManager) ServeHTTP(w stdHttp.ResponseWriter, r *stdHttp.Request) { + hc := hcm.pool.Get().(*pch.HttpContext) + defer hcm.pool.Put(hc) + + hc.Writer = w + hc.Request = r + hc.Reset() + hc.Timeout = hcm.config.Timeout + err := hcm.Handle(hc) + if err != nil { + logger.Errorf("ServeHTTP %v", err) + } +} + +// handleHTTPRequest handle http request +func (hcm *HttpConnectionManager) handleHTTPRequest(c *pch.HttpContext) { + filterChain := hcm.filterManager.CreateFilterChain(c) + + // recover any err when filterChain run + defer func() { + if err := recover(); err != nil { + logger.Warnf("[dubbopixiu go] Occur An Unexpected Err: %+v", err) + c.SendLocalReply(stdHttp.StatusInternalServerError, []byte(fmt.Sprintf("Occur An Unexpected Err: %v", err))) + } + }() + + //todo timeout + filterChain.OnDecode(c) + hcm.buildTargetResponse(c) + filterChain.OnEncode(c) + hcm.writeResponse(c) +} + +func (hcm *HttpConnectionManager) writeResponse(c *pch.HttpContext) { + if !c.LocalReply() { + writer := c.Writer + writer.WriteHeader(c.GetStatusCode()) + if _, err := writer.Write(c.TargetResp.Data); err != nil { + logger.Errorf("write response error: %s", err) + } + } +} + +func (hcm *HttpConnectionManager) buildTargetResponse(c *pch.HttpContext) { + if c.LocalReply() { + return + } + + switch res := c.SourceResp.(type) { + case *stdHttp.Response: + body, err := io.ReadAll(res.Body) + if err != nil { + panic(err) + } + //close body + _ = res.Body.Close() + //Merge header + remoteHeader := res.Header + for k := range remoteHeader { + c.AddHeader(k, remoteHeader.Get(k)) + } + //status code + c.StatusCode(res.StatusCode) + c.TargetResp = &client.Response{Data: body} + case []byte: + c.StatusCode(stdHttp.StatusOK) + if json.Valid(res) { + c.AddHeader(constant.HeaderKeyContextType, constant.HeaderValueApplicationJson) + } else { + c.AddHeader(constant.HeaderKeyContextType, constant.HeaderValueTextPlain) + } + c.TargetResp = &client.Response{Data: res} + default: + //dubbo go generic invoke + response := util.NewDubboResponse(res, false) + c.StatusCode(stdHttp.StatusOK) + c.AddHeader(constant.HeaderKeyContextType, constant.HeaderValueJsonUtf8) + c.TargetResp = response + } +} + +func (hcm *HttpConnectionManager) findRoute(hc *pch.HttpContext) error { + ra, err := hcm.routerCoordinator.Route(hc) + if err != nil { + hc.SendLocalReply(stdHttp.StatusNotFound, constant.Default404Body) + + e := errors.Errorf("Requested URL %s not found", hc.GetUrl()) + logger.Debug(e.Error()) + return e + // return 404 + } + hc.RouteEntry(ra) + return nil +} diff --git a/pixiu/pkg/common/http/manager_test.go b/pkg/common/http/manager_test.go similarity index 90% rename from pixiu/pkg/common/http/manager_test.go rename to pkg/common/http/manager_test.go index 46424e388..684dd2dd2 100644 --- a/pixiu/pkg/common/http/manager_test.go +++ b/pkg/common/http/manager_test.go @@ -29,12 +29,12 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/router/trie" - contexthttp "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/mock" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/common/router/trie" + contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/context/mock" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) const ( diff --git a/pixiu/pkg/common/mock/router.go b/pkg/common/mock/router.go similarity index 100% rename from pixiu/pkg/common/mock/router.go rename to pkg/common/mock/router.go diff --git a/pkg/common/router/router.go b/pkg/common/router/router.go new file mode 100644 index 000000000..cd175eb2a --- /dev/null +++ b/pkg/common/router/router.go @@ -0,0 +1,172 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 router + +import ( + stdHttp "net/http" + "strings" + "sync" +) + +import ( + "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/router/trie" + "github.com/apache/dubbo-go-pixiu/pkg/common/util/stringutil" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/server" +) + +type ( + // RouterCoordinator the router coordinator for http connection manager + RouterCoordinator struct { + activeConfig *model.RouteConfiguration + rw sync.RWMutex + } +) + +// CreateRouterCoordinator create coordinator for http connection manager +func CreateRouterCoordinator(routeConfig *model.RouteConfiguration) *RouterCoordinator { + rc := &RouterCoordinator{activeConfig: routeConfig} + if routeConfig.Dynamic { + server.GetRouterManager().AddRouterListener(rc) + } + rc.initTrie() + rc.initRegex() + return rc +} + +// Route find routeAction for request +func (rm *RouterCoordinator) Route(hc *http.HttpContext) (*model.RouteAction, error) { + rm.rw.RLock() + defer rm.rw.RUnlock() + + return rm.route(hc.Request) +} + +func (rm *RouterCoordinator) RouteByPathAndName(path, method string) (*model.RouteAction, error) { + rm.rw.RLock() + defer rm.rw.RUnlock() + + return rm.activeConfig.RouteByPathAndMethod(path, method) +} + +func (rm *RouterCoordinator) route(req *stdHttp.Request) (*model.RouteAction, error) { + // match those route that only contains headers first + var matched []*model.Router + for _, route := range rm.activeConfig.Routes { + if len(route.Match.Prefix) > 0 { + continue + } + if route.Match.MatchHeader(req) { + matched = append(matched, route) + } + } + + // always return the first match of header if got any + if len(matched) > 0 { + if len(matched[0].Route.Cluster) == 0 { + return nil, errors.New("action is nil. please check your configuration.") + } + return &matched[0].Route, nil + } + + // match those route that only contains prefix + // TODO: may consider implementing both prefix and header in the future + return rm.activeConfig.Route(req) +} + +func getTrieKey(method string, path string, isPrefix bool) string { + if isPrefix { + if !strings.HasSuffix(path, constant.PathSlash) { + path = path + constant.PathSlash + } + path = path + "**" + } + return stringutil.GetTrieKey(method, path) +} + +func (rm *RouterCoordinator) initTrie() { + if rm.activeConfig.RouteTrie.IsEmpty() { + rm.activeConfig.RouteTrie = trie.NewTrie() + } + for _, router := range rm.activeConfig.Routes { + rm.OnAddRouter(router) + } +} + +func (rm *RouterCoordinator) initRegex() { + for _, router := range rm.activeConfig.Routes { + headers := router.Match.Headers + for i := range headers { + if headers[i].Regex && len(headers[i].Values) > 0 { + // regexp always use first value of header + err := headers[i].SetValueRegex(headers[i].Values[0]) + if err != nil { + logger.Errorf("invalid regexp in headers[%d]: %v", i, err) + panic(err) + } + } + } + } +} + +// OnAddRouter add router +func (rm *RouterCoordinator) OnAddRouter(r *model.Router) { + //TODO: lock move to trie node + rm.rw.Lock() + defer rm.rw.Unlock() + if r.Match.Methods == nil { + r.Match.Methods = []string{constant.Get, constant.Put, constant.Delete, constant.Post, constant.Options} + } + isPrefix := r.Match.Prefix != "" + for _, method := range r.Match.Methods { + var key string + if isPrefix { + key = getTrieKey(method, r.Match.Prefix, isPrefix) + } else { + key = getTrieKey(method, r.Match.Path, isPrefix) + } + _, _ = rm.activeConfig.RouteTrie.Put(key, r.Route) + } +} + +// OnDeleteRouter delete router +func (rm *RouterCoordinator) OnDeleteRouter(r *model.Router) { + rm.rw.Lock() + defer rm.rw.Unlock() + + if r.Match.Methods == nil { + r.Match.Methods = []string{constant.Get, constant.Put, constant.Delete, constant.Post} + } + isPrefix := r.Match.Prefix != "" + for _, method := range r.Match.Methods { + var key string + if isPrefix { + key = getTrieKey(method, r.Match.Prefix, isPrefix) + } else { + key = getTrieKey(method, r.Match.Path, isPrefix) + } + _, _ = rm.activeConfig.RouteTrie.Remove(key) + } +} diff --git a/pixiu/pkg/common/router/router_test.go b/pkg/common/router/router_test.go similarity index 96% rename from pixiu/pkg/common/router/router_test.go rename to pkg/common/router/router_test.go index b41a0b5b4..464415149 100644 --- a/pixiu/pkg/common/router/router_test.go +++ b/pkg/common/router/router_test.go @@ -28,9 +28,9 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/router/trie" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/mock" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/common/router/trie" + "github.com/apache/dubbo-go-pixiu/pkg/context/mock" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) func TestCreateRouterCoordinator(t *testing.T) { diff --git a/pixiu/pkg/common/router/trie/trie.go b/pkg/common/router/trie/trie.go similarity index 98% rename from pixiu/pkg/common/router/trie/trie.go rename to pkg/common/router/trie/trie.go index bb533800f..5060fa873 100644 --- a/pixiu/pkg/common/router/trie/trie.go +++ b/pkg/common/router/trie/trie.go @@ -26,8 +26,8 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/util/stringutil" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/common/util/stringutil" + "github.com/apache/dubbo-go-pixiu/pkg/logger" ) // Trie diff --git a/pixiu/pkg/common/router/trie/trie_test.go b/pkg/common/router/trie/trie_test.go similarity index 98% rename from pixiu/pkg/common/router/trie/trie_test.go rename to pkg/common/router/trie/trie_test.go index 8b88af750..3b3aa40fd 100644 --- a/pixiu/pkg/common/router/trie/trie_test.go +++ b/pkg/common/router/trie/trie_test.go @@ -26,7 +26,7 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/util/stringutil" + "github.com/apache/dubbo-go-pixiu/pkg/common/util/stringutil" ) func TestTrie_Put(t *testing.T) { diff --git a/pixiu/pkg/common/runtime/runtime.go b/pkg/common/runtime/runtime.go similarity index 100% rename from pixiu/pkg/common/runtime/runtime.go rename to pkg/common/runtime/runtime.go diff --git a/pixiu/pkg/common/shutdown/graceful_shutdown_signal_darwin.go b/pkg/common/shutdown/graceful_shutdown_signal_darwin.go similarity index 100% rename from pixiu/pkg/common/shutdown/graceful_shutdown_signal_darwin.go rename to pkg/common/shutdown/graceful_shutdown_signal_darwin.go diff --git a/pixiu/pkg/common/shutdown/graceful_shutdown_signal_linux.go b/pkg/common/shutdown/graceful_shutdown_signal_linux.go similarity index 100% rename from pixiu/pkg/common/shutdown/graceful_shutdown_signal_linux.go rename to pkg/common/shutdown/graceful_shutdown_signal_linux.go diff --git a/pixiu/pkg/common/shutdown/graceful_shutdown_signal_windows.go b/pkg/common/shutdown/graceful_shutdown_signal_windows.go similarity index 100% rename from pixiu/pkg/common/shutdown/graceful_shutdown_signal_windows.go rename to pkg/common/shutdown/graceful_shutdown_signal_windows.go diff --git a/pkg/common/util/response.go b/pkg/common/util/response.go new file mode 100644 index 000000000..e8d406d07 --- /dev/null +++ b/pkg/common/util/response.go @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 util + +import ( + "encoding/json" + "fmt" + "reflect" + "strings" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/client" +) + +func NewDubboResponse(data interface{}, hump bool) *client.Response { + r, _ := dealResp(data, hump) + bytes, _ := json.Marshal(r) + return &client.Response{Data: bytes} +} + +func dealResp(in interface{}, HumpToLine bool) (interface{}, error) { + if in == nil { + return in, nil + } + switch reflect.TypeOf(in).Kind() { + case reflect.Map: + if _, ok := in.(map[interface{}]interface{}); ok { + m := mapIItoMapSI(in) + if HumpToLine { + m = humpToLine(m) + } + return m, nil + } else if inm, ok := in.(map[string]interface{}); ok { + if HumpToLine { + m := humpToLine(in) + return m, nil + } + return inm, nil + } + case reflect.Slice: + if data, ok := in.([]byte); ok { + // raw bytes data should return directly + return data, nil + } + value := reflect.ValueOf(in) + newTemps := make([]interface{}, 0, value.Len()) + for i := 0; i < value.Len(); i++ { + if value.Index(i).CanInterface() { + newTemp, e := dealResp(value.Index(i).Interface(), HumpToLine) + if e != nil { + return nil, e + } + newTemps = append(newTemps, newTemp) + } else { + return nil, fmt.Errorf("unexpect err,value:%v", value) + } + } + return newTemps, nil + default: + return in, nil + } + return in, nil +} + +func mapIItoMapSI(in interface{}) interface{} { + var inMap map[interface{}]interface{} + if v, ok := in.(map[interface{}]interface{}); !ok { + return in + } else { + inMap = v + } + outMap := make(map[string]interface{}, len(inMap)) + + for k, v := range inMap { + if v == nil { + continue + } + s := fmt.Sprint(k) + if s == "class" { + // ignore the "class" field + continue + } + vt := reflect.TypeOf(v) + switch vt.Kind() { + case reflect.Map: + if _, ok := v.(map[interface{}]interface{}); ok { + v = mapIItoMapSI(v) + } + case reflect.Slice: + vl := reflect.ValueOf(v) + os := make([]interface{}, 0, vl.Len()) + for i := 0; i < vl.Len(); i++ { + if vl.Index(i).CanInterface() { + osv := mapIItoMapSI(vl.Index(i).Interface()) + os = append(os, osv) + } + } + v = os + } + outMap[s] = v + + } + return outMap +} + +// traverse all the keys in the map and change the hump to underline +func humpToLine(in interface{}) interface{} { + var m map[string]interface{} + if v, ok := in.(map[string]interface{}); ok { + m = v + } else { + return in + } + + out := make(map[string]interface{}, len(m)) + for k1, v1 := range m { + x := humpToUnderline(k1) + + if v1 == nil { + out[x] = v1 + } else if reflect.TypeOf(v1).Kind() == reflect.Struct { + out[x] = humpToLine(struct2Map(v1)) + } else if reflect.TypeOf(v1).Kind() == reflect.Slice { + value := reflect.ValueOf(v1) + newTemps := make([]interface{}, 0, value.Len()) + for i := 0; i < value.Len(); i++ { + newTemp := humpToLine(value.Index(i).Interface()) + newTemps = append(newTemps, newTemp) + } + out[x] = newTemps + } else if reflect.TypeOf(v1).Kind() == reflect.Map { + out[x] = humpToLine(v1) + } else { + out[x] = v1 + } + } + return out +} + +func humpToUnderline(s string) string { + data := make([]byte, 0, len(s)*2) + j := false + num := len(s) + for i := 0; i < num; i++ { + d := s[i] + if i > 0 && d >= 'A' && d <= 'Z' && j { + data = append(data, '_') + } + if d != '_' { + j = true + } + data = append(data, d) + } + return strings.ToLower(string(data[:])) +} + +func struct2Map(obj interface{}) map[string]interface{} { + t := reflect.TypeOf(obj) + v := reflect.ValueOf(obj) + + data := make(map[string]interface{}) + for i := 0; i < t.NumField(); i++ { + data[t.Field(i).Name] = v.Field(i).Interface() + } + return data +} diff --git a/pixiu/pkg/common/util/response_test.go b/pkg/common/util/response_test.go similarity index 100% rename from pixiu/pkg/common/util/response_test.go rename to pkg/common/util/response_test.go diff --git a/pixiu/pkg/common/util/stringutil/stringutil.go b/pkg/common/util/stringutil/stringutil.go similarity index 97% rename from pixiu/pkg/common/util/stringutil/stringutil.go rename to pkg/common/util/stringutil/stringutil.go index 108bbac90..23ba5eafd 100644 --- a/pixiu/pkg/common/util/stringutil/stringutil.go +++ b/pkg/common/util/stringutil/stringutil.go @@ -28,7 +28,7 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" ) // Split url split to []string by "/" diff --git a/pixiu/pkg/common/yaml/testdata/config.yml b/pkg/common/yaml/testdata/config.yml similarity index 100% rename from pixiu/pkg/common/yaml/testdata/config.yml rename to pkg/common/yaml/testdata/config.yml diff --git a/pixiu/pkg/common/yaml/yaml.go b/pkg/common/yaml/yaml.go similarity index 100% rename from pixiu/pkg/common/yaml/yaml.go rename to pkg/common/yaml/yaml.go diff --git a/pixiu/pkg/common/yaml/yaml_test.go b/pkg/common/yaml/yaml_test.go similarity index 100% rename from pixiu/pkg/common/yaml/yaml_test.go rename to pkg/common/yaml/yaml_test.go diff --git a/pkg/config/analysis/README.md b/pkg/config/analysis/README.md deleted file mode 100644 index fcdbfa8f4..000000000 --- a/pkg/config/analysis/README.md +++ /dev/null @@ -1,226 +0,0 @@ -# Analyzers - -The purpose of analyzers is to examine Istio configuration for potential problems that should be surfaced back to the user. An analyzer takes as input a Context object that contains methods to inspect the configuration snapshot being analyzed, as well as methods for reporting any issues discovered. - -## Writing Analyzers - -### 1. Create the code - -Analyzers need to implement the Analyzer interface ( in the `galley/pkg/config/analysis` package). They should be created under the analyzers subdirectory, and given their own appropriate subpackage. - -An annotated example: - -```go -package virtualservice - -// - -type GatewayAnalyzer struct{} - -// Compile-time check that this Analyzer correctly implements the interface -var _ analysis.Analyzer = &GatewayAnalyzer{} - -// Metadata implements Analyzer -func (s *GatewayAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - // Each analyzer should have a unique name. Use . - Name: "virtualservice.GatewayAnalyzer", - // Each analyzer should have a short, one line description of what they - // do. This description is shown when --list-analyzers is called via - // the command line. - Description: "Checks that VirtualService resources reference Gateways that exist", - // Each analyzer should register the collections that it needs to use as input. - Inputs: collection.Names{ - collections.IstioNetworkingV1Alpha3Gateways.Name(), - collections.IstioNetworkingV1Alpha3Virtualservices.Name(), - }, - } -} - -// Analyze implements Analyzer -func (s *GatewayAnalyzer) Analyze(c analysis.Context) { - // The context object has several functions that let you access the configuration resources - // in the current snapshot. The available collections, and how they map to k8s resources, - // are defined in galley/pkg/config/schema/metadata.yaml - // Available resources are listed under the "localAnalysis" snapshot in that file. - c.ForEach(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), func(r *resource.Instance) bool { - s.analyzeVirtualService(r, c) - return true - }) -} - -func (s *GatewayAnalyzer) analyzeVirtualService(r *resource.Instance, c analysis.Context) { - // The actual resource entry, represented as a protobuf message, can be obtained via - // the Item property of resource.Instance. It will need to be cast to the appropriate type. - // - // Since the resource.Instance also contains important metadata not included in the protobuf - // message (such as the resource namespace/name) it's often useful to not do this casting - // too early. - vs := r.Item.(*v1alpha3.VirtualService) - - // The resource name includes the namespace, if one exists. It should generally be safe to - // assume that the namespace is not blank, except for cluster-scoped resources. - for i, gwName := range vs.Gateways { - if !c.Exists(collections.IstioNetworkingV1Alpha3Gateways, resource.NewName(r.Metadata.FullName.Namespace, gwName)) { - // Messages are defined in galley/pkg/config/analysis/msg/messages.yaml - // From there, code is generated for each message type, including a constructor function - // that you can use to create a new validation message of each type. - msg := msg.NewReferencedResourceNotFound(r, "gateway", gwName) - - // Field map contains the path of the field as the key, and its line number as the value was stored in the resource. - // - // From the util package, find the correct path template of the field that needs to be analyzed, and - // by giving the required parameters, the exact error line number can be found for the message for final displaying. - // - // If the path template does not exist, you can add the template in util/find_errorline_utils.go for future use. - // If this exact line feature is not applied, or the message does not have a specific field like SchemaValidationError, - // then the starting line number of the resource will be displayed instead. - if line, ok := util.ErrorLine(r, fmt.Sprintf(util.VSGateway, i)); ok { - msg.Line = line - } - - // Messages are reported via the passed-in context object. - c.Report(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), msg) - } - } -} -``` - -### 2. Add the Analyzer to All() - -In order to be run, analyzers need to be registered in the [analyzers.All()](https://github.com/istio/istio/blob/master/galley/pkg/config/analysis/analyzers/all.go) function. Add your analyzer as an entry there. - -### 3. Create new message types - -If your analyzer requires any new message types (meaning a unique template and error code), you will need to do the following: - -1. Add an entry to [galley/pkg/config/analysis/msg/messages.yaml](https://github.com/istio/istio/blob/master/galley/pkg/config/analysis/msg/messages.yaml) e.g. - - ```yaml - - name: "SandwichNotFound" - code: IST0199 - level: Error - description: "This resource requires a sandwich to function." - template: "This resource requires a fresh %s on %s sandwich in order to run." - args: - - name: fillings - type: string - - name: bread - type: string - ``` - -1. Run `BUILD_WITH_CONTAINER=1 make gen`: - -1. Use the new type in your analyzer - - ```go - msg := msg.NewSandwichNotFound(resourceEntry, "ham", "rye") - ``` - -Also note: - -* Messages can have different levels (Error, Warning, Info). -* The code range 0000-0100 is reserved for internal and/or future use. -* Please keep entries in `messages.yaml` ordered by code. - -### 4. Add path templates - -If your analyzer requires to display the exact error line number, but the path template is not available, you should -add the template in [galley/pkg/config/analysis/analyzers/util/find_errorline_utils.go](https://github.com/istio/istio/blob/master/galley/pkg/config/analysis/analyzers/util/find_errorline_utils.go) - -e.g for the GatewayAnalyzer used as an example above, you would add something like this to `find_errorline_utils.go`: - -```go - // Path for VirtualService gateway. - // Required parameters: gateway index. - VSGateway = "{.spec.gateways[%d]}" -``` - -### 5. Adding unit tests - -For each new analyzer, you should add an entry to the test grid in -[analyzers_test.go](https://github.com/istio/istio/blob/master/galley/pkg/config/analysis/analyzers/analyzers_test.go). -If you want to add additional unit testing beyond this, you can, but analyzers are required to be covered in this test grid. - -e.g. for the GatewayAnalyzer used as an example above, you would add something like this to `analyzers_test.go`: - -```go - { - // Unique name for this test case - name: "virtualServiceGateways", - // List of input YAML files to load as resources before running analysis - inputFiles: []string{ - "testdata/virtualservice_gateways.yaml", - }, - // A single specific analyzer to run - analyzer: &virtualservice.GatewayAnalyzer{}, - // List of expected validation messages, as (messageType, [.]) tuples - expected: []message{ - {msg.ReferencedResourceNotFound, "VirtualService httpbin-bogus"}, - }, - }, -``` - -`virtualservice_gateways.yaml`: - -```yaml -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: httpbin-gateway -spec: - selector: - istio: ingressgateway ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: httpbin -spec: - hosts: - - "*" - gateways: - - httpbin-gateway # Expected: no validation error since this gateway exists ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: httpbin-bogus -spec: - hosts: - - "*" - gateways: - - httpbin-gateway-bogus # Expected: validation error since this gateway does not exist -``` - -You should include both positive and negative test cases in the input YAML: we want to verify both that bad entries -generate messages and that good entries do not. - -Since the analysis is tightly scoped and the YAML isn't checked for completeness, it's OK to partially specify the -resources in YAML to keep the test cases as simple and legible as possible. - -Note that this test framework will also verify that the resources requested in testing match the resources listed as -inputs in the analyzer metadata. This should help you find any unused inputs and/or missing test cases. - -### 6. Testing via istioctl - -You can use `istioctl analyze` to run all analyzers, including your new one. e.g. - -```sh -make istioctl && $GOPATH/out/linux_amd64/release/istioctl analyze -``` - -### 7. Write a user-facing documentation page - -Each analysis message needs to be documented for customers. This is done by introducing a markdown file for -each message in the [istio.io](https://github.com/istio/istio.io) repo in the [content/en/docs/reference/config/analysis](https://github.com/istio/istio.io/tree/master/content/en/docs/reference/config/analysis) directory. You create -a subdirectory with the code of the error message, and add a `index.md` file that contains the -full description of the problem with potential remediation steps, examples, etc. See the existing -files in that directory for examples of how this is done. - -## FAQ - -### What if I need a resource not available as a collection? - -Please open an issue (directed at the "Configuration" product area) or visit the -[\#config channel on Slack](https://istio.slack.com/messages/C7KSV4AHJ) to discuss it. diff --git a/pkg/config/analysis/analyzer.go b/pkg/config/analysis/analyzer.go deleted file mode 100644 index ff18ad082..000000000 --- a/pkg/config/analysis/analyzer.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package analysis - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/scope" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// Analyzer is an interface for analyzing configuration. -type Analyzer interface { - Metadata() Metadata - Analyze(c Context) -} - -// CombinedAnalyzer is a special Analyzer that combines multiple analyzers into one -type CombinedAnalyzer struct { - name string - analyzers []Analyzer -} - -// Combine multiple analyzers into a single one. -// For input metadata, use the union of the component analyzers -func Combine(name string, analyzers ...Analyzer) *CombinedAnalyzer { - return &CombinedAnalyzer{ - name: name, - analyzers: analyzers, - } -} - -// Metadata implements Analyzer -func (c *CombinedAnalyzer) Metadata() Metadata { - return Metadata{ - Name: c.name, - Inputs: combineInputs(c.analyzers), - } -} - -// Analyze implements Analyzer -func (c *CombinedAnalyzer) Analyze(ctx Context) { - for _, a := range c.analyzers { - scope.Analysis.Debugf("Started analyzer %q...", a.Metadata().Name) - if ctx.Canceled() { - scope.Analysis.Debugf("Analyzer %q has been cancelled...", c.Metadata().Name) - return - } - a.Analyze(ctx) - scope.Analysis.Debugf("Completed analyzer %q...", a.Metadata().Name) - } -} - -// RemoveSkipped removes analyzers that should be skipped, meaning they meet one of the following criteria: -// 1. The analyzer requires disabled input collections. The names of removed analyzers are returned. -// Transformer information is used to determine, based on the disabled input collections, which output collections -// should be disabled. Any analyzers that require those output collections will be removed. -// 2. The analyzer requires a collection not available in the current snapshot(s) -func (c *CombinedAnalyzer) RemoveSkipped(schemas collection.Schemas) []string { - s := sets.New() - for _, sc := range schemas.All() { - s.Insert(sc.Name().String()) - } - - var enabled []Analyzer - var removedNames []string -mainloop: - for _, a := range c.analyzers { - for _, in := range a.Metadata().Inputs { - if !s.Contains(in.String()) { - scope.Analysis.Infof("Skipping analyzer %q because collection %s is not in the snapshot(s).", a.Metadata().Name, in) - removedNames = append(removedNames, a.Metadata().Name) - continue mainloop - } - } - - enabled = append(enabled, a) - } - - c.analyzers = enabled - return removedNames -} - -// AnalyzerNames returns the names of analyzers in this combined analyzer -func (c *CombinedAnalyzer) AnalyzerNames() []string { - var result []string - for _, a := range c.analyzers { - result = append(result, a.Metadata().Name) - } - return result -} - -func combineInputs(analyzers []Analyzer) collection.Names { - result := make([]collection.Name, 0) - for _, a := range analyzers { - result = append(result, a.Metadata().Inputs...) - } - - return result -} diff --git a/pkg/config/analysis/analyzer_test.go b/pkg/config/analysis/analyzer_test.go deleted file mode 100644 index db348dad3..000000000 --- a/pkg/config/analysis/analyzer_test.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package analysis - -import ( - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/diag" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - resource2 "github.com/apache/dubbo-go-pixiu/pkg/config/schema/resource" -) - -type analyzer struct { - name string - inputs collection.Names - ran bool -} - -// Metadata implements Analyzer -func (a *analyzer) Metadata() Metadata { - return Metadata{ - Name: a.name, - Inputs: a.inputs, - } -} - -// Analyze implements Analyzer -func (a *analyzer) Analyze(Context) { - a.ran = true -} - -type context struct{} - -func (ctx *context) Report(collection.Name, diag.Message) {} -func (ctx *context) Find(collection.Name, resource.FullName) *resource.Instance { return nil } -func (ctx *context) Exists(collection.Name, resource.FullName) bool { return false } -func (ctx *context) ForEach(collection.Name, IteratorFn) {} -func (ctx *context) Canceled() bool { return false } - -func TestCombinedAnalyzer(t *testing.T) { - g := NewWithT(t) - - col1 := newSchema("col1") - col2 := newSchema("col2") - col3 := newSchema("col3") - col4 := newSchema("col4") - - a1 := &analyzer{name: "a1", inputs: collection.Names{col1.Name()}} - a2 := &analyzer{name: "a2", inputs: collection.Names{col2.Name()}} - a3 := &analyzer{name: "a3", inputs: collection.Names{col3.Name()}} - a4 := &analyzer{name: "a4", inputs: collection.Names{col4.Name()}} - - a := Combine("combined", a1, a2, a3, a4) - g.Expect(a.Metadata().Inputs).To(ConsistOf(col1.Name(), col2.Name(), col3.Name(), col4.Name())) - - avalableSchemas := collection.NewSchemasBuilder() - avalableSchemas.Add(&testSchemaImpl{col1.Name()}) - avalableSchemas.Add(&testSchemaImpl{col2.Name()}) - - removed := a.RemoveSkipped(avalableSchemas.Build()) - - g.Expect(removed).To(ConsistOf(a3.Metadata().Name, a4.Metadata().Name)) - g.Expect(a.Metadata().Inputs).To(ConsistOf(col1.Name(), col2.Name())) - - a.Analyze(&context{}) - - g.Expect(a1.ran).To(BeTrue()) - g.Expect(a2.ran).To(BeTrue()) - g.Expect(a3.ran).To(BeFalse()) - g.Expect(a4.ran).To(BeFalse()) -} - -func newSchema(name string) collection.Schema { - return collection.Builder{ - Name: name, - Resource: resource2.Builder{ - Kind: name, - Plural: name + "s", - ProtoPackage: "github.com/gogo/protobuf/types", - Proto: "google.protobuf.Empty", - }.MustBuild(), - }.MustBuild() -} - -type testSchemaImpl struct { - name collection.Name -} - -// String interface method implementation. -func (s *testSchemaImpl) String() string { - return string(s.Name()) -} - -func (s *testSchemaImpl) Name() collection.Name { - return s.name -} - -func (s *testSchemaImpl) VariableName() string { - panic("implement me") -} - -func (s *testSchemaImpl) Resource() resource2.Schema { - panic("implement me") -} - -func (s *testSchemaImpl) IsDisabled() bool { - panic("implement me") -} - -func (s *testSchemaImpl) Disable() collection.Schema { - panic("implement me") -} - -func (s *testSchemaImpl) Equal(o collection.Schema) bool { - panic("implement me") -} diff --git a/pkg/config/analysis/analyzers/all.go b/pkg/config/analysis/analyzers/all.go deleted file mode 100644 index ca1443ad6..000000000 --- a/pkg/config/analysis/analyzers/all.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package analyzers - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/annotations" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/authz" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/deployment" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/deprecation" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/destinationrule" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/envoyfilter" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/gateway" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/injection" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/multicluster" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/schema" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/service" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/serviceentry" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/sidecar" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/virtualservice" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/webhook" -) - -// All returns all analyzers -func All() []analysis.Analyzer { - analyzers := []analysis.Analyzer{ - // Please keep this list sorted alphabetically by pkg.name for convenience - &annotations.K8sAnalyzer{}, - &authz.AuthorizationPoliciesAnalyzer{}, - &deployment.ServiceAssociationAnalyzer{}, - &deployment.ApplicationUIDAnalyzer{}, - &deprecation.FieldAnalyzer{}, - &gateway.IngressGatewayPortAnalyzer{}, - &gateway.CertificateAnalyzer{}, - &gateway.SecretAnalyzer{}, - &gateway.ConflictingGatewayAnalyzer{}, - &injection.Analyzer{}, - &injection.ImageAnalyzer{}, - &injection.ImageAutoAnalyzer{}, - &multicluster.MeshNetworksAnalyzer{}, - &service.PortNameAnalyzer{}, - &sidecar.DefaultSelectorAnalyzer{}, - &sidecar.SelectorAnalyzer{}, - &virtualservice.ConflictingMeshGatewayHostsAnalyzer{}, - &virtualservice.DestinationHostAnalyzer{}, - &virtualservice.DestinationRuleAnalyzer{}, - &virtualservice.GatewayAnalyzer{}, - &virtualservice.JWTClaimRouteAnalyzer{}, - &virtualservice.RegexAnalyzer{}, - &destinationrule.CaCertificateAnalyzer{}, - &serviceentry.ProtocolAdressesAnalyzer{}, - &webhook.Analyzer{}, - &envoyfilter.EnvoyPatchAnalyzer{}, - } - - analyzers = append(analyzers, schema.AllValidationAnalyzers()...) - - return analyzers -} - -// AllCombined returns all analyzers combined as one -func AllCombined() *analysis.CombinedAnalyzer { - return analysis.Combine("all", All()...) -} diff --git a/pkg/config/analysis/analyzers/analyzers_bench_test.go b/pkg/config/analysis/analyzers/analyzers_bench_test.go deleted file mode 100644 index 7be6a2e60..000000000 --- a/pkg/config/analysis/analyzers/analyzers_bench_test.go +++ /dev/null @@ -1,108 +0,0 @@ -/* - Copyright Istio Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package analyzers - -import ( - "fmt" - "testing" -) - -import ( - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/memory" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/local" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// This is a very basic benchmark on unit test data, so it doesn't tell us anything about how an analyzer performs at scale -func BenchmarkAnalyzers(b *testing.B) { - for _, tc := range testGrid { - tc := tc // Capture range variable so subtests work correctly - b.Run(tc.name+"-bench", func(b *testing.B) { - sa, err := setupAnalyzerForCase(tc, nil) - if err != nil { - b.Fatalf("Error setting up analysis for benchmark on testcase %s: %v", tc.name, err) - } - - b.ResetTimer() - - // Run the analysis - _, err = runAnalyzer(sa) - if err != nil { - b.Fatalf("Error running analysis for benchmark on testcase %s: %v", tc.name, err) - } - }) - } -} - -func BenchmarkAnalyzersArtificialBlankData100(b *testing.B) { - benchmarkAnalyzersArtificialBlankData(100, b) -} - -func BenchmarkAnalyzersArtificialBlankData200(b *testing.B) { - benchmarkAnalyzersArtificialBlankData(200, b) -} - -func BenchmarkAnalyzersArtificialBlankData400(b *testing.B) { - benchmarkAnalyzersArtificialBlankData(400, b) -} - -func BenchmarkAnalyzersArtificialBlankData800(b *testing.B) { - benchmarkAnalyzersArtificialBlankData(800, b) -} - -// Benchmark analyzers against an artificial set of blank data. -// This does not cover all scaling factors, and it's not representative of a realistic snapshot, but it does cover some things. -func benchmarkAnalyzersArtificialBlankData(count int, b *testing.B) { - // Suppress log noise from validation warnings - validationScope := log.Scopes()["validation"] - oldLevel := validationScope.GetOutputLevel() - validationScope.SetOutputLevel(log.ErrorLevel) - defer validationScope.SetOutputLevel(oldLevel) - - // Generate blank test data - store := memory.MakeSkipValidation(collections.All) - collections.All.ForEach(func(s collection.Schema) bool { - for i := 0; i < count; i++ { - name := resource.NewFullName("default", resource.LocalName(fmt.Sprintf("%s-%d", s.Name(), i))) - _, _ = store.Create(config.Config{ - Meta: config.Meta{ - GroupVersionKind: s.Resource().GroupVersionKind(), - Name: name.Name.String(), - Namespace: name.Namespace.String(), - }, - Spec: s.Resource().MustNewInstance(), - }) - } - - return false - }) - ctx := local.NewContext(store, make(chan struct{}), func(name collection.Name) {}) - - b.ResetTimer() - for _, a := range All() { - b.Run(a.Metadata().Name+"-bench", func(b *testing.B) { - a.Analyze(ctx) - }) - } -} diff --git a/pkg/config/analysis/analyzers/analyzers_test.go b/pkg/config/analysis/analyzers/analyzers_test.go deleted file mode 100644 index eeadffcf0..000000000 --- a/pkg/config/analysis/analyzers/analyzers_test.go +++ /dev/null @@ -1,848 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package analyzers - -import ( - "fmt" - "os" - "regexp" - "strings" - "testing" - "time" -) - -import ( - . "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/annotations" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/authz" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/deployment" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/deprecation" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/destinationrule" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/envoyfilter" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/gateway" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/injection" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/maturity" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/multicluster" - schemaValidation "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/schema" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/service" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/serviceentry" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/sidecar" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/virtualservice" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/webhook" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/diag" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/local" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -type message struct { - messageType *diag.MessageType - origin string -} - -type testCase struct { - name string - inputFiles []string - meshConfigFile string // Optional - meshNetworksFile string // Optional - analyzer analysis.Analyzer - expected []message - skipAll bool -} - -// Some notes on setting up tests for Analyzers: -// * The resources in the input files don't necessarily need to be completely defined, just defined enough for the analyzer being tested. -// * Please keep this list sorted alphabetically by the pkg.name of the analyzer for convenience -// * Expected messages are in the format {msg.ValidationMessageType, "//"}. -// - Note that if Namespace is omitted in the input YAML, it will be skipped here. -var testGrid = []testCase{ - { - name: "misannoted", - inputFiles: []string{ - "testdata/misannotated.yaml", - }, - analyzer: &annotations.K8sAnalyzer{}, - expected: []message{ - {msg.UnknownAnnotation, "Service httpbin"}, - {msg.InvalidAnnotation, "Pod invalid-annotations"}, - {msg.MisplacedAnnotation, "Pod grafana-test"}, - {msg.MisplacedAnnotation, "Deployment fortio-deploy"}, - {msg.MisplacedAnnotation, "Namespace staging"}, - {msg.DeprecatedAnnotation, "Deployment fortio-deploy"}, - }, - }, - { - name: "alpha", - inputFiles: []string{ - "testdata/misannotated.yaml", - }, - analyzer: &maturity.AlphaAnalyzer{}, - expected: []message{ - {msg.AlphaAnnotation, "Deployment fortio-deploy"}, - {msg.AlphaAnnotation, "Pod invalid-annotations"}, - {msg.AlphaAnnotation, "Pod invalid-annotations"}, - {msg.AlphaAnnotation, "Service httpbin"}, - }, - skipAll: true, - }, - { - name: "deprecation", - inputFiles: []string{"testdata/deprecation.yaml"}, - analyzer: &deprecation.FieldAnalyzer{}, - expected: []message{ - {msg.Deprecated, "VirtualService foo/productpage"}, - {msg.Deprecated, "Sidecar default/no-selector"}, - }, - }, - { - name: "gatewayNoWorkload", - inputFiles: []string{"testdata/gateway-no-workload.yaml"}, - analyzer: &gateway.IngressGatewayPortAnalyzer{}, - expected: []message{ - {msg.ReferencedResourceNotFound, "Gateway httpbin-gateway"}, - }, - }, - { - name: "gatewayBadPort", - inputFiles: []string{"testdata/gateway-no-port.yaml"}, - analyzer: &gateway.IngressGatewayPortAnalyzer{}, - expected: []message{ - {msg.GatewayPortNotOnWorkload, "Gateway httpbin-gateway"}, - }, - }, - { - name: "gatewayCorrectPort", - inputFiles: []string{"testdata/gateway-correct-port.yaml"}, - analyzer: &gateway.IngressGatewayPortAnalyzer{}, - expected: []message{ - // no messages, this test case verifies no false positives - }, - }, - { - name: "gatewayCustomIngressGateway", - inputFiles: []string{"testdata/gateway-custom-ingressgateway.yaml"}, - analyzer: &gateway.IngressGatewayPortAnalyzer{}, - expected: []message{ - // no messages, this test case verifies no false positives - }, - }, - { - name: "gatewayCustomIngressGatewayBadPort", - inputFiles: []string{"testdata/gateway-custom-ingressgateway-badport.yaml"}, - analyzer: &gateway.IngressGatewayPortAnalyzer{}, - expected: []message{ - {msg.GatewayPortNotOnWorkload, "Gateway httpbin-gateway"}, - }, - }, - { - name: "gatewayServiceMatchPod", - inputFiles: []string{"testdata/gateway-custom-ingressgateway-svcselector.yaml"}, - analyzer: &gateway.IngressGatewayPortAnalyzer{}, - expected: []message{ - {msg.GatewayPortNotOnWorkload, "Gateway httpbin8002-gateway"}, - }, - }, - { - name: "gatewaySecret", - inputFiles: []string{"testdata/gateway-secrets.yaml"}, - analyzer: &gateway.SecretAnalyzer{}, - expected: []message{ - {msg.ReferencedResourceNotFound, "Gateway defaultgateway-bogusCredentialName"}, - {msg.ReferencedResourceNotFound, "Gateway customgateway-wrongnamespace"}, - {msg.ReferencedResourceNotFound, "Gateway bogusgateway"}, - }, - }, - { - name: "conflicting gateways detect", - inputFiles: []string{"testdata/conflicting-gateways.yaml"}, - analyzer: &gateway.ConflictingGatewayAnalyzer{}, - expected: []message{ - {msg.ConflictingGateways, "Gateway alpha"}, - {msg.ConflictingGateways, "Gateway beta"}, - }, - }, - { - name: "istioInjection", - inputFiles: []string{"testdata/injection.yaml"}, - analyzer: &injection.Analyzer{}, - expected: []message{ - {msg.NamespaceNotInjected, "Namespace bar"}, - {msg.PodMissingProxy, "Pod default/noninjectedpod"}, - {msg.NamespaceMultipleInjectionLabels, "Namespace busted"}, - }, - }, - { - name: "istioInjectionEnableNamespacesByDefault", - inputFiles: []string{ - "testdata/injection.yaml", - "testdata/common/sidecar-injector-enabled-nsbydefault.yaml", - }, - analyzer: &injection.Analyzer{}, - expected: []message{ - {msg.NamespaceInjectionEnabledByDefault, "Namespace bar"}, - {msg.PodMissingProxy, "Pod default/noninjectedpod"}, - {msg.NamespaceMultipleInjectionLabels, "Namespace busted"}, - }, - }, - { - name: "istioInjectionProxyImageMismatch", - inputFiles: []string{ - "testdata/injection-with-mismatched-sidecar.yaml", - "testdata/common/sidecar-injector-configmap.yaml", - }, - analyzer: &injection.ImageAnalyzer{}, - expected: []message{ - {msg.IstioProxyImageMismatch, "Pod enabled-namespace/details-v1-pod-old"}, - }, - }, - { - name: "istioInjectionProxyImageMismatchAbsolute", - inputFiles: []string{ - "testdata/injection-with-mismatched-sidecar.yaml", - "testdata/sidecar-injector-configmap-absolute-override.yaml", - }, - analyzer: &injection.ImageAnalyzer{}, - expected: []message{ - {msg.IstioProxyImageMismatch, "Pod enabled-namespace/details-v1-pod-old"}, - }, - }, - { - name: "istioInjectionProxyImageMismatchWithRevisionCanary", - inputFiles: []string{ - "testdata/injection-with-mismatched-sidecar.yaml", - "testdata/sidecar-injector-configmap-absolute-override.yaml", - "testdata/sidecar-injector-configmap-with-revision-canary.yaml", - }, - analyzer: &injection.ImageAnalyzer{}, - expected: []message{ - {msg.IstioProxyImageMismatch, "Pod enabled-namespace/details-v1-pod-old"}, - {msg.IstioProxyImageMismatch, "Pod revision-namespace/revision-details-v1-pod-old"}, - }, - }, - { - name: "portNameNotFollowConvention", - inputFiles: []string{"testdata/service-no-port-name.yaml"}, - analyzer: &service.PortNameAnalyzer{}, - expected: []message{ - {msg.PortNameIsNotUnderNamingConvention, "Service my-namespace1/my-service1"}, - {msg.PortNameIsNotUnderNamingConvention, "Service my-namespace1/my-service1"}, - {msg.PortNameIsNotUnderNamingConvention, "Service my-namespace2/my-service2"}, - }, - }, - { - name: "namedPort", - inputFiles: []string{"testdata/service-port-name.yaml"}, - analyzer: &service.PortNameAnalyzer{}, - expected: []message{}, - }, - { - name: "unnamedPortInSystemNamespace", - inputFiles: []string{"testdata/service-no-port-name-system-namespace.yaml"}, - analyzer: &service.PortNameAnalyzer{}, - expected: []message{}, - }, - { - name: "sidecarDefaultSelector", - inputFiles: []string{"testdata/sidecar-default-selector.yaml"}, - analyzer: &sidecar.DefaultSelectorAnalyzer{}, - expected: []message{ - {msg.MultipleSidecarsWithoutWorkloadSelectors, "Sidecar ns2/has-conflict-2"}, - {msg.MultipleSidecarsWithoutWorkloadSelectors, "Sidecar ns2/has-conflict-1"}, - }, - }, - { - name: "sidecarSelector", - inputFiles: []string{"testdata/sidecar-selector.yaml"}, - analyzer: &sidecar.SelectorAnalyzer{}, - expected: []message{ - {msg.ReferencedResourceNotFound, "Sidecar default/maps-to-nonexistent"}, - {msg.ReferencedResourceNotFound, "Sidecar other/maps-to-different-ns"}, - {msg.ConflictingSidecarWorkloadSelectors, "Sidecar default/dupe-1"}, - {msg.ConflictingSidecarWorkloadSelectors, "Sidecar default/dupe-2"}, - {msg.ConflictingSidecarWorkloadSelectors, "Sidecar default/overlap-1"}, - {msg.ConflictingSidecarWorkloadSelectors, "Sidecar default/overlap-2"}, - }, - }, - { - name: "virtualServiceConflictingMeshGatewayHosts", - inputFiles: []string{"testdata/virtualservice_conflictingmeshgatewayhosts.yaml"}, - analyzer: &virtualservice.ConflictingMeshGatewayHostsAnalyzer{}, - expected: []message{ - {msg.ConflictingMeshGatewayVirtualServiceHosts, "VirtualService team3/ratings"}, - {msg.ConflictingMeshGatewayVirtualServiceHosts, "VirtualService team4/ratings"}, - {msg.ConflictingMeshGatewayVirtualServiceHosts, "VirtualService foo/ratings"}, - {msg.ConflictingMeshGatewayVirtualServiceHosts, "VirtualService bar/ratings"}, - {msg.ConflictingMeshGatewayVirtualServiceHosts, "VirtualService foo/productpage"}, - {msg.ConflictingMeshGatewayVirtualServiceHosts, "VirtualService foo/bogus-productpage"}, - }, - }, - { - name: "virtualServiceDestinationHosts", - inputFiles: []string{"testdata/virtualservice_destinationhosts.yaml"}, - analyzer: &virtualservice.DestinationHostAnalyzer{}, - expected: []message{ - {msg.ReferencedResourceNotFound, "VirtualService default/reviews-bogushost"}, - {msg.ReferencedResourceNotFound, "VirtualService default/reviews-bookinfo-other"}, - {msg.ReferencedResourceNotFound, "VirtualService default/reviews-mirror-bogushost"}, - {msg.ReferencedResourceNotFound, "VirtualService default/reviews-bogusport"}, - {msg.VirtualServiceDestinationPortSelectorRequired, "VirtualService default/reviews-2port-missing"}, - {msg.ReferencedResourceNotFound, "VirtualService dubbo-system/cross-namespace-details"}, - }, - }, - { - name: "virtualServiceDestinationRules", - inputFiles: []string{"testdata/virtualservice_destinationrules.yaml"}, - analyzer: &virtualservice.DestinationRuleAnalyzer{}, - expected: []message{ - {msg.ReferencedResourceNotFound, "VirtualService default/reviews-bogussubset"}, - {msg.ReferencedResourceNotFound, "VirtualService default/reviews-mirror-bogussubset"}, - }, - }, - { - name: "virtualServiceGateways", - inputFiles: []string{"testdata/virtualservice_gateways.yaml"}, - analyzer: &virtualservice.GatewayAnalyzer{}, - expected: []message{ - {msg.ReferencedResourceNotFound, "VirtualService httpbin-bogus"}, - - {msg.VirtualServiceHostNotFoundInGateway, "VirtualService default/cross-test"}, - {msg.VirtualServiceHostNotFoundInGateway, "VirtualService httpbin-bogus"}, - {msg.VirtualServiceHostNotFoundInGateway, "VirtualService httpbin"}, - }, - }, - { - name: "virtualServiceJWTClaimRoute", - inputFiles: []string{"testdata/virtualservice_jwtclaimroute.yaml"}, - analyzer: &virtualservice.JWTClaimRouteAnalyzer{}, - expected: []message{ - {msg.JwtClaimBasedRoutingWithoutRequestAuthN, "VirtualService foo"}, - }, - }, - { - name: "serviceMultipleDeployments", - inputFiles: []string{"testdata/deployment-multi-service.yaml"}, - analyzer: &deployment.ServiceAssociationAnalyzer{}, - expected: []message{ - {msg.DeploymentAssociatedToMultipleServices, "Deployment bookinfo/multiple-svc-multiple-prot"}, - {msg.DeploymentAssociatedToMultipleServices, "Deployment bookinfo/multiple-without-port"}, - {msg.DeploymentRequiresServiceAssociated, "Deployment bookinfo/no-services"}, - {msg.DeploymentRequiresServiceAssociated, "Deployment injection-disabled-ns/ann-enabled-ns-disabled"}, - {msg.DeploymentConflictingPorts, "Deployment bookinfo/conflicting-ports"}, - }, - }, - { - name: "deploymentMultiServicesInDifferentNamespace", - inputFiles: []string{"testdata/deployment-multi-service-different-ns.yaml"}, - analyzer: &deployment.ServiceAssociationAnalyzer{}, - expected: []message{}, - }, - { - name: "regexes", - inputFiles: []string{ - "testdata/virtualservice_regexes.yaml", - }, - analyzer: &virtualservice.RegexAnalyzer{}, - expected: []message{ - {msg.InvalidRegexp, "VirtualService bad-match"}, - {msg.InvalidRegexp, "VirtualService ecma-not-v2"}, - {msg.InvalidRegexp, "VirtualService lots-of-regexes"}, - {msg.InvalidRegexp, "VirtualService lots-of-regexes"}, - {msg.InvalidRegexp, "VirtualService lots-of-regexes"}, - {msg.InvalidRegexp, "VirtualService lots-of-regexes"}, - {msg.InvalidRegexp, "VirtualService lots-of-regexes"}, - {msg.InvalidRegexp, "VirtualService lots-of-regexes"}, - }, - }, - { - name: "unknown service registry in mesh networks", - inputFiles: []string{ - "testdata/multicluster-unknown-serviceregistry.yaml", - }, - meshNetworksFile: "testdata/common/meshnetworks.yaml", - analyzer: &multicluster.MeshNetworksAnalyzer{}, - expected: []message{ - {msg.UnknownMeshNetworksServiceRegistry, "MeshNetworks dubbo-system/meshnetworks"}, - {msg.UnknownMeshNetworksServiceRegistry, "MeshNetworks dubbo-system/meshnetworks"}, - }, - }, - { - name: "authorizationpolicies", - inputFiles: []string{ - "testdata/authorizationpolicies.yaml", - }, - analyzer: &authz.AuthorizationPoliciesAnalyzer{}, - expected: []message{ - {msg.NoMatchingWorkloadsFound, "AuthorizationPolicy dubbo-system/meshwide-httpbin-v1"}, - {msg.NoMatchingWorkloadsFound, "AuthorizationPolicy httpbin-empty/httpbin-empty-namespace-wide"}, - {msg.NoMatchingWorkloadsFound, "AuthorizationPolicy httpbin/httpbin-nopods"}, - {msg.ReferencedResourceNotFound, "AuthorizationPolicy httpbin/httpbin-bogus-ns"}, - {msg.ReferencedResourceNotFound, "AuthorizationPolicy httpbin/httpbin-bogus-ns"}, - {msg.ReferencedResourceNotFound, "AuthorizationPolicy httpbin/httpbin-bogus-not-ns"}, - {msg.ReferencedResourceNotFound, "AuthorizationPolicy httpbin/httpbin-bogus-not-ns"}, - }, - }, - { - name: "destinationrule with no cacert, simple at destinationlevel", - inputFiles: []string{ - "testdata/destinationrule-simple-destination.yaml", - }, - analyzer: &destinationrule.CaCertificateAnalyzer{}, - expected: []message{ - {msg.NoServerCertificateVerificationDestinationLevel, "DestinationRule db-tls"}, - }, - }, - { - name: "destinationrule with no cacert, mutual at destinationlevel", - inputFiles: []string{ - "testdata/destinationrule-mutual-destination.yaml", - }, - analyzer: &destinationrule.CaCertificateAnalyzer{}, - expected: []message{ - {msg.NoServerCertificateVerificationDestinationLevel, "DestinationRule db-mtls"}, - }, - }, - { - name: "destinationrule with no cacert, simple at portlevel", - inputFiles: []string{ - "testdata/destinationrule-simple-port.yaml", - }, - analyzer: &destinationrule.CaCertificateAnalyzer{}, - expected: []message{ - {msg.NoServerCertificateVerificationPortLevel, "DestinationRule db-tls"}, - }, - }, - { - name: "destinationrule with no cacert, mutual at portlevel", - inputFiles: []string{ - "testdata/destinationrule-mutual-port.yaml", - }, - analyzer: &destinationrule.CaCertificateAnalyzer{}, - expected: []message{ - {msg.NoServerCertificateVerificationPortLevel, "DestinationRule db-mtls"}, - }, - }, - { - name: "destinationrule with no cacert, mutual at destinationlevel and simple at port level", - inputFiles: []string{ - "testdata/destinationrule-compound-simple-mutual.yaml", - }, - analyzer: &destinationrule.CaCertificateAnalyzer{}, - expected: []message{ - {msg.NoServerCertificateVerificationDestinationLevel, "DestinationRule db-mtls"}, - {msg.NoServerCertificateVerificationPortLevel, "DestinationRule db-mtls"}, - }, - }, - { - name: "destinationrule with no cacert, simple at destinationlevel and mutual at port level", - inputFiles: []string{ - "testdata/destinationrule-compound-mutual-simple.yaml", - }, - analyzer: &destinationrule.CaCertificateAnalyzer{}, - expected: []message{ - {msg.NoServerCertificateVerificationPortLevel, "DestinationRule db-mtls"}, - {msg.NoServerCertificateVerificationDestinationLevel, "DestinationRule db-mtls"}, - }, - }, - { - name: "destinationrule with both cacerts", - inputFiles: []string{ - "testdata/destinationrule-with-ca.yaml", - }, - analyzer: &destinationrule.CaCertificateAnalyzer{}, - expected: []message{}, - }, - { - name: "dupmatches", - inputFiles: []string{ - "testdata/virtualservice_dupmatches.yaml", - "testdata/virtualservice_overlappingmatches.yaml", - }, - analyzer: schemaValidation.CollectionValidationAnalyzer(collections.IstioNetworkingV1Alpha3Virtualservices), - expected: []message{ - {msg.VirtualServiceUnreachableRule, "VirtualService duplicate-match"}, - {msg.VirtualServiceUnreachableRule, "VirtualService foo/sample-foo-cluster01"}, - {msg.VirtualServiceIneffectiveMatch, "VirtualService almost-duplicate-match"}, - {msg.VirtualServiceIneffectiveMatch, "VirtualService duplicate-match"}, - - {msg.VirtualServiceUnreachableRule, "VirtualService duplicate-tcp-match"}, - {msg.VirtualServiceUnreachableRule, "VirtualService duplicate-empty-tcp"}, - {msg.VirtualServiceIneffectiveMatch, "VirtualService almost-duplicate-tcp-match"}, - {msg.VirtualServiceIneffectiveMatch, "VirtualService duplicate-tcp-match"}, - - {msg.VirtualServiceUnreachableRule, "VirtualService none/tls-routing"}, - {msg.VirtualServiceIneffectiveMatch, "VirtualService none/tls-routing-almostmatch"}, - {msg.VirtualServiceIneffectiveMatch, "VirtualService none/tls-routing"}, - - {msg.VirtualServiceIneffectiveMatch, "VirtualService non-method-get"}, - {msg.VirtualServiceIneffectiveMatch, "VirtualService overlapping-in-single-match"}, - {msg.VirtualServiceIneffectiveMatch, "VirtualService overlapping-in-two-matches"}, - {msg.VirtualServiceIneffectiveMatch, "VirtualService overlapping-mathes-with-different-methods"}, - }, - }, - { - name: "host defined in virtualservice not found in the gateway", - inputFiles: []string{ - "testdata/virtualservice_host_not_found_gateway.yaml", - }, - analyzer: &virtualservice.GatewayAnalyzer{}, - expected: []message{ - {msg.VirtualServiceHostNotFoundInGateway, "VirtualService default/testing-service-02-test-01"}, - {msg.VirtualServiceHostNotFoundInGateway, "VirtualService default/testing-service-02-test-02"}, - {msg.VirtualServiceHostNotFoundInGateway, "VirtualService default/testing-service-02-test-03"}, - {msg.VirtualServiceHostNotFoundInGateway, "VirtualService default/testing-service-03-test-04"}, - }, - }, - { - name: "host defined in virtualservice not found in the gateway", - inputFiles: []string{ - "testdata/virtualservice_host_not_found_gateway_with_ns_prefix.yaml", - }, - analyzer: &virtualservice.GatewayAnalyzer{}, - expected: []message{ - {msg.VirtualServiceHostNotFoundInGateway, "VirtualService default/testing-service-01-test-01"}, - }, - }, - { - name: "missing Addresses and Protocol in Service Entry", - inputFiles: []string{ - "testdata/serviceentry-missing-addresses-protocol.yaml", - }, - analyzer: &serviceentry.ProtocolAdressesAnalyzer{}, - expected: []message{ - {msg.ServiceEntryAddressesRequired, "ServiceEntry default/service-entry-test-03"}, - {msg.ServiceEntryAddressesRequired, "ServiceEntry default/service-entry-test-04"}, - {msg.ServiceEntryAddressesRequired, "ServiceEntry default/service-entry-test-07"}, - }, - }, - { - name: "certificate duplication in Gateway", - inputFiles: []string{ - "testdata/gateway-duplicate-certificate.yaml", - }, - analyzer: &gateway.CertificateAnalyzer{}, - expected: []message{ - {msg.GatewayDuplicateCertificate, "Gateway dubbo-system/gateway-01-test-01"}, - {msg.GatewayDuplicateCertificate, "Gateway dubbo-system/gateway-02-test-01"}, - {msg.GatewayDuplicateCertificate, "Gateway dubbo-system/gateway-01-test-02"}, - {msg.GatewayDuplicateCertificate, "Gateway default/gateway-01-test-03"}, - }, - }, - { - name: "webook", - inputFiles: []string{ - "testdata/webhook.yaml", - }, - analyzer: &webhook.Analyzer{}, - expected: []message{ - {msg.InvalidWebhook, "MutatingWebhookConfiguration istio-sidecar-injector-missing-overlap"}, - {msg.InvalidWebhook, "MutatingWebhookConfiguration istio-sidecar-injector-missing-overlap"}, - {msg.InvalidWebhook, "MutatingWebhookConfiguration istio-sidecar-injector-overlap"}, - }, - }, - { - name: "Route Rule no effect on Ingress", - inputFiles: []string{ - "testdata/virtualservice_route_rule_no_effects_ingress.yaml", - }, - analyzer: &virtualservice.DestinationHostAnalyzer{}, - expected: []message{ - {msg.IngressRouteRulesNotAffected, "VirtualService default/testing-service-01-test-01"}, - }, - }, - { - name: "Application Pod SecurityContext with UID 1337", - inputFiles: []string{ - "testdata/pod-sec-uid.yaml", - }, - analyzer: &deployment.ApplicationUIDAnalyzer{}, - expected: []message{ - {msg.InvalidApplicationUID, "Pod pod-sec-uid"}, - }, - }, - { - name: "Application Container SecurityContext with UID 1337", - inputFiles: []string{ - "testdata/pod-con-sec-uid.yaml", - }, - analyzer: &deployment.ApplicationUIDAnalyzer{}, - expected: []message{ - {msg.InvalidApplicationUID, "Pod con-sec-uid"}, - }, - }, - { - name: "Deployment Pod SecurityContext with UID 1337", - inputFiles: []string{ - "testdata/deployment-pod-sec-uid.yaml", - }, - analyzer: &deployment.ApplicationUIDAnalyzer{}, - expected: []message{ - {msg.InvalidApplicationUID, "Deployment deploy-pod-sec-uid"}, - }, - }, - { - name: "Deployment Container SecurityContext with UID 1337", - inputFiles: []string{ - "testdata/deployment-con-sec-uid.yaml", - }, - analyzer: &deployment.ApplicationUIDAnalyzer{}, - expected: []message{ - {msg.InvalidApplicationUID, "Deployment deploy-con-sec-uid"}, - }, - }, - { - name: "Detect `image: auto` in non-injected pods", - inputFiles: []string{ - "testdata/image-auto.yaml", - }, - analyzer: &injection.ImageAutoAnalyzer{}, - expected: []message{ - {msg.ImageAutoWithoutInjectionWarning, "Deployment not-injected/non-injected-gateway-deployment"}, - {msg.ImageAutoWithoutInjectionError, "Pod default/injected-pod"}, - }, - }, - { - name: "ExternalNameServiceTypeInvalidPortName", - inputFiles: []string{"testdata/incorrect-port-name-external-name-service-type.yaml"}, - analyzer: &service.PortNameAnalyzer{}, - expected: []message{ - {msg.ExternalNameServiceTypeInvalidPortName, "Service nginx-ns/nginx"}, - {msg.ExternalNameServiceTypeInvalidPortName, "Service nginx-ns2/nginx-svc2"}, - {msg.ExternalNameServiceTypeInvalidPortName, "Service nginx-ns3/nginx-svc3"}, - }, - }, - { - name: "ExternalNameServiceTypeValidPortName", - inputFiles: []string{"testdata/correct-port-name-external-name-service-type.yaml"}, - analyzer: &service.PortNameAnalyzer{}, - expected: []message{ - // Test no messages are received for correct port name - }, - }, - { - name: "EnvoyFilterUsesRelativeOperation", - inputFiles: []string{"testdata/relative-envoy-filter-operation.yaml"}, - analyzer: &envoyfilter.EnvoyPatchAnalyzer{}, - expected: []message{ - {msg.EnvoyFilterUsesRelativeOperation, "EnvoyFilter bookinfo/test-reviews-lua-1"}, - {msg.EnvoyFilterUsesRelativeOperation, "EnvoyFilter bookinfo/test-reviews-lua-2"}, - }, - }, - { - name: "EnvoyFilterUsesAbsoluteOperation", - inputFiles: []string{"testdata/absolute-envoy-filter-operation.yaml"}, - analyzer: &envoyfilter.EnvoyPatchAnalyzer{}, - expected: []message{ - // Test no messages are received for absolute operation usage - }, - }, -} - -// regex patterns for analyzer names that should be explicitly ignored for testing -var ignoreAnalyzers = []string{ - // ValidationAnalyzer doesn't have any of its own logic, it just wraps the schema validation. - // We assume that detailed testing for schema validation is being done elsewhere. - // Testing the ValidationAnalyzer as a wrapper is done in a separate unit test.) - `schema\.ValidationAnalyzer\.*`, -} - -// TestAnalyzers allows for table-based testing of Analyzers. -func TestAnalyzers(t *testing.T) { - requestedInputsByAnalyzer := make(map[string]map[collection.Name]struct{}) - - // For each test case, verify we get the expected messages as output - for _, tc := range testGrid { - tc := tc // Capture range variable so subtests work correctly - t.Run(tc.name, func(t *testing.T) { - g := NewWithT(t) - - // Set up a hook to record which collections are accessed by each analyzer - analyzerName := tc.analyzer.Metadata().Name - cr := func(col collection.Name) { - if _, ok := requestedInputsByAnalyzer[analyzerName]; !ok { - requestedInputsByAnalyzer[analyzerName] = make(map[collection.Name]struct{}) - } - requestedInputsByAnalyzer[analyzerName][col] = struct{}{} - } - - // Set up Analyzer for this test case - sa, err := setupAnalyzerForCase(tc, cr) - if err != nil { - t.Fatalf("Error setting up analysis for testcase %s: %v", tc.name, err) - } - - // Run the analysis - result, err := runAnalyzer(sa) - if err != nil { - t.Fatalf("Error running analysis on testcase %s: %v", tc.name, err) - } - - g.Expect(extractFields(result.Messages)).To(ConsistOf(tc.expected), "%v", prettyPrintMessages(result.Messages)) - }) - } - - // Verify that the collections actually accessed during testing actually match - // the collections declared as inputs for each of the analyzers - t.Run("CheckMetadataInputs", func(t *testing.T) { - g := NewWithT(t) - outer: - for _, a := range All() { - analyzerName := a.Metadata().Name - - // Skip this check for explicitly ignored analyzers - for _, regex := range ignoreAnalyzers { - match, err := regexp.MatchString(regex, analyzerName) - if err != nil { - t.Fatalf("Error compiling ignoreAnalyzers regex %q: %v", regex, err) - } - if match { - continue outer - } - } - - requestedInputs := make([]collection.Name, 0) - for col := range requestedInputsByAnalyzer[analyzerName] { - requestedInputs = append(requestedInputs, col) - } - - g.Expect(a.Metadata().Inputs).To(ConsistOf(requestedInputs), fmt.Sprintf( - "Metadata inputs for analyzer %q don't match actual collections accessed during testing. "+ - "Either the metadata is wrong or the test cases for the analyzer are insufficient.", analyzerName)) - } - }) -} - -// Verify that all of the analyzers tested here are also registered in All() -func TestAnalyzersInAll(t *testing.T) { - g := NewWithT(t) - - var allNames []string - for _, a := range All() { - allNames = append(allNames, a.Metadata().Name) - } - - for _, tc := range testGrid { - if !tc.skipAll { - g.Expect(allNames).To(ContainElement(tc.analyzer.Metadata().Name)) - } - } -} - -func TestAnalyzersHaveUniqueNames(t *testing.T) { - g := NewWithT(t) - - existingNames := sets.New() - for _, a := range All() { - n := a.Metadata().Name - // TODO (Nino-K): remove this condition once metadata is clean up - if existingNames.Contains(n) && n == "schema.ValidationAnalyzer.ServiceEntry" { - continue - } - g.Expect(existingNames.Contains(n)).To(BeFalse(), fmt.Sprintf("Analyzer name %q is used more than once. "+ - "Analyzers should be registered in All() exactly once and have a unique name.", n)) - - existingNames.Insert(n) - } -} - -func TestAnalyzersHaveDescription(t *testing.T) { - g := NewWithT(t) - - for _, a := range All() { - g.Expect(a.Metadata().Description).ToNot(Equal("")) - } -} - -func setupAnalyzerForCase(tc testCase, cr local.CollectionReporterFn) (*local.IstiodAnalyzer, error) { - sa := local.NewSourceAnalyzer(analysis.Combine("testCase", tc.analyzer), "", "dubbo-system", cr, true, 10*time.Second) - - // If a mesh config file is specified, use it instead of the defaults - if tc.meshConfigFile != "" { - err := sa.AddFileKubeMeshConfig(tc.meshConfigFile) - if err != nil { - return nil, fmt.Errorf("error applying mesh config file %s: %v", tc.meshConfigFile, err) - } - } - - if tc.meshNetworksFile != "" { - err := sa.AddFileKubeMeshNetworks(tc.meshNetworksFile) - if err != nil { - return nil, fmt.Errorf("error apply mesh networks file %s: %v", tc.meshNetworksFile, err) - } - } - - // Include default resources - err := sa.AddDefaultResources() - if err != nil { - return nil, fmt.Errorf("error adding default resources: %v", err) - } - - // Gather test files - var files []local.ReaderSource - for _, f := range tc.inputFiles { - of, err := os.Open(f) - if err != nil { - return nil, fmt.Errorf("error opening test file: %q", f) - } - files = append(files, local.ReaderSource{Name: f, Reader: of}) - } - - // Include resources from test files - err = sa.AddReaderKubeSource(files) - if err != nil { - return nil, fmt.Errorf("error setting up file kube source on testcase %s: %v", tc.name, err) - } - - return sa, nil -} - -func runAnalyzer(sa *local.IstiodAnalyzer) (local.AnalysisResult, error) { - cancel := make(chan struct{}) - result, err := sa.Analyze(cancel) - if err != nil { - return local.AnalysisResult{}, err - } - return result, err -} - -// Pull just the fields we want to check out of diag.Message -func extractFields(msgs diag.Messages) []message { - result := make([]message, 0) - for _, m := range msgs { - expMsg := message{ - messageType: m.Type, - } - if m.Resource != nil { - expMsg.origin = m.Resource.Origin.FriendlyName() - } - - result = append(result, expMsg) - } - return result -} - -func prettyPrintMessages(msgs diag.Messages) string { - var sb strings.Builder - fmt.Fprintf(&sb, "Analyzer messages: %d\n", len(msgs)) - for _, m := range msgs { - fmt.Fprintf(&sb, "\t%s\n", m.String()) - } - return sb.String() -} diff --git a/pkg/config/analysis/analyzers/annotations/annotations.go b/pkg/config/analysis/analyzers/annotations/annotations.go deleted file mode 100644 index 6abbbca93..000000000 --- a/pkg/config/analysis/analyzers/annotations/annotations.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package annotations - -import ( - "strings" -) - -import ( - "istio.io/api/annotation" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/kube/inject" -) - -// K8sAnalyzer checks for misplaced and invalid Istio annotations in K8s resources -type K8sAnalyzer struct{} - -var istioAnnotations = annotation.AllResourceAnnotations() - -// Metadata implements analyzer.Analyzer -func (*K8sAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "annotations.K8sAnalyzer", - Description: "Checks for misplaced and invalid Istio annotations in Kubernetes resources", - Inputs: collection.Names{ - collections.K8SCoreV1Namespaces.Name(), - collections.K8SCoreV1Services.Name(), - collections.K8SCoreV1Pods.Name(), - collections.K8SAppsV1Deployments.Name(), - }, - } -} - -// Analyze implements analysis.Analyzer -func (fa *K8sAnalyzer) Analyze(ctx analysis.Context) { - ctx.ForEach(collections.K8SCoreV1Namespaces.Name(), func(r *resource.Instance) bool { - fa.allowAnnotations(r, ctx, "Namespace", collections.K8SCoreV1Namespaces.Name()) - return true - }) - ctx.ForEach(collections.K8SCoreV1Services.Name(), func(r *resource.Instance) bool { - fa.allowAnnotations(r, ctx, "Service", collections.K8SCoreV1Services.Name()) - return true - }) - ctx.ForEach(collections.K8SCoreV1Pods.Name(), func(r *resource.Instance) bool { - fa.allowAnnotations(r, ctx, "Pod", collections.K8SCoreV1Pods.Name()) - return true - }) - ctx.ForEach(collections.K8SAppsV1Deployments.Name(), func(r *resource.Instance) bool { - fa.allowAnnotations(r, ctx, "Deployment", collections.K8SAppsV1Deployments.Name()) - return true - }) -} - -var deprecationExtraMessages = map[string]string{ - annotation.SidecarInject.Name: ` in favor of the "sidecar.istio.io/inject" label`, -} - -func (*K8sAnalyzer) allowAnnotations(r *resource.Instance, ctx analysis.Context, kind string, collectionType collection.Name) { - if len(r.Metadata.Annotations) == 0 { - return - } - - // It is fine if the annotation is kubectl.kubernetes.io/last-applied-configuration. -outer: - for ann, value := range r.Metadata.Annotations { - if !istioAnnotation(ann) { - continue - } - - annotationDef := lookupAnnotation(ann) - if annotationDef == nil { - m := msg.NewUnknownAnnotation(r, ann) - util.AddLineNumber(r, ann, m) - - ctx.Report(collectionType, m) - continue - } - - if annotationDef.Deprecated { - if _, f := r.Metadata.Labels[annotation.SidecarInject.Name]; f && ann == annotation.SidecarInject.Name { - // Skip to avoid noise; the user has the deprecated annotation but they also have the replacement - // This means they are likely aware its deprecated, but are keeping both variants around for maximum - // compatibility - } else { - m := msg.NewDeprecatedAnnotation(r, ann, deprecationExtraMessages[annotationDef.Name]) - util.AddLineNumber(r, ann, m) - - ctx.Report(collectionType, m) - } - } - - // If the annotation def attaches to Any, exit early - for _, rt := range annotationDef.Resources { - if rt == annotation.Any { - continue outer - } - } - - attachesTo := resourceTypesAsStrings(annotationDef.Resources) - if !contains(attachesTo, kind) { - m := msg.NewMisplacedAnnotation(r, ann, strings.Join(attachesTo, ", ")) - util.AddLineNumber(r, ann, m) - - ctx.Report(collectionType, m) - continue - } - - validationFunction := inject.AnnotationValidation[ann] - if validationFunction != nil { - if err := validationFunction(value); err != nil { - m := msg.NewInvalidAnnotation(r, ann, err.Error()) - util.AddLineNumber(r, ann, m) - - ctx.Report(collectionType, m) - continue - } - } - } -} - -// istioAnnotation is true if the annotation is in Istio's namespace -func istioAnnotation(ann string) bool { - // We document this Kubernetes annotation, we should analyze it as well - if ann == "kubernetes.io/ingress.class" { - return true - } - - parts := strings.Split(ann, "/") - if len(parts) == 0 { - return false - } - - if !strings.HasSuffix(parts[0], "istio.io") { - return false - } - - return true -} - -func contains(candidates []string, s string) bool { - for _, candidate := range candidates { - if s == candidate { - return true - } - } - - return false -} - -func lookupAnnotation(ann string) *annotation.Instance { - for _, candidate := range istioAnnotations { - if candidate.Name == ann { - return candidate - } - } - - return nil -} - -func resourceTypesAsStrings(resourceTypes []annotation.ResourceTypes) []string { - retval := []string{} - for _, resourceType := range resourceTypes { - if s := resourceType.String(); s != "Unknown" { - retval = append(retval, s) - } - } - return retval -} diff --git a/pkg/config/analysis/analyzers/authz/authorizationpolicies.go b/pkg/config/analysis/analyzers/authz/authorizationpolicies.go deleted file mode 100644 index 7174b443a..000000000 --- a/pkg/config/analysis/analyzers/authz/authorizationpolicies.go +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package authz - -import ( - "fmt" - "strings" -) - -import ( - "istio.io/api/mesh/v1alpha1" - "istio.io/api/security/v1beta1" - k8s_labels "k8s.io/apimachinery/pkg/labels" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// AuthorizationPoliciesAnalyzer checks the validity of authorization policies -type AuthorizationPoliciesAnalyzer struct{} - -var ( - _ analysis.Analyzer = &AuthorizationPoliciesAnalyzer{} - meshConfig *v1alpha1.MeshConfig -) - -func (a *AuthorizationPoliciesAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "auth.AuthorizationPoliciesAnalyzer", - Description: "Checks the validity of authorization policies", - Inputs: collection.Names{ - collections.IstioMeshV1Alpha1MeshConfig.Name(), - collections.IstioSecurityV1Beta1Authorizationpolicies.Name(), - collections.K8SCoreV1Namespaces.Name(), - collections.K8SCoreV1Pods.Name(), - }, - } -} - -func (a *AuthorizationPoliciesAnalyzer) Analyze(c analysis.Context) { - podLabelsMap := initPodLabelsMap(c) - - c.ForEach(collections.IstioSecurityV1Beta1Authorizationpolicies.Name(), func(r *resource.Instance) bool { - a.analyzeNoMatchingWorkloads(r, c, podLabelsMap) - a.analyzeNamespaceNotFound(r, c) - return true - }) -} - -func (a *AuthorizationPoliciesAnalyzer) analyzeNoMatchingWorkloads(r *resource.Instance, c analysis.Context, podLabelsMap map[string][]k8s_labels.Set) { - ap := r.Message.(*v1beta1.AuthorizationPolicy) - apNs := r.Metadata.FullName.Namespace.String() - - // If AuthzPolicy is mesh-wide - if meshWidePolicy(apNs, c) { - // If it has selector, need further analysis - if ap.Selector != nil { - apSelector := k8s_labels.SelectorFromSet(ap.Selector.MatchLabels) - // If there is at least one pod matching the selector within the whole mesh - if !hasMatchingPodsRunning(apSelector, podLabelsMap) { - c.Report(collections.IstioSecurityV1Beta1Authorizationpolicies.Name(), msg.NewNoMatchingWorkloadsFound(r, apSelector.String())) - } - } - - // If AuthzPolicy is mesh-wide and selectorless, - // no need to keep the analysis - return - } - - // If the AuthzPolicy is namespace-wide and there are present Pods, - // no messages should be triggered. - if ap.Selector == nil { - if len(podLabelsMap[apNs]) == 0 { - c.Report(collections.IstioSecurityV1Beta1Authorizationpolicies.Name(), msg.NewNoMatchingWorkloadsFound(r, "")) - } - return - } - - // If the AuthzPolicy has Selector, then need to find a matching Pod. - apSelector := k8s_labels.SelectorFromSet(ap.Selector.MatchLabels) - if !hasMatchingPodsRunningIn(apSelector, podLabelsMap[apNs]) { - c.Report(collections.IstioSecurityV1Beta1Authorizationpolicies.Name(), msg.NewNoMatchingWorkloadsFound(r, apSelector.String())) - } -} - -// Returns true when the namespace is the root namespace. -// It takes the MeshConfig names istio, if not the last instance found. -func meshWidePolicy(ns string, c analysis.Context) bool { - mConf := fetchMeshConfig(c) - return mConf != nil && ns == mConf.GetRootNamespace() -} - -func fetchMeshConfig(c analysis.Context) *v1alpha1.MeshConfig { - if meshConfig != nil { - return meshConfig - } - - c.ForEach(collections.IstioMeshV1Alpha1MeshConfig.Name(), func(r *resource.Instance) bool { - meshConfig = r.Message.(*v1alpha1.MeshConfig) - return r.Metadata.FullName.Name != util.MeshConfigName - }) - - return meshConfig -} - -func hasMatchingPodsRunning(selector k8s_labels.Selector, podLabelsMap map[string][]k8s_labels.Set) bool { - for _, setList := range podLabelsMap { - if hasMatchingPodsRunningIn(selector, setList) { - return true - } - } - return false -} - -func hasMatchingPodsRunningIn(selector k8s_labels.Selector, setList []k8s_labels.Set) bool { - hasMatchingPods := false - for _, labels := range setList { - if selector.Matches(labels) { - hasMatchingPods = true - break - } - } - return hasMatchingPods -} - -func (a *AuthorizationPoliciesAnalyzer) analyzeNamespaceNotFound(r *resource.Instance, c analysis.Context) { - ap := r.Message.(*v1beta1.AuthorizationPolicy) - - for i, rule := range ap.Rules { - for j, from := range rule.From { - for k, ns := range append(from.Source.Namespaces, from.Source.NotNamespaces...) { - if !matchNamespace(ns, c) { - m := msg.NewReferencedResourceNotFound(r, "namespace", ns) - - nsIndex := k - if nsIndex >= len(from.Source.Namespaces) { - nsIndex -= len(from.Source.Namespaces) - } - - if line, ok := util.ErrorLine(r, fmt.Sprintf(util.AuthorizationPolicyNameSpace, i, j, nsIndex)); ok { - m.Line = line - } - - c.Report(collections.IstioSecurityV1Beta1Authorizationpolicies.Name(), m) - } - } - } - } -} - -func matchNamespace(exp string, c analysis.Context) bool { - match := false - c.ForEach(collections.K8SCoreV1Namespaces.Name(), func(r *resource.Instance) bool { - ns := r.Metadata.FullName.String() - match = namespaceMatch(ns, exp) - return !match - }) - - return match -} - -func namespaceMatch(ns, exp string) bool { - if strings.EqualFold(exp, "*") { - return true - } - if strings.HasPrefix(exp, "*") { - return strings.HasSuffix(ns, strings.TrimPrefix(exp, "*")) - } - if strings.HasSuffix(exp, "*") { - return strings.HasPrefix(ns, strings.TrimSuffix(exp, "*")) - } - - return strings.EqualFold(ns, exp) -} - -// Build a map indexed by namespace with in-mesh Pod's labels -func initPodLabelsMap(c analysis.Context) map[string][]k8s_labels.Set { - podLabelsMap := make(map[string][]k8s_labels.Set) - - c.ForEach(collections.K8SCoreV1Pods.Name(), func(r *resource.Instance) bool { - pLabels := k8s_labels.Set(r.Metadata.Labels) - - ns := r.Metadata.FullName.Namespace.String() - if podLabelsMap[ns] == nil { - podLabelsMap[ns] = make([]k8s_labels.Set, 0) - } - - if util.PodInMesh(r, c) { - podLabelsMap[ns] = append(podLabelsMap[ns], pLabels) - } - - return true - }) - - return podLabelsMap -} diff --git a/pkg/config/analysis/analyzers/authz/authorizationpolicies_test.go b/pkg/config/analysis/analyzers/authz/authorizationpolicies_test.go deleted file mode 100644 index c21855f15..000000000 --- a/pkg/config/analysis/analyzers/authz/authorizationpolicies_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package authz - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -func TestNamespaceMatch(t *testing.T) { - assert.Equal(t, namespaceMatch("test-login", "*"), true) - - assert.Equal(t, namespaceMatch("test-login", "test-*"), true) - assert.Equal(t, namespaceMatch("test-login", "*-test"), false) - - assert.Equal(t, namespaceMatch("test-login", "login-*"), false) - assert.Equal(t, namespaceMatch("test-login", "*-login"), true) -} diff --git a/pkg/config/analysis/analyzers/deployment/pod.go b/pkg/config/analysis/analyzers/deployment/pod.go deleted file mode 100644 index 2699bbe47..000000000 --- a/pkg/config/analysis/analyzers/deployment/pod.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package deployment - -import ( - apps_v1 "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -type ApplicationUIDAnalyzer struct{} - -var _ analysis.Analyzer = &ApplicationUIDAnalyzer{} - -const ( - UserID = int64(1337) -) - -func (appUID *ApplicationUIDAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "applicationUID.Analyzer", - Description: "Checks invalid application UID", - Inputs: collection.Names{ - collections.K8SCoreV1Pods.Name(), - collections.K8SAppsV1Deployments.Name(), - }, - } -} - -func (appUID *ApplicationUIDAnalyzer) Analyze(context analysis.Context) { - context.ForEach(collections.K8SCoreV1Pods.Name(), func(resource *resource.Instance) bool { - appUID.analyzeAppUIDForPod(resource, context) - return true - }) - context.ForEach(collections.K8SAppsV1Deployments.Name(), func(resource *resource.Instance) bool { - appUID.analyzeAppUIDForDeployment(resource, context) - return true - }) -} - -func (appUID *ApplicationUIDAnalyzer) analyzeAppUIDForPod(resource *resource.Instance, context analysis.Context) { - p := resource.Message.(*v1.PodSpec) - // Skip analyzing control plane for IST0144 - if util.IsIstioControlPlane(resource) { - return - } - message := msg.NewInvalidApplicationUID(resource) - - if p.SecurityContext != nil && p.SecurityContext.RunAsUser != nil { - if *p.SecurityContext.RunAsUser == UserID { - context.Report(collections.K8SCoreV1Pods.Name(), message) - } - } - for _, container := range p.Containers { - if container.Name != util.IstioProxyName && container.Name != util.IstioOperator { - if container.SecurityContext != nil && container.SecurityContext.RunAsUser != nil { - if *container.SecurityContext.RunAsUser == UserID { - context.Report(collections.K8SCoreV1Pods.Name(), message) - } - } - } - } -} - -func (appUID *ApplicationUIDAnalyzer) analyzeAppUIDForDeployment(resource *resource.Instance, context analysis.Context) { - d := resource.Message.(*apps_v1.DeploymentSpec) - // Skip analyzing control plane for IST0144 - if util.IsIstioControlPlane(resource) { - return - } - message := msg.NewInvalidApplicationUID(resource) - spec := d.Template.Spec - - if spec.SecurityContext != nil && spec.SecurityContext.RunAsUser != nil { - if *spec.SecurityContext.RunAsUser == UserID { - context.Report(collections.K8SAppsV1Deployments.Name(), message) - } - } - for _, container := range spec.Containers { - if container.Name != util.IstioProxyName && container.Name != util.IstioOperator { - if container.SecurityContext != nil && container.SecurityContext.RunAsUser != nil { - if *container.SecurityContext.RunAsUser == UserID { - context.Report(collections.K8SAppsV1Deployments.Name(), message) - } - } - } - } -} diff --git a/pkg/config/analysis/analyzers/deployment/services.go b/pkg/config/analysis/analyzers/deployment/services.go deleted file mode 100644 index c808f5107..000000000 --- a/pkg/config/analysis/analyzers/deployment/services.go +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package deployment - -import ( - "fmt" - "strconv" -) - -import ( - apps_v1 "k8s.io/api/apps/v1" - core_v1 "k8s.io/api/core/v1" - k8s_labels "k8s.io/apimachinery/pkg/labels" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -type ServiceAssociationAnalyzer struct{} - -var _ analysis.Analyzer = &ServiceAssociationAnalyzer{} - -type ( - PortMap map[int32]ProtocolMap - ProtocolMap map[core_v1.Protocol]ServiceNames - ServiceNames []string - ServiceSpecWithName struct { - Name string - Spec *core_v1.ServiceSpec - } -) - -// targetPort port serviceName -type targetPortMap map[string]map[int32]string - -func (s *ServiceAssociationAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "deployment.MultiServiceAnalyzer", - Description: "Checks association between services and pods", - Inputs: collection.Names{ - collections.K8SCoreV1Services.Name(), - collections.K8SAppsV1Deployments.Name(), - collections.K8SCoreV1Namespaces.Name(), - }, - } -} - -func (s *ServiceAssociationAnalyzer) Analyze(c analysis.Context) { - c.ForEach(collections.K8SAppsV1Deployments.Name(), func(r *resource.Instance) bool { - if util.DeploymentInMesh(r, c) { - s.analyzeDeploymentPortProtocol(r, c) - s.analyzeDeploymentTargetPorts(r, c) - } - return true - }) -} - -// analyzeDeploymentPortProtocol analyzes the specific service mesh deployment -func (s *ServiceAssociationAnalyzer) analyzeDeploymentPortProtocol(r *resource.Instance, c analysis.Context) { - // Find matching services with resulting pod from deployment - matchingSvcs := s.findMatchingServices(r, c) - - // If there isn't any matching service, generate message: At least one service is needed. - if len(matchingSvcs) == 0 { - c.Report(collections.K8SAppsV1Deployments.Name(), msg.NewDeploymentRequiresServiceAssociated(r)) - return - } - - // Generate a port map from the matching services. - // It creates a structure that will allow us to detect - // if there are different protocols for the same port. - portMap := servicePortMap(matchingSvcs) - - // Determining which ports use more than one protocol. - for port := range portMap { - // In case there are two protocols using same port number, generate a message - protMap := portMap[port] - if len(protMap) > 1 { - // Collect names from both protocols - svcNames := make(ServiceNames, 0) - for protocol := range protMap { - svcNames = append(svcNames, protMap[protocol]...) - } - m := msg.NewDeploymentAssociatedToMultipleServices(r, r.Metadata.FullName.Name.String(), port, svcNames) - - if line, ok := util.ErrorLine(r, fmt.Sprintf(util.MetadataName)); ok { - m.Line = line - } - - // Reporting the message for the deployment, port and conflicting services. - c.Report(collections.K8SAppsV1Deployments.Name(), m) - } - } -} - -// analyzeDeploymentPortProtocol analyzes the targetPorts conflicting -func (s *ServiceAssociationAnalyzer) analyzeDeploymentTargetPorts(r *resource.Instance, c analysis.Context) { - // Find matching services with resulting pod from deployment - matchingSvcs := s.findMatchingServices(r, c) - - // If there isn't any matching service, generate message: At least one service is needed. - if len(matchingSvcs) == 0 { - c.Report(collections.K8SAppsV1Deployments.Name(), msg.NewDeploymentRequiresServiceAssociated(r)) - return - } - - tpm := serviceTargetPortsMap(matchingSvcs) - - // Determining which ports use more than one protocol. - for targetPort, portServices := range tpm { - if len(portServices) > 1 { - // Collect names from both protocols - svcNames := make(ServiceNames, 0, len(portServices)) - ports := make([]int32, 0, len(portServices)) - for p, s := range portServices { - svcNames = append(svcNames, s) - ports = append(ports, p) - } - m := msg.NewDeploymentConflictingPorts(r, r.Metadata.FullName.Name.String(), svcNames, targetPort, ports) - - if line, ok := util.ErrorLine(r, fmt.Sprintf(util.MetadataName)); ok { - m.Line = line - } - - // Reporting the message for the deployment, port and conflicting services. - c.Report(collections.K8SAppsV1Deployments.Name(), m) - } - } -} - -// findMatchingServices returns an slice of Services that matches with deployment's pods. -func (s *ServiceAssociationAnalyzer) findMatchingServices(r *resource.Instance, c analysis.Context) []ServiceSpecWithName { - matchingSvcs := make([]ServiceSpecWithName, 0) - d := r.Message.(*apps_v1.DeploymentSpec) - deploymentNS := r.Metadata.FullName.Namespace.String() - - c.ForEach(collections.K8SCoreV1Services.Name(), func(r *resource.Instance) bool { - s := r.Message.(*core_v1.ServiceSpec) - - sSelector := k8s_labels.SelectorFromSet(s.Selector) - pLabels := k8s_labels.Set(d.Template.Labels) - if sSelector.Matches(pLabels) && r.Metadata.FullName.Namespace.String() == deploymentNS { - matchingSvcs = append(matchingSvcs, ServiceSpecWithName{r.Metadata.FullName.String(), s}) - } - - return true - }) - - return matchingSvcs -} - -// servicePortMap build a map of ports and protocols for each Service. e.g. m[80]["TCP"] -> svcA, svcB, svcC -func servicePortMap(svcs []ServiceSpecWithName) PortMap { - portMap := PortMap{} - - for _, swn := range svcs { - svc := swn.Spec - for _, sPort := range svc.Ports { - // If it is the first occurrence of this port, create a ProtocolMap - if _, ok := portMap[sPort.Port]; !ok { - portMap[sPort.Port] = ProtocolMap{} - } - - // Default protocol is TCP - protocol := sPort.Protocol - if protocol == "" { - protocol = core_v1.ProtocolTCP - } - - // Appending the service information for the Port/Protocol combination - portMap[sPort.Port][protocol] = append(portMap[sPort.Port][protocol], swn.Name) - } - } - - return portMap -} - -// serviceTargetPortsMap build a map of targetPort and ports for each Service. e.g. m["80"][80] -> svc -func serviceTargetPortsMap(svcs []ServiceSpecWithName) targetPortMap { - pm := targetPortMap{} - for _, swn := range svcs { - svc := swn.Spec - for _, sPort := range svc.Ports { - p := sPort.TargetPort.String() - if p == "0" || p == "" { - // By default and for convenience, the targetPort is set to the same value as the port field. - p = strconv.Itoa(int(sPort.Port)) - } - if _, ok := pm[p]; !ok { - pm[p] = map[int32]string{} - } - pm[p][sPort.Port] = swn.Name - } - } - return pm -} diff --git a/pkg/config/analysis/analyzers/deprecation/deprecation.go b/pkg/config/analysis/analyzers/deprecation/deprecation.go deleted file mode 100644 index c7349c956..000000000 --- a/pkg/config/analysis/analyzers/deprecation/deprecation.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package deprecation - -import ( - "fmt" -) - -import ( - "istio.io/api/networking/v1alpha3" - k8sext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// FieldAnalyzer checks for deprecated Istio types and fields -type FieldAnalyzer struct{} - -// Tracks Istio CRDs removed from manifests/charts/base/crds/crd-all.gen.yaml -var deprecatedCRDs = []k8sext.CustomResourceDefinitionSpec{ - { - Group: "rbac.istio.io", - Names: k8sext.CustomResourceDefinitionNames{Kind: "ClusterRbacConfig"}, - }, - { - Group: "rbac.istio.io", - Names: k8sext.CustomResourceDefinitionNames{Kind: "RbacConfig"}, - }, - { - Group: "rbac.istio.io", - Names: k8sext.CustomResourceDefinitionNames{Kind: "ServiceRole"}, - }, - { - Group: "rbac.istio.io", - Names: k8sext.CustomResourceDefinitionNames{Kind: "ServiceRoleBinding"}, - }, -} - -// Currently we don't have an Istio API that tells which Istio API fields are deprecated. -// Run `find . -name "*.proto" -exec grep -i "deprecated=true" \{\} \; -print` -// to see what is deprecated. This analyzer is hand-crafted. - -// Metadata implements analyzer.Analyzer -func (*FieldAnalyzer) Metadata() analysis.Metadata { - deprecationInputs := collection.Names{ - collections.IstioNetworkingV1Alpha3Virtualservices.Name(), - collections.IstioNetworkingV1Alpha3Sidecars.Name(), - collections.K8SApiextensionsK8SIoV1Customresourcedefinitions.Name(), - } - - return analysis.Metadata{ - Name: "deprecation.DeprecationAnalyzer", - Description: "Checks for deprecated Istio types and fields", - Inputs: append(deprecationInputs, - collections.Deprecated.CollectionNames()...), - } -} - -// Analyze implements analysis.Analyzer -func (fa *FieldAnalyzer) Analyze(ctx analysis.Context) { - ctx.ForEach(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), func(r *resource.Instance) bool { - fa.analyzeVirtualService(r, ctx) - return true - }) - ctx.ForEach(collections.IstioNetworkingV1Alpha3Sidecars.Name(), func(r *resource.Instance) bool { - fa.analyzeSidecar(r, ctx) - return true - }) - ctx.ForEach(collections.K8SApiextensionsK8SIoV1Customresourcedefinitions.Name(), func(r *resource.Instance) bool { - fa.analyzeCRD(r, ctx) - return true - }) - for _, name := range collections.Deprecated.CollectionNames() { - ctx.ForEach(name, func(r *resource.Instance) bool { - ctx.Report(name, - msg.NewDeprecated(r, crDeprecatedMessage(name.String()))) - return true - }) - } -} - -func (*FieldAnalyzer) analyzeCRD(r *resource.Instance, ctx analysis.Context) { - for _, depCRD := range deprecatedCRDs { - var group, kind string - switch crd := r.Message.(type) { - case *k8sext.CustomResourceDefinition: - group = crd.Spec.Group - kind = crd.Spec.Names.Kind - case *k8sext.CustomResourceDefinitionSpec: - group = crd.Group - kind = crd.Names.Kind - } - if group == depCRD.Group && kind == depCRD.Names.Kind { - ctx.Report(collections.K8SApiextensionsK8SIoV1Customresourcedefinitions.Name(), - msg.NewDeprecated(r, crRemovedMessage(depCRD.Group, depCRD.Names.Kind))) - } - } -} - -func (*FieldAnalyzer) analyzeSidecar(r *resource.Instance, ctx analysis.Context) { - sc := r.Message.(*v1alpha3.Sidecar) - - if sc.OutboundTrafficPolicy != nil { - if sc.OutboundTrafficPolicy.EgressProxy != nil { - ctx.Report(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), - msg.NewDeprecated(r, ignoredMessage("OutboundTrafficPolicy.EgressProxy"))) - } - } -} - -func (*FieldAnalyzer) analyzeVirtualService(r *resource.Instance, ctx analysis.Context) { - vs := r.Message.(*v1alpha3.VirtualService) - - for _, httpRoute := range vs.Http { - if httpRoute.Fault != nil { - if httpRoute.Fault.Delay != nil { - // nolint: staticcheck - if httpRoute.Fault.Delay.Percent > 0 { - ctx.Report(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), - msg.NewDeprecated(r, replacedMessage("HTTPRoute.fault.delay.percent", "HTTPRoute.fault.delay.percentage"))) - } - } - } - } -} - -func replacedMessage(deprecated, replacement string) string { - return fmt.Sprintf("%s is deprecated; use %s", deprecated, replacement) -} - -func ignoredMessage(field string) string { - return fmt.Sprintf("%s ignored", field) -} - -func crDeprecatedMessage(typename string) string { - return fmt.Sprintf("Custom resource type %q is deprecated", typename) -} - -func crRemovedMessage(group, kind string) string { - return fmt.Sprintf("Custom resource type %s %s is removed", group, kind) -} diff --git a/pkg/config/analysis/analyzers/destinationrule/ca-certificates.go b/pkg/config/analysis/analyzers/destinationrule/ca-certificates.go deleted file mode 100644 index 643a06fdb..000000000 --- a/pkg/config/analysis/analyzers/destinationrule/ca-certificates.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package destinationrule - -import ( - "fmt" -) - -import ( - "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// CaCertificateAnalyzer checks if CaCertificate is set in case mode is SIMPLE/MUTUAL -type CaCertificateAnalyzer struct{} - -var _ analysis.Analyzer = &CaCertificateAnalyzer{} - -func (c *CaCertificateAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "destinationrule.CaCertificateAnalyzer", - Description: "Checks if caCertificates is set when TLS mode is SIMPLE/MUTUAL", - Inputs: collection.Names{ - collections.IstioNetworkingV1Alpha3Destinationrules.Name(), - }, - } -} - -func (c *CaCertificateAnalyzer) Analyze(ctx analysis.Context) { - ctx.ForEach(collections.IstioNetworkingV1Alpha3Destinationrules.Name(), func(r *resource.Instance) bool { - c.analyzeDestinationRule(r, ctx) - return true - }) -} - -func (c *CaCertificateAnalyzer) analyzeDestinationRule(r *resource.Instance, ctx analysis.Context) { - dr := r.Message.(*v1alpha3.DestinationRule) - drNs := r.Metadata.FullName.Namespace - drName := r.Metadata.FullName.String() - mode := dr.GetTrafficPolicy().GetTls().GetMode() - - if mode == v1alpha3.ClientTLSSettings_SIMPLE || mode == v1alpha3.ClientTLSSettings_MUTUAL { - if dr.GetTrafficPolicy().GetTls().GetCaCertificates() == "" { - m := msg.NewNoServerCertificateVerificationDestinationLevel(r, drName, - drNs.String(), mode.String(), dr.GetHost()) - - if line, ok := util.ErrorLine(r, fmt.Sprintf(util.DestinationRuleTLSCert)); ok { - m.Line = line - } - ctx.Report(collections.IstioNetworkingV1Alpha3Destinationrules.Name(), m) - } - } - portSettings := dr.TrafficPolicy.GetPortLevelSettings() - - for i, p := range portSettings { - mode = p.GetTls().GetMode() - if mode == v1alpha3.ClientTLSSettings_SIMPLE || mode == v1alpha3.ClientTLSSettings_MUTUAL { - if p.GetTls().GetCaCertificates() == "" { - m := msg.NewNoServerCertificateVerificationPortLevel(r, drName, - drNs.String(), mode.String(), dr.GetHost(), p.GetPort().String()) - - if line, ok := util.ErrorLine(r, fmt.Sprintf(util.DestinationRuleTLSPortLevelCert, i)); ok { - m.Line = line - } - ctx.Report(collections.IstioNetworkingV1Alpha3Destinationrules.Name(), m) - } - } - } -} diff --git a/pkg/config/analysis/analyzers/envoyfilter/envoyfilter.go b/pkg/config/analysis/analyzers/envoyfilter/envoyfilter.go deleted file mode 100644 index 630c7e561..000000000 --- a/pkg/config/analysis/analyzers/envoyfilter/envoyfilter.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package envoyfilter - -import ( - "fmt" -) - -import ( - network "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// EnvoyPatchAnalyzer checks envoyFilters to see if the patch section is okay -type EnvoyPatchAnalyzer struct{} - -// (compile-time check that we implement the interface) -var _ analysis.Analyzer = &EnvoyPatchAnalyzer{} - -// Metadata implements analysis.Analyzer -func (*EnvoyPatchAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "envoyfilter.EnvoyPatchAnalyzer", - Description: "Checks an envoyFilters ", - Inputs: collection.Names{ - collections.IstioNetworkingV1Alpha3Envoyfilters.Name(), - }, - } -} - -// Analyze implements analysis.Analyzer -func (s *EnvoyPatchAnalyzer) Analyze(c analysis.Context) { - c.ForEach(collections.IstioNetworkingV1Alpha3Envoyfilters.Name(), func(r *resource.Instance) bool { - s.analyzeEnvoyFilterPatch(r, c) - return true - }) -} - -func (*EnvoyPatchAnalyzer) analyzeEnvoyFilterPatch(r *resource.Instance, c analysis.Context) { - ef := r.Message.(*network.EnvoyFilter) - - // if the envoyFilter does not have a priority and it uses the INSERT_BEFORE or INSERT_AFTER - // then it can have issues if its using an operation that is relative to another filter. - // the default priority for an envoyFilter is 0 - if ef.Priority == 0 { - for index, patch := range ef.ConfigPatches { - if patch.Patch.Operation == network.EnvoyFilter_Patch_INSERT_BEFORE || patch.Patch.Operation == network.EnvoyFilter_Patch_INSERT_AFTER { - // indicate that this envoy filter does not have a priority and has a relative patch - // operation set which can cause issues. Indicate a warning to the use to use a - // priority to ensure its placed after something or use the INSERT_FIRST option to - // ensure its always done first - message := msg.NewEnvoyFilterUsesRelativeOperation(r) - - if line, ok := util.ErrorLine(r, fmt.Sprintf(util.EnvoyFilterConfigPath, index)); ok { - message.Line = line - } - - c.Report(collections.IstioNetworkingV1Alpha3Envoyfilters.Name(), message) - } - } - } -} diff --git a/pkg/config/analysis/analyzers/gateway/certificate.go b/pkg/config/analysis/analyzers/gateway/certificate.go deleted file mode 100644 index 21c426001..000000000 --- a/pkg/config/analysis/analyzers/gateway/certificate.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -type CertificateAnalyzer struct{} - -var _ analysis.Analyzer = &CertificateAnalyzer{} - -func (*CertificateAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "gateway.CertificateAnalyzer", - Description: "Checks a gateway certificate", - Inputs: collection.Names{ - collections.IstioNetworkingV1Alpha3Gateways.Name(), - }, - } -} - -// Analyze implements analysis.Analyzer -func (gateway *CertificateAnalyzer) Analyze(context analysis.Context) { - context.ForEach(collections.IstioNetworkingV1Alpha3Gateways.Name(), func(resource *resource.Instance) bool { - gateway.analyzeDuplicateCertificate(resource, context, features.ScopeGatewayToNamespace) - return true - }) -} - -func (gateway *CertificateAnalyzer) analyzeDuplicateCertificate(currentResource *resource.Instance, context analysis.Context, scopeGatewayToNamespace bool) { - currentGateway := currentResource.Message.(*v1alpha3.Gateway) - currentGatewayFullName := currentResource.Metadata.FullName - gateways := getGatewaysWithSelector(context, scopeGatewayToNamespace, currentGatewayFullName, currentGateway.Selector) - - for _, gatewayFullName := range gateways { - // ignore matching the same exact gateway - if currentGatewayFullName == gatewayFullName { - continue - } - - gatewayInstance := context.Find(collections.IstioNetworkingV1Alpha3Gateways.Name(), gatewayFullName) - gateway := gatewayInstance.Message.(*v1alpha3.Gateway) - for _, currentServer := range currentGateway.Servers { - for _, server := range gateway.Servers { - // make sure have TLS configuration - if currentServer.Tls == nil || server.Tls == nil { - continue - } - - if haveSameCertificate(currentServer.Tls, server.Tls) { - gatewayNames := []string{currentGatewayFullName.String(), gatewayFullName.String()} - message := msg.NewGatewayDuplicateCertificate(currentResource, gatewayNames) - - if line, ok := util.ErrorLine(currentResource, util.MetadataName); ok { - message.Line = line - } - - context.Report(collections.IstioNetworkingV1Alpha3Gateways.Name(), message) - } - } - } - } -} - -func haveSameCertificate(currentGatewayTLS, gatewayTLS *v1alpha3.ServerTLSSettings) bool { - if currentGatewayTLS.CredentialName != "" && gatewayTLS.CredentialName != "" { - return currentGatewayTLS.CredentialName == gatewayTLS.CredentialName - } - - if currentGatewayTLS.CredentialName == "" && gatewayTLS.CredentialName == "" { - if currentGatewayTLS.ServerCertificate != "" && gatewayTLS.ServerCertificate != "" { - if currentGatewayTLS.ServerCertificate == gatewayTLS.ServerCertificate { - if currentGatewayTLS.PrivateKey != "" && gatewayTLS.PrivateKey != "" { - return currentGatewayTLS.PrivateKey == gatewayTLS.PrivateKey - } - return false - } - } - } - - return false -} - -// get all gateways that is superset of the selector -func getGatewaysWithSelector(c analysis.Context, gwScope bool, currentGWName resource.FullName, currentGWSelector map[string]string) []resource.FullName { - var gateways []resource.FullName - - c.ForEach(collections.IstioNetworkingV1Alpha3Gateways.Name(), func(resource *resource.Instance) bool { - // if scopeToNamespace true, ignore adding gateways from other namespace - if gwScope { - if currentGWName.Namespace != resource.Metadata.FullName.Namespace { - return true - } - } - - // if current gateway selector is empty, match all gateway - if len(currentGWSelector) == 0 { - gateways = append(gateways, resource.Metadata.FullName) - return true - } - - gateway := resource.Message.(*v1alpha3.Gateway) - // if current gateway selector is subset of other gateway selector - // add other gateway - if selectorSubset(currentGWSelector, gateway.Selector) { - gateways = append(gateways, resource.Metadata.FullName) - } - - return true - }) - - return gateways -} - -func selectorSubset(selectorX, selectorY map[string]string) bool { - var count int - - for keyX, valueX := range selectorX { - for keyY, valueY := range selectorY { - if keyX == keyY { - // if have same key but different value - // mean selectorX is not subset of selectorY - if valueX != valueY { - return false - } - // if key and value is same - // increase the counting - count++ - } - } - } - - // if total counting is not same with the length - // of selectorX, selectorX is not subset of selectorY - return count == len(selectorX) -} diff --git a/pkg/config/analysis/analyzers/gateway/conflictinggateway.go b/pkg/config/analysis/analyzers/gateway/conflictinggateway.go deleted file mode 100644 index d39c4b2f5..000000000 --- a/pkg/config/analysis/analyzers/gateway/conflictinggateway.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "fmt" - "strconv" - "strings" -) - -import ( - "istio.io/api/networking/v1alpha3" - k8s_labels "k8s.io/apimachinery/pkg/labels" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// ConflictingGatewayAnalyzer checks a gateway's selector, port number and hosts. -type ConflictingGatewayAnalyzer struct{} - -// (compile-time check that we implement the interface) -var _ analysis.Analyzer = &ConflictingGatewayAnalyzer{} - -// Metadata implements analysis.Analyzer -func (*ConflictingGatewayAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "gateway.ConflictingGatewayAnalyzer", - Description: "Checks a gateway's selector, port number and hosts", - Inputs: collection.Names{ - collections.IstioNetworkingV1Alpha3Gateways.Name(), - }, - } -} - -// Analyze implements analysis.Analyzer -func (s *ConflictingGatewayAnalyzer) Analyze(c analysis.Context) { - gwConflictingMap := initGatewaysMap(c) - c.ForEach(collections.IstioNetworkingV1Alpha3Gateways.Name(), func(r *resource.Instance) bool { - s.analyzeGateway(r, c, gwConflictingMap) - return true - }) -} - -func (*ConflictingGatewayAnalyzer) analyzeGateway(r *resource.Instance, c analysis.Context, - gwCMap map[string]map[string][]string) { - gw := r.Message.(*v1alpha3.Gateway) - gwName := r.Metadata.FullName.String() - // For pods selected by gw.Selector, find Services that select them and remember those ports - gwSelector := k8s_labels.SelectorFromSet(gw.Selector) - sGWSelector := gwSelector.String() - - // Check non-exist gateway with particular selector - isExists := false - for gwmKey := range gwCMap { - if strings.Contains(gwmKey, sGWSelector) { - isExists = true - break - } - } - if sGWSelector != "" && !isExists { - m := msg.NewReferencedResourceNotFound(r, "selector", sGWSelector) - label := util.ExtractLabelFromSelectorString(sGWSelector) - if line, ok := util.ErrorLine(r, fmt.Sprintf(util.GatewaySelector, label)); ok { - m.Line = line - } - c.Report(collections.IstioNetworkingV1Alpha3Gateways.Name(), m) - return - } - - for _, server := range gw.Servers { - var rmsg []string - conflictingGWMatch := 0 - sPortNumber := strconv.Itoa(int(server.Port.Number)) - mapKey := genGatewayMapKey(sGWSelector, sPortNumber) - for gwNameKey, gwHostsValue := range gwCMap[mapKey] { - for _, gwHost := range server.Hosts { - // both selector and portnumber are the same, then check hosts - if isGWsHostMatched(gwHost, gwHostsValue) { - if gwName != gwNameKey { - conflictingGWMatch++ - rmsg = append(rmsg, gwNameKey) - } - } - } - } - if conflictingGWMatch > 0 { - reportMsg := strings.Join(rmsg, ",") - hostsMsg := strings.Join(server.Hosts, ",") - m := msg.NewConflictingGateways(r, reportMsg, sGWSelector, sPortNumber, hostsMsg) - c.Report(collections.IstioNetworkingV1Alpha3Gateways.Name(), m) - } - } -} - -// isGWsHostMatched implements gateway's hosts match -func isGWsHostMatched(gwInstance string, gwHostList []string) bool { - gwInstanceNamed := host.Name(gwInstance) - for _, gwElem := range gwHostList { - gwElemNamed := host.Name(gwElem) - if gwInstanceNamed.Matches(gwElemNamed) { - return true - } - } - return false -} - -// initGatewaysMap implements initilization for gateways Map -func initGatewaysMap(ctx analysis.Context) map[string]map[string][]string { - gwConflictingMap := make(map[string]map[string][]string) - ctx.ForEach(collections.IstioNetworkingV1Alpha3Gateways.Name(), func(r *resource.Instance) bool { - gw := r.Message.(*v1alpha3.Gateway) - gwName := r.Metadata.FullName.String() - - gwSelector := k8s_labels.SelectorFromSet(gw.Selector) - sGWSelector := gwSelector.String() - for _, server := range gw.Servers { - sPortNumber := strconv.Itoa(int(server.Port.Number)) - mapKey := genGatewayMapKey(sGWSelector, sPortNumber) - if _, exits := gwConflictingMap[mapKey]; !exits { - objMap := make(map[string][]string) - objMap[gwName] = server.Hosts - gwConflictingMap[mapKey] = objMap - } else { - gwConflictingMap[mapKey][gwName] = server.Hosts - } - } - return true - }) - return gwConflictingMap -} - -func genGatewayMapKey(selector, portNumber string) string { - key := selector + "~" + portNumber - return key -} diff --git a/pkg/config/analysis/analyzers/gateway/gateway.go b/pkg/config/analysis/analyzers/gateway/gateway.go deleted file mode 100644 index 5d2f14fff..000000000 --- a/pkg/config/analysis/analyzers/gateway/gateway.go +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "fmt" -) - -import ( - "istio.io/api/networking/v1alpha3" - v1 "k8s.io/api/core/v1" - k8s_labels "k8s.io/apimachinery/pkg/labels" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// IngressGatewayPortAnalyzer checks a gateway's ports against the gateway's Kubernetes service ports. -type IngressGatewayPortAnalyzer struct{} - -// (compile-time check that we implement the interface) -var _ analysis.Analyzer = &IngressGatewayPortAnalyzer{} - -// Metadata implements analysis.Analyzer -func (*IngressGatewayPortAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "gateway.IngressGatewayPortAnalyzer", - Description: "Checks a gateway's ports against the gateway's Kubernetes service ports", - Inputs: collection.Names{ - collections.IstioNetworkingV1Alpha3Gateways.Name(), - collections.K8SCoreV1Pods.Name(), - collections.K8SCoreV1Services.Name(), - }, - } -} - -// Analyze implements analysis.Analyzer -func (s *IngressGatewayPortAnalyzer) Analyze(c analysis.Context) { - c.ForEach(collections.IstioNetworkingV1Alpha3Gateways.Name(), func(r *resource.Instance) bool { - s.analyzeGateway(r, c) - return true - }) -} - -func (*IngressGatewayPortAnalyzer) analyzeGateway(r *resource.Instance, c analysis.Context) { - gw := r.Message.(*v1alpha3.Gateway) - - // Typically there will be a single istio-ingressgateway service, which will select - // the same ingress gateway pod workload as the Gateway resource. If there are multiple - // Kubernetes services, and they offer different TCP port combinations, this validator will - // not report a problem if *any* selecting service exposes the Gateway's port. - servicePorts := map[uint32]bool{} - gwSelectorMatches := 0 - - // For pods selected by gw.Selector, find Services that select them and remember those ports - gwSelector := k8s_labels.SelectorFromSet(gw.Selector) - c.ForEach(collections.K8SCoreV1Pods.Name(), func(rPod *resource.Instance) bool { - podLabels := k8s_labels.Set(rPod.Metadata.Labels) - if gwSelector.Matches(podLabels) { - gwSelectorMatches++ - c.ForEach(collections.K8SCoreV1Services.Name(), func(rSvc *resource.Instance) bool { - nsSvc := string(rSvc.Metadata.FullName.Namespace) - if nsSvc != rPod.Metadata.FullName.Namespace.String() { - return true // Services only select pods in their namespace - } - - service := rSvc.Message.(*v1.ServiceSpec) - // TODO I want to match service.Namespace to pod.ObjectMeta.Namespace - svcSelector := k8s_labels.SelectorFromSet(service.Selector) - if svcSelector.Matches(podLabels) { - for _, port := range service.Ports { - if port.Protocol == "TCP" { - servicePorts[uint32(port.Port)] = true - } - } - } - return true - }) - } - return true - }) - - // Report if we found no pods matching this gateway's selector - if gwSelectorMatches == 0 { - m := msg.NewReferencedResourceNotFound(r, "selector", gwSelector.String()) - - label := util.ExtractLabelFromSelectorString(gwSelector.String()) - if line, ok := util.ErrorLine(r, fmt.Sprintf(util.GatewaySelector, label)); ok { - m.Line = line - } - - c.Report(collections.IstioNetworkingV1Alpha3Gateways.Name(), m) - return - } - - // Check each Gateway port against what the workload ingress service offers - for _, server := range gw.Servers { - if server.Port != nil { - _, ok := servicePorts[server.Port.Number] - if !ok { - m := msg.NewGatewayPortNotOnWorkload(r, gwSelector.String(), int(server.Port.Number)) - - label := util.ExtractLabelFromSelectorString(gwSelector.String()) - if line, ok := util.ErrorLine(r, fmt.Sprintf(util.GatewaySelector, label)); ok { - m.Line = line - } - - c.Report(collections.IstioNetworkingV1Alpha3Gateways.Name(), m) - } - } - } -} diff --git a/pkg/config/analysis/analyzers/gateway/secret.go b/pkg/config/analysis/analyzers/gateway/secret.go deleted file mode 100644 index d1b95bffe..000000000 --- a/pkg/config/analysis/analyzers/gateway/secret.go +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "fmt" -) - -import ( - "istio.io/api/networking/v1alpha3" - "k8s.io/apimachinery/pkg/labels" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// SecretAnalyzer checks a gateway's referenced secrets for correctness -type SecretAnalyzer struct{} - -var _ analysis.Analyzer = &SecretAnalyzer{} - -// Metadata implements analysis.Analyzer -func (a *SecretAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "gateway.SecretAnalyzer", - Description: "Checks a gateway's referenced secrets for correctness", - Inputs: collection.Names{ - collections.IstioNetworkingV1Alpha3Gateways.Name(), - collections.K8SCoreV1Pods.Name(), - collections.K8SCoreV1Secrets.Name(), - }, - } -} - -// Analyze implements analysis.Analyzer -func (a *SecretAnalyzer) Analyze(ctx analysis.Context) { - ctx.ForEach(collections.IstioNetworkingV1Alpha3Gateways.Name(), func(r *resource.Instance) bool { - gw := r.Message.(*v1alpha3.Gateway) - - gwNs := getGatewayNamespace(ctx, gw) - - // If we can't find a namespace for the gateway, it's because there's no matching selector. Exit early with a different message. - if gwNs == "" { - - gwSelector := labels.SelectorFromSet(gw.Selector) - m := msg.NewReferencedResourceNotFound(r, "selector", labels.SelectorFromSet(gw.Selector).String()) - - label := util.ExtractLabelFromSelectorString(gwSelector.String()) - if line, ok := util.ErrorLine(r, fmt.Sprintf(util.GatewaySelector, label)); ok { - m.Line = line - } - - ctx.Report(collections.IstioNetworkingV1Alpha3Gateways.Name(), m) - return true - } - - for i, srv := range gw.GetServers() { - tls := srv.GetTls() - if tls == nil { - continue - } - - cn := tls.GetCredentialName() - if cn == "" { - continue - } - - if !ctx.Exists(collections.K8SCoreV1Secrets.Name(), resource.NewShortOrFullName(gwNs, cn)) { - m := msg.NewReferencedResourceNotFound(r, "credentialName", cn) - - if line, ok := util.ErrorLine(r, fmt.Sprintf(util.CredentialName, i)); ok { - m.Line = line - } - - ctx.Report(collections.IstioNetworkingV1Alpha3Gateways.Name(), m) - } - } - return true - }) -} - -// Gets the namespace for the gateway (in terms of the actual workload selected by the gateway, NOT the namespace of the Gateway CRD) -// Assumes that all selected workloads are in the same namespace, if this is not the case which one's namespace gets returned is undefined. -func getGatewayNamespace(ctx analysis.Context, gw *v1alpha3.Gateway) resource.Namespace { - var ns resource.Namespace - - gwSelector := labels.SelectorFromSet(gw.Selector) - ctx.ForEach(collections.K8SCoreV1Pods.Name(), func(rPod *resource.Instance) bool { - if gwSelector.Matches(labels.Set(rPod.Metadata.Labels)) { - ns = rPod.Metadata.FullName.Namespace - return false - } - return true - }) - - return ns -} diff --git a/pkg/config/analysis/analyzers/injection/image-auto.go b/pkg/config/analysis/analyzers/injection/image-auto.go deleted file mode 100644 index 81f07a6ed..000000000 --- a/pkg/config/analysis/analyzers/injection/image-auto.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package injection - -import ( - "strings" -) - -import ( - admit_v1 "k8s.io/api/admissionregistration/v1" - apps_v1 "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - klabels "k8s.io/apimachinery/pkg/labels" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// ImageAutoAnalyzer reports an error if Pods and Deployments with `image: auto` are not going to be injected. -type ImageAutoAnalyzer struct{} - -var _ analysis.Analyzer = &ImageAutoAnalyzer{} - -const ( - istioProxyContainerName = "istio-proxy" - manualInjectionImage = "auto" -) - -// Metadata implements Analyzer. -func (a *ImageAutoAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "injection.ImageAutoAnalyzer", - Description: "Makes sure that Pods and Deployments with `image: auto` are going to be injected", - Inputs: collection.Names{ - collections.K8SCoreV1Namespaces.Name(), - collections.K8SCoreV1Pods.Name(), - collections.K8SAppsV1Deployments.Name(), - collections.K8SAdmissionregistrationK8SIoV1Mutatingwebhookconfigurations.Name(), - }, - } -} - -// Analyze implements Analyzer. -func (a *ImageAutoAnalyzer) Analyze(c analysis.Context) { - var istioWebhooks []admit_v1.MutatingWebhook - c.ForEach(collections.K8SAdmissionregistrationK8SIoV1Mutatingwebhookconfigurations.Name(), func(resource *resource.Instance) bool { - mwhc := resource.Message.(*admit_v1.MutatingWebhookConfiguration) - for _, wh := range mwhc.Webhooks { - if strings.HasSuffix(wh.Name, "istio.io") { - istioWebhooks = append(istioWebhooks, wh) - } - } - return true - }) - c.ForEach(collections.K8SCoreV1Pods.Name(), func(resource *resource.Instance) bool { - p := resource.Message.(*v1.PodSpec) - // If a pod has `image: auto` it is broken whether the webhooks match or not - if !hasAutoImage(p) { - return true - } - m := msg.NewImageAutoWithoutInjectionError(resource, "Pod", resource.Metadata.FullName.Name.String()) - c.Report(collections.K8SCoreV1Pods.Name(), m) - return true - }) - c.ForEach(collections.K8SAppsV1Deployments.Name(), func(resource *resource.Instance) bool { - d := resource.Message.(*apps_v1.DeploymentSpec) - if !hasAutoImage(&d.Template.Spec) { - return true - } - nsLabels := getNamespaceLabels(c, resource.Metadata.FullName.Namespace.String()) - if !matchesWebhooks(nsLabels, d.Template.Labels, istioWebhooks) { - m := msg.NewImageAutoWithoutInjectionWarning(resource, "Deployment", resource.Metadata.FullName.Name.String()) - c.Report(collections.K8SAppsV1Deployments.Name(), m) - } - return true - }) -} - -func hasAutoImage(spec *v1.PodSpec) bool { - for _, c := range spec.Containers { - if c.Name == istioProxyContainerName && c.Image == manualInjectionImage { - return true - } - } - return false -} - -func getNamespaceLabels(c analysis.Context, nsName string) map[string]string { - if nsName == "" { - nsName = "default" - } - ns := c.Find(collections.K8SCoreV1Namespaces.Name(), resource.NewFullName("", resource.LocalName(nsName))) - if ns == nil { - return nil - } - return ns.Metadata.Labels -} - -func matchesWebhooks(nsLabels, podLabels map[string]string, istioWebhooks []admit_v1.MutatingWebhook) bool { - for _, w := range istioWebhooks { - if selectorMatches(w.NamespaceSelector, nsLabels) && selectorMatches(w.ObjectSelector, podLabels) { - return true - } - } - return false -} - -func selectorMatches(selector *metav1.LabelSelector, labels klabels.Set) bool { - // From webhook spec: "Default to the empty LabelSelector, which matchesWebhooks everything." - if selector == nil { - return true - } - s, err := metav1.LabelSelectorAsSelector(selector) - if err != nil { - return false - } - return s.Matches(labels) -} diff --git a/pkg/config/analysis/analyzers/injection/injection-image.go b/pkg/config/analysis/analyzers/injection/injection-image.go deleted file mode 100644 index b96a227e0..000000000 --- a/pkg/config/analysis/analyzers/injection/injection-image.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package injection - -import ( - "encoding/json" - "fmt" - "regexp" - "strings" -) - -import ( - v1 "k8s.io/api/core/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// ImageAnalyzer checks the image of auto-injection configured with the running proxies on pods. -type ImageAnalyzer struct{} - -var _ analysis.Analyzer = &ImageAnalyzer{} - -// injectionConfigMap is a snippet of the sidecar injection ConfigMap -type injectionConfigMap struct { - Global global `json:"global"` -} - -type global struct { - Hub string `json:"hub"` - Tag string `json:"tag"` - Proxy proxy `json:"proxy"` -} - -type proxy struct { - Image string `json:"image"` -} - -// Metadata implements Analyzer. -func (a *ImageAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "injection.ImageAnalyzer", - Description: "Checks the image of auto-injection configured with the running proxies on pods", - Inputs: collection.Names{ - collections.K8SCoreV1Namespaces.Name(), - collections.K8SCoreV1Pods.Name(), - collections.K8SCoreV1Configmaps.Name(), - }, - } -} - -// Analyze implements Analyzer. -func (a *ImageAnalyzer) Analyze(c analysis.Context) { - proxyImageMap := make(map[string]string) - - // when multiple injector configmaps exist, we may need to assess them respectively. - c.ForEach(collections.K8SCoreV1Configmaps.Name(), func(r *resource.Instance) bool { - cmName := r.Metadata.FullName.Name.String() - match, _ := regexp.MatchString(`^istio-sidecar-injector`, cmName) - if match { - cm := r.Message.(*v1.ConfigMap) - proxyImageMap[cmName] = GetIstioProxyImage(cm) - - return true - } - return true - }) - - if len(proxyImageMap) == 0 { - return - } - - injectedNamespaces := make(map[string]string) - - // Collect the list of namespaces that have istio injection enabled. - c.ForEach(collections.K8SCoreV1Namespaces.Name(), func(r *resource.Instance) bool { - nsRevision, okNewInjectionLabel := r.Metadata.Labels[RevisionInjectionLabelName] - if r.Metadata.Labels[util.InjectionLabelName] == util.InjectionLabelEnableValue || okNewInjectionLabel { - if okNewInjectionLabel { - injectedNamespaces[r.Metadata.FullName.String()] = nsRevision - } else { - injectedNamespaces[r.Metadata.FullName.String()] = "default" - } - } else { - return true - } - - return true - }) - - c.ForEach(collections.K8SCoreV1Pods.Name(), func(r *resource.Instance) bool { - var injectionCMName string - pod := r.Message.(*v1.PodSpec) - - if nsRevision, ok := injectedNamespaces[r.Metadata.FullName.Namespace.String()]; ok { - // Generate the injection configmap name with different revision for every pod - injectionCMName = util.GetInjectorConfigMapName(nsRevision) - } else { - return true - } - - // If the pod has been annotated with a custom sidecar, then ignore as - // it always overrides the injector logic. - if r.Metadata.Annotations["sidecar.istio.io/proxyImage"] != "" { - return true - } - - for i, container := range pod.Containers { - if container.Name != util.IstioProxyName { - continue - } - - proxyImage, okImage := proxyImageMap[injectionCMName] - if !okImage { - return true - } - if container.Image != proxyImage { - m := msg.NewIstioProxyImageMismatch(r, container.Image, proxyImage) - - if line, ok := util.ErrorLine(r, fmt.Sprintf(util.ImageInContainer, i)); ok { - m.Line = line - } - - c.Report(collections.K8SCoreV1Pods.Name(), m) - } - } - - return true - }) -} - -// GetIstioProxyImage retrieves the proxy image name defined in the sidecar injector -// configuration. -func GetIstioProxyImage(cm *v1.ConfigMap) string { - var m injectionConfigMap - if err := json.Unmarshal([]byte(cm.Data[util.InjectionConfigMapValue]), &m); err != nil { - return "" - } - // The injector template has a similar '{ contains "/" ... }' conditional - if strings.Contains(m.Global.Proxy.Image, "/") { - return m.Global.Proxy.Image - } - return fmt.Sprintf("%s/%s:%s", m.Global.Hub, m.Global.Proxy.Image, m.Global.Tag) -} diff --git a/pkg/config/analysis/analyzers/injection/injection.go b/pkg/config/analysis/analyzers/injection/injection.go deleted file mode 100644 index 7c18ca7aa..000000000 --- a/pkg/config/analysis/analyzers/injection/injection.go +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package injection - -import ( - "encoding/json" - "fmt" - "strings" -) - -import ( - "istio.io/api/annotation" - "istio.io/api/label" - v1 "k8s.io/api/core/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// Analyzer checks conditions related to Istio sidecar injection. -type Analyzer struct{} - -var _ analysis.Analyzer = &Analyzer{} - -// We assume that enablement is via an istio-injection=enabled or istio.io/rev namespace label -// In theory, there can be alternatives using Mutatingwebhookconfiguration, but they're very uncommon -// See https://istio.io/docs/ops/troubleshooting/injection/ for more info. -var ( - RevisionInjectionLabelName = label.IoIstioRev.Name -) - -// Metadata implements Analyzer -func (a *Analyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "injection.Analyzer", - Description: "Checks conditions related to Istio sidecar injection", - Inputs: collection.Names{ - collections.K8SCoreV1Namespaces.Name(), - collections.K8SCoreV1Pods.Name(), - collections.K8SCoreV1Configmaps.Name(), - }, - } -} - -// Analyze implements Analyzer -func (a *Analyzer) Analyze(c analysis.Context) { - enableNamespacesByDefault := false - injectedNamespaces := make(map[string]bool) - - c.ForEach(collections.K8SCoreV1Namespaces.Name(), func(r *resource.Instance) bool { - if r.Metadata.FullName.String() == constants.IstioSystemNamespace { - return true - } - - ns := r.Metadata.FullName.String() - if util.IsSystemNamespace(resource.Namespace(ns)) { - return true - } - - injectionLabel := r.Metadata.Labels[util.InjectionLabelName] - nsRevision, okNewInjectionLabel := r.Metadata.Labels[RevisionInjectionLabelName] - - // verify the enableNamespacesByDefault flag in injection configmaps - c.ForEach(collections.K8SCoreV1Configmaps.Name(), func(r *resource.Instance) bool { - injectionCMName := util.GetInjectorConfigMapName(nsRevision) - if r.Metadata.FullName.Name.String() == injectionCMName { - cm := r.Message.(*v1.ConfigMap) - enableNamespacesByDefault = GetEnableNamespacesByDefaultFromInjectedConfigMap(cm) - return false - } - return true - }) - - if injectionLabel == "" && !okNewInjectionLabel { - // if Istio is installed with sidecarInjectorWebhook.enableNamespacesByDefault=true - // (in the istio-sidecar-injector configmap), we need to reverse this logic and treat this as an injected namespace - if enableNamespacesByDefault { - m := msg.NewNamespaceInjectionEnabledByDefault(r) - c.Report(collections.K8SCoreV1Namespaces.Name(), m) - return true - } - - m := msg.NewNamespaceNotInjected(r, ns, ns) - - if line, ok := util.ErrorLine(r, fmt.Sprintf(util.MetadataName)); ok { - m.Line = line - } - - c.Report(collections.K8SCoreV1Namespaces.Name(), m) - return true - } - - if okNewInjectionLabel { - if injectionLabel != "" { - - m := msg.NewNamespaceMultipleInjectionLabels(r, ns, ns) - - if line, ok := util.ErrorLine(r, fmt.Sprintf(util.MetadataName)); ok { - m.Line = line - } - - c.Report(collections.K8SCoreV1Namespaces.Name(), m) - return true - } - } else if injectionLabel != util.InjectionLabelEnableValue { - // If legacy label has any value other than the enablement value, they are deliberately not injecting it, so ignore - return true - } - - injectedNamespaces[ns] = true - - return true - }) - - c.ForEach(collections.K8SCoreV1Pods.Name(), func(r *resource.Instance) bool { - pod := r.Message.(*v1.PodSpec) - - if !injectedNamespaces[r.Metadata.FullName.Namespace.String()] { - return true - } - - // If a pod has injection explicitly disabled, no need to check further - if val := r.Metadata.Annotations[annotation.SidecarInject.Name]; strings.EqualFold(val, "false") { - return true - } - - proxyImage := "" - for _, container := range pod.Containers { - if container.Name == util.IstioProxyName { - proxyImage = container.Image - break - } - } - - if proxyImage == "" { - c.Report(collections.K8SCoreV1Pods.Name(), msg.NewPodMissingProxy(r, r.Metadata.FullName.String())) - } - - return true - }) -} - -// GetInjectedConfigMapValuesStruct retrieves value of sidecarInjectorWebhook.enableNamespacesByDefault -// defined in the sidecar injector configuration. -func GetEnableNamespacesByDefaultFromInjectedConfigMap(cm *v1.ConfigMap) bool { - var injectedCMValues map[string]interface{} - if err := json.Unmarshal([]byte(cm.Data[util.InjectionConfigMapValue]), &injectedCMValues); err != nil { - return false - } - - injectionEnable := injectedCMValues[util.InjectorWebhookConfigKey].(map[string]interface{})[util.InjectorWebhookConfigValue] - return injectionEnable.(bool) -} diff --git a/pkg/config/analysis/analyzers/maturity/maturity.go b/pkg/config/analysis/analyzers/maturity/maturity.go deleted file mode 100644 index 99dc0bcf0..000000000 --- a/pkg/config/analysis/analyzers/maturity/maturity.go +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package maturity - -import ( - "strings" -) - -import ( - "istio.io/api/annotation" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// AlphaAnalyzer checks for alpha Istio annotations in K8s resources -type AlphaAnalyzer struct{} - -// the alpha analyzer is currently explicitly left out of the default collection of analyzers to run, as it results -// in too much noise for users, with annotations that are set by default. Once the noise dies down, this should be -// added to the CombinedAnalyzers() function. - -var istioAnnotations = annotation.AllResourceAnnotations() - -// Metadata implements analyzer.Analyzer -func (*AlphaAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "annotations.AlphaAnalyzer", - Description: "Checks for alpha Istio annotations in Kubernetes resources", - Inputs: collection.Names{ - collections.K8SCoreV1Namespaces.Name(), - collections.K8SCoreV1Services.Name(), - collections.K8SCoreV1Pods.Name(), - collections.K8SAppsV1Deployments.Name(), - }, - } -} - -// Analyze implements analysis.Analyzer -func (fa *AlphaAnalyzer) Analyze(ctx analysis.Context) { - ctx.ForEach(collections.K8SCoreV1Namespaces.Name(), func(r *resource.Instance) bool { - fa.allowAnnotations(r, ctx, collections.K8SCoreV1Namespaces.Name()) - return true - }) - ctx.ForEach(collections.K8SCoreV1Services.Name(), func(r *resource.Instance) bool { - fa.allowAnnotations(r, ctx, collections.K8SCoreV1Services.Name()) - return true - }) - ctx.ForEach(collections.K8SCoreV1Pods.Name(), func(r *resource.Instance) bool { - fa.allowAnnotations(r, ctx, collections.K8SCoreV1Pods.Name()) - return true - }) - ctx.ForEach(collections.K8SAppsV1Deployments.Name(), func(r *resource.Instance) bool { - fa.allowAnnotations(r, ctx, collections.K8SAppsV1Deployments.Name()) - return true - }) -} - -func (*AlphaAnalyzer) allowAnnotations(r *resource.Instance, ctx analysis.Context, collectionType collection.Name) { - if len(r.Metadata.Annotations) == 0 { - return - } - - // It is fine if the annotation is kubectl.kubernetes.io/last-applied-configuration. - for ann := range r.Metadata.Annotations { - if !istioAnnotation(ann) { - continue - } - - if annotationDef := lookupAnnotation(ann); annotationDef != nil { - if annotationDef.FeatureStatus == annotation.Alpha { - // this annotation is set by default in istiod, don't alert on it. - if annotationDef.Name == annotation.SidecarStatus.Name { - continue - } - m := msg.NewAlphaAnnotation(r, ann) - util.AddLineNumber(r, ann, m) - - ctx.Report(collectionType, m) - } - } - } -} - -// istioAnnotation is true if the annotation is in Istio's namespace -func istioAnnotation(ann string) bool { - // We document this Kubernetes annotation, we should analyze it as well - if ann == "kubernetes.io/ingress.class" { - return true - } - - parts := strings.Split(ann, "/") - if len(parts) == 0 { - return false - } - - if !strings.HasSuffix(parts[0], "istio.io") { - return false - } - - return true -} - -func lookupAnnotation(ann string) *annotation.Instance { - for _, candidate := range istioAnnotations { - if candidate.Name == ann { - return candidate - } - } - - return nil -} diff --git a/pkg/config/analysis/analyzers/multicluster/meshnetworks.go b/pkg/config/analysis/analyzers/multicluster/meshnetworks.go deleted file mode 100644 index 978f6b006..000000000 --- a/pkg/config/analysis/analyzers/multicluster/meshnetworks.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package multicluster - -import ( - "fmt" -) - -import ( - "istio.io/api/mesh/v1alpha1" - v1 "k8s.io/api/core/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/kube/multicluster" -) - -// MeshNetworksAnalyzer validates MeshNetworks configuration in multi-cluster. -type MeshNetworksAnalyzer struct{} - -var _ analysis.Analyzer = &MeshNetworksAnalyzer{} - -// Service Registries that are known to istio. -var serviceRegistries = []provider.ID{ - provider.Mock, - provider.Kubernetes, - provider.External, -} - -// Metadata implements Analyzer -func (s *MeshNetworksAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "meshnetworks.MeshNetworksAnalyzer", - Description: "Check the validity of MeshNetworks in the cluster", - Inputs: collection.Names{ - collections.IstioMeshV1Alpha1MeshNetworks.Name(), - collections.K8SCoreV1Secrets.Name(), - }, - } -} - -// Analyze implements Analyzer -func (s *MeshNetworksAnalyzer) Analyze(c analysis.Context) { - c.ForEach(collections.K8SCoreV1Secrets.Name(), func(r *resource.Instance) bool { - if r.Metadata.Labels[multicluster.MultiClusterSecretLabel] == "true" { - s := r.Message.(*v1.Secret) - for c := range s.Data { - serviceRegistries = append(serviceRegistries, provider.ID(c)) - } - } - return true - }) - - // only one meshnetworks config should exist. - c.ForEach(collections.IstioMeshV1Alpha1MeshNetworks.Name(), func(r *resource.Instance) bool { - mn := r.Message.(*v1alpha1.MeshNetworks) - for i, n := range mn.Networks { - for j, e := range n.Endpoints { - switch re := e.Ne.(type) { - case *v1alpha1.Network_NetworkEndpoints_FromRegistry: - found := false - for _, s := range serviceRegistries { - if provider.ID(re.FromRegistry) == s { - found = true - } - } - if !found { - m := msg.NewUnknownMeshNetworksServiceRegistry(r, re.FromRegistry, i) - - if line, ok := util.ErrorLine(r, fmt.Sprintf(util.FromRegistry, i, j)); ok { - m.Line = line - } - - c.Report(collections.IstioMeshV1Alpha1MeshNetworks.Name(), m) - } - } - } - } - return true - }) -} diff --git a/pkg/config/analysis/analyzers/schema/validation.go b/pkg/config/analysis/analyzers/schema/validation.go deleted file mode 100644 index 9a2d846b5..000000000 --- a/pkg/config/analysis/analyzers/schema/validation.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package schema - -import ( - "fmt" -) - -import ( - "github.com/hashicorp/go-multierror" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/diag" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/validation" -) - -// ValidationAnalyzer runs schema validation as an analyzer and reports any violations as messages -type ValidationAnalyzer struct { - s collection.Schema -} - -var _ analysis.Analyzer = &ValidationAnalyzer{} - -func CollectionValidationAnalyzer(s collection.Schema) analysis.Analyzer { - return &ValidationAnalyzer{s: s} -} - -// AllValidationAnalyzers returns a slice with a validation analyzer for each Istio schema -// This automation comes with an assumption: that the collection names used by the schema match the metadata used by Galley components -func AllValidationAnalyzers() []analysis.Analyzer { - result := make([]analysis.Analyzer, 0) - collections.Istio.ForEach(func(s collection.Schema) (done bool) { - result = append(result, &ValidationAnalyzer{s: s}) - return - }) - return result -} - -// Metadata implements Analyzer -func (a *ValidationAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: fmt.Sprintf("schema.ValidationAnalyzer.%s", a.s.Resource().Kind()), - Description: fmt.Sprintf("Runs schema validation as an analyzer on '%s' resources", a.s.Resource().Kind()), - Inputs: collection.Names{a.s.Name()}, - } -} - -// Analyze implements Analyzer -func (a *ValidationAnalyzer) Analyze(ctx analysis.Context) { - c := a.s.Name() - - ctx.ForEach(c, func(r *resource.Instance) bool { - ns := r.Metadata.FullName.Namespace - name := r.Metadata.FullName.Name - - warnings, err := a.s.Resource().ValidateConfig(config.Config{ - Meta: config.Meta{ - Name: string(name), - Namespace: string(ns), - }, - Spec: r.Message, - }) - if err != nil { - if multiErr, ok := err.(*multierror.Error); ok { - for _, err := range multiErr.WrappedErrors() { - ctx.Report(c, morePreciseMessage(r, err, true)) - } - } else { - ctx.Report(c, morePreciseMessage(r, err, true)) - } - } - if warnings != nil { - if multiErr, ok := warnings.(*multierror.Error); ok { - for _, err := range multiErr.WrappedErrors() { - ctx.Report(c, morePreciseMessage(r, err, false)) - } - } else { - ctx.Report(c, morePreciseMessage(r, warnings, false)) - } - } - - return true - }) -} - -func morePreciseMessage(r *resource.Instance, err error, isError bool) diag.Message { - if aae, ok := err.(*validation.AnalysisAwareError); ok { - switch aae.Type { - case "VirtualServiceUnreachableRule": - return msg.NewVirtualServiceUnreachableRule(r, aae.Parameters[0].(string), aae.Parameters[1].(string)) - case "VirtualServiceIneffectiveMatch": - return msg.NewVirtualServiceIneffectiveMatch(r, aae.Parameters[0].(string), aae.Parameters[1].(string), aae.Parameters[2].(string)) - } - } - if !isError { - return msg.NewSchemaWarning(r, err) - } - return msg.NewSchemaValidationError(r, err) -} diff --git a/pkg/config/analysis/analyzers/schema/validation_test.go b/pkg/config/analysis/analyzers/schema/validation_test.go deleted file mode 100644 index 4ace5a90b..000000000 --- a/pkg/config/analysis/analyzers/schema/validation_test.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package schema - -import ( - "fmt" - "testing" -) - -import ( - "github.com/hashicorp/go-multierror" - . "github.com/onsi/gomega" - "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/testing/fixtures" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - resource2 "github.com/apache/dubbo-go-pixiu/pkg/config/schema/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/validation" -) - -func TestCorrectArgs(t *testing.T) { - g := NewWithT(t) - - m1 := &v1alpha3.VirtualService{} - - testSchema := schemaWithValidateFn(func(cfg config.Config) (warnings validation.Warning, errs error) { - g.Expect(cfg.Name).To(Equal("name")) - g.Expect(cfg.Namespace).To(Equal("ns")) - g.Expect(cfg.Spec).To(Equal(m1)) - return nil, nil - }) - ctx := &fixtures.Context{ - Resources: []*resource.Instance{ - { - Message: &v1alpha3.VirtualService{}, - Metadata: resource.Metadata{ - FullName: resource.NewFullName("ns", "name"), - }, - Origin: fakeOrigin{}, - }, - }, - } - a := ValidationAnalyzer{s: testSchema} - a.Analyze(ctx) -} - -func TestSchemaValidationWrapper(t *testing.T) { - testCol := collections.IstioNetworkingV1Alpha3Virtualservices.Name() - - m1 := &v1alpha3.VirtualService{} - m2 := &v1alpha3.VirtualService{} - m3 := &v1alpha3.VirtualService{} - - testSchema := schemaWithValidateFn(func(cfg config.Config) (warnings validation.Warning, errs error) { - if cfg.Spec == m1 { - return nil, nil - } - if cfg.Spec == m2 { - return nil, fmt.Errorf("") - } - if cfg.Spec == m3 { - return nil, multierror.Append(fmt.Errorf(""), fmt.Errorf("")) - } - return nil, nil - }) - - a := ValidationAnalyzer{s: testSchema} - - t.Run("CheckMetadataInputs", func(t *testing.T) { - g := NewWithT(t) - g.Expect(a.Metadata().Inputs).To(ConsistOf(testCol)) - }) - - t.Run("NoErrors", func(t *testing.T) { - g := NewWithT(t) - ctx := &fixtures.Context{ - Resources: []*resource.Instance{ - { - Message: m1, - }, - }, - } - a.Analyze(ctx) - g.Expect(ctx.Reports).To(BeEmpty()) - }) - - t.Run("SingleError", func(t *testing.T) { - g := NewWithT(t) - - ctx := &fixtures.Context{ - Resources: []*resource.Instance{ - { - Message: m2, - Origin: fakeOrigin{}, - }, - }, - } - a.Analyze(ctx) - g.Expect(ctx.Reports).To(HaveLen(1)) - g.Expect(ctx.Reports[0].Type).To(Equal(msg.SchemaValidationError)) - }) - - t.Run("MultiError", func(t *testing.T) { - g := NewWithT(t) - ctx := &fixtures.Context{ - Resources: []*resource.Instance{ - { - Message: m3, - Origin: fakeOrigin{}, - }, - }, - } - a.Analyze(ctx) - g.Expect(ctx.Reports).To(HaveLen(2)) - g.Expect(ctx.Reports[0].Type).To(Equal(msg.SchemaValidationError)) - g.Expect(ctx.Reports[1].Type).To(Equal(msg.SchemaValidationError)) - }) -} - -func schemaWithValidateFn(validateFn func(cfg config.Config) (validation.Warning, error)) collection.Schema { - original := collections.IstioNetworkingV1Alpha3Virtualservices - return collection.Builder{ - Name: original.Name().String(), - Resource: resource2.Builder{ - ClusterScoped: original.Resource().IsClusterScoped(), - Kind: original.Resource().Kind(), - Plural: original.Resource().Plural(), - Group: original.Resource().Group(), - Version: original.Resource().Version(), - Proto: original.Resource().Proto(), - ProtoPackage: original.Resource().ProtoPackage(), - ValidateProto: validateFn, - }.MustBuild(), - }.MustBuild() -} - -type fakeOrigin struct{} - -func (fakeOrigin) FriendlyName() string { return "myFriendlyName" } -func (fakeOrigin) Comparator() string { return "myFriendlyName" } -func (fakeOrigin) Namespace() resource.Namespace { return "myNamespace" } -func (fakeOrigin) Reference() resource.Reference { return fakeReference{} } -func (fakeOrigin) FieldMap() map[string]int { return make(map[string]int) } - -type fakeReference struct{} - -func (fakeReference) String() string { return "" } diff --git a/pkg/config/analysis/analyzers/service/portname.go b/pkg/config/analysis/analyzers/service/portname.go deleted file mode 100644 index e25275ae5..000000000 --- a/pkg/config/analysis/analyzers/service/portname.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package service - -import ( - "fmt" -) - -import ( - v1 "k8s.io/api/core/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - configKube "github.com/apache/dubbo-go-pixiu/pkg/config/kube" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// PortNameAnalyzer checks the port name of the service -type PortNameAnalyzer struct{} - -var _ analysis.Analyzer = &PortNameAnalyzer{} - -// Metadata implements Analyzer -func (s *PortNameAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "service.PortNameAnalyzer", - Description: "Checks the port names associated with each service", - Inputs: collection.Names{ - collections.K8SCoreV1Services.Name(), - }, - } -} - -// Analyze implements Analyzer -func (s *PortNameAnalyzer) Analyze(c analysis.Context) { - c.ForEach(collections.K8SCoreV1Services.Name(), func(r *resource.Instance) bool { - svcNs := r.Metadata.FullName.Namespace - - // Skip system namespaces entirely - if util.IsSystemNamespace(svcNs) { - return true - } - - // Skip port name check for istio control plane - if util.IsIstioControlPlane(r) { - return true - } - - s.analyzeService(r, c) - return true - }) -} - -func (s *PortNameAnalyzer) analyzeService(r *resource.Instance, c analysis.Context) { - svc := r.Message.(*v1.ServiceSpec) - for i, port := range svc.Ports { - instance := configKube.ConvertProtocol(port.Port, port.Name, port.Protocol, port.AppProtocol) - if instance.IsUnsupported() || port.Name == "tcp" && svc.Type == "ExternalName" { - - m := msg.NewPortNameIsNotUnderNamingConvention( - r, port.Name, int(port.Port), port.TargetPort.String()) - - if svc.Type == "ExternalName" { - m = msg.NewExternalNameServiceTypeInvalidPortName(r) - } - - if line, ok := util.ErrorLine(r, fmt.Sprintf(util.PortInPorts, i)); ok { - m.Line = line - } - c.Report(collections.K8SCoreV1Services.Name(), m) - } - } -} diff --git a/pkg/config/analysis/analyzers/serviceentry/protocoladresses.go b/pkg/config/analysis/analyzers/serviceentry/protocoladresses.go deleted file mode 100644 index 4f0fc1bff..000000000 --- a/pkg/config/analysis/analyzers/serviceentry/protocoladresses.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package serviceentry - -import ( - "fmt" -) - -import ( - "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -type ProtocolAdressesAnalyzer struct{} - -var _ analysis.Analyzer = &ProtocolAdressesAnalyzer{} - -func (serviceEntry *ProtocolAdressesAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "serviceentry.Analyzer", - Description: "Checks the validity of ServiceEntry", - Inputs: collection.Names{ - collections.IstioNetworkingV1Alpha3Serviceentries.Name(), - }, - } -} - -func (serviceEntry *ProtocolAdressesAnalyzer) Analyze(context analysis.Context) { - context.ForEach(collections.IstioNetworkingV1Alpha3Serviceentries.Name(), func(resource *resource.Instance) bool { - serviceEntry.analyzeProtocolAddresses(resource, context) - return true - }) -} - -func (serviceEntry *ProtocolAdressesAnalyzer) analyzeProtocolAddresses(resource *resource.Instance, context analysis.Context) { - se := resource.Message.(*v1alpha3.ServiceEntry) - - if se.Addresses == nil { - for index, port := range se.Ports { - if port.Protocol == "" || port.Protocol == "TCP" { - message := msg.NewServiceEntryAddressesRequired(resource) - - if line, ok := util.ErrorLine(resource, fmt.Sprintf(util.ServiceEntryPort, index)); ok { - message.Line = line - } - - context.Report(collections.IstioNetworkingV1Alpha3Serviceentries.Name(), message) - } - } - } -} diff --git a/pkg/config/analysis/analyzers/sidecar/defaultselector.go b/pkg/config/analysis/analyzers/sidecar/defaultselector.go deleted file mode 100644 index f24d3aaaa..000000000 --- a/pkg/config/analysis/analyzers/sidecar/defaultselector.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package sidecar - -import ( - "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// DefaultSelectorAnalyzer validates, per namespace, that there aren't multiple -// sidecar resources that have no selector. This is distinct from -// SelectorAnalyzer because it does not require pods, so it can run even if that -// collection is unavailable. -type DefaultSelectorAnalyzer struct{} - -var _ analysis.Analyzer = &DefaultSelectorAnalyzer{} - -// Metadata implements Analyzer -func (a *DefaultSelectorAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "sidecar.DefaultSelectorAnalyzer", - Description: "Validates that there aren't multiple sidecar resources that have no selector", - Inputs: collection.Names{ - collections.IstioNetworkingV1Alpha3Sidecars.Name(), - }, - } -} - -// Analyze implements Analyzer -func (a *DefaultSelectorAnalyzer) Analyze(c analysis.Context) { - nsToSidecars := make(map[resource.Namespace][]*resource.Instance) - - c.ForEach(collections.IstioNetworkingV1Alpha3Sidecars.Name(), func(r *resource.Instance) bool { - s := r.Message.(*v1alpha3.Sidecar) - - ns := r.Metadata.FullName.Namespace - - if s.WorkloadSelector == nil { - nsToSidecars[ns] = append(nsToSidecars[ns], r) - } - return true - }) - - // Check for more than one selector-less sidecar instance, per namespace - for ns, sList := range nsToSidecars { - if len(sList) > 1 { - sNames := getNames(sList) - for _, r := range sList { - c.Report(collections.IstioNetworkingV1Alpha3Sidecars.Name(), msg.NewMultipleSidecarsWithoutWorkloadSelectors(r, sNames, string(ns))) - } - } - } -} diff --git a/pkg/config/analysis/analyzers/sidecar/selector.go b/pkg/config/analysis/analyzers/sidecar/selector.go deleted file mode 100644 index 0c077b114..000000000 --- a/pkg/config/analysis/analyzers/sidecar/selector.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package sidecar - -import ( - "fmt" -) - -import ( - "istio.io/api/networking/v1alpha3" - "k8s.io/apimachinery/pkg/labels" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// SelectorAnalyzer validates, per namespace, that: -// * sidecar resources that define a workload selector match at least one pod -// * there aren't multiple sidecar resources that select overlapping pods -type SelectorAnalyzer struct{} - -var _ analysis.Analyzer = &SelectorAnalyzer{} - -// Metadata implements Analyzer -func (a *SelectorAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "sidecar.SelectorAnalyzer", - Description: "Validates that sidecars that define a workload selector " + - "match at least one pod, and that there aren't multiple sidecar resources that select overlapping pods", - Inputs: collection.Names{ - collections.IstioNetworkingV1Alpha3Sidecars.Name(), - collections.K8SCoreV1Pods.Name(), - }, - } -} - -// Analyze implements Analyzer -func (a *SelectorAnalyzer) Analyze(c analysis.Context) { - podsToSidecars := make(map[resource.FullName][]*resource.Instance) - - // This is using an unindexed approach for matching selectors. - // Using an index for selectoes is problematic because selector != label - // We can match a label to a selector, but we can't generate a selector from a label. - c.ForEach(collections.IstioNetworkingV1Alpha3Sidecars.Name(), func(rs *resource.Instance) bool { - s := rs.Message.(*v1alpha3.Sidecar) - - // For this analysis, ignore Sidecars with no workload selectors specified at all. - if s.WorkloadSelector == nil || len(s.WorkloadSelector.Labels) == 0 { - return true - } - - sNs := rs.Metadata.FullName.Namespace - sel := labels.SelectorFromSet(s.WorkloadSelector.Labels) - - foundPod := false - c.ForEach(collections.K8SCoreV1Pods.Name(), func(rp *resource.Instance) bool { - pNs := rp.Metadata.FullName.Namespace - podLabels := labels.Set(rp.Metadata.Labels) - - // Only attempt to match in the same namespace - if pNs != sNs { - return true - } - - if sel.Matches(podLabels) { - foundPod = true - podsToSidecars[rp.Metadata.FullName] = append(podsToSidecars[rp.Metadata.FullName], rs) - } - - return true - }) - - if !foundPod { - m := msg.NewReferencedResourceNotFound(rs, "selector", sel.String()) - - label := util.ExtractLabelFromSelectorString(sel.String()) - if line, ok := util.ErrorLine(rs, fmt.Sprintf(util.WorkloadSelector, label)); ok { - m.Line = line - } - - c.Report(collections.IstioNetworkingV1Alpha3Sidecars.Name(), m) - } - - return true - }) - - for p, sList := range podsToSidecars { - if len(sList) == 1 { - continue - } - - sNames := getNames(sList) - - for _, rs := range sList { - - m := msg.NewConflictingSidecarWorkloadSelectors(rs, sNames, - p.Namespace.String(), p.Name.String()) - - if line, ok := util.ErrorLine(rs, fmt.Sprintf(util.MetadataName)); ok { - m.Line = line - } - - c.Report(collections.IstioNetworkingV1Alpha3Sidecars.Name(), m) - } - } -} diff --git a/pkg/config/analysis/analyzers/sidecar/util.go b/pkg/config/analysis/analyzers/sidecar/util.go deleted file mode 100644 index d45fc0f75..000000000 --- a/pkg/config/analysis/analyzers/sidecar/util.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package sidecar - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" -) - -func getNames(entries []*resource.Instance) []string { - names := make([]string, 0, len(entries)) - for _, rs := range entries { - names = append(names, string(rs.Metadata.FullName.Name)) - } - return names -} diff --git a/pkg/config/analysis/analyzers/testdata/absolute-envoy-filter-operation.yaml b/pkg/config/analysis/analyzers/testdata/absolute-envoy-filter-operation.yaml deleted file mode 100644 index f1e5532c9..000000000 --- a/pkg/config/analysis/analyzers/testdata/absolute-envoy-filter-operation.yaml +++ /dev/null @@ -1,129 +0,0 @@ -# If the patch operation is INSERT_FIRST or priority is set, then the analyzer will not do anything -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: test-reviews-lua-1 - namespace: bookinfo -spec: - workloadSelector: - labels: - app: reviews - configPatches: - # The first patch adds the lua filter to the listener/http connection manager - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - listener: - portNumber: 8080 - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_FIRST - value: # lua filter specification - name: envoy.lua - typed_config: - "@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua" - inlineCode: | - function envoy_on_request(request_handle) - -- Make an HTTP call to an upstream host with the following headers, body, and timeout. - local headers, body = request_handle:httpCall( - "lua_cluster", - { - [":method"] = "POST", - [":path"] = "/acl", - [":authority"] = "internal.org.net" - }, - "authorize call", - 5000) - end - # The second patch adds the cluster that is referenced by the lua code - # cds match is omitted as a new cluster is being added - - applyTo: CLUSTER - match: - context: SIDECAR_OUTBOUND - patch: - operation: ADD - value: # cluster specification - name: "lua_cluster" - type: STRICT_DNS - connect_timeout: 0.5s - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: lua_cluster - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - protocol: TCP - address: "internal.org.net" - port_value: 8888 - ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: test-reviews-lua-2 - namespace: bookinfo -spec: - workloadSelector: - labels: - app: reviews - priority: 10 - configPatches: - # The first patch adds the lua filter to the listener/http connection manager - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - listener: - portNumber: 8080 - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: # lua filter specification - name: envoy.lua - typed_config: - "@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua" - inlineCode: | - function envoy_on_request(request_handle) - -- Make an HTTP call to an upstream host with the following headers, body, and timeout. - local headers, body = request_handle:httpCall( - "lua_cluster", - { - [":method"] = "POST", - [":path"] = "/acl", - [":authority"] = "internal.org.net" - }, - "authorize call", - 5000) - end - # The second patch adds the cluster that is referenced by the lua code - # cds match is omitted as a new cluster is being added - - applyTo: CLUSTER - match: - context: SIDECAR_OUTBOUND - patch: - operation: ADD - value: # cluster specification - name: "lua_cluster" - type: STRICT_DNS - connect_timeout: 0.5s - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: lua_cluster - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - protocol: TCP - address: "internal.org.net" - port_value: 8888 ---- diff --git a/pkg/config/analysis/analyzers/testdata/authorizationpolicies.yaml b/pkg/config/analysis/analyzers/testdata/authorizationpolicies.yaml deleted file mode 100644 index ba48d2a42..000000000 --- a/pkg/config/analysis/analyzers/testdata/authorizationpolicies.yaml +++ /dev/null @@ -1,275 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: httpbin - labels: - istio-injection: "enabled" -spec: {} ---- -apiVersion: v1 -kind: Service -metadata: - name: httpbin - namespace: httpbin - labels: - app: httpbin -spec: - ports: - - name: http - port: 8000 - targetPort: 80 - selector: - app: httpbin ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: httpbin - namespace: httpbin -spec: - replicas: 1 - selector: - matchLabels: - app: httpbin - version: v1 - template: - metadata: - labels: - app: httpbin - version: v1 - spec: - containers: - - image: docker.io/kennethreitz/httpbin - imagePullPolicy: IfNotPresent - name: httpbin - ports: - - containerPort: 80 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: istio - namespace: httpbin -spec: - hosts: - - subsystem.istio.io - - "*.kiali.io" - location: MESH_EXTERNAL - ports: - - number: 80 - name: http - protocol: HTTP - resolution: DNS ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin # This is a correct scenario - namespace: httpbin -spec: - selector: # There are workloads matching this selector - matchLabels: - app: httpbin - version: v1 - rules: - - from: - - source: - principals: ["cluster.local/ns/default/sa/sleep"] - - source: - namespaces: ["httpbin"] # Namespace exists - to: - - operation: - methods: ["GET"] - paths: ["/info*"] - - operation: - methods: ["POST"] - paths: ["/data"] - when: - - key: request.auth.claims[iss] - values: ["https://accounts.google.com"] ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: meshwide-httpbin - namespace: dubbo-system # valid: it applies to whole mesh -spec: - {} ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: meshwide-httpbin-v1 - namespace: dubbo-system # invalid: no pods running anywhere in the mesh -spec: - selector: - matchLabels: - version: bogus-version ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-empty-namespace-wide # Invalid, no pods running - namespace: httpbin-empty -spec: - rules: - - from: - - source: - principals: ["cluster.local/ns/default/sa/sleep"] - - source: - namespaces: ["httpbin"] - to: - - operation: - methods: ["GET"] - paths: ["/info*"] - - operation: - methods: ["POST"] - paths: ["/data"] - when: - - key: request.auth.claims[iss] - values: ["https://accounts.google.com"] ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-namespace-wide # valid, one pod running - namespace: httpbin -spec: - rules: - - from: - - source: - principals: ["cluster.local/ns/default/sa/sleep"] - - source: - namespaces: ["httpbin"] - to: - - operation: - methods: ["GET"] - paths: ["/info*"] - - operation: - methods: ["POST"] - paths: ["/data"] - when: - - key: request.auth.claims[iss] - values: ["https://accounts.google.com"] ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-nopods # Invalid: there aren't matching workloads for this selector - namespace: httpbin -spec: - selector: - matchLabels: - app: bogus-label # Bogus label. No matching workloads - version: v1 - rules: - - from: - - source: - principals: ["cluster.local/ns/default/sa/sleep"] - - source: - namespaces: ["httpbin"] - to: - - operation: - methods: ["GET"] - paths: ["/info*"] - - operation: - methods: ["POST"] - paths: ["/data"] - when: - - key: request.auth.claims[iss] - values: ["https://accounts.google.com"] ---- -apiVersion: v1 -kind: Namespace -metadata: - name: prod-httpbin - labels: - istio-injection: "enabled" -spec: {} ---- -apiVersion: v1 -kind: Namespace -metadata: - name: httpbin-test - labels: - istio-injection: "enabled" -spec: {} ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-bogus-not-ns # Invalid: There are two namespaces in the source notNamespaces that doesn't exist - namespace: httpbin -spec: - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - from: - - source: - principals: ["cluster.local/ns/default/sa/sleep"] - - source: - notNamespaces: - - "prod-*" - - "*-test" - - "*-bogus" # No namespace matching - - "bogus-*" # No namespace matching - - "*" - - "httpbin" - to: - - operation: - methods: ["GET"] - paths: ["/info*"] - - operation: - methods: ["POST"] - paths: ["/data"] - when: - - key: request.auth.claims[iss] - values: ["https://accounts.google.com"] ---- -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: httpbin-bogus-ns # Invalid: there is one source namespace expr that doesn't match any namespace - namespace: httpbin -spec: - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - from: - - source: - principals: ["cluster.local/ns/default/sa/sleep"] - - source: - namespaces: - - "prod-*" - - "*-test" - - "*-bogus" # No namespace matching - - "bogus-*" # No namespace matching - - "*" - - "httpbin" - to: - - operation: - methods: ["GET"] - paths: ["/info*"] - - operation: - methods: ["POST"] - paths: ["/data"] - when: - - key: request.auth.claims[iss] - values: ["https://accounts.google.com"] ---- -apiVersion: v1 -kind: Pod -metadata: - labels: - app: httpbin - version: v1 - name: httpbin-55bf89f8c9-wzfrh - namespace: httpbin -spec: - containers: - - image: gcr.io/google-samples/microservices-demo/adservice:v0.1.1 - name: server diff --git a/pkg/config/analysis/analyzers/testdata/common/meshnetworks.yaml b/pkg/config/analysis/analyzers/testdata/common/meshnetworks.yaml deleted file mode 100644 index b3c393bed..000000000 --- a/pkg/config/analysis/analyzers/testdata/common/meshnetworks.yaml +++ /dev/null @@ -1,13 +0,0 @@ -networks: - network1: - endpoints: - - fromRegistry: kubernetes - gateways: - - port: 443 - registry_service_name: istio-ingressgateway.dubbo-system.svc.cluster.local - network2: - endpoints: - - fromRegistry: istio-testing - gateways: - - port: 443 - registry_service_name: istio-ingressgateway.dubbo-system.svc.cluster.local diff --git a/pkg/config/analysis/analyzers/testdata/common/sidecar-injector-configmap.yaml b/pkg/config/analysis/analyzers/testdata/common/sidecar-injector-configmap.yaml deleted file mode 100644 index cbe2cda26..000000000 --- a/pkg/config/analysis/analyzers/testdata/common/sidecar-injector-configmap.yaml +++ /dev/null @@ -1,418 +0,0 @@ -apiVersion: v1 -data: - config: |- - policy: enabled - alwaysInjectSelector: - [] - neverInjectSelector: - [] - template: | - rewriteAppHTTPProbe: {{ valueOrDefault .Values.sidecarInjectorWebhook.rewriteAppHTTPProbe false }} - {{- if or (not .Values.istio_cni.enabled) .Values.global.proxy.enableCoreDump }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{- if not .Values.istio_cni.enabled }} - - name: istio-init - {{- if contains "/" .Values.global.proxy_init.image }} - image: "{{ .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .Values.global.hub }}/{{ .Values.global.proxy_init.image }}:{{ .Values.global.tag }}" - {{- end }} - command: - - istio-iptables - - "-p" - - 15001 - - "-z" - - "15006" - - "-u" - - 1337 - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` `*` }}" - - "-d" - - "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - imagePullPolicy: "{{ valueOrDefault .Values.global.imagePullPolicy `Always` }}" - {{- if .Values.global.proxy_init.resources }} - resources: - {{ toYaml .Values.global.proxy_init.resources | indent 4 }} - {{- else }} - resources: {} - {{- end }} - securityContext: - runAsUser: 0 - runAsNonRoot: false - capabilities: - add: - - NET_ADMIN - {{- if .Values.global.proxy.privileged }} - privileged: true - {{- end }} - restartPolicy: Always - {{- end }} - {{ end -}} - {{- if eq .Values.global.proxy.enableCoreDump true }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" .Values.global.proxy_init.image }} - image: "{{ .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .Values.global.hub }}/{{ .Values.global.proxy_init.image }}:{{ .Values.global.tag }}" - {{- end }} - imagePullPolicy: "{{ valueOrDefault .Values.global.imagePullPolicy `Always` }}" - resources: {} - securityContext: - runAsUser: 0 - runAsNonRoot: false - privileged: true - {{ end }} - {{- end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .Values.global.hub }}/{{ .Values.global.proxy.image }}:{{ .Values.global.tag }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --configPath - - "/etc/istio/proxy" - - --binaryPath - - "/usr/local/bin/envoy" - - --serviceCluster - {{ if ne "" (index .ObjectMeta.Labels "app") -}} - - "{{ index .ObjectMeta.Labels `app` }}.$(POD_NAMESPACE)" - {{ else -}} - - "{{ valueOrDefault .DeploymentMeta.Name `istio-proxy` }}.{{ valueOrDefault .DeploymentMeta.Namespace `default` }}" - {{ end -}} - - --drainDuration - - "{{ formatDuration .ProxyConfig.DrainDuration }}" - - --parentShutdownDuration - - "{{ formatDuration .ProxyConfig.ParentShutdownDuration }}" - - --discoveryAddress - - "{{ annotation .ObjectMeta `sidecar.istio.io/discoveryAddress` .ProxyConfig.DiscoveryAddress }}" - {{- if eq .Values.global.proxy.tracer "lightstep" }} - - --lightstepAddress - - "{{ .ProxyConfig.GetTracing.GetLightstep.GetAddress }}" - - --lightstepAccessToken - - "{{ .ProxyConfig.GetTracing.GetLightstep.GetAccessToken }}" - - --lightstepSecure={{ .ProxyConfig.GetTracing.GetLightstep.GetSecure }} - - --lightstepCacertPath - - "{{ .ProxyConfig.GetTracing.GetLightstep.GetCacertPath }}" - {{- else if eq .Values.global.proxy.tracer "zipkin" }} - - --zipkinAddress - - "{{ .ProxyConfig.GetTracing.GetZipkin.GetAddress }}" - {{- else if eq .Values.global.proxy.tracer "datadog" }} - - --datadogAgentAddress - - "{{ .ProxyConfig.GetTracing.GetDatadog.GetAddress }}" - {{- end }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel}} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel}} - - --connectTimeout - - "{{ formatDuration .ProxyConfig.ConnectTimeout }}" - {{- if .Values.global.proxy.envoyStatsd.enabled }} - - --statsdUdpAddress - - "{{ .ProxyConfig.StatsdUdpAddress }}" - {{- end }} - {{- if .Values.global.proxy.envoyMetricsService.enabled }} - - --envoyMetricsServiceAddress - - "{{ .ProxyConfig.GetEnvoyMetricsService.GetAddress }}" - {{- end }} - {{- if .Values.global.proxy.envoyAccessLogService.enabled }} - - --envoyAccessLogServiceAddress - - "{{ .ProxyConfig.GetEnvoyAccessLogService.GetAddress }}" - {{- end }} - - --proxyAdminPort - - "{{ .ProxyConfig.ProxyAdminPort }}" - {{ if gt .ProxyConfig.Concurrency 0 -}} - - --concurrency - - "{{ .ProxyConfig.Concurrency }}" - {{ end -}} - {{- if .Values.global.controlPlaneSecurityEnabled }} - - --controlPlaneAuthPolicy - - MUTUAL_TLS - {{- else }} - - --controlPlaneAuthPolicy - - NONE - {{- end }} - - --dnsRefreshRate - - {{ valueOrDefault .Values.global.proxy.dnsRefreshRate "300s" }} - {{- if (ne (annotation .ObjectMeta "status.sidecar.istio.io/port" .Values.global.proxy.statusPort) "0") }} - - --statusPort - - "{{ annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort }}" - - --applicationPorts - - "{{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/applicationPorts` (applicationPorts .Spec.Containers) }}" - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - --templateFile=/etc/istio/custom-bootstrap/envoy_bootstrap.json - {{- end }} - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - {{- if eq .Values.global.proxy.tracer "datadog" }} - {{- if isset .ObjectMeta.Annotations `apm.datadoghq.com/env` }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- end }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: ISTIO_META_CONFIG_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: SDS_ENABLED - value: "{{ .Values.global.sds.enabled }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - - name: ISTIO_META_INCLUDE_INBOUND_PORTS - value: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` (applicationPorts .Spec.Containers) }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{ if .ObjectMeta.Labels }} - - name: ISTIO_METAJSON_LABELS - value: | - {{ toJSON .ObjectMeta.Labels }} - {{ end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: {{ .DeploymentMeta.Name }} - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.sds.customTokenDirectory }} - - name: ISTIO_META_SDS_TOKEN_PATH - value: "{{ .Values.global.sds.customTokenDirectory -}}/sdstoken" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if .Values.global.trustDomain }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.trustDomain }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - imagePullPolicy: "{{ valueOrDefault .Values.global.imagePullPolicy `Always` }}" - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if .Values.global.proxy.privileged }} - privileged: true - {{- end }} - {{- if ne .Values.global.proxy.enableCoreDump true }} - readOnlyRootFilesystem: true - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - capabilities: - add: - - NET_ADMIN - runAsGroup: 1337 - {{ else -}} - {{ if .Values.global.sds.enabled }} - runAsGroup: 1337 - {{- end }} - runAsUser: 1337 - {{- end }} - resources: - {{ if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end}} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{ else -}} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 4 }} - {{- end }} - {{ end -}} - volumeMounts: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if .Values.global.sds.enabled }} - - mountPath: /var/run/sds - name: sds-uds-path - readOnly: true - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- if .Values.global.sds.customTokenDirectory }} - - mountPath: "{{ .Values.global.sds.customTokenDirectory -}}" - name: custom-sds-token - readOnly: true - {{- end }} - {{- else }} - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .Values.global.tracer.lightstep.cacertPath }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetLightstep.GetCacertPath }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{- end }} - volumes: - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - - emptyDir: - medium: Memory - name: istio-envoy - {{- if .Values.global.sds.enabled }} - - name: sds-uds-path - hostPath: - path: /var/run/sds - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- if .Values.global.sds.customTokenDirectory }} - - name: custom-sds-token - secret: - secretName: sdstokensecret - {{- end }} - {{- else }} - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 2 }} - {{ end }} - {{ end }} - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .Values.global.tracer.lightstep.cacertPath }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.podDNSSearchNamespaces }} - dnsConfig: - searches: - {{- range .Values.global.podDNSSearchNamespaces }} - - {{ render . }} - {{- end }} - {{- end }} - injectedAnnotations: - values: '{"certmanager":{"enabled":false,"hub":"quay.io/jetstack","image":"cert-manager-controller","namespace":"dubbo-system","tag":"v0.6.2"},"cni":{"namespace":"dubbo-system"},"galley":{"enableAnalysis":false,"enabled":true,"image":"galley","namespace":"dubbo-system"},"gateways":{"istio-egressgateway":{"autoscaleEnabled":true,"enabled":false,"namespace":"dubbo-system","ports":[{"name":"http2","port":80},{"name":"https","port":443},{"name":"tls","port":15443,"targetPort":15443}],"secretVolumes":[{"mountPath":"/etc/istio/egressgateway-certs","name":"egressgateway-certs","secretName":"istio-egressgateway-certs"},{"mountPath":"/etc/istio/egressgateway-ca-certs","name":"egressgateway-ca-certs","secretName":"istio-egressgateway-ca-certs"}],"type":"ClusterIP","zvpn":{"enabled":true,"suffix":"global"}},"istio-ingressgateway":{"applicationPorts":"","autoscaleEnabled":true,"debug":"info","domain":"","enabled":true,"meshExpansionPorts":[{"name":"tcp-pilot-grpc-tls","port":15011,"targetPort":15011},{"name":"tcp-citadel-grpc-tls","port":8060,"targetPort":8060},{"name":"tcp-dns-tls","port":853,"targetPort":853}],"namespace":"dubbo-system","ports":[{"name":"status-port","port":15020,"targetPort":15020},{"name":"http2","port":80,"targetPort":80},{"name":"https","port":443},{"name":"kiali","port":15029,"targetPort":15029},{"name":"prometheus","port":15030,"targetPort":15030},{"name":"grafana","port":15031,"targetPort":15031},{"name":"tracing","port":15032,"targetPort":15032},{"name":"tls","port":15443,"targetPort":15443}],"sds":{"enabled":false,"image":"node-agent-k8s","resources":{"limits":{"cpu":"2000m","memory":"1024Mi"},"requests":{"cpu":"100m","memory":"128Mi"}}},"secretVolumes":[{"mountPath":"/etc/istio/ingressgateway-certs","name":"ingressgateway-certs","secretName":"istio-ingressgateway-certs"},{"mountPath":"/etc/istio/ingressgateway-ca-certs","name":"ingressgateway-ca-certs","secretName":"istio-ingressgateway-ca-certs"}],"type":"LoadBalancer","zvpn":{"enabled":true,"suffix":"global"}}},"global":{"arch":{"amd64":2,"ppc64le":2,"s390x":2},"certificates":[],"configNamespace":"dubbo-system","configValidation":true,"controlPlaneSecurityEnabled":true,"defaultNodeSelector":{},"defaultPodDisruptionBudget":{"enabled":true},"defaultResources":{"requests":{"cpu":"10m"}},"disablePolicyChecks":true,"enableHelmTest":false,"enableTracing":true,"enabled":true,"hub":"docker.io/istio","imagePullPolicy":"IfNotPresent","imagePullSecrets":[],"istioNamespace":"dubbo-system","k8sIngress":{"enableHttps":false,"enabled":false,"gatewayName":"ingressgateway"},"localityLbSetting":{"enabled":true},"logAsJson":false,"logging":{"level":"default:info"},"meshExpansion":{"enabled":false,"useILB":false},"meshNetworks":{},"mtls":{"auto":false,"enabled":false},"multiCluster":{"clusterName":"","enabled":false},"namespace":"dubbo-system","network":"","omitSidecarInjectorConfigMap":false,"oneNamespace":false,"operatorManageWebhooks":false,"outboundTrafficPolicy":{"mode":"ALLOW_ANY"},"policyCheckFailOpen":false,"policyNamespace":"dubbo-system","priorityClassName":"","prometheusNamespace":"dubbo-system","proxy":{"accessLogEncoding":"TEXT","accessLogFile":"","accessLogFormat":"","autoInject":"enabled","clusterDomain":"cluster.local","componentLogLevel":"misc:error","concurrency":2,"dnsRefreshRate":"300s","enableCoreDump":false,"envoyAccessLogService":{"enabled":false},"envoyMetricsService":{"enabled":false,"tcpKeepalive":{"interval":"10s","probes":3,"time":"10s"},"tlsSettings":{"mode":"DISABLE","subjectAltNames":[]}},"envoyStatsd":{"enabled":false},"excludeIPRanges":"","excludeInboundPorts":"","excludeOutboundPorts":"","image":"proxyv2","includeIPRanges":"*","includeInboundPorts":"*","kubevirtInterfaces":"","logLevel":"warning","privileged":false,"protocolDetectionTimeout":"100ms","readinessFailureThreshold":30,"readinessInitialDelaySeconds":1,"readinessPeriodSeconds":2,"resources":{"limits":{"cpu":"2000m","memory":"1024Mi"},"requests":{"cpu":"100m","memory":"128Mi"}},"statusPort":15020,"tracer":"zipkin"},"proxy_init":{"image":"proxyv2","resources":{"limits":{"cpu":"100m","memory":"50Mi"},"requests":{"cpu":"10m","memory":"10Mi"}}},"sds":{"enabled":false,"token":{"aud":"istio-ca"},"udsPath":""},"securityNamespace":"dubbo-system","tag":"1.3.1","telemetryNamespace":"dubbo-system","tracer":{"datadog":{"address":"$(HOST_IP):8126"},"lightstep":{"accessToken":"","address":"","cacertPath":"","secure":true},"zipkin":{"address":""}},"trustDomain":"cluster.local","useMCP":true},"grafana":{"accessMode":"ReadWriteMany","contextPath":"/grafana","dashboardProviders":{"dashboardproviders.yaml":{"apiVersion":1,"providers":[{"disableDeletion":false,"folder":"istio","name":"istio","options":{"path":"/var/lib/grafana/dashboards/istio"},"orgId":1,"type":"file"}]}},"datasources":{"datasources.yaml":{"apiVersion":1}},"enabled":false,"env":{},"envSecrets":{},"image":{"repository":"grafana/grafana","tag":"6.4.3"},"ingress":{"enabled":false,"hosts":["grafana.local"]},"namespace":"dubbo-system","nodeSelector":{},"persist":false,"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"security":{"enabled":false,"passphraseKey":"passphrase","secretName":"grafana","usernameKey":"username"},"service":{"annotations":{},"externalPort":3000,"name":"http","type":"ClusterIP"},"storageClassName":"","tolerations":[]},"istio_cni":{"enabled":false},"istiocoredns":{"coreDNSImage":"coredns/coredns","coreDNSPluginImage":"istio/coredns-plugin:0.2-istio-1.1","coreDNSTag":"1.6.2","enabled":false,"namespace":"dubbo-system"},"kiali":{"contextPath":"/kiali","createDemoSecret":false,"dashboard":{"passphraseKey":"passphrase","secretName":"kiali","usernameKey":"username","viewOnlyMode":false},"enabled":false,"hub":"quay.io/kiali","ingress":{"enabled":false,"hosts":["kiali.local"]},"namespace":"dubbo-system","nodeSelector":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"security":{"cert_file":"/kiali-cert/cert-chain.pem","enabled":false,"private_key_file":"/kiali-cert/key.pem"},"tag":"v1.9"},"mixer":{"adapters":{"kubernetesenv":{"enabled":true},"prometheus":{"enabled":true,"metricsExpiryDuration":"10m"},"stackdriver":{"auth":{"apiKey":"","appCredentials":false,"serviceAccountPath":""},"enabled":false,"tracer":{"enabled":false,"sampleProbability":1}},"stdio":{"enabled":false,"outputAsJson":false},"useAdapterCRDs":false},"policy":{"adapters":{"kubernetesenv":{"enabled":true},"useAdapterCRDs":false},"autoscaleEnabled":true,"enabled":true,"image":"mixer","namespace":"dubbo-system","sessionAffinityEnabled":false},"telemetry":{"autoscaleEnabled":true,"enabled":true,"env":{"GOMAXPROCS":"6"},"image":"mixer","loadshedding":{"latencyThreshold":"100ms","mode":"enforce"},"namespace":"dubbo-system","nodeSelector":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"reportBatchMaxEntries":100,"reportBatchMaxTime":"1s","sessionAffinityEnabled":false,"tolerations":[],"useMCP":true}},"nodeagent":{"enabled":false,"image":"node-agent-k8s","namespace":"dubbo-system"},"pilot":{"appNamespaces":[],"autoscaleEnabled":true,"autoscaleMax":5,"autoscaleMin":1,"configMap":true,"configNamespace":"istio-config","cpu":{"targetAverageUtilization":80},"enableProtocolSniffingForInbound":false,"enableProtocolSniffingForOutbound":true,"enabled":true,"env":{},"image":"pilot","ingress":{"ingressClass":"istio","ingressControllerMode":"OFF","ingressService":"istio-ingressgateway"},"keepaliveMaxServerConnectionAge":"30m","meshNetworks":{"networks":{}},"namespace":"dubbo-system","nodeSelector":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"policy":{"enabled":false},"replicaCount":1,"tolerations":[],"traceSampling":1,"useMCP":true},"prometheus":{"contextPath":"/prometheus","enabled":true,"hub":"docker.io/prom","ingress":{"enabled":false,"hosts":["prometheus.local"]},"namespace":"dubbo-system","nodeSelector":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"retention":"6h","scrapeInterval":"15s","security":{"enabled":true},"tag":"v2.12.0","tolerations":[]},"security":{"dnsCerts":{"istio-pilot-service-account.istio-control":"istio-pilot.istio-control"},"enableNamespacesByDefault":true,"enabled":true,"image":"citadel","namespace":"dubbo-system","selfSigned":true,"trustDomain":"cluster.local"},"sidecarInjectorWebhook":{"alwaysInjectSelector":[],"enableNamespacesByDefault":false,"enabled":true,"image":"sidecar_injector","injectLabel":"istio-injection","injectedAnnotations":{},"namespace":"dubbo-system","neverInjectSelector":[],"nodeSelector":{},"objectSelector":{"autoInject":true,"enabled":false},"podAnnotations":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"resources":{},"rewriteAppHTTPProbe":false,"rollingMaxSurge":"100%","rollingMaxUnavailable":"25%","selfSigned":false,"tolerations":[]},"telemetry":{"enabled":true,"v1":{"enabled":true},"v2":{"enabled":false,"prometheus":{"enabled":true},"stackdriver":{"configOverride":{},"enabled":false,"logging":false,"monitoring":false,"topology":false}}},"tracing":{"enabled":false,"ingress":{"enabled":false},"jaeger":{"accessMode":"ReadWriteMany","enabled":false,"hub":"docker.io/jaegertracing","memory":{"max_traces":50000},"namespace":"dubbo-system","persist":false,"spanStorageType":"badger","storageClassName":"","tag":"1.18"},"nodeSelector":{},"opencensus":{"exporters":{"stackdriver":{"enable_tracing":true}},"hub":"docker.io/omnition","resources":{"limits":{"cpu":"1","memory":"2Gi"},"requests":{"cpu":"200m","memory":"400Mi"}},"tag":"0.1.9"},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"provider":"jaeger","service":{"annotations":{},"externalPort":9411,"name":"http-query","type":"ClusterIP"},"zipkin":{"hub":"docker.io/openzipkin","javaOptsHeap":700,"maxSpans":500000,"node":{"cpus":2},"probeStartupDelay":200,"queryPort":9411,"resources":{"limits":{"cpu":"300m","memory":"900Mi"},"requests":{"cpu":"150m","memory":"900Mi"}},"tag":"2.14.2"}},"version":""}' -kind: ConfigMap -metadata: - labels: - app: sidecar-injector - istio: sidecar-injector - operator.istio.io/component: Injector - operator.istio.io/managed: Reconcile - operator.istio.io/version: 1.3.1 - release: istio - name: istio-sidecar-injector - namespace: dubbo-system diff --git a/pkg/config/analysis/analyzers/testdata/common/sidecar-injector-enabled-nsbydefault.yaml b/pkg/config/analysis/analyzers/testdata/common/sidecar-injector-enabled-nsbydefault.yaml deleted file mode 100644 index e124ae6eb..000000000 --- a/pkg/config/analysis/analyzers/testdata/common/sidecar-injector-enabled-nsbydefault.yaml +++ /dev/null @@ -1,418 +0,0 @@ -apiVersion: v1 -data: - config: |- - policy: enabled - alwaysInjectSelector: - [] - neverInjectSelector: - [] - template: | - rewriteAppHTTPProbe: {{ valueOrDefault .Values.sidecarInjectorWebhook.rewriteAppHTTPProbe false }} - {{- if or (not .Values.istio_cni.enabled) .Values.global.proxy.enableCoreDump }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{- if not .Values.istio_cni.enabled }} - - name: istio-init - {{- if contains "/" .Values.global.proxy_init.image }} - image: "{{ .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .Values.global.hub }}/{{ .Values.global.proxy_init.image }}:{{ .Values.global.tag }}" - {{- end }} - command: - - istio-iptables - - "-p" - - 15001 - - "-z" - - "15006" - - "-u" - - 1337 - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` `*` }}" - - "-d" - - "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - imagePullPolicy: "{{ valueOrDefault .Values.global.imagePullPolicy `Always` }}" - {{- if .Values.global.proxy_init.resources }} - resources: - {{ toYaml .Values.global.proxy_init.resources | indent 4 }} - {{- else }} - resources: {} - {{- end }} - securityContext: - runAsUser: 0 - runAsNonRoot: false - capabilities: - add: - - NET_ADMIN - {{- if .Values.global.proxy.privileged }} - privileged: true - {{- end }} - restartPolicy: Always - {{- end }} - {{ end -}} - {{- if eq .Values.global.proxy.enableCoreDump true }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" .Values.global.proxy_init.image }} - image: "{{ .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .Values.global.hub }}/{{ .Values.global.proxy_init.image }}:{{ .Values.global.tag }}" - {{- end }} - imagePullPolicy: "{{ valueOrDefault .Values.global.imagePullPolicy `Always` }}" - resources: {} - securityContext: - runAsUser: 0 - runAsNonRoot: false - privileged: true - {{ end }} - {{- end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .Values.global.hub }}/{{ .Values.global.proxy.image }}:{{ .Values.global.tag }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --configPath - - "/etc/istio/proxy" - - --binaryPath - - "/usr/local/bin/envoy" - - --serviceCluster - {{ if ne "" (index .ObjectMeta.Labels "app") -}} - - "{{ index .ObjectMeta.Labels `app` }}.$(POD_NAMESPACE)" - {{ else -}} - - "{{ valueOrDefault .DeploymentMeta.Name `istio-proxy` }}.{{ valueOrDefault .DeploymentMeta.Namespace `default` }}" - {{ end -}} - - --drainDuration - - "{{ formatDuration .ProxyConfig.DrainDuration }}" - - --parentShutdownDuration - - "{{ formatDuration .ProxyConfig.ParentShutdownDuration }}" - - --discoveryAddress - - "{{ annotation .ObjectMeta `sidecar.istio.io/discoveryAddress` .ProxyConfig.DiscoveryAddress }}" - {{- if eq .Values.global.proxy.tracer "lightstep" }} - - --lightstepAddress - - "{{ .ProxyConfig.GetTracing.GetLightstep.GetAddress }}" - - --lightstepAccessToken - - "{{ .ProxyConfig.GetTracing.GetLightstep.GetAccessToken }}" - - --lightstepSecure={{ .ProxyConfig.GetTracing.GetLightstep.GetSecure }} - - --lightstepCacertPath - - "{{ .ProxyConfig.GetTracing.GetLightstep.GetCacertPath }}" - {{- else if eq .Values.global.proxy.tracer "zipkin" }} - - --zipkinAddress - - "{{ .ProxyConfig.GetTracing.GetZipkin.GetAddress }}" - {{- else if eq .Values.global.proxy.tracer "datadog" }} - - --datadogAgentAddress - - "{{ .ProxyConfig.GetTracing.GetDatadog.GetAddress }}" - {{- end }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel}} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel}} - - --connectTimeout - - "{{ formatDuration .ProxyConfig.ConnectTimeout }}" - {{- if .Values.global.proxy.envoyStatsd.enabled }} - - --statsdUdpAddress - - "{{ .ProxyConfig.StatsdUdpAddress }}" - {{- end }} - {{- if .Values.global.proxy.envoyMetricsService.enabled }} - - --envoyMetricsServiceAddress - - "{{ .ProxyConfig.GetEnvoyMetricsService.GetAddress }}" - {{- end }} - {{- if .Values.global.proxy.envoyAccessLogService.enabled }} - - --envoyAccessLogServiceAddress - - "{{ .ProxyConfig.GetEnvoyAccessLogService.GetAddress }}" - {{- end }} - - --proxyAdminPort - - "{{ .ProxyConfig.ProxyAdminPort }}" - {{ if gt .ProxyConfig.Concurrency 0 -}} - - --concurrency - - "{{ .ProxyConfig.Concurrency }}" - {{ end -}} - {{- if .Values.global.controlPlaneSecurityEnabled }} - - --controlPlaneAuthPolicy - - MUTUAL_TLS - {{- else }} - - --controlPlaneAuthPolicy - - NONE - {{- end }} - - --dnsRefreshRate - - {{ valueOrDefault .Values.global.proxy.dnsRefreshRate "300s" }} - {{- if (ne (annotation .ObjectMeta "status.sidecar.istio.io/port" .Values.global.proxy.statusPort) "0") }} - - --statusPort - - "{{ annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort }}" - - --applicationPorts - - "{{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/applicationPorts` (applicationPorts .Spec.Containers) }}" - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - --templateFile=/etc/istio/custom-bootstrap/envoy_bootstrap.json - {{- end }} - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - {{- if eq .Values.global.proxy.tracer "datadog" }} - {{- if isset .ObjectMeta.Annotations `apm.datadoghq.com/env` }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- end }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: ISTIO_META_CONFIG_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: SDS_ENABLED - value: "{{ .Values.global.sds.enabled }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - - name: ISTIO_META_INCLUDE_INBOUND_PORTS - value: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` (applicationPorts .Spec.Containers) }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{ if .ObjectMeta.Labels }} - - name: ISTIO_METAJSON_LABELS - value: | - {{ toJSON .ObjectMeta.Labels }} - {{ end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: {{ .DeploymentMeta.Name }} - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.sds.customTokenDirectory }} - - name: ISTIO_META_SDS_TOKEN_PATH - value: "{{ .Values.global.sds.customTokenDirectory -}}/sdstoken" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if .Values.global.trustDomain }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.trustDomain }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - imagePullPolicy: "{{ valueOrDefault .Values.global.imagePullPolicy `Always` }}" - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if .Values.global.proxy.privileged }} - privileged: true - {{- end }} - {{- if ne .Values.global.proxy.enableCoreDump true }} - readOnlyRootFilesystem: true - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - capabilities: - add: - - NET_ADMIN - runAsGroup: 1337 - {{ else -}} - {{ if .Values.global.sds.enabled }} - runAsGroup: 1337 - {{- end }} - runAsUser: 1337 - {{- end }} - resources: - {{ if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end}} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{ else -}} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 4 }} - {{- end }} - {{ end -}} - volumeMounts: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if .Values.global.sds.enabled }} - - mountPath: /var/run/sds - name: sds-uds-path - readOnly: true - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- if .Values.global.sds.customTokenDirectory }} - - mountPath: "{{ .Values.global.sds.customTokenDirectory -}}" - name: custom-sds-token - readOnly: true - {{- end }} - {{- else }} - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .Values.global.tracer.lightstep.cacertPath }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetLightstep.GetCacertPath }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{- end }} - volumes: - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - - emptyDir: - medium: Memory - name: istio-envoy - {{- if .Values.global.sds.enabled }} - - name: sds-uds-path - hostPath: - path: /var/run/sds - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- if .Values.global.sds.customTokenDirectory }} - - name: custom-sds-token - secret: - secretName: sdstokensecret - {{- end }} - {{- else }} - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 2 }} - {{ end }} - {{ end }} - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .Values.global.tracer.lightstep.cacertPath }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.podDNSSearchNamespaces }} - dnsConfig: - searches: - {{- range .Values.global.podDNSSearchNamespaces }} - - {{ render . }} - {{- end }} - {{- end }} - injectedAnnotations: - values: '{"certmanager":{"enabled":false,"hub":"quay.io/jetstack","image":"cert-manager-controller","namespace":"dubbo-system","tag":"v0.6.2"},"cni":{"namespace":"dubbo-system"},"galley":{"enableAnalysis":false,"enabled":true,"image":"galley","namespace":"dubbo-system"},"gateways":{"istio-egressgateway":{"autoscaleEnabled":true,"enabled":false,"namespace":"dubbo-system","ports":[{"name":"http2","port":80},{"name":"https","port":443},{"name":"tls","port":15443,"targetPort":15443}],"secretVolumes":[{"mountPath":"/etc/istio/egressgateway-certs","name":"egressgateway-certs","secretName":"istio-egressgateway-certs"},{"mountPath":"/etc/istio/egressgateway-ca-certs","name":"egressgateway-ca-certs","secretName":"istio-egressgateway-ca-certs"}],"type":"ClusterIP","zvpn":{"enabled":true,"suffix":"global"}},"istio-ingressgateway":{"applicationPorts":"","autoscaleEnabled":true,"debug":"info","domain":"","enabled":true,"meshExpansionPorts":[{"name":"tcp-pilot-grpc-tls","port":15011,"targetPort":15011},{"name":"tcp-citadel-grpc-tls","port":8060,"targetPort":8060},{"name":"tcp-dns-tls","port":853,"targetPort":853}],"namespace":"dubbo-system","ports":[{"name":"status-port","port":15020,"targetPort":15020},{"name":"http2","port":80,"targetPort":80},{"name":"https","port":443},{"name":"kiali","port":15029,"targetPort":15029},{"name":"prometheus","port":15030,"targetPort":15030},{"name":"grafana","port":15031,"targetPort":15031},{"name":"tracing","port":15032,"targetPort":15032},{"name":"tls","port":15443,"targetPort":15443}],"sds":{"enabled":false,"image":"node-agent-k8s","resources":{"limits":{"cpu":"2000m","memory":"1024Mi"},"requests":{"cpu":"100m","memory":"128Mi"}}},"secretVolumes":[{"mountPath":"/etc/istio/ingressgateway-certs","name":"ingressgateway-certs","secretName":"istio-ingressgateway-certs"},{"mountPath":"/etc/istio/ingressgateway-ca-certs","name":"ingressgateway-ca-certs","secretName":"istio-ingressgateway-ca-certs"}],"type":"LoadBalancer","zvpn":{"enabled":true,"suffix":"global"}}},"global":{"arch":{"amd64":2,"ppc64le":2,"s390x":2},"certificates":[],"configNamespace":"dubbo-system","configValidation":true,"controlPlaneSecurityEnabled":true,"defaultNodeSelector":{},"defaultPodDisruptionBudget":{"enabled":true},"defaultResources":{"requests":{"cpu":"10m"}},"disablePolicyChecks":true,"enableHelmTest":false,"enableTracing":true,"enabled":true,"hub":"docker.io/istio","imagePullPolicy":"IfNotPresent","imagePullSecrets":[],"istioNamespace":"dubbo-system","k8sIngress":{"enableHttps":false,"enabled":false,"gatewayName":"ingressgateway"},"localityLbSetting":{"enabled":true},"logAsJson":false,"logging":{"level":"default:info"},"meshExpansion":{"enabled":false,"useILB":false},"meshNetworks":{},"mtls":{"auto":false,"enabled":false},"multiCluster":{"clusterName":"","enabled":false},"namespace":"dubbo-system","network":"","omitSidecarInjectorConfigMap":false,"oneNamespace":false,"operatorManageWebhooks":false,"outboundTrafficPolicy":{"mode":"ALLOW_ANY"},"policyCheckFailOpen":false,"policyNamespace":"dubbo-system","priorityClassName":"","prometheusNamespace":"dubbo-system","proxy":{"accessLogEncoding":"TEXT","accessLogFile":"","accessLogFormat":"","autoInject":"enabled","clusterDomain":"cluster.local","componentLogLevel":"misc:error","concurrency":2,"dnsRefreshRate":"300s","enableCoreDump":false,"envoyAccessLogService":{"enabled":false},"envoyMetricsService":{"enabled":false,"tcpKeepalive":{"interval":"10s","probes":3,"time":"10s"},"tlsSettings":{"mode":"DISABLE","subjectAltNames":[]}},"envoyStatsd":{"enabled":false},"excludeIPRanges":"","excludeInboundPorts":"","excludeOutboundPorts":"","image":"proxyv2","includeIPRanges":"*","includeInboundPorts":"*","kubevirtInterfaces":"","logLevel":"warning","privileged":false,"protocolDetectionTimeout":"100ms","readinessFailureThreshold":30,"readinessInitialDelaySeconds":1,"readinessPeriodSeconds":2,"resources":{"limits":{"cpu":"2000m","memory":"1024Mi"},"requests":{"cpu":"100m","memory":"128Mi"}},"statusPort":15020,"tracer":"zipkin"},"proxy_init":{"image":"proxyv2","resources":{"limits":{"cpu":"100m","memory":"50Mi"},"requests":{"cpu":"10m","memory":"10Mi"}}},"sds":{"enabled":false,"token":{"aud":"istio-ca"},"udsPath":""},"securityNamespace":"dubbo-system","tag":"1.3.1","telemetryNamespace":"dubbo-system","tracer":{"datadog":{"address":"$(HOST_IP):8126"},"lightstep":{"accessToken":"","address":"","cacertPath":"","secure":true},"zipkin":{"address":""}},"trustDomain":"cluster.local","useMCP":true},"grafana":{"accessMode":"ReadWriteMany","contextPath":"/grafana","dashboardProviders":{"dashboardproviders.yaml":{"apiVersion":1,"providers":[{"disableDeletion":false,"folder":"istio","name":"istio","options":{"path":"/var/lib/grafana/dashboards/istio"},"orgId":1,"type":"file"}]}},"datasources":{"datasources.yaml":{"apiVersion":1}},"enabled":false,"env":{},"envSecrets":{},"image":{"repository":"grafana/grafana","tag":"6.4.3"},"ingress":{"enabled":false,"hosts":["grafana.local"]},"namespace":"dubbo-system","nodeSelector":{},"persist":false,"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"security":{"enabled":false,"passphraseKey":"passphrase","secretName":"grafana","usernameKey":"username"},"service":{"annotations":{},"externalPort":3000,"name":"http","type":"ClusterIP"},"storageClassName":"","tolerations":[]},"istio_cni":{"enabled":false},"istiocoredns":{"coreDNSImage":"coredns/coredns","coreDNSPluginImage":"istio/coredns-plugin:0.2-istio-1.1","coreDNSTag":"1.6.2","enabled":false,"namespace":"dubbo-system"},"kiali":{"contextPath":"/kiali","createDemoSecret":false,"dashboard":{"passphraseKey":"passphrase","secretName":"kiali","usernameKey":"username","viewOnlyMode":false},"enabled":false,"hub":"quay.io/kiali","ingress":{"enabled":false,"hosts":["kiali.local"]},"namespace":"dubbo-system","nodeSelector":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"security":{"cert_file":"/kiali-cert/cert-chain.pem","enabled":false,"private_key_file":"/kiali-cert/key.pem"},"tag":"v1.9"},"mixer":{"adapters":{"kubernetesenv":{"enabled":true},"prometheus":{"enabled":true,"metricsExpiryDuration":"10m"},"stackdriver":{"auth":{"apiKey":"","appCredentials":false,"serviceAccountPath":""},"enabled":false,"tracer":{"enabled":false,"sampleProbability":1}},"stdio":{"enabled":false,"outputAsJson":false},"useAdapterCRDs":false},"policy":{"adapters":{"kubernetesenv":{"enabled":true},"useAdapterCRDs":false},"autoscaleEnabled":true,"enabled":true,"image":"mixer","namespace":"dubbo-system","sessionAffinityEnabled":false},"telemetry":{"autoscaleEnabled":true,"enabled":true,"env":{"GOMAXPROCS":"6"},"image":"mixer","loadshedding":{"latencyThreshold":"100ms","mode":"enforce"},"namespace":"dubbo-system","nodeSelector":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"reportBatchMaxEntries":100,"reportBatchMaxTime":"1s","sessionAffinityEnabled":false,"tolerations":[],"useMCP":true}},"nodeagent":{"enabled":false,"image":"node-agent-k8s","namespace":"dubbo-system"},"pilot":{"appNamespaces":[],"autoscaleEnabled":true,"autoscaleMax":5,"autoscaleMin":1,"configMap":true,"configNamespace":"istio-config","cpu":{"targetAverageUtilization":80},"enableProtocolSniffingForInbound":false,"enableProtocolSniffingForOutbound":true,"enabled":true,"env":{},"image":"pilot","ingress":{"ingressClass":"istio","ingressControllerMode":"OFF","ingressService":"istio-ingressgateway"},"keepaliveMaxServerConnectionAge":"30m","meshNetworks":{"networks":{}},"namespace":"dubbo-system","nodeSelector":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"policy":{"enabled":false},"replicaCount":1,"tolerations":[],"traceSampling":1,"useMCP":true},"prometheus":{"contextPath":"/prometheus","enabled":true,"hub":"docker.io/prom","ingress":{"enabled":false,"hosts":["prometheus.local"]},"namespace":"dubbo-system","nodeSelector":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"retention":"6h","scrapeInterval":"15s","security":{"enabled":true},"tag":"v2.12.0","tolerations":[]},"security":{"dnsCerts":{"istio-pilot-service-account.istio-control":"istio-pilot.istio-control"},"enableNamespacesByDefault":true,"enabled":true,"image":"citadel","namespace":"dubbo-system","selfSigned":true,"trustDomain":"cluster.local"},"sidecarInjectorWebhook":{"alwaysInjectSelector":[],"enableNamespacesByDefault":true,"enabled":true,"image":"sidecar_injector","injectLabel":"istio-injection","injectedAnnotations":{},"namespace":"dubbo-system","neverInjectSelector":[],"nodeSelector":{},"objectSelector":{"autoInject":true,"enabled":false},"podAnnotations":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"resources":{},"rewriteAppHTTPProbe":false,"rollingMaxSurge":"100%","rollingMaxUnavailable":"25%","selfSigned":false,"tolerations":[]},"telemetry":{"enabled":true,"v1":{"enabled":true},"v2":{"enabled":false,"prometheus":{"enabled":true},"stackdriver":{"configOverride":{},"enabled":false,"logging":false,"monitoring":false,"topology":false}}},"tracing":{"enabled":false,"ingress":{"enabled":false},"jaeger":{"accessMode":"ReadWriteMany","enabled":false,"hub":"docker.io/jaegertracing","memory":{"max_traces":50000},"namespace":"dubbo-system","persist":false,"spanStorageType":"badger","storageClassName":"","tag":"1.18"},"nodeSelector":{},"opencensus":{"exporters":{"stackdriver":{"enable_tracing":true}},"hub":"docker.io/omnition","resources":{"limits":{"cpu":"1","memory":"2Gi"},"requests":{"cpu":"200m","memory":"400Mi"}},"tag":"0.1.9"},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"provider":"jaeger","service":{"annotations":{},"externalPort":9411,"name":"http-query","type":"ClusterIP"},"zipkin":{"hub":"docker.io/openzipkin","javaOptsHeap":700,"maxSpans":500000,"node":{"cpus":2},"probeStartupDelay":200,"queryPort":9411,"resources":{"limits":{"cpu":"300m","memory":"900Mi"},"requests":{"cpu":"150m","memory":"900Mi"}},"tag":"2.14.2"}},"version":""}' -kind: ConfigMap -metadata: - labels: - app: sidecar-injector - istio: sidecar-injector - operator.istio.io/component: Injector - operator.istio.io/managed: Reconcile - operator.istio.io/version: 1.3.1 - release: istio - name: istio-sidecar-injector - namespace: dubbo-system diff --git a/pkg/config/analysis/analyzers/testdata/conflicting-gateways.yaml b/pkg/config/analysis/analyzers/testdata/conflicting-gateways.yaml deleted file mode 100644 index 44472df4c..000000000 --- a/pkg/config/analysis/analyzers/testdata/conflicting-gateways.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: beta -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 80 - name: tcp - protocol: TCP - hosts: - - "foo.bar" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: alpha -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 80 - name: http - protocol: HTTP - hosts: - - "foo.bar" \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/correct-port-name-external-name-service-type.yaml b/pkg/config/analysis/analyzers/testdata/correct-port-name-external-name-service-type.yaml deleted file mode 100644 index b9fea07dc..000000000 --- a/pkg/config/analysis/analyzers/testdata/correct-port-name-external-name-service-type.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: nginx-svc4 - namespace: nginx-ns4 -spec: - externalName: nginx.example.com - ports: - - name: https - port: 443 - protocol: TCP - targetPort: 443 - type: ExternalName \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/deployment-con-sec-uid.yaml b/pkg/config/analysis/analyzers/testdata/deployment-con-sec-uid.yaml deleted file mode 100644 index b02cd5b9b..000000000 --- a/pkg/config/analysis/analyzers/testdata/deployment-con-sec-uid.yaml +++ /dev/null @@ -1,32 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: deploy-con-sec-uid - labels: - app: helloworld - version: v1 -spec: - replicas: 1 - selector: - matchLabels: - app: helloworld - version: v1 - template: - metadata: - labels: - app: helloworld - version: v1 - spec: - securityContext: - runAsUser: 1337 - containers: - - name: helloworld - image: docker.io/istio/examples-helloworld-v1 - securityContext: - runAsUser: 1337 - resources: - requests: - cpu: "100m" - imagePullPolicy: IfNotPresent #Always - ports: - - containerPort: 5000 \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/deployment-multi-service-different-ns.yaml b/pkg/config/analysis/analyzers/testdata/deployment-multi-service-different-ns.yaml deleted file mode 100644 index 6792193ce..000000000 --- a/pkg/config/analysis/analyzers/testdata/deployment-multi-service-different-ns.yaml +++ /dev/null @@ -1,77 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: bookinfo - labels: - istio-injection: "enabled" -spec: {} ---- -apiVersion: v1 -kind: Namespace -metadata: - name: bookinfo2 - labels: - istio-injection: "enabled" -spec: {} ---- -# Deployment should not generate a warning: although two services using that deployment -# using the same port, they are in different namespaces. -apiVersion: apps/v1 -kind: Deployment -metadata: - name: conflicting-ports - namespace: bookinfo - labels: - app: conflicting-ports - version: v1 -spec: - replicas: 1 - selector: - matchLabels: - app: conflicting-ports - version: v1 - template: - metadata: - labels: - app: conflicting-ports - version: v1 - spec: - serviceAccountName: bookinfo-details - containers: - - name: details - image: docker.io/istio/examples-bookinfo-details-v1:1.15.0 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9080 ---- -apiVersion: v1 -kind: Service -metadata: - name: conflicting-ports-1 - namespace: bookinfo - labels: - app: conflicting-ports -spec: - ports: - - port: 9080 - name: tcp - targetPort: 9080 - protocol: TCP - selector: - app: conflicting-ports ---- -apiVersion: v1 -kind: Service -metadata: - name: conflicting-ports-1 - namespace: bookinfo2 - labels: - app: conflicting-ports -spec: - ports: - - port: 9090 - name: http - targetPort: 9080 - protocol: HTTP - selector: - app: conflicting-ports diff --git a/pkg/config/analysis/analyzers/testdata/deployment-multi-service.yaml b/pkg/config/analysis/analyzers/testdata/deployment-multi-service.yaml deleted file mode 100644 index aca05655b..000000000 --- a/pkg/config/analysis/analyzers/testdata/deployment-multi-service.yaml +++ /dev/null @@ -1,357 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: bookinfo - labels: - istio-injection: "enabled" -spec: {} ---- -# Deployment should generate a warning: two services using that deployment -# using the same port but different protocol. -apiVersion: apps/v1 -kind: Deployment -metadata: - name: multiple-svc-multiple-prot - namespace: bookinfo - labels: - app: details - version: v1 -spec: - replicas: 1 - selector: - matchLabels: - app: details - version: v1 - template: - metadata: - labels: - app: details - version: v1 - spec: - serviceAccountName: bookinfo-details - containers: - - name: details - image: docker.io/istio/examples-bookinfo-details-v1:1.15.0 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9080 ---- -apiVersion: v1 -kind: Service -metadata: - name: details-tcp-v1 - namespace: bookinfo - labels: - app: details - service: details -spec: - ports: - - port: 9080 - name: tcp - protocol: TCP - selector: - app: details ---- -apiVersion: v1 -kind: Service -metadata: - name: details-http-v1 - namespace: bookinfo - labels: - app: details - service: details -spec: - ports: - - port: 9080 - name: http - protocol: HTTP - selector: - app: details - ---- -# Deployment should generate a warning: two services using that deployment -# using the same port but different protocol. -apiVersion: apps/v1 -kind: Deployment -metadata: - name: conflicting-ports - namespace: bookinfo - labels: - app: conflicting-ports - version: v1 -spec: - replicas: 1 - selector: - matchLabels: - app: conflicting-ports - version: v1 - template: - metadata: - labels: - app: conflicting-ports - version: v1 - spec: - serviceAccountName: bookinfo-details - containers: - - name: details - image: docker.io/istio/examples-bookinfo-details-v1:1.15.0 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9080 ---- -apiVersion: v1 -kind: Service -metadata: - name: conflicting-ports-1 - namespace: bookinfo - labels: - app: conflicting-ports -spec: - ports: - - port: 9080 - name: tcp - targetPort: 9080 - protocol: TCP - selector: - app: conflicting-ports ---- -apiVersion: v1 -kind: Service -metadata: - name: conflicting-ports-2 - namespace: bookinfo - labels: - app: conflicting-ports -spec: - ports: - - port: 9090 - name: http - targetPort: 9080 - protocol: HTTP - selector: - app: conflicting-ports ---- -# Deployment has two ports exposed, there are two services pointing to different ports. -# It shouldn't generate a warning. -apiVersion: apps/v1 -kind: Deployment -metadata: - name: reviews-v2 - namespace: bookinfo - labels: - app: reviews - version: v2 -spec: - replicas: 1 - selector: - matchLabels: - app: reviews - version: v2 - template: - metadata: - labels: - app: reviews - version: v2 - spec: - serviceAccountName: bookinfo-reviews - containers: - - name: reviews - image: docker.io/istio/examples-bookinfo-reviews-v2:1.15.0 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9080 - - name: reviews-2 - image: docker.io/istio/examples-bookinfo-reviews-v2:1.15.0 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9090 ---- -apiVersion: v1 -kind: Service -metadata: - name: reviews-http-9080 - namespace: bookinfo - labels: - app: reviews - service: reviews -spec: - ports: - - port: 9080 - name: http - protocol: HTTP - selector: - app: reviews ---- -apiVersion: v1 -kind: Service -metadata: - name: reviews-http-9090 - namespace: bookinfo - labels: - app: reviews - service: reviews -spec: - ports: - - port: 9090 - name: http - protocol: HTTP - selector: - app: reviews ---- -# Deployment has no service attached. It should generate a warning. -apiVersion: apps/v1 -kind: Deployment -metadata: - name: no-services - namespace: bookinfo - labels: - app: ratings - version: v1 -spec: - replicas: 1 - selector: - matchLabels: - app: ratings - version: v1 - template: - metadata: - labels: - app: ratings - version: v1 - spec: - serviceAccountName: bookinfo-ratings - containers: - - name: ratings - image: docker.io/istio/examples-bookinfo-ratings-v1:1.15.0 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9080 ---- -# Deployment doesn't have any container port specified and has two services using same port but different protocol. -# It should generate a warning. -apiVersion: apps/v1 -kind: Deployment -metadata: - name: multiple-without-port - namespace: bookinfo - labels: - app: productpage - version: v1 -spec: - replicas: 1 - selector: - matchLabels: - app: productpage - version: v1 - template: - metadata: - labels: - app: productpage - version: v1 - spec: - serviceAccountName: bookinfo-productpage - containers: - - name: productpage - image: docker.io/istio/examples-bookinfo-productpage-v1:1.15.0 - imagePullPolicy: IfNotPresent ---- -apiVersion: v1 -kind: Service -metadata: - name: productpage-tcp-v1 - namespace: bookinfo - labels: - app: productpage - service: productpage -spec: - ports: - - port: 9080 - name: tcp - protocol: TCP - selector: - app: productpage ---- -apiVersion: v1 -kind: Service -metadata: - name: productpage-http-v1 - namespace: bookinfo - labels: - app: productpage - service: productpage -spec: - ports: - - port: 9080 - name: http - protocol: HTTP - selector: - app: productpage ---- -# Deployment has no services attached but also is not in the service mesh. -# It shouldn't generate a warning. -apiVersion: apps/v1 -kind: Deployment -metadata: - name: deployment-out-mesh - namespace: bookinfo - labels: - app: productpage - version: v1 -spec: - replicas: 1 - selector: - matchLabels: - app: productpage - version: v1 - template: - metadata: - annotations: - sidecar.istio.io/inject: "false" - labels: - app: productpage - version: v1 - spec: - serviceAccountName: bookinfo-productpage - containers: - - name: productpage - image: docker.io/istio/examples-bookinfo-productpage-v1:1.15.0 - imagePullPolicy: IfNotPresent ---- -apiVersion: v1 -kind: Namespace -metadata: - name: injection-disabled-ns -spec: {} ---- -# Deployment has multiple service attached but using same port but different protocol. -# Sidecar is enabled although the namespaced doesn't have automatic injection. -# It should generate a warning. -apiVersion: apps/v1 -kind: Deployment -metadata: - name: ann-enabled-ns-disabled - namespace: injection-disabled-ns - labels: - app: ratings - version: v1 -spec: - replicas: 1 - selector: - matchLabels: - app: ratings - version: v1 - template: - metadata: - annotations: - sidecar.istio.io/inject: "true" - labels: - app: ratings - version: v1 - spec: - serviceAccountName: bookinfo-ratings - containers: - - name: ratings - image: docker.io/istio/examples-bookinfo-ratings-v1:1.15.0 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9080 diff --git a/pkg/config/analysis/analyzers/testdata/deployment-pod-sec-uid.yaml b/pkg/config/analysis/analyzers/testdata/deployment-pod-sec-uid.yaml deleted file mode 100644 index 7f65dd702..000000000 --- a/pkg/config/analysis/analyzers/testdata/deployment-pod-sec-uid.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: deploy-pod-sec-uid - labels: - app: helloworld - version: v1 -spec: - replicas: 1 - selector: - matchLabels: - app: helloworld - version: v1 - template: - metadata: - labels: - app: helloworld - version: v1 - spec: - securityContext: - runAsUser: 1337 - containers: - - name: helloworld - image: docker.io/istio/examples-helloworld-v1 - resources: - requests: - cpu: "100m" - imagePullPolicy: IfNotPresent #Always - ports: - - containerPort: 5000 \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/deprecation.yaml b/pkg/config/analysis/analyzers/testdata/deprecation.yaml deleted file mode 100644 index 445546005..000000000 --- a/pkg/config/analysis/analyzers/testdata/deprecation.yaml +++ /dev/null @@ -1,57 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: istio-multicluster-egressgateway - namespace: dubbo-system -spec: - # workloadLabels is deprecated - workloadLabels: - istio: egressgateway - # filters is deprecated - filters: - - listenerMatch: - portNumber: 15443 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: productpage - namespace: foo -spec: - hosts: - - productpage - http: - - fault: - delay: - percent: 50 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - name: no-selector - namespace: default -spec: - ingress: - - port: - number: 9080 - defaultEndpoint: unix:///var/run/some.sock - egress: - - hosts: - - "./*" - outboundTrafficPolicy: - mode: ALLOW_ANY - egressProxy: - host: example - port: - number: 9080 ---- -apiVersion: config.istio.io/v1alpha2 -kind: QuotaSpec -metadata: - name: request-count - namespace: dubbo-system -spec: - rules: - - quotas: - - charge: 1 - quota: requestcount diff --git a/pkg/config/analysis/analyzers/testdata/destinationrule-compound-mutual-simple.yaml b/pkg/config/analysis/analyzers/testdata/destinationrule-compound-mutual-simple.yaml deleted file mode 100644 index fb23c19be..000000000 --- a/pkg/config/analysis/analyzers/testdata/destinationrule-compound-mutual-simple.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# No caCertificates when mode is simple at destination level and MUTUAL at port level -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: db-mtls -spec: - host: mydbserver.prod.svc.cluster.local - trafficPolicy: - tls: - mode: SIMPLE - clientCertificate: /etc/certs/myclientcert.pem - privateKey: /etc/certs/client_private_key.pem - portLevelSettings: - - port: - number: 443 - tls: - mode: MUTUAL - clientCertificate: /etc/certs/myclientcert.pem - privateKey: /etc/certs/client_private_key.pem - sni: my-nginx.mesh-external.svc.cluster.local - diff --git a/pkg/config/analysis/analyzers/testdata/destinationrule-compound-simple-mutual.yaml b/pkg/config/analysis/analyzers/testdata/destinationrule-compound-simple-mutual.yaml deleted file mode 100644 index 98b912fd2..000000000 --- a/pkg/config/analysis/analyzers/testdata/destinationrule-compound-simple-mutual.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# No caCertificates when mode is simple at destination level -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: db-mtls -spec: - host: mydbserver.prod.svc.cluster.local - trafficPolicy: - tls: - mode: MUTUAL - clientCertificate: /etc/certs/myclientcert.pem - privateKey: /etc/certs/client_private_key.pem - portLevelSettings: - - port: - number: 443 - tls: - mode: SIMPLE - clientCertificate: /etc/certs/myclientcert.pem - privateKey: /etc/certs/client_private_key.pem - sni: my-nginx.mesh-external.svc.cluster.local - diff --git a/pkg/config/analysis/analyzers/testdata/destinationrule-mutual-destination.yaml b/pkg/config/analysis/analyzers/testdata/destinationrule-mutual-destination.yaml deleted file mode 100644 index 7c8d4642e..000000000 --- a/pkg/config/analysis/analyzers/testdata/destinationrule-mutual-destination.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# No caCertificates when mode is mutual at destination level -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: db-mtls -spec: - host: mydbserver.prod.svc.cluster.local - trafficPolicy: - tls: - mode: MUTUAL - clientCertificate: /etc/certs/myclientcert.pem - privateKey: /etc/certs/client_private_key.pem diff --git a/pkg/config/analysis/analyzers/testdata/destinationrule-mutual-port.yaml b/pkg/config/analysis/analyzers/testdata/destinationrule-mutual-port.yaml deleted file mode 100644 index 0bd6c4ba9..000000000 --- a/pkg/config/analysis/analyzers/testdata/destinationrule-mutual-port.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# No caCertificates when mode is mutual at port level -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: db-mtls -spec: - host: mydbserver.prod.svc.cluster.local - trafficPolicy: - portLevelSettings: - - port: - number: 443 - tls: - mode: MUTUAL - clientCertificate: /etc/certs/myclientcert.pem - privateKey: /etc/certs/client_private_key.pem - sni: my-nginx.mesh-external.svc.cluster.local diff --git a/pkg/config/analysis/analyzers/testdata/destinationrule-simple-destination.yaml b/pkg/config/analysis/analyzers/testdata/destinationrule-simple-destination.yaml deleted file mode 100644 index baad382b4..000000000 --- a/pkg/config/analysis/analyzers/testdata/destinationrule-simple-destination.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# No caCertificates when mode is simple at destination level -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: db-tls -spec: - host: mydbserver.prod.svc.cluster.local - trafficPolicy: - tls: - mode: SIMPLE - clientCertificate: /etc/certs/myclientcert.pem - privateKey: /etc/certs/client_private_key.pem diff --git a/pkg/config/analysis/analyzers/testdata/destinationrule-simple-port.yaml b/pkg/config/analysis/analyzers/testdata/destinationrule-simple-port.yaml deleted file mode 100644 index 55c61f185..000000000 --- a/pkg/config/analysis/analyzers/testdata/destinationrule-simple-port.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# No caCertificates when mode is simple at port level -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: db-tls -spec: - host: mydbserver.prod.svc.cluster.local - trafficPolicy: - portLevelSettings: - - port: - number: 443 - tls: - mode: SIMPLE - clientCertificate: /etc/certs/myclientcert.pem - privateKey: /etc/certs/client_private_key.pem - sni: my-nginx.mesh-external.svc.cluster.local diff --git a/pkg/config/analysis/analyzers/testdata/destinationrule-with-ca.yaml b/pkg/config/analysis/analyzers/testdata/destinationrule-with-ca.yaml deleted file mode 100644 index 0a20e6b90..000000000 --- a/pkg/config/analysis/analyzers/testdata/destinationrule-with-ca.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# caCertificates when mode is mutual at destination level and simple at port level -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: db-mtls -spec: - host: mydbserver.prod.svc.cluster.local - trafficPolicy: - tls: - mode: MUTUAL - clientCertificate: /etc/certs/myclientcert.pem - privateKey: /etc/certs/client_private_key.pem - caCertificates: /etc/certs/root.pem - portLevelSettings: - - port: - number: 443 - tls: - mode: SIMPLE - clientCertificate: /etc/certs/myclientcert.pem - privateKey: /etc/certs/client_private_key.pem - caCertificates: /etc/certs/root.pem - sni: my-nginx.mesh-external.svc.cluster.local diff --git a/pkg/config/analysis/analyzers/testdata/gateway-correct-port.yaml b/pkg/config/analysis/analyzers/testdata/gateway-correct-port.yaml deleted file mode 100644 index ecf428c8a..000000000 --- a/pkg/config/analysis/analyzers/testdata/gateway-correct-port.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# Gateway with bogus port -# -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: httpbin-gateway -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 80 - name: http - protocol: HTTP - hosts: - - "*" diff --git a/pkg/config/analysis/analyzers/testdata/gateway-custom-ingressgateway-badport.yaml b/pkg/config/analysis/analyzers/testdata/gateway-custom-ingressgateway-badport.yaml deleted file mode 100644 index d913944e9..000000000 --- a/pkg/config/analysis/analyzers/testdata/gateway-custom-ingressgateway-badport.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# Gateway with non-standard IngressGateway -# -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: httpbin-gateway -spec: - selector: - myapp: private-ingressgateway - servers: - - port: - number: 80 - name: http2 - protocol: HTTP - hosts: - - "*" ---- -apiVersion: v1 -kind: Pod -metadata: - labels: - myapp: private-ingressgateway - name: my-ingressgateway-1234 -spec: - containers: - - args: - name: istio-proxy ---- -apiVersion: v1 -kind: Service -metadata: - name: my-ingressgateway -spec: - ports: - - name: http2 - nodePort: 31380 - port: 8003 - protocol: TCP - targetPort: 80 - selector: - myapp: private-ingressgateway diff --git a/pkg/config/analysis/analyzers/testdata/gateway-custom-ingressgateway-svcselector.yaml b/pkg/config/analysis/analyzers/testdata/gateway-custom-ingressgateway-svcselector.yaml deleted file mode 100644 index 04837a47b..000000000 --- a/pkg/config/analysis/analyzers/testdata/gateway-custom-ingressgateway-svcselector.yaml +++ /dev/null @@ -1,80 +0,0 @@ -# Gateways for 8001 and 8002 correct matching -# -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: httpbin8001-gateway -spec: - selector: - myapp: ingressgateway-8001 - servers: - - port: - number: 8001 - name: http2 - protocol: HTTP - hosts: - - "*" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: httpbin8002-gateway -spec: - selector: - myapp: ingressgateway-8001 - servers: - - port: - number: 8002 - name: http2 - protocol: HTTP - hosts: - - "*" ---- -apiVersion: v1 -kind: Pod -metadata: - labels: - myapp: ingressgateway-8001 - name: my-ingressgateway-8001 -spec: - containers: - - args: - name: istio-proxy ---- -apiVersion: v1 -kind: Pod -metadata: - labels: - myapp: ingressgateway-8002 - name: my-ingressgateway-8002 -spec: - containers: - - args: - name: istio-proxy ---- -apiVersion: v1 -kind: Service -metadata: - name: my-8002 -spec: - ports: - - name: http2 - nodePort: 31380 - port: 8002 - protocol: TCP - targetPort: 80 - selector: - myapp: ingressgateway-8002 ---- -apiVersion: v1 -kind: Service -metadata: - name: my-8001 -spec: - ports: - - name: http2 - port: 8001 - protocol: TCP - selector: - myapp: ingressgateway-8001 - \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/gateway-custom-ingressgateway.yaml b/pkg/config/analysis/analyzers/testdata/gateway-custom-ingressgateway.yaml deleted file mode 100644 index ccfa2e439..000000000 --- a/pkg/config/analysis/analyzers/testdata/gateway-custom-ingressgateway.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# Gateway with non-standard IngressGateway -# -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: httpbin-gateway -spec: - selector: - myapp: private-ingressgateway - servers: - - port: - number: 8003 - name: http2 - protocol: HTTP - hosts: - - "*" ---- -apiVersion: v1 -kind: Pod -metadata: - labels: - myapp: private-ingressgateway - name: my-ingressgateway-1234 -spec: - containers: - - args: - name: istio-proxy ---- -apiVersion: v1 -kind: Service -metadata: - name: my-ingressgateway -spec: - ports: - - name: http2 - nodePort: 31380 - port: 8003 - protocol: TCP - targetPort: 80 - selector: - myapp: private-ingressgateway diff --git a/pkg/config/analysis/analyzers/testdata/gateway-duplicate-certificate.yaml b/pkg/config/analysis/analyzers/testdata/gateway-duplicate-certificate.yaml deleted file mode 100644 index 083352707..000000000 --- a/pkg/config/analysis/analyzers/testdata/gateway-duplicate-certificate.yaml +++ /dev/null @@ -1,366 +0,0 @@ ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: gateway-01-test-01 - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 443 - name: https - protocol: HTTPS - tls: - mode: SIMPLE - credentialName: "test-01-wildcard-cert" # validation error since have same certificate with gateway-02-test-01 - hosts: - - "01.test-01.com" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: gateway-02-test-01 - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 443 - name: https - protocol: HTTPS - tls: - mode: SIMPLE - credentialName: "test-01-wildcard-cert" # validation error since have same certificate with gateway-01-test-01 - hosts: - - "02.test-01.com" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: gateway-01-test-02 - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 443 - name: https - protocol: HTTPS - tls: - mode: SIMPLE - credentialName: "test-02-wildcard-cert" # validation error since have same certificate with gateway-01-test-02 - hosts: - - "01.test-02.com" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: gateway-02-test-02 - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - type: internal - servers: - - port: - number: 443 - name: https - protocol: HTTPS - tls: - mode: SIMPLE - credentialName: "test-02-wildcard-cert" # no validation error, because this gateway selector is not subset of other gateway selector - hosts: - - "02.test-02.com" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: gateway-01-test-03 - namespace: default -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 443 - name: https - protocol: HTTPS - tls: - mode: SIMPLE - credentialName: "test-03-wildcard-cert" # validation error, since have same certificate with gateway-02-test-03 - hosts: - - "01.test-03.com" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: gateway-02-test-03 - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - type: internal - servers: - - port: - number: 443 - name: https - protocol: HTTPS - tls: - mode: SIMPLE - credentialName: "test-03-wildcard-cert" # no validation error, because this gateway selector is not subset of other gateway selector - hosts: - - "02.test-03.com" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: gateway-01-test-04 - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 443 - name: https - protocol: HTTPS - tls: - mode: SIMPLE - credentialName: "01-test-04-wildcard-cert" # no validation error - hosts: - - "01.test-04.com" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: gateway-02-test-04 - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 443 - name: https - protocol: HTTPS - tls: - mode: SIMPLE - credentialName: "02-test-04-wildcard-cert" # no validation error - hosts: - - "02.test-04.com" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: gateway-01-test-05 - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 443 - name: https - protocol: HTTPS - tls: - mode: SIMPLE - credentialName: "01-test-05-wildcard-cert" # no validation error - hosts: - - "01.test-05.com" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: gateway-02-test-05 - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - type: internal - servers: - - port: - number: 443 - name: https - protocol: HTTPS - tls: - mode: SIMPLE - credentialName: "02-test-05-wildcard-cert" # no validation error - hosts: - - "02.test-05.com" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: gateway-01-test-06 - namespace: default -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 443 - name: https - protocol: HTTPS - tls: - mode: SIMPLE - credentialName: "01-test-06-wildcard-cert" # no validation error - hosts: - - "01.test-06.com" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: gateway-02-test-06 - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 443 - name: https - protocol: HTTPS - tls: - mode: SIMPLE - credentialName: "02-test-06-wildcard-cert" # no validation error - hosts: - - "02.test-06.com" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: gateway-03-test-06 - namespace: default -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 80 - name: http - protocol: HTTP - hosts: - - "03.test-06.com" # no validation error ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: gateway-04-test-06 - namespace: default -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 80 - name: http - protocol: HTTP - hosts: - - "04.test-06.com" # no validation error ---- -apiVersion: v1 -kind: Pod -metadata: - labels: - type: internal - istio: ingressgateway - name: internal-ingressgateway - namespace: dubbo-system -spec: - containers: - - args: - name: istio-proxy ---- -apiVersion: v1 -data: - cert: aHVzaCBodXNoIGh1c2gK - key: c2VjcmV0IHNlY3JldAo= -kind: Secret -metadata: - name: test-01-wildcard-cert - namespace: dubbo-system -type: Opaque ---- -apiVersion: v1 -data: - cert: aHVzaCBodXNoIGh1c2gK - key: c2VjcmV0IHNlY3JldAo= -kind: Secret -metadata: - name: test-02-wildcard-cert - namespace: dubbo-system -type: Opaque ---- -apiVersion: v1 -data: - cert: aHVzaCBodXNoIGh1c2gK - key: c2VjcmV0IHNlY3JldAo= -kind: Secret -metadata: - name: test-03-wildcard-cert - namespace: dubbo-system -type: Opaque ---- -apiVersion: v1 -data: - cert: aHVzaCBodXNoIGh1c2gK - key: c2VjcmV0IHNlY3JldAo= -kind: Secret -metadata: - name: 01-test-04-wildcard-cert - namespace: dubbo-system -type: Opaque ---- -apiVersion: v1 -data: - cert: aHVzaCBodXNoIGh1c2gK - key: c2VjcmV0IHNlY3JldAo= -kind: Secret -metadata: - name: 02-test-04-wildcard-cert - namespace: dubbo-system -type: Opaque ---- -apiVersion: v1 -data: - cert: aHVzaCBodXNoIGh1c2gK - key: c2VjcmV0IHNlY3JldAo= -kind: Secret -metadata: - name: 01-test-05-wildcard-cert - namespace: dubbo-system -type: Opaque ---- -apiVersion: v1 -data: - cert: aHVzaCBodXNoIGh1c2gK - key: c2VjcmV0IHNlY3JldAo= -kind: Secret -metadata: - name: 02-test-05-wildcard-cert - namespace: dubbo-system -type: Opaque ---- -apiVersion: v1 -data: - cert: aHVzaCBodXNoIGh1c2gK - key: c2VjcmV0IHNlY3JldAo= -kind: Secret -metadata: - name: 01-test-06-wildcard-cert - namespace: dubbo-system -type: Opaque ---- -apiVersion: v1 -data: - cert: aHVzaCBodXNoIGh1c2gK - key: c2VjcmV0IHNlY3JldAo= -kind: Secret -metadata: - name: 02-test-06-wildcard-cert - namespace: dubbo-system -type: Opaque diff --git a/pkg/config/analysis/analyzers/testdata/gateway-no-port.yaml b/pkg/config/analysis/analyzers/testdata/gateway-no-port.yaml deleted file mode 100644 index ade7c081c..000000000 --- a/pkg/config/analysis/analyzers/testdata/gateway-no-port.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Gateway with bogus port -# -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: httpbin-gateway -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 80 - name: http - protocol: HTTP - hosts: - - "*" - - port: - number: 8004 - name: http2 - protocol: HTTP - hosts: - - "*" diff --git a/pkg/config/analysis/analyzers/testdata/gateway-no-workload.yaml b/pkg/config/analysis/analyzers/testdata/gateway-no-workload.yaml deleted file mode 100644 index caa272f12..000000000 --- a/pkg/config/analysis/analyzers/testdata/gateway-no-workload.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Gateway with bad selector -# -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: httpbin-gateway -spec: - selector: - istio: ingresstypo diff --git a/pkg/config/analysis/analyzers/testdata/gateway-secrets.yaml b/pkg/config/analysis/analyzers/testdata/gateway-secrets.yaml deleted file mode 100644 index 498c8bcdb..000000000 --- a/pkg/config/analysis/analyzers/testdata/gateway-secrets.yaml +++ /dev/null @@ -1,105 +0,0 @@ -apiVersion: v1 -data: - cert: aHVzaCBodXNoIGh1c2gK - key: c2VjcmV0IHNlY3JldAo= -kind: Secret -metadata: - name: httpbin-credential - namespace: dubbo-system -type: Opaque ---- -apiVersion: v1 -kind: Pod -metadata: - labels: - myapp: custom-gateway - name: custom-gateway ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: defaultgateway-noerrors -spec: - selector: - istio: ingressgateway # use istio default ingress gateway, so we expect the credential in dubbo-system - servers: - - port: - number: 443 - name: https - protocol: HTTPS - tls: - mode: SIMPLE - credentialName: "httpbin-credential" # Correct credential, should not produce an error message - hosts: - - "httpbin.example.com" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: defaultgateway-bogusCredentialName -spec: - selector: - istio: ingressgateway # use istio default ingress gateway, so we expect the credential in dubbo-system - servers: - - port: - number: 443 - name: https - protocol: HTTPS - tls: - mode: SIMPLE - credentialName: "httpbin-credential-bogus" # Should break, wrong credential name - hosts: - - "httpbin.example.com" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: customgateway-wrongnamespace -spec: - selector: - istio: custom-gateway # Custom gateway - servers: - - port: - number: 443 - name: https - protocol: HTTPS - tls: - mode: SIMPLE - credentialName: "httpbin-credential" # Should break, doesn't exist in custom-gateway's namespace - hosts: - - "httpbin.example.com" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: bogusgateway -spec: - selector: - istio: custom-gateway # Nothing matches this, should generate an error - servers: - - port: - number: 443 - name: https - protocol: HTTPS - tls: - mode: SIMPLE - credentialName: "httpbin-credential" - hosts: - - "httpbin.example.com" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: defaultgateway-nocredential # No credentialName specified, we shouldn't generate any errors -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 443 - name: https - protocol: HTTPS - tls: - mode: SIMPLE - hosts: - - "httpbin.example.com" \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/image-auto.yaml b/pkg/config/analysis/analyzers/testdata/image-auto.yaml deleted file mode 100644 index e9a779d4b..000000000 --- a/pkg/config/analysis/analyzers/testdata/image-auto.yaml +++ /dev/null @@ -1,86 +0,0 @@ -# Injected namespace. -apiVersion: v1 -kind: Namespace -metadata: - name: injected - labels: - istio-injection: enabled ---- -# Non-injected namespace. -apiVersion: v1 -kind: Namespace -metadata: - name: non-injected ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - name: istio-sidecar-injector -webhooks: - - admissionReviewVersions: - - v1beta1 - clientConfig: - service: - name: fake - namespace: dubbo-system - name: namespace.sidecar-injector.istio.io - namespaceSelector: - matchLabels: - istio-injection: enabled - - admissionReviewVersions: - - v1beta1 - clientConfig: - service: - name: fake - namespace: dubbo-system - name: object.sidecar-injector.istio.io - objectSelector: - matchLabels: - sidecar.istio.io/inject: "true" ---- -# Should produce error! -apiVersion: v1 -kind: Pod -metadata: - name: injected-pod - namespace: default -spec: - containers: - - image: auto - name: istio-proxy ---- -# Not injected, should produce error! -apiVersion: apps/v1 -kind: Deployment -metadata: - name: non-injected-gateway-deployment - namespace: not-injected -spec: - selector: - matchLabels: - istio: ingressgateway - template: - metadata: - annotations: - inject.istio.io/templates: gateway - labels: - istio: ingressgateway - spec: - containers: - - name: istio-proxy - image: auto ---- -# No image auto, should not produce error! -apiVersion: v1 -kind: Pod -metadata: - name: istiod-canary-1234567890-12345 - namespace: dubbo-system - labels: - app: istiod - istio: pilot - sidecar.istio.io/inject: "true" -spec: - containers: - - image: ubuntu - name: ubuntu diff --git a/pkg/config/analysis/analyzers/testdata/incorrect-port-name-external-name-service-type.yaml b/pkg/config/analysis/analyzers/testdata/incorrect-port-name-external-name-service-type.yaml deleted file mode 100644 index 6844a2389..000000000 --- a/pkg/config/analysis/analyzers/testdata/incorrect-port-name-external-name-service-type.yaml +++ /dev/null @@ -1,40 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: nginx - namespace: nginx-ns -spec: - externalName: nginx.example.com - ports: - - name: nginx - port: 443 - protocol: TCP - targetPort: 443 - type: ExternalName ---- -apiVersion: v1 -kind: Service -metadata: - name: nginx-svc2 - namespace: nginx-ns2 -spec: - externalName: nginx.example.com - ports: - - port: 443 - protocol: TCP - targetPort: 443 - type: ExternalName ---- -apiVersion: v1 -kind: Service -metadata: - name: nginx-svc3 - namespace: nginx-ns3 -spec: - externalName: nginx.example.com - ports: - - name: tcp - port: 443 - protocol: TCP - targetPort: 443 - type: ExternalName \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/injection-with-mismatched-sidecar.yaml b/pkg/config/analysis/analyzers/testdata/injection-with-mismatched-sidecar.yaml deleted file mode 100644 index c28b71d82..000000000 --- a/pkg/config/analysis/analyzers/testdata/injection-with-mismatched-sidecar.yaml +++ /dev/null @@ -1,134 +0,0 @@ -# Namespace 'enabled-namespace' has istio injection enabled, so will be enforced. -apiVersion: v1 -kind: Namespace -metadata: - labels: - istio-injection: enabled - name: enabled-namespace ---- -# Details-v1-pod-old is out of date and should get a warning. -apiVersion: v1 -kind: Pod -metadata: - labels: - app: details - name: details-v1-pod-old - namespace: enabled-namespace -spec: - containers: - - image: docker.io/istio/examples-bookinfo-details-v1:1.15.0 - name: details - - image: docker.io/istio/proxyv2:1.3.0 - name: istio-proxy ---- -# Details-v1-pod-new is up to date, and should be ignored. -apiVersion: v1 -kind: Pod -metadata: - labels: - app: details - name: details-v1-pod-new - namespace: enabled-namespace -spec: - containers: - - image: docker.io/istio/examples-bookinfo-details-v1:1.15.0 - name: details - - image: docker.io/istio/proxyv2:1.3.1 - name: istio-proxy ---- -# details-v1-pod-overwritten-sidecar has a custom sidecar version injected and -# therefore should not match the injector version. -apiVersion: v1 -kind: Pod -metadata: - labels: - app: details - annotations: - sidecar.istio.io/proxyImage: docker.io/istio/proxyv2:1.3.0-prerelease - name: details-v1-pod-overwritten-sidecar - namespace: enabled-namespace -spec: - containers: - - image: docker.io/istio/examples-bookinfo-details-v2:1.15.0 - name: details - - image: docker.io/istio/proxyv2:1.3.0-prerelease - name: istio-proxy ---- -# Namespace 'default' does not have an explicit label, so no version checking should happen. -apiVersion: v1 -kind: Namespace -metadata: - name: default ---- -# Details-v2-pod-old is out of date, but since istio-injection is not enabled on the namespace, -# no warning occurs. -apiVersion: v1 -kind: Pod -metadata: - labels: - app: details - name: details-v2-pod-old - namespace: default -spec: - containers: - - image: docker.io/istio/examples-bookinfo-details-v2:1.15.0 - name: details - - image: docker.io/istio/proxyv2:1.3.0 - name: istio-proxy ---- -# Namespace 'revision-namespace' has istio injection enabled and label istio.io/rev=canary, so will be enforced. -apiVersion: v1 -kind: Namespace -metadata: - labels: - istio.io/rev: canary - name: revision-namespace ---- -# Revision-details-v1-pod-old is out of date and should get a warning. -apiVersion: v1 -kind: Pod -metadata: - labels: - app: details - name: revision-details-v1-pod-old - namespace: revision-namespace -spec: - containers: - - image: docker.io/istio/examples-bookinfo-details-v1:1.15.0 - name: details - - image: docker.io/istio/proxyv2:1.3.0 - name: istio-proxy ---- -# Revision-details-v1-pod-new is up to date, and should be ignored. -apiVersion: v1 -kind: Pod -metadata: - labels: - app: details - name: revision-details-v1-pod-new - namespace: revision-namespace -spec: - containers: - - image: docker.io/istio/examples-bookinfo-details-v1:1.15.0 - name: details - - image: docker.io/istio/proxyv2:1.3.1 - name: istio-proxy ---- -# revision-details-v1-pod-overwritten-sidecar has a custom sidecar version injected and -# therefore should not match the injector version. -apiVersion: v1 -kind: Pod -metadata: - labels: - app: details - annotations: - sidecar.istio.io/proxyImage: docker.io/istio/proxyv2:1.3.0-prerelease - name: revision-details-v1-pod-overwritten-sidecar - namespace: revision-namespace -spec: - containers: - - image: docker.io/istio/examples-bookinfo-details-v2:1.15.0 - name: details - - image: docker.io/istio/proxyv2:1.3.0-prerelease - name: istio-proxy ---- diff --git a/pkg/config/analysis/analyzers/testdata/injection.yaml b/pkg/config/analysis/analyzers/testdata/injection.yaml deleted file mode 100644 index cab01a74d..000000000 --- a/pkg/config/analysis/analyzers/testdata/injection.yaml +++ /dev/null @@ -1,101 +0,0 @@ -# Broken config in a yaml config file -# - -# Namespace is explicitly enabled -apiVersion: v1 -kind: Namespace -metadata: - labels: - istio-injection: enabled - name: default ---- -# Namespace is explicitly disabled, Should not generate warning! -apiVersion: v1 -kind: Namespace -metadata: - labels: - istio-injection: disabled - name: foo ---- -# Namespace doesn't have the label at all -apiVersion: v1 -kind: Namespace -metadata: - name: bar ---- -# Namespace is explicitly enabled in the new style -apiVersion: v1 -kind: Namespace -metadata: - labels: - istio.io/rev: canary - name: canary-test ---- -# Namespace is explicitly enabled in the new style, but refers to a non-existent control plane -apiVersion: v1 -kind: Namespace -metadata: - labels: - istio.io/rev: pidgeon - name: pidgeon-test ---- -# Namespace has both old and new labels -apiVersion: v1 -kind: Namespace -metadata: - name: busted - labels: - istio-injection: enabled - istio.io/rev: canary ---- -# Pod that's injected. No warning -apiVersion: v1 -kind: Pod -metadata: - name: injectedpod - namespace: default -spec: - containers: - - image: gcr.io/google-samples/microservices-demo/adservice:v0.1.1 - name: server - - image: docker.io/istio/proxyv2:1.3.0-rc.2 - name: istio-proxy ---- -# Pod that's not injected and should be. Should generate warning -apiVersion: v1 -kind: Pod -metadata: - name: noninjectedpod - namespace: default -spec: - containers: - - image: gcr.io/google-samples/microservices-demo/adservice:v0.1.1 - name: server ---- -# Pod that explicitly disables injection. Should not generate a warning. -apiVersion: v1 -kind: Pod -metadata: - name: podinjectiondisabled - namespace: default - annotations: - sidecar.istio.io/inject: "false" -spec: - containers: - - image: gcr.io/google-samples/microservices-demo/adservice:v0.1.1 - name: server ---- -# Control plane pod proving the existence of istio.io/rev 'canary' -apiVersion: v1 -kind: Pod -metadata: - name: istiod-canary-1234567890-12345 - namespace: dubbo-system - labels: - app: istiod - istio: pilot - istio.io/rev: canary -spec: - containers: - - image: gcr.io/google-samples/microservices-demo/adservice:v0.1.1 - name: server diff --git a/pkg/config/analysis/analyzers/testdata/mesh-with-automtls.yaml b/pkg/config/analysis/analyzers/testdata/mesh-with-automtls.yaml deleted file mode 100644 index cce6f43ab..000000000 --- a/pkg/config/analysis/analyzers/testdata/mesh-with-automtls.yaml +++ /dev/null @@ -1 +0,0 @@ -enableAutoMtls: true \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/misannotated.yaml b/pkg/config/analysis/analyzers/testdata/misannotated.yaml deleted file mode 100644 index 9ab735133..000000000 --- a/pkg/config/analysis/analyzers/testdata/misannotated.yaml +++ /dev/null @@ -1,119 +0,0 @@ -# K8s object with unknown Istio annotations -apiVersion: v1 -kind: Service -metadata: - name: httpbin - labels: - app: httpbin - annotations: - # no such Istio annotation - networking.istio.io/exportThree: bar - # Valid Istio annotation - networking.istio.io/exportTo: baz -spec: - ports: - - name: http - port: 8000 - targetPort: 80 - selector: - app: httpbin ---- -# K8s object with invalid Istio annotations -apiVersion: v1 -kind: Pod -metadata: - name: invalid-annotations - annotations: - # Validation checks this is an int - this should be invalid - readiness.status.sidecar.istio.io/initialDelaySeconds: bar - # Validation checks this is an int - this should be valid - readiness.status.sidecar.istio.io/periodSeconds: "10" -spec: - containers: - - name: "foo" - command: ['curl'] ---- -apiVersion: v1 -kind: Service -metadata: - name: details - labels: - app: details - annotations: - # Annotation that Istio doesn't know about, but isn't an Istio annotation, thus ignored - kubernetes.io/psp: my-privileged-psp -spec: - ports: - - name: http - port: 80 - targetPort: 9080 - selector: - app: details ---- -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - name: gateway - annotations: - # Not valid, should be kubernetes.io/ingress.class, the only non-istio.io annotation we document - istio.io/ingress.class: "istio" -spec: - rules: - - http: - paths: - - path: /productpage - backend: - serviceName: productpage - servicePort: 8080 ---- -apiVersion: v1 -kind: Pod -metadata: - name: grafana-test - labels: - app: grafana-test - istio: grafana - annotations: - # valid here - sidecar.istio.io/inject: "false" - # analyzer ignores, not ours - helm.sh/hook: test-success - # invalid here - kubernetes.io/ingress.class: "istio" -spec: - containers: - - name: "grafana-test" - command: ['curl'] - args: ['http://grafana:{{ .Values.grafana.service.externalPort }}'] - restartPolicy: Never ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: fortio-deploy - annotations: - # This annotation belongs on the spec/template so it can be applied to a pod, not here - sidecar.istio.io/statsInclusionPrefixes: cluster.outbound -spec: - replicas: 1 - selector: - matchLabels: - app: fortio - template: - metadata: - annotations: - # sidecar.istio.io/statsInclusionPrefixes should be here - labels: - app: fortio - spec: - containers: - - name: fortio ---- -apiVersion: v1 -kind: Namespace -metadata: - name: staging - annotations: - # Sidecar injector annotation does not belong to Namespace - sidecar.istio.io/inject: "true" -spec: {} \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/mtls-exports.yaml b/pkg/config/analysis/analyzers/testdata/mtls-exports.yaml deleted file mode 100644 index e9c77b4fb..000000000 --- a/pkg/config/analysis/analyzers/testdata/mtls-exports.yaml +++ /dev/null @@ -1,109 +0,0 @@ -# We have two namespaces. A service in primary has MTLS specified and a DR, but -# its not exported. This should cause traffic from secondary to fail. -# Note that we also have a service in secondary with an identical DR, but its -# exported publicly so no problems occur. -apiVersion: v1 -kind: Namespace -metadata: - name: primary ---- -apiVersion: v1 -kind: Namespace -metadata: - name: secondary ---- -apiVersion: v1 -kind: Service -metadata: - name: myservice - namespace: primary -spec: - selector: - app: myservice - ports: - - protocol: TCP - port: 80 - targetPort: 8080 ---- -apiVersion: v1 -kind: Pod -metadata: - name: myservice-pod - namespace: primary - labels: - app: myservice -spec: - containers: - - name: istio-proxy ---- -apiVersion: authentication.istio.io/v1alpha1 -kind: Policy -metadata: - name: default - namespace: primary -spec: - targets: - - name: myservice - peers: - - mtls: {} ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: default - namespace: primary -spec: - host: myservice.primary.svc.cluster.local - trafficPolicy: - tls: - mode: ISTIO_MUTUAL - exportTo: - - "." ---- -apiVersion: v1 -kind: Service -metadata: - name: myotherservice - namespace: secondary -spec: - selector: - app: myotherservice - ports: - - protocol: TCP - port: 80 - targetPort: 8080 ---- -apiVersion: v1 -kind: Pod -metadata: - name: myotherservice-pod - namespace: secondary - labels: - app: myotherservice -spec: - containers: - - name: istio-proxy ---- -apiVersion: authentication.istio.io/v1alpha1 -kind: Policy -metadata: - name: default - namespace: secondary -spec: - targets: - - name: myotherservice - peers: - - mtls: {} ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: default - namespace: secondary -spec: - host: myotherservice.secondary.svc.cluster.local - trafficPolicy: - tls: - mode: ISTIO_MUTUAL - exportTo: - - "*" \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/mtls-global-dr-no-meshpolicy.yaml b/pkg/config/analysis/analyzers/testdata/mtls-global-dr-no-meshpolicy.yaml deleted file mode 100644 index e779a0dc4..000000000 --- a/pkg/config/analysis/analyzers/testdata/mtls-global-dr-no-meshpolicy.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# A rule for all traffic specifies MTLS, but we don't have a global mesh policy. -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: default - namespace: dubbo-system -spec: - host: "*.local" - trafficPolicy: - tls: - mode: ISTIO_MUTUAL ---- \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/mtls-ignores-istio-control-plane.yaml b/pkg/config/analysis/analyzers/testdata/mtls-ignores-istio-control-plane.yaml deleted file mode 100644 index a5b0ecdb8..000000000 --- a/pkg/config/analysis/analyzers/testdata/mtls-ignores-istio-control-plane.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Within the dubbo-system namespace, we have a control plane component with a DR -# that would normally violate policy. However the istio control plane is -# not controlled via Policy declarations, and is therefore exempt. We don't -# expect the validator to complain about this either. -apiVersion: authentication.istio.io/v1alpha1 -kind: MeshPolicy -metadata: - name: default -spec: - peers: - - mtls: {} ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: default - namespace: dubbo-system -spec: - host: "*.local" - trafficPolicy: - tls: - mode: ISTIO_MUTUAL diff --git a/pkg/config/analysis/analyzers/testdata/mtls-ignores-system-namespaces.yaml b/pkg/config/analysis/analyzers/testdata/mtls-ignores-system-namespaces.yaml deleted file mode 100644 index c9f287cd0..000000000 --- a/pkg/config/analysis/analyzers/testdata/mtls-ignores-system-namespaces.yaml +++ /dev/null @@ -1,42 +0,0 @@ -# kube-system is an explicitly ignored namespace, and should not generate any errors -# even if it would otherwise -apiVersion: authentication.istio.io/v1alpha1 -kind: MeshPolicy -metadata: - name: default -spec: - peers: - - mtls: {} ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: default - namespace: dubbo-system -spec: - host: "*.local" - trafficPolicy: - tls: - mode: ISTIO_MUTUAL ---- -apiVersion: v1 -kind: Service -metadata: - name: kube-dns - namespace: kube-system -spec: - clusterIP: 10.31.240.10 - ports: - - name: dns - port: 53 - protocol: UDP - targetPort: 53 - - name: dns-tcp - port: 53 - protocol: TCP - targetPort: 53 - selector: - k8s-app: kube-dns - sessionAffinity: None - type: ClusterIP - diff --git a/pkg/config/analysis/analyzers/testdata/mtls-meshpolicy-permissive.yaml b/pkg/config/analysis/analyzers/testdata/mtls-meshpolicy-permissive.yaml deleted file mode 100644 index f823ddbaf..000000000 --- a/pkg/config/analysis/analyzers/testdata/mtls-meshpolicy-permissive.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# A global mesh policy with no DRs should not cause an error when in permissive mode. -apiVersion: authentication.istio.io/v1alpha1 -kind: MeshPolicy -metadata: - name: default -spec: - peers: - - mtls: - mode: PERMISSIVE ---- \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/mtls-meshpolicy.yaml b/pkg/config/analysis/analyzers/testdata/mtls-meshpolicy.yaml deleted file mode 100644 index bbf044022..000000000 --- a/pkg/config/analysis/analyzers/testdata/mtls-meshpolicy.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# A global mesh policy with no DRs should cause an error -# Require mTLS for our service -apiVersion: authentication.istio.io/v1alpha1 -kind: MeshPolicy -metadata: - name: default -spec: - peers: - - mtls: {} ---- \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/mtls-no-dr.yaml b/pkg/config/analysis/analyzers/testdata/mtls-no-dr.yaml deleted file mode 100644 index 8f0cfdb6b..000000000 --- a/pkg/config/analysis/analyzers/testdata/mtls-no-dr.yaml +++ /dev/null @@ -1,97 +0,0 @@ -# A service exists called missing-dr-service -apiVersion: v1 -kind: Service -metadata: - name: missing-dr-service - namespace: missing-dr -spec: - selector: - app: missing-dr-service - ports: - - protocol: TCP - port: 80 - targetPort: 8080 ---- -apiVersion: v1 -kind: Pod -metadata: - name: missing-dr-service-pod - namespace: missing-dr - labels: - app: missing-dr-service -spec: - containers: - - name: istio-proxy ---- -# Require mTLS for our service -apiVersion: authentication.istio.io/v1alpha1 -kind: Policy -metadata: - name: default - namespace: missing-dr -spec: - targets: - - name: missing-dr-service - peers: - - mtls: {} -# Without a DR, this will fail. ---- -apiVersion: v1 -kind: Service -metadata: - name: has-dr-service - namespace: has-dr -spec: - selector: - app: has-dr-service - ports: - - protocol: TCP - port: 80 - targetPort: 8080 ---- -apiVersion: v1 -kind: Pod -metadata: - name: has-dr-service-pod - namespace: has-dr - labels: - app: has-dr-service -spec: - containers: - - name: istio-proxy ---- -# We also specify another mtls for another service, but this one has a DR. -apiVersion: authentication.istio.io/v1alpha1 -kind: Policy -metadata: - name: default - namespace: has-dr -spec: - targets: - - name: has-dr-service - peers: - - mtls: {} ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: default - namespace: has-dr -spec: - host: has-dr-service.has-dr.svc.cluster.local - trafficPolicy: - tls: - mode: ISTIO_MUTUAL ---- -# We also add a DR explicitly in here for missing-dr-service to ensure we only -# see one error message. -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: missing-dr-service-dr - namespace: has-dr -spec: - host: missing-dr-service.missing-dr.svc.cluster.local - trafficPolicy: - tls: - mode: ISTIO_MUTUAL \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/mtls-no-policy.yaml b/pkg/config/analysis/analyzers/testdata/mtls-no-policy.yaml deleted file mode 100644 index 0fdacfde8..000000000 --- a/pkg/config/analysis/analyzers/testdata/mtls-no-policy.yaml +++ /dev/null @@ -1,38 +0,0 @@ -# We have a DR for our service, but no policy -# A service exists called missing-dr-service -apiVersion: v1 -kind: Service -metadata: - name: missing-policy-service - namespace: no-policy -spec: - selector: - app: missing-policy-service - ports: - - protocol: TCP - port: 80 - targetPort: 8080 ---- -apiVersion: v1 -kind: Pod -metadata: - name: missing-policy-service-pod - namespace: no-policy - labels: - app: missing-policy-service -spec: - containers: - - name: istio-proxy ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: no-policy-service-dr - namespace: no-policy -spec: - host: missing-policy-service.no-policy.svc.cluster.local - trafficPolicy: - tls: - mode: ISTIO_MUTUAL -# Nothing else specified. Without a Policy, this will fail. ---- diff --git a/pkg/config/analysis/analyzers/testdata/mtls-no-sidecar.yaml b/pkg/config/analysis/analyzers/testdata/mtls-no-sidecar.yaml deleted file mode 100644 index d01afb28e..000000000 --- a/pkg/config/analysis/analyzers/testdata/mtls-no-sidecar.yaml +++ /dev/null @@ -1,108 +0,0 @@ -# We have a global mesh policy, and a global DR to use mTLS. We also have a DR -# to override that for a specific service with no sidecar. This shouldn't cause -# any errors. -apiVersion: authentication.istio.io/v1alpha1 -kind: MeshPolicy -metadata: - name: default -spec: - peers: - - mtls: {} ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: default - namespace: dubbo-system -spec: - host: "*.local" - trafficPolicy: - tls: - mode: ISTIO_MUTUAL ---- -apiVersion: v1 -kind: Service -metadata: - name: missing-sidecar-service - namespace: no-sidecar -spec: - selector: - app: missing-sidecar-service - ports: - - protocol: TCP - port: 80 - targetPort: 8080 ---- -apiVersion: v1 -kind: Pod -metadata: - name: missing-sidecar-service-pod - namespace: no-sidecar - labels: - app: missing-sidecar-service -spec: - containers: - - name: some-other-container-not-the-proxy ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: no-sidecar-service-dr - namespace: dubbo-system -spec: - host: missing-sidecar-service.no-sidecar.svc.cluster.local - trafficPolicy: - tls: - mode: DISABLE ---- -# Also handle the case where a service exists with no pods. Assume it has no -# sidecar. -apiVersion: v1 -kind: Service -metadata: - name: missing-pod-service - namespace: no-sidecar -spec: - selector: - app: missing-pod-service - ports: - - protocol: TCP - port: 80 - targetPort: 8080 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: missing-pod-service-dr - namespace: dubbo-system -spec: - host: missing-pod-service.no-sidecar.svc.cluster.local - trafficPolicy: - tls: - mode: DISABLE ---- -# Finally, include a service with no sidecar and no policy disabling mTLS. We -# should generate a warning for that. -apiVersion: v1 -kind: Service -metadata: - name: no-sidecar-or-dr-service - namespace: no-sidecar -spec: - selector: - app: no-sidecar-or-dr-service - ports: - - protocol: TCP - port: 80 - targetPort: 8080 ---- -apiVersion: v1 -kind: Pod -metadata: - name: no-sidecar-or-dr-service-pod - namespace: no-sidecar - labels: - app: no-sidecar-or-dr-service -spec: - containers: - - name: some-other-container-not-the-proxy \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/mtls-with-port.yaml b/pkg/config/analysis/analyzers/testdata/mtls-with-port.yaml deleted file mode 100644 index 160c53b0c..000000000 --- a/pkg/config/analysis/analyzers/testdata/mtls-with-port.yaml +++ /dev/null @@ -1,62 +0,0 @@ -# Our service has three secure mtls ports, but the DR only specifies one. Note -# that one of the secure ports is a UDP port - we ignore those, so the analyzer -# should only produce one message. -apiVersion: authentication.istio.io/v1alpha1 -kind: Policy -metadata: - name: default - namespace: my-namespace -spec: - targets: - - name: my-service - ports: - - number: 8080 - - number: 8081 - - number: 8082 - peers: - - mtls: ---- -apiVersion: v1 -kind: Service -metadata: - name: my-service - namespace: my-namespace -spec: - selector: - app: my-service - ports: - - protocol: TCP - port: 8080 - targetPort: 8080 - - protocol: TCP - port: 8081 - targetPort: 8081 - - protocol: UDP - port: 8082 - targetPort: 8082 ---- -apiVersion: v1 -kind: Pod -metadata: - name: my-service-pod - namespace: my-namespace - labels: - app: my-service -spec: - containers: - - name: istio-proxy ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: default - namespace: my-namespace -spec: - host: my-service.my-namespace.svc.cluster.local - trafficPolicy: - portLevelSettings: - - port: - number: 8080 - tls: - mode: ISTIO_MUTUAL ---- \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/multicluster-unknown-serviceregistry.yaml b/pkg/config/analysis/analyzers/testdata/multicluster-unknown-serviceregistry.yaml deleted file mode 100644 index 6285be686..000000000 --- a/pkg/config/analysis/analyzers/testdata/multicluster-unknown-serviceregistry.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - labels: - istio/multiCluster: "true" - name: istio-remote-secret-istio-remote - namespace: dubbo-system -stringData: - istio-remote: | - apiVersion: v1 - clusters: - - cluster: - certificate-authority-data: Sis3WWhtcHNI - server: https://1.2.3.4 - name: gke_istio-test_us-central1-f_istio-remote - contexts: - - context: - cluster: gke_istio-test_us-central1-f_istio-remote - user: gke_istio-test_us-central1-f_istio-remote - name: gke_istio-test_us-central1-f_istio-remote - current-context: gke_istio-test_us-central1-f_istio-remote - kind: Config - preferences: {} - users: - - name: gke_istio-test_us-central1-f_istio-remote - user: - token: zLmlvL3NlcnZ diff --git a/pkg/config/analysis/analyzers/testdata/peerauthentication-crd.yaml b/pkg/config/analysis/analyzers/testdata/peerauthentication-crd.yaml deleted file mode 100644 index ade7b6fab..000000000 --- a/pkg/config/analysis/analyzers/testdata/peerauthentication-crd.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: peerauthentications.security.istio.io -spec: \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/pod-con-sec-uid.yaml b/pkg/config/analysis/analyzers/testdata/pod-con-sec-uid.yaml deleted file mode 100644 index cbe8cff15..000000000 --- a/pkg/config/analysis/analyzers/testdata/pod-con-sec-uid.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: con-sec-uid - labels: - app: helloworld - version: v1 -spec: - containers: - - name: helloworld - image: docker.io/istio/examples-helloworld-v1 - securityContext: - runAsUser: 1337 - resources: - requests: - cpu: "100m" - imagePullPolicy: IfNotPresent #Always - ports: - - containerPort: 5000 \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/pod-sec-uid.yaml b/pkg/config/analysis/analyzers/testdata/pod-sec-uid.yaml deleted file mode 100644 index 3c1f198f4..000000000 --- a/pkg/config/analysis/analyzers/testdata/pod-sec-uid.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: pod-sec-uid - labels: - app: helloworld - version: v2 -spec: - securityContext: - runAsUser: 1337 - containers: - - name: helloworld - image: docker.io/istio/examples-helloworld-v2 - resources: - requests: - cpu: "100m" - imagePullPolicy: IfNotPresent #Always - ports: - - containerPort: 5000 diff --git a/pkg/config/analysis/analyzers/testdata/relative-envoy-filter-operation.yaml b/pkg/config/analysis/analyzers/testdata/relative-envoy-filter-operation.yaml deleted file mode 100644 index e56673436..000000000 --- a/pkg/config/analysis/analyzers/testdata/relative-envoy-filter-operation.yaml +++ /dev/null @@ -1,129 +0,0 @@ -# If the patch operation is INSERT_FIRST or priority is set, then the analyzer will not do anything -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: test-reviews-lua-1 - namespace: bookinfo -spec: - workloadSelector: - labels: - app: reviews - configPatches: - # The first patch adds the lua filter to the listener/http connection manager - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - listener: - portNumber: 8080 - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: # lua filter specification - name: envoy.lua - typed_config: - "@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua" - inlineCode: | - function envoy_on_request(request_handle) - -- Make an HTTP call to an upstream host with the following headers, body, and timeout. - local headers, body = request_handle:httpCall( - "lua_cluster", - { - [":method"] = "POST", - [":path"] = "/acl", - [":authority"] = "internal.org.net" - }, - "authorize call", - 5000) - end - # The second patch adds the cluster that is referenced by the lua code - # cds match is omitted as a new cluster is being added - - applyTo: CLUSTER - match: - context: SIDECAR_OUTBOUND - patch: - operation: ADD - value: # cluster specification - name: "lua_cluster" - type: STRICT_DNS - connect_timeout: 0.5s - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: lua_cluster - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - protocol: TCP - address: "internal.org.net" - port_value: 8888 - ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: test-reviews-lua-2 - namespace: bookinfo -spec: - workloadSelector: - labels: - app: reviews - configPatches: - # The first patch adds the lua filter to the listener/http connection manager - - applyTo: HTTP_FILTER - match: - context: SIDECAR_INBOUND - listener: - portNumber: 8080 - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_AFTER - value: # lua filter specification - name: envoy.lua - typed_config: - "@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua" - inlineCode: | - function envoy_on_request(request_handle) - -- Make an HTTP call to an upstream host with the following headers, body, and timeout. - local headers, body = request_handle:httpCall( - "lua_cluster", - { - [":method"] = "POST", - [":path"] = "/acl", - [":authority"] = "internal.org.net" - }, - "authorize call", - 5000) - end - # The second patch adds the cluster that is referenced by the lua code - # cds match is omitted as a new cluster is being added - - applyTo: CLUSTER - match: - context: SIDECAR_OUTBOUND - patch: - operation: ADD - value: # cluster specification - name: "lua_cluster" - type: STRICT_DNS - connect_timeout: 0.5s - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: lua_cluster - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - protocol: TCP - address: "internal.org.net" - port_value: 8888 - - diff --git a/pkg/config/analysis/analyzers/testdata/service-no-port-name-system-namespace.yaml b/pkg/config/analysis/analyzers/testdata/service-no-port-name-system-namespace.yaml deleted file mode 100644 index 34d9d873a..000000000 --- a/pkg/config/analysis/analyzers/testdata/service-no-port-name-system-namespace.yaml +++ /dev/null @@ -1,63 +0,0 @@ -# If port is unnamed or port name doesn't follow [-], the analyzer will report warning. -# If the service is in system namespace, i.e., kube-system, dubbo-system, kube-public, the check will be skipped. -apiVersion: v1 -kind: Service -metadata: - name: my-service1 - namespace: kube-system # Skipped because it's in a kube system namespace -spec: - selector: - app: my-service1 - ports: - - protocol: TCP - port: 8080 - targetPort: 8080 - - protocol: TCP - port: 8081 - targetPort: 8081 ---- -apiVersion: v1 -kind: Service -metadata: - name: my-service2 # Skipped because it has an istio: label - namespace: dubbo-system - labels: - istio: xxx -spec: - selector: - app: my-service2 - ports: - - name: foo - protocol: TCP - port: 8080 - targetPort: 8080 ---- -apiVersion: v1 -kind: Service -metadata: - name: my-service3 # Skipped because it's in a kube system namespace - namespace: kube-public -spec: - selector: - app: my-service3 - ports: - - name: bar - protocol: TCP - port: 8080 - targetPort: 8080 ---- -apiVersion: v1 -kind: Service -metadata: - name: my-service4 # Skipped because of the release:istio label - namespace: dubbo-system - labels: - release: istio -spec: - selector: - app: my-service4 - ports: - - name: foo - protocol: TCP - port: 8080 - targetPort: 8080 diff --git a/pkg/config/analysis/analyzers/testdata/service-no-port-name.yaml b/pkg/config/analysis/analyzers/testdata/service-no-port-name.yaml deleted file mode 100644 index 1ca0c3998..000000000 --- a/pkg/config/analysis/analyzers/testdata/service-no-port-name.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# If port is unnamed or port name doesn't follow [-], the analyzer will report warning. -apiVersion: v1 -kind: Service -metadata: - name: my-service1 - namespace: my-namespace1 -spec: - selector: - app: my-service1 - ports: - - protocol: TCP - port: 8080 - targetPort: 8080 - - protocol: TCP - port: 8081 - targetPort: 8081 ---- -apiVersion: v1 -kind: Service -metadata: - name: my-service2 - namespace: my-namespace2 -spec: - selector: - app: my-service2 - ports: - - name: foo - protocol: TCP - port: 8080 - targetPort: 8080 diff --git a/pkg/config/analysis/analyzers/testdata/service-port-name.yaml b/pkg/config/analysis/analyzers/testdata/service-port-name.yaml deleted file mode 100644 index 76e5142c9..000000000 --- a/pkg/config/analysis/analyzers/testdata/service-port-name.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# If port is unnamed or port name doesn't follow [-], the analyzer will report warning. -apiVersion: v1 -kind: Service -metadata: - name: my-service - namespace: my-namespace -spec: - selector: - app: my-service - ports: - - name: tcp-foo - protocol: TCP - port: 8080 - targetPort: 8080 \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/serviceentry-missing-addresses-protocol.yaml b/pkg/config/analysis/analyzers/testdata/serviceentry-missing-addresses-protocol.yaml deleted file mode 100644 index 1abb37b37..000000000 --- a/pkg/config/analysis/analyzers/testdata/serviceentry-missing-addresses-protocol.yaml +++ /dev/null @@ -1,121 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-entry-test-01 # Expected: no validation error - namespace: default -spec: - hosts: - - 'istio.io' - exportTo: - - "." - ports: - - number: 443 - name: https - protocol: HTTPS - location: MESH_EXTERNAL - resolution: DNS ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-entry-test-02 # Expected: no validation error - namespace: default -spec: - hosts: - - 'istio.io' - addresses: - - 10.0.0.1 - exportTo: - - "." - ports: - - number: 443 - name: https - protocol: HTTPS - location: MESH_EXTERNAL - resolution: DNS ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-entry-test-03 # Expected: validation error missing addresses and protocol - namespace: default -spec: - hosts: - - 'istio.io' - exportTo: - - "." - ports: - - number: 443 - name: https - location: MESH_EXTERNAL - resolution: DNS ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-entry-test-04 # Expected: validation error missing addresses and protocol - namespace: default -spec: - hosts: - - 'istio.io' - exportTo: - - "." - ports: - - number: 443 - name: https - protocol: "" - location: MESH_EXTERNAL - resolution: DNS ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-entry-test-05 # Expected: no validation error - namespace: default -spec: - hosts: - - 'istio.io' - exportTo: - - "." - ports: - - number: 443 - name: https - protocol: HTTPS - location: MESH_EXTERNAL - resolution: DNS ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-entry-test-06 # Expected: no validation error - namespace: default -spec: - hosts: - - 'istio.io' - addresses: - - 10.0.0.1 - exportTo: - - "." - ports: - - number: 443 - name: https - protocol: TCP - location: MESH_EXTERNAL - resolution: DNS ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-entry-test-07 # Expected: validation error missing addresses with protocol TCP - namespace: default -spec: - hosts: - - 'istio.io' - exportTo: - - "." - ports: - - number: 443 - name: https - protocol: TCP - location: MESH_EXTERNAL - resolution: DNS diff --git a/pkg/config/analysis/analyzers/testdata/sidecar-default-selector.yaml b/pkg/config/analysis/analyzers/testdata/sidecar-default-selector.yaml deleted file mode 100644 index bb7d9f5fc..000000000 --- a/pkg/config/analysis/analyzers/testdata/sidecar-default-selector.yaml +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - name: no-selector # Since this is the only Sidecar in the namespace without a workload selector, no conflict - namespace: ns1 -spec: - egress: - - hosts: - - "./*" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - name: has-selector - namespace: ns1 -spec: - workloadSelector: # Since this has a workload selector, it shouldn't conflict with the other Sidecar in the namespace - labels: - app: foo - egress: - - hosts: - - "./*" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - name: has-conflict-1 # Both Sidecars in this namespace omit workload selector, so they are in conflict - namespace: ns2 -spec: - egress: - - hosts: - - "./*" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - name: has-conflict-2 # Both Sidecars in this namespace omit workload selector, so they are in conflict - namespace: ns2 -spec: - egress: - - hosts: - - "./*" \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/sidecar-injector-configmap-absolute-override.yaml b/pkg/config/analysis/analyzers/testdata/sidecar-injector-configmap-absolute-override.yaml deleted file mode 100644 index 8464bdfb3..000000000 --- a/pkg/config/analysis/analyzers/testdata/sidecar-injector-configmap-absolute-override.yaml +++ /dev/null @@ -1,418 +0,0 @@ -apiVersion: v1 -data: - config: |- - policy: enabled - alwaysInjectSelector: - [] - neverInjectSelector: - [] - template: | - rewriteAppHTTPProbe: {{ valueOrDefault .Values.sidecarInjectorWebhook.rewriteAppHTTPProbe false }} - {{- if or (not .Values.istio_cni.enabled) .Values.global.proxy.enableCoreDump }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{- if not .Values.istio_cni.enabled }} - - name: istio-init - {{- if contains "/" .Values.global.proxy_init.image }} - image: "{{ .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .Values.global.hub }}/{{ .Values.global.proxy_init.image }}:{{ .Values.global.tag }}" - {{- end }} - command: - - istio-iptables - - "-p" - - 15001 - - "-z" - - "15006" - - "-u" - - 1337 - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` `*` }}" - - "-d" - - "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - imagePullPolicy: "{{ valueOrDefault .Values.global.imagePullPolicy `Always` }}" - {{- if .Values.global.proxy_init.resources }} - resources: - {{ toYaml .Values.global.proxy_init.resources | indent 4 }} - {{- else }} - resources: {} - {{- end }} - securityContext: - runAsUser: 0 - runAsNonRoot: false - capabilities: - add: - - NET_ADMIN - {{- if .Values.global.proxy.privileged }} - privileged: true - {{- end }} - restartPolicy: Always - {{- end }} - {{ end -}} - {{- if eq .Values.global.proxy.enableCoreDump true }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" .Values.global.proxy_init.image }} - image: "{{ .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .Values.global.hub }}/{{ .Values.global.proxy_init.image }}:{{ .Values.global.tag }}" - {{- end }} - imagePullPolicy: "{{ valueOrDefault .Values.global.imagePullPolicy `Always` }}" - resources: {} - securityContext: - runAsUser: 0 - runAsNonRoot: false - privileged: true - {{ end }} - {{- end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .Values.global.hub }}/{{ .Values.global.proxy.image }}:{{ .Values.global.tag }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --configPath - - "/etc/istio/proxy" - - --binaryPath - - "/usr/local/bin/envoy" - - --serviceCluster - {{ if ne "" (index .ObjectMeta.Labels "app") -}} - - "{{ index .ObjectMeta.Labels `app` }}.$(POD_NAMESPACE)" - {{ else -}} - - "{{ valueOrDefault .DeploymentMeta.Name `istio-proxy` }}.{{ valueOrDefault .DeploymentMeta.Namespace `default` }}" - {{ end -}} - - --drainDuration - - "{{ formatDuration .ProxyConfig.DrainDuration }}" - - --parentShutdownDuration - - "{{ formatDuration .ProxyConfig.ParentShutdownDuration }}" - - --discoveryAddress - - "{{ annotation .ObjectMeta `sidecar.istio.io/discoveryAddress` .ProxyConfig.DiscoveryAddress }}" - {{- if eq .Values.global.proxy.tracer "lightstep" }} - - --lightstepAddress - - "{{ .ProxyConfig.GetTracing.GetLightstep.GetAddress }}" - - --lightstepAccessToken - - "{{ .ProxyConfig.GetTracing.GetLightstep.GetAccessToken }}" - - --lightstepSecure={{ .ProxyConfig.GetTracing.GetLightstep.GetSecure }} - - --lightstepCacertPath - - "{{ .ProxyConfig.GetTracing.GetLightstep.GetCacertPath }}" - {{- else if eq .Values.global.proxy.tracer "zipkin" }} - - --zipkinAddress - - "{{ .ProxyConfig.GetTracing.GetZipkin.GetAddress }}" - {{- else if eq .Values.global.proxy.tracer "datadog" }} - - --datadogAgentAddress - - "{{ .ProxyConfig.GetTracing.GetDatadog.GetAddress }}" - {{- end }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel}} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel}} - - --connectTimeout - - "{{ formatDuration .ProxyConfig.ConnectTimeout }}" - {{- if .Values.global.proxy.envoyStatsd.enabled }} - - --statsdUdpAddress - - "{{ .ProxyConfig.StatsdUdpAddress }}" - {{- end }} - {{- if .Values.global.proxy.envoyMetricsService.enabled }} - - --envoyMetricsServiceAddress - - "{{ .ProxyConfig.GetEnvoyMetricsService.GetAddress }}" - {{- end }} - {{- if .Values.global.proxy.envoyAccessLogService.enabled }} - - --envoyAccessLogServiceAddress - - "{{ .ProxyConfig.GetEnvoyAccessLogService.GetAddress }}" - {{- end }} - - --proxyAdminPort - - "{{ .ProxyConfig.ProxyAdminPort }}" - {{ if gt .ProxyConfig.Concurrency 0 -}} - - --concurrency - - "{{ .ProxyConfig.Concurrency }}" - {{ end -}} - {{- if .Values.global.controlPlaneSecurityEnabled }} - - --controlPlaneAuthPolicy - - MUTUAL_TLS - {{- else }} - - --controlPlaneAuthPolicy - - NONE - {{- end }} - - --dnsRefreshRate - - {{ valueOrDefault .Values.global.proxy.dnsRefreshRate "300s" }} - {{- if (ne (annotation .ObjectMeta "status.sidecar.istio.io/port" .Values.global.proxy.statusPort) "0") }} - - --statusPort - - "{{ annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort }}" - - --applicationPorts - - "{{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/applicationPorts` (applicationPorts .Spec.Containers) }}" - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - --templateFile=/etc/istio/custom-bootstrap/envoy_bootstrap.json - {{- end }} - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - {{- if eq .Values.global.proxy.tracer "datadog" }} - {{- if isset .ObjectMeta.Annotations `apm.datadoghq.com/env` }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- end }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: ISTIO_META_CONFIG_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: SDS_ENABLED - value: "{{ .Values.global.sds.enabled }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - - name: ISTIO_META_INCLUDE_INBOUND_PORTS - value: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` (applicationPorts .Spec.Containers) }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{ if .ObjectMeta.Labels }} - - name: ISTIO_METAJSON_LABELS - value: | - {{ toJSON .ObjectMeta.Labels }} - {{ end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: {{ .DeploymentMeta.Name }} - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.sds.customTokenDirectory }} - - name: ISTIO_META_SDS_TOKEN_PATH - value: "{{ .Values.global.sds.customTokenDirectory -}}/sdstoken" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if .Values.global.trustDomain }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.trustDomain }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - imagePullPolicy: "{{ valueOrDefault .Values.global.imagePullPolicy `Always` }}" - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if .Values.global.proxy.privileged }} - privileged: true - {{- end }} - {{- if ne .Values.global.proxy.enableCoreDump true }} - readOnlyRootFilesystem: true - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - capabilities: - add: - - NET_ADMIN - runAsGroup: 1337 - {{ else -}} - {{ if .Values.global.sds.enabled }} - runAsGroup: 1337 - {{- end }} - runAsUser: 1337 - {{- end }} - resources: - {{ if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end}} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{ else -}} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 4 }} - {{- end }} - {{ end -}} - volumeMounts: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if .Values.global.sds.enabled }} - - mountPath: /var/run/sds - name: sds-uds-path - readOnly: true - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- if .Values.global.sds.customTokenDirectory }} - - mountPath: "{{ .Values.global.sds.customTokenDirectory -}}" - name: custom-sds-token - readOnly: true - {{- end }} - {{- else }} - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .Values.global.tracer.lightstep.cacertPath }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetLightstep.GetCacertPath }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{- end }} - volumes: - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - - emptyDir: - medium: Memory - name: istio-envoy - {{- if .Values.global.sds.enabled }} - - name: sds-uds-path - hostPath: - path: /var/run/sds - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- if .Values.global.sds.customTokenDirectory }} - - name: custom-sds-token - secret: - secretName: sdstokensecret - {{- end }} - {{- else }} - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 2 }} - {{ end }} - {{ end }} - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .Values.global.tracer.lightstep.cacertPath }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.podDNSSearchNamespaces }} - dnsConfig: - searches: - {{- range .Values.global.podDNSSearchNamespaces }} - - {{ render . }} - {{- end }} - {{- end }} - injectedAnnotations: - values: '{"certmanager":{"enabled":false,"hub":"quay.io/jetstack","image":"cert-manager-controller","namespace":"dubbo-system","tag":"v0.6.2"},"cni":{"namespace":"dubbo-system"},"galley":{"enableAnalysis":false,"enabled":true,"image":"galley","namespace":"dubbo-system"},"gateways":{"istio-egressgateway":{"autoscaleEnabled":true,"enabled":false,"namespace":"dubbo-system","ports":[{"name":"http2","port":80},{"name":"https","port":443},{"name":"tls","port":15443,"targetPort":15443}],"secretVolumes":[{"mountPath":"/etc/istio/egressgateway-certs","name":"egressgateway-certs","secretName":"istio-egressgateway-certs"},{"mountPath":"/etc/istio/egressgateway-ca-certs","name":"egressgateway-ca-certs","secretName":"istio-egressgateway-ca-certs"}],"type":"ClusterIP","zvpn":{"enabled":true,"suffix":"global"}},"istio-ingressgateway":{"applicationPorts":"","autoscaleEnabled":true,"debug":"info","domain":"","enabled":true,"meshExpansionPorts":[{"name":"tcp-pilot-grpc-tls","port":15011,"targetPort":15011},{"name":"tcp-citadel-grpc-tls","port":8060,"targetPort":8060},{"name":"tcp-dns-tls","port":853,"targetPort":853}],"namespace":"dubbo-system","ports":[{"name":"status-port","port":15020,"targetPort":15020},{"name":"http2","port":80,"targetPort":80},{"name":"https","port":443},{"name":"kiali","port":15029,"targetPort":15029},{"name":"prometheus","port":15030,"targetPort":15030},{"name":"grafana","port":15031,"targetPort":15031},{"name":"tracing","port":15032,"targetPort":15032},{"name":"tls","port":15443,"targetPort":15443}],"sds":{"enabled":false,"image":"node-agent-k8s","resources":{"limits":{"cpu":"2000m","memory":"1024Mi"},"requests":{"cpu":"100m","memory":"128Mi"}}},"secretVolumes":[{"mountPath":"/etc/istio/ingressgateway-certs","name":"ingressgateway-certs","secretName":"istio-ingressgateway-certs"},{"mountPath":"/etc/istio/ingressgateway-ca-certs","name":"ingressgateway-ca-certs","secretName":"istio-ingressgateway-ca-certs"}],"type":"LoadBalancer","zvpn":{"enabled":true,"suffix":"global"}}},"global":{"arch":{"amd64":2,"ppc64le":2,"s390x":2},"certificates":[],"configNamespace":"dubbo-system","configValidation":true,"controlPlaneSecurityEnabled":true,"defaultNodeSelector":{},"defaultPodDisruptionBudget":{"enabled":true},"defaultResources":{"requests":{"cpu":"10m"}},"disablePolicyChecks":true,"enableHelmTest":false,"enableTracing":true,"enabled":true,"hub":"docker.io/istio","imagePullPolicy":"IfNotPresent","imagePullSecrets":[],"istioNamespace":"dubbo-system","k8sIngress":{"enableHttps":false,"enabled":false,"gatewayName":"ingressgateway"},"localityLbSetting":{"enabled":true},"logAsJson":false,"logging":{"level":"default:info"},"meshExpansion":{"enabled":false,"useILB":false},"meshNetworks":{},"mtls":{"auto":false,"enabled":false},"multiCluster":{"clusterName":"","enabled":false},"namespace":"dubbo-system","network":"","omitSidecarInjectorConfigMap":false,"oneNamespace":false,"operatorManageWebhooks":false,"outboundTrafficPolicy":{"mode":"ALLOW_ANY"},"policyCheckFailOpen":false,"policyNamespace":"dubbo-system","priorityClassName":"","prometheusNamespace":"dubbo-system","proxy":{"accessLogEncoding":"TEXT","accessLogFile":"","accessLogFormat":"","autoInject":"enabled","clusterDomain":"cluster.local","componentLogLevel":"misc:error","concurrency":2,"dnsRefreshRate":"300s","enableCoreDump":false,"envoyAccessLogService":{"enabled":false},"envoyMetricsService":{"enabled":false,"tcpKeepalive":{"interval":"10s","probes":3,"time":"10s"},"tlsSettings":{"mode":"DISABLE","subjectAltNames":[]}},"envoyStatsd":{"enabled":false},"excludeIPRanges":"","excludeInboundPorts":"","excludeOutboundPorts":"","image":"docker.io/istio/proxyv2:1.3.1","includeIPRanges":"*","includeInboundPorts":"*","kubevirtInterfaces":"","logLevel":"warning","privileged":false,"protocolDetectionTimeout":"100ms","readinessFailureThreshold":30,"readinessInitialDelaySeconds":1,"readinessPeriodSeconds":2,"resources":{"limits":{"cpu":"2000m","memory":"1024Mi"},"requests":{"cpu":"100m","memory":"128Mi"}},"statusPort":15020,"tracer":"zipkin"},"proxy_init":{"image":"proxyv2","resources":{"limits":{"cpu":"100m","memory":"50Mi"},"requests":{"cpu":"10m","memory":"10Mi"}}},"sds":{"enabled":false,"token":{"aud":"istio-ca"},"udsPath":""},"securityNamespace":"dubbo-system","tag":"1.3.1","telemetryNamespace":"dubbo-system","tracer":{"datadog":{"address":"$(HOST_IP):8126"},"lightstep":{"accessToken":"","address":"","cacertPath":"","secure":true},"zipkin":{"address":""}},"trustDomain":"cluster.local","useMCP":true},"grafana":{"accessMode":"ReadWriteMany","contextPath":"/grafana","dashboardProviders":{"dashboardproviders.yaml":{"apiVersion":1,"providers":[{"disableDeletion":false,"folder":"istio","name":"istio","options":{"path":"/var/lib/grafana/dashboards/istio"},"orgId":1,"type":"file"}]}},"datasources":{"datasources.yaml":{"apiVersion":1}},"enabled":false,"env":{},"envSecrets":{},"image":{"repository":"grafana/grafana","tag":"6.4.3"},"ingress":{"enabled":false,"hosts":["grafana.local"]},"namespace":"dubbo-system","nodeSelector":{},"persist":false,"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"security":{"enabled":false,"passphraseKey":"passphrase","secretName":"grafana","usernameKey":"username"},"service":{"annotations":{},"externalPort":3000,"name":"http","type":"ClusterIP"},"storageClassName":"","tolerations":[]},"istio_cni":{"enabled":false},"istiocoredns":{"coreDNSImage":"coredns/coredns","coreDNSPluginImage":"istio/coredns-plugin:0.2-istio-1.1","coreDNSTag":"1.6.2","enabled":false,"namespace":"dubbo-system"},"kiali":{"contextPath":"/kiali","createDemoSecret":false,"dashboard":{"passphraseKey":"passphrase","secretName":"kiali","usernameKey":"username","viewOnlyMode":false},"enabled":false,"hub":"quay.io/kiali","ingress":{"enabled":false,"hosts":["kiali.local"]},"namespace":"dubbo-system","nodeSelector":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"security":{"cert_file":"/kiali-cert/cert-chain.pem","enabled":false,"private_key_file":"/kiali-cert/key.pem"},"tag":"v1.9"},"mixer":{"adapters":{"kubernetesenv":{"enabled":true},"prometheus":{"enabled":true,"metricsExpiryDuration":"10m"},"stackdriver":{"auth":{"apiKey":"","appCredentials":false,"serviceAccountPath":""},"enabled":false,"tracer":{"enabled":false,"sampleProbability":1}},"stdio":{"enabled":false,"outputAsJson":false},"useAdapterCRDs":false},"policy":{"adapters":{"kubernetesenv":{"enabled":true},"useAdapterCRDs":false},"autoscaleEnabled":true,"enabled":true,"image":"mixer","namespace":"dubbo-system","sessionAffinityEnabled":false},"telemetry":{"autoscaleEnabled":true,"enabled":true,"env":{"GOMAXPROCS":"6"},"image":"mixer","loadshedding":{"latencyThreshold":"100ms","mode":"enforce"},"namespace":"dubbo-system","nodeSelector":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"reportBatchMaxEntries":100,"reportBatchMaxTime":"1s","sessionAffinityEnabled":false,"tolerations":[],"useMCP":true}},"nodeagent":{"enabled":false,"image":"node-agent-k8s","namespace":"dubbo-system"},"pilot":{"appNamespaces":[],"autoscaleEnabled":true,"autoscaleMax":5,"autoscaleMin":1,"configMap":true,"configNamespace":"istio-config","cpu":{"targetAverageUtilization":80},"enableProtocolSniffingForInbound":false,"enableProtocolSniffingForOutbound":true,"enabled":true,"env":{},"image":"pilot","ingress":{"ingressClass":"istio","ingressControllerMode":"OFF","ingressService":"istio-ingressgateway"},"keepaliveMaxServerConnectionAge":"30m","meshNetworks":{"networks":{}},"namespace":"dubbo-system","nodeSelector":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"policy":{"enabled":false},"replicaCount":1,"tolerations":[],"traceSampling":1,"useMCP":true},"prometheus":{"contextPath":"/prometheus","enabled":true,"hub":"docker.io/prom","ingress":{"enabled":false,"hosts":["prometheus.local"]},"namespace":"dubbo-system","nodeSelector":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"retention":"6h","scrapeInterval":"15s","security":{"enabled":true},"tag":"v2.12.0","tolerations":[]},"security":{"dnsCerts":{"istio-pilot-service-account.istio-control":"istio-pilot.istio-control"},"enableNamespacesByDefault":true,"enabled":true,"image":"citadel","namespace":"dubbo-system","selfSigned":true,"trustDomain":"cluster.local"},"sidecarInjectorWebhook":{"alwaysInjectSelector":[],"enableNamespacesByDefault":false,"enabled":true,"image":"sidecar_injector","injectLabel":"istio-injection","injectedAnnotations":{},"namespace":"dubbo-system","neverInjectSelector":[],"nodeSelector":{},"objectSelector":{"autoInject":true,"enabled":false},"podAnnotations":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"resources":{},"rewriteAppHTTPProbe":false,"rollingMaxSurge":"100%","rollingMaxUnavailable":"25%","selfSigned":false,"tolerations":[]},"telemetry":{"enabled":true,"v1":{"enabled":true},"v2":{"enabled":false,"prometheus":{"enabled":true},"stackdriver":{"configOverride":{},"enabled":false,"logging":false,"monitoring":false,"topology":false}}},"tracing":{"enabled":false,"ingress":{"enabled":false},"jaeger":{"accessMode":"ReadWriteMany","enabled":false,"hub":"docker.io/jaegertracing","memory":{"max_traces":50000},"namespace":"dubbo-system","persist":false,"spanStorageType":"badger","storageClassName":"","tag":"1.18"},"nodeSelector":{},"opencensus":{"exporters":{"stackdriver":{"enable_tracing":true}},"hub":"docker.io/omnition","resources":{"limits":{"cpu":"1","memory":"2Gi"},"requests":{"cpu":"200m","memory":"400Mi"}},"tag":"0.1.9"},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"provider":"jaeger","service":{"annotations":{},"externalPort":9411,"name":"http-query","type":"ClusterIP"},"zipkin":{"hub":"docker.io/openzipkin","javaOptsHeap":700,"maxSpans":500000,"node":{"cpus":2},"probeStartupDelay":200,"queryPort":9411,"resources":{"limits":{"cpu":"300m","memory":"900Mi"},"requests":{"cpu":"150m","memory":"900Mi"}},"tag":"2.14.2"}},"version":""}' -kind: ConfigMap -metadata: - labels: - app: sidecar-injector - istio: sidecar-injector - operator.istio.io/component: Injector - operator.istio.io/managed: Reconcile - operator.istio.io/version: 1.3.1 - release: istio - name: istio-sidecar-injector - namespace: dubbo-system diff --git a/pkg/config/analysis/analyzers/testdata/sidecar-injector-configmap-with-revision-canary.yaml b/pkg/config/analysis/analyzers/testdata/sidecar-injector-configmap-with-revision-canary.yaml deleted file mode 100644 index 98e63715f..000000000 --- a/pkg/config/analysis/analyzers/testdata/sidecar-injector-configmap-with-revision-canary.yaml +++ /dev/null @@ -1,418 +0,0 @@ -apiVersion: v1 -data: - config: |- - policy: enabled - alwaysInjectSelector: - [] - neverInjectSelector: - [] - template: | - rewriteAppHTTPProbe: {{ valueOrDefault .Values.sidecarInjectorWebhook.rewriteAppHTTPProbe false }} - {{- if or (not .Values.istio_cni.enabled) .Values.global.proxy.enableCoreDump }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{- if not .Values.istio_cni.enabled }} - - name: istio-init - {{- if contains "/" .Values.global.proxy_init.image }} - image: "{{ .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .Values.global.hub }}/{{ .Values.global.proxy_init.image }}:{{ .Values.global.tag }}" - {{- end }} - command: - - istio-iptables - - "-p" - - 15001 - - "-z" - - "15006" - - "-u" - - 1337 - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` `*` }}" - - "-d" - - "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - imagePullPolicy: "{{ valueOrDefault .Values.global.imagePullPolicy `Always` }}" - {{- if .Values.global.proxy_init.resources }} - resources: - {{ toYaml .Values.global.proxy_init.resources | indent 4 }} - {{- else }} - resources: {} - {{- end }} - securityContext: - runAsUser: 0 - runAsNonRoot: false - capabilities: - add: - - NET_ADMIN - {{- if .Values.global.proxy.privileged }} - privileged: true - {{- end }} - restartPolicy: Always - {{- end }} - {{ end -}} - {{- if eq .Values.global.proxy.enableCoreDump true }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" .Values.global.proxy_init.image }} - image: "{{ .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .Values.global.hub }}/{{ .Values.global.proxy_init.image }}:{{ .Values.global.tag }}" - {{- end }} - imagePullPolicy: "{{ valueOrDefault .Values.global.imagePullPolicy `Always` }}" - resources: {} - securityContext: - runAsUser: 0 - runAsNonRoot: false - privileged: true - {{ end }} - {{- end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .Values.global.hub }}/{{ .Values.global.proxy.image }}:{{ .Values.global.tag }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --configPath - - "/etc/istio/proxy" - - --binaryPath - - "/usr/local/bin/envoy" - - --serviceCluster - {{ if ne "" (index .ObjectMeta.Labels "app") -}} - - "{{ index .ObjectMeta.Labels `app` }}.$(POD_NAMESPACE)" - {{ else -}} - - "{{ valueOrDefault .DeploymentMeta.Name `istio-proxy` }}.{{ valueOrDefault .DeploymentMeta.Namespace `default` }}" - {{ end -}} - - --drainDuration - - "{{ formatDuration .ProxyConfig.DrainDuration }}" - - --parentShutdownDuration - - "{{ formatDuration .ProxyConfig.ParentShutdownDuration }}" - - --discoveryAddress - - "{{ annotation .ObjectMeta `sidecar.istio.io/discoveryAddress` .ProxyConfig.DiscoveryAddress }}" - {{- if eq .Values.global.proxy.tracer "lightstep" }} - - --lightstepAddress - - "{{ .ProxyConfig.GetTracing.GetLightstep.GetAddress }}" - - --lightstepAccessToken - - "{{ .ProxyConfig.GetTracing.GetLightstep.GetAccessToken }}" - - --lightstepSecure={{ .ProxyConfig.GetTracing.GetLightstep.GetSecure }} - - --lightstepCacertPath - - "{{ .ProxyConfig.GetTracing.GetLightstep.GetCacertPath }}" - {{- else if eq .Values.global.proxy.tracer "zipkin" }} - - --zipkinAddress - - "{{ .ProxyConfig.GetTracing.GetZipkin.GetAddress }}" - {{- else if eq .Values.global.proxy.tracer "datadog" }} - - --datadogAgentAddress - - "{{ .ProxyConfig.GetTracing.GetDatadog.GetAddress }}" - {{- end }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel}} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel}} - - --connectTimeout - - "{{ formatDuration .ProxyConfig.ConnectTimeout }}" - {{- if .Values.global.proxy.envoyStatsd.enabled }} - - --statsdUdpAddress - - "{{ .ProxyConfig.StatsdUdpAddress }}" - {{- end }} - {{- if .Values.global.proxy.envoyMetricsService.enabled }} - - --envoyMetricsServiceAddress - - "{{ .ProxyConfig.GetEnvoyMetricsService.GetAddress }}" - {{- end }} - {{- if .Values.global.proxy.envoyAccessLogService.enabled }} - - --envoyAccessLogServiceAddress - - "{{ .ProxyConfig.GetEnvoyAccessLogService.GetAddress }}" - {{- end }} - - --proxyAdminPort - - "{{ .ProxyConfig.ProxyAdminPort }}" - {{ if gt .ProxyConfig.Concurrency 0 -}} - - --concurrency - - "{{ .ProxyConfig.Concurrency }}" - {{ end -}} - {{- if .Values.global.controlPlaneSecurityEnabled }} - - --controlPlaneAuthPolicy - - MUTUAL_TLS - {{- else }} - - --controlPlaneAuthPolicy - - NONE - {{- end }} - - --dnsRefreshRate - - {{ valueOrDefault .Values.global.proxy.dnsRefreshRate "300s" }} - {{- if (ne (annotation .ObjectMeta "status.sidecar.istio.io/port" .Values.global.proxy.statusPort) "0") }} - - --statusPort - - "{{ annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort }}" - - --applicationPorts - - "{{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/applicationPorts` (applicationPorts .Spec.Containers) }}" - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - --templateFile=/etc/istio/custom-bootstrap/envoy_bootstrap.json - {{- end }} - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - {{- if eq .Values.global.proxy.tracer "datadog" }} - {{- if isset .ObjectMeta.Annotations `apm.datadoghq.com/env` }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- end }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: ISTIO_META_CONFIG_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: SDS_ENABLED - value: "{{ .Values.global.sds.enabled }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - - name: ISTIO_META_INCLUDE_INBOUND_PORTS - value: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` (applicationPorts .Spec.Containers) }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{ if .ObjectMeta.Labels }} - - name: ISTIO_METAJSON_LABELS - value: | - {{ toJSON .ObjectMeta.Labels }} - {{ end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: {{ .DeploymentMeta.Name }} - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.sds.customTokenDirectory }} - - name: ISTIO_META_SDS_TOKEN_PATH - value: "{{ .Values.global.sds.customTokenDirectory -}}/sdstoken" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if .Values.global.trustDomain }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.trustDomain }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - imagePullPolicy: "{{ valueOrDefault .Values.global.imagePullPolicy `Always` }}" - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if .Values.global.proxy.privileged }} - privileged: true - {{- end }} - {{- if ne .Values.global.proxy.enableCoreDump true }} - readOnlyRootFilesystem: true - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - capabilities: - add: - - NET_ADMIN - runAsGroup: 1337 - {{ else -}} - {{ if .Values.global.sds.enabled }} - runAsGroup: 1337 - {{- end }} - runAsUser: 1337 - {{- end }} - resources: - {{ if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end}} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{ else -}} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 4 }} - {{- end }} - {{ end -}} - volumeMounts: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if .Values.global.sds.enabled }} - - mountPath: /var/run/sds - name: sds-uds-path - readOnly: true - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- if .Values.global.sds.customTokenDirectory }} - - mountPath: "{{ .Values.global.sds.customTokenDirectory -}}" - name: custom-sds-token - readOnly: true - {{- end }} - {{- else }} - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .Values.global.tracer.lightstep.cacertPath }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetLightstep.GetCacertPath }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{- end }} - volumes: - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - - emptyDir: - medium: Memory - name: istio-envoy - {{- if .Values.global.sds.enabled }} - - name: sds-uds-path - hostPath: - path: /var/run/sds - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- if .Values.global.sds.customTokenDirectory }} - - name: custom-sds-token - secret: - secretName: sdstokensecret - {{- end }} - {{- else }} - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 2 }} - {{ end }} - {{ end }} - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .Values.global.tracer.lightstep.cacertPath }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.podDNSSearchNamespaces }} - dnsConfig: - searches: - {{- range .Values.global.podDNSSearchNamespaces }} - - {{ render . }} - {{- end }} - {{- end }} - injectedAnnotations: - values: '{"certmanager":{"enabled":false,"hub":"quay.io/jetstack","image":"cert-manager-controller","namespace":"dubbo-system","tag":"v0.6.2"},"cni":{"namespace":"dubbo-system"},"galley":{"enableAnalysis":false,"enabled":true,"image":"galley","namespace":"dubbo-system"},"gateways":{"istio-egressgateway":{"autoscaleEnabled":true,"enabled":false,"namespace":"dubbo-system","ports":[{"name":"http2","port":80},{"name":"https","port":443},{"name":"tls","port":15443,"targetPort":15443}],"secretVolumes":[{"mountPath":"/etc/istio/egressgateway-certs","name":"egressgateway-certs","secretName":"istio-egressgateway-certs"},{"mountPath":"/etc/istio/egressgateway-ca-certs","name":"egressgateway-ca-certs","secretName":"istio-egressgateway-ca-certs"}],"type":"ClusterIP","zvpn":{"enabled":true,"suffix":"global"}},"istio-ingressgateway":{"applicationPorts":"","autoscaleEnabled":true,"debug":"info","domain":"","enabled":true,"meshExpansionPorts":[{"name":"tcp-pilot-grpc-tls","port":15011,"targetPort":15011},{"name":"tcp-citadel-grpc-tls","port":8060,"targetPort":8060},{"name":"tcp-dns-tls","port":853,"targetPort":853}],"namespace":"dubbo-system","ports":[{"name":"status-port","port":15020,"targetPort":15020},{"name":"http2","port":80,"targetPort":80},{"name":"https","port":443},{"name":"kiali","port":15029,"targetPort":15029},{"name":"prometheus","port":15030,"targetPort":15030},{"name":"grafana","port":15031,"targetPort":15031},{"name":"tracing","port":15032,"targetPort":15032},{"name":"tls","port":15443,"targetPort":15443}],"sds":{"enabled":false,"image":"node-agent-k8s","resources":{"limits":{"cpu":"2000m","memory":"1024Mi"},"requests":{"cpu":"100m","memory":"128Mi"}}},"secretVolumes":[{"mountPath":"/etc/istio/ingressgateway-certs","name":"ingressgateway-certs","secretName":"istio-ingressgateway-certs"},{"mountPath":"/etc/istio/ingressgateway-ca-certs","name":"ingressgateway-ca-certs","secretName":"istio-ingressgateway-ca-certs"}],"type":"LoadBalancer","zvpn":{"enabled":true,"suffix":"global"}}},"global":{"arch":{"amd64":2,"ppc64le":2,"s390x":2},"certificates":[],"configNamespace":"dubbo-system","configValidation":true,"controlPlaneSecurityEnabled":true,"defaultNodeSelector":{},"defaultPodDisruptionBudget":{"enabled":true},"defaultResources":{"requests":{"cpu":"10m"}},"disablePolicyChecks":true,"enableHelmTest":false,"enableTracing":true,"enabled":true,"hub":"docker.io/istio","imagePullPolicy":"IfNotPresent","imagePullSecrets":[],"istioNamespace":"dubbo-system","k8sIngress":{"enableHttps":false,"enabled":false,"gatewayName":"ingressgateway"},"localityLbSetting":{"enabled":true},"logAsJson":false,"logging":{"level":"default:info"},"meshExpansion":{"enabled":false,"useILB":false},"meshNetworks":{},"mtls":{"auto":false,"enabled":false},"multiCluster":{"clusterName":"","enabled":false},"namespace":"dubbo-system","network":"","omitSidecarInjectorConfigMap":false,"oneNamespace":false,"operatorManageWebhooks":false,"outboundTrafficPolicy":{"mode":"ALLOW_ANY"},"policyCheckFailOpen":false,"policyNamespace":"dubbo-system","priorityClassName":"","prometheusNamespace":"dubbo-system","proxy":{"accessLogEncoding":"TEXT","accessLogFile":"","accessLogFormat":"","autoInject":"enabled","clusterDomain":"cluster.local","componentLogLevel":"misc:error","concurrency":2,"dnsRefreshRate":"300s","enableCoreDump":false,"envoyAccessLogService":{"enabled":false},"envoyMetricsService":{"enabled":false,"tcpKeepalive":{"interval":"10s","probes":3,"time":"10s"},"tlsSettings":{"mode":"DISABLE","subjectAltNames":[]}},"envoyStatsd":{"enabled":false},"excludeIPRanges":"","excludeInboundPorts":"","excludeOutboundPorts":"","image":"docker.io/istio/proxyv2:1.3.1","includeIPRanges":"*","includeInboundPorts":"*","kubevirtInterfaces":"","logLevel":"warning","privileged":false,"protocolDetectionTimeout":"100ms","readinessFailureThreshold":30,"readinessInitialDelaySeconds":1,"readinessPeriodSeconds":2,"resources":{"limits":{"cpu":"2000m","memory":"1024Mi"},"requests":{"cpu":"100m","memory":"128Mi"}},"statusPort":15020,"tracer":"zipkin"},"proxy_init":{"image":"proxyv2","resources":{"limits":{"cpu":"100m","memory":"50Mi"},"requests":{"cpu":"10m","memory":"10Mi"}}},"sds":{"enabled":false,"token":{"aud":"istio-ca"},"udsPath":""},"securityNamespace":"dubbo-system","tag":"1.3.1","telemetryNamespace":"dubbo-system","tracer":{"datadog":{"address":"$(HOST_IP):8126"},"lightstep":{"accessToken":"","address":"","cacertPath":"","secure":true},"zipkin":{"address":""}},"trustDomain":"cluster.local","useMCP":true},"grafana":{"accessMode":"ReadWriteMany","contextPath":"/grafana","dashboardProviders":{"dashboardproviders.yaml":{"apiVersion":1,"providers":[{"disableDeletion":false,"folder":"istio","name":"istio","options":{"path":"/var/lib/grafana/dashboards/istio"},"orgId":1,"type":"file"}]}},"datasources":{"datasources.yaml":{"apiVersion":1}},"enabled":false,"env":{},"envSecrets":{},"image":{"repository":"grafana/grafana","tag":"6.4.3"},"ingress":{"enabled":false,"hosts":["grafana.local"]},"namespace":"dubbo-system","nodeSelector":{},"persist":false,"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"security":{"enabled":false,"passphraseKey":"passphrase","secretName":"grafana","usernameKey":"username"},"service":{"annotations":{},"externalPort":3000,"name":"http","type":"ClusterIP"},"storageClassName":"","tolerations":[]},"istio_cni":{"enabled":false},"istiocoredns":{"coreDNSImage":"coredns/coredns","coreDNSPluginImage":"istio/coredns-plugin:0.2-istio-1.1","coreDNSTag":"1.6.2","enabled":false,"namespace":"dubbo-system"},"kiali":{"contextPath":"/kiali","createDemoSecret":false,"dashboard":{"passphraseKey":"passphrase","secretName":"kiali","usernameKey":"username","viewOnlyMode":false},"enabled":false,"hub":"quay.io/kiali","ingress":{"enabled":false,"hosts":["kiali.local"]},"namespace":"dubbo-system","nodeSelector":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"security":{"cert_file":"/kiali-cert/cert-chain.pem","enabled":false,"private_key_file":"/kiali-cert/key.pem"},"tag":"v1.9"},"mixer":{"adapters":{"kubernetesenv":{"enabled":true},"prometheus":{"enabled":true,"metricsExpiryDuration":"10m"},"stackdriver":{"auth":{"apiKey":"","appCredentials":false,"serviceAccountPath":""},"enabled":false,"tracer":{"enabled":false,"sampleProbability":1}},"stdio":{"enabled":false,"outputAsJson":false},"useAdapterCRDs":false},"policy":{"adapters":{"kubernetesenv":{"enabled":true},"useAdapterCRDs":false},"autoscaleEnabled":true,"enabled":true,"image":"mixer","namespace":"dubbo-system","sessionAffinityEnabled":false},"telemetry":{"autoscaleEnabled":true,"enabled":true,"env":{"GOMAXPROCS":"6"},"image":"mixer","loadshedding":{"latencyThreshold":"100ms","mode":"enforce"},"namespace":"dubbo-system","nodeSelector":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"reportBatchMaxEntries":100,"reportBatchMaxTime":"1s","sessionAffinityEnabled":false,"tolerations":[],"useMCP":true}},"nodeagent":{"enabled":false,"image":"node-agent-k8s","namespace":"dubbo-system"},"pilot":{"appNamespaces":[],"autoscaleEnabled":true,"autoscaleMax":5,"autoscaleMin":1,"configMap":true,"configNamespace":"istio-config","cpu":{"targetAverageUtilization":80},"enableProtocolSniffingForInbound":false,"enableProtocolSniffingForOutbound":true,"enabled":true,"env":{},"image":"pilot","ingress":{"ingressClass":"istio","ingressControllerMode":"OFF","ingressService":"istio-ingressgateway"},"keepaliveMaxServerConnectionAge":"30m","meshNetworks":{"networks":{}},"namespace":"dubbo-system","nodeSelector":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"policy":{"enabled":false},"replicaCount":1,"tolerations":[],"traceSampling":1,"useMCP":true},"prometheus":{"contextPath":"/prometheus","enabled":true,"hub":"docker.io/prom","ingress":{"enabled":false,"hosts":["prometheus.local"]},"namespace":"dubbo-system","nodeSelector":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"retention":"6h","scrapeInterval":"15s","security":{"enabled":true},"tag":"v2.12.0","tolerations":[]},"security":{"dnsCerts":{"istio-pilot-service-account.istio-control":"istio-pilot.istio-control"},"enableNamespacesByDefault":true,"enabled":true,"image":"citadel","namespace":"dubbo-system","selfSigned":true,"trustDomain":"cluster.local"},"sidecarInjectorWebhook":{"alwaysInjectSelector":[],"enableNamespacesByDefault":false,"enabled":true,"image":"sidecar_injector","injectLabel":"istio-injection","injectedAnnotations":{},"namespace":"dubbo-system","neverInjectSelector":[],"nodeSelector":{},"objectSelector":{"autoInject":true,"enabled":false},"podAnnotations":{},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"replicaCount":1,"resources":{},"rewriteAppHTTPProbe":false,"rollingMaxSurge":"100%","rollingMaxUnavailable":"25%","selfSigned":false,"tolerations":[]},"telemetry":{"enabled":true,"v1":{"enabled":true},"v2":{"enabled":false,"prometheus":{"enabled":true},"stackdriver":{"configOverride":{},"enabled":false,"logging":false,"monitoring":false,"topology":false}}},"tracing":{"enabled":false,"ingress":{"enabled":false},"jaeger":{"accessMode":"ReadWriteMany","enabled":false,"hub":"docker.io/jaegertracing","memory":{"max_traces":50000},"namespace":"dubbo-system","persist":false,"spanStorageType":"badger","storageClassName":"","tag":"1.18"},"nodeSelector":{},"opencensus":{"exporters":{"stackdriver":{"enable_tracing":true}},"hub":"docker.io/omnition","resources":{"limits":{"cpu":"1","memory":"2Gi"},"requests":{"cpu":"200m","memory":"400Mi"}},"tag":"0.1.9"},"podAntiAffinityLabelSelector":[],"podAntiAffinityTermLabelSelector":[],"provider":"jaeger","service":{"annotations":{},"externalPort":9411,"name":"http-query","type":"ClusterIP"},"zipkin":{"hub":"docker.io/openzipkin","javaOptsHeap":700,"maxSpans":500000,"node":{"cpus":2},"probeStartupDelay":200,"queryPort":9411,"resources":{"limits":{"cpu":"300m","memory":"900Mi"},"requests":{"cpu":"150m","memory":"900Mi"}},"tag":"2.14.2"}},"version":""}' -kind: ConfigMap -metadata: - labels: - app: sidecar-injector - istio: sidecar-injector - operator.istio.io/component: Injector - operator.istio.io/managed: Reconcile - operator.istio.io/version: 1.3.1 - release: istio - name: istio-sidecar-injector-canary - namespace: dubbo-system diff --git a/pkg/config/analysis/analyzers/testdata/sidecar-selector.yaml b/pkg/config/analysis/analyzers/testdata/sidecar-selector.yaml deleted file mode 100644 index c178a4520..000000000 --- a/pkg/config/analysis/analyzers/testdata/sidecar-selector.yaml +++ /dev/null @@ -1,123 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - labels: - app: productpage - name: productpage - namespace: default ---- -apiVersion: v1 -kind: Pod -metadata: - labels: - app: productpage - name: productpage-other - namespace: other ---- -apiVersion: v1 -kind: Pod -metadata: - labels: - app: reviews - name: reviews - namespace: default ---- -apiVersion: v1 -kind: Pod -metadata: - labels: - app: ratings-app - myapp: ratings-myapp - name: ratings - namespace: default ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - name: maps-correctly-no-conflicts - namespace: default -spec: - workloadSelector: - labels: - app: productpage # Maps to an existing workload without conflicts in the same ns, no error - egress: - - hosts: - - "./*" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - name: maps-to-nonexistent - namespace: default -spec: - workloadSelector: - labels: - app: bogus # This doesn't exist, and should generate an error - egress: - - hosts: - - "./*" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - name: maps-to-different-ns - namespace: other -spec: - workloadSelector: - labels: - app: reviews # This doesn't exist in the current namespace, and should generate an error - egress: - - hosts: - - "./*" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - name: dupe-1 - namespace: default -spec: - workloadSelector: - labels: - app: reviews # Multiple sidecars have the same selector, should generate errors for both - egress: - - hosts: - - "./*" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - name: dupe-2 - namespace: default -spec: - workloadSelector: - labels: - app: reviews # Multiple sidecars have the same selector, should generate errors for both - egress: - - hosts: - - "./*" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - name: overlap-1 - namespace: default -spec: - workloadSelector: - labels: - app: ratings-app # Multiple sidecars select overlapping workloads, should generate errors for both - egress: - - hosts: - - "./*" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - name: overlap-2 - namespace: default -spec: - workloadSelector: - labels: - myapp: ratings-myapp # Multiple sidecars select overlapping workloads, should generate errors for both - egress: - - hosts: - - "./*" diff --git a/pkg/config/analysis/analyzers/testdata/virtualservice_conflictingmeshgatewayhosts.yaml b/pkg/config/analysis/analyzers/testdata/virtualservice_conflictingmeshgatewayhosts.yaml deleted file mode 100644 index 1a5d8bef4..000000000 --- a/pkg/config/analysis/analyzers/testdata/virtualservice_conflictingmeshgatewayhosts.yaml +++ /dev/null @@ -1,155 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: productpage - namespace: foo -spec: - hosts: - - productpage # should generate an error as this conflicts with VirtualService foo/bogus - http: - - route: - - destination: - host: productpage ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: bogus-productpage - namespace: foo -spec: - hosts: - - productpage # should generate an error as this conflicts with VirtualService foo/productpage - http: - - route: - - destination: - host: reviews ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews - namespace: foo -spec: - hosts: - - reviews # shouldn't generate an error as there's no conflicting VirtualService - http: - - route: - - destination: - host: reviews ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews - namespace: bar -spec: - hosts: - - reviews.foo.svc.cluster.local # shouldn't generate an error as the gateway is different even though host is the same - gateways: - - istio-ingressgateway - http: - - route: - - destination: - host: reviews ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: ratings - namespace: foo -spec: - hosts: - - ratings # should generate an error as this conflicts with VirtualService bar/ratings - gateways: - - mesh - http: - - route: - - destination: - host: ratings ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: ratings - namespace: bar -spec: - hosts: - - ratings.foo.svc.cluster.local # should generate an error as mesh gateway is specified and hosts conflict with VirtualService foo/ratings - - google.com - gateways: - - istio-ingressgateway - - mesh - http: - - route: - - destination: - host: ratings ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: ratings - namespace: team1 -spec: - hosts: - - ratings # shouldn't generate an error as this doesn't conflict with VirtualService ratings.team2 due to exportTo setting - gateways: - - mesh - exportTo: - - "." - http: - - route: - - destination: - host: ratings ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: ratings - namespace: team2 -spec: - hosts: - - ratings.team1.svc.cluster.local # shouldn't generate an error as this VirtualService doesn't conflict with VirtualService ratings.team1 due to exportTo setting - - google.com - exportTo: - - "." - gateways: - - istio-ingressgateway - - mesh - http: - - route: - - destination: - host: ratings ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: ratings - namespace: team3 -spec: - hosts: - - ratings # should generate an error as this conflicts with VirtualService ratings.team4 - gateways: - - mesh - exportTo: - - "*" - http: - - route: - - destination: - host: ratings ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: ratings - namespace: team4 -spec: - hosts: - - ratings.team3.svc.cluster.local # should generate an error as this conflicts with VirtualService ratings.team3 - gateways: - - istio-ingressgateway - - mesh - http: - - route: - - destination: - host: ratings ---- diff --git a/pkg/config/analysis/analyzers/testdata/virtualservice_destinationhosts.yaml b/pkg/config/analysis/analyzers/testdata/virtualservice_destinationhosts.yaml deleted file mode 100644 index f7c0647f5..000000000 --- a/pkg/config/analysis/analyzers/testdata/virtualservice_destinationhosts.yaml +++ /dev/null @@ -1,273 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: reviews - namespace: default -spec: - ports: - - port: 42 - name: tcp-test - protocol: TCP ---- -apiVersion: v1 -kind: Service -metadata: - name: reviews-2port - namespace: default -spec: - ports: - - port: 80 - name: http-test - protocol: HTTP - - port: 443 - name: https-test - protocol: HTTPS ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: external-reviews - namespace: default -spec: - hosts: - - external-reviews.org ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: eu-wildcard - namespace: default -spec: - hosts: - - "*.eu.bookinfo.com" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-entry-ignore - namespace: default-ignore -spec: - exportTo: - - "." - hosts: - - "*" # This ServiceEntry should not match any instance as it isn't exported to other namespaces ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: service-entry-other - namespace: other -spec: - exportTo: - - "." - hosts: - - "other.bookinfo.com" # This ServiceEntry should not match any instance as it isn't exported to other namespaces ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews - namespace: default -spec: - http: - - route: - - destination: # This virtualservice has no validation errors (base case) - host: reviews - subset: v1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews-bogushost - namespace: default -spec: - http: - - route: - - destination: - host: reviews-bogus # This host does not exist, should result in a validation error - subset: v1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews-fqdn - namespace: default -spec: - http: - - route: - - destination: - host: reviews.default.svc.cluster.local # FQDN representation is valid and should not generate an error - subset: v1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews-external - namespace: default -spec: - http: - - route: - - destination: - host: external-reviews.org # Referring to a ServiceEntry host is valid and should not generate an error - # Since this is an "external" service, subset is omitted ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews-bookinfo-eu - namespace: default -spec: - http: - - route: - - destination: - host: reviews.eu.bookinfo.com # This should match the eu-wildcard service entry and not generate an error ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews-bookinfo-eu-wildcard - namespace: default -spec: - http: - - route: - - destination: - host: "*.eu.bookinfo.com" # Should match *.eu.bookinfo.com ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews-bookinfo-other - namespace: default -spec: - http: - - route: - - destination: - host: other.bookinfo.com # Should generate validation error, the SE is in another namespace ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews-mirror - namespace: default -spec: - http: - - route: - - destination: - host: reviews - subset: v1 - mirror: # Includes mirroring, but should not generate any errors - host: reviews - subset: v1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews-mirror-bogushost - namespace: default -spec: - http: - - route: - - destination: - host: reviews - subset: v1 - mirror: - host: reviews-bogus # This host does not exist, should result in a validation error - subset: v1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews-bogusport - namespace: default -spec: - http: - - route: - - destination: - host: reviews - subset: v1 - port: - number: 999 # No match for this port number, should generate an error ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews-2port-missing - namespace: default -spec: - http: - - route: - - destination: # Since reviews-2port exposes multiple ports, not including a port in the destination is an error - host: reviews-2port - subset: v1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews-2port-present - namespace: default -spec: - http: - - route: - - destination: - host: reviews-2port - subset: v1 - port: - number: 80 # Should not generate an error since we specify a valid port, as required in this case ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - # This VirtualService is in 'dubbo-system' and uses a FQDN destination - name: cross-namespace - namespace: dubbo-system -spec: - hosts: [reviews] - http: - - route: - - destination: - # Should not generate error because the this host exists, just not in our namespace - host: reviews.default.svc.cluster.local ---- -apiVersion: v1 -kind: Service -metadata: - name: details - namespace: default - annotations: - networking.istio.io/exportTo: banana - labels: - app: details - service: details -spec: - ports: - - port: 9080 - name: http - selector: - app: details ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - # This VirtualService is in 'dubbo-system' and uses a FQDN destination, but this ns doesn't see that Service - name: cross-namespace-details - namespace: dubbo-system -spec: - hosts: [details] - http: - - route: - - destination: - # Should generate error, because details is only exported to "banana" ns - host: details.default.svc.cluster.local ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - # This is cross-namespace, but not a problem, details has explicit networking.istio.io/exportTo=banana - name: banana-details - namespace: banana -spec: - hosts: [details] - http: - - route: - - destination: - host: details.default.svc.cluster.local diff --git a/pkg/config/analysis/analyzers/testdata/virtualservice_destinationrules.yaml b/pkg/config/analysis/analyzers/testdata/virtualservice_destinationrules.yaml deleted file mode 100644 index 25fc135c6..000000000 --- a/pkg/config/analysis/analyzers/testdata/virtualservice_destinationrules.yaml +++ /dev/null @@ -1,77 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: reviews - namespace: default -spec: - host: reviews - subsets: - - labels: - version: v1 - name: v1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews - namespace: default -spec: - http: - - route: - - destination: # This virtualservice has no validation errors (base case) - host: reviews - subset: v1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews-bogussubset - namespace: default -spec: - http: - - route: - - destination: - host: reviews - subset: bogus # This subset does not exist, should result in a validation error ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews-fqdn - namespace: default -spec: - http: - - route: - - destination: - host: reviews.default.svc.cluster.local # FQDN representation is valid and should not generate an error - subset: v1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews-mirror - namespace: default -spec: - http: - - route: - - destination: - host: reviews - subset: v1 - mirror: # Includes mirroring, but should not generate any errors - host: reviews - subset: v1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews-mirror-bogussubset - namespace: default -spec: - http: - - route: - - destination: - host: reviews - subset: v1 - mirror: - host: reviews - subset: bogus # This subset does not exist, should result in a validation error \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/virtualservice_dupmatches.yaml b/pkg/config/analysis/analyzers/testdata/virtualservice_dupmatches.yaml deleted file mode 100644 index bae49bde9..000000000 --- a/pkg/config/analysis/analyzers/testdata/virtualservice_dupmatches.yaml +++ /dev/null @@ -1,206 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: sample-foo-cluster01 - namespace: foo -spec: - hosts: - - sample.foo.svc.cluster.local - http: - - fault: - delay: - fixedDelay: 5s - percentage: - value: 100 - route: - - destination: - host: sample.foo.svc.cluster.local - - mirror: - host: sample.bar.svc.cluster.local - route: - - destination: - host: sample.bar.svc.cluster.local - subset: v1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: duplicate-match -spec: - hosts: - - sample.baz.svc.cluster.local - http: - - name: send /api/v1/products to sample.foo - match: - - name: prefix /api/v1/products - uri: - prefix: /api/v1/products - route: - - destination: - host: sample.foo.svc.cluster.local - - name: send /api/v1/products to sample.bar - match: - - uri: - prefix: /api/v1/products - route: - - destination: - host: sample.bar.svc.cluster.local - subset: v1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: almost-duplicate-match -spec: - hosts: - - sample.bar.svc.cluster.local - http: - - match: - - uri: - prefix: /api/v1/products - route: - - destination: - host: sample.foo.svc.cluster.local - - name: send /api/v1/products and /potato to sample.bar - match: - - name: prefix /api/v1/products - uri: - prefix: /api/v1/products - - name: prefix /potato - uri: - prefix: /potato - route: - - destination: - host: sample.bar.svc.cluster.local - subset: v1 ---- ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: duplicate-tcp-match -spec: - hosts: - - "*" - gateways: - - tcp-echo-gateway - tcp: - - match: - - port: 31400 - route: - - destination: - host: tcp-echo - port: - number: 9000 - subset: v1 - - match: - - port: 31400 - route: - - destination: - host: tcp-echo - port: - number: 9000 - subset: v2 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: duplicate-empty-tcp -spec: - hosts: - - "*" - gateways: - - tcp-echo-gateway - tcp: - - route: - - destination: - host: tcp-echo - port: - number: 9000 - subset: v1 - - route: - - destination: - host: tcp-echo - port: - number: 9000 - subset: v2 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: almost-duplicate-tcp-match -spec: - hosts: - - "*" - gateways: - - tcp-echo-gateway - tcp: - - match: - - port: 31400 - route: - - destination: - host: tcp-echo - port: - number: 9000 - subset: v1 - - match: - - port: 31400 - - port: 31401 - route: - - destination: - host: tcp-echo - port: - number: 9000 - subset: v2 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: tls-routing - namespace: none -spec: - hosts: - - www1.googleapis.com - - api1.facebook.com - tls: - - match: - - port: 2443 - sniHosts: - - www1.googleapis.com - route: - - destination: - host: www1.googleapis.com - - match: - - port: 2443 - sniHosts: - - www1.googleapis.com - route: - - destination: - host: api1.facebook.com ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: tls-routing-almostmatch - namespace: none -spec: - hosts: - - www1.example.com - tls: - - match: - - port: 2443 - sniHosts: - - www1.example.com - route: - - destination: - host: www1.googleapis.com - - match: - - port: 2443 - sniHosts: - - www1.example.com - - port: 8443 - sniHosts: - - www1.example.com - route: - - destination: - host: api1.facebook.com diff --git a/pkg/config/analysis/analyzers/testdata/virtualservice_gateways.yaml b/pkg/config/analysis/analyzers/testdata/virtualservice_gateways.yaml deleted file mode 100644 index 805d0a61d..000000000 --- a/pkg/config/analysis/analyzers/testdata/virtualservice_gateways.yaml +++ /dev/null @@ -1,59 +0,0 @@ -# Broken config in a yaml config file -# -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: httpbin-gateway -spec: - selector: - istio: ingressgateway ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: crossnamespace-gw - namespace: another -spec: - selector: - istio: ingressgateway ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: httpbin -spec: - hosts: - - "*" - gateways: - - httpbin-gateway # Expected: no validation error since this gateway exists ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: httpbin-bogus -spec: - hosts: - - "*" - gateways: - - httpbin-gateway-bogus # Expected: validation error since this gateway does not exist ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: httpbin-mesh -spec: - hosts: - - "*" - gateways: - - mesh # Expected: no validation error, "mesh" is a special-case value ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: cross-test - namespace: default -spec: - hosts: - - "*" - gateways: - - another/crossnamespace-gw # No validation error expected diff --git a/pkg/config/analysis/analyzers/testdata/virtualservice_host_not_found_gateway.yaml b/pkg/config/analysis/analyzers/testdata/virtualservice_host_not_found_gateway.yaml deleted file mode 100644 index 77cac4644..000000000 --- a/pkg/config/analysis/analyzers/testdata/virtualservice_host_not_found_gateway.yaml +++ /dev/null @@ -1,252 +0,0 @@ ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: testing-gateway-01-test-01 - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - testing-01.com - port: - name: http - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: testing-service-01-test-01 - namespace: default -spec: - gateways: - - dubbo-system/testing-gateway-01-test-01 - hosts: - - testing-01.com # Expected: no validation error since this host exists - http: - - match: - - uri: - prefix: / - route: - - destination: - host: ratings ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: testing-service-02-test-01 - namespace: default -spec: - gateways: - - dubbo-system/testing-gateway-01-test-01 - hosts: - - wrong-01.com # Expected: validation error since this host does not exist - http: - - match: - - uri: - prefix: / - route: - - destination: - host: ratings ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: testing-gateway-01-test-02 - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - '*.testing-02.com' - port: - name: http - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: testing-service-01-test-02 - namespace: default -spec: - gateways: - - dubbo-system/testing-gateway-01-test-02 - hosts: - - 'web.testing-02.com' # Expected: no validation error since this host match the wildcard - http: - - match: - - uri: - prefix: / - route: - - destination: - host: ratings ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: testing-service-02-test-02 - namespace: default -spec: - gateways: - - dubbo-system/testing-gateway-01-test-02 - hosts: - - 'web.wrong.com' # Expected: validation error since this host does not exist - http: - - match: - - uri: - prefix: / - route: - - destination: - host: ratings ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: testing-gateway-01-test-03 - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - '*.api.testing-03.com' - port: - name: http - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: testing-gateway-02-test-03 - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - '*.homepage.testing-03.com' - port: - name: http - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: testing-service-01-test-03 - namespace: default -spec: - gateways: - - dubbo-system/testing-gateway-01-test-03 - - dubbo-system/testing-gateway-02-test-03 - hosts: - - 'user.api.testing-03.com' - - 'profile.homepage.testing-03.com' # Expected: no validation error since this host match the wildcard - http: - - match: - - uri: - prefix: / - route: - - destination: - host: ratings ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: testing-service-02-test-03 - namespace: default -spec: - gateways: - - dubbo-system/testing-gateway-01-test-03 - - dubbo-system/testing-gateway-02-test-03 - hosts: - - 'user.api.testing-03.com' # Expected: validation error since this host does not exist - http: - - match: - - uri: - prefix: / - route: - - destination: - host: ratings ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: testing-gateway-01-test-04 - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - '*.testing-01-04.com' - port: - name: http - number: 80 - protocol: HTTP - - hosts: - - 'web.testing-02-04.com' - port: - name: http - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: testing-service-01-test-04 - namespace: default -spec: - gateways: - - dubbo-system/testing-gateway-01-test-04 - hosts: - - 'web.testing-02-04.com' # Expected: no validation error since this host exists - http: - - match: - - uri: - prefix: / - route: - - destination: - host: ratings ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: testing-service-02-test-04 - namespace: default -spec: - gateways: - - dubbo-system/testing-gateway-01-test-04 - hosts: - - 'profile.user.testing-01-04.com' # Expected: no validation error since this host match the wildcard - http: - - match: - - uri: - prefix: / - route: - - destination: - host: ratings ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: testing-service-03-test-04 - namespace: default -spec: - gateways: - - dubbo-system/testing-gateway-01-test-04 - hosts: - - 'user.testing-02-04.com' - - 'users.testing-02-04.com' # Expected: validation error since this host does not exist - http: - - match: - - uri: - prefix: / - route: - - destination: - host: ratings diff --git a/pkg/config/analysis/analyzers/testdata/virtualservice_host_not_found_gateway_with_ns_prefix.yaml b/pkg/config/analysis/analyzers/testdata/virtualservice_host_not_found_gateway_with_ns_prefix.yaml deleted file mode 100644 index d4e14237f..000000000 --- a/pkg/config/analysis/analyzers/testdata/virtualservice_host_not_found_gateway_with_ns_prefix.yaml +++ /dev/null @@ -1,207 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: testing-gateway - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - './testing-01.com' - port: - name: http - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: testing-service-01-test-01 - namespace: dubbo-system -spec: - gateways: - - dubbo-system/testing-gateway - hosts: - - testing-01.com # Expected: no validation error because this host exists - http: - - match: - - uri: - prefix: / - route: - - destination: - host: ratings ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: testing-service-01-test-01 - namespace: default -spec: - gateways: - - dubbo-system/testing-gateway - hosts: - - testing-01.com # Expected: validation error because this host is in the different namespace - http: - - match: - - uri: - prefix: / - route: - - destination: - host: ratings ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: testing-gateway-2 - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - '*/testing-01.com' - port: - name: http - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: testing-service-01-test-02 - namespace: dubbo-system -spec: - gateways: - - dubbo-system/testing-gateway-2 - hosts: - - testing-01.com # Expected: no validation error because this host exists - http: - - match: - - uri: - prefix: / - route: - - destination: - host: ratings ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: testing-service-01-test-02 - namespace: default -spec: - gateways: - - dubbo-system/testing-gateway-2 - hosts: - - testing-01.com # Expected: no validation error because the gateway ns prefix is a wildcard match - http: - - match: - - uri: - prefix: / - route: - - destination: - host: ratings ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: testing-gateway-3 - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - '*/*' - port: - name: http - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: testing-service-01-test-03 - namespace: dubbo-system -spec: - gateways: - - dubbo-system/testing-gateway-3 - hosts: - - testing-01.com # Expected: no validation error because this host exists - http: - - match: - - uri: - prefix: / - route: - - destination: - host: ratings ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: testing-service-01-test-03 - namespace: default -spec: - gateways: - - dubbo-system/testing-gateway-3 - hosts: - - testing-01.com # Expected: no validation error because the gateway ns prefix is a wildcard match - http: - - match: - - uri: - prefix: / - route: - - destination: - host: ratings ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: testing-gateway-4 - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - 'testing-01.com' # should be the same result with '*/testing-01.com' - port: - name: http - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: testing-service-01-test-04 - namespace: dubbo-system -spec: - gateways: - - dubbo-system/testing-gateway-4 - hosts: - - testing-01.com # Expected: no validation error because this host exists - http: - - match: - - uri: - prefix: / - route: - - destination: - host: ratings ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: testing-service-01-test-04 - namespace: default -spec: - gateways: - - dubbo-system/testing-gateway-4 - hosts: - - testing-01.com # Expected: no validation error because this host exists - http: - - match: - - uri: - prefix: / - route: - - destination: - host: ratings diff --git a/pkg/config/analysis/analyzers/testdata/virtualservice_jwtclaimroute.yaml b/pkg/config/analysis/analyzers/testdata/virtualservice_jwtclaimroute.yaml deleted file mode 100644 index 7cb0978eb..000000000 --- a/pkg/config/analysis/analyzers/testdata/virtualservice_jwtclaimroute.yaml +++ /dev/null @@ -1,38 +0,0 @@ -# The following virtual service should cause the error IST0149 because it uses JWT claim based routing but there is -# no request authentication applied on the gateway. -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: foo -spec: - hosts: - - "foo.com" - gateways: - - foo-gateway - http: - - match: - - uri: - prefix: / - headers: - "@request.auth.claims.foo": - exact: foo - route: - - destination: - host: istiod.dubbo-system.svc.cluster.local - port: - number: 15010 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: foo-gateway -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 80 - name: http - protocol: HTTP - hosts: - - "foo.com" diff --git a/pkg/config/analysis/analyzers/testdata/virtualservice_overlappingmatches.yaml b/pkg/config/analysis/analyzers/testdata/virtualservice_overlappingmatches.yaml deleted file mode 100644 index 437c46630..000000000 --- a/pkg/config/analysis/analyzers/testdata/virtualservice_overlappingmatches.yaml +++ /dev/null @@ -1,121 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: non-method-get -spec: - hosts: - - sample.baz.svc.cluster.local - http: - - name: "send product to sample.foo" - match: - - uri: - prefix: "/api/v1/product" - - uri: - prefix: "/api/v1/products" - method: - exact: GET - route: - - destination: - host: sample.foo.svc.cluster.local ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: uri-with-prefix-exact -spec: - hosts: - - sample.baz.svc.cluster.local - http: - - name: "send product to sample.foo" - match: - - uri: - prefix: "/" - - uri: - exact: "/" - method: - exact: GET - route: - - destination: - host: sample.foo.svc.cluster.local ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: overlapping-in-single-match -spec: - hosts: - - sample.baz.svc.cluster.local - http: - - name: "send product to sample.foo" - match: - - uri: - prefix: "/api/v1/product" - method: - exact: GET - - uri: - prefix: "/api/v1/products" - method: - exact: GET - route: - - destination: - host: sample.foo.svc.cluster.local ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: overlapping-in-two-matches -spec: - hosts: - - sample.baz.svc.cluster.local - http: - - name: "send product to sample.foo" - match: - - uri: - prefix: "/api/v1/product" - method: - exact: GET - route: - - destination: - host: sample.foo.svc.cluster.local - - name: "send products to sample.bar" - match: - - uri: - prefix: "/api/v1/products" - method: - exact: GET - route: - - destination: - host: sample.bar.svc.cluster.local - subset: v1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: overlapping-mathes-with-different-methods -spec: - hosts: - - sample.baz.svc.cluster.local - http: - - name: "send product to sample.foo" - match: - - uri: - prefix: "/api/v1/prod" - method: - exact: GET - route: - - destination: - host: sample.foo.svc.cluster.local - - name: "send products to sample.bar" - match: - - uri: - prefix: "/api/v1/product" - method: - exact: GET - - uri: - prefix: "/api/v1/products" - method: - exact: POST - route: - - destination: - host: sample.bar.svc.cluster.local - subset: v1 \ No newline at end of file diff --git a/pkg/config/analysis/analyzers/testdata/virtualservice_regexes.yaml b/pkg/config/analysis/analyzers/testdata/virtualservice_regexes.yaml deleted file mode 100644 index 23f2a71d8..000000000 --- a/pkg/config/analysis/analyzers/testdata/virtualservice_regexes.yaml +++ /dev/null @@ -1,96 +0,0 @@ -# Broken config in a yaml config file -# -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: bad-match -spec: - hosts: - - "*" - gateways: - - bookinfo-gateway - http: - - match: - - uri: - regex: "[A-Z" - route: - - destination: - host: productpage ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: valid-regexp -spec: - hosts: - - "*" - gateways: - - bookinfo-gateway - http: - - match: - - uri: - regex: "[A-Z]" - route: - - destination: - host: productpage ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: ecma-not-v2 -spec: - hosts: - - "*" - gateways: - - bookinfo-gateway - http: - - match: - - uri: - regex: "^(?!..).*" - route: - - destination: - host: productpage ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: no-regexes -spec: - hosts: - - "*" - gateways: - - bookinfo-gateway - http: - - route: - - destination: - host: productpage ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: lots-of-regexes -spec: - hosts: - - "*" - gateways: - - bookinfo-gateway - http: - - match: - - uri: - regex: "[A-Z" - - scheme: - regex: "[A-" - - authority: - regex: "[A-B" - - headers: - end-user: - regex: "[jason-" - - queryParams: - zipcode: - regex: "[1-" - corsPolicy: - allowOrigins: - - regex: "[O-R" - route: - - destination: - host: productpage diff --git a/pkg/config/analysis/analyzers/testdata/virtualservice_route_rule_no_effects_ingress.yaml b/pkg/config/analysis/analyzers/testdata/virtualservice_route_rule_no_effects_ingress.yaml deleted file mode 100644 index 5b33fc31f..000000000 --- a/pkg/config/analysis/analyzers/testdata/virtualservice_route_rule_no_effects_ingress.yaml +++ /dev/null @@ -1,100 +0,0 @@ ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: testing-gateway-01-test-01 - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - testing-01.com - port: - name: http - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: testing-service-01-test-01 - namespace: default -spec: - gateways: - - dubbo-system/testing-gateway-01-test-01 - hosts: - - testing-01.com - http: - - route: - - destination: - host: ratings # Expected: validation error since destination don't have subset ---- -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: testing-gateway-02-test-02 - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - hosts: - - testing-02.com - port: - name: http - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: testing-service-02-test-02 - namespace: default -spec: - gateways: - - dubbo-system/testing-gateway-02-test-02 - hosts: - - testing-02.com - http: - - route: - - destination: - host: ratings # Expected: no validation error - subset: v1 ---- -apiVersion: v1 -kind: Service -metadata: - name: ratings - namespace: default -spec: - ports: - - port: 80 - name: http-ratings - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: ratings-01 - namespace: default -spec: - hosts: - - ratings - http: - - route: - - destination: - host: ratings - subset: v1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: ratings - namespace: default -spec: - host: ratings - subsets: - - name: v1 - labels: - version: v1 diff --git a/pkg/config/analysis/analyzers/testdata/webhook.yaml b/pkg/config/analysis/analyzers/testdata/webhook.yaml deleted file mode 100644 index c7182dbf2..000000000 --- a/pkg/config/analysis/analyzers/testdata/webhook.yaml +++ /dev/null @@ -1,38 +0,0 @@ -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - name: istio-sidecar-injector-missing-overlap -webhooks: -- admissionReviewVersions: - - v1beta1 - clientConfig: - service: - name: fake - namespace: dubbo-system - name: sidecar-injector.istio.io - namespaceSelector: - matchLabels: - istio-injection: enabled ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - name: istio-sidecar-injector-overlap -webhooks: -- admissionReviewVersions: - - v1beta1 - clientConfig: - service: - name: istiod - namespace: dubbo-system - name: sidecar-injector.istio.io - namespaceSelector: - matchLabels: - istio-injection: enabled ---- -apiVersion: v1 -kind: Service -metadata: - name: istiod - namespace: dubbo-system -spec: {} diff --git a/pkg/config/analysis/analyzers/util/config.go b/pkg/config/analysis/analyzers/util/config.go deleted file mode 100644 index f1b39ad2e..000000000 --- a/pkg/config/analysis/analyzers/util/config.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/kube/inject" -) - -// IsSystemNamespace returns true for system namespaces -func IsSystemNamespace(ns resource.Namespace) bool { - return inject.IgnoredNamespaces.Contains(ns.String()) -} - -// IsIstioControlPlane returns true for resources that are part of the Istio control plane -func IsIstioControlPlane(r *resource.Instance) bool { - if _, ok := r.Metadata.Labels["istio"]; ok { - return true - } - if r.Metadata.Labels["release"] == "istio" { - return true - } - return false -} - -// IsMatched check if the term can be matched in a slice of string -func IsMatched(slice []string, term string) bool { - for _, val := range slice { - matched := strings.Contains(term, val) - return matched - } - return false -} - -func GetInjectorConfigMapName(revision string) string { - name := InjectionConfigMap - if revision == "" || revision == "default" { - return name - } - return name + "-" + revision -} diff --git a/pkg/config/analysis/analyzers/util/constants.go b/pkg/config/analysis/analyzers/util/constants.go deleted file mode 100644 index 899ce9414..000000000 --- a/pkg/config/analysis/analyzers/util/constants.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "regexp" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" -) - -const ( - DefaultKubernetesDomain = "svc." + constants.DefaultKubernetesDomain - ExportToNamespaceLocal = "." - ExportToAllNamespaces = "*" - IstioProxyName = "istio-proxy" - IstioOperator = "istio-operator" - MeshGateway = "mesh" - Wildcard = "*" - MeshConfigName = "istio" - InjectionLabelName = "istio-injection" - InjectionLabelEnableValue = "enabled" - InjectionConfigMap = "istio-sidecar-injector" - InjectionConfigMapValue = "values" - InjectorWebhookConfigKey = "sidecarInjectorWebhook" - InjectorWebhookConfigValue = "enableNamespacesByDefault" -) - -var fqdnPattern = regexp.MustCompile(`^(.+)\.(.+)\.svc\.cluster\.local$`) diff --git a/pkg/config/analysis/analyzers/util/exportto.go b/pkg/config/analysis/analyzers/util/exportto.go deleted file mode 100644 index 37fe9cae6..000000000 --- a/pkg/config/analysis/analyzers/util/exportto.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -// IsExportToAllNamespaces returns true if export to applies to all namespaces -// and false if it is set to namespace local. -func IsExportToAllNamespaces(exportTos []string) bool { - exportedToAll := true - for _, e := range exportTos { - if e == ExportToNamespaceLocal { - exportedToAll = false - } else if e == ExportToAllNamespaces { - // give preference to "*" and stop iterating - exportedToAll = true - break - } - } - return exportedToAll -} diff --git a/pkg/config/analysis/analyzers/util/exportto_test.go b/pkg/config/analysis/analyzers/util/exportto_test.go deleted file mode 100644 index b3d97b253..000000000 --- a/pkg/config/analysis/analyzers/util/exportto_test.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -func TestIsExportToAllNamespaces(t *testing.T) { - g := NewWithT(t) - - // Empty array - g.Expect(IsExportToAllNamespaces(nil)).To(Equal(true)) - - // Array with "*" - g.Expect(IsExportToAllNamespaces([]string{"*"})).To(Equal(true)) - - // Array with "." - g.Expect(IsExportToAllNamespaces([]string{"."})).To(Equal(false)) - - // Array with "." & "*" - g.Expect(IsExportToAllNamespaces([]string{".", "*"})).To(Equal(true)) - - // Array with "bogus" - g.Expect(IsExportToAllNamespaces([]string{"bogus"})).To(Equal(true)) -} diff --git a/pkg/config/analysis/analyzers/util/find_errorline_utils.go b/pkg/config/analysis/analyzers/util/find_errorline_utils.go deleted file mode 100644 index 15d0f4428..000000000 --- a/pkg/config/analysis/analyzers/util/find_errorline_utils.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "fmt" - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/diag" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" -) - -const ( - // Path templates for different fields with different paths, may edited by future developers if not covered in this list - // Use the path template to find the exact line number for the field - - // Path for host in VirtualService. - // Required parameters: route rule, route rule index, route index. - DestinationHost = "{.spec.%s[%d].route[%d].destination.host}" - - // Path for mirror host in VirtualService. - // Required parameters: http index. - MirrorHost = "{.spec.http[%d].mirror.host}" - - // Path for VirtualService gateway. - // Required parameters: gateway index. - VSGateway = "{.spec.gateways[%d]}" - - // Path for regex match of uri, scheme, method and authority. - // Required parameters: http index, match index, where to match. - URISchemeMethodAuthorityRegexMatch = "{.spec.http[%d].match[%d].%s.regex}" - - // Path for regex match of headers and queryParams. - // Required parameters: http index, match index, where to match, match key. - HeaderAndQueryParamsRegexMatch = "{.spec.http[%d].match[%d].%s.%s.regex}" - - // Path for regex match of allowOrigins. - // Required parameters: http index, allowOrigins index. - AllowOriginsRegexMatch = "{.spec.http[%d].corsPolicy.allowOrigins[%d].regex}" - - // Path for workload selector. - // Required parameters: selector label. - WorkloadSelector = "{.spec.workloadSelector.labels.%s}" - - // Path for port from ports collections. - // Required parameters: port index. - PortInPorts = "{.spec.ports[%d].port}" - - // Path for fromRegistry in the mesh networks. - // Required parameters: network name, endPoint index. - FromRegistry = "{.networks.%s.endpoints[%d]}" - - // Path for the image in the container. - // Required parameters: container index. - ImageInContainer = "{.spec.containers[%d].image}" - - // Path for namespace in metadata. - // Required parameters: none. - MetadataNamespace = "{.metadata.namespace}" - - // Path for name in metadata. - // Required parameters: none. - MetadataName = "{.metadata.name}" - - // Path for namespace in authorizationPolicy. - // Required parameters: rule index, from index, namespace index. - AuthorizationPolicyNameSpace = "{.spec.rules[%d].from[%d].source.namespaces[%d]}" - - // Path for annotation. - // Required parameters: annotation name. - Annotation = "{.metadata.annotations.%s}" - - // Path for selector in Gateway. - // Required parameters: selector label. - GatewaySelector = "{.spec.selector.%s}" - - // Path for credentialName. - // Required parameters: server index. - CredentialName = "{.spec.servers[%d].tls.credentialName}" - - // Path for Port in ServiceEntry. - // Required parameters: port index. - ServiceEntryPort = "{.spec.ports[%d].name}" - - // Path for DestinationRule tls certificate. - // Required parameters: none. - DestinationRuleTLSCert = "{.spec.trafficPolicy.tls.caCertificates}" - - // Path for DestinationRule port-level tls certificate. - // Required parameters: portLevelSettings index. - DestinationRuleTLSPortLevelCert = "{.spec.trafficPolicy.portLevelSettings[%d].tls.caCertificates}" - - // Path for ConfigPatch in envoyFilter - // Required parameters: envoyFilter config patch index - EnvoyFilterConfigPath = "{.spec.configPatches[%d].patch.value}" -) - -// ErrorLine returns the line number of the input path key in the resource -func ErrorLine(r *resource.Instance, path string) (line int, found bool) { - fieldMap := r.Origin.FieldMap() - line, ok := fieldMap[path] - if !ok { - return 0, false - } - return line, true -} - -// ExtractLabelFromSelectorString returns the label of the match in the k8s labels.Selector -func ExtractLabelFromSelectorString(s string) string { - equalIndex := strings.Index(s, "=") - if equalIndex < 0 { - return "" - } - return s[:equalIndex] -} - -func AddLineNumber(r *resource.Instance, ann string, m diag.Message) bool { - if line, ok := ErrorLine(r, fmt.Sprintf(Annotation, ann)); ok { - m.Line = line - return true - } - return false -} diff --git a/pkg/config/analysis/analyzers/util/find_errorline_utils_test.go b/pkg/config/analysis/analyzers/util/find_errorline_utils_test.go deleted file mode 100644 index bd9fd71ed..000000000 --- a/pkg/config/analysis/analyzers/util/find_errorline_utils_test.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "fmt" - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -import ( - kube2 "github.com/apache/dubbo-go-pixiu/pkg/config/legacy/source/kube" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" -) - -var fieldMap = map[string]int{ - "{.metadata.name}": 1, - "{.metadata.namespace}": 1, - "{.metadata.annotations.test}": 1, - "{.spec.test[0].route[0].destination.host}": 1, - "{.spec.http[0].mirror.host}": 1, - "{.spec.gateways[0]}": 1, - "{.spec.http[0].match[0].test.regex}": 1, - "{.spec.http[0].match[0].test.test.regex}": 1, - "{.spec.http[0].corsPolicy.allowOrigins[0].regex}": 1, - "{.spec.workloadSelector.labels.test}": 1, - "{.spec.ports[0].port}": 1, - "{.spec.containers[0].image}": 1, - "{.spec.rules[0].from[0].source.namespaces[0]}": 1, - "{.spec.selector.test}": 1, - "{.spec.servers[0].tls.credentialName}": 1, - "{.networks.test.endpoints[0]}": 1, - "{.spec.trafficPolicy.tls.caCertificates}": 1, - "{.spec.trafficPolicy.portLevelSettings[0].tls.caCertificates}": 1, - "{.spec.configPatches[0].patch.value}": 1, -} - -func TestExtractLabelFromSelectorString(t *testing.T) { - g := NewWithT(t) - s := "label=test" - g.Expect(ExtractLabelFromSelectorString(s)).To(Equal("label")) -} - -func TestErrorLine(t *testing.T) { - g := NewWithT(t) - r := &resource.Instance{Origin: &kube2.Origin{FieldsMap: fieldMap}} - test1, err1 := ErrorLine(r, "{.metadata.name}") - test2, err2 := ErrorLine(r, "{.metadata.fake}") - g.Expect(test1).To(Equal(1)) - g.Expect(err1).To(Equal(true)) - g.Expect(test2).To(Equal(0)) - g.Expect(err2).To(Equal(false)) -} - -func TestConstants(t *testing.T) { - g := NewWithT(t) - - constantsPath := []string{ - fmt.Sprintf(DestinationHost, "test", 0, 0), - fmt.Sprintf(MirrorHost, 0), - fmt.Sprintf(VSGateway, 0), - fmt.Sprintf(URISchemeMethodAuthorityRegexMatch, 0, 0, "test"), - fmt.Sprintf(HeaderAndQueryParamsRegexMatch, 0, 0, "test", "test"), - fmt.Sprintf(AllowOriginsRegexMatch, 0, 0), - fmt.Sprintf(WorkloadSelector, "test"), - fmt.Sprintf(PortInPorts, 0), - fmt.Sprintf(FromRegistry, "test", 0), - fmt.Sprintf(ImageInContainer, 0), - fmt.Sprintf(AuthorizationPolicyNameSpace, 0, 0, 0), - fmt.Sprintf(Annotation, "test"), - fmt.Sprintf(GatewaySelector, "test"), - fmt.Sprintf(CredentialName, 0), - fmt.Sprintf(DestinationRuleTLSPortLevelCert, 0), - MetadataNamespace, - MetadataName, - DestinationRuleTLSCert, - fmt.Sprintf(EnvoyFilterConfigPath, 0), - } - - for _, v := range constantsPath { - g.Expect(fieldMap[v]).To(Equal(1)) - } -} diff --git a/pkg/config/analysis/analyzers/util/hosts.go b/pkg/config/analysis/analyzers/util/hosts.go deleted file mode 100644 index c4d2b009a..000000000 --- a/pkg/config/analysis/analyzers/util/hosts.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" -) - -type ScopedFqdn string - -// GetScopeAndFqdn splits ScopedFqdn back to scope namespace and fqdn parts -func (s ScopedFqdn) GetScopeAndFqdn() (string, string) { - parts := strings.SplitN(string(s), "/", 2) - return parts[0], parts[1] -} - -// InScopeOf returns true if ns is in the scope of ScopedFqdn -func (s ScopedFqdn) InScopeOf(ns string) bool { - scope, fqdn := s.GetScopeAndFqdn() - fn := GetFullNameFromFQDN(fqdn) - return scope == "*" || scope == "." && ns == fn.Namespace.String() || scope == ns -} - -// NewScopedFqdn converts the passed host to FQDN if needed and applies the passed scope. -func NewScopedFqdn(scope string, namespace resource.Namespace, host string) ScopedFqdn { - fqdn := ConvertHostToFQDN(namespace, host) - return ScopedFqdn(scope + "/" + fqdn) -} - -// GetResourceNameFromHost figures out the resource.FullName to look up from the provided host string -// We need to handle two possible formats: short name and FQDN -// https://istio.io/docs/reference/config/networking/v1alpha3/virtual-service/#Destination -func GetResourceNameFromHost(defaultNamespace resource.Namespace, host string) resource.FullName { - // First, try to parse as FQDN (which can be cross-namespace) - name := GetFullNameFromFQDN(host) - - // Otherwise, treat this as a short name and use the assumed namespace - if name.Namespace == "" { - name.Namespace = defaultNamespace - name.Name = resource.LocalName(host) - } - return name -} - -// GetFullNameFromFQDN tries to parse namespace and name from a fqdn. -// Empty strings are returned if either namespace or name cannot be parsed. -func GetFullNameFromFQDN(fqdn string) resource.FullName { - result := fqdnPattern.FindAllStringSubmatch(fqdn, -1) - if len(result) == 0 { - return resource.FullName{ - Namespace: "", - Name: "", - } - } - return resource.FullName{ - Namespace: resource.Namespace(result[0][2]), - Name: resource.LocalName(result[0][1]), - } -} - -// ConvertHostToFQDN returns the given host as a FQDN, if it isn't already. -func ConvertHostToFQDN(namespace resource.Namespace, host string) string { - fqdn := host - // Convert to FQDN only if host is not a wildcard or a FQDN - if !strings.HasPrefix(host, "*") && - !strings.Contains(host, ".") { - fqdn = host + "." + string(namespace) + "." + DefaultKubernetesDomain - } - return fqdn -} diff --git a/pkg/config/analysis/analyzers/util/hosts_test.go b/pkg/config/analysis/analyzers/util/hosts_test.go deleted file mode 100644 index 6d3375d75..000000000 --- a/pkg/config/analysis/analyzers/util/hosts_test.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" -) - -func TestGetResourceNameFromHost(t *testing.T) { - g := NewWithT(t) - - // FQDN, same namespace - g.Expect(GetResourceNameFromHost("default", "foo.default.svc.cluster.local")).To(Equal(resource.NewFullName("default", "foo"))) - // FQDN, cross namespace - g.Expect(GetResourceNameFromHost("default", "foo.other.svc.cluster.local")).To(Equal(resource.NewFullName("other", "foo"))) - // short name - g.Expect(GetResourceNameFromHost("default", "foo")).To(Equal(resource.NewFullName("default", "foo"))) - // bogus FQDN (gets treated like a short name) - g.Expect(GetResourceNameFromHost("default", "foo.svc.cluster.local")).To(Equal(resource.NewFullName("default", "foo.svc.cluster.local"))) -} - -func TestGetScopedFqdnHostname(t *testing.T) { - g := NewWithT(t) - - // FQDN, same namespace, local scope - g.Expect(NewScopedFqdn("default", "default", "foo.default.svc.cluster.local")).To(Equal(ScopedFqdn("default/foo.default.svc.cluster.local"))) - // FQDN, cross namespace, local scope - g.Expect(NewScopedFqdn("default", "other", "foo.default.svc.cluster.local")).To(Equal(ScopedFqdn("default/foo.default.svc.cluster.local"))) - // FQDN, same namespace, all namespaces scope - g.Expect(NewScopedFqdn("*", "default", "foo.default.svc.cluster.local")).To(Equal(ScopedFqdn("*/foo.default.svc.cluster.local"))) - // FQDN, cross namespace, all namespaces scope - g.Expect(NewScopedFqdn("*", "other", "foo.default.svc.cluster.local")).To(Equal(ScopedFqdn("*/foo.default.svc.cluster.local"))) - - // short name, same namespace, local scope - g.Expect(NewScopedFqdn("default", "default", "foo")).To(Equal(ScopedFqdn("default/foo.default.svc.cluster.local"))) - // short name, same namespace, all namespaces scope - g.Expect(NewScopedFqdn("*", "default", "foo")).To(Equal(ScopedFqdn("*/foo.default.svc.cluster.local"))) - - // wildcard, local scope - g.Expect(NewScopedFqdn("foo", "foo", "*")).To(Equal(ScopedFqdn("foo/*"))) - // wildcard sub domain, local scope - g.Expect(NewScopedFqdn("foo", "foo", "*.xyz.abc")).To(Equal(ScopedFqdn("foo/*.xyz.abc"))) - // wildcard, all namespaces scope - g.Expect(NewScopedFqdn("*", "foo", "*")).To(Equal(ScopedFqdn("*/*"))) - // wildcard sub domain, all namespaces scope - g.Expect(NewScopedFqdn("*", "foo", "*.xyz.abc")).To(Equal(ScopedFqdn("*/*.xyz.abc"))) - - // external host, local scope - g.Expect(NewScopedFqdn("foo", "foo", "xyz.abc")).To(Equal(ScopedFqdn("foo/xyz.abc"))) - // external host, all namespaces scope - g.Expect(NewScopedFqdn("*", "foo", "xyz.abc")).To(Equal(ScopedFqdn("*/xyz.abc"))) -} - -func TestScopedFqdn_GetScopeAndFqdn(t *testing.T) { - g := NewWithT(t) - - ns, fqdn := ScopedFqdn("default/reviews.default.svc.cluster.local").GetScopeAndFqdn() - g.Expect(ns).To(Equal("default")) - g.Expect(fqdn).To(Equal("reviews.default.svc.cluster.local")) - - ns, fqdn = ScopedFqdn("*/reviews.default.svc.cluster.local").GetScopeAndFqdn() - g.Expect(ns).To(Equal("*")) - g.Expect(fqdn).To(Equal("reviews.default.svc.cluster.local")) - - ns, fqdn = ScopedFqdn("foo/*.xyz.abc").GetScopeAndFqdn() - g.Expect(ns).To(Equal("foo")) - g.Expect(fqdn).To(Equal("*.xyz.abc")) -} - -func TestScopedFqdn_InScopeOf(t *testing.T) { - tests := []struct { - ScFqdn ScopedFqdn - Namespace string - Want bool - }{ - {"*/reviews.bookinfo.svc.cluster.local", "bookinfo", true}, - {"*/reviews.bookinfo.svc.cluster.local", "foo", true}, - {"./reviews.bookinfo.svc.cluster.local", "bookinfo", true}, - {"./reviews.bookinfo.svc.cluster.local", "foo", false}, - {"bookinfo/reviews.bookinfo.svc.cluster.local", "bookinfo", true}, - {"bookinfo/reviews.bookinfo.svc.cluster.local", "foo", false}, - } - - for _, test := range tests { - if test.ScFqdn.InScopeOf(test.Namespace) != test.Want { - t.Errorf("%s is in the scope of %s: %t. It should be %t", test.ScFqdn, test.Namespace, !test.Want, test.Want) - } - } -} diff --git a/pkg/config/analysis/analyzers/util/in_mesh.go b/pkg/config/analysis/analyzers/util/in_mesh.go deleted file mode 100644 index 044f7d10c..000000000 --- a/pkg/config/analysis/analyzers/util/in_mesh.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "istio.io/api/annotation" - apps_v1 "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// DeploymentinMesh returns true if deployment is in the service mesh (has sidecar) -func DeploymentInMesh(r *resource.Instance, c analysis.Context) bool { - d := r.Message.(*apps_v1.DeploymentSpec) - return inMesh(d.Template.Annotations, resource.Namespace(r.Metadata.FullName.Namespace.String()), d.Template.Spec.Containers, c) -} - -// PodInMesh returns true if a Pod is in the service mesh (has sidecar) -func PodInMesh(r *resource.Instance, c analysis.Context) bool { - p := r.Message.(*v1.PodSpec) - return inMesh(r.Metadata.Annotations, r.Metadata.FullName.Namespace, p.Containers, c) -} - -func inMesh(annos map[string]string, namespace resource.Namespace, containers []v1.Container, c analysis.Context) bool { - // If pod has the sidecar container set, then, the pod is in the mesh - if hasIstioProxy(containers) { - return true - } - - // If Pod has annotation, return the injection annotation value - if piv, pivok := getPodSidecarInjectionStatus(annos); pivok { - return piv - } - - // In case the annotation is not present but there is a auto-injection label on the namespace, - // return the auto-injection label status - if niv, nivok := getNamesSidecarInjectionStatus(namespace, c); nivok { - return niv - } - - return false -} - -// getPodSidecarInjectionStatus returns two booleans: enabled and ok. -// enabled is true when deployment d PodSpec has either the annotation 'sidecar.istio.io/inject: "true"' -// ok is true when the PodSpec doesn't have the 'sidecar.istio.io/inject' annotation present. -func getPodSidecarInjectionStatus(annos map[string]string) (enabled bool, ok bool) { - v, ok := annos[annotation.SidecarInject.Name] - return v == "true", ok -} - -// autoInjectionEnabled returns two booleans: enabled and ok. -// enabled is true when namespace ns has 'istio-injection' label set to 'enabled' -// ok is true when the namespace doesn't have the label 'istio-injection' -func getNamesSidecarInjectionStatus(ns resource.Namespace, c analysis.Context) (enabled bool, ok bool) { - enabled, ok = false, false - - namespace := c.Find(collections.K8SCoreV1Namespaces.Name(), resource.NewFullName("", resource.LocalName(ns))) - if namespace != nil { - enabled, ok = namespace.Metadata.Labels[InjectionLabelName] == InjectionLabelEnableValue, true - } - - return enabled, ok -} - -func hasIstioProxy(containers []v1.Container) bool { - proxyImage := "" - for _, container := range containers { - if container.Name == IstioProxyName { - proxyImage = container.Image - break - } - } - - return proxyImage != "" -} diff --git a/pkg/config/analysis/analyzers/util/service_lookup.go b/pkg/config/analysis/analyzers/util/service_lookup.go deleted file mode 100644 index fd4ea78b4..000000000 --- a/pkg/config/analysis/analyzers/util/service_lookup.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "strings" -) - -import ( - "istio.io/api/annotation" - "istio.io/api/networking/v1alpha3" - corev1 "k8s.io/api/core/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -func InitServiceEntryHostMap(ctx analysis.Context) map[ScopedFqdn]*v1alpha3.ServiceEntry { - result := make(map[ScopedFqdn]*v1alpha3.ServiceEntry) - - ctx.ForEach(collections.IstioNetworkingV1Alpha3Serviceentries.Name(), func(r *resource.Instance) bool { - s := r.Message.(*v1alpha3.ServiceEntry) - hostsNamespaceScope := string(r.Metadata.FullName.Namespace) - if IsExportToAllNamespaces(s.ExportTo) { - hostsNamespaceScope = ExportToAllNamespaces - } - for _, h := range s.GetHosts() { - result[NewScopedFqdn(hostsNamespaceScope, r.Metadata.FullName.Namespace, h)] = s - } - return true - }) - - // converts k8s service to serviceEntry since destinationHost - // validation is performed against serviceEntry - ctx.ForEach(collections.K8SCoreV1Services.Name(), func(r *resource.Instance) bool { - s := r.Message.(*corev1.ServiceSpec) - var se *v1alpha3.ServiceEntry - hostsNamespaceScope, ok := r.Metadata.Annotations[annotation.NetworkingExportTo.Name] - if !ok { - hostsNamespaceScope = ExportToAllNamespaces - } - var ports []*v1alpha3.Port - for _, p := range s.Ports { - ports = append(ports, &v1alpha3.Port{ - Number: uint32(p.Port), - Name: p.Name, - Protocol: string(p.Protocol), - }) - } - host := ConvertHostToFQDN(r.Metadata.FullName.Namespace, r.Metadata.FullName.Name.String()) - se = &v1alpha3.ServiceEntry{ - Hosts: []string{host}, - Ports: ports, - } - result[NewScopedFqdn(hostsNamespaceScope, r.Metadata.FullName.Namespace, r.Metadata.FullName.Name.String())] = se - return true - }) - return result -} - -func GetDestinationHost(sourceNs resource.Namespace, host string, serviceEntryHosts map[ScopedFqdn]*v1alpha3.ServiceEntry) *v1alpha3.ServiceEntry { - // Check explicitly defined ServiceEntries as well as services discovered from the platform - - // ServiceEntries can be either namespace scoped or exposed to all namespaces - nsScopedFqdn := NewScopedFqdn(string(sourceNs), sourceNs, host) - if s, ok := serviceEntryHosts[nsScopedFqdn]; ok { - return s - } - - // Check ServiceEntries which are exposed to all namespaces - allNsScopedFqdn := NewScopedFqdn(ExportToAllNamespaces, sourceNs, host) - if s, ok := serviceEntryHosts[allNsScopedFqdn]; ok { - return s - } - - // Now check wildcard matches, namespace scoped or all namespaces - // (This more expensive checking left for last) - // Assumes the wildcard entries are correctly formatted ("*") - for seHostScopedFqdn, s := range serviceEntryHosts { - scope, seHost := seHostScopedFqdn.GetScopeAndFqdn() - - // Skip over non-wildcard entries - if !strings.HasPrefix(seHost, Wildcard) { - continue - } - - // Skip over entries not visible to the current virtual service namespace - if scope != ExportToAllNamespaces && scope != string(sourceNs) { - continue - } - - seHostWithoutWildcard := strings.TrimPrefix(seHost, Wildcard) - hostWithoutWildCard := strings.TrimPrefix(host, Wildcard) - - if strings.HasSuffix(hostWithoutWildCard, seHostWithoutWildcard) { - return s - } - } - - return nil -} diff --git a/pkg/config/analysis/analyzers/virtualservice/conflictingmeshgatewayhosts.go b/pkg/config/analysis/analyzers/virtualservice/conflictingmeshgatewayhosts.go deleted file mode 100644 index 04017f95f..000000000 --- a/pkg/config/analysis/analyzers/virtualservice/conflictingmeshgatewayhosts.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package virtualservice - -import ( - "fmt" - "strings" -) - -import ( - "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// ConflictingMeshGatewayHostsAnalyzer checks if multiple virtual services -// associated with the mesh gateway have conflicting hosts. The behavior is -// undefined if conflicts exist. -type ConflictingMeshGatewayHostsAnalyzer struct{} - -var _ analysis.Analyzer = &ConflictingMeshGatewayHostsAnalyzer{} - -// Metadata implements Analyzer -func (c *ConflictingMeshGatewayHostsAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "virtualservice.ConflictingMeshGatewayHostsAnalyzer", - Description: "Checks if multiple virtual services associated with the mesh gateway have conflicting hosts", - Inputs: collection.Names{ - collections.IstioNetworkingV1Alpha3Virtualservices.Name(), - }, - } -} - -// Analyze implements Analyzer -func (c *ConflictingMeshGatewayHostsAnalyzer) Analyze(ctx analysis.Context) { - hs := initMeshGatewayHosts(ctx) - for scopedFqdn, vsList := range hs { - if len(vsList) > 1 { - vsNames := combineResourceEntryNames(vsList) - for i := range vsList { - - m := msg.NewConflictingMeshGatewayVirtualServiceHosts(vsList[i], vsNames, string(scopedFqdn)) - - if line, ok := util.ErrorLine(vsList[i], fmt.Sprintf(util.MetadataName)); ok { - m.Line = line - } - - ctx.Report(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), m) - } - } - } -} - -func combineResourceEntryNames(rList []*resource.Instance) string { - names := make([]string, 0, len(rList)) - for _, r := range rList { - names = append(names, r.Metadata.FullName.String()) - } - return strings.Join(names, ",") -} - -func initMeshGatewayHosts(ctx analysis.Context) map[util.ScopedFqdn][]*resource.Instance { - hostsVirtualServices := map[util.ScopedFqdn][]*resource.Instance{} - ctx.ForEach(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), func(r *resource.Instance) bool { - vs := r.Message.(*v1alpha3.VirtualService) - vsNamespace := r.Metadata.FullName.Namespace - vsAttachedToMeshGateway := false - // No entry in gateways imply "mesh" by default - if len(vs.Gateways) == 0 { - vsAttachedToMeshGateway = true - } else { - for _, g := range vs.Gateways { - if g == util.MeshGateway { - vsAttachedToMeshGateway = true - } - } - } - if vsAttachedToMeshGateway { - // determine the scope of hosts i.e. local to VirtualService namespace or - // all namespaces - hostsNamespaceScope := vsNamespace - exportToAllNamespaces := util.IsExportToAllNamespaces(vs.ExportTo) - if exportToAllNamespaces { - hostsNamespaceScope = util.ExportToAllNamespaces - } - - for _, h := range vs.Hosts { - scopedFqdn := util.NewScopedFqdn(string(hostsNamespaceScope), vsNamespace, h) - vsNames := hostsVirtualServices[scopedFqdn] - if len(vsNames) == 0 { - hostsVirtualServices[scopedFqdn] = []*resource.Instance{r} - } else { - hostsVirtualServices[scopedFqdn] = append(hostsVirtualServices[scopedFqdn], r) - } - } - } - return true - }) - return hostsVirtualServices -} diff --git a/pkg/config/analysis/analyzers/virtualservice/destinationhosts.go b/pkg/config/analysis/analyzers/virtualservice/destinationhosts.go deleted file mode 100644 index f0a1e19fe..000000000 --- a/pkg/config/analysis/analyzers/virtualservice/destinationhosts.go +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package virtualservice - -import ( - "fmt" -) - -import ( - "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// DestinationHostAnalyzer checks the destination hosts associated with each virtual service -type DestinationHostAnalyzer struct{} - -var _ analysis.Analyzer = &DestinationHostAnalyzer{} - -type hostAndSubset struct { - host resource.FullName - subset string -} - -// Metadata implements Analyzer -func (a *DestinationHostAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "virtualservice.DestinationHostAnalyzer", - Description: "Checks the destination hosts associated with each virtual service", - Inputs: collection.Names{ - collections.IstioNetworkingV1Alpha3Serviceentries.Name(), - collections.IstioNetworkingV1Alpha3Virtualservices.Name(), - collections.K8SCoreV1Services.Name(), - }, - } -} - -// Analyze implements Analyzer -func (a *DestinationHostAnalyzer) Analyze(ctx analysis.Context) { - // Precompute the set of service entry hosts that exist (there can be more than one defined per ServiceEntry CRD) - serviceEntryHosts := util.InitServiceEntryHostMap(ctx) - virtualServiceDestinations := initVirtualServiceDestinations(ctx) - - ctx.ForEach(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), func(r *resource.Instance) bool { - a.analyzeVirtualService(r, ctx, serviceEntryHosts) - a.analyzeSubset(r, ctx, virtualServiceDestinations) - return true - }) -} - -func (a *DestinationHostAnalyzer) analyzeSubset(r *resource.Instance, ctx analysis.Context, vsDestinations map[resource.FullName][]*v1alpha3.Destination) { - vs := r.Message.(*v1alpha3.VirtualService) - - // if there's no gateway specified, we're done - if len(vs.Gateways) == 0 { - return - } - - for ruleIndex, http := range vs.Http { - for routeIndex, route := range http.Route { - if route.Destination.Subset == "" { - for virtualservice, destinations := range vsDestinations { - for _, destination := range destinations { - if destination.Host == route.Destination.Host { - m := msg.NewIngressRouteRulesNotAffected(r, virtualservice.String(), r.Metadata.FullName.String()) - - key := fmt.Sprintf(util.DestinationHost, http.Name, ruleIndex, routeIndex) - if line, ok := util.ErrorLine(r, key); ok { - m.Line = line - } - - ctx.Report(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), m) - } - } - } - } - } - } -} - -// get all virtualservice that have destination with subset -func initVirtualServiceDestinations(ctx analysis.Context) map[resource.FullName][]*v1alpha3.Destination { - virtualservices := make(map[resource.FullName][]*v1alpha3.Destination) - - ctx.ForEach(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), func(r *resource.Instance) bool { - virtualservice := r.Message.(*v1alpha3.VirtualService) - for _, routes := range virtualservice.Http { - for _, destinations := range routes.Route { - // if there's no subset specified, we're done - if destinations.Destination.Subset != "" { - for _, host := range virtualservice.Hosts { - if destinations.Destination.Host == host { - virtualservices[r.Metadata.FullName] = append(virtualservices[r.Metadata.FullName], destinations.Destination) - } - } - } - } - } - - return true - }) - - return virtualservices -} - -func (a *DestinationHostAnalyzer) analyzeVirtualService(r *resource.Instance, ctx analysis.Context, - serviceEntryHosts map[util.ScopedFqdn]*v1alpha3.ServiceEntry) { - vs := r.Message.(*v1alpha3.VirtualService) - - for _, d := range getRouteDestinations(vs) { - s := util.GetDestinationHost(r.Metadata.FullName.Namespace, d.Destination.GetHost(), serviceEntryHosts) - if s == nil { - - m := msg.NewReferencedResourceNotFound(r, "host", d.Destination.GetHost()) - - key := fmt.Sprintf(util.DestinationHost, d.RouteRule, d.ServiceIndex, d.DestinationIndex) - if line, found := util.ErrorLine(r, key); found { - m.Line = line - } - - ctx.Report(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), m) - continue - } - checkServiceEntryPorts(ctx, r, d, s) - } - - for _, d := range getHTTPMirrorDestinations(vs) { - s := util.GetDestinationHost(r.Metadata.FullName.Namespace, d.Destination.GetHost(), serviceEntryHosts) - if s == nil { - - m := msg.NewReferencedResourceNotFound(r, "mirror host", d.Destination.GetHost()) - - key := fmt.Sprintf(util.MirrorHost, d.ServiceIndex) - if line, ok := util.ErrorLine(r, key); ok { - m.Line = line - } - - ctx.Report(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), m) - continue - } - checkServiceEntryPorts(ctx, r, d, s) - } -} - -func checkServiceEntryPorts(ctx analysis.Context, r *resource.Instance, d *AnnotatedDestination, s *v1alpha3.ServiceEntry) { - if d.Destination.GetPort() == nil { - // If destination port isn't specified, it's only a problem if the service being referenced exposes multiple ports. - if len(s.GetPorts()) > 1 { - var portNumbers []int - for _, p := range s.GetPorts() { - portNumbers = append(portNumbers, int(p.GetNumber())) - } - - m := msg.NewVirtualServiceDestinationPortSelectorRequired(r, d.Destination.GetHost(), portNumbers) - - if d.RouteRule == "http.mirror" { - key := fmt.Sprintf(util.MirrorHost, d.ServiceIndex) - if line, ok := util.ErrorLine(r, key); ok { - m.Line = line - } - } else { - key := fmt.Sprintf(util.DestinationHost, d.RouteRule, d.ServiceIndex, d.DestinationIndex) - if line, ok := util.ErrorLine(r, key); ok { - m.Line = line - } - } - - ctx.Report(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), m) - return - } - - // Otherwise, it's not needed and we're done here. - return - } - - foundPort := false - for _, p := range s.GetPorts() { - if d.Destination.GetPort().GetNumber() == p.GetNumber() { - foundPort = true - break - } - } - if !foundPort { - - m := msg.NewReferencedResourceNotFound(r, "host:port", - fmt.Sprintf("%s:%d", d.Destination.GetHost(), d.Destination.GetPort().GetNumber())) - - if d.RouteRule == "http.mirror" { - key := fmt.Sprintf(util.MirrorHost, d.ServiceIndex) - if line, ok := util.ErrorLine(r, key); ok { - m.Line = line - } - } else { - key := fmt.Sprintf(util.DestinationHost, d.RouteRule, d.ServiceIndex, d.DestinationIndex) - if line, ok := util.ErrorLine(r, key); ok { - m.Line = line - } - } - - ctx.Report(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), m) - } -} diff --git a/pkg/config/analysis/analyzers/virtualservice/destinationrules.go b/pkg/config/analysis/analyzers/virtualservice/destinationrules.go deleted file mode 100644 index fe28fd9f8..000000000 --- a/pkg/config/analysis/analyzers/virtualservice/destinationrules.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package virtualservice - -import ( - "fmt" -) - -import ( - "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// DestinationRuleAnalyzer checks the destination rules associated with each virtual service -type DestinationRuleAnalyzer struct{} - -var _ analysis.Analyzer = &DestinationRuleAnalyzer{} - -// Metadata implements Analyzer -func (d *DestinationRuleAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "virtualservice.DestinationRuleAnalyzer", - Description: "Checks the destination rules associated with each virtual service", - Inputs: collection.Names{ - collections.IstioNetworkingV1Alpha3Virtualservices.Name(), - collections.IstioNetworkingV1Alpha3Destinationrules.Name(), - }, - } -} - -// Analyze implements Analyzer -func (d *DestinationRuleAnalyzer) Analyze(ctx analysis.Context) { - // To avoid repeated iteration, precompute the set of existing destination host+subset combinations - destHostsAndSubsets := initDestHostsAndSubsets(ctx) - - ctx.ForEach(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), func(r *resource.Instance) bool { - d.analyzeVirtualService(r, ctx, destHostsAndSubsets) - return true - }) -} - -func (d *DestinationRuleAnalyzer) analyzeVirtualService(r *resource.Instance, ctx analysis.Context, - destHostsAndSubsets map[hostAndSubset]bool) { - vs := r.Message.(*v1alpha3.VirtualService) - ns := r.Metadata.FullName.Namespace - - for _, ad := range getRouteDestinations(vs) { - if !d.checkDestinationSubset(ns, ad.Destination, destHostsAndSubsets) { - - m := msg.NewReferencedResourceNotFound(r, "host+subset in destinationrule", - fmt.Sprintf("%s+%s", ad.Destination.GetHost(), ad.Destination.GetSubset())) - - key := fmt.Sprintf(util.DestinationHost, ad.RouteRule, ad.ServiceIndex, ad.DestinationIndex) - if line, ok := util.ErrorLine(r, key); ok { - m.Line = line - } - - ctx.Report(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), m) - } - } - - for _, ad := range getHTTPMirrorDestinations(vs) { - if !d.checkDestinationSubset(ns, ad.Destination, destHostsAndSubsets) { - - m := msg.NewReferencedResourceNotFound(r, "mirror+subset in destinationrule", - fmt.Sprintf("%s+%s", ad.Destination.GetHost(), ad.Destination.GetSubset())) - - key := fmt.Sprintf(util.MirrorHost, ad.ServiceIndex) - if line, ok := util.ErrorLine(r, key); ok { - m.Line = line - } - - ctx.Report(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), m) - } - } -} - -func (d *DestinationRuleAnalyzer) checkDestinationSubset(vsNamespace resource.Namespace, destination *v1alpha3.Destination, - destHostsAndSubsets map[hostAndSubset]bool) bool { - name := util.GetResourceNameFromHost(vsNamespace, destination.GetHost()) - subset := destination.GetSubset() - - // if there's no subset specified, we're done - if subset == "" { - return true - } - - hs := hostAndSubset{ - host: name, - subset: subset, - } - if _, ok := destHostsAndSubsets[hs]; ok { - return true - } - - return false -} - -func initDestHostsAndSubsets(ctx analysis.Context) map[hostAndSubset]bool { - hostsAndSubsets := make(map[hostAndSubset]bool) - ctx.ForEach(collections.IstioNetworkingV1Alpha3Destinationrules.Name(), func(r *resource.Instance) bool { - dr := r.Message.(*v1alpha3.DestinationRule) - drNamespace := r.Metadata.FullName.Namespace - - for _, ss := range dr.GetSubsets() { - hs := hostAndSubset{ - host: util.GetResourceNameFromHost(drNamespace, dr.GetHost()), - subset: ss.GetName(), - } - hostsAndSubsets[hs] = true - } - return true - }) - return hostsAndSubsets -} diff --git a/pkg/config/analysis/analyzers/virtualservice/gateways.go b/pkg/config/analysis/analyzers/virtualservice/gateways.go deleted file mode 100644 index b5d8da9df..000000000 --- a/pkg/config/analysis/analyzers/virtualservice/gateways.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package virtualservice - -import ( - "fmt" - "strings" -) - -import ( - "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// GatewayAnalyzer checks the gateways associated with each virtual service -type GatewayAnalyzer struct{} - -var _ analysis.Analyzer = &GatewayAnalyzer{} - -// Metadata implements Analyzer -func (s *GatewayAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "virtualservice.GatewayAnalyzer", - Description: "Checks the gateways associated with each virtual service", - Inputs: collection.Names{ - collections.IstioNetworkingV1Alpha3Gateways.Name(), - collections.IstioNetworkingV1Alpha3Virtualservices.Name(), - }, - } -} - -// Analyze implements Analyzer -func (s *GatewayAnalyzer) Analyze(c analysis.Context) { - c.ForEach(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), func(r *resource.Instance) bool { - s.analyzeVirtualService(r, c) - return true - }) -} - -func (s *GatewayAnalyzer) analyzeVirtualService(r *resource.Instance, c analysis.Context) { - vs := r.Message.(*v1alpha3.VirtualService) - vsNs := r.Metadata.FullName.Namespace - vsName := r.Metadata.FullName - - for i, gwName := range vs.Gateways { - // This is a special-case accepted value - if gwName == util.MeshGateway { - continue - } - - gwFullName := resource.NewShortOrFullName(vsNs, gwName) - - if !c.Exists(collections.IstioNetworkingV1Alpha3Gateways.Name(), gwFullName) { - m := msg.NewReferencedResourceNotFound(r, "gateway", gwName) - - if line, ok := util.ErrorLine(r, fmt.Sprintf(util.VSGateway, i)); ok { - m.Line = line - } - - c.Report(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), m) - } - - if !vsHostInGateway(c, gwFullName, vs.Hosts, vsNs.String()) { - m := msg.NewVirtualServiceHostNotFoundInGateway(r, vs.Hosts, vsName.String(), gwFullName.String()) - - if line, ok := util.ErrorLine(r, fmt.Sprintf(util.VSGateway, i)); ok { - m.Line = line - } - - c.Report(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), m) - } - } -} - -func vsHostInGateway(c analysis.Context, gateway resource.FullName, vsHosts []string, vsNamespace string) bool { - var gatewayHosts []string - var gatewayNs string - - c.ForEach(collections.IstioNetworkingV1Alpha3Gateways.Name(), func(r *resource.Instance) bool { - if r.Metadata.FullName == gateway { - s := r.Message.(*v1alpha3.Gateway) - gatewayNs = r.Metadata.FullName.Namespace.String() - for _, v := range s.Servers { - sanitizeServerHostNamespace(v, gatewayNs) - gatewayHosts = append(gatewayHosts, v.Hosts...) - } - } - - return true - }) - - gatewayHostNames := host.NamesForNamespace(gatewayHosts, vsNamespace) - for _, gh := range gatewayHostNames { - for _, vsh := range vsHosts { - gatewayHost := gh - vsHost := host.Name(vsh) - - if gatewayHost.Matches(vsHost) { - return true - } - } - } - - return false -} - -// convert ./host to currentNamespace/Host -// */host to just host -// */* to just * -func sanitizeServerHostNamespace(server *v1alpha3.Server, namespace string) { - for i, h := range server.Hosts { - if strings.Contains(h, "/") { - parts := strings.Split(h, "/") - if parts[0] == "." { - server.Hosts[i] = fmt.Sprintf("%s/%s", namespace, parts[1]) - } else if parts[0] == "*" { - if parts[1] == "*" { - server.Hosts = []string{"*"} - return - } - server.Hosts[i] = parts[1] - } - } - } -} diff --git a/pkg/config/analysis/analyzers/virtualservice/jwtclaimroute.go b/pkg/config/analysis/analyzers/virtualservice/jwtclaimroute.go deleted file mode 100644 index c36d99f22..000000000 --- a/pkg/config/analysis/analyzers/virtualservice/jwtclaimroute.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package virtualservice - -import ( - "strings" -) - -import ( - "istio.io/api/networking/v1alpha3" - "istio.io/api/security/v1beta1" - k8s_labels "k8s.io/apimachinery/pkg/labels" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/util/constant" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -type JWTClaimRouteAnalyzer struct{} - -var _ analysis.Analyzer = &JWTClaimRouteAnalyzer{} - -// Metadata implements Analyzer -func (s *JWTClaimRouteAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "virtualservice.JWTClaimRouteAnalyzer", - Description: "Checks the VirtualService using JWT claim based routing has corresponding RequestAuthentication", - Inputs: collection.Names{ - collections.IstioNetworkingV1Alpha3Virtualservices.Name(), - collections.IstioSecurityV1Beta1Requestauthentications.Name(), - collections.IstioNetworkingV1Alpha3Gateways.Name(), - collections.K8SCoreV1Pods.Name(), - }, - } -} - -// Analyze implements Analyzer -func (s *JWTClaimRouteAnalyzer) Analyze(c analysis.Context) { - requestAuthNByNamespace := map[string][]k8s_labels.Selector{} - c.ForEach(collections.IstioSecurityV1Beta1Requestauthentications.Name(), func(r *resource.Instance) bool { - ns := r.Metadata.FullName.Namespace.String() - if _, found := requestAuthNByNamespace[ns]; !found { - requestAuthNByNamespace[ns] = []k8s_labels.Selector{} - } - ra := r.Message.(*v1beta1.RequestAuthentication) - raSelector := k8s_labels.SelectorFromSet(ra.GetSelector().GetMatchLabels()) - requestAuthNByNamespace[ns] = append(requestAuthNByNamespace[ns], raSelector) - return true - }) - - c.ForEach(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), func(r *resource.Instance) bool { - s.analyze(r, c, requestAuthNByNamespace) - return true - }) -} - -func (s *JWTClaimRouteAnalyzer) analyze(r *resource.Instance, c analysis.Context, requestAuthNByNamespace map[string][]k8s_labels.Selector) { - // Check if the virtual service is using JWT claim based routing. - vs := r.Message.(*v1alpha3.VirtualService) - var vsRouteKey string - if vsRouteKey = routeBasedOnJWTClaimKey(vs); vsRouteKey == "" { - return - } - vsNs := r.Metadata.FullName.Namespace - - // Check if the virtual service is applied to gateway. - for _, gwName := range vs.Gateways { - if gwName == util.MeshGateway { - continue - } - - gwFullName := resource.NewShortOrFullName(vsNs, gwName) - gwRes := c.Find(collections.IstioNetworkingV1Alpha3Gateways.Name(), gwFullName) - if gwRes == nil { - // The gateway does not exist, this should already be covered by the gateway analyzer. - continue - } - - gw := gwRes.Message.(*v1alpha3.Gateway) - gwSelector := k8s_labels.SelectorFromSet(gw.Selector) - - // Check each pod selected by the gateway. - c.ForEach(collections.K8SCoreV1Pods.Name(), func(rPod *resource.Instance) bool { - podLabels := k8s_labels.Set(rPod.Metadata.Labels) - if !gwSelector.Matches(podLabels) { - return true - } - - // Check if there is request authentication applied to the pod. - var hasRequestAuthNForPod bool - - raSelectors := requestAuthNByNamespace[constants.IstioSystemNamespace] - raSelectors = append(raSelectors, requestAuthNByNamespace[rPod.Metadata.FullName.Namespace.String()]...) - for _, raSelector := range raSelectors { - if raSelector.Matches(podLabels) { - hasRequestAuthNForPod = true - break - } - } - if !hasRequestAuthNForPod { - m := msg.NewJwtClaimBasedRoutingWithoutRequestAuthN(r, vsRouteKey, gwFullName.String(), rPod.Metadata.FullName.Name.String()) - c.Report(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), m) - } - return true - }) - } -} - -func routeBasedOnJWTClaimKey(vs *v1alpha3.VirtualService) string { - for _, httpRoute := range vs.GetHttp() { - for _, match := range httpRoute.GetMatch() { - for key := range match.GetHeaders() { - if strings.HasPrefix(key, constant.HeaderJWTClaim) { - return key - } - } - for key := range match.GetWithoutHeaders() { - if strings.HasPrefix(key, constant.HeaderJWTClaim) { - return key - } - } - } - } - return "" -} diff --git a/pkg/config/analysis/analyzers/virtualservice/regexes.go b/pkg/config/analysis/analyzers/virtualservice/regexes.go deleted file mode 100644 index 5d3a18f95..000000000 --- a/pkg/config/analysis/analyzers/virtualservice/regexes.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package virtualservice - -import ( - "fmt" - "regexp" -) - -import ( - "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// RegexAnalyzer checks all regexes in a virtual service -type RegexAnalyzer struct{} - -var _ analysis.Analyzer = &RegexAnalyzer{} - -// Metadata implements Analyzer -func (a *RegexAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "virtualservice.RegexAnalyzer", - Description: "Checks regex syntax", - Inputs: collection.Names{ - collections.IstioNetworkingV1Alpha3Virtualservices.Name(), - }, - } -} - -// Analyze implements Analyzer -func (a *RegexAnalyzer) Analyze(ctx analysis.Context) { - ctx.ForEach(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), func(r *resource.Instance) bool { - a.analyzeVirtualService(r, ctx) - return true - }) -} - -func (a *RegexAnalyzer) analyzeVirtualService(r *resource.Instance, ctx analysis.Context) { - vs := r.Message.(*v1alpha3.VirtualService) - - for i, route := range vs.GetHttp() { - for j, m := range route.GetMatch() { - - analyzeStringMatch(r, m.GetUri(), ctx, "uri", - fmt.Sprintf(util.URISchemeMethodAuthorityRegexMatch, i, j, "uri")) - analyzeStringMatch(r, m.GetScheme(), ctx, "scheme", - fmt.Sprintf(util.URISchemeMethodAuthorityRegexMatch, i, j, "scheme")) - analyzeStringMatch(r, m.GetMethod(), ctx, "method", - fmt.Sprintf(util.URISchemeMethodAuthorityRegexMatch, i, j, "method")) - analyzeStringMatch(r, m.GetAuthority(), ctx, "authority", - fmt.Sprintf(util.URISchemeMethodAuthorityRegexMatch, i, j, "authority")) - for key, h := range m.GetHeaders() { - analyzeStringMatch(r, h, ctx, "headers", - fmt.Sprintf(util.HeaderAndQueryParamsRegexMatch, i, j, "headers", key)) - } - for key, qp := range m.GetQueryParams() { - analyzeStringMatch(r, qp, ctx, "queryParams", - fmt.Sprintf(util.HeaderAndQueryParamsRegexMatch, i, j, "queryParams", key)) - } - // We don't validate withoutHeaders, because they are undocumented - } - for j, origin := range route.GetCorsPolicy().GetAllowOrigins() { - analyzeStringMatch(r, origin, ctx, "corsPolicy.allowOrigins", - fmt.Sprintf(util.AllowOriginsRegexMatch, i, j)) - } - } -} - -func analyzeStringMatch(r *resource.Instance, sm *v1alpha3.StringMatch, ctx analysis.Context, where string, key string) { - re := sm.GetRegex() - if re == "" { - return - } - - _, err := regexp.Compile(re) - if err == nil { - return - } - - m := msg.NewInvalidRegexp(r, where, re, err.Error()) - - // Get line number for different match field - if line, ok := util.ErrorLine(r, key); ok { - m.Line = line - } - - ctx.Report(collections.IstioNetworkingV1Alpha3Virtualservices.Name(), m) -} diff --git a/pkg/config/analysis/analyzers/virtualservice/util.go b/pkg/config/analysis/analyzers/virtualservice/util.go deleted file mode 100644 index af04aec7d..000000000 --- a/pkg/config/analysis/analyzers/virtualservice/util.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package virtualservice - -import ( - "istio.io/api/networking/v1alpha3" -) - -// AnnotatedDestination holds metadata about a Destination object that is used for analyzing -type AnnotatedDestination struct { - RouteRule string - ServiceIndex int - DestinationIndex int - Destination *v1alpha3.Destination -} - -func getRouteDestinations(vs *v1alpha3.VirtualService) []*AnnotatedDestination { - destinations := make([]*AnnotatedDestination, 0) - for i, r := range vs.GetTcp() { - for j, rd := range r.GetRoute() { - destinations = append(destinations, &AnnotatedDestination{ - RouteRule: "tcp", - ServiceIndex: i, - DestinationIndex: j, - Destination: rd.GetDestination(), - }) - } - } - for i, r := range vs.GetTls() { - for j, rd := range r.GetRoute() { - destinations = append(destinations, &AnnotatedDestination{ - RouteRule: "tls", - ServiceIndex: i, - DestinationIndex: j, - Destination: rd.GetDestination(), - }) - } - } - for i, r := range vs.GetHttp() { - for j, rd := range r.GetRoute() { - destinations = append(destinations, &AnnotatedDestination{ - RouteRule: "http", - ServiceIndex: i, - DestinationIndex: j, - Destination: rd.GetDestination(), - }) - } - } - - return destinations -} - -func getHTTPMirrorDestinations(vs *v1alpha3.VirtualService) []*AnnotatedDestination { - var destinations []*AnnotatedDestination - - for i, r := range vs.GetHttp() { - if m := r.GetMirror(); m != nil { - destinations = append(destinations, &AnnotatedDestination{ - RouteRule: "http.mirror", - ServiceIndex: i, - Destination: m, - }) - } - } - - return destinations -} diff --git a/pkg/config/analysis/analyzers/webhook/webhook.go b/pkg/config/analysis/analyzers/webhook/webhook.go deleted file mode 100644 index d0c095379..000000000 --- a/pkg/config/analysis/analyzers/webhook/webhook.go +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package webhook - -import ( - "fmt" - "strings" -) - -import ( - "istio.io/api/label" - v1 "k8s.io/api/admissionregistration/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - klabels "k8s.io/apimachinery/pkg/labels" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -var ( - webhookCol = collections.K8SAdmissionregistrationK8SIoV1Mutatingwebhookconfigurations.Name() - serviceCol = collections.K8SCoreV1Services.Name() -) - -type Analyzer struct { - SkipServiceCheck bool -} - -var _ analysis.Analyzer = &Analyzer{} - -func (a *Analyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "webhook.Analyzer", - Description: "Checks the validity of Istio webhooks", - Inputs: collection.Names{ - webhookCol, - serviceCol, - }, - } -} - -func getNamespaceLabels() []klabels.Set { - return []klabels.Set{ - {}, - {"istio-injection": "enabled"}, - {"istio-injection": "disabled"}, - } -} - -func getObjectLabels() []klabels.Set { - return []klabels.Set{ - {}, - {"sidecar.istio.io/inject": "true"}, - {"sidecar.istio.io/inject": "false"}, - } -} - -func (a *Analyzer) Analyze(context analysis.Context) { - // First, extract and index all webhooks we found - webhooks := map[string][]v1.MutatingWebhook{} - resources := map[string]*resource.Instance{} - revisions := sets.New() - context.ForEach(webhookCol, func(resource *resource.Instance) bool { - wh := resource.Message.(*v1.MutatingWebhookConfiguration) - revs := extractRevisions(wh) - if len(revs) == 0 && !isIstioWebhook(wh) { - return true - } - webhooks[resource.Metadata.FullName.String()] = wh.Webhooks - for _, h := range wh.Webhooks { - resources[fmt.Sprintf("%v/%v", resource.Metadata.FullName.String(), h.Name)] = resource - } - revisions.InsertAll(revs...) - return true - }) - - // Set up all relevant namespace and object selector permutations - namespaceLabels := getNamespaceLabels() - for rev := range revisions { - for _, base := range getNamespaceLabels() { - base[label.IoIstioRev.Name] = rev - namespaceLabels = append(namespaceLabels, base) - } - } - objectLabels := getObjectLabels() - for rev := range revisions { - for _, base := range getObjectLabels() { - base[label.IoIstioRev.Name] = rev - objectLabels = append(objectLabels, base) - } - } - - // For each permutation, we check which webhooks it matches. It must match exactly 0 or 1! - for _, nl := range namespaceLabels { - for _, ol := range objectLabels { - matches := sets.New() - for name, whs := range webhooks { - for _, wh := range whs { - if selectorMatches(wh.NamespaceSelector, nl) && selectorMatches(wh.ObjectSelector, ol) { - matches.Insert(fmt.Sprintf("%v/%v", name, wh.Name)) - } - } - } - if len(matches) > 1 { - for match := range matches { - others := matches.Difference(sets.New(match)) - context.Report(webhookCol, msg.NewInvalidWebhook(resources[match], - fmt.Sprintf("Webhook overlaps with others: %v. This may cause injection to occur twice.", others.UnsortedList()))) - } - } - } - } - - // Next, check service references - if a.SkipServiceCheck { - return - } - for name, whs := range webhooks { - for _, wh := range whs { - if wh.ClientConfig.Service == nil { - // it is an url, skip it - continue - } - fname := resource.NewFullName( - resource.Namespace(wh.ClientConfig.Service.Namespace), - resource.LocalName(wh.ClientConfig.Service.Name)) - if !context.Exists(serviceCol, fname) { - context.Report(webhookCol, msg.NewInvalidWebhook(resources[fmt.Sprintf("%v/%v", name, wh.Name)], - fmt.Sprintf("Injector refers to a control plane service that does not exist: %v.", fname))) - } - } - } -} - -func isIstioWebhook(wh *v1.MutatingWebhookConfiguration) bool { - for _, w := range wh.Webhooks { - if strings.HasSuffix(w.Name, "istio.io") { - return true - } - } - return false -} - -func extractRevisions(wh *v1.MutatingWebhookConfiguration) []string { - revs := sets.New() - if r, f := wh.Labels[label.IoIstioRev.Name]; f { - revs.Insert(r) - } - for _, webhook := range wh.Webhooks { - if webhook.NamespaceSelector != nil { - if r, f := webhook.NamespaceSelector.MatchLabels[label.IoIstioRev.Name]; f { - revs.Insert(r) - } - - for _, ls := range webhook.NamespaceSelector.MatchExpressions { - if ls.Key == label.IoIstioRev.Name { - revs.InsertAll(ls.Values...) - } - } - } - if webhook.ObjectSelector != nil { - if r, f := webhook.ObjectSelector.MatchLabels[label.IoIstioRev.Name]; f { - revs.Insert(r) - } - - for _, ls := range webhook.ObjectSelector.MatchExpressions { - if ls.Key == label.IoIstioRev.Name { - revs.InsertAll(ls.Values...) - } - } - } - } - return revs.UnsortedList() -} - -func selectorMatches(selector *metav1.LabelSelector, labels klabels.Set) bool { - // From webhook spec: "Default to the empty LabelSelector, which matches everything." - if selector == nil { - return true - } - s, err := metav1.LabelSelectorAsSelector(selector) - if err != nil { - return false - } - return s.Matches(labels) -} diff --git a/pkg/config/analysis/context.go b/pkg/config/analysis/context.go deleted file mode 100644 index 5104e9e38..000000000 --- a/pkg/config/analysis/context.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package analysis - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/diag" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" -) - -// IteratorFn is used to iterate over a set of collection entries. It must return true to keep iterating. -type IteratorFn func(r *resource.Instance) bool - -// Context is an analysis context that is passed to individual analyzers. -type Context interface { - // Report a diagnostic message - Report(c collection.Name, t diag.Message) - - // Find a resource in the collection. If not found, nil is returned - Find(c collection.Name, name resource.FullName) *resource.Instance - - // Exists returns true if the specified resource exists in the context, false otherwise - Exists(c collection.Name, name resource.FullName) bool - - // ForEach iterates over all the entries of a given collection. - ForEach(c collection.Name, fn IteratorFn) - - // Canceled indicates that the context has been canceled. The analyzer should stop executing as soon as possible. - Canceled() bool -} diff --git a/pkg/config/analysis/diag/helper.go b/pkg/config/analysis/diag/helper.go deleted file mode 100644 index 11fecd268..000000000 --- a/pkg/config/analysis/diag/helper.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package diag - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" -) - -var ( - _ resource.Origin = &testOrigin{} - _ resource.Reference = &testReference{} -) - -type testOrigin struct { - name string - ref resource.Reference - fieldMap map[string]int -} - -func (o testOrigin) FriendlyName() string { - return o.name -} - -func (o testOrigin) Comparator() string { - return o.name -} - -func (o testOrigin) Namespace() resource.Namespace { - return "" -} - -func (o testOrigin) Reference() resource.Reference { - return o.ref -} - -func (o testOrigin) FieldMap() map[string]int { - return o.fieldMap -} - -type testReference struct { - name string -} - -func (r testReference) String() string { - return r.name -} - -func MockResource(name string) *resource.Instance { - return &resource.Instance{ - Metadata: resource.Metadata{ - FullName: resource.NewShortOrFullName("default", name), - }, - Origin: testOrigin{name: name}, - } -} diff --git a/pkg/config/analysis/diag/level.go b/pkg/config/analysis/diag/level.go deleted file mode 100644 index 6373c57f5..000000000 --- a/pkg/config/analysis/diag/level.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package diag - -import ( - "strings" -) - -// Level is the severity level of a message. -type Level struct { - sortOrder int - name string -} - -func (l Level) String() string { - return l.name -} - -func (l Level) IsWorseThanOrEqualTo(target Level) bool { - return l.sortOrder <= target.sortOrder -} - -var ( - // Info level is for informational messages - Info = Level{2, "Info"} - - // Warning level is for warning messages - Warning = Level{1, "Warning"} - - // Error level is for error messages - Error = Level{0, "Error"} -) - -// GetAllLevels returns an arbitrarily ordered slice of all Levels defined. -func GetAllLevels() []Level { - return []Level{Info, Warning, Error} -} - -// GetAllLevelStrings returns a list of strings representing the names of all Levels defined. The order is arbitrary but -// should be the same as GetAllLevels. -func GetAllLevelStrings() []string { - levels := GetAllLevels() - var s []string - for _, l := range levels { - s = append(s, l.name) - } - return s -} - -// GetUppercaseStringToLevelMap returns a mapping of uppercase strings to Level structs. This function is intended to be -// used to convert user input to structs. -func GetUppercaseStringToLevelMap() map[string]Level { - m := make(map[string]Level) - for _, l := range GetAllLevels() { - m[strings.ToUpper(l.name)] = l - } - return m -} diff --git a/pkg/config/analysis/diag/message.go b/pkg/config/analysis/diag/message.go deleted file mode 100644 index 117463ebc..000000000 --- a/pkg/config/analysis/diag/message.go +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package diag - -import ( - "encoding/json" - "fmt" - "strconv" - "strings" -) - -import ( - "istio.io/api/analysis/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/url" -) - -// MessageType is a type of diagnostic message -type MessageType struct { - // The level of the message. - level Level - - // The error code of the message - code string - - // TODO: Make this localizable - template string -} - -// Level returns the level of the MessageType -func (m *MessageType) Level() Level { return m.level } - -// Code returns the code of the MessageType -func (m *MessageType) Code() string { return m.code } - -// Template returns the message template used by the MessageType -func (m *MessageType) Template() string { return m.template } - -// Message is a specific diagnostic message -// TODO: Implement using Analysis message API -type Message struct { - Type *MessageType - - // The Parameters to the message - Parameters []interface{} - - // Resource is the underlying resource instance associated with the - // message, or nil if no resource is associated with it. - Resource *resource.Instance - - // DocRef is an optional reference tracker for the documentation URL - DocRef string - - // Line is the line number of the error place in the message - Line int -} - -// Unstructured returns this message as a JSON-style unstructured map -func (m *Message) Unstructured(includeOrigin bool) map[string]interface{} { - result := make(map[string]interface{}) - - result["code"] = m.Type.Code() - result["level"] = m.Type.Level().String() - if includeOrigin && m.Resource != nil { - result["origin"] = m.Resource.Origin.FriendlyName() - if m.Resource.Origin.Reference() != nil { - loc := m.Resource.Origin.Reference().String() - if m.Line != 0 { - loc = m.ReplaceLine(loc) - } - result["reference"] = loc - } - } - result["message"] = fmt.Sprintf(m.Type.Template(), m.Parameters...) - - docQueryString := "" - if m.DocRef != "" { - docQueryString = fmt.Sprintf("?ref=%s", m.DocRef) - } - result["documentationUrl"] = fmt.Sprintf("%s/%s/%s", url.ConfigAnalysis, strings.ToLower(m.Type.Code()), docQueryString) - - return result -} - -func (m *Message) AnalysisMessageBase() *v1alpha1.AnalysisMessageBase { - docQueryString := "" - if m.DocRef != "" { - docQueryString = fmt.Sprintf("?ref=%s", m.DocRef) - } - docURL := fmt.Sprintf("%s/%s/%s", url.ConfigAnalysis, strings.ToLower(m.Type.Code()), docQueryString) - - return &v1alpha1.AnalysisMessageBase{ - DocumentationUrl: docURL, - Level: v1alpha1.AnalysisMessageBase_Level(v1alpha1.AnalysisMessageBase_Level_value[strings.ToUpper(m.Type.Level().String())]), - Type: &v1alpha1.AnalysisMessageBase_Type{ - Code: m.Type.Code(), - }, - } -} - -// UnstructuredAnalysisMessageBase returns this message as a JSON-style unstructured map in AnalaysisMessageBase -// TODO(jasonwzm): Remove once message implements AnalysisMessageBase -func (m *Message) UnstructuredAnalysisMessageBase() map[string]interface{} { - mb := m.AnalysisMessageBase() - - var r map[string]interface{} - - j, err := json.Marshal(mb) - if err != nil { - return r - } - json.Unmarshal(j, &r) // nolint: errcheck - - return r -} - -// Origin returns the origin of the message -func (m *Message) Origin() string { - origin := "" - if m.Resource != nil { - loc := "" - if m.Resource.Origin.Reference() != nil { - loc = " " + m.Resource.Origin.Reference().String() - if m.Line != 0 { - loc = m.ReplaceLine(loc) - } - } - origin = " (" + m.Resource.Origin.FriendlyName() + loc + ")" - } - return origin -} - -// String implements io.Stringer -func (m *Message) String() string { - return fmt.Sprintf("%v [%v]%s %s", - m.Type.Level(), m.Type.Code(), m.Origin(), - fmt.Sprintf(m.Type.Template(), m.Parameters...)) -} - -// MarshalJSON satisfies the Marshaler interface -func (m *Message) MarshalJSON() ([]byte, error) { - return json.Marshal(m.Unstructured(true)) -} - -// NewMessageType returns a new MessageType instance. -func NewMessageType(level Level, code, template string) *MessageType { - return &MessageType{ - level: level, - code: code, - template: template, - } -} - -// NewMessage returns a new Message instance from an existing type. -func NewMessage(mt *MessageType, r *resource.Instance, p ...interface{}) Message { - return Message{ - Type: mt, - Resource: r, - Parameters: p, - } -} - -// ReplaceLine replaces the line number from the input String method of Reference to the line number from Message -func (m Message) ReplaceLine(l string) string { - colonSep := strings.Split(l, ":") - if len(colonSep) < 2 { - return l - } - _, err := strconv.Atoi(strings.TrimSpace(colonSep[len(colonSep)-1])) - if err == nil { - colonSep[len(colonSep)-1] = fmt.Sprintf("%d", m.Line) - } - return strings.Join(colonSep, ":") -} diff --git a/pkg/config/analysis/diag/message_test.go b/pkg/config/analysis/diag/message_test.go deleted file mode 100644 index 0c09b480e..000000000 --- a/pkg/config/analysis/diag/message_test.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package diag - -import ( - "encoding/json" - "fmt" - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/url" -) - -func TestMessage_String(t *testing.T) { - g := NewWithT(t) - mt := NewMessageType(Error, "IST-0042", "Cheese type not found: %q") - m := NewMessage(mt, nil, "Feta") - - g.Expect(m.String()).To(Equal(`Error [IST-0042] Cheese type not found: "Feta"`)) -} - -func TestMessageWithResource_String(t *testing.T) { - g := NewWithT(t) - mt := NewMessageType(Error, "IST-0042", "Cheese type not found: %q") - m := NewMessage(mt, &resource.Instance{Origin: testOrigin{name: "toppings/cheese", ref: testReference{"path/to/file"}}}, "Feta") - - g.Expect(m.String()).To(Equal(`Error [IST-0042] (toppings/cheese path/to/file) Cheese type not found: "Feta"`)) -} - -func TestMessage_Unstructured(t *testing.T) { - g := NewWithT(t) - mt := NewMessageType(Error, "IST-0042", "Cheese type not found: %q") - m := NewMessage(mt, nil, "Feta") - - g.Expect(m.Unstructured(true)).To(Not(HaveKey("origin"))) - g.Expect(m.Unstructured(false)).To(Not(HaveKey("origin"))) - - m = NewMessage(mt, &resource.Instance{Origin: testOrigin{name: "toppings/cheese"}}, "Feta") - - g.Expect(m.Unstructured(true)).To((HaveKey("origin"))) - g.Expect(m.Unstructured(false)).To(Not(HaveKey("origin"))) -} - -func TestMessageWithDocRef(t *testing.T) { - g := NewWithT(t) - mt := NewMessageType(Error, "IST0042", "Cheese type not found: %q") - m := NewMessage(mt, nil, "Feta") - m.DocRef = "test-ref" - g.Expect(m.Unstructured(false)["documentationUrl"]).To(Equal(url.ConfigAnalysis + "/ist0042/?ref=test-ref")) -} - -func TestMessage_JSON(t *testing.T) { - g := NewWithT(t) - mt := NewMessageType(Error, "IST0042", "Cheese type not found: %q") - m := NewMessage(mt, &resource.Instance{Origin: testOrigin{name: "toppings/cheese", ref: testReference{"path/to/file"}}}, "Feta") - - j, _ := json.Marshal(&m) - g.Expect(string(j)).To(Equal(`{"code":"IST0042","documentationUrl":"` + url.ConfigAnalysis + `/ist0042/"` + - `,"level":"Error","message":"Cheese type not found: \"Feta\"","origin":"toppings/cheese","reference":"path/to/file"}`)) -} - -func TestMessage_ReplaceLine(t *testing.T) { - testCases := []string{"test.yaml", "test.yaml:1", "test.yaml:10", "test.yaml: 10", "test", "test:10", "123:10", "123"} - result := make([]string, 0) - g := NewGomegaWithT(t) - m := &Message{Line: 321} - for _, v := range testCases { - result = append(result, m.ReplaceLine(v)) - } - g.Expect(result).To(Equal([]string{"test.yaml", "test.yaml:321", "test.yaml:321", "test.yaml:321", "test", "test:321", "123:321", "123"})) -} - -func TestMessage_UnstructuredAnalysisMessageBase(t *testing.T) { - g := NewWithT(t) - mt := NewMessageType(Error, "IST0042", "Cheese type not found: %q") - m := NewMessage(mt, &resource.Instance{Origin: testOrigin{name: "toppings/cheese", ref: testReference{"path/to/file"}}}, "Feta") - m.DocRef = "test-ref" - - mb := m.UnstructuredAnalysisMessageBase() - g.Expect(mb["documentationUrl"]).To(Equal(fmt.Sprintf("%s/%s/%s", url.ConfigAnalysis, "ist0042", "?ref=test-ref"))) - g.Expect(mb["level"]).To(Equal("ERROR")) - g.Expect(mb["type"]).To(Equal( - map[string]interface{}{ - "code": "IST0042", - }, - )) -} diff --git a/pkg/config/analysis/diag/messages.go b/pkg/config/analysis/diag/messages.go deleted file mode 100644 index d8ddd62aa..000000000 --- a/pkg/config/analysis/diag/messages.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package diag - -import ( - "sort" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/object" -) - -// Messages is a slice of Message items. -type Messages []Message - -// Add a new message to the messages -func (ms *Messages) Add(m ...Message) { - *ms = append(*ms, m...) -} - -// Sort the message lexicographically by level, code, resource origin name, then string. -func (ms *Messages) Sort() { - sort.Slice(*ms, func(i, j int) bool { - a, b := (*ms)[i], (*ms)[j] - switch { - case a.Type.Level() != b.Type.Level(): - return a.Type.Level().sortOrder < b.Type.Level().sortOrder - case a.Type.Code() != b.Type.Code(): - return a.Type.Code() < b.Type.Code() - case a.Resource == nil && b.Resource != nil: - return true - case a.Resource != nil && b.Resource == nil: - return false - case a.Resource != nil && b.Resource != nil && a.Resource.Origin.Comparator() != b.Resource.Origin.Comparator(): - return a.Resource.Origin.Comparator() < b.Resource.Origin.Comparator() - default: - return a.String() < b.String() - } - }) -} - -// SortedDedupedCopy returns a different sorted (and deduped) Messages struct. -func (ms *Messages) SortedDedupedCopy() Messages { - newMs := append((*ms)[:0:0], *ms...) - newMs.Sort() - - // Take advantage of the fact that the list is already sorted to dedupe - // messages (any duplicates should be adjacent). - var deduped Messages - for _, m := range newMs { - // Two messages are duplicates if they have the same string representation. - if len(deduped) != 0 && deduped[len(deduped)-1].String() == m.String() { - continue - } - deduped = append(deduped, m) - } - return deduped -} - -// SetDocRef sets the doc URL reference tracker for the messages -func (ms *Messages) SetDocRef(docRef string) *Messages { - for i := range *ms { - (*ms)[i].DocRef = docRef - } - return ms -} - -// FilterOutLowerThan only keeps messages at or above the specified output level -func (ms *Messages) FilterOutLowerThan(outputLevel Level) Messages { - outputMessages := Messages{} - for _, m := range *ms { - if m.Type.Level().IsWorseThanOrEqualTo(outputLevel) { - outputMessages = append(outputMessages, m) - } - } - return outputMessages -} - -func (ms *Messages) FilterOutBasedOnResources(resources object.K8sObjects) Messages { - outputMessages := Messages{} - for _, m := range *ms { - for _, rs := range resources { - if rs.Name == m.Resource.Metadata.FullName.Name.String() { - outputMessages = append(outputMessages, m) - break - } - } - } - return outputMessages -} diff --git a/pkg/config/analysis/diag/messages_test.go b/pkg/config/analysis/diag/messages_test.go deleted file mode 100644 index c5169739e..000000000 --- a/pkg/config/analysis/diag/messages_test.go +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package diag - -import ( - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/url" -) - -func TestMessages_Sort(t *testing.T) { - g := NewWithT(t) - - firstMsg := NewMessage( - NewMessageType(Error, "B1", "Template: %q"), - MockResource("B"), - "B", - ) - secondMsg := NewMessage( - NewMessageType(Warning, "A1", "Template: %q"), - MockResource("B"), - "B", - ) - thirdMsg := NewMessage( - NewMessageType(Warning, "B1", "Template: %q"), - MockResource("A"), - "B", - ) - fourthMsg := NewMessage( - NewMessageType(Warning, "B1", "Template: %q"), - MockResource("B"), - "A", - ) - fifthMsg := NewMessage( - NewMessageType(Warning, "B1", "Template: %q"), - MockResource("B"), - "B", - ) - - msgs := Messages{fifthMsg, fourthMsg, thirdMsg, secondMsg, firstMsg} - expectedMsgs := Messages{firstMsg, secondMsg, thirdMsg, fourthMsg, fifthMsg} - - msgs.Sort() - - g.Expect(msgs).To(Equal(expectedMsgs)) -} - -func TestMessages_SortWithNilOrigin(t *testing.T) { - g := NewWithT(t) - - firstMsg := NewMessage( - NewMessageType(Error, "B1", "Template: %q"), - nil, - "B", - ) - secondMsg := NewMessage( - NewMessageType(Error, "B1", "Template: %q"), - nil, - "C", - ) - thirdMsg := NewMessage( - NewMessageType(Error, "B1", "Template: %q"), - MockResource("B"), - "B", - ) - - msgs := Messages{thirdMsg, secondMsg, firstMsg} - expectedMsgs := Messages{firstMsg, secondMsg, thirdMsg} - - msgs.Sort() - - g.Expect(msgs).To(Equal(expectedMsgs)) -} - -func TestMessages_SortedCopy(t *testing.T) { - g := NewWithT(t) - - firstMsg := NewMessage( - NewMessageType(Error, "B1", "Template: %q"), - MockResource("B"), - "B", - ) - secondMsg := NewMessage( - NewMessageType(Warning, "A1", "Template: %q"), - MockResource("B"), - "B", - ) - // Oops, we have a duplicate (identical to firstMsg) - it should be removed. - thirdMsg := NewMessage( - NewMessageType(Error, "B1", "Template: %q"), - MockResource("B"), - "B", - ) - - msgs := Messages{thirdMsg, secondMsg, firstMsg} - expectedMsgs := Messages{firstMsg, secondMsg} - - newMsgs := msgs.SortedDedupedCopy() - - g.Expect(newMsgs).To(Equal(expectedMsgs)) -} - -func TestMessages_SetRefDoc(t *testing.T) { - g := NewWithT(t) - - firstMsg := NewMessage( - NewMessageType(Error, "B1", "Template: %q"), - MockResource("B"), - "B", - ) - secondMsg := NewMessage( - NewMessageType(Info, "C1", "Template: %q"), - MockResource("B"), - "B", - ) - - msgs := Messages{firstMsg, secondMsg} - msgs.SetDocRef("istioctl-awesome") - - getDocURL := func(msg Message) string { - return msg.Unstructured(false)["documentationUrl"].(string) - } - - g.Expect(getDocURL(msgs[0])).To(Equal(url.ConfigAnalysis + "/b1/?ref=istioctl-awesome")) - g.Expect(getDocURL(msgs[1])).To(Equal(url.ConfigAnalysis + "/c1/?ref=istioctl-awesome")) -} - -func TestMessages_Filter(t *testing.T) { - g := NewWithT(t) - - firstMsg := NewMessage( - NewMessageType(Error, "B1", "Template: %q"), - MockResource("B"), - "B", - ) - secondMsg := NewMessage( - NewMessageType(Info, "A1", "Template: %q"), - MockResource("B"), - "B", - ) - thirdMsg := NewMessage( - NewMessageType(Warning, "C1", "Template: %q"), - MockResource("B"), - "B", - ) - - msgs := Messages{firstMsg, secondMsg, thirdMsg} - filteredMsgs := msgs.FilterOutLowerThan(Warning) - expectedMsgs := Messages{firstMsg, thirdMsg} - - g.Expect(filteredMsgs).To(Equal(expectedMsgs)) -} - -func TestMessages_FilterOutAll(t *testing.T) { - g := NewWithT(t) - - firstMsg := NewMessage( - NewMessageType(Info, "A1", "Template: %q"), - MockResource("B"), - "B", - ) - secondMsg := NewMessage( - NewMessageType(Warning, "C1", "Template: %q"), - MockResource("B"), - "B", - ) - - msgs := Messages{firstMsg, secondMsg} - filteredMsgs := msgs.FilterOutLowerThan(Error) - expectedMsgs := Messages{} - - g.Expect(filteredMsgs).To(Equal(expectedMsgs)) -} diff --git a/pkg/config/analysis/incluster/controller.go b/pkg/config/analysis/incluster/controller.go deleted file mode 100644 index 0b62ca376..000000000 --- a/pkg/config/analysis/incluster/controller.go +++ /dev/null @@ -1,123 +0,0 @@ -/* - Copyright Istio Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package incluster - -import ( - "fmt" - "strings" - "time" -) - -import ( - v1alpha12 "istio.io/api/analysis/v1alpha1" - "istio.io/api/meta/v1alpha1" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/crdclient" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/status" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/diag" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/local" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -// Controller manages repeatedly running analyzers in istiod, and reporting results -// via istio status fields. -type Controller struct { - analyzer *local.IstiodAnalyzer - statusctl *status.Controller -} - -func NewController(stop <-chan struct{}, rwConfigStore model.ConfigStoreController, - kubeClient kube.Client, namespace string, statusManager *status.Manager, domainSuffix string) (*Controller, error) { - ia := local.NewIstiodAnalyzer(analyzers.AllCombined(), - "", resource.Namespace(namespace), func(name collection.Name) {}, true) - ia.AddSource(rwConfigStore) - // Filter out configs watched by rwConfigStore so we don't watch multiple times - store, err := crdclient.NewForSchemas(kubeClient, "default", - domainSuffix, collections.All.Remove(rwConfigStore.Schemas().All()...)) - if err != nil { - return nil, fmt.Errorf("unable to load common types for analysis, releasing lease: %v", err) - } - ia.AddSource(store) - kubeClient.RunAndWait(stop) - err = ia.Init(stop) - if err != nil { - return nil, fmt.Errorf("unable to initialize analysis controller, releasing lease: %s", err) - } - ctl := statusManager.CreateIstioStatusController(func(status *v1alpha1.IstioStatus, context interface{}) *v1alpha1.IstioStatus { - msgs := context.(diag.Messages) - // zero out analysis messages, as this is the sole controller for those - status.ValidationMessages = []*v1alpha12.AnalysisMessageBase{} - for _, msg := range msgs { - status.ValidationMessages = append(status.ValidationMessages, msg.AnalysisMessageBase()) - } - return status - }) - return &Controller{analyzer: ia, statusctl: ctl}, nil -} - -// Run is blocking -func (c *Controller) Run(stop <-chan struct{}) { - t := time.NewTicker(features.AnalysisInterval) - oldmsgs := diag.Messages{} - for { - select { - case <-t.C: - res, err := c.analyzer.ReAnalyze(stop) - if err != nil { - log.Errorf("In-cluster analysis has failed: %s", err) - continue - } - // reorganize messages to map - index := map[status.Resource]diag.Messages{} - for _, m := range res.Messages { - key := status.ResourceFromMetadata(m.Resource.Metadata) - index[key] = append(index[key], m) - } - // if we previously had a message that has been removed, ensure it is removed - // TODO: this creates a state destruction problem when istiod crashes - // in that old messages may not be removed. Not sure how to fix this - // other than write every object's status every loop. - for _, m := range oldmsgs { - key := status.ResourceFromMetadata(m.Resource.Metadata) - if _, ok := index[key]; !ok { - index[key] = diag.Messages{} - } - } - for r, m := range index { - // don't try to write status for non-istio types - if strings.HasSuffix(r.Group, "istio.io") { - log.Debugf("enqueueing update for %s/%s", r.Namespace, r.Name) - c.statusctl.EnqueueStatusUpdateResource(m, r) - } - } - oldmsgs = res.Messages - log.Debugf("finished enqueueing all statuses") - case <-stop: - t.Stop() - break - } - } -} diff --git a/pkg/config/analysis/local/analyze_test.go b/pkg/config/analysis/local/analyze_test.go deleted file mode 100644 index 0834586b2..000000000 --- a/pkg/config/analysis/local/analyze_test.go +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package local - -import ( - "context" - "fmt" - "os" - "strings" - "testing" - "time" -) - -import ( - . "github.com/onsi/gomega" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -type testAnalyzer struct { - fn func(analysis.Context) - inputs collection.Names -} - -var blankTestAnalyzer = &testAnalyzer{ - fn: func(_ analysis.Context) {}, - inputs: []collection.Name{}, -} - -var ( - // YamlN1I1V1 is a testing resource in Yaml form - YamlN1I1V1 = ` -apiVersion: testdata.istio.io/v1alpha1 -kind: Kind1 -metadata: - namespace: n1 - name: i1 -spec: - n1_i1: v1 -` - blankCombinedAnalyzer = analysis.Combine("testCombined", blankTestAnalyzer) - timeout = 1 * time.Second -) - -// Metadata implements Analyzer -func (a *testAnalyzer) Metadata() analysis.Metadata { - return analysis.Metadata{ - Name: "testAnalyzer", - Inputs: a.inputs, - } -} - -// Analyze implements Analyzer -func (a *testAnalyzer) Analyze(ctx analysis.Context) { - a.fn(ctx) -} - -func TestAbortWithNoSources(t *testing.T) { - g := NewWithT(t) - - cancel := make(chan struct{}) - - sa := NewSourceAnalyzer(blankCombinedAnalyzer, "", "", nil, false, timeout) - _, err := sa.Analyze(cancel) - g.Expect(err).To(Not(BeNil())) -} - -func TestAnalyzersRun(t *testing.T) { - g := NewWithT(t) - - cancel := make(chan struct{}) - - r := createTestResource(t, "ns", "resource", "v1") - m := msg.NewInternalError(r, "msg") - a := &testAnalyzer{ - fn: func(ctx analysis.Context) { - ctx.Exists(K8SCollection1.Name(), resource.NewFullName("", "")) - ctx.Report(K8SCollection1.Name(), m) - }, - } - - var collectionAccessed collection.Name - cr := func(col collection.Name) { - collectionAccessed = col - } - - sa := NewSourceAnalyzer(analysis.Combine("a", a), "", "", cr, false, timeout) - err := sa.AddReaderKubeSource(nil) - g.Expect(err).To(BeNil()) - - result, err := sa.Analyze(cancel) - g.Expect(err).To(BeNil()) - g.Expect(result.Messages).To(ConsistOf(m)) - g.Expect(collectionAccessed).To(Equal(K8SCollection1.Name())) - g.Expect(result.ExecutedAnalyzers).To(ConsistOf(a.Metadata().Name)) -} - -func TestFilterOutputByNamespace(t *testing.T) { - g := NewWithT(t) - - cancel := make(chan struct{}) - - r1 := createTestResource(t, "ns1", "resource", "v1") - r2 := createTestResource(t, "ns2", "resource", "v1") - msg1 := msg.NewInternalError(r1, "msg") - msg2 := msg.NewInternalError(r2, "msg") - a := &testAnalyzer{ - fn: func(ctx analysis.Context) { - ctx.Report(K8SCollection1.Name(), msg1) - ctx.Report(K8SCollection1.Name(), msg2) - }, - } - - sa := NewSourceAnalyzer(analysis.Combine("a", a), "ns1", "", nil, false, timeout) - err := sa.AddReaderKubeSource(nil) - g.Expect(err).To(BeNil()) - - result, err := sa.Analyze(cancel) - g.Expect(err).To(BeNil()) - g.Expect(result.Messages).To(ConsistOf(msg1)) -} - -func TestAddInMemorySource(t *testing.T) { - g := NewWithT(t) - - sa := NewSourceAnalyzer(blankCombinedAnalyzer, "", "", nil, false, timeout) - - src := model.NewFakeStore() - sa.AddSource(dfCache{ConfigStore: src}) - assert.Equal(t, sa.meshCfg, mesh.DefaultMeshConfig()) // Base default meshcfg - g.Expect(sa.meshNetworks.Networks).To(HaveLen(0)) - g.Expect(sa.stores).To(HaveLen(1)) -} - -func TestAddRunningKubeSource(t *testing.T) { - g := NewWithT(t) - - mk := kube.NewFakeClient() - - sa := NewSourceAnalyzer(blankCombinedAnalyzer, "", "", nil, false, timeout) - - sa.AddRunningKubeSource(mk) - assert.Equal(t, sa.meshCfg, mesh.DefaultMeshConfig()) // Base default meshcfg - g.Expect(sa.meshNetworks.Networks).To(HaveLen(0)) - g.Expect(sa.stores).To(HaveLen(1)) -} - -func TestAddRunningKubeSourceWithIstioMeshConfigMap(t *testing.T) { - g := NewWithT(t) - - istioNamespace := resource.Namespace("dubbo-system") - - testRootNamespace := "testNamespace" - - cfg := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: meshConfigMapName, - }, - Data: map[string]string{ - meshConfigMapKey: fmt.Sprintf("rootNamespace: %s", testRootNamespace), - meshNetworksMapKey: `networks: {"n1": {}, "n2": {}}`, - }, - } - - mk := kube.NewFakeClient() - if _, err := mk.Kube().CoreV1().ConfigMaps(istioNamespace.String()).Create(context.TODO(), cfg, metav1.CreateOptions{}); err != nil { - t.Fatalf("Error creating mesh config configmap: %v", err) - } - - sa := NewSourceAnalyzer(blankCombinedAnalyzer, "", istioNamespace, nil, false, timeout) - - sa.AddRunningKubeSource(mk) - g.Expect(sa.meshCfg.RootNamespace).To(Equal(testRootNamespace)) - g.Expect(sa.meshNetworks.Networks).To(HaveLen(2)) - g.Expect(sa.stores).To(HaveLen(1)) -} - -func TestAddReaderKubeSource(t *testing.T) { - g := NewWithT(t) - - sa := NewSourceAnalyzer(blankCombinedAnalyzer, "", "", nil, false, timeout) - - tmpfile := tempFileFromString(t, YamlN1I1V1) - defer os.Remove(tmpfile.Name()) - - err := sa.AddReaderKubeSource([]ReaderSource{{Reader: tmpfile}}) - g.Expect(err).To(BeNil()) - assert.Equal(t, sa.meshCfg, mesh.DefaultMeshConfig()) // Base default meshcfg - g.Expect(sa.stores).To(HaveLen(0)) - - // Note that a blank file for mesh cfg is equivalent to specifying all the defaults - testRootNamespace := "testNamespace" - tmpMeshFile := tempFileFromString(t, fmt.Sprintf("rootNamespace: %s", testRootNamespace)) - defer func() { _ = os.Remove(tmpMeshFile.Name()) }() - - err = sa.AddFileKubeMeshConfig(tmpMeshFile.Name()) - g.Expect(err).To(BeNil()) - g.Expect(sa.meshCfg.RootNamespace).To(Equal(testRootNamespace)) // Should be mesh config from the file now -} - -func TestAddReaderKubeSourceSkipsBadEntries(t *testing.T) { - g := NewWithT(t) - - sa := NewSourceAnalyzer(blankCombinedAnalyzer, "", "", nil, false, timeout) - - tmpfile := tempFileFromString(t, JoinString(YamlN1I1V1, "bogus resource entry\n")) - defer func() { _ = os.Remove(tmpfile.Name()) }() - - err := sa.AddReaderKubeSource([]ReaderSource{{Reader: tmpfile}}) - g.Expect(err).To(Not(BeNil())) -} - -const ( - yamlSeparator = "---\n" -) - -// JoinString joins the given yaml parts into a single multipart document. -func JoinString(parts ...string) string { - var st strings.Builder - - var lastIsNewLine bool - for _, p := range parts { - if len(p) == 0 { - continue - } - if st.Len() != 0 { - if !lastIsNewLine { - _, _ = st.WriteString("\n") - } - st.WriteString(yamlSeparator) - } - _, _ = st.WriteString(p) - lastIsNewLine = p[len(p)-1] == '\n' - } - - return st.String() -} - -func TestDefaultResourcesRespectsMeshConfig(t *testing.T) { - g := NewWithT(t) - - sa := NewSourceAnalyzer(blankCombinedAnalyzer, "", "", nil, false, timeout) - - // With ingress off, we shouldn't generate any default resources - ingressOffMeshCfg := tempFileFromString(t, "ingressControllerMode: 'OFF'") - defer func() { _ = os.Remove(ingressOffMeshCfg.Name()) }() - - err := sa.AddFileKubeMeshConfig(ingressOffMeshCfg.Name()) - g.Expect(err).To(BeNil()) - sa.AddDefaultResources() - g.Expect(sa.stores).To(BeEmpty()) - - // With ingress on, though, we should. - ingressStrictMeshCfg := tempFileFromString(t, "ingressControllerMode: 'STRICT'") - defer func() { _ = os.Remove(ingressStrictMeshCfg.Name()) }() - - err = sa.AddFileKubeMeshConfig(ingressStrictMeshCfg.Name()) - g.Expect(err).To(BeNil()) - sa.AddDefaultResources() - g.Expect(sa.stores).To(HaveLen(0)) -} - -func tempFileFromString(t *testing.T, content string) *os.File { - t.Helper() - tmpfile, err := os.CreateTemp("", "") - if err != nil { - t.Fatal(err) - } - _, err = tmpfile.WriteString(content) - if err != nil { - t.Fatal(err) - } - err = tmpfile.Sync() - if err != nil { - t.Fatal(err) - } - _, err = tmpfile.Seek(0, 0) - if err != nil { - t.Fatal(err) - } - return tmpfile -} diff --git a/pkg/config/analysis/local/context.go b/pkg/config/analysis/local/context.go deleted file mode 100644 index ceb697bce..000000000 --- a/pkg/config/analysis/local/context.go +++ /dev/null @@ -1,200 +0,0 @@ -/* - Copyright Istio Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package local - -import ( - "encoding/json" - "fmt" -) - -import ( - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/file" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/diag" - "github.com/apache/dubbo-go-pixiu/pkg/config/legacy/source/kube" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// NewContext allows tests to use istiodContext without exporting it. returned context is not threadsafe. -func NewContext(store model.ConfigStore, cancelCh <-chan struct{}, collectionReporter CollectionReporterFn) analysis.Context { - return &istiodContext{ - store: store, - cancelCh: cancelCh, - messages: diag.Messages{}, - collectionReporter: collectionReporter, - found: map[key]*resource.Instance{}, - foundCollections: map[collection.Name]map[resource.FullName]*resource.Instance{}, - } -} - -type istiodContext struct { - store model.ConfigStore - cancelCh <-chan struct{} - messages diag.Messages - collectionReporter CollectionReporterFn - found map[key]*resource.Instance - foundCollections map[collection.Name]map[resource.FullName]*resource.Instance -} - -type key struct { - collectionName collection.Name - name resource.FullName -} - -func (i *istiodContext) Report(c collection.Name, m diag.Message) { - i.messages.Add(m) -} - -func (i *istiodContext) Find(col collection.Name, name resource.FullName) *resource.Instance { - i.collectionReporter(col) - if result, ok := i.found[key{col, name}]; ok { - return result - } - if cache, ok := i.foundCollections[col]; ok { - if result, ok2 := cache[name]; ok2 { - return result - } - } - colschema, ok := collections.All.Find(col.String()) - if !ok { - log.Warnf("collection %s could not be found", col.String()) - return nil - } - cfg := i.store.Get(colschema.Resource().GroupVersionKind(), name.Name.String(), name.Namespace.String()) - if cfg == nil { - log.Warnf(" %s resource [%s/%s] could not be found", colschema.Resource().GroupVersionKind(), name.Namespace.String(), name.Name.String()) - return nil - } - result, err := cfgToInstance(*cfg, col, colschema) - if err != nil { - log.Errorf("failed converting found config %s %s/%s to instance: %s, ", - cfg.Meta.GroupVersionKind.Kind, cfg.Meta.Namespace, cfg.Meta.Namespace, err) - return nil - } - i.found[key{col, name}] = result - return result -} - -func (i *istiodContext) Exists(col collection.Name, name resource.FullName) bool { - i.collectionReporter(col) - return i.Find(col, name) != nil -} - -func (i *istiodContext) ForEach(col collection.Name, fn analysis.IteratorFn) { - i.collectionReporter(col) - if cached, ok := i.foundCollections[col]; ok { - for _, res := range cached { - if !fn(res) { - break - } - } - return - } - colschema, ok := collections.All.Find(col.String()) - if !ok { - // TODO: demote this log before merging - log.Errorf("collection %s could not be found", col.String()) - return - } - // TODO: this needs to include file source as well - cfgs, err := i.store.List(colschema.Resource().GroupVersionKind(), "") - if err != nil { - // TODO: demote this log before merging - log.Errorf("collection %s could not be listed: %s", col.String(), err) - return - } - broken := false - cache := map[resource.FullName]*resource.Instance{} - for _, cfg := range cfgs { - k := key{ - col, resource.FullName{ - Name: resource.LocalName(cfg.Name), - Namespace: resource.Namespace(cfg.Namespace), - }, - } - if res, ok := i.found[k]; ok { - if !broken && !fn(res) { - broken = true - } - cache[res.Metadata.FullName] = res - continue - } - res, err := cfgToInstance(cfg, col, colschema) - if err != nil { - // TODO: demote this log before merging - log.Error(err) - // TODO: is continuing the right thing here? - continue - } - if !broken && !fn(res) { - broken = true - } - cache[res.Metadata.FullName] = res - } - if len(cache) > 0 { - i.foundCollections[col] = cache - } -} - -func (i *istiodContext) Canceled() bool { - select { - case <-i.cancelCh: - return true - default: - return false - } -} - -func cfgToInstance(cfg config.Config, col collection.Name, colschema collection.Schema) (*resource.Instance, error) { - res := resource.PilotConfigToInstance(&cfg, colschema.Resource()) - fmstring := cfg.Meta.Annotations[file.FieldMapKey] - var out map[string]int - if fmstring != "" { - err := json.Unmarshal([]byte(fmstring), &out) - if err != nil { - return nil, fmt.Errorf("error parsing fieldmap: %s", err) - } - } - refstring := cfg.Meta.Annotations[file.ReferenceKey] - var outref resource.Reference - if refstring != "" { - outref = &kube.Position{} - err := json.Unmarshal([]byte(refstring), outref) - if err != nil { - return nil, fmt.Errorf("error parsing reference: %s", err) - } - } - res.Origin = &kube.Origin{ - Collection: col, - Kind: colschema.Resource().Kind(), - FullName: res.Metadata.FullName, - Version: resource.Version(cfg.ResourceVersion), - Ref: outref, - FieldsMap: out, - } - // MCP is not aware of generation, add that here. - res.Metadata.Generation = cfg.Generation - return res, nil -} diff --git a/pkg/config/analysis/local/defaults.go b/pkg/config/analysis/local/defaults.go deleted file mode 100644 index 2db762705..000000000 --- a/pkg/config/analysis/local/defaults.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package local - -import ( - "bytes" - "text/template" -) - -const defaultIstioIngressGateway = ` -apiVersion: v1 -kind: Pod -metadata: - labels: - istio: ingressgateway - name: {{.ingressService}}-dummypod - namespace: {{.namespace}} -spec: - containers: - - args: - name: istio-proxy ---- -apiVersion: v1 -kind: Service -metadata: - name: {{.ingressService}} - namespace: {{.namespace}} -spec: - ports: - - name: http2 - nodePort: 31380 - port: 80 - protocol: TCP - targetPort: 80 - - name: https - nodePort: 31390 - port: 443 - protocol: TCP - targetPort: 443 - - name: tcp - nodePort: 31400 - port: 31400 - protocol: TCP - targetPort: 31400 - - name: tls - nodePort: 31447 - port: 15443 - protocol: TCP - targetPort: 15443 - selector: - istio: ingressgateway -` - -func getDefaultIstioIngressGateway(namespace, ingressService string) (string, error) { - result, err := generate(defaultIstioIngressGateway, map[string]string{"namespace": namespace, "ingressService": ingressService}) - if err != nil { - return "", err - } - - return result, nil -} - -func generate(tmpl string, params map[string]string) (string, error) { - t := template.Must(template.New("code").Parse(tmpl)) - - var b bytes.Buffer - if err := t.Execute(&b, params); err != nil { - return "", err - } - return b.String(), nil -} diff --git a/pkg/config/analysis/local/helpers_test.go b/pkg/config/analysis/local/helpers_test.go deleted file mode 100644 index 0abf524ef..000000000 --- a/pkg/config/analysis/local/helpers_test.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package local - -// Test helpers common to this package - -import ( - "reflect" - "testing" -) - -import ( - "github.com/gogo/protobuf/types" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/legacy/source/kube" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - r2 "github.com/apache/dubbo-go-pixiu/pkg/config/schema/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/validation" -) - -// K8SCollection1 describes the collection k8s/collection1 -var K8SCollection1 = collection.Builder{ - Name: "k8s/collection1", - VariableName: "K8SCollection1", - Resource: r2.Builder{ - Group: "testdata.istio.io", - Kind: "Kind1", - Plural: "Kind1s", - Version: "v1alpha1", - Proto: "google.protobuf.Struct", - ReflectType: reflect.TypeOf(&types.Struct{}).Elem(), - ProtoPackage: "github.com/gogo/protobuf/types", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), -}.MustBuild() - -func createTestResource(t *testing.T, ns, name, version string) *resource.Instance { - t.Helper() - rname := resource.NewFullName(resource.Namespace(ns), resource.LocalName(name)) - return &resource.Instance{ - Metadata: resource.Metadata{ - FullName: rname, - Version: resource.Version(version), - }, - Message: &types.Empty{}, - Origin: &kube.Origin{ - FullName: rname, - }, - } -} diff --git a/pkg/config/analysis/local/istiod_analyze.go b/pkg/config/analysis/local/istiod_analyze.go deleted file mode 100644 index c065250b9..000000000 --- a/pkg/config/analysis/local/istiod_analyze.go +++ /dev/null @@ -1,465 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package local - -import ( - "context" - "fmt" - "io" - "os" - "strings" - "time" -) - -import ( - "github.com/hashicorp/go-multierror" - "github.com/ryanuber/go-glob" - "istio.io/api/annotation" - "istio.io/api/mesh/v1alpha1" - kerrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/aggregate" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/file" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/crdclient" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/memory" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/diag" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/scope" - mesh_const "github.com/apache/dubbo-go-pixiu/pkg/config/legacy/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/legacy/util/kuberesource" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - kubelib "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// IstiodAnalyzer handles local analysis of k8s event sources, both live and file-based -type IstiodAnalyzer struct { - // internalStore stores synthetic configs for analysis (mesh config, etc) - internalStore model.ConfigStore - // stores contains all the (non file) config sources to analyze - stores []model.ConfigStoreController - // fileSource contains all file bases sources - fileSource *file.KubeSource - - analyzer *analysis.CombinedAnalyzer - namespace resource.Namespace - istioNamespace resource.Namespace - - initializedStore model.ConfigStoreController - - // List of code and resource suppressions to exclude messages on - suppressions []AnalysisSuppression - - // Mesh config for this analyzer. This can come from multiple sources, and the last added version will take precedence. - meshCfg *v1alpha1.MeshConfig - - // Mesh networks config for this analyzer. - meshNetworks *v1alpha1.MeshNetworks - - // Which kube resources are used by this analyzer - // Derived from metadata and the specified analyzer and transformer providers - kubeResources collection.Schemas - - // Hook function called when a collection is used in analysis - collectionReporter CollectionReporterFn - - clientsToRun []kubelib.Client -} - -// NewSourceAnalyzer is a drop-in replacement for the galley function, adapting to istiod analyzer. -func NewSourceAnalyzer(analyzer *analysis.CombinedAnalyzer, namespace, istioNamespace resource.Namespace, - cr CollectionReporterFn, serviceDiscovery bool, _ time.Duration) *IstiodAnalyzer { - return NewIstiodAnalyzer(analyzer, namespace, istioNamespace, cr, serviceDiscovery) -} - -// NewIstiodAnalyzer creates a new IstiodAnalyzer with no sources. Use the Add*Source -// methods to add sources in ascending precedence order, -// then execute Analyze to perform the analysis -func NewIstiodAnalyzer(analyzer *analysis.CombinedAnalyzer, namespace, - istioNamespace resource.Namespace, cr CollectionReporterFn, serviceDiscovery bool) *IstiodAnalyzer { - // collectionReporter hook function defaults to no-op - if cr == nil { - cr = func(collection.Name) {} - } - - // Get the closure of all input collections for our analyzer, paying attention to transforms - kubeResources := kuberesource.SkipExcludedCollections( - analyzer.Metadata().Inputs, - kuberesource.DefaultExcludedResourceKinds(), - serviceDiscovery) - - mcfg := mesh.DefaultMeshConfig() - sa := &IstiodAnalyzer{ - meshCfg: mcfg, - meshNetworks: mesh.DefaultMeshNetworks(), - analyzer: analyzer, - namespace: namespace, - internalStore: memory.Make(collection.SchemasFor(collections.IstioMeshV1Alpha1MeshNetworks, collections.IstioMeshV1Alpha1MeshConfig)), - istioNamespace: istioNamespace, - kubeResources: kubeResources, - collectionReporter: cr, - } - - return sa -} - -// ReAnalyze loads the sources and executes the analysis, assuming init is already called -func (sa *IstiodAnalyzer) ReAnalyze(cancel <-chan struct{}) (AnalysisResult, error) { - var result AnalysisResult - store := sa.initializedStore - result.ExecutedAnalyzers = sa.analyzer.AnalyzerNames() - result.SkippedAnalyzers = sa.analyzer.RemoveSkipped(store.Schemas()) - - cache.WaitForCacheSync(cancel, - store.HasSynced) - - ctx := NewContext(store, cancel, sa.collectionReporter) - - sa.analyzer.Analyze(ctx) - - namespaces := make(map[resource.Namespace]struct{}) - if sa.namespace != "" { - namespaces[sa.namespace] = struct{}{} - } - // TODO: analysis is run for all namespaces, even if they are requested to be filtered. - msgs := filterMessages(ctx.(*istiodContext).messages, namespaces, sa.suppressions) - result.Messages = msgs.SortedDedupedCopy() - - return result, nil -} - -// Analyze loads the sources and executes the analysis -func (sa *IstiodAnalyzer) Analyze(cancel <-chan struct{}) (AnalysisResult, error) { - err2 := sa.Init(cancel) - if err2 != nil { - return AnalysisResult{}, err2 - } - return sa.ReAnalyze(cancel) -} - -func (sa *IstiodAnalyzer) Init(cancel <-chan struct{}) error { - // We need at least one non-meshcfg source - if len(sa.stores) == 0 && sa.fileSource == nil { - return fmt.Errorf("at least one file and/or Kubernetes source must be provided") - } - - // TODO: there's gotta be a better way to convert v1meshconfig to config.Config... - // Create a store containing mesh config. There should be exactly one. - _, err := sa.internalStore.Create(config.Config{ - Meta: config.Meta{ - Name: mesh_const.MeshConfigResourceName.Name.String(), - Namespace: mesh_const.MeshConfigResourceName.Namespace.String(), - GroupVersionKind: collections.IstioMeshV1Alpha1MeshConfig.Resource().GroupVersionKind(), - }, - Spec: sa.meshCfg, - }) - if err != nil { - return fmt.Errorf("something unexpected happened while creating the meshconfig: %s", err) - } - // Create a store containing meshnetworks. There should be exactly one. - _, err = sa.internalStore.Create(config.Config{ - Meta: config.Meta{ - Name: mesh_const.MeshNetworksResourceName.Name.String(), - Namespace: mesh_const.MeshNetworksResourceName.Namespace.String(), - GroupVersionKind: collections.IstioMeshV1Alpha1MeshNetworks.Resource().GroupVersionKind(), - }, - Spec: sa.meshNetworks, - }) - if err != nil { - return fmt.Errorf("something unexpected happened while creating the meshnetworks: %s", err) - } - allstores := append(sa.stores, dfCache{ConfigStore: sa.internalStore}) - if sa.fileSource != nil { - allstores = append(allstores, sa.fileSource) - } - - for _, c := range sa.clientsToRun { - // TODO: this could be parallel - c.RunAndWait(cancel) - } - - store, err := aggregate.MakeWriteableCache(allstores, nil) - if err != nil { - return err - } - go store.Run(cancel) - sa.initializedStore = store - return nil -} - -type dfCache struct { - model.ConfigStore -} - -func (d dfCache) RegisterEventHandler(kind config.GroupVersionKind, handler model.EventHandler) { - panic("implement me") -} - -// Run intentionally left empty -func (d dfCache) Run(_ <-chan struct{}) { -} - -func (d dfCache) SetWatchErrorHandler(f func(r *cache.Reflector, err error)) error { - panic("implement me") -} - -func (d dfCache) HasSynced() bool { - return true -} - -// SetSuppressions will set the list of suppressions for the analyzer. Any -// resource that matches the provided suppression will not be included in the -// final message output. -func (sa *IstiodAnalyzer) SetSuppressions(suppressions []AnalysisSuppression) { - sa.suppressions = suppressions -} - -// AddReaderKubeSource adds a source based on the specified k8s yaml files to the current IstiodAnalyzer -func (sa *IstiodAnalyzer) AddReaderKubeSource(readers []ReaderSource) error { - var src *file.KubeSource - if sa.fileSource != nil { - src = sa.fileSource - } else { - src = file.NewKubeSource(sa.kubeResources) - sa.fileSource = src - } - src.SetDefaultNamespace(sa.namespace) - - var errs error - - // If we encounter any errors reading or applying files, track them but attempt to continue - for _, r := range readers { - by, err := io.ReadAll(r.Reader) - if err != nil { - errs = multierror.Append(errs, err) - continue - } - - if err = src.ApplyContent(r.Name, string(by)); err != nil { - errs = multierror.Append(errs, err) - } - } - return errs -} - -// AddRunningKubeSource adds a source based on a running k8s cluster to the current IstiodAnalyzer -// Also tries to get mesh config from the running cluster, if it can -func (sa *IstiodAnalyzer) AddRunningKubeSource(c kubelib.Client) { - sa.AddRunningKubeSourceWithRevision(c, "default") -} - -func (sa *IstiodAnalyzer) AddRunningKubeSourceWithRevision(c kubelib.Client, revision string) { - // TODO: are either of these string constants intended to vary? - // This gets us only istio/ ones - store, err := crdclient.NewForSchemas(c, revision, "cluster.local", sa.kubeResources) - // RunAndWait must be called after NewForSchema so that the informers are all created and started. - if err != nil { - scope.Analysis.Errorf("error adding kube crdclient: %v", err) - return - } - sa.stores = append(sa.stores, store) - err = store.SetWatchErrorHandler(func(r *cache.Reflector, err error) { - // failed resources will never be synced, which causes the process to hang indefinitely. - // better to fail fast, and get a good idea for the failure. - scope.Analysis.Errorf("Failed to watch crd resource for analysis: %s", err) - }) - if err != nil { - scope.Analysis.Errorf("error setting up error handling for kube crdclient: %v", err) - return - } - sa.clientsToRun = append(sa.clientsToRun, c) - - // Since we're using a running k8s source, try to get meshconfig and meshnetworks from the configmap. - if err := sa.addRunningKubeIstioConfigMapSource(c); err != nil { - _, err := c.CoreV1().Namespaces().Get(context.TODO(), sa.istioNamespace.String(), metav1.GetOptions{}) - if kerrors.IsNotFound(err) { - // An AnalysisMessage already show up to warn the absence of dubbo-system namespace, so making it debug level. - scope.Analysis.Debugf("%v namespace not found. Istio may not be installed in the target cluster. "+ - "Using default mesh configuration values for analysis", sa.istioNamespace.String()) - } else if err != nil { - scope.Analysis.Errorf("error getting mesh config from running kube source: %v", err) - } - } -} - -// AddSource adds a source based on user supplied configstore to the current IstiodAnalyzer -// Assumes that the source has same or subset of resource types that this analyzer is configured with. -// This can be used by external users who import the analyzer as a module within their own controllers. -func (sa *IstiodAnalyzer) AddSource(src model.ConfigStoreController) { - sa.stores = append(sa.stores, src) -} - -// AddFileKubeMeshConfig gets mesh config from the specified yaml file -func (sa *IstiodAnalyzer) AddFileKubeMeshConfig(file string) error { - by, err := os.ReadFile(file) - if err != nil { - return err - } - - cfg, err := mesh.ApplyMeshConfigDefaults(string(by)) - if err != nil { - return err - } - - sa.meshCfg = cfg - return nil -} - -// AddFileKubeMeshNetworks gets a file meshnetworks and add it to the analyzer. -func (sa *IstiodAnalyzer) AddFileKubeMeshNetworks(file string) error { - mn, err := mesh.ReadMeshNetworks(file) - if err != nil { - return err - } - - sa.meshNetworks = mn - return nil -} - -// AddDefaultResources adds some basic dummy Istio resources, based on mesh configuration. -// This is useful for files-only analysis cases where we don't expect the user to be including istio system resources -// and don't want to generate false positives because they aren't there. -// Respect mesh config when deciding which default resources should be generated -func (sa *IstiodAnalyzer) AddDefaultResources() error { - var readers []ReaderSource - - if sa.meshCfg.GetIngressControllerMode() != v1alpha1.MeshConfig_OFF { - ingressResources, err := getDefaultIstioIngressGateway(sa.istioNamespace.String(), sa.meshCfg.GetIngressService()) - if err != nil { - return err - } - readers = append(readers, ReaderSource{Reader: strings.NewReader(ingressResources), Name: "internal-ingress"}) - } - - if len(readers) == 0 { - return nil - } - - return sa.AddReaderKubeSource(readers) -} - -func (sa *IstiodAnalyzer) addRunningKubeIstioConfigMapSource(client kubelib.Client) error { - meshConfigMap, err := client.CoreV1().ConfigMaps(string(sa.istioNamespace)).Get(context.TODO(), meshConfigMapName, metav1.GetOptions{}) - if err != nil { - return fmt.Errorf("could not read configmap %q from namespace %q: %v", meshConfigMapName, sa.istioNamespace, err) - } - - configYaml, ok := meshConfigMap.Data[meshConfigMapKey] - if !ok { - return fmt.Errorf("missing config map key %q", meshConfigMapKey) - } - - cfg, err := mesh.ApplyMeshConfigDefaults(configYaml) - if err != nil { - return fmt.Errorf("error parsing mesh config: %v", err) - } - - sa.meshCfg = cfg - - meshNetworksYaml, ok := meshConfigMap.Data[meshNetworksMapKey] - if !ok { - return fmt.Errorf("missing config map key %q", meshNetworksMapKey) - } - - mn, err := mesh.ParseMeshNetworks(meshNetworksYaml) - if err != nil { - return fmt.Errorf("error parsing mesh networks: %v", err) - } - - sa.meshNetworks = mn - return nil -} - -// CollectionReporterFn is a hook function called whenever a collection is accessed through the AnalyzingDistributor's context -type CollectionReporterFn func(collection.Name) - -// copied from processing/snapshotter/analyzingdistributor.go -func filterMessages(messages diag.Messages, namespaces map[resource.Namespace]struct{}, suppressions []AnalysisSuppression) diag.Messages { - nsNames := sets.New() - for k := range namespaces { - nsNames.Insert(k.String()) - } - - var msgs diag.Messages -FilterMessages: - for _, m := range messages { - // Only keep messages for resources in namespaces we want to analyze if the - // message doesn't have an origin (meaning we can't determine the - // namespace). Also kept are cluster-level resources where the namespace is - // the empty string. If no such limit is specified, keep them all. - if len(namespaces) > 0 && m.Resource != nil && m.Resource.Origin.Namespace() != "" { - if !nsNames.Contains(m.Resource.Origin.Namespace().String()) { - continue FilterMessages - } - } - - // Filter out any messages on resources with suppression annotations. - if m.Resource != nil && m.Resource.Metadata.Annotations[annotation.GalleyAnalyzeSuppress.Name] != "" { - for _, code := range strings.Split(m.Resource.Metadata.Annotations[annotation.GalleyAnalyzeSuppress.Name], ",") { - if code == "*" || m.Type.Code() == code { - scope.Analysis.Debugf("Suppressing code %s on resource %s due to resource annotation", m.Type.Code(), m.Resource.Origin.FriendlyName()) - continue FilterMessages - } - } - } - - // Filter out any messages that match our suppressions. - for _, s := range suppressions { - if m.Resource == nil || s.Code != m.Type.Code() { - continue - } - - if !glob.Glob(s.ResourceName, m.Resource.Origin.FriendlyName()) { - continue - } - scope.Analysis.Debugf("Suppressing code %s on resource %s due to suppressions list", m.Type.Code(), m.Resource.Origin.FriendlyName()) - continue FilterMessages - } - - msgs = append(msgs, m) - } - return msgs -} - -// AnalysisSuppression describes a resource and analysis code to be suppressed -// (e.g. ignored) during analysis. Used when a particular message code is to be -// ignored for a specific resource. -type AnalysisSuppression struct { - // Code is the analysis code to suppress (e.g. "IST0104"). - Code string - - // ResourceName is the name of the resource to suppress the message for. For - // K8s resources it has the same form as used by istioctl (e.g. - // "DestinationRule default.dubbo-system"). Note that globbing wildcards are - // supported (e.g. "DestinationRule *.dubbo-system"). - ResourceName string -} - -// ReaderSource is a tuple of a io.Reader and filepath. -type ReaderSource struct { - // Name is the name of the source (commonly the path to a file, but can be "-" for sources read from stdin or "" if completely synthetic). - Name string - // Reader is the reader instance to use. - Reader io.Reader -} diff --git a/pkg/config/analysis/local/local.go b/pkg/config/analysis/local/local.go deleted file mode 100644 index b9121b3f2..000000000 --- a/pkg/config/analysis/local/local.go +++ /dev/null @@ -1,34 +0,0 @@ -/* - Copyright Istio Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package local - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/diag" -) - -const ( - meshConfigMapKey = "mesh" - meshConfigMapName = "istio" - meshNetworksMapKey = "meshNetworks" -) - -// AnalysisResult represents the returnable results of an analysis execution -type AnalysisResult struct { - Messages diag.Messages - SkippedAnalyzers []string - ExecutedAnalyzers []string -} diff --git a/pkg/config/analysis/metadata.go b/pkg/config/analysis/metadata.go deleted file mode 100644 index 416bfe077..000000000 --- a/pkg/config/analysis/metadata.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package analysis - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" -) - -// Metadata represents metadata for an analyzer -type Metadata struct { - Name string - // Description is a short explanation of what the analyzer checks. This - // field is displayed to users when --list-analyzers is called. - Description string - Inputs collection.Names -} diff --git a/pkg/config/analysis/msg/generate.main.go b/pkg/config/analysis/msg/generate.main.go deleted file mode 100644 index ff19e1e45..000000000 --- a/pkg/config/analysis/msg/generate.main.go +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build ignore -// +build ignore - -package main - -import ( - "bytes" - "fmt" - "os" - "regexp" - "text/template" -) - -import ( - "sigs.k8s.io/yaml" -) - -const ( - codeRegex = `^IST\d\d\d\d$` - nameRegex = `^[[:upper:]]\w*$` -) - -// Utility for generating messages.gen.go. Called from gen.go -func main() { - if len(os.Args) != 3 { - fmt.Println("Invalid args:", os.Args) - os.Exit(-1) - } - - input := os.Args[1] - output := os.Args[2] - - m, err := read(input) - if err != nil { - fmt.Println("Error reading metadata:", err) - os.Exit(-2) - } - - err = validate(m) - if err != nil { - fmt.Println("Error validating messages:", err) - os.Exit(-3) - } - - code, err := generate(m) - if err != nil { - fmt.Println("Error generating code:", err) - os.Exit(-4) - } - - if err = os.WriteFile(output, []byte(code), os.ModePerm); err != nil { - fmt.Println("Error writing output file:", err) - os.Exit(-5) - } -} - -func read(path string) (*messages, error) { - b, err := os.ReadFile(path) - if err != nil { - return nil, fmt.Errorf("unable to read input file: %v", err) - } - - m := &messages{} - - if err := yaml.Unmarshal(b, m); err != nil { - return nil, err - } - - return m, nil -} - -// Enforce that names and codes follow expected regex and are unique -func validate(ms *messages) error { - codes := make(map[string]bool) - names := make(map[string]bool) - - for _, m := range ms.Messages { - matched, err := regexp.MatchString(codeRegex, m.Code) - if err != nil { - return err - } - if !matched { - return fmt.Errorf("Error code for message %q must follow the regex %s", m.Name, codeRegex) - } - - if codes[m.Code] { - return fmt.Errorf("Error codes must be unique, %q defined more than once", m.Code) - } - codes[m.Code] = true - - matched, err = regexp.MatchString(nameRegex, m.Name) - if err != nil { - return err - } - if !matched { - return fmt.Errorf("Name for message %q must follow the regex %s", m.Name, nameRegex) - } - - if names[m.Name] { - return fmt.Errorf("Message names must be unique, %q defined more than once", m.Name) - } - names[m.Name] = true - } - return nil -} - -var tmpl = ` -// GENERATED FILE -- DO NOT EDIT -// - -package msg - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/diag" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" -) - -var ( - {{- range .Messages}} - // {{.Name}} defines a diag.MessageType for message "{{.Name}}". - // Description: {{.Description}} - {{.Name}} = diag.NewMessageType(diag.{{.Level}}, "{{.Code}}", "{{.Template}}") - {{end}} -) - -// All returns a list of all known message types. -func All() []*diag.MessageType { - return []*diag.MessageType{ - {{- range .Messages}} - {{.Name}}, - {{- end}} - } -} - -{{range .Messages}} -// New{{.Name}} returns a new diag.Message based on {{.Name}}. -func New{{.Name}}(r *resource.Instance{{range .Args}}, {{.Name}} {{.Type}}{{end}}) diag.Message { - return diag.NewMessage( - {{.Name}}, - r, - {{- range .Args}} - {{.Name}}, - {{- end}} - ) -} -{{end}} -` - -func generate(m *messages) (string, error) { - t := template.Must(template.New("code").Parse(tmpl)) - - var b bytes.Buffer - if err := t.Execute(&b, m); err != nil { - return "", err - } - return b.String(), nil -} - -type messages struct { - Messages []message `json:"messages"` -} - -type message struct { - Name string `json:"name"` - Code string `json:"code"` - Level string `json:"level"` - Description string `json:"description"` - Template string `json:"template"` - Url string `json:"url"` - Args []arg `json:"args"` -} - -type arg struct { - Name string `json:"name"` - Type string `json:"type"` -} diff --git a/pkg/config/analysis/msg/messages.gen.go b/pkg/config/analysis/msg/messages.gen.go deleted file mode 100755 index 1c2be3f85..000000000 --- a/pkg/config/analysis/msg/messages.gen.go +++ /dev/null @@ -1,735 +0,0 @@ -// GENERATED FILE -- DO NOT EDIT -// - -package msg - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/diag" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" -) - -var ( - // InternalError defines a diag.MessageType for message "InternalError". - // Description: There was an internal error in the toolchain. This is almost always a bug in the implementation. - InternalError = diag.NewMessageType(diag.Error, "IST0001", "Internal error: %v") - - // Deprecated defines a diag.MessageType for message "Deprecated". - // Description: A feature that the configuration is depending on is now deprecated. - Deprecated = diag.NewMessageType(diag.Warning, "IST0002", "Deprecated: %s") - - // ReferencedResourceNotFound defines a diag.MessageType for message "ReferencedResourceNotFound". - // Description: A resource being referenced does not exist. - ReferencedResourceNotFound = diag.NewMessageType(diag.Error, "IST0101", "Referenced %s not found: %q") - - // NamespaceNotInjected defines a diag.MessageType for message "NamespaceNotInjected". - // Description: A namespace is not enabled for Istio injection. - NamespaceNotInjected = diag.NewMessageType(diag.Info, "IST0102", "The namespace is not enabled for Istio injection. Run 'kubectl label namespace %s istio-injection=enabled' to enable it, or 'kubectl label namespace %s istio-injection=disabled' to explicitly mark it as not needing injection.") - - // PodMissingProxy defines a diag.MessageType for message "PodMissingProxy". - // Description: A pod is missing the Istio proxy. - PodMissingProxy = diag.NewMessageType(diag.Warning, "IST0103", "The pod %s is missing the Istio proxy. This can often be resolved by restarting or redeploying the workload.") - - // GatewayPortNotOnWorkload defines a diag.MessageType for message "GatewayPortNotOnWorkload". - // Description: Unhandled gateway port - GatewayPortNotOnWorkload = diag.NewMessageType(diag.Warning, "IST0104", "The gateway refers to a port that is not exposed on the workload (pod selector %s; port %d)") - - // IstioProxyImageMismatch defines a diag.MessageType for message "IstioProxyImageMismatch". - // Description: The image of the Istio proxy running on the pod does not match the image defined in the injection configuration. - IstioProxyImageMismatch = diag.NewMessageType(diag.Warning, "IST0105", "The image of the Istio proxy running on the pod does not match the image defined in the injection configuration (pod image: %s; injection configuration image: %s). This often happens after upgrading the Istio control-plane and can be fixed by redeploying the pod.") - - // SchemaValidationError defines a diag.MessageType for message "SchemaValidationError". - // Description: The resource has a schema validation error. - SchemaValidationError = diag.NewMessageType(diag.Error, "IST0106", "Schema validation error: %v") - - // MisplacedAnnotation defines a diag.MessageType for message "MisplacedAnnotation". - // Description: An Istio annotation is applied to the wrong kind of resource. - MisplacedAnnotation = diag.NewMessageType(diag.Warning, "IST0107", "Misplaced annotation: %s can only be applied to %s") - - // UnknownAnnotation defines a diag.MessageType for message "UnknownAnnotation". - // Description: An Istio annotation is not recognized for any kind of resource - UnknownAnnotation = diag.NewMessageType(diag.Warning, "IST0108", "Unknown annotation: %s") - - // ConflictingMeshGatewayVirtualServiceHosts defines a diag.MessageType for message "ConflictingMeshGatewayVirtualServiceHosts". - // Description: Conflicting hosts on VirtualServices associated with mesh gateway - ConflictingMeshGatewayVirtualServiceHosts = diag.NewMessageType(diag.Error, "IST0109", "The VirtualServices %s associated with mesh gateway define the same host %s which can lead to undefined behavior. This can be fixed by merging the conflicting VirtualServices into a single resource.") - - // ConflictingSidecarWorkloadSelectors defines a diag.MessageType for message "ConflictingSidecarWorkloadSelectors". - // Description: A Sidecar resource selects the same workloads as another Sidecar resource - ConflictingSidecarWorkloadSelectors = diag.NewMessageType(diag.Error, "IST0110", "The Sidecars %v in namespace %q select the same workload pod %q, which can lead to undefined behavior.") - - // MultipleSidecarsWithoutWorkloadSelectors defines a diag.MessageType for message "MultipleSidecarsWithoutWorkloadSelectors". - // Description: More than one sidecar resource in a namespace has no workload selector - MultipleSidecarsWithoutWorkloadSelectors = diag.NewMessageType(diag.Error, "IST0111", "The Sidecars %v in namespace %q have no workload selector, which can lead to undefined behavior.") - - // VirtualServiceDestinationPortSelectorRequired defines a diag.MessageType for message "VirtualServiceDestinationPortSelectorRequired". - // Description: A VirtualService routes to a service with more than one port exposed, but does not specify which to use. - VirtualServiceDestinationPortSelectorRequired = diag.NewMessageType(diag.Error, "IST0112", "This VirtualService routes to a service %q that exposes multiple ports %v. Specifying a port in the destination is required to disambiguate.") - - // MTLSPolicyConflict defines a diag.MessageType for message "MTLSPolicyConflict". - // Description: A DestinationRule and Policy are in conflict with regards to mTLS. - MTLSPolicyConflict = diag.NewMessageType(diag.Error, "IST0113", "A DestinationRule and Policy are in conflict with regards to mTLS for host %s. The DestinationRule %q specifies that mTLS must be %t but the Policy object %q specifies %s.") - - // DeploymentAssociatedToMultipleServices defines a diag.MessageType for message "DeploymentAssociatedToMultipleServices". - // Description: The resulting pods of a service mesh deployment can't be associated with multiple services using the same port but different protocols. - DeploymentAssociatedToMultipleServices = diag.NewMessageType(diag.Warning, "IST0116", "This deployment %s is associated with multiple services using port %d but different protocols: %v") - - // DeploymentRequiresServiceAssociated defines a diag.MessageType for message "DeploymentRequiresServiceAssociated". - // Description: The resulting pods of a service mesh deployment must be associated with at least one service. - DeploymentRequiresServiceAssociated = diag.NewMessageType(diag.Warning, "IST0117", "No service associated with this deployment. Service mesh deployments must be associated with a service.") - - // PortNameIsNotUnderNamingConvention defines a diag.MessageType for message "PortNameIsNotUnderNamingConvention". - // Description: Port name is not under naming convention. Protocol detection is applied to the port. - PortNameIsNotUnderNamingConvention = diag.NewMessageType(diag.Info, "IST0118", "Port name %s (port: %d, targetPort: %s) doesn't follow the naming convention of Istio port.") - - // JwtFailureDueToInvalidServicePortPrefix defines a diag.MessageType for message "JwtFailureDueToInvalidServicePortPrefix". - // Description: Authentication policy with JWT targets Service with invalid port specification. - JwtFailureDueToInvalidServicePortPrefix = diag.NewMessageType(diag.Warning, "IST0119", "Authentication policy with JWT targets Service with invalid port specification (port: %d, name: %s, protocol: %s, targetPort: %s).") - - // InvalidRegexp defines a diag.MessageType for message "InvalidRegexp". - // Description: Invalid Regex - InvalidRegexp = diag.NewMessageType(diag.Warning, "IST0122", "Field %q regular expression invalid: %q (%s)") - - // NamespaceMultipleInjectionLabels defines a diag.MessageType for message "NamespaceMultipleInjectionLabels". - // Description: A namespace has both new and legacy injection labels - NamespaceMultipleInjectionLabels = diag.NewMessageType(diag.Warning, "IST0123", "The namespace has both new and legacy injection labels. Run 'kubectl label namespace %s istio.io/rev-' or 'kubectl label namespace %s istio-injection-'") - - // InvalidAnnotation defines a diag.MessageType for message "InvalidAnnotation". - // Description: An Istio annotation that is not valid - InvalidAnnotation = diag.NewMessageType(diag.Warning, "IST0125", "Invalid annotation %s: %s") - - // UnknownMeshNetworksServiceRegistry defines a diag.MessageType for message "UnknownMeshNetworksServiceRegistry". - // Description: A service registry in Mesh Networks is unknown - UnknownMeshNetworksServiceRegistry = diag.NewMessageType(diag.Error, "IST0126", "Unknown service registry %s in network %s") - - // NoMatchingWorkloadsFound defines a diag.MessageType for message "NoMatchingWorkloadsFound". - // Description: There aren't workloads matching the resource labels - NoMatchingWorkloadsFound = diag.NewMessageType(diag.Warning, "IST0127", "No matching workloads for this resource with the following labels: %s") - - // NoServerCertificateVerificationDestinationLevel defines a diag.MessageType for message "NoServerCertificateVerificationDestinationLevel". - // Description: No caCertificates are set in DestinationRule, this results in no verification of presented server certificate. - NoServerCertificateVerificationDestinationLevel = diag.NewMessageType(diag.Error, "IST0128", "DestinationRule %s in namespace %s has TLS mode set to %s but no caCertificates are set to validate server identity for host: %s") - - // NoServerCertificateVerificationPortLevel defines a diag.MessageType for message "NoServerCertificateVerificationPortLevel". - // Description: No caCertificates are set in DestinationRule, this results in no verification of presented server certificate for traffic to a given port. - NoServerCertificateVerificationPortLevel = diag.NewMessageType(diag.Warning, "IST0129", "DestinationRule %s in namespace %s has TLS mode set to %s but no caCertificates are set to validate server identity for host: %s at port %s") - - // VirtualServiceUnreachableRule defines a diag.MessageType for message "VirtualServiceUnreachableRule". - // Description: A VirtualService rule will never be used because a previous rule uses the same match. - VirtualServiceUnreachableRule = diag.NewMessageType(diag.Warning, "IST0130", "VirtualService rule %v not used (%s).") - - // VirtualServiceIneffectiveMatch defines a diag.MessageType for message "VirtualServiceIneffectiveMatch". - // Description: A VirtualService rule match duplicates a match in a previous rule. - VirtualServiceIneffectiveMatch = diag.NewMessageType(diag.Info, "IST0131", "VirtualService rule %v match %v is not used (duplicate/overlapping match in rule %v).") - - // VirtualServiceHostNotFoundInGateway defines a diag.MessageType for message "VirtualServiceHostNotFoundInGateway". - // Description: Host defined in VirtualService not found in Gateway. - VirtualServiceHostNotFoundInGateway = diag.NewMessageType(diag.Warning, "IST0132", "one or more host %v defined in VirtualService %s not found in Gateway %s.") - - // SchemaWarning defines a diag.MessageType for message "SchemaWarning". - // Description: The resource has a schema validation warning. - SchemaWarning = diag.NewMessageType(diag.Warning, "IST0133", "Schema validation warning: %v") - - // ServiceEntryAddressesRequired defines a diag.MessageType for message "ServiceEntryAddressesRequired". - // Description: Virtual IP addresses are required for ports serving TCP (or unset) protocol - ServiceEntryAddressesRequired = diag.NewMessageType(diag.Warning, "IST0134", "ServiceEntry addresses are required for this protocol.") - - // DeprecatedAnnotation defines a diag.MessageType for message "DeprecatedAnnotation". - // Description: A resource is using a deprecated Istio annotation. - DeprecatedAnnotation = diag.NewMessageType(diag.Info, "IST0135", "Annotation %q has been deprecated%s and may not work in future Istio versions.") - - // AlphaAnnotation defines a diag.MessageType for message "AlphaAnnotation". - // Description: An Istio annotation may not be suitable for production. - AlphaAnnotation = diag.NewMessageType(diag.Info, "IST0136", "Annotation %q is part of an alpha-phase feature and may be incompletely supported.") - - // DeploymentConflictingPorts defines a diag.MessageType for message "DeploymentConflictingPorts". - // Description: Two services selecting the same workload with the same targetPort MUST refer to the same port. - DeploymentConflictingPorts = diag.NewMessageType(diag.Warning, "IST0137", "This deployment %s is associated with multiple services %v using targetPort %q but different ports: %v.") - - // GatewayDuplicateCertificate defines a diag.MessageType for message "GatewayDuplicateCertificate". - // Description: Duplicate certificate in multiple gateways may cause 404s if clients re-use HTTP2 connections. - GatewayDuplicateCertificate = diag.NewMessageType(diag.Warning, "IST0138", "Duplicate certificate in multiple gateways %v may cause 404s if clients re-use HTTP2 connections.") - - // InvalidWebhook defines a diag.MessageType for message "InvalidWebhook". - // Description: Webhook is invalid or references a control plane service that does not exist. - InvalidWebhook = diag.NewMessageType(diag.Error, "IST0139", "%v") - - // IngressRouteRulesNotAffected defines a diag.MessageType for message "IngressRouteRulesNotAffected". - // Description: Route rules have no effect on ingress gateway requests - IngressRouteRulesNotAffected = diag.NewMessageType(diag.Warning, "IST0140", "Subset in virtual service %s has no effect on ingress gateway %s requests") - - // InsufficientPermissions defines a diag.MessageType for message "InsufficientPermissions". - // Description: Required permissions to install Istio are missing. - InsufficientPermissions = diag.NewMessageType(diag.Error, "IST0141", "Missing required permission to create resource %v (%v)") - - // UnsupportedKubernetesVersion defines a diag.MessageType for message "UnsupportedKubernetesVersion". - // Description: The Kubernetes version is not supported - UnsupportedKubernetesVersion = diag.NewMessageType(diag.Error, "IST0142", "The Kubernetes Version %q is lower than the minimum version: %v") - - // LocalhostListener defines a diag.MessageType for message "LocalhostListener". - // Description: A port exposed in a Service is bound to a localhost address - LocalhostListener = diag.NewMessageType(diag.Error, "IST0143", "Port %v is exposed in a Service but listens on localhost. It will not be exposed to other pods.") - - // InvalidApplicationUID defines a diag.MessageType for message "InvalidApplicationUID". - // Description: Application pods should not run as user ID (UID) 1337 - InvalidApplicationUID = diag.NewMessageType(diag.Warning, "IST0144", "User ID (UID) 1337 is reserved for the sidecar proxy.") - - // ConflictingGateways defines a diag.MessageType for message "ConflictingGateways". - // Description: Gateway should not have the same selector, port and matched hosts of server - ConflictingGateways = diag.NewMessageType(diag.Error, "IST0145", "Conflict with gateways %s (workload selector %s, port %s, hosts %v).") - - // ImageAutoWithoutInjectionWarning defines a diag.MessageType for message "ImageAutoWithoutInjectionWarning". - // Description: Deployments with `image: auto` should be targeted for injection. - ImageAutoWithoutInjectionWarning = diag.NewMessageType(diag.Warning, "IST0146", "%s %s contains `image: auto` but does not match any Istio injection webhook selectors.") - - // ImageAutoWithoutInjectionError defines a diag.MessageType for message "ImageAutoWithoutInjectionError". - // Description: Pods with `image: auto` should be targeted for injection. - ImageAutoWithoutInjectionError = diag.NewMessageType(diag.Error, "IST0147", "%s %s contains `image: auto` but does not match any Istio injection webhook selectors.") - - // NamespaceInjectionEnabledByDefault defines a diag.MessageType for message "NamespaceInjectionEnabledByDefault". - // Description: user namespace should be injectable if Istio is installed with enableNamespacesByDefault enabled and neither injection label is set. - NamespaceInjectionEnabledByDefault = diag.NewMessageType(diag.Info, "IST0148", "is enabled for Istio injection, as Istio is installed with enableNamespacesByDefault as true.") - - // JwtClaimBasedRoutingWithoutRequestAuthN defines a diag.MessageType for message "JwtClaimBasedRoutingWithoutRequestAuthN". - // Description: Virtual service using JWT claim based routing without request authentication. - JwtClaimBasedRoutingWithoutRequestAuthN = diag.NewMessageType(diag.Error, "IST0149", "The virtual service uses the JWT claim based routing (key: %s) but found no request authentication for the gateway (%s) pod (%s). The request authentication must first be applied for the gateway pods to validate the JWT token and make the claims available for routing.") - - // ExternalNameServiceTypeInvalidPortName defines a diag.MessageType for message "ExternalNameServiceTypeInvalidPortName". - // Description: Proxy may prevent tcp named ports and unmatched traffic for ports serving TCP protocol from being forwarded correctly for ExternalName services. - ExternalNameServiceTypeInvalidPortName = diag.NewMessageType(diag.Warning, "IST0150", "Port name for ExternalName service is invalid. Proxy may prevent tcp named ports and unmatched traffic for ports serving TCP protocol from being forwarded correctly") - - // EnvoyFilterUsesRelativeOperation defines a diag.MessageType for message "EnvoyFilterUsesRelativeOperation". - // Description: This envoy filter does not have a priority and has a relative patch operation set which can cause the envoyFilter not to be applied. Using the INSERT_FIRST option or setting the priority may help in ensuring the envoyFilter is applied correctly - EnvoyFilterUsesRelativeOperation = diag.NewMessageType(diag.Warning, "IST0151", "This envoy filter does not have a priority and has a relative patch operation set which can cause the envoyFilter not to be applied. Using the INSERT_FIRST option or setting the priority may help in ensuring the envoyFilter is applied correctly") -) - -// All returns a list of all known message types. -func All() []*diag.MessageType { - return []*diag.MessageType{ - InternalError, - Deprecated, - ReferencedResourceNotFound, - NamespaceNotInjected, - PodMissingProxy, - GatewayPortNotOnWorkload, - IstioProxyImageMismatch, - SchemaValidationError, - MisplacedAnnotation, - UnknownAnnotation, - ConflictingMeshGatewayVirtualServiceHosts, - ConflictingSidecarWorkloadSelectors, - MultipleSidecarsWithoutWorkloadSelectors, - VirtualServiceDestinationPortSelectorRequired, - MTLSPolicyConflict, - DeploymentAssociatedToMultipleServices, - DeploymentRequiresServiceAssociated, - PortNameIsNotUnderNamingConvention, - JwtFailureDueToInvalidServicePortPrefix, - InvalidRegexp, - NamespaceMultipleInjectionLabels, - InvalidAnnotation, - UnknownMeshNetworksServiceRegistry, - NoMatchingWorkloadsFound, - NoServerCertificateVerificationDestinationLevel, - NoServerCertificateVerificationPortLevel, - VirtualServiceUnreachableRule, - VirtualServiceIneffectiveMatch, - VirtualServiceHostNotFoundInGateway, - SchemaWarning, - ServiceEntryAddressesRequired, - DeprecatedAnnotation, - AlphaAnnotation, - DeploymentConflictingPorts, - GatewayDuplicateCertificate, - InvalidWebhook, - IngressRouteRulesNotAffected, - InsufficientPermissions, - UnsupportedKubernetesVersion, - LocalhostListener, - InvalidApplicationUID, - ConflictingGateways, - ImageAutoWithoutInjectionWarning, - ImageAutoWithoutInjectionError, - NamespaceInjectionEnabledByDefault, - JwtClaimBasedRoutingWithoutRequestAuthN, - ExternalNameServiceTypeInvalidPortName, - EnvoyFilterUsesRelativeOperation, - } -} - -// NewInternalError returns a new diag.Message based on InternalError. -func NewInternalError(r *resource.Instance, detail string) diag.Message { - return diag.NewMessage( - InternalError, - r, - detail, - ) -} - -// NewDeprecated returns a new diag.Message based on Deprecated. -func NewDeprecated(r *resource.Instance, detail string) diag.Message { - return diag.NewMessage( - Deprecated, - r, - detail, - ) -} - -// NewReferencedResourceNotFound returns a new diag.Message based on ReferencedResourceNotFound. -func NewReferencedResourceNotFound(r *resource.Instance, reftype string, refval string) diag.Message { - return diag.NewMessage( - ReferencedResourceNotFound, - r, - reftype, - refval, - ) -} - -// NewNamespaceNotInjected returns a new diag.Message based on NamespaceNotInjected. -func NewNamespaceNotInjected(r *resource.Instance, namespace string, namespace2 string) diag.Message { - return diag.NewMessage( - NamespaceNotInjected, - r, - namespace, - namespace2, - ) -} - -// NewPodMissingProxy returns a new diag.Message based on PodMissingProxy. -func NewPodMissingProxy(r *resource.Instance, podName string) diag.Message { - return diag.NewMessage( - PodMissingProxy, - r, - podName, - ) -} - -// NewGatewayPortNotOnWorkload returns a new diag.Message based on GatewayPortNotOnWorkload. -func NewGatewayPortNotOnWorkload(r *resource.Instance, selector string, port int) diag.Message { - return diag.NewMessage( - GatewayPortNotOnWorkload, - r, - selector, - port, - ) -} - -// NewIstioProxyImageMismatch returns a new diag.Message based on IstioProxyImageMismatch. -func NewIstioProxyImageMismatch(r *resource.Instance, proxyImage string, injectionImage string) diag.Message { - return diag.NewMessage( - IstioProxyImageMismatch, - r, - proxyImage, - injectionImage, - ) -} - -// NewSchemaValidationError returns a new diag.Message based on SchemaValidationError. -func NewSchemaValidationError(r *resource.Instance, err error) diag.Message { - return diag.NewMessage( - SchemaValidationError, - r, - err, - ) -} - -// NewMisplacedAnnotation returns a new diag.Message based on MisplacedAnnotation. -func NewMisplacedAnnotation(r *resource.Instance, annotation string, kind string) diag.Message { - return diag.NewMessage( - MisplacedAnnotation, - r, - annotation, - kind, - ) -} - -// NewUnknownAnnotation returns a new diag.Message based on UnknownAnnotation. -func NewUnknownAnnotation(r *resource.Instance, annotation string) diag.Message { - return diag.NewMessage( - UnknownAnnotation, - r, - annotation, - ) -} - -// NewConflictingMeshGatewayVirtualServiceHosts returns a new diag.Message based on ConflictingMeshGatewayVirtualServiceHosts. -func NewConflictingMeshGatewayVirtualServiceHosts(r *resource.Instance, virtualServices string, host string) diag.Message { - return diag.NewMessage( - ConflictingMeshGatewayVirtualServiceHosts, - r, - virtualServices, - host, - ) -} - -// NewConflictingSidecarWorkloadSelectors returns a new diag.Message based on ConflictingSidecarWorkloadSelectors. -func NewConflictingSidecarWorkloadSelectors(r *resource.Instance, conflictingSidecars []string, namespace string, workloadPod string) diag.Message { - return diag.NewMessage( - ConflictingSidecarWorkloadSelectors, - r, - conflictingSidecars, - namespace, - workloadPod, - ) -} - -// NewMultipleSidecarsWithoutWorkloadSelectors returns a new diag.Message based on MultipleSidecarsWithoutWorkloadSelectors. -func NewMultipleSidecarsWithoutWorkloadSelectors(r *resource.Instance, conflictingSidecars []string, namespace string) diag.Message { - return diag.NewMessage( - MultipleSidecarsWithoutWorkloadSelectors, - r, - conflictingSidecars, - namespace, - ) -} - -// NewVirtualServiceDestinationPortSelectorRequired returns a new diag.Message based on VirtualServiceDestinationPortSelectorRequired. -func NewVirtualServiceDestinationPortSelectorRequired(r *resource.Instance, destHost string, destPorts []int) diag.Message { - return diag.NewMessage( - VirtualServiceDestinationPortSelectorRequired, - r, - destHost, - destPorts, - ) -} - -// NewMTLSPolicyConflict returns a new diag.Message based on MTLSPolicyConflict. -func NewMTLSPolicyConflict(r *resource.Instance, host string, destinationRuleName string, destinationRuleMTLSMode bool, policyName string, policyMTLSMode string) diag.Message { - return diag.NewMessage( - MTLSPolicyConflict, - r, - host, - destinationRuleName, - destinationRuleMTLSMode, - policyName, - policyMTLSMode, - ) -} - -// NewDeploymentAssociatedToMultipleServices returns a new diag.Message based on DeploymentAssociatedToMultipleServices. -func NewDeploymentAssociatedToMultipleServices(r *resource.Instance, deployment string, port int32, services []string) diag.Message { - return diag.NewMessage( - DeploymentAssociatedToMultipleServices, - r, - deployment, - port, - services, - ) -} - -// NewDeploymentRequiresServiceAssociated returns a new diag.Message based on DeploymentRequiresServiceAssociated. -func NewDeploymentRequiresServiceAssociated(r *resource.Instance) diag.Message { - return diag.NewMessage( - DeploymentRequiresServiceAssociated, - r, - ) -} - -// NewPortNameIsNotUnderNamingConvention returns a new diag.Message based on PortNameIsNotUnderNamingConvention. -func NewPortNameIsNotUnderNamingConvention(r *resource.Instance, portName string, port int, targetPort string) diag.Message { - return diag.NewMessage( - PortNameIsNotUnderNamingConvention, - r, - portName, - port, - targetPort, - ) -} - -// NewJwtFailureDueToInvalidServicePortPrefix returns a new diag.Message based on JwtFailureDueToInvalidServicePortPrefix. -func NewJwtFailureDueToInvalidServicePortPrefix(r *resource.Instance, port int, portName string, protocol string, targetPort string) diag.Message { - return diag.NewMessage( - JwtFailureDueToInvalidServicePortPrefix, - r, - port, - portName, - protocol, - targetPort, - ) -} - -// NewInvalidRegexp returns a new diag.Message based on InvalidRegexp. -func NewInvalidRegexp(r *resource.Instance, where string, re string, problem string) diag.Message { - return diag.NewMessage( - InvalidRegexp, - r, - where, - re, - problem, - ) -} - -// NewNamespaceMultipleInjectionLabels returns a new diag.Message based on NamespaceMultipleInjectionLabels. -func NewNamespaceMultipleInjectionLabels(r *resource.Instance, namespace string, namespace2 string) diag.Message { - return diag.NewMessage( - NamespaceMultipleInjectionLabels, - r, - namespace, - namespace2, - ) -} - -// NewInvalidAnnotation returns a new diag.Message based on InvalidAnnotation. -func NewInvalidAnnotation(r *resource.Instance, annotation string, problem string) diag.Message { - return diag.NewMessage( - InvalidAnnotation, - r, - annotation, - problem, - ) -} - -// NewUnknownMeshNetworksServiceRegistry returns a new diag.Message based on UnknownMeshNetworksServiceRegistry. -func NewUnknownMeshNetworksServiceRegistry(r *resource.Instance, serviceregistry string, network string) diag.Message { - return diag.NewMessage( - UnknownMeshNetworksServiceRegistry, - r, - serviceregistry, - network, - ) -} - -// NewNoMatchingWorkloadsFound returns a new diag.Message based on NoMatchingWorkloadsFound. -func NewNoMatchingWorkloadsFound(r *resource.Instance, labels string) diag.Message { - return diag.NewMessage( - NoMatchingWorkloadsFound, - r, - labels, - ) -} - -// NewNoServerCertificateVerificationDestinationLevel returns a new diag.Message based on NoServerCertificateVerificationDestinationLevel. -func NewNoServerCertificateVerificationDestinationLevel(r *resource.Instance, destinationrule string, namespace string, mode string, host string) diag.Message { - return diag.NewMessage( - NoServerCertificateVerificationDestinationLevel, - r, - destinationrule, - namespace, - mode, - host, - ) -} - -// NewNoServerCertificateVerificationPortLevel returns a new diag.Message based on NoServerCertificateVerificationPortLevel. -func NewNoServerCertificateVerificationPortLevel(r *resource.Instance, destinationrule string, namespace string, mode string, host string, port string) diag.Message { - return diag.NewMessage( - NoServerCertificateVerificationPortLevel, - r, - destinationrule, - namespace, - mode, - host, - port, - ) -} - -// NewVirtualServiceUnreachableRule returns a new diag.Message based on VirtualServiceUnreachableRule. -func NewVirtualServiceUnreachableRule(r *resource.Instance, ruleno string, reason string) diag.Message { - return diag.NewMessage( - VirtualServiceUnreachableRule, - r, - ruleno, - reason, - ) -} - -// NewVirtualServiceIneffectiveMatch returns a new diag.Message based on VirtualServiceIneffectiveMatch. -func NewVirtualServiceIneffectiveMatch(r *resource.Instance, ruleno string, matchno string, dupno string) diag.Message { - return diag.NewMessage( - VirtualServiceIneffectiveMatch, - r, - ruleno, - matchno, - dupno, - ) -} - -// NewVirtualServiceHostNotFoundInGateway returns a new diag.Message based on VirtualServiceHostNotFoundInGateway. -func NewVirtualServiceHostNotFoundInGateway(r *resource.Instance, host []string, virtualservice string, gateway string) diag.Message { - return diag.NewMessage( - VirtualServiceHostNotFoundInGateway, - r, - host, - virtualservice, - gateway, - ) -} - -// NewSchemaWarning returns a new diag.Message based on SchemaWarning. -func NewSchemaWarning(r *resource.Instance, err error) diag.Message { - return diag.NewMessage( - SchemaWarning, - r, - err, - ) -} - -// NewServiceEntryAddressesRequired returns a new diag.Message based on ServiceEntryAddressesRequired. -func NewServiceEntryAddressesRequired(r *resource.Instance) diag.Message { - return diag.NewMessage( - ServiceEntryAddressesRequired, - r, - ) -} - -// NewDeprecatedAnnotation returns a new diag.Message based on DeprecatedAnnotation. -func NewDeprecatedAnnotation(r *resource.Instance, annotation string, extra string) diag.Message { - return diag.NewMessage( - DeprecatedAnnotation, - r, - annotation, - extra, - ) -} - -// NewAlphaAnnotation returns a new diag.Message based on AlphaAnnotation. -func NewAlphaAnnotation(r *resource.Instance, annotation string) diag.Message { - return diag.NewMessage( - AlphaAnnotation, - r, - annotation, - ) -} - -// NewDeploymentConflictingPorts returns a new diag.Message based on DeploymentConflictingPorts. -func NewDeploymentConflictingPorts(r *resource.Instance, deployment string, services []string, targetPort string, ports []int32) diag.Message { - return diag.NewMessage( - DeploymentConflictingPorts, - r, - deployment, - services, - targetPort, - ports, - ) -} - -// NewGatewayDuplicateCertificate returns a new diag.Message based on GatewayDuplicateCertificate. -func NewGatewayDuplicateCertificate(r *resource.Instance, gateways []string) diag.Message { - return diag.NewMessage( - GatewayDuplicateCertificate, - r, - gateways, - ) -} - -// NewInvalidWebhook returns a new diag.Message based on InvalidWebhook. -func NewInvalidWebhook(r *resource.Instance, error string) diag.Message { - return diag.NewMessage( - InvalidWebhook, - r, - error, - ) -} - -// NewIngressRouteRulesNotAffected returns a new diag.Message based on IngressRouteRulesNotAffected. -func NewIngressRouteRulesNotAffected(r *resource.Instance, virtualservicesubset string, virtualservice string) diag.Message { - return diag.NewMessage( - IngressRouteRulesNotAffected, - r, - virtualservicesubset, - virtualservice, - ) -} - -// NewInsufficientPermissions returns a new diag.Message based on InsufficientPermissions. -func NewInsufficientPermissions(r *resource.Instance, resource string, error string) diag.Message { - return diag.NewMessage( - InsufficientPermissions, - r, - resource, - error, - ) -} - -// NewUnsupportedKubernetesVersion returns a new diag.Message based on UnsupportedKubernetesVersion. -func NewUnsupportedKubernetesVersion(r *resource.Instance, version string, minimumVersion string) diag.Message { - return diag.NewMessage( - UnsupportedKubernetesVersion, - r, - version, - minimumVersion, - ) -} - -// NewLocalhostListener returns a new diag.Message based on LocalhostListener. -func NewLocalhostListener(r *resource.Instance, port string) diag.Message { - return diag.NewMessage( - LocalhostListener, - r, - port, - ) -} - -// NewInvalidApplicationUID returns a new diag.Message based on InvalidApplicationUID. -func NewInvalidApplicationUID(r *resource.Instance) diag.Message { - return diag.NewMessage( - InvalidApplicationUID, - r, - ) -} - -// NewConflictingGateways returns a new diag.Message based on ConflictingGateways. -func NewConflictingGateways(r *resource.Instance, gateway string, selector string, portnumber string, hosts string) diag.Message { - return diag.NewMessage( - ConflictingGateways, - r, - gateway, - selector, - portnumber, - hosts, - ) -} - -// NewImageAutoWithoutInjectionWarning returns a new diag.Message based on ImageAutoWithoutInjectionWarning. -func NewImageAutoWithoutInjectionWarning(r *resource.Instance, resourceType string, resourceName string) diag.Message { - return diag.NewMessage( - ImageAutoWithoutInjectionWarning, - r, - resourceType, - resourceName, - ) -} - -// NewImageAutoWithoutInjectionError returns a new diag.Message based on ImageAutoWithoutInjectionError. -func NewImageAutoWithoutInjectionError(r *resource.Instance, resourceType string, resourceName string) diag.Message { - return diag.NewMessage( - ImageAutoWithoutInjectionError, - r, - resourceType, - resourceName, - ) -} - -// NewNamespaceInjectionEnabledByDefault returns a new diag.Message based on NamespaceInjectionEnabledByDefault. -func NewNamespaceInjectionEnabledByDefault(r *resource.Instance) diag.Message { - return diag.NewMessage( - NamespaceInjectionEnabledByDefault, - r, - ) -} - -// NewJwtClaimBasedRoutingWithoutRequestAuthN returns a new diag.Message based on JwtClaimBasedRoutingWithoutRequestAuthN. -func NewJwtClaimBasedRoutingWithoutRequestAuthN(r *resource.Instance, key string, gateway string, pod string) diag.Message { - return diag.NewMessage( - JwtClaimBasedRoutingWithoutRequestAuthN, - r, - key, - gateway, - pod, - ) -} - -// NewExternalNameServiceTypeInvalidPortName returns a new diag.Message based on ExternalNameServiceTypeInvalidPortName. -func NewExternalNameServiceTypeInvalidPortName(r *resource.Instance) diag.Message { - return diag.NewMessage( - ExternalNameServiceTypeInvalidPortName, - r, - ) -} - -// NewEnvoyFilterUsesRelativeOperation returns a new diag.Message based on EnvoyFilterUsesRelativeOperation. -func NewEnvoyFilterUsesRelativeOperation(r *resource.Instance) diag.Message { - return diag.NewMessage( - EnvoyFilterUsesRelativeOperation, - r, - ) -} diff --git a/pkg/config/analysis/msg/messages.go b/pkg/config/analysis/msg/messages.go deleted file mode 100644 index b5460a4b2..000000000 --- a/pkg/config/analysis/msg/messages.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package msg - -// Create static initializers file -//go:generate go run "$REPO_ROOT/pkg/config/analysis/msg/generate.main.go" messages.yaml messages.gen.go - -//go:generate goimports -w -local istio.io "$REPO_ROOT/pkg/config/analysis/msg/messages.gen.go" diff --git a/pkg/config/analysis/msg/messages.yaml b/pkg/config/analysis/msg/messages.yaml deleted file mode 100644 index b9de6bf35..000000000 --- a/pkg/config/analysis/msg/messages.yaml +++ /dev/null @@ -1,565 +0,0 @@ -# Please keep entries ordered by code. -# NOTE: The range 0000-0100 is reserved for internal and/or future use. -messages: - - name: "InternalError" - code: IST0001 - level: Error - description: "There was an internal error in the toolchain. This is almost always a bug in the implementation." - template: "Internal error: %v" - url: "https://istio.io/latest/docs/reference/config/analysis/ist0001/" - args: - - name: detail - type: string - - - name: "Deprecated" - code: IST0002 - level: Warning - description: "A feature that the configuration is depending on is now deprecated." - template: "Deprecated: %s" - url: "https://istio.io/latest/docs/reference/config/analysis/ist0002/" - args: - - name: detail - type: string - - - name: "ReferencedResourceNotFound" - code: IST0101 - level: Error - description: "A resource being referenced does not exist." - template: "Referenced %s not found: %q" - url: "https://istio.io/latest/docs/reference/config/analysis/ist0101/" - args: - - name: reftype - type: string - - name: refval - type: string - - - name: "NamespaceNotInjected" - code: IST0102 - level: Info - description: "A namespace is not enabled for Istio injection." - template: "The namespace is not enabled for Istio injection. Run 'kubectl label namespace %s istio-injection=enabled' to enable it, or 'kubectl label namespace %s istio-injection=disabled' to explicitly mark it as not needing injection." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0102/" - args: - - name: namespace - type: string - - name: namespace2 - type: string - - - name: "PodMissingProxy" - code: IST0103 - level: Warning - description: "A pod is missing the Istio proxy." - template: "The pod %s is missing the Istio proxy. This can often be resolved by restarting or redeploying the workload." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0103/" - args: - - name: podName - type: string - - - name: "GatewayPortNotOnWorkload" - code: IST0104 - level: Warning - description: "Unhandled gateway port" - template: "The gateway refers to a port that is not exposed on the workload (pod selector %s; port %d)" - url: "https://istio.io/latest/docs/reference/config/analysis/ist0104/" - args: - - name: selector - type: string - - name: port - type: int - - - name: "IstioProxyImageMismatch" - code: IST0105 - level: Warning - description: "The image of the Istio proxy running on the pod does not match the image defined in the injection configuration." - template: "The image of the Istio proxy running on the pod does not match the image defined in the injection configuration (pod image: %s; injection configuration image: %s). This often happens after upgrading the Istio control-plane and can be fixed by redeploying the pod." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0105/" - args: - - name: proxyImage - type: string - - name: injectionImage - type: string - - - name: "SchemaValidationError" - code: IST0106 - level: Error - description: "The resource has a schema validation error." - template: "Schema validation error: %v" - url: "https://istio.io/latest/docs/reference/config/analysis/ist0106/" - args: - - name: err - type: error - - - name: "MisplacedAnnotation" - code: IST0107 - level: Warning - description: "An Istio annotation is applied to the wrong kind of resource." - template: "Misplaced annotation: %s can only be applied to %s" - url: "https://istio.io/latest/docs/reference/config/analysis/ist0107/" - args: - - name: annotation - type: string - - name: kind - type: string - - - name: "UnknownAnnotation" - code: IST0108 - level: Warning - description: "An Istio annotation is not recognized for any kind of resource" - template: "Unknown annotation: %s" - url: "https://istio.io/latest/docs/reference/config/analysis/ist0108/" - args: - - name: annotation - type: string - - - name: "ConflictingMeshGatewayVirtualServiceHosts" - code: IST0109 - level: Error - description: "Conflicting hosts on VirtualServices associated with mesh gateway" - template: "The VirtualServices %s associated with mesh gateway define the same host %s which can lead to undefined behavior. This can be fixed by merging the conflicting VirtualServices into a single resource." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0109/" - args: - - name: virtualServices - type: string - - name: host - type: string - - - name: "ConflictingSidecarWorkloadSelectors" - code: IST0110 - level: Error - description: "A Sidecar resource selects the same workloads as another Sidecar resource" - template: "The Sidecars %v in namespace %q select the same workload pod %q, which can lead to undefined behavior." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0110/" - args: - - name: conflictingSidecars - type: "[]string" - - name: namespace - type: string - - name: workloadPod - type: string - - - name: "MultipleSidecarsWithoutWorkloadSelectors" - code: IST0111 - level: Error - description: "More than one sidecar resource in a namespace has no workload selector" - template: "The Sidecars %v in namespace %q have no workload selector, which can lead to undefined behavior." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0111/" - args: - - name: conflictingSidecars - type: "[]string" - - name: namespace - type: string - - - name: "VirtualServiceDestinationPortSelectorRequired" - code: IST0112 - level: Error - description: "A VirtualService routes to a service with more than one port exposed, but does not specify which to use." - template: "This VirtualService routes to a service %q that exposes multiple ports %v. Specifying a port in the destination is required to disambiguate." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0112/" - args: - - name: destHost - type: string - - name: destPorts - type: "[]int" - - - name: "MTLSPolicyConflict" - code: IST0113 - level: Error - description: "A DestinationRule and Policy are in conflict with regards to mTLS." - template: "A DestinationRule and Policy are in conflict with regards to mTLS for host %s. The DestinationRule %q specifies that mTLS must be %t but the Policy object %q specifies %s." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0113/" - args: - - name: host - type: string - - name: destinationRuleName - type: string - - name: destinationRuleMTLSMode - type: bool - - name: policyName - type: string - - name: policyMTLSMode - type: string - - # IST0114 RETIRED - # IST0115 RETIRED - - - name: "DeploymentAssociatedToMultipleServices" - code: IST0116 - level: Warning - description: "The resulting pods of a service mesh deployment can't be associated with multiple services using the same port but different protocols." - template: "This deployment %s is associated with multiple services using port %d but different protocols: %v" - url: "https://istio.io/latest/docs/reference/config/analysis/ist0116/" - args: - - name: deployment - type: string - - name: port - type: int32 - - name: services - type: "[]string" - - - name: "DeploymentRequiresServiceAssociated" - code: IST0117 - level: Warning - description: "The resulting pods of a service mesh deployment must be associated with at least one service." - template: "No service associated with this deployment. Service mesh deployments must be associated with a service." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0117/" - - - name: "PortNameIsNotUnderNamingConvention" - code: IST0118 - level: Info - description: "Port name is not under naming convention. Protocol detection is applied to the port." - template: "Port name %s (port: %d, targetPort: %s) doesn't follow the naming convention of Istio port." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0118/" - args: - - name: portName - type: string - - name: port - type: int - - name: targetPort - type: string - - - name: "JwtFailureDueToInvalidServicePortPrefix" - code: IST0119 - level: Warning - description: "Authentication policy with JWT targets Service with invalid port specification." - template: "Authentication policy with JWT targets Service with invalid port specification (port: %d, name: %s, protocol: %s, targetPort: %s)." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0119/" - args: - - name: port - type: int - - name: portName - type: string - - name: protocol - type: string - - name: targetPort - type: string - - # IST0120 RETIRED - # IST0121 RETIRED - - - name: "InvalidRegexp" - code: IST0122 - level: Warning - description: "Invalid Regex" - template: "Field %q regular expression invalid: %q (%s)" - url: "https://istio.io/latest/docs/reference/config/analysis/ist0122/" - args: - - name: where - type: string - - name: re - type: string - - name: problem - type: string - - - name: "NamespaceMultipleInjectionLabels" - code: IST0123 - level: Warning - description: "A namespace has both new and legacy injection labels" - template: "The namespace has both new and legacy injection labels. Run 'kubectl label namespace %s istio.io/rev-' or 'kubectl label namespace %s istio-injection-'" - url: "https://istio.io/latest/docs/reference/config/analysis/ist0123/" - args: - - name: namespace - type: string - - name: namespace2 - type: string - - - name: "InvalidAnnotation" - code: IST0125 - level: Warning - description: "An Istio annotation that is not valid" - template: "Invalid annotation %s: %s" - url: "https://istio.io/latest/docs/reference/config/analysis/ist0125/" - args: - - name: annotation - type: string - - name: problem - type: string - - - name: "UnknownMeshNetworksServiceRegistry" - code: IST0126 - level: Error - description: "A service registry in Mesh Networks is unknown" - template: "Unknown service registry %s in network %s" - args: - - name: serviceregistry - type: string - - name: network - type: string - - - name: "NoMatchingWorkloadsFound" - code: IST0127 - level: Warning - description: "There aren't workloads matching the resource labels" - template: "No matching workloads for this resource with the following labels: %s" - url: "https://istio.io/latest/docs/reference/config/analysis/ist0127/" - args: - - name: labels - type: string - - - name: "NoServerCertificateVerificationDestinationLevel" - code: IST0128 - level: Error - description: "No caCertificates are set in DestinationRule, this results in no verification of presented server certificate." - template: "DestinationRule %s in namespace %s has TLS mode set to %s but no caCertificates are set to validate server identity for host: %s" - url: "https://istio.io/latest/docs/reference/config/analysis/ist0128/" - args: - - name: destinationrule - type: string - - name: namespace - type: string - - name: mode - type: string - - name: host - type: string - - - name: "NoServerCertificateVerificationPortLevel" - code: IST0129 - level: Warning - description: "No caCertificates are set in DestinationRule, this results in no verification of presented server certificate for traffic to a given port." - template: "DestinationRule %s in namespace %s has TLS mode set to %s but no caCertificates are set to validate server identity for host: %s at port %s" - url: "https://istio.io/latest/docs/reference/config/analysis/ist0129/" - args: - - name: destinationrule - type: string - - name: namespace - type: string - - name: mode - type: string - - name: host - type: string - - name: port - type: string - - - name: "VirtualServiceUnreachableRule" - code: IST0130 - level: Warning - description: "A VirtualService rule will never be used because a previous rule uses the same match." - template: "VirtualService rule %v not used (%s)." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0130/" - args: - - name: ruleno - type: string - - name: reason - type: "string" - - - name: "VirtualServiceIneffectiveMatch" - code: IST0131 - level: Info - description: "A VirtualService rule match duplicates a match in a previous rule." - template: "VirtualService rule %v match %v is not used (duplicate/overlapping match in rule %v)." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0131/" - args: - - name: ruleno - type: string - - name: matchno - type: string - - name: dupno - type: string - - - name: "VirtualServiceHostNotFoundInGateway" - code: IST0132 - level: Warning - description: "Host defined in VirtualService not found in Gateway." - template: "one or more host %v defined in VirtualService %s not found in Gateway %s." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0132/" - args: - - name: host - type: "[]string" - - name: virtualservice - type: string - - name: gateway - type: string - - - name: "SchemaWarning" - code: IST0133 - level: Warning - description: "The resource has a schema validation warning." - template: "Schema validation warning: %v" - args: - - name: err - type: error - - - name: "ServiceEntryAddressesRequired" - code: IST0134 - level: Warning - description: "Virtual IP addresses are required for ports serving TCP (or unset) protocol" - template: "ServiceEntry addresses are required for this protocol." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0134/" - - - name: "DeprecatedAnnotation" - code: IST0135 - level: Info - description: "A resource is using a deprecated Istio annotation." - template: "Annotation %q has been deprecated%s and may not work in future Istio versions." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0135/" - args: - - name: annotation - type: string - - name: extra - type: string - - - name: "AlphaAnnotation" - code: IST0136 - level: Info - description: "An Istio annotation may not be suitable for production." - template: "Annotation %q is part of an alpha-phase feature and may be incompletely supported." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0136/" - args: - - name: annotation - type: string - - - name: "DeploymentConflictingPorts" - code: IST0137 - level: Warning - description: "Two services selecting the same workload with the same targetPort MUST refer to the same port." - template: "This deployment %s is associated with multiple services %v using targetPort %q but different ports: %v." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0137/" - args: - - name: deployment - type: string - - name: services - type: "[]string" - - name: targetPort - type: string - - name: ports - type: "[]int32" - -# https://github.com/envoyproxy/envoy/issues/6767 - - name: "GatewayDuplicateCertificate" - code: IST0138 - level: Warning - description: "Duplicate certificate in multiple gateways may cause 404s if clients re-use HTTP2 connections." - template: "Duplicate certificate in multiple gateways %v may cause 404s if clients re-use HTTP2 connections." - args: - - name: gateways - type: "[]string" - - - name: "InvalidWebhook" - code: IST0139 - level: Error - description: "Webhook is invalid or references a control plane service that does not exist." - template: "%v" - args: - - name: error - type: string - - - name: "IngressRouteRulesNotAffected" - code: IST0140 - level: Warning - description: "Route rules have no effect on ingress gateway requests" - template: "Subset in virtual service %s has no effect on ingress gateway %s requests" - args: - - name: virtualservicesubset - type: string - - name: virtualservice - type: string - - - name: "InsufficientPermissions" - code: IST0141 - level: Error - description: "Required permissions to install Istio are missing." - template: "Missing required permission to create resource %v (%v)" - args: - - name: resource - type: string - - name: error - type: string - - - name: "UnsupportedKubernetesVersion" - code: IST0142 - level: Error - description: "The Kubernetes version is not supported" - template: "The Kubernetes Version %q is lower than the minimum version: %v" - args: - - name: version - type: string - - name: minimumVersion - type: string - - - name: "LocalhostListener" - code: IST0143 - level: Error - description: "A port exposed in a Service is bound to a localhost address" - template: "Port %v is exposed in a Service but listens on localhost. It will not be exposed to other pods." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0143/" - args: - - name: port - type: string - - - name: "InvalidApplicationUID" - code: IST0144 - level: Warning - description: "Application pods should not run as user ID (UID) 1337" - template: "User ID (UID) 1337 is reserved for the sidecar proxy." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0144/" - - - name: "ConflictingGateways" - code: IST0145 - level: Error - description: "Gateway should not have the same selector, port and matched hosts of server" - template: "Conflict with gateways %s (workload selector %s, port %s, hosts %v)." - args: - - name: gateway - type: string - - name: selector - type: string - - name: portnumber - type: string - - name: hosts - type: string - - - name: "ImageAutoWithoutInjectionWarning" - code: IST0146 - level: Warning - description: "Deployments with `image: auto` should be targeted for injection." - template: "%s %s contains `image: auto` but does not match any Istio injection webhook selectors." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0146/" - args: - - name: resourceType - type: string - - name: resourceName - type: string - - - name: "ImageAutoWithoutInjectionError" - code: IST0147 - level: Error - description: "Pods with `image: auto` should be targeted for injection." - template: "%s %s contains `image: auto` but does not match any Istio injection webhook selectors." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0147/" - args: - - name: resourceType - type: string - - name: resourceName - type: string - - - name: "NamespaceInjectionEnabledByDefault" - code: IST0148 - level: Info - description: "user namespace should be injectable if Istio is installed with enableNamespacesByDefault enabled and neither injection label is set." - template: "is enabled for Istio injection, as Istio is installed with enableNamespacesByDefault as true." - url: "https://istio.io/latest/docs/reference/config/analysis/ist0148/" - - - name: "JwtClaimBasedRoutingWithoutRequestAuthN" - code: IST0149 - level: Error - description: "Virtual service using JWT claim based routing without request authentication." - template: "The virtual service uses the JWT claim based routing (key: %s) but found no request authentication for the gateway (%s) pod (%s). The request authentication must first be applied for the gateway pods to validate the JWT token and make the claims available for routing." - args: - - name: key - type: string - - name: gateway - type: string - - name: pod - type: string - - - name: "ExternalNameServiceTypeInvalidPortName" - code: IST0150 - level: Warning - description: "Proxy may prevent tcp named ports and unmatched traffic for ports serving TCP protocol from being forwarded correctly for ExternalName services." - template: "Port name for ExternalName service is invalid. Proxy may prevent tcp named ports and unmatched traffic for ports serving TCP protocol from being forwarded correctly" - - - name: "EnvoyFilterUsesRelativeOperation" - code: IST0151 - level: Warning - description: "This envoy filter does not have a priority and has a relative patch operation set which can cause the envoyFilter not to be applied. Using the INSERT_FIRST option or setting the priority may help in ensuring the envoyFilter is applied correctly" - template: "This envoy filter does not have a priority and has a relative patch operation set which can cause the envoyFilter not to be applied. Using the INSERT_FIRST option or setting the priority may help in ensuring the envoyFilter is applied correctly" - \ No newline at end of file diff --git a/pkg/config/analysis/scope/scope.go b/pkg/config/analysis/scope/scope.go deleted file mode 100644 index 23df1c458..000000000 --- a/pkg/config/analysis/scope/scope.go +++ /dev/null @@ -1,28 +0,0 @@ -/* - Copyright Istio Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package scope - -import ( - "istio.io/pkg/log" -) - -var ( - // Analysis is a logging scope used by configuration analysis component. - Analysis = log.RegisterScope("analysis", "Scope for configuration analysis runtime", 0) - // Processing is a logging scope used by configuration processing pipeline. - Processing = log.RegisterScope("processing", "Scope for configuration processing runtime", 0) -) diff --git a/pkg/config/analysis/testing/fixtures/context.go b/pkg/config/analysis/testing/fixtures/context.go deleted file mode 100644 index 0e26d511f..000000000 --- a/pkg/config/analysis/testing/fixtures/context.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package fixtures - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis" - "github.com/apache/dubbo-go-pixiu/pkg/config/analysis/diag" - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" -) - -// Context is a test fixture of analysis.Context -type Context struct { - Resources []*resource.Instance - Reports []diag.Message -} - -var _ analysis.Context = &Context{} - -// Report implements analysis.Context -func (ctx *Context) Report(_ collection.Name, t diag.Message) { - ctx.Reports = append(ctx.Reports, t) -} - -// Find implements analysis.Context -func (ctx *Context) Find(collection.Name, resource.FullName) *resource.Instance { return nil } - -// Exists implements analysis.Context -func (ctx *Context) Exists(collection.Name, resource.FullName) bool { return false } - -// ForEach implements analysis.Context -func (ctx *Context) ForEach(_ collection.Name, fn analysis.IteratorFn) { - for _, r := range ctx.Resources { - fn(r) - } -} - -// Canceled implements analysis.Context -func (ctx *Context) Canceled() bool { return false } diff --git a/pkg/config/api_config.go b/pkg/config/api_config.go new file mode 100644 index 000000000..11bb14277 --- /dev/null +++ b/pkg/config/api_config.go @@ -0,0 +1,464 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 config + +import ( + "regexp" + "strconv" + "strings" + "sync" + "time" +) + +import ( + fc "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" + etcdv3 "github.com/dubbogo/gost/database/kv/etcd/v3" + perrors "github.com/pkg/errors" + "go.etcd.io/etcd/api/v3/mvccpb" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/yaml" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +var ( + apiConfig *fc.APIConfig + client *etcdv3.Client + listener APIConfigResourceListener + lock sync.RWMutex +) + +var ( + BASE_INFO_NAME = "name" + BASE_INFO_DESC = "description" +) + +// APIConfigResourceListener defines api resource and method config listener interface +type APIConfigResourceListener interface { + // ResourceChange handle modify resource event + ResourceChange(new fc.Resource, old fc.Resource) bool // bool is return for interface implement is interesting + // ResourceAdd handle add resource event + ResourceAdd(res fc.Resource) bool + // ResourceDelete handle delete resource event + ResourceDelete(deleted fc.Resource) bool + // MethodChange handle modify method event + MethodChange(res fc.Resource, method fc.Method, old fc.Method) bool + // MethodAdd handle add method below one resource event + MethodAdd(res fc.Resource, method fc.Method) bool + // MethodDelete handle delete method event + MethodDelete(res fc.Resource, method fc.Method) bool +} + +// LoadAPIConfigFromFile load the api config from file +func LoadAPIConfigFromFile(path string) (*fc.APIConfig, error) { + if len(path) == 0 { + return nil, perrors.Errorf("Config file not specified") + } + logger.Infof("Load API configuration file form %s", path) + apiConf := &fc.APIConfig{} + err := yaml.UnmarshalYMLConfig(path, apiConf) + if err != nil { + return nil, perrors.Errorf("unmarshalYmlConfig error %s", perrors.WithStack(err)) + } + apiConfig = apiConf + return apiConf, nil +} + +// LoadAPIConfig load the api config from config center +func LoadAPIConfig(metaConfig *model.APIMetaConfig) (*fc.APIConfig, error) { + tmpClient, err := etcdv3.NewConfigClientWithErr( + etcdv3.WithName(etcdv3.RegistryETCDV3Client), + etcdv3.WithTimeout(10*time.Second), + etcdv3.WithEndpoints(strings.Split(metaConfig.Address, ",")...), + ) + if err != nil { + return nil, perrors.Errorf("Init etcd client fail error %s", err) + } + + client = tmpClient + kList, vList, err := client.GetChildren(metaConfig.APIConfigPath) + if err != nil { + return nil, perrors.Errorf("Get remote config fail error %s", err) + } + if err = initAPIConfigFromKVList(kList, vList); err != nil { + return nil, err + } + // TODO: init other setting which need fetch from remote + go listenResourceAndMethodEvent(metaConfig.APIConfigPath) + // TODO: watch other setting which need fetch from remote + return apiConfig, nil +} + +func initAPIConfigFromKVList(kList, vList []string) error { + var skList, svList, mkList, mvList []string + var baseInfo string + + for i, k := range kList { + v := vList[i] + //handle base info + re := getCheckBaseInfoRegexp() + if m := re.Match([]byte(k)); m { + baseInfo = v + continue + } + + // handle resource + re = getCheckResourceRegexp() + if m := re.Match([]byte(k)); m { + skList = append(skList, k) + svList = append(svList, v) + continue + } + // handle method + re = getExtractMethodRegexp() + if m := re.Match([]byte(k)); m { + mkList = append(mkList, k) + mvList = append(mvList, v) + continue + } + } + + lock.Lock() + defer lock.Unlock() + + tmpApiConf := &fc.APIConfig{} + if err := initBaseInfoFromString(tmpApiConf, baseInfo); err != nil { + logger.Errorf("initBaseInfoFromString error %s", err) + return err + } + if err := initAPIConfigServiceFromKvList(tmpApiConf, skList, svList); err != nil { + logger.Errorf("initAPIConfigServiceFromKvList error %s", err) + return err + } + if err := initAPIConfigMethodFromKvList(tmpApiConf, mkList, mvList); err != nil { + logger.Errorf("initAPIConfigMethodFromKvList error %s", err) + return err + } + + apiConfig = tmpApiConf + return nil +} + +func initBaseInfoFromString(conf *fc.APIConfig, str string) error { + properties := make(map[string]string, 8) + if err := yaml.UnmarshalYML([]byte(str), properties); err != nil { + logger.Errorf("unmarshalYmlConfig error %s", err) + return err + } + if v, ok := properties[BASE_INFO_NAME]; ok { + conf.Name = v + } + if v, ok := properties[BASE_INFO_DESC]; ok { + conf.Description = v + } + return nil +} + +func initAPIConfigMethodFromKvList(config *fc.APIConfig, kList, vList []string) error { + for i := range kList { + v := vList[i] + method := &fc.Method{} + err := yaml.UnmarshalYML([]byte(v), method) + if err != nil { + logger.Errorf("unmarshalYmlConfig error %s", err) + return err + } + + found := false + for r, resource := range config.Resources { + if method.ResourcePath != resource.Path { + continue + } + + for j, old := range resource.Methods { + if old.HTTPVerb == method.HTTPVerb { + // modify one method + resource.Methods[j] = *method + found = true + } + } + if !found { + resource.Methods = append(resource.Methods, *method) + config.Resources[r] = resource + found = true + } + } + + // not found one resource, so need add empty resource first + if !found { + resource := &fc.Resource{} + resource.Methods = append(resource.Methods, *method) + resource.Path = method.ResourcePath + config.Resources = append(config.Resources, *resource) + } + } + return nil +} + +func initAPIConfigServiceFromKvList(config *fc.APIConfig, kList, vList []string) error { + for i := range kList { + v := vList[i] + resource := &fc.Resource{} + err := yaml.UnmarshalYML([]byte(v), resource) + if err != nil { + logger.Errorf("unmarshalYmlConfig error %s", err) + return err + } + + found := false + if config.Resources == nil { + config.Resources = make([]fc.Resource, 0) + } + + for i, old := range config.Resources { + if old.Path != resource.Path { + continue + } + // replace old with new one except method list + resource.Methods = old.Methods + config.Resources[i] = *resource + found = true + } + + if !found { + config.Resources = append(config.Resources, *resource) + } + continue + } + return nil +} + +func listenResourceAndMethodEvent(key string) bool { + for { + wc, err := client.WatchWithPrefix(key) + if err != nil { + logger.Warnf("Watch api config {key:%s} = error{%s}", key, err) + return false + } + + select { + + // client stopped + case <-client.Done(): + logger.Warnf("client stopped") + return false + // client ctx stop + // handle etcd events + case e, ok := <-wc: + if !ok { + logger.Warnf("watch-chan closed") + return false + } + + if e.Err() != nil { + logger.Errorf("watch ERR {err: %s}", e.Err()) + continue + } + for _, event := range e.Events { + switch event.Type { + case mvccpb.PUT: + logger.Infof("get event (key{%s}) = event{EventNodePut}", event.Kv.Key) + handlePutEvent(event.Kv.Key, event.Kv.Value) + case mvccpb.DELETE: + logger.Infof("get event (key{%s}) = event{EventNodeDeleted}", event.Kv.Key) + handleDeleteEvent(event.Kv.Key, event.Kv.Value) + default: + logger.Infof("get event (key{%s}) = event{%d}", event.Kv.Key, event.Type) + } + } + } + } +} + +func handleDeleteEvent(key, val []byte) { + lock.Lock() + defer lock.Unlock() + + keyStr := string(key) + keyStr = strings.TrimSuffix(keyStr, "/") + + re := getCheckResourceRegexp() + if m := re.Match(key); m { + pathArray := strings.Split(keyStr, "/") + if len(pathArray) == 0 { + logger.Errorf("handleDeleteEvent key format error") + return + } + resourceIdStr := pathArray[len(pathArray)-1] + ID, err := strconv.Atoi(resourceIdStr) + if err != nil { + logger.Errorf("handleDeleteEvent ID is not int error %s", err) + return + } + deleteApiConfigResource(ID) + return + } + + re = getExtractMethodRegexp() + if m := re.Match(key); m { + pathArray := strings.Split(keyStr, "/") + if len(pathArray) < 3 { + logger.Errorf("handleDeleteEvent key format error") + return + } + resourceIdStr := pathArray[len(pathArray)-3] + resourceId, err := strconv.Atoi(resourceIdStr) + if err != nil { + logger.Errorf("handleDeleteEvent ID is not int error %s", err) + return + } + + methodIdStr := pathArray[len(pathArray)-1] + methodId, err := strconv.Atoi(methodIdStr) + if err != nil { + logger.Errorf("handleDeleteEvent ID is not int error %s", err) + return + } + deleteApiConfigMethod(resourceId, methodId) + } +} + +func handlePutEvent(key, val []byte) { + lock.Lock() + defer lock.Unlock() + + re := getCheckResourceRegexp() + if m := re.Match(key); m { + res := &fc.Resource{} + err := yaml.UnmarshalYML(val, res) + if err != nil { + logger.Errorf("handlePutEvent UnmarshalYML error %s", err) + return + } + mergeApiConfigResource(*res) + return + } + + re = getExtractMethodRegexp() + if m := re.Match(key); m { + res := &fc.Method{} + err := yaml.UnmarshalYML(val, res) + if err != nil { + logger.Errorf("handlePutEvent UnmarshalYML error %s", err) + return + } + mergeApiConfigMethod(res.ResourcePath, *res) + return + } + + //handle base info + re = getCheckBaseInfoRegexp() + if m := re.Match(key); m { + mergeBaseInfo(val) + return + } +} + +func deleteApiConfigResource(resourceId int) { + for i := 0; i < len(apiConfig.Resources); i++ { + itr := apiConfig.Resources[i] + if itr.ID == resourceId { + apiConfig.Resources = append(apiConfig.Resources[:i], apiConfig.Resources[i+1:]...) + listener.ResourceDelete(itr) + return + } + } +} + +func mergeApiConfigResource(val fc.Resource) { + for i, resource := range apiConfig.Resources { + if val.ID != resource.ID { + continue + } + // modify one resource + val.Methods = resource.Methods + apiConfig.Resources[i] = val + listener.ResourceChange(val, resource) + return + } + // add one resource + apiConfig.Resources = append(apiConfig.Resources, val) + listener.ResourceAdd(val) +} + +func mergeBaseInfo(val []byte) { + _ = initBaseInfoFromString(apiConfig, string(val)) +} + +func deleteApiConfigMethod(resourceId, methodId int) { + for _, resource := range apiConfig.Resources { + if resource.ID != resourceId { + continue + } + + for i := 0; i < len(resource.Methods); i++ { + method := resource.Methods[i] + + if method.ID == methodId { + resource.Methods = append(resource.Methods[:i], resource.Methods[i+1:]...) + listener.MethodDelete(resource, method) + return + } + } + } +} + +func mergeApiConfigMethod(path string, val fc.Method) { + for i, resource := range apiConfig.Resources { + if path != resource.Path { + continue + } + + for j, method := range resource.Methods { + if method.ID == val.ID { + // modify one method + resource.Methods[j] = val + listener.MethodChange(resource, val, method) + apiConfig.Resources[i] = resource + return + } + } + // add one method + resource.Methods = append(resource.Methods, val) + apiConfig.Resources[i] = resource + listener.MethodAdd(resource, val) + } +} + +func getCheckBaseInfoRegexp() *regexp.Regexp { + return regexp.MustCompile(".+/base$") +} + +func getCheckResourceRegexp() *regexp.Regexp { + return regexp.MustCompile(".+/resources/[^/]+/?$") +} + +func getExtractMethodRegexp() *regexp.Regexp { + return regexp.MustCompile(".+/resources/([^/]+)/method/[^/]+/?$") +} + +func getCheckRatelimitRegexp() *regexp.Regexp { + return regexp.MustCompile(".+/filter/ratelimit") +} + +// RegisterConfigListener register APIConfigListener +func RegisterConfigListener(li APIConfigResourceListener) { + listener = li +} diff --git a/pixiu/pkg/config/api_config_test.go b/pkg/config/api_config_test.go similarity index 92% rename from pixiu/pkg/config/api_config_test.go rename to pkg/config/api_config_test.go index 941200486..cd425c8aa 100644 --- a/pixiu/pkg/config/api_config_test.go +++ b/pkg/config/api_config_test.go @@ -27,8 +27,8 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/yaml" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/config" + "github.com/apache/dubbo-go-pixiu/pkg/common/yaml" + "github.com/apache/dubbo-go-pixiu/pkg/config" ) func TestLoadAPIConfigFromFile(t *testing.T) { diff --git a/pixiu/pkg/config/conf_test.yaml b/pkg/config/conf_test.yaml similarity index 100% rename from pixiu/pkg/config/conf_test.yaml rename to pkg/config/conf_test.yaml diff --git a/pixiu/pkg/config/config_load.go b/pkg/config/config_load.go similarity index 97% rename from pixiu/pkg/config/config_load.go rename to pkg/config/config_load.go index 8b7088270..a004a77d6 100644 --- a/pixiu/pkg/config/config_load.go +++ b/pkg/config/config_load.go @@ -34,9 +34,9 @@ import ( import ( "github.com/apache/dubbo-go-pixiu/configcenter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) var ( diff --git a/pixiu/pkg/config/config_load_test.go b/pkg/config/config_load_test.go similarity index 98% rename from pixiu/pkg/config/config_load_test.go rename to pkg/config/config_load_test.go index a8527bcc7..68c2720ec 100644 --- a/pixiu/pkg/config/config_load_test.go +++ b/pkg/config/config_load_test.go @@ -30,7 +30,7 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) var b model.Bootstrap diff --git a/pkg/config/constants/constants.go b/pkg/config/constants/constants.go deleted file mode 100644 index 80879ad13..000000000 --- a/pkg/config/constants/constants.go +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package constants - -const ( - // UnspecifiedIP constant for empty IP address - UnspecifiedIP = "0.0.0.0" - - // AuthCertsPath is the path location for mTLS certificates - AuthCertsPath = "/etc/certs/" - - // CertChainFilename is mTLS chain file - CertChainFilename = "cert-chain.pem" - - // DefaultServerCertChain is the default path to the mTLS chain file - DefaultCertChain = AuthCertsPath + CertChainFilename - - // KeyFilename is mTLS private key - KeyFilename = "key.pem" - - // DefaultServerKey is the default path to the mTLS private key file - DefaultKey = AuthCertsPath + KeyFilename - - // RootCertFilename is mTLS root cert - RootCertFilename = "root-cert.pem" - - // DefaultRootCert is the default path to the mTLS root cert file - DefaultRootCert = AuthCertsPath + RootCertFilename - - // ConfigPathDir config directory for storing envoy json config files. - ConfigPathDir = "./etc/istio/proxy" - - // IstioDataDir is the directory to store binary data such as envoy core dump, profile, and downloaded Wasm modules. - IstioDataDir = "/var/lib/istio/data" - - // BinaryPathFilename envoy binary location - BinaryPathFilename = "/usr/local/bin/envoy" - - // ServiceClusterName service cluster name used in xDS calls - ServiceClusterName = "istio-proxy" - - // IstioIngressGatewayName is the internal gateway name assigned to ingress - IstioIngressGatewayName = "istio-autogenerated-k8s-ingress" - - KubernetesGatewayName = "istio-autogenerated-k8s-gateway" - - // IstioIngressNamespace is the namespace where Istio ingress controller is deployed - IstioIngressNamespace = "dubbo-system" - - // DefaultKubernetesDomain the default service domain suffix for Kubernetes, if not overridden in config. - // TODO(nmittler): Rename this to DefaultClusterLocalDomain. - // TODO(nmittler): Search/replace explicit usages of the string with this constant. - DefaultKubernetesDomain = "cluster.local" - - // DefaultClusterSetLocalDomain is the default domain suffix for Kubernetes Multi-Cluster Services (MCS) - // used for load balancing requests against endpoints across the ClusterSet (i.e. mesh). - DefaultClusterSetLocalDomain = "clusterset.local" - - // IstioLabel indicates that a workload is part of a named Istio system component. - IstioLabel = "istio" - - // IstioIngressLabelValue is value for IstioLabel that identifies an ingress workload. - // TODO we should derive this from IngressClass - IstioIngressLabelValue = "ingressgateway" - - // IstioSystemNamespace is the namespace where Istio's components are deployed - IstioSystemNamespace = "dubbo-system" - - // DefaultAuthenticationPolicyName is the name of the cluster-scoped authentication policy. Only - // policy with this name in the cluster-scoped will be considered. - DefaultAuthenticationPolicyName = "default" - - // IstioMeshGateway is the built in gateway for all sidecars - IstioMeshGateway = "mesh" - - // The data name in the ConfigMap of each namespace storing the root cert of non-Kube CA. - CACertNamespaceConfigMapDataName = "root-cert.pem" - - // PodInfoLabelsPath is the filepath that pod labels will be stored - // This is typically set by the downward API - PodInfoLabelsPath = "./etc/istio/pod/labels" - - // PodInfoAnnotationsPath is the filepath that pod annotations will be stored - // This is typically set by the downward API - PodInfoAnnotationsPath = "./etc/istio/pod/annotations" - - // DefaultServiceAccountName is the default service account to use for remote cluster access. - DefaultServiceAccountName = "istio-reader-service-account" - - // DefaultConfigServiceAccountName is the default service account to use for external Istiod config cluster access. - DefaultConfigServiceAccountName = "istiod" - - // KubeSystemNamespace is the system namespace where we place kubernetes system components. - KubeSystemNamespace string = "kube-system" - - // KubePublicNamespace is the namespace where we place kubernetes public info (ConfigMaps). - KubePublicNamespace string = "kube-public" - - // KubeNodeLeaseNamespace is the namespace for the lease objects associated with each kubernetes node. - KubeNodeLeaseNamespace string = "kube-node-lease" - - // LocalPathStorageNamespace is the namespace for dynamically provisioning persistent local storage with - // Kubernetes. Typically used with the Kind cluster: https://github.com/rancher/local-path-provisioner - LocalPathStorageNamespace string = "local-path-storage" - - TestVMLabel = "istio.io/test-vm" - - TestVMVersionLabel = "istio.io/test-vm-version" - - // Label to skip config comparison. - AlwaysPushLabel = "internal.istio.io/always-push" - - // InternalParentName declares the original resource of an internally-generate config. This is used by ingress and the gateway-api. - InternalParentName = "internal.istio.io/parent" - InternalRouteSemantics = "internal.istio.io/route-semantics" - RouteSemanticsIngress = "ingress" - RouteSemanticsGateway = "gateway" - - // TrustworthyJWTPath is the default 3P token to authenticate with third party services - TrustworthyJWTPath = "./var/run/secrets/tokens/istio-token" - - // CertProviderIstiod uses istiod self signed DNS certificates for the control plane - CertProviderIstiod = "istiod" - // CertProviderKubernetes uses the Kubernetes CSR API to generate a DNS certificate for the control plane - CertProviderKubernetes = "kubernetes" - // CertProviderKubernetesSignerPrefix uses the Kubernetes CSR API and the specified signer to generate a DNS certificate for the control plane - CertProviderKubernetesSignerPrefix = "k8s.io/" - // CertProviderCustom uses the custom root certificate mounted in a well known location for the control plane - CertProviderCustom = "custom" - // CertProviderNone does not create any certificates for the control plane. It is assumed that some external - // load balancer, such as an Istio Gateway, is terminating the TLS. - CertProviderNone = "none" -) diff --git a/pkg/config/conversion.go b/pkg/config/conversion.go deleted file mode 100644 index fecc19dd6..000000000 --- a/pkg/config/conversion.go +++ /dev/null @@ -1,45 +0,0 @@ -/* - Copyright Istio Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package config - -import ( - "google.golang.org/protobuf/types/known/timestamppb" - mcp "istio.io/api/mcp/v1alpha1" -) - -// Convert from model.Config, which has no associated proto, to MCP Resource proto. -// TODO: define a proto matching Config - to avoid useless superficial conversions. -func PilotConfigToResource(c *Config) (*mcp.Resource, error) { - r := &mcp.Resource{} - - // MCP, K8S and Istio configs use gogo configs - // On the wire it's the same as golang proto. - a, err := ToProto(c.Spec) - if err != nil { - return nil, err - } - r.Body = a - r.Metadata = &mcp.Metadata{ - Name: c.Namespace + "/" + c.Name, - CreateTime: timestamppb.New(c.CreationTimestamp), - Version: c.ResourceVersion, - Labels: c.Labels, - Annotations: c.Annotations, - } - - return r, nil -} diff --git a/pkg/config/crd/validator.go b/pkg/config/crd/validator.go deleted file mode 100644 index d4f1a1e99..000000000 --- a/pkg/config/crd/validator.go +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package crd - -import ( - "fmt" - "io" - "os" - "path/filepath" -) - -import ( - "github.com/hashicorp/go-multierror" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema" - structuraldefaulting "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting" - "k8s.io/apiextensions-apiserver/pkg/apiserver/validation" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - kubeyaml "k8s.io/apimachinery/pkg/util/yaml" - "k8s.io/kube-openapi/pkg/validation/validate" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/yml" -) - -// Validator returns a new validator for custom resources -// Warning: this is meant for usage in tests only -type Validator struct { - byGvk map[schema.GroupVersionKind]*validate.SchemaValidator - structural map[schema.GroupVersionKind]*structuralschema.Structural - // If enabled, resources without a validator will be ignored. Otherwise, they will fail. - SkipMissing bool -} - -func (v *Validator) ValidateCustomResourceYAML(data string) error { - var errs *multierror.Error - for _, item := range yml.SplitString(data) { - obj := &unstructured.Unstructured{} - if err := yaml.Unmarshal([]byte(item), obj); err != nil { - return err - } - errs = multierror.Append(errs, v.ValidateCustomResource(obj)) - } - return errs.ErrorOrNil() -} - -func (v *Validator) ValidateCustomResource(o runtime.Object) error { - content, err := runtime.DefaultUnstructuredConverter.ToUnstructured(o) - if err != nil { - return err - } - - un := &unstructured.Unstructured{Object: content} - vd, f := v.byGvk[un.GroupVersionKind()] - if !f { - if v.SkipMissing { - return nil - } - return fmt.Errorf("failed to validate type %v: no validator found", un.GroupVersionKind()) - } - // Fill in defaults - structuraldefaulting.Default(un.Object, v.structural[un.GroupVersionKind()]) - if err := validation.ValidateCustomResource(nil, un.Object, vd).ToAggregate(); err != nil { - return fmt.Errorf("%v/%v/%v: %v", un.GroupVersionKind().Kind, un.GetName(), un.GetNamespace(), err) - } - return nil -} - -func NewValidatorFromFiles(files ...string) (*Validator, error) { - crds := []apiextensions.CustomResourceDefinition{} - closers := make([]io.Closer, 0, len(files)) - defer func() { - for _, closer := range closers { - closer.Close() - } - }() - for _, file := range files { - data, err := os.Open(file) - if err != nil { - return nil, fmt.Errorf("failed to read input yaml file: %v", err) - } - closers = append(closers, data) - - yamlDecoder := kubeyaml.NewYAMLOrJSONDecoder(data, 512*1024) - for { - un := &unstructured.Unstructured{} - err = yamlDecoder.Decode(&un) - if err == io.EOF { - break - } - if err != nil { - return nil, err - } - crd := apiextensions.CustomResourceDefinition{} - switch un.GroupVersionKind() { - case schema.GroupVersionKind{ - Group: "apiextensions.k8s.io", - Version: "v1", - Kind: "CustomResourceDefinition", - }: - crdv1 := apiextensionsv1.CustomResourceDefinition{} - if err := runtime.DefaultUnstructuredConverter. - FromUnstructured(un.UnstructuredContent(), &crdv1); err != nil { - return nil, err - } - if err := apiextensionsv1.Convert_v1_CustomResourceDefinition_To_apiextensions_CustomResourceDefinition(&crdv1, &crd, nil); err != nil { - return nil, err - } - case schema.GroupVersionKind{ - Group: "apiextensions.k8s.io", - Version: "v1beta1", - Kind: "CustomResourceDefinition", - }: - crdv1beta1 := apiextensionsv1beta1.CustomResourceDefinition{} - if err := runtime.DefaultUnstructuredConverter. - FromUnstructured(un.UnstructuredContent(), &crdv1beta1); err != nil { - return nil, err - } - if err := apiextensionsv1beta1.Convert_v1beta1_CustomResourceDefinition_To_apiextensions_CustomResourceDefinition(&crdv1beta1, &crd, nil); err != nil { - return nil, err - } - default: - return nil, fmt.Errorf("unknown CRD type: %v", un.GroupVersionKind()) - } - crds = append(crds, crd) - } - } - return NewValidatorFromCRDs(crds...) -} - -func NewValidatorFromCRDs(crds ...apiextensions.CustomResourceDefinition) (*Validator, error) { - v := &Validator{ - byGvk: map[schema.GroupVersionKind]*validate.SchemaValidator{}, - structural: map[schema.GroupVersionKind]*structuralschema.Structural{}, - } - for _, crd := range crds { - versions := crd.Spec.Versions - if len(versions) == 0 { - versions = []apiextensions.CustomResourceDefinitionVersion{{Name: crd.Spec.Version}} // nolint: staticcheck - } - for _, ver := range versions { - gvk := schema.GroupVersionKind{ - Group: crd.Spec.Group, - Version: ver.Name, - Kind: crd.Spec.Names.Kind, - } - crdSchema := ver.Schema - if crdSchema == nil { - crdSchema = crd.Spec.Validation - } - if crdSchema == nil { - return nil, fmt.Errorf("crd did not have validation defined") - } - - schemaValidator, _, err := validation.NewSchemaValidator(crdSchema) - if err != nil { - return nil, err - } - structural, err := structuralschema.NewStructural(crdSchema.OpenAPIV3Schema) - if err != nil { - return nil, err - } - - v.byGvk[gvk] = schemaValidator - v.structural[gvk] = structural - } - } - - return v, nil -} - -func NewIstioValidator(t test.Failer) *Validator { - v, err := NewValidatorFromFiles( - filepath.Join(env.IstioSrc, "tests/integration/pilot/testdata/gateway-api-crd.yaml"), - filepath.Join(env.IstioSrc, "manifests/charts/base/crds/crd-all.gen.yaml")) - if err != nil { - t.Fatal(err) - } - return v -} diff --git a/pkg/config/crd/validator_test.go b/pkg/config/crd/validator_test.go deleted file mode 100644 index c2d80f292..000000000 --- a/pkg/config/crd/validator_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package crd - -import ( - "testing" -) - -func TestValidator(t *testing.T) { - validator := NewIstioValidator(t) - t.Run("valid", func(t *testing.T) { - if err := validator.ValidateCustomResourceYAML(` -apiVersion: security.istio.io/v1beta1 -kind: PeerAuthentication -metadata: - name: default -spec: - mtls: - mode: STRICT -`); err != nil { - t.Fatal(err) - } - }) - t.Run("invalid", func(t *testing.T) { - if err := validator.ValidateCustomResourceYAML(` -apiVersion: security.istio.io/v1beta1 -kind: PeerAuthentication -metadata: - name: default -spec: - mtls: - mode: BAD -`); err == nil { - t.Fatal("expected error but got none") - } - }) -} diff --git a/pkg/config/doc.go b/pkg/config/doc.go deleted file mode 100644 index 915579c2c..000000000 --- a/pkg/config/doc.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package config is a common, top-level folder for aggregating Istio-wide config related libraries and utilities. -// More details can be found here: https://docs.google.com/document/d/1atY5vDHy5sXJP7qIaFQS3ixQZvOxUciPgULH5qTPX_8/ -package config diff --git a/pkg/config/gateway/gateway.go b/pkg/config/gateway/gateway.go deleted file mode 100644 index 537ca8ef3..000000000 --- a/pkg/config/gateway/gateway.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" -) - -// IsTLSServer returns true if this server is non HTTP, with some TLS settings for termination/passthrough -func IsTLSServer(server *v1alpha3.Server) bool { - if server.Tls != nil && !protocol.Parse(server.Port.Protocol).IsHTTP() { - return true - } - return false -} - -// IsHTTPServer returns true if this server is using HTTP or HTTPS with termination -func IsHTTPServer(server *v1alpha3.Server) bool { - p := protocol.Parse(server.Port.Protocol) - if p.IsHTTP() { - return true - } - - if p == protocol.HTTPS && server.Tls != nil && !IsPassThroughServer(server) { - return true - } - - return false -} - -// IsEligibleForHTTP3Upgrade returns true if we can create an HTTP/3 server -// listening of QUIC for the given server. It must be a TLS non-passthrough -// as TLS is mandatory for QUIC -func IsEligibleForHTTP3Upgrade(server *v1alpha3.Server) bool { - if !features.EnableQUICListeners { - return false - } - p := protocol.Parse(server.Port.Protocol) - return p == protocol.HTTPS && server.Tls != nil && !IsPassThroughServer(server) -} - -// IsPassThroughServer returns true if this server does TLS passthrough (auto or manual) -func IsPassThroughServer(server *v1alpha3.Server) bool { - if server.Tls == nil { - return false - } - - if server.Tls.Mode == v1alpha3.ServerTLSSettings_PASSTHROUGH || - server.Tls.Mode == v1alpha3.ServerTLSSettings_AUTO_PASSTHROUGH { - return true - } - - return false -} diff --git a/pkg/config/gateway/gateway_test.go b/pkg/config/gateway/gateway_test.go deleted file mode 100644 index aaa913b3d..000000000 --- a/pkg/config/gateway/gateway_test.go +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gateway - -import ( - "testing" -) - -import ( - "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -func TestIsTLSServer(t *testing.T) { - cases := []struct { - name string - server *v1alpha3.Server - expected bool - }{ - { - name: "tls non nil and HTTP as transport protocol", - server: &v1alpha3.Server{ - Port: &v1alpha3.Port{ - Number: 80, - Protocol: string(protocol.HTTP), - Name: "http", - }, - Tls: &v1alpha3.ServerTLSSettings{HttpsRedirect: true}, - }, - expected: false, - }, - { - name: "tls non nil and TCP as transport protocol", - server: &v1alpha3.Server{ - Port: &v1alpha3.Port{ - Number: 80, - Protocol: string(protocol.TCP), - Name: "tcp", - }, - Tls: &v1alpha3.ServerTLSSettings{HttpsRedirect: true}, - }, - expected: true, - }, - { - name: "tls nil and HTTP as transport protocol", - server: &v1alpha3.Server{ - Port: &v1alpha3.Port{ - Number: 80, - Protocol: string(protocol.HTTP), - Name: "http", - }, - }, - expected: false, - }, - { - name: "tls nil and TCP as transport protocol", - server: &v1alpha3.Server{ - Port: &v1alpha3.Port{ - Number: 80, - Protocol: string(protocol.TCP), - Name: "tcp", - }, - }, - expected: false, - }, - } - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - actual := IsTLSServer(tc.server) - if actual != tc.expected { - t.Errorf("IsTLSServer(%s) => %t, want %t", - tc.server, actual, tc.expected) - } - }) - } -} - -func TestIsHTTPServer(t *testing.T) { - cases := []struct { - name string - server *v1alpha3.Server - expected bool - }{ - { - name: "HTTP as transport protocol", - server: &v1alpha3.Server{ - Port: &v1alpha3.Port{ - Number: 80, - Protocol: string(protocol.HTTP), - Name: "http", - }, - }, - expected: true, - }, - { - name: "HTTPS traffic with passthrough ServerTLS mode", - server: &v1alpha3.Server{ - Port: &v1alpha3.Port{ - Number: 80, - Protocol: string(protocol.HTTPS), - Name: "https", - }, - Tls: &v1alpha3.ServerTLSSettings{Mode: v1alpha3.ServerTLSSettings_PASSTHROUGH}, - }, - expected: false, - }, - { - name: "HTTP traffic with passthrough ServerTLS mode", - server: &v1alpha3.Server{ - Port: &v1alpha3.Port{ - Number: 80, - Protocol: string(protocol.HTTP), - Name: "http", - }, - Tls: &v1alpha3.ServerTLSSettings{Mode: v1alpha3.ServerTLSSettings_PASSTHROUGH}, - }, - expected: true, - }, - { - name: "HTTPS traffic with istio mutual ServerTLS mode", - server: &v1alpha3.Server{ - Port: &v1alpha3.Port{ - Number: 80, - Protocol: string(protocol.HTTPS), - Name: "https", - }, - Tls: &v1alpha3.ServerTLSSettings{Mode: v1alpha3.ServerTLSSettings_ISTIO_MUTUAL}, - }, - expected: true, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - actual := IsHTTPServer(tc.server) - if actual != tc.expected { - t.Errorf("IsHTTPServer(%s) => %t, want %t", - tc.server, actual, tc.expected) - } - }) - } -} - -func TestIsEligibleForHTTP3Upgrade(t *testing.T) { - cases := []struct { - name string - server *v1alpha3.Server - enableQUICListeners bool - expected bool - }{ - { - name: "EnableQUICListeners set to false", - server: &v1alpha3.Server{ - Port: &v1alpha3.Port{ - Number: 80, - Protocol: string(protocol.HTTP), - Name: "http", - }, - }, - expected: false, - enableQUICListeners: false, - }, - { - name: "HTTP as transport protocol and EnableQUICListeners set to true", - server: &v1alpha3.Server{ - Port: &v1alpha3.Port{ - Number: 80, - Protocol: string(protocol.HTTP), - Name: "http", - }, - }, - expected: false, - enableQUICListeners: true, - }, - { - name: "HTTPS traffic with passthrough ServerTLS mode and EnableQUICListeners set to true", - server: &v1alpha3.Server{ - Port: &v1alpha3.Port{ - Number: 80, - Protocol: string(protocol.HTTPS), - Name: "https", - }, - Tls: &v1alpha3.ServerTLSSettings{Mode: v1alpha3.ServerTLSSettings_PASSTHROUGH}, - }, - enableQUICListeners: true, - expected: false, - }, - { - name: "HTTPS traffic with istio mutual ServerTLS mode and EnableQUICListeners set to true", - server: &v1alpha3.Server{ - Port: &v1alpha3.Port{ - Number: 80, - Protocol: string(protocol.HTTPS), - Name: "https", - }, - Tls: &v1alpha3.ServerTLSSettings{Mode: v1alpha3.ServerTLSSettings_ISTIO_MUTUAL}, - }, - enableQUICListeners: true, - expected: true, - }, - } - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - test.SetBoolForTest(t, &features.EnableQUICListeners, tc.enableQUICListeners) - actual := IsEligibleForHTTP3Upgrade(tc.server) - if actual != tc.expected { - t.Errorf("IsEligibleForHTTP3Upgrade(%s) => %t, want %t", - tc.server, actual, tc.expected) - } - }) - } -} - -func TestIsPassThroughServer(t *testing.T) { - cases := []struct { - name string - server *v1alpha3.Server - expected bool - }{ - { - name: "nil server TlS", - server: &v1alpha3.Server{ - Port: &v1alpha3.Port{ - Number: 80, - Protocol: string(protocol.HTTP), - Name: "http", - }, - }, - expected: false, - }, - { - name: "passthrough ServerTLS mode", - server: &v1alpha3.Server{ - Port: &v1alpha3.Port{ - Number: 80, - Protocol: string(protocol.HTTPS), - Name: "https", - }, - Tls: &v1alpha3.ServerTLSSettings{Mode: v1alpha3.ServerTLSSettings_PASSTHROUGH}, - }, - expected: true, - }, - { - name: "auto passthrough ServerTLS mode", - server: &v1alpha3.Server{ - Port: &v1alpha3.Port{ - Number: 80, - Protocol: string(protocol.HTTP), - Name: "http", - }, - Tls: &v1alpha3.ServerTLSSettings{Mode: v1alpha3.ServerTLSSettings_AUTO_PASSTHROUGH}, - }, - expected: true, - }, - { - name: "istio mutual ServerTLS mode", - server: &v1alpha3.Server{ - Port: &v1alpha3.Port{ - Number: 80, - Protocol: string(protocol.HTTPS), - Name: "https", - }, - Tls: &v1alpha3.ServerTLSSettings{Mode: v1alpha3.ServerTLSSettings_ISTIO_MUTUAL}, - }, - expected: false, - }, - } - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - actual := IsPassThroughServer(tc.server) - if actual != tc.expected { - t.Errorf("IsPassThroughServer(%s) => %t, want %t", - tc.server, actual, tc.expected) - } - }) - } -} diff --git a/pkg/config/host/name.go b/pkg/config/host/name.go deleted file mode 100644 index ac013eaae..000000000 --- a/pkg/config/host/name.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package host - -import ( - "strings" -) - -// Name describes a (possibly wildcarded) hostname -type Name string - -// Matches returns true if this hostname overlaps with the other hostname. Names overlap if: -// - they're fully resolved (i.e. not wildcarded) and match exactly (i.e. an exact string match) -// - one or both are wildcarded (e.g. "*.foo.com"), in which case we use wildcard resolution rules -// to determine if h is covered by o or o is covered by h. -// e.g.: -// -// Name("foo.com").Matches("foo.com") = true -// Name("foo.com").Matches("bar.com") = false -// Name("*.com").Matches("foo.com") = true -// Name("bar.com").Matches("*.com") = true -// Name("*.foo.com").Matches("foo.com") = false -// Name("*").Matches("foo.com") = true -// Name("*").Matches("*.com") = true -func (n Name) Matches(o Name) bool { - hWildcard := n.IsWildCarded() - oWildcard := o.IsWildCarded() - - if hWildcard { - if oWildcard { - // both n and o are wildcards - if len(n) < len(o) { - return strings.HasSuffix(string(o[1:]), string(n[1:])) - } - return strings.HasSuffix(string(n[1:]), string(o[1:])) - } - // only n is wildcard - return strings.HasSuffix(string(o), string(n[1:])) - } - - if oWildcard { - // only o is wildcard - return strings.HasSuffix(string(n), string(o[1:])) - } - - // both are non-wildcards, so do normal string comparison - return n == o -} - -// SubsetOf returns true if this hostname is a valid subset of the other hostname. The semantics are -// the same as "Matches", but only in one direction (i.e., h is covered by o). -func (n Name) SubsetOf(o Name) bool { - hWildcard := n.IsWildCarded() - oWildcard := o.IsWildCarded() - - if hWildcard { - if oWildcard { - // both n and o are wildcards - if len(n) < len(o) { - return false - } - return strings.HasSuffix(string(n[1:]), string(o[1:])) - } - // only n is wildcard - return false - } - - if oWildcard { - // only o is wildcard - return strings.HasSuffix(string(n), string(o[1:])) - } - - // both are non-wildcards, so do normal string comparison - return n == o -} - -func (n Name) IsWildCarded() bool { - return len(n) > 0 && n[0] == '*' -} - -func (n Name) String() string { - return string(n) -} diff --git a/pkg/config/host/name_test.go b/pkg/config/host/name_test.go deleted file mode 100644 index 4bc8e7de3..000000000 --- a/pkg/config/host/name_test.go +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package host_test - -import ( - "fmt" - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/host" -) - -func TestNameMatches(t *testing.T) { - tests := []struct { - name string - a, b host.Name - out bool - }{ - {"empty", "", "", true}, - {"first empty", "", "foo.com", false}, - {"second empty", "foo.com", "", false}, - - { - "non-wildcard domain", - "foo.com", "foo.com", true, - }, - { - "non-wildcard domain", - "bar.com", "foo.com", false, - }, - { - "non-wildcard domain - order doesn't matter", - "foo.com", "bar.com", false, - }, - - { - "domain does not match subdomain", - "bar.foo.com", "foo.com", false, - }, - { - "domain does not match subdomain - order doesn't matter", - "foo.com", "bar.foo.com", false, - }, - - { - "wildcard matches subdomains", - "*.com", "foo.com", true, - }, - { - "wildcard matches subdomains", - "*.com", "bar.com", true, - }, - { - "wildcard matches subdomains", - "*.foo.com", "bar.foo.com", true, - }, - - {"wildcard matches anything", "*", "foo.com", true}, - {"wildcard matches anything", "*", "*.com", true}, - {"wildcard matches anything", "*", "com", true}, - {"wildcard matches anything", "*", "*", true}, - {"wildcard matches anything", "*", "", true}, - - {"wildcarded domain matches wildcarded subdomain", "*.com", "*.foo.com", true}, - {"wildcarded sub-domain does not match domain", "foo.com", "*.foo.com", false}, - {"wildcarded sub-domain does not match domain - order doesn't matter", "*.foo.com", "foo.com", false}, - - {"long wildcard does not match short host", "*.foo.bar.baz", "baz", false}, - {"long wildcard does not match short host - order doesn't matter", "baz", "*.foo.bar.baz", false}, - {"long wildcard matches short wildcard", "*.foo.bar.baz", "*.baz", true}, - {"long name matches short wildcard", "foo.bar.baz", "*.baz", true}, - } - - for idx, tt := range tests { - t.Run(fmt.Sprintf("[%d] %s", idx, tt.name), func(t *testing.T) { - if tt.out != tt.a.Matches(tt.b) { - t.Fatalf("%q.Matches(%q) = %t wanted %t", tt.a, tt.b, !tt.out, tt.out) - } - }) - } -} - -func TestNameSubsetOf(t *testing.T) { - tests := []struct { - name string - a, b host.Name - out bool - }{ - {"empty", "", "", true}, - {"first empty", "", "foo.com", false}, - {"second empty", "foo.com", "", false}, - - { - "non-wildcard domain", - "foo.com", "foo.com", true, - }, - { - "non-wildcard domain", - "bar.com", "foo.com", false, - }, - { - "non-wildcard domain - order doesn't matter", - "foo.com", "bar.com", false, - }, - - { - "domain does not match subdomain", - "bar.foo.com", "foo.com", false, - }, - { - "domain does not match subdomain - order doesn't matter", - "foo.com", "bar.foo.com", false, - }, - - { - "wildcard matches subdomains", - "foo.com", "*.com", true, - }, - { - "wildcard matches subdomains", - "bar.com", "*.com", true, - }, - { - "wildcard matches subdomains", - "bar.foo.com", "*.foo.com", true, - }, - - {"wildcard matches anything", "foo.com", "*", true}, - {"wildcard matches anything", "*.com", "*", true}, - {"wildcard matches anything", "com", "*", true}, - {"wildcard matches anything", "*", "*", true}, - {"wildcard matches anything", "", "*", true}, - - {"wildcarded domain matches wildcarded subdomain", "*.foo.com", "*.com", true}, - {"wildcarded sub-domain does not match domain", "*.foo.com", "foo.com", false}, - - {"long wildcard does not match short host", "*.foo.bar.baz", "baz", false}, - {"long name matches short wildcard", "foo.bar.baz", "*.baz", true}, - } - - for idx, tt := range tests { - t.Run(fmt.Sprintf("[%d] %s", idx, tt.name), func(t *testing.T) { - if tt.out != tt.a.SubsetOf(tt.b) { - t.Fatalf("%q.SubsetOf(%q) = %t wanted %t", tt.a, tt.b, !tt.out, tt.out) - } - }) - } -} - -func BenchmarkNameMatch(b *testing.B) { - tests := []struct { - a, z host.Name - matches bool - }{ - {"foo.com", "foo.com", true}, - {"*.com", "foo.com", true}, - {"*.foo.com", "bar.foo.com", true}, - {"*", "foo.com", true}, - {"*", "*.com", true}, - {"*", "", true}, - {"*.com", "*.foo.com", true}, - {"foo.com", "*.foo.com", false}, - {"*.foo.bar.baz", "baz", false}, - } - for n := 0; n < b.N; n++ { - for _, test := range tests { - doesMatch := test.a.Matches(test.z) - if doesMatch != test.matches { - b.Fatalf("does not match") - } - } - } -} diff --git a/pkg/config/host/names.go b/pkg/config/host/names.go deleted file mode 100644 index 64e29757d..000000000 --- a/pkg/config/host/names.go +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package host - -import ( - "sort" - "strings" -) - -// Names is a collection of Name; it exists so it's easy to sort hostnames consistently across Istio. -// In a few locations we care about the order hostnames appear in Envoy config: primarily HTTP routes, but also in -// gateways, and for SNI. In those locations, we sort hostnames longest to shortest with wildcards last. -type Names []Name - -// prove we implement the interface at compile time -var _ sort.Interface = Names{} - -func (h Names) Len() int { - return len(h) -} - -func (h Names) Less(i, j int) bool { - a, b := h[i], h[j] - if len(a) == 0 && len(b) == 0 { - return true // doesn't matter, they're both the empty string - } - - // we sort longest to shortest, alphabetically, with wildcards last - ai, aj := string(a[0]) == "*", string(b[0]) == "*" - if ai && !aj { - // h[i] is a wildcard, but h[j] isn't; therefore h[j] < h[i] - return false - } else if !ai && aj { - // h[j] is a wildcard, but h[i] isn't; therefore h[i] < h[j] - return true - } - - // they're either both wildcards, or both not; in either case we sort them longest to shortest, alphabetically - if len(a) == len(b) { - return a < b - } - - return len(a) > len(b) -} - -func (h Names) Swap(i, j int) { - h[i], h[j] = h[j], h[i] -} - -func (h Names) Contains(host Name) bool { - for _, hHost := range h { - if hHost == host { - return true - } - } - return false -} - -// Intersection returns the subset of host names that are covered by both h and other. -// e.g.: -// -// Names(["foo.com","bar.com"]).Intersection(Names(["*.com"])) = Names(["foo.com","bar.com"]) -// Names(["foo.com","*.net"]).Intersection(Names(["*.com","bar.net"])) = Names(["foo.com","bar.net"]) -// Names(["foo.com","*.net"]).Intersection(Names(["*.bar.net"])) = Names(["*.bar.net"]) -// Names(["foo.com"]).Intersection(Names(["bar.com"])) = Names([]) -// Names([]).Intersection(Names(["bar.com"]) = Names([]) -func (h Names) Intersection(other Names) Names { - result := make(Names, 0, len(h)) - for _, hHost := range h { - for _, oHost := range other { - if hHost.SubsetOf(oHost) { - if !result.Contains(hHost) { - result = append(result, hHost) - } - } else if oHost.SubsetOf(hHost) { - if !result.Contains(oHost) { - result = append(result, oHost) - } - } - } - } - return result -} - -// NewNames converts a slice of host name strings to type Names. -func NewNames(hosts []string) Names { - result := make(Names, 0, len(hosts)) - for _, host := range hosts { - result = append(result, Name(host)) - } - return result -} - -// NamesForNamespace returns the subset of hosts that are in the specified namespace. -// The list of hosts contains host names optionally qualified with namespace/ or */. -// If not qualified or qualified with *, the host name is considered to be in every namespace. -// e.g.: -// NamesForNamespace(["ns1/foo.com","ns2/bar.com"], "ns1") = Names(["foo.com"]) -// NamesForNamespace(["ns1/foo.com","ns2/bar.com"], "ns3") = Names([]) -// NamesForNamespace(["ns1/foo.com","*/bar.com"], "ns1") = Names(["foo.com","bar.com"]) -// NamesForNamespace(["ns1/foo.com","*/bar.com"], "ns3") = Names(["bar.com"]) -// NamesForNamespace(["foo.com","ns2/bar.com"], "ns2") = Names(["foo.com","bar.com"]) -// NamesForNamespace(["foo.com","ns2/bar.com"], "ns3") = Names(["foo.com"]) -func NamesForNamespace(hosts []string, namespace string) Names { - result := make(Names, 0, len(hosts)) - for _, host := range hosts { - if strings.Contains(host, "/") { - parts := strings.Split(host, "/") - if parts[0] != namespace && parts[0] != "*" { - continue - } - // strip the namespace - host = parts[1] - } - result = append(result, Name(host)) - } - return result -} diff --git a/pkg/config/host/names_test.go b/pkg/config/host/names_test.go deleted file mode 100644 index 0e933853c..000000000 --- a/pkg/config/host/names_test.go +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package host_test - -import ( - "fmt" - "reflect" - "sort" - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/host" -) - -func TestNamesIntersection(t *testing.T) { - tests := []struct { - a, b, intersection host.Names - }{ - { - host.Names{"foo,com"}, - host.Names{"bar.com"}, - host.Names{}, - }, - { - host.Names{"foo.com", "bar.com"}, - host.Names{"bar.com"}, - host.Names{"bar.com"}, - }, - { - host.Names{"foo.com", "bar.com"}, - host.Names{"*.com"}, - host.Names{"foo.com", "bar.com"}, - }, - { - host.Names{"*.com"}, - host.Names{"foo.com", "bar.com"}, - host.Names{"foo.com", "bar.com"}, - }, - { - host.Names{"foo.com", "*.net"}, - host.Names{"*.com", "bar.net"}, - host.Names{"foo.com", "bar.net"}, - }, - { - host.Names{"foo.com", "*.net"}, - host.Names{"*.bar.net"}, - host.Names{"*.bar.net"}, - }, - { - host.Names{"foo.com", "bar.net"}, - host.Names{"*"}, - host.Names{"foo.com", "bar.net"}, - }, - { - host.Names{"foo.com"}, - host.Names{}, - host.Names{}, - }, - { - host.Names{}, - host.Names{"bar.com"}, - host.Names{}, - }, - { - host.Names{"*", "foo.com"}, - host.Names{"foo.com"}, - host.Names{"foo.com"}, - }, - } - - for idx, tt := range tests { - t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { - result := tt.a.Intersection(tt.b) - if !reflect.DeepEqual(result, tt.intersection) { - t.Fatalf("%v.Intersection(%v) = %v, want %v", tt.a, tt.b, result, tt.intersection) - } - }) - } -} - -func TestNamesForNamespace(t *testing.T) { - tests := []struct { - hosts []string - namespace string - want host.Names - }{ - { - []string{"ns1/foo.com", "ns2/bar.com"}, - "ns1", - host.Names{"foo.com"}, - }, - { - []string{"ns1/foo.com", "ns2/bar.com"}, - "ns3", - host.Names{}, - }, - { - []string{"ns1/foo.com", "*/bar.com"}, - "ns1", - host.Names{"foo.com", "bar.com"}, - }, - { - []string{"ns1/foo.com", "*/bar.com"}, - "ns3", - host.Names{"bar.com"}, - }, - { - []string{"foo.com", "ns2/bar.com"}, - "ns2", - host.Names{"foo.com", "bar.com"}, - }, - { - []string{"foo.com", "ns2/bar.com"}, - "ns3", - host.Names{"foo.com"}, - }, - } - - for idx, tt := range tests { - t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { - result := host.NamesForNamespace(tt.hosts, tt.namespace) - if !reflect.DeepEqual(result, tt.want) { - t.Fatalf("host.NamesForNamespace(%v, %v) = %v, want %v", tt.hosts, tt.namespace, result, tt.want) - } - }) - } -} - -func TestNamesSortOrder(t *testing.T) { - tests := []struct { - in, want host.Names - }{ - // Prove we sort alphabetically: - { - host.Names{"b", "a"}, - host.Names{"a", "b"}, - }, - { - host.Names{"bb", "cc", "aa"}, - host.Names{"aa", "bb", "cc"}, - }, - // Prove we sort longest first, alphabetically: - { - host.Names{"b", "a", "aa"}, - host.Names{"aa", "a", "b"}, - }, - { - host.Names{"foo.com", "bar.com", "foo.bar.com"}, - host.Names{"foo.bar.com", "bar.com", "foo.com"}, - }, - // We sort wildcards last, always - { - host.Names{"a", "*", "z"}, - host.Names{"a", "z", "*"}, - }, - { - host.Names{"foo.com", "bar.com", "*.com"}, - host.Names{"bar.com", "foo.com", "*.com"}, - }, - { - host.Names{"foo.com", "bar.com", "*.com", "*.foo.com", "*", "baz.bar.com"}, - host.Names{"baz.bar.com", "bar.com", "foo.com", "*.foo.com", "*.com", "*"}, - }, - } - - for idx, tt := range tests { - t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { - // Save a copy to report errors with - tmp := make(host.Names, len(tt.in)) - copy(tmp, tt.in) - - sort.Sort(tt.in) - if !reflect.DeepEqual(tt.in, tt.want) { - t.Fatalf("sort.Sort(%v) = %v, want %v", tmp, tt.in, tt.want) - } - }) - } -} - -func BenchmarkNamesSort(b *testing.B) { - unsorted := host.Names{"foo.com", "bar.com", "*.com", "*.foo.com", "*", "baz.bar.com"} - - for n := 0; n < b.N; n++ { - given := make(host.Names, len(unsorted)) - copy(given, unsorted) - sort.Sort(given) - } -} diff --git a/pkg/config/kube/conversion.go b/pkg/config/kube/conversion.go deleted file mode 100644 index 1b006714b..000000000 --- a/pkg/config/kube/conversion.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "strings" -) - -import ( - coreV1 "k8s.io/api/core/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" -) - -const ( - SMTP = 25 - DNS = 53 - MySQL = 3306 - MongoDB = 27017 -) - -// Ports be skipped for protocol sniffing. Applications bound to these ports will be broken if -// protocol sniffing is enabled. -var wellKnownPorts = map[int32]struct{}{ - SMTP: {}, - DNS: {}, - MySQL: {}, - MongoDB: {}, -} - -var ( - grpcWeb = string(protocol.GRPCWeb) - grpcWebLen = len(grpcWeb) -) - -// ConvertProtocol from k8s protocol and port name -func ConvertProtocol(port int32, portName string, proto coreV1.Protocol, appProto *string) protocol.Instance { - if proto == coreV1.ProtocolUDP { - return protocol.UDP - } - - // If application protocol is set, we will use that - // If not, use the port name - name := portName - if appProto != nil { - name = *appProto - } - - // Check if the port name prefix is "grpc-web". Need to do this before the general - // prefix check below, since it contains a hyphen. - if len(name) >= grpcWebLen && strings.EqualFold(name[:grpcWebLen], grpcWeb) { - return protocol.GRPCWeb - } - - // Parse the port name to find the prefix, if any. - i := strings.IndexByte(name, '-') - if i >= 0 { - name = name[:i] - } - - p := protocol.Parse(name) - if p == protocol.Unsupported { - // Make TCP as default protocol for well know ports if protocol is not specified. - if _, has := wellKnownPorts[port]; has { - return protocol.TCP - } - } - return p -} diff --git a/pkg/config/kube/conversion_test.go b/pkg/config/kube/conversion_test.go deleted file mode 100644 index 6a7564fc3..000000000 --- a/pkg/config/kube/conversion_test.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "testing" -) - -import ( - coreV1 "k8s.io/api/core/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" -) - -func TestConvertProtocol(t *testing.T) { - https := "https" - cases := []struct { - name string - port int32 - portName string - proto coreV1.Protocol - appProto *string - expectedProto protocol.Instance - }{ - { - name: "resolves empty", - expectedProto: protocol.Unsupported, - }, - { - name: "resolves from protocol directly", - proto: coreV1.ProtocolUDP, - expectedProto: protocol.UDP, - }, - { - name: "resolves from port name", - portName: "http-something", - expectedProto: protocol.HTTP, - }, - { - name: "prefers appProto over portName", - portName: "http-something", - appProto: &https, - expectedProto: protocol.HTTPS, - }, - { - name: "resolves from appProto", - portName: "something-httpx", - appProto: &https, - expectedProto: protocol.HTTPS, - }, - { - name: "resolves grpc-web", - portName: "grpc-web-x", - expectedProto: protocol.GRPCWeb, - }, - { - name: "makes sure grpc-web is not resolved incorrectly", - portName: "grpcweb-x", - expectedProto: protocol.Unsupported, - }, - { - name: "resolves based on known ports", - port: 3306, // mysql - portName: "random-name", - expectedProto: protocol.TCP, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - actualProto := ConvertProtocol(tc.port, tc.portName, tc.proto, tc.appProto) - if actualProto != tc.expectedProto { - t.Errorf("ConvertProtocol(%d, %s, %s, %v) => %s, want %s", - tc.port, tc.portName, tc.proto, tc.appProto, actualProto, tc.expectedProto) - } - }) - } -} diff --git a/pkg/config/labels/instance.go b/pkg/config/labels/instance.go deleted file mode 100644 index c49d2218f..000000000 --- a/pkg/config/labels/instance.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package labels - -import ( - "bytes" - "fmt" - "regexp" - "sort" -) - -import ( - "github.com/hashicorp/go-multierror" -) - -const ( - DNS1123LabelMaxLength = 63 // Public for testing only. - dns1123LabelFmt = "[a-zA-Z0-9](?:[-a-zA-Z0-9]*[a-zA-Z0-9])?" - // a wild-card prefix is an '*', a normal DNS1123 label with a leading '*' or '*-', or a normal DNS1123 label - wildcardPrefix = `(\*|(\*|\*-)?` + dns1123LabelFmt + `)` - - // Using kubernetes requirement, a valid key must be a non-empty string consist - // of alphanumeric characters, '-', '_' or '.', and must start and end with an - // alphanumeric character (e.g. 'MyValue', or 'my_value', or '12345' - qualifiedNameFmt = "(?:[A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]" - - // In Kubernetes, label names can start with a DNS name followed by a '/': - dnsNamePrefixFmt = dns1123LabelFmt + `(?:\.` + dns1123LabelFmt + `)*/` - dnsNamePrefixMaxLength = 253 -) - -var ( - tagRegexp = regexp.MustCompile("^(" + dnsNamePrefixFmt + ")?(" + qualifiedNameFmt + ")$") // label value can be an empty string - labelValueRegexp = regexp.MustCompile("^" + "(" + qualifiedNameFmt + ")?" + "$") - dns1123LabelRegexp = regexp.MustCompile("^" + dns1123LabelFmt + "$") - wildcardPrefixRegexp = regexp.MustCompile("^" + wildcardPrefix + "$") -) - -// Instance is a non empty map of arbitrary strings. Each version of a service can -// be differentiated by a unique set of labels associated with the version. These -// labels are assigned to all instances of a particular service version. For -// example, lets say catalog.mystore.com has 2 versions v1 and v2. v1 instances -// could have labels gitCommit=aeiou234, region=us-east, while v2 instances could -// have labels name=kittyCat,region=us-east. -type Instance map[string]string - -// SubsetOf is true if the label has same values for the keys -func (i Instance) SubsetOf(that Instance) bool { - if len(i) == 0 { - return true - } - - if len(that) == 0 || len(that) < len(i) { - return false - } - - for k, v1 := range i { - if v2, ok := that[k]; !ok || v1 != v2 { - return false - } - } - return true -} - -// Equals returns true if the labels are equal. -func (i Instance) Equals(that Instance) bool { - if i == nil { - return that == nil - } - if that == nil { - return i == nil - } - if len(i) != len(that) { - return false - } - return i.SubsetOf(that) -} - -// Validate ensures tag is well-formed -func (i Instance) Validate() error { - if i == nil { - return nil - } - var errs error - for k, v := range i { - if err := validateTagKey(k); err != nil { - errs = multierror.Append(errs, err) - } - if !labelValueRegexp.MatchString(v) { - errs = multierror.Append(errs, fmt.Errorf("invalid tag value: %q", v)) - } - } - return errs -} - -// IsDNS1123Label tests for a string that conforms to the definition of a label in -// DNS (RFC 1123). -func IsDNS1123Label(value string) bool { - return len(value) <= DNS1123LabelMaxLength && dns1123LabelRegexp.MatchString(value) -} - -// IsWildcardDNS1123Label tests for a string that conforms to the definition of a label in DNS (RFC 1123), but allows -// the wildcard label (`*`), and typical labels with a leading astrisk instead of alphabetic character (e.g. "*-foo") -func IsWildcardDNS1123Label(value string) bool { - return len(value) <= DNS1123LabelMaxLength && wildcardPrefixRegexp.MatchString(value) -} - -// validateTagKey checks that a string is valid as a Kubernetes label name. -func validateTagKey(k string) error { - match := tagRegexp.FindStringSubmatch(k) - if match == nil { - return fmt.Errorf("invalid tag key: %q", k) - } - - if len(match[1]) > 0 { - dnsPrefixLength := len(match[1]) - 1 // exclude the trailing / from the length - if dnsPrefixLength > dnsNamePrefixMaxLength { - return fmt.Errorf("invalid tag key: %q (DNS prefix is too long)", k) - } - } - - if len(match[2]) > DNS1123LabelMaxLength { - return fmt.Errorf("invalid tag key: %q (name is too long)", k) - } - - return nil -} - -func (i Instance) String() string { - labels := make([]string, 0, len(i)) - for k, v := range i { - if len(v) > 0 { - labels = append(labels, fmt.Sprintf("%s=%s", k, v)) - } else { - labels = append(labels, k) - } - } - sort.Strings(labels) - - var buffer bytes.Buffer - first := true - for _, label := range labels { - if !first { - buffer.WriteString(",") - } else { - first = false - } - buffer.WriteString(label) - } - return buffer.String() -} diff --git a/pkg/config/labels/instance_test.go b/pkg/config/labels/instance_test.go deleted file mode 100644 index f738c7a90..000000000 --- a/pkg/config/labels/instance_test.go +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package labels_test - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" -) - -func TestInstance(t *testing.T) { - cases := []struct { - left labels.Instance - right labels.Instance - expected bool - }{ - { - left: nil, - right: labels.Instance{"app": "a"}, - expected: true, - }, - { - left: labels.Instance{"app": "a"}, - right: nil, - expected: false, - }, - { - left: labels.Instance{"app": "a"}, - right: labels.Instance{"app": "a", "prod": "env"}, - expected: true, - }, - { - left: labels.Instance{"app": "a", "prod": "env"}, - right: labels.Instance{"app": "a"}, - expected: false, - }, - { - left: labels.Instance{"app": "a"}, - right: labels.Instance{"app": "b", "prod": "env"}, - expected: false, - }, - { - left: labels.Instance{"foo": ""}, - right: labels.Instance{"app": "a"}, - expected: false, - }, - { - left: labels.Instance{"foo": ""}, - right: labels.Instance{"app": "a", "foo": ""}, - expected: true, - }, - { - left: labels.Instance{"app": "a", "foo": ""}, - right: labels.Instance{"foo": ""}, - expected: false, - }, - } - for _, c := range cases { - got := c.left.SubsetOf(c.right) - if got != c.expected { - t.Errorf("%v.SubsetOf(%v) got %v, expected %v", c.left, c.right, got, c.expected) - } - } -} - -func TestInstanceValidate(t *testing.T) { - cases := []struct { - name string - tags labels.Instance - valid bool - }{ - { - name: "empty tags", - valid: true, - }, - { - name: "bad tag", - tags: labels.Instance{"^": "^"}, - }, - { - name: "good tag", - tags: labels.Instance{"key": "value"}, - valid: true, - }, - { - name: "good tag - empty value", - tags: labels.Instance{"key": ""}, - valid: true, - }, - { - name: "good tag - DNS prefix", - tags: labels.Instance{"k8s.io/key": "value"}, - valid: true, - }, - { - name: "good tag - subdomain DNS prefix", - tags: labels.Instance{"app.kubernetes.io/name": "value"}, - valid: true, - }, - { - name: "bad tag - empty key", - tags: labels.Instance{"": "value"}, - }, - { - name: "bad tag key 1", - tags: labels.Instance{".key": "value"}, - }, - { - name: "bad tag key 2", - tags: labels.Instance{"key_": "value"}, - }, - { - name: "bad tag key 3", - tags: labels.Instance{"key$": "value"}, - }, - { - name: "bad tag key - invalid DNS prefix", - tags: labels.Instance{"istio./key": "value"}, - }, - { - name: "bad tag value 1", - tags: labels.Instance{"key": ".value"}, - }, - { - name: "bad tag value 2", - tags: labels.Instance{"key": "value_"}, - }, - { - name: "bad tag value 3", - tags: labels.Instance{"key": "value$"}, - }, - } - for _, c := range cases { - if got := c.tags.Validate(); (got == nil) != c.valid { - t.Errorf("%s failed: got valid=%v but wanted valid=%v: %v", c.name, got == nil, c.valid, got) - } - } -} diff --git a/pkg/config/legacy/mesh/const.go b/pkg/config/legacy/mesh/const.go deleted file mode 100644 index 2069ccd24..000000000 --- a/pkg/config/legacy/mesh/const.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" -) - -// MeshConfigResourceName is the resource name for the Istio MeshConfig resource. -var MeshConfigResourceName = resource.NewFullName("dubbo-system", "meshconfig") - -// MeshNetworksResourceName is the resource name for the Istio MeshNetworks resource. -var MeshNetworksResourceName = resource.NewFullName("dubbo-system", "meshnetworks") diff --git a/pkg/config/legacy/source/kube/origin.go b/pkg/config/legacy/source/kube/origin.go deleted file mode 100644 index b863f90bd..000000000 --- a/pkg/config/legacy/source/kube/origin.go +++ /dev/null @@ -1,102 +0,0 @@ -/* - Copyright Istio Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package kube - -import ( - "fmt" - "path/filepath" - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -// Origin is a K8s specific implementation of resource.Origin -type Origin struct { - Collection collection.Name - Kind string - FullName resource.FullName - Version resource.Version - Ref resource.Reference - FieldsMap map[string]int -} - -var ( - _ resource.Origin = &Origin{} - _ resource.Reference = &Position{} -) - -// FriendlyName implements resource.Origin -func (o *Origin) FriendlyName() string { - parts := strings.Split(o.FullName.String(), "/") - if len(parts) == 2 { - // The istioctl convention is [/]. - // This code has no notion of a default and always shows the namespace. - return fmt.Sprintf("%s %s/%s", o.Kind, parts[0], parts[1]) - } - return fmt.Sprintf("%s %s", o.Kind, o.FullName.String()) -} - -func (o *Origin) Comparator() string { - return o.Kind + "/" + o.FullName.Name.String() + "/" + o.FullName.Namespace.String() -} - -// Namespace implements resource.Origin -func (o *Origin) Namespace() resource.Namespace { - // Special case: the namespace of a namespace resource is its own name - if o.Collection == collections.K8SCoreV1Namespaces.Name() { - return resource.Namespace(o.FullName.Name) - } - - return o.FullName.Namespace -} - -// Reference implements resource.Origin -func (o *Origin) Reference() resource.Reference { - return o.Ref -} - -// GetFieldMap implements resource.Origin -func (o *Origin) FieldMap() map[string]int { - return o.FieldsMap -} - -// Position is a representation of the location of a source. -type Position struct { - Filename string // filename, if any - Line int // line number, starting at 1 -} - -// String outputs the string representation of the position. -func (p *Position) String() string { - s := p.Filename - // TODO: support json file position. - if p.isValid() && filepath.Ext(p.Filename) != ".json" { - if s != "" { - s += ":" - } - s += fmt.Sprintf("%d", p.Line) - } - return s -} - -func (p *Position) isValid() bool { - return p.Line > 0 && p.Filename != "" -} diff --git a/pkg/config/legacy/testing/fixtures/expect.go b/pkg/config/legacy/testing/fixtures/expect.go deleted file mode 100644 index c2b5a04bb..000000000 --- a/pkg/config/legacy/testing/fixtures/expect.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package fixtures - -import ( - "fmt" - "testing" -) - -import ( - "github.com/google/go-cmp/cmp" -) - -// ExpectEqual calls CheckEqual and fails the test if it returns an error. -func ExpectEqual(t *testing.T, o1 interface{}, o2 interface{}) { - t.Helper() - if err := CheckEqual(o1, o2); err != nil { - t.Fatal(err) - } -} - -// CheckEqual checks that o1 and o2 are equal. If not, returns an error with the diff. -func CheckEqual(o1 interface{}, o2 interface{}) error { - if diff := cmp.Diff(o1, o2); diff != "" { - return fmt.Errorf(diff) - } - return nil -} diff --git a/pkg/config/legacy/util/kuberesource/resources.go b/pkg/config/legacy/util/kuberesource/resources.go deleted file mode 100644 index de8fac5b2..000000000 --- a/pkg/config/legacy/util/kuberesource/resources.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kuberesource - -import ( - "fmt" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/resource" -) - -func SkipExcludedCollections(requiredCols collection.Names, excludedResourceKinds []string, enableServiceDiscovery bool) collection.Schemas { - resultBuilder := collection.NewSchemasBuilder() - for _, name := range requiredCols { - s, f := collections.All.Find(name.String()) - if !f { - continue - } - disabled := false - if isKindExcluded(excludedResourceKinds, s.Resource().Kind()) { - // Found a matching exclude directive for this KubeResource. Disable the resource. - disabled = true - - // Check and see if this is needed for Service Discovery. If needed, we will need to re-enable. - if enableServiceDiscovery { - if IsRequiredForServiceDiscovery(s.Resource()) { - // This is needed for service discovery. Re-enable. - disabled = false - } - } - } - - if disabled { - continue - } - - _ = resultBuilder.Add(s) - } - - return resultBuilder.Build() -} - -// DefaultExcludedResourceKinds returns the default list of resource kinds to exclude. -func DefaultExcludedResourceKinds() []string { - resources := make([]string, 0) - for _, r := range collections.Kube.All() { - if IsDefaultExcluded(r.Resource()) { - resources = append(resources, r.Resource().Kind()) - } - } - return resources -} - -func isKindExcluded(excludedResourceKinds []string, kind string) bool { - for _, excludedKind := range excludedResourceKinds { - if kind == excludedKind { - return true - } - } - - return false -} - -// the following code minimally duplicates logic from galley/pkg/config/source/kube/rt/known.go -// without propagating the many dependencies it comes with. - -var knownTypes = map[string]struct{}{ - asTypesKey("", "Service"): {}, - asTypesKey("", "Namespace"): {}, - asTypesKey("", "Node"): {}, - asTypesKey("", "Pod"): {}, - asTypesKey("", "Secret"): {}, -} - -func asTypesKey(group, kind string) string { - if group == "" { - return kind - } - return fmt.Sprintf("%s/%s", group, kind) -} - -func IsRequiredForServiceDiscovery(res resource.Schema) bool { - key := asTypesKey(res.Group(), res.Kind()) - _, ok := knownTypes[key] - return ok -} - -func IsDefaultExcluded(res resource.Schema) bool { - return IsRequiredForServiceDiscovery(res) -} diff --git a/pkg/config/mesh/kubemesh/leak_test.go b/pkg/config/mesh/kubemesh/leak_test.go deleted file mode 100644 index 55685e874..000000000 --- a/pkg/config/mesh/kubemesh/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kubemesh - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pkg/config/mesh/kubemesh/watcher.go b/pkg/config/mesh/kubemesh/watcher.go deleted file mode 100644 index 81d0811e1..000000000 --- a/pkg/config/mesh/kubemesh/watcher.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kubemesh - -import ( - "fmt" -) - -import ( - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/log" - v1 "k8s.io/api/core/v1" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/configmapwatcher" -) - -// NewConfigMapWatcher creates a new Watcher for changes to the given ConfigMap. -func NewConfigMapWatcher(client kube.Client, namespace, name, key string, multiWatch bool, stop <-chan struct{}) *mesh.MultiWatcher { - w := mesh.NewMultiWatcher(mesh.DefaultMeshConfig()) - c := configmapwatcher.NewController(client, namespace, name, func(cm *v1.ConfigMap) { - meshNetworks, err := ReadNetworksConfigMap(cm, "meshNetworks") - if err != nil { - // Keep the last known config in case there's a misconfiguration issue. - log.Warnf("failed to read mesh config from ConfigMap: %v", err) - return - } - if meshNetworks != nil { - w.SetNetworks(meshNetworks) - } - if multiWatch { - meshConfig := meshConfigMapData(cm, key) - w.HandleMeshConfigData(meshConfig) - return - } - // Original behavior - just per-revision config - meshConfig, err := ReadConfigMap(cm, key) - if err != nil { - // Keep the last known config in case there's a misconfiguration issue. - log.Warnf("failed to read mesh config from ConfigMap: %v", err) - return - } - w.HandleMeshConfig(meshConfig) - }) - - go c.Run(stop) - - // Ensure the ConfigMap is initially loaded if present. - if !cache.WaitForCacheSync(stop, c.HasSynced) { - log.Error("failed to wait for cache sync") - } - return w -} - -func AddUserMeshConfig(client kube.Client, watcher mesh.Watcher, namespace, key, userMeshConfig string, stop chan struct{}) { - c := configmapwatcher.NewController(client, namespace, userMeshConfig, func(cm *v1.ConfigMap) { - meshConfig := meshConfigMapData(cm, key) - watcher.HandleUserMeshConfig(meshConfig) - }) - - go c.Run(stop) - if !cache.WaitForCacheSync(stop, c.HasSynced) { - log.Error("failed to wait for cache sync") - } -} - -func meshConfigMapData(cm *v1.ConfigMap, key string) string { - if cm == nil { - return "" - } - - cfgYaml, exists := cm.Data[key] - if !exists { - return "" - } - - return cfgYaml -} - -func ReadConfigMap(cm *v1.ConfigMap, key string) (*meshconfig.MeshConfig, error) { - if cm == nil { - log.Info("no ConfigMap found, using default MeshConfig config") - return mesh.DefaultMeshConfig(), nil - } - - cfgYaml, exists := cm.Data[key] - if !exists { - return nil, fmt.Errorf("missing ConfigMap key %q", key) - } - - meshConfig, err := mesh.ApplyMeshConfigDefaults(cfgYaml) - if err != nil { - return nil, fmt.Errorf("failed reading MeshConfig config: %v. YAML:\n%s", err, cfgYaml) - } - - log.Info("Loaded MeshConfig config from Kubernetes API server.") - return meshConfig, nil -} - -func ReadNetworksConfigMap(cm *v1.ConfigMap, key string) (*meshconfig.MeshNetworks, error) { - if cm == nil { - log.Info("no ConfigMap found, using existing MeshNetworks config") - return nil, nil - } - - cfgYaml, exists := cm.Data[key] - if !exists { - return nil, nil - } - - meshNetworks, err := mesh.ParseMeshNetworks(cfgYaml) - if err != nil { - return nil, fmt.Errorf("failed reading MeshNetworks config: %v. YAML:\n%s", err, cfgYaml) - } - - log.Info("Loaded MeshNetworks config from Kubernetes API server.") - return meshNetworks, nil -} diff --git a/pkg/config/mesh/kubemesh/watcher_test.go b/pkg/config/mesh/kubemesh/watcher_test.go deleted file mode 100644 index 4bc1c9014..000000000 --- a/pkg/config/mesh/kubemesh/watcher_test.go +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kubemesh - -import ( - "context" - "fmt" - "sync" - "testing" - "time" -) - -import ( - "github.com/google/go-cmp/cmp" - . "github.com/onsi/gomega" - "google.golang.org/protobuf/testing/protocmp" - meshconfig "istio.io/api/mesh/v1alpha1" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - corev1 "k8s.io/client-go/kubernetes/typed/core/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -const ( - namespace string = "dubbo-system" - name string = "istio" - key string = "MeshConfig" -) - -func makeConfigMapWithName(name, resourceVersion string, data map[string]string) *v1.ConfigMap { - return &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - ResourceVersion: resourceVersion, - }, - Data: data, - } -} - -func makeConfigMap(resourceVersion string, data map[string]string) *v1.ConfigMap { - return makeConfigMapWithName(name, resourceVersion, data) -} - -func TestExtraConfigmap(t *testing.T) { - extraCmName := "extra" - - cmCore := makeConfigMap("1", map[string]string{ - key: "ingressClass: core", - }) - cmUser := makeConfigMapWithName(extraCmName, "1", map[string]string{ - key: "ingressClass: user", - }) - cmUserinvalid := makeConfigMapWithName(extraCmName, "1", map[string]string{ - key: "ingressClass: 1", - }) - setup := func(t test.Failer) (corev1.ConfigMapInterface, mesh.Watcher) { - client := kube.NewFakeClient() - cms := client.Kube().CoreV1().ConfigMaps(namespace) - stop := make(chan struct{}) - t.Cleanup(func() { close(stop) }) - w := NewConfigMapWatcher(client, namespace, name, key, true, stop) - AddUserMeshConfig(client, w, namespace, key, extraCmName, stop) - return cms, w - } - - t.Run("core first", func(t *testing.T) { - cms, w := setup(t) - if _, err := cms.Create(context.Background(), cmCore, metav1.CreateOptions{}); err != nil { - t.Fatal(err) - } - if _, err := cms.Create(context.Background(), cmUser, metav1.CreateOptions{}); err != nil { - t.Fatal(err) - } - retry.UntilOrFail(t, func() bool { return w.Mesh().GetIngressClass() == "core" }, retry.Delay(time.Millisecond), retry.Timeout(time.Second)) - }) - t.Run("user first", func(t *testing.T) { - cms, w := setup(t) - if _, err := cms.Create(context.Background(), cmUser, metav1.CreateOptions{}); err != nil { - t.Fatal(err) - } - if _, err := cms.Create(context.Background(), cmCore, metav1.CreateOptions{}); err != nil { - t.Fatal(err) - } - retry.UntilOrFail(t, func() bool { return w.Mesh().GetIngressClass() == "core" }, retry.Delay(time.Millisecond), retry.Timeout(time.Second)) - }) - t.Run("only user", func(t *testing.T) { - cms, w := setup(t) - if _, err := cms.Create(context.Background(), cmUser, metav1.CreateOptions{}); err != nil { - t.Fatal(err) - } - retry.UntilOrFail(t, func() bool { return w.Mesh().GetIngressClass() == "user" }, retry.Delay(time.Millisecond), retry.Timeout(time.Second)) - }) - t.Run("only core", func(t *testing.T) { - cms, w := setup(t) - if _, err := cms.Create(context.Background(), cmCore, metav1.CreateOptions{}); err != nil { - t.Fatal(err) - } - retry.UntilOrFail(t, func() bool { return w.Mesh().GetIngressClass() == "core" }, retry.Delay(time.Millisecond), retry.Timeout(time.Second)) - }) - t.Run("invalid user config", func(t *testing.T) { - cms, w := setup(t) - if _, err := cms.Create(context.Background(), cmCore, metav1.CreateOptions{}); err != nil { - t.Fatal(err) - } - if _, err := cms.Create(context.Background(), cmUserinvalid, metav1.CreateOptions{}); err != nil { - t.Fatal(err) - } - retry.UntilOrFail(t, func() bool { return w.Mesh().GetIngressClass() == "core" }, retry.Delay(time.Millisecond), retry.Timeout(time.Second)) - }) -} - -func TestNewConfigMapWatcher(t *testing.T) { - yaml := "trustDomain: something.new" - m, err := mesh.ApplyMeshConfigDefaults(yaml) - if err != nil { - t.Fatal(err) - } - - cm := makeConfigMap("1", map[string]string{ - key: yaml, - }) - badCM := makeConfigMap("2", map[string]string{ - "other-key": yaml, - }) - badCM2 := makeConfigMap("3", map[string]string{ - key: "bad yaml", - }) - - client := kube.NewFakeClient() - cms := client.Kube().CoreV1().ConfigMaps(namespace) - stop := make(chan struct{}) - t.Cleanup(func() { close(stop) }) - w := NewConfigMapWatcher(client, namespace, name, key, false, stop) - - var mu sync.Mutex - newM := mesh.DefaultMeshConfig() - w.AddMeshHandler(func() { - mu.Lock() - defer mu.Unlock() - newM = w.Mesh() - }) - - steps := []struct { - added *v1.ConfigMap - updated *v1.ConfigMap - deleted *v1.ConfigMap - - expect *meshconfig.MeshConfig - }{ - {expect: mesh.DefaultMeshConfig()}, - {added: cm, expect: m}, - - // Handle misconfiguration errors. - {updated: badCM, expect: m}, - {updated: cm, expect: m}, - {updated: badCM2, expect: m}, - {updated: badCM, expect: m}, - {updated: cm, expect: m}, - - {deleted: cm, expect: mesh.DefaultMeshConfig()}, - {added: badCM, expect: mesh.DefaultMeshConfig()}, - } - - for i, step := range steps { - t.Run(fmt.Sprintf("[%v]", i), func(t *testing.T) { - g := NewWithT(t) - - switch { - case step.added != nil: - _, err := cms.Create(context.TODO(), step.added, metav1.CreateOptions{}) - g.Expect(err).Should(BeNil()) - case step.updated != nil: - _, err := cms.Update(context.TODO(), step.updated, metav1.UpdateOptions{}) - g.Expect(err).Should(BeNil()) - case step.deleted != nil: - g.Expect(cms.Delete(context.TODO(), step.deleted.Name, metav1.DeleteOptions{})). - Should(Succeed()) - } - - retry.UntilOrFail(t, func() bool { return cmp.Equal(w.Mesh(), step.expect, protocmp.Transform()) }) - retry.UntilOrFail(t, func() bool { - mu.Lock() - defer mu.Unlock() - return cmp.Equal(newM, step.expect, protocmp.Transform()) - }) - }) - } -} diff --git a/pkg/config/mesh/mesh.go b/pkg/config/mesh/mesh.go deleted file mode 100644 index ee5eec301..000000000 --- a/pkg/config/mesh/mesh.go +++ /dev/null @@ -1,335 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "fmt" - "os" - "time" -) - -import ( - "github.com/hashicorp/go-multierror" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/durationpb" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/api/networking/v1alpha3" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/validation" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// DefaultProxyConfig for individual proxies -func DefaultProxyConfig() *meshconfig.ProxyConfig { - // TODO: include revision based on REVISION env - // TODO: set default namespace based on POD_NAMESPACE env - return &meshconfig.ProxyConfig{ - ConfigPath: constants.ConfigPathDir, - ClusterName: &meshconfig.ProxyConfig_ServiceCluster{ServiceCluster: constants.ServiceClusterName}, - DrainDuration: durationpb.New(45 * time.Second), - ParentShutdownDuration: durationpb.New(60 * time.Second), - TerminationDrainDuration: durationpb.New(5 * time.Second), - ProxyAdminPort: 15000, - Concurrency: &wrappers.Int32Value{Value: 2}, - ControlPlaneAuthPolicy: meshconfig.AuthenticationPolicy_MUTUAL_TLS, - DiscoveryAddress: "istiod.dubbo-system.svc:15012", - Tracing: &meshconfig.Tracing{ - Tracer: &meshconfig.Tracing_Zipkin_{ - Zipkin: &meshconfig.Tracing_Zipkin{ - Address: "zipkin.dubbo-system:9411", - }, - }, - }, - - // Code defaults - BinaryPath: constants.BinaryPathFilename, - StatNameLength: 189, - StatusPort: 15020, - } -} - -// DefaultMeshNetworks returns a default meshnetworks configuration. -// By default, it is empty. -func DefaultMeshNetworks() *meshconfig.MeshNetworks { - mn := EmptyMeshNetworks() - return &mn -} - -// DefaultMeshConfig returns the default mesh config. -// This is merged with values from the mesh config map. -func DefaultMeshConfig() *meshconfig.MeshConfig { - proxyConfig := DefaultProxyConfig() - - // Defaults matching the standard install - // order matches the generated mesh config. - return &meshconfig.MeshConfig{ - EnableTracing: true, - AccessLogFile: "", - AccessLogEncoding: meshconfig.MeshConfig_TEXT, - AccessLogFormat: "", - EnableEnvoyAccessLogService: false, - ProtocolDetectionTimeout: durationpb.New(0), - IngressService: "istio-ingressgateway", - IngressControllerMode: meshconfig.MeshConfig_STRICT, - IngressClass: "istio", - TrustDomain: constants.DefaultKubernetesDomain, - TrustDomainAliases: []string{}, - EnableAutoMtls: &wrappers.BoolValue{Value: true}, - OutboundTrafficPolicy: &meshconfig.MeshConfig_OutboundTrafficPolicy{Mode: meshconfig.MeshConfig_OutboundTrafficPolicy_ALLOW_ANY}, - LocalityLbSetting: &v1alpha3.LocalityLoadBalancerSetting{ - Enabled: &wrappers.BoolValue{Value: true}, - }, - Certificates: []*meshconfig.Certificate{}, - DefaultConfig: proxyConfig, - - RootNamespace: constants.IstioSystemNamespace, - ProxyListenPort: 15001, - ConnectTimeout: durationpb.New(10 * time.Second), - DefaultServiceExportTo: []string{"*"}, - DefaultVirtualServiceExportTo: []string{"*"}, - DefaultDestinationRuleExportTo: []string{"*"}, - // DnsRefreshRate is only used when DNS requests fail (NXDOMAIN or SERVFAIL). For success, the TTL - // will be used. - // https://datatracker.ietf.org/doc/html/rfc2308#section-3 defines how negative DNS results should handle TTLs, - // but Envoy does not respect this (https://github.com/envoyproxy/envoy/issues/20885). - // To counter this, we bump up the default to 60s to avoid overloading DNS servers. - DnsRefreshRate: durationpb.New(60 * time.Second), - ThriftConfig: &meshconfig.MeshConfig_ThriftConfig{}, - ServiceSettings: make([]*meshconfig.MeshConfig_ServiceSettings, 0), - - DefaultProviders: &meshconfig.MeshConfig_DefaultProviders{}, - ExtensionProviders: []*meshconfig.MeshConfig_ExtensionProvider{ - { - Name: "prometheus", - Provider: &meshconfig.MeshConfig_ExtensionProvider_Prometheus{ - Prometheus: &meshconfig.MeshConfig_ExtensionProvider_PrometheusMetricsProvider{}, - }, - }, - { - Name: "stackdriver", - Provider: &meshconfig.MeshConfig_ExtensionProvider_Stackdriver{ - Stackdriver: &meshconfig.MeshConfig_ExtensionProvider_StackdriverProvider{}, - }, - }, - { - Name: "envoy", - Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLog{ - EnvoyFileAccessLog: &meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider{ - Path: "/dev/stdout", - }, - }, - }, - }, - } -} - -// ApplyProxyConfig applies the give proxy config yaml to a mesh config object. The passed in mesh config -// will not be modified. -func ApplyProxyConfig(yaml string, meshConfig *meshconfig.MeshConfig) (*meshconfig.MeshConfig, error) { - mc := proto.Clone(meshConfig).(*meshconfig.MeshConfig) - pc, err := applyProxyConfig(yaml, mc.DefaultConfig) - if err != nil { - return nil, err - } - mc.DefaultConfig = pc - return mc, nil -} - -func applyProxyConfig(yaml string, proxyConfig *meshconfig.ProxyConfig) (*meshconfig.ProxyConfig, error) { - origMetadata := proxyConfig.ProxyMetadata - if err := protomarshal.ApplyYAML(yaml, proxyConfig); err != nil { - return nil, fmt.Errorf("could not parse proxy config: %v", err) - } - newMetadata := proxyConfig.ProxyMetadata - proxyConfig.ProxyMetadata = mergeMap(origMetadata, newMetadata) - return proxyConfig, nil -} - -func extractYamlField(key string, mp map[string]interface{}) (string, error) { - proxyConfig := mp[key] - if proxyConfig == nil { - return "", nil - } - bytes, err := yaml.Marshal(proxyConfig) - if err != nil { - return "", err - } - return string(bytes), nil -} - -func toMap(yamlText string) (map[string]interface{}, error) { - mp := map[string]interface{}{} - if err := yaml.Unmarshal([]byte(yamlText), &mp); err != nil { - return nil, err - } - return mp, nil -} - -// ApplyMeshConfig returns a new MeshConfig decoded from the -// input YAML with the provided defaults applied to omitted configuration values. -func ApplyMeshConfig(yaml string, defaultConfig *meshconfig.MeshConfig) (*meshconfig.MeshConfig, error) { - // We want to keep semantics that all fields are overrides, except proxy config is a merge. This allows - // decent customization while also not requiring users to redefine the entire proxy config if they want to override - // Note: if we want to add more structure in the future, we will likely need to revisit this idea. - - // Store the current set proxy config so we don't wipe it out, we will configure this later - prevProxyConfig := defaultConfig.DefaultConfig - prevDefaultProvider := defaultConfig.DefaultProviders - prevExtensionProviders := defaultConfig.ExtensionProviders - prevTrustDomainAliases := defaultConfig.TrustDomainAliases - - defaultConfig.DefaultConfig = DefaultProxyConfig() - if err := protomarshal.ApplyYAML(yaml, defaultConfig); err != nil { - return nil, multierror.Prefix(err, "failed to convert to proto.") - } - defaultConfig.DefaultConfig = prevProxyConfig - - raw, err := toMap(yaml) - if err != nil { - return nil, err - } - // Get just the proxy config yaml - pc, err := extractYamlField("defaultConfig", raw) - if err != nil { - return nil, multierror.Prefix(err, "failed to extract proxy config") - } - if pc != "" { - pc, err := applyProxyConfig(pc, defaultConfig.DefaultConfig) - if err != nil { - return nil, err - } - defaultConfig.DefaultConfig = pc - } - - defaultConfig.DefaultProviders = prevDefaultProvider - dp, err := extractYamlField("defaultProviders", raw) - if err != nil { - return nil, multierror.Prefix(err, "failed to extract default providers") - } - if dp != "" { - if err := protomarshal.ApplyYAML(dp, defaultConfig.DefaultProviders); err != nil { - return nil, fmt.Errorf("could not parse default providers: %v", err) - } - } - - newExtensionProviders := defaultConfig.ExtensionProviders - defaultConfig.ExtensionProviders = prevExtensionProviders - for _, p := range newExtensionProviders { - found := false - for _, e := range defaultConfig.ExtensionProviders { - if p.Name == e.Name { - e.Provider = p.Provider - found = true - break - } - } - if !found { - defaultConfig.ExtensionProviders = append(defaultConfig.ExtensionProviders, p) - } - } - - defaultConfig.TrustDomainAliases = sets.New(append(defaultConfig.TrustDomainAliases, prevTrustDomainAliases...)...).SortedList() - - if err := validation.ValidateMeshConfig(defaultConfig); err != nil { - return nil, err - } - - return defaultConfig, nil -} - -func mergeMap(original map[string]string, merger map[string]string) map[string]string { - if original == nil && merger == nil { - return nil - } - if original == nil { - original = map[string]string{} - } - for k, v := range merger { - original[k] = v - } - return original -} - -// ApplyMeshConfigDefaults returns a new MeshConfig decoded from the -// input YAML with defaults applied to omitted configuration values. -func ApplyMeshConfigDefaults(yaml string) (*meshconfig.MeshConfig, error) { - return ApplyMeshConfig(yaml, DefaultMeshConfig()) -} - -func DeepCopyMeshConfig(mc *meshconfig.MeshConfig) (*meshconfig.MeshConfig, error) { - j, err := protomarshal.ToJSON(mc) - if err != nil { - return nil, err - } - nmc := &meshconfig.MeshConfig{} - if err := protomarshal.ApplyJSON(j, nmc); err != nil { - return nil, err - } - return nmc, nil -} - -// EmptyMeshNetworks configuration with no networks -func EmptyMeshNetworks() meshconfig.MeshNetworks { - return meshconfig.MeshNetworks{ - Networks: map[string]*meshconfig.Network{}, - } -} - -// ParseMeshNetworks returns a new MeshNetworks decoded from the -// input YAML. -func ParseMeshNetworks(yaml string) (*meshconfig.MeshNetworks, error) { - out := EmptyMeshNetworks() - if err := protomarshal.ApplyYAML(yaml, &out); err != nil { - return nil, multierror.Prefix(err, "failed to convert to proto.") - } - - if err := validation.ValidateMeshNetworks(&out); err != nil { - return nil, err - } - return &out, nil -} - -// ReadMeshNetworks gets mesh networks configuration from a config file -func ReadMeshNetworks(filename string) (*meshconfig.MeshNetworks, error) { - yaml, err := os.ReadFile(filename) - if err != nil { - return nil, multierror.Prefix(err, "cannot read networks config file") - } - return ParseMeshNetworks(string(yaml)) -} - -// ReadMeshConfig gets mesh configuration from a config file -func ReadMeshConfig(filename string) (*meshconfig.MeshConfig, error) { - yaml, err := os.ReadFile(filename) - if err != nil { - return nil, multierror.Prefix(err, "cannot read mesh config file") - } - return ApplyMeshConfigDefaults(string(yaml)) -} - -// ReadMeshConfigData gets mesh configuration yaml from a config file -func ReadMeshConfigData(filename string) (string, error) { - yaml, err := os.ReadFile(filename) - if err != nil { - return "", multierror.Prefix(err, "cannot read mesh config file") - } - return string(yaml), nil -} diff --git a/pkg/config/mesh/mesh_test.go b/pkg/config/mesh/mesh_test.go deleted file mode 100644 index dc42ff5cd..000000000 --- a/pkg/config/mesh/mesh_test.go +++ /dev/null @@ -1,404 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh_test - -import ( - "fmt" - "testing" -) - -import ( - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/testing/protocmp" - "google.golang.org/protobuf/types/known/wrapperspb" - meshconfig "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/validation" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -func TestApplyProxyConfig(t *testing.T) { - config := mesh.DefaultMeshConfig() - defaultDiscovery := config.DefaultConfig.DiscoveryAddress - - t.Run("apply single", func(t *testing.T) { - mc, err := mesh.ApplyProxyConfig("discoveryAddress: foo", config) - if err != nil { - t.Fatal(err) - } - if mc.DefaultConfig.DiscoveryAddress != "foo" { - t.Fatalf("expected discoveryAddress: foo, got %q", mc.DefaultConfig.DiscoveryAddress) - } - }) - - t.Run("apply again", func(t *testing.T) { - mc, err := mesh.ApplyProxyConfig("drainDuration: 5s", config) - if err != nil { - t.Fatal(err) - } - // Ensure we didn't modify the passed in mesh config - if mc.DefaultConfig.DiscoveryAddress != defaultDiscovery { - t.Fatalf("expected discoveryAddress: %q, got %q", defaultDiscovery, mc.DefaultConfig.DiscoveryAddress) - } - if mc.DefaultConfig.DrainDuration.Seconds != 5 { - t.Fatalf("expected drainDuration: 5s, got %q", mc.DefaultConfig.DrainDuration.Seconds) - } - }) - - t.Run("apply proxy metadata", func(t *testing.T) { - config := mesh.DefaultMeshConfig() - config.DefaultConfig.ProxyMetadata = map[string]string{ - "merged": "original", - "default": "foo", - } - mc, err := mesh.ApplyProxyConfig(`proxyMetadata: {"merged":"override","override":"bar"}`, config) - assert.NoError(t, err) - // Ensure we didn't modify the passed in mesh config - assert.Equal(t, mc.DefaultConfig.ProxyMetadata, map[string]string{ - "merged": "override", - "default": "foo", - "override": "bar", - }, "unexpected proxy metadata") - }) - t.Run("apply proxy metadata to mesh config", func(t *testing.T) { - config := mesh.DefaultMeshConfig() - config.DefaultConfig.ProxyMetadata = map[string]string{ - "merged": "original", - "default": "foo", - } - mc, err := mesh.ApplyMeshConfig(`defaultConfig: - proxyMetadata: {"merged":"override","override":"bar"}`, config) - if err != nil { - t.Fatal(err) - } - // Ensure we didn't modify the passed in mesh config - assert.Equal(t, mc.DefaultConfig.ProxyMetadata, map[string]string{ - "merged": "override", - "default": "foo", - "override": "bar", - }, "unexpected proxy metadata") - }) - t.Run("apply should not modify", func(t *testing.T) { - config := mesh.DefaultMeshConfig() - config.DefaultConfig.ProxyMetadata = map[string]string{ - "foo": "bar", - } - orig, err := protomarshal.ToYAML(config) - if err != nil { - t.Fatal(err) - } - - if _, err := mesh.ApplyProxyConfig(`proxyMetadata: {"merged":"override","override":"bar"}`, config); err != nil { - t.Fatal(err) - } - after, err := protomarshal.ToYAML(config) - if err != nil { - t.Fatal(err) - } - if orig != after { - t.Fatalf("Changed before and after. Expected %v, got %v", orig, after) - } - }) -} - -func TestDefaultProxyConfig(t *testing.T) { - if err := validation.ValidateMeshConfigProxyConfig(mesh.DefaultProxyConfig()); err != nil { - t.Errorf("validation of default proxy config failed with %v", err) - } -} - -func TestDefaultMeshConfig(t *testing.T) { - if err := validation.ValidateMeshConfig(mesh.DefaultMeshConfig()); err != nil { - t.Errorf("validation of default mesh config failed with %v", err) - } -} - -func TestApplyMeshConfigDefaults(t *testing.T) { - configPath := "/test/config/patch" - yaml := fmt.Sprintf(` -defaultConfig: - configPath: %s -`, configPath) - - want := mesh.DefaultMeshConfig() - want.DefaultConfig.ConfigPath = configPath - - got, err := mesh.ApplyMeshConfigDefaults(yaml) - if err != nil { - t.Fatalf("ApplyMeshConfigDefaults() failed: %v", err) - } - assert.Equal(t, got, want) - // Verify overrides - got, err = mesh.ApplyMeshConfigDefaults(` -serviceSettings: - - settings: - clusterLocal: true - host: - - "*.myns.svc.cluster.local" -ingressClass: foo -enableTracing: false -trustDomainAliases: ["default", "both"] -defaultServiceExportTo: -- "foo" -outboundTrafficPolicy: - mode: REGISTRY_ONLY -clusterLocalNamespaces: -- "foons" -defaultProviders: - tracing: [foo] -extensionProviders: -- name: sd - stackdriver: {} -defaultConfig: - tracing: {} - concurrency: 4`) - if err != nil { - t.Fatal(err) - } - if got.DefaultConfig.Tracing.GetZipkin() != nil { - t.Error("Failed to override tracing") - } - if len(got.DefaultProviders.GetMetrics()) != 0 { - t.Errorf("default providers deep merge failed, got %v", got.DefaultProviders.GetMetrics()) - } - if !cmp.Equal(getExtensionProviders(got.ExtensionProviders), []string{"prometheus", "stackdriver", "envoy", "sd"}, protocmp.Transform()) { - t.Errorf("extension providers deep merge failed, got %v", getExtensionProviders(got.ExtensionProviders)) - } - if len(got.TrustDomainAliases) != 2 { - t.Errorf("trust domain aliases deep merge failed") - } - - gotY, err := protomarshal.ToYAML(got) - t.Log("Result: \n", gotY, err) -} - -func getExtensionProviders(eps []*meshconfig.MeshConfig_ExtensionProvider) []string { - got := []string{} - for _, ep := range eps { - got = append(got, ep.Name) - } - return got -} - -func TestDeepMerge(t *testing.T) { - cases := []struct { - name string - in string - out string - }{ - { - name: "set other default provider", - in: ` -defaultProviders: - tracing: [foo]`, - out: `defaultProviders: - metrics: - - stackdriver - tracing: - - foo -extensionProviders: -- name: stackdriver - stackdriver: - maxNumberOfAttributes: 3 -trustDomainAliases: ["both", "default"] -`, - }, - { - name: "override default provider", - in: ` -defaultProviders: - metrics: [foo]`, - out: `defaultProviders: - metrics: - - foo -extensionProviders: -- name: stackdriver - stackdriver: - maxNumberOfAttributes: 3 -trustDomainAliases: ["both", "default"] -`, - }, - { - name: "replace builtin provider", - in: ` -extensionProviders: -- name: stackdriver - stackdriver: - maxNumberOfAnnotations: 5`, - out: `defaultProviders: - metrics: - - stackdriver -extensionProviders: -- name: stackdriver - stackdriver: - maxNumberOfAnnotations: 5 -trustDomainAliases: ["both", "default"] -`, - }, - { - name: "add provider with existing type", - in: ` -extensionProviders: -- name: stackdriver-annotations - stackdriver: - maxNumberOfAnnotations: 5`, - out: `defaultProviders: - metrics: - - stackdriver -extensionProviders: -- name: stackdriver - stackdriver: - maxNumberOfAttributes: 3 -- name: stackdriver-annotations - stackdriver: - maxNumberOfAnnotations: 5 -trustDomainAliases: ["both", "default"] -`, - }, - { - name: "add provider", - in: ` -extensionProviders: -- name: prometheus - prometheus: {}`, - out: `defaultProviders: - metrics: - - stackdriver -extensionProviders: -- name: stackdriver - stackdriver: - maxNumberOfAttributes: 3 -- name: prometheus - prometheus: {} -trustDomainAliases: ["both", "default"] -`, - }, - { - name: "add trust domain aliases", - in: ` -trustDomainAliases: ["added", "both"]`, - out: `defaultProviders: - metrics: - - stackdriver -extensionProviders: -- name: stackdriver - stackdriver: - maxNumberOfAttributes: 3 -trustDomainAliases: -- added -- both -- default -`, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - mc := mesh.DefaultMeshConfig() - mc.DefaultProviders = &meshconfig.MeshConfig_DefaultProviders{ - Metrics: []string{"stackdriver"}, - } - mc.ExtensionProviders = []*meshconfig.MeshConfig_ExtensionProvider{{ - Name: "stackdriver", - Provider: &meshconfig.MeshConfig_ExtensionProvider_Stackdriver{ - Stackdriver: &meshconfig.MeshConfig_ExtensionProvider_StackdriverProvider{ - MaxNumberOfAttributes: &wrapperspb.Int64Value{Value: 3}, - }, - }, - }} - mc.TrustDomainAliases = []string{"default", "both"} - res, err := mesh.ApplyMeshConfig(tt.in, mc) - if err != nil { - t.Fatal(err) - } - // Just extract fields we are testing - minimal := &meshconfig.MeshConfig{} - minimal.DefaultProviders = res.DefaultProviders - minimal.ExtensionProviders = res.ExtensionProviders - minimal.TrustDomainAliases = res.TrustDomainAliases - - want := &meshconfig.MeshConfig{} - protomarshal.ApplyYAML(tt.out, want) - if d := cmp.Diff(want, minimal, protocmp.Transform()); d != "" { - t.Fatalf("got diff %v", d) - } - }) - } -} - -func TestApplyMeshNetworksDefaults(t *testing.T) { - yml := ` -networks: - network1: - endpoints: - - fromCidr: "192.168.0.1/24" - gateways: - - address: 1.1.1.1 - port: 80 - network2: - endpoints: - - fromRegistry: reg1 - gateways: - - registryServiceName: reg1 - port: 443 -` - - want := mesh.EmptyMeshNetworks() - want.Networks = map[string]*meshconfig.Network{ - "network1": { - Endpoints: []*meshconfig.Network_NetworkEndpoints{ - { - Ne: &meshconfig.Network_NetworkEndpoints_FromCidr{ - FromCidr: "192.168.0.1/24", - }, - }, - }, - Gateways: []*meshconfig.Network_IstioNetworkGateway{ - { - Gw: &meshconfig.Network_IstioNetworkGateway_Address{ - Address: "1.1.1.1", - }, - Port: 80, - }, - }, - }, - "network2": { - Endpoints: []*meshconfig.Network_NetworkEndpoints{ - { - Ne: &meshconfig.Network_NetworkEndpoints_FromRegistry{ - FromRegistry: "reg1", - }, - }, - }, - Gateways: []*meshconfig.Network_IstioNetworkGateway{ - { - Gw: &meshconfig.Network_IstioNetworkGateway_RegistryServiceName{ - RegistryServiceName: "reg1", - }, - Port: 443, - }, - }, - }, - } - - got, err := mesh.ParseMeshNetworks(yml) - if err != nil { - t.Fatalf("ApplyMeshNetworksDefaults() failed: %v", err) - } - assert.Equal(t, got, &want) -} diff --git a/pkg/config/mesh/networks_watcher.go b/pkg/config/mesh/networks_watcher.go deleted file mode 100644 index 7fc653531..000000000 --- a/pkg/config/mesh/networks_watcher.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "fmt" - "reflect" - "sync" -) - -import ( - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/filewatcher" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -// NetworksHolder is a holder of a mesh networks configuration. -type NetworksHolder interface { - SetNetworks(*meshconfig.MeshNetworks) - Networks() *meshconfig.MeshNetworks -} - -// NetworksWatcher watches changes to the mesh networks config. -type NetworksWatcher interface { - NetworksHolder - - AddNetworksHandler(func()) -} - -var _ NetworksWatcher = &internalNetworkWatcher{} - -type internalNetworkWatcher struct { - mutex sync.RWMutex - handlers []func() - networks *meshconfig.MeshNetworks -} - -// NewFixedNetworksWatcher creates a new NetworksWatcher that always returns the given config. -// It will never fire any events, since the config never changes. -func NewFixedNetworksWatcher(networks *meshconfig.MeshNetworks) NetworksWatcher { - return &internalNetworkWatcher{ - networks: networks, - } -} - -// NewNetworksWatcher creates a new watcher for changes to the given networks config file. -func NewNetworksWatcher(fileWatcher filewatcher.FileWatcher, filename string) (NetworksWatcher, error) { - meshNetworks, err := ReadMeshNetworks(filename) - if err != nil { - return nil, fmt.Errorf("failed to read mesh networks configuration from %q: %v", filename, err) - } - - networksdump, _ := protomarshal.ToJSONWithIndent(meshNetworks, " ") - log.Infof("mesh networks configuration: %s", networksdump) - - w := &internalNetworkWatcher{ - networks: meshNetworks, - } - - // Watch the networks config file for changes and reload if it got modified - addFileWatcher(fileWatcher, filename, func() { - // Reload the config file - meshNetworks, err := ReadMeshNetworks(filename) - if err != nil { - log.Warnf("failed to read mesh networks configuration from %q: %v", filename, err) - return - } - w.SetNetworks(meshNetworks) - }) - return w, nil -} - -// Networks returns the latest network configuration for the mesh. -func (w *internalNetworkWatcher) Networks() *meshconfig.MeshNetworks { - if w == nil { - return nil - } - w.mutex.RLock() - defer w.mutex.RUnlock() - return w.networks -} - -// SetNetworks will use the given value for mesh networks and notify all handlers of the change -func (w *internalNetworkWatcher) SetNetworks(meshNetworks *meshconfig.MeshNetworks) { - var handlers []func() - - w.mutex.Lock() - if !reflect.DeepEqual(meshNetworks, w.networks) { - networksdump, _ := protomarshal.ToJSONWithIndent(meshNetworks, " ") - log.Infof("mesh networks configuration updated to: %s", networksdump) - - // Store the new config. - w.networks = meshNetworks - handlers = append([]func(){}, w.handlers...) - } - w.mutex.Unlock() - - // Notify the handlers of the change. - for _, h := range handlers { - h() - } -} - -// AddNetworksHandler registers a callback handler for changes to the mesh network config. -func (w *internalNetworkWatcher) AddNetworksHandler(h func()) { - w.mutex.Lock() - defer w.mutex.Unlock() - - // hack: prepend handlers; the last to be added will be run first and block other handlers - w.handlers = append([]func(){h}, w.handlers...) -} diff --git a/pkg/config/mesh/networks_watcher_test.go b/pkg/config/mesh/networks_watcher_test.go deleted file mode 100644 index 7d917434e..000000000 --- a/pkg/config/mesh/networks_watcher_test.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh_test - -import ( - "testing" - "time" -) - -import ( - . "github.com/onsi/gomega" - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/filewatcher" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" -) - -func TestNewNetworksWatcherWithBadInputShouldFail(t *testing.T) { - g := NewWithT(t) - _, err := mesh.NewNetworksWatcher(filewatcher.NewWatcher(), "") - g.Expect(err).ToNot(BeNil()) -} - -func TestNetworksWatcherShouldNotifyHandlers(t *testing.T) { - g := NewWithT(t) - - path := newTempFile(t) - defer removeSilent(path) - - n := meshconfig.MeshNetworks{ - Networks: make(map[string]*meshconfig.Network), - } - writeMessage(t, path, &n) - - w := newNetworksWatcher(t, path) - g.Expect(w.Networks()).To(Equal(&n)) - - doneCh := make(chan struct{}, 1) - - var newN *meshconfig.MeshNetworks - w.AddNetworksHandler(func() { - newN = w.Networks() - close(doneCh) - }) - - // Change the file to trigger the update. - n.Networks["test"] = &meshconfig.Network{} - writeMessage(t, path, &n) - - select { - case <-doneCh: - g.Expect(newN).To(Equal(&n)) - g.Expect(w.Networks()).To(Equal(newN)) - break - case <-time.After(time.Second * 5): - t.Fatal("timed out waiting for update") - } -} - -func newNetworksWatcher(t *testing.T, filename string) mesh.NetworksWatcher { - t.Helper() - w, err := mesh.NewNetworksWatcher(filewatcher.NewWatcher(), filename) - if err != nil { - t.Fatal(err) - } - return w -} diff --git a/pkg/config/mesh/watcher.go b/pkg/config/mesh/watcher.go deleted file mode 100644 index 6119b2fd2..000000000 --- a/pkg/config/mesh/watcher.go +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "reflect" - "sync" - "sync/atomic" - "time" - "unsafe" -) - -import ( - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/filewatcher" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -// Holder of a mesh configuration. -type Holder interface { - Mesh() *meshconfig.MeshConfig -} - -// Watcher is a Holder whose mesh config can be updated asynchronously. -type Watcher interface { - Holder - - // AddMeshHandler registers a callback handler for changes to the mesh config. - AddMeshHandler(func()) - - // HandleUserMeshConfig keeps track of user mesh config overrides. These are merged with the standard - // mesh config, which takes precedence. - HandleUserMeshConfig(string) -} - -// MultiWatcher is a struct wrapping the internal injector to let users know that both -type MultiWatcher struct { - internalWatcher - internalNetworkWatcher -} - -func NewMultiWatcher(config *meshconfig.MeshConfig) *MultiWatcher { - return &MultiWatcher{ - internalWatcher: internalWatcher{MeshConfig: config}, - } -} - -var _ Watcher = &internalWatcher{} - -type internalWatcher struct { - mutex sync.Mutex - handlers []func() - // Current merged mesh config - MeshConfig *meshconfig.MeshConfig - - userMeshConfig string - revMeshConfig string -} - -// NewFixedWatcher creates a new Watcher that always returns the given mesh config. It will never -// fire any events, since the config never changes. -func NewFixedWatcher(mesh *meshconfig.MeshConfig) Watcher { - return &internalWatcher{ - MeshConfig: mesh, - } -} - -// NewFileWatcher creates a new Watcher for changes to the given mesh config file. Returns an error -// if the given file does not exist or failed during parsing. -func NewFileWatcher(fileWatcher filewatcher.FileWatcher, filename string, multiWatch bool) (Watcher, error) { - meshConfigYaml, err := ReadMeshConfigData(filename) - if err != nil { - return nil, err - } - - meshConfig, err := ApplyMeshConfigDefaults(meshConfigYaml) - if err != nil { - return nil, err - } - - w := &internalWatcher{ - MeshConfig: meshConfig, - revMeshConfig: meshConfigYaml, - } - - // Watch the config file for changes and reload if it got modified - addFileWatcher(fileWatcher, filename, func() { - if multiWatch { - meshConfig, err := ReadMeshConfigData(filename) - if err != nil { - log.Warnf("failed to read mesh configuration, using default: %v", err) - return - } - w.HandleMeshConfigData(meshConfig) - return - } - // Reload the config file - meshConfig, err = ReadMeshConfig(filename) - if err != nil { - log.Warnf("failed to read mesh configuration, using default: %v", err) - return - } - w.HandleMeshConfig(meshConfig) - }) - return w, nil -} - -// Mesh returns the latest mesh config. -func (w *internalWatcher) Mesh() *meshconfig.MeshConfig { - return (*meshconfig.MeshConfig)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&w.MeshConfig)))) -} - -// AddMeshHandler registers a callback handler for changes to the mesh config. -func (w *internalWatcher) AddMeshHandler(h func()) { - w.mutex.Lock() - defer w.mutex.Unlock() - w.handlers = append(w.handlers, h) -} - -// HandleMeshConfigData keeps track of the standard mesh config. These are merged with the user -// mesh config, but takes precedence. -func (w *internalWatcher) HandleMeshConfigData(yaml string) { - w.mutex.Lock() - defer w.mutex.Unlock() - w.revMeshConfig = yaml - merged := w.merged() - w.handleMeshConfigInternal(merged) -} - -// HandleUserMeshConfig keeps track of user mesh config overrides. These are merged with the standard -// mesh config, which takes precedence. -func (w *internalWatcher) HandleUserMeshConfig(yaml string) { - w.mutex.Lock() - defer w.mutex.Unlock() - w.userMeshConfig = yaml - merged := w.merged() - w.handleMeshConfigInternal(merged) -} - -// merged returns the merged user and revision config. -func (w *internalWatcher) merged() *meshconfig.MeshConfig { - mc := DefaultMeshConfig() - if w.userMeshConfig != "" { - mc1, err := ApplyMeshConfig(w.userMeshConfig, mc) - if err != nil { - log.Errorf("user config invalid, ignoring it %v %s", err, w.userMeshConfig) - } else { - mc = mc1 - log.Infof("Applied user config: %s", PrettyFormatOfMeshConfig(mc)) - } - } - if w.revMeshConfig != "" { - mc1, err := ApplyMeshConfig(w.revMeshConfig, mc) - if err != nil { - log.Errorf("revision config invalid, ignoring it %v %s", err, w.userMeshConfig) - } else { - mc = mc1 - log.Infof("Applied revision mesh config: %s", PrettyFormatOfMeshConfig(mc)) - } - } - return mc -} - -// HandleMeshConfig calls all handlers for a given mesh configuration update. This must be called -// with a lock on w.Mutex, or updates may be applied out of order. -func (w *internalWatcher) HandleMeshConfig(meshConfig *meshconfig.MeshConfig) { - w.mutex.Lock() - defer w.mutex.Unlock() - w.handleMeshConfigInternal(meshConfig) -} - -// handleMeshConfigInternal behaves the same as HandleMeshConfig but must be called under a lock -func (w *internalWatcher) handleMeshConfigInternal(meshConfig *meshconfig.MeshConfig) { - var handlers []func() - - if !reflect.DeepEqual(meshConfig, w.MeshConfig) { - log.Infof("mesh configuration updated to: %s", PrettyFormatOfMeshConfig(meshConfig)) - if !reflect.DeepEqual(meshConfig.ConfigSources, w.MeshConfig.ConfigSources) { - log.Info("mesh configuration sources have changed") - // TODO Need to recreate or reload initConfigController() - } - - atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&w.MeshConfig)), unsafe.Pointer(meshConfig)) - handlers = append(handlers, w.handlers...) - } - - // TODO hack: the first handler added is the ConfigPush, other handlers affect what will be pushed, so reversing iteration - for i := len(handlers) - 1; i >= 0; i-- { - handlers[i]() - } -} - -// Add to the FileWatcher the provided file and execute the provided function -// on any change event for this file. -// Using a debouncing mechanism to avoid calling the callback multiple times -// per event. -func addFileWatcher(fileWatcher filewatcher.FileWatcher, file string, callback func()) { - _ = fileWatcher.Add(file) - go func() { - var timerC <-chan time.Time - for { - select { - case <-timerC: - timerC = nil - callback() - case <-fileWatcher.Events(file): - // Use a timer to debounce configuration updates - if timerC == nil { - timerC = time.After(100 * time.Millisecond) - } - } - } - }() -} - -func PrettyFormatOfMeshConfig(meshConfig *meshconfig.MeshConfig) string { - meshConfigDump, _ := protomarshal.ToJSONWithIndent(meshConfig, " ") - return meshConfigDump -} diff --git a/pkg/config/mesh/watcher_test.go b/pkg/config/mesh/watcher_test.go deleted file mode 100644 index 9b9bd6650..000000000 --- a/pkg/config/mesh/watcher_test.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh_test - -import ( - "os" - "path/filepath" - "testing" - "time" -) - -import ( - . "github.com/onsi/gomega" - "google.golang.org/protobuf/proto" - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/filewatcher" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -func TestNewWatcherWithBadInputShouldFail(t *testing.T) { - g := NewWithT(t) - _, err := mesh.NewFileWatcher(filewatcher.NewWatcher(), "", false) - g.Expect(err).ToNot(BeNil()) -} - -func TestWatcherShouldNotifyHandlers(t *testing.T) { - watcherShouldNotifyHandlers(t, false) -} - -func TestMultiWatcherShouldNotifyHandlers(t *testing.T) { - watcherShouldNotifyHandlers(t, true) -} - -func watcherShouldNotifyHandlers(t *testing.T, multi bool) { - path := newTempFile(t) - - m := mesh.DefaultMeshConfig() - writeMessage(t, path, m) - - w := newWatcher(t, path, multi) - assert.Equal(t, w.Mesh(), m) - - doneCh := make(chan struct{}, 1) - - var newM *meshconfig.MeshConfig - w.AddMeshHandler(func() { - newM = w.Mesh() - close(doneCh) - }) - - // Change the file to trigger the update. - m.IngressClass = "foo" - writeMessage(t, path, m) - - select { - case <-doneCh: - assert.Equal(t, newM, m) - assert.Equal(t, w.Mesh(), newM) - break - case <-time.After(time.Second * 5): - t.Fatal("timed out waiting for update") - } -} - -func newWatcher(t testing.TB, filename string, multi bool) mesh.Watcher { - t.Helper() - w, err := mesh.NewFileWatcher(filewatcher.NewWatcher(), filename, multi) - if err != nil { - t.Fatal(err) - } - return w -} - -func newTempFile(t testing.TB) string { - t.Helper() - - f, err := os.CreateTemp(t.TempDir(), t.Name()) - if err != nil { - t.Fatal(err) - } - t.Cleanup(func() { _ = f.Close() }) - - path, err := filepath.Abs(f.Name()) - if err != nil { - t.Fatal(err) - } - return path -} - -func writeMessage(t testing.TB, path string, msg proto.Message) { - t.Helper() - yml, err := protomarshal.ToYAML(msg) - if err != nil { - t.Fatal(err) - } - writeFile(t, path, yml) -} - -func writeFile(t testing.TB, path, content string) { - t.Helper() - if err := os.WriteFile(path, []byte(content), 0o666); err != nil { - t.Fatal(err) - } -} - -func removeSilent(path string) { - _ = os.RemoveAll(path) -} - -func BenchmarkGetMesh(b *testing.B) { - b.StopTimer() - - path := newTempFile(b) - defer removeSilent(path) - - m := mesh.DefaultMeshConfig() - writeMessage(b, path, m) - - w := newWatcher(b, path, false) - - b.StartTimer() - - handler := func(mc *meshconfig.MeshConfig) { - // Do nothing - } - - for i := 0; i < b.N; i++ { - handler(w.Mesh()) - } -} diff --git a/pkg/config/mesh/watcher_test_utils.go b/pkg/config/mesh/watcher_test_utils.go deleted file mode 100644 index 98106949b..000000000 --- a/pkg/config/mesh/watcher_test_utils.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "errors" - "time" -) - -import ( - meshconfig "istio.io/api/mesh/v1alpha1" -) - -// only used for testing, exposes a blocking Update method that allows test environments to trigger meshConfig updates -type TestWatcher struct { - internalWatcher - doneCh chan struct{} // used to implement a blocking Update method -} - -func NewTestWatcher(meshConfig *meshconfig.MeshConfig) *TestWatcher { - w := &TestWatcher{ - internalWatcher: internalWatcher{MeshConfig: meshConfig}, - } - w.doneCh = make(chan struct{}, 1) - w.AddMeshHandler(func() { - w.doneCh <- struct{}{} - }) - return w -} - -// blocks until watcher handlers trigger -func (t *TestWatcher) Update(meshConfig *meshconfig.MeshConfig, timeout time.Duration) error { - t.HandleMeshConfig(meshConfig) - select { - case <-t.doneCh: - return nil - case <-time.After(time.Second * timeout): - return errors.New("timed out waiting for mesh.Watcher handler to trigger") - } -} diff --git a/pixiu/pkg/config/mock/api_config.yml b/pkg/config/mock/api_config.yml similarity index 100% rename from pixiu/pkg/config/mock/api_config.yml rename to pkg/config/mock/api_config.yml diff --git a/pkg/config/model.go b/pkg/config/model.go deleted file mode 100644 index 56e6952a8..000000000 --- a/pkg/config/model.go +++ /dev/null @@ -1,366 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package config - -import ( - "bytes" - "encoding/json" - "fmt" - "reflect" - "time" -) - -import ( - gogojsonpb "github.com/gogo/protobuf/jsonpb" - gogoproto "github.com/gogo/protobuf/proto" - gogotypes "github.com/gogo/protobuf/types" - "github.com/golang/protobuf/jsonpb" // nolint: staticcheck - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" - "google.golang.org/protobuf/types/known/anypb" - "google.golang.org/protobuf/types/known/structpb" - "istio.io/api/label" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - kubetypes "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/util/gogoprotomarshal" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -// Meta is metadata attached to each configuration unit. -// The revision is optional, and if provided, identifies the -// last update operation on the object. -type Meta struct { - // GroupVersionKind is a short configuration name that matches the content message type - // (e.g. "route-rule") - GroupVersionKind GroupVersionKind `json:"type,omitempty"` - - // UID - UID string `json:"uid,omitempty"` - - // Name is a unique immutable identifier in a namespace - Name string `json:"name,omitempty"` - - // Namespace defines the space for names (optional for some types), - // applications may choose to use namespaces for a variety of purposes - // (security domains, fault domains, organizational domains) - Namespace string `json:"namespace,omitempty"` - - // Domain defines the suffix of the fully qualified name past the namespace. - // Domain is not a part of the unique key unlike name and namespace. - Domain string `json:"domain,omitempty"` - - // Map of string keys and values that can be used to organize and categorize - // (scope and select) objects. - Labels map[string]string `json:"labels,omitempty"` - - // Annotations is an unstructured key value map stored with a resource that may be - // set by external tools to store and retrieve arbitrary metadata. They are not - // queryable and should be preserved when modifying objects. - Annotations map[string]string `json:"annotations,omitempty"` - - // ResourceVersion is an opaque identifier for tracking updates to the config registry. - // The implementation may use a change index or a commit log for the revision. - // The config client should not make any assumptions about revisions and rely only on - // exact equality to implement optimistic concurrency of read-write operations. - // - // The lifetime of an object of a particular revision depends on the underlying data store. - // The data store may compactify old revisions in the interest of storage optimization. - // - // An empty revision carries a special meaning that the associated object has - // not been stored and assigned a revision. - ResourceVersion string `json:"resourceVersion,omitempty"` - - // CreationTimestamp records the creation time - CreationTimestamp time.Time `json:"creationTimestamp,omitempty"` - - // OwnerReferences allows specifying in-namespace owning objects. - OwnerReferences []metav1.OwnerReference `json:"ownerReferences,omitempty"` - - // A sequence number representing a specific generation of the desired state. Populated by the system. Read-only. - Generation int64 `json:"generation,omitempty"` -} - -// Config is a configuration unit consisting of the type of configuration, the -// key identifier that is unique per type, and the content represented as a -// protobuf message. -type Config struct { - Meta - - // Spec holds the configuration object as a gogo protobuf message - Spec Spec - - // Status holds long-running status. - Status Status -} - -func ObjectInRevision(o *Config, rev string) bool { - configEnv, f := o.Labels[label.IoIstioRev.Name] - if !f { - // This is a global object, and always included - return true - } - // Otherwise, only return true if revisions equal - return configEnv == rev -} - -// Spec defines the spec for the config. In order to use below helper methods, -// this must be one of: -// * golang/protobuf Message -// * gogo/protobuf Message -// * Able to marshal/unmarshal using json -type Spec interface{} - -func ToProto(s Spec) (*anypb.Any, error) { - // golang protobuf. Use protoreflect.ProtoMessage to distinguish from gogo - // golang/protobuf 1.4+ will have this interface. Older golang/protobuf are gogo compatible - // but also not used by Istio at all. - if pb, ok := s.(protoreflect.ProtoMessage); ok { - return anypb.New(pb) - } - - // gogo protobuf - if pb, ok := s.(gogoproto.Message); ok { - gogoany, err := gogotypes.MarshalAny(pb) - if err != nil { - return nil, err - } - return &anypb.Any{ - TypeUrl: gogoany.TypeUrl, - Value: gogoany.Value, - }, nil - } - - js, err := json.Marshal(s) - if err != nil { - return nil, err - } - pbs := &structpb.Struct{} - if err := jsonpb.Unmarshal(bytes.NewReader(js), pbs); err != nil { - return nil, err - } - return anypb.New(pbs) -} - -func ToMap(s Spec) (map[string]interface{}, error) { - js, err := ToJSON(s) - if err != nil { - return nil, err - } - - // Unmarshal from json bytes to go map - var data map[string]interface{} - err = json.Unmarshal(js, &data) - if err != nil { - return nil, err - } - - return data, nil -} - -func ToJSON(s Spec) ([]byte, error) { - // golang protobuf. Use protoreflect.ProtoMessage to distinguish from gogo - // golang/protobuf 1.4+ will have this interface. Older golang/protobuf are gogo compatible - // but also not used by Istio at all. - if _, ok := s.(protoreflect.ProtoMessage); ok { - if pb, ok := s.(proto.Message); ok { - b, err := protomarshal.Marshal(pb) - return b, err - } - } - - b := &bytes.Buffer{} - // gogo protobuf - if pb, ok := s.(gogoproto.Message); ok { - err := (&gogojsonpb.Marshaler{}).Marshal(b, pb) - return b.Bytes(), err - } - - return json.Marshal(s) -} - -type deepCopier interface { - DeepCopyInterface() interface{} -} - -func ApplyYAML(s Spec, yml string) error { - js, err := yaml.YAMLToJSON([]byte(yml)) - if err != nil { - return err - } - return ApplyJSON(s, string(js)) -} - -func ApplyJSONStrict(s Spec, js string) error { - // golang protobuf. Use protoreflect.ProtoMessage to distinguish from gogo - // golang/protobuf 1.4+ will have this interface. Older golang/protobuf are gogo compatible - // but also not used by Istio at all. - if _, ok := s.(protoreflect.ProtoMessage); ok { - if pb, ok := s.(proto.Message); ok { - err := protomarshal.ApplyJSONStrict(js, pb) - return err - } - } - - // gogo protobuf - if pb, ok := s.(gogoproto.Message); ok { - err := gogoprotomarshal.ApplyJSONStrict(js, pb) - return err - } - - d := json.NewDecoder(bytes.NewReader([]byte(js))) - d.DisallowUnknownFields() - return d.Decode(&s) -} - -func ApplyJSON(s Spec, js string) error { - // golang protobuf. Use protoreflect.ProtoMessage to distinguish from gogo - // golang/protobuf 1.4+ will have this interface. Older golang/protobuf are gogo compatible - // but also not used by Istio at all. - if _, ok := s.(protoreflect.ProtoMessage); ok { - if pb, ok := s.(proto.Message); ok { - err := protomarshal.ApplyJSON(js, pb) - return err - } - } - - // gogo protobuf - if pb, ok := s.(gogoproto.Message); ok { - err := gogoprotomarshal.ApplyJSON(js, pb) - return err - } - - return json.Unmarshal([]byte(js), &s) -} - -func DeepCopy(s interface{}) interface{} { - if s == nil { - return nil - } - // If deep copy is defined, use that - if dc, ok := s.(deepCopier); ok { - return dc.DeepCopyInterface() - } - - // golang protobuf. Use protoreflect.ProtoMessage to distinguish from gogo - // golang/protobuf 1.4+ will have this interface. Older golang/protobuf are gogo compatible - // but also not used by Istio at all. - if _, ok := s.(protoreflect.ProtoMessage); ok { - if pb, ok := s.(proto.Message); ok { - return proto.Clone(pb) - } - } - - // gogo protobuf - if pb, ok := s.(gogoproto.Message); ok { - return gogoproto.Clone(pb) - } - - // If we don't have a deep copy method, we will have to do some reflection magic. Its not ideal, - // but all Istio types have an efficient deep copy. - js, err := json.Marshal(s) - if err != nil { - return nil - } - - data := reflect.New(reflect.TypeOf(s)).Interface() - if err := json.Unmarshal(js, data); err != nil { - return nil - } - data = reflect.ValueOf(data).Elem().Interface() - return data -} - -type Status interface{} - -// Key function for the configuration objects -func Key(grp, ver, typ, name, namespace string) string { - return grp + "/" + ver + "/" + typ + "/" + namespace + "/" + name // Format: %s/%s/%s/%s/%s -} - -// Key is the unique identifier for a configuration object -func (meta *Meta) Key() string { - return Key( - meta.GroupVersionKind.Group, meta.GroupVersionKind.Version, meta.GroupVersionKind.Kind, - meta.Name, meta.Namespace) -} - -func (c Config) DeepCopy() Config { - var clone Config - clone.Meta = c.Meta - if c.Labels != nil { - clone.Labels = make(map[string]string) - for k, v := range c.Labels { - clone.Labels[k] = v - } - } - if c.Annotations != nil { - clone.Annotations = make(map[string]string) - for k, v := range c.Annotations { - clone.Annotations[k] = v - } - } - clone.Spec = DeepCopy(c.Spec) - if c.Status != nil { - clone.Status = DeepCopy(c.Status) - } - return clone -} - -var _ fmt.Stringer = GroupVersionKind{} - -type GroupVersionKind struct { - Group string `json:"group"` - Version string `json:"version"` - Kind string `json:"kind"` -} - -func (g GroupVersionKind) String() string { - return g.CanonicalGroup() + "/" + g.Version + "/" + g.Kind -} - -// GroupVersion returns the group/version similar to what would be found in the apiVersion field of a Kubernetes resource. -func (g GroupVersionKind) GroupVersion() string { - if g.Group == "" { - return g.Version - } - return g.Group + "/" + g.Version -} - -// Kubernetes returns the same GVK, using the Kubernetes object type -func (g GroupVersionKind) Kubernetes() schema.GroupVersionKind { - return schema.GroupVersionKind{ - Group: g.Group, - Version: g.Version, - Kind: g.Kind, - } -} - -// CanonicalGroup returns the group with defaulting applied. This means an empty group will -// be treated as "core", following Kubernetes API standards -func (g GroupVersionKind) CanonicalGroup() string { - if g.Group != "" { - return g.Group - } - return "core" -} - -// PatchFunc provides the cached config as a base for modification. Only diff the between the cfg -// parameter and the returned Config will be applied. -type PatchFunc func(cfg Config) (Config, kubetypes.PatchType) diff --git a/pkg/config/model_test.go b/pkg/config/model_test.go deleted file mode 100644 index 9b18f8860..000000000 --- a/pkg/config/model_test.go +++ /dev/null @@ -1,359 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package config - -import ( - "fmt" - "reflect" - "testing" - "time" -) - -import ( - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/testing/protocmp" - networking "istio.io/api/networking/v1alpha3" - corev1 "k8s.io/api/core/v1" - "sigs.k8s.io/gateway-api/apis/v1alpha2" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/config" -) - -func TestDeepCopy(t *testing.T) { - cfg := Config{ - Meta: Meta{ - Name: "name1", - Namespace: "zzz", - CreationTimestamp: time.Now(), - Labels: map[string]string{"app": "test-app"}, - Annotations: map[string]string{"policy.istio.io/checkRetries": "3"}, - }, - Spec: &networking.Gateway{}, - } - - copied := cfg.DeepCopy() - - if diff := cmp.Diff(copied, cfg, protocmp.Transform()); diff != "" { - t.Fatalf("cloned config is not identical: %v", diff) - } - - copied.Labels["app"] = "cloned-app" - copied.Annotations["policy.istio.io/checkRetries"] = "0" - if cfg.Labels["app"] == copied.Labels["app"] || - cfg.Annotations["policy.istio.io/checkRetries"] == copied.Annotations["policy.istio.io/checkRetries"] { - t.Fatalf("Did not deep copy labels and annotations") - } - - // change the copied gateway to see if the original config is not effected - copiedGateway := copied.Spec.(*networking.Gateway) - copiedGateway.Selector = map[string]string{"app": "test"} - - gateway := cfg.Spec.(*networking.Gateway) - if gateway.Selector != nil { - t.Errorf("Original gateway is mutated") - } -} - -type TestStruct struct { - Name string `json:"name"` -} - -func TestDeepCopyTypes(t *testing.T) { - cases := []struct { - input Spec - modify func(c Spec) Spec - option cmp.Option - }{ - // Istio type - { - &networking.VirtualService{Gateways: []string{"foo"}}, - func(c Spec) Spec { - c.(*networking.VirtualService).Gateways = []string{"bar"} - return c - }, - protocmp.Transform(), - }, - // Kubernetes type - { - &corev1.PodSpec{ServiceAccountName: "foobar"}, - func(c Spec) Spec { - c.(*corev1.PodSpec).ServiceAccountName = "bar" - return c - }, - nil, - }, - // gateway-api type - { - &v1alpha2.GatewayClassSpec{ControllerName: "foo"}, - func(c Spec) Spec { - c.(*v1alpha2.GatewayClassSpec).ControllerName = "bar" - return c - }, - nil, - }, - // mock type - { - &config.MockConfig{Key: "foobar"}, - func(c Spec) Spec { - c.(*config.MockConfig).Key = "bar" - return c - }, - protocmp.Transform(), - }, - // XDS type, to test golang/proto - { - &cluster.Cluster{Name: "foobar"}, - func(c Spec) Spec { - c.(*cluster.Cluster).Name = "bar" - return c - }, - protocmp.Transform(), - }, - // Random struct pointer - { - &TestStruct{Name: "foobar"}, - func(c Spec) Spec { - c.(*TestStruct).Name = "bar" - return c - }, - nil, - }, - // Random struct - { - TestStruct{Name: "foobar"}, - func(c Spec) Spec { - x := c.(TestStruct) - x.Name = "bar" - return x - }, - nil, - }, - // Slice - { - []string{"foo"}, - func(c Spec) Spec { - x := c.([]string) - x[0] = "a" - return x - }, - nil, - }, - // Array - { - [1]string{"foo"}, - func(c Spec) Spec { - x := c.([1]string) - x[0] = "a" - return x - }, - nil, - }, - // Map - { - map[string]string{"a": "b"}, - func(c Spec) Spec { - x := c.(map[string]string) - x["a"] = "x" - return x - }, - nil, - }, - } - for _, tt := range cases { - t.Run(fmt.Sprintf("%T", tt.input), func(t *testing.T) { - cpy := DeepCopy(tt.input) - if diff := cmp.Diff(tt.input, cpy, tt.option); diff != "" { - t.Fatalf("Type was %T now is %T. Diff: %v", tt.input, cpy, diff) - } - changed := tt.modify(tt.input) - if cmp.Equal(cpy, changed, tt.option) { - t.Fatalf("deep copy allowed modification") - } - }) - } -} - -func TestApplyJSON(t *testing.T) { - cases := []struct { - input Spec - json string - output Spec - option cmp.Option - }{ - // Istio type - { - input: &networking.VirtualService{}, - json: `{"gateways":["foobar"],"fake-field":1}`, - output: &networking.VirtualService{Gateways: []string{"foobar"}}, - }, - // Kubernetes type - { - input: &corev1.PodSpec{}, - json: `{"serviceAccountName":"foobar","fake-field":1}`, - output: &corev1.PodSpec{ServiceAccountName: "foobar"}, - }, - // gateway-api type - { - input: &v1alpha2.GatewayClassSpec{}, - json: `{"controllerName":"foobar","fake-field":1}`, - output: &v1alpha2.GatewayClassSpec{ControllerName: "foobar"}, - }, - // mock type - { - input: &config.MockConfig{}, - json: `{"key":"foobar","fake-field":1}`, - output: &config.MockConfig{Key: "foobar"}, - }, - // XDS type, to test golang/proto - { - input: &cluster.Cluster{}, - json: `{"name":"foobar","fake-field":1}`, - output: &cluster.Cluster{Name: "foobar"}, - option: protocmp.Transform(), - }, - // Random struct - { - input: &TestStruct{}, - json: `{"name":"foobar","fake-field":1}`, - output: &TestStruct{Name: "foobar"}, - }, - } - for _, tt := range cases { - t.Run(fmt.Sprintf("%T", tt.input), func(t *testing.T) { - if err := ApplyJSON(tt.input, tt.json); err != nil { - t.Fatal(err) - } - if diff := cmp.Diff(tt.input, tt.output, protocmp.Transform()); diff != "" { - t.Fatalf("Diff: %v", diff) - } - if err := ApplyJSONStrict(tt.input, tt.json); err == nil { - t.Fatalf("expected error from non existent field in strict mode") - } - }) - } -} - -func TestToJSON(t *testing.T) { - cases := []struct { - input Spec - json string - }{ - // Istio type - { - input: &networking.VirtualService{Gateways: []string{"foobar"}}, - json: `{"gateways":["foobar"]}`, - }, - // Kubernetes type - { - input: &corev1.PodSpec{ServiceAccountName: "foobar"}, - json: `{"serviceAccountName":"foobar"}`, - }, - // gateway-api type - { - input: &v1alpha2.GatewayClassSpec{ControllerName: "foobar"}, - json: `{"controllerName":"foobar"}`, - }, - // mock type - { - input: &config.MockConfig{Key: "foobar"}, - json: `{"key":"foobar"}`, - }, - // XDS type, to test golang/proto - { - input: &cluster.Cluster{Name: "foobar"}, - json: `{"name":"foobar"}`, - }, - // Random struct - { - input: &TestStruct{Name: "foobar"}, - json: `{"name":"foobar"}`, - }, - } - for _, tt := range cases { - t.Run(fmt.Sprintf("%T", tt.input), func(t *testing.T) { - jb, err := ToJSON(tt.input) - if err != nil { - t.Fatal(err) - } - if string(jb) != tt.json { - t.Fatalf("got %v want %v", string(jb), tt.json) - } - }) - } -} - -func TestToMap(t *testing.T) { - cases := []struct { - input Spec - mp map[string]interface{} - }{ - // Istio type - { - input: &networking.VirtualService{Gateways: []string{"foobar"}}, - mp: map[string]interface{}{ - "gateways": []interface{}{"foobar"}, - }, - }, - // Kubernetes type - { - input: &corev1.PodSpec{ServiceAccountName: "foobar"}, - mp: map[string]interface{}{ - "serviceAccountName": "foobar", - }, - }, - // gateway-api type - { - input: &v1alpha2.GatewayClassSpec{ControllerName: "foobar"}, - mp: map[string]interface{}{ - "controllerName": "foobar", - }, - }, - // mock type - { - input: &config.MockConfig{Key: "foobar"}, - mp: map[string]interface{}{ - "key": "foobar", - }, - }, - // XDS type, to test golang/proto - { - input: &cluster.Cluster{Name: "foobar"}, - mp: map[string]interface{}{ - "name": "foobar", - }, - }, - // Random struct - { - input: &TestStruct{Name: "foobar"}, - mp: map[string]interface{}{ - "name": "foobar", - }, - }, - } - for _, tt := range cases { - t.Run(fmt.Sprintf("%T", tt.input), func(t *testing.T) { - got, err := ToMap(tt.input) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(got, tt.mp) { - t.Fatalf("got %+v want %+v", got, tt.mp) - } - }) - } -} diff --git a/pkg/config/protocol/instance.go b/pkg/config/protocol/instance.go deleted file mode 100644 index 5f710e92d..000000000 --- a/pkg/config/protocol/instance.go +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package protocol - -import ( - "strings" -) - -// Instance defines network protocols for ports -type Instance string - -func (i Instance) String() string { - return string(i) -} - -const ( - // GRPC declares that the port carries gRPC traffic. - GRPC Instance = "GRPC" - // GRPCWeb declares that the port carries gRPC traffic. - GRPCWeb Instance = "GRPC-Web" - // HTTP declares that the port carries HTTP/1.1 traffic. - // Note that HTTP/1.0 or earlier may not be supported by the proxy. - HTTP Instance = "HTTP" - // HTTP_PROXY declares that the port is a generic outbound proxy port. - // Note that this is currently applicable only for defining sidecar egress listeners. - // nolint - HTTP_PROXY Instance = "HTTP_PROXY" - // HTTP2 declares that the port carries HTTP/2 traffic. - HTTP2 Instance = "HTTP2" - // HTTPS declares that the port carries HTTPS traffic. - HTTPS Instance = "HTTPS" - // TCP declares the the port uses TCP. - // This is the default protocol for a service port. - TCP Instance = "TCP" - // TLS declares that the port carries TLS traffic. - // TLS traffic is assumed to contain SNI as part of the handshake. - TLS Instance = "TLS" - // UDP declares that the port uses UDP. - // Note that UDP protocol is not currently supported by the proxy. - UDP Instance = "UDP" - // Mongo declares that the port carries MongoDB traffic. - Mongo Instance = "Mongo" - // Redis declares that the port carries Redis traffic. - Redis Instance = "Redis" - // MySQL declares that the port carries MySQL traffic. - MySQL Instance = "MySQL" - // Unsupported - value to signify that the protocol is unsupported. - Unsupported Instance = "UnsupportedProtocol" -) - -// Parse from string ignoring case -func Parse(s string) Instance { - switch strings.ToLower(s) { - case "tcp": - return TCP - case "udp": - return UDP - case "grpc": - return GRPC - case "grpc-web": - return GRPCWeb - case "http": - return HTTP - case "http_proxy": - return HTTP_PROXY - case "http2": - return HTTP2 - case "https": - return HTTPS - case "tls": - return TLS - case "mongo": - return Mongo - case "redis": - return Redis - case "mysql": - return MySQL - } - - return Unsupported -} - -// IsHTTP2 is true for protocols that use HTTP/2 as transport protocol -func (i Instance) IsHTTP2() bool { - switch i { - case HTTP2, GRPC, GRPCWeb: - return true - default: - return false - } -} - -// IsHTTP is true for protocols that use HTTP as transport protocol -func (i Instance) IsHTTP() bool { - switch i { - case HTTP, HTTP2, HTTP_PROXY, GRPC, GRPCWeb: - return true - default: - return false - } -} - -// IsTCP is true for protocols that use TCP as transport protocol -func (i Instance) IsTCP() bool { - switch i { - case TCP, HTTPS, TLS, Mongo, Redis, MySQL: - return true - default: - return false - } -} - -// IsTLS is true for protocols on top of TLS (e.g. HTTPS) -func (i Instance) IsTLS() bool { - switch i { - case HTTPS, TLS: - return true - default: - return false - } -} - -// IsHTTPS is true if protocol is HTTPS -func (i Instance) IsHTTPS() bool { - switch i { - case HTTPS: - return true - default: - return false - } -} - -// IsGRPC is true for GRPC protocols. -func (i Instance) IsGRPC() bool { - switch i { - case GRPC, GRPCWeb: - return true - default: - return false - } -} - -func (i Instance) IsUnsupported() bool { - return i == Unsupported -} - -// AfterTLSTermination returns the protocol that will be used if TLS is terminated on the current protocol. -func (i Instance) AfterTLSTermination() Instance { - switch i { - case HTTPS: - return HTTP - case TLS: - return TCP - default: - return i - } -} diff --git a/pkg/config/protocol/instance_test.go b/pkg/config/protocol/instance_test.go deleted file mode 100644 index 2d590f9af..000000000 --- a/pkg/config/protocol/instance_test.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package protocol_test - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" -) - -func TestIsHTTP(t *testing.T) { - if protocol.UDP.IsHTTP() { - t.Errorf("UDP is not HTTP protocol") - } - if !protocol.GRPC.IsHTTP() { - t.Errorf("gRPC is HTTP protocol") - } -} - -func TestParse(t *testing.T) { - testPairs := []struct { - name string - out protocol.Instance - }{ - {"tcp", protocol.TCP}, - {"http", protocol.HTTP}, - {"HTTP", protocol.HTTP}, - {"Http", protocol.HTTP}, - {"http_proxy", protocol.HTTP_PROXY}, - {"Http_Proxy", protocol.HTTP_PROXY}, - {"HTTP_PROXY", protocol.HTTP_PROXY}, - {"https", protocol.HTTPS}, - {"http2", protocol.HTTP2}, - {"grpc", protocol.GRPC}, - {"grpc-web", protocol.GRPCWeb}, - {"gRPC-Web", protocol.GRPCWeb}, - {"grpc-Web", protocol.GRPCWeb}, - {"udp", protocol.UDP}, - {"Mongo", protocol.Mongo}, - {"mongo", protocol.Mongo}, - {"MONGO", protocol.Mongo}, - {"Redis", protocol.Redis}, - {"redis", protocol.Redis}, - {"REDIS", protocol.Redis}, - {"Mysql", protocol.MySQL}, - {"mysql", protocol.MySQL}, - {"MYSQL", protocol.MySQL}, - {"MySQL", protocol.MySQL}, - {"", protocol.Unsupported}, - {"SMTP", protocol.Unsupported}, - } - - for _, testPair := range testPairs { - testName := testPair.name - if testName == "" { - testName = "[empty]" - } - t.Run(testName, func(t *testing.T) { - out := protocol.Parse(testPair.name) - if out != testPair.out { - t.Fatalf("Parse(%q) => %q, want %q", testPair.name, out, testPair.out) - } - }) - } -} diff --git a/pkg/config/resource/instance.go b/pkg/config/resource/instance.go deleted file mode 100644 index c91c95137..000000000 --- a/pkg/config/resource/instance.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package resource contains core abstract types for representing configuration resources. -package resource - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" -) - -// Instance is the abstract representation of a versioned config resource in Istio. -type Instance struct { - Metadata Metadata - Message config.Spec - Origin Origin -} - -// IsEmpty returns true if the resource Instance.Message is nil. -func (r *Instance) IsEmpty() bool { - return r.Message == nil -} - -// Clone returns a deep-copy of this entry. Warning, this is expensive! -func (r *Instance) Clone() *Instance { - result := &Instance{} - if r.Message != nil { - result.Message = config.DeepCopy(r.Message) - } - result.Metadata = r.Metadata.Clone() - return result -} diff --git a/pkg/config/resource/instance_test.go b/pkg/config/resource/instance_test.go deleted file mode 100644 index 08d805278..000000000 --- a/pkg/config/resource/instance_test.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -import ( - "testing" -) - -import ( - "github.com/gogo/protobuf/types" - . "github.com/onsi/gomega" -) - -func TestInstance_IsEmpty_False(t *testing.T) { - g := NewWithT(t) - - e := Instance{ - Message: &types.Empty{}, - } - - g.Expect(e.IsEmpty()).To(BeFalse()) -} - -func TestInstance_IsEmpty_True(t *testing.T) { - g := NewWithT(t) - e := Instance{} - - g.Expect(e.IsEmpty()).To(BeTrue()) -} - -func TestInstance_Clone_Empty(t *testing.T) { - g := NewWithT(t) - e := &Instance{} - - c := e.Clone() - g.Expect(c).To(Equal(e)) -} - -func TestInstance_Clone_NonEmpty(t *testing.T) { - g := NewWithT(t) - - e := &Instance{ - Message: &types.Empty{}, - } - - c := e.Clone() - g.Expect(c).To(Equal(e)) -} diff --git a/pkg/config/resource/metadata.go b/pkg/config/resource/metadata.go deleted file mode 100644 index 9631093a3..000000000 --- a/pkg/config/resource/metadata.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -import ( - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/resource" -) - -// Metadata about a resource. -type Metadata struct { - Schema resource.Schema - FullName FullName - CreateTime time.Time - Version Version - Generation int64 - Labels StringMap - Annotations StringMap -} - -// Clone Metadata. Warning, this is expensive! -func (m *Metadata) Clone() Metadata { - result := *m - result.Annotations = m.Annotations.Clone() - result.Labels = m.Labels.Clone() - return result -} diff --git a/pkg/config/resource/metadata_test.go b/pkg/config/resource/metadata_test.go deleted file mode 100644 index a7f63a248..000000000 --- a/pkg/config/resource/metadata_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -import ( - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -func TestMetadata_Clone_NilMaps(t *testing.T) { - g := NewWithT(t) - - m := Metadata{ - FullName: NewFullName("ns1", "rs1"), - Version: Version("v1"), - } - - c := m.Clone() - g.Expect(m).To(Equal(c)) -} - -func TestMetadata_Clone_NonNilMaps(t *testing.T) { - g := NewWithT(t) - - m := Metadata{ - FullName: NewFullName("ns1", "rs1"), - Version: Version("v1"), - Annotations: map[string]string{"foo": "bar"}, - Labels: map[string]string{"l1": "l2"}, - } - - c := m.Clone() - g.Expect(m).To(Equal(c)) -} diff --git a/pkg/config/resource/name.go b/pkg/config/resource/name.go deleted file mode 100644 index f0ebd774e..000000000 --- a/pkg/config/resource/name.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -import ( - "fmt" - "strings" -) - -// Namespace containing the resource. -type Namespace string - -func (n Namespace) String() string { - return string(n) -} - -// LocalName that uniquely identifies the resource within the Namespace. -type LocalName string - -func (n LocalName) String() string { - return string(n) -} - -// FullName is a name that uniquely identifies a resource within the mesh. -type FullName struct { - Namespace Namespace - Name LocalName -} - -// String interface implementation. -func (n FullName) String() string { - if len(n.Namespace) == 0 { - return string(n.Name) - } - return string(n.Namespace) + "/" + string(n.Name) -} - -// NewShortOrFullName tries to parse the given name to resource.Name. If the name does not include namespace information, -// the defaultNamespace is used. -func NewShortOrFullName(defaultNamespace Namespace, name string) FullName { - parts := strings.SplitN(name, "/", 2) - if len(parts) == 1 { - return FullName{ - Namespace: defaultNamespace, - Name: LocalName(parts[0]), - } - } - - return FullName{ - Namespace: Namespace(parts[0]), - Name: LocalName(parts[1]), - } -} - -// Validate that the Name and Namespace are set. -func (n FullName) Validate() error { - if len(n.Name) == 0 { - return fmt.Errorf("invalid name '%s': name must not be empty", n.String()) - } - return nil -} - -// NewFullName creates a new FullName from the given Namespace and Name. -func NewFullName(ns Namespace, n LocalName) FullName { - return FullName{ - Namespace: ns, - Name: n, - } -} - -// ParseFullName parses the given name string that was serialized via FullName.String() -func ParseFullName(name string) (FullName, error) { - return ParseFullNameWithDefaultNamespace("", name) -} - -// ParseFullName parses the given name string using defaultNamespace if no namespace is found. -func ParseFullNameWithDefaultNamespace(defaultNamespace Namespace, name string) (FullName, error) { - out := NewShortOrFullName(defaultNamespace, name) - - if err := out.Validate(); err != nil { - return FullName{}, fmt.Errorf("failed parsing name '%v': %v", name, err) - } - return out, nil -} diff --git a/pkg/config/resource/name_test.go b/pkg/config/resource/name_test.go deleted file mode 100644 index 8380fa2bd..000000000 --- a/pkg/config/resource/name_test.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -import ( - "testing" -) - -func TestFullNameString(t *testing.T) { - n := NewFullName("ns1", "l1") - if n.String() != "ns1/l1" { - t.Fatalf("unexpected name string: %v", n.String()) - } -} - -func TestFullNameString_NoName(t *testing.T) { - n := NewFullName("ns1", "") - if n.String() != "ns1/" { - t.Fatalf("unexpected name string: %v", n.String()) - } -} - -func TestFullNameString_NoNamespace(t *testing.T) { - n := NewFullName("", "l1") - if n.String() != "l1" { - t.Fatalf("unexpected name string: %v", n.String()) - } -} - -func TestParseFullName_Segments(t *testing.T) { - steps := []struct { - description string - name string - want string - err string - valid bool - }{ - { - description: "namespace with emapty name", - name: "testNamespace/", - want: "", - err: "failed parsing name 'testNamespace/': invalid name 'testNamespace/': name must not be empty", - valid: false, - }, - { - description: "name with empty namespace", - name: "/testName", - want: "testName", - err: "", - valid: true, - }, - { - description: "all empty", - name: "/", - want: "", - err: "failed parsing name '/': invalid name '': name must not be empty", - valid: false, - }, - { - description: "multiple segments", // Only the first segment is treated specially - name: "testName//someotherStuff", - want: "testName//someotherStuff", - err: "", - valid: true, - }, - { - description: "valid name with namespace", - name: "testNamespace/testName", - want: "testNamespace/testName", - valid: true, - }, - } - for _, s := range steps { - t.Run(s.description, func(tt *testing.T) { - n, err := ParseFullName(s.name) - if err == nil && n.String() != s.want { - tt.Fatalf("unexpected name: %v", n) - } - if s.valid { - if err != nil { - tt.Fatalf("expected no err but got: %v", err) - } - return - } - if err == nil { - tt.Fatalf("expected err but got: %v", err) - } - if err.Error() != s.err { - tt.Fatalf("expected err \"%s\" but got: %v", s.err, err.Error()) - } - }) - } -} - -func TestNewShortOrFullName_HasNamespace(t *testing.T) { - actual := NewShortOrFullName("ns1", "ns2/l1") - expected := NewFullName("ns2", "l1") - if actual != expected { - t.Fatalf("Expected %v, got %v", expected, actual) - } -} - -func TestNewShortOrFullName_NoNamespace(t *testing.T) { - actual := NewShortOrFullName("ns1", "l1") - expected := NewFullName("ns1", "l1") - if actual != expected { - t.Fatalf("Expected %v, got %v", expected, actual) - } -} diff --git a/pkg/config/resource/origin.go b/pkg/config/resource/origin.go deleted file mode 100644 index eefa452c7..000000000 --- a/pkg/config/resource/origin.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -// Origin of a resource. This is source-implementation dependent. -type Origin interface { - FriendlyName() string - Namespace() Namespace - Reference() Reference - - // FieldMap returns the flat map containing paths of the fields in the resource as keys, - // and their corresponding line numbers as values - FieldMap() map[string]int - Comparator() string -} - -// Reference provides more information about an Origin. This is also source-implementation dependant. -type Reference interface { - String() string -} diff --git a/pkg/config/resource/serialization.go b/pkg/config/resource/serialization.go deleted file mode 100644 index 461e8ef05..000000000 --- a/pkg/config/resource/serialization.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/resource" -) - -// PilotConfigToInstance convert from config.Config, which has no associated proto, to MCP Resource proto. -func PilotConfigToInstance(c *config.Config, schema resource.Schema) *Instance { - return &Instance{ - Metadata: Metadata{ - Schema: schema, - FullName: FullName{Namespace(c.Namespace), LocalName(c.Name)}, - CreateTime: c.CreationTimestamp, - Version: Version(c.ResourceVersion), - Labels: c.Labels, - Annotations: c.Annotations, - }, - Message: c.Spec, - } -} diff --git a/pkg/config/resource/stringmap.go b/pkg/config/resource/stringmap.go deleted file mode 100644 index 7fc75d219..000000000 --- a/pkg/config/resource/stringmap.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -// StringMap is used to store labels and annotations. -type StringMap map[string]string - -// Clone the StringMap -func (s StringMap) Clone() StringMap { - if s == nil { - return nil - } - - m := make(map[string]string, len(s)) - for k, v := range s { - m[k] = v - } - - return m -} - -// CloneOrCreate clones a StringMap. It creates the map if it doesn't exist. -func (s StringMap) CloneOrCreate() StringMap { - m := s.Clone() - if m == nil { - m = make(map[string]string) - } - return m -} - -// Remove the given name from the string map -func (s StringMap) Delete(name string) { - if s != nil { - delete(s, name) - } -} diff --git a/pkg/config/resource/stringmap_test.go b/pkg/config/resource/stringmap_test.go deleted file mode 100644 index 23dfb0622..000000000 --- a/pkg/config/resource/stringmap_test.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -import ( - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -func TestStringMap_Clone_Nil(t *testing.T) { - g := NewWithT(t) - - var s StringMap - - c := s.Clone() - g.Expect(c).To(BeNil()) -} - -func TestStringMap_Clone_NonNil(t *testing.T) { - g := NewWithT(t) - - var s StringMap = map[string]string{ - "foo": "bar", - } - - c := s.Clone() - g.Expect(c).NotTo(BeNil()) - g.Expect(c).To(HaveLen(1)) - g.Expect(c["foo"]).To(Equal("bar")) -} - -func TestStringMap_CloneOrCreate_Nil(t *testing.T) { - g := NewWithT(t) - - var s StringMap - - c := s.CloneOrCreate() - g.Expect(c).NotTo(BeNil()) - g.Expect(c).To(HaveLen(0)) -} - -func TestStringMap_CloneOrCreate_NonNil(t *testing.T) { - g := NewWithT(t) - - var s StringMap = map[string]string{ - "foo": "bar", - } - - c := s.CloneOrCreate() - g.Expect(c).NotTo(BeNil()) - g.Expect(c).To(HaveLen(1)) - g.Expect(c["foo"]).To(Equal("bar")) -} - -func TestStringMap_Delete_NonNil(t *testing.T) { - g := NewWithT(t) - - var s StringMap = map[string]string{ - "foo": "bar", - } - - s.Delete("foo") - g.Expect(s).NotTo(BeNil()) - g.Expect(s).To(HaveLen(0)) -} - -func TestStringMap_Delete_Nil(t *testing.T) { - g := NewWithT(t) - - var s StringMap - - s.Delete("foo") - g.Expect(s).To(BeNil()) -} diff --git a/pkg/config/resource/version.go b/pkg/config/resource/version.go deleted file mode 100644 index 76574fa1b..000000000 --- a/pkg/config/resource/version.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -// Version is the version identifier of a resource. -type Version string diff --git a/pkg/config/schema/ast/ast.go b/pkg/config/schema/ast/ast.go deleted file mode 100644 index 8e794e31f..000000000 --- a/pkg/config/schema/ast/ast.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ast - -import ( - "encoding/json" -) - -import ( - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/validation" - "github.com/apache/dubbo-go-pixiu/pkg/util/strcase" -) - -// Metadata is the top-level container. -type Metadata struct { - Collections []*Collection `json:"collections"` - Resources []*Resource `json:"resources"` -} - -var _ json.Unmarshaler = &Metadata{} - -// Collection metadata. Describes basic structure of collections. -type Collection struct { - Name string `json:"name"` - VariableName string `json:"variableName"` - Description string `json:"description"` - Group string `json:"group"` - Kind string `json:"kind"` - Pilot bool `json:"pilot"` - Builtin bool `json:"builtin"` - Deprecated bool `json:"deprecated"` -} - -// Resource metadata for resources contained within a collection. -type Resource struct { - Group string `json:"group"` - Version string `json:"version"` - Kind string `json:"kind"` - Plural string `json:"plural"` - ClusterScoped bool `json:"clusterScoped"` - Proto string `json:"proto"` - ProtoPackage string `json:"protoPackage"` - StatusProto string `json:"statusProto"` - StatusProtoPackage string `json:"statusProtoPackage"` - Validate string `json:"validate"` - Description string `json:"description"` -} - -// FindResourceForGroupKind looks up a resource with the given group and kind. Returns nil if not found. -func (m *Metadata) FindResourceForGroupKind(group, kind string) *Resource { - for _, r := range m.Resources { - if r.Group == group && r.Kind == kind { - return r - } - } - return nil -} - -// UnmarshalJSON implements json.Unmarshaler -func (m *Metadata) UnmarshalJSON(data []byte) error { - var in struct { - Collections []*Collection `json:"collections"` - Resources []*Resource `json:"resources"` - } - - if err := json.Unmarshal(data, &in); err != nil { - return err - } - - m.Collections = in.Collections - m.Resources = in.Resources - - // Process resources. - for i, r := range m.Resources { - if r.Validate == "" { - validateFn := "Validate" + asResourceVariableName(r.Kind) - if !validation.IsValidateFunc(validateFn) { - validateFn = "EmptyValidate" - } - m.Resources[i].Validate = validateFn - } - } - - // Process collections. - for i, c := range m.Collections { - // If no variable name was specified, use default. - if c.VariableName == "" { - m.Collections[i].VariableName = asCollectionVariableName(c.Name) - } - - if c.Description == "" { - m.Collections[i].Description = "describes the collection " + c.Name - } - } - - return nil -} - -// Parse and return a yaml representation of Metadata -func Parse(yamlText string) (*Metadata, error) { - var s Metadata - err := yaml.Unmarshal([]byte(yamlText), &s) - if err != nil { - return nil, err - } - return &s, nil -} - -func asResourceVariableName(n string) string { - return strcase.CamelCase(n) -} - -func asCollectionVariableName(n string) string { - n = strcase.CamelCaseWithSeparator(n, "/") - n = strcase.CamelCaseWithSeparator(n, ".") - return n -} diff --git a/pkg/config/schema/ast/ast_test.go b/pkg/config/schema/ast/ast_test.go deleted file mode 100644 index 8c77a7079..000000000 --- a/pkg/config/schema/ast/ast_test.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ast - -import ( - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -func TestParse(t *testing.T) { - cases := []struct { - input string - expected *Metadata - }{ - { - input: ``, - expected: &Metadata{}, - }, - { - input: ` -collections: - - name: "istio/meshconfig" - kind: "MeshConfig" - group: "" - -resources: - - kind: "VirtualService" - group: "networking.istio.io" - version: "v1alpha3" - proto: "istio.networking.v1alpha3.VirtualService" - protoPackage: "istio.io/api/networking/v1alpha3" -`, - expected: &Metadata{ - Collections: []*Collection{ - { - Name: "istio/meshconfig", - VariableName: "IstioMeshconfig", - Description: "describes the collection istio/meshconfig", - Kind: "MeshConfig", - Group: "", - }, - }, - Resources: []*Resource{ - { - Kind: "VirtualService", - Group: "networking.istio.io", - Version: "v1alpha3", - Proto: "istio.networking.v1alpha3.VirtualService", - ProtoPackage: "istio.io/api/networking/v1alpha3", - Validate: "ValidateVirtualService", - }, - }, - }, - }, - } - - for _, c := range cases { - t.Run("", func(t *testing.T) { - g := NewWithT(t) - actual, err := Parse(c.input) - g.Expect(err).To(BeNil()) - g.Expect(actual).To(Equal(c.expected)) - }) - } -} diff --git a/pkg/config/schema/codegen/collections.go b/pkg/config/schema/codegen/collections.go deleted file mode 100644 index efef9479c..000000000 --- a/pkg/config/schema/codegen/collections.go +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package codegen - -import ( - "fmt" - "sort" - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/ast" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -const staticResourceTemplate = ` -// GENERATED FILE -- DO NOT EDIT -// - -package {{.PackageName}} - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" -) - -var ( -{{- range .Entries }} - {{.Type}} = config.GroupVersionKind{Group: "{{.Resource.Group}}", Version: "{{.Resource.Version}}", Kind: "{{.Resource.Kind}}"} -{{- end }} -) -` - -const staticCollectionsTemplate = ` -{{- .FilePrefix}} -// GENERATED FILE -- DO NOT EDIT -// - -package {{.PackageName}} - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/validation" - "reflect" -{{- range .Packages}} - {{.ImportName}} "{{.PackageName}}" -{{- end}} -) - -var ( -{{ range .Entries }} - {{ commentBlock (wordWrap (printf "%s %s" .Collection.VariableName .Collection.Description) 70) 1 }} - {{ .Collection.VariableName }} = collection.Builder { - Name: "{{ .Collection.Name }}", - VariableName: "{{ .Collection.VariableName }}", - Resource: resource.Builder { - Group: "{{ .Resource.Group }}", - Kind: "{{ .Resource.Kind }}", - Plural: "{{ .Resource.Plural }}", - Version: "{{ .Resource.Version }}", - Proto: "{{ .Resource.Proto }}", - {{- if ne .Resource.StatusProto "" }}StatusProto: "{{ .Resource.StatusProto }}",{{end}} - ReflectType: {{ .Type }}, - {{- if ne .StatusType "" }}StatusType: {{ .StatusType }}, {{end}} - ProtoPackage: "{{ .Resource.ProtoPackage }}", - {{- if ne "" .Resource.StatusProtoPackage}}StatusPackage: "{{ .Resource.StatusProtoPackage }}", {{end}} - ClusterScoped: {{ .Resource.ClusterScoped }}, - ValidateProto: validation.{{ .Resource.Validate }}, - }.MustBuild(), - }.MustBuild() -{{ end }} - - // All contains all collections in the system. - All = collection.NewSchemasBuilder(). - {{- range .Entries }} - MustAdd({{ .Collection.VariableName }}). - {{- end }} - Build() - - // Istio contains only Istio collections. - Istio = collection.NewSchemasBuilder(). - {{- range .Entries }} - {{- if (hasPrefix .Collection.Name "istio/") }} - MustAdd({{ .Collection.VariableName }}). - {{- end}} - {{- end }} - Build() - - // Kube contains only kubernetes collections. - Kube = collection.NewSchemasBuilder(). - {{- range .Entries }} - {{- if (hasPrefix .Collection.Name "k8s/") }} - MustAdd({{ .Collection.VariableName }}). - {{- end }} - {{- end }} - Build() - - // Builtin contains only native Kubernetes collections. This differs from Kube, which has - // Kubernetes controlled CRDs - Builtin = collection.NewSchemasBuilder(). - {{- range .Entries }} - {{- if .Collection.Builtin }} - MustAdd({{ .Collection.VariableName }}). - {{- end }} - {{- end }} - Build() - - // Pilot contains only collections used by Pilot. - Pilot = collection.NewSchemasBuilder(). - {{- range .Entries }} - {{- if .Collection.Pilot }} - MustAdd({{ .Collection.VariableName }}). - {{- end}} - {{- end }} - Build() - - // PilotGatewayAPI contains only collections used by Pilot, including experimental Service Api. - PilotGatewayAPI = collection.NewSchemasBuilder(). - {{- range .Entries }} - {{- if or (.Collection.Pilot) (hasPrefix .Collection.Name "k8s/gateway_api") }} - MustAdd({{ .Collection.VariableName }}). - {{- end}} - {{- end }} - Build() - - // Deprecated contains only collections used by that will soon be used by nothing. - Deprecated = collection.NewSchemasBuilder(). - {{- range .Entries }} - {{- if .Collection.Deprecated }} - MustAdd({{ .Collection.VariableName }}). - {{- end}} - {{- end }} - Build() -) -` - -type colEntry struct { - Collection *ast.Collection - Resource *ast.Resource - Type string - StatusType string -} - -func WriteGvk(packageName string, m *ast.Metadata) (string, error) { - entries := make([]colEntry, 0, len(m.Collections)) - customNames := map[string]string{ - "k8s/gateway_api/v1alpha2/gateways": "KubernetesGateway", - } - for _, c := range m.Collections { - r := m.FindResourceForGroupKind(c.Group, c.Kind) - if r == nil { - return "", fmt.Errorf("failed to find resource (%s/%s) for collection %s", c.Group, c.Kind, c.Name) - } - - name := r.Kind - if cn, f := customNames[c.Name]; f { - name = cn - } - entries = append(entries, colEntry{ - Type: name, - Resource: r, - }) - } - - sort.Slice(entries, func(i, j int) bool { - return strings.Compare(entries[i].Type, entries[j].Type) < 0 - }) - - context := struct { - Entries []colEntry - PackageName string - }{ - Entries: entries, - PackageName: packageName, - } - - // Calculate the Go packages that needs to be imported for the proto types to be registered. - return applyTemplate(staticResourceTemplate, context) -} - -type packageImport struct { - PackageName string - ImportName string -} - -// StaticCollections generates a Go file for static-importing Proto packages, so that they get registered statically. -func StaticCollections(packageName string, m *ast.Metadata, filter func(name string) bool, prefix string) (string, error) { - entries := make([]colEntry, 0, len(m.Collections)) - for _, c := range m.Collections { - if !filter(c.Name) { - continue - } - r := m.FindResourceForGroupKind(c.Group, c.Kind) - if r == nil { - return "", fmt.Errorf("failed to find resource (%s/%s) for collection %s", c.Group, c.Kind, c.Name) - } - - spl := strings.Split(r.Proto, ".") - tname := spl[len(spl)-1] - stat := strings.Split(r.StatusProto, ".") - statName := stat[len(stat)-1] - e := colEntry{ - Collection: c, - Resource: r, - Type: fmt.Sprintf("reflect.TypeOf(&%s.%s{}).Elem()", toImport(r.ProtoPackage), tname), - } - if r.StatusProtoPackage != "" { - e.StatusType = fmt.Sprintf("reflect.TypeOf(&%s.%s{}).Elem()", toImport(r.StatusProtoPackage), statName) - } - entries = append(entries, e) - } - // Single instance and sort names - names := sets.New() - - for _, r := range m.Resources { - if r.ProtoPackage != "" { - names.Insert(r.ProtoPackage) - } - if r.StatusProtoPackage != "" { - names.Insert(r.StatusProtoPackage) - } - } - - packages := make([]packageImport, 0, names.Len()) - for p := range names { - packages = append(packages, packageImport{p, toImport(p)}) - } - sort.Slice(packages, func(i, j int) bool { - return strings.Compare(packages[i].PackageName, packages[j].PackageName) < 0 - }) - - sort.Slice(entries, func(i, j int) bool { - return strings.Compare(entries[i].Collection.Name, entries[j].Collection.Name) < 0 - }) - - context := struct { - Entries []colEntry - PackageName string - FilePrefix string - Packages []packageImport - }{ - Entries: entries, - PackageName: packageName, - Packages: packages, - FilePrefix: prefix, - } - - // Calculate the Go packages that needs to be imported for the proto types to be registered. - return applyTemplate(staticCollectionsTemplate, context) -} - -func toImport(p string) string { - return strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(p, "/", ""), ".", ""), "-", "") -} diff --git a/pkg/config/schema/codegen/collections_test.go b/pkg/config/schema/codegen/collections_test.go deleted file mode 100644 index 4fa3e3c0a..000000000 --- a/pkg/config/schema/codegen/collections_test.go +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package codegen - -import ( - "fmt" - "strings" - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/ast" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -func TestStaticCollections(t *testing.T) { - cases := []struct { - packageName string - m *ast.Metadata - err string - output string - }{ - { - packageName: "pkg", - m: &ast.Metadata{ - Collections: []*ast.Collection{ - { - Name: "foo", - VariableName: "Foo", - Description: "describes a really cool foo thing", - Group: "foo.group", - Kind: "fookind", - }, - { - Name: "bar", - VariableName: "Bar", - Description: "describes a really cool bar thing", - Group: "bar.group", - Kind: "barkind", - }, - }, - Resources: []*ast.Resource{ - { - Group: "foo.group", - Version: "v1", - Kind: "fookind", - Plural: "fookinds", - ClusterScoped: true, - Proto: "google.protobuf.Struct", - ProtoPackage: "github.com/gogo/protobuf/types", - Validate: "EmptyValidate", - }, - { - Group: "bar.group", - Version: "v1", - Kind: "barkind", - Plural: "barkinds", - ClusterScoped: false, - Proto: "google.protobuf.Struct", - ProtoPackage: "github.com/gogo/protobuf/types", - Validate: "EmptyValidate", - }, - }, - }, - output: ` -// GENERATED FILE -- DO NOT EDIT -// - -package pkg - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/validation" - "reflect" - githubcomgogoprotobuftypes "github.com/gogo/protobuf/types" -) - -var ( - - // Bar describes a really cool bar thing - Bar = collection.Builder { - Name: "bar", - VariableName: "Bar", - Resource: resource.Builder { - Group: "bar.group", - Kind: "barkind", - Plural: "barkinds", - Version: "v1", - Proto: "google.protobuf.Struct", - ReflectType: reflect.TypeOf(&githubcomgogoprotobuftypes.Struct{}).Elem(), - ProtoPackage: "github.com/gogo/protobuf/types", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // Foo describes a really cool foo thing - Foo = collection.Builder { - Name: "foo", - VariableName: "Foo", - Resource: resource.Builder { - Group: "foo.group", - Kind: "fookind", - Plural: "fookinds", - Version: "v1", - Proto: "google.protobuf.Struct", - ReflectType: reflect.TypeOf(&githubcomgogoprotobuftypes.Struct{}).Elem(), - ProtoPackage: "github.com/gogo/protobuf/types", - ClusterScoped: true, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - - // All contains all collections in the system. - All = collection.NewSchemasBuilder(). - MustAdd(Bar). - MustAdd(Foo). - Build() - - // Istio contains only Istio collections. - Istio = collection.NewSchemasBuilder(). - Build() - - // Kube contains only kubernetes collections. - Kube = collection.NewSchemasBuilder(). - Build() - - // Builtin contains only native Kubernetes collections. This differs from Kube, which has - // Kubernetes controlled CRDs - Builtin = collection.NewSchemasBuilder(). - Build() - - // Pilot contains only collections used by Pilot. - Pilot = collection.NewSchemasBuilder(). - Build() - - // PilotGatewayAPI contains only collections used by Pilot, including experimental Service Api. - PilotGatewayAPI = collection.NewSchemasBuilder(). - Build() - - // Deprecated contains only collections used by that will soon be used by nothing. - Deprecated = collection.NewSchemasBuilder(). - Build() -) -`, - }, - } - - for _, c := range cases { - t.Run("", func(t *testing.T) { - g := NewWithT(t) - - s, err := StaticCollections(c.packageName, c.m, func(name string) bool { - return true - }, "") - if c.err != "" { - g.Expect(err).NotTo(BeNil()) - g.Expect(err.Error()).To(Equal(s)) - } else { - g.Expect(err).To(BeNil()) - fmt.Println(strings.TrimSpace(c.output)) - assert.Equal(t, strings.TrimSpace(s), strings.TrimSpace(c.output)) - } - }) - } -} diff --git a/pkg/config/schema/codegen/common.go b/pkg/config/schema/codegen/common.go deleted file mode 100644 index 5eb4bc5ae..000000000 --- a/pkg/config/schema/codegen/common.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package codegen - -import ( - "bytes" - "strings" - "text/template" -) - -const ( - commentLinePrefix = "// " -) - -func applyTemplate(tmpl string, i interface{}) (string, error) { - t := template.New("tmpl").Funcs(template.FuncMap{ - "wordWrap": wordWrap, - "commentBlock": commentBlock, - "hasPrefix": strings.HasPrefix, - }) - - t2 := template.Must(t.Parse(tmpl)) - - var b bytes.Buffer - if err := t2.Execute(&b, i); err != nil { - return "", err - } - - return b.String(), nil -} - -func commentBlock(in []string, indentTabs int) string { - // Copy the input array. - in = append([]string{}, in...) - - // Apply the tabs and comment prefix to each line. - for lineIndex := range in { - prefix := "" - for tabIndex := 0; lineIndex > 0 && tabIndex < indentTabs; tabIndex++ { - prefix += "\t" - } - prefix += commentLinePrefix - in[lineIndex] = prefix + in[lineIndex] - } - - // Join the lines with carriage returns. - return strings.Join(in, "\n") -} - -func wordWrap(in string, maxLineLength int) []string { - // First, split the input based on any user-created lines (i.e. the string contains "\n"). - inputLines := strings.Split(in, "\n") - outputLines := make([]string, 0) - - line := "" - for i, inputLine := range inputLines { - if i > 0 { - // Process a user-defined carriage return. - outputLines = append(outputLines, line) - line = "" - } - - words := strings.Split(inputLine, " ") - - for len(words) > 0 { - // Take the next word. - word := words[0] - words = words[1:] - - if len(line)+len(word) > maxLineLength { - // Need to word wrap - emit the current line. - outputLines = append(outputLines, line) - line = "" - } - - // Add the word to the current line. - if len(line) > 0 { - line += " " - } - line += word - } - } - - // Emit the final line - outputLines = append(outputLines, line) - - return outputLines -} diff --git a/pkg/config/schema/codegen/common_test.go b/pkg/config/schema/codegen/common_test.go deleted file mode 100644 index 318b4bc13..000000000 --- a/pkg/config/schema/codegen/common_test.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package codegen - -import ( - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -func TestCommentBlock(t *testing.T) { - cases := []struct { - name string - input []string - indentTabs int - expected string - }{ - { - name: "single line", - input: []string{ - "single line comment", - }, - indentTabs: 1, - expected: "// single line comment", - }, - { - name: "single line", - input: []string{ - "first line no indent", - "second line has indent", - }, - indentTabs: 3, - expected: "// first line no indent\n" + - "\t\t\t// second line has indent", - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - g := NewWithT(t) - output := commentBlock(c.input, c.indentTabs) - g.Expect(output).To(Equal(c.expected)) - }) - } -} - -func TestWordWrap(t *testing.T) { - cases := []struct { - name string - input string - maxLineLength int - expected []string - }{ - { - name: "no wrap", - input: "no wrap is required", - maxLineLength: 100, - expected: []string{ - "no wrap is required", - }, - }, - { - name: "wrap after word", - input: "wrap after word", - maxLineLength: 11, - expected: []string{ - "wrap after", - "word", - }, - }, - { - name: "wrap mid word", - input: "wrap mid-word", - maxLineLength: 10, - expected: []string{ - "wrap", - "mid-word", - }, - }, - { - name: "user carriage return", - input: "user carriage\nreturn", - maxLineLength: 100, - expected: []string{ - "user carriage", - "return", - }, - }, - { - name: "multiple lines", - input: "This is a long-winded example.\nIt shows:\n -user-defined carriage returns\n " + - "-wrapping at the max line length\n -removal of extra whitespace around words", - maxLineLength: 22, - expected: []string{ - "This is a long-winded", - "example.", - "It shows:", - "-user-defined carriage", - "returns", - "-wrapping at the max", - "line length", - "-removal of extra", - "whitespace around words", - }, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - g := NewWithT(t) - output := wordWrap(c.input, c.maxLineLength) - g.Expect(output).To(Equal(c.expected)) - }) - } -} diff --git a/pkg/config/schema/codegen/tools/collections.main.go b/pkg/config/schema/codegen/tools/collections.main.go deleted file mode 100644 index 716db3f7c..000000000 --- a/pkg/config/schema/codegen/tools/collections.main.go +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build ignore -// +build ignore - -package main - -import ( - "fmt" - "os" - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/ast" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/codegen" -) - -// Utility for generating collections.gen.go. Called from gen.go -func main() { - if len(os.Args) != 4 && len(os.Args) != 7 { - fmt.Printf("Invalid args: %v", os.Args) - os.Exit(-1) - } - - pkg := os.Args[1] - input := os.Args[2] - output := os.Args[3] - var splitOn, splitOutput, splitPrefix string - if len(os.Args) > 4 { - splitOn = os.Args[4] - splitOutput = os.Args[5] - splitPrefix = os.Args[6] - } - - // Read the input file - b, err := os.ReadFile(input) - if err != nil { - fmt.Printf("unable to read input file: %v", err) - os.Exit(-2) - } - - // Parse the file. - m, err := ast.Parse(string(b)) - if err != nil { - fmt.Printf("failed parsing input file: %v", err) - os.Exit(-3) - } - - var contents string - - if pkg == "gvk" { - contents, err = codegen.WriteGvk(pkg, m) - if err != nil { - fmt.Printf("Error applying static init template: %v", err) - os.Exit(-3) - } - if err = os.WriteFile(output, []byte(contents), os.ModePerm); err != nil { - fmt.Printf("Error writing output file: %v", err) - os.Exit(-4) - } - return - } - if splitOn == "" { - contents, err = codegen.StaticCollections(pkg, m, func(name string) bool { - return true - }, "") - if err != nil { - fmt.Printf("Error applying static init template: %v", err) - os.Exit(-3) - } - if err = os.WriteFile(output, []byte(contents), os.ModePerm); err != nil { - fmt.Printf("Error writing output file: %v", err) - os.Exit(-4) - } - return - } - fullPrefix := "// +build !" + splitPrefix - fullContents, err := codegen.StaticCollections(pkg, m, func(name string) bool { - return true - }, fullPrefix) - if err != nil { - fmt.Printf("Error applying static init template: %v", err) - os.Exit(-3) - } - if err = os.WriteFile(output, []byte(fullContents), os.ModePerm); err != nil { - fmt.Printf("Error writing output file: %v", err) - os.Exit(-4) - } - matchPrefix := "// +build " + splitPrefix - matchContents, err := codegen.StaticCollections(pkg, m, func(name string) bool { - return !strings.Contains(name, splitOn) - }, matchPrefix) - if err != nil { - fmt.Printf("Error applying static init template: %v", err) - os.Exit(-3) - } - if err = os.WriteFile(splitOutput, []byte(matchContents), os.ModePerm); err != nil { - fmt.Printf("Error writing output file: %v", err) - os.Exit(-4) - } - return -} diff --git a/pkg/config/schema/collection/name.go b/pkg/config/schema/collection/name.go deleted file mode 100644 index 87d294f38..000000000 --- a/pkg/config/schema/collection/name.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package collection - -import ( - "regexp" -) - -// Name of a collection. -type Name string - -var validNameRegex = regexp.MustCompile(`^[a-zA-Z0-9_][a-zA-Z0-9_\.]*(/[a-zA-Z0-9_][a-zA-Z0-9_\.]*)*$`) - -// NewName returns a strongly typed collection. Panics if the name is not valid. -func NewName(n string) Name { - if !IsValidName(n) { - panic("collection.NewName: invalid collection name: " + n) - } - return Name(n) -} - -// String interface method implementation. -func (n Name) String() string { - return string(n) -} - -// IsValidName returns true if the given collection is a valid name. -func IsValidName(name string) bool { - return validNameRegex.MatchString(name) -} diff --git a/pkg/config/schema/collection/name_test.go b/pkg/config/schema/collection/name_test.go deleted file mode 100644 index 2b0431599..000000000 --- a/pkg/config/schema/collection/name_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package collection - -import ( - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -func TestNewName(t *testing.T) { - g := NewWithT(t) - - c := NewName("c1") - g.Expect(c.String()).To(Equal("c1")) -} - -func TestNewName_Invalid(t *testing.T) { - g := NewWithT(t) - defer func() { - r := recover() - g.Expect(r).NotTo(BeNil()) - }() - - _ = NewName("/") -} - -func TestName_String(t *testing.T) { - g := NewWithT(t) - c := NewName("c1") - - g.Expect(c.String()).To(Equal("c1")) -} - -func TestIsValidName_Valid(t *testing.T) { - data := []string{ - "foo", - "9", - "b", - "a", - "_", - "a0_9", - "a0_9/fruj_", - "abc/def", - } - - for _, d := range data { - t.Run(d, func(t *testing.T) { - g := NewWithT(t) - b := IsValidName(d) - g.Expect(b).To(BeTrue()) - }) - } -} - -func TestIsValidName_Invalid(t *testing.T) { - data := []string{ - "", - "/", - "/a", - "a/", - "$a/bc", - "z//a", - } - - for _, d := range data { - t.Run(d, func(t *testing.T) { - g := NewWithT(t) - b := IsValidName(d) - g.Expect(b).To(BeFalse()) - }) - } -} diff --git a/pkg/config/schema/collection/names.go b/pkg/config/schema/collection/names.go deleted file mode 100644 index c9f1626ca..000000000 --- a/pkg/config/schema/collection/names.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package collection - -import ( - "sort" - "strings" -) - -// Names is a collection of names -type Names []Name - -// Clone names -func (n Names) Clone() Names { - r := make([]Name, len(n)) - copy(r, n) - return r -} - -// Sort the names in ascending order. -func (n Names) Sort() { - sort.SliceStable(n, func(i, j int) bool { - return strings.Compare((n)[i].String(), (n)[j].String()) < 0 - }) -} diff --git a/pkg/config/schema/collection/names_test.go b/pkg/config/schema/collection/names_test.go deleted file mode 100644 index cb72f7c5e..000000000 --- a/pkg/config/schema/collection/names_test.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package collection_test - -import ( - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -func TestNames_Clone(t *testing.T) { - g := NewWithT(t) - - n := collection.Names{collections.All.All()[0].Name(), collections.All.All()[1].Name()} - - n2 := n.Clone() - g.Expect(n2).To(Equal(n)) -} - -func TestNames_Sort(t *testing.T) { - g := NewWithT(t) - - n := collection.Names{collections.IstioMeshV1Alpha1MeshConfig.Name(), collections.IstioExtensionsV1Alpha1Wasmplugins.Name()} - expected := collection.Names{collections.IstioExtensionsV1Alpha1Wasmplugins.Name(), collections.IstioMeshV1Alpha1MeshConfig.Name()} - - n.Sort() - g.Expect(n).To(Equal(expected)) -} diff --git a/pkg/config/schema/collection/schema.go b/pkg/config/schema/collection/schema.go deleted file mode 100644 index 44d733b14..000000000 --- a/pkg/config/schema/collection/schema.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package collection - -import ( - "fmt" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/resource" -) - -// Schema for a collection. -type Schema interface { - fmt.Stringer - - // Name of the collection. - Name() Name - - // VariableName is a utility method used to help with codegen. It provides the name of a Schema instance variable. - VariableName() string - - // Resource is the schema for resources contained in this collection. - Resource() resource.Schema - - // Equal is a helper function for testing equality between Schema instances. This supports comparison - // with the cmp library. - Equal(other Schema) bool -} - -// Builder is config for the creation of a Schema -type Builder struct { - Name string - VariableName string - Resource resource.Schema -} - -// Build a Schema instance. -func (b Builder) Build() (Schema, error) { - if !IsValidName(b.Name) { - return nil, fmt.Errorf("invalid collection name: %s", b.Name) - } - if b.Resource == nil { - return nil, fmt.Errorf("collection %s: resource must be non-nil", b.Name) - } - - return &schemaImpl{ - name: NewName(b.Name), - variableName: b.VariableName, - resource: b.Resource, - }, nil -} - -// MustBuild calls Build and panics if it fails. -func (b Builder) MustBuild() Schema { - s, err := b.Build() - if err != nil { - panic(fmt.Sprintf("MustBuild: %v", err)) - } - - return s -} - -type schemaImpl struct { - resource resource.Schema - name Name - variableName string -} - -// String interface method implementation. -func (s *schemaImpl) String() string { - return fmt.Sprintf("[Schema](%s, %q, %s)", s.name, s.resource.ProtoPackage(), s.resource.Proto()) -} - -func (s *schemaImpl) Name() Name { - return s.name -} - -func (s *schemaImpl) VariableName() string { - return s.variableName -} - -func (s *schemaImpl) Resource() resource.Schema { - return s.resource -} - -func (s *schemaImpl) Equal(o Schema) bool { - return s.name == o.Name() && - s.Resource().Equal(o.Resource()) -} diff --git a/pkg/config/schema/collection/schema_test.go b/pkg/config/schema/collection/schema_test.go deleted file mode 100644 index dc927bd3e..000000000 --- a/pkg/config/schema/collection/schema_test.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package collection_test - -import ( - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/resource" -) - -func TestSchema_NewSchema(t *testing.T) { - g := NewWithT(t) - - s, err := collection.Builder{ - Name: "foo", - Resource: emptyResource, - }.Build() - g.Expect(err).To(BeNil()) - g.Expect(s.Name()).To(Equal(collection.NewName("foo"))) - g.Expect(s.Resource().ProtoPackage()).To(Equal("github.com/gogo/protobuf/types")) - g.Expect(s.Resource().Proto()).To(Equal("google.protobuf.Empty")) -} - -func TestSchema_NewSchema_Error(t *testing.T) { - g := NewWithT(t) - - _, err := collection.Builder{ - Name: "$", - Resource: emptyResource, - }.Build() - g.Expect(err).NotTo(BeNil()) -} - -func TestSchema_MustNewSchema(t *testing.T) { - g := NewWithT(t) - defer func() { - r := recover() - g.Expect(r).To(BeNil()) - }() - - s := collection.Builder{ - Name: "foo", - Resource: emptyResource, - }.MustBuild() - g.Expect(s.Name()).To(Equal(collection.NewName("foo"))) - g.Expect(s.Resource().ProtoPackage()).To(Equal("github.com/gogo/protobuf/types")) - g.Expect(s.Resource().Proto()).To(Equal("google.protobuf.Empty")) -} - -func TestSchema_MustNewSchema_Error(t *testing.T) { - g := NewWithT(t) - defer func() { - r := recover() - g.Expect(r).NotTo(BeNil()) - }() - - collection.Builder{ - Name: "$", - Resource: resource.Builder{ - ProtoPackage: "github.com/gogo/protobuf/types", - Proto: "google.protobuf.Empty", - }.MustBuild(), - }.MustBuild() -} - -func TestSchema_String(t *testing.T) { - g := NewWithT(t) - - s := collection.Builder{ - Name: "foo", - Resource: resource.Builder{ - Kind: "Empty", - Plural: "empties", - ProtoPackage: "github.com/gogo/protobuf/types", - Proto: "google.protobuf.Empty", - }.MustBuild(), - }.MustBuild() - - g.Expect(s.String()).To(Equal(`[Schema](foo, "github.com/gogo/protobuf/types", google.protobuf.Empty)`)) -} diff --git a/pkg/config/schema/collection/schemas.go b/pkg/config/schema/collection/schemas.go deleted file mode 100644 index dd694c04b..000000000 --- a/pkg/config/schema/collection/schemas.go +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package collection - -import ( - "fmt" - "sort" - "strings" -) - -import ( - "github.com/google/go-cmp/cmp" - "github.com/hashicorp/go-multierror" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" -) - -// Schemas contains metadata about configuration resources. -type Schemas struct { - byCollection map[Name]Schema - byAddOrder []Schema -} - -// SchemasFor is a shortcut for creating Schemas. It uses MustAdd for each element. -func SchemasFor(schemas ...Schema) Schemas { - b := NewSchemasBuilder() - for _, s := range schemas { - b.MustAdd(s) - } - return b.Build() -} - -// SchemasBuilder is a builder for the schemas type. -type SchemasBuilder struct { - schemas Schemas -} - -// NewSchemasBuilder returns a new instance of SchemasBuilder. -func NewSchemasBuilder() *SchemasBuilder { - s := Schemas{ - byCollection: make(map[Name]Schema), - } - - return &SchemasBuilder{ - schemas: s, - } -} - -// Add a new collection to the schemas. -func (b *SchemasBuilder) Add(s Schema) error { - if _, found := b.schemas.byCollection[s.Name()]; found { - return fmt.Errorf("collection already exists: %v", s.Name()) - } - - b.schemas.byCollection[s.Name()] = s - b.schemas.byAddOrder = append(b.schemas.byAddOrder, s) - return nil -} - -// MustAdd calls Add and panics if it fails. -func (b *SchemasBuilder) MustAdd(s Schema) *SchemasBuilder { - if err := b.Add(s); err != nil { - panic(fmt.Sprintf("SchemasBuilder.MustAdd: %v", err)) - } - return b -} - -// Build a new schemas from this SchemasBuilder. -func (b *SchemasBuilder) Build() Schemas { - s := b.schemas - - // Avoid modify after Build. - b.schemas = Schemas{} - - return s -} - -// ForEach executes the given function on each contained schema, until the function returns true. -func (s Schemas) ForEach(handleSchema func(Schema) (done bool)) { - for _, schema := range s.byAddOrder { - if handleSchema(schema) { - return - } - } -} - -func (s Schemas) Intersect(otherSchemas Schemas) Schemas { - resultBuilder := NewSchemasBuilder() - for _, myschema := range s.All() { - if _, ok := otherSchemas.FindByGroupVersionResource(myschema.Resource().GroupVersionResource()); ok { - // an error indicates the schema has already been added, which doesn't negatively impact intersect - _ = resultBuilder.Add(myschema) - } - } - return resultBuilder.Build() -} - -func (s Schemas) Union(otherSchemas Schemas) Schemas { - resultBuilder := NewSchemasBuilder() - for _, myschema := range s.All() { - // an error indicates the schema has already been added, which doesn't negatively impact intersect - _ = resultBuilder.Add(myschema) - } - for _, myschema := range otherSchemas.All() { - // an error indicates the schema has already been added, which doesn't negatively impact intersect - _ = resultBuilder.Add(myschema) - } - return resultBuilder.Build() -} - -// Find looks up a Schema by its collection name. -func (s Schemas) Find(collection string) (Schema, bool) { - i, ok := s.byCollection[Name(collection)] - return i, ok -} - -// MustFind calls Find and panics if not found. -func (s Schemas) MustFind(collection string) Schema { - i, ok := s.Find(collection) - if !ok { - panic(fmt.Sprintf("schemas.MustFind: matching entry not found for collection: %q", collection)) - } - return i -} - -// FindByGroupVersionKind searches and returns the first schema with the given GVK -func (s Schemas) FindByGroupVersionKind(gvk config.GroupVersionKind) (Schema, bool) { - for _, rs := range s.byAddOrder { - if rs.Resource().GroupVersionKind() == gvk { - return rs, true - } - } - - return nil, false -} - -// FindByGroupVersionResource searches and returns the first schema with the given GVR -func (s Schemas) FindByGroupVersionResource(gvr schema.GroupVersionResource) (Schema, bool) { - for _, rs := range s.byAddOrder { - if rs.Resource().GroupVersionResource() == gvr { - return rs, true - } - } - - return nil, false -} - -// FindByPlural searches and returns the first schema with the given Group, Version and plural form of the Kind -func (s Schemas) FindByPlural(group, version, plural string) (Schema, bool) { - for _, rs := range s.byAddOrder { - if rs.Resource().Plural() == plural && - rs.Resource().Group() == group && - rs.Resource().Version() == version { - return rs, true - } - } - - return nil, false -} - -// MustFindByGroupVersionKind calls FindByGroupVersionKind and panics if not found. -func (s Schemas) MustFindByGroupVersionKind(gvk config.GroupVersionKind) Schema { - r, found := s.FindByGroupVersionKind(gvk) - if !found { - panic(fmt.Sprintf("Schemas.MustFindByGroupVersionKind: unable to find %s", gvk)) - } - return r -} - -// All returns all known Schemas -func (s Schemas) All() []Schema { - return append(make([]Schema, 0, len(s.byAddOrder)), s.byAddOrder...) -} - -// Add creates a copy of this Schemas with the given schemas added. -func (s Schemas) Add(toAdd ...Schema) Schemas { - b := NewSchemasBuilder() - - for _, s := range s.byAddOrder { - b.MustAdd(s) - } - - for _, s := range toAdd { - b.MustAdd(s) - } - - return b.Build() -} - -// Remove creates a copy of this Schemas with the given schemas removed. -func (s Schemas) Remove(toRemove ...Schema) Schemas { - b := NewSchemasBuilder() - - for _, s := range s.byAddOrder { - shouldAdd := true - for _, r := range toRemove { - if r.Name() == s.Name() { - shouldAdd = false - break - } - } - if shouldAdd { - b.MustAdd(s) - } - } - - return b.Build() -} - -// CollectionNames returns all known collections. -func (s Schemas) CollectionNames() Names { - result := make(Names, 0, len(s.byAddOrder)) - - for _, info := range s.byAddOrder { - result = append(result, info.Name()) - } - - sort.Slice(result, func(i, j int) bool { - return strings.Compare(result[i].String(), result[j].String()) < 0 - }) - - return result -} - -// Kinds returns all known resource kinds. -func (s Schemas) Kinds() []string { - kinds := make(map[string]struct{}, len(s.byAddOrder)) - for _, s := range s.byAddOrder { - kinds[s.Resource().Kind()] = struct{}{} - } - - out := make([]string, 0, len(kinds)) - for kind := range kinds { - out = append(out, kind) - } - - sort.Strings(out) - return out -} - -// Validate the schemas. Returns error if there is a problem. -func (s Schemas) Validate() (err error) { - for _, c := range s.byAddOrder { - err = multierror.Append(err, c.Resource().Validate()).ErrorOrNil() - } - return -} - -func (s Schemas) Equal(o Schemas) bool { - return cmp.Equal(s.byAddOrder, o.byAddOrder) -} diff --git a/pkg/config/schema/collection/schemas_test.go b/pkg/config/schema/collection/schemas_test.go deleted file mode 100644 index 234855da0..000000000 --- a/pkg/config/schema/collection/schemas_test.go +++ /dev/null @@ -1,427 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package collection_test - -import ( - "testing" -) - -import ( - _ "github.com/gogo/protobuf/types" - . "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/resource" -) - -var ( - emptyResource = resource.Builder{ - Kind: "Empty", - Plural: "empties", - ProtoPackage: "github.com/gogo/protobuf/types", - Proto: "google.protobuf.Empty", - }.MustBuild() - - structResource = resource.Builder{ - Kind: "Struct", - Plural: "structs", - ProtoPackage: "github.com/gogo/protobuf/types", - Proto: "google.protobuf.Struct", - }.MustBuild() -) - -func TestSchemas_Basic(t *testing.T) { - g := NewWithT(t) - - s := collection.Builder{ - Name: "foo", - Resource: emptyResource, - }.MustBuild() - - schemas := collection.SchemasFor(s) - g.Expect(schemas.All()).To(HaveLen(1)) - g.Expect(schemas.All()[0]).To(Equal(s)) -} - -func TestSchemas_MustAdd(t *testing.T) { - g := NewWithT(t) - defer func() { - r := recover() - g.Expect(r).To(BeNil()) - }() - b := collection.NewSchemasBuilder() - - s := collection.Builder{ - Name: "foo", - Resource: emptyResource, - }.MustBuild() - b.MustAdd(s) -} - -func TestSchemas_MustRegister_Panic(t *testing.T) { - g := NewWithT(t) - defer func() { - r := recover() - g.Expect(r).NotTo(BeNil()) - }() - b := collection.NewSchemasBuilder() - - s := collection.Builder{ - Name: "foo", - Resource: emptyResource, - }.MustBuild() - b.MustAdd(s) - b.MustAdd(s) -} - -func TestSchemas_Find(t *testing.T) { - g := NewWithT(t) - - s := collection.Builder{ - Name: "foo", - Resource: emptyResource, - }.MustBuild() - - schemas := collection.SchemasFor(s) - - s2, found := schemas.Find("foo") - g.Expect(found).To(BeTrue()) - g.Expect(s2).To(Equal(s)) - - _, found = schemas.Find("zoo") - g.Expect(found).To(BeFalse()) -} - -func TestSchemas_MustFind(t *testing.T) { - g := NewWithT(t) - defer func() { - r := recover() - g.Expect(r).To(BeNil()) - }() - - b := collection.NewSchemasBuilder() - - s := collection.Builder{ - Name: "foo", - Resource: emptyResource, - }.MustBuild() - - b.MustAdd(s) - schemas := b.Build() - - s2 := schemas.MustFind("foo") - g.Expect(s2).To(Equal(s)) -} - -func TestSchemas_MustFind_Panic(t *testing.T) { - g := NewWithT(t) - defer func() { - r := recover() - g.Expect(r).NotTo(BeNil()) - }() - - b := collection.NewSchemasBuilder() - - s := collection.Builder{ - Name: "foo", - Resource: emptyResource, - }.MustBuild() - - b.MustAdd(s) - schemas := b.Build() - - _ = schemas.MustFind("zoo") -} - -func TestSchema_FindByGroupVersionKind(t *testing.T) { - g := NewWithT(t) - - s := collection.Builder{ - Name: "foo", - Resource: resource.Builder{ - ProtoPackage: "github.com/gogo/protobuf/types", - Proto: "google.protobuf.Empty", - Group: "mygroup", - Kind: "Empty", - Plural: "empties", - Version: "v1", - }.MustBuild(), - }.MustBuild() - - schemas := collection.SchemasFor(s) - - s2, found := schemas.FindByGroupVersionKind(config.GroupVersionKind{ - Group: "mygroup", - Version: "v1", - Kind: "Empty", - }) - g.Expect(found).To(BeTrue()) - g.Expect(s2).To(Equal(s)) - - _, found = schemas.FindByGroupVersionKind(config.GroupVersionKind{ - Group: "fake", - Version: "v1", - Kind: "Empty", - }) - g.Expect(found).To(BeFalse()) -} - -func TestSchema_MustFindByGroupVersionKind(t *testing.T) { - g := NewWithT(t) - b := collection.NewSchemasBuilder() - - s := collection.Builder{ - Name: "foo", - Resource: resource.Builder{ - ProtoPackage: "github.com/gogo/protobuf/types", - Proto: "google.protobuf.Empty", - Group: "mygroup", - Kind: "Empty", - Plural: "empties", - Version: "v1", - }.MustBuild(), - }.MustBuild() - - b.MustAdd(s) - schemas := b.Build() - - got := schemas.MustFindByGroupVersionKind(config.GroupVersionKind{ - Group: "mygroup", - Version: "v1", - Kind: "Empty", - }) - g.Expect(s).To(Equal(got)) -} - -func TestSchema_MustFindByGroupVersionKind_Panic(t *testing.T) { - g := NewWithT(t) - - defer func() { - r := recover() - g.Expect(r).NotTo(BeNil()) - }() - - schemas := collection.NewSchemasBuilder().Build() - _ = schemas.MustFindByGroupVersionKind(config.GroupVersionKind{ - Group: "mygroup", - Version: "v1", - Kind: "Empty", - }) -} - -func TestSchemas_CollectionNames(t *testing.T) { - g := NewWithT(t) - b := collection.NewSchemasBuilder() - - s1 := collection.Builder{ - Name: "foo", - Resource: emptyResource, - }.MustBuild() - s2 := collection.Builder{ - Name: "bar", - Resource: emptyResource, - }.MustBuild() - b.MustAdd(s1) - b.MustAdd(s2) - - names := b.Build().CollectionNames() - expected := collection.Names{collection.NewName("bar"), collection.NewName("foo")} - g.Expect(names).To(Equal(expected)) -} - -func TestSchemas_Kinds(t *testing.T) { - g := NewWithT(t) - - s := collection.SchemasFor( - collection.Builder{ - Name: "foo", - Resource: emptyResource, - }.MustBuild(), - collection.Builder{ - Name: "bar", - Resource: emptyResource, - }.MustBuild(), - collection.Builder{ - Name: "baz", - Resource: structResource, - }.MustBuild()) - - actual := s.Kinds() - expected := []string{emptyResource.Kind(), structResource.Kind()} - g.Expect(actual).To(Equal(expected)) -} - -func TestSchemas_Validate(t *testing.T) { - cases := []struct { - name string - schemas []collection.Schema - expectError bool - }{ - { - name: "valid", - schemas: []collection.Schema{ - collection.Builder{ - Name: "foo", - Resource: resource.Builder{ - Kind: "Empty1", - Plural: "Empty1s", - Proto: "google.protobuf.Empty", - }.MustBuild(), - }.MustBuild(), - collection.Builder{ - Name: "bar", - Resource: resource.Builder{ - Kind: "Empty2", - Plural: "Empty2s", - Proto: "google.protobuf.Empty", - }.MustBuild(), - }.MustBuild(), - }, - expectError: false, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - g := NewWithT(t) - b := collection.NewSchemasBuilder() - for _, s := range c.schemas { - b.MustAdd(s) - } - err := b.Build().Validate() - if c.expectError { - g.Expect(err).ToNot(BeNil()) - } else { - g.Expect(err).To(BeNil()) - } - }) - } -} - -func TestSchemas_Validate_Error(t *testing.T) { - g := NewWithT(t) - b := collection.NewSchemasBuilder() - - s1 := collection.Builder{ - Name: "foo", - Resource: resource.Builder{ - Kind: "Zoo", - ProtoPackage: "github.com/gogo/protobuf/types", - Proto: "zoo", - }.BuildNoValidate(), - }.MustBuild() - b.MustAdd(s1) - - err := b.Build().Validate() - g.Expect(err).NotTo(BeNil()) -} - -func TestSchemas_ForEach(t *testing.T) { - schemas := collection.SchemasFor( - collection.Builder{ - Name: "foo", - Resource: emptyResource, - }.MustBuild(), - collection.Builder{ - Name: "bar", - Resource: emptyResource, - }.MustBuild(), - ) - - cases := []struct { - name string - expected []string - actual func() []string - }{ - { - name: "all", - expected: []string{"foo", "bar"}, - actual: func() []string { - a := make([]string, 0) - schemas.ForEach(func(s collection.Schema) bool { - a = append(a, s.Name().String()) - return false - }) - return a - }, - }, - { - name: "exit early", - expected: []string{"foo"}, - actual: func() []string { - a := make([]string, 0) - schemas.ForEach(func(s collection.Schema) bool { - a = append(a, s.Name().String()) - return true - }) - return a - }, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - g := NewWithT(t) - actual := c.actual() - g.Expect(actual).To(Equal(c.expected)) - }) - } -} - -func TestSchemas_Remove(t *testing.T) { - g := NewWithT(t) - - foo := collection.Builder{ - Name: "foo", - Resource: emptyResource, - }.MustBuild() - bar := collection.Builder{ - Name: "bar", - Resource: emptyResource, - }.MustBuild() - baz := collection.Builder{ - Name: "baz", - Resource: emptyResource, - }.MustBuild() - - schemas := collection.SchemasFor(foo, bar) - g.Expect(schemas.Remove(bar)).To(Equal(collection.SchemasFor(foo))) - g.Expect(schemas.Remove(foo, bar, baz)).To(Equal(collection.SchemasFor())) - g.Expect(schemas).To(Equal(collection.SchemasFor(foo, bar))) -} - -func TestSchemas_Add(t *testing.T) { - g := NewWithT(t) - - foo := collection.Builder{ - Name: "foo", - Resource: emptyResource, - }.MustBuild() - bar := collection.Builder{ - Name: "bar", - Resource: emptyResource, - }.MustBuild() - baz := collection.Builder{ - Name: "baz", - Resource: emptyResource, - }.MustBuild() - - schemas := collection.SchemasFor(foo, bar) - g.Expect(schemas.Add(baz)).To(Equal(collection.SchemasFor(foo, bar, baz))) - g.Expect(schemas).To(Equal(collection.SchemasFor(foo, bar))) -} diff --git a/pkg/config/schema/collections/collections.agent.gen.go b/pkg/config/schema/collections/collections.agent.gen.go deleted file mode 100755 index 371e4c7a3..000000000 --- a/pkg/config/schema/collections/collections.agent.gen.go +++ /dev/null @@ -1,451 +0,0 @@ -//go:build agent -// +build agent - -// GENERATED FILE -- DO NOT EDIT -// - -package collections - -import ( - "reflect" -) - -import ( - istioioapiextensionsv1alpha1 "istio.io/api/extensions/v1alpha1" - istioioapimeshv1alpha1 "istio.io/api/mesh/v1alpha1" - istioioapimetav1alpha1 "istio.io/api/meta/v1alpha1" - istioioapinetworkingv1alpha3 "istio.io/api/networking/v1alpha3" - istioioapinetworkingv1beta1 "istio.io/api/networking/v1beta1" - istioioapisecurityv1beta1 "istio.io/api/security/v1beta1" - istioioapitelemetryv1alpha1 "istio.io/api/telemetry/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/validation" -) - -var ( - - // IstioExtensionsV1Alpha1Servicemetadatas describes the collection - // istio/extensions/v1alpha1/servicemetadatas - IstioExtensionsV1Alpha1Servicemetadatas = collection.Builder{ - Name: "istio/extensions/v1alpha1/servicemetadatas", - VariableName: "IstioExtensionsV1Alpha1Servicemetadatas", - Resource: resource.Builder{ - Group: "extensions.istio.io", - Kind: "ServiceMetadata", - Plural: "servicemetadatas", - Version: "v1alpha1", - Proto: "istio.extensions.v1alpha1.ServiceMetadata", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapiextensionsv1alpha1.ServiceMetadata{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/extensions/v1alpha1", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // IstioExtensionsV1Alpha1Servicenamemappings describes the collection - // istio/extensions/v1alpha1/servicenamemappings - IstioExtensionsV1Alpha1Servicenamemappings = collection.Builder{ - Name: "istio/extensions/v1alpha1/servicenamemappings", - VariableName: "IstioExtensionsV1Alpha1Servicenamemappings", - Resource: resource.Builder{ - Group: "extensions.istio.io", - Kind: "ServiceNameMapping", - Plural: "servicenamemappings", - Version: "v1alpha1", - Proto: "istio.extensions.v1alpha1.ServiceNameMapping", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapiextensionsv1alpha1.ServiceNameMapping{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/extensions/v1alpha1", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // IstioExtensionsV1Alpha1Wasmplugins describes the collection - // istio/extensions/v1alpha1/wasmplugins - IstioExtensionsV1Alpha1Wasmplugins = collection.Builder{ - Name: "istio/extensions/v1alpha1/wasmplugins", - VariableName: "IstioExtensionsV1Alpha1Wasmplugins", - Resource: resource.Builder{ - Group: "extensions.istio.io", - Kind: "WasmPlugin", - Plural: "wasmplugins", - Version: "v1alpha1", - Proto: "istio.extensions.v1alpha1.WasmPlugin", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapiextensionsv1alpha1.WasmPlugin{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/extensions/v1alpha1", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateWasmPlugin, - }.MustBuild(), - }.MustBuild() - - // IstioMeshV1Alpha1MeshConfig describes the collection - // istio/mesh/v1alpha1/MeshConfig - IstioMeshV1Alpha1MeshConfig = collection.Builder{ - Name: "istio/mesh/v1alpha1/MeshConfig", - VariableName: "IstioMeshV1Alpha1MeshConfig", - Resource: resource.Builder{ - Group: "", - Kind: "MeshConfig", - Plural: "meshconfigs", - Version: "v1alpha1", - Proto: "istio.mesh.v1alpha1.MeshConfig", - ReflectType: reflect.TypeOf(&istioioapimeshv1alpha1.MeshConfig{}).Elem(), - ProtoPackage: "istio.io/api/mesh/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // IstioMeshV1Alpha1MeshNetworks describes the collection - // istio/mesh/v1alpha1/MeshNetworks - IstioMeshV1Alpha1MeshNetworks = collection.Builder{ - Name: "istio/mesh/v1alpha1/MeshNetworks", - VariableName: "IstioMeshV1Alpha1MeshNetworks", - Resource: resource.Builder{ - Group: "", - Kind: "MeshNetworks", - Plural: "meshnetworks", - Version: "v1alpha1", - Proto: "istio.mesh.v1alpha1.MeshNetworks", - ReflectType: reflect.TypeOf(&istioioapimeshv1alpha1.MeshNetworks{}).Elem(), - ProtoPackage: "istio.io/api/mesh/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // IstioNetworkingV1Alpha3Destinationrules describes the collection - // istio/networking/v1alpha3/destinationrules - IstioNetworkingV1Alpha3Destinationrules = collection.Builder{ - Name: "istio/networking/v1alpha3/destinationrules", - VariableName: "IstioNetworkingV1Alpha3Destinationrules", - Resource: resource.Builder{ - Group: "networking.istio.io", - Kind: "DestinationRule", - Plural: "destinationrules", - Version: "v1alpha3", - Proto: "istio.networking.v1alpha3.DestinationRule", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapinetworkingv1alpha3.DestinationRule{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/networking/v1alpha3", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateDestinationRule, - }.MustBuild(), - }.MustBuild() - - // IstioNetworkingV1Alpha3Envoyfilters describes the collection - // istio/networking/v1alpha3/envoyfilters - IstioNetworkingV1Alpha3Envoyfilters = collection.Builder{ - Name: "istio/networking/v1alpha3/envoyfilters", - VariableName: "IstioNetworkingV1Alpha3Envoyfilters", - Resource: resource.Builder{ - Group: "networking.istio.io", - Kind: "EnvoyFilter", - Plural: "envoyfilters", - Version: "v1alpha3", - Proto: "istio.networking.v1alpha3.EnvoyFilter", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapinetworkingv1alpha3.EnvoyFilter{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/networking/v1alpha3", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateEnvoyFilter, - }.MustBuild(), - }.MustBuild() - - // IstioNetworkingV1Alpha3Gateways describes the collection - // istio/networking/v1alpha3/gateways - IstioNetworkingV1Alpha3Gateways = collection.Builder{ - Name: "istio/networking/v1alpha3/gateways", - VariableName: "IstioNetworkingV1Alpha3Gateways", - Resource: resource.Builder{ - Group: "networking.istio.io", - Kind: "Gateway", - Plural: "gateways", - Version: "v1alpha3", - Proto: "istio.networking.v1alpha3.Gateway", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapinetworkingv1alpha3.Gateway{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/networking/v1alpha3", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateGateway, - }.MustBuild(), - }.MustBuild() - - // IstioNetworkingV1Alpha3Serviceentries describes the collection - // istio/networking/v1alpha3/serviceentries - IstioNetworkingV1Alpha3Serviceentries = collection.Builder{ - Name: "istio/networking/v1alpha3/serviceentries", - VariableName: "IstioNetworkingV1Alpha3Serviceentries", - Resource: resource.Builder{ - Group: "networking.istio.io", - Kind: "ServiceEntry", - Plural: "serviceentries", - Version: "v1alpha3", - Proto: "istio.networking.v1alpha3.ServiceEntry", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapinetworkingv1alpha3.ServiceEntry{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/networking/v1alpha3", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateServiceEntry, - }.MustBuild(), - }.MustBuild() - - // IstioNetworkingV1Alpha3Sidecars describes the collection - // istio/networking/v1alpha3/sidecars - IstioNetworkingV1Alpha3Sidecars = collection.Builder{ - Name: "istio/networking/v1alpha3/sidecars", - VariableName: "IstioNetworkingV1Alpha3Sidecars", - Resource: resource.Builder{ - Group: "networking.istio.io", - Kind: "Sidecar", - Plural: "sidecars", - Version: "v1alpha3", - Proto: "istio.networking.v1alpha3.Sidecar", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapinetworkingv1alpha3.Sidecar{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/networking/v1alpha3", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateSidecar, - }.MustBuild(), - }.MustBuild() - - // IstioNetworkingV1Alpha3Virtualservices describes the collection - // istio/networking/v1alpha3/virtualservices - IstioNetworkingV1Alpha3Virtualservices = collection.Builder{ - Name: "istio/networking/v1alpha3/virtualservices", - VariableName: "IstioNetworkingV1Alpha3Virtualservices", - Resource: resource.Builder{ - Group: "networking.istio.io", - Kind: "VirtualService", - Plural: "virtualservices", - Version: "v1alpha3", - Proto: "istio.networking.v1alpha3.VirtualService", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapinetworkingv1alpha3.VirtualService{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/networking/v1alpha3", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateVirtualService, - }.MustBuild(), - }.MustBuild() - - // IstioNetworkingV1Alpha3Workloadentries describes the collection - // istio/networking/v1alpha3/workloadentries - IstioNetworkingV1Alpha3Workloadentries = collection.Builder{ - Name: "istio/networking/v1alpha3/workloadentries", - VariableName: "IstioNetworkingV1Alpha3Workloadentries", - Resource: resource.Builder{ - Group: "networking.istio.io", - Kind: "WorkloadEntry", - Plural: "workloadentries", - Version: "v1alpha3", - Proto: "istio.networking.v1alpha3.WorkloadEntry", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapinetworkingv1alpha3.WorkloadEntry{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/networking/v1alpha3", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateWorkloadEntry, - }.MustBuild(), - }.MustBuild() - - // IstioNetworkingV1Alpha3Workloadgroups describes the collection - // istio/networking/v1alpha3/workloadgroups - IstioNetworkingV1Alpha3Workloadgroups = collection.Builder{ - Name: "istio/networking/v1alpha3/workloadgroups", - VariableName: "IstioNetworkingV1Alpha3Workloadgroups", - Resource: resource.Builder{ - Group: "networking.istio.io", - Kind: "WorkloadGroup", - Plural: "workloadgroups", - Version: "v1alpha3", - Proto: "istio.networking.v1alpha3.WorkloadGroup", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapinetworkingv1alpha3.WorkloadGroup{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/networking/v1alpha3", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateWorkloadGroup, - }.MustBuild(), - }.MustBuild() - - // IstioNetworkingV1Beta1Proxyconfigs describes the collection - // istio/networking/v1beta1/proxyconfigs - IstioNetworkingV1Beta1Proxyconfigs = collection.Builder{ - Name: "istio/networking/v1beta1/proxyconfigs", - VariableName: "IstioNetworkingV1Beta1Proxyconfigs", - Resource: resource.Builder{ - Group: "networking.istio.io", - Kind: "ProxyConfig", - Plural: "proxyconfigs", - Version: "v1beta1", - Proto: "istio.networking.v1beta1.ProxyConfig", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapinetworkingv1beta1.ProxyConfig{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/networking/v1beta1", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateProxyConfig, - }.MustBuild(), - }.MustBuild() - - // IstioSecurityV1Beta1Authorizationpolicies describes the collection - // istio/security/v1beta1/authorizationpolicies - IstioSecurityV1Beta1Authorizationpolicies = collection.Builder{ - Name: "istio/security/v1beta1/authorizationpolicies", - VariableName: "IstioSecurityV1Beta1Authorizationpolicies", - Resource: resource.Builder{ - Group: "security.istio.io", - Kind: "AuthorizationPolicy", - Plural: "authorizationpolicies", - Version: "v1beta1", - Proto: "istio.security.v1beta1.AuthorizationPolicy", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapisecurityv1beta1.AuthorizationPolicy{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/security/v1beta1", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateAuthorizationPolicy, - }.MustBuild(), - }.MustBuild() - - // IstioSecurityV1Beta1Peerauthentications describes the collection - // istio/security/v1beta1/peerauthentications - IstioSecurityV1Beta1Peerauthentications = collection.Builder{ - Name: "istio/security/v1beta1/peerauthentications", - VariableName: "IstioSecurityV1Beta1Peerauthentications", - Resource: resource.Builder{ - Group: "security.istio.io", - Kind: "PeerAuthentication", - Plural: "peerauthentications", - Version: "v1beta1", - Proto: "istio.security.v1beta1.PeerAuthentication", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapisecurityv1beta1.PeerAuthentication{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/security/v1beta1", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidatePeerAuthentication, - }.MustBuild(), - }.MustBuild() - - // IstioSecurityV1Beta1Requestauthentications describes the collection - // istio/security/v1beta1/requestauthentications - IstioSecurityV1Beta1Requestauthentications = collection.Builder{ - Name: "istio/security/v1beta1/requestauthentications", - VariableName: "IstioSecurityV1Beta1Requestauthentications", - Resource: resource.Builder{ - Group: "security.istio.io", - Kind: "RequestAuthentication", - Plural: "requestauthentications", - Version: "v1beta1", - Proto: "istio.security.v1beta1.RequestAuthentication", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapisecurityv1beta1.RequestAuthentication{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/security/v1beta1", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateRequestAuthentication, - }.MustBuild(), - }.MustBuild() - - // IstioTelemetryV1Alpha1Telemetries describes the collection - // istio/telemetry/v1alpha1/telemetries - IstioTelemetryV1Alpha1Telemetries = collection.Builder{ - Name: "istio/telemetry/v1alpha1/telemetries", - VariableName: "IstioTelemetryV1Alpha1Telemetries", - Resource: resource.Builder{ - Group: "telemetry.istio.io", - Kind: "Telemetry", - Plural: "telemetries", - Version: "v1alpha1", - Proto: "istio.telemetry.v1alpha1.Telemetry", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapitelemetryv1alpha1.Telemetry{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/telemetry/v1alpha1", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateTelemetry, - }.MustBuild(), - }.MustBuild() - - // All contains all collections in the system. - All = collection.NewSchemasBuilder(). - MustAdd(IstioExtensionsV1Alpha1Servicemetadatas). - MustAdd(IstioExtensionsV1Alpha1Servicenamemappings). - MustAdd(IstioExtensionsV1Alpha1Wasmplugins). - MustAdd(IstioMeshV1Alpha1MeshConfig). - MustAdd(IstioMeshV1Alpha1MeshNetworks). - MustAdd(IstioNetworkingV1Alpha3Destinationrules). - MustAdd(IstioNetworkingV1Alpha3Envoyfilters). - MustAdd(IstioNetworkingV1Alpha3Gateways). - MustAdd(IstioNetworkingV1Alpha3Serviceentries). - MustAdd(IstioNetworkingV1Alpha3Sidecars). - MustAdd(IstioNetworkingV1Alpha3Virtualservices). - MustAdd(IstioNetworkingV1Alpha3Workloadentries). - MustAdd(IstioNetworkingV1Alpha3Workloadgroups). - MustAdd(IstioNetworkingV1Beta1Proxyconfigs). - MustAdd(IstioSecurityV1Beta1Authorizationpolicies). - MustAdd(IstioSecurityV1Beta1Peerauthentications). - MustAdd(IstioSecurityV1Beta1Requestauthentications). - MustAdd(IstioTelemetryV1Alpha1Telemetries). - Build() - - // Istio contains only Istio collections. - Istio = collection.NewSchemasBuilder(). - MustAdd(IstioExtensionsV1Alpha1Servicemetadatas). - MustAdd(IstioExtensionsV1Alpha1Servicenamemappings). - MustAdd(IstioExtensionsV1Alpha1Wasmplugins). - MustAdd(IstioMeshV1Alpha1MeshConfig). - MustAdd(IstioMeshV1Alpha1MeshNetworks). - MustAdd(IstioNetworkingV1Alpha3Destinationrules). - MustAdd(IstioNetworkingV1Alpha3Envoyfilters). - MustAdd(IstioNetworkingV1Alpha3Gateways). - MustAdd(IstioNetworkingV1Alpha3Serviceentries). - MustAdd(IstioNetworkingV1Alpha3Sidecars). - MustAdd(IstioNetworkingV1Alpha3Virtualservices). - MustAdd(IstioNetworkingV1Alpha3Workloadentries). - MustAdd(IstioNetworkingV1Alpha3Workloadgroups). - MustAdd(IstioNetworkingV1Beta1Proxyconfigs). - MustAdd(IstioSecurityV1Beta1Authorizationpolicies). - MustAdd(IstioSecurityV1Beta1Peerauthentications). - MustAdd(IstioSecurityV1Beta1Requestauthentications). - MustAdd(IstioTelemetryV1Alpha1Telemetries). - Build() - - // Kube contains only kubernetes collections. - Kube = collection.NewSchemasBuilder(). - Build() - - // Builtin contains only native Kubernetes collections. This differs from Kube, which has - // Kubernetes controlled CRDs - Builtin = collection.NewSchemasBuilder(). - Build() - - // Pilot contains only collections used by Pilot. - Pilot = collection.NewSchemasBuilder(). - MustAdd(IstioExtensionsV1Alpha1Servicemetadatas). - MustAdd(IstioExtensionsV1Alpha1Servicenamemappings). - MustAdd(IstioExtensionsV1Alpha1Wasmplugins). - MustAdd(IstioNetworkingV1Alpha3Destinationrules). - MustAdd(IstioNetworkingV1Alpha3Envoyfilters). - MustAdd(IstioNetworkingV1Alpha3Gateways). - MustAdd(IstioNetworkingV1Alpha3Serviceentries). - MustAdd(IstioNetworkingV1Alpha3Sidecars). - MustAdd(IstioNetworkingV1Alpha3Virtualservices). - MustAdd(IstioNetworkingV1Alpha3Workloadentries). - MustAdd(IstioNetworkingV1Alpha3Workloadgroups). - MustAdd(IstioNetworkingV1Beta1Proxyconfigs). - MustAdd(IstioSecurityV1Beta1Authorizationpolicies). - MustAdd(IstioSecurityV1Beta1Peerauthentications). - MustAdd(IstioSecurityV1Beta1Requestauthentications). - MustAdd(IstioTelemetryV1Alpha1Telemetries). - Build() - - // PilotGatewayAPI contains only collections used by Pilot, including experimental Service Api. - PilotGatewayAPI = collection.NewSchemasBuilder(). - MustAdd(IstioExtensionsV1Alpha1Servicemetadatas). - MustAdd(IstioExtensionsV1Alpha1Servicenamemappings). - MustAdd(IstioExtensionsV1Alpha1Wasmplugins). - MustAdd(IstioNetworkingV1Alpha3Destinationrules). - MustAdd(IstioNetworkingV1Alpha3Envoyfilters). - MustAdd(IstioNetworkingV1Alpha3Gateways). - MustAdd(IstioNetworkingV1Alpha3Serviceentries). - MustAdd(IstioNetworkingV1Alpha3Sidecars). - MustAdd(IstioNetworkingV1Alpha3Virtualservices). - MustAdd(IstioNetworkingV1Alpha3Workloadentries). - MustAdd(IstioNetworkingV1Alpha3Workloadgroups). - MustAdd(IstioNetworkingV1Beta1Proxyconfigs). - MustAdd(IstioSecurityV1Beta1Authorizationpolicies). - MustAdd(IstioSecurityV1Beta1Peerauthentications). - MustAdd(IstioSecurityV1Beta1Requestauthentications). - MustAdd(IstioTelemetryV1Alpha1Telemetries). - Build() - - // Deprecated contains only collections used by that will soon be used by nothing. - Deprecated = collection.NewSchemasBuilder(). - Build() -) diff --git a/pkg/config/schema/collections/collections.gen.go b/pkg/config/schema/collections/collections.gen.go deleted file mode 100755 index 45fa0bb99..000000000 --- a/pkg/config/schema/collections/collections.gen.go +++ /dev/null @@ -1,807 +0,0 @@ -//go:build !agent -// +build !agent - -// GENERATED FILE -- DO NOT EDIT -// - -package collections - -import ( - "reflect" -) - -import ( - istioioapiextensionsv1alpha1 "istio.io/api/extensions/v1alpha1" - istioioapimeshv1alpha1 "istio.io/api/mesh/v1alpha1" - istioioapimetav1alpha1 "istio.io/api/meta/v1alpha1" - istioioapinetworkingv1alpha3 "istio.io/api/networking/v1alpha3" - istioioapinetworkingv1beta1 "istio.io/api/networking/v1beta1" - istioioapisecurityv1beta1 "istio.io/api/security/v1beta1" - istioioapitelemetryv1alpha1 "istio.io/api/telemetry/v1alpha1" - k8sioapiadmissionregistrationv1 "k8s.io/api/admissionregistration/v1" - k8sioapiappsv1 "k8s.io/api/apps/v1" - k8sioapicorev1 "k8s.io/api/core/v1" - k8sioapiextensionsv1beta1 "k8s.io/api/extensions/v1beta1" - k8sioapiextensionsapiserverpkgapisapiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - sigsk8siogatewayapiapisv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/validation" -) - -var ( - - // IstioExtensionsV1Alpha1Servicemetadatas describes the collection - // istio/extensions/v1alpha1/servicemetadatas - IstioExtensionsV1Alpha1Servicemetadatas = collection.Builder{ - Name: "istio/extensions/v1alpha1/servicemetadatas", - VariableName: "IstioExtensionsV1Alpha1Servicemetadatas", - Resource: resource.Builder{ - Group: "extensions.istio.io", - Kind: "ServiceMetadata", - Plural: "servicemetadatas", - Version: "v1alpha1", - Proto: "istio.extensions.v1alpha1.ServiceMetadata", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapiextensionsv1alpha1.ServiceMetadata{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/extensions/v1alpha1", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // IstioExtensionsV1Alpha1Servicenamemappings describes the collection - // istio/extensions/v1alpha1/servicenamemappings - IstioExtensionsV1Alpha1Servicenamemappings = collection.Builder{ - Name: "istio/extensions/v1alpha1/servicenamemappings", - VariableName: "IstioExtensionsV1Alpha1Servicenamemappings", - Resource: resource.Builder{ - Group: "extensions.istio.io", - Kind: "ServiceNameMapping", - Plural: "servicenamemappings", - Version: "v1alpha1", - Proto: "istio.extensions.v1alpha1.ServiceNameMapping", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapiextensionsv1alpha1.ServiceNameMapping{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/extensions/v1alpha1", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // IstioExtensionsV1Alpha1Wasmplugins describes the collection - // istio/extensions/v1alpha1/wasmplugins - IstioExtensionsV1Alpha1Wasmplugins = collection.Builder{ - Name: "istio/extensions/v1alpha1/wasmplugins", - VariableName: "IstioExtensionsV1Alpha1Wasmplugins", - Resource: resource.Builder{ - Group: "extensions.istio.io", - Kind: "WasmPlugin", - Plural: "wasmplugins", - Version: "v1alpha1", - Proto: "istio.extensions.v1alpha1.WasmPlugin", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapiextensionsv1alpha1.WasmPlugin{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/extensions/v1alpha1", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateWasmPlugin, - }.MustBuild(), - }.MustBuild() - - // IstioMeshV1Alpha1MeshConfig describes the collection - // istio/mesh/v1alpha1/MeshConfig - IstioMeshV1Alpha1MeshConfig = collection.Builder{ - Name: "istio/mesh/v1alpha1/MeshConfig", - VariableName: "IstioMeshV1Alpha1MeshConfig", - Resource: resource.Builder{ - Group: "", - Kind: "MeshConfig", - Plural: "meshconfigs", - Version: "v1alpha1", - Proto: "istio.mesh.v1alpha1.MeshConfig", - ReflectType: reflect.TypeOf(&istioioapimeshv1alpha1.MeshConfig{}).Elem(), - ProtoPackage: "istio.io/api/mesh/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // IstioMeshV1Alpha1MeshNetworks describes the collection - // istio/mesh/v1alpha1/MeshNetworks - IstioMeshV1Alpha1MeshNetworks = collection.Builder{ - Name: "istio/mesh/v1alpha1/MeshNetworks", - VariableName: "IstioMeshV1Alpha1MeshNetworks", - Resource: resource.Builder{ - Group: "", - Kind: "MeshNetworks", - Plural: "meshnetworks", - Version: "v1alpha1", - Proto: "istio.mesh.v1alpha1.MeshNetworks", - ReflectType: reflect.TypeOf(&istioioapimeshv1alpha1.MeshNetworks{}).Elem(), - ProtoPackage: "istio.io/api/mesh/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // IstioNetworkingV1Alpha3Destinationrules describes the collection - // istio/networking/v1alpha3/destinationrules - IstioNetworkingV1Alpha3Destinationrules = collection.Builder{ - Name: "istio/networking/v1alpha3/destinationrules", - VariableName: "IstioNetworkingV1Alpha3Destinationrules", - Resource: resource.Builder{ - Group: "networking.istio.io", - Kind: "DestinationRule", - Plural: "destinationrules", - Version: "v1alpha3", - Proto: "istio.networking.v1alpha3.DestinationRule", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapinetworkingv1alpha3.DestinationRule{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/networking/v1alpha3", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateDestinationRule, - }.MustBuild(), - }.MustBuild() - - // IstioNetworkingV1Alpha3Envoyfilters describes the collection - // istio/networking/v1alpha3/envoyfilters - IstioNetworkingV1Alpha3Envoyfilters = collection.Builder{ - Name: "istio/networking/v1alpha3/envoyfilters", - VariableName: "IstioNetworkingV1Alpha3Envoyfilters", - Resource: resource.Builder{ - Group: "networking.istio.io", - Kind: "EnvoyFilter", - Plural: "envoyfilters", - Version: "v1alpha3", - Proto: "istio.networking.v1alpha3.EnvoyFilter", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapinetworkingv1alpha3.EnvoyFilter{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/networking/v1alpha3", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateEnvoyFilter, - }.MustBuild(), - }.MustBuild() - - // IstioNetworkingV1Alpha3Gateways describes the collection - // istio/networking/v1alpha3/gateways - IstioNetworkingV1Alpha3Gateways = collection.Builder{ - Name: "istio/networking/v1alpha3/gateways", - VariableName: "IstioNetworkingV1Alpha3Gateways", - Resource: resource.Builder{ - Group: "networking.istio.io", - Kind: "Gateway", - Plural: "gateways", - Version: "v1alpha3", - Proto: "istio.networking.v1alpha3.Gateway", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapinetworkingv1alpha3.Gateway{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/networking/v1alpha3", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateGateway, - }.MustBuild(), - }.MustBuild() - - // IstioNetworkingV1Alpha3Serviceentries describes the collection - // istio/networking/v1alpha3/serviceentries - IstioNetworkingV1Alpha3Serviceentries = collection.Builder{ - Name: "istio/networking/v1alpha3/serviceentries", - VariableName: "IstioNetworkingV1Alpha3Serviceentries", - Resource: resource.Builder{ - Group: "networking.istio.io", - Kind: "ServiceEntry", - Plural: "serviceentries", - Version: "v1alpha3", - Proto: "istio.networking.v1alpha3.ServiceEntry", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapinetworkingv1alpha3.ServiceEntry{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/networking/v1alpha3", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateServiceEntry, - }.MustBuild(), - }.MustBuild() - - // IstioNetworkingV1Alpha3Sidecars describes the collection - // istio/networking/v1alpha3/sidecars - IstioNetworkingV1Alpha3Sidecars = collection.Builder{ - Name: "istio/networking/v1alpha3/sidecars", - VariableName: "IstioNetworkingV1Alpha3Sidecars", - Resource: resource.Builder{ - Group: "networking.istio.io", - Kind: "Sidecar", - Plural: "sidecars", - Version: "v1alpha3", - Proto: "istio.networking.v1alpha3.Sidecar", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapinetworkingv1alpha3.Sidecar{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/networking/v1alpha3", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateSidecar, - }.MustBuild(), - }.MustBuild() - - // IstioNetworkingV1Alpha3Virtualservices describes the collection - // istio/networking/v1alpha3/virtualservices - IstioNetworkingV1Alpha3Virtualservices = collection.Builder{ - Name: "istio/networking/v1alpha3/virtualservices", - VariableName: "IstioNetworkingV1Alpha3Virtualservices", - Resource: resource.Builder{ - Group: "networking.istio.io", - Kind: "VirtualService", - Plural: "virtualservices", - Version: "v1alpha3", - Proto: "istio.networking.v1alpha3.VirtualService", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapinetworkingv1alpha3.VirtualService{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/networking/v1alpha3", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateVirtualService, - }.MustBuild(), - }.MustBuild() - - // IstioNetworkingV1Alpha3Workloadentries describes the collection - // istio/networking/v1alpha3/workloadentries - IstioNetworkingV1Alpha3Workloadentries = collection.Builder{ - Name: "istio/networking/v1alpha3/workloadentries", - VariableName: "IstioNetworkingV1Alpha3Workloadentries", - Resource: resource.Builder{ - Group: "networking.istio.io", - Kind: "WorkloadEntry", - Plural: "workloadentries", - Version: "v1alpha3", - Proto: "istio.networking.v1alpha3.WorkloadEntry", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapinetworkingv1alpha3.WorkloadEntry{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/networking/v1alpha3", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateWorkloadEntry, - }.MustBuild(), - }.MustBuild() - - // IstioNetworkingV1Alpha3Workloadgroups describes the collection - // istio/networking/v1alpha3/workloadgroups - IstioNetworkingV1Alpha3Workloadgroups = collection.Builder{ - Name: "istio/networking/v1alpha3/workloadgroups", - VariableName: "IstioNetworkingV1Alpha3Workloadgroups", - Resource: resource.Builder{ - Group: "networking.istio.io", - Kind: "WorkloadGroup", - Plural: "workloadgroups", - Version: "v1alpha3", - Proto: "istio.networking.v1alpha3.WorkloadGroup", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapinetworkingv1alpha3.WorkloadGroup{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/networking/v1alpha3", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateWorkloadGroup, - }.MustBuild(), - }.MustBuild() - - // IstioNetworkingV1Beta1Proxyconfigs describes the collection - // istio/networking/v1beta1/proxyconfigs - IstioNetworkingV1Beta1Proxyconfigs = collection.Builder{ - Name: "istio/networking/v1beta1/proxyconfigs", - VariableName: "IstioNetworkingV1Beta1Proxyconfigs", - Resource: resource.Builder{ - Group: "networking.istio.io", - Kind: "ProxyConfig", - Plural: "proxyconfigs", - Version: "v1beta1", - Proto: "istio.networking.v1beta1.ProxyConfig", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapinetworkingv1beta1.ProxyConfig{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/networking/v1beta1", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateProxyConfig, - }.MustBuild(), - }.MustBuild() - - // IstioSecurityV1Beta1Authorizationpolicies describes the collection - // istio/security/v1beta1/authorizationpolicies - IstioSecurityV1Beta1Authorizationpolicies = collection.Builder{ - Name: "istio/security/v1beta1/authorizationpolicies", - VariableName: "IstioSecurityV1Beta1Authorizationpolicies", - Resource: resource.Builder{ - Group: "security.istio.io", - Kind: "AuthorizationPolicy", - Plural: "authorizationpolicies", - Version: "v1beta1", - Proto: "istio.security.v1beta1.AuthorizationPolicy", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapisecurityv1beta1.AuthorizationPolicy{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/security/v1beta1", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateAuthorizationPolicy, - }.MustBuild(), - }.MustBuild() - - // IstioSecurityV1Beta1Peerauthentications describes the collection - // istio/security/v1beta1/peerauthentications - IstioSecurityV1Beta1Peerauthentications = collection.Builder{ - Name: "istio/security/v1beta1/peerauthentications", - VariableName: "IstioSecurityV1Beta1Peerauthentications", - Resource: resource.Builder{ - Group: "security.istio.io", - Kind: "PeerAuthentication", - Plural: "peerauthentications", - Version: "v1beta1", - Proto: "istio.security.v1beta1.PeerAuthentication", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapisecurityv1beta1.PeerAuthentication{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/security/v1beta1", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidatePeerAuthentication, - }.MustBuild(), - }.MustBuild() - - // IstioSecurityV1Beta1Requestauthentications describes the collection - // istio/security/v1beta1/requestauthentications - IstioSecurityV1Beta1Requestauthentications = collection.Builder{ - Name: "istio/security/v1beta1/requestauthentications", - VariableName: "IstioSecurityV1Beta1Requestauthentications", - Resource: resource.Builder{ - Group: "security.istio.io", - Kind: "RequestAuthentication", - Plural: "requestauthentications", - Version: "v1beta1", - Proto: "istio.security.v1beta1.RequestAuthentication", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapisecurityv1beta1.RequestAuthentication{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/security/v1beta1", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateRequestAuthentication, - }.MustBuild(), - }.MustBuild() - - // IstioTelemetryV1Alpha1Telemetries describes the collection - // istio/telemetry/v1alpha1/telemetries - IstioTelemetryV1Alpha1Telemetries = collection.Builder{ - Name: "istio/telemetry/v1alpha1/telemetries", - VariableName: "IstioTelemetryV1Alpha1Telemetries", - Resource: resource.Builder{ - Group: "telemetry.istio.io", - Kind: "Telemetry", - Plural: "telemetries", - Version: "v1alpha1", - Proto: "istio.telemetry.v1alpha1.Telemetry", StatusProto: "istio.meta.v1alpha1.IstioStatus", - ReflectType: reflect.TypeOf(&istioioapitelemetryv1alpha1.Telemetry{}).Elem(), StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(), - ProtoPackage: "istio.io/api/telemetry/v1alpha1", StatusPackage: "istio.io/api/meta/v1alpha1", - ClusterScoped: false, - ValidateProto: validation.ValidateTelemetry, - }.MustBuild(), - }.MustBuild() - - // K8SAdmissionregistrationK8SIoV1Mutatingwebhookconfigurations describes - // the collection - // k8s/admissionregistration.k8s.io/v1/mutatingwebhookconfigurations - K8SAdmissionregistrationK8SIoV1Mutatingwebhookconfigurations = collection.Builder{ - Name: "k8s/admissionregistration.k8s.io/v1/mutatingwebhookconfigurations", - VariableName: "K8SAdmissionregistrationK8SIoV1Mutatingwebhookconfigurations", - Resource: resource.Builder{ - Group: "admissionregistration.k8s.io", - Kind: "MutatingWebhookConfiguration", - Plural: "mutatingwebhookconfigurations", - Version: "v1", - Proto: "k8s.io.api.admissionregistration.v1.MutatingWebhookConfiguration", - ReflectType: reflect.TypeOf(&k8sioapiadmissionregistrationv1.MutatingWebhookConfiguration{}).Elem(), - ProtoPackage: "k8s.io/api/admissionregistration/v1", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // K8SApiextensionsK8SIoV1Customresourcedefinitions describes the - // collection k8s/apiextensions.k8s.io/v1/customresourcedefinitions - K8SApiextensionsK8SIoV1Customresourcedefinitions = collection.Builder{ - Name: "k8s/apiextensions.k8s.io/v1/customresourcedefinitions", - VariableName: "K8SApiextensionsK8SIoV1Customresourcedefinitions", - Resource: resource.Builder{ - Group: "apiextensions.k8s.io", - Kind: "CustomResourceDefinition", - Plural: "customresourcedefinitions", - Version: "v1", - Proto: "k8s.io.apiextensions_apiserver.pkg.apis.apiextensions.v1.CustomResourceDefinition", - ReflectType: reflect.TypeOf(&k8sioapiextensionsapiserverpkgapisapiextensionsv1.CustomResourceDefinition{}).Elem(), - ProtoPackage: "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // K8SAppsV1Deployments describes the collection k8s/apps/v1/deployments - K8SAppsV1Deployments = collection.Builder{ - Name: "k8s/apps/v1/deployments", - VariableName: "K8SAppsV1Deployments", - Resource: resource.Builder{ - Group: "apps", - Kind: "Deployment", - Plural: "deployments", - Version: "v1", - Proto: "k8s.io.api.apps.v1.DeploymentSpec", - ReflectType: reflect.TypeOf(&k8sioapiappsv1.DeploymentSpec{}).Elem(), - ProtoPackage: "k8s.io/api/apps/v1", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // K8SCoreV1Configmaps describes the collection k8s/core/v1/configmaps - K8SCoreV1Configmaps = collection.Builder{ - Name: "k8s/core/v1/configmaps", - VariableName: "K8SCoreV1Configmaps", - Resource: resource.Builder{ - Group: "", - Kind: "ConfigMap", - Plural: "configmaps", - Version: "v1", - Proto: "k8s.io.api.core.v1.ConfigMap", - ReflectType: reflect.TypeOf(&k8sioapicorev1.ConfigMap{}).Elem(), - ProtoPackage: "k8s.io/api/core/v1", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // K8SCoreV1Endpoints describes the collection k8s/core/v1/endpoints - K8SCoreV1Endpoints = collection.Builder{ - Name: "k8s/core/v1/endpoints", - VariableName: "K8SCoreV1Endpoints", - Resource: resource.Builder{ - Group: "", - Kind: "Endpoints", - Plural: "endpoints", - Version: "v1", - Proto: "k8s.io.api.core.v1.Endpoints", - ReflectType: reflect.TypeOf(&k8sioapicorev1.Endpoints{}).Elem(), - ProtoPackage: "k8s.io/api/core/v1", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // K8SCoreV1Namespaces describes the collection k8s/core/v1/namespaces - K8SCoreV1Namespaces = collection.Builder{ - Name: "k8s/core/v1/namespaces", - VariableName: "K8SCoreV1Namespaces", - Resource: resource.Builder{ - Group: "", - Kind: "Namespace", - Plural: "namespaces", - Version: "v1", - Proto: "k8s.io.api.core.v1.NamespaceSpec", - ReflectType: reflect.TypeOf(&k8sioapicorev1.NamespaceSpec{}).Elem(), - ProtoPackage: "k8s.io/api/core/v1", - ClusterScoped: true, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // K8SCoreV1Nodes describes the collection k8s/core/v1/nodes - K8SCoreV1Nodes = collection.Builder{ - Name: "k8s/core/v1/nodes", - VariableName: "K8SCoreV1Nodes", - Resource: resource.Builder{ - Group: "", - Kind: "Node", - Plural: "nodes", - Version: "v1", - Proto: "k8s.io.api.core.v1.NodeSpec", - ReflectType: reflect.TypeOf(&k8sioapicorev1.NodeSpec{}).Elem(), - ProtoPackage: "k8s.io/api/core/v1", - ClusterScoped: true, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // K8SCoreV1Pods describes the collection k8s/core/v1/pods - K8SCoreV1Pods = collection.Builder{ - Name: "k8s/core/v1/pods", - VariableName: "K8SCoreV1Pods", - Resource: resource.Builder{ - Group: "", - Kind: "Pod", - Plural: "pods", - Version: "v1", - Proto: "k8s.io.api.core.v1.PodSpec", - ReflectType: reflect.TypeOf(&k8sioapicorev1.PodSpec{}).Elem(), - ProtoPackage: "k8s.io/api/core/v1", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // K8SCoreV1Secrets describes the collection k8s/core/v1/secrets - K8SCoreV1Secrets = collection.Builder{ - Name: "k8s/core/v1/secrets", - VariableName: "K8SCoreV1Secrets", - Resource: resource.Builder{ - Group: "", - Kind: "Secret", - Plural: "secrets", - Version: "v1", - Proto: "k8s.io.api.core.v1.Secret", - ReflectType: reflect.TypeOf(&k8sioapicorev1.Secret{}).Elem(), - ProtoPackage: "k8s.io/api/core/v1", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // K8SCoreV1Services describes the collection k8s/core/v1/services - K8SCoreV1Services = collection.Builder{ - Name: "k8s/core/v1/services", - VariableName: "K8SCoreV1Services", - Resource: resource.Builder{ - Group: "", - Kind: "Service", - Plural: "services", - Version: "v1", - Proto: "k8s.io.api.core.v1.ServiceSpec", - ReflectType: reflect.TypeOf(&k8sioapicorev1.ServiceSpec{}).Elem(), - ProtoPackage: "k8s.io/api/core/v1", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // K8SExtensionsV1Beta1Ingresses describes the collection - // k8s/extensions/v1beta1/ingresses - K8SExtensionsV1Beta1Ingresses = collection.Builder{ - Name: "k8s/extensions/v1beta1/ingresses", - VariableName: "K8SExtensionsV1Beta1Ingresses", - Resource: resource.Builder{ - Group: "extensions", - Kind: "Ingress", - Plural: "ingresses", - Version: "v1beta1", - Proto: "k8s.io.api.extensions.v1beta1.IngressSpec", StatusProto: "k8s.io.gateway_api.api.v1alpha1.IngressStatus", - ReflectType: reflect.TypeOf(&k8sioapiextensionsv1beta1.IngressSpec{}).Elem(), StatusType: reflect.TypeOf(&k8sioapiextensionsv1beta1.IngressStatus{}).Elem(), - ProtoPackage: "k8s.io/api/extensions/v1beta1", StatusPackage: "k8s.io/api/extensions/v1beta1", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // K8SGatewayApiV1Alpha2Gatewayclasses describes the collection - // k8s/gateway_api/v1alpha2/gatewayclasses - K8SGatewayApiV1Alpha2Gatewayclasses = collection.Builder{ - Name: "k8s/gateway_api/v1alpha2/gatewayclasses", - VariableName: "K8SGatewayApiV1Alpha2Gatewayclasses", - Resource: resource.Builder{ - Group: "gateway.networking.k8s.io", - Kind: "GatewayClass", - Plural: "gatewayclasses", - Version: "v1alpha2", - Proto: "k8s.io.gateway_api.api.v1alpha1.GatewayClassSpec", StatusProto: "k8s.io.gateway_api.api.v1alpha1.GatewayClassStatus", - ReflectType: reflect.TypeOf(&sigsk8siogatewayapiapisv1alpha2.GatewayClassSpec{}).Elem(), StatusType: reflect.TypeOf(&sigsk8siogatewayapiapisv1alpha2.GatewayClassStatus{}).Elem(), - ProtoPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2", StatusPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2", - ClusterScoped: true, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // K8SGatewayApiV1Alpha2Gateways describes the collection - // k8s/gateway_api/v1alpha2/gateways - K8SGatewayApiV1Alpha2Gateways = collection.Builder{ - Name: "k8s/gateway_api/v1alpha2/gateways", - VariableName: "K8SGatewayApiV1Alpha2Gateways", - Resource: resource.Builder{ - Group: "gateway.networking.k8s.io", - Kind: "Gateway", - Plural: "gateways", - Version: "v1alpha2", - Proto: "k8s.io.gateway_api.api.v1alpha1.GatewaySpec", StatusProto: "k8s.io.gateway_api.api.v1alpha1.GatewayStatus", - ReflectType: reflect.TypeOf(&sigsk8siogatewayapiapisv1alpha2.GatewaySpec{}).Elem(), StatusType: reflect.TypeOf(&sigsk8siogatewayapiapisv1alpha2.GatewayStatus{}).Elem(), - ProtoPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2", StatusPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // K8SGatewayApiV1Alpha2Httproutes describes the collection - // k8s/gateway_api/v1alpha2/httproutes - K8SGatewayApiV1Alpha2Httproutes = collection.Builder{ - Name: "k8s/gateway_api/v1alpha2/httproutes", - VariableName: "K8SGatewayApiV1Alpha2Httproutes", - Resource: resource.Builder{ - Group: "gateway.networking.k8s.io", - Kind: "HTTPRoute", - Plural: "httproutes", - Version: "v1alpha2", - Proto: "k8s.io.gateway_api.api.v1alpha1.HTTPRouteSpec", StatusProto: "k8s.io.gateway_api.api.v1alpha1.HTTPRouteStatus", - ReflectType: reflect.TypeOf(&sigsk8siogatewayapiapisv1alpha2.HTTPRouteSpec{}).Elem(), StatusType: reflect.TypeOf(&sigsk8siogatewayapiapisv1alpha2.HTTPRouteStatus{}).Elem(), - ProtoPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2", StatusPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // K8SGatewayApiV1Alpha2Referencepolicies describes the collection - // k8s/gateway_api/v1alpha2/referencepolicies - K8SGatewayApiV1Alpha2Referencepolicies = collection.Builder{ - Name: "k8s/gateway_api/v1alpha2/referencepolicies", - VariableName: "K8SGatewayApiV1Alpha2Referencepolicies", - Resource: resource.Builder{ - Group: "gateway.networking.k8s.io", - Kind: "ReferencePolicy", - Plural: "referencepolicies", - Version: "v1alpha2", - Proto: "k8s.io.gateway_api.api.v1alpha1.ReferencePolicySpec", - ReflectType: reflect.TypeOf(&sigsk8siogatewayapiapisv1alpha2.ReferencePolicySpec{}).Elem(), - ProtoPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // K8SGatewayApiV1Alpha2Tcproutes describes the collection - // k8s/gateway_api/v1alpha2/tcproutes - K8SGatewayApiV1Alpha2Tcproutes = collection.Builder{ - Name: "k8s/gateway_api/v1alpha2/tcproutes", - VariableName: "K8SGatewayApiV1Alpha2Tcproutes", - Resource: resource.Builder{ - Group: "gateway.networking.k8s.io", - Kind: "TCPRoute", - Plural: "tcproutes", - Version: "v1alpha2", - Proto: "k8s.io.gateway_api.api.v1alpha1.TCPRouteSpec", StatusProto: "k8s.io.gateway_api.api.v1alpha1.TCPRouteStatus", - ReflectType: reflect.TypeOf(&sigsk8siogatewayapiapisv1alpha2.TCPRouteSpec{}).Elem(), StatusType: reflect.TypeOf(&sigsk8siogatewayapiapisv1alpha2.TCPRouteStatus{}).Elem(), - ProtoPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2", StatusPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // K8SGatewayApiV1Alpha2Tlsroutes describes the collection - // k8s/gateway_api/v1alpha2/tlsroutes - K8SGatewayApiV1Alpha2Tlsroutes = collection.Builder{ - Name: "k8s/gateway_api/v1alpha2/tlsroutes", - VariableName: "K8SGatewayApiV1Alpha2Tlsroutes", - Resource: resource.Builder{ - Group: "gateway.networking.k8s.io", - Kind: "TLSRoute", - Plural: "tlsroutes", - Version: "v1alpha2", - Proto: "k8s.io.gateway_api.api.v1alpha1.TLSRouteSpec", StatusProto: "k8s.io.gateway_api.api.v1alpha1.TLSRouteStatus", - ReflectType: reflect.TypeOf(&sigsk8siogatewayapiapisv1alpha2.TLSRouteSpec{}).Elem(), StatusType: reflect.TypeOf(&sigsk8siogatewayapiapisv1alpha2.TLSRouteStatus{}).Elem(), - ProtoPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2", StatusPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2", - ClusterScoped: false, - ValidateProto: validation.EmptyValidate, - }.MustBuild(), - }.MustBuild() - - // All contains all collections in the system. - All = collection.NewSchemasBuilder(). - MustAdd(IstioExtensionsV1Alpha1Servicemetadatas). - MustAdd(IstioExtensionsV1Alpha1Servicenamemappings). - MustAdd(IstioExtensionsV1Alpha1Wasmplugins). - MustAdd(IstioMeshV1Alpha1MeshConfig). - MustAdd(IstioMeshV1Alpha1MeshNetworks). - MustAdd(IstioNetworkingV1Alpha3Destinationrules). - MustAdd(IstioNetworkingV1Alpha3Envoyfilters). - MustAdd(IstioNetworkingV1Alpha3Gateways). - MustAdd(IstioNetworkingV1Alpha3Serviceentries). - MustAdd(IstioNetworkingV1Alpha3Sidecars). - MustAdd(IstioNetworkingV1Alpha3Virtualservices). - MustAdd(IstioNetworkingV1Alpha3Workloadentries). - MustAdd(IstioNetworkingV1Alpha3Workloadgroups). - MustAdd(IstioNetworkingV1Beta1Proxyconfigs). - MustAdd(IstioSecurityV1Beta1Authorizationpolicies). - MustAdd(IstioSecurityV1Beta1Peerauthentications). - MustAdd(IstioSecurityV1Beta1Requestauthentications). - MustAdd(IstioTelemetryV1Alpha1Telemetries). - MustAdd(K8SAdmissionregistrationK8SIoV1Mutatingwebhookconfigurations). - MustAdd(K8SApiextensionsK8SIoV1Customresourcedefinitions). - MustAdd(K8SAppsV1Deployments). - MustAdd(K8SCoreV1Configmaps). - MustAdd(K8SCoreV1Endpoints). - MustAdd(K8SCoreV1Namespaces). - MustAdd(K8SCoreV1Nodes). - MustAdd(K8SCoreV1Pods). - MustAdd(K8SCoreV1Secrets). - MustAdd(K8SCoreV1Services). - MustAdd(K8SExtensionsV1Beta1Ingresses). - MustAdd(K8SGatewayApiV1Alpha2Gatewayclasses). - MustAdd(K8SGatewayApiV1Alpha2Gateways). - MustAdd(K8SGatewayApiV1Alpha2Httproutes). - MustAdd(K8SGatewayApiV1Alpha2Referencepolicies). - MustAdd(K8SGatewayApiV1Alpha2Tcproutes). - MustAdd(K8SGatewayApiV1Alpha2Tlsroutes). - Build() - - // Istio contains only Istio collections. - Istio = collection.NewSchemasBuilder(). - MustAdd(IstioExtensionsV1Alpha1Servicemetadatas). - MustAdd(IstioExtensionsV1Alpha1Servicenamemappings). - MustAdd(IstioExtensionsV1Alpha1Wasmplugins). - MustAdd(IstioMeshV1Alpha1MeshConfig). - MustAdd(IstioMeshV1Alpha1MeshNetworks). - MustAdd(IstioNetworkingV1Alpha3Destinationrules). - MustAdd(IstioNetworkingV1Alpha3Envoyfilters). - MustAdd(IstioNetworkingV1Alpha3Gateways). - MustAdd(IstioNetworkingV1Alpha3Serviceentries). - MustAdd(IstioNetworkingV1Alpha3Sidecars). - MustAdd(IstioNetworkingV1Alpha3Virtualservices). - MustAdd(IstioNetworkingV1Alpha3Workloadentries). - MustAdd(IstioNetworkingV1Alpha3Workloadgroups). - MustAdd(IstioNetworkingV1Beta1Proxyconfigs). - MustAdd(IstioSecurityV1Beta1Authorizationpolicies). - MustAdd(IstioSecurityV1Beta1Peerauthentications). - MustAdd(IstioSecurityV1Beta1Requestauthentications). - MustAdd(IstioTelemetryV1Alpha1Telemetries). - Build() - - // Kube contains only kubernetes collections. - Kube = collection.NewSchemasBuilder(). - MustAdd(K8SAdmissionregistrationK8SIoV1Mutatingwebhookconfigurations). - MustAdd(K8SApiextensionsK8SIoV1Customresourcedefinitions). - MustAdd(K8SAppsV1Deployments). - MustAdd(K8SCoreV1Configmaps). - MustAdd(K8SCoreV1Endpoints). - MustAdd(K8SCoreV1Namespaces). - MustAdd(K8SCoreV1Nodes). - MustAdd(K8SCoreV1Pods). - MustAdd(K8SCoreV1Secrets). - MustAdd(K8SCoreV1Services). - MustAdd(K8SExtensionsV1Beta1Ingresses). - MustAdd(K8SGatewayApiV1Alpha2Gatewayclasses). - MustAdd(K8SGatewayApiV1Alpha2Gateways). - MustAdd(K8SGatewayApiV1Alpha2Httproutes). - MustAdd(K8SGatewayApiV1Alpha2Referencepolicies). - MustAdd(K8SGatewayApiV1Alpha2Tcproutes). - MustAdd(K8SGatewayApiV1Alpha2Tlsroutes). - Build() - - // Builtin contains only native Kubernetes collections. This differs from Kube, which has - // Kubernetes controlled CRDs - Builtin = collection.NewSchemasBuilder(). - MustAdd(K8SAdmissionregistrationK8SIoV1Mutatingwebhookconfigurations). - MustAdd(K8SApiextensionsK8SIoV1Customresourcedefinitions). - MustAdd(K8SAppsV1Deployments). - MustAdd(K8SCoreV1Configmaps). - MustAdd(K8SCoreV1Endpoints). - MustAdd(K8SCoreV1Namespaces). - MustAdd(K8SCoreV1Nodes). - MustAdd(K8SCoreV1Pods). - MustAdd(K8SCoreV1Secrets). - MustAdd(K8SCoreV1Services). - MustAdd(K8SExtensionsV1Beta1Ingresses). - Build() - - // Pilot contains only collections used by Pilot. - Pilot = collection.NewSchemasBuilder(). - MustAdd(IstioExtensionsV1Alpha1Servicemetadatas). - MustAdd(IstioExtensionsV1Alpha1Servicenamemappings). - MustAdd(IstioExtensionsV1Alpha1Wasmplugins). - MustAdd(IstioNetworkingV1Alpha3Destinationrules). - MustAdd(IstioNetworkingV1Alpha3Envoyfilters). - MustAdd(IstioNetworkingV1Alpha3Gateways). - MustAdd(IstioNetworkingV1Alpha3Serviceentries). - MustAdd(IstioNetworkingV1Alpha3Sidecars). - MustAdd(IstioNetworkingV1Alpha3Virtualservices). - MustAdd(IstioNetworkingV1Alpha3Workloadentries). - MustAdd(IstioNetworkingV1Alpha3Workloadgroups). - MustAdd(IstioNetworkingV1Beta1Proxyconfigs). - MustAdd(IstioSecurityV1Beta1Authorizationpolicies). - MustAdd(IstioSecurityV1Beta1Peerauthentications). - MustAdd(IstioSecurityV1Beta1Requestauthentications). - MustAdd(IstioTelemetryV1Alpha1Telemetries). - Build() - - // PilotGatewayAPI contains only collections used by Pilot, including experimental Service Api. - PilotGatewayAPI = collection.NewSchemasBuilder(). - MustAdd(IstioExtensionsV1Alpha1Servicemetadatas). - MustAdd(IstioExtensionsV1Alpha1Servicenamemappings). - MustAdd(IstioExtensionsV1Alpha1Wasmplugins). - MustAdd(IstioNetworkingV1Alpha3Destinationrules). - MustAdd(IstioNetworkingV1Alpha3Envoyfilters). - MustAdd(IstioNetworkingV1Alpha3Gateways). - MustAdd(IstioNetworkingV1Alpha3Serviceentries). - MustAdd(IstioNetworkingV1Alpha3Sidecars). - MustAdd(IstioNetworkingV1Alpha3Virtualservices). - MustAdd(IstioNetworkingV1Alpha3Workloadentries). - MustAdd(IstioNetworkingV1Alpha3Workloadgroups). - MustAdd(IstioNetworkingV1Beta1Proxyconfigs). - MustAdd(IstioSecurityV1Beta1Authorizationpolicies). - MustAdd(IstioSecurityV1Beta1Peerauthentications). - MustAdd(IstioSecurityV1Beta1Requestauthentications). - MustAdd(IstioTelemetryV1Alpha1Telemetries). - MustAdd(K8SGatewayApiV1Alpha2Gatewayclasses). - MustAdd(K8SGatewayApiV1Alpha2Gateways). - MustAdd(K8SGatewayApiV1Alpha2Httproutes). - MustAdd(K8SGatewayApiV1Alpha2Referencepolicies). - MustAdd(K8SGatewayApiV1Alpha2Tcproutes). - MustAdd(K8SGatewayApiV1Alpha2Tlsroutes). - Build() - - // Deprecated contains only collections used by that will soon be used by nothing. - Deprecated = collection.NewSchemasBuilder(). - Build() -) diff --git a/pkg/config/schema/collections/mock.go b/pkg/config/schema/collections/mock.go deleted file mode 100644 index 6468f598b..000000000 --- a/pkg/config/schema/collections/mock.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package collections - -import ( - "errors" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/validation" - testconfig "github.com/apache/dubbo-go-pixiu/pkg/test/config" -) - -var ( - // Mock is used purely for testing - Mock = collection.Builder{ - Name: "mock", - VariableName: "Mock", - Resource: resource.Builder{ - ClusterScoped: false, - Kind: "MockConfig", - Plural: "mockconfigs", - Group: "test.istio.io", - Version: "v1", - Proto: "config.MockConfig", - ProtoPackage: "github.com/apache/dubbo-go-pixiu/pkg/test/config", - ValidateProto: func(cfg config.Config) (validation.Warning, error) { - if cfg.Spec.(*testconfig.MockConfig).Key == "" { - return nil, errors.New("empty key") - } - return nil, nil - }, - }.MustBuild(), - }.MustBuild() - - // Mocks is a Schemas containing the Mock Schema. - Mocks = collection.NewSchemasBuilder().MustAdd(Mock).Build() -) diff --git a/pkg/config/schema/generate.go b/pkg/config/schema/generate.go deleted file mode 100644 index b28c15968..000000000 --- a/pkg/config/schema/generate.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package schema - -// Create collection constants -// We will generate collections twice. Once is the full collection set. The other includes only Istio types, with build tags set for agent -// This allows the agent to use collections without importing all of Kuberntes libraries -// nolint: lll -//go:generate go run $REPO_ROOT/pkg/config/schema/codegen/tools/collections.main.go collections metadata.yaml "$REPO_ROOT/pkg/config/schema/collections/collections.gen.go" k8s "$REPO_ROOT/pkg/config/schema/collections/collections.agent.gen.go" "agent" -// Create gvk helpers -//go:generate go run $REPO_ROOT/pkg/config/schema/codegen/tools/collections.main.go gvk metadata.yaml "$REPO_ROOT/pkg/config/schema/gvk/resources.gen.go" - -//go:generate goimports -w -local istio.io "$REPO_ROOT/pkg/config/schema/collections/collections.gen.go" -//go:generate goimports -w -local istio.io "$REPO_ROOT/pkg/config/schema/collections/collections.agent.gen.go" diff --git a/pkg/config/schema/gvk/resources.gen.go b/pkg/config/schema/gvk/resources.gen.go deleted file mode 100755 index a52a9f887..000000000 --- a/pkg/config/schema/gvk/resources.gen.go +++ /dev/null @@ -1,46 +0,0 @@ -// GENERATED FILE -- DO NOT EDIT -// - -package gvk - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" -) - -var ( - AuthorizationPolicy = config.GroupVersionKind{Group: "security.istio.io", Version: "v1beta1", Kind: "AuthorizationPolicy"} - ConfigMap = config.GroupVersionKind{Group: "", Version: "v1", Kind: "ConfigMap"} - CustomResourceDefinition = config.GroupVersionKind{Group: "apiextensions.k8s.io", Version: "v1", Kind: "CustomResourceDefinition"} - Deployment = config.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"} - DestinationRule = config.GroupVersionKind{Group: "networking.istio.io", Version: "v1alpha3", Kind: "DestinationRule"} - Endpoints = config.GroupVersionKind{Group: "", Version: "v1", Kind: "Endpoints"} - EnvoyFilter = config.GroupVersionKind{Group: "networking.istio.io", Version: "v1alpha3", Kind: "EnvoyFilter"} - Gateway = config.GroupVersionKind{Group: "networking.istio.io", Version: "v1alpha3", Kind: "Gateway"} - GatewayClass = config.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1alpha2", Kind: "GatewayClass"} - HTTPRoute = config.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1alpha2", Kind: "HTTPRoute"} - Ingress = config.GroupVersionKind{Group: "extensions", Version: "v1beta1", Kind: "Ingress"} - KubernetesGateway = config.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1alpha2", Kind: "Gateway"} - MeshConfig = config.GroupVersionKind{Group: "", Version: "v1alpha1", Kind: "MeshConfig"} - MeshNetworks = config.GroupVersionKind{Group: "", Version: "v1alpha1", Kind: "MeshNetworks"} - MutatingWebhookConfiguration = config.GroupVersionKind{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "MutatingWebhookConfiguration"} - Namespace = config.GroupVersionKind{Group: "", Version: "v1", Kind: "Namespace"} - Node = config.GroupVersionKind{Group: "", Version: "v1", Kind: "Node"} - PeerAuthentication = config.GroupVersionKind{Group: "security.istio.io", Version: "v1beta1", Kind: "PeerAuthentication"} - Pod = config.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"} - ProxyConfig = config.GroupVersionKind{Group: "networking.istio.io", Version: "v1beta1", Kind: "ProxyConfig"} - ReferencePolicy = config.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1alpha2", Kind: "ReferencePolicy"} - RequestAuthentication = config.GroupVersionKind{Group: "security.istio.io", Version: "v1beta1", Kind: "RequestAuthentication"} - Secret = config.GroupVersionKind{Group: "", Version: "v1", Kind: "Secret"} - Service = config.GroupVersionKind{Group: "", Version: "v1", Kind: "Service"} - ServiceEntry = config.GroupVersionKind{Group: "networking.istio.io", Version: "v1alpha3", Kind: "ServiceEntry"} - ServiceMetadata = config.GroupVersionKind{Group: "extensions.istio.io", Version: "v1alpha1", Kind: "ServiceMetadata"} - ServiceNameMapping = config.GroupVersionKind{Group: "extensions.istio.io", Version: "v1alpha1", Kind: "ServiceNameMapping"} - Sidecar = config.GroupVersionKind{Group: "networking.istio.io", Version: "v1alpha3", Kind: "Sidecar"} - TCPRoute = config.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1alpha2", Kind: "TCPRoute"} - TLSRoute = config.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1alpha2", Kind: "TLSRoute"} - Telemetry = config.GroupVersionKind{Group: "telemetry.istio.io", Version: "v1alpha1", Kind: "Telemetry"} - VirtualService = config.GroupVersionKind{Group: "networking.istio.io", Version: "v1alpha3", Kind: "VirtualService"} - WasmPlugin = config.GroupVersionKind{Group: "extensions.istio.io", Version: "v1alpha1", Kind: "WasmPlugin"} - WorkloadEntry = config.GroupVersionKind{Group: "networking.istio.io", Version: "v1alpha3", Kind: "WorkloadEntry"} - WorkloadGroup = config.GroupVersionKind{Group: "networking.istio.io", Version: "v1alpha3", Kind: "WorkloadGroup"} -) diff --git a/pkg/config/schema/metadata.yaml b/pkg/config/schema/metadata.yaml deleted file mode 100644 index d1c0d965a..000000000 --- a/pkg/config/schema/metadata.yaml +++ /dev/null @@ -1,500 +0,0 @@ -# Copyright 2019 Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in conformance 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. - -# -# This is the main metadata file for Galley processing. -# #### KEEP ENTRIES ALPHASORTED! #### -# - -# The total set of collections, both Istio (i.e. MCP) and K8s (API Server/K8s). -collections: - ## Istio collections - - name: "istio/extensions/v1alpha1/wasmplugins" - kind: "WasmPlugin" - group: "extensions.istio.io" - pilot: true - - - name: "istio/mesh/v1alpha1/MeshConfig" - kind: "MeshConfig" - group: "" - - - name: "istio/mesh/v1alpha1/MeshNetworks" - kind: "MeshNetworks" - group: "" - - - name: "istio/networking/v1alpha3/destinationrules" - kind: DestinationRule - group: "networking.istio.io" - pilot: true - - - name: "istio/networking/v1alpha3/envoyfilters" - kind: "EnvoyFilter" - group: "networking.istio.io" - pilot: true - - - name: "istio/networking/v1alpha3/gateways" - kind: "Gateway" - group: "networking.istio.io" - pilot: true - - - name: "istio/networking/v1alpha3/serviceentries" - kind: "ServiceEntry" - group: "networking.istio.io" - pilot: true - - - name: "istio/networking/v1alpha3/workloadentries" - kind: "WorkloadEntry" - group: "networking.istio.io" - pilot: true - - - name: "istio/networking/v1alpha3/workloadgroups" - kind: "WorkloadGroup" - group: "networking.istio.io" - pilot: true - - - name: "istio/networking/v1alpha3/sidecars" - kind: "Sidecar" - group: "networking.istio.io" - pilot: true - - - name: "istio/networking/v1alpha3/virtualservices" - kind: "VirtualService" - group: "networking.istio.io" - pilot: true - - - name: "istio/networking/v1beta1/proxyconfigs" - kind: "ProxyConfig" - group: "networking.istio.io" - pilot: true - - - name: "istio/security/v1beta1/authorizationpolicies" - kind: AuthorizationPolicy - group: "security.istio.io" - pilot: true - - - name: "istio/security/v1beta1/requestauthentications" - kind: RequestAuthentication - group: "security.istio.io" - pilot: true - - - name: "istio/security/v1beta1/peerauthentications" - kind: PeerAuthentication - group: "security.istio.io" - pilot: true - - - name: "istio/telemetry/v1alpha1/telemetries" - kind: "Telemetry" - group: "telemetry.istio.io" - pilot: true - - ### K8s collections ### - - # Built-in K8s collections - - name: "k8s/apiextensions.k8s.io/v1/customresourcedefinitions" - kind: "CustomResourceDefinition" - group: "apiextensions.k8s.io" - builtin: true - - - name: "k8s/admissionregistration.k8s.io/v1/mutatingwebhookconfigurations" - kind: "MutatingWebhookConfiguration" - group: "admissionregistration.k8s.io" - builtin: true - - - name: "k8s/apps/v1/deployments" - kind: "Deployment" - group: "apps" - builtin: true - - - name: "k8s/core/v1/endpoints" - kind: "Endpoints" - group: "" - builtin: true - - - name: "k8s/core/v1/namespaces" - kind: "Namespace" - group: "" - builtin: true - - - name: "k8s/core/v1/nodes" - kind: "Node" - group: "" - builtin: true - - - name: "k8s/core/v1/pods" - kind: "Pod" - group: "" - builtin: true - - - name: "k8s/core/v1/secrets" - kind: "Secret" - group: "" - builtin: true - - - name: "k8s/core/v1/services" - kind: "Service" - group: "" - builtin: true - - - name: "k8s/core/v1/configmaps" - kind: "ConfigMap" - group: "" - builtin: true - - - name: "k8s/extensions/v1beta1/ingresses" - kind: "Ingress" - group: "extensions" - builtin: true - - - kind: "GatewayClass" - name: "k8s/gateway_api/v1alpha2/gatewayclasses" - group: "gateway.networking.k8s.io" - - - kind: "Gateway" - name: "k8s/gateway_api/v1alpha2/gateways" - group: "gateway.networking.k8s.io" - - - kind: "HTTPRoute" - name: "k8s/gateway_api/v1alpha2/httproutes" - group: "gateway.networking.k8s.io" - - - kind: "TCPRoute" - name: "k8s/gateway_api/v1alpha2/tcproutes" - group: "gateway.networking.k8s.io" - - - kind: "TLSRoute" - name: "k8s/gateway_api/v1alpha2/tlsroutes" - group: "gateway.networking.k8s.io" - - - kind: "ReferencePolicy" - name: "k8s/gateway_api/v1alpha2/referencepolicies" - group: "gateway.networking.k8s.io" - - - kind: "ServiceMetadata" - name: "istio/extensions/v1alpha1/servicemetadatas" - group: "extensions.istio.io" - pilot: true - - - name: "istio/extensions/v1alpha1/servicenamemappings" - kind: ServiceNameMapping - group: "extensions.istio.io" - pilot: true - -# Configuration for resource types. -resources: - # Kubernetes specific configuration. - - kind: "CustomResourceDefinition" - plural: "customresourcedefinitions" - group: "apiextensions.k8s.io" - version: "v1" - proto: "k8s.io.apiextensions_apiserver.pkg.apis.apiextensions.v1.CustomResourceDefinition" - protoPackage: "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - - - kind: "MutatingWebhookConfiguration" - plural: "mutatingwebhookconfigurations" - group: "admissionregistration.k8s.io" - version: "v1" - proto: "k8s.io.api.admissionregistration.v1.MutatingWebhookConfiguration" - protoPackage: "k8s.io/api/admissionregistration/v1" - - - kind: "Deployment" - plural: "deployments" - group: "apps" - version: "v1" - proto: "k8s.io.api.apps.v1.DeploymentSpec" - protoPackage: "k8s.io/api/apps/v1" - - - kind: "Endpoints" - plural: "endpoints" - version: "v1" - proto: "k8s.io.api.core.v1.Endpoints" - protoPackage: "k8s.io/api/core/v1" - - - kind: "Namespace" - plural: "namespaces" - version: "v1" - clusterScoped: true - proto: "k8s.io.api.core.v1.NamespaceSpec" - protoPackage: "k8s.io/api/core/v1" - - - kind: "Node" - plural: "nodes" - version: "v1" - clusterScoped: true - proto: "k8s.io.api.core.v1.NodeSpec" - protoPackage: "k8s.io/api/core/v1" - - - kind: "Pod" - plural: "pods" - version: "v1" - proto: "k8s.io.api.core.v1.PodSpec" - protoPackage: "k8s.io/api/core/v1" - - - kind: "Secret" - plural: "secrets" - version: "v1" - proto: "k8s.io.api.core.v1.Secret" - protoPackage: "k8s.io/api/core/v1" - - - kind: "Service" - plural: "services" - version: "v1" - proto: "k8s.io.api.core.v1.ServiceSpec" - protoPackage: "k8s.io/api/core/v1" - - - kind: "ConfigMap" - plural: "configmaps" - version: "v1" - proto: "k8s.io.api.core.v1.ConfigMap" - protoPackage: "k8s.io/api/core/v1" - - - kind: "Ingress" - plural: "ingresses" - group: "extensions" - version: "v1beta1" - proto: "k8s.io.api.extensions.v1beta1.IngressSpec" - protoPackage: "k8s.io/api/extensions/v1beta1" - statusProto: "k8s.io.gateway_api.api.v1alpha1.IngressStatus" - statusProtoPackage: "k8s.io/api/extensions/v1beta1" - - - kind: "GatewayClass" - plural: "gatewayclasses" - group: "gateway.networking.k8s.io" - version: "v1alpha2" - clusterScoped: true - protoPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2" - proto: "k8s.io.gateway_api.api.v1alpha1.GatewayClassSpec" - statusProto: "k8s.io.gateway_api.api.v1alpha1.GatewayClassStatus" - statusProtoPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2" - - - kind: "Gateway" - plural: "gateways" - group: "gateway.networking.k8s.io" - version: "v1alpha2" - protoPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2" - proto: "k8s.io.gateway_api.api.v1alpha1.GatewaySpec" - validate: "EmptyValidate" - statusProto: "k8s.io.gateway_api.api.v1alpha1.GatewayStatus" - statusProtoPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2" - - - kind: "HTTPRoute" - plural: "httproutes" - group: "gateway.networking.k8s.io" - version: "v1alpha2" - protoPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2" - proto: "k8s.io.gateway_api.api.v1alpha1.HTTPRouteSpec" - statusProto: "k8s.io.gateway_api.api.v1alpha1.HTTPRouteStatus" - statusProtoPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2" - - - kind: "TCPRoute" - plural: "tcproutes" - group: "gateway.networking.k8s.io" - version: "v1alpha2" - protoPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2" - proto: "k8s.io.gateway_api.api.v1alpha1.TCPRouteSpec" - statusProto: "k8s.io.gateway_api.api.v1alpha1.TCPRouteStatus" - statusProtoPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2" - - - kind: "TLSRoute" - plural: "tlsroutes" - group: "gateway.networking.k8s.io" - version: "v1alpha2" - protoPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2" - proto: "k8s.io.gateway_api.api.v1alpha1.TLSRouteSpec" - statusProtoPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2" - statusProto: "k8s.io.gateway_api.api.v1alpha1.TLSRouteStatus" - - - kind: "ReferencePolicy" - plural: "referencepolicies" - group: "gateway.networking.k8s.io" - version: "v1alpha2" - protoPackage: "sigs.k8s.io/gateway-api/apis/v1alpha2" - proto: "k8s.io.gateway_api.api.v1alpha1.ReferencePolicySpec" - - ## Istio resources - - kind: "VirtualService" - plural: "virtualservices" - group: "networking.istio.io" - version: "v1alpha3" - proto: "istio.networking.v1alpha3.VirtualService" - protoPackage: "istio.io/api/networking/v1alpha3" - description: "describes v1alpha3 route rules" - statusProto: "istio.meta.v1alpha1.IstioStatus" - statusProtoPackage: "istio.io/api/meta/v1alpha1" - - - kind: "Gateway" - plural: "gateways" - group: "networking.istio.io" - version: "v1alpha3" - proto: "istio.networking.v1alpha3.Gateway" - protoPackage: "istio.io/api/networking/v1alpha3" - description: "describes a gateway (how a proxy is exposed on the network)" - statusProto: "istio.meta.v1alpha1.IstioStatus" - statusProtoPackage: "istio.io/api/meta/v1alpha1" - - - kind: "ServiceEntry" - plural: "serviceentries" - group: "networking.istio.io" - version: "v1alpha3" - proto: "istio.networking.v1alpha3.ServiceEntry" - protoPackage: "istio.io/api/networking/v1alpha3" - description: "describes service entries" - statusProto: "istio.meta.v1alpha1.IstioStatus" - statusProtoPackage: "istio.io/api/meta/v1alpha1" - - - kind: "WorkloadEntry" - plural: "workloadentries" - group: "networking.istio.io" - version: "v1alpha3" - proto: "istio.networking.v1alpha3.WorkloadEntry" - protoPackage: "istio.io/api/networking/v1alpha3" - description: "describes workload entries" - statusProto: "istio.meta.v1alpha1.IstioStatus" - statusProtoPackage: "istio.io/api/meta/v1alpha1" - - - kind: "WorkloadGroup" - plural: "workloadgroups" - group: "networking.istio.io" - version: "v1alpha3" - proto: "istio.networking.v1alpha3.WorkloadGroup" - protoPackage: "istio.io/api/networking/v1alpha3" - description: "describes workload groups" - statusProto: "istio.meta.v1alpha1.IstioStatus" - statusProtoPackage: "istio.io/api/meta/v1alpha1" - - - kind: DestinationRule - plural: "destinationrules" - group: "networking.istio.io" - version: "v1alpha3" - proto: "istio.networking.v1alpha3.DestinationRule" - protoPackage: "istio.io/api/networking/v1alpha3" - description: "describes destination rules" - statusProto: "istio.meta.v1alpha1.IstioStatus" - statusProtoPackage: "istio.io/api/meta/v1alpha1" - - - kind: "EnvoyFilter" - plural: "envoyfilters" - group: "networking.istio.io" - version: "v1alpha3" - proto: "istio.networking.v1alpha3.EnvoyFilter" - protoPackage: "istio.io/api/networking/v1alpha3" - description: "describes additional envoy filters to be inserted by Pilot" - statusProto: "istio.meta.v1alpha1.IstioStatus" - statusProtoPackage: "istio.io/api/meta/v1alpha1" - - - kind: "Sidecar" - plural: "sidecars" - group: "networking.istio.io" - version: "v1alpha3" - proto: "istio.networking.v1alpha3.Sidecar" - protoPackage: "istio.io/api/networking/v1alpha3" - description: "describes the listeners associated with sidecars in a namespace" - statusProto: "istio.meta.v1alpha1.IstioStatus" - statusProtoPackage: "istio.io/api/meta/v1alpha1" - - - kind: "ProxyConfig" - plural: "proxyconfigs" - group: "networking.istio.io" - version: "v1beta1" - proto: "istio.networking.v1beta1.ProxyConfig" - protoPackage: "istio.io/api/networking/v1beta1" - description: "defines configuration for individual workloads" - statusProto: "istio.meta.v1alpha1.IstioStatus" - statusProtoPackage: "istio.io/api/meta/v1alpha1" - - - kind: "MeshConfig" - plural: "meshconfigs" - group: "" - version: "v1alpha1" - proto: "istio.mesh.v1alpha1.MeshConfig" - protoPackage: "istio.io/api/mesh/v1alpha1" - description: "describes the configuration for the Istio mesh." - - - kind: "MeshNetworks" - plural: "meshnetworks" - group: "" - version: "v1alpha1" - proto: "istio.mesh.v1alpha1.MeshNetworks" - protoPackage: "istio.io/api/mesh/v1alpha1" - description: "describes the networks for the Istio mesh." - - - kind: AuthorizationPolicy - plural: "authorizationpolicies" - group: "security.istio.io" - version: "v1beta1" - proto: "istio.security.v1beta1.AuthorizationPolicy" - protoPackage: "istio.io/api/security/v1beta1" - description: "describes the authorization policy." - statusProto: "istio.meta.v1alpha1.IstioStatus" - statusProtoPackage: "istio.io/api/meta/v1alpha1" - - - kind: RequestAuthentication - plural: "requestauthentications" - group: "security.istio.io" - version: "v1beta1" - proto: "istio.security.v1beta1.RequestAuthentication" - protoPackage: "istio.io/api/security/v1beta1" - description: "describes the request authentication." - statusProto: "istio.meta.v1alpha1.IstioStatus" - statusProtoPackage: "istio.io/api/meta/v1alpha1" - - - kind: PeerAuthentication - plural: "peerauthentications" - group: "security.istio.io" - version: "v1beta1" - proto: "istio.security.v1beta1.PeerAuthentication" - protoPackage: "istio.io/api/security/v1beta1" - validate: "ValidatePeerAuthentication" - description: "describes the peer authentication." - statusProto: "istio.meta.v1alpha1.IstioStatus" - statusProtoPackage: "istio.io/api/meta/v1alpha1" - - - kind: "Telemetry" - plural: "telemetries" - group: "telemetry.istio.io" - version: "v1alpha1" - proto: "istio.telemetry.v1alpha1.Telemetry" - protoPackage: "istio.io/api/telemetry/v1alpha1" - description: "describes telemetry configuration for workloads" - statusProto: "istio.meta.v1alpha1.IstioStatus" - statusProtoPackage: "istio.io/api/meta/v1alpha1" - - - kind: "WasmPlugin" - plural: "wasmplugins" - group: "extensions.istio.io" - version: "v1alpha1" - proto: "istio.extensions.v1alpha1.WasmPlugin" - protoPackage: "istio.io/api/extensions/v1alpha1" - description: "" - statusProto: "istio.meta.v1alpha1.IstioStatus" - statusProtoPackage: "istio.io/api/meta/v1alpha1" - - - kind: ServiceMetadata - plural: "servicemetadatas" - group: "extensions.istio.io" - version: "v1alpha1" - proto: "istio.extensions.v1alpha1.ServiceMetadata" - protoPackage: "istio.io/api/extensions/v1alpha1" - description: "describes service metadata" - statusProto: "istio.meta.v1alpha1.IstioStatus" - statusProtoPackage: "istio.io/api/meta/v1alpha1" - - - kind: ServiceNameMapping - plural: "servicenamemappings" - group: "extensions.istio.io" - version: "v1alpha1" - proto: "istio.extensions.v1alpha1.ServiceNameMapping" - protoPackage: "istio.io/api/extensions/v1alpha1" - description: "describes service name mappings" - statusProto: "istio.meta.v1alpha1.IstioStatus" - statusProtoPackage: "istio.io/api/meta/v1alpha1" \ No newline at end of file diff --git a/pkg/config/schema/resource/schema.go b/pkg/config/schema/resource/schema.go deleted file mode 100644 index 4d50cb0c1..000000000 --- a/pkg/config/schema/resource/schema.go +++ /dev/null @@ -1,340 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -import ( - "errors" - "fmt" - "reflect" -) - -import ( - "github.com/hashicorp/go-multierror" - "google.golang.org/protobuf/reflect/protoreflect" - "google.golang.org/protobuf/reflect/protoregistry" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/validation" -) - -// Schema for a resource. -type Schema interface { - fmt.Stringer - - // GroupVersionKind of the resource. This is the only way to uniquely identify a resource. - GroupVersionKind() config.GroupVersionKind - - // GroupVersionResource of the resource. - GroupVersionResource() schema.GroupVersionResource - - // IsClusterScoped indicates that this resource is scoped to a particular namespace within a cluster. - IsClusterScoped() bool - - // Kind for this resource. - Kind() string - - // Plural returns the plural form of the Kind. - Plural() string - - // Group for this resource. - Group() string - - // Version of this resource. - Version() string - - // APIVersion is a utility that returns a k8s API version string of the form "Group/Version". - APIVersion() string - - // Proto returns the protocol buffer type name for this resource. - Proto() string - - // ProtoPackage returns the golang package for the protobuf resource. - ProtoPackage() string - - // NewInstance returns a new instance of the protocol buffer message for this resource. - NewInstance() (config.Spec, error) - - // Status returns the associated status of the schema - Status() (config.Status, error) - - // StatusKind returns the Kind of the status field. If unset, the field does not support status. - StatusKind() string - StatusPackage() string - - // MustNewInstance calls NewInstance and panics if an error occurs. - MustNewInstance() config.Spec - - // Validate this schema. - Validate() error - - // ValidateConfig validates that the given config message is of the correct type for this schema - // and that the contents are valid. - ValidateConfig(cfg config.Config) (validation.Warning, error) - - // Equal is a helper function for testing equality between Schema instances. This supports comparison - // with the cmp library. - Equal(other Schema) bool -} - -// Builder for a Schema. -type Builder struct { - // ClusterScoped is true for resource in cluster-level. - ClusterScoped bool - - // Kind is the config proto type. - Kind string - - // Plural is the type in plural. - Plural string - - // Group is the config proto group. - Group string - - // Version is the config proto version. - Version string - - // Proto refers to the protobuf message type name corresponding to the type - Proto string - - StatusProto string - - // ReflectType is the type of the go struct - ReflectType reflect.Type - - // StatusType is the type of the associated status. - StatusType reflect.Type - - // ProtoPackage refers to the name of golang package for the protobuf message. - ProtoPackage string - - // StatusPackage refers to the name of the golang status package. - StatusPackage string - - // ValidateProto performs validation on protobuf messages based on this schema. - ValidateProto validation.ValidateFunc -} - -// Build a Schema instance. -func (b Builder) Build() (Schema, error) { - s := b.BuildNoValidate() - - // Validate the schema. - if err := s.Validate(); err != nil { - return nil, err - } - - return s, nil -} - -// MustBuild calls Build and panics if it fails. -func (b Builder) MustBuild() Schema { - s, err := b.Build() - if err != nil { - panic(fmt.Sprintf("MustBuild: %v", err)) - } - return s -} - -// BuildNoValidate builds the Schema without checking the fields. -func (b Builder) BuildNoValidate() Schema { - if b.ValidateProto == nil { - b.ValidateProto = validation.EmptyValidate - } - - return &schemaImpl{ - clusterScoped: b.ClusterScoped, - gvk: config.GroupVersionKind{ - Group: b.Group, - Version: b.Version, - Kind: b.Kind, - }, - plural: b.Plural, - apiVersion: b.Group + "/" + b.Version, - proto: b.Proto, - goPackage: b.ProtoPackage, - reflectType: b.ReflectType, - validateConfig: b.ValidateProto, - statusType: b.StatusType, - statusPackage: b.StatusPackage, - } -} - -type schemaImpl struct { - clusterScoped bool - gvk config.GroupVersionKind - plural string - apiVersion string - proto string - goPackage string - validateConfig validation.ValidateFunc - reflectType reflect.Type - statusType reflect.Type - statusPackage string -} - -func (s *schemaImpl) GroupVersionKind() config.GroupVersionKind { - return s.gvk -} - -func (s *schemaImpl) GroupVersionResource() schema.GroupVersionResource { - return schema.GroupVersionResource{ - Group: s.Group(), - Version: s.Version(), - Resource: s.Plural(), - } -} - -func (s *schemaImpl) IsClusterScoped() bool { - return s.clusterScoped -} - -func (s *schemaImpl) Kind() string { - return s.gvk.Kind -} - -func (s *schemaImpl) Plural() string { - return s.plural -} - -func (s *schemaImpl) Group() string { - return s.gvk.Group -} - -func (s *schemaImpl) Version() string { - return s.gvk.Version -} - -func (s *schemaImpl) APIVersion() string { - return s.apiVersion -} - -func (s *schemaImpl) Proto() string { - return s.proto -} - -func (s *schemaImpl) ProtoPackage() string { - return s.goPackage -} - -func (s *schemaImpl) StatusPackage() string { - return s.statusPackage -} - -func (s *schemaImpl) Validate() (err error) { - if !labels.IsDNS1123Label(s.Kind()) { - err = multierror.Append(err, fmt.Errorf("invalid kind: %s", s.Kind())) - } - if !labels.IsDNS1123Label(s.plural) { - err = multierror.Append(err, fmt.Errorf("invalid plural for kind %s: %s", s.Kind(), s.plural)) - } - if s.reflectType == nil && getProtoMessageType(s.proto) == nil { - err = multierror.Append(err, fmt.Errorf("proto message or reflect type not found: %v", s.proto)) - } - return -} - -func (s *schemaImpl) String() string { - return fmt.Sprintf("[Schema](%s, %q, %s)", s.Kind(), s.goPackage, s.proto) -} - -func (s *schemaImpl) NewInstance() (config.Spec, error) { - rt := s.reflectType - var instance interface{} - if rt == nil { - // Use proto - t, err := protoMessageType(protoreflect.FullName(s.proto)) - if err != nil || t == nil { - return nil, errors.New("failed to find reflect type") - } - instance = t.New().Interface() - } else { - instance = reflect.New(rt).Interface() - } - - p, ok := instance.(config.Spec) - if !ok { - return nil, fmt.Errorf( - "newInstance: message is not an instance of config.Spec. kind:%s, type:%v, value:%v", - s.Kind(), rt, instance) - } - return p, nil -} - -func (s *schemaImpl) Status() (config.Status, error) { - statTyp := s.statusType - if statTyp == nil { - return nil, errors.New("unknown status type") - } - instance := reflect.New(statTyp).Interface() - p, ok := instance.(config.Status) - if !ok { - return nil, fmt.Errorf("status: statusType not an instance of config.Status. type: %v, value: %v", statTyp, instance) - } - return p, nil -} - -func (s *schemaImpl) StatusKind() string { - if s.statusType == nil { - return "" - } - return s.statusType.Name() -} - -func (s *schemaImpl) MustNewInstance() config.Spec { - p, err := s.NewInstance() - if err != nil { - panic(err) - } - return p -} - -func (s *schemaImpl) ValidateConfig(cfg config.Config) (validation.Warning, error) { - return s.validateConfig(cfg) -} - -func (s *schemaImpl) Equal(o Schema) bool { - return s.IsClusterScoped() == o.IsClusterScoped() && - s.Kind() == o.Kind() && - s.Plural() == o.Plural() && - s.Group() == o.Group() && - s.Version() == o.Version() && - s.Proto() == o.Proto() && - s.ProtoPackage() == o.ProtoPackage() -} - -// FromKubernetesGVK converts a Kubernetes GVK to an Istio GVK -func FromKubernetesGVK(in *schema.GroupVersionKind) config.GroupVersionKind { - return config.GroupVersionKind{ - Group: in.Group, - Version: in.Version, - Kind: in.Kind, - } -} - -// getProtoMessageType returns the Go lang type of the proto with the specified name. -func getProtoMessageType(protoMessageName string) reflect.Type { - t, err := protoMessageType(protoreflect.FullName(protoMessageName)) - if err != nil || t == nil { - return nil - } - t.New().Interface() - return reflect.TypeOf(t.Zero().Interface()) -} - -var protoMessageType = protoregistry.GlobalTypes.FindMessageByName diff --git a/pkg/config/schema/resource/schema_test.go b/pkg/config/schema/resource/schema_test.go deleted file mode 100644 index 2756737ed..000000000 --- a/pkg/config/schema/resource/schema_test.go +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -import ( - "testing" -) - -import ( - . "github.com/onsi/gomega" - "google.golang.org/protobuf/reflect/protoreflect" - "google.golang.org/protobuf/types/known/emptypb" -) - -func TestValidate(t *testing.T) { - cases := []struct { - name string - b Builder - expectError bool - }{ - { - name: "valid", - b: Builder{ - Kind: "Empty", - Plural: "Empties", - ProtoPackage: "github.com/gogo/protobuf/types", - Proto: "google.protobuf.Empty", - }, - expectError: false, - }, - { - name: "invalid kind", - b: Builder{ - Kind: "", - Plural: "Empties", - ProtoPackage: "github.com/gogo/protobuf/types", - Proto: "google.protobuf.Empty", - }, - expectError: true, - }, - { - name: "invalid plural", - b: Builder{ - Kind: "Empty", - Plural: "", - ProtoPackage: "github.com/gogo/protobuf/types", - Proto: "google.protobuf.Empty", - }, - expectError: true, - }, - { - name: "invalid proto", - b: Builder{ - Kind: "Boo", - Plural: "Boos", - ProtoPackage: "github.com/gogo/protobuf/types", - Proto: "boo", - }, - expectError: true, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - g := NewWithT(t) - - err := c.b.BuildNoValidate().Validate() - if c.expectError { - g.Expect(err).ToNot(BeNil()) - } else { - g.Expect(err).To(BeNil()) - } - }) - } -} - -func TestBuild(t *testing.T) { - cases := []struct { - name string - b Builder - expectError bool - }{ - { - name: "valid", - b: Builder{ - Kind: "Empty", - Plural: "Empties", - ProtoPackage: "github.com/gogo/protobuf/types", - Proto: "google.protobuf.Empty", - }, - expectError: false, - }, - { - name: "invalid kind", - b: Builder{ - Kind: "", - Plural: "Empties", - ProtoPackage: "github.com/gogo/protobuf/types", - Proto: "google.protobuf.Empty", - }, - expectError: true, - }, - { - name: "invalid plural", - b: Builder{ - Kind: "Empty", - Plural: "", - ProtoPackage: "github.com/gogo/protobuf/types", - Proto: "google.protobuf.Empty", - }, - expectError: true, - }, - { - name: "invalid proto", - b: Builder{ - Kind: "Boo", - Plural: "Boos", - ProtoPackage: "github.com/gogo/protobuf/types", - Proto: "boo", - }, - expectError: true, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - g := NewWithT(t) - - _, err := c.b.Build() - if c.expectError { - g.Expect(err).ToNot(BeNil()) - } else { - g.Expect(err).To(BeNil()) - } - }) - } -} - -func TestCanonicalName(t *testing.T) { - cases := []struct { - name string - s Schema - expected string - }{ - { - name: "group", - s: Builder{ - Group: "g", - Version: "v", - Kind: "k", - Plural: "ks", - ProtoPackage: "github.com/gogo/protobuf/types", - Proto: "google.protobuf.Empty", - }.MustBuild(), - expected: "g/v/k", - }, - { - name: "no group", - s: Builder{ - Group: "", - Version: "v", - Kind: "k", - Plural: "ks", - ProtoPackage: "github.com/gogo/protobuf/types", - Proto: "google.protobuf.Empty", - }.MustBuild(), - expected: "core/v/k", - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - g := NewWithT(t) - g.Expect(c.s.GroupVersionKind().String()).To(Equal(c.expected)) - }) - } -} - -func TestNewProtoInstance(t *testing.T) { - g := NewWithT(t) - - s := Builder{ - Kind: "Empty", - Plural: "Empties", - ProtoPackage: "google.golang.org/protobuf/types/known/emptypb", - Proto: "google.protobuf.Empty", - }.MustBuild() - - p, err := s.NewInstance() - g.Expect(err).To(BeNil()) - g.Expect(p).To(Equal(&emptypb.Empty{})) -} - -func TestMustNewProtoInstance_Panic_Nil(t *testing.T) { - g := NewWithT(t) - defer func() { - r := recover() - g.Expect(r).NotTo(BeNil()) - }() - old := protoMessageType - defer func() { - protoMessageType = old - }() - protoMessageType = func(message protoreflect.FullName) (protoreflect.MessageType, error) { - return nil, nil - } - - s := Builder{ - Kind: "Empty", - ProtoPackage: "github.com/gogo/protobuf/types", - Proto: "google.protobuf.Empty", - }.MustBuild() - - _ = s.MustNewInstance() -} - -func TestString(t *testing.T) { - g := NewWithT(t) - - s := Builder{ - Kind: "Empty", - Plural: "Empties", - ProtoPackage: "github.com/gogo/protobuf/types", - Proto: "google.protobuf.Empty", - }.MustBuild() - - g.Expect(s.String()).To(Equal(`[Schema](Empty, "github.com/gogo/protobuf/types", google.protobuf.Empty)`)) -} diff --git a/pkg/config/security/security.go b/pkg/config/security/security.go deleted file mode 100644 index d6b6f8d56..000000000 --- a/pkg/config/security/security.go +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package security - -import ( - "fmt" - "net" - "net/url" - "strconv" - "strings" - "unicode" -) - -import ( - "github.com/hashicorp/go-multierror" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// JwksInfo provides values resulting from parsing a jwks URI. -type JwksInfo struct { - Hostname host.Name - Scheme string - Port int - UseSSL bool -} - -const ( - attrRequestHeader = "request.headers" // header name is surrounded by brackets, e.g. "request.headers[User-Agent]". - attrSrcIP = "source.ip" // supports both single ip and cidr, e.g. "10.1.2.3" or "10.1.0.0/16". - attrRemoteIP = "remote.ip" // original client ip determined from x-forwarded-for or proxy protocol. - attrSrcNamespace = "source.namespace" // e.g. "default". - attrSrcPrincipal = "source.principal" // source identity, e,g, "cluster.local/ns/default/sa/productpage". - attrRequestPrincipal = "request.auth.principal" // authenticated principal of the request. - attrRequestAudiences = "request.auth.audiences" // intended audience(s) for this authentication information. - attrRequestPresenter = "request.auth.presenter" // authorized presenter of the credential. - attrRequestClaims = "request.auth.claims" // claim name is surrounded by brackets, e.g. "request.auth.claims[iss]". - attrDestIP = "destination.ip" // supports both single ip and cidr, e.g. "10.1.2.3" or "10.1.0.0/16". - attrDestPort = "destination.port" // must be in the range [0, 65535]. - attrDestLabel = "destination.labels" // label name is surrounded by brackets, e.g. "destination.labels[version]". - attrDestName = "destination.name" // short service name, e.g. "productpage". - attrDestNamespace = "destination.namespace" // e.g. "default". - attrDestUser = "destination.user" // service account, e.g. "bookinfo-productpage". - attrConnSNI = "connection.sni" // server name indication, e.g. "www.example.com". - attrExperimental = "experimental.envoy.filters." -) - -// ParseJwksURI parses the input URI and returns the corresponding hostname, port, and whether SSL is used. -// URI must start with "http://" or "https://", which corresponding to "http" or "https" scheme. -// Port number is extracted from URI if available (i.e from postfix :, eg. ":80"), or assigned -// to a default value based on URI scheme (80 for http and 443 for https). -// Port name is set to URI scheme value. -func ParseJwksURI(jwksURI string) (JwksInfo, error) { - u, err := url.Parse(jwksURI) - if err != nil { - return JwksInfo{}, err - } - info := JwksInfo{} - switch u.Scheme { - case "http": - info.UseSSL = false - info.Port = 80 - case "https": - info.UseSSL = true - info.Port = 443 - default: - return JwksInfo{}, fmt.Errorf("URI scheme %q is not supported", u.Scheme) - } - - if u.Port() != "" { - info.Port, err = strconv.Atoi(u.Port()) - if err != nil { - return JwksInfo{}, err - } - } - info.Hostname = host.Name(u.Hostname()) - info.Scheme = u.Scheme - - return info, nil -} - -func CheckEmptyValues(key string, values []string) error { - for _, value := range values { - if value == "" { - return fmt.Errorf("empty value not allowed, found in %s", key) - } - } - return nil -} - -func ValidateAttribute(key string, values []string) error { - if err := CheckEmptyValues(key, values); err != nil { - return err - } - switch { - case hasPrefix(key, attrRequestHeader): - return validateMapKey(key) - case isEqual(key, attrSrcIP): - return ValidateIPs(values) - case isEqual(key, attrRemoteIP): - return ValidateIPs(values) - case isEqual(key, attrSrcNamespace): - case isEqual(key, attrSrcPrincipal): - case isEqual(key, attrRequestPrincipal): - case isEqual(key, attrRequestAudiences): - case isEqual(key, attrRequestPresenter): - case hasPrefix(key, attrRequestClaims): - return validateMapKey(key) - case isEqual(key, attrDestIP): - return ValidateIPs(values) - case isEqual(key, attrDestPort): - return ValidatePorts(values) - case isEqual(key, attrConnSNI): - case hasPrefix(key, attrExperimental): - return validateMapKey(key) - case isEqual(key, attrDestNamespace): - return fmt.Errorf("attribute %s is replaced by the metadata.namespace", key) - case hasPrefix(key, attrDestLabel): - return fmt.Errorf("attribute %s is replaced by the workload selector", key) - case isEqual(key, attrDestName, attrDestUser): - return fmt.Errorf("deprecated attribute %s: only supported in v1alpha1", key) - default: - return fmt.Errorf("unknown attribute: %s", key) - } - return nil -} - -func isEqual(key string, values ...string) bool { - for _, v := range values { - if key == v { - return true - } - } - return false -} - -func hasPrefix(key string, prefix string) bool { - return strings.HasPrefix(key, prefix) -} - -func ValidateIPs(ips []string) error { - var errs *multierror.Error - for _, v := range ips { - if strings.Contains(v, "/") { - if _, _, err := net.ParseCIDR(v); err != nil { - errs = multierror.Append(errs, fmt.Errorf("bad CIDR range (%s): %v", v, err)) - } - } else { - if ip := net.ParseIP(v); ip == nil { - errs = multierror.Append(errs, fmt.Errorf("bad IP address (%s)", v)) - } - } - } - return errs.ErrorOrNil() -} - -func ValidatePorts(ports []string) error { - var errs *multierror.Error - for _, port := range ports { - p, err := strconv.ParseUint(port, 10, 32) - if err != nil || p > 65535 { - errs = multierror.Append(errs, fmt.Errorf("bad port (%s): %v", port, err)) - } - } - return errs.ErrorOrNil() -} - -func validateMapKey(key string) error { - open := strings.Index(key, "[") - if strings.HasSuffix(key, "]") && open > 0 && open < len(key)-2 { - return nil - } - return fmt.Errorf("bad key (%s): should have format a[b]", key) -} - -// ValidCipherSuites contains a list of all ciphers supported in Gateway.server.tls.cipherSuites -// Extracted from: `bssl ciphers -openssl-name ALL | rg -v PSK` -var ValidCipherSuites = sets.New( - "ECDHE-ECDSA-AES128-GCM-SHA256", - "ECDHE-RSA-AES128-GCM-SHA256", - "ECDHE-ECDSA-AES256-GCM-SHA384", - "ECDHE-RSA-AES256-GCM-SHA384", - "ECDHE-ECDSA-CHACHA20-POLY1305", - "ECDHE-RSA-CHACHA20-POLY1305", - "ECDHE-ECDSA-AES128-SHA", - "ECDHE-RSA-AES128-SHA", - "ECDHE-ECDSA-AES256-SHA", - "ECDHE-RSA-AES256-SHA", - "AES128-GCM-SHA256", - "AES256-GCM-SHA384", - "AES128-SHA", - "AES256-SHA", - "DES-CBC3-SHA", -) - -func IsValidCipherSuite(cs string) bool { - if cs == "" || cs == "ALL" { - return true - } - if !unicode.IsNumber(rune(cs[0])) && !unicode.IsLetter(rune(cs[0])) { - // Not all of these are correct, but this is needed to support advanced cases like - and + operators - // without needing to parse the full expression - return true - } - return ValidCipherSuites.Contains(cs) -} diff --git a/pkg/config/security/security_test.go b/pkg/config/security/security_test.go deleted file mode 100644 index c3946d8e3..000000000 --- a/pkg/config/security/security_test.go +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package security_test - -import ( - "reflect" - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/security" -) - -func TestParseJwksURI(t *testing.T) { - cases := []struct { - in string - expected security.JwksInfo - expectedError bool - }{ - { - in: "foo.bar.com", - expectedError: true, - }, - { - in: "tcp://foo.bar.com:abc", - expectedError: true, - }, - { - in: "http://foo.bar.com:abc", - expectedError: true, - }, - { - in: "http://foo.bar.com", - expected: security.JwksInfo{ - Hostname: "foo.bar.com", - Scheme: "http", - Port: 80, - UseSSL: false, - }, - }, - { - in: "https://foo.bar.com", - expected: security.JwksInfo{ - Hostname: "foo.bar.com", - Scheme: "https", - Port: 443, - UseSSL: true, - }, - }, - { - in: "http://foo.bar.com:1234", - expected: security.JwksInfo{ - Hostname: "foo.bar.com", - Scheme: "http", - Port: 1234, - UseSSL: false, - }, - }, - { - in: "https://foo.bar.com:1234/secure/key", - expected: security.JwksInfo{ - Hostname: "foo.bar.com", - Scheme: "https", - Port: 1234, - UseSSL: true, - }, - }, - } - for _, c := range cases { - actual, err := security.ParseJwksURI(c.in) - if c.expectedError == (err == nil) { - t.Fatalf("ParseJwksURI(%s): expected error (%v), got (%v)", c.in, c.expectedError, err) - } - if !reflect.DeepEqual(c.expected, actual) { - t.Fatalf("expected %+v, got %+v", c.expected, actual) - } - } -} - -func TestValidateCondition(t *testing.T) { - cases := []struct { - key string - values []string - wantError bool - }{ - { - key: "request.headers[:authority]", - values: []string{"productpage", ""}, - wantError: true, - }, - { - key: "request.headers[:authority]", - values: []string{"productpage"}, - }, - { - key: "request.headers[]", - values: []string{"productpage"}, - wantError: true, - }, - { - key: "source.ip", - values: []string{"1.2.3.4", "5.6.7.0/24"}, - }, - { - key: "source.ip", - values: []string{"a.b.c.d"}, - wantError: true, - }, - { - key: "remote.ip", - values: []string{"1.2.3.4", "5.6.7.0/24"}, - }, - { - key: "remote.ip", - values: []string{"a.b.c.d"}, - wantError: true, - }, - { - key: "source.namespace", - values: []string{"value"}, - }, - { - key: "source.user", - values: []string{"value"}, - wantError: true, - }, - { - key: "source.principal", - values: []string{"value"}, - }, - { - key: "request.auth.principal", - values: []string{"value"}, - }, - { - key: "request.auth.audiences", - values: []string{"value"}, - }, - { - key: "request.auth.presenter", - values: []string{"value"}, - }, - { - key: "request.auth.claims[id]", - values: []string{"123"}, - }, - { - key: "request.auth.claims[]", - values: []string{"value"}, - wantError: true, - }, - { - key: "destination.ip", - values: []string{"1.2.3.4", "5.6.7.0/24"}, - }, - { - key: "destination.ip", - values: []string{"a.b.c.d"}, - wantError: true, - }, - { - key: "destination.port", - values: []string{"80", "90"}, - }, - { - key: "destination.port", - values: []string{"80", "x"}, - wantError: true, - }, - { - key: "destination.labels[app]", - values: []string{"value"}, - wantError: true, - }, - { - key: "destination.name", - values: []string{"value"}, - wantError: true, - }, - { - key: "destination.namespace", - values: []string{"value"}, - wantError: true, - }, - { - key: "destination.user", - values: []string{"value"}, - wantError: true, - }, - { - key: "connection.sni", - values: []string{"value"}, - }, - { - key: "experimental.envoy.filters.a.b[c]", - values: []string{"value"}, - }, - { - key: "experimental.envoy.filters.a.b.x", - values: []string{"value"}, - wantError: true, - }, - } - for _, c := range cases { - err := security.ValidateAttribute(c.key, c.values) - if c.wantError == (err == nil) { - t.Fatalf("ValidateAttribute(%s): want error (%v) but got (%v)", c.key, c.wantError, err) - } - } -} diff --git a/pkg/config/validation/extensionprovider.go b/pkg/config/validation/extensionprovider.go deleted file mode 100644 index 2b6625429..000000000 --- a/pkg/config/validation/extensionprovider.go +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validation - -import ( - "fmt" - "net/url" - "strconv" - "strings" -) - -import ( - envoytypev3 "github.com/envoyproxy/go-control-plane/envoy/type/v3" - "github.com/hashicorp/go-multierror" - meshconfig "istio.io/api/mesh/v1alpha1" -) - -func validateExtensionProviderService(service string) error { - if service == "" { - return fmt.Errorf("service must not be empty") - } - parts := strings.Split(service, "/") - if len(parts) == 1 { - if err := ValidateFQDN(service); err != nil { - if err2 := ValidateIPAddress(service); err2 != nil { - return fmt.Errorf("invalid service fmt %s: %s", service, err2) - } - } - } else { - if err := validateNamespaceSlashWildcardHostname(service, false); err != nil { - return err - } - } - return nil -} - -func validateExtensionProviderEnvoyExtAuthzStatusOnError(status string) error { - if status == "" { - return nil - } - code, err := strconv.ParseInt(status, 10, 32) - if err != nil { - return fmt.Errorf("invalid statusOnError value %s: %v", status, err) - } - if _, found := envoytypev3.StatusCode_name[int32(code)]; !found { - return fmt.Errorf("unsupported statusOnError value %s, supported values: %v", status, envoytypev3.StatusCode_name) - } - return nil -} - -func ValidateExtensionProviderEnvoyExtAuthzHTTP(config *meshconfig.MeshConfig_ExtensionProvider_EnvoyExternalAuthorizationHttpProvider) (errs error) { - if config == nil { - return fmt.Errorf("nil EnvoyExternalAuthorizationHttpProvider") - } - if err := ValidatePort(int(config.Port)); err != nil { - errs = appendErrors(errs, err) - } - if err := validateExtensionProviderService(config.Service); err != nil { - errs = appendErrors(errs, err) - } - if err := validateExtensionProviderEnvoyExtAuthzStatusOnError(config.StatusOnError); err != nil { - errs = appendErrors(errs, err) - } - if config.PathPrefix != "" { - if _, err := url.Parse(config.PathPrefix); err != nil { - errs = appendErrors(errs, fmt.Errorf("invalid pathPrefix %s: %v", config.PathPrefix, err)) - } - if !strings.HasPrefix(config.PathPrefix, "/") { - errs = appendErrors(errs, fmt.Errorf("pathPrefix should begin with `/` but found %q", config.PathPrefix)) - } - } - return -} - -func ValidateExtensionProviderEnvoyExtAuthzGRPC(config *meshconfig.MeshConfig_ExtensionProvider_EnvoyExternalAuthorizationGrpcProvider) (errs error) { - if config == nil { - return fmt.Errorf("nil EnvoyExternalAuthorizationGrpcProvider") - } - if err := ValidatePort(int(config.Port)); err != nil { - errs = appendErrors(errs, fmt.Errorf("invalid service port: %v", err)) - } - if err := validateExtensionProviderService(config.Service); err != nil { - errs = appendErrors(errs, err) - } - if err := validateExtensionProviderEnvoyExtAuthzStatusOnError(config.StatusOnError); err != nil { - errs = appendErrors(errs, err) - } - return -} - -func validateExtensionProviderTracingZipkin(config *meshconfig.MeshConfig_ExtensionProvider_ZipkinTracingProvider) (errs error) { - if config == nil { - return fmt.Errorf("nil TracingZipkinProvider") - } - if err := validateExtensionProviderService(config.Service); err != nil { - errs = appendErrors(errs, err) - } - if err := ValidatePort(int(config.Port)); err != nil { - errs = appendErrors(errs, fmt.Errorf("invalid service port: %v", err)) - } - return -} - -func validateExtensionProviderTracingLightStep(config *meshconfig.MeshConfig_ExtensionProvider_LightstepTracingProvider) (errs error) { - if config == nil { - return fmt.Errorf("nil TracingLightStepProvider") - } - if err := validateExtensionProviderService(config.Service); err != nil { - errs = appendErrors(errs, err) - } - if err := ValidatePort(int(config.Port)); err != nil { - errs = appendErrors(errs, fmt.Errorf("invalid service port: %v", err)) - } - if config.AccessToken == "" { - errs = appendErrors(errs, fmt.Errorf("access token is required")) - } - return -} - -func validateExtensionProviderTracingDatadog(config *meshconfig.MeshConfig_ExtensionProvider_DatadogTracingProvider) (errs error) { - if config == nil { - return fmt.Errorf("nil TracingDatadogProvider") - } - if err := validateExtensionProviderService(config.Service); err != nil { - errs = appendErrors(errs, err) - } - if err := ValidatePort(int(config.Port)); err != nil { - errs = appendErrors(errs, fmt.Errorf("invalid service port: %v", err)) - } - return -} - -func validateExtensionProviderTracingOpenCensusAgent(config *meshconfig.MeshConfig_ExtensionProvider_OpenCensusAgentTracingProvider) (errs error) { - if config == nil { - return fmt.Errorf("nil OpenCensusAgent") - } - if err := validateExtensionProviderService(config.Service); err != nil { - errs = appendErrors(errs, err) - } - if err := ValidatePort(int(config.Port)); err != nil { - errs = appendErrors(errs, fmt.Errorf("invalid service port: %v", err)) - } - return -} - -func validateExtensionProviderTracingSkyWalking(config *meshconfig.MeshConfig_ExtensionProvider_SkyWalkingTracingProvider) (errs error) { - if config == nil { - return fmt.Errorf("nil TracingSkyWalkingProvider") - } - if err := validateExtensionProviderService(config.Service); err != nil { - errs = appendErrors(errs, err) - } - if err := ValidatePort(int(config.Port)); err != nil { - errs = appendErrors(errs, fmt.Errorf("invalid service port: %v", err)) - } - return -} - -func validateExtensionProviderMetricsPrometheus(_ *meshconfig.MeshConfig_ExtensionProvider_PrometheusMetricsProvider) error { - return nil -} - -func validateExtensionProviderStackdriver(_ *meshconfig.MeshConfig_ExtensionProvider_StackdriverProvider) error { - return nil -} - -func validateExtensionProviderEnvoyFileAccessLog(_ *meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider) error { - return nil -} - -func ValidateExtensionProviderEnvoyOtelAls(provider *meshconfig.MeshConfig_ExtensionProvider_EnvoyOpenTelemetryLogProvider) (errs error) { - if provider == nil { - return fmt.Errorf("nil EnvoyOpenTelemetryLogProvider") - } - if err := ValidatePort(int(provider.Port)); err != nil { - errs = appendErrors(errs, err) - } - if err := validateExtensionProviderService(provider.Service); err != nil { - errs = appendErrors(errs, err) - } - return -} - -func ValidateExtensionProviderEnvoyHTTPAls(provider *meshconfig.MeshConfig_ExtensionProvider_EnvoyHttpGrpcV3LogProvider) (errs error) { - if provider == nil { - return fmt.Errorf("nil EnvoyHttpGrpcV3LogProvider") - } - if err := ValidatePort(int(provider.Port)); err != nil { - errs = appendErrors(errs, err) - } - if err := validateExtensionProviderService(provider.Service); err != nil { - errs = appendErrors(errs, err) - } - return -} - -func ValidateExtensionProviderEnvoyTCPAls(provider *meshconfig.MeshConfig_ExtensionProvider_EnvoyTcpGrpcV3LogProvider) (errs error) { - if provider == nil { - return fmt.Errorf("nil EnvoyTcpGrpcV3LogProvider") - } - if err := ValidatePort(int(provider.Port)); err != nil { - errs = appendErrors(errs, err) - } - if err := validateExtensionProviderService(provider.Service); err != nil { - errs = appendErrors(errs, err) - } - return -} - -func validateExtensionProvider(config *meshconfig.MeshConfig) (errs error) { - definedProviders := map[string]struct{}{} - for _, c := range config.ExtensionProviders { - var currentErrs error - // Provider name must be unique and not empty. - if c.Name == "" { - currentErrs = appendErrors(currentErrs, fmt.Errorf("empty extension provider name")) - } else { - if _, found := definedProviders[c.Name]; found { - currentErrs = appendErrors(currentErrs, fmt.Errorf("duplicate extension provider name %s", c.Name)) - } - definedProviders[c.Name] = struct{}{} - } - - switch provider := c.Provider.(type) { - case *meshconfig.MeshConfig_ExtensionProvider_EnvoyExtAuthzHttp: - currentErrs = appendErrors(currentErrs, ValidateExtensionProviderEnvoyExtAuthzHTTP(provider.EnvoyExtAuthzHttp)) - case *meshconfig.MeshConfig_ExtensionProvider_EnvoyExtAuthzGrpc: - currentErrs = appendErrors(currentErrs, ValidateExtensionProviderEnvoyExtAuthzGRPC(provider.EnvoyExtAuthzGrpc)) - case *meshconfig.MeshConfig_ExtensionProvider_Zipkin: - currentErrs = appendErrors(currentErrs, validateExtensionProviderTracingZipkin(provider.Zipkin)) - case *meshconfig.MeshConfig_ExtensionProvider_Lightstep: - currentErrs = appendErrors(currentErrs, validateExtensionProviderTracingLightStep(provider.Lightstep)) - case *meshconfig.MeshConfig_ExtensionProvider_Datadog: - currentErrs = appendErrors(currentErrs, validateExtensionProviderTracingDatadog(provider.Datadog)) - case *meshconfig.MeshConfig_ExtensionProvider_Opencensus: - currentErrs = appendErrors(currentErrs, validateExtensionProviderTracingOpenCensusAgent(provider.Opencensus)) - case *meshconfig.MeshConfig_ExtensionProvider_Skywalking: - currentErrs = appendErrors(currentErrs, validateExtensionProviderTracingSkyWalking(provider.Skywalking)) - case *meshconfig.MeshConfig_ExtensionProvider_Prometheus: - currentErrs = appendErrors(currentErrs, validateExtensionProviderMetricsPrometheus(provider.Prometheus)) - case *meshconfig.MeshConfig_ExtensionProvider_Stackdriver: - currentErrs = appendErrors(currentErrs, validateExtensionProviderStackdriver(provider.Stackdriver)) - case *meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLog: - currentErrs = appendErrors(currentErrs, validateExtensionProviderEnvoyFileAccessLog(provider.EnvoyFileAccessLog)) - case *meshconfig.MeshConfig_ExtensionProvider_EnvoyOtelAls: - currentErrs = appendErrors(currentErrs, ValidateExtensionProviderEnvoyOtelAls(provider.EnvoyOtelAls)) - case *meshconfig.MeshConfig_ExtensionProvider_EnvoyHttpAls: - currentErrs = appendErrors(currentErrs, ValidateExtensionProviderEnvoyHTTPAls(provider.EnvoyHttpAls)) - case *meshconfig.MeshConfig_ExtensionProvider_EnvoyTcpAls: - currentErrs = appendErrors(currentErrs, ValidateExtensionProviderEnvoyTCPAls(provider.EnvoyTcpAls)) - // TODO: add exhaustiveness test - default: - currentErrs = appendErrors(currentErrs, fmt.Errorf("unsupported provider: %v of type %T", provider, provider)) - } - currentErrs = multierror.Prefix(currentErrs, fmt.Sprintf("invalid extension provider %s:", c.Name)) - errs = appendErrors(errs, currentErrs) - } - return -} diff --git a/pkg/config/validation/extensionprovider_test.go b/pkg/config/validation/extensionprovider_test.go deleted file mode 100644 index e15c8cf91..000000000 --- a/pkg/config/validation/extensionprovider_test.go +++ /dev/null @@ -1,464 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validation - -import ( - "testing" -) - -import ( - meshconfig "istio.io/api/mesh/v1alpha1" -) - -func TestValidateExtensionProviderService(t *testing.T) { - tests := []struct { - service string - valid bool - name string - }{ - { - service: "127.0.0.1", - valid: true, - name: "pure ip4 address", - }, - { - service: "2001:1::1", - valid: true, - name: "pure ip6 address", - }, - { - service: "istio-pilot.dubbo-system.svc.cluster.local", - valid: true, - name: "standard kubernetes FQDN", - }, - { - service: "istio-pilot.dubbo-system.svc.cluster.local:3000", - valid: false, - name: "standard kubernetes FQDN with port", - }, - { - service: "bar/istio.io", - valid: true, - name: "extension provider service with namespace", - }, - { - service: "bar/istio.io:3000", - valid: false, - name: "extension provider service with namespace and port", - }, - { - service: "bar/foo/istio.io", - valid: false, - name: "extension provider service with namespace", - }, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - err := validateExtensionProviderService(tt.service) - valid := err == nil - if valid != tt.valid { - t.Errorf("Expected valid=%v, got valid=%v for %v", tt.valid, valid, tt.service) - } - }) - } -} - -func TestValidateExtensionProviderTracingZipkin(t *testing.T) { - cases := []struct { - name string - config *meshconfig.MeshConfig_ExtensionProvider_ZipkinTracingProvider - valid bool - }{ - { - name: "zipkin normal", - config: &meshconfig.MeshConfig_ExtensionProvider_ZipkinTracingProvider{ - Service: "zipkin.dubbo-system", - Port: 9411, - }, - valid: true, - }, - { - name: "zipkin service with namespace", - config: &meshconfig.MeshConfig_ExtensionProvider_ZipkinTracingProvider{ - Service: "namespace/zipkin.dubbo-system", - Port: 9411, - }, - valid: true, - }, - { - name: "zipkin service with invalid namespace", - config: &meshconfig.MeshConfig_ExtensionProvider_ZipkinTracingProvider{ - Service: "name/space/zipkin.dubbo-system", - Port: 9411, - }, - valid: false, - }, - { - name: "zipkin service with port", - config: &meshconfig.MeshConfig_ExtensionProvider_ZipkinTracingProvider{ - Service: "zipkin.dubbo-system:9411", - Port: 9411, - }, - valid: false, - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - err := validateExtensionProviderTracingZipkin(c.config) - valid := err == nil - if valid != c.valid { - t.Errorf("Expected valid=%v, got valid=%v for %v", c.valid, valid, c.config) - } - }) - } -} - -func TestValidateExtensionProviderTracingLightstep(t *testing.T) { - cases := []struct { - name string - config *meshconfig.MeshConfig_ExtensionProvider_LightstepTracingProvider - valid bool - }{ - { - name: "lightstep normal", - config: &meshconfig.MeshConfig_ExtensionProvider_LightstepTracingProvider{ - Service: "collector.lightstep", - Port: 8080, - AccessToken: "abcdefg1234567", - }, - valid: true, - }, - { - name: "lightstep service with namespace", - config: &meshconfig.MeshConfig_ExtensionProvider_LightstepTracingProvider{ - Service: "namespace/collector.lightstep", - Port: 8080, - AccessToken: "abcdefg1234567", - }, - valid: true, - }, - { - name: "lightstep service with missing port", - config: &meshconfig.MeshConfig_ExtensionProvider_LightstepTracingProvider{ - Service: "10.0.0.100", - AccessToken: "abcdefg1234567", - }, - valid: false, - }, - { - name: "lightstep service with missing accesstoken", - config: &meshconfig.MeshConfig_ExtensionProvider_LightstepTracingProvider{ - Service: "namespace/collector.lightstep", - Port: 8080, - }, - valid: false, - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - err := validateExtensionProviderTracingLightStep(c.config) - valid := err == nil - if valid != c.valid { - t.Errorf("Expected valid=%v, got valid=%v for %v", c.valid, valid, c.config) - } - }) - } -} - -func TestValidateExtensionProviderTracingDatadog(t *testing.T) { - cases := []struct { - name string - config *meshconfig.MeshConfig_ExtensionProvider_DatadogTracingProvider - valid bool - }{ - { - name: "datadog normal", - config: &meshconfig.MeshConfig_ExtensionProvider_DatadogTracingProvider{ - Service: "datadog-agent.com", - Port: 8126, - }, - valid: true, - }, - { - name: "datadog service with namespace", - config: &meshconfig.MeshConfig_ExtensionProvider_DatadogTracingProvider{ - Service: "namespace/datadog-agent.com", - Port: 8126, - }, - valid: true, - }, - { - name: "datadog service with invalid namespace", - config: &meshconfig.MeshConfig_ExtensionProvider_DatadogTracingProvider{ - Service: "name/space/datadog-agent.com", - Port: 8126, - }, - valid: false, - }, - { - name: "datadog service with port", - config: &meshconfig.MeshConfig_ExtensionProvider_DatadogTracingProvider{ - Service: "datadog-agent.com:8126", - Port: 8126, - }, - valid: false, - }, - { - name: "datadog missing port", - config: &meshconfig.MeshConfig_ExtensionProvider_DatadogTracingProvider{ - Service: "datadog-agent.com", - }, - valid: false, - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - err := validateExtensionProviderTracingDatadog(c.config) - valid := err == nil - if valid != c.valid { - t.Errorf("Expected valid=%v, got valid=%v for %v", c.valid, valid, c.config) - } - }) - } -} - -func TestValidateExtensionProviderTracingOpenCensusAgent(t *testing.T) { - cases := []struct { - name string - config *meshconfig.MeshConfig_ExtensionProvider_OpenCensusAgentTracingProvider - valid bool - }{ - { - name: "opencensus normal", - config: &meshconfig.MeshConfig_ExtensionProvider_OpenCensusAgentTracingProvider{ - Service: "opencensus-agent.com", - Port: 4000, - }, - valid: true, - }, - { - name: "opencensus service with namespace", - config: &meshconfig.MeshConfig_ExtensionProvider_OpenCensusAgentTracingProvider{ - Service: "namespace/opencensus-agent.com", - Port: 4000, - }, - valid: true, - }, - { - name: "opencensus service with invalid namespace", - config: &meshconfig.MeshConfig_ExtensionProvider_OpenCensusAgentTracingProvider{ - Service: "name/space/opencensus-agent.com", - Port: 4000, - }, - valid: false, - }, - { - name: "opencensus service with port", - config: &meshconfig.MeshConfig_ExtensionProvider_OpenCensusAgentTracingProvider{ - Service: "opencensus-agent.com:4000", - Port: 4000, - }, - valid: false, - }, - { - name: "opencensus missing port", - config: &meshconfig.MeshConfig_ExtensionProvider_OpenCensusAgentTracingProvider{ - Service: "opencensus-agent.com", - }, - valid: false, - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - err := validateExtensionProviderTracingOpenCensusAgent(c.config) - valid := err == nil - if valid != c.valid { - t.Errorf("Expected valid=%v, got valid=%v for %v", c.valid, valid, c.config) - } - }) - } -} - -func TestValidateExtensionProviderEnvoyOtelAls(t *testing.T) { - cases := []struct { - name string - provider *meshconfig.MeshConfig_ExtensionProvider_EnvoyOpenTelemetryLogProvider - valid bool - }{ - { - name: "otel normal", - provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyOpenTelemetryLogProvider{ - Service: "otel.istio-syste.svc", - Port: 4000, - }, - valid: true, - }, - { - name: "otel service with namespace", - provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyOpenTelemetryLogProvider{ - Service: "namespace/otel.istio-syste.svc", - Port: 4000, - }, - valid: true, - }, - { - name: "otel service with invalid namespace", - provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyOpenTelemetryLogProvider{ - Service: "name/space/otel.istio-syste.svc", - Port: 4000, - }, - valid: false, - }, - { - name: "otel service with port", - provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyOpenTelemetryLogProvider{ - Service: "otel.istio-syste.svc:4000", - Port: 4000, - }, - valid: false, - }, - { - name: "otel missing port", - provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyOpenTelemetryLogProvider{ - Service: "otel.istio-syste.svc", - }, - valid: false, - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - err := ValidateExtensionProviderEnvoyOtelAls(c.provider) - valid := err == nil - if valid != c.valid { - t.Errorf("Expected valid=%v, got valid=%v for %v", c.valid, valid, c.provider) - } - }) - } -} - -func TestValidateExtensionProviderEnvoyHTTPAls(t *testing.T) { - cases := []struct { - name string - provider *meshconfig.MeshConfig_ExtensionProvider_EnvoyHttpGrpcV3LogProvider - valid bool - }{ - { - name: "normal", - provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyHttpGrpcV3LogProvider{ - Service: "grpc-als.istio-syste.svc", - Port: 4000, - }, - valid: true, - }, - { - name: "service with namespace", - provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyHttpGrpcV3LogProvider{ - Service: "namespace/grpc-als.istio-syste.svc", - Port: 4000, - }, - valid: true, - }, - { - name: "service with invalid namespace", - provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyHttpGrpcV3LogProvider{ - Service: "name/space/grpc-als.istio-syste.svc", - Port: 4000, - }, - valid: false, - }, - { - name: "service with port", - provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyHttpGrpcV3LogProvider{ - Service: "grpc-als.istio-syste.svc:4000", - Port: 4000, - }, - valid: false, - }, - { - name: "missing port", - provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyHttpGrpcV3LogProvider{ - Service: "grpc-als.istio-syste.svc", - }, - valid: false, - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - err := ValidateExtensionProviderEnvoyHTTPAls(c.provider) - valid := err == nil - if valid != c.valid { - t.Errorf("Expected valid=%v, got valid=%v for %v", c.valid, valid, c.provider) - } - }) - } -} - -func TestValidateExtensionProviderEnvoyTCPAls(t *testing.T) { - cases := []struct { - name string - provider *meshconfig.MeshConfig_ExtensionProvider_EnvoyTcpGrpcV3LogProvider - valid bool - }{ - { - name: "normal", - provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyTcpGrpcV3LogProvider{ - Service: "grpc-als.istio-syste.svc", - Port: 4000, - }, - valid: true, - }, - { - name: "service with namespace", - provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyTcpGrpcV3LogProvider{ - Service: "namespace/grpc-als.istio-syste.svc", - Port: 4000, - }, - valid: true, - }, - { - name: "service with invalid namespace", - provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyTcpGrpcV3LogProvider{ - Service: "name/space/grpc-als.istio-syste.svc", - Port: 4000, - }, - valid: false, - }, - { - name: "service with port", - provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyTcpGrpcV3LogProvider{ - Service: "grpc-als.istio-syste.svc:4000", - Port: 4000, - }, - valid: false, - }, - { - name: "missing port", - provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyTcpGrpcV3LogProvider{ - Service: "grpc-als.istio-syste.svc", - }, - valid: false, - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - err := ValidateExtensionProviderEnvoyTCPAls(c.provider) - valid := err == nil - if valid != c.valid { - t.Errorf("Expected valid=%v, got valid=%v for %v", c.valid, valid, c.provider) - } - }) - } -} diff --git a/pkg/config/validation/validation.go b/pkg/config/validation/validation.go deleted file mode 100644 index a9cd8218a..000000000 --- a/pkg/config/validation/validation.go +++ /dev/null @@ -1,3766 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validation - -import ( - "encoding/json" - "errors" - "fmt" - "net" - "net/http" - "net/url" - "path" - "regexp" - "strconv" - "strings" - "time" -) - -import ( - udpaa "github.com/cncf/xds/go/udpa/annotations" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "github.com/hashicorp/go-multierror" - "github.com/lestrrat-go/jwx/jwk" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" - "google.golang.org/protobuf/reflect/protoregistry" - "google.golang.org/protobuf/types/descriptorpb" - any "google.golang.org/protobuf/types/known/anypb" - "google.golang.org/protobuf/types/known/durationpb" - "istio.io/api/annotation" - extensions "istio.io/api/extensions/v1alpha1" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" - networkingv1beta1 "istio.io/api/networking/v1beta1" - security_beta "istio.io/api/security/v1beta1" - telemetry "istio.io/api/telemetry/v1alpha1" - type_beta "istio.io/api/type/v1beta1" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/util/constant" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/gateway" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/config/security" - "github.com/apache/dubbo-go-pixiu/pkg/config/visibility" - "github.com/apache/dubbo-go-pixiu/pkg/config/xds" - "github.com/apache/dubbo-go-pixiu/pkg/kube/apimirror" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// Constants for duration fields -const ( - // nolint: revive - connectTimeoutMax = time.Second * 30 - // nolint: revive - connectTimeoutMin = time.Millisecond - - drainTimeMax = time.Hour - parentShutdownTimeMax = time.Hour - - // UnixAddressPrefix is the prefix used to indicate an address is for a Unix Domain socket. It is used in - // ServiceEntry.Endpoint.Address message. - UnixAddressPrefix = "unix://" - - matchExact = "exact:" - matchPrefix = "prefix:" -) - -const ( - regionIndex int = iota - zoneIndex - subZoneIndex -) - -var ( - // envoy supported retry on header values - supportedRetryOnPolicies = map[string]bool{ - // 'x-envoy-retry-on' supported policies: - // https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/router_filter.html#x-envoy-retry-on - "5xx": true, - "gateway-error": true, - "reset": true, - "connect-failure": true, - "retriable-4xx": true, - "refused-stream": true, - "retriable-status-codes": true, - "retriable-headers": true, - "envoy-ratelimited": true, - - // 'x-envoy-retry-grpc-on' supported policies: - // https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/router_filter#x-envoy-retry-grpc-on - "cancelled": true, - "deadline-exceeded": true, - "internal": true, - "resource-exhausted": true, - "unavailable": true, - } - - // golang supported methods: https://golang.org/src/net/http/method.go - supportedMethods = map[string]bool{ - http.MethodGet: true, - http.MethodHead: true, - http.MethodPost: true, - http.MethodPut: true, - http.MethodPatch: true, - http.MethodDelete: true, - http.MethodConnect: true, - http.MethodOptions: true, - http.MethodTrace: true, - } - - scope = log.RegisterScope("validation", "CRD validation debugging", 0) - - // EmptyValidate is a Validate that does nothing and returns no error. - EmptyValidate = registerValidateFunc("EmptyValidate", - func(config.Config) (Warning, error) { - return nil, nil - }) - - validateFuncs = make(map[string]ValidateFunc) -) - -type Warning error - -// Validation holds errors and warnings. They can be joined with additional errors by called appendValidation -type Validation struct { - Err error - Warning Warning -} - -type AnalysisAwareError struct { - Type string - Msg string - Parameters []interface{} -} - -// OverlappingMatchValidationForHTTPRoute holds necessary information from virtualservice -// to do such overlapping match validation -type OverlappingMatchValidationForHTTPRoute struct { - RouteStr string - MatchStr string - Prefix string - MatchPort uint32 - MatchMethod string - MatchAuthority string - MatchHeaders map[string]string - MatchQueryParams map[string]string - MatchNonHeaders map[string]string -} - -var _ error = Validation{} - -// WrapError turns an error into a Validation -func WrapError(e error) Validation { - return Validation{Err: e} -} - -// WrapWarning turns an error into a Validation as a warning -func WrapWarning(e error) Validation { - return Validation{Warning: e} -} - -// Warningf formats according to a format specifier and returns the string as a -// value that satisfies error. Like Errorf, but for warnings. -func Warningf(format string, a ...interface{}) Validation { - return WrapWarning(fmt.Errorf(format, a...)) -} - -func (v Validation) Unwrap() (Warning, error) { - return v.Warning, v.Err -} - -func (v Validation) Error() string { - if v.Err == nil { - return "" - } - return v.Err.Error() -} - -// ValidateFunc defines a validation func for an API proto. -type ValidateFunc func(config config.Config) (Warning, error) - -// IsValidateFunc indicates whether there is a validation function with the given name. -func IsValidateFunc(name string) bool { - return GetValidateFunc(name) != nil -} - -// GetValidateFunc returns the validation function with the given name, or null if it does not exist. -func GetValidateFunc(name string) ValidateFunc { - return validateFuncs[name] -} - -func registerValidateFunc(name string, f ValidateFunc) ValidateFunc { - // Wrap the original validate function with an extra validate function for the annotation "istio.io/dry-run". - validate := validateAnnotationDryRun(f) - validateFuncs[name] = validate - return validate -} - -func validateAnnotationDryRun(f ValidateFunc) ValidateFunc { - return func(config config.Config) (Warning, error) { - _, isAuthz := config.Spec.(*security_beta.AuthorizationPolicy) - // Only the AuthorizationPolicy supports the annotation "istio.io/dry-run". - if err := checkDryRunAnnotation(config, isAuthz); err != nil { - return nil, err - } - return f(config) - } -} - -func checkDryRunAnnotation(cfg config.Config, allowed bool) error { - if val, found := cfg.Annotations[annotation.IoIstioDryRun.Name]; found { - if !allowed { - return fmt.Errorf("%s/%s has unsupported annotation %s, please remove the annotation", cfg.Namespace, cfg.Name, annotation.IoIstioDryRun.Name) - } - if spec, ok := cfg.Spec.(*security_beta.AuthorizationPolicy); ok { - switch spec.Action { - case security_beta.AuthorizationPolicy_ALLOW, security_beta.AuthorizationPolicy_DENY: - if _, err := strconv.ParseBool(val); err != nil { - return fmt.Errorf("%s/%s has annotation %s with invalid value (%s): %v", cfg.Namespace, cfg.Name, annotation.IoIstioDryRun.Name, val, err) - } - default: - return fmt.Errorf("the annotation %s currently only supports action ALLOW/DENY, found action %v in %s/%s", - annotation.IoIstioDryRun.Name, spec.Action, cfg.Namespace, cfg.Name) - } - } - } - return nil -} - -// ValidatePort checks that the network port is in range -func ValidatePort(port int) error { - if 1 <= port && port <= 65535 { - return nil - } - return fmt.Errorf("port number %d must be in the range 1..65535", port) -} - -// ValidateFQDN checks a fully-qualified domain name -func ValidateFQDN(fqdn string) error { - if err := checkDNS1123Preconditions(fqdn); err != nil { - return err - } - return validateDNS1123Labels(fqdn) -} - -// ValidateWildcardDomain checks that a domain is a valid FQDN, but also allows wildcard prefixes. -func ValidateWildcardDomain(domain string) error { - if err := checkDNS1123Preconditions(domain); err != nil { - return err - } - // We only allow wildcards in the first label; split off the first label (parts[0]) from the rest of the host (parts[1]) - parts := strings.SplitN(domain, ".", 2) - if !labels.IsWildcardDNS1123Label(parts[0]) { - return fmt.Errorf("domain name %q invalid (label %q invalid)", domain, parts[0]) - } else if len(parts) > 1 { - return validateDNS1123Labels(parts[1]) - } - return nil -} - -// encapsulates DNS 1123 checks common to both wildcarded hosts and FQDNs -func checkDNS1123Preconditions(name string) error { - if len(name) > 255 { - return fmt.Errorf("domain name %q too long (max 255)", name) - } - if len(name) == 0 { - return fmt.Errorf("empty domain name not allowed") - } - return nil -} - -func validateDNS1123Labels(domain string) error { - parts := strings.Split(domain, ".") - topLevelDomain := parts[len(parts)-1] - if _, err := strconv.Atoi(topLevelDomain); err == nil { - return fmt.Errorf("domain name %q invalid (top level domain %q cannot be all-numeric)", domain, topLevelDomain) - } - for i, label := range parts { - // Allow the last part to be empty, for unambiguous names like `istio.io.` - if i == len(parts)-1 && label == "" { - return nil - } - if !labels.IsDNS1123Label(label) { - return fmt.Errorf("domain name %q invalid (label %q invalid)", domain, label) - } - } - return nil -} - -// validate the trust domain format -func ValidateTrustDomain(domain string) error { - if len(domain) == 0 { - return fmt.Errorf("empty domain name not allowed") - } - parts := strings.Split(domain, ".") - for i, label := range parts { - // Allow the last part to be empty, for unambiguous names like `istio.io.` - if i == len(parts)-1 && label == "" { - return nil - } - if !labels.IsDNS1123Label(label) { - return fmt.Errorf("trust domain name %q invalid", domain) - } - } - return nil -} - -// ValidateHTTPHeaderName validates a header name -func ValidateHTTPHeaderName(name string) error { - if name == "" { - return fmt.Errorf("header name cannot be empty") - } - return nil -} - -// ValidateHTTPHeaderWithAuthorityOperationName validates a header name when used to add/set in request. -func ValidateHTTPHeaderWithAuthorityOperationName(name string) error { - if name == "" { - return fmt.Errorf("header name cannot be empty") - } - // Authority header is validated later - if isInternalHeader(name) && !isAuthorityHeader(name) { - return fmt.Errorf(`invalid header %q: header cannot have ":" prefix`, name) - } - return nil -} - -// ValidateHTTPHeaderWithHostOperationName validates a header name when used to destination specific add/set in request. -// TODO(https://github.com/envoyproxy/envoy/issues/16775) merge with ValidateHTTPHeaderWithAuthorityOperationName -func ValidateHTTPHeaderWithHostOperationName(name string) error { - if name == "" { - return fmt.Errorf("header name cannot be empty") - } - // Authority header is validated later - if isInternalHeader(name) && !strings.EqualFold(name, "host") { - return fmt.Errorf(`invalid header %q: header cannot have ":" prefix`, name) - } - return nil -} - -// ValidateHTTPHeaderOperationName validates a header name when used to remove from request or modify response. -func ValidateHTTPHeaderOperationName(name string) error { - if name == "" { - return fmt.Errorf("header name cannot be empty") - } - if strings.EqualFold(name, "host") { - return fmt.Errorf(`invalid header %q: cannot set Host header`, name) - } - if isInternalHeader(name) { - return fmt.Errorf(`invalid header %q: header cannot have ":" prefix`, name) - } - return nil -} - -// ValidateHTTPHeaderValue validates a header value for Envoy -// Valid: "foo", "%HOSTNAME%", "100%%", "prefix %HOSTNAME% suffix" -// Invalid: "abc%123" -// We don't try to check that what is inside the %% is one of Envoy recognized values, we just prevent invalid config. -// See: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers.html#custom-request-response-headers -func ValidateHTTPHeaderValue(value string) error { - if strings.Count(value, "%")%2 != 0 { - return errors.New("single % not allowed. Escape by doubling to %% or encase Envoy variable name in pair of %") - } - return nil -} - -// ValidatePercent checks that percent is in range -func ValidatePercent(val int32) error { - if val < 0 || val > 100 { - return fmt.Errorf("percentage %v is not in range 0..100", val) - } - return nil -} - -// validatePercentage checks if the specified fractional percentage is valid. -func validatePercentage(percentage *networking.Percent) error { - if percentage != nil { - if percentage.Value < 0.0 || percentage.Value > 100.0 || (percentage.Value > 0.0 && percentage.Value < 0.0001) { - return fmt.Errorf("percentage %v is neither 0.0, nor in range [0.0001, 100.0]", percentage.Value) - } - } - return nil -} - -// ValidateIPSubnet checks that a string is in "CIDR notation" or "Dot-decimal notation" -func ValidateIPSubnet(subnet string) error { - // We expect a string in "CIDR notation" or "Dot-decimal notation" - // E.g., a.b.c.d/xx form or just a.b.c.d or 2001:1::1/64 - if strings.Count(subnet, "/") == 1 { - // We expect a string in "CIDR notation", i.e. a.b.c.d/xx or 2001:1::1/64 form - ip, _, err := net.ParseCIDR(subnet) - if err != nil { - return fmt.Errorf("%v is not a valid CIDR block", subnet) - } - if ip.To4() == nil && ip.To16() == nil { - return fmt.Errorf("%v is not a valid IPv4 or IPv6 address", subnet) - } - return nil - } - return ValidateIPAddress(subnet) -} - -// ValidateIPAddress validates that a string in "CIDR notation" or "Dot-decimal notation" -func ValidateIPAddress(addr string) error { - ip := net.ParseIP(addr) - if ip == nil { - return fmt.Errorf("%v is not a valid IP", addr) - } - - return nil -} - -// ValidateUnixAddress validates that the string is a valid unix domain socket path. -func ValidateUnixAddress(addr string) error { - if len(addr) == 0 { - return errors.New("unix address must not be empty") - } - - // Allow unix abstract domain sockets whose names start with @ - if strings.HasPrefix(addr, "@") { - return nil - } - - // Note that we use path, not path/filepath even though a domain socket path is a file path. We don't want the - // Pilot output to depend on which OS Pilot is run on, so we always use Unix-style forward slashes. - if !path.IsAbs(addr) || strings.HasSuffix(addr, "/") { - return fmt.Errorf("%s is not an absolute path to a file", addr) - } - return nil -} - -// ValidateGateway checks gateway specifications -var ValidateGateway = registerValidateFunc("ValidateGateway", - func(cfg config.Config) (Warning, error) { - name := cfg.Name - v := Validation{} - // Gateway name must conform to the DNS label format (no dots) - if !labels.IsDNS1123Label(name) { - v = appendValidation(v, fmt.Errorf("invalid gateway name: %q", name)) - } - value, ok := cfg.Spec.(*networking.Gateway) - if !ok { - v = appendValidation(v, fmt.Errorf("cannot cast to gateway: %#v", cfg.Spec)) - return v.Unwrap() - } - - if len(value.Servers) == 0 { - v = appendValidation(v, fmt.Errorf("gateway must have at least one server")) - } else { - for _, server := range value.Servers { - v = appendValidation(v, validateServer(server)) - } - } - - // Ensure unique port names - portNames := make(map[string]bool) - - for _, s := range value.Servers { - if s == nil { - v = appendValidation(v, fmt.Errorf("server may not be nil")) - continue - } - if s.Port != nil { - if portNames[s.Port.Name] { - v = appendValidation(v, fmt.Errorf("port names in servers must be unique: duplicate name %s", s.Port.Name)) - } - portNames[s.Port.Name] = true - if !protocol.Parse(s.Port.Protocol).IsHTTP() && s.GetTls().GetHttpsRedirect() { - v = appendValidation(v, WrapWarning(fmt.Errorf("tls.httpsRedirect should only be used with http servers"))) - } - } - } - - return v.Unwrap() - }) - -func validateServer(server *networking.Server) (v Validation) { - if server == nil { - return WrapError(fmt.Errorf("cannot have nil server")) - } - if len(server.Hosts) == 0 { - v = appendValidation(v, fmt.Errorf("server config must contain at least one host")) - } else { - for _, hostname := range server.Hosts { - v = appendValidation(v, validateNamespaceSlashWildcardHostname(hostname, true)) - } - } - portErr := validateServerPort(server.Port) - if portErr != nil { - v = appendValidation(v, portErr) - } - v = appendValidation(v, validateServerBind(server.Port, server.Bind)) - v = appendValidation(v, validateTLSOptions(server.Tls)) - - // If port is HTTPS or TLS, make sure that server has TLS options - if portErr == nil { - p := protocol.Parse(server.Port.Protocol) - if p.IsTLS() && server.Tls == nil { - v = appendValidation(v, fmt.Errorf("server must have TLS settings for HTTPS/TLS protocols")) - } else if !p.IsTLS() && server.Tls != nil { - // only tls redirect is allowed if this is a HTTP server - if p.IsHTTP() { - if !gateway.IsPassThroughServer(server) || - server.Tls.CaCertificates != "" || server.Tls.PrivateKey != "" || server.Tls.ServerCertificate != "" { - v = appendValidation(v, fmt.Errorf("server cannot have TLS settings for plain text HTTP ports")) - } - } else { - v = appendValidation(v, fmt.Errorf("server cannot have TLS settings for non HTTPS/TLS ports")) - } - } - } - return v -} - -func validateServerPort(port *networking.Port) (errs error) { - if port == nil { - return appendErrors(errs, fmt.Errorf("port is required")) - } - if protocol.Parse(port.Protocol) == protocol.Unsupported { - errs = appendErrors(errs, fmt.Errorf("invalid protocol %q, supported protocols are HTTP, HTTP2, GRPC, GRPC-WEB, MONGO, REDIS, MYSQL, TCP", port.Protocol)) - } - if port.Number > 0 { - errs = appendErrors(errs, ValidatePort(int(port.Number))) - } - - if port.Name == "" { - errs = appendErrors(errs, fmt.Errorf("port name must be set: %v", port)) - } - return -} - -func validateServerBind(port *networking.Port, bind string) (errs error) { - if strings.HasPrefix(bind, UnixAddressPrefix) { - errs = appendErrors(errs, ValidateUnixAddress(strings.TrimPrefix(bind, UnixAddressPrefix))) - if port != nil && port.Number != 0 { - errs = appendErrors(errs, fmt.Errorf("port number must be 0 for unix domain socket: %v", port)) - } - } else if len(bind) != 0 { - errs = appendErrors(errs, ValidateIPAddress(bind)) - } - return -} - -func validateTLSOptions(tls *networking.ServerTLSSettings) (v Validation) { - if tls == nil { - // no tls config at all is valid - return - } - - invalidCiphers := sets.New() - validCiphers := sets.New() - duplicateCiphers := sets.New() - for _, cs := range tls.CipherSuites { - if !security.IsValidCipherSuite(cs) { - invalidCiphers.Insert(cs) - } else { - if !validCiphers.Contains(cs) { - validCiphers.Insert(cs) - } else { - duplicateCiphers.Insert(cs) - } - } - } - - if len(invalidCiphers) > 0 { - v = appendWarningf(v, "ignoring invalid cipher suites: %v", invalidCiphers.SortedList()) - } - - if len(duplicateCiphers) > 0 { - v = appendWarningf(v, "ignoring duplicate cipher suites: %v", duplicateCiphers.SortedList()) - } - - if tls.Mode == networking.ServerTLSSettings_ISTIO_MUTUAL { - // ISTIO_MUTUAL TLS mode uses either SDS or default certificate mount paths - // therefore, we should fail validation if other TLS fields are set - if tls.ServerCertificate != "" { - v = appendValidation(v, fmt.Errorf("ISTIO_MUTUAL TLS cannot have associated server certificate")) - } - if tls.PrivateKey != "" { - v = appendValidation(v, fmt.Errorf("ISTIO_MUTUAL TLS cannot have associated private key")) - } - if tls.CaCertificates != "" { - v = appendValidation(v, fmt.Errorf("ISTIO_MUTUAL TLS cannot have associated CA bundle")) - } - if tls.CredentialName != "" { - if features.EnableLegacyIstioMutualCredentialName { - // Legacy mode enabled, just warn - v = appendWarningf(v, "ISTIO_MUTUAL TLS cannot have associated credentialName") - } else { - v = appendValidation(v, fmt.Errorf("ISTIO_MUTUAL TLS cannot have associated credentialName")) - } - } - return - } - - if tls.Mode == networking.ServerTLSSettings_PASSTHROUGH || tls.Mode == networking.ServerTLSSettings_AUTO_PASSTHROUGH { - if tls.ServerCertificate != "" || tls.PrivateKey != "" || tls.CaCertificates != "" || tls.CredentialName != "" { - // Warn for backwards compatibility - v = appendWarningf(v, "%v mode does not use certificates, they will be ignored", tls.Mode) - } - } - - if (tls.Mode == networking.ServerTLSSettings_SIMPLE || tls.Mode == networking.ServerTLSSettings_MUTUAL) && tls.CredentialName != "" { - // If tls mode is SIMPLE or MUTUAL, and CredentialName is specified, credentials are fetched - // remotely. ServerCertificate and CaCertificates fields are not required. - return - } - if tls.Mode == networking.ServerTLSSettings_SIMPLE { - if tls.ServerCertificate == "" { - v = appendValidation(v, fmt.Errorf("SIMPLE TLS requires a server certificate")) - } - if tls.PrivateKey == "" { - v = appendValidation(v, fmt.Errorf("SIMPLE TLS requires a private key")) - } - } else if tls.Mode == networking.ServerTLSSettings_MUTUAL { - if tls.ServerCertificate == "" { - v = appendValidation(v, fmt.Errorf("MUTUAL TLS requires a server certificate")) - } - if tls.PrivateKey == "" { - v = appendValidation(v, fmt.Errorf("MUTUAL TLS requires a private key")) - } - if tls.CaCertificates == "" { - v = appendValidation(v, fmt.Errorf("MUTUAL TLS requires a client CA bundle")) - } - } - return -} - -// ValidateDestinationRule checks proxy policies -var ValidateDestinationRule = registerValidateFunc("ValidateDestinationRule", - func(cfg config.Config) (Warning, error) { - rule, ok := cfg.Spec.(*networking.DestinationRule) - if !ok { - return nil, fmt.Errorf("cannot cast to destination rule") - } - v := Validation{} - if features.EnableDestinationRuleInheritance { - if rule.Host == "" { - if rule.GetWorkloadSelector() != nil { - v = appendValidation(v, - fmt.Errorf("mesh/namespace destination rule cannot have workloadSelector configured")) - } - if len(rule.Subsets) != 0 { - v = appendValidation(v, - fmt.Errorf("mesh/namespace destination rule cannot have subsets")) - } - if len(rule.ExportTo) != 0 { - v = appendValidation(v, - fmt.Errorf("mesh/namespace destination rule cannot have exportTo configured")) - } - if rule.TrafficPolicy != nil && len(rule.TrafficPolicy.PortLevelSettings) != 0 { - v = appendValidation(v, - fmt.Errorf("mesh/namespace destination rule cannot have portLevelSettings configured")) - } - } else { - v = appendValidation(v, ValidateWildcardDomain(rule.Host)) - } - } else { - v = appendValidation(v, ValidateWildcardDomain(rule.Host)) - } - - v = appendValidation(v, validateTrafficPolicy(rule.TrafficPolicy)) - - for _, subset := range rule.Subsets { - if subset == nil { - v = appendValidation(v, errors.New("subset may not be null")) - continue - } - v = appendValidation(v, validateSubset(subset)) - } - v = appendValidation(v, - validateExportTo(cfg.Namespace, rule.ExportTo, false, rule.GetWorkloadSelector() != nil)) - - v = appendValidation(v, validateWorkloadSelector(rule.GetWorkloadSelector())) - - return v.Unwrap() - }) - -func validateExportTo(namespace string, exportTo []string, isServiceEntry bool, isDestinationRuleWithSelector bool) (errs error) { - if len(exportTo) > 0 { - // Make sure there are no duplicates - exportToSet := sets.New() - for _, e := range exportTo { - key := e - if visibility.Instance(e) == visibility.Private { - // substitute this with the current namespace so that we - // can check for duplicates like ., namespace - key = namespace - } - if exportToSet.Contains(key) { - if key != e { - errs = appendErrors(errs, fmt.Errorf("duplicate entries in exportTo: . and current namespace %s", namespace)) - } else { - errs = appendErrors(errs, fmt.Errorf("duplicate entries in exportTo for entry %s", e)) - } - } else { - // if this is a serviceEntry, allow ~ in exportTo as it can be used to create - // a service that is not even visible within the local namespace to anyone other - // than the proxies of that service. - if isServiceEntry && visibility.Instance(e) == visibility.None { - exportToSet.Insert(key) - } else { - if err := visibility.Instance(key).Validate(); err != nil { - errs = appendErrors(errs, err) - } else { - exportToSet.Insert(key) - } - } - } - } - - // Make sure workloadSelector based destination rule does not use exportTo other than current namespace - if isDestinationRuleWithSelector && !exportToSet.IsEmpty() { - if exportToSet.Contains(namespace) { - if len(exportToSet) > 1 { - errs = appendErrors(errs, fmt.Errorf("destination rule with workload selector cannot have multiple entries in exportTo")) - } - } else { - errs = appendErrors(errs, fmt.Errorf("destination rule with workload selector cannot have exportTo beyond current namespace")) - } - } - - // Make sure we have only one of . or * - if exportToSet.Contains(string(visibility.Public)) { - // make sure that there are no other entries in the exportTo - // i.e. no point in saying ns1,ns2,*. Might as well say * - if len(exportTo) > 1 { - errs = appendErrors(errs, fmt.Errorf("cannot have both public (*) and non-public exportTo values for a resource")) - } - } - - // if this is a service entry, then we need to disallow * and ~ together. Or ~ and other namespaces - if exportToSet.Contains(string(visibility.None)) { - if len(exportTo) > 1 { - errs = appendErrors(errs, fmt.Errorf("cannot export service entry to no one (~) and someone")) - } - } - } - - return -} - -func validateAlphaWorkloadSelector(selector *networking.WorkloadSelector) error { - var errs error - if selector != nil { - for k, v := range selector.Labels { - if k == "" { - errs = appendErrors(errs, - fmt.Errorf("empty key is not supported in selector: %q", fmt.Sprintf("%s=%s", k, v))) - } - if strings.Contains(k, "*") || strings.Contains(v, "*") { - errs = appendErrors(errs, - fmt.Errorf("wildcard is not supported in selector: %q", fmt.Sprintf("%s=%s", k, v))) - } - } - } - - return errs -} - -// ValidateEnvoyFilter checks envoy filter config supplied by user -var ValidateEnvoyFilter = registerValidateFunc("ValidateEnvoyFilter", - func(cfg config.Config) (Warning, error) { - errs := Validation{} - rule, ok := cfg.Spec.(*networking.EnvoyFilter) - if !ok { - return nil, fmt.Errorf("cannot cast to Envoy filter") - } - - if err := validateAlphaWorkloadSelector(rule.WorkloadSelector); err != nil { - return nil, err - } - - for _, cp := range rule.ConfigPatches { - if cp == nil { - errs = appendValidation(errs, fmt.Errorf("Envoy filter: null config patch")) // nolint: stylecheck - continue - } - if cp.ApplyTo == networking.EnvoyFilter_INVALID { - errs = appendValidation(errs, fmt.Errorf("Envoy filter: missing applyTo")) // nolint: stylecheck - continue - } - if cp.Patch == nil { - errs = appendValidation(errs, fmt.Errorf("Envoy filter: missing patch")) // nolint: stylecheck - continue - } - if cp.Patch.Operation == networking.EnvoyFilter_Patch_INVALID { - errs = appendValidation(errs, fmt.Errorf("Envoy filter: missing patch operation")) // nolint: stylecheck - continue - } - if cp.Patch.Operation != networking.EnvoyFilter_Patch_REMOVE && cp.Patch.Value == nil { - errs = appendValidation(errs, fmt.Errorf("Envoy filter: missing patch value for non-remove operation")) // nolint: stylecheck - continue - } - - // ensure that the supplied regex for proxy version compiles - if cp.Match != nil && cp.Match.Proxy != nil && cp.Match.Proxy.ProxyVersion != "" { - if _, err := regexp.Compile(cp.Match.Proxy.ProxyVersion); err != nil { - errs = appendValidation(errs, fmt.Errorf("Envoy filter: invalid regex for proxy version, [%v]", err)) // nolint: stylecheck - continue - } - } - // ensure that applyTo, match and patch all line up - switch cp.ApplyTo { - case networking.EnvoyFilter_LISTENER, - networking.EnvoyFilter_FILTER_CHAIN, - networking.EnvoyFilter_NETWORK_FILTER, - networking.EnvoyFilter_HTTP_FILTER: - if cp.Match != nil && cp.Match.ObjectTypes != nil { - if cp.Match.GetListener() == nil { - errs = appendValidation(errs, fmt.Errorf("Envoy filter: applyTo for listener class objects cannot have non listener match")) // nolint: stylecheck - continue - } - listenerMatch := cp.Match.GetListener() - if listenerMatch.FilterChain != nil { - if listenerMatch.FilterChain.Filter != nil { - if cp.ApplyTo == networking.EnvoyFilter_LISTENER || cp.ApplyTo == networking.EnvoyFilter_FILTER_CHAIN { - // This would be an error but is a warning for backwards compatibility - errs = appendValidation(errs, WrapWarning( - fmt.Errorf("Envoy filter: filter match has no effect when used with %v", cp.ApplyTo))) // nolint: stylecheck - } - // filter names are required if network filter matches are being made - if listenerMatch.FilterChain.Filter.Name == "" { - errs = appendValidation(errs, fmt.Errorf("Envoy filter: filter match has no name to match on")) // nolint: stylecheck - continue - } else if listenerMatch.FilterChain.Filter.SubFilter != nil { - // sub filter match is supported only for applyTo HTTP_FILTER - if cp.ApplyTo != networking.EnvoyFilter_HTTP_FILTER { - errs = appendValidation(errs, fmt.Errorf("Envoy filter: subfilter match can be used with applyTo HTTP_FILTER only")) // nolint: stylecheck - continue - } - // sub filter match requires the network filter to match to envoy http connection manager - if listenerMatch.FilterChain.Filter.Name != wellknown.HTTPConnectionManager && - listenerMatch.FilterChain.Filter.Name != "envoy.http_connection_manager" { - errs = appendValidation(errs, fmt.Errorf("Envoy filter: subfilter match requires filter match with %s", // nolint: stylecheck - wellknown.HTTPConnectionManager)) - continue - } - if listenerMatch.FilterChain.Filter.SubFilter.Name == "" { - errs = appendValidation(errs, fmt.Errorf("Envoy filter: subfilter match has no name to match on")) // nolint: stylecheck - continue - } - } - - errs = appendValidation(errs, validateListenerMatchName(listenerMatch.FilterChain.Filter.GetName())) - errs = appendValidation(errs, validateListenerMatchName(listenerMatch.FilterChain.Filter.GetSubFilter().GetName())) - } - } - } - case networking.EnvoyFilter_ROUTE_CONFIGURATION, networking.EnvoyFilter_VIRTUAL_HOST, networking.EnvoyFilter_HTTP_ROUTE: - if cp.Match != nil && cp.Match.ObjectTypes != nil { - if cp.Match.GetRouteConfiguration() == nil { - errs = appendValidation(errs, - fmt.Errorf("Envoy filter: applyTo for http route class objects cannot have non route configuration match")) // nolint: stylecheck - } - } - - case networking.EnvoyFilter_CLUSTER: - if cp.Match != nil && cp.Match.ObjectTypes != nil { - if cp.Match.GetCluster() == nil { - errs = appendValidation(errs, fmt.Errorf("Envoy filter: applyTo for cluster class objects cannot have non cluster match")) // nolint: stylecheck - } - } - } - // ensure that the struct is valid - if _, err := xds.BuildXDSObjectFromStruct(cp.ApplyTo, cp.Patch.Value, false); err != nil { - errs = appendValidation(errs, err) - } else { - // Run with strict validation, and emit warnings. This helps capture cases like unknown fields - // We do not want to reject in case the proto is valid but our libraries are outdated - obj, err := xds.BuildXDSObjectFromStruct(cp.ApplyTo, cp.Patch.Value, true) - if err != nil { - errs = appendValidation(errs, WrapWarning(err)) - } - - // Append any deprecation notices - if obj != nil { - errs = appendValidation(errs, validateDeprecatedFilterTypes(obj)) - errs = appendValidation(errs, validateMissingTypedConfigFilterTypes(obj)) - } - } - } - - return errs.Unwrap() - }) - -func validateListenerMatchName(name string) error { - if newName, f := xds.ReverseDeprecatedFilterNames[name]; f { - return WrapWarning(fmt.Errorf("using deprecated filter name %q; use %q instead", name, newName)) - } - return nil -} - -func recurseDeprecatedTypes(message protoreflect.Message) ([]string, error) { - var topError error - var deprecatedTypes []string - if message == nil { - return nil, nil - } - message.Range(func(descriptor protoreflect.FieldDescriptor, value protoreflect.Value) bool { - m, isMessage := value.Interface().(protoreflect.Message) - if isMessage { - anyMessage, isAny := m.Interface().(*any.Any) - if isAny { - mt, err := protoregistry.GlobalTypes.FindMessageByURL(anyMessage.TypeUrl) - if err != nil { - topError = err - return false - } - var fileOpts proto.Message = mt.Descriptor().ParentFile().Options().(*descriptorpb.FileOptions) - if proto.HasExtension(fileOpts, udpaa.E_FileStatus) { - ext := proto.GetExtension(fileOpts, udpaa.E_FileStatus) - udpaext, ok := ext.(*udpaa.StatusAnnotation) - if !ok { - topError = fmt.Errorf("extension was of wrong type: %T", ext) - return false - } - if udpaext.PackageVersionStatus == udpaa.PackageVersionStatus_FROZEN { - deprecatedTypes = append(deprecatedTypes, anyMessage.TypeUrl) - } - } - } - newTypes, err := recurseDeprecatedTypes(m) - if err != nil { - topError = err - return false - } - deprecatedTypes = append(deprecatedTypes, newTypes...) - } - return true - }) - return deprecatedTypes, topError -} - -// recurseMissingTypedConfig checks that configured filters do not rely on `name` and elide `typed_config`. -// This is temporarily enabled in Envoy by the envoy.reloadable_features.no_extension_lookup_by_name flag, but in the future will be removed. -func recurseMissingTypedConfig(message protoreflect.Message) []string { - var deprecatedTypes []string - if message == nil { - return nil - } - // First, iterate over the fields to find the 'name' field to help with reporting errors. - var name string - for i := 0; i < message.Type().Descriptor().Fields().Len(); i++ { - field := message.Type().Descriptor().Fields().Get(i) - if field.JSONName() == "name" { - name = fmt.Sprintf("%v", message.Get(field).Interface()) - } - } - - // Now go through fields again - for i := 0; i < message.Type().Descriptor().Fields().Len(); i++ { - field := message.Type().Descriptor().Fields().Get(i) - set := message.Has(field) - // If it has a typedConfig field, it must be set. - // Note: it is possible there is some API that has typedConfig but has a non-deprecated alternative, - // but I couldn't find any. Worst case, this is a warning, not an error, so a false positive is not so bad. - if field.JSONName() == "typedConfig" && !set { - deprecatedTypes = append(deprecatedTypes, name) - } - if set { - // If the field was set and is a message, recurse into it to check children - m, isMessage := message.Get(field).Interface().(protoreflect.Message) - if isMessage { - deprecatedTypes = append(deprecatedTypes, recurseMissingTypedConfig(m)...) - } - } - } - return deprecatedTypes -} - -func validateDeprecatedFilterTypes(obj proto.Message) error { - deprecated, err := recurseDeprecatedTypes(obj.ProtoReflect()) - if err != nil { - return fmt.Errorf("failed to find deprecated types: %v", err) - } - if len(deprecated) > 0 { - return WrapWarning(fmt.Errorf("using deprecated type_url(s); %v", strings.Join(deprecated, ", "))) - } - return nil -} - -func validateMissingTypedConfigFilterTypes(obj proto.Message) error { - missing := recurseMissingTypedConfig(obj.ProtoReflect()) - if len(missing) > 0 { - return WrapWarning(fmt.Errorf("using deprecated types by name without typed_config; %v", strings.Join(missing, ", "))) - } - return nil -} - -// validates that hostname in ns/ is a valid hostname according to -// API specs -func validateSidecarOrGatewayHostnamePart(hostname string, isGateway bool) (errs error) { - // short name hosts are not allowed - if hostname != "*" && !strings.Contains(hostname, ".") { - errs = appendErrors(errs, fmt.Errorf("short names (non FQDN) are not allowed")) - } - - if err := ValidateWildcardDomain(hostname); err != nil { - if !isGateway { - errs = appendErrors(errs, err) - } - - // Gateway allows IP as the host string, as well - ipAddr := net.ParseIP(hostname) - if ipAddr == nil { - errs = appendErrors(errs, err) - } - } - return -} - -func validateNamespaceSlashWildcardHostname(hostname string, isGateway bool) (errs error) { - parts := strings.SplitN(hostname, "/", 2) - if len(parts) != 2 { - if isGateway { - // Old style host in the gateway - return validateSidecarOrGatewayHostnamePart(hostname, true) - } - errs = appendErrors(errs, fmt.Errorf("host must be of form namespace/dnsName")) - return - } - - if len(parts[0]) == 0 || len(parts[1]) == 0 { - errs = appendErrors(errs, fmt.Errorf("config namespace and dnsName in host entry cannot be empty")) - } - - if !isGateway { - // namespace can be * or . or ~ or a valid DNS label in sidecars - if parts[0] != "*" && parts[0] != "." && parts[0] != "~" { - if !labels.IsDNS1123Label(parts[0]) { - errs = appendErrors(errs, fmt.Errorf("invalid namespace value %q in sidecar", parts[0])) - } - } - } else { - // namespace can be * or . or a valid DNS label in gateways - if parts[0] != "*" && parts[0] != "." { - if !labels.IsDNS1123Label(parts[0]) { - errs = appendErrors(errs, fmt.Errorf("invalid namespace value %q in gateway", parts[0])) - } - } - } - errs = appendErrors(errs, validateSidecarOrGatewayHostnamePart(parts[1], isGateway)) - return -} - -// ValidateSidecar checks sidecar config supplied by user -var ValidateSidecar = registerValidateFunc("ValidateSidecar", - func(cfg config.Config) (Warning, error) { - errs := Validation{} - rule, ok := cfg.Spec.(*networking.Sidecar) - if !ok { - return nil, fmt.Errorf("cannot cast to Sidecar") - } - - if err := validateAlphaWorkloadSelector(rule.WorkloadSelector); err != nil { - return nil, err - } - - if len(rule.Egress) == 0 && len(rule.Ingress) == 0 && rule.OutboundTrafficPolicy == nil { - return nil, fmt.Errorf("sidecar: empty configuration provided") - } - - portMap := make(map[uint32]struct{}) - for _, i := range rule.Ingress { - if i == nil { - errs = appendValidation(errs, fmt.Errorf("sidecar: ingress may not be null")) - continue - } - if i.Port == nil { - errs = appendValidation(errs, fmt.Errorf("sidecar: port is required for ingress listeners")) - continue - } - - bind := i.GetBind() - errs = appendValidation(errs, validateSidecarIngressPortAndBind(i.Port, bind)) - - if _, found := portMap[i.Port.Number]; found { - errs = appendValidation(errs, fmt.Errorf("sidecar: ports on IP bound listeners must be unique")) - } - portMap[i.Port.Number] = struct{}{} - - if len(i.DefaultEndpoint) != 0 { - if strings.HasPrefix(i.DefaultEndpoint, UnixAddressPrefix) { - errs = appendValidation(errs, ValidateUnixAddress(strings.TrimPrefix(i.DefaultEndpoint, UnixAddressPrefix))) - } else { - // format should be 127.0.0.1:port or :port - parts := strings.Split(i.DefaultEndpoint, ":") - if len(parts) < 2 { - errs = appendValidation(errs, fmt.Errorf("sidecar: defaultEndpoint must be of form 127.0.0.1:, 0.0.0.0:, unix://filepath, or unset")) - } else { - if len(parts[0]) > 0 && parts[0] != "127.0.0.1" && parts[0] != "0.0.0.0" { - errs = appendValidation(errs, fmt.Errorf("sidecar: defaultEndpoint must be of form 127.0.0.1:, 0.0.0.0:, unix://filepath, or unset")) - } - - port, err := strconv.Atoi(parts[1]) - if err != nil { - errs = appendValidation(errs, fmt.Errorf("sidecar: defaultEndpoint port (%s) is not a number: %v", parts[1], err)) - } else { - errs = appendValidation(errs, ValidatePort(port)) - } - } - } - } - - if i.Tls != nil { - if len(i.Tls.SubjectAltNames) > 0 { - errs = appendValidation(errs, fmt.Errorf("sidecar: subjectAltNames is not supported in ingress tls")) - } - if i.Tls.HttpsRedirect { - errs = appendValidation(errs, fmt.Errorf("sidecar: httpsRedirect is not supported")) - } - if i.Tls.CredentialName != "" { - errs = appendValidation(errs, fmt.Errorf("sidecar: credentialName is not currently supported")) - } - if i.Tls.Mode == networking.ServerTLSSettings_ISTIO_MUTUAL || i.Tls.Mode == networking.ServerTLSSettings_AUTO_PASSTHROUGH { - errs = appendValidation(errs, fmt.Errorf("configuration is invalid: cannot set mode to %s in sidecar ingress tls", i.Tls.Mode.String())) - } - protocol := protocol.Parse(i.Port.Protocol) - if !protocol.IsTLS() { - errs = appendValidation(errs, fmt.Errorf("server cannot have TLS settings for non HTTPS/TLS ports")) - } - errs = appendValidation(errs, validateTLSOptions(i.Tls)) - } - } - - portMap = make(map[uint32]struct{}) - udsMap := make(map[string]struct{}) - catchAllEgressListenerFound := false - for index, egress := range rule.Egress { - if egress == nil { - errs = appendValidation(errs, errors.New("egress listener may not be null")) - continue - } - // there can be only one catch all egress listener with empty port, and it should be the last listener. - if egress.Port == nil { - if !catchAllEgressListenerFound { - if index == len(rule.Egress)-1 { - catchAllEgressListenerFound = true - } else { - errs = appendValidation(errs, fmt.Errorf("sidecar: the egress listener with empty port should be the last listener in the list")) - } - } else { - errs = appendValidation(errs, fmt.Errorf("sidecar: egress can have only one listener with empty port")) - continue - } - } else { - bind := egress.GetBind() - captureMode := egress.GetCaptureMode() - errs = appendValidation(errs, validateSidecarEgressPortBindAndCaptureMode(egress.Port, bind, captureMode)) - - if egress.Port.Number == 0 { - if _, found := udsMap[bind]; found { - errs = appendValidation(errs, fmt.Errorf("sidecar: unix domain socket values for listeners must be unique")) - } - udsMap[bind] = struct{}{} - } else { - if _, found := portMap[egress.Port.Number]; found { - errs = appendValidation(errs, fmt.Errorf("sidecar: ports on IP bound listeners must be unique")) - } - portMap[egress.Port.Number] = struct{}{} - } - } - - // validate that the hosts field is a slash separated value - // of form ns1/host, or */host, or */*, or ns1/*, or ns1/*.example.com - if len(egress.Hosts) == 0 { - errs = appendValidation(errs, fmt.Errorf("sidecar: egress listener must contain at least one host")) - } else { - nssSvcs := map[string]map[string]bool{} - for _, hostname := range egress.Hosts { - parts := strings.SplitN(hostname, "/", 2) - if len(parts) == 2 { - ns := parts[0] - svc := parts[1] - if ns == "." { - ns = cfg.Namespace - } - if _, ok := nssSvcs[ns]; !ok { - nssSvcs[ns] = map[string]bool{} - } - - // test/a - // test/a - // test/* - if svc != "*" { - if _, ok := nssSvcs[ns][svc]; ok || nssSvcs[ns]["*"] { - // already exists - // TODO: prevent this invalid setting, maybe in 1.12+ - errs = appendValidation(errs, WrapWarning(fmt.Errorf("duplicated egress host: %s", hostname))) - } - } else { - if len(nssSvcs[ns]) != 0 { - errs = appendValidation(errs, WrapWarning(fmt.Errorf("duplicated egress host: %s", hostname))) - } - } - nssSvcs[ns][svc] = true - } - errs = appendValidation(errs, validateNamespaceSlashWildcardHostname(hostname, false)) - } - // */* - // test/a - if nssSvcs["*"]["*"] && len(nssSvcs) != 1 { - errs = appendValidation(errs, WrapWarning(fmt.Errorf("`*/*` host select all resources, no other hosts can be added"))) - } - } - } - - errs = appendValidation(errs, validateSidecarOutboundTrafficPolicy(rule.OutboundTrafficPolicy)) - - return errs.Unwrap() - }) - -func validateSidecarOutboundTrafficPolicy(tp *networking.OutboundTrafficPolicy) (errs error) { - if tp == nil { - return - } - mode := tp.GetMode() - if tp.EgressProxy != nil { - if mode != networking.OutboundTrafficPolicy_ALLOW_ANY { - errs = appendErrors(errs, fmt.Errorf("sidecar: egress_proxy must be set only with ALLOW_ANY outbound_traffic_policy mode")) - return - } - - errs = appendErrors(errs, ValidateFQDN(tp.EgressProxy.GetHost())) - - if tp.EgressProxy.Port == nil { - errs = appendErrors(errs, fmt.Errorf("sidecar: egress_proxy port must be non-nil")) - return - } - errs = appendErrors(errs, validateDestination(tp.EgressProxy)) - } - return -} - -func validateSidecarEgressPortBindAndCaptureMode(port *networking.Port, bind string, - captureMode networking.CaptureMode) (errs error) { - // Port name is optional. Validate if exists. - if len(port.Name) > 0 { - errs = appendErrors(errs, ValidatePortName(port.Name)) - } - - // Handle Unix domain sockets - if port.Number == 0 { - // require bind to be a unix domain socket - errs = appendErrors(errs, - ValidateProtocol(port.Protocol)) - - if !strings.HasPrefix(bind, UnixAddressPrefix) { - errs = appendErrors(errs, fmt.Errorf("sidecar: ports with 0 value must have a unix domain socket bind address")) - } else { - errs = appendErrors(errs, ValidateUnixAddress(strings.TrimPrefix(bind, UnixAddressPrefix))) - } - - if captureMode != networking.CaptureMode_DEFAULT && captureMode != networking.CaptureMode_NONE { - errs = appendErrors(errs, fmt.Errorf("sidecar: captureMode must be DEFAULT/NONE for unix domain socket listeners")) - } - } else { - errs = appendErrors(errs, - ValidateProtocol(port.Protocol), - ValidatePort(int(port.Number))) - - if len(bind) != 0 { - errs = appendErrors(errs, ValidateIPAddress(bind)) - } - } - - return -} - -func validateSidecarIngressPortAndBind(port *networking.Port, bind string) (errs error) { - // Port name is optional. Validate if exists. - if len(port.Name) > 0 { - errs = appendErrors(errs, ValidatePortName(port.Name)) - } - - errs = appendErrors(errs, - ValidateProtocol(port.Protocol), - ValidatePort(int(port.Number))) - - if len(bind) != 0 { - errs = appendErrors(errs, ValidateIPAddress(bind)) - } - - return -} - -func validateTrafficPolicy(policy *networking.TrafficPolicy) Validation { - if policy == nil { - return Validation{} - } - if policy.OutlierDetection == nil && policy.ConnectionPool == nil && - policy.LoadBalancer == nil && policy.Tls == nil && policy.PortLevelSettings == nil { - return WrapError(fmt.Errorf("traffic policy must have at least one field")) - } - - return appendValidation(validateOutlierDetection(policy.OutlierDetection), - validateConnectionPool(policy.ConnectionPool), - validateLoadBalancer(policy.LoadBalancer), - validateTLS(policy.Tls), - validatePortTrafficPolicies(policy.PortLevelSettings)) -} - -func validateOutlierDetection(outlier *networking.OutlierDetection) (errs Validation) { - if outlier == nil { - return - } - - if outlier.BaseEjectionTime != nil { - errs = appendValidation(errs, ValidateDuration(outlier.BaseEjectionTime)) - } - // nolint: staticcheck - if outlier.ConsecutiveErrors != 0 { - warn := "outlier detection consecutive errors is deprecated, use consecutiveGatewayErrors or consecutive5xxErrors instead" - scope.Warnf(warn) - errs = appendValidation(errs, WrapWarning(errors.New(warn))) - } - if !outlier.SplitExternalLocalOriginErrors && outlier.ConsecutiveLocalOriginFailures.GetValue() > 0 { - err := "outlier detection consecutive local origin failures is specified, but split external local origin errors is set to false" - errs = appendValidation(errs, errors.New(err)) - } - if outlier.Interval != nil { - errs = appendValidation(errs, ValidateDuration(outlier.Interval)) - } - errs = appendValidation(errs, ValidatePercent(outlier.MaxEjectionPercent), ValidatePercent(outlier.MinHealthPercent)) - - return -} - -func validateConnectionPool(settings *networking.ConnectionPoolSettings) (errs error) { - if settings == nil { - return - } - if settings.Http == nil && settings.Tcp == nil { - return fmt.Errorf("connection pool must have at least one field") - } - - if httpSettings := settings.Http; httpSettings != nil { - if httpSettings.Http1MaxPendingRequests < 0 { - errs = appendErrors(errs, fmt.Errorf("http1 max pending requests must be non-negative")) - } - if httpSettings.Http2MaxRequests < 0 { - errs = appendErrors(errs, fmt.Errorf("http2 max requests must be non-negative")) - } - if httpSettings.MaxRequestsPerConnection < 0 { - errs = appendErrors(errs, fmt.Errorf("max requests per connection must be non-negative")) - } - if httpSettings.MaxRetries < 0 { - errs = appendErrors(errs, fmt.Errorf("max retries must be non-negative")) - } - if httpSettings.IdleTimeout != nil { - errs = appendErrors(errs, ValidateDuration(httpSettings.IdleTimeout)) - } - if httpSettings.H2UpgradePolicy == networking.ConnectionPoolSettings_HTTPSettings_UPGRADE && httpSettings.UseClientProtocol { - errs = appendErrors(errs, fmt.Errorf("use client protocol must not be true when H2UpgradePolicy is UPGRADE")) - } - } - - if tcp := settings.Tcp; tcp != nil { - if tcp.MaxConnections < 0 { - errs = appendErrors(errs, fmt.Errorf("max connections must be non-negative")) - } - if tcp.ConnectTimeout != nil { - errs = appendErrors(errs, ValidateDuration(tcp.ConnectTimeout)) - } - } - - return -} - -func validateLoadBalancer(settings *networking.LoadBalancerSettings) (errs error) { - if settings == nil { - return - } - - // simple load balancing is always valid - - consistentHash := settings.GetConsistentHash() - if consistentHash != nil { - httpCookie := consistentHash.GetHttpCookie() - if httpCookie != nil { - if httpCookie.Name == "" { - errs = appendErrors(errs, fmt.Errorf("name required for HttpCookie")) - } - if httpCookie.Ttl == nil { - errs = appendErrors(errs, fmt.Errorf("ttl required for HttpCookie")) - } - } - } - if err := validateLocalityLbSetting(settings.LocalityLbSetting); err != nil { - errs = multierror.Append(errs, err) - } - return -} - -func validateTLS(settings *networking.ClientTLSSettings) (errs error) { - if settings == nil { - return - } - - if (settings.Mode == networking.ClientTLSSettings_SIMPLE || settings.Mode == networking.ClientTLSSettings_MUTUAL) && - settings.CredentialName != "" { - if settings.ClientCertificate != "" || settings.CaCertificates != "" || settings.PrivateKey != "" { - errs = appendErrors(errs, - fmt.Errorf("cannot specify client certificates or CA certificate If credentialName is set")) - } - - // If tls mode is SIMPLE or MUTUAL, and CredentialName is specified, credentials are fetched - // remotely. ServerCertificate and CaCertificates fields are not required. - return - } - - if settings.Mode == networking.ClientTLSSettings_MUTUAL { - if settings.ClientCertificate == "" { - errs = appendErrors(errs, fmt.Errorf("client certificate required for mutual tls")) - } - if settings.PrivateKey == "" { - errs = appendErrors(errs, fmt.Errorf("private key required for mutual tls")) - } - } - - return -} - -func validateSubset(subset *networking.Subset) error { - return appendErrors(validateSubsetName(subset.Name), - labels.Instance(subset.Labels).Validate(), - validateTrafficPolicy(subset.TrafficPolicy)) -} - -func validatePortTrafficPolicies(pls []*networking.TrafficPolicy_PortTrafficPolicy) (errs error) { - for _, t := range pls { - if t == nil { - errs = appendErrors(errs, fmt.Errorf("traffic policy may not be null")) - continue - } - if t.Port == nil { - errs = appendErrors(errs, fmt.Errorf("portTrafficPolicy must have valid port")) - } - if t.OutlierDetection == nil && t.ConnectionPool == nil && - t.LoadBalancer == nil && t.Tls == nil { - errs = appendErrors(errs, fmt.Errorf("port traffic policy must have at least one field")) - } else { - errs = appendErrors(errs, validateOutlierDetection(t.OutlierDetection), - validateConnectionPool(t.ConnectionPool), - validateLoadBalancer(t.LoadBalancer), - validateTLS(t.Tls)) - } - } - return -} - -// ValidateProxyAddress checks that a network address is well-formed -func ValidateProxyAddress(hostAddr string) error { - hostname, p, err := net.SplitHostPort(hostAddr) - if err != nil { - return fmt.Errorf("unable to split %q: %v", hostAddr, err) - } - port, err := strconv.Atoi(p) - if err != nil { - return fmt.Errorf("port (%s) is not a number: %v", p, err) - } - if err = ValidatePort(port); err != nil { - return err - } - if err = ValidateFQDN(hostname); err != nil { - ip := net.ParseIP(hostname) - if ip == nil { - return fmt.Errorf("%q is not a valid hostname or an IP address", hostname) - } - } - - return nil -} - -// ValidateDuration checks that a proto duration is well-formed -func ValidateDuration(pd *durationpb.Duration) error { - dur := pd.AsDuration() - if dur < time.Millisecond { - return errors.New("duration must be greater than 1ms") - } - if dur%time.Millisecond != 0 { - return errors.New("only durations to ms precision are supported") - } - return nil -} - -// ValidateDurationRange verifies range is in specified duration -func ValidateDurationRange(dur, min, max time.Duration) error { - if dur > max || dur < min { - return fmt.Errorf("time %v must be >%v and <%v", dur.String(), min.String(), max.String()) - } - - return nil -} - -// ValidateParentAndDrain checks that parent and drain durations are valid -func ValidateParentAndDrain(drainTime, parentShutdown *durationpb.Duration) (errs error) { - if err := ValidateDuration(drainTime); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, "invalid drain duration:")) - } - if err := ValidateDuration(parentShutdown); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, "invalid parent shutdown duration:")) - } - if errs != nil { - return - } - - drainDuration := drainTime.AsDuration() - parentShutdownDuration := parentShutdown.AsDuration() - - if drainDuration%time.Second != 0 { - errs = multierror.Append(errs, - errors.New("drain time only supports durations to seconds precision")) - } - if parentShutdownDuration%time.Second != 0 { - errs = multierror.Append(errs, - errors.New("parent shutdown time only supports durations to seconds precision")) - } - if parentShutdownDuration <= drainDuration { - errs = multierror.Append(errs, - fmt.Errorf("parent shutdown time %v must be greater than drain time %v", - parentShutdownDuration.String(), drainDuration.String())) - } - - if drainDuration > drainTimeMax { - errs = multierror.Append(errs, - fmt.Errorf("drain time %v must be <%v", drainDuration.String(), drainTimeMax.String())) - } - - if parentShutdownDuration > parentShutdownTimeMax { - errs = multierror.Append(errs, - fmt.Errorf("parent shutdown time %v must be <%v", - parentShutdownDuration.String(), parentShutdownTimeMax.String())) - } - - return -} - -// ValidateLightstepCollector validates the configuration for sending envoy spans to LightStep -func ValidateLightstepCollector(ls *meshconfig.Tracing_Lightstep) error { - var errs error - if ls.GetAddress() == "" { - errs = multierror.Append(errs, errors.New("address is required")) - } - if err := ValidateProxyAddress(ls.GetAddress()); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, "invalid lightstep address:")) - } - if ls.GetAccessToken() == "" { - errs = multierror.Append(errs, errors.New("access token is required")) - } - return errs -} - -// validateCustomTags validates that tracing CustomTags map does not contain any nil items -func validateCustomTags(tags map[string]*meshconfig.Tracing_CustomTag) error { - for tagName, tagVal := range tags { - if tagVal == nil { - return fmt.Errorf("encountered nil value for custom tag: %s", tagName) - } - } - return nil -} - -// ValidateZipkinCollector validates the configuration for sending envoy spans to Zipkin -func ValidateZipkinCollector(z *meshconfig.Tracing_Zipkin) error { - return ValidateProxyAddress(strings.Replace(z.GetAddress(), "$(HOST_IP)", "127.0.0.1", 1)) -} - -// ValidateDatadogCollector validates the configuration for sending envoy spans to Datadog -func ValidateDatadogCollector(d *meshconfig.Tracing_Datadog) error { - // If the address contains $(HOST_IP), replace it with a valid IP before validation. - return ValidateProxyAddress(strings.Replace(d.GetAddress(), "$(HOST_IP)", "127.0.0.1", 1)) -} - -// ValidateConnectTimeout validates the envoy connection timeout -func ValidateConnectTimeout(timeout *durationpb.Duration) error { - if err := ValidateDuration(timeout); err != nil { - return err - } - - err := ValidateDurationRange(timeout.AsDuration(), connectTimeoutMin, connectTimeoutMax) - return err -} - -// ValidateProtocolDetectionTimeout validates the envoy protocol detection timeout -func ValidateProtocolDetectionTimeout(timeout *durationpb.Duration) error { - dur := timeout.AsDuration() - // 0s is a valid value if trying to disable protocol detection timeout - if dur == time.Second*0 { - return nil - } - if dur%time.Millisecond != 0 { - return errors.New("only durations to ms precision are supported") - } - - return nil -} - -// ValidateMaxServerConnectionAge validate negative duration -func ValidateMaxServerConnectionAge(in time.Duration) error { - if err := IsNegativeDuration(in); err != nil { - return fmt.Errorf("%v: --keepaliveMaxServerConnectionAge only accepts positive duration eg: 30m", err) - } - return nil -} - -// IsNegativeDuration check if the duration is negative -func IsNegativeDuration(in time.Duration) error { - if in < 0 { - return fmt.Errorf("invalid duration: %s", in.String()) - } - return nil -} - -// ValidateMeshConfig checks that the mesh config is well-formed -func ValidateMeshConfig(mesh *meshconfig.MeshConfig) (errs error) { - if err := ValidatePort(int(mesh.ProxyListenPort)); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, "invalid proxy listen port:")) - } - - if err := ValidateConnectTimeout(mesh.ConnectTimeout); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, "invalid connect timeout:")) - } - - if err := ValidateProtocolDetectionTimeout(mesh.ProtocolDetectionTimeout); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, "invalid protocol detection timeout:")) - } - - if mesh.DefaultConfig == nil { - errs = multierror.Append(errs, errors.New("missing default config")) - } else if err := ValidateMeshConfigProxyConfig(mesh.DefaultConfig); err != nil { - errs = multierror.Append(errs, err) - } - - if err := validateLocalityLbSetting(mesh.LocalityLbSetting); err != nil { - errs = multierror.Append(errs, err) - } - - if err := validateServiceSettings(mesh); err != nil { - errs = multierror.Append(errs, err) - } - - if err := validateTrustDomainConfig(mesh); err != nil { - errs = multierror.Append(errs, err) - } - - if err := validateExtensionProvider(mesh); err != nil { - scope.Warnf("found invalid extension provider (can be ignored if the given extension provider is not used): %v", err) - } - - return -} - -func validateTrustDomainConfig(config *meshconfig.MeshConfig) (errs error) { - if err := ValidateTrustDomain(config.TrustDomain); err != nil { - errs = multierror.Append(errs, fmt.Errorf("trustDomain: %v", err)) - } - for i, tda := range config.TrustDomainAliases { - if err := ValidateTrustDomain(tda); err != nil { - errs = multierror.Append(errs, fmt.Errorf("trustDomainAliases[%d], domain `%s` : %v", i, tda, err)) - } - } - return -} - -func validateServiceSettings(config *meshconfig.MeshConfig) (errs error) { - for sIndex, s := range config.ServiceSettings { - for _, h := range s.Hosts { - if err := ValidateWildcardDomain(h); err != nil { - errs = multierror.Append(errs, fmt.Errorf("serviceSettings[%d], host `%s`: %v", sIndex, h, err)) - } - } - } - return -} - -func validatePrivateKeyProvider(pkpConf *meshconfig.PrivateKeyProvider) error { - var errs error - if pkpConf.GetProvider() == nil { - errs = multierror.Append(errs, errors.New("private key provider confguration is required")) - } - - switch pkpConf.GetProvider().(type) { - case *meshconfig.PrivateKeyProvider_Cryptomb: - cryptomb := pkpConf.GetCryptomb() - if cryptomb == nil { - errs = multierror.Append(errs, errors.New("cryptomb confguration is required")) - } else { - pollDelay := cryptomb.GetPollDelay() - if pollDelay == nil { - errs = multierror.Append(errs, errors.New("pollDelay is required")) - } else if pollDelay.GetSeconds() == 0 && pollDelay.GetNanos() == 0 { - errs = multierror.Append(errs, errors.New("pollDelay must be non zero")) - } - } - default: - errs = multierror.Append(errs, errors.New("unknown private key provider")) - } - - return errs -} - -// ValidateMeshConfigProxyConfig checks that the mesh config is well-formed -func ValidateMeshConfigProxyConfig(config *meshconfig.ProxyConfig) (errs error) { - if config.ConfigPath == "" { - errs = multierror.Append(errs, errors.New("config path must be set")) - } - - if config.BinaryPath == "" { - errs = multierror.Append(errs, errors.New("binary path must be set")) - } - - clusterName := config.GetClusterName() - switch naming := clusterName.(type) { - case *meshconfig.ProxyConfig_ServiceCluster: - if naming.ServiceCluster == "" { - errs = multierror.Append(errs, errors.New("service cluster must be specified")) - } - case *meshconfig.ProxyConfig_TracingServiceName_: // intentionally left empty for now - default: - errs = multierror.Append(errs, errors.New("oneof service cluster or tracing service name must be specified")) - } - - if err := ValidateParentAndDrain(config.DrainDuration, config.ParentShutdownDuration); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, "invalid parent and drain time combination")) - } - - // discovery address is mandatory since mutual TLS relies on CDS. - // strictly speaking, proxies can operate without RDS/CDS and with hot restarts - // but that requires additional test validation - if config.DiscoveryAddress == "" { - errs = multierror.Append(errs, errors.New("discovery address must be set to the proxy discovery service")) - } else if err := ValidateProxyAddress(config.DiscoveryAddress); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, "invalid discovery address:")) - } - - if tracer := config.GetTracing().GetLightstep(); tracer != nil { - if err := ValidateLightstepCollector(tracer); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, "invalid lightstep config:")) - } - } - - if tracer := config.GetTracing().GetZipkin(); tracer != nil { - if err := ValidateZipkinCollector(tracer); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, "invalid zipkin config:")) - } - } - - if tracer := config.GetTracing().GetDatadog(); tracer != nil { - if err := ValidateDatadogCollector(tracer); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, "invalid datadog config:")) - } - } - - if tracer := config.GetTracing().GetTlsSettings(); tracer != nil { - if err := validateTLS(tracer); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, "invalid tracing TLS config:")) - } - } - - if tracerCustomTags := config.GetTracing().GetCustomTags(); tracerCustomTags != nil { - if err := validateCustomTags(tracerCustomTags); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, "invalid tracing custom tags:")) - } - } - - if config.StatsdUdpAddress != "" { - if err := ValidateProxyAddress(config.StatsdUdpAddress); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, fmt.Sprintf("invalid statsd udp address %q:", config.StatsdUdpAddress))) - } - } - - // nolint: staticcheck - if config.EnvoyMetricsServiceAddress != "" { - if err := ValidateProxyAddress(config.EnvoyMetricsServiceAddress); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, fmt.Sprintf("invalid envoy metrics service address %q:", config.EnvoyMetricsServiceAddress))) - } else { - scope.Warnf("EnvoyMetricsServiceAddress is deprecated, use EnvoyMetricsService instead.") // nolint: stylecheck - } - } - - if config.EnvoyMetricsService != nil && config.EnvoyMetricsService.Address != "" { - if err := ValidateProxyAddress(config.EnvoyMetricsService.Address); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, fmt.Sprintf("invalid envoy metrics service address %q:", config.EnvoyMetricsService.Address))) - } - } - - if config.EnvoyAccessLogService != nil && config.EnvoyAccessLogService.Address != "" { - if err := ValidateProxyAddress(config.EnvoyAccessLogService.Address); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, fmt.Sprintf("invalid envoy access log service address %q:", config.EnvoyAccessLogService.Address))) - } - } - - if err := ValidatePort(int(config.ProxyAdminPort)); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, "invalid proxy admin port:")) - } - - if err := ValidateControlPlaneAuthPolicy(config.ControlPlaneAuthPolicy); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, "invalid authentication policy:")) - } - - if err := ValidatePort(int(config.StatusPort)); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, "invalid status port:")) - } - - if pkpConf := config.GetPrivateKeyProvider(); pkpConf != nil { - if err := validatePrivateKeyProvider(pkpConf); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, "invalid private key provider confguration:")) - } - } - - return -} - -func ValidateControlPlaneAuthPolicy(policy meshconfig.AuthenticationPolicy) error { - if policy == meshconfig.AuthenticationPolicy_NONE || policy == meshconfig.AuthenticationPolicy_MUTUAL_TLS { - return nil - } - return fmt.Errorf("unrecognized control plane auth policy %q", policy) -} - -func validateWorkloadSelector(selector *type_beta.WorkloadSelector) error { - var errs error - if selector != nil { - for k, v := range selector.MatchLabels { - if k == "" { - errs = appendErrors(errs, - fmt.Errorf("empty key is not supported in selector: %q", fmt.Sprintf("%s=%s", k, v))) - } - if strings.Contains(k, "*") || strings.Contains(v, "*") { - errs = appendErrors(errs, - fmt.Errorf("wildcard is not supported in selector: %q", fmt.Sprintf("%s=%s", k, v))) - } - } - } - - return errs -} - -// ValidateAuthorizationPolicy checks that AuthorizationPolicy is well-formed. -var ValidateAuthorizationPolicy = registerValidateFunc("ValidateAuthorizationPolicy", - func(cfg config.Config) (Warning, error) { - in, ok := cfg.Spec.(*security_beta.AuthorizationPolicy) - if !ok { - return nil, fmt.Errorf("cannot cast to AuthorizationPolicy") - } - - var errs error - if err := validateWorkloadSelector(in.Selector); err != nil { - errs = appendErrors(errs, err) - } - - if in.Action == security_beta.AuthorizationPolicy_CUSTOM { - if in.Rules == nil { - errs = appendErrors(errs, fmt.Errorf("CUSTOM action without `rules` is meaningless as it will never be triggered, "+ - "add an empty rule `{}` if you want it be triggered for every request")) - } else { - if in.GetProvider() == nil || in.GetProvider().GetName() == "" { - errs = appendErrors(errs, fmt.Errorf("`provider.name` must not be empty")) - } - } - // TODO(yangminzhu): Add support for more matching rules. - for _, rule := range in.GetRules() { - check := func(invalid bool, name string) error { - if invalid { - return fmt.Errorf("%s is currently not supported with CUSTOM action", name) - } - return nil - } - for _, from := range rule.GetFrom() { - if src := from.GetSource(); src != nil { - errs = appendErrors(errs, check(len(src.Namespaces) != 0, "From.Namespaces")) - errs = appendErrors(errs, check(len(src.NotNamespaces) != 0, "From.NotNamespaces")) - errs = appendErrors(errs, check(len(src.Principals) != 0, "From.Principals")) - errs = appendErrors(errs, check(len(src.NotPrincipals) != 0, "From.NotPrincipals")) - errs = appendErrors(errs, check(len(src.RequestPrincipals) != 0, "From.RequestPrincipals")) - errs = appendErrors(errs, check(len(src.NotRequestPrincipals) != 0, "From.NotRequestPrincipals")) - } - } - for _, when := range rule.GetWhen() { - errs = appendErrors(errs, check(when.Key == "source.namespace", when.Key)) - errs = appendErrors(errs, check(when.Key == "source.principal", when.Key)) - errs = appendErrors(errs, check(strings.HasPrefix(when.Key, "request.auth."), when.Key)) - } - } - } - if in.GetProvider() != nil && in.Action != security_beta.AuthorizationPolicy_CUSTOM { - errs = appendErrors(errs, fmt.Errorf("`provider` must not be with non CUSTOM action, found %s", in.Action)) - } - - if in.Action == security_beta.AuthorizationPolicy_DENY && in.Rules == nil { - errs = appendErrors(errs, fmt.Errorf("DENY action without `rules` is meaningless as it will never be triggered, "+ - "add an empty rule `{}` if you want it be triggered for every request")) - } - - for i, rule := range in.GetRules() { - if rule == nil { - errs = appendErrors(errs, fmt.Errorf("`rule` must not be nil, found at rule %d", i)) - continue - } - if rule.From != nil && len(rule.From) == 0 { - errs = appendErrors(errs, fmt.Errorf("`from` must not be empty, found at rule %d", i)) - } - for _, from := range rule.From { - if from == nil { - errs = appendErrors(errs, fmt.Errorf("`from` must not be nil, found at rule %d", i)) - continue - } - if from.Source == nil { - errs = appendErrors(errs, fmt.Errorf("`from.source` must not be nil, found at rule %d", i)) - } else { - src := from.Source - if len(src.Principals) == 0 && len(src.RequestPrincipals) == 0 && len(src.Namespaces) == 0 && len(src.IpBlocks) == 0 && - len(src.RemoteIpBlocks) == 0 && len(src.NotPrincipals) == 0 && len(src.NotRequestPrincipals) == 0 && len(src.NotNamespaces) == 0 && - len(src.NotIpBlocks) == 0 && len(src.NotRemoteIpBlocks) == 0 { - errs = appendErrors(errs, fmt.Errorf("`from.source` must not be empty, found at rule %d", i)) - } - errs = appendErrors(errs, security.ValidateIPs(from.Source.GetIpBlocks())) - errs = appendErrors(errs, security.ValidateIPs(from.Source.GetNotIpBlocks())) - errs = appendErrors(errs, security.ValidateIPs(from.Source.GetRemoteIpBlocks())) - errs = appendErrors(errs, security.ValidateIPs(from.Source.GetNotRemoteIpBlocks())) - errs = appendErrors(errs, security.CheckEmptyValues("Principals", src.Principals)) - errs = appendErrors(errs, security.CheckEmptyValues("RequestPrincipals", src.RequestPrincipals)) - errs = appendErrors(errs, security.CheckEmptyValues("Namespaces", src.Namespaces)) - errs = appendErrors(errs, security.CheckEmptyValues("IpBlocks", src.IpBlocks)) - errs = appendErrors(errs, security.CheckEmptyValues("RemoteIpBlocks", src.RemoteIpBlocks)) - errs = appendErrors(errs, security.CheckEmptyValues("NotPrincipals", src.NotPrincipals)) - errs = appendErrors(errs, security.CheckEmptyValues("NotRequestPrincipals", src.NotRequestPrincipals)) - errs = appendErrors(errs, security.CheckEmptyValues("NotNamespaces", src.NotNamespaces)) - errs = appendErrors(errs, security.CheckEmptyValues("NotIpBlocks", src.NotIpBlocks)) - errs = appendErrors(errs, security.CheckEmptyValues("NotRemoteIpBlocks", src.NotRemoteIpBlocks)) - } - } - if rule.To != nil && len(rule.To) == 0 { - errs = appendErrors(errs, fmt.Errorf("`to` must not be empty, found at rule %d", i)) - } - for _, to := range rule.To { - if to == nil { - errs = appendErrors(errs, fmt.Errorf("`to` must not be nil, found at rule %d", i)) - continue - } - if to.Operation == nil { - errs = appendErrors(errs, fmt.Errorf("`to.operation` must not be nil, found at rule %d", i)) - } else { - op := to.Operation - if len(op.Ports) == 0 && len(op.Methods) == 0 && len(op.Paths) == 0 && len(op.Hosts) == 0 && - len(op.NotPorts) == 0 && len(op.NotMethods) == 0 && len(op.NotPaths) == 0 && len(op.NotHosts) == 0 { - errs = appendErrors(errs, fmt.Errorf("`to.operation` must not be empty, found at rule %d", i)) - } - errs = appendErrors(errs, security.ValidatePorts(to.Operation.GetPorts())) - errs = appendErrors(errs, security.ValidatePorts(to.Operation.GetNotPorts())) - errs = appendErrors(errs, security.CheckEmptyValues("Ports", op.Ports)) - errs = appendErrors(errs, security.CheckEmptyValues("Methods", op.Methods)) - errs = appendErrors(errs, security.CheckEmptyValues("Paths", op.Paths)) - errs = appendErrors(errs, security.CheckEmptyValues("Hosts", op.Hosts)) - errs = appendErrors(errs, security.CheckEmptyValues("NotPorts", op.NotPorts)) - errs = appendErrors(errs, security.CheckEmptyValues("NotMethods", op.NotMethods)) - errs = appendErrors(errs, security.CheckEmptyValues("NotPaths", op.NotPaths)) - errs = appendErrors(errs, security.CheckEmptyValues("NotHosts", op.NotHosts)) - } - } - for _, condition := range rule.GetWhen() { - key := condition.GetKey() - if key == "" { - errs = appendErrors(errs, fmt.Errorf("`key` must not be empty")) - } else { - if len(condition.GetValues()) == 0 && len(condition.GetNotValues()) == 0 { - errs = appendErrors(errs, fmt.Errorf("at least one of `values` or `notValues` must be set for key %s", - key)) - } else { - if err := security.ValidateAttribute(key, condition.GetValues()); err != nil { - errs = appendErrors(errs, fmt.Errorf("invalid `value` for `key` %s: %v", key, err)) - } - if err := security.ValidateAttribute(key, condition.GetNotValues()); err != nil { - errs = appendErrors(errs, fmt.Errorf("invalid `notValue` for `key` %s: %v", key, err)) - } - } - } - } - } - return nil, multierror.Prefix(errs, fmt.Sprintf("invalid policy %s.%s:", cfg.Name, cfg.Namespace)) - }) - -// ValidateRequestAuthentication checks that request authentication spec is well-formed. -var ValidateRequestAuthentication = registerValidateFunc("ValidateRequestAuthentication", - func(cfg config.Config) (Warning, error) { - in, ok := cfg.Spec.(*security_beta.RequestAuthentication) - if !ok { - return nil, errors.New("cannot cast to RequestAuthentication") - } - - var errs error - errs = appendErrors(errs, validateWorkloadSelector(in.Selector)) - - for _, rule := range in.JwtRules { - errs = appendErrors(errs, validateJwtRule(rule)) - } - return nil, errs - }) - -func validateJwtRule(rule *security_beta.JWTRule) (errs error) { - if rule == nil { - return nil - } - if len(rule.Issuer) == 0 { - errs = multierror.Append(errs, errors.New("issuer must be set")) - } - for _, audience := range rule.Audiences { - if len(audience) == 0 { - errs = multierror.Append(errs, errors.New("audience must be non-empty string")) - } - } - - if len(rule.JwksUri) != 0 { - if _, err := security.ParseJwksURI(rule.JwksUri); err != nil { - errs = multierror.Append(errs, err) - } - } - - if rule.Jwks != "" { - _, err := jwk.Parse([]byte(rule.Jwks)) - if err != nil { - errs = multierror.Append(errs, fmt.Errorf("jwks parse error: %v", err)) - } - } - - for _, location := range rule.FromHeaders { - if location == nil { - errs = multierror.Append(errs, errors.New("location header name must be non-null")) - continue - } - if len(location.Name) == 0 { - errs = multierror.Append(errs, errors.New("location header name must be non-empty string")) - } - } - - for _, location := range rule.FromParams { - if len(location) == 0 { - errs = multierror.Append(errs, errors.New("location query must be non-empty string")) - } - } - return -} - -// ValidatePeerAuthentication checks that peer authentication spec is well-formed. -var ValidatePeerAuthentication = registerValidateFunc("ValidatePeerAuthentication", - func(cfg config.Config) (Warning, error) { - in, ok := cfg.Spec.(*security_beta.PeerAuthentication) - if !ok { - return nil, errors.New("cannot cast to PeerAuthentication") - } - - var errs error - emptySelector := in.Selector == nil || len(in.Selector.MatchLabels) == 0 - - if emptySelector && len(in.PortLevelMtls) != 0 { - errs = appendErrors(errs, - fmt.Errorf("mesh/namespace peer authentication cannot have port level mTLS")) - } - - if in.PortLevelMtls != nil && len(in.PortLevelMtls) == 0 { - errs = appendErrors(errs, - fmt.Errorf("port level mTLS, if defined, must have at least one element")) - } - - for port := range in.PortLevelMtls { - if port == 0 { - errs = appendErrors(errs, fmt.Errorf("port cannot be 0")) - } - } - - errs = appendErrors(errs, validateWorkloadSelector(in.Selector)) - - return nil, errs - }) - -// ValidateVirtualService checks that a v1alpha3 route rule is well-formed. -var ValidateVirtualService = registerValidateFunc("ValidateVirtualService", - func(cfg config.Config) (Warning, error) { - virtualService, ok := cfg.Spec.(*networking.VirtualService) - if !ok { - return nil, errors.New("cannot cast to virtual service") - } - errs := Validation{} - if len(virtualService.Hosts) == 0 { - // This must be delegate - enforce delegate validations. - if len(virtualService.Gateways) != 0 { - // meaningless to specify gateways in delegate - errs = appendValidation(errs, fmt.Errorf("delegate virtual service must have no gateways specified")) - } - if len(virtualService.Tls) != 0 { - // meaningless to specify tls in delegate, we donot support tls delegate - errs = appendValidation(errs, fmt.Errorf("delegate virtual service must have no tls route specified")) - } - if len(virtualService.Tcp) != 0 { - // meaningless to specify tls in delegate, we donot support tcp delegate - errs = appendValidation(errs, fmt.Errorf("delegate virtual service must have no tcp route specified")) - } - } - - appliesToMesh := false - appliesToGateway := false - if len(virtualService.Gateways) == 0 { - appliesToMesh = true - } else { - errs = appendValidation(errs, validateGatewayNames(virtualService.Gateways)) - for _, gatewayName := range virtualService.Gateways { - if gatewayName == constants.IstioMeshGateway { - appliesToMesh = true - } else { - appliesToGateway = true - } - } - } - - if !appliesToGateway { - validateJWTClaimRoute := func(headers map[string]*networking.StringMatch) { - for key := range headers { - if strings.HasPrefix(key, constant.HeaderJWTClaim) { - msg := fmt.Sprintf("JWT claim based routing (key: %s) is only supported for gateway, found no gateways: %v", key, virtualService.Gateways) - errs = appendValidation(errs, errors.New(msg)) - } - } - } - for _, http := range virtualService.GetHttp() { - for _, m := range http.GetMatch() { - validateJWTClaimRoute(m.GetHeaders()) - validateJWTClaimRoute(m.GetWithoutHeaders()) - } - } - } - - allHostsValid := true - for _, virtualHost := range virtualService.Hosts { - if err := ValidateWildcardDomain(virtualHost); err != nil { - ipAddr := net.ParseIP(virtualHost) // Could also be an IP - if ipAddr == nil { - errs = appendValidation(errs, err) - allHostsValid = false - } - } else if appliesToMesh && virtualHost == "*" { - errs = appendValidation(errs, fmt.Errorf("wildcard host * is not allowed for virtual services bound to the mesh gateway")) - allHostsValid = false - } - } - - // Check for duplicate hosts - // Duplicates include literal duplicates as well as wildcard duplicates - // E.g., *.foo.com, and *.com are duplicates in the same virtual service - if allHostsValid { - for i := 0; i < len(virtualService.Hosts); i++ { - hostI := host.Name(virtualService.Hosts[i]) - for j := i + 1; j < len(virtualService.Hosts); j++ { - hostJ := host.Name(virtualService.Hosts[j]) - if hostI.Matches(hostJ) { - errs = appendValidation(errs, fmt.Errorf("duplicate hosts in virtual service: %s & %s", hostI, hostJ)) - } - } - } - } - - if len(virtualService.Http) == 0 && len(virtualService.Tcp) == 0 && len(virtualService.Tls) == 0 { - errs = appendValidation(errs, errors.New("http, tcp or tls must be provided in virtual service")) - } - for _, httpRoute := range virtualService.Http { - if httpRoute == nil { - errs = appendValidation(errs, errors.New("http route may not be null")) - continue - } - errs = appendValidation(errs, validateHTTPRoute(httpRoute, len(virtualService.Hosts) == 0)) - } - for _, tlsRoute := range virtualService.Tls { - errs = appendValidation(errs, validateTLSRoute(tlsRoute, virtualService)) - } - for _, tcpRoute := range virtualService.Tcp { - errs = appendValidation(errs, validateTCPRoute(tcpRoute)) - } - - errs = appendValidation(errs, validateExportTo(cfg.Namespace, virtualService.ExportTo, false, false)) - - warnUnused := func(ruleno, reason string) { - errs = appendValidation(errs, WrapWarning(&AnalysisAwareError{ - Type: "VirtualServiceUnreachableRule", - Msg: fmt.Sprintf("virtualService rule %v not used (%s)", ruleno, reason), - Parameters: []interface{}{ruleno, reason}, - })) - } - warnIneffective := func(ruleno, matchno, dupno string) { - errs = appendValidation(errs, WrapWarning(&AnalysisAwareError{ - Type: "VirtualServiceIneffectiveMatch", - Msg: fmt.Sprintf("virtualService rule %v match %v is not used (duplicate/overlapping match in rule %v)", ruleno, matchno, dupno), - Parameters: []interface{}{ruleno, matchno, dupno}, - })) - } - - analyzeUnreachableHTTPRules(virtualService.Http, warnUnused, warnIneffective) - analyzeUnreachableTCPRules(virtualService.Tcp, warnUnused, warnIneffective) - analyzeUnreachableTLSRules(virtualService.Tls, warnUnused, warnIneffective) - - return errs.Unwrap() - }) - -func assignExactOrPrefix(exact, prefix string) string { - if exact != "" { - return matchExact + exact - } - if prefix != "" { - return matchPrefix + prefix - } - return "" -} - -// genMatchHTTPRoutes build the match rules into struct OverlappingMatchValidationForHTTPRoute -// based on particular HTTPMatchRequest, according to comments on https://github.com/istio/istio/pull/32701 -// only support Match's port, method, authority, headers, query params and nonheaders for now. -func genMatchHTTPRoutes(route *networking.HTTPRoute, match *networking.HTTPMatchRequest, - rulen, matchn int) (matchHTTPRoutes *OverlappingMatchValidationForHTTPRoute) { - // skip current match if no match field for current route - if match == nil { - return nil - } - // skip current match if no URI field - if match.Uri == nil { - return nil - } - // store all httproute with prefix match uri - tmpPrefix := match.Uri.GetPrefix() - if tmpPrefix != "" { - // set Method - methodExact := match.Method.GetExact() - methodPrefix := match.Method.GetPrefix() - methodMatch := assignExactOrPrefix(methodExact, methodPrefix) - // if no method information, it should be GET by default - if methodMatch == "" { - methodMatch = matchExact + "GET" - } - - // set Authority - authorityExact := match.Authority.GetExact() - authorityPrefix := match.Authority.GetPrefix() - authorityMatch := assignExactOrPrefix(authorityExact, authorityPrefix) - - // set Headers - headerMap := make(map[string]string) - for hkey, hvalue := range match.Headers { - hvalueExact := hvalue.GetExact() - hvaluePrefix := hvalue.GetPrefix() - hvalueMatch := assignExactOrPrefix(hvalueExact, hvaluePrefix) - headerMap[hkey] = hvalueMatch - } - - // set QueryParams - QPMap := make(map[string]string) - for qpkey, qpvalue := range match.QueryParams { - qpvalueExact := qpvalue.GetExact() - qpvaluePrefix := qpvalue.GetPrefix() - qpvalueMatch := assignExactOrPrefix(qpvalueExact, qpvaluePrefix) - QPMap[qpkey] = qpvalueMatch - } - - // set WithoutHeaders - noHeaderMap := make(map[string]string) - for nhkey, nhvalue := range match.WithoutHeaders { - nhvalueExact := nhvalue.GetExact() - nhvaluePrefix := nhvalue.GetPrefix() - nhvalueMatch := assignExactOrPrefix(nhvalueExact, nhvaluePrefix) - noHeaderMap[nhkey] = nhvalueMatch - } - - matchHTTPRoutes = &OverlappingMatchValidationForHTTPRoute{ - routeName(route, rulen), - requestName(match, matchn), - tmpPrefix, - match.Port, - methodMatch, - authorityMatch, - headerMap, - QPMap, - noHeaderMap, - } - return - } - return nil -} - -// coveredValidation validate the overlapping match between two instance of OverlappingMatchValidationForHTTPRoute -func coveredValidation(vA, vB *OverlappingMatchValidationForHTTPRoute) bool { - // check the URI overlapping match, such as vB.Prefix is '/debugs' and vA.Prefix is '/debug' - if strings.HasPrefix(vB.Prefix, vA.Prefix) { - // check the port field - if vB.MatchPort != vA.MatchPort { - return false - } - - // check the match method - if vA.MatchMethod != vB.MatchMethod { - if !strings.HasPrefix(vA.MatchMethod, vB.MatchMethod) { - return false - } - } - - // check the match authority - if vA.MatchAuthority != vB.MatchAuthority { - if !strings.HasPrefix(vA.MatchAuthority, vB.MatchAuthority) { - return false - } - } - - // check the match Headers - vAHeaderLen := len(vA.MatchHeaders) - vBHeaderLen := len(vB.MatchHeaders) - if vAHeaderLen != vBHeaderLen { - return false - } - for hdKey, hdValue := range vA.MatchHeaders { - vBhdValue, ok := vB.MatchHeaders[hdKey] - if !ok { - return false - } else if hdValue != vBhdValue { - if !strings.HasPrefix(hdValue, vBhdValue) { - return false - } - } - } - - // check the match QueryParams - vAQPLen := len(vA.MatchQueryParams) - vBQPLen := len(vB.MatchQueryParams) - if vAQPLen != vBQPLen { - return false - } - for qpKey, qpValue := range vA.MatchQueryParams { - vBqpValue, ok := vB.MatchQueryParams[qpKey] - if !ok { - return false - } else if qpValue != vBqpValue { - if !strings.HasPrefix(qpValue, vBqpValue) { - return false - } - } - } - - // check the match NonHeaders - vANonHDLen := len(vA.MatchNonHeaders) - vBNonHDLen := len(vB.MatchNonHeaders) - if vANonHDLen != vBNonHDLen { - return false - } - for nhKey, nhValue := range vA.MatchNonHeaders { - vBnhValue, ok := vB.MatchNonHeaders[nhKey] - if !ok { - return false - } else if nhValue != vBnhValue { - if !strings.HasPrefix(nhValue, vBnhValue) { - return false - } - } - } - } else { - // no URI overlapping match - return false - } - return true -} - -func analyzeUnreachableHTTPRules(routes []*networking.HTTPRoute, - reportUnreachable func(ruleno, reason string), reportIneffective func(ruleno, matchno, dupno string)) { - matchesEncountered := make(map[string]int) - emptyMatchEncountered := -1 - var matchHTTPRoutes []*OverlappingMatchValidationForHTTPRoute - for rulen, route := range routes { - if route == nil { - continue - } - if len(route.Match) == 0 { - if emptyMatchEncountered >= 0 { - reportUnreachable(routeName(route, rulen), "only the last rule can have no matches") - } - emptyMatchEncountered = rulen - continue - } - - duplicateMatches := 0 - for matchn, match := range route.Match { - dupn, ok := matchesEncountered[asJSON(match)] - if ok { - reportIneffective(routeName(route, rulen), requestName(match, matchn), routeName(routes[dupn], dupn)) - duplicateMatches++ - // no need to handle for totally duplicated match rules - continue - } - matchesEncountered[asJSON(match)] = rulen - // build the match rules into struct OverlappingMatchValidationForHTTPRoute based on current match - matchHTTPRoute := genMatchHTTPRoutes(route, match, rulen, matchn) - if matchHTTPRoute != nil { - matchHTTPRoutes = append(matchHTTPRoutes, matchHTTPRoute) - } - } - if duplicateMatches == len(route.Match) { - reportUnreachable(routeName(route, rulen), "all matches used by prior rules") - } - } - - // at least 2 prefix matched routes for overlapping match validation - if len(matchHTTPRoutes) > 1 { - // check the overlapping match from the first prefix information - for routeIndex, routePrefix := range matchHTTPRoutes { - for rIndex := routeIndex + 1; rIndex < len(matchHTTPRoutes); rIndex++ { - // exclude the duplicate-match cases which have been validated above - if strings.Compare(matchHTTPRoutes[rIndex].Prefix, routePrefix.Prefix) == 0 { - continue - } - // Validate former prefix match does not cover the latter one. - if coveredValidation(routePrefix, matchHTTPRoutes[rIndex]) { - prefixMatchA := matchHTTPRoutes[rIndex].MatchStr + " of prefix " + matchHTTPRoutes[rIndex].Prefix - prefixMatchB := routePrefix.MatchStr + " of prefix " + routePrefix.Prefix + " on " + routePrefix.RouteStr - reportIneffective(matchHTTPRoutes[rIndex].RouteStr, prefixMatchA, prefixMatchB) - } - } - } - } -} - -// NOTE: This method identical to analyzeUnreachableHTTPRules. -func analyzeUnreachableTCPRules(routes []*networking.TCPRoute, - reportUnreachable func(ruleno, reason string), reportIneffective func(ruleno, matchno, dupno string)) { - matchesEncountered := make(map[string]int) - emptyMatchEncountered := -1 - for rulen, route := range routes { - if route == nil { - continue - } - if len(route.Match) == 0 { - if emptyMatchEncountered >= 0 { - reportUnreachable(routeName(route, rulen), "only the last rule can have no matches") - } - emptyMatchEncountered = rulen - continue - } - - duplicateMatches := 0 - for matchn, match := range route.Match { - dupn, ok := matchesEncountered[asJSON(match)] - if ok { - reportIneffective(routeName(route, rulen), requestName(match, matchn), routeName(routes[dupn], dupn)) - duplicateMatches++ - } else { - matchesEncountered[asJSON(match)] = rulen - } - } - if duplicateMatches == len(route.Match) { - reportUnreachable(routeName(route, rulen), "all matches used by prior rules") - } - } -} - -// NOTE: This method identical to analyzeUnreachableHTTPRules. -func analyzeUnreachableTLSRules(routes []*networking.TLSRoute, - reportUnreachable func(ruleno, reason string), reportIneffective func(ruleno, matchno, dupno string)) { - matchesEncountered := make(map[string]int) - emptyMatchEncountered := -1 - for rulen, route := range routes { - if route == nil { - continue - } - if len(route.Match) == 0 { - if emptyMatchEncountered >= 0 { - reportUnreachable(routeName(route, rulen), "only the last rule can have no matches") - } - emptyMatchEncountered = rulen - continue - } - - duplicateMatches := 0 - for matchn, match := range route.Match { - dupn, ok := matchesEncountered[asJSON(match)] - if ok { - reportIneffective(routeName(route, rulen), requestName(match, matchn), routeName(routes[dupn], dupn)) - duplicateMatches++ - } else { - matchesEncountered[asJSON(match)] = rulen - } - } - if duplicateMatches == len(route.Match) { - reportUnreachable(routeName(route, rulen), "all matches used by prior rules") - } - } -} - -// asJSON() creates a JSON serialization of a match, to use for match comparison. We don't use the JSON itself. -func asJSON(data interface{}) string { - // Remove the name, so we can create a serialization that only includes traffic routing config - switch mr := data.(type) { - case *networking.HTTPMatchRequest: - if mr != nil && mr.Name != "" { - cl := &networking.HTTPMatchRequest{} - protomarshal.ShallowCopy(cl, mr) - cl.Name = "" - data = cl - } - } - - b, err := json.Marshal(data) - if err != nil { - return err.Error() - } - return string(b) -} - -func routeName(route interface{}, routen int) string { - switch r := route.(type) { - case *networking.HTTPRoute: - if r.Name != "" { - return fmt.Sprintf("%q", r.Name) - } - // TCP and TLS routes have no names - } - - return fmt.Sprintf("#%d", routen) -} - -func requestName(match interface{}, matchn int) string { - switch mr := match.(type) { - case *networking.HTTPMatchRequest: - if mr != nil && mr.Name != "" { - return fmt.Sprintf("%q", mr.Name) - } - // TCP and TLS matches have no names - } - - return fmt.Sprintf("#%d", matchn) -} - -func validateTLSRoute(tls *networking.TLSRoute, context *networking.VirtualService) (errs Validation) { - if tls == nil { - return - } - if len(tls.Match) == 0 { - errs = appendValidation(errs, errors.New("TLS route must have at least one match condition")) - } - for _, match := range tls.Match { - errs = appendValidation(errs, validateTLSMatch(match, context)) - } - if len(tls.Route) == 0 { - errs = appendValidation(errs, errors.New("TLS route is required")) - } - errs = appendValidation(errs, validateRouteDestinations(tls.Route)) - return errs -} - -func validateTLSMatch(match *networking.TLSMatchAttributes, context *networking.VirtualService) (errs Validation) { - if match == nil { - errs = appendValidation(errs, errors.New("TLS match may not be null")) - return - } - if len(match.SniHosts) == 0 { - errs = appendValidation(errs, fmt.Errorf("TLS match must have at least one SNI host")) - } else { - for _, sniHost := range match.SniHosts { - errs = appendValidation(errs, validateSniHost(sniHost, context)) - } - } - - for _, destinationSubnet := range match.DestinationSubnets { - errs = appendValidation(errs, ValidateIPSubnet(destinationSubnet)) - } - - if match.Port != 0 { - errs = appendValidation(errs, ValidatePort(int(match.Port))) - } - errs = appendValidation(errs, labels.Instance(match.SourceLabels).Validate()) - errs = appendValidation(errs, validateGatewayNames(match.Gateways)) - return -} - -func validateSniHost(sniHost string, context *networking.VirtualService) (errs Validation) { - if err := ValidateWildcardDomain(sniHost); err != nil { - ipAddr := net.ParseIP(sniHost) // Could also be an IP - if ipAddr != nil { - errs = appendValidation(errs, WrapWarning(fmt.Errorf("using an IP address (%q) goes against SNI spec and most clients do not support this", ipAddr))) - return - } - return appendValidation(errs, err) - } - sniHostname := host.Name(sniHost) - for _, hostname := range context.Hosts { - if sniHostname.SubsetOf(host.Name(hostname)) { - return - } - } - return appendValidation(errs, fmt.Errorf("SNI host %q is not a compatible subset of any of the virtual service hosts: [%s]", - sniHost, strings.Join(context.Hosts, ", "))) -} - -func validateTCPRoute(tcp *networking.TCPRoute) (errs error) { - if tcp == nil { - return nil - } - for _, match := range tcp.Match { - errs = appendErrors(errs, validateTCPMatch(match)) - } - if len(tcp.Route) == 0 { - errs = appendErrors(errs, errors.New("TCP route is required")) - } - errs = appendErrors(errs, validateRouteDestinations(tcp.Route)) - return -} - -func validateTCPMatch(match *networking.L4MatchAttributes) (errs error) { - if match == nil { - errs = multierror.Append(errs, errors.New("tcp match may not be nil")) - return - } - for _, destinationSubnet := range match.DestinationSubnets { - errs = appendErrors(errs, ValidateIPSubnet(destinationSubnet)) - } - if match.Port != 0 { - errs = appendErrors(errs, ValidatePort(int(match.Port))) - } - errs = appendErrors(errs, labels.Instance(match.SourceLabels).Validate()) - errs = appendErrors(errs, validateGatewayNames(match.Gateways)) - return -} - -func validateStringMatchRegexp(sm *networking.StringMatch, where string) error { - switch sm.GetMatchType().(type) { - case *networking.StringMatch_Regex: - default: - return nil - } - re := sm.GetRegex() - if re == "" { - return fmt.Errorf("%q: regex string match should not be empty", where) - } - - // Envoy enforces a re2.max_program_size.error_level re2 program size is not the same as length, - // but it is always *larger* than length. Because goland does not have a way to evaluate the - // program size, we approximate by the length. To ensure that a program that is smaller than 1024 - // length but larger than 1024 size does not enter the system, we program Envoy to allow very large - // regexs to avoid NACKs. See - // https://github.com/jpeach/snippets/blob/889fda84cc8713af09205438b33553eb69dd5355/re2sz.cc to - // evaluate program size. - if len(re) > 1024 { - return fmt.Errorf("%q: regex is too large, max length allowed is 1024", where) - } - - _, err := regexp.Compile(re) - if err == nil { - return nil - } - - return fmt.Errorf("%q: %w; Istio uses RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax)", where, err) -} - -func validateGatewayNames(gatewayNames []string) (errs Validation) { - for _, gatewayName := range gatewayNames { - parts := strings.SplitN(gatewayName, "/", 2) - if len(parts) != 2 { - if strings.Contains(gatewayName, ".") { - // Legacy FQDN style - parts := strings.Split(gatewayName, ".") - recommended := fmt.Sprintf("%s/%s", parts[1], parts[0]) - errs = appendValidation(errs, WrapWarning(fmt.Errorf( - "using legacy gatewayName format %q; prefer the / format: %q", gatewayName, recommended))) - } - errs = appendValidation(errs, ValidateFQDN(gatewayName)) - return - } - - if len(parts[0]) == 0 || len(parts[1]) == 0 { - errs = appendValidation(errs, fmt.Errorf("config namespace and gateway name cannot be empty")) - } - - // namespace and name must be DNS labels - if !labels.IsDNS1123Label(parts[0]) { - errs = appendValidation(errs, fmt.Errorf("invalid value for namespace: %q", parts[0])) - } - - if !labels.IsDNS1123Label(parts[1]) { - errs = appendValidation(errs, fmt.Errorf("invalid value for gateway name: %q", parts[1])) - } - } - return -} - -func validateHTTPRouteDestinations(weights []*networking.HTTPRouteDestination) (errs error) { - var totalWeight int32 - for _, weight := range weights { - if weight == nil { - errs = multierror.Append(errs, errors.New("weight may not be nil")) - continue - } - if weight.Destination == nil { - errs = multierror.Append(errs, errors.New("destination is required")) - } - - // header manipulations - for name, val := range weight.Headers.GetRequest().GetAdd() { - errs = appendErrors(errs, ValidateHTTPHeaderWithHostOperationName(name)) - errs = appendErrors(errs, ValidateHTTPHeaderValue(val)) - } - for name, val := range weight.Headers.GetRequest().GetSet() { - errs = appendErrors(errs, ValidateHTTPHeaderWithHostOperationName(name)) - errs = appendErrors(errs, ValidateHTTPHeaderValue(val)) - } - for _, name := range weight.Headers.GetRequest().GetRemove() { - errs = appendErrors(errs, ValidateHTTPHeaderOperationName(name)) - } - for name, val := range weight.Headers.GetResponse().GetAdd() { - errs = appendErrors(errs, ValidateHTTPHeaderOperationName(name)) - errs = appendErrors(errs, ValidateHTTPHeaderValue(val)) - } - for name, val := range weight.Headers.GetResponse().GetSet() { - errs = appendErrors(errs, ValidateHTTPHeaderOperationName(name)) - errs = appendErrors(errs, ValidateHTTPHeaderValue(val)) - } - for _, name := range weight.Headers.GetResponse().GetRemove() { - errs = appendErrors(errs, ValidateHTTPHeaderOperationName(name)) - } - - errs = appendErrors(errs, validateDestination(weight.Destination)) - errs = appendErrors(errs, ValidatePercent(weight.Weight)) - totalWeight += weight.Weight - } - if len(weights) > 1 && totalWeight != 100 { - errs = appendErrors(errs, fmt.Errorf("total destination weight %v != 100", totalWeight)) - } - return -} - -func validateRouteDestinations(weights []*networking.RouteDestination) (errs error) { - var totalWeight int32 - for _, weight := range weights { - if weight == nil { - errs = multierror.Append(errs, errors.New("weight may not be nil")) - continue - } - if weight.Destination == nil { - errs = multierror.Append(errs, errors.New("destination is required")) - } - errs = appendErrors(errs, validateDestination(weight.Destination)) - errs = appendErrors(errs, ValidatePercent(weight.Weight)) - totalWeight += weight.Weight - } - if len(weights) > 1 && totalWeight != 100 { - errs = appendErrors(errs, fmt.Errorf("total destination weight %v != 100", totalWeight)) - } - return -} - -func validateCORSPolicy(policy *networking.CorsPolicy) (errs error) { - if policy == nil { - return - } - - for _, origin := range policy.AllowOrigins { - errs = appendErrors(errs, validateAllowOrigins(origin)) - } - - for _, method := range policy.AllowMethods { - errs = appendErrors(errs, validateHTTPMethod(method)) - } - - for _, name := range policy.AllowHeaders { - errs = appendErrors(errs, ValidateHTTPHeaderName(name)) - } - - for _, name := range policy.ExposeHeaders { - errs = appendErrors(errs, ValidateHTTPHeaderName(name)) - } - - if policy.MaxAge != nil { - errs = appendErrors(errs, ValidateDuration(policy.MaxAge)) - if policy.MaxAge.Nanos > 0 { - errs = multierror.Append(errs, errors.New("max_age duration is accurate only to seconds precision")) - } - } - - return -} - -func validateAllowOrigins(origin *networking.StringMatch) error { - var match string - switch origin.MatchType.(type) { - case *networking.StringMatch_Exact: - match = origin.GetExact() - case *networking.StringMatch_Prefix: - match = origin.GetPrefix() - case *networking.StringMatch_Regex: - match = origin.GetRegex() - } - if match == "" { - return fmt.Errorf("'%v' is not a valid match type for CORS allow origins", match) - } - return validateStringMatchRegexp(origin, "corsPolicy.allowOrigins") -} - -func validateHTTPMethod(method string) error { - if !supportedMethods[method] { - return fmt.Errorf("%q is not a supported HTTP method", method) - } - return nil -} - -func validateHTTPFaultInjection(fault *networking.HTTPFaultInjection) (errs error) { - if fault == nil { - return - } - - if fault.Abort == nil && fault.Delay == nil { - errs = multierror.Append(errs, errors.New("HTTP fault injection must have an abort and/or a delay")) - } - - errs = appendErrors(errs, validateHTTPFaultInjectionAbort(fault.Abort)) - errs = appendErrors(errs, validateHTTPFaultInjectionDelay(fault.Delay)) - - return -} - -func validateHTTPFaultInjectionAbort(abort *networking.HTTPFaultInjection_Abort) (errs error) { - if abort == nil { - return - } - - errs = appendErrors(errs, validatePercentage(abort.Percentage)) - - switch abort.ErrorType.(type) { - case *networking.HTTPFaultInjection_Abort_GrpcStatus: - // TODO: gRPC status validation - errs = multierror.Append(errs, errors.New("gRPC abort fault injection not supported yet")) - case *networking.HTTPFaultInjection_Abort_Http2Error: - // TODO: HTTP2 error validation - errs = multierror.Append(errs, errors.New("HTTP/2 abort fault injection not supported yet")) - case *networking.HTTPFaultInjection_Abort_HttpStatus: - errs = appendErrors(errs, validateHTTPStatus(abort.GetHttpStatus())) - } - - return -} - -func validateHTTPStatus(status int32) error { - if status < 200 || status > 600 { - return fmt.Errorf("HTTP status %d is not in range 200-599", status) - } - return nil -} - -func validateHTTPFaultInjectionDelay(delay *networking.HTTPFaultInjection_Delay) (errs error) { - if delay == nil { - return - } - - errs = appendErrors(errs, validatePercentage(delay.Percentage)) - - switch v := delay.HttpDelayType.(type) { - case *networking.HTTPFaultInjection_Delay_FixedDelay: - errs = appendErrors(errs, ValidateDuration(v.FixedDelay)) - case *networking.HTTPFaultInjection_Delay_ExponentialDelay: - errs = appendErrors(errs, ValidateDuration(v.ExponentialDelay)) - errs = multierror.Append(errs, fmt.Errorf("exponentialDelay not supported yet")) - } - - return -} - -func validateDestination(destination *networking.Destination) (errs error) { - if destination == nil { - return - } - - hostname := destination.Host - if hostname == "*" { - errs = appendErrors(errs, fmt.Errorf("invalid destination host %s", hostname)) - } else { - errs = appendErrors(errs, ValidateWildcardDomain(hostname)) - } - if destination.Subset != "" { - errs = appendErrors(errs, validateSubsetName(destination.Subset)) - } - if destination.Port != nil { - errs = appendErrors(errs, validatePortSelector(destination.Port)) - } - - return -} - -func validateSubsetName(name string) error { - if len(name) == 0 { - return fmt.Errorf("subset name cannot be empty") - } - if !labels.IsDNS1123Label(name) { - return fmt.Errorf("subset name is invalid: %s", name) - } - return nil -} - -func validatePortSelector(selector *networking.PortSelector) (errs error) { - if selector == nil { - return nil - } - - // port must be a number - number := int(selector.GetNumber()) - errs = appendErrors(errs, ValidatePort(number)) - return -} - -func validateHTTPRetry(retries *networking.HTTPRetry) (errs error) { - if retries == nil { - return - } - - if retries.Attempts < 0 { - errs = multierror.Append(errs, errors.New("attempts cannot be negative")) - } - - if retries.Attempts == 0 && (retries.PerTryTimeout != nil || retries.RetryOn != "" || retries.RetryRemoteLocalities != nil) { - errs = appendErrors(errs, errors.New("http retry policy configured when attempts are set to 0 (disabled)")) - } - - if retries.PerTryTimeout != nil { - errs = appendErrors(errs, ValidateDuration(retries.PerTryTimeout)) - } - if retries.RetryOn != "" { - retryOnPolicies := strings.Split(retries.RetryOn, ",") - for _, policy := range retryOnPolicies { - // Try converting it to an integer to see if it's a valid HTTP status code. - i, _ := strconv.Atoi(policy) - - if http.StatusText(i) == "" && !supportedRetryOnPolicies[policy] { - errs = appendErrors(errs, fmt.Errorf("%q is not a valid retryOn policy", policy)) - } - } - } - - return -} - -func validateHTTPRedirect(redirect *networking.HTTPRedirect) error { - if redirect == nil { - return nil - } - if redirect.Uri == "" && redirect.Authority == "" && redirect.RedirectPort == nil && redirect.Scheme == "" { - return errors.New("redirect must specify URI, authority, scheme, or port") - } - - if redirect.RedirectCode != 0 { - if redirect.RedirectCode < 300 || redirect.RedirectCode > 399 { - return fmt.Errorf("%d is not a valid redirect code, must be 3xx", redirect.RedirectCode) - } - } - if redirect.Scheme != "" && redirect.Scheme != "http" && redirect.Scheme != "https" { - return fmt.Errorf(`invalid redirect scheme, must be "http" or "https"`) - } - if redirect.GetPort() > 0 { - if err := ValidatePort(int(redirect.GetPort())); err != nil { - return err - } - } - return nil -} - -func validateHTTPRewrite(rewrite *networking.HTTPRewrite) error { - if rewrite != nil && rewrite.Uri == "" && rewrite.Authority == "" { - return errors.New("rewrite must specify URI, authority, or both") - } - return nil -} - -// ValidateWorkloadEntry validates a workload entry. -var ValidateWorkloadEntry = registerValidateFunc("ValidateWorkloadEntry", - func(cfg config.Config) (Warning, error) { - we, ok := cfg.Spec.(*networking.WorkloadEntry) - if !ok { - return nil, fmt.Errorf("cannot cast to workload entry") - } - return validateWorkloadEntry(we) - }) - -func validateWorkloadEntry(we *networking.WorkloadEntry) (Warning, error) { - errs := Validation{} - if we.Address == "" { - return nil, fmt.Errorf("address must be set") - } - // Since we don't know if its meant to be DNS or STATIC type without association with a ServiceEntry, - // check based on content and try validations. - addr := we.Address - // First check if it is a Unix endpoint - this will be specified for STATIC. - if strings.HasPrefix(we.Address, UnixAddressPrefix) { - errs = appendValidation(errs, ValidateUnixAddress(strings.TrimPrefix(addr, UnixAddressPrefix))) - if len(we.Ports) != 0 { - errs = appendValidation(errs, fmt.Errorf("unix endpoint %s must not include ports", we.Address)) - } - } else { - // This could be IP (in STATIC resolution) or DNS host name (for DNS). - ipAddr := net.ParseIP(we.Address) - if ipAddr == nil { - if err := ValidateFQDN(we.Address); err != nil { // Otherwise could be an FQDN - errs = appendValidation(errs, - fmt.Errorf("endpoint address %q is not a valid FQDN or an IP address", we.Address)) - } - } - } - - errs = appendValidation(errs, - labels.Instance(we.Labels).Validate()) - for name, port := range we.Ports { - // TODO: Validate port is part of Service Port - which is tricky to validate with out service entry. - errs = appendValidation(errs, - ValidatePortName(name), - ValidatePort(int(port))) - } - return errs.Unwrap() -} - -// ValidateWorkloadGroup validates a workload group. -var ValidateWorkloadGroup = registerValidateFunc("ValidateWorkloadGroup", - func(cfg config.Config) (warnings Warning, errs error) { - wg, ok := cfg.Spec.(*networking.WorkloadGroup) - if !ok { - return nil, fmt.Errorf("cannot cast to workload entry") - } - - if wg.Template == nil { - return nil, fmt.Errorf("template is required") - } - // Do not call validateWorkloadEntry. Some fields, such as address, are required in WorkloadEntry - // but not in the template since they are auto populated - - if wg.Metadata != nil { - if err := labels.Instance(wg.Metadata.Labels).Validate(); err != nil { - return nil, fmt.Errorf("invalid labels: %v", err) - } - } - - return nil, validateReadinessProbe(wg.Probe) - }) - -func validateReadinessProbe(probe *networking.ReadinessProbe) (errs error) { - if probe == nil { - return nil - } - if probe.PeriodSeconds < 0 { - errs = appendErrors(errs, fmt.Errorf("periodSeconds must be non-negative")) - } - if probe.InitialDelaySeconds < 0 { - errs = appendErrors(errs, fmt.Errorf("initialDelaySeconds must be non-negative")) - } - if probe.TimeoutSeconds < 0 { - errs = appendErrors(errs, fmt.Errorf("timeoutSeconds must be non-negative")) - } - if probe.SuccessThreshold < 0 { - errs = appendErrors(errs, fmt.Errorf("successThreshold must be non-negative")) - } - if probe.FailureThreshold < 0 { - errs = appendErrors(errs, fmt.Errorf("failureThreshold must be non-negative")) - } - switch m := probe.HealthCheckMethod.(type) { - case *networking.ReadinessProbe_HttpGet: - h := m.HttpGet - if h == nil { - errs = appendErrors(errs, fmt.Errorf("httpGet may not be nil")) - break - } - errs = appendErrors(errs, ValidatePort(int(h.Port))) - if h.Scheme != "" && h.Scheme != string(apimirror.URISchemeHTTPS) && h.Scheme != string(apimirror.URISchemeHTTP) { - errs = appendErrors(errs, fmt.Errorf(`httpGet.scheme must be one of "http", "https"`)) - } - for _, header := range h.HttpHeaders { - if header == nil { - errs = appendErrors(errs, fmt.Errorf("invalid nil header")) - continue - } - errs = appendErrors(errs, ValidateHTTPHeaderName(header.Name)) - } - case *networking.ReadinessProbe_TcpSocket: - h := m.TcpSocket - if h == nil { - errs = appendErrors(errs, fmt.Errorf("tcpSocket may not be nil")) - break - } - errs = appendErrors(errs, ValidatePort(int(h.Port))) - case *networking.ReadinessProbe_Exec: - h := m.Exec - if h == nil { - errs = appendErrors(errs, fmt.Errorf("exec may not be nil")) - break - } - if len(h.Command) == 0 { - errs = appendErrors(errs, fmt.Errorf("exec.command is required")) - } - default: - errs = appendErrors(errs, fmt.Errorf("unknown health check method %T", m)) - } - return errs -} - -// ValidateServiceEntry validates a service entry. -var ValidateServiceEntry = registerValidateFunc("ValidateServiceEntry", - func(cfg config.Config) (Warning, error) { - serviceEntry, ok := cfg.Spec.(*networking.ServiceEntry) - if !ok { - return nil, fmt.Errorf("cannot cast to service entry") - } - - if err := validateAlphaWorkloadSelector(serviceEntry.WorkloadSelector); err != nil { - return nil, err - } - - errs := Validation{} - - if serviceEntry.WorkloadSelector != nil && serviceEntry.Endpoints != nil { - errs = appendValidation(errs, fmt.Errorf("only one of WorkloadSelector or Endpoints is allowed in Service Entry")) - } - - if len(serviceEntry.Hosts) == 0 { - errs = appendValidation(errs, fmt.Errorf("service entry must have at least one host")) - } - for _, hostname := range serviceEntry.Hosts { - // Full wildcard is not allowed in the service entry. - if hostname == "*" { - errs = appendValidation(errs, fmt.Errorf("invalid host %s", hostname)) - } else { - errs = appendValidation(errs, ValidateWildcardDomain(hostname)) - } - } - - cidrFound := false - for _, address := range serviceEntry.Addresses { - cidrFound = cidrFound || strings.Contains(address, "/") - errs = appendValidation(errs, ValidateIPSubnet(address)) - } - - if cidrFound { - if serviceEntry.Resolution != networking.ServiceEntry_NONE && serviceEntry.Resolution != networking.ServiceEntry_STATIC { - errs = appendValidation(errs, fmt.Errorf("CIDR addresses are allowed only for NONE/STATIC resolution types")) - } - } - - servicePortNumbers := make(map[uint32]bool) - servicePorts := make(map[string]bool, len(serviceEntry.Ports)) - for _, port := range serviceEntry.Ports { - if port == nil { - errs = appendValidation(errs, fmt.Errorf("service entry port may not be null")) - continue - } - if servicePorts[port.Name] { - errs = appendValidation(errs, fmt.Errorf("service entry port name %q already defined", port.Name)) - } - servicePorts[port.Name] = true - if servicePortNumbers[port.Number] { - errs = appendValidation(errs, fmt.Errorf("service entry port %d already defined", port.Number)) - } - servicePortNumbers[port.Number] = true - if port.TargetPort != 0 { - errs = appendValidation(errs, ValidatePort(int(port.TargetPort))) - } - errs = appendValidation(errs, - ValidatePortName(port.Name), - ValidateProtocol(port.Protocol), - ValidatePort(int(port.Number))) - } - - switch serviceEntry.Resolution { - case networking.ServiceEntry_NONE: - if len(serviceEntry.Endpoints) != 0 { - errs = appendValidation(errs, fmt.Errorf("no endpoints should be provided for resolution type none")) - } - case networking.ServiceEntry_STATIC: - unixEndpoint := false - for _, endpoint := range serviceEntry.Endpoints { - addr := endpoint.GetAddress() - if strings.HasPrefix(addr, UnixAddressPrefix) { - unixEndpoint = true - errs = appendValidation(errs, ValidateUnixAddress(strings.TrimPrefix(addr, UnixAddressPrefix))) - if len(endpoint.Ports) != 0 { - errs = appendValidation(errs, fmt.Errorf("unix endpoint %s must not include ports", addr)) - } - } else { - errs = appendValidation(errs, ValidateIPAddress(addr)) - - for name, port := range endpoint.Ports { - if !servicePorts[name] { - errs = appendValidation(errs, fmt.Errorf("endpoint port %v is not defined by the service entry", port)) - } - } - } - errs = appendValidation(errs, labels.Instance(endpoint.Labels).Validate()) - } - if unixEndpoint && len(serviceEntry.Ports) != 1 { - errs = appendValidation(errs, errors.New("exactly 1 service port required for unix endpoints")) - } - case networking.ServiceEntry_DNS, networking.ServiceEntry_DNS_ROUND_ROBIN: - if len(serviceEntry.Endpoints) == 0 { - for _, hostname := range serviceEntry.Hosts { - if err := ValidateFQDN(hostname); err != nil { - errs = appendValidation(errs, - fmt.Errorf("hosts must be FQDN if no endpoints are provided for resolution mode %s", serviceEntry.Resolution)) - } - } - } - - for _, endpoint := range serviceEntry.Endpoints { - ipAddr := net.ParseIP(endpoint.Address) // Typically it is an IP address - if ipAddr == nil { - if err := ValidateFQDN(endpoint.Address); err != nil { // Otherwise could be an FQDN - errs = appendValidation(errs, - fmt.Errorf("endpoint address %q is not a valid FQDN or an IP address", endpoint.Address)) - } - } - errs = appendValidation(errs, - labels.Instance(endpoint.Labels).Validate()) - for name, port := range endpoint.Ports { - if !servicePorts[name] { - errs = appendValidation(errs, fmt.Errorf("endpoint port %v is not defined by the service entry", port)) - } - errs = appendValidation(errs, - ValidatePortName(name), - ValidatePort(int(port))) - } - } - if len(serviceEntry.Addresses) > 0 { - for _, port := range serviceEntry.Ports { - p := protocol.Parse(port.Protocol) - if p.IsTCP() { - if len(serviceEntry.Hosts) > 1 { - // TODO: prevent this invalid setting, maybe in 1.11+ - errs = appendValidation(errs, WrapWarning(fmt.Errorf("service entry can not have more than one host specified "+ - "simultaneously with address and tcp port"))) - } - break - } - } - } - default: - errs = appendValidation(errs, fmt.Errorf("unsupported resolution type %s", - networking.ServiceEntry_Resolution_name[int32(serviceEntry.Resolution)])) - } - - // multiple hosts and TCP is invalid unless the resolution type is NONE. - // depending on the protocol, we can differentiate between hosts when proxying: - // - with HTTP, the authority header can be used - // - with HTTPS/TLS with SNI, the ServerName can be used - // however, for plain TCP there is no way to differentiate between the - // hosts so we consider it invalid, unless the resolution type is NONE - // (because the hosts are ignored). - if serviceEntry.Resolution != networking.ServiceEntry_NONE && len(serviceEntry.Hosts) > 1 { - for _, port := range serviceEntry.Ports { - p := protocol.Parse(port.Protocol) - if !p.IsHTTP() && !p.IsTLS() { - errs = appendValidation(errs, fmt.Errorf("multiple hosts provided with non-HTTP, non-TLS ports")) - break - } - } - } - - errs = appendValidation(errs, validateExportTo(cfg.Namespace, serviceEntry.ExportTo, true, false)) - return errs.Unwrap() - }) - -// ValidatePortName validates a port name to DNS-1123 -func ValidatePortName(name string) error { - if !labels.IsDNS1123Label(name) { - return fmt.Errorf("invalid port name: %s", name) - } - return nil -} - -// ValidateProtocol validates a portocol name is known -func ValidateProtocol(protocolStr string) error { - // Empty string is used for protocol sniffing. - if protocolStr != "" && protocol.Parse(protocolStr) == protocol.Unsupported { - return fmt.Errorf("unsupported protocol: %s", protocolStr) - } - return nil -} - -// wrapper around multierror.Append that enforces the invariant that if all input errors are nil, the output -// error is nil (allowing validation without branching). -func appendValidation(v Validation, vs ...error) Validation { - appendError := func(err, err2 error) error { - if err == nil { - return err2 - } else if err2 == nil { - return err - } - return multierror.Append(err, err2) - } - - for _, nv := range vs { - switch t := nv.(type) { - case Validation: - v.Err = appendError(v.Err, t.Err) - v.Warning = appendError(v.Warning, t.Warning) - default: - v.Err = appendError(v.Err, t) - } - } - return v -} - -// appendErrorf appends a formatted error string -// nolint: unparam -func appendErrorf(v Validation, format string, a ...interface{}) Validation { - return appendValidation(v, fmt.Errorf(format, a...)) -} - -// appendWarningf appends a formatted warning string -// nolint: unparam -func appendWarningf(v Validation, format string, a ...interface{}) Validation { - return appendValidation(v, Warningf(format, a...)) -} - -// wrapper around multierror.Append that enforces the invariant that if all input errors are nil, the output -// error is nil (allowing validation without branching). -func appendErrors(err error, errs ...error) error { - appendError := func(err, err2 error) error { - if err == nil { - return err2 - } else if err2 == nil { - return err - } - return multierror.Append(err, err2) - } - - for _, err2 := range errs { - switch t := err2.(type) { - case Validation: - err = appendError(err, t.Err) - default: - err = appendError(err, err2) - } - } - return err -} - -// validateLocalityLbSetting checks the LocalityLbSetting of MeshConfig -func validateLocalityLbSetting(lb *networking.LocalityLoadBalancerSetting) error { - if lb == nil { - return nil - } - - if len(lb.GetDistribute()) > 0 && len(lb.GetFailover()) > 0 { - return fmt.Errorf("can not simultaneously specify 'distribute' and 'failover'") - } - - srcLocalities := make([]string, 0, len(lb.GetDistribute())) - for _, locality := range lb.GetDistribute() { - srcLocalities = append(srcLocalities, locality.From) - var totalWeight uint32 - destLocalities := make([]string, 0) - for loc, weight := range locality.To { - destLocalities = append(destLocalities, loc) - if weight <= 0 || weight > 100 { - return fmt.Errorf("locality weight must be in range [1, 100]") - } - totalWeight += weight - } - if totalWeight != 100 { - return fmt.Errorf("total locality weight %v != 100", totalWeight) - } - if err := validateLocalities(destLocalities); err != nil { - return err - } - } - - if err := validateLocalities(srcLocalities); err != nil { - return err - } - - for _, failover := range lb.GetFailover() { - if failover.From == failover.To { - return fmt.Errorf("locality lb failover settings must specify different regions") - } - if strings.Contains(failover.From, "/") || strings.Contains(failover.To, "/") { - return fmt.Errorf("locality lb failover only specify region") - } - if strings.Contains(failover.To, "*") || strings.Contains(failover.From, "*") { - return fmt.Errorf("locality lb failover region should not contain '*' wildcard") - } - } - - return nil -} - -func validateLocalities(localities []string) error { - regionZoneSubZoneMap := map[string]map[string]map[string]bool{} - for _, locality := range localities { - if n := strings.Count(locality, "*"); n > 0 { - if n > 1 || !strings.HasSuffix(locality, "*") { - return fmt.Errorf("locality %s wildcard '*' number can not exceed 1 and must be in the end", locality) - } - } - if _, exist := regionZoneSubZoneMap["*"]; exist { - return fmt.Errorf("locality %s overlap with previous specified ones", locality) - } - - region, zone, subZone, localityIndex, err := getLocalityParam(locality) - if err != nil { - return fmt.Errorf("locality %s must not contain empty region/zone/subzone info", locality) - } - - switch localityIndex { - case regionIndex: - if _, exist := regionZoneSubZoneMap[region]; exist { - return fmt.Errorf("locality %s overlap with previous specified ones", locality) - } - regionZoneSubZoneMap[region] = map[string]map[string]bool{"*": {"*": true}} - case zoneIndex: - if _, exist := regionZoneSubZoneMap[region]; exist { - if _, exist := regionZoneSubZoneMap[region]["*"]; exist { - return fmt.Errorf("locality %s overlap with previous specified ones", locality) - } - if _, exist := regionZoneSubZoneMap[region][zone]; exist { - return fmt.Errorf("locality %s overlap with previous specified ones", locality) - } - regionZoneSubZoneMap[region][zone] = map[string]bool{"*": true} - } else { - regionZoneSubZoneMap[region] = map[string]map[string]bool{zone: {"*": true}} - } - case subZoneIndex: - if _, exist := regionZoneSubZoneMap[region]; exist { - if _, exist := regionZoneSubZoneMap[region]["*"]; exist { - return fmt.Errorf("locality %s overlap with previous specified ones", locality) - } - if _, exist := regionZoneSubZoneMap[region][zone]; exist { - if regionZoneSubZoneMap[region][zone]["*"] { - return fmt.Errorf("locality %s overlap with previous specified ones", locality) - } - if regionZoneSubZoneMap[region][zone][subZone] { - return fmt.Errorf("locality %s overlap with previous specified ones", locality) - } - regionZoneSubZoneMap[region][zone][subZone] = true - } else { - regionZoneSubZoneMap[region][zone] = map[string]bool{subZone: true} - } - } else { - regionZoneSubZoneMap[region] = map[string]map[string]bool{zone: {subZone: true}} - } - } - } - - return nil -} - -func getLocalityParam(locality string) (string, string, string, int, error) { - var region, zone, subZone string - items := strings.SplitN(locality, "/", 3) - for i, item := range items { - if item == "" { - return "", "", "", -1, errors.New("item is nil") - } - switch i { - case regionIndex: - region = items[i] - case zoneIndex: - zone = items[i] - case subZoneIndex: - subZone = items[i] - } - } - return region, zone, subZone, len(items) - 1, nil -} - -// ValidateMeshNetworks validates meshnetworks. -func ValidateMeshNetworks(meshnetworks *meshconfig.MeshNetworks) (errs error) { - // TODO validate using the same gateway on multiple networks? - for name, network := range meshnetworks.Networks { - if err := validateNetwork(network); err != nil { - errs = multierror.Append(errs, multierror.Prefix(err, fmt.Sprintf("invalid network %v:", name))) - } - } - return -} - -func validateNetwork(network *meshconfig.Network) (errs error) { - for _, n := range network.Endpoints { - switch e := n.Ne.(type) { - case *meshconfig.Network_NetworkEndpoints_FromCidr: - if err := ValidateIPSubnet(e.FromCidr); err != nil { - errs = multierror.Append(errs, err) - } - case *meshconfig.Network_NetworkEndpoints_FromRegistry: - if ok := labels.IsDNS1123Label(e.FromRegistry); !ok { - errs = multierror.Append(errs, fmt.Errorf("invalid registry name: %v", e.FromRegistry)) - } - } - } - for _, n := range network.Gateways { - switch g := n.Gw.(type) { - case *meshconfig.Network_IstioNetworkGateway_RegistryServiceName: - if err := ValidateFQDN(g.RegistryServiceName); err != nil { - errs = multierror.Append(errs, err) - } - case *meshconfig.Network_IstioNetworkGateway_Address: - if ipErr := ValidateIPAddress(g.Address); ipErr != nil { - if !features.ResolveHostnameGateways { - err := fmt.Errorf("%v (hostname is allowed if RESOLVE_HOSTNAME_GATEWAYS is enabled)", ipErr) - errs = multierror.Append(errs, err) - } else if fqdnErr := ValidateFQDN(g.Address); fqdnErr != nil { - errs = multierror.Append(fmt.Errorf("%v is not a valid IP address or DNS name", g.Address)) - } - } - } - if err := ValidatePort(int(n.Port)); err != nil { - errs = multierror.Append(errs, err) - } - } - return -} - -func (aae *AnalysisAwareError) Error() string { - return aae.Msg -} - -// ValidateProxyConfig validates a ProxyConfig CR (as opposed to the MeshConfig field). -var ValidateProxyConfig = registerValidateFunc("ValidateProxyConfig", - func(cfg config.Config) (Warning, error) { - spec, ok := cfg.Spec.(*networkingv1beta1.ProxyConfig) - if !ok { - return nil, fmt.Errorf("cannot cast to proxyconfig") - } - - errs := Validation{} - - errs = appendValidation(errs, - validateWorkloadSelector(spec.Selector), - validateConcurrency(spec.Concurrency.GetValue()), - ) - return errs.Unwrap() - }) - -func validateConcurrency(concurrency int32) (v Validation) { - if concurrency < 0 { - v = appendErrorf(v, "concurrency must be greater than or equal to 0") - } - return -} - -// ValidateTelemetry validates a Telemetry. -var ValidateTelemetry = registerValidateFunc("ValidateTelemetry", - func(cfg config.Config) (Warning, error) { - spec, ok := cfg.Spec.(*telemetry.Telemetry) - if !ok { - return nil, fmt.Errorf("cannot cast to telemetry") - } - - errs := Validation{} - - errs = appendValidation(errs, - validateWorkloadSelector(spec.Selector), - validateTelemetryMetrics(spec.Metrics), - validateTelemetryTracing(spec.Tracing), - validateTelemetryAccessLogging(spec.AccessLogging), - ) - return errs.Unwrap() - }) - -func validateTelemetryAccessLogging(logging []*telemetry.AccessLogging) (v Validation) { - if len(logging) > 1 { - v = appendWarningf(v, "multiple accessLogging is not currently supported") - } - for idx, l := range logging { - if l == nil { - continue - } - if len(l.Providers) > 1 { - v = appendValidation(v, Warningf("accessLogging[%d]: multiple providers is not currently supported", idx)) - } - if l.Filter != nil { - v = appendValidation(v, validateTelemetryFilter(l.Filter)) - } - v = appendValidation(v, validateTelemetryProviders(l.Providers)) - } - return -} - -func validateTelemetryTracing(tracing []*telemetry.Tracing) (v Validation) { - if len(tracing) > 1 { - v = appendWarningf(v, "multiple tracing is not currently supported") - } - for _, l := range tracing { - if l == nil { - continue - } - if len(l.Providers) > 1 { - v = appendWarningf(v, "multiple providers is not currently supported") - } - v = appendValidation(v, validateTelemetryProviders(l.Providers)) - if l.RandomSamplingPercentage.GetValue() < 0 || l.RandomSamplingPercentage.GetValue() > 100 { - v = appendErrorf(v, "randomSamplingPercentage must be in range [0.0, 100.0]") - } - for name, tag := range l.CustomTags { - if name == "" { - v = appendErrorf(v, "tag name may not be empty") - } - if tag == nil { - v = appendErrorf(v, "tag '%s' may not have a nil value", name) - continue - } - switch t := tag.Type.(type) { - case *telemetry.Tracing_CustomTag_Literal: - if t.Literal.GetValue() == "" { - v = appendErrorf(v, "literal tag value may not be empty") - } - case *telemetry.Tracing_CustomTag_Header: - if t.Header.GetName() == "" { - v = appendErrorf(v, "header tag name may not be empty") - } - case *telemetry.Tracing_CustomTag_Environment: - if t.Environment.GetName() == "" { - v = appendErrorf(v, "environment tag name may not be empty") - } - } - } - } - return -} - -func validateTelemetryMetrics(metrics []*telemetry.Metrics) (v Validation) { - for _, l := range metrics { - if l == nil { - continue - } - v = appendValidation(v, validateTelemetryProviders(l.Providers)) - for _, o := range l.Overrides { - if o == nil { - v = appendErrorf(v, "tagOverrides may not be null") - continue - } - for tagName, to := range o.TagOverrides { - if tagName == "" { - v = appendWarningf(v, "tagOverrides.name may not be empty") - } - if to == nil { - v = appendErrorf(v, "tagOverrides may not be null") - continue - } - switch to.Operation { - case telemetry.MetricsOverrides_TagOverride_UPSERT: - if to.Value == "" { - v = appendErrorf(v, "tagOverrides.value must be set set when operation is UPSERT") - } - case telemetry.MetricsOverrides_TagOverride_REMOVE: - if to.Value != "" { - v = appendErrorf(v, "tagOverrides.value may only be set when operation is UPSERT") - } - } - } - if o.Match != nil { - switch mm := o.Match.MetricMatch.(type) { - case *telemetry.MetricSelector_CustomMetric: - if mm.CustomMetric == "" { - v = appendErrorf(v, "customMetric may not be empty") - } - } - } - } - } - return -} - -func validateTelemetryProviders(providers []*telemetry.ProviderRef) error { - for _, p := range providers { - if p == nil || p.Name == "" { - return fmt.Errorf("providers.name may not be empty") - } - } - return nil -} - -// ValidateWasmPlugin validates a WasmPlugin. -var ValidateWasmPlugin = registerValidateFunc("ValidateWasmPlugin", - func(cfg config.Config) (Warning, error) { - spec, ok := cfg.Spec.(*extensions.WasmPlugin) - if !ok { - return nil, fmt.Errorf("cannot cast to wasmplugin") - } - - errs := Validation{} - errs = appendValidation(errs, - validateWorkloadSelector(spec.Selector), - validateWasmPluginURL(spec.Url), - validateWasmPluginSHA(spec), - validateWasmPluginVMConfig(spec.VmConfig), - ) - return errs.Unwrap() - }) - -func validateWasmPluginURL(pluginURL string) error { - if pluginURL == "" { - return fmt.Errorf("url field needs to be set") - } - validSchemes := map[string]bool{ - "": true, "file": true, "http": true, "https": true, "oci": true, - } - - u, err := url.Parse(pluginURL) - if err != nil { - return fmt.Errorf("failed to parse url: %s", err) - } - if _, found := validSchemes[u.Scheme]; !found { - return fmt.Errorf("url contains unsupported scheme: %s", u.Scheme) - } - return nil -} - -func validateWasmPluginSHA(plugin *extensions.WasmPlugin) error { - if plugin.Sha256 == "" { - return nil - } - if len(plugin.Sha256) != 64 { - return fmt.Errorf("sha256 field must be 64 characters long") - } - for _, r := range plugin.Sha256 { - if !('a' <= r && r <= 'f' || '0' <= r && r <= '9') { - return fmt.Errorf("sha256 field must match [a-f0-9]{64} pattern") - } - } - return nil -} - -func validateWasmPluginVMConfig(vm *extensions.VmConfig) error { - if vm == nil || len(vm.Env) == 0 { - return nil - } - - keys := sets.New() - for _, env := range vm.Env { - if env == nil { - continue - } - - if env.Name == "" { - return fmt.Errorf("spec.vmConfig.env invalid") - } - - if keys.Contains(env.Name) { - return fmt.Errorf("duplicate env") - } - keys.Insert(env.Name) - } - - return nil -} diff --git a/pkg/config/validation/validation_agent.go b/pkg/config/validation/validation_agent.go deleted file mode 100644 index 5b35d1613..000000000 --- a/pkg/config/validation/validation_agent.go +++ /dev/null @@ -1,27 +0,0 @@ -//go:build agent -// +build agent - -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validation - -import ( - telemetry "istio.io/api/telemetry/v1alpha1" -) - -// NOP validation that isolated `go-cel` package for istio-agent binary -func validateTelemetryFilter(filter *telemetry.AccessLogging_Filter) error { - return nil -} diff --git a/pkg/config/validation/validation_istiod.go b/pkg/config/validation/validation_istiod.go deleted file mode 100644 index e7801a7c1..000000000 --- a/pkg/config/validation/validation_istiod.go +++ /dev/null @@ -1,38 +0,0 @@ -//go:build !agent -// +build !agent - -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validation - -import ( - "fmt" -) - -import ( - "github.com/google/cel-go/cel" - telemetry "istio.io/api/telemetry/v1alpha1" -) - -func validateTelemetryFilter(filter *telemetry.AccessLogging_Filter) error { - expr := filter.Expression - env, _ := cel.NewEnv() - _, issue := env.Parse(expr) - if issue.Err() != nil { - return fmt.Errorf("must be a valid CEL expression, %w", issue.Err()) - } - - return nil -} diff --git a/pkg/config/validation/validation_test.go b/pkg/config/validation/validation_test.go deleted file mode 100644 index deaa6b2ee..000000000 --- a/pkg/config/validation/validation_test.go +++ /dev/null @@ -1,7326 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validation - -import ( - "strings" - "testing" - "time" -) - -import ( - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" - "github.com/hashicorp/go-multierror" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/durationpb" - "google.golang.org/protobuf/types/known/structpb" - "google.golang.org/protobuf/types/known/wrapperspb" - extensions "istio.io/api/extensions/v1alpha1" - meshconfig "istio.io/api/mesh/v1alpha1" - networking "istio.io/api/networking/v1alpha3" - networkingv1beta1 "istio.io/api/networking/v1beta1" - security_beta "istio.io/api/security/v1beta1" - telemetry "istio.io/api/telemetry/v1alpha1" - api "istio.io/api/type/v1beta1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -const ( - // Config name for testing - someName = "foo" - // Config namespace for testing. - someNamespace = "bar" -) - -func TestValidateFQDN(t *testing.T) { - tests := []struct { - fqdn string - valid bool - name string - }{ - { - fqdn: strings.Repeat("x", 256), - valid: false, - name: "long FQDN", - }, - { - fqdn: "", - valid: false, - name: "empty FQDN", - }, - { - fqdn: "istio.io", - valid: true, - name: "standard FQDN", - }, - { - fqdn: "istio.io.", - valid: true, - name: "unambiguous FQDN", - }, - { - fqdn: "istio-pilot.dubbo-system.svc.cluster.local", - valid: true, - name: "standard kubernetes FQDN", - }, - { - fqdn: "istio-pilot.dubbo-system.svc.cluster.local.", - valid: true, - name: "unambiguous kubernetes FQDN", - }, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - err := ValidateFQDN(tt.fqdn) - valid := err == nil - if valid != tt.valid { - t.Errorf("Expected valid=%v, got valid=%v for %v", tt.valid, valid, tt.fqdn) - } - }) - - } -} - -func TestValidateWildcardDomain(t *testing.T) { - tests := []struct { - name string - in string - out string - }{ - {"empty", "", "empty"}, - {"too long", strings.Repeat("x", 256), "too long"}, - {"happy", strings.Repeat("x", 63), ""}, - {"wildcard", "*", ""}, - {"wildcard multi-segment", "*.bar.com", ""}, - {"wildcard single segment", "*foo", ""}, - {"wildcard prefix", "*foo.bar.com", ""}, - {"wildcard prefix dash", "*-foo.bar.com", ""}, - {"bad wildcard", "foo.*.com", "invalid"}, - {"bad wildcard", "foo*.bar.com", "invalid"}, - {"IP address", "1.1.1.1", "invalid"}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := ValidateWildcardDomain(tt.in) - if err == nil && tt.out != "" { - t.Fatalf("ValidateWildcardDomain(%v) = nil, wanted %q", tt.in, tt.out) - } else if err != nil && tt.out == "" { - t.Fatalf("ValidateWildcardDomain(%v) = %v, wanted nil", tt.in, err) - } else if err != nil && !strings.Contains(err.Error(), tt.out) { - t.Fatalf("ValidateWildcardDomain(%v) = %v, wanted %q", tt.in, err, tt.out) - } - }) - } -} - -func TestValidateTrustDomain(t *testing.T) { - tests := []struct { - name string - in string - err string - }{ - {"empty", "", "empty"}, - {"happy", strings.Repeat("x", 63), ""}, - {"multi-segment", "foo.bar.com", ""}, - {"middle dash", "f-oo.bar.com", ""}, - {"trailing dot", "foo.bar.com.", ""}, - {"prefix dash", "-foo.bar.com", "invalid"}, - {"forward slash separated", "foo/bar/com", "invalid"}, - {"colon separated", "foo:bar:com", "invalid"}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := ValidateTrustDomain(tt.in) - if err == nil && tt.err != "" { - t.Fatalf("ValidateTrustDomain(%v) = nil, wanted %q", tt.in, tt.err) - } else if err != nil && tt.err == "" { - t.Fatalf("ValidateTrustDomain(%v) = %v, wanted nil", tt.in, err) - } else if err != nil && !strings.Contains(err.Error(), tt.err) { - t.Fatalf("ValidateTrustDomain(%v) = %v, wanted %q", tt.in, err, tt.err) - } - }) - } -} - -func TestValidatePort(t *testing.T) { - ports := map[int]bool{ - 0: false, - 65536: false, - -1: false, - 100: true, - 1000: true, - 65535: true, - } - for port, valid := range ports { - if got := ValidatePort(port); (got == nil) != valid { - t.Errorf("Failed: got valid=%t but wanted valid=%t: %v for %d", got == nil, valid, got, port) - } - } -} - -func TestValidateControlPlaneAuthPolicy(t *testing.T) { - cases := []struct { - name string - policy meshconfig.AuthenticationPolicy - isValid bool - }{ - { - name: "invalid policy", - policy: -1, - isValid: false, - }, - { - name: "valid policy", - policy: 0, - isValid: true, - }, - { - name: "valid policy", - policy: 1, - isValid: true, - }, - { - name: "invalid policy", - policy: 2, - isValid: false, - }, - { - name: "invalid policy", - policy: 100, - isValid: false, - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - if got := ValidateControlPlaneAuthPolicy(c.policy); (got == nil) != c.isValid { - t.Errorf("got valid=%v but wanted valid=%v: %v", got == nil, c.isValid, got) - } - }) - } -} - -func TestValidateProxyAddress(t *testing.T) { - addresses := map[string]bool{ - "istio-pilot:80": true, - "istio-pilot": false, - "isti..:80": false, - "10.0.0.100:9090": true, - "10.0.0.100": false, - "istio-pilot:port": false, - "istio-pilot:100000": false, - "[2001:db8::100]:80": true, - "[2001:db8::10::20]:80": false, - "[2001:db8::100]": false, - "[2001:db8::100]:port": false, - "2001:db8::100:80": false, - } - for addr, valid := range addresses { - if got := ValidateProxyAddress(addr); (got == nil) != valid { - t.Errorf("Failed: got valid=%t but wanted valid=%t: %v for %s", got == nil, valid, got, addr) - } - } -} - -func TestValidateDuration(t *testing.T) { - type durationCheck struct { - duration *durationpb.Duration - isValid bool - } - - checks := []durationCheck{ - { - duration: &durationpb.Duration{Seconds: 1}, - isValid: true, - }, - { - duration: &durationpb.Duration{Seconds: 1, Nanos: -1}, - isValid: false, - }, - { - duration: &durationpb.Duration{Seconds: -11, Nanos: -1}, - isValid: false, - }, - { - duration: &durationpb.Duration{Nanos: 1}, - isValid: false, - }, - { - duration: &durationpb.Duration{Seconds: 1, Nanos: 1}, - isValid: false, - }, - } - - for _, check := range checks { - if got := ValidateDuration(check.duration); (got == nil) != check.isValid { - t.Errorf("Failed: got valid=%t but wanted valid=%t: %v for %v", got == nil, check.isValid, got, check.duration) - } - } -} - -func TestValidateParentAndDrain(t *testing.T) { - type ParentDrainTime struct { - Parent *durationpb.Duration - Drain *durationpb.Duration - Valid bool - } - - combinations := []ParentDrainTime{ - { - Parent: &durationpb.Duration{Seconds: 2}, - Drain: &durationpb.Duration{Seconds: 1}, - Valid: true, - }, - { - Parent: &durationpb.Duration{Seconds: 1}, - Drain: &durationpb.Duration{Seconds: 1}, - Valid: false, - }, - { - Parent: &durationpb.Duration{Seconds: 1}, - Drain: &durationpb.Duration{Seconds: 2}, - Valid: false, - }, - { - Parent: &durationpb.Duration{Seconds: 2}, - Drain: &durationpb.Duration{Seconds: 1, Nanos: 1000000}, - Valid: false, - }, - { - Parent: &durationpb.Duration{Seconds: 2, Nanos: 1000000}, - Drain: &durationpb.Duration{Seconds: 1}, - Valid: false, - }, - { - Parent: &durationpb.Duration{Seconds: -2}, - Drain: &durationpb.Duration{Seconds: 1}, - Valid: false, - }, - { - Parent: &durationpb.Duration{Seconds: 2}, - Drain: &durationpb.Duration{Seconds: -1}, - Valid: false, - }, - { - Parent: &durationpb.Duration{Seconds: 1 + int64(time.Hour/time.Second)}, - Drain: &durationpb.Duration{Seconds: 10}, - Valid: false, - }, - { - Parent: &durationpb.Duration{Seconds: 10}, - Drain: &durationpb.Duration{Seconds: 1 + int64(time.Hour/time.Second)}, - Valid: false, - }, - } - for _, combo := range combinations { - if got := ValidateParentAndDrain(combo.Drain, combo.Parent); (got == nil) != combo.Valid { - t.Errorf("Failed: got valid=%t but wanted valid=%t: %v for Parent:%v Drain:%v", - got == nil, combo.Valid, got, combo.Parent, combo.Drain) - } - } -} - -func TestValidateConnectTimeout(t *testing.T) { - type durationCheck struct { - duration *durationpb.Duration - isValid bool - } - - checks := []durationCheck{ - { - duration: &durationpb.Duration{Seconds: 1}, - isValid: true, - }, - { - duration: &durationpb.Duration{Seconds: 31}, - isValid: false, - }, - { - duration: &durationpb.Duration{Nanos: 99999}, - isValid: false, - }, - } - - for _, check := range checks { - if got := ValidateConnectTimeout(check.duration); (got == nil) != check.isValid { - t.Errorf("Failed: got valid=%t but wanted valid=%t: %v for %v", got == nil, check.isValid, got, check.duration) - } - } -} - -func TestValidateMaxServerConnectionAge(t *testing.T) { - type durationCheck struct { - duration time.Duration - isValid bool - } - durMin, _ := time.ParseDuration("-30m") - durHr, _ := time.ParseDuration("-1.5h") - checks := []durationCheck{ - { - duration: 30 * time.Minute, - isValid: true, - }, - { - duration: durMin, - isValid: false, - }, - { - duration: durHr, - isValid: false, - }, - } - - for _, check := range checks { - if got := ValidateMaxServerConnectionAge(check.duration); (got == nil) != check.isValid { - t.Errorf("Failed: got valid=%t but wanted valid=%t: %v for %v", got == nil, check.isValid, got, check.duration) - } - } -} - -func TestValidateProtocolDetectionTimeout(t *testing.T) { - type durationCheck struct { - duration *durationpb.Duration - isValid bool - } - - checks := []durationCheck{ - { - duration: &durationpb.Duration{Seconds: 1}, - isValid: true, - }, - { - duration: &durationpb.Duration{Nanos: 99999}, - isValid: false, - }, - { - duration: &durationpb.Duration{Nanos: 0}, - isValid: true, - }, - } - - for _, check := range checks { - if got := ValidateProtocolDetectionTimeout(check.duration); (got == nil) != check.isValid { - t.Errorf("Failed: got valid=%t but wanted valid=%t: %v for %v", got == nil, check.isValid, got, check.duration) - } - } -} - -func TestValidateMeshConfig(t *testing.T) { - if ValidateMeshConfig(&meshconfig.MeshConfig{}) == nil { - t.Error("expected an error on an empty mesh config") - } - - invalid := &meshconfig.MeshConfig{ - ProxyListenPort: 0, - ConnectTimeout: durationpb.New(-1 * time.Second), - DefaultConfig: &meshconfig.ProxyConfig{}, - TrustDomain: "", - TrustDomainAliases: []string{"a.$b", "a/b", ""}, - ExtensionProviders: []*meshconfig.MeshConfig_ExtensionProvider{ - { - Name: "default", - Provider: &meshconfig.MeshConfig_ExtensionProvider_EnvoyExtAuthzHttp{ - EnvoyExtAuthzHttp: &meshconfig.MeshConfig_ExtensionProvider_EnvoyExternalAuthorizationHttpProvider{ - Service: "foo/ext-authz", - Port: 999999, - }, - }, - }, - }, - } - - err := ValidateMeshConfig(invalid) - if err == nil { - t.Errorf("expected an error on invalid proxy mesh config: %v", invalid) - } else { - wantErrors := []string{ - "invalid proxy listen port", - "invalid connect timeout", - "config path must be set", - "binary path must be set", - "oneof service cluster or tracing service name must be specified", - "invalid parent and drain time combination invalid drain duration", - "invalid parent and drain time combination invalid parent shutdown duration", - "discovery address must be set to the proxy discovery service", - "invalid proxy admin port", - "invalid status port", - "trustDomain: empty domain name not allowed", - "trustDomainAliases[0]", - "trustDomainAliases[1]", - "trustDomainAliases[2]", - } - switch err := err.(type) { - case *multierror.Error: - // each field must cause an error in the field - if len(err.Errors) != len(wantErrors) { - t.Errorf("expected %d errors but found %v", len(wantErrors), err) - } else { - for i := 0; i < len(wantErrors); i++ { - if !strings.HasPrefix(err.Errors[i].Error(), wantErrors[i]) { - t.Errorf("expected error %q at index %d but found %q", wantErrors[i], i, err.Errors[i]) - } - } - } - default: - t.Errorf("expected a multi error as output") - } - } -} - -func TestValidateMeshConfigProxyConfig(t *testing.T) { - valid := &meshconfig.ProxyConfig{ - ConfigPath: "/etc/istio/proxy", - BinaryPath: "/usr/local/bin/envoy", - DiscoveryAddress: "istio-pilot.dubbo-system:15010", - ProxyAdminPort: 15000, - DrainDuration: durationpb.New(45 * time.Second), - ParentShutdownDuration: durationpb.New(60 * time.Second), - ClusterName: &meshconfig.ProxyConfig_ServiceCluster{ServiceCluster: "istio-proxy"}, - StatsdUdpAddress: "istio-statsd-prom-bridge.dubbo-system:9125", - EnvoyMetricsService: &meshconfig.RemoteService{Address: "metrics-service.dubbo-system:15000"}, - EnvoyAccessLogService: &meshconfig.RemoteService{Address: "accesslog-service.dubbo-system:15000"}, - ControlPlaneAuthPolicy: meshconfig.AuthenticationPolicy_MUTUAL_TLS, - Tracing: nil, - StatusPort: 15020, - PrivateKeyProvider: nil, - } - - modify := func(config *meshconfig.ProxyConfig, fieldSetter func(*meshconfig.ProxyConfig)) *meshconfig.ProxyConfig { - clone := proto.Clone(config).(*meshconfig.ProxyConfig) - fieldSetter(clone) - return clone - } - - cases := []struct { - name string - in *meshconfig.ProxyConfig - isValid bool - }{ - { - name: "empty proxy config", - in: &meshconfig.ProxyConfig{}, - isValid: false, - }, - { - name: "valid proxy config", - in: valid, - isValid: true, - }, - { - name: "config path invalid", - in: modify(valid, func(c *meshconfig.ProxyConfig) { c.ConfigPath = "" }), - isValid: false, - }, - { - name: "binary path invalid", - in: modify(valid, func(c *meshconfig.ProxyConfig) { c.BinaryPath = "" }), - isValid: false, - }, - { - name: "discovery address invalid", - in: modify(valid, func(c *meshconfig.ProxyConfig) { c.DiscoveryAddress = "10.0.0.100" }), - isValid: false, - }, - { - name: "proxy admin port invalid", - in: modify(valid, func(c *meshconfig.ProxyConfig) { c.ProxyAdminPort = 0 }), - isValid: false, - }, - { - name: "proxy admin port invalid", - in: modify(valid, func(c *meshconfig.ProxyConfig) { c.ProxyAdminPort = 65536 }), - isValid: false, - }, - { - name: "validate status port", - in: modify(valid, func(c *meshconfig.ProxyConfig) { c.StatusPort = 0 }), - isValid: false, - }, - { - name: "validate vstatus port", - in: modify(valid, func(c *meshconfig.ProxyConfig) { c.StatusPort = 65536 }), - isValid: false, - }, - { - name: "drain duration invalid", - in: modify(valid, func(c *meshconfig.ProxyConfig) { c.DrainDuration = durationpb.New(-1 * time.Second) }), - isValid: false, - }, - { - name: "parent shutdown duration invalid", - in: modify(valid, func(c *meshconfig.ProxyConfig) { c.ParentShutdownDuration = durationpb.New(-1 * time.Second) }), - isValid: false, - }, - { - name: "service cluster invalid", - in: modify(valid, func(c *meshconfig.ProxyConfig) { - c.ClusterName = &meshconfig.ProxyConfig_ServiceCluster{ServiceCluster: ""} - }), - isValid: false, - }, - { - name: "statsd udp address invalid", - in: modify(valid, func(c *meshconfig.ProxyConfig) { c.StatsdUdpAddress = "10.0.0.100" }), - isValid: false, - }, - { - name: "envoy metrics service address invalid", - in: modify(valid, func(c *meshconfig.ProxyConfig) { - c.EnvoyMetricsService = &meshconfig.RemoteService{Address: "metrics-service.dubbo-system"} - }), - isValid: false, - }, - { - name: "envoy access log service address invalid", - in: modify(valid, func(c *meshconfig.ProxyConfig) { - c.EnvoyAccessLogService = &meshconfig.RemoteService{Address: "accesslog-service.dubbo-system"} - }), - isValid: false, - }, - { - name: "control plane auth policy invalid", - in: modify(valid, func(c *meshconfig.ProxyConfig) { c.ControlPlaneAuthPolicy = -1 }), - isValid: false, - }, - { - name: "zipkin address is valid", - in: modify(valid, - func(c *meshconfig.ProxyConfig) { - c.Tracing = &meshconfig.Tracing{ - Tracer: &meshconfig.Tracing_Zipkin_{ - Zipkin: &meshconfig.Tracing_Zipkin{ - Address: "zipkin.dubbo-system:9411", - }, - }, - } - }, - ), - isValid: true, - }, - { - name: "zipkin address with $(HOST_IP) is valid", - in: modify(valid, - func(c *meshconfig.ProxyConfig) { - c.Tracing = &meshconfig.Tracing{ - Tracer: &meshconfig.Tracing_Zipkin_{ - Zipkin: &meshconfig.Tracing_Zipkin{ - Address: "$(HOST_IP):9411", - }, - }, - } - }, - ), - isValid: true, - }, - { - name: "zipkin config invalid", - in: modify(valid, - func(c *meshconfig.ProxyConfig) { - c.Tracing = &meshconfig.Tracing{ - Tracer: &meshconfig.Tracing_Zipkin_{ - Zipkin: &meshconfig.Tracing_Zipkin{ - Address: "10.0.0.100", - }, - }, - } - }, - ), - isValid: false, - }, - { - name: "lightstep config is valid", - in: modify(valid, - func(c *meshconfig.ProxyConfig) { - c.Tracing = &meshconfig.Tracing{ - Tracer: &meshconfig.Tracing_Lightstep_{ - Lightstep: &meshconfig.Tracing_Lightstep{ - Address: "collector.lightstep:8080", - AccessToken: "abcdefg1234567", - }, - }, - } - }, - ), - isValid: true, - }, - { - name: "lightstep address invalid", - in: modify(valid, - func(c *meshconfig.ProxyConfig) { - c.Tracing = &meshconfig.Tracing{ - Tracer: &meshconfig.Tracing_Lightstep_{ - Lightstep: &meshconfig.Tracing_Lightstep{ - Address: "10.0.0.100", - AccessToken: "abcdefg1234567", - }, - }, - } - }, - ), - isValid: false, - }, - { - name: "lightstep address empty but lightstep access token is not", - in: modify(valid, - func(c *meshconfig.ProxyConfig) { - c.Tracing = &meshconfig.Tracing{ - Tracer: &meshconfig.Tracing_Lightstep_{ - Lightstep: &meshconfig.Tracing_Lightstep{ - Address: "", - AccessToken: "abcdefg1234567", - }, - }, - } - }, - ), - isValid: false, - }, - { - name: "lightstep address is valid but access token is empty", - in: modify(valid, - func(c *meshconfig.ProxyConfig) { - c.Tracing = &meshconfig.Tracing{ - Tracer: &meshconfig.Tracing_Lightstep_{ - Lightstep: &meshconfig.Tracing_Lightstep{ - Address: "collector.lightstep:8080", - AccessToken: "", - }, - }, - } - }, - ), - isValid: false, - }, - { - name: "lightstep access token empty but lightstep address is not", - in: modify(valid, - func(c *meshconfig.ProxyConfig) { - c.Tracing = &meshconfig.Tracing{ - Tracer: &meshconfig.Tracing_Lightstep_{ - Lightstep: &meshconfig.Tracing_Lightstep{ - Address: "10.0.0.100", - AccessToken: "", - }, - }, - } - }, - ), - isValid: false, - }, - { - name: "lightstep address and lightstep token both empty", - in: modify(valid, - func(c *meshconfig.ProxyConfig) { - c.Tracing = &meshconfig.Tracing{ - Tracer: &meshconfig.Tracing_Lightstep_{ - Lightstep: &meshconfig.Tracing_Lightstep{ - Address: "", - AccessToken: "", - }, - }, - } - }, - ), - isValid: false, - }, - { - name: "datadog without address", - in: modify(valid, - func(c *meshconfig.ProxyConfig) { - c.Tracing = &meshconfig.Tracing{ - Tracer: &meshconfig.Tracing_Datadog_{ - Datadog: &meshconfig.Tracing_Datadog{}, - }, - } - }, - ), - isValid: false, - }, - { - name: "datadog with correct address", - in: modify(valid, - func(c *meshconfig.ProxyConfig) { - c.Tracing = &meshconfig.Tracing{ - Tracer: &meshconfig.Tracing_Datadog_{ - Datadog: &meshconfig.Tracing_Datadog{ - Address: "datadog-agent:8126", - }, - }, - } - }, - ), - isValid: true, - }, - { - name: "datadog with invalid address", - in: modify(valid, - func(c *meshconfig.ProxyConfig) { - c.Tracing = &meshconfig.Tracing{ - Tracer: &meshconfig.Tracing_Datadog_{ - Datadog: &meshconfig.Tracing_Datadog{ - Address: "address-missing-port-number", - }, - }, - } - }, - ), - isValid: false, - }, - { - name: "custom tags with a literal value", - in: modify(valid, - func(c *meshconfig.ProxyConfig) { - c.Tracing = &meshconfig.Tracing{ - CustomTags: map[string]*meshconfig.Tracing_CustomTag{ - "clusterID": { - Type: &meshconfig.Tracing_CustomTag_Literal{ - Literal: &meshconfig.Tracing_Literal{ - Value: "cluster1", - }, - }, - }, - }, - } - }, - ), - isValid: true, - }, - { - name: "custom tags with a nil value", - in: modify(valid, - func(c *meshconfig.ProxyConfig) { - c.Tracing = &meshconfig.Tracing{ - CustomTags: map[string]*meshconfig.Tracing_CustomTag{ - "clusterID": nil, - }, - } - }, - ), - isValid: false, - }, - { - name: "private key provider with empty provider", - in: modify(valid, - func(c *meshconfig.ProxyConfig) { - c.PrivateKeyProvider = &meshconfig.PrivateKeyProvider{} - }, - ), - isValid: false, - }, - { - name: "private key provider with cryptomb without poll_delay", - in: modify(valid, - func(c *meshconfig.ProxyConfig) { - c.PrivateKeyProvider = &meshconfig.PrivateKeyProvider{ - Provider: &meshconfig.PrivateKeyProvider_Cryptomb{ - Cryptomb: &meshconfig.PrivateKeyProvider_CryptoMb{}, - }, - } - }, - ), - isValid: false, - }, - { - name: "private key provider with cryptomb zero poll_delay", - in: modify(valid, - func(c *meshconfig.ProxyConfig) { - c.PrivateKeyProvider = &meshconfig.PrivateKeyProvider{ - Provider: &meshconfig.PrivateKeyProvider_Cryptomb{ - Cryptomb: &meshconfig.PrivateKeyProvider_CryptoMb{ - PollDelay: &durationpb.Duration{ - Seconds: 0, - Nanos: 0, - }, - }, - }, - } - }, - ), - isValid: false, - }, - { - name: "private key provider with cryptomb", - in: modify(valid, - func(c *meshconfig.ProxyConfig) { - c.PrivateKeyProvider = &meshconfig.PrivateKeyProvider{ - Provider: &meshconfig.PrivateKeyProvider_Cryptomb{ - Cryptomb: &meshconfig.PrivateKeyProvider_CryptoMb{ - PollDelay: &durationpb.Duration{ - Seconds: 0, - Nanos: 10000, - }, - }, - }, - } - }, - ), - isValid: true, - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - if got := ValidateMeshConfigProxyConfig(c.in); (got == nil) != c.isValid { - if c.isValid { - t.Errorf("got error %v, wanted none", got) - } else { - t.Error("got no error, wanted one") - } - } - }) - } - - invalid := &meshconfig.ProxyConfig{ - ConfigPath: "", - BinaryPath: "", - DiscoveryAddress: "10.0.0.100", - ProxyAdminPort: 0, - DrainDuration: durationpb.New(-1 * time.Second), - ParentShutdownDuration: durationpb.New(-1 * time.Second), - ClusterName: &meshconfig.ProxyConfig_ServiceCluster{ServiceCluster: ""}, - StatsdUdpAddress: "10.0.0.100", - EnvoyMetricsService: &meshconfig.RemoteService{Address: "metrics-service"}, - EnvoyAccessLogService: &meshconfig.RemoteService{Address: "accesslog-service"}, - ControlPlaneAuthPolicy: -1, - StatusPort: 0, - Tracing: &meshconfig.Tracing{ - Tracer: &meshconfig.Tracing_Zipkin_{ - Zipkin: &meshconfig.Tracing_Zipkin{ - Address: "10.0.0.100", - }, - }, - }, - } - - err := ValidateMeshConfigProxyConfig(invalid) - if err == nil { - t.Errorf("expected an error on invalid proxy mesh config: %v", invalid) - } else { - switch err := err.(type) { - case *multierror.Error: - // each field must cause an error in the field - if len(err.Errors) != 13 { - t.Errorf("expected an error for each field %v", err) - } - default: - t.Errorf("expected a multi error as output") - } - } -} - -func TestValidateGateway(t *testing.T) { - tests := []struct { - name string - in proto.Message - out string - warning string - }{ - {"empty", &networking.Gateway{}, "server", ""}, - {"invalid message", &networking.Server{}, "cannot cast", ""}, - { - "happy domain", - &networking.Gateway{ - Servers: []*networking.Server{{ - Hosts: []string{"foo.bar.com"}, - Port: &networking.Port{Name: "name1", Number: 7, Protocol: "http"}, - }}, - }, - "", "", - }, - { - "happy multiple servers", - &networking.Gateway{ - Servers: []*networking.Server{ - { - Hosts: []string{"foo.bar.com"}, - Port: &networking.Port{Name: "name1", Number: 7, Protocol: "http"}, - }, - }, - }, - "", "", - }, - { - "invalid port", - &networking.Gateway{ - Servers: []*networking.Server{ - { - Hosts: []string{"foo.bar.com"}, - Port: &networking.Port{Name: "name1", Number: 66000, Protocol: "http"}, - }, - }, - }, - "port", "", - }, - { - "duplicate port names", - &networking.Gateway{ - Servers: []*networking.Server{ - { - Hosts: []string{"foo.bar.com"}, - Port: &networking.Port{Name: "foo", Number: 80, Protocol: "http"}, - }, - { - Hosts: []string{"scooby.doo.com"}, - Port: &networking.Port{Name: "foo", Number: 8080, Protocol: "http"}, - }, - }, - }, - "port names", "", - }, - { - "invalid domain", - &networking.Gateway{ - Servers: []*networking.Server{ - { - Hosts: []string{"foo.*.bar.com"}, - Port: &networking.Port{Number: 7, Protocol: "http"}, - }, - }, - }, - "domain", "", - }, - { - "valid httpsRedirect", - &networking.Gateway{ - Servers: []*networking.Server{ - { - Hosts: []string{"bar.com"}, - Port: &networking.Port{Name: "http", Number: 80, Protocol: "http"}, - Tls: &networking.ServerTLSSettings{HttpsRedirect: true}, - }, - }, - }, - "", "", - }, - { - "invalid https httpsRedirect", - &networking.Gateway{ - Servers: []*networking.Server{ - { - Hosts: []string{"bar.com"}, - Port: &networking.Port{Name: "https", Number: 80, Protocol: "https"}, - Tls: &networking.ServerTLSSettings{HttpsRedirect: true}, - }, - }, - }, - "", "tls.httpsRedirect should only be used with http servers", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - warn, err := ValidateGateway(config.Config{ - Meta: config.Meta{ - Name: someName, - Namespace: someNamespace, - }, - Spec: tt.in, - }) - checkValidationMessage(t, warn, err, tt.warning, tt.out) - }) - } -} - -func TestValidateServer(t *testing.T) { - tests := []struct { - name string - in *networking.Server - out string - }{ - {"empty", &networking.Server{}, "host"}, - {"empty", &networking.Server{}, "port"}, - { - "happy", - &networking.Server{ - Hosts: []string{"foo.bar.com"}, - Port: &networking.Port{Number: 7, Name: "http", Protocol: "http"}, - }, - "", - }, - { - "happy ip", - &networking.Server{ - Hosts: []string{"1.1.1.1"}, - Port: &networking.Port{Number: 7, Name: "http", Protocol: "http"}, - }, - "", - }, - { - "happy ns/name", - &networking.Server{ - Hosts: []string{"ns1/foo.bar.com"}, - Port: &networking.Port{Number: 7, Name: "http", Protocol: "http"}, - }, - "", - }, - { - "happy */name", - &networking.Server{ - Hosts: []string{"*/foo.bar.com"}, - Port: &networking.Port{Number: 7, Name: "http", Protocol: "http"}, - }, - "", - }, - { - "happy ./name", - &networking.Server{ - Hosts: []string{"./foo.bar.com"}, - Port: &networking.Port{Number: 7, Name: "http", Protocol: "http"}, - }, - "", - }, - { - "invalid domain ns/name format", - &networking.Server{ - Hosts: []string{"ns1/foo.*.bar.com"}, - Port: &networking.Port{Number: 7, Name: "http", Protocol: "http"}, - }, - "domain", - }, - { - "invalid domain", - &networking.Server{ - Hosts: []string{"foo.*.bar.com"}, - Port: &networking.Port{Number: 7, Name: "http", Protocol: "http"}, - }, - "domain", - }, - { - "invalid short name host", - &networking.Server{ - Hosts: []string{"foo"}, - Port: &networking.Port{Number: 7, Name: "http", Protocol: "http"}, - }, - "short names", - }, - { - "invalid port", - &networking.Server{ - Hosts: []string{"foo.bar.com"}, - Port: &networking.Port{Number: 66000, Name: "http", Protocol: "http"}, - }, - "port", - }, - { - "invalid tls options", - &networking.Server{ - Hosts: []string{"foo.bar.com"}, - Port: &networking.Port{Number: 1, Name: "http", Protocol: "http"}, - Tls: &networking.ServerTLSSettings{Mode: networking.ServerTLSSettings_SIMPLE}, - }, - "TLS", - }, - { - "no tls on HTTPS", - &networking.Server{ - Hosts: []string{"foo.bar.com"}, - Port: &networking.Port{Number: 10000, Name: "https", Protocol: "https"}, - }, - "must have TLS", - }, - { - "tls on HTTP", - &networking.Server{ - Hosts: []string{"foo.bar.com"}, - Port: &networking.Port{Number: 10000, Name: "http", Protocol: "http"}, - Tls: &networking.ServerTLSSettings{Mode: networking.ServerTLSSettings_SIMPLE}, - }, - "cannot have TLS", - }, - { - "tls redirect on HTTP", - &networking.Server{ - Hosts: []string{"foo.bar.com"}, - Port: &networking.Port{Number: 10000, Name: "http", Protocol: "http"}, - Tls: &networking.ServerTLSSettings{ - HttpsRedirect: true, - }, - }, - "", - }, - { - "bind ip", - &networking.Server{ - Hosts: []string{"foo.bar.com"}, - Port: &networking.Port{Number: 7, Name: "http", Protocol: "http"}, - Bind: "127.0.0.1", - }, - "", - }, - { - "bind unix path with invalid port", - &networking.Server{ - Hosts: []string{"foo.bar.com"}, - Port: &networking.Port{Number: 7, Name: "http", Protocol: "http"}, - Bind: "unix://@foobar", - }, - "port number must be 0 for unix domain socket", - }, - { - "bind unix path", - &networking.Server{ - Hosts: []string{"foo.bar.com"}, - Port: &networking.Port{Number: 0, Name: "http", Protocol: "http"}, - Bind: "unix://@foobar", - }, - "", - }, - { - "bind bad ip", - &networking.Server{ - Hosts: []string{"foo.bar.com"}, - Port: &networking.Port{Number: 0, Name: "http", Protocol: "http"}, - Bind: "foo.bar", - }, - "foo.bar is not a valid IP", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - v := validateServer(tt.in) - warn, err := v.Unwrap() - checkValidationMessage(t, warn, err, "", tt.out) - }) - } -} - -func TestValidateServerPort(t *testing.T) { - tests := []struct { - name string - in *networking.Port - out string - }{ - {"empty", &networking.Port{}, "invalid protocol"}, - {"empty", &networking.Port{}, "port name"}, - { - "happy", - &networking.Port{ - Protocol: "http", - Number: 1, - Name: "Henry", - }, - "", - }, - { - "invalid protocol", - &networking.Port{ - Protocol: "kafka", - Number: 1, - Name: "Henry", - }, - "invalid protocol", - }, - { - "invalid number", - &networking.Port{ - Protocol: "http", - Number: uint32(1 << 30), - Name: "http", - }, - "port number", - }, - { - "name, no number", - &networking.Port{ - Protocol: "http", - Number: 0, - Name: "Henry", - }, - "", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := validateServerPort(tt.in) - if err == nil && tt.out != "" { - t.Fatalf("validateServerPort(%v) = nil, wanted %q", tt.in, tt.out) - } else if err != nil && tt.out == "" { - t.Fatalf("validateServerPort(%v) = %v, wanted nil", tt.in, err) - } else if err != nil && !strings.Contains(err.Error(), tt.out) { - t.Fatalf("validateServerPort(%v) = %v, wanted %q", tt.in, err, tt.out) - } - }) - } -} - -func TestValidateTlsOptions(t *testing.T) { - tests := []struct { - name string - in *networking.ServerTLSSettings - out string - warning string - }{ - {"empty", &networking.ServerTLSSettings{}, "", ""}, - { - "simple", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - ServerCertificate: "Captain Jean-Luc Picard", - PrivateKey: "Khan Noonien Singh", - }, - "", "", - }, - { - "simple with client bundle", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - ServerCertificate: "Captain Jean-Luc Picard", - PrivateKey: "Khan Noonien Singh", - CaCertificates: "Commander William T. Riker", - }, - "", "", - }, - { - "simple sds with client bundle", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - ServerCertificate: "Captain Jean-Luc Picard", - PrivateKey: "Khan Noonien Singh", - CaCertificates: "Commander William T. Riker", - CredentialName: "sds-name", - }, - "", "", - }, - { - "simple no server cert", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - ServerCertificate: "", - PrivateKey: "Khan Noonien Singh", - }, - "server certificate", "", - }, - { - "simple no private key", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - ServerCertificate: "Captain Jean-Luc Picard", - PrivateKey: "", - }, - "private key", "", - }, - { - "simple sds no server cert", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - ServerCertificate: "", - PrivateKey: "Khan Noonien Singh", - CredentialName: "sds-name", - }, - "", "", - }, - { - "simple sds no private key", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - ServerCertificate: "Captain Jean-Luc Picard", - PrivateKey: "", - CredentialName: "sds-name", - }, - "", "", - }, - { - "mutual", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_MUTUAL, - ServerCertificate: "Captain Jean-Luc Picard", - PrivateKey: "Khan Noonien Singh", - CaCertificates: "Commander William T. Riker", - }, - "", "", - }, - { - "mutual sds", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_MUTUAL, - ServerCertificate: "Captain Jean-Luc Picard", - PrivateKey: "Khan Noonien Singh", - CaCertificates: "Commander William T. Riker", - CredentialName: "sds-name", - }, - "", "", - }, - { - "mutual no server cert", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_MUTUAL, - ServerCertificate: "", - PrivateKey: "Khan Noonien Singh", - CaCertificates: "Commander William T. Riker", - }, - "server certificate", "", - }, - { - "mutual sds no server cert", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_MUTUAL, - ServerCertificate: "", - PrivateKey: "Khan Noonien Singh", - CaCertificates: "Commander William T. Riker", - CredentialName: "sds-name", - }, - "", "", - }, - { - "mutual no client CA bundle", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_MUTUAL, - ServerCertificate: "Captain Jean-Luc Picard", - PrivateKey: "Khan Noonien Singh", - CaCertificates: "", - }, - "client CA bundle", "", - }, - // this pair asserts we get errors about both client and server certs missing when in mutual mode - // and both are absent, but requires less rewriting of the testing harness than merging the cases - { - "mutual no certs", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_MUTUAL, - ServerCertificate: "", - PrivateKey: "", - CaCertificates: "", - }, - "server certificate", "", - }, - { - "mutual no certs", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_MUTUAL, - ServerCertificate: "", - PrivateKey: "", - CaCertificates: "", - }, - "private key", "", - }, - { - "mutual no certs", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_MUTUAL, - ServerCertificate: "", - PrivateKey: "", - CaCertificates: "", - }, - "client CA bundle", "", - }, - { - "pass through sds no certs", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_PASSTHROUGH, - ServerCertificate: "", - CaCertificates: "", - CredentialName: "sds-name", - }, - "", "PASSTHROUGH mode does not use certificates", - }, - { - "istio_mutual no certs", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, - ServerCertificate: "", - PrivateKey: "", - CaCertificates: "", - }, - "", "", - }, - { - "istio_mutual with server cert", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, - ServerCertificate: "Captain Jean-Luc Picard", - }, - "cannot have associated server cert", "", - }, - { - "istio_mutual with client bundle", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, - ServerCertificate: "Captain Jean-Luc Picard", - PrivateKey: "Khan Noonien Singh", - CaCertificates: "Commander William T. Riker", - }, - "cannot have associated", "", - }, - { - "istio_mutual with private key", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, - PrivateKey: "Khan Noonien Singh", - }, - "cannot have associated private key", "", - }, - { - "istio_mutual with credential name", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, - CredentialName: "some-cred", - }, - "cannot have associated credentialName", "", - }, - { - "invalid cipher suites", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - CredentialName: "sds-name", - CipherSuites: []string{"not-a-cipher-suite"}, - }, - "", "not-a-cipher-suite", - }, - { - "valid cipher suites", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - CredentialName: "sds-name", - CipherSuites: []string{"ECDHE-ECDSA-AES128-SHA"}, - }, - "", "", - }, - { - "cipher suites operations", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - CredentialName: "sds-name", - CipherSuites: []string{"-ECDHE-ECDSA-AES128-SHA"}, - }, - "", "", - }, - { - "duplicate cipher suites", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - CredentialName: "sds-name", - CipherSuites: []string{"ECDHE-ECDSA-AES128-SHA", "ECDHE-ECDSA-AES128-SHA"}, - }, - "", "ECDHE-ECDSA-AES128-SHA", - }, - { - "invalid cipher suites with invalid config", - &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - CipherSuites: []string{"not-a-cipher-suite"}, - }, - "requires a private key", "not-a-cipher-suite", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - v := validateTLSOptions(tt.in) - warn, err := v.Unwrap() - checkValidationMessage(t, warn, err, tt.warning, tt.out) - }) - } -} - -func TestValidateTLS(t *testing.T) { - testCases := []struct { - name string - tls *networking.ClientTLSSettings - valid bool - }{ - { - name: "SIMPLE: Credential Name set correctly", - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - CredentialName: "some credential", - ClientCertificate: "", - PrivateKey: "", - CaCertificates: "", - }, - valid: true, - }, - { - name: "SIMPLE CredentialName set with ClientCertificate specified", - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - CredentialName: "credential", - ClientCertificate: "cert", - PrivateKey: "", - CaCertificates: "", - }, - valid: false, - }, - { - name: "SIMPLE: CredentialName set with PrivateKey specified", - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - CredentialName: "credential", - ClientCertificate: "", - PrivateKey: "key", - CaCertificates: "", - }, - valid: false, - }, - { - name: "SIMPLE: CredentialName set with CACertficiates specified", - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - CredentialName: "credential", - ClientCertificate: "", - PrivateKey: "", - CaCertificates: "ca", - }, - valid: false, - }, - { - name: "MUTUAL: Credential Name set correctly", - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - CredentialName: "some credential", - ClientCertificate: "", - PrivateKey: "", - CaCertificates: "", - }, - valid: true, - }, - { - name: "MUTUAL CredentialName set with ClientCertificate specified", - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - CredentialName: "credential", - ClientCertificate: "cert", - PrivateKey: "", - CaCertificates: "", - }, - valid: false, - }, - { - name: "MUTUAL: CredentialName set with PrivateKey specified", - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - CredentialName: "credential", - ClientCertificate: "", - PrivateKey: "key", - CaCertificates: "", - }, - valid: false, - }, - { - name: "MUTUAL: CredentialName set with CACertficiates specified", - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - CredentialName: "credential", - ClientCertificate: "", - PrivateKey: "", - CaCertificates: "ca", - }, - valid: false, - }, - { - name: "MUTUAL: CredentialName not set with ClientCertificate and Key specified", - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - ClientCertificate: "cert", - PrivateKey: "key", - }, - valid: true, - }, - { - name: "MUTUAL: CredentialName not set with ClientCertificate specified and Key missing", - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - ClientCertificate: "cert", - PrivateKey: "", - }, - valid: false, - }, - { - name: "MUTUAL: CredentialName not set with ClientCertificate missing and Key specified", - tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_MUTUAL, - ClientCertificate: "", - PrivateKey: "key", - }, - valid: false, - }, - } - - for _, tc := range testCases { - if got := validateTLS(tc.tls); (got == nil) != tc.valid { - t.Errorf("ValidateTLS(%q) => got valid=%v, want valid=%v", - tc.name, got == nil, tc.valid) - } - } -} - -func TestValidateHTTPHeaderName(t *testing.T) { - testCases := []struct { - name string - valid bool - }{ - {name: "header1", valid: true}, - {name: "X-Requested-With", valid: true}, - {name: "", valid: false}, - } - - for _, tc := range testCases { - if got := ValidateHTTPHeaderName(tc.name); (got == nil) != tc.valid { - t.Errorf("ValidateHTTPHeaderName(%q) => got valid=%v, want valid=%v", - tc.name, got == nil, tc.valid) - } - } -} - -func TestValidateCORSPolicy(t *testing.T) { - testCases := []struct { - name string - in *networking.CorsPolicy - valid bool - }{ - {name: "valid", in: &networking.CorsPolicy{ - AllowMethods: []string{"GET", "POST"}, - AllowHeaders: []string{"header1", "header2"}, - ExposeHeaders: []string{"header3"}, - MaxAge: &durationpb.Duration{Seconds: 2}, - }, valid: true}, - {name: "bad method", in: &networking.CorsPolicy{ - AllowMethods: []string{"GET", "PUTT"}, - AllowHeaders: []string{"header1", "header2"}, - ExposeHeaders: []string{"header3"}, - MaxAge: &durationpb.Duration{Seconds: 2}, - }, valid: false}, - {name: "bad header", in: &networking.CorsPolicy{ - AllowMethods: []string{"GET", "POST"}, - AllowHeaders: []string{"header1", "header2"}, - ExposeHeaders: []string{""}, - MaxAge: &durationpb.Duration{Seconds: 2}, - }, valid: false}, - {name: "bad max age", in: &networking.CorsPolicy{ - AllowMethods: []string{"GET", "POST"}, - AllowHeaders: []string{"header1", "header2"}, - ExposeHeaders: []string{"header3"}, - MaxAge: &durationpb.Duration{Seconds: 2, Nanos: 42}, - }, valid: false}, - {name: "empty matchType AllowOrigins", in: &networking.CorsPolicy{ - AllowOrigins: []*networking.StringMatch{ - {MatchType: &networking.StringMatch_Exact{Exact: ""}}, - {MatchType: &networking.StringMatch_Prefix{Prefix: ""}}, - {MatchType: &networking.StringMatch_Regex{Regex: ""}}, - }, - AllowMethods: []string{"GET", "POST"}, - AllowHeaders: []string{"header1", "header2"}, - ExposeHeaders: []string{"header3"}, - MaxAge: &durationpb.Duration{Seconds: 2}, - }, valid: false}, - {name: "non empty matchType AllowOrigins", in: &networking.CorsPolicy{ - AllowOrigins: []*networking.StringMatch{ - {MatchType: &networking.StringMatch_Exact{Exact: "exact"}}, - {MatchType: &networking.StringMatch_Prefix{Prefix: "prefix"}}, - {MatchType: &networking.StringMatch_Regex{Regex: "regex"}}, - }, - AllowMethods: []string{"GET", "POST"}, - AllowHeaders: []string{"header1", "header2"}, - ExposeHeaders: []string{"header3"}, - MaxAge: &durationpb.Duration{Seconds: 2}, - }, valid: true}, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - if got := validateCORSPolicy(tc.in); (got == nil) != tc.valid { - t.Errorf("got valid=%v, want valid=%v: %v", - got == nil, tc.valid, got) - } - }) - } -} - -func TestValidateHTTPStatus(t *testing.T) { - testCases := []struct { - in int32 - valid bool - }{ - {-100, false}, - {0, false}, - {200, true}, - {600, true}, - {601, false}, - } - - for _, tc := range testCases { - if got := validateHTTPStatus(tc.in); (got == nil) != tc.valid { - t.Errorf("validateHTTPStatus(%d) => got valid=%v, want valid=%v", - tc.in, got, tc.valid) - } - } -} - -func TestValidateHTTPFaultInjectionAbort(t *testing.T) { - testCases := []struct { - name string - in *networking.HTTPFaultInjection_Abort - valid bool - }{ - {name: "nil", in: nil, valid: true}, - {name: "valid", in: &networking.HTTPFaultInjection_Abort{ - Percentage: &networking.Percent{ - Value: 20, - }, - ErrorType: &networking.HTTPFaultInjection_Abort_HttpStatus{ - HttpStatus: 200, - }, - }, valid: true}, - {name: "valid default", in: &networking.HTTPFaultInjection_Abort{ - ErrorType: &networking.HTTPFaultInjection_Abort_HttpStatus{ - HttpStatus: 200, - }, - }, valid: true}, - {name: "invalid http status", in: &networking.HTTPFaultInjection_Abort{ - Percentage: &networking.Percent{ - Value: 20, - }, - ErrorType: &networking.HTTPFaultInjection_Abort_HttpStatus{ - HttpStatus: 9000, - }, - }, valid: false}, - {name: "invalid low http status", in: &networking.HTTPFaultInjection_Abort{ - Percentage: &networking.Percent{ - Value: 20, - }, - ErrorType: &networking.HTTPFaultInjection_Abort_HttpStatus{ - HttpStatus: 100, - }, - }, valid: false}, - {name: "valid percentage", in: &networking.HTTPFaultInjection_Abort{ - Percentage: &networking.Percent{ - Value: 0.001, - }, - ErrorType: &networking.HTTPFaultInjection_Abort_HttpStatus{ - HttpStatus: 200, - }, - }, valid: true}, - {name: "invalid fractional percent", in: &networking.HTTPFaultInjection_Abort{ - Percentage: &networking.Percent{ - Value: -10.0, - }, - ErrorType: &networking.HTTPFaultInjection_Abort_HttpStatus{ - HttpStatus: 200, - }, - }, valid: false}, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - if got := validateHTTPFaultInjectionAbort(tc.in); (got == nil) != tc.valid { - t.Errorf("got valid=%v, want valid=%v: %v", - got == nil, tc.valid, got) - } - }) - } -} - -func TestValidateHTTPFaultInjectionDelay(t *testing.T) { - testCases := []struct { - name string - in *networking.HTTPFaultInjection_Delay - valid bool - }{ - {name: "nil", in: nil, valid: true}, - {name: "valid fixed", in: &networking.HTTPFaultInjection_Delay{ - Percentage: &networking.Percent{ - Value: 20, - }, - HttpDelayType: &networking.HTTPFaultInjection_Delay_FixedDelay{ - FixedDelay: &durationpb.Duration{Seconds: 3}, - }, - }, valid: true}, - {name: "valid default", in: &networking.HTTPFaultInjection_Delay{ - HttpDelayType: &networking.HTTPFaultInjection_Delay_FixedDelay{ - FixedDelay: &durationpb.Duration{Seconds: 3}, - }, - }, valid: true}, - {name: "invalid percent", in: &networking.HTTPFaultInjection_Delay{ - Percentage: &networking.Percent{ - Value: 101, - }, - HttpDelayType: &networking.HTTPFaultInjection_Delay_FixedDelay{ - FixedDelay: &durationpb.Duration{Seconds: 3}, - }, - }, valid: false}, - {name: "invalid delay", in: &networking.HTTPFaultInjection_Delay{ - Percentage: &networking.Percent{ - Value: 20, - }, - HttpDelayType: &networking.HTTPFaultInjection_Delay_FixedDelay{ - FixedDelay: &durationpb.Duration{Seconds: 3, Nanos: 42}, - }, - }, valid: false}, - {name: "valid fractional percentage", in: &networking.HTTPFaultInjection_Delay{ - Percentage: &networking.Percent{ - Value: 0.001, - }, - HttpDelayType: &networking.HTTPFaultInjection_Delay_FixedDelay{ - FixedDelay: &durationpb.Duration{Seconds: 3}, - }, - }, valid: true}, - {name: "invalid fractional percentage", in: &networking.HTTPFaultInjection_Delay{ - Percentage: &networking.Percent{ - Value: -10.0, - }, - HttpDelayType: &networking.HTTPFaultInjection_Delay_FixedDelay{ - FixedDelay: &durationpb.Duration{Seconds: 3}, - }, - }, valid: false}, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - if got := validateHTTPFaultInjectionDelay(tc.in); (got == nil) != tc.valid { - t.Errorf("got valid=%v, want valid=%v: %v", - got == nil, tc.valid, got) - } - }) - } -} - -func TestValidateHTTPRetry(t *testing.T) { - testCases := []struct { - name string - in *networking.HTTPRetry - valid bool - }{ - {name: "valid", in: &networking.HTTPRetry{ - Attempts: 10, - PerTryTimeout: &durationpb.Duration{Seconds: 2}, - RetryOn: "5xx,gateway-error", - }, valid: true}, - {name: "disable retries", in: &networking.HTTPRetry{ - Attempts: 0, - }, valid: true}, - {name: "invalid, retry policy configured but attempts set to zero", in: &networking.HTTPRetry{ - Attempts: 0, - PerTryTimeout: &durationpb.Duration{Seconds: 2}, - RetryOn: "5xx,gateway-error", - }, valid: false}, - {name: "valid default", in: &networking.HTTPRetry{ - Attempts: 10, - }, valid: true}, - {name: "valid http status retryOn", in: &networking.HTTPRetry{ - Attempts: 10, - PerTryTimeout: &durationpb.Duration{Seconds: 2}, - RetryOn: "503,connect-failure", - }, valid: true}, - {name: "invalid attempts", in: &networking.HTTPRetry{ - Attempts: -1, - PerTryTimeout: &durationpb.Duration{Seconds: 2}, - }, valid: false}, - {name: "invalid timeout", in: &networking.HTTPRetry{ - Attempts: 10, - PerTryTimeout: &durationpb.Duration{Seconds: 2, Nanos: 1}, - }, valid: false}, - {name: "timeout too small", in: &networking.HTTPRetry{ - Attempts: 10, - PerTryTimeout: &durationpb.Duration{Nanos: 999}, - }, valid: false}, - {name: "invalid policy retryOn", in: &networking.HTTPRetry{ - Attempts: 10, - PerTryTimeout: &durationpb.Duration{Seconds: 2}, - RetryOn: "5xx,invalid policy", - }, valid: false}, - {name: "invalid http status retryOn", in: &networking.HTTPRetry{ - Attempts: 10, - PerTryTimeout: &durationpb.Duration{Seconds: 2}, - RetryOn: "600,connect-failure", - }, valid: false}, - {name: "invalid, retryRemoteLocalities configured but attempts set to zero", in: &networking.HTTPRetry{ - Attempts: 0, - RetryRemoteLocalities: &wrapperspb.BoolValue{Value: false}, - }, valid: false}, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - if got := validateHTTPRetry(tc.in); (got == nil) != tc.valid { - t.Errorf("got valid=%v, want valid=%v: %v", - got == nil, tc.valid, got) - } - }) - } -} - -func TestValidateHTTPRewrite(t *testing.T) { - testCases := []struct { - name string - in *networking.HTTPRewrite - valid bool - }{ - { - name: "nil in", - in: nil, - valid: true, - }, - { - name: "uri and authority", - in: &networking.HTTPRewrite{ - Uri: "/path/to/resource", - Authority: "foobar.org", - }, - valid: true, - }, - { - name: "uri", - in: &networking.HTTPRewrite{ - Uri: "/path/to/resource", - }, - valid: true, - }, - { - name: "authority", - in: &networking.HTTPRewrite{ - Authority: "foobar.org", - }, - valid: true, - }, - { - name: "no uri or authority", - in: &networking.HTTPRewrite{}, - valid: false, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - if got := validateHTTPRewrite(tc.in); (got == nil) != tc.valid { - t.Errorf("got valid=%v, want valid=%v: %v", - got == nil, tc.valid, got) - } - }) - } -} - -func TestValidatePortName(t *testing.T) { - testCases := []struct { - name string - valid bool - }{ - { - name: "", - valid: false, - }, - { - name: "simple", - valid: true, - }, - { - name: "full", - valid: true, - }, - { - name: "toolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolong", - valid: false, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - if err := ValidatePortName(tc.name); (err == nil) != tc.valid { - t.Fatalf("got valid=%v but wanted valid=%v: %v", err == nil, tc.valid, err) - } - }) - } -} - -func TestValidateHTTPRedirect(t *testing.T) { - testCases := []struct { - name string - redirect *networking.HTTPRedirect - valid bool - }{ - { - name: "nil redirect", - redirect: nil, - valid: true, - }, - { - name: "empty uri and authority", - redirect: &networking.HTTPRedirect{ - Uri: "", - Authority: "", - }, - valid: false, - }, - { - name: "too small redirect code", - redirect: &networking.HTTPRedirect{ - Uri: "t", - Authority: "", - RedirectCode: 299, - }, - valid: false, - }, - { - name: "too large redirect code", - redirect: &networking.HTTPRedirect{ - Uri: "t", - Authority: "", - RedirectCode: 400, - }, - valid: false, - }, - { - name: "empty authority", - redirect: &networking.HTTPRedirect{ - Uri: "t", - Authority: "", - }, - valid: true, - }, - { - name: "empty uri", - redirect: &networking.HTTPRedirect{ - Uri: "", - Authority: "t", - }, - valid: true, - }, - { - name: "empty redirect code", - redirect: &networking.HTTPRedirect{ - Uri: "t", - Authority: "t", - RedirectCode: 0, - }, - valid: true, - }, - { - name: "normal redirect", - redirect: &networking.HTTPRedirect{ - Uri: "t", - Authority: "t", - RedirectCode: 308, - }, - valid: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - if err := validateHTTPRedirect(tc.redirect); (err == nil) != tc.valid { - t.Fatalf("got valid=%v but wanted valid=%v: %v", err == nil, tc.valid, err) - } - }) - } -} - -func TestValidateDestinationWithInheritance(t *testing.T) { - test.SetBoolForTest(t, &features.EnableDestinationRuleInheritance, true) - cases := []struct { - name string - in proto.Message - valid bool - }{ - {name: "simple destination rule", in: &networking.DestinationRule{ - Host: "reviews", - TrafficPolicy: &networking.TrafficPolicy{ - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - }, - }, - Subsets: []*networking.Subset{ - {Name: "v1", Labels: map[string]string{"version": "v1"}}, - {Name: "v2", Labels: map[string]string{"version": "v2"}}, - }, - }, valid: true}, - {name: "simple global destination rule", in: &networking.DestinationRule{ - Host: "reviews", - TrafficPolicy: &networking.TrafficPolicy{ - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - }, - }, - }, valid: true}, - {name: "global rule with subsets", in: &networking.DestinationRule{ - TrafficPolicy: &networking.TrafficPolicy{ - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - }, - }, - Subsets: []*networking.Subset{ - {Name: "v1", Labels: map[string]string{"version": "v1"}}, - {Name: "v2", Labels: map[string]string{"version": "v2"}}, - }, - }, valid: false}, - {name: "global rule with exportTo", in: &networking.DestinationRule{ - TrafficPolicy: &networking.TrafficPolicy{ - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - }, - }, - ExportTo: []string{"ns1", "ns2"}, - }, valid: false}, - {name: "empty host with workloadSelector", in: &networking.DestinationRule{ - TrafficPolicy: &networking.TrafficPolicy{ - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - }, - }, - WorkloadSelector: &api.WorkloadSelector{ - MatchLabels: map[string]string{"app": "app1"}, - }, - }, valid: false}, - {name: "global rule with portLevelSettings", in: &networking.DestinationRule{ - TrafficPolicy: &networking.TrafficPolicy{ - Tls: &networking.ClientTLSSettings{ - Mode: networking.ClientTLSSettings_SIMPLE, - }, - PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ - { - Port: &networking.PortSelector{Number: 8000}, - OutlierDetection: &networking.OutlierDetection{ - MinHealthPercent: 20, - }, - }, - }, - }, - }, valid: false}, - } - for _, c := range cases { - if _, got := ValidateDestinationRule(config.Config{ - Meta: config.Meta{ - Name: someName, - Namespace: someNamespace, - }, - Spec: c.in, - }); (got == nil) != c.valid { - t.Errorf("ValidateDestinationRule failed on %v: got valid=%v but wanted valid=%v: %v", - c.name, got == nil, c.valid, got) - } - } -} - -func TestValidateDestination(t *testing.T) { - testCases := []struct { - name string - destination *networking.Destination - valid bool - }{ - { - name: "empty", - destination: &networking.Destination{}, // nothing - valid: false, - }, - { - name: "simple", - destination: &networking.Destination{ - Host: "foo.bar", - }, - valid: true, - }, - { - name: "full", - destination: &networking.Destination{ - Host: "foo.bar", - Subset: "shiny", - Port: &networking.PortSelector{ - Number: 5000, - }, - }, - valid: true, - }, - { - name: "unnumbered-selector", - destination: &networking.Destination{ - Host: "foo.bar", - Subset: "shiny", - Port: &networking.PortSelector{}, - }, - valid: false, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - if err := validateDestination(tc.destination); (err == nil) != tc.valid { - t.Fatalf("got valid=%v but wanted valid=%v: %v", err == nil, tc.valid, err) - } - }) - } -} - -func TestValidateHTTPRoute(t *testing.T) { - testCases := []struct { - name string - route *networking.HTTPRoute - valid bool - }{ - {name: "empty", route: &networking.HTTPRoute{ // nothing - }, valid: false}, - {name: "simple", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }, valid: true}, - {name: "no destination", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: nil, - }}, - }, valid: false}, - {name: "weighted", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz.south"}, - Weight: 25, - }, { - Destination: &networking.Destination{Host: "foo.baz.east"}, - Weight: 75, - }}, - }, valid: true}, - {name: "total weight > 100", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz.south"}, - Weight: 55, - }, { - Destination: &networking.Destination{Host: "foo.baz.east"}, - Weight: 50, - }}, - }, valid: false}, - {name: "total weight < 100", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz.south"}, - Weight: 49, - }, { - Destination: &networking.Destination{Host: "foo.baz.east"}, - Weight: 50, - }}, - }, valid: false}, - {name: "simple redirect", route: &networking.HTTPRoute{ - Redirect: &networking.HTTPRedirect{ - Uri: "/lerp", - Authority: "foo.biz", - }, - }, valid: true}, - {name: "conflicting redirect and route", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - Redirect: &networking.HTTPRedirect{ - Uri: "/lerp", - Authority: "foo.biz", - }, - }, valid: false}, - {name: "request response headers", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }, valid: true}, - {name: "valid headers", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - Headers: &networking.Headers{ - Request: &networking.Headers_HeaderOperations{ - Add: map[string]string{ - "name": "", - }, - Set: map[string]string{ - "name": "", - }, - Remove: []string{ - "name", - }, - }, - Response: &networking.Headers_HeaderOperations{ - Add: map[string]string{ - "name": "", - }, - Set: map[string]string{ - "name": "", - }, - Remove: []string{ - "name", - }, - }, - }, - }}, - }, valid: true}, - {name: "empty header name - request add", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - Headers: &networking.Headers{ - Request: &networking.Headers_HeaderOperations{ - Add: map[string]string{ - "": "value", - }, - }, - }, - }}, - }, valid: false}, - {name: "empty header name - request set", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - Headers: &networking.Headers{ - Request: &networking.Headers_HeaderOperations{ - Set: map[string]string{ - "": "value", - }, - }, - }, - }}, - }, valid: false}, - {name: "empty header name - request remove", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - Headers: &networking.Headers{ - Request: &networking.Headers_HeaderOperations{ - Remove: []string{ - "", - }, - }, - }, - }}, - }, valid: false}, - {name: "empty header name - response add", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - Headers: &networking.Headers{ - Response: &networking.Headers_HeaderOperations{ - Add: map[string]string{ - "": "value", - }, - }, - }, - }}, - }, valid: false}, - {name: "empty header name - response set", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - Headers: &networking.Headers{ - Response: &networking.Headers_HeaderOperations{ - Set: map[string]string{ - "": "value", - }, - }, - }, - }}, - }, valid: false}, - {name: "empty header name - response remove", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - Headers: &networking.Headers{ - Response: &networking.Headers_HeaderOperations{ - Remove: []string{ - "", - }, - }, - }, - }}, - }, valid: false}, - {name: "envoy escaped % set", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - Headers: &networking.Headers{ - Response: &networking.Headers_HeaderOperations{ - Set: map[string]string{ - "i-love-istio": "100%%", - }, - }, - }, - }}, - }, valid: true}, - {name: "envoy variable set", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - Headers: &networking.Headers{ - Response: &networking.Headers_HeaderOperations{ - Set: map[string]string{ - "name": "%HOSTNAME%", - }, - }, - }, - }}, - }, valid: true}, - {name: "envoy unescaped % set", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - Headers: &networking.Headers{ - Response: &networking.Headers_HeaderOperations{ - Set: map[string]string{ - "name": "abcd%oijasodifj", - }, - }, - }, - }}, - }, valid: false}, - {name: "envoy escaped % add", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - Headers: &networking.Headers{ - Response: &networking.Headers_HeaderOperations{ - Add: map[string]string{ - "i-love-istio": "100%% and more", - }, - }, - }, - }}, - }, valid: true}, - {name: "envoy variable add", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - Headers: &networking.Headers{ - Response: &networking.Headers_HeaderOperations{ - Add: map[string]string{ - "name": "hello %HOSTNAME%", - }, - }, - }, - }}, - }, valid: true}, - {name: "envoy unescaped % add", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - Headers: &networking.Headers{ - Response: &networking.Headers_HeaderOperations{ - Add: map[string]string{ - "name": "abcd%oijasodifj", - }, - }, - }, - }}, - }, valid: false}, - {name: "null header match", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.bar"}, - }}, - Match: []*networking.HTTPMatchRequest{{ - Headers: map[string]*networking.StringMatch{ - "header": nil, - }, - }}, - }, valid: false}, - {name: "nil match", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.bar"}, - }}, - Match: nil, - }, valid: true}, - {name: "match with nil element", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.bar"}, - }}, - Match: []*networking.HTTPMatchRequest{nil}, - }, valid: true}, - {name: "invalid mirror percent", route: &networking.HTTPRoute{ - MirrorPercent: &wrapperspb.UInt32Value{Value: 101}, - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.bar"}, - }}, - Match: []*networking.HTTPMatchRequest{nil}, - }, valid: false}, - {name: "invalid mirror percentage", route: &networking.HTTPRoute{ - MirrorPercentage: &networking.Percent{ - Value: 101, - }, - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.bar"}, - }}, - Match: []*networking.HTTPMatchRequest{nil}, - }, valid: false}, - {name: "valid mirror percentage", route: &networking.HTTPRoute{ - MirrorPercentage: &networking.Percent{ - Value: 1, - }, - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.bar"}, - }}, - Match: []*networking.HTTPMatchRequest{nil}, - }, valid: true}, - {name: "negative mirror percentage", route: &networking.HTTPRoute{ - MirrorPercentage: &networking.Percent{ - Value: -1, - }, - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.bar"}, - }}, - Match: []*networking.HTTPMatchRequest{nil}, - }, valid: false}, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - if err := validateHTTPRoute(tc.route, false); (err.Err == nil) != tc.valid { - t.Fatalf("got valid=%v but wanted valid=%v: %v", err.Err == nil, tc.valid, err) - } - }) - } -} - -func TestValidateRouteDestination(t *testing.T) { - testCases := []struct { - name string - routes []*networking.RouteDestination - valid bool - }{ - {name: "simple", routes: []*networking.RouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, valid: true}, - {name: "wildcard dash", routes: []*networking.RouteDestination{{ - Destination: &networking.Destination{Host: "*-foo.baz"}, - }}, valid: true}, - {name: "wildcard prefix", routes: []*networking.RouteDestination{{ - Destination: &networking.Destination{Host: "*foo.baz"}, - }}, valid: true}, - {name: "wildcard", routes: []*networking.RouteDestination{{ - Destination: &networking.Destination{Host: "*"}, - }}, valid: false}, - {name: "bad wildcard", routes: []*networking.RouteDestination{{ - Destination: &networking.Destination{Host: "foo.*"}, - }}, valid: false}, - {name: "bad fqdn", routes: []*networking.RouteDestination{{ - Destination: &networking.Destination{Host: "default/baz"}, - }}, valid: false}, - {name: "no destination", routes: []*networking.RouteDestination{{ - Destination: nil, - }}, valid: false}, - {name: "weighted", routes: []*networking.RouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz.south"}, - Weight: 25, - }, { - Destination: &networking.Destination{Host: "foo.baz.east"}, - Weight: 75, - }}, valid: true}, - {name: "weight < 0", routes: []*networking.RouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz.south"}, - Weight: 5, - }, { - Destination: &networking.Destination{Host: "foo.baz.east"}, - Weight: -1, - }}, valid: false}, - {name: "total weight > 100", routes: []*networking.RouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz.south"}, - Weight: 55, - }, { - Destination: &networking.Destination{Host: "foo.baz.east"}, - Weight: 50, - }}, valid: false}, - {name: "total weight < 100", routes: []*networking.RouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz.south"}, - Weight: 49, - }, { - Destination: &networking.Destination{Host: "foo.baz.east"}, - Weight: 50, - }}, valid: false}, - {name: "total weight = 100", routes: []*networking.RouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz.south"}, - Weight: 100, - }, { - Destination: &networking.Destination{Host: "foo.baz.east"}, - Weight: 0, - }}, valid: true}, - {name: "weight = 0", routes: []*networking.RouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz.south"}, - Weight: 0, - }}, valid: true}, - {name: "total weight = 0 with multi RouteDestination", routes: []*networking.RouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz.south"}, - Weight: 0, - }, { - Destination: &networking.Destination{Host: "foo.baz.east"}, - Weight: 0, - }}, valid: false}, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - if err := validateRouteDestinations(tc.routes); (err == nil) != tc.valid { - t.Fatalf("got valid=%v but wanted valid=%v: %v", err == nil, tc.valid, err) - } - }) - } -} - -// TODO: add TCP test cases once it is implemented -func TestValidateVirtualService(t *testing.T) { - testCases := []struct { - name string - in proto.Message - valid bool - warning bool - }{ - {name: "simple", in: &networking.VirtualService{ - Hosts: []string{"foo.bar"}, - Http: []*networking.HTTPRoute{{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }}, - }, valid: true}, - {name: "duplicate hosts", in: &networking.VirtualService{ - Hosts: []string{"*.foo.bar", "*.bar"}, - Http: []*networking.HTTPRoute{{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }}, - }, valid: false}, - {name: "with no destination", in: &networking.VirtualService{ - Hosts: []string{"*.foo.bar", "*.bar"}, - Http: []*networking.HTTPRoute{{ - Route: []*networking.HTTPRouteDestination{{}}, - }}, - }, valid: false}, - {name: "destination with out hosts", in: &networking.VirtualService{ - Hosts: []string{"*.foo.bar", "*.bar"}, - Http: []*networking.HTTPRoute{{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{}, - }}, - }}, - }, valid: false}, - {name: "delegate with no hosts", in: &networking.VirtualService{ - Hosts: nil, - Http: []*networking.HTTPRoute{{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }}, - }, valid: true}, - {name: "bad host", in: &networking.VirtualService{ - Hosts: []string{"foo.ba!r"}, - Http: []*networking.HTTPRoute{{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }}, - }, valid: false}, - {name: "no tcp or http routing", in: &networking.VirtualService{ - Hosts: []string{"foo.bar"}, - }, valid: false}, - {name: "bad gateway", in: &networking.VirtualService{ - Hosts: []string{"foo.bar"}, - Gateways: []string{"b@dgateway"}, - Http: []*networking.HTTPRoute{{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }}, - }, valid: false}, - {name: "FQDN for gateway", in: &networking.VirtualService{ - Hosts: []string{"foo.bar"}, - Gateways: []string{"gateway.example.com"}, - Http: []*networking.HTTPRoute{{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }}, - }, valid: true, warning: true}, - {name: "namespace/name for gateway", in: &networking.VirtualService{ - Hosts: []string{"foo.bar"}, - Gateways: []string{"ns1/gateway"}, - Http: []*networking.HTTPRoute{{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }}, - }, valid: true}, - {name: "namespace/* for gateway", in: &networking.VirtualService{ - Hosts: []string{"foo.bar"}, - Gateways: []string{"ns1/*"}, - Http: []*networking.HTTPRoute{{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }}, - }, valid: false}, - {name: "*/name for gateway", in: &networking.VirtualService{ - Hosts: []string{"foo.bar"}, - Gateways: []string{"*/gateway"}, - Http: []*networking.HTTPRoute{{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }}, - }, valid: false}, - {name: "wildcard for mesh gateway", in: &networking.VirtualService{ - Hosts: []string{"*"}, - Http: []*networking.HTTPRoute{{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }}, - }, valid: false}, - {name: "wildcard for non-mesh gateway", in: &networking.VirtualService{ - Hosts: []string{"*"}, - Gateways: []string{"somegateway"}, - Http: []*networking.HTTPRoute{{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }}, - }, valid: true}, - {name: "missing tcp route", in: &networking.VirtualService{ - Hosts: []string{"foo.bar"}, - Tcp: []*networking.TCPRoute{{ - Match: []*networking.L4MatchAttributes{ - {Port: 999}, - }, - }}, - }, valid: false}, - {name: "missing tls route", in: &networking.VirtualService{ - Hosts: []string{"foo.bar"}, - Tls: []*networking.TLSRoute{{ - Match: []*networking.TLSMatchAttributes{ - { - Port: 999, - SniHosts: []string{"foo.bar"}, - }, - }, - }}, - }, valid: false}, - {name: "deprecated mirror", in: &networking.VirtualService{ - Hosts: []string{"foo.bar"}, - Gateways: []string{"ns1/gateway"}, - Http: []*networking.HTTPRoute{{ - MirrorPercent: &wrapperspb.UInt32Value{Value: 5}, - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }}, - }, valid: true, warning: true}, - {name: "set authority", in: &networking.VirtualService{ - Hosts: []string{"foo.bar"}, - Http: []*networking.HTTPRoute{{ - Headers: &networking.Headers{ - Request: &networking.Headers_HeaderOperations{Set: map[string]string{":authority": "foo"}}, - }, - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }}, - }, valid: true, warning: false}, - {name: "set authority in destination", in: &networking.VirtualService{ - Hosts: []string{"foo.bar"}, - Http: []*networking.HTTPRoute{{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - Headers: &networking.Headers{ - Request: &networking.Headers_HeaderOperations{Set: map[string]string{":authority": "foo"}}, - }, - }}, - }}, - }, valid: false, warning: false}, - {name: "set authority in rewrite and header", in: &networking.VirtualService{ - Hosts: []string{"foo.bar"}, - Http: []*networking.HTTPRoute{{ - Headers: &networking.Headers{ - Request: &networking.Headers_HeaderOperations{Set: map[string]string{":authority": "foo"}}, - }, - Rewrite: &networking.HTTPRewrite{Authority: "bar"}, - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }}, - }, valid: false, warning: false}, - {name: "non-method-get", in: &networking.VirtualService{ - Hosts: []string{"foo.bar"}, - Http: []*networking.HTTPRoute{{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/api/v1/product"}, - }, - }, - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/api/v1/products"}, - }, - Method: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "GET"}, - }, - }, - }, - }}, - }, valid: true, warning: true}, - {name: "uri-with-prefix-exact", in: &networking.VirtualService{ - Hosts: []string{"foo.bar"}, - Http: []*networking.HTTPRoute{{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/"}, - }, - }, - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "/"}, - }, - Method: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "GET"}, - }, - }, - }, - }}, - }, valid: true, warning: false}, - {name: "jwt claim route without gateway", in: &networking.VirtualService{ - Hosts: []string{"foo.bar"}, - Gateways: []string{"mesh"}, - Http: []*networking.HTTPRoute{{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - Match: []*networking.HTTPMatchRequest{ - { - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "/"}, - }, - Headers: map[string]*networking.StringMatch{ - "@request.auth.claims.foo": { - MatchType: &networking.StringMatch_Exact{Exact: "bar"}, - }, - }, - }, - }, - }}, - }, valid: false, warning: false}, - {name: "ip address as sni host", in: &networking.VirtualService{ - Hosts: []string{"foo.bar"}, - Tls: []*networking.TLSRoute{{ - Route: []*networking.RouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - Match: []*networking.TLSMatchAttributes{ - { - Port: 999, - SniHosts: []string{"1.1.1.1"}, - }, - }, - }}, - }, valid: true, warning: true}, - {name: "invalid wildcard as sni host", in: &networking.VirtualService{ - Hosts: []string{"foo.bar"}, - Tls: []*networking.TLSRoute{{ - Route: []*networking.RouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - Match: []*networking.TLSMatchAttributes{ - { - Port: 999, - SniHosts: []string{"foo.*.com"}, - }, - }, - }}, - }, valid: false, warning: false}, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - warn, err := ValidateVirtualService(config.Config{Spec: tc.in}) - checkValidation(t, warn, err, tc.valid, tc.warning) - }) - } -} - -func TestValidateWorkloadEntry(t *testing.T) { - testCases := []struct { - name string - in proto.Message - valid bool - warning bool - }{ - { - name: "valid", - in: &networking.WorkloadEntry{Address: "1.2.3.4"}, - valid: true, - }, - { - name: "missing address", - in: &networking.WorkloadEntry{}, - valid: false, - }, - { - name: "valid unix endpoint", - in: &networking.WorkloadEntry{Address: "unix:///lon/google/com"}, - valid: true, - }, - { - name: "invalid unix endpoint", - in: &networking.WorkloadEntry{Address: "unix:///lon/google/com", Ports: map[string]uint32{"7777": 7777}}, - valid: false, - }, - { - name: "valid FQDN", - in: &networking.WorkloadEntry{Address: "validdns.com", Ports: map[string]uint32{"7777": 7777}}, - valid: true, - }, - { - name: "invalid FQDN", - in: &networking.WorkloadEntry{Address: "invaliddns.com:9443", Ports: map[string]uint32{"7777": 7777}}, - valid: false, - }, - { - name: "valid IP", - in: &networking.WorkloadEntry{Address: "172.16.1.1", Ports: map[string]uint32{"7777": 7777}}, - valid: true, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - warn, err := ValidateWorkloadEntry(config.Config{Spec: tc.in}) - checkValidation(t, warn, err, tc.valid, tc.warning) - }) - } -} - -func TestValidateWorkloadGroup(t *testing.T) { - testCases := []struct { - name string - in proto.Message - valid bool - warning bool - }{ - { - name: "valid", - in: &networking.WorkloadGroup{Template: &networking.WorkloadEntry{}}, - valid: true, - }, - { - name: "invalid", - in: &networking.WorkloadGroup{Template: &networking.WorkloadEntry{}, Metadata: &networking.WorkloadGroup_ObjectMeta{Labels: map[string]string{ - ".": "~", - }}}, - valid: false, - }, - { - name: "probe missing method", - in: &networking.WorkloadGroup{ - Template: &networking.WorkloadEntry{}, - Probe: &networking.ReadinessProbe{}, - }, - valid: false, - }, - { - name: "probe nil", - in: &networking.WorkloadGroup{ - Template: &networking.WorkloadEntry{}, - Probe: &networking.ReadinessProbe{ - HealthCheckMethod: &networking.ReadinessProbe_HttpGet{}, - }, - }, - valid: false, - }, - { - name: "probe http empty", - in: &networking.WorkloadGroup{ - Template: &networking.WorkloadEntry{}, - Probe: &networking.ReadinessProbe{ - HealthCheckMethod: &networking.ReadinessProbe_HttpGet{ - HttpGet: &networking.HTTPHealthCheckConfig{}, - }, - }, - }, - valid: false, - }, - { - name: "probe http valid", - in: &networking.WorkloadGroup{ - Template: &networking.WorkloadEntry{}, - Probe: &networking.ReadinessProbe{ - HealthCheckMethod: &networking.ReadinessProbe_HttpGet{ - HttpGet: &networking.HTTPHealthCheckConfig{ - Port: 5, - }, - }, - }, - }, - valid: true, - }, - { - name: "probe tcp invalid", - in: &networking.WorkloadGroup{ - Template: &networking.WorkloadEntry{}, - Probe: &networking.ReadinessProbe{ - HealthCheckMethod: &networking.ReadinessProbe_TcpSocket{ - TcpSocket: &networking.TCPHealthCheckConfig{}, - }, - }, - }, - valid: false, - }, - { - name: "probe tcp valid", - in: &networking.WorkloadGroup{ - Template: &networking.WorkloadEntry{}, - Probe: &networking.ReadinessProbe{ - HealthCheckMethod: &networking.ReadinessProbe_TcpSocket{ - TcpSocket: &networking.TCPHealthCheckConfig{ - Port: 5, - }, - }, - }, - }, - valid: true, - }, - { - name: "probe exec invalid", - in: &networking.WorkloadGroup{ - Template: &networking.WorkloadEntry{}, - Probe: &networking.ReadinessProbe{ - HealthCheckMethod: &networking.ReadinessProbe_Exec{ - Exec: &networking.ExecHealthCheckConfig{}, - }, - }, - }, - valid: false, - }, - { - name: "probe exec valid", - in: &networking.WorkloadGroup{ - Template: &networking.WorkloadEntry{}, - Probe: &networking.ReadinessProbe{ - HealthCheckMethod: &networking.ReadinessProbe_Exec{ - Exec: &networking.ExecHealthCheckConfig{ - Command: []string{"foo", "bar"}, - }, - }, - }, - }, - valid: true, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - warn, err := ValidateWorkloadGroup(config.Config{Spec: tc.in}) - checkValidation(t, warn, err, tc.valid, tc.warning) - }) - } -} - -func checkValidation(t *testing.T, gotWarning Warning, gotError error, valid bool, warning bool) { - t.Helper() - if (gotError == nil) != valid { - t.Fatalf("got valid=%v but wanted valid=%v: %v", gotError == nil, valid, gotError) - } - if (gotWarning == nil) == warning { - t.Fatalf("got warning=%v but wanted warning=%v", gotWarning, warning) - } -} - -func stringOrEmpty(v error) string { - if v == nil { - return "" - } - return v.Error() -} - -func checkValidationMessage(t *testing.T, gotWarning Warning, gotError error, wantWarning string, wantError string) { - t.Helper() - if (gotError == nil) != (wantError == "") { - t.Fatalf("got err=%v but wanted err=%v", gotError, wantError) - } - if !strings.Contains(stringOrEmpty(gotError), wantError) { - t.Fatalf("got err=%v but wanted err=%v", gotError, wantError) - } - - if (gotWarning == nil) != (wantWarning == "") { - t.Fatalf("got warning=%v but wanted warning=%v", gotWarning, wantWarning) - } - if !strings.Contains(stringOrEmpty(gotWarning), wantWarning) { - t.Fatalf("got warning=%v but wanted warning=%v", gotWarning, wantWarning) - } -} - -func TestValidateDestinationRule(t *testing.T) { - cases := []struct { - name string - in proto.Message - valid bool - }{ - {name: "simple destination rule", in: &networking.DestinationRule{ - Host: "reviews", - Subsets: []*networking.Subset{ - {Name: "v1", Labels: map[string]string{"version": "v1"}}, - {Name: "v2", Labels: map[string]string{"version": "v2"}}, - }, - }, valid: true}, - - {name: "missing destination name", in: &networking.DestinationRule{ - Host: "", - Subsets: []*networking.Subset{ - {Name: "v1", Labels: map[string]string{"version": "v1"}}, - {Name: "v2", Labels: map[string]string{"version": "v2"}}, - }, - }, valid: false}, - - {name: "missing subset name", in: &networking.DestinationRule{ - Host: "reviews", - Subsets: []*networking.Subset{ - {Name: "", Labels: map[string]string{"version": "v1"}}, - {Name: "v2", Labels: map[string]string{"version": "v2"}}, - }, - }, valid: false}, - - {name: "valid traffic policy, top level", in: &networking.DestinationRule{ - Host: "reviews", - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: networking.LoadBalancerSettings_ROUND_ROBIN, - }, - }, - ConnectionPool: &networking.ConnectionPoolSettings{ - Tcp: &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: 7}, - Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: 11}, - }, - OutlierDetection: &networking.OutlierDetection{ - MinHealthPercent: 20, - }, - }, - Subsets: []*networking.Subset{ - {Name: "v1", Labels: map[string]string{"version": "v1"}}, - {Name: "v2", Labels: map[string]string{"version": "v2"}}, - }, - }, valid: true}, - - {name: "invalid traffic policy, top level", in: &networking.DestinationRule{ - Host: "reviews", - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: networking.LoadBalancerSettings_ROUND_ROBIN, - }, - }, - ConnectionPool: &networking.ConnectionPoolSettings{}, - OutlierDetection: &networking.OutlierDetection{ - MinHealthPercent: 20, - }, - }, - Subsets: []*networking.Subset{ - {Name: "v1", Labels: map[string]string{"version": "v1"}}, - {Name: "v2", Labels: map[string]string{"version": "v2"}}, - }, - }, valid: false}, - - {name: "valid traffic policy, subset level", in: &networking.DestinationRule{ - Host: "reviews", - Subsets: []*networking.Subset{ - { - Name: "v1", Labels: map[string]string{"version": "v1"}, - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: networking.LoadBalancerSettings_ROUND_ROBIN, - }, - }, - ConnectionPool: &networking.ConnectionPoolSettings{ - Tcp: &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: 7}, - Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: 11}, - }, - OutlierDetection: &networking.OutlierDetection{ - MinHealthPercent: 20, - }, - }, - }, - {Name: "v2", Labels: map[string]string{"version": "v2"}}, - }, - }, valid: true}, - - {name: "invalid traffic policy, subset level", in: &networking.DestinationRule{ - Host: "reviews", - Subsets: []*networking.Subset{ - { - Name: "v1", Labels: map[string]string{"version": "v1"}, - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: networking.LoadBalancerSettings_ROUND_ROBIN, - }, - }, - ConnectionPool: &networking.ConnectionPoolSettings{}, - OutlierDetection: &networking.OutlierDetection{ - MinHealthPercent: 20, - }, - }, - }, - {Name: "v2", Labels: map[string]string{"version": "v2"}}, - }, - }, valid: false}, - - {name: "valid traffic policy, both levels", in: &networking.DestinationRule{ - Host: "reviews", - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: networking.LoadBalancerSettings_ROUND_ROBIN, - }, - }, - ConnectionPool: &networking.ConnectionPoolSettings{ - Tcp: &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: 7}, - Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: 11}, - }, - OutlierDetection: &networking.OutlierDetection{ - MinHealthPercent: 20, - }, - }, - Subsets: []*networking.Subset{ - { - Name: "v1", Labels: map[string]string{"version": "v1"}, - TrafficPolicy: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: networking.LoadBalancerSettings_ROUND_ROBIN, - }, - }, - ConnectionPool: &networking.ConnectionPoolSettings{ - Tcp: &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: 7}, - Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: 11}, - }, - OutlierDetection: &networking.OutlierDetection{ - MinHealthPercent: 30, - }, - }, - }, - {Name: "v2", Labels: map[string]string{"version": "v2"}}, - }, - }, valid: true}, - } - for _, c := range cases { - if _, got := ValidateDestinationRule(config.Config{ - Meta: config.Meta{ - Name: someName, - Namespace: someNamespace, - }, - Spec: c.in, - }); (got == nil) != c.valid { - t.Errorf("ValidateDestinationRule failed on %v: got valid=%v but wanted valid=%v: %v", - c.name, got == nil, c.valid, got) - } - } -} - -func TestValidateTrafficPolicy(t *testing.T) { - cases := []struct { - name string - in *networking.TrafficPolicy - valid bool - }{ - { - name: "valid traffic policy", in: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: networking.LoadBalancerSettings_ROUND_ROBIN, - }, - }, - ConnectionPool: &networking.ConnectionPoolSettings{ - Tcp: &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: 7}, - Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: 11}, - }, - OutlierDetection: &networking.OutlierDetection{ - MinHealthPercent: 20, - }, - }, - valid: true, - }, - { - name: "invalid traffic policy, nil entries", in: &networking.TrafficPolicy{}, - valid: false, - }, - - { - name: "invalid traffic policy, missing port in port level settings", in: &networking.TrafficPolicy{ - PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ - { - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: networking.LoadBalancerSettings_ROUND_ROBIN, - }, - }, - ConnectionPool: &networking.ConnectionPoolSettings{ - Tcp: &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: 7}, - Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: 11}, - }, - OutlierDetection: &networking.OutlierDetection{ - MinHealthPercent: 20, - }, - }, - }, - }, - valid: false, - }, - { - name: "invalid traffic policy, bad connection pool", in: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: networking.LoadBalancerSettings_ROUND_ROBIN, - }, - }, - ConnectionPool: &networking.ConnectionPoolSettings{}, - OutlierDetection: &networking.OutlierDetection{ - MinHealthPercent: 20, - }, - }, - valid: false, - }, - { - name: "invalid traffic policy, panic threshold too low", in: &networking.TrafficPolicy{ - LoadBalancer: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: networking.LoadBalancerSettings_ROUND_ROBIN, - }, - }, - ConnectionPool: &networking.ConnectionPoolSettings{ - Tcp: &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: 7}, - Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: 11}, - }, - OutlierDetection: &networking.OutlierDetection{ - MinHealthPercent: -1, - }, - }, - valid: false, - }, - { - name: "invalid traffic policy, both upgrade and use client protocol set", in: &networking.TrafficPolicy{ - ConnectionPool: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - H2UpgradePolicy: networking.ConnectionPoolSettings_HTTPSettings_UPGRADE, - UseClientProtocol: true, - }, - }, - }, - valid: false, - }, - } - for _, c := range cases { - if got := validateTrafficPolicy(c.in).Err; (got == nil) != c.valid { - t.Errorf("ValidateTrafficPolicy failed on %v: got valid=%v but wanted valid=%v: %v", - c.name, got == nil, c.valid, got) - } - } -} - -func TestValidateConnectionPool(t *testing.T) { - cases := []struct { - name string - in *networking.ConnectionPoolSettings - valid bool - }{ - { - name: "valid connection pool, tcp and http", in: &networking.ConnectionPoolSettings{ - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - MaxConnections: 7, - ConnectTimeout: &durationpb.Duration{Seconds: 2}, - }, - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - Http1MaxPendingRequests: 2, - Http2MaxRequests: 11, - MaxRequestsPerConnection: 5, - MaxRetries: 4, - IdleTimeout: &durationpb.Duration{Seconds: 30}, - }, - }, - valid: true, - }, - - { - name: "valid connection pool, tcp only", in: &networking.ConnectionPoolSettings{ - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - MaxConnections: 7, - ConnectTimeout: &durationpb.Duration{Seconds: 2}, - }, - }, - valid: true, - }, - - { - name: "valid connection pool, http only", in: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - Http1MaxPendingRequests: 2, - Http2MaxRequests: 11, - MaxRequestsPerConnection: 5, - MaxRetries: 4, - IdleTimeout: &durationpb.Duration{Seconds: 30}, - }, - }, - valid: true, - }, - - { - name: "valid connection pool, http only with empty idle timeout", in: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{ - Http1MaxPendingRequests: 2, - Http2MaxRequests: 11, - MaxRequestsPerConnection: 5, - MaxRetries: 4, - }, - }, - valid: true, - }, - - {name: "invalid connection pool, empty", in: &networking.ConnectionPoolSettings{}, valid: false}, - - { - name: "invalid connection pool, bad max connections", in: &networking.ConnectionPoolSettings{ - Tcp: &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: -1}, - }, - valid: false, - }, - - { - name: "invalid connection pool, bad connect timeout", in: &networking.ConnectionPoolSettings{ - Tcp: &networking.ConnectionPoolSettings_TCPSettings{ - ConnectTimeout: &durationpb.Duration{Seconds: 2, Nanos: 5}, - }, - }, - valid: false, - }, - - { - name: "invalid connection pool, bad max pending requests", in: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{Http1MaxPendingRequests: -1}, - }, - valid: false, - }, - - { - name: "invalid connection pool, bad max requests", in: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: -1}, - }, - valid: false, - }, - - { - name: "invalid connection pool, bad max requests per connection", in: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{MaxRequestsPerConnection: -1}, - }, - valid: false, - }, - - { - name: "invalid connection pool, bad max retries", in: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{MaxRetries: -1}, - }, - valid: false, - }, - - { - name: "invalid connection pool, bad idle timeout", in: &networking.ConnectionPoolSettings{ - Http: &networking.ConnectionPoolSettings_HTTPSettings{IdleTimeout: &durationpb.Duration{Seconds: 30, Nanos: 5}}, - }, - valid: false, - }, - } - - for _, c := range cases { - if got := validateConnectionPool(c.in); (got == nil) != c.valid { - t.Errorf("ValidateConnectionSettings failed on %v: got valid=%v but wanted valid=%v: %v", - c.name, got == nil, c.valid, got) - } - } -} - -func TestValidateLoadBalancer(t *testing.T) { - duration := durationpb.Duration{Seconds: int64(time.Hour / time.Second)} - cases := []struct { - name string - in *networking.LoadBalancerSettings - valid bool - }{ - { - name: "valid load balancer with simple load balancing", in: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_Simple{ - Simple: networking.LoadBalancerSettings_ROUND_ROBIN, - }, - }, - valid: true, - }, - - { - name: "valid load balancer with consistentHash load balancing", in: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{ - ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ - MinimumRingSize: 1024, - HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpCookie{ - HttpCookie: &networking.LoadBalancerSettings_ConsistentHashLB_HTTPCookie{ - Name: "test", - Ttl: &duration, - }, - }, - }, - }, - }, - valid: true, - }, - - { - name: "invalid load balancer with consistentHash load balancing, missing ttl", in: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{ - ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ - MinimumRingSize: 1024, - HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpCookie{ - HttpCookie: &networking.LoadBalancerSettings_ConsistentHashLB_HTTPCookie{ - Name: "test", - }, - }, - }, - }, - }, - valid: false, - }, - - { - name: "invalid load balancer with consistentHash load balancing, missing name", in: &networking.LoadBalancerSettings{ - LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{ - ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ - MinimumRingSize: 1024, - HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpCookie{ - HttpCookie: &networking.LoadBalancerSettings_ConsistentHashLB_HTTPCookie{ - Ttl: &duration, - }, - }, - }, - }, - }, - valid: false, - }, - } - - for _, c := range cases { - if got := validateLoadBalancer(c.in); (got == nil) != c.valid { - t.Errorf("validateLoadBalancer failed on %v: got valid=%v but wanted valid=%v: %v", - c.name, got == nil, c.valid, got) - } - } -} - -func TestValidateOutlierDetection(t *testing.T) { - cases := []struct { - name string - in *networking.OutlierDetection - valid bool - warn bool - }{ - {name: "valid outlier detection", in: &networking.OutlierDetection{ - Interval: &durationpb.Duration{Seconds: 2}, - BaseEjectionTime: &durationpb.Duration{Seconds: 2}, - MaxEjectionPercent: 50, - }, valid: true}, - - { - name: "invalid outlier detection, bad interval", in: &networking.OutlierDetection{ - Interval: &durationpb.Duration{Seconds: 2, Nanos: 5}, - }, - valid: false, - }, - - { - name: "invalid outlier detection, bad base ejection time", in: &networking.OutlierDetection{ - BaseEjectionTime: &durationpb.Duration{Seconds: 2, Nanos: 5}, - }, - valid: false, - }, - - { - name: "invalid outlier detection, bad max ejection percent", in: &networking.OutlierDetection{ - MaxEjectionPercent: 105, - }, - valid: false, - }, - { - name: "invalid outlier detection, panic threshold too low", in: &networking.OutlierDetection{ - MinHealthPercent: -1, - }, - valid: false, - }, - { - name: "invalid outlier detection, panic threshold too high", in: &networking.OutlierDetection{ - MinHealthPercent: 101, - }, - valid: false, - }, - { - name: "deprecated outlier detection, ConsecutiveErrors", in: &networking.OutlierDetection{ - ConsecutiveErrors: 101, - }, - valid: true, - warn: true, - }, - { - name: "consecutive local origin errors is set but split local origin errors is not set", in: &networking.OutlierDetection{ - ConsecutiveLocalOriginFailures: &wrapperspb.UInt32Value{Value: 10}, - }, - valid: false, - }, - } - - for _, c := range cases { - got := validateOutlierDetection(c.in) - if (got.Err == nil) != c.valid { - t.Errorf("ValidateOutlierDetection failed on %v: got valid=%v but wanted valid=%v: %v", - c.name, got.Err == nil, c.valid, got.Err) - } - if (got.Warning == nil) == c.warn { - t.Errorf("ValidateOutlierDetection failed on %v: got warn=%v but wanted warn=%v: %v", - c.name, got.Warning == nil, c.warn, got.Warning) - } - } -} - -func TestValidateEnvoyFilter(t *testing.T) { - tests := []struct { - name string - in proto.Message - error string - warning string - }{ - {name: "empty filters", in: &networking.EnvoyFilter{}, error: ""}, - - {name: "invalid applyTo", in: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: 0, - }, - }, - }, error: "Envoy filter: missing applyTo"}, - {name: "nil patch", in: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_LISTENER, - Patch: nil, - }, - }, - }, error: "Envoy filter: missing patch"}, - {name: "invalid patch operation", in: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_LISTENER, - Patch: &networking.EnvoyFilter_Patch{}, - }, - }, - }, error: "Envoy filter: missing patch operation"}, - {name: "nil patch value", in: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_LISTENER, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_ADD, - }, - }, - }, - }, error: "Envoy filter: missing patch value for non-remove operation"}, - {name: "match with invalid regex", in: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_LISTENER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Proxy: &networking.EnvoyFilter_ProxyMatch{ - ProxyVersion: "%#@~++==`24c234`", - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - }, - }, error: "Envoy filter: invalid regex for proxy version, [error parsing regexp: invalid nested repetition operator: `++`]"}, - {name: "match with valid regex", in: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_LISTENER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - Proxy: &networking.EnvoyFilter_ProxyMatch{ - ProxyVersion: `release-1\.2-23434`, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - }, - }, error: ""}, - {name: "listener with invalid match", in: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_LISTENER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Cluster{ - Cluster: &networking.EnvoyFilter_ClusterMatch{}, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - }, - }, error: "Envoy filter: applyTo for listener class objects cannot have non listener match"}, - {name: "listener with invalid filter match", in: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_NETWORK_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Sni: "124", - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{}, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - }, - }, error: "Envoy filter: filter match has no name to match on"}, - {name: "listener with sub filter match and invalid applyTo", in: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_NETWORK_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: "random", - SubFilter: &networking.EnvoyFilter_ListenerMatch_SubFilterMatch{}, - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - }, - }, error: "Envoy filter: subfilter match can be used with applyTo HTTP_FILTER only"}, - {name: "listener with sub filter match and invalid filter name", in: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_HTTP_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: "random", - SubFilter: &networking.EnvoyFilter_ListenerMatch_SubFilterMatch{}, - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - }, - }, error: "Envoy filter: subfilter match requires filter match with envoy.filters.network.http_connection_manager"}, - {name: "listener with sub filter match and no sub filter name", in: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_HTTP_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: wellknown.HTTPConnectionManager, - SubFilter: &networking.EnvoyFilter_ListenerMatch_SubFilterMatch{}, - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - }, - }, error: "Envoy filter: subfilter match has no name to match on"}, - {name: "route configuration with invalid match", in: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_VIRTUAL_HOST, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Cluster{ - Cluster: &networking.EnvoyFilter_ClusterMatch{}, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - }, - }, error: "Envoy filter: applyTo for http route class objects cannot have non route configuration match"}, - {name: "cluster with invalid match", in: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_CLUSTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{}, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_REMOVE, - }, - }, - }, - }, error: "Envoy filter: applyTo for cluster class objects cannot have non cluster match"}, - {name: "invalid patch value", in: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_CLUSTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Cluster{ - Cluster: &networking.EnvoyFilter_ClusterMatch{}, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_ADD, - Value: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "name": { - Kind: &structpb.Value_BoolValue{BoolValue: false}, - }, - }, - }, - }, - }, - }, - }, error: `Envoy filter: json: cannot unmarshal bool into Go value of type string`}, - {name: "happy config", in: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_CLUSTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Cluster{ - Cluster: &networking.EnvoyFilter_ClusterMatch{}, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_ADD, - Value: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "lb_policy": { - Kind: &structpb.Value_StringValue{StringValue: "RING_HASH"}, - }, - }, - }, - }, - }, - { - ApplyTo: networking.EnvoyFilter_NETWORK_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Name: "envoy.tcp_proxy", - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_INSERT_BEFORE, - Value: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "typed_config": { - Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "@type": { - Kind: &structpb.Value_StringValue{ - StringValue: "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz", - }, - }, - }, - }}, - }, - }, - }, - }, - }, - { - ApplyTo: networking.EnvoyFilter_NETWORK_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Name: "envoy.tcp_proxy", - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_INSERT_FIRST, - Value: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "typed_config": { - Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "@type": { - Kind: &structpb.Value_StringValue{ - StringValue: "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz", - }, - }, - }, - }}, - }, - }, - }, - }, - }, - }, - }, error: ""}, - {name: "deprecated config", in: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_NETWORK_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Name: "envoy.tcp_proxy", - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_INSERT_FIRST, - Value: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "typed_config": { - Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "@type": { - Kind: &structpb.Value_StringValue{ - StringValue: "type.googleapis.com/envoy.config.filter.network.ext_authz.v2.ExtAuthz", - }, - }, - }, - }}, - }, - }, - }, - }, - }, - }, - }, error: "", warning: "using deprecated type_url"}, - {name: "deprecated type", in: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_HTTP_FILTER, - Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{ - ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ - Listener: &networking.EnvoyFilter_ListenerMatch{ - FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{ - Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{ - Name: "envoy.http_connection_manager", - }, - }, - }, - }, - }, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_INSERT_FIRST, - Value: &structpb.Struct{}, - }, - }, - }, - }, error: "", warning: "using deprecated filter name"}, - // Regression test for https://github.com/golang/protobuf/issues/1374 - {name: "duration marshal", in: &networking.EnvoyFilter{ - ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{ - { - ApplyTo: networking.EnvoyFilter_CLUSTER, - Patch: &networking.EnvoyFilter_Patch{ - Operation: networking.EnvoyFilter_Patch_ADD, - Value: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "dns_refresh_rate": { - Kind: &structpb.Value_StringValue{ - StringValue: "500ms", - }, - }, - }, - }, - }, - }, - }, - }, error: "", warning: ""}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - warn, err := ValidateEnvoyFilter(config.Config{ - Meta: config.Meta{ - Name: someName, - Namespace: someNamespace, - }, - Spec: tt.in, - }) - checkValidationMessage(t, warn, err, tt.warning, tt.error) - }) - } -} - -func TestValidateServiceEntries(t *testing.T) { - cases := []struct { - name string - in *networking.ServiceEntry - valid bool - warning bool - }{ - { - name: "discovery type DNS", in: &networking.ServiceEntry{ - Hosts: []string{"*.google.com"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - {Number: 8080, Protocol: "http", Name: "http-valid2"}, - }, - Endpoints: []*networking.WorkloadEntry{ - {Address: "lon.google.com", Ports: map[string]uint32{"http-valid1": 8080}}, - {Address: "in.google.com", Ports: map[string]uint32{"http-valid2": 9080}}, - }, - Resolution: networking.ServiceEntry_DNS, - }, - valid: true, - }, - { - name: "discovery type DNS Round Robin", in: &networking.ServiceEntry{ - Hosts: []string{"*.istio.io"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - {Number: 8080, Protocol: "http", Name: "http-valid2"}, - }, - Endpoints: []*networking.WorkloadEntry{ - {Address: "api-v1.istio.io", Ports: map[string]uint32{"http-valid1": 8080}}, - {Address: "api-v2.istio.io", Ports: map[string]uint32{"http-valid2": 9080}}, - }, - Resolution: networking.ServiceEntry_DNS_ROUND_ROBIN, - }, - valid: true, - }, - { - name: "discovery type DNS, label tlsMode: istio", in: &networking.ServiceEntry{ - Hosts: []string{"*.google.com"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - {Number: 8080, Protocol: "http", Name: "http-valid2"}, - }, - Endpoints: []*networking.WorkloadEntry{ - {Address: "lon.google.com", Ports: map[string]uint32{"http-valid1": 8080}, Labels: map[string]string{"security.istio.io/tlsMode": "istio"}}, - {Address: "in.google.com", Ports: map[string]uint32{"http-valid2": 9080}, Labels: map[string]string{"security.istio.io/tlsMode": "istio"}}, - }, - Resolution: networking.ServiceEntry_DNS, - }, - valid: true, - }, - - { - name: "discovery type DNS, one host set with IP address and https port", - in: &networking.ServiceEntry{ - Hosts: []string{"httpbin.org"}, - Addresses: []string{"10.10.10.10"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - {Number: 8080, Protocol: "http", Name: "http-valid2"}, - {Number: 443, Protocol: "https", Name: "https"}, - }, - Resolution: networking.ServiceEntry_DNS, - }, - valid: true, - warning: false, - }, - - { - name: "discovery type DNS, multi hosts set with IP address and https port", - in: &networking.ServiceEntry{ - Hosts: []string{"httpbin.org", "wikipedia.org"}, - Addresses: []string{"10.10.10.10"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - {Number: 8080, Protocol: "http", Name: "http-valid2"}, - {Number: 443, Protocol: "https", Name: "https"}, - }, - Resolution: networking.ServiceEntry_DNS, - }, - valid: true, - warning: true, - }, - - { - name: "discovery type DNS, IP address set", - in: &networking.ServiceEntry{ - Hosts: []string{"*.google.com"}, - Addresses: []string{"10.10.10.10"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - {Number: 8080, Protocol: "http", Name: "http-valid2"}, - }, - Endpoints: []*networking.WorkloadEntry{ - {Address: "lon.google.com", Ports: map[string]uint32{"http-valid1": 8080}}, - {Address: "in.google.com", Ports: map[string]uint32{"http-valid2": 9080}}, - }, - Resolution: networking.ServiceEntry_DNS, - }, - valid: true, - warning: false, - }, - - { - name: "discovery type DNS, IP in endpoints", in: &networking.ServiceEntry{ - Hosts: []string{"*.google.com"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - {Number: 8080, Protocol: "http", Name: "http-valid2"}, - }, - Endpoints: []*networking.WorkloadEntry{ - {Address: "1.1.1.1", Ports: map[string]uint32{"http-valid1": 8080}}, - {Address: "in.google.com", Ports: map[string]uint32{"http-valid2": 9080}}, - }, - Resolution: networking.ServiceEntry_DNS, - }, - valid: true, - }, - - { - name: "empty hosts", in: &networking.ServiceEntry{ - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - }, - Endpoints: []*networking.WorkloadEntry{ - {Address: "in.google.com", Ports: map[string]uint32{"http-valid2": 9080}}, - }, - Resolution: networking.ServiceEntry_DNS, - }, - valid: false, - }, - - { - name: "bad hosts", in: &networking.ServiceEntry{ - Hosts: []string{"-"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - }, - Endpoints: []*networking.WorkloadEntry{ - {Address: "in.google.com", Ports: map[string]uint32{"http-valid2": 9080}}, - }, - Resolution: networking.ServiceEntry_DNS, - }, - valid: false, - }, - { - name: "full wildcard host", in: &networking.ServiceEntry{ - Hosts: []string{"foo.com", "*"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - }, - Endpoints: []*networking.WorkloadEntry{ - {Address: "in.google.com", Ports: map[string]uint32{"http-valid2": 9080}}, - }, - Resolution: networking.ServiceEntry_DNS, - }, - valid: false, - }, - { - name: "short name host", in: &networking.ServiceEntry{ - Hosts: []string{"foo", "bar.com"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - }, - Endpoints: []*networking.WorkloadEntry{ - {Address: "in.google.com", Ports: map[string]uint32{"http-valid1": 9080}}, - }, - Resolution: networking.ServiceEntry_DNS, - }, - valid: true, - }, - { - name: "undefined endpoint port", in: &networking.ServiceEntry{ - Hosts: []string{"google.com"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - {Number: 80, Protocol: "http", Name: "http-valid2"}, - }, - Endpoints: []*networking.WorkloadEntry{ - {Address: "lon.google.com", Ports: map[string]uint32{"http-valid1": 8080}}, - {Address: "in.google.com", Ports: map[string]uint32{"http-dne": 9080}}, - }, - Resolution: networking.ServiceEntry_DNS, - }, - valid: false, - }, - - { - name: "discovery type DNS, non-FQDN endpoint", in: &networking.ServiceEntry{ - Hosts: []string{"*.google.com"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - {Number: 8080, Protocol: "http", Name: "http-valid2"}, - }, - Endpoints: []*networking.WorkloadEntry{ - {Address: "*.lon.google.com", Ports: map[string]uint32{"http-valid1": 8080}}, - {Address: "in.google.com", Ports: map[string]uint32{"http-dne": 9080}}, - }, - Resolution: networking.ServiceEntry_DNS, - }, - valid: false, - }, - - { - name: "discovery type DNS, non-FQDN host", in: &networking.ServiceEntry{ - Hosts: []string{"*.google.com"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - {Number: 8080, Protocol: "http", Name: "http-valid2"}, - }, - - Resolution: networking.ServiceEntry_DNS, - }, - valid: false, - }, - - { - name: "discovery type DNS, no endpoints", in: &networking.ServiceEntry{ - Hosts: []string{"google.com"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - {Number: 8080, Protocol: "http", Name: "http-valid2"}, - }, - - Resolution: networking.ServiceEntry_DNS, - }, - valid: true, - }, - - { - name: "discovery type DNS, unix endpoint", in: &networking.ServiceEntry{ - Hosts: []string{"*.google.com"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - }, - Endpoints: []*networking.WorkloadEntry{ - {Address: "unix:///lon/google/com"}, - }, - Resolution: networking.ServiceEntry_DNS, - }, - valid: false, - }, - - { - name: "discovery type none", in: &networking.ServiceEntry{ - Hosts: []string{"google.com"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - {Number: 8080, Protocol: "http", Name: "http-valid2"}, - }, - Resolution: networking.ServiceEntry_NONE, - }, - valid: true, - }, - - { - name: "discovery type none, endpoints provided", in: &networking.ServiceEntry{ - Hosts: []string{"google.com"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - {Number: 8080, Protocol: "http", Name: "http-valid2"}, - }, - Endpoints: []*networking.WorkloadEntry{ - {Address: "lon.google.com", Ports: map[string]uint32{"http-valid1": 8080}}, - }, - Resolution: networking.ServiceEntry_NONE, - }, - valid: false, - }, - - { - name: "discovery type none, cidr addresses", in: &networking.ServiceEntry{ - Hosts: []string{"google.com"}, - Addresses: []string{"172.1.2.16/16"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - {Number: 8080, Protocol: "http", Name: "http-valid2"}, - }, - Resolution: networking.ServiceEntry_NONE, - }, - valid: true, - }, - - { - name: "discovery type static, cidr addresses with endpoints", in: &networking.ServiceEntry{ - Hosts: []string{"google.com"}, - Addresses: []string{"172.1.2.16/16"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - {Number: 8080, Protocol: "http", Name: "http-valid2"}, - }, - Endpoints: []*networking.WorkloadEntry{ - {Address: "1.1.1.1", Ports: map[string]uint32{"http-valid1": 8080}}, - {Address: "2.2.2.2", Ports: map[string]uint32{"http-valid2": 9080}}, - }, - Resolution: networking.ServiceEntry_STATIC, - }, - valid: true, - }, - - { - name: "discovery type static", in: &networking.ServiceEntry{ - Hosts: []string{"google.com"}, - Addresses: []string{"172.1.2.16"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - {Number: 8080, Protocol: "http", Name: "http-valid2"}, - }, - Endpoints: []*networking.WorkloadEntry{ - {Address: "1.1.1.1", Ports: map[string]uint32{"http-valid1": 8080}}, - {Address: "2.2.2.2", Ports: map[string]uint32{"http-valid2": 9080}}, - }, - Resolution: networking.ServiceEntry_STATIC, - }, - valid: true, - }, - - { - name: "discovery type static, FQDN in endpoints", in: &networking.ServiceEntry{ - Hosts: []string{"google.com"}, - Addresses: []string{"172.1.2.16"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - {Number: 8080, Protocol: "http", Name: "http-valid2"}, - }, - Endpoints: []*networking.WorkloadEntry{ - {Address: "google.com", Ports: map[string]uint32{"http-valid1": 8080}}, - {Address: "2.2.2.2", Ports: map[string]uint32{"http-valid2": 9080}}, - }, - Resolution: networking.ServiceEntry_STATIC, - }, - valid: false, - }, - - { - name: "discovery type static, missing endpoints", in: &networking.ServiceEntry{ - Hosts: []string{"google.com"}, - Addresses: []string{"172.1.2.16"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - {Number: 8080, Protocol: "http", Name: "http-valid2"}, - }, - Resolution: networking.ServiceEntry_STATIC, - }, - valid: true, - }, - - { - name: "discovery type static, bad endpoint port name", in: &networking.ServiceEntry{ - Hosts: []string{"google.com"}, - Addresses: []string{"172.1.2.16"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - {Number: 8080, Protocol: "http", Name: "http-valid2"}, - }, - Endpoints: []*networking.WorkloadEntry{ - {Address: "1.1.1.1", Ports: map[string]uint32{"http-valid1": 8080}}, - {Address: "2.2.2.2", Ports: map[string]uint32{"http-dne": 9080}}, - }, - Resolution: networking.ServiceEntry_STATIC, - }, - valid: false, - }, - - { - name: "discovery type none, conflicting port names", in: &networking.ServiceEntry{ - Hosts: []string{"google.com"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-conflict"}, - {Number: 8080, Protocol: "http", Name: "http-conflict"}, - }, - Resolution: networking.ServiceEntry_NONE, - }, - valid: false, - }, - - { - name: "discovery type none, conflicting port numbers", in: &networking.ServiceEntry{ - Hosts: []string{"google.com"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-conflict1"}, - {Number: 80, Protocol: "http", Name: "http-conflict2"}, - }, - Resolution: networking.ServiceEntry_NONE, - }, - valid: false, - }, - - { - name: "unix socket", in: &networking.ServiceEntry{ - Hosts: []string{"uds.cluster.local"}, - Ports: []*networking.Port{ - {Number: 6553, Protocol: "grpc", Name: "grpc-service1"}, - }, - Resolution: networking.ServiceEntry_STATIC, - Endpoints: []*networking.WorkloadEntry{ - {Address: "unix:///path/to/socket"}, - }, - }, - valid: true, - }, - - { - name: "unix socket, relative path", in: &networking.ServiceEntry{ - Hosts: []string{"uds.cluster.local"}, - Ports: []*networking.Port{ - {Number: 6553, Protocol: "grpc", Name: "grpc-service1"}, - }, - Resolution: networking.ServiceEntry_STATIC, - Endpoints: []*networking.WorkloadEntry{ - {Address: "unix://./relative/path.sock"}, - }, - }, - valid: false, - }, - - { - name: "unix socket, endpoint ports", in: &networking.ServiceEntry{ - Hosts: []string{"uds.cluster.local"}, - Ports: []*networking.Port{ - {Number: 6553, Protocol: "grpc", Name: "grpc-service1"}, - }, - Resolution: networking.ServiceEntry_STATIC, - Endpoints: []*networking.WorkloadEntry{ - {Address: "unix:///path/to/socket", Ports: map[string]uint32{"grpc-service1": 6553}}, - }, - }, - valid: false, - }, - - { - name: "unix socket, multiple service ports", in: &networking.ServiceEntry{ - Hosts: []string{"uds.cluster.local"}, - Ports: []*networking.Port{ - {Number: 6553, Protocol: "grpc", Name: "grpc-service1"}, - {Number: 80, Protocol: "http", Name: "http-service2"}, - }, - Resolution: networking.ServiceEntry_STATIC, - Endpoints: []*networking.WorkloadEntry{ - {Address: "unix:///path/to/socket"}, - }, - }, - valid: false, - }, - { - name: "empty protocol", in: &networking.ServiceEntry{ - Hosts: []string{"google.com"}, - Addresses: []string{"172.1.2.16/16"}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - {Number: 8080, Name: "http-valid2"}, - }, - Resolution: networking.ServiceEntry_NONE, - }, - valid: true, - }, - { - name: "selector", in: &networking.ServiceEntry{ - Hosts: []string{"google.com"}, - WorkloadSelector: &networking.WorkloadSelector{Labels: map[string]string{"foo": "bar"}}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - }, - }, - valid: true, - }, - { - name: "selector and endpoints", in: &networking.ServiceEntry{ - Hosts: []string{"google.com"}, - WorkloadSelector: &networking.WorkloadSelector{Labels: map[string]string{"foo": "bar"}}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - }, - Endpoints: []*networking.WorkloadEntry{ - {Address: "1.1.1.1"}, - }, - }, - valid: false, - }, - { - name: "bad selector key", in: &networking.ServiceEntry{ - Hosts: []string{"google.com"}, - WorkloadSelector: &networking.WorkloadSelector{Labels: map[string]string{"": "bar"}}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1"}, - }, - }, - valid: false, - }, - { - name: "repeat target port", in: &networking.ServiceEntry{ - Hosts: []string{"google.com"}, - WorkloadSelector: &networking.WorkloadSelector{Labels: map[string]string{"key": "bar"}}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1", TargetPort: 80}, - {Number: 81, Protocol: "http", Name: "http-valid2", TargetPort: 80}, - }, - }, - valid: true, - }, - { - name: "valid target port", in: &networking.ServiceEntry{ - Hosts: []string{"google.com"}, - WorkloadSelector: &networking.WorkloadSelector{Labels: map[string]string{"key": "bar"}}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1", TargetPort: 81}, - }, - }, - valid: true, - }, - { - name: "invalid target port", in: &networking.ServiceEntry{ - Hosts: []string{"google.com"}, - WorkloadSelector: &networking.WorkloadSelector{Labels: map[string]string{"key": "bar"}}, - Ports: []*networking.Port{ - {Number: 80, Protocol: "http", Name: "http-valid1", TargetPort: 65536}, - }, - }, - valid: false, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - warning, err := ValidateServiceEntry(config.Config{ - Meta: config.Meta{ - Name: someName, - Namespace: someNamespace, - }, - Spec: c.in, - }) - if (err == nil) != c.valid { - t.Errorf("ValidateServiceEntry got valid=%v but wanted valid=%v: %v", - err == nil, c.valid, err) - } - if (warning != nil) != c.warning { - t.Errorf("ValidateServiceEntry got warning=%v but wanted warning=%v: %v", - warning != nil, c.warning, warning) - } - }) - } -} - -func TestValidateAuthorizationPolicy(t *testing.T) { - cases := []struct { - name string - annotations map[string]string - in proto.Message - valid bool - }{ - { - name: "good", - in: &security_beta.AuthorizationPolicy{ - Selector: &api.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - "version": "v1", - }, - }, - Rules: []*security_beta.Rule{ - { - From: []*security_beta.Rule_From{ - { - Source: &security_beta.Source{ - Principals: []string{"sa1"}, - }, - }, - { - Source: &security_beta.Source{ - Principals: []string{"sa2"}, - }, - }, - }, - To: []*security_beta.Rule_To{ - { - Operation: &security_beta.Operation{ - Methods: []string{"GET"}, - }, - }, - { - Operation: &security_beta.Operation{ - Methods: []string{"POST"}, - }, - }, - }, - When: []*security_beta.Condition{ - { - Key: "source.ip", - Values: []string{"1.2.3.4", "5.6.7.0/24"}, - }, - { - Key: "request.headers[:authority]", - Values: []string{"v1", "v2"}, - }, - }, - }, - }, - }, - valid: true, - }, - { - name: "custom-good", - in: &security_beta.AuthorizationPolicy{ - Action: security_beta.AuthorizationPolicy_CUSTOM, - ActionDetail: &security_beta.AuthorizationPolicy_Provider{ - Provider: &security_beta.AuthorizationPolicy_ExtensionProvider{ - Name: "my-custom-authz", - }, - }, - Rules: []*security_beta.Rule{{}}, - }, - valid: true, - }, - { - name: "custom-empty-provider", - in: &security_beta.AuthorizationPolicy{ - Action: security_beta.AuthorizationPolicy_CUSTOM, - ActionDetail: &security_beta.AuthorizationPolicy_Provider{ - Provider: &security_beta.AuthorizationPolicy_ExtensionProvider{ - Name: "", - }, - }, - Rules: []*security_beta.Rule{{}}, - }, - valid: false, - }, - { - name: "custom-nil-provider", - in: &security_beta.AuthorizationPolicy{ - Action: security_beta.AuthorizationPolicy_CUSTOM, - Rules: []*security_beta.Rule{{}}, - }, - valid: false, - }, - { - name: "custom-invalid-rule", - in: &security_beta.AuthorizationPolicy{ - Action: security_beta.AuthorizationPolicy_CUSTOM, - ActionDetail: &security_beta.AuthorizationPolicy_Provider{ - Provider: &security_beta.AuthorizationPolicy_ExtensionProvider{ - Name: "my-custom-authz", - }, - }, - Rules: []*security_beta.Rule{ - { - From: []*security_beta.Rule_From{ - { - Source: &security_beta.Source{ - Namespaces: []string{"ns"}, - NotNamespaces: []string{"ns"}, - Principals: []string{"id"}, - NotPrincipals: []string{"id"}, - RequestPrincipals: []string{"req"}, - NotRequestPrincipals: []string{"req"}, - }, - }, - }, - When: []*security_beta.Condition{ - { - Key: "source.namespace", - Values: []string{"source.namespace1"}, - NotValues: []string{"source.namespace2"}, - }, - { - Key: "source.principal", - Values: []string{"source.principal1"}, - NotValues: []string{"source.principal2"}, - }, - { - Key: "request.auth.claims[a]", - Values: []string{"claims1"}, - NotValues: []string{"claims2"}, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "provider-wrong-action", - in: &security_beta.AuthorizationPolicy{ - Action: security_beta.AuthorizationPolicy_ALLOW, - ActionDetail: &security_beta.AuthorizationPolicy_Provider{ - Provider: &security_beta.AuthorizationPolicy_ExtensionProvider{ - Name: "", - }, - }, - Rules: []*security_beta.Rule{{}}, - }, - valid: false, - }, - { - name: "allow-rules-nil", - in: &security_beta.AuthorizationPolicy{ - Action: security_beta.AuthorizationPolicy_ALLOW, - }, - valid: true, - }, - { - name: "dry-run-valid-allow", - annotations: map[string]string{"istio.io/dry-run": "true"}, - in: &security_beta.AuthorizationPolicy{ - Action: security_beta.AuthorizationPolicy_ALLOW, - }, - valid: true, - }, - { - name: "dry-run-valid-deny", - annotations: map[string]string{"istio.io/dry-run": "false"}, - in: &security_beta.AuthorizationPolicy{ - Action: security_beta.AuthorizationPolicy_DENY, - Rules: []*security_beta.Rule{{}}, - }, - valid: true, - }, - { - name: "dry-run-invalid-value", - annotations: map[string]string{"istio.io/dry-run": "foo"}, - in: &security_beta.AuthorizationPolicy{ - Action: security_beta.AuthorizationPolicy_ALLOW, - }, - valid: false, - }, - { - name: "dry-run-invalid-action-custom", - annotations: map[string]string{"istio.io/dry-run": "true"}, - in: &security_beta.AuthorizationPolicy{ - Action: security_beta.AuthorizationPolicy_CUSTOM, - }, - valid: false, - }, - { - name: "dry-run-invalid-action-audit", - annotations: map[string]string{"istio.io/dry-run": "true"}, - in: &security_beta.AuthorizationPolicy{ - Action: security_beta.AuthorizationPolicy_AUDIT, - }, - valid: false, - }, - { - name: "deny-rules-nil", - in: &security_beta.AuthorizationPolicy{ - Action: security_beta.AuthorizationPolicy_DENY, - }, - valid: false, - }, - { - name: "selector-empty-value", - in: &security_beta.AuthorizationPolicy{ - Selector: &api.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "", - "version": "v1", - }, - }, - }, - valid: true, - }, - { - name: "selector-empty-key", - in: &security_beta.AuthorizationPolicy{ - Selector: &api.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - "": "v1", - }, - }, - }, - valid: false, - }, - { - name: "selector-wildcard-value", - in: &security_beta.AuthorizationPolicy{ - Selector: &api.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin-*", - }, - }, - }, - valid: false, - }, - { - name: "selector-wildcard-key", - in: &security_beta.AuthorizationPolicy{ - Selector: &api.WorkloadSelector{ - MatchLabels: map[string]string{ - "app-*": "httpbin", - }, - }, - }, - valid: false, - }, - { - name: "from-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - From: []*security_beta.Rule_From{}, - }, - }, - }, - valid: false, - }, - { - name: "source-nil", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - From: []*security_beta.Rule_From{ - {}, - }, - }, - }, - }, - valid: false, - }, - { - name: "source-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - From: []*security_beta.Rule_From{ - { - Source: &security_beta.Source{}, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "to-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - To: []*security_beta.Rule_To{}, - }, - }, - }, - valid: false, - }, - { - name: "operation-nil", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - To: []*security_beta.Rule_To{ - {}, - }, - }, - }, - }, - valid: false, - }, - { - name: "operation-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - To: []*security_beta.Rule_To{ - { - Operation: &security_beta.Operation{}, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "Principals-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - From: []*security_beta.Rule_From{ - { - Source: &security_beta.Source{ - Principals: []string{"p1", ""}, - }, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "NotPrincipals-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - From: []*security_beta.Rule_From{ - { - Source: &security_beta.Source{ - NotPrincipals: []string{"p1", ""}, - }, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "RequestPrincipals-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - From: []*security_beta.Rule_From{ - { - Source: &security_beta.Source{ - RequestPrincipals: []string{"p1", ""}, - }, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "NotRequestPrincipals-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - From: []*security_beta.Rule_From{ - { - Source: &security_beta.Source{ - NotRequestPrincipals: []string{"p1", ""}, - }, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "Namespaces-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - From: []*security_beta.Rule_From{ - { - Source: &security_beta.Source{ - Namespaces: []string{"ns", ""}, - }, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "NotNamespaces-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - From: []*security_beta.Rule_From{ - { - Source: &security_beta.Source{ - NotNamespaces: []string{"ns", ""}, - }, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "IpBlocks-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - From: []*security_beta.Rule_From{ - { - Source: &security_beta.Source{ - IpBlocks: []string{"1.2.3.4", ""}, - }, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "NotIpBlocks-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - From: []*security_beta.Rule_From{ - { - Source: &security_beta.Source{ - NotIpBlocks: []string{"1.2.3.4", ""}, - }, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "RemoteIpBlocks-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - From: []*security_beta.Rule_From{ - { - Source: &security_beta.Source{ - RemoteIpBlocks: []string{"1.2.3.4", ""}, - }, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "NotRemoteIpBlocks-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - From: []*security_beta.Rule_From{ - { - Source: &security_beta.Source{ - NotRemoteIpBlocks: []string{"1.2.3.4", ""}, - }, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "Hosts-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - To: []*security_beta.Rule_To{ - { - Operation: &security_beta.Operation{ - Hosts: []string{"host", ""}, - }, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "NotHosts-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - To: []*security_beta.Rule_To{ - { - Operation: &security_beta.Operation{ - NotHosts: []string{"host", ""}, - }, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "Ports-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - To: []*security_beta.Rule_To{ - { - Operation: &security_beta.Operation{ - Ports: []string{"80", ""}, - }, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "NotPorts-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - To: []*security_beta.Rule_To{ - { - Operation: &security_beta.Operation{ - NotPorts: []string{"80", ""}, - }, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "Methods-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - To: []*security_beta.Rule_To{ - { - Operation: &security_beta.Operation{ - Methods: []string{"GET", ""}, - }, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "NotMethods-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - To: []*security_beta.Rule_To{ - { - Operation: &security_beta.Operation{ - NotMethods: []string{"GET", ""}, - }, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "Paths-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - To: []*security_beta.Rule_To{ - { - Operation: &security_beta.Operation{ - Paths: []string{"/path", ""}, - }, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "NotPaths-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - To: []*security_beta.Rule_To{ - { - Operation: &security_beta.Operation{ - NotPaths: []string{"/path", ""}, - }, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "value-empty", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - When: []*security_beta.Condition{ - { - Key: "request.headers[:authority]", - Values: []string{"v1", ""}, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "invalid ip and port in ipBlocks", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - From: []*security_beta.Rule_From{ - { - Source: &security_beta.Source{ - IpBlocks: []string{"1.2.3.4", "ip1"}, - NotIpBlocks: []string{"5.6.7.8", "ip2"}, - }, - }, - }, - To: []*security_beta.Rule_To{ - { - Operation: &security_beta.Operation{ - Ports: []string{"80", "port1"}, - NotPorts: []string{"90", "port2"}, - }, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "invalid ip and port in remoteIpBlocks", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - From: []*security_beta.Rule_From{ - { - Source: &security_beta.Source{ - RemoteIpBlocks: []string{"1.2.3.4", "ip1"}, - NotRemoteIpBlocks: []string{"5.6.7.8", "ip2"}, - }, - }, - }, - To: []*security_beta.Rule_To{ - { - Operation: &security_beta.Operation{ - Ports: []string{"80", "port1"}, - NotPorts: []string{"90", "port2"}, - }, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "condition-key-missing", - in: &security_beta.AuthorizationPolicy{ - Selector: &api.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - }, - }, - Rules: []*security_beta.Rule{ - { - When: []*security_beta.Condition{ - { - Values: []string{"v1", "v2"}, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "condition-key-empty", - in: &security_beta.AuthorizationPolicy{ - Selector: &api.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - }, - }, - Rules: []*security_beta.Rule{ - { - When: []*security_beta.Condition{ - { - Key: "", - Values: []string{"v1", "v2"}, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "condition-value-missing", - in: &security_beta.AuthorizationPolicy{ - Selector: &api.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - }, - }, - Rules: []*security_beta.Rule{ - { - When: []*security_beta.Condition{ - { - Key: "source.principal", - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "condition-value-invalid", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - When: []*security_beta.Condition{ - { - Key: "source.ip", - Values: []string{"a.b.c.d"}, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "condition-notValue-invalid", - in: &security_beta.AuthorizationPolicy{ - Rules: []*security_beta.Rule{ - { - When: []*security_beta.Condition{ - { - Key: "source.ip", - NotValues: []string{"a.b.c.d"}, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "condition-unknown", - in: &security_beta.AuthorizationPolicy{ - Selector: &api.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - }, - }, - Rules: []*security_beta.Rule{ - { - When: []*security_beta.Condition{ - { - Key: "key1", - Values: []string{"v1"}, - }, - }, - }, - }, - }, - valid: false, - }, - } - - for _, c := range cases { - if _, got := ValidateAuthorizationPolicy(config.Config{ - Meta: config.Meta{ - Name: "name", - Namespace: "namespace", - Annotations: c.annotations, - }, - Spec: c.in, - }); (got == nil) != c.valid { - t.Errorf("got: %v\nwant: %v", got, c.valid) - } - } -} - -func TestValidateSidecar(t *testing.T) { - tests := []struct { - name string - in *networking.Sidecar - valid bool - warn bool - }{ - {"empty ingress and egress", &networking.Sidecar{}, false, false}, - {"default", &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"*/*"}, - }, - }, - }, true, false}, - {"import local namespace with wildcard", &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"./*"}, - }, - }, - }, true, false}, - {"import local namespace with fqdn", &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"./foo.com"}, - }, - }, - }, true, false}, - {"import nothing", &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"~/*"}, - }, - }, - }, true, false}, - {"bad egress host 1", &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"*"}, - }, - }, - }, false, false}, - {"bad egress host 2", &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"/"}, - }, - }, - }, false, false}, - {"empty egress host", &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{}, - }, - }, - }, false, false}, - {"multiple wildcard egress", &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{ - "*/foo.com", - }, - }, - { - Hosts: []string{ - "ns1/bar.com", - }, - }, - }, - }, false, false}, - {"wildcard egress not in end", &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{ - "*/foo.com", - }, - }, - { - Port: &networking.Port{ - Protocol: "http", - Number: 8080, - Name: "h8080", - }, - Hosts: []string{ - "ns1/bar.com", - }, - }, - }, - }, false, false}, - {"invalid Port", &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Protocol: "http1", - Number: 1000000, - Name: "", - }, - Hosts: []string{ - "ns1/bar.com", - }, - }, - }, - }, false, false}, - {"Port without name", &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Protocol: "http", - Number: 8080, - }, - Hosts: []string{ - "ns1/bar.com", - }, - }, - }, - }, true, false}, - {"UDS bind in outbound", &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Protocol: "http", - Number: 0, - Name: "uds", - }, - Hosts: []string{ - "ns1/bar.com", - }, - Bind: "unix:///@foo/bar/com", - }, - }, - }, true, false}, - {"UDS bind in inbound", &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{ - Protocol: "http", - Number: 0, - Name: "uds", - }, - Bind: "unix:///@foo/bar/com", - DefaultEndpoint: "127.0.0.1:9999", - }, - }, - }, false, false}, - {"UDS bind in outbound 2", &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Protocol: "http", - Number: 0, - Name: "uds", - }, - Hosts: []string{ - "ns1/bar.com", - }, - Bind: "unix:///foo/bar/com", - }, - }, - }, true, false}, - {"invalid bind", &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Protocol: "http", - Number: 0, - Name: "uds", - }, - Hosts: []string{ - "ns1/bar.com", - }, - Bind: "foobar:///@foo/bar/com", - }, - }, - }, false, false}, - {"invalid capture mode with uds bind", &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Protocol: "http", - Number: 0, - Name: "uds", - }, - Hosts: []string{ - "ns1/bar.com", - }, - Bind: "unix:///@foo/bar/com", - CaptureMode: networking.CaptureMode_IPTABLES, - }, - }, - }, false, false}, - {"duplicate UDS bind", &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Protocol: "http", - Number: 0, - Name: "uds", - }, - Hosts: []string{ - "ns1/bar.com", - }, - Bind: "unix:///@foo/bar/com", - }, - { - Port: &networking.Port{ - Protocol: "http", - Number: 0, - Name: "uds", - }, - Hosts: []string{ - "ns1/bar.com", - }, - Bind: "unix:///@foo/bar/com", - }, - }, - }, false, false}, - {"duplicate ports", &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Port: &networking.Port{ - Protocol: "http", - Number: 90, - Name: "foo", - }, - Hosts: []string{ - "ns1/bar.com", - }, - }, - { - Port: &networking.Port{ - Protocol: "tcp", - Number: 90, - Name: "tcp", - }, - Hosts: []string{ - "ns2/bar.com", - }, - }, - }, - }, false, false}, - {"ingress without port", &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - DefaultEndpoint: "127.0.0.1:110", - }, - }, - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"*/*"}, - }, - }, - }, false, false}, - {"ingress with duplicate ports", &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{ - Protocol: "http", - Number: 90, - Name: "foo", - }, - DefaultEndpoint: "127.0.0.1:110", - }, - { - Port: &networking.Port{ - Protocol: "tcp", - Number: 90, - Name: "bar", - }, - DefaultEndpoint: "127.0.0.1:110", - }, - }, - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"*/*"}, - }, - }, - }, false, false}, - {"ingress without default endpoint", &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{ - Protocol: "http", - Number: 90, - Name: "foo", - }, - }, - }, - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"*/*"}, - }, - }, - }, true, false}, - {"ingress with invalid default endpoint IP", &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{ - Protocol: "http", - Number: 90, - Name: "foo", - }, - DefaultEndpoint: "1.1.1.1:90", - }, - }, - }, false, false}, - {"ingress with invalid default endpoint uds", &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{ - Protocol: "http", - Number: 90, - Name: "foo", - }, - DefaultEndpoint: "unix:///", - }, - }, - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"*/*"}, - }, - }, - }, false, false}, - {"ingress with invalid default endpoint port", &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{ - Protocol: "http", - Number: 90, - Name: "foo", - }, - DefaultEndpoint: "127.0.0.1:hi", - }, - }, - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"*/*"}, - }, - }, - }, false, false}, - {"valid ingress and egress", &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{ - Protocol: "http", - Number: 90, - Name: "foo", - }, - DefaultEndpoint: "127.0.0.1:9999", - }, - }, - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"*/*"}, - }, - }, - }, true, false}, - {"valid ingress and empty egress", &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{ - Protocol: "http", - Number: 90, - Name: "foo", - }, - DefaultEndpoint: "127.0.0.1:9999", - }, - }, - }, true, false}, - {"empty", &networking.Sidecar{}, false, false}, - {"just outbound traffic policy", &networking.Sidecar{OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, - }}, true, false}, - {"empty protocol", &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{ - Number: 90, - Name: "foo", - }, - DefaultEndpoint: "127.0.0.1:9999", - }, - }, - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"*/*"}, - }, - }, - }, true, false}, - {"ALLOW_ANY sidecar egress policy with no egress proxy ", &networking.Sidecar{ - OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, - }, - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"*/*"}, - }, - }, - }, true, false}, - {"sidecar egress proxy with RESGISTRY_ONLY(default)", &networking.Sidecar{ - OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - EgressProxy: &networking.Destination{ - Host: "foo.bar", - Subset: "shiny", - Port: &networking.PortSelector{ - Number: 5000, - }, - }, - }, - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"*/*"}, - }, - }, - }, false, false}, - {"sidecar egress proxy with ALLOW_ANY", &networking.Sidecar{ - OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, - EgressProxy: &networking.Destination{ - Host: "foo.bar", - Subset: "shiny", - Port: &networking.PortSelector{ - Number: 5000, - }, - }, - }, - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"*/*"}, - }, - }, - }, true, false}, - {"sidecar egress proxy with ALLOW_ANY, service hostname invalid fqdn", &networking.Sidecar{ - OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, - EgressProxy: &networking.Destination{ - Host: "foobar*123", - Subset: "shiny", - Port: &networking.PortSelector{ - Number: 5000, - }, - }, - }, - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"*/*"}, - }, - }, - }, false, false}, - {"sidecar egress proxy(without Port) with ALLOW_ANY", &networking.Sidecar{ - OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ - Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, - EgressProxy: &networking.Destination{ - Host: "foo.bar", - Subset: "shiny", - }, - }, - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{"*/*"}, - }, - }, - }, false, false}, - {"sidecar egress only one wildcarded", &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{ - "*/*", - "test/a.com", - }, - }, - }, - }, true, true}, - {"sidecar egress wildcarded ns", &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{ - "*/b.com", - "test/a.com", - }, - }, - }, - }, true, false}, - {"sidecar egress duplicated with wildcarded same namespace", &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{ - "test/*", - "test/a.com", - }, - }, - }, - }, true, true}, - {"sidecar egress duplicated with wildcarded same namespace .", &networking.Sidecar{ - Egress: []*networking.IstioEgressListener{ - { - Hosts: []string{ - "./*", - "bar/a.com", - }, - }, - }, - }, true, true}, - {"ingress tls mode set to ISTIO_MUTUAL", &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{ - Protocol: "http", - Number: 90, - Name: "foo", - }, - DefaultEndpoint: "127.0.0.1:9999", - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, - }, - }, - }, - }, false, false}, - {"ingress tls mode set to ISTIO_AUTO_PASSTHROUGH", &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{ - Protocol: "http", - Number: 90, - Name: "foo", - }, - DefaultEndpoint: "127.0.0.1:9999", - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_AUTO_PASSTHROUGH, - }, - }, - }, - }, false, false}, - {"ingress tls invalid protocol", &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{ - Protocol: "tcp", - Number: 90, - Name: "foo", - }, - DefaultEndpoint: "127.0.0.1:9999", - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - }, - }, - }, - }, false, false}, - {"ingress tls httpRedirect is not supported", &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{ - Protocol: "tcp", - Number: 90, - Name: "foo", - }, - DefaultEndpoint: "127.0.0.1:9999", - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - HttpsRedirect: true, - }, - }, - }, - }, false, false}, - {"ingress tls SAN entries are not supported", &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{ - Protocol: "tcp", - Number: 90, - Name: "foo", - }, - DefaultEndpoint: "127.0.0.1:9999", - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - SubjectAltNames: []string{"httpbin.com"}, - }, - }, - }, - }, false, false}, - {"ingress tls credentialName is not supported", &networking.Sidecar{ - Ingress: []*networking.IstioIngressListener{ - { - Port: &networking.Port{ - Protocol: "tcp", - Number: 90, - Name: "foo", - }, - DefaultEndpoint: "127.0.0.1:9999", - Tls: &networking.ServerTLSSettings{ - Mode: networking.ServerTLSSettings_SIMPLE, - CredentialName: "secret-name", - }, - }, - }, - }, false, false}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - warn, err := ValidateSidecar(config.Config{ - Meta: config.Meta{ - Name: "foo", - Namespace: "bar", - }, - Spec: tt.in, - }) - checkValidation(t, warn, err, tt.valid, tt.warn) - }) - } -} - -func TestValidateLocalityLbSetting(t *testing.T) { - cases := []struct { - name string - in *networking.LocalityLoadBalancerSetting - valid bool - }{ - { - name: "valid mesh config without LocalityLoadBalancerSetting", - in: nil, - valid: true, - }, - - { - name: "invalid LocalityLoadBalancerSetting_Distribute total weight > 100", - in: &networking.LocalityLoadBalancerSetting{ - Distribute: []*networking.LocalityLoadBalancerSetting_Distribute{ - { - From: "a/b/c", - To: map[string]uint32{ - "a/b/c": 80, - "a/b1": 25, - }, - }, - }, - }, - valid: false, - }, - { - name: "invalid LocalityLoadBalancerSetting_Distribute total weight < 100", - in: &networking.LocalityLoadBalancerSetting{ - Distribute: []*networking.LocalityLoadBalancerSetting_Distribute{ - { - From: "a/b/c", - To: map[string]uint32{ - "a/b/c": 80, - "a/b1": 15, - }, - }, - }, - }, - valid: false, - }, - { - name: "invalid LocalityLoadBalancerSetting_Distribute weight = 0", - in: &networking.LocalityLoadBalancerSetting{ - Distribute: []*networking.LocalityLoadBalancerSetting_Distribute{ - { - From: "a/b/c", - To: map[string]uint32{ - "a/b/c": 0, - "a/b1": 100, - }, - }, - }, - }, - valid: false, - }, - { - name: "invalid LocalityLoadBalancerSetting specify both distribute and failover", - in: &networking.LocalityLoadBalancerSetting{ - Distribute: []*networking.LocalityLoadBalancerSetting_Distribute{ - { - From: "a/b/c", - To: map[string]uint32{ - "a/b/c": 80, - "a/b1": 20, - }, - }, - }, - Failover: []*networking.LocalityLoadBalancerSetting_Failover{ - { - From: "region1", - To: "region2", - }, - }, - }, - valid: false, - }, - - { - name: "invalid failover src and dst have same region", - in: &networking.LocalityLoadBalancerSetting{ - Failover: []*networking.LocalityLoadBalancerSetting_Failover{ - { - From: "region1", - To: "region1", - }, - }, - }, - valid: false, - }, - { - name: "invalid failover src contain '*' wildcard", - in: &networking.LocalityLoadBalancerSetting{ - Failover: []*networking.LocalityLoadBalancerSetting_Failover{ - { - From: "*", - To: "region2", - }, - }, - }, - valid: false, - }, - { - name: "invalid failover dst contain '*' wildcard", - in: &networking.LocalityLoadBalancerSetting{ - Failover: []*networking.LocalityLoadBalancerSetting_Failover{ - { - From: "region1", - To: "*", - }, - }, - }, - valid: false, - }, - { - name: "invalid failover src contain '/' separator", - in: &networking.LocalityLoadBalancerSetting{ - Failover: []*networking.LocalityLoadBalancerSetting_Failover{ - { - From: "region1/zone1", - To: "region2", - }, - }, - }, - valid: false, - }, - { - name: "invalid failover dst contain '/' separator", - in: &networking.LocalityLoadBalancerSetting{ - Failover: []*networking.LocalityLoadBalancerSetting_Failover{ - { - From: "region1", - To: "region2/zone1", - }, - }, - }, - valid: false, - }, - } - - for _, c := range cases { - if got := validateLocalityLbSetting(c.in); (got == nil) != c.valid { - t.Errorf("ValidateLocalityLbSetting failed on %v: got valid=%v but wanted valid=%v: %v", - c.name, got == nil, c.valid, got) - } - } -} - -func TestValidateLocalities(t *testing.T) { - cases := []struct { - name string - localities []string - valid bool - }{ - { - name: "multi wildcard locality", - localities: []string{"*/zone/*"}, - valid: false, - }, - { - name: "wildcard not in suffix", - localities: []string{"*/zone"}, - valid: false, - }, - { - name: "explicit wildcard region overlap", - localities: []string{"*", "a/b/c"}, - valid: false, - }, - { - name: "implicit wildcard region overlap", - localities: []string{"a", "a/b/c"}, - valid: false, - }, - { - name: "explicit wildcard zone overlap", - localities: []string{"a/*", "a/b/c"}, - valid: false, - }, - { - name: "implicit wildcard zone overlap", - localities: []string{"a/b", "a/b/c"}, - valid: false, - }, - { - name: "explicit wildcard subzone overlap", - localities: []string{"a/b/*", "a/b/c"}, - valid: false, - }, - { - name: "implicit wildcard subzone overlap", - localities: []string{"a/b", "a/b/c"}, - valid: false, - }, - { - name: "valid localities", - localities: []string{"a1/*", "a2/*", "a3/b3/c3", "a4/b4", "a5/b5/*"}, - valid: true, - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - err := validateLocalities(c.localities) - if !c.valid && err == nil { - t.Errorf("expect invalid localities") - } - - if c.valid && err != nil { - t.Errorf("expect valid localities. but got err %v", err) - } - }) - } -} - -func TestValidationIPAddress(t *testing.T) { - tests := []struct { - name string - addr string - ok bool - }{ - { - name: "valid ipv4 address", - addr: "1.1.1.1", - ok: true, - }, - { - name: "invalid ipv4 address", - addr: "1.1.A.1", - ok: false, - }, - { - name: "valid ipv6 subnet", - addr: "2001:1::1", - ok: true, - }, - { - name: "invalid ipv6 address", - addr: "2001:1:G::1", - ok: false, - }, - } - for _, tt := range tests { - if err := ValidateIPAddress(tt.addr); err != nil { - if tt.ok { - t.Errorf("test: \"%s\" expected to succeed but failed with error: %+v", tt.name, err) - } - } else { - if !tt.ok { - t.Errorf("test: \"%s\" expected to fail but succeeded", tt.name) - } - } - } -} - -func TestValidationIPSubnet(t *testing.T) { - tests := []struct { - name string - subnet string - ok bool - }{ - { - name: "valid ipv4 subnet", - subnet: "1.1.1.1/24", - ok: true, - }, - { - name: "invalid ipv4 subnet", - subnet: "1.1.1.1/48", - ok: false, - }, - { - name: "valid ipv6 subnet", - subnet: "2001:1::1/64", - ok: true, - }, - { - name: "invalid ipv6 subnet", - subnet: "2001:1::1/132", - ok: false, - }, - } - - for _, tt := range tests { - if err := ValidateIPSubnet(tt.subnet); err != nil { - if tt.ok { - t.Errorf("test: \"%s\" expected to succeed but failed with error: %+v", tt.name, err) - } - } else { - if !tt.ok { - t.Errorf("test: \"%s\" expected to fail but succeeded", tt.name) - } - } - } -} - -func TestValidateRequestAuthentication(t *testing.T) { - cases := []struct { - name string - configName string - annotations map[string]string - in proto.Message - valid bool - }{ - { - name: "empty spec", - configName: constants.DefaultAuthenticationPolicyName, - in: &security_beta.RequestAuthentication{}, - valid: true, - }, - { - name: "another empty spec", - configName: constants.DefaultAuthenticationPolicyName, - in: &security_beta.RequestAuthentication{ - Selector: &api.WorkloadSelector{}, - }, - valid: true, - }, - { - name: "empty spec with non default name", - configName: someName, - in: &security_beta.RequestAuthentication{}, - valid: true, - }, - { - name: "dry run annotation not supported", - configName: someName, - annotations: map[string]string{"istio.io/dry-run": "true"}, - in: &security_beta.RequestAuthentication{}, - valid: false, - }, - { - name: "default name with non empty selector", - configName: constants.DefaultAuthenticationPolicyName, - in: &security_beta.RequestAuthentication{ - Selector: &api.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - }, - }, - }, - valid: true, - }, - { - name: "empty jwt rule", - configName: "foo", - in: &security_beta.RequestAuthentication{ - JwtRules: []*security_beta.JWTRule{ - {}, - }, - }, - valid: false, - }, - { - name: "empty issuer", - configName: "foo", - in: &security_beta.RequestAuthentication{ - JwtRules: []*security_beta.JWTRule{ - { - Issuer: "", - }, - }, - }, - valid: false, - }, - { - name: "bad JwksUri - no protocol", - configName: "foo", - in: &security_beta.RequestAuthentication{ - JwtRules: []*security_beta.JWTRule{ - { - Issuer: "foo.com", - JwksUri: "foo.com", - }, - }, - }, - valid: false, - }, - { - name: "bad JwksUri - invalid port", - configName: "foo", - in: &security_beta.RequestAuthentication{ - JwtRules: []*security_beta.JWTRule{ - { - Issuer: "foo.com", - JwksUri: "https://foo.com:not-a-number", - }, - }, - }, - valid: false, - }, - { - name: "empty value", - configName: "foo", - in: &security_beta.RequestAuthentication{ - Selector: &api.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - "version": "", - }, - }, - JwtRules: []*security_beta.JWTRule{ - { - Issuer: "foo.com", - JwksUri: "https://foo.com/cert", - }, - }, - }, - valid: true, - }, - { - name: "bad selector - empty key", - configName: "foo", - in: &security_beta.RequestAuthentication{ - Selector: &api.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - "": "v1", - }, - }, - JwtRules: []*security_beta.JWTRule{ - { - Issuer: "foo.com", - JwksUri: "https://foo.com/cert", - }, - }, - }, - valid: false, - }, - { - name: "bad header location", - configName: constants.DefaultAuthenticationPolicyName, - in: &security_beta.RequestAuthentication{ - JwtRules: []*security_beta.JWTRule{ - { - Issuer: "foo.com", - JwksUri: "https://foo.com", - FromHeaders: []*security_beta.JWTHeader{ - { - Name: "", - Prefix: "Bearer ", - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "bad param location", - configName: constants.DefaultAuthenticationPolicyName, - in: &security_beta.RequestAuthentication{ - JwtRules: []*security_beta.JWTRule{ - { - Issuer: "foo.com", - JwksUri: "https://foo.com", - FromParams: []string{""}, - }, - }, - }, - valid: false, - }, - { - name: "good", - configName: constants.DefaultAuthenticationPolicyName, - in: &security_beta.RequestAuthentication{ - JwtRules: []*security_beta.JWTRule{ - { - Issuer: "foo.com", - JwksUri: "https://foo.com", - FromHeaders: []*security_beta.JWTHeader{ - { - Name: "x-foo", - Prefix: "Bearer ", - }, - }, - }, - }, - }, - valid: true, - }, - { - name: "jwks ok", - configName: constants.DefaultAuthenticationPolicyName, - in: &security_beta.RequestAuthentication{ - JwtRules: []*security_beta.JWTRule{ - { - Issuer: "foo.com", - Jwks: "{ \"keys\":[ {\"e\":\"AQAB\",\"kid\":\"DHFbpoIUqrY8t2zpA2qXfCmr5VO5ZEr4RzHU_-envvQ\",\"kty\":\"RSA\",\"n\":\"xAE7eB6qugXyCAG3yhh7pkDkT65pHymX-P7KfIupjf59vsdo91bSP9C8H07pSAGQO1MV_xFj9VswgsCg4R6otmg5PV2He95lZdHtOcU5DXIg_pbhLdKXbi66GlVeK6ABZOUW3WYtnNHD-91gVuoeJT_DwtGGcp4ignkgXfkiEm4sw-4sfb4qdt5oLbyVpmW6x9cfa7vs2WTfURiCrBoUqgBo_-4WTiULmmHSGZHOjzwa8WtrtOQGsAFjIbno85jp6MnGGGZPYZbDAa_b3y5u-YpW7ypZrvD8BgtKVjgtQgZhLAGezMt0ua3DRrWnKqTZ0BJ_EyxOGuHJrLsn00fnMQ\"}]}", // nolint: lll - FromHeaders: []*security_beta.JWTHeader{ - { - Name: "x-foo", - Prefix: "Bearer ", - }, - }, - }, - }, - }, - valid: true, - }, - { - name: "jwks error", - configName: constants.DefaultAuthenticationPolicyName, - in: &security_beta.RequestAuthentication{ - JwtRules: []*security_beta.JWTRule{ - { - Issuer: "foo.com", - Jwks: "foo", - FromHeaders: []*security_beta.JWTHeader{ - { - Name: "x-foo", - Prefix: "Bearer ", - }, - }, - }, - }, - }, - valid: false, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - if _, got := ValidateRequestAuthentication(config.Config{ - Meta: config.Meta{ - Name: c.configName, - Namespace: someNamespace, - Annotations: c.annotations, - }, - Spec: c.in, - }); (got == nil) != c.valid { - t.Errorf("got(%v) != want(%v)\n", got, c.valid) - } - }) - } -} - -func TestValidatePeerAuthentication(t *testing.T) { - cases := []struct { - name string - configName string - in proto.Message - valid bool - }{ - { - name: "empty spec", - configName: constants.DefaultAuthenticationPolicyName, - in: &security_beta.PeerAuthentication{}, - valid: true, - }, - { - name: "empty mtls", - configName: constants.DefaultAuthenticationPolicyName, - in: &security_beta.PeerAuthentication{ - Selector: &api.WorkloadSelector{}, - }, - valid: true, - }, - { - name: "empty spec with non default name", - configName: someName, - in: &security_beta.PeerAuthentication{}, - valid: true, - }, - { - name: "default name with non empty selector", - configName: constants.DefaultAuthenticationPolicyName, - in: &security_beta.PeerAuthentication{ - Selector: &api.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - }, - }, - }, - valid: true, - }, - { - name: "empty port level mtls", - configName: "foo", - in: &security_beta.PeerAuthentication{ - Selector: &api.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - }, - }, - PortLevelMtls: map[uint32]*security_beta.PeerAuthentication_MutualTLS{}, - }, - valid: false, - }, - { - name: "empty selector with port level mtls", - configName: constants.DefaultAuthenticationPolicyName, - in: &security_beta.PeerAuthentication{ - PortLevelMtls: map[uint32]*security_beta.PeerAuthentication_MutualTLS{ - 8080: { - Mode: security_beta.PeerAuthentication_MutualTLS_UNSET, - }, - }, - }, - valid: false, - }, - { - name: "port 0", - configName: "foo", - in: &security_beta.PeerAuthentication{ - PortLevelMtls: map[uint32]*security_beta.PeerAuthentication_MutualTLS{ - 0: { - Mode: security_beta.PeerAuthentication_MutualTLS_UNSET, - }, - }, - }, - valid: false, - }, - { - name: "unset mode", - configName: constants.DefaultAuthenticationPolicyName, - in: &security_beta.PeerAuthentication{ - Mtls: &security_beta.PeerAuthentication_MutualTLS{ - Mode: security_beta.PeerAuthentication_MutualTLS_UNSET, - }, - }, - valid: true, - }, - { - name: "port level", - configName: "port-level", - in: &security_beta.PeerAuthentication{ - Selector: &api.WorkloadSelector{ - MatchLabels: map[string]string{ - "app": "httpbin", - }, - }, - PortLevelMtls: map[uint32]*security_beta.PeerAuthentication_MutualTLS{ - 8080: { - Mode: security_beta.PeerAuthentication_MutualTLS_UNSET, - }, - }, - }, - valid: true, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - if _, got := ValidatePeerAuthentication(config.Config{ - Meta: config.Meta{ - Name: c.configName, - Namespace: someNamespace, - }, - Spec: c.in, - }); (got == nil) != c.valid { - t.Errorf("got(%v) != want(%v)\n", got, c.valid) - } - }) - } -} - -func TestServiceSettings(t *testing.T) { - cases := []struct { - name string - hosts []string - valid bool - }{ - { - name: "good", - hosts: []string{ - "*.foo.bar", - "bar.baz.svc.cluster.local", - }, - valid: true, - }, - { - name: "bad", - hosts: []string{ - "foo.bar.*", - }, - valid: false, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - m := meshconfig.MeshConfig{ - ServiceSettings: []*meshconfig.MeshConfig_ServiceSettings{ - { - Hosts: c.hosts, - }, - }, - } - - if got := validateServiceSettings(&m); (got == nil) != c.valid { - t.Errorf("got(%v) != want(%v)\n", got, c.valid) - } - }) - } -} - -func TestValidateMeshNetworks(t *testing.T) { - testcases := []struct { - name string - mn *meshconfig.MeshNetworks - valid bool - }{ - { - name: "Empty MeshNetworks", - mn: &meshconfig.MeshNetworks{}, - valid: true, - }, - { - name: "Valid MeshNetworks", - mn: &meshconfig.MeshNetworks{ - Networks: map[string]*meshconfig.Network{ - "n1": { - Endpoints: []*meshconfig.Network_NetworkEndpoints{ - { - Ne: &meshconfig.Network_NetworkEndpoints_FromRegistry{ - FromRegistry: "Kubernetes", - }, - }, - }, - Gateways: []*meshconfig.Network_IstioNetworkGateway{ - { - Gw: &meshconfig.Network_IstioNetworkGateway_RegistryServiceName{ - RegistryServiceName: "istio-ingressgateway.dubbo-system.svc.cluster.local", - }, - Port: 80, - }, - }, - }, - "n2": { - Endpoints: []*meshconfig.Network_NetworkEndpoints{ - { - Ne: &meshconfig.Network_NetworkEndpoints_FromRegistry{ - FromRegistry: "cluster1", - }, - }, - }, - Gateways: []*meshconfig.Network_IstioNetworkGateway{ - { - Gw: &meshconfig.Network_IstioNetworkGateway_RegistryServiceName{ - RegistryServiceName: "istio-ingressgateway.dubbo-system.svc.cluster.local", - }, - Port: 443, - }, - }, - }, - }, - }, - valid: true, - }, - { - name: "Invalid Gateway Address", - mn: &meshconfig.MeshNetworks{ - Networks: map[string]*meshconfig.Network{ - "n1": { - Endpoints: []*meshconfig.Network_NetworkEndpoints{ - { - Ne: &meshconfig.Network_NetworkEndpoints_FromRegistry{ - FromRegistry: "Kubernetes", - }, - }, - }, - Gateways: []*meshconfig.Network_IstioNetworkGateway{ - { - Gw: &meshconfig.Network_IstioNetworkGateway_Address{ - Address: "1nv@lidhostname", - }, - Port: 80, - }, - }, - }, - }, - }, - valid: false, - }, - { - name: "Invalid registry name", - mn: &meshconfig.MeshNetworks{ - Networks: map[string]*meshconfig.Network{ - "n1": { - Endpoints: []*meshconfig.Network_NetworkEndpoints{ - { - Ne: &meshconfig.Network_NetworkEndpoints_FromRegistry{ - FromRegistry: "cluster.local", - }, - }, - }, - Gateways: []*meshconfig.Network_IstioNetworkGateway{ - { - Gw: &meshconfig.Network_IstioNetworkGateway_RegistryServiceName{ - RegistryServiceName: "istio-ingressgateway.dubbo-system.svc.cluster.local", - }, - Port: 80, - }, - }, - }, - }, - }, - valid: false, - }, - } - - for _, tc := range testcases { - t.Run(tc.name, func(t *testing.T) { - err := ValidateMeshNetworks(tc.mn) - if err != nil && tc.valid { - t.Errorf("error not expected on valid meshnetworks: %v", tc.mn) - } - if err == nil && !tc.valid { - t.Errorf("expected an error on invalid meshnetworks: %v", tc.mn) - } - }) - } -} - -func Test_validateExportTo(t *testing.T) { - tests := []struct { - name string - namespace string - exportTo []string - isDestinationRuleWithSelector bool - isServiceEntry bool - wantErr bool - }{ - { - name: "empty exportTo is okay", - namespace: "ns5", - wantErr: false, - }, - { - name: "* is allowed", - namespace: "ns5", - exportTo: []string{"*"}, - wantErr: false, - }, - { - name: ". and ns1 are allowed", - namespace: "ns5", - exportTo: []string{".", "ns1"}, - wantErr: false, - }, - { - name: "bunch of namespaces in exportTo is okay", - namespace: "ns5", - exportTo: []string{"ns1", "ns2", "ns5"}, - wantErr: false, - }, - { - name: "~ is allowed for service entry configs", - namespace: "ns5", - exportTo: []string{"~"}, - isServiceEntry: true, - wantErr: false, - }, - { - name: "~ not allowed for non service entry configs", - namespace: "ns5", - exportTo: []string{"~", "ns1"}, - wantErr: true, - }, - { - name: ". and * together are not allowed", - namespace: "ns5", - exportTo: []string{".", "*"}, - wantErr: true, - }, - { - name: "* and ns1 together are not allowed", - namespace: "ns5", - exportTo: []string{"*", "ns1"}, - wantErr: true, - }, - { - name: ". and same namespace in exportTo is not okay", - namespace: "ns5", - exportTo: []string{".", "ns5"}, - wantErr: true, - }, - { - name: "duplicate namespaces in exportTo is not okay", - namespace: "ns5", - exportTo: []string{"ns1", "ns2", "ns1"}, - wantErr: true, - }, - { - name: "duplicate none in service entry exportTo is not okay", - namespace: "ns5", - exportTo: []string{"~", "~", "ns1"}, - isServiceEntry: true, - wantErr: true, - }, - { - name: "invalid namespace names are not okay", - namespace: "ns5", - exportTo: []string{"ns1_"}, - wantErr: true, - }, - { - name: "none and other namespaces cannot be combined in service entry exportTo", - namespace: "ns5", - exportTo: []string{"~", "ns1"}, - isServiceEntry: true, - wantErr: true, - }, - { - name: "destination rule with workloadselector cannot have exportTo (*)", - namespace: "ns5", - exportTo: []string{"*"}, - isServiceEntry: false, - isDestinationRuleWithSelector: true, - wantErr: true, - }, - { - name: "destination rule with workloadselector can have only exportTo (.)", - namespace: "ns5", - exportTo: []string{"."}, - isServiceEntry: false, - isDestinationRuleWithSelector: true, - wantErr: false, - }, - { - name: "destination rule with workloadselector cannot have another ns in exportTo (.)", - namespace: "ns5", - exportTo: []string{"somens"}, - isServiceEntry: false, - isDestinationRuleWithSelector: true, - wantErr: true, - }, - { - name: "destination rule with workloadselector cannot have another ns in addition to own ns in exportTo (.)", - namespace: "ns5", - exportTo: []string{".", "somens"}, - isServiceEntry: false, - isDestinationRuleWithSelector: true, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := validateExportTo(tt.namespace, tt.exportTo, tt.isServiceEntry, tt.isDestinationRuleWithSelector); (err != nil) != tt.wantErr { - t.Errorf("validateExportTo() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - -func TestValidateTelemetry(t *testing.T) { - tests := []struct { - name string - in proto.Message - err string - warning string - }{ - {"empty", &telemetry.Telemetry{}, "", ""}, - {"invalid message", &networking.Server{}, "cannot cast", ""}, - { - "multiple providers", - &telemetry.Telemetry{ - Tracing: []*telemetry.Tracing{{ - Providers: []*telemetry.ProviderRef{ - {Name: "a"}, - {Name: "b"}, - }, - }}, - }, - "", "multiple providers", - }, - { - "multiple tracers", - &telemetry.Telemetry{ - Tracing: []*telemetry.Tracing{{}, {}}, - }, - "", "multiple tracing", - }, - { - "bad randomSamplingPercentage", - &telemetry.Telemetry{ - Tracing: []*telemetry.Tracing{{ - RandomSamplingPercentage: &wrapperspb.DoubleValue{Value: 101}, - }}, - }, - "randomSamplingPercentage", "", - }, - { - "tracing with a good custom tag", - &telemetry.Telemetry{ - Tracing: []*telemetry.Tracing{{ - CustomTags: map[string]*telemetry.Tracing_CustomTag{ - "clusterID": { - Type: &telemetry.Tracing_CustomTag_Environment{ - Environment: &telemetry.Tracing_Environment{ - Name: "FOO", - }, - }, - }, - }, - }}, - }, - "", "", - }, - { - "tracing with a nil custom tag", - &telemetry.Telemetry{ - Tracing: []*telemetry.Tracing{{ - CustomTags: map[string]*telemetry.Tracing_CustomTag{ - "clusterID": nil, - }, - }}, - }, - "tag 'clusterID' may not have a nil value", "", - }, - { - "bad metrics operation", - &telemetry.Telemetry{ - Metrics: []*telemetry.Metrics{{ - Overrides: []*telemetry.MetricsOverrides{ - { - TagOverrides: map[string]*telemetry.MetricsOverrides_TagOverride{ - "my-tag": { - Operation: telemetry.MetricsOverrides_TagOverride_UPSERT, - Value: "", - }, - }, - }, - }, - }}, - }, - "must be set set when operation is UPSERT", "", - }, - { - "good metrics operation", - &telemetry.Telemetry{ - Metrics: []*telemetry.Metrics{{ - Overrides: []*telemetry.MetricsOverrides{ - { - TagOverrides: map[string]*telemetry.MetricsOverrides_TagOverride{ - "my-tag": { - Operation: telemetry.MetricsOverrides_TagOverride_UPSERT, - Value: "some-cel-expression", - }, - }, - }, - }, - }}, - }, - "", "", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - warn, err := ValidateTelemetry(config.Config{ - Meta: config.Meta{ - Name: someName, - Namespace: someNamespace, - }, - Spec: tt.in, - }) - checkValidationMessage(t, warn, err, tt.warning, tt.err) - }) - } -} - -func TestValidateProxyConfig(t *testing.T) { - tests := []struct { - name string - in proto.Message - out string - warning string - }{ - {"empty", &networkingv1beta1.ProxyConfig{}, "", ""}, - {name: "invalid concurrency", in: &networkingv1beta1.ProxyConfig{ - Concurrency: &wrapperspb.Int32Value{Value: -1}, - }, out: "concurrency must be greater than or equal to 0"}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - warn, err := ValidateProxyConfig(config.Config{ - Meta: config.Meta{ - Name: someName, - Namespace: someNamespace, - }, - Spec: tt.in, - }) - checkValidationMessage(t, warn, err, tt.warning, tt.out) - }) - } -} - -func TestValidateTelemetryFilter(t *testing.T) { - cases := []struct { - filter *telemetry.AccessLogging_Filter - valid bool - }{ - { - filter: &telemetry.AccessLogging_Filter{ - Expression: "response.code >= 400", - }, - valid: true, - }, - { - filter: &telemetry.AccessLogging_Filter{ - Expression: "connection.mtls && request.url_path.contains('v1beta3')", - }, - valid: true, - }, - { - filter: &telemetry.AccessLogging_Filter{ - // TODO: find a better way to verify this - // this should be an invalid expression - Expression: "response.code", - }, - valid: true, - }, - { - filter: &telemetry.AccessLogging_Filter{ - Expression: ")++++", - }, - valid: false, - }, - } - - for _, tc := range cases { - t.Run("", func(t *testing.T) { - err := validateTelemetryFilter(tc.filter) - errFound := err != nil - if tc.valid && errFound { - t.Errorf("validateTelemetryFilter(%v) produced unexpected error: %v", tc.filter, err) - } - if !tc.valid && !errFound { - t.Errorf("validateTelemetryFilter(%v) did not produce expected error", tc.filter) - } - }) - } -} - -func TestValidateWasmPlugin(t *testing.T) { - tests := []struct { - name string - in proto.Message - out string - warning string - }{ - {"empty", &extensions.WasmPlugin{}, "url field needs to be set", ""}, - {"invalid message", &networking.Server{}, "cannot cast", ""}, - { - "wrong scheme", - &extensions.WasmPlugin{ - Url: "ftp://test.com/test", - }, - "unsupported scheme", "", - }, - { - "valid http", - &extensions.WasmPlugin{ - Url: "http://test.com/test", - }, - "", "", - }, - { - "valid http w/ sha", - &extensions.WasmPlugin{ - Url: "http://test.com/test", - Sha256: "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b", - }, - "", "", - }, - { - "short sha", - &extensions.WasmPlugin{ - Url: "http://test.com/test", - Sha256: "01ba47", - }, - "sha256 field must be 64 characters long", "", - }, - { - "invalid sha", - &extensions.WasmPlugin{ - Url: "http://test.com/test", - Sha256: "test", - }, - "sha256 field must be 64 characters long", "", - }, - { - "invalid sha characters", - &extensions.WasmPlugin{ - Url: "http://test.com/test", - Sha256: "01Ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b", - }, - "sha256 field must match [a-f0-9]{64} pattern", "", - }, - { - "valid oci", - &extensions.WasmPlugin{ - Url: "oci://test.com/test", - }, - "", "", - }, - { - "valid oci no scheme", - &extensions.WasmPlugin{ - Url: "test.com/test", - }, - "", "", - }, - { - "invalid vm config - invalid env name", - &extensions.WasmPlugin{ - Url: "test.com/test", - VmConfig: &extensions.VmConfig{ - Env: []*extensions.EnvVar{ - { - Name: "", - ValueFrom: extensions.EnvValueSource_HOST, - }, - }, - }, - }, - "spec.vmConfig.env invalid", "", - }, - { - "invalid vm config - duplicate env", - &extensions.WasmPlugin{ - Url: "test.com/test", - VmConfig: &extensions.VmConfig{ - Env: []*extensions.EnvVar{ - { - Name: "ENV1", - Value: "VAL1", - }, - { - Name: "ENV1", - Value: "VAL1", - }, - }, - }, - }, - "duplicate env", "", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - warn, err := ValidateWasmPlugin(config.Config{ - Meta: config.Meta{ - Name: someName, - Namespace: someNamespace, - }, - Spec: tt.in, - }) - checkValidationMessage(t, warn, err, tt.warning, tt.out) - }) - } -} - -func TestRecurseMissingTypedConfig(t *testing.T) { - good := &listener.Filter{ - Name: wellknown.TCPProxy, - ConfigType: &listener.Filter_TypedConfig{TypedConfig: nil}, - } - bad := &listener.Filter{ - Name: wellknown.TCPProxy, - } - assert.Equal(t, recurseMissingTypedConfig(good.ProtoReflect()), []string{}, "typed config set") - assert.Equal(t, recurseMissingTypedConfig(bad.ProtoReflect()), []string{wellknown.TCPProxy}, "typed config not set") -} diff --git a/pkg/config/validation/virtualservice.go b/pkg/config/validation/virtualservice.go deleted file mode 100644 index fcb8f4997..000000000 --- a/pkg/config/validation/virtualservice.go +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validation - -import ( - "errors" - "fmt" - "strings" -) - -import ( - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" -) - -type HTTPRouteType int - -const ( - IndependentRoute = iota - RootRoute - DelegateRoute -) - -func getHTTPRouteType(http *networking.HTTPRoute, isDelegate bool) HTTPRouteType { - if isDelegate { - return DelegateRoute - } - // root vs's http route - if http.Delegate != nil { - return RootRoute - } - return IndependentRoute -} - -func validateHTTPRoute(http *networking.HTTPRoute, delegate bool) (errs Validation) { - routeType := getHTTPRouteType(http, delegate) - // check for conflicts - errs = WrapError(validateHTTPRouteConflict(http, routeType)) - - // check http route match requests - errs = appendValidation(errs, validateHTTPRouteMatchRequest(http, routeType)) - - // header manipulation - for name, val := range http.Headers.GetRequest().GetAdd() { - errs = appendValidation(errs, ValidateHTTPHeaderWithAuthorityOperationName(name)) - errs = appendValidation(errs, ValidateHTTPHeaderValue(val)) - } - for name, val := range http.Headers.GetRequest().GetSet() { - errs = appendValidation(errs, ValidateHTTPHeaderWithAuthorityOperationName(name)) - errs = appendValidation(errs, ValidateHTTPHeaderValue(val)) - } - for _, name := range http.Headers.GetRequest().GetRemove() { - errs = appendValidation(errs, ValidateHTTPHeaderOperationName(name)) - } - for name, val := range http.Headers.GetResponse().GetAdd() { - errs = appendValidation(errs, ValidateHTTPHeaderOperationName(name)) - errs = appendValidation(errs, ValidateHTTPHeaderValue(val)) - } - for name, val := range http.Headers.GetResponse().GetSet() { - errs = appendValidation(errs, ValidateHTTPHeaderOperationName(name)) - errs = appendValidation(errs, ValidateHTTPHeaderValue(val)) - } - for _, name := range http.Headers.GetResponse().GetRemove() { - errs = appendValidation(errs, ValidateHTTPHeaderOperationName(name)) - } - - errs = appendValidation(errs, validateCORSPolicy(http.CorsPolicy)) - errs = appendValidation(errs, validateHTTPFaultInjection(http.Fault)) - - // nolint: staticcheck - if http.MirrorPercent != nil { - if value := http.MirrorPercent.GetValue(); value > 100 { - errs = appendValidation(errs, fmt.Errorf("mirror_percent must have a max value of 100 (it has %d)", value)) - } - errs = appendValidation(errs, WrapWarning(errors.New(`using deprecated setting "mirrorPercent", use "mirrorPercentage" instead`))) - } - - if http.MirrorPercentage != nil { - value := http.MirrorPercentage.GetValue() - if value > 100 { - errs = appendValidation(errs, fmt.Errorf("mirror_percentage must have a max value of 100 (it has %f)", value)) - } - if value < 0 { - errs = appendValidation(errs, fmt.Errorf("mirror_percentage must have a min value of 0 (it has %f)", value)) - } - } - - errs = appendValidation(errs, validateDestination(http.Mirror)) - errs = appendValidation(errs, validateHTTPRedirect(http.Redirect)) - errs = appendValidation(errs, validateHTTPRetry(http.Retries)) - errs = appendValidation(errs, validateHTTPRewrite(http.Rewrite)) - errs = appendValidation(errs, validateAuthorityRewrite(http.Rewrite, http.Headers)) - errs = appendValidation(errs, validateHTTPRouteDestinations(http.Route)) - if http.Timeout != nil { - errs = appendValidation(errs, ValidateDuration(http.Timeout)) - } - - return -} - -// validateAuthorityRewrite ensures we only attempt rewrite authority in a single place. -func validateAuthorityRewrite(rewrite *networking.HTTPRewrite, headers *networking.Headers) error { - current := rewrite.GetAuthority() - for k, v := range headers.GetRequest().GetSet() { - if !isAuthorityHeader(k) { - continue - } - if current != "" { - return fmt.Errorf("authority header cannot be set multiple times: have %q, attempting to set %q", current, v) - } - current = v - } - for k, v := range headers.GetRequest().GetAdd() { - if !isAuthorityHeader(k) { - continue - } - if current != "" { - return fmt.Errorf("authority header cannot be set multiple times: have %q, attempting to set %q", current, v) - } - current = v - } - return nil -} - -func validateHTTPRouteMatchRequest(http *networking.HTTPRoute, routeType HTTPRouteType) (errs error) { - if routeType == IndependentRoute { - for _, match := range http.Match { - if match != nil { - for name, header := range match.Headers { - if header == nil { - errs = appendErrors(errs, fmt.Errorf("header match %v cannot be null", name)) - } - errs = appendErrors(errs, ValidateHTTPHeaderName(name)) - errs = appendErrors(errs, validateStringMatchRegexp(header, "headers")) - } - - errs = appendErrors(errs, validateStringMatchRegexp(match.GetUri(), "uri")) - errs = appendErrors(errs, validateStringMatchRegexp(match.GetScheme(), "scheme")) - errs = appendErrors(errs, validateStringMatchRegexp(match.GetMethod(), "method")) - errs = appendErrors(errs, validateStringMatchRegexp(match.GetAuthority(), "authority")) - for _, qp := range match.GetQueryParams() { - errs = appendErrors(errs, validateStringMatchRegexp(qp, "queryParams")) - } - } - } - } else { - for _, match := range http.Match { - if match != nil { - if containRegexMatch(match.Uri) { - errs = appendErrors(errs, errors.New("url match does not support regex match for delegating")) - } - if containRegexMatch(match.Scheme) { - errs = appendErrors(errs, errors.New("scheme match does not support regex match for delegating")) - } - if containRegexMatch(match.Method) { - errs = appendErrors(errs, errors.New("method match does not support regex match for delegating")) - } - if containRegexMatch(match.Authority) { - errs = appendErrors(errs, errors.New("authority match does not support regex match for delegating")) - } - - for name, header := range match.Headers { - if header == nil { - errs = appendErrors(errs, fmt.Errorf("header match %v cannot be null", name)) - } - if containRegexMatch(header) { - errs = appendErrors(errs, fmt.Errorf("header match %v does not support regex match for delegating", name)) - } - errs = appendErrors(errs, ValidateHTTPHeaderName(name)) - } - for name, param := range match.QueryParams { - if param == nil { - errs = appendErrors(errs, fmt.Errorf("query param match %v cannot be null", name)) - } - if containRegexMatch(param) { - errs = appendErrors(errs, fmt.Errorf("query param match %v does not support regex match for delegating", name)) - } - } - for name, header := range match.WithoutHeaders { - if header == nil { - errs = appendErrors(errs, fmt.Errorf("withoutHeaders match %v cannot be null", name)) - } - if containRegexMatch(header) { - errs = appendErrors(errs, fmt.Errorf("withoutHeaders match %v does not support regex match for delegating", name)) - } - errs = appendErrors(errs, ValidateHTTPHeaderName(name)) - } - - } - } - } - - for _, match := range http.Match { - if match != nil { - if match.Port != 0 { - errs = appendErrors(errs, ValidatePort(int(match.Port))) - } - errs = appendErrors(errs, labels.Instance(match.SourceLabels).Validate()) - errs = appendErrors(errs, validateGatewayNames(match.Gateways)) - if match.SourceNamespace != "" { - if !labels.IsDNS1123Label(match.SourceNamespace) { - errs = appendErrors(errs, fmt.Errorf("sourceNamespace match %s is invalid", match.SourceNamespace)) - } - } - } - } - - return -} - -func validateHTTPRouteConflict(http *networking.HTTPRoute, routeType HTTPRouteType) (errs error) { - if routeType == RootRoute { - // This is to check root conflict - // only delegate can be specified - if http.Redirect != nil { - errs = appendErrors(errs, fmt.Errorf("root HTTP route %s must not specify redirect", http.Name)) - } - if http.Route != nil { - errs = appendErrors(errs, fmt.Errorf("root HTTP route %s must not specify route", http.Name)) - } - return errs - } - - // This is to check delegate conflict - if routeType == DelegateRoute { - if http.Delegate != nil { - errs = appendErrors(errs, errors.New("delegate HTTP route cannot contain delegate")) - } - } - - // check for conflicts - if http.Redirect != nil { - if len(http.Route) > 0 { - errs = appendErrors(errs, errors.New("HTTP route cannot contain both route and redirect")) - } - - if http.Fault != nil { - errs = appendErrors(errs, errors.New("HTTP route cannot contain both fault and redirect")) - } - - if http.Rewrite != nil { - errs = appendErrors(errs, errors.New("HTTP route rule cannot contain both rewrite and redirect")) - } - } else if len(http.Route) == 0 { - errs = appendErrors(errs, errors.New("HTTP route or redirect is required")) - } - - return errs -} - -func containRegexMatch(config *networking.StringMatch) bool { - if config == nil { - return false - } - switch config.GetMatchType().(type) { - case *networking.StringMatch_Regex: - return true - default: - return false - } -} - -// isInternalHeader returns true if a header refers to an internal value that cannot be modified by Envoy -func isInternalHeader(headerKey string) bool { - return strings.HasPrefix(headerKey, ":") || strings.EqualFold(headerKey, "host") -} - -// isAuthorityHeader returns true if a header refers to the authority header -func isAuthorityHeader(headerKey string) bool { - return strings.EqualFold(headerKey, ":authority") || strings.EqualFold(headerKey, "host") -} diff --git a/pkg/config/validation/virtualservice_test.go b/pkg/config/validation/virtualservice_test.go deleted file mode 100644 index a1f67d55b..000000000 --- a/pkg/config/validation/virtualservice_test.go +++ /dev/null @@ -1,636 +0,0 @@ -// Copyright 2010 Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validation - -import ( - "testing" -) - -import ( - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/wrapperspb" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" -) - -func TestValidateChainingVirtualService(t *testing.T) { - testCases := []struct { - name string - in proto.Message - valid bool - }{ - { - name: "root simple", - in: &networking.VirtualService{ - Hosts: []string{"foo.bar"}, - Gateways: []string{"test-gateway"}, - Http: []*networking.HTTPRoute{{ - Delegate: &networking.Delegate{ - Name: "test", - Namespace: "test", - }, - }}, - }, - valid: true, - }, - { - name: "root applies to sidecar with delegate", - in: &networking.VirtualService{ - Hosts: []string{"foo.bar"}, - Http: []*networking.HTTPRoute{{ - Delegate: &networking.Delegate{ - Name: "test", - Namespace: "test", - }, - }}, - }, - valid: true, - }, - { - name: "root with delegate and destination in one route", - in: &networking.VirtualService{ - Hosts: []string{"foo.bar"}, - Http: []*networking.HTTPRoute{{ - Delegate: &networking.Delegate{ - Name: "test", - Namespace: "test", - }, - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }}, - }, - valid: false, - }, - { - name: "delegate with no destination", - in: &networking.VirtualService{ - Hosts: []string{}, - Http: []*networking.HTTPRoute{{}}, - }, - valid: false, - }, - { - name: "delegate with delegate", - in: &networking.VirtualService{ - Hosts: []string{}, - Http: []*networking.HTTPRoute{ - { - Delegate: &networking.Delegate{ - Name: "test", - Namespace: "test", - }, - }, - }, - }, - valid: false, - }, - { - name: "delegate with TCP route", - in: &networking.VirtualService{ - Hosts: []string{}, - Tcp: []*networking.TCPRoute{{ - Route: []*networking.RouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }}, - }, - valid: false, - }, - { - name: "delegate with gateway", - in: &networking.VirtualService{ - Hosts: []string{}, - Gateways: []string{"test"}, - Http: []*networking.HTTPRoute{{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }}, - }, - valid: false, - }, - { - name: "delegate with sourceNamespace", - in: &networking.VirtualService{ - Hosts: []string{}, - Http: []*networking.HTTPRoute{{ - Match: []*networking.HTTPMatchRequest{ - { - SourceNamespace: "test", - }, - }, - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }}, - }, - valid: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - if _, err := ValidateVirtualService(config.Config{Spec: tc.in}); (err == nil) != tc.valid { - t.Fatalf("got valid=%v but wanted valid=%v: %v", err == nil, tc.valid, err) - } - }) - } -} - -func TestValidateRootHTTPRoute(t *testing.T) { - testCases := []struct { - name string - route *networking.HTTPRoute - valid bool - }{ - { - name: "simple", - route: &networking.HTTPRoute{ - Delegate: &networking.Delegate{ - Name: "test", - Namespace: "test", - }, - }, - valid: true, - }, - { - name: "route and delegate conflict", - route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - Delegate: &networking.Delegate{ - Name: "test", - Namespace: "test", - }, - }, - valid: false, - }, - { - name: "redirect and delegate conflict", - route: &networking.HTTPRoute{ - Redirect: &networking.HTTPRedirect{ - Uri: "/lerp", - Authority: "foo.biz", - }, - Delegate: &networking.Delegate{ - Name: "test", - Namespace: "test", - }, - }, - valid: false, - }, - { - name: "null header match", route: &networking.HTTPRoute{ - Delegate: &networking.Delegate{ - Name: "test", - Namespace: "test", - }, - Match: []*networking.HTTPMatchRequest{{ - Headers: map[string]*networking.StringMatch{ - "header": nil, - }, - }}, - }, valid: false, - }, - { - name: "prefix header match", route: &networking.HTTPRoute{ - Delegate: &networking.Delegate{ - Name: "test", - Namespace: "test", - }, - Match: []*networking.HTTPMatchRequest{{ - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Prefix{Prefix: "test"}, - }, - }, - }}, - }, valid: true, - }, - { - name: "exact header match", route: &networking.HTTPRoute{ - Delegate: &networking.Delegate{ - Name: "test", - Namespace: "test", - }, - Match: []*networking.HTTPMatchRequest{{ - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Exact{Exact: "test"}, - }, - }, - }}, - }, valid: true, - }, - {name: "regex header match", route: &networking.HTTPRoute{ - Delegate: &networking.Delegate{ - Name: "test", - Namespace: "test", - }, - Match: []*networking.HTTPMatchRequest{{ - Headers: map[string]*networking.StringMatch{ - "header": { - MatchType: &networking.StringMatch_Regex{Regex: "test"}, - }, - }, - }}, - }, valid: false}, - {name: "regex uri match", route: &networking.HTTPRoute{ - Delegate: &networking.Delegate{ - Name: "test", - Namespace: "test", - }, - Match: []*networking.HTTPMatchRequest{{ - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Regex{Regex: "test"}, - }, - }}, - }, valid: false}, - {name: "prefix uri match", route: &networking.HTTPRoute{ - Delegate: &networking.Delegate{ - Name: "test", - Namespace: "test", - }, - Match: []*networking.HTTPMatchRequest{{ - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: "test"}, - }, - }}, - }, valid: true}, - {name: "exact uri match", route: &networking.HTTPRoute{ - Delegate: &networking.Delegate{ - Name: "test", - Namespace: "test", - }, - Match: []*networking.HTTPMatchRequest{{ - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: "test"}, - }, - }}, - }, valid: true}, - { - name: "prefix queryParams match", route: &networking.HTTPRoute{ - Delegate: &networking.Delegate{ - Name: "test", - Namespace: "test", - }, - Match: []*networking.HTTPMatchRequest{{ - QueryParams: map[string]*networking.StringMatch{ - "q": { - MatchType: &networking.StringMatch_Prefix{Prefix: "test"}, - }, - }, - }}, - }, valid: true, - }, - { - name: "exact queryParams match", route: &networking.HTTPRoute{ - Delegate: &networking.Delegate{ - Name: "test", - Namespace: "test", - }, - Match: []*networking.HTTPMatchRequest{{ - QueryParams: map[string]*networking.StringMatch{ - "q": { - MatchType: &networking.StringMatch_Exact{Exact: "test"}, - }, - }, - }}, - }, valid: true, - }, - {name: "regex queryParams match", route: &networking.HTTPRoute{ - Delegate: &networking.Delegate{ - Name: "test", - Namespace: "test", - }, - Match: []*networking.HTTPMatchRequest{{ - Headers: map[string]*networking.StringMatch{ - "q": { - MatchType: &networking.StringMatch_Regex{Regex: "test"}, - }, - }, - }}, - }, valid: false}, - {name: "empty regex match in method", route: &networking.HTTPRoute{ - Match: []*networking.HTTPMatchRequest{{ - Method: &networking.StringMatch{ - MatchType: &networking.StringMatch_Regex{Regex: ""}, - }, - }}, - Redirect: &networking.HTTPRedirect{ - Uri: "/", - Authority: "foo.biz", - }, - }, valid: false}, - {name: "empty regex match in uri", route: &networking.HTTPRoute{ - Match: []*networking.HTTPMatchRequest{{ - Uri: &networking.StringMatch{ - MatchType: &networking.StringMatch_Regex{Regex: ""}, - }, - }}, - Redirect: &networking.HTTPRedirect{ - Uri: "/", - Authority: "foo.biz", - }, - }, valid: false}, - {name: "empty regex match in query", route: &networking.HTTPRoute{ - Match: []*networking.HTTPMatchRequest{{ - QueryParams: map[string]*networking.StringMatch{ - "q": { - MatchType: &networking.StringMatch_Regex{Regex: ""}, - }, - }, - }}, - Redirect: &networking.HTTPRedirect{ - Uri: "/", - Authority: "foo.biz", - }, - }, valid: false}, - {name: "empty regex match in scheme", route: &networking.HTTPRoute{ - Match: []*networking.HTTPMatchRequest{{ - Scheme: &networking.StringMatch{ - MatchType: &networking.StringMatch_Regex{Regex: ""}, - }, - }}, - Redirect: &networking.HTTPRedirect{ - Uri: "/", - Authority: "foo.biz", - }, - }, valid: false}, - {name: "empty regex match in scheme", route: &networking.HTTPRoute{ - Match: []*networking.HTTPMatchRequest{{ - Authority: &networking.StringMatch{ - MatchType: &networking.StringMatch_Regex{Regex: ""}, - }, - }}, - Redirect: &networking.HTTPRedirect{ - Uri: "/", - Authority: "foo.biz", - }, - }, valid: false}, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - if err := validateHTTPRoute(tc.route, false); (err.Err == nil) != tc.valid { - t.Fatalf("got valid=%v but wanted valid=%v: %v", err.Err == nil, tc.valid, err) - } - }) - } -} - -func TestValidateDelegateHTTPRoute(t *testing.T) { - testCases := []struct { - name string - route *networking.HTTPRoute - valid bool - }{ - {name: "empty", route: &networking.HTTPRoute{ // nothing - }, valid: false}, - {name: "simple", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }, valid: true}, - {name: "no destination", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: nil, - }}, - }, valid: false}, - {name: "weighted", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz.south"}, - Weight: 25, - }, { - Destination: &networking.Destination{Host: "foo.baz.east"}, - Weight: 75, - }}, - }, valid: true}, - {name: "total weight > 100", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz.south"}, - Weight: 55, - }, { - Destination: &networking.Destination{Host: "foo.baz.east"}, - Weight: 50, - }}, - }, valid: false}, - {name: "total weight < 100", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz.south"}, - Weight: 49, - }, { - Destination: &networking.Destination{Host: "foo.baz.east"}, - Weight: 50, - }}, - }, valid: false}, - {name: "simple redirect", route: &networking.HTTPRoute{ - Redirect: &networking.HTTPRedirect{ - Uri: "/lerp", - Authority: "foo.biz", - }, - }, valid: true}, - {name: "conflicting redirect and route", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - Redirect: &networking.HTTPRedirect{ - Uri: "/lerp", - Authority: "foo.biz", - }, - }, valid: false}, - {name: "request response headers", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - }}, - }, valid: true}, - {name: "valid headers", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - Headers: &networking.Headers{ - Request: &networking.Headers_HeaderOperations{ - Add: map[string]string{ - "name": "", - }, - Set: map[string]string{ - "name": "", - }, - Remove: []string{ - "name", - }, - }, - Response: &networking.Headers_HeaderOperations{ - Add: map[string]string{ - "name": "", - }, - Set: map[string]string{ - "name": "", - }, - Remove: []string{ - "name", - }, - }, - }, - }}, - }, valid: true}, - {name: "empty header name - request add", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - Headers: &networking.Headers{ - Request: &networking.Headers_HeaderOperations{ - Add: map[string]string{ - "": "value", - }, - }, - }, - }}, - }, valid: false}, - {name: "empty header name - request set", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - Headers: &networking.Headers{ - Request: &networking.Headers_HeaderOperations{ - Set: map[string]string{ - "": "value", - }, - }, - }, - }}, - }, valid: false}, - {name: "empty header name - request remove", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - Headers: &networking.Headers{ - Request: &networking.Headers_HeaderOperations{ - Remove: []string{ - "", - }, - }, - }, - }}, - }, valid: false}, - {name: "empty header name - response add", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - Headers: &networking.Headers{ - Response: &networking.Headers_HeaderOperations{ - Add: map[string]string{ - "": "value", - }, - }, - }, - }}, - }, valid: false}, - {name: "empty header name - response set", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - Headers: &networking.Headers{ - Response: &networking.Headers_HeaderOperations{ - Set: map[string]string{ - "": "value", - }, - }, - }, - }}, - }, valid: false}, - {name: "empty header name - response remove", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.baz"}, - Headers: &networking.Headers{ - Response: &networking.Headers_HeaderOperations{ - Remove: []string{ - "", - }, - }, - }, - }}, - }, valid: false}, - {name: "null header match", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.bar"}, - }}, - Match: []*networking.HTTPMatchRequest{{ - Headers: map[string]*networking.StringMatch{ - "header": nil, - }, - }}, - }, valid: false}, - {name: "nil match", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.bar"}, - }}, - Match: nil, - }, valid: true}, - {name: "match with nil element", route: &networking.HTTPRoute{ - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.bar"}, - }}, - Match: []*networking.HTTPMatchRequest{nil}, - }, valid: true}, - {name: "invalid mirror percent", route: &networking.HTTPRoute{ - MirrorPercent: &wrapperspb.UInt32Value{Value: 101}, - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.bar"}, - }}, - Match: []*networking.HTTPMatchRequest{nil}, - }, valid: false}, - {name: "invalid mirror percentage", route: &networking.HTTPRoute{ - MirrorPercentage: &networking.Percent{ - Value: 101, - }, - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.bar"}, - }}, - Match: []*networking.HTTPMatchRequest{nil}, - }, valid: false}, - {name: "valid mirror percentage", route: &networking.HTTPRoute{ - MirrorPercentage: &networking.Percent{ - Value: 1, - }, - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.bar"}, - }}, - Match: []*networking.HTTPMatchRequest{nil}, - }, valid: true}, - {name: "negative mirror percentage", route: &networking.HTTPRoute{ - MirrorPercentage: &networking.Percent{ - Value: -1, - }, - Route: []*networking.HTTPRouteDestination{{ - Destination: &networking.Destination{Host: "foo.bar"}, - }}, - Match: []*networking.HTTPMatchRequest{nil}, - }, valid: false}, - {name: "delegate route with delegate", route: &networking.HTTPRoute{ - Delegate: &networking.Delegate{ - Name: "test", - Namespace: "test", - }, - }, valid: false}, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - if err := validateHTTPRoute(tc.route, true); (err.Err == nil) != tc.valid { - t.Fatalf("got valid=%v but wanted valid=%v: %v", err.Err == nil, tc.valid, err) - } - }) - } -} diff --git a/pixiu/pkg/config/version.go b/pkg/config/version.go similarity index 100% rename from pixiu/pkg/config/version.go rename to pkg/config/version.go diff --git a/pkg/config/visibility/visibility.go b/pkg/config/visibility/visibility.go deleted file mode 100644 index 0628c72a2..000000000 --- a/pkg/config/visibility/visibility.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package visibility - -import ( - "fmt" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" -) - -// Instance defines whether a given config or service is exported to local namespace, some set of namespaces, or -// all namespaces or none -type Instance string - -const ( - // Private implies namespace local config - Private Instance = "." - // Public implies config is visible to all - Public Instance = "*" - // None implies service is visible to no one. Used for services only - None Instance = "~" -) - -// Validate a visibility value ( ./*/~/some namespace name which is DNS1123 label) -func (v Instance) Validate() (errs error) { - switch v { - case Private, Public: - return nil - case None: - return fmt.Errorf("exportTo ~ (none) is not allowed for Istio configuration objects") - default: - if !labels.IsDNS1123Label(string(v)) { - return fmt.Errorf("only .,*,~, or a valid DNS 1123 label is allowed as exportTo entry") - } - } - return nil -} diff --git a/pixiu/pkg/config/xds/README.md b/pkg/config/xds/README.md similarity index 100% rename from pixiu/pkg/config/xds/README.md rename to pkg/config/xds/README.md diff --git a/pixiu/pkg/config/xds/apiclient/apiclient.go b/pkg/config/xds/apiclient/apiclient.go similarity index 100% rename from pixiu/pkg/config/xds/apiclient/apiclient.go rename to pkg/config/xds/apiclient/apiclient.go diff --git a/pkg/config/xds/apiclient/grpc.go b/pkg/config/xds/apiclient/grpc.go new file mode 100644 index 000000000..3de81cfbf --- /dev/null +++ b/pkg/config/xds/apiclient/grpc.go @@ -0,0 +1,383 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 apiclient + +import ( + "context" + stderr "errors" + "sync" + "time" +) + +import ( + envoyconfigcorev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + discoverypb "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" + extensionpb "github.com/envoyproxy/go-control-plane/envoy/service/extension/v3" + "github.com/envoyproxy/go-control-plane/pkg/resource/v3" + "github.com/pkg/errors" + "google.golang.org/grpc" + "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/protobuf/types/known/anypb" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/server/controls" +) + +// agent name to talk with xDS server +const xdsAgentName = "dubbo-go-pixiu" + +var ( + grpcMg *GRPCClusterManager + ErrClusterNotFound = stderr.New("can not find cluster") +) + +type ( + GrpcExtensionApiClient struct { + config model.ApiConfigSource + grpcMg *GRPCClusterManager + node *model.Node + xDSExtensionClient extensionpb.ExtensionConfigDiscoveryServiceClient + resourceNames []ResourceTypeName + typeUrl string + exitCh chan struct{} + } + xdsState struct { + nonce string + deltaVersion map[string]string + versionInfo string + } +) + +func Init(clusterMg controls.ClusterManager) { + grpcMg = &GRPCClusterManager{ + clusters: &sync.Map{}, + clusterMg: clusterMg, + } +} + +func Stop() { + if err := grpcMg.Close(); err != nil { //todo + logger.Errorf("grpcClusterManager close failed. %v", err) + } +} + +// CreateGrpExtensionApiClient create Grpc type ApiClient +func CreateGrpExtensionApiClient(config *model.ApiConfigSource, node *model.Node, + exitCh chan struct{}, + typeName ResourceTypeName) *GrpcExtensionApiClient { + v := &GrpcExtensionApiClient{ + config: *config, + node: node, + typeUrl: typeName, + grpcMg: grpcMg, + exitCh: exitCh, + } + v.init() + return v +} + +// Fetch get config data from discovery service and return Any type. +func (g *GrpcExtensionApiClient) Fetch(localVersion string) ([]*ProtoAny, error) { + clsRsp, err := g.xDSExtensionClient.FetchExtensionConfigs(context.Background(), &discoverypb.DiscoveryRequest{ + VersionInfo: localVersion, + Node: g.makeNode(), + ResourceNames: g.resourceNames, + TypeUrl: resource.ExtensionConfigType, //"type.googleapis.com/pixiu.config.listener.v3.Listener", //resource.ListenerType, + }) + if err != nil { + return nil, errors.Wrapf(err, "fetch dynamic resource from remote error. %s", g.resourceNames) + } + logger.Infof("init the from xds server typeUrl=%s version=%s", clsRsp.TypeUrl, clsRsp.VersionInfo) + extensions := make([]*ProtoAny, 0, len(clsRsp.Resources)) + for _, clsResource := range clsRsp.Resources { + elems, err := g.decodeSource(clsResource) + if err != nil { + return nil, err + } + extensions = append(extensions, elems) + } + return extensions, nil +} + +func (g *GrpcExtensionApiClient) decodeSource(resource *anypb.Any) (*ProtoAny, error) { + extension := envoyconfigcorev3.TypedExtensionConfig{} + err := resource.UnmarshalTo(&extension) + if err != nil { + return nil, errors.Wrapf(err, "typed extension as expected.(%s)", g.resourceNames) + } + elems := &ProtoAny{typeConfig: &extension} + return elems, nil +} + +func (g *GrpcExtensionApiClient) makeNode() *envoyconfigcorev3.Node { + return &envoyconfigcorev3.Node{ + Id: g.node.Id, + Cluster: g.node.Cluster, + UserAgentName: xdsAgentName, + } +} + +func (g *GrpcExtensionApiClient) Delta() (chan *DeltaResources, error) { + outputCh := make(chan *DeltaResources) + return outputCh, g.runDelta(outputCh) +} + +func (g *GrpcExtensionApiClient) runDelta(output chan<- *DeltaResources) error { + var delta extensionpb.ExtensionConfigDiscoveryService_DeltaExtensionConfigsClient + var cancel context.CancelFunc + var xState xdsState + backoff := func() { + for { + //back off + var err error + var ctx context.Context // context to sync exitCh + ctx, cancel = context.WithCancel(context.TODO()) + delta, err = g.sendInitDeltaRequest(ctx, &xState) + if err != nil { + logger.Error("can not receive delta discovery request, will back off 1 sec later", err) + select { + case <-time.After(1 * time.Second): + case <-g.exitCh: + logger.Infof("get close single.") + return + } + + continue //backoff + } + return //success + } + } + + backoff() + if delta == nil { // delta instance not created because exitCh + return nil + } + go func() { + //waiting exitCh close + for range g.exitCh { + } + cancel() + }() + //get message + go func() { + for { // delta response backoff. + for { //loop consume recv data form xds server(sendInitDeltaRequest) + resp, err := delta.Recv() + if err != nil { //todo backoff retry + logger.Error("can not receive delta discovery request", err) + break + } + g.handleDeltaResponse(resp, &xState, output) + + err = g.subscribeOnGoingChang(delta, &xState) + if err != nil { + logger.Error("can not recv delta discovery request", err) + break + } + } + backoff() + } + }() + + return nil +} + +func (g *GrpcExtensionApiClient) handleDeltaResponse(resp *discoverypb.DeltaDiscoveryResponse, xState *xdsState, output chan<- *DeltaResources) { + // save the xds state + xState.deltaVersion = make(map[string]string, 1) + xState.nonce = resp.Nonce + + resources := &DeltaResources{ + NewResources: make([]*ProtoAny, 0, 1), + RemovedResource: make([]string, 0, 1), + } + logger.Infof("get xDS message nonce, %s", resp.Nonce) + for _, res := range resp.RemovedResources { + logger.Infof("remove resource found ", res) + resources.RemovedResource = append(resources.RemovedResource, res) + } + + for _, res := range resp.Resources { + logger.Infof("new resource found %s version=%s", res.Name, res.Version) + xState.deltaVersion[res.Name] = res.Version + elems, err := g.decodeSource(res.Resource) + if err != nil { + logger.Infof("can not decode source %s version=%s", res.Name, res.Version, err) + } + resources.NewResources = append(resources.NewResources, elems) + } + //notify the resource change handler + output <- resources +} + +func (g *GrpcExtensionApiClient) subscribeOnGoingChang(delta extensionpb.ExtensionConfigDiscoveryService_DeltaExtensionConfigsClient, xState *xdsState) error { + err := delta.Send(&discoverypb.DeltaDiscoveryRequest{ + Node: g.makeNode(), + TypeUrl: resource.ExtensionConfigType, + InitialResourceVersions: xState.deltaVersion, + ResponseNonce: xState.nonce, + }) + return err +} + +func (g *GrpcExtensionApiClient) sendInitDeltaRequest(ctx context.Context, xState *xdsState) (extensionpb.ExtensionConfigDiscoveryService_DeltaExtensionConfigsClient, error) { + delta, err := g.xDSExtensionClient.DeltaExtensionConfigs(ctx) + if err != nil { + return nil, errors.Wrapf(err, "can not start delta stream with xds server ") + } + err = delta.Send(&discoverypb.DeltaDiscoveryRequest{ + Node: g.makeNode(), + TypeUrl: resource.ExtensionConfigType, + ResourceNamesSubscribe: []string{g.typeUrl}, + ResourceNamesUnsubscribe: nil, + InitialResourceVersions: xState.deltaVersion, + ResponseNonce: xState.nonce, + ErrorDetail: nil, + }) + if err != nil { + return nil, errors.Wrapf(err, "can not send delta discovery request") + } + return delta, nil +} + +func (g *GrpcExtensionApiClient) init() { + if len(g.config.ClusterName) == 0 { + panic("should config one cluster at least") + } + //todo implement multiple grpc api services + if len(g.config.ClusterName) > 1 { + logger.Warnf("defined multiple cluster for xDS api services but only one support.") + } + cluster, err := g.grpcMg.GetGrpcCluster(g.config.ClusterName[0]) + + if err != nil { + logger.Errorf("get cluster for init error. error=%v", err) + panic(err) + } + conn, err := cluster.GetConnection() + if err != nil { + panic(err) + } + g.xDSExtensionClient = extensionpb.NewExtensionConfigDiscoveryServiceClient(conn) +} + +type GRPCClusterManager struct { + clusters *sync.Map // map[clusterName]*grpcCluster + clusterMg controls.ClusterManager +} + +type GRPCCluster struct { + name string //cluster name + config *model.ClusterConfig + once sync.Once + conn *grpc.ClientConn +} + +// GetGrpcCluster get the cluster or create it first time. +func (g *GRPCClusterManager) GetGrpcCluster(name string) (*GRPCCluster, error) { + if !g.clusterMg.HasCluster(name) { + return nil, errors.Wrapf(ErrClusterNotFound, "name = %s", name) + } + + if load, ok := g.clusters.Load(name); ok { + grpcCluster := load.(*GRPCCluster) // grpcClusterManager only + return grpcCluster, nil + } + + store, err := g.clusterMg.CloneXdsControlStore() + if err != nil { + return nil, errors.WithMessagef(err, "clone cluster store failed") + } + + var clusterCfg *model.ClusterConfig + for _, cfg := range store.Config() { + if cfg.Name == name { + clusterCfg = cfg + break + } + } + if clusterCfg == nil { + return nil, errors.Wrapf(ErrClusterNotFound, "name of %s", name) + } + newCluster := &GRPCCluster{ + name: name, + config: clusterCfg, + } + g.clusters.Store(name, newCluster) + return newCluster, nil +} + +func (g *GRPCClusterManager) Close() (err error) { + //todo enhance the close process when concurrent + g.clusters.Range(func(_, value interface{}) bool { + if conn := value.(*grpc.ClientConn); conn != nil { + if err = conn.Close(); err != nil { + logger.Errorf("can not close grpc connection.", err) + } + } + return true + }) + return nil +} + +func (g *GRPCCluster) GetConnection() (conn *grpc.ClientConn, err error) { + g.once.Do(func() { + creds := insecure.NewCredentials() + //if *xdsCreds { // todo + // log.Println("Using xDS credentials...") + // var err error + // if creds, err = xdscreds.NewClientCredentials(xdscreds.ClientOptions{FallbackCreds: insecure.NewCredentials()}); err != nil { + // log.Fatalf("failed to create client-side xDS credentials: %v", err) + // } + //} + if len(g.config.Endpoints) == 0 { + err = errors.Errorf("expect endpoint.") + return + } + endpoint := g.config.Endpoints[0].Address.GetAddress() + logger.Infof("to connect xds server %s ...", endpoint) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) //todo fix timeout cancel warning + defer cancel() + conn, err = grpc.DialContext(ctx, endpoint, + grpc.WithTransportCredentials(creds), + grpc.WithBlock(), + ) + if err != nil { + err = errors.Errorf("grpc.Dial(%s) failed: %v", endpoint, err) + return + } + logger.Infof("connected xds server (%s)", endpoint) + g.conn = conn + }) + return g.conn, nil +} + +func (g *GRPCCluster) IsAlive() bool { + return g.conn.GetState() == connectivity.Ready +} + +func (g *GRPCCluster) Close() error { + if err := g.conn.Close(); err != nil { + return errors.Wrapf(err, "can not close. %v", g.config) + } + return nil +} diff --git a/pixiu/pkg/config/xds/apiclient/grpc_envoy.go b/pkg/config/xds/apiclient/grpc_envoy.go similarity index 99% rename from pixiu/pkg/config/xds/apiclient/grpc_envoy.go rename to pkg/config/xds/apiclient/grpc_envoy.go index 19b8a2ab7..359095a70 100644 --- a/pixiu/pkg/config/xds/apiclient/grpc_envoy.go +++ b/pkg/config/xds/apiclient/grpc_envoy.go @@ -41,7 +41,7 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) type GrpcApiClientOption func(*AggGrpcApiClient) diff --git a/pkg/config/xds/apiclient/grpc_test.go b/pkg/config/xds/apiclient/grpc_test.go new file mode 100644 index 000000000..986ed8b1c --- /dev/null +++ b/pkg/config/xds/apiclient/grpc_test.go @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 apiclient + +import ( + "context" + "sync" + "testing" +) + +import ( + "github.com/cch123/supermonkey" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/connectivity" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/server/controls" + "github.com/apache/dubbo-go-pixiu/pkg/server/controls/mocks" +) + +func TestGRPCClusterManager_GetGrpcCluster(t *testing.T) { + cluster := &model.ClusterConfig{ + Name: "cluster-1", + TypeStr: "GRPC", + Endpoints: []*model.Endpoint{ + { + Address: model.SocketAddress{ + Address: "localhost", + Port: 18000, + }, + }, + }, + } + type fields struct { + clusters *sync.Map + clusterMg controls.ClusterManager + } + type args struct { + name string + } + ctrl := gomock.NewController(t) + clusterMg := mocks.NewMockClusterManager(ctrl) + clusterMg.EXPECT(). + HasCluster("cluster-1").AnyTimes(). + Return(true) + clusterMg.EXPECT().CloneXdsControlStore().DoAndReturn(func() (controls.ClusterStore, error) { + store := mocks.NewMockClusterStore(ctrl) + store.EXPECT().Config().Return([]*model.ClusterConfig{cluster}) + return store, nil + }) + clusterMg.EXPECT().HasCluster("cluster-2").Return(false) + tests := []struct { + name string + fields fields + args args + wantErr error + }{ + {"test-simple", fields{ + clusters: &sync.Map{}, + clusterMg: clusterMg, + }, args{name: "cluster-1"}, nil}, + {"test-not-exist", fields{ + clusters: &sync.Map{}, + clusterMg: clusterMg, + }, args{name: "cluster-2"}, ErrClusterNotFound}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := &GRPCClusterManager{ + clusters: tt.fields.clusters, + clusterMg: tt.fields.clusterMg, + } + got, err := g.GetGrpcCluster(tt.args.name) + assert := require.New(t) + if tt.wantErr != nil { + assert.ErrorIs(err, tt.wantErr) + return + } + assert.NotNil(got) + //run two times. + got, err = g.GetGrpcCluster(tt.args.name) + assert.NoError(err) + assert.NotNil(got) + + }) + } +} + +func TestGRPCCluster_GetConnect(t *testing.T) { + cluster := &model.ClusterConfig{ + Name: "cluster-1", + TypeStr: "GRPC", + Endpoints: []*model.Endpoint{ + { + Address: model.SocketAddress{ + Address: "localhost", + Port: 18000, + }, + }, + }, + } + g := GRPCCluster{ + name: "name", + config: cluster, + once: sync.Once{}, + conn: nil, + } + + gconn := &grpc.ClientConn{} + var state = connectivity.Ready + supermonkey.Patch(grpc.DialContext, func(ctx context.Context, target string, opts ...grpc.DialOption) (conn *grpc.ClientConn, err error) { + return gconn, nil + }) + supermonkey.Patch((*grpc.ClientConn).Close, func(_ *grpc.ClientConn) error { + return nil + }) + supermonkey.Patch((*grpc.ClientConn).GetState, func(_ *grpc.ClientConn) connectivity.State { + return state + }) + + defer supermonkey.UnpatchAll() + assert := require.New(t) + conn, err := g.GetConnection() + assert.NoError(err) + assert.NotNil(conn) + assert.NotNil(g.conn) + assert.True(g.IsAlive()) + err = g.Close() + assert.NoError(err) + state = connectivity.Shutdown + assert.False(g.IsAlive()) +} diff --git a/pkg/config/xds/cds.go b/pkg/config/xds/cds.go new file mode 100644 index 000000000..1403407fd --- /dev/null +++ b/pkg/config/xds/cds.go @@ -0,0 +1,247 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 xds + +import ( + "github.com/dubbo-go-pixiu/pixiu-api/pkg/api" + xdspb "github.com/dubbo-go-pixiu/pixiu-api/pkg/xds/model" + "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/config/xds/apiclient" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/server/controls" +) + +type CdsManager struct { + DiscoverApi + clusterMg controls.ClusterManager +} + +// Fetch overwrite DiscoverApi.Fetch. +func (c *CdsManager) Fetch() error { + r, err := c.DiscoverApi.Fetch("") //todo use local version + if err != nil { + return err + } + clusters := make([]*xdspb.Cluster, 0, len(r)) + for _, one := range r { + extClusters := &xdspb.PixiuExtensionClusters{} + if err := one.To(extClusters); err != nil { + logger.Errorf("unknown resource of %s, expect Listener", one.GetName()) + continue + } + logger.Infof("clusters from xds server %v", extClusters) + clusters = append(clusters, extClusters.Clusters...) + } + + return c.setupCluster(clusters) +} + +func (c *CdsManager) Delta() error { + readCh, err := c.DiscoverApi.Delta() + if err != nil { + return err + } + go c.asyncHandler(readCh) + return nil +} + +func (c *CdsManager) asyncHandler(read chan *apiclient.DeltaResources) { + for one := range read { + clusters := make([]*xdspb.Cluster, 0, len(one.NewResources)) + for _, one := range one.NewResources { + cluster := &xdspb.PixiuExtensionClusters{} + if err := one.To(cluster); err != nil { + logger.Errorf("unknown resource of %s, expect Listener", one.GetName()) + continue + } + logger.Infof("clusters from xds server %v", cluster) + clusters = append(clusters, cluster.Clusters...) + + } + if err := c.setupCluster(clusters); err != nil { + logger.Errorf("can not setup cluster.", err) + } + } +} + +func (c *CdsManager) removeCluster(clusterNames []string) { + c.clusterMg.RemoveCluster(clusterNames) +} + +func (c *CdsManager) setupCluster(clusters []*xdspb.Cluster) error { + + laterApplies := make([]func() error, 0, len(clusters)) + toRemoveHash := make(map[string]struct{}, len(clusters)) + + store, err := c.clusterMg.CloneXdsControlStore() + if err != nil { + return errors.WithMessagef(err, "can not clone cluster store when update cluster") + } + //todo this will remove the cluster which defined locally. + for _, cluster := range store.Config() { + toRemoveHash[cluster.Name] = struct{}{} + } + for _, cluster := range clusters { + delete(toRemoveHash, cluster.Name) + + makeCluster := c.makeCluster(cluster) + switch { + case c.clusterMg.HasCluster(cluster.Name): + laterApplies = append(laterApplies, func() error { + c.clusterMg.UpdateCluster(makeCluster) + return nil + }) + default: + laterApplies = append(laterApplies, func() error { + c.clusterMg.AddCluster(makeCluster) + return nil + }) + } + } + + c.removeClusters(toRemoveHash) + for _, fn := range laterApplies { //do update and add new cluster. + if err := fn(); err != nil { + logger.Errorf("can not modify cluster", err) + } + } + return nil +} + +func (c *CdsManager) removeClusters(toRemoveList map[string]struct{}) { + removeClusters := make([]string, 0, len(toRemoveList)) + for clusterName := range toRemoveList { + removeClusters = append(removeClusters, clusterName) + } + if len(toRemoveList) == 0 { + return + } + c.removeCluster(removeClusters) +} + +func (c *CdsManager) makeCluster(cluster *xdspb.Cluster) *model.ClusterConfig { + return &model.ClusterConfig{ + Name: cluster.Name, + TypeStr: cluster.TypeStr, + Type: c.makeClusterType(cluster), + EdsClusterConfig: c.makeEdsClusterConfig(cluster.EdsClusterConfig), + LbStr: c.makeLoadBalancePolicy(cluster.LbStr), + HealthChecks: c.makeHealthChecks(cluster.HealthChecks), + Endpoints: c.makeEndpoints(cluster.Endpoints), + } +} + +func (c *CdsManager) makeLoadBalancePolicy(lb string) model.LbPolicyType { + return model.LbPolicyTypeValue[lb] +} + +func (c *CdsManager) makeClusterType(cluster *xdspb.Cluster) model.DiscoveryType { + return model.DiscoveryTypeValue[cluster.TypeStr] +} + +func (c *CdsManager) makeEndpoints(endpoints []*xdspb.Endpoint) []*model.Endpoint { + r := make([]*model.Endpoint, len(endpoints)) + for i, endpoint := range endpoints { + r[i] = &model.Endpoint{ + ID: endpoint.Id, + Name: endpoint.Name, + Address: c.makeAddress(endpoint), + Metadata: endpoint.Metadata, + } + } + return r +} + +func (c *CdsManager) makeAddress(endpoint *xdspb.Endpoint) model.SocketAddress { + if endpoint == nil || endpoint.Address == nil { + return model.SocketAddress{} + } + return model.SocketAddress{ + Address: endpoint.Address.Address, + Port: int(endpoint.Address.Port), + ResolverName: endpoint.Address.ResolverName, + Domains: endpoint.Address.Domains, + CertsDir: endpoint.Address.CertsDir, + } +} + +func (c *CdsManager) makeHealthChecks(checks []*xdspb.HealthCheck) (result []model.HealthCheckConfig) { + //todo implement me after fix model.HealthCheck type define + //result = make([]model.HealthCheck, 0, len(checks)) + //for _, check := range checks { + // switch one := check.GetChecker().(type) { + // case *xdspb.HealthCheck_HttpChecker: + // result = append(result, model.HttpHealthCheck{ + // Host: one.HttpChecker.Host, + // Path: one.HttpChecker.Path, + // UseHttp2: one.HttpChecker.UseHttp2, + // ExpectedStatuses: one.HttpChecker.ExpectedStatuses, + // }) + // case *xdspb.HealthCheck_GrpcChecker: + // result = append(result, model.GrpcHealthCheck{ + // ServiceName: one.GrpcChecker.ServiceName, + // Authority: one.GrpcChecker.Authority, + // }) + // case *xdspb.HealthCheck_CustomChecker: + // result = append(result, model.CustomHealthCheck{ + // Name: one.CustomChecker.Name, + // Config: func() interface{} { + // if one.CustomChecker.Config == nil { + // return nil + // } + // return one.CustomChecker.Config.AsMap() + // }(), + // }) + // } + //} + return +} + +func (c *CdsManager) makeEdsClusterConfig(edsConfig *xdspb.EdsClusterConfig) model.EdsClusterConfig { + if edsConfig == nil { + return model.EdsClusterConfig{} + } + return model.EdsClusterConfig{ + EdsConfig: model.ConfigSource{ + Path: edsConfig.EdsConfig.Path, + ApiConfigSource: c.makeApiConfigSource(edsConfig.EdsConfig.ApiConfigSource), + }, + ServiceName: edsConfig.ServiceName, + } +} + +func (c *CdsManager) makeApiConfigSource(apiConfig *xdspb.ApiConfigSource) (result model.ApiConfigSource) { + apiType, ok := model.ApiTypeValue[apiConfig.APITypeStr] + if !ok { + logger.Errorf("unknown apiType %s", apiConfig.APITypeStr) + return + } + + return model.ApiConfigSource{ + APIType: api.ApiType(apiType), + APITypeStr: apiConfig.APITypeStr, + ClusterName: apiConfig.ClusterName, + RefreshDelay: apiConfig.RefreshDelay, + RequestTimeout: apiConfig.RequestTimeout, + GrpcServices: nil, //todo create node of pb + } +} diff --git a/pkg/config/xds/cds_test.go b/pkg/config/xds/cds_test.go new file mode 100644 index 000000000..d2303302f --- /dev/null +++ b/pkg/config/xds/cds_test.go @@ -0,0 +1,200 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 xds + +import ( + stderr "errors" + "testing" +) + +import ( + "github.com/cch123/supermonkey" + "github.com/dubbo-go-pixiu/pixiu-api/pkg/xds" + pixiupb "github.com/dubbo-go-pixiu/pixiu-api/pkg/xds/model" + core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/anypb" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/config/xds/apiclient" + "github.com/apache/dubbo-go-pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/server/controls" + "github.com/apache/dubbo-go-pixiu/pkg/server/controls/mocks" +) + +func makeClusters() *pixiupb.PixiuExtensionClusters { + return &pixiupb.PixiuExtensionClusters{ + Clusters: []*pixiupb.Cluster{ + { + Name: "http-baidu", + TypeStr: "http", + Endpoints: []*pixiupb.Endpoint{{ + Id: "backend", + Address: &pixiupb.SocketAddress{ + Address: "httpbin.org", + Port: 80, + }, + }}, + }, + }, + } +} + +func getCdsConfig() *core.TypedExtensionConfig { + makeClusters := func() *pixiupb.PixiuExtensionClusters { + return &pixiupb.PixiuExtensionClusters{ + Clusters: []*pixiupb.Cluster{ + { + Name: "http-baidu", + TypeStr: "http", + Endpoints: []*pixiupb.Endpoint{{ + Id: "backend", + Address: &pixiupb.SocketAddress{ + Address: "httpbin.org", + Port: 80, + }, + }}, + }, + }, + } + } + cdsResource, _ := anypb.New(makeClusters()) + return &core.TypedExtensionConfig{ + Name: xds.ClusterType, + TypedConfig: cdsResource, + } +} + +func TestCdsManager_Fetch(t *testing.T) { + var fetchResult []*apiclient.ProtoAny + var fetchError error + var cluster = map[string]struct{}{} + var updateCluster *model.ClusterConfig + var addCluster *model.ClusterConfig + xdsConfig := getCdsConfig() + + ctrl := gomock.NewController(t) + clusterMg := mocks.NewMockClusterManager(ctrl) + //var deltaResult chan *apiclient.DeltaResources + //var deltaErr error + supermonkey.Patch((*apiclient.GrpcExtensionApiClient).Fetch, func(_ *apiclient.GrpcExtensionApiClient, localVersion string) ([]*apiclient.ProtoAny, error) { + return fetchResult, fetchError + }) + //supermonkey.Patch(server.GetClusterManager, func() *server.ClusterManager { + // return nil + //}) + clusterMg.EXPECT().HasCluster(gomock.Any()).DoAndReturn(func(clusterName string) bool { + _, ok := cluster[clusterName] + return ok + }) + + clusterMg.EXPECT().UpdateCluster(gomock.Any()).AnyTimes().Do(func(new *model.ClusterConfig) { + updateCluster = new + }) + clusterMg.EXPECT().AddCluster(gomock.Any()).AnyTimes().Do(func(c *model.ClusterConfig) { + addCluster = c + }) + clusterMg.EXPECT().RemoveCluster(gomock.Any()).AnyTimes() + clusterMg.EXPECT().CloneXdsControlStore().AnyTimes().DoAndReturn(func() (controls.ClusterStore, error) { + store := mocks.NewMockClusterStore(ctrl) + store.EXPECT().Config().AnyTimes() + return store, nil + }) + //supermonkey.Patch((*server.ClusterManager).HasCluster, func(_ *server.ClusterManager, clusterName string) bool { + // _, ok := cluster[clusterName] + // return ok + //}) + //supermonkey.Patch((*server.ClusterManager).UpdateCluster, func(_ *server.ClusterManager, new *model.Cluster) { + // updateCluster = new + //}) + //supermonkey.Patch((*server.ClusterManager).AddCluster, func(_ *server.ClusterManager, c *model.Cluster) { + // addCluster = c + //}) + //supermonkey.Patch((*server.ClusterManager).RemoveCluster, func(_ *server.ClusterManager, names []string) { + // //do nothing. + //}) + //supermonkey.Patch((*server.ClusterManager).CloneStore, func(_ *server.ClusterManager) (*server.ClusterStore, error) { + // return &server.ClusterStore{}, nil + //}) + //supermonkey.Patch((*apiclient.GrpcExtensionApiClient).Delta, func(_ *apiclient.GrpcExtensionApiClient) (chan *apiclient.DeltaResources, error) { + // return deltaResult, deltaErr + //}) + defer supermonkey.UnpatchAll() + + tests := []struct { + name string + mockResult []*apiclient.ProtoAny + mockError error + wantErr bool + wantNewCluster bool + wantUpdateCluster bool + }{ + {"error", nil, stderr.New("error test"), true, false, false}, + {"simple", nil, nil, false, false, false}, + {"withValue", func() []*apiclient.ProtoAny { + return []*apiclient.ProtoAny{ + apiclient.NewProtoAny(xdsConfig), + } + }(), nil, false, true, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &CdsManager{ + DiscoverApi: &apiclient.GrpcExtensionApiClient{}, + clusterMg: clusterMg, + } + //reset context value. + fetchError = tt.mockError + fetchResult = tt.mockResult + updateCluster = nil + addCluster = nil + + err := c.Fetch() + assert := require.New(t) + if tt.wantErr { + assert.Error(err) + return + } + assert.NoError(err) + if tt.wantUpdateCluster { + assert.NotNil(updateCluster) + } else { + assert.Nil(updateCluster) + } + if tt.wantNewCluster { + assert.NotNil(addCluster) + } else { + assert.Nil(addCluster) + } + }) + } +} + +func TestCdsManager_makeCluster(t *testing.T) { + c := &CdsManager{} + cluster := makeClusters().Clusters[0] + modelCluster := c.makeCluster(cluster) + assert := require.New(t) + assert.Equal(cluster.Name, modelCluster.Name) + assert.Equal(cluster.TypeStr, modelCluster.TypeStr) + assert.Equal(cluster.Endpoints[0].Name, modelCluster.Endpoints[0].Name) + assert.Equal(cluster.Endpoints[0].Address.Address, modelCluster.Endpoints[0].Address.Address) + assert.Equal(cluster.Endpoints[0].Address.Port, int64(modelCluster.Endpoints[0].Address.Port)) +} diff --git a/pkg/config/xds/deprecated.go b/pkg/config/xds/deprecated.go deleted file mode 100644 index 8a72f3dd0..000000000 --- a/pkg/config/xds/deprecated.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - "github.com/envoyproxy/go-control-plane/pkg/wellknown" -) - -var ( - // DeprecatedFilterNames is to support both canonical filter names - // and deprecated filter names for backward compatibility. Istiod - // generates canonical filter names. - DeprecatedFilterNames = map[string]string{ - wellknown.Buffer: "envoy.buffer", - wellknown.CORS: "envoy.cors", - "envoy.filters.http.csrf": "envoy.csrf", - wellknown.Dynamo: "envoy.http_dynamo_filter", - wellknown.HTTPExternalAuthorization: "envoy.ext_authz", - wellknown.Fault: "envoy.fault", - wellknown.GRPCHTTP1Bridge: "envoy.grpc_http1_bridge", - wellknown.GRPCJSONTranscoder: "envoy.grpc_json_transcoder", - wellknown.GRPCWeb: "envoy.grpc_web", - wellknown.Gzip: "envoy.gzip", - wellknown.HealthCheck: "envoy.health_check", - wellknown.IPTagging: "envoy.ip_tagging", - wellknown.Lua: "envoy.lua", - wellknown.HTTPRateLimit: "envoy.rate_limit", - wellknown.Router: "envoy.router", - wellknown.Squash: "envoy.squash", - wellknown.HttpInspector: "envoy.listener.http_inspector", - wellknown.OriginalDestination: "envoy.listener.original_dst", - "envoy.filters.listener.original_src": "envoy.listener.original_src", - wellknown.ProxyProtocol: "envoy.listener.proxy_protocol", - wellknown.TlsInspector: "envoy.listener.tls_inspector", - wellknown.ClientSSLAuth: "envoy.client_ssl_auth", - wellknown.ExternalAuthorization: "envoy.ext_authz", - wellknown.HTTPConnectionManager: "envoy.http_connection_manager", - wellknown.MongoProxy: "envoy.mongo_proxy", - wellknown.RateLimit: "envoy.ratelimit", - wellknown.RedisProxy: "envoy.redis_proxy", - wellknown.TCPProxy: "envoy.tcp_proxy", - } - - ReverseDeprecatedFilterNames = reverse(DeprecatedFilterNames) -) - -func reverse(names map[string]string) map[string]string { - resp := make(map[string]string, len(names)) - for k, v := range names { - resp[v] = k - } - return resp -} diff --git a/pkg/config/xds/filter_types.gen.go b/pkg/config/xds/filter_types.gen.go deleted file mode 100644 index 6ea48a38a..000000000 --- a/pkg/config/xds/filter_types.gen.go +++ /dev/null @@ -1,347 +0,0 @@ -//go:build !agent -// +build !agent - -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// GENERATED FILE -- DO NOT EDIT - -package xds - -import ( - _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/http/language/v3alpha" - _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/http/squash/v3" - _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/http/sxg/v3alpha" - _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/network/kafka_broker/v3" - _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/network/kafka_mesh/v3alpha" - _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/network/mysql_proxy/v3" - _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/network/postgres_proxy/v3alpha" - _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/network/rocketmq_proxy/v3" - _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/network/sip_proxy/router/v3alpha" - _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/network/sip_proxy/tra/v3alpha" - _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/network/sip_proxy/v3alpha" - _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/matching/input_matchers/hyperscan/v3alpha" - _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/private_key_providers/cryptomb/v3alpha" - _ "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/vcl/v3alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/admin/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/admin/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/api/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth" - _ "github.com/envoyproxy/go-control-plane/envoy/api/v2/cluster" - _ "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" - _ "github.com/envoyproxy/go-control-plane/envoy/api/v2/endpoint" - _ "github.com/envoyproxy/go-control-plane/envoy/api/v2/listener" - _ "github.com/envoyproxy/go-control-plane/envoy/api/v2/ratelimit" - _ "github.com/envoyproxy/go-control-plane/envoy/api/v2/route" - _ "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/config/cluster/aggregate/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/config/cluster/dynamic_forward_proxy/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/config/common/dynamic_forward_proxy/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/config/common/key_value/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/config/common/matcher/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/config/common/mutation_rules/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/config/common/tap/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/accesslog/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/dubbo/router/v2alpha1" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/fault/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/adaptive_concurrency/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/aws_lambda/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/aws_request_signing/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/buffer/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/cache/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/compressor/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/cors/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/csrf/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/dynamic_forward_proxy/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/dynamo/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/ext_authz/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/fault/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/grpc_http1_bridge/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/grpc_http1_reverse_bridge/v2alpha1" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/grpc_stats/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/grpc_web/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/gzip/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/header_to_metadata/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/health_check/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/ip_tagging/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/jwt_authn/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/lua/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/on_demand/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/original_src/v2alpha1" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/rate_limit/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/rbac/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/router/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/squash/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/tap/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/transcoder/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/listener/http_inspector/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/listener/original_dst/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/listener/original_src/v2alpha1" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/listener/proxy_protocol/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/listener/tls_inspector/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/client_ssl_auth/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/direct_response/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/dubbo_proxy/v2alpha1" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/echo/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/ext_authz/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/http_connection_manager/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/kafka_broker/v2alpha1" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/local_rate_limit/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/mongo_proxy/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/rate_limit/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/rbac/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/redis_proxy/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/sni_cluster/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/tcp_proxy/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/thrift_proxy/v2alpha1" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/thrift/rate_limit/v2alpha1" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/thrift/router/v2alpha1" - _ "github.com/envoyproxy/go-control-plane/envoy/config/filter/udp/udp_proxy/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/config/grpc_credential/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/config/grpc_credential/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/config/health_checker/redis/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/listener/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/config/metrics/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/metrics/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/config/overload/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/config/overload/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/config/ratelimit/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/ratelimit/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/config/resource_monitor/fixed_heap/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/config/resource_monitor/injected_resource/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/config/retry/omit_canary_hosts/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/retry/omit_host_metadata/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/retry/previous_hosts/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/config/tap/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/config/trace/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/trace/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/config/transport_socket/alts/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/config/transport_socket/raw_buffer/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/config/transport_socket/tap/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/data/accesslog/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/data/accesslog/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/data/cluster/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/data/cluster/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/data/core/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/data/core/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/data/dns/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/data/dns/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/data/tap/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/data/tap/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/file/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/filters/cel/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/grpc/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/open_telemetry/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/stream/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/wasm/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/bootstrap/internal_listener/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/cache/simple_http_cache/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/aggregate/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/dynamic_forward_proxy/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/redis/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/common/async_files/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/common/dynamic_forward_proxy/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/common/matching/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/common/ratelimit/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/common/tap/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/compression/brotli/compressor/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/compression/brotli/decompressor/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/compression/gzip/compressor/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/compression/gzip/decompressor/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/compression/zstd/compressor/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/compression/zstd/decompressor/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/config/validators/minimum_clusters/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/common/dependency/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/common/fault/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/common/matcher/action/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/adaptive_concurrency/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/admission_control/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/alternate_protocols_cache/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/aws_lambda/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/aws_request_signing/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/bandwidth_limit/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/buffer/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cache/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cdn_loop/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/composite/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/compressor/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cors/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/csrf/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/decompressor/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamic_forward_proxy/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamo/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_proc/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/fault/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/file_system_buffer/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/gcp_authn/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_http1_bridge/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_http1_reverse_bridge/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_json_transcoder/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_stats/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_web/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/gzip/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/header_to_metadata/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/health_check/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ip_tagging/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/jwt_authn/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/kill_request/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/local_ratelimit/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/lua/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/oauth2/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/on_demand/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/original_src/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ratelimit/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/rbac/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/set_metadata/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/stateful_session/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/tap/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/wasm/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/http_inspector/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/original_dst/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/original_src/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/proxy_protocol/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/tls_inspector/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/client_ssl_auth/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/connection_limit/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/direct_response/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/dubbo_proxy/router/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/dubbo_proxy/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/echo/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/ext_authz/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/local_ratelimit/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/meta_protocol_proxy/matcher/action/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/meta_protocol_proxy/matcher/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/meta_protocol_proxy/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/mongo_proxy/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/ratelimit/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/rbac/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/redis_proxy/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/sni_cluster/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/sni_dynamic_forward_proxy/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/thrift_proxy/filters/ratelimit/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/thrift_proxy/router/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/thrift_proxy/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/wasm/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/zookeeper_proxy/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/udp/dns_filter/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/udp/udp_proxy/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/formatter/metadata/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/formatter/req_without_query/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/health_checkers/redis/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/http/header_formatters/preserve_case/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/http/original_ip_detection/custom_header/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/http/original_ip_detection/xff/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/http/stateful_session/cookie/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/internal_redirect/allow_listed_routes/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/internal_redirect/previous_routes/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/internal_redirect/safe_cross_scheme/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/key_value/file_based/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/ring_hash/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/round_robin/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/wrr_locality/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/matching/common_inputs/environment_variable/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/matching/common_inputs/network/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/matching/input_matchers/consistent_hashing/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/matching/input_matchers/ip/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/network/dns_resolver/apple/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/network/dns_resolver/cares/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/network/socket_interface/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/quic/crypto_stream/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/quic/proof_source/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/rate_limit_descriptors/expr/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/rbac/matchers/upstream_ip_port/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/request_id/uuid/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/resource_monitors/fixed_heap/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/resource_monitors/injected_resource/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/retry/host/omit_canary_hosts/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/retry/host/omit_host_metadata/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/retry/host/previous_hosts/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/retry/priority/previous_priorities/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/stat_sinks/graphite_statsd/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/stat_sinks/wasm/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/alts/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/proxy_protocol/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/quic/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/raw_buffer/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/s2a/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/starttls/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tap/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tcp_stats/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/generic/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/http/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/tcp/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/tcp/generic/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/wasm/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/extensions/watchdog/profile_action/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/service/accesslog/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/service/accesslog/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/service/auth/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/service/auth/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/service/cluster/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/service/endpoint/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/service/event_reporting/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/service/event_reporting/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/service/extension/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/service/health/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/service/listener/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/service/load_stats/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/service/load_stats/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/service/metrics/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/service/metrics/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/service/ratelimit/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/service/ratelimit/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/service/route/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/service/runtime/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/service/secret/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/service/status/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/service/status/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/service/tap/v2alpha" - _ "github.com/envoyproxy/go-control-plane/envoy/service/tap/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/service/trace/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/service/trace/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/type/http/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/type/metadata/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/type/metadata/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/type/tracing/v2" - _ "github.com/envoyproxy/go-control-plane/envoy/type/tracing/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/type/v3" - _ "github.com/envoyproxy/go-control-plane/envoy/watchdog/v3" - _ "istio.io/api/envoy/config/filter/http/alpn/v2alpha1" - _ "istio.io/api/envoy/config/filter/http/authn/v2alpha1" - _ "istio.io/api/envoy/config/filter/http/jwt_auth/v2alpha1" - _ "istio.io/api/envoy/config/filter/network/tcp_cluster_rewrite/v2alpha1" -) diff --git a/pkg/config/xds/filter_types.go b/pkg/config/xds/filter_types.go deleted file mode 100644 index fca98bc1d..000000000 --- a/pkg/config/xds/filter_types.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// nolint: lll -// -//go:generate sh -c "echo '//go:build !agent' > filter_types.gen.go" -//go:generate sh -c "echo '// +build !agent\n' >> filter_types.gen.go" -//go:generate sh -c "echo '// Copyright Istio Authors' >> filter_types.gen.go" -//go:generate sh -c "echo '//' >> filter_types.gen.go" -//go:generate sh -c "echo '// Licensed under the Apache License, Version 2.0 (the \"License\");' >> filter_types.gen.go" -//go:generate sh -c "echo '// you may not use this file except in compliance with the License.' >> filter_types.gen.go" -//go:generate sh -c "echo '// You may obtain a copy of the License at' >> filter_types.gen.go" -//go:generate sh -c "echo '//' >> filter_types.gen.go" -//go:generate sh -c "echo '// http://www.apache.org/licenses/LICENSE-2.0' >> filter_types.gen.go" -//go:generate sh -c "echo '//' >> filter_types.gen.go" -//go:generate sh -c "echo '// Unless required by applicable law or agreed to in writing, software' >> filter_types.gen.go" -//go:generate sh -c "echo '// distributed under the License is distributed on an \"AS IS\" BASIS,' >> filter_types.gen.go" -//go:generate sh -c "echo '// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.' >> filter_types.gen.go" -//go:generate sh -c "echo '// See the License for the specific language governing permissions and' >> filter_types.gen.go" -//go:generate sh -c "echo '// limitations under the License.\n' >> filter_types.gen.go" -//go:generate sh -c "echo '// GENERATED FILE -- DO NOT EDIT\n' >> filter_types.gen.go" -//go:generate sh -c "echo 'package xds\n\nimport (' >> filter_types.gen.go" -//go:generate sh -c "go list github.com/envoyproxy/go-control-plane/... | grep 'v[2-9]' | grep -v /pkg/ | xargs -I{} echo '\t_ \"{}\"' >> filter_types.gen.go" -//go:generate sh -c "echo '\n\t// Istio-specific Envoy filters' >> filter_types.gen.go" -//go:generate sh -c "go list istio.io/api/envoy/config/filter/... | grep 'v[2-9]' | xargs -I{} echo '\t_ \"{}\"' >> filter_types.gen.go" -//go:generate sh -c "echo ')' >> filter_types.gen.go" -package xds - -// Import all Envoy filter types so they are registered and deserialization does not fail -// when using them in the "typed_config" attributes. -// The filter types are autogenerated by looking at all packages in go-control-plane -// As a result, this will need to be re-run when updating go-control-plane if new packages are added. diff --git a/pkg/config/xds/filters.go b/pkg/config/xds/filters.go deleted file mode 100644 index 48057ba77..000000000 --- a/pkg/config/xds/filters.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package xds - -import ( - resource "github.com/envoyproxy/go-control-plane/pkg/resource/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" -) - -const ( - WasmHTTPFilterType = resource.APITypePrefix + wellknown.HTTPWasm - TypedStructType = resource.APITypePrefix + "udpa.type.v1.TypedStruct" - - StatsFilterName = "istio.stats" - StackdriverFilterName = "istio.stackdriver" -) diff --git a/pkg/config/xds/lds.go b/pkg/config/xds/lds.go new file mode 100644 index 000000000..cc455d129 --- /dev/null +++ b/pkg/config/xds/lds.go @@ -0,0 +1,217 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 xds + +import ( + "encoding/json" + "strconv" +) + +import ( + xdsModel "github.com/dubbo-go-pixiu/pixiu-api/pkg/xds/model" + "gopkg.in/yaml.v2" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/config/xds/apiclient" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/server/controls" +) + +type LdsManager struct { + DiscoverApi + listenerMg controls.ListenerManager +} + +// Fetch overwrite DiscoverApi.Fetch. +func (l *LdsManager) Fetch() error { + r, err := l.DiscoverApi.Fetch("") //todo use local version + if err != nil { + return err + } + listeners := make([]*xdsModel.Listener, 0, len(r)) + for _, one := range r { + listener := &xdsModel.PixiuExtensionListeners{} + if err := one.To(listener); err != nil { + logger.Errorf("unknown resource of %s, expect Listener", one.GetName()) + continue + } + logger.Infof("listener xds server %v", listener) + listeners = append(listeners, listener.Listeners...) + } + l.setupListeners(listeners) + return nil +} + +func (l *LdsManager) Delta() error { + readCh, err := l.DiscoverApi.Delta() + if err != nil { + return err + } + go l.asyncHandler(readCh) + return nil +} + +func (l *LdsManager) asyncHandler(read chan *apiclient.DeltaResources) { + for delta := range read { + listeners := make([]*xdsModel.Listener, 0, len(delta.NewResources)) + for _, one := range delta.NewResources { + listener := &xdsModel.PixiuExtensionListeners{} + if err := one.To(listener); err != nil { + logger.Errorf("unknown resource of %s, expect Listener", one.GetName()) + continue + } + logger.Infof("listener xds server %v", listener) + listeners = append(listeners, listener.Listeners...) + } + + l.setupListeners(listeners) + } +} + +func (l *LdsManager) makeSocketAddress(address *xdsModel.SocketAddress) model.SocketAddress { + if address == nil { + return model.SocketAddress{} + } + return model.SocketAddress{ + Address: address.Address, + Port: int(address.Port), + ResolverName: address.ResolverName, + //Domains: _l.Address.do, todo add the domains + //CertsDir: _l.Address.SocketAddress"", //todo add the domains + } +} + +func (l *LdsManager) removeListeners(toRemoveHash map[string]struct{}) { + names := make([]string, 0, len(toRemoveHash)) + for name := range toRemoveHash { + names = append(names, name) + } + l.listenerMg.RemoveListener(names) +} + +// setupListeners setup listeners accord to dynamic resource +func (l *LdsManager) setupListeners(listeners []*xdsModel.Listener) { + //Make sure each one has a unique name like "host-port-protocol" + for _, v := range listeners { + v.Name = resolveListenerName(v.Address.SocketAddress.Address, int(v.Address.SocketAddress.Port), v.Protocol.String()) + } + + laterApplies := make([]func() error, 0, len(listeners)) + toRemoveHash := make(map[string]struct{}, len(listeners)) + + lm := l.listenerMg + activeListeners, err := lm.CloneXdsControlListener() + if err != nil { + logger.Errorf("Clone Xds Control Listener fail: %s", err) + return + } + //put all current listeners to $toRemoveHash + for _, v := range activeListeners { + //Make sure each one has a unique name like "host-port-protocol" + v.Name = resolveListenerName(v.Address.SocketAddress.Address, v.Address.SocketAddress.Port, v.ProtocolStr) + toRemoveHash[v.Name] = struct{}{} + } + + for _, listener := range listeners { + delete(toRemoveHash, listener.Name) + + modelListener := l.makeListener(listener) + // add or update later after removes + switch { + case lm.HasListener(modelListener.Name): + laterApplies = append(laterApplies, func() error { + return lm.UpdateListener(&modelListener) + }) + default: + laterApplies = append(laterApplies, func() error { + return lm.AddListener(&modelListener) + }) + } + } + // remove the listeners first to prevent tcp port conflict + l.removeListeners(toRemoveHash) + //do update and add new cluster. + for _, fn := range laterApplies { + if err := fn(); err != nil { + logger.Errorf("can not modify listener", err) + } + } +} + +func resolveListenerName(host string, port int, protocol string) string { + return host + "-" + strconv.Itoa(port) + "-" + protocol +} + +func (l *LdsManager) makeListener(listener *xdsModel.Listener) model.Listener { + return model.Listener{ + Name: listener.Name, + ProtocolStr: listener.Protocol.String(), + Protocol: model.ProtocolType(model.ProtocolTypeValue[listener.Protocol.String()]), + Address: l.makeAddress(listener.Address), + FilterChain: l.makeFilterChain(listener.FilterChain), + Config: nil, // todo set the additional config + } +} + +func (l *LdsManager) makeFilterChain(fChain *xdsModel.FilterChain) model.FilterChain { + return model.FilterChain{ + Filters: l.makeFilters(fChain.Filters), + } +} + +func (l *LdsManager) makeFilters(filters []*xdsModel.NetworkFilter) []model.NetworkFilter { + result := make([]model.NetworkFilter, 0, len(filters)) + for _, filter := range filters { + result = append(result, model.NetworkFilter{ + Name: filter.Name, + //Config: filter., todo define the config of filter + Config: l.makeConfig(filter), + }) + } + return result +} + +func (l *LdsManager) makeConfig(filter *xdsModel.NetworkFilter) (m map[string]interface{}) { + switch cfg := filter.Config.(type) { + case *xdsModel.NetworkFilter_Yaml: + if err := yaml.Unmarshal([]byte(cfg.Yaml.Content), &m); err != nil { + logger.Errorf("can not make yaml from filter.Config: %s", cfg.Yaml.Content, err) + } + case *xdsModel.NetworkFilter_Json: + if err := json.Unmarshal([]byte(cfg.Json.Content), &m); err != nil { + logger.Errorf("can not make json from filter.Config: %s", cfg.Json.Content, err) + } + case *xdsModel.NetworkFilter_Struct: + m = cfg.Struct.AsMap() + default: + logger.Errorf("can not get filter config of %s", filter.Name) + } + return +} + +func (l *LdsManager) makeAddress(addr *xdsModel.Address) model.Address { + if addr == nil { + return model.Address{} + } + return model.Address{ + SocketAddress: l.makeSocketAddress(addr.SocketAddress), + Name: addr.Name, + } +} diff --git a/pkg/config/xds/lds_test.go b/pkg/config/xds/lds_test.go new file mode 100644 index 000000000..9f71ab126 --- /dev/null +++ b/pkg/config/xds/lds_test.go @@ -0,0 +1,279 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 xds + +import ( + "testing" +) + +import ( + pixiupb "github.com/dubbo-go-pixiu/pixiu-api/pkg/xds/model" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/encoding/protojson" + structpb2 "google.golang.org/protobuf/types/known/structpb" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +func TestLdsManager_makeConfig(t *testing.T) { + var httpManagerConfigYaml = ` +route_config: + routes: + - match: + prefix: "/" + route: + cluster: "http_bin" + cluster_not_found_response_code: "505" +http_filters: + - name: dgp.filter.http.httpproxy + config: + - name: dgp.filter.http.response + config: +` + configMap := map[string]interface{}{ + "route_config": map[string]interface{}{ + "routes": []interface{}{ + map[string]interface{}{ + "match": map[string]interface{}{ + "prefix": "/", + }, + "route": map[string]interface{}{ + "cluster": "http_bin", + "cluster_not_found_response_code": "505", + }, + }, + }, + }, + "http_filters": []interface{}{ + map[string]interface{}{ + "name": "dgp.filter.http.httpproxy", + "config": nil, + }, + map[string]interface{}{ + "name": "dgp.filter.http.response", + "config": nil, + }, + }, + } + httpManagerConfigStruct, _ := structpb2.NewStruct(configMap) + + oneConfigMap := map[string]interface{}{ + "route_config": map[interface{}]interface{}{ + "routes": []interface{}{ + map[interface{}]interface{}{ + "match": map[interface{}]interface{}{ + "prefix": "/", + }, + "route": map[interface{}]interface{}{ + "cluster": "http_bin", + "cluster_not_found_response_code": "505", + }, + }, + }, + }, + "http_filters": []interface{}{ + map[interface{}]interface{}{ + "name": "dgp.filter.http.httpproxy", + "config": nil, + }, + map[interface{}]interface{}{ + "name": "dgp.filter.http.response", + "config": nil, + }, + }, + } + + type args struct { + filter *pixiupb.NetworkFilter + } + tests := []struct { + name string + args args + wantM map[string]interface{} + }{ + { + name: "yaml", + args: args{ + filter: &pixiupb.NetworkFilter{ + Name: "", + Config: &pixiupb.NetworkFilter_Yaml{ + Yaml: &pixiupb.Config{ + Content: httpManagerConfigYaml, + }}, + }, + }, + wantM: oneConfigMap, + }, + { + name: "struct", + args: args{ + filter: &pixiupb.NetworkFilter{ + Name: "", + Config: &pixiupb.NetworkFilter_Struct{Struct: httpManagerConfigStruct}, + }, + }, + wantM: configMap, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + l := &LdsManager{} + r := l.makeConfig(tt.args.filter) + assert := require.New(t) + assert.Equal(tt.wantM, r) + }) + } +} + +func TestMakeListener(t *testing.T) { + lm := &LdsManager{} + json := ` +{ + "name": "net/http", + "address": { + "socketAddress": { + "address": "0.0.0.0", + "port": "8080" + } + }, + "filterChain": { + "filters": [ + { + "name": "dgp.filter.httpconnectionmanager", + "struct": { + "http_filters": [ + { + "config": null, + "name": "dgp.filter.http.httpproxy" + } + ], + "route_config": { + "routes": [ + { + "match": { + "prefix": "/" + }, + "route": { + "cluster": "http_bin", + "cluster_not_found_response_code": 503 + } + } + ] + } + } + } + ] + } + } +` + l := &pixiupb.Listener{} + if err := protojson.Unmarshal([]byte(json), l); err != nil { + t.Fatal(err) + } + listener := lm.makeListener(l) + assert.NotNil(t, listener) + assert.Equal(t, "net/http", listener.Name) + assert.Equal(t, "0.0.0.0", listener.Address.SocketAddress.Address) + assert.Equal(t, 8080, listener.Address.SocketAddress.Port) + assert.Equal(t, 1, len(listener.FilterChain.Filters)) +} + +type mockListenerManager struct { + m map[string]*model.Listener +} + +func (m *mockListenerManager) AddListener(l *model.Listener) error { + m.m[l.Name] = l + return nil +} + +func (m *mockListenerManager) UpdateListener(l *model.Listener) error { + m.m[l.Name] = l + return nil +} + +func (m *mockListenerManager) RemoveListener(names []string) { + for _, name := range names { + delete(m.m, name) + } +} + +func (m *mockListenerManager) HasListener(name string) bool { + _, ok := m.m[name] + return ok +} + +func (m *mockListenerManager) CloneXdsControlListener() ([]*model.Listener, error) { + var res []*model.Listener + for _, v := range m.m { + res = append(res, v) + } + return res, nil +} + +func TestSetupListeners(t *testing.T) { + mock := &mockListenerManager{m: map[string]*model.Listener{}} + lm := &LdsManager{listenerMg: mock} + + listeners := []*pixiupb.Listener{ + { + Protocol: pixiupb.Listener_HTTP, + Address: &pixiupb.Address{ + SocketAddress: &pixiupb.SocketAddress{ + Address: "0.0.0.0", + Port: 8080, + }, + }, + FilterChain: &pixiupb.FilterChain{}, + }, + { + Protocol: pixiupb.Listener_TRIPLE, + Address: &pixiupb.Address{ + SocketAddress: &pixiupb.SocketAddress{ + Address: "0.0.0.0", + Port: 8081, + }, + }, + FilterChain: &pixiupb.FilterChain{}, + }, + } + lm.setupListeners(listeners) + for _, v := range listeners { + assert.Equal(t, v.Protocol.String(), model.ProtocolTypeName[int32(mock.m[v.Name].Protocol)]) + assert.Equal(t, v.Address.SocketAddress.Address, mock.m[v.Name].Address.SocketAddress.Address) + assert.Equal(t, int(v.Address.SocketAddress.Port), mock.m[v.Name].Address.SocketAddress.Port) + } + + newListeners := []*pixiupb.Listener{ + { + Protocol: pixiupb.Listener_HTTP, + Address: &pixiupb.Address{ + SocketAddress: &pixiupb.SocketAddress{ + Address: "0.0.0.0", + Port: 8080, + }, + }, + FilterChain: &pixiupb.FilterChain{}, + }, + } + lm.setupListeners(newListeners) + assert.Equal(t, 1, len(mock.m)) +} diff --git a/pkg/config/xds/xds.go b/pkg/config/xds/xds.go index c78817602..715796974 100644 --- a/pkg/config/xds/xds.go +++ b/pkg/config/xds/xds.go @@ -1,94 +1,170 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 xds import ( - "bytes" - "errors" - "fmt" + "sync" ) import ( - bootstrapv3 "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v3" - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - httpConn "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - "github.com/golang/protobuf/jsonpb" // nolint: staticcheck - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/structpb" - networking "istio.io/api/networking/v1alpha3" + "github.com/dubbo-go-pixiu/pixiu-api/pkg/xds" + "github.com/mitchellh/mapstructure" ) import ( - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/config/xds/apiclient" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/server/controls" ) -// nolint: interfacer -func BuildXDSObjectFromStruct(applyTo networking.EnvoyFilter_ApplyTo, value *structpb.Struct, strict bool) (proto.Message, error) { - if value == nil { - // for remove ops - return nil, nil +type ( + DiscoverApi interface { + Fetch(localVersion string) ([]*apiclient.ProtoAny, error) + Delta() (chan *apiclient.DeltaResources, error) } - var obj proto.Message - switch applyTo { - case networking.EnvoyFilter_CLUSTER: - obj = &cluster.Cluster{} - case networking.EnvoyFilter_LISTENER: - obj = &listener.Listener{} - case networking.EnvoyFilter_ROUTE_CONFIGURATION: - obj = &route.RouteConfiguration{} - case networking.EnvoyFilter_FILTER_CHAIN: - obj = &listener.FilterChain{} - case networking.EnvoyFilter_HTTP_FILTER: - obj = &httpConn.HttpFilter{} - case networking.EnvoyFilter_NETWORK_FILTER: - obj = &listener.Filter{} - case networking.EnvoyFilter_VIRTUAL_HOST: - obj = &route.VirtualHost{} - case networking.EnvoyFilter_HTTP_ROUTE: - obj = &route.Route{} - case networking.EnvoyFilter_EXTENSION_CONFIG: - obj = &core.TypedExtensionConfig{} - case networking.EnvoyFilter_BOOTSTRAP: - obj = &bootstrapv3.Bootstrap{} - default: - return nil, fmt.Errorf("Envoy filter: unknown object type for applyTo %s", applyTo.String()) // nolint: stylecheck + + AdapterConfig struct { + } + + Xds struct { + //ads DiscoverApi //aggregate discover service manager todo to implement + cds *CdsManager //cluster discover service manager + lds *LdsManager //listener discover service manager + exitCh chan struct{} + listenerMg controls.ListenerManager + clusterMg controls.ClusterManager + dynamicResourceMg controls.DynamicResourceManager } +) - if err := StructToMessage(value, obj, strict); err != nil { - return nil, fmt.Errorf("Envoy filter: %v", err) // nolint: stylecheck +func (a *Xds) createApiManager(config *model.ApiConfigSource, + node *model.Node, + resourceType apiclient.ResourceTypeName) DiscoverApi { + if config == nil { + return nil + } + + switch config.APIType { + case model.ApiTypeGRPC: + return apiclient.CreateGrpExtensionApiClient(config, node, a.exitCh, resourceType) + case model.ApiTypeIstioGRPC: + dubboServices, err := a.readDubboServiceFromListener() + if err != nil { + logger.Errorf("can not read listener. %v", err) + return nil + } + return apiclient.CreateEnvoyGrpcApiClient(config, node, a.exitCh, resourceType, apiclient.WithIstioService(dubboServices...)) + default: + logger.Errorf("un-support the api type %s", config.APITypeStr) + return nil } - return obj, nil } -func StructToMessage(pbst *structpb.Struct, out proto.Message, strict bool) error { - if pbst == nil { - return errors.New("nil struct") +func (a *Xds) readDubboServiceFromListener() ([]string, error) { + dubboServices := make([]string, 0) + listeners, err := a.listenerMg.CloneXdsControlListener() + if err != nil { + return nil, err } - buf := &bytes.Buffer{} - if err := (&jsonpb.Marshaler{OrigName: true}).Marshal(buf, pbst); err != nil { - return err + for _, l := range listeners { + for _, filter := range l.FilterChain.Filters { + if filter.Name != constant.HTTPConnectManagerFilter { + continue + } + var cfg *model.HttpConnectionManagerConfig + if filter.Config != nil { + if err := mapstructure.Decode(filter.Config, &cfg); err != nil { + logger.Error("read listener config error when init xds", err) + continue + } + } + for _, httpFilter := range cfg.HTTPFilters { + if httpFilter.Name == constant.HTTPDirectDubboProxyFilter { + for _, route := range cfg.RouteConfig.Routes { + dubboServices = append(dubboServices, route.Route.Cluster) + } + } + } + } + } + return dubboServices, nil +} + +func (a *Xds) Start() { + if a.dynamicResourceMg == nil { // if dm is nil, then config not initialized. + logger.Infof("can not get dynamic resource manager. maybe the config has not initialized") + return } + apiclient.Init(a.clusterMg) - // If strict is not set, ignore unknown fields as they may be sending versions of - // the proto we are not internally using - if strict { - return protomarshal.Unmarshal(buf.Bytes(), out) + // lds fetch just run on init phase. + if a.dynamicResourceMg.GetLds() != nil { + a.lds = &LdsManager{ + DiscoverApi: a.createApiManager(a.dynamicResourceMg.GetLds(), a.dynamicResourceMg.GetNode(), xds.ListenerType), + listenerMg: a.listenerMg, + } + if err := a.lds.Delta(); err != nil { + logger.Errorf("can not fetch lds err is %+v", err) + } } - return protomarshal.UnmarshalAllowUnknown(buf.Bytes(), out) + // catch the ongoing cds config change. + if a.dynamicResourceMg.GetCds() != nil { + a.cds = &CdsManager{ + DiscoverApi: a.createApiManager(a.dynamicResourceMg.GetCds(), a.dynamicResourceMg.GetNode(), xds.ClusterType), + clusterMg: a.clusterMg, + } + if err := a.cds.Delta(); err != nil { + logger.Errorf("can not fetch lds") + } + } + +} + +func (a *Xds) Stop() { + apiclient.Stop() + close(a.exitCh) +} + +var ( + client Client + once sync.Once +) + +// Client xds client +type Client interface { + Stop() +} + +// StartXdsClient create XdsClient and run. only one xds client create at first(singleton) +func StartXdsClient(listenerMg controls.ListenerManager, clusterMg controls.ClusterManager, drm controls.DynamicResourceManager) Client { + once.Do(func() { + xdsClient := &Xds{ + listenerMg: listenerMg, + clusterMg: clusterMg, + dynamicResourceMg: drm, + exitCh: make(chan struct{}), + } + xdsClient.Start() + client = xdsClient + }) + + return client } diff --git a/pkg/config/xds/xds_test.go b/pkg/config/xds/xds_test.go new file mode 100644 index 000000000..f1956d72c --- /dev/null +++ b/pkg/config/xds/xds_test.go @@ -0,0 +1,133 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 xds + +import ( + "context" + "fmt" + "testing" +) + +import ( + monkey "github.com/cch123/supermonkey" + "github.com/dubbo-go-pixiu/pixiu-api/pkg/xds" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/connectivity" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/config/xds/apiclient" + "github.com/apache/dubbo-go-pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/server/controls" + "github.com/apache/dubbo-go-pixiu/pkg/server/controls/mocks" +) + +func TestAdapter_createApiManager(t *testing.T) { + cluster := &model.ClusterConfig{ + Name: "cluster-1", + TypeStr: "GRPC", + Endpoints: []*model.Endpoint{ + { + Address: model.SocketAddress{ + Address: "localhost", + Port: 18000, + }, + }, + }, + } + + node := model.Node{ + Cluster: "test-cluster", + Id: "node-test-1", + } + + apiConfig := model.ApiConfigSource{ + APIType: model.ApiTypeGRPC, + APITypeStr: "GRPC", + ClusterName: []string{"cluster-1"}, + } + + var state = connectivity.Ready + gconn := &grpc.ClientConn{} + monkey.Patch(grpc.DialContext, func(ctx context.Context, target string, opts ...grpc.DialOption) (conn *grpc.ClientConn, err error) { + fmt.Println("***** DialContext") + return gconn, nil + }) + monkey.Patch((*grpc.ClientConn).Close, func(_ *grpc.ClientConn) error { + return nil + }) + monkey.Patch((*grpc.ClientConn).GetState, func(_ *grpc.ClientConn) connectivity.State { + return state + }) + //monkey.Patch(server.GetDynamicResourceManager, func() server.DynamicResourceManager { + // return &server.DynamicResourceManagerImpl{} + //}) + //monkey.Patch((*server.DynamicResourceManagerImpl).GetLds, func(_ *server.DynamicResourceManagerImpl) *model.ApiConfigSource { + // return &apiConfig + //}) + //monkey.Patch((*server.DynamicResourceManagerImpl).GetCds, func(_ *server.DynamicResourceManagerImpl) *model.ApiConfigSource { + // return &apiConfig + //}) + //monkey.Patch(server.GetClusterManager, func() *server.ClusterManager { + // return nil + //}) + //monkey.Patch((*server.ClusterManager).CloneStore, func(_ *server.ClusterManager) (*server.ClusterStore, error) { + // return &server.ClusterStore{ + // Config: []*model.Cluster{cluster}, + // Version: 1, + // }, nil + //}) + + monkey.Patch((*apiclient.GrpcExtensionApiClient).Fetch, func(_ *apiclient.GrpcExtensionApiClient, localVersion string) ([]*apiclient.ProtoAny, error) { + return nil, nil + }) + monkey.Patch((*apiclient.GrpcExtensionApiClient).Delta, func(_ *apiclient.GrpcExtensionApiClient) (chan *apiclient.DeltaResources, error) { + ch := make(chan *apiclient.DeltaResources) + close(ch) + return ch, nil + }) + + //init cluster manager + ctrl := gomock.NewController(t) + clusterMg := mocks.NewMockClusterManager(ctrl) + { + clusterMg.EXPECT(). + HasCluster("cluster-1").AnyTimes(). + Return(true) + clusterMg.EXPECT().CloneXdsControlStore().DoAndReturn(func() (controls.ClusterStore, error) { + store := mocks.NewMockClusterStore(ctrl) + store.EXPECT().Config().Return([]*model.ClusterConfig{cluster}) + return store, nil + }) + // delete this stub by #https://github.com/golang/mock/issues/530 + //clusterMg.EXPECT().HasCluster("cluster-2").Return(false) + apiclient.Init(clusterMg) + } + + defer monkey.UnpatchAll() + + ada := Xds{ + clusterMg: clusterMg, + } + ada.Start() + api := ada.createApiManager(&apiConfig, &node, xds.ClusterType) + assert := require.New(t) + assert.NotNil(api) +} diff --git a/pixiu/pkg/context/base_context.go b/pkg/context/base_context.go similarity index 96% rename from pixiu/pkg/context/base_context.go rename to pkg/context/base_context.go index 182dd9e6b..a34e4758f 100644 --- a/pixiu/pkg/context/base_context.go +++ b/pkg/context/base_context.go @@ -18,7 +18,7 @@ package context import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) type ( diff --git a/pkg/context/dubbo/context.go b/pkg/context/dubbo/context.go new file mode 100644 index 000000000..54105baf1 --- /dev/null +++ b/pkg/context/dubbo/context.go @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 dubbo + +import ( + "context" +) + +import ( + "dubbo.apache.org/dubbo-go/v3/protocol" + "dubbo.apache.org/dubbo-go/v3/protocol/invocation" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +// RpcContext the rpc invocation context +type RpcContext struct { + RpcInvocation *invocation.RPCInvocation + RpcResult protocol.Result + Route *model.RouteAction + Ctx context.Context +} + +// SetError set error in RpcResult +func (c *RpcContext) SetError(err error) { + if c.RpcResult == nil { + c.RpcResult = &protocol.RPCResult{} + } + c.RpcResult.SetError(err) +} + +// SetResult set successful result in RpcResult +func (c *RpcContext) SetResult(result protocol.Result) { + c.RpcResult = result +} + +// SetInvocation set invocation +func (c *RpcContext) SetInvocation(invocation *invocation.RPCInvocation) { + c.RpcInvocation = invocation +} + +// SetRoute set route +func (c *RpcContext) SetRoute(route *model.RouteAction) { + c.Route = route +} + +func (c *RpcContext) GenerateHash() string { + req := c.RpcInvocation + return req.MethodName() + "." + req.Invoker().GetURL().String() +} diff --git a/pkg/context/http/context.go b/pkg/context/http/context.go new file mode 100644 index 000000000..56c8b246a --- /dev/null +++ b/pkg/context/http/context.go @@ -0,0 +1,238 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 http + +import ( + "context" + "encoding/json" + "math" + "net" + "net/http" + "net/url" + "strings" + "time" +) + +import ( + "github.com/dubbo-go-pixiu/pixiu-api/pkg/router" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/client" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +const abortIndex int8 = math.MaxInt8 / 2 + +// HttpContext http context +type HttpContext struct { + //Deprecated: waiting to delete + Index int8 + //Deprecated: waiting to delete + Filters FilterChain + Timeout time.Duration + Ctx context.Context + Params map[string]interface{} + + // localReply Means that the request was interrupted, + // which may occur in the Decode or Encode stage + localReply bool + // statusCode code will be return + statusCode int + // localReplyBody: happen error + localReplyBody []byte + // the response context will return. + TargetResp *client.Response + // client call response. + SourceResp interface{} + + HttpConnectionManager model.HttpConnectionManagerConfig + Route *model.RouteAction + Api *router.API + + Request *http.Request + Writer http.ResponseWriter +} + +type ( + // ErrResponse err response. + ErrResponse struct { + Message string `json:"message"` + } + + // FilterFunc filter func, filter + FilterFunc func(c *HttpContext) + + // FilterChain filter chain + FilterChain []FilterFunc +) + +// Deprecated: Next logic for lookup filter +func (hc *HttpContext) Next() { +} + +// Reset reset http context +func (hc *HttpContext) Reset() { + hc.Ctx = nil + hc.Index = -1 + hc.Filters = []FilterFunc{} + hc.Route = nil + hc.Api = nil + + hc.TargetResp = nil + hc.SourceResp = nil + hc.statusCode = 0 + hc.localReply = false + hc.localReplyBody = nil +} + +// RouteEntry set route +func (hc *HttpContext) RouteEntry(r *model.RouteAction) { + hc.Route = r +} + +// GetRouteEntry get route +func (hc *HttpContext) GetRouteEntry() *model.RouteAction { + return hc.Route +} + +// AddHeader add header +func (hc *HttpContext) AddHeader(k, v string) { + hc.Writer.Header().Add(k, v) +} + +// GetHeader get header +func (hc *HttpContext) GetHeader(k string) string { + return hc.Request.Header.Get(k) +} + +// AllHeaders get all headers +func (hc *HttpContext) AllHeaders() http.Header { + return hc.Request.Header +} + +// GetUrl get http request url +func (hc *HttpContext) GetUrl() string { + return hc.Request.URL.Path +} + +func (hc *HttpContext) SetUrl(url string) { + hc.Request.URL.Path = url +} + +// GetMethod get method, POST/GET ... +func (hc *HttpContext) GetMethod() string { + return hc.Request.Method +} + +// GetClientIP get client IP +func (hc *HttpContext) GetClientIP() string { + xForwardedFor := hc.Request.Header.Get("X-Forwarded-For") + ip := strings.TrimSpace(strings.Split(xForwardedFor, ",")[0]) + if len(ip) != 0 { + return ip + } + + ip = strings.TrimSpace(hc.Request.Header.Get("X-Real-Ip")) + if len(ip) != 0 { + return ip + } + + if ip, _, err := net.SplitHostPort(strings.TrimSpace(hc.Request.RemoteAddr)); err == nil && len(ip) != 0 { + return ip + } + + return "" +} + +// GetApplicationName get application name +func (hc *HttpContext) GetApplicationName() string { + if u, err := url.Parse(hc.Request.RequestURI); err == nil { + return strings.Split(u.Path, "/")[0] + } + + return "" +} + +// SendLocalReply Means that the request was interrupted and Response will be sent directly +// Even if it’s currently in to Decode stage +func (hc *HttpContext) SendLocalReply(status int, body []byte) { + hc.localReply = true + hc.statusCode = status + hc.localReplyBody = body + hc.TargetResp = &client.Response{Data: body} + if json.Valid(body) { + hc.AddHeader(constant.HeaderKeyContextType, constant.HeaderValueApplicationJson) + } else { + hc.AddHeader(constant.HeaderKeyContextType, constant.HeaderValueTextPlain) + } + writer := hc.Writer + writer.WriteHeader(status) + _, err := writer.Write(body) + if err != nil { + logger.Errorf("write fail: %v", err) + } +} + +func (hc *HttpContext) GetLocalReplyBody() []byte { + return hc.localReplyBody +} + +func (hc *HttpContext) GetStatusCode() int { + return hc.statusCode +} + +func (hc *HttpContext) StatusCode(code int) { + hc.statusCode = code +} + +func (hc *HttpContext) LocalReply() bool { + return hc.localReply +} + +// API sets the API to http context +func (hc *HttpContext) API(api router.API) { + if hc.Timeout > api.Timeout { + hc.Timeout = api.Timeout + } + hc.Api = &api +} + +// GetAPI get api +func (hc *HttpContext) GetAPI() *router.API { + return hc.Api +} + +// Deprecated: Abort filter chain break , filter after the current filter will not executed. +func (hc *HttpContext) Abort() { + hc.Index = abortIndex +} + +// Deprecated: AppendFilterFunc append filter func. +func (hc *HttpContext) AppendFilterFunc(ff ...FilterFunc) { + for _, v := range ff { + hc.Filters = append(hc.Filters, v) + } +} + +func (hc *HttpContext) GenerateHash() string { + req := hc.Request + return req.Method + "." + req.RequestURI +} diff --git a/pixiu/pkg/context/http/context_test.go b/pkg/context/http/context_test.go similarity index 100% rename from pixiu/pkg/context/http/context_test.go rename to pkg/context/http/context_test.go diff --git a/pkg/context/mock/context.go b/pkg/context/mock/context.go new file mode 100644 index 000000000..7fb0147cf --- /dev/null +++ b/pkg/context/mock/context.go @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 mock + +import ( + "context" + "fmt" + "net/http" +) + +import ( + contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http" +) + +// GetMockHTTPContext mock context for test. +func GetMockHTTPContext(r *http.Request) *contexthttp.HttpContext { + result := &contexthttp.HttpContext{ + Index: -1, + Request: r, + } + + w := mockWriter{header: map[string][]string{}} + result.Writer = &w + result.Reset() + result.Ctx = context.Background() + return result +} + +type mockWriter struct { + header http.Header +} + +func (w *mockWriter) Header() http.Header { + return w.header +} + +func (w *mockWriter) Write(b []byte) (int, error) { + fmt.Println(string(b)) + return -1, nil +} + +func (w *mockWriter) WriteHeader(statusCode int) { + fmt.Println(statusCode) +} diff --git a/pkg/dns/client/dns.go b/pkg/dns/client/dns.go deleted file mode 100644 index 8e1c78676..000000000 --- a/pkg/dns/client/dns.go +++ /dev/null @@ -1,636 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package client - -import ( - "fmt" - "net" - "os" - "strings" - "sync/atomic" - "time" -) - -import ( - "github.com/google/uuid" - "github.com/miekg/dns" - istiolog "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - dnsProto "github.com/apache/dubbo-go-pixiu/pkg/dns/proto" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -var log = istiolog.RegisterScope("dns", "Istio DNS proxy", 0) - -// LocalDNSServer holds configurations for the DNS downstreamUDPServer in Istio Agent -type LocalDNSServer struct { - // Holds the pointer to the DNS lookup table - lookupTable atomic.Value - - // nameTable holds the original NameTable, for debugging - nameTable atomic.Value - - dnsProxies []*dnsProxy - - resolvConfServers []string - searchNamespaces []string - // The namespace where the proxy resides - // determines the hosts used for shortname resolution - proxyNamespace string - // Optimizations to save space and time - proxyDomain string - proxyDomainParts []string - - respondBeforeSync bool -} - -// LookupTable is borrowed from https://github.com/coredns/coredns/blob/master/plugin/hosts/hostsfile.go -type LookupTable struct { - // This table will be first looked up to see if the host is something that we got a Nametable entry for - // (i.e. came from istiod's service registry). If it is, then we will be able to confidently return - // NXDOMAIN errors for AAAA records for such hosts when only A records exist (or vice versa). If the - // host does not exist in this map, then we will return nil, causing the caller to query the upstream - // DNS server to resolve the host. Without this map, we would end up making unnecessary upstream DNS queries - // for hosts that will never resolve (e.g., AAAA for svc1.ns1.svc.cluster.local.svc.cluster.local.) - allHosts map[string]struct{} - - // The key is a FQDN matching a DNS query (like example.com.), the value is pre-created DNS RR records - // of A or AAAA type as appropriate. - name4 map[string][]dns.RR - name6 map[string][]dns.RR - // The cname records here (comprised of different variants of the hosts above, - // expanded by the search namespaces) pointing to the actual host. - cname map[string][]dns.RR -} - -const ( - // In case the client decides to honor the TTL, keep it low so that we can always serve - // the latest IP for a host. - // TODO: make it configurable - defaultTTLInSeconds = 30 -) - -func NewLocalDNSServer(proxyNamespace, proxyDomain string, addr string) (*LocalDNSServer, error) { - h := &LocalDNSServer{ - proxyNamespace: proxyNamespace, - } - - registerStats() - - // proxyDomain could contain the namespace making it redundant. - // we just need the .svc.cluster.local piece - parts := strings.Split(proxyDomain, ".") - if len(parts) > 0 { - if parts[0] == proxyNamespace { - parts = parts[1:] - } - h.proxyDomainParts = parts - h.proxyDomain = strings.Join(parts, ".") - } - - resolvConf := "/etc/resolv.conf" - // If running as root and the alternate resolv.conf file exists, use it instead. - // This is used when running in Docker or VMs, without iptables DNS interception. - if strings.HasSuffix(addr, ":53") { - if os.Getuid() == 0 { - h.respondBeforeSync = true - // TODO: we can also copy /etc/resolv.conf to /var/lib/istio/resolv.conf and - // replace it with 'nameserver 127.0.0.1' - if _, err := os.Stat("/var/lib/istio/resolv.conf"); !os.IsNotExist(err) { - resolvConf = "/var/lib/istio/resolv.conf" - } - } else { - log.Error("DNS address :53 and not running as root, use default") - addr = "localhost:15053" - } - } - - // We will use the local resolv.conf for resolving unknown names. - dnsConfig, err := dns.ClientConfigFromFile(resolvConf) - if err != nil { - log.Warnf("failed to load /etc/resolv.conf: %v", err) - return nil, err - } - - // Unlike traditional DNS resolvers, we do not need to append the search - // namespace to a given query and try to resolve it. This is because the - // agent acts as a DNS interceptor for DNS queries made by the application. - // The application's resolver is already sending us DNS queries, one for each - // of the DNS search namespaces. We simply need to check the existence of this - // name in our local nametable. If not, we will forward the query to the - // upstream resolvers as is. - if dnsConfig != nil { - for _, s := range dnsConfig.Servers { - h.resolvConfServers = append(h.resolvConfServers, net.JoinHostPort(s, dnsConfig.Port)) - } - h.searchNamespaces = dnsConfig.Search - } - - log.WithLabels("search", h.searchNamespaces, "servers", h.resolvConfServers).Debugf("initialized DNS") - - if addr == "" { - addr = "localhost:15053" - } - v4, v6 := separateIPtypes(dnsConfig.Servers) - host, port, err := net.SplitHostPort(addr) - if err != nil { - return nil, fmt.Errorf("dns address must be a valid host:port") - } - addresses := []string{addr} - if host == "localhost" && len(v4)+len(v6) > 0 { - addresses = []string{} - // When binding to "localhost", go will pick v4 OR v6. In dual stack, we may need v4 AND v6. - // If we are in this situation, explicitly listen to v4, v6, or both. - if len(v4) > 0 { - addresses = append(addresses, net.JoinHostPort("127.0.0.1", port)) - } - if len(v6) > 0 { - addresses = append(addresses, net.JoinHostPort("::1", port)) - } - } - for _, ipAddr := range addresses { - for _, proto := range []string{"udp", "tcp"} { - proxy, err := newDNSProxy(proto, ipAddr, h) - if err != nil { - return nil, err - } - h.dnsProxies = append(h.dnsProxies, proxy) - - } - } - - return h, nil -} - -// StartDNS starts DNS-over-UDP and DNS-over-TCP servers. -func (h *LocalDNSServer) StartDNS() { - for _, p := range h.dnsProxies { - go p.start() - } -} - -func (h *LocalDNSServer) UpdateLookupTable(nt *dnsProto.NameTable) { - lookupTable := &LookupTable{ - allHosts: map[string]struct{}{}, - name4: map[string][]dns.RR{}, - name6: map[string][]dns.RR{}, - cname: map[string][]dns.RR{}, - } - h.BuildAlternateHosts(nt, lookupTable.buildDNSAnswers) - h.lookupTable.Store(lookupTable) - h.nameTable.Store(nt) - log.Debugf("updated lookup table with %d hosts", len(lookupTable.allHosts)) -} - -// BuildAlternateHosts builds alternate hosts for Kubernetes services in the name table and -// calls the passed in function with the built alternate hosts. -func (h *LocalDNSServer) BuildAlternateHosts(nt *dnsProto.NameTable, - apply func(map[string]struct{}, []net.IP, []net.IP, []string)) { - for hostname, ni := range nt.Table { - // Given a host - // if its a non-k8s host, store the host+. as the key with the pre-computed DNS RR records - // if its a k8s host, store all variants (i.e. shortname+., shortname+namespace+., fqdn+., etc.) - // shortname+. is only for hosts in current namespace - var altHosts sets.Set - if ni.Registry == string(provider.Kubernetes) { - altHosts = generateAltHosts(hostname, ni, h.proxyNamespace, h.proxyDomain, h.proxyDomainParts) - } else { - if !strings.HasSuffix(hostname, ".") { - hostname += "." - } - altHosts = sets.New(hostname) - } - ipv4, ipv6 := separateIPtypes(ni.Ips) - if len(ipv6) == 0 && len(ipv4) == 0 { - // malformed ips - continue - } - apply(altHosts, ipv4, ipv6, h.searchNamespaces) - } -} - -// upstrem sends the requeset to the upstream server, with associated logs and metrics -func (h *LocalDNSServer) upstream(proxy *dnsProxy, req *dns.Msg, hostname string) *dns.Msg { - upstreamRequests.Increment() - start := time.Now() - // We did not find the host in our internal cache. Query upstream and return the response as is. - log.Debugf("response for hostname %q not found in dns proxy, querying upstream", hostname) - response := h.queryUpstream(proxy.upstreamClient, req, log) - requestDuration.Record(time.Since(start).Seconds()) - log.Debugf("upstream response for hostname %q : %v", hostname, response) - return response -} - -// ServeDNS is the implementation of DNS interface -func (h *LocalDNSServer) ServeDNS(proxy *dnsProxy, w dns.ResponseWriter, req *dns.Msg) { - requests.Increment() - var response *dns.Msg - log := log.WithLabels("protocol", proxy.protocol, "edns", req.IsEdns0() != nil) - if log.DebugEnabled() { - id := uuid.New() - log = log.WithLabels("id", id) - } - log.Debugf("request %v", req) - - if len(req.Question) == 0 { - response = new(dns.Msg) - response.SetReply(req) - response.Rcode = dns.RcodeServerFailure - _ = w.WriteMsg(response) - return - } - - lp := h.lookupTable.Load() - hostname := strings.ToLower(req.Question[0].Name) - if lp == nil { - if h.respondBeforeSync { - response = h.upstream(proxy, req, hostname) - response.Truncate(size(proxy.protocol, req)) - _ = w.WriteMsg(response) - } else { - log.Debugf("dns request for host %q before lookup table is loaded", hostname) - response = new(dns.Msg) - response.SetReply(req) - response.Rcode = dns.RcodeServerFailure - _ = w.WriteMsg(response) - } - return - } - lookupTable := lp.(*LookupTable) - var answers []dns.RR - - // This name will always end in a dot. - // We expect only one question in the query even though the spec allows many - // clients usually do not do more than one query either. - answers, hostFound := lookupTable.lookupHost(req.Question[0].Qtype, hostname) - - if hostFound { - response = new(dns.Msg) - response.SetReply(req) - // We are the authority here, since we control DNS for known hostnames - response.Authoritative = true - // Even if answers is empty, we still return NOERROR. This matches expected behavior of DNS - // servers. NXDOMAIN means we do not know *anything* about the domain; if we set it here then - // a client (ie curl, see https://github.com/istio/istio/issues/31250) sending parallel - // requests for A and AAAA may get NXDOMAIN for AAAA and treat the entire thing as a NXDOMAIN - response.Answer = answers - // Randomize the responses; this ensures for things like headless services we can do DNS-LB - // This matches standard kube-dns behavior. We only do this for cached responses as the - // upstream DNS server would already round robin if desired. - if len(answers) > 0 { - roundRobinResponse(response) - } - log.Debugf("response for hostname %q (found=true): %v", hostname, response) - } else { - response = h.upstream(proxy, req, hostname) - } - // Compress the response - we don't know if the incoming response was compressed or not. If it was, - // but we don't compress on the outbound, we will run into issues. For example, if the compressed - // size is 450 bytes but uncompressed 1000 bytes now we are outside of the non-eDNS UDP size limits - response.Truncate(size(proxy.protocol, req)) - _ = w.WriteMsg(response) -} - -// IsReady returns true if DNS lookup table is updated atleast once. -func (h *LocalDNSServer) IsReady() bool { - return h.lookupTable.Load() != nil -} - -func (h *LocalDNSServer) NameTable() *dnsProto.NameTable { - lt := h.nameTable.Load() - if lt == nil { - return nil - } - return lt.(*dnsProto.NameTable) -} - -// Inspired by https://github.com/coredns/coredns/blob/master/plugin/loadbalance/loadbalance.go -func roundRobinResponse(res *dns.Msg) { - if res.Rcode != dns.RcodeSuccess { - return - } - - if res.Question[0].Qtype == dns.TypeAXFR || res.Question[0].Qtype == dns.TypeIXFR { - return - } - - res.Answer = roundRobin(res.Answer) - res.Ns = roundRobin(res.Ns) - res.Extra = roundRobin(res.Extra) -} - -func roundRobin(in []dns.RR) []dns.RR { - cname := make([]dns.RR, 0) - address := make([]dns.RR, 0) - mx := make([]dns.RR, 0) - rest := make([]dns.RR, 0) - for _, r := range in { - switch r.Header().Rrtype { - case dns.TypeCNAME: - cname = append(cname, r) - case dns.TypeA, dns.TypeAAAA: - address = append(address, r) - case dns.TypeMX: - mx = append(mx, r) - default: - rest = append(rest, r) - } - } - - roundRobinShuffle(address) - roundRobinShuffle(mx) - - out := append(cname, rest...) - out = append(out, address...) - out = append(out, mx...) - return out -} - -func roundRobinShuffle(records []dns.RR) { - switch l := len(records); l { - case 0, 1: - break - case 2: - if dns.Id()%2 == 0 { - records[0], records[1] = records[1], records[0] - } - default: - for j := 0; j < l*(int(dns.Id())%4+1); j++ { - q := int(dns.Id()) % l - p := int(dns.Id()) % l - if q == p { - p = (p + 1) % l - } - records[q], records[p] = records[p], records[q] - } - } -} - -func (h *LocalDNSServer) Close() { - for _, p := range h.dnsProxies { - p.close() - } -} - -// TODO: Figure out how to send parallel queries to all nameservers -func (h *LocalDNSServer) queryUpstream(upstreamClient *dns.Client, req *dns.Msg, scope *istiolog.Scope) *dns.Msg { - var response *dns.Msg - for _, upstream := range h.resolvConfServers { - cResponse, _, err := upstreamClient.Exchange(req, upstream) - if err == nil { - response = cResponse - break - } - scope.Infof("upstream failure: %v", err) - } - if response == nil { - failures.Increment() - response = new(dns.Msg) - response.SetReply(req) - response.Rcode = dns.RcodeServerFailure - } - return response -} - -func separateIPtypes(ips []string) (ipv4, ipv6 []net.IP) { - for _, ip := range ips { - addr := net.ParseIP(ip) - if addr == nil { - log.Debugf("ignoring un-parsable IP address: %v", ip) - continue - } - if addr.To4() != nil { - ipv4 = append(ipv4, addr.To4()) - } else { - ipv6 = append(ipv6, addr) - } - } - return -} - -func generateAltHosts(hostname string, nameinfo *dnsProto.NameTable_NameInfo, proxyNamespace, proxyDomain string, - proxyDomainParts []string, -) sets.Set { - out := sets.New() - out.Insert(hostname + ".") - // do not generate alt hostnames if the service is in a different domain (i.e. cluster) than the proxy - // as we have no way to resolve conflicts on name.namespace entries across clusters of different domains - if proxyDomain == "" || !strings.HasSuffix(hostname, proxyDomain) { - return out - } - out.Insert(nameinfo.Shortname + "." + nameinfo.Namespace + ".") - if proxyNamespace == nameinfo.Namespace { - out.Insert(nameinfo.Shortname + ".") - } - // Do we need to generate entries for name.namespace.svc, name.namespace.svc.cluster, etc. ? - // If these are not that frequently used, then not doing so here will save some space and time - // as some people have very long proxy domains with multiple dots - // For now, we will generate just one more domain (which is usually the .svc piece). - out.Insert(nameinfo.Shortname + "." + nameinfo.Namespace + "." + proxyDomainParts[0] + ".") - - // Add any additional alt hostnames. - // nolint: staticcheck - for _, altHost := range nameinfo.AltHosts { - out.Insert(altHost + ".") - } - return out -} - -// Given a host, this function first decides if the host is part of our service registry. -// If it is not part of the registry, return nil so that caller queries upstream. If it is part -// of registry, we will look it up in one of our tables, failing which we will return NXDOMAIN. -func (table *LookupTable) lookupHost(qtype uint16, hostname string) ([]dns.RR, bool) { - var hostFound bool - - question := host.Name(hostname) - wildcard := false - // First check if host exists in all hosts. - _, hostFound = table.allHosts[hostname] - // If it is not found, check if a wildcard host exists for it. - // For example for "*.example.com", with the question "svc.svcns.example.com", - // we check if we have entries for "*.svcns.example.com", "*.example.com" etc. - if !hostFound { - labels := dns.SplitDomainName(hostname) - for idx := range labels { - qhost := "*." + strings.Join(labels[idx+1:], ".") + "." - if _, hostFound = table.allHosts[qhost]; hostFound { - wildcard = true - hostname = qhost - break - } - } - } - - if !hostFound { - return nil, false - } - - var out []dns.RR - // Odds are, the first query will always be an expanded hostname - // (productpage.ns1.svc.cluster.local.ns1.svc.cluster.local) - // So lookup the cname table first - cn := table.cname[hostname] - if len(cn) > 0 { - // this was a cname match - hostname = cn[0].(*dns.CNAME).Target - } - var ipAnswers []dns.RR - var wcAnswers []dns.RR - switch qtype { - case dns.TypeA: - ipAnswers = table.name4[hostname] - case dns.TypeAAAA: - ipAnswers = table.name6[hostname] - default: - // TODO: handle PTR records for reverse dns lookups - return nil, false - } - - if len(ipAnswers) > 0 { - // For wildcard hosts, set the host that is being queried for. - if wildcard { - for _, answer := range ipAnswers { - copied := dns.Copy(answer) - copied.Header().Name = string(question) - wcAnswers = append(wcAnswers, copied) - } - } - // We will return a chained response. In a chained response, the first entry is the cname record, - // and the second one is the A/AAAA record itself. Some clients do not follow cname redirects - // with additional DNS queries. Instead, they expect all the resolved records to be in the same - // big DNS response (presumably assuming that a recursive DNS query should do the deed, resolve - // cname et al and return the composite response). - out = append(out, cn...) - if wildcard { - out = append(out, wcAnswers...) - } else { - out = append(out, ipAnswers...) - } - } - return out, hostFound -} - -// This function stores the list of hostnames along with the precomputed DNS response for that hostname. -// Most hostnames have a DNS response containing the A/AAAA records. In addition, this function stores a -// variant of the host+ the first search domain in resolv.conf as the first query -// is likely to be host.ns.svc.cluster.local (e.g., www.google.com.ns1.svc.cluster.local) due to -// the list of search namespaces in resolv.conf (unless the app explicitly does www.google.com. which is unlikely). -// We will resolve www.google.com.ns1.svc.cluster.local with a CNAME record pointing to www.google.com. -// which will cause the client's resolver to automatically resolve www.google.com. , and short circuit the lengthy -// search process down to just two DNS queries. This will eliminate unnecessary upstream DNS queries from the -// agent, reduce load on DNS servers and improve overall latency. This idea was borrowed and adapted from -// the autopath plugin in coredns. The implementation here is very different from auto path though. -// Autopath does inline computation to see if the given query could potentially match something else -// and then returns a CNAME record. In our case, we preemptively store these random dns names as a host -// in the lookup table with a CNAME record as the DNS response. This technique eliminates the need -// to do string parsing, memory allocations, etc. at query time at the cost of Nx number of entries (i.e. memory) to store -// the lookup table, where N is number of search namespaces. -func (table *LookupTable) buildDNSAnswers(altHosts map[string]struct{}, ipv4 []net.IP, ipv6 []net.IP, searchNamespaces []string) { - for h := range altHosts { - h = strings.ToLower(h) - table.allHosts[h] = struct{}{} - if len(ipv4) > 0 { - table.name4[h] = a(h, ipv4) - } - if len(ipv6) > 0 { - table.name6[h] = aaaa(h, ipv6) - } - if len(searchNamespaces) > 0 { - // NOTE: Right now, rather than storing one expanded host for each one of the search namespace - // entries, we are going to store just the first one (assuming that most clients will - // do sequential dns resolution, starting with the first search namespace) - - // host h already ends with a . - // search namespace might not. So we append one in the end if needed - expandedHost := strings.ToLower(h + searchNamespaces[0]) - if !strings.HasSuffix(searchNamespaces[0], ".") { - expandedHost += "." - } - // make sure this is not a proper hostname - // if host is productpage, and search namespace is ns1.svc.cluster.local - // then the expanded host productpage.ns1.svc.cluster.local is a valid hostname - // that is likely to be already present in the altHosts - if _, exists := altHosts[expandedHost]; !exists { - table.cname[expandedHost] = cname(expandedHost, h) - table.allHosts[expandedHost] = struct{}{} - } - } - } -} - -// Borrowed from https://github.com/coredns/coredns/blob/master/plugin/hosts/hosts.go -// a takes a slice of net.IPs and returns a slice of A RRs. -func a(host string, ips []net.IP) []dns.RR { - answers := make([]dns.RR, len(ips)) - for i, ip := range ips { - r := new(dns.A) - r.Hdr = dns.RR_Header{Name: host, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: defaultTTLInSeconds} - r.A = ip - answers[i] = r - } - return answers -} - -// aaaa takes a slice of net.IPs and returns a slice of AAAA RRs. -func aaaa(host string, ips []net.IP) []dns.RR { - answers := make([]dns.RR, len(ips)) - for i, ip := range ips { - r := new(dns.AAAA) - r.Hdr = dns.RR_Header{Name: host, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: defaultTTLInSeconds} - r.AAAA = ip - answers[i] = r - } - return answers -} - -func cname(host string, targetHost string) []dns.RR { - answer := new(dns.CNAME) - answer.Hdr = dns.RR_Header{ - Name: host, - Rrtype: dns.TypeCNAME, - Class: dns.ClassINET, - Ttl: defaultTTLInSeconds, - } - answer.Target = targetHost - return []dns.RR{answer} -} - -// Size returns if buffer size *advertised* in the requests OPT record. -// Or when the request was over TCP, we return the maximum allowed size of 64K. -func size(proto string, r *dns.Msg) int { - size := uint16(0) - if o := r.IsEdns0(); o != nil { - size = o.UDPSize() - } - - // normalize size - size = ednsSize(proto, size) - return int(size) -} - -// ednsSize returns a normalized size based on proto. -func ednsSize(proto string, size uint16) uint16 { - if proto == "tcp" { - return dns.MaxMsgSize - } - if size < dns.MinMsgSize { - return dns.MinMsgSize - } - return size -} diff --git a/pkg/dns/client/dns_test.go b/pkg/dns/client/dns_test.go deleted file mode 100644 index ca90eeaca..000000000 --- a/pkg/dns/client/dns_test.go +++ /dev/null @@ -1,568 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package client - -import ( - "fmt" - "net" - "reflect" - "strings" - "testing" - "time" -) - -import ( - "github.com/miekg/dns" - "go.uber.org/atomic" -) - -import ( - dnsProto "github.com/apache/dubbo-go-pixiu/pkg/dns/proto" - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -var testAgentDNSAddr = "127.0.0.1:15053" - -func TestDNS(t *testing.T) { - initDNS(t) - testCases := []struct { - name string - host string - id int - queryAAAA bool - expected []dns.RR - expectResolutionFailure int - expectExternalResolution bool - modifyReq func(msg *dns.Msg) - }{ - { - name: "success: non k8s host in local cache", - host: "www.google.com.", - expected: a("www.google.com.", []net.IP{net.ParseIP("1.1.1.1").To4()}), - }, - { - name: "success: non k8s host with search namespace yields cname+A record", - host: "www.google.com.ns1.svc.cluster.local.", - expected: append(cname("www.google.com.ns1.svc.cluster.local.", "www.google.com."), - a("www.google.com.", []net.IP{net.ParseIP("1.1.1.1").To4()})...), - }, - { - name: "success: non k8s host not in local cache", - host: "www.bing.com.", - expectExternalResolution: true, - }, - { - name: "success: k8s host - fqdn", - host: "productpage.ns1.svc.cluster.local.", - expected: a("productpage.ns1.svc.cluster.local.", []net.IP{net.ParseIP("9.9.9.9").To4()}), - }, - { - name: "success: k8s host - name.namespace", - host: "productpage.ns1.", - expected: a("productpage.ns1.", []net.IP{net.ParseIP("9.9.9.9").To4()}), - }, - { - name: "success: k8s host - shortname", - host: "productpage.", - expected: a("productpage.", []net.IP{net.ParseIP("9.9.9.9").To4()}), - }, - { - name: "success: k8s host (name.namespace) with search namespace yields cname+A record", - host: "productpage.ns1.ns1.svc.cluster.local.", - expected: append(cname("productpage.ns1.ns1.svc.cluster.local.", "productpage.ns1."), - a("productpage.ns1.", []net.IP{net.ParseIP("9.9.9.9").To4()})...), - }, - { - name: "success: AAAA query for IPv4 k8s host (name.namespace) with search namespace", - host: "productpage.ns1.ns1.svc.cluster.local.", - queryAAAA: true, - }, - { - name: "success: k8s host - non local namespace - name.namespace", - host: "example.ns2.", - expected: a("example.ns2.", []net.IP{net.ParseIP("10.10.10.10").To4()}), - }, - { - name: "success: k8s host - non local namespace - fqdn", - host: "example.ns2.svc.cluster.local.", - expected: a("example.ns2.svc.cluster.local.", []net.IP{net.ParseIP("10.10.10.10").To4()}), - }, - { - name: "success: k8s host - non local namespace - name.namespace.svc", - host: "example.ns2.svc.", - expected: a("example.ns2.svc.", []net.IP{net.ParseIP("10.10.10.10").To4()}), - }, - { - name: "failure: k8s host - non local namespace - shortname", - host: "example.", - expectResolutionFailure: dns.RcodeNameError, - }, - { - name: "success: alt host - name", - host: "svc-with-alt.", - expected: a("svc-with-alt.", []net.IP{net.ParseIP("15.15.15.15").To4()}), - }, - { - name: "success: alt host - name.namespace", - host: "svc-with-alt.ns1.", - expected: a("svc-with-alt.ns1.", []net.IP{net.ParseIP("15.15.15.15").To4()}), - }, - { - name: "success: alt host - name.namespace.svc", - host: "svc-with-alt.ns1.svc.", - expected: a("svc-with-alt.ns1.svc.", []net.IP{net.ParseIP("15.15.15.15").To4()}), - }, - { - name: "success: alt host - name.namespace.svc.cluster.local", - host: "svc-with-alt.ns1.svc.cluster.local.", - expected: a("svc-with-alt.ns1.svc.cluster.local.", []net.IP{net.ParseIP("15.15.15.15").To4()}), - }, - { - name: "success: alt host - name.namespace.svc.clusterset.local", - host: "svc-with-alt.ns1.svc.clusterset.local.", - expected: a("svc-with-alt.ns1.svc.clusterset.local.", []net.IP{net.ParseIP("15.15.15.15").To4()}), - }, - { - name: "success: remote cluster k8s svc - same ns and different domain - fqdn", - host: "details.ns2.svc.cluster.remote.", - id: 2, - expected: a("details.ns2.svc.cluster.remote.", - []net.IP{ - net.ParseIP("13.13.13.13").To4(), - net.ParseIP("14.14.14.14").To4(), - net.ParseIP("12.12.12.12").To4(), - net.ParseIP("11.11.11.11").To4(), - }), - }, - { - name: "success: remote cluster k8s svc round robin", - host: "details.ns2.svc.cluster.remote.", - id: 1, - expected: a("details.ns2.svc.cluster.remote.", - []net.IP{ - net.ParseIP("13.13.13.13").To4(), - net.ParseIP("14.14.14.14").To4(), - net.ParseIP("11.11.11.11").To4(), - net.ParseIP("12.12.12.12").To4(), - }), - }, - { - name: "failure: remote cluster k8s svc - same ns and different domain - name.namespace", - host: "details.ns2.", - expectResolutionFailure: dns.RcodeNameError, // on home machines, the ISP may resolve to some generic webpage. So this test may fail on laptops - }, - { - name: "success: TypeA query returns A records only", - host: "dual.localhost.", - expected: a("dual.localhost.", []net.IP{net.ParseIP("2.2.2.2").To4()}), - }, - { - name: "success: wild card returns A record correctly", - host: "foo.wildcard.", - expected: a("foo.wildcard.", []net.IP{net.ParseIP("10.10.10.10").To4()}), - }, - { - name: "success: specific wild card returns A record correctly", - host: "a.b.wildcard.", - expected: a("a.b.wildcard.", []net.IP{net.ParseIP("11.11.11.11").To4()}), - }, - { - name: "success: wild card with domain returns A record correctly", - host: "foo.svc.mesh.company.net.", - expected: a("foo.svc.mesh.company.net.", []net.IP{net.ParseIP("10.1.2.3").To4()}), - }, - { - name: "success: wild card with namespace with domain returns A record correctly", - host: "foo.foons.svc.mesh.company.net.", - expected: a("foo.foons.svc.mesh.company.net.", []net.IP{net.ParseIP("10.1.2.3").To4()}), - }, - { - name: "success: wild card with search domain returns A record correctly", - host: "foo.svc.mesh.company.net.ns1.svc.cluster.local.", - expected: append(cname("*.svc.mesh.company.net.ns1.svc.cluster.local.", "*.svc.mesh.company.net."), - a("foo.svc.mesh.company.net.ns1.svc.cluster.local.", []net.IP{net.ParseIP("10.1.2.3").To4()})...), - }, - { - name: "success: TypeAAAA query returns AAAA records only", - host: "dual.localhost.", - queryAAAA: true, - expected: aaaa("dual.localhost.", []net.IP{net.ParseIP("2001:db8:0:0:0:ff00:42:8329")}), - }, - { - // This is not a NXDOMAIN, but empty response - name: "success: Error response if only AAAA records exist for typeA", - host: "ipv6.localhost.", - }, - { - // This is not a NXDOMAIN, but empty response - name: "success: Error response if only A records exist for typeAAAA", - host: "ipv4.localhost.", - queryAAAA: true, - }, - { - name: "udp: large request", - host: "giant.", - // Upstream UDP server returns big response, we cannot serve it. Compliant server would truncate it. - expectResolutionFailure: dns.RcodeServerFailure, - }, - { - name: "tcp: large request", - host: "giant.", - expected: giantResponse, - }, - { - name: "large request edns", - host: "giant.", - expectResolutionFailure: dns.RcodeSuccess, - expected: giantResponse, - modifyReq: func(msg *dns.Msg) { - msg.SetEdns0(dns.MaxMsgSize, false) - }, - }, - { - name: "large request truncated", - host: "giant-tc.", - expectResolutionFailure: dns.RcodeSuccess, - expected: giantResponse[:29], - }, - { - name: "success: hostname with a period", - host: "example.localhost.", - expected: a("example.localhost.", []net.IP{net.ParseIP("3.3.3.3").To4()}), - }, - } - - clients := []dns.Client{ - { - Timeout: 3 * time.Second, - Net: "udp", - UDPSize: 65535, - }, - { - Timeout: 3 * time.Second, - Net: "tcp", - }, - } - currentID := atomic.NewInt32(0) - oldID := dns.Id - dns.Id = func() uint16 { - return uint16(currentID.Inc()) - } - defer func() { dns.Id = oldID }() - for i := range clients { - for _, tt := range testCases { - // Test is for explicit network - if (strings.HasPrefix(tt.name, "udp") || strings.HasPrefix(tt.name, "tcp")) && !strings.HasPrefix(tt.name, clients[i].Net) { - continue - } - t.Run(clients[i].Net+"-"+tt.name, func(t *testing.T) { - m := new(dns.Msg) - q := dns.TypeA - if tt.queryAAAA { - q = dns.TypeAAAA - } - m.SetQuestion(tt.host, q) - if tt.modifyReq != nil { - tt.modifyReq(m) - } - if tt.id != 0 { - currentID.Store(int32(tt.id)) - defer func() { currentID.Store(0) }() - } - res, _, err := clients[i].Exchange(m, testAgentDNSAddr) - if res != nil { - t.Log("size: ", len(res.Answer)) - } - if err != nil { - t.Errorf("Failed to resolve query for %s: %v", tt.host, err) - } else { - for _, answer := range res.Answer { - if answer.Header().Class != dns.ClassINET { - t.Errorf("expected class INET for all responses, got %+v for host %s", answer.Header(), tt.host) - } - } - if tt.expectExternalResolution { - // just make sure that the response has a valid DNS response from upstream resolvers - if res.Rcode != dns.RcodeSuccess { - t.Errorf("upstream dns resolution for %s failed: %v", tt.host, res) - } - } else { - if tt.expectResolutionFailure > 0 && tt.expectResolutionFailure != res.Rcode { - t.Errorf("expected resolution failure does not match with response code for %s: expected: %v, got: %v", - tt.host, tt.expectResolutionFailure, res.Rcode) - } - if !equalsDNSrecords(res.Answer, tt.expected) { - t.Log(res) - t.Errorf("dns responses for %s do not match. \n got %v\nwant %v", tt.host, res.Answer, tt.expected) - } - } - } - }) - } - } -} - -// Baseline: -// -// ~150us via agent if cached for A/AAAA -// ~300us via agent when doing the cname redirect -// 5-6ms to upstream resolver directly -// 6-7ms via agent to upstream resolver (cache miss) -// -// Also useful for load testing is using dnsperf. This can be run with: -// -// docker run -v $PWD:$PWD -w $PWD --network host quay.io/ssro/dnsperf dnsperf -p 15053 -d input -c 100 -l 30 -// -// where `input` contains dns queries to run, such as `echo.default. A` -func BenchmarkDNS(t *testing.B) { - initDNS(t) - t.Run("via-agent-cache-miss", func(b *testing.B) { - bench(b, testAgentDNSAddr, "www.bing.com.") - }) - t.Run("via-agent-cache-hit-fqdn", func(b *testing.B) { - bench(b, testAgentDNSAddr, "www.google.com.") - }) - t.Run("via-agent-cache-hit-cname", func(b *testing.B) { - bench(b, testAgentDNSAddr, "www.google.com.ns1.svc.cluster.local.") - }) -} - -func bench(t *testing.B, nameserver string, hostname string) { - errs := 0 - nrs := 0 - nxdomain := 0 - cnames := 0 - c := dns.Client{ - Timeout: 1 * time.Second, - } - for i := 0; i < t.N; i++ { - toResolve := hostname - redirect: - m := new(dns.Msg) - m.SetQuestion(toResolve, dns.TypeA) - res, _, err := c.Exchange(m, nameserver) - - if err != nil { - errs++ - } else if len(res.Answer) == 0 { - nrs++ - } else { - for _, a := range res.Answer { - if arec, ok := a.(*dns.A); !ok { - // check if this is a cname redirect. If so, repeat the resolution - // assuming the client does not see/respect the inlined A record in the response. - if crec, ok := a.(*dns.CNAME); !ok { - errs++ - } else { - cnames++ - toResolve = crec.Target - goto redirect - } - } else { - if arec.Hdr.Rrtype != dns.RcodeSuccess { - nxdomain++ - } - } - } - } - } - - if errs+nrs > 0 { - t.Log("Sent", t.N, "err", errs, "no response", nrs, "nxdomain", nxdomain, "cname redirect", cnames) - } -} - -var giantResponse = func() []dns.RR { - ips := make([]net.IP, 0) - for i := 0; i < 64; i++ { - ips = append(ips, net.ParseIP(fmt.Sprintf("240.0.0.%d", i)).To4()) - } - return a("aaaaaaaaaaaa.aaaaaa.", ips) -}() - -func makeUpstream(t test.Failer, responses map[string]string) string { - mux := dns.NewServeMux() - mux.HandleFunc(".", func(resp dns.ResponseWriter, msg *dns.Msg) { - answer := new(dns.Msg) - answer.SetReply(msg) - answer.Rcode = dns.RcodeNameError - if err := resp.WriteMsg(answer); err != nil { - t.Fatalf("err: %s", err) - } - }) - for hn, desiredResp := range responses { - mux.HandleFunc(hn, func(resp dns.ResponseWriter, msg *dns.Msg) { - answer := dns.Msg{ - Answer: a(hn, []net.IP{net.ParseIP(desiredResp).To4()}), - } - answer.SetReply(msg) - answer.Rcode = dns.RcodeSuccess - if err := resp.WriteMsg(&answer); err != nil { - t.Fatalf("err: %s", err) - } - }) - } - mux.HandleFunc("giant.", func(resp dns.ResponseWriter, msg *dns.Msg) { - answer := &dns.Msg{ - Answer: giantResponse, - } - answer.SetReply(msg) - answer.Rcode = dns.RcodeSuccess - if err := resp.WriteMsg(answer); err != nil { - t.Fatalf("err: %s", err) - } - }) - mux.HandleFunc("giant-tc.", func(resp dns.ResponseWriter, msg *dns.Msg) { - answer := &dns.Msg{ - Answer: giantResponse, - } - answer.SetReply(msg) - answer.Rcode = dns.RcodeSuccess - answer.Truncate(size("udp", msg)) - if err := resp.WriteMsg(answer); err != nil { - t.Fatalf("err: %s", err) - } - }) - up := make(chan struct{}) - server := &dns.Server{ - Addr: "127.0.0.1:0", - Net: "udp", - Handler: mux, - NotifyStartedFunc: func() { close(up) }, - ReadTimeout: time.Second, - WriteTimeout: time.Second, - } - go func() { - if err := server.ListenAndServe(); err != nil { - log.Warnf("listen error: %v", err) - } - }() - select { - case <-time.After(time.Second * 10): - t.Fatalf("setup timeout") - case <-up: - } - t.Cleanup(func() { _ = server.Shutdown() }) - server.Addr = server.PacketConn.LocalAddr().String() - - // Setup TCP server on same port - up = make(chan struct{}) - tcp := &dns.Server{ - Addr: server.Addr, - Net: "tcp", - Handler: mux, - NotifyStartedFunc: func() { close(up) }, - } - go func() { - if err := tcp.ListenAndServe(); err != nil { - log.Warnf("listen error: %v", err) - } - }() - select { - case <-time.After(time.Second * 10): - t.Fatalf("setup timeout") - case <-up: - } - t.Cleanup(func() { _ = tcp.Shutdown() }) - t.Cleanup(func() { _ = server.Shutdown() }) - tcp.Addr = server.PacketConn.LocalAddr().String() - return server.Addr -} - -func initDNS(t test.Failer) *LocalDNSServer { - srv := makeUpstream(t, map[string]string{"www.bing.com.": "1.1.1.1"}) - testAgentDNS, err := NewLocalDNSServer("ns1", "ns1.svc.cluster.local", "localhost:15053") - if err != nil { - t.Fatal(err) - } - testAgentDNS.resolvConfServers = []string{srv} - testAgentDNS.StartDNS() - testAgentDNS.searchNamespaces = []string{"ns1.svc.cluster.local", "svc.cluster.local", "cluster.local"} - testAgentDNS.UpdateLookupTable(&dnsProto.NameTable{ - Table: map[string]*dnsProto.NameTable_NameInfo{ - "www.google.com": { - Ips: []string{"1.1.1.1"}, - Registry: "External", - }, - "productpage.ns1.svc.cluster.local": { - Ips: []string{"9.9.9.9"}, - Registry: "Kubernetes", - Namespace: "ns1", - Shortname: "productpage", - }, - "example.ns2.svc.cluster.local": { - Ips: []string{"10.10.10.10"}, - Registry: "Kubernetes", - Namespace: "ns2", - Shortname: "example", - }, - "details.ns2.svc.cluster.remote": { - Ips: []string{"11.11.11.11", "12.12.12.12", "13.13.13.13", "14.14.14.14"}, - Registry: "Kubernetes", - Namespace: "ns2", - Shortname: "details", - }, - "svc-with-alt.ns1.svc.cluster.local": { - Ips: []string{"15.15.15.15"}, - Registry: "Kubernetes", - Namespace: "ns1", - Shortname: "svc-with-alt", - AltHosts: []string{ - "svc-with-alt.ns1.svc.clusterset.local", - }, - }, - "ipv6.localhost": { - Ips: []string{"2001:db8:0:0:0:ff00:42:8329"}, - Registry: "External", - }, - "dual.localhost": { - Ips: []string{"2.2.2.2", "2001:db8:0:0:0:ff00:42:8329"}, - Registry: "External", - }, - "ipv4.localhost": { - Ips: []string{"2.2.2.2"}, - Registry: "External", - }, - "*.b.wildcard": { - Ips: []string{"11.11.11.11"}, - Registry: "External", - }, - "*.wildcard": { - Ips: []string{"10.10.10.10"}, - Registry: "External", - }, - "*.svc.mesh.company.net": { - Ips: []string{"10.1.2.3"}, - Registry: "External", - }, - "example.localhost.": { - Ips: []string{"3.3.3.3"}, - Registry: "External", - }, - }, - }) - t.Cleanup(testAgentDNS.Close) - return testAgentDNS -} - -// reflect.DeepEqual doesn't seem to work well for dns.RR -// as the Rdlength field is not updated in the a(), or aaaa() calls. -// so zero them out before doing reflect.Deepequal -func equalsDNSrecords(got []dns.RR, want []dns.RR) bool { - for i := range got { - got[i].Header().Rdlength = 0 - } - return reflect.DeepEqual(got, want) -} diff --git a/pkg/dns/client/leak_test.go b/pkg/dns/client/leak_test.go deleted file mode 100644 index 6fae06e81..000000000 --- a/pkg/dns/client/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package client - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pkg/dns/client/monitoring.go b/pkg/dns/client/monitoring.go deleted file mode 100644 index e08a7cf3b..000000000 --- a/pkg/dns/client/monitoring.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package client - -import ( - "istio.io/pkg/monitoring" -) - -var ( - requests = monitoring.NewSum( - "dns_requests_total", - "Total number of DNS requests.", - ) - - upstreamRequests = monitoring.NewSum( - "dns_upstream_requests_total", - "Total number of DNS requests forwarded to upstream.", - ) - - failures = monitoring.NewSum( - "dns_upstream_failures_total", - "Total number of DNS requests forwarded to upstream.", - ) - - requestDuration = monitoring.NewDistribution( - "dns_upstream_request_duration_seconds", - "Total time in seconds Istio takes to get DNS response from upstream.", - []float64{.005, .001, 0.01, 0.1, 1, 5}, - ) -) - -func registerStats() { - monitoring.MustRegister(requests) - monitoring.MustRegister(upstreamRequests) - monitoring.MustRegister(failures) - monitoring.MustRegister(requestDuration) -} diff --git a/pkg/dns/client/proxy.go b/pkg/dns/client/proxy.go deleted file mode 100644 index ad27871d9..000000000 --- a/pkg/dns/client/proxy.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package client - -import ( - "net" - "time" -) - -import ( - "github.com/miekg/dns" -) - -type dnsProxy struct { - serveMux *dns.ServeMux - server *dns.Server - - // This is the upstream Client used to make upstream DNS queries - // in case the data is not in our name table. - upstreamClient *dns.Client - protocol string - resolver *LocalDNSServer -} - -func newDNSProxy(protocol, addr string, resolver *LocalDNSServer) (*dnsProxy, error) { - p := &dnsProxy{ - serveMux: dns.NewServeMux(), - server: &dns.Server{}, - upstreamClient: &dns.Client{ - Net: protocol, - DialTimeout: 5 * time.Second, - ReadTimeout: 5 * time.Second, - WriteTimeout: 5 * time.Second, - }, - protocol: protocol, - resolver: resolver, - } - - var err error - p.serveMux.Handle(".", p) - p.server.Handler = p.serveMux - if protocol == "udp" { - p.server.PacketConn, err = net.ListenPacket("udp", addr) - } else { - p.server.Listener, err = net.Listen("tcp", addr) - } - log.Infof("Starting local %s DNS server on %v", p.protocol, addr) - if err != nil { - log.Errorf("Failed to listen on %s port %s: %v", protocol, addr, err) - return nil, err - } - return p, nil -} - -func (p *dnsProxy) start() { - err := p.server.ActivateAndServe() - if err != nil { - log.Errorf("Local %s DNS server terminated: %v", p.protocol, err) - } -} - -func (p *dnsProxy) close() { - if p.server != nil { - if err := p.server.Shutdown(); err != nil { - log.Errorf("error in shutting down %s dns downstreamUDPServer :%v", p.protocol, err) - } - } -} - -func (p *dnsProxy) ServeDNS(w dns.ResponseWriter, req *dns.Msg) { - p.resolver.ServeDNS(p, w, req) -} diff --git a/pkg/dns/proto/nds.pb.go b/pkg/dns/proto/nds.pb.go deleted file mode 100644 index 842c0c36f..000000000 --- a/pkg/dns/proto/nds.pb.go +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.27.1 -// protoc (unknown) -// source: dns/proto/nds.proto - -package istio_networking_nds_v1 - -import ( - reflect "reflect" - sync "sync" -) - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// Table of hostnames and their IPs to br used for DNS resolution at the agent -// Sent by istiod to istio agents via xds -type NameTable struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Map of hostname to resolution attributes. - Table map[string]*NameTable_NameInfo `protobuf:"bytes,1,rep,name=table,proto3" json:"table,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` -} - -func (x *NameTable) Reset() { - *x = NameTable{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_nds_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *NameTable) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*NameTable) ProtoMessage() {} - -func (x *NameTable) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_nds_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use NameTable.ProtoReflect.Descriptor instead. -func (*NameTable) Descriptor() ([]byte, []int) { - return file_dns_proto_nds_proto_rawDescGZIP(), []int{0} -} - -func (x *NameTable) GetTable() map[string]*NameTable_NameInfo { - if x != nil { - return x.Table - } - return nil -} - -type NameTable_NameInfo struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // List of IPs for the host. - Ips []string `protobuf:"bytes,1,rep,name=ips,proto3" json:"ips,omitempty"` - // The name of the service registry containing the service (e.g. 'Kubernetes'). - Registry string `protobuf:"bytes,2,opt,name=registry,proto3" json:"registry,omitempty"` - // The k8s service name. Only applies when registry=`Kubernetes` - Shortname string `protobuf:"bytes,3,opt,name=shortname,proto3" json:"shortname,omitempty"` - // The k8s namespace for the service. Only applies when registry=`Kubernetes` - Namespace string `protobuf:"bytes,4,opt,name=namespace,proto3" json:"namespace,omitempty"` - // Deprecated. Was added for experimentation only. - // - // Deprecated: Do not use. - AltHosts []string `protobuf:"bytes,5,rep,name=alt_hosts,json=altHosts,proto3" json:"alt_hosts,omitempty"` -} - -func (x *NameTable_NameInfo) Reset() { - *x = NameTable_NameInfo{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_nds_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *NameTable_NameInfo) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*NameTable_NameInfo) ProtoMessage() {} - -func (x *NameTable_NameInfo) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_nds_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use NameTable_NameInfo.ProtoReflect.Descriptor instead. -func (*NameTable_NameInfo) Descriptor() ([]byte, []int) { - return file_dns_proto_nds_proto_rawDescGZIP(), []int{0, 0} -} - -func (x *NameTable_NameInfo) GetIps() []string { - if x != nil { - return x.Ips - } - return nil -} - -func (x *NameTable_NameInfo) GetRegistry() string { - if x != nil { - return x.Registry - } - return "" -} - -func (x *NameTable_NameInfo) GetShortname() string { - if x != nil { - return x.Shortname - } - return "" -} - -func (x *NameTable_NameInfo) GetNamespace() string { - if x != nil { - return x.Namespace - } - return "" -} - -// Deprecated: Do not use. -func (x *NameTable_NameInfo) GetAltHosts() []string { - if x != nil { - return x.AltHosts - } - return nil -} - -var File_dns_proto_nds_proto protoreflect.FileDescriptor - -var file_dns_proto_nds_proto_rawDesc = []byte{ - 0x0a, 0x13, 0x64, 0x6e, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6e, 0x64, 0x73, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x17, 0x69, 0x73, 0x74, 0x69, 0x6f, 0x2e, 0x6e, 0x65, 0x74, - 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x22, 0xcf, - 0x02, 0x0a, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x43, 0x0a, 0x05, - 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x69, 0x73, - 0x74, 0x69, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x6e, - 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x2e, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x1a, 0x95, 0x01, 0x0a, 0x08, 0x4e, 0x61, 0x6d, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, - 0x0a, 0x03, 0x69, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x70, 0x73, - 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x12, 0x1c, 0x0a, 0x09, - 0x73, 0x68, 0x6f, 0x72, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x09, 0x61, 0x6c, 0x74, 0x5f, - 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, - 0x08, 0x61, 0x6c, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x1a, 0x65, 0x0a, 0x0a, 0x54, 0x61, 0x62, - 0x6c, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x41, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x69, 0x73, 0x74, 0x69, 0x6f, - 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x6e, 0x64, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x4e, 0x61, 0x6d, - 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x42, 0x48, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, - 0x70, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2d, 0x67, 0x6f, 0x2d, 0x70, - 0x69, 0x78, 0x69, 0x75, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x64, 0x6e, 0x73, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2f, 0x69, 0x73, 0x74, 0x69, 0x6f, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x69, 0x6e, 0x67, 0x5f, 0x6e, 0x64, 0x73, 0x5f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, -} - -var ( - file_dns_proto_nds_proto_rawDescOnce sync.Once - file_dns_proto_nds_proto_rawDescData = file_dns_proto_nds_proto_rawDesc -) - -func file_dns_proto_nds_proto_rawDescGZIP() []byte { - file_dns_proto_nds_proto_rawDescOnce.Do(func() { - file_dns_proto_nds_proto_rawDescData = protoimpl.X.CompressGZIP(file_dns_proto_nds_proto_rawDescData) - }) - return file_dns_proto_nds_proto_rawDescData -} - -var file_dns_proto_nds_proto_msgTypes = make([]protoimpl.MessageInfo, 3) -var file_dns_proto_nds_proto_goTypes = []interface{}{ - (*NameTable)(nil), // 0: istio.networking.nds.v1.NameTable - (*NameTable_NameInfo)(nil), // 1: istio.networking.nds.v1.NameTable.NameInfo - nil, // 2: istio.networking.nds.v1.NameTable.TableEntry -} -var file_dns_proto_nds_proto_depIdxs = []int32{ - 2, // 0: istio.networking.nds.v1.NameTable.table:type_name -> istio.networking.nds.v1.NameTable.TableEntry - 1, // 1: istio.networking.nds.v1.NameTable.TableEntry.value:type_name -> istio.networking.nds.v1.NameTable.NameInfo - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name -} - -func init() { file_dns_proto_nds_proto_init() } -func file_dns_proto_nds_proto_init() { - if File_dns_proto_nds_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_dns_proto_nds_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NameTable); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_nds_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NameTable_NameInfo); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_dns_proto_nds_proto_rawDesc, - NumEnums: 0, - NumMessages: 3, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_dns_proto_nds_proto_goTypes, - DependencyIndexes: file_dns_proto_nds_proto_depIdxs, - MessageInfos: file_dns_proto_nds_proto_msgTypes, - }.Build() - File_dns_proto_nds_proto = out.File - file_dns_proto_nds_proto_rawDesc = nil - file_dns_proto_nds_proto_goTypes = nil - file_dns_proto_nds_proto_depIdxs = nil -} diff --git a/pkg/dns/proto/nds.proto b/pkg/dns/proto/nds.proto deleted file mode 100644 index 773708c7f..000000000 --- a/pkg/dns/proto/nds.proto +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package istio.networking.nds.v1; - -option go_package = "github.com/apache/dubbo-go-pixiu/pkg/dns/proto/istio_networking_nds_v1"; - -// Table of hostnames and their IPs to br used for DNS resolution at the agent -// Sent by istiod to istio agents via xds -message NameTable { - message NameInfo { - // List of IPs for the host. - repeated string ips = 1; - - // The name of the service registry containing the service (e.g. 'Kubernetes'). - string registry = 2; - - // The k8s service name. Only applies when registry=`Kubernetes` - string shortname = 3; - - // The k8s namespace for the service. Only applies when registry=`Kubernetes` - string namespace = 4; - - // Deprecated. Was added for experimentation only. - repeated string alt_hosts = 5 [deprecated = true]; - } - - // Map of hostname to resolution attributes. - map table = 1; -} - diff --git a/pkg/dns/server/name_table.go b/pkg/dns/server/name_table.go deleted file mode 100644 index 6b30f4c3c..000000000 --- a/pkg/dns/server/name_table.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "net" - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - dnsProto "github.com/apache/dubbo-go-pixiu/pkg/dns/proto" -) - -// Config for building the name table. -type Config struct { - Node *model.Proxy - Push *model.PushContext - - // MulticlusterHeadlessEnabled if true, the DNS name table for a headless service will resolve to - // same-network endpoints in any cluster. - MulticlusterHeadlessEnabled bool -} - -// BuildNameTable produces a table of hostnames and their associated IPs that can then -// be used by the agent to resolve DNS. This logic is always active. However, local DNS resolution -// will only be effective if DNS capture is enabled in the proxy -func BuildNameTable(cfg Config) *dnsProto.NameTable { - if cfg.Node.Type != model.SidecarProxy { - // DNS resolution is only for sidecars - return nil - } - - out := &dnsProto.NameTable{ - Table: make(map[string]*dnsProto.NameTable_NameInfo), - } - for _, svc := range cfg.Node.SidecarScope.Services() { - svcAddress := svc.GetAddressForProxy(cfg.Node) - var addressList []string - hostName := svc.Hostname - if svcAddress != constants.UnspecifiedIP { - // Filter out things we cannot parse as IP. Generally this means CIDRs, as anything else - // should be caught in validation. - if addr := net.ParseIP(svcAddress); addr == nil { - continue - } - addressList = append(addressList, svcAddress) - } else { - // The IP will be unspecified here if its headless service or if the auto - // IP allocation logic for service entry was unable to allocate an IP. - if svc.Resolution == model.Passthrough && len(svc.Ports) > 0 { - for _, instance := range cfg.Push.ServiceInstancesByPort(svc, svc.Ports[0].Port, nil) { - // TODO(stevenctl): headless across-networks https://github.com/istio/istio/issues/38327 - sameNetwork := cfg.Node.InNetwork(instance.Endpoint.Network) - sameCluster := cfg.Node.InCluster(instance.Endpoint.Locality.ClusterID) - // For all k8s headless services, populate the dns table with the endpoint IPs as k8s does. - // And for each individual pod, populate the dns table with the endpoint IP with a manufactured host name. - if instance.Endpoint.SubDomain != "" && sameNetwork { - // Follow k8s pods dns naming convention of "...svc." - // i.e. "mysql-0.mysql.default.svc.cluster.local". - parts := strings.SplitN(hostName.String(), ".", 2) - if len(parts) != 2 { - continue - } - address := []string{instance.Endpoint.Address} - shortName := instance.Endpoint.HostName + "." + instance.Endpoint.SubDomain - host := shortName + "." + parts[1] // Add cluster domain. - nameInfo := &dnsProto.NameTable_NameInfo{ - Ips: address, - Registry: string(svc.Attributes.ServiceRegistry), - Namespace: svc.Attributes.Namespace, - Shortname: shortName, - } - - if _, f := out.Table[host]; !f || sameCluster { - // We may have the same pod in two clusters (ie mysql-0 deployed in both places). - // We can only return a single IP for these queries. We should prefer the local cluster, - // so if the entry already exists only overwrite it if the instance is in our own cluster. - out.Table[host] = nameInfo - } - } - skipForMulticluster := !cfg.MulticlusterHeadlessEnabled && !sameCluster - if skipForMulticluster || !sameNetwork { - // We take only cluster-local endpoints. While this seems contradictory to - // our logic other parts of the code, where cross-cluster is the default. - // However, this only impacts the DNS response. If we were to send all - // endpoints, cross network routing would break, as we do passthrough LB and - // don't go through the network gateway. While we could, hypothetically, send - // "network-local" endpoints, this would still make enabling DNS give vastly - // different load balancing than without, so its probably best to filter. - // This ends up matching the behavior of Kubernetes DNS. - continue - } - // TODO: should we skip the node's own IP like we do in listener? - addressList = append(addressList, instance.Endpoint.Address) - } - } - if len(addressList) == 0 { - // could not reliably determine the addresses of endpoints of headless service - // or this is not a k8s service - continue - } - } - - nameInfo := &dnsProto.NameTable_NameInfo{ - Ips: addressList, - Registry: string(svc.Attributes.ServiceRegistry), - } - if svc.Attributes.ServiceRegistry == provider.Kubernetes && - !strings.HasSuffix(hostName.String(), "."+constants.DefaultClusterSetLocalDomain) { - // The agent will take care of resolving a, a.ns, a.ns.svc, etc. - // No need to provide a DNS entry for each variant. - // - // NOTE: This is not done for Kubernetes Multi-Cluster Services (MCS) hosts, in order - // to avoid conflicting with the entries for the regular (cluster.local) service. - nameInfo.Namespace = svc.Attributes.Namespace - nameInfo.Shortname = svc.Attributes.Name - } - out.Table[hostName.String()] = nameInfo - } - return out -} diff --git a/pkg/dns/server/name_table_test.go b/pkg/dns/server/name_table_test.go deleted file mode 100644 index dfbf56d81..000000000 --- a/pkg/dns/server/name_table_test.go +++ /dev/null @@ -1,459 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server_test - -import ( - "testing" -) - -import ( - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/testing/protocmp" - meshconfig "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/serviceregistry/provider" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/host" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - dnsProto "github.com/apache/dubbo-go-pixiu/pkg/dns/proto" - dnsServer "github.com/apache/dubbo-go-pixiu/pkg/dns/server" -) - -// nolint -func makeServiceInstances(proxy *model.Proxy, service *model.Service, hostname, subdomain string) map[int][]*model.ServiceInstance { - instances := make(map[int][]*model.ServiceInstance) - for _, port := range service.Ports { - instances[port.Port] = makeInstances(proxy, service, port.Port, port.Port) - instances[port.Port][0].Endpoint.HostName = hostname - instances[port.Port][0].Endpoint.SubDomain = subdomain - instances[port.Port][0].Endpoint.Network = proxy.Metadata.Network - instances[port.Port][0].Endpoint.Locality.ClusterID = proxy.Metadata.ClusterID - } - return instances -} - -func TestNameTable(t *testing.T) { - mesh := &meshconfig.MeshConfig{RootNamespace: "dubbo-system"} - proxy := &model.Proxy{ - IPAddresses: []string{"9.9.9.9"}, - Metadata: &model.NodeMetadata{}, - Type: model.SidecarProxy, - DNSDomain: "testns.svc.cluster.local", - } - nw1proxy := &model.Proxy{ - IPAddresses: []string{"9.9.9.9"}, - Metadata: &model.NodeMetadata{Network: "nw1"}, - Type: model.SidecarProxy, - DNSDomain: "testns.svc.cluster.local", - } - cl1proxy := &model.Proxy{ - IPAddresses: []string{"9.9.9.9"}, - Metadata: &model.NodeMetadata{ClusterID: "cl1"}, - Type: model.SidecarProxy, - DNSDomain: "testns.svc.cluster.local", - } - - pod1 := &model.Proxy{ - IPAddresses: []string{"1.2.3.4"}, - Metadata: &model.NodeMetadata{}, - Type: model.SidecarProxy, - DNSDomain: "testns.svc.cluster.local", - } - pod2 := &model.Proxy{ - IPAddresses: []string{"9.6.7.8"}, - Metadata: &model.NodeMetadata{Network: "nw2", ClusterID: "cl2"}, - Type: model.SidecarProxy, - DNSDomain: "testns.svc.cluster.local", - } - pod3 := &model.Proxy{ - IPAddresses: []string{"19.6.7.8"}, - Metadata: &model.NodeMetadata{Network: "nw1"}, - Type: model.SidecarProxy, - DNSDomain: "testns.svc.cluster.local", - } - pod4 := &model.Proxy{ - IPAddresses: []string{"9.16.7.8"}, - Metadata: &model.NodeMetadata{ClusterID: "cl1"}, - Type: model.SidecarProxy, - DNSDomain: "testns.svc.cluster.local", - } - - headlessService := &model.Service{ - Hostname: host.Name("headless-svc.testns.svc.cluster.local"), - DefaultAddress: constants.UnspecifiedIP, - Ports: model.PortList{&model.Port{ - Name: "tcp-port", - Port: 9000, - Protocol: protocol.TCP, - }}, - Resolution: model.Passthrough, - Attributes: model.ServiceAttributes{ - Name: "headless-svc", - Namespace: "testns", - ServiceRegistry: provider.Kubernetes, - }, - } - - headlessServiceForServiceEntry := &model.Service{ - Hostname: host.Name("foo.bar.com"), - DefaultAddress: constants.UnspecifiedIP, - Ports: model.PortList{&model.Port{ - Name: "tcp-port", - Port: 9000, - Protocol: protocol.TCP, - }}, - Resolution: model.Passthrough, - Attributes: model.ServiceAttributes{ - Name: "foo.bar.com", - Namespace: "testns", - ServiceRegistry: provider.External, - LabelSelectors: map[string]string{"wl": "headless-foobar"}, - }, - } - - wildcardService := &model.Service{ - Hostname: host.Name("*.testns.svc.cluster.local"), - DefaultAddress: "172.10.10.10", - Ports: model.PortList{ - &model.Port{ - Name: "tcp-port", - Port: 9000, - Protocol: protocol.TCP, - }, - &model.Port{ - Name: "http-port", - Port: 8000, - Protocol: protocol.HTTP, - }, - }, - Resolution: model.ClientSideLB, - Attributes: model.ServiceAttributes{ - Name: "wildcard-svc", - Namespace: "testns", - ServiceRegistry: provider.Kubernetes, - }, - } - - cidrService := &model.Service{ - Hostname: host.Name("*.testns.svc.cluster.local"), - DefaultAddress: "172.217.0.0/16", - Ports: model.PortList{ - &model.Port{ - Name: "tcp-port", - Port: 9000, - Protocol: protocol.TCP, - }, - &model.Port{ - Name: "http-port", - Port: 8000, - Protocol: protocol.HTTP, - }, - }, - Resolution: model.ClientSideLB, - Attributes: model.ServiceAttributes{ - Name: "cidr-svc", - Namespace: "testns", - ServiceRegistry: provider.Kubernetes, - }, - } - - push := model.NewPushContext() - push.Mesh = mesh - push.AddPublicServices([]*model.Service{headlessService}) - push.AddServiceInstances(headlessService, - makeServiceInstances(pod1, headlessService, "pod1", "headless-svc")) - push.AddServiceInstances(headlessService, - makeServiceInstances(pod2, headlessService, "pod2", "headless-svc")) - push.AddServiceInstances(headlessService, - makeServiceInstances(pod3, headlessService, "pod3", "headless-svc")) - push.AddServiceInstances(headlessService, - makeServiceInstances(pod4, headlessService, "pod4", "headless-svc")) - - wpush := model.NewPushContext() - wpush.Mesh = mesh - wpush.AddPublicServices([]*model.Service{wildcardService}) - - cpush := model.NewPushContext() - cpush.Mesh = mesh - wpush.AddPublicServices([]*model.Service{cidrService}) - - sepush := model.NewPushContext() - sepush.Mesh = mesh - sepush.AddPublicServices([]*model.Service{headlessServiceForServiceEntry}) - sepush.AddServiceInstances(headlessServiceForServiceEntry, - makeServiceInstances(pod1, headlessServiceForServiceEntry, "", "")) - sepush.AddServiceInstances(headlessServiceForServiceEntry, - makeServiceInstances(pod2, headlessServiceForServiceEntry, "", "")) - sepush.AddServiceInstances(headlessServiceForServiceEntry, - makeServiceInstances(pod3, headlessServiceForServiceEntry, "", "")) - sepush.AddServiceInstances(headlessServiceForServiceEntry, - makeServiceInstances(pod4, headlessServiceForServiceEntry, "", "")) - - cases := []struct { - name string - proxy *model.Proxy - push *model.PushContext - enableMultiClusterHeadless bool - expectedNameTable *dnsProto.NameTable - }{ - { - name: "headless service pods", - proxy: proxy, - push: push, - expectedNameTable: &dnsProto.NameTable{ - Table: map[string]*dnsProto.NameTable_NameInfo{ - "pod1.headless-svc.testns.svc.cluster.local": { - Ips: []string{"1.2.3.4"}, - Registry: "Kubernetes", - Shortname: "pod1.headless-svc", - Namespace: "testns", - }, - "pod2.headless-svc.testns.svc.cluster.local": { - Ips: []string{"9.6.7.8"}, - Registry: "Kubernetes", - Shortname: "pod2.headless-svc", - Namespace: "testns", - }, - "pod3.headless-svc.testns.svc.cluster.local": { - Ips: []string{"19.6.7.8"}, - Registry: "Kubernetes", - Shortname: "pod3.headless-svc", - Namespace: "testns", - }, - "pod4.headless-svc.testns.svc.cluster.local": { - Ips: []string{"9.16.7.8"}, - Registry: "Kubernetes", - Shortname: "pod4.headless-svc", - Namespace: "testns", - }, - "headless-svc.testns.svc.cluster.local": { - Ips: []string{"1.2.3.4", "9.6.7.8", "19.6.7.8", "9.16.7.8"}, - Registry: "Kubernetes", - Shortname: "headless-svc", - Namespace: "testns", - }, - }, - }, - }, - { - name: "headless service pods with network isolation", - proxy: nw1proxy, - push: push, - expectedNameTable: &dnsProto.NameTable{ - Table: map[string]*dnsProto.NameTable_NameInfo{ - "pod1.headless-svc.testns.svc.cluster.local": { - Ips: []string{"1.2.3.4"}, - Registry: "Kubernetes", - Shortname: "pod1.headless-svc", - Namespace: "testns", - }, - "pod3.headless-svc.testns.svc.cluster.local": { - Ips: []string{"19.6.7.8"}, - Registry: "Kubernetes", - Shortname: "pod3.headless-svc", - Namespace: "testns", - }, - "pod4.headless-svc.testns.svc.cluster.local": { - Ips: []string{"9.16.7.8"}, - Registry: "Kubernetes", - Shortname: "pod4.headless-svc", - Namespace: "testns", - }, - "headless-svc.testns.svc.cluster.local": { - Ips: []string{"1.2.3.4", "19.6.7.8", "9.16.7.8"}, - Registry: "Kubernetes", - Shortname: "headless-svc", - Namespace: "testns", - }, - }, - }, - }, - { - name: "multi cluster headless service pods", - proxy: cl1proxy, - push: push, - expectedNameTable: &dnsProto.NameTable{ - Table: map[string]*dnsProto.NameTable_NameInfo{ - "pod1.headless-svc.testns.svc.cluster.local": { - Ips: []string{"1.2.3.4"}, - Registry: "Kubernetes", - Shortname: "pod1.headless-svc", - Namespace: "testns", - }, - "pod2.headless-svc.testns.svc.cluster.local": { - Ips: []string{"9.6.7.8"}, - Registry: "Kubernetes", - Shortname: "pod2.headless-svc", - Namespace: "testns", - }, - "pod3.headless-svc.testns.svc.cluster.local": { - Ips: []string{"19.6.7.8"}, - Registry: "Kubernetes", - Shortname: "pod3.headless-svc", - Namespace: "testns", - }, - "pod4.headless-svc.testns.svc.cluster.local": { - Ips: []string{"9.16.7.8"}, - Registry: "Kubernetes", - Shortname: "pod4.headless-svc", - Namespace: "testns", - }, - "headless-svc.testns.svc.cluster.local": { - Ips: []string{"1.2.3.4", "19.6.7.8", "9.16.7.8"}, - Registry: "Kubernetes", - Shortname: "headless-svc", - Namespace: "testns", - }, - }, - }, - }, - { - name: "multi cluster headless service pods with multi cluster enabled", - proxy: cl1proxy, - push: push, - enableMultiClusterHeadless: true, - expectedNameTable: &dnsProto.NameTable{ - Table: map[string]*dnsProto.NameTable_NameInfo{ - "pod1.headless-svc.testns.svc.cluster.local": { - Ips: []string{"1.2.3.4"}, - Registry: "Kubernetes", - Shortname: "pod1.headless-svc", - Namespace: "testns", - }, - "pod2.headless-svc.testns.svc.cluster.local": { - Ips: []string{"9.6.7.8"}, - Registry: "Kubernetes", - Shortname: "pod2.headless-svc", - Namespace: "testns", - }, - "pod3.headless-svc.testns.svc.cluster.local": { - Ips: []string{"19.6.7.8"}, - Registry: "Kubernetes", - Shortname: "pod3.headless-svc", - Namespace: "testns", - }, - "pod4.headless-svc.testns.svc.cluster.local": { - Ips: []string{"9.16.7.8"}, - Registry: "Kubernetes", - Shortname: "pod4.headless-svc", - Namespace: "testns", - }, - "headless-svc.testns.svc.cluster.local": { - Ips: []string{"1.2.3.4", "9.6.7.8", "19.6.7.8", "9.16.7.8"}, - Registry: "Kubernetes", - Shortname: "headless-svc", - Namespace: "testns", - }, - }, - }, - }, - { - name: "wildcard service pods", - proxy: proxy, - push: wpush, - expectedNameTable: &dnsProto.NameTable{ - Table: map[string]*dnsProto.NameTable_NameInfo{ - "*.testns.svc.cluster.local": { - Ips: []string{"172.10.10.10"}, - Registry: "Kubernetes", - Shortname: "wildcard-svc", - Namespace: "testns", - }, - }, - }, - }, - { - name: "cidr service", - proxy: proxy, - push: cpush, - expectedNameTable: &dnsProto.NameTable{ - Table: map[string]*dnsProto.NameTable_NameInfo{}, - }, - }, - { - name: "service entry with resolution = NONE", - proxy: proxy, - push: sepush, - expectedNameTable: &dnsProto.NameTable{ - Table: map[string]*dnsProto.NameTable_NameInfo{ - "foo.bar.com": { - Ips: []string{"1.2.3.4", "9.6.7.8", "19.6.7.8", "9.16.7.8"}, - Registry: "External", - }, - }, - }, - }, - { - name: "service entry with resolution = NONE with network isolation", - proxy: nw1proxy, - push: sepush, - expectedNameTable: &dnsProto.NameTable{ - Table: map[string]*dnsProto.NameTable_NameInfo{ - "foo.bar.com": { - Ips: []string{"1.2.3.4", "19.6.7.8", "9.16.7.8"}, - Registry: "External", - }, - }, - }, - }, - { - name: "multi cluster service entry with resolution = NONE", - proxy: cl1proxy, - push: sepush, - expectedNameTable: &dnsProto.NameTable{ - Table: map[string]*dnsProto.NameTable_NameInfo{ - "foo.bar.com": { - Ips: []string{"1.2.3.4", "19.6.7.8", "9.16.7.8"}, - Registry: "External", - }, - }, - }, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - tt.proxy.SidecarScope = model.ConvertToSidecarScope(tt.push, nil, "default") - if diff := cmp.Diff(dnsServer.BuildNameTable(dnsServer.Config{ - Node: tt.proxy, - Push: tt.push, - MulticlusterHeadlessEnabled: tt.enableMultiClusterHeadless, - }), tt.expectedNameTable, protocmp.Transform()); diff != "" { - t.Fatalf("got diff: %v", diff) - } - }) - } -} - -func makeInstances(proxy *model.Proxy, svc *model.Service, servicePort int, targetPort int) []*model.ServiceInstance { - ret := make([]*model.ServiceInstance, 0) - for _, p := range svc.Ports { - if p.Port != servicePort { - continue - } - ret = append(ret, &model.ServiceInstance{ - Service: svc, - ServicePort: p, - Endpoint: &model.IstioEndpoint{ - Address: proxy.IPAddresses[0], - ServicePortName: p.Name, - EndpointPort: uint32(targetPort), - }, - }) - } - return ret -} diff --git a/pkg/envoy/admin.go b/pkg/envoy/admin.go deleted file mode 100644 index 4345894fe..000000000 --- a/pkg/envoy/admin.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package envoy - -import ( - "bytes" - "fmt" - "io" - "net/http" - "strings" -) - -import ( - envoyAdmin "github.com/envoyproxy/go-control-plane/envoy/admin/v3" - "google.golang.org/protobuf/proto" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -// Shutdown initiates a graceful shutdown of Envoy. -func Shutdown(adminPort uint32) error { - _, err := doEnvoyPost("quitquitquit", "", "", adminPort) - return err -} - -// DrainListeners drains inbound listeners of Envoy so that inflight requests -// can gracefully finish and even continue making outbound calls as needed. -func DrainListeners(adminPort uint32, inboundonly bool) error { - var drainURL string - if inboundonly { - drainURL = "drain_listeners?inboundonly&graceful" - } else { - drainURL = "drain_listeners?graceful" - } - res, err := doEnvoyPost(drainURL, "", "", adminPort) - log.Debugf("Drain listener endpoint response : %s", res.String()) - return err -} - -// GetServerInfo returns a structure representing a call to /server_info -func GetServerInfo(adminPort uint32) (*envoyAdmin.ServerInfo, error) { - buffer, err := doEnvoyGet("server_info", adminPort) - if err != nil { - return nil, err - } - - msg := &envoyAdmin.ServerInfo{} - if err := unmarshal(buffer.String(), msg); err != nil { - return nil, err - } - - return msg, nil -} - -// GetConfigDump polls Envoy admin port for the config dump and returns the response. -func GetConfigDump(adminPort uint32) (*envoyAdmin.ConfigDump, error) { - buffer, err := doEnvoyGet("config_dump", adminPort) - if err != nil { - return nil, err - } - - msg := &envoyAdmin.ConfigDump{} - if err := unmarshal(buffer.String(), msg); err != nil { - return nil, err - } - return msg, nil -} - -func doEnvoyGet(path string, adminPort uint32) (*bytes.Buffer, error) { - requestURL := fmt.Sprintf("http://localhost:%d/%s", adminPort, path) - buffer, err := doHTTPGet(requestURL) - if err != nil { - return nil, err - } - return buffer, nil -} - -func doEnvoyPost(path, contentType, body string, adminPort uint32) (*bytes.Buffer, error) { - requestURL := fmt.Sprintf("http://localhost:%d/%s", adminPort, path) - buffer, err := doHTTPPost(requestURL, contentType, body) - if err != nil { - return nil, err - } - return buffer, nil -} - -func doHTTPGet(requestURL string) (*bytes.Buffer, error) { - response, err := http.Get(requestURL) - if err != nil { - return nil, err - } - defer func() { _ = response.Body.Close() }() - - if response.StatusCode != http.StatusOK { - return nil, fmt.Errorf("unexpected status %d", response.StatusCode) - } - - var b bytes.Buffer - if _, err := io.Copy(&b, response.Body); err != nil { - return nil, err - } - return &b, nil -} - -func doHTTPPost(requestURL, contentType, body string) (*bytes.Buffer, error) { - response, err := http.Post(requestURL, contentType, strings.NewReader(body)) - if err != nil { - return nil, err - } - defer func() { _ = response.Body.Close() }() - - var b bytes.Buffer - if _, err := io.Copy(&b, response.Body); err != nil { - return nil, err - } - return &b, nil -} - -func unmarshal(jsonString string, msg proto.Message) error { - return protomarshal.UnmarshalAllowUnknown([]byte(jsonString), msg) -} diff --git a/pkg/envoy/agent.go b/pkg/envoy/agent.go deleted file mode 100644 index 5b8cb0e44..000000000 --- a/pkg/envoy/agent.go +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package envoy - -import ( - "context" - "errors" - "fmt" - "strconv" - "strings" - "time" -) - -import ( - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/http" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -var errAbort = errors.New("epoch aborted") - -const errOutOfMemory = "signal: killed" - -var activeConnectionCheckDelay = 1 * time.Second - -// NewAgent creates a new proxy agent for the proxy start-up and clean-up functions. -func NewAgent(proxy Proxy, terminationDrainDuration, minDrainDuration time.Duration, localhost string, - adminPort, statusPort, prometheusPort int, exitOnZeroActiveConnections bool) *Agent { - knownIstioListeners := sets.New( - fmt.Sprintf("listener.0.0.0.0_%d.downstream_cx_active", statusPort), - fmt.Sprintf("listener.0.0.0.0_%d.downstream_cx_active", prometheusPort), - "listener.admin.downstream_cx_active", - "listener.admin.main_thread.downstream_cx_active", - ) - return &Agent{ - proxy: proxy, - statusCh: make(chan exitStatus, 1), // context might stop drainage - drainCh: make(chan struct{}), - abortCh: make(chan error, 1), - terminationDrainDuration: terminationDrainDuration, - minDrainDuration: minDrainDuration, - exitOnZeroActiveConnections: exitOnZeroActiveConnections, - adminPort: adminPort, - statusPort: statusPort, - prometheusPort: prometheusPort, - localhost: localhost, - knownIstioListeners: knownIstioListeners, - } -} - -// Proxy defines command interface for a proxy -type Proxy interface { - // Run command for an epoch, and abort channel - Run(int, <-chan error) error - - // Drains the current epoch. - Drain() error - - // Cleanup command for an epoch - Cleanup(int) - - // UpdateConfig writes a new config file - UpdateConfig(config []byte) error -} - -type Agent struct { - // proxy commands - proxy Proxy - - // channel for proxy exit notifications - statusCh chan exitStatus - - drainCh chan struct{} - - abortCh chan error - - // time to allow for the proxy to drain before terminating all remaining proxy processes - terminationDrainDuration time.Duration - minDrainDuration time.Duration - - adminPort int - localhost string - - statusPort int - prometheusPort int - - knownIstioListeners sets.Set - - exitOnZeroActiveConnections bool -} - -type exitStatus struct { - epoch int - err error -} - -// Run starts the envoy and waits until it terminates. -func (a *Agent) Run(ctx context.Context) { - log.Info("Starting proxy agent") - go a.runWait(0, a.abortCh) - - select { - case status := <-a.statusCh: - if status.err != nil { - if status.err.Error() == errOutOfMemory { - log.Warnf("Envoy may have been out of memory killed. Check memory usage and limits.") - } - log.Errorf("Epoch %d exited with error: %v", status.epoch, status.err) - } else { - log.Infof("Epoch %d exited normally", status.epoch) - } - - log.Infof("No more active epochs, terminating") - case <-ctx.Done(): - a.terminate() - status := <-a.statusCh - if status.err == errAbort { - log.Infof("Epoch %d aborted normally", status.epoch) - } else { - log.Warnf("Epoch %d aborted abnormally", status.epoch) - } - log.Info("Agent has successfully terminated") - } -} - -func (a *Agent) terminate() { - log.Infof("Agent draining Proxy") - e := a.proxy.Drain() - if e != nil { - log.Warnf("Error in invoking drain listeners endpoint %v", e) - } - // If exitOnZeroActiveConnections is enabled, always sleep minimumDrainDuration then exit - // after min(all connections close, terminationGracePeriodSeconds-minimumDrainDuration). - // exitOnZeroActiveConnections is disabled (default), retain the existing behavior. - if a.exitOnZeroActiveConnections { - log.Infof("Agent draining proxy for %v, then waiting for active connections to terminate...", a.minDrainDuration) - time.Sleep(a.minDrainDuration) - log.Infof("Checking for active connections...") - ticker := time.NewTicker(activeConnectionCheckDelay) - for range ticker.C { - ac, err := a.activeProxyConnections() - if err != nil { - log.Errorf(err.Error()) - a.abortCh <- errAbort - return - } - if ac == -1 { - log.Info("downstream_cx_active are not available. This either means there are no downstream connection established yet" + - " or the stats are not enabled. Skipping active connections check...") - a.abortCh <- errAbort - return - } - if ac == 0 { - log.Info("There are no more active connections. terminating proxy...") - a.abortCh <- errAbort - return - } - log.Infof("There are still %d active connections", ac) - } - } else { - log.Infof("Graceful termination period is %v, starting...", a.terminationDrainDuration) - time.Sleep(a.terminationDrainDuration) - log.Infof("Graceful termination period complete, terminating remaining proxies.") - a.abortCh <- errAbort - } - log.Warnf("Aborted all epochs") -} - -func (a *Agent) activeProxyConnections() (int, error) { - activeConnectionsURL := fmt.Sprintf("http://%s:%d/stats?usedonly&filter=downstream_cx_active$", a.localhost, a.adminPort) - stats, err := http.DoHTTPGet(activeConnectionsURL) - if err != nil { - return -1, fmt.Errorf("unable to get listener stats from Envoy : %v", err) - } - if stats.Len() == 0 { - return -1, nil - } - activeConnections := 0 - for stats.Len() > 0 { - line, _ := stats.ReadString('\n') - parts := strings.Split(line, ":") - if len(parts) != 2 { - log.Warnf("envoy stat line is missing separator. line:%s", line) - continue - } - // downstream_cx_active is accounted under "http." and "listener." for http listeners. - // Only consider listener stats. Listener stats also will have per worker stats, we can - // ignore them. - if !strings.HasPrefix(parts[0], "listener.") || strings.Contains(parts[0], "worker_") { - continue - } - // If the stat is for a known Istio listener skip it. - if a.knownIstioListeners.Contains(parts[0]) { - continue - } - val, err := strconv.ParseUint(strings.TrimSpace(parts[1]), 10, 64) - if err != nil { - log.Warnf("failed parsing Envoy stat %s (error: %s) line: %s", parts[0], err.Error(), line) - continue - } - activeConnections += int(val) - } - if activeConnections > 0 { - log.Debugf("Active connections stats: %s", stats.String()) - } - return activeConnections, nil -} - -// runWait runs the start-up command as a go routine and waits for it to finish -func (a *Agent) runWait(epoch int, abortCh <-chan error) { - log.Infof("Epoch %d starting", epoch) - err := a.proxy.Run(epoch, abortCh) - a.proxy.Cleanup(epoch) - a.statusCh <- exitStatus{epoch: epoch, err: err} -} diff --git a/pkg/envoy/agent_test.go b/pkg/envoy/agent_test.go deleted file mode 100644 index ef45395fa..000000000 --- a/pkg/envoy/agent_test.go +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package envoy - -import ( - "context" - "net" - "testing" - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/status/testserver" -) - -var invalidStats = "" - -var downstreamCxPostiveAcStats = "http.admin.downstream_cx_active: 2 \n" + - "http.agent.downstream_cx_active: 0 \n" + - "http.inbound_0.0.0.0_8080.downstream_cx_active: 0 \n" + - "listener.0.0.0.0_15001.downstream_cx_active: 0 \n" + - "listener.0.0.0.0_15006.downstream_cx_active: 0 \n" + - "listener.0.0.0.0_15021.downstream_cx_active: 0 \n" + - "listener.0.0.0.0_8443.downstream_cx_active: 6 \n" + - "listener.0.0.0.0_9093.downstream_cx_active: 8 \n" + - "listener.10.112.32.70_9043.downstream_cx_active: 1 \n" + - "listener.10.112.33.230_2181.downstream_cx_active: 0 \n" + - "listener.10.112.40.186_2181.downstream_cx_active: 1 \n" + - "listener.10.112.43.239_9043.downstream_cx_active: 2 \n" + - "listener.10.112.49.68_9043.downstream_cx_active: 1 \n" + - "listener.admin.downstream_cx_active: 2 \n" + - "listener.admin.main_thread.downstream_cx_active: 2" - -var downstreamCxZeroAcStats = "http.admin.downstream_cx_active: 2 \n" + - "http.agent.downstream_cx_active: 0 \n" + - "http.inbound_0.0.0.0_8080.downstream_cx_active: 0 \n" + - "listener.0.0.0.0_15001.downstream_cx_active: 0 \n" + - "listener.0.0.0.0_15006.downstream_cx_active: 0 \n" + - "listener.0.0.0.0_15021.downstream_cx_active: 1 \n" + - "listener.0.0.0.0_8443.downstream_cx_active: 0 \n" + - "listener.0.0.0.0_9093.downstream_cx_active: 0 \n" + - "listener.10.112.32.70_9043.downstream_cx_active: 0 \n" + - "listener.10.112.33.230_2181.downstream_cx_active: 0 \n" + - "listener.10.112.40.186_2181.downstream_cx_active: 0 \n" + - "listener.10.112.43.239_9043.downstream_cx_active: 0 \n" + - "listener.10.112.49.68_9043.downstream_cx_active: 0\n" + - "listener.admin.downstream_cx_active: 2 \n" + - "listener.admin.main_thread.downstream_cx_active: 2" - -// TestProxy sample struct for proxy -type TestProxy struct { - run func(int, <-chan error) error - cleanup func(int) - blockChannel chan interface{} -} - -func (tp TestProxy) Run(epoch int, stop <-chan error) error { - if tp.run == nil { - return nil - } - return tp.run(epoch, stop) -} - -func (tp TestProxy) Drain() error { - tp.blockChannel <- "unblock" - return nil -} - -func (tp TestProxy) Cleanup(epoch int) { - if tp.cleanup != nil { - tp.cleanup(epoch) - } -} - -func (tp TestProxy) UpdateConfig(_ []byte) error { - return nil -} - -// TestStartExit starts a proxy and ensures the agent exits once the proxy exits -func TestStartExit(t *testing.T) { - ctx := context.Background() - done := make(chan struct{}) - a := NewAgent(TestProxy{}, 0, 0, "", 0, 0, 0, true) - go func() { - a.Run(ctx) - done <- struct{}{} - }() - <-done -} - -// TestStartDrain tests basic start, termination sequence -// - Runs with passed config -// - Terminate is called -// - Runs with drain config -// - Aborts all proxies -func TestStartDrain(t *testing.T) { - wantEpoch := 0 - proxiesStarted, wantProxiesStarted := 0, 1 - blockChan := make(chan interface{}) - ctx, cancel := context.WithCancel(context.Background()) - start := func(currentEpoch int, _ <-chan error) error { - proxiesStarted++ - if currentEpoch != wantEpoch { - t.Errorf("start wanted epoch %v, got %v", wantEpoch, currentEpoch) - } - wantEpoch = currentEpoch + 1 - blockChan <- "unblock" - if currentEpoch == 0 { - <-ctx.Done() - time.Sleep(time.Second * 2) // ensure initial proxy doesn't terminate too quickly - } - return nil - } - a := NewAgent(TestProxy{run: start, blockChannel: blockChan}, -10*time.Second, 0, "", 0, 0, 0, true) - go func() { a.Run(ctx) }() - <-blockChan - cancel() - <-blockChan - <-ctx.Done() - - if proxiesStarted != wantProxiesStarted { - t.Errorf("expected %v proxies to be started, got %v", wantProxiesStarted, proxiesStarted) - } -} - -// TestStartTwiceStop applies three configs and validates that cleanups are called in order -func TestStartStop(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - start := func(epoch int, _ <-chan error) error { - return nil - } - cleanup := func(epoch int) { - if epoch == 0 { - cancel() - } - } - a := NewAgent(TestProxy{run: start, cleanup: cleanup}, 0, 0, "", 0, 0, 0, true) - go func() { a.Run(ctx) }() - <-ctx.Done() -} - -// TestRecovery tests that recovery is applied once -func TestRecovery(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - failed := false - start := func(epoch int, _ <-chan error) error { - if epoch == 0 && !failed { - failed = true - return nil - } - if epoch > 0 { - t.Errorf("Should not reconcile after success") - } - <-ctx.Done() - return nil - } - a := NewAgent(TestProxy{run: start}, 0, 0, "", 0, 0, 0, true) - go func() { a.Run(ctx) }() - - // make sure we don't try to reconcile twice - <-time.After(100 * time.Millisecond) - cancel() -} - -func TestActiveConnections(t *testing.T) { - cases := []struct { - name string - stats string - expected int - }{ - { - "invalid stats", - invalidStats, - -1, - }, - { - "valid active connections", - downstreamCxPostiveAcStats, - 19, - }, - { - "zero active connections", - downstreamCxZeroAcStats, - 0, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - server := testserver.CreateAndStartServer(tt.stats) - defer server.Close() - - agent := NewAgent(TestProxy{}, 0, 0, "localhost", server.Listener.Addr().(*net.TCPAddr).Port, 15021, 15009, true) - if ac, _ := agent.activeProxyConnections(); ac != tt.expected { - t.Errorf("unexpected active proxy connections. expected: %d got: %d", tt.expected, ac) - } - }) - } -} diff --git a/pkg/envoy/instance.go b/pkg/envoy/instance.go deleted file mode 100644 index 2e7fe2e80..000000000 --- a/pkg/envoy/instance.go +++ /dev/null @@ -1,483 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package envoy - -import ( - "context" - "errors" - "fmt" - "os" - "os/exec" - "sync" - "time" -) - -import ( - envoyAdmin "github.com/envoyproxy/go-control-plane/envoy/admin/v3" - "istio.io/pkg/log" -) - -const ( - defaultName = "envoy" - defaultLiveTimeout = 20 * time.Second -) - -// Config for an Envoy Instance. -type Config struct { - // Options provides the command-line options to be passed to Envoy. - Options Options - - // Name of the envoy instance, used for logging only. If not provided, defaults to "envoy". - Name string - - // BinaryPath the path to the Envoy binary. - BinaryPath string - - // WorkingDir to be used when running Envoy. If not set, the current working directory is used. - WorkingDir string - - // AdminPort specifies the administration port for the Envoy server. If not set, will - // be determined by parsing the Envoy bootstrap configuration file. - AdminPort uint32 - - // SkipBaseIDClose skips calling Close on the BaseID, if one was specified. - SkipBaseIDClose bool -} - -// Waitable specifies a waitable operation. -type Waitable interface { - // WithTimeout specifies an upper bound on the wait time. - WithTimeout(timeout time.Duration) Waitable - - // Do performs the wait. By default, waits indefinitely. To specify an upper bound on - // the wait time, use WithTimeout. If the wait times out, returns the last known error - // for retried operations or context.DeadlineExceeded if no previous error was encountered. - Do() error -} - -// Instance of an Envoy process. -type Instance interface { - // Config returns the configuration for this Instance. - Config() Config - - // BaseID used to start Envoy. If not set, returns InvalidBaseID. - BaseID() BaseID - - // Epoch used to start Envoy. If it was not set, defaults to 0. - Epoch() Epoch - - // Start the Envoy Instance. The process will be killed if the given context is canceled. - // - // If this Instance was created via NewInstanceForHotRestart, this method will block until the parent - // Instance terminates or goes "live". This is due to the fact that hot restart will fail if the previous - // envoy process is still initializing. - Start(ctx context.Context) Instance - - // NewInstanceForHotRestart creates a new Envoy Instance that is configured for a hot restart of this - // Instance (i.e. epoch is incremented). During a hot restart of Envoy, the old process is drained and - // traffic is shifted over to the new process. - // - // The caller must Start the returned instance to initiate the hot restart. - // - // If a new Instance is successfully created, it assumes ownership of the Envoy shared memory segment - // used for hot restart. This means that when this Instance exits, it will no longer destroy - // the shared memory segment, regardless of the value of SkipBaseIDClose. - // - // If this Instance hasn't been started, calling this method does nothing and simply returns - // this Instance since there is nothing to restart. - // - // This method may only be called once on a given Instance. Subsequent calls will return an error. - NewInstanceForHotRestart() (Instance, error) - - // WaitUntilLive polls the Envoy ServerInfo endpoint and waits for it to transition to "live". If the - // wait times out, returns the last known error or context.DeadlineExceeded if no error occurred within the - // specified duration. - WaitLive() Waitable - - // AdminPort gets the administration port for Envoy. - AdminPort() uint32 - - // GetServerInfo returns a structure representing a call to /server_info - GetServerInfo() (*envoyAdmin.ServerInfo, error) - - // GetConfigDump polls Envoy admin port for the config dump and returns the response. - GetConfigDump() (*envoyAdmin.ConfigDump, error) - - // Wait for the Instance to terminate. - Wait() Waitable - - // Kill the process, if running. - Kill() error - - // KillAndWait is a helper that calls Kill and then waits for the process to terminate. - KillAndWait() Waitable - - // Shutdown initiates the graceful termination of Envoy. Returns immediately and does not - // wait for the process to exit. - Shutdown() error - - // ShutdownAndWait is a helper that calls Shutdown and waits for the process to terminate. - ShutdownAndWait() Waitable - - // DrainListeners drains listeners of Envoy so that inflight requests - // can gracefully finish and even continue making outbound calls as needed. - DrainListeners() error -} - -// FactoryFunc is a function that manufactures Envoy Instances. -type FactoryFunc func(cfg Config) (Instance, error) - -var _ FactoryFunc = New - -// New creates a new Envoy Instance with the given options. -func New(cfg Config) (Instance, error) { - if cfg.Name == "" { - cfg.Name = defaultName - } - - // Process the binary path. - if cfg.BinaryPath == "" { - return nil, errors.New("must specify an Envoy binary") - } - - // Create the config object from the options. - ctx := newConfigContext() - if err := cfg.Options.validate(ctx); err != nil { - return nil, err - } - - // Extract the admin port from the configuration. - adminPort := cfg.AdminPort - if adminPort == 0 { - var err error - adminPort, err = ctx.getAdminPort() - if err != nil { - return nil, err - } - } - - // Create a new command with the specified options. - args := cfg.Options.ToArgs() - cmd := exec.Command(cfg.BinaryPath, args...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if cfg.WorkingDir != "" { - cmd.Dir = cfg.WorkingDir - } - - return &instance{ - name: cfg.Name, - config: cfg, - cmd: cmd, - adminPort: adminPort, - baseID: ctx.baseID, - epoch: ctx.epoch, - waitCh: make(chan struct{}, 1), - }, nil -} - -type instance struct { - config Config - name string - waitErr error - cmd *exec.Cmd - waitCh chan struct{} - adminPort uint32 - baseID BaseID - epoch Epoch - started bool - hotRestart Instance - mux sync.Mutex -} - -func (i *instance) Config() Config { - return i.config -} - -func (i *instance) Start(ctx context.Context) Instance { - i.mux.Lock() - defer i.mux.Unlock() - - log.Infof("%s starting with command: %v", i.logID(), i.cmd.Args) - - // Make sure we haven't already started. - if i.started { - log.Infof("%s was already started, skipping Start", i.logID()) - return i - } - i.started = true - - // Start Envoy. - if err := i.cmd.Start(); err != nil { - i.waitErr = err - i.close() - return i - } - - // Asynchronously wait for the command to terminate. - doneCh := make(chan error, 1) - go func() { - // Send the - doneCh <- i.cmd.Wait() - close(doneCh) - }() - - go func() { - // Free all resources and close doneCh when we exit. - defer i.close() - - select { - case <-ctx.Done(): - log.Infof("%s Aborting: %v", i.logID(), ctx.Err()) - i.waitErr = ctx.Err() - - // Context aborted ... kill the process. - if err := i.Kill(); err != nil { - log.Warnf("%s kill failed: %v", i.logID(), err) - } - return - case i.waitErr = <-doneCh: - log.Infof("%s exited with error: %v", i.logID(), i.waitErr) - return - } - }() - - return i -} - -func (i *instance) NewInstanceForHotRestart() (Instance, error) { - i.mux.Lock() - defer i.mux.Unlock() - - if i.hotRestart != nil { - return nil, fmt.Errorf("%s already created a hot restart Instance", i.logID()) - } - - if !i.started { - // This instance hasn't been started yet, no restart required. - return i, nil - } - - // If this is a hot restart, wait for the parent process to be live before creating the new - // instance. - if err := i.WaitLive().WithTimeout(defaultLiveTimeout).Do(); err != nil { - log.Warnf("%s failed to go live: %v. Proceeding with hot restart", - i.logID(), err) - } - - // Copy the configuration, but replace the epoch. - cfg := i.config - cfg.Options = make(Options, 0, len(cfg.Options)) - for _, o := range i.config.Options { - if o.FlagName() != Epoch(0).FlagName() { - cfg.Options = append(cfg.Options, o) - } - } - - // Increment the epoch on the new Instance. - cfg.Options = append(cfg.Options, i.epoch+1) - - // Create the new instance. - hotRestart, err := New(cfg) - if err != nil { - return nil, err - } - i.hotRestart = hotRestart - - return hotRestart, nil -} - -func (i *instance) WaitLive() Waitable { - return &waitableImpl{ - instance: i, - retryPeriod: 200 * time.Millisecond, - retryHandler: func() (bool, error) { - info, err := GetServerInfo(i.adminPort) - if err != nil { - return true, err - } - - switch info.State { - case envoyAdmin.ServerInfo_LIVE: - // We're live! - return false, nil - case envoyAdmin.ServerInfo_DRAINING: - // Don't retry, it'll never happen. - return false, errors.New("envoy will never go live, it's already draining") - default: - // Retry. - return true, fmt.Errorf("envoy not live. Server State: %s", info.State) - } - }, - } -} - -func (i *instance) AdminPort() uint32 { - return i.adminPort -} - -func (i *instance) BaseID() BaseID { - return i.baseID -} - -func (i *instance) Epoch() Epoch { - return i.epoch -} - -func (i *instance) GetServerInfo() (*envoyAdmin.ServerInfo, error) { - return GetServerInfo(i.adminPort) -} - -func (i *instance) GetConfigDump() (*envoyAdmin.ConfigDump, error) { - return GetConfigDump(i.adminPort) -} - -func (i *instance) Wait() Waitable { - return &waitableImpl{ - instance: i, - } -} - -func (i *instance) Kill() error { - if i.cmd.Process == nil { - return errors.New("envoy process was not started") - } - - return i.cmd.Process.Kill() -} - -func (i *instance) KillAndWait() Waitable { - return &waitableImpl{ - instance: i, - creationErr: i.Kill(), - } -} - -func (i *instance) Shutdown() error { - return Shutdown(i.adminPort) -} - -func (i *instance) ShutdownAndWait() Waitable { - return &waitableImpl{ - instance: i, - creationErr: i.Shutdown(), - } -} - -func (i *instance) DrainListeners() error { - return DrainListeners(i.adminPort, true) -} - -func (i *instance) close() { - // Delete the shared memory segment (used for hot restart) if configured to do and no - // further hot-restarts were initiated. If another restart was initiated, we hand off - // ownership of the shared memory to that Instance. - if !i.config.SkipBaseIDClose && i.hotRestart == nil { - if err := i.baseID.Close(); err != nil { - log.Infof("Failed freeing BaseID for %s: %v", i.logID(), err) - } - } - - close(i.waitCh) -} - -func (i *instance) logID() string { - return fmt.Sprintf("Envoy '%s' (epoch %d)", i.name, i.epoch) -} - -var _ Waitable = &waitableImpl{} - -type waitableImpl struct { - *instance - - creationErr error - - retryPeriod time.Duration - retryHandler func() (bool, error) - - timeout time.Duration -} - -func (w *waitableImpl) Do() error { - if w.creationErr != nil { - return w.creationErr - } - - // Create a dummy time channel to be used if needed. - dummyTimeCh := make(chan time.Time, 1) - defer close(dummyTimeCh) - - // Create the timeout channel - var timeoutCh <-chan time.Time - if w.timeout > 0 { - // Create a timer for the specified duration. - timer := time.NewTimer(w.timeout) - defer timer.Stop() - timeoutCh = timer.C - } else { - // No timeout was specified, just create dummy channel. - timeoutCh = dummyTimeCh - } - - var retryCh <-chan time.Time - isRetrying := w.retryPeriod > 0 - if isRetrying { - // Create a ticker for the specified duration. - ticker := time.NewTicker(w.retryPeriod) - defer ticker.Stop() - retryCh = ticker.C - } else { - // No ticker was specified, just use a dummy channel. - retryCh = dummyTimeCh - } - - var lastErr error - for { - select { - case <-w.waitCh: - if w.waitErr != nil { - return w.waitErr - } - if lastErr != nil { - return lastErr - } - if isRetrying { - // Envoy exited before the retry operation was successful, - return errors.New("envoy process exited before wait completed") - } - return nil - case <-retryCh: - shouldRetry, err := w.retryHandler() - if !shouldRetry { - return err - } - - // We're retrying, save the last error. - lastErr = err - case <-timeoutCh: - if lastErr != nil { - return lastErr - } - // The timeout occurred before any other events. - return context.DeadlineExceeded - } - } -} - -func (w *waitableImpl) WithTimeout(d time.Duration) Waitable { - out := *w - out.timeout = d - return &out -} diff --git a/pkg/envoy/instance_test.go b/pkg/envoy/instance_test.go deleted file mode 100644 index aa42de1d8..000000000 --- a/pkg/envoy/instance_test.go +++ /dev/null @@ -1,549 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package envoy_test - -import ( - "context" - "errors" - "os" - "path/filepath" - "runtime" - "testing" - "time" -) - -import ( - envoyAdmin "github.com/envoyproxy/go-control-plane/envoy/admin/v3" - . "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/envoy" - testEnvoy "github.com/apache/dubbo-go-pixiu/pkg/test/envoy" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/reserveport" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/tmpl" -) - -var envoyLogFormat = envoy.LogFormat("[ENVOY][%Y-%m-%d %T.%e][%t][%l][%n] %v") - -func TestNewWithoutConfigShouldFail(t *testing.T) { - g := NewWithT(t) - - _, err := envoy.New(envoy.Config{}) - g.Expect(err).ToNot(BeNil()) -} - -func TestNewWithDuplicateOptionsShouldFail(t *testing.T) { - g := NewWithT(t) - - h := newBootstrapHelper(t) - defer h.Close() - - _, err := envoy.New(envoy.Config{ - BinaryPath: testEnvoy.FindBinaryOrFail(t), - Options: options( - envoy.ConfigPath(h.BootstrapFile()), - envoy.ConfigPath(h.BootstrapFile())), - }) - g.Expect(err).ToNot(BeNil()) -} - -func TestNewWithInvalidOptionShouldFail(t *testing.T) { - g := NewWithT(t) - - _, err := envoy.New(envoy.Config{ - BinaryPath: testEnvoy.FindBinaryOrFail(t), - Options: options(envoy.ConfigPath("file/does/not/exist")), - }) - g.Expect(err).ToNot(BeNil()) -} - -func TestNewWithoutAdminPortShouldFail(t *testing.T) { - g := NewWithT(t) - - _, err := envoy.New(envoy.Config{ - BinaryPath: testEnvoy.FindBinaryOrFail(t), - Options: options(envoy.ConfigYaml("{}")), - }) - g.Expect(err).ToNot(BeNil()) -} - -func TestNewWithoutBinaryPathShouldFail(t *testing.T) { - g := NewWithT(t) - - h := newBootstrapHelper(t) - defer h.Close() - - _, err := envoy.New(envoy.Config{ - Options: options(envoy.ConfigPath(h.BootstrapFile())), - }) - g.Expect(err).ToNot(BeNil()) -} - -func TestNewWithConfigPathShouldSucceed(t *testing.T) { - g := NewWithT(t) - - h := newBootstrapHelper(t) - defer h.Close() - - i := h.NewOrFail(t, envoy.Config{ - BinaryPath: testEnvoy.FindBinaryOrFail(t), - Options: options(envoy.ConfigPath(h.BootstrapFile())), - }) - g.Expect(i.Config().Name).To(Equal("envoy")) - g.Expect(i.Config().BinaryPath).ToNot(Equal("")) -} - -func TestNewWithConfigYamlShouldSucceed(t *testing.T) { - g := NewWithT(t) - - h := newBootstrapHelper(t) - defer h.Close() - - i := h.NewOrFail(t, envoy.Config{ - BinaryPath: testEnvoy.FindBinaryOrFail(t), - Options: options(envoy.ConfigYaml(h.BootstrapContent(t))), - }) - g.Expect(i.Config().Name).To(Equal("envoy")) - g.Expect(i.Config().BinaryPath).ToNot(Equal("")) -} - -func TestStartWithBadBinaryShouldFail(t *testing.T) { - runLinuxOnly(t) - - h := newBootstrapHelper(t) - defer h.Close() - - i := h.NewOrFail(t, envoy.Config{ - BinaryPath: absPath("testdata/envoy_bootstrap.json"), // Not a binary file. - Options: options(envoy.ConfigYaml(h.BootstrapContent(t))), - }) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - i.Start(ctx) - waitForError(t, i) -} - -func TestStartEnvoyShouldSucceed(t *testing.T) { - runLinuxOnly(t) - - g := NewWithT(t) - - h := newBootstrapHelper(t) - defer h.Close() - - i := h.NewOrFail(t, envoy.Config{ - BinaryPath: testEnvoy.FindBinaryOrFail(t), - Options: options(envoy.ConfigPath(h.BootstrapFile())), - }) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - i.Start(ctx) - waitLive(t, i) - - // Wait for a bit and verify that Envoy hasn't terminated. - err := i.Wait().WithTimeout(time.Second * 1).Do() - g.Expect(err).To(Equal(context.DeadlineExceeded)) -} - -func TestStartTwiceShouldDoNothing(t *testing.T) { - runLinuxOnly(t) - - h := newBootstrapHelper(t) - defer h.Close() - - i := h.NewOrFail(t, envoy.Config{ - BinaryPath: testEnvoy.FindBinaryOrFail(t), - Options: options(envoy.ConfigPath(h.BootstrapFile())), - }) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - i.Start(ctx) - i.Start(ctx) - waitLive(t, i) -} - -func TestHotRestartTwiceShouldFail(t *testing.T) { - runLinuxOnly(t) - - g := NewWithT(t) - - h := newBootstrapHelper(t) - defer h.Close() - - i := h.NewOrFail(t, envoy.Config{ - BinaryPath: testEnvoy.FindBinaryOrFail(t), - Options: options(envoy.ConfigPath(h.BootstrapFile())), - }) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - i.Start(ctx) - - _, err := i.NewInstanceForHotRestart() - g.Expect(err).To(BeNil()) - - _, err = i.NewInstanceForHotRestart() - g.Expect(err).ToNot(BeNil()) -} - -func TestHotRestart(t *testing.T) { - runLinuxOnly(t) - - g := NewWithT(t) - - h := newBootstrapHelper(t) - defer h.Close() - - i := h.NewOrFail(t, envoy.Config{ - BinaryPath: testEnvoy.FindBinaryOrFail(t), - Options: options( - envoy.ConfigPath(h.BootstrapFile()), - // Setting parameters to force shutdown of the first Envoy quickly. - envoy.DrainDuration(1*time.Second), - envoy.ParentShutdownDuration(1*time.Second)), - }) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - i.Start(ctx) - restartInstance, err := i.NewInstanceForHotRestart() - g.Expect(err).To(BeNil()) - g.Expect(restartInstance.Epoch()).To(Equal(envoy.Epoch(1))) - - // Confirm that the first instance is live. - info, err := i.GetServerInfo() - g.Expect(err).To(BeNil()) - g.Expect(info.State).To(Equal(envoyAdmin.ServerInfo_LIVE)) - - // Start the restart instance. - restartInstance.Start(ctx) - - // Wait for the first instance to exit - if err := i.Wait().WithTimeout(5 * time.Second).Do(); err != nil { - t.Fatal(err) - } - - waitLive(t, restartInstance) -} - -func TestCommandLineArgs(t *testing.T) { - runLinuxOnly(t) - - g := NewWithT(t) - - h := newBootstrapHelper(t) - defer h.Close() - - logPath := filepath.Join(h.tempDir, "envoyLogPath.txt") - drainDuration := time.Second * 30 - parentShutdownDuration := time.Second * 60 - i := h.NewOrFail(t, envoy.Config{ - BinaryPath: testEnvoy.FindBinaryOrFail(t), - Options: options( - envoy.ConfigPath(h.BootstrapFile()), - envoy.ConfigYaml("{}"), - envoy.LogLevelDebug, - envoy.LogPath(logPath), - envoy.ComponentLogLevels{ - envoy.ComponentLogLevel{ - Name: "upstream", - Level: envoy.LogLevelWarning, - }, - }, - envoy.LocalAddressIPVersion(envoy.IPV4), - envoy.Concurrency(7), - envoy.DisableHotRestart(true), - envoy.Epoch(1), - envoy.ServiceCluster("mycluster"), - envoy.ServiceNode("mynode"), - envoy.DrainDuration(drainDuration), - envoy.ParentShutdownDuration(parentShutdownDuration), - ), - }) - g.Expect(i.Epoch()).To(Equal(envoy.Epoch(1))) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - i.Start(ctx) - waitLive(t, i) - - info, err := i.GetServerInfo() - g.Expect(err).To(BeNil()) - - // Just verify that options we specified are reflected in the binary. We don't - // do an exhaustive test of the various options here, since that is done in the options tests. - opts := info.CommandLineOptions - g.Expect(opts.ConfigPath).To(Equal(h.BootstrapFile())) - g.Expect(opts.ConfigYaml).To(Equal("{}")) - g.Expect(opts.LogLevel).To(Equal(envoy.LogLevelDebug.FlagValue())) - g.Expect(opts.LogFormat).To(Equal(envoyLogFormat.FlagValue())) - g.Expect(opts.LogPath).To(Equal(logPath)) - g.Expect(opts.ComponentLogLevel).To(Equal("upstream:warning")) - g.Expect(opts.LocalAddressIpVersion).To(Equal(envoyAdmin.CommandLineOptions_v4)) - g.Expect(opts.BaseId).To(Equal(h.baseID.GetInternalEnvoyValue())) - g.Expect(opts.Concurrency).To(Equal(uint32(7))) - g.Expect(opts.DisableHotRestart).To(BeTrue()) - g.Expect(opts.RestartEpoch).To(Equal(uint32(1))) - g.Expect(opts.ServiceCluster).To(Equal("mycluster")) - g.Expect(opts.ServiceNode).To(Equal("mynode")) - g.Expect(opts.DrainTime.AsDuration()).To(Equal(drainDuration)) - g.Expect(opts.ParentShutdownTime.AsDuration()).To(Equal(parentShutdownDuration)) -} - -func TestShutdown(t *testing.T) { - runLinuxOnly(t) - - g := NewWithT(t) - - h := newBootstrapHelper(t) - defer h.Close() - - i := h.NewOrFail(t, envoy.Config{ - BinaryPath: testEnvoy.FindBinaryOrFail(t), - Options: options(envoy.ConfigPath(h.BootstrapFile())), - }) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - i.Start(ctx) - waitLive(t, i) - - // Initiate the shutdown. - g.Expect(i.Shutdown()).To(BeNil()) - - // Wait for the shutdown to complete. - wait(t, i) -} - -func TestKillEnvoy(t *testing.T) { - runLinuxOnly(t) - - g := NewWithT(t) - - h := newBootstrapHelper(t) - defer h.Close() - - i := h.NewOrFail(t, envoy.Config{ - BinaryPath: testEnvoy.FindBinaryOrFail(t), - Options: options(envoy.ConfigPath(h.BootstrapFile())), - }) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - i.Start(ctx) - waitLive(t, i) - - // Kill the process. - g.Expect(i.Kill()).To(BeNil()) - - // Expect the process to return an error. - waitForError(t, i) -} - -func TestCancelContext(t *testing.T) { - runLinuxOnly(t) - - h := newBootstrapHelper(t) - defer h.Close() - - i := h.NewOrFail(t, envoy.Config{ - BinaryPath: testEnvoy.FindBinaryOrFail(t), - Options: options(envoy.ConfigPath(h.BootstrapFile())), - }) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - i.Start(ctx) - waitLive(t, i) - - // Cancel the context. - cancel() - - // Expect the process to return an error. - waitForError(t, i) -} - -func TestConfigDump(t *testing.T) { - runLinuxOnly(t) - - g := NewWithT(t) - - h := newBootstrapHelper(t) - defer h.Close() - - i := h.NewOrFail(t, envoy.Config{ - BinaryPath: testEnvoy.FindBinaryOrFail(t), - Options: options(envoy.ConfigPath(h.BootstrapFile())), - }) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - i.Start(ctx) - waitLive(t, i) - - cd, err := i.GetConfigDump() - g.Expect(err).To(BeNil()) - - // Basic verification of the config dump.. - for _, c := range cd.Configs { - if c.TypeUrl == "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump" { - b := envoyAdmin.BootstrapConfigDump{} - g.Expect(c.UnmarshalTo(&b)).To(BeNil()) - - g.Expect(b.Bootstrap.Admin.GetAddress().GetSocketAddress().GetPortValue()).To(Equal(i.AdminPort())) - return - } - } - t.Fatal("failed validating envoy config dump") -} - -type bootstrapHelper struct { - tempDir string - portMgr reserveport.PortManager - bootstrapFile string - baseID envoy.BaseID -} - -func newBootstrapHelper(t *testing.T) *bootstrapHelper { - t.Helper() - tempDir := t.TempDir() - portMgr := reserveport.NewPortManagerOrFail(t) - adminPort := portMgr.ReservePortNumberOrFail(t) - listenerPort := portMgr.ReservePortNumberOrFail(t) - bootstrapFile := newBootstrapFile(t, tempDir, adminPort, listenerPort) - return &bootstrapHelper{ - tempDir: tempDir, - portMgr: portMgr, - bootstrapFile: bootstrapFile, - baseID: envoy.GenerateBaseID(), - } -} - -func (h *bootstrapHelper) BootstrapFile() string { - return h.bootstrapFile -} - -func (h *bootstrapHelper) BootstrapContent(t *testing.T) string { - t.Helper() - content, err := os.ReadFile(h.bootstrapFile) - if err != nil { - t.Fatal(err) - } - return string(content) -} - -func (h *bootstrapHelper) Close() { - _ = os.RemoveAll(h.tempDir) - h.portMgr.CloseSilently() -} - -func (h *bootstrapHelper) New(cfg envoy.Config) (envoy.Instance, error) { - cfg.Options = append(cfg.Options, h.baseID) - return envoy.New(cfg) -} - -func (h *bootstrapHelper) NewOrFail(t *testing.T, cfg envoy.Config) envoy.Instance { - t.Helper() - i, err := h.New(cfg) - if err != nil { - t.Fatal(err) - } - return i -} - -func absPath(path string) string { - path, err := filepath.Abs(path) - if err != nil { - panic(err) - } - return path -} - -func newBootstrapFile(t *testing.T, tempDir string, adminPort, listenerPort uint16) string { - t.Helper() - data := map[string]interface{}{ - "nodeID": t.Name(), - "cluster": t.Name(), - "localhost": "127.0.0.1", - "wildcard": "0.0.0.0", - "adminPort": adminPort, - "listenerPort": listenerPort, - } - - // Read the template file. - tmplContent, err := os.ReadFile("testdata/envoy_bootstrap_v2.tmpl.json") - if err != nil { - t.Fatal(err) - } - - // Parse the template file. - tpl := tmpl.ParseOrFail(t, string(tmplContent)) - - // Execute the template with the given data. - content := tmpl.ExecuteOrFail(t, tpl, data) - - // Write out the result to the output file. - outputFile := filepath.Join(tempDir, t.Name()+".json") - if err := os.WriteFile(outputFile, []byte(content), os.ModePerm); err != nil { - t.Fatal(err) - } - return outputFile -} - -func wait(t *testing.T, i envoy.Instance) { - t.Helper() - if err := i.Wait().WithTimeout(2 * time.Second).Do(); err != nil { - t.Fatal(err) - } -} - -func waitForError(t *testing.T, i envoy.Instance) { - t.Helper() - if err := i.Wait().WithTimeout(2 * time.Second).Do(); err == nil { - t.Fatal(errors.New("expected error")) - } -} - -func waitLive(t *testing.T, i envoy.Instance) { - t.Helper() - if err := i.WaitLive().WithTimeout(10 * time.Second).Do(); err != nil { - t.Fatal(err) - } -} - -func options(opts ...envoy.Option) []envoy.Option { - return append([]envoy.Option{envoyLogFormat}, opts...) -} - -func runLinuxOnly(t *testing.T) { - t.Helper() - if runtime.GOOS != "linux" { - t.Skip("test requires GOOS=linux") - } -} diff --git a/pkg/envoy/options.go b/pkg/envoy/options.go deleted file mode 100644 index 30cf353cf..000000000 --- a/pkg/envoy/options.go +++ /dev/null @@ -1,746 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package envoy - -import ( - "errors" - "fmt" - "math" - "math/rand" - "os" - "strconv" - "strings" - "time" -) - -import ( - envoyBootstrap "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v3" - "github.com/hashicorp/go-multierror" - "istio.io/pkg/log" - "sigs.k8s.io/yaml" -) - -const ( - // InvalidBaseID used to indicate that the Envoy BaseID has not been set. Attempting - // to Close this BaseID will have no effect. - InvalidBaseID = BaseID(math.MaxUint32) -) - -// FlagName is the raw flag name passed to envoy. -type FlagName string - -func (n FlagName) String() string { - return string(n) -} - -// Option for an Envoy Instance. -type Option interface { - // FlagName returns the flag name used on the command line. - FlagName() FlagName - - // FlagValue returns the flag value used on the command line. Can be empty for boolean flags. - FlagValue() string - - apply(ctx *configContext) - validate(ctx *configContext) error -} - -type Options []Option - -// ToArgs creates the command line arguments for the list of options. -func (options Options) ToArgs() []string { - // Get the arguments for the command. - args := make([]string, 0, len(options)*2) - for _, o := range options { - name := o.FlagName() - if name != "" { - args = append(args, name.String()) - value := o.FlagValue() - if value != "" { - args = append(args, value) - } - } - } - return args -} - -// Validate the Options. -func (options Options) Validate() error { - return options.validate(newConfigContext()) -} - -// validate is an internal method for validation. -func (options Options) validate(ctx *configContext) error { - // Check for any duplicate user-specified options - if err := options.checkDuplicates(); err != nil { - return err - } - - // Add a placeholder ConfigPath option. Can and should be overridden by user-provided value. - // Used for force config validation to ensure that either configPath or configYaml has been set. - opts := append(Options{ConfigPath("")}, options...) - - // Apply the options to the context. - for _, o := range opts { - o.apply(ctx) - } - - // Validate all of the options. - for _, o := range opts { - if err := o.validate(ctx); err != nil { - return err - } - } - - return nil -} - -// checkDuplicates ensures to make sure that there are no duplicate options. -func (options Options) checkDuplicates() error { - optionSet := make(map[FlagName]struct{}) - for _, o := range options { - if _, ok := optionSet[o.FlagName()]; ok { - return fmt.Errorf("multiple options specified for %s", o.FlagName()) - } - optionSet[o.FlagName()] = struct{}{} - } - return nil -} - -// NewOptions creates new Options from the given raw Envoy arguments. Returns an error if a problem -// was encountered while parsing the arguments. -func NewOptions(args ...string) (Options, error) { - out := make(Options, 0, len(args)) - var next *genericOption - for _, arg := range args { - arg = strings.TrimSpace(arg) - if strings.HasPrefix(arg, "-") { - // The argument is a new flag name. - if next != nil { - out = append(out, next) - } - - flagName := FlagName(arg) - - if v, ok := flagValidators[flagName]; ok { - // Known flag - use an existing validator. - next = &genericOption{ - v: v, - } - } else { - // Unknown flag - No validator. - next = &genericOption{ - v: &flagValidator{ - flagName: flagName, - apply: func(*configContext, string) {}, - validate: func(*configContext, string) error { - return nil - }, - }, - } - } - } else { - // The argument is a flag value. - if next == nil { - return nil, fmt.Errorf("raw argument missing flag name: %s", arg) - } - - // Completed the current flag. - next.value = arg - out = append(out, next) - next = nil - } - } - - if next != nil { - out = append(out, next) - } - - return out, nil -} - -// LogLevel is an Option that sets the Envoy log level. -type LogLevel string - -var _ Option = LogLevel("") - -const ( - LogLevelTrace LogLevel = "trace" - LogLevelDebug LogLevel = "debug" - LogLevelInfo LogLevel = "info" - LogLevelWarning LogLevel = "warning" - LogLevelCritical LogLevel = "critical" - LogLevelOff LogLevel = "off" -) - -func (l LogLevel) FlagName() FlagName { - return logLevelValidator.flagName -} - -func (l LogLevel) FlagValue() string { - return string(l) -} - -func (l LogLevel) apply(ctx *configContext) { - logLevelValidator.apply(ctx, l.FlagValue()) -} - -func (l LogLevel) validate(ctx *configContext) error { - return logLevelValidator.validate(ctx, l.FlagValue()) -} - -var logLevelValidator = registerFlagValidator(&flagValidator{ - flagName: "--log-level", - apply: func(ctx *configContext, flagValue string) { - // Do nothing. - }, - validate: func(ctx *configContext, flagValue string) error { - logLevel := LogLevel(flagValue) - switch logLevel { - case LogLevelTrace, LogLevelDebug, LogLevelInfo, LogLevelWarning, LogLevelCritical, LogLevelOff: - return nil - default: - return fmt.Errorf("unsupported log level: %v", logLevel) - } - }, -}) - -// ComponentLogLevel defines the log level for a single component. -type ComponentLogLevel struct { - Name string - Level LogLevel -} - -func (l ComponentLogLevel) String() string { - return l.Name + ":" + string(l.Level) -} - -// ParseComponentLogLevels parses the given envoy --component-log-level value string. -func ParseComponentLogLevels(value string) ComponentLogLevels { - parts := strings.Split(value, ",") - - out := make(ComponentLogLevels, 0, len(parts)) - - for _, part := range parts { - keyAndValue := strings.Split(part, ":") - if len(keyAndValue) == 2 { - out = append(out, ComponentLogLevel{ - Name: keyAndValue[0], - Level: LogLevel(keyAndValue[1]), - }) - } - } - - return out -} - -// ComponentLogLevels is an Option for multiple component log levels. -type ComponentLogLevels []ComponentLogLevel - -var _ Option = ComponentLogLevels{} - -func (l ComponentLogLevels) apply(ctx *configContext) { - componentLogLevelValidator.apply(ctx, l.FlagValue()) -} - -func (l ComponentLogLevels) validate(ctx *configContext) error { - return componentLogLevelValidator.validate(ctx, l.FlagValue()) -} - -func (l ComponentLogLevels) FlagName() FlagName { - return componentLogLevelValidator.flagName -} - -func (l ComponentLogLevels) FlagValue() string { - strLevels := make([]string, 0, len(l)) - for _, cl := range l { - strLevels = append(strLevels, cl.String()) - } - return strings.Join(strLevels, ",") -} - -var componentLogLevelValidator = registerFlagValidator(&flagValidator{ - flagName: "--component-log-level", - apply: func(ctx *configContext, flagValue string) { - // Do nothing. - }, - validate: func(ctx *configContext, flagValue string) error { - l := ParseComponentLogLevels(flagValue) - for i, cl := range l { - if cl.Name == "" { - return fmt.Errorf("name is empty for component log level %d", i) - } - if err := cl.Level.validate(ctx); err != nil { - return fmt.Errorf("level invalid for component log level %d: %v", i, err) - } - } - return nil - }, -}) - -// IPVersion is an enumeration for IP versions for the --local-address-ip-version flag. -type IPVersion string - -const ( - IPV4 IPVersion = "v4" - IPV6 IPVersion = "v6" -) - -// LocalAddressIPVersion sets the --local-address-ip-version flag, which sets the IP address -// version used for the local IP address. The default is V4. -func LocalAddressIPVersion(v IPVersion) Option { - return &genericOption{ - value: string(v), - v: localAddressIPVersionValidator, - } -} - -var localAddressIPVersionValidator = registerFlagValidator(&flagValidator{ - flagName: "--local-address-ip-version", - validate: func(ctx *configContext, flagValue string) error { - ipVersion := IPVersion(flagValue) - switch ipVersion { - case IPV4, IPV6: - return nil - default: - return fmt.Errorf("invalid LocalAddressIPVersion %v", ipVersion) - } - }, -}) - -// ConfigPath sets the --config-path flag, which provides Envoy with the -// to the v2 bootstrap configuration file. If not set, ConfigYaml is required. -func ConfigPath(path string) Option { - return &genericOption{ - value: path, - v: configPathValidator, - } -} - -var configPathValidator = registerFlagValidator(&flagValidator{ - flagName: "--config-path", - apply: func(ctx *configContext, flagValue string) { - ctx.configPath = flagValue - }, - validate: func(ctx *configContext, flagValue string) error { - // Ensure that either config path or configYaml is specified. - if ctx.configPath == "" && ctx.configYaml == "" { - return errors.New("must specify ConfigPath and/or ConfigYaml ") - } - if ctx.configPath != "" { - // Check that the path set in the config exists. - if _, err := os.Stat(ctx.configPath); os.IsNotExist(err) { - return fmt.Errorf("configPath file does not exist: %s", ctx.configPath) - } - } - return nil - }, -}) - -// ConfigYaml sets the --config-yaml flag, which provides Envoy with the -// a YAML string for a v2 bootstrap configuration. If ConfigPath is also set, the values in this -// YAML string will override and merge with the bootstrap loaded from ConfigPath. -func ConfigYaml(yaml string) Option { - return &genericOption{ - value: yaml, - v: configYamlValidator, - } -} - -var configYamlValidator = registerFlagValidator(&flagValidator{ - flagName: "--config-yaml", - apply: func(ctx *configContext, flagValue string) { - ctx.configYaml = flagValue - }, -}) - -// BaseID is an Option that sets the --base-id flag. This is typically only needed when running multiple -// Envoys on the same machine (common in testing environments). -// -// Envoy will allocate shared memory if provided with a BaseID. This shared memory is used during hot restarts. -// It is up to the caller to free this memory by calling Close() on the BaseID when appropriate. -type BaseID uint32 - -var _ Option = BaseID(0) - -func (bid BaseID) FlagName() FlagName { - return baseIDValidator.flagName -} - -func (bid BaseID) FlagValue() string { - return strconv.FormatUint(uint64(bid), 10) -} - -func (bid BaseID) apply(ctx *configContext) { - baseIDValidator.apply(ctx, bid.FlagValue()) -} - -func (bid BaseID) validate(ctx *configContext) error { - return baseIDValidator.validate(ctx, bid.FlagValue()) -} - -// GetInternalEnvoyValue returns the value used internally by Envoy. -func (bid BaseID) GetInternalEnvoyValue() uint64 { - return uint64(bid) -} - -// Close removes the shared memory allocated by Envoy for this BaseID. -func (bid BaseID) Close() error { - if bid != InvalidBaseID { - // Envoy internally multiplies the base ID from the command line by 10 so that they have spread - // for domain sockets. - path := fmt.Sprintf("/dev/shm/envoy_shared_memory_%d", bid.GetInternalEnvoyValue()) - if err := os.Remove(path); err != nil { - return fmt.Errorf("error deleting Envoy base ID %d shared memory %s: %v", bid, path, err) - } - log.Debugf("successfully freed Envoy base ID %d shared memory %s", bid, path) - } - return nil -} - -var baseIDValidator = registerFlagValidator(&flagValidator{ - flagName: "--base-id", - apply: func(ctx *configContext, flagValue string) { - if bid, err := strconv.ParseUint(flagValue, 10, 64); err == nil { - ctx.baseID = BaseID(bid) - } - }, - validate: func(ctx *configContext, flagValue string) error { - if _, err := strconv.ParseUint(flagValue, 10, 64); err != nil { - return err - } - return nil - }, -}) - -// GenerateBaseID is a method copied from Envoy server tests. -// -// Computes a numeric ID to incorporate into the names of shared-memory segments and -// domain sockets, to help keep them distinct from other tests that might be running concurrently. -func GenerateBaseID() BaseID { - // The PID is needed to isolate namespaces between concurrent processes in CI. - pid := uint32(os.Getpid()) - - // A random number is needed to avoid BaseID collisions for multiple Envoys started from the same - // process. - randNum := rand.Uint32() - - // Pick a prime number to give more of the 32-bits of entropy to the PID, and the - // remainder to the random number. - fourDigitPrime := uint32(7919) - value := pid*fourDigitPrime + randNum%fourDigitPrime - - // TODO(nmittler): Limit to uint16 - Large values seem to cause unexpected shared memory paths in envoy. - out := BaseID(value % math.MaxUint16) - return out -} - -// Concurrency sets the --concurrency flag, which sets the number of worker threads to run. -func Concurrency(concurrency uint16) Option { - return &genericOption{ - value: strconv.FormatUint(uint64(concurrency), 10), - v: concurrencyValidator, - } -} - -var concurrencyValidator = registerFlagValidator(&flagValidator{ - flagName: "--concurrency", - validate: func(ctx *configContext, flagValue string) error { - if _, err := strconv.ParseUint(flagValue, 10, 64); err != nil { - return err - } - return nil - }, -}) - -// DisableHotRestart sets the --disable-hot-restart flag. -func DisableHotRestart(disable bool) Option { - var v *flagValidator - if disable { - v = disableHotRestartValidator - } - - return &genericOption{ - v: v, - value: "", - } -} - -var disableHotRestartValidator = registerBoolFlagValidator("--disable-hot-restart") - -// LogPath sets the --log-path flag, which specifies the output file for logs. If not set -// logs will be written to stderr. -func LogPath(path string) Option { - return &genericOption{ - value: path, - v: logPathValidator, - } -} - -var logPathValidator = registerFlagValidator(&flagValidator{ - flagName: "--log-path", -}) - -// LogFormat sets the --log-format flag, which specifies the format string to use for log -// messages. -func LogFormat(format string) Option { - return &genericOption{ - value: format, - v: logFormatValidator, - } -} - -var logFormatValidator = registerFlagValidator(&flagValidator{ - flagName: "--log-format", -}) - -// Epoch sets the --restart-epoch flag, which specifies the epoch used for hot restart. -type Epoch uint32 - -func (e Epoch) FlagName() FlagName { - return epochValidator.flagName -} - -func (e Epoch) FlagValue() string { - return strconv.FormatUint(uint64(e), 10) -} - -func (e Epoch) apply(ctx *configContext) { - epochValidator.apply(ctx, e.FlagValue()) -} - -func (e Epoch) validate(ctx *configContext) error { - return epochValidator.validate(ctx, e.FlagValue()) -} - -var epochValidator = registerFlagValidator(&flagValidator{ - flagName: "--restart-epoch", - apply: func(ctx *configContext, flagValue string) { - if e, err := strconv.ParseUint(flagValue, 10, 32); err == nil { - ctx.epoch = Epoch(e) - } - }, - validate: func(ctx *configContext, flagValue string) error { - if _, err := strconv.ParseUint(flagValue, 10, 32); err != nil { - return err - } - return nil - }, -}) - -// ServiceCluster sets the --service-cluster flag, which defines the local service cluster -// name where Envoy is running -func ServiceCluster(c string) Option { - return &genericOption{ - value: c, - v: serviceClusterValidator, - } -} - -var serviceClusterValidator = registerFlagValidator(&flagValidator{ - flagName: "--service-cluster", -}) - -// ServiceNode sets the --service-node flag, which defines the local service node name -// where Envoy is running -func ServiceNode(n string) Option { - return &genericOption{ - value: n, - v: serviceNodeValidator, - } -} - -var serviceNodeValidator = registerFlagValidator(&flagValidator{ - flagName: "--service-node", -}) - -// DrainDuration sets the --drain-time-s flag, which defines the amount of time that Envoy will -// drain connections during a hot restart. -func DrainDuration(duration time.Duration) Option { - return &genericOption{ - value: strconv.Itoa(int(duration / time.Second)), - v: drainDurationValidator, - } -} - -var drainDurationValidator = registerFlagValidator(&flagValidator{ - flagName: "--drain-time-s", - validate: func(ctx *configContext, flagValue string) error { - if _, err := strconv.ParseUint(flagValue, 10, 32); err != nil { - return err - } - return nil - }, -}) - -// ParentShutdownDuration sets the --parent-shutdown-time-s flag, which defines the amount of -// time that Envoy will wait before shutting down the parent process during a hot restart -func ParentShutdownDuration(duration time.Duration) Option { - return &genericOption{ - value: strconv.Itoa(int(duration / time.Second)), - v: parentShutdownDurationValidator, - } -} - -var parentShutdownDurationValidator = registerFlagValidator(&flagValidator{ - flagName: "--parent-shutdown-time-s", - validate: func(ctx *configContext, flagValue string) error { - if _, err := strconv.ParseUint(flagValue, 10, 32); err != nil { - return err - } - return nil - }, -}) - -func registerBoolFlagValidator(flagName string) *flagValidator { - return registerFlagValidator(&flagValidator{ - flagName: FlagName(flagName), - validate: func(ctx *configContext, flagValue string) error { - switch flagValue { - case "", "true": - return nil - default: - return fmt.Errorf("unexpected boolean value for flag %s: %s", flagName, flagValue) - } - }, - }) -} - -func getAdminPortFromYaml(yamlData string) (uint32, error) { - jsonData, err := yaml.YAMLToJSON([]byte(yamlData)) - if err != nil { - return 0, fmt.Errorf("error converting envoy bootstrap YAML to JSON: %v", err) - } - - bootstrap := &envoyBootstrap.Bootstrap{} - if err := unmarshal(string(jsonData), bootstrap); err != nil { - return 0, fmt.Errorf("error parsing Envoy bootstrap JSON: %v", err) - } - if bootstrap.GetAdmin() == nil { - return 0, errors.New("unable to locate admin in envoy bootstrap") - } - if bootstrap.GetAdmin().GetAddress() == nil { - return 0, errors.New("unable to locate admin/address in envoy bootstrap") - } - if bootstrap.GetAdmin().GetAddress().GetSocketAddress() == nil { - return 0, errors.New("unable to locate admin/address/socket_address in envoy bootstrap") - } - if bootstrap.GetAdmin().GetAddress().GetSocketAddress().GetPortValue() == 0 { - return 0, errors.New("unable to locate admin/address/socket_address/port_value in envoy bootstrap") - } - return bootstrap.GetAdmin().GetAddress().GetSocketAddress().GetPortValue(), nil -} - -// configContext stores the output of applied Options. -type configContext struct { - configPath string - configYaml string - baseID BaseID - epoch Epoch -} - -func newConfigContext() *configContext { - return &configContext{ - baseID: InvalidBaseID, - } -} - -func (c *configContext) getAdminPort() (uint32, error) { - var err error - - // First, check the config yaml, which overrides config-path. - if c.configYaml != "" { - if port, e := getAdminPortFromYaml(c.configYaml); e != nil { - err = fmt.Errorf("failed to locate admin port in envoy config-yaml: %v", e) - } else { - // Found the port! - return port, nil - } - } - - // Haven't found it yet - check configPath. - if c.configPath == "" { - return 0, multierror.Append(err, errors.New("unable to process envoy bootstrap")) - } - - content, e := os.ReadFile(c.configPath) - if e != nil { - return 0, multierror.Append(err, fmt.Errorf("failed reading config-path file %s: %v", c.configPath, e)) - } - - port, e := getAdminPortFromYaml(string(content)) - if e != nil { - return 0, multierror.Append(err, fmt.Errorf("failed to locate admin port in envoy config-yaml: %v", e)) - } - // Found the port! - return port, nil -} - -var flagValidators = make(map[FlagName]*flagValidator) - -type flagValidator struct { - flagName FlagName - apply func(ctx *configContext, flagValue string) - validate func(ctx *configContext, flagValue string) error -} - -func registerFlagValidator(v *flagValidator) *flagValidator { - flagValidators[v.flagName] = v - - // Fill in missing methods with defaults. - if v.apply == nil { - v.apply = func(*configContext, string) {} - } - if v.validate == nil { - v.validate = func(*configContext, string) error { - return nil - } - } - return v -} - -var _ Option = &genericOption{} - -type genericOption struct { - v *flagValidator - value string -} - -func (o *genericOption) FlagName() FlagName { - if o.v != nil { - return o.v.flagName - } - return "" -} - -func (o *genericOption) FlagValue() string { - if o.v != nil { - return o.value - } - return "" -} - -func (o *genericOption) apply(ctx *configContext) { - if o.v != nil && o.v.apply != nil { - o.v.apply(ctx, o.value) - } -} - -func (o *genericOption) validate(ctx *configContext) error { - if o.v != nil && o.v.validate != nil { - return o.v.validate(ctx, o.value) - } - return nil -} diff --git a/pkg/envoy/options_test.go b/pkg/envoy/options_test.go deleted file mode 100644 index 2d0eef6ab..000000000 --- a/pkg/envoy/options_test.go +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package envoy_test - -import ( - "testing" - "time" -) - -import ( - . "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/envoy" -) - -var testConfigPath = absPath("testdata/bootstrap.json") - -func TestNewOptions(t *testing.T) { - g := NewWithT(t) - - args := []string{"--config-path", testConfigPath, "--k2", "v2", "--k3"} - actuals, err := envoy.NewOptions(args...) - g.Expect(err).To(BeNil()) - g.Expect(actuals.Validate()).To(BeNil()) - - g.Expect(actuals.ToArgs()).To(Equal(args)) -} - -func TestNewOptionsWithoutConfigPathShouldFail(t *testing.T) { - g := NewWithT(t) - - args := []string{"--k2", "v2", "--k3"} - actuals, err := envoy.NewOptions(args...) - g.Expect(err).To(BeNil()) - g.Expect(actuals.Validate()).ToNot(BeNil()) -} - -func TestNewOptionsWithInvalidFlagShouldFail(t *testing.T) { - g := NewWithT(t) - - args := []string{"--config-path", testConfigPath, "--k1", "v1", "k2", "v2"} - _, err := envoy.NewOptions(args...) - g.Expect(err).ToNot(BeNil()) -} - -func TestNewOptionsWithDuplicateFlagShouldFail(t *testing.T) { - g := NewWithT(t) - - args := []string{"--config-path", testConfigPath, "--k1", "v1", "--k1", "v2"} - actuals, err := envoy.NewOptions(args...) - g.Expect(err).To(BeNil()) - g.Expect(actuals.Validate()).ToNot(BeNil()) -} - -func TestDuplicateOptions(t *testing.T) { - g := NewWithT(t) - - options := envoy.Options{envoy.ConfigYaml("{a:b}"), envoy.ConfigYaml("{a:b}")} - g.Expect(options.Validate()).ToNot(BeNil()) -} - -func TestGoodOptions(t *testing.T) { - cases := []struct { - name string - expectedArgs []string - options envoy.Options - }{ - { - name: "config-yaml", - expectedArgs: []string{"--config-yaml", "{a:b}"}, - options: envoy.Options{envoy.ConfigYaml("{a:b}")}, - }, - { - name: "log-level", - expectedArgs: []string{"--config-yaml", "{}", "--log-level", "info"}, - options: envoy.Options{envoy.ConfigYaml("{}"), envoy.LogLevelInfo}, - }, - { - name: "component-log-level", - expectedArgs: []string{"--config-yaml", "{}", "--component-log-level", "a:info,b:warning"}, - options: envoy.Options{envoy.ConfigYaml("{}"), envoy.ComponentLogLevels{ - envoy.ComponentLogLevel{ - Name: "a", - Level: envoy.LogLevelInfo, - }, - envoy.ComponentLogLevel{ - Name: "b", - Level: envoy.LogLevelWarning, - }, - }}, - }, - { - name: "local-address-ip-version", - expectedArgs: []string{"--config-yaml", "{}", "--local-address-ip-version", "v4"}, - options: envoy.Options{envoy.ConfigYaml("{}"), envoy.LocalAddressIPVersion(envoy.IPV4)}, - }, - { - name: "base-id", - expectedArgs: []string{"--config-yaml", "{}", "--base-id", "123"}, - options: envoy.Options{envoy.ConfigYaml("{}"), envoy.BaseID(123)}, - }, - { - name: "concurrency", - expectedArgs: []string{"--config-yaml", "{}", "--concurrency", "4"}, - options: envoy.Options{envoy.ConfigYaml("{}"), envoy.Concurrency(4)}, - }, - { - name: "disable-hot-restart:true", - expectedArgs: []string{"--config-yaml", "{}", "--disable-hot-restart"}, - options: envoy.Options{envoy.ConfigYaml("{}"), envoy.DisableHotRestart(true)}, - }, - { - name: "disable-hot-restart:false", - expectedArgs: []string{"--config-yaml", "{}"}, - options: envoy.Options{envoy.ConfigYaml("{}"), envoy.DisableHotRestart(false)}, - }, - { - name: "log-path", - expectedArgs: []string{"--config-yaml", "{}", "--log-path", "fake/path"}, - options: envoy.Options{envoy.ConfigYaml("{}"), envoy.LogPath("fake/path")}, - }, - { - name: "log-format", - expectedArgs: []string{"--config-yaml", "{}", "--log-format", "some format"}, - options: envoy.Options{envoy.ConfigYaml("{}"), envoy.LogFormat("some format")}, - }, - { - name: "restart-epoch", - expectedArgs: []string{"--config-yaml", "{}", "--restart-epoch", "123"}, - options: envoy.Options{envoy.ConfigYaml("{}"), envoy.Epoch(123)}, - }, - { - name: "service-cluster", - expectedArgs: []string{"--config-yaml", "{}", "--service-cluster", "fake-cluster"}, - options: envoy.Options{envoy.ConfigYaml("{}"), envoy.ServiceCluster("fake-cluster")}, - }, - { - name: "service-node", - expectedArgs: []string{"--config-yaml", "{}", "--service-node", "fake-node"}, - options: envoy.Options{envoy.ConfigYaml("{}"), envoy.ServiceNode("fake-node")}, - }, - { - name: "drain-time-s", - expectedArgs: []string{"--config-yaml", "{}", "--drain-time-s", "15"}, - options: envoy.Options{envoy.ConfigYaml("{}"), envoy.DrainDuration(15 * time.Second)}, - }, - { - name: "parent-shutdown-time-s", - expectedArgs: []string{"--config-yaml", "{}", "--parent-shutdown-time-s", "15"}, - options: envoy.Options{envoy.ConfigYaml("{}"), envoy.ParentShutdownDuration(15 * time.Second)}, - }, - } - - for _, c := range cases { - c := c - t.Run(c.name, func(t *testing.T) { - g := NewWithT(t) - g.Expect(c.options.Validate()).To(BeNil()) - g.Expect(c.options.ToArgs()).To(Equal(c.expectedArgs)) - }) - } -} - -func TestInvalidLogLevel(t *testing.T) { - g := NewWithT(t) - options := envoy.Options{envoy.ConfigYaml("{}"), envoy.LogLevel("bad")} - g.Expect(options.Validate()).ToNot(BeNil()) -} - -func TestInvalidComponentLogLevels(t *testing.T) { - g := NewWithT(t) - options := envoy.Options{envoy.ConfigYaml("{}"), envoy.ComponentLogLevels{ - envoy.ComponentLogLevel{ - Name: "a", - Level: envoy.LogLevel("bad"), - }, - }} - g.Expect(options.Validate()).ToNot(BeNil()) -} - -func TestInvalidLocalAddressIPVersion(t *testing.T) { - g := NewWithT(t) - options := envoy.Options{envoy.ConfigYaml("{}"), envoy.LocalAddressIPVersion("bad")} - g.Expect(options.Validate()).ToNot(BeNil()) -} - -func TestInvalidConfigPath(t *testing.T) { - g := NewWithT(t) - options := envoy.Options{envoy.ConfigPath("bad/file/path")} - g.Expect(options.Validate()).ToNot(BeNil()) -} - -func TestInvalidBaseID(t *testing.T) { - g := NewWithT(t) - actuals, err := envoy.NewOptions("--config-path", testConfigPath, "--base-id", "bad-int-value") - g.Expect(err).To(BeNil()) - g.Expect(actuals.Validate()).ToNot(BeNil()) -} - -func TestInvalidConcurrency(t *testing.T) { - g := NewWithT(t) - actuals, err := envoy.NewOptions("--config-path", testConfigPath, "--concurrency", "bad-int-value") - g.Expect(err).To(BeNil()) - g.Expect(actuals.Validate()).ToNot(BeNil()) -} - -func TestInvalidRestartEpoch(t *testing.T) { - g := NewWithT(t) - actuals, err := envoy.NewOptions("--config-path", testConfigPath, "--restart-epoch", "bad-int-value") - g.Expect(err).To(BeNil()) - g.Expect(actuals.Validate()).ToNot(BeNil()) -} - -func TestInvalidDrainDuration(t *testing.T) { - g := NewWithT(t) - actuals, err := envoy.NewOptions("--config-path", testConfigPath, "--drain-time-s", "bad-int-value") - g.Expect(err).To(BeNil()) - g.Expect(actuals.Validate()).ToNot(BeNil()) -} - -func TestInvalidParentShutdownDuration(t *testing.T) { - g := NewWithT(t) - actuals, err := envoy.NewOptions("--config-path", testConfigPath, "--parent-shutdown-time-s", "bad-int-value") - g.Expect(err).To(BeNil()) - g.Expect(actuals.Validate()).ToNot(BeNil()) -} - -func TestGenerateBaseID(t *testing.T) { - g := NewWithT(t) - - baseID := envoy.GenerateBaseID() - expected := []string{"--config-yaml", "{}", "--base-id", baseID.FlagValue()} - options := envoy.Options{envoy.ConfigYaml("{}"), baseID} - g.Expect(options.Validate()).To(BeNil()) - g.Expect(options.ToArgs()).To(Equal(expected)) -} - -func TestParseComponentLogLevels(t *testing.T) { - g := NewWithT(t) - expected := envoy.ComponentLogLevels{ - envoy.ComponentLogLevel{ - Name: "a", - Level: envoy.LogLevelInfo, - }, - envoy.ComponentLogLevel{ - Name: "b", - Level: envoy.LogLevelWarning, - }, - } - actual := envoy.ParseComponentLogLevels("a:info,b:warning") - g.Expect(actual).To(Equal(expected)) -} diff --git a/pkg/envoy/proxy.go b/pkg/envoy/proxy.go deleted file mode 100644 index 2d839fcf2..000000000 --- a/pkg/envoy/proxy.go +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package envoy - -import ( - "fmt" - "os" - "os/exec" - "strings" - "syscall" -) - -import ( - "google.golang.org/protobuf/types/known/durationpb" - "istio.io/pkg/env" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/util/network" -) - -type envoy struct { - ProxyConfig - extraArgs []string -} - -// Envoy binary flags -type ProxyConfig struct { - LogLevel string - ComponentLogLevel string - NodeIPs []string - Sidecar bool - LogAsJSON bool - // TODO: outlier log path configuration belongs to mesh ProxyConfig - OutlierLogPath string - - BinaryPath string - ConfigPath string - ConfigCleanup bool - AdminPort int32 - DrainDuration *durationpb.Duration - ParentShutdownDuration *durationpb.Duration - Concurrency int32 - - // For unit testing, in combination with NoEnvoy prevents agent.Run from blocking - TestOnly bool - AgentIsRoot bool -} - -// NewProxy creates an instance of the proxy control commands -func NewProxy(cfg ProxyConfig) Proxy { - // inject tracing flag for higher levels - var args []string - logLevel, componentLogs := splitComponentLog(cfg.LogLevel) - if logLevel != "" { - args = append(args, "-l", logLevel) - } - if len(componentLogs) > 0 { - args = append(args, "--component-log-level", strings.Join(componentLogs, ",")) - } else if cfg.ComponentLogLevel != "" { - // Use the old setting if we don't set any component log levels in LogLevel - args = append(args, "--component-log-level", cfg.ComponentLogLevel) - } - - return &envoy{ - ProxyConfig: cfg, - extraArgs: args, - } -} - -// splitComponentLog breaks down an argument string into a log level (ie "info") and component log levels (ie "misc:error"). -// This allows using a single log level API, with the same semantics as Istio's logging, to configure Envoy which -// has two different settings -func splitComponentLog(level string) (string, []string) { - levels := strings.Split(level, ",") - var logLevel string - var componentLogs []string - for _, sl := range levels { - spl := strings.Split(sl, ":") - if len(spl) == 1 { - logLevel = spl[0] - } else if len(spl) == 2 { - componentLogs = append(componentLogs, sl) - } else { - log.Warnf("dropping invalid log level: %v", sl) - } - } - return logLevel, componentLogs -} - -func (e *envoy) Drain() error { - adminPort := uint32(e.AdminPort) - - err := DrainListeners(adminPort, e.Sidecar) - if err != nil { - log.Infof("failed draining listeners for Envoy on port %d: %v", adminPort, err) - } - return err -} - -func (e *envoy) UpdateConfig(config []byte) error { - return os.WriteFile(e.ConfigPath, config, 0o666) -} - -func (e *envoy) args(fname string, epoch int, bootstrapConfig string) []string { - proxyLocalAddressType := "v4" - if network.AllIPv6(e.NodeIPs) { - proxyLocalAddressType = "v6" - } - startupArgs := []string{ - "-c", fname, - "--restart-epoch", fmt.Sprint(epoch), - "--drain-time-s", fmt.Sprint(int(e.DrainDuration.AsDuration().Seconds())), - "--drain-strategy", "immediate", // Clients are notified as soon as the drain process starts. - "--parent-shutdown-time-s", fmt.Sprint(int(e.ParentShutdownDuration.AsDuration().Seconds())), - "--local-address-ip-version", proxyLocalAddressType, - // Reduce default flush interval from 10s to 1s. The access log buffer size is 64k and each log is ~256 bytes - // This means access logs will be written once we have ~250 requests, or ever 1s, which ever comes first. - // Reducing this to 1s optimizes for UX while retaining performance. - // At low QPS access logs are unlikely a bottleneck, and these users will now see logs after 1s rather than 10s. - // At high QPS (>250 QPS) we will log the same amount as we will log due to exceeding buffer size, rather - // than the flush interval. - "--file-flush-interval-msec", "1000", - "--disable-hot-restart", // We don't use it, so disable it to simplify Envoy's logic - } - if e.ProxyConfig.LogAsJSON { - startupArgs = append(startupArgs, - "--log-format", - `{"level":"%l","time":"%Y-%m-%dT%T.%fZ","scope":"envoy %n","msg":"%j"}`, - ) - } else { - // format is like `2020-04-07T16:52:30.471425Z info envoy config ...message.. - // this matches Istio log format - startupArgs = append(startupArgs, "--log-format", "%Y-%m-%dT%T.%fZ\t%l\tenvoy %n\t%v") - } - - startupArgs = append(startupArgs, e.extraArgs...) - - if bootstrapConfig != "" { - bytes, err := os.ReadFile(bootstrapConfig) - if err != nil { - log.Warnf("Failed to read bootstrap override %s, %v", bootstrapConfig, err) - } else { - startupArgs = append(startupArgs, "--config-yaml", string(bytes)) - } - } - - if e.Concurrency > 0 { - startupArgs = append(startupArgs, "--concurrency", fmt.Sprint(e.Concurrency)) - } - - return startupArgs -} - -var istioBootstrapOverrideVar = env.RegisterStringVar("ISTIO_BOOTSTRAP_OVERRIDE", "", "") - -func (e *envoy) Run(epoch int, abort <-chan error) error { - // spin up a new Envoy process - args := e.args(e.ConfigPath, epoch, istioBootstrapOverrideVar.Get()) - log.Infof("Envoy command: %v", args) - - /* #nosec */ - cmd := exec.Command(e.BinaryPath, args...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if e.AgentIsRoot { - cmd.SysProcAttr = &syscall.SysProcAttr{} - cmd.SysProcAttr.Credential = &syscall.Credential{ - Uid: 1337, - Gid: 1337, - } - } - - if err := cmd.Start(); err != nil { - return err - } - done := make(chan error, 1) - go func() { - done <- cmd.Wait() - }() - - select { - case err := <-abort: - log.Warnf("Aborting epoch %d", epoch) - if errKill := cmd.Process.Kill(); errKill != nil { - log.Warnf("killing epoch %d caused an error %v", epoch, errKill) - } - return err - case err := <-done: - return err - } -} - -func (e *envoy) Cleanup(epoch int) { - if e.ConfigCleanup { - if err := os.Remove(e.ConfigPath); err != nil { - log.Warnf("Failed to delete config file %s for %d, %v", e.ConfigPath, epoch, err) - } - } -} diff --git a/pkg/envoy/proxy_test.go b/pkg/envoy/proxy_test.go deleted file mode 100644 index 6d45f52e8..000000000 --- a/pkg/envoy/proxy_test.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package envoy - -import ( - "reflect" - "testing" -) - -import ( - "google.golang.org/protobuf/types/known/wrapperspb" - meshconfig "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" -) - -func TestEnvoyArgs(t *testing.T) { - proxyConfig := (*model.NodeMetaProxyConfig)(mesh.DefaultProxyConfig()) - proxyConfig.ClusterName = &meshconfig.ProxyConfig_ServiceCluster{ServiceCluster: "my-cluster"} - proxyConfig.Concurrency = &wrapperspb.Int32Value{Value: 8} - - cfg := ProxyConfig{ - LogLevel: "trace", - ComponentLogLevel: "misc:error", - NodeIPs: []string{"10.75.2.9", "192.168.11.18"}, - BinaryPath: proxyConfig.BinaryPath, - ConfigPath: proxyConfig.ConfigPath, - ConfigCleanup: true, - AdminPort: proxyConfig.ProxyAdminPort, - DrainDuration: proxyConfig.DrainDuration, - ParentShutdownDuration: proxyConfig.ParentShutdownDuration, - Concurrency: 8, - } - - test := &envoy{ - ProxyConfig: cfg, - extraArgs: []string{"-l", "trace", "--component-log-level", "misc:error"}, - } - - testProxy := NewProxy(cfg) - if !reflect.DeepEqual(testProxy, test) { - t.Errorf("unexpected struct got\n%v\nwant\n%v", testProxy, test) - } - - got := test.args("test.json", 5, "testdata/bootstrap.json") - want := []string{ - "-c", "test.json", - "--restart-epoch", "5", - "--drain-time-s", "45", - "--drain-strategy", "immediate", - "--parent-shutdown-time-s", "60", - "--local-address-ip-version", "v4", - "--file-flush-interval-msec", "1000", - "--disable-hot-restart", - "--log-format", "%Y-%m-%dT%T.%fZ\t%l\tenvoy %n\t%v", - "-l", "trace", - "--component-log-level", "misc:error", - "--config-yaml", `{"key": "value"}`, - "--concurrency", "8", - } - if !reflect.DeepEqual(got, want) { - t.Errorf("envoyArgs() => got:\n%v,\nwant:\n%v", got, want) - } -} - -func TestSplitComponentLog(t *testing.T) { - cases := []struct { - input string - log string - components []string - }{ - {"info", "info", nil}, - // A bit odd, but istio logging behaves this way so might as well be consistent - {"info,warn", "warn", nil}, - {"info,misc:warn", "info", []string{"misc:warn"}}, - {"misc:warn,info", "info", []string{"misc:warn"}}, - {"misc:warn,info,upstream:debug", "info", []string{"misc:warn", "upstream:debug"}}, - } - for _, tt := range cases { - t.Run(tt.input, func(t *testing.T) { - l, c := splitComponentLog(tt.input) - if l != tt.log { - t.Errorf("expected log level %v, got %v", tt.log, l) - } - if !reflect.DeepEqual(c, tt.components) { - t.Errorf("expected component log level %v, got %v", tt.components, c) - } - }) - } -} diff --git a/pkg/envoy/testdata/bootstrap.json b/pkg/envoy/testdata/bootstrap.json deleted file mode 100644 index 9082d25ef..000000000 --- a/pkg/envoy/testdata/bootstrap.json +++ /dev/null @@ -1 +0,0 @@ -{"key": "value"} \ No newline at end of file diff --git a/pkg/envoy/testdata/envoy_bootstrap_v2.tmpl.json b/pkg/envoy/testdata/envoy_bootstrap_v2.tmpl.json deleted file mode 100644 index 4dd417516..000000000 --- a/pkg/envoy/testdata/envoy_bootstrap_v2.tmpl.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "node": { - "id": "{{ .nodeID }}", - "cluster": "{{ .cluster }}" - }, - "admin": { - "access_log_path": "/dev/null", - "address": { - "socket_address": { - "address": "{{ .localhost }}", - "port_value": {{ .adminPort }} - } - } - }, - "static_resources": { - "clusters": [ - { - "name": "prometheus_stats", - "type": "STATIC", - "connect_timeout": "0.250s", - "lb_policy": "ROUND_ROBIN", - "load_assignment": { - "cluster_name": "prometheus_stats", - "endpoints": [{ - "lb_endpoints": [{ - "endpoint": { - "address":{ - "socket_address": { - "address": "{{ .localhost }}", - "port_value": {{ .adminPort }} - } - } - } - }] - }] - } - } - ], - "listeners":[ - { - "address": { - "socket_address": { - "protocol": "TCP", - "address": "0.0.0.0", - "port_value": {{ .listenerPort }} - } - }, - "filter_chains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "codec_type": "AUTO", - "stat_prefix": "stats", - "route_config": { - "virtual_hosts": [ - { - "name": "backend", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/stats/prometheus" - }, - "route": { - "cluster": "prometheus_stats" - } - } - ] - } - ] - }, - "http_filters": [{ - "name": "envoy.filters.http.router", - "typed_config": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - }] - } - } - ] - } - ] - } - ] - } -} diff --git a/pkg/errdict/errdict.go b/pkg/errdict/errdict.go deleted file mode 100644 index 5c564ea2b..000000000 --- a/pkg/errdict/errdict.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package errdict - -import ( - "strings" -) - -import ( - "istio.io/pkg/structured" -) - -const ( - LikelyCauseFirstPrefix = "The likely cause is " - LikeyCauseSecondPrefix = "Another possible cause could be " -) - -// General boilerplate. -const ( - // Boilerplate messages applicable all over the code base. - - // Action - ActionIfErrPersistsCheckBugList = "If this error persists, " + ActionCheckBugList - ActionIfErrSureCorrectConfigContactSupport = "If you are sure your configuration is correct, " + ActionCheckBugList - ActionCheckBugList = "see https://istio.io/latest/about/bugs for possible solutions." - - // LikelyCause - LikelyCauseAPIServer = "a problem with the Kubernetes API server" - LikelyCauseConfiguration = "an incorrect or badly formatted configuration" - LikelyCauseSoftwareBug = "an issue with the Istio code" - - // Is the error permanent? - TransiencePermanentForInstall = "If the error occurred immediately after installation, it is likely permanent." -) - -// Operator specific -const ( - // Impact - OperatorImpactFailedToGetObjectFromAPIServer = "If the error is transient, the impact is low. If permanent, " + - "updates for the objects cannot be processed leading to an out of sync control plane." - OperatorImpactNoUpdates = "In this error state, changes to the IstioOperator CR will not result in the Istio " + - "control plane being updated." -) - -var ( - OperatorFailedToGetObjectFromAPIServer = &structured.Error{ - MoreInfo: "Failed to get an object from the Kubernetes API server, because of a transient " + - "API server error or the object no longer exists.", - Impact: OperatorImpactFailedToGetObjectFromAPIServer, - LikelyCause: formatCauses(LikelyCauseAPIServer) + " " + TransiencePermanentForInstall, - Action: "If the error is because the object was deleted, it can be safely ignored. Otherwise, if the " + - "error persists, " + ActionCheckBugList, - } - OperatorFailedToGetObjectInCallback = &structured.Error{ - MoreInfo: "A Kubernetes update for an IstioOperator resource did not " + - "contain an IstioOperator object.", - Impact: OperatorImpactNoUpdates, - LikelyCause: formatCauses(LikelyCauseAPIServer) + " " + TransiencePermanentForInstall, - Action: ActionIfErrPersistsCheckBugList, - } - OperatorFailedToAddFinalizer = &structured.Error{ - MoreInfo: "A finalizer could not be added to the IstioOperator resource. The " + - "controller uses the finalizer to temporarily prevent the resource from being deleted while the Istio " + - "control plane is being deleted.", - Impact: "When the IstioOperator resource is deleted, the Istio control plane may " + - "not be fully removed.", - LikelyCause: formatCauses(LikelyCauseAPIServer), - Action: "Delete and re-add the IstioOperator resource. " + ActionIfErrPersistsCheckBugList, - } - OperatorFailedToRemoveFinalizer = &structured.Error{ - MoreInfo: "The finalizer set by the operator controller could not be removed " + - "when the IstioOperator resource was deleted.", - Impact: "The IstioOperator resource can not be removed by the operator controller.", - LikelyCause: formatCauses(LikelyCauseAPIServer), - Action: "Remove the IstioOperator resource finalizer manually using kubectl edit.", - } - OperatorFailedToMergeUserIOP = &structured.Error{ - MoreInfo: "The values in the selected spec.profile could not be merged with " + - "the user IstioOperator resource.", - Impact: "The operator controller cannot create and act upon the user " + - "defined IstioOperator resource. The Istio control plane will not be installed or updated.", - LikelyCause: formatCauses(LikelyCauseConfiguration, LikelyCauseSoftwareBug), - Action: "Check that the IstioOperator resource has the correct syntax. " + - ActionIfErrSureCorrectConfigContactSupport, - } - OperatorFailedToConfigure = &structured.Error{ - MoreInfo: "the IstioOperator Resource could not be applied on the cluster " + - "because of incompatible Kubernetes settings", - Impact: OperatorImpactNoUpdates, - LikelyCause: formatCauses(LikelyCauseConfiguration), - Action: "Esnure that IstioOperator config is compatible with current Kubernetes version." + - ActionIfErrSureCorrectConfigContactSupport, - } -) - -func fixFormat(s string) string { - s = strings.TrimSpace(s) - return strings.TrimSuffix(s, ".") -} - -func formatCauses(causes ...string) string { - if len(causes) == 0 { - return "" - } - out := LikelyCauseFirstPrefix + fixFormat(causes[0]) + "." - for _, c := range causes[1:] { - out += LikeyCauseSecondPrefix + fixFormat(c) + "." - } - return out -} diff --git a/pkg/file/file.go b/pkg/file/file.go deleted file mode 100644 index 9e3d254f2..000000000 --- a/pkg/file/file.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package file - -import ( - "fmt" - "os" - "path/filepath" -) - -// Copies file by reading the file then writing atomically into the target directory -func AtomicCopy(srcFilepath, targetDir, targetFilename string) error { - info, err := os.Stat(srcFilepath) - if err != nil { - return err - } - - input, err := os.ReadFile(srcFilepath) - if err != nil { - return err - } - - return AtomicWrite(filepath.Join(targetDir, targetFilename), input, info.Mode()) -} - -func Copy(srcFilepath, targetDir, targetFilename string) error { - info, err := os.Stat(srcFilepath) - if err != nil { - return err - } - - input, err := os.ReadFile(srcFilepath) - if err != nil { - return err - } - - return os.WriteFile(filepath.Join(targetDir, targetFilename), input, info.Mode()) -} - -// Write atomically by writing to a temporary file in the same directory then renaming -func AtomicWrite(path string, data []byte, mode os.FileMode) (err error) { - tmpFile, err := os.CreateTemp(filepath.Dir(path), filepath.Base(path)+".tmp.") - if err != nil { - return - } - defer func() { - if Exists(tmpFile.Name()) { - if rmErr := os.Remove(tmpFile.Name()); rmErr != nil { - if err != nil { - err = fmt.Errorf("%s: %w", rmErr.Error(), err) - } else { - err = rmErr - } - } - } - }() - - if err = os.Chmod(tmpFile.Name(), mode); err != nil { - return - } - - _, err = tmpFile.Write(data) - if err != nil { - if closeErr := tmpFile.Close(); closeErr != nil { - err = fmt.Errorf("%s: %w", closeErr.Error(), err) - } - return - } - if err = tmpFile.Close(); err != nil { - return - } - - err = os.Rename(tmpFile.Name(), path) - return -} - -func Exists(name string) bool { - _, err := os.Stat(name) - return err == nil -} - -const ( - // PrivateFileMode grants owner to read/write a file. - PrivateFileMode = 0o600 -) - -// IsDirWriteable checks if dir is writable by writing and removing a file -// to dir. It returns nil if dir is writable. -// Inspired by etcd fileutil. -func IsDirWriteable(dir string) error { - f := filepath.Join(dir, ".touch") - if err := os.WriteFile(f, []byte(""), PrivateFileMode); err != nil { - return err - } - return os.Remove(f) -} - -// DirEquals check if two directories are referring to the same directory -func DirEquals(a, b string) (bool, error) { - aa, err := filepath.Abs(a) - if err != nil { - return false, err - } - bb, err := filepath.Abs(b) - if err != nil { - return false, err - } - return aa == bb, nil -} diff --git a/pixiu/pkg/filter/accesslog/access_log.go b/pkg/filter/accesslog/access_log.go similarity index 95% rename from pixiu/pkg/filter/accesslog/access_log.go rename to pkg/filter/accesslog/access_log.go index de0801eab..b475bf69d 100644 --- a/pixiu/pkg/filter/accesslog/access_log.go +++ b/pkg/filter/accesslog/access_log.go @@ -27,9 +27,9 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" ) const ( diff --git a/pixiu/pkg/filter/accesslog/access_log_test.go b/pkg/filter/accesslog/access_log_test.go similarity index 91% rename from pixiu/pkg/filter/accesslog/access_log_test.go rename to pkg/filter/accesslog/access_log_test.go index 3310dc9ce..6d95b2c26 100644 --- a/pixiu/pkg/filter/accesslog/access_log_test.go +++ b/pkg/filter/accesslog/access_log_test.go @@ -30,10 +30,10 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/mock" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/client" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/context/mock" + "github.com/apache/dubbo-go-pixiu/pkg/logger" ) func TestAccessLog_Write_to_file(t *testing.T) { diff --git a/pkg/filter/accesslog/log.go b/pkg/filter/accesslog/log.go new file mode 100644 index 000000000..e7b8f1f86 --- /dev/null +++ b/pkg/filter/accesslog/log.go @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 accesslog + +import ( + "os" + "path/filepath" + "time" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +// access log config, enable default value true, outputpath default value console +// AccessLogConfig access log will out put into console +type AccessLogConfig struct { + OutPutPath string `yaml:"outPutPath" json:"outPutPath" mapstructure:"outPutPath" default:"console"` +} + +// AccessLogWriter access log chan +type AccessLogWriter struct { + AccessLogDataChan chan AccessLogData +} + +// AccessLogData access log data +type AccessLogData struct { + AccessLogMsg string + AccessLogConfig AccessLogConfig +} + +// Writer writer msg into chan +func (alw *AccessLogWriter) Writer(accessLogData AccessLogData) { + select { + case alw.AccessLogDataChan <- accessLogData: + return + default: + logger.Warn("the channel is full and the access logIntoChannel data will be dropped") + return + } +} + +// Write write log into out put path +func (alw *AccessLogWriter) Write() { + go func() { + for accessLogData := range alw.AccessLogDataChan { + alw.writeLogToFile(accessLogData) + } + }() +} + +// write log to file or console +func (alw *AccessLogWriter) writeLogToFile(ald AccessLogData) { + alc := ald.AccessLogConfig + alm := ald.AccessLogMsg + if len(alc.OutPutPath) == 0 || alc.OutPutPath == constant.Console { + logger.Info(alm) + return + } + _ = WriteToFile(alm, alc.OutPutPath) +} + +// WriteToFile write message to access log file +func WriteToFile(accessLogMsg string, filePath string) error { + pd := filepath.Dir(filePath) + if _, err := os.Stat(pd); err != nil { + if os.IsExist(err) { + logger.Warnf("can not open log dir: %s, %v", filePath, err) + } + err = os.MkdirAll(pd, os.ModePerm) + if err != nil { + logger.Warnf("can not create log dir: %s, %v", filePath, err) + return err + } + } + logFile, err := os.OpenFile(filePath, os.O_CREATE|os.O_APPEND|os.O_RDWR, constant.LogFileMode) + if err != nil { + logger.Warnf("can not open the access log file: %s, %v", filePath, err) + return err + } + now := time.Now().Format(constant.FileDateFormat) + fileInfo, err := logFile.Stat() + if err != nil { + logger.Warnf("can not get the info of access log file: %s, %v", filePath, err) + return err + } + last := fileInfo.ModTime().Format(constant.FileDateFormat) + + // this is confused. + // for example, if the last = '2020-03-04' + // and today is '2020-03-05' + // we will create one new file to log access data + // By this way, we can split the access log based on days. + if now != last { + err = os.Rename(fileInfo.Name(), fileInfo.Name()+"."+now) + if err != nil { + logger.Warnf("can not rename access log file: %s, %v", fileInfo.Name(), err) + return err + } + logFile, err = os.OpenFile(fileInfo.Name(), os.O_CREATE|os.O_APPEND|os.O_RDWR, constant.LogFileMode) + if err != nil { + logger.Warnf("can not open access log file: %s, %v", fileInfo.Name(), err) + return err + } + } + _, err = logFile.WriteString(accessLogMsg + "\n") + if err != nil { + logger.Warnf("can not write to access log file: %s, v%", fileInfo.Name(), err) + return err + } + return nil +} diff --git a/pixiu/pkg/filter/auth/jwt/config.go b/pkg/filter/auth/jwt/config.go similarity index 100% rename from pixiu/pkg/filter/auth/jwt/config.go rename to pkg/filter/auth/jwt/config.go diff --git a/pkg/filter/auth/jwt/jwt.go b/pkg/filter/auth/jwt/jwt.go new file mode 100644 index 000000000..9179a313d --- /dev/null +++ b/pkg/filter/auth/jwt/jwt.go @@ -0,0 +1,225 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 jwt + +import ( + "encoding/json" + "fmt" + stdHttp "net/http" + "strings" + "time" +) + +import ( + "github.com/MicahParks/keyfunc" + jwt4 "github.com/golang-jwt/jwt/v4" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +const ( + Kind = constant.HTTPAuthJwtFilter +) + +func init() { + filter.RegisterHttpFilter(&Plugin{}) +} + +type ( + // Plugin is http filter plugin. + Plugin struct { + } + + // FilterFactory is http filter instance + FilterFactory struct { + cfg *Config + errMsg []byte + providerJwks map[string]Provider + } + + Filter struct { + cfg *Config + errMsg []byte + providerJwks map[string]Provider + } + + // Config describe the config of FilterFactory + Config struct { + ErrMsg string `yaml:"err_msg" json:"err_msg" mapstructure:"err_msg"` + Rules []Rules `yaml:"rules" json:"rules" mapstructure:"rules"` + Providers []Providers `yaml:"providers" json:"providers" mapstructure:"providers"` + } +) + +func (p Plugin) Kind() string { + return Kind +} + +func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { + return &FilterFactory{cfg: &Config{}, providerJwks: map[string]Provider{}}, nil +} + +func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error { + f := &Filter{cfg: factory.cfg, errMsg: factory.errMsg, providerJwks: factory.providerJwks} + chain.AppendDecodeFilters(f) + return nil +} + +func (f *Filter) Decode(ctx *http.HttpContext) filter.FilterStatus { + + path := ctx.Request.RequestURI + + router := false + + for _, rule := range f.cfg.Rules { + if strings.HasPrefix(path, rule.Match.Prefix) { + router = true + if f.validAny(rule, ctx) || f.validAll(rule, ctx) { + router = false + break + } + } + } + + if router { + ctx.SendLocalReply(stdHttp.StatusUnauthorized, f.errMsg) + return filter.Stop + } + return filter.Continue +} + +// validAny route single provider verification +func (f *Filter) validAny(rule Rules, ctx *http.HttpContext) bool { + + providerName := rule.Requires.RequiresAny.ProviderName + + if provider, ok := f.providerJwks[providerName]; ok { + ctx.Request.Header.Set(provider.forwardPayloadHeader, provider.issuer) + if key := ctx.Request.Header.Get(provider.headers.Name); key != "" { + return checkToken(key, provider.headers.ValuePrefix, providerName, provider) + } + } + + return false +} + +// validAll route multiple provider verification +func (f *Filter) validAll(rule Rules, ctx *http.HttpContext) bool { + + for _, requirement := range rule.Requires.RequiresAll { + if provider, ok := f.providerJwks[requirement.ProviderName]; ok { + ctx.Request.Header.Set(provider.forwardPayloadHeader, provider.issuer) + if key := ctx.Request.Header.Get(provider.headers.Name); key != "" { + if checkToken(key, provider.headers.ValuePrefix, requirement.ProviderName, provider) { + return true + } + } + } + } + + return false +} + +func (factory *FilterFactory) Apply() error { + + if len(factory.cfg.Providers) == 0 { + return fmt.Errorf("providers is null") + } + + for _, provider := range factory.cfg.Providers { + + if provider.Local != nil { + jwksJSON := json.RawMessage(provider.Local.InlineString) + jwks, err := keyfunc.NewJSON(jwksJSON) + if err != nil { + logger.Warnf("failed to create JWKs from JSON. provider:%s Error: %s", provider.Name, err.Error()) + } else { + provider.FromHeaders.setDefault() + factory.providerJwks[provider.Name] = Provider{jwk: jwks, headers: provider.FromHeaders, + issuer: provider.Issuer, forwardPayloadHeader: provider.ForwardPayloadHeader} + continue + } + } + + if provider.Remote != nil { + uri := provider.Remote.HttpURI + timeout, err := time.ParseDuration(uri.TimeOut) + if err != nil { + logger.Warnf("jwt provides timeout parse fail: %s", err.Error()) + continue + } + + options := keyfunc.Options{RefreshTimeout: timeout} + jwks, err := keyfunc.Get(uri.Uri, options) + if err != nil { + logger.Warnf("failed to create JWKs from resource at the given URL. provider:%s Error: %s", provider.Name, err.Error()) + } else { + provider.FromHeaders.setDefault() + factory.providerJwks[provider.Name] = Provider{jwk: jwks, headers: provider.FromHeaders, + issuer: provider.Issuer, forwardPayloadHeader: provider.ForwardPayloadHeader} + } + } + } + + if len(factory.providerJwks) == 0 { + return fmt.Errorf("providers is null") + } + + if factory.cfg.ErrMsg == "" { + factory.cfg.ErrMsg = "token invalid" + } + + errMsg, _ := json.Marshal(http.ErrResponse{Message: factory.cfg.ErrMsg}) + factory.errMsg = errMsg + + return nil +} + +func (h *FromHeaders) setDefault() { + if h.Name == "" { + h.Name = "Authorization" + } + + if h.ValuePrefix == "" { + h.ValuePrefix = "Bearer " + } +} + +func checkToken(value, prefix, providerName string, provider Provider) bool { + if !strings.HasPrefix(value, prefix) { + logger.Warn("header value prefix mismatch provider:", providerName) + return false + } + + token, err := jwt4.Parse(value[len(prefix):], provider.jwk.Keyfunc) + if err != nil { + logger.Warnf("failed to parse JWKs from JSON. provider:%s Error: %s", providerName, err.Error()) + return false + } + + return token.Valid +} + +func (factory *FilterFactory) Config() interface{} { + return factory.cfg +} diff --git a/pkg/filter/authority/authority.go b/pkg/filter/authority/authority.go new file mode 100644 index 000000000..dbb977271 --- /dev/null +++ b/pkg/filter/authority/authority.go @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 authority + +import ( + nh "net/http" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" +) + +const ( + Kind = constant.HTTPAuthorityFilter +) + +func init() { + filter.RegisterHttpFilter(&Plugin{}) +} + +type ( + // AuthorityPlugin is http filter plugin. + Plugin struct { + } + // FilterFactory is http filter instance + FilterFactory struct { + cfg *AuthorityConfiguration + } + Filter struct { + cfg *AuthorityConfiguration + } +) + +func (p *Plugin) Kind() string { + return Kind +} + +func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { + return &FilterFactory{cfg: &AuthorityConfiguration{}}, nil +} + +func (factory *FilterFactory) Config() interface{} { + return factory.cfg +} + +func (factory *FilterFactory) Apply() error { + return nil +} + +func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error { + f := &Filter{cfg: factory.cfg} + chain.AppendDecodeFilters(f) + return nil +} + +func (f *Filter) Decode(c *http.HttpContext) filter.FilterStatus { + for _, r := range f.cfg.Rules { + item := c.GetClientIP() + if r.Limit == App { + item = c.GetApplicationName() + } + + result := passCheck(item, r) + if !result { + c.SendLocalReply(nh.StatusForbidden, constant.Default403Body) + return filter.Stop + } + } + return filter.Continue +} + +func passCheck(item string, rule AuthorityRule) bool { + result := false + for _, it := range rule.Items { + if it == item { + result = true + break + } + } + + if (rule.Strategy == Blacklist && result) || (rule.Strategy == Whitelist && !result) { + return false + } + + return true +} diff --git a/pixiu/pkg/filter/authority/authority_test.go b/pkg/filter/authority/authority_test.go similarity index 89% rename from pixiu/pkg/filter/authority/authority_test.go rename to pkg/filter/authority/authority_test.go index 177ae6bbf..f92675738 100644 --- a/pixiu/pkg/filter/authority/authority_test.go +++ b/pkg/filter/authority/authority_test.go @@ -27,9 +27,9 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/yaml" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/mock" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/common/yaml" + "github.com/apache/dubbo-go-pixiu/pkg/context/mock" ) func TestAuth(t *testing.T) { diff --git a/pixiu/pkg/filter/authority/config.go b/pkg/filter/authority/config.go similarity index 100% rename from pixiu/pkg/filter/authority/config.go rename to pkg/filter/authority/config.go diff --git a/pixiu/pkg/filter/cors/cors.go b/pkg/filter/cors/cors.go similarity index 92% rename from pixiu/pkg/filter/cors/cors.go rename to pkg/filter/cors/cors.go index b76b87159..5191e8e4c 100644 --- a/pixiu/pkg/filter/cors/cors.go +++ b/pkg/filter/cors/cors.go @@ -22,10 +22,9 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pkg/http/headers" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" ) const ( @@ -85,7 +84,7 @@ func (f *Filter) Decode(ctx *http.HttpContext) filter.FilterStatus { if c == nil { return filter.Continue } - if ctx.GetHeader(headers.Origin) == "" { + if ctx.GetHeader(constant.Origin) == "" { // not a cors request return filter.Continue } diff --git a/pixiu/pkg/filter/csrf/csrf.go b/pkg/filter/csrf/csrf.go similarity index 94% rename from pixiu/pkg/filter/csrf/csrf.go rename to pkg/filter/csrf/csrf.go index 0a06be560..c4b615b55 100644 --- a/pixiu/pkg/filter/csrf/csrf.go +++ b/pkg/filter/csrf/csrf.go @@ -25,9 +25,9 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" ) const ( diff --git a/pixiu/pkg/filter/event/event.go b/pkg/filter/event/event.go similarity index 87% rename from pixiu/pkg/filter/event/event.go rename to pkg/filter/event/event.go index 60f9f6e41..c6516e77d 100644 --- a/pixiu/pkg/filter/event/event.go +++ b/pkg/filter/event/event.go @@ -23,12 +23,12 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/client/mq" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/client" + "github.com/apache/dubbo-go-pixiu/pkg/client/mq" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" ) const ( diff --git a/pixiu/pkg/filter/failinject/config.go b/pkg/filter/failinject/config.go similarity index 100% rename from pixiu/pkg/filter/failinject/config.go rename to pkg/filter/failinject/config.go diff --git a/pkg/filter/failinject/filter.go b/pkg/filter/failinject/filter.go new file mode 100644 index 000000000..865bd4139 --- /dev/null +++ b/pkg/filter/failinject/filter.go @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 failinject + +import ( + "math/rand" + "time" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + contextHttp "github.com/apache/dubbo-go-pixiu/pkg/context/http" +) + +const ( + Kind = constant.HTTPFailInjectFilter +) + +func init() { + filter.RegisterHttpFilter(&Plugin{}) +} + +type ( + Plugin struct { + } + + FilterFactory struct { + cfg *Config + } + Filter struct { + cfg *Config + } +) + +func (factory *FilterFactory) Apply() error { + return nil +} + +func (factory *FilterFactory) Config() interface{} { + return factory.cfg +} + +func (factory *FilterFactory) PrepareFilterChain(ctx *contextHttp.HttpContext, chain filter.FilterChain) error { + f := Filter{ + cfg: factory.cfg, + } + chain.AppendDecodeFilters(f) + return nil +} + +func (p *Plugin) Kind() string { + return Kind +} + +func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { + return &FilterFactory{ + cfg: &Config{ + Rules: make(map[URI]*Rule), + }, + }, nil +} + +func (f Filter) Decode(ctx *contextHttp.HttpContext) filter.FilterStatus { + uriCfg := &Rule{} + if v, ok := f.cfg.Rules[URI(ctx.Request.RequestURI)]; ok { + uriCfg = v + } + if uriCfg == nil { + return filter.Continue + } + + matched := f.match(uriCfg) + if !matched { + return filter.Continue + } + + if uriCfg.Type == TypeAbort { + ctx.SendLocalReply(uriCfg.StatusCode, []byte(uriCfg.Body)) + return filter.Stop + } + + if uriCfg.Type == TypeDelay { + time.Sleep(uriCfg.Delay) + return filter.Stop + } + + return filter.Continue +} + +// match determines whether a given request hits the error injection based on the rule's trigger type. +// +// - If the rule is nil, the request doesn't hit the error injection. +// - If the rule's trigger type is "Always", the request always hits the error injection. +// - If the rule's trigger type is "Percentage", the request hits the error injection based on a specified percentage chance. +// - If the rule's trigger type is "Random", the request hits the error injection based on a random chance. +// +// Returns: +// - true if the request hits the error injection +// - false otherwise +func (f Filter) match(rule *Rule) (matched bool) { + if rule == nil { + return false + } + if rule.TriggerType == TriggerTypeAlways { + return true + } + if rule.TriggerType == TriggerTypePercentage { + return percentage(rule.Odds) + } + if rule.TriggerType == TriggerTypeRandom { + return random() + } + return false +} + +// random if the current request is matched +func random() bool { + return (rand.Intn(1000)+1)%2 == 0 +} + +// percentage checks if the current request matches based on a given odds percentage. +// The function returns true with a probability equal to the odds percentage. +// +// Parameters: +// - odds: Percentage chance (0-100) of the function returning true. +// +// Returns: +// - true if a generated random number between 1 and 100 (inclusive) is less than or equal to the odds. +// - false otherwise. +func percentage(odds int) bool { + if odds <= 0 { + return false + } + // generate rand number in 1-100 + num := rand.Intn(100) + 1 + return num <= odds +} diff --git a/pkg/filter/header/header.go b/pkg/filter/header/header.go new file mode 100644 index 000000000..ffbe38ed0 --- /dev/null +++ b/pkg/filter/header/header.go @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 header + +import ( + "strings" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" +) + +const ( + Kind = constant.HTTPHeaderFilter +) + +func init() { + filter.RegisterHttpFilter(&Plugin{}) +} + +type ( + // Plugin is http filter plugin. + Plugin struct { + } + // FilterFactory is http filter instance + FilterFactory struct { + cfg *Config + } + // Filter is http filter instance + Filter struct { + cfg *Config + } + // Config describe the config of FilterFactory + Config struct{} +) + +func (p *Plugin) Kind() string { + return Kind +} + +func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { + return &FilterFactory{}, nil +} + +func (factory *FilterFactory) Config() interface{} { + return factory.cfg +} + +func (factory *FilterFactory) Apply() error { + return nil +} + +func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error { + f := &Filter{cfg: factory.cfg} + chain.AppendDecodeFilters(f) + return nil +} + +func (f *Filter) Decode(hc *http.HttpContext) filter.FilterStatus { + api := hc.GetAPI() + headers := api.Headers + if len(headers) == 0 { + return filter.Continue + } + + urlHeaders := hc.AllHeaders() + if len(urlHeaders) == 0 { + return filter.Continue + } + + for headerName, headerValue := range headers { + urlHeaderValues := urlHeaders.Values(strings.ToLower(headerName)) + if urlHeaderValues == nil { + return filter.Continue + } + for _, urlHeaderValue := range urlHeaderValues { + if urlHeaderValue == headerValue { + goto FOUND + } + } + FOUND: + continue + } + return filter.Continue +} diff --git a/pkg/filter/header/header_test.go b/pkg/filter/header/header_test.go new file mode 100644 index 000000000..f28620084 --- /dev/null +++ b/pkg/filter/header/header_test.go @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 header + +import ( + "bytes" + "net/http" + "testing" +) + +import ( + "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" + "github.com/dubbo-go-pixiu/pixiu-api/pkg/router" + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/context/mock" +) + +func TestHeader(t *testing.T) { + p := Plugin{} + + filter, _ := p.CreateFilterFactory() + err := filter.Apply() + assert.Nil(t, err) + + api := router.API{ + URLPattern: "/mock/:id/:name", + Method: getMockMethod(config.MethodGet), + Headers: map[string]string{}, + } + request, err := http.NewRequest("POST", "http://www.dubbogopixiu.com/mock/test?name=tc", bytes.NewReader([]byte("{\"id\":\"12345\"}"))) + assert.NoError(t, err) + c := mock.GetMockHTTPContext(request) + c.API(api) + + request.Header.Set("filter", "test") + api.Headers["filter"] = "test" + c1 := mock.GetMockHTTPContext(request) + c1.API(api) + + request.Header.Set("filter", "test1") + c2 := mock.GetMockHTTPContext(request) + c2.API(api) +} + +func getMockMethod(verb config.HTTPVerb) config.Method { + inbound := config.InboundRequest{} + integration := config.IntegrationRequest{} + return config.Method{ + Enable: true, + HTTPVerb: verb, + InboundRequest: inbound, + IntegrationRequest: integration, + } +} diff --git a/pixiu/pkg/filter/host/host.go b/pkg/filter/host/host.go similarity index 90% rename from pixiu/pkg/filter/host/host.go rename to pkg/filter/host/host.go index e83ca4c4d..463ccfb9d 100644 --- a/pixiu/pkg/filter/host/host.go +++ b/pkg/filter/host/host.go @@ -18,9 +18,9 @@ package host import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - contexthttp "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http" ) const ( diff --git a/pixiu/pkg/filter/host/host_test.go b/pkg/filter/host/host_test.go similarity index 95% rename from pixiu/pkg/filter/host/host_test.go rename to pkg/filter/host/host_test.go index 430e5c26c..e4ba624ab 100644 --- a/pixiu/pkg/filter/host/host_test.go +++ b/pkg/filter/host/host_test.go @@ -28,7 +28,7 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/mock" + "github.com/apache/dubbo-go-pixiu/pkg/context/mock" ) func TestHost(t *testing.T) { diff --git a/pixiu/pkg/filter/http/apiconfig/api/discovery_service.go b/pkg/filter/http/apiconfig/api/discovery_service.go similarity index 98% rename from pixiu/pkg/filter/http/apiconfig/api/discovery_service.go rename to pkg/filter/http/apiconfig/api/discovery_service.go index b374ed457..866e18da3 100644 --- a/pixiu/pkg/filter/http/apiconfig/api/discovery_service.go +++ b/pkg/filter/http/apiconfig/api/discovery_service.go @@ -29,9 +29,9 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - pc "github.com/apache/dubbo-go-pixiu/pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/router" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + pc "github.com/apache/dubbo-go-pixiu/pkg/config" + "github.com/apache/dubbo-go-pixiu/pkg/router" ) // APIDiscoveryService api discovery service interface diff --git a/pixiu/pkg/filter/http/apiconfig/api/discovery_service_test.go b/pkg/filter/http/apiconfig/api/discovery_service_test.go similarity index 97% rename from pixiu/pkg/filter/http/apiconfig/api/discovery_service_test.go rename to pkg/filter/http/apiconfig/api/discovery_service_test.go index a63b0a93d..bbe5fdf0a 100644 --- a/pixiu/pkg/filter/http/apiconfig/api/discovery_service_test.go +++ b/pkg/filter/http/apiconfig/api/discovery_service_test.go @@ -28,8 +28,8 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/mock" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/config" + "github.com/apache/dubbo-go-pixiu/pkg/common/mock" + "github.com/apache/dubbo-go-pixiu/pkg/config" ) func TestNewLocalMemoryAPIDiscoveryService(t *testing.T) { diff --git a/pkg/filter/http/apiconfig/api_config.go b/pkg/filter/http/apiconfig/api_config.go new file mode 100644 index 000000000..1d13e21d3 --- /dev/null +++ b/pkg/filter/http/apiconfig/api_config.go @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 apiconfig + +import ( + "net/http" +) + +import ( + fc "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" + "github.com/dubbo-go-pixiu/pixiu-api/pkg/router" + "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/config" + contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/filter/http/apiconfig/api" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/server" +) + +const ( + // Kind is the kind of Fallback. + Kind = constant.HTTPApiConfigFilter +) + +func init() { + filter.RegisterHttpFilter(&Plugin{}) +} + +type ( + Plugin struct { + } + + FilterFactory struct { + cfg *ApiConfigConfig + apiService api.APIDiscoveryService + } + Filter struct { + apiService api.APIDiscoveryService + } +) + +func (p *Plugin) Kind() string { + return Kind +} + +func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { + return &FilterFactory{cfg: &ApiConfigConfig{}}, nil +} + +func (factory *FilterFactory) Config() interface{} { + return factory.cfg +} + +func (factory *FilterFactory) Apply() error { + factory.apiService = api.NewLocalMemoryAPIDiscoveryService() + + if factory.cfg.Dynamic { + server.GetApiConfigManager().AddApiConfigListener(factory.cfg.DynamicAdapter, factory) + return nil + } + + config, err := initApiConfig(factory.cfg) + if err != nil { + logger.Errorf("Get ApiConfig fail: %v", err) + } + if err := factory.apiService.InitAPIsFromConfig(*config); err != nil { + logger.Errorf("InitAPIsFromConfig fail: %v", err) + } + + return nil +} + +func (factory *FilterFactory) OnAddAPI(r router.API) error { + return factory.apiService.AddOrUpdateAPI(r) +} +func (factory *FilterFactory) OnRemoveAPI(r router.API) error { + return factory.apiService.RemoveAPIByIntance(r) +} + +func (factory *FilterFactory) OnDeleteRouter(r fc.Resource) error { + return factory.apiService.RemoveAPIByPath(r) +} + +func (factory *FilterFactory) GetAPIService() api.APIDiscoveryService { + return factory.apiService +} + +func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error { + f := &Filter{apiService: factory.apiService} + chain.AppendDecodeFilters(f) + return nil +} + +func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus { + req := ctx.Request + v, err := f.apiService.MatchAPI(req.URL.Path, fc.HTTPVerb(req.Method)) + if err != nil { + ctx.SendLocalReply(http.StatusNotFound, constant.Default404Body) + e := errors.Errorf("Requested URL %s not found", req.URL.Path) + logger.Debug(e.Error()) + return filter.Stop + } + + if !v.Method.Enable { + ctx.SendLocalReply(http.StatusNotAcceptable, constant.Default406Body) + e := errors.Errorf("Requested API %s %s does not online", req.Method, req.URL.Path) + logger.Debug(e.Error()) + return filter.Stop + } + ctx.API(v) + return filter.Continue +} + +func (factory *FilterFactory) GetApiService() api.APIDiscoveryService { + return factory.apiService +} + +// initApiConfig return value of the bool is for the judgment of whether is a api meta data error, a kind of silly (?) +func initApiConfig(cf *ApiConfigConfig) (*fc.APIConfig, error) { + if cf.APIMetaConfig != nil { + a, err := config.LoadAPIConfig(cf.APIMetaConfig) + if err != nil { + logger.Warnf("load api config from etcd error:%+v", err) + return nil, err + } + return a, nil + } + + a, err := config.LoadAPIConfigFromFile(cf.Path) + if err != nil { + logger.Errorf("load api config error:%+v", err) + return nil, err + } + return a, nil +} + +var _ filter.HttpFilterFactory = new(FilterFactory) diff --git a/pkg/filter/http/apiconfig/config.go b/pkg/filter/http/apiconfig/config.go new file mode 100644 index 000000000..c08da2da2 --- /dev/null +++ b/pkg/filter/http/apiconfig/config.go @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 apiconfig + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +// ApiConfigConfig the config for api_config filter +type ApiConfigConfig struct { + APIMetaConfig *model.APIMetaConfig `yaml:"api_meta_config" json:"api_meta_config,omitempty"` + Path string `yaml:"path" json:"path,omitempty"` + Dynamic bool `yaml:"dynamic" json:"dynamic,omitempty"` + DynamicAdapter string `yaml:"dynamic_adapter" json:"dynamic_adapter,omitempty"` +} diff --git a/pkg/filter/http/dubboproxy/dubbo.go b/pkg/filter/http/dubboproxy/dubbo.go new file mode 100644 index 000000000..78840d045 --- /dev/null +++ b/pkg/filter/http/dubboproxy/dubbo.go @@ -0,0 +1,242 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 dubboproxy + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "reflect" + "strings" +) + +import ( + "dubbo.apache.org/dubbo-go/v3/common" + dubboConstant "dubbo.apache.org/dubbo-go/v3/common/constant" + "dubbo.apache.org/dubbo-go/v3/protocol/dubbo" + "dubbo.apache.org/dubbo-go/v3/protocol/invocation" + hessian "github.com/apache/dubbo-go-hessian2" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + pixiuHttp "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/server" +) + +const ( + // Kind is the kind of plugin. + Kind = constant.HTTPDirectDubboProxyFilter +) + +func init() { + filter.RegisterHttpFilter(&Plugin{}) +} + +type ( + + // Plugin is http to dubbo with direct generic call filter plugin. + Plugin struct{} + + // FilterFactory is http to dubbo with direct generic call filter instance + FilterFactory struct { + cfg *Config + } + + // Filter http to dubbo with direct generic call + Filter struct{} + + // Config http to dubbo with direct generic call config + Config struct{} +) + +// Kind return plugin kind +func (p *Plugin) Kind() string { + return Kind +} + +// CreateFilterFactory create filter factory instance +func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { + return &FilterFactory{cfg: &Config{}}, nil +} + +// Config return filter facotry config, now is empty +func (factory *FilterFactory) Config() interface{} { + return factory.cfg +} + +// Apply init filter factory, now is empty +func (factory *FilterFactory) Apply() error { + return nil +} + +// PrepareFilterChain prepare filter chain +func (factory *FilterFactory) PrepareFilterChain(ctx *pixiuHttp.HttpContext, chain filter.FilterChain) error { + f := &Filter{} + chain.AppendDecodeFilters(f) + return nil +} + +// Decode handle http request to dubbo direct generic call and return http response +func (f *Filter) Decode(hc *pixiuHttp.HttpContext) filter.FilterStatus { + rEntry := hc.GetRouteEntry() + if rEntry == nil { + logger.Info("[dubbo-go-pixiu] http not match route") + bt, _ := json.Marshal(pixiuHttp.ErrResponse{Message: "not match route"}) + hc.SendLocalReply(http.StatusNotFound, bt) + return filter.Stop + } + logger.Debugf("[dubbo-go-pixiu] client choose endpoint from cluster :%v", rEntry.Cluster) + + clusterName := rEntry.Cluster + clusterManager := server.GetClusterManager() + endpoint := clusterManager.PickEndpoint(clusterName, hc) + if endpoint == nil { + logger.Info("[dubbo-go-pixiu] cluster not found endpoint") + bt, _ := json.Marshal(pixiuHttp.ErrResponse{Message: "cluster not found endpoint"}) + hc.SendLocalReply(http.StatusServiceUnavailable, bt) + return filter.Stop + } + + // http://host/{application}/{service}/{method} or https://host/{application}/{service}/{method} + rawPath := hc.Request.URL.Path + rawPath = strings.Trim(rawPath, "/") + splits := strings.Split(rawPath, "/") + + if len(splits) != 3 { + logger.Info("[dubbo-go-pixiu] http path pattern error. path pattern should be http://127.0.0.1/{application}/{service}/{method}") + bt, _ := json.Marshal(pixiuHttp.ErrResponse{Message: "http path pattern error"}) + hc.SendLocalReply(http.StatusBadRequest, bt) + return filter.Stop + } + + service := splits[1] + method := splits[2] + interfaceKey := service + + groupKey := hc.Request.Header.Get(constant.DubboGroup) + versionKey := hc.Request.Header.Get(constant.DubboServiceVersion) + types := hc.Request.Header.Get(constant.DubboServiceMethodTypes) + + rawBody, err := io.ReadAll(hc.Request.Body) + if err != nil { + logger.Infof("[dubbo-go-pixiu] read request body error %v", err) + bt, _ := json.Marshal(pixiuHttp.ErrResponse{Message: fmt.Sprintf("read request body error %v", err)}) + hc.SendLocalReply(http.StatusBadRequest, bt) + return filter.Stop + } + + var body interface{} + if err := json.Unmarshal(rawBody, &body); err != nil { + logger.Infof("[dubbo-go-pixiu] unmarshal request body error %v", err) + bt, _ := json.Marshal(pixiuHttp.ErrResponse{Message: fmt.Sprintf("unmarshal request body error %v", err)}) + hc.SendLocalReply(http.StatusBadRequest, bt) + return filter.Stop + } + + inIArr := make([]interface{}, 3) + inVArr := make([]reflect.Value, 3) + inIArr[0] = method + + var ( + typesList []string + valuesList []hessian.Object + ) + + if types != "" { + typesList = strings.Split(types, ",") + } + + values := body + if _, ok := values.([]interface{}); ok { + for _, v := range values.([]interface{}) { + valuesList = append(valuesList, v) + } + } else { + valuesList = append(valuesList, values) + } + + inIArr[1] = typesList + inIArr[2] = valuesList + + inVArr[0] = reflect.ValueOf(inIArr[0]) + inVArr[1] = reflect.ValueOf(inIArr[1]) + inVArr[2] = reflect.ValueOf(inIArr[2]) + + invoc := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("$invoke"), + invocation.WithArguments(inIArr), + invocation.WithParameterValues(inVArr)) + + url, err := common.NewURL(endpoint.Address.GetAddress(), + common.WithProtocol(dubbo.DUBBO), common.WithParamsValue(dubboConstant.SerializationKey, dubboConstant.Hessian2Serialization), + common.WithParamsValue(dubboConstant.GenericFilterKey, "true"), + common.WithParamsValue(dubboConstant.InterfaceKey, interfaceKey), + common.WithParamsValue(dubboConstant.ReferenceFilterKey, "generic,filter"), + // dubboAttachment must contains group and version info + common.WithParamsValue(dubboConstant.GroupKey, groupKey), + common.WithParamsValue(dubboConstant.VersionKey, versionKey), + common.WithPath(interfaceKey), + ) + if err != nil { + logger.Infof("[dubbo-go-pixiu] newURL error %v", err) + bt, _ := json.Marshal(pixiuHttp.ErrResponse{Message: fmt.Sprintf("newURL error %v", err)}) + hc.SendLocalReply(http.StatusServiceUnavailable, bt) + return filter.Stop + } + + dubboProtocol := dubbo.NewDubboProtocol() + + // TODO: will print many Error when failed to connect server + invoker := dubboProtocol.Refer(url) + if invoker == nil { + logger.Info("[dubbo-go-pixiu] dubbo protocol refer error") + bt, _ := json.Marshal(pixiuHttp.ErrResponse{Message: "dubbo protocol refer error"}) + hc.SendLocalReply(http.StatusServiceUnavailable, bt) + return filter.Stop + } + + var resp interface{} + invoc.SetReply(&resp) + + invCtx, cancel := context.WithTimeout(context.Background(), hc.Timeout) + defer cancel() + result := invoker.Invoke(invCtx, invoc) + result.SetAttachments(invoc.Attachments()) + + if result.Error() != nil { + logger.Debugf("[dubbo-go-pixiu] invoke result error %v", result.Error()) + bt, _ := json.Marshal(pixiuHttp.ErrResponse{Message: fmt.Sprintf("invoke result error %v", result.Error())}) + // TODO statusCode I don't know what dubbo returns when it times out, first use the string to judge + if strings.Contains(result.Error().Error(), "timeout") { + hc.SendLocalReply(http.StatusGatewayTimeout, bt) + } + hc.SendLocalReply(http.StatusServiceUnavailable, bt) + return filter.Stop + } + + value := reflect.ValueOf(result.Result()) + result.SetResult(value.Elem().Interface()) + hc.SourceResp = resp + invoker.Destroy() + // response write in hcm + return filter.Continue +} diff --git a/pixiu/pkg/filter/http/grpcproxy/descriptor.go b/pkg/filter/http/grpcproxy/descriptor.go similarity index 98% rename from pixiu/pkg/filter/http/grpcproxy/descriptor.go rename to pkg/filter/http/grpcproxy/descriptor.go index 6fe17873b..a4d996f46 100644 --- a/pixiu/pkg/filter/http/grpcproxy/descriptor.go +++ b/pkg/filter/http/grpcproxy/descriptor.go @@ -35,8 +35,8 @@ import ( ) import ( - ct "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" + ct "github.com/apache/dubbo-go-pixiu/pkg/context" + "github.com/apache/dubbo-go-pixiu/pkg/logger" ) type Descriptor struct { diff --git a/pixiu/pkg/filter/http/grpcproxy/descriptor_operation.go b/pkg/filter/http/grpcproxy/descriptor_operation.go similarity index 100% rename from pixiu/pkg/filter/http/grpcproxy/descriptor_operation.go rename to pkg/filter/http/grpcproxy/descriptor_operation.go diff --git a/pkg/filter/http/grpcproxy/descriptor_source.go b/pkg/filter/http/grpcproxy/descriptor_source.go new file mode 100644 index 000000000..3d4dd4e68 --- /dev/null +++ b/pkg/filter/http/grpcproxy/descriptor_source.go @@ -0,0 +1,192 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 grpcproxy + +import ( + "fmt" + "sync" +) + +import ( + "github.com/jhump/protoreflect/desc" + "github.com/jhump/protoreflect/dynamic" + "github.com/jhump/protoreflect/grpcreflect" + "github.com/pkg/errors" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +type DescriptorSource interface { + // ListServices returns a list of fully-qualified service names. It will be all services in a set of + // descriptor files or the set of all services exposed by a gRPC server. + ListServices() ([]string, error) + // FindSymbol returns a descriptor for the given fully-qualified symbol name. + FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) + // AllExtensionsForType returns all known extension fields that extend the given message type name. + AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) +} + +var ErrReflectionNotSupported = errors.New("server does not support the reflection API") + +// serverSource by gRPC server reflection +type serverSource struct { + client *grpcreflect.Client +} + +func (s *serverSource) ListServices() ([]string, error) { + svcs, err := s.client.ListServices() + return svcs, reflectionSupport(err) +} + +func (s *serverSource) FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) { + file, err := s.client.FileContainingSymbol(fullyQualifiedName) + if err != nil { + return nil, reflectionSupport(err) + } + d := file.FindSymbol(fullyQualifiedName) + if d == nil { + return nil, errors.New(fmt.Sprintf("%s not found: %s", "Symbol", fullyQualifiedName)) + } + return d, nil +} + +func (s *serverSource) AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) { + var exts []*desc.FieldDescriptor + nums, err := s.client.AllExtensionNumbersForType(typeName) + if err != nil { + return nil, reflectionSupport(err) + } + for _, fieldNum := range nums { + ext, err := s.client.ResolveExtension(typeName, fieldNum) + if err != nil { + return nil, reflectionSupport(err) + } + exts = append(exts, ext) + } + return exts, nil +} + +func reflectionSupport(err error) error { + if err == nil { + return nil + } + if stat, ok := status.FromError(err); ok && stat.Code() == codes.Unimplemented { + return ErrReflectionNotSupported + } + return err +} + +// fileSource by gRPC proto file +type fileSource struct { + files map[string]*desc.FileDescriptor + er *dynamic.ExtensionRegistry + erInit sync.Once +} + +func (fs *fileSource) ListServices() ([]string, error) { + set := map[string]bool{} + for _, fd := range fs.files { + for _, svc := range fd.GetServices() { + set[svc.GetFullyQualifiedName()] = true + } + } + sl := make([]string, 0, len(set)) + for svc := range set { + sl = append(sl, svc) + } + return sl, nil +} + +func (fs *fileSource) FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) { + for _, fd := range fs.files { + if dsc := fd.FindSymbol(fullyQualifiedName); dsc != nil { + return dsc, nil + } + } + return nil, fmt.Errorf("could not found symbol %v", fullyQualifiedName) +} + +func (fs *fileSource) AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) { + fs.erInit.Do(func() { + fs.er = &dynamic.ExtensionRegistry{} + for _, fd := range fs.files { + fs.er.AddExtensionsFromFile(fd) + } + }) + return fs.er.AllExtensionsForType(typeName), nil +} + +// compositeSource by fileSource and serverSource +type compositeSource struct { + reflection DescriptorSource + file DescriptorSource +} + +func (cs *compositeSource) ListServices() ([]string, error) { + if cs.reflection == nil { + return nil, ErrReflectionNotSupported + } + return cs.reflection.ListServices() +} + +func (cs *compositeSource) FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) { + if cs.reflection != nil { + descriptor, err := cs.reflection.FindSymbol(fullyQualifiedName) + if err == nil { + logger.Debugf("%s find symbol by reflection : %v", loggerHeader, descriptor) + return descriptor, nil + } + } + + return cs.file.FindSymbol(fullyQualifiedName) +} + +func (cs *compositeSource) AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) { + + if cs.reflection == nil { + fileExts, err := cs.file.AllExtensionsForType(typeName) + if err != nil { + return fileExts, nil + } + } else { + exts, err := cs.reflection.AllExtensionsForType(typeName) + if err != nil { + return cs.file.AllExtensionsForType(typeName) + } + tags := make(map[int32]bool) + for _, ext := range exts { + tags[ext.GetNumber()] = true + } + + fileExts, err := cs.file.AllExtensionsForType(typeName) + if err != nil { + return exts, nil + } + for _, ext := range fileExts { + if !tags[ext.GetNumber()] { + exts = append(exts, ext) + } + } + return exts, nil + } + return nil, nil +} diff --git a/pkg/filter/http/grpcproxy/grpc.go b/pkg/filter/http/grpcproxy/grpc.go new file mode 100644 index 000000000..9549cdc03 --- /dev/null +++ b/pkg/filter/http/grpcproxy/grpc.go @@ -0,0 +1,404 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 grpcproxy + +import ( + "context" + "errors" + "fmt" + "io" + stdHttp "net/http" + "strings" + "sync" + "time" +) + +import ( + "github.com/golang/protobuf/jsonpb" //nolint + "github.com/golang/protobuf/proto" //nolint + "github.com/jhump/protoreflect/desc" + "github.com/jhump/protoreflect/dynamic" + "github.com/jhump/protoreflect/dynamic/grpcdynamic" + perrors "github.com/pkg/errors" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + ct "github.com/apache/dubbo-go-pixiu/pkg/context" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/server" +) + +const ( + // Kind is the kind of Fallback. + Kind = constant.HTTPGrpcProxyFilter + + loggerHeader = "[grpc-proxy]" + + // DescriptorSourceKey current ds + DescriptorSourceKey = "DescriptorSource" + + // GrpcClientConnKey the grpc-client-conn by the coroutine local + GrpcClientConnKey = "GrpcClientConn" +) + +func init() { + filter.RegisterHttpFilter(&Plugin{}) +} + +const ( + NONE = "none" + AUTO = "auto" + LOCAL = "local" + REMOTE = "remote" +) + +type ( + DescriptorSourceStrategy string + + // Plugin is grpc filter plugin. + Plugin struct { + } + + // FilterFactory is grpc filter instance + FilterFactory struct { + cfg *Config + // grpc descriptor source factory + descriptor *Descriptor + // hold grpc.ClientConns, key format: cluster name + "." + endpoint + pools map[string]*sync.Pool + + extReg *dynamic.ExtensionRegistry + registered map[string]bool + } + Filter struct { + cfg *Config + // grpc descriptor source factory + descriptor *Descriptor + // hold grpc.ClientConns, key format: cluster name + "." + endpoint + pools map[string]*sync.Pool + + extReg *dynamic.ExtensionRegistry + registered map[string]bool + } + + // Config describe the config of AccessFilter + Config struct { + DescriptorSourceStrategy DescriptorSourceStrategy `yaml:"descriptor_source_strategy" json:"descriptor_source_strategy" default:"auto"` + Path string `yaml:"path" json:"path"` + Rules []*Rule `yaml:"rules" json:"rules"` //nolint + Timeout time.Duration `yaml:"timeout" json:"timeout"` //nolint + } + + Rule struct { + Selector string `yaml:"selector" json:"selector"` + Match Match `yaml:"match" json:"match"` + } + + Match struct { + Method string `yaml:"method" json:"method"` //nolint + } +) + +func (c DescriptorSourceStrategy) String() string { + return string(c) +} + +func (c DescriptorSourceStrategy) Val() DescriptorSourceStrategy { + switch c { + case NONE: + return NONE + case AUTO: + return AUTO + case LOCAL: + return LOCAL + case REMOTE: + return REMOTE + } + return "" +} + +func (p *Plugin) Kind() string { + return Kind +} + +func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { + return &FilterFactory{cfg: &Config{DescriptorSourceStrategy: AUTO}, descriptor: &Descriptor{}}, nil +} + +func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error { + f := &Filter{cfg: factory.cfg, descriptor: factory.descriptor, pools: factory.pools, extReg: factory.extReg, registered: factory.registered} + chain.AppendDecodeFilters(f) + return nil +} + +// getServiceAndMethod first return value is package.service, second one is method name +func getServiceAndMethod(path string) (string, string) { + pos := strings.LastIndex(path, "/") + if pos < 0 { + return "", "" + } + + mth := path[pos+1:] + prefix := strings.TrimSuffix(path, "/"+mth) + + pos = strings.LastIndex(prefix, "/") + if pos < 0 { + return "", "" + } + + svc := prefix[pos+1:] + return svc, mth +} + +// Decode use the default http to grpc transcoding strategy https://cloud.google.com/endpoints/docs/grpc/transcoding +func (f *Filter) Decode(c *http.HttpContext) filter.FilterStatus { + svc, mth := getServiceAndMethod(c.GetUrl()) + + var clientConn *grpc.ClientConn + var err error + + re := c.GetRouteEntry() + logger.Debugf("%s client choose endpoint from cluster :%v", loggerHeader, re.Cluster) + + e := server.GetClusterManager().PickEndpoint(re.Cluster, c) + if e == nil { + logger.Errorf("%s err {cluster not exists}", loggerHeader) + c.SendLocalReply(stdHttp.StatusServiceUnavailable, []byte("cluster not exists")) + return filter.Stop + } + // timeout for Dial and Invoke + ctx, cancel := context.WithTimeout(c.Ctx, c.Timeout) + defer cancel() + ep := e.Address.GetAddress() + + p, ok := f.pools[strings.Join([]string{re.Cluster, ep}, ".")] + if !ok { + p = &sync.Pool{} + } + + clientConn, ok = p.Get().(*grpc.ClientConn) + if !ok || clientConn == nil { + // TODO(Kenway): Support Credential and TLS + clientConn, err = grpc.DialContext(ctx, ep, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil || clientConn == nil { + logger.Errorf("%s err {failed to connect to grpc service provider}", loggerHeader) + c.SendLocalReply(stdHttp.StatusServiceUnavailable, []byte((fmt.Sprintf("%s", err)))) + return filter.Stop + } + } + + // get DescriptorSource, contain file and reflection + source, err := f.descriptor.getDescriptorSource(context.WithValue(ctx, ct.ContextKey(GrpcClientConnKey), clientConn), f.cfg) + if err != nil { + logger.Errorf("%s err %s : %s ", loggerHeader, "get desc source fail", err) + c.SendLocalReply(stdHttp.StatusInternalServerError, []byte("service not config proto file or the server not support reflection API")) + return filter.Stop + } + //put DescriptorSource concurrent, del if no need + ctx = context.WithValue(ctx, ct.ContextKey(DescriptorSourceKey), source) + + dscp, err := source.FindSymbol(svc) + if err != nil { + logger.Errorf("%s err {%s}", loggerHeader, "request path invalid") + c.SendLocalReply(stdHttp.StatusBadRequest, []byte("method not allow")) + return filter.Stop + } + + svcDesc, ok := dscp.(*desc.ServiceDescriptor) + if !ok { + logger.Errorf("%s err {service not expose, %s}", loggerHeader, svc) + c.SendLocalReply(stdHttp.StatusBadRequest, []byte(fmt.Sprintf("service not expose, %s", svc))) + return filter.Stop + } + + mthDesc := svcDesc.FindMethodByName(mth) + + err = f.registerExtension(source, mthDesc) + if err != nil { + logger.Errorf("%s err {%s}", loggerHeader, "register extension failed") + c.SendLocalReply(stdHttp.StatusInternalServerError, []byte(fmt.Sprintf("%s", err))) + return filter.Stop + } + + msgFac := dynamic.NewMessageFactoryWithExtensionRegistry(f.extReg) + grpcReq := msgFac.NewMessage(mthDesc.GetInputType()) + + err = jsonToProtoMsg(c.Request.Body, grpcReq) + if err != nil && !errors.Is(err, io.EOF) { + logger.Errorf("%s err {failed to convert json to proto msg, %s}", loggerHeader, err.Error()) + c.SendLocalReply(stdHttp.StatusInternalServerError, []byte(fmt.Sprintf("%s", err))) + return filter.Stop + } + + stub := grpcdynamic.NewStubWithMessageFactory(clientConn, msgFac) + + // metadata in grpc has the same feature in http + md := mapHeaderToMetadata(c.AllHeaders()) + ctx = metadata.NewOutgoingContext(ctx, md) + + md = metadata.MD{} + t := metadata.MD{} + + resp, err := Invoke(ctx, stub, mthDesc, grpcReq, grpc.Header(&md), grpc.Trailer(&t)) + // judge err is server side error or not + if st, ok := status.FromError(err); !ok || isServerError(st) { + if isServerTimeout(st) { + logger.Errorf("%s err {failed to invoke grpc service provider because timeout, err:%s}", loggerHeader, err.Error()) + c.SendLocalReply(stdHttp.StatusGatewayTimeout, []byte(fmt.Sprintf("%s", err))) + return filter.Stop + } + logger.Errorf("%s err {failed to invoke grpc service provider, %s}", loggerHeader, err.Error()) + c.SendLocalReply(stdHttp.StatusServiceUnavailable, []byte(fmt.Sprintf("%s", err))) + return filter.Stop + } + + res, err := protoMsgToJson(resp) + if err != nil { + logger.Errorf("%s err {failed to convert proto msg to json, %s}", loggerHeader, err.Error()) + c.SendLocalReply(stdHttp.StatusInternalServerError, []byte(fmt.Sprintf("%s", err))) + return filter.Stop + } + + h := mapMetadataToHeader(md) + th := mapMetadataToHeader(t) + + // let response filter handle resp + c.SourceResp = &stdHttp.Response{ + StatusCode: stdHttp.StatusOK, + Header: h, + Body: io.NopCloser(strings.NewReader(res)), + Trailer: th, + Request: c.Request, + } + p.Put(clientConn) + return filter.Continue +} + +func (f *Filter) registerExtension(source DescriptorSource, mthDesc *desc.MethodDescriptor) error { + err := RegisterExtension(source, f.extReg, mthDesc.GetInputType(), f.registered) + if err != nil { + return perrors.New("register extension failed") + } + + err = RegisterExtension(source, f.extReg, mthDesc.GetOutputType(), f.registered) + if err != nil { + return perrors.New("register extension failed") + } + return nil +} + +func RegisterExtension(source DescriptorSource, extReg *dynamic.ExtensionRegistry, msgDesc *desc.MessageDescriptor, registered map[string]bool) error { + msgType := msgDesc.GetFullyQualifiedName() + if _, ok := registered[msgType]; ok { + return nil + } + + if len(msgDesc.GetExtensionRanges()) > 0 { + fds, err := source.AllExtensionsForType(msgType) + if err != nil { + return fmt.Errorf("failed to find msg type {%s} in file source", msgType) + } + + err = extReg.AddExtension(fds...) + if err != nil { + return fmt.Errorf("failed to register extensions of msgType {%s}, err is {%s}", msgType, err.Error()) + } + } + + for _, fd := range msgDesc.GetFields() { + if fd.GetMessageType() != nil { + err := RegisterExtension(source, extReg, fd.GetMessageType(), registered) + if err != nil { + return err + } + } + } + + return nil +} + +func mapHeaderToMetadata(header stdHttp.Header) metadata.MD { + md := metadata.MD{} + for key, val := range header { + md.Append(key, val...) + } + return md +} + +func mapMetadataToHeader(md metadata.MD) stdHttp.Header { + h := stdHttp.Header{} + for key, val := range md { + for _, v := range val { + h.Add(key, v) + } + } + return h +} + +func jsonToProtoMsg(reader io.Reader, msg proto.Message) error { + body, err := io.ReadAll(reader) + if err != nil { + return err + } + return jsonpb.UnmarshalString(string(body), msg) +} + +func protoMsgToJson(msg proto.Message) (string, error) { + m := jsonpb.Marshaler{} + return m.MarshalToString(msg) +} + +func isServerError(st *status.Status) bool { + return st.Code() == codes.DeadlineExceeded || st.Code() == codes.ResourceExhausted || st.Code() == codes.Internal || + st.Code() == codes.Unavailable +} + +func isServerTimeout(st *status.Status) bool { + return st.Code() == codes.DeadlineExceeded || st.Code() == codes.Canceled +} + +func (factory *FilterFactory) Config() interface{} { + return factory.cfg +} + +func (factory *FilterFactory) Apply() error { + + err := configCheck(factory.cfg) + if err != nil { + return err + } + + factory.descriptor.initDescriptorSource(factory.cfg) + + return nil +} + +func configCheck(cfg *Config) error { + if len(cfg.DescriptorSourceStrategy.Val()) == 0 { + return perrors.Errorf("grpc descriptor source config `descriptor_source_strategy` is `%s`, maybe set it `%s`", cfg.DescriptorSourceStrategy.String(), AUTO) + } + return nil +} diff --git a/pixiu/pkg/filter/http/grpcproxy/invoke.go b/pkg/filter/http/grpcproxy/invoke.go similarity index 100% rename from pixiu/pkg/filter/http/grpcproxy/invoke.go rename to pkg/filter/http/grpcproxy/invoke.go diff --git a/pixiu/pkg/filter/http/httpproxy/routerfilter.go b/pkg/filter/http/httpproxy/routerfilter.go similarity index 93% rename from pixiu/pkg/filter/http/httpproxy/routerfilter.go rename to pkg/filter/http/httpproxy/routerfilter.go index 41ad533ee..d47cfdafa 100644 --- a/pixiu/pkg/filter/http/httpproxy/routerfilter.go +++ b/pkg/filter/http/httpproxy/routerfilter.go @@ -26,11 +26,11 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/server" ) const ( diff --git a/pkg/filter/http/loadbalancer/loadbalancer.go b/pkg/filter/http/loadbalancer/loadbalancer.go new file mode 100644 index 000000000..e037ec006 --- /dev/null +++ b/pkg/filter/http/loadbalancer/loadbalancer.go @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 loadbalancer + +import ( + "math/rand" + "strings" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http" +) + +const ( + Kind = constant.HTTPLoadBalanceFilter +) + +func init() { + filter.RegisterHttpFilter(&Plugin{}) +} + +type ( + // Plugin is http filter plugin. + Plugin struct { + } + + // FilterFactory is http filter instance + FilterFactory struct { + cfg *Config + } + // Filter is http filter instance + Filter struct { + cfg *Config + } + Config struct{} +) + +func (p *Plugin) Kind() string { + return Kind +} + +func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { + return &FilterFactory{cfg: &Config{}}, nil +} + +func (factory *FilterFactory) Config() interface{} { + return factory.cfg +} + +func (factory *FilterFactory) Apply() error { + return nil +} + +func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error { + f := &Filter{cfg: factory.cfg} + chain.AppendDecodeFilters(f) + return nil +} + +func (f *Filter) Decode(c *contexthttp.HttpContext) filter.FilterStatus { + allInstances := strings.Split(c.GetAPI().IntegrationRequest.HTTPBackendConfig.URL, ",") + idx := rand.Int31n(int32(len(allInstances))) + c.Api.IntegrationRequest.HTTPBackendConfig.URL = strings.TrimSpace(allInstances[idx]) + return filter.Continue +} diff --git a/pixiu/pkg/filter/http/proxyrewrite/rewrite.go b/pkg/filter/http/proxyrewrite/rewrite.go similarity index 91% rename from pixiu/pkg/filter/http/proxyrewrite/rewrite.go rename to pkg/filter/http/proxyrewrite/rewrite.go index 1fd05387a..f8ab76204 100644 --- a/pixiu/pkg/filter/http/proxyrewrite/rewrite.go +++ b/pkg/filter/http/proxyrewrite/rewrite.go @@ -26,10 +26,10 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - contexthttp "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" ) const ( diff --git a/pixiu/pkg/filter/http/proxyrewrite/rewrite_test.go b/pkg/filter/http/proxyrewrite/rewrite_test.go similarity index 93% rename from pixiu/pkg/filter/http/proxyrewrite/rewrite_test.go rename to pkg/filter/http/proxyrewrite/rewrite_test.go index 3142e5f41..ef43e1acf 100644 --- a/pixiu/pkg/filter/http/proxyrewrite/rewrite_test.go +++ b/pkg/filter/http/proxyrewrite/rewrite_test.go @@ -27,8 +27,8 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/mock" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/context/mock" ) func TestDecode(t *testing.T) { diff --git a/pkg/filter/http/proxywasm/filter.go b/pkg/filter/http/proxywasm/filter.go new file mode 100644 index 000000000..255247c18 --- /dev/null +++ b/pkg/filter/http/proxywasm/filter.go @@ -0,0 +1,126 @@ +//go:build !windows + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 proxywasm + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/wasm" +) + +func init() { + filter.RegisterHttpFilter(&Plugin{}) +} + +type ( + Plugin struct { + } + + WasmFilterFactory struct { + cfg *Config + } + + WasmFilter struct { + abiContextWrappers map[string]*wasm.ABIContextWrapper + } + + Config struct { + WasmServices []*Service `yaml:"wasm_services" json:"wasm_services" mapstructure:"wasm_services"` + } + + Service struct { + Name string `yaml:"name" json:"name" mapstructure:"name"` + } +) + +func (w *WasmFilter) Decode(ctx *http.HttpContext) filter.FilterStatus { + + // sample: display http header + if wrapper := w.abiContextWrappers[wasm.HeaderLevel]; wrapper != nil { + wrapper.Context.Instance.Lock(wrapper.Context) + defer wrapper.Context.Instance.Unlock() + + err := wrapper.Context.GetExports().ProxyOnContextCreate(wrapper.ContextID, wasm.GetServiceRootID(wasm.HeaderLevel)) + if err != nil { + logger.Warnf("[dubbo-go-pixiu] wasmFilter call ProxyOnContextCreate failed: %v", err) + return filter.Continue + } + + _, err = wrapper.Context.GetExports().ProxyOnRequestHeaders(wrapper.ContextID, int32(len(ctx.Request.Header)), 1) + if err != nil { + logger.Warnf("[dubbo-go-pixiu] wasmFilter call ProxyOnRequestHeaders failed: %v", err) + } + + err = wrapper.Context.GetExports().ProxyOnDelete(wrapper.ContextID) + if err != nil { + logger.Warnf("[dubbo-go-pixiu] wasmFilter call ProxyOnDelete failed: %v", err) + } + } + return filter.Continue +} + +func (w *WasmFilter) Encode(ctx *http.HttpContext) filter.FilterStatus { + for _, wrapper := range w.abiContextWrappers { + if err := wasm.ContextDone(wrapper); err != nil { + logger.Warnf("[dubbo-go-pixiu] wasmFilter call contextDone failed: %v", err) + } + } + + if val := ctx.GetHeader("go-wasm-header"); val != "" { + if _, err := ctx.Writer.Write([]byte(val)); err != nil { + logger.Errorf("write response error: %s", err) + } + } + return filter.Continue +} + +func (factory *WasmFilterFactory) Config() interface{} { + return factory.cfg +} + +func (factory *WasmFilterFactory) Apply() error { + return nil +} + +func (factory *WasmFilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error { + filter := &WasmFilter{ + abiContextWrappers: make(map[string]*wasm.ABIContextWrapper), + } + for _, service := range factory.cfg.WasmServices { + if abiContext := wasm.CreateABIContextByName(service.Name, ctx); abiContext != nil { + filter.abiContextWrappers[service.Name] = abiContext + } + } + chain.AppendDecodeFilters(filter) + chain.AppendEncodeFilters(filter) + return nil +} + +func (p *Plugin) Kind() string { + return constant.HTTPWasmFilter +} + +func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { + return &WasmFilterFactory{ + cfg: &Config{}, + }, nil +} diff --git a/pixiu/pkg/filter/http/proxywasm/filter_windows.go b/pkg/filter/http/proxywasm/filter_windows.go similarity index 87% rename from pixiu/pkg/filter/http/proxywasm/filter_windows.go rename to pkg/filter/http/proxywasm/filter_windows.go index 41721d367..0dc4039af 100644 --- a/pixiu/pkg/filter/http/proxywasm/filter_windows.go +++ b/pkg/filter/http/proxywasm/filter_windows.go @@ -20,10 +20,10 @@ package proxywasm import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" ) func init() { diff --git a/pkg/filter/http/remote/call.go b/pkg/filter/http/remote/call.go new file mode 100644 index 000000000..83054e982 --- /dev/null +++ b/pkg/filter/http/remote/call.go @@ -0,0 +1,250 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 remote + +import ( + "errors" + "fmt" + "net/http" + "os" + "strconv" + "strings" +) + +import ( + apiConf "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" + "github.com/dubbo-go-pixiu/pixiu-api/pkg/router" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/client" + "github.com/apache/dubbo-go-pixiu/pkg/client/dubbo" + clienthttp "github.com/apache/dubbo-go-pixiu/pkg/client/http" + "github.com/apache/dubbo-go-pixiu/pkg/client/triple" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +const ( + OPEN = iota + CLOSE + ALL +) + +const ( + Kind = constant.HTTPDubboProxyFilter +) + +func init() { + filter.RegisterHttpFilter(&Plugin{}) +} + +type ( + mockLevel int8 + + Plugin struct { + } + + FilterFactory struct { + conf *config + } + + Filter struct { + conf config + } + + config struct { + Level mockLevel `yaml:"level,omitempty" json:"level,omitempty"` + Dpc *dubbo.DubboProxyConfig `yaml:"dubboProxyConfig,omitempty" json:"dubboProxyConfig,omitempty"` + } +) + +func (p *Plugin) Kind() string { + return Kind +} + +func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { + return &FilterFactory{conf: &config{}}, nil +} + +func (factory *FilterFactory) Config() interface{} { + return factory.conf +} + +func (factory *FilterFactory) Apply() error { + mock := 1 + mockStr := os.Getenv(constant.EnvMock) + if len(mockStr) > 0 { + i, err := strconv.Atoi(mockStr) + if err == nil { + mock = i + } + } + level := mockLevel(mock) + if level < 0 || level > 2 { + level = CLOSE + } + factory.conf.Level = level + // must init it at apply function + if factory.conf.Dpc == nil { + return errors.New("expect the dubboProxyConfig config the registries") + } + dubbo.InitDefaultDubboClient(factory.conf.Dpc) + triple.InitDefaultTripleClient(factory.conf.Dpc.Protoset) + return nil +} + +func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error { + f := &Filter{conf: *factory.conf} + chain.AppendDecodeFilters(f) + return nil +} + +func (f *Filter) Decode(c *contexthttp.HttpContext) filter.FilterStatus { + if f.conf.Dpc != nil && f.conf.Dpc.AutoResolve { + if err := f.resolve(c); err != nil { + c.SendLocalReply(http.StatusInternalServerError, []byte(fmt.Sprintf("auto resolve err: %s", err))) + return filter.Stop + } + } + + api := c.GetAPI() + + if (f.conf.Level == OPEN && api.Mock) || (f.conf.Level == ALL) { + c.SourceResp = &contexthttp.ErrResponse{ + Message: "mock success", + } + return filter.Continue + } + + typ := api.Method.IntegrationRequest.RequestType + + cli, err := f.matchClient(typ) + if err != nil { + panic(err) + } + + req := client.NewReq(c.Request.Context(), c.Request, *api) + req.Timeout = c.Timeout + resp, err := cli.Call(req) + if err != nil { + logger.Errorf("[dubbo-go-pixiu] client call err: %v!", err) + if strings.Contains(strings.ToLower(err.Error()), "timeout") { + c.SendLocalReply(http.StatusGatewayTimeout, []byte(fmt.Sprintf("client call timeout err: %s", err))) + return filter.Stop + } + c.SendLocalReply(http.StatusInternalServerError, []byte(fmt.Sprintf("client call err: %s", err))) + return filter.Stop + } + + logger.Debugf("[dubbo-go-pixiu] client call resp: %v", resp) + + c.SourceResp = resp + return filter.Continue +} + +func (f *Filter) matchClient(typ apiConf.RequestType) (client.Client, error) { + switch strings.ToLower(string(typ)) { + case string(apiConf.DubboRequest): + return dubbo.SingletonDubboClient(), nil + // todo @(laurence) add triple to apiConf + case "triple": + return triple.SingletonTripleClient(f.conf.Dpc.Protoset), nil + case string(apiConf.HTTPRequest): + return clienthttp.SingletonHTTPClient(), nil + default: + return nil, errors.New("not support") + } +} + +func (f *Filter) resolve(ctx *contexthttp.HttpContext) error { + // method must be post + req := ctx.Request + if req.Method != http.MethodPost { + return errors.New("http request method must be post when trying to auto resolve") + } + // header must has x-dubbo-http1.1-dubbo-version to declare using auto resolve rule + version := req.Header.Get(constant.DubboHttpDubboVersion) + if version == "" { + return errors.New("http request must has x-dubbo-http1.1-dubbo-version header when trying to auto resolve") + } + + // http://host/{application}/{service}/{method} or https://host/{application}/{service}/{method} + rawPath := req.URL.Path + rawPath = strings.Trim(rawPath, "/") + splits := strings.Split(rawPath, "/") + if len(splits) != 3 { + return errors.New("http request path must meet {application}/{service}/{method} format when trying to auto resolve") + } + + integrationRequest := apiConf.IntegrationRequest{} + resolveProtocol := req.Header.Get(constant.DubboServiceProtocol) + if resolveProtocol == string(apiConf.HTTPRequest) { + integrationRequest.RequestType = apiConf.HTTPRequest + } else if resolveProtocol == string(apiConf.DubboRequest) { + integrationRequest.RequestType = apiConf.DubboRequest + } else if resolveProtocol == "triple" { + integrationRequest.RequestType = "triple" + } else { + return errors.New("http request has unknown protocol in x-dubbo-service-protocol when trying to auto resolve") + } + + dubboBackendConfig := apiConf.DubboBackendConfig{} + dubboBackendConfig.Version = req.Header.Get(constant.DubboServiceVersion) + dubboBackendConfig.Group = req.Header.Get(constant.DubboGroup) + integrationRequest.DubboBackendConfig = dubboBackendConfig + + defaultMappingParams := []apiConf.MappingParam{ + { + Name: "requestBody.values", + MapTo: "opt.values", + }, { + Name: "requestBody.types", + MapTo: "opt.types", + }, { + Name: "uri.application", + MapTo: "opt.application", + }, { + Name: "uri.interface", + MapTo: "opt.interface", + }, { + Name: "uri.method", + MapTo: "opt.method", + }, + } + integrationRequest.MappingParams = defaultMappingParams + + method := apiConf.Method{ + Enable: true, + Mock: false, + HTTPVerb: http.MethodPost, + } + method.IntegrationRequest = integrationRequest + + inboundRequest := apiConf.InboundRequest{} + inboundRequest.RequestType = apiConf.HTTPRequest + method.InboundRequest = inboundRequest + + api := router.API{} + api.URLPattern = "/:application/:interface/:method" + api.Method = method + ctx.API(api) + return nil +} diff --git a/pkg/filter/metric/metric.go b/pkg/filter/metric/metric.go new file mode 100644 index 000000000..044c4f06b --- /dev/null +++ b/pkg/filter/metric/metric.go @@ -0,0 +1,216 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 metric + +import ( + "fmt" + stdhttp "net/http" + "time" +) + +import ( + "github.com/pkg/errors" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric/global" + "go.opentelemetry.io/otel/metric/instrument" + "go.opentelemetry.io/otel/metric/instrument/syncint64" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/client" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +const ( + Kind = constant.HTTPMetricFilter +) + +var ( + totalElapsed syncint64.Counter + totalCount syncint64.Counter + totalError syncint64.Counter + + sizeRequest syncint64.Counter + sizeResponse syncint64.Counter + durationHist syncint64.Histogram +) + +func init() { + filter.RegisterHttpFilter(&Plugin{}) +} + +type ( + // FilterFactory is http filter plugin. + Plugin struct { + } + // FilterFactory is http filter instance + FilterFactory struct { + } + Filter struct { + start time.Time + } + // Config describe the config of FilterFactory + Config struct{} +) + +func (p *Plugin) Kind() string { + return Kind +} + +func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { + return &FilterFactory{}, nil +} + +func (factory *FilterFactory) Config() interface{} { + return &struct{}{} +} + +func (factory *FilterFactory) Apply() error { + // init + err := registerOtelMetric() + return err +} + +func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error { + f := &Filter{} + chain.AppendDecodeFilters(f) + chain.AppendEncodeFilters(f) + return nil +} + +func (f *Filter) Decode(c *http.HttpContext) filter.FilterStatus { + f.start = time.Now() + return filter.Continue +} + +func (f *Filter) Encode(c *http.HttpContext) filter.FilterStatus { + + commonAttrs := []attribute.KeyValue{ + attribute.String("code", fmt.Sprintf("%d", c.GetStatusCode())), + attribute.String("method", c.Request.Method), + attribute.String("url", c.GetUrl()), + attribute.String("host", c.Request.Host), + } + + latency := time.Since(f.start) + totalCount.Add(c.Ctx, 1, commonAttrs...) + latencyMilli := latency.Milliseconds() + totalElapsed.Add(c.Ctx, latencyMilli, commonAttrs...) + if c.LocalReply() { + totalError.Add(c.Ctx, 1) + } + + durationHist.Record(c.Ctx, latencyMilli, commonAttrs...) + size, err := computeApproximateRequestSize(c.Request) + if err != nil { + logger.Warn("can not compute request size", err) + } else { + sizeRequest.Add(c.Ctx, int64(size), commonAttrs...) + } + + size, err = computeApproximateResponseSize(c.TargetResp) + if err != nil { + logger.Warn("can not compute response size", err) + } else { + sizeResponse.Add(c.Ctx, int64(size), commonAttrs...) + } + + logger.Debugf("[Metric] [UPSTREAM] receive request | %d | %s | %s | %s | ", c.GetStatusCode(), latency, c.GetMethod(), c.GetUrl()) + return filter.Continue +} + +func computeApproximateResponseSize(res *client.Response) (int, error) { + if res == nil { + return 0, errors.New("client.Response is null pointer ") + } + return len(res.Data), nil +} + +func computeApproximateRequestSize(r *stdhttp.Request) (int, error) { + if r == nil { + return 0, errors.New("http.Request is null pointer ") + } + s := 0 + if r.URL != nil { + s = len(r.URL.Path) + } + s += len(r.Method) + s += len(r.Proto) + for name, values := range r.Header { + s += len(name) + for _, value := range values { + s += len(value) + } + } + s += len(r.Host) + if r.ContentLength != -1 { + s += int(r.ContentLength) + } + return s, nil +} + +func registerOtelMetric() error { + meter := global.MeterProvider().Meter("pixiu") + + elapsedCounter, err := meter.SyncInt64().Counter("pixiu_request_elapsed", instrument.WithDescription("request total elapsed in pixiu")) + if err != nil { + logger.Errorf("register pixiu_request_elapsed metric failed, err: %v", err) + return err + } + totalElapsed = elapsedCounter + + count, err := meter.SyncInt64().Counter("pixiu_request_count", instrument.WithDescription("request total count in pixiu")) + if err != nil { + logger.Errorf("register pixiu_request_count metric failed, err: %v", err) + return err + } + totalCount = count + + errorCounter, err := meter.SyncInt64().Counter("pixiu_request_error_count", instrument.WithDescription("request error total count in pixiu")) + if err != nil { + logger.Errorf("register pixiu_request_error_count metric failed, err: %v", err) + return err + } + totalError = errorCounter + + sizeRequest, err = meter.SyncInt64().Counter("pixiu_request_content_length", instrument.WithDescription("request total content length in pixiu")) + if err != nil { + logger.Errorf("register pixiu_request_content_length metric failed, err: %v", err) + return err + } + + sizeResponse, err = meter.SyncInt64().Counter("pixiu_response_content_length", instrument.WithDescription("request total content length response in pixiu")) + if err != nil { + logger.Errorf("register pixiu_response_content_length metric failed, err: %v", err) + return err + } + + durationHist, err = meter.SyncInt64().Histogram( + "pixiu_process_time_millicec", + instrument.WithDescription("request process time response in pixiu"), + ) + if err != nil { + logger.Errorf("register pixiu_process_time_millisec metric failed, err: %v", err) + return err + } + + return nil +} diff --git a/pkg/filter/metric/metric_test.go b/pkg/filter/metric/metric_test.go new file mode 100644 index 000000000..c9f4b481a --- /dev/null +++ b/pkg/filter/metric/metric_test.go @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 metric + +import ( + "bytes" + "net/http" + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/context/mock" +) + +func TestMetric(t *testing.T) { + filter := &Filter{} + err := registerOtelMetric() + if err != nil { + t.Fatal(err) + return + } + + request, err := http.NewRequest("POST", "http://www.dubbogopixiu.com/mock/test?name=tc", bytes.NewReader([]byte("{\"id\":\"12345\"}"))) + assert.NoError(t, err) + c := mock.GetMockHTTPContext(request) + filter.Decode(c) + filter.Encode(c) + t.Log("log filter test is finished") +} diff --git a/pixiu/pkg/filter/network/dubboproxy/dubbofiltermanager.go b/pkg/filter/network/dubboproxy/dubbofiltermanager.go similarity index 89% rename from pixiu/pkg/filter/network/dubboproxy/dubbofiltermanager.go rename to pkg/filter/network/dubboproxy/dubbofiltermanager.go index 7dc9439fc..d76e4995f 100644 --- a/pixiu/pkg/filter/network/dubboproxy/dubbofiltermanager.go +++ b/pkg/filter/network/dubboproxy/dubbofiltermanager.go @@ -18,10 +18,10 @@ package dubboproxy import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/yaml" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/common/yaml" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) // DubboFilterManager manage filters diff --git a/pixiu/pkg/filter/network/dubboproxy/filter/http/httpfilter.go b/pkg/filter/network/dubboproxy/filter/http/httpfilter.go similarity index 93% rename from pixiu/pkg/filter/network/dubboproxy/filter/http/httpfilter.go rename to pkg/filter/network/dubboproxy/filter/http/httpfilter.go index 04a7f2330..1593fe159 100644 --- a/pixiu/pkg/filter/network/dubboproxy/filter/http/httpfilter.go +++ b/pkg/filter/network/dubboproxy/filter/http/httpfilter.go @@ -34,10 +34,10 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - dubbo2 "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/dubbo" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + dubbo2 "github.com/apache/dubbo-go-pixiu/pkg/context/dubbo" + "github.com/apache/dubbo-go-pixiu/pkg/server" ) const ( diff --git a/pixiu/pkg/filter/network/dubboproxy/filter/proxy/proxyfilter.go b/pkg/filter/network/dubboproxy/filter/proxy/proxyfilter.go similarity index 95% rename from pixiu/pkg/filter/network/dubboproxy/filter/proxy/proxyfilter.go rename to pkg/filter/network/dubboproxy/filter/proxy/proxyfilter.go index 72e5a929b..8f35b87bc 100644 --- a/pixiu/pkg/filter/network/dubboproxy/filter/proxy/proxyfilter.go +++ b/pkg/filter/network/dubboproxy/filter/proxy/proxyfilter.go @@ -32,10 +32,10 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - dubbo2 "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/dubbo" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + dubbo2 "github.com/apache/dubbo-go-pixiu/pkg/context/dubbo" + "github.com/apache/dubbo-go-pixiu/pkg/server" ) const ( diff --git a/pkg/filter/network/dubboproxy/manager.go b/pkg/filter/network/dubboproxy/manager.go new file mode 100644 index 000000000..92e7b1b78 --- /dev/null +++ b/pkg/filter/network/dubboproxy/manager.go @@ -0,0 +1,194 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 dubboproxy + +import ( + "context" + "reflect" +) + +import ( + dubboConstant "dubbo.apache.org/dubbo-go/v3/common/constant" + "dubbo.apache.org/dubbo-go/v3/protocol/dubbo" + "dubbo.apache.org/dubbo-go/v3/protocol/invocation" + "dubbo.apache.org/dubbo-go/v3/remoting" + hessian "github.com/apache/dubbo-go-hessian2" + "github.com/dubbogo/grpc-go/metadata" + "github.com/go-errors/errors" + perrors "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + router2 "github.com/apache/dubbo-go-pixiu/pkg/common/router" + dubbo2 "github.com/apache/dubbo-go-pixiu/pkg/context/dubbo" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +// DubboProxyConnectionManager network filter for dubbo +type DubboProxyConnectionManager struct { + filter.EmptyNetworkFilter + config *model.DubboProxyConnectionManagerConfig + routerCoordinator *router2.RouterCoordinator + codec *dubbo.DubboCodec + filterManager *DubboFilterManager +} + +// CreateDubboProxyConnectionManager create dubbo proxy connection manager +func CreateDubboProxyConnectionManager(config *model.DubboProxyConnectionManagerConfig) *DubboProxyConnectionManager { + filterManager := NewDubboFilterManager(config.DubboFilters) + hcm := &DubboProxyConnectionManager{config: config, codec: &dubbo.DubboCodec{}, filterManager: filterManager} + hcm.routerCoordinator = router2.CreateRouterCoordinator(&config.RouteConfig) + return hcm +} + +// OnDecode decode bytes to DecodeResult +func (dcm *DubboProxyConnectionManager) OnDecode(data []byte) (interface{}, int, error) { + + resp, length, err := dcm.codec.Decode(data) + // err := pkg.Unmarshal(buf, p.client) + if err != nil { + if perrors.Is(err, hessian.ErrHeaderNotEnough) || perrors.Is(err, hessian.ErrBodyNotEnough) { + return nil, 0, nil + } + logger.Errorf("pkg.Unmarshal error:%+v", err) + return nil, length, err + } + return resp, length, nil +} + +// OnEncode encode Response to bytes +func (dcm *DubboProxyConnectionManager) OnEncode(pkg interface{}) ([]byte, error) { + res, ok := pkg.(*remoting.Response) + if ok { + buf, err := (dcm.codec).EncodeResponse(res) + if err != nil { + logger.Warnf("binary.Write(res{%#v}) = err{%#v}", res, perrors.WithStack(err)) + return nil, perrors.WithStack(err) + } + return buf.Bytes(), nil + } + + req, ok := pkg.(*remoting.Request) + if ok { + buf, err := (dcm.codec).EncodeRequest(req) + if err != nil { + logger.Warnf("binary.Write(req{%#v}) = err{%#v}", res, perrors.WithStack(err)) + return nil, perrors.WithStack(err) + } + return buf.Bytes(), nil + } + + logger.Errorf("illegal pkg:%+v\n, it is %+v", pkg, reflect.TypeOf(pkg)) + return nil, perrors.New("invalid rpc response") +} + +// OnTripleData handle triple rpc invocation +func (dcm *DubboProxyConnectionManager) OnTripleData(ctx context.Context, methodName string, arguments []interface{}) (interface{}, error) { + dubboAttachment := make(map[string]interface{}) + md, ok := metadata.FromIncomingContext(ctx) + if ok { + for k := range md { + dubboAttachment[k] = md.Get(k)[0] + } + } + interfaceName := dubboAttachment[constant.InterfaceKey].(string) + + ra, err := dcm.routerCoordinator.RouteByPathAndName(interfaceName, methodName) + + if err != nil { + return nil, errors.Errorf("Requested dubbo rpc invocation route not found") + } + + len := len(arguments) + inVArr := make([]reflect.Value, len) + for i := 0; i < len; i++ { + inVArr[i] = reflect.ValueOf(arguments[i]) + } + // old invocation can't set parameterValues + invoc := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName(methodName), + invocation.WithArguments(arguments), + invocation.WithParameterValues(inVArr), + invocation.WithAttachments(dubboAttachment)) + + rpcContext := &dubbo2.RpcContext{} + rpcContext.SetInvocation(invoc) + rpcContext.SetRoute(ra) + dcm.handleRpcInvocation(rpcContext) + result := rpcContext.RpcResult + return result, result.Error() +} + +// OnData handle dubbo rpc invocation +func (dcm *DubboProxyConnectionManager) OnData(data interface{}) (interface{}, error) { + old_invoc, ok := data.(*invocation.RPCInvocation) + if !ok { + panic("create invocation occur some exception for the type is not suitable one.") + } + // need reconstruct RPCInvocation ParameterValues witch is same with arguments. refer to dubbogo/common/proxy/proxy.makeDubboCallProxy + arguments := old_invoc.Arguments() + len := len(arguments) + inVArr := make([]reflect.Value, len) + for i := 0; i < len; i++ { + inVArr[i] = reflect.ValueOf(arguments[i]) + } + //old invocation can't set parameterValues + invoc := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName(old_invoc.MethodName()), + invocation.WithArguments(old_invoc.Arguments()), + invocation.WithCallBack(old_invoc.CallBack()), invocation.WithParameterValues(inVArr), + invocation.WithAttachments(old_invoc.Attachments())) + + path, _ := old_invoc.GetAttachmentInterface(dubboConstant.PathKey).(string) + ra, err := dcm.routerCoordinator.RouteByPathAndName(path, old_invoc.MethodName()) + + if err != nil { + return nil, errors.Errorf("Requested dubbo rpc invocation route not found") + } + + ctx := &dubbo2.RpcContext{} + ctx.SetRoute(ra) + ctx.SetInvocation(invoc) + dcm.handleRpcInvocation(ctx) + result := ctx.RpcResult + return result, nil +} + +// handleRpcInvocation handle rpc request +func (dcm *DubboProxyConnectionManager) handleRpcInvocation(c *dubbo2.RpcContext) { + filterChain := dcm.filterManager.filters + + // recover any err when filterChain run + defer func() { + if err := recover(); err != nil { + logger.Warnf("[dubbopixiu go] Occur An Unexpected Err: %+v", err) + c.SetError(errors.Errorf("Occur An Unexpected Err: %v", err)) + } + }() + + for _, f := range filterChain { + status := f.Handle(c) + switch status { + case filter.Continue: + continue + case filter.Stop: + return + } + } +} diff --git a/pkg/filter/network/dubboproxy/plugin.go b/pkg/filter/network/dubboproxy/plugin.go new file mode 100644 index 000000000..35e6a0a7d --- /dev/null +++ b/pkg/filter/network/dubboproxy/plugin.go @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 dubboproxy + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/common/util/stringutil" + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +const ( + Kind = constant.DubboConnectManagerFilter +) + +func init() { + filter.RegisterNetworkFilterPlugin(&Plugin{}) +} + +type ( + // Plugin the dubbo networkfilter plugin + Plugin struct{} +) + +// Kind kind +func (p *Plugin) Kind() string { + return Kind +} + +// CreateFilter create dubbo networkfilter +func (p *Plugin) CreateFilter(config interface{}) (filter.NetworkFilter, error) { + hcmc, ok := config.(*model.DubboProxyConnectionManagerConfig) + hcmc.Timeout = stringutil.ResolveTimeStr2Time(hcmc.TimeoutStr, constant.DefaultReqTimeout) + if !ok { + panic("CreateFilter occur some exception for the type is not suitable one.") + } + return CreateDubboProxyConnectionManager(hcmc), nil +} + +// Config return DubboProxyConnectionManagerConfig +func (p *Plugin) Config() interface{} { + return &model.DubboProxyConnectionManagerConfig{} +} diff --git a/pkg/filter/network/grpcconnectionmanager/plugin.go b/pkg/filter/network/grpcconnectionmanager/plugin.go new file mode 100644 index 000000000..34c4c49f9 --- /dev/null +++ b/pkg/filter/network/grpcconnectionmanager/plugin.go @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 grpcconnectionmanager + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/common/grpc" + "github.com/apache/dubbo-go-pixiu/pkg/common/util/stringutil" + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +const ( + Kind = constant.GRPCConnectManagerFilter +) + +func init() { + filter.RegisterNetworkFilterPlugin(&Plugin{}) +} + +type ( + Plugin struct{} +) + +// Kind kind +func (p *Plugin) Kind() string { + return Kind +} + +// CreateFilter create grpc network filter +func (p *Plugin) CreateFilter(config interface{}) (filter.NetworkFilter, error) { + hcmc := config.(*model.GRPCConnectionManagerConfig) + hcmc.Timeout = stringutil.ResolveTimeStr2Time(hcmc.TimeoutStr, constant.DefaultReqTimeout) + return grpc.CreateGrpcConnectionManager(hcmc), nil +} + +// Config return GRPCConnectionManagerConfig +func (p *Plugin) Config() interface{} { + return &model.GRPCConnectionManagerConfig{} +} diff --git a/pkg/filter/network/httpconnectionmanager/plugin.go b/pkg/filter/network/httpconnectionmanager/plugin.go new file mode 100644 index 000000000..58612aeab --- /dev/null +++ b/pkg/filter/network/httpconnectionmanager/plugin.go @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 httpconnectionmanager + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/common/http" + "github.com/apache/dubbo-go-pixiu/pkg/common/util/stringutil" + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +const ( + Kind = constant.HTTPConnectManagerFilter +) + +func init() { + filter.RegisterNetworkFilterPlugin(&Plugin{}) +} + +type ( + Plugin struct{} +) + +// Kind +func (p *Plugin) Kind() string { + return Kind +} + +// CreateFilter create http network filter +func (p *Plugin) CreateFilter(config interface{}) (filter.NetworkFilter, error) { + hcmc := config.(*model.HttpConnectionManagerConfig) + hcmc.Timeout = stringutil.ResolveTimeStr2Time(hcmc.TimeoutStr, constant.DefaultReqTimeout) + return http.CreateHttpConnectionManager(hcmc), nil +} + +// Config return HttpConnectionManagerConfig +func (p *Plugin) Config() interface{} { + return &model.HttpConnectionManagerConfig{} +} diff --git a/pixiu/pkg/filter/prometheus/config.go b/pkg/filter/prometheus/config.go similarity index 100% rename from pixiu/pkg/filter/prometheus/config.go rename to pkg/filter/prometheus/config.go diff --git a/pkg/filter/prometheus/metric.go b/pkg/filter/prometheus/metric.go new file mode 100644 index 000000000..d2579c12f --- /dev/null +++ b/pkg/filter/prometheus/metric.go @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 prometheus + +import ( + stdHttp "net/http" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + contextHttp "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + prom "github.com/apache/dubbo-go-pixiu/pkg/prometheus" +) + +const ( + Kind = constant.HTTPPrometheusMetricFilter +) + +func init() { + filter.RegisterHttpFilter(&Plugin{}) +} + +type ( + Plugin struct { + } + + FilterFactory struct { + Cfg *MetricCollectConfiguration + Prom *prom.Prometheus + } + + Filter struct { + Cfg *MetricCollectConfiguration + Prom *prom.Prometheus + } +) + +func (p Plugin) Kind() string { + return Kind +} + +func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { + + return &FilterFactory{ + Cfg: &MetricCollectConfiguration{}, + Prom: prom.NewPrometheus(), + }, nil +} + +func (factory *FilterFactory) Config() interface{} { + + return factory.Cfg +} + +func (factory *FilterFactory) Apply() error { + + return nil +} + +func (factory *FilterFactory) PrepareFilterChain(ctx *contextHttp.HttpContext, chain filter.FilterChain) error { + + f := &Filter{ + Cfg: factory.Cfg, + Prom: factory.Prom, + } + f.Prom.SetPushGatewayUrl(f.Cfg.Rules.PushGatewayURL, f.Cfg.Rules.MetricPath) + f.Prom.SetPushIntervalThreshold(f.Cfg.Rules.CounterPush, f.Cfg.Rules.PushIntervalThreshold) + f.Prom.SetPushGatewayJob(f.Cfg.Rules.PushJobName) + chain.AppendDecodeFilters(f) + return nil +} + +func (f *Filter) Decode(ctx *contextHttp.HttpContext) filter.FilterStatus { + + if f.Cfg == nil { + logger.Errorf("Message:Filter Metric Collect Configuration is null") + ctx.SendLocalReply(stdHttp.StatusForbidden, constant.Default403Body) + return filter.Continue + } + if f.Prom == nil { + logger.Errorf("Message:Prometheus Collector is not initialized") + ctx.SendLocalReply(stdHttp.StatusForbidden, constant.Default403Body) + return filter.Continue + } + if f.Cfg.Rules.CounterPush && f.Cfg.Rules.PushIntervalThreshold == 0 { + ctx.SendLocalReply(stdHttp.StatusForbidden, constant.Default403Body) + return filter.Continue + } + start := f.Prom.HandlerFunc() + err := start(ctx) + if err != nil { + logger.Errorf("Message:Context HandlerFunc error") + ctx.SendLocalReply(stdHttp.StatusForbidden, constant.Default403Body) + } + return filter.Continue +} diff --git a/pkg/filter/prometheus/metric_test.go b/pkg/filter/prometheus/metric_test.go new file mode 100644 index 000000000..7f39c547f --- /dev/null +++ b/pkg/filter/prometheus/metric_test.go @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 prometheus + +import ( + "bytes" + "encoding/json" + "net/http" + "testing" + "time" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/client" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/common/yaml" + "github.com/apache/dubbo-go-pixiu/pkg/context/mock" +) + +func TestCounterExporterApiMetric(t *testing.T) { + rules := MetricCollectConfiguration{ + MetricCollectRule{ + MetricPath: "/metrics", + PushGatewayURL: "http://127.0.0.1:9091", + PushJobName: "pixiu", + CounterPush: true, + PushIntervalThreshold: 100, + }, + } + _, err := yaml.MarshalYML(rules) + assert.Nil(t, err) + config := &rules + p := Plugin{} + msg := "this is test msg" + metricFilterFactory, _ := p.CreateFilterFactory() + if factory, ok := metricFilterFactory.(*FilterFactory); ok { + factory.Cfg = config + err = factory.Apply() + assert.Nil(t, err) + chain := filter.NewDefaultFilterChain() + for i := 0; i < 1000; i++ { + data := GetApiStatsResponse() + body, _ := json.Marshal(&data) + request, _ := http.NewRequest("POST", "/_api/health", bytes.NewBuffer(body)) + ctx := mock.GetMockHTTPContext(request) + ctx.TargetResp = client.NewResponse([]byte(msg)) + err := factory.PrepareFilterChain(ctx, chain) + assert.Nil(t, err) + chain.OnDecode(ctx) + } + } + time.Sleep(20 * time.Second) +} + +func GetApiStatsResponse() ApiStatsResponse { + return ApiStatsResponse{ + ApiStats: []ApiStat{ + { + ApiName: "api1", + ApiRequests: 1000, + }, + }, + } +} + +type ApiStatsResponse struct { + ApiStats []ApiStat `json:"api_stats"` +} + +type ApiStat struct { + ApiName string `json:"api_name"` + ApiRequests int64 `json:"api_requests"` +} diff --git a/pixiu/pkg/filter/seata/branch_transaction_service.go b/pkg/filter/seata/branch_transaction_service.go similarity index 99% rename from pixiu/pkg/filter/seata/branch_transaction_service.go rename to pkg/filter/seata/branch_transaction_service.go index 9df455352..e8439c0b7 100644 --- a/pixiu/pkg/filter/seata/branch_transaction_service.go +++ b/pkg/filter/seata/branch_transaction_service.go @@ -34,7 +34,7 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/logger" ) const ( diff --git a/pixiu/pkg/filter/seata/config.go b/pkg/filter/seata/config.go similarity index 100% rename from pixiu/pkg/filter/seata/config.go rename to pkg/filter/seata/config.go diff --git a/pkg/filter/seata/context.go b/pkg/filter/seata/context.go new file mode 100644 index 000000000..6d7ec75bc --- /dev/null +++ b/pkg/filter/seata/context.go @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 seata + +import ( + "bytes" + "encoding/json" + "net/http" +) + +import ( + "vimagination.zapto.org/byteio" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +var ( + VarHost = "host" + VarQueryString = "queryString" +) + +type RequestContext struct { + ActionContext map[string]string + Headers http.Header + Body []byte + Trailers http.Header +} + +func (ctx *RequestContext) Encode() ([]byte, error) { + var ( + err error + actionContextData []byte + headersData []byte + trailersData []byte + b bytes.Buffer + ) + + w := byteio.BigEndianWriter{Writer: &b} + + if ctx.ActionContext == nil || len(ctx.ActionContext) == 0 { + w.WriteUint32(0) + } else { + actionContextData, err = json.Marshal(ctx.ActionContext) + if err != nil { + return nil, err + } + + w.WriteUint32(uint32(len(actionContextData))) + w.Write(actionContextData) + } + + if ctx.Headers == nil || len(ctx.Headers) == 0 { + w.WriteUint32(0) + } else { + headersData, err = json.Marshal(ctx.Headers) + if err != nil { + return nil, err + } + w.WriteUint32(uint32(len(headersData))) + w.Write(headersData) + } + + if ctx.Trailers == nil || len(ctx.Trailers) == 0 { + w.WriteUint32(0) + } else { + trailersData, err = json.Marshal(ctx.Trailers) + if err != nil { + return nil, err + } + w.WriteUint32(uint32(len(trailersData))) + w.Write(trailersData) + } + + w.WriteUint32(uint32(len(ctx.Body))) + b.Write(ctx.Body) + + return b.Bytes(), nil +} + +func (ctx *RequestContext) Decode(b []byte) error { + var ( + actionContextData []byte + headersData []byte + bodyData []byte + trailersData []byte + ) + r := byteio.BigEndianReader{Reader: bytes.NewReader(b)} + + contextLength, _, err := r.ReadUint32() + if err != nil { + return err + } + if contextLength > 0 { + actionContextData = make([]byte, contextLength, contextLength) + r.Read(actionContextData) + } + + headerLength, _, err := r.ReadUint32() + if err != nil { + return err + } + if headerLength > 0 { + headersData = make([]byte, headerLength, headerLength) + r.Read(headersData) + } + + trailersLength, _, err := r.ReadUint32() + if err != nil { + return err + } + if trailersLength > 0 { + trailersData = make([]byte, trailersLength, trailersLength) + r.Read(trailersData) + } + + bodyLength, _, err := r.ReadUint32() + if err != nil { + return err + } + if bodyLength > 0 { + bodyData = make([]byte, bodyLength, bodyLength) + r.Read(bodyData) + } + + if actionContextData != nil { + err = json.Unmarshal(actionContextData, &(ctx.ActionContext)) + if err != nil { + logger.Errorf("unmarshal action context failed, %v", err) + } + } + + if headersData != nil { + err = json.Unmarshal(headersData, &(ctx.Headers)) + if err != nil { + logger.Errorf("unmarshal headers failed, %v", err) + } + } + + if trailersData != nil { + err = json.Unmarshal(trailersData, &(ctx.ActionContext)) + if err != nil { + logger.Errorf("unmarshal trailers failed, %v", err) + } + } + + ctx.Body = bodyData + return nil +} diff --git a/pkg/filter/seata/filter.go b/pkg/filter/seata/filter.go new file mode 100644 index 000000000..c274d6ba3 --- /dev/null +++ b/pkg/filter/seata/filter.go @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 seata + +import ( + netHttp "net/http" + "strings" +) + +import ( + "github.com/opentrx/seata-golang/v2/pkg/apis" + "github.com/opentrx/seata-golang/v2/pkg/util/runtime" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" +) + +const ( + Kind = "dgp.filter.http.seata" + + SEATA = "seata" + XID = "x_seata_xid" + BranchID = "x_seata_branch_id" +) + +func init() { + filter.RegisterHttpFilter(&Plugin{}) +} + +type ( + // MetricFilter is http Filter plugin. + Plugin struct { + } + // FilterFactory is http Filter instance + FilterFactory struct { + conf *Seata + transactionInfos map[string]*TransactionInfo + tccResources map[string]*TCCResource + transactionClient apis.TransactionManagerServiceClient + resourceClient apis.ResourceManagerServiceClient + branchMessages chan *apis.BranchMessage + } + // Filter is http Filter instance + Filter struct { + conf *Seata + transactionInfos map[string]*TransactionInfo + tccResources map[string]*TCCResource + transactionClient apis.TransactionManagerServiceClient + resourceClient apis.ResourceManagerServiceClient + branchMessages chan *apis.BranchMessage + + globalResult bool + branchRegisterResult bool + } +) + +func (ap *Plugin) Kind() string { + return Kind +} + +func (ap *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { + return &FilterFactory{ + conf: &Seata{}, + transactionInfos: make(map[string]*TransactionInfo), + tccResources: make(map[string]*TCCResource), + branchMessages: make(chan *apis.BranchMessage), + }, nil +} + +func (factory *FilterFactory) Config() interface{} { + return factory.conf +} + +func (factory *FilterFactory) Apply() error { + conn, err := grpc.Dial(factory.conf.ServerAddressing, + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithKeepaliveParams(factory.conf.GetClientParameters())) + if err != nil { + return err + } + + factory.transactionClient = apis.NewTransactionManagerServiceClient(conn) + factory.resourceClient = apis.NewResourceManagerServiceClient(conn) + + runtime.GoWithRecover(func() { + factory.branchCommunicate() + }, nil) + + for _, ti := range factory.conf.TransactionInfos { + factory.transactionInfos[ti.RequestPath] = ti + } + + for _, r := range factory.conf.TCCResources { + factory.tccResources[r.PrepareRequestPath] = r + } + return nil +} + +func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error { + f := &Filter{ + conf: factory.conf, + transactionInfos: factory.transactionInfos, + tccResources: factory.tccResources, + transactionClient: factory.transactionClient, + resourceClient: factory.resourceClient, + branchMessages: factory.branchMessages, + } + chain.AppendDecodeFilters(f) + return nil +} + +func (f *Filter) Decode(ctx *http.HttpContext) filter.FilterStatus { + path := ctx.Request.URL.Path + method := ctx.Request.Method + + if method != netHttp.MethodPost { + return filter.Continue + } + + transactionInfo, found := f.transactionInfos[strings.ToLower(path)] + if found { + f.globalResult = f.handleHttp1GlobalBegin(ctx, transactionInfo) + } + + tccResource, exists := f.tccResources[strings.ToLower(path)] + if exists { + f.branchRegisterResult = f.handleHttp1BranchRegister(ctx, tccResource) + } + return filter.Continue +} + +func (f *Filter) Encode(ctx *http.HttpContext) filter.FilterStatus { + if f.globalResult { + f.handleHttp1GlobalEnd(ctx) + } + + if f.branchRegisterResult { + f.handleHttp1BranchEnd(ctx) + } + return filter.Continue +} diff --git a/pixiu/pkg/filter/seata/transaction.go b/pkg/filter/seata/transaction.go similarity index 98% rename from pixiu/pkg/filter/seata/transaction.go rename to pkg/filter/seata/transaction.go index a224f6e2b..48e11b096 100644 --- a/pixiu/pkg/filter/seata/transaction.go +++ b/pkg/filter/seata/transaction.go @@ -32,9 +32,9 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/server" ) // handleHttp1GlobalBegin return bool, represent whether continue diff --git a/pixiu/pkg/filter/sentinel/circuitbreaker/circuit_breaker.go b/pkg/filter/sentinel/circuitbreaker/circuit_breaker.go similarity index 93% rename from pixiu/pkg/filter/sentinel/circuitbreaker/circuit_breaker.go rename to pkg/filter/sentinel/circuitbreaker/circuit_breaker.go index bf4bf2e91..c9535f8a0 100644 --- a/pixiu/pkg/filter/sentinel/circuitbreaker/circuit_breaker.go +++ b/pkg/filter/sentinel/circuitbreaker/circuit_breaker.go @@ -30,11 +30,11 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - pkgs "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/sentinel" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" + pkgs "github.com/apache/dubbo-go-pixiu/pkg/filter/sentinel" + "github.com/apache/dubbo-go-pixiu/pkg/logger" ) const ( diff --git a/pixiu/pkg/filter/sentinel/circuitbreaker/circuit_breaker_test.go b/pkg/filter/sentinel/circuitbreaker/circuit_breaker_test.go similarity index 88% rename from pixiu/pkg/filter/sentinel/circuitbreaker/circuit_breaker_test.go rename to pkg/filter/sentinel/circuitbreaker/circuit_breaker_test.go index 67863e392..f0ef76002 100644 --- a/pixiu/pkg/filter/sentinel/circuitbreaker/circuit_breaker_test.go +++ b/pkg/filter/sentinel/circuitbreaker/circuit_breaker_test.go @@ -28,10 +28,10 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/yaml" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/mock" - pkgs "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/sentinel" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/common/yaml" + "github.com/apache/dubbo-go-pixiu/pkg/context/mock" + pkgs "github.com/apache/dubbo-go-pixiu/pkg/filter/sentinel" ) func TestFilter(t *testing.T) { diff --git a/pixiu/pkg/filter/sentinel/config.go b/pkg/filter/sentinel/config.go similarity index 100% rename from pixiu/pkg/filter/sentinel/config.go rename to pkg/filter/sentinel/config.go diff --git a/pixiu/pkg/filter/sentinel/exact.go b/pkg/filter/sentinel/exact.go similarity index 100% rename from pixiu/pkg/filter/sentinel/exact.go rename to pkg/filter/sentinel/exact.go diff --git a/pixiu/pkg/filter/sentinel/logger_warpper.go b/pkg/filter/sentinel/logger_warpper.go similarity index 97% rename from pixiu/pkg/filter/sentinel/logger_warpper.go rename to pkg/filter/sentinel/logger_warpper.go index b0de342c7..b5c0d4276 100644 --- a/pixiu/pkg/filter/sentinel/logger_warpper.go +++ b/pkg/filter/sentinel/logger_warpper.go @@ -18,7 +18,7 @@ package sentinel import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/logger" ) // loggerWrapper diff --git a/pixiu/pkg/filter/sentinel/matcher.go b/pkg/filter/sentinel/matcher.go similarity index 100% rename from pixiu/pkg/filter/sentinel/matcher.go rename to pkg/filter/sentinel/matcher.go diff --git a/pkg/filter/sentinel/ratelimit/config.go b/pkg/filter/sentinel/ratelimit/config.go new file mode 100644 index 000000000..fb19590e2 --- /dev/null +++ b/pkg/filter/sentinel/ratelimit/config.go @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 ratelimit + +import ( + "github.com/alibaba/sentinel-golang/core/flow" +) + +import ( + pkgs "github.com/apache/dubbo-go-pixiu/pkg/filter/sentinel" +) + +type ( + // Config rate limit config + Config struct { + Resources []*pkgs.Resource `json:"resources,omitempty" yaml:"resources,omitempty"` + Rules []*Rule `json:"rules,omitempty" yaml:"rules,omitempty"` + LogPath string `json:"logPath,omitempty" yaml:"logPath,omitempty"` + } + + // Rule api group 's rate-limit rule + Rule struct { + ID int64 `json:"id,omitempty" yaml:"id,omitempty"` + FlowRule flow.Rule `json:"flowRule,omitempty" yaml:"flowRule,omitempty"` + Enable bool `json:"enable,omitempty" yaml:"enable,omitempty"` + } +) diff --git a/pkg/filter/sentinel/ratelimit/matcher_test.go b/pkg/filter/sentinel/ratelimit/matcher_test.go new file mode 100644 index 000000000..fe95fd8f5 --- /dev/null +++ b/pkg/filter/sentinel/ratelimit/matcher_test.go @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 ratelimit + +import ( + "testing" +) + +import ( + "github.com/alibaba/sentinel-golang/core/flow" + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/filter/sentinel" +) + +func TestMatch(t *testing.T) { + config := mockConfig() + m := sentinel.NewMatcher() + m.Load(config.Resources) + + tests := []struct { + give string + matched bool + res string + }{ + { + give: "/api/v1/test-dubbo/user", + matched: true, + res: "test-dubbo", + }, + { + give: "/api/v1/test-dubbo/user/getInfo", + matched: true, + res: "test-dubbo", + }, + { + give: "/api/v1/test-dubbo/A", + matched: false, + res: "", + }, + { + give: "/api/v1/http/foo", + matched: true, + res: "test-http", + }, + { + give: "/api/v1/http/bar/1.json", + matched: true, + res: "test-http", + }, + { + give: "/api/v1/http", + matched: false, + res: "", + }, + } + for _, tt := range tests { + t.Run(tt.give, func(t *testing.T) { + res, ok := m.Match(tt.give) + assert.Equal(t, res, tt.res) + assert.Equal(t, ok, tt.matched) + }) + } +} + +func mockConfig() *Config { + c := Config{ + Resources: []*sentinel.Resource{ + { + Name: "test-dubbo", + Items: []*sentinel.Item{ + {MatchStrategy: sentinel.EXACT, Pattern: "/api/v1/test-dubbo/user"}, + {MatchStrategy: sentinel.REGEX, Pattern: "/api/v1/test-dubbo/user/*"}, + }, + }, + { + Name: "test-http", + Items: []*sentinel.Item{ + {MatchStrategy: sentinel.EXACT, Pattern: "/api/v1/http/foo"}, + {MatchStrategy: sentinel.EXACT, Pattern: "/api/v1/http/bar"}, + + {MatchStrategy: sentinel.REGEX, Pattern: "/api/v1/http/foo/*"}, + {MatchStrategy: sentinel.REGEX, Pattern: "/api/v1/http/bar/*"}, + }, + }, + }, + Rules: []*Rule{ + { + Enable: true, + FlowRule: flow.Rule{ + Threshold: 100, + StatIntervalInMs: 1000, + }, + }, + }, + } + return &c +} diff --git a/pkg/filter/sentinel/ratelimit/mock.go b/pkg/filter/sentinel/ratelimit/mock.go new file mode 100644 index 000000000..a70bb9d6c --- /dev/null +++ b/pkg/filter/sentinel/ratelimit/mock.go @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 ratelimit + +import ( + "github.com/alibaba/sentinel-golang/core/flow" +) + +import ( + pkgs "github.com/apache/dubbo-go-pixiu/pkg/filter/sentinel" +) + +func GetMockedRateLimitConfig() *Config { + c := Config{ + Resources: []*pkgs.Resource{ + { + Name: "test-dubbo", + Items: []*pkgs.Item{ + {MatchStrategy: pkgs.EXACT, Pattern: "/api/v1/test-dubbo/user"}, + {MatchStrategy: pkgs.REGEX, Pattern: "/api/v1/test-dubbo/user/*"}, + }, + }, + { + Name: "test-http", + Items: []*pkgs.Item{ + {MatchStrategy: pkgs.EXACT, Pattern: "/api/v1/http/foo"}, + {MatchStrategy: pkgs.EXACT, Pattern: "/api/v1/http/bar"}, + + {MatchStrategy: pkgs.REGEX, Pattern: "/api/v1/http/foo/*"}, + {MatchStrategy: pkgs.REGEX, Pattern: "/api/v1/http/bar/*"}, + }, + }, + }, + Rules: []*Rule{ + { + Enable: true, + FlowRule: flow.Rule{ + Threshold: 100, + StatIntervalInMs: 1000, + }, + }, + }, + } + return &c +} diff --git a/pixiu/pkg/filter/sentinel/ratelimit/rate_limit.go b/pkg/filter/sentinel/ratelimit/rate_limit.go similarity index 91% rename from pixiu/pkg/filter/sentinel/ratelimit/rate_limit.go rename to pkg/filter/sentinel/ratelimit/rate_limit.go index 31b1c835f..c7d3f7a37 100644 --- a/pixiu/pkg/filter/sentinel/ratelimit/rate_limit.go +++ b/pkg/filter/sentinel/ratelimit/rate_limit.go @@ -31,11 +31,11 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - contexthttp "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" - pkgs "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/sentinel" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http" + pkgs "github.com/apache/dubbo-go-pixiu/pkg/filter/sentinel" + "github.com/apache/dubbo-go-pixiu/pkg/logger" ) const ( diff --git a/pixiu/pkg/filter/sentinel/ratelimit/rate_limit_test.go b/pkg/filter/sentinel/ratelimit/rate_limit_test.go similarity index 88% rename from pixiu/pkg/filter/sentinel/ratelimit/rate_limit_test.go rename to pkg/filter/sentinel/ratelimit/rate_limit_test.go index f306c41dd..96d37f079 100644 --- a/pixiu/pkg/filter/sentinel/ratelimit/rate_limit_test.go +++ b/pkg/filter/sentinel/ratelimit/rate_limit_test.go @@ -28,9 +28,9 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/yaml" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/mock" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/common/yaml" + "github.com/apache/dubbo-go-pixiu/pkg/context/mock" ) func TestFilter(t *testing.T) { diff --git a/pixiu/pkg/filter/sentinel/regex.go b/pkg/filter/sentinel/regex.go similarity index 100% rename from pixiu/pkg/filter/sentinel/regex.go rename to pkg/filter/sentinel/regex.go diff --git a/pkg/filter/tracing/tracing.go b/pkg/filter/tracing/tracing.go new file mode 100644 index 000000000..650ae85bf --- /dev/null +++ b/pkg/filter/tracing/tracing.go @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 tracing + +import ( + "go.opentelemetry.io/otel/trace" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/server" +) + +const TraceIDInHeader = "dubbo-go-pixiu-trace-id" + +// nolint +func init() { + filter.RegisterHttpFilter(&Plugin{}) +} + +// tracerFilter is a filter for tracer +type ( + Plugin struct { + } + + TraceFilterFactory struct { + } + + TraceFilter struct { + span trace.Span + } + + Config struct{} +) + +func (ap *Plugin) Kind() string { + return constant.TracingFilter +} + +func (ap *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { + return &TraceFilterFactory{}, nil +} + +func (m *TraceFilterFactory) Config() interface{} { + return &Config{} +} + +func (m *TraceFilterFactory) Apply() error { + return nil +} + +func (mf *TraceFilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error { + t := &TraceFilter{} + chain.AppendDecodeFilters(t) + chain.AppendEncodeFilters(t) + return nil +} + +// Docode execute tracerFilter filter logic. +func (f *TraceFilter) Decode(hc *contexthttp.HttpContext) filter.FilterStatus { + spanName := "HTTP-" + hc.Request.Method + tr := server.GetTraceDriverManager().GetDriver().Tracer("tracing.HTTPProtocol") + ctxWithId, span := tr.Start(hc.Ctx, spanName) + + hc.Request = hc.Request.WithContext(ctxWithId) + f.span = span + return filter.Continue +} + +func (f *TraceFilter) Encode(hc *contexthttp.HttpContext) filter.FilterStatus { + f.span.End() + return filter.Continue +} diff --git a/pixiu/pkg/filter/traffic/models.go b/pkg/filter/traffic/models.go similarity index 100% rename from pixiu/pkg/filter/traffic/models.go rename to pkg/filter/traffic/models.go diff --git a/pkg/filter/traffic/traffic.go b/pkg/filter/traffic/traffic.go new file mode 100644 index 000000000..cf40496dc --- /dev/null +++ b/pkg/filter/traffic/traffic.go @@ -0,0 +1,196 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 traffic + +import ( + "errors" + "fmt" + "math/rand" + "strings" + "time" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +const ( + canaryByHeader CanaryHeaders = "canary-by-header" + unInitialize int = -1 +) + +func init() { + filter.RegisterHttpFilter(&Plugin{}) +} + +type ( + CanaryHeaders string + + Plugin struct { + } + + FilterFactory struct { + cfg *Config + record map[string]struct{} + rulesMap map[string][]*ClusterWrapper + } + + Filter struct { + weight int + Rules []*ClusterWrapper + } + + Config struct { + Traffics []*Cluster `yaml:"traffics" json:"traffics" mapstructure:"traffics"` + } + + ClusterWrapper struct { + weightCeil int + weightFloor int + header string + Cluster *Cluster + } + + Cluster struct { + Name string `yaml:"name" json:"name" mapstructure:"name"` + Router string `yaml:"router" json:"router" mapstructure:"router"` + CanaryByHeader string `yaml:"canary-by-header" json:"canary-by-header" mapstructure:"canary-by-header"` + CanaryWeight int `yaml:"canary-weight" json:"canary-weight" mapstructure:"canary-weight"` + } +) + +func (p *Plugin) Kind() string { + return constant.HTTPTrafficFilter +} + +func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) { + return &FilterFactory{ + cfg: &Config{}, + record: map[string]struct{}{}, + rulesMap: map[string][]*ClusterWrapper{}, + }, nil +} + +func (factory *FilterFactory) Config() interface{} { + return factory.cfg +} + +func (factory *FilterFactory) Apply() error { + var ( + router string + up int + ) + + for _, cluster := range factory.cfg.Traffics { + up = 0 + router = cluster.Router + if len(factory.rulesMap[router]) != 0 { + lastCeil := factory.rulesMap[router][len(factory.rulesMap[router])-1].weightCeil + if lastCeil != -1 { + up = lastCeil + } + } + + wp := &ClusterWrapper{ + Cluster: cluster, + weightCeil: -1, + weightFloor: -1, + } + if cluster.CanaryByHeader != "" { + if _, ok := factory.record[cluster.CanaryByHeader]; ok { + return errors.New("duplicate canary-by-header values") + } else { + factory.record[cluster.CanaryByHeader] = struct{}{} + wp.header = cluster.CanaryByHeader + } + } + if cluster.CanaryWeight > 0 && cluster.CanaryWeight <= 100 { + if up+cluster.CanaryWeight > 100 { + return fmt.Errorf("[dubbo-go-pixiu] clusters' weight sum more than 100 in %v service!", cluster.Router) + } else { + wp.weightFloor = up + up += cluster.CanaryWeight + wp.weightCeil = up + } + } + factory.rulesMap[router] = append(factory.rulesMap[router], wp) + } + return nil +} + +func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error { + f := &Filter{ + weight: unInitialize, + } + f.Rules = factory.rulesMatch(ctx.Request.RequestURI) + chain.AppendDecodeFilters(f) + return nil +} + +func (f *Filter) Decode(ctx *http.HttpContext) filter.FilterStatus { + if f.Rules != nil { + var cluster string + for _, wp := range f.Rules { + if f.trafficHeader(wp, ctx) { + cluster = wp.Cluster.Name + logger.Debugf("[dubbo-go-pixiu] execute traffic split to cluster %s", wp.Cluster.Name) + break + } + } + if cluster != "" { + ctx.Route.Cluster = cluster + } else if cluster == "" { + for _, wp := range f.Rules { + if f.trafficWeight(wp) { + ctx.Route.Cluster = wp.Cluster.Name + cluster = wp.Cluster.Name + logger.Debugf("[dubbo-go-pixiu] execute traffic split to cluster %s", cluster) + break + } + } + } + } else { + logger.Warnf("[dubbo-go-pixiu] execute traffic split fail because of empty rules.") + } + return filter.Continue +} + +func (f *Filter) trafficHeader(c *ClusterWrapper, ctx *http.HttpContext) bool { + return spiltHeader(ctx.Request, c.header) +} + +func (f *Filter) trafficWeight(c *ClusterWrapper) bool { + if f.weight == unInitialize { + rand.Seed(time.Now().UnixNano()) + f.weight = rand.Intn(100) + 1 + } + + return spiltWeight(f.weight, c.weightFloor, c.weightCeil) +} + +func (factory *FilterFactory) rulesMatch(path string) []*ClusterWrapper { + for key := range factory.rulesMap { + if strings.HasPrefix(path, key) { + return factory.rulesMap[key] + } + } + return nil +} diff --git a/pixiu/pkg/filterchain/network_filter_chain.go b/pkg/filterchain/network_filter_chain.go similarity index 93% rename from pixiu/pkg/filterchain/network_filter_chain.go rename to pkg/filterchain/network_filter_chain.go index 25f49a51a..cc6787ffc 100644 --- a/pixiu/pkg/filterchain/network_filter_chain.go +++ b/pkg/filterchain/network_filter_chain.go @@ -27,10 +27,10 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/yaml" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/common/yaml" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) type NetworkFilterChain struct { diff --git a/pkg/fuzz/README.md b/pkg/fuzz/README.md deleted file mode 100644 index 3d928010a..000000000 --- a/pkg/fuzz/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# Fuzzing Istio Code - -The Istio (Go) code base is fuzzed using native Go fuzzing. -For general docs on how to fuzz in Go, see [Getting started with fuzzing](https://go.dev/doc/tutorial/fuzz). - -## Writing a test - -Generally, writing a fuzz test for Istio is the same as any other Go program. -However, because most of our fuzzing is based on complex structs rather than the primitives Go supports natively, -the `pkg/fuzz` package contains a number of helpers to fuzz. - -Here is an example: - -```go -// Define a new fuzzer. Must have Fuzz prefix -func FuzzBuildHTTP(f *testing.F) { - fuzz.BaseCases(f) // Insert basic cases so a few trivial cases run in presubmit - f.Fuzz(func(t *testing.T, data []byte) { - fg := fuzz.New(t, data) - // Setup a few structs for testing - bundle := fuzz.Struct[trustdomain.Bundle](fg) - // This one has a custom validator - push := fuzz.Struct[*model.PushContext](fg, validatePush) - // *model.Proxy, and other types, implement the fuzz.Validator interface and already validate some basics. - node := fuzz.Struct[*model.Proxy](fg) - option := fuzz.Struct[Option](fg) - - // Run our actual test code. In this case, we are just checking nothing crashes. - // In other tests, explicit assertions may be helpful. - policies := push.AuthzPolicies.ListAuthorizationPolicies(node.ConfigNamespace, node.Metadata.Labels) - New(bundle, push, policies, option).BuildHTTP() - }) -} -``` - -## Running tests - -Fuzz tests can be run using standard Go tooling: - -```shell -go test ./path/to/pkg -v -run=^$ -fuzz=Fuzz -``` - -## CI testing - -Go fuzzers are run as part of standard unit tests against known test cases (from `f.Add` (which `fuzz.BaseCases` calls), or `testdata`). -For continuous fuzzing, [`OSS-Fuzz`](https://github.com/google/oss-fuzz) continually builds and runs the fuzzers and reports any failures. -These results are private to the Istio Product Security WG until disclosed. diff --git a/pkg/fuzz/util.go b/pkg/fuzz/util.go deleted file mode 100644 index 92ff44ca2..000000000 --- a/pkg/fuzz/util.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package fuzz - -import ( - "bytes" - "fmt" - "testing" -) - -import ( - fuzzheaders "github.com/AdaLogics/go-fuzz-headers" -) - -// Helper is a helper struct for fuzzing -type Helper struct { - cf *fuzzheaders.ConsumeFuzzer - t *testing.T -} - -type Validator interface { - // FuzzValidate returns true if the current struct is valid for fuzzing. - FuzzValidate() bool -} - -// New creates a new fuzz.Helper, capable of generating more complex types -func New(t *testing.T, data []byte) Helper { - return Helper{cf: fuzzheaders.NewConsumer(data), t: t} -} - -// Struct generates a Struct. Validation patterns can be passed in - if any return false, the fuzz case is skipped. -// Additionally, if the T implements Validator, it will implicitly be used. -func Struct[T any](h Helper, validators ...func(T) bool) T { - d := new(T) - if err := h.cf.GenerateStruct(d); err != nil { - h.t.Skip(err.Error()) - } - r := *d - validate(h, validators, r) - return r -} - -// Slice generates a slice of Structs -func Slice[T any](h Helper, count int, validators ...func(T) bool) []T { - if count < 0 { - // Make it easier to just pass fuzzer generated counts, typically with %max applied - count *= -1 - } - res := make([]T, 0, count) - for i := 0; i < count; i++ { - d := new(T) - if err := h.cf.GenerateStruct(d); err != nil { - h.t.Skip(err.Error()) - } - r := *d - validate(h, validators, r) - res = append(res, r) - } - return res -} - -func validate[T any](h Helper, validators []func(T) bool, r T) { - if fz, ok := any(r).(Validator); ok { - if !fz.FuzzValidate() { - h.t.Skip("struct didn't pass validator") - } - } - for i, v := range validators { - if !v(r) { - h.t.Skipf(fmt.Sprintf("struct didn't pass validator %d", i)) - } - } -} - -// BaseCases inserts a few trivial test cases to do a very brief sanity check of a test that relies on []byte inputs -func BaseCases(f Fuzzer) { - for _, c := range [][]byte{ - {}, - []byte("."), - bytes.Repeat([]byte("."), 1000), - } { - f.Add(c) - } -} - -// Fuzzer abstracts *testing.F to handle oss-fuzz's (temporary) workaround to support native fuzzing, -// which utilizes a custom type. -type Fuzzer interface { - Add(args ...any) -} diff --git a/pkg/hbone/README.md b/pkg/hbone/README.md deleted file mode 100644 index 94a501fce..000000000 --- a/pkg/hbone/README.md +++ /dev/null @@ -1,71 +0,0 @@ -# HTTP Based Overlay Network (HBONE) - -HTTP Based Overlay Network (HBONE) is the protocol used by Istio for communication between workloads in the mesh. -At a high level, the protocol consists of tunneling TCP connections over HTTP/2 CONNECT, over mTLS. - -## Specification - -TODO - -## Implementations - -### Clients - -#### CLI - -A CLI client is available using the `client` binary. - -Usage examples: - -```shell -go install ./pkg/test/echo/cmd/client -# Send request to 127.0.0.1:8080 (Note only IPs are supported) via an HBONE proxy on port 15008 -client --hbone-client-cert tests/testdata/certs/cert.crt --hbone-client-key tests/testdata/certs/cert.key \ - http://127.0.0.1:8080 \ - --hbone 127.0.0.1:15008 -``` - -#### Golang - -An (unstable) library to make HBONE connections is available at `pkg/hbone`. - -Usage example: - -```go -d := hbone.NewDialer(hbone.Config{ - ProxyAddress: "1.2.3.4:15008", - Headers: map[string][]string{ - "some-addition-metadata": {"test-value"}, - }, - TLS: nil, // TLS is strongly recommended in real world -}) -client, _ := d.Dial("tcp", testAddr) -client.Write([]byte("hello world")) -``` - -### Server - -#### Server CLI - -A CLI client is available using the `server` binary. - -Usage examples: - -```shell -go install ./pkg/test/echo/cmd/server -# Serve on port 15008 (default) with TLS -server --tls 15008 --crt tests/testdata/certs/cert.crt --key tests/testdata/certs/cert.key -``` - -#### Server Golang Library - -An (unstable) library to run an HBONE server is available at `pkg/hbone`. - -Usage example: - -```go -s := hbone.NewServer() -// TLS is strongly recommended in real world -l, _ := net.Listen("tcp", "0.0.0.0:15008") -s.Serve(l) -``` diff --git a/pkg/hbone/dialer.go b/pkg/hbone/dialer.go deleted file mode 100644 index 15ab914c9..000000000 --- a/pkg/hbone/dialer.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package hbone - -import ( - "context" - "crypto/tls" - "fmt" - "io" - "net" - "net/http" - "strings" - "sync" - "time" -) - -import ( - "golang.org/x/net/http2" - "golang.org/x/net/proxy" - istiolog "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/security/pkg/pki/util" -) - -var log = istiolog.RegisterScope("hbone", "", 0) - -// Config defines the configuration for a given dialer. All fields other than ProxyAddress are optional -type Config struct { - // ProxyAddress defines the address of the HBONE proxy we are connecting to - ProxyAddress string - Headers http.Header - TLS *tls.Config -} - -type Dialer interface { - proxy.Dialer - proxy.ContextDialer -} - -// NewDialer creates a Dialer that proxies connections over HBONE to the configured proxy. -func NewDialer(cfg Config) Dialer { - var transport *http2.Transport - if cfg.TLS != nil { - transport = &http2.Transport{ - TLSClientConfig: cfg.TLS, - } - } else { - transport = &http2.Transport{ - // For h2c - AllowHTTP: true, - DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) { - return net.Dial(network, addr) - }, - } - } - return &dialer{ - cfg: cfg, - transport: transport, - } -} - -type dialer struct { - cfg Config - transport *http2.Transport -} - -// DialContext connects to `address` via the HBONE proxy. -func (d *dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { - if network != "tcp" { - return net.Dial(network, address) - } - // TODO: use context - c, s := net.Pipe() - err := d.proxyTo(s, d.cfg, address) - if err != nil { - return nil, err - } - return c, nil -} - -func (d dialer) Dial(network, address string) (c net.Conn, err error) { - return d.DialContext(context.Background(), network, address) -} - -func (d *dialer) proxyTo(conn io.ReadWriteCloser, req Config, address string) error { - t0 := time.Now() - - url := "http://" + req.ProxyAddress - if req.TLS != nil { - url = "https://" + req.ProxyAddress - } - // Setup a pipe. We could just pass `conn` to `http.NewRequest`, but this has a few issues: - // * Less visibility into i/o - // * http will call conn.Close, which will close before we want to (finished writing response). - pr, pw := io.Pipe() - r, err := http.NewRequest(http.MethodConnect, url, pr) - if err != nil { - return fmt.Errorf("new request: %v", err) - } - r.Host = address - - // Initiate CONNECT. - log.Infof("initiate CONNECT to %v via %v", r.Host, url) - - resp, err := d.transport.RoundTrip(r) - if err != nil { - return fmt.Errorf("round trip: %v", err) - } - var remoteID string - if resp.TLS != nil && len(resp.TLS.PeerCertificates) > 0 { - ids, _ := util.ExtractIDs(resp.TLS.PeerCertificates[0].Extensions) - if len(ids) > 0 { - remoteID = ids[0] - } - } - log.WithLabels("host", r.Host, "remote", remoteID).Info("CONNECT established") - go func() { - defer conn.Close() - defer resp.Body.Close() - - wg := sync.WaitGroup{} - wg.Add(1) - go func() { - // handle upstream (hbone server) --> downstream (app) - copyBuffered(conn, resp.Body, log.WithLabels("name", "body to conn")) - wg.Done() - }() - // Copy from conn into the pipe, which will then be sent as part of the request - // handle upstream (hbone server) <-- downstream (app) - copyBuffered(pw, conn, log.WithLabels("name", "conn to pipe")) - - wg.Wait() - log.Info("stream closed in ", time.Since(t0)) - }() - - return nil -} - -// TLSDialWithDialer is an implementation of tls.DialWithDialer that accepts a generic Dialer -func TLSDialWithDialer(dialer Dialer, network, addr string, config *tls.Config) (*tls.Conn, error) { - return tlsDial(context.Background(), dialer, network, addr, config) -} - -func tlsDial(ctx context.Context, netDialer Dialer, network, addr string, config *tls.Config) (*tls.Conn, error) { - rawConn, err := netDialer.DialContext(ctx, network, addr) - if err != nil { - return nil, err - } - - colonPos := strings.LastIndex(addr, ":") - if colonPos == -1 { - colonPos = len(addr) - } - hostname := addr[:colonPos] - - if config == nil { - config = &tls.Config{} - } - // If no ServerName is set, infer the ServerName - // from the hostname we're connecting to. - if config.ServerName == "" { - // Make a copy to avoid polluting argument or default. - c := config.Clone() - c.ServerName = hostname - config = c - } - - conn := tls.Client(rawConn, config) - if err := conn.HandshakeContext(ctx); err != nil { - rawConn.Close() - return nil, err - } - return conn, nil -} diff --git a/pkg/hbone/dialer_test.go b/pkg/hbone/dialer_test.go deleted file mode 100644 index 3bc190377..000000000 --- a/pkg/hbone/dialer_test.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package hbone - -import ( - "net" - "testing" -) - -func newTCPServer(t testing.TB, data string) string { - n, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - t.Logf("opened listener on %v", n.Addr().String()) - go func() { - for { - c, err := n.Accept() - if err != nil { - log.Info(err) - return - } - log.Info("accepted connection") - c.Write([]byte(data)) - c.Close() - } - }() - t.Cleanup(func() { - n.Close() - }) - return n.Addr().String() -} - -func TestDialerError(t *testing.T) { - d := NewDialer(Config{ - ProxyAddress: "127.0.0.10:1", // Random address that should fail to dial - Headers: map[string][]string{ - "some-addition-metadata": {"test-value"}, - }, - TLS: nil, // No TLS for simplification - }) - _, err := d.Dial("tcp", "fake") - if err == nil { - t.Fatal("expected error, got none.") - } -} - -func TestDialer(t *testing.T) { - testAddr := newTCPServer(t, "hello") - proxy := newHBONEServer(t) - d := NewDialer(Config{ - ProxyAddress: proxy, - Headers: map[string][]string{ - "some-addition-metadata": {"test-value"}, - }, - TLS: nil, // No TLS for simplification - }) - send := func() { - client, err := d.Dial("tcp", testAddr) - if err != nil { - t.Fatal(err) - } - defer client.Close() - - go func() { - n, err := client.Write([]byte("hello world")) - log.Infof("wrote %v/%v", n, err) - }() - - buf := make([]byte, 8) - n, err := client.Read(buf) - if err != nil { - t.Fatalf("err with %v: %v", n, err) - } - if string(buf[:n]) != "hello" { - t.Fatalf("got unexpected buffer: %v", string(buf[:n])) - } - t.Logf("Read %v", string(buf[:n])) - } - // Make sure we can create multiple connections - send() - send() -} - -func newHBONEServer(t *testing.T) string { - s := NewServer() - l, err := net.Listen("tcp", "0.0.0.0:0") - if err != nil { - t.Fatal(err) - } - go func() { - _ = s.Serve(l) - }() - t.Cleanup(func() { - _ = l.Close() - }) - return l.Addr().String() -} diff --git a/pkg/hbone/server.go b/pkg/hbone/server.go deleted file mode 100644 index cc559dcdb..000000000 --- a/pkg/hbone/server.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package hbone - -import ( - "context" - "net" - "net/http" - "sync" - "time" -) - -import ( - "golang.org/x/net/http2" - "golang.org/x/net/http2/h2c" -) - -func NewServer() *http.Server { - // Need to set this to allow timeout on the read header - h1 := &http.Transport{ - ExpectContinueTimeout: 3 * time.Second, - } - h2, _ := http2.ConfigureTransports(h1) - h2.ReadIdleTimeout = 10 * time.Minute // TODO: much larger to support long-lived connections - h2.AllowHTTP = true - h2Server := &http2.Server{} - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodConnect { - if handleConnect(w, r) { - return - } - } else { - log.Errorf("non-CONNECT: %v", r.Method) - w.WriteHeader(http.StatusMethodNotAllowed) - } - }) - hs := &http.Server{ - Handler: h2c.NewHandler(handler, h2Server), - } - return hs -} - -func handleConnect(w http.ResponseWriter, r *http.Request) bool { - t0 := time.Now() - log.WithLabels("host", r.Host, "source", r.RemoteAddr).Info("Received CONNECT") - // Send headers back immediately so we can start getting the body - w.(http.Flusher).Flush() - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - dst, err := (&net.Dialer{}).DialContext(ctx, "tcp", r.Host) - if err != nil { - w.WriteHeader(http.StatusServiceUnavailable) - log.Errorf("failed to dial upstream: %v", err) - return true - } - log.Infof("Connected to %v", r.Host) - w.WriteHeader(http.StatusOK) - - wg := sync.WaitGroup{} - wg.Add(1) - go func() { - // downstream (hbone client) <-- upstream (app) - copyBuffered(w, dst, log.WithLabels("name", "dst to w")) - r.Body.Close() - wg.Done() - }() - // downstream (hbone client) --> upstream (app) - copyBuffered(dst, r.Body, log.WithLabels("name", "body to dst")) - wg.Wait() - log.Infof("connection closed in %v", time.Since(t0)) - return false -} diff --git a/pkg/hbone/util.go b/pkg/hbone/util.go deleted file mode 100644 index faf0fb66e..000000000 --- a/pkg/hbone/util.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package hbone - -import ( - "io" - "net" - "net/http" - "sync" - "time" -) - -import ( - istiolog "istio.io/pkg/log" -) - -// createBuffer to get a buffer. io.Copy uses 32k. -// experimental use shows ~20k max read with Firefox. -var bufferPoolCopy = sync.Pool{New: func() any { - return make([]byte, 0, 32*1024) -}} - -func copyBuffered(dst io.Writer, src io.Reader, log *istiolog.Scope) { - buf1 := bufferPoolCopy.Get().([]byte) - // nolint: staticcheck - defer bufferPoolCopy.Put(buf1) - bufCap := cap(buf1) - buf := buf1[0:bufCap:bufCap] - - // For netstack: src is a gonet.Conn, doesn't implement WriterTo. Dst is a net.TcpConn - and implements ReadFrom. - // CopyBuffered is the actual implementation of Copy and CopyBuffer. - // if buf is nil, one is allocated. - // Duplicated from io - - // This will prevent stats from working. - // If the reader has a WriteTo method, use it to do the copy. - // Avoids an allocation and a copy. - //if wt, ok := src.(io.WriterTo); ok { - // return wt.WriteTo(dst) - //} - // Similarly, if the writer has a ReadFrom method, use it to do the copy. - //if rt, ok := dst.(io.ReaderFrom); ok { - // return rt.ReadFrom(src) - //} - for { - if srcc, ok := src.(net.Conn); ok { - // Best effort - _ = srcc.SetReadDeadline(time.Now().Add(15 * time.Minute)) - } - nr, err := src.Read(buf) - log.Debugf("read %v/%v", nr, err) - if nr > 0 { // before dealing with the read error - nw, ew := dst.Write(buf[0:nr]) - log.Debugf("write %v/%v", nw, ew) - if f, ok := dst.(http.Flusher); ok { - f.Flush() - } - if nr != nw { // Should not happen - ew = io.ErrShortWrite - } - if ew != nil { - return - } - } - if err != nil { - // read is already closed - we need to close out - _ = closeWriter(dst) - return - } - } -} - -// CloseWriter is one of possible interfaces implemented by Out to send a FIN, without closing -// the input. Some writers only do this when Close is called. -type CloseWriter interface { - CloseWrite() error -} - -func closeWriter(dst io.Writer) error { - if cw, ok := dst.(CloseWriter); ok { - return cw.CloseWrite() - } - if c, ok := dst.(io.Closer); ok { - return c.Close() - } - if rw, ok := dst.(http.ResponseWriter); ok { - // Server side HTTP stream. For client side, FIN can be sent by closing the pipe (or - // request body). For server, the FIN will be sent when the handler returns - but - // this only happen after request is completed and body has been read. If server wants - // to send FIN first - while still reading the body - we are in trouble. - - // That means HTTP2 TCP servers provide no way to send a FIN from server, without - // having the request fully read. - - // This works for H2 with the current library - but very tricky, if not set as trailer. - rw.Header().Set("X-Close", "0") - rw.(http.Flusher).Flush() - return nil - } - log.Infof("Server out not Closer nor CloseWriter nor ResponseWriter: %v", dst) - return nil -} diff --git a/pkg/http/get.go b/pkg/http/get.go deleted file mode 100644 index 08abe13d7..000000000 --- a/pkg/http/get.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package http - -import ( - "bytes" - "fmt" - "io" - "net/http" - "time" -) - -const requestTimeout = time.Second * 1 // Default timeout. - -func DoHTTPGetWithTimeout(requestURL string, t time.Duration) (*bytes.Buffer, error) { - httpClient := &http.Client{ - Timeout: t, - } - - response, err := httpClient.Get(requestURL) - if err != nil { - return nil, err - } - defer response.Body.Close() - - if response.StatusCode != http.StatusOK { - return nil, fmt.Errorf("unexpected status %d", response.StatusCode) - } - - var b bytes.Buffer - if _, err := io.Copy(&b, response.Body); err != nil { - return nil, err - } - return &b, nil -} - -func DoHTTPGet(requestURL string) (*bytes.Buffer, error) { - return DoHTTPGetWithTimeout(requestURL, requestTimeout) -} diff --git a/pkg/http/headers/builder.go b/pkg/http/headers/builder.go deleted file mode 100644 index b29d9c380..000000000 --- a/pkg/http/headers/builder.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package headers - -import ( - "net/http" -) - -// Builder for HTTP headers. -type Builder struct { - headers http.Header -} - -// New Builder for HTTP headers. -func New() *Builder { - return &Builder{ - headers: make(http.Header), - } -} - -// Get returns the current value for the key. -func (b *Builder) Get(key string) string { - return b.headers.Get(key) -} - -// With sets the given header value. -func (b *Builder) With(key, value string) *Builder { - b.headers.Set(key, value) - return b -} - -// WithAuthz sets the Authorization header with the given token. -func (b *Builder) WithAuthz(token string) *Builder { - if token != "" { - return b.With(Authorization, "Bearer "+token) - } - return b -} - -// WithHost sets the Host header to the given value. -func (b *Builder) WithHost(host string) *Builder { - return b.With(Host, host) -} - -// WithXForwardedFor sets the origin IP of the request. -func (b *Builder) WithXForwardedFor(ip string) *Builder { - return b.With(XForwardedFor, ip) -} - -func (b *Builder) Build() http.Header { - if b == nil { - return nil - } - - return b.headers -} - -func (b *Builder) BuildTo(out http.Header) { - if b == nil { - return - } - - for k, v := range b.headers { - out[k] = v - } -} diff --git a/pkg/http/headers/wellknown.go b/pkg/http/headers/wellknown.go deleted file mode 100644 index 8d524ba4f..000000000 --- a/pkg/http/headers/wellknown.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package headers - -const ( - Host = "Host" - Authorization = "Authorization" - XForwardedFor = "X-Forwarded-For" - AccessControlRequestMethod = "Access-Control-Request-Method" - Origin = "Origin" - XForwardedProto = "X-Forwarded-Proto" -) diff --git a/pkg/istio-agent/README.md b/pkg/istio-agent/README.md deleted file mode 100644 index df2b39af8..000000000 --- a/pkg/istio-agent/README.md +++ /dev/null @@ -1,94 +0,0 @@ -# Istio Agent - -This document describes the internal architecture of Istio agent. - -## High Level overview - -![High Level overview](docs/overview.svg) - -At a high level, the Istio agent acts as an intermediate proxy between Istiod and Envoy. This is done -at two levels. For distributing workload certificates, Envoy will send [SDS](https://www.envoyproxy.io/docs/envoy/latest/configuration/security/secret) -requests to the agent, causing the agent to submit a CSR to the configured CA (generally Istiod). For other configuration, -Envoy will send [ADS](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/operations/dynamic_configuration#aggregated-xds-ads) -requests to the agent, which will be forwarded to the configured discovery server (general Istiod). - -## CA Flow - -istio-agent checks the presence of a socket file on the defined **socket path** `/var/run/secrets/workload-spiffe-uds/socket`. - -* If a socket is found, istio-agent will not start its own SDS Server and Envoy will be configured to use that socket as its source of cryptographic material. -* If a socket is not found, istio-agent then checks whether certificate files are present or not on the defined **certificate path** `/var/run/secrets/workload-spiffe-credentials`. If certificate files are found, istio-agent will start its own SDS Server, listening and serving these certificates on the defined **socket path** `/var/run/secrets/workload-spiffe-uds/socket`, - while also keeping file watchers on them. Envoy proxy then connects to istio-agent's SDS Server through the defined socket path and gets the cryptographic materials of the certificate files served by the SDS API. -* If istio-agent does not find either the socket, or the certificate files in their respective paths it will start its own SDS Server using a `caClient` to connect to istiod or an external CA, to fetch cryptographic materials (See Default CA Flow). - -![SDS decision flow](docs/sds-flow.svg) - -### Default CA Flow through istio-agent - -![CA Flow](docs/ca.svg) - -A single SDS request from Envoy goes through a few different layers in istio-agent. - -1. First, the request is handled by the SDS server. This is mostly just an intermediate translation layer exposing - the `SecretManager` to Envoy, without much business logic. For each resource requested by Envoy, the SDS server - will call `SecretManager.GenerateSecret(resourceName)` -1. When `GenerateSecret` is called, the `SecretManager` is expected to return a new certificate. This can occur in a few ways. - 1. The most common method (pictured above) is to sign a new certificate by calling the configured CA. Typically, this is Istiod. - 1. If the certificate is not yet expired, the `SecretManager` also can return a cache response. In practice, this would - only happen in cases where Envoy were to re-request a resource, which is fairly rare. - 1. `SecretManager` can also read certificates from files. When this is configured, no CA client is used. -1. The `caClient` will be configured to use either JWT or mTLS authentication. For JWT authentication, gRPC's `PerRPCCredentials` - is configured with a `TokenProvider` which handles the logic of adding the proper JWT to each request. mTLS is configured - by a tls.Config that points to files on disk. - -It should be noted there is a circular dependency with mTLS authentication; in order to fetch a certificate we need -a certificate. This can be handled in various ways: -* `GenerateSecret` may additionally write any signed certificates to disk, with `OUTPUT_CERTS` configured. -* Users may have external CA setups that pre-configure certificates. -* The CaClient can use JWT token for the initial setup, then switch to mTLS certificates. - -Note that `OUTPUT_CERTS` can be used to refresh certificates using previously provisioned certificates, by configuring -the ca client to use certificates written to the same directory we have configured them to be written to. - -## Authentication - -The agent supports two forms of authentication with the CA/discovery servers: mTLS and JWT. Varying deployment -topologies mix and match these two. - -For a standard Kubernetes deployment, both CA and discovery will use JWT authentication, with a token automatically -[generated and rotated by Kubernetes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-token-volume-projection). - -For discovery, the JWT token will be read directly from a file and sent as is. For CA, this logic is a bit more complex, -as the support for external CAs is more mature than external discovery servers. This supports some additional -configuration, a `CredentialFetcher` which allows fetching a token from places other than a file (for example, a local -metadata server), and a `TokenExchanger` which allows exchanging a token for another form to match the CA server requirements. - -For VMs, the standard flow is for the user to provision a short-lived JWT token onto the VM. After the initial -CSR, certificates are written to disk and mTLS is used for future requests. If the VM restarted, it would continue -to use the certificates written to disk, assuming the downtime is less than certificate expiration. This is why -the certificates are persisted to disk, rather than kept in memory like in the standard Kubernetes deployment. - -## Certificate Rotation - -The agent also handles rotating certificates near expiration. It does so by triggering a callback from the `SecretManager` to the SDS server -when a certificate is near expiration (configurable by `SECRET_GRACE_PERIOD_RATIO`, defaulting to half of the expiration). If the SDS server -is still interested in this certificate (ie, Envoy is still connected and requesting the certificate), the SDS server will send another request -to generate a new secret and push the updated certificate to Envoy. This ensures that we do not permanently watch certificates even after -Envoy has stopped requested them; if there are no subscriptions they update will be ignored. If Envoy later watches these certificates again, -a new one will be generated on demand. - -## Configuration - -| Variable | Description | -| - | - | -|CA_ADDR|Address of CA, defaults to discoveryAddress| -|CA_PROVIDER|Type of CA; supported values are GoogleCA or Citadel (although anything but GoogleCA will use Citadel); defaults to Citadel| -|PROV_CERT|certificates to be used for mTLS communication with control plane only; NOT for workload mTLS| -|OUTPUT_CERT|write all fetched certificates to some directory. Used to support applications that need certificates (Prometheus) as well as rotating mTLS control plane authentication.| -|FILE_MOUNTED_CERTS|completely disable CA path, exclusively use certs mounted into the pod with set certificate file locations| -|CREDENTIAL_FETCHER_TYPE|allows using custom credential fetcher, for VMs with existing identity| -|CREDENTIAL_IDENTITY_PROVIDER|just used to control the audience for VMs with existing identity| -|PROXY_XDS_VIA_AGENT|use istio-agent to proxy XDS. True for all use cases now, likely can be always-on now or soon| -|PROXY_XDS_DEBUG_VIA_AGENT|Offer XDS istio.io/debug API on agent's 15004 HTTP endpoint. (Requires PROXY_XDS_VIA_AGENT)| -|{XDS,CA}_ROOT_CA|explicitly configure root certificate path| -|PILOT_CERT_PROVIDER|just used to determine XDS/CA root certificate; redundant with {XDS,CA}_ROOT_CA.| diff --git a/pkg/istio-agent/agent.go b/pkg/istio-agent/agent.go deleted file mode 100644 index d3e3cdbbf..000000000 --- a/pkg/istio-agent/agent.go +++ /dev/null @@ -1,907 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istioagent - -import ( - "context" - "errors" - "fmt" - "io" - "math/rand" - "net" - "os" - "path" - "path/filepath" - "strings" - "sync" - "time" -) - -import ( - "github.com/cenkalti/backoff/v4" - bootstrapv3 "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "github.com/golang/protobuf/proto" // nolint: staticcheck - "google.golang.org/api/option" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - mesh "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/filewatcher" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/config" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/bootstrap" - "github.com/apache/dubbo-go-pixiu/pkg/bootstrap/platform" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - dnsClient "github.com/apache/dubbo-go-pixiu/pkg/dns/client" - dnsProto "github.com/apache/dubbo-go-pixiu/pkg/dns/proto" - "github.com/apache/dubbo-go-pixiu/pkg/envoy" - "github.com/apache/dubbo-go-pixiu/pkg/istio-agent/grpcxds" - "github.com/apache/dubbo-go-pixiu/pkg/security" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" - "github.com/apache/dubbo-go-pixiu/security/pkg/nodeagent/cache" - "github.com/apache/dubbo-go-pixiu/security/pkg/nodeagent/caclient" - citadel "github.com/apache/dubbo-go-pixiu/security/pkg/nodeagent/caclient/providers/citadel" - gca "github.com/apache/dubbo-go-pixiu/security/pkg/nodeagent/caclient/providers/google" - cas "github.com/apache/dubbo-go-pixiu/security/pkg/nodeagent/caclient/providers/google-cas" - "github.com/apache/dubbo-go-pixiu/security/pkg/nodeagent/sds" -) - -// To debug: -// curl -X POST localhost:15000/logging?config=trace - to see SendingDiscoveryRequest -// Breakpoints in secretcache.go GenerateSecret.. -// Note that istiod currently can't validate the JWT token unless it runs on k8s -// Main problem is the JWT validation check which hardcodes the k8s server address and token location. -// -// To test on a local machine, for debugging: -// -// kis exec $POD -- cat /run/secrets/istio-token/istio-token > var/run/secrets/tokens/istio-token -// kis port-forward $POD 15010:15010 & -// -// You can also copy the K8S CA and a token to be used to connect to k8s - but will need removing the hardcoded addr -// kis exec $POD -- cat /run/secrets/kubernetes.io/serviceaccount/{ca.crt,token} > var/run/secrets/kubernetes.io/serviceaccount/ -// -// Or disable the jwt validation while debugging SDS problems. -const ( - // Location of K8S CA root. - k8sCAPath = "./var/run/secrets/kubernetes.io/serviceaccount/ca.crt" - - // CitadelCACertPath is the directory for Citadel CA certificate. - // This is mounted from config map 'istio-ca-root-cert'. Part of startup, - // this may be replaced with ./etc/certs, if a root-cert.pem is found, to - // handle secrets mounted from non-citadel CAs. - CitadelCACertPath = "./var/run/secrets/istio" -) - -const ( - MetadataClientCertKey = "ISTIO_META_TLS_CLIENT_KEY" - MetadataClientCertChain = "ISTIO_META_TLS_CLIENT_CERT_CHAIN" - MetadataClientRootCert = "ISTIO_META_TLS_CLIENT_ROOT_CERT" -) - -// Agent contains the configuration of the agent, based on the injected -// environment: -// - SDS hostPath if node-agent was used -// - /etc/certs/key if Citadel or other mounted Secrets are used -// - root cert to use for connecting to XDS server -// - CA address, with proper defaults and detection -type Agent struct { - proxyConfig *mesh.ProxyConfig - - cfg *AgentOptions - secOpts *security.Options - envoyOpts envoy.ProxyConfig - - envoyAgent *envoy.Agent - envoyWaitCh chan error - - sdsServer *sds.Server - secretCache *cache.SecretManagerClient - - // Used when proxying envoy xds via istio-agent is enabled. - xdsProxy *XdsProxy - caFileWatcher filewatcher.FileWatcher - - // local DNS Server that processes DNS requests locally and forwards to upstream DNS if needed. - localDNSServer *dnsClient.LocalDNSServer - - // Signals true completion (e.g. with delayed graceful termination of Envoy) - wg sync.WaitGroup -} - -// AgentOptions contains additional config for the agent, not included in ProxyConfig. -// Most are from env variables ( still experimental ) or for testing only. -// Eventually most non-test settings should graduate to ProxyConfig -// Please don't add 100 parameters to the NewAgent function (or any other)! -type AgentOptions struct { - // ProxyXDSDebugViaAgent if true will listen on 15004 and forward queries - // to XDS istio.io/debug. (Requires ProxyXDSViaAgent). - ProxyXDSDebugViaAgent bool - // Port value for the debugging endpoint. - ProxyXDSDebugViaAgentPort int - // DNSCapture indicates if the XDS proxy has dns capture enabled or not - // This option will not be considered if proxyXDSViaAgent is false. - DNSCapture bool - // DNSAddr is the DNS capture address - DNSAddr string - // ProxyType is the type of proxy we are configured to handle - ProxyType model.NodeType - // ProxyNamespace to use for local dns resolution - ProxyNamespace string - // ProxyDomain is the DNS domain associated with the proxy (assumed - // to include the namespace as well) (for local dns resolution) - ProxyDomain string - // Node identifier used by Envoy - ServiceNode string - - // XDSRootCerts is the location of the root CA for the XDS connection. Used for setting platform certs or - // using custom roots. - XDSRootCerts string - - // CARootCerts of the location of the root CA for the CA connection. Used for setting platform certs or - // using custom roots. - CARootCerts string - - // Extra headers to add to the XDS connection. - XDSHeaders map[string]string - - // Is the proxy an IPv6 proxy - IsIPv6 bool - - // Path to local UDS to communicate with Envoy - XdsUdsPath string - - // Ability to retrieve ProxyConfig dynamically through XDS - EnableDynamicProxyConfig bool - - // All of the proxy's IP Addresses - ProxyIPAddresses []string - - // Enables dynamic generation of bootstrap. - EnableDynamicBootstrap bool - - // Envoy status port (that circles back to the agent status port). Really belongs to the proxy config. - // Cannot be eradicated because mistakes have been made. - EnvoyStatusPort int - - // Envoy prometheus port that circles back to its admin port for prom endpoint. Really belongs to the - // proxy config. - EnvoyPrometheusPort int - - MinimumDrainDuration time.Duration - - ExitOnZeroActiveConnections bool - - // Cloud platform - Platform platform.Environment - - // GRPCBootstrapPath if set will generate a file compatible with GRPC_XDS_BOOTSTRAP - GRPCBootstrapPath string - - // Disables all envoy agent features - DisableEnvoy bool - DownstreamGrpcOptions []grpc.ServerOption - - IstiodSAN string - - WASMInsecureRegistries []string -} - -// NewAgent hosts the functionality for local SDS and XDS. This consists of the local SDS server and -// associated clients to sign certificates (when not using files), and the local XDS proxy (including -// health checking for VMs and DNS proxying). -func NewAgent(proxyConfig *mesh.ProxyConfig, agentOpts *AgentOptions, sopts *security.Options, eopts envoy.ProxyConfig) *Agent { - return &Agent{ - proxyConfig: proxyConfig, - cfg: agentOpts, - secOpts: sopts, - envoyOpts: eopts, - caFileWatcher: filewatcher.NewWatcher(), - } -} - -// EnvoyDisabled if true indicates calling Run will not run and wait for Envoy. -func (a *Agent) EnvoyDisabled() bool { - return a.envoyOpts.TestOnly || a.cfg.DisableEnvoy -} - -// WaitForSigterm if true indicates calling Run will block until SIGTERM or SIGNT is received. -func (a *Agent) WaitForSigterm() bool { - return a.EnvoyDisabled() && !a.envoyOpts.TestOnly -} - -func (a *Agent) generateNodeMetadata() (*model.Node, error) { - provCert, err := a.FindRootCAForXDS() - if err != nil { - return nil, fmt.Errorf("failed to find root CA cert for XDS: %v", err) - } - - if provCert == "" { - // Envoy only supports load from file. If we want to use system certs, use best guess - // To be more correct this could lookup all the "well known" paths but this is extremely \ - // unlikely to run on a non-debian based machine, and if it is it can be explicitly configured - provCert = "/etc/ssl/certs/ca-certificates.crt" - } - var pilotSAN []string - if a.proxyConfig.ControlPlaneAuthPolicy == mesh.AuthenticationPolicy_MUTUAL_TLS { - // Obtain Pilot SAN, using DNS. - pilotSAN = []string{config.GetPilotSan(a.proxyConfig.DiscoveryAddress)} - } - - return bootstrap.GetNodeMetaData(bootstrap.MetadataOptions{ - ID: a.cfg.ServiceNode, - Envs: os.Environ(), - Platform: a.cfg.Platform, - InstanceIPs: a.cfg.ProxyIPAddresses, - StsPort: a.secOpts.STSPort, - ProxyConfig: a.proxyConfig, - PilotSubjectAltName: pilotSAN, - OutlierLogPath: a.envoyOpts.OutlierLogPath, - ProvCert: provCert, - EnvoyPrometheusPort: a.cfg.EnvoyPrometheusPort, - EnvoyStatusPort: a.cfg.EnvoyStatusPort, - ExitOnZeroActiveConnections: a.cfg.ExitOnZeroActiveConnections, - XDSRootCert: a.cfg.XDSRootCerts, - }) -} - -func (a *Agent) initializeEnvoyAgent(ctx context.Context) error { - node, err := a.generateNodeMetadata() - if err != nil { - return fmt.Errorf("failed to generate bootstrap metadata: %v", err) - } - - log.Infof("Pilot SAN: %v", node.Metadata.PilotSubjectAltName) - - // Note: the cert checking still works, the generated file is updated if certs are changed. - // We just don't save the generated file, but use a custom one instead. Pilot will keep - // monitoring the certs and restart if the content of the certs changes. - if len(a.proxyConfig.CustomConfigFile) > 0 { - // there is a custom configuration. Don't write our own config - but keep watching the certs. - a.envoyOpts.ConfigPath = a.proxyConfig.CustomConfigFile - a.envoyOpts.ConfigCleanup = false - } else { - out, err := bootstrap.New(bootstrap.Config{ - Node: node, - }).CreateFileForEpoch(0) - if err != nil { - return fmt.Errorf("failed to generate bootstrap config: %v", err) - } - a.envoyOpts.ConfigPath = out - a.envoyOpts.ConfigCleanup = true - } - - // Back-fill envoy options from proxy config options - a.envoyOpts.BinaryPath = a.proxyConfig.BinaryPath - a.envoyOpts.AdminPort = a.proxyConfig.ProxyAdminPort - a.envoyOpts.DrainDuration = a.proxyConfig.DrainDuration - a.envoyOpts.ParentShutdownDuration = a.proxyConfig.ParentShutdownDuration - a.envoyOpts.Concurrency = a.proxyConfig.Concurrency.GetValue() - - // Checking only uid should be sufficient - but tests also run as root and - // will break due to permission errors if we start envoy as 1337. - // This is a mode used for permission-less docker, where iptables can't be - // used. - a.envoyOpts.AgentIsRoot = os.Getuid() == 0 && strings.HasSuffix(a.cfg.DNSAddr, ":53") - - envoyProxy := envoy.NewProxy(a.envoyOpts) - - drainDuration := a.proxyConfig.TerminationDrainDuration.AsDuration() - localHostAddr := localHostIPv4 - if a.cfg.IsIPv6 { - localHostAddr = localHostIPv6 - } - a.envoyAgent = envoy.NewAgent(envoyProxy, drainDuration, a.cfg.MinimumDrainDuration, localHostAddr, - int(a.proxyConfig.ProxyAdminPort), a.cfg.EnvoyStatusPort, a.cfg.EnvoyPrometheusPort, a.cfg.ExitOnZeroActiveConnections) - a.envoyWaitCh = make(chan error, 1) - if a.cfg.EnableDynamicBootstrap { - // Simulate an xDS request for a bootstrap - a.wg.Add(1) - go func() { - defer a.wg.Done() - - // wait indefinitely and keep retrying with jittered exponential backoff - backoff := 500 - max := 30000 - retries: - for { - // handleStream hands on to request after exit, so create a fresh one instead. - request := &bootstrapDiscoveryRequest{ - node: node, - envoyWaitCh: a.envoyWaitCh, - envoyUpdate: envoyProxy.UpdateConfig, - } - _ = a.xdsProxy.handleStream(request) - select { - case <-a.envoyWaitCh: - break retries - default: - } - delay := time.Duration(rand.Int()%backoff) * time.Millisecond - log.Infof("retrying bootstrap discovery request with backoff: %v", delay) - select { - case <-ctx.Done(): - break retries - case <-time.After(delay): - } - if backoff < max/2 { - backoff *= 2 - } else { - backoff = max - } - } - }() - } else { - close(a.envoyWaitCh) - } - return nil -} - -type bootstrapDiscoveryRequest struct { - node *model.Node - envoyWaitCh chan error - envoyUpdate func(data []byte) error - sent bool - received bool -} - -// Send refers to a request from the xDS proxy. -func (b *bootstrapDiscoveryRequest) Send(resp *discovery.DiscoveryResponse) error { - if resp.TypeUrl == v3.BootstrapType && !b.received { - b.received = true - if len(resp.Resources) != 1 { - b.envoyWaitCh <- fmt.Errorf("unexpected number of bootstraps: %d", len(resp.Resources)) - return nil - } - var bs bootstrapv3.Bootstrap - if err := resp.Resources[0].UnmarshalTo(&bs); err != nil { - b.envoyWaitCh <- fmt.Errorf("failed to unmarshal bootstrap: %v", err) - return nil - } - by, err := protomarshal.MarshalIndent(&bs, " ") - if err != nil { - b.envoyWaitCh <- fmt.Errorf("failed to marshal bootstrap as JSON: %v", err) - return nil - } - if err := b.envoyUpdate(by); err != nil { - b.envoyWaitCh <- fmt.Errorf("failed to update bootstrap from discovery: %v", err) - return nil - } - close(b.envoyWaitCh) - } - return nil -} - -// Recv Receive refers to a request to the xDS proxy. -func (b *bootstrapDiscoveryRequest) Recv() (*discovery.DiscoveryRequest, error) { - if b.sent { - <-b.envoyWaitCh - return nil, io.EOF - } - b.sent = true - return &discovery.DiscoveryRequest{ - TypeUrl: v3.BootstrapType, - Node: bootstrap.ConvertNodeToXDSNode(b.node), - }, nil -} - -func (b *bootstrapDiscoveryRequest) Context() context.Context { return context.Background() } - -// Run is a non-blocking call which returns either an error or a function to await for completion. -func (a *Agent) Run(ctx context.Context) (func(), error) { - var err error - if err = a.initLocalDNSServer(); err != nil { - return nil, fmt.Errorf("failed to start local DNS server: %v", err) - } - - socketExists, err := checkSocket(ctx, security.WorkloadIdentitySocketPath) - if err != nil { - return nil, fmt.Errorf("failed to check SDS socket: %v", err) - } - - if socketExists { - log.Info("SDS socket found. Istio SDS Server won't be started") - } else { - log.Info("SDS socket not found. Starting Istio SDS Server") - err = a.initSdsServer() - if err != nil { - return nil, fmt.Errorf("failed to start SDS server: %v", err) - } - } - - a.xdsProxy, err = initXdsProxy(a) - if err != nil { - return nil, fmt.Errorf("failed to start xds proxy: %v", err) - } - if a.cfg.ProxyXDSDebugViaAgent { - err = a.xdsProxy.initDebugInterface(a.cfg.ProxyXDSDebugViaAgentPort) - if err != nil { - return nil, fmt.Errorf("failed to start istio tap server: %v", err) - } - } - - if a.cfg.GRPCBootstrapPath != "" { - if err := a.generateGRPCBootstrap(); err != nil { - return nil, fmt.Errorf("failed generating gRPC XDS bootstrap: %v", err) - } - } - - rootCAForXDS, err := a.FindRootCAForXDS() - if err != nil { - return nil, fmt.Errorf("failed to find root XDS CA: %v", err) - } - go a.caFileWatcherHandler(ctx, rootCAForXDS) - - if !a.EnvoyDisabled() { - err = a.initializeEnvoyAgent(ctx) - if err != nil { - return nil, fmt.Errorf("failed to start envoy agent: %v", err) - } - - a.wg.Add(1) - go func() { - defer a.wg.Done() - - if a.cfg.EnableDynamicBootstrap { - start := time.Now() - var err error - select { - case err = <-a.envoyWaitCh: - case <-ctx.Done(): - // Early cancellation before envoy started. - return - } - if err != nil { - log.Errorf("failed to write updated envoy bootstrap: %v", err) - return - } - log.Infof("received server-side bootstrap in %v", time.Since(start)) - } - - // This is a blocking call for graceful termination. - a.envoyAgent.Run(ctx) - }() - } else if a.WaitForSigterm() { - // wait for SIGTERM and perform graceful shutdown - a.wg.Add(1) - go func() { - defer a.wg.Done() - <-ctx.Done() - }() - } - return a.wg.Wait, nil -} - -func (a *Agent) initSdsServer() error { - var err error - if security.CheckWorkloadCertificate(security.WorkloadIdentityCertChainPath, security.WorkloadIdentityKeyPath, security.WorkloadIdentityRootCertPath) { - log.Info("workload certificate files detected, creating secret manager without caClient") - a.secOpts.RootCertFilePath = security.WorkloadIdentityRootCertPath - a.secOpts.CertChainFilePath = security.WorkloadIdentityCertChainPath - a.secOpts.KeyFilePath = security.WorkloadIdentityKeyPath - - a.secretCache, err = cache.NewSecretManagerClient(nil, a.secOpts) - if err != nil { - return fmt.Errorf("failed to start workload secret manager %v", err) - } - } else { - a.secretCache, err = a.newSecretManager() - if err != nil { - return fmt.Errorf("failed to start workload secret manager %v", err) - } - } - - if err != nil { - return fmt.Errorf("failed to start workload secret manager %v", err) - } - if a.cfg.DisableEnvoy { - // For proxyless we don't need an SDS server, but still need the keys and - // we need them refreshed periodically. - // - // This is based on the code from newSDSService, but customized to have explicit rotation. - go func() { - st := a.secretCache - st.RegisterSecretHandler(func(resourceName string) { - // The secret handler is called when a secret should be renewed, after invalidating the cache. - // The handler does not call GenerateSecret - it is a side-effect of the SDS generate() method, which - // is called by sdsServer.OnSecretUpdate, which triggers a push and eventually calls sdsservice.Generate - // TODO: extract the logic to detect expiration time, and use a simpler code to rotate to files. - _, _ = a.getWorkloadCerts(st) - }) - _, _ = a.getWorkloadCerts(st) - }() - } else { - pkpConf := a.proxyConfig.GetPrivateKeyProvider() - a.sdsServer = sds.NewServer(a.secOpts, a.secretCache, pkpConf) - a.secretCache.RegisterSecretHandler(a.sdsServer.OnSecretUpdate) - } - - return nil -} - -// getWorkloadCerts will attempt to get a cert, with infinite exponential backoff -// It will not return until both workload cert and root cert are generated. -// -// TODO: evaluate replacing the STS server with a file data source, to simplify Envoy config -func (a *Agent) getWorkloadCerts(st *cache.SecretManagerClient) (sk *security.SecretItem, err error) { - b := backoff.NewExponentialBackOff() - b.MaxElapsedTime = 0 - - for { - sk, err = st.GenerateSecret(security.WorkloadKeyCertResourceName) - if err == nil { - break - } - log.Warnf("failed to get certificate: %v", err) - time.Sleep(b.NextBackOff()) - } - for { - _, err := st.GenerateSecret(security.RootCertReqResourceName) - if err == nil { - break - } - log.Warnf("failed to get root certificate: %v", err) - time.Sleep(b.NextBackOff()) - } - return -} - -func (a *Agent) caFileWatcherHandler(ctx context.Context, caFile string) { - if err := a.caFileWatcher.Add(caFile); err != nil { - log.Warnf("Failed to add file watcher %s, caFile", caFile) - } - - log.Debugf("Add CA file %s watcher", caFile) - for { - select { - case gotEvent := <-a.caFileWatcher.Events(caFile): - log.Debugf("Receive file %s event %v", caFile, gotEvent) - if err := a.xdsProxy.InitIstiodDialOptions(a); err != nil { - log.Warnf("Failed to init xds proxy dial options") - } - case <-ctx.Done(): - return - } - } -} - -func (a *Agent) initLocalDNSServer() (err error) { - // we don't need dns server on gateways - if a.cfg.DNSCapture && a.cfg.ProxyType == model.SidecarProxy { - if a.localDNSServer, err = dnsClient.NewLocalDNSServer(a.cfg.ProxyNamespace, a.cfg.ProxyDomain, a.cfg.DNSAddr); err != nil { - return err - } - a.localDNSServer.StartDNS() - } - return nil -} - -func (a *Agent) generateGRPCBootstrap() error { - // generate metadata - node, err := a.generateNodeMetadata() - if err != nil { - return fmt.Errorf("failed generating node metadata: %v", err) - } - - if err := os.MkdirAll(filepath.Dir(a.cfg.GRPCBootstrapPath), 0o700); err != nil { - return err - } - - _, err = grpcxds.GenerateBootstrapFile(grpcxds.GenerateBootstrapOptions{ - Node: node, - XdsUdsPath: a.cfg.XdsUdsPath, - DiscoveryAddress: a.proxyConfig.DiscoveryAddress, - CertDir: a.secOpts.OutputKeyCertToDir, - }, a.cfg.GRPCBootstrapPath) - if err != nil { - return err - } - return nil -} - -func (a *Agent) Check() (err error) { - // we dont need dns server on gateways - if a.cfg.DNSCapture && a.cfg.ProxyType == model.SidecarProxy { - if !a.localDNSServer.IsReady() { - return errors.New("istio DNS capture is turned ON and DNS lookup table is not ready yet") - } - } - return nil -} - -func (a *Agent) GetDNSTable() *dnsProto.NameTable { - if a.localDNSServer != nil { - nt := a.localDNSServer.NameTable() - nt = proto.Clone(nt).(*dnsProto.NameTable) - a.localDNSServer.BuildAlternateHosts(nt, func(althosts map[string]struct{}, ipv4 []net.IP, ipv6 []net.IP, _ []string) { - for host := range althosts { - if _, exists := nt.Table[host]; !exists { - adresses := make([]string, len(ipv4)+len(ipv6)) - for _, ip := range ipv4 { - adresses = append(adresses, ip.String()) - } - for _, ip := range ipv6 { - adresses = append(adresses, ip.String()) - } - nt.Table[host] = &dnsProto.NameTable_NameInfo{ - Ips: adresses, - Registry: "Kubernetes", - } - } - } - }) - return nt - } - return nil -} - -func (a *Agent) Close() { - if a.xdsProxy != nil { - a.xdsProxy.close() - } - if a.localDNSServer != nil { - a.localDNSServer.Close() - } - if a.sdsServer != nil { - a.sdsServer.Stop() - } - if a.secretCache != nil { - a.secretCache.Close() - } - if a.caFileWatcher != nil { - _ = a.caFileWatcher.Close() - } -} - -// FindRootCAForXDS determines the root CA to be configured in bootstrap file. -// It may be different from the CA for the cert server - which is based on CA_ADDR -// In addition it deals with the case the XDS server is on port 443, expected with a proper cert. -// /etc/ssl/certs/ca-certificates.crt -func (a *Agent) FindRootCAForXDS() (string, error) { - var rootCAPath string - - if a.cfg.XDSRootCerts == security.SystemRootCerts { - // Special case input for root cert configuration to use system root certificates - return "", nil - } else if a.cfg.XDSRootCerts != "" { - // Using specific platform certs or custom roots - rootCAPath = a.cfg.XDSRootCerts - } else if fileExists(security.DefaultRootCertFilePath) { - // Old style - mounted cert. This is used for XDS auth only, - // not connecting to CA_ADDR because this mode uses external - // agent (Secret refresh, etc) - return security.DefaultRootCertFilePath, nil - } else if a.secOpts.PilotCertProvider == constants.CertProviderKubernetes { - // Using K8S - this is likely incorrect, may work by accident (https://github.com/istio/istio/issues/22161) - rootCAPath = k8sCAPath - } else if a.secOpts.ProvCert != "" { - // This was never completely correct - PROV_CERT are only intended for auth with CA_ADDR, - // and should not be involved in determining the root CA. - // For VMs, the root cert file used to auth may be populated afterwards. - // Thus, return directly here and skip checking for existence. - return a.secOpts.ProvCert + "/root-cert.pem", nil - } else if a.secOpts.FileMountedCerts { - // FileMountedCerts - Load it from Proxy Metadata. - rootCAPath = a.proxyConfig.ProxyMetadata[MetadataClientRootCert] - } else if a.secOpts.PilotCertProvider == constants.CertProviderNone { - return "", fmt.Errorf("root CA file for XDS required but configured provider as none") - } else { - // PILOT_CERT_PROVIDER - default is istiod - // This is the default - a mounted config map on K8S - rootCAPath = path.Join(CitadelCACertPath, constants.CACertNamespaceConfigMapDataName) - } - - // Additional checks for root CA cert existence. Fail early, instead of obscure envoy errors - if fileExists(rootCAPath) { - return rootCAPath, nil - } - - return "", fmt.Errorf("root CA file for XDS does not exist %s", rootCAPath) -} - -// GetKeyCertsForXDS return the key cert files path for connecting with xds. -func (a *Agent) GetKeyCertsForXDS() (string, string) { - var key, cert string - if a.secOpts.ProvCert != "" { - key, cert = getKeyCertInner(a.secOpts.ProvCert) - } else if a.secOpts.FileMountedCerts { - key = a.proxyConfig.ProxyMetadata[MetadataClientCertKey] - cert = a.proxyConfig.ProxyMetadata[MetadataClientCertChain] - } - return key, cert -} - -func fileExists(path string) bool { - if fi, err := os.Stat(path); err == nil && fi.Mode().IsRegular() { - return true - } - return false -} - -func socketFileExists(path string) bool { - if fi, err := os.Stat(path); err == nil && !fi.Mode().IsRegular() { - return true - } - return false -} - -// Checks whether the socket exists and is responsive. -// If it doesn't exist, returns (false, nil) -// If it exists and is NOT responsive, tries to delete the socket file. -// If it can be deleted, returns (false, nil). -// If it cannot be deleted, returns (false, error). -// Otherwise, returns (true, nil) -func checkSocket(ctx context.Context, socketPath string) (bool, error) { - socketExists := socketFileExists(socketPath) - if !socketExists { - return false, nil - } - - err := socketHealthCheck(ctx, socketPath) - if err != nil { - log.Debugf("SDS socket detected but not healthy: %v", err) - err = os.Remove(socketPath) - if err != nil { - return false, fmt.Errorf("existing SDS socket could not be removed: %v", err) - } - return false, nil - } - - return true, nil -} - -func socketHealthCheck(ctx context.Context, socketPath string) error { - ctx, cancel := context.WithDeadline(ctx, time.Now().Add(time.Second)) - defer cancel() - - conn, err := grpc.DialContext(ctx, fmt.Sprintf("unix:%s", socketPath), - grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.FailOnNonTempDialError(true), - grpc.WithReturnConnectionError(), - grpc.WithBlock(), - ) - if err != nil { - return err - } - conn.Close() - - return nil -} - -// FindRootCAForCA Find the root CA to use when connecting to the CA (Istiod or external). -func (a *Agent) FindRootCAForCA() (string, error) { - var rootCAPath string - - if a.cfg.CARootCerts == security.SystemRootCerts { - return "", nil - } else if a.cfg.CARootCerts != "" { - rootCAPath = a.cfg.CARootCerts - } else if a.secOpts.PilotCertProvider == constants.CertProviderKubernetes { - // Using K8S - this is likely incorrect, may work by accident. - // API is GA. - rootCAPath = k8sCAPath // ./var/run/secrets/kubernetes.io/serviceaccount/ca.crt - } else if a.secOpts.PilotCertProvider == constants.CertProviderCustom { - rootCAPath = security.DefaultRootCertFilePath // ./etc/certs/root-cert.pem - } else if a.secOpts.ProvCert != "" { - // This was never completely correct - PROV_CERT are only intended for auth with CA_ADDR, - // and should not be involved in determining the root CA. - // For VMs, the root cert file used to auth may be populated afterwards. - // Thus, return directly here and skip checking for existence. - return a.secOpts.ProvCert + "/root-cert.pem", nil - } else if a.secOpts.PilotCertProvider == constants.CertProviderNone { - return "", fmt.Errorf("root CA file for CA required but configured provider as none") - } else { - // This is the default - a mounted config map on K8S - rootCAPath = path.Join(CitadelCACertPath, constants.CACertNamespaceConfigMapDataName) - // or: "./var/run/secrets/istio/root-cert.pem" - } - - // Additional checks for root CA cert existence. - if fileExists(rootCAPath) { - return rootCAPath, nil - } - - return "", fmt.Errorf("root CA file for CA does not exist %s", rootCAPath) -} - -// getKeyCertsForXDS return the key cert files path for connecting with CA server. -func (a *Agent) getKeyCertsForCA() (string, string) { - var key, cert string - if a.secOpts.ProvCert != "" { - key, cert = getKeyCertInner(a.secOpts.ProvCert) - } - return key, cert -} - -func getKeyCertInner(certPath string) (string, string) { - key := path.Join(certPath, constants.KeyFilename) - cert := path.Join(certPath, constants.CertChainFilename) - return key, cert -} - -// newSecretManager creates the SecretManager for workload secrets -func (a *Agent) newSecretManager() (*cache.SecretManagerClient, error) { - // If proxy is using file mounted certs, we do not have to connect to CA. - if a.secOpts.FileMountedCerts { - log.Info("Workload is using file mounted certificates. Skipping connecting to CA") - return cache.NewSecretManagerClient(nil, a.secOpts) - } - - log.Infof("CA Endpoint %s, provider %s", a.secOpts.CAEndpoint, a.secOpts.CAProviderName) - - // TODO: this should all be packaged in a plugin, possibly with optional compilation. - if a.secOpts.CAProviderName == security.GoogleCAProvider { - // Use a plugin to an external CA - this has direct support for the K8S JWT token - // This is only used if the proper env variables are injected - otherwise the existing Citadel or Istiod will be - // used. - caClient, err := gca.NewGoogleCAClient(a.secOpts.CAEndpoint, true, caclient.NewCATokenProvider(a.secOpts)) - if err != nil { - return nil, err - } - return cache.NewSecretManagerClient(caClient, a.secOpts) - } else if a.secOpts.CAProviderName == security.GoogleCASProvider { - // Use a plugin - caClient, err := cas.NewGoogleCASClient(a.secOpts.CAEndpoint, - option.WithGRPCDialOption(grpc.WithPerRPCCredentials(caclient.NewCATokenProvider(a.secOpts)))) - if err != nil { - return nil, err - } - return cache.NewSecretManagerClient(caClient, a.secOpts) - } - - // Using citadel CA - var tlsOpts *citadel.TLSOptions - var err error - // Special case: if Istiod runs on a secure network, on the default port, don't use TLS - // TODO: may add extra cases or explicit settings - but this is a rare use cases, mostly debugging - if strings.HasSuffix(a.secOpts.CAEndpoint, ":15010") { - log.Warn("Debug mode or IP-secure network") - } else { - tlsOpts = &citadel.TLSOptions{} - tlsOpts.RootCert, err = a.FindRootCAForCA() - if err != nil { - return nil, fmt.Errorf("failed to find root CA cert for CA: %v", err) - } - - if tlsOpts.RootCert == "" { - log.Infof("Using CA %s cert with system certs", a.secOpts.CAEndpoint) - } else if _, err := os.Stat(tlsOpts.RootCert); os.IsNotExist(err) { - log.Fatalf("invalid config - %s missing a root certificate %s", a.secOpts.CAEndpoint, tlsOpts.RootCert) - } else { - log.Infof("Using CA %s cert with certs: %s", a.secOpts.CAEndpoint, tlsOpts.RootCert) - } - - tlsOpts.Key, tlsOpts.Cert = a.getKeyCertsForCA() - } - - // Will use TLS unless the reserved 15010 port is used ( istiod on an ipsec/secure VPC) - // rootCert may be nil - in which case the system roots are used, and the CA is expected to have public key - // Otherwise assume the injection has mounted /etc/certs/root-cert.pem - caClient, err := citadel.NewCitadelClient(a.secOpts, tlsOpts) - if err != nil { - return nil, err - } - - return cache.NewSecretManagerClient(caClient, a.secOpts) -} - -// GRPCBootstrapPath returns the most recently generated gRPC bootstrap or nil if there is none. -func (a *Agent) GRPCBootstrapPath() string { - return a.cfg.GRPCBootstrapPath -} diff --git a/pkg/istio-agent/agent_test.go b/pkg/istio-agent/agent_test.go deleted file mode 100644 index 5e78928c4..000000000 --- a/pkg/istio-agent/agent_test.go +++ /dev/null @@ -1,978 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istioagent - -import ( - "context" - "crypto/tls" - "crypto/x509" - "encoding/json" - "fmt" - "net" - "os" - "path" - "path/filepath" - "reflect" - "sort" - "strings" - "testing" - "time" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/reflection" - meshconfig "istio.io/api/mesh/v1alpha1" - pkgenv "istio.io/pkg/env" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - testutil "github.com/apache/dubbo-go-pixiu/pilot/test/util" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/bootstrap" - "github.com/apache/dubbo-go-pixiu/pkg/bootstrap/platform" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/envoy" - "github.com/apache/dubbo-go-pixiu/pkg/file" - "github.com/apache/dubbo-go-pixiu/pkg/istio-agent/grpcxds" - "github.com/apache/dubbo-go-pixiu/pkg/security" - "github.com/apache/dubbo-go-pixiu/pkg/spiffe" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" - "github.com/apache/dubbo-go-pixiu/security/pkg/credentialfetcher/plugin" - "github.com/apache/dubbo-go-pixiu/security/pkg/nodeagent/cache" - camock "github.com/apache/dubbo-go-pixiu/security/pkg/nodeagent/caclient/providers/mock" - "github.com/apache/dubbo-go-pixiu/security/pkg/nodeagent/cafile" - "github.com/apache/dubbo-go-pixiu/security/pkg/nodeagent/sds" - "github.com/apache/dubbo-go-pixiu/security/pkg/nodeagent/test/mock" - pkiutil "github.com/apache/dubbo-go-pixiu/security/pkg/pki/util" - "github.com/apache/dubbo-go-pixiu/security/pkg/stsservice" - stsmock "github.com/apache/dubbo-go-pixiu/security/pkg/stsservice/mock" - stsserver "github.com/apache/dubbo-go-pixiu/security/pkg/stsservice/server" - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestAgent(t *testing.T) { - wd := t.TempDir() - mktemp := t.TempDir - // Normally we call leak checker first. Here we call it after TempDir to avoid the (extremely - // rare) race condition of a certificate being written at the same time the cleanup occurs, which - // causes the test to fail. By checking for the leak first, we ensure all of our activities have - // fully cleanup up. - // https://github.com/golang/go/issues/43547 - leak.Check(t) - // We run in the temp dir to ensure that when we are writing to the hardcoded ./etc/certs we - // don't collide with other tests - if err := os.Chdir(wd); err != nil { - t.Fatal(err) - } - certDir := filepath.Join(env.IstioSrc, "./tests/testdata/certs/pilot") - fakeSpiffeID := "spiffe://cluster.local/ns/fake-namespace/sa/fake-sa" - // As a hack, we are using the serving sets as client cert, so the identity here is istiod not a spiffe - preProvisionID := "istiod.dubbo-system.svc" - - checkCertsWritten := func(t *testing.T, dir string) { - retry.UntilSuccessOrFail(t, func() error { - // Ensure we output the certs - if names := filenames(t, dir); !reflect.DeepEqual(names, []string{"cert-chain.pem", "key.pem", "root-cert.pem"}) { - return fmt.Errorf("expected certs to be output, but got %v", names) - } - return nil - }, retry.Delay(time.Millisecond*10), retry.Timeout(time.Second*5)) - } - - // NOTE: Long test names may result in weird errors on bind; see https://github.com/golang/go/issues/6895 - t.Run("Kubernetes defaults", func(t *testing.T) { - // XDS and CA are both using JWT authentication and TLS. Root certificates distributed in - // configmap to each namespace. - Setup(t).Check(t, security.WorkloadKeyCertResourceName, security.RootCertReqResourceName) - }) - t.Run("RSA", func(t *testing.T) { - // All of the other tests use ECC for speed. Here we make sure RSA still works - Setup(t, func(a AgentTest) AgentTest { - a.Security.ECCSigAlg = "" - return a - }).Check(t, security.WorkloadKeyCertResourceName, security.RootCertReqResourceName) - }) - t.Run("Kubernetes defaults output key and cert", func(t *testing.T) { - // same as "Kubernetes defaults", but also output the key and cert. This can be used for tools - // that expect certs as files, like Prometheus. - dir := mktemp() - sds := Setup(t, func(a AgentTest) AgentTest { - a.Security.OutputKeyCertToDir = dir - a.Security.SecretRotationGracePeriodRatio = 1 - return a - }).Check(t, security.WorkloadKeyCertResourceName, security.RootCertReqResourceName) - - // Ensure we output the certs - checkCertsWritten(t, dir) - - // We rotate immediately, so we expect these files to be constantly updated - go sds[security.WorkloadKeyCertResourceName].DrainResponses() - expectFileChanged(t, filepath.Join(dir, "cert-chain.pem"), filepath.Join(dir, "key.pem")) - }) - t.Run("Kubernetes defaults - output key and cert without SDS", func(t *testing.T) { - dir := mktemp() - Setup(t, func(a AgentTest) AgentTest { - a.Security.OutputKeyCertToDir = dir - a.Security.SecretRotationGracePeriodRatio = 1 - return a - }) - // We start the agent, but never send a single SDS request - // This behavior is useful for supporting writing the certs to disk without Envoy - checkCertsWritten(t, dir) - - // TODO: this does not actually work, rotation is tied to SDS currently - // expectFileChanged(t, filepath.Join(dir, "cert-chain.pem")) - // expectFileChanged(t, filepath.Join(dir, "key.pem")) - }) - t.Run("File mounted certs", func(t *testing.T) { - // User sets FileMountedCerts. They also need to set ISTIO_META_TLS_CLIENT* to specify the - // file paths. CA communication is disabled. mTLS is always used for authentication with - // Istiod, never JWT. - dir := mktemp() - copyCerts(t, dir) - - cfg := security.SdsCertificateConfig{ - CertificatePath: filepath.Join(dir, "cert-chain.pem"), - PrivateKeyPath: filepath.Join(dir, "key.pem"), - CaCertificatePath: filepath.Join(dir, "root-cert.pem"), - } - Setup(t, func(a AgentTest) AgentTest { - // Ensure we use the mTLS certs for XDS - a.XdsAuthenticator.Set("", preProvisionID) - // Ensure we don't try to connect to CA - a.CaAuthenticator.Set("", "") - a.Security.CertChainFilePath = cfg.CertificatePath - a.Security.KeyFilePath = cfg.PrivateKeyPath - a.Security.RootCertFilePath = cfg.CaCertificatePath - a.ProxyConfig.ProxyMetadata = map[string]string{} - a.ProxyConfig.ProxyMetadata[MetadataClientCertChain] = filepath.Join(dir, "cert-chain.pem") - a.ProxyConfig.ProxyMetadata[MetadataClientCertKey] = filepath.Join(dir, "key.pem") - a.ProxyConfig.ProxyMetadata[MetadataClientRootCert] = filepath.Join(dir, "root-cert.pem") - a.Security.FileMountedCerts = true - return a - }).Check(t, cfg.GetRootResourceName(), cfg.GetResourceName()) - }) - t.Run("File mounted certs with bogus token path", func(t *testing.T) { - // User sets FileMountedCerts and a bogus token path. - // They also need to set ISTIO_META_TLS_CLIENT* to specify the file paths. - // CA communication is disabled. mTLS is used for authentication with Istiod. - dir := mktemp() - copyCerts(t, dir) - - cfg := security.SdsCertificateConfig{ - CertificatePath: filepath.Join(dir, "cert-chain.pem"), - PrivateKeyPath: filepath.Join(dir, "key.pem"), - CaCertificatePath: filepath.Join(dir, "root-cert.pem"), - } - Setup(t, func(a AgentTest) AgentTest { - // Ensure we use the mTLS certs for XDS - a.XdsAuthenticator.Set("", preProvisionID) - // Ensure we don't try to connect to CA - a.CaAuthenticator.Set("", "") - a.Security.CertChainFilePath = cfg.CertificatePath - a.Security.KeyFilePath = cfg.PrivateKeyPath - a.Security.RootCertFilePath = cfg.CaCertificatePath - a.Security.CredFetcher = plugin.CreateTokenPlugin(filepath.Join(env.IstioSrc, "pkg/istio-agent/testdata/token")) - a.ProxyConfig.ProxyMetadata = map[string]string{} - a.ProxyConfig.ProxyMetadata[MetadataClientCertChain] = filepath.Join(dir, "cert-chain.pem") - a.ProxyConfig.ProxyMetadata[MetadataClientCertKey] = filepath.Join(dir, "key.pem") - a.ProxyConfig.ProxyMetadata[MetadataClientRootCert] = filepath.Join(dir, "root-cert.pem") - a.Security.FileMountedCerts = true - return a - }).Check(t, cfg.GetRootResourceName(), cfg.GetResourceName()) - }) - t.Run("OS CA Certs are able to be accessed", func(t *testing.T) { - // Try loading an OS CA Cert from OS CA Certs file paths. - dir := mktemp() - copyCertsWithOSRootCA(t, dir) - - osRootPath := security.GetOSRootFilePath() - caRootCert := filepath.Base(osRootPath) - - cfg := security.SdsCertificateConfig{ - CertificatePath: filepath.Join(dir, "cert-chain.pem"), - PrivateKeyPath: filepath.Join(dir, "key.pem"), - CaCertificatePath: filepath.Join(dir, caRootCert), - } - Setup(t, func(a AgentTest) AgentTest { - // Ensure we use the mTLS certs for XDS - a.XdsAuthenticator.Set("", preProvisionID) - // Ensure we don't try to connect to CA - a.CaAuthenticator.Set("", "") - a.ProxyConfig.ProxyMetadata = map[string]string{} - a.ProxyConfig.ProxyMetadata[MetadataClientCertChain] = filepath.Join(dir, "cert-chain.pem") - a.ProxyConfig.ProxyMetadata[MetadataClientCertKey] = filepath.Join(dir, "key.pem") - a.ProxyConfig.ProxyMetadata[MetadataClientRootCert] = filepath.Join(dir, caRootCert) - a.Security.FileMountedCerts = true - a.Security.CARootPath = cafile.CACertFilePath - return a - }).Check(t, cfg.GetRootResourceName(), cfg.GetResourceName()) - }) - t.Run("Implicit file mounted certs", func(t *testing.T) { - // User mounts certificates in /etc/certs (hardcoded path). CA communication is disabled. - // mTLS is always used for authentication with Istiod, never JWT. - copyCerts(t, "./etc/certs") - t.Cleanup(func() { - _ = os.RemoveAll("./etc/certs") - }) - Setup(t, func(a AgentTest) AgentTest { - // Ensure we use the token - a.XdsAuthenticator.Set("fake", "") - // Ensure we don't try to connect to CA - a.CaAuthenticator.Set("", "") - a.Security.CertChainFilePath = security.DefaultCertChainFilePath - a.Security.KeyFilePath = security.DefaultKeyFilePath - a.Security.RootCertFilePath = security.DefaultRootCertFilePath - return a - }).Check(t, security.WorkloadKeyCertResourceName, security.RootCertReqResourceName) - }) - t.Run("GKE workload certificates", func(t *testing.T) { - dir := mktemp() - dir = filepath.Join(dir, "./var/run/secrets/workload-spiffe-credentials") - // The cert and key names of GKE workload certificates differ from - // the default file mounted cert and key names. - copyGkeWorkloadCerts(t, dir) - cfg := security.SdsCertificateConfig{ - CertificatePath: filepath.Join(dir, "certificates.pem"), - PrivateKeyPath: filepath.Join(dir, "private_key.pem"), - CaCertificatePath: filepath.Join(dir, "ca_certificates.pem"), - } - Setup(t, func(a AgentTest) AgentTest { - // Ensure we use the token - a.XdsAuthenticator.Set("fake", "") - // Ensure we don't try to connect to CA - a.CaAuthenticator.Set("", "") - a.Security.CertChainFilePath = cfg.CertificatePath - a.Security.KeyFilePath = cfg.PrivateKeyPath - a.Security.RootCertFilePath = cfg.CaCertificatePath - a.Security.FileMountedCerts = true - return a - }).Check(t, security.WorkloadKeyCertResourceName, security.RootCertReqResourceName) - }) - t.Run("External SDS socket", func(t *testing.T) { - dir := mktemp() - copyCerts(t, dir) - - secOpts := &security.Options{} - secOpts.RootCertFilePath = filepath.Join(dir, "/root-cert.pem") - secOpts.CertChainFilePath = filepath.Join(dir, "/cert-chain.pem") - secOpts.KeyFilePath = filepath.Join(dir, "/key.pem") - - secretCache, err := cache.NewSecretManagerClient(nil, secOpts) - if err != nil { - t.Fatal(err) - } - defer secretCache.Close() - - // this SDS Server listens on the well-known socket path serving the certs copied to the temp directory, - // and acts as the external SDS Server that the Agent will detect at startup - sdsServer := sds.NewServer(secOpts, secretCache, nil) - defer sdsServer.Stop() - - Setup(t).Check(t, security.WorkloadKeyCertResourceName, security.RootCertReqResourceName) - - t.Cleanup(func() { - _ = os.RemoveAll(dir) - }) - }) - t.Run("Unhealthy SDS socket", func(t *testing.T) { - dir := filepath.Dir(security.WorkloadIdentitySocketPath) - os.MkdirAll(dir, 0o755) - - // starting an unresponsive listener on the socket - l, err := net.Listen("unix", security.WorkloadIdentitySocketPath) - if err != nil { - t.Fatal(err) - } - defer l.Close() - - Setup(t).Check(t, security.WorkloadKeyCertResourceName, security.RootCertReqResourceName) - - t.Cleanup(func() { - _ = os.RemoveAll(dir) - }) - }) - t.Run("Workload certificates", func(t *testing.T) { - dir := security.WorkloadIdentityCredentialsPath - if err := os.MkdirAll(dir, 0o755); err != nil { - t.Fatal(err) - } - copyCerts(t, dir) - - Setup(t).Check(t, security.WorkloadKeyCertResourceName, security.RootCertReqResourceName) - - t.Cleanup(func() { - _ = os.RemoveAll(dir) - }) - }) - t.Run("VMs", func(t *testing.T) { - // Bootstrap sets up a short lived JWT token and root certificate. The initial run will fetch - // a certificate and write it to disk. This will be used (by mTLS authenticator) for future - // requests to both the CA and XDS. - dir := mktemp() - t.Run("initial run", func(t *testing.T) { - a := Setup(t, func(a AgentTest) AgentTest { - a.XdsAuthenticator.Set("", fakeSpiffeID) - a.Security.OutputKeyCertToDir = dir - a.Security.ProvCert = dir - return a - }) - a.Check(t, security.WorkloadKeyCertResourceName, security.RootCertReqResourceName) - }) - t.Run("reboot", func(t *testing.T) { - // Switch the JWT to a bogus path, to simulate the VM being rebooted - a := Setup(t, func(a AgentTest) AgentTest { - a.XdsAuthenticator.Set("", fakeSpiffeID) - a.CaAuthenticator.Set("", fakeSpiffeID) - a.Security.OutputKeyCertToDir = dir - a.Security.ProvCert = dir - a.Security.CredFetcher = plugin.CreateTokenPlugin(filepath.Join(env.IstioSrc, "pkg/istio-agent/testdata/token")) - return a - }) - // Ensure we can still make requests - a.Check(t, security.WorkloadKeyCertResourceName, security.RootCertReqResourceName) - a.Check(t, security.WorkloadKeyCertResourceName, security.RootCertReqResourceName) - }) - }) - t.Run("VMs to etc/certs", func(t *testing.T) { - // Handle special edge case where we output certs to /etc/certs, which has magic implicit - // reading from file logic - dir := "./etc/certs" - if err := os.MkdirAll(dir, 0o755); err != nil { - t.Fatal(err) - } - t.Cleanup(func() { - _ = os.RemoveAll(dir) - }) - a := Setup(t, func(a AgentTest) AgentTest { - a.XdsAuthenticator.Set("", fakeSpiffeID) - a.Security.OutputKeyCertToDir = dir - a.Security.ProvCert = dir - a.Security.SecretRotationGracePeriodRatio = 1 - return a - }) - sds := a.Check(t, security.WorkloadKeyCertResourceName, security.RootCertReqResourceName) - - // Ensure we output the certs - checkCertsWritten(t, dir) - - // The provisioning certificates should not be touched - go sds[security.WorkloadKeyCertResourceName].DrainResponses() - expectFileChanged(t, filepath.Join(dir, "cert-chain.pem"), filepath.Join(dir, "key.pem")) - }) - t.Run("VMs provisioned certificates - short lived", func(t *testing.T) { - // User has certificates pre-provisioned on the VM by some sort of tooling, pointed to by - // PROV_CERT. These are used for mTLS auth with XDS and CA. Certificates are short lived, - // OUTPUT_CERT = PROV_CERT. This is the same as "VMs", just skipping the initial - // JWT exchange. - dir := mktemp() - copyCerts(t, dir) - - sds := Setup(t, func(a AgentTest) AgentTest { - a.CaAuthenticator.Set("", preProvisionID) - a.XdsAuthenticator.Set("", fakeSpiffeID) - a.Security.OutputKeyCertToDir = dir - a.Security.ProvCert = dir - a.Security.SecretRotationGracePeriodRatio = 1 - return a - }).Check(t, security.WorkloadKeyCertResourceName, security.RootCertReqResourceName) - - // The provisioning certificates should not be touched - go sds[security.WorkloadKeyCertResourceName].DrainResponses() - expectFileChanged(t, filepath.Join(dir, "cert-chain.pem"), filepath.Join(dir, "key.pem")) - }) - t.Run("VMs provisioned certificates - long lived", func(t *testing.T) { - // User has certificates pre-provisioned on the VM by some sort of tooling, pointed to by - // PROV_CERT. These are used for mTLS auth with XDS and CA. Certificates are long lived, we - // always use the same certificate for control plane authentication and the short lived - // certificates returned from the CA for workload authentication - dir := mktemp() - copyCerts(t, dir) - - sds := Setup(t, func(a AgentTest) AgentTest { - a.CaAuthenticator.Set("", preProvisionID) - a.XdsAuthenticator.Set("", preProvisionID) - a.Security.ProvCert = dir - a.Security.SecretRotationGracePeriodRatio = 1 - return a - }).Check(t, security.WorkloadKeyCertResourceName, security.RootCertReqResourceName) - - // The provisioning certificates should not be touched - go sds[security.WorkloadKeyCertResourceName].DrainResponses() - expectFileUnchanged(t, filepath.Join(dir, "cert-chain.pem"), filepath.Join(dir, "key.pem")) - }) - t.Run("Token exchange", func(t *testing.T) { - // This is used in environments where the CA expects a different token type than K8s jwt, and - // exchanges it for another form using TokenExchanger - Setup(t, func(a AgentTest) AgentTest { - a.CaAuthenticator.Set("some-token", "") - a.Security.TokenExchanger = camock.NewMockTokenExchangeServer(map[string]string{"fake": "some-token"}) - return a - }).Check(t, security.WorkloadKeyCertResourceName, security.RootCertReqResourceName) - }) - t.Run("Token exchange with credential fetcher", func(t *testing.T) { - // This is used with platform credentials, where the platform provides some underlying - // identity (typically for VMs, not Kubernetes), which is exchanged with TokenExchanger - // before sending to the CA. - dir := mktemp() - a := Setup(t, func(a AgentTest) AgentTest { - a.CaAuthenticator.Set("some-token", "") - a.XdsAuthenticator.Set("", fakeSpiffeID) - a.Security.TokenExchanger = camock.NewMockTokenExchangeServer(map[string]string{"platform-cred": "some-token"}) - a.Security.CredFetcher = plugin.CreateMockPlugin("platform-cred") - a.Security.OutputKeyCertToDir = dir - a.Security.ProvCert = dir - a.AgentConfig.XDSRootCerts = filepath.Join(certDir, "root-cert.pem") - return a - }) - - a.Check(t, security.WorkloadKeyCertResourceName, security.RootCertReqResourceName) - }) - t.Run("Token exchange with credential fetcher downtime", func(t *testing.T) { - // This ensures our pre-warming is resilient to temporary downtime of the CA - dir := mktemp() - a := Setup(t, func(a AgentTest) AgentTest { - // Make CA deny all requests to simulate downtime - a.CaAuthenticator.Set("", "") - a.XdsAuthenticator.Set("", fakeSpiffeID) - a.Security.TokenExchanger = camock.NewMockTokenExchangeServer(map[string]string{"platform-cred": "some-token"}) - a.Security.CredFetcher = plugin.CreateMockPlugin("platform-cred") - a.Security.OutputKeyCertToDir = dir - a.Security.ProvCert = dir - a.AgentConfig.XDSRootCerts = filepath.Join(certDir, "root-cert.pem") - return a - }) - - go func() { - // Wait until we get a failure - if err := retry.UntilSuccess(func() error { - if a.CaAuthenticator.Failures.Load() < 2 { - return fmt.Errorf("not enough failures yet") - } - return nil - }); err != nil { - // never got failures, we cannot fail in goroutine so just log it and the outer - // function will fail - log.Error(err) - return - } - // Bring the CA back up - a.CaAuthenticator.Set("some-token", "") - }() - - a.Check(t, security.WorkloadKeyCertResourceName, security.RootCertReqResourceName) - }) - envoyReady := func(t test.Failer, name string, port int) { - retry.UntilSuccessOrFail(t, func() error { - code, _, _ := env.HTTPGet(fmt.Sprintf("http://localhost:%d/ready", port)) - if code != 200 { - return fmt.Errorf("envoy %q is not ready", name) - } - return nil - }, retry.Timeout(time.Second*150)) - } - t.Run("Envoy lifecycle", func(t *testing.T) { - Setup(t, func(a AgentTest) AgentTest { - a.envoyEnable = true - a.ProxyConfig.StatusPort = 15020 - a.ProxyConfig.ProxyAdminPort = 15000 - a.AgentConfig.EnvoyPrometheusPort = 15090 - a.AgentConfig.EnvoyStatusPort = 15021 - a.AgentConfig.ProxyXDSDebugViaAgent = false // uses a fixed port - return a - }).Check(t, security.WorkloadKeyCertResourceName, security.RootCertReqResourceName) - envoyReady(t, "first agent", 15000) - Setup(t, func(a AgentTest) AgentTest { - a.envoyEnable = true - a.ProxyConfig.StatusPort = 25020 - a.ProxyConfig.ProxyAdminPort = 25000 - a.AgentConfig.EnvoyPrometheusPort = 25090 - a.AgentConfig.EnvoyStatusPort = 25021 - a.AgentConfig.ProxyXDSDebugViaAgent = false // uses a fixed port - return a - }).Check(t, security.WorkloadKeyCertResourceName, security.RootCertReqResourceName) - envoyReady(t, "second agent", 25000) - }) - t.Run("Envoy bootstrap discovery", func(t *testing.T) { - Setup(t, func(a AgentTest) AgentTest { - a.envoyEnable = true - a.ProxyConfig.StatusPort = 15020 - a.ProxyConfig.ProxyAdminPort = 15000 - a.AgentConfig.EnvoyPrometheusPort = 15090 - a.AgentConfig.EnvoyStatusPort = 15021 - a.AgentConfig.EnableDynamicBootstrap = true - return a - }).Check(t, security.WorkloadKeyCertResourceName, security.RootCertReqResourceName) - envoyReady(t, "bootstrap discovery", 15000) - }) - t.Run("gRPC XDS bootstrap", func(t *testing.T) { - bootstrapPath := path.Join(mktemp(), "grpc-bootstrap.json") - a := Setup(t, func(a AgentTest) AgentTest { - a.Security.OutputKeyCertToDir = "/cert/path" - a.AgentConfig.GRPCBootstrapPath = bootstrapPath - a.envoyEnable = false - return a - }) - generated, err := grpcxds.LoadBootstrap(bootstrapPath) - if err != nil { - t.Fatalf("could not read bootstrap config: %v", err) - } - - // goldenfile determinism - for _, k := range []string{"PROV_CERT", "PROXY_CONFIG"} { - delete(generated.Node.Metadata.Fields, k) - } - got, err := json.MarshalIndent(generated, "", " ") - if err != nil { - t.Fatalf("failed to marshal bootstrap: %v", err) - } - got = []byte(strings.ReplaceAll(string(got), a.agent.cfg.XdsUdsPath, "etc/istio/XDS")) - - testutil.CompareContent(t, got, filepath.Join(env.IstioSrc, "pkg/istio-agent/testdata/grpc-bootstrap.json")) - }) - t.Run("ROOT CA change", func(t *testing.T) { - dir := mktemp() - rootCertFileName := "root-cert.pem" - - // use a invalid root cert, XDS will fail with `authentication handshake failed` - localRootCert := filepath.Join(env.IstioSrc, "./tests/testdata/local/etc/certs/root-cert.pem") - if err := file.Copy(localRootCert, dir, rootCertFileName); err != nil { - t.Fatalf("failed to init root CA: %v", err) - } - a := Setup(t, func(a AgentTest) AgentTest { - a.AgentConfig.XDSRootCerts = path.Join(dir, rootCertFileName) - return a - }) - meta := proxyConfigToMetadata(t, a.ProxyConfig) - if err := test.Wrap(func(t test.Failer) { - conn := setupDownstreamConnectionUDS(t, a.AgentConfig.XdsUdsPath) - xdsc := xds.NewAdsTest(t, conn).WithMetadata(meta) - _ = xdsc.RequestResponseAck(t, nil) - }); err == nil { - t.Fatalf("connect success with wrong CA") - } - - // change ROOT CA, XDS will success - if err := file.Copy(path.Join(certDir, rootCertFileName), dir, rootCertFileName); err != nil { - t.Fatalf("failed to change root CA: %v", err) - } - a.Check(t, security.WorkloadKeyCertResourceName, security.RootCertReqResourceName) - }) - t.Run("GCP", func(t *testing.T) { - os.MkdirAll(filepath.Join(wd, "var/run/secrets/tokens"), 0o755) - os.WriteFile(filepath.Join(wd, "var/run/secrets/tokens/istio-token"), []byte("test-token"), 0o644) - a := Setup(t, func(a AgentTest) AgentTest { - a.envoyEnable = true - a.enableSTS = true - a.XdsAuthenticator.Set("Fake STS", "") - a.ProxyConfig.ProxyBootstrapTemplatePath = filepath.Join(env.IstioSrc, "./tools/packaging/common/gcp_envoy_bootstrap.json") - a.AgentConfig.Platform = &fakePlatform{meta: map[string]string{ - "gcp_project": "my-sd-project", - }} - a.AgentConfig.XDSRootCerts = filepath.Join(env.IstioSrc, "./tests/testdata/certs/pilot/root-cert.pem") - return a - }) - // We cannot actually check that envoy is ready, since it depends on RTDS and Istiod does not implement this. - // So instead just make sure it authenticated, which ensures the full STS flow properly functions - retry.UntilOrFail(t, func() bool { return a.XdsAuthenticator.Successes.Load() > 0 }) - }) -} - -type AgentTest struct { - ProxyConfig *meshconfig.ProxyConfig - Security security.Options - AgentConfig AgentOptions - XdsAuthenticator *security.FakeAuthenticator - CaAuthenticator *security.FakeAuthenticator - - envoyEnable bool - enableSTS bool - - agent *Agent -} - -func Setup(t *testing.T, opts ...func(a AgentTest) AgentTest) *AgentTest { - d := t.TempDir() - resp := AgentTest{ - XdsAuthenticator: security.NewFakeAuthenticator("xds").Set("fake", ""), - CaAuthenticator: security.NewFakeAuthenticator("ca").Set("fake", ""), - ProxyConfig: mesh.DefaultProxyConfig(), - } - // Run through opts one time just to get the authenticators. - for _, opt := range opts { - resp = opt(resp) - } - ca := setupCa(t, resp.CaAuthenticator) - resp.Security = security.Options{ - CAEndpoint: ca.URL, - CAProviderName: "Citadel", - TrustDomain: "cluster.local", - CredFetcher: plugin.CreateTokenPlugin(filepath.Join(env.IstioSrc, "pkg/istio-agent/testdata/token")), - WorkloadNamespace: "namespace", - ServiceAccount: "sa", - // Signing in 2048 bit RSA is extremely slow when running with -race enabled, sometimes taking 5s+ in - // our CI, causing flakes. We use ECC as the default to speed this up. - ECCSigAlg: string(pkiutil.EcdsaSigAlg), - CARootPath: cafile.CACertFilePath, - } - proxy := &model.Proxy{ - ID: "pod1.fake-namespace", - DNSDomain: "fake-namespace.svc.cluster.local", - Type: model.SidecarProxy, - IPAddresses: []string{"127.0.0.1"}, - } - resp.ProxyConfig = mesh.DefaultProxyConfig() - resp.ProxyConfig.DiscoveryAddress = setupDiscovery(t, resp.XdsAuthenticator, ca.KeyCertBundle.GetRootCertPem()) - rootCert := filepath.Join(env.IstioSrc, "./tests/testdata/certs/pilot/root-cert.pem") - resp.AgentConfig = AgentOptions{ - ProxyXDSDebugViaAgent: true, - CARootCerts: rootCert, - XDSRootCerts: rootCert, - XdsUdsPath: filepath.Join(d, "XDS"), - ServiceNode: proxy.ServiceNode(), - } - - // Set-up envoy defaults - resp.ProxyConfig.ProxyBootstrapTemplatePath = filepath.Join(env.IstioSrc, "./tools/packaging/common/envoy_bootstrap.json") - resp.ProxyConfig.ConfigPath = d - resp.ProxyConfig.BinaryPath = filepath.Join(env.LocalOut, "envoy") - if path, exists := pkgenv.RegisterStringVar("ENVOY_PATH", "", "Specifies the path to an Envoy binary.").Lookup(); exists { - resp.ProxyConfig.BinaryPath = path - } - resp.ProxyConfig.TerminationDrainDuration = nil // no need to be graceful in a test - resp.AgentConfig.ProxyIPAddresses = []string{"127.0.0.1"} // ensures IPv4 binding - resp.AgentConfig.Platform = &platform.Unknown{} // disable discovery - - // Run through opts again to apply settings - for _, opt := range opts { - resp = opt(resp) - } - if resp.enableSTS { - tokenManager := stsmock.CreateFakeTokenManager() - tokenManager.SetRespStsParam(stsservice.StsResponseParameters{ - AccessToken: "Fake STS", - IssuedTokenType: "urn:ietf:params:oauth:token-type:access_token", - TokenType: "Bearer", - ExpiresIn: 60, - Scope: "example.com", - }) - stsServer, err := stsserver.NewServer(stsserver.Config{ - LocalHostAddr: "localhost", - LocalPort: 0, - }, tokenManager) - if err != nil { - t.Fatal(err) - } - resp.Security.STSPort = stsServer.Port - t.Cleanup(stsServer.Stop) - } - - a := NewAgent(resp.ProxyConfig, &resp.AgentConfig, &resp.Security, envoy.ProxyConfig{TestOnly: !resp.envoyEnable}) - t.Cleanup(a.Close) - ctx, done := context.WithCancel(context.Background()) - wait, err := a.Run(ctx) - if err != nil { - t.Fatal(err) - } - // First signal to terminate, then wait for completion (reverse order semantics). - t.Cleanup(wait) - t.Cleanup(done) - - resp.agent = a - return &resp -} - -func (a *AgentTest) Check(t *testing.T, expectedSDS ...string) map[string]*xds.AdsTest { - // Ensure we can send XDS requests - meta := proxyConfigToMetadata(t, a.ProxyConfig) - - // We add a retry around XDS since some of the auth methods are eventually consistent, relying on - // the CSR flow to complete first. This mirrors Envoy, which will also retry indefinitely - var resp *discovery.DiscoveryResponse - retry.UntilSuccessOrFail(t, func() error { - return test.Wrap(func(t test.Failer) { - conn := setupDownstreamConnectionUDS(t, a.AgentConfig.XdsUdsPath) - xdsc := xds.NewAdsTest(t, conn).WithMetadata(meta) - resp = xdsc.RequestResponseAck(t, nil) - }) - }, retry.Timeout(time.Second*15), retry.Delay(time.Millisecond*200)) - - sdsStreams := map[string]*xds.AdsTest{} - gotKeys := []string{} - for _, res := range xdstest.ExtractSecretResources(t, resp.Resources) { - sds := xds.NewSdsTest(t, setupDownstreamConnectionUDS(t, security.WorkloadIdentitySocketPath)). - WithMetadata(meta). - WithTimeout(time.Second * 20) // CSR can be extremely slow with race detection enabled due to 2048 RSA - sds.RequestResponseAck(t, &discovery.DiscoveryRequest{ResourceNames: []string{res}}) - sdsStreams[res] = sds - gotKeys = append(gotKeys, res) - } - if !reflect.DeepEqual(expectedSDS, gotKeys) { - t.Errorf("expected SDS resources %v got %v", expectedSDS, gotKeys) - } - return sdsStreams -} - -func copyCerts(t *testing.T, dir string) { - if err := os.MkdirAll(dir, 0o755); err != nil { - t.Fatal(err) - } - if err := file.Copy(filepath.Join(env.IstioSrc, "./tests/testdata/certs/pilot/cert-chain.pem"), dir, "cert-chain.pem"); err != nil { - t.Fatal(err) - } - if err := file.Copy(filepath.Join(env.IstioSrc, "./tests/testdata/certs/pilot/key.pem"), dir, "key.pem"); err != nil { - t.Fatal(err) - } - if err := file.Copy(filepath.Join(env.IstioSrc, "./tests/testdata/certs/pilot/root-cert.pem"), dir, "root-cert.pem"); err != nil { - t.Fatal(err) - } -} - -func copyGkeWorkloadCerts(t *testing.T, dir string) { - if err := os.MkdirAll(dir, 0o755); err != nil { - t.Fatal(err) - } - - if err := file.Copy(filepath.Join(env.IstioSrc, "./tests/testdata/certs/pilot/cert-chain.pem"), dir, "certificates.pem"); err != nil { - t.Fatal(err) - } - if err := file.Copy(filepath.Join(env.IstioSrc, "./tests/testdata/certs/pilot/key.pem"), dir, "private_key.pem"); err != nil { - t.Fatal(err) - } - if err := file.Copy(filepath.Join(env.IstioSrc, "./tests/testdata/certs/pilot/root-cert.pem"), dir, "ca_certificates.pem"); err != nil { - t.Fatal(err) - } -} - -func copyCertsWithOSRootCA(t *testing.T, dir string) { - caRootPath := security.GetOSRootFilePath() - if caRootPath == "" { - t.Fatal("OS CA Root Cert could not be found.") - } - caRootCert := filepath.Base(caRootPath) - if caRootCert == "" { - t.Fatal("OS CA Root Cert couldn't be found.") - } - if err := os.MkdirAll(dir, 0o755); err != nil { - t.Fatal(err) - } - if err := file.Copy(filepath.Join(env.IstioSrc, "./tests/testdata/certs/pilot/cert-chain.pem"), dir, "cert-chain.pem"); err != nil { - t.Fatal(err) - } - if err := file.Copy(filepath.Join(env.IstioSrc, "./tests/testdata/certs/pilot/key.pem"), dir, "key.pem"); err != nil { - t.Fatal(err) - } - if err := file.Copy(filepath.Join(caRootPath), dir, caRootCert); err != nil { - t.Fatal(err) - } -} - -func expectFileChanged(t *testing.T, files ...string) { - t.Helper() - initials := [][]byte{} - for _, f := range files { - initials = append(initials, testutil.ReadFile(t, f)) - } - retry.UntilSuccessOrFail(t, func() error { - for i, f := range files { - now, err := os.ReadFile(f) - if err != nil { - return err - } - if reflect.DeepEqual(initials[i], now) { - return fmt.Errorf("file is unchanged") - } - } - return nil - }, retry.Delay(time.Millisecond*10), retry.Timeout(time.Second*15)) -} - -func expectFileUnchanged(t *testing.T, files ...string) { - t.Helper() - initials := [][]byte{} - for _, f := range files { - initials = append(initials, testutil.ReadFile(t, f)) - } - for attempt := 0; attempt < 10; attempt++ { - time.Sleep(time.Millisecond * 10) - for i, f := range files { - now := testutil.ReadFile(t, f) - if !reflect.DeepEqual(initials[i], now) { - t.Fatalf("file is changed!") - } - } - } -} - -func filenames(t *testing.T, dir string) []string { - t.Helper() - res := []string{} - contents, err := os.ReadDir(dir) - if err != nil { - t.Fatal(err) - } - for _, f := range contents { - res = append(res, f.Name()) - } - sort.Strings(res) - return res -} - -func proxyConfigToMetadata(t *testing.T, proxyConfig *meshconfig.ProxyConfig) model.NodeMetadata { - t.Helper() - m := map[string]interface{}{} - for k, v := range proxyConfig.ProxyMetadata { - if strings.HasPrefix(k, bootstrap.IstioMetaPrefix) { - m[strings.TrimPrefix(k, bootstrap.IstioMetaPrefix)] = v - } - } - b, err := json.Marshal(m) - if err != nil { - t.Fatal(err) - } - meta := model.NodeMetadata{} - if err := json.Unmarshal(b, &meta); err != nil { - t.Fatal(err) - } - pc := (*model.NodeMetaProxyConfig)(proxyConfig) - meta.Namespace = "fake-namespace" - meta.ServiceAccount = "fake-sa" - meta.ProxyConfig = pc - return meta -} - -func setupCa(t *testing.T, auth *security.FakeAuthenticator) *mock.CAServer { - t.Helper() - opt := tlsOptions(t) - s, err := mock.NewCAServerWithKeyCert(0, - testutil.ReadFile(t, filepath.Join(env.IstioSrc, "./tests/testdata/certs/pilot/ca-key.pem")), - testutil.ReadFile(t, filepath.Join(env.IstioSrc, "./tests/testdata/certs/pilot/ca-cert.pem")), - opt) - if err != nil { - t.Fatal(err) - } - t.Cleanup(s.GRPCServer.Stop) - - s.Authenticators = []security.Authenticator{auth} - - return s -} - -func tlsOptions(t *testing.T, extraRoots ...[]byte) grpc.ServerOption { - t.Helper() - cert, err := tls.LoadX509KeyPair( - filepath.Join(env.IstioSrc, "./tests/testdata/certs/pilot/cert-chain.pem"), - filepath.Join(env.IstioSrc, "./tests/testdata/certs/pilot/key.pem")) - if err != nil { - t.Fatal(err) - } - peerCertVerifier := spiffe.NewPeerCertVerifier() - if err := peerCertVerifier.AddMappingFromPEM("cluster.local", - testutil.ReadFile(t, filepath.Join(env.IstioSrc, "./tests/testdata/certs/pilot/root-cert.pem"))); err != nil { - t.Fatal(err) - } - for _, r := range extraRoots { - if err := peerCertVerifier.AddMappingFromPEM("cluster.local", r); err != nil { - t.Fatal(err) - } - } - return grpc.Creds(credentials.NewTLS(&tls.Config{ - Certificates: []tls.Certificate{cert}, - ClientAuth: tls.VerifyClientCertIfGiven, - ClientCAs: peerCertVerifier.GetGeneralCertPool(), - VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { - err := peerCertVerifier.VerifyPeerCert(rawCerts, verifiedChains) - if err != nil { - log.Infof("Could not verify certificate: %v", err) - } - return err - }, - })) -} - -func setupDiscovery(t *testing.T, auth *security.FakeAuthenticator, certPem []byte) string { - t.Helper() - - l, err := net.Listen("tcp", "localhost:0") - if err != nil { - t.Fatal(err) - } - opt := tlsOptions(t, certPem) - // Set up a simple service to make sure we have mTLS requested - ds := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ConfigString: ` -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: app - namespace: default -spec: - hosts: - - app.com - ports: - - number: 80 - name: http - protocol: HTTP ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: plaintext - namespace: default -spec: - host: app.com - trafficPolicy: - tls: - mode: ISTIO_MUTUAL -`}) - ds.Discovery.Authenticators = []security.Authenticator{auth} - grpcServer := grpc.NewServer(opt) - reflection.Register(grpcServer) - ds.Discovery.Register(grpcServer) - go func() { - _ = grpcServer.Serve(l) - }() - t.Cleanup(grpcServer.Stop) - return net.JoinHostPort("localhost", fmt.Sprint(l.Addr().(*net.TCPAddr).Port)) -} - -type fakePlatform struct { - meta map[string]string - labels map[string]string -} - -func (f *fakePlatform) Metadata() map[string]string { - return f.meta -} - -func (f *fakePlatform) Locality() *core.Locality { - return &core.Locality{} -} - -func (f *fakePlatform) Labels() map[string]string { - return f.labels -} - -func (f *fakePlatform) IsKubernetes() bool { - return true -} diff --git a/pkg/istio-agent/docs/ca.dot b/pkg/istio-agent/docs/ca.dot deleted file mode 100644 index 498f5dcd4..000000000 --- a/pkg/istio-agent/docs/ca.dot +++ /dev/null @@ -1,23 +0,0 @@ -digraph { - grpc -> ca [label="Send CSR gRPC Request"] - subgraph cluster_istioagent { - label = "Istio Agent" - color="orange" - sds - SecretManager -> caClient [label="Sign CSR"] - caClient -> grpc - grpc -> TokenProvider [dir=none,label="Fetch JWT",color=purple] - grpc -> cfiles [dir=none,label="Fetch Cert",color=purple] - - sds -> SecretManager [label="Generate certificate"] - SecretManager -> cfiles [label="Write certs to file"] - cfiles [label="Certificate Files"] - grpc [shape=diamond] - } - - subgraph cluster_istiod { - label = "Istiod" - color="lightblue" - ca - } -} diff --git a/pkg/istio-agent/docs/ca.svg b/pkg/istio-agent/docs/ca.svg deleted file mode 100644 index 71136d6ef..000000000 --- a/pkg/istio-agent/docs/ca.svg +++ /dev/null @@ -1,105 +0,0 @@ - - - -%0 - - -cluster_istioagent - -Istio Agent - - -cluster_istiod - -Istiod - - - -grpc - -grpc - - - -ca - -ca - - - -grpc->ca - - -Send CSR gRPC Request - - - -TokenProvider - -TokenProvider - - - -grpc->TokenProvider - -Fetch JWT - - - -cfiles - -Certificate Files - - - -grpc->cfiles - -Fetch Cert - - - -sds - -sds - - - -SecretManager - -SecretManager - - - -sds->SecretManager - - -Generate certificate - - - -caClient - -caClient - - - -SecretManager->caClient - - -Sign CSR - - - -SecretManager->cfiles - - -Write certs to file - - - -caClient->grpc - - - - - \ No newline at end of file diff --git a/pkg/istio-agent/docs/overview.dot b/pkg/istio-agent/docs/overview.dot deleted file mode 100644 index c664d2c7a..000000000 --- a/pkg/istio-agent/docs/overview.dot +++ /dev/null @@ -1,24 +0,0 @@ -digraph { - envoy -> sds [dir=both, label="SDS"] - envoy -> xdsproxy [dir=both, label="ADS"] - - sds -> ca [label="CSR"] - - xdsproxy -> discovery [dir=both,label="ADS"] - - envoy [shape=hexagon, color=purple] - - subgraph cluster_istioagent { - label = "Istio Agent" - color="orange" - sds - xdsproxy - } - - subgraph cluster_istiod { - label = "Istiod" - color="lightblue" - ca - discovery - } -} diff --git a/pkg/istio-agent/docs/overview.svg b/pkg/istio-agent/docs/overview.svg deleted file mode 100644 index 17d55798e..000000000 --- a/pkg/istio-agent/docs/overview.svg +++ /dev/null @@ -1,78 +0,0 @@ - - - -%0 - - -cluster_istioagent - -Istio Agent - - -cluster_istiod - -Istiod - - - -envoy - -envoy - - - -sds - -sds - - - -envoy->sds - - - -SDS - - - -xdsproxy - -xdsproxy - - - -envoy->xdsproxy - - - -ADS - - - -ca - -ca - - - -sds->ca - - -CSR - - - -discovery - -discovery - - - -xdsproxy->discovery - - - -ADS - - - \ No newline at end of file diff --git a/pkg/istio-agent/docs/sds-flow.svg b/pkg/istio-agent/docs/sds-flow.svg deleted file mode 100644 index 4c1591ff9..000000000 --- a/pkg/istio-agent/docs/sds-flow.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - -
Envoy fetches secrets directly from Spire Agent
Envoy fetches secrets di...
istio-agent starts
istio-agent starts
SDS socket exists? 
SDS socket exists? 
Doesn't start SDS server
Doesn't start SDS server
Certificates files exist?
Certificates files e...
Starts SDS server with the configured caClient
Starts SDS server with t...
Starts SDS server without caClient
Starts SDS server withou...
Envoy fetches secrets directly from istio-agent
Envoy fetches secrets di...
Yes
Yes
No
No
No
No
Yes
Yes
No SDS socket,
neither certificates
No SDS socket,...
No SDS socket,
existing certificates
No SDS socket,...
Existing SDS socket
Existing SDS socket
Text is not SVG - cannot display
\ No newline at end of file diff --git a/pkg/istio-agent/grpcxds/grpc_bootstrap.go b/pkg/istio-agent/grpcxds/grpc_bootstrap.go deleted file mode 100644 index 9721caa40..000000000 --- a/pkg/istio-agent/grpcxds/grpc_bootstrap.go +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package grpcxds - -import ( - "encoding/json" - "fmt" - "os" - "path" - "time" -) - -import ( - corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - "google.golang.org/protobuf/types/known/durationpb" - "google.golang.org/protobuf/types/known/structpb" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/file" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -const ( - ServerListenerNamePrefix = "xds.istio.io/grpc/lds/inbound/" - // ServerListenerNameTemplate for the name of the Listener resource to subscribe to for a gRPC - // server. If the token `%s` is present in the string, all instances of the - // token will be replaced with the server's listening "IP:port" (e.g., - // "0.0.0.0:8080", "[::]:8080"). - ServerListenerNameTemplate = ServerListenerNamePrefix + "%s" -) - -// Bootstrap contains the general structure of what's expected by GRPC's XDS implementation. -// See https://github.com/grpc/grpc-go/blob/master/xds/internal/xdsclient/bootstrap/bootstrap.go -// TODO use structs from gRPC lib if created/exported -type Bootstrap struct { - XDSServers []XdsServer `json:"xds_servers,omitempty"` - Node *corev3.Node `json:"node,omitempty"` - CertProviders map[string]CertificateProvider `json:"certificate_providers,omitempty"` - ServerListenerNameTemplate string `json:"server_listener_resource_name_template,omitempty"` -} - -type ChannelCreds struct { - Type string `json:"type,omitempty"` - Config interface{} `json:"config,omitempty"` -} - -type XdsServer struct { - ServerURI string `json:"server_uri,omitempty"` - ChannelCreds []ChannelCreds `json:"channel_creds,omitempty"` - ServerFeatures []string `json:"server_features,omitempty"` -} - -type CertificateProvider struct { - PluginName string `json:"plugin_name,omitempty"` - Config interface{} `json:"config,omitempty"` -} - -func (cp *CertificateProvider) UnmarshalJSON(data []byte) error { - var dat map[string]*json.RawMessage - if err := json.Unmarshal(data, &dat); err != nil { - return err - } - *cp = CertificateProvider{} - - if pluginNameVal, ok := dat["plugin_name"]; ok { - if err := json.Unmarshal(*pluginNameVal, &cp.PluginName); err != nil { - log.Warnf("failed parsing plugin_name in certificate_provider: %v", err) - } - } else { - log.Warnf("did not find plugin_name in certificate_provider") - } - - if configVal, ok := dat["config"]; ok { - var err error - switch cp.PluginName { - case FileWatcherCertProviderName: - config := FileWatcherCertProviderConfig{} - err = json.Unmarshal(*configVal, &config) - cp.Config = config - default: - config := FileWatcherCertProviderConfig{} - err = json.Unmarshal(*configVal, &config) - cp.Config = config - } - if err != nil { - log.Warnf("failed parsing config in certificate_provider: %v", err) - } - } else { - log.Warnf("did not find config in certificate_provider") - } - - return nil -} - -const FileWatcherCertProviderName = "file_watcher" - -type FileWatcherCertProviderConfig struct { - CertificateFile string `json:"certificate_file,omitempty"` - PrivateKeyFile string `json:"private_key_file,omitempty"` - CACertificateFile string `json:"ca_certificate_file,omitempty"` - RefreshDuration json.RawMessage `json:"refresh_interval,omitempty"` -} - -func (c *FileWatcherCertProviderConfig) FilePaths() []string { - return []string{c.CertificateFile, c.PrivateKeyFile, c.CACertificateFile} -} - -// FileWatcherProvider returns the FileWatcherCertProviderConfig if one exists in CertProviders -func (b *Bootstrap) FileWatcherProvider() *FileWatcherCertProviderConfig { - if b == nil || b.CertProviders == nil { - return nil - } - for _, provider := range b.CertProviders { - if provider.PluginName == FileWatcherCertProviderName { - cfg, ok := provider.Config.(FileWatcherCertProviderConfig) - if !ok { - return nil - } - return &cfg - } - } - return nil -} - -// LoadBootstrap loads a Bootstrap from the given file path. -func LoadBootstrap(file string) (*Bootstrap, error) { - data, err := os.ReadFile(file) - if err != nil { - return nil, err - } - b := &Bootstrap{} - if err := json.Unmarshal(data, b); err != nil { - return nil, err - } - return b, err -} - -type GenerateBootstrapOptions struct { - Node *model.Node - XdsUdsPath string - DiscoveryAddress string - CertDir string -} - -// GenerateBootstrap generates the bootstrap structure for gRPC XDS integration. -func GenerateBootstrap(opts GenerateBootstrapOptions) (*Bootstrap, error) { - xdsMeta, err := extractMeta(opts.Node) - if err != nil { - return nil, fmt.Errorf("failed extracting xds metadata: %v", err) - } - - // TODO direct to CP should use secure channel (most likely JWT + TLS, but possibly allow mTLS) - serverURI := opts.DiscoveryAddress - if opts.XdsUdsPath != "" { - serverURI = fmt.Sprintf("unix:///%s", opts.XdsUdsPath) - } - - bootstrap := Bootstrap{ - XDSServers: []XdsServer{{ - ServerURI: serverURI, - // connect locally via agent - ChannelCreds: []ChannelCreds{{Type: "insecure"}}, - ServerFeatures: []string{"xds_v3"}, - }}, - Node: &corev3.Node{ - Id: opts.Node.ID, - Locality: opts.Node.Locality, - Metadata: xdsMeta, - }, - ServerListenerNameTemplate: ServerListenerNameTemplate, - } - - if opts.CertDir != "" { - // TODO use a more appropriate interval - refresh, err := protomarshal.Marshal(durationpb.New(15 * time.Minute)) - if err != nil { - return nil, err - } - - bootstrap.CertProviders = map[string]CertificateProvider{ - "default": { - PluginName: "file_watcher", - Config: FileWatcherCertProviderConfig{ - PrivateKeyFile: path.Join(opts.CertDir, "key.pem"), - CertificateFile: path.Join(opts.CertDir, "cert-chain.pem"), - CACertificateFile: path.Join(opts.CertDir, "root-cert.pem"), - RefreshDuration: refresh, - }, - }, - } - } - - return &bootstrap, err -} - -func extractMeta(node *model.Node) (*structpb.Struct, error) { - bytes, err := json.Marshal(node.Metadata) - if err != nil { - return nil, err - } - rawMeta := map[string]interface{}{} - if err := json.Unmarshal(bytes, &rawMeta); err != nil { - return nil, err - } - xdsMeta, err := structpb.NewStruct(rawMeta) - if err != nil { - return nil, err - } - return xdsMeta, nil -} - -// GenerateBootstrapFile generates and writes atomically as JSON to the given file path. -func GenerateBootstrapFile(opts GenerateBootstrapOptions, path string) (*Bootstrap, error) { - bootstrap, err := GenerateBootstrap(opts) - if err != nil { - return nil, err - } - jsonData, err := json.MarshalIndent(bootstrap, "", " ") - if err != nil { - return nil, err - } - if err := file.AtomicWrite(path, jsonData, os.FileMode(0o644)); err != nil { - return nil, fmt.Errorf("failed writing to %s: %v", path, err) - } - return bootstrap, nil -} diff --git a/pkg/istio-agent/health/health_check.go b/pkg/istio-agent/health/health_check.go deleted file mode 100644 index 32ca84894..000000000 --- a/pkg/istio-agent/health/health_check.go +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package health - -import ( - "net/http" - "strings" - "time" -) - -import ( - "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/status" - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/status/ready" - "github.com/apache/dubbo-go-pixiu/pkg/kube/apimirror" -) - -type WorkloadHealthChecker struct { - config applicationHealthCheckConfig - prober Prober -} - -// internal field purely for convenience -type applicationHealthCheckConfig struct { - InitialDelay time.Duration - ProbeTimeout time.Duration - CheckFrequency time.Duration - SuccessThresh int - FailThresh int -} - -type ProbeEvent struct { - Healthy bool - UnhealthyStatus int32 - UnhealthyMessage string -} - -const ( - lastStateUndefined = iota - lastStateHealthy - lastStateUnhealthy -) - -func fillInDefaults(cfg *v1alpha3.ReadinessProbe, ipAddresses []string) *v1alpha3.ReadinessProbe { - cfg = cfg.DeepCopy() - // Thresholds have a minimum of 1 - cfg.FailureThreshold = orDefault(cfg.FailureThreshold, 1) - cfg.SuccessThreshold = orDefault(cfg.SuccessThreshold, 1) - - // InitialDelaySeconds allows zero, no default needed - cfg.TimeoutSeconds = orDefault(cfg.TimeoutSeconds, 1) - cfg.PeriodSeconds = orDefault(cfg.PeriodSeconds, 10) - - switch h := cfg.HealthCheckMethod.(type) { - case *v1alpha3.ReadinessProbe_HttpGet: - if h.HttpGet.Path == "" { - h.HttpGet.Path = "/" - } - if h.HttpGet.Scheme == "" { - h.HttpGet.Scheme = string(apimirror.URISchemeHTTP) - } - h.HttpGet.Scheme = strings.ToLower(h.HttpGet.Scheme) - if h.HttpGet.Host == "" { - if len(ipAddresses) == 0 || status.LegacyLocalhostProbeDestination.Get() { - h.HttpGet.Host = "localhost" - } else { - h.HttpGet.Host = ipAddresses[0] - } - } - } - return cfg -} - -func NewWorkloadHealthChecker(cfg *v1alpha3.ReadinessProbe, envoyProbe ready.Prober, proxyAddrs []string, ipv6 bool) *WorkloadHealthChecker { - // if a config does not exist return a no-op prober - if cfg == nil { - return nil - } - cfg = fillInDefaults(cfg, proxyAddrs) - var prober Prober - switch healthCheckMethod := cfg.HealthCheckMethod.(type) { - case *v1alpha3.ReadinessProbe_HttpGet: - prober = NewHTTPProber(healthCheckMethod.HttpGet, ipv6) - case *v1alpha3.ReadinessProbe_TcpSocket: - prober = &TCPProber{Config: healthCheckMethod.TcpSocket} - case *v1alpha3.ReadinessProbe_Exec: - prober = &ExecProber{Config: healthCheckMethod.Exec} - default: - prober = nil - } - - probers := []Prober{} - if envoyProbe != nil { - probers = append(probers, &EnvoyProber{envoyProbe}) - } - probers = append(probers, prober) - return &WorkloadHealthChecker{ - config: applicationHealthCheckConfig{ - InitialDelay: time.Duration(cfg.InitialDelaySeconds) * time.Second, - ProbeTimeout: time.Duration(cfg.TimeoutSeconds) * time.Second, - CheckFrequency: time.Duration(cfg.PeriodSeconds) * time.Second, - SuccessThresh: int(cfg.SuccessThreshold), - FailThresh: int(cfg.FailureThreshold), - }, - prober: AggregateProber{Probes: probers}, - } -} - -func orDefault(val int32, def int32) int32 { - if val == 0 { - return def - } - return val -} - -// PerformApplicationHealthCheck Performs the application-provided configuration health check. -// Instead of a heartbeat-based health checks, we only send on a health state change, and this is -// determined by the success & failure threshold provided by the user. -func (w *WorkloadHealthChecker) PerformApplicationHealthCheck(callback func(*ProbeEvent), quit chan struct{}) { - if w == nil { - return - } - - healthCheckLog.Infof("starting health check for %T in %v", w.prober, w.config.InitialDelay) - // delay before starting probes. - time.Sleep(w.config.InitialDelay) - - // tracks number of success & failures after last success/failure - numSuccess, numFail := 0, 0 - // if the last send/event was a success, this is true, by default false because we want to - // first send a healthy message. - lastState := lastStateUndefined - - doCheck := func() { - // probe target - healthy, err := w.prober.Probe(w.config.ProbeTimeout) - if healthy.IsHealthy() { - healthCheckLog.Debug("probe completed with healthy status") - // we were healthy, increment success counter - numSuccess++ - // wipe numFail (need consecutive success) - numFail = 0 - // if we reached the threshold, mark the target as healthy - if numSuccess == w.config.SuccessThresh && lastState != lastStateHealthy { - healthCheckLog.Info("success threshold hit, marking as healthy") - callback(&ProbeEvent{Healthy: true}) - numSuccess = 0 - lastState = lastStateHealthy - } - } else { - healthCheckLog.Debugf("probe completed with unhealthy status: %v", err) - // we were not healthy, increment fail counter - numFail++ - // wipe numSuccess (need consecutive failure) - numSuccess = 0 - // if we reached the fail threshold, mark the target as unhealthy - if numFail == w.config.FailThresh && lastState != lastStateUnhealthy { - healthCheckLog.Infof("failure threshold hit, marking as unhealthy: %v", err) - numFail = 0 - callback(&ProbeEvent{ - Healthy: false, - UnhealthyStatus: http.StatusInternalServerError, - UnhealthyMessage: err.Error(), - }) - lastState = lastStateUnhealthy - } - } - } - - // Send the first request immediately - doCheck() - periodTicker := time.NewTicker(w.config.CheckFrequency) - defer periodTicker.Stop() - for { - select { - case <-quit: - return - case <-periodTicker.C: - doCheck() - } - } -} diff --git a/pkg/istio-agent/health/health_check_test.go b/pkg/istio-agent/health/health_check_test.go deleted file mode 100644 index b17a5241a..000000000 --- a/pkg/istio-agent/health/health_check_test.go +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package health - -import ( - "fmt" - "net" - "net/http" - "net/http/httptest" - "strconv" - "strings" - "testing" - "time" -) - -import ( - "go.uber.org/atomic" - "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/util/reserveport" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -func TestWorkloadHealthChecker_PerformApplicationHealthCheck(t *testing.T) { - t.Run("tcp", func(t *testing.T) { - port := reserveport.NewPortManagerOrFail(t).ReservePortNumberOrFail(t) - tcpHealthChecker := NewWorkloadHealthChecker(&v1alpha3.ReadinessProbe{ - InitialDelaySeconds: 0, - TimeoutSeconds: 1, - PeriodSeconds: 1, - SuccessThreshold: 1, - FailureThreshold: 1, - HealthCheckMethod: &v1alpha3.ReadinessProbe_TcpSocket{ - TcpSocket: &v1alpha3.TCPHealthCheckConfig{ - Host: "localhost", - Port: uint32(port), - }, - }, - }, nil, []string{"127.0.0.1"}, false) - // Speed up tests - tcpHealthChecker.config.CheckFrequency = time.Millisecond - - quitChan := make(chan struct{}) - - expectedTCPEvents := [7]*ProbeEvent{ - {Healthy: false}, - {Healthy: true}, - {Healthy: false}, - {Healthy: true}, - {Healthy: false}, - {Healthy: true}, - {Healthy: false}, - } - tcpHealthStatuses := [7]bool{false, true, false, true, false, true, false} - - cont := make(chan struct{}, 6) - // wait for go-ahead for state change - go func() { - for i := 0; i < len(tcpHealthStatuses); i++ { - if tcpHealthStatuses[i] { - var srv net.Listener - // open port until we get confirmation that - // retry in case of port conflicts - if err := retry.UntilSuccess(func() (err error) { - srv, err = net.Listen("tcp", fmt.Sprintf("localhost:%d", port)) - return - }, retry.Delay(time.Millisecond)); err != nil { - t.Log(err) - return - } - <-cont - srv.Close() - } else { - <-cont - } - } - }() - - eventNum := atomic.NewInt32(0) - go tcpHealthChecker.PerformApplicationHealthCheck(func(event *ProbeEvent) { - if eventNum.Load() >= 7 { - return - } - if event.Healthy != expectedTCPEvents[eventNum.Load()].Healthy { - t.Errorf("%s: got event healthy: %v at idx %v when expected healthy: %v", "tcp", event.Healthy, eventNum.Load(), expectedTCPEvents[eventNum.Load()].Healthy) - } - cont <- struct{}{} - eventNum.Inc() - }, quitChan) - retry.UntilSuccessOrFail(t, func() error { - if int(eventNum.Load()) != len(expectedTCPEvents) { - return fmt.Errorf("waiting for %v events", len(expectedTCPEvents)-int(eventNum.Load())) - } - return nil - }, retry.Delay(time.Millisecond*10), retry.Timeout(time.Second)) - close(quitChan) - }) - t.Run("http", func(t *testing.T) { - httpPath := "/test/health/check" - httpHealthStatuses := [4]bool{true, false, false, true} - httpServerEventCount := 0 - sss := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - if httpServerEventCount < len(httpHealthStatuses) && httpHealthStatuses[httpServerEventCount] { - writer.WriteHeader(200) - writer.Write([]byte("foobar")) - } else { - writer.WriteHeader(500) - } - httpServerEventCount++ - })) - host, ports, err := net.SplitHostPort(strings.TrimPrefix(sss.URL, "http://")) - if err != nil { - t.Fatal(err) - } - port, err := strconv.Atoi(ports) - if err != nil { - t.Fatal(err) - } - t.Cleanup(sss.Close) - httpHealthChecker := NewWorkloadHealthChecker(&v1alpha3.ReadinessProbe{ - InitialDelaySeconds: 0, - TimeoutSeconds: 1, - PeriodSeconds: 1, - SuccessThreshold: 1, - FailureThreshold: 1, - HealthCheckMethod: &v1alpha3.ReadinessProbe_HttpGet{ - HttpGet: &v1alpha3.HTTPHealthCheckConfig{ - Path: httpPath, - Port: uint32(port), - Scheme: "http", - Host: host, - }, - }, - }, nil, []string{"127.0.0.1"}, false) - // Speed up tests - httpHealthChecker.config.CheckFrequency = time.Millisecond - quitChan := make(chan struct{}) - t.Cleanup(func() { - close(quitChan) - }) - expectedHTTPEvents := [4]*ProbeEvent{ - {Healthy: true}, - {Healthy: false}, - {Healthy: true}, - {Healthy: false}, - } - - eventNum := atomic.NewInt32(0) - go httpHealthChecker.PerformApplicationHealthCheck(func(event *ProbeEvent) { - if event.Healthy != expectedHTTPEvents[eventNum.Load()].Healthy { - t.Errorf("tcp: got event healthy: %v at idx %v when expected healthy: %v", - event.Healthy, eventNum.Load(), expectedHTTPEvents[eventNum.Load()].Healthy) - } - eventNum.Inc() - }, quitChan) - - retry.UntilSuccessOrFail(t, func() error { - if int(eventNum.Load()) != len(expectedHTTPEvents) { - return fmt.Errorf("waiting for %v events", len(expectedHTTPEvents)-int(eventNum.Load())) - } - return nil - }, retry.Delay(time.Millisecond*10), retry.Timeout(time.Second)) - }) -} diff --git a/pkg/istio-agent/health/health_probers.go b/pkg/istio-agent/health/health_probers.go deleted file mode 100644 index af8b4bc3b..000000000 --- a/pkg/istio-agent/health/health_probers.go +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package health - -import ( - "context" - "crypto/tls" - "fmt" - "net" - "net/http" - "net/url" - "os/exec" - "strconv" - "time" -) - -import ( - "istio.io/api/networking/v1alpha3" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/status" - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/status/ready" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common/scheme" -) - -var healthCheckLog = log.RegisterScope("healthcheck", "Health Checks performed by Istio-Agent", 0) - -type Prober interface { - // Probe will healthcheck and return whether or not the target is healthy. - // If an error returned is not nil, it is assumed that the process could - // not complete, and Probe() was unable to determine whether or not the - // target was healthy. - Probe(timeout time.Duration) (ProbeResult, error) -} - -type ProbeResult string - -const ( - Healthy ProbeResult = "HEALTHY" - Unhealthy ProbeResult = "UNHEALTHY" - Unknown ProbeResult = "UNKNOWN" -) - -func (p *ProbeResult) IsHealthy() bool { - return *p == Healthy -} - -type HTTPProber struct { - Config *v1alpha3.HTTPHealthCheckConfig - Transport *http.Transport -} - -var _ Prober = &HTTPProber{} - -func NewHTTPProber(cfg *v1alpha3.HTTPHealthCheckConfig, ipv6 bool) *HTTPProber { - h := new(HTTPProber) - h.Config = cfg - - // Create an http.Transport with TLSClientConfig for HTTPProber if the scheme is https, - // otherwise set up an empty one. - if cfg.Scheme == string(scheme.HTTPS) { - h.Transport = &http.Transport{ - DisableKeepAlives: true, - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } - } else { - h.Transport = &http.Transport{ - DisableKeepAlives: true, - } - } - d := &net.Dialer{ - LocalAddr: status.UpstreamLocalAddressIPv4, - } - if ipv6 { - d.LocalAddr = status.UpstreamLocalAddressIPv6 - } - h.Transport.DialContext = d.DialContext - return h -} - -// HttpProber_Probe will return whether or not the target is healthy (true -> healthy) -// -// by making an HTTP Get response. -func (h *HTTPProber) Probe(timeout time.Duration) (ProbeResult, error) { - client := &http.Client{ - Timeout: timeout, - Transport: h.Transport, - } - // transform crd into net http header - headers := make(http.Header) - for _, val := range h.Config.HttpHeaders { - // net.httpHeaders value is a []string but uses only index 0 - headers[val.Name] = append(headers[val.Name], val.Value) - } - targetURL, err := url.Parse(h.Config.Path) - // Something is busted with the path, but it's too late to reject it. Pass it along as is. - if err != nil { - targetURL = &url.URL{ - Path: h.Config.Path, - } - } - targetURL.Scheme = h.Config.Scheme - targetURL.Host = net.JoinHostPort(h.Config.Host, strconv.Itoa(int(h.Config.Port))) - if err != nil { - healthCheckLog.Errorf("unable to parse url: %v", err) - return Unknown, err - } - req, err := http.NewRequest("GET", targetURL.String(), nil) - if err != nil { - return Unknown, err - } - req.Header = headers - if headers.Get("Host") != "" { - req.Host = headers.Get("Host") - } - if _, ok := headers["User-Agent"]; !ok { - // explicitly set User-Agent so it's not set to default Go value. K8s use kube-probe. - headers.Set("User-Agent", "istio-probe/1.0") - } - res, err := client.Do(req) - // if we were unable to connect, count as failure - if err != nil { - return Unhealthy, err - } - defer func() { - err = res.Body.Close() - if err != nil { - healthCheckLog.Error(err) - } - }() - // from [200,400) - if res.StatusCode >= http.StatusOK && res.StatusCode < http.StatusBadRequest { - return Healthy, nil - } - return Unhealthy, fmt.Errorf("status code was not from [200,400), bad code %v", res.StatusCode) -} - -type TCPProber struct { - Config *v1alpha3.TCPHealthCheckConfig -} - -var _ Prober = &TCPProber{} - -func (t *TCPProber) Probe(timeout time.Duration) (ProbeResult, error) { - // if we cant connect, count as fail - conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%v", t.Config.Host, t.Config.Port), timeout) - if err != nil { - return Unhealthy, err - } - err = conn.Close() - if err != nil { - healthCheckLog.Errorf("Unable to close TCP Socket: %v", err) - } - return Healthy, nil -} - -type ExecProber struct { - Config *v1alpha3.ExecHealthCheckConfig -} - -var _ Prober = &ExecProber{} - -func (e *ExecProber) Probe(timeout time.Duration) (ProbeResult, error) { - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - cmd := exec.CommandContext(ctx, e.Config.Command[0], e.Config.Command[1:]...) - if err := cmd.Run(); err != nil { - select { - case <-ctx.Done(): - return Unhealthy, fmt.Errorf("command timeout exceeded: %v", err) - default: - } - return Unhealthy, err - } - return Healthy, nil -} - -type EnvoyProber struct { - Config ready.Prober -} - -var _ Prober = &EnvoyProber{} - -func (a EnvoyProber) Probe(time.Duration) (ProbeResult, error) { - if err := a.Config.Check(); err != nil { - return Unhealthy, err - } - return Healthy, nil -} - -type AggregateProber struct { - Probes []Prober -} - -var _ Prober = &AggregateProber{} - -func (a AggregateProber) Probe(timeout time.Duration) (ProbeResult, error) { - for _, probe := range a.Probes { - res, err := probe.Probe(timeout) - if err != nil || !res.IsHealthy() { - return res, err - } - } - return Healthy, nil -} diff --git a/pkg/istio-agent/health/health_probers_test.go b/pkg/istio-agent/health/health_probers_test.go deleted file mode 100644 index f8e5e0262..000000000 --- a/pkg/istio-agent/health/health_probers_test.go +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package health - -import ( - "errors" - "net" - "net/http" - "net/http/httptest" - "net/url" - "strconv" - "testing" - "time" -) - -import ( - "istio.io/api/networking/v1alpha3" -) - -func TestHttpProber(t *testing.T) { - tests := []struct { - desc string - statusCode int - expectedProbeResult ProbeResult - expectedError error - }{ - { - desc: "Healthy - 200 status code", - statusCode: 200, - expectedProbeResult: Healthy, - expectedError: nil, - }, - { - desc: "Unhealthy - 500 status code", - statusCode: 500, - expectedProbeResult: Unhealthy, - expectedError: errors.New("status code was not from [200,400)"), - }, - { - desc: "Unhealthy - Could not connect to server", - statusCode: -1, - expectedProbeResult: Unhealthy, - expectedError: errors.New("dial tcp 127.0.0.1:: connect: connection refused"), - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - server, port := createHTTPServer(tt.statusCode) - defer server.Close() - httpProber := NewHTTPProber( - &v1alpha3.HTTPHealthCheckConfig{ - Path: "/test/health/check", - Port: port, - Host: "127.0.0.1", - Scheme: "http", - }, false) - - if tt.statusCode == -1 { - server.Close() - } - - got, err := httpProber.Probe(time.Second) - if got != tt.expectedProbeResult || (err == nil && tt.expectedError != nil) || (err != nil && tt.expectedError == nil) { - t.Errorf("%s: got: %v, expected: %v, got error: %v, expected error %v", tt.desc, got, tt.expectedProbeResult, err, tt.expectedError) - } - }) - } -} - -func TestTcpProber(t *testing.T) { - tests := []struct { - desc string - expectedProbeResult ProbeResult - expectedError error - }{ - { - desc: "Healthy", - expectedProbeResult: Healthy, - expectedError: nil, - }, - { - desc: "Unhealthy", - expectedProbeResult: Unhealthy, - expectedError: errors.New("dial tcp 127.0.0.1:: connect: connection refused"), - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - server, port := createHTTPServer(200) - defer server.Close() - tcpProber := TCPProber{ - Config: &v1alpha3.TCPHealthCheckConfig{ - Host: "127.0.0.1", - Port: port, - }, - } - - if tt.expectedProbeResult == Unhealthy { - server.Close() - } - - got, err := tcpProber.Probe(time.Second) - if got != tt.expectedProbeResult || (err == nil && tt.expectedError != nil) || (err != nil && tt.expectedError == nil) { - t.Errorf("%s: got: %v, expected: %v, got error: %v, expected error %v", tt.desc, got, tt.expectedProbeResult, err, tt.expectedError) - } - }) - } -} - -func TestExecProber(t *testing.T) { - tests := []struct { - desc string - command []string - expectedProbeResult ProbeResult - expectedError error - }{ - { - desc: "Healthy", - command: []string{"true"}, - expectedProbeResult: Healthy, - expectedError: nil, - }, - { - desc: "Unhealthy", - command: []string{"false"}, - expectedProbeResult: Unhealthy, - expectedError: errors.New("exit status 1"), - }, - { - desc: "Timeout", - command: []string{"sleep", "10"}, - expectedProbeResult: Unhealthy, - expectedError: errors.New("command timeout exceeded: signal: killed"), - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - execProber := &ExecProber{ - Config: &v1alpha3.ExecHealthCheckConfig{ - Command: tt.command, - }, - } - - got, err := execProber.Probe(time.Millisecond * 200) - if got != tt.expectedProbeResult { - t.Errorf("got: %v, expected: %v", got, tt.expectedProbeResult) - } - errorOrEmpty := func(e error) string { - if e != nil { - return e.Error() - } - return "" - } - if errorOrEmpty(err) != errorOrEmpty(tt.expectedError) { - t.Errorf("got err: %v, expected err: %v", err, tt.expectedError) - } - }) - } -} - -func createHTTPServer(statusCode int) (*httptest.Server, uint32) { - server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - writer.WriteHeader(statusCode) - })) - - u, _ := url.Parse(server.URL) - _, p, _ := net.SplitHostPort(u.Host) - port, _ := strconv.ParseUint(p, 10, 32) - - return server, uint32(port) -} diff --git a/pkg/istio-agent/health/leak_test.go b/pkg/istio-agent/health/leak_test.go deleted file mode 100644 index 224c3096a..000000000 --- a/pkg/istio-agent/health/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package health - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pkg/istio-agent/leak_test.go b/pkg/istio-agent/leak_test.go deleted file mode 100644 index 98bbc9e1a..000000000 --- a/pkg/istio-agent/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istioagent - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pkg/istio-agent/metrics/metrics.go b/pkg/istio-agent/metrics/metrics.go deleted file mode 100644 index a5fd32a00..000000000 --- a/pkg/istio-agent/metrics/metrics.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package metrics - -import ( - "istio.io/pkg/monitoring" -) - -var ( - disconnectionTypeTag = monitoring.MustCreateLabel("type") - - // IstiodConnectionFailures records total number of connection failures to Istiod. - IstiodConnectionFailures = monitoring.NewSum( - "istiod_connection_failures", - "The total number of connection failures to Istiod", - ) - - // istiodDisconnections records total number of unexpected disconnections by Istiod. - istiodDisconnections = monitoring.NewSum( - "istiod_connection_terminations", - "The total number of connection errors to Istiod", - monitoring.WithLabels(disconnectionTypeTag), - ) - - // envoyDisconnections records total number of unexpected disconnections by Envoy. - envoyDisconnections = monitoring.NewSum( - "envoy_connection_terminations", - "The total number of connection errors from envoy", - monitoring.WithLabels(disconnectionTypeTag), - ) - - // TODO: Add type url as type for requeasts and responses if needed. - - // XdsProxyRequests records total number of downstream requests. - XdsProxyRequests = monitoring.NewSum( - "xds_proxy_requests", - "The total number of Xds Proxy Requests", - ) - - // XdsProxyResponses records total number of upstream responses. - XdsProxyResponses = monitoring.NewSum( - "xds_proxy_responses", - "The total number of Xds Proxy Responses", - ) - - IstiodConnectionCancellations = istiodDisconnections.With(disconnectionTypeTag.Value(Cancel)) - IstiodConnectionErrors = istiodDisconnections.With(disconnectionTypeTag.Value(Error)) - EnvoyConnectionCancellations = envoyDisconnections.With(disconnectionTypeTag.Value(Cancel)) - EnvoyConnectionErrors = envoyDisconnections.With(disconnectionTypeTag.Value(Error)) -) - -var ( - Cancel = "cancelled" - Error = "error" -) - -func init() { - monitoring.MustRegister( - IstiodConnectionFailures, - IstiodConnectionErrors, - istiodDisconnections, - envoyDisconnections, - ) -} diff --git a/pkg/istio-agent/testdata/grpc-bootstrap.json b/pkg/istio-agent/testdata/grpc-bootstrap.json deleted file mode 100644 index f86cacac5..000000000 --- a/pkg/istio-agent/testdata/grpc-bootstrap.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "xds_servers": [ - { - "server_uri": "unix:///etc/istio/XDS", - "channel_creds": [ - { - "type": "insecure" - } - ], - "server_features": [ - "xds_v3" - ] - } - ], - "node": { - "id": "sidecar~127.0.0.1~pod1.fake-namespace~fake-namespace.svc.cluster.local", - "metadata": { - "INSTANCE_IPS": "127.0.0.1", - "PILOT_SAN": [ - "istiod.dubbo-system.svc" - ] - }, - "locality": {}, - "UserAgentVersionType": null - }, - "certificate_providers": { - "default": { - "plugin_name": "file_watcher", - "config": { - "certificate_file": "/cert/path/cert-chain.pem", - "private_key_file": "/cert/path/key.pem", - "ca_certificate_file": "/cert/path/root-cert.pem", - "refresh_interval": "900s" - } - } - }, - "server_listener_resource_name_template": "xds.istio.io/grpc/lds/inbound/%s" -} \ No newline at end of file diff --git a/pkg/istio-agent/testdata/token b/pkg/istio-agent/testdata/token deleted file mode 100644 index f0f877ced..000000000 --- a/pkg/istio-agent/testdata/token +++ /dev/null @@ -1 +0,0 @@ -fake \ No newline at end of file diff --git a/pkg/istio-agent/xds_proxy.go b/pkg/istio-agent/xds_proxy.go deleted file mode 100644 index 0aa381ed2..000000000 --- a/pkg/istio-agent/xds_proxy.go +++ /dev/null @@ -1,882 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istioagent - -import ( - "context" - "crypto/tls" - "crypto/x509" - "encoding/json" - "fmt" - "math" - "net" - "net/http" - "net/url" - "os" - "strings" - "sync" - "time" -) - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "go.uber.org/atomic" - google_rpc "google.golang.org/genproto/googleapis/rpc/status" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/keepalive" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/reflection" - any "google.golang.org/protobuf/types/known/anypb" - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/status/ready" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - istiogrpc "github.com/apache/dubbo-go-pixiu/pilot/pkg/grpc" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - dnsProto "github.com/apache/dubbo-go-pixiu/pkg/dns/proto" - "github.com/apache/dubbo-go-pixiu/pkg/istio-agent/health" - "github.com/apache/dubbo-go-pixiu/pkg/istio-agent/metrics" - istiokeepalive "github.com/apache/dubbo-go-pixiu/pkg/keepalive" - "github.com/apache/dubbo-go-pixiu/pkg/uds" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" - "github.com/apache/dubbo-go-pixiu/pkg/wasm" - "github.com/apache/dubbo-go-pixiu/security/pkg/nodeagent/caclient" - "github.com/apache/dubbo-go-pixiu/security/pkg/pki/util" -) - -const ( - defaultClientMaxReceiveMessageSize = math.MaxInt32 - defaultInitialConnWindowSize = 1024 * 1024 // default gRPC InitialWindowSize - defaultInitialWindowSize = 1024 * 1024 // default gRPC ConnWindowSize -) - -var connectionNumber = atomic.NewUint32(0) - -// ResponseHandler handles a XDS response in the agent. These will not be forwarded to Envoy. -// Currently, all handlers function on a single resource per type, so the API only exposes one -// resource. -type ResponseHandler func(resp *any.Any) error - -// XDS Proxy proxies all XDS requests from envoy to istiod, in addition to allowing -// subsystems inside the agent to also communicate with either istiod/envoy (eg dns, sds, etc). -// The goal here is to consolidate all xds related connections to istiod/envoy into a -// single tcp connection with multiple gRPC streams. -// TODO: Right now, the workloadSDS server and gatewaySDS servers are still separate -// connections. These need to be consolidated. -// TODO: consolidate/use ADSC struct - a lot of duplication. -type XdsProxy struct { - stopChan chan struct{} - clusterID string - downstreamListener net.Listener - downstreamGrpcServer *grpc.Server - istiodAddress string - istiodDialOptions []grpc.DialOption - optsMutex sync.RWMutex - handlers map[string]ResponseHandler - healthChecker *health.WorkloadHealthChecker - xdsHeaders map[string]string - xdsUdsPath string - proxyAddresses []string - - httpTapServer *http.Server - tapMutex sync.RWMutex - tapResponseChannel chan *discovery.DiscoveryResponse - - // connected stores the active gRPC stream. The proxy will only have 1 connection at a time - connected *ProxyConnection - initialRequest *discovery.DiscoveryRequest - initialDeltaRequest *discovery.DeltaDiscoveryRequest - connectedMutex sync.RWMutex - - // Wasm cache and ecds channel are used to replace wasm remote load with local file. - wasmCache wasm.Cache - - // ecds version and nonce uses atomic only to prevent race in testing. - // In reality there should not be race as istiod will only have one - // in flight update for each type of resource. - // TODO(bianpengyuan): this relies on the fact that istiod versions all ECDS resources - // the same in a update response. This needs update to support per resource versioning, - // in case istiod changes its behavior, or a different ECDS server is used. - ecdsLastAckVersion atomic.String - ecdsLastNonce atomic.String - downstreamGrpcOptions []grpc.ServerOption - istiodSAN string -} - -var proxyLog = log.RegisterScope("xdsproxy", "XDS Proxy in Istio Agent", 0) - -const ( - localHostIPv4 = "127.0.0.1" - localHostIPv6 = "[::1]" -) - -func initXdsProxy(ia *Agent) (*XdsProxy, error) { - var err error - localHostAddr := localHostIPv4 - if ia.cfg.IsIPv6 { - localHostAddr = localHostIPv6 - } - var envoyProbe ready.Prober - if !ia.cfg.DisableEnvoy { - envoyProbe = &ready.Probe{ - AdminPort: uint16(ia.proxyConfig.ProxyAdminPort), - LocalHostAddr: localHostAddr, - } - } - - cache := wasm.NewLocalFileCache(constants.IstioDataDir, wasm.DefaultWasmModulePurgeInterval, wasm.DefaultWasmModuleExpiry, ia.cfg.WASMInsecureRegistries) - proxy := &XdsProxy{ - istiodAddress: ia.proxyConfig.DiscoveryAddress, - istiodSAN: ia.cfg.IstiodSAN, - clusterID: ia.secOpts.ClusterID, - handlers: map[string]ResponseHandler{}, - stopChan: make(chan struct{}), - healthChecker: health.NewWorkloadHealthChecker(ia.proxyConfig.ReadinessProbe, envoyProbe, ia.cfg.ProxyIPAddresses, ia.cfg.IsIPv6), - xdsHeaders: ia.cfg.XDSHeaders, - xdsUdsPath: ia.cfg.XdsUdsPath, - wasmCache: cache, - proxyAddresses: ia.cfg.ProxyIPAddresses, - downstreamGrpcOptions: ia.cfg.DownstreamGrpcOptions, - } - - if ia.localDNSServer != nil { - proxy.handlers[v3.NameTableType] = func(resp *any.Any) error { - var nt dnsProto.NameTable - if err := resp.UnmarshalTo(&nt); err != nil { - log.Errorf("failed to unmarshal name table: %v", err) - return err - } - ia.localDNSServer.UpdateLookupTable(&nt) - return nil - } - } - if ia.cfg.EnableDynamicProxyConfig && ia.secretCache != nil { - proxy.handlers[v3.ProxyConfigType] = func(resp *any.Any) error { - pc := &meshconfig.ProxyConfig{} - if err := resp.UnmarshalTo(pc); err != nil { - log.Errorf("failed to unmarshal proxy config: %v", err) - return err - } - caCerts := pc.GetCaCertificatesPem() - log.Debugf("received new certificates to add to mesh trust domain: %v", caCerts) - trustBundle := []byte{} - for _, cert := range caCerts { - trustBundle = util.AppendCertByte(trustBundle, []byte(cert)) - } - return ia.secretCache.UpdateConfigTrustBundle(trustBundle) - } - } - - proxyLog.Infof("Initializing with upstream address %q and cluster %q", proxy.istiodAddress, proxy.clusterID) - - if err = proxy.initDownstreamServer(); err != nil { - return nil, err - } - - if err = proxy.InitIstiodDialOptions(ia); err != nil { - return nil, err - } - - go func() { - if err := proxy.downstreamGrpcServer.Serve(proxy.downstreamListener); err != nil { - log.Errorf("failed to accept downstream gRPC connection %v", err) - } - }() - - go proxy.healthChecker.PerformApplicationHealthCheck(func(healthEvent *health.ProbeEvent) { - // Store the same response as Delta and SotW. Depending on how Envoy connects we will use one or the other. - var req *discovery.DiscoveryRequest - if healthEvent.Healthy { - req = &discovery.DiscoveryRequest{TypeUrl: v3.HealthInfoType} - } else { - req = &discovery.DiscoveryRequest{ - TypeUrl: v3.HealthInfoType, - ErrorDetail: &google_rpc.Status{ - Code: int32(codes.Internal), - Message: healthEvent.UnhealthyMessage, - }, - } - } - proxy.PersistRequest(req) - var deltaReq *discovery.DeltaDiscoveryRequest - if healthEvent.Healthy { - deltaReq = &discovery.DeltaDiscoveryRequest{TypeUrl: v3.HealthInfoType} - } else { - deltaReq = &discovery.DeltaDiscoveryRequest{ - TypeUrl: v3.HealthInfoType, - ErrorDetail: &google_rpc.Status{ - Code: int32(codes.Internal), - Message: healthEvent.UnhealthyMessage, - }, - } - } - proxy.PersistDeltaRequest(deltaReq) - }, proxy.stopChan) - - return proxy, nil -} - -// PersistRequest sends a request to the currently connected proxy. Additionally, on any reconnection -// to the upstream XDS request we will resend this request. -func (p *XdsProxy) PersistRequest(req *discovery.DiscoveryRequest) { - var ch chan *discovery.DiscoveryRequest - var stop chan struct{} - - p.connectedMutex.Lock() - if p.connected != nil { - ch = p.connected.requestsChan - stop = p.connected.stopChan - } - p.initialRequest = req - p.connectedMutex.Unlock() - - // Immediately send if we are currently connect - if ch != nil { - select { - case ch <- req: - case <-stop: - } - } -} - -func (p *XdsProxy) UnregisterStream(c *ProxyConnection) { - p.connectedMutex.Lock() - defer p.connectedMutex.Unlock() - if p.connected != nil && p.connected == c { - close(p.connected.stopChan) - p.connected = nil - } -} - -func (p *XdsProxy) RegisterStream(c *ProxyConnection) { - p.connectedMutex.Lock() - defer p.connectedMutex.Unlock() - if p.connected != nil { - proxyLog.Warnf("registered overlapping stream; closing previous") - close(p.connected.stopChan) - } - p.connected = c -} - -type ProxyConnection struct { - conID uint32 - upstreamError chan error - downstreamError chan error - requestsChan chan *discovery.DiscoveryRequest - responsesChan chan *discovery.DiscoveryResponse - deltaRequestsChan chan *discovery.DeltaDiscoveryRequest - deltaResponsesChan chan *discovery.DeltaDiscoveryResponse - stopChan chan struct{} - downstream adsStream - upstream discovery.AggregatedDiscoveryService_StreamAggregatedResourcesClient - downstreamDeltas discovery.AggregatedDiscoveryService_DeltaAggregatedResourcesServer - upstreamDeltas discovery.AggregatedDiscoveryService_DeltaAggregatedResourcesClient -} - -// sendRequest is a small wrapper around sending to con.requestsChan. This ensures that we do not -// block forever on -func (con *ProxyConnection) sendRequest(req *discovery.DiscoveryRequest) { - select { - case con.requestsChan <- req: - case <-con.stopChan: - } -} - -type adsStream interface { - Send(*discovery.DiscoveryResponse) error - Recv() (*discovery.DiscoveryRequest, error) - Context() context.Context -} - -// Every time envoy makes a fresh connection to the agent, we reestablish a new connection to the upstream xds -// This ensures that a new connection between istiod and agent doesn't end up consuming pending messages from envoy -// as the new connection may not go to the same istiod. Vice versa case also applies. -func (p *XdsProxy) StreamAggregatedResources(downstream discovery.AggregatedDiscoveryService_StreamAggregatedResourcesServer) error { - proxyLog.Debugf("accepted XDS connection from Envoy, forwarding to upstream XDS server") - return p.handleStream(downstream) -} - -func (p *XdsProxy) handleStream(downstream adsStream) error { - con := &ProxyConnection{ - conID: connectionNumber.Inc(), - upstreamError: make(chan error, 2), // can be produced by recv and send - downstreamError: make(chan error, 2), // can be produced by recv and send - requestsChan: make(chan *discovery.DiscoveryRequest, 10), - responsesChan: make(chan *discovery.DiscoveryResponse, 10), - stopChan: make(chan struct{}), - downstream: downstream, - } - - p.RegisterStream(con) - defer p.UnregisterStream(con) - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - defer cancel() - - upstreamConn, err := p.buildUpstreamConn(ctx) - if err != nil { - proxyLog.Errorf("failed to connect to upstream %s: %v", p.istiodAddress, err) - metrics.IstiodConnectionFailures.Increment() - return err - } - defer upstreamConn.Close() - - xds := discovery.NewAggregatedDiscoveryServiceClient(upstreamConn) - ctx = metadata.AppendToOutgoingContext(context.Background(), "ClusterID", p.clusterID) - for k, v := range p.xdsHeaders { - ctx = metadata.AppendToOutgoingContext(ctx, k, v) - } - // We must propagate upstream termination to Envoy. This ensures that we resume the full XDS sequence on new connection - return p.HandleUpstream(ctx, con, xds) -} - -func (p *XdsProxy) buildUpstreamConn(ctx context.Context) (*grpc.ClientConn, error) { - p.optsMutex.RLock() - opts := make([]grpc.DialOption, 0, len(p.istiodDialOptions)) - opts = append(opts, p.istiodDialOptions...) - p.optsMutex.RUnlock() - - return grpc.DialContext(ctx, p.istiodAddress, opts...) -} - -func (p *XdsProxy) HandleUpstream(ctx context.Context, con *ProxyConnection, xds discovery.AggregatedDiscoveryServiceClient) error { - upstream, err := xds.StreamAggregatedResources(ctx, - grpc.MaxCallRecvMsgSize(defaultClientMaxReceiveMessageSize)) - if err != nil { - // Envoy logs errors again, so no need to log beyond debug level - proxyLog.Debugf("failed to create upstream grpc client: %v", err) - // Increase metric when xds connection error, for example: forgot to restart ingressgateway or sidecar after changing root CA. - metrics.IstiodConnectionErrors.Increment() - return err - } - proxyLog.Infof("connected to upstream XDS server: %s", p.istiodAddress) - defer proxyLog.Debugf("disconnected from XDS server: %s", p.istiodAddress) - - con.upstream = upstream - - // Handle upstream xds recv - go func() { - for { - // from istiod - resp, err := con.upstream.Recv() - if err != nil { - select { - case con.upstreamError <- err: - case <-con.stopChan: - } - return - } - select { - case con.responsesChan <- resp: - case <-con.stopChan: - } - } - }() - - go p.handleUpstreamRequest(con) - go p.handleUpstreamResponse(con) - - for { - select { - case err := <-con.upstreamError: - // error from upstream Istiod. - if istiogrpc.IsExpectedGRPCError(err) { - proxyLog.Debugf("upstream [%d] terminated with status %v", con.conID, err) - metrics.IstiodConnectionCancellations.Increment() - } else { - proxyLog.Warnf("upstream [%d] terminated with unexpected error %v", con.conID, err) - metrics.IstiodConnectionErrors.Increment() - } - return err - case err := <-con.downstreamError: - // error from downstream Envoy. - if istiogrpc.IsExpectedGRPCError(err) { - proxyLog.Debugf("downstream [%d] terminated with status %v", con.conID, err) - metrics.EnvoyConnectionCancellations.Increment() - } else { - proxyLog.Warnf("downstream [%d] terminated with unexpected error %v", con.conID, err) - metrics.EnvoyConnectionErrors.Increment() - } - // On downstream error, we will return. This propagates the error to downstream envoy which will trigger reconnect - return err - case <-con.stopChan: - proxyLog.Debugf("stream stopped") - return nil - } - } -} - -func (p *XdsProxy) handleUpstreamRequest(con *ProxyConnection) { - initialRequestsSent := atomic.NewBool(false) - go func() { - for { - // recv xds requests from envoy - req, err := con.downstream.Recv() - if err != nil { - select { - case con.downstreamError <- err: - case <-con.stopChan: - } - return - } - - // forward to istiod - con.sendRequest(req) - if !initialRequestsSent.Load() && req.TypeUrl == v3.ListenerType { - // fire off an initial NDS request - if _, f := p.handlers[v3.NameTableType]; f { - con.sendRequest(&discovery.DiscoveryRequest{ - TypeUrl: v3.NameTableType, - }) - } - // fire off an initial PCDS request - if _, f := p.handlers[v3.ProxyConfigType]; f { - con.sendRequest(&discovery.DiscoveryRequest{ - TypeUrl: v3.ProxyConfigType, - }) - } - // set flag before sending the initial request to prevent race. - initialRequestsSent.Store(true) - // Fire of a configured initial request, if there is one - p.connectedMutex.RLock() - initialRequest := p.initialRequest - if initialRequest != nil { - con.sendRequest(initialRequest) - } - p.connectedMutex.RUnlock() - } - } - }() - - defer con.upstream.CloseSend() // nolint - for { - select { - case req := <-con.requestsChan: - if req.TypeUrl == v3.HealthInfoType && !initialRequestsSent.Load() { - // only send healthcheck probe after LDS request has been sent - continue - } - proxyLog.Debugf("request for type url %s", req.TypeUrl) - metrics.XdsProxyRequests.Increment() - if req.TypeUrl == v3.ExtensionConfigurationType { - if req.VersionInfo != "" { - p.ecdsLastAckVersion.Store(req.VersionInfo) - } - p.ecdsLastNonce.Store(req.ResponseNonce) - } - if err := sendUpstream(con.upstream, req); err != nil { - proxyLog.Errorf("upstream [%d] send error for type url %s: %v", con.conID, req.TypeUrl, err) - con.upstreamError <- err - return - } - case <-con.stopChan: - return - } - } -} - -func (p *XdsProxy) handleUpstreamResponse(con *ProxyConnection) { - forwardEnvoyCh := make(chan *discovery.DiscoveryResponse, 1) - for { - select { - case resp := <-con.responsesChan: - // TODO: separate upstream response handling from requests sending, which are both time costly - proxyLog.Debugf("response for type url %s", resp.TypeUrl) - metrics.XdsProxyResponses.Increment() - if h, f := p.handlers[resp.TypeUrl]; f { - if len(resp.Resources) == 0 { - // Empty response, nothing to do - // This assumes internal types are always singleton - break - } - err := h(resp.Resources[0]) - var errorResp *google_rpc.Status - if err != nil { - errorResp = &google_rpc.Status{ - Code: int32(codes.Internal), - Message: err.Error(), - } - } - // Send ACK/NACK - con.sendRequest(&discovery.DiscoveryRequest{ - VersionInfo: resp.VersionInfo, - TypeUrl: resp.TypeUrl, - ResponseNonce: resp.Nonce, - ErrorDetail: errorResp, - }) - continue - } - switch resp.TypeUrl { - case v3.ExtensionConfigurationType: - if features.WasmRemoteLoadConversion { - // If Wasm remote load conversion feature is enabled, rewrite and send. - go p.rewriteAndForward(con, resp, func(resp *discovery.DiscoveryResponse) { - // Forward the response using the thread of `handleUpstreamResponse` - // to prevent concurrent access to forwardToEnvoy - select { - case forwardEnvoyCh <- resp: - case <-con.stopChan: - } - }) - } else { - // Otherwise, forward ECDS resource update directly to Envoy. - forwardToEnvoy(con, resp) - } - default: - if strings.HasPrefix(resp.TypeUrl, "istio.io/debug") { - p.forwardToTap(resp) - } else { - forwardToEnvoy(con, resp) - } - } - case resp := <-forwardEnvoyCh: - forwardToEnvoy(con, resp) - case <-con.stopChan: - return - } - } -} - -func (p *XdsProxy) rewriteAndForward(con *ProxyConnection, resp *discovery.DiscoveryResponse, forward func(resp *discovery.DiscoveryResponse)) { - sendNack := wasm.MaybeConvertWasmExtensionConfig(resp.Resources, p.wasmCache) - if sendNack { - proxyLog.Debugf("sending NACK for ECDS resources %+v", resp.Resources) - con.sendRequest(&discovery.DiscoveryRequest{ - VersionInfo: p.ecdsLastAckVersion.Load(), - TypeUrl: v3.ExtensionConfigurationType, - ResponseNonce: resp.Nonce, - ErrorDetail: &google_rpc.Status{ - // TODO(bianpengyuan): make error message more informative. - Message: "failed to fetch wasm module", - }, - }) - return - } - proxyLog.Debugf("forward ECDS resources %+v", resp.Resources) - forward(resp) -} - -func (p *XdsProxy) forwardToTap(resp *discovery.DiscoveryResponse) { - select { - case p.tapResponseChannel <- resp: - default: - log.Infof("tap response %q arrived too late; discarding", resp.TypeUrl) - } -} - -func forwardToEnvoy(con *ProxyConnection, resp *discovery.DiscoveryResponse) { - if !v3.IsEnvoyType(resp.TypeUrl) { - proxyLog.Errorf("Skipping forwarding type url %s to Envoy as is not a valid Envoy type", resp.TypeUrl) - return - } - if err := sendDownstream(con.downstream, resp); err != nil { - select { - case con.downstreamError <- err: - // we cannot return partial error and hope to restart just the downstream - // as we are blindly proxying req/responses. For now, the best course of action - // is to terminate upstream connection as well and restart afresh. - proxyLog.Errorf("downstream [%d] send error: %v", con.conID, err) - default: - // Do not block on downstream error channel push, this could happen when forward - // is triggered from a separated goroutine (e.g. ECDS processing go routine) while - // downstream connection has already been teared down and no receiver is available - // for downstream error channel. - proxyLog.Debugf("downstream [%d] error channel full, but get downstream send error: %v", con.conID, err) - } - - return - } -} - -func (p *XdsProxy) close() { - close(p.stopChan) - p.wasmCache.Cleanup() - if p.httpTapServer != nil { - _ = p.httpTapServer.Close() - } - if p.downstreamGrpcServer != nil { - p.downstreamGrpcServer.Stop() - } - if p.downstreamListener != nil { - _ = p.downstreamListener.Close() - } -} - -func (p *XdsProxy) initDownstreamServer() error { - l, err := uds.NewListener(p.xdsUdsPath) - if err != nil { - return err - } - // TODO: Expose keepalive options to agent cmd line flags. - opts := p.downstreamGrpcOptions - opts = append(opts, istiogrpc.ServerOptions(istiokeepalive.DefaultOption())...) - grpcs := grpc.NewServer(opts...) - discovery.RegisterAggregatedDiscoveryServiceServer(grpcs, p) - reflection.Register(grpcs) - p.downstreamGrpcServer = grpcs - p.downstreamListener = l - return nil -} - -func (p *XdsProxy) InitIstiodDialOptions(agent *Agent) error { - opts, err := p.buildUpstreamClientDialOpts(agent) - if err != nil { - return err - } - - p.optsMutex.Lock() - p.istiodDialOptions = opts - p.optsMutex.Unlock() - return nil -} - -func (p *XdsProxy) buildUpstreamClientDialOpts(sa *Agent) ([]grpc.DialOption, error) { - tlsOpts, err := p.getTLSDialOption(sa) - if err != nil { - return nil, fmt.Errorf("failed to build TLS dial option to talk to upstream: %v", err) - } - - keepaliveOption := grpc.WithKeepaliveParams(keepalive.ClientParameters{ - Time: 30 * time.Second, - Timeout: 10 * time.Second, - }) - - initialWindowSizeOption := grpc.WithInitialWindowSize(int32(defaultInitialWindowSize)) - initialConnWindowSizeOption := grpc.WithInitialConnWindowSize(int32(defaultInitialConnWindowSize)) - msgSizeOption := grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaultClientMaxReceiveMessageSize)) - // Make sure the dial is blocking as we don't want any other operation to resume until the - // connection to upstream has been made. - dialOptions := []grpc.DialOption{ - tlsOpts, - keepaliveOption, initialWindowSizeOption, initialConnWindowSizeOption, msgSizeOption, - } - - dialOptions = append(dialOptions, grpc.WithPerRPCCredentials(caclient.NewXDSTokenProvider(sa.secOpts))) - return dialOptions, nil -} - -// Returns the TLS option to use when talking to Istiod -// If provisioned cert is set, it will return a mTLS related config -// Else it will return a one-way TLS related config with the assumption -// that the consumer code will use tokens to authenticate the upstream. -func (p *XdsProxy) getTLSDialOption(agent *Agent) (grpc.DialOption, error) { - if agent.proxyConfig.ControlPlaneAuthPolicy == meshconfig.AuthenticationPolicy_NONE { - return grpc.WithTransportCredentials(insecure.NewCredentials()), nil - } - rootCert, err := p.getRootCertificate(agent) - if err != nil { - return nil, err - } - - config := tls.Config{ - GetClientCertificate: func(*tls.CertificateRequestInfo) (*tls.Certificate, error) { - var certificate tls.Certificate - key, cert := agent.GetKeyCertsForXDS() - if key != "" && cert != "" { - // Load the certificate from disk - certificate, err = tls.LoadX509KeyPair(cert, key) - if err != nil { - return nil, err - } - } - return &certificate, nil - }, - RootCAs: rootCert, - } - - // strip the port from the address - parts := strings.Split(agent.proxyConfig.DiscoveryAddress, ":") - config.ServerName = parts[0] - - // For debugging on localhost (with port forward) - // This matches the logic for the CA; this code should eventually be shared - if strings.Contains(config.ServerName, "localhost") { - config.ServerName = "istiod.dubbo-system.svc" - } - - if p.istiodSAN != "" { - config.ServerName = p.istiodSAN - } - // TODO: if istiodSAN starts with spiffe://, use custom validation. - - config.MinVersion = tls.VersionTLS12 - transportCreds := credentials.NewTLS(&config) - return grpc.WithTransportCredentials(transportCreds), nil -} - -func (p *XdsProxy) getRootCertificate(agent *Agent) (*x509.CertPool, error) { - var certPool *x509.CertPool - var rootCert []byte - - xdsCACertPath, err := agent.FindRootCAForXDS() - if err != nil { - return nil, fmt.Errorf("failed to find root CA cert for XDS: %v", err) - } - - if xdsCACertPath != "" { - rootCert, err = os.ReadFile(xdsCACertPath) - if err != nil { - return nil, err - } - - certPool = x509.NewCertPool() - ok := certPool.AppendCertsFromPEM(rootCert) - if !ok { - return nil, fmt.Errorf("failed to create TLS dial option with root certificates") - } - } else { - certPool, err = x509.SystemCertPool() - if err != nil { - return nil, err - } - } - return certPool, nil -} - -// sendUpstream sends discovery request. -func sendUpstream(upstream xds.DiscoveryClient, request *discovery.DiscoveryRequest) error { - return istiogrpc.Send(upstream.Context(), func() error { return upstream.Send(request) }) -} - -// sendDownstream sends discovery response. -func sendDownstream(downstream adsStream, response *discovery.DiscoveryResponse) error { - return istiogrpc.Send(downstream.Context(), func() error { return downstream.Send(response) }) -} - -// tapRequest() sends "req" to Istiod, and returns a matching response, or `nil` on timeout. -// Requests are serialized -- only one may be in-flight at a time. -func (p *XdsProxy) tapRequest(req *discovery.DiscoveryRequest, timeout time.Duration) (*discovery.DiscoveryResponse, error) { - if p.connected == nil { - return nil, fmt.Errorf("proxy not connected to Istiod") - } - - // Only allow one tap request at a time - p.tapMutex.Lock() - defer p.tapMutex.Unlock() - - // Send to Istiod - p.connected.sendRequest(req) - - // Wait for expected response or timeout - for { - select { - case res := <-p.tapResponseChannel: - if res.TypeUrl == req.TypeUrl { - return res, nil - } - case <-time.After(timeout): - return nil, nil - } - } -} - -func (p *XdsProxy) makeTapHandler() func(w http.ResponseWriter, req *http.Request) { - return func(w http.ResponseWriter, req *http.Request) { - qp, err := url.ParseQuery(req.URL.RawQuery) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - fmt.Fprintf(w, "%v\n", err) - return - } - typeURL := fmt.Sprintf("istio.io%s", req.URL.Path) - dr := discovery.DiscoveryRequest{ - TypeUrl: typeURL, - } - resourceName := qp.Get("resourceName") - if resourceName != "" { - dr.ResourceNames = []string{resourceName} - } - response, err := p.tapRequest(&dr, 5*time.Second) - if err != nil { - w.WriteHeader(http.StatusServiceUnavailable) - fmt.Fprintf(w, "%v\n", err) - return - } - - if response == nil { - log.Infof("timed out waiting for Istiod to respond to %q", typeURL) - w.WriteHeader(http.StatusGatewayTimeout) - return - } - - // Try to unmarshal Istiod's response using protojson (needed for Envoy protobufs) - w.Header().Add("Content-Type", "application/json") - b, err := protomarshal.MarshalIndent(response, " ") - if err == nil { - _, err = w.Write(b) - if err != nil { - log.Infof("fail to write debug response: %v", err) - } - return - } - - // Failed as protobuf. Try as regular JSON - proxyLog.Warnf("could not marshal istiod response as pb: %v", err) - j, err := json.Marshal(response) - if err != nil { - // Couldn't unmarshal at all - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "%v\n", err) - return - } - _, err = w.Write(j) - if err != nil { - log.Infof("fail to write debug response: %v", err) - return - } - } -} - -// initDebugInterface() listens on localhost:${PORT} for path /debug/... -// forwards the paths to Istiod as xDS requests -// waits for response from Istiod, sends it as JSON -func (p *XdsProxy) initDebugInterface(port int) error { - p.tapResponseChannel = make(chan *discovery.DiscoveryResponse) - - httpMux := http.NewServeMux() - handler := p.makeTapHandler() - httpMux.HandleFunc("/debug/", handler) - httpMux.HandleFunc("/debug", handler) // For 1.10 Istiod which uses istio.io/debug - - p.httpTapServer = &http.Server{ - Addr: fmt.Sprintf("localhost:%d", port), - Handler: httpMux, - IdleTimeout: 90 * time.Second, // matches http.DefaultTransport keep-alive timeout - ReadTimeout: 30 * time.Second, - } - - // create HTTP listener - listener, err := net.Listen("tcp", p.httpTapServer.Addr) - if err != nil { - return err - } - - go func() { - log.Infof("starting Http service at %s", listener.Addr()) - if err := p.httpTapServer.Serve(listener); err != nil { - log.Errorf("error serving tap http server: %v", err) - } - }() - - return nil -} diff --git a/pkg/istio-agent/xds_proxy_delta.go b/pkg/istio-agent/xds_proxy_delta.go deleted file mode 100644 index 57d372821..000000000 --- a/pkg/istio-agent/xds_proxy_delta.go +++ /dev/null @@ -1,338 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istioagent - -import ( - "context" - "time" -) - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - google_rpc "google.golang.org/genproto/googleapis/rpc/status" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/metadata" - any "google.golang.org/protobuf/types/known/anypb" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - istiogrpc "github.com/apache/dubbo-go-pixiu/pilot/pkg/grpc" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pkg/istio-agent/metrics" - "github.com/apache/dubbo-go-pixiu/pkg/wasm" -) - -// sendDeltaRequest is a small wrapper around sending to con.requestsChan. This ensures that we do not -// block forever on -func (con *ProxyConnection) sendDeltaRequest(req *discovery.DeltaDiscoveryRequest) { - select { - case con.deltaRequestsChan <- req: - case <-con.stopChan: - } -} - -// requests from envoy -// for aditya: -// downstream -> envoy (anything "behind" xds proxy) -// upstream -> istiod (in front of xds proxy)? -func (p *XdsProxy) DeltaAggregatedResources(downstream discovery.AggregatedDiscoveryService_DeltaAggregatedResourcesServer) error { - proxyLog.Debugf("accepted delta xds connection from envoy, forwarding to upstream") - - con := &ProxyConnection{ - upstreamError: make(chan error, 2), // can be produced by recv and send - downstreamError: make(chan error, 2), // can be produced by recv and send - deltaRequestsChan: make(chan *discovery.DeltaDiscoveryRequest, 10), - deltaResponsesChan: make(chan *discovery.DeltaDiscoveryResponse, 10), - stopChan: make(chan struct{}), - downstreamDeltas: downstream, - } - p.RegisterStream(con) - defer p.UnregisterStream(con) - - // Handle downstream xds - initialRequestsSent := false - go func() { - // Send initial request - p.connectedMutex.RLock() - initialRequest := p.initialDeltaRequest - p.connectedMutex.RUnlock() - - for { - // From Envoy - req, err := downstream.Recv() - if err != nil { - select { - case con.downstreamError <- err: - case <-con.stopChan: - } - return - } - // forward to istiod - con.sendDeltaRequest(req) - if !initialRequestsSent && req.TypeUrl == v3.ListenerType { - // fire off an initial NDS request - if _, f := p.handlers[v3.NameTableType]; f { - con.sendDeltaRequest(&discovery.DeltaDiscoveryRequest{ - TypeUrl: v3.NameTableType, - }) - } - // fire off an initial PCDS request - if _, f := p.handlers[v3.ProxyConfigType]; f { - con.sendDeltaRequest(&discovery.DeltaDiscoveryRequest{ - TypeUrl: v3.ProxyConfigType, - }) - } - // Fire of a configured initial request, if there is one - if initialRequest != nil { - con.sendDeltaRequest(initialRequest) - } - initialRequestsSent = true - } - } - }() - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - defer cancel() - upstreamConn, err := p.buildUpstreamConn(ctx) - if err != nil { - proxyLog.Errorf("failed to connect to upstream %s: %v", p.istiodAddress, err) - metrics.IstiodConnectionFailures.Increment() - return err - } - defer upstreamConn.Close() - - xds := discovery.NewAggregatedDiscoveryServiceClient(upstreamConn) - ctx = metadata.AppendToOutgoingContext(context.Background(), "ClusterID", p.clusterID) - for k, v := range p.xdsHeaders { - ctx = metadata.AppendToOutgoingContext(ctx, k, v) - } - // We must propagate upstream termination to Envoy. This ensures that we resume the full XDS sequence on new connection - return p.HandleDeltaUpstream(ctx, con, xds) -} - -func (p *XdsProxy) HandleDeltaUpstream(ctx context.Context, con *ProxyConnection, xds discovery.AggregatedDiscoveryServiceClient) error { - deltaUpstream, err := xds.DeltaAggregatedResources(ctx, - grpc.MaxCallRecvMsgSize(defaultClientMaxReceiveMessageSize)) - if err != nil { - // Envoy logs errors again, so no need to log beyond debug level - proxyLog.Debugf("failed to create delta upstream grpc client: %v", err) - // Increase metric when xds connection error, for example: forgot to restart ingressgateway or sidecar after changing root CA. - metrics.IstiodConnectionErrors.Increment() - return err - } - proxyLog.Infof("connected to delta upstream XDS server: %s", p.istiodAddress) - defer proxyLog.Debugf("disconnected from delta XDS server: %s", p.istiodAddress) - - con.upstreamDeltas = deltaUpstream - - // handle responses from istiod - go func() { - for { - resp, err := deltaUpstream.Recv() - if err != nil { - select { - case con.upstreamError <- err: - case <-con.stopChan: - } - return - } - select { - case con.deltaResponsesChan <- resp: - case <-con.stopChan: - } - } - }() - - go p.handleUpstreamDeltaRequest(con) - go p.handleUpstreamDeltaResponse(con) - - // todo wasm load conversion - for { - select { - case err := <-con.upstreamError: - // error from upstream Istiod. - if istiogrpc.IsExpectedGRPCError(err) { - proxyLog.Debugf("upstream terminated with status %v", err) - metrics.IstiodConnectionCancellations.Increment() - } else { - proxyLog.Warnf("upstream terminated with unexpected error %v", err) - metrics.IstiodConnectionErrors.Increment() - } - return err - case err := <-con.downstreamError: - // error from downstream Envoy. - if istiogrpc.IsExpectedGRPCError(err) { - proxyLog.Debugf("downstream terminated with status %v", err) - metrics.EnvoyConnectionCancellations.Increment() - } else { - proxyLog.Warnf("downstream terminated with unexpected error %v", err) - metrics.EnvoyConnectionErrors.Increment() - } - // On downstream error, we will return. This propagates the error to downstream envoy which will trigger reconnect - return err - case <-con.stopChan: - proxyLog.Debugf("stream stopped") - return nil - } - } -} - -func (p *XdsProxy) handleUpstreamDeltaRequest(con *ProxyConnection) { - defer func() { - _ = con.upstreamDeltas.CloseSend() - }() - for { - select { - case req := <-con.deltaRequestsChan: - proxyLog.Debugf("delta request for type url %s", req.TypeUrl) - metrics.XdsProxyRequests.Increment() - if req.TypeUrl == v3.ExtensionConfigurationType { - p.ecdsLastNonce.Store(req.ResponseNonce) - } - if err := sendUpstreamDelta(con.upstreamDeltas, req); err != nil { - proxyLog.Errorf("upstream send error for type url %s: %v", req.TypeUrl, err) - con.upstreamError <- err - return - } - case <-con.stopChan: - return - } - } -} - -func (p *XdsProxy) handleUpstreamDeltaResponse(con *ProxyConnection) { - forwardEnvoyCh := make(chan *discovery.DeltaDiscoveryResponse, 1) - for { - select { - case resp := <-con.deltaResponsesChan: - // TODO: separate upstream response handling from requests sending, which are both time costly - proxyLog.Debugf("response for type url %s", resp.TypeUrl) - metrics.XdsProxyResponses.Increment() - if h, f := p.handlers[resp.TypeUrl]; f { - if len(resp.Resources) == 0 { - // Empty response, nothing to do - // This assumes internal types are always singleton - break - } - err := h(resp.Resources[0].Resource) - var errorResp *google_rpc.Status - if err != nil { - errorResp = &google_rpc.Status{ - Code: int32(codes.Internal), - Message: err.Error(), - } - } - // Send ACK/NACK - con.sendDeltaRequest(&discovery.DeltaDiscoveryRequest{ - TypeUrl: resp.TypeUrl, - ResponseNonce: resp.Nonce, - ErrorDetail: errorResp, - }) - continue - } - switch resp.TypeUrl { - case v3.ExtensionConfigurationType: - if features.WasmRemoteLoadConversion { - // If Wasm remote load conversion feature is enabled, rewrite and send. - go p.deltaRewriteAndForward(con, resp, func(resp *discovery.DeltaDiscoveryResponse) { - // Forward the response using the thread of `handleUpstreamResponse` - // to prevent concurrent access to forwardToEnvoy - select { - case forwardEnvoyCh <- resp: - case <-con.stopChan: - } - }) - } else { - // Otherwise, forward ECDS resource update directly to Envoy. - forwardDeltaToEnvoy(con, resp) - } - default: - forwardDeltaToEnvoy(con, resp) - } - case resp := <-forwardEnvoyCh: - forwardDeltaToEnvoy(con, resp) - case <-con.stopChan: - return - } - } -} - -func (p *XdsProxy) deltaRewriteAndForward(con *ProxyConnection, resp *discovery.DeltaDiscoveryResponse, forward func(resp *discovery.DeltaDiscoveryResponse)) { - resources := make([]*any.Any, 0, len(resp.Resources)) - for i := range resp.Resources { - resources = append(resources, resp.Resources[i].Resource) - } - sendNack := wasm.MaybeConvertWasmExtensionConfig(resources, p.wasmCache) - if sendNack { - proxyLog.Debugf("sending NACK for ECDS resources %+v", resp.Resources) - con.sendDeltaRequest(&discovery.DeltaDiscoveryRequest{ - TypeUrl: v3.ExtensionConfigurationType, - ResponseNonce: resp.Nonce, - ErrorDetail: &google_rpc.Status{ - // TODO(bianpengyuan): make error message more informative. - Message: "failed to fetch wasm module", - }, - }) - return - } - proxyLog.Debugf("forward ECDS resources %+v", resp.Resources) - forward(resp) -} - -func forwardDeltaToEnvoy(con *ProxyConnection, resp *discovery.DeltaDiscoveryResponse) { - if err := sendDownstreamDelta(con.downstreamDeltas, resp); err != nil { - select { - case con.downstreamError <- err: - proxyLog.Errorf("downstream send error: %v", err) - default: - proxyLog.Debugf("downstream error channel full, but get downstream send error: %v", err) - } - - return - } -} - -func sendUpstreamDelta(deltaUpstream discovery.AggregatedDiscoveryService_DeltaAggregatedResourcesClient, - req *discovery.DeltaDiscoveryRequest) error { - return istiogrpc.Send(deltaUpstream.Context(), func() error { return deltaUpstream.Send(req) }) -} - -func sendDownstreamDelta(deltaDownstream discovery.AggregatedDiscoveryService_DeltaAggregatedResourcesServer, - res *discovery.DeltaDiscoveryResponse) error { - return istiogrpc.Send(deltaDownstream.Context(), func() error { return deltaDownstream.Send(res) }) -} - -func (p *XdsProxy) PersistDeltaRequest(req *discovery.DeltaDiscoveryRequest) { - var ch chan *discovery.DeltaDiscoveryRequest - var stop chan struct{} - - p.connectedMutex.Lock() - if p.connected != nil { - ch = p.connected.deltaRequestsChan - stop = p.connected.stopChan - } - p.initialDeltaRequest = req - p.connectedMutex.Unlock() - - // Immediately send if we are currently connect - if ch != nil { - select { - case ch <- req: - case <-stop: - } - } -} diff --git a/pkg/istio-agent/xds_proxy_delta_test.go b/pkg/istio-agent/xds_proxy_delta_test.go deleted file mode 100644 index 9157923f8..000000000 --- a/pkg/istio-agent/xds_proxy_delta_test.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istioagent - -import ( - "testing" - "time" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "google.golang.org/grpc" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" -) - -// TestXdsLeak is a regression test for https://github.com/istio/istio/issues/34097 -func TestDeltaXdsLeak(t *testing.T) { - proxy := setupXdsProxyWithDownstreamOptions(t, []grpc.ServerOption{grpc.StreamInterceptor(xdstest.SlowServerInterceptor(time.Second, time.Second))}) - f := xdstest.NewMockServer(t) - setDialOptions(proxy, f.Listener) - proxy.istiodDialOptions = append(proxy.istiodDialOptions, grpc.WithStreamInterceptor(xdstest.SlowClientInterceptor(0, time.Second*10))) - conn := setupDownstreamConnection(t, proxy) - downstream := deltaStream(t, conn) - sendDeltaDownstreamWithoutResponse(t, downstream) - for i := 0; i < 15; i++ { - // Send a bunch of responses from Istiod. These should not block, even though there are more sends than responseChan can hold - f.SendDeltaResponse(&discovery.DeltaDiscoveryResponse{TypeUrl: v3.ClusterType}) - } - // Exit test, closing the connections. We should not have any goroutine leaks (checked by leak.CheckMain) -} - -// sendDownstreamWithoutResponse sends a response without waiting for a response -func sendDeltaDownstreamWithoutResponse(t *testing.T, downstream discovery.AggregatedDiscoveryService_DeltaAggregatedResourcesClient) { - t.Helper() - err := downstream.Send(&discovery.DeltaDiscoveryRequest{ - TypeUrl: v3.ClusterType, - Node: &core.Node{ - Id: "sidecar~0.0.0.0~debug~cluster.local", - }, - }) - if err != nil { - t.Fatal(err) - } -} - -// Validates basic xds proxy flow by proxying one CDS requests end to end. -func TestDeltaXdsProxyBasicFlow(t *testing.T) { - proxy := setupXdsProxy(t) - f := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - setDialOptions(proxy, f.BufListener) - conn := setupDownstreamConnection(t, proxy) - downstream := deltaStream(t, conn) - sendDeltaDownstreamWithNode(t, downstream, model.NodeMetadata{ - Namespace: "default", - InstanceIPs: []string{"1.1.1.1"}, - }) -} - -func deltaStream(t *testing.T, conn *grpc.ClientConn) discovery.AggregatedDiscoveryService_DeltaAggregatedResourcesClient { - t.Helper() - adsClient := discovery.NewAggregatedDiscoveryServiceClient(conn) - downstream, err := adsClient.DeltaAggregatedResources(ctx) - if err != nil { - t.Fatal(err) - } - return downstream -} - -func sendDeltaDownstreamWithNode(t *testing.T, downstream discovery.AggregatedDiscoveryService_DeltaAggregatedResourcesClient, meta model.NodeMetadata) { - t.Helper() - node := &core.Node{ - Id: "sidecar~1.1.1.1~debug~cluster.local", - Metadata: meta.ToStruct(), - } - err := downstream.Send(&discovery.DeltaDiscoveryRequest{TypeUrl: v3.ClusterType, Node: node}) - if err != nil { - t.Fatal(err) - } - res, err := downstream.Recv() - if err != nil { - t.Fatal(err) - } - if res == nil || res.TypeUrl != v3.ClusterType { - t.Fatalf("Expected to get cluster response but got %v", res) - } - err = downstream.Send(&discovery.DeltaDiscoveryRequest{TypeUrl: v3.ListenerType, Node: node}) - if err != nil { - t.Fatal(err) - } - res, err = downstream.Recv() - if err != nil { - t.Fatal(err) - } - if res == nil || res.TypeUrl != v3.ListenerType { - t.Fatalf("Expected to get listener response but got %v", res) - } -} diff --git a/pkg/istio-agent/xds_proxy_test.go b/pkg/istio-agent/xds_proxy_test.go deleted file mode 100644 index c642b8eab..000000000 --- a/pkg/istio-agent/xds_proxy_test.go +++ /dev/null @@ -1,638 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istioagent - -import ( - "context" - "errors" - "fmt" - "net" - "os" - "path" - "path/filepath" - "testing" - "time" -) - -import ( - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - wasm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/wasm/v3" - wasmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/wasm/v3" - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - google_rpc "google.golang.org/genproto/googleapis/rpc/status" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/test/bufconn" - "google.golang.org/protobuf/proto" - extensions "istio.io/api/extensions/v1alpha1" - networking "istio.io/api/networking/v1alpha3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model/status" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - v3 "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds/v3" - "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/envoy" - "github.com/apache/dubbo-go-pixiu/pkg/security" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -// TestXdsLeak is a regression test for https://github.com/istio/istio/issues/34097 -func TestXdsLeak(t *testing.T) { - proxy := setupXdsProxyWithDownstreamOptions(t, []grpc.ServerOption{grpc.StreamInterceptor(xdstest.SlowServerInterceptor(time.Second, time.Second))}) - f := xdstest.NewMockServer(t) - setDialOptions(proxy, f.Listener) - proxy.istiodDialOptions = append(proxy.istiodDialOptions, grpc.WithStreamInterceptor(xdstest.SlowClientInterceptor(0, time.Second*10))) - conn := setupDownstreamConnection(t, proxy) - downstream := stream(t, conn) - sendDownstreamWithoutResponse(t, downstream) - for i := 0; i < 15; i++ { - // Send a bunch of responses from Istiod. These should not block, even though there are more sends than responseChan can hold - f.SendResponse(&discovery.DiscoveryResponse{TypeUrl: v3.ClusterType}) - } - // Exit test, closing the connections. We should not have any goroutine leaks (checked by leak.CheckMain) -} - -// sendDownstreamWithoutResponse sends a response without waiting for a response -func sendDownstreamWithoutResponse(t *testing.T, downstream discovery.AggregatedDiscoveryService_StreamAggregatedResourcesClient) { - t.Helper() - err := downstream.Send(&discovery.DiscoveryRequest{ - TypeUrl: v3.ClusterType, - Node: &core.Node{ - Id: "sidecar~0.0.0.0~debug~cluster.local", - }, - }) - if err != nil { - t.Fatal(err) - } -} - -// Validates basic xds proxy flow by proxying one CDS requests end to end. -func TestXdsProxyBasicFlow(t *testing.T) { - proxy := setupXdsProxy(t) - f := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - setDialOptions(proxy, f.BufListener) - conn := setupDownstreamConnection(t, proxy) - downstream := stream(t, conn) - sendDownstreamWithNode(t, downstream, model.NodeMetadata{ - Namespace: "default", - InstanceIPs: []string{"1.1.1.1"}, - }) -} - -// Validates the proxy health checking updates -func TestXdsProxyHealthCheck(t *testing.T) { - healthy := &discovery.DiscoveryRequest{TypeUrl: v3.HealthInfoType} - unhealthy := &discovery.DiscoveryRequest{ - TypeUrl: v3.HealthInfoType, - ErrorDetail: &google_rpc.Status{ - Code: 500, - Message: "unhealthy", - }, - } - node := model.NodeMetadata{ - AutoRegisterGroup: "group", - Namespace: "default", - InstanceIPs: []string{"1.1.1.1"}, - } - proxy := setupXdsProxy(t) - - f := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - if _, err := f.Store().Create(config.Config{ - Meta: config.Meta{ - Name: "group", - Namespace: "default", - GroupVersionKind: gvk.WorkloadGroup, - }, - Spec: &networking.WorkloadGroup{ - Template: &networking.WorkloadEntry{}, - }, - }); err != nil { - t.Fatal(err) - } - setDialOptions(proxy, f.BufListener) - conn := setupDownstreamConnection(t, proxy) - downstream := stream(t, conn) - - // Setup test helpers - waitDisconnect := func() { - retry.UntilSuccessOrFail(t, func() error { - proxy.connectedMutex.Lock() - defer proxy.connectedMutex.Unlock() - if proxy.connected != nil { - return fmt.Errorf("still connected") - } - return nil - }, retry.Timeout(time.Second), retry.Delay(time.Millisecond)) - } - expectCondition := func(expected string) { - t.Helper() - retry.UntilSuccessOrFail(t, func() error { - cfg := f.Store().Get(gvk.WorkloadEntry, "group-1.1.1.1", "default") - if cfg == nil { - return fmt.Errorf("config not found") - } - con := status.GetConditionFromSpec(*cfg, status.ConditionHealthy) - if con == nil { - if expected == "" { - return nil - } - return fmt.Errorf("found no conditions, expected %v", expected) - } - if con.Status != expected { - return fmt.Errorf("expected status %q got %q", expected, con.Status) - } - return nil - }, retry.Timeout(time.Second*2)) - } - - // send cds before healthcheck, to make wle registered - coreNode := &core.Node{ - Id: "sidecar~1.1.1.1~debug~cluster.local", - Metadata: node.ToStruct(), - } - err := downstream.Send(&discovery.DiscoveryRequest{TypeUrl: v3.ClusterType, Node: coreNode}) - if err != nil { - t.Fatal(err) - } - _, err = downstream.Recv() - if err != nil { - t.Fatal(err) - } - - // healthcheck before lds will be not sent - proxy.PersistRequest(healthy) - expectCondition("") - - // simulate envoy send xds requests - sendDownstreamWithNode(t, downstream, node) - - // after lds sent, the caching healthcheck will be resent - expectCondition(status.StatusTrue) - - // Flip status back and forth, ensure we update - proxy.PersistRequest(healthy) - expectCondition(status.StatusTrue) - proxy.PersistRequest(unhealthy) - expectCondition(status.StatusFalse) - proxy.PersistRequest(healthy) - expectCondition(status.StatusTrue) - - // Completely disconnect - conn.Close() - downstream.CloseSend() - waitDisconnect() - conn = setupDownstreamConnection(t, proxy) - downstream = stream(t, conn) - sendDownstreamWithNode(t, downstream, node) - - // Old status should remain - expectCondition(status.StatusTrue) - // And still update when we send new requests - proxy.PersistRequest(unhealthy) - expectCondition(status.StatusFalse) - - // Send a new update while we are disconnected - conn.Close() - downstream.CloseSend() - waitDisconnect() - proxy.PersistRequest(healthy) - conn = setupDownstreamConnection(t, proxy) - downstream = stream(t, conn) - sendDownstreamWithNode(t, downstream, node) - - // When we reconnect, our status update should still persist - expectCondition(status.StatusTrue) - - // Confirm more updates are honored - proxy.PersistRequest(healthy) - expectCondition(status.StatusTrue) - proxy.PersistRequest(unhealthy) - expectCondition(status.StatusFalse) - - // Disconnect and remove workload entry. This could happen if there is an outage and istiod cleans up - // the workload entry. - conn.Close() - downstream.CloseSend() - waitDisconnect() - f.Store().Delete(gvk.WorkloadEntry, "group-1.1.1.1", "default", nil) - proxy.PersistRequest(healthy) - conn = setupDownstreamConnection(t, proxy) - downstream = stream(t, conn) - sendDownstreamWithNode(t, downstream, node) - - // When we reconnect, our status update should be re-applied - expectCondition(status.StatusTrue) - - // Confirm more updates are honored - proxy.PersistRequest(unhealthy) - expectCondition(status.StatusFalse) - proxy.PersistRequest(healthy) - expectCondition(status.StatusTrue) -} - -func setupXdsProxy(t *testing.T) *XdsProxy { - return setupXdsProxyWithDownstreamOptions(t, nil) -} - -func setupXdsProxyWithDownstreamOptions(t *testing.T, opts []grpc.ServerOption) *XdsProxy { - secOpts := &security.Options{ - FileMountedCerts: true, - } - proxyConfig := mesh.DefaultProxyConfig() - proxyConfig.DiscoveryAddress = "buffcon" - - // While the actual test runs on plain text, these are setup to build the default dial options - // with out these it looks for default cert location and fails. - proxyConfig.ProxyMetadata = map[string]string{ - MetadataClientCertChain: path.Join(env.IstioSrc, "tests/testdata/certs/pilot/cert-chain.pem"), - MetadataClientCertKey: path.Join(env.IstioSrc, "tests/testdata/certs/pilot/key.pem"), - MetadataClientRootCert: path.Join(env.IstioSrc, "tests/testdata/certs/pilot/root-cert.pem"), - } - dir := t.TempDir() - ia := NewAgent(proxyConfig, &AgentOptions{ - XdsUdsPath: filepath.Join(dir, "XDS"), - DownstreamGrpcOptions: opts, - }, secOpts, envoy.ProxyConfig{TestOnly: true}) - t.Cleanup(func() { - ia.Close() - }) - proxy, err := initXdsProxy(ia) - if err != nil { - t.Fatalf("Failed to initialize xds proxy %v", err) - } - ia.xdsProxy = proxy - - return proxy -} - -func setDialOptions(p *XdsProxy, l *bufconn.Listener) { - // Override istiodDialOptions so that the test can connect with plain text and with buffcon listener. - p.istiodDialOptions = []grpc.DialOption{ - grpc.WithBlock(), - grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { - return l.Dial() - }), - } -} - -var ctx = metadata.AppendToOutgoingContext(context.Background(), "ClusterID", "Kubernetes") - -// Validates basic xds proxy flow by proxying one CDS requests end to end. -func TestXdsProxyReconnects(t *testing.T) { - waitDisconnect := func(proxy *XdsProxy) { - retry.UntilSuccessOrFail(t, func() error { - proxy.connectedMutex.Lock() - defer proxy.connectedMutex.Unlock() - if proxy.connected != nil { - return fmt.Errorf("still connected") - } - return nil - }, retry.Timeout(time.Second), retry.Delay(time.Millisecond)) - } - t.Run("Envoy close and open stream", func(t *testing.T) { - proxy := setupXdsProxy(t) - f := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - setDialOptions(proxy, f.BufListener) - - conn := setupDownstreamConnection(t, proxy) - downstream := stream(t, conn) - sendDownstreamWithNode(t, downstream, model.NodeMetadata{ - Namespace: "default", - InstanceIPs: []string{"1.1.1.1"}, - }) - - downstream.CloseSend() - waitDisconnect(proxy) - - downstream = stream(t, conn) - sendDownstreamWithNode(t, downstream, model.NodeMetadata{ - Namespace: "default", - InstanceIPs: []string{"1.1.1.1"}, - }) - }) - t.Run("Envoy opens multiple stream", func(t *testing.T) { - proxy := setupXdsProxy(t) - f := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - setDialOptions(proxy, f.BufListener) - - conn := setupDownstreamConnection(t, proxy) - downstream := stream(t, conn) - sendDownstreamWithNode(t, downstream, model.NodeMetadata{ - Namespace: "default", - InstanceIPs: []string{"1.1.1.1"}, - }) - downstream = stream(t, conn) - sendDownstreamWithNode(t, downstream, model.NodeMetadata{ - Namespace: "default", - InstanceIPs: []string{"1.1.1.1"}, - }) - }) - t.Run("Envoy closes connection", func(t *testing.T) { - proxy := setupXdsProxy(t) - f := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - setDialOptions(proxy, f.BufListener) - - conn := setupDownstreamConnection(t, proxy) - downstream := stream(t, conn) - sendDownstreamWithNode(t, downstream, model.NodeMetadata{ - Namespace: "default", - InstanceIPs: []string{"1.1.1.1"}, - }) - conn.Close() - - c := setupDownstreamConnection(t, proxy) - downstream = stream(t, c) - sendDownstreamWithNode(t, downstream, model.NodeMetadata{ - Namespace: "default", - InstanceIPs: []string{"1.1.1.1"}, - }) - }) - t.Run("Envoy sends concurrent requests", func(t *testing.T) { - // Envoy doesn't really do this, in reality it should only have a single connection. However, - // this ensures we are robust against cases where envoy rapidly disconnects and reconnects - proxy := setupXdsProxy(t) - f := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - setDialOptions(proxy, f.BufListener) - - conn := setupDownstreamConnection(t, proxy) - downstream := stream(t, conn) - sendDownstreamWithNode(t, downstream, model.NodeMetadata{ - Namespace: "default", - InstanceIPs: []string{"1.1.1.1"}, - }) - - c := setupDownstreamConnection(t, proxy) - downstream = stream(t, c) - sendDownstreamWithNode(t, downstream, model.NodeMetadata{ - Namespace: "default", - InstanceIPs: []string{"1.1.1.1"}, - }) - conn.Close() - - sendDownstreamWithNode(t, downstream, model.NodeMetadata{ - Namespace: "default", - InstanceIPs: []string{"1.1.1.1"}, - }) - }) - t.Run("Istiod closes connection", func(t *testing.T) { - proxy := setupXdsProxy(t) - f := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{}) - - // Here we set up a real listener (instead of in memory) since we need to close and re-open - // a new listener on the same port, which we cannot do with the in memory listener. - listener, err := net.Listen("tcp", ":0") - if err != nil { - t.Fatal(err) - } - proxy.istiodAddress = listener.Addr().String() - proxy.istiodDialOptions = []grpc.DialOption{grpc.WithBlock(), grpc.WithTransportCredentials(insecure.NewCredentials())} - - // Setup gRPC server - grpcServer := grpc.NewServer() - t.Cleanup(grpcServer.Stop) - f.Discovery.Register(grpcServer) - go grpcServer.Serve(listener) - - // Send initial request - conn := setupDownstreamConnection(t, proxy) - downstream := stream(t, conn) - sendDownstreamWithNode(t, downstream, model.NodeMetadata{ - Namespace: "default", - InstanceIPs: []string{"1.1.1.1"}, - }) - - // Stop server, setup a new one. This simulates an Istiod pod being torn down - grpcServer.Stop() - listener, err = net.Listen("tcp", listener.Addr().String()) - if err != nil { - t.Fatal(err) - } - waitDisconnect(proxy) - - grpcServer = grpc.NewServer() - t.Cleanup(grpcServer.Stop) - f.Discovery.Register(grpcServer) - go grpcServer.Serve(listener) - - // Send downstream again - downstream = stream(t, conn) - sendDownstreamWithNode(t, downstream, model.NodeMetadata{ - Namespace: "default", - InstanceIPs: []string{"1.1.1.1"}, - }) - }) -} - -type fakeAckCache struct{} - -func (f *fakeAckCache) Get(string, string, string, string, time.Duration, []byte, extensions.PullPolicy) (string, error) { - return "test", nil -} -func (f *fakeAckCache) Cleanup() {} - -type fakeNackCache struct{} - -func (f *fakeNackCache) Get(string, string, string, string, time.Duration, []byte, extensions.PullPolicy) (string, error) { - return "", errors.New("errror") -} -func (f *fakeNackCache) Cleanup() {} - -func TestECDSWasmConversion(t *testing.T) { - node := model.NodeMetadata{ - Namespace: "default", - InstanceIPs: []string{"1.1.1.1"}, - ClusterID: "Kubernetes", - } - proxy := setupXdsProxy(t) - - // Reset wasm cache to a fake ACK cache. - proxy.wasmCache.Cleanup() - proxy.wasmCache = &fakeAckCache{} - - // Initialize discovery server with an ECDS resource. - ef, err := os.ReadFile(path.Join(env.IstioSrc, "pilot/pkg/xds/testdata/ecds.yaml")) - if err != nil { - t.Fatal(err) - } - f := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ - ConfigString: string(ef), - }) - setDialOptions(proxy, f.BufListener) - conn := setupDownstreamConnection(t, proxy) - downstream := stream(t, conn) - - // Send first request, wasm module async fetch should work fine and - // downstream should receive a local file based wasm extension config. - err = downstream.Send(&discovery.DiscoveryRequest{ - TypeUrl: v3.ExtensionConfigurationType, - ResourceNames: []string{"extension-config"}, - Node: &core.Node{ - Id: "sidecar~1.1.1.1~debug~cluster.local", - Metadata: node.ToStruct(), - }, - }) - if err != nil { - t.Fatal(err) - } - - // Verify that the request received has been rewritten to local file. - gotResp, err := downstream.Recv() - if err != nil { - t.Fatal(err) - } - if len(gotResp.Resources) != 1 { - t.Errorf("xds proxy ecds wasm conversion number of received resource got %v want 1", len(gotResp.Resources)) - } - gotEcdsConfig := &core.TypedExtensionConfig{} - if err := gotResp.Resources[0].UnmarshalTo(gotEcdsConfig); err != nil { - t.Fatalf("wasm config conversion output %v failed to unmarshal", gotResp.Resources[0]) - } - wasm := &wasm.Wasm{ - Config: &wasmv3.PluginConfig{ - Vm: &wasmv3.PluginConfig_VmConfig{ - VmConfig: &wasmv3.VmConfig{ - Code: &core.AsyncDataSource{Specifier: &core.AsyncDataSource_Local{ - Local: &core.DataSource{ - Specifier: &core.DataSource_Filename{ - Filename: "test", - }, - }, - }}, - }, - }, - }, - } - wantEcdsConfig := &core.TypedExtensionConfig{ - Name: "extension-config", - TypedConfig: util.MessageToAny(wasm), - } - - if !proto.Equal(gotEcdsConfig, wantEcdsConfig) { - t.Errorf("xds proxy wasm config conversion got %v want %v", gotEcdsConfig, wantEcdsConfig) - } - v1 := proxy.ecdsLastAckVersion - n1 := proxy.ecdsLastNonce - - // reset wasm cache to a NACK cache, and recreate xds server as well to simulate a version bump - proxy.wasmCache = &fakeNackCache{} - f = xds.NewFakeDiscoveryServer(t, xds.FakeOptions{ - ConfigString: string(ef), - }) - setDialOptions(proxy, f.BufListener) - conn = setupDownstreamConnection(t, proxy) - downstream = stream(t, conn) - - // send request again, this time a nack should be returned from the xds proxy due to NACK wasm cache. - err = downstream.Send(&discovery.DiscoveryRequest{ - VersionInfo: gotResp.VersionInfo, - TypeUrl: v3.ExtensionConfigurationType, - ResourceNames: []string{"extension-config"}, - Node: &core.Node{ - Id: "sidecar~1.1.1.1~debug~cluster.local", - Metadata: node.ToStruct(), - }, - }) - if err != nil { - t.Fatal(err) - } - - // Wait until nonce was updated, which represents an ACK/NACK has been received. - retry.UntilSuccessOrFail(t, func() error { - if proxy.ecdsLastNonce.Load() == n1.Load() { - return errors.New("last process nonce has not been updated. no ecds ack/nack is received yet") - } - return nil - }, retry.Timeout(time.Second), retry.Delay(time.Millisecond)) - - // Verify that the last ack version remains the same, which represents the latest DiscoveryRequest is a NACK. - v2 := proxy.ecdsLastAckVersion - if v1.Load() == v2.Load() { - t.Errorf("last ack ecds request was updated. expect it to remain the same which represents a nack for ecds update") - } -} - -func stream(t *testing.T, conn *grpc.ClientConn) discovery.AggregatedDiscoveryService_StreamAggregatedResourcesClient { - t.Helper() - adsClient := discovery.NewAggregatedDiscoveryServiceClient(conn) - downstream, err := adsClient.StreamAggregatedResources(ctx) - if err != nil { - t.Fatal(err) - } - return downstream -} - -func sendDownstreamWithNode(t *testing.T, downstream discovery.AggregatedDiscoveryService_StreamAggregatedResourcesClient, meta model.NodeMetadata) { - t.Helper() - node := &core.Node{ - Id: "sidecar~1.1.1.1~debug~cluster.local", - Metadata: meta.ToStruct(), - } - err := downstream.Send(&discovery.DiscoveryRequest{TypeUrl: v3.ClusterType, Node: node}) - if err != nil { - t.Fatal(err) - } - res, err := downstream.Recv() - if err != nil { - t.Fatal(err) - } - if res == nil || res.TypeUrl != v3.ClusterType { - t.Fatalf("Expected to get cluster response but got %v", res) - } - err = downstream.Send(&discovery.DiscoveryRequest{TypeUrl: v3.ListenerType, Node: node}) - if err != nil { - t.Fatal(err) - } - res, err = downstream.Recv() - if err != nil { - t.Fatal(err) - } - if res == nil || res.TypeUrl != v3.ListenerType { - t.Fatalf("Expected to get listener response but got %v", res) - } -} - -func setupDownstreamConnectionUDS(t test.Failer, path string) *grpc.ClientConn { - var opts []grpc.DialOption - - opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) { - var d net.Dialer - return d.DialContext(ctx, "unix", path) - })) - - conn, err := grpc.Dial(path, opts...) - if err != nil { - t.Fatal(err) - } - - t.Cleanup(func() { - conn.Close() - }) - return conn -} - -func setupDownstreamConnection(t *testing.T, proxy *XdsProxy) *grpc.ClientConn { - return setupDownstreamConnectionUDS(t, proxy.xdsUdsPath) -} diff --git a/pkg/jwt/jwt.go b/pkg/jwt/jwt.go deleted file mode 100644 index 7a7c1e8ce..000000000 --- a/pkg/jwt/jwt.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package jwt - -const ( - PolicyThirdParty = "third-party-jwt" - PolicyFirstParty = "first-party-jwt" -) diff --git a/pkg/keepalive/options.go b/pkg/keepalive/options.go deleted file mode 100644 index 5f724ed56..000000000 --- a/pkg/keepalive/options.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package keepalive - -import ( - "math" - "time" -) - -import ( - "github.com/spf13/cobra" -) - -const ( - // Infinity is the maximum possible duration for keepalive values - Infinity = time.Duration(math.MaxInt64) -) - -// Options defines the set of options used for grpc keepalive. -// The Time and Timeout options are used for both client and server connections, -// whereas MaxServerConnectionAge* options are applicable on the server side only -// (as implied by the options' name...) -type Options struct { - // After a duration of this time if the server/client doesn't see any activity it pings the peer to see if the transport is still alive. - Time time.Duration - // After having pinged for keepalive check, the server waits for a duration of Timeout and if no activity is seen even after that - // the connection is closed. - Timeout time.Duration - // MaxServerConnectionAge is a duration for the maximum amount of time a - // connection may exist before it will be closed by the server sending a GoAway. - // A random jitter is added to spread out connection storms. - // See https://github.com/grpc/grpc-go/blob/bd0b3b2aa2a9c87b323ee812359b0e9cda680dad/keepalive/keepalive.go#L49 - MaxServerConnectionAge time.Duration // default value is infinity - // MaxServerConnectionAgeGrace is an additive period after MaxServerConnectionAge - // after which the connection will be forcibly closed by the server. - MaxServerConnectionAgeGrace time.Duration // default value 10s -} - -// DefaultOption returns the default keepalive options. -func DefaultOption() *Options { - return &Options{ - Time: 30 * time.Second, - Timeout: 10 * time.Second, - MaxServerConnectionAge: Infinity, - MaxServerConnectionAgeGrace: 10 * time.Second, - } -} - -// AttachCobraFlags attaches a set of Cobra flags to the given Cobra command. -// -// Cobra is the command-line processor that Istio uses. This command attaches -// the necessary set of flags to configure the grpc keepalive options. -func (o *Options) AttachCobraFlags(cmd *cobra.Command) { - cmd.PersistentFlags().DurationVar(&o.Time, "keepaliveInterval", o.Time, - "The time interval if no activity on the connection it pings the peer to see if the transport is alive") - cmd.PersistentFlags().DurationVar(&o.Timeout, "keepaliveTimeout", o.Timeout, - "After having pinged for keepalive check, the client/server waits for a duration of keepaliveTimeout "+ - "and if no activity is seen even after that the connection is closed.") - cmd.PersistentFlags().DurationVar(&o.MaxServerConnectionAge, "keepaliveMaxServerConnectionAge", - o.MaxServerConnectionAge, "Maximum duration a connection will be kept open on the server before a graceful close.") -} diff --git a/pkg/keepalive/options_test.go b/pkg/keepalive/options_test.go deleted file mode 100644 index 3d4e6e236..000000000 --- a/pkg/keepalive/options_test.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package keepalive_test - -import ( - "bytes" - "fmt" - "testing" - "time" -) - -import ( - "github.com/spf13/cobra" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/keepalive" -) - -// Test default maximum connection age is set to infinite, preserving previous -// unbounded lifetime behavior. -func TestAgeDefaultsToInfinite(t *testing.T) { - ko := keepalive.DefaultOption() - - if ko.MaxServerConnectionAge != keepalive.Infinity { - t.Errorf("%s maximum connection age %v", t.Name(), ko.MaxServerConnectionAge) - } -} - -// Confirm maximum connection age parameters can be set from the command line. -func TestSetConnectionAgeCommandlineOptions(t *testing.T) { - ko := keepalive.DefaultOption() - cmd := &cobra.Command{} - ko.AttachCobraFlags(cmd) - - buf := new(bytes.Buffer) - cmd.SetOut(buf) - cmd.SetErr(buf) - sec := 1 * time.Second - cmd.SetArgs([]string{ - fmt.Sprintf("--keepaliveMaxServerConnectionAge=%v", sec), - }) - - if err := cmd.Execute(); err != nil { - t.Errorf("%s %s", t.Name(), err.Error()) - } - if ko.MaxServerConnectionAge != sec { - t.Errorf("%s maximum connection age %v", t.Name(), ko.MaxServerConnectionAge) - } -} diff --git a/pkg/kube/adapter.go b/pkg/kube/adapter.go deleted file mode 100644 index 73f4e5c4b..000000000 --- a/pkg/kube/adapter.go +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "fmt" -) - -import ( - kubeApiAdmissionv1 "k8s.io/api/admission/v1" - kubeApiAdmissionv1beta1 "k8s.io/api/admission/v1beta1" - authenticationv1 "k8s.io/api/authentication/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" -) - -const ( - // APIVersion constants - admissionAPIV1 = "admission.k8s.io/v1" - admissionAPIV1beta1 = "admission.k8s.io/v1beta1" - - // Operation constants - Create string = "CREATE" - Update string = "UPDATE" - Delete string = "DELETE" - Connect string = "CONNECT" -) - -// AdmissionReview describes an admission review request/response. -type AdmissionReview struct { - // TypeMeta describes an individual object in an API response or request - // with strings representing the type of the object and its API schema version. - // Structures that are versioned or persisted should inline TypeMeta. - metav1.TypeMeta - - // Request describes the attributes for the admission request. - Request *AdmissionRequest `json:"request,omitempty"` - - // Response describes the attributes for the admission response. - Response *AdmissionResponse `json:"response,omitempty"` -} - -// AdmissionRequest describes the admission.Attributes for the admission request. -type AdmissionRequest struct { - - // UID is an identifier for the individual request/response. It allows us to distinguish instances of requests which are - // otherwise identical (parallel requests, requests when earlier requests did not modify etc) - // The UID is meant to track the round trip (request/response) between the KAS and the WebHook, not the user request. - // It is suitable for correlating log entries between the webhook and apiserver, for either auditing or debugging. - UID types.UID `json:"uid"` - - // Kind is the fully-qualified type of object being submitted (for example, v1.Pod or autoscaling.v1.Scale) - Kind metav1.GroupVersionKind `json:"kind"` - - // Resource is the fully-qualified resource being requested (for example, v1.pods) - Resource metav1.GroupVersionResource `json:"resource"` - - // SubResource is the subresource being requested, if any (for example, "status" or "scale") - SubResource string `json:"subResource,omitempty"` - // RequestKind is the fully-qualified type of the original API request (for example, v1.Pod or autoscaling.v1.Scale). - // If this is specified and differs from the value in "kind", an equivalent match and conversion was performed. - // - // For example, if deployments can be modified via apps/v1 and apps/v1beta1, and a webhook registered a rule of - // `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]` and `matchPolicy: Equivalent`, - // an API request to apps/v1beta1 deployments would be converted and sent to the webhook - // with `kind: {group:"apps", version:"v1", kind:"Deployment"}` (matching the rule the webhook registered for), - // and `requestKind: {group:"apps", version:"v1beta1", kind:"Deployment"}` (indicating the kind of the original API request). - // - RequestKind *metav1.GroupVersionKind `json:"requestKind,omitempty"` - - // RequestResource is the fully-qualified resource of the original API request (for example, v1.pods). - // If this is specified and differs from the value in "resource", an equivalent match and conversion was performed. - // - // For example, if deployments can be modified via apps/v1 and apps/v1beta1, and a webhook registered a rule of - // `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]` and `matchPolicy: Equivalent`, - // an API request to apps/v1beta1 deployments would be converted and sent to the webhook - // with `resource: {group:"apps", version:"v1", resource:"deployments"}` (matching the resource the webhook registered for), - // and `requestResource: {group:"apps", version:"v1beta1", resource:"deployments"}` (indicating the resource of the original API request). - // - RequestResource *metav1.GroupVersionResource `json:"requestResource,omitempty"` - - // RequestSubResource is the name of the subresource of the original API request, if any (for example, "status" or "scale") - // If this is specified and differs from the value in "subResource", an equivalent match and conversion was performed. - RequestSubResource string `json:"requestSubResource,omitempty"` - - // UserInfo is information about the requesting user - UserInfo authenticationv1.UserInfo `json:"userInfo"` - - // Name is the name of the object as presented in the request. On a CREATE operation, the client may omit name and - // rely on the server to generate the name. If that is the case, this field will contain an empty string. - Name string `json:"name,omitempty"` - - // Namespace is the namespace associated with the request (if any). - Namespace string `json:"namespace,omitempty"` - - // Operation is the operation being performed. This may be different than the operation - // requested. e.g. a patch can result in either a CREATE or UPDATE Operation. - Operation string `json:"operation"` - - // Object is the object from the incoming request. - Object runtime.RawExtension `json:"object,omitempty"` - - // OldObject is the existing object. Only populated for DELETE and UPDATE requests. - OldObject runtime.RawExtension `json:"oldObject,omitempty"` - - // DryRun indicates that modifications will definitely not be persisted for this request. - // Defaults to false. - DryRun *bool `json:"dryRun,omitempty"` - - // Options is the operation option structure of the operation being performed. - // e.g. `meta.k8s.io/v1.DeleteOptions` or `meta.k8s.io/v1.CreateOptions`. This may be - // different than the options the caller provided. e.g. for a patch request the performed - // Operation might be a CREATE, in which case the Options will a - // `meta.k8s.io/v1.CreateOptions` even though the caller provided `meta.k8s.io/v1.PatchOptions`. - Options runtime.RawExtension `json:"options,omitempty"` -} - -// AdmissionResponse describes an admission response. -type AdmissionResponse struct { - - // UID is an identifier for the individual request/response. - // This should be copied over from the corresponding AdmissionRequest. - UID types.UID `json:"uid"` - - // Allowed indicates whether or not the admission request was permitted. - Allowed bool `json:"allowed"` - - // Result contains extra details into why an admission request was denied. - // This field IS NOT consulted in any way if "Allowed" is "true". - Result *metav1.Status `json:"status,omitempty"` - - // The patch body. Currently we only support "JSONPatch" which implements RFC 6902. - Patch []byte `json:"patch,omitempty"` - - // The type of Patch. Currently we only allow "JSONPatch". - PatchType *string `json:"patchType,omitempty"` - - // AuditAnnotations is an unstructured key value map set by remote admission controller (e.g. error=image-blacklisted). - // MutatingAdmissionWebhook and ValidatingAdmissionWebhook admission controller will prefix the keys with - // admission webhook name (e.g. imagepolicy.example.com/error=image-blacklisted). AuditAnnotations will be provided by - // the admission webhook to add additional context to the audit log for this request. - AuditAnnotations map[string]string `json:"auditAnnotations,omitempty"` - - // warnings is a list of warning messages to return to the requesting API client. - // Warning messages describe a problem the client making the API request should correct or be aware of. - // Limit warnings to 120 characters if possible. - // Warnings over 256 characters and large numbers of warnings may be truncated. - Warnings []string `json:"warnings,omitempty"` -} - -func AdmissionReviewKubeToAdapter(object runtime.Object) (*AdmissionReview, error) { - var typeMeta metav1.TypeMeta - var req *AdmissionRequest - var resp *AdmissionResponse - switch obj := object.(type) { - case *kubeApiAdmissionv1beta1.AdmissionReview: - typeMeta = obj.TypeMeta - arv1beta1Response := obj.Response - arv1beta1Request := obj.Request - if arv1beta1Response != nil { - resp = &AdmissionResponse{ - UID: arv1beta1Response.UID, - Allowed: arv1beta1Response.Allowed, - Result: arv1beta1Response.Result, - Patch: arv1beta1Response.Patch, - Warnings: arv1beta1Response.Warnings, - } - if arv1beta1Response.PatchType != nil { - patchType := string(*arv1beta1Response.PatchType) - resp.PatchType = &patchType - } - } - if arv1beta1Request != nil { - req = &AdmissionRequest{ - UID: arv1beta1Request.UID, - Kind: arv1beta1Request.Kind, - Resource: arv1beta1Request.Resource, - UserInfo: arv1beta1Request.UserInfo, - Name: arv1beta1Request.Name, - Namespace: arv1beta1Request.Namespace, - Operation: string(arv1beta1Request.Operation), - Object: arv1beta1Request.Object, - OldObject: arv1beta1Request.OldObject, - } - } - - case *kubeApiAdmissionv1.AdmissionReview: - typeMeta = obj.TypeMeta - arv1Response := obj.Response - arv1Request := obj.Request - if arv1Response != nil { - resp = &AdmissionResponse{ - UID: arv1Response.UID, - Allowed: arv1Response.Allowed, - Result: arv1Response.Result, - Patch: arv1Response.Patch, - Warnings: arv1Response.Warnings, - } - if arv1Response.PatchType != nil { - patchType := string(*arv1Response.PatchType) - resp.PatchType = &patchType - } - } - - if arv1Request != nil { - req = &AdmissionRequest{ - UID: arv1Request.UID, - Kind: arv1Request.Kind, - Resource: arv1Request.Resource, - UserInfo: arv1Request.UserInfo, - Name: arv1Request.Name, - Namespace: arv1Request.Namespace, - Operation: string(arv1Request.Operation), - Object: arv1Request.Object, - OldObject: arv1Request.OldObject, - } - } - - default: - return nil, fmt.Errorf("unsupported type :%v", object.GetObjectKind()) - } - - return &AdmissionReview{ - TypeMeta: typeMeta, - Request: req, - Response: resp, - }, nil -} - -func AdmissionReviewAdapterToKube(ar *AdmissionReview, apiVersion string) runtime.Object { - var res runtime.Object - arRequest := ar.Request - arResponse := ar.Response - if apiVersion == "" { - apiVersion = admissionAPIV1beta1 - } - switch apiVersion { - case admissionAPIV1beta1: - arv1beta1 := kubeApiAdmissionv1beta1.AdmissionReview{} - if arRequest != nil { - arv1beta1.Request = &kubeApiAdmissionv1beta1.AdmissionRequest{ - UID: arRequest.UID, - Kind: arRequest.Kind, - Resource: arRequest.Resource, - SubResource: arRequest.SubResource, - Name: arRequest.Name, - Namespace: arRequest.Namespace, - RequestKind: arRequest.RequestKind, - RequestResource: arRequest.RequestResource, - RequestSubResource: arRequest.RequestSubResource, - Operation: kubeApiAdmissionv1beta1.Operation(arRequest.Operation), - UserInfo: arRequest.UserInfo, - Object: arRequest.Object, - OldObject: arRequest.OldObject, - DryRun: arRequest.DryRun, - Options: arRequest.Options, - } - } - if arResponse != nil { - var patchType *kubeApiAdmissionv1beta1.PatchType - if arResponse.PatchType != nil { - patchType = (*kubeApiAdmissionv1beta1.PatchType)(arResponse.PatchType) - } - arv1beta1.Response = &kubeApiAdmissionv1beta1.AdmissionResponse{ - UID: arResponse.UID, - Allowed: arResponse.Allowed, - Result: arResponse.Result, - Patch: arResponse.Patch, - PatchType: patchType, - AuditAnnotations: arResponse.AuditAnnotations, - Warnings: arResponse.Warnings, - } - } - arv1beta1.TypeMeta = ar.TypeMeta - res = &arv1beta1 - case admissionAPIV1: - arv1 := kubeApiAdmissionv1.AdmissionReview{} - if arRequest != nil { - arv1.Request = &kubeApiAdmissionv1.AdmissionRequest{ - UID: arRequest.UID, - Kind: arRequest.Kind, - Resource: arRequest.Resource, - SubResource: arRequest.SubResource, - Name: arRequest.Name, - Namespace: arRequest.Namespace, - RequestKind: arRequest.RequestKind, - RequestResource: arRequest.RequestResource, - RequestSubResource: arRequest.RequestSubResource, - Operation: kubeApiAdmissionv1.Operation(arRequest.Operation), - UserInfo: arRequest.UserInfo, - Object: arRequest.Object, - OldObject: arRequest.OldObject, - DryRun: arRequest.DryRun, - Options: arRequest.Options, - } - } - if arResponse != nil { - var patchType *kubeApiAdmissionv1.PatchType - if arResponse.PatchType != nil { - patchType = (*kubeApiAdmissionv1.PatchType)(arResponse.PatchType) - } - arv1.Response = &kubeApiAdmissionv1.AdmissionResponse{ - UID: arResponse.UID, - Allowed: arResponse.Allowed, - Result: arResponse.Result, - Patch: arResponse.Patch, - PatchType: patchType, - AuditAnnotations: arResponse.AuditAnnotations, - Warnings: arResponse.Warnings, - } - } - arv1.TypeMeta = ar.TypeMeta - res = &arv1 - } - return res -} diff --git a/pkg/kube/apimirror/probe.go b/pkg/kube/apimirror/probe.go deleted file mode 100644 index 2355c9659..000000000 --- a/pkg/kube/apimirror/probe.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package apimirror contains copies of Kubernetes APIs. This allows json serialization, without worrying about -// importing the massive 15mb+ Kubernetes API libraries. -// This is intended for import only by istio-agent. Any other binaries (Istiod) should likely import the -// upstream Kubernetes API instead. -package apimirror - -import ( - "k8s.io/apimachinery/pkg/util/intstr" -) - -// HTTPGetAction describes an action based on HTTP Get requests. -type HTTPGetAction struct { - // Path to access on the HTTP server. - // +optional - Path string `json:"path,omitempty" protobuf:"bytes,1,opt,name=path"` - // Name or number of the port to access on the container. - // Number must be in the range 1 to 65535. - // Name must be an IANA_SVC_NAME. - Port intstr.IntOrString `json:"port" protobuf:"bytes,2,opt,name=port"` - // Host name to connect to, defaults to the pod IP. You probably want to set - // "Host" in httpHeaders instead. - // +optional - Host string `json:"host,omitempty" protobuf:"bytes,3,opt,name=host"` - // Scheme to use for connecting to the host. - // Defaults to HTTP. - // +optional - Scheme URIScheme `json:"scheme,omitempty" protobuf:"bytes,4,opt,name=scheme,casttype=URIScheme"` - // Custom headers to set in the request. HTTP allows repeated headers. - // +optional - HTTPHeaders []HTTPHeader `json:"httpHeaders,omitempty" protobuf:"bytes,5,rep,name=httpHeaders"` -} - -// URIScheme identifies the scheme used for connection to a host for Get actions -type URIScheme string - -const ( - // URISchemeHTTP means that the scheme used will be http:// - URISchemeHTTP URIScheme = "HTTP" - // URISchemeHTTPS means that the scheme used will be https:// - URISchemeHTTPS URIScheme = "HTTPS" -) - -// HTTPHeader describes a custom header to be used in HTTP probes -type HTTPHeader struct { - // The header field name - Name string `json:"name" protobuf:"bytes,1,opt,name=name"` - // The header field value - Value string `json:"value" protobuf:"bytes,2,opt,name=value"` -} - -// TCPSocketAction describes an action based on opening a socket -type TCPSocketAction struct { - // Number or name of the port to access on the container. - // Number must be in the range 1 to 65535. - // Name must be an IANA_SVC_NAME. - Port intstr.IntOrString `json:"port" protobuf:"bytes,1,opt,name=port"` - // Optional: Host name to connect to, defaults to the pod IP. - // +optional - Host string `json:"host,omitempty" protobuf:"bytes,2,opt,name=host"` -} - -// GRPCAction describes an action based on GRPC health check -type GRPCAction struct { - // Port number of the gRPC service. Number must be in the range 1 to 65535. - Port int32 `json:"port" protobuf:"bytes,1,opt,name=port"` - - // Service is the name of the service to place in the gRPC HealthCheckRequest - // (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). - // - // If this is not specified, the default behavior is defined by gRPC. - // +optional - // +default="" - Service *string `json:"service" protobuf:"bytes,2,opt,name=service"` -} diff --git a/pkg/kube/client.go b/pkg/kube/client.go deleted file mode 100644 index 34bd6f266..000000000 --- a/pkg/kube/client.go +++ /dev/null @@ -1,1166 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "os" - "reflect" - "strings" - "sync" - "time" -) - -import ( - "github.com/hashicorp/go-multierror" - "go.uber.org/atomic" - "golang.org/x/sync/errgroup" - "google.golang.org/grpc/credentials" - "istio.io/api/label" - clientextensions "istio.io/client-go/pkg/apis/extensions/v1alpha1" - clientnetworkingalpha "istio.io/client-go/pkg/apis/networking/v1alpha3" - clientnetworkingbeta "istio.io/client-go/pkg/apis/networking/v1beta1" - clientsecurity "istio.io/client-go/pkg/apis/security/v1beta1" - clienttelemetry "istio.io/client-go/pkg/apis/telemetry/v1alpha1" - istioclient "istio.io/client-go/pkg/clientset/versioned" - istiofake "istio.io/client-go/pkg/clientset/versioned/fake" - istioinformer "istio.io/client-go/pkg/informers/externalversions" - "istio.io/pkg/version" - v1 "k8s.io/api/core/v1" - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - kubeExtClient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" - extfake "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake" - kubeExtInformers "k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/util/wait" - kubeVersion "k8s.io/apimachinery/pkg/version" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/cli-runtime/pkg/genericclioptions" - "k8s.io/cli-runtime/pkg/printers" - "k8s.io/cli-runtime/pkg/resource" - "k8s.io/client-go/discovery" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/dynamic/dynamicinformer" - dynamicfake "k8s.io/client-go/dynamic/fake" - "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/fake" - kubescheme "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/metadata" - metadatafake "k8s.io/client-go/metadata/fake" - "k8s.io/client-go/metadata/metadatainformer" - "k8s.io/client-go/rest" - clienttesting "k8s.io/client-go/testing" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/tools/remotecommand" - "k8s.io/kubectl/pkg/cmd/apply" - kubectlDelete "k8s.io/kubectl/pkg/cmd/delete" - "k8s.io/kubectl/pkg/cmd/util" - gatewayapi "sigs.k8s.io/gateway-api/apis/v1alpha2" - gatewayapiclient "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned" - gatewayapifake "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned/fake" - gatewayapiinformer "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/apis" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/kube/mcs" - "github.com/apache/dubbo-go-pixiu/pkg/queue" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/yml" -) - -const ( - defaultLocalAddress = "localhost" - fieldManager = "istio-kube-client" -) - -// Client is a helper for common Kubernetes client operations. This contains various different kubernetes -// clients using a shared config. It is expected that all of Istiod can share the same set of clients and -// informers. Sharing informers is especially important for load on the API server/Istiod itself. -type Client interface { - // TODO: stop embedding this, it will conflict with future additions. Use Kube() instead is preferred - kubernetes.Interface - // RESTConfig returns the Kubernetes rest.Config used to configure the clients. - RESTConfig() *rest.Config - - // Ext returns the API extensions client. - Ext() kubeExtClient.Interface - - // Kube returns the core kube client - Kube() kubernetes.Interface - - // Dynamic client. - Dynamic() dynamic.Interface - - // Metadata returns the Metadata kube client. - Metadata() metadata.Interface - - // Istio returns the Istio kube client. - Istio() istioclient.Interface - - // GatewayAPI returns the gateway-api kube client. - GatewayAPI() gatewayapiclient.Interface - - // KubeInformer returns an informer for core kube client - KubeInformer() informers.SharedInformerFactory - - // DynamicInformer returns an informer for dynamic client - DynamicInformer() dynamicinformer.DynamicSharedInformerFactory - - // MetadataInformer returns an informer for metadata client - MetadataInformer() metadatainformer.SharedInformerFactory - - // IstioInformer returns an informer for the istio client - IstioInformer() istioinformer.SharedInformerFactory - - // GatewayAPIInformer returns an informer for the gateway-api client - GatewayAPIInformer() gatewayapiinformer.SharedInformerFactory - - // ExtInformer returns an informer for the extension client - ExtInformer() kubeExtInformers.SharedInformerFactory - - // RunAndWait starts all informers and waits for their caches to sync. - // Warning: this must be called AFTER .Informer() is called, which will register the informer. - RunAndWait(stop <-chan struct{}) - - // GetKubernetesVersion returns the Kubernetes server version - GetKubernetesVersion() (*kubeVersion.Info, error) -} - -// ExtendedClient is an extended client with additional helpers/functionality for Istioctl and testing. -type ExtendedClient interface { - Client - // Revision of the Istio control plane. - Revision() string - - // EnvoyDo makes an http request to the Envoy in the specified pod. - EnvoyDo(ctx context.Context, podName, podNamespace, method, path string) ([]byte, error) - - // EnvoyDoWithPort makes an http request to the Envoy in the specified pod and port. - EnvoyDoWithPort(ctx context.Context, podName, podNamespace, method, path string, port int) ([]byte, error) - - // AllDiscoveryDo makes an http request to each Istio discovery instance. - AllDiscoveryDo(ctx context.Context, namespace, path string) (map[string][]byte, error) - - // GetIstioVersions gets the version for each Istio control plane component. - GetIstioVersions(ctx context.Context, namespace string) (*version.MeshInfo, error) - - // PodsForSelector finds pods matching selector. - PodsForSelector(ctx context.Context, namespace string, labelSelectors ...string) (*v1.PodList, error) - - // GetIstioPods retrieves the pod objects for Istio deployments - GetIstioPods(ctx context.Context, namespace string, params map[string]string) ([]v1.Pod, error) - - // PodExecCommands takes a list of commands and the pod data to run the commands in the specified pod. - PodExecCommands(podName, podNamespace, container string, commands []string) (stdout string, stderr string, err error) - - // PodExec takes a command and the pod data to run the command in the specified pod. - PodExec(podName, podNamespace, container string, command string) (stdout string, stderr string, err error) - - // PodLogs retrieves the logs for the given pod. - PodLogs(ctx context.Context, podName string, podNamespace string, container string, previousLog bool) (string, error) - - // NewPortForwarder creates a new PortForwarder configured for the given pod. If localPort=0, a port will be - // dynamically selected. If localAddress is empty, "localhost" is used. - NewPortForwarder(podName string, ns string, localAddress string, localPort int, podPort int) (PortForwarder, error) - - // ApplyYAMLFiles applies the resources in the given YAML files. - ApplyYAMLFiles(namespace string, yamlFiles ...string) error - - // ApplyYAMLFilesDryRun performs a dry run for applying the resource in the given YAML files - ApplyYAMLFilesDryRun(namespace string, yamlFiles ...string) error - - // DeleteYAMLFiles deletes the resources in the given YAML files. - DeleteYAMLFiles(namespace string, yamlFiles ...string) error - - // DeleteYAMLFilesDryRun performs a dry run for deleting the resources in the given YAML files. - DeleteYAMLFilesDryRun(namespace string, yamlFiles ...string) error - - // CreatePerRPCCredentials creates a gRPC bearer token provider that can create (and renew!) Istio tokens - CreatePerRPCCredentials(ctx context.Context, tokenNamespace, tokenServiceAccount string, audiences []string, - expirationSeconds int64) (credentials.PerRPCCredentials, error) - - // UtilFactory returns a kubectl factory - UtilFactory() util.Factory -} - -var ( - _ Client = &client{} - _ ExtendedClient = &client{} -) - -const resyncInterval = 0 - -// NewFakeClient creates a new, fake, client -func NewFakeClient(objects ...runtime.Object) ExtendedClient { - c := &client{ - informerWatchesPending: atomic.NewInt32(0), - } - c.Interface = fake.NewSimpleClientset(objects...) - c.kube = c.Interface - c.kubeInformer = informers.NewSharedInformerFactory(c.Interface, resyncInterval) - s := FakeIstioScheme - - c.metadata = metadatafake.NewSimpleMetadataClient(s) - c.metadataInformer = metadatainformer.NewSharedInformerFactory(c.metadata, resyncInterval) - // Support some galley tests using basicmetadata - // If you are adding something to this list, consider other options like adding to the scheme. - gvrToListKind := map[schema.GroupVersionResource]string{ - {Group: "testdata.istio.io", Version: "v1alpha1", Resource: "Kind1s"}: "Kind1List", - } - c.dynamic = dynamicfake.NewSimpleDynamicClientWithCustomListKinds(s, gvrToListKind) - c.dynamicInformer = dynamicinformer.NewDynamicSharedInformerFactory(c.dynamic, resyncInterval) - - c.istio = istiofake.NewSimpleClientset() - c.istioInformer = istioinformer.NewSharedInformerFactoryWithOptions(c.istio, resyncInterval) - - c.gatewayapi = gatewayapifake.NewSimpleClientset() - c.gatewayapiInformer = gatewayapiinformer.NewSharedInformerFactory(c.gatewayapi, resyncInterval) - - c.extSet = extfake.NewSimpleClientset() - c.extInformer = kubeExtInformers.NewSharedInformerFactory(c.extSet, resyncInterval) - - // https://github.com/kubernetes/kubernetes/issues/95372 - // There is a race condition in the client fakes, where events that happen between the List and Watch - // of an informer are dropped. To avoid this, we explicitly manage the list and watch, ensuring all lists - // have an associated watch before continuing. - // This would likely break any direct calls to List(), but for now our tests don't do that anyways. If we need - // to in the future we will need to identify the Lists that have a corresponding Watch, possibly by looking - // at created Informers - // an atomic.Int is used instead of sync.WaitGroup because wg.Add and wg.Wait cannot be called concurrently - listReactor := func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { - c.informerWatchesPending.Inc() - return false, nil, nil - } - watchReactor := func(tracker clienttesting.ObjectTracker) func(action clienttesting.Action) (handled bool, ret watch.Interface, err error) { - return func(action clienttesting.Action) (handled bool, ret watch.Interface, err error) { - gvr := action.GetResource() - ns := action.GetNamespace() - watch, err := tracker.Watch(gvr, ns) - if err != nil { - return false, nil, err - } - c.informerWatchesPending.Dec() - return true, watch, nil - } - } - for _, fc := range []fakeClient{ - c.kube.(*fake.Clientset), - c.istio.(*istiofake.Clientset), - c.gatewayapi.(*gatewayapifake.Clientset), - c.dynamic.(*dynamicfake.FakeDynamicClient), - // TODO: send PR to client-go to add Tracker() - // c.metadata.(*metadatafake.FakeMetadataClient), - } { - fc.PrependReactor("list", "*", listReactor) - fc.PrependWatchReactor("*", watchReactor(fc.Tracker())) - } - - // discoveryv1/EndpontSlices readable from discoveryv1beta1/EndpointSlices - c.mirrorQueue = queue.NewQueue(1 * time.Second) - mirrorResource( - c.mirrorQueue, - c.kubeInformer.Discovery().V1().EndpointSlices().Informer(), - c.kube.DiscoveryV1beta1().EndpointSlices, - endpointSliceV1toV1beta1, - ) - - c.fastSync = true - - return c -} - -func NewFakeClientWithVersion(minor string, objects ...runtime.Object) ExtendedClient { - c := NewFakeClient(objects...).(*client) - if minor != "" && minor != "latest" { - c.versionOnce.Do(func() { - c.version = &kubeVersion.Info{Major: "1", Minor: minor, GitVersion: fmt.Sprintf("v1.%v.0", minor)} - }) - } - return c -} - -type fakeClient interface { - PrependReactor(verb, resource string, reaction clienttesting.ReactionFunc) - PrependWatchReactor(resource string, reaction clienttesting.WatchReactionFunc) - Tracker() clienttesting.ObjectTracker -} - -// Client is a helper wrapper around the Kube RESTClient for istioctl -> Pilot/Envoy/Mesh related things -type client struct { - kubernetes.Interface - - clientFactory util.Factory - config *rest.Config - - extSet kubeExtClient.Interface - extInformer kubeExtInformers.SharedInformerFactory - - kube kubernetes.Interface - kubeInformer informers.SharedInformerFactory - - dynamic dynamic.Interface - dynamicInformer dynamicinformer.DynamicSharedInformerFactory - - metadata metadata.Interface - metadataInformer metadatainformer.SharedInformerFactory - - istio istioclient.Interface - istioInformer istioinformer.SharedInformerFactory - - gatewayapi gatewayapiclient.Interface - gatewayapiInformer gatewayapiinformer.SharedInformerFactory - - // If enable, will wait for cache syncs with extremely short delay. This should be used only for tests - fastSync bool - informerWatchesPending *atomic.Int32 - - mirrorQueue queue.Instance - mirrorQueueStarted atomic.Bool - - // These may be set only when creating an extended client. - revision string - restClient *rest.RESTClient - discoveryClient discovery.CachedDiscoveryInterface - mapper meta.RESTMapper - - versionOnce sync.Once - version *kubeVersion.Info -} - -// newClientInternal creates a Kubernetes client from the given factory. -func newClientInternal(clientFactory util.Factory, revision string) (*client, error) { - var c client - var err error - - c.clientFactory = clientFactory - - c.config, err = clientFactory.ToRESTConfig() - if err != nil { - return nil, err - } - - c.revision = revision - - c.restClient, err = clientFactory.RESTClient() - if err != nil { - return nil, err - } - - c.discoveryClient, err = clientFactory.ToDiscoveryClient() - if err != nil { - return nil, err - } - c.mapper, err = clientFactory.ToRESTMapper() - if err != nil { - return nil, err - } - - c.Interface, err = kubernetes.NewForConfig(c.config) - c.kube = c.Interface - if err != nil { - return nil, err - } - c.kubeInformer = informers.NewSharedInformerFactory(c.Interface, resyncInterval) - - c.metadata, err = metadata.NewForConfig(c.config) - if err != nil { - return nil, err - } - c.metadataInformer = metadatainformer.NewSharedInformerFactory(c.metadata, resyncInterval) - - c.dynamic, err = dynamic.NewForConfig(c.config) - if err != nil { - return nil, err - } - c.dynamicInformer = dynamicinformer.NewDynamicSharedInformerFactory(c.dynamic, resyncInterval) - - c.istio, err = istioclient.NewForConfig(c.config) - if err != nil { - return nil, err - } - c.istioInformer = istioinformer.NewSharedInformerFactory(c.istio, resyncInterval) - - c.gatewayapi, err = gatewayapiclient.NewForConfig(c.config) - if err != nil { - return nil, err - } - c.gatewayapiInformer = gatewayapiinformer.NewSharedInformerFactory(c.gatewayapi, resyncInterval) - - c.extSet, err = kubeExtClient.NewForConfig(c.config) - if err != nil { - return nil, err - } - c.extInformer = kubeExtInformers.NewSharedInformerFactory(c.extSet, resyncInterval) - - return &c, nil -} - -// NewDefaultClient returns a default client, using standard Kubernetes config resolution to determine -// the cluster to access. -func NewDefaultClient() (ExtendedClient, error) { - return NewExtendedClient(BuildClientCmd("", ""), "") -} - -// NewExtendedClient creates a Kubernetes client from the given ClientConfig. The "revision" parameter -// controls the behavior of GetIstioPods, by selecting a specific revision of the control plane. -func NewExtendedClient(clientConfig clientcmd.ClientConfig, revision string) (ExtendedClient, error) { - return newClientInternal(newClientFactory(clientConfig), revision) -} - -// NewClient creates a Kubernetes client from the given rest config. -func NewClient(clientConfig clientcmd.ClientConfig) (Client, error) { - return newClientInternal(newClientFactory(clientConfig), "") -} - -func (c *client) RESTConfig() *rest.Config { - if c.config == nil { - return nil - } - cpy := *c.config - return &cpy -} - -func (c *client) Ext() kubeExtClient.Interface { - return c.extSet -} - -func (c *client) Dynamic() dynamic.Interface { - return c.dynamic -} - -func (c *client) Kube() kubernetes.Interface { - return c.kube -} - -func (c *client) Metadata() metadata.Interface { - return c.metadata -} - -func (c *client) Istio() istioclient.Interface { - return c.istio -} - -func (c *client) GatewayAPI() gatewayapiclient.Interface { - return c.gatewayapi -} - -func (c *client) KubeInformer() informers.SharedInformerFactory { - return c.kubeInformer -} - -func (c *client) DynamicInformer() dynamicinformer.DynamicSharedInformerFactory { - return c.dynamicInformer -} - -func (c *client) MetadataInformer() metadatainformer.SharedInformerFactory { - return c.metadataInformer -} - -func (c *client) IstioInformer() istioinformer.SharedInformerFactory { - return c.istioInformer -} - -func (c *client) GatewayAPIInformer() gatewayapiinformer.SharedInformerFactory { - return c.gatewayapiInformer -} - -func (c *client) ExtInformer() kubeExtInformers.SharedInformerFactory { - return c.extInformer -} - -// RunAndWait starts all informers and waits for their caches to sync. -// Warning: this must be called AFTER .Informer() is called, which will register the informer. -func (c *client) RunAndWait(stop <-chan struct{}) { - if c.mirrorQueue != nil && !c.mirrorQueueStarted.Load() { - c.mirrorQueueStarted.Store(true) - go c.mirrorQueue.Run(stop) - } - c.kubeInformer.Start(stop) - c.dynamicInformer.Start(stop) - c.metadataInformer.Start(stop) - c.istioInformer.Start(stop) - c.gatewayapiInformer.Start(stop) - c.extInformer.Start(stop) - if c.fastSync { - // WaitForCacheSync will virtually never be synced on the first call, as its called immediately after Start() - // This triggers a 100ms delay per call, which is often called 2-3 times in a test, delaying tests. - // Instead, we add an aggressive sync polling - fastWaitForCacheSync(stop, c.kubeInformer) - fastWaitForCacheSyncDynamic(stop, c.dynamicInformer) - fastWaitForCacheSyncDynamic(stop, c.metadataInformer) - fastWaitForCacheSync(stop, c.istioInformer) - fastWaitForCacheSync(stop, c.gatewayapiInformer) - fastWaitForCacheSync(stop, c.extInformer) - _ = wait.PollImmediate(time.Microsecond*100, wait.ForeverTestTimeout, func() (bool, error) { - select { - case <-stop: - return false, fmt.Errorf("channel closed") - default: - } - if c.informerWatchesPending.Load() == 0 { - return true, nil - } - return false, nil - }) - } else { - c.kubeInformer.WaitForCacheSync(stop) - c.dynamicInformer.WaitForCacheSync(stop) - c.metadataInformer.WaitForCacheSync(stop) - c.istioInformer.WaitForCacheSync(stop) - c.gatewayapiInformer.WaitForCacheSync(stop) - c.extInformer.WaitForCacheSync(stop) - } -} - -func (c *client) GetKubernetesVersion() (*kubeVersion.Info, error) { - c.versionOnce.Do(func() { - v, err := c.Discovery().ServerVersion() - if err == nil { - c.version = v - } - }) - if c.version != nil { - return c.version, nil - } - // Initial attempt failed, retry on each call to this function - v, err := c.Discovery().ServerVersion() - if err != nil { - c.version = v - } - return c.version, err -} - -type reflectInformerSync interface { - WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool -} - -type dynamicInformerSync interface { - WaitForCacheSync(stopCh <-chan struct{}) map[schema.GroupVersionResource]bool -} - -// Wait for cache sync immediately, rather than with 100ms delay which slows tests -// See https://github.com/kubernetes/kubernetes/issues/95262#issuecomment-703141573 -func fastWaitForCacheSync(stop <-chan struct{}, informerFactory reflectInformerSync) { - returnImmediately := make(chan struct{}) - close(returnImmediately) - _ = wait.PollImmediate(time.Microsecond*100, wait.ForeverTestTimeout, func() (bool, error) { - select { - case <-stop: - return false, fmt.Errorf("channel closed") - default: - } - for _, synced := range informerFactory.WaitForCacheSync(returnImmediately) { - if !synced { - return false, nil - } - } - return true, nil - }) -} - -func fastWaitForCacheSyncDynamic(stop <-chan struct{}, informerFactory dynamicInformerSync) { - returnImmediately := make(chan struct{}) - close(returnImmediately) - _ = wait.PollImmediate(time.Microsecond*100, wait.ForeverTestTimeout, func() (bool, error) { - select { - case <-stop: - return false, fmt.Errorf("channel closed") - default: - } - for _, synced := range informerFactory.WaitForCacheSync(returnImmediately) { - if !synced { - return false, nil - } - } - return true, nil - }) -} - -// WaitForCacheSyncInterval waits for caches to populate, with explicitly configured interval -func WaitForCacheSyncInterval(stopCh <-chan struct{}, interval time.Duration, cacheSyncs ...cache.InformerSynced) bool { - err := wait.PollImmediateUntil(interval, - func() (bool, error) { - for _, syncFunc := range cacheSyncs { - if !syncFunc() { - return false, nil - } - } - return true, nil - }, - stopCh) - return err == nil -} - -func (c *client) Revision() string { - return c.revision -} - -func (c *client) PodExecCommands(podName, podNamespace, container string, commands []string) (stdout, stderr string, err error) { - defer func() { - if err != nil { - if len(stderr) > 0 { - err = fmt.Errorf("error exec'ing into %s/%s %s container: %v\n%s", - podNamespace, podName, container, err, stderr) - } else { - err = fmt.Errorf("error exec'ing into %s/%s %s container: %v", - podNamespace, podName, container, err) - } - } - }() - - req := c.restClient.Post(). - Resource("pods"). - Name(podName). - Namespace(podNamespace). - SubResource("exec"). - Param("container", container). - VersionedParams(&v1.PodExecOptions{ - Container: container, - Command: commands, - Stdin: false, - Stdout: true, - Stderr: true, - TTY: false, - }, kubescheme.ParameterCodec) - - wrapper, upgrader, err := roundTripperFor(c.config) - if err != nil { - return "", "", err - } - exec, err := remotecommand.NewSPDYExecutorForTransports(wrapper, upgrader, "POST", req.URL()) - if err != nil { - return "", "", err - } - - var stdoutBuf, stderrBuf bytes.Buffer - err = exec.Stream(remotecommand.StreamOptions{ - Stdin: nil, - Stdout: &stdoutBuf, - Stderr: &stderrBuf, - Tty: false, - }) - - stdout = stdoutBuf.String() - stderr = stderrBuf.String() - return -} - -func (c *client) PodExec(podName, podNamespace, container string, command string) (stdout, stderr string, err error) { - commandFields := strings.Fields(command) - return c.PodExecCommands(podName, podNamespace, container, commandFields) -} - -func (c *client) PodLogs(ctx context.Context, podName, podNamespace, container string, previousLog bool) (string, error) { - opts := &v1.PodLogOptions{ - Container: container, - Previous: previousLog, - } - res, err := c.CoreV1().Pods(podNamespace).GetLogs(podName, opts).Stream(ctx) - if err != nil { - return "", err - } - defer closeQuietly(res) - - builder := &strings.Builder{} - if _, err = io.Copy(builder, res); err != nil { - return "", err - } - - return builder.String(), nil -} - -func (c *client) AllDiscoveryDo(ctx context.Context, istiodNamespace, path string) (map[string][]byte, error) { - istiods, err := c.GetIstioPods(ctx, istiodNamespace, map[string]string{ - "labelSelector": "app=istiod", - "fieldSelector": "status.phase=Running", - }) - if err != nil { - return nil, err - } - if len(istiods) == 0 { - return nil, errors.New("unable to find any Istiod instances") - } - - result := map[string][]byte{} - for _, istiod := range istiods { - res, err := c.portForwardRequest(ctx, istiod.Name, istiod.Namespace, http.MethodGet, path, 15014) - if err != nil { - return nil, err - } - if len(res) > 0 { - result[istiod.Name] = res - } - } - // If any Discovery servers responded, treat as a success - if len(result) > 0 { - return result, nil - } - return nil, nil -} - -func (c *client) EnvoyDo(ctx context.Context, podName, podNamespace, method, path string) ([]byte, error) { - return c.portForwardRequest(ctx, podName, podNamespace, method, path, 15000) -} - -func (c *client) EnvoyDoWithPort(ctx context.Context, podName, podNamespace, method, path string, port int) ([]byte, error) { - return c.portForwardRequest(ctx, podName, podNamespace, method, path, port) -} - -func (c *client) portForwardRequest(ctx context.Context, podName, podNamespace, method, path string, port int) ([]byte, error) { - formatError := func(err error) error { - return fmt.Errorf("failure running port forward process: %v", err) - } - - fw, err := c.NewPortForwarder(podName, podNamespace, "127.0.0.1", 0, port) - if err != nil { - return nil, err - } - if err = fw.Start(); err != nil { - return nil, formatError(err) - } - defer fw.Close() - req, err := http.NewRequest(method, fmt.Sprintf("http://%s/%s", fw.Address(), path), nil) - if err != nil { - return nil, formatError(err) - } - resp, err := http.DefaultClient.Do(req.WithContext(ctx)) - if err != nil { - return nil, formatError(err) - } - defer closeQuietly(resp.Body) - out, err := io.ReadAll(resp.Body) - if err != nil { - return nil, formatError(err) - } - - return out, nil -} - -func (c *client) GetIstioPods(ctx context.Context, namespace string, params map[string]string) ([]v1.Pod, error) { - if c.revision != "" { - labelSelector, ok := params["labelSelector"] - if ok { - params["labelSelector"] = fmt.Sprintf("%s,%s=%s", labelSelector, label.IoIstioRev.Name, c.revision) - } else { - params["labelSelector"] = fmt.Sprintf("%s=%s", label.IoIstioRev.Name, c.revision) - } - } - - req := c.restClient.Get(). - Resource("pods"). - Namespace(namespace) - for k, v := range params { - req.Param(k, v) - } - - res := req.Do(ctx) - if res.Error() != nil { - return nil, fmt.Errorf("unable to retrieve Pods: %v", res.Error()) - } - list := &v1.PodList{} - if err := res.Into(list); err != nil { - return nil, fmt.Errorf("unable to parse PodList: %v", res.Error()) - } - return list.Items, nil -} - -// ExtractExecResult wraps PodExec and return the execution result and error if has any. -func (c *client) extractExecResult(podName, podNamespace, container, cmd string) (string, error) { - stdout, stderr, err := c.PodExec(podName, podNamespace, container, cmd) - if err != nil { - if stderr != "" { - return "", fmt.Errorf("error exec'ing into %s/%s %s container: %w\n%s", podNamespace, podName, container, err, stderr) - } - return "", fmt.Errorf("error exec'ing into %s/%s %s container: %w", podNamespace, podName, container, err) - } - return stdout, nil -} - -func (c *client) GetIstioVersions(ctx context.Context, namespace string) (*version.MeshInfo, error) { - pods, err := c.GetIstioPods(ctx, namespace, map[string]string{ - "labelSelector": "app=istiod", - "fieldSelector": "status.phase=Running", - }) - if err != nil { - return nil, err - } - if len(pods) == 0 { - return nil, fmt.Errorf("no running Istio pods in %q", namespace) - } - - var errs error - res := version.MeshInfo{} - for _, pod := range pods { - component := pod.Labels["istio"] - server := version.ServerInfo{Component: component} - - // :15014/version returns something like - // 1.7-alpha.9c900ba74d10a1affe7c23557ef0eebd6103b03c-9c900ba74d10a1affe7c23557ef0eebd6103b03c-Clean - result, err := c.CoreV1().Pods(pod.Namespace).ProxyGet("", pod.Name, "15014", "/version", nil).DoRaw(ctx) - if err != nil { - bi, execErr := c.getIstioVersionUsingExec(&pod) - if execErr != nil { - errs = multierror.Append(errs, - fmt.Errorf("error port-forwarding into %s.%s: %v", pod.Namespace, pod.Name, err), - execErr, - ) - continue - } - server.Info = *bi - res = append(res, server) - continue - } - if len(result) > 0 { - setServerInfoWithIstiodVersionInfo(&server.Info, string(result)) - // (Golang version not available through :15014/version endpoint) - - res = append(res, server) - } - } - return &res, errs -} - -func (c *client) getIstioVersionUsingExec(pod *v1.Pod) (*version.BuildInfo, error) { - // exclude data plane components from control plane list - labelToPodDetail := map[string]struct { - binary string - container string - }{ - "pilot": {"/usr/local/bin/pilot-discovery", "discovery"}, - "istiod": {"/usr/local/bin/pilot-discovery", "discovery"}, - "citadel": {"/usr/local/bin/istio_ca", "citadel"}, - "galley": {"/usr/local/bin/galley", "galley"}, - "telemetry": {"/usr/local/bin/mixs", "mixer"}, - "policy": {"/usr/local/bin/mixs", "mixer"}, - "sidecar-injector": {"/usr/local/bin/sidecar-injector", "sidecar-injector-webhook"}, - } - - component := pod.Labels["istio"] - - // Special cases - switch component { - case "statsd-prom-bridge": - // statsd-prom-bridge doesn't support version - return nil, fmt.Errorf("statsd-prom-bridge doesn't support version") - case "mixer": - component = pod.Labels["istio-mixer-type"] - } - - detail, ok := labelToPodDetail[component] - if !ok { - return nil, fmt.Errorf("unknown Istio component %q", component) - } - - stdout, stderr, err := c.PodExec(pod.Name, pod.Namespace, detail.container, - fmt.Sprintf("%s version -o json", detail.binary)) - if err != nil { - return nil, fmt.Errorf("error exec'ing into %s %s container: %w", pod.Name, detail.container, err) - } - - var v version.Version - err = json.Unmarshal([]byte(stdout), &v) - if err == nil && v.ClientVersion.Version != "" { - return v.ClientVersion, nil - } - - return nil, fmt.Errorf("error reading %s %s container version: %v", pod.Name, detail.container, stderr) -} - -func (c *client) NewPortForwarder(podName, ns, localAddress string, localPort int, podPort int) (PortForwarder, error) { - return newPortForwarder(c.config, podName, ns, localAddress, localPort, podPort) -} - -func (c *client) PodsForSelector(ctx context.Context, namespace string, labelSelectors ...string) (*v1.PodList, error) { - return c.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{ - LabelSelector: strings.Join(labelSelectors, ","), - }) -} - -func (c *client) ApplyYAMLFiles(namespace string, yamlFiles ...string) error { - g, _ := errgroup.WithContext(context.TODO()) - for _, f := range removeEmptyFiles(yamlFiles) { - f := f - g.Go(func() error { - return c.applyYAMLFile(namespace, false, f) - }) - } - return g.Wait() -} - -func (c *client) ApplyYAMLFilesDryRun(namespace string, yamlFiles ...string) error { - g, _ := errgroup.WithContext(context.TODO()) - for _, f := range removeEmptyFiles(yamlFiles) { - f := f - g.Go(func() error { - return c.applyYAMLFile(namespace, true, f) - }) - } - return g.Wait() -} - -func (c *client) CreatePerRPCCredentials(_ context.Context, tokenNamespace, tokenServiceAccount string, audiences []string, - expirationSeconds int64) (credentials.PerRPCCredentials, error) { - return NewRPCCredentials(c, tokenNamespace, tokenServiceAccount, audiences, expirationSeconds, 60) -} - -func (c *client) UtilFactory() util.Factory { - return c.clientFactory -} - -// TODO once we drop Kubernetes 1.15 support we can drop all of this code in favor of Server Side Apply -// Following https://ymmt2005.hatenablog.com/entry/2020/04/14/An_example_of_using_dynamic_client_of_k8s.io/client-go -func (c *client) applyYAMLFile(namespace string, dryRun bool, file string) error { - // Create the options. - streams, _, stdout, stderr := genericclioptions.NewTestIOStreams() - flags := apply.NewApplyFlags(c.clientFactory, streams) - flags.DeleteFlags.FileNameFlags.Filenames = &[]string{file} - - cmd := apply.NewCmdApply("", c.clientFactory, streams) - opts, err := flags.ToOptions(cmd, "", nil) - if err != nil { - return err - } - opts.DynamicClient = c.dynamic - opts.DryRunVerifier = resource.NewQueryParamVerifier(c.dynamic, c.discoveryClient, resource.QueryParamDryRun) - opts.FieldValidationVerifier = resource.NewQueryParamVerifier(c.dynamic, c.clientFactory.OpenAPIGetter(), resource.QueryParamFieldValidation) - opts.FieldManager = fieldManager - if dryRun { - opts.DryRunStrategy = util.DryRunServer - } - - // allow for a success message operation to be specified at print time - opts.ToPrinter = func(operation string) (printers.ResourcePrinter, error) { - opts.PrintFlags.NamePrintFlags.Operation = operation - util.PrintFlagsWithDryRunStrategy(opts.PrintFlags, opts.DryRunStrategy) - return opts.PrintFlags.ToPrinter() - } - - if len(namespace) > 0 { - opts.Namespace = namespace - opts.EnforceNamespace = true - } else { - var err error - opts.Namespace, opts.EnforceNamespace, err = c.clientFactory.ToRawKubeConfigLoader().Namespace() - if err != nil { - return err - } - } - - opts.DeleteOptions = &kubectlDelete.DeleteOptions{ - DynamicClient: c.dynamic, - IOStreams: streams, - FilenameOptions: flags.DeleteFlags.FileNameFlags.ToOptions(), - } - - opts.OpenAPISchema, _ = c.clientFactory.OpenAPISchema() - - opts.Validator, err = c.clientFactory.Validator(metav1.FieldValidationStrict, opts.FieldValidationVerifier) - if err != nil { - return err - } - opts.Builder = c.clientFactory.NewBuilder() - opts.Mapper = c.mapper - - opts.PostProcessorFn = opts.PrintAndPrunePostProcessor() - - if err := opts.Run(); err != nil { - // Concatenate the stdout and stderr - s := stdout.String() + stderr.String() - return fmt.Errorf("%v: %s", err, s) - } - // If we are changing CRDs, invalidate the discovery client so future calls will not fail - if !dryRun { - f, _ := os.ReadFile(file) - if len(yml.SplitYamlByKind(string(f))[gvk.CustomResourceDefinition.Kind]) > 0 { - c.discoveryClient.Invalidate() - } - } - return nil -} - -func (c *client) DeleteYAMLFiles(namespace string, yamlFiles ...string) (err error) { - yamlFiles = removeEmptyFiles(yamlFiles) - - // Run each delete concurrently and collect the errors. - errs := make([]error, len(yamlFiles)) - g, _ := errgroup.WithContext(context.TODO()) - for i, f := range yamlFiles { - i, f := i, f - g.Go(func() error { - errs[i] = c.deleteFile(namespace, false, f) - return errs[i] - }) - } - _ = g.Wait() - return multierror.Append(nil, errs...).ErrorOrNil() -} - -func (c *client) DeleteYAMLFilesDryRun(namespace string, yamlFiles ...string) (err error) { - yamlFiles = removeEmptyFiles(yamlFiles) - - // Run each delete concurrently and collect the errors. - errs := make([]error, len(yamlFiles)) - g, _ := errgroup.WithContext(context.TODO()) - for i, f := range yamlFiles { - i, f := i, f - g.Go(func() error { - errs[i] = c.deleteFile(namespace, true, f) - return errs[i] - }) - } - _ = g.Wait() - return multierror.Append(nil, errs...).ErrorOrNil() -} - -func (c *client) deleteFile(namespace string, dryRun bool, file string) error { - // Create the options. - streams, _, stdout, stderr := genericclioptions.NewTestIOStreams() - - cmdNamespace, enforceNamespace, err := c.clientFactory.ToRawKubeConfigLoader().Namespace() - if err != nil { - return err - } - - if len(namespace) > 0 { - cmdNamespace = namespace - enforceNamespace = true - } - - fileOpts := resource.FilenameOptions{ - Filenames: []string{file}, - } - - opts := kubectlDelete.DeleteOptions{ - FilenameOptions: fileOpts, - CascadingStrategy: metav1.DeletePropagationBackground, - GracePeriod: -1, - IgnoreNotFound: true, - WaitForDeletion: true, - WarnClusterScope: enforceNamespace, - DynamicClient: c.dynamic, - DryRunVerifier: resource.NewQueryParamVerifier(c.dynamic, c.discoveryClient, resource.QueryParamDryRun), - IOStreams: streams, - } - if dryRun { - opts.DryRunStrategy = util.DryRunServer - } - - r := c.clientFactory.NewBuilder(). - Unstructured(). - ContinueOnError(). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, &fileOpts). - LabelSelectorParam(opts.LabelSelector). - FieldSelectorParam(opts.FieldSelector). - SelectAllParam(opts.DeleteAll). - AllNamespaces(opts.DeleteAllNamespaces). - Flatten(). - Do() - err = r.Err() - if err != nil { - return err - } - opts.Result = r - - opts.Mapper = c.mapper - - if err := opts.RunDelete(c.clientFactory); err != nil { - // Concatenate the stdout and stderr - s := stdout.String() + stderr.String() - return fmt.Errorf("%v: %s", err, s) - } - return nil -} - -func closeQuietly(c io.Closer) { - _ = c.Close() -} - -func removeEmptyFiles(files []string) []string { - out := make([]string, 0, len(files)) - for _, f := range files { - if !isEmptyFile(f) { - out = append(out, f) - } - } - return out -} - -func isEmptyFile(f string) bool { - fileInfo, err := os.Stat(f) - if err != nil { - return true - } - if fileInfo.Size() == 0 { - return true - } - return false -} - -// IstioScheme returns a scheme will all known Istio-related types added -var IstioScheme = istioScheme() - -// FakeIstioScheme is an IstioScheme that has List type registered. -var FakeIstioScheme = func() *runtime.Scheme { - s := istioScheme() - // Workaround https://github.com/kubernetes/kubernetes/issues/107823 - s.AddKnownTypeWithName(schema.GroupVersionKind{Group: "fake-metadata-client-group", Version: "v1", Kind: "List"}, &metav1.List{}) - return s -}() - -func istioScheme() *runtime.Scheme { - scheme := runtime.NewScheme() - utilruntime.Must(kubescheme.AddToScheme(scheme)) - utilruntime.Must(mcs.AddToScheme(scheme)) - utilruntime.Must(clientnetworkingalpha.AddToScheme(scheme)) - utilruntime.Must(clientnetworkingbeta.AddToScheme(scheme)) - utilruntime.Must(clientsecurity.AddToScheme(scheme)) - utilruntime.Must(clienttelemetry.AddToScheme(scheme)) - utilruntime.Must(clientextensions.AddToScheme(scheme)) - utilruntime.Must(gatewayapi.AddToScheme(scheme)) - utilruntime.Must(apis.AddToScheme(scheme)) - utilruntime.Must(apiextensionsv1.AddToScheme(scheme)) - return scheme -} - -func setServerInfoWithIstiodVersionInfo(serverInfo *version.BuildInfo, istioInfo string) { - versionParts := strings.Split(istioInfo, "-") - nParts := len(versionParts) - if nParts >= 3 { - // The format will be like 1.12.0-016bc46f4a5e0ef3fa135b3c5380ab7765467c1a-dirty-Modified - // version is '1.12.0' - // revision is '016bc46f4a5e0ef3fa135b3c5380ab7765467c1a-dirty' - // status is 'Modified' - serverInfo.Version = versionParts[0] - serverInfo.GitTag = serverInfo.Version - serverInfo.GitRevision = strings.Join(versionParts[1:nParts-1], "-") - serverInfo.BuildStatus = versionParts[nParts-1] - } else { - serverInfo.Version = istioInfo - } -} diff --git a/pkg/kube/client_config.go b/pkg/kube/client_config.go deleted file mode 100644 index 1e08d7503..000000000 --- a/pkg/kube/client_config.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/tools/clientcmd/api" -) - -const ( - contextName = "context0" - clusterName = "cluster0" - authInfoName = "authInfo0" -) - -var _ clientcmd.ClientConfig = &clientConfig{} - -// clientConfig is a utility that allows construction of a k8s ClientConfig from -// a k8s rest.Config -type clientConfig struct { - restConfig rest.Config -} - -// NewClientConfigForRestConfig creates a new k8s clientcmd.ClientConfig from the given rest.Config. -func NewClientConfigForRestConfig(restConfig *rest.Config) clientcmd.ClientConfig { - return &clientConfig{ - restConfig: *restConfig, - } -} - -func (c *clientConfig) RawConfig() (api.Config, error) { - cfg := api.Config{ - Kind: "Config", - APIVersion: "v1", - Preferences: api.Preferences{}, - Clusters: map[string]*api.Cluster{ - clusterName: newCluster(&c.restConfig), - }, - AuthInfos: map[string]*api.AuthInfo{ - authInfoName: newAuthInfo(&c.restConfig), - }, - Contexts: map[string]*api.Context{ - contextName: { - Cluster: clusterName, - AuthInfo: authInfoName, - }, - }, - CurrentContext: contextName, - } - - return cfg, nil -} - -func (c *clientConfig) ClientConfig() (*rest.Config, error) { - return c.copyRestConfig(), nil -} - -func (c *clientConfig) Namespace() (string, bool, error) { - return "default", false, nil -} - -func (c *clientConfig) ConfigAccess() clientcmd.ConfigAccess { - return nil -} - -func (c *clientConfig) copyRestConfig() *rest.Config { - out := c.restConfig - return &out -} - -func newAuthInfo(restConfig *rest.Config) *api.AuthInfo { - return &api.AuthInfo{ - ClientCertificate: restConfig.CertFile, - ClientCertificateData: restConfig.CertData, - ClientKey: restConfig.KeyFile, - ClientKeyData: restConfig.KeyData, - Token: restConfig.BearerToken, - TokenFile: restConfig.BearerTokenFile, - Impersonate: restConfig.Impersonate.UserName, - ImpersonateGroups: restConfig.Impersonate.Groups, - ImpersonateUserExtra: restConfig.Impersonate.Extra, - Username: restConfig.Username, - Password: restConfig.Password, - AuthProvider: restConfig.AuthProvider, - Exec: restConfig.ExecProvider, - } -} - -func newCluster(restConfig *rest.Config) *api.Cluster { - return &api.Cluster{ - Server: restConfig.Host, - TLSServerName: restConfig.ServerName, - InsecureSkipTLSVerify: restConfig.Insecure, - CertificateAuthority: restConfig.CAFile, - CertificateAuthorityData: restConfig.CAData, - } -} diff --git a/pkg/kube/client_factory.go b/pkg/kube/client_factory.go deleted file mode 100644 index 8daa31414..000000000 --- a/pkg/kube/client_factory.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "sync" -) - -import ( - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/cli-runtime/pkg/resource" - "k8s.io/client-go/discovery" - "k8s.io/client-go/discovery/cached/memory" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - "k8s.io/client-go/restmapper" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/kubectl/pkg/cmd/util" - "k8s.io/kubectl/pkg/util/openapi" - "k8s.io/kubectl/pkg/validation" -) - -var _ util.Factory = &clientFactory{} - -// clientFactory implements the kubectl util.Factory, which is provides access to various k8s clients. -type clientFactory struct { - clientConfig clientcmd.ClientConfig - factory util.Factory - - mapperOnce sync.Once - mapper meta.RESTMapper - expander meta.RESTMapper - - discoveryOnce sync.Once - discoveryClient discovery.CachedDiscoveryInterface -} - -// newClientFactory creates a new util.Factory from the given clientcmd.ClientConfig. -func newClientFactory(clientConfig clientcmd.ClientConfig) util.Factory { - out := &clientFactory{ - clientConfig: clientConfig, - } - - out.factory = util.NewFactory(out) - return out -} - -func (c *clientFactory) ToRESTConfig() (*rest.Config, error) { - restConfig, err := c.clientConfig.ClientConfig() - if err != nil { - return nil, err - } - return SetRestDefaults(restConfig), nil -} - -func (c *clientFactory) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) { - c.discoveryOnce.Do(func() { - restConfig, err := c.ToRESTConfig() - if err != nil { - return - } - d, err := discovery.NewDiscoveryClientForConfig(restConfig) - if err != nil { - return - } - c.discoveryClient = memory.NewMemCacheClient(d) - }) - return c.discoveryClient, nil -} - -func (c *clientFactory) ToRESTMapper() (meta.RESTMapper, error) { - discoveryClient, err := c.ToDiscoveryClient() - if err != nil { - return nil, err - } - c.mapperOnce.Do(func() { - c.mapper = restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient) - c.expander = restmapper.NewShortcutExpander(c.mapper, discoveryClient) - }) - return c.expander, nil -} - -func (c *clientFactory) ToRawKubeConfigLoader() clientcmd.ClientConfig { - return c.clientConfig -} - -func (c *clientFactory) DynamicClient() (dynamic.Interface, error) { - restConfig, err := c.ToRESTConfig() - if err != nil { - return nil, err - } - - return dynamic.NewForConfig(restConfig) -} - -func (c *clientFactory) KubernetesClientSet() (*kubernetes.Clientset, error) { - restConfig, err := c.ToRESTConfig() - if err != nil { - return nil, err - } - return kubernetes.NewForConfig(restConfig) -} - -func (c *clientFactory) RESTClient() (*rest.RESTClient, error) { - return c.factory.RESTClient() -} - -func (c *clientFactory) NewBuilder() *resource.Builder { - return c.factory.NewBuilder() -} - -func (c *clientFactory) ClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) { - return c.factory.ClientForMapping(mapping) -} - -func (c *clientFactory) UnstructuredClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) { - return c.factory.UnstructuredClientForMapping(mapping) -} - -func (c *clientFactory) Validator(validationDirective string, verifier *resource.QueryParamVerifier) (validation.Schema, error) { - return c.factory.Validator(validationDirective, verifier) -} - -func (c *clientFactory) OpenAPISchema() (openapi.Resources, error) { - return c.factory.OpenAPISchema() -} - -func (c *clientFactory) OpenAPIGetter() discovery.OpenAPISchemaInterface { - return c.factory.OpenAPIGetter() -} diff --git a/pkg/kube/client_test.go b/pkg/kube/client_test.go deleted file mode 100644 index 752cc5dca..000000000 --- a/pkg/kube/client_test.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "context" - "reflect" - "testing" -) - -import ( - version2 "istio.io/pkg/version" -) - -const istioNamespace = "dubbo-system" - -func TestMockClient_GetIstioVersions(t *testing.T) { - tests := []struct { - version string - expected version2.BuildInfo - }{ - { - version: "1.12.0-016bc46f4a5e0ef3fa135b3c5380ab7765467c1a-dirty-Modified", - expected: version2.BuildInfo{ - Version: "1.12.0", - GitRevision: "016bc46f4a5e0ef3fa135b3c5380ab7765467c1a-dirty", - GolangVersion: "", - BuildStatus: "Modified", - GitTag: "1.12.0", - }, - }, - { - version: "1.12.0-016bc46f4a5e0ef3fa135b3c5380ab7765467c1a-Clean", - expected: version2.BuildInfo{ - Version: "1.12.0", - GitRevision: "016bc46f4a5e0ef3fa135b3c5380ab7765467c1a", - GolangVersion: "", - BuildStatus: "Clean", - GitTag: "1.12.0", - }, - }, - { - version: "1.12.0", - expected: version2.BuildInfo{ - Version: "1.12.0", - }, - }, - } - for _, test := range tests { - mc := MockClient{IstiodVersion: test.version} - version, err := mc.GetIstioVersions(context.TODO(), istioNamespace) - if err != nil { - t.Fatal(err) - } - if version == nil { - t.Fatal("no version obtained") - } - for _, info := range *version { - if !reflect.DeepEqual(info.Info, test.expected) { - t.Fatal("the version result is not the same as the expected one") - } - } - } -} diff --git a/pkg/kube/configmapwatcher/configmapwatcher.go b/pkg/kube/configmapwatcher/configmapwatcher.go deleted file mode 100644 index 2093d6dc2..000000000 --- a/pkg/kube/configmapwatcher/configmapwatcher.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configmapwatcher - -import ( - "fmt" - "time" -) - -import ( - "go.uber.org/atomic" - "istio.io/pkg/log" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/informers" - informersv1 "k8s.io/client-go/informers/core/v1" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/controllers" -) - -// Controller watches a ConfigMap and calls the given callback when the ConfigMap changes. -// The ConfigMap is passed to the callback, or nil if it doesn't exist. -type Controller struct { - informer informersv1.ConfigMapInformer - queue controllers.Queue - - configMapNamespace string - configMapName string - callback func(*v1.ConfigMap) - - hasSynced atomic.Bool -} - -// NewController returns a new ConfigMap watcher controller. -func NewController(client kube.Client, namespace, name string, callback func(*v1.ConfigMap)) *Controller { - c := &Controller{ - configMapNamespace: namespace, - configMapName: name, - callback: callback, - } - - // Although using a separate informer factory isn't ideal, - // this does so to limit watching to only the specified ConfigMap. - c.informer = informers.NewSharedInformerFactoryWithOptions(client.Kube(), 12*time.Hour, - informers.WithNamespace(namespace), - informers.WithTweakListOptions(func(listOptions *metav1.ListOptions) { - listOptions.FieldSelector = fields.OneTermEqualSelector(metav1.ObjectNameField, name).String() - })). - Core().V1().ConfigMaps() - - c.queue = controllers.NewQueue("configmap "+name, controllers.WithReconciler(c.processItem)) - c.informer.Informer().AddEventHandler(controllers.FilteredObjectSpecHandler(c.queue.AddObject, func(o controllers.Object) bool { - // Filter out configmaps - return o.GetName() == name && o.GetNamespace() == namespace - })) - - return c -} - -func (c *Controller) Run(stop <-chan struct{}) { - go c.informer.Informer().Run(stop) - if !cache.WaitForCacheSync(stop, c.informer.Informer().HasSynced) { - log.Error("failed to wait for cache sync") - return - } - c.queue.Run(stop) -} - -// HasSynced returns whether the underlying cache has synced and the callback has been called at least once. -func (c *Controller) HasSynced() bool { - return c.queue.HasSynced() -} - -func (c *Controller) processItem(name types.NamespacedName) error { - cm, err := c.informer.Lister().ConfigMaps(c.configMapNamespace).Get(c.configMapName) - if err != nil { - if !errors.IsNotFound(err) { - return fmt.Errorf("error fetching object %s error: %v", c.configMapName, err) - } - cm = nil - } - c.callback(cm) - - c.hasSynced.Store(true) - return nil -} diff --git a/pkg/kube/configmapwatcher/configmapwatcher_test.go b/pkg/kube/configmapwatcher/configmapwatcher_test.go deleted file mode 100644 index af0148147..000000000 --- a/pkg/kube/configmapwatcher/configmapwatcher_test.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configmapwatcher - -import ( - "context" - "fmt" - "sync" - "testing" - "time" -) - -import ( - . "github.com/onsi/gomega" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -const ( - configMapNamespace string = "dubbo-system" - configMapName string = "watched" -) - -func makeConfigMap(name, resourceVersion string) *v1.ConfigMap { - return &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: configMapNamespace, - Name: name, - ResourceVersion: resourceVersion, - }, - Data: map[string]string{ - "mesh": "trustDomain: cluster.local", - }, - } -} - -var ( - mu sync.Mutex - called bool - newCM *v1.ConfigMap -) - -func callback(cm *v1.ConfigMap) { - mu.Lock() - defer mu.Unlock() - called = true - newCM = cm -} - -func getCalled() bool { - mu.Lock() - defer mu.Unlock() - return called -} - -func getCM() *v1.ConfigMap { - mu.Lock() - defer mu.Unlock() - return newCM -} - -func resetCalled() { - called = false - newCM = nil -} - -func Test_ConfigMapWatcher(t *testing.T) { - client := kube.NewFakeClient() - cm := makeConfigMap(configMapName, "1") - cm1 := makeConfigMap(configMapName, "2") - cm2 := makeConfigMap("not-watched", "1") - steps := []struct { - added *v1.ConfigMap - updated *v1.ConfigMap - deleted *v1.ConfigMap - expectCalled bool - expectCM *v1.ConfigMap - }{ - {added: cm2}, - {added: cm, expectCalled: true, expectCM: cm}, - {updated: cm}, - {updated: cm1, expectCalled: true, expectCM: cm1}, - {deleted: cm1, expectCalled: true}, - {deleted: cm2}, - } - - stop := make(chan struct{}) - c := NewController(client, configMapNamespace, configMapName, callback) - go c.Run(stop) - cache.WaitForCacheSync(stop, c.HasSynced) - - cms := client.Kube().CoreV1().ConfigMaps(configMapNamespace) - for i, step := range steps { - resetCalled() - - t.Run(fmt.Sprintf("[%v]", i), func(t *testing.T) { - g := NewWithT(t) - - switch { - case step.added != nil: - _, err := cms.Create(context.TODO(), step.added, metav1.CreateOptions{}) - g.Expect(err).Should(BeNil()) - case step.updated != nil: - _, err := cms.Update(context.TODO(), step.updated, metav1.UpdateOptions{}) - g.Expect(err).Should(BeNil()) - case step.deleted != nil: - g.Expect(cms.Delete(context.TODO(), step.deleted.Name, metav1.DeleteOptions{})). - Should(Succeed()) - } - - if step.expectCalled { - g.Eventually(getCalled, time.Second).Should(Equal(true)) - g.Eventually(getCM, time.Second).Should(Equal(newCM)) - } else { - g.Consistently(getCalled).Should(Equal(false)) - } - }) - } -} diff --git a/pkg/kube/controllers/common.go b/pkg/kube/controllers/common.go deleted file mode 100644 index 5fa371f55..000000000 --- a/pkg/kube/controllers/common.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controllers - -import ( - "fmt" -) - -import ( - istiolog "istio.io/pkg/log" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" -) - -var log = istiolog.RegisterScope("controllers", "common controller logic", 0) - -// Object is a union of runtime + meta objects. Essentially every k8s object meets this interface. -// and certainly all that we care about. -type Object interface { - metav1.Object - runtime.Object -} - -// UnstructuredToGVR extracts the GVR of an unstructured resource. This is useful when using dynamic -// clients. -func UnstructuredToGVR(u unstructured.Unstructured) (schema.GroupVersionResource, error) { - res := schema.GroupVersionResource{} - gv, err := schema.ParseGroupVersion(u.GetAPIVersion()) - if err != nil { - return res, err - } - - gk := config.GroupVersionKind{ - Group: gv.Group, - Version: gv.Version, - Kind: u.GetKind(), - } - found, ok := collections.All.FindByGroupVersionKind(gk) - if !ok { - return res, fmt.Errorf("unknown gvk: %v", gk) - } - return schema.GroupVersionResource{ - Group: gk.Group, - Version: gk.Version, - Resource: found.Resource().Plural(), - }, nil -} - -// ObjectToGVR extracts the GVR of an unstructured resource. This is useful when using dynamic -// clients. -func ObjectToGVR(u Object) (schema.GroupVersionResource, error) { - gvk := u.GetObjectKind().GroupVersionKind() - - gk := config.GroupVersionKind{ - Group: gvk.Group, - Version: gvk.Version, - Kind: gvk.Kind, - } - found, ok := collections.All.FindByGroupVersionKind(gk) - if !ok { - return schema.GroupVersionResource{}, fmt.Errorf("unknown gvk: %v", gk) - } - return schema.GroupVersionResource{ - Group: gk.Group, - Version: gk.Version, - Resource: found.Resource().Plural(), - }, nil -} - -// EnqueueForParentHandler returns a handler that will enqueue the parent (by ownerRef) resource -func EnqueueForParentHandler(q Queue, kind config.GroupVersionKind) func(obj Object) { - handler := func(obj Object) { - for _, ref := range obj.GetOwnerReferences() { - refGV, err := schema.ParseGroupVersion(ref.APIVersion) - if err != nil { - log.Errorf("could not parse OwnerReference api version %q: %v", ref.APIVersion, err) - continue - } - if refGV == kind.Kubernetes().GroupVersion() { - // We found a parent we care about, add it to the queue - q.Add(types.NamespacedName{ - Namespace: obj.GetNamespace(), - Name: obj.GetName(), - }) - } - } - } - return handler -} - -// ObjectHandler returns a handler that will act on the latest version of an object -// This means Add/Update/Delete are all handled the same and are just used to trigger reconciling. -func ObjectHandler(handler func(o Object)) cache.ResourceEventHandler { - h := func(obj interface{}) { - o := extractObject(obj) - if o == nil { - return - } - handler(o) - } - return cache.ResourceEventHandlerFuncs{ - AddFunc: h, - UpdateFunc: func(oldObj, newObj interface{}) { - h(newObj) - }, - DeleteFunc: h, - } -} - -// FilteredObjectHandler returns a handler that will act on the latest version of an object -// This means Add/Update/Delete are all handled the same and are just used to trigger reconciling. -// If filters are set, returning 'false' will exclude the event. For Add and Deletes, the filter will be based -// on the new or old item. For updates, the item will be handled if either the new or the old object is updated. -func FilteredObjectHandler(handler func(o Object), filter func(o Object) bool) cache.ResourceEventHandler { - return filteredObjectHandler(handler, false, filter) -} - -// FilteredObjectSpecHandler returns a handler that will act on the latest version of an object -// This means Add/Update/Delete are all handled the same and are just used to trigger reconciling. -// Unlike FilteredObjectHandler, the handler is only trigger when the resource spec changes (ie resourceVersion) -// If filters are set, returning 'false' will exclude the event. For Add and Deletes, the filter will be based -// on the new or old item. For updates, the item will be handled if either the new or the old object is updated. -func FilteredObjectSpecHandler(handler func(o Object), filter func(o Object) bool) cache.ResourceEventHandler { - return filteredObjectHandler(handler, true, filter) -} - -func filteredObjectHandler(handler func(o Object), onlyIncludeSpecChanges bool, filter func(o Object) bool) cache.ResourceEventHandler { - single := func(obj interface{}) { - o := extractObject(obj) - if o == nil { - return - } - if !filter(o) { - return - } - handler(o) - } - return cache.ResourceEventHandlerFuncs{ - AddFunc: single, - UpdateFunc: func(oldInterface, newInterace interface{}) { - oldObj := extractObject(oldInterface) - if oldObj == nil { - return - } - newObj := extractObject(newInterace) - if newObj == nil { - return - } - if onlyIncludeSpecChanges && oldObj.GetResourceVersion() == newObj.GetResourceVersion() { - return - } - newer := filter(newObj) - older := filter(oldObj) - if !newer && !older { - return - } - handler(newObj) - }, - DeleteFunc: single, - } -} - -func extractObject(obj interface{}) Object { - o, ok := obj.(Object) - if !ok { - tombstone, ok := obj.(cache.DeletedFinalStateUnknown) - if !ok { - log.Errorf("couldn't get object from tombstone %+v", obj) - return nil - } - o, ok = tombstone.Obj.(Object) - if !ok { - log.Errorf("tombstone contained object that is not an object %+v", obj) - return nil - } - } - return o -} - -// IgnoreNotFound returns nil on NotFound errors. -// All other values that are not NotFound errors or nil are returned unmodified. -func IgnoreNotFound(err error) error { - if apierrors.IsNotFound(err) { - return nil - } - return err -} diff --git a/pkg/kube/controllers/queue.go b/pkg/kube/controllers/queue.go deleted file mode 100644 index c9b82b192..000000000 --- a/pkg/kube/controllers/queue.go +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controllers - -import ( - "go.uber.org/atomic" - istiolog "istio.io/pkg/log" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/util/workqueue" -) - -// Queue defines an abstraction around Kubernetes' workqueue. -// Items enqueued are deduplicated; this generally means relying on ordering of events in the queue is not feasible. -type Queue struct { - queue workqueue.RateLimitingInterface - initialSync *atomic.Bool - name string - maxAttempts int - workFn func(key interface{}) error - log *istiolog.Scope -} - -// WithName sets a name for the queue. This is used for logging -func WithName(name string) func(q *Queue) { - return func(q *Queue) { - q.name = name - } -} - -// WithRateLimiter allows defining a custom rate limitter for the queue -func WithRateLimiter(r workqueue.RateLimiter) func(q *Queue) { - return func(q *Queue) { - q.queue = workqueue.NewRateLimitingQueue(r) - } -} - -// WithMaxAttempts allows defining a custom max attempts for the queue. If not set, items will not be retried -func WithMaxAttempts(n int) func(q *Queue) { - return func(q *Queue) { - q.maxAttempts = n - } -} - -// WithReconciler defines the handler function to handle items in the queue. -func WithReconciler(f func(key types.NamespacedName) error) func(q *Queue) { - return func(q *Queue) { - q.workFn = func(key interface{}) error { - return f(key.(types.NamespacedName)) - } - } -} - -// WithGenericReconciler defines the handler function to handle items in the queue that can handle any type -func WithGenericReconciler(f func(key interface{}) error) func(q *Queue) { - return func(q *Queue) { - q.workFn = func(key interface{}) error { - return f(key) - } - } -} - -// NewQueue creates a new queue -func NewQueue(name string, options ...func(*Queue)) Queue { - q := Queue{ - name: name, - initialSync: atomic.NewBool(false), - } - for _, o := range options { - o(&q) - } - if q.queue == nil { - q.queue = workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()) - } - q.log = log.WithLabels("controller", q.name) - return q -} - -// Add an item to the queue. -func (q Queue) Add(item interface{}) { - q.queue.Add(item) -} - -// AddObject takes an Object and adds the types.NamespacedName associated. -func (q Queue) AddObject(obj Object) { - q.queue.Add(types.NamespacedName{ - Namespace: obj.GetNamespace(), - Name: obj.GetName(), - }) -} - -// Run the queue. This is synchronous, so should typically be called in a goroutine. -func (q Queue) Run(stop <-chan struct{}) { - defer q.queue.ShutDown() - q.log.Infof("starting") - q.queue.Add(defaultSyncSignal) - done := make(chan struct{}) - go func() { - // Process updates until we return false, which indicates the queue is terminated - for q.processNextItem() { - } - close(done) - }() - select { - case <-stop: - case <-done: - } - q.log.Infof("stopped") -} - -// syncSignal defines a dummy signal that is enqueued when .Run() is called. This allows us to detect -// when we have processed all items added to the queue prior to Run(). -type syncSignal struct{} - -// defaultSyncSignal is a singleton instanceof syncSignal. -var defaultSyncSignal = syncSignal{} - -// HasSynced returns true if the queue has 'synced'. A synced queue has started running and has -// processed all events that were added prior to Run() being called Warning: these items will be -// processed at least once, but may have failed. -func (q Queue) HasSynced() bool { - return q.initialSync.Load() -} - -// processNextItem is the main workFn loop for the queue -func (q Queue) processNextItem() bool { - // Wait until there is a new item in the working queue - key, quit := q.queue.Get() - if quit { - // We are done, signal to exit the queue - return false - } - - // We got the sync signal. This is not a real event, so we exit early after signaling we are synced - if key == defaultSyncSignal { - q.log.Debugf("synced") - q.initialSync.Store(true) - return true - } - - q.log.Debugf("handling update: %v", key) - - // 'Done marks item as done processing' - should be called at the end of all processing - defer q.queue.Done(key) - - err := q.workFn(key) - if err != nil { - if q.queue.NumRequeues(key) < q.maxAttempts { - q.log.Errorf("error handling %v, retrying: %v", key, err) - q.queue.AddRateLimited(key) - // Return early, so we do not call Forget(), allowing the rate limiting to backoff - return true - } - q.log.Errorf("error handling %v, and retry budget exceeded: %v", key, err) - } - // 'Forget indicates that an item is finished being retried.' - should be called whenever we do not want to backoff on this key. - q.queue.Forget(key) - return true -} diff --git a/pkg/kube/controllers/queue_test.go b/pkg/kube/controllers/queue_test.go deleted file mode 100644 index 22e9cb578..000000000 --- a/pkg/kube/controllers/queue_test.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controllers - -import ( - "testing" -) - -import ( - "go.uber.org/atomic" - "k8s.io/apimachinery/pkg/types" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -func TestQueue(t *testing.T) { - handles := atomic.NewInt32(0) - q := NewQueue("custom", WithReconciler(func(key types.NamespacedName) error { - handles.Inc() - return nil - })) - stop := make(chan struct{}) - t.Cleanup(func() { - close(stop) - }) - q.Add(types.NamespacedName{Name: "something"}) - go q.Run(stop) - retry.UntilOrFail(t, q.HasSynced) - if got := handles.Load(); got != 1 { - t.Fatalf("expected 1 handle, got %v", got) - } -} diff --git a/pkg/kube/fakemirror.go b/pkg/kube/fakemirror.go deleted file mode 100644 index 21012579f..000000000 --- a/pkg/kube/fakemirror.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "context" - "reflect" -) - -import ( - gogoproto "github.com/gogo/protobuf/proto" - "istio.io/pkg/log" - corev1 "k8s.io/api/core/v1" - discoveryv1 "k8s.io/api/discovery/v1" - discoveryv1beta1 "k8s.io/api/discovery/v1beta1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/queue" -) - -type convertFn func(obj interface{}) metav1.Common - -type untypedMirror struct { - createClient reflect.Value - queue queue.Instance - convertRes convertFn -} - -func mirrorTo(q queue.Instance, createClient interface{}, convert convertFn) *untypedMirror { - // TODO go 1.18 generics may help avoid reflection - untyped := reflect.ValueOf(createClient) - if untyped.Type().Kind() != reflect.Func { - panic("non-func passed to mirrorTo") - } - return &untypedMirror{ - createClient: untyped, - queue: q, - convertRes: convert, - } -} - -func (c *untypedMirror) OnAdd(obj interface{}) { - meta := obj.(metav1.Object) - res := c.convertRes(obj) - if res == nil { - log.Warnf("failed to mirror resource %v/%v", meta.GetName(), meta.GetName()) - return - } - log.Debugf("Mirroring ADD %s/%s", meta.GetName(), meta.GetName()) - c.queue.Push(func() error { - return c.Create(meta.GetNamespace(), res) - }) -} - -func (c *untypedMirror) OnUpdate(_, obj interface{}) { - meta := obj.(metav1.Object) - res := c.convertRes(obj) - if res == nil { - log.Warnf("failed to mirror resource %v/%v", meta.GetName(), meta.GetName()) - return - } - res.SetResourceVersion("") - log.Debugf("Mirroring UPDATE %s/%s", meta.GetName(), meta.GetName()) - c.queue.Push(func() error { - return c.Update(meta.GetNamespace(), res) - }) -} - -func (c *untypedMirror) OnDelete(obj interface{}) { - meta := obj.(metav1.Object) - log.Debugf("Mirroring DELETE %s/%s", meta.GetName(), meta.GetName()) - c.queue.Push(func() error { - return c.Delete(meta.GetNamespace(), meta.GetName()) - }) -} - -func (c *untypedMirror) do(ns string, method string, args ...interface{}) []reflect.Value { - return c.createClient.Call(argValues(ns))[0].MethodByName(method).Call(argValues(args...)) -} - -func (c *untypedMirror) Create(ns string, obj interface{}) error { - ret := c.do(ns, "Create", context.TODO(), obj, metav1.CreateOptions{}) - err, _ := ret[1].Interface().(error) - return err -} - -func (c *untypedMirror) Update(ns string, obj interface{}) error { - ret := c.do(ns, "Update", context.TODO(), obj, metav1.UpdateOptions{}) - err, _ := ret[1].Interface().(error) - return err -} - -func (c *untypedMirror) Delete(ns string, name string) error { - ret := c.do(ns, "Delete", context.TODO(), name, metav1.DeleteOptions{}) - err, _ := ret[0].Interface().(error) - return err -} - -func argValues(args ...interface{}) []reflect.Value { - out := make([]reflect.Value, len(args)) - for i, arg := range args { - out[i] = reflect.ValueOf(arg) - } - return out -} - -func mirrorResource(q queue.Instance, from cache.SharedIndexInformer, toClientFunc interface{}, convertFn convertFn) { - from.AddEventHandler(mirrorTo(q, toClientFunc, convertFn)) -} - -func endpointSliceV1toV1beta1(obj interface{}) metav1.Common { - in, ok := obj.(*discoveryv1.EndpointSlice) - if !ok { - return nil - } - marshaled, err := gogoproto.Marshal(in) - if err != nil { - return nil - } - out := &discoveryv1beta1.EndpointSlice{} - if err := gogoproto.Unmarshal(marshaled, out); err != nil { - return nil - } - for i, endpoint := range out.Endpoints { - endpoint.Topology = in.Endpoints[i].DeprecatedTopology - if in.Endpoints[i].Zone != nil { - // not sure if this is 100% accurate - endpoint.Topology[corev1.LabelTopologyRegion] = *in.Endpoints[i].Zone - endpoint.Topology[corev1.LabelTopologyZone] = *in.Endpoints[i].Zone - } - out.Endpoints[i] = endpoint - } - return out -} diff --git a/pkg/kube/inject/app_probe.go b/pkg/kube/inject/app_probe.go deleted file mode 100644 index a3655f3b7..000000000 --- a/pkg/kube/inject/app_probe.go +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package inject implements kube-inject or webhoook autoinject feature to inject sidecar. -// This file is focused on rewriting Kubernetes app probers to support mutual TLS. -package inject - -import ( - "encoding/json" - "strconv" -) - -import ( - "istio.io/api/annotation" - "istio.io/pkg/log" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/util/intstr" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/status" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" -) - -// ShouldRewriteAppHTTPProbers returns if we should rewrite apps' probers config. -func ShouldRewriteAppHTTPProbers(annotations map[string]string, specSetting bool) bool { - if annotations != nil { - if value, ok := annotations[annotation.SidecarRewriteAppHTTPProbers.Name]; ok { - if isSetInAnnotation, err := strconv.ParseBool(value); err == nil { - return isSetInAnnotation - } - } - } - return specSetting -} - -// FindSidecar returns the pointer to the first container whose name matches the "istio-proxy". -func FindSidecar(containers []corev1.Container) *corev1.Container { - return FindContainer(ProxyContainerName, containers) -} - -// FindContainer returns the pointer to the first container whose name matches. -func FindContainer(name string, containers []corev1.Container) *corev1.Container { - for i := range containers { - if containers[i].Name == name { - return &containers[i] - } - } - return nil -} - -// convertAppProber returns an overwritten `Probe` for pilot agent to take over. -func convertAppProber(probe *corev1.Probe, newURL string, statusPort int) *corev1.Probe { - if probe == nil { - return nil - } - if probe.HTTPGet != nil { - return convertAppProberHTTPGet(probe, newURL, statusPort) - } else if probe.TCPSocket != nil && features.RewriteTCPProbes { - return convertAppProberTCPSocket(probe, newURL, statusPort) - } else if probe.GRPC != nil { - return convertAppProberGRPC(probe, newURL, statusPort) - } - - return nil -} - -// convertAppProberHTTPGet returns an overwritten `Probe` (HttpGet) for pilot agent to take over. -func convertAppProberHTTPGet(probe *corev1.Probe, newURL string, statusPort int) *corev1.Probe { - p := probe.DeepCopy() - // Change the application container prober config. - p.HTTPGet.Port = intstr.FromInt(statusPort) - p.HTTPGet.Path = newURL - // For HTTPS prober, we change to HTTP, - // and pilot agent uses https to request application prober endpoint. - // Kubelet -> HTTP -> Pilot Agent -> HTTPS -> Application - if p.HTTPGet.Scheme == corev1.URISchemeHTTPS { - p.HTTPGet.Scheme = corev1.URISchemeHTTP - } - return p -} - -// convertAppProberTCPSocket returns an overwritten `Probe` (TcpSocket) for pilot agent to take over. -func convertAppProberTCPSocket(probe *corev1.Probe, newURL string, statusPort int) *corev1.Probe { - p := probe.DeepCopy() - // the sidecar intercepts all tcp connections, so we change it to a HTTP probe and the sidecar will check tcp - p.HTTPGet = &corev1.HTTPGetAction{} - p.HTTPGet.Port = intstr.FromInt(statusPort) - p.HTTPGet.Path = newURL - - p.TCPSocket = nil - return p -} - -// convertAppProberGRPC returns an overwritten `Probe` (gRPC) for pilot agent to take over. -func convertAppProberGRPC(probe *corev1.Probe, newURL string, statusPort int) *corev1.Probe { - p := probe.DeepCopy() - // the sidecar intercepts all gRPC connections, so we change it to a HTTP probe and the sidecar will check gRPC - p.HTTPGet = &corev1.HTTPGetAction{} - p.HTTPGet.Port = intstr.FromInt(statusPort) - p.HTTPGet.Path = newURL - // For gRPC prober, we change to HTTP, - // and pilot agent uses gRPC to request application prober endpoint. - // Kubelet -> HTTP -> Pilot Agent -> gRPC -> Application - p.GRPC = nil - return p -} - -type KubeAppProbers map[string]*Prober - -// Prober represents a single container prober -type Prober struct { - HTTPGet *corev1.HTTPGetAction `json:"httpGet,omitempty"` - TCPSocket *corev1.TCPSocketAction `json:"tcpSocket,omitempty"` - GRPC *corev1.GRPCAction `json:"grpc,omitempty"` - TimeoutSeconds int32 `json:"timeoutSeconds,omitempty"` -} - -// DumpAppProbers returns a json encoded string as `status.KubeAppProbers`. -// Also update the probers so that all usages of named port will be resolved to integer. -func DumpAppProbers(podSpec *corev1.PodSpec, targetPort int32) string { - out := KubeAppProbers{} - updateNamedPort := func(p *Prober, portMap map[string]int32) *Prober { - if p == nil { - return nil - } - if p.GRPC != nil { - // don't need to update for gRPC probe port as it only supports integer - return p - } - if p.HTTPGet == nil && p.TCPSocket == nil { - return nil - } - - var probePort *intstr.IntOrString - if p.HTTPGet != nil { - probePort = &p.HTTPGet.Port - } else { - probePort = &p.TCPSocket.Port - } - - if probePort.Type == intstr.String { - port, exists := portMap[probePort.StrVal] - if !exists { - return nil - } - *probePort = intstr.FromInt(int(port)) - } else if probePort.IntVal == targetPort { - // Already is rewritten - return nil - } - return p - } - for _, c := range podSpec.Containers { - if c.Name == ProxyContainerName { - continue - } - readyz, livez, startupz := status.FormatProberURL(c.Name) - portMap := map[string]int32{} - for _, p := range c.Ports { - if p.Name != "" { - portMap[p.Name] = p.ContainerPort - } - } - if h := updateNamedPort(kubeProbeToInternalProber(c.ReadinessProbe), portMap); h != nil { - out[readyz] = h - } - if h := updateNamedPort(kubeProbeToInternalProber(c.LivenessProbe), portMap); h != nil { - out[livez] = h - } - if h := updateNamedPort(kubeProbeToInternalProber(c.StartupProbe), portMap); h != nil { - out[startupz] = h - } - - } - // prevent generate '{}' - if len(out) == 0 { - return "" - } - b, err := json.Marshal(out) - if err != nil { - log.Errorf("failed to serialize the app prober config %v", err) - return "" - } - return string(b) -} - -// patchRewriteProbe generates the patch for webhook. -func patchRewriteProbe(annotations map[string]string, pod *corev1.Pod, defaultPort int32) { - statusPort := int(defaultPort) - if v, f := annotations[annotation.SidecarStatusPort.Name]; f { - p, err := strconv.Atoi(v) - if err != nil { - log.Errorf("Invalid annotation %v=%v: %v", annotation.SidecarStatusPort.Name, v, err) - } - statusPort = p - } - for i, c := range pod.Spec.Containers { - // Skip sidecar container. - if c.Name == ProxyContainerName { - continue - } - readyz, livez, startupz := status.FormatProberURL(c.Name) - if probePatch := convertAppProber(c.ReadinessProbe, readyz, statusPort); probePatch != nil { - c.ReadinessProbe = probePatch - } - if probePatch := convertAppProber(c.LivenessProbe, livez, statusPort); probePatch != nil { - c.LivenessProbe = probePatch - } - if probePatch := convertAppProber(c.StartupProbe, startupz, statusPort); probePatch != nil { - c.StartupProbe = probePatch - } - pod.Spec.Containers[i] = c - } -} - -// kubeProbeToInternalProber converts a Kubernetes Probe to an Istio internal Prober -func kubeProbeToInternalProber(probe *corev1.Probe) *Prober { - if probe == nil { - return nil - } - - if probe.HTTPGet != nil { - return &Prober{ - HTTPGet: probe.HTTPGet, - TimeoutSeconds: probe.TimeoutSeconds, - } - } - - if probe.TCPSocket != nil && features.RewriteTCPProbes { - return &Prober{ - TCPSocket: probe.TCPSocket, - TimeoutSeconds: probe.TimeoutSeconds, - } - } - - if probe.GRPC != nil { - return &Prober{ - GRPC: probe.GRPC, - TimeoutSeconds: probe.TimeoutSeconds, - } - } - - return nil -} diff --git a/pkg/kube/inject/app_probe_test.go b/pkg/kube/inject/app_probe_test.go deleted file mode 100644 index 96618811c..000000000 --- a/pkg/kube/inject/app_probe_test.go +++ /dev/null @@ -1,370 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package inject - -import ( - "reflect" - "testing" -) - -import ( - "istio.io/api/annotation" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/util/intstr" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -func TestFindSidecar(t *testing.T) { - proxy := corev1.Container{Name: "istio-proxy"} - app := corev1.Container{Name: "app"} - for _, tc := range []struct { - name string - containers []corev1.Container - index int - }{ - {"only-sidecar", []corev1.Container{proxy}, 0}, - {"app-and-sidecar", []corev1.Container{app, proxy}, 1}, - {"no-sidecar", []corev1.Container{app}, -1}, - } { - got := FindSidecar(tc.containers) - var want *corev1.Container - if tc.index == -1 { - want = nil - } else { - want = &tc.containers[tc.index] - } - if got != want { - t.Errorf("[%v] failed, want %v, got %v", tc.name, want, got) - } - } -} - -func TestShouldRewriteAppHTTPProbers(t *testing.T) { - for _, tc := range []struct { - name string - specSetting bool - annotations map[string]string - expected bool - }{ - { - name: "RewriteAppHTTPProbe-set-in-annotations", - specSetting: false, - annotations: nil, - expected: false, - }, - { - name: "RewriteAppHTTPProbe-set-in-annotations", - specSetting: true, - annotations: nil, - expected: true, - }, - { - name: "RewriteAppHTTPProbe-set-in-sidecar-injection-spec", - specSetting: false, - annotations: map[string]string{}, - expected: false, - }, - { - name: "RewriteAppHTTPProbe-set-in-sidecar-injection-spec", - specSetting: true, - annotations: map[string]string{}, - expected: true, - }, - { - name: "RewriteAppHTTPProbe-set-in-annotations", - specSetting: false, - annotations: map[string]string{annotation.SidecarRewriteAppHTTPProbers.Name: "true"}, - expected: true, - }, - { - name: "RewriteAppHTTPProbe-set-in-sidecar-injection-spec-&-annotations", - specSetting: true, - annotations: map[string]string{annotation.SidecarRewriteAppHTTPProbers.Name: "true"}, - expected: true, - }, - { - name: "RewriteAppHTTPProbe-set-in-annotations", - specSetting: false, - annotations: map[string]string{annotation.SidecarRewriteAppHTTPProbers.Name: "false"}, - expected: false, - }, - { - name: "RewriteAppHTTPProbe-set-in-sidecar-injection-spec-&-annotations", - specSetting: true, - annotations: map[string]string{annotation.SidecarRewriteAppHTTPProbers.Name: "false"}, - expected: false, - }, - } { - got := ShouldRewriteAppHTTPProbers(tc.annotations, tc.specSetting) - want := tc.expected - if got != want { - t.Errorf("[%v] failed, want %v, got %v", tc.name, want, got) - } - } -} - -func TestDumpAppGRPCProbers(t *testing.T) { - svc := "foo" - for _, tc := range []struct { - name string - podSpec *corev1.PodSpec - expected string - }{ - { - name: "simple gRPC liveness probe", - podSpec: &corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "foo", - LivenessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - GRPC: &corev1.GRPCAction{ - Port: 1234, - }, - }, - }, - }, - }, - }, - expected: ` -{ - "/app-health/foo/livez": { - "grpc": { - "port": 1234, - "service": null - } - } -}`, - }, - { - name: "gRPC readiness probe with service", - podSpec: &corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "bar", - ReadinessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - GRPC: &corev1.GRPCAction{ - Port: 1234, - Service: &svc, - }, - }, - }, - }, - }, - }, - expected: ` -{ - "/app-health/bar/readyz": { - "grpc": { - "port": 1234, - "service": "foo" - } - } -}`, - }, - { - name: "gRPC startup probe with service and timeout", - podSpec: &corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "foo", - StartupProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - GRPC: &corev1.GRPCAction{ - Port: 1234, - Service: &svc, - }, - }, - TimeoutSeconds: 10, - }, - }, - }, - }, - expected: ` -{ - "/app-health/foo/startupz": { - "grpc": { - "port": 1234, - "service": "foo" - }, - "timeoutSeconds": 10 - } -}`, - }, - } { - got := DumpAppProbers(tc.podSpec, 15020) - test.JSONEquals(t, got, tc.expected) - } -} - -func TestPatchRewriteProbe(t *testing.T) { - svc := "foo" - annotations := map[string]string{} - statusPort := intstr.FromInt(15020) - for _, tc := range []struct { - name string - pod *corev1.Pod - annotations map[string]string - expected *corev1.Pod - }{ - { - name: "pod with no probes", - pod: &corev1.Pod{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "foo", - }, - }, - }, - }, - expected: &corev1.Pod{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "foo", - }, - }, - }, - }, - }, - { - name: "pod with a gRPC liveness probe", - pod: &corev1.Pod{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "foo", - LivenessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - GRPC: &corev1.GRPCAction{ - Port: 1234, - }, - }, - }, - }, - }, - }, - }, - expected: &corev1.Pod{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "foo", - LivenessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - HTTPGet: &corev1.HTTPGetAction{ - Path: "/app-health/foo/livez", - Port: statusPort, - }, - }, - }, - }, - }, - }, - }, - }, - { - name: "pod with gRPC liveness,readiness,startup probes", - pod: &corev1.Pod{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "foo", - LivenessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - GRPC: &corev1.GRPCAction{ - Port: 1234, - Service: &svc, - }, - }, - }, - ReadinessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - GRPC: &corev1.GRPCAction{ - Port: 1235, - Service: &svc, - }, - }, - TimeoutSeconds: 10, - }, - }, - { - Name: "bar", - StartupProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - GRPC: &corev1.GRPCAction{ - Port: 1236, - }, - }, - TimeoutSeconds: 20, - PeriodSeconds: 10, - InitialDelaySeconds: 10, - }, - }, - }, - }, - }, - expected: &corev1.Pod{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "foo", - LivenessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - HTTPGet: &corev1.HTTPGetAction{ - Path: "/app-health/foo/livez", - Port: statusPort, - }, - }, - }, - ReadinessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - HTTPGet: &corev1.HTTPGetAction{ - Path: "/app-health/foo/readyz", - Port: statusPort, - }, - }, - TimeoutSeconds: 10, - }, - }, - { - Name: "bar", - StartupProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - HTTPGet: &corev1.HTTPGetAction{ - Path: "/app-health/bar/startupz", - Port: statusPort, - }, - }, - TimeoutSeconds: 20, - PeriodSeconds: 10, - InitialDelaySeconds: 10, - }, - }, - }, - }, - }, - }, - } { - patchRewriteProbe(annotations, tc.pod, 15020) - if !reflect.DeepEqual(tc.pod, tc.expected) { - t.Errorf("[%v] failed, want %v, got %v", tc.name, tc.expected, tc.pod) - } - } -} diff --git a/pkg/kube/inject/initializer.go b/pkg/kube/inject/initializer.go deleted file mode 100644 index 3befbb96b..000000000 --- a/pkg/kube/inject/initializer.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package inject - -import ( - openshiftv1 "github.com/openshift/api/apps/v1" - appsv1 "k8s.io/api/apps/v1" - batchv1 "k8s.io/api/batch/v1" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// IgnoredNamespaces contains the system namespaces referenced from Kubernetes: -// Ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/#viewing-namespaces -// "kube-system": The namespace for objects created by the Kubernetes system. -// "kube-public": This namespace is mostly reserved for cluster usage. -// "kube-node-lease": This namespace for the lease objects associated with each node -// -// which improves the performance of the node heartbeats as the cluster scales. -// -// "local-path-storage": Dynamically provisioning persistent local storage with Kubernetes. -// -// used with Kind cluster: https://github.com/rancher/local-path-provisioner -var IgnoredNamespaces = sets.New( - constants.KubeSystemNamespace, - constants.KubePublicNamespace, - constants.KubeNodeLeaseNamespace, - constants.LocalPathStorageNamespace) - -var ( - kinds = []struct { - groupVersion schema.GroupVersion - obj runtime.Object - resource string - apiPath string - }{ - {v1.SchemeGroupVersion, &v1.ReplicationController{}, "replicationcontrollers", "/api"}, - {v1.SchemeGroupVersion, &v1.Pod{}, "pods", "/api"}, - - {appsv1.SchemeGroupVersion, &appsv1.Deployment{}, "deployments", "/apis"}, - {appsv1.SchemeGroupVersion, &appsv1.DaemonSet{}, "daemonsets", "/apis"}, - {appsv1.SchemeGroupVersion, &appsv1.ReplicaSet{}, "replicasets", "/apis"}, - - {batchv1.SchemeGroupVersion, &batchv1.Job{}, "jobs", "/apis"}, - {batchv1.SchemeGroupVersion, &batchv1.CronJob{}, "cronjobs", "/apis"}, - - {appsv1.SchemeGroupVersion, &appsv1.StatefulSet{}, "statefulsets", "/apis"}, - - {v1.SchemeGroupVersion, &v1.List{}, "lists", "/apis"}, - - {openshiftv1.GroupVersion, &openshiftv1.DeploymentConfig{}, "deploymentconfigs", "/apis"}, - } - injectScheme = runtime.NewScheme() -) - -func init() { - for _, kind := range kinds { - injectScheme.AddKnownTypes(kind.groupVersion, kind.obj) - injectScheme.AddUnversionedTypes(kind.groupVersion, kind.obj) - } -} diff --git a/pkg/kube/inject/initializer_test.go b/pkg/kube/inject/initializer_test.go deleted file mode 100644 index abd09069c..000000000 --- a/pkg/kube/inject/initializer_test.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package inject - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" -) - -func TestSystemNamespaces_Contains(t *testing.T) { - tests := []struct { - ns string - expected bool - }{ - { - ns: constants.KubeSystemNamespace, - expected: true, - }, - { - ns: constants.KubePublicNamespace, - expected: true, - }, - { - ns: constants.KubeNodeLeaseNamespace, - expected: true, - }, - { - ns: constants.LocalPathStorageNamespace, - expected: true, - }, - { - ns: "fake", - expected: false, - }, - } - for _, test := range tests { - if IgnoredNamespaces.Contains(test.ns) != test.expected { - t.Fatal("the system namespaces are incorrect") - } - } -} diff --git a/pkg/kube/inject/inject.go b/pkg/kube/inject/inject.go deleted file mode 100644 index 95f948e0a..000000000 --- a/pkg/kube/inject/inject.go +++ /dev/null @@ -1,923 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package inject - -import ( - "bufio" - "bytes" - "encoding/json" - "fmt" - "io" - "math" - "reflect" - "sort" - "strconv" - "strings" - "text/template" -) - -import ( - "github.com/Masterminds/sprig/v3" - jsonpatch "github.com/evanphx/json-patch/v5" - "istio.io/api/annotation" - "istio.io/api/label" - meshconfig "istio.io/api/mesh/v1alpha1" - proxyConfig "istio.io/api/networking/v1beta1" - "istio.io/pkg/log" - appsv1 "k8s.io/api/apps/v1" - batch "k8s.io/api/batch/v1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - yamlDecoder "k8s.io/apimachinery/pkg/util/yaml" - "sigs.k8s.io/yaml" -) - -import ( - opconfig "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" -) - -// InjectionPolicy determines the policy for injecting the -// sidecar proxy into the watched namespace(s). -type InjectionPolicy string - -const ( - // InjectionPolicyDisabled specifies that the sidecar injector - // will not inject the sidecar into resources by default for the - // namespace(s) being watched. Resources can enable injection - // using the "sidecar.istio.io/inject" annotation with value of - // true. - InjectionPolicyDisabled InjectionPolicy = "disabled" - - // InjectionPolicyEnabled specifies that the sidecar injector will - // inject the sidecar into resources by default for the - // namespace(s) being watched. Resources can disable injection - // using the "sidecar.istio.io/inject" annotation with value of - // false. - InjectionPolicyEnabled InjectionPolicy = "enabled" -) - -const ( - // ProxyContainerName is used by e2e integration tests for fetching logs - ProxyContainerName = "istio-proxy" - - // ValidationContainerName is the name of the init container that validates - // if CNI has made the necessary changes to iptables - ValidationContainerName = "istio-validation" - - // InitContainerName is the name of the init container that deploys iptables - InitContainerName = "istio-init" - - // EnableCoreDumpName is the name of the init container that allows core dumps - EnableCoreDumpName = "enable-core-dump" -) - -const ( - // ImageTypeDebug is the suffix of the debug image. - ImageTypeDebug = "debug" - // ImageTypeDistroless is the suffix of the distroless image. - ImageTypeDistroless = "distroless" - // ImageTypeDefault is the type name of the default image, sufix is elided. - ImageTypeDefault = "default" -) - -// SidecarTemplateData is the data object to which the templated -// version of `SidecarInjectionSpec` is applied. -type SidecarTemplateData struct { - TypeMeta metav1.TypeMeta - DeploymentMeta metav1.ObjectMeta - ObjectMeta metav1.ObjectMeta - Spec corev1.PodSpec - ProxyConfig *meshconfig.ProxyConfig - MeshConfig *meshconfig.MeshConfig - Values map[string]interface{} - Revision string - EstimatedConcurrency int - ProxyImage string -} - -type ( - Template *corev1.Pod - RawTemplates map[string]string - Templates map[string]*template.Template -) - -type Injector interface { - Inject(pod *corev1.Pod, namespace string) ([]byte, error) -} - -// Config specifies the sidecar injection configuration This includes -// the sidecar template and cluster-side injection policy. It is used -// by kube-inject, sidecar injector, and http endpoint. -type Config struct { - Policy InjectionPolicy `json:"policy"` - - // DefaultTemplates defines the default template to use for pods that do not explicitly specify a template - DefaultTemplates []string `json:"defaultTemplates"` - - // RawTemplates defines a set of templates to be used. The specified template will be run, provided with - // SidecarTemplateData, and merged with the original pod spec using a strategic merge patch. - RawTemplates RawTemplates `json:"templates"` - - // Aliases defines a translation of a name to inject template. For example, `sidecar: [proxy,init]` could allow - // referencing two templates, "proxy" and "init" by a single name, "sidecar". - // Expansion is not recursive. - Aliases map[string][]string `json:"aliases"` - - // NeverInjectSelector: Refuses the injection on pods whose labels match this selector. - // It's an array of label selectors, that will be OR'ed, meaning we will iterate - // over it and stop at the first match - // Takes precedence over AlwaysInjectSelector. - NeverInjectSelector []metav1.LabelSelector `json:"neverInjectSelector"` - - // AlwaysInjectSelector: Forces the injection on pods whose labels match this selector. - // It's an array of label selectors, that will be OR'ed, meaning we will iterate - // over it and stop at the first match - AlwaysInjectSelector []metav1.LabelSelector `json:"alwaysInjectSelector"` - - // InjectedAnnotations are additional annotations that will be added to the pod spec after injection - // This is primarily to support PSP annotations. - InjectedAnnotations map[string]string `json:"injectedAnnotations"` - - // Templates is a pre-parsed copy of RawTemplates - Templates Templates `json:"-"` -} - -const ( - SidecarTemplateName = "sidecar" -) - -// UnmarshalConfig unmarshals the provided YAML configuration, while normalizing the resulting configuration -func UnmarshalConfig(yml []byte) (Config, error) { - var injectConfig Config - if err := yaml.Unmarshal(yml, &injectConfig); err != nil { - return injectConfig, fmt.Errorf("failed to unmarshal injection template: %v", err) - } - if injectConfig.RawTemplates == nil { - injectConfig.RawTemplates = make(map[string]string) - } - if len(injectConfig.DefaultTemplates) == 0 { - injectConfig.DefaultTemplates = []string{SidecarTemplateName} - } - if len(injectConfig.RawTemplates) == 0 { - log.Warnf("injection templates are empty." + - " This may be caused by using an injection template from an older version of Istio." + - " Please ensure the template is correct; mismatch template versions can lead to unexpected results, including pods not being injected.") - } - - var err error - injectConfig.Templates, err = ParseTemplates(injectConfig.RawTemplates) - if err != nil { - return injectConfig, err - } - - return injectConfig, nil -} - -func injectRequired(ignored []string, config *Config, podSpec *corev1.PodSpec, metadata metav1.ObjectMeta) bool { // nolint: lll - // Skip injection when host networking is enabled. The problem is - // that the iptables changes are assumed to be within the pod when, - // in fact, they are changing the routing at the host level. This - // often results in routing failures within a node which can - // affect the network provider within the cluster causing - // additional pod failures. - if podSpec.HostNetwork { - return false - } - - // skip special kubernetes system namespaces - for _, namespace := range ignored { - if metadata.Namespace == namespace { - return false - } - } - - annos := metadata.GetAnnotations() - - var useDefault bool - var inject bool - - objectSelector := annos[annotation.SidecarInject.Name] - if lbl, labelPresent := metadata.GetLabels()[annotation.SidecarInject.Name]; labelPresent { - // The label is the new API; if both are present we prefer the label - objectSelector = lbl - } - switch strings.ToLower(objectSelector) { - // http://yaml.org/type/bool.html - case "y", "yes", "true", "on": - inject = true - case "": - useDefault = true - } - - // If an annotation is not explicitly given, check the LabelSelectors, starting with NeverInject - if useDefault { - for _, neverSelector := range config.NeverInjectSelector { - selector, err := metav1.LabelSelectorAsSelector(&neverSelector) - if err != nil { - log.Warnf("Invalid selector for NeverInjectSelector: %v (%v)", neverSelector, err) - } else if !selector.Empty() && selector.Matches(labels.Set(metadata.Labels)) { - log.Debugf("Explicitly disabling injection for pod %s/%s due to pod labels matching NeverInjectSelector config map entry.", - metadata.Namespace, potentialPodName(metadata)) - inject = false - useDefault = false - break - } - } - } - - // If there's no annotation nor a NeverInjectSelector, check the AlwaysInject one - if useDefault { - for _, alwaysSelector := range config.AlwaysInjectSelector { - selector, err := metav1.LabelSelectorAsSelector(&alwaysSelector) - if err != nil { - log.Warnf("Invalid selector for AlwaysInjectSelector: %v (%v)", alwaysSelector, err) - } else if !selector.Empty() && selector.Matches(labels.Set(metadata.Labels)) { - log.Debugf("Explicitly enabling injection for pod %s/%s due to pod labels matching AlwaysInjectSelector config map entry.", - metadata.Namespace, potentialPodName(metadata)) - inject = true - useDefault = false - break - } - } - } - - var required bool - switch config.Policy { - default: // InjectionPolicyOff - log.Errorf("Illegal value for autoInject:%s, must be one of [%s,%s]. Auto injection disabled!", - config.Policy, InjectionPolicyDisabled, InjectionPolicyEnabled) - required = false - case InjectionPolicyDisabled: - if useDefault { - required = false - } else { - required = inject - } - case InjectionPolicyEnabled: - if useDefault { - required = true - } else { - required = inject - } - } - - if log.DebugEnabled() { - // Build a log message for the annotations. - annotationStr := "" - for name := range AnnotationValidation { - value, ok := annos[name] - if !ok { - value = "(unset)" - } - annotationStr += fmt.Sprintf("%s:%s ", name, value) - } - - log.Debugf("Sidecar injection policy for %v/%v: namespacePolicy:%v useDefault:%v inject:%v required:%v %s", - metadata.Namespace, - potentialPodName(metadata), - config.Policy, - useDefault, - inject, - required, - annotationStr) - } - - return required -} - -// ProxyImage constructs image url in a backwards compatible way. -// values based name => {{ .Values.global.hub }}/{{ .Values.global.proxy.image }}:{{ .Values.global.tag }} -func ProxyImage(values *opconfig.Values, image *proxyConfig.ProxyImage, annotations map[string]string) string { - imageName := "proxyv2" - global := values.GetGlobal() - - tag := "" - if global.GetTag() != nil { // Tag is an interface but we need the string form. - tag = fmt.Sprintf("%v", global.GetTag().AsInterface()) - } - - imageType := "" - if image != nil { - imageType = image.ImageType - } - - if global.GetProxy() != nil && global.GetProxy().GetImage() != "" { - imageName = global.GetProxy().GetImage() - } - - if it, ok := annotations[annotation.SidecarProxyImageType.Name]; ok { - imageType = it - } - - return imageURL(global.GetHub(), imageName, tag, imageType) -} - -// imageURL creates url from parts. -// imageType is appended if not empty -// if imageType is already present in the tag, then it is replaced. -// docker.io/istio/proxyv2:1.12-distroless -// gcr.io/gke-release/asm/proxyv2:1.11.2-asm.17-distroless -// docker.io/istio/proxyv2:1.12 -func imageURL(hub, imageName, tag, imageType string) string { - return hub + "/" + imageName + ":" + updateImageTypeIfPresent(tag, imageType) -} - -// KnownImageTypes are image types that istio pubishes. -var KnownImageTypes = []string{ImageTypeDistroless, ImageTypeDebug} - -func updateImageTypeIfPresent(tag string, imageType string) string { - if imageType == "" { - return tag - } - - for _, i := range KnownImageTypes { - if strings.HasSuffix(tag, "-"+i) { - tag = tag[:len(tag)-(len(i)+1)] - break - } - } - - if imageType == ImageTypeDefault { - return tag - } - - return tag + "-" + imageType -} - -// RunTemplate renders the sidecar template -// Returns the raw string template, as well as the parse pod form -func RunTemplate(params InjectionParameters) (mergedPod *corev1.Pod, templatePod *corev1.Pod, err error) { - metadata := ¶ms.pod.ObjectMeta - meshConfig := params.meshConfig - - if err := validateAnnotations(metadata.GetAnnotations()); err != nil { - log.Errorf("Injection failed due to invalid annotations: %v", err) - return nil, nil, err - } - - cluster := params.valuesConfig.asStruct.GetGlobal().GetMultiCluster().GetClusterName() - // TODO allow overriding the values.global network in injection with the system namespace label - network := params.valuesConfig.asStruct.GetGlobal().GetNetwork() - // params may be set from webhook URL, take priority over values yaml - if params.proxyEnvs["ISTIO_META_CLUSTER_ID"] != "" { - cluster = params.proxyEnvs["ISTIO_META_CLUSTER_ID"] - } - if params.proxyEnvs["ISTIO_META_NETWORK"] != "" { - network = params.proxyEnvs["ISTIO_META_NETWORK"] - } - // explicit label takes highest precedence - if n, ok := metadata.Labels[label.TopologyNetwork.Name]; ok { - network = n - } - - // use network in values for template, and proxy env variables - if cluster != "" { - params.proxyEnvs["ISTIO_META_CLUSTER_ID"] = cluster - } - if network != "" { - params.proxyEnvs["ISTIO_META_NETWORK"] = network - } - - strippedPod, err := reinsertOverrides(stripPod(params)) - if err != nil { - return nil, nil, err - } - - data := SidecarTemplateData{ - TypeMeta: params.typeMeta, - DeploymentMeta: params.deployMeta, - ObjectMeta: strippedPod.ObjectMeta, - Spec: strippedPod.Spec, - ProxyConfig: params.proxyConfig, - MeshConfig: meshConfig, - Values: params.valuesConfig.asMap, - Revision: params.revision, - EstimatedConcurrency: estimateConcurrency(params.proxyConfig, metadata.Annotations, params.valuesConfig.asStruct), - ProxyImage: ProxyImage(params.valuesConfig.asStruct, params.proxyConfig.Image, strippedPod.Annotations), - } - - mergedPod = params.pod - templatePod = &corev1.Pod{} - for _, templateName := range selectTemplates(params) { - parsedTemplate, f := params.templates[templateName] - if !f { - return nil, nil, fmt.Errorf("requested template %q not found; have %v", - templateName, strings.Join(knownTemplates(params.templates), ", ")) - } - bbuf, err := runTemplate(parsedTemplate, data) - if err != nil { - return nil, nil, err - } - - mergedPod, err = applyOverlayYAML(mergedPod, bbuf.Bytes()) - if err != nil { - return nil, nil, fmt.Errorf("failed parsing generated injected YAML (check Istio sidecar injector configuration): %v", err) - } - templatePod, err = applyOverlayYAML(templatePod, bbuf.Bytes()) - if err != nil { - return nil, nil, fmt.Errorf("failed applying injection overlay: %v", err) - } - } - - return mergedPod, templatePod, nil -} - -func knownTemplates(t Templates) []string { - keys := make([]string, 0, len(t)) - for k := range t { - keys = append(keys, k) - } - return keys -} - -func selectTemplates(params InjectionParameters) []string { - if a, f := params.pod.Annotations[annotation.InjectTemplates.Name]; f { - names := []string{} - for _, tmplName := range strings.Split(a, ",") { - name := strings.TrimSpace(tmplName) - names = append(names, name) - } - return resolveAliases(params, names) - } - return resolveAliases(params, params.defaultTemplate) -} - -func resolveAliases(params InjectionParameters, names []string) []string { - ret := []string{} - for _, name := range names { - if al, f := params.aliases[name]; f { - ret = append(ret, al...) - } else { - ret = append(ret, name) - } - } - return ret -} - -func stripPod(req InjectionParameters) *corev1.Pod { - pod := req.pod.DeepCopy() - prevStatus := injectionStatus(pod) - if prevStatus == nil { - return req.pod - } - // We found a previous status annotation. Possibly we are re-injecting the pod - // To ensure idempotency, remove our injected containers first - for _, c := range prevStatus.Containers { - pod.Spec.Containers = modifyContainers(pod.Spec.Containers, c, Remove) - } - for _, c := range prevStatus.InitContainers { - pod.Spec.InitContainers = modifyContainers(pod.Spec.InitContainers, c, Remove) - } - - targetPort := strconv.Itoa(int(req.meshConfig.GetDefaultConfig().GetStatusPort())) - if cur, f := getPrometheusPort(pod); f { - // We have already set the port, assume user is controlling this or, more likely, re-injected - // the pod. - if cur == targetPort { - clearPrometheusAnnotations(pod) - } - } - delete(pod.Annotations, annotation.SidecarStatus.Name) - - return pod -} - -func injectionStatus(pod *corev1.Pod) *SidecarInjectionStatus { - var statusBytes []byte - if pod.ObjectMeta.Annotations != nil { - if value, ok := pod.ObjectMeta.Annotations[annotation.SidecarStatus.Name]; ok { - statusBytes = []byte(value) - } - } - if statusBytes == nil { - return nil - } - - // default case when injected pod has explicit status - var iStatus SidecarInjectionStatus - if err := json.Unmarshal(statusBytes, &iStatus); err != nil { - return nil - } - return &iStatus -} - -func parseDryTemplate(tmplStr string, funcMap map[string]interface{}) (*template.Template, error) { - temp := template.New("inject") - t, err := temp.Funcs(sprig.TxtFuncMap()).Funcs(funcMap).Parse(tmplStr) - if err != nil { - log.Infof("Failed to parse template: %v %v\n", err, tmplStr) - return nil, err - } - - return t, nil -} - -func runTemplate(tmpl *template.Template, data SidecarTemplateData) (bytes.Buffer, error) { - var res bytes.Buffer - if err := tmpl.Execute(&res, &data); err != nil { - log.Errorf("Invalid template: %v", err) - return bytes.Buffer{}, err - } - - return res, nil -} - -// IntoResourceFile injects the istio proxy into the specified -// kubernetes YAML file. -// nolint: lll -func IntoResourceFile(injector Injector, sidecarTemplate Templates, - valuesConfig ValuesConfig, revision string, meshconfig *meshconfig.MeshConfig, in io.Reader, out io.Writer, warningHandler func(string)) error { - reader := yamlDecoder.NewYAMLReader(bufio.NewReaderSize(in, 4096)) - for { - raw, err := reader.Read() - if err == io.EOF { - break - } - if err != nil { - return err - } - - obj, err := FromRawToObject(raw) - if err != nil && !runtime.IsNotRegisteredError(err) { - return err - } - - var updated []byte - if err == nil { - outObject, err := IntoObject(injector, sidecarTemplate, valuesConfig, revision, meshconfig, obj, warningHandler) // nolint: vetshadow - if err != nil { - return err - } - if updated, err = yaml.Marshal(outObject); err != nil { - return err - } - } else { - updated = raw // unchanged - } - - if _, err = out.Write(updated); err != nil { - return err - } - if _, err = fmt.Fprint(out, "---\n"); err != nil { - return err - } - } - return nil -} - -// FromRawToObject is used to convert from raw to the runtime object -func FromRawToObject(raw []byte) (runtime.Object, error) { - var typeMeta metav1.TypeMeta - if err := yaml.Unmarshal(raw, &typeMeta); err != nil { - return nil, err - } - - gvk := schema.FromAPIVersionAndKind(typeMeta.APIVersion, typeMeta.Kind) - obj, err := injectScheme.New(gvk) - if err != nil { - return nil, err - } - if err = yaml.Unmarshal(raw, obj); err != nil { - return nil, err - } - - return obj, nil -} - -// IntoObject convert the incoming resources into Injected resources -// nolint: lll -func IntoObject(injector Injector, sidecarTemplate Templates, valuesConfig ValuesConfig, - revision string, meshconfig *meshconfig.MeshConfig, in runtime.Object, warningHandler func(string)) (interface{}, error) { - out := in.DeepCopyObject() - - var deploymentMetadata metav1.ObjectMeta - var metadata *metav1.ObjectMeta - var podSpec *corev1.PodSpec - var typeMeta metav1.TypeMeta - - // Handle Lists - if list, ok := out.(*corev1.List); ok { - result := list - - for i, item := range list.Items { - obj, err := FromRawToObject(item.Raw) - if runtime.IsNotRegisteredError(err) { - continue - } - if err != nil { - return nil, err - } - - r, err := IntoObject(injector, sidecarTemplate, valuesConfig, revision, meshconfig, obj, warningHandler) // nolint: vetshadow - if err != nil { - return nil, err - } - - re := runtime.RawExtension{} - re.Object = r.(runtime.Object) - result.Items[i] = re - } - return result, nil - } - - // CronJobs have JobTemplates in them, instead of Templates, so we - // special case them. - switch v := out.(type) { - case *batch.CronJob: - job := v - typeMeta = job.TypeMeta - metadata = &job.Spec.JobTemplate.ObjectMeta - deploymentMetadata = job.ObjectMeta - podSpec = &job.Spec.JobTemplate.Spec.Template.Spec - case *corev1.Pod: - pod := v - typeMeta = pod.TypeMeta - metadata = &pod.ObjectMeta - deploymentMetadata = pod.ObjectMeta - podSpec = &pod.Spec - case *appsv1.Deployment: // Added to be explicit about the most expected case - deploy := v - typeMeta = deploy.TypeMeta - deploymentMetadata = deploy.ObjectMeta - metadata = &deploy.Spec.Template.ObjectMeta - podSpec = &deploy.Spec.Template.Spec - default: - // `in` is a pointer to an Object. Dereference it. - outValue := reflect.ValueOf(out).Elem() - - typeMeta = outValue.FieldByName("TypeMeta").Interface().(metav1.TypeMeta) - - deploymentMetadata = outValue.FieldByName("ObjectMeta").Interface().(metav1.ObjectMeta) - - templateValue := outValue.FieldByName("Spec").FieldByName("Template") - // `Template` is defined as a pointer in some older API - // definitions, e.g. ReplicationController - if templateValue.Kind() == reflect.Ptr { - if templateValue.IsNil() { - return out, fmt.Errorf("spec.template is required value") - } - templateValue = templateValue.Elem() - } - metadata = templateValue.FieldByName("ObjectMeta").Addr().Interface().(*metav1.ObjectMeta) - podSpec = templateValue.FieldByName("Spec").Addr().Interface().(*corev1.PodSpec) - } - - name := metadata.Name - if name == "" { - name = deploymentMetadata.Name - } - namespace := metadata.Namespace - if namespace == "" { - namespace = deploymentMetadata.Namespace - } - - var fullName string - if deploymentMetadata.Namespace != "" { - fullName = fmt.Sprintf("%s/%s", deploymentMetadata.Namespace, name) - } else { - fullName = name - } - - kind := typeMeta.Kind - - // Skip injection when host networking is enabled. The problem is - // that the iptable changes are assumed to be within the pod when, - // in fact, they are changing the routing at the host level. This - // often results in routing failures within a node which can - // affect the network provider within the cluster causing - // additional pod failures. - if podSpec.HostNetwork { - warningStr := fmt.Sprintf("===> Skipping injection because %q has host networking enabled\n", - fullName) - if kind != "" { - warningStr = fmt.Sprintf("===> Skipping injection because %s %q has host networking enabled\n", - kind, fullName) - } - warningHandler(warningStr) - return out, nil - } - - // skip injection for injected pods - if len(podSpec.Containers) > 1 { - _, hasStatus := metadata.Annotations[annotation.SidecarStatus.Name] - for _, c := range podSpec.Containers { - if c.Name == ProxyContainerName && hasStatus { - warningStr := fmt.Sprintf("===> Skipping injection because %q has injected %q sidecar already\n", - fullName, ProxyContainerName) - if kind != "" { - warningStr = fmt.Sprintf("===> Skipping injection because %s %s %q has host networking enabled\n", - kind, fullName, ProxyContainerName) - } - warningHandler(warningStr) - return out, nil - } - } - } - - pod := &corev1.Pod{ - ObjectMeta: *metadata, - Spec: *podSpec, - } - - var patchBytes []byte - var err error - if injector != nil { - patchBytes, err = injector.Inject(pod, namespace) - } - if err != nil { - return nil, err - } - // TODO(Monkeyanator) istioctl injection still applies just the pod annotation since we don't have - // the ProxyConfig CRs here. - if pca, f := metadata.GetAnnotations()[annotation.ProxyConfig.Name]; f { - var merr error - meshconfig, merr = mesh.ApplyProxyConfig(pca, meshconfig) - if merr != nil { - return nil, merr - } - } - - if patchBytes == nil { - if !injectRequired(IgnoredNamespaces.UnsortedList(), &Config{Policy: InjectionPolicyEnabled}, &pod.Spec, pod.ObjectMeta) { - warningStr := fmt.Sprintf("===> Skipping injection because %q has sidecar injection disabled\n", fullName) - if kind != "" { - warningStr = fmt.Sprintf("===> Skipping injection because %s %q has sidecar injection disabled\n", - kind, fullName) - } - warningHandler(warningStr) - return out, nil - } - params := InjectionParameters{ - pod: pod, - deployMeta: deploymentMetadata, - typeMeta: typeMeta, - // Todo replace with some template resolver abstraction - templates: sidecarTemplate, - defaultTemplate: []string{SidecarTemplateName}, - meshConfig: meshconfig, - proxyConfig: meshconfig.GetDefaultConfig(), - valuesConfig: valuesConfig, - revision: revision, - proxyEnvs: map[string]string{}, - injectedAnnotations: nil, - } - patchBytes, err = injectPod(params) - } - if err != nil { - return nil, err - } - patched, err := applyJSONPatchToPod(pod, patchBytes) - if err != nil { - return nil, err - } - patchedObject, _, err := jsonSerializer.Decode(patched, nil, &corev1.Pod{}) - if err != nil { - return nil, err - } - patchedPod := patchedObject.(*corev1.Pod) - *metadata = patchedPod.ObjectMeta - *podSpec = patchedPod.Spec - return out, nil -} - -func applyJSONPatchToPod(input *corev1.Pod, patch []byte) ([]byte, error) { - objJS, err := runtime.Encode(jsonSerializer, input) - if err != nil { - return nil, err - } - - p, err := jsonpatch.DecodePatch(patch) - if err != nil { - return nil, err - } - - patchedJSON, err := p.Apply(objJS) - if err != nil { - return nil, err - } - return patchedJSON, nil -} - -// SidecarInjectionStatus contains basic information about the -// injected sidecar. This includes the names of added containers and -// volumes. -type SidecarInjectionStatus struct { - InitContainers []string `json:"initContainers"` - Containers []string `json:"containers"` - Volumes []string `json:"volumes"` - ImagePullSecrets []string `json:"imagePullSecrets"` - Revision string `json:"revision"` -} - -func potentialPodName(metadata metav1.ObjectMeta) string { - if metadata.Name != "" { - return metadata.Name - } - if metadata.GenerateName != "" { - return metadata.GenerateName + "***** (actual name not yet known)" - } - return "" -} - -// overwriteClusterInfo updates cluster name and network from url path -// This is needed when webconfig config runs on a different cluster than webhook -func overwriteClusterInfo(containers []corev1.Container, params InjectionParameters) { - if len(params.proxyEnvs) > 0 { - log.Debugf("Updating cluster envs based on inject url: %s\n", params.proxyEnvs) - for i, c := range containers { - if c.Name == ProxyContainerName { - updateClusterEnvs(&containers[i], params.proxyEnvs) - break - } - } - } -} - -func updateClusterEnvs(container *corev1.Container, newKVs map[string]string) { - envVars := make([]corev1.EnvVar, 0) - - for _, env := range container.Env { - if _, found := newKVs[env.Name]; !found { - envVars = append(envVars, env) - } - } - - keys := make([]string, 0, len(newKVs)) - for key := range newKVs { - keys = append(keys, key) - } - sort.Strings(keys) - for _, key := range keys { - val := newKVs[key] - envVars = append(envVars, corev1.EnvVar{Name: key, Value: val, ValueFrom: nil}) - } - container.Env = envVars -} - -// Uses the default concurrency 2, unless either overridden by proxy config to a positive number, -// or special value 0, in which case the value is computed from CPU limits/requests. -func estimateConcurrency(cfg *meshconfig.ProxyConfig, annotations map[string]string, valuesStruct *opconfig.Values) int { - if cfg != nil && cfg.Concurrency != nil { - concurrency := int(cfg.Concurrency.Value) - if concurrency > 0 { - return concurrency - } - if limit, ok := annotations[annotation.SidecarProxyCPULimit.Name]; ok { - out, err := quantityToConcurrency(limit) - if err == nil { - return out - } - } else if request, ok := annotations[annotation.SidecarProxyCPU.Name]; ok { - out, err := quantityToConcurrency(request) - if err == nil { - return out - } - } else if resources := valuesStruct.GetGlobal().GetProxy().GetResources(); resources != nil { // nolint: staticcheck - if resources.Limits != nil { - if limit, ok := resources.Limits["cpu"]; ok { - out, err := quantityToConcurrency(limit) - if err == nil { - return out - } - } - } - if resources.Requests != nil { - if request, ok := resources.Requests["cpu"]; ok { - out, err := quantityToConcurrency(request) - if err == nil { - return out - } - } - } - } - } - return 2 -} - -// Convert k8s quantity to its milli value (e.g. ceil(quantity * 1000)) and then to concurrency. -// With the resource setting, we round up to single integer number; for example, if we have a 500m limit -// the pod will get concurrency=1. With 6500m, it will get concurrency=7. -func quantityToConcurrency(quantity string) (int, error) { - q, err := resource.ParseQuantity(quantity) - if err != nil { - return 0, err - } - return int(math.Ceil(float64(q.MilliValue()) / 1000)), nil -} diff --git a/pkg/kube/inject/inject_test.go b/pkg/kube/inject/inject_test.go deleted file mode 100644 index 7b4c38ef3..000000000 --- a/pkg/kube/inject/inject_test.go +++ /dev/null @@ -1,1087 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package inject - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "os" - "strings" - "testing" - "time" -) - -import ( - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/testing/protocmp" - "google.golang.org/protobuf/types/known/durationpb" - "google.golang.org/protobuf/types/known/structpb" - "istio.io/api/annotation" - meshapi "istio.io/api/mesh/v1alpha1" - proxyConfig "istio.io/api/networking/v1beta1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -import ( - opconfig "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/test/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// TestInjection tests both the mutating webhook and kube-inject. It does this by sharing the same input and output -// test files and running through the two different code paths. -func TestInjection(t *testing.T) { - type testCase struct { - in string - want string - setFlags []string - inFilePath string - mesh func(m *meshapi.MeshConfig) - skipWebhook bool - expectedError string - expectedLog string - setup func(t test.Failer) - } - cases := []testCase{ - // verify cni - { - in: "hello.yaml", - want: "hello.yaml.cni.injected", - setFlags: []string{ - "components.cni.enabled=true", - "values.istio_cni.chained=true", - "values.global.network=network1", - }, - }, - { - in: "hello.yaml", - want: "hello.yaml.proxyImageName.injected", - setFlags: []string{ - "values.global.proxy.image=proxyTest", - }, - }, - { - in: "hello.yaml", - want: "hello-tproxy.yaml.injected", - mesh: func(m *meshapi.MeshConfig) { - m.DefaultConfig.InterceptionMode = meshapi.ProxyConfig_TPROXY - }, - }, - { - in: "hello.yaml", - want: "hello-always.yaml.injected", - setFlags: []string{"values.global.imagePullPolicy=Always"}, - }, - { - in: "hello.yaml", - want: "hello-never.yaml.injected", - setFlags: []string{"values.global.imagePullPolicy=Never"}, - }, - { - in: "enable-core-dump.yaml", - want: "enable-core-dump.yaml.injected", - setFlags: []string{"values.global.proxy.enableCoreDump=true"}, - }, - { - in: "format-duration.yaml", - want: "format-duration.yaml.injected", - mesh: func(m *meshapi.MeshConfig) { - m.DefaultConfig.DrainDuration = durationpb.New(time.Second * 23) - m.DefaultConfig.ParentShutdownDuration = durationpb.New(time.Second * 42) - }, - }, - { - // Verifies that parameters are applied properly when no annotations are provided. - in: "traffic-params.yaml", - want: "traffic-params.yaml.injected", - setFlags: []string{ - `values.global.proxy.includeIPRanges=127.0.0.1/24,10.96.0.1/24`, - `values.global.proxy.excludeIPRanges=10.96.0.2/24,10.96.0.3/24`, - `values.global.proxy.excludeInboundPorts=4,5,6`, - `values.global.proxy.statusPort=0`, - }, - }, - { - // Verifies that the status params behave properly. - in: "status_params.yaml", - want: "status_params.yaml.injected", - setFlags: []string{ - `values.global.proxy.statusPort=123`, - `values.global.proxy.readinessInitialDelaySeconds=100`, - `values.global.proxy.readinessPeriodSeconds=200`, - `values.global.proxy.readinessFailureThreshold=300`, - }, - }, - { - // Verifies that the kubevirtInterfaces list are applied properly from parameters.. - in: "kubevirtInterfaces.yaml", - want: "kubevirtInterfaces.yaml.injected", - setFlags: []string{ - `values.global.proxy.statusPort=123`, - `values.global.proxy.readinessInitialDelaySeconds=100`, - `values.global.proxy.readinessPeriodSeconds=200`, - `values.global.proxy.readinessFailureThreshold=300`, - }, - }, - { - // Verifies that global.imagePullSecrets are applied properly - in: "hello.yaml", - want: "hello-image-secrets-in-values.yaml.injected", - inFilePath: "hello-image-secrets-in-values.iop.yaml", - }, - { - // Verifies that global.imagePullSecrets are appended properly - in: "hello-image-pull-secret.yaml", - want: "hello-multiple-image-secrets.yaml.injected", - inFilePath: "hello-image-secrets-in-values.iop.yaml", - }, - { - // Verifies that global.podDNSSearchNamespaces are applied properly - in: "hello.yaml", - want: "hello-template-in-values.yaml.injected", - inFilePath: "hello-template-in-values.iop.yaml", - }, - { - // Verifies that global.mountMtlsCerts is applied properly - in: "hello.yaml", - want: "hello-mount-mtls-certs.yaml.injected", - setFlags: []string{`values.global.mountMtlsCerts=true`}, - }, - { - // Verifies that k8s.v1.cni.cncf.io/networks is set to istio-cni when not chained - in: "hello.yaml", - want: "hello-cncf-networks.yaml.injected", - setFlags: []string{ - `components.cni.enabled=true`, - `values.istio_cni.chained=false`, - }, - }, - { - // Verifies that istio-cni is appended to k8s.v1.cni.cncf.io/networks flat value if set - in: "hello-existing-cncf-networks.yaml", - want: "hello-existing-cncf-networks.yaml.injected", - setFlags: []string{ - `components.cni.enabled=true`, - `values.istio_cni.chained=false`, - }, - }, - { - // Verifies that istio-cni is appended to k8s.v1.cni.cncf.io/networks JSON value - in: "hello-existing-cncf-networks-json.yaml", - want: "hello-existing-cncf-networks-json.yaml.injected", - setFlags: []string{ - `components.cni.enabled=true`, - `values.istio_cni.chained=false`, - }, - }, - { - // Verifies that HoldApplicationUntilProxyStarts in MeshConfig puts sidecar in front - in: "hello.yaml", - want: "hello.proxyHoldsApplication.yaml.injected", - setFlags: []string{ - `values.global.proxy.holdApplicationUntilProxyStarts=true`, - }, - }, - { - // Verifies that HoldApplicationUntilProxyStarts in MeshConfig puts sidecar in front - in: "hello-probes.yaml", - want: "hello-probes.proxyHoldsApplication.yaml.injected", - setFlags: []string{ - `values.global.proxy.holdApplicationUntilProxyStarts=true`, - }, - }, - { - // Verifies that HoldApplicationUntilProxyStarts in proxyconfig sets lifecycle hook - in: "hello-probes-proxyHoldApplication-ProxyConfig.yaml", - want: "hello-probes-proxyHoldApplication-ProxyConfig.yaml.injected", - }, - { - // Verifies that HoldApplicationUntilProxyStarts=false in proxyconfig 'OR's with MeshConfig setting - in: "hello-probes-noProxyHoldApplication-ProxyConfig.yaml", - want: "hello-probes-noProxyHoldApplication-ProxyConfig.yaml.injected", - setFlags: []string{ - `values.global.proxy.holdApplicationUntilProxyStarts=true`, - }, - }, - { - // A test with no pods is not relevant for webhook - in: "hello-service.yaml", - want: "hello-service.yaml.injected", - skipWebhook: true, - }, - { - // Cronjob is tricky for webhook test since the spec is different. Since the real code will - // get a pod anyways, the test isn't too useful for webhook anyways. - in: "cronjob.yaml", - want: "cronjob.yaml.injected", - skipWebhook: true, - }, - { - in: "traffic-annotations-bad-includeipranges.yaml", - expectedError: "includeipranges", - }, - { - in: "traffic-annotations-bad-excludeipranges.yaml", - expectedError: "excludeipranges", - }, - { - in: "traffic-annotations-bad-includeinboundports.yaml", - expectedError: "includeinboundports", - }, - { - in: "traffic-annotations-bad-excludeinboundports.yaml", - expectedError: "excludeinboundports", - }, - { - in: "traffic-annotations-bad-excludeoutboundports.yaml", - expectedError: "excludeoutboundports", - }, - { - in: "hello.yaml", - want: "hello-no-seccontext.yaml.injected", - setup: func(t test.Failer) { - test.SetBoolForTest(t, &features.EnableLegacyFSGroupInjection, false) - test.SetEnvForTest(t, "ENABLE_LEGACY_FSGROUP_INJECTION", "false") - }, - }, - { - in: "traffic-annotations.yaml", - want: "traffic-annotations.yaml.injected", - mesh: func(m *meshapi.MeshConfig) { - if m.DefaultConfig.ProxyMetadata == nil { - m.DefaultConfig.ProxyMetadata = map[string]string{} - } - m.DefaultConfig.ProxyMetadata["ISTIO_META_TLS_CLIENT_KEY"] = "/etc/identity/client/keys/client-key.pem" - }, - }, - { - in: "proxy-override.yaml", - want: "proxy-override.yaml.injected", - }, - { - in: "explicit-security-context.yaml", - want: "explicit-security-context.yaml.injected", - }, - { - in: "only-proxy-container.yaml", - want: "only-proxy-container.yaml.injected", - }, - { - in: "proxy-override-args.yaml", - want: "proxy-override-args.yaml.injected", - }, - { - in: "custom-template.yaml", - want: "custom-template.yaml.injected", - inFilePath: "custom-template.iop.yaml", - }, - { - in: "tcp-probes.yaml", - want: "tcp-probes.yaml.injected", - }, - { - in: "tcp-probes.yaml", - want: "tcp-probes-disabled.yaml.injected", - setup: func(t test.Failer) { - test.SetBoolForTest(t, &features.RewriteTCPProbes, false) - }, - }, - { - in: "hello-host-network-with-ns.yaml", - want: "hello-host-network-with-ns.yaml.injected", - expectedLog: "Skipping injection because Deployment \"sample/hello-host-network\" has host networking enabled", - }, - } - // Keep track of tests we add options above - // We will search for all test files and skip these ones - alreadyTested := sets.New() - for _, t := range cases { - if t.want != "" { - alreadyTested.Insert(t.want) - } else { - alreadyTested.Insert(t.in + ".injected") - } - } - files, err := os.ReadDir("testdata/inject") - if err != nil { - t.Fatal(err) - } - if len(files) < 3 { - t.Fatalf("Didn't find test files - something must have gone wrong") - } - // Automatically add any other test files in the folder. This ensures we don't - // forget to add to this list, that we don't have duplicates, etc - // Keep track of all golden files so we can ensure we don't have unused ones later - allOutputFiles := sets.New() - for _, f := range files { - if strings.HasSuffix(f.Name(), ".injected") { - allOutputFiles.Insert(f.Name()) - } - if strings.HasSuffix(f.Name(), ".iop.yaml") { - continue - } - if !strings.HasSuffix(f.Name(), ".yaml") { - continue - } - want := f.Name() + ".injected" - if alreadyTested.Contains(want) { - continue - } - cases = append(cases, testCase{in: f.Name(), want: want}) - } - - // Precompute injection settings. This may seem like a premature optimization, but due to the size of - // YAMLs, with -race this was taking >10min in some cases to generate! - if util.Refresh() { - writeInjectionSettings(t, "default", nil, "") - for i, c := range cases { - if c.setFlags != nil || c.inFilePath != "" { - writeInjectionSettings(t, fmt.Sprintf("%s.%d", c.in, i), c.setFlags, c.inFilePath) - } - } - } - // Preload default settings. Computation here is expensive, so this speeds the tests up substantially - defaultTemplate, defaultValues, defaultMesh := readInjectionSettings(t, "default") - for i, c := range cases { - i, c := i, c - testName := fmt.Sprintf("[%02d] %s", i, c.want) - if c.expectedError != "" { - testName = fmt.Sprintf("[%02d] %s", i, c.in) - } - t.Run(testName, func(t *testing.T) { - if c.setup != nil { - c.setup(t) - } else { - // Tests with custom setup modify global state and cannot run in parallel - t.Parallel() - } - - mc, err := mesh.DeepCopyMeshConfig(defaultMesh) - if err != nil { - t.Fatal(err) - } - sidecarTemplate, valuesConfig := defaultTemplate, defaultValues - if c.setFlags != nil || c.inFilePath != "" { - sidecarTemplate, valuesConfig, mc = readInjectionSettings(t, fmt.Sprintf("%s.%d", c.in, i)) - } - if c.mesh != nil { - c.mesh(mc) - } - - inputFilePath := "testdata/inject/" + c.in - wantFilePath := "testdata/inject/" + c.want - in, err := os.Open(inputFilePath) - if err != nil { - t.Fatalf("Failed to open %q: %v", inputFilePath, err) - } - t.Cleanup(func() { - _ = in.Close() - }) - - // First we test kube-inject. This will run exactly what kube-inject does, and write output to the golden files - t.Run("kube-inject", func(t *testing.T) { - var got bytes.Buffer - logs := make([]string, 0) - warn := func(s string) { - logs = append(logs, s) - t.Log(s) - } - if err = IntoResourceFile(nil, sidecarTemplate.Templates, valuesConfig, "", mc, in, &got, warn); err != nil { - if c.expectedError != "" { - if !strings.Contains(strings.ToLower(err.Error()), c.expectedError) { - t.Fatalf("expected error %q got %q", c.expectedError, err) - } - return - } - t.Fatalf("IntoResourceFile(%v) returned an error: %v", inputFilePath, err) - } - if c.expectedError != "" { - t.Fatalf("expected error but got none") - } - if c.expectedLog != "" { - hasExpectedLog := false - for _, log := range logs { - if strings.Contains(log, c.expectedLog) { - hasExpectedLog = true - break - } - } - if !hasExpectedLog { - t.Fatal("expected log but got none") - } - } - - // The version string is a maintenance pain for this test. Strip the version string before comparing. - gotBytes := util.StripVersion(got.Bytes()) - wantBytes := util.ReadGoldenFile(t, gotBytes, wantFilePath) - - util.CompareBytes(t, gotBytes, wantBytes, wantFilePath) - }) - - // Exit early if we don't need to test webhook. We can skip errors since its redundant - // and painful to test here. - if c.expectedError != "" || c.skipWebhook { - return - } - // Next run the webhook test. This one is a bit trickier as the webhook operates - // on Pods, but the inputs are Deployments/StatefulSets/etc. As a result, we need - // to convert these to pods, then run the injection This test will *not* - // overwrite golden files, as we do not have identical textual output as - // kube-inject. Instead, we just compare the desired/actual pod specs. - t.Run("webhook", func(t *testing.T) { - webhook := &Webhook{ - Config: sidecarTemplate, - meshConfig: mc, - env: &model.Environment{ - PushContext: &model.PushContext{ - ProxyConfigs: &model.ProxyConfigs{}, - }, - }, - valuesConfig: valuesConfig, - revision: "default", - } - // Split multi-part yaml documents. Input and output will have the same number of parts. - inputYAMLs := splitYamlFile(inputFilePath, t) - wantYAMLs := splitYamlFile(wantFilePath, t) - for i := 0; i < len(inputYAMLs); i++ { - t.Run(fmt.Sprintf("yamlPart[%d]", i), func(t *testing.T) { - runWebhook(t, webhook, inputYAMLs[i], wantYAMLs[i], true) - }) - } - }) - }) - } - - // Make sure we don't have any stale test data leftover, as it can cause confusion. - for _, c := range cases { - delete(allOutputFiles, c.want) - } - if len(allOutputFiles) != 0 { - t.Fatalf("stale golden files found: %v", allOutputFiles.UnsortedList()) - } -} - -func testInjectionTemplate(t *testing.T, template, input, expected string) { - t.Helper() - tmpl, err := ParseTemplates(map[string]string{SidecarTemplateName: template}) - if err != nil { - t.Fatal(err) - } - webhook := &Webhook{ - Config: &Config{ - Templates: tmpl, - Policy: InjectionPolicyEnabled, - DefaultTemplates: []string{SidecarTemplateName}, - }, - env: &model.Environment{ - PushContext: &model.PushContext{ - ProxyConfigs: &model.ProxyConfigs{}, - }, - }, - } - runWebhook(t, webhook, []byte(input), []byte(expected), false) -} - -func TestMultipleInjectionTemplates(t *testing.T) { - p, err := ParseTemplates(map[string]string{ - "sidecar": ` -spec: - containers: - - name: istio-proxy - image: proxy -`, - "init": ` -spec: - initContainers: - - name: istio-init - image: proxy -`, - }) - if err != nil { - t.Fatal(err) - } - webhook := &Webhook{ - Config: &Config{ - Templates: p, - Aliases: map[string][]string{"both": {"sidecar", "init"}}, - Policy: InjectionPolicyEnabled, - }, - env: &model.Environment{ - PushContext: &model.PushContext{ - ProxyConfigs: &model.ProxyConfigs{}, - }, - }, - } - - input := ` -apiVersion: v1 -kind: Pod -metadata: - name: hello - annotations: - inject.istio.io/templates: sidecar,init -spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" -` - inputAlias := ` -apiVersion: v1 -kind: Pod -metadata: - name: hello - annotations: - inject.istio.io/templates: both -spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" -` - // nolint: lll - expected := ` -apiVersion: v1 -kind: Pod -metadata: - annotations: - inject.istio.io/templates: %s - prometheus.io/path: /stats/prometheus - prometheus.io/port: "0" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"version":"","initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null}' - name: hello -spec: - initContainers: - - name: istio-init - image: proxy - containers: - - name: hello - image: fake.docker.io/google-samples/hello-go-gke:1.0 - - name: istio-proxy - image: proxy -` - runWebhook(t, webhook, []byte(input), []byte(fmt.Sprintf(expected, "sidecar,init")), false) - runWebhook(t, webhook, []byte(inputAlias), []byte(fmt.Sprintf(expected, "both")), false) -} - -// TestStrategicMerge ensures we can use https://github.com/kubernetes/community/blob/master/contributors/devel/sig-api-machinery/strategic-merge-patch.md -// directives in the injection template -func TestStrategicMerge(t *testing.T) { - testInjectionTemplate(t, - ` -metadata: - labels: - $patch: replace - foo: bar -spec: - containers: - - name: injected - image: "fake.docker.io/google-samples/hello-go-gke:1.1" -`, - ` -apiVersion: v1 -kind: Pod -metadata: - name: hello - labels: - key: value -spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" -`, - - // We expect resources to only have limits, since we had the "replace" directive. - // nolint: lll - ` -apiVersion: v1 -kind: Pod -metadata: - annotations: - prometheus.io/path: /stats/prometheus - prometheus.io/port: "0" - prometheus.io/scrape: "true" - labels: - foo: bar - name: hello -spec: - containers: - - name: injected - image: "fake.docker.io/google-samples/hello-go-gke:1.1" - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" -`) -} - -func runWebhook(t *testing.T, webhook *Webhook, inputYAML []byte, wantYAML []byte, idempotencyCheck bool) { - // Convert the input YAML to a deployment. - inputRaw, err := FromRawToObject(inputYAML) - if err != nil { - t.Fatal(err) - } - inputPod := objectToPod(t, inputRaw) - - // Convert the wanted YAML to a deployment. - wantRaw, err := FromRawToObject(wantYAML) - if err != nil { - t.Fatal(err) - } - wantPod := objectToPod(t, wantRaw) - - // Generate the patch. At runtime, the webhook would actually generate the patch against the - // pod configuration. But since our input files are deployments, rather than actual pod instances, - // we have to apply the patch to the template portion of the deployment only. - templateJSON := convertToJSON(inputPod, t) - got := webhook.inject(&kube.AdmissionReview{ - Request: &kube.AdmissionRequest{ - Object: runtime.RawExtension{ - Raw: templateJSON, - }, - Namespace: jsonToUnstructured(inputYAML, t).GetNamespace(), - }, - }, "") - var gotPod *corev1.Pod - // Apply the generated patch to the template. - if got.Patch != nil { - patchedPod := &corev1.Pod{} - patch := prettyJSON(got.Patch, t) - patchedTemplateJSON := applyJSONPatch(templateJSON, patch, t) - if err := json.Unmarshal(patchedTemplateJSON, patchedPod); err != nil { - t.Fatal(err) - } - gotPod = patchedPod - } else { - gotPod = inputPod - } - - if err := normalizeAndCompareDeployments(gotPod, wantPod, false, t); err != nil { - t.Fatal(err) - } - if idempotencyCheck { - t.Run("idempotency", func(t *testing.T) { - if err := normalizeAndCompareDeployments(gotPod, wantPod, true, t); err != nil { - t.Fatal(err) - } - }) - } -} - -func TestSkipUDPPorts(t *testing.T) { - cases := []struct { - c corev1.Container - ports []string - }{ - { - c: corev1.Container{ - Ports: []corev1.ContainerPort{}, - }, - }, - { - c: corev1.Container{ - Ports: []corev1.ContainerPort{ - { - ContainerPort: 80, - Protocol: corev1.ProtocolTCP, - }, - { - ContainerPort: 8080, - Protocol: corev1.ProtocolTCP, - }, - }, - }, - ports: []string{"80", "8080"}, - }, - { - c: corev1.Container{ - Ports: []corev1.ContainerPort{ - { - ContainerPort: 53, - Protocol: corev1.ProtocolTCP, - }, - { - ContainerPort: 53, - Protocol: corev1.ProtocolUDP, - }, - }, - }, - ports: []string{"53"}, - }, - { - c: corev1.Container{ - Ports: []corev1.ContainerPort{ - { - ContainerPort: 80, - Protocol: corev1.ProtocolTCP, - }, - { - ContainerPort: 53, - Protocol: corev1.ProtocolUDP, - }, - }, - }, - ports: []string{"80"}, - }, - { - c: corev1.Container{ - Ports: []corev1.ContainerPort{ - { - ContainerPort: 53, - Protocol: corev1.ProtocolUDP, - }, - }, - }, - }, - } - for i := range cases { - expectPorts := cases[i].ports - ports := getPortsForContainer(cases[i].c) - if len(ports) != len(expectPorts) { - t.Fatalf("unexpect ports result for case %d", i) - } - for j := 0; j < len(ports); j++ { - if ports[j] != expectPorts[j] { - t.Fatalf("unexpect ports result for case %d: expect %v, got %v", i, expectPorts, ports) - } - } - } -} - -func TestCleanProxyConfig(t *testing.T) { - overrides := mesh.DefaultProxyConfig() - overrides.ConfigPath = "/foo/bar" - overrides.DrainDuration = durationpb.New(7 * time.Second) - overrides.ProxyMetadata = map[string]string{ - "foo": "barr", - } - explicit := mesh.DefaultProxyConfig() - explicit.ConfigPath = constants.ConfigPathDir - explicit.DrainDuration = durationpb.New(45 * time.Second) - cases := []struct { - name string - proxy *meshapi.ProxyConfig - expect string - }{ - { - "default", - mesh.DefaultProxyConfig(), - `{}`, - }, - { - "explicit default", - explicit, - `{}`, - }, - { - "overrides", - overrides, - `{"configPath":"/foo/bar","drainDuration":"7s","proxyMetadata":{"foo":"barr"}}`, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - got := protoToJSON(tt.proxy) - if got != tt.expect { - t.Fatalf("incorrect output: got %v, expected %v", got, tt.expect) - } - roundTrip, err := mesh.ApplyProxyConfig(got, mesh.DefaultMeshConfig()) - if err != nil { - t.Fatal(err) - } - if !cmp.Equal(roundTrip.GetDefaultConfig(), tt.proxy, protocmp.Transform()) { - t.Fatalf("round trip is not identical: got \n%+v, expected \n%+v", *roundTrip.GetDefaultConfig(), tt.proxy) - } - }) - } -} - -func TestAppendMultusNetwork(t *testing.T) { - cases := []struct { - name string - in string - want string - }{ - { - name: "empty", - in: "", - want: "istio-cni", - }, - { - name: "flat-single", - in: "macvlan-conf-1", - want: "macvlan-conf-1, istio-cni", - }, - { - name: "flat-multiple", - in: "macvlan-conf-1, macvlan-conf-2", - want: "macvlan-conf-1, macvlan-conf-2, istio-cni", - }, - { - name: "json-single", - in: `[{"name": "macvlan-conf-1"}]`, - want: `[{"name": "macvlan-conf-1"}, {"name": "istio-cni"}]`, - }, - { - name: "json-multiple", - in: `[{"name": "macvlan-conf-1"}, {"name": "macvlan-conf-2"}]`, - want: `[{"name": "macvlan-conf-1"}, {"name": "macvlan-conf-2"}, {"name": "istio-cni"}]`, - }, - { - name: "json-multiline", - in: `[ - {"name": "macvlan-conf-1"}, - {"name": "macvlan-conf-2"} - ]`, - want: `[ - {"name": "macvlan-conf-1"}, - {"name": "macvlan-conf-2"} - , {"name": "istio-cni"}]`, - }, - { - name: "json-multiline-additional-fields", - in: `[ - {"name": "macvlan-conf-1", "another-field": "another-value"}, - {"name": "macvlan-conf-2"} - ]`, - want: `[ - {"name": "macvlan-conf-1", "another-field": "another-value"}, - {"name": "macvlan-conf-2"} - , {"name": "istio-cni"}]`, - }, - { - name: "json-preconfigured-istio-cni", - in: `[ - {"name": "macvlan-conf-1"}, - {"name": "macvlan-conf-2"}, - {"name": "istio-cni", "config": "additional-config"}, - ]`, - want: `[ - {"name": "macvlan-conf-1"}, - {"name": "macvlan-conf-2"}, - {"name": "istio-cni", "config": "additional-config"}, - ]`, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - actual := appendMultusNetwork(tc.in, "istio-cni") - if actual != tc.want { - t.Fatalf("Unexpected result.\nExpected:\n%v\nActual:\n%v", tc.want, actual) - } - t.Run("idempotency", func(t *testing.T) { - actual := appendMultusNetwork(actual, "istio-cni") - if actual != tc.want { - t.Fatalf("Function is not idempotent.\nExpected:\n%v\nActual:\n%v", tc.want, actual) - } - }) - }) - } -} - -func Test_updateClusterEnvs(t *testing.T) { - type args struct { - container *corev1.Container - newKVs map[string]string - } - tests := []struct { - name string - args args - want *corev1.Container - }{ - { - args: args{ - container: &corev1.Container{}, - newKVs: parseInjectEnvs("/inject/net/network1/cluster/cluster1"), - }, - want: &corev1.Container{ - Env: []corev1.EnvVar{ - { - Name: "ISTIO_META_CLUSTER_ID", - Value: "cluster1", - }, - { - Name: "ISTIO_META_NETWORK", - Value: "network1", - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - updateClusterEnvs(tt.args.container, tt.args.newKVs) - if !cmp.Equal(tt.args.container.Env, tt.want.Env) { - t.Fatalf("updateClusterEnvs got \n%+v, expected \n%+v", tt.args.container.Env, tt.want.Env) - } - }) - } -} - -func TestQuantityConversion(t *testing.T) { - for _, tt := range []struct { - in string - out int - err error - }{ - { - in: "4000m", - out: 4, - }, - { - in: "6500m", - out: 7, - }, - { - in: "200mi", - err: errors.New("unable to parse"), - }, - } { - t.Run(tt.in, func(t *testing.T) { - got, err := quantityToConcurrency(tt.in) - if err != nil { - if tt.err == nil { - t.Errorf("expected no error, got %v", err) - } - } else { - if tt.out != got { - t.Errorf("got %v, want %v", got, tt.out) - } - } - }) - } -} - -func TestProxyImage(t *testing.T) { - val := func(hub string, tag interface{}) *opconfig.Values { - t, _ := structpb.NewValue(tag) - return &opconfig.Values{ - Global: &opconfig.GlobalConfig{ - Hub: hub, - Tag: t, - }, - } - } - pc := func(imageType string) *proxyConfig.ProxyImage { - return &proxyConfig.ProxyImage{ - ImageType: imageType, - } - } - - ann := func(imageType string) map[string]string { - if imageType == "" { - return nil - } - return map[string]string{ - annotation.SidecarProxyImageType.Name: imageType, - } - } - - for _, tt := range []struct { - desc string - v *opconfig.Values - pc *proxyConfig.ProxyImage - ann map[string]string - want string - }{ - { - desc: "vals-only-int-tag", - v: val("docker.io/istio", 11), - want: "docker.io/istio/proxyv2:11", - }, - { - desc: "pc overrides imageType - float tag", - v: val("docker.io/istio", 1.12), - pc: pc("distroless"), - want: "docker.io/istio/proxyv2:1.12-distroless", - }, - { - desc: "annotation overrides imageType", - v: val("gcr.io/gke-release/asm", "1.11.2-asm.17"), - ann: ann("distroless"), - want: "gcr.io/gke-release/asm/proxyv2:1.11.2-asm.17-distroless", - }, - { - desc: "pc and annotation overrides imageType", - v: val("gcr.io/gke-release/asm", "1.11.2-asm.17"), - pc: pc("distroless"), - ann: ann("debug"), - want: "gcr.io/gke-release/asm/proxyv2:1.11.2-asm.17-debug", - }, - { - desc: "pc and annotation overrides imageType, ann is default", - v: val("gcr.io/gke-release/asm", "1.11.2-asm.17"), - pc: pc("debug"), - ann: ann("default"), - want: "gcr.io/gke-release/asm/proxyv2:1.11.2-asm.17", - }, - { - desc: "pc overrides imageType with default, tag also has image type", - v: val("gcr.io/gke-release/asm", "1.11.2-asm.17-distroless"), - pc: pc("default"), - want: "gcr.io/gke-release/asm/proxyv2:1.11.2-asm.17", - }, - { - desc: "ann overrides imageType with default, tag also has image type", - v: val("gcr.io/gke-release/asm", "1.11.2-asm.17-distroless"), - ann: ann("default"), - want: "gcr.io/gke-release/asm/proxyv2:1.11.2-asm.17", - }, - { - desc: "pc overrides imageType, tag also has image type", - v: val("docker.io/istio", "1.12-debug"), - pc: pc("distroless"), - want: "docker.io/istio/proxyv2:1.12-distroless", - }, - { - desc: "annotation overrides imageType, tag also has the same image type", - v: val("docker.io/istio", "1.12-distroless"), - ann: ann("distroless"), - want: "docker.io/istio/proxyv2:1.12-distroless", - }, - { - desc: "unusual tag should work", - v: val("private-repo/istio", "1.12-this-is-unusual-tag"), - want: "private-repo/istio/proxyv2:1.12-this-is-unusual-tag", - }, - { - desc: "unusual tag should work, default override", - v: val("private-repo/istio", "1.12-this-is-unusual-tag-distroless"), - pc: pc("default"), - want: "private-repo/istio/proxyv2:1.12-this-is-unusual-tag", - }, - { - desc: "annotation overrides imageType with unusual tag", - v: val("private-repo/istio", "1.12-this-is-unusual-tag"), - ann: ann("distroless"), - want: "private-repo/istio/proxyv2:1.12-this-is-unusual-tag-distroless", - }, - } { - t.Run(tt.desc, func(t *testing.T) { - got := ProxyImage(tt.v, tt.pc, tt.ann) - if got != tt.want { - t.Errorf("got: <%s>, want <%s> <== value(%v) proxyConfig(%v) ann(%v)", got, tt.want, tt.v, tt.pc, tt.ann) - } - }) - } -} diff --git a/pkg/kube/inject/monitoring.go b/pkg/kube/inject/monitoring.go deleted file mode 100644 index da94d41f7..000000000 --- a/pkg/kube/inject/monitoring.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package inject - -import ( - "istio.io/pkg/monitoring" -) - -var ( - totalInjections = monitoring.NewSum( - "sidecar_injection_requests_total", - "Total number of sidecar injection requests.", - ) - - totalSuccessfulInjections = monitoring.NewSum( - "sidecar_injection_success_total", - "Total number of successful sidecar injection requests.", - ) - - totalFailedInjections = monitoring.NewSum( - "sidecar_injection_failure_total", - "Total number of failed sidecar injection requests.", - ) - - totalSkippedInjections = monitoring.NewSum( - "sidecar_injection_skip_total", - "Total number of skipped sidecar injection requests.", - ) -) - -func init() { - monitoring.MustRegister( - totalInjections, - totalSuccessfulInjections, - totalFailedInjections, - totalSkippedInjections, - ) -} diff --git a/pkg/kube/inject/template.go b/pkg/kube/inject/template.go deleted file mode 100644 index 55fe94f77..000000000 --- a/pkg/kube/inject/template.go +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package inject - -import ( - "encoding/json" - "fmt" - "os" - "path" - "strconv" - "strings" - "text/template" -) - -import ( - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/durationpb" - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/log" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -var InjectionFuncmap = createInjectionFuncmap() - -func createInjectionFuncmap() template.FuncMap { - return template.FuncMap{ - "formatDuration": formatDuration, - "isset": isset, - "excludeInboundPort": excludeInboundPort, - "includeInboundPorts": includeInboundPorts, - "kubevirtInterfaces": kubevirtInterfaces, - "applicationPorts": applicationPorts, - "annotation": getAnnotation, - "valueOrDefault": valueOrDefault, - "toJSON": toJSON, - "toJson": toJSON, // Used by, e.g. Istio 1.0.5 template sidecar-injector-configmap.yaml - "fromJSON": fromJSON, - "structToJSON": structToJSON, - "protoToJSON": protoToJSON, - "toYaml": toYaml, - "indent": indent, - "directory": directory, - "contains": flippedContains, - "toLower": strings.ToLower, - "appendMultusNetwork": appendMultusNetwork, - "env": env, - } -} - -// Allows the template to use env variables from istiod. -// Istiod will use a custom template, without 'values.yaml', and the pod will have -// an optional 'vendor' configmap where additional settings can be defined. -func env(key string, def string) string { - val := os.Getenv(key) - if val == "" { - return def - } - return val -} - -func formatDuration(in *durationpb.Duration) string { - return in.AsDuration().String() -} - -func isset(m map[string]string, key string) bool { - _, ok := m[key] - return ok -} - -func directory(filepath string) string { - dir, _ := path.Split(filepath) - return dir -} - -func flippedContains(needle, haystack string) bool { - return strings.Contains(haystack, needle) -} - -func excludeInboundPort(port interface{}, excludedInboundPorts string) string { - portStr := strings.TrimSpace(fmt.Sprint(port)) - if len(portStr) == 0 || portStr == "0" { - // Nothing to do. - return excludedInboundPorts - } - - // Exclude the readiness port if not already excluded. - ports := splitPorts(excludedInboundPorts) - outPorts := make([]string, 0, len(ports)) - for _, port := range ports { - if port == portStr { - // The port is already excluded. - return excludedInboundPorts - } - port = strings.TrimSpace(port) - if len(port) > 0 { - outPorts = append(outPorts, port) - } - } - - // The port was not already excluded - exclude it now. - outPorts = append(outPorts, portStr) - return strings.Join(outPorts, ",") -} - -func valueOrDefault(value interface{}, defaultValue interface{}) interface{} { - if value == "" || value == nil { - return defaultValue - } - return value -} - -func toJSON(m map[string]string) string { - if m == nil { - return "{}" - } - - ba, err := json.Marshal(m) - if err != nil { - log.Warnf("Unable to marshal %v", m) - return "{}" - } - - return string(ba) -} - -func fromJSON(j string) interface{} { - var m interface{} - err := json.Unmarshal([]byte(j), &m) - if err != nil { - log.Warnf("Unable to unmarshal %s", j) - return "{}" - } - - return m -} - -func indent(spaces int, source string) string { - res := strings.Split(source, "\n") - for i, line := range res { - if i > 0 { - res[i] = fmt.Sprintf(fmt.Sprintf("%% %ds%%s", spaces), "", line) - } - } - return strings.Join(res, "\n") -} - -func toYaml(value interface{}) string { - y, err := yaml.Marshal(value) - if err != nil { - log.Warnf("Unable to marshal %v", value) - return "" - } - - return string(y) -} - -func getAnnotation(meta metav1.ObjectMeta, name string, defaultValue interface{}) string { - value, ok := meta.Annotations[name] - if !ok { - value = fmt.Sprint(defaultValue) - } - return value -} - -func appendMultusNetwork(existingValue, istioCniNetwork string) string { - if existingValue == "" { - return istioCniNetwork - } - i := strings.LastIndex(existingValue, "]") - isJSON := i != -1 - if isJSON { - networks := []map[string]interface{}{} - err := json.Unmarshal([]byte(existingValue), &networks) - if err != nil { - // existingValue is not valid JSON; nothing we can do but skip injection - log.Warnf("Unable to unmarshal Multus Network annotation JSON value: %v", err) - return existingValue - } - for _, net := range networks { - if net["name"] == istioCniNetwork { - return existingValue - } - } - return existingValue[0:i] + fmt.Sprintf(`, {"name": "%s"}`, istioCniNetwork) + existingValue[i:] - } - for _, net := range strings.Split(existingValue, ",") { - if strings.TrimSpace(net) == istioCniNetwork { - return existingValue - } - } - return existingValue + ", " + istioCniNetwork -} - -// this function is no longer used by the template but kept around for backwards compatibility -func applicationPorts(containers []corev1.Container) string { - return getContainerPorts(containers, func(c corev1.Container) bool { - return c.Name != ProxyContainerName - }) -} - -func includeInboundPorts(containers []corev1.Container) string { - // Include the ports from all containers in the deployment. - return getContainerPorts(containers, func(corev1.Container) bool { return true }) -} - -func getPortsForContainer(container corev1.Container) []string { - parts := make([]string, 0) - for _, p := range container.Ports { - if p.Protocol == corev1.ProtocolUDP || p.Protocol == corev1.ProtocolSCTP { - continue - } - parts = append(parts, strconv.Itoa(int(p.ContainerPort))) - } - return parts -} - -func getContainerPorts(containers []corev1.Container, shouldIncludePorts func(corev1.Container) bool) string { - parts := make([]string, 0) - for _, c := range containers { - if shouldIncludePorts(c) { - parts = append(parts, getPortsForContainer(c)...) - } - } - - return strings.Join(parts, ",") -} - -func kubevirtInterfaces(s string) string { - return s -} - -func structToJSON(v interface{}) string { - if v == nil { - return "{}" - } - - ba, err := json.Marshal(v) - if err != nil { - log.Warnf("Unable to marshal %v", v) - return "{}" - } - - return string(ba) -} - -func protoToJSON(v proto.Message) string { - v = cleanProxyConfig(v) - if v == nil { - return "{}" - } - - ba, err := protomarshal.ToJSON(v) - if err != nil { - log.Warnf("Unable to marshal %v: %v", v, err) - return "{}" - } - - return ba -} - -// Rather than dump the entire proxy config, we remove fields that are default -// This makes the pod spec much smaller -// This is not comprehensive code, but nothing will break if this misses some fields -func cleanProxyConfig(msg proto.Message) proto.Message { - originalProxyConfig, ok := msg.(*meshconfig.ProxyConfig) - if !ok || originalProxyConfig == nil { - return msg - } - pc := proto.Clone(originalProxyConfig).(*meshconfig.ProxyConfig) - defaults := mesh.DefaultProxyConfig() - if pc.ConfigPath == defaults.ConfigPath { - pc.ConfigPath = "" - } - if pc.BinaryPath == defaults.BinaryPath { - pc.BinaryPath = "" - } - if pc.ControlPlaneAuthPolicy == defaults.ControlPlaneAuthPolicy { - pc.ControlPlaneAuthPolicy = 0 - } - if x, ok := pc.GetClusterName().(*meshconfig.ProxyConfig_ServiceCluster); ok { - if x.ServiceCluster == defaults.GetClusterName().(*meshconfig.ProxyConfig_ServiceCluster).ServiceCluster { - pc.ClusterName = nil - } - } - - if proto.Equal(pc.DrainDuration, defaults.DrainDuration) { - pc.DrainDuration = nil - } - if proto.Equal(pc.TerminationDrainDuration, defaults.TerminationDrainDuration) { - pc.TerminationDrainDuration = nil - } - if proto.Equal(pc.ParentShutdownDuration, defaults.ParentShutdownDuration) { - pc.ParentShutdownDuration = nil - } - if pc.DiscoveryAddress == defaults.DiscoveryAddress { - pc.DiscoveryAddress = "" - } - if proto.Equal(pc.EnvoyMetricsService, defaults.EnvoyMetricsService) { - pc.EnvoyMetricsService = nil - } - if proto.Equal(pc.EnvoyAccessLogService, defaults.EnvoyAccessLogService) { - pc.EnvoyAccessLogService = nil - } - if proto.Equal(pc.Tracing, defaults.Tracing) { - pc.Tracing = nil - } - if pc.ProxyAdminPort == defaults.ProxyAdminPort { - pc.ProxyAdminPort = 0 - } - if pc.StatNameLength == defaults.StatNameLength { - pc.StatNameLength = 0 - } - if pc.StatusPort == defaults.StatusPort { - pc.StatusPort = 0 - } - if proto.Equal(pc.Concurrency, defaults.Concurrency) { - pc.Concurrency = nil - } - if len(pc.ProxyMetadata) == 0 { - pc.ProxyMetadata = nil - } - return proto.Message(pc) -} diff --git a/pkg/kube/inject/testdata/inject/auth.non-default-service-account.yaml b/pkg/kube/inject/testdata/inject/auth.non-default-service-account.yaml deleted file mode 100644 index 3015f0515..000000000 --- a/pkg/kube/inject/testdata/inject/auth.non-default-service-account.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - serviceAccountName: non-default - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/auth.non-default-service-account.yaml.injected b/pkg/kube/inject/testdata/inject/auth.non-default-service-account.yaml.injected deleted file mode 100644 index 6baf77bcf..000000000 --- a/pkg/kube/inject/testdata/inject/auth.non-default-service-account.yaml.injected +++ /dev/null @@ -1,216 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - serviceAccountName: non-default - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/auth.yaml b/pkg/kube/inject/testdata/inject/auth.yaml deleted file mode 100644 index a7cff42d8..000000000 --- a/pkg/kube/inject/testdata/inject/auth.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/auth.yaml.injected b/pkg/kube/inject/testdata/inject/auth.yaml.injected deleted file mode 100644 index 4a48b3124..000000000 --- a/pkg/kube/inject/testdata/inject/auth.yaml.injected +++ /dev/null @@ -1,215 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/cronjob.yaml b/pkg/kube/inject/testdata/inject/cronjob.yaml deleted file mode 100644 index 4b8108a0e..000000000 --- a/pkg/kube/inject/testdata/inject/cronjob.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: batch/v2alpha1 -kind: CronJob -metadata: - name: hellocron -spec: - schedule: "*/1 * * * *" - jobTemplate: - spec: - template: - spec: - containers: - - name: hello - image: busybox - args: - - /bin/sh - - -c - - date; echo Hello from the Kubernetes cluster - restartPolicy: OnFailure diff --git a/pkg/kube/inject/testdata/inject/cronjob.yaml.injected b/pkg/kube/inject/testdata/inject/cronjob.yaml.injected deleted file mode 100644 index 91f781e7a..000000000 --- a/pkg/kube/inject/testdata/inject/cronjob.yaml.injected +++ /dev/null @@ -1,211 +0,0 @@ -apiVersion: batch/v2alpha1 -kind: CronJob -metadata: - creationTimestamp: null - name: hellocron -spec: - jobTemplate: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hellocron - service.istio.io/canonical-revision: latest - spec: - template: - metadata: - creationTimestamp: null - spec: - containers: - - args: - - /bin/sh - - -c - - date; echo Hello from the Kubernetes cluster - image: busybox - name: hello - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hellocron - - name: ISTIO_META_OWNER - value: kubernetes://apis/batch/v2alpha1/namespaces/default/cronjobs/hellocron - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - restartPolicy: OnFailure - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert - schedule: '*/1 * * * *' -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/custom-template.iop.yaml b/pkg/kube/inject/testdata/inject/custom-template.iop.yaml deleted file mode 100644 index f14eb2639..000000000 --- a/pkg/kube/inject/testdata/inject/custom-template.iop.yaml +++ /dev/null @@ -1,37 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -metadata: - namespace: dubbo-system - name: example-istiocontrolplane -spec: - values: - sidecarInjectorWebhook: - templates: - custom: | - metadata: - annotations: - # Disable the built-in transformations. In the future we may want a template-level API - prometheus.istio.io/merge-metrics: "false" - sidecar.istio.io/rewriteAppHTTPProbers: "false" - foo: bar - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: SOME_ENV - value: "true" - - name: SOME_FILE - value: /var/lib/data/foo.json - volumeMounts: - - mountPath: /var/lib/data/foo.json - subPath: foo.json - name: some-injected-file - {{- end}} - volumes: - - name: some-injected-file - downwardAPI: - items: - - path: foo.json - fieldRef: - fieldPath: "metadata.annotations['foo']" diff --git a/pkg/kube/inject/testdata/inject/custom-template.yaml b/pkg/kube/inject/testdata/inject/custom-template.yaml deleted file mode 100644 index 0d1914bac..000000000 --- a/pkg/kube/inject/testdata/inject/custom-template.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - selector: - matchLabels: - app: hello - template: - metadata: - annotations: - inject.istio.io/templates: "custom" - labels: - app: hello - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - readinessProbe: - httpGet: - port: 80 diff --git a/pkg/kube/inject/testdata/inject/custom-template.yaml.injected b/pkg/kube/inject/testdata/inject/custom-template.yaml.injected deleted file mode 100644 index 8fd7ecade..000000000 --- a/pkg/kube/inject/testdata/inject/custom-template.yaml.injected +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - selector: - matchLabels: - app: hello - strategy: {} - template: - metadata: - annotations: - foo: bar - inject.istio.io/templates: custom - prometheus.istio.io/merge-metrics: "false" - proxy.istio.io/overrides: '{"containers":[{"name":"hello","image":"fake.docker.io/google-samples/hello-go-gke:1.0","resources":{},"readinessProbe":{"httpGet":{"port":80}}}]}' - sidecar.istio.io/rewriteAppHTTPProbers: "false" - sidecar.istio.io/status: '{"initContainers":null,"containers":["hello"],"volumes":["some-injected-file"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - spec: - containers: - - env: - - name: SOME_ENV - value: "true" - - name: SOME_FILE - value: /var/lib/data/foo.json - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - readinessProbe: - httpGet: - port: 80 - resources: {} - volumeMounts: - - mountPath: /var/lib/data/foo.json - name: some-injected-file - subPath: foo.json - volumes: - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.annotations['foo'] - path: foo.json - name: some-injected-file -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/daemonset.yaml b/pkg/kube/inject/testdata/inject/daemonset.yaml deleted file mode 100644 index 16fa77f6e..000000000 --- a/pkg/kube/inject/testdata/inject/daemonset.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: apps/v1 -kind: DaemonSet -metadata: - name: hello -spec: - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/daemonset.yaml.injected b/pkg/kube/inject/testdata/inject/daemonset.yaml.injected deleted file mode 100644 index 29c6ddfe1..000000000 --- a/pkg/kube/inject/testdata/inject/daemonset.yaml.injected +++ /dev/null @@ -1,218 +0,0 @@ -apiVersion: apps/v1 -kind: DaemonSet -metadata: - creationTimestamp: null - name: hello -spec: - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/daemonsets/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert - updateStrategy: {} -status: - currentNumberScheduled: 0 - desiredNumberScheduled: 0 - numberMisscheduled: 0 - numberReady: 0 ---- diff --git a/pkg/kube/inject/testdata/inject/deploymentconfig-multi.yaml b/pkg/kube/inject/testdata/inject/deploymentconfig-multi.yaml deleted file mode 100644 index 575dba725..000000000 --- a/pkg/kube/inject/testdata/inject/deploymentconfig-multi.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: v1 -kind: List -items: - - kind: Service - apiVersion: v1 - metadata: - name: frontend - spec: - selector: - app: hello - tier: frontend - ports: - - protocol: "TCP" - port: 80 - targetPort: 80 - type: LoadBalancer - - - apiVersion: apps.openshift.io/v1 - kind: DeploymentConfig - metadata: - name: hello - spec: - replicas: 7 - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - triggers: - - type: "ConfigChange" - - type: "ImageChange" - imageChangeParams: - automatic: true - containerNames: - - "helloworld" - from: - kind: "ImageStreamTag" - name: "hello-go-gke:1.0" - strategy: - type: "Rolling" - paused: false - revisionHistoryLimit: 2 - minReadySeconds: 0 diff --git a/pkg/kube/inject/testdata/inject/deploymentconfig-multi.yaml.injected b/pkg/kube/inject/testdata/inject/deploymentconfig-multi.yaml.injected deleted file mode 100644 index a403ba772..000000000 --- a/pkg/kube/inject/testdata/inject/deploymentconfig-multi.yaml.injected +++ /dev/null @@ -1,247 +0,0 @@ -apiVersion: v1 -items: -- apiVersion: v1 - kind: Service - metadata: - name: frontend - spec: - ports: - - port: 80 - protocol: TCP - targetPort: 80 - selector: - app: hello - tier: frontend - type: LoadBalancer -- apiVersion: apps.openshift.io/v1 - kind: DeploymentConfig - metadata: - creationTimestamp: null - name: hello - spec: - replicas: 7 - revisionHistoryLimit: 2 - strategy: - resources: {} - type: Rolling - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps.openshift.io/v1/namespaces/default/deploymentconfigs/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert - test: false - triggers: - - type: ConfigChange - - imageChangeParams: - automatic: true - containerNames: - - helloworld - from: - kind: ImageStreamTag - name: hello-go-gke:1.0 - type: ImageChange - status: - availableReplicas: 0 - latestVersion: 0 - observedGeneration: 0 - replicas: 0 - unavailableReplicas: 0 - updatedReplicas: 0 -kind: List -metadata: {} ---- diff --git a/pkg/kube/inject/testdata/inject/deploymentconfig-with-canonical-service-label.yaml b/pkg/kube/inject/testdata/inject/deploymentconfig-with-canonical-service-label.yaml deleted file mode 100644 index a1d499d20..000000000 --- a/pkg/kube/inject/testdata/inject/deploymentconfig-with-canonical-service-label.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: v1 -kind: DeploymentConfig -metadata: - name: hello -spec: - replicas: 7 - template: - metadata: - labels: - app: hello - tier: backend - track: stable - service.istio.io/canonical-name: test-service-name - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - triggers: - - type: "ConfigChange" - - type: "ImageChange" - imageChangeParams: - automatic: true - containerNames: - - "helloworld" - from: - kind: "ImageStreamTag" - name: "hello-go-gke:1.0" - strategy: - type: "Rolling" - paused: false - revisionHistoryLimit: 2 - minReadySeconds: 0 diff --git a/pkg/kube/inject/testdata/inject/deploymentconfig-with-canonical-service-label.yaml.injected b/pkg/kube/inject/testdata/inject/deploymentconfig-with-canonical-service-label.yaml.injected deleted file mode 100644 index d3dab8c49..000000000 --- a/pkg/kube/inject/testdata/inject/deploymentconfig-with-canonical-service-label.yaml.injected +++ /dev/null @@ -1,230 +0,0 @@ -apiVersion: v1 -kind: DeploymentConfig -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - revisionHistoryLimit: 2 - strategy: - resources: {} - type: Rolling - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: test-service-name - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/v1/namespaces/default/deploymentconfigs/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert - test: false - triggers: - - type: ConfigChange - - imageChangeParams: - automatic: true - containerNames: - - helloworld - from: - kind: ImageStreamTag - name: hello-go-gke:1.0 - type: ImageChange -status: - availableReplicas: 0 - latestVersion: 0 - observedGeneration: 0 - replicas: 0 - unavailableReplicas: 0 - updatedReplicas: 0 ---- diff --git a/pkg/kube/inject/testdata/inject/deploymentconfig.yaml b/pkg/kube/inject/testdata/inject/deploymentconfig.yaml deleted file mode 100644 index 6ad102e99..000000000 --- a/pkg/kube/inject/testdata/inject/deploymentconfig.yaml +++ /dev/null @@ -1,34 +0,0 @@ -apiVersion: apps.openshift.io/v1 -kind: DeploymentConfig -metadata: - name: hello -spec: - replicas: 7 - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - triggers: - - type: "ConfigChange" - - type: "ImageChange" - imageChangeParams: - automatic: true - containerNames: - - "helloworld" - from: - kind: "ImageStreamTag" - name: "hello-go-gke:1.0" - strategy: - type: "Rolling" - paused: false - revisionHistoryLimit: 2 - minReadySeconds: 0 diff --git a/pkg/kube/inject/testdata/inject/deploymentconfig.yaml.injected b/pkg/kube/inject/testdata/inject/deploymentconfig.yaml.injected deleted file mode 100644 index 9f8ce566b..000000000 --- a/pkg/kube/inject/testdata/inject/deploymentconfig.yaml.injected +++ /dev/null @@ -1,230 +0,0 @@ -apiVersion: apps.openshift.io/v1 -kind: DeploymentConfig -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - revisionHistoryLimit: 2 - strategy: - resources: {} - type: Rolling - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps.openshift.io/v1/namespaces/default/deploymentconfigs/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert - test: false - triggers: - - type: ConfigChange - - imageChangeParams: - automatic: true - containerNames: - - helloworld - from: - kind: ImageStreamTag - name: hello-go-gke:1.0 - type: ImageChange -status: - availableReplicas: 0 - latestVersion: 0 - observedGeneration: 0 - replicas: 0 - unavailableReplicas: 0 - updatedReplicas: 0 ---- diff --git a/pkg/kube/inject/testdata/inject/enable-core-dump-annotation.yaml b/pkg/kube/inject/testdata/inject/enable-core-dump-annotation.yaml deleted file mode 100644 index cc12bda67..000000000 --- a/pkg/kube/inject/testdata/inject/enable-core-dump-annotation.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - annotations: - sidecar.istio.io/enableCoreDump: "true" - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/enable-core-dump-annotation.yaml.injected b/pkg/kube/inject/testdata/inject/enable-core-dump-annotation.yaml.injected deleted file mode 100644 index 583f33cb7..000000000 --- a/pkg/kube/inject/testdata/inject/enable-core-dump-annotation.yaml.injected +++ /dev/null @@ -1,243 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/enableCoreDump: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init","enable-core-dump"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c - unlimited - command: - - /bin/sh - image: apache/dubbo-agent:latest - name: enable-core-dump - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/enable-core-dump.yaml b/pkg/kube/inject/testdata/inject/enable-core-dump.yaml deleted file mode 100644 index a7cff42d8..000000000 --- a/pkg/kube/inject/testdata/inject/enable-core-dump.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/enable-core-dump.yaml.injected b/pkg/kube/inject/testdata/inject/enable-core-dump.yaml.injected deleted file mode 100644 index bb7609605..000000000 --- a/pkg/kube/inject/testdata/inject/enable-core-dump.yaml.injected +++ /dev/null @@ -1,242 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init","enable-core-dump"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c - unlimited - command: - - /bin/sh - image: apache/dubbo-agent:latest - name: enable-core-dump - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/explicit-security-context.yaml b/pkg/kube/inject/testdata/inject/explicit-security-context.yaml deleted file mode 100644 index 459e6dd6d..000000000 --- a/pkg/kube/inject/testdata/inject/explicit-security-context.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - selector: - matchLabels: - app: hello - template: - metadata: - labels: - app: hello - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - # We expect this to get overwritten to 1337 - securityContext: - fsGroup: 1234 diff --git a/pkg/kube/inject/testdata/inject/explicit-security-context.yaml.injected b/pkg/kube/inject/testdata/inject/explicit-security-context.yaml.injected deleted file mode 100644 index d170c6b3e..000000000 --- a/pkg/kube/inject/testdata/inject/explicit-security-context.yaml.injected +++ /dev/null @@ -1,206 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - selector: - matchLabels: - app: hello - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/format-duration.yaml b/pkg/kube/inject/testdata/inject/format-duration.yaml deleted file mode 100644 index a7cff42d8..000000000 --- a/pkg/kube/inject/testdata/inject/format-duration.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/format-duration.yaml.injected b/pkg/kube/inject/testdata/inject/format-duration.yaml.injected deleted file mode 100644 index 0efe5baef..000000000 --- a/pkg/kube/inject/testdata/inject/format-duration.yaml.injected +++ /dev/null @@ -1,215 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {"drainDuration":"23s","parentShutdownDuration":"42s"} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/frontend.yaml b/pkg/kube/inject/testdata/inject/frontend.yaml deleted file mode 100644 index 44589d2b8..000000000 --- a/pkg/kube/inject/testdata/inject/frontend.yaml +++ /dev/null @@ -1,39 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: frontend -spec: - selector: - app: hello - tier: frontend - ports: - - protocol: "TCP" - port: 80 - targetPort: 80 - type: LoadBalancer ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: frontend -spec: - replicas: 1 - selector: - matchLabels: - app: hello - tier: frontend - track: stable - template: - metadata: - labels: - app: hello - tier: frontend - track: stable - spec: - containers: - - name: nginx - image: "fake.docker.io/google-samples/hello-frontend:1.0" - lifecycle: - preStop: - exec: - command: ["/usr/sbin/nginx","-s","quit"] diff --git a/pkg/kube/inject/testdata/inject/frontend.yaml.injected b/pkg/kube/inject/testdata/inject/frontend.yaml.injected deleted file mode 100644 index 227209df6..000000000 --- a/pkg/kube/inject/testdata/inject/frontend.yaml.injected +++ /dev/null @@ -1,232 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: frontend -spec: - selector: - app: hello - tier: frontend - ports: - - protocol: "TCP" - port: 80 - targetPort: 80 - type: LoadBalancer ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: frontend -spec: - replicas: 1 - selector: - matchLabels: - app: hello - tier: frontend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: nginx - kubectl.kubernetes.io/default-logs-container: nginx - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: frontend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-frontend:1.0 - lifecycle: - preStop: - exec: - command: - - /usr/sbin/nginx - - -s - - quit - name: nginx - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - ] - - name: ISTIO_META_APP_CONTAINERS - value: nginx - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: frontend - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/frontend - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/grpc-agent.yaml b/pkg/kube/inject/testdata/inject/grpc-agent.yaml deleted file mode 100644 index 35b24d733..000000000 --- a/pkg/kube/inject/testdata/inject/grpc-agent.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: grpc -spec: - selector: - matchLabels: - app: grpc - template: - metadata: - annotations: - inject.istio.io/templates: grpc-agent - labels: - app: grpc - spec: - containers: - - name: traffic - image: "fake.docker.io/google-samples/traffic-go-gke:1.0" - readinessProbe: - httpGet: - port: 80 diff --git a/pkg/kube/inject/testdata/inject/grpc-agent.yaml.injected b/pkg/kube/inject/testdata/inject/grpc-agent.yaml.injected deleted file mode 100644 index 9f9873142..000000000 --- a/pkg/kube/inject/testdata/inject/grpc-agent.yaml.injected +++ /dev/null @@ -1,170 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: grpc -spec: - selector: - matchLabels: - app: grpc - strategy: {} - template: - metadata: - annotations: - inject.istio.io/templates: grpc-agent - kubectl.kubernetes.io/default-container: traffic - kubectl.kubernetes.io/default-logs-container: traffic - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - proxy.istio.io/overrides: '{"containers":[{"name":"traffic","image":"fake.docker.io/google-samples/traffic-go-gke:1.0","resources":{},"readinessProbe":{"httpGet":{"port":80}}}]}' - sidecar.istio.io/rewriteAppHTTPProbers: "false" - sidecar.istio.io/status: '{"initContainers":null,"containers":["traffic","istio-proxy"],"volumes":["workload-socket","workload-certs","istio-xds","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: grpc - service.istio.io/canonical-name: grpc - service.istio.io/canonical-revision: latest - spec: - containers: - - env: - - name: GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT - value: "true" - - name: GRPC_XDS_BOOTSTRAP - value: /etc/istio/proxy/grpc-bootstrap.json - image: fake.docker.io/google-samples/traffic-go-gke:1.0 - name: traffic - readinessProbe: - httpGet: - port: 80 - resources: {} - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-xds - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --log_output_level=default:info - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - ] - - name: ISTIO_META_APP_CONTAINERS - value: traffic - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: grpc - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/grpc - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - image: apache/dubbo-agent:latest - name: istio-proxy - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15020 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-xds - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - - emptyDir: - medium: Memory - name: istio-xds - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/grpc-simple.yaml b/pkg/kube/inject/testdata/inject/grpc-simple.yaml deleted file mode 100644 index 5e7adef08..000000000 --- a/pkg/kube/inject/testdata/inject/grpc-simple.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: grpc -spec: - selector: - matchLabels: - app: grpc - template: - metadata: - annotations: - inject.istio.io/templates: grpc-simple - labels: - app: grpc - spec: - containers: - - name: traffic - image: "fake.docker.io/google-samples/traffic-go-gke:1.0" - readinessProbe: - httpGet: - port: 80 diff --git a/pkg/kube/inject/testdata/inject/grpc-simple.yaml.injected b/pkg/kube/inject/testdata/inject/grpc-simple.yaml.injected deleted file mode 100644 index e9de00533..000000000 --- a/pkg/kube/inject/testdata/inject/grpc-simple.yaml.injected +++ /dev/null @@ -1,90 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: grpc -spec: - selector: - matchLabels: - app: grpc - strategy: {} - template: - metadata: - annotations: - inject.istio.io/templates: grpc-simple - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - proxy.istio.io/overrides: '{"containers":[{"name":"traffic","image":"fake.docker.io/google-samples/traffic-go-gke:1.0","resources":{},"readinessProbe":{"httpGet":{"port":80}}}]}' - sidecar.istio.io/status: '{"initContainers":["grpc-bootstrap-init"],"containers":["traffic"],"volumes":["grpc-io-proxyless-bootstrap"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: grpc - spec: - containers: - - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - image: fake.docker.io/google-samples/traffic-go-gke:1.0 - name: traffic - readinessProbe: - httpGet: - port: 80 - resources: {} - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - initContainers: - - command: - - sh - - -c - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - dubbo-system - image: busybox:1.28 - name: grpc-bootstrap-init - resources: {} - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - volumes: - - emptyDir: {} - name: grpc-io-proxyless-bootstrap -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-always.yaml.injected b/pkg/kube/inject/testdata/inject/hello-always.yaml.injected deleted file mode 100644 index 03a2829c2..000000000 --- a/pkg/kube/inject/testdata/inject/hello-always.yaml.injected +++ /dev/null @@ -1,217 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - imagePullPolicy: Always - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - imagePullPolicy: Always - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-cncf-networks.yaml.injected b/pkg/kube/inject/testdata/inject/hello-cncf-networks.yaml.injected deleted file mode 100644 index d5ee453f9..000000000 --- a/pkg/kube/inject/testdata/inject/hello-cncf-networks.yaml.injected +++ /dev/null @@ -1,219 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - k8s.v1.cni.cncf.io/networks: istio-cni - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/interceptionMode: REDIRECT - sidecar.istio.io/status: '{"initContainers":["istio-validation"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - traffic.sidecar.istio.io/excludeInboundPorts: "15020" - traffic.sidecar.istio.io/includeInboundPorts: '*' - traffic.sidecar.istio.io/includeOutboundIPRanges: '*' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - - --run-validation - - --skip-rule-apply - image: apache/dubbo-agent:latest - name: istio-validation - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-existing-cncf-networks-json.yaml b/pkg/kube/inject/testdata/inject/hello-existing-cncf-networks-json.yaml deleted file mode 100644 index 660791eba..000000000 --- a/pkg/kube/inject/testdata/inject/hello-existing-cncf-networks-json.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - annotations: - k8s.v1.cni.cncf.io/networks: '[{"name": "alt_cni"}]' - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/hello-existing-cncf-networks-json.yaml.injected b/pkg/kube/inject/testdata/inject/hello-existing-cncf-networks-json.yaml.injected deleted file mode 100644 index 2dd545efc..000000000 --- a/pkg/kube/inject/testdata/inject/hello-existing-cncf-networks-json.yaml.injected +++ /dev/null @@ -1,219 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - k8s.v1.cni.cncf.io/networks: '[{"name": "alt_cni"}, {"name": "istio-cni"}]' - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/interceptionMode: REDIRECT - sidecar.istio.io/status: '{"initContainers":["istio-validation"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - traffic.sidecar.istio.io/excludeInboundPorts: "15020" - traffic.sidecar.istio.io/includeInboundPorts: '*' - traffic.sidecar.istio.io/includeOutboundIPRanges: '*' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - - --run-validation - - --skip-rule-apply - image: apache/dubbo-agent:latest - name: istio-validation - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-existing-cncf-networks.yaml b/pkg/kube/inject/testdata/inject/hello-existing-cncf-networks.yaml deleted file mode 100644 index a6411fbcc..000000000 --- a/pkg/kube/inject/testdata/inject/hello-existing-cncf-networks.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - annotations: - k8s.v1.cni.cncf.io/networks: alt_cni - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/hello-existing-cncf-networks.yaml.injected b/pkg/kube/inject/testdata/inject/hello-existing-cncf-networks.yaml.injected deleted file mode 100644 index 5d0812dd9..000000000 --- a/pkg/kube/inject/testdata/inject/hello-existing-cncf-networks.yaml.injected +++ /dev/null @@ -1,219 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - k8s.v1.cni.cncf.io/networks: alt_cni, istio-cni - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/interceptionMode: REDIRECT - sidecar.istio.io/status: '{"initContainers":["istio-validation"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - traffic.sidecar.istio.io/excludeInboundPorts: "15020" - traffic.sidecar.istio.io/includeInboundPorts: '*' - traffic.sidecar.istio.io/includeOutboundIPRanges: '*' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - - --run-validation - - --skip-rule-apply - image: apache/dubbo-agent:latest - name: istio-validation - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-host-network-with-ns.yaml b/pkg/kube/inject/testdata/inject/hello-host-network-with-ns.yaml deleted file mode 100644 index 50a3758a9..000000000 --- a/pkg/kube/inject/testdata/inject/hello-host-network-with-ns.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello-host-network - namespace: sample -spec: - replicas: 7 - selector: - matchLabels: - app: hello-host-network - tier: backend - track: stable - template: - metadata: - labels: - app: hello-host-network - tier: backend - track: stable - spec: - containers: - - name: hello-host-network - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - hostNetwork: true diff --git a/pkg/kube/inject/testdata/inject/hello-host-network-with-ns.yaml.injected b/pkg/kube/inject/testdata/inject/hello-host-network-with-ns.yaml.injected deleted file mode 100644 index ce27e92d9..000000000 --- a/pkg/kube/inject/testdata/inject/hello-host-network-with-ns.yaml.injected +++ /dev/null @@ -1,32 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello-host-network - namespace: sample -spec: - replicas: 7 - selector: - matchLabels: - app: hello-host-network - tier: backend - track: stable - strategy: {} - template: - metadata: - creationTimestamp: null - labels: - app: hello-host-network - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello-host-network - ports: - - containerPort: 80 - name: http - resources: {} - hostNetwork: true -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-host-network.yaml b/pkg/kube/inject/testdata/inject/hello-host-network.yaml deleted file mode 100644 index c0c09c5ed..000000000 --- a/pkg/kube/inject/testdata/inject/hello-host-network.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello-host-network -spec: - replicas: 7 - selector: - matchLabels: - app: hello-host-network - tier: backend - track: stable - template: - metadata: - labels: - app: hello-host-network - tier: backend - track: stable - spec: - containers: - - name: hello-host-network - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - hostNetwork: true \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inject/hello-host-network.yaml.injected b/pkg/kube/inject/testdata/inject/hello-host-network.yaml.injected deleted file mode 100644 index ae33f6357..000000000 --- a/pkg/kube/inject/testdata/inject/hello-host-network.yaml.injected +++ /dev/null @@ -1,31 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello-host-network -spec: - replicas: 7 - selector: - matchLabels: - app: hello-host-network - tier: backend - track: stable - strategy: {} - template: - metadata: - creationTimestamp: null - labels: - app: hello-host-network - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello-host-network - ports: - - containerPort: 80 - name: http - resources: {} - hostNetwork: true -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-ignore.yaml b/pkg/kube/inject/testdata/inject/hello-ignore.yaml deleted file mode 100644 index 45996a30c..000000000 --- a/pkg/kube/inject/testdata/inject/hello-ignore.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - annotations: - sidecar.istio.io/inject: "false" - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/hello-ignore.yaml.injected b/pkg/kube/inject/testdata/inject/hello-ignore.yaml.injected deleted file mode 100644 index d2ec77b90..000000000 --- a/pkg/kube/inject/testdata/inject/hello-ignore.yaml.injected +++ /dev/null @@ -1,32 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - sidecar.istio.io/inject: "false" - creationTimestamp: null - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-image-pull-secret.yaml b/pkg/kube/inject/testdata/inject/hello-image-pull-secret.yaml deleted file mode 100644 index ee5960a3e..000000000 --- a/pkg/kube/inject/testdata/inject/hello-image-pull-secret.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - imagePullSecrets: - - name: fooSecret - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/hello-image-pull-secret.yaml.injected b/pkg/kube/inject/testdata/inject/hello-image-pull-secret.yaml.injected deleted file mode 100644 index ac93dff4d..000000000 --- a/pkg/kube/inject/testdata/inject/hello-image-pull-secret.yaml.injected +++ /dev/null @@ -1,217 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - imagePullSecrets: - - name: fooSecret - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-image-secrets-in-values.iop.yaml b/pkg/kube/inject/testdata/inject/hello-image-secrets-in-values.iop.yaml deleted file mode 100644 index 510ee20f1..000000000 --- a/pkg/kube/inject/testdata/inject/hello-image-secrets-in-values.iop.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -metadata: - namespace: dubbo-system - name: example-istiocontrolplane -spec: - values: - global: - imagePullSecrets: - - "barSecret" diff --git a/pkg/kube/inject/testdata/inject/hello-image-secrets-in-values.yaml.injected b/pkg/kube/inject/testdata/inject/hello-image-secrets-in-values.yaml.injected deleted file mode 100644 index fc7d3830b..000000000 --- a/pkg/kube/inject/testdata/inject/hello-image-secrets-in-values.yaml.injected +++ /dev/null @@ -1,217 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":["barSecret"],"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - imagePullSecrets: - - name: barSecret - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-mount-mtls-certs.yaml.injected b/pkg/kube/inject/testdata/inject/hello-mount-mtls-certs.yaml.injected deleted file mode 100644 index 25d16b66f..000000000 --- a/pkg/kube/inject/testdata/inject/hello-mount-mtls-certs.yaml.injected +++ /dev/null @@ -1,222 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert","istio-certs"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert - - name: istio-certs - secret: - optional: true - secretName: istio.default -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-mtls-not-ready.yaml b/pkg/kube/inject/testdata/inject/hello-mtls-not-ready.yaml deleted file mode 100644 index 113efb931..000000000 --- a/pkg/kube/inject/testdata/inject/hello-mtls-not-ready.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - security.istio.io/tlsMode: "disabled" - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/hello-mtls-not-ready.yaml.injected b/pkg/kube/inject/testdata/inject/hello-mtls-not-ready.yaml.injected deleted file mode 100644 index 0775521c5..000000000 --- a/pkg/kube/inject/testdata/inject/hello-mtls-not-ready.yaml.injected +++ /dev/null @@ -1,215 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: disabled - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-multi.yaml b/pkg/kube/inject/testdata/inject/hello-multi.yaml deleted file mode 100644 index b5368952d..000000000 --- a/pkg/kube/inject/testdata/inject/hello-multi.yaml +++ /dev/null @@ -1,55 +0,0 @@ ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello-v1 -spec: - replicas: 3 - selector: - matchLabels: - app: hello - tier: backend - track: stable - version: v1 - template: - metadata: - labels: - app: hello - tier: backend - track: stable - version: v1 - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello-v2 -spec: - replicas: 3 - selector: - matchLabels: - app: hello - tier: backend - track: stable - version: v2 - template: - metadata: - labels: - app: hello - tier: backend - track: stable - version: v2 - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 81 ---- diff --git a/pkg/kube/inject/testdata/inject/hello-multi.yaml.injected b/pkg/kube/inject/testdata/inject/hello-multi.yaml.injected deleted file mode 100644 index b3c9e4a94..000000000 --- a/pkg/kube/inject/testdata/inject/hello-multi.yaml.injected +++ /dev/null @@ -1,434 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello-v1 -spec: - replicas: 3 - selector: - matchLabels: - app: hello - tier: backend - track: stable - version: v1 - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: v1 - tier: backend - track: stable - version: v1 - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello-v1 - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello-v1 - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello-v2 -spec: - replicas: 3 - selector: - matchLabels: - app: hello - tier: backend - track: stable - version: v2 - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: v2 - tier: backend - track: stable - version: v2 - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 81 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":81} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello-v2 - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello-v2 - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-multiple-image-secrets.yaml.injected b/pkg/kube/inject/testdata/inject/hello-multiple-image-secrets.yaml.injected deleted file mode 100644 index 305733a0f..000000000 --- a/pkg/kube/inject/testdata/inject/hello-multiple-image-secrets.yaml.injected +++ /dev/null @@ -1,218 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":["barSecret"],"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - imagePullSecrets: - - name: barSecret - - name: fooSecret - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-namespace.yaml b/pkg/kube/inject/testdata/inject/hello-namespace.yaml deleted file mode 100644 index 4e220d3ea..000000000 --- a/pkg/kube/inject/testdata/inject/hello-namespace.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello - namespace: test -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/hello-namespace.yaml.injected b/pkg/kube/inject/testdata/inject/hello-namespace.yaml.injected deleted file mode 100644 index aca07ca36..000000000 --- a/pkg/kube/inject/testdata/inject/hello-namespace.yaml.injected +++ /dev/null @@ -1,216 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello - namespace: test -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/test/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-never.yaml.injected b/pkg/kube/inject/testdata/inject/hello-never.yaml.injected deleted file mode 100644 index 0eced265f..000000000 --- a/pkg/kube/inject/testdata/inject/hello-never.yaml.injected +++ /dev/null @@ -1,217 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - imagePullPolicy: Never - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - imagePullPolicy: Never - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-no-seccontext.yaml.injected b/pkg/kube/inject/testdata/inject/hello-no-seccontext.yaml.injected deleted file mode 100644 index 3dd31bf0c..000000000 --- a/pkg/kube/inject/testdata/inject/hello-no-seccontext.yaml.injected +++ /dev/null @@ -1,213 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-probes-noProxyHoldApplication-ProxyConfig.yaml b/pkg/kube/inject/testdata/inject/hello-probes-noProxyHoldApplication-ProxyConfig.yaml deleted file mode 100644 index 9778bd974..000000000 --- a/pkg/kube/inject/testdata/inject/hello-probes-noProxyHoldApplication-ProxyConfig.yaml +++ /dev/null @@ -1,46 +0,0 @@ ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - annotations: - proxy.istio.io/config: '{ "holdApplicationUntilProxyStarts": false }' - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - livenessProbe: - httpGet: - port: http - readinessProbe: - httpGet: - port: 3333 - - name: world - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 90 - livenessProbe: - httpGet: - port: http - readinessProbe: - exec: - command: - - cat - - /tmp/healthy \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inject/hello-probes-noProxyHoldApplication-ProxyConfig.yaml.injected b/pkg/kube/inject/testdata/inject/hello-probes-noProxyHoldApplication-ProxyConfig.yaml.injected deleted file mode 100644 index 1f4c94aa0..000000000 --- a/pkg/kube/inject/testdata/inject/hello-probes-noProxyHoldApplication-ProxyConfig.yaml.injected +++ /dev/null @@ -1,248 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - proxy.istio.io/config: '{ "holdApplicationUntilProxyStarts": false }' - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {"holdApplicationUntilProxyStarts":false} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ,{"name":"http","containerPort":90} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello,world - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - - name: ISTIO_KUBE_APP_PROBERS - value: '{"/app-health/hello/livez":{"httpGet":{"port":80}},"/app-health/hello/readyz":{"httpGet":{"port":3333}},"/app-health/world/livez":{"httpGet":{"port":90}}}' - image: apache/dubbo-agent:latest - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - path: /app-health/hello/livez - port: 15020 - name: hello - ports: - - containerPort: 80 - name: http - readinessProbe: - httpGet: - path: /app-health/hello/readyz - port: 15020 - resources: {} - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - path: /app-health/world/livez - port: 15020 - name: world - ports: - - containerPort: 90 - name: http - readinessProbe: - exec: - command: - - cat - - /tmp/healthy - resources: {} - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-probes-proxyHoldApplication-ProxyConfig.yaml b/pkg/kube/inject/testdata/inject/hello-probes-proxyHoldApplication-ProxyConfig.yaml deleted file mode 100644 index 8d24eb7ec..000000000 --- a/pkg/kube/inject/testdata/inject/hello-probes-proxyHoldApplication-ProxyConfig.yaml +++ /dev/null @@ -1,46 +0,0 @@ ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - annotations: - proxy.istio.io/config: '{ "holdApplicationUntilProxyStarts": true }' - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - livenessProbe: - httpGet: - port: http - readinessProbe: - httpGet: - port: 3333 - - name: world - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 90 - livenessProbe: - httpGet: - port: http - readinessProbe: - exec: - command: - - cat - - /tmp/healthy \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inject/hello-probes-proxyHoldApplication-ProxyConfig.yaml.injected b/pkg/kube/inject/testdata/inject/hello-probes-proxyHoldApplication-ProxyConfig.yaml.injected deleted file mode 100644 index 00268e421..000000000 --- a/pkg/kube/inject/testdata/inject/hello-probes-proxyHoldApplication-ProxyConfig.yaml.injected +++ /dev/null @@ -1,248 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - proxy.istio.io/config: '{ "holdApplicationUntilProxyStarts": true }' - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {"holdApplicationUntilProxyStarts":true} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ,{"name":"http","containerPort":90} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello,world - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - - name: ISTIO_KUBE_APP_PROBERS - value: '{"/app-health/hello/livez":{"httpGet":{"port":80}},"/app-health/hello/readyz":{"httpGet":{"port":3333}},"/app-health/world/livez":{"httpGet":{"port":90}}}' - image: apache/dubbo-agent:latest - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - path: /app-health/hello/livez - port: 15020 - name: hello - ports: - - containerPort: 80 - name: http - readinessProbe: - httpGet: - path: /app-health/hello/readyz - port: 15020 - resources: {} - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - path: /app-health/world/livez - port: 15020 - name: world - ports: - - containerPort: 90 - name: http - readinessProbe: - exec: - command: - - cat - - /tmp/healthy - resources: {} - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-probes-with-flag-set-in-annotation.yaml b/pkg/kube/inject/testdata/inject/hello-probes-with-flag-set-in-annotation.yaml deleted file mode 100644 index 8ced3bdd5..000000000 --- a/pkg/kube/inject/testdata/inject/hello-probes-with-flag-set-in-annotation.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - annotations: - sidecar.istio.io/rewriteAppHTTPProbers: "true" - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - livenessProbe: - httpGet: - port: http - readinessProbe: - httpGet: - port: 3333 - - name: world - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 90 - livenessProbe: - httpGet: - port: http - readinessProbe: - exec: - command: - - cat - - /tmp/healthy diff --git a/pkg/kube/inject/testdata/inject/hello-probes-with-flag-set-in-annotation.yaml.injected b/pkg/kube/inject/testdata/inject/hello-probes-with-flag-set-in-annotation.yaml.injected deleted file mode 100644 index 7a707e1ca..000000000 --- a/pkg/kube/inject/testdata/inject/hello-probes-with-flag-set-in-annotation.yaml.injected +++ /dev/null @@ -1,242 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/rewriteAppHTTPProbers: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - path: /app-health/hello/livez - port: 15020 - name: hello - ports: - - containerPort: 80 - name: http - readinessProbe: - httpGet: - path: /app-health/hello/readyz - port: 15020 - resources: {} - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - path: /app-health/world/livez - port: 15020 - name: world - ports: - - containerPort: 90 - name: http - readinessProbe: - exec: - command: - - cat - - /tmp/healthy - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ,{"name":"http","containerPort":90} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello,world - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - - name: ISTIO_KUBE_APP_PROBERS - value: '{"/app-health/hello/livez":{"httpGet":{"port":80}},"/app-health/hello/readyz":{"httpGet":{"port":3333}},"/app-health/world/livez":{"httpGet":{"port":90}}}' - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-probes-with-flag-unset-in-annotation.yaml b/pkg/kube/inject/testdata/inject/hello-probes-with-flag-unset-in-annotation.yaml deleted file mode 100644 index 01e3da3b1..000000000 --- a/pkg/kube/inject/testdata/inject/hello-probes-with-flag-unset-in-annotation.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - annotations: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - livenessProbe: - httpGet: - port: http - readinessProbe: - httpGet: - port: 3333 - - name: world - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 90 - livenessProbe: - httpGet: - port: http - readinessProbe: - exec: - command: - - cat - - /tmp/healthy diff --git a/pkg/kube/inject/testdata/inject/hello-probes-with-flag-unset-in-annotation.yaml.injected b/pkg/kube/inject/testdata/inject/hello-probes-with-flag-unset-in-annotation.yaml.injected deleted file mode 100644 index 06c4d8d99..000000000 --- a/pkg/kube/inject/testdata/inject/hello-probes-with-flag-unset-in-annotation.yaml.injected +++ /dev/null @@ -1,237 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/rewriteAppHTTPProbers: "false" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - port: http - name: hello - ports: - - containerPort: 80 - name: http - readinessProbe: - httpGet: - port: 3333 - resources: {} - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - port: http - name: world - ports: - - containerPort: 90 - name: http - readinessProbe: - exec: - command: - - cat - - /tmp/healthy - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ,{"name":"http","containerPort":90} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello,world - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-probes.proxyHoldsApplication.yaml.injected b/pkg/kube/inject/testdata/inject/hello-probes.proxyHoldsApplication.yaml.injected deleted file mode 100644 index 1fc1c7ef6..000000000 --- a/pkg/kube/inject/testdata/inject/hello-probes.proxyHoldsApplication.yaml.injected +++ /dev/null @@ -1,247 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ,{"name":"http","containerPort":90} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello,world - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - - name: ISTIO_KUBE_APP_PROBERS - value: '{"/app-health/hello/livez":{"httpGet":{"port":80}},"/app-health/hello/readyz":{"httpGet":{"port":3333}},"/app-health/world/livez":{"httpGet":{"port":90}}}' - image: apache/dubbo-agent:latest - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - path: /app-health/hello/livez - port: 15020 - name: hello - ports: - - containerPort: 80 - name: http - readinessProbe: - httpGet: - path: /app-health/hello/readyz - port: 15020 - resources: {} - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - path: /app-health/world/livez - port: 15020 - name: world - ports: - - containerPort: 90 - name: http - readinessProbe: - exec: - command: - - cat - - /tmp/healthy - resources: {} - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-probes.yaml b/pkg/kube/inject/testdata/inject/hello-probes.yaml deleted file mode 100644 index 1804ebbda..000000000 --- a/pkg/kube/inject/testdata/inject/hello-probes.yaml +++ /dev/null @@ -1,43 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - livenessProbe: - httpGet: - port: http - readinessProbe: - httpGet: - port: 3333 - - name: world - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 90 - livenessProbe: - httpGet: - port: http - readinessProbe: - exec: - command: - - cat - - /tmp/healthy diff --git a/pkg/kube/inject/testdata/inject/hello-probes.yaml.injected b/pkg/kube/inject/testdata/inject/hello-probes.yaml.injected deleted file mode 100644 index 04b818410..000000000 --- a/pkg/kube/inject/testdata/inject/hello-probes.yaml.injected +++ /dev/null @@ -1,241 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - path: /app-health/hello/livez - port: 15020 - name: hello - ports: - - containerPort: 80 - name: http - readinessProbe: - httpGet: - path: /app-health/hello/readyz - port: 15020 - resources: {} - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - path: /app-health/world/livez - port: 15020 - name: world - ports: - - containerPort: 90 - name: http - readinessProbe: - exec: - command: - - cat - - /tmp/healthy - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ,{"name":"http","containerPort":90} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello,world - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - - name: ISTIO_KUBE_APP_PROBERS - value: '{"/app-health/hello/livez":{"httpGet":{"port":80}},"/app-health/hello/readyz":{"httpGet":{"port":3333}},"/app-health/world/livez":{"httpGet":{"port":90}}}' - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-proxy-override.yaml b/pkg/kube/inject/testdata/inject/hello-proxy-override.yaml deleted file mode 100644 index 0ee552dd0..000000000 --- a/pkg/kube/inject/testdata/inject/hello-proxy-override.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - annotations: - sidecar.istio.io/proxyImage: "docker.io/istio/proxy2_debug:unittest" - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/hello-proxy-override.yaml.injected b/pkg/kube/inject/testdata/inject/hello-proxy-override.yaml.injected deleted file mode 100644 index 361447e07..000000000 --- a/pkg/kube/inject/testdata/inject/hello-proxy-override.yaml.injected +++ /dev/null @@ -1,216 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/proxyImage: docker.io/istio/proxy2_debug:unittest - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: docker.io/istio/proxy2_debug:unittest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: docker.io/istio/proxy2_debug:unittest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-readiness.yaml b/pkg/kube/inject/testdata/inject/hello-readiness.yaml deleted file mode 100644 index 481a30b9e..000000000 --- a/pkg/kube/inject/testdata/inject/hello-readiness.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - readinessProbe: - httpGet: - path: /ip - port: 8000 diff --git a/pkg/kube/inject/testdata/inject/hello-readiness.yaml.injected b/pkg/kube/inject/testdata/inject/hello-readiness.yaml.injected deleted file mode 100644 index 29ff49a42..000000000 --- a/pkg/kube/inject/testdata/inject/hello-readiness.yaml.injected +++ /dev/null @@ -1,221 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - readinessProbe: - httpGet: - path: /app-health/hello/readyz - port: 15020 - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - - name: ISTIO_KUBE_APP_PROBERS - value: '{"/app-health/hello/readyz":{"httpGet":{"path":"/ip","port":8000}}}' - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-service.yaml b/pkg/kube/inject/testdata/inject/hello-service.yaml deleted file mode 100644 index 8be93e1da..000000000 --- a/pkg/kube/inject/testdata/inject/hello-service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: hello -spec: - selector: - app: hello - tier: backend - ports: - - protocol: TCP - port: 80 - targetPort: http diff --git a/pkg/kube/inject/testdata/inject/hello-service.yaml.injected b/pkg/kube/inject/testdata/inject/hello-service.yaml.injected deleted file mode 100644 index cdc037cbc..000000000 --- a/pkg/kube/inject/testdata/inject/hello-service.yaml.injected +++ /dev/null @@ -1,13 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: hello -spec: - selector: - app: hello - tier: backend - ports: - - protocol: TCP - port: 80 - targetPort: http ---- diff --git a/pkg/kube/inject/testdata/inject/hello-template-in-values.iop.yaml b/pkg/kube/inject/testdata/inject/hello-template-in-values.iop.yaml deleted file mode 100644 index e18c7c19c..000000000 --- a/pkg/kube/inject/testdata/inject/hello-template-in-values.iop.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -metadata: - namespace: dubbo-system - name: example-istiocontrolplane -spec: - values: - global: - podDNSSearchNamespaces: - - "global" - - "{{ valueOrDefault .DeploymentMeta.Namespace \"default\" }}.global" diff --git a/pkg/kube/inject/testdata/inject/hello-template-in-values.yaml.injected b/pkg/kube/inject/testdata/inject/hello-template-in-values.yaml.injected deleted file mode 100644 index 4a48b3124..000000000 --- a/pkg/kube/inject/testdata/inject/hello-template-in-values.yaml.injected +++ /dev/null @@ -1,215 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello-tproxy.yaml.injected b/pkg/kube/inject/testdata/inject/hello-tproxy.yaml.injected deleted file mode 100644 index 2a663f713..000000000 --- a/pkg/kube/inject/testdata/inject/hello-tproxy.yaml.injected +++ /dev/null @@ -1,217 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {"interceptionMode":"TPROXY"} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: TPROXY - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - TPROXY - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello.proxyHoldsApplication.yaml.injected b/pkg/kube/inject/testdata/inject/hello.proxyHoldsApplication.yaml.injected deleted file mode 100644 index 31d33d22e..000000000 --- a/pkg/kube/inject/testdata/inject/hello.proxyHoldsApplication.yaml.injected +++ /dev/null @@ -1,221 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello.yaml b/pkg/kube/inject/testdata/inject/hello.yaml deleted file mode 100644 index e4b7621b7..000000000 --- a/pkg/kube/inject/testdata/inject/hello.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/hello.yaml.cni.injected b/pkg/kube/inject/testdata/inject/hello.yaml.cni.injected deleted file mode 100644 index 9a8f0af92..000000000 --- a/pkg/kube/inject/testdata/inject/hello.yaml.cni.injected +++ /dev/null @@ -1,221 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/interceptionMode: REDIRECT - sidecar.istio.io/status: '{"initContainers":["istio-validation"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - traffic.sidecar.istio.io/excludeInboundPorts: "15020" - traffic.sidecar.istio.io/includeInboundPorts: '*' - traffic.sidecar.istio.io/includeOutboundIPRanges: '*' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - topology.istio.io/network: network1 - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - - name: ISTIO_META_NETWORK - value: network1 - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - - --run-validation - - --skip-rule-apply - image: apache/dubbo-agent:latest - name: istio-validation - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello.yaml.injected b/pkg/kube/inject/testdata/inject/hello.yaml.injected deleted file mode 100644 index 4a48b3124..000000000 --- a/pkg/kube/inject/testdata/inject/hello.yaml.injected +++ /dev/null @@ -1,215 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/hello.yaml.proxyImageName.injected b/pkg/kube/inject/testdata/inject/hello.yaml.proxyImageName.injected deleted file mode 100644 index 5ded003ae..000000000 --- a/pkg/kube/inject/testdata/inject/hello.yaml.proxyImageName.injected +++ /dev/null @@ -1,215 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/proxyTest:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/proxyTest:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/https-probes.yaml b/pkg/kube/inject/testdata/inject/https-probes.yaml deleted file mode 100644 index 5c70aab9e..000000000 --- a/pkg/kube/inject/testdata/inject/https-probes.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - livenessProbe: - httpGet: - port: http - readinessProbe: - httpGet: - port: 3333 - scheme: HTTPS - - name: world - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 90 - livenessProbe: - httpGet: - port: http - readinessProbe: - exec: - command: - - cat - - /tmp/healthy diff --git a/pkg/kube/inject/testdata/inject/https-probes.yaml.injected b/pkg/kube/inject/testdata/inject/https-probes.yaml.injected deleted file mode 100644 index 0c1ec46e2..000000000 --- a/pkg/kube/inject/testdata/inject/https-probes.yaml.injected +++ /dev/null @@ -1,242 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - path: /app-health/hello/livez - port: 15020 - name: hello - ports: - - containerPort: 80 - name: http - readinessProbe: - httpGet: - path: /app-health/hello/readyz - port: 15020 - scheme: HTTP - resources: {} - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - path: /app-health/world/livez - port: 15020 - name: world - ports: - - containerPort: 90 - name: http - readinessProbe: - exec: - command: - - cat - - /tmp/healthy - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ,{"name":"http","containerPort":90} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello,world - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - - name: ISTIO_KUBE_APP_PROBERS - value: '{"/app-health/hello/livez":{"httpGet":{"port":80}},"/app-health/hello/readyz":{"httpGet":{"port":3333,"scheme":"HTTPS"}},"/app-health/world/livez":{"httpGet":{"port":90}}}' - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/job.yaml b/pkg/kube/inject/testdata/inject/job.yaml deleted file mode 100644 index 964de82ec..000000000 --- a/pkg/kube/inject/testdata/inject/job.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: pi -spec: - template: - metadata: - name: pi - spec: - containers: - - name: pi - image: perl - command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] - restartPolicy: Never diff --git a/pkg/kube/inject/testdata/inject/job.yaml.injected b/pkg/kube/inject/testdata/inject/job.yaml.injected deleted file mode 100644 index b544df65c..000000000 --- a/pkg/kube/inject/testdata/inject/job.yaml.injected +++ /dev/null @@ -1,208 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - creationTimestamp: null - name: pi -spec: - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: pi - kubectl.kubernetes.io/default-logs-container: pi - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: pi - service.istio.io/canonical-revision: latest - name: pi - spec: - containers: - - command: - - perl - - -Mbignum=bpi - - -wle - - print bpi(2000) - image: perl - name: pi - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - ] - - name: ISTIO_META_APP_CONTAINERS - value: pi - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: pi - - name: ISTIO_META_OWNER - value: kubernetes://apis/batch/v1/namespaces/default/jobs/pi - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - restartPolicy: Never - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/kubevirtInterfaces.yaml b/pkg/kube/inject/testdata/inject/kubevirtInterfaces.yaml deleted file mode 100644 index bbdee30d0..000000000 --- a/pkg/kube/inject/testdata/inject/kubevirtInterfaces.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - annotations: - traffic.sidecar.istio.io/kubevirtInterfaces: "net1" - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/kubevirtInterfaces.yaml.injected b/pkg/kube/inject/testdata/inject/kubevirtInterfaces.yaml.injected deleted file mode 100644 index e6a8905df..000000000 --- a/pkg/kube/inject/testdata/inject/kubevirtInterfaces.yaml.injected +++ /dev/null @@ -1,218 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - traffic.sidecar.istio.io/kubevirtInterfaces: net1 - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 300 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 100 - periodSeconds: 200 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,123 - - -k - - net1 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/kubevirtInterfaces_list.yaml b/pkg/kube/inject/testdata/inject/kubevirtInterfaces_list.yaml deleted file mode 100644 index bb4ba9bdb..000000000 --- a/pkg/kube/inject/testdata/inject/kubevirtInterfaces_list.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - annotations: - traffic.sidecar.istio.io/kubevirtInterfaces: "net1,net2" - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/kubevirtInterfaces_list.yaml.injected b/pkg/kube/inject/testdata/inject/kubevirtInterfaces_list.yaml.injected deleted file mode 100644 index 50593bd4d..000000000 --- a/pkg/kube/inject/testdata/inject/kubevirtInterfaces_list.yaml.injected +++ /dev/null @@ -1,218 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - traffic.sidecar.istio.io/kubevirtInterfaces: net1,net2 - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - - -k - - net1,net2 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/list-frontend.yaml b/pkg/kube/inject/testdata/inject/list-frontend.yaml deleted file mode 100644 index 7f4596455..000000000 --- a/pkg/kube/inject/testdata/inject/list-frontend.yaml +++ /dev/null @@ -1,41 +0,0 @@ -apiVersion: v1 -kind: List -items: - - kind: Service - apiVersion: v1 - metadata: - name: frontend - spec: - selector: - app: hello - tier: frontend - ports: - - protocol: "TCP" - port: 80 - targetPort: 80 - type: LoadBalancer - - apiVersion: apps/v1 - kind: Deployment - metadata: - name: frontend - spec: - replicas: 1 - selector: - matchLabels: - app: hello - tier: frontend - track: stable - template: - metadata: - labels: - app: hello - tier: frontend - track: stable - spec: - containers: - - name: nginx - image: "fake.docker.io/google-samples/hello-frontend:1.0" - lifecycle: - preStop: - exec: - command: ["/usr/sbin/nginx","-s","quit"] diff --git a/pkg/kube/inject/testdata/inject/list-frontend.yaml.injected b/pkg/kube/inject/testdata/inject/list-frontend.yaml.injected deleted file mode 100644 index 0e0a3ab6f..000000000 --- a/pkg/kube/inject/testdata/inject/list-frontend.yaml.injected +++ /dev/null @@ -1,235 +0,0 @@ -apiVersion: v1 -items: -- apiVersion: v1 - kind: Service - metadata: - name: frontend - spec: - ports: - - port: 80 - protocol: TCP - targetPort: 80 - selector: - app: hello - tier: frontend - type: LoadBalancer -- apiVersion: apps/v1 - kind: Deployment - metadata: - creationTimestamp: null - name: frontend - spec: - replicas: 1 - selector: - matchLabels: - app: hello - tier: frontend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: nginx - kubectl.kubernetes.io/default-logs-container: nginx - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: frontend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-frontend:1.0 - lifecycle: - preStop: - exec: - command: - - /usr/sbin/nginx - - -s - - quit - name: nginx - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - ] - - name: ISTIO_META_APP_CONTAINERS - value: nginx - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: frontend - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/frontend - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert - status: {} -kind: List -metadata: {} ---- diff --git a/pkg/kube/inject/testdata/inject/list.yaml b/pkg/kube/inject/testdata/inject/list.yaml deleted file mode 100644 index 6142991d8..000000000 --- a/pkg/kube/inject/testdata/inject/list.yaml +++ /dev/null @@ -1,56 +0,0 @@ -apiVersion: v1 -kind: List -items: - - apiVersion: apps/v1 - kind: Deployment - metadata: - name: hello-v1 - spec: - replicas: 3 - selector: - matchLabels: - app: hello - tier: backend - track: stable - version: v1 - template: - metadata: - labels: - app: hello - tier: backend - track: stable - version: v1 - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - - - apiVersion: apps/v1 - kind: Deployment - metadata: - name: hello-v2 - spec: - replicas: 3 - selector: - matchLabels: - app: hello - tier: backend - track: stable - version: v2 - template: - metadata: - labels: - app: hello - tier: backend - track: stable - version: v2 - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 81 diff --git a/pkg/kube/inject/testdata/inject/list.yaml.injected b/pkg/kube/inject/testdata/inject/list.yaml.injected deleted file mode 100644 index bf6063aeb..000000000 --- a/pkg/kube/inject/testdata/inject/list.yaml.injected +++ /dev/null @@ -1,437 +0,0 @@ -apiVersion: v1 -items: -- apiVersion: apps/v1 - kind: Deployment - metadata: - creationTimestamp: null - name: hello-v1 - spec: - replicas: 3 - selector: - matchLabels: - app: hello - tier: backend - track: stable - version: v1 - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: v1 - tier: backend - track: stable - version: v1 - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello-v1 - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello-v1 - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert - status: {} -- apiVersion: apps/v1 - kind: Deployment - metadata: - creationTimestamp: null - name: hello-v2 - spec: - replicas: 3 - selector: - matchLabels: - app: hello - tier: backend - track: stable - version: v2 - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: v2 - tier: backend - track: stable - version: v2 - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 81 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":81} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello-v2 - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello-v2 - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert - status: {} -kind: List -metadata: {} ---- diff --git a/pkg/kube/inject/testdata/inject/multi-container.yaml b/pkg/kube/inject/testdata/inject/multi-container.yaml deleted file mode 100644 index e8433ea4b..000000000 --- a/pkg/kube/inject/testdata/inject/multi-container.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: app -spec: - replicas: 1 - selector: - matchLabels: - app: app - template: - metadata: - labels: - app: app - spec: - containers: - - image: image - name: name1 - - image: alpine - name: name2 - ports: - - containerPort: 123 - name: foo - - name: name3 - image: alpine diff --git a/pkg/kube/inject/testdata/inject/multi-container.yaml.injected b/pkg/kube/inject/testdata/inject/multi-container.yaml.injected deleted file mode 100644 index d0e9e244d..000000000 --- a/pkg/kube/inject/testdata/inject/multi-container.yaml.injected +++ /dev/null @@ -1,217 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: app -spec: - replicas: 1 - selector: - matchLabels: - app: app - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: name1 - kubectl.kubernetes.io/default-logs-container: name1 - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: app - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: app - service.istio.io/canonical-revision: latest - spec: - containers: - - image: image - name: name1 - resources: {} - - image: alpine - name: name2 - ports: - - containerPort: 123 - name: foo - resources: {} - - image: alpine - name: name3 - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"foo","containerPort":123} - ] - - name: ISTIO_META_APP_CONTAINERS - value: name1,name2,name3 - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: app - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/app - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/multi-init.yaml b/pkg/kube/inject/testdata/inject/multi-init.yaml deleted file mode 100644 index 8548de293..000000000 --- a/pkg/kube/inject/testdata/inject/multi-init.yaml +++ /dev/null @@ -1,31 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - initContainers: - - name: init-one - image: "busybox" - command: ["sh", "-c", "true"] - - name: init-two - image: "busybox" - command: ["sh", "-c", "true"] diff --git a/pkg/kube/inject/testdata/inject/multi-init.yaml.injected b/pkg/kube/inject/testdata/inject/multi-init.yaml.injected deleted file mode 100644 index 9ebb6a4f7..000000000 --- a/pkg/kube/inject/testdata/inject/multi-init.yaml.injected +++ /dev/null @@ -1,229 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - command: - - sh - - -c - - "true" - image: busybox - name: init-one - resources: {} - - command: - - sh - - -c - - "true" - image: busybox - name: init-two - resources: {} - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/multiple-templates.yaml b/pkg/kube/inject/testdata/inject/multiple-templates.yaml deleted file mode 100644 index b97b5a622..000000000 --- a/pkg/kube/inject/testdata/inject/multiple-templates.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - selector: - matchLabels: - app: hello - template: - metadata: - annotations: - # There is no real purpose of setting this multiple times; this just makes sure it doesn't blow - # up if a user does happen to configure this - inject.istio.io/templates: sidecar,sidecar,sidecar - labels: - app: hello - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - - name: istio-proxy - image: foo/bar diff --git a/pkg/kube/inject/testdata/inject/multiple-templates.yaml.injected b/pkg/kube/inject/testdata/inject/multiple-templates.yaml.injected deleted file mode 100644 index 241273906..000000000 --- a/pkg/kube/inject/testdata/inject/multiple-templates.yaml.injected +++ /dev/null @@ -1,208 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - selector: - matchLabels: - app: hello - strategy: {} - template: - metadata: - annotations: - inject.istio.io/templates: sidecar,sidecar,sidecar - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - proxy.istio.io/overrides: '{"containers":[{"name":"istio-proxy","image":"foo/bar","resources":{}}]}' - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: foo/bar - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/named_port.yaml b/pkg/kube/inject/testdata/inject/named_port.yaml deleted file mode 100644 index cebfb235d..000000000 --- a/pkg/kube/inject/testdata/inject/named_port.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - readinessProbe: - httpGet: - port: http diff --git a/pkg/kube/inject/testdata/inject/named_port.yaml.injected b/pkg/kube/inject/testdata/inject/named_port.yaml.injected deleted file mode 100644 index 0e9605f05..000000000 --- a/pkg/kube/inject/testdata/inject/named_port.yaml.injected +++ /dev/null @@ -1,221 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - readinessProbe: - httpGet: - path: /app-health/hello/readyz - port: 15020 - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - - name: ISTIO_KUBE_APP_PROBERS - value: '{"/app-health/hello/readyz":{"httpGet":{"port":80}}}' - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/one_container.yaml b/pkg/kube/inject/testdata/inject/one_container.yaml deleted file mode 100644 index 9f8b718f4..000000000 --- a/pkg/kube/inject/testdata/inject/one_container.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - livenessProbe: - httpGet: - port: http - readinessProbe: - httpGet: - port: 3333 diff --git a/pkg/kube/inject/testdata/inject/one_container.yaml.injected b/pkg/kube/inject/testdata/inject/one_container.yaml.injected deleted file mode 100644 index 52633bfb4..000000000 --- a/pkg/kube/inject/testdata/inject/one_container.yaml.injected +++ /dev/null @@ -1,225 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - path: /app-health/hello/livez - port: 15020 - name: hello - ports: - - containerPort: 80 - name: http - readinessProbe: - httpGet: - path: /app-health/hello/readyz - port: 15020 - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - - name: ISTIO_KUBE_APP_PROBERS - value: '{"/app-health/hello/livez":{"httpGet":{"port":80}},"/app-health/hello/readyz":{"httpGet":{"port":3333}}}' - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/only-proxy-container.yaml b/pkg/kube/inject/testdata/inject/only-proxy-container.yaml deleted file mode 100644 index cbb8e52e7..000000000 --- a/pkg/kube/inject/testdata/inject/only-proxy-container.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: istio-ingressgateway -spec: - selector: - matchLabels: - istio: ingressgateway - template: - metadata: - labels: - istio: ingressgateway - spec: - # Ensure we can have istio-proxy as the only container. This isn't particularly useful as a sidecar - # but will be used when we have a dedicated template to run a pod as a Gateway - containers: - - name: istio-proxy - image: auto diff --git a/pkg/kube/inject/testdata/inject/only-proxy-container.yaml.injected b/pkg/kube/inject/testdata/inject/only-proxy-container.yaml.injected deleted file mode 100644 index 45de9d135..000000000 --- a/pkg/kube/inject/testdata/inject/only-proxy-container.yaml.injected +++ /dev/null @@ -1,201 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: istio-ingressgateway -spec: - selector: - matchLabels: - istio: ingressgateway - strategy: {} - template: - metadata: - annotations: - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - proxy.istio.io/overrides: '{"containers":[{"name":"istio-proxy","resources":{}}]}' - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - istio: ingressgateway - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: istio-ingressgateway - service.istio.io/canonical-revision: latest - spec: - containers: - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - ] - - name: ISTIO_META_APP_CONTAINERS - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: istio-ingressgateway - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/istio-ingressgateway - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/pod.yaml b/pkg/kube/inject/testdata/inject/pod.yaml deleted file mode 100644 index 856ce625a..000000000 --- a/pkg/kube/inject/testdata/inject/pod.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: hellopod -spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - diff --git a/pkg/kube/inject/testdata/inject/pod.yaml.injected b/pkg/kube/inject/testdata/inject/pod.yaml.injected deleted file mode 100644 index 8c25c007c..000000000 --- a/pkg/kube/inject/testdata/inject/pod.yaml.injected +++ /dev/null @@ -1,201 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hellopod - service.istio.io/canonical-revision: latest - name: hellopod -spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hellopod - - name: ISTIO_META_OWNER - value: kubernetes://apis/v1/namespaces/default/pods/hellopod - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/prometheus-scrape.yaml b/pkg/kube/inject/testdata/inject/prometheus-scrape.yaml deleted file mode 100644 index e873c8d10..000000000 --- a/pkg/kube/inject/testdata/inject/prometheus-scrape.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - annotations: - prometheus.io/scrape: "false" - name: hellopod -spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - diff --git a/pkg/kube/inject/testdata/inject/prometheus-scrape.yaml.injected b/pkg/kube/inject/testdata/inject/prometheus-scrape.yaml.injected deleted file mode 100644 index c28682bc1..000000000 --- a/pkg/kube/inject/testdata/inject/prometheus-scrape.yaml.injected +++ /dev/null @@ -1,199 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/scrape: "false" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hellopod - service.istio.io/canonical-revision: latest - name: hellopod -spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hellopod - - name: ISTIO_META_OWNER - value: kubernetes://apis/v1/namespaces/default/pods/hellopod - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/prometheus-scrape2.yaml b/pkg/kube/inject/testdata/inject/prometheus-scrape2.yaml deleted file mode 100644 index 121b81772..000000000 --- a/pkg/kube/inject/testdata/inject/prometheus-scrape2.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - annotations: - prometheus.io.scrape: "false" - name: hellopod -spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - diff --git a/pkg/kube/inject/testdata/inject/prometheus-scrape2.yaml.injected b/pkg/kube/inject/testdata/inject/prometheus-scrape2.yaml.injected deleted file mode 100644 index 48b96ff6d..000000000 --- a/pkg/kube/inject/testdata/inject/prometheus-scrape2.yaml.injected +++ /dev/null @@ -1,199 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io.scrape: "false" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hellopod - service.istio.io/canonical-revision: latest - name: hellopod -spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hellopod - - name: ISTIO_META_OWNER - value: kubernetes://apis/v1/namespaces/default/pods/hellopod - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/proxy-override-args.yaml b/pkg/kube/inject/testdata/inject/proxy-override-args.yaml deleted file mode 100644 index 0a843781d..000000000 --- a/pkg/kube/inject/testdata/inject/proxy-override-args.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - selector: - matchLabels: - app: hello - template: - metadata: - labels: - app: hello - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - - name: istio-proxy - image: auto - # Test that we can override a complex field like the command - args: ["-c", "my-config.yaml"] - command: - - envoy diff --git a/pkg/kube/inject/testdata/inject/proxy-override-args.yaml.injected b/pkg/kube/inject/testdata/inject/proxy-override-args.yaml.injected deleted file mode 100644 index b8903b507..000000000 --- a/pkg/kube/inject/testdata/inject/proxy-override-args.yaml.injected +++ /dev/null @@ -1,202 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - selector: - matchLabels: - app: hello - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - proxy.istio.io/overrides: '{"containers":[{"name":"istio-proxy","command":["envoy"],"args":["-c","my-config.yaml"],"resources":{}}]}' - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - resources: {} - - args: - - -c - - my-config.yaml - command: - - envoy - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/proxy-override.yaml b/pkg/kube/inject/testdata/inject/proxy-override.yaml deleted file mode 100644 index d1b881a1c..000000000 --- a/pkg/kube/inject/testdata/inject/proxy-override.yaml +++ /dev/null @@ -1,53 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - selector: - matchLabels: - app: hello - template: - metadata: - labels: - app: hello - spec: - initContainers: - - name: istio-init - args: - - my - - custom - - args - image: fake/custom-image - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - - name: istio-proxy - image: auto - resources: - requests: - cpu: 123m - livenessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 10 - periodSeconds: 2 - timeoutSeconds: 3 - # Check various types merge find - tty: true - terminationMessagePath: "/foo/bar" - volumeMounts: - - mountPath: /etc/certs - name: certs - lifecycle: - preStop: - exec: - command: ["sleep", "10"] - securityContext: - allowPrivilegeEscalation: true - readOnlyRootFilesystem: false - volumes: - - name: certs - secret: - secretName: istio-certs diff --git a/pkg/kube/inject/testdata/inject/proxy-override.yaml.injected b/pkg/kube/inject/testdata/inject/proxy-override.yaml.injected deleted file mode 100644 index 2b75d26ef..000000000 --- a/pkg/kube/inject/testdata/inject/proxy-override.yaml.injected +++ /dev/null @@ -1,214 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - selector: - matchLabels: - app: hello - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - proxy.istio.io/overrides: '{"containers":[{"name":"istio-proxy","resources":{"requests":{"cpu":"123m"}},"volumeMounts":[{"name":"certs","mountPath":"/etc/certs"}],"livenessProbe":{"httpGet":{"path":"/healthz/ready","port":15021},"initialDelaySeconds":10,"timeoutSeconds":3,"periodSeconds":2,"failureThreshold":30},"lifecycle":{"preStop":{"exec":{"command":["sleep","10"]}}},"terminationMessagePath":"/foo/bar","securityContext":{"readOnlyRootFilesystem":false,"allowPrivilegeEscalation":true},"tty":true}],"initContainers":[{"name":"istio-init","image":"fake/custom-image","args":["my","custom","args"],"resources":{}}]}' - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - lifecycle: - preStop: - exec: - command: - - sleep - - "10" - livenessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 10 - periodSeconds: 2 - timeoutSeconds: 3 - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 123m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: true - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - terminationMessagePath: /foo/bar - tty: true - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - - mountPath: /etc/certs - name: certs - initContainers: - - args: - - my - - custom - - args - image: fake/custom-image - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert - - name: certs - secret: - secretName: istio-certs -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/ready_live.yaml b/pkg/kube/inject/testdata/inject/ready_live.yaml deleted file mode 100644 index 1804ebbda..000000000 --- a/pkg/kube/inject/testdata/inject/ready_live.yaml +++ /dev/null @@ -1,43 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - livenessProbe: - httpGet: - port: http - readinessProbe: - httpGet: - port: 3333 - - name: world - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 90 - livenessProbe: - httpGet: - port: http - readinessProbe: - exec: - command: - - cat - - /tmp/healthy diff --git a/pkg/kube/inject/testdata/inject/ready_live.yaml.injected b/pkg/kube/inject/testdata/inject/ready_live.yaml.injected deleted file mode 100644 index 04b818410..000000000 --- a/pkg/kube/inject/testdata/inject/ready_live.yaml.injected +++ /dev/null @@ -1,241 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - path: /app-health/hello/livez - port: 15020 - name: hello - ports: - - containerPort: 80 - name: http - readinessProbe: - httpGet: - path: /app-health/hello/readyz - port: 15020 - resources: {} - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - path: /app-health/world/livez - port: 15020 - name: world - ports: - - containerPort: 90 - name: http - readinessProbe: - exec: - command: - - cat - - /tmp/healthy - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ,{"name":"http","containerPort":90} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello,world - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - - name: ISTIO_KUBE_APP_PROBERS - value: '{"/app-health/hello/livez":{"httpGet":{"port":80}},"/app-health/hello/readyz":{"httpGet":{"port":3333}},"/app-health/world/livez":{"httpGet":{"port":90}}}' - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/ready_only.yaml b/pkg/kube/inject/testdata/inject/ready_only.yaml deleted file mode 100644 index 678c93202..000000000 --- a/pkg/kube/inject/testdata/inject/ready_only.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - readinessProbe: - httpGet: - port: 3333 diff --git a/pkg/kube/inject/testdata/inject/ready_only.yaml.injected b/pkg/kube/inject/testdata/inject/ready_only.yaml.injected deleted file mode 100644 index 91d274875..000000000 --- a/pkg/kube/inject/testdata/inject/ready_only.yaml.injected +++ /dev/null @@ -1,221 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - readinessProbe: - httpGet: - path: /app-health/hello/readyz - port: 15020 - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - - name: ISTIO_KUBE_APP_PROBERS - value: '{"/app-health/hello/readyz":{"httpGet":{"port":3333}}}' - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/replicaset.yaml b/pkg/kube/inject/testdata/inject/replicaset.yaml deleted file mode 100644 index e21bafe8c..000000000 --- a/pkg/kube/inject/testdata/inject/replicaset.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: apps/v1 -kind: ReplicaSet -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - template: - metadata: - labels: - app: hello - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/replicaset.yaml.injected b/pkg/kube/inject/testdata/inject/replicaset.yaml.injected deleted file mode 100644 index 3e5df6d5f..000000000 --- a/pkg/kube/inject/testdata/inject/replicaset.yaml.injected +++ /dev/null @@ -1,211 +0,0 @@ -apiVersion: apps/v1 -kind: ReplicaSet -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/replicasets/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: - replicas: 0 ---- diff --git a/pkg/kube/inject/testdata/inject/replicationcontroller.yaml b/pkg/kube/inject/testdata/inject/replicationcontroller.yaml deleted file mode 100644 index 6eff0b9b5..000000000 --- a/pkg/kube/inject/testdata/inject/replicationcontroller.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: v1 -kind: ReplicationController -metadata: - name: nginx -spec: - replicas: 3 - selector: - app: nginx - template: - metadata: - name: nginx - labels: - app: nginx - spec: - containers: - - name: nginx - image: nginx - ports: - - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/replicationcontroller.yaml.injected b/pkg/kube/inject/testdata/inject/replicationcontroller.yaml.injected deleted file mode 100644 index dae8d38d1..000000000 --- a/pkg/kube/inject/testdata/inject/replicationcontroller.yaml.injected +++ /dev/null @@ -1,210 +0,0 @@ -apiVersion: v1 -kind: ReplicationController -metadata: - creationTimestamp: null - name: nginx -spec: - replicas: 3 - selector: - app: nginx - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: nginx - kubectl.kubernetes.io/default-logs-container: nginx - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: nginx - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: nginx - service.istio.io/canonical-revision: latest - name: nginx - spec: - containers: - - image: nginx - name: nginx - ports: - - containerPort: 80 - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: nginx - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: nginx - - name: ISTIO_META_OWNER - value: kubernetes://apis/v1/namespaces/default/replicationcontrollers/nginx - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: - replicas: 0 ---- diff --git a/pkg/kube/inject/testdata/inject/resource_annotations.yaml b/pkg/kube/inject/testdata/inject/resource_annotations.yaml deleted file mode 100644 index 62e60e493..000000000 --- a/pkg/kube/inject/testdata/inject/resource_annotations.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: resource -spec: - replicas: 7 - selector: - matchLabels: - app: resource - template: - metadata: - annotations: - sidecar.istio.io/proxyCPU: "100m" - sidecar.istio.io/proxyCPULimit: "1000m" - sidecar.istio.io/proxyMemory: "1Gi" - sidecar.istio.io/proxyMemoryLimit: "2Gi" - labels: - app: resource - spec: - containers: - - name: resource - image: "fake.docker.io/google-samples/traffic-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/resource_annotations.yaml.injected b/pkg/kube/inject/testdata/inject/resource_annotations.yaml.injected deleted file mode 100644 index 2ab7615f0..000000000 --- a/pkg/kube/inject/testdata/inject/resource_annotations.yaml.injected +++ /dev/null @@ -1,215 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: resource -spec: - replicas: 7 - selector: - matchLabels: - app: resource - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: resource - kubectl.kubernetes.io/default-logs-container: resource - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/proxyCPU: 100m - sidecar.istio.io/proxyCPULimit: 1000m - sidecar.istio.io/proxyMemory: 1Gi - sidecar.istio.io/proxyMemoryLimit: 2Gi - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: resource - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: resource - service.istio.io/canonical-revision: latest - spec: - containers: - - image: fake.docker.io/google-samples/traffic-go-gke:1.0 - name: resource - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: resource - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: resource - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/resource - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "1" - memory: 2Gi - requests: - cpu: 100m - memory: 1Gi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "1" - memory: 2Gi - requests: - cpu: 100m - memory: 1Gi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/startup_live.yaml b/pkg/kube/inject/testdata/inject/startup_live.yaml deleted file mode 100644 index 6db195e4f..000000000 --- a/pkg/kube/inject/testdata/inject/startup_live.yaml +++ /dev/null @@ -1,43 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - livenessProbe: - httpGet: - port: http - startupProbe: - httpGet: - port: 3333 - - name: world - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 90 - livenessProbe: - httpGet: - port: http - startupProbe: - exec: - command: - - cat - - /tmp/healthy diff --git a/pkg/kube/inject/testdata/inject/startup_live.yaml.injected b/pkg/kube/inject/testdata/inject/startup_live.yaml.injected deleted file mode 100644 index c4d173f80..000000000 --- a/pkg/kube/inject/testdata/inject/startup_live.yaml.injected +++ /dev/null @@ -1,241 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - path: /app-health/hello/livez - port: 15020 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - startupProbe: - httpGet: - path: /app-health/hello/startupz - port: 15020 - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - path: /app-health/world/livez - port: 15020 - name: world - ports: - - containerPort: 90 - name: http - resources: {} - startupProbe: - exec: - command: - - cat - - /tmp/healthy - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ,{"name":"http","containerPort":90} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello,world - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - - name: ISTIO_KUBE_APP_PROBERS - value: '{"/app-health/hello/livez":{"httpGet":{"port":80}},"/app-health/hello/startupz":{"httpGet":{"port":3333}},"/app-health/world/livez":{"httpGet":{"port":90}}}' - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/startup_only.yaml b/pkg/kube/inject/testdata/inject/startup_only.yaml deleted file mode 100644 index 3a5a3e8b8..000000000 --- a/pkg/kube/inject/testdata/inject/startup_only.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - startupProbe: - httpGet: - port: 3333 diff --git a/pkg/kube/inject/testdata/inject/startup_only.yaml.injected b/pkg/kube/inject/testdata/inject/startup_only.yaml.injected deleted file mode 100644 index 0c232a7b1..000000000 --- a/pkg/kube/inject/testdata/inject/startup_only.yaml.injected +++ /dev/null @@ -1,221 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - startupProbe: - httpGet: - path: /app-health/hello/startupz - port: 15020 - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - - name: ISTIO_KUBE_APP_PROBERS - value: '{"/app-health/hello/startupz":{"httpGet":{"port":3333}}}' - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/startup_ready_live.yaml b/pkg/kube/inject/testdata/inject/startup_ready_live.yaml deleted file mode 100644 index 374ccad60..000000000 --- a/pkg/kube/inject/testdata/inject/startup_ready_live.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - startupProbe: - httpGet: - port: 3333 - livenessProbe: - httpGet: - port: http - readinessProbe: - httpGet: - port: 3333 - - name: world - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 90 - startupProbe: - httpGet: - port: http - livenessProbe: - httpGet: - port: http - readinessProbe: - exec: - command: - - cat - - /tmp/healthy diff --git a/pkg/kube/inject/testdata/inject/startup_ready_live.yaml.injected b/pkg/kube/inject/testdata/inject/startup_ready_live.yaml.injected deleted file mode 100644 index 5ef2bbba0..000000000 --- a/pkg/kube/inject/testdata/inject/startup_ready_live.yaml.injected +++ /dev/null @@ -1,249 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - path: /app-health/hello/livez - port: 15020 - name: hello - ports: - - containerPort: 80 - name: http - readinessProbe: - httpGet: - path: /app-health/hello/readyz - port: 15020 - resources: {} - startupProbe: - httpGet: - path: /app-health/hello/startupz - port: 15020 - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - path: /app-health/world/livez - port: 15020 - name: world - ports: - - containerPort: 90 - name: http - readinessProbe: - exec: - command: - - cat - - /tmp/healthy - resources: {} - startupProbe: - httpGet: - path: /app-health/world/startupz - port: 15020 - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ,{"name":"http","containerPort":90} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello,world - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - - name: ISTIO_KUBE_APP_PROBERS - value: '{"/app-health/hello/livez":{"httpGet":{"port":80}},"/app-health/hello/readyz":{"httpGet":{"port":3333}},"/app-health/hello/startupz":{"httpGet":{"port":3333}},"/app-health/world/livez":{"httpGet":{"port":90}},"/app-health/world/startupz":{"httpGet":{"port":90}}}' - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/statefulset.yaml b/pkg/kube/inject/testdata/inject/statefulset.yaml deleted file mode 100644 index bc0b4ed1d..000000000 --- a/pkg/kube/inject/testdata/inject/statefulset.yaml +++ /dev/null @@ -1,32 +0,0 @@ -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: hello -spec: - serviceName: hello - selector: - matchLabels: - app: hello - tier: backend - track: stable - replicas: 3 - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - name: hello - ports: - - containerPort: 80 - name: http - volumeMounts: - - name: "data" - mountPath: "/var/lib/data" - volumes: - - name: data - hostPath: - path: "/mnt/disks/ssd0" diff --git a/pkg/kube/inject/testdata/inject/statefulset.yaml.injected b/pkg/kube/inject/testdata/inject/statefulset.yaml.injected deleted file mode 100644 index acf1677a4..000000000 --- a/pkg/kube/inject/testdata/inject/statefulset.yaml.injected +++ /dev/null @@ -1,224 +0,0 @@ -apiVersion: apps/v1 -kind: StatefulSet -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 3 - selector: - matchLabels: - app: hello - tier: backend - track: stable - serviceName: hello - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - resources: {} - volumeMounts: - - mountPath: /var/lib/data - name: data - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/statefulsets/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert - - hostPath: - path: /mnt/disks/ssd0 - name: data - updateStrategy: {} -status: - availableReplicas: 0 - replicas: 0 ---- diff --git a/pkg/kube/inject/testdata/inject/status_annotations.yaml b/pkg/kube/inject/testdata/inject/status_annotations.yaml deleted file mode 100644 index 853afb191..000000000 --- a/pkg/kube/inject/testdata/inject/status_annotations.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: statusPort -spec: - replicas: 7 - selector: - matchLabels: - app: status - template: - metadata: - annotations: - status.sidecar.istio.io/port: "123" - readiness.status.sidecar.istio.io/initialDelaySeconds: "100" - readiness.status.sidecar.istio.io/periodSeconds: "200" - readiness.status.sidecar.istio.io/failureThreshold: "300" - readiness.status.sidecar.istio.io/applicationPorts: "1,2,3" - labels: - app: status - spec: - containers: - - name: status - image: "fake.docker.io/google-samples/traffic-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/status_annotations.yaml.injected b/pkg/kube/inject/testdata/inject/status_annotations.yaml.injected deleted file mode 100644 index 5804ac91e..000000000 --- a/pkg/kube/inject/testdata/inject/status_annotations.yaml.injected +++ /dev/null @@ -1,216 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: statusPort -spec: - replicas: 7 - selector: - matchLabels: - app: status - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: status - kubectl.kubernetes.io/default-logs-container: status - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - readiness.status.sidecar.istio.io/applicationPorts: 1,2,3 - readiness.status.sidecar.istio.io/failureThreshold: "300" - readiness.status.sidecar.istio.io/initialDelaySeconds: "100" - readiness.status.sidecar.istio.io/periodSeconds: "200" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - status.sidecar.istio.io/port: "123" - creationTimestamp: null - labels: - app: status - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: status - service.istio.io/canonical-revision: latest - spec: - containers: - - image: fake.docker.io/google-samples/traffic-go-gke:1.0 - name: status - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: status - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: statusPort - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/statusPort - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 300 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 100 - periodSeconds: 200 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,123 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/status_annotations_zeroport.yaml b/pkg/kube/inject/testdata/inject/status_annotations_zeroport.yaml deleted file mode 100644 index 7f95d7cc1..000000000 --- a/pkg/kube/inject/testdata/inject/status_annotations_zeroport.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: statusPort -spec: - replicas: 7 - selector: - matchLabels: - app: status - template: - metadata: - annotations: - status.sidecar.istio.io/port: "0" - readiness.status.sidecar.istio.io/initialDelaySeconds: "100" - readiness.status.sidecar.istio.io/periodSeconds: "200" - readiness.status.sidecar.istio.io/failureThreshold: "300" - readiness.status.sidecar.istio.io/applicationPorts: "1,2,3" - labels: - app: status - spec: - containers: - - name: status - image: "fake.docker.io/google-samples/traffic-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/status_annotations_zeroport.yaml.injected b/pkg/kube/inject/testdata/inject/status_annotations_zeroport.yaml.injected deleted file mode 100644 index dce88ca95..000000000 --- a/pkg/kube/inject/testdata/inject/status_annotations_zeroport.yaml.injected +++ /dev/null @@ -1,208 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: statusPort -spec: - replicas: 7 - selector: - matchLabels: - app: status - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: status - kubectl.kubernetes.io/default-logs-container: status - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - readiness.status.sidecar.istio.io/applicationPorts: 1,2,3 - readiness.status.sidecar.istio.io/failureThreshold: "300" - readiness.status.sidecar.istio.io/initialDelaySeconds: "100" - readiness.status.sidecar.istio.io/periodSeconds: "200" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - status.sidecar.istio.io/port: "0" - creationTimestamp: null - labels: - app: status - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: status - service.istio.io/canonical-revision: latest - spec: - containers: - - image: fake.docker.io/google-samples/traffic-go-gke:1.0 - name: status - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: status - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: statusPort - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/statusPort - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/status_params.yaml b/pkg/kube/inject/testdata/inject/status_params.yaml deleted file mode 100644 index 7dde974c1..000000000 --- a/pkg/kube/inject/testdata/inject/status_params.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: statusPort -spec: - replicas: 7 - selector: - matchLabels: - app: status - template: - metadata: - labels: - app: status - spec: - containers: - - name: status - image: "fake.docker.io/google-samples/traffic-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/status_params.yaml.injected b/pkg/kube/inject/testdata/inject/status_params.yaml.injected deleted file mode 100644 index 5a5601f00..000000000 --- a/pkg/kube/inject/testdata/inject/status_params.yaml.injected +++ /dev/null @@ -1,211 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: statusPort -spec: - replicas: 7 - selector: - matchLabels: - app: status - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: status - kubectl.kubernetes.io/default-logs-container: status - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: status - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: status - service.istio.io/canonical-revision: latest - spec: - containers: - - image: fake.docker.io/google-samples/traffic-go-gke:1.0 - name: status - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: status - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: statusPort - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/statusPort - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 300 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 100 - periodSeconds: 200 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,123 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/tcp-probes-disabled.yaml.injected b/pkg/kube/inject/testdata/inject/tcp-probes-disabled.yaml.injected deleted file mode 100644 index 53279e0e6..000000000 --- a/pkg/kube/inject/testdata/inject/tcp-probes-disabled.yaml.injected +++ /dev/null @@ -1,221 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - tcpSocket: - port: http - name: hello - ports: - - containerPort: 80 - name: http - readinessProbe: - tcpSocket: - port: 3333 - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/tcp-probes.yaml b/pkg/kube/inject/testdata/inject/tcp-probes.yaml deleted file mode 100644 index b139dc3fb..000000000 --- a/pkg/kube/inject/testdata/inject/tcp-probes.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - livenessProbe: - tcpSocket: - port: http - readinessProbe: - tcpSocket: - port: 3333 diff --git a/pkg/kube/inject/testdata/inject/tcp-probes.yaml.injected b/pkg/kube/inject/testdata/inject/tcp-probes.yaml.injected deleted file mode 100644 index 84650e47c..000000000 --- a/pkg/kube/inject/testdata/inject/tcp-probes.yaml.injected +++ /dev/null @@ -1,225 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - livenessProbe: - httpGet: - path: /app-health/hello/livez - port: 15020 - name: hello - ports: - - containerPort: 80 - name: http - readinessProbe: - httpGet: - path: /app-health/hello/readyz - port: 15020 - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - - name: ISTIO_KUBE_APP_PROBERS - value: '{"/app-health/hello/livez":{"tcpSocket":{"port":80}},"/app-health/hello/readyz":{"tcpSocket":{"port":3333}}}' - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/traffic-annotations-bad-excludeinboundports.yaml b/pkg/kube/inject/testdata/inject/traffic-annotations-bad-excludeinboundports.yaml deleted file mode 100644 index fc8df334b..000000000 --- a/pkg/kube/inject/testdata/inject/traffic-annotations-bad-excludeinboundports.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: traffic -spec: - replicas: 7 - selector: - matchLabels: - app: traffic - template: - metadata: - annotations: - traffic.sidecar.istio.io/excludeInboundPorts: "*" - labels: - app: traffic - spec: - containers: - - name: traffic - image: "fake.docker.io/google-samples/traffic-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/traffic-annotations-bad-excludeipranges.yaml b/pkg/kube/inject/testdata/inject/traffic-annotations-bad-excludeipranges.yaml deleted file mode 100644 index 210c4e93f..000000000 --- a/pkg/kube/inject/testdata/inject/traffic-annotations-bad-excludeipranges.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: traffic -spec: - replicas: 7 - selector: - matchLabels: - app: traffic - template: - metadata: - annotations: - traffic.sidecar.istio.io/excludeOutboundIPRanges: "*" - labels: - app: traffic - spec: - containers: - - name: traffic - image: "fake.docker.io/google-samples/traffic-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/traffic-annotations-bad-excludeoutboundports.yaml b/pkg/kube/inject/testdata/inject/traffic-annotations-bad-excludeoutboundports.yaml deleted file mode 100644 index 88816d654..000000000 --- a/pkg/kube/inject/testdata/inject/traffic-annotations-bad-excludeoutboundports.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: traffic -spec: - replicas: 7 - selector: - matchLabels: - app: traffic - template: - metadata: - annotations: - traffic.sidecar.istio.io/excludeOutboundPorts: "bad" - labels: - app: traffic - spec: - containers: - - name: traffic - image: "fake.docker.io/google-samples/traffic-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/traffic-annotations-bad-includeinboundports.yaml b/pkg/kube/inject/testdata/inject/traffic-annotations-bad-includeinboundports.yaml deleted file mode 100644 index 2d79d2c2f..000000000 --- a/pkg/kube/inject/testdata/inject/traffic-annotations-bad-includeinboundports.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: traffic -spec: - replicas: 7 - selector: - matchLabels: - app: traffic - template: - metadata: - annotations: - traffic.sidecar.istio.io/includeInboundPorts: "bad" - labels: - app: traffic - spec: - containers: - - name: traffic - image: "fake.docker.io/google-samples/traffic-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/traffic-annotations-bad-includeipranges.yaml b/pkg/kube/inject/testdata/inject/traffic-annotations-bad-includeipranges.yaml deleted file mode 100644 index 5f133be26..000000000 --- a/pkg/kube/inject/testdata/inject/traffic-annotations-bad-includeipranges.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: traffic -spec: - replicas: 7 - selector: - matchLabels: - app: traffic - template: - metadata: - annotations: - traffic.sidecar.istio.io/includeOutboundIPRanges: "bad" - labels: - app: traffic - spec: - containers: - - name: traffic - image: "fake.docker.io/google-samples/traffic-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/traffic-annotations-empty-includes.yaml b/pkg/kube/inject/testdata/inject/traffic-annotations-empty-includes.yaml deleted file mode 100644 index bbb59dca6..000000000 --- a/pkg/kube/inject/testdata/inject/traffic-annotations-empty-includes.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: traffic -spec: - replicas: 7 - selector: - matchLabels: - app: traffic - template: - metadata: - annotations: - traffic.sidecar.istio.io/includeInboundPorts: "" - traffic.sidecar.istio.io/excludeInboundPorts: "4,5,6" - traffic.sidecar.istio.io/includeOutboundIPRanges: "" - traffic.sidecar.istio.io/excludeOutboundIPRanges: "10.96.0.2/24,10.96.0.3/24" - labels: - app: traffic - spec: - containers: - - name: traffic - image: "fake.docker.io/google-samples/traffic-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/traffic-annotations-empty-includes.yaml.injected b/pkg/kube/inject/testdata/inject/traffic-annotations-empty-includes.yaml.injected deleted file mode 100644 index a9f6a8535..000000000 --- a/pkg/kube/inject/testdata/inject/traffic-annotations-empty-includes.yaml.injected +++ /dev/null @@ -1,215 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: traffic -spec: - replicas: 7 - selector: - matchLabels: - app: traffic - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: traffic - kubectl.kubernetes.io/default-logs-container: traffic - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - traffic.sidecar.istio.io/excludeInboundPorts: 4,5,6 - traffic.sidecar.istio.io/excludeOutboundIPRanges: 10.96.0.2/24,10.96.0.3/24 - traffic.sidecar.istio.io/includeInboundPorts: "" - traffic.sidecar.istio.io/includeOutboundIPRanges: "" - creationTimestamp: null - labels: - app: traffic - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: traffic - service.istio.io/canonical-revision: latest - spec: - containers: - - image: fake.docker.io/google-samples/traffic-go-gke:1.0 - name: traffic - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: traffic - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: traffic - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/traffic - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - "" - - -x - - 10.96.0.2/24,10.96.0.3/24 - - -b - - "" - - -d - - 15090,15021,4,5,6,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/traffic-annotations-wildcards.yaml b/pkg/kube/inject/testdata/inject/traffic-annotations-wildcards.yaml deleted file mode 100644 index c70f6bee0..000000000 --- a/pkg/kube/inject/testdata/inject/traffic-annotations-wildcards.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: traffic -spec: - replicas: 7 - selector: - matchLabels: - app: traffic - template: - metadata: - annotations: - traffic.sidecar.istio.io/includeInboundPorts: "*" - traffic.sidecar.istio.io/excludeInboundPorts: "4,5,6" - traffic.sidecar.istio.io/includeOutboundIPRanges: "*" - traffic.sidecar.istio.io/excludeOutboundIPRanges: "10.96.0.2/24,10.96.0.3/24" - labels: - app: traffic - spec: - containers: - - name: traffic - image: "fake.docker.io/google-samples/traffic-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/traffic-annotations-wildcards.yaml.injected b/pkg/kube/inject/testdata/inject/traffic-annotations-wildcards.yaml.injected deleted file mode 100644 index d4648d51b..000000000 --- a/pkg/kube/inject/testdata/inject/traffic-annotations-wildcards.yaml.injected +++ /dev/null @@ -1,215 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: traffic -spec: - replicas: 7 - selector: - matchLabels: - app: traffic - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: traffic - kubectl.kubernetes.io/default-logs-container: traffic - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - traffic.sidecar.istio.io/excludeInboundPorts: 4,5,6 - traffic.sidecar.istio.io/excludeOutboundIPRanges: 10.96.0.2/24,10.96.0.3/24 - traffic.sidecar.istio.io/includeInboundPorts: '*' - traffic.sidecar.istio.io/includeOutboundIPRanges: '*' - creationTimestamp: null - labels: - app: traffic - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: traffic - service.istio.io/canonical-revision: latest - spec: - containers: - - image: fake.docker.io/google-samples/traffic-go-gke:1.0 - name: traffic - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: traffic - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: traffic - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/traffic - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - 10.96.0.2/24,10.96.0.3/24 - - -b - - '*' - - -d - - 15090,15021,4,5,6,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/traffic-annotations.yaml b/pkg/kube/inject/testdata/inject/traffic-annotations.yaml deleted file mode 100644 index 8903eb8a0..000000000 --- a/pkg/kube/inject/testdata/inject/traffic-annotations.yaml +++ /dev/null @@ -1,34 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: traffic -spec: - replicas: 7 - selector: - matchLabels: - app: traffic - template: - metadata: - annotations: - # We set 4 CPUs here and concurrency=0 below. Expect concurrency to be set to 4. - sidecar.istio.io/proxyCPU: 4000m - traffic.sidecar.istio.io/includeInboundPorts: "1,2,3" - traffic.sidecar.istio.io/excludeInboundPorts: "4,5,6" - traffic.sidecar.istio.io/excludeOutboundPorts: "7,8,9" - traffic.sidecar.istio.io/includeOutboundIPRanges: "127.0.0.1/24,10.96.0.1/24" - traffic.sidecar.istio.io/excludeOutboundIPRanges: "10.96.0.2/24,10.96.0.3/24" - proxy.istio.io/config: |- - discoveryAddress: foo:123 - concurrency: 0 - proxyMetadata: - FOO: bar - ISTIO_META_TLS_CLIENT_KEY: /etc/identity2/client/keys/client-key.pem - labels: - app: traffic - spec: - containers: - - name: traffic - image: "fake.docker.io/google-samples/traffic-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/traffic-annotations.yaml.injected b/pkg/kube/inject/testdata/inject/traffic-annotations.yaml.injected deleted file mode 100644 index b957dfa99..000000000 --- a/pkg/kube/inject/testdata/inject/traffic-annotations.yaml.injected +++ /dev/null @@ -1,226 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: traffic -spec: - replicas: 7 - selector: - matchLabels: - app: traffic - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: traffic - kubectl.kubernetes.io/default-logs-container: traffic - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - proxy.istio.io/config: |- - discoveryAddress: foo:123 - concurrency: 0 - proxyMetadata: - FOO: bar - ISTIO_META_TLS_CLIENT_KEY: /etc/identity2/client/keys/client-key.pem - sidecar.istio.io/proxyCPU: 4000m - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - traffic.sidecar.istio.io/excludeInboundPorts: 4,5,6 - traffic.sidecar.istio.io/excludeOutboundIPRanges: 10.96.0.2/24,10.96.0.3/24 - traffic.sidecar.istio.io/excludeOutboundPorts: 7,8,9 - traffic.sidecar.istio.io/includeInboundPorts: 1,2,3 - traffic.sidecar.istio.io/includeOutboundIPRanges: 127.0.0.1/24,10.96.0.1/24 - creationTimestamp: null - labels: - app: traffic - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: traffic - service.istio.io/canonical-revision: latest - spec: - containers: - - image: fake.docker.io/google-samples/traffic-go-gke:1.0 - name: traffic - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "4" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {"discoveryAddress":"foo:123","concurrency":0,"proxyMetadata":{"FOO":"bar","ISTIO_META_TLS_CLIENT_KEY":"/etc/identity2/client/keys/client-key.pem"}} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: traffic - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: traffic - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/traffic - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - - name: FOO - value: bar - - name: ISTIO_META_TLS_CLIENT_KEY - value: /etc/identity2/client/keys/client-key.pem - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - requests: - cpu: "4" - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - 127.0.0.1/24,10.96.0.1/24 - - -x - - 10.96.0.2/24,10.96.0.3/24 - - -b - - 1,2,3 - - -d - - 15090,15021,4,5,6,15020 - - -o - - 7,8,9 - env: - - name: FOO - value: bar - - name: ISTIO_META_TLS_CLIENT_KEY - value: /etc/identity2/client/keys/client-key.pem - image: apache/dubbo-agent:latest - name: istio-init - resources: - requests: - cpu: "4" - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/traffic-params-empty-includes.yaml b/pkg/kube/inject/testdata/inject/traffic-params-empty-includes.yaml deleted file mode 100644 index 290830a4a..000000000 --- a/pkg/kube/inject/testdata/inject/traffic-params-empty-includes.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: traffic -spec: - replicas: 7 - selector: - matchLabels: - app: traffic - template: - metadata: - labels: - app: traffic - spec: - containers: - - name: traffic - image: "fake.docker.io/google-samples/traffic-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/traffic-params-empty-includes.yaml.injected b/pkg/kube/inject/testdata/inject/traffic-params-empty-includes.yaml.injected deleted file mode 100644 index 65c69ae24..000000000 --- a/pkg/kube/inject/testdata/inject/traffic-params-empty-includes.yaml.injected +++ /dev/null @@ -1,211 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: traffic -spec: - replicas: 7 - selector: - matchLabels: - app: traffic - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: traffic - kubectl.kubernetes.io/default-logs-container: traffic - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: traffic - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: traffic - service.istio.io/canonical-revision: latest - spec: - containers: - - image: fake.docker.io/google-samples/traffic-go-gke:1.0 - name: traffic - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: traffic - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: traffic - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/traffic - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/traffic-params.yaml b/pkg/kube/inject/testdata/inject/traffic-params.yaml deleted file mode 100644 index 290830a4a..000000000 --- a/pkg/kube/inject/testdata/inject/traffic-params.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: traffic -spec: - replicas: 7 - selector: - matchLabels: - app: traffic - template: - metadata: - labels: - app: traffic - spec: - containers: - - name: traffic - image: "fake.docker.io/google-samples/traffic-go-gke:1.0" - ports: - - name: http - containerPort: 80 diff --git a/pkg/kube/inject/testdata/inject/traffic-params.yaml.injected b/pkg/kube/inject/testdata/inject/traffic-params.yaml.injected deleted file mode 100644 index db130eb5b..000000000 --- a/pkg/kube/inject/testdata/inject/traffic-params.yaml.injected +++ /dev/null @@ -1,203 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: traffic -spec: - replicas: 7 - selector: - matchLabels: - app: traffic - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: traffic - kubectl.kubernetes.io/default-logs-container: traffic - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: traffic - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: traffic - service.istio.io/canonical-revision: latest - spec: - containers: - - image: fake.docker.io/google-samples/traffic-go-gke:1.0 - name: traffic - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: traffic - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: traffic - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/traffic - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - 127.0.0.1/24,10.96.0.1/24 - - -x - - 10.96.0.2/24,10.96.0.3/24 - - -b - - '*' - - -d - - 15090,15021,4,5,6 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/two_container.yaml b/pkg/kube/inject/testdata/inject/two_container.yaml deleted file mode 100644 index 682491307..000000000 --- a/pkg/kube/inject/testdata/inject/two_container.yaml +++ /dev/null @@ -1,37 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - template: - metadata: - labels: - app: hello - tier: backend - track: stable - spec: - containers: - - name: hello - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - readinessProbe: - httpGet: - path: /ip - port: 8000 - - name: world - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 90 - readinessProbe: - httpGet: - path: /ipv6 - port: 9000 diff --git a/pkg/kube/inject/testdata/inject/two_container.yaml.injected b/pkg/kube/inject/testdata/inject/two_container.yaml.injected deleted file mode 100644 index 4507c0744..000000000 --- a/pkg/kube/inject/testdata/inject/two_container.yaml.injected +++ /dev/null @@ -1,232 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: hello -spec: - replicas: 7 - selector: - matchLabels: - app: hello - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: hello - kubectl.kubernetes.io/default-logs-container: hello - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}' - creationTimestamp: null - labels: - app: hello - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: hello - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: hello - ports: - - containerPort: 80 - name: http - readinessProbe: - httpGet: - path: /app-health/hello/readyz - port: 15020 - resources: {} - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: world - ports: - - containerPort: 90 - name: http - readinessProbe: - httpGet: - path: /app-health/world/readyz - port: 15020 - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ,{"name":"http","containerPort":90} - ] - - name: ISTIO_META_APP_CONTAINERS - value: hello,world - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: hello - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/hello - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - - name: ISTIO_KUBE_APP_PROBERS - value: '{"/app-health/hello/readyz":{"httpGet":{"path":"/ip","port":8000}},"/app-health/world/readyz":{"httpGet":{"path":"/ipv6","port":9000}}}' - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert -status: {} ---- diff --git a/pkg/kube/inject/testdata/inject/user-volume.yaml b/pkg/kube/inject/testdata/inject/user-volume.yaml deleted file mode 100644 index 309598b96..000000000 --- a/pkg/kube/inject/testdata/inject/user-volume.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: user-volume -spec: - replicas: 1 - selector: - matchLabels: - app: user-volume - tier: backend - track: stable - template: - metadata: - labels: - app: user-volume - tier: backend - track: stable - annotations: - sidecar.istio.io/userVolumeMount: '{"user-volume-1":{"mountPath":"/mnt/volume-1","readOnly":true},"user-volume-2":{"mountPath":"/mnt/volume-2"}}' - sidecar.istio.io/userVolume: '{"user-volume-1":{"persistentVolumeClaim":{"claimName":"pvc-claim"}},"user-volume-2":{"configMap":{"name":"configmap-volume","items":[{"key":"some-key","path":"/some-path"}]}}}' - spec: - containers: - - name: user-volume - image: "fake.docker.io/google-samples/hello-go-gke:1.0" - ports: - - name: http - containerPort: 80 - diff --git a/pkg/kube/inject/testdata/inject/user-volume.yaml.injected b/pkg/kube/inject/testdata/inject/user-volume.yaml.injected deleted file mode 100644 index 5468b77e0..000000000 --- a/pkg/kube/inject/testdata/inject/user-volume.yaml.injected +++ /dev/null @@ -1,231 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - name: user-volume -spec: - replicas: 1 - selector: - matchLabels: - app: user-volume - tier: backend - track: stable - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: user-volume - kubectl.kubernetes.io/default-logs-container: user-volume - prometheus.io/path: /stats/prometheus - prometheus.io/port: "15020" - prometheus.io/scrape: "true" - sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["workload-socket","workload-certs","istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert","user-volume-1","user-volume-2"],"imagePullSecrets":null,"revision":"default"}' - sidecar.istio.io/userVolume: '{"user-volume-1":{"persistentVolumeClaim":{"claimName":"pvc-claim"}},"user-volume-2":{"configMap":{"name":"configmap-volume","items":[{"key":"some-key","path":"/some-path"}]}}}' - sidecar.istio.io/userVolumeMount: '{"user-volume-1":{"mountPath":"/mnt/volume-1","readOnly":true},"user-volume-2":{"mountPath":"/mnt/volume-2"}}' - creationTimestamp: null - labels: - app: user-volume - security.istio.io/tlsMode: istio - service.istio.io/canonical-name: user-volume - service.istio.io/canonical-revision: latest - tier: backend - track: stable - spec: - containers: - - image: fake.docker.io/google-samples/hello-go-gke:1.0 - name: user-volume - ports: - - containerPort: 80 - name: http - resources: {} - - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.cluster.local - - --proxyLogLevel=warning - - --proxyComponentLogLevel=misc:error - - --log_output_level=default:info - - --concurrency - - "2" - env: - - name: JWT_POLICY - value: third-party-jwt - - name: PILOT_CERT_PROVIDER - value: istiod - - name: CA_ADDR - value: istiod.dubbo-system.svc:15012 - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {"name":"http","containerPort":80} - ] - - name: ISTIO_META_APP_CONTAINERS - value: user-volume - - name: ISTIO_META_CLUSTER_ID - value: Kubernetes - - name: ISTIO_META_INTERCEPTION_MODE - value: REDIRECT - - name: ISTIO_META_WORKLOAD_NAME - value: user-volume - - name: ISTIO_META_OWNER - value: kubernetes://apis/apps/v1/namespaces/default/deployments/user-volume - - name: ISTIO_META_MESH_ID - value: cluster.local - - name: TRUST_DOMAIN - value: cluster.local - image: apache/dubbo-agent:latest - name: istio-proxy - ports: - - containerPort: 15090 - name: http-envoy-prom - protocol: TCP - readinessProbe: - failureThreshold: 30 - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: 1 - periodSeconds: 2 - timeoutSeconds: 3 - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsNonRoot: true - runAsUser: 1337 - volumeMounts: - - mountPath: /var/run/secrets/workload-spiffe-uds - name: workload-socket - - mountPath: /var/run/secrets/workload-spiffe-credentials - name: workload-certs - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - - mountPath: /var/lib/istio/data - name: istio-data - - mountPath: /etc/istio/proxy - name: istio-envoy - - mountPath: /var/run/secrets/tokens - name: istio-token - - mountPath: /etc/istio/pod - name: istio-podinfo - - mountPath: /mnt/volume-1 - name: user-volume-1 - readOnly: true - - mountPath: /mnt/volume-2 - name: user-volume-2 - initContainers: - - args: - - istio-iptables - - -p - - "15001" - - -z - - "15006" - - -u - - "1337" - - -m - - REDIRECT - - -i - - '*' - - -x - - "" - - -b - - '*' - - -d - - 15090,15021,15020 - image: apache/dubbo-agent:latest - name: istio-init - resources: - limits: - cpu: "2" - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - drop: - - ALL - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - securityContext: - fsGroup: 1337 - volumes: - - name: workload-socket - - name: workload-certs - - emptyDir: - medium: Memory - name: istio-envoy - - emptyDir: {} - name: istio-data - - downwardAPI: - items: - - fieldRef: - fieldPath: metadata.labels - path: labels - - fieldRef: - fieldPath: metadata.annotations - path: annotations - name: istio-podinfo - - name: istio-token - projected: - sources: - - serviceAccountToken: - audience: istio-ca - expirationSeconds: 43200 - path: istio-token - - configMap: - name: istio-ca-root-cert - name: istiod-ca-cert - - name: user-volume-1 - persistentVolumeClaim: - claimName: pvc-claim - - configMap: - items: - - key: some-key - path: /some-path - name: configmap-volume - name: user-volume-2 -status: {} ---- diff --git a/pkg/kube/inject/testdata/inputs/custom-template.yaml.34.mesh.gen.yaml b/pkg/kube/inject/testdata/inputs/custom-template.yaml.34.mesh.gen.yaml deleted file mode 100644 index 8317bc059..000000000 --- a/pkg/kube/inject/testdata/inputs/custom-template.yaml.34.mesh.gen.yaml +++ /dev/null @@ -1,9 +0,0 @@ -defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 -enablePrometheusMerge: true -rootNamespace: dubbo-system -trustDomain: cluster.local \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/custom-template.yaml.34.template.gen.yaml b/pkg/kube/inject/testdata/inputs/custom-template.yaml.34.template.gen.yaml deleted file mode 100644 index 10b646759..000000000 --- a/pkg/kube/inject/testdata/inputs/custom-template.yaml.34.template.gen.yaml +++ /dev/null @@ -1,1228 +0,0 @@ -# defaultTemplates defines the default template to use for pods that do not explicitly specify a template -defaultTemplates: [sidecar] -policy: enabled -alwaysInjectSelector: - [] -neverInjectSelector: - [] -injectedAnnotations: -template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" -templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - dubbo: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} - custom: | - metadata: - annotations: - # Disable the built-in transformations. In the future we may want a template-level API - prometheus.istio.io/merge-metrics: "false" - sidecar.istio.io/rewriteAppHTTPProbers: "false" - foo: bar - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: SOME_ENV - value: "true" - - name: SOME_FILE - value: /var/lib/data/foo.json - volumeMounts: - - mountPath: /var/lib/data/foo.json - subPath: foo.json - name: some-injected-file - {{- end}} - volumes: - - name: some-injected-file - downwardAPI: - items: - - path: foo.json - fieldRef: - fieldPath: "metadata.annotations['foo']" \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/custom-template.yaml.34.values.gen.yaml b/pkg/kube/inject/testdata/inputs/custom-template.yaml.34.values.gen.yaml deleted file mode 100644 index 3ef230f01..000000000 --- a/pkg/kube/inject/testdata/inputs/custom-template.yaml.34.values.gen.yaml +++ /dev/null @@ -1,134 +0,0 @@ -{ - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": false, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "enabled": false - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": { - "custom": "metadata:\n annotations:\n # Disable the built-in transformations. In the future we may want a template-level API\n prometheus.istio.io/merge-metrics: \"false\"\n sidecar.istio.io/rewriteAppHTTPProbers: \"false\"\n foo: bar\nspec:\n containers:\n {{- range $index, $container := .Spec.Containers }}\n - name: {{ $container.Name }}\n env:\n - name: SOME_ENV\n value: \"true\"\n - name: SOME_FILE\n value: /var/lib/data/foo.json\n volumeMounts:\n - mountPath: /var/lib/data/foo.json\n subPath: foo.json\n name: some-injected-file\n {{- end}}\n volumes:\n - name: some-injected-file\n downwardAPI:\n items:\n - path: foo.json\n fieldRef:\n fieldPath: \"metadata.annotations['foo']\"\n" - } - } -} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/default.mesh.gen.yaml b/pkg/kube/inject/testdata/inputs/default.mesh.gen.yaml deleted file mode 100644 index 8317bc059..000000000 --- a/pkg/kube/inject/testdata/inputs/default.mesh.gen.yaml +++ /dev/null @@ -1,9 +0,0 @@ -defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 -enablePrometheusMerge: true -rootNamespace: dubbo-system -trustDomain: cluster.local \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/default.template.gen.yaml b/pkg/kube/inject/testdata/inputs/default.template.gen.yaml deleted file mode 100644 index 719f0bba7..000000000 --- a/pkg/kube/inject/testdata/inputs/default.template.gen.yaml +++ /dev/null @@ -1,1200 +0,0 @@ -# defaultTemplates defines the default template to use for pods that do not explicitly specify a template -defaultTemplates: [sidecar] -policy: enabled -alwaysInjectSelector: - [] -neverInjectSelector: - [] -injectedAnnotations: -template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" -templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - dubbo: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/default.values.gen.yaml b/pkg/kube/inject/testdata/inputs/default.values.gen.yaml deleted file mode 100644 index 6ff018940..000000000 --- a/pkg/kube/inject/testdata/inputs/default.values.gen.yaml +++ /dev/null @@ -1,132 +0,0 @@ -{ - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": false, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "enabled": false - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } -} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/enable-core-dump.yaml.5.mesh.gen.yaml b/pkg/kube/inject/testdata/inputs/enable-core-dump.yaml.5.mesh.gen.yaml deleted file mode 100644 index 8317bc059..000000000 --- a/pkg/kube/inject/testdata/inputs/enable-core-dump.yaml.5.mesh.gen.yaml +++ /dev/null @@ -1,9 +0,0 @@ -defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 -enablePrometheusMerge: true -rootNamespace: dubbo-system -trustDomain: cluster.local \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/enable-core-dump.yaml.5.template.gen.yaml b/pkg/kube/inject/testdata/inputs/enable-core-dump.yaml.5.template.gen.yaml deleted file mode 100644 index 719f0bba7..000000000 --- a/pkg/kube/inject/testdata/inputs/enable-core-dump.yaml.5.template.gen.yaml +++ /dev/null @@ -1,1200 +0,0 @@ -# defaultTemplates defines the default template to use for pods that do not explicitly specify a template -defaultTemplates: [sidecar] -policy: enabled -alwaysInjectSelector: - [] -neverInjectSelector: - [] -injectedAnnotations: -template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" -templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - dubbo: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/enable-core-dump.yaml.5.values.gen.yaml b/pkg/kube/inject/testdata/inputs/enable-core-dump.yaml.5.values.gen.yaml deleted file mode 100644 index 029cb9b99..000000000 --- a/pkg/kube/inject/testdata/inputs/enable-core-dump.yaml.5.values.gen.yaml +++ /dev/null @@ -1,132 +0,0 @@ -{ - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": false, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": true, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "enabled": false - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } -} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello-existing-cncf-networks-json.yaml.16.mesh.gen.yaml b/pkg/kube/inject/testdata/inputs/hello-existing-cncf-networks-json.yaml.16.mesh.gen.yaml deleted file mode 100644 index 8317bc059..000000000 --- a/pkg/kube/inject/testdata/inputs/hello-existing-cncf-networks-json.yaml.16.mesh.gen.yaml +++ /dev/null @@ -1,9 +0,0 @@ -defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 -enablePrometheusMerge: true -rootNamespace: dubbo-system -trustDomain: cluster.local \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello-existing-cncf-networks-json.yaml.16.template.gen.yaml b/pkg/kube/inject/testdata/inputs/hello-existing-cncf-networks-json.yaml.16.template.gen.yaml deleted file mode 100644 index 719f0bba7..000000000 --- a/pkg/kube/inject/testdata/inputs/hello-existing-cncf-networks-json.yaml.16.template.gen.yaml +++ /dev/null @@ -1,1200 +0,0 @@ -# defaultTemplates defines the default template to use for pods that do not explicitly specify a template -defaultTemplates: [sidecar] -policy: enabled -alwaysInjectSelector: - [] -neverInjectSelector: - [] -injectedAnnotations: -template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" -templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - dubbo: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello-existing-cncf-networks-json.yaml.16.values.gen.yaml b/pkg/kube/inject/testdata/inputs/hello-existing-cncf-networks-json.yaml.16.values.gen.yaml deleted file mode 100644 index bca4466e8..000000000 --- a/pkg/kube/inject/testdata/inputs/hello-existing-cncf-networks-json.yaml.16.values.gen.yaml +++ /dev/null @@ -1,133 +0,0 @@ -{ - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": false, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "chained": false, - "enabled": true - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } -} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello-existing-cncf-networks.yaml.15.mesh.gen.yaml b/pkg/kube/inject/testdata/inputs/hello-existing-cncf-networks.yaml.15.mesh.gen.yaml deleted file mode 100644 index 8317bc059..000000000 --- a/pkg/kube/inject/testdata/inputs/hello-existing-cncf-networks.yaml.15.mesh.gen.yaml +++ /dev/null @@ -1,9 +0,0 @@ -defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 -enablePrometheusMerge: true -rootNamespace: dubbo-system -trustDomain: cluster.local \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello-existing-cncf-networks.yaml.15.template.gen.yaml b/pkg/kube/inject/testdata/inputs/hello-existing-cncf-networks.yaml.15.template.gen.yaml deleted file mode 100644 index 719f0bba7..000000000 --- a/pkg/kube/inject/testdata/inputs/hello-existing-cncf-networks.yaml.15.template.gen.yaml +++ /dev/null @@ -1,1200 +0,0 @@ -# defaultTemplates defines the default template to use for pods that do not explicitly specify a template -defaultTemplates: [sidecar] -policy: enabled -alwaysInjectSelector: - [] -neverInjectSelector: - [] -injectedAnnotations: -template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" -templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - dubbo: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello-existing-cncf-networks.yaml.15.values.gen.yaml b/pkg/kube/inject/testdata/inputs/hello-existing-cncf-networks.yaml.15.values.gen.yaml deleted file mode 100644 index bca4466e8..000000000 --- a/pkg/kube/inject/testdata/inputs/hello-existing-cncf-networks.yaml.15.values.gen.yaml +++ /dev/null @@ -1,133 +0,0 @@ -{ - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": false, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "chained": false, - "enabled": true - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } -} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello-image-pull-secret.yaml.11.mesh.gen.yaml b/pkg/kube/inject/testdata/inputs/hello-image-pull-secret.yaml.11.mesh.gen.yaml deleted file mode 100644 index 8317bc059..000000000 --- a/pkg/kube/inject/testdata/inputs/hello-image-pull-secret.yaml.11.mesh.gen.yaml +++ /dev/null @@ -1,9 +0,0 @@ -defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 -enablePrometheusMerge: true -rootNamespace: dubbo-system -trustDomain: cluster.local \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello-image-pull-secret.yaml.11.template.gen.yaml b/pkg/kube/inject/testdata/inputs/hello-image-pull-secret.yaml.11.template.gen.yaml deleted file mode 100644 index 719f0bba7..000000000 --- a/pkg/kube/inject/testdata/inputs/hello-image-pull-secret.yaml.11.template.gen.yaml +++ /dev/null @@ -1,1200 +0,0 @@ -# defaultTemplates defines the default template to use for pods that do not explicitly specify a template -defaultTemplates: [sidecar] -policy: enabled -alwaysInjectSelector: - [] -neverInjectSelector: - [] -injectedAnnotations: -template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" -templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - dubbo: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello-image-pull-secret.yaml.11.values.gen.yaml b/pkg/kube/inject/testdata/inputs/hello-image-pull-secret.yaml.11.values.gen.yaml deleted file mode 100644 index 9b3ef8bb2..000000000 --- a/pkg/kube/inject/testdata/inputs/hello-image-pull-secret.yaml.11.values.gen.yaml +++ /dev/null @@ -1,134 +0,0 @@ -{ - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": false, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "", - "imagePullSecrets": [ - "barSecret" - ], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "enabled": false - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } -} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello-probes-noProxyHoldApplication-ProxyConfig.yaml.20.mesh.gen.yaml b/pkg/kube/inject/testdata/inputs/hello-probes-noProxyHoldApplication-ProxyConfig.yaml.20.mesh.gen.yaml deleted file mode 100644 index 8317bc059..000000000 --- a/pkg/kube/inject/testdata/inputs/hello-probes-noProxyHoldApplication-ProxyConfig.yaml.20.mesh.gen.yaml +++ /dev/null @@ -1,9 +0,0 @@ -defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 -enablePrometheusMerge: true -rootNamespace: dubbo-system -trustDomain: cluster.local \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello-probes-noProxyHoldApplication-ProxyConfig.yaml.20.template.gen.yaml b/pkg/kube/inject/testdata/inputs/hello-probes-noProxyHoldApplication-ProxyConfig.yaml.20.template.gen.yaml deleted file mode 100644 index 719f0bba7..000000000 --- a/pkg/kube/inject/testdata/inputs/hello-probes-noProxyHoldApplication-ProxyConfig.yaml.20.template.gen.yaml +++ /dev/null @@ -1,1200 +0,0 @@ -# defaultTemplates defines the default template to use for pods that do not explicitly specify a template -defaultTemplates: [sidecar] -policy: enabled -alwaysInjectSelector: - [] -neverInjectSelector: - [] -injectedAnnotations: -template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" -templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - dubbo: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello-probes-noProxyHoldApplication-ProxyConfig.yaml.20.values.gen.yaml b/pkg/kube/inject/testdata/inputs/hello-probes-noProxyHoldApplication-ProxyConfig.yaml.20.values.gen.yaml deleted file mode 100644 index c3f57fc5b..000000000 --- a/pkg/kube/inject/testdata/inputs/hello-probes-noProxyHoldApplication-ProxyConfig.yaml.20.values.gen.yaml +++ /dev/null @@ -1,132 +0,0 @@ -{ - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": false, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": true, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "enabled": false - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } -} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello-probes.yaml.18.mesh.gen.yaml b/pkg/kube/inject/testdata/inputs/hello-probes.yaml.18.mesh.gen.yaml deleted file mode 100644 index 8317bc059..000000000 --- a/pkg/kube/inject/testdata/inputs/hello-probes.yaml.18.mesh.gen.yaml +++ /dev/null @@ -1,9 +0,0 @@ -defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 -enablePrometheusMerge: true -rootNamespace: dubbo-system -trustDomain: cluster.local \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello-probes.yaml.18.template.gen.yaml b/pkg/kube/inject/testdata/inputs/hello-probes.yaml.18.template.gen.yaml deleted file mode 100644 index 719f0bba7..000000000 --- a/pkg/kube/inject/testdata/inputs/hello-probes.yaml.18.template.gen.yaml +++ /dev/null @@ -1,1200 +0,0 @@ -# defaultTemplates defines the default template to use for pods that do not explicitly specify a template -defaultTemplates: [sidecar] -policy: enabled -alwaysInjectSelector: - [] -neverInjectSelector: - [] -injectedAnnotations: -template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" -templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - dubbo: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello-probes.yaml.18.values.gen.yaml b/pkg/kube/inject/testdata/inputs/hello-probes.yaml.18.values.gen.yaml deleted file mode 100644 index c3f57fc5b..000000000 --- a/pkg/kube/inject/testdata/inputs/hello-probes.yaml.18.values.gen.yaml +++ /dev/null @@ -1,132 +0,0 @@ -{ - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": false, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": true, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "enabled": false - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } -} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.0.mesh.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.0.mesh.gen.yaml deleted file mode 100644 index 8317bc059..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.0.mesh.gen.yaml +++ /dev/null @@ -1,9 +0,0 @@ -defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 -enablePrometheusMerge: true -rootNamespace: dubbo-system -trustDomain: cluster.local \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.0.template.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.0.template.gen.yaml deleted file mode 100644 index 719f0bba7..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.0.template.gen.yaml +++ /dev/null @@ -1,1200 +0,0 @@ -# defaultTemplates defines the default template to use for pods that do not explicitly specify a template -defaultTemplates: [sidecar] -policy: enabled -alwaysInjectSelector: - [] -neverInjectSelector: - [] -injectedAnnotations: -template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" -templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - dubbo: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.0.values.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.0.values.gen.yaml deleted file mode 100644 index 7e21df930..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.0.values.gen.yaml +++ /dev/null @@ -1,133 +0,0 @@ -{ - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": false, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "network1", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "chained": true, - "enabled": true - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } -} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.1.mesh.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.1.mesh.gen.yaml deleted file mode 100644 index 8317bc059..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.1.mesh.gen.yaml +++ /dev/null @@ -1,9 +0,0 @@ -defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 -enablePrometheusMerge: true -rootNamespace: dubbo-system -trustDomain: cluster.local \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.1.template.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.1.template.gen.yaml deleted file mode 100644 index 719f0bba7..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.1.template.gen.yaml +++ /dev/null @@ -1,1200 +0,0 @@ -# defaultTemplates defines the default template to use for pods that do not explicitly specify a template -defaultTemplates: [sidecar] -policy: enabled -alwaysInjectSelector: - [] -neverInjectSelector: - [] -injectedAnnotations: -template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" -templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - dubbo: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.1.values.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.1.values.gen.yaml deleted file mode 100644 index 9a0b8b5de..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.1.values.gen.yaml +++ /dev/null @@ -1,132 +0,0 @@ -{ - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": false, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "proxyTest", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "enabled": false - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } -} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.10.mesh.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.10.mesh.gen.yaml deleted file mode 100644 index 8317bc059..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.10.mesh.gen.yaml +++ /dev/null @@ -1,9 +0,0 @@ -defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 -enablePrometheusMerge: true -rootNamespace: dubbo-system -trustDomain: cluster.local \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.10.template.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.10.template.gen.yaml deleted file mode 100644 index 719f0bba7..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.10.template.gen.yaml +++ /dev/null @@ -1,1200 +0,0 @@ -# defaultTemplates defines the default template to use for pods that do not explicitly specify a template -defaultTemplates: [sidecar] -policy: enabled -alwaysInjectSelector: - [] -neverInjectSelector: - [] -injectedAnnotations: -template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" -templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - dubbo: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.10.values.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.10.values.gen.yaml deleted file mode 100644 index 9b3ef8bb2..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.10.values.gen.yaml +++ /dev/null @@ -1,134 +0,0 @@ -{ - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": false, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "", - "imagePullSecrets": [ - "barSecret" - ], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "enabled": false - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } -} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.12.mesh.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.12.mesh.gen.yaml deleted file mode 100644 index 8317bc059..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.12.mesh.gen.yaml +++ /dev/null @@ -1,9 +0,0 @@ -defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 -enablePrometheusMerge: true -rootNamespace: dubbo-system -trustDomain: cluster.local \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.12.template.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.12.template.gen.yaml deleted file mode 100644 index 719f0bba7..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.12.template.gen.yaml +++ /dev/null @@ -1,1200 +0,0 @@ -# defaultTemplates defines the default template to use for pods that do not explicitly specify a template -defaultTemplates: [sidecar] -policy: enabled -alwaysInjectSelector: - [] -neverInjectSelector: - [] -injectedAnnotations: -template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" -templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - dubbo: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.12.values.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.12.values.gen.yaml deleted file mode 100644 index c7023dbaa..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.12.values.gen.yaml +++ /dev/null @@ -1,136 +0,0 @@ -{ - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": false, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "podDNSSearchNamespaces": [ - "global", - "{{ valueOrDefault .DeploymentMeta.Namespace \"default\" }}.global" - ], - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "enabled": false - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } -} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.13.mesh.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.13.mesh.gen.yaml deleted file mode 100644 index 8317bc059..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.13.mesh.gen.yaml +++ /dev/null @@ -1,9 +0,0 @@ -defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 -enablePrometheusMerge: true -rootNamespace: dubbo-system -trustDomain: cluster.local \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.13.template.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.13.template.gen.yaml deleted file mode 100644 index 719f0bba7..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.13.template.gen.yaml +++ /dev/null @@ -1,1200 +0,0 @@ -# defaultTemplates defines the default template to use for pods that do not explicitly specify a template -defaultTemplates: [sidecar] -policy: enabled -alwaysInjectSelector: - [] -neverInjectSelector: - [] -injectedAnnotations: -template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" -templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - dubbo: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.13.values.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.13.values.gen.yaml deleted file mode 100644 index a230c64a6..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.13.values.gen.yaml +++ /dev/null @@ -1,132 +0,0 @@ -{ - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": false, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": true, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "enabled": false - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } -} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.14.mesh.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.14.mesh.gen.yaml deleted file mode 100644 index 8317bc059..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.14.mesh.gen.yaml +++ /dev/null @@ -1,9 +0,0 @@ -defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 -enablePrometheusMerge: true -rootNamespace: dubbo-system -trustDomain: cluster.local \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.14.template.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.14.template.gen.yaml deleted file mode 100644 index 719f0bba7..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.14.template.gen.yaml +++ /dev/null @@ -1,1200 +0,0 @@ -# defaultTemplates defines the default template to use for pods that do not explicitly specify a template -defaultTemplates: [sidecar] -policy: enabled -alwaysInjectSelector: - [] -neverInjectSelector: - [] -injectedAnnotations: -template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" -templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - dubbo: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.14.values.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.14.values.gen.yaml deleted file mode 100644 index bca4466e8..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.14.values.gen.yaml +++ /dev/null @@ -1,133 +0,0 @@ -{ - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": false, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "chained": false, - "enabled": true - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } -} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.17.mesh.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.17.mesh.gen.yaml deleted file mode 100644 index 8317bc059..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.17.mesh.gen.yaml +++ /dev/null @@ -1,9 +0,0 @@ -defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 -enablePrometheusMerge: true -rootNamespace: dubbo-system -trustDomain: cluster.local \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.17.template.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.17.template.gen.yaml deleted file mode 100644 index 719f0bba7..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.17.template.gen.yaml +++ /dev/null @@ -1,1200 +0,0 @@ -# defaultTemplates defines the default template to use for pods that do not explicitly specify a template -defaultTemplates: [sidecar] -policy: enabled -alwaysInjectSelector: - [] -neverInjectSelector: - [] -injectedAnnotations: -template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" -templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - dubbo: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.17.values.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.17.values.gen.yaml deleted file mode 100644 index c3f57fc5b..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.17.values.gen.yaml +++ /dev/null @@ -1,132 +0,0 @@ -{ - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": false, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": true, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "enabled": false - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } -} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.3.mesh.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.3.mesh.gen.yaml deleted file mode 100644 index 8317bc059..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.3.mesh.gen.yaml +++ /dev/null @@ -1,9 +0,0 @@ -defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 -enablePrometheusMerge: true -rootNamespace: dubbo-system -trustDomain: cluster.local \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.3.template.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.3.template.gen.yaml deleted file mode 100644 index 719f0bba7..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.3.template.gen.yaml +++ /dev/null @@ -1,1200 +0,0 @@ -# defaultTemplates defines the default template to use for pods that do not explicitly specify a template -defaultTemplates: [sidecar] -policy: enabled -alwaysInjectSelector: - [] -neverInjectSelector: - [] -injectedAnnotations: -template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" -templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - dubbo: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.3.values.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.3.values.gen.yaml deleted file mode 100644 index e463e1b91..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.3.values.gen.yaml +++ /dev/null @@ -1,132 +0,0 @@ -{ - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": false, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "Always", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "enabled": false - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } -} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.4.mesh.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.4.mesh.gen.yaml deleted file mode 100644 index 8317bc059..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.4.mesh.gen.yaml +++ /dev/null @@ -1,9 +0,0 @@ -defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 -enablePrometheusMerge: true -rootNamespace: dubbo-system -trustDomain: cluster.local \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.4.template.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.4.template.gen.yaml deleted file mode 100644 index 719f0bba7..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.4.template.gen.yaml +++ /dev/null @@ -1,1200 +0,0 @@ -# defaultTemplates defines the default template to use for pods that do not explicitly specify a template -defaultTemplates: [sidecar] -policy: enabled -alwaysInjectSelector: - [] -neverInjectSelector: - [] -injectedAnnotations: -template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" -templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - dubbo: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/hello.yaml.4.values.gen.yaml b/pkg/kube/inject/testdata/inputs/hello.yaml.4.values.gen.yaml deleted file mode 100644 index 5ddfb85bf..000000000 --- a/pkg/kube/inject/testdata/inputs/hello.yaml.4.values.gen.yaml +++ /dev/null @@ -1,132 +0,0 @@ -{ - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": false, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "Never", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 15020, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "enabled": false - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } -} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/kubevirtInterfaces.yaml.9.mesh.gen.yaml b/pkg/kube/inject/testdata/inputs/kubevirtInterfaces.yaml.9.mesh.gen.yaml deleted file mode 100644 index 8317bc059..000000000 --- a/pkg/kube/inject/testdata/inputs/kubevirtInterfaces.yaml.9.mesh.gen.yaml +++ /dev/null @@ -1,9 +0,0 @@ -defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 -enablePrometheusMerge: true -rootNamespace: dubbo-system -trustDomain: cluster.local \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/kubevirtInterfaces.yaml.9.template.gen.yaml b/pkg/kube/inject/testdata/inputs/kubevirtInterfaces.yaml.9.template.gen.yaml deleted file mode 100644 index 719f0bba7..000000000 --- a/pkg/kube/inject/testdata/inputs/kubevirtInterfaces.yaml.9.template.gen.yaml +++ /dev/null @@ -1,1200 +0,0 @@ -# defaultTemplates defines the default template to use for pods that do not explicitly specify a template -defaultTemplates: [sidecar] -policy: enabled -alwaysInjectSelector: - [] -neverInjectSelector: - [] -injectedAnnotations: -template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" -templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - dubbo: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/kubevirtInterfaces.yaml.9.values.gen.yaml b/pkg/kube/inject/testdata/inputs/kubevirtInterfaces.yaml.9.values.gen.yaml deleted file mode 100644 index cf6dbddea..000000000 --- a/pkg/kube/inject/testdata/inputs/kubevirtInterfaces.yaml.9.values.gen.yaml +++ /dev/null @@ -1,132 +0,0 @@ -{ - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": false, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 300, - "readinessInitialDelaySeconds": 100, - "readinessPeriodSeconds": 200, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 123, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "enabled": false - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } -} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/status_params.yaml.8.mesh.gen.yaml b/pkg/kube/inject/testdata/inputs/status_params.yaml.8.mesh.gen.yaml deleted file mode 100644 index 8317bc059..000000000 --- a/pkg/kube/inject/testdata/inputs/status_params.yaml.8.mesh.gen.yaml +++ /dev/null @@ -1,9 +0,0 @@ -defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 -enablePrometheusMerge: true -rootNamespace: dubbo-system -trustDomain: cluster.local \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/status_params.yaml.8.template.gen.yaml b/pkg/kube/inject/testdata/inputs/status_params.yaml.8.template.gen.yaml deleted file mode 100644 index 719f0bba7..000000000 --- a/pkg/kube/inject/testdata/inputs/status_params.yaml.8.template.gen.yaml +++ /dev/null @@ -1,1200 +0,0 @@ -# defaultTemplates defines the default template to use for pods that do not explicitly specify a template -defaultTemplates: [sidecar] -policy: enabled -alwaysInjectSelector: - [] -neverInjectSelector: - [] -injectedAnnotations: -template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" -templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - dubbo: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/status_params.yaml.8.values.gen.yaml b/pkg/kube/inject/testdata/inputs/status_params.yaml.8.values.gen.yaml deleted file mode 100644 index cf6dbddea..000000000 --- a/pkg/kube/inject/testdata/inputs/status_params.yaml.8.values.gen.yaml +++ /dev/null @@ -1,132 +0,0 @@ -{ - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": false, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "", - "excludeInboundPorts": "", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "dubbo-agent", - "includeIPRanges": "*", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 300, - "readinessInitialDelaySeconds": 100, - "readinessPeriodSeconds": 200, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 123, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "enabled": false - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } -} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/traffic-params.yaml.7.mesh.gen.yaml b/pkg/kube/inject/testdata/inputs/traffic-params.yaml.7.mesh.gen.yaml deleted file mode 100644 index 8317bc059..000000000 --- a/pkg/kube/inject/testdata/inputs/traffic-params.yaml.7.mesh.gen.yaml +++ /dev/null @@ -1,9 +0,0 @@ -defaultConfig: - discoveryAddress: istiod.dubbo-system.svc:15012 - proxyMetadata: {} - tracing: - zipkin: - address: zipkin.dubbo-system:9411 -enablePrometheusMerge: true -rootNamespace: dubbo-system -trustDomain: cluster.local \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/traffic-params.yaml.7.template.gen.yaml b/pkg/kube/inject/testdata/inputs/traffic-params.yaml.7.template.gen.yaml deleted file mode 100644 index 719f0bba7..000000000 --- a/pkg/kube/inject/testdata/inputs/traffic-params.yaml.7.template.gen.yaml +++ /dev/null @@ -1,1200 +0,0 @@ -# defaultTemplates defines the default template to use for pods that do not explicitly specify a template -defaultTemplates: [sidecar] -policy: enabled -alwaysInjectSelector: - [] -neverInjectSelector: - [] -injectedAnnotations: -template: "{{ Template_Version_And_Istio_Version_Mismatched_Check_Installation }}" -templates: - sidecar: | - {{- define "resources" }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - {{- end }} - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - security.istio.io/tlsMode: {{ index .ObjectMeta.Labels `security.istio.io/tlsMode` | default "istio" | quote }} - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if ge (len $containers) 1 }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - {{- end }} - {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }} - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{- end }} - {{- end }} - {{- if .Values.istio_cni.enabled }} - {{- if not .Values.istio_cni.chained }} - k8s.v1.cni.cncf.io/networks: '{{ appendMultusNetwork (index .ObjectMeta.Annotations `k8s.v1.cni.cncf.io/networks`) `istio-cni` }}', - {{- end }} - sidecar.istio.io/interceptionMode: "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}", - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}traffic.sidecar.istio.io/includeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}traffic.sidecar.istio.io/excludeOutboundIPRanges: "{{.}}",{{ end }} - {{ with annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}traffic.sidecar.istio.io/includeInboundPorts: "{{.}}",{{ end }} - traffic.sidecar.istio.io/excludeInboundPorts: "{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}", - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") }} - traffic.sidecar.istio.io/includeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}", - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne .Values.global.proxy.excludeOutboundPorts "") }} - traffic.sidecar.istio.io/excludeOutboundPorts: "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}", - {{- end }} - {{ with index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}traffic.sidecar.istio.io/kubevirtInterfaces: "{{.}}",{{ end }} - {{- end }} - } - spec: - {{- $holdProxy := or .ProxyConfig.HoldApplicationUntilProxyStarts.GetValue .Values.global.proxy.holdApplicationUntilProxyStarts }} - initContainers: - {{ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `NONE` }} - {{ if .Values.istio_cni.enabled -}} - - name: istio-validation - {{ else -}} - - name: istio-init - {{ end -}} - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - istio-iptables - - "-p" - - {{ .MeshConfig.ProxyListenPort | default "15001" | quote }} - - "-z" - - "15006" - - "-u" - - "1337" - - "-m" - - "{{ annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode }}" - - "-i" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` .Values.global.proxy.includeIPRanges }}" - - "-x" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundIPRanges` .Values.global.proxy.excludeIPRanges }}" - - "-b" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeInboundPorts` .Values.global.proxy.includeInboundPorts }}" - - "-d" - {{- if excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }} - - "15090,15021,{{ excludeInboundPort (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) (annotation .ObjectMeta `traffic.sidecar.istio.io/excludeInboundPorts` .Values.global.proxy.excludeInboundPorts) }}" - {{- else }} - - "15090,15021" - {{- end }} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/includeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.includeOutboundPorts "") "") -}} - - "-q" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundPorts` .Values.global.proxy.includeOutboundPorts }}" - {{ end -}} - {{ if or (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/excludeOutboundPorts`) (ne (valueOrDefault .Values.global.proxy.excludeOutboundPorts "") "") -}} - - "-o" - - "{{ annotation .ObjectMeta `traffic.sidecar.istio.io/excludeOutboundPorts` .Values.global.proxy.excludeOutboundPorts }}" - {{ end -}} - {{ if (isset .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces`) -}} - - "-k" - - "{{ index .ObjectMeta.Annotations `traffic.sidecar.istio.io/kubevirtInterfaces` }}" - {{ end -}} - {{ if .Values.istio_cni.enabled -}} - - "--run-validation" - - "--skip-rule-apply" - {{ end -}} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{- if .ProxyConfig.ProxyMetadata }} - env: - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - privileged: {{ .Values.global.proxy.privileged }} - capabilities: - {{- if not .Values.istio_cni.enabled }} - add: - - NET_ADMIN - - NET_RAW - {{- end }} - drop: - - ALL - {{- if not .Values.istio_cni.enabled }} - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - readOnlyRootFilesystem: true - runAsGroup: 1337 - runAsUser: 1337 - runAsNonRoot: true - {{- end }} - restartPolicy: Always - {{ end -}} - {{- if eq (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - - name: enable-core-dump - args: - - -c - - sysctl -w kernel.core_pattern=/var/lib/istio/data/core.proxy && ulimit -c unlimited - command: - - /bin/sh - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy_init.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - resources: - {{ template "resources" . }} - securityContext: - allowPrivilegeEscalation: true - capabilities: - add: - - SYS_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: false - runAsGroup: 0 - runAsNonRoot: false - runAsUser: 0 - {{ end }} - containers: - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if gt .EstimatedConcurrency 0 }} - - --concurrency - - "{{ .EstimatedConcurrency }}" - {{- end -}} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- else if $holdProxy }} - lifecycle: - postStart: - exec: - command: - - pilot-agent - - wait - {{- end }} - env: - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: ISTIO_BOOTSTRAP_OVERRIDE - value: "/etc/istio/custom-bootstrap/custom_bootstrap.json" - {{- end }} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- if and (eq .Values.global.proxy.tracer "datadog") (isset .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - {{- range $key, $value := fromJSON (index .ObjectMeta.Annotations `apm.datadoghq.com/env`) }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - securityContext: - {{- if eq (index .ProxyConfig.ProxyMetadata "IPTABLES_TRACE_LOGGING") "true" }} - allowPrivilegeEscalation: true - capabilities: - add: - - NET_ADMIN - drop: - - ALL - privileged: true - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - runAsNonRoot: false - runAsUser: 0 - {{- else }} - allowPrivilegeEscalation: {{ .Values.global.proxy.privileged }} - capabilities: - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - add: - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY` -}} - - NET_ADMIN - {{- end }} - {{ if eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true` -}} - - NET_BIND_SERVICE - {{- end }} - {{- end }} - drop: - - ALL - privileged: {{ .Values.global.proxy.privileged }} - readOnlyRootFilesystem: {{ ne (annotation .ObjectMeta `sidecar.istio.io/enableCoreDump` .Values.global.proxy.enableCoreDump) "true" }} - runAsGroup: 1337 - fsGroup: 1337 - {{ if or (eq (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) `TPROXY`) (eq (annotation .ObjectMeta `sidecar.istio.io/capNetBindService` .Values.global.proxy.capNetBindService) `true`) -}} - runAsNonRoot: false - runAsUser: 0 - {{- else -}} - runAsNonRoot: true - runAsUser: 1337 - {{- end }} - {{- end }} - resources: - {{ template "resources" . }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - mountPath: /etc/istio/custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - mountPath: {{ directory .ProxyConfig.GetTracing.GetTlsSettings.GetCaCertificates }} - name: lightstep-certs - readOnly: true - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else }} - - emptyDir: - name: workload-certs - {{- end }} - {{- if (isset .ObjectMeta.Annotations `sidecar.istio.io/bootstrapOverride`) }} - - name: custom-bootstrap-volume - configMap: - name: {{ annotation .ObjectMeta `sidecar.istio.io/bootstrapOverride` "" }} - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- if and (eq .Values.global.proxy.tracer "lightstep") .ProxyConfig.GetTracing.GetTlsSettings }} - - name: lightstep-certs - secret: - optional: true - secretName: lightstep.cacert - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - gateway: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - istio.io/rev: {{ .Revision | default "default" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - } - spec: - containers: - - name: istio-proxy - {{- if contains "/" .Values.global.proxy.image }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - ports: - - containerPort: 15090 - protocol: TCP - name: http-envoy-prom - args: - - proxy - - router - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }} - - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - {{- if .Values.global.proxy.lifecycle }} - lifecycle: - {{ toYaml .Values.global.proxy.lifecycle | indent 6 }} - {{- end }} - env: - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - readinessProbe: - httpGet: - path: /healthz/ready - port: 15021 - initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - mountPath: /var/run/secrets/workload-spiffe-credentials - readOnly: true - {{- else }} - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # SDS channel between istioagent and Envoy - - mountPath: /etc/istio/proxy - name: istio-envoy - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - mountPath: /etc/certs/ - name: istio-certs - readOnly: true - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - volumes: - - emptyDir: {} - name: workload-socket - {{- if eq .Values.global.caName "GkeWorkloadCertificate" }} - - name: gke-workload-certificate - csi: - driver: workloadcertificates.security.cloud.google.com - {{- else}} - - emptyDir: {} - name: workload-certs - {{- end }} - # SDS channel between istioagent and Envoy - - emptyDir: - medium: Memory - name: istio-envoy - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if .Values.global.mountMtlsCerts }} - # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications. - - name: istio-certs - secret: - optional: true - {{ if eq .Spec.ServiceAccountName "" }} - secretName: istio.default - {{ else -}} - secretName: {{ printf "istio.%s" .Spec.ServiceAccountName }} - {{ end -}} - {{- end }} - {{- if .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.global.imagePullSecrets }} - - name: {{ . }} - {{- end }} - {{- end }} - {{- if eq (env "ENABLE_LEGACY_FSGROUP_INJECTION" "true") "true" }} - securityContext: - fsGroup: 1337 - {{- end }} - grpc-simple: | - metadata: - sidecar.istio.io/rewriteAppHTTPProbers: "false" - spec: - initContainers: - - name: grpc-bootstrap-init - image: busybox:1.28 - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ISTIO_NAMESPACE - value: | - {{ .Values.global.istioNamespace }} - command: - - sh - - "-c" - - |- - NODE_ID="sidecar~${INSTANCE_IP}~${POD_NAME}.${POD_NAMESPACE}~cluster.local" - SERVER_URI="dns:///istiod.${ISTIO_NAMESPACE}.svc:15010" - echo ' - { - "xds_servers": [ - { - "server_uri": "'${SERVER_URI}'", - "channel_creds": [{"type": "insecure"}], - "server_features" : ["xds_v3"] - } - ], - "node": { - "id": "'${NODE_ID}'", - "metadata": { - "GENERATOR": "grpc" - } - } - }' > /var/lib/grpc/data/bootstrap.json - containers: - {{- range $index, $container := .Spec.Containers }} - - name: {{ $container.Name }} - env: - - name: GRPC_XDS_BOOTSTRAP - value: /var/lib/grpc/data/bootstrap.json - - name: GRPC_GO_LOG_VERBOSITY_LEVEL - value: "99" - - name: GRPC_GO_LOG_SEVERITY_LEVEL - value: info - volumeMounts: - - mountPath: /var/lib/grpc/data/ - name: grpc-io-proxyless-bootstrap - {{- end }} - volumes: - - name: grpc-io-proxyless-bootstrap - emptyDir: {} - grpc-agent: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" - value: "true" - - name: "GRPC_XDS_BOOTSTRAP" - value: "/etc/istio/proxy/grpc-bootstrap.json" - volumeMounts: - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- end }} - {{- end }} - - name: istio-proxy - {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }} - image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}" - {{- else }} - image: "{{ .ProxyImage }}" - {{- end }} - args: - - proxy - - sidecar - - --domain - - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }} - - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }} - {{- if .Values.global.sts.servicePort }} - - --stsPort={{ .Values.global.sts.servicePort }} - {{- end }} - {{- if .Values.global.logAsJson }} - - --log_as_json - {{- end }} - env: - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ .Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ .Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if .Values.global.caAddress }} - value: {{ .Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON .ProxyConfig }} - - name: ISTIO_META_POD_PORTS - value: |- - [ - {{- $first := true }} - {{- range $index1, $c := .Spec.Containers }} - {{- range $index2, $p := $c.Ports }} - {{- if (structToJSON $p) }} - {{if not $first}},{{end}}{{ structToJSON $p }} - {{- $first = false }} - {{- end }} - {{- end}} - {{- end}} - ] - - name: ISTIO_META_APP_CONTAINERS - value: "{{ $containers | join "," }}" - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index .ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) .ProxyConfig.InterceptionMode.String }}" - {{- if .Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ .Values.global.network }}" - {{- end }} - {{- if .DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ .DeploymentMeta.Name }}" - {{ end }} - {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }} - {{- end}} - {{- if .Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ .Values.global.meshID }}" - {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := .ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}} - {{ if ne (annotation .ObjectMeta `status.sidecar.istio.io/port` .Values.global.proxy.statusPort) `0` }} - readinessProbe: - httpGet: - path: /healthz/ready - port: {{ .Values.global.proxy.statusPort }} - initialDelaySeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/initialDelaySeconds` .Values.global.proxy.readinessInitialDelaySeconds }} - periodSeconds: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/periodSeconds` .Values.global.proxy.readinessPeriodSeconds }} - timeoutSeconds: 3 - failureThreshold: {{ annotation .ObjectMeta `readiness.status.sidecar.istio.io/failureThreshold` .Values.global.proxy.readinessFailureThreshold }} - {{ end -}} - resources: - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) }} - requests: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPU` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemory` }}" - {{ end }} - {{- end }} - {{- if or (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) }} - limits: - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit`) -}} - cpu: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyCPULimit` }}" - {{ end }} - {{ if (isset .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit`) -}} - memory: "{{ index .ObjectMeta.Annotations `sidecar.istio.io/proxyMemoryLimit` }}" - {{ end }} - {{- end }} - {{- else }} - {{- if .Values.global.proxy.resources }} - {{ toYaml .Values.global.proxy.resources | indent 6 }} - {{- end }} - {{- end }} - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq .Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ .Values.global.sds.token.aud }} - {{- end }} - {{- if eq .Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset .ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index .ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - dubbo: | - {{- $containers := list }} - {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}} - metadata: - labels: - service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name | quote }} - service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest" | quote }} - annotations: { - {{- if eq (len $containers) 1 }} - kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}", - kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}", - {{ end }} - sidecar.istio.io/rewriteAppHTTPProbers: "false", - } - spec: - containers: - {{- range $index, $container := .Spec.Containers }} - {{ if not (eq $container.Name "istio-proxy") }} - - name: {{ $container.Name }} - env: - - name: DUBBO_XDS_ENABLE - value: "true" - - name: ISTIO_META_GENERATOR - value: grpc - - name: OUTPUT_CERTS - value: /var/lib/istio/data - {{- if eq (env "PILOT_ENABLE_INBOUND_PASSTHROUGH" "true") "false" }} - - name: REWRITE_PROBE_LEGACY_LOCALHOST_DESTINATION - value: "true" - {{- end }} - - name: JWT_POLICY - value: {{ $.Values.global.jwtPolicy }} - - name: PILOT_CERT_PROVIDER - value: {{ $.Values.global.pilotCertProvider }} - - name: CA_ADDR - {{- if $.Values.global.caAddress }} - value: {{ $.Values.global.caAddress }} - {{- else }} - value: istiod{{- if not (eq $.Values.revision "") }}-{{ $.Values.revision }}{{- end }}.{{ $.Values.global.istioNamespace }}.svc:15012 - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: SERVICE_ACCOUNT - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: PROXY_CONFIG - value: | - {{ protoToJSON $.ProxyConfig }} - - name: ISTIO_META_CLUSTER_ID - value: "{{ valueOrDefault $.Values.global.multiCluster.clusterName `Kubernetes` }}" - - name: ISTIO_META_INTERCEPTION_MODE - value: "{{ or (index $.ObjectMeta.Annotations `sidecar.istio.io/interceptionMode`) $.ProxyConfig.InterceptionMode.String }}" - {{- if $.Values.global.network }} - - name: ISTIO_META_NETWORK - value: "{{ $.Values.global.network }}" - {{- end }} - {{- if $.DeploymentMeta.Name }} - - name: ISTIO_META_WORKLOAD_NAME - value: "{{ $.DeploymentMeta.Name }}" - {{ end }} - {{- if and $.TypeMeta.APIVersion $.DeploymentMeta.Name }} - - name: ISTIO_META_OWNER - value: kubernetes://apis/{{ $.TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault $.DeploymentMeta.Namespace `default` }}/{{ toLower $.TypeMeta.Kind}}s/{{ $.DeploymentMeta.Name }} - {{- end}} - {{- if $.Values.global.meshID }} - - name: ISTIO_META_MESH_ID - value: "{{ $.Values.global.meshID }}" - {{- else if (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: ISTIO_META_MESH_ID - value: "{{ (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }}" - {{- end }} - {{- with (valueOrDefault $.MeshConfig.TrustDomain $.Values.global.trustDomain) }} - - name: TRUST_DOMAIN - value: "{{ . }}" - {{- end }} - {{- range $key, $value := $.ProxyConfig.ProxyMetadata }} - - name: {{ $key }} - value: "{{ $value }}" - {{- end }} - # grpc uses xds:/// to resolve – no need to resolve VIP - - name: ISTIO_META_DNS_CAPTURE - value: "false" - - name: DISABLE_ENVOY - value: "true" - volumeMounts: - - name: workload-socket - mountPath: /var/run/secrets/workload-spiffe-uds - - name: workload-certs - mountPath: /var/run/secrets/workload-spiffe-credentials - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - mountPath: /var/run/secrets/istio - name: istiod-ca-cert - {{- end }} - - mountPath: /var/lib/istio/data - name: istio-data - # UDS channel between istioagent and gRPC client for XDS/SDS - - mountPath: /etc/istio/proxy - name: istio-xds - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - mountPath: /var/run/secrets/tokens - name: istio-token - {{- end }} - - name: istio-podinfo - mountPath: /etc/istio/pod - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount` }} - {{ range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolumeMount`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 6 }} - {{ end }} - {{- end }} - volumes: - - emptyDir: {} - name: workload-socket - - emptyDir: {} - name: workload-certs - # UDS channel between istioagent and gRPC client for XDS/SDS - - emptyDir: - medium: Memory - name: istio-xds - - name: istio-data - emptyDir: {} - - name: istio-podinfo - downwardAPI: - items: - - path: "labels" - fieldRef: - fieldPath: metadata.labels - - path: "annotations" - fieldRef: - fieldPath: metadata.annotations - {{- if eq $.Values.global.jwtPolicy "third-party-jwt" }} - - name: istio-token - projected: - sources: - - serviceAccountToken: - path: istio-token - expirationSeconds: 43200 - audience: {{ $.Values.global.sds.token.aud }} - {{- end }} - {{- if eq $.Values.global.pilotCertProvider "istiod" }} - - name: istiod-ca-cert - configMap: - name: istio-ca-root-cert - {{- end }} - {{- if isset $.ObjectMeta.Annotations `sidecar.istio.io/userVolume` }} - {{range $index, $value := fromJSON (index $.ObjectMeta.Annotations `sidecar.istio.io/userVolume`) }} - - name: "{{ $index }}" - {{ toYaml $value | indent 4 }} - {{ end }} - {{ end }} - {{- end }} - {{- end }} \ No newline at end of file diff --git a/pkg/kube/inject/testdata/inputs/traffic-params.yaml.7.values.gen.yaml b/pkg/kube/inject/testdata/inputs/traffic-params.yaml.7.values.gen.yaml deleted file mode 100644 index cc7a2bb94..000000000 --- a/pkg/kube/inject/testdata/inputs/traffic-params.yaml.7.values.gen.yaml +++ /dev/null @@ -1,132 +0,0 @@ -{ - "global": { - "autoscalingv2API": true, - "caAddress": "", - "caName": "", - "configCluster": false, - "configValidation": true, - "defaultNodeSelector": {}, - "defaultPodDisruptionBudget": { - "enabled": true - }, - "defaultResources": { - "requests": { - "cpu": "10m" - } - }, - "enabled": false, - "externalIstiod": false, - "hub": "apache", - "imagePullPolicy": "", - "imagePullSecrets": [], - "istioNamespace": "dubbo-system", - "istiod": { - "enableAnalysis": false - }, - "jwtPolicy": "third-party-jwt", - "logAsJson": false, - "logging": { - "level": "default:info" - }, - "meshID": "", - "meshNetworks": {}, - "mountMtlsCerts": false, - "multiCluster": { - "clusterName": "", - "enabled": false - }, - "namespace": "dubbo-system", - "network": "", - "omitSidecarInjectorConfigMap": false, - "oneNamespace": false, - "operatorManageWebhooks": false, - "pilotCertProvider": "istiod", - "priorityClassName": "", - "proxy": { - "autoInject": "enabled", - "clusterDomain": "cluster.local", - "componentLogLevel": "misc:error", - "enableCoreDump": false, - "excludeIPRanges": "10.96.0.2/24,10.96.0.3/24", - "excludeInboundPorts": "4,5,6", - "excludeOutboundPorts": "", - "holdApplicationUntilProxyStarts": false, - "image": "dubbo-agent", - "includeIPRanges": "127.0.0.1/24,10.96.0.1/24", - "includeInboundPorts": "*", - "includeOutboundPorts": "", - "logLevel": "warning", - "privileged": false, - "readinessFailureThreshold": 30, - "readinessInitialDelaySeconds": 1, - "readinessPeriodSeconds": 2, - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - }, - "statusPort": 0, - "tracer": "zipkin" - }, - "proxy_init": { - "image": "dubbo-agent", - "resources": { - "limits": { - "cpu": "2000m", - "memory": "1024Mi" - }, - "requests": { - "cpu": "10m", - "memory": "10Mi" - } - } - }, - "remotePilotAddress": "", - "sds": { - "token": { - "aud": "istio-ca" - } - }, - "sts": { - "servicePort": 0 - }, - "tag": "latest", - "tracer": { - "datadog": { - "address": "$(HOST_IP):8126" - }, - "lightstep": { - "accessToken": "", - "address": "" - }, - "stackdriver": { - "debug": false, - "maxNumberOfAnnotations": 200, - "maxNumberOfAttributes": 200, - "maxNumberOfMessageEvents": 200 - }, - "zipkin": { - "address": "" - } - }, - "useMCP": false - }, - "istio_cni": { - "enabled": false - }, - "revision": "", - "sidecarInjectorWebhook": { - "alwaysInjectSelector": [], - "defaultTemplates": [], - "enableNamespacesByDefault": false, - "injectedAnnotations": {}, - "neverInjectSelector": [], - "rewriteAppHTTPProbe": true, - "templates": {} - } -} \ No newline at end of file diff --git a/pkg/kube/inject/validate.go b/pkg/kube/inject/validate.go deleted file mode 100644 index 90b8c8aac..000000000 --- a/pkg/kube/inject/validate.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package inject - -import ( - "fmt" - "net" - "strconv" - "strings" -) - -import ( - "github.com/hashicorp/go-multierror" - "istio.io/api/annotation" - meshconfig "istio.io/api/mesh/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/validation" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -type annotationValidationFunc func(value string) error - -// per-sidecar policy and status -var ( - AnnotationValidation = map[string]annotationValidationFunc{ - annotation.SidecarInterceptionMode.Name: validateInterceptionMode, - annotation.SidecarEnableCoreDump.Name: validateBool, - annotation.SidecarStatusPort.Name: validateStatusPort, - annotation.SidecarStatusReadinessInitialDelaySeconds.Name: validateUInt32, - annotation.SidecarStatusReadinessPeriodSeconds.Name: validateUInt32, - annotation.SidecarStatusReadinessFailureThreshold.Name: validateUInt32, - annotation.SidecarTrafficIncludeOutboundIPRanges.Name: ValidateIncludeIPRanges, - annotation.SidecarTrafficExcludeOutboundIPRanges.Name: ValidateExcludeIPRanges, - annotation.SidecarTrafficIncludeInboundPorts.Name: ValidateIncludeInboundPorts, - annotation.SidecarTrafficExcludeInboundPorts.Name: ValidateExcludeInboundPorts, - annotation.SidecarTrafficExcludeOutboundPorts.Name: ValidateExcludeOutboundPorts, - annotation.PrometheusMergeMetrics.Name: validateBool, - annotation.ProxyConfig.Name: validateProxyConfig, - } -) - -func validateProxyConfig(value string) error { - config := mesh.DefaultProxyConfig() - if err := protomarshal.ApplyYAML(value, config); err != nil { - return fmt.Errorf("failed to convert to apply proxy config: %v", err) - } - return validation.ValidateMeshConfigProxyConfig(config) -} - -func validateAnnotations(annotations map[string]string) (err error) { - for name, value := range annotations { - if v, ok := AnnotationValidation[name]; ok { - if e := v(value); e != nil { - err = multierror.Append(err, fmt.Errorf("invalid value '%s' for annotation '%s': %v", value, name, e)) - } - } - } - return -} - -func validatePortList(parameterName, ports string) error { - if _, err := parsePorts(ports); err != nil { - return fmt.Errorf("%s invalid: %v", parameterName, err) - } - return nil -} - -// validateInterceptionMode validates the interceptionMode annotation -func validateInterceptionMode(mode string) error { - switch mode { - case meshconfig.ProxyConfig_REDIRECT.String(): - case meshconfig.ProxyConfig_TPROXY.String(): - case string(model.InterceptionNone): // not a global mesh config - must be enabled for each sidecar - default: - return fmt.Errorf("interceptionMode invalid, use REDIRECT,TPROXY,NONE: %v", mode) - } - return nil -} - -// ValidateIncludeIPRanges validates the includeIPRanges parameter -func ValidateIncludeIPRanges(ipRanges string) error { - if ipRanges != "*" { - if e := validateCIDRList(ipRanges); e != nil { - return fmt.Errorf("includeIPRanges invalid: %v", e) - } - } - return nil -} - -// ValidateExcludeIPRanges validates the excludeIPRanges parameter -func ValidateExcludeIPRanges(ipRanges string) error { - if e := validateCIDRList(ipRanges); e != nil { - return fmt.Errorf("excludeIPRanges invalid: %v", e) - } - return nil -} - -// ValidateIncludeInboundPorts validates the includeInboundPorts parameter -func ValidateIncludeInboundPorts(ports string) error { - if ports != "*" { - return validatePortList("includeInboundPorts", ports) - } - return nil -} - -// ValidateExcludeInboundPorts validates the excludeInboundPorts parameter -func ValidateExcludeInboundPorts(ports string) error { - return validatePortList("excludeInboundPorts", ports) -} - -// ValidateExcludeOutboundPorts validates the excludeOutboundPorts parameter -func ValidateExcludeOutboundPorts(ports string) error { - return validatePortList("excludeOutboundPorts", ports) -} - -// validateStatusPort validates the statusPort parameter -func validateStatusPort(port string) error { - if _, e := parsePort(port); e != nil { - return fmt.Errorf("excludeInboundPorts invalid: %v", e) - } - return nil -} - -// validateUInt32 validates that the given annotation value is a positive integer. -func validateUInt32(value string) error { - _, err := strconv.ParseUint(value, 10, 32) - return err -} - -// validateBool validates that the given annotation value is a boolean. -func validateBool(value string) error { - _, err := strconv.ParseBool(value) - return err -} - -func validateCIDRList(cidrs string) error { - if len(cidrs) > 0 { - for _, cidr := range strings.Split(cidrs, ",") { - if _, _, err := net.ParseCIDR(cidr); err != nil { - return fmt.Errorf("failed parsing cidr '%s': %v", cidr, err) - } - } - } - return nil -} - -func splitPorts(portsString string) []string { - return strings.Split(portsString, ",") -} - -func parsePort(portStr string) (int, error) { - port, err := strconv.ParseUint(strings.TrimSpace(portStr), 10, 16) - if err != nil { - return 0, fmt.Errorf("failed parsing port '%s': %v", portStr, err) - } - return int(port), nil -} - -func parsePorts(portsString string) ([]int, error) { - portsString = strings.TrimSpace(portsString) - ports := make([]int, 0) - if len(portsString) > 0 { - for _, portStr := range splitPorts(portsString) { - port, err := parsePort(portStr) - if err != nil { - return nil, fmt.Errorf("failed parsing port '%s': %v", portStr, err) - } - ports = append(ports, port) - } - } - return ports, nil -} diff --git a/pkg/kube/inject/watcher.go b/pkg/kube/inject/watcher.go deleted file mode 100644 index 2b0f24e45..000000000 --- a/pkg/kube/inject/watcher.go +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package inject - -import ( - "context" - "fmt" - "path/filepath" - "time" -) - -import ( - "github.com/fsnotify/fsnotify" - "istio.io/pkg/log" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/configmapwatcher" -) - -// Watcher watches for and reacts to injection config updates. -type Watcher interface { - // SetHandler sets the handler that is run when the config changes. - // Must call this before Run. - SetHandler(func(*Config, string) error) - - // Run starts the Watcher. Must call this after SetHandler. - Run(<-chan struct{}) - - // Get returns the sidecar and values configuration. - Get() (*Config, string, error) -} - -var _ Watcher = &fileWatcher{} - -var _ Watcher = &configMapWatcher{} - -type fileWatcher struct { - watcher *fsnotify.Watcher - configFile string - valuesFile string - handler func(*Config, string) error -} - -type configMapWatcher struct { - c *configmapwatcher.Controller - client kube.Client - namespace string - name string - configKey string - valuesKey string - handler func(*Config, string) error -} - -// NewFileWatcher creates a Watcher for local config and values files. -func NewFileWatcher(configFile, valuesFile string) (Watcher, error) { - watcher, err := fsnotify.NewWatcher() - if err != nil { - return nil, err - } - // watch the parent directory of the target files so we can catch - // symlink updates of k8s ConfigMaps volumes. - watchDir, _ := filepath.Split(configFile) - if err := watcher.Add(watchDir); err != nil { - return nil, fmt.Errorf("could not watch %v: %v", watchDir, err) - } - return &fileWatcher{ - watcher: watcher, - configFile: configFile, - valuesFile: valuesFile, - }, nil -} - -func (w *fileWatcher) Run(stop <-chan struct{}) { - defer w.watcher.Close() - var timerC <-chan time.Time - for { - select { - case <-timerC: - timerC = nil - sidecarConfig, valuesConfig, err := w.Get() - if err != nil { - log.Errorf("update error: %v", err) - break - } - if w.handler != nil { - if err := w.handler(sidecarConfig, valuesConfig); err != nil { - log.Errorf("update error: %v", err) - } - } - case event, ok := <-w.watcher.Events: - if !ok { - return - } - log.Debugf("Injector watch update: %+v", event) - // use a timer to debounce configuration updates - if ((event.Op&fsnotify.Write == fsnotify.Write) || (event.Op&fsnotify.Create == fsnotify.Create)) && timerC == nil { - timerC = time.After(watchDebounceDelay) - } - case err, ok := <-w.watcher.Errors: - if !ok { - return - } - log.Errorf("Watcher error: %v", err) - case <-stop: - return - } - } -} - -func (w *fileWatcher) Get() (*Config, string, error) { - return loadConfig(w.configFile, w.valuesFile) -} - -func (w *fileWatcher) SetHandler(handler func(*Config, string) error) { - w.handler = handler -} - -// NewConfigMapWatcher creates a new Watcher for changes to the given ConfigMap. -func NewConfigMapWatcher(client kube.Client, namespace, name, configKey, valuesKey string) Watcher { - w := &configMapWatcher{ - client: client, - namespace: namespace, - name: name, - configKey: configKey, - valuesKey: valuesKey, - } - w.c = configmapwatcher.NewController(client, namespace, name, func(cm *v1.ConfigMap) { - sidecarConfig, valuesConfig, err := readConfigMap(cm, configKey, valuesKey) - if err != nil { - log.Warnf("failed to read injection config from ConfigMap: %v", err) - return - } - if w.handler != nil { - if err := w.handler(sidecarConfig, valuesConfig); err != nil { - log.Errorf("update error: %v", err) - } - } - }) - return w -} - -func (w *configMapWatcher) Run(stop <-chan struct{}) { - w.c.Run(stop) -} - -func (w *configMapWatcher) Get() (*Config, string, error) { - cms := w.client.CoreV1().ConfigMaps(w.namespace) - cm, err := cms.Get(context.TODO(), w.name, metav1.GetOptions{}) - if err != nil { - return nil, "", err - } - return readConfigMap(cm, w.configKey, w.valuesKey) -} - -func (w *configMapWatcher) SetHandler(handler func(*Config, string) error) { - w.handler = handler -} - -func readConfigMap(cm *v1.ConfigMap, configKey, valuesKey string) (*Config, string, error) { - if cm == nil { - return nil, "", fmt.Errorf("no ConfigMap found") - } - - configYaml, exists := cm.Data[configKey] - if !exists { - return nil, "", fmt.Errorf("missing ConfigMap config key %q", configKey) - } - c, err := unmarshalConfig([]byte(configYaml)) - if err != nil { - return nil, "", fmt.Errorf("failed reading config: %v. YAML:\n%s", err, configYaml) - } - - valuesConfig, exists := cm.Data[valuesKey] - if !exists { - return nil, "", fmt.Errorf("missing ConfigMap values key %q", valuesKey) - } - return c, valuesConfig, nil -} diff --git a/pkg/kube/inject/watcher_test.go b/pkg/kube/inject/watcher_test.go deleted file mode 100644 index 67b99b6d7..000000000 --- a/pkg/kube/inject/watcher_test.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package inject - -import ( - "context" - "fmt" - "sync" - "testing" - "time" -) - -import ( - . "github.com/onsi/gomega" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -const ( - namespace string = "dubbo-system" - cmName string = "istio-sidecar-injector" - configKey string = "config" - valuesKey string = "values" -) - -func makeConfigMap(resourceVersion string, data map[string]string) *v1.ConfigMap { - return &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: cmName, - ResourceVersion: resourceVersion, - }, - Data: data, - } -} - -func TestNewConfigMapWatcher(t *testing.T) { - configYaml := "policy: enabled" - c, err := UnmarshalConfig([]byte(configYaml)) - if err != nil { - t.Fatal(err) - } - valuesConfig := "sidecarInjectorWebhook: {}" - - cm := makeConfigMap("1", map[string]string{ - configKey: configYaml, - valuesKey: valuesConfig, - }) - badCM := makeConfigMap("2", map[string]string{ - configKey: configYaml, - }) - badCM2 := makeConfigMap("3", map[string]string{ - valuesKey: valuesConfig, - }) - badCM3 := makeConfigMap("4", map[string]string{ - configKey: "bad yaml", - valuesKey: valuesConfig, - }) - - var mu sync.Mutex - var newConfig *Config - var newValues string - - client := kube.NewFakeClient() - w := NewConfigMapWatcher(client, namespace, cmName, configKey, valuesKey) - w.SetHandler(func(config *Config, values string) error { - mu.Lock() - defer mu.Unlock() - newConfig = config - newValues = values - return nil - }) - stop := make(chan struct{}) - go w.Run(stop) - controller := w.(*configMapWatcher).c - cache.WaitForCacheSync(stop, controller.HasSynced) - client.RunAndWait(stop) - - cms := client.Kube().CoreV1().ConfigMaps(namespace) - steps := []struct { - added *v1.ConfigMap - updated *v1.ConfigMap - deleted *v1.ConfigMap - }{ - {added: cm}, - - // Handle misconfiguration errors. - {updated: badCM}, - {updated: cm}, - {updated: badCM2}, - {updated: badCM}, - {updated: cm}, - {updated: badCM3}, - {updated: cm}, - - // Handle deletion. - {deleted: cm}, - {added: cm}, - } - - for i, step := range steps { - t.Run(fmt.Sprintf("[%v]", i), func(t *testing.T) { - g := NewWithT(t) - - switch { - case step.added != nil: - _, err := cms.Create(context.TODO(), step.added, metav1.CreateOptions{}) - g.Expect(err).Should(BeNil()) - case step.updated != nil: - _, err := cms.Update(context.TODO(), step.updated, metav1.UpdateOptions{}) - g.Expect(err).Should(BeNil()) - case step.deleted != nil: - g.Expect(cms.Delete(context.TODO(), step.deleted.Name, metav1.DeleteOptions{})). - Should(Succeed()) - } - - g.Eventually(func() *Config { - mu.Lock() - defer mu.Unlock() - return newConfig - }, time.Second).Should(Equal(&c)) - g.Eventually(func() string { - mu.Lock() - defer mu.Unlock() - return newValues - }, time.Second).Should(Equal(valuesConfig)) - }) - } -} diff --git a/pkg/kube/inject/webhook.go b/pkg/kube/inject/webhook.go deleted file mode 100644 index c1df58a42..000000000 --- a/pkg/kube/inject/webhook.go +++ /dev/null @@ -1,1051 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package inject - -import ( - "crypto/sha256" - "encoding/json" - "errors" - "fmt" - "net/http" - "os" - "strconv" - "strings" - "sync" - "text/template" - "time" -) - -import ( - "github.com/prometheus/prometheus/util/strutil" - "gomodules.xyz/jsonpatch/v3" - "istio.io/api/annotation" - "istio.io/api/label" - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/log" - kubeApiAdmissionv1 "k8s.io/api/admission/v1" - kubeApiAdmissionv1beta1 "k8s.io/api/admission/v1beta1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/serializer" - kjson "k8s.io/apimachinery/pkg/runtime/serializer/json" - "k8s.io/apimachinery/pkg/util/mergepatch" - "k8s.io/apimachinery/pkg/util/strategicpatch" - "sigs.k8s.io/yaml" -) - -import ( - opconfig "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/pilot/cmd/pilot-agent/status" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -var ( - runtimeScheme = runtime.NewScheme() - codecs = serializer.NewCodecFactory(runtimeScheme) - deserializer = codecs.UniversalDeserializer() - jsonSerializer = kjson.NewSerializerWithOptions(kjson.DefaultMetaFactory, runtimeScheme, runtimeScheme, kjson.SerializerOptions{}) - URLParameterToEnv = map[string]string{ - "cluster": "ISTIO_META_CLUSTER_ID", - "net": "ISTIO_META_NETWORK", - } -) - -func init() { - _ = corev1.AddToScheme(runtimeScheme) - _ = kubeApiAdmissionv1.AddToScheme(runtimeScheme) - _ = kubeApiAdmissionv1beta1.AddToScheme(runtimeScheme) -} - -const ( - // prometheus will convert annotation to this format - // `prometheus.io/scrape` `prometheus.io.scrape` `prometheus-io/scrape` have the same meaning in Prometheus - // for more details, please checkout [here](https://github.com/prometheus/prometheus/blob/71a0f42331566a8849863d77078083edbb0b3bc4/util/strutil/strconv.go#L40) - prometheusScrapeAnnotation = "prometheus_io_scrape" - prometheusPortAnnotation = "prometheus_io_port" - prometheusPathAnnotation = "prometheus_io_path" - - watchDebounceDelay = 100 * time.Millisecond -) - -// Webhook implements a mutating webhook for automatic proxy injection. -type Webhook struct { - mu sync.RWMutex - Config *Config - meshConfig *meshconfig.MeshConfig - valuesConfig ValuesConfig - - watcher Watcher - - env *model.Environment - revision string -} - -// nolint directives: interfacer -func loadConfig(injectFile, valuesFile string) (*Config, string, error) { - data, err := os.ReadFile(injectFile) - if err != nil { - return nil, "", err - } - var c *Config - if c, err = unmarshalConfig(data); err != nil { - log.Warnf("Failed to parse injectFile %s", string(data)) - return nil, "", err - } - - valuesConfig, err := os.ReadFile(valuesFile) - if err != nil { - return nil, "", err - } - return c, string(valuesConfig), nil -} - -func unmarshalConfig(data []byte) (*Config, error) { - c, err := UnmarshalConfig(data) - if err != nil { - return nil, err - } - - log.Debugf("New inject configuration: sha256sum %x", sha256.Sum256(data)) - log.Debugf("Policy: %v", c.Policy) - log.Debugf("AlwaysInjectSelector: %v", c.AlwaysInjectSelector) - log.Debugf("NeverInjectSelector: %v", c.NeverInjectSelector) - log.Debugf("Templates: |\n %v", c.RawTemplates, "\n", "\n ", -1) - return &c, nil -} - -// WebhookParameters configures parameters for the sidecar injection -// webhook. -type WebhookParameters struct { - // Watcher watches the sidecar injection configuration. - Watcher Watcher - - // Port is the webhook port, e.g. typically 443 for https. - // This is mainly used for tests. Webhook runs on the port started by Istiod. - Port int - - Env *model.Environment - - // Use an existing mux instead of creating our own. - Mux *http.ServeMux - - // The istio.io/rev this injector is responsible for - Revision string -} - -// NewWebhook creates a new instance of a mutating webhook for automatic sidecar injection. -func NewWebhook(p WebhookParameters) (*Webhook, error) { - if p.Mux == nil { - return nil, errors.New("expected mux to be passed, but was not passed") - } - - wh := &Webhook{ - watcher: p.Watcher, - meshConfig: p.Env.Mesh(), - env: p.Env, - revision: p.Revision, - } - - p.Watcher.SetHandler(wh.updateConfig) - sidecarConfig, valuesConfig, err := p.Watcher.Get() - if err != nil { - return nil, err - } - if err := wh.updateConfig(sidecarConfig, valuesConfig); err != nil { - log.Errorf("failed to process webhook config: %v", err) - } - - p.Mux.HandleFunc("/inject", wh.serveInject) - p.Mux.HandleFunc("/inject/", wh.serveInject) - - p.Env.Watcher.AddMeshHandler(func() { - wh.mu.Lock() - wh.meshConfig = p.Env.Mesh() - wh.mu.Unlock() - }) - - return wh, nil -} - -// Run implements the webhook server -func (wh *Webhook) Run(stop <-chan struct{}) { - go wh.watcher.Run(stop) -} - -func (wh *Webhook) updateConfig(sidecarConfig *Config, valuesConfig string) error { - wh.mu.Lock() - defer wh.mu.Unlock() - wh.Config = sidecarConfig - vc, err := NewValuesConfig(valuesConfig) - if err != nil { - return err - } - wh.valuesConfig = vc - return nil -} - -type ContainerReorder int - -const ( - MoveFirst ContainerReorder = iota - MoveLast - Remove -) - -func modifyContainers(cl []corev1.Container, name string, modifier ContainerReorder) []corev1.Container { - containers := []corev1.Container{} - var match *corev1.Container - for _, c := range cl { - c := c - if c.Name != name { - containers = append(containers, c) - } else { - match = &c - } - } - if match == nil { - return containers - } - switch modifier { - case MoveFirst: - return append([]corev1.Container{*match}, containers...) - case MoveLast: - return append(containers, *match) - case Remove: - return containers - default: - return cl - } -} - -func enablePrometheusMerge(mesh *meshconfig.MeshConfig, anno map[string]string) bool { - // If annotation is present, we look there first - if val, f := anno[annotation.PrometheusMergeMetrics.Name]; f { - bval, err := strconv.ParseBool(val) - if err != nil { - // This shouldn't happen since we validate earlier in the code - log.Warnf("invalid annotation %v=%v", annotation.PrometheusMergeMetrics.Name, bval) - } else { - return bval - } - } - // If mesh config setting is present, use that - if mesh.GetEnablePrometheusMerge() != nil { - return mesh.GetEnablePrometheusMerge().Value - } - // Otherwise, we default to enable - return true -} - -func toAdmissionResponse(err error) *kube.AdmissionResponse { - return &kube.AdmissionResponse{Result: &metav1.Status{Message: err.Error()}} -} - -func ParseTemplates(tmpls RawTemplates) (Templates, error) { - ret := make(Templates, len(tmpls)) - for k, t := range tmpls { - p, err := parseDryTemplate(t, InjectionFuncmap) - if err != nil { - return nil, err - } - ret[k] = p - } - return ret, nil -} - -type ValuesConfig struct { - raw string - asStruct *opconfig.Values - asMap map[string]interface{} -} - -func NewValuesConfig(v string) (ValuesConfig, error) { - c := ValuesConfig{raw: v} - valuesStruct := &opconfig.Values{} - if err := protomarshal.ApplyYAML(v, valuesStruct); err != nil { - return c, fmt.Errorf("could not parse configuration values: %v", err) - } - c.asStruct = valuesStruct - - values := map[string]interface{}{} - if err := yaml.Unmarshal([]byte(v), &values); err != nil { - return c, fmt.Errorf("could not parse configuration values: %v", err) - } - c.asMap = values - return c, nil -} - -type InjectionParameters struct { - pod *corev1.Pod - deployMeta metav1.ObjectMeta - typeMeta metav1.TypeMeta - templates map[string]*template.Template - defaultTemplate []string - aliases map[string][]string - meshConfig *meshconfig.MeshConfig - proxyConfig *meshconfig.ProxyConfig - valuesConfig ValuesConfig - revision string - proxyEnvs map[string]string - injectedAnnotations map[string]string -} - -func checkPreconditions(params InjectionParameters) { - spec := params.pod.Spec - metadata := params.pod.ObjectMeta - // If DNSPolicy is not ClusterFirst, the Envoy sidecar may not able to connect to Istio Pilot. - if spec.DNSPolicy != "" && spec.DNSPolicy != corev1.DNSClusterFirst { - podName := potentialPodName(metadata) - log.Warnf("%q's DNSPolicy is not %q. The Envoy sidecar may not able to connect to Istio Pilot", - metadata.Namespace+"/"+podName, corev1.DNSClusterFirst) - } -} - -func getInjectionStatus(podSpec corev1.PodSpec, revision string) string { - stat := &SidecarInjectionStatus{} - for _, c := range podSpec.InitContainers { - stat.InitContainers = append(stat.InitContainers, c.Name) - } - for _, c := range podSpec.Containers { - stat.Containers = append(stat.Containers, c.Name) - } - for _, c := range podSpec.Volumes { - stat.Volumes = append(stat.Volumes, c.Name) - } - for _, c := range podSpec.ImagePullSecrets { - stat.ImagePullSecrets = append(stat.ImagePullSecrets, c.Name) - } - // Rather than setting istio.io/rev label on injected pods include them here in status annotation. - // This keeps us from overwriting the istio.io/rev label when using revision tags (i.e. istio.io/rev=). - if revision == "" { - revision = "default" - } - stat.Revision = revision - statusAnnotationValue, err := json.Marshal(stat) - if err != nil { - return "{}" - } - return string(statusAnnotationValue) -} - -// injectPod is the core of the injection logic. This takes a pod and injection -// template, as well as some inputs to the injection template, and produces a -// JSON patch. -// -// In the webhook, we will receive a Pod directly from Kubernetes, and return the -// patch directly; Kubernetes will take care of applying the patch. -// -// For kube-inject, we will parse out a Pod from YAML (which may involve -// extraction from higher level types like Deployment), then apply the patch -// locally. -// -// The injection logic works by first applying the rendered injection template on -// top of the input pod This is done using a Strategic Patch Merge -// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-api-machinery/strategic-merge-patch.md) -// Currently only a single template is supported, although in the future the template to use will be configurable -// and multiple templates will be supported by applying them in successive order. -// -// In addition to the plain templating, there is some post processing done to -// handle cases that cannot feasibly be covered in the template, such as -// re-ordering pods, rewriting readiness probes, etc. -func injectPod(req InjectionParameters) ([]byte, error) { - checkPreconditions(req) - - // The patch will be built relative to the initial pod, capture its current state - originalPodSpec, err := json.Marshal(req.pod) - if err != nil { - return nil, err - } - - // Run the injection template, giving us a partial pod spec - mergedPod, injectedPodData, err := RunTemplate(req) - if err != nil { - return nil, fmt.Errorf("failed to run injection template: %v", err) - } - - mergedPod, err = reapplyOverwrittenContainers(mergedPod, req.pod, injectedPodData) - if err != nil { - return nil, fmt.Errorf("failed to re apply container: %v", err) - } - - // Apply some additional transformations to the pod - if err := postProcessPod(mergedPod, *injectedPodData, req); err != nil { - return nil, fmt.Errorf("failed to process pod: %v", err) - } - - patch, err := createPatch(mergedPod, originalPodSpec) - if err != nil { - return nil, fmt.Errorf("failed to create patch: %v", err) - } - - log.Debugf("AdmissionResponse: patch=%v\n", string(patch)) - return patch, nil -} - -// reapplyOverwrittenContainers enables users to provide container level overrides for settings in the injection template -// * originalPod: the pod before injection. If needed, we will apply some configurations from this pod on top of the final pod -// * templatePod: the rendered injection template. This is needed only to see what containers we injected -// * finalPod: the current result of injection, roughly equivalent to the merging of originalPod and templatePod -// There are essentially three cases we cover here: -// 1. There is no overlap in containers in original and template pod. We will do nothing. -// 2. There is an overlap (ie, both define istio-proxy), but that is because the pod is being re-injected. -// In this case we do nothing, since we want to apply the new settings -// 3. There is an overlap. We will re-apply the original container. -// -// Where "overlap" is a container defined in both the original and template pod. Typically, this would mean -// the user has defined an `istio-proxy` container in their own pod spec. -func reapplyOverwrittenContainers(finalPod *corev1.Pod, originalPod *corev1.Pod, templatePod *corev1.Pod) (*corev1.Pod, error) { - type podOverrides struct { - Containers []corev1.Container `json:"containers,omitempty"` - InitContainers []corev1.Container `json:"initContainers,omitempty"` - } - - overrides := podOverrides{} - existingOverrides := podOverrides{} - if annotationOverrides, f := originalPod.Annotations[annotation.ProxyOverrides.Name]; f { - if err := json.Unmarshal([]byte(annotationOverrides), &existingOverrides); err != nil { - return nil, err - } - } - - for _, c := range templatePod.Spec.Containers { - match := FindContainer(c.Name, existingOverrides.Containers) - if match == nil { - match = FindContainer(c.Name, originalPod.Spec.Containers) - } - if match == nil { - continue - } - overlay := *match.DeepCopy() - if overlay.Image == AutoImage { - overlay.Image = "" - } - overrides.Containers = append(overrides.Containers, overlay) - newMergedPod, err := applyContainer(finalPod, overlay) - if err != nil { - return nil, fmt.Errorf("failed to apply sidecar container: %v", err) - } - finalPod = newMergedPod - } - for _, c := range templatePod.Spec.InitContainers { - match := FindContainer(c.Name, existingOverrides.InitContainers) - if match == nil { - match = FindContainer(c.Name, originalPod.Spec.InitContainers) - } - if match == nil { - continue - } - overlay := *match.DeepCopy() - if overlay.Image == AutoImage { - overlay.Image = "" - } - overrides.InitContainers = append(overrides.InitContainers, overlay) - newMergedPod, err := applyInitContainer(finalPod, overlay) - if err != nil { - return nil, fmt.Errorf("failed to apply sidecar init container: %v", err) - } - finalPod = newMergedPod - } - - _, alreadyInjected := originalPod.Annotations[annotation.SidecarStatus.Name] - if !alreadyInjected && (len(overrides.Containers) > 0 || len(overrides.InitContainers) > 0) { - // We found any overrides. Put them in the pod annotation so we can re-apply them on re-injection - js, err := json.Marshal(overrides) - if err != nil { - return nil, err - } - if finalPod.Annotations == nil { - finalPod.Annotations = map[string]string{} - } - finalPod.Annotations[annotation.ProxyOverrides.Name] = string(js) - } - - return finalPod, nil -} - -// reinsertOverrides applies the containers listed in OverrideAnnotation to a pod. This is to achieve -// idempotency by handling an edge case where an injection template is modifying a container already -// present in the pod spec. In these cases, the logic to strip injected containers would remove the -// original injected parts as well, leading to the templating logic being different (for example, -// reading the .Spec.Containers field would be empty). -func reinsertOverrides(pod *corev1.Pod) (*corev1.Pod, error) { - type podOverrides struct { - Containers []corev1.Container `json:"containers,omitempty"` - InitContainers []corev1.Container `json:"initContainers,omitempty"` - } - - existingOverrides := podOverrides{} - if annotationOverrides, f := pod.Annotations[annotation.ProxyOverrides.Name]; f { - if err := json.Unmarshal([]byte(annotationOverrides), &existingOverrides); err != nil { - return nil, err - } - } - - pod = pod.DeepCopy() - for _, c := range existingOverrides.Containers { - match := FindContainer(c.Name, pod.Spec.Containers) - if match != nil { - continue - } - pod.Spec.Containers = append(pod.Spec.Containers, c) - } - - for _, c := range existingOverrides.InitContainers { - match := FindContainer(c.Name, pod.Spec.InitContainers) - if match != nil { - continue - } - pod.Spec.InitContainers = append(pod.Spec.InitContainers, c) - } - - return pod, nil -} - -func createPatch(pod *corev1.Pod, original []byte) ([]byte, error) { - reinjected, err := json.Marshal(pod) - if err != nil { - return nil, err - } - p, err := jsonpatch.CreatePatch(original, reinjected) - if err != nil { - return nil, err - } - return json.Marshal(p) -} - -// postProcessPod applies additionally transformations to the pod after merging with the injected template -// This is generally things that cannot reasonably be added to the template -func postProcessPod(pod *corev1.Pod, injectedPod corev1.Pod, req InjectionParameters) error { - if pod.Annotations == nil { - pod.Annotations = map[string]string{} - } - if pod.Labels == nil { - pod.Labels = map[string]string{} - } - - overwriteClusterInfo(pod.Spec.Containers, req) - - if err := applyPrometheusMerge(pod, req.meshConfig); err != nil { - return err - } - - if err := applyRewrite(pod, req); err != nil { - return err - } - - applyMetadata(pod, injectedPod, req) - - if err := reorderPod(pod, req); err != nil { - return err - } - - return nil -} - -func applyMetadata(pod *corev1.Pod, injectedPodData corev1.Pod, req InjectionParameters) { - if nw, ok := req.proxyEnvs["ISTIO_META_NETWORK"]; ok { - pod.Labels[label.TopologyNetwork.Name] = nw - } - // Add all additional injected annotations. These are overridden if needed - pod.Annotations[annotation.SidecarStatus.Name] = getInjectionStatus(injectedPodData.Spec, req.revision) - - // Deprecated; should be set directly in the template instead - for k, v := range req.injectedAnnotations { - pod.Annotations[k] = v - } -} - -// reorderPod ensures containers are properly ordered after merging -func reorderPod(pod *corev1.Pod, req InjectionParameters) error { - var merr error - mc := req.meshConfig - // Get copy of pod proxyconfig, to determine container ordering - if pca, f := req.pod.ObjectMeta.GetAnnotations()[annotation.ProxyConfig.Name]; f { - mc, merr = mesh.ApplyProxyConfig(pca, req.meshConfig) - if merr != nil { - return merr - } - } - - // nolint: staticcheck - holdPod := mc.GetDefaultConfig().GetHoldApplicationUntilProxyStarts().GetValue() || - req.valuesConfig.asStruct.GetGlobal().GetProxy().GetHoldApplicationUntilProxyStarts().GetValue() - - proxyLocation := MoveLast - // If HoldApplicationUntilProxyStarts is set, reorder the proxy location - if holdPod { - proxyLocation = MoveFirst - } - - // Proxy container should be last, unless HoldApplicationUntilProxyStarts is set - // This is to ensure `kubectl exec` and similar commands continue to default to the user's container - pod.Spec.Containers = modifyContainers(pod.Spec.Containers, ProxyContainerName, proxyLocation) - // Validation container must be first to block any user containers - pod.Spec.InitContainers = modifyContainers(pod.Spec.InitContainers, ValidationContainerName, MoveFirst) - // Init container must be last to allow any traffic to pass before iptables is setup - pod.Spec.InitContainers = modifyContainers(pod.Spec.InitContainers, InitContainerName, MoveLast) - pod.Spec.InitContainers = modifyContainers(pod.Spec.InitContainers, EnableCoreDumpName, MoveLast) - - return nil -} - -func applyRewrite(pod *corev1.Pod, req InjectionParameters) error { - sidecar := FindSidecar(pod.Spec.Containers) - if sidecar == nil { - return nil - } - - rewrite := ShouldRewriteAppHTTPProbers(pod.Annotations, req.valuesConfig.asStruct.GetSidecarInjectorWebhook().GetRewriteAppHTTPProbe().GetValue()) - // We don't have to escape json encoding here when using golang libraries. - if rewrite { - if prober := DumpAppProbers(&pod.Spec, req.meshConfig.GetDefaultConfig().GetStatusPort()); prober != "" { - sidecar.Env = append(sidecar.Env, corev1.EnvVar{Name: status.KubeAppProberEnvName, Value: prober}) - } - patchRewriteProbe(pod.Annotations, pod, req.meshConfig.GetDefaultConfig().GetStatusPort()) - } - return nil -} - -var emptyScrape = status.PrometheusScrapeConfiguration{} - -// applyPrometheusMerge configures prometheus scraping annotations for the "metrics merge" feature. -// This moves the current prometheus.io annotations into an environment variable and replaces them -// pointing to the agent. -func applyPrometheusMerge(pod *corev1.Pod, mesh *meshconfig.MeshConfig) error { - if getPrometheusScrape(pod) && - enablePrometheusMerge(mesh, pod.ObjectMeta.Annotations) { - targetPort := strconv.Itoa(int(mesh.GetDefaultConfig().GetStatusPort())) - if cur, f := getPrometheusPort(pod); f { - // We have already set the port, assume user is controlling this or, more likely, re-injected - // the pod. - if cur == targetPort { - return nil - } - } - scrape := getPrometheusScrapeConfiguration(pod) - sidecar := FindSidecar(pod.Spec.Containers) - if sidecar != nil && scrape != emptyScrape { - by, err := json.Marshal(scrape) - if err != nil { - return err - } - sidecar.Env = append(sidecar.Env, corev1.EnvVar{Name: status.PrometheusScrapingConfig.Name, Value: string(by)}) - } - if pod.Annotations == nil { - pod.Annotations = map[string]string{} - } - // if a user sets `prometheus/io/path: foo`, then we add `prometheus.io/path: /stats/prometheus` - // prometheus will pick a random one - // need to clear out all variants and then set ours - clearPrometheusAnnotations(pod) - pod.Annotations["prometheus.io/port"] = targetPort - pod.Annotations["prometheus.io/path"] = "/stats/prometheus" - pod.Annotations["prometheus.io/scrape"] = "true" - return nil - } - - return nil -} - -// getPrometheusScrape respect prometheus scrape config -// not to doing prometheusMerge if this return false -func getPrometheusScrape(pod *corev1.Pod) bool { - for k, val := range pod.Annotations { - if strutil.SanitizeLabelName(k) != prometheusScrapeAnnotation { - continue - } - - if scrape, err := strconv.ParseBool(val); err == nil { - return scrape - } - } - - return true -} - -var prometheusAnnotations = sets.New( - prometheusPathAnnotation, - prometheusPortAnnotation, - prometheusScrapeAnnotation, -) - -func clearPrometheusAnnotations(pod *corev1.Pod) { - needRemovedKeys := make([]string, 0, 2) - for k := range pod.Annotations { - anno := strutil.SanitizeLabelName(k) - if prometheusAnnotations.Contains(anno) { - needRemovedKeys = append(needRemovedKeys, k) - } - } - - for _, k := range needRemovedKeys { - delete(pod.Annotations, k) - } -} - -func getPrometheusScrapeConfiguration(pod *corev1.Pod) status.PrometheusScrapeConfiguration { - cfg := status.PrometheusScrapeConfiguration{} - - for k, val := range pod.Annotations { - anno := strutil.SanitizeLabelName(k) - switch anno { - case prometheusPortAnnotation: - cfg.Port = val - case prometheusScrapeAnnotation: - cfg.Scrape = val - case prometheusPathAnnotation: - cfg.Path = val - } - } - - return cfg -} - -func getPrometheusPort(pod *corev1.Pod) (string, bool) { - for k, val := range pod.Annotations { - if strutil.SanitizeLabelName(k) != prometheusPortAnnotation { - continue - } - - return val, true - } - - return "", false -} - -const ( - // AutoImage is the special image name to indicate to the injector that we should use the injected image, and NOT override it - // This is necessary because image is a required field on container, so if a user defines an istio-proxy container - // with customizations they must set an image. - AutoImage = "auto" -) - -// applyContainer merges a container spec on top of the provided pod -func applyContainer(target *corev1.Pod, container corev1.Container) (*corev1.Pod, error) { - overlay := &corev1.Pod{Spec: corev1.PodSpec{Containers: []corev1.Container{container}}} - - overlayJSON, err := json.Marshal(overlay) - if err != nil { - return nil, err - } - - return applyOverlay(target, overlayJSON) -} - -// applyInitContainer merges a container spec on top of the provided pod as an init container -func applyInitContainer(target *corev1.Pod, container corev1.Container) (*corev1.Pod, error) { - overlay := &corev1.Pod{Spec: corev1.PodSpec{ - // We need to set containers to empty, otherwise it will marshal as "null" and delete all containers - Containers: []corev1.Container{}, - InitContainers: []corev1.Container{container}, - }} - - overlayJSON, err := json.Marshal(overlay) - if err != nil { - return nil, err - } - - return applyOverlay(target, overlayJSON) -} - -func patchHandleUnmarshal(j []byte, unmarshal func(data []byte, v interface{}) error) (map[string]interface{}, error) { - if j == nil { - j = []byte("{}") - } - - m := map[string]interface{}{} - err := unmarshal(j, &m) - if err != nil { - return nil, mergepatch.ErrBadJSONDoc - } - return m, nil -} - -// StrategicMergePatchYAML is a small fork of strategicpatch.StrategicMergePatch to allow YAML patches -// This avoids expensive conversion from YAML to JSON -func StrategicMergePatchYAML(originalJSON []byte, patchYAML []byte, dataStruct interface{}) ([]byte, error) { - schema, err := strategicpatch.NewPatchMetaFromStruct(dataStruct) - if err != nil { - return nil, err - } - - originalMap, err := patchHandleUnmarshal(originalJSON, json.Unmarshal) - if err != nil { - return nil, err - } - patchMap, err := patchHandleUnmarshal(patchYAML, func(data []byte, v interface{}) error { - return yaml.Unmarshal(data, v) - }) - if err != nil { - return nil, err - } - - result, err := strategicpatch.StrategicMergeMapPatchUsingLookupPatchMeta(originalMap, patchMap, schema) - if err != nil { - return nil, err - } - - return json.Marshal(result) -} - -// applyContainer merges a pod spec, provided as JSON, on top of the provided pod -func applyOverlayYAML(target *corev1.Pod, overlayYAML []byte) (*corev1.Pod, error) { - currentJSON, err := json.Marshal(target) - if err != nil { - return nil, err - } - - pod := corev1.Pod{} - // Overlay the injected template onto the original podSpec - patched, err := StrategicMergePatchYAML(currentJSON, overlayYAML, pod) - if err != nil { - return nil, fmt.Errorf("strategic merge: %v", err) - } - - if err := json.Unmarshal(patched, &pod); err != nil { - return nil, fmt.Errorf("unmarshal patched pod: %v", err) - } - return &pod, nil -} - -// applyContainer merges a pod spec, provided as JSON, on top of the provided pod -func applyOverlay(target *corev1.Pod, overlayJSON []byte) (*corev1.Pod, error) { - currentJSON, err := json.Marshal(target) - if err != nil { - return nil, err - } - - pod := corev1.Pod{} - // Overlay the injected template onto the original podSpec - patched, err := strategicpatch.StrategicMergePatch(currentJSON, overlayJSON, pod) - if err != nil { - return nil, fmt.Errorf("strategic merge: %v", err) - } - - if err := json.Unmarshal(patched, &pod); err != nil { - return nil, fmt.Errorf("unmarshal patched pod: %v", err) - } - return &pod, nil -} - -func (wh *Webhook) inject(ar *kube.AdmissionReview, path string) *kube.AdmissionResponse { - req := ar.Request - var pod corev1.Pod - if err := json.Unmarshal(req.Object.Raw, &pod); err != nil { - handleError(fmt.Sprintf("Could not unmarshal raw object: %v %s", err, - string(req.Object.Raw))) - return toAdmissionResponse(err) - } - // Managed fields is sometimes extremely large, leading to excessive CPU time on patch generation - // It does not impact the injection output at all, so we can just remove it. - pod.ManagedFields = nil - - // Deal with potential empty fields, e.g., when the pod is created by a deployment - podName := potentialPodName(pod.ObjectMeta) - if pod.ObjectMeta.Namespace == "" { - pod.ObjectMeta.Namespace = req.Namespace - } - log.Infof("Sidecar injection request for %v/%v", req.Namespace, podName) - log.Debugf("Object: %v", string(req.Object.Raw)) - log.Debugf("OldObject: %v", string(req.OldObject.Raw)) - - wh.mu.RLock() - if !injectRequired(IgnoredNamespaces.UnsortedList(), wh.Config, &pod.Spec, pod.ObjectMeta) { - log.Infof("Skipping %s/%s due to policy check", pod.ObjectMeta.Namespace, podName) - totalSkippedInjections.Increment() - wh.mu.RUnlock() - return &kube.AdmissionResponse{ - Allowed: true, - } - } - - proxyConfig := mesh.DefaultProxyConfig() - if wh.env.PushContext != nil && wh.env.PushContext.ProxyConfigs != nil { - if generatedProxyConfig := wh.env.PushContext.ProxyConfigs.EffectiveProxyConfig( - &model.NodeMetadata{ - Namespace: pod.Namespace, - Labels: pod.Labels, - Annotations: pod.Annotations, - }, wh.meshConfig); generatedProxyConfig != nil { - proxyConfig = generatedProxyConfig - } - } - deploy, typeMeta := kube.GetDeployMetaFromPod(&pod) - params := InjectionParameters{ - pod: &pod, - deployMeta: deploy, - typeMeta: typeMeta, - templates: wh.Config.Templates, - defaultTemplate: wh.Config.DefaultTemplates, - aliases: wh.Config.Aliases, - meshConfig: wh.meshConfig, - proxyConfig: proxyConfig, - valuesConfig: wh.valuesConfig, - revision: wh.revision, - injectedAnnotations: wh.Config.InjectedAnnotations, - proxyEnvs: parseInjectEnvs(path), - } - wh.mu.RUnlock() - - patchBytes, err := injectPod(params) - if err != nil { - handleError(fmt.Sprintf("Pod injection failed: %v", err)) - return toAdmissionResponse(err) - } - - reviewResponse := kube.AdmissionResponse{ - Allowed: true, - Patch: patchBytes, - PatchType: func() *string { - pt := "JSONPatch" - return &pt - }(), - } - totalSuccessfulInjections.Increment() - return &reviewResponse -} - -func (wh *Webhook) serveInject(w http.ResponseWriter, r *http.Request) { - totalInjections.Increment() - var body []byte - if r.Body != nil { - if data, err := kube.HTTPConfigReader(r); err == nil { - body = data - } else { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - } - if len(body) == 0 { - handleError("no body found") - http.Error(w, "no body found", http.StatusBadRequest) - return - } - - // verify the content type is accurate - contentType := r.Header.Get("Content-Type") - if contentType != "application/json" { - handleError(fmt.Sprintf("contentType=%s, expect application/json", contentType)) - http.Error(w, "invalid Content-Type, want `application/json`", http.StatusUnsupportedMediaType) - return - } - - path := "" - if r.URL != nil { - path = r.URL.Path - } - - var reviewResponse *kube.AdmissionResponse - var obj runtime.Object - var ar *kube.AdmissionReview - if out, _, err := deserializer.Decode(body, nil, obj); err != nil { - handleError(fmt.Sprintf("Could not decode body: %v", err)) - reviewResponse = toAdmissionResponse(err) - } else { - log.Debugf("AdmissionRequest for path=%s\n", path) - ar, err = kube.AdmissionReviewKubeToAdapter(out) - if err != nil { - handleError(fmt.Sprintf("Could not decode object: %v", err)) - } - reviewResponse = wh.inject(ar, path) - } - - response := kube.AdmissionReview{} - response.Response = reviewResponse - var responseKube runtime.Object - var apiVersion string - if ar != nil { - apiVersion = ar.APIVersion - response.TypeMeta = ar.TypeMeta - if response.Response != nil { - if ar.Request != nil { - response.Response.UID = ar.Request.UID - } - } - } - responseKube = kube.AdmissionReviewAdapterToKube(&response, apiVersion) - resp, err := json.Marshal(responseKube) - if err != nil { - log.Errorf("Could not encode response: %v", err) - http.Error(w, fmt.Sprintf("could not encode response: %v", err), http.StatusInternalServerError) - } - if _, err := w.Write(resp); err != nil { - log.Errorf("Could not write response: %v", err) - http.Error(w, fmt.Sprintf("could not write response: %v", err), http.StatusInternalServerError) - } -} - -// parseInjectEnvs parse new envs from inject url path -// follow format: /inject/k1/v1/k2/v2 when values do not contain slashes, -// follow format: /inject/:ENV:net=network1:ENV:cluster=cluster1:ENV:rootpage=/foo/bar -// when values contain slashes. -func parseInjectEnvs(path string) map[string]string { - path = strings.TrimSuffix(path, "/") - res := func(path string) []string { - parts := strings.SplitN(path, "/", 3) - // The 3rd part has to start with separator :ENV: - // If not, this inject path is considered using slash as separator - // If length is less than 3, then the path is simply "/inject", - // process just like before :ENV: separator is introduced. - var newRes []string - if len(parts) == 3 { - if strings.HasPrefix(parts[2], ":ENV:") { - pairs := strings.Split(parts[2], ":ENV:") - for i := 1; i < len(pairs); i++ { // skip the first part, it is a nil - pair := strings.SplitN(pairs[i], "=", 2) - // The first part is the variable name which can not be empty - // the second part is the variable value which can be empty but has to exist - // for example, aaa=bbb, aaa= are valid, but =aaa or = are not valid, the - // invalid ones will be ignored. - if len(pair[0]) > 0 && len(pair) == 2 { - newRes = append(newRes, pair...) - } - } - return newRes - } - return strings.Split(parts[2], "/") - } - return newRes - }(path) - newEnvs := make(map[string]string) - - for i := 0; i < len(res); i += 2 { - k := res[i] - if i == len(res)-1 { // ignore the last key without value - log.Warnf("Odd number of inject env entries, ignore the last key %s\n", k) - break - } - - env, found := URLParameterToEnv[k] - if !found { - env = strings.ToUpper(k) // if not found, use the custom env directly - } - if env != "" { - newEnvs[env] = res[i+1] - } - } - - return newEnvs -} - -func handleError(message string) { - log.Errorf(message) - totalFailedInjections.Increment() -} diff --git a/pkg/kube/inject/webhook_test.go b/pkg/kube/inject/webhook_test.go deleted file mode 100644 index 362ca2c07..000000000 --- a/pkg/kube/inject/webhook_test.go +++ /dev/null @@ -1,1316 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package inject - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "net/http" - "net/http/httptest" - "os" - "path/filepath" - "reflect" - "strings" - "testing" -) - -import ( - jsonpatch "github.com/evanphx/json-patch/v5" - openshiftv1 "github.com/openshift/api/apps/v1" - "google.golang.org/protobuf/types/known/wrapperspb" - "istio.io/api/annotation" - meshconfig "istio.io/api/mesh/v1alpha1" - v1beta12 "istio.io/api/networking/v1beta1" - "k8s.io/api/admission/v1beta1" - appsv1 "k8s.io/api/apps/v1" - batchv1 "k8s.io/api/batch/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - k8syaml "k8s.io/apimachinery/pkg/util/yaml" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/pkg/manifest" - "github.com/apache/dubbo-go-pixiu/operator/pkg/name" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/test/util" - "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/file" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" - sutil "github.com/apache/dubbo-go-pixiu/security/pkg/nodeagent/util" -) - -const yamlSeparator = "\n---" - -var minimalSidecarTemplate = &Config{ - Policy: InjectionPolicyEnabled, - DefaultTemplates: []string{SidecarTemplateName}, - RawTemplates: map[string]string{SidecarTemplateName: ` -spec: - initContainers: - - name: istio-init - containers: - - name: istio-proxy - volumes: - - name: istio-envoy - imagePullSecrets: - - name: istio-image-pull-secrets -`}, -} - -func parseToLabelSelector(t *testing.T, selector string) *metav1.LabelSelector { - result, err := metav1.ParseToLabelSelector(selector) - if err != nil { - t.Errorf("Invalid selector %v: %v", selector, err) - } - - return result -} - -func TestInjectRequired(t *testing.T) { - podSpec := &corev1.PodSpec{} - podSpecHostNetwork := &corev1.PodSpec{ - HostNetwork: true, - } - cases := []struct { - config *Config - podSpec *corev1.PodSpec - meta metav1.ObjectMeta - want bool - }{ - { - config: &Config{ - Policy: InjectionPolicyEnabled, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "no-policy", - Namespace: "test-namespace", - Annotations: map[string]string{}, - }, - want: true, - }, - { - config: &Config{ - Policy: InjectionPolicyEnabled, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "default-policy", - Namespace: "test-namespace", - }, - want: true, - }, - { - config: &Config{ - Policy: InjectionPolicyEnabled, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "force-on-policy", - Namespace: "test-namespace", - Annotations: map[string]string{annotation.SidecarInject.Name: "true"}, - }, - want: true, - }, - { - config: &Config{ - Policy: InjectionPolicyEnabled, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "force-off-policy", - Namespace: "test-namespace", - Annotations: map[string]string{annotation.SidecarInject.Name: "false"}, - }, - want: false, - }, - { - config: &Config{ - Policy: InjectionPolicyDisabled, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "no-policy", - Namespace: "test-namespace", - Annotations: map[string]string{}, - }, - want: false, - }, - { - config: &Config{ - Policy: InjectionPolicyDisabled, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "default-policy", - Namespace: "test-namespace", - }, - want: false, - }, - { - config: &Config{ - Policy: InjectionPolicyDisabled, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "force-on-policy", - Namespace: "test-namespace", - Annotations: map[string]string{annotation.SidecarInject.Name: "true"}, - }, - want: true, - }, - { - config: &Config{ - Policy: InjectionPolicyDisabled, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "force-off-policy", - Namespace: "test-namespace", - Annotations: map[string]string{annotation.SidecarInject.Name: "false"}, - }, - want: false, - }, - { - config: &Config{ - Policy: InjectionPolicyEnabled, - }, - podSpec: podSpecHostNetwork, - meta: metav1.ObjectMeta{ - Name: "force-off-policy", - Namespace: "test-namespace", - Annotations: map[string]string{}, - }, - want: false, - }, - { - config: &Config{ - Policy: "wrong_policy", - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "wrong-policy", - Namespace: "test-namespace", - Annotations: map[string]string{}, - }, - want: false, - }, - { - config: &Config{ - Policy: InjectionPolicyEnabled, - AlwaysInjectSelector: []metav1.LabelSelector{{MatchLabels: map[string]string{"foo": "bar"}}}, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-enabled-always-inject-no-labels", - Namespace: "test-namespace", - }, - want: true, - }, - { - config: &Config{ - Policy: InjectionPolicyEnabled, - AlwaysInjectSelector: []metav1.LabelSelector{{MatchLabels: map[string]string{"foo": "bar"}}}, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-enabled-always-inject-with-labels", - Namespace: "test-namespace", - Labels: map[string]string{"foo": "bar1"}, - }, - want: true, - }, - { - config: &Config{ - Policy: InjectionPolicyDisabled, - AlwaysInjectSelector: []metav1.LabelSelector{{MatchLabels: map[string]string{"foo": "bar"}}}, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-disabled-always-inject-no-labels", - Namespace: "test-namespace", - }, - want: false, - }, - { - config: &Config{ - Policy: InjectionPolicyDisabled, - AlwaysInjectSelector: []metav1.LabelSelector{{MatchLabels: map[string]string{"foo": "bar"}}}, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-disabled-always-inject-with-labels", - Namespace: "test-namespace", - Labels: map[string]string{"foo": "bar"}, - }, - want: true, - }, - { - config: &Config{ - Policy: InjectionPolicyEnabled, - NeverInjectSelector: []metav1.LabelSelector{{MatchLabels: map[string]string{"foo": "bar"}}}, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-enabled-never-inject-no-labels", - Namespace: "test-namespace", - }, - want: true, - }, - { - config: &Config{ - Policy: InjectionPolicyEnabled, - NeverInjectSelector: []metav1.LabelSelector{{MatchLabels: map[string]string{"foo": "bar"}}}, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-enabled-never-inject-with-labels", - Namespace: "test-namespace", - Labels: map[string]string{"foo": "bar"}, - }, - want: false, - }, - { - config: &Config{ - Policy: InjectionPolicyDisabled, - NeverInjectSelector: []metav1.LabelSelector{{MatchLabels: map[string]string{"foo": "bar"}}}, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-disabled-never-inject-no-labels", - Namespace: "test-namespace", - }, - want: false, - }, - { - config: &Config{ - Policy: InjectionPolicyDisabled, - NeverInjectSelector: []metav1.LabelSelector{{MatchLabels: map[string]string{"foo": "bar"}}}, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-disabled-never-inject-with-labels", - Namespace: "test-namespace", - Labels: map[string]string{"foo": "bar"}, - }, - want: false, - }, - { - config: &Config{ - Policy: InjectionPolicyEnabled, - NeverInjectSelector: []metav1.LabelSelector{*parseToLabelSelector(t, "foo")}, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-enabled-never-inject-with-empty-label", - Namespace: "test-namespace", - Labels: map[string]string{"foo": "", "foo2": "bar2"}, - }, - want: false, - }, - { - config: &Config{ - Policy: InjectionPolicyDisabled, - AlwaysInjectSelector: []metav1.LabelSelector{*parseToLabelSelector(t, "foo")}, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-disabled-always-inject-with-empty-label", - Namespace: "test-namespace", - Labels: map[string]string{"foo": "", "foo2": "bar2"}, - }, - want: true, - }, - { - config: &Config{ - Policy: InjectionPolicyDisabled, - AlwaysInjectSelector: []metav1.LabelSelector{*parseToLabelSelector(t, "always")}, - NeverInjectSelector: []metav1.LabelSelector{*parseToLabelSelector(t, "never")}, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-disabled-always-never-inject-with-label-returns-true", - Namespace: "test-namespace", - Labels: map[string]string{"always": "bar", "foo2": "bar2"}, - }, - want: true, - }, - { - config: &Config{ - Policy: InjectionPolicyDisabled, - AlwaysInjectSelector: []metav1.LabelSelector{*parseToLabelSelector(t, "always")}, - NeverInjectSelector: []metav1.LabelSelector{*parseToLabelSelector(t, "never")}, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-disabled-always-never-inject-with-label-returns-false", - Namespace: "test-namespace", - Labels: map[string]string{"never": "bar", "foo2": "bar2"}, - }, - want: false, - }, - { - config: &Config{ - Policy: InjectionPolicyDisabled, - AlwaysInjectSelector: []metav1.LabelSelector{*parseToLabelSelector(t, "always")}, - NeverInjectSelector: []metav1.LabelSelector{*parseToLabelSelector(t, "never")}, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-disabled-always-never-inject-with-both-labels", - Namespace: "test-namespace", - Labels: map[string]string{"always": "bar", "never": "bar2"}, - }, - want: false, - }, - { - config: &Config{ - Policy: InjectionPolicyEnabled, - NeverInjectSelector: []metav1.LabelSelector{*parseToLabelSelector(t, "foo")}, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-enabled-annotation-true-never-inject", - Namespace: "test-namespace", - Annotations: map[string]string{annotation.SidecarInject.Name: "true"}, - Labels: map[string]string{"foo": "", "foo2": "bar2"}, - }, - want: true, - }, - { - config: &Config{ - Policy: InjectionPolicyEnabled, - AlwaysInjectSelector: []metav1.LabelSelector{*parseToLabelSelector(t, "foo")}, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-enabled-annotation-false-always-inject", - Namespace: "test-namespace", - Annotations: map[string]string{annotation.SidecarInject.Name: "false"}, - Labels: map[string]string{"foo": "", "foo2": "bar2"}, - }, - want: false, - }, - { - config: &Config{ - Policy: InjectionPolicyDisabled, - AlwaysInjectSelector: []metav1.LabelSelector{*parseToLabelSelector(t, "foo")}, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-disabled-annotation-false-always-inject", - Namespace: "test-namespace", - Annotations: map[string]string{annotation.SidecarInject.Name: "false"}, - Labels: map[string]string{"foo": "", "foo2": "bar2"}, - }, - want: false, - }, - { - config: &Config{ - Policy: InjectionPolicyEnabled, - NeverInjectSelector: []metav1.LabelSelector{*parseToLabelSelector(t, "foo"), *parseToLabelSelector(t, "bar")}, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-enabled-never-inject-multiple-labels", - Namespace: "test-namespace", - Labels: map[string]string{"label1": "", "bar": "anything"}, - }, - want: false, - }, - { - config: &Config{ - Policy: InjectionPolicyDisabled, - AlwaysInjectSelector: []metav1.LabelSelector{*parseToLabelSelector(t, "foo"), *parseToLabelSelector(t, "bar")}, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-enabled-always-inject-multiple-labels", - Namespace: "test-namespace", - Labels: map[string]string{"label1": "", "bar": "anything"}, - }, - want: true, - }, - { - config: &Config{ - Policy: InjectionPolicyDisabled, - NeverInjectSelector: []metav1.LabelSelector{*parseToLabelSelector(t, "foo")}, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-disabled-annotation-true-never-inject", - Namespace: "test-namespace", - Annotations: map[string]string{annotation.SidecarInject.Name: "true"}, - Labels: map[string]string{"foo": "", "foo2": "bar2"}, - }, - want: true, - }, - { - config: &Config{ - Policy: InjectionPolicyDisabled, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-disabled-label-enabled", - Namespace: "test-namespace", - // Annotations: map[string]string{annotation.SidecarInject.Name: "true"}, - Labels: map[string]string{annotation.SidecarInject.Name: "true"}, - }, - want: true, - }, - { - config: &Config{ - Policy: InjectionPolicyDisabled, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-disabled-both-enabled", - Namespace: "test-namespace", - Annotations: map[string]string{annotation.SidecarInject.Name: "true"}, - Labels: map[string]string{annotation.SidecarInject.Name: "true"}, - }, - want: true, - }, - { - config: &Config{ - Policy: InjectionPolicyDisabled, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-disabled-label-enabled-annotation-disabled", - Namespace: "test-namespace", - Annotations: map[string]string{annotation.SidecarInject.Name: "false"}, - Labels: map[string]string{annotation.SidecarInject.Name: "true"}, - }, - want: true, - }, - { - config: &Config{ - Policy: InjectionPolicyDisabled, - }, - podSpec: podSpec, - meta: metav1.ObjectMeta{ - Name: "policy-disabled-label-disabled-annotation-enabled", - Namespace: "test-namespace", - Annotations: map[string]string{annotation.SidecarInject.Name: "true"}, - Labels: map[string]string{annotation.SidecarInject.Name: "false"}, - }, - want: false, - }, - } - - for _, c := range cases { - if got := injectRequired(IgnoredNamespaces.UnsortedList(), c.config, c.podSpec, c.meta); got != c.want { - t.Errorf("injectRequired(%v, %v) got %v want %v", c.config, c.meta, got, c.want) - } - } -} - -func simulateOwnerRef(m metav1.ObjectMeta, name string, gvk schema.GroupVersionKind) metav1.ObjectMeta { - controller := true - m.GenerateName = name - m.OwnerReferences = []metav1.OwnerReference{{ - APIVersion: gvk.GroupVersion().String(), - Kind: gvk.Kind, - Name: name, - Controller: &controller, - }} - return m -} - -func objectToPod(t testing.TB, obj runtime.Object) *corev1.Pod { - gvk := obj.GetObjectKind().GroupVersionKind() - defaultConversion := func(template corev1.PodTemplateSpec, name string) *corev1.Pod { - template.ObjectMeta = simulateOwnerRef(template.ObjectMeta, name, gvk) - return &corev1.Pod{ - ObjectMeta: template.ObjectMeta, - Spec: template.Spec, - } - } - switch o := obj.(type) { - case *corev1.Pod: - return o - case *batchv1.CronJob: - o.Spec.JobTemplate.Spec.Template.ObjectMeta = simulateOwnerRef(o.Spec.JobTemplate.Spec.Template.ObjectMeta, o.Name, gvk) - return &corev1.Pod{ - ObjectMeta: o.Spec.JobTemplate.Spec.Template.ObjectMeta, - Spec: o.Spec.JobTemplate.Spec.Template.Spec, - } - case *appsv1.DaemonSet: - return defaultConversion(o.Spec.Template, o.Name) - case *appsv1.ReplicaSet: - return defaultConversion(o.Spec.Template, o.Name) - case *corev1.ReplicationController: - return defaultConversion(*o.Spec.Template, o.Name) - case *appsv1.StatefulSet: - return defaultConversion(o.Spec.Template, o.Name) - case *batchv1.Job: - return defaultConversion(o.Spec.Template, o.Name) - case *openshiftv1.DeploymentConfig: - return defaultConversion(*o.Spec.Template, o.Name) - case *appsv1.Deployment: - // Deployment is special since its a double nested resource - rsgvk := schema.GroupVersionKind{Kind: "ReplicaSet", Group: "apps", Version: "v1"} - o.Spec.Template.ObjectMeta = simulateOwnerRef(o.Spec.Template.ObjectMeta, o.Name+"-fake", rsgvk) - o.Spec.Template.ObjectMeta.GenerateName += "-" - if o.Spec.Template.ObjectMeta.Labels == nil { - o.Spec.Template.ObjectMeta.Labels = map[string]string{} - } - o.Spec.Template.ObjectMeta.Labels["pod-template-hash"] = "fake" - return &corev1.Pod{ - ObjectMeta: o.Spec.Template.ObjectMeta, - Spec: o.Spec.Template.Spec, - } - } - t.Fatalf("unknown type: %T", obj) - return nil -} - -func readInjectionSettings(t testing.TB, fname string) (*Config, ValuesConfig, *meshconfig.MeshConfig) { - values := file.AsStringOrFail(t, filepath.Join("testdata", "inputs", fname+".values.gen.yaml")) - template := file.AsBytesOrFail(t, filepath.Join("testdata", "inputs", fname+".template.gen.yaml")) - meshc := file.AsStringOrFail(t, filepath.Join("testdata", "inputs", fname+".mesh.gen.yaml")) - - vc, err := NewValuesConfig(values) - if err != nil { - t.Fatal(err) - } - - cfg, err := UnmarshalConfig(template) - if err != nil { - t.Fatalf("failed to unmarshal injectionConfig: %v", err) - } - meshConfig, err := mesh.ApplyMeshConfig(meshc, mesh.DefaultMeshConfig()) - if err != nil { - t.Fatalf("failed to unmarshal meshconfig: %v", err) - } - - return &cfg, vc, meshConfig -} - -// loadInjectionSettings will render the charts using the operator, with given yaml overrides. -// This allows us to fully simulate what will actually happen at run time. -func writeInjectionSettings(t testing.TB, fname string, setFlags []string, inFilePath string) { - // add --set installPackagePath= - setFlags = append(setFlags, "installPackagePath="+defaultInstallPackageDir(), "profile=empty", "components.pilot.enabled=true") - var inFilenames []string - if inFilePath != "" { - inFilenames = []string{"testdata/inject/" + inFilePath} - } - - l := clog.NewConsoleLogger(os.Stdout, os.Stderr, nil) - manifests, _, err := manifest.GenManifests(inFilenames, setFlags, false, nil, nil, l) - if err != nil { - t.Fatalf("failed to generate manifests: %v", err) - } - for _, mlist := range manifests[name.PilotComponentName] { - for _, object := range strings.Split(mlist, yamlSeparator) { - if len(object) == 0 { - continue - } - r := bytes.NewReader([]byte(object)) - decoder := k8syaml.NewYAMLOrJSONDecoder(r, 1024) - - out := &unstructured.Unstructured{} - err := decoder.Decode(out) - if err != nil { - t.Fatalf("error decoding object %q: %v", object, err) - } - if out.GetName() == "istio-sidecar-injector" && (out.GroupVersionKind() == schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"}) { - data, ok := out.Object["data"].(map[string]interface{}) - if !ok { - t.Fatalf("failed to convert %v", out) - } - config, ok := data["config"].(string) - if !ok { - t.Fatalf("failed to config %v", data) - } - vs, ok := data["values"].(string) - if !ok { - t.Fatalf("failed to config %v", data) - } - if err := os.WriteFile(filepath.Join("testdata", "inputs", fname+".values.gen.yaml"), []byte(vs), 0o644); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join("testdata", "inputs", fname+".template.gen.yaml"), []byte(config), 0o644); err != nil { - t.Fatal(err) - } - } else if out.GetName() == "istio" && (out.GroupVersionKind() == schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"}) { - data, ok := out.Object["data"].(map[string]interface{}) - if !ok { - t.Fatalf("failed to convert %v", out) - } - meshdata, ok := data["mesh"].(string) - if !ok { - t.Fatalf("failed to get meshconfig %v", data) - } - if err := os.WriteFile(filepath.Join("testdata", "inputs", fname+".mesh.gen.yaml"), []byte(meshdata), 0o644); err != nil { - t.Fatal(err) - } - } - } - } -} - -func splitYamlFile(yamlFile string, t *testing.T) [][]byte { - t.Helper() - yamlBytes := util.ReadFile(t, yamlFile) - return splitYamlBytes(yamlBytes, t) -} - -func splitYamlBytes(yaml []byte, t *testing.T) [][]byte { - t.Helper() - stringParts := strings.Split(string(yaml), yamlSeparator) - byteParts := make([][]byte, 0) - for _, stringPart := range stringParts { - byteParts = append(byteParts, getInjectableYamlDocs(stringPart, t)...) - } - if len(byteParts) == 0 { - t.Fatalf("Found no injectable parts") - } - return byteParts -} - -func getInjectableYamlDocs(yamlDoc string, t *testing.T) [][]byte { - t.Helper() - m := make(map[string]interface{}) - if err := yaml.Unmarshal([]byte(yamlDoc), &m); err != nil { - t.Fatal(err) - } - switch m["kind"] { - case "Deployment", "DeploymentConfig", "DaemonSet", "StatefulSet", "Job", "ReplicaSet", - "ReplicationController", "CronJob", "Pod": - return [][]byte{[]byte(yamlDoc)} - case "List": - // Split apart the list into separate yaml documents. - out := make([][]byte, 0) - list := metav1.List{} - if err := yaml.Unmarshal([]byte(yamlDoc), &list); err != nil { - t.Fatal(err) - } - for _, i := range list.Items { - iout, err := yaml.Marshal(i) - if err != nil { - t.Fatal(err) - } - injectables := getInjectableYamlDocs(string(iout), t) - out = append(out, injectables...) - } - return out - default: - // No injectable parts. - return [][]byte{} - } -} - -func convertToJSON(i interface{}, t *testing.T) []byte { - t.Helper() - outputJSON, err := json.Marshal(i) - if err != nil { - t.Fatal(err) - } - return prettyJSON(outputJSON, t) -} - -func prettyJSON(inputJSON []byte, t *testing.T) []byte { - t.Helper() - // Pretty-print the JSON - var prettyBuffer bytes.Buffer - if err := json.Indent(&prettyBuffer, inputJSON, "", " "); err != nil { - t.Fatalf(err.Error()) - } - return prettyBuffer.Bytes() -} - -func applyJSONPatch(input, patch []byte, t *testing.T) []byte { - t.Helper() - p, err := jsonpatch.DecodePatch(patch) - if err != nil { - t.Fatal(err) - } - - patchedJSON, err := p.Apply(input) - if err != nil { - t.Fatal(err) - } - return prettyJSON(patchedJSON, t) -} - -func jsonToUnstructured(obj []byte, t *testing.T) *unstructured.Unstructured { - r := bytes.NewReader(obj) - decoder := k8syaml.NewYAMLOrJSONDecoder(r, 1024) - - out := &unstructured.Unstructured{} - err := decoder.Decode(out) - if err != nil { - t.Fatalf("error decoding object: %v", err) - } - return out -} - -func normalizeAndCompareDeployments(got, want *corev1.Pod, ignoreIstioMetaJSONAnnotationsEnv bool, t *testing.T) error { - t.Helper() - // Scrub unimportant fields that tend to differ. - delete(got.Annotations, annotation.SidecarStatus.Name) - delete(want.Annotations, annotation.SidecarStatus.Name) - - for _, c := range got.Spec.Containers { - for _, env := range c.Env { - if env.ValueFrom != nil { - env.ValueFrom.FieldRef.APIVersion = "" - } - // check if metajson is encoded correctly - if strings.HasPrefix(env.Name, "ISTIO_METAJSON_") { - var mm map[string]string - if err := json.Unmarshal([]byte(env.Value), &mm); err != nil { - t.Fatalf("unable to unmarshal %s: %v", env.Value, err) - } - } - } - } - - if ignoreIstioMetaJSONAnnotationsEnv { - removeContainerEnvEntry(got, "ISTIO_METAJSON_ANNOTATIONS") - removeContainerEnvEntry(want, "ISTIO_METAJSON_ANNOTATIONS") - } - - gotString, err := json.Marshal(got) - if err != nil { - t.Fatal(err) - } - wantString, err := json.Marshal(want) - if err != nil { - t.Fatal(err) - } - - return util.Compare(gotString, wantString) -} - -func removeContainerEnvEntry(pod *corev1.Pod, envVarName string) { - for i, c := range pod.Spec.Containers { - for j, v := range c.Env { - if v.Name == envVarName { - pod.Spec.Containers[i].Env = append(c.Env[:j], c.Env[j+1:]...) - break - } - } - } -} - -func makeTestData(t testing.TB, skip bool, apiVersion string) []byte { - t.Helper() - - pod := corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Annotations: map[string]string{}, - }, - Spec: corev1.PodSpec{ - Volumes: []corev1.Volume{{Name: "v0"}}, - InitContainers: []corev1.Container{{Name: "c0"}}, - Containers: []corev1.Container{{Name: "c1"}}, - ImagePullSecrets: []corev1.LocalObjectReference{{Name: "p0"}}, - }, - } - - if skip { - pod.ObjectMeta.Annotations[annotation.SidecarInject.Name] = "false" - } - - raw, err := json.Marshal(&pod) - if err != nil { - t.Fatalf("Could not create test pod: %v", err) - } - - review := v1beta1.AdmissionReview{ - TypeMeta: metav1.TypeMeta{ - Kind: "AdmissionReview", - APIVersion: fmt.Sprintf("admission.k8s.io/%s", apiVersion), - }, - Request: &v1beta1.AdmissionRequest{ - Kind: metav1.GroupVersionKind{ - Group: v1beta1.GroupName, - Version: apiVersion, - Kind: "AdmissionRequest", - }, - Object: runtime.RawExtension{ - Raw: raw, - }, - Operation: v1beta1.Create, - }, - } - reviewJSON, err := json.Marshal(review) - if err != nil { - t.Fatalf("Failed to create AdmissionReview: %v", err) - } - return reviewJSON -} - -func createWebhook(t testing.TB, cfg *Config, pcResources int) *Webhook { - t.Helper() - dir := t.TempDir() - - configBytes, err := yaml.Marshal(cfg) - if err != nil { - t.Fatalf("Could not marshal test injection config: %v", err) - } - _, values, _ := readInjectionSettings(t, "default") - var ( - configFile = filepath.Join(dir, "config-file.yaml") - valuesFile = filepath.Join(dir, "values-file.yaml") - port = 0 - ) - - if err := os.WriteFile(configFile, configBytes, 0o644); err != nil { // nolint: vetshadow - t.Fatalf("WriteFile(%v) failed: %v", configFile, err) - } - - if err := os.WriteFile(valuesFile, []byte(values.raw), 0o644); err != nil { // nolint: vetshadow - t.Fatalf("WriteFile(%v) failed: %v", valuesFile, err) - } - - // mesh config - m := mesh.DefaultMeshConfig() - store := model.NewFakeStore() - for i := 0; i < pcResources; i++ { - store.Create(newProxyConfig(fmt.Sprintf("pc-%d", i), "dubbo-system", &v1beta12.ProxyConfig{ - Concurrency: &wrapperspb.Int32Value{Value: int32(i % 5)}, - EnvironmentVariables: map[string]string{ - fmt.Sprintf("VAR_%d", i): fmt.Sprint(i), - }, - })) - } - pcs, _ := model.GetProxyConfigs(store, m) - env := model.Environment{ - Watcher: mesh.NewFixedWatcher(m), - PushContext: &model.PushContext{ - ProxyConfigs: pcs, - }, - ConfigStore: model.MakeIstioStore(store), - } - watcher, err := NewFileWatcher(configFile, valuesFile) - if err != nil { - t.Fatalf("NewFileWatcher() failed: %v", err) - } - wh, err := NewWebhook(WebhookParameters{ - Watcher: watcher, - Port: port, - Env: &env, - Mux: http.NewServeMux(), - }) - if err != nil { - t.Fatalf("NewWebhook() failed: %v", err) - } - return wh -} - -func TestRunAndServe(t *testing.T) { - // TODO: adjust the test to match prod defaults instead of fake defaults. - wh := createWebhook(t, minimalSidecarTemplate, 0) - stop := make(chan struct{}) - defer func() { close(stop) }() - wh.Run(stop) - - validReview := makeTestData(t, false, "v1beta1") - validReviewV1 := makeTestData(t, false, "v1") - skipReview := makeTestData(t, true, "v1beta1") - - // nolint: lll - validPatch := []byte(`[ -{ - "op": "add", - "path": "/metadata/annotations", - "value": { - "prometheus.io/path": "/stats/prometheus", - "prometheus.io/port": "15020", - "prometheus.io/scrape": "true", - "sidecar.istio.io/status": "{\"initContainers\":[\"istio-init\"],\"containers\":[\"istio-proxy\"],\"volumes\":[\"istio-envoy\"],\"imagePullSecrets\":[\"istio-image-pull-secrets\"],\"revision\":\"default\"}" - } -}, -{ - "op": "add", - "path": "/spec/volumes/1", - "value": { - "name": "v0" - } -}, -{ - "op": "replace", - "path": "/spec/volumes/0/name", - "value": "istio-envoy" -}, -{ - "op": "add", - "path": "/spec/initContainers/1", - "value": { - "name": "istio-init", - "resources": {} - } -}, -{ - "op": "add", - "path": "/spec/containers/1", - "value": { - "name": "istio-proxy", - "resources": {} - } -}, -{ - "op": "add", - "path": "/spec/imagePullSecrets/1", - "value": { - "name": "p0" - } -}, -{ - "op": "replace", - "path": "/spec/imagePullSecrets/0/name", - "value": "istio-image-pull-secrets" -} -]`) - - cases := []struct { - name string - body []byte - contentType string - wantAllowed bool - wantStatusCode int - wantPatch []byte - }{ - { - name: "valid", - body: validReview, - contentType: "application/json", - wantAllowed: true, - wantStatusCode: http.StatusOK, - wantPatch: validPatch, - }, - { - name: "valid(v1 version)", - body: validReviewV1, - contentType: "application/json", - wantAllowed: true, - wantStatusCode: http.StatusOK, - wantPatch: validPatch, - }, - { - name: "skipped", - body: skipReview, - contentType: "application/json", - wantAllowed: true, - wantStatusCode: http.StatusOK, - }, - { - name: "wrong content-type", - body: validReview, - contentType: "application/yaml", - wantAllowed: false, - wantStatusCode: http.StatusUnsupportedMediaType, - }, - { - name: "bad content", - body: []byte{0, 1, 2, 3, 4, 5}, // random data - contentType: "application/json", - wantAllowed: false, - wantStatusCode: http.StatusOK, - }, - { - name: "missing body", - contentType: "application/json", - wantAllowed: false, - wantStatusCode: http.StatusBadRequest, - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("[%d] %s", i, c.name), func(t *testing.T) { - req := httptest.NewRequest("POST", "http://sidecar-injector/inject", bytes.NewReader(c.body)) - req.Header.Add("Content-Type", c.contentType) - - w := httptest.NewRecorder() - wh.serveInject(w, req) - res := w.Result() - - if res.StatusCode != c.wantStatusCode { - t.Fatalf("wrong status code: \ngot %v \nwant %v", res.StatusCode, c.wantStatusCode) - } - - if res.StatusCode != http.StatusOK { - return - } - - gotBody, err := io.ReadAll(res.Body) - if err != nil { - t.Fatalf("could not read body: %v", err) - } - var gotReview v1beta1.AdmissionReview - if err := json.Unmarshal(gotBody, &gotReview); err != nil { - t.Fatalf("could not decode response body: %v", err) - } - if gotReview.Response.Allowed != c.wantAllowed { - t.Fatalf("AdmissionReview.Response.Allowed is wrong : got %v want %v", - gotReview.Response.Allowed, c.wantAllowed) - } - - var gotPatch bytes.Buffer - if len(gotReview.Response.Patch) > 0 { - if err := json.Compact(&gotPatch, gotReview.Response.Patch); err != nil { - t.Fatalf(err.Error()) - } - } - var wantPatch bytes.Buffer - if len(c.wantPatch) > 0 { - if err := json.Compact(&wantPatch, c.wantPatch); err != nil { - t.Fatalf(err.Error()) - } - } - - if !bytes.Equal(gotPatch.Bytes(), wantPatch.Bytes()) { - t.Fatalf("got bad patch: \n got %v \n want %v", gotPatch.String(), wantPatch.String()) - } - }) - } - // Now Validate that metrics are created. - testSideCarInjectorMetrics(t) -} - -func testSideCarInjectorMetrics(t *testing.T) { - expected := []string{ - "sidecar_injection_requests_total", - "sidecar_injection_success_total", - "sidecar_injection_skip_total", - "sidecar_injection_failure_total", - } - for _, e := range expected { - retry.UntilSuccessOrFail(t, func() error { - got, err := sutil.GetMetricsCounterValueWithTags(e, nil) - if err != nil { - return err - } - if got <= 0 { - return fmt.Errorf("metric empty") - } - return nil - }) - } -} - -func benchmarkInjectServe(pcs int, b *testing.B) { - sidecarTemplate, _, _ := readInjectionSettings(b, "default") - wh := createWebhook(b, sidecarTemplate, pcs) - - stop := make(chan struct{}) - defer func() { close(stop) }() - wh.Run(stop) - - body := makeTestData(b, false, "v1beta1") - - b.ResetTimer() - for i := 0; i < b.N; i++ { - req := httptest.NewRequest("POST", "http://sidecar-injector/inject", bytes.NewReader(body)) - req.Header.Add("Content-Type", "application/json") - - wh.serveInject(httptest.NewRecorder(), req) - } -} - -func BenchmarkInjectServePC0(b *testing.B) { - benchmarkInjectServe(0, b) -} - -func BenchmarkInjectServePC5(b *testing.B) { - benchmarkInjectServe(5, b) -} - -func BenchmarkInjectServePC15(b *testing.B) { - benchmarkInjectServe(15, b) -} - -func TestEnablePrometheusAggregation(t *testing.T) { - tests := []struct { - name string - mesh *meshconfig.MeshConfig - anno map[string]string - want bool - }{ - { - "no settings", - nil, - nil, - true, - }, - { - "mesh on", - &meshconfig.MeshConfig{EnablePrometheusMerge: &wrapperspb.BoolValue{Value: true}}, - nil, - true, - }, - { - "mesh off", - &meshconfig.MeshConfig{EnablePrometheusMerge: &wrapperspb.BoolValue{Value: false}}, - nil, - false, - }, - { - "annotation on", - &meshconfig.MeshConfig{EnablePrometheusMerge: &wrapperspb.BoolValue{Value: false}}, - map[string]string{annotation.PrometheusMergeMetrics.Name: "true"}, - true, - }, - { - "annotation off", - &meshconfig.MeshConfig{EnablePrometheusMerge: &wrapperspb.BoolValue{Value: true}}, - map[string]string{annotation.PrometheusMergeMetrics.Name: "false"}, - false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := enablePrometheusMerge(tt.mesh, tt.anno); got != tt.want { - t.Errorf("enablePrometheusMerge() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestParseInjectEnvs(t *testing.T) { - cases := []struct { - name string - in string - want map[string]string - }{ - { - name: "empty", - in: "/", - want: map[string]string{}, - }, - { - name: "no-kv", - in: "/inject", - want: map[string]string{}, - }, - { - name: "no-kv-with-tail", - in: "/inject/", - want: map[string]string{}, - }, - { - name: "one-kv", - in: "/inject/cluster/cluster1", - want: map[string]string{"ISTIO_META_CLUSTER_ID": "cluster1"}, - }, - { - name: "two-kv", - in: "/inject/cluster/cluster1/net/network1/", - want: map[string]string{"ISTIO_META_CLUSTER_ID": "cluster1", "ISTIO_META_NETWORK": "network1"}, - }, - { - name: "not-predefined-kv", - in: "/inject/cluster/cluster1/custom_env/foo", - want: map[string]string{"ISTIO_META_CLUSTER_ID": "cluster1", "CUSTOM_ENV": "foo"}, - }, - { - name: "one-key-without-value", - in: "/inject/cluster", - want: map[string]string{}, - }, - { - name: "key-without-value", - in: "/inject/cluster/cluster1/network", - want: map[string]string{"ISTIO_META_CLUSTER_ID": "cluster1"}, - }, - { - name: "key-with-values-contain-slashes", - in: "/inject/:ENV:cluster=cluster2:ENV:rootpage=/foo/bar", - want: map[string]string{"ISTIO_META_CLUSTER_ID": "cluster2", "ROOTPAGE": "/foo/bar"}, - }, - { - // this is to test the path not following :ENV: format, the - // path will be considered using slash as separator - name: "no-predefined-kv-with-values-contain-ENV-separator", - in: "/inject/rootpage1/value1/rootpage2/:ENV:abcd=efgh", - want: map[string]string{"ROOTPAGE1": "value1", "ROOTPAGE2": ":ENV:abcd=efgh"}, - }, - { - // this is to test the path following :ENV: format, but two variables - // do not have correct format, thus they will be ignored. Eg. :ENV:=abb - // :ENV:=, these two are not correct variables. - name: "no-predefined-kv-with-values-contain-ENV-separator-invalid-format", - in: "/inject/:ENV:rootpage1=efgh:ENV:=abb:ENV:=", - want: map[string]string{"ROOTPAGE1": "efgh"}, - }, - { - // this is to test that the path is an url encoded string, still using - // slash as separators - name: "no-predefined-kv-with-mixed-values", - in: func() string { - req, _ := http.NewRequest("GET", - "%2Finject%2Frootpage1%2Ffoo%2Frootpage2%2Fbar", nil) - return req.URL.Path - }(), - want: map[string]string{"ROOTPAGE1": "foo", "ROOTPAGE2": "bar"}, - }, - { - // this is to test that the path is an url encoded string and :ENV: as separator - // eg. /inject/:ENV:rootpage1=/foo/bar:ENV:rootpage2=/bar/toe but url encoded. - name: "no-predefined-kv-with-slashes", - in: func() string { - req, _ := http.NewRequest("GET", - "%2Finject%2F%3AENV%3Arootpage1%3D%2Ffoo%2Fbar%3AENV%3Arootpage2%3D%2Fbar%2Ftoe", nil) - return req.URL.Path - }(), - want: map[string]string{"ROOTPAGE1": "/foo/bar", "ROOTPAGE2": "/bar/toe"}, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - actual := parseInjectEnvs(tc.in) - if !reflect.DeepEqual(actual, tc.want) { - t.Fatalf("Expected result %#v, but got %#v", tc.want, actual) - } - }) - } -} - -func newProxyConfig(name, ns string, spec config.Spec) config.Config { - return config.Config{ - Meta: config.Meta{ - GroupVersionKind: gvk.ProxyConfig, - Name: name, - Namespace: ns, - }, - Spec: spec, - } -} - -// defaultInstallPackageDir returns a path to a snapshot of the helm charts used for testing. -func defaultInstallPackageDir() string { - wd, err := os.Getwd() - if err != nil { - panic(err) - } - return filepath.Join(wd, "../../../manifests/") -} diff --git a/pkg/kube/labels/labels.go b/pkg/kube/labels/labels.go deleted file mode 100644 index 57943eb37..000000000 --- a/pkg/kube/labels/labels.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package labels provides utility methods for retrieving Istio-specific labels -// from Kubernetes resources. -package labels - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" -) - -// CanonicalService returns the values of the following labels from the supplied map: -// - service.istio.io/canonical-name -// - service.istio.io/canonical-revision -// -// If the labels are not in the map, a set of fallbacks are checked. For canonical name, -// `app.kubernetes.io/name` is checked, then `app`, evenutually falling back to the -// supplied `workloadName`. For canonical revision, `app.kubernetes.io/version` is checked, -// followed by `version` and finally defaulting to the literal value of `"latest"`. -func CanonicalService(labels map[string]string, workloadName string) (string, string) { - return canonicalServiceName(labels, workloadName), canonicalServiceRevision(labels) -} - -func canonicalServiceRevision(labels map[string]string) string { - if rev, ok := labels[model.IstioCanonicalServiceRevisionLabelName]; ok { - return rev - } - - if rev, ok := labels["app.kubernetes.io/version"]; ok { - return rev - } - - if rev, ok := labels["version"]; ok { - return rev - } - - return "latest" -} - -func canonicalServiceName(labels map[string]string, workloadName string) string { - if svc, ok := labels[model.IstioCanonicalServiceLabelName]; ok { - return svc - } - - if svc, ok := labels["app.kubernetes.io/name"]; ok { - return svc - } - - if svc, ok := labels["app"]; ok { - return svc - } - - return workloadName -} diff --git a/pkg/kube/mcs/register.go b/pkg/kube/mcs/register.go deleted file mode 100644 index b95e98f59..000000000 --- a/pkg/kube/mcs/register.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mcs - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - mcs "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" -) - -var ( - schemeBuilder = &runtime.SchemeBuilder{} - - // AddToScheme is used to register MCS CRDs to a runtime.Scheme - AddToScheme = schemeBuilder.AddToScheme - - // MCSSchemeGroupVersion is group version used to register Kubernetes Multi-Cluster Services (MCS) objects - MCSSchemeGroupVersion = schema.GroupVersion{Group: features.MCSAPIGroup, Version: features.MCSAPIVersion} - - ServiceExportGVR = MCSSchemeGroupVersion.WithResource("serviceexports") - ServiceImportGVR = MCSSchemeGroupVersion.WithResource("serviceimports") -) - -func init() { - schemeBuilder.Register(addKnownTypes) -} - -func addKnownTypes(scheme *runtime.Scheme) error { - // Register Kubernetes Multi-Cluster Services (MCS) objects. - scheme.AddKnownTypes(MCSSchemeGroupVersion, - &mcs.ServiceExport{}, - &mcs.ServiceExportList{}, - &mcs.ServiceImport{}, - &mcs.ServiceImportList{}) - metav1.AddToGroupVersion(scheme, MCSSchemeGroupVersion) - - return nil -} diff --git a/pkg/kube/mock_client.go b/pkg/kube/mock_client.go deleted file mode 100644 index 6ee92ca3b..000000000 --- a/pkg/kube/mock_client.go +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "context" - "fmt" - "net/http" -) - -import ( - "google.golang.org/grpc/credentials" - istioclient "istio.io/client-go/pkg/clientset/versioned" - istioinformer "istio.io/client-go/pkg/informers/externalversions" - "istio.io/pkg/version" - v1 "k8s.io/api/core/v1" - "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" - kubeExtInformers "k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions" - kubeVersion "k8s.io/apimachinery/pkg/version" - "k8s.io/cli-runtime/pkg/resource" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/dynamic/dynamicinformer" - "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/metadata" - "k8s.io/client-go/metadata/metadatainformer" - "k8s.io/client-go/rest" - "k8s.io/client-go/rest/fake" - cmdtesting "k8s.io/kubectl/pkg/cmd/testing" - "k8s.io/kubectl/pkg/cmd/util" - serviceapisclient "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned" - serviceapisinformer "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions" - mcsapisclient "sigs.k8s.io/mcs-api/pkg/client/clientset/versioned" - mcsapisinformer "sigs.k8s.io/mcs-api/pkg/client/informers/externalversions" -) - -var _ ExtendedClient = MockClient{} - -type MockPortForwarder struct{} - -func (m MockPortForwarder) Start() error { - return nil -} - -func (m MockPortForwarder) Address() string { - return "localhost:3456" -} - -func (m MockPortForwarder) Close() { -} - -func (m MockPortForwarder) WaitForStop() { -} - -var _ PortForwarder = MockPortForwarder{} - -// MockClient for tests that rely on kube.Client. -type MockClient struct { - kubernetes.Interface - RestClient *rest.RESTClient - // Results is a map of podName to the results of the expected test on the pod - Results map[string][]byte - DiscoverablePods map[string]map[string]*v1.PodList - RevisionValue string - ConfigValue *rest.Config - IstioVersions *version.MeshInfo - KubernetesVersion uint - IstiodVersion string -} - -func (c MockClient) ExtInformer() kubeExtInformers.SharedInformerFactory { - panic("not used in mock") -} - -func (c MockClient) Istio() istioclient.Interface { - panic("not used in mock") -} - -func (c MockClient) GatewayAPI() serviceapisclient.Interface { - panic("not used in mock") -} - -func (c MockClient) MCSApis() mcsapisclient.Interface { - panic("not used in mock") -} - -func (c MockClient) IstioInformer() istioinformer.SharedInformerFactory { - panic("not used in mock") -} - -func (c MockClient) GatewayAPIInformer() serviceapisinformer.SharedInformerFactory { - panic("not used in mock") -} - -func (c MockClient) MCSApisInformer() mcsapisinformer.SharedInformerFactory { - panic("not used in mock") -} - -func (c MockClient) Metadata() metadata.Interface { - panic("not used in mock") -} - -func (c MockClient) KubeInformer() informers.SharedInformerFactory { - panic("not used in mock") -} - -func (c MockClient) DynamicInformer() dynamicinformer.DynamicSharedInformerFactory { - panic("not used in mock") -} - -func (c MockClient) MetadataInformer() metadatainformer.SharedInformerFactory { - panic("not used in mock") -} - -func (c MockClient) RunAndWait(stop <-chan struct{}) { - panic("not used in mock") -} - -func (c MockClient) Kube() kubernetes.Interface { - return c.Interface -} - -func (c MockClient) DynamicClient() dynamic.Interface { - panic("not used in mock") -} - -func (c MockClient) MetadataClient() metadata.Interface { - panic("not used in mock") -} - -func (c MockClient) AllDiscoveryDo(_ context.Context, _, _ string) (map[string][]byte, error) { - return c.Results, nil -} - -func (c MockClient) EnvoyDo(ctx context.Context, podName, podNamespace, method, path string) ([]byte, error) { - results, ok := c.Results[podName] - if !ok { - return nil, fmt.Errorf("unable to retrieve Pod: pods %q not found", podName) - } - return results, nil -} - -func (c MockClient) EnvoyDoWithPort(ctx context.Context, podName, podNamespace, method, path string, port int) ([]byte, error) { - results, ok := c.Results[podName] - if !ok { - return nil, fmt.Errorf("unable to retrieve Pod: pods %q not found", podName) - } - return results, nil -} - -func (c MockClient) RESTConfig() *rest.Config { - return c.ConfigValue -} - -func (c MockClient) GetIstioVersions(_ context.Context, _ string) (*version.MeshInfo, error) { - if c.IstiodVersion != "" { - server := version.BuildInfo{} - setServerInfoWithIstiodVersionInfo(&server, c.IstiodVersion) - return &version.MeshInfo{ - { - Info: server, - }, - }, nil - } - return c.IstioVersions, nil -} - -func (c MockClient) PodsForSelector(_ context.Context, namespace string, labelSelectors ...string) (*v1.PodList, error) { - podsForNamespace, ok := c.DiscoverablePods[namespace] - if !ok { - return &v1.PodList{}, nil - } - var allPods v1.PodList - for _, selector := range labelSelectors { - pods, ok := podsForNamespace[selector] - if !ok { - return &v1.PodList{}, nil - } - allPods.TypeMeta = pods.TypeMeta - allPods.ListMeta = pods.ListMeta - allPods.Items = append(allPods.Items, pods.Items...) - } - return &allPods, nil -} - -func (c MockClient) Revision() string { - return c.RevisionValue -} - -func (c MockClient) REST() rest.Interface { - panic("not implemented by mock") -} - -func (c MockClient) ApplyYAMLFiles(string, ...string) error { - return nil -} - -func (c MockClient) ApplyYAMLFilesDryRun(string, ...string) error { - panic("not implemented by mock") -} - -// CreatePerRPCCredentials -- when implemented -- mocks per-RPC credentials (bearer token) -func (c MockClient) CreatePerRPCCredentials(ctx context.Context, tokenNamespace, tokenServiceAccount string, audiences []string, - expirationSeconds int64) (credentials.PerRPCCredentials, error) { - panic("not implemented by mock") -} - -func (c MockClient) DeleteYAMLFiles(string, ...string) error { - panic("not implemented by mock") -} - -func (c MockClient) DeleteYAMLFilesDryRun(string, ...string) error { - panic("not implemented by mock") -} - -func (c MockClient) Ext() clientset.Interface { - panic("not implemented by mock") -} - -func (c MockClient) Dynamic() dynamic.Interface { - panic("not implemented by mock") -} - -func (c MockClient) GetKubernetesVersion() (*kubeVersion.Info, error) { - minor := fmt.Sprint(c.KubernetesVersion) - if c.KubernetesVersion == 0 { - minor = "16" - } - return &kubeVersion.Info{ - Major: "1", - Minor: minor, - }, nil -} - -func (c MockClient) GetIstioPods(_ context.Context, _ string, _ map[string]string) ([]v1.Pod, error) { - return nil, fmt.Errorf("TODO MockClient doesn't implement IstioPods") -} - -func (c MockClient) PodExecCommands(podName, podNamespace, container string, commands []string) (stdout string, stderr string, err error) { - return "", "", fmt.Errorf("TODO MockClient doesn't implement exec") -} - -func (c MockClient) PodExec(_, _, _ string, _ string) (string, string, error) { - return "", "", fmt.Errorf("TODO MockClient doesn't implement exec") -} - -func (c MockClient) PodLogs(_ context.Context, _ string, _ string, _ string, _ bool) (string, error) { - return "", fmt.Errorf("TODO MockClient doesn't implement logs") -} - -func (c MockClient) NewPortForwarder(_, _, _ string, _, _ int) (PortForwarder, error) { - return MockPortForwarder{}, nil -} - -// UtilFactory mock's kubectl's utility factory. This code sets up a fake factory, -// similar to the one in https://github.com/kubernetes/kubectl/blob/master/pkg/cmd/describe/describe_test.go -func (c MockClient) UtilFactory() util.Factory { - tf := cmdtesting.NewTestFactory() - _, _, codec := cmdtesting.NewExternalScheme() - tf.UnstructuredClient = &fake.RESTClient{ - NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, - Resp: &http.Response{ - StatusCode: http.StatusOK, - Header: cmdtesting.DefaultHeader(), - Body: cmdtesting.ObjBody(codec, - cmdtesting.NewInternalType("", "", "foo")), - }, - } - return tf -} diff --git a/pkg/kube/multicluster/secretcontroller.go b/pkg/kube/multicluster/secretcontroller.go deleted file mode 100644 index 8352e9f27..000000000 --- a/pkg/kube/multicluster/secretcontroller.go +++ /dev/null @@ -1,602 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package multicluster - -import ( - "bytes" - "context" - "crypto/sha256" - "errors" - "fmt" - "sync" - "time" -) - -import ( - "github.com/hashicorp/go-multierror" - "go.uber.org/atomic" - "istio.io/pkg/log" - "istio.io/pkg/monitoring" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/tools/clientcmd/api" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/controllers" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -const ( - MultiClusterSecretLabel = "istio/multiCluster" -) - -func init() { - monitoring.MustRegister(timeouts) - monitoring.MustRegister(clustersCount) -} - -var ( - timeouts = monitoring.NewSum( - "remote_cluster_sync_timeouts_total", - "Number of times remote clusters took too long to sync, causing slow startup that excludes remote clusters.", - ) - - clusterType = monitoring.MustCreateLabel("cluster_type") - - clustersCount = monitoring.NewGauge( - "istiod_managed_clusters", - "Number of clusters managed by istiod", - monitoring.WithLabels(clusterType), - ) - - localClusters = clustersCount.With(clusterType.Value("local")) - remoteClusters = clustersCount.With(clusterType.Value("remote")) -) - -type ClusterHandler interface { - ClusterAdded(cluster *Cluster, stop <-chan struct{}) error - ClusterUpdated(cluster *Cluster, stop <-chan struct{}) error - ClusterDeleted(clusterID cluster.ID) error -} - -// Controller is the controller implementation for Secret resources -type Controller struct { - namespace string - localClusterID cluster.ID - localClusterClient kube.Client - queue controllers.Queue - informer cache.SharedIndexInformer - - cs *ClusterStore - - handlers []ClusterHandler - - once sync.Once - syncInterval time.Duration - remoteSyncTimeout atomic.Bool -} - -// Cluster defines cluster struct -type Cluster struct { - // ID of the cluster. - ID cluster.ID - // SyncTimeout is marked after features.RemoteClusterTimeout. - SyncTimeout *atomic.Bool - // Client for accessing the cluster. - Client kube.Client - - kubeConfigSha [sha256.Size]byte - - stop chan struct{} - // initialSync is marked when RunAndWait completes - initialSync *atomic.Bool -} - -// Stop channel which is closed when the cluster is removed or the Controller that created the client is stopped. -// Client.RunAndWait is called using this channel. -func (r *Cluster) Stop() <-chan struct{} { - return r.stop -} - -func (c *Controller) AddHandler(h ClusterHandler) { - log.Infof("handling remote clusters in %T", h) - c.handlers = append(c.handlers, h) -} - -// Run starts the cluster's informers and waits for caches to sync. Once caches are synced, we mark the cluster synced. -// This should be called after each of the handlers have registered informers, and should be run in a goroutine. -func (r *Cluster) Run() { - r.Client.RunAndWait(r.Stop()) - r.initialSync.Store(true) -} - -func (r *Cluster) HasSynced() bool { - return r.initialSync.Load() || r.SyncTimeout.Load() -} - -func (r *Cluster) SyncDidTimeout() bool { - return r.SyncTimeout.Load() && !r.HasSynced() -} - -// ClusterStore is a collection of clusters -type ClusterStore struct { - sync.RWMutex - // keyed by secret key(ns/name)->clusterID - remoteClusters map[string]map[cluster.ID]*Cluster - clusters sets.Set -} - -// newClustersStore initializes data struct to store clusters information -func newClustersStore() *ClusterStore { - return &ClusterStore{ - remoteClusters: make(map[string]map[cluster.ID]*Cluster), - clusters: sets.New(), - } -} - -func (c *ClusterStore) Store(secretKey string, clusterID cluster.ID, value *Cluster) { - c.Lock() - defer c.Unlock() - if _, ok := c.remoteClusters[secretKey]; !ok { - c.remoteClusters[secretKey] = make(map[cluster.ID]*Cluster) - } - c.remoteClusters[secretKey][clusterID] = value - c.clusters.Insert(string(clusterID)) -} - -func (c *ClusterStore) Delete(secretKey string, clusterID cluster.ID) { - c.Lock() - defer c.Unlock() - delete(c.remoteClusters[secretKey], clusterID) - c.clusters.Delete(string(clusterID)) - if len(c.remoteClusters[secretKey]) == 0 { - delete(c.remoteClusters, secretKey) - } -} - -func (c *ClusterStore) Get(secretKey string, clusterID cluster.ID) *Cluster { - c.RLock() - defer c.RUnlock() - if _, ok := c.remoteClusters[secretKey]; !ok { - return nil - } - return c.remoteClusters[secretKey][clusterID] -} - -func (c *ClusterStore) Contains(clusterID cluster.ID) bool { - c.RLock() - defer c.RUnlock() - return c.clusters.Contains(string(clusterID)) -} - -func (c *ClusterStore) GetByID(clusterID cluster.ID) *Cluster { - c.RLock() - defer c.RUnlock() - for _, clusters := range c.remoteClusters { - c, ok := clusters[clusterID] - if ok { - return c - } - } - return nil -} - -// All returns a copy of the current remote clusters. -func (c *ClusterStore) All() map[string]map[cluster.ID]*Cluster { - if c == nil { - return nil - } - c.RLock() - defer c.RUnlock() - out := make(map[string]map[cluster.ID]*Cluster, len(c.remoteClusters)) - for secret, clusters := range c.remoteClusters { - out[secret] = make(map[cluster.ID]*Cluster, len(clusters)) - for cid, c := range clusters { - outCluster := *c - out[secret][cid] = &outCluster - } - } - return out -} - -// GetExistingClustersFor return existing clusters registered for the given secret -func (c *ClusterStore) GetExistingClustersFor(secretKey string) []*Cluster { - c.RLock() - defer c.RUnlock() - out := make([]*Cluster, 0, len(c.remoteClusters[secretKey])) - for _, cluster := range c.remoteClusters[secretKey] { - out = append(out, cluster) - } - return out -} - -func (c *ClusterStore) Len() int { - c.Lock() - defer c.Unlock() - out := 0 - for _, clusterMap := range c.remoteClusters { - out += len(clusterMap) - } - return out -} - -// NewController returns a new secret controller -func NewController(kubeclientset kube.Client, namespace string, localClusterID cluster.ID) *Controller { - secretsInformer := cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) { - opts.LabelSelector = MultiClusterSecretLabel + "=true" - return kubeclientset.CoreV1().Secrets(namespace).List(context.TODO(), opts) - }, - WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) { - opts.LabelSelector = MultiClusterSecretLabel + "=true" - return kubeclientset.CoreV1().Secrets(namespace).Watch(context.TODO(), opts) - }, - }, - &corev1.Secret{}, 0, cache.Indexers{}, - ) - - // init gauges - localClusters.Record(1.0) - remoteClusters.Record(0.0) - - controller := &Controller{ - namespace: namespace, - localClusterID: localClusterID, - localClusterClient: kubeclientset, - cs: newClustersStore(), - informer: secretsInformer, - syncInterval: 100 * time.Millisecond, - } - controller.queue = controllers.NewQueue("multicluster secret", controllers.WithReconciler(controller.processItem)) - - secretsInformer.AddEventHandler(controllers.ObjectHandler(controller.queue.AddObject)) - return controller -} - -// Run starts the controller until it receives a message over stopCh -func (c *Controller) Run(stopCh <-chan struct{}) error { - // run handlers for the local cluster; do not store this *Cluster in the ClusterStore or give it a SyncTimeout - // this is done outside the goroutine, we should block other Run/startFuncs until this is registered - localCluster := &Cluster{Client: c.localClusterClient, ID: c.localClusterID} - if err := c.handleAdd(localCluster, stopCh); err != nil { - return fmt.Errorf("failed initializing local cluster %s: %v", c.localClusterID, err) - } - go func() { - t0 := time.Now() - log.Info("Starting multicluster remote secrets controller") - - go c.informer.Run(stopCh) - - if !kube.WaitForCacheSyncInterval(stopCh, c.syncInterval, c.informer.HasSynced) { - log.Error("Failed to sync multicluster remote secrets controller cache") - return - } - log.Infof("multicluster remote secrets controller cache synced in %v", time.Since(t0)) - if features.RemoteClusterTimeout != 0 { - time.AfterFunc(features.RemoteClusterTimeout, func() { - c.remoteSyncTimeout.Store(true) - }) - } - c.queue.Run(stopCh) - }() - return nil -} - -func (c *Controller) hasSynced() bool { - if !c.queue.HasSynced() { - log.Debug("secret controller did not sync secrets presented at startup") - // we haven't finished processing the secrets that were present at startup - return false - } - c.cs.RLock() - defer c.cs.RUnlock() - for _, clusterMap := range c.cs.remoteClusters { - for _, cluster := range clusterMap { - if !cluster.HasSynced() { - log.Debugf("remote cluster %s registered informers have not been synced up yet", cluster.ID) - return false - } - } - } - - return true -} - -func (c *Controller) HasSynced() bool { - synced := c.hasSynced() - if synced { - return true - } - if c.remoteSyncTimeout.Load() { - c.once.Do(func() { - log.Errorf("remote clusters failed to sync after %v", features.RemoteClusterTimeout) - timeouts.Increment() - }) - return true - } - - return synced -} - -func (c *Controller) processItem(key types.NamespacedName) error { - log.Infof("processing secret event for secret %s", key) - obj, exists, err := c.informer.GetIndexer().GetByKey(key.String()) - if err != nil { - return fmt.Errorf("error fetching object %s error: %v", key, err) - } - if exists { - log.Debugf("secret %s exists in informer cache, processing it", key) - c.addSecret(key, obj.(*corev1.Secret)) - } else { - log.Debugf("secret %s does not exist in informer cache, deleting it", key) - c.deleteSecret(key.String()) - } - remoteClusters.Record(float64(c.cs.Len())) - - return nil -} - -// BuildClientsFromConfig creates kube.Clients from the provided kubeconfig. This is overridden for testing only -var BuildClientsFromConfig = func(kubeConfig []byte) (kube.Client, error) { - if len(kubeConfig) == 0 { - return nil, errors.New("kubeconfig is empty") - } - - rawConfig, err := clientcmd.Load(kubeConfig) - if err != nil { - return nil, fmt.Errorf("kubeconfig cannot be loaded: %v", err) - } - - if err := clientcmd.Validate(*rawConfig); err != nil { - return nil, fmt.Errorf("kubeconfig is not valid: %v", err) - } - if err := sanitizeKubeConfig(*rawConfig, features.InsecureKubeConfigOptions); err != nil { - return nil, fmt.Errorf("kubeconfig is not allowed: %v", err) - } - - clientConfig := clientcmd.NewDefaultClientConfig(*rawConfig, &clientcmd.ConfigOverrides{}) - - clients, err := kube.NewClient(clientConfig) - if err != nil { - return nil, fmt.Errorf("failed to create kube clients: %v", err) - } - return clients, nil -} - -// sanitizeKubeConfig sanitizes a kubeconfig file to strip out insecure settings which may leak -// confidential materials. -// See https://github.com/kubernetes/kubectl/issues/697 -func sanitizeKubeConfig(config api.Config, allowlist sets.Set) error { - for k, auths := range config.AuthInfos { - if ap := auths.AuthProvider; ap != nil { - // We currently are importing 5 authenticators: gcp, azure, exec, and openstack - switch ap.Name { - case "oidc": - // OIDC is safe as it doesn't read files or execute code. - // create-remote-secret specifically supports OIDC so its probably important to not break this. - default: - if !allowlist.Contains(ap.Name) { - // All the others - gcp, azure, exec, and openstack - are unsafe - return fmt.Errorf("auth provider %s is not allowed", ap.Name) - } - } - } - if auths.ClientKey != "" && !allowlist.Contains("clientKey") { - return fmt.Errorf("clientKey is not allowed") - } - if auths.ClientCertificate != "" && !allowlist.Contains("clientCertificate") { - return fmt.Errorf("clientCertificate is not allowed") - } - if auths.TokenFile != "" && !allowlist.Contains("tokenFile") { - return fmt.Errorf("tokenFile is not allowed") - } - if auths.Exec != nil && !allowlist.Contains("exec") { - return fmt.Errorf("exec is not allowed") - } - // Reconstruct the AuthInfo so if a new field is added we will not include it without review - config.AuthInfos[k] = &api.AuthInfo{ - // LocationOfOrigin: Not needed - ClientCertificate: auths.ClientCertificate, - ClientCertificateData: auths.ClientCertificateData, - ClientKey: auths.ClientKey, - ClientKeyData: auths.ClientKeyData, - Token: auths.Token, - TokenFile: auths.TokenFile, - Impersonate: auths.Impersonate, - ImpersonateGroups: auths.ImpersonateGroups, - ImpersonateUserExtra: auths.ImpersonateUserExtra, - Username: auths.Username, - Password: auths.Password, - AuthProvider: auths.AuthProvider, // Included because it is sanitized above - Exec: auths.Exec, - // Extensions: Not needed, - } - - // Other relevant fields that are not acted on: - // * Cluster.Server (and ProxyURL). This allows the user to send requests to arbitrary URLs, enabling potential SSRF attacks. - // However, we don't actually know what valid URLs are, so we cannot reasonably constrain this. Instead, - // we try to limit what confidential information could be exfiltrated (from AuthInfo). Additionally, the user cannot control - // the paths we send requests to, limiting potential attack scope. - // * Cluster.CertificateAuthority. While this reads from files, the result is not attached to the request and is instead - // entirely local - } - return nil -} - -func (c *Controller) createRemoteCluster(kubeConfig []byte, clusterID string) (*Cluster, error) { - clients, err := BuildClientsFromConfig(kubeConfig) - if err != nil { - return nil, err - } - return &Cluster{ - ID: cluster.ID(clusterID), - Client: clients, - stop: make(chan struct{}), - // for use inside the package, to close on cleanup - initialSync: atomic.NewBool(false), - SyncTimeout: &c.remoteSyncTimeout, - kubeConfigSha: sha256.Sum256(kubeConfig), - }, nil -} - -func (c *Controller) addSecret(name types.NamespacedName, s *corev1.Secret) { - secretKey := name.String() - // First delete clusters - existingClusters := c.cs.GetExistingClustersFor(secretKey) - for _, existingCluster := range existingClusters { - if _, ok := s.Data[string(existingCluster.ID)]; !ok { - c.deleteCluster(secretKey, existingCluster.ID) - } - } - - for clusterID, kubeConfig := range s.Data { - if cluster.ID(clusterID) == c.localClusterID { - log.Infof("ignoring cluster %v from secret %v as it would overwrite the local cluster", clusterID, secretKey) - continue - } - - action, callback := "Adding", c.handleAdd - if prev := c.cs.Get(secretKey, cluster.ID(clusterID)); prev != nil { - action, callback = "Updating", c.handleUpdate - // clusterID must be unique even across multiple secrets - kubeConfigSha := sha256.Sum256(kubeConfig) - if bytes.Equal(kubeConfigSha[:], prev.kubeConfigSha[:]) { - log.Infof("skipping update of cluster_id=%v from secret=%v: (kubeconfig are identical)", clusterID, secretKey) - continue - } - // stop previous remote cluster - prev.Stop() - } else if c.cs.Contains(cluster.ID(clusterID)) { - // if the cluster has been registered before by another secret, ignore the new one. - log.Warnf("cluster %d from secret %s has already been registered", clusterID, secretKey) - continue - } - log.Infof("%s cluster %v from secret %v", action, clusterID, secretKey) - - remoteCluster, err := c.createRemoteCluster(kubeConfig, clusterID) - if err != nil { - log.Errorf("%s cluster_id=%v from secret=%v: %v", action, clusterID, secretKey, err) - continue - } - c.cs.Store(secretKey, remoteCluster.ID, remoteCluster) - if err := callback(remoteCluster, remoteCluster.stop); err != nil { - log.Errorf("%s cluster_id from secret=%v: %s %v", action, clusterID, secretKey, err) - continue - } - log.Infof("finished callback for %s and starting to sync", clusterID) - go remoteCluster.Run() - } - - log.Infof("Number of remote clusters: %d", c.cs.Len()) -} - -func (c *Controller) deleteSecret(secretKey string) { - for _, cluster := range c.cs.GetExistingClustersFor(secretKey) { - if cluster.ID == c.localClusterID { - log.Infof("ignoring delete cluster %v from secret %v as it would overwrite the local cluster", c.localClusterID, secretKey) - continue - } - log.Infof("Deleting cluster_id=%v configured by secret=%v", cluster.ID, secretKey) - close(cluster.stop) - err := c.handleDelete(cluster.ID) - if err != nil { - log.Errorf("Error removing cluster_id=%v configured by secret=%v: %v", - cluster.ID, secretKey, err) - } - c.cs.Delete(secretKey, cluster.ID) - } - - log.Infof("Number of remote clusters: %d", c.cs.Len()) -} - -func (c *Controller) deleteCluster(secretKey string, clusterID cluster.ID) { - c.cs.Lock() - defer func() { - c.cs.Unlock() - log.Infof("Number of remote clusters: %d", c.cs.Len()) - }() - log.Infof("Deleting cluster_id=%v configured by secret=%v", clusterID, secretKey) - close(c.cs.remoteClusters[secretKey][clusterID].stop) - err := c.handleDelete(clusterID) - if err != nil { - log.Errorf("Error removing cluster_id=%v configured by secret=%v: %v", - clusterID, secretKey, err) - } - delete(c.cs.remoteClusters[secretKey], clusterID) -} - -func (c *Controller) handleAdd(cluster *Cluster, stop <-chan struct{}) error { - var errs *multierror.Error - for _, handler := range c.handlers { - errs = multierror.Append(errs, handler.ClusterAdded(cluster, stop)) - } - return errs.ErrorOrNil() -} - -func (c *Controller) handleUpdate(cluster *Cluster, stop <-chan struct{}) error { - var errs *multierror.Error - for _, handler := range c.handlers { - errs = multierror.Append(errs, handler.ClusterUpdated(cluster, stop)) - } - return errs.ErrorOrNil() -} - -func (c *Controller) handleDelete(key cluster.ID) error { - var errs *multierror.Error - for _, handler := range c.handlers { - errs = multierror.Append(errs, handler.ClusterDeleted(key)) - } - return errs.ErrorOrNil() -} - -// ListRemoteClusters provides debug info about connected remote clusters. -func (c *Controller) ListRemoteClusters() []cluster.DebugInfo { - var out []cluster.DebugInfo - for secretName, clusters := range c.cs.All() { - for clusterID, c := range clusters { - syncStatus := "syncing" - if c.HasSynced() { - syncStatus = "synced" - } else if c.SyncDidTimeout() { - syncStatus = "timeout" - } - - out = append(out, cluster.DebugInfo{ - ID: clusterID, - SecretName: secretName, - SyncStatus: syncStatus, - }) - } - } - return out -} - -func (c *Controller) GetRemoteKubeClient(clusterID cluster.ID) kubernetes.Interface { - if remoteCluster := c.cs.GetByID(clusterID); remoteCluster != nil { - return remoteCluster.Client - } - return nil -} diff --git a/pkg/kube/multicluster/secretcontroller_test.go b/pkg/kube/multicluster/secretcontroller_test.go deleted file mode 100644 index 2cd422513..000000000 --- a/pkg/kube/multicluster/secretcontroller_test.go +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package multicluster - -import ( - "context" - "fmt" - "sync" - "testing" - "time" -) - -import ( - "github.com/google/go-cmp/cmp" - . "github.com/onsi/gomega" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/clientcmd/api" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" - "github.com/apache/dubbo-go-pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -const secretNamespace string = "dubbo-system" - -type clusterCredential struct { - clusterID string - kubeconfig []byte -} - -func makeSecret(secret string, clusterConfigs ...clusterCredential) *v1.Secret { - s := &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: secret, - Namespace: secretNamespace, - Labels: map[string]string{ - MultiClusterSecretLabel: "true", - }, - }, - Data: map[string][]byte{}, - } - - for _, config := range clusterConfigs { - s.Data[config.clusterID] = config.kubeconfig - } - return s -} - -var ( - mu sync.Mutex - added cluster.ID - updated cluster.ID - deleted cluster.ID -) - -var _ ClusterHandler = &handler{} - -type handler struct{} - -func (h handler) ClusterAdded(cluster *Cluster, stop <-chan struct{}) error { - mu.Lock() - defer mu.Unlock() - added = cluster.ID - return nil -} - -func (h handler) ClusterUpdated(cluster *Cluster, stop <-chan struct{}) error { - mu.Lock() - defer mu.Unlock() - updated = cluster.ID - return nil -} - -func (h handler) ClusterDeleted(id cluster.ID) error { - mu.Lock() - defer mu.Unlock() - deleted = id - return nil -} - -func resetCallbackData() { - added = "" - updated = "" - deleted = "" -} - -func Test_SecretController(t *testing.T) { - BuildClientsFromConfig = func(kubeConfig []byte) (kube.Client, error) { - return kube.NewFakeClient(), nil - } - test.SetDurationForTest(t, &features.RemoteClusterTimeout, 10*time.Nanosecond) - clientset := kube.NewFakeClient() - - var ( - secret0 = makeSecret("s0", clusterCredential{"c0", []byte("kubeconfig0-0")}) - secret0UpdateKubeconfigChanged = makeSecret("s0", clusterCredential{"c0", []byte("kubeconfig0-1")}) - secret0UpdateKubeconfigSame = makeSecret("s0", clusterCredential{"c0", []byte("kubeconfig0-1")}) - secret0AddCluster = makeSecret("s0", clusterCredential{"c0", []byte("kubeconfig0-1")}, clusterCredential{"c0-1", []byte("kubeconfig0-2")}) - secret0DeleteCluster = secret0UpdateKubeconfigChanged // "c0-1" cluster deleted - secret1 = makeSecret("s1", clusterCredential{"c1", []byte("kubeconfig1-0")}) - ) - secret0UpdateKubeconfigSame.Annotations = map[string]string{"foo": "bar"} - - steps := []struct { - // only set one of these per step. The others should be nil. - add *v1.Secret - update *v1.Secret - delete *v1.Secret - - // only set one of these per step. The others should be empty. - wantAdded cluster.ID - wantUpdated cluster.ID - wantDeleted cluster.ID - }{ - {add: secret0, wantAdded: "c0"}, // 0 - {update: secret0UpdateKubeconfigChanged, wantUpdated: "c0"}, // 1 - {update: secret0UpdateKubeconfigSame}, // 2 - {update: secret0AddCluster, wantAdded: "c0-1"}, // 3 - {update: secret0DeleteCluster, wantDeleted: "c0-1"}, // 4 - {add: secret1, wantAdded: "c1"}, // 5 - {delete: secret0, wantDeleted: "c0"}, // 6 - {delete: secret1, wantDeleted: "c1"}, // 7 - } - - // Start the secret controller and sleep to allow secret process to start. - stopCh := make(chan struct{}) - t.Cleanup(func() { - close(stopCh) - }) - c := NewController(clientset, secretNamespace, "") - c.AddHandler(&handler{}) - _ = c.Run(stopCh) - t.Run("sync timeout", func(t *testing.T) { - retry.UntilOrFail(t, c.HasSynced, retry.Timeout(2*time.Second)) - }) - kube.WaitForCacheSyncInterval(stopCh, time.Microsecond, c.informer.HasSynced) - clientset.RunAndWait(stopCh) - - for i, step := range steps { - resetCallbackData() - - t.Run(fmt.Sprintf("[%v]", i), func(t *testing.T) { - g := NewWithT(t) - - switch { - case step.add != nil: - _, err := clientset.CoreV1().Secrets(secretNamespace).Create(context.TODO(), step.add, metav1.CreateOptions{}) - g.Expect(err).Should(BeNil()) - case step.update != nil: - _, err := clientset.CoreV1().Secrets(secretNamespace).Update(context.TODO(), step.update, metav1.UpdateOptions{}) - g.Expect(err).Should(BeNil()) - case step.delete != nil: - g.Expect(clientset.CoreV1().Secrets(secretNamespace).Delete(context.TODO(), step.delete.Name, metav1.DeleteOptions{})). - Should(Succeed()) - } - - switch { - case step.wantAdded != "": - g.Eventually(func() cluster.ID { - mu.Lock() - defer mu.Unlock() - return added - }, 10*time.Second).Should(Equal(step.wantAdded)) - case step.wantUpdated != "": - g.Eventually(func() cluster.ID { - mu.Lock() - defer mu.Unlock() - return updated - }, 10*time.Second).Should(Equal(step.wantUpdated)) - case step.wantDeleted != "": - g.Eventually(func() cluster.ID { - mu.Lock() - defer mu.Unlock() - return deleted - }, 10*time.Second).Should(Equal(step.wantDeleted)) - default: - g.Consistently(func() bool { - mu.Lock() - defer mu.Unlock() - return added == "" && updated == "" && deleted == "" - }).Should(Equal(true)) - } - }) - } -} - -func TestSanitizeKubeConfig(t *testing.T) { - cases := []struct { - name string - config api.Config - allowlist sets.Set - want api.Config - wantErr bool - }{ - { - name: "empty", - config: api.Config{}, - want: api.Config{}, - wantErr: false, - }, - { - name: "exec", - config: api.Config{ - AuthInfos: map[string]*api.AuthInfo{ - "default": { - Exec: &api.ExecConfig{ - Command: "sleep", - }, - }, - }, - }, - wantErr: true, - }, - { - name: "exec allowlist", - allowlist: sets.New("exec"), - config: api.Config{ - AuthInfos: map[string]*api.AuthInfo{ - "default": { - Exec: &api.ExecConfig{ - Command: "sleep", - }, - }, - }, - }, - want: api.Config{ - AuthInfos: map[string]*api.AuthInfo{ - "default": { - Exec: &api.ExecConfig{ - Command: "sleep", - }, - }, - }, - }, - wantErr: false, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - err := sanitizeKubeConfig(tt.config, tt.allowlist) - if (err != nil) != tt.wantErr { - t.Fatalf("sanitizeKubeConfig() error = %v, wantErr %v", err, tt.wantErr) - } - if err != nil { - return - } - if diff := cmp.Diff(tt.config, tt.want); diff != "" { - t.Fatal(diff) - } - }) - } -} diff --git a/pkg/kube/portforwarder.go b/pkg/kube/portforwarder.go deleted file mode 100644 index fae9e6798..000000000 --- a/pkg/kube/portforwarder.go +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "context" - "fmt" - "io" - "net" - "net/http" - "os" -) - -import ( - v1 "k8s.io/api/core/v1" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/portforward" - "k8s.io/client-go/transport/spdy" -) - -// PortForwarder manages the forwarding of a single port. -type PortForwarder interface { - // Start runs this forwarder. - Start() error - - // Address returns the local forwarded address. Only valid while the forwarder is running. - Address() string - - // Close this forwarder and release an resources. - Close() - - // WaitForStop blocks until connection closed (e.g. control-C interrupt) - WaitForStop() -} - -var _ PortForwarder = &forwarder{} - -type forwarder struct { - stopCh chan struct{} - restConfig *rest.Config - podName string - ns string - localAddress string - localPort int - podPort int - address string -} - -func (f *forwarder) Start() error { - errCh := make(chan error, 1) - readyCh := make(chan struct{}, 1) - go func() { - for { - select { - case <-f.stopCh: - return - default: - } - // Build a new port forwarder. - fw, err := f.buildK8sPortForwarder(readyCh) - if err != nil { - errCh <- err - break - } - if err = fw.ForwardPorts(); err != nil { - errCh <- err - break - } - // At this point, either the stopCh has been closed, or port forwarder connection is broken. - // the port forwarder should have already been ready before. - // No need to notify the ready channel anymore when forwarding again. - readyCh = nil - } - }() - - select { - case err := <-errCh: - return fmt.Errorf("failure running port forward process: %v", err) - case <-readyCh: - // The forwarder is now ready. - return nil - } -} - -func (f *forwarder) Address() string { - return f.address -} - -func (f *forwarder) Close() { - close(f.stopCh) - // Closing the stop channel should close anything - // opened by f.forwarder.ForwardPorts() -} - -func (f *forwarder) WaitForStop() { - <-f.stopCh -} - -func newPortForwarder(restConfig *rest.Config, podName, ns, localAddress string, localPort, podPort int) (PortForwarder, error) { - if localAddress == "" { - localAddress = defaultLocalAddress - } - f := &forwarder{ - stopCh: make(chan struct{}), - restConfig: restConfig, - podName: podName, - ns: ns, - localAddress: localAddress, - localPort: localPort, - podPort: podPort, - } - if f.localPort == 0 { - var err error - f.localPort, err = availablePort(f.localAddress) - if err != nil { - return nil, fmt.Errorf("failure allocating port: %v", err) - } - } - - sLocalPort := fmt.Sprintf("%d", f.localPort) - f.address = net.JoinHostPort(localAddress, sLocalPort) - return f, nil -} - -func (f *forwarder) buildK8sPortForwarder(readyCh chan struct{}) (*portforward.PortForwarder, error) { - restClient, err := rest.RESTClientFor(f.restConfig) - if err != nil { - return nil, err - } - - req := restClient.Post().Resource("pods").Namespace(f.ns).Name(f.podName).SubResource("portforward") - serverURL := req.URL() - - roundTripper, upgrader, err := roundTripperFor(f.restConfig) - if err != nil { - return nil, fmt.Errorf("failure creating roundtripper: %v", err) - } - - dialer := spdy.NewDialer(upgrader, &http.Client{Transport: roundTripper}, http.MethodPost, serverURL) - - fw, err := portforward.NewOnAddresses(dialer, - []string{f.localAddress}, - []string{fmt.Sprintf("%d:%d", f.localPort, f.podPort)}, - f.stopCh, - readyCh, - io.Discard, - os.Stderr) - if err != nil { - return nil, fmt.Errorf("failed establishing port-forward: %v", err) - } - - // Run the same check as k8s.io/kubectl/pkg/cmd/portforward/portforward.go - // so that we will fail early if there is a problem contacting API server. - podGet := restClient.Get().Resource("pods").Namespace(f.ns).Name(f.podName) - obj, err := podGet.Do(context.TODO()).Get() - if err != nil { - return nil, fmt.Errorf("failed retrieving: %v in the %q namespace", err, f.ns) - } - pod, ok := obj.(*v1.Pod) - if !ok { - return nil, fmt.Errorf("failed getting pod, object type is %T", obj) - } - if pod.Status.Phase != v1.PodRunning { - return nil, fmt.Errorf("pod is not running. Status=%v", pod.Status.Phase) - } - - return fw, nil -} - -func availablePort(localAddr string) (int, error) { - addr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(localAddr, "0")) - if err != nil { - return 0, err - } - - l, err := net.ListenTCP("tcp", addr) - if err != nil { - return 0, err - } - port := l.Addr().(*net.TCPAddr).Port - return port, l.Close() -} diff --git a/pkg/kube/rpc_creds.go b/pkg/kube/rpc_creds.go deleted file mode 100644 index 9b37006a2..000000000 --- a/pkg/kube/rpc_creds.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "context" - "sync" - "time" -) - -import ( - "google.golang.org/grpc/credentials" - "istio.io/pkg/log" - authenticationv1 "k8s.io/api/authentication/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -type tokenSupplier struct { - // The token itself. (These are public in case we need to serialize) - Token string - Expires time.Time - - // regenerate tokens using this - mu sync.RWMutex - tokenNamespace string - tokenServiceAccount string - audiences []string - expirationSeconds int64 - kubeClient Client - // sunsetPeriod is how long before expiration that we start trying to renew (a minute) - sunsetPeriod time.Duration -} - -var _ credentials.PerRPCCredentials = &tokenSupplier{} - -// NewRPCCredentials creates a PerRPCCredentials capable of getting tokens from Istio and tracking their expiration -func NewRPCCredentials(kubeClient Client, tokenNamespace, tokenSA string, - tokenAudiences []string, expirationSeconds, sunsetPeriodSeconds int64) (credentials.PerRPCCredentials, error) { - tokenRequest, err := createServiceAccountToken(context.TODO(), kubeClient, tokenNamespace, tokenSA, tokenAudiences, expirationSeconds) - if err != nil { - return nil, err - } - return &tokenSupplier{ - Token: tokenRequest.Status.Token, - Expires: tokenRequest.Status.ExpirationTimestamp.Time, - - // Save in case we need to renew during a very long-lived gRPC - tokenNamespace: tokenNamespace, - tokenServiceAccount: tokenSA, - audiences: tokenAudiences, - expirationSeconds: expirationSeconds, - sunsetPeriod: time.Duration(sunsetPeriodSeconds) * time.Second, - kubeClient: kubeClient, - }, nil -} - -// GetRequestMetadata fulfills the grpc/credentials.PerRPCCredentials interface -func (its *tokenSupplier) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { - its.mu.RLock() - token := its.Token - needRecreate := time.Until(its.Expires) < its.sunsetPeriod - its.mu.RUnlock() - - if needRecreate { - its.mu.Lock() - // This checks the same condition as above. (The outer check is to bypass the mutex when it is too early to renew) - if time.Until(its.Expires) < its.sunsetPeriod { - log.Debug("GetRequestMetadata will generate a new token to replace one that is about to expire") - // We have no 'renew' method, just request a new token - tokenRequest, err := createServiceAccountToken(ctx, its.kubeClient, its.tokenNamespace, its.tokenServiceAccount, - its.audiences, its.expirationSeconds) - if err == nil { - its.Token = tokenRequest.Status.Token - its.Expires = tokenRequest.Status.ExpirationTimestamp.Time - } else { - log.Infof("GetRequestMetadata failed to recreate token: %v", err.Error()) - } - } - token = its.Token - its.mu.Unlock() - } - - return map[string]string{ - "authorization": "Bearer " + token, - }, nil -} - -// RequireTransportSecurity fulfills the grpc/credentials.PerRPCCredentials interface -func (its *tokenSupplier) RequireTransportSecurity() bool { - return false -} - -func createServiceAccountToken(ctx context.Context, client Client, - tokenNamespace, tokenServiceAccount string, audiences []string, expirationSeconds int64) (*authenticationv1.TokenRequest, error) { - return client.Kube().CoreV1().ServiceAccounts(tokenNamespace).CreateToken(ctx, tokenServiceAccount, - &authenticationv1.TokenRequest{ - Spec: authenticationv1.TokenRequestSpec{ - Audiences: audiences, - ExpirationSeconds: &expirationSeconds, - }, - }, metav1.CreateOptions{}) -} diff --git a/pkg/kube/rpc_creds_test.go b/pkg/kube/rpc_creds_test.go deleted file mode 100644 index 08eaa80b2..000000000 --- a/pkg/kube/rpc_creds_test.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "context" - "reflect" - "testing" - "time" -) - -import ( - v1 "k8s.io/api/authentication/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/rand" - "k8s.io/client-go/kubernetes/fake" - clienttesting "k8s.io/client-go/testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -func Test_tokenSupplier_GetRequestMetadata(t *testing.T) { - ctx := context.Background() - cli := NewFakeClient() - clientset := cli.Kube().(*fake.Clientset) - clientset.PrependReactor("create", "serviceaccounts", - func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { - act, ok := action.(clienttesting.CreateActionImpl) - if !ok { - return false, nil, nil - } - tokenReq, ok := act.Object.(*v1.TokenRequest) - if !ok { - return false, nil, nil - } - t := tokenReq.DeepCopy() - now := time.Now() - t.Status.ExpirationTimestamp = metav1.NewTime(now.Add(time.Duration(*t.Spec.ExpirationSeconds) * time.Second)) - t.Status.Token = rand.String(16) - return true, t, nil - }, - ) - - const ( - refreshSeconds = 1 - expirationSeconds = 60 - sunsetPeriodSeconds = expirationSeconds - refreshSeconds - ) - - perCred, err := NewRPCCredentials(cli, "default", "default", nil, expirationSeconds, sunsetPeriodSeconds) - if err != nil { - t.Fatal(err) - } - m1, err := perCred.GetRequestMetadata(ctx) - if err != nil { - t.Fatal(err) - } - - m2, err := perCred.GetRequestMetadata(ctx) - if err != nil { - t.Fatal(err) - } - - if !reflect.DeepEqual(m1, m2) { - t.Fatalf("Unexpectedly getting a new tokens") - } - - var m3 map[string]string - retry.UntilOrFail(t, - func() bool { - m3, err = perCred.GetRequestMetadata(ctx) - return err == nil && !reflect.DeepEqual(m1, m3) - }, - retry.Delay(refreshSeconds*time.Second), - retry.Timeout(expirationSeconds*time.Second), - ) - if reflect.DeepEqual(m1, m3) { - t.Fatalf("Unexpectedly not getting a new token") - } -} diff --git a/pkg/kube/spdy.go b/pkg/kube/spdy.go deleted file mode 100644 index 5b7f3e9a5..000000000 --- a/pkg/kube/spdy.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "crypto/tls" - "fmt" - "net/http" -) - -import ( - spdyStream "k8s.io/apimachinery/pkg/util/httpstream/spdy" - "k8s.io/client-go/rest" - "k8s.io/client-go/transport/spdy" -) - -// roundTripperFor creates a SPDY upgrader that will work over custom transports. -func roundTripperFor(restConfig *rest.Config) (http.RoundTripper, spdy.Upgrader, error) { - // Get the TLS config. - tlsConfig, err := rest.TLSConfigFor(restConfig) - if err != nil { - return nil, nil, fmt.Errorf("failed getting TLS config: %w", err) - } - if tlsConfig == nil && restConfig.Transport != nil { - // If using a custom transport, skip server verification on the upgrade. - tlsConfig = &tls.Config{ - InsecureSkipVerify: true, - } - } - - var upgrader *spdyStream.SpdyRoundTripper - if restConfig.Proxy != nil { - upgrader = spdyStream.NewRoundTripperWithProxy(tlsConfig, restConfig.Proxy) - } else { - upgrader = spdyStream.NewRoundTripper(tlsConfig) - } - wrapper, err := rest.HTTPWrappersForConfig(restConfig, upgrader) - if err != nil { - return nil, nil, fmt.Errorf("failed creating SPDY upgrade wrapper: %w", err) - } - return wrapper, upgrader, nil -} diff --git a/pkg/kube/util.go b/pkg/kube/util.go deleted file mode 100644 index df2bcc477..000000000 --- a/pkg/kube/util.go +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "fmt" - "io" - "net/http" - "os" - "path/filepath" - "regexp" - "strings" -) - -import ( - istioversion "istio.io/pkg/version" - kubeApiCore "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/serializer" - "k8s.io/client-go/kubernetes" - _ "k8s.io/client-go/plugin/pkg/client/auth" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" -) - -var cronJobNameRegexp = regexp.MustCompile(`(.+)-\d{8,10}$`) - -// BuildClientConfig builds a client rest config from a kubeconfig filepath and context. -// It overrides the current context with the one provided (empty to use default). -// -// This is a modified version of k8s.io/client-go/tools/clientcmd/BuildConfigFromFlags with the -// difference that it loads default configs if not running in-cluster. -func BuildClientConfig(kubeconfig, context string) (*rest.Config, error) { - c, err := BuildClientCmd(kubeconfig, context).ClientConfig() - if err != nil { - return nil, err - } - return SetRestDefaults(c), nil -} - -// BuildClientCmd builds a client cmd config from a kubeconfig filepath and context. -// It overrides the current context with the one provided (empty to use default). -// -// This is a modified version of k8s.io/client-go/tools/clientcmd/BuildConfigFromFlags with the -// difference that it loads default configs if not running in-cluster. -func BuildClientCmd(kubeconfig, context string, overrides ...func(*clientcmd.ConfigOverrides)) clientcmd.ClientConfig { - if kubeconfig != "" { - info, err := os.Stat(kubeconfig) - if err != nil || info.Size() == 0 { - // If the specified kubeconfig doesn't exists / empty file / any other error - // from file stat, fall back to default - kubeconfig = "" - } - } - - // Config loading rules: - // 1. kubeconfig if it not empty string - // 2. Config(s) in KUBECONFIG environment variable - // 3. In cluster config if running in-cluster - // 4. Use $HOME/.kube/config - loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() - loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig - loadingRules.ExplicitPath = kubeconfig - configOverrides := &clientcmd.ConfigOverrides{ - ClusterDefaults: clientcmd.ClusterDefaults, - CurrentContext: context, - } - - for _, fn := range overrides { - fn(configOverrides) - } - - return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) -} - -// CreateClientset is a helper function that builds a kubernetes Clienset from a kubeconfig -// filepath. See `BuildClientConfig` for kubeconfig loading rules. -func CreateClientset(kubeconfig, context string, fns ...func(*rest.Config)) (*kubernetes.Clientset, error) { - c, err := BuildClientConfig(kubeconfig, context) - if err != nil { - return nil, fmt.Errorf("build client config: %v", err) - } - for _, fn := range fns { - fn(c) - } - return kubernetes.NewForConfig(c) -} - -// DefaultRestConfig returns the rest.Config for the given kube config file and context. -func DefaultRestConfig(kubeconfig, configContext string, fns ...func(*rest.Config)) (*rest.Config, error) { - config, err := BuildClientConfig(kubeconfig, configContext) - if err != nil { - return nil, err - } - config = SetRestDefaults(config) - - for _, fn := range fns { - fn(config) - } - - return config, nil -} - -// adjustCommand returns the last component of the -// OS-specific command path for use in User-Agent. -func adjustCommand(p string) string { - // Unlikely, but better than returning "". - if len(p) == 0 { - return "unknown" - } - return filepath.Base(p) -} - -// IstioUserAgent returns the user agent string based on the command being used. -// example: pilot-discovery/1.9.5 or istioctl/1.10.0 -// This is a specialized version of rest.DefaultKubernetesUserAgent() -func IstioUserAgent() string { - return adjustCommand(os.Args[0]) + "/" + istioversion.Info.Version -} - -// SetRestDefaults is a helper function that sets default values for the given rest.Config. -// This function is idempotent. -func SetRestDefaults(config *rest.Config) *rest.Config { - if config.GroupVersion == nil || config.GroupVersion.Empty() { - config.GroupVersion = &kubeApiCore.SchemeGroupVersion - } - if len(config.APIPath) == 0 { - if len(config.GroupVersion.Group) == 0 { - config.APIPath = "/api" - } else { - config.APIPath = "/apis" - } - } - if len(config.ContentType) == 0 { - config.ContentType = runtime.ContentTypeJSON - } - if config.NegotiatedSerializer == nil { - // This codec factory ensures the resources are not converted. Therefore, resources - // will not be round-tripped through internal versions. Defaulting does not happen - // on the client. - config.NegotiatedSerializer = serializer.NewCodecFactory(IstioScheme).WithoutConversion() - } - if len(config.UserAgent) == 0 { - config.UserAgent = IstioUserAgent() - } - - return config -} - -// CheckPodReadyOrComplete returns nil if the given pod and all of its containers are ready or terminated -// successfully. -func CheckPodReadyOrComplete(pod *kubeApiCore.Pod) error { - switch pod.Status.Phase { - case kubeApiCore.PodSucceeded: - return nil - case kubeApiCore.PodRunning: - return CheckPodReady(pod) - default: - return fmt.Errorf("%s", pod.Status.Phase) - } -} - -// CheckPodReady returns nil if the given pod and all of its containers are ready. -func CheckPodReady(pod *kubeApiCore.Pod) error { - switch pod.Status.Phase { - case kubeApiCore.PodRunning: - // Wait until all containers are ready. - for _, containerStatus := range pod.Status.ContainerStatuses { - if !containerStatus.Ready { - return fmt.Errorf("container not ready: '%s'", containerStatus.Name) - } - } - if len(pod.Status.Conditions) > 0 { - for _, condition := range pod.Status.Conditions { - if condition.Type == kubeApiCore.PodReady && condition.Status != kubeApiCore.ConditionTrue { - return fmt.Errorf("pod not ready, condition message: %v", condition.Message) - } - } - } - return nil - default: - return fmt.Errorf("%s", pod.Status.Phase) - } -} - -// GetDeployMetaFromPod heuristically derives deployment metadata from the pod spec. -func GetDeployMetaFromPod(pod *kubeApiCore.Pod) (metav1.ObjectMeta, metav1.TypeMeta) { - if pod == nil { - return metav1.ObjectMeta{}, metav1.TypeMeta{} - } - // try to capture more useful namespace/name info for deployments, etc. - // TODO(dougreid): expand to enable lookup of OWNERs recursively a la kubernetesenv - deployMeta := pod.ObjectMeta - deployMeta.ManagedFields = nil - deployMeta.OwnerReferences = nil - - typeMetadata := metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - } - if len(pod.GenerateName) > 0 { - // if the pod name was generated (or is scheduled for generation), we can begin an investigation into the controlling reference for the pod. - var controllerRef metav1.OwnerReference - controllerFound := false - for _, ref := range pod.GetOwnerReferences() { - if ref.Controller != nil && *ref.Controller { - controllerRef = ref - controllerFound = true - break - } - } - if controllerFound { - typeMetadata.APIVersion = controllerRef.APIVersion - typeMetadata.Kind = controllerRef.Kind - - // heuristic for deployment detection - deployMeta.Name = controllerRef.Name - if typeMetadata.Kind == "ReplicaSet" && pod.Labels["pod-template-hash"] != "" && strings.HasSuffix(controllerRef.Name, pod.Labels["pod-template-hash"]) { - name := strings.TrimSuffix(controllerRef.Name, "-"+pod.Labels["pod-template-hash"]) - deployMeta.Name = name - typeMetadata.Kind = "Deployment" - } else if typeMetadata.Kind == "ReplicationController" && pod.Labels["deploymentconfig"] != "" { - // If the pod is controlled by the replication controller, which is created by the DeploymentConfig resource in - // Openshift platform, set the deploy name to the deployment config's name, and the kind to 'DeploymentConfig'. - // - // nolint: lll - // For DeploymentConfig details, refer to - // https://docs.openshift.com/container-platform/4.1/applications/deployments/what-deployments-are.html#deployments-and-deploymentconfigs_what-deployments-are - // - // For the reference to the pod label 'deploymentconfig', refer to - // https://github.com/openshift/library-go/blob/7a65fdb398e28782ee1650959a5e0419121e97ae/pkg/apps/appsutil/const.go#L25 - deployMeta.Name = pod.Labels["deploymentconfig"] - typeMetadata.Kind = "DeploymentConfig" - delete(deployMeta.Labels, "deploymentconfig") - } else if typeMetadata.Kind == "Job" { - // If job name suffixed with `-`, where the length of digit timestamp is 8~10, - // trim the suffix and set kind to cron job. - if jn := cronJobNameRegexp.FindStringSubmatch(controllerRef.Name); len(jn) == 2 { - deployMeta.Name = jn[1] - typeMetadata.Kind = "CronJob" - // heuristically set cron job api version to v1beta1 as it cannot be derived from pod metadata. - // Cronjob is not GA yet and latest version is v1beta1: https://github.com/kubernetes/enhancements/pull/978 - typeMetadata.APIVersion = "batch/v1beta1" - } - } - } - } - - if deployMeta.Name == "" { - // if we haven't been able to extract a deployment name, then just give it the pod name - deployMeta.Name = pod.Name - } - - return deployMeta, typeMetadata -} - -// MaxRequestBodyBytes represents the max size of Kubernetes objects we read. Kubernetes allows a 2x -// buffer on the max etcd size -// (https://github.com/kubernetes/kubernetes/blob/0afa569499d480df4977568454a50790891860f5/staging/src/k8s.io/apiserver/pkg/server/config.go#L362). -// We allow an additional 2x buffer, as it is still fairly cheap (6mb) -const MaxRequestBodyBytes = int64(6 * 1024 * 1024) - -// HTTPConfigReader is reads an HTTP request, imposing size restrictions aligned with Kubernetes limits -func HTTPConfigReader(req *http.Request) ([]byte, error) { - defer req.Body.Close() - lr := &io.LimitedReader{ - R: req.Body, - N: MaxRequestBodyBytes + 1, - } - data, err := io.ReadAll(lr) - if err != nil { - return nil, err - } - if lr.N <= 0 { - return nil, errors.NewRequestEntityTooLargeError(fmt.Sprintf("limit is %d", MaxRequestBodyBytes)) - } - return data, nil -} diff --git a/pkg/kube/util_test.go b/pkg/kube/util_test.go deleted file mode 100644 index f56072145..000000000 --- a/pkg/kube/util_test.go +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "fmt" - "os" - "path/filepath" - "reflect" - "testing" -) - -import ( - kubeApiCore "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestBuildClientConfig(t *testing.T) { - config1, err := generateKubeConfig("1.1.1.1", "3.3.3.3") - if err != nil { - t.Fatalf("Failed to create a sample kubernetes config file. Err: %v", err) - } - defer os.RemoveAll(filepath.Dir(config1)) - config2, err := generateKubeConfig("2.2.2.2", "4.4.4.4") - if err != nil { - t.Fatalf("Failed to create a sample kubernetes config file. Err: %v", err) - } - defer os.RemoveAll(filepath.Dir(config2)) - - tests := []struct { - name string - explicitKubeconfig string - envKubeconfig string - context string - wantErr bool - wantHost string - }{ - { - name: "DefaultSystemKubeconfig", - explicitKubeconfig: "", - envKubeconfig: config1, - wantErr: false, - wantHost: "https://1.1.1.1:8001", - }, - { - name: "SinglePath", - explicitKubeconfig: config1, - wantErr: false, - envKubeconfig: "", - wantHost: "https://1.1.1.1:8001", - }, - { - name: "MultiplePathsFirst", - explicitKubeconfig: "", - wantErr: false, - envKubeconfig: fmt.Sprintf("%s:%s", config1, config2), - wantHost: "https://1.1.1.1:8001", - }, - { - name: "MultiplePathsSecond", - explicitKubeconfig: "", - wantErr: false, - envKubeconfig: fmt.Sprintf("missing:%s", config2), - wantHost: "https://2.2.2.2:8001", - }, - { - name: "NonCurrentContext", - explicitKubeconfig: config1, - wantErr: false, - envKubeconfig: "", - context: "cluster2.local-context", - wantHost: "https://3.3.3.3:8001", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - currentEnv := os.Getenv("KUBECONFIG") - err := os.Setenv("KUBECONFIG", tt.envKubeconfig) - if err != nil { - t.Fatalf("Failed to set KUBECONFIG environment variable") - } - defer os.Setenv("KUBECONFIG", currentEnv) - - resp, err := BuildClientConfig(tt.explicitKubeconfig, tt.context) - if (err != nil) != tt.wantErr { - t.Fatalf("BuildClientConfig() error = %v, wantErr %v", err, tt.wantErr) - } - if resp != nil && resp.Host != tt.wantHost { - t.Fatalf("Incorrect host. Got: %s, Want: %s", resp.Host, tt.wantHost) - } - }) - } -} - -func generateKubeConfig(cluster1Host string, cluster2Host string) (string, error) { - tempDir, err := os.MkdirTemp("/tmp/", ".kube") - if err != nil { - return "", err - } - filePath := filepath.Join(tempDir, "config") - - template := `apiVersion: v1 -kind: Config -clusters: -- cluster: - insecure-skip-tls-verify: true - server: https://%s:8001 - name: cluster.local -- cluster: - insecure-skip-tls-verify: true - server: https://%s:8001 - name: cluster2.local -contexts: -- context: - cluster: cluster.local - namespace: default - user: admin - name: cluster.local-context -- context: - cluster: cluster2.local - namespace: default - user: admin - name: cluster2.local-context -current-context: cluster.local-context -preferences: {} -users: -- name: admin - user: - token: sdsddsd` - - sampleConfig := fmt.Sprintf(template, cluster1Host, cluster2Host) - err = os.WriteFile(filePath, []byte(sampleConfig), 0o644) - if err != nil { - return "", err - } - return filePath, nil -} - -func TestCronJobMetadata(t *testing.T) { - tests := []struct { - name string - jobName string - wantTypeMetadata metav1.TypeMeta - wantObjectMetadata metav1.ObjectMeta - }{ - { - name: "cron-job-name-sec", - jobName: "sec-1234567890", - wantTypeMetadata: metav1.TypeMeta{ - Kind: "CronJob", - APIVersion: "batch/v1beta1", - }, - wantObjectMetadata: metav1.ObjectMeta{ - Name: "sec", - GenerateName: "sec-1234567890-pod", - }, - }, - { - name: "cron-job-name-min", - jobName: "min-12345678", - wantTypeMetadata: metav1.TypeMeta{ - Kind: "CronJob", - APIVersion: "batch/v1beta1", - }, - wantObjectMetadata: metav1.ObjectMeta{ - Name: "min", - GenerateName: "min-12345678-pod", - }, - }, - { - name: "non-cron-job-name", - jobName: "job-123", - wantTypeMetadata: metav1.TypeMeta{ - Kind: "Job", - APIVersion: "v1", - }, - wantObjectMetadata: metav1.ObjectMeta{ - Name: "job-123", - GenerateName: "job-123-pod", - }, - }, - } - - for _, tt := range tests { - controller := true - t.Run(tt.name, func(t *testing.T) { - gotObjectMeta, gotTypeMeta := GetDeployMetaFromPod( - &kubeApiCore.Pod{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: tt.jobName + "-pod", - OwnerReferences: []metav1.OwnerReference{{ - APIVersion: "v1", - Controller: &controller, - Kind: "Job", - Name: tt.jobName, - }}, - }, - }, - ) - if !reflect.DeepEqual(gotObjectMeta, tt.wantObjectMetadata) { - t.Errorf("Object metadata got %+v want %+v", gotObjectMeta, tt.wantObjectMetadata) - } - if !reflect.DeepEqual(gotTypeMeta, tt.wantTypeMetadata) { - t.Errorf("Type metadata got %+v want %+v", gotTypeMeta, tt.wantTypeMetadata) - } - }) - } -} - -func TestDeploymentConfigMetadata(t *testing.T) { - tests := []struct { - name string - pod *kubeApiCore.Pod - wantTypeMetadata metav1.TypeMeta - wantObjectMetadata metav1.ObjectMeta - }{ - { - name: "deployconfig-name-deploy", - pod: podForDeploymentConfig("deploy", true), - wantTypeMetadata: metav1.TypeMeta{ - Kind: "DeploymentConfig", - APIVersion: "v1", - }, - wantObjectMetadata: metav1.ObjectMeta{ - Name: "deploy", - GenerateName: "deploy-rc-pod", - Labels: map[string]string{}, - }, - }, - { - name: "deployconfig-name-deploy2", - pod: podForDeploymentConfig("deploy2", true), - wantTypeMetadata: metav1.TypeMeta{ - Kind: "DeploymentConfig", - APIVersion: "v1", - }, - wantObjectMetadata: metav1.ObjectMeta{ - Name: "deploy2", - GenerateName: "deploy2-rc-pod", - Labels: map[string]string{}, - }, - }, - { - name: "non-deployconfig-label", - pod: podForDeploymentConfig("dep", false), - wantTypeMetadata: metav1.TypeMeta{ - Kind: "ReplicationController", - APIVersion: "v1", - }, - wantObjectMetadata: metav1.ObjectMeta{ - Name: "dep-rc", - GenerateName: "dep-rc-pod", - Labels: map[string]string{}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotObjectMeta, gotTypeMeta := GetDeployMetaFromPod(tt.pod) - if !reflect.DeepEqual(gotObjectMeta, tt.wantObjectMetadata) { - t.Errorf("Object metadata got %+v want %+v", gotObjectMeta, tt.wantObjectMetadata) - } - if !reflect.DeepEqual(gotTypeMeta, tt.wantTypeMetadata) { - t.Errorf("Type metadata got %+v want %+v", gotTypeMeta, tt.wantTypeMetadata) - } - }) - } -} - -func podForDeploymentConfig(deployConfigName string, hasDeployConfigLabel bool) *kubeApiCore.Pod { - controller := true - labels := make(map[string]string) - if hasDeployConfigLabel { - labels["deploymentconfig"] = deployConfigName - } - return &kubeApiCore.Pod{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: deployConfigName + "-rc-pod", - OwnerReferences: []metav1.OwnerReference{{ - APIVersion: "v1", - Controller: &controller, - Kind: "ReplicationController", - Name: deployConfigName + "-rc", - }}, - Labels: labels, - }, - } -} diff --git a/pkg/kube/version.go b/pkg/kube/version.go deleted file mode 100644 index e4755b800..000000000 --- a/pkg/kube/version.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "fmt" -) - -import ( - "k8s.io/apimachinery/pkg/util/version" - kubeVersion "k8s.io/apimachinery/pkg/version" -) - -// IsAtLeastVersion returns true if the client is at least the specified version. -// For example, on Kubernetes v1.15.2, IsAtLeastVersion(13) == true, IsAtLeastVersion(17) == false -func IsAtLeastVersion(client Client, minorVersion uint) bool { - clusterVersion, err := client.GetKubernetesVersion() - if err != nil { - return true - } - return IsKubeAtLeastOrLessThanVersion(clusterVersion, minorVersion, true) -} - -// IsLessThanVersion returns true if the client version is less than the specified version. -// For example, on Kubernetes v1.15.2, IsLessThanVersion(13) == false, IsLessThanVersion(17) == true -func IsLessThanVersion(client Client, minorVersion uint) bool { - clusterVersion, err := client.GetKubernetesVersion() - if err != nil { - return true - } - return IsKubeAtLeastOrLessThanVersion(clusterVersion, minorVersion, false) -} - -// IsKubeAtLeastOrLessThanVersion returns if the kubernetes version is at least or less than the specified version. -func IsKubeAtLeastOrLessThanVersion(clusterVersion *kubeVersion.Info, minorVersion uint, atLeast bool) bool { - if clusterVersion == nil { - return true - } - cv, err := version.ParseGeneric(fmt.Sprintf("v%s.%s.0", clusterVersion.Major, clusterVersion.Minor)) - if err != nil { - return true - } - ev, err := version.ParseGeneric(fmt.Sprintf("v1.%d.0", minorVersion)) - if err != nil { - return true - } - if atLeast { - return cv.AtLeast(ev) - } - return cv.LessThan(ev) -} diff --git a/pkg/kube/version_test.go b/pkg/kube/version_test.go deleted file mode 100644 index ab626e175..000000000 --- a/pkg/kube/version_test.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "testing" -) - -func TestIsAtLeastVersion(t *testing.T) { - tests := []struct { - name string - clusterVersion uint - minorVersion uint - want bool - }{ - { - name: "exact match", - clusterVersion: 15, - minorVersion: 15, - want: true, - }, - { - name: "too old", - clusterVersion: 14, - minorVersion: 15, - want: false, - }, - { - name: "newer", - clusterVersion: 16, - minorVersion: 15, - want: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - cl := &MockClient{KubernetesVersion: tt.clusterVersion} - if got := IsAtLeastVersion(cl, tt.minorVersion); got != tt.want { - t.Errorf("IsAtLeastVersion() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestIsLessThanVersionVersion(t *testing.T) { - tests := []struct { - name string - clusterVersion uint - minorVersion uint - want bool - }{ - { - name: "exact match", - clusterVersion: 15, - minorVersion: 15, - want: false, - }, - { - name: "older", - clusterVersion: 14, - minorVersion: 15, - want: true, - }, - { - name: "too new", - clusterVersion: 16, - minorVersion: 15, - want: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - cl := &MockClient{KubernetesVersion: tt.clusterVersion} - if got := IsLessThanVersion(cl, tt.minorVersion); got != tt.want { - t.Errorf("IsLessThanVersion() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/pixiu/pkg/listener/http/http_listener.go b/pkg/listener/http/http_listener.go similarity index 95% rename from pixiu/pkg/listener/http/http_listener.go rename to pkg/listener/http/http_listener.go index 542a825a3..9ed2015af 100644 --- a/pixiu/pkg/listener/http/http_listener.go +++ b/pkg/listener/http/http_listener.go @@ -33,11 +33,11 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filterchain" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/listener" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/config" + "github.com/apache/dubbo-go-pixiu/pkg/filterchain" + "github.com/apache/dubbo-go-pixiu/pkg/listener" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) func init() { diff --git a/pixiu/pkg/listener/http2/http2_listener.go b/pkg/listener/http2/http2_listener.go similarity index 94% rename from pixiu/pkg/listener/http2/http2_listener.go rename to pkg/listener/http2/http2_listener.go index 73a067def..3d7bc91e3 100644 --- a/pixiu/pkg/listener/http2/http2_listener.go +++ b/pkg/listener/http2/http2_listener.go @@ -31,11 +31,11 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filterchain" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/listener" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/config" + "github.com/apache/dubbo-go-pixiu/pkg/filterchain" + "github.com/apache/dubbo-go-pixiu/pkg/listener" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) func init() { diff --git a/pkg/listener/listener.go b/pkg/listener/listener.go new file mode 100644 index 000000000..63685881d --- /dev/null +++ b/pkg/listener/listener.go @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 listener + +import ( + "sync/atomic" +) + +import ( + "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/filterchain" + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +var factoryMap = make(map[model.ProtocolType]func(lc *model.Listener, bs *model.Bootstrap) (ListenerService, error), 8) + +type ( + ListenerService interface { + // Start the listener service + Start() error + // Close the listener service forcefully + Close() error + // ShutDown gracefully shuts down the listener. + ShutDown(interface{}) error + // Refresh config + Refresh(model.Listener) error + } + + BaseListenerService struct { + Config *model.Listener + FilterChain *filterchain.NetworkFilterChain + } + + ListenerGracefulShutdownConfig struct { + ActiveCount int32 + RejectRequest bool + } +) + +// SetListenerServiceFactory will store the listenerService factory by name +func SetListenerServiceFactory(protocol model.ProtocolType, newRegFunc func(lc *model.Listener, bs *model.Bootstrap) (ListenerService, error)) { + factoryMap[protocol] = newRegFunc +} + +// CreateListenerService create listener service +func CreateListenerService(lc *model.Listener, bs *model.Bootstrap) (ListenerService, error) { + if registry, ok := factoryMap[lc.Protocol]; ok { + reg, err := registry(lc, bs) + if err != nil { + panic("Initialize ListenerService " + lc.Name + "failed due to: " + err.Error()) + } + return reg, nil + } + return nil, errors.New("Registry " + lc.ProtocolStr + " does not support yet") +} + +func (lgsc *ListenerGracefulShutdownConfig) AddActiveCount(num int32) { + atomic.AddInt32(&lgsc.ActiveCount, num) +} diff --git a/pixiu/pkg/listener/tcp/pkg_handler.go b/pkg/listener/tcp/pkg_handler.go similarity index 100% rename from pixiu/pkg/listener/tcp/pkg_handler.go rename to pkg/listener/tcp/pkg_handler.go diff --git a/pixiu/pkg/listener/tcp/server_handler.go b/pkg/listener/tcp/server_handler.go similarity index 99% rename from pixiu/pkg/listener/tcp/server_handler.go rename to pkg/listener/tcp/server_handler.go index b7c1904b3..4f6f6e019 100644 --- a/pixiu/pkg/listener/tcp/server_handler.go +++ b/pkg/listener/tcp/server_handler.go @@ -35,7 +35,7 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/logger" ) const ( diff --git a/pixiu/pkg/listener/tcp/tcp_listener.go b/pkg/listener/tcp/tcp_listener.go similarity index 93% rename from pixiu/pkg/listener/tcp/tcp_listener.go rename to pkg/listener/tcp/tcp_listener.go index 0fdb4d8f2..9cfab57e4 100644 --- a/pixiu/pkg/listener/tcp/tcp_listener.go +++ b/pkg/listener/tcp/tcp_listener.go @@ -30,11 +30,11 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filterchain" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/listener" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/config" + "github.com/apache/dubbo-go-pixiu/pkg/filterchain" + "github.com/apache/dubbo-go-pixiu/pkg/listener" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) func init() { diff --git a/pixiu/pkg/listener/triple/triple_listener.go b/pkg/listener/triple/triple_listener.go similarity index 94% rename from pixiu/pkg/listener/triple/triple_listener.go rename to pkg/listener/triple/triple_listener.go index 0010d6e6e..0262869cd 100644 --- a/pixiu/pkg/listener/triple/triple_listener.go +++ b/pkg/listener/triple/triple_listener.go @@ -32,11 +32,11 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filterchain" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/listener" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/config" + "github.com/apache/dubbo-go-pixiu/pkg/filterchain" + "github.com/apache/dubbo-go-pixiu/pkg/listener" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) func init() { diff --git a/pixiu/pkg/logger/log.yml b/pkg/logger/log.yml similarity index 100% rename from pixiu/pkg/logger/log.yml rename to pkg/logger/log.yml diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go new file mode 100644 index 000000000..38d7bbafe --- /dev/null +++ b/pkg/logger/logger.go @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 logger + +import ( + "fmt" + "os" + "path" + "sync" +) + +import ( + perrors "github.com/pkg/errors" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/yaml" +) + +var logger Logger + +// DubbogoPXLogger is logger struct +type DubbogoPXLogger struct { + mutex sync.Mutex + Logger + dynamicLevel zap.AtomicLevel + // disable presents the logger state. if disable is true, the logger will write nothing + // the default value is false + disable bool +} + +// Logger +type Logger interface { + Info(args ...interface{}) + Warn(args ...interface{}) + Error(args ...interface{}) + Debug(args ...interface{}) + + Infof(fmt string, args ...interface{}) + Warnf(fmt string, args ...interface{}) + Errorf(fmt string, args ...interface{}) + Debugf(fmt string, args ...interface{}) +} + +func init() { + // only use in test case, so just load default config + if logger == nil { + InitLogger(nil) + } +} + +// InitLog load from config path +func InitLog(logConfFile string) error { + if logConfFile == "" { + InitLogger(nil) + return perrors.New("log configure file name is nil") + } + if path.Ext(logConfFile) != ".yml" { + InitLogger(nil) + return perrors.New(fmt.Sprintf("log configure file name %s suffix must be .yml", logConfFile)) + } + + confFileStream, err := os.ReadFile(logConfFile) + if err != nil { + InitLogger(nil) + return perrors.New(fmt.Sprintf("os.ReadFile file:%s, error:%v", logConfFile, err)) + } + + conf := &zap.Config{} + err = yaml.UnmarshalYML(confFileStream, conf) + if err != nil { + InitLogger(nil) + return perrors.New(fmt.Sprintf("[Unmarshal]init logger error: %v", err)) + } + + InitLogger(conf) + + return nil +} + +func InitLogger(conf *zap.Config) { + var zapLoggerConfig zap.Config + if conf == nil { + zapLoggerConfig = zap.NewDevelopmentConfig() + zapLoggerEncoderConfig := zapcore.EncoderConfig{ + TimeKey: "time", + LevelKey: "level", + NameKey: "logger", + CallerKey: "caller", + MessageKey: "message", + StacktraceKey: "stacktrace", + EncodeLevel: zapcore.CapitalColorLevelEncoder, + EncodeTime: zapcore.ISO8601TimeEncoder, + EncodeDuration: zapcore.SecondsDurationEncoder, + EncodeCaller: zapcore.ShortCallerEncoder, + } + zapLoggerConfig.EncoderConfig = zapLoggerEncoderConfig + } else { + zapLoggerConfig = *conf + } + zapLogger, _ := zapLoggerConfig.Build(zap.AddCallerSkip(1)) + // logger = zapLogger.Sugar() + logger = &DubbogoPXLogger{Logger: zapLogger.Sugar(), dynamicLevel: zapLoggerConfig.Level} +} + +func SetLogger(log Logger) { + logger = log +} + +func GetLogger() Logger { + return logger +} + +func SetLoggerLevel(level string) bool { + if l, ok := logger.(OpsLogger); ok { + l.SetLoggerLevel(level) + return true + } + return false +} + +type OpsLogger interface { + Logger + // SetLoggerLevel function as name + SetLoggerLevel(level string) +} + +// SetLoggerLevel ... +func (dpl *DubbogoPXLogger) SetLoggerLevel(level string) { + l := new(zapcore.Level) + l.Set(level) + dpl.dynamicLevel.SetLevel(*l) +} diff --git a/pixiu/pkg/logger/logger_test.go b/pkg/logger/logger_test.go similarity index 100% rename from pixiu/pkg/logger/logger_test.go rename to pkg/logger/logger_test.go diff --git a/pixiu/pkg/logger/logging.go b/pkg/logger/logging.go similarity index 100% rename from pixiu/pkg/logger/logging.go rename to pkg/logger/logging.go diff --git a/pixiu/pkg/model/adapter.go b/pkg/model/adapter.go similarity index 100% rename from pixiu/pkg/model/adapter.go rename to pkg/model/adapter.go diff --git a/pixiu/pkg/model/base.go b/pkg/model/base.go similarity index 100% rename from pixiu/pkg/model/base.go rename to pkg/model/base.go diff --git a/pkg/model/bootstrap.go b/pkg/model/bootstrap.go new file mode 100644 index 000000000..6e78aa637 --- /dev/null +++ b/pkg/model/bootstrap.go @@ -0,0 +1,187 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 model + +import ( + "time" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +// Bootstrap the door +type Bootstrap struct { + StaticResources StaticResources `yaml:"static_resources" json:"static_resources" mapstructure:"static_resources"` + DynamicResources *DynamicResources `yaml:"dynamic_resources" json:"dynamic_resources" mapstructure:"dynamic_resources"` + Metric Metric `yaml:"metric" json:"metric" mapstructure:"metric"` + Node *Node `yaml:"node" json:"node" mapstructure:"node"` + Trace *TracerConfig `yaml:"tracing" json:"tracing" mapstructure:"tracing"` + Wasm *WasmConfig `yaml:"wasm" json:"wasm" mapstructure:"wasm"` + Config *ConfigCenter `yaml:"config-center" json:"config-center" mapstructure:"config-center"` + // Third party dependency + Nacos *Nacos `yaml:"nacos" json:"nacos" mapstructure:"nacos"` +} + +// Node node info for dynamic identifier +type Node struct { + Cluster string `yaml:"cluster" json:"cluster" mapstructure:"cluster"` + Id string `yaml:"id" json:"id" mapstructure:"id"` +} + +// GetListeners +func (bs *Bootstrap) GetListeners() []*Listener { + return bs.StaticResources.Listeners +} + +func (bs *Bootstrap) GetStaticListeners() []*Listener { + return bs.StaticResources.Listeners +} + +// GetShutdownConfig +func (bs *Bootstrap) GetShutdownConfig() *ShutdownConfig { + if bs.StaticResources.ShutdownConfig == nil { + bs.StaticResources.ShutdownConfig = &ShutdownConfig{ + Timeout: "0s", + StepTimeout: "0s", + RejectPolicy: "immediacy", + } + } + return bs.StaticResources.ShutdownConfig +} + +// GetPprof +func (bs *Bootstrap) GetPprof() PprofConf { + return bs.StaticResources.PprofConf +} + +// ExistCluster +func (bs *Bootstrap) ExistCluster(name string) bool { + if len(bs.StaticResources.Clusters) > 0 { + for _, v := range bs.StaticResources.Clusters { + if v.Name == name { + return true + } + } + } + + return false +} + +// StaticResources +type StaticResources struct { + Listeners []*Listener `yaml:"listeners" json:"listeners" mapstructure:"listeners"` + Clusters []*ClusterConfig `yaml:"clusters" json:"clusters" mapstructure:"clusters"` + Adapters []*Adapter `yaml:"adapters" json:"adapters" mapstructure:"adapters"` + ShutdownConfig *ShutdownConfig `yaml:"shutdown_config" json:"shutdown_config" mapstructure:"shutdown_config"` + PprofConf PprofConf `yaml:"pprofConf" json:"pprofConf" mapstructure:"pprofConf"` +} + +// DynamicResources config the dynamic resource source +// +// "lds_config": "{...}", # config lister load source +// "cds_config": "{...}", # config cluster load source +// "ads_config": "{...}" +// "ada_config": "{...}" # config adaptor load source +type DynamicResources struct { + LdsConfig *ApiConfigSource `yaml:"lds_config" json:"lds_config" mapstructure:"lds_config"` + CdsConfig *ApiConfigSource `yaml:"cds_config" json:"cds_config" mapstructure:"cds_config"` + AdsConfig *ApiConfigSource `yaml:"ads_config" json:"ads_config" mapstructure:"ads_config"` +} + +// ShutdownConfig how to shutdown server. +type ShutdownConfig struct { + Timeout string `default:"0s" yaml:"timeout" json:"timeout,omitempty"` + StepTimeout string `default:"0s" yaml:"step_timeout" json:"step_timeout,omitempty"` + RejectPolicy string `default:"immediacy" yaml:"reject_policy" json:"reject_policy,omitempty"` +} + +// GetTimeoutOfShutdown +func (sdc *ShutdownConfig) GetTimeout() time.Duration { + result, err := time.ParseDuration(sdc.Timeout) + if err != nil { + defaultTimeout := 60 * time.Second + logger.Errorf("The Timeout configuration is invalid: %s, and we will use the default value: %s, err: %v", + sdc.Timeout, defaultTimeout.String(), err) + return defaultTimeout + } + return result +} + +// APIMetaConfig how to find api config, file or etcd etc. +type APIMetaConfig struct { + Address string `yaml:"address" json:"address,omitempty"` + APIConfigPath string `default:"/pixiu/config/api" yaml:"api_config_path" json:"api_config_path,omitempty" mapstructure:"api_config_path"` +} + +// TimeoutConfig the config of ConnectTimeout and RequestTimeout +type TimeoutConfig struct { + ConnectTimeoutStr string `default:"5s" yaml:"connect_timeout" json:"connect_timeout,omitempty"` // ConnectTimeout timeout for connect to cluster node + RequestTimeoutStr string `default:"10s" yaml:"request_timeout" json:"request_timeout,omitempty"` +} + +type Config struct { + Listeners []*Listener `yaml:"listeners" json:"listeners" mapstructure:"listeners"` + Clusters []*ClusterConfig `yaml:"clusters" json:"clusters" mapstructure:"clusters"` + Adapters []*Adapter `yaml:"adapters" json:"adapters" mapstructure:"adapters"` + ShutdownConfig *ShutdownConfig `yaml:"shutdown_config" json:"shutdown_config" mapstructure:"shutdown_config"` + PprofConf PprofConf `yaml:"pprofConf" json:"pprofConf" mapstructure:"pprofConf"` +} + +type Nacos struct { + ServerConfigs []*NacosServerConfig `yaml:"server_configs" json:"server-configs" mapstructure:"server_configs"` + ClientConfig *NacosClientConfig `yaml:"client-config" json:"client-config" mapstructure:"client_config"` + DataId string `yaml:"data-id" json:"data-id" mapstructure:"data_id" default:"pixiu.yaml"` + Group string `yaml:"group" json:"group" mapstructure:"group" default:"DEFAULT_GROUP"` +} + +type NacosServerConfig struct { + IpAddr string `json:"ip_addr,omitempty" yaml:"ip_addr" mapstructure:"ip_addr"` + Port uint64 `json:"port,omitempty" yaml:"port" mapstructure:"port"` + Scheme string `json:"scheme" yaml:"scheme" mapstructure:"scheme"` + ContextPath string `json:"contextPath" yaml:"contextPath" mapstructure:"contextPath"` +} + +type NacosClientConfig struct { + TimeoutMs uint64 `json:"timeout_ms,omitempty" yaml:"timeout_ms" mapstructure:"timeout_ms"` // timeout for requesting Nacos server, default value is 10000ms + ListenInterval uint64 `json:"listen_interval,omitempty" yaml:"listen_interval" mapstructure:"listen_interval"` // Deprecated + BeatInterval int64 `json:"beat_interval,omitempty" yaml:"beat_interval" mapstructure:"beat_interval"` // the time interval for sending beat to server,default value is 5000ms + NamespaceId string `json:"namespace_id,omitempty" yaml:"namespace_id" mapstructure:"namespace_id"` // the namespaceId of Nacos.When namespace is public, fill in the blank string here. + AppName string `json:"app_name,omitempty" yaml:"app_name" mapstructure:"app_name"` // the appName + Endpoint string `json:"endpoint,omitempty" yaml:"endpoint" mapstructure:"endpoint"` // the endpoint for get Nacos server addresses + RegionId string `json:"region_id,omitempty" yaml:"region_id" mapstructure:"region_id"` // the regionId for kms + AccessKey string `json:"access_key,omitempty" yaml:"access_key" mapstructure:"access_key"` // the AccessKey for kms + SecretKey string `json:"secret_key,omitempty" yaml:"secret_key" mapstructure:"secret_key"` // the SecretKey for kms + OpenKMS bool `json:"open_kms,omitempty" yaml:"open_kms" mapstructure:"open_kms"` // it's to open kms,default is false. https://help.aliyun.com/product/28933.html + CacheDir string `json:"cache_dir,omitempty" yaml:"cache_dir" mapstructure:"cache_dir" default:"/tmp/nacos/cache"` // the directory for persist nacos service info,default value is current path + UpdateThreadNum int `json:"update_thread_num,omitempty" yaml:"update_thread_num" mapstructure:"update_thread_num"` // the number of gorutine for update nacos service info,default value is 20 + NotLoadCacheAtStart bool `json:"not_load_cache_at_start,omitempty" yaml:"not_load_cache_at_start" mapstructure:"not_load_cache_at_start"` // not to load persistent nacos service info in CacheDir at start time + UpdateCacheWhenEmpty bool `json:"update_cache_when_empty,omitempty" yaml:"update_cache_when_empty" mapstructure:"update_cache_when_empty"` // update cache when get empty service instance from server + Username string `json:"username,omitempty" yaml:"username" mapstructure:"username"` // the username for nacos auth + Password string `json:"password,omitempty" yaml:"password" mapstructure:"password"` // the password for nacos auth + LogDir string `json:"log_dir,omitempty" yaml:"log_dir" mapstructure:"log_dir" default:"/tmp/nacos/log"` // the directory for log, default is current path + LogLevel string `json:"log_level,omitempty" yaml:"log_level" mapstructure:"log_level"` // the level of log, it's must be debug,info,warn,error, default value is info + //LogSampling *ClientLogSamplingConfig // the sampling config of log + ContextPath string `json:"context_path,omitempty" yaml:"context_path" mapstructure:"context_path"` // the nacos server contextpath + //LogRollingConfig *ClientLogRollingConfig // the log rolling config +} + +type ConfigCenter struct { + Type string `json:"type,omitempty" yaml:"type"` + Enable string `json:"enable" yaml:"enable"` +} diff --git a/pixiu/pkg/model/cluster.go b/pkg/model/cluster.go similarity index 100% rename from pixiu/pkg/model/cluster.go rename to pkg/model/cluster.go diff --git a/pixiu/pkg/model/filter.go b/pkg/model/filter.go similarity index 100% rename from pixiu/pkg/model/filter.go rename to pkg/model/filter.go diff --git a/pixiu/pkg/model/health.go b/pkg/model/health.go similarity index 100% rename from pixiu/pkg/model/health.go rename to pkg/model/health.go diff --git a/pkg/model/http.go b/pkg/model/http.go new file mode 100644 index 000000000..1cf0ac7e8 --- /dev/null +++ b/pkg/model/http.go @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 model + +import ( + "time" +) + +import ( + "github.com/mitchellh/mapstructure" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +// HttpConnectionManagerConfig +type HttpConnectionManagerConfig struct { + RouteConfig RouteConfiguration `yaml:"route_config" json:"route_config" mapstructure:"route_config"` + HTTPFilters []*HTTPFilter `yaml:"http_filters" json:"http_filters" mapstructure:"http_filters"` + ServerName string `yaml:"server_name" json:"server_name" mapstructure:"server_name"` + IdleTimeoutStr string `yaml:"idle_timeout" json:"idle_timeout" mapstructure:"idle_timeout"` + GenerateRequestID bool `yaml:"generate_request_id" json:"generate_request_id" mapstructure:"generate_request_id"` + TimeoutStr string `yaml:"timeout" json:"timeout" mapstructure:"timeout"` + Timeout time.Duration `yaml:"-" json:"-" mapstructure:"-"` +} + +// GRPCConnectionManagerConfig +type GRPCConnectionManagerConfig struct { + RouteConfig RouteConfiguration `yaml:"route_config" json:"route_config" mapstructure:"route_config"` + TimeoutStr string `yaml:"timeout" json:"timeout" mapstructure:"timeout"` + Timeout time.Duration `yaml:"-" json:"-" mapstructure:"-"` +} + +// DubboProxyConnectionManagerConfig +type DubboProxyConnectionManagerConfig struct { + RouteConfig RouteConfiguration `yaml:"route_config" json:"route_config" mapstructure:"route_config"` + DubboFilters []*DubboFilter `yaml:"dubbo_filters" json:"dubbo_filters" mapstructure:"dubbo_filters"` + TimeoutStr string `yaml:"timeout" json:"timeout" mapstructure:"timeout"` + Timeout time.Duration `yaml:"-" json:"-" mapstructure:"-"` +} + +// HTTPFilter http filter +type HTTPFilter struct { + Name string `yaml:"name" json:"name" mapstructure:"name"` + Config map[string]interface{} `yaml:"config" json:"config" mapstructure:"config"` +} + +// DubboFilter dubbo filter +type DubboFilter struct { + Name string `yaml:"name" json:"name" mapstructure:"name"` + Config map[string]interface{} `yaml:"config" json:"config" mapstructure:"config"` +} + +type RequestMethod int32 + +const ( + METHOD_UNSPECIFIED = 0 + iota // (DEFAULT) + GET + HEAD + POST + PUT + DELETE + CONNECT + OPTIONS + TRACE +) + +var RequestMethodName = map[int32]string{ + 0: "METHOD_UNSPECIFIED", + 1: "GET", + 2: "HEAD", + 3: "POST", + 4: "PUT", + 5: "DELETE", + 6: "CONNECT", + 7: "OPTIONS", + 8: "TRACE", +} + +var RequestMethodValue = map[string]int32{ + "METHOD_UNSPECIFIED": 0, + "GET": 1, + "HEAD": 2, + "POST": 3, + "PUT": 4, + "DELETE": 5, + "CONNECT": 6, + "OPTIONS": 7, + "TRACE": 8, +} + +// HttpConfig the http config +type HttpConfig struct { + IdleTimeoutStr string `yaml:"idle_timeout" json:"idle_timeout" mapstructure:"idle_timeout"` + ReadTimeoutStr string `json:"read_timeout,omitempty" yaml:"read_timeout,omitempty" mapstructure:"read_timeout"` + WriteTimeoutStr string `json:"write_timeout,omitempty" yaml:"write_timeout,omitempty" mapstructure:"write_timeout"` + MaxHeaderBytes int `json:"max_header_bytes,omitempty" yaml:"max_header_bytes,omitempty" mapstructure:"max_header_bytes"` +} + +func MapInStruct(cfg interface{}) *HttpConfig { + var hc *HttpConfig + if cfg != nil { + if ok := mapstructure.Decode(cfg, &hc); ok != nil { + logger.Error("Config error", ok) + } + } + return hc +} diff --git a/pixiu/pkg/model/lb.go b/pkg/model/lb.go similarity index 100% rename from pixiu/pkg/model/lb.go rename to pkg/model/lb.go diff --git a/pixiu/pkg/model/listener.go b/pkg/model/listener.go similarity index 100% rename from pixiu/pkg/model/listener.go rename to pkg/model/listener.go diff --git a/pixiu/pkg/model/match.go b/pkg/model/match.go similarity index 100% rename from pixiu/pkg/model/match.go rename to pkg/model/match.go diff --git a/pixiu/pkg/model/metric.go b/pkg/model/metric.go similarity index 100% rename from pixiu/pkg/model/metric.go rename to pkg/model/metric.go diff --git a/pixiu/pkg/model/pprof.go b/pkg/model/pprof.go similarity index 100% rename from pixiu/pkg/model/pprof.go rename to pkg/model/pprof.go diff --git a/pixiu/pkg/model/remote.go b/pkg/model/remote.go similarity index 100% rename from pixiu/pkg/model/remote.go rename to pkg/model/remote.go diff --git a/pkg/model/router.go b/pkg/model/router.go new file mode 100644 index 000000000..7c1927f13 --- /dev/null +++ b/pkg/model/router.go @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 model + +import ( + stdHttp "net/http" + "regexp" +) + +import ( + "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/router/trie" + "github.com/apache/dubbo-go-pixiu/pkg/common/util/stringutil" +) + +// Router struct +type ( + Router struct { + ID string `yaml:"id" json:"id" mapstructure:"id"` + Match RouterMatch `yaml:"match" json:"match" mapstructure:"match"` + Route RouteAction `yaml:"route" json:"route" mapstructure:"route"` + } + + // RouterMatch + RouterMatch struct { + Prefix string `yaml:"prefix" json:"prefix" mapstructure:"prefix"` + Path string `yaml:"path" json:"path" mapstructure:"path"` + // Regex string `yaml:"regex" json:"regex" mapstructure:"regex"` TODO: next version + Methods []string `yaml:"methods" json:"methods" mapstructure:"methods"` + Headers []HeaderMatcher `yaml:"headers,omitempty" json:"headers,omitempty" mapstructure:"headers"` + // pathRE *regexp.Regexp + } + + // RouteAction match route should do + RouteAction struct { + Cluster string `yaml:"cluster" json:"cluster" mapstructure:"cluster"` + ClusterNotFoundResponseCode int `yaml:"cluster_not_found_response_code" json:"cluster_not_found_response_code" mapstructure:"cluster_not_found_response_code"` + } + + // RouteConfiguration + RouteConfiguration struct { + RouteTrie trie.Trie `yaml:"-" json:"-" mapstructure:"-"` + Routes []*Router `yaml:"routes" json:"routes" mapstructure:"routes"` + Dynamic bool `yaml:"dynamic" json:"dynamic" mapstructure:"dynamic"` + } + + // HeaderMatcher include Name header key, Values header value, Regex regex value + HeaderMatcher struct { + Name string `yaml:"name" json:"name" mapstructure:"name"` + Values []string `yaml:"values" json:"values" mapstructure:"values"` + Regex bool `yaml:"regex" json:"regex" mapstructure:"regex"` + valueRE *regexp.Regexp + } +) + +func NewRouterMatchPrefix(name string) RouterMatch { + return RouterMatch{Prefix: "/" + name + "/"} +} + +func (rc *RouteConfiguration) RouteByPathAndMethod(path, method string) (*RouteAction, error) { + if rc.RouteTrie.IsEmpty() { + return nil, errors.Errorf("router configuration is empty") + } + + node, _, _ := rc.RouteTrie.Match(stringutil.GetTrieKey(method, path)) + if node == nil { + return nil, errors.Errorf("route failed for %s, no rules matched.", stringutil.GetTrieKey(method, path)) + } + if node.GetBizInfo() == nil { + return nil, errors.Errorf("action is nil. please check your configuration.") + } + ret := (node.GetBizInfo()).(RouteAction) + + return &ret, nil +} + +func (rc *RouteConfiguration) Route(req *stdHttp.Request) (*RouteAction, error) { + return rc.RouteByPathAndMethod(req.URL.Path, req.Method) +} + +// MatchHeader used when there's only headers to match +func (rm *RouterMatch) MatchHeader(req *stdHttp.Request) bool { + if len(rm.Methods) > 0 { + for _, method := range rm.Methods { + if method == req.Method { + goto HEADER + } + } + return false + } +HEADER: + for _, header := range rm.Headers { + if val := req.Header.Get(header.Name); len(val) > 0 { + if header.MatchValues(val) { + return true + } + } + } + return false +} + +// MatchValues match values in header, including regex type +func (hm *HeaderMatcher) MatchValues(dst string) bool { + if hm.Regex && hm.valueRE != nil { + return hm.valueRE.MatchString(dst) + } + + for _, src := range hm.Values { + if src == dst { + return true + } + } + return false +} + +// SetValueRegex compile the regex, disable regex if it failed +func (hm *HeaderMatcher) SetValueRegex(regex string) error { + r, err := regexp.Compile(regex) + if err == nil { + hm.valueRE = r + return nil + } + hm.Regex = false + return err +} diff --git a/pixiu/pkg/model/trace.go b/pkg/model/trace.go similarity index 100% rename from pixiu/pkg/model/trace.go rename to pkg/model/trace.go diff --git a/pixiu/pkg/model/tracing.go b/pkg/model/tracing.go similarity index 100% rename from pixiu/pkg/model/tracing.go rename to pkg/model/tracing.go diff --git a/pixiu/pkg/model/wasm.go b/pkg/model/wasm.go similarity index 100% rename from pixiu/pkg/model/wasm.go rename to pkg/model/wasm.go diff --git a/pkg/network/id.go b/pkg/network/id.go deleted file mode 100644 index 6e2aa5936..000000000 --- a/pkg/network/id.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package network - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/util/identifier" -) - -// ID is the unique identifier for a network. -type ID string - -func (id ID) Equals(other ID) bool { - return identifier.IsSameOrEmpty(string(id), string(other)) -} - -func (id ID) String() string { - return string(id) -} diff --git a/pixiu/pkg/pluginregistry/proxywasm_register.go b/pkg/pluginregistry/proxywasm_register.go similarity index 92% rename from pixiu/pkg/pluginregistry/proxywasm_register.go rename to pkg/pluginregistry/proxywasm_register.go index 9d74a2daa..ff6319e10 100644 --- a/pixiu/pkg/pluginregistry/proxywasm_register.go +++ b/pkg/pluginregistry/proxywasm_register.go @@ -20,5 +20,5 @@ package pluginregistry import ( - _ "github.com/apache/dubbo-go-pixiu/pixiu/pkg/filter/http/proxywasm" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/http/proxywasm" ) diff --git a/pkg/pluginregistry/registry.go b/pkg/pluginregistry/registry.go new file mode 100644 index 000000000..a65c29190 --- /dev/null +++ b/pkg/pluginregistry/registry.go @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 pluginregistry + +import ( + _ "github.com/apache/dubbo-go-pixiu/pkg/adapter/dubboregistry" + _ "github.com/apache/dubbo-go-pixiu/pkg/adapter/springcloud" + _ "github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer/maglev" + _ "github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer/rand" + _ "github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer/ringhash" + _ "github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer/roundrobin" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/accesslog" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/auth/jwt" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/authority" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/cors" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/csrf" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/failinject" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/header" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/host" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/http/apiconfig" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/http/dubboproxy" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/http/grpcproxy" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/http/httpproxy" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/http/loadbalancer" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/http/proxyrewrite" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/http/remote" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/metric" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/network/dubboproxy" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/network/dubboproxy/filter/http" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/network/dubboproxy/filter/proxy" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/network/grpcconnectionmanager" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/network/httpconnectionmanager" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/prometheus" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/seata" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/tracing" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/traffic" + _ "github.com/apache/dubbo-go-pixiu/pkg/listener/http" + _ "github.com/apache/dubbo-go-pixiu/pkg/listener/http2" + _ "github.com/apache/dubbo-go-pixiu/pkg/listener/tcp" + _ "github.com/apache/dubbo-go-pixiu/pkg/listener/triple" +) diff --git a/pkg/pool/pool.go b/pkg/pool/pool.go new file mode 100644 index 000000000..927287511 --- /dev/null +++ b/pkg/pool/pool.go @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 pool + +import ( + "errors" + "sync" +) + +import ( + "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/client" + "github.com/apache/dubbo-go-pixiu/pkg/client/dubbo" + "github.com/apache/dubbo-go-pixiu/pkg/client/http" +) + +// ClientPool a pool of client. +type ClientPool struct { + poolMap map[config.RequestType]*sync.Pool +} + +var ( + clientPool *ClientPool + once = sync.Once{} +) + +func newClientPool() *ClientPool { + clientPool := &ClientPool{ + poolMap: make(map[config.RequestType]*sync.Pool, 4), + } + // init default support request type + clientPool.poolMap[config.DubboRequest] = &sync.Pool{ + New: func() interface{} { + return dubbo.NewDubboClient() + }, + } + clientPool.poolMap[config.HTTPRequest] = &sync.Pool{ + New: func() interface{} { + return http.NewHTTPClient() + }, + } + return clientPool +} + +// SingletonPool singleton pool +func SingletonPool() *ClientPool { + if clientPool == nil { + once.Do(func() { + clientPool = newClientPool() + }) + } + + return clientPool +} + +// GetClient a factory method to get a client according to apiType . +func (pool *ClientPool) GetClient(t config.RequestType) (client.Client, error) { + if pool.poolMap[t] != nil { + return pool.poolMap[t].Get().(client.Client), nil + } + return nil, errors.New("protocol not supported yet") +} + +// Put put client to pool. +func (pool *ClientPool) Put(t config.RequestType, c client.Client) error { + if pool.poolMap[t] != nil { + pool.poolMap[t].Put(c) + return nil + } + + return errors.New("protocol not supported yet") +} diff --git a/pkg/prometheus/prometheus.go b/pkg/prometheus/prometheus.go new file mode 100644 index 000000000..058d4928d --- /dev/null +++ b/pkg/prometheus/prometheus.go @@ -0,0 +1,390 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 prometheus + +import ( + "bytes" + "errors" + "net/http" + "os" + "strconv" + "sync" + "time" +) + +import ( + "github.com/dubbo-go-pixiu/pixiu-api/pkg/context" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/expfmt" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/client" + contextHttp "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +var defaultSubsystem = "pixiu" + +type ContextHandlerFunc func(c *contextHttp.HttpContext) error + +const ( + _ = iota // ignore first value by assigning to blank identifier + KB float64 = 1 << (10 * iota) + MB + GB + TB +) + +type FavContextKeyType string + +type Metric struct { + MetricCollector prometheus.Collector + ID string + Name string + Description string + Type string + Args []string + Buckets []float64 +} + +// reqDurBuckets is the buckets for request duration. Here, we use the prometheus defaults +// which are for ~10s request length max: []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10} +var reqDurBuckets = prometheus.DefBuckets + +// reqSzBuckets is the buckets for request size. Here we define a spectrom from 1KB thru 1NB up to 10MB. +var reqSzBuckets = []float64{1.0 * KB, 2.0 * KB, 5.0 * KB, 10.0 * KB, 100 * KB, 500 * KB, 1.0 * MB, 2.5 * MB, 5.0 * MB, 10.0 * MB} + +// resSzBuckets is the buckets for response size. Here we define a spectrom from 1KB thru 1NB up to 10MB. +var resSzBuckets = []float64{1.0 * KB, 2.0 * KB, 5.0 * KB, 10.0 * KB, 100 * KB, 500 * KB, 1.0 * MB, 2.5 * MB, 5.0 * MB, 10.0 * MB} + +// Standard default metrics +// counter, counter_vec, gauge, gauge_vec, +// histogram, histogram_vec, summary, summary_vec + +var reqCnt = &Metric{ + ID: "reqCnt", + Name: "requests_total", + Description: "How many HTTP requests processed, partitioned by status code and HTTP method.", + Type: "counter_vec", + Args: []string{"code", "method", "host", "url"}, +} + +var reqDur = &Metric{ + ID: "reqDur", + Name: "request_duration_seconds", + Description: "The HTTP request latencies in seconds.", + Args: []string{"code", "method", "url"}, + Type: "histogram_vec", + Buckets: reqDurBuckets, +} + +var resSz = &Metric{ + ID: "resSz", + Name: "response_size_bytes", + Description: "The HTTP response sizes in bytes.", + Args: []string{"code", "method", "url"}, + Type: "histogram_vec", + Buckets: resSzBuckets, +} + +var reqSz = &Metric{ + ID: "reqSz", + Name: "request_size_bytes", + Description: "The HTTP request sizes in bytes.", + Args: []string{"code", "method", "url"}, + Type: "histogram_vec", + Buckets: reqSzBuckets, +} + +var standardMetrics = []*Metric{ + reqCnt, + reqDur, + resSz, + reqSz, +} + +// NewMetric associates prometheus.Collector based on Metric.Type +func NewMetric(m *Metric, subsystem string) prometheus.Collector { + var metric prometheus.Collector + switch m.Type { + case "counter_vec": + metric = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Subsystem: subsystem, + Name: m.Name, + Help: m.Description, + }, + m.Args, + ) + case "counter": + metric = prometheus.NewCounter( + prometheus.CounterOpts{ + Subsystem: subsystem, + Name: m.Name, + Help: m.Description, + }, + ) + case "gauge_vec": + metric = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Subsystem: subsystem, + Name: m.Name, + Help: m.Description, + }, + m.Args, + ) + case "gauge": + metric = prometheus.NewGauge( + prometheus.GaugeOpts{ + Subsystem: subsystem, + Name: m.Name, + Help: m.Description, + }, + ) + case "histogram_vec": + metric = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Subsystem: subsystem, + Name: m.Name, + Help: m.Description, + Buckets: m.Buckets, + }, + m.Args, + ) + case "histogram": + metric = prometheus.NewHistogram( + prometheus.HistogramOpts{ + Subsystem: subsystem, + Name: m.Name, + Help: m.Description, + Buckets: m.Buckets, + }, + ) + case "summary_vec": + metric = prometheus.NewSummaryVec( + prometheus.SummaryOpts{ + Subsystem: subsystem, + Name: m.Name, + Help: m.Description, + }, + m.Args, + ) + case "summary": + metric = prometheus.NewSummary( + prometheus.SummaryOpts{ + Subsystem: subsystem, + Name: m.Name, + Help: m.Description, + }, + ) + } + return metric +} + +type RequestCounterLabelMappingFunc func(c *contextHttp.HttpContext) string + +type Prometheus struct { + reqCnt *prometheus.CounterVec + reqDur, reqSz, resSz *prometheus.HistogramVec + Ppg PushGateway + + MetricsList []*Metric + MetricsPath string + Subsystem string + + RequestCounterURLLabelMappingFunc RequestCounterLabelMappingFunc + RequestCounterHostLabelMappingFunc RequestCounterLabelMappingFunc + + URLLabelFromContext string + Datacontext context.Context +} + +// PushGateway contains the configuration for pushing to a Prometheus pushgateway (optional) +type PushGateway struct { + CounterPush bool + PushIntervalSeconds time.Duration + PushIntervalThreshold int + PushGatewayURL string + Job string + counter int + mutex sync.RWMutex +} + +// NewPrometheus generates a new set of metrics with a certain subsystem name +func NewPrometheus() *Prometheus { + var metricsList []*Metric + metricsList = append(metricsList, standardMetrics...) + p := &Prometheus{ + MetricsList: metricsList, + Subsystem: defaultSubsystem, + RequestCounterURLLabelMappingFunc: func(c *contextHttp.HttpContext) string { + return c.GetUrl() + }, + RequestCounterHostLabelMappingFunc: func(c *contextHttp.HttpContext) string { + return c.Request.Host + }, + } + p.registerMetrics() + return p +} + +func (p *Prometheus) registerMetrics() { + for _, metricDef := range p.MetricsList { + metric := NewMetric(metricDef, p.Subsystem) + if err := prometheus.Register(metric); err != nil { + logger.Errorf("%s could not be registered in Prometheus: %v", metricDef.Name, err) + } + switch metricDef { + + case reqCnt: + p.reqCnt = metric.(*prometheus.CounterVec) + case reqDur: + p.reqDur = metric.(*prometheus.HistogramVec) + case resSz: + p.resSz = metric.(*prometheus.HistogramVec) + case reqSz: + p.reqSz = metric.(*prometheus.HistogramVec) + } + metricDef.MetricCollector = metric + } +} + +func (p *Prometheus) SetPushGatewayUrl(pushGatewayURL, metricspath string) { + + p.Ppg.PushGatewayURL = pushGatewayURL + p.MetricsPath = metricspath +} + +func (p *Prometheus) SetPushIntervalThreshold(isTurn bool, pushIntervalThreshold int) { + p.Ppg.CounterPush = isTurn + p.Ppg.PushIntervalThreshold = pushIntervalThreshold +} + +func (p *Prometheus) SetPushGatewayJob(j string) { + p.Ppg.Job = j +} + +func (p *Prometheus) startPushCounter() { + if p.Ppg.counter >= p.Ppg.PushIntervalThreshold { + go p.sendMetricsToPushGateway(p.getMetrics()) + p.Ppg.counter = 0 + } +} + +func (p *Prometheus) SetPushGateway() { + if p.Ppg.CounterPush { + p.startPushCounter() + } +} + +func (p *Prometheus) getMetrics() []byte { + out := &bytes.Buffer{} + metricFamilies, err := prometheus.DefaultGatherer.Gather() + if err != nil { + logger.Errorf("prometheus.DefaultGatherer.Gather error: %v", err) + return []byte{} + } + for i := range metricFamilies { + _, err := expfmt.MetricFamilyToText(out, metricFamilies[i]) + if err != nil { + logger.Errorf("failed to converts a MetricFamily proto message into text format %v", err) + } + } + return out.Bytes() +} + +func (p *Prometheus) sendMetricsToPushGateway(metrics []byte) { + req, err := http.NewRequest(http.MethodPost, p.getPushGatewayURL(), bytes.NewBuffer(metrics)) + if err != nil { + logger.Errorf("failed to create push gateway request: %v", err) + return + } + if _, err = (&http.Client{}).Do(req); err != nil { + logger.Errorf("Error sending to push gateway: %v", err) + } +} + +func (p *Prometheus) getPushGatewayURL() string { + h, _ := os.Hostname() + if p.Ppg.Job == "" { + p.Ppg.Job = "pixiu" + } + return p.Ppg.PushGatewayURL + p.MetricsPath + "/job/" + p.Ppg.Job + "/instance/" + h +} + +// HandlerFunc defines handler function for middleware +func (p *Prometheus) HandlerFunc() ContextHandlerFunc { + return func(c *contextHttp.HttpContext) error { + start := time.Now() + reqSz, err1 := computeApproximateRequestSize(c.Request) + //fmt.Println("reqSz", reqSz) + elapsed := float64(time.Since(start)) / float64(time.Second) + //fmt.Println("elapsed ", elapsed) + url := p.RequestCounterURLLabelMappingFunc(c) + //fmt.Println("url ", url) + statusStr := strconv.Itoa(c.GetStatusCode()) + //fmt.Println("statusStr", statusStr) + method := c.GetMethod() + //fmt.Println("method ", method) + p.reqDur.WithLabelValues(statusStr, method, url).Observe(elapsed) + p.reqCnt.WithLabelValues(statusStr, method, p.RequestCounterHostLabelMappingFunc(c), url).Inc() + if err1 == nil { + p.reqSz.WithLabelValues(statusStr, method, url).Observe(float64(reqSz)) + } + resSz, err2 := computeApproximateResponseSize(c.TargetResp) + if err2 == nil { + p.resSz.WithLabelValues(statusStr, method, url).Observe(float64(resSz)) + } + p.Ppg.mutex.Lock() + p.Ppg.counter = p.Ppg.counter + 1 + defer p.Ppg.mutex.Unlock() + p.SetPushGateway() + return nil + } +} + +func computeApproximateRequestSize(r *http.Request) (int, error) { + if r == nil { + return 0, errors.New("http.Request is null pointer ") + } + s := 0 + if r.URL != nil { + s = len(r.URL.Path) + } + s += len(r.Method) + s += len(r.Proto) + for name, values := range r.Header { + s += len(name) + for _, value := range values { + s += len(value) + } + } + s += len(r.Host) + if r.ContentLength != -1 { + s += int(r.ContentLength) + } + return s, nil +} + +func computeApproximateResponseSize(res *client.Response) (int, error) { + if res == nil { + return 0, errors.New("client.Response is null pointer ") + } + return len(res.Data), nil +} diff --git a/pkg/proto/merge/merge.go b/pkg/proto/merge/merge.go deleted file mode 100644 index 05b6fa440..000000000 --- a/pkg/proto/merge/merge.go +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package merge - -/* - CODE Copied and modified from https://github.com/kumahq/kuma/blob/master/pkg/util/proto/google_proto.go - because of: https://github.com/golang/protobuf/issues/1359 - - Copyright 2019 The Go Authors. All rights reserved. - Use of this source code is governed by a BSD-style - license that can be found in the LICENSE file. -*/ - -import ( - "fmt" -) - -import ( - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" - "google.golang.org/protobuf/types/known/durationpb" -) - -type ( - MergeFunction func(dst, src protoreflect.Message) - mergeOptions struct { - customMergeFn map[protoreflect.FullName]MergeFunction - } -) -type OptionFn func(options mergeOptions) mergeOptions - -func MergeFunctionOptionFn(name protoreflect.FullName, function MergeFunction) OptionFn { - return func(options mergeOptions) mergeOptions { - options.customMergeFn[name] = function - return options - } -} - -// ReplaceMergeFn instead of merging all subfields one by one, takes src and set it to dest -var ReplaceMergeFn MergeFunction = func(dst, src protoreflect.Message) { - dst.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { - dst.Clear(fd) - return true - }) - src.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { - dst.Set(fd, v) - return true - }) -} - -var options = []OptionFn{ - // Workaround https://github.com/golang/protobuf/issues/1359, merge duration properly - MergeFunctionOptionFn((&durationpb.Duration{}).ProtoReflect().Descriptor().FullName(), ReplaceMergeFn), -} - -func Merge(dst, src proto.Message) { - merge(dst, src, options...) -} - -// Merge Code of proto.Merge with modifications to support custom types -func merge(dst, src proto.Message, opts ...OptionFn) { - mo := mergeOptions{customMergeFn: map[protoreflect.FullName]MergeFunction{}} - for _, opt := range opts { - mo = opt(mo) - } - dstMsg, srcMsg := dst.ProtoReflect(), src.ProtoReflect() - if dstMsg.Descriptor() != srcMsg.Descriptor() { - if got, want := dstMsg.Descriptor().FullName(), srcMsg.Descriptor().FullName(); got != want { - panic(fmt.Sprintf("descriptor mismatch: %v != %v", got, want)) - } - panic("descriptor mismatch") - } - mo.mergeMessage(dstMsg, srcMsg) -} - -func (o mergeOptions) mergeMessage(dst, src protoreflect.Message) { - // The regular proto.mergeMessage would have a fast path method option here. - // As we want to have exceptions we always use the slow path. - if !dst.IsValid() { - panic(fmt.Sprintf("cannot merge into invalid %v message", dst.Descriptor().FullName())) - } - - src.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { - switch { - case fd.IsList(): - o.mergeList(dst.Mutable(fd).List(), v.List(), fd) - case fd.IsMap(): - o.mergeMap(dst.Mutable(fd).Map(), v.Map(), fd.MapValue()) - case fd.Message() != nil: - mergeFn, exists := o.customMergeFn[fd.Message().FullName()] - if exists { - mergeFn(dst.Mutable(fd).Message(), v.Message()) - } else { - o.mergeMessage(dst.Mutable(fd).Message(), v.Message()) - } - case fd.Kind() == protoreflect.BytesKind: - dst.Set(fd, o.cloneBytes(v)) - default: - dst.Set(fd, v) - } - return true - }) - - if len(src.GetUnknown()) > 0 { - dst.SetUnknown(append(dst.GetUnknown(), src.GetUnknown()...)) - } -} - -func (o mergeOptions) mergeList(dst, src protoreflect.List, fd protoreflect.FieldDescriptor) { - // Merge semantics appends to the end of the existing list. - for i, n := 0, src.Len(); i < n; i++ { - switch v := src.Get(i); { - case fd.Message() != nil: - dstv := dst.NewElement() - o.mergeMessage(dstv.Message(), v.Message()) - dst.Append(dstv) - case fd.Kind() == protoreflect.BytesKind: - dst.Append(o.cloneBytes(v)) - default: - dst.Append(v) - } - } -} - -func (o mergeOptions) mergeMap(dst, src protoreflect.Map, fd protoreflect.FieldDescriptor) { - // Merge semantics replaces, rather than merges into existing entries. - src.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool { - switch { - case fd.Message() != nil: - dstv := dst.NewValue() - o.mergeMessage(dstv.Message(), v.Message()) - dst.Set(k, dstv) - case fd.Kind() == protoreflect.BytesKind: - dst.Set(k, o.cloneBytes(v)) - default: - dst.Set(k, v) - } - return true - }) -} - -func (o mergeOptions) cloneBytes(v protoreflect.Value) protoreflect.Value { - return protoreflect.ValueOfBytes(append([]byte{}, v.Bytes()...)) -} diff --git a/pkg/proto/types.go b/pkg/proto/types.go deleted file mode 100644 index 7be12257c..000000000 --- a/pkg/proto/types.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package proto - -import ( - wrappers "google.golang.org/protobuf/types/known/wrapperspb" -) - -var ( - // BoolTrue is a bool true pointer of types.BoolValue. - BoolTrue = &wrappers.BoolValue{ - Value: true, - } - - // BoolFalse is a bool false pointer of types.BoolValue. - BoolFalse = &wrappers.BoolValue{ - Value: false, - } -) diff --git a/pkg/proxy/proxyinfo.go b/pkg/proxy/proxyinfo.go deleted file mode 100644 index d43834c6f..000000000 --- a/pkg/proxy/proxyinfo.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package proxy - -import ( - "context" - "encoding/json" - "fmt" -) - -import ( - istioVersion "istio.io/pkg/version" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/xds" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -type sidecarSyncStatus struct { - // nolint: structcheck, unused - pilot string - xds.SyncStatus -} - -// GetProxyInfo retrieves infos of proxies that connect to the Istio control plane of specific revision. -func GetProxyInfo(kubeconfig, configContext, revision, istioNamespace string) (*[]istioVersion.ProxyInfo, error) { - kubeClient, err := kube.NewExtendedClient(kube.BuildClientCmd(kubeconfig, configContext), revision) - if err != nil { - return nil, err - } - // Ask Pilot for the Envoy sidecar sync status, which includes the sidecar version info - allSyncz, err := kubeClient.AllDiscoveryDo(context.TODO(), istioNamespace, "/debug/syncz") - if err != nil { - return nil, err - } - - pi := []istioVersion.ProxyInfo{} - for _, syncz := range allSyncz { - var sss []*sidecarSyncStatus - err = json.Unmarshal(syncz, &sss) - if err != nil { - return nil, err - } - - for _, ss := range sss { - pi = append(pi, istioVersion.ProxyInfo{ - ID: ss.ProxyID, - IstioVersion: ss.SyncStatus.IstioVersion, - }) - } - } - - return &pi, nil -} - -// GetIDsFromProxyInfo is a helper function to retrieve list of IDs from Proxy. -func GetIDsFromProxyInfo(kubeconfig, configContext, revision, istioNamespace string) ([]string, error) { - var IDs []string - pi, err := GetProxyInfo(kubeconfig, configContext, revision, istioNamespace) - if err != nil { - return IDs, fmt.Errorf("failed to get proxy infos: %v", err) - } - for _, pi := range *pi { - IDs = append(IDs, pi.ID) - } - return IDs, nil -} diff --git a/pkg/queue/delay.go b/pkg/queue/delay.go deleted file mode 100644 index 933038e9e..000000000 --- a/pkg/queue/delay.go +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright 2017 Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package queue - -import ( - "container/heap" - "runtime" - "sync" - "time" -) - -import ( - "istio.io/pkg/log" -) - -type delayTask struct { - do func() error - runAt time.Time - retries int -} - -const maxTaskRetry = 3 - -var _ heap.Interface = &pq{} - -// pq implements an internal priority queue so that tasks with the soonest expiry will be run first. -// Methods on pq are not threadsafe, access should be protected. -// much of this is taken from the example at https://golang.org/pkg/container/heap/ -type pq []*delayTask - -func (q pq) Len() int { - return len(q) -} - -func (q pq) Less(i, j int) bool { - return q[i].runAt.Before(q[j].runAt) -} - -func (q *pq) Swap(i, j int) { - (*q)[i], (*q)[j] = (*q)[j], (*q)[i] -} - -func (q *pq) Push(x interface{}) { - *q = append(*q, x.(*delayTask)) -} - -func (q *pq) Pop() interface{} { - old := *q - n := len(old) - c := cap(old) - // Shrink the capacity of task queue. - if n < c/2 && c > 32 { - npq := make(pq, n, c/2) - copy(npq, old) - old = npq - } - if n == 0 { - return nil - } - item := old[n-1] - old[n-1] = nil // avoid memory leak - *q = old[0 : n-1] - return item -} - -// Peek is not managed by the container/heap package, so we return the 0th element in the list. -func (q *pq) Peek() interface{} { - if q.Len() < 1 { - return nil - } - return (*q)[0] -} - -// Delayed implements queue such that tasks are executed after a specified delay. -type Delayed interface { - Instance - PushDelayed(t Task, delay time.Duration) -} - -var _ Delayed = &delayQueue{} - -// DelayQueueOption configure the behavior of the queue. Must be applied before Run. -type DelayQueueOption func(*delayQueue) - -// DelayQueueBuffer sets maximum number of tasks awaiting execution. If this limit is reached, Push and PushDelayed -// will block until there is room. -func DelayQueueBuffer(bufferSize int) DelayQueueOption { - return func(queue *delayQueue) { - if queue.enqueue != nil { - close(queue.enqueue) - } - queue.enqueue = make(chan *delayTask, bufferSize) - } -} - -// DelayQueueWorkers sets the number of background worker goroutines await tasks to execute. Effectively the -// maximum number of concurrent tasks. -func DelayQueueWorkers(workers int) DelayQueueOption { - return func(queue *delayQueue) { - queue.workers = workers - } -} - -// workerChanBuf determines whether the channel of a worker should be a buffered channel -// to get the best performance. -var workerChanBuf = func() int { - // Use blocking channel if GOMAXPROCS=1. - // This switches context from sender to receiver immediately, - // which results in higher performance. - var n int - if n = runtime.GOMAXPROCS(0); n == 1 { - return 0 - } - - // Make channel non-blocking and set up its capacity with GOMAXPROCS if GOMAXPROCS>1, - // otherwise the sender might be dragged down if the receiver is CPU-bound. - // - // GOMAXPROCS determines how many goroutines can run in parallel, - // which makes it the best choice as the channel capacity, - return n -}() - -// NewDelayed gives a Delayed queue with maximum concurrency specified by workers. -func NewDelayed(opts ...DelayQueueOption) Delayed { - q := &delayQueue{ - workers: 1, - queue: &pq{}, - execute: make(chan *delayTask, workerChanBuf), - enqueue: make(chan *delayTask, 100), - } - for _, o := range opts { - o(q) - } - return q -} - -type delayQueue struct { - workers int - workerStopped []chan struct{} - - // incoming - enqueue chan *delayTask - // outgoing - execute chan *delayTask - - mu sync.Mutex - queue *pq -} - -// PushDelayed will execute the task after waiting for the delay -func (d *delayQueue) PushDelayed(t Task, delay time.Duration) { - task := &delayTask{do: t, runAt: time.Now().Add(delay)} - select { - case d.enqueue <- task: - // buffer has room to enqueue - default: - // TODO warn and resize buffer - // if the buffer is full, we take the more expensive route of locking and pushing directly to the heap - d.mu.Lock() - heap.Push(d.queue, task) - d.mu.Unlock() - } -} - -// Push will execute the task as soon as possible -func (d *delayQueue) Push(task Task) { - d.PushDelayed(task, 0) -} - -func (d *delayQueue) Closed() <-chan struct{} { - done := make(chan struct{}) - go func() { - for _, ch := range d.workerStopped { - <-ch - } - close(done) - }() - return done -} - -func (d *delayQueue) Run(stop <-chan struct{}) { - for i := 0; i < d.workers; i++ { - d.workerStopped = append(d.workerStopped, d.work(stop)) - } - - push := func(t *delayTask) bool { - select { - case d.execute <- t: - return true - case <-stop: - return false - } - } - - for { - var task *delayTask - d.mu.Lock() - if head := d.queue.Peek(); head != nil { - task = head.(*delayTask) - heap.Pop(d.queue) - } - d.mu.Unlock() - - if task != nil { - delay := time.Until(task.runAt) - if delay <= 0 { - // execute now and continue processing incoming enqueues/tasks - if !push(task) { - return - } - } else { - // not ready yet, don't block enqueueing - await := time.NewTimer(delay) - select { - case t := <-d.enqueue: - d.mu.Lock() - heap.Push(d.queue, t) - // put the old "head" back on the queue, it may be scheduled to execute after the one - // that was just pushed - heap.Push(d.queue, task) - d.mu.Unlock() - case <-await.C: - if !push(task) { - return - } - case <-stop: - await.Stop() - return - } - await.Stop() - } - } else { - // no items, wait for Push or stop - select { - case t := <-d.enqueue: - d.mu.Lock() - d.queue.Push(t) - d.mu.Unlock() - case <-stop: - return - } - } - } -} - -// work takes a channel that signals to stop, and returns a channel that signals the worker has fully stopped -func (d *delayQueue) work(stop <-chan struct{}) (stopped chan struct{}) { - stopped = make(chan struct{}) - go func() { - defer close(stopped) - for { - select { - case t := <-d.execute: - if err := t.do(); err != nil { - if t.retries < maxTaskRetry { - d.Push(t.do) - t.retries++ - log.Warnf("Work item handle failed: %v %d times, retry it", err, t.retries) - continue - } - log.Errorf("Work item handle failed: %v, reaching the maximum retry times: %d, drop it", err, maxTaskRetry) - } - case <-stop: - return - } - } - }() - return -} diff --git a/pkg/queue/delay_test.go b/pkg/queue/delay_test.go deleted file mode 100644 index 9a4ef70e4..000000000 --- a/pkg/queue/delay_test.go +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2017 Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package queue - -import ( - "container/heap" - "sync" - "testing" - "time" -) - -func TestPriorityQueue(t *testing.T) { - pq := &pq{} - - t0 := time.Now() - t1 := &delayTask{runAt: t0.Add(0)} - t2 := &delayTask{runAt: t0.Add(1 * time.Hour)} - t3 := &delayTask{runAt: t0.Add(2 * time.Hour)} - t4 := &delayTask{runAt: t0.Add(3 * time.Hour)} - sorted := []*delayTask{t1, t2, t3, t4} - // fill in an unsorted order - unsorted := []*delayTask{t4, t2, t3, t1} - for _, task := range unsorted { - heap.Push(pq, task) - } - - // dequeue should be in order - for i, task := range sorted { - peeked := pq.Peek() - popped := heap.Pop(pq) - if task != popped { - t.Fatalf("pop %d was not in order", i) - } - if peeked != popped { - t.Fatalf("did not peek at the next item to be popped") - } - } -} - -func TestDelayQueueOrdering(t *testing.T) { - dq := NewDelayed(DelayQueueWorkers(2)) - stop := make(chan struct{}) - defer close(stop) - go dq.Run(stop) - - mu := sync.Mutex{} - var t0, t1, t2 time.Time - - done := make(chan struct{}) - dq.PushDelayed(func() error { - mu.Lock() - defer mu.Unlock() - defer close(done) - t2 = time.Now() - return nil - }, 200*time.Millisecond) - dq.PushDelayed(func() error { - mu.Lock() - defer mu.Unlock() - t1 = time.Now() - return nil - }, 100*time.Millisecond) - dq.Push(func() error { - mu.Lock() - defer mu.Unlock() - t0 = time.Now() - return nil - }) - - select { - case <-time.After(500 * time.Millisecond): - case <-done: - } - - mu.Lock() - if !(t2.After(t1) && t1.After(t0)) { - t.Errorf("expected jobs to be run in order based on delays") - } - mu.Unlock() -} - -func TestDelayQueuePushBeforeRun(t *testing.T) { - // This is a regression test to ensure we can push while Run() is called without a race - dq := NewDelayed(DelayQueueBuffer(0)) - st := make(chan struct{}) - go func() { - // Enqueue a bunch until we stop - for { - select { - case <-st: - return - default: - } - dq.Push(func() error { - return nil - }) - } - }() - go dq.Run(st) - // Wait a bit - <-time.After(time.Millisecond * 10) - close(st) -} - -func TestDelayQueuePushNonblockingWithFullBuffer(t *testing.T) { - queuedItems := 50 - dq := NewDelayed(DelayQueueBuffer(0), DelayQueueWorkers(0)) - - success := make(chan struct{}) - timeout := time.After(500 * time.Millisecond) - defer close(success) - - go func() { - for i := 0; i < queuedItems; i++ { - dq.PushDelayed(func() error { return nil }, time.Minute*time.Duration(queuedItems-i)) - } - success <- struct{}{} - }() - - select { - case <-success: - dq := dq.(*delayQueue) - dq.mu.Lock() - if dq.queue.Len() < queuedItems { - t.Fatalf("expected 50 items in the queue, got %d", dq.queue.Len()) - } - dq.mu.Unlock() - return - case <-timeout: - t.Fatal("timed out waiting for enqueues") - } -} - -func TestPriorityQueueShrinking(t *testing.T) { - c := 48 - pq := make(pq, 0, c) - pqp := &pq - - t0 := time.Now() - for i := 0; i < c; i++ { - dt := &delayTask{runAt: t0.Add(time.Duration(i) * time.Hour)} - heap.Push(pqp, dt) - } - - if len(pq) != c { - t.Fatalf("the length of pq should be %d, but end up %d", c, len(pq)) - } - - if cap(pq) != c { - t.Fatalf("the capacity of pq should be %d, but end up %d", c, cap(pq)) - } - - for i := 0; i < c; i++ { - _ = heap.Pop(pqp) - if i == 1+c/2 && cap(pq) != c/2 { - t.Fatalf("the capacity of pq should be reduced to half its length %d, but got %d", c/2, cap(pq)) - } - } -} diff --git a/pkg/queue/instance.go b/pkg/queue/instance.go deleted file mode 100644 index f67cdf232..000000000 --- a/pkg/queue/instance.go +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package queue - -import ( - "sync" - "time" -) - -import ( - "github.com/cenkalti/backoff/v4" - "istio.io/pkg/log" - "k8s.io/apimachinery/pkg/util/rand" -) - -// Task to be performed. -type Task func() error - -type BackoffTask struct { - task Task - backoff *backoff.ExponentialBackOff -} - -// Instance of work tickets processed using a rate-limiting loop -type Instance interface { - // Push a task. - Push(task Task) - // Run the loop until a signal on the channel - Run(<-chan struct{}) - - // Closed returns a chan that will be signaled when the Instance has stopped processing tasks. - Closed() <-chan struct{} -} - -type queueImpl struct { - delay time.Duration - retryBackoff *backoff.ExponentialBackOff - tasks []*BackoffTask - cond *sync.Cond - closing bool - closed chan struct{} - closeOnce *sync.Once - id string -} - -func newExponentialBackOff(eb *backoff.ExponentialBackOff) *backoff.ExponentialBackOff { - if eb == nil { - return nil - } - teb := backoff.NewExponentialBackOff() - teb.InitialInterval = eb.InitialInterval - teb.MaxElapsedTime = eb.MaxElapsedTime - teb.MaxInterval = eb.MaxInterval - teb.Multiplier = eb.Multiplier - teb.RandomizationFactor = eb.RandomizationFactor - return teb -} - -// NewQueue instantiates a queue with a processing function -func NewQueue(errorDelay time.Duration) Instance { - return NewQueueWithID(errorDelay, rand.String(10)) -} - -func NewQueueWithID(errorDelay time.Duration, name string) Instance { - return &queueImpl{ - delay: errorDelay, - tasks: make([]*BackoffTask, 0), - closing: false, - closed: make(chan struct{}), - closeOnce: &sync.Once{}, - cond: sync.NewCond(&sync.Mutex{}), - id: name, - } -} - -func NewBackOffQueue(backoff *backoff.ExponentialBackOff) Instance { - return &queueImpl{ - retryBackoff: backoff, - tasks: make([]*BackoffTask, 0), - closing: false, - closed: make(chan struct{}), - closeOnce: &sync.Once{}, - cond: sync.NewCond(&sync.Mutex{}), - } -} - -func (q *queueImpl) Push(item Task) { - q.cond.L.Lock() - defer q.cond.L.Unlock() - if !q.closing { - q.tasks = append(q.tasks, &BackoffTask{item, newExponentialBackOff(q.retryBackoff)}) - } - q.cond.Signal() -} - -func (q *queueImpl) pushRetryTask(item *BackoffTask) { - q.cond.L.Lock() - defer q.cond.L.Unlock() - if !q.closing { - q.tasks = append(q.tasks, item) - } - q.cond.Signal() -} - -func (q *queueImpl) Closed() <-chan struct{} { - return q.closed -} - -func (q *queueImpl) Run(stop <-chan struct{}) { - log.Debugf("started queue %s", q.id) - defer func() { - q.closeOnce.Do(func() { - log.Debugf("closed queue %s", q.id) - close(q.closed) - }) - }() - go func() { - <-stop - q.cond.L.Lock() - q.cond.Signal() - q.closing = true - q.cond.L.Unlock() - }() - - for { - q.cond.L.Lock() - - // wait for closing to be set, or a task to be pushed - for !q.closing && len(q.tasks) == 0 { - q.cond.Wait() - } - - if q.closing { - q.cond.L.Unlock() - // We must be shutting down. - return - } - - backoffTask := q.tasks[0] - // Slicing will not free the underlying elements of the array, so explicitly clear them out here - q.tasks[0] = nil - q.tasks = q.tasks[1:] - - q.cond.L.Unlock() - - if err := backoffTask.task(); err != nil { - delay := q.delay - if q.retryBackoff != nil { - delay = backoffTask.backoff.NextBackOff() - } - log.Infof("Work item handle failed (%v), retry after delay %v", err, delay) - time.AfterFunc(delay, func() { - q.pushRetryTask(backoffTask) - }) - } - } -} diff --git a/pkg/queue/instance_test.go b/pkg/queue/instance_test.go deleted file mode 100644 index fb695e4da..000000000 --- a/pkg/queue/instance_test.go +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package queue - -import ( - "errors" - "sync" - "testing" - "time" -) - -import ( - "github.com/cenkalti/backoff/v4" - "go.uber.org/atomic" -) - -func TestOrdering(t *testing.T) { - numValues := 1000 - - q := NewQueue(1 * time.Microsecond) - stop := make(chan struct{}) - defer close(stop) - - wg := sync.WaitGroup{} - wg.Add(numValues) - mu := sync.Mutex{} - out := make([]int, 0) - for i := 0; i < numValues; i++ { - i := i - - q.Push(func() error { - mu.Lock() - out = append(out, i) - defer mu.Unlock() - wg.Done() - return nil - }) - - // Start the queue at the halfway point. - if i == numValues/2 { - go q.Run(stop) - } - } - - // wait for all task processed - wg.Wait() - - if len(out) != numValues { - t.Fatalf("expected output array length %d to equal %d", len(out), numValues) - } - - for i := 0; i < numValues; i++ { - if i != out[i] { - t.Fatalf("expected out[%d] %v to equal %v", i, out[i], i) - } - } -} - -func TestRetry(t *testing.T) { - q := NewQueue(1 * time.Microsecond) - stop := make(chan struct{}) - defer close(stop) - - // Push a task that fails the first time and retries. - wg := sync.WaitGroup{} - wg.Add(2) - failed := false - q.Push(func() error { - defer wg.Done() - if failed { - return nil - } - failed = true - return errors.New("fake error") - }) - - go q.Run(stop) - - // wait for the task to run twice. - wg.Wait() -} - -func TestRetryWithBackoff(t *testing.T) { - ebf := backoff.NewExponentialBackOff() - ebf.InitialInterval = 1 * time.Microsecond - ebf.MaxInterval = 5 * time.Microsecond - q := NewBackOffQueue(ebf) - stop := make(chan struct{}) - defer close(stop) - - // Push a task that fails the first time and retries. - wg := sync.WaitGroup{} - wg.Add(2) - failed := false - q.Push(func() error { - defer wg.Done() - if failed { - return nil - } - failed = true - return errors.New("fake error") - }) - - go q.Run(stop) - - // wait for the task to run twice. - wg.Wait() -} - -func TestResourceFree(t *testing.T) { - q := NewQueue(1 * time.Microsecond) - stop := make(chan struct{}) - signal := make(chan struct{}) - go func() { - q.Run(stop) - signal <- struct{}{} - }() - - q.Push(func() error { - t.Log("mock exec") - return nil - }) - - // mock queue block wait cond signal - time.AfterFunc(10*time.Millisecond, func() { - close(stop) - }) - - select { - case <-time.After(200 * time.Millisecond): - t.Error("close stop, method exit timeout.") - case <-signal: - t.Log("queue return.") - } -} - -func TestClosed(t *testing.T) { - t.Run("immediate close", func(t *testing.T) { - stop := make(chan struct{}) - q := NewQueue(0) - go q.Run(stop) - close(stop) - if err := WaitForClose(q, 10*time.Second); err != nil { - t.Error(err) - } - }) - t.Run("no tasks after close", func(t *testing.T) { - stop := make(chan struct{}) - q := NewQueue(0) - taskComplete := atomic.NewBool(false) - q.Push(func() error { - close(stop) - return nil - }) - go q.Run(stop) - if err := WaitForClose(q, 10*time.Second); err != nil { - t.Error() - } - q.Push(func() error { - taskComplete.Store(true) - return nil - }) - if taskComplete.Load() { - t.Error("task ran on closed queue") - } - }) -} diff --git a/pkg/queue/leak_test.go b/pkg/queue/leak_test.go deleted file mode 100644 index 90bf2153e..000000000 --- a/pkg/queue/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package queue - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pkg/queue/util.go b/pkg/queue/util.go deleted file mode 100644 index a0152b8fd..000000000 --- a/pkg/queue/util.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2017 Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package queue - -import ( - "fmt" - "time" -) - -// WaitForClose blocks until the Instance has stopped processing tasks or the timeout expires. -// If the timeout is zero, it will wait until the queue is done processing. -// WaitForClose an error if the timeout expires. -func WaitForClose(q Instance, timeout time.Duration) error { - closed := q.Closed() - if timeout == 0 { - <-closed - return nil - } - timer := time.NewTimer(timeout) - defer timer.Stop() - select { - case <-closed: - return nil - case <-timer.C: - return fmt.Errorf("timeout waiting for queue to close after %v", timeout) - } -} diff --git a/pkg/remote/nacos/client.go b/pkg/remote/nacos/client.go new file mode 100644 index 000000000..eb43b2b63 --- /dev/null +++ b/pkg/remote/nacos/client.go @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 nacos + +import ( + "net" + "strconv" + "strings" + "time" +) + +import ( + "github.com/nacos-group/nacos-sdk-go/clients" + "github.com/nacos-group/nacos-sdk-go/clients/naming_client" + "github.com/nacos-group/nacos-sdk-go/common/constant" + model2 "github.com/nacos-group/nacos-sdk-go/model" + "github.com/nacos-group/nacos-sdk-go/vo" + perrors "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +type NacosClient struct { + namingClient naming_client.INamingClient +} + +func (client *NacosClient) GetAllServicesInfo(param vo.GetAllServiceInfoParam) (model2.ServiceList, error) { + return client.namingClient.GetAllServicesInfo(param) +} + +func (client *NacosClient) SelectInstances(param vo.SelectInstancesParam) ([]model2.Instance, error) { + return client.namingClient.SelectInstances(param) +} + +func (client *NacosClient) Subscribe(param *vo.SubscribeParam) error { + return client.namingClient.Subscribe(param) +} + +func (client *NacosClient) Unsubscribe(param *vo.SubscribeParam) error { + return client.namingClient.Unsubscribe(param) +} + +func NewNacosClient(config *model.RemoteConfig) (*NacosClient, error) { + configMap := make(map[string]interface{}, 2) + addresses := strings.Split(config.Address, ",") + serverConfigs := make([]constant.ServerConfig, 0, len(addresses)) + for _, addr := range addresses { + ip, portStr, err := net.SplitHostPort(addr) + if err != nil { + return nil, perrors.WithMessagef(err, "split [%s] ", addr) + } + port, _ := strconv.Atoi(portStr) + serverConfigs = append(serverConfigs, constant.ServerConfig{ + IpAddr: ip, + Port: uint64(port), + }) + } + configMap["serverConfigs"] = serverConfigs + + duration, _ := time.ParseDuration(config.Timeout) + client, err := clients.NewNamingClient( + vo.NacosClientParam{ + ClientConfig: constant.NewClientConfig( + constant.WithTimeoutMs(uint64(duration.Milliseconds())), + constant.WithNotLoadCacheAtStart(true), + constant.WithUpdateCacheWhenEmpty(true), + ), + ServerConfigs: serverConfigs, + }, + ) + + if err != nil { + return nil, perrors.WithMessagef(err, "nacos client create error") + } + return &NacosClient{client}, nil +} diff --git a/pkg/remote/zookeeper/client.go b/pkg/remote/zookeeper/client.go new file mode 100644 index 000000000..7d850d36a --- /dev/null +++ b/pkg/remote/zookeeper/client.go @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 zookeeper + +import ( + "strings" + "sync" + "time" +) + +import ( + gxzookeeper "github.com/dubbogo/gost/database/kv/zk" + perrors "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +func ValidateZookeeperClient(container ZkClientFacade, zkName string) error { + lock := container.ZkClientLock() + config := container.GetConfig() + + lock.Lock() + defer lock.Unlock() + + if container.ZkClient() == nil { + newClient, cltErr := NewZkClient(zkName, config) + if cltErr != nil { + return perrors.WithMessagef(cltErr, "create zookeeper client (address:%+v)", config.Address) + } + container.SetZkClient(newClient) + } + return nil +} + +func NewZkClient(zkName string, config *model.RemoteConfig) (*gxzookeeper.ZookeeperClient, error) { + var ( + zkAddresses = strings.Split(config.Address, ",") + ) + timeout, _ := time.ParseDuration(config.Timeout) + + newClient, cltErr := gxzookeeper.NewZookeeperClient(zkName, zkAddresses, true, gxzookeeper.WithZkTimeOut(timeout)) + if cltErr != nil { + logger.Warnf("newZookeeperClient(name{%s}, zk address{%v}, timeout{%d}) = error{%v}", + zkName, config.Address, timeout.String(), cltErr) + return nil, perrors.WithMessagef(cltErr, "newZookeeperClient(address:%+v)", config.Address) + } + return newClient, nil +} + +type Listener interface { + // Close closes this listener + Close() + // WatchAndHandle watch the target path and handle the event + WatchAndHandle() +} + +type ZkClientFacade interface { + Name() string + ZkClient() *gxzookeeper.ZookeeperClient + SetZkClient(*gxzookeeper.ZookeeperClient) + ZkClientLock() *sync.Mutex + WaitGroup() *sync.WaitGroup // for wait group control, zk client listener & zk client container + Done() chan struct{} // for registry destroy + RestartCallBack() bool + GetConfig() *model.RemoteConfig +} + +// HandleClientRestart keeps the connection between client and server +// This method should be used only once. You can use handleClientRestart() in package registry. +func HandleClientRestart(r ZkClientFacade) { + defer r.WaitGroup().Done() + for { + select { + case <-r.ZkClient().Reconnect(): + r.RestartCallBack() + time.Sleep(10 * time.Microsecond) + case <-r.Done(): + logger.Warnf("receive registry destroy event, quit client restart handler") + return + } + } +} diff --git a/pkg/revisions/default_watcher.go b/pkg/revisions/default_watcher.go deleted file mode 100644 index 9e544e345..000000000 --- a/pkg/revisions/default_watcher.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package revisions - -import ( - "sync" - "time" -) - -import ( - "istio.io/api/label" - "istio.io/pkg/log" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/controllers" -) - -const ( - defaultTagWebhookName = "istio-revision-tag-default" -) - -// DefaultWatcher keeps track of the current default revision and can notify watchers -// when the default revision changes. -type DefaultWatcher interface { - Run(stopCh <-chan struct{}) - HasSynced() bool - GetDefault() string - AddHandler(handler DefaultHandler) -} - -// DefaultHandler is a callback for when the default revision changes. -type DefaultHandler func(string) - -type defaultWatcher struct { - revision string - defaultRevision string - handlers []DefaultHandler - - queue controllers.Queue - webhookInformer cache.SharedIndexInformer - mu sync.RWMutex -} - -func NewDefaultWatcher(client kube.Client, revision string) DefaultWatcher { - p := &defaultWatcher{ - revision: revision, - mu: sync.RWMutex{}, - } - p.queue = controllers.NewQueue("default revision", controllers.WithReconciler(p.setDefault)) - p.webhookInformer = client.KubeInformer().Admissionregistration().V1().MutatingWebhookConfigurations().Informer() - p.webhookInformer.AddEventHandler(controllers.FilteredObjectHandler(p.queue.AddObject, isDefaultTagWebhook)) - - return p -} - -func (p *defaultWatcher) Run(stopCh <-chan struct{}) { - if !kube.WaitForCacheSyncInterval(stopCh, time.Second, p.webhookInformer.HasSynced) { - log.Errorf("failed to sync default watcher") - return - } - p.queue.Run(stopCh) -} - -// GetDefault returns the current default revision. -func (p *defaultWatcher) GetDefault() string { - p.mu.RLock() - defer p.mu.RUnlock() - return p.defaultRevision -} - -// AddHandler registers a new handler for updates to default revision changes. -func (p *defaultWatcher) AddHandler(handler DefaultHandler) { - p.mu.Lock() - defer p.mu.Unlock() - p.handlers = append(p.handlers, handler) -} - -func (p *defaultWatcher) HasSynced() bool { - return p.queue.HasSynced() -} - -// notifyHandlers notifies all registered handlers on default revision change. -// assumes externally locked. -func (p *defaultWatcher) notifyHandlers() { - for _, handler := range p.handlers { - handler(p.defaultRevision) - } -} - -func (p *defaultWatcher) setDefault(key types.NamespacedName) error { - revision := "" - wh, _, _ := p.webhookInformer.GetIndexer().GetByKey(key.Name) - if wh != nil { - revision = wh.(metav1.Object).GetLabels()[label.IoIstioRev.Name] - } - p.mu.Lock() - defer p.mu.Unlock() - p.defaultRevision = revision - p.notifyHandlers() - return nil -} - -func isDefaultTagWebhook(obj controllers.Object) bool { - return obj.GetName() == defaultTagWebhookName -} diff --git a/pkg/revisions/default_watcher_test.go b/pkg/revisions/default_watcher_test.go deleted file mode 100644 index a48c7e680..000000000 --- a/pkg/revisions/default_watcher_test.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package revisions - -import ( - "context" - "fmt" - "testing" - "time" -) - -import ( - "istio.io/api/label" - "istio.io/pkg/log" - v1 "k8s.io/api/admissionregistration/v1" - kerrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -func newDefaultWatcher(client kube.Client, revision string) DefaultWatcher { - defaultWatcher := NewDefaultWatcher(client, revision) - return defaultWatcher -} - -func createDefaultWebhook(t test.Failer, client kubernetes.Interface, revision string) { - t.Helper() - defaultWebhookConfiguration := &v1.MutatingWebhookConfiguration{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: defaultTagWebhookName, - Labels: map[string]string{ - label.IoIstioRev.Name: revision, - }, - }, - } - mwhClient := client.AdmissionregistrationV1().MutatingWebhookConfigurations() - _, err := mwhClient.Update(context.TODO(), defaultWebhookConfiguration, metav1.UpdateOptions{}) - if err != nil && kerrors.IsNotFound(err) { - _, err = mwhClient.Create(context.TODO(), defaultWebhookConfiguration, metav1.CreateOptions{}) - if err != nil { - t.Fatalf("failed to update or create default webhook: %v", err) - } - } -} - -func deleteDefaultWebhook(t test.Failer, client kubernetes.Interface) { - t.Helper() - mwhClient := client.AdmissionregistrationV1().MutatingWebhookConfigurations() - err := mwhClient.Delete(context.TODO(), defaultTagWebhookName, metav1.DeleteOptions{}) - if err != nil { - t.Fatalf("failed to delete default webhook: %v", err) - } -} - -func expectRevision(t test.Failer, watcher DefaultWatcher, expected string) { - t.Helper() - retry.UntilSuccessOrFail(t, func() error { - got := watcher.GetDefault() - if got != expected { - return fmt.Errorf("wanted default revision %q, got %q", expected, got) - } - return nil - }, retry.Timeout(time.Second*10), retry.BackoffDelay(time.Millisecond*10)) -} - -func expectRevisionChan(t test.Failer, revisionChan chan string, expected string) { - select { - case rev := <-revisionChan: - if rev != expected { - t.Fatalf("expected revision %q to be produced on chan, got %q", expected, rev) - } - case <-time.After(time.Second * 5): - t.Fatalf("timed out waiting for value on default revision chan") - } -} - -func TestNoDefaultRevision(t *testing.T) { - stop := make(chan struct{}) - client := kube.NewFakeClient() - w := newDefaultWatcher(client, "default") - client.RunAndWait(stop) - go w.Run(stop) - // if have no default tag for some reason, should return "" - expectRevision(t, w, "") - close(stop) -} - -func TestDefaultRevisionChanges(t *testing.T) { - log.FindScope("controllers").SetOutputLevel(log.DebugLevel) - stop := make(chan struct{}) - client := kube.NewFakeClient() - w := newDefaultWatcher(client, "default") - client.RunAndWait(stop) - go w.Run(stop) - expectRevision(t, w, "") - // change default to "red" - createDefaultWebhook(t, client, "red") - expectRevision(t, w, "red") - - // change default to "green" - createDefaultWebhook(t, client, "green") - expectRevision(t, w, "green") - - // remove default - deleteDefaultWebhook(t, client) - expectRevision(t, w, "") - close(stop) -} - -func TestHandlers(t *testing.T) { - stop := make(chan struct{}) - client := kube.NewFakeClient() - w := newDefaultWatcher(client, "default") - client.RunAndWait(stop) - go w.Run(stop) - expectRevision(t, w, "") - - // add a handler to watch default revision changes, ensure it's triggered - newDefaultChan := make(chan string) - handler := func(revision string) { - newDefaultChan <- revision - } - w.AddHandler(handler) - createDefaultWebhook(t, client, "green") - expectRevisionChan(t, newDefaultChan, "green") - close(stop) -} diff --git a/pkg/router/api.go b/pkg/router/api.go new file mode 100644 index 000000000..28ac8986e --- /dev/null +++ b/pkg/router/api.go @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 router + +import ( + "net/url" + "strings" +) + +import ( + "github.com/dubbo-go-pixiu/pixiu-api/pkg/router" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" +) + +// GetURIParams returns the values retrieved from the rawURL +func GetURIParams(api *router.API, rawURL url.URL) url.Values { + return wildcardMatch(api.URLPattern, rawURL.Path) +} + +// IsWildCardBackendPath checks whether the configured path of +// the upstream restful service contains parameters +func IsWildCardBackendPath(api *router.API) bool { + if len(api.IntegrationRequest.Path) == 0 { + return false + } + return strings.Contains(api.IntegrationRequest.Path, constant.PathParamIdentifier) +} diff --git a/pixiu/pkg/router/api_test.go b/pkg/router/api_test.go similarity index 100% rename from pixiu/pkg/router/api_test.go rename to pkg/router/api_test.go diff --git a/pkg/router/route.go b/pkg/router/route.go new file mode 100644 index 000000000..f21e5be22 --- /dev/null +++ b/pkg/router/route.go @@ -0,0 +1,295 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 router + +import ( + "net/url" + "strings" + "sync" +) + +import ( + "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" + "github.com/dubbo-go-pixiu/pixiu-api/pkg/router" + "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/router/trie" + "github.com/apache/dubbo-go-pixiu/pkg/common/util/stringutil" + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +// Node defines the single method of the router configured API +type Node struct { + fullPath string + filters []string + method *config.Method + headers map[string]string +} + +// Route defines the tree of router APIs +type Route struct { + lock sync.RWMutex + tree trie.Trie +} + +// ClearAPI clear the api +func (rt *Route) ClearAPI() error { + rt.lock.Lock() + defer rt.lock.Unlock() + rt.tree.Clear() + return nil +} + +func (rt *Route) RemoveAPI(api router.API) { + lowerCasePath := strings.ToLower(api.URLPattern) + key := getTrieKey(api.Method.HTTPVerb, lowerCasePath, false) + + rt.lock.Lock() + defer rt.lock.Unlock() + // if api is exists + if exists, err := rt.tree.Contains(key); err != nil { + logger.Errorf("rt.tree.Get(key) err: %s", err.Error()) + return + } else if exists { + // append new cluster to the node + if node, _, exists, err := rt.tree.Get(key); err != nil { + logger.Errorf("rt.tree.Get(key) err: %s", err.Error()) + return + } else if exists { + bizInfoInterface := node.GetBizInfo() + bizInfo, ok := bizInfoInterface.(*Node) + if bizInfo == nil || !ok { + logger.Error("bizInfoInterface.(*Node) failed") + return + } + // avoid thread safe problem + clusters := strings.Split(bizInfo.method.HTTPBackendConfig.URL, ",") + if len(clusters) > 1 { + var i int + for i = 0; i < len(clusters); i++ { + if clusters[i] == api.URL { + break + } + } + + if i == len(clusters) { + return + } + + // operate pointer has no necessary to call update api + bizInfo.method.HTTPBackendConfig.URL = strings.Join(append(clusters[:i], clusters[i+1:]...), ",") + } else { + // double check, avoid removing api that does not exists + if !strings.Contains(bizInfo.method.HTTPBackendConfig.URL, api.URL) { + return + } + // if backend has only one node, then just directly remove + _, _ = rt.tree.Remove(key) + } + } + } +} + +func getTrieKey(method config.HTTPVerb, path string, isPrefix bool) string { + if isPrefix { + if !strings.HasSuffix(path, constant.PathSlash) { + path = path + constant.PathSlash + } + path = path + "**" + } + return stringutil.GetTrieKey(string(method), path) +} + +// PutAPI puts an api into the resource +func (rt *Route) PutAPI(api router.API) error { + lowerCasePath := strings.ToLower(api.URLPattern) + key := getTrieKey(api.Method.HTTPVerb, lowerCasePath, false) + node, ok := rt.getNode(key) + if !ok { + rn := &Node{ + fullPath: lowerCasePath, + method: &api.Method, + headers: api.Headers, + } + rt.lock.Lock() + defer rt.lock.Unlock() + _, _ = rt.tree.Put(key, rn) + return nil + } + return errors.Errorf("Method %s with address %s already exists in path %s", + api.Method.HTTPVerb, lowerCasePath, node.fullPath) +} + +// PutOrUpdateAPI puts or updates an api into the resource +func (rt *Route) PutOrUpdateAPI(api router.API) error { + lowerCasePath := strings.ToLower(api.URLPattern) + key := getTrieKey(api.Method.HTTPVerb, lowerCasePath, false) + rn := &Node{ + fullPath: lowerCasePath, + method: &api.Method, + headers: api.Headers, + } + rt.lock.Lock() + defer rt.lock.Unlock() + + // if api is exists + if exists, err := rt.tree.Contains(key); err != nil { + return err + } else if exists { + // append new cluster to the node + if node, _, exists, err := rt.tree.Get(key); err != nil { + return err + } else if exists { + bizInfoInterface := node.GetBizInfo() + bizInfo, ok := bizInfoInterface.(*Node) + if bizInfo == nil || !ok { + return errors.New("bizInfoInterface.(*Node) failed") + } + if !strings.Contains(bizInfo.method.HTTPBackendConfig.URL, api.URL) { + // operate pointer has no necessary to call update api + bizInfo.method.HTTPBackendConfig.URL = bizInfo.method.HTTPBackendConfig.URL + "," + api.URL + } + } + } else { + if ok, err := rt.tree.PutOrUpdate(key, rn); !ok { + return err + } + } + + return nil +} + +// FindAPI return if api has path in trie,or nil +func (rt *Route) FindAPI(fullPath string, httpverb config.HTTPVerb) (*router.API, bool) { + lowerCasePath := strings.ToLower(fullPath) + key := getTrieKey(httpverb, lowerCasePath, false) + if n, found := rt.getNode(key); found { + rt.lock.RLock() + defer rt.lock.RUnlock() + return &router.API{ + URLPattern: n.fullPath, + Method: *n.method, + Headers: n.headers, + }, found + } + return nil, false +} + +// MatchAPI FindAPI returns the api that meets the rule +func (rt *Route) MatchAPI(fullPath string, httpverb config.HTTPVerb) (*router.API, bool) { + lowerCasePath := strings.ToLower(fullPath) + key := getTrieKey(httpverb, lowerCasePath, false) + if n, found := rt.matchNode(key); found { + rt.lock.RLock() + defer rt.lock.RUnlock() + return &router.API{ + URLPattern: n.fullPath, + Method: *n.method, + Headers: n.headers, + }, found + } + return nil, false +} + +// DeleteNode delete node by fullPath +func (rt *Route) DeleteNode(fullPath string) bool { + rt.lock.RLock() + defer rt.lock.RUnlock() + methodList := [8]config.HTTPVerb{"ANY", "GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"} + for _, v := range methodList { + key := getTrieKey(v, fullPath, false) + _, _ = rt.tree.Remove(key) + } + return true +} + +// DeleteAPI delete api by fullPath and http verb +func (rt *Route) DeleteAPI(fullPath string, httpverb config.HTTPVerb) bool { + lowerCasePath := strings.ToLower(fullPath) + key := getTrieKey(httpverb, lowerCasePath, false) + if _, found := rt.getNode(key); found { + rt.lock.RLock() + defer rt.lock.RUnlock() + _, _ = rt.tree.Remove(key) + return true + } + return false +} + +func (rt *Route) getNode(fullPath string) (*Node, bool) { + var n interface{} + var found bool + rt.lock.RLock() + defer rt.lock.RUnlock() + trieNode, _, _, _ := rt.tree.Get(fullPath) + found = trieNode != nil + if !found { + return nil, false + } + n = trieNode.GetBizInfo() + if n == nil { + return nil, false + } + return n.(*Node), found +} + +func (rt *Route) matchNode(fullPath string) (*Node, bool) { + var n interface{} + var found bool + rt.lock.RLock() + defer rt.lock.RUnlock() + trieNode, _, _ := rt.tree.Match(fullPath) + found = trieNode != nil + if !found { + return nil, false + } + n = trieNode.GetBizInfo() + if n == nil { + return nil, false + } + return n.(*Node), found +} + +func wildcardMatch(wildcardPath string, checkPath string) url.Values { + + cPaths := strings.Split(strings.TrimLeft(checkPath, constant.PathSlash), constant.PathSlash) + wPaths := strings.Split(strings.TrimLeft(wildcardPath, constant.PathSlash), constant.PathSlash) + result := url.Values{} + if len(cPaths) == 0 || len(wPaths) == 0 || len(cPaths) != len(wPaths) { + return nil + } + for i := 0; i < len(cPaths); i++ { + if !strings.EqualFold(cPaths[i], wPaths[i]) && !strings.HasPrefix(wPaths[i], constant.PathParamIdentifier) { + return nil + } + if strings.HasPrefix(wPaths[i], constant.PathParamIdentifier) { + result.Add(strings.TrimPrefix(wPaths[i], constant.PathParamIdentifier), cPaths[i]) + } + } + return result +} + +// NewRoute returns an empty router tree +func NewRoute() *Route { + return &Route{ + tree: trie.NewTrie(), + } +} diff --git a/pkg/router/route_test.go b/pkg/router/route_test.go new file mode 100644 index 000000000..eda989010 --- /dev/null +++ b/pkg/router/route_test.go @@ -0,0 +1,195 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 router + +import ( + "testing" +) + +import ( + "github.com/dubbo-go-pixiu/pixiu-api/pkg/api/config" + "github.com/dubbo-go-pixiu/pixiu-api/pkg/router" + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/router/trie" +) + +func getMockMethod(verb config.HTTPVerb) config.Method { + inbound := config.InboundRequest{} + integration := config.IntegrationRequest{} + return config.Method{ + Enable: true, + HTTPVerb: verb, + InboundRequest: inbound, + IntegrationRequest: integration, + } +} + +func TestPut(t *testing.T) { + rt := &Route{ + tree: trie.NewTrie(), + } + n0 := getMockMethod(config.MethodGet) + _ = rt.PutAPI(router.API{URLPattern: "/", Method: n0}) + _, ok := rt.FindAPI("/", n0.HTTPVerb) + assert.True(t, ok) + + err := rt.PutAPI(router.API{URLPattern: "/", Method: n0}) + assert.Error(t, err, "Method GET already exists in path /") + + n1 := getMockMethod(config.MethodPost) + err = rt.PutAPI(router.API{URLPattern: "/mock", Method: n0}) + assert.Nil(t, err) + err = rt.PutAPI(router.API{URLPattern: "/mock", Method: n1}) + assert.Nil(t, err) + _, ok = rt.FindAPI("/mock", n0.HTTPVerb) + assert.True(t, ok) + _, ok = rt.FindAPI("/mock", n1.HTTPVerb) + assert.True(t, ok) + + err = rt.PutAPI(router.API{URLPattern: "/mock/test", Method: n0}) + assert.Nil(t, err) + _, ok = rt.FindAPI("/mock/test", n0.HTTPVerb) + assert.True(t, ok) + + _ = rt.PutAPI(router.API{URLPattern: "/test/:id", Method: n0}) + _, ok = rt.FindAPI("/test/:id", n0.HTTPVerb) + assert.True(t, ok) + + err = rt.PutAPI(router.API{URLPattern: "/test/:id", Method: n1}) + assert.Nil(t, err) + err = rt.PutAPI(router.API{URLPattern: "/test/js", Method: n0}) + assert.Nil(t, err) + err = rt.PutAPI(router.API{URLPattern: "/test/:id/mock", Method: n0}) + _, ok = rt.FindAPI("/test/:id/mock", n0.HTTPVerb) + assert.True(t, ok) + assert.Nil(t, err) +} + +func TestMatchMethod(t *testing.T) { + rt := &Route{ + tree: trie.NewTrie(), + } + n0 := getMockMethod(config.MethodGet) + n1 := getMockMethod(config.MethodPost) + e := rt.PutAPI(router.API{URLPattern: "/theboys", Method: n0}) + assert.Nil(t, e) + e = rt.PutAPI(router.API{URLPattern: "/theboys/:id", Method: n0}) + assert.Nil(t, e) + e = rt.PutAPI(router.API{URLPattern: "/vought/:id/supe/:name", Method: n1}) + assert.Nil(t, e) + + m, ok := rt.MatchAPI("/theboys", config.MethodGet) + assert.True(t, ok) + assert.NotNil(t, m) + assert.Equal(t, m.URLPattern, "/theboys") + + m, ok = rt.MatchAPI("/theboys", config.MethodPost) + assert.False(t, ok) + assert.Nil(t, m) + + m, ok = rt.MatchAPI("/vought/123/supe/startlight", config.MethodPost) + assert.True(t, ok) + assert.NotNil(t, m) + assert.Equal(t, m.URLPattern, "/vought/:id/supe/:name") + + m, ok = rt.MatchAPI("/vought/123/supe/startlight", config.MethodPost) + assert.True(t, ok) + assert.NotNil(t, m) + assert.Equal(t, m.URLPattern, "/vought/:id/supe/:name") +} + +// +//func TestUpdateMethod(t *testing.T) { +// m0 := getMockMethod(config.MethodGet) +// m1 := getMockMethod(config.MethodGet) +// m0.DubboBackendConfig.Version = "1.0.0" +// m1.DubboBackendConfig.Version = "2.0.0" +// +// rt := NewRoute() +// rt.PutAPI(router.API{URLPattern: "/marvel", Method: m0}) +// m, _ := rt.FindAPI("/marvel", config.MethodGet) +// assert.Equal(t, m.DubboBackendConfig.Version, "1.0.0") +// rt.UpdateAPI(router.API{URLPattern: "/marvel", Method: m1}) +// m, ok := rt.FindAPI("/marvel", config.MethodGet) +// assert.True(t, ok) +// assert.Equal(t, m.DubboBackendConfig.Version, "2.0.0") +// +// rt.PutAPI(router.API{URLPattern: "/theboys/:id", Method: m0}) +// m, _ = rt.FindAPI("/theBoys/12345", config.MethodGet) +// assert.Equal(t, m.DubboBackendConfig.Version, "1.0.0") +// rt.UpdateAPI(router.API{URLPattern: "/theBoys/:id", Method: m1}) +// m, ok = rt.FindAPI("/theBoys/12345", config.MethodGet) +// assert.True(t, ok) +// assert.Equal(t, m.DubboBackendConfig.Version, "2.0.0") +//} + +//func TestSearchWildcard(t *testing.T) { +// rt := &Route{ +// tree: avltree.NewWithStringComparator(), +// wildcardTree: avltree.NewWithStringComparator(), +// } +// n0 := getMockMethod(config.MethodGet) +// e := rt.PutAPI(router.API{URLPattern: "/theboys", Method: n0}) +// assert.Nil(t, e) +// e = rt.PutAPI(router.API{URLPattern: "/theboys/:id", Method: n0}) +// assert.Nil(t, e) +// e = rt.PutAPI(router.API{URLPattern: "/vought/:id/supe/:name", Method: n0}) +// assert.Nil(t, e) +// +// _, ok := rt.searchWildcard("/marvel") +// assert.False(t, ok) +// _, ok = rt.searchWildcard("/theboys/:id/age") +// assert.False(t, ok) +// _, ok = rt.searchWildcard("/theboys/butcher") +// assert.True(t, ok) +// _, ok = rt.searchWildcard("/vought/:id/supe/homelander") +// assert.True(t, ok) +//} + +func TestWildcardMatch(t *testing.T) { + vals := wildcardMatch("/vought/:id", "/vought/12345") + assert.NotNil(t, vals) + assert.Equal(t, vals.Get("id"), "12345") + vals = wildcardMatch("/vought/:id", "/vought/125abc") + assert.NotNil(t, vals) + assert.Equal(t, vals.Get("id"), "125abc") + vals = wildcardMatch("/vought/:id", "/vought/1234abcd/status") + assert.Nil(t, vals) + vals = wildcardMatch("/voughT/:id/:action", "/Vought/1234abcd/attack") + assert.NotNil(t, vals) + assert.Equal(t, vals.Get("id"), "1234abcd") + assert.Equal(t, vals.Get("action"), "attack") + vals = wildcardMatch("/voughT/:id/status", "/Vought/1234abcd/status") + assert.NotNil(t, vals) + assert.Equal(t, vals.Get("id"), "1234abcd") +} + +func TestGetFilters(t *testing.T) { + rt := NewRoute() + n0 := getMockMethod(config.MethodGet) + n1 := getMockMethod(config.MethodPost) + e := rt.PutAPI(router.API{URLPattern: "/theboys", Method: n0}) + assert.Nil(t, e) + e = rt.PutAPI(router.API{URLPattern: "/theboys/:id", Method: n0}) + assert.Nil(t, e) + e = rt.PutAPI(router.API{URLPattern: "/vought/:id/supe/:name", Method: n1}) + assert.Nil(t, e) +} diff --git a/pkg/security/mock.go b/pkg/security/mock.go deleted file mode 100644 index b3fefe7ce..000000000 --- a/pkg/security/mock.go +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package security - -import ( - "context" - "errors" - "fmt" - "net/http" - "sync" -) - -import ( - "go.uber.org/atomic" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/peer" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/spiffe" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" - "github.com/apache/dubbo-go-pixiu/security/pkg/pki/util" -) - -type DirectSecretManager struct { - items map[string]*SecretItem - mu sync.RWMutex -} - -var _ SecretManager = &DirectSecretManager{} - -func NewDirectSecretManager() *DirectSecretManager { - return &DirectSecretManager{ - items: map[string]*SecretItem{}, - } -} - -func (d *DirectSecretManager) GenerateSecret(resourceName string) (*SecretItem, error) { - d.mu.RLock() - defer d.mu.RUnlock() - si, f := d.items[resourceName] - if !f { - return nil, fmt.Errorf("resource %v not found", resourceName) - } - return si, nil -} - -func (d *DirectSecretManager) Set(resourceName string, secret *SecretItem) { - d.mu.Lock() - defer d.mu.Unlock() - if secret == nil { - delete(d.items, resourceName) - } else { - d.items[resourceName] = secret - } -} - -type FakeAuthenticator struct { - AllowedToken string - AllowedCert string - Name string - - Successes *atomic.Int32 - Failures *atomic.Int32 - - mu sync.Mutex -} - -func NewFakeAuthenticator(name string) *FakeAuthenticator { - return &FakeAuthenticator{ - Name: name, - Successes: atomic.NewInt32(0), - Failures: atomic.NewInt32(0), - } -} - -func (f *FakeAuthenticator) AuthenticateRequest(req *http.Request) (*Caller, error) { - return nil, errors.New("not implemented") -} - -func (f *FakeAuthenticator) Authenticate(ctx context.Context) (*Caller, error) { - f.mu.Lock() - at := f.AllowedToken - ac := f.AllowedCert - f.mu.Unlock() - token := checkToken(ctx, at) - cert := checkCert(ctx, ac) - id := []string{spiffe.Identity{ - TrustDomain: "cluster.local", - Namespace: "fake-namespace", - ServiceAccount: "fake-sa", - }.String()} - log.WithLabels("name", f.Name, "cert", cert, "token", token).Infof("authentication complete") - if cert == nil { - f.Successes.Inc() - return &Caller{ - AuthSource: AuthSourceClientCertificate, - Identities: id, - }, nil - } - if token == nil { - f.Successes.Inc() - return &Caller{ - AuthSource: AuthSourceIDToken, - Identities: id, - }, nil - } - f.Failures.Inc() - return nil, fmt.Errorf("neither token (%v) nor cert (%v) succeeded", token, cert) -} - -func (f *FakeAuthenticator) AuthenticatorType() string { - return "fake" -} - -func (f *FakeAuthenticator) Set(token string, identity string) *FakeAuthenticator { - f.mu.Lock() - defer f.mu.Unlock() - f.AllowedToken = token - f.AllowedCert = identity - return f -} - -var _ Authenticator = &FakeAuthenticator{} - -func checkToken(ctx context.Context, expected string) error { - if expected == "" { - return fmt.Errorf("jwt authentication not allowed") - } - targetJWT, err := ExtractBearerToken(ctx) - if err != nil { - return fmt.Errorf("target JWT extraction error: %v", err) - } - if targetJWT != expected { - return fmt.Errorf("expected token %q got %q", expected, targetJWT) - } - return nil -} - -func checkCert(ctx context.Context, expected string) error { - if expected == "" { - return fmt.Errorf("cert authentication not allowed") - } - - p, ok := peer.FromContext(ctx) - if !ok || p.AuthInfo == nil { - return fmt.Errorf("no client certificate is presented") - } - - if authType := p.AuthInfo.AuthType(); authType != "tls" { - return fmt.Errorf("unsupported auth type: %q", authType) - } - - tlsInfo := p.AuthInfo.(credentials.TLSInfo) - chains := tlsInfo.State.VerifiedChains - if len(chains) == 0 || len(chains[0]) == 0 { - return fmt.Errorf("no verified chain is found") - } - - ids, err := util.ExtractIDs(chains[0][0].Extensions) - if err != nil { - return fmt.Errorf("failed to extract IDs") - } - if !sets.New(ids...).Contains(expected) { - return fmt.Errorf("expected identity %q, got %v", expected, ids) - } - - return nil -} diff --git a/pkg/security/retry.go b/pkg/security/retry.go deleted file mode 100644 index 838921c90..000000000 --- a/pkg/security/retry.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package security - -import ( - "time" -) - -import ( - retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/security/pkg/monitoring" -) - -var caLog = log.RegisterScope("ca", "ca client", 0) - -// CARetryOptions returns the default retry options recommended for CA calls -// This includes 5 retries, with backoff from 100ms -> 1.6s with jitter. -var CARetryOptions = []retry.CallOption{ - retry.WithMax(5), - retry.WithBackoff(wrapBackoffWithMetrics(retry.BackoffExponentialWithJitter(100*time.Millisecond, 0.1))), - retry.WithCodes(codes.Canceled, codes.DeadlineExceeded, codes.ResourceExhausted, codes.Aborted, codes.Internal, codes.Unavailable), -} - -// CARetryInterceptor is a grpc UnaryInterceptor that adds retry options, as a convenience wrapper -// around CARetryOptions. If needed to chain with other interceptors, the CARetryOptions can be used -// directly. -func CARetryInterceptor() grpc.DialOption { - return grpc.WithUnaryInterceptor(retry.UnaryClientInterceptor(CARetryOptions...)) -} - -// grpcretry has no hooks to trigger logic on failure (https://github.com/grpc-ecosystem/go-grpc-middleware/issues/375) -// Instead, we can wrap the backoff hook to log/increment metrics before returning the backoff result. -func wrapBackoffWithMetrics(bf retry.BackoffFunc) retry.BackoffFunc { - return func(attempt uint) time.Duration { - wait := bf(attempt) - caLog.Warnf("ca request failed, starting attempt %d in %v", attempt, wait) - monitoring.NumOutgoingRetries.With(monitoring.RequestType.Value(monitoring.CSR)).Increment() - return wait - } -} diff --git a/pkg/security/security.go b/pkg/security/security.go deleted file mode 100644 index ae2602407..000000000 --- a/pkg/security/security.go +++ /dev/null @@ -1,499 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package security - -import ( - "context" - "fmt" - "net/http" - "os" - "strings" - "time" -) - -import ( - "google.golang.org/grpc/metadata" - "istio.io/pkg/env" - istiolog "istio.io/pkg/log" -) - -const ( - // etc/certs files are used with external CA managing the certs, - // i.e. mounted Secret or external plugin. - // If present, FileMountedCerts should be true. - - // DefaultCertChainFilePath is the well-known path for an existing certificate chain file - DefaultCertChainFilePath = "./etc/certs/cert-chain.pem" - - // DefaultKeyFilePath is the well-known path for an existing key file - DefaultKeyFilePath = "./etc/certs/key.pem" - - // DefaultRootCertFilePath is the well-known path for an existing root certificate file - DefaultRootCertFilePath = "./etc/certs/root-cert.pem" - - // WorkloadIdentitySocketPath is the well-known path to the Unix Domain Socket for SDS. - WorkloadIdentitySocketPath = "./var/run/secrets/workload-spiffe-uds/socket" - - // WorkloadIdentityCredentialsPath is the well-known path to a folder with workload certificate files. - WorkloadIdentityCredentialsPath = "./var/run/secrets/workload-spiffe-credentials" - - // WorkloadIdentityCertChainPath is the well-known path to a workload certificate chain file. - WorkloadIdentityCertChainPath = WorkloadIdentityCredentialsPath + "/cert-chain.pem" - - // WorkloadIdentityKeyPath is the well-known path to a workload key file. - WorkloadIdentityKeyPath = WorkloadIdentityCredentialsPath + "/key.pem" - - // WorkloadIdentityRootCertPath is the well-known path to a workload root certificate file. - WorkloadIdentityRootCertPath = WorkloadIdentityCredentialsPath + "/root-cert.pem" - - // GkeWorkloadCertChainFilePath is the well-known path for the GKE workload certificate chain file. - // Quoted from https://cloud.google.com/traffic-director/docs/security-proxyless-setup#create-service: - // "On creation, each Pod gets a volume at /var/run/secrets/workload-spiffe-credentials." - GkeWorkloadCertChainFilePath = WorkloadIdentityCredentialsPath + "/certificates.pem" - - // GkeWorkloadKeyFilePath is the well-known path for the GKE workload certificate key file - GkeWorkloadKeyFilePath = WorkloadIdentityCredentialsPath + "/private_key.pem" - - // GkeWorkloadRootCertFilePath is the well-known path for the GKE workload root certificate file - GkeWorkloadRootCertFilePath = WorkloadIdentityCredentialsPath + "/ca_certificates.pem" - - // SystemRootCerts is special case input for root cert configuration to use system root certificates. - SystemRootCerts = "SYSTEM" - - // RootCertReqResourceName is resource name of discovery request for root certificate. - RootCertReqResourceName = "ROOTCA" - - // WorkloadKeyCertResourceName is the resource name of the discovery request for workload - // identity. - // TODO: change all the pilot one reference definition here instead. - WorkloadKeyCertResourceName = "default" - - // GCE is Credential fetcher type of Google plugin - GCE = "GoogleComputeEngine" - - // JWT is a Credential fetcher type that reads from a JWT token file - JWT = "JWT" - - // Mock is Credential fetcher type of mock plugin - Mock = "Mock" // testing only - - // GoogleCAProvider uses the Google CA for workload certificate signing - GoogleCAProvider = "GoogleCA" - - // GoogleCASProvider uses the Google certificate Authority Service to sign workload certificates - GoogleCASProvider = "GoogleCAS" - - // GkeWorkloadCertificateProvider uses the GKE workload certificates - GkeWorkloadCertificateProvider = "GkeWorkloadCertificate" - - // FileRootSystemCACert is a unique resource name signaling that the system CA certificate should be used - FileRootSystemCACert = "file-root:system" -) - -// TODO: For 1.8, make sure MeshConfig is updated with those settings, -// they should be dynamic to allow migrations without restart. -// Both are critical. -var ( - // Require3PToken disables the use of K8S 1P tokens. Note that 1P tokens can be used to request - // 3P TOKENS. A 1P token is the token automatically mounted by Kubelet and used for authentication with - // the Apiserver. - Require3PToken = env.RegisterBoolVar("REQUIRE_3P_TOKEN", false, - "Reject k8s default tokens, without audience. If false, default K8S token will be accepted") - - // TokenAudiences specifies a list of audiences for SDS trustworthy JWT. This is to make sure that the CSR requests - // contain the JWTs intended for Citadel. - TokenAudiences = strings.Split(env.RegisterStringVar("TOKEN_AUDIENCES", "istio-ca", - "A list of comma separated audiences to check in the JWT token before issuing a certificate. "+ - "The token is accepted if it matches with one of the audiences").Get(), ",") -) - -const ( - BearerTokenPrefix = "Bearer " - - K8sTokenPrefix = "Istio " - - // CertSigner info - CertSigner = "CertSigner" -) - -// Options provides all of the configuration parameters for secret discovery service -// and CA configuration. Used in both Istiod and Agent. -// TODO: ProxyConfig should have most of those, and be passed to all components -// (as source of truth) -type Options struct { - // CAEndpoint is the CA endpoint to which node agent sends CSR request. - CAEndpoint string - - // CAEndpointSAN overrides the ServerName extracted from CAEndpoint. - CAEndpointSAN string - - // The CA provider name. - CAProviderName string - - // TrustDomain corresponds to the trust root of a system. - // https://github.com/spiffe/spiffe/blob/master/standards/SPIFFE-ID.md#21-trust-domain - TrustDomain string - - // Whether to generate PKCS#8 private keys. - Pkcs8Keys bool - - // OutputKeyCertToDir is the directory for output the key and certificate - OutputKeyCertToDir string - - // ProvCert is the directory for client to provide the key and certificate to CA server when authenticating - // with mTLS. This is not used for workload mTLS communication, and is - ProvCert string - - // ClusterID is the cluster where the agent resides. - // Normally initialized from ISTIO_META_CLUSTER_ID - after a tortuous journey it - // makes its way into the ClusterID metadata of Citadel gRPC request to create the cert. - // Didn't find much doc - but I suspect used for 'central cluster' use cases - so should - // match the cluster name set in the MC setup. - ClusterID string - - // The type of Elliptical Signature algorithm to use - // when generating private keys. Currently only ECDSA is supported. - ECCSigAlg string - - // FileMountedCerts indicates whether the proxy is using file - // mounted certs created by a foreign CA. Refresh is managed by the external - // CA, by updating the Secret or VM file. We will watch the file for changes - // or check before the cert expires. This assumes the certs are in the - // well-known ./etc/certs location. - FileMountedCerts bool - - // PilotCertProvider is the provider of the Pilot certificate (PILOT_CERT_PROVIDER env) - // Determines the root CA file to use for connecting to CA gRPC: - // - istiod - // - kubernetes - // - custom - // - none - PilotCertProvider string - - // secret TTL. - SecretTTL time.Duration - - // The ratio of cert lifetime to refresh a cert. For example, at 0.10 and 1 hour TTL, - // we would refresh 6 minutes before expiration. - SecretRotationGracePeriodRatio float64 - - // STS port - STSPort int - - // authentication provider specific plugins, will exchange the token - // For example exchange long lived refresh with access tokens. - // Used by the secret fetcher when signing CSRs. - // Optional; if not present the token will be used directly - TokenExchanger TokenExchanger - - // credential fetcher. - CredFetcher CredFetcher - - // credential identity provider - CredIdentityProvider string - - // Namespace corresponding to workload - WorkloadNamespace string - - // Name of the Service Account - ServiceAccount string - - // XDS auth provider - XdsAuthProvider string - - // Token manager for the token exchange of XDS - TokenManager TokenManager - - // Cert signer info - CertSigner string - - // Delay in reading certificates from file after the change is detected. This is useful in cases - // where the write operation of key and cert take longer. - FileDebounceDuration time.Duration - - // Root Cert read from the OS - CARootPath string - - // The path for an existing certificate chain file - CertChainFilePath string - // The path for an existing key file - KeyFilePath string - // The path for an existing root certificate bundle - RootCertFilePath string -} - -// TokenManager contains methods for generating token. -type TokenManager interface { - // GenerateToken takes STS request parameters and generates token. Returns - // StsResponseParameters in JSON. - GenerateToken(parameters StsRequestParameters) ([]byte, error) - // DumpTokenStatus dumps status of all generated tokens and returns status in JSON. - DumpTokenStatus() ([]byte, error) - // GetMetadata returns the metadata headers related to the token - GetMetadata(forCA bool, xdsAuthProvider, token string) (map[string]string, error) -} - -// StsRequestParameters stores all STS request attributes defined in -// https://tools.ietf.org/html/draft-ietf-oauth-token-exchange-16#section-2.1 -type StsRequestParameters struct { - // REQUIRED. The value "urn:ietf:params:oauth:grant-type:token- exchange" - // indicates that a token exchange is being performed. - GrantType string - // OPTIONAL. Indicates the location of the target service or resource where - // the client intends to use the requested security token. - Resource string - // OPTIONAL. The logical name of the target service where the client intends - // to use the requested security token. - Audience string - // OPTIONAL. A list of space-delimited, case-sensitive strings, that allow - // the client to specify the desired Scope of the requested security token in the - // context of the service or Resource where the token will be used. - Scope string - // OPTIONAL. An identifier, for the type of the requested security token. - RequestedTokenType string - // REQUIRED. A security token that represents the identity of the party on - // behalf of whom the request is being made. - SubjectToken string - // REQUIRED. An identifier, that indicates the type of the security token in - // the "subject_token" parameter. - SubjectTokenType string - // OPTIONAL. A security token that represents the identity of the acting party. - ActorToken string - // An identifier, that indicates the type of the security token in the - // "actor_token" parameter. - ActorTokenType string -} - -// Client interface defines the clients need to implement to talk to CA for CSR. -// The Agent will create a key pair and a CSR, and use an implementation of this -// interface to get back a signed certificate. There is no guarantee that the SAN -// in the request will be returned - server may replace it. -type Client interface { - CSRSign(csrPEM []byte, certValidTTLInSec int64) ([]string, error) - Close() - // Retrieve CA root certs If CA publishes API endpoint for this - GetRootCertBundle() ([]string, error) -} - -// SecretManager defines secrets management interface which is used by SDS. -type SecretManager interface { - // GenerateSecret generates new secret for the given resource. - // - // The current implementation also watched the generated secret and trigger a callback when it is - // near expiry. It will constructs the SAN based on the token's 'sub' claim, expected to be in - // the K8S format. No other JWTs are currently supported due to client logic. If JWT is - // missing/invalid, the resourceName is used. - GenerateSecret(resourceName string) (*SecretItem, error) -} - -// TokenExchanger provides common interfaces so that authentication providers could choose to implement their specific logic. -type TokenExchanger interface { - // ExchangeToken provides a common interface to exchange an existing token for a new one. - ExchangeToken(serviceAccountToken string) (string, error) -} - -// SecretItem is the cached item in in-memory secret store. -type SecretItem struct { - CertificateChain []byte - PrivateKey []byte - - RootCert []byte - - // ResourceName passed from envoy SDS discovery request. - // "ROOTCA" for root cert request, "default" for key/cert request. - ResourceName string - - CreatedTime time.Time - - ExpireTime time.Time -} - -type CredFetcher interface { - // GetPlatformCredential fetches workload credential provided by the platform. - GetPlatformCredential() (string, error) - - // GetIdentityProvider returns the name of the IdentityProvider that can authenticate the workload credential. - GetIdentityProvider() string - - // Stop releases resources and cleans up. - Stop() -} - -// AuthSource represents where authentication result is derived from. -type AuthSource int - -const ( - AuthSourceClientCertificate AuthSource = iota - AuthSourceIDToken -) - -const ( - authorizationMeta = "authorization" -) - -// Caller carries the identity and authentication source of a caller. -type Caller struct { - AuthSource AuthSource - Identities []string -} - -type Authenticator interface { - Authenticate(ctx context.Context) (*Caller, error) - AuthenticatorType() string - AuthenticateRequest(req *http.Request) (*Caller, error) -} - -func ExtractBearerToken(ctx context.Context) (string, error) { - md, ok := metadata.FromIncomingContext(ctx) - if !ok { - return "", fmt.Errorf("no metadata is attached") - } - - authHeader, exists := md[authorizationMeta] - if !exists { - return "", fmt.Errorf("no HTTP authorization header exists") - } - - for _, value := range authHeader { - if strings.HasPrefix(value, BearerTokenPrefix) { - return strings.TrimPrefix(value, BearerTokenPrefix), nil - } - } - - return "", fmt.Errorf("no bearer token exists in HTTP authorization header") -} - -func ExtractRequestToken(req *http.Request) (string, error) { - value := req.Header.Get(authorizationMeta) - if value == "" { - return "", fmt.Errorf("no HTTP authorization header exists") - } - - if strings.HasPrefix(value, BearerTokenPrefix) { - return strings.TrimPrefix(value, BearerTokenPrefix), nil - } - if strings.HasPrefix(value, K8sTokenPrefix) { - return strings.TrimPrefix(value, K8sTokenPrefix), nil - } - - return "", fmt.Errorf("no bearer token exists in HTTP authorization header") -} - -// GetOSRootFilePath returns the first file path detected from a list of known CA certificate file paths. -// If none of the known CA certificate files are found, a warning in printed and an empty string is returned. -func GetOSRootFilePath() string { - // Get and store the OS CA certificate path for Linux systems - // Source of CA File Paths: https://golang.org/src/crypto/x509/root_linux.go - certFiles := []string{ - "/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc. - "/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL 6 - "/etc/ssl/ca-bundle.pem", // OpenSUSE - "/etc/pki/tls/cacert.pem", // OpenELEC - "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7 - "/etc/ssl/cert.pem", // Alpine Linux - "/usr/local/etc/ssl/cert.pem", // FreeBSD - } - - for _, cert := range certFiles { - if _, err := os.Stat(cert); err == nil { - istiolog.Debugf("Using OS CA certificate for proxy: %s", cert) - return cert - } - } - istiolog.Warn("OS CA Cert could not be found for agent") - return "" -} - -// CheckWorkloadCertificate returns true when the workload certificate -// files are present under the provided paths. Otherwise, return false. -func CheckWorkloadCertificate(certChainFilePath, keyFilePath, rootCertFilePath string) bool { - if _, err := os.Stat(certChainFilePath); err != nil { - return false - } - if _, err := os.Stat(keyFilePath); err != nil { - return false - } - if _, err := os.Stat(rootCertFilePath); err != nil { - return false - } - return true -} - -type SdsCertificateConfig struct { - CertificatePath string - PrivateKeyPath string - CaCertificatePath string -} - -const ( - ResourceSeparator = "~" -) - -// GetResourceName converts a SdsCertificateConfig to a string to be used as an SDS resource name -func (s SdsCertificateConfig) GetResourceName() string { - if s.IsKeyCertificate() { - return "file-cert:" + s.CertificatePath + ResourceSeparator + s.PrivateKeyPath // Format: file-cert:%s~%s - } - return "" -} - -// GetRootResourceName converts a SdsCertificateConfig to a string to be used as an SDS resource name for the root -func (s SdsCertificateConfig) GetRootResourceName() string { - if s.IsRootCertificate() { - return "file-root:" + s.CaCertificatePath // Format: file-root:%s - } - return "" -} - -// IsRootCertificate returns true if this config represents a root certificate config. -func (s SdsCertificateConfig) IsRootCertificate() bool { - return s.CaCertificatePath != "" -} - -// IsKeyCertificate returns true if this config represents key certificate config. -func (s SdsCertificateConfig) IsKeyCertificate() bool { - return s.CertificatePath != "" && s.PrivateKeyPath != "" -} - -// SdsCertificateConfigFromResourceName converts the provided resource name into a SdsCertificateConfig -// If the resource name is not valid, false is returned. -func SdsCertificateConfigFromResourceName(resource string) (SdsCertificateConfig, bool) { - if strings.HasPrefix(resource, "file-cert:") { - filesString := strings.TrimPrefix(resource, "file-cert:") - split := strings.Split(filesString, ResourceSeparator) - if len(split) != 2 { - return SdsCertificateConfig{}, false - } - return SdsCertificateConfig{split[0], split[1], ""}, true - } else if strings.HasPrefix(resource, "file-root:") { - filesString := strings.TrimPrefix(resource, "file-root:") - split := strings.Split(filesString, ResourceSeparator) - - if len(split) != 1 { - return SdsCertificateConfig{}, false - } - return SdsCertificateConfig{"", "", split[0]}, true - } else { - return SdsCertificateConfig{}, false - } -} - -// SdsCertificateConfigFromResourceNameForOSCACert converts the OS resource name into a SdsCertificateConfig -func SdsCertificateConfigFromResourceNameForOSCACert(resource string) (SdsCertificateConfig, bool) { - if resource == "" { - return SdsCertificateConfig{}, false - } - return SdsCertificateConfig{"", "", resource}, true -} diff --git a/pkg/security/security_test.go b/pkg/security/security_test.go deleted file mode 100644 index 9105f665a..000000000 --- a/pkg/security/security_test.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package security - -import ( - "testing" -) - -func TestSdsCertificateConfigFromResourceName(t *testing.T) { - cases := []struct { - name string - resource string - root bool - key bool - output SdsCertificateConfig - rootResourceName string - resourceName string - }{ - { - "cert", - "file-cert:cert~key", - false, - true, - SdsCertificateConfig{"cert", "key", ""}, - "", - "file-cert:cert~key", - }, - { - "root cert", - "file-root:root", - true, - false, - SdsCertificateConfig{"", "", "root"}, - "file-root:root", - "", - }, - { - "invalid prefix", - "file:root", - false, - false, - SdsCertificateConfig{"", "", ""}, - "", - "", - }, - { - "invalid contents", - "file-root:root~extra", - false, - false, - SdsCertificateConfig{"", "", ""}, - "", - "", - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - got, _ := SdsCertificateConfigFromResourceName(tt.resource) - if got != tt.output { - t.Fatalf("got %v, expected %v", got, tt.output) - } - if root := got.IsRootCertificate(); root != tt.root { - t.Fatalf("unexpected isRootCertificate got %v, expected %v", root, tt.root) - } - if key := got.IsKeyCertificate(); key != tt.key { - t.Fatalf("unexpected IsKeyCertificate got %v, expected %v", key, tt.key) - } - if root := got.GetRootResourceName(); root != tt.rootResourceName { - t.Fatalf("unexpected GetRootResourceName got %v, expected %v", root, tt.rootResourceName) - } - if cert := got.GetResourceName(); cert != tt.resourceName { - t.Fatalf("unexpected GetRootResourceName got %v, expected %v", cert, tt.resourceName) - } - }) - } -} diff --git a/pixiu/pkg/server/adapter_manager.go b/pkg/server/adapter_manager.go similarity index 90% rename from pixiu/pkg/server/adapter_manager.go rename to pkg/server/adapter_manager.go index 08714e06b..8276d0260 100644 --- a/pixiu/pkg/server/adapter_manager.go +++ b/pkg/server/adapter_manager.go @@ -22,10 +22,10 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/adapter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/yaml" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/adapter" + "github.com/apache/dubbo-go-pixiu/pkg/common/yaml" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) type AdapterManager struct { diff --git a/pixiu/pkg/server/adapter_manager_test.go b/pkg/server/adapter_manager_test.go similarity index 93% rename from pixiu/pkg/server/adapter_manager_test.go rename to pkg/server/adapter_manager_test.go index d079e094a..e136bf979 100644 --- a/pixiu/pkg/server/adapter_manager_test.go +++ b/pkg/server/adapter_manager_test.go @@ -26,8 +26,8 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/extension/adapter" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/adapter" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) type ( diff --git a/pixiu/pkg/server/api_config_manager.go b/pkg/server/api_config_manager.go similarity index 97% rename from pixiu/pkg/server/api_config_manager.go rename to pkg/server/api_config_manager.go index 616e555fb..1e2410982 100644 --- a/pixiu/pkg/server/api_config_manager.go +++ b/pkg/server/api_config_manager.go @@ -28,7 +28,7 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) type ( diff --git a/pixiu/pkg/server/cluster_manager.go b/pkg/server/cluster_manager.go similarity index 95% rename from pixiu/pkg/server/cluster_manager.go rename to pkg/server/cluster_manager.go index 787936180..f46a6ca13 100644 --- a/pixiu/pkg/server/cluster_manager.go +++ b/pkg/server/cluster_manager.go @@ -24,12 +24,12 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/cluster" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/cluster/loadbalancer" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/yaml" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server/controls" + "github.com/apache/dubbo-go-pixiu/pkg/cluster" + "github.com/apache/dubbo-go-pixiu/pkg/cluster/loadbalancer" + "github.com/apache/dubbo-go-pixiu/pkg/common/yaml" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/server/controls" ) // generate cluster name for unnamed cluster diff --git a/pixiu/pkg/server/cluster_manager_test.go b/pkg/server/cluster_manager_test.go similarity index 97% rename from pixiu/pkg/server/cluster_manager_test.go rename to pkg/server/cluster_manager_test.go index e014719ba..85341559f 100644 --- a/pixiu/pkg/server/cluster_manager_test.go +++ b/pkg/server/cluster_manager_test.go @@ -26,7 +26,7 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) func TestClusterManager(t *testing.T) { diff --git a/pixiu/pkg/server/controls/controls.go b/pkg/server/controls/controls.go similarity index 96% rename from pixiu/pkg/server/controls/controls.go rename to pkg/server/controls/controls.go index ccb0fcd50..aee5498d4 100644 --- a/pixiu/pkg/server/controls/controls.go +++ b/pkg/server/controls/controls.go @@ -18,7 +18,7 @@ package controls import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) type ( diff --git a/pixiu/pkg/server/controls/mocks/mocks.go b/pkg/server/controls/mocks/mocks.go similarity index 98% rename from pixiu/pkg/server/controls/mocks/mocks.go rename to pkg/server/controls/mocks/mocks.go index 558c5b8f9..90a2334a8 100644 --- a/pixiu/pkg/server/controls/mocks/mocks.go +++ b/pkg/server/controls/mocks/mocks.go @@ -26,8 +26,8 @@ import ( ) import ( - model "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" - controls "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server/controls" + model "github.com/apache/dubbo-go-pixiu/pkg/model" + controls "github.com/apache/dubbo-go-pixiu/pkg/server/controls" ) // MockClusterManager is a mock of ClusterManager interface. diff --git a/pixiu/pkg/server/dynamic_resource_manager.go b/pkg/server/dynamic_resource_manager.go similarity index 94% rename from pixiu/pkg/server/dynamic_resource_manager.go rename to pkg/server/dynamic_resource_manager.go index 64649434d..8b7163616 100644 --- a/pixiu/pkg/server/dynamic_resource_manager.go +++ b/pkg/server/dynamic_resource_manager.go @@ -23,9 +23,9 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/config/xds" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/config/xds" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) // DynamicResourceManager help to management the dynamic resource diff --git a/pixiu/pkg/server/dynamic_resource_manager_test.go b/pkg/server/dynamic_resource_manager_test.go similarity index 97% rename from pixiu/pkg/server/dynamic_resource_manager_test.go rename to pkg/server/dynamic_resource_manager_test.go index aeb9702a7..ec54f6a6c 100644 --- a/pixiu/pkg/server/dynamic_resource_manager_test.go +++ b/pkg/server/dynamic_resource_manager_test.go @@ -30,9 +30,9 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/config/xds" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server/controls" + "github.com/apache/dubbo-go-pixiu/pkg/config/xds" + "github.com/apache/dubbo-go-pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/server/controls" ) func Test_createDynamicResourceManger(t *testing.T) { diff --git a/pkg/server/http.go b/pkg/server/http.go new file mode 100644 index 000000000..b68f6d03c --- /dev/null +++ b/pkg/server/http.go @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 server + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/router/trie" + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +// DefaultHttpConnectionManager +func DefaultHttpConnectionManager() *model.HttpConnectionManagerConfig { + return &model.HttpConnectionManagerConfig{ + RouteConfig: model.RouteConfiguration{ + RouteTrie: trie.NewTrieWithDefault("/api/v1/**", model.RouteAction{ + Cluster: constant.HeaderValueAll, + }), + }, + HTTPFilters: []*model.HTTPFilter{ + { + Name: constant.HTTPProxyFilter, + }, + }, + } +} diff --git a/pixiu/pkg/server/listener_manager.go b/pkg/server/listener_manager.go similarity index 96% rename from pixiu/pkg/server/listener_manager.go rename to pkg/server/listener_manager.go index 94ca29b19..1cdccda4b 100644 --- a/pixiu/pkg/server/listener_manager.go +++ b/pkg/server/listener_manager.go @@ -32,10 +32,10 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/shutdown" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/listener" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/common/shutdown" + "github.com/apache/dubbo-go-pixiu/pkg/listener" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) // wrapListenerService wrap listener service and its configuration. diff --git a/pixiu/pkg/server/otel.go b/pkg/server/otel.go similarity index 94% rename from pixiu/pkg/server/otel.go rename to pkg/server/otel.go index 050a021b0..3ff18cffc 100644 --- a/pixiu/pkg/server/otel.go +++ b/pkg/server/otel.go @@ -31,8 +31,8 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) func registerOtelMetricMeter(conf model.Metric) { diff --git a/pixiu/pkg/server/pixiu_start.go b/pkg/server/pixiu_start.go similarity index 93% rename from pixiu/pkg/server/pixiu_start.go rename to pkg/server/pixiu_start.go index 8dbb76cce..2ae9fd37f 100644 --- a/pixiu/pkg/server/pixiu_start.go +++ b/pkg/server/pixiu_start.go @@ -24,11 +24,11 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/tracing" + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/config" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/tracing" ) var server *Server diff --git a/pixiu/pkg/server/router_manager.go b/pkg/server/router_manager.go similarity index 96% rename from pixiu/pkg/server/router_manager.go rename to pkg/server/router_manager.go index 1b1d4f734..e73b7814c 100644 --- a/pixiu/pkg/server/router_manager.go +++ b/pkg/server/router_manager.go @@ -18,7 +18,7 @@ package server import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) type ( diff --git a/pkg/spiffe/spiffe.go b/pkg/spiffe/spiffe.go deleted file mode 100644 index 7d093e8cf..000000000 --- a/pkg/spiffe/spiffe.go +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package spiffe - -import ( - "crypto/tls" - "crypto/x509" - "encoding/json" - "encoding/pem" - "fmt" - "net" - "net/http" - "net/url" - "strings" - "sync" - "time" -) - -import ( - "gopkg.in/square/go-jose.v2" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" -) - -const ( - Scheme = "spiffe" - - URIPrefix = Scheme + "://" - URIPrefixLen = len(URIPrefix) - - // The default SPIFFE URL value for trust domain - defaultTrustDomain = constants.DefaultKubernetesDomain - - ServiceAccountSegment = "sa" - NamespaceSegment = "ns" -) - -var ( - trustDomain = defaultTrustDomain - trustDomainMutex sync.RWMutex - - firstRetryBackOffTime = time.Millisecond * 50 - - spiffeLog = log.RegisterScope("spiffe", "SPIFFE library logging", 0) - - totalRetryTimeout = time.Second * 10 -) - -type Identity struct { - TrustDomain string - Namespace string - ServiceAccount string -} - -func ParseIdentity(s string) (Identity, error) { - if !strings.HasPrefix(s, URIPrefix) { - return Identity{}, fmt.Errorf("identity is not a spiffe format: %v", s) - } - split := strings.Split(s[URIPrefixLen:], "/") - if len(split) != 5 { - return Identity{}, fmt.Errorf("identity is not a spiffe format: %v", s) - } - if split[1] != NamespaceSegment || split[3] != ServiceAccountSegment { - return Identity{}, fmt.Errorf("identity is not a spiffe format: %v", s) - } - return Identity{ - TrustDomain: split[0], - Namespace: split[2], - ServiceAccount: split[4], - }, nil -} - -func (i Identity) String() string { - return URIPrefix + i.TrustDomain + "/ns/" + i.Namespace + "/sa/" + i.ServiceAccount -} - -type bundleDoc struct { - jose.JSONWebKeySet - Sequence uint64 `json:"spiffe_sequence,omitempty"` - RefreshHint int `json:"spiffe_refresh_hint,omitempty"` -} - -func SetTrustDomain(value string) { - // Replace special characters in spiffe - v := strings.Replace(value, "@", ".", -1) - trustDomainMutex.Lock() - trustDomain = v - trustDomainMutex.Unlock() -} - -func GetTrustDomain() string { - trustDomainMutex.RLock() - defer trustDomainMutex.RUnlock() - return trustDomain -} - -// GenSpiffeURI returns the formatted uri(SPIFFE format for now) for the certificate. -func GenSpiffeURI(ns, serviceAccount string) (string, error) { - var err error - if ns == "" || serviceAccount == "" { - err = fmt.Errorf( - "namespace or service account empty for SPIFFE uri ns=%v serviceAccount=%v", ns, serviceAccount) - } - return URIPrefix + GetTrustDomain() + "/ns/" + ns + "/sa/" + serviceAccount, err -} - -// MustGenSpiffeURI returns the formatted uri(SPIFFE format for now) for the certificate and logs if there was an error. -func MustGenSpiffeURI(ns, serviceAccount string) string { - uri, err := GenSpiffeURI(ns, serviceAccount) - if err != nil { - spiffeLog.Debug(err.Error()) - } - return uri -} - -// ExpandWithTrustDomains expands a given spiffe identities, plus a list of truts domain aliases. -// We ensure the returned list does not contain duplicates; the original input is always retained. -// For example, -// ExpandWithTrustDomains({"spiffe://td1/ns/def/sa/def"}, {"td1", "td2"}) returns -// -// {"spiffe://td1/ns/def/sa/def", "spiffe://td2/ns/def/sa/def"}. -// -// ExpandWithTrustDomains({"spiffe://td1/ns/def/sa/a", "spiffe://td1/ns/def/sa/b"}, {"td2"}) returns -// -// {"spiffe://td1/ns/def/sa/a", "spiffe://td2/ns/def/sa/a", "spiffe://td1/ns/def/sa/b", "spiffe://td2/ns/def/sa/b"}. -func ExpandWithTrustDomains(spiffeIdentities, trustDomainAliases []string) map[string]struct{} { - out := map[string]struct{}{} - for _, id := range spiffeIdentities { - out[id] = struct{}{} - // Expand with aliases set. - m, err := ParseIdentity(id) - if err != nil { - spiffeLog.Errorf("Failed to extract SPIFFE trust domain from %v: %v", id, err) - continue - } - for _, td := range trustDomainAliases { - m.TrustDomain = td - out[m.String()] = struct{}{} - } - } - return out -} - -// GetTrustDomainFromURISAN extracts the trust domain part from the URI SAN in the X.509 certificate. -func GetTrustDomainFromURISAN(uriSan string) (string, error) { - parsed, err := ParseIdentity(uriSan) - if err != nil { - return "", fmt.Errorf("failed to parse URI SAN %s. Error: %v", uriSan, err) - } - return parsed.TrustDomain, nil -} - -// RetrieveSpiffeBundleRootCertsFromStringInput retrieves the trusted CA certificates from a list of SPIFFE bundle endpoints. -// It can use the system cert pool and the supplied certificates to validate the endpoints. -// The input endpointTuples should be in the format of: -// "foo|URL1||bar|URL2||baz|URL3..." -func RetrieveSpiffeBundleRootCertsFromStringInput(inputString string, extraTrustedCerts []*x509.Certificate) ( - map[string][]*x509.Certificate, error) { - spiffeLog.Infof("Processing SPIFFE bundle configuration: %v", inputString) - config := make(map[string]string) - tuples := strings.Split(inputString, "||") - for _, tuple := range tuples { - items := strings.Split(tuple, "|") - if len(items) != 2 { - return nil, fmt.Errorf("config is invalid: %v. Expected |", tuple) - } - trustDomain := items[0] - endpoint := items[1] - config[trustDomain] = endpoint - } - - caCertPool, err := x509.SystemCertPool() - if err != nil { - return nil, fmt.Errorf("failed to get SystemCertPool: %v", err) - } - for _, cert := range extraTrustedCerts { - caCertPool.AddCert(cert) - } - return RetrieveSpiffeBundleRootCerts(config, caCertPool, totalRetryTimeout) -} - -// RetrieveSpiffeBundleRootCerts retrieves the trusted CA certificates from a list of SPIFFE bundle endpoints. -// It can use the system cert pool and the supplied certificates to validate the endpoints. -func RetrieveSpiffeBundleRootCerts(config map[string]string, caCertPool *x509.CertPool, retryTimeout time.Duration) ( - map[string][]*x509.Certificate, error) { - httpClient := &http.Client{ - Timeout: time.Second * 10, - } - - ret := map[string][]*x509.Certificate{} - for trustdomain, endpoint := range config { - if !strings.HasPrefix(endpoint, "https://") { - endpoint = "https://" + endpoint - } - u, err := url.Parse(endpoint) - if err != nil { - return nil, fmt.Errorf("failed to split the SPIFFE bundle URL: %v", err) - } - - config := &tls.Config{ - ServerName: u.Hostname(), - RootCAs: caCertPool, - } - - httpClient.Transport = &http.Transport{ - Proxy: http.ProxyFromEnvironment, - TLSClientConfig: config, - DialContext: (&net.Dialer{ - Timeout: time.Second * 10, - }).DialContext, - IdleConnTimeout: 90 * time.Second, - TLSHandshakeTimeout: 10 * time.Second, - ExpectContinueTimeout: 1 * time.Second, - } - - retryBackoffTime := firstRetryBackOffTime - startTime := time.Now() - var resp *http.Response - for { - resp, err = httpClient.Get(endpoint) - var errMsg string - if err != nil { - errMsg = fmt.Sprintf("Calling %s failed with error: %v", endpoint, err) - } else if resp == nil { - errMsg = fmt.Sprintf("Calling %s failed with nil response", endpoint) - } else if resp.StatusCode != http.StatusOK { - b := make([]byte, 1024) - n, _ := resp.Body.Read(b) - errMsg = fmt.Sprintf("Calling %s failed with unexpected status: %v, fetching bundle: %s", - endpoint, resp.StatusCode, string(b[:n])) - } else { - break - } - - if startTime.Add(retryTimeout).Before(time.Now()) { - return nil, fmt.Errorf("exhausted retries to fetch the SPIFFE bundle %s from url %s. Latest error: %v", - trustdomain, endpoint, errMsg) - } - - spiffeLog.Warnf("%s, retry in %v", errMsg, retryBackoffTime) - time.Sleep(retryBackoffTime) - retryBackoffTime *= 2 // Exponentially increase the retry backoff time. - } - defer resp.Body.Close() - - doc := new(bundleDoc) - if err := json.NewDecoder(resp.Body).Decode(doc); err != nil { - return nil, fmt.Errorf("trust domain [%s] at URL [%s] failed to decode bundle: %v", trustdomain, endpoint, err) - } - - var cert *x509.Certificate - for i, key := range doc.Keys { - if key.Use == "x509-svid" { - if len(key.Certificates) != 1 { - return nil, fmt.Errorf("trust domain [%s] at URL [%s] expected 1 certificate in x509-svid entry %d; got %d", - trustdomain, endpoint, i, len(key.Certificates)) - } - cert = key.Certificates[0] - } - } - if cert == nil { - return nil, fmt.Errorf("trust domain [%s] at URL [%s] does not provide a X509 SVID", trustdomain, endpoint) - } - if certs, ok := ret[trustdomain]; ok { - ret[trustdomain] = append(certs, cert) - } else { - ret[trustdomain] = []*x509.Certificate{cert} - } - } - for trustDomain, certs := range ret { - spiffeLog.Infof("Loaded SPIFFE trust bundle for: %v, containing %d certs", trustDomain, len(certs)) - } - return ret, nil -} - -// PeerCertVerifier is an instance to verify the peer certificate in the SPIFFE way using the retrieved root certificates. -type PeerCertVerifier struct { - generalCertPool *x509.CertPool - certPools map[string]*x509.CertPool -} - -// NewPeerCertVerifier returns a new PeerCertVerifier. -func NewPeerCertVerifier() *PeerCertVerifier { - return &PeerCertVerifier{ - generalCertPool: x509.NewCertPool(), - certPools: make(map[string]*x509.CertPool), - } -} - -// GetGeneralCertPool returns generalCertPool containing all root certs. -func (v *PeerCertVerifier) GetGeneralCertPool() *x509.CertPool { - return v.generalCertPool -} - -// AddMapping adds a new trust domain to certificates mapping to the certPools map. -func (v *PeerCertVerifier) AddMapping(trustDomain string, certs []*x509.Certificate) { - if v.certPools[trustDomain] == nil { - v.certPools[trustDomain] = x509.NewCertPool() - } - for _, cert := range certs { - v.certPools[trustDomain].AddCert(cert) - v.generalCertPool.AddCert(cert) - } - spiffeLog.Infof("Added %d certs to trust domain %s in peer cert verifier", len(certs), trustDomain) -} - -// AddMappingFromPEM adds multiple RootCA's to the spiffe Trust bundle in the trustDomain namespace -func (v *PeerCertVerifier) AddMappingFromPEM(trustDomain string, rootCertBytes []byte) error { - block, rest := pem.Decode(rootCertBytes) - var blockBytes []byte - - // Loop while there are no block are found - for block != nil { - blockBytes = append(blockBytes, block.Bytes...) - block, rest = pem.Decode(rest) - } - - rootCAs, err := x509.ParseCertificates(blockBytes) - if err != nil { - spiffeLog.Errorf("parse certificate from rootPEM got error: %v", err) - return fmt.Errorf("parse certificate from rootPEM got error: %v", err) - } - - v.AddMapping(trustDomain, rootCAs) - return nil -} - -// AddMappings merges a trust domain to certs map to the certPools map. -func (v *PeerCertVerifier) AddMappings(certMap map[string][]*x509.Certificate) { - for trustDomain, certs := range certMap { - v.AddMapping(trustDomain, certs) - } -} - -// VerifyPeerCert is an implementation of tls.Config.VerifyPeerCertificate. -// It verifies the peer certificate using the root certificates associated with its trust domain. -func (v *PeerCertVerifier) VerifyPeerCert(rawCerts [][]byte, _ [][]*x509.Certificate) error { - if len(rawCerts) == 0 { - // Peer doesn't present a certificate. Just skip. Other authn methods may be used. - return nil - } - var peerCert *x509.Certificate - intCertPool := x509.NewCertPool() - for id, rawCert := range rawCerts { - cert, err := x509.ParseCertificate(rawCert) - if err != nil { - return err - } - if id == 0 { - peerCert = cert - } else { - intCertPool.AddCert(cert) - } - } - if len(peerCert.URIs) != 1 { - return fmt.Errorf("peer certificate does not contain 1 URI type SAN, detected %d", len(peerCert.URIs)) - } - trustDomain, err := GetTrustDomainFromURISAN(peerCert.URIs[0].String()) - if err != nil { - return err - } - rootCertPool, ok := v.certPools[trustDomain] - if !ok { - return fmt.Errorf("no cert pool found for trust domain %s", trustDomain) - } - - _, err = peerCert.Verify(x509.VerifyOptions{ - Roots: rootCertPool, - Intermediates: intCertPool, - }) - return err -} diff --git a/pkg/spiffe/spiffe_test.go b/pkg/spiffe/spiffe_test.go deleted file mode 100644 index 54aa7213a..000000000 --- a/pkg/spiffe/spiffe_test.go +++ /dev/null @@ -1,643 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package spiffe - -import ( - "bytes" - "crypto/tls" - "crypto/x509" - "encoding/pem" - "net/http" - "net/http/httptest" - "path/filepath" - "reflect" - "strings" - "testing" - "time" -) - -import ( - "github.com/google/go-cmp/cmp" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/test/util" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" -) - -var ( - validSpiffeX509Bundle = ` -{ - "spiffe_sequence": 1, - "spiffe_refresh_hint": 450000, - "keys": [ - { - "kty": "RSA", - "use": "x509-svid", - "n": "r10W2IcjT-vvSTpaFsS4OAcPOX87kw-zKZuJgXhxDhkOQyBdPZpUfK4H8yZ2q14Laym4bmiMLocIeGP70k` + - `UXcp9T4SP-P0DmBTPx3hVgP3YteHzaKsja056VtDs9kAufmFGemTSCenMt7aSlryUbLRO0H-__fTeNkCXR7uIoq` + - `RfU6jL0nN4EBh02q724iGuX6dpJcQam5bEJjq6Kn4Ry4qn1xHXqQXM4o2f6xDT13sp4U32stpmKh0HOd1WWKr0W` + - `RYnAh4GnToKr21QySZi9QWTea3zqeFmti-Isji1dKZkgZA2S89BdTWSLe6S_9lV0mtdXvDaT8RmaIX72jE_Abhn` + - `bUYV84pNYv-T2LtIKoi5PjWk0raaYoexAjtCWiu3PnizxjYOnNwpzgQN9Qh_rY2jv74cgzG50_Ft1B7XUiakNFx` + - `AiD1k6pNuiu4toY0Es7qt1yeqaC2zcIuuV7HUv1AbFBkIdF5quJHVtZ5AE1MCh1ipLPq-lIjmFdQKSRdbssVw8y` + - `q9FtFVyVqTz9GnQtoctCIPGQqmJDWmt8E7gjFhweUQo-fGgGuTlZRl9fiPQ6luPyGQ1WL6wH79G9eu4UtmgUDNw` + - `q7kpYq0_NQ5vw_1WQSY3LsPclfKzkZ-Lw2RVef-SFVVvUFMcd_3ALeeEnnSe4GSY-7vduPUAE5qMH7M", - "e": "AQAB", - "x5c": ["MIIGlDCCBHygAwIBAgIQEW25APa7S9Sj/Nj6V6GxQTANBgkqhkiG9w0BAQsFADCBwTELMAkGA1UEBhM` + - `CVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxEzARBgNVBAoTCkdvb2dsZS` + - `BMTEMxDjAMBgNVBAsTBUNsb3VkMWAwXgYDVQQDDFdpc3Rpb192MV9jbG91ZF93b3JrbG9hZF9yb290LXNpZ25lc` + - `i0wLTIwMTgtMDQtMjVUMTQ6MTE6MzMtMDc6MDAgSzoxLCAxOkg1MnZnd0VtM3RjOjA6MTgwIBcNMTgwNDI1MjEx` + - `MTMzWhgPMjExODA0MjUyMjExMzNaMIHBMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1U` + - `EBxMNTW91bnRhaW4gVmlldzETMBEGA1UEChMKR29vZ2xlIExMQzEOMAwGA1UECxMFQ2xvdWQxYDBeBgNVBAMMV2` + - `lzdGlvX3YxX2Nsb3VkX3dvcmtsb2FkX3Jvb3Qtc2lnbmVyLTAtMjAxOC0wNC0yNVQxNDoxMTozMy0wNzowMCBLO` + - `jEsIDE6SDUydmd3RW0zdGM6MDoxODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK9dFtiHI0/r70k6` + - `WhbEuDgHDzl/O5MPsymbiYF4cQ4ZDkMgXT2aVHyuB/MmdqteC2spuG5ojC6HCHhj+9JFF3KfU+Ej/j9A5gUz8d4` + - `VYD92LXh82irI2tOelbQ7PZALn5hRnpk0gnpzLe2kpa8lGy0TtB/v/303jZAl0e7iKKkX1Ooy9JzeBAYdNqu9uI` + - `hrl+naSXEGpuWxCY6uip+EcuKp9cR16kFzOKNn+sQ09d7KeFN9rLaZiodBzndVliq9FkWJwIeBp06Cq9tUMkmYv` + - `UFk3mt86nhZrYviLI4tXSmZIGQNkvPQXU1ki3ukv/ZVdJrXV7w2k/EZmiF+9oxPwG4Z21GFfOKTWL/k9i7SCqIu` + - `T41pNK2mmKHsQI7Qlortz54s8Y2DpzcKc4EDfUIf62No7++HIMxudPxbdQe11ImpDRcQIg9ZOqTboruLaGNBLO6` + - `rdcnqmgts3CLrlex1L9QGxQZCHReariR1bWeQBNTAodYqSz6vpSI5hXUCkkXW7LFcPMqvRbRVclak8/Rp0LaHLQ` + - `iDxkKpiQ1prfBO4IxYcHlEKPnxoBrk5WUZfX4j0Opbj8hkNVi+sB+/RvXruFLZoFAzcKu5KWKtPzUOb8P9VkEmN` + - `y7D3JXys5Gfi8NkVXn/khVVb1BTHHf9wC3nhJ50nuBkmPu73bj1ABOajB+zAgMBAAGjgYMwgYAwDgYDVR0PAQH/` + - `BAQDAgEGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQ` + - `/VsuyjgRDAEmcZjyJ77619Js9ijAfBgNVHSMEGDAWgBQ/VsuyjgRDAEmcZjyJ77619Js9ijANBgkqhkiG9w0BAQ` + - `sFAAOCAgEAUc5QJOqxmMJY0E2rcHEWQYRah1vat3wuIHtEZ3SkSumyj+y9eyIHb9XTTyc4SyGyX1n8Rary8oSgQ` + - `V4cbyJTFXEEQOGLHB9/98EKThgJtfPsos2WKe/59S8yN05onpxcaL9y4S295Kv9kcSQxLm5UfjlqsKeHJZymvxi` + - `YzmBox7LA1zqcLYZvslJNkJxKAk5JA66iyDSQqOK7jIixn8pi305dFGCZglUFStwWqY6Rc9rR8EycVhSx2AhrvT` + - `7OQTVdKLfoKA84D8JZJPB7hrxqKf7JJFs87Kjt7c/5bXPFJ2osmjoNYnbHjiq64bh20sSCd630qvhhePLwjjOlB` + - `PiFyK36o/hQN871AEm1SCHy+aQcfJqF5KTgPnZQy5D+D/CGau+BfkO+WCGDVxRleYBJ4g2NbATolygB2KWXrj07` + - `U/WaWqV2hERbkmxXFh6cUdlkX2MeoG4v6ZD2OKAPx5DpJCfp0TEq6PznP+Z1mLd/ZjGsOF8R2WGQJEuU8HRzvsr` + - `0wsX9UyLMqf5XViDK11V/W+dcIvjHCayBpX2se3dfex5jFht+JcQc+iwB8caSXkR6tGSiargEtSJODORacO9IB8` + - `b6W8Sm//JWf/8zyiCcMm1i2yVVphwE1kczFwunAh0JB896VaXGVxXeKEAMQoXHjgDdCYp8/Etxjb8UkCmyjU="] - } - ] -}` - - invalidSpiffeX509Bundle = ` -{ - "spiffe_sequence": 1, - "spiffe_refresh_hint": 450000, - "keys": [ - { - "kty": "RSA", - "use": "x509-svid", - "n": "r10W2IcjT-vvSTpaFsS4OAcPOX87kw-zKZuJgXhxDhkOQyBdPZpUfK4H8yZ2q14Laym4bmiMLocIeGP70k` + - `UXcp9T4SP-P0DmBTPx3hVgP3YteHzaKsja056VtDs9kAufmFGemTSCenMt7aSlryUbLRO0H-__fTeNkCXR7uIoq` + - `RfU6jL0nN4EBh02q724iGuX6dpJcQam5bEJjq6Kn4Ry4qn1xHXqQXM4o2f6xDT13sp4U32stpmKh0HOd1WWKr0W` + - `RYnAh4GnToKr21QySZi9QWTea3zqeFmti-Isji1dKZkgZA2S89BdTWSLe6S_9lV0mtdXvDaT8RmaIX72jE_Abhn` + - `bUYV84pNYv-T2LtIKoi5PjWk0raaYoexAjtCWiu3PnizxjYOnNwpzgQN9Qh_rY2jv74cgzG50_Ft1B7XUiakNFx` + - `AiD1k6pNuiu4toY0Es7qt1yeqaC2zcIuuV7HUv1AbFBkIdF5quJHVtZ5AE1MCh1ipLPq-lIjmFdQKSRdbssVw8y` + - `q9FtFVyVqTz9GnQtoctCIPGQqmJDWmt8E7gjFhweUQo-fGgGuTlZRl9fiPQ6luPyGQ1WL6wH79G9eu4UtmgUDNw` + - `q7kpYq0_NQ5vw_1WQSY3LsPclfKzkZ-Lw2RVef-SFVVvUFMcd_3ALeeEnnSe4GSY-7vduPUAE5qMH7M", - "e": "AQAB" - } - ] -}` - - // validRootCertFile, validIntCertFile and validWorkloadCertFile are in a certification chain. - // They are generated using tools/certs/Makefile. Replace "cluster.local" with "foo.doamin.com" - // export INTERMEDIATE_DAYS=3650 - // export WORKLOAD_DAYS=3650 - // make foo-certs-selfSigned - validRootCertFile1 = filepath.Join(env.IstioSrc, "security/pkg/pki/testdata/spiffe-root-cert-1.pem") - validRootCertFile2 = filepath.Join(env.IstioSrc, "security/pkg/pki/testdata/spiffe-root-cert-2.pem") - validIntCertFile = filepath.Join(env.IstioSrc, "security/pkg/pki/testdata/spiffe-int-cert.pem") - validWorkloadCertFile = filepath.Join(env.IstioSrc, "security/pkg/pki/testdata/spiffe-workload-cert.pem") - validWorkloadKeyFile = filepath.Join(env.IstioSrc, "security/pkg/pki/testdata/spiffe-workload-key.pem") -) - -func TestGenSpiffeURI(t *testing.T) { - oldTrustDomain := GetTrustDomain() - defer SetTrustDomain(oldTrustDomain) - - testCases := []struct { - namespace string - trustDomain string - serviceAccount string - expectedError string - expectedURI string - }{ - { - serviceAccount: "sa", - trustDomain: defaultTrustDomain, - expectedError: "namespace or service account empty for SPIFFE uri", - }, - { - namespace: "ns", - trustDomain: defaultTrustDomain, - expectedError: "namespace or service account empty for SPIFFE uri", - }, - { - namespace: "namespace-foo", - serviceAccount: "service-bar", - trustDomain: defaultTrustDomain, - expectedURI: "spiffe://cluster.local/ns/namespace-foo/sa/service-bar", - }, - { - namespace: "foo", - serviceAccount: "bar", - trustDomain: defaultTrustDomain, - expectedURI: "spiffe://cluster.local/ns/foo/sa/bar", - }, - { - namespace: "foo", - serviceAccount: "bar", - trustDomain: "kube-federating-id@testproj.iam.gserviceaccount.com", - expectedURI: "spiffe://kube-federating-id.testproj.iam.gserviceaccount.com/ns/foo/sa/bar", - }, - } - for id, tc := range testCases { - SetTrustDomain(tc.trustDomain) - got, err := GenSpiffeURI(tc.namespace, tc.serviceAccount) - if tc.expectedError == "" && err != nil { - t.Errorf("teste case [%v] failed, error %v", id, tc) - } - if tc.expectedError != "" { - if err == nil { - t.Errorf("want get error %v, got nil", tc.expectedError) - } else if !strings.Contains(err.Error(), tc.expectedError) { - t.Errorf("want error contains %v, got error %v", tc.expectedError, err) - } - continue - } - if got != tc.expectedURI { - t.Errorf("unexpected subject name, want %v, got %v", tc.expectedURI, got) - } - } -} - -func TestGetSetTrustDomain(t *testing.T) { - oldTrustDomain := GetTrustDomain() - defer SetTrustDomain(oldTrustDomain) - - cases := []struct { - in string - out string - }{ - { - in: "test.local", - out: "test.local", - }, - { - in: "test@local", - out: "test.local", - }, - } - for _, c := range cases { - t.Run(c.in, func(t *testing.T) { - SetTrustDomain(c.in) - if GetTrustDomain() != c.out { - t.Errorf("expected=%s, actual=%s", c.out, GetTrustDomain()) - } - }) - } -} - -func TestMustGenSpiffeURI(t *testing.T) { - if nonsense := MustGenSpiffeURI("", ""); nonsense != "spiffe://cluster.local/ns//sa/" { - t.Errorf("Unexpected spiffe URI for empty namespace and service account: %s", nonsense) - } -} - -// The test starts one or two local servers and tests RetrieveSpiffeBundleRootCerts is able to correctly retrieve the -// SPIFFE bundles. -func TestRetrieveSpiffeBundleRootCertsFromStringInput(t *testing.T) { - inputStringTemplate1 := `foo|URL1` - inputStringTemplate2 := `foo|URL1||bar|URL2` - totalRetryTimeout = time.Millisecond * 50 - testCases := []struct { - name string - template string - trustCert bool - status int - body string - twoServers bool - errContains string - }{ - { - name: "success", - template: inputStringTemplate1, - trustCert: true, - status: http.StatusOK, - body: validSpiffeX509Bundle, - twoServers: false, - }, - { - name: "success", - template: inputStringTemplate2, - trustCert: true, - status: http.StatusOK, - body: validSpiffeX509Bundle, - twoServers: true, - }, - { - name: "Invalid input 1", - template: "foo||URL1", - trustCert: false, - status: http.StatusOK, - body: validSpiffeX509Bundle, - twoServers: false, - errContains: "config is invalid", - }, - { - name: "Invalid input 2", - template: "foo|URL1|bar|URL2", - trustCert: false, - status: http.StatusOK, - body: validSpiffeX509Bundle, - twoServers: true, - errContains: "config is invalid", - }, - { - name: "Invalid input 3", - template: "URL1||bar|URL2", - trustCert: false, - status: http.StatusOK, - body: validSpiffeX509Bundle, - twoServers: true, - errContains: "config is invalid", - }, - { - name: "Unauthenticated cert", - template: inputStringTemplate1, - trustCert: false, - status: http.StatusOK, - body: validSpiffeX509Bundle, - twoServers: false, - errContains: "x509: certificate signed by unknown authority", - }, - { - name: "non-200 status", - template: inputStringTemplate1, - trustCert: true, - status: http.StatusServiceUnavailable, - body: "tHe SYsTEm iS DowN", - twoServers: false, - errContains: "unexpected status: 503, fetching bundle: tHe SYsTEm iS DowN", - }, - { - name: "Certificate absent", - template: inputStringTemplate1, - trustCert: true, - status: http.StatusOK, - body: invalidSpiffeX509Bundle, - twoServers: false, - errContains: "expected 1 certificate in x509-svid entry 0; got 0", - }, - { - name: "invalid bundle content", - template: inputStringTemplate1, - trustCert: true, - status: http.StatusOK, - body: "NOT JSON", - twoServers: false, - errContains: "failed to decode bundle", - }, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - w.WriteHeader(testCase.status) - _, _ = w.Write([]byte(testCase.body)) - }) - server := httptest.NewTLSServer(handler) - input := strings.Replace(testCase.template, "URL1", server.Listener.Addr().String(), 1) - var trustedCerts []*x509.Certificate - if testCase.trustCert { - trustedCerts = append(trustedCerts, server.Certificate()) - } - if testCase.twoServers { - input = strings.Replace(testCase.template, "URL1", server.Listener.Addr().String(), 1) - handler2 := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - w.WriteHeader(testCase.status) - _, _ = w.Write([]byte(testCase.body)) - }) - server2 := httptest.NewTLSServer(handler2) - input = strings.Replace(input, "URL2", server2.Listener.Addr().String(), 1) - if testCase.trustCert { - trustedCerts = append(trustedCerts, server2.Certificate()) - } - - } - rootCertMap, err := RetrieveSpiffeBundleRootCertsFromStringInput(input, trustedCerts) - if testCase.errContains != "" { - if !strings.Contains(err.Error(), testCase.errContains) { - t.Errorf("unexpected error returned: %v. The error should contain: %s", err, testCase.errContains) - } - return - } - if err != nil { - t.Errorf("unexpected error: %s. Expected no error.", err) - } - if rootCertMap == nil { - t.Errorf("returned root cert map is nil") - } - }) - } -} - -// TestVerifyPeerCert tests VerifyPeerCert is effective at the client side, using a TLS server. -func TestGetGeneralCertPoolAndVerifyPeerCert(t *testing.T) { - validRootCert := string(util.ReadFile(t, validRootCertFile1)) - validRootCert2 := string(util.ReadFile(t, validRootCertFile2)) - validIntCert := string(util.ReadFile(t, validIntCertFile)) - validWorkloadCert := string(util.ReadFile(t, validWorkloadCertFile)) - validWorkloadKey := string(util.ReadFile(t, validWorkloadKeyFile)) - - server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("Response body")) - })) - - workloadCertBlock, _ := pem.Decode([]byte(validWorkloadCert)) - if workloadCertBlock == nil { - t.Fatalf("failed to decode workload PEM cert") - } - intCertBlock, _ := pem.Decode([]byte(validIntCert)) - if intCertBlock == nil { - t.Fatalf("failed to decode intermediate PEM cert") - } - serverCert := [][]byte{workloadCertBlock.Bytes, intCertBlock.Bytes} - - keyBlock, _ := pem.Decode([]byte(validWorkloadKey)) - if keyBlock == nil { - t.Fatalf("failed to parse PEM block containing the workload private key") - } - - privateKey, err := x509.ParsePKCS1PrivateKey(keyBlock.Bytes) - if err != nil { - t.Fatalf("failed to parse workload private key: %v", privateKey) - } - - server.TLS = &tls.Config{ - Certificates: []tls.Certificate{ - { - Certificate: serverCert, - PrivateKey: privateKey, - }, - }, - } - server.StartTLS() - defer server.Close() - - testCases := []struct { - name string - certMap map[string][]string - errContains string - }{ - { - name: "Successful validation", - certMap: map[string][]string{"foo.domain.com": {validRootCert}}, - }, - { - name: "Successful validation with multiple roots", - certMap: map[string][]string{"foo.domain.com": {validRootCert, validRootCert2}}, - }, - { - name: "Successful validation with multiple roots multiple mappings", - certMap: map[string][]string{ - "foo.domain.com": {validRootCert, validRootCert2}, - "bar.domain.com": {validRootCert2}, - }, - }, - { - name: "No trusted root CA", - certMap: map[string][]string{"foo.domain.com": {validRootCert2}}, - errContains: "x509: certificate signed by unknown authority", - }, - { - name: "Unknown trust domain", - certMap: map[string][]string{"bar.domain.com": {validRootCert}}, - errContains: "no cert pool found for trust domain foo.domain.com", - }, - { - name: "trustdomain not mapped to the needed root cert", - certMap: map[string][]string{ - "foo.domain.com": {validRootCert2}, - "bar.domain.com": {validRootCert}, - }, - errContains: "x509: certificate signed by unknown authority", - }, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - certMap := make(map[string][]*x509.Certificate) - for trustDomain, certStrs := range testCase.certMap { - certMap[trustDomain] = []*x509.Certificate{} - for _, certStr := range certStrs { - block, _ := pem.Decode([]byte(certStr)) - if block == nil { - t.Fatalf("Can't decode the root cert.") - } - rootCert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - t.Fatalf("Failed to parse certificate: " + err.Error()) - } - certMap[trustDomain] = append(certMap[trustDomain], rootCert) - } - } - - verifier := NewPeerCertVerifier() - verifier.AddMappings(certMap) - if verifier == nil { - t.Fatalf("Failed to create peer cert verifier.") - } - client := &http.Client{ - Timeout: time.Second, - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - RootCAs: verifier.GetGeneralCertPool(), - ServerName: "foo.domain.com/ns/foo/sa/default", - VerifyPeerCertificate: verifier.VerifyPeerCert, - }, - }, - } - - req, err := http.NewRequest("POST", "https://"+server.Listener.Addr().String(), bytes.NewBuffer([]byte("ABC"))) - if err != nil { - t.Errorf("failed to create HTTP client: %v", err) - } - _, err = client.Do(req) - if testCase.errContains != "" { - if err == nil { - t.Errorf("Expected error should contain %s but seeing no error.", testCase.errContains) - } else if !strings.Contains(err.Error(), testCase.errContains) { - t.Errorf("unexpected error returned: %v. The error should contain: %s", err, testCase.errContains) - } - return - } - if err != nil { - t.Errorf("unexpected error: %s. Expected no error.", err) - } - }) - } -} - -func TestExpandWithTrustDomains(t *testing.T) { - testCases := []struct { - name string - spiffeURI []string - trustDomains []string - want map[string]struct{} - }{ - { - name: "Basic", - spiffeURI: []string{"spiffe://cluster.local/ns/def/sa/def"}, - trustDomains: []string{ - "foo", - }, - want: map[string]struct{}{ - "spiffe://cluster.local/ns/def/sa/def": {}, - "spiffe://foo/ns/def/sa/def": {}, - }, - }, - { - name: "InvalidInput", - spiffeURI: []string{"spiffe:///abcdef", "spffff://a/b/c", "abcdef"}, - trustDomains: []string{ - "foo", - }, - want: map[string]struct{}{ - "spiffe:///abcdef": {}, - "spffff://a/b/c": {}, - "abcdef": {}, - }, - }, - { - name: "EmptyTrustDomains", - spiffeURI: []string{"spiffe://cluster.local/ns/def/sa/def"}, - trustDomains: []string{}, - want: map[string]struct{}{ - "spiffe://cluster.local/ns/def/sa/def": {}, - }, - }, - { - name: "WithOriginalTrustDomain", - spiffeURI: []string{"spiffe://cluster.local/ns/def/sa/def"}, - trustDomains: []string{ - "foo", - "cluster.local", - }, - want: map[string]struct{}{ - "spiffe://cluster.local/ns/def/sa/def": {}, - "spiffe://foo/ns/def/sa/def": {}, - }, - }, - { - name: "TwoIentities", - spiffeURI: []string{"spiffe://cluster.local/ns/def/sa/def", "spiffe://cluster.local/ns/a/sa/a"}, - trustDomains: []string{ - "foo", - }, - want: map[string]struct{}{ - "spiffe://cluster.local/ns/def/sa/def": {}, - "spiffe://foo/ns/def/sa/def": {}, - "spiffe://cluster.local/ns/a/sa/a": {}, - "spiffe://foo/ns/a/sa/a": {}, - }, - }, - { - name: "CustomIdentityFormat", - spiffeURI: []string{"spiffe://cluster.local/custom-suffix"}, - trustDomains: []string{ - "foo", - }, - want: map[string]struct{}{ - "spiffe://cluster.local/custom-suffix": {}, - }, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - got := ExpandWithTrustDomains(tc.spiffeURI, tc.trustDomains) - if diff := cmp.Diff(got, tc.want); diff != "" { - t.Errorf("unexpected expanded results: %v", diff) - } - }) - } -} - -func TestIdentity(t *testing.T) { - cases := []struct { - input string - expected *Identity - }{ - { - "spiffe://td/ns/ns/sa/sa", - &Identity{ - TrustDomain: "td", - Namespace: "ns", - ServiceAccount: "sa", - }, - }, - { - "spiffe://td.with.dots/ns/ns.with.dots/sa/sa.with.dots", - &Identity{ - TrustDomain: "td.with.dots", - Namespace: "ns.with.dots", - ServiceAccount: "sa.with.dots", - }, - }, - { - // Empty ns and sa - "spiffe://td/ns//sa/", - &Identity{TrustDomain: "td", Namespace: "", ServiceAccount: ""}, - }, - { - // Missing spiffe prefix - "td/ns/ns/sa/sa", - nil, - }, - { - // Missing SA - "spiffe://td/ns/ns/sa", - nil, - }, - { - // Trailing / - "spiffe://td/ns/ns/sa/sa/", - nil, - }, - { - // wrong separator / - "spiffe://td/ns/ns/foobar/sa/", - nil, - }, - } - for _, tt := range cases { - t.Run(tt.input, func(t *testing.T) { - got, err := ParseIdentity(tt.input) - if tt.expected == nil { - if err == nil { - t.Fatalf("expected error, got %#v", got) - } - return - } - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(got, *tt.expected) { - t.Fatalf("expected %#v, got %#v", *tt.expected, got) - } - - roundTrip := got.String() - if roundTrip != tt.input { - t.Fatalf("round trip failed, expected %q got %q", tt.input, roundTrip) - } - }) - } -} diff --git a/pkg/test/cert/ca/intermediate.go b/pkg/test/cert/ca/intermediate.go deleted file mode 100644 index c112e4425..000000000 --- a/pkg/test/cert/ca/intermediate.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ca - -import ( - "os" - "path/filepath" -) - -import ( - kubeApiCore "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/cert" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/file" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/tmpl" -) - -const ( - istioConfTemplate = ` -[ req ] -encrypt_key = no -prompt = no -utf8 = yes -default_md = sha256 -default_bits = 4096 -req_extensions = req_ext -x509_extensions = req_ext -distinguished_name = req_dn -[ req_ext ] -subjectKeyIdentifier = hash -basicConstraints = critical, CA:true, pathlen:0 -keyUsage = critical, digitalSignature, nonRepudiation, keyEncipherment, keyCertSign -subjectAltName=@san -[ san ] -DNS.1 = istiod.{{ .SystemNamespace }} -DNS.2 = istiod.{{ .SystemNamespace }}.svc -DNS.3 = istio-pilot.{{ .SystemNamespace }} -DNS.4 = istio-pilot.{{ .SystemNamespace }}.svc -[ req_dn ] -O = Istio -CN = Intermediate CA -` -) - -// NewIstioConfig creates an extensions configuration for Istio, using the given system namespace in -// the DNS SANs. -func NewIstioConfig(systemNamespace string) (string, error) { - return tmpl.Evaluate(istioConfTemplate, map[string]interface{}{ - "SystemNamespace": systemNamespace, - }) -} - -// IntermediateCA is an intermediate CA for a single cluster. -type Intermediate struct { - KeyFile string - ConfFile string - CSRFile string - CertFile string - Root Root -} - -// NewIntermediate creates a new intermediate CA for the given cluster. -func NewIntermediate(workDir, config string, root Root) (Intermediate, error) { - ca := Intermediate{ - KeyFile: filepath.Join(workDir, "ca-key.pem"), - ConfFile: filepath.Join(workDir, "ca.conf"), - CSRFile: filepath.Join(workDir, "ca.csr"), - CertFile: filepath.Join(workDir, "ca-cert.pem"), - Root: root, - } - - // Write out the CA config file. - if err := os.WriteFile(ca.ConfFile, []byte(config), os.ModePerm); err != nil { - return Intermediate{}, err - } - - // Create the key for the intermediate CA. - if err := cert.GenerateKey(ca.KeyFile); err != nil { - return Intermediate{}, err - } - - // Create the CSR for the intermediate CA. - if err := cert.GenerateCSR(ca.ConfFile, ca.KeyFile, ca.CSRFile); err != nil { - return Intermediate{}, err - } - - // Create the intermediate cert, signed by the root. - if err := cert.GenerateIntermediateCert(ca.ConfFile, ca.CSRFile, root.CertFile, - root.KeyFile, ca.CertFile); err != nil { - return Intermediate{}, err - } - - return ca, nil -} - -// NewIstioCASecret creates a secret (named "cacerts") containing the intermediate certificate and cert chain. -// If available when Istio starts, this will be used instead of Istio's autogenerated self-signed root -// (istio-ca-secret). This can be used in a multicluster environment in order to establish a common root of -// trust between the clusters. -func (ca Intermediate) NewIstioCASecret() (*kubeApiCore.Secret, error) { - caCert, err := file.AsString(ca.CertFile) - if err != nil { - return nil, err - } - caKey, err := file.AsString(ca.KeyFile) - if err != nil { - return nil, err - } - rootCert, err := file.AsString(ca.Root.CertFile) - if err != nil { - return nil, err - } - - // Create the cert chain by concatenating the intermediate and root certs. - certChain := caCert + rootCert - - return &kubeApiCore.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "cacerts", - }, - Data: map[string][]byte{ - "ca-cert.pem": []byte(caCert), - "ca-key.pem": []byte(caKey), - "cert-chain.pem": []byte(certChain), - "root-cert.pem": []byte(rootCert), - }, - }, nil -} diff --git a/pkg/test/cert/ca/root.go b/pkg/test/cert/ca/root.go deleted file mode 100644 index 0e9fd36aa..000000000 --- a/pkg/test/cert/ca/root.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ca - -import ( - "os" - "path/filepath" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/cert" -) - -var rootCAConf = ` -[ req ] -encrypt_key = no -prompt = no -utf8 = yes -default_md = sha256 -default_bits = 4096 -req_extensions = req_ext -x509_extensions = req_ext -distinguished_name = req_dn -[ req_ext ] -subjectKeyIdentifier = hash -basicConstraints = critical, CA:true -keyUsage = critical, digitalSignature, nonRepudiation, keyEncipherment, keyCertSign -[ req_dn ] -O = Istio -CN = Root CA` - -// Root contains the cryptographic files for a self-signed root CA. -type Root struct { - // KeyFile is the path to the file containing the private key for the CA. - KeyFile string - - // ConfFile is the path to the file containing the extensions configuration file. - ConfFile string - - // CSRFile used to generate the cert. - CSRFile string - - // CertFile the cert for the root CA. - CertFile string -} - -// NewRoot generates the files for a new self-signed Root CA files under the given directory. -func NewRoot(workDir string) (Root, error) { - root := Root{ - KeyFile: filepath.Join(workDir, "root-key.pem"), - ConfFile: filepath.Join(workDir, "root-ca.conf"), - CSRFile: filepath.Join(workDir, "root-ca.csr"), - CertFile: filepath.Join(workDir, "root-cert.pem"), - } - - // Write out the conf file. - if err := os.WriteFile(root.ConfFile, []byte(rootCAConf), os.ModePerm); err != nil { - return Root{}, err - } - - // Create the root key. - if err := cert.GenerateKey(root.KeyFile); err != nil { - return Root{}, err - } - - // Create the root CSR - if err := cert.GenerateCSR(root.ConfFile, root.KeyFile, root.CSRFile); err != nil { - return Root{}, err - } - - // Create the root cert - if err := cert.GenerateCert(root.ConfFile, root.CSRFile, root.KeyFile, root.CertFile); err != nil { - return Root{}, err - } - return root, nil -} diff --git a/pkg/test/cert/cert.go b/pkg/test/cert/cert.go deleted file mode 100644 index b3cc6760d..000000000 --- a/pkg/test/cert/cert.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cert - -import ( - "fmt" - "os/exec" -) - -// GenerateKey and writes output to keyFile. -func GenerateKey(keyFile string) error { - return openssl("genrsa", "-out", keyFile, "4096") -} - -// GenerateCSR and writes output to csrFile. -func GenerateCSR(confFile, keyFile, csrFile string) error { - return openssl("req", "-new", - "-config", confFile, - "-key", keyFile, - "-out", csrFile) -} - -// GenerateCert and writes output to certFile. -func GenerateCert(confFile, csrFile, keyFile, certFile string) error { - return openssl("x509", "-req", - "-days", "100000", - "-signkey", keyFile, - "-extensions", "req_ext", - "-extfile", confFile, - "-in", csrFile, - "-out", certFile) -} - -// GenerateIntermediateCert from the rootCA and writes to certFile. -func GenerateIntermediateCert(confFile, csrFile, rootCertFile, rootKeyFile, certFile string) error { - return openssl("x509", "-req", - "-days", "100000", - "-CA", rootCertFile, - "-CAkey", rootKeyFile, - "-CAcreateserial", - "-extensions", "req_ext", - "-extfile", confFile, - "-in", csrFile, - "-out", certFile) -} - -func openssl(args ...string) error { - cmd := exec.Command("openssl", args...) - if out, err := cmd.CombinedOutput(); err != nil { - return fmt.Errorf("command %s failed: %q %v", cmd.String(), string(out), err) - } - return nil -} diff --git a/pkg/test/config/mock_config.pb.go b/pkg/test/config/mock_config.pb.go deleted file mode 100644 index 68821e345..000000000 --- a/pkg/test/config/mock_config.pb.go +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.27.1 -// protoc v3.18.0 -// source: pkg/test/config/mock_config.proto - -// Basic config resource consisting -// of a set of key-value pairs - -package config - -import ( - reflect "reflect" - sync "sync" -) - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type MockConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` - Pairs []*ConfigPair `protobuf:"bytes,2,rep,name=pairs,proto3" json:"pairs,omitempty"` -} - -func (x *MockConfig) Reset() { - *x = MockConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_test_config_mock_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *MockConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*MockConfig) ProtoMessage() {} - -func (x *MockConfig) ProtoReflect() protoreflect.Message { - mi := &file_pkg_test_config_mock_config_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use MockConfig.ProtoReflect.Descriptor instead. -func (*MockConfig) Descriptor() ([]byte, []int) { - return file_pkg_test_config_mock_config_proto_rawDescGZIP(), []int{0} -} - -func (x *MockConfig) GetKey() string { - if x != nil { - return x.Key - } - return "" -} - -func (x *MockConfig) GetPairs() []*ConfigPair { - if x != nil { - return x.Pairs - } - return nil -} - -type ConfigPair struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` - Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` -} - -func (x *ConfigPair) Reset() { - *x = ConfigPair{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_test_config_mock_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ConfigPair) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ConfigPair) ProtoMessage() {} - -func (x *ConfigPair) ProtoReflect() protoreflect.Message { - mi := &file_pkg_test_config_mock_config_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ConfigPair.ProtoReflect.Descriptor instead. -func (*ConfigPair) Descriptor() ([]byte, []int) { - return file_pkg_test_config_mock_config_proto_rawDescGZIP(), []int{1} -} - -func (x *ConfigPair) GetKey() string { - if x != nil { - return x.Key - } - return "" -} - -func (x *ConfigPair) GetValue() string { - if x != nil { - return x.Value - } - return "" -} - -var File_pkg_test_config_mock_config_proto protoreflect.FileDescriptor - -var file_pkg_test_config_mock_config_proto_rawDesc = []byte{ - 0x0a, 0x21, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x2f, 0x6d, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x48, 0x0a, 0x0a, 0x4d, - 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x28, 0x0a, 0x05, 0x70, - 0x61, 0x69, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, - 0x70, 0x61, 0x69, 0x72, 0x73, 0x22, 0x34, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x50, - 0x61, 0x69, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x0a, 0x5a, 0x08, 0x2e, - 0x3b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_pkg_test_config_mock_config_proto_rawDescOnce sync.Once - file_pkg_test_config_mock_config_proto_rawDescData = file_pkg_test_config_mock_config_proto_rawDesc -) - -func file_pkg_test_config_mock_config_proto_rawDescGZIP() []byte { - file_pkg_test_config_mock_config_proto_rawDescOnce.Do(func() { - file_pkg_test_config_mock_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_pkg_test_config_mock_config_proto_rawDescData) - }) - return file_pkg_test_config_mock_config_proto_rawDescData -} - -var file_pkg_test_config_mock_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_pkg_test_config_mock_config_proto_goTypes = []interface{}{ - (*MockConfig)(nil), // 0: config.MockConfig - (*ConfigPair)(nil), // 1: config.ConfigPair -} -var file_pkg_test_config_mock_config_proto_depIdxs = []int32{ - 1, // 0: config.MockConfig.pairs:type_name -> config.ConfigPair - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name -} - -func init() { file_pkg_test_config_mock_config_proto_init() } -func file_pkg_test_config_mock_config_proto_init() { - if File_pkg_test_config_mock_config_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_pkg_test_config_mock_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MockConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_test_config_mock_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ConfigPair); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_pkg_test_config_mock_config_proto_rawDesc, - NumEnums: 0, - NumMessages: 2, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_pkg_test_config_mock_config_proto_goTypes, - DependencyIndexes: file_pkg_test_config_mock_config_proto_depIdxs, - MessageInfos: file_pkg_test_config_mock_config_proto_msgTypes, - }.Build() - File_pkg_test_config_mock_config_proto = out.File - file_pkg_test_config_mock_config_proto_rawDesc = nil - file_pkg_test_config_mock_config_proto_goTypes = nil - file_pkg_test_config_mock_config_proto_depIdxs = nil -} diff --git a/pkg/test/config/mock_config.proto b/pkg/test/config/mock_config.proto deleted file mode 100644 index ce53bd876..000000000 --- a/pkg/test/config/mock_config.proto +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - - -// Basic config resource consisting -// of a set of key-value pairs - -package config; - -option go_package = ".;config"; - -message MockConfig { - string key = 1; - repeated ConfigPair pairs = 2; -} - -message ConfigPair { - string key = 1; - string value = 2; -} diff --git a/pkg/test/csrctrl/authority/authority.go b/pkg/test/csrctrl/authority/authority.go deleted file mode 100644 index 5b589aa9f..000000000 --- a/pkg/test/csrctrl/authority/authority.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package authority - -import ( - "crypto" - "crypto/rand" - "crypto/x509" - "fmt" - "math/big" - "time" -) - -var serialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128) - -// CertificateAuthority implements a certificate authority that supports policy -// based signing. It's used by the signing controller. -type CertificateAuthority struct { - // RawCert is an optional field to determine if signing cert/key pairs have changed - RawCert []byte - // RawKey is an optional field to determine if signing cert/key pairs have changed - RawKey []byte - - Certificate *x509.Certificate - PrivateKey crypto.Signer - Backdate time.Duration - Now func() time.Time -} - -// Sign signs a certificate request, applying a SigningPolicy and returns a DER -// encoded x509 certificate. -func (ca *CertificateAuthority) Sign(crDER []byte, policy SigningPolicy) ([]byte, error) { - now := time.Now() - if ca.Now != nil { - now = ca.Now() - } - - nbf := now.Add(-ca.Backdate) - if !nbf.Before(ca.Certificate.NotAfter) { - return nil, fmt.Errorf("the signer has expired: NotAfter=%v", ca.Certificate.NotAfter) - } - - cr, err := x509.ParseCertificateRequest(crDER) - if err != nil { - return nil, fmt.Errorf("unable to parse certificate request: %v", err) - } - if err := cr.CheckSignature(); err != nil { - return nil, fmt.Errorf("unable to verify certificate request signature: %v", err) - } - - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - return nil, fmt.Errorf("unable to generate a serial number for %s: %v", cr.Subject.CommonName, err) - } - - tmpl := &x509.Certificate{ - SerialNumber: serialNumber, - Subject: cr.Subject, - DNSNames: cr.DNSNames, - IPAddresses: cr.IPAddresses, - EmailAddresses: cr.EmailAddresses, - URIs: cr.URIs, - PublicKeyAlgorithm: cr.PublicKeyAlgorithm, - PublicKey: cr.PublicKey, - Extensions: cr.Extensions, - ExtraExtensions: cr.ExtraExtensions, - NotBefore: nbf, - } - if err := policy.apply(tmpl); err != nil { - return nil, err - } - - if !tmpl.NotAfter.Before(ca.Certificate.NotAfter) { - tmpl.NotAfter = ca.Certificate.NotAfter - } - if !now.Before(ca.Certificate.NotAfter) { - return nil, fmt.Errorf("refusing to sign a certificate that expired in the past") - } - - der, err := x509.CreateCertificate(rand.Reader, tmpl, ca.Certificate, cr.PublicKey, ca.PrivateKey) - if err != nil { - return nil, fmt.Errorf("failed to sign certificate: %v", err) - } - return der, nil -} diff --git a/pkg/test/csrctrl/authority/policies.go b/pkg/test/csrctrl/authority/policies.go deleted file mode 100644 index 32390ce96..000000000 --- a/pkg/test/csrctrl/authority/policies.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package authority - -import ( - "crypto/x509" - "fmt" - "sort" - "time" -) - -import ( - capi "k8s.io/api/certificates/v1" -) - -// SigningPolicy validates a CertificateRequest before it's signed by the -// CertificateAuthority. It may default or otherwise mutate a certificate -// template. -type SigningPolicy interface { - // not-exporting apply forces signing policy implementations to be internal - // to this package. - apply(template *x509.Certificate) error -} - -// PermissiveSigningPolicy is the signing policy historically used by the local -// signer. -// -// - It forwards all SANs from the original signing request. -// - It sets allowed usages as configured in the policy. -// - It sets NotAfter based on the TTL configured in the policy. -// - It zeros all extensions. -// - It sets BasicConstraints to true. -// - It sets IsCA to false. -type PermissiveSigningPolicy struct { - // TTL is the certificate TTL. It's used to calculate the NotAfter value of - // the certificate. - TTL time.Duration - // Usages are the allowed usages of a certificate. - Usages []capi.KeyUsage -} - -func (p PermissiveSigningPolicy) apply(tmpl *x509.Certificate) error { - usage, extUsages, err := keyUsagesFromStrings(p.Usages) - if err != nil { - return err - } - tmpl.KeyUsage = usage - tmpl.ExtKeyUsage = extUsages - tmpl.NotAfter = tmpl.NotBefore.Add(p.TTL) - - tmpl.ExtraExtensions = nil - tmpl.Extensions = nil - tmpl.BasicConstraintsValid = true - tmpl.IsCA = false - - return nil -} - -var keyUsageDict = map[capi.KeyUsage]x509.KeyUsage{ - capi.UsageSigning: x509.KeyUsageDigitalSignature, - capi.UsageDigitalSignature: x509.KeyUsageDigitalSignature, - capi.UsageContentCommitment: x509.KeyUsageContentCommitment, - capi.UsageKeyEncipherment: x509.KeyUsageKeyEncipherment, - capi.UsageKeyAgreement: x509.KeyUsageKeyAgreement, - capi.UsageDataEncipherment: x509.KeyUsageDataEncipherment, - capi.UsageCertSign: x509.KeyUsageCertSign, - capi.UsageCRLSign: x509.KeyUsageCRLSign, - capi.UsageEncipherOnly: x509.KeyUsageEncipherOnly, - capi.UsageDecipherOnly: x509.KeyUsageDecipherOnly, -} - -var extKeyUsageDict = map[capi.KeyUsage]x509.ExtKeyUsage{ - capi.UsageAny: x509.ExtKeyUsageAny, - capi.UsageServerAuth: x509.ExtKeyUsageServerAuth, - capi.UsageClientAuth: x509.ExtKeyUsageClientAuth, - capi.UsageCodeSigning: x509.ExtKeyUsageCodeSigning, - capi.UsageEmailProtection: x509.ExtKeyUsageEmailProtection, - capi.UsageSMIME: x509.ExtKeyUsageEmailProtection, - capi.UsageIPsecEndSystem: x509.ExtKeyUsageIPSECEndSystem, - capi.UsageIPsecTunnel: x509.ExtKeyUsageIPSECTunnel, - capi.UsageIPsecUser: x509.ExtKeyUsageIPSECUser, - capi.UsageTimestamping: x509.ExtKeyUsageTimeStamping, - capi.UsageOCSPSigning: x509.ExtKeyUsageOCSPSigning, - capi.UsageMicrosoftSGC: x509.ExtKeyUsageMicrosoftServerGatedCrypto, - capi.UsageNetscapeSGC: x509.ExtKeyUsageNetscapeServerGatedCrypto, -} - -// keyUsagesFromStrings will translate a slice of usage strings from the -// certificates API ("pkg/apis/certificates".KeyUsage) to x509.KeyUsage and -// x509.ExtKeyUsage types. -func keyUsagesFromStrings(usages []capi.KeyUsage) (x509.KeyUsage, []x509.ExtKeyUsage, error) { - var keyUsage x509.KeyUsage - var unrecognized []capi.KeyUsage - extKeyUsages := make(map[x509.ExtKeyUsage]struct{}) - for _, usage := range usages { - if val, ok := keyUsageDict[usage]; ok { - keyUsage |= val - } else if val, ok := extKeyUsageDict[usage]; ok { - extKeyUsages[val] = struct{}{} - } else { - unrecognized = append(unrecognized, usage) - } - } - - var sorted sortedExtKeyUsage - for eku := range extKeyUsages { - sorted = append(sorted, eku) - } - sort.Sort(sorted) - - if len(unrecognized) > 0 { - return 0, nil, fmt.Errorf("unrecognized usage values: %q", unrecognized) - } - - return keyUsage, []x509.ExtKeyUsage(sorted), nil -} - -type sortedExtKeyUsage []x509.ExtKeyUsage - -func (s sortedExtKeyUsage) Len() int { - return len(s) -} - -func (s sortedExtKeyUsage) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} - -func (s sortedExtKeyUsage) Less(i, j int) bool { - return s[i] < s[j] -} diff --git a/pkg/test/csrctrl/controllers/csr_controller.go b/pkg/test/csrctrl/controllers/csr_controller.go deleted file mode 100644 index 636f475ab..000000000 --- a/pkg/test/csrctrl/controllers/csr_controller.go +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// An example implementation of a CSR Controller. -package csrctrl - -import ( - "context" - "fmt" - "time" -) - -import ( - "istio.io/pkg/log" - capi "k8s.io/api/certificates/v1" - "k8s.io/apimachinery/pkg/runtime" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/csrctrl/signer" - "github.com/apache/dubbo-go-pixiu/security/pkg/k8s/chiron" - "github.com/apache/dubbo-go-pixiu/security/pkg/pki/util" -) - -// CertificateSigningRequestSigningReconciler reconciles a CertificateSigningRequest object -type CertificateSigningRequestSigningReconciler struct { - client.Client - SignerRoot string - CtrlCertTTL time.Duration - Scheme *runtime.Scheme - SignerNames []string - Signers map[string]*signer.Signer - appendRootCert bool -} - -// +kubebuilder:rbac:groups=certificates.k8s.io,resources=certificatesigningrequests,verbs=get;list;watch -// +kubebuilder:rbac:groups=certificates.k8s.io,resources=certificatesigningrequests/status,verbs=patch -// +kubebuilder:rbac:groups=core,resources=events,verbs=create;patch - -func (r *CertificateSigningRequestSigningReconciler) Reconcile(_ context.Context, req ctrl.Request) (ctrl.Result, error) { - var csr capi.CertificateSigningRequest - if err := r.Client.Get(context.TODO(), req.NamespacedName, &csr); err != nil { - return ctrl.Result{}, fmt.Errorf("error %q getting CSR", err) - } - - var exist bool - _, exist = r.Signers[csr.Spec.SignerName] - switch { - case !csr.DeletionTimestamp.IsZero(): - log.Info("CSR has been deleted. Ignoring.") - case csr.Spec.SignerName == "": - log.Info("CSR does not have a signer name. Ignoring.") - case !exist: - log.Infof("CSR signer name does not match. Ignoring.", "signer-name: %s", csr.Spec.SignerName) - case csr.Status.Certificate != nil: - log.Info("CSR has already been signed. Ignoring.") - case IsCertificateRequestApproved(&csr): - log.Info("CSR is not approved, Ignoring.") - default: - log.Info("Signing") - x509cr, err := util.ParsePemEncodedCSR(csr.Spec.Request) - if err != nil { - log.Infof("unable to parse csr: %v", err) - return ctrl.Result{}, nil - } - - var ok bool - var signer *signer.Signer - if signer, ok = r.Signers[csr.Spec.SignerName]; !ok { - return ctrl.Result{}, fmt.Errorf("error no signer can sign this csr: %v", err) - } - requestedLifeTime := signer.CertTTL - requestedDuration, ok := csr.Annotations[chiron.RequestLifeTimeAnnotationForCertManager] - if ok { - duration, err := time.ParseDuration(requestedDuration) - if err == nil { - requestedLifeTime = duration - } - } - cert, err := signer.Sign(x509cr, csr.Spec.Usages, requestedLifeTime, r.appendRootCert) - if err != nil { - return ctrl.Result{}, fmt.Errorf("error auto signing csr: %v", err) - } - patch := client.MergeFrom(csr.DeepCopy()) - csr.Status.Certificate = cert - if err := r.Client.Status().Patch(context.TODO(), &csr, patch); err != nil { - return ctrl.Result{}, fmt.Errorf("error patching CSR: %v", err) - } - log.Infof("The CSR [%s] has been signed", csr.Spec.SignerName) - } - return ctrl.Result{}, nil -} - -func (r *CertificateSigningRequestSigningReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&capi.CertificateSigningRequest{}). - Complete(r) -} - -// IsCertificateRequestApproved returns true if a certificate request has the -// "Approved" condition and no "Denied" conditions; false otherwise. -func IsCertificateRequestApproved(csr *capi.CertificateSigningRequest) bool { - approved, denied := GetCertApprovalCondition(&csr.Status) - return approved && !denied -} - -func GetCertApprovalCondition(status *capi.CertificateSigningRequestStatus) (approved bool, denied bool) { - for _, c := range status.Conditions { - if c.Type == capi.CertificateApproved { - approved = true - } - if c.Type == capi.CertificateDenied { - denied = true - } - } - return -} diff --git a/pkg/test/csrctrl/controllers/start_csrctrl.go b/pkg/test/csrctrl/controllers/start_csrctrl.go deleted file mode 100644 index 71f6ac866..000000000 --- a/pkg/test/csrctrl/controllers/start_csrctrl.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package csrctrl - -import ( - "context" - "os" - "strings" - "time" -) - -import ( - "istio.io/pkg/log" - capi "k8s.io/api/certificates/v1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/rest" - ctrl "sigs.k8s.io/controller-runtime" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/csrctrl/signer" -) - -const ( - // Define the root path for signer to store CA and private key files. - signerRoot = "/tmp/pki/signer/" - - // The duration of the signed certificates - certificateDuration = 1 * time.Hour -) - -var ( - scheme = runtime.NewScheme() - loggingOptions = log.DefaultOptions() - _ = capi.AddToScheme(scheme) - _ = corev1.AddToScheme(scheme) -) - -type SignerRootCert struct { - Signer string - Rootcert string -} - -func RunCSRController(signerNames string, appendRootCert bool, config *rest.Config, c <-chan struct{}, - certChan chan *SignerRootCert) { - // Config Istio log - if err := log.Configure(loggingOptions); err != nil { - log.Infof("Unable to configure Istio log error: %v", err) - os.Exit(-1) - } - mgr, err := ctrl.NewManager(config, ctrl.Options{ - Scheme: scheme, - // disabel the metric server to avoid the port conflicting - MetricsBindAddress: "0", - }) - if err != nil { - log.Infof("Unable to start manager error: %v", err) - os.Exit(-1) - } - - arrSingers := strings.Split(signerNames, ",") - signersMap := make(map[string]*signer.Signer, len(arrSingers)) - for _, signerName := range arrSingers { - signer, sErr := signer.NewSigner(signerRoot, signerName, certificateDuration) - if sErr != nil { - log.Infof("Unable to start signer for [%s], error: %v", signerName, sErr) - os.Exit(-1) - } - signersMap[signerName] = signer - rootCert, rErr := os.ReadFile(signer.GetRootCerts()) - if rErr != nil { - log.Infof("Unable to read root cert for signer [%s], error: %v", signerName, sErr) - os.Exit(-1) - } - rootCertsForSigner := &SignerRootCert{ - Signer: signerName, - Rootcert: string(rootCert), - } - certChan <- rootCertsForSigner - } - - if err := (&CertificateSigningRequestSigningReconciler{ - Client: mgr.GetClient(), - SignerRoot: signerRoot, - CtrlCertTTL: certificateDuration, - Scheme: mgr.GetScheme(), - SignerNames: arrSingers, - Signers: signersMap, - appendRootCert: appendRootCert, - }).SetupWithManager(mgr); err != nil { - log.Infof("Unable to create Controller fro controller CSRSigningReconciler, error: %v", err) - os.Exit(-1) - } - ctx, cancel := context.WithCancel(context.Background()) - go func() { - <-c - cancel() - }() - // +kubebuilder:scaffold:builder - log.Info("starting manager") - if err := mgr.Start(ctx); err != nil { - log.Infof("Problem running manager, error: %v", err) - os.Exit(-1) - } -} diff --git a/pkg/test/csrctrl/signer/ca_provider.go b/pkg/test/csrctrl/signer/ca_provider.go deleted file mode 100644 index d3d267871..000000000 --- a/pkg/test/csrctrl/signer/ca_provider.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package signer - -import ( - "bytes" - "crypto" - "fmt" - "os" - "sync/atomic" - "time" -) - -import ( - "k8s.io/client-go/util/cert" - "k8s.io/client-go/util/keyutil" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/cert/ca" - "github.com/apache/dubbo-go-pixiu/pkg/test/csrctrl/authority" -) - -func newCAProvider(signerRoot, signerName string) (*caProvider, error) { - strRoot := signerRoot + "/" + signerName + "/" - // create the folder if it does not exist - if _, err := os.Stat(strRoot); os.IsNotExist(err) { - dErr := os.MkdirAll(strRoot, os.ModePerm) - if dErr != nil { - return nil, fmt.Errorf("error creating CA cert folder %s: %v", strRoot, dErr) - } - cErr := os.Chmod(strRoot, os.ModePerm) - if cErr != nil { - return nil, fmt.Errorf("error change mode of CA cert folder %s: %v", strRoot, cErr) - } - } - caLoader, err := ca.NewRoot(strRoot) - if err != nil { - return nil, fmt.Errorf("error reading CA cert file %s: %v", strRoot, err) - } - // Create the new extensions config for the CA - caConfig, err := ca.NewIstioConfig("dubbo-system") - if err != nil { - return nil, err - } - intermediateCA, err := ca.NewIntermediate(strRoot, caConfig, caLoader) - if err != nil { - return nil, err - } - ret := &caProvider{ - caLoader: caLoader, - caIntermediate: intermediateCA, - } - if err := ret.setCA(); err != nil { - return nil, err - } - - return ret, nil -} - -type caProvider struct { - caValue atomic.Value - caLoader ca.Root - caIntermediate ca.Intermediate -} - -// currentCertContent retrieve current certificate content from cert file -func (p *caProvider) currentCertContent() ([]byte, error) { - certBytes, err := os.ReadFile(p.caIntermediate.CertFile) - if err != nil { - return []byte(""), fmt.Errorf("error reading CA from cert file %s: %v", p.caLoader.CertFile, err) - } - return certBytes, nil -} - -// currentKeyContent retrieve current private key content from key file -func (p *caProvider) currentKeyContent() ([]byte, error) { - keyBytes, err := os.ReadFile(p.caIntermediate.KeyFile) - if err != nil { - return []byte(""), fmt.Errorf("error reading private key from key file %s: %v", p.caLoader.KeyFile, err) - } - return keyBytes, nil -} - -// setCA unconditionally stores the current cert/key content -func (p *caProvider) setCA() error { - certPEM, cerr := p.currentCertContent() - if cerr != nil { - return cerr - } - - keyPEM, kerr := p.currentKeyContent() - if kerr != nil { - return kerr - } - - certs, err := cert.ParseCertsPEM(certPEM) - if err != nil { - return fmt.Errorf("error reading CA cert file %q: %v", p.caLoader.CertFile, err) - } - if len(certs) != 1 { - return fmt.Errorf("error reading CA cert file %q: expected 1 certificate, found %d", p.caLoader.CertFile, len(certs)) - } - - key, err := keyutil.ParsePrivateKeyPEM(keyPEM) - if err != nil { - return fmt.Errorf("error reading CA key file %q: %v", p.caLoader.KeyFile, err) - } - priv, ok := key.(crypto.Signer) - if !ok { - return fmt.Errorf("error reading CA key file %q: key did not implement crypto.Signer", p.caLoader.KeyFile) - } - - ca := &authority.CertificateAuthority{ - RawCert: certPEM, - RawKey: keyPEM, - - Certificate: certs[0], - PrivateKey: priv, - Backdate: 5 * time.Minute, - } - p.caValue.Store(ca) - - return nil -} - -// currentCA provides the current value of the CA. -// It always check for a stale value. This is cheap because it's all an in memory cache of small slices. -func (p *caProvider) currentCA() (*authority.CertificateAuthority, error) { - certPEM, cerr := p.currentCertContent() - if cerr != nil { - return nil, cerr - } - - keyPEM, kerr := p.currentKeyContent() - if kerr != nil { - return nil, kerr - } - currCA := p.caValue.Load().(*authority.CertificateAuthority) - if bytes.Equal(currCA.RawCert, certPEM) && bytes.Equal(currCA.RawKey, keyPEM) { - return currCA, nil - } - - // the bytes weren't equal, so we have to set and then load - if err := p.setCA(); err != nil { - return currCA, err - } - return p.caValue.Load().(*authority.CertificateAuthority), nil -} diff --git a/pkg/test/csrctrl/signer/signer.go b/pkg/test/csrctrl/signer/signer.go deleted file mode 100644 index 22ac7e381..000000000 --- a/pkg/test/csrctrl/signer/signer.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package signer implements a CA signer that uses keys stored on local disk. -package signer - -import ( - "bytes" - "crypto/x509" - "encoding/pem" - "fmt" - "time" -) - -import ( - capi "k8s.io/api/certificates/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/csrctrl/authority" - "github.com/apache/dubbo-go-pixiu/security/pkg/pki/util" -) - -type Signer struct { - caProvider *caProvider - CertTTL time.Duration -} - -func NewSigner(signerRoot, signerName string, certificateDuration time.Duration) (*Signer, error) { - caProvider, err := newCAProvider(signerRoot, signerName) - if err != nil { - return nil, err - } - - ret := &Signer{ - caProvider: caProvider, - CertTTL: certificateDuration, - } - return ret, nil -} - -func (s *Signer) Sign(x509cr *x509.CertificateRequest, usages []capi.KeyUsage, requestedLifetime time.Duration, appendRootCert bool) ([]byte, error) { - currCA, err := s.caProvider.currentCA() - if err != nil { - return nil, err - } - der, err := currCA.Sign(x509cr.Raw, authority.PermissiveSigningPolicy{ - TTL: requestedLifetime, - Usages: usages, - }) - if err != nil { - return nil, err - } - - _, err = x509.ParseCertificate(der) - if err != nil { - return nil, fmt.Errorf("error decoding DER certificate bytes: %s", err.Error()) - } - - pemBytes := bytes.NewBuffer([]byte{}) - err = pem.Encode(pemBytes, &pem.Block{Type: "CERTIFICATE", Bytes: der}) - if err != nil { - return nil, fmt.Errorf("error encoding certificate PEM: %s", err.Error()) - } - - intermediateCerts, err := util.AppendRootCerts(pemBytes.Bytes(), s.caProvider.caIntermediate.CertFile) - if err != nil { - return nil, fmt.Errorf("failed to append intermediate certificates (%v)", err) - } - if appendRootCert { - rootCerts, err := util.AppendRootCerts(intermediateCerts, s.caProvider.caLoader.CertFile) - if err != nil { - return nil, fmt.Errorf("failed to append root certificates (%v)", err) - } - return rootCerts, nil - } - return intermediateCerts, nil -} - -func (s *Signer) GetRootCerts() string { - return s.caProvider.caLoader.CertFile -} diff --git a/pkg/test/datasets/Readme.md b/pkg/test/datasets/Readme.md deleted file mode 100644 index e0333ad65..000000000 --- a/pkg/test/datasets/Readme.md +++ /dev/null @@ -1,90 +0,0 @@ -# How To Use Galley Test Data Set - -## Some of this information is out-of-date. It is preserved for reference, though the datasets may still be used - -The Galley Test Data is designed to tests Galley from an inputs/outputs -perspective. It uses an embedded set of input and golden files from which -tests are calculated and executed. - -The general directory/file structure is as follows: - -```plain -# Area specific test data set. -galley/testdatasets//dataset/... - -# Custom entry-point code for the data set, in a given area. -galley/testdatasets//dataset.go - -# Generated Go file for test assets -galley/testdatasets//dataset.gen.go -``` - -## Conversion Test Data - -The Conversion test data has the following format: - -```plain -# Input file for the test -.../dataset/**/.yaml - -# Optional Mesh Config Input file -.../dataset/**/_meshconfig.yaml - -# Required expected resources file. -.../dataset/**/_expected.json -``` - -Tests can be ignored by adding a .skip file - -```plain -# Input file for the test -.../dataset/**/.yaml - -# Optional file, indicating that the test should be skipped. -.../dataset/**/.skip -``` - -The test file structure also allows multiple stages: - -```plain -# Input file for the test -.../dataset/**/_.yaml -.../dataset/**/__meshconfig.yaml -.../dataset/**/__expected.json - -e.g. -# First stage files. Meshconfig carries over to the next stage -.../dataset/**/foo_0.yaml -.../dataset/**/foo_0_meshconfig.yaml -.../dataset/**/foo_0_expected.json -# Second stage files. -.../dataset/**/foo_1.yaml -.../dataset/**/foo_1_expected.json - -``` - -The expected file structure is as follows: - -```json -{ - "collection": [ - { - "Metadata": { - "name": "output-resource-1" - }, - "Body": {} - }, - { - "Metadata": { - "name": "outout-resource-2" - }, - "Body": {} - } - ] -} -``` - -To add a new test data fileset: - 1. Create a new, appropriately named folder under dataset. - 2. Create the input, expected, and (optionally) the mesh config files. - 3. Run `BUILD_WITH_CONTAINER=1 make go-gen` from the repository root. diff --git a/pkg/test/datasets/validation/dataset.go b/pkg/test/datasets/validation/dataset.go deleted file mode 100644 index 2cd2131e8..000000000 --- a/pkg/test/datasets/validation/dataset.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validation - -import ( - "embed" -) - -// FS embeds the manifests -// -//go:embed dataset/* -var FS embed.FS diff --git a/pkg/test/datasets/validation/dataset/extensions-v1alpha1-WasmPlugin-invalid.yaml b/pkg/test/datasets/validation/dataset/extensions-v1alpha1-WasmPlugin-invalid.yaml deleted file mode 100644 index 8ad373ec3..000000000 --- a/pkg/test/datasets/validation/dataset/extensions-v1alpha1-WasmPlugin-invalid.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: extensions.istio.io/v1alpha1 -kind: WasmPlugin -metadata: - name: invalid -spec: - url: ftp://domain.com/module.wasm - sha256: test diff --git a/pkg/test/datasets/validation/dataset/extensions-v1alpha1-WasmPlugin-valid.yaml b/pkg/test/datasets/validation/dataset/extensions-v1alpha1-WasmPlugin-valid.yaml deleted file mode 100644 index 7f492e1c3..000000000 --- a/pkg/test/datasets/validation/dataset/extensions-v1alpha1-WasmPlugin-valid.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: extensions.istio.io/v1alpha1 -kind: WasmPlugin -metadata: - name: valid -spec: - selector: - matchLabels: - istio: ingressgateway - url: oci://private-registry:5000/openid-connect/openid:latest - imagePullPolicy: IfNotPresent - imagePullSecret: private-registry-pull-secret - phase: AUTHN - pluginConfig: - openid_server: authn - openid_realm: ingress diff --git a/pkg/test/datasets/validation/dataset/networking-v1alpha3-DestinationRule-invalid.yaml b/pkg/test/datasets/validation/dataset/networking-v1alpha3-DestinationRule-invalid.yaml deleted file mode 100644 index 1f467008c..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1alpha3-DestinationRule-invalid.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: invalid-destination-rule -spec: - subsets: - - name: v1 - labels: - version: v1 - - name: v2 - labels: - version: v2 diff --git a/pkg/test/datasets/validation/dataset/networking-v1alpha3-DestinationRule-valid.yaml b/pkg/test/datasets/validation/dataset/networking-v1alpha3-DestinationRule-valid.yaml deleted file mode 100644 index eeb0f96b2..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1alpha3-DestinationRule-valid.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: valid-destination-rule -spec: - host: c - subsets: - - name: v1 - labels: - version: v1 - - name: v2 - labels: - version: v2 diff --git a/pkg/test/datasets/validation/dataset/networking-v1alpha3-EnvoyFilter-invalid.yaml b/pkg/test/datasets/validation/dataset/networking-v1alpha3-EnvoyFilter-invalid.yaml deleted file mode 100644 index ed26c44d1..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1alpha3-EnvoyFilter-invalid.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: simple-envoy-filter -spec: - configPatches: - - applyTo: LISTENER - patch: {} diff --git a/pkg/test/datasets/validation/dataset/networking-v1alpha3-EnvoyFilter-valid.yaml b/pkg/test/datasets/validation/dataset/networking-v1alpha3-EnvoyFilter-valid.yaml deleted file mode 100644 index deb228806..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1alpha3-EnvoyFilter-valid.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: simple-envoy-filter -spec: - workloadSelector: - labels: - app: c - diff --git a/pkg/test/datasets/validation/dataset/networking-v1alpha3-Gateway-invalid.yaml b/pkg/test/datasets/validation/dataset/networking-v1alpha3-Gateway-invalid.yaml deleted file mode 100644 index 9d1013c3e..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1alpha3-Gateway-invalid.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# Routes TCP traffic through the ingressgateway Gateway to service A. -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: invalid-gateway -spec: - selector: - # DO NOT CHANGE THESE LABELS - # The ingressgateway is defined in install/kubernetes/helm/istio/values.yaml - # with these labels - istio: ingressgateway diff --git a/pkg/test/datasets/validation/dataset/networking-v1alpha3-Gateway-valid.yaml b/pkg/test/datasets/validation/dataset/networking-v1alpha3-Gateway-valid.yaml deleted file mode 100644 index 101939890..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1alpha3-Gateway-valid.yaml +++ /dev/null @@ -1,18 +0,0 @@ -# Routes TCP traffic through the ingressgateway Gateway to service A. -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: valid-gateway -spec: - selector: - # DO NOT CHANGE THESE LABELS - # The ingressgateway is defined in install/kubernetes/helm/istio/values.yaml - # with these labels - istio: ingressgateway - servers: - - port: - number: 31400 - protocol: TCP - name: tcp - hosts: - - a.dubbo-system.svc.cluster.local diff --git a/pkg/test/datasets/validation/dataset/networking-v1alpha3-ServiceEntry-invalid.yaml b/pkg/test/datasets/validation/dataset/networking-v1alpha3-ServiceEntry-invalid.yaml deleted file mode 100644 index 901cabe18..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1alpha3-ServiceEntry-invalid.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: invalid-service-entry -spec: - ports: - - number: 80 - name: http - protocol: HTTP - endpoints: - - address: t.dubbo-system.svc.cluster.local - ports: - http: 8080 diff --git a/pkg/test/datasets/validation/dataset/networking-v1alpha3-ServiceEntry-valid.yaml b/pkg/test/datasets/validation/dataset/networking-v1alpha3-ServiceEntry-valid.yaml deleted file mode 100644 index b4e3b3ffb..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1alpha3-ServiceEntry-valid.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: valid-service-entry -spec: - hosts: - - eu.bookinfo.com - ports: - - number: 80 - name: http - protocol: HTTP - resolution: DNS - endpoints: - # Rather than relying on an external host that might become unreachable (causing test failures) - # we can mock the external endpoint using service t which has no sidecar. - - address: t.dubbo-system.svc.cluster.local # TODO: this is brittle - ports: - http: 8080 # TODO test https diff --git a/pkg/test/datasets/validation/dataset/networking-v1alpha3-Sidecar-invalid.yaml b/pkg/test/datasets/validation/dataset/networking-v1alpha3-Sidecar-invalid.yaml deleted file mode 100644 index bf24cfa62..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1alpha3-Sidecar-invalid.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - name: invalid-sidecar-config -spec: - egress: [] diff --git a/pkg/test/datasets/validation/dataset/networking-v1alpha3-Sidecar-valid.yaml b/pkg/test/datasets/validation/dataset/networking-v1alpha3-Sidecar-valid.yaml deleted file mode 100644 index 746cd47a4..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1alpha3-Sidecar-valid.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - name: valid-sidecar-config -spec: - egress: - - hosts: - - "abc/*" diff --git a/pkg/test/datasets/validation/dataset/networking-v1alpha3-VirtualService-invalid.yaml b/pkg/test/datasets/validation/dataset/networking-v1alpha3-VirtualService-invalid.yaml deleted file mode 100644 index 73b06b8e2..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1alpha3-VirtualService-invalid.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: invalid-virtual-service -spec: - http: - - route: - - destination: - host: c - subset: v1 - weight: 75 - - destination: - host: c - subset: v2 - weight: 15 diff --git a/pkg/test/datasets/validation/dataset/networking-v1alpha3-VirtualService-regexp-invalid.yaml b/pkg/test/datasets/validation/dataset/networking-v1alpha3-VirtualService-regexp-invalid.yaml deleted file mode 100644 index 6e00bcad7..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1alpha3-VirtualService-regexp-invalid.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: ecma-not-v2 -spec: - hosts: - - "*" - gateways: - - bookinfo-gateway - http: - - match: - - uri: - regex: "^(?!..).*" - route: - - destination: - host: productpage diff --git a/pkg/test/datasets/validation/dataset/networking-v1alpha3-VirtualService-valid.yaml b/pkg/test/datasets/validation/dataset/networking-v1alpha3-VirtualService-valid.yaml deleted file mode 100644 index 1a2d2adab..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1alpha3-VirtualService-valid.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: valid-virtual-service -spec: - hosts: - - c - http: - - route: - - destination: - host: c - subset: v1 - weight: 75 - - destination: - host: c - subset: v2 - weight: 25 diff --git a/pkg/test/datasets/validation/dataset/networking-v1alpha3-WorkloadEntry-invalid.yaml b/pkg/test/datasets/validation/dataset/networking-v1alpha3-WorkloadEntry-invalid.yaml deleted file mode 100644 index 687ced45f..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1alpha3-WorkloadEntry-invalid.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: WorkloadEntry -metadata: - name: invalid-workload-entry -spec: - address: "" diff --git a/pkg/test/datasets/validation/dataset/networking-v1alpha3-WorkloadEntry-valid.yaml b/pkg/test/datasets/validation/dataset/networking-v1alpha3-WorkloadEntry-valid.yaml deleted file mode 100644 index 890886fb0..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1alpha3-WorkloadEntry-valid.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: WorkloadEntry -metadata: - name: valid-workload-entry -spec: - address: "1.2.3.4" diff --git a/pkg/test/datasets/validation/dataset/networking-v1alpha3-WorkloadGroup-invalid.yaml b/pkg/test/datasets/validation/dataset/networking-v1alpha3-WorkloadGroup-invalid.yaml deleted file mode 100644 index 5b06adc8d..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1alpha3-WorkloadGroup-invalid.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: WorkloadGroup -metadata: - name: reviews -spec: - metadata: - labels: - app.kubernetes.io/name: reviews - app.kubernetes.io/version: "1.3.4" - ".": "~" diff --git a/pkg/test/datasets/validation/dataset/networking-v1alpha3-WorkloadGroup-valid.yaml b/pkg/test/datasets/validation/dataset/networking-v1alpha3-WorkloadGroup-valid.yaml deleted file mode 100644 index 7f8e92c96..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1alpha3-WorkloadGroup-valid.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: WorkloadGroup -metadata: - name: reviews -spec: - metadata: - labels: - app.kubernetes.io/name: reviews - app.kubernetes.io/version: "1.3.4" - template: - ports: - grpc: 3550 - http: 8080 - serviceAccount: default diff --git a/pkg/test/datasets/validation/dataset/networking-v1beta-DestinationRule-invalid.yaml b/pkg/test/datasets/validation/dataset/networking-v1beta-DestinationRule-invalid.yaml deleted file mode 100644 index 8639b0266..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1beta-DestinationRule-invalid.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: networking.istio.io/v1beta1 -kind: DestinationRule -metadata: - name: invalid-destination-rule -spec: - subsets: - - name: v1 - labels: - version: v1 - - name: v2 - labels: - version: v2 diff --git a/pkg/test/datasets/validation/dataset/networking-v1beta-DestinationRule-valid.yaml b/pkg/test/datasets/validation/dataset/networking-v1beta-DestinationRule-valid.yaml deleted file mode 100644 index e03e5e87a..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1beta-DestinationRule-valid.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: networking.istio.io/v1beta1 -kind: DestinationRule -metadata: - name: valid-destination-rule -spec: - host: c - subsets: - - name: v1 - labels: - version: v1 - - name: v2 - labels: - version: v2 diff --git a/pkg/test/datasets/validation/dataset/networking-v1beta-Gateway-invalid.yaml b/pkg/test/datasets/validation/dataset/networking-v1beta-Gateway-invalid.yaml deleted file mode 100644 index ee5615464..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1beta-Gateway-invalid.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# Routes TCP traffic through the ingressgateway Gateway to service A. -apiVersion: networking.istio.io/v1beta1 -kind: Gateway -metadata: - name: invalid-gateway -spec: - selector: - # DO NOT CHANGE THESE LABELS - # The ingressgateway is defined in install/kubernetes/helm/istio/values.yaml - # with these labels - istio: ingressgateway diff --git a/pkg/test/datasets/validation/dataset/networking-v1beta-Gateway-valid.yaml b/pkg/test/datasets/validation/dataset/networking-v1beta-Gateway-valid.yaml deleted file mode 100644 index ecf366daf..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1beta-Gateway-valid.yaml +++ /dev/null @@ -1,18 +0,0 @@ -# Routes TCP traffic through the ingressgateway Gateway to service A. -apiVersion: networking.istio.io/v1beta1 -kind: Gateway -metadata: - name: valid-gateway -spec: - selector: - # DO NOT CHANGE THESE LABELS - # The ingressgateway is defined in install/kubernetes/helm/istio/values.yaml - # with these labels - istio: ingressgateway - servers: - - port: - number: 31400 - protocol: TCP - name: tcp - hosts: - - a.dubbo-system.svc.cluster.local diff --git a/pkg/test/datasets/validation/dataset/networking-v1beta-Sidecar-invalid.yaml b/pkg/test/datasets/validation/dataset/networking-v1beta-Sidecar-invalid.yaml deleted file mode 100644 index b4591e5b8..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1beta-Sidecar-invalid.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: networking.istio.io/v1beta1 -kind: Sidecar -metadata: - name: invalid-sidecar-config -spec: - egress: [] diff --git a/pkg/test/datasets/validation/dataset/networking-v1beta-Sidecar-valid.yaml b/pkg/test/datasets/validation/dataset/networking-v1beta-Sidecar-valid.yaml deleted file mode 100644 index ab99f5f40..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1beta-Sidecar-valid.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: networking.istio.io/v1beta1 -kind: Sidecar -metadata: - name: valid-sidecar-config -spec: - egress: - - hosts: - - "abc/*" diff --git a/pkg/test/datasets/validation/dataset/networking-v1beta-VirtualService-invalid.yaml b/pkg/test/datasets/validation/dataset/networking-v1beta-VirtualService-invalid.yaml deleted file mode 100644 index e20fa1b76..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1beta-VirtualService-invalid.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: networking.istio.io/v1beta1 -kind: VirtualService -metadata: - name: invalid-virtual-service -spec: - http: - - route: - - destination: - host: c - subset: v1 - weight: 75 - - destination: - host: c - subset: v2 - weight: 15 diff --git a/pkg/test/datasets/validation/dataset/networking-v1beta-VirtualService-valid.yaml b/pkg/test/datasets/validation/dataset/networking-v1beta-VirtualService-valid.yaml deleted file mode 100644 index c2dc142a5..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1beta-VirtualService-valid.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: networking.istio.io/v1beta1 -kind: VirtualService -metadata: - name: valid-virtual-service -spec: - hosts: - - c - http: - - route: - - destination: - host: c - subset: v1 - weight: 75 - - destination: - host: c - subset: v2 - weight: 25 diff --git a/pkg/test/datasets/validation/dataset/networking-v1beta-WorkloadEntry-invalid.yaml b/pkg/test/datasets/validation/dataset/networking-v1beta-WorkloadEntry-invalid.yaml deleted file mode 100644 index 5e363f5f1..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1beta-WorkloadEntry-invalid.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: networking.istio.io/v1beta1 -kind: WorkloadEntry -metadata: - name: valid-workload-entry -spec: - address: "" diff --git a/pkg/test/datasets/validation/dataset/networking-v1beta-WorkloadEntry-valid.yaml b/pkg/test/datasets/validation/dataset/networking-v1beta-WorkloadEntry-valid.yaml deleted file mode 100644 index aab96d7ad..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1beta-WorkloadEntry-valid.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: networking.istio.io/v1beta1 -kind: WorkloadEntry -metadata: - name: valid-workload-entry -spec: - address: 1.2.3.4 diff --git a/pkg/test/datasets/validation/dataset/networking-v1beta1-ProxyConfig-invalid.yaml b/pkg/test/datasets/validation/dataset/networking-v1beta1-ProxyConfig-invalid.yaml deleted file mode 100644 index f145f3fd6..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1beta1-ProxyConfig-invalid.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: networking.istio.io/v1beta1 -kind: ProxyConfig -metadata: - name: invalid-example-pc -spec: - concurrency: -1 diff --git a/pkg/test/datasets/validation/dataset/networking-v1beta1-ProxyConfig-valid.yaml b/pkg/test/datasets/validation/dataset/networking-v1beta1-ProxyConfig-valid.yaml deleted file mode 100644 index 62949171a..000000000 --- a/pkg/test/datasets/validation/dataset/networking-v1beta1-ProxyConfig-valid.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: networking.istio.io/v1beta1 -kind: ProxyConfig -metadata: - name: valid-example-pc -spec: - concurrency: 3 diff --git a/pkg/test/datasets/validation/dataset/security-v1beta1-AuthorizationPolicy-invalid.yaml b/pkg/test/datasets/validation/dataset/security-v1beta1-AuthorizationPolicy-invalid.yaml deleted file mode 100644 index 3caa847b9..000000000 --- a/pkg/test/datasets/validation/dataset/security-v1beta1-AuthorizationPolicy-invalid.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: invalid-authorization-policy -spec: - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - from: - - source: - principals: ["cluster.local/ns/default/sa/sleep"] - to: - - operation: - methods: ["GET"] - when: - - values: ["key is missing"] diff --git a/pkg/test/datasets/validation/dataset/security-v1beta1-AuthorizationPolicy-valid.yaml b/pkg/test/datasets/validation/dataset/security-v1beta1-AuthorizationPolicy-valid.yaml deleted file mode 100644 index e6b3b9fa5..000000000 --- a/pkg/test/datasets/validation/dataset/security-v1beta1-AuthorizationPolicy-valid.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: AuthorizationPolicy -metadata: - name: authorization-policy -spec: - selector: - matchLabels: - app: httpbin - version: v1 - rules: - - from: - - source: - principals: ["cluster.local/ns/default/sa/sleep"] - - source: - namespaces: ["test"] - to: - - operation: - methods: ["GET"] - paths: ["/info*"] - - operation: - methods: ["POST"] - paths: ["/data"] - when: - - key: request.auth.claims[iss] - values: ["https://accounts.google.com"] diff --git a/pkg/test/datasets/validation/dataset/security-v1beta1-PeerAuthentication-invalid.yaml b/pkg/test/datasets/validation/dataset/security-v1beta1-PeerAuthentication-invalid.yaml deleted file mode 100644 index a6a5089b5..000000000 --- a/pkg/test/datasets/validation/dataset/security-v1beta1-PeerAuthentication-invalid.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: PeerAuthentication -metadata: - name: invalid-peer-authentication -spec: - portLevelMtls: {} diff --git a/pkg/test/datasets/validation/dataset/security-v1beta1-PeerAuthentication-valid.yaml b/pkg/test/datasets/validation/dataset/security-v1beta1-PeerAuthentication-valid.yaml deleted file mode 100644 index 099077c97..000000000 --- a/pkg/test/datasets/validation/dataset/security-v1beta1-PeerAuthentication-valid.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: PeerAuthentication -metadata: - name: valid-peer-authentication -spec: - selector: - matchLabels: - app: httpbin - version: v1 - mtls: - mode: PERMISSIVE - portLevelMtls: - 8080: - mode: STRICT diff --git a/pkg/test/datasets/validation/dataset/security-v1beta1-RequestAuthentication-invalid.yaml b/pkg/test/datasets/validation/dataset/security-v1beta1-RequestAuthentication-invalid.yaml deleted file mode 100644 index 52a7e13cb..000000000 --- a/pkg/test/datasets/validation/dataset/security-v1beta1-RequestAuthentication-invalid.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: RequestAuthentication -metadata: - name: invalid-request-authentication -spec: - selector: - matchLabels: - app: httpbin - version: v1 - jwtRules: - - issuer: "" diff --git a/pkg/test/datasets/validation/dataset/security-v1beta1-RequestAuthentication-valid.yaml b/pkg/test/datasets/validation/dataset/security-v1beta1-RequestAuthentication-valid.yaml deleted file mode 100644 index 040c32b43..000000000 --- a/pkg/test/datasets/validation/dataset/security-v1beta1-RequestAuthentication-valid.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: security.istio.io/v1beta1 -kind: RequestAuthentication -metadata: - name: valid-request-authentication -spec: - selector: - matchLabels: - app: httpbin - version: v1 - jwtRules: - - issuer: example.com diff --git a/pkg/test/datasets/validation/dataset/telemetry-v1alpha1-Telemetry-invalid.yaml b/pkg/test/datasets/validation/dataset/telemetry-v1alpha1-Telemetry-invalid.yaml deleted file mode 100644 index 2a7860a7b..000000000 --- a/pkg/test/datasets/validation/dataset/telemetry-v1alpha1-Telemetry-invalid.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: telemetry.istio.io/v1alpha1 -kind: Telemetry -metadata: - name: invalid -spec: - tracing: - - randomSamplingPercentage: 101 diff --git a/pkg/test/datasets/validation/dataset/telemetry-v1alpha1-Telemetry-valid.yaml b/pkg/test/datasets/validation/dataset/telemetry-v1alpha1-Telemetry-valid.yaml deleted file mode 100644 index 93ad798ea..000000000 --- a/pkg/test/datasets/validation/dataset/telemetry-v1alpha1-Telemetry-valid.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: telemetry.istio.io/v1alpha1 -kind: Telemetry -metadata: - name: valid -spec: - tracing: - - randomSamplingPercentage: 10.00 diff --git a/pkg/test/echo/client.go b/pkg/test/echo/client.go deleted file mode 100644 index 4ae737c82..000000000 --- a/pkg/test/echo/client.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -import ( - "context" - "crypto/tls" - "crypto/x509" - "fmt" - "io" - "strings" - "time" -) - -import ( - "go.uber.org/atomic" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" - xdscreds "google.golang.org/grpc/credentials/xds" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/proto" -) - -var _ io.Closer = &Client{} - -// Client of an Echo server that simplifies request/response processing for Forward commands. -type Client struct { - conn *grpc.ClientConn - client proto.EchoTestServiceClient -} - -// New creates a new echo client.Instance that is connected to the given server address. -func New(address string, tlsSettings *common.TLSSettings, extraDialOpts ...grpc.DialOption) (*Client, error) { - // Connect to the GRPC (command) endpoint of 'this' app. - // TODO: make use of common.ConnectionTimeout once it increases - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() - dialOptions := []grpc.DialOption{grpc.WithBlock()} - if tlsSettings != nil { - cert, err := tls.X509KeyPair([]byte(tlsSettings.ClientCert), []byte(tlsSettings.Key)) - if err != nil { - return nil, err - } - - var certPool *x509.CertPool - certPool, err = x509.SystemCertPool() - if err != nil { - return nil, fmt.Errorf("failed to fetch Cert from SystemCertPool: %v", err) - } - - if tlsSettings.RootCert != "" && !certPool.AppendCertsFromPEM([]byte(tlsSettings.RootCert)) { - return nil, fmt.Errorf("failed to create cert pool") - } - cfg := credentials.NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}, RootCAs: certPool}) - // If provided, override the hostname - if tlsSettings.Hostname != "" { - dialOptions = append(dialOptions, grpc.WithAuthority(tlsSettings.Hostname)) - } - dialOptions = append(dialOptions, grpc.WithTransportCredentials(cfg)) - } else if strings.HasPrefix(address, "xds:///") { - creds, err := xdscreds.NewClientCredentials(xdscreds.ClientOptions{FallbackCreds: insecure.NewCredentials()}) - if err != nil { - return nil, err - } - dialOptions = append(dialOptions, grpc.WithTransportCredentials(creds)) - } else { - dialOptions = append(dialOptions, grpc.WithTransportCredentials(insecure.NewCredentials())) - } - dialOptions = append(dialOptions, extraDialOpts...) - conn, err := grpc.DialContext(ctx, address, dialOptions...) - if err != nil { - return nil, err - } - client := proto.NewEchoTestServiceClient(conn) - - return &Client{ - conn: conn, - client: client, - }, nil -} - -// Close the EchoClient and free any resources. -func (c *Client) Close() error { - if c.conn != nil { - return c.conn.Close() - } - return nil -} - -func (c *Client) Echo(ctx context.Context, request *proto.EchoRequest) (Response, error) { - resp, err := c.client.Echo(ctx, request) - if err != nil { - return Response{}, err - } - return parseResponse(resp.Message), nil -} - -// ForwardEcho sends the given forward request and parses the response for easier processing. Only fails if the request fails. -func (c *Client) ForwardEcho(ctx context.Context, request *proto.ForwardEchoRequest) (Responses, error) { - // Forward a request from 'this' service to the destination service. - GlobalEchoRequests.Add(uint64(request.Count)) - resp, err := c.client.ForwardEcho(ctx, request) - if err != nil { - return nil, err - } - - return ParseResponses(request, resp), nil -} - -// GlobalEchoRequests records how many echo calls we have made total, from all sources. -// Note: go tests are distinct binaries per test suite, so this is the suite level number of calls -var GlobalEchoRequests = atomic.NewUint64(0) diff --git a/pkg/test/echo/cmd/client/main.go b/pkg/test/echo/cmd/client/main.go deleted file mode 100644 index 5af8c5a0f..000000000 --- a/pkg/test/echo/cmd/client/main.go +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// An example implementation of a client. - -package main - -import ( - "context" - "fmt" - "net/url" - "os" - "strings" - "time" -) - -import ( - "github.com/spf13/cobra" - _ "google.golang.org/grpc/xds" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/cmd" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/proto" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/server/forwarder" -) - -var ( - count int - timeout time.Duration - qps int - uds string - headers []string - msg string - expect string - expectSet bool - method string - http2 bool - http3 bool - insecureSkipVerify bool - alpn []string - serverName string - serverFirst bool - followRedirects bool - clientCert string - clientKey string - - caFile string - - loggingOptions = log.DefaultOptions() - - rootCmd = &cobra.Command{ - Use: "client", - Short: "Istio test Echo client.", - SilenceUsage: true, - Long: `Istio test Echo client used for generating traffic between instances of the Echo service. -For Kubernetes, this can be run from a source pod via "kubectl exec" with the desired flags. -In general, Echo's gRPC interface (ForwardEcho) should be preferred. This client is only needed in cases -where the network configuration doesn't support gRPC to the source pod.' -`, - Args: cobra.ExactArgs(1), - PersistentPreRunE: configureLogging, - Run: func(cmd *cobra.Command, args []string) { - expectSet = cmd.Flags().Changed("expect") - // Create a request from the flags. - request, err := getRequest(args[0]) - if err != nil { - log.Fatala(err) - os.Exit(-1) - } - - // Create a forwarder. - f, err := forwarder.New(forwarder.Config{ - Request: request, - UDS: uds, - }) - if err != nil { - log.Fatalf("Failed to create forwarder: %v", err) // nolint: revive - os.Exit(-1) - } - - // Run the forwarder. - defer func() { - _ = f.Close() - }() - response, err := f.Run(context.Background()) - if err != nil { - log.Fatalf("Error %s\n", err) // nolint: revive - os.Exit(-1) - } - - // Log the output to stdout. - for _, line := range response.Output { - fmt.Println(line) - } - - log.Infof("All requests succeeded") - }, - } -) - -func configureLogging(_ *cobra.Command, _ []string) error { - if err := log.Configure(loggingOptions); err != nil { - return err - } - return nil -} - -func init() { - rootCmd.PersistentFlags().IntVar(&count, "count", common.DefaultCount, "Number of times to make the request") - rootCmd.PersistentFlags().IntVar(&qps, "qps", 0, "Queries per second") - rootCmd.PersistentFlags().DurationVar(&timeout, "timeout", common.DefaultRequestTimeout, "Request timeout") - rootCmd.PersistentFlags().StringVar(&uds, "uds", "", - "Specify the Unix Domain Socket to connect to") - rootCmd.PersistentFlags().StringSliceVarP(&headers, "header", "H", headers, - "A list of http headers (use Host for authority) - 'name: value', following curl syntax") - rootCmd.PersistentFlags().StringVar(&caFile, "ca", "", "CA root cert file") - rootCmd.PersistentFlags().StringVar(&msg, "msg", "HelloWorld", - "message to send (for websockets)") - rootCmd.PersistentFlags().StringVar(&expect, "expect", "", - "message to expect (for tcp)") - rootCmd.PersistentFlags().StringVar(&method, "method", "", "method to use (for HTTP)") - rootCmd.PersistentFlags().BoolVar(&http2, "http2", false, - "send http requests as HTTP2 with prior knowledge") - rootCmd.PersistentFlags().BoolVar(&http3, "http3", false, - "send http requests as HTTP 3") - rootCmd.PersistentFlags().BoolVarP(&insecureSkipVerify, "insecure-skip-verify", "k", insecureSkipVerify, - "do not verify TLS") - rootCmd.PersistentFlags().BoolVar(&serverFirst, "server-first", false, - "Treat as a server first protocol; do not send request until magic string is received") - rootCmd.PersistentFlags().BoolVarP(&followRedirects, "follow-redirects", "L", false, - "If enabled, will follow 3xx redirects with the Location header") - rootCmd.PersistentFlags().StringVar(&clientCert, "client-cert", "", "client certificate file to use for request") - rootCmd.PersistentFlags().StringVar(&clientKey, "client-key", "", "client certificate key file to use for request") - rootCmd.PersistentFlags().StringSliceVarP(&alpn, "alpn", "", nil, "alpn to set") - rootCmd.PersistentFlags().StringVarP(&serverName, "server-name", "", serverName, "server name to set") - - loggingOptions.AttachCobraFlags(rootCmd) - - cmd.AddFlags(rootCmd) -} - -// Adds http scheme to and url if not set. This matches curl logic -func defaultScheme(u string) string { - p, err := url.Parse(u) - if err != nil { - return u - } - if p.Scheme == "" { - return "http://" + u - } - return u -} - -func getRequest(url string) (*proto.ForwardEchoRequest, error) { - request := &proto.ForwardEchoRequest{ - Url: defaultScheme(url), - TimeoutMicros: common.DurationToMicros(timeout), - Count: int32(count), - Qps: int32(qps), - Message: msg, - Http2: http2, - Http3: http3, - ServerFirst: serverFirst, - FollowRedirects: followRedirects, - Method: method, - ServerName: serverName, - InsecureSkipVerify: insecureSkipVerify, - } - - if expectSet { - request.ExpectedResponse = &wrappers.StringValue{Value: expect} - } - - if alpn != nil { - request.Alpn = &proto.Alpn{Value: alpn} - } - - for _, header := range headers { - parts := strings.Split(header, ":") - - // require name:value format - if len(parts) != 2 { - return nil, fmt.Errorf("invalid header format: %q (want name:value)", header) - } - - request.Headers = append(request.Headers, &proto.Header{ - Key: parts[0], - Value: strings.Trim(parts[1], " "), - }) - } - - if clientCert != "" && clientKey != "" { - request.CertFile = clientCert - request.KeyFile = clientKey - } - if caFile != "" { - request.CaCertFile = caFile - } - return request, nil -} - -func main() { - if err := rootCmd.Execute(); err != nil { - os.Exit(-1) - } -} diff --git a/pkg/test/echo/cmd/server/main.go b/pkg/test/echo/cmd/server/main.go deleted file mode 100644 index 855dfae89..000000000 --- a/pkg/test/echo/cmd/server/main.go +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "os" - "os/signal" - "strconv" - "syscall" -) - -import ( - "github.com/spf13/cobra" - _ "google.golang.org/grpc/xds" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/cmd" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/server" -) - -var ( - httpPorts []int - grpcPorts []int - tcpPorts []int - tlsPorts []int - instanceIPPorts []int - localhostIPPorts []int - serverFirstPorts []int - xdsGRPCServers []int - metricsPort int - uds string - version string - cluster string - crt string - key string - istioVersion string - disableALPN bool - - loggingOptions = log.DefaultOptions() - - rootCmd = &cobra.Command{ - Use: "server", - Short: "Echo server application.", - SilenceUsage: true, - Long: `Echo application for testing Istio E2E`, - PersistentPreRunE: configureLogging, - Run: func(cmd *cobra.Command, args []string) { - ports := make(common.PortList, len(httpPorts)+len(grpcPorts)+len(tcpPorts)) - tlsByPort := map[int]bool{} - for _, p := range tlsPorts { - tlsByPort[p] = true - } - serverFirstByPort := map[int]bool{} - for _, p := range serverFirstPorts { - serverFirstByPort[p] = true - } - xdsGRPCByPort := map[int]bool{} - for _, p := range xdsGRPCServers { - xdsGRPCByPort[p] = true - } - portIndex := 0 - for i, p := range httpPorts { - ports[portIndex] = &common.Port{ - Name: "http-" + strconv.Itoa(i), - Protocol: protocol.HTTP, - Port: p, - TLS: tlsByPort[p], - ServerFirst: serverFirstByPort[p], - } - portIndex++ - } - for i, p := range grpcPorts { - ports[portIndex] = &common.Port{ - Name: "grpc-" + strconv.Itoa(i), - Protocol: protocol.GRPC, - Port: p, - TLS: tlsByPort[p], - ServerFirst: serverFirstByPort[p], - XDSServer: xdsGRPCByPort[p], - } - portIndex++ - } - for i, p := range tcpPorts { - ports[portIndex] = &common.Port{ - Name: "tcp-" + strconv.Itoa(i), - Protocol: protocol.TCP, - Port: p, - TLS: tlsByPort[p], - ServerFirst: serverFirstByPort[p], - } - portIndex++ - } - instanceIPByPort := map[int]struct{}{} - for _, p := range instanceIPPorts { - instanceIPByPort[p] = struct{}{} - } - localhostIPByPort := map[int]struct{}{} - for _, p := range localhostIPPorts { - localhostIPByPort[p] = struct{}{} - } - - s := server.New(server.Config{ - Ports: ports, - Metrics: metricsPort, - BindIPPortsMap: instanceIPByPort, - BindLocalhostPortsMap: localhostIPByPort, - TLSCert: crt, - TLSKey: key, - Version: version, - Cluster: cluster, - IstioVersion: istioVersion, - UDSServer: uds, - DisableALPN: disableALPN, - }) - - if err := s.Start(); err != nil { - log.Error(err) - os.Exit(-1) - } - defer func() { - _ = s.Close() - }() - - // Wait for the process to be shutdown. - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) - <-sigs - }, - } -) - -func configureLogging(_ *cobra.Command, _ []string) error { - if err := log.Configure(loggingOptions); err != nil { - return err - } - return nil -} - -func init() { - rootCmd.PersistentFlags().IntSliceVar(&httpPorts, "port", []int{8080}, "HTTP/1.1 ports") - rootCmd.PersistentFlags().IntSliceVar(&grpcPorts, "grpc", []int{7070}, "GRPC ports") - rootCmd.PersistentFlags().IntSliceVar(&tcpPorts, "tcp", []int{9090}, "TCP ports") - rootCmd.PersistentFlags().IntSliceVar(&tlsPorts, "tls", []int{}, "Ports that are using TLS. These must be defined as http/grpc/tcp.") - rootCmd.PersistentFlags().IntSliceVar(&instanceIPPorts, "bind-ip", []int{}, "Ports that are bound to INSTANCE_IP rather than wildcard IP.") - rootCmd.PersistentFlags().IntSliceVar(&localhostIPPorts, "bind-localhost", []int{}, "Ports that are bound to localhost rather than wildcard IP.") - rootCmd.PersistentFlags().IntSliceVar(&serverFirstPorts, "server-first", []int{}, "Ports that are server first. These must be defined as tcp.") - rootCmd.PersistentFlags().IntSliceVar(&xdsGRPCServers, "xds-grpc-server", []int{}, "Ports that should rely on XDS configuration to serve.") - rootCmd.PersistentFlags().IntVar(&metricsPort, "metrics", 0, "Metrics port") - rootCmd.PersistentFlags().StringVar(&uds, "uds", "", "HTTP server on unix domain socket") - rootCmd.PersistentFlags().StringVar(&version, "version", "", "Version string") - rootCmd.PersistentFlags().StringVar(&cluster, "cluster", "", "Cluster where this server is deployed") - rootCmd.PersistentFlags().StringVar(&crt, "crt", "", "gRPC TLS server-side certificate") - rootCmd.PersistentFlags().StringVar(&key, "key", "", "gRPC TLS server-side key") - rootCmd.PersistentFlags().StringVar(&istioVersion, "istio-version", "", "Istio sidecar version") - rootCmd.PersistentFlags().BoolVar(&disableALPN, "disable-alpn", disableALPN, "disable ALPN negotiation") - - loggingOptions.AttachCobraFlags(rootCmd) - - cmd.AddFlags(rootCmd) -} - -func main() { - if err := rootCmd.Execute(); err != nil { - os.Exit(-1) - } -} diff --git a/pkg/test/echo/common/dialer.go b/pkg/test/echo/common/dialer.go deleted file mode 100644 index 610e74cd5..000000000 --- a/pkg/test/echo/common/dialer.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package common - -import ( - "context" - "net" - "net/http" -) - -import ( - "github.com/gorilla/websocket" - "google.golang.org/grpc" -) - -var ( - // DefaultGRPCDialFunc just calls grpc.Dial directly, with no alterations to the arguments. - DefaultGRPCDialFunc = grpc.DialContext - // DefaultWebsocketDialFunc just calls dialer.Dial, with no alterations to the arguments. - DefaultWebsocketDialFunc = func(dialer *websocket.Dialer, urlStr string, requestHeader http.Header) (*websocket.Conn, *http.Response, error) { - return dialer.Dial(urlStr, requestHeader) - } - // DefaultHTTPDoFunc just calls client.Do with no alterations to the arguments. - DefaultHTTPDoFunc = func(client *http.Client, req *http.Request) (*http.Response, error) { - return client.Do(req) - } - // DefaultTCPDialFunc just calls dialer.Dial, with no alterations to the arguments. - DefaultTCPDialFunc = func(dialer net.Dialer, ctx context.Context, address string) (net.Conn, error) { - return dialer.DialContext(ctx, "tcp", address) - } - // DefaultDialer is provides defaults for all dial functions. - DefaultDialer = Dialer{ - GRPC: DefaultGRPCDialFunc, - Websocket: DefaultWebsocketDialFunc, - HTTP: DefaultHTTPDoFunc, - TCP: DefaultTCPDialFunc, - } -) - -// GRPCDialFunc a function for establishing a GRPC connection. -type GRPCDialFunc func(ctx context.Context, address string, opts ...grpc.DialOption) (*grpc.ClientConn, error) - -// WebsocketDialFunc a function for establishing a Websocket connection. -type WebsocketDialFunc func(dialer *websocket.Dialer, urlStr string, requestHeader http.Header) (*websocket.Conn, *http.Response, error) - -// HTTPDoFunc a function for executing an HTTP request. -type HTTPDoFunc func(client *http.Client, req *http.Request) (*http.Response, error) - -// TCPDialFunc a function for establishing a TCP connection. -type TCPDialFunc func(dialer net.Dialer, ctx context.Context, address string) (net.Conn, error) - -// Dialer is a replaceable set of functions for creating client-side connections for various protocols, allowing a test -// application to intercept the connection creation. -type Dialer struct { - GRPC GRPCDialFunc - Websocket WebsocketDialFunc - HTTP HTTPDoFunc - TCP TCPDialFunc -} - -// FillInDefaults fills in any missing dial functions with defaults -func (d Dialer) FillInDefaults() Dialer { - ret := DefaultDialer - - if d.GRPC != nil { - ret.GRPC = d.GRPC - } - if d.Websocket != nil { - ret.Websocket = d.Websocket - } - if d.HTTP != nil { - ret.HTTP = d.HTTP - } - if d.TCP != nil { - ret.TCP = d.TCP - } - return ret -} diff --git a/pkg/test/echo/common/metrics.go b/pkg/test/echo/common/metrics.go deleted file mode 100644 index e12ca74f9..000000000 --- a/pkg/test/echo/common/metrics.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package common - -import ( - "istio.io/pkg/monitoring" -) - -type EchoMetrics struct { - HTTPRequests monitoring.Metric - GrpcRequests monitoring.Metric - TCPRequests monitoring.Metric -} - -var ( - PortLabel = monitoring.MustCreateLabel("port") - Metrics = &EchoMetrics{ - HTTPRequests: monitoring.NewSum( - "istio_echo_http_requests_total", - "The number of http requests total", - ), - GrpcRequests: monitoring.NewSum( - "istio_echo_grpc_requests_total", - "The number of grpc requests total", - ), - TCPRequests: monitoring.NewSum( - "istio_echo_tcp_requests_total", - "The number of tcp requests total", - ), - } -) - -func init() { - monitoring.MustRegister(Metrics.HTTPRequests, Metrics.GrpcRequests, Metrics.TCPRequests) -} diff --git a/pkg/test/echo/common/model.go b/pkg/test/echo/common/model.go deleted file mode 100644 index 7db7e1d0c..000000000 --- a/pkg/test/echo/common/model.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package common - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" -) - -// TLSSettings defines TLS configuration for Echo server -type TLSSettings struct { - // If not empty, RootCert supplies the extra root cert that will be appended to the system cert pool. - RootCert string - ClientCert string - Key string - // If provided, override the host name used for the connection - // This needed for integration tests, as we are connecting using a port-forward (127.0.0.1), so - // any DNS certs will not validate. - Hostname string - // If set to true, the cert will be provisioned by proxy, and extra cert volume will be mounted. - ProxyProvision bool - // AcceptAnyALPN, if true, will make the server accept ANY ALPNs. This comes at the expense of - // allowing h2 negotiation and being able to detect the negotiated ALPN (as there is none), because - // Golang doesn't like us doing this (https://github.com/golang/go/issues/46310). - // This is useful when the server is simulating Envoy which does unconventional things with ALPN. - AcceptAnyALPN bool -} - -// Port represents a network port where a service is listening for -// connections. The port should be annotated with the type of protocol -// used by the port. -type Port struct { - // Name ascribes a human readable name for the port object. When a - // service has multiple ports, the name field is mandatory - Name string - - // Port number where the service can be reached. Does not necessarily - // map to the corresponding port numbers for the instances behind the - // service. - Port int - - // Protocol to be used for the port. - Protocol protocol.Instance - - // TLS determines if the port will use TLS. - TLS bool - - // ServerFirst if a port will be server first - ServerFirst bool - - // InstanceIP determines if echo will listen on the instance IP, or wildcard - InstanceIP bool - - // LocalhostIP determines if echo will listen on the localhost IP; otherwise, it will listen on wildcard - LocalhostIP bool - - // XDSServer, for gRPC servers, will use the xds.NewGRPCServer constructor to rely on XDS configuration. - // If this flag is set but the environment variable feature gates aren't, we should fail due to gRPC internals. - XDSServer bool - - // XDSTestBootstrap allows settings per-endpoint bootstrap without using the GRPC_XDS_BOOTSTRAP env var - XDSTestBootstrap []byte - - // XDSReadinessTLS determines if the XDS server should expect a TLS server, used for readiness probes - XDSReadinessTLS bool -} - -// PortList is a set of ports -type PortList []*Port - -var ServerFirstMagicString = "server-first-protocol\n" diff --git a/pkg/test/echo/common/scheme/scheme.go b/pkg/test/echo/common/scheme/scheme.go deleted file mode 100644 index 1b9baa2db..000000000 --- a/pkg/test/echo/common/scheme/scheme.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package scheme - -// Scheme enumerates the optional schemes for requests. -type Instance string - -const ( - HTTP Instance = "http" - HTTPS Instance = "https" - GRPC Instance = "grpc" - XDS Instance = "xds" - WebSocket Instance = "ws" - TCP Instance = "tcp" - // TLS sends a TLS connection and reports back the properties of the TLS connection - // This is similar to `openssl s_client` - // Response data is not returned; only information about the TLS handshake. - TLS Instance = "tls" - // DNS does a DNS query and reports back the results. - DNS Instance = "dns" -) diff --git a/pkg/test/echo/common/util.go b/pkg/test/echo/common/util.go deleted file mode 100644 index 3a126e0f0..000000000 --- a/pkg/test/echo/common/util.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package common - -import ( - "flag" - "net/http" - "strings" - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/proto" -) - -func init() { - flag.DurationVar(&DefaultRequestTimeout, "istio.test.echo.requestTimeout", DefaultRequestTimeout, - "Specifies the timeout for individual ForwardEcho calls.") -} - -var DefaultRequestTimeout = 5 * time.Second - -const ( - ConnectionTimeout = 2 * time.Second - DefaultCount = 1 -) - -// FillInDefaults fills in the timeout and count if not specified in the given message. -func FillInDefaults(request *proto.ForwardEchoRequest) { - request.TimeoutMicros = DurationToMicros(GetTimeout(request)) - request.Count = int32(GetCount(request)) -} - -// GetTimeout returns the timeout value as a time.Duration or DefaultRequestTimeout if not set. -func GetTimeout(request *proto.ForwardEchoRequest) time.Duration { - timeout := MicrosToDuration(request.TimeoutMicros) - if timeout == 0 { - timeout = DefaultRequestTimeout - } - return timeout -} - -// GetCount returns the count value or DefaultCount if not set. -func GetCount(request *proto.ForwardEchoRequest) int { - if request.Count > 1 { - return int(request.Count) - } - return DefaultCount -} - -func HTTPToProtoHeaders(headers http.Header) []*proto.Header { - out := make([]*proto.Header, 0, len(headers)) - for k, v := range headers { - out = append(out, &proto.Header{Key: k, Value: strings.Join(v, ",")}) - } - return out -} - -func ProtoToHTTPHeaders(headers []*proto.Header) http.Header { - out := make(http.Header) - for _, h := range headers { - // Avoid using .Add() to allow users to pass non-canonical forms - out[h.Key] = append(out[h.Key], h.Value) - } - return out -} - -// GetHeaders returns the headers for the message. -func GetHeaders(request *proto.ForwardEchoRequest) http.Header { - return ProtoToHTTPHeaders(request.Headers) -} - -// MicrosToDuration converts the given microseconds to a time.Duration. -func MicrosToDuration(micros int64) time.Duration { - return time.Duration(micros) * time.Microsecond -} - -// DurationToMicros converts the given duration to microseconds. -func DurationToMicros(d time.Duration) int64 { - return int64(d / time.Microsecond) -} diff --git a/pkg/test/echo/common/websocket.go b/pkg/test/echo/common/websocket.go deleted file mode 100644 index edbbb133a..000000000 --- a/pkg/test/echo/common/websocket.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package common - -import ( - "net/http" -) - -const ( - webSocketHeaderKey = "testwebsocket" - webSocketHeaderValue = "enabled" -) - -// IsWebSocketRequest indicates whether the request contains the header that triggers an upgrade to the WebSocket protocol. -func IsWebSocketRequest(r *http.Request) bool { - return r.Header.Get(webSocketHeaderKey) == webSocketHeaderValue -} - -// SetWebSocketHeader sets the header on the request which will trigger an upgrade to the WebSocket protocol. -func SetWebSocketHeader(headers http.Header) { - headers.Set(webSocketHeaderKey, webSocketHeaderValue) -} diff --git a/pkg/test/echo/docker/Dockerfile.app b/pkg/test/echo/docker/Dockerfile.app deleted file mode 100644 index 179075cbb..000000000 --- a/pkg/test/echo/docker/Dockerfile.app +++ /dev/null @@ -1,11 +0,0 @@ -ARG BASE_VERSION=latest - -FROM gcr.io/istio-release/base:${BASE_VERSION} - -ARG TARGETARCH -COPY ${TARGETARCH:-amd64}/client /usr/local/bin/client -COPY ${TARGETARCH:-amd64}/server /usr/local/bin/server -COPY certs/cert.crt /cert.crt -COPY certs/cert.key /cert.key - -ENTRYPOINT ["/usr/local/bin/server"] diff --git a/pkg/test/echo/docker/Dockerfile.app_sidecar b/pkg/test/echo/docker/Dockerfile.app_sidecar deleted file mode 100644 index 60c194c7a..000000000 --- a/pkg/test/echo/docker/Dockerfile.app_sidecar +++ /dev/null @@ -1,23 +0,0 @@ -ARG VM_IMAGE_NAME=ubuntu -ARG VM_IMAGE_VERSION=jammy -ARG BASE_VERSION=latest -FROM gcr.io/istio-release/app_sidecar_base_${VM_IMAGE_NAME}_${VM_IMAGE_VERSION}:${BASE_VERSION} - -# Install the certs. -COPY certs/ /var/lib/istio/ -COPY certs/default/ /var/run/secrets/istio/ - -# Install the sidecar components -COPY istio-sidecar.deb /tmp/istio-sidecar.deb -RUN dpkg -i /tmp/istio-sidecar.deb && rm /tmp/istio-sidecar.deb - -# Sudoers used to allow tcpdump and other debug utilities. -COPY sudoers /etc/sudoers - -# Install the Echo application -COPY echo-start.sh /usr/local/bin/echo-start.sh -ARG TARGETARCH -COPY ${TARGETARCH:-amd64}/client /usr/local/bin/client -COPY ${TARGETARCH:-amd64}/server /usr/local/bin/server - -ENTRYPOINT ["/usr/local/bin/echo-start.sh"] diff --git a/pkg/test/echo/docker/Dockerfile.app_sidecar_base b/pkg/test/echo/docker/Dockerfile.app_sidecar_base deleted file mode 100644 index 4f7168f3e..000000000 --- a/pkg/test/echo/docker/Dockerfile.app_sidecar_base +++ /dev/null @@ -1,28 +0,0 @@ -ARG VM_IMAGE_NAME=ubuntu -ARG VM_IMAGE_VERSION=jammy -FROM ${VM_IMAGE_NAME}:${VM_IMAGE_VERSION} -# Dockerfile for different VM OS versions -ENV DEBIAN_FRONTEND=noninteractive - -# Do not add more stuff to this list that isn't small or critically useful. -# If you occasionally need something on the container do -# sudo apt-get update && apt-get whichever - -# hadolint ignore=DL3005,DL3008 -RUN apt-get update && \ - apt-get install --no-install-recommends -y \ - iptables \ - iproute2 \ - sudo \ - ca-certificates \ - && apt-get upgrade -y \ - && apt-get clean \ - && rm -rf /var/log/*log /var/lib/apt/lists/* /var/log/apt/* /var/lib/dpkg/*-old /var/cache/debconf/*-old - -RUN if [ -f /usr/sbin/iptables-legacy ]; then \ - update-alternatives --set iptables /usr/sbin/iptables-legacy && \ - update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy; fi - -# Add a user that will run the application. This allows running as this user and capture iptables -RUN useradd -m --uid 1338 application && \ - echo "application ALL=NOPASSWD: ALL" >> /etc/sudoers diff --git a/pkg/test/echo/docker/Dockerfile.app_sidecar_base_centos b/pkg/test/echo/docker/Dockerfile.app_sidecar_base_centos deleted file mode 100644 index 7b227cc6c..000000000 --- a/pkg/test/echo/docker/Dockerfile.app_sidecar_base_centos +++ /dev/null @@ -1,17 +0,0 @@ -ARG VM_IMAGE_NAME=rockylinux -ARG VM_IMAGE_VERSION=8 -FROM ${VM_IMAGE_NAME}:${VM_IMAGE_VERSION} - -# hadolint ignore=DL3005,DL3008,DL3033 -RUN yum install -y \ - iptables \ - iproute \ - sudo \ - ca-certificates \ - && update-ca-trust \ - && yum clean all \ - && rm -rf /var/cache/yum - -# Add a user that will run the application. This allows running as this user and capture iptables -RUN useradd -m --uid 1338 application && \ - echo "application ALL=NOPASSWD: ALL" >> /etc/sudoers diff --git a/pkg/test/echo/docker/Dockerfile.app_sidecar_centos_7 b/pkg/test/echo/docker/Dockerfile.app_sidecar_centos_7 deleted file mode 100644 index 2bee14fea..000000000 --- a/pkg/test/echo/docker/Dockerfile.app_sidecar_centos_7 +++ /dev/null @@ -1,21 +0,0 @@ -ARG BASE_VERSION=latest -FROM gcr.io/istio-release/app_sidecar_base_centos_7:${BASE_VERSION} - -# Install the certs. -COPY certs/ /var/lib/istio/ -COPY certs/default/ /var/run/secrets/istio/ - -# Install the sidecar components -COPY istio-sidecar-centos-7.rpm /tmp/istio-sidecar-centos-7.rpm -RUN rpm -vi /tmp/istio-sidecar-centos-7.rpm && rm /tmp/istio-sidecar-centos-7.rpm - -# Sudoers used to allow tcpdump and other debug utilities. -COPY sudoers /etc/sudoers - -# Install the Echo application -COPY echo-start.sh /usr/local/bin/echo-start.sh -ARG TARGETARCH -COPY ${TARGETARCH:-amd64}/client /usr/local/bin/client -COPY ${TARGETARCH:-amd64}/server /usr/local/bin/server - -ENTRYPOINT ["/usr/local/bin/echo-start.sh"] diff --git a/pkg/test/echo/docker/Dockerfile.app_sidecar_centos_8 b/pkg/test/echo/docker/Dockerfile.app_sidecar_centos_8 deleted file mode 100644 index c0ae3f913..000000000 --- a/pkg/test/echo/docker/Dockerfile.app_sidecar_centos_8 +++ /dev/null @@ -1,23 +0,0 @@ -ARG BASE_VERSION=latest -ARG VM_IMAGE_NAME=rockylinux -ARG VM_IMAGE_VERSION=8 -FROM gcr.io/istio-release/app_sidecar_base_${VM_IMAGE_NAME}_${VM_IMAGE_VERSION}:${BASE_VERSION} - -# Install the certs. -COPY certs/ /var/lib/istio/ -COPY certs/default/ /var/run/secrets/istio/ - -# Install the sidecar components -COPY istio-sidecar.rpm /tmp/istio-sidecar.rpm -RUN rpm -vi /tmp/istio-sidecar.rpm && rm /tmp/istio-sidecar.rpm - -# Sudoers used to allow tcpdump and other debug utilities. -COPY sudoers /etc/sudoers - -# Install the Echo application -COPY echo-start.sh /usr/local/bin/echo-start.sh -ARG TARGETARCH -COPY ${TARGETARCH:-amd64}/client /usr/local/bin/client -COPY ${TARGETARCH:-amd64}/server /usr/local/bin/server - -ENTRYPOINT ["/usr/local/bin/echo-start.sh"] diff --git a/pkg/test/echo/docker/echo-start.sh b/pkg/test/echo/docker/echo-start.sh deleted file mode 100755 index 9c129064b..000000000 --- a/pkg/test/echo/docker/echo-start.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash -# -# 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. -# -################################################################################ - -set -ex - -# To support image builders which cannot do RUN, do the run commands at startup. -# This exploits the fact the images remove the installer once its installed. -# This is a horrible idea for production images, but these are just for tests. -[[ -f /tmp/istio-sidecar-centos-7.rpm ]] && rpm -vi /tmp/istio-sidecar-centos-7.rpm && rm /tmp/istio-sidecar-centos-7.rpm -[[ -f /tmp/istio-sidecar.rpm ]] && rpm -vi /tmp/istio-sidecar.rpm && rm /tmp/istio-sidecar.rpm -[[ -f /tmp/istio-sidecar.deb ]] && dpkg -i /tmp/istio-sidecar.deb && rm /tmp/istio-sidecar.deb - -# IF ECHO_ARGS is unset, make it an empty string. -ECHO_ARGS=${ECHO_ARGS:-} -# Split ECHO_ARGS by spaces. -IFS=' ' read -r -a ECHO_ARGS_ARRAY <<< "$ECHO_ARGS" - -ISTIO_LOG_DIR=${ISTIO_LOG_DIR:-/var/log/istio} - -# Run the pilot agent and Envoy -/usr/local/bin/istio-start.sh& - -# Start the echo server. -"/usr/local/bin/server" "${ECHO_ARGS_ARRAY[@]}" diff --git a/pkg/test/echo/docker/sudoers b/pkg/test/echo/docker/sudoers deleted file mode 100644 index 4fb12c624..000000000 --- a/pkg/test/echo/docker/sudoers +++ /dev/null @@ -1,4 +0,0 @@ -root ALL=(ALL) ALL -%wheel ALL=(ALL) ALL -application ALL=NOPASSWD: ALL -istio-proxy ALL=NOPASSWD: ALL diff --git a/pkg/test/echo/fields.go b/pkg/test/echo/fields.go deleted file mode 100644 index 3b31f69b1..000000000 --- a/pkg/test/echo/fields.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -// Field is a list of fields returned in responses from the Echo server. -type Field string - -func (f Field) String() string { - return string(f) -} - -const ( - RequestIDField Field = "X-Request-Id" - ServiceVersionField Field = "ServiceVersion" - ServicePortField Field = "ServicePort" - StatusCodeField Field = "StatusCode" - URLField Field = "URL" - HostField Field = "Host" - HostnameField Field = "Hostname" - MethodField Field = "Method" - ProtocolField Field = "Proto" - AlpnField Field = "Alpn" - RequestHeaderField Field = "RequestHeader" - ResponseHeaderField Field = "ResponseHeader" - ClusterField Field = "Cluster" - IstioVersionField Field = "IstioVersion" - IPField Field = "IP" // The Requester’s IP Address. -) diff --git a/pkg/test/echo/parse.go b/pkg/test/echo/parse.go deleted file mode 100644 index f5479ac7b..000000000 --- a/pkg/test/echo/parse.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -import ( - "net/http" - "regexp" - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/proto" -) - -var ( - requestIDFieldRegex = regexp.MustCompile("(?i)" + string(RequestIDField) + "=(.*)") - serviceVersionFieldRegex = regexp.MustCompile(string(ServiceVersionField) + "=(.*)") - servicePortFieldRegex = regexp.MustCompile(string(ServicePortField) + "=(.*)") - statusCodeFieldRegex = regexp.MustCompile(string(StatusCodeField) + "=(.*)") - hostFieldRegex = regexp.MustCompile(string(HostField) + "=(.*)") - hostnameFieldRegex = regexp.MustCompile(string(HostnameField) + "=(.*)") - requestHeaderFieldRegex = regexp.MustCompile(string(RequestHeaderField) + "=(.*)") - responseHeaderFieldRegex = regexp.MustCompile(string(ResponseHeaderField) + "=(.*)") - URLFieldRegex = regexp.MustCompile(string(URLField) + "=(.*)") - ClusterFieldRegex = regexp.MustCompile(string(ClusterField) + "=(.*)") - IstioVersionFieldRegex = regexp.MustCompile(string(IstioVersionField) + "=(.*)") - IPFieldRegex = regexp.MustCompile(string(IPField) + "=(.*)") - methodFieldRegex = regexp.MustCompile(string(MethodField) + "=(.*)") - protocolFieldRegex = regexp.MustCompile(string(ProtocolField) + "=(.*)") - alpnFieldRegex = regexp.MustCompile(string(AlpnField) + "=(.*)") -) - -func ParseResponses(req *proto.ForwardEchoRequest, resp *proto.ForwardEchoResponse) Responses { - responses := make([]Response, len(resp.Output)) - for i, output := range resp.Output { - responses[i] = parseResponse(output) - responses[i].RequestURL = req.Url - } - return responses -} - -func parseResponse(output string) Response { - out := Response{ - RawContent: output, - RequestHeaders: make(http.Header), - ResponseHeaders: make(http.Header), - } - - match := requestIDFieldRegex.FindStringSubmatch(output) - if match != nil { - out.ID = match[1] - } - - match = methodFieldRegex.FindStringSubmatch(output) - if match != nil { - out.Method = match[1] - } - - match = protocolFieldRegex.FindStringSubmatch(output) - if match != nil { - out.Protocol = match[1] - } - - match = alpnFieldRegex.FindStringSubmatch(output) - if match != nil { - out.Alpn = match[1] - } - - match = serviceVersionFieldRegex.FindStringSubmatch(output) - if match != nil { - out.Version = match[1] - } - - match = servicePortFieldRegex.FindStringSubmatch(output) - if match != nil { - out.Port = match[1] - } - - match = statusCodeFieldRegex.FindStringSubmatch(output) - if match != nil { - out.Code = match[1] - } - - match = hostFieldRegex.FindStringSubmatch(output) - if match != nil { - out.Host = match[1] - } - - match = hostnameFieldRegex.FindStringSubmatch(output) - if match != nil { - out.Hostname = match[1] - } - - match = URLFieldRegex.FindStringSubmatch(output) - if match != nil { - out.URL = match[1] - } - - match = ClusterFieldRegex.FindStringSubmatch(output) - if match != nil { - out.Cluster = match[1] - } - - match = IstioVersionFieldRegex.FindStringSubmatch(output) - if match != nil { - out.IstioVersion = match[1] - } - - match = IPFieldRegex.FindStringSubmatch(output) - if match != nil { - out.IP = match[1] - } - - out.rawBody = map[string]string{} - - matches := requestHeaderFieldRegex.FindAllStringSubmatch(output, -1) - for _, kv := range matches { - sl := strings.SplitN(kv[1], ":", 2) - if len(sl) != 2 { - continue - } - out.RequestHeaders.Set(sl[0], sl[1]) - } - - matches = responseHeaderFieldRegex.FindAllStringSubmatch(output, -1) - for _, kv := range matches { - sl := strings.SplitN(kv[1], ":", 2) - if len(sl) != 2 { - continue - } - out.ResponseHeaders.Set(sl[0], sl[1]) - } - - for _, l := range strings.Split(output, "\n") { - prefixSplit := strings.Split(l, "body] ") - if len(prefixSplit) != 2 { - continue - } - kv := strings.SplitN(prefixSplit[1], "=", 2) - if len(kv) != 2 { - continue - } - out.rawBody[kv[0]] = kv[1] - } - - return out -} diff --git a/pkg/test/echo/proto/echo.pb.go b/pkg/test/echo/proto/echo.pb.go deleted file mode 100644 index debaf1498..000000000 --- a/pkg/test/echo/proto/echo.pb.go +++ /dev/null @@ -1,715 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.27.1 -// protoc (unknown) -// source: test/echo/proto/echo.proto - -// Generate with protoc --go_out=. echo.proto -I /work/common-protos/ -I. - -package proto - -import ( - reflect "reflect" - sync "sync" -) - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type EchoRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` -} - -func (x *EchoRequest) Reset() { - *x = EchoRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_test_echo_proto_echo_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *EchoRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*EchoRequest) ProtoMessage() {} - -func (x *EchoRequest) ProtoReflect() protoreflect.Message { - mi := &file_test_echo_proto_echo_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use EchoRequest.ProtoReflect.Descriptor instead. -func (*EchoRequest) Descriptor() ([]byte, []int) { - return file_test_echo_proto_echo_proto_rawDescGZIP(), []int{0} -} - -func (x *EchoRequest) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -type EchoResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` -} - -func (x *EchoResponse) Reset() { - *x = EchoResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_test_echo_proto_echo_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *EchoResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*EchoResponse) ProtoMessage() {} - -func (x *EchoResponse) ProtoReflect() protoreflect.Message { - mi := &file_test_echo_proto_echo_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use EchoResponse.ProtoReflect.Descriptor instead. -func (*EchoResponse) Descriptor() ([]byte, []int) { - return file_test_echo_proto_echo_proto_rawDescGZIP(), []int{1} -} - -func (x *EchoResponse) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -type Header struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` - Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` -} - -func (x *Header) Reset() { - *x = Header{} - if protoimpl.UnsafeEnabled { - mi := &file_test_echo_proto_echo_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Header) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Header) ProtoMessage() {} - -func (x *Header) ProtoReflect() protoreflect.Message { - mi := &file_test_echo_proto_echo_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Header.ProtoReflect.Descriptor instead. -func (*Header) Descriptor() ([]byte, []int) { - return file_test_echo_proto_echo_proto_rawDescGZIP(), []int{2} -} - -func (x *Header) GetKey() string { - if x != nil { - return x.Key - } - return "" -} - -func (x *Header) GetValue() string { - if x != nil { - return x.Value - } - return "" -} - -type ForwardEchoRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Count int32 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"` - Qps int32 `protobuf:"varint,2,opt,name=qps,proto3" json:"qps,omitempty"` - TimeoutMicros int64 `protobuf:"varint,3,opt,name=timeout_micros,json=timeoutMicros,proto3" json:"timeout_micros,omitempty"` - Url string `protobuf:"bytes,4,opt,name=url,proto3" json:"url,omitempty"` - Headers []*Header `protobuf:"bytes,5,rep,name=headers,proto3" json:"headers,omitempty"` - Message string `protobuf:"bytes,6,opt,name=message,proto3" json:"message,omitempty"` - // Method for the request. Valid only for HTTP - Method string `protobuf:"bytes,9,opt,name=method,proto3" json:"method,omitempty"` - // If true, requests will be sent using h2c prior knowledge - Http2 bool `protobuf:"varint,7,opt,name=http2,proto3" json:"http2,omitempty"` - // If true, requests will be sent using http3 - Http3 bool `protobuf:"varint,15,opt,name=http3,proto3" json:"http3,omitempty"` - // If true, requests will not be sent until magic string is received - ServerFirst bool `protobuf:"varint,8,opt,name=serverFirst,proto3" json:"serverFirst,omitempty"` - // If true, 301 redirects will be followed - FollowRedirects bool `protobuf:"varint,14,opt,name=followRedirects,proto3" json:"followRedirects,omitempty"` - // If non-empty, make the request with the corresponding cert and key. - Cert string `protobuf:"bytes,10,opt,name=cert,proto3" json:"cert,omitempty"` - Key string `protobuf:"bytes,11,opt,name=key,proto3" json:"key,omitempty"` - // If non-empty, verify the server CA - CaCert string `protobuf:"bytes,12,opt,name=caCert,proto3" json:"caCert,omitempty"` - // If non-empty, make the request with the corresponding cert and key file. - CertFile string `protobuf:"bytes,16,opt,name=certFile,proto3" json:"certFile,omitempty"` - KeyFile string `protobuf:"bytes,17,opt,name=keyFile,proto3" json:"keyFile,omitempty"` - // If non-empty, verify the server CA with the ca cert file. - CaCertFile string `protobuf:"bytes,18,opt,name=caCertFile,proto3" json:"caCertFile,omitempty"` - // Skip verifying peer's certificate. - InsecureSkipVerify bool `protobuf:"varint,19,opt,name=insecureSkipVerify,proto3" json:"insecureSkipVerify,omitempty"` - // List of ALPNs to present. If not set, this will be automatically be set based on the protocol - Alpn *Alpn `protobuf:"bytes,13,opt,name=alpn,proto3" json:"alpn,omitempty"` - // Server name (SNI) to present in TLS connections. If not set, Host will be used for http requests. - ServerName string `protobuf:"bytes,20,opt,name=serverName,proto3" json:"serverName,omitempty"` - // Expected response determines what string to look for in the response to validate TCP requests succeeded. - // If not set, defaults to "StatusCode=200" - ExpectedResponse *wrapperspb.StringValue `protobuf:"bytes,21,opt,name=expectedResponse,proto3" json:"expectedResponse,omitempty"` -} - -func (x *ForwardEchoRequest) Reset() { - *x = ForwardEchoRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_test_echo_proto_echo_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ForwardEchoRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ForwardEchoRequest) ProtoMessage() {} - -func (x *ForwardEchoRequest) ProtoReflect() protoreflect.Message { - mi := &file_test_echo_proto_echo_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ForwardEchoRequest.ProtoReflect.Descriptor instead. -func (*ForwardEchoRequest) Descriptor() ([]byte, []int) { - return file_test_echo_proto_echo_proto_rawDescGZIP(), []int{3} -} - -func (x *ForwardEchoRequest) GetCount() int32 { - if x != nil { - return x.Count - } - return 0 -} - -func (x *ForwardEchoRequest) GetQps() int32 { - if x != nil { - return x.Qps - } - return 0 -} - -func (x *ForwardEchoRequest) GetTimeoutMicros() int64 { - if x != nil { - return x.TimeoutMicros - } - return 0 -} - -func (x *ForwardEchoRequest) GetUrl() string { - if x != nil { - return x.Url - } - return "" -} - -func (x *ForwardEchoRequest) GetHeaders() []*Header { - if x != nil { - return x.Headers - } - return nil -} - -func (x *ForwardEchoRequest) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -func (x *ForwardEchoRequest) GetMethod() string { - if x != nil { - return x.Method - } - return "" -} - -func (x *ForwardEchoRequest) GetHttp2() bool { - if x != nil { - return x.Http2 - } - return false -} - -func (x *ForwardEchoRequest) GetHttp3() bool { - if x != nil { - return x.Http3 - } - return false -} - -func (x *ForwardEchoRequest) GetServerFirst() bool { - if x != nil { - return x.ServerFirst - } - return false -} - -func (x *ForwardEchoRequest) GetFollowRedirects() bool { - if x != nil { - return x.FollowRedirects - } - return false -} - -func (x *ForwardEchoRequest) GetCert() string { - if x != nil { - return x.Cert - } - return "" -} - -func (x *ForwardEchoRequest) GetKey() string { - if x != nil { - return x.Key - } - return "" -} - -func (x *ForwardEchoRequest) GetCaCert() string { - if x != nil { - return x.CaCert - } - return "" -} - -func (x *ForwardEchoRequest) GetCertFile() string { - if x != nil { - return x.CertFile - } - return "" -} - -func (x *ForwardEchoRequest) GetKeyFile() string { - if x != nil { - return x.KeyFile - } - return "" -} - -func (x *ForwardEchoRequest) GetCaCertFile() string { - if x != nil { - return x.CaCertFile - } - return "" -} - -func (x *ForwardEchoRequest) GetInsecureSkipVerify() bool { - if x != nil { - return x.InsecureSkipVerify - } - return false -} - -func (x *ForwardEchoRequest) GetAlpn() *Alpn { - if x != nil { - return x.Alpn - } - return nil -} - -func (x *ForwardEchoRequest) GetServerName() string { - if x != nil { - return x.ServerName - } - return "" -} - -func (x *ForwardEchoRequest) GetExpectedResponse() *wrapperspb.StringValue { - if x != nil { - return x.ExpectedResponse - } - return nil -} - -type Alpn struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Value []string `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"` -} - -func (x *Alpn) Reset() { - *x = Alpn{} - if protoimpl.UnsafeEnabled { - mi := &file_test_echo_proto_echo_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Alpn) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Alpn) ProtoMessage() {} - -func (x *Alpn) ProtoReflect() protoreflect.Message { - mi := &file_test_echo_proto_echo_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Alpn.ProtoReflect.Descriptor instead. -func (*Alpn) Descriptor() ([]byte, []int) { - return file_test_echo_proto_echo_proto_rawDescGZIP(), []int{4} -} - -func (x *Alpn) GetValue() []string { - if x != nil { - return x.Value - } - return nil -} - -type ForwardEchoResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Output []string `protobuf:"bytes,1,rep,name=output,proto3" json:"output,omitempty"` -} - -func (x *ForwardEchoResponse) Reset() { - *x = ForwardEchoResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_test_echo_proto_echo_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ForwardEchoResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ForwardEchoResponse) ProtoMessage() {} - -func (x *ForwardEchoResponse) ProtoReflect() protoreflect.Message { - mi := &file_test_echo_proto_echo_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ForwardEchoResponse.ProtoReflect.Descriptor instead. -func (*ForwardEchoResponse) Descriptor() ([]byte, []int) { - return file_test_echo_proto_echo_proto_rawDescGZIP(), []int{5} -} - -func (x *ForwardEchoResponse) GetOutput() []string { - if x != nil { - return x.Output - } - return nil -} - -var File_test_echo_proto_echo_proto protoreflect.FileDescriptor - -var file_test_echo_proto_echo_proto_rawDesc = []byte{ - 0x0a, 0x1a, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0x27, 0x0a, 0x0b, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x28, 0x0a, 0x0c, - 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x30, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x97, 0x05, 0x0a, 0x12, 0x46, 0x6f, 0x72, - 0x77, 0x61, 0x72, 0x64, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x71, 0x70, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x03, 0x71, 0x70, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x6f, - 0x75, 0x74, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x10, - 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, - 0x12, 0x27, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x68, - 0x74, 0x74, 0x70, 0x32, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x68, 0x74, 0x74, 0x70, - 0x32, 0x12, 0x14, 0x0a, 0x05, 0x68, 0x74, 0x74, 0x70, 0x33, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x05, 0x68, 0x74, 0x74, 0x70, 0x33, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x46, 0x69, 0x72, 0x73, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x46, 0x69, 0x72, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x0f, 0x66, 0x6f, 0x6c, - 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x73, 0x18, 0x0e, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0f, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, - 0x63, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x72, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x63, 0x65, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x0b, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x61, 0x43, - 0x65, 0x72, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x61, 0x43, 0x65, 0x72, - 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x65, 0x72, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x10, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x65, 0x72, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x18, 0x0a, - 0x07, 0x6b, 0x65, 0x79, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x6b, 0x65, 0x79, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x61, 0x43, 0x65, 0x72, - 0x74, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x61, 0x43, - 0x65, 0x72, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x2e, 0x0a, 0x12, 0x69, 0x6e, 0x73, 0x65, 0x63, - 0x75, 0x72, 0x65, 0x53, 0x6b, 0x69, 0x70, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x18, 0x13, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x12, 0x69, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x53, 0x6b, 0x69, - 0x70, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, 0x1f, 0x0a, 0x04, 0x61, 0x6c, 0x70, 0x6e, 0x18, - 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x6c, - 0x70, 0x6e, 0x52, 0x04, 0x61, 0x6c, 0x70, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x48, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x65, - 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x15, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x52, 0x10, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1c, 0x0a, 0x04, 0x41, 0x6c, 0x70, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x22, 0x2d, 0x0a, 0x13, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x45, 0x63, 0x68, 0x6f, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, - 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x32, - 0x88, 0x01, 0x0a, 0x0f, 0x45, 0x63, 0x68, 0x6f, 0x54, 0x65, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x45, 0x63, 0x68, 0x6f, 0x12, 0x12, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x45, - 0x63, 0x68, 0x6f, 0x12, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x6f, 0x72, 0x77, - 0x61, 0x72, 0x64, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x45, 0x63, - 0x68, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x2e, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_test_echo_proto_echo_proto_rawDescOnce sync.Once - file_test_echo_proto_echo_proto_rawDescData = file_test_echo_proto_echo_proto_rawDesc -) - -func file_test_echo_proto_echo_proto_rawDescGZIP() []byte { - file_test_echo_proto_echo_proto_rawDescOnce.Do(func() { - file_test_echo_proto_echo_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_echo_proto_echo_proto_rawDescData) - }) - return file_test_echo_proto_echo_proto_rawDescData -} - -var file_test_echo_proto_echo_proto_msgTypes = make([]protoimpl.MessageInfo, 6) -var file_test_echo_proto_echo_proto_goTypes = []interface{}{ - (*EchoRequest)(nil), // 0: proto.EchoRequest - (*EchoResponse)(nil), // 1: proto.EchoResponse - (*Header)(nil), // 2: proto.Header - (*ForwardEchoRequest)(nil), // 3: proto.ForwardEchoRequest - (*Alpn)(nil), // 4: proto.Alpn - (*ForwardEchoResponse)(nil), // 5: proto.ForwardEchoResponse - (*wrapperspb.StringValue)(nil), // 6: google.protobuf.StringValue -} -var file_test_echo_proto_echo_proto_depIdxs = []int32{ - 2, // 0: proto.ForwardEchoRequest.headers:type_name -> proto.Header - 4, // 1: proto.ForwardEchoRequest.alpn:type_name -> proto.Alpn - 6, // 2: proto.ForwardEchoRequest.expectedResponse:type_name -> google.protobuf.StringValue - 0, // 3: proto.EchoTestService.Echo:input_type -> proto.EchoRequest - 3, // 4: proto.EchoTestService.ForwardEcho:input_type -> proto.ForwardEchoRequest - 1, // 5: proto.EchoTestService.Echo:output_type -> proto.EchoResponse - 5, // 6: proto.EchoTestService.ForwardEcho:output_type -> proto.ForwardEchoResponse - 5, // [5:7] is the sub-list for method output_type - 3, // [3:5] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name -} - -func init() { file_test_echo_proto_echo_proto_init() } -func file_test_echo_proto_echo_proto_init() { - if File_test_echo_proto_echo_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_test_echo_proto_echo_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EchoRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_echo_proto_echo_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EchoResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_echo_proto_echo_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Header); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_echo_proto_echo_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ForwardEchoRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_echo_proto_echo_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Alpn); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_echo_proto_echo_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ForwardEchoResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_test_echo_proto_echo_proto_rawDesc, - NumEnums: 0, - NumMessages: 6, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_test_echo_proto_echo_proto_goTypes, - DependencyIndexes: file_test_echo_proto_echo_proto_depIdxs, - MessageInfos: file_test_echo_proto_echo_proto_msgTypes, - }.Build() - File_test_echo_proto_echo_proto = out.File - file_test_echo_proto_echo_proto_rawDesc = nil - file_test_echo_proto_echo_proto_goTypes = nil - file_test_echo_proto_echo_proto_depIdxs = nil -} diff --git a/pkg/test/echo/proto/echo.proto b/pkg/test/echo/proto/echo.proto deleted file mode 100644 index 6dacc3c44..000000000 --- a/pkg/test/echo/proto/echo.proto +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -import "google/protobuf/wrappers.proto"; - -// Generate with protoc --go_out=. echo.proto -I /work/common-protos/ -I. -package proto; -option go_package="../proto"; - -service EchoTestService { - rpc Echo (EchoRequest) returns (EchoResponse); - rpc ForwardEcho (ForwardEchoRequest) returns (ForwardEchoResponse); -} - -message EchoRequest { - string message = 1; -} - -message EchoResponse { - string message = 1; -} - -message Header { - string key = 1; - string value = 2; -} - -message ForwardEchoRequest { - int32 count = 1; - int32 qps = 2; - int64 timeout_micros = 3; - string url = 4; - repeated Header headers = 5; - string message = 6; - // Method for the request. Valid only for HTTP - string method = 9; - // If true, requests will be sent using h2c prior knowledge - bool http2 = 7; - // If true, requests will be sent using http3 - bool http3 = 15; - // If true, requests will not be sent until magic string is received - bool serverFirst = 8; - // If true, 301 redirects will be followed - bool followRedirects = 14; - // If non-empty, make the request with the corresponding cert and key. - string cert = 10; - string key = 11; - // If non-empty, verify the server CA - string caCert = 12; - // If non-empty, make the request with the corresponding cert and key file. - string certFile = 16; - string keyFile = 17; - // If non-empty, verify the server CA with the ca cert file. - string caCertFile = 18; - // Skip verifying peer's certificate. - bool insecureSkipVerify = 19; - // List of ALPNs to present. If not set, this will be automatically be set based on the protocol - Alpn alpn = 13; - // Server name (SNI) to present in TLS connections. If not set, Host will be used for http requests. - string serverName = 20; - // Expected response determines what string to look for in the response to validate TCP requests succeeded. - // If not set, defaults to "StatusCode=200" - google.protobuf.StringValue expectedResponse = 21; -} - -message Alpn { - repeated string value = 1; -} - -message ForwardEchoResponse { - repeated string output = 1; -} diff --git a/pkg/test/echo/proto/echo_grpc.pb.go b/pkg/test/echo/proto/echo_grpc.pb.go deleted file mode 100644 index f89558ef9..000000000 --- a/pkg/test/echo/proto/echo_grpc.pb.go +++ /dev/null @@ -1,144 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.2.0 -// - protoc (unknown) -// source: test/echo/proto/echo.proto - -package proto - -import ( - context "context" -) - -import ( - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 - -// EchoTestServiceClient is the client API for EchoTestService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type EchoTestServiceClient interface { - Echo(ctx context.Context, in *EchoRequest, opts ...grpc.CallOption) (*EchoResponse, error) - ForwardEcho(ctx context.Context, in *ForwardEchoRequest, opts ...grpc.CallOption) (*ForwardEchoResponse, error) -} - -type echoTestServiceClient struct { - cc grpc.ClientConnInterface -} - -func NewEchoTestServiceClient(cc grpc.ClientConnInterface) EchoTestServiceClient { - return &echoTestServiceClient{cc} -} - -func (c *echoTestServiceClient) Echo(ctx context.Context, in *EchoRequest, opts ...grpc.CallOption) (*EchoResponse, error) { - out := new(EchoResponse) - err := c.cc.Invoke(ctx, "/proto.EchoTestService/Echo", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *echoTestServiceClient) ForwardEcho(ctx context.Context, in *ForwardEchoRequest, opts ...grpc.CallOption) (*ForwardEchoResponse, error) { - out := new(ForwardEchoResponse) - err := c.cc.Invoke(ctx, "/proto.EchoTestService/ForwardEcho", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// EchoTestServiceServer is the server API for EchoTestService service. -// All implementations must embed UnimplementedEchoTestServiceServer -// for forward compatibility -type EchoTestServiceServer interface { - Echo(context.Context, *EchoRequest) (*EchoResponse, error) - ForwardEcho(context.Context, *ForwardEchoRequest) (*ForwardEchoResponse, error) - mustEmbedUnimplementedEchoTestServiceServer() -} - -// UnimplementedEchoTestServiceServer must be embedded to have forward compatible implementations. -type UnimplementedEchoTestServiceServer struct { -} - -func (UnimplementedEchoTestServiceServer) Echo(context.Context, *EchoRequest) (*EchoResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Echo not implemented") -} -func (UnimplementedEchoTestServiceServer) ForwardEcho(context.Context, *ForwardEchoRequest) (*ForwardEchoResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ForwardEcho not implemented") -} -func (UnimplementedEchoTestServiceServer) mustEmbedUnimplementedEchoTestServiceServer() {} - -// UnsafeEchoTestServiceServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to EchoTestServiceServer will -// result in compilation errors. -type UnsafeEchoTestServiceServer interface { - mustEmbedUnimplementedEchoTestServiceServer() -} - -func RegisterEchoTestServiceServer(s grpc.ServiceRegistrar, srv EchoTestServiceServer) { - s.RegisterService(&EchoTestService_ServiceDesc, srv) -} - -func _EchoTestService_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(EchoRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(EchoTestServiceServer).Echo(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/proto.EchoTestService/Echo", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(EchoTestServiceServer).Echo(ctx, req.(*EchoRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _EchoTestService_ForwardEcho_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ForwardEchoRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(EchoTestServiceServer).ForwardEcho(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/proto.EchoTestService/ForwardEcho", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(EchoTestServiceServer).ForwardEcho(ctx, req.(*ForwardEchoRequest)) - } - return interceptor(ctx, in, info, handler) -} - -// EchoTestService_ServiceDesc is the grpc.ServiceDesc for EchoTestService service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var EchoTestService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "proto.EchoTestService", - HandlerType: (*EchoTestServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Echo", - Handler: _EchoTestService_Echo_Handler, - }, - { - MethodName: "ForwardEcho", - Handler: _EchoTestService_ForwardEcho_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "test/echo/proto/echo.proto", -} diff --git a/pkg/test/echo/response.go b/pkg/test/echo/response.go deleted file mode 100644 index 32f1da015..000000000 --- a/pkg/test/echo/response.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -import ( - "fmt" - "net/http" - "sort" - "strings" -) - -// HeaderType is a helper enum for retrieving Headers from a Response. -type HeaderType string - -const ( - RequestHeader HeaderType = "request" - ResponseHeader HeaderType = "response" -) - -// Response represents a response to a single echo request. -type Response struct { - // RequestURL is the requested URL. This differs from URL, which is the just the path. - // For example, RequestURL=http://foo/bar, URL=/bar - RequestURL string - // Method used (for HTTP). - Method string - // Protocol used for the request. - Protocol string - // Alpn value (for HTTP). - Alpn string - // RawContent is the original unparsed content for this response - RawContent string - // ID is a unique identifier of the resource in the response - ID string - // URL is the url the request is sent to - URL string - // Version is the version of the resource in the response - Version string - // Port is the port of the resource in the response - Port string - // Code is the response code - Code string - // Host is the host called by the request - Host string - // Hostname is the host that responded to the request - Hostname string - // The cluster where the server is deployed. - Cluster string - // IstioVersion for the Istio sidecar. - IstioVersion string - // IP is the requester's ip address - IP string - // rawBody gives a map of all key/values in the body of the response. - rawBody map[string]string - RequestHeaders http.Header - ResponseHeaders http.Header -} - -// Count occurrences of the given text within the body of this response. -func (r Response) Count(text string) int { - return strings.Count(r.RawContent, text) -} - -// GetHeaders returns the appropriate headers for the given type. -func (r Response) GetHeaders(hType HeaderType) http.Header { - switch hType { - case RequestHeader: - return r.RequestHeaders - case ResponseHeader: - return r.ResponseHeaders - default: - panic("invalid HeaderType enum: " + hType) - } -} - -// Body returns the lines of the response body, in order -func (r Response) Body() []string { - type keyValue struct { - k, v string - } - var keyValues []keyValue - // rawBody is in random order, so get the order back via sorting. - for k, v := range r.rawBody { - keyValues = append(keyValues, keyValue{k, v}) - } - sort.Slice(keyValues, func(i, j int) bool { - return keyValues[i].k < keyValues[j].k - }) - var resp []string - for _, kv := range keyValues { - resp = append(resp, kv.v) - } - return resp -} - -func (r Response) String() string { - out := "" - out += fmt.Sprintf("RawContent: %s\n", r.RawContent) - out += fmt.Sprintf("ID: %s\n", r.ID) - out += fmt.Sprintf("Method: %s\n", r.Method) - out += fmt.Sprintf("Protocol: %s\n", r.Protocol) - out += fmt.Sprintf("Alpn: %s\n", r.Alpn) - out += fmt.Sprintf("URL: %s\n", r.URL) - out += fmt.Sprintf("Version: %s\n", r.Version) - out += fmt.Sprintf("Port: %s\n", r.Port) - out += fmt.Sprintf("Code: %s\n", r.Code) - out += fmt.Sprintf("Host: %s\n", r.Host) - out += fmt.Sprintf("Hostname: %s\n", r.Hostname) - out += fmt.Sprintf("Cluster: %s\n", r.Cluster) - out += fmt.Sprintf("IstioVersion: %s\n", r.IstioVersion) - out += fmt.Sprintf("IP: %s\n", r.IP) - out += fmt.Sprintf("Request Headers: %v\n", r.RequestHeaders) - out += fmt.Sprintf("Response Headers: %v\n", r.ResponseHeaders) - - return out -} diff --git a/pkg/test/echo/responses.go b/pkg/test/echo/responses.go deleted file mode 100644 index 218681aaa..000000000 --- a/pkg/test/echo/responses.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -import ( - "fmt" -) - -// Responses is an ordered list of parsed response objects. -type Responses []Response - -func (r Responses) IsEmpty() bool { - return len(r) == 0 -} - -// Len returns the length of the parsed responses. -func (r Responses) Len() int { - return len(r) -} - -// Count occurrences of the given text within the bodies of all responses. -func (r Responses) Count(text string) int { - count := 0 - for _, c := range r { - count += c.Count(text) - } - return count -} - -// Match returns a subset of Responses that match the given predicate. -func (r Responses) Match(f func(r Response) bool) Responses { - var matched []Response - for _, rr := range r { - if f(rr) { - matched = append(matched, rr) - } - } - return matched -} - -func (r Responses) String() string { - out := "" - for i, resp := range r { - out += fmt.Sprintf("Response[%d]:\n%s", i, resp.String()) - } - return out -} diff --git a/pkg/test/echo/server/endpoint/grpc.go b/pkg/test/echo/server/endpoint/grpc.go deleted file mode 100644 index 492d0f752..000000000 --- a/pkg/test/echo/server/endpoint/grpc.go +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package endpoint - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "net" - "net/http" - "os" - "strconv" - "strings" - "time" -) - -import ( - "github.com/google/uuid" - "google.golang.org/grpc" - "google.golang.org/grpc/admin" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" - xdscreds "google.golang.org/grpc/credentials/xds" - "google.golang.org/grpc/health" - grpcHealth "google.golang.org/grpc/health/grpc_health_v1" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/peer" - "google.golang.org/grpc/reflection" - "google.golang.org/grpc/xds" - "k8s.io/utils/env" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/istio-agent/grpcxds" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/proto" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/server/forwarder" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -var _ Instance = &grpcInstance{} - -// grpcServer is the intersection of used methods for grpc.Server and xds.GRPCServer -type grpcServer interface { - reflection.GRPCServer - Serve(listener net.Listener) error - Stop() -} - -type grpcInstance struct { - Config - server grpcServer - cleanups []func() -} - -func newGRPC(config Config) Instance { - return &grpcInstance{ - Config: config, - } -} - -func (s *grpcInstance) GetConfig() Config { - return s.Config -} - -func (s *grpcInstance) newServer(opts ...grpc.ServerOption) grpcServer { - if s.Port.XDSServer { - if len(s.Port.XDSTestBootstrap) > 0 { - opts = append(opts, xds.BootstrapContentsForTesting(s.Port.XDSTestBootstrap)) - } - epLog.Infof("Using xDS for serverside gRPC on %d", s.Port.Port) - return xds.NewGRPCServer(opts...) - } - return grpc.NewServer(opts...) -} - -func (s *grpcInstance) Start(onReady OnReadyFunc) error { - // Listen on the given port and update the port if it changed from what was passed in. - listener, p, err := listenOnAddress(s.ListenerIP, s.Port.Port) - if err != nil { - return err - } - // Store the actual listening port back to the argument. - s.Port.Port = p - - var opts []grpc.ServerOption - if s.Port.TLS { - epLog.Infof("Listening GRPC (over TLS) on %v", p) - // Create the TLS credentials - creds, errCreds := credentials.NewServerTLSFromFile(s.TLSCert, s.TLSKey) - if errCreds != nil { - epLog.Errorf("could not load TLS keys: %s", errCreds) - } - opts = append(opts, grpc.Creds(creds)) - } else if s.Port.XDSServer { - epLog.Infof("Listening GRPC (over xDS-configured mTLS) on %v", p) - creds, err := xdscreds.NewServerCredentials(xdscreds.ServerOptions{ - FallbackCreds: insecure.NewCredentials(), - }) - if err != nil { - return err - } - opts = append(opts, grpc.Creds(creds)) - } else { - epLog.Infof("Listening GRPC on %v", p) - } - s.server = s.newServer(opts...) - - // add the standard grpc health check - healthServer := health.NewServer() - grpcHealth.RegisterHealthServer(s.server, healthServer) - - proto.RegisterEchoTestServiceServer(s.server, &EchoGrpcHandler{ - Config: s.Config, - }) - reflection.Register(s.server) - if val, _ := env.GetBool("EXPOSE_GRPC_ADMIN", false); val { - cleanup, err := admin.Register(s.server) - if err != nil { - return err - } - s.cleanups = append(s.cleanups, cleanup) - } - // Start serving GRPC traffic. - go func() { - err := s.server.Serve(listener) - epLog.Warnf("Port %d listener terminated with error: %v", p, err) - }() - - // Notify the WaitGroup once the port has transitioned to ready. - go s.awaitReady(func() { - healthServer.SetServingStatus("", grpcHealth.HealthCheckResponse_SERVING) - onReady() - }, listener) - - return nil -} - -func (s *grpcInstance) awaitReady(onReady OnReadyFunc, listener net.Listener) { - defer onReady() - - err := retry.UntilSuccess(func() error { - cert, key, ca, err := s.certsFromBootstrapForReady() - if err != nil { - return err - } - req := &proto.ForwardEchoRequest{ - Url: "grpc://" + listener.Addr().String(), - Message: "hello", - TimeoutMicros: common.DurationToMicros(readyInterval), - } - if s.Port.XDSReadinessTLS { - // TODO: using the servers key/cert is not always valid, it may not be allowed to make requests to itself - req.CertFile = cert - req.KeyFile = key - req.CaCertFile = ca - req.InsecureSkipVerify = true - } - f, err := forwarder.New(forwarder.Config{ - XDSTestBootstrap: s.Port.XDSTestBootstrap, - Request: req, - }) - defer func() { - _ = f.Close() - }() - - if err != nil { - return err - } - _, err = f.Run(context.Background()) - return err - }, retry.Timeout(readyTimeout), retry.Delay(readyInterval)) - if err != nil { - epLog.Errorf("readiness failed for GRPC endpoint %s: %v", listener.Addr().String(), err) - } else { - epLog.Infof("ready for GRPC endpoint %s", listener.Addr().String()) - } -} - -// TODO (hack) we have to send certs OR use xds:///fqdn. We don't know our own fqdn, and even if we did -// we could send traffic to another instance. Instead we look into gRPC internals to authenticate with ourself. -func (s *grpcInstance) certsFromBootstrapForReady() (cert string, key string, ca string, err error) { - if !s.Port.XDSServer { - return - } - - var bootstrapData []byte - if data := s.Port.XDSTestBootstrap; len(data) > 0 { - bootstrapData = data - } else if path := os.Getenv("GRPC_XDS_BOOTSTRAP"); len(path) > 0 { - bootstrapData, err = os.ReadFile(path) - } else if data := os.Getenv("GRPC_XDS_BOOTSTRAP_CONFIG"); len(data) > 0 { - bootstrapData = []byte(data) - } - var bootstrap grpcxds.Bootstrap - if uerr := json.Unmarshal(bootstrapData, &bootstrap); uerr != nil { - err = uerr - return - } - certs := bootstrap.FileWatcherProvider() - if certs == nil { - err = fmt.Errorf("no certs found in bootstrap") - return - } - cert = certs.CertificateFile - key = certs.PrivateKeyFile - ca = certs.CACertificateFile - return -} - -func (s *grpcInstance) Close() error { - if s.server != nil { - s.server.Stop() - } - for _, cleanup := range s.cleanups { - cleanup() - } - return nil -} - -type EchoGrpcHandler struct { - proto.UnimplementedEchoTestServiceServer - Config -} - -func (h *EchoGrpcHandler) Echo(ctx context.Context, req *proto.EchoRequest) (*proto.EchoResponse, error) { - defer common.Metrics.GrpcRequests.With(common.PortLabel.Value(strconv.Itoa(h.Port.Port))).Increment() - body := bytes.Buffer{} - md, ok := metadata.FromIncomingContext(ctx) - if ok { - for key, values := range md { - if strings.HasSuffix(key, "-bin") { - // Skip binary headers. - continue - } - - field := key - - if key == ":authority" { - for _, value := range values { - writeField(&body, echo.HostField, value) - } - } - - for _, value := range values { - writeRequestHeader(&body, field, value) - } - } - } - - id := uuid.New() - epLog.WithLabels("message", req.GetMessage(), "headers", md, "id", id).Infof("GRPC Request") - - portNumber := 0 - if h.Port != nil { - portNumber = h.Port.Port - } - - ip := "0.0.0.0" - if peerInfo, ok := peer.FromContext(ctx); ok { - ip, _, _ = net.SplitHostPort(peerInfo.Addr.String()) - } - - writeField(&body, echo.StatusCodeField, strconv.Itoa(http.StatusOK)) - writeField(&body, echo.ServiceVersionField, h.Version) - writeField(&body, echo.ServicePortField, strconv.Itoa(portNumber)) - writeField(&body, echo.ClusterField, h.Cluster) - writeField(&body, echo.IPField, ip) - writeField(&body, echo.IstioVersionField, h.IstioVersion) - writeField(&body, echo.ProtocolField, "GRPC") - writeField(&body, "Echo", req.GetMessage()) - - if hostname, err := os.Hostname(); err == nil { - writeField(&body, echo.HostnameField, hostname) - } - - epLog.WithLabels("id", id).Infof("GRPC Response") - return &proto.EchoResponse{Message: body.String()}, nil -} - -func (h *EchoGrpcHandler) ForwardEcho(ctx context.Context, req *proto.ForwardEchoRequest) (*proto.ForwardEchoResponse, error) { - id := uuid.New() - l := epLog.WithLabels("url", req.Url, "id", id) - l.Infof("ForwardEcho request") - t0 := time.Now() - instance, err := forwarder.New(forwarder.Config{ - Request: req, - }) - if err != nil { - return nil, err - } - defer func() { _ = instance.Close() }() - - ret, err := instance.Run(ctx) - if err == nil { - l.WithLabels("latency", time.Since(t0)).Infof("ForwardEcho response complete: %v", ret.GetOutput()) - } else { - l.WithLabels("latency", time.Since(t0)).Infof("ForwardEcho response failed: %v", err) - } - return ret, err -} diff --git a/pkg/test/echo/server/endpoint/http.go b/pkg/test/echo/server/endpoint/http.go deleted file mode 100644 index ff20cbe7f..000000000 --- a/pkg/test/echo/server/endpoint/http.go +++ /dev/null @@ -1,470 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package endpoint - -import ( - "bytes" - "context" - "crypto/tls" - "fmt" - "math/rand" - "net" - "net/http" - "os" - "sort" - "strconv" - "strings" - "time" -) - -import ( - "github.com/google/uuid" - "github.com/gorilla/websocket" - "golang.org/x/net/http2" - "golang.org/x/net/http2/h2c" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -const ( - readyTimeout = 10 * time.Second - readyInterval = 2 * time.Second -) - -var webSocketUpgrader = websocket.Upgrader{ - CheckOrigin: func(r *http.Request) bool { - // allow all connections by default - return true - }, -} - -var _ Instance = &httpInstance{} - -type httpInstance struct { - Config - server *http.Server -} - -func newHTTP(config Config) Instance { - return &httpInstance{ - Config: config, - } -} - -func (s *httpInstance) GetConfig() Config { - return s.Config -} - -func (s *httpInstance) Start(onReady OnReadyFunc) error { - h2s := &http2.Server{} - s.server = &http.Server{ - Handler: h2c.NewHandler(&httpHandler{ - Config: s.Config, - }, h2s), - } - - var listener net.Listener - var port int - var err error - if s.isUDS() { - port = 0 - listener, err = listenOnUDS(s.UDSServer) - } else if s.Port.TLS { - cert, cerr := tls.LoadX509KeyPair(s.TLSCert, s.TLSKey) - if cerr != nil { - return fmt.Errorf("could not load TLS keys: %v", cerr) - } - nextProtos := []string{"h2", "http/1.1", "http/1.0"} - if s.DisableALPN { - nextProtos = nil - } - config := &tls.Config{ - Certificates: []tls.Certificate{cert}, - NextProtos: nextProtos, - GetConfigForClient: func(info *tls.ClientHelloInfo) (*tls.Config, error) { - // There isn't a way to pass through all ALPNs presented by the client down to the - // HTTP server to return in the response. However, for debugging, we can at least log - // them at this level. - epLog.Infof("TLS connection with alpn: %v", info.SupportedProtos) - return nil, nil - }, - } - // Listen on the given port and update the port if it changed from what was passed in. - listener, port, err = listenOnAddressTLS(s.ListenerIP, s.Port.Port, config) - // Store the actual listening port back to the argument. - s.Port.Port = port - } else { - // Listen on the given port and update the port if it changed from what was passed in. - listener, port, err = listenOnAddress(s.ListenerIP, s.Port.Port) - // Store the actual listening port back to the argument. - s.Port.Port = port - } - - if err != nil { - return err - } - - if s.isUDS() { - fmt.Printf("Listening HTTP/1.1 on %v\n", s.UDSServer) - } else if s.Port.TLS { - s.server.Addr = fmt.Sprintf(":%d", port) - fmt.Printf("Listening HTTPS/1.1 on %v\n", port) - } else { - s.server.Addr = fmt.Sprintf(":%d", port) - fmt.Printf("Listening HTTP/1.1 on %v\n", port) - } - - // Start serving HTTP traffic. - go func() { - err := s.server.Serve(listener) - epLog.Warnf("Port %d listener terminated with error: %v", port, err) - }() - - // Notify the WaitGroup once the port has transitioned to ready. - go s.awaitReady(onReady, listener.Addr().String()) - - return nil -} - -func (s *httpInstance) isUDS() bool { - return s.UDSServer != "" -} - -func (s *httpInstance) awaitReady(onReady OnReadyFunc, address string) { - defer onReady() - - client := http.Client{} - var url string - if s.isUDS() { - url = "http://unix/" + s.UDSServer - client.Transport = &http.Transport{ - DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { - return net.Dial("unix", s.UDSServer) - }, - } - } else if s.Port.TLS { - url = fmt.Sprintf("https://%s", address) - client.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} - } else { - url = fmt.Sprintf("http://%s", address) - } - - err := retry.UntilSuccess(func() error { - resp, err := client.Get(url) - if err != nil { - return err - } - defer resp.Body.Close() - - // The handler applies server readiness when handling HTTP requests. Since the - // server won't become ready until all endpoints (including this one) report - // ready, the handler will return 503. This means that the endpoint is now ready. - if resp.StatusCode != http.StatusServiceUnavailable { - return fmt.Errorf("unexpected status code %d", resp.StatusCode) - } - - // Server is up now, we're ready. - return nil - }, retry.Timeout(readyTimeout), retry.Delay(readyInterval)) - - if err != nil { - epLog.Errorf("readiness failed for endpoint %s: %v", url, err) - } else { - epLog.Infof("ready for HTTP endpoint %s", url) - } -} - -func (s *httpInstance) Close() error { - if s.server != nil { - return s.server.Close() - } - return nil -} - -type httpHandler struct { - Config -} - -// Imagine a pie of different flavors. -// The flavors are the HTTP response codes. -// The chance of a particular flavor is ( slices / sum of slices ). -type codeAndSlices struct { - httpResponseCode int - slices int -} - -func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - id := uuid.New() - epLog.WithLabels("method", r.Method, "url", r.URL, "host", r.Host, "headers", r.Header, "id", id).Infof("HTTP Request") - if h.Port == nil { - defer common.Metrics.HTTPRequests.With(common.PortLabel.Value("uds")).Increment() - } else { - defer common.Metrics.HTTPRequests.With(common.PortLabel.Value(strconv.Itoa(h.Port.Port))).Increment() - } - if !h.IsServerReady() { - // Handle readiness probe failure. - epLog.Infof("HTTP service not ready, returning 503") - w.WriteHeader(http.StatusServiceUnavailable) - return - } - - if common.IsWebSocketRequest(r) { - h.webSocketEcho(w, r) - } else { - h.echo(w, r, id) - } -} - -// nolint: interfacer -func writeError(out *bytes.Buffer, msg string) { - epLog.Warn(msg) - _, _ = out.WriteString(msg + "\n") -} - -func (h *httpHandler) echo(w http.ResponseWriter, r *http.Request, id uuid.UUID) { - body := bytes.Buffer{} - - if err := r.ParseForm(); err != nil { - writeError(&body, "ParseForm() error: "+err.Error()) - } - - // If the request has form ?delay=[:duration] wait for duration - // For example, ?delay=10s will cause the response to wait 10s before responding - if err := delayResponse(r); err != nil { - writeError(&body, "error delaying response error: "+err.Error()) - } - - // If the request has form ?headers=name:value[,name:value]* return those headers in response - if err := setHeaderResponseFromHeaders(r, w); err != nil { - writeError(&body, "response headers error: "+err.Error()) - } - - // If the request has form ?codes=code[:chance][,code[:chance]]* return those codes, rather than 200 - // For example, ?codes=500:1,200:1 returns 500 1/2 times and 200 1/2 times - // For example, ?codes=500:90,200:10 returns 500 90% of times and 200 10% of times - code, err := setResponseFromCodes(r, w) - if err != nil { - writeError(&body, "codes error: "+err.Error()) - } - - h.addResponsePayload(r, &body) - - w.Header().Set("Content-Type", "application/text") - if _, err := w.Write(body.Bytes()); err != nil { - epLog.Warn(err) - } - epLog.WithLabels("code", code, "headers", w.Header(), "id", id).Infof("HTTP Response") -} - -func (h *httpHandler) webSocketEcho(w http.ResponseWriter, r *http.Request) { - // adapted from https://github.com/gorilla/websocket/blob/master/examples/echo/server.go - // First send upgrade headers - c, err := webSocketUpgrader.Upgrade(w, r, nil) - if err != nil { - epLog.Warn("websocket-echo upgrade failed: " + err.Error()) - return - } - - defer func() { _ = c.Close() }() - - // ping - mt, message, err := c.ReadMessage() - if err != nil { - epLog.Warn("websocket-echo read failed: " + err.Error()) - return - } - - body := bytes.Buffer{} - h.addResponsePayload(r, &body) - body.Write(message) - - writeField(&body, echo.StatusCodeField, strconv.Itoa(http.StatusOK)) - - // pong - err = c.WriteMessage(mt, body.Bytes()) - if err != nil { - writeError(&body, "websocket-echo write failed: "+err.Error()) - return - } -} - -// nolint: interfacer -func (h *httpHandler) addResponsePayload(r *http.Request, body *bytes.Buffer) { - port := "" - if h.Port != nil { - port = strconv.Itoa(h.Port.Port) - } - - writeField(body, echo.ServiceVersionField, h.Version) - writeField(body, echo.ServicePortField, port) - writeField(body, echo.HostField, r.Host) - // Use raw path, we don't want golang normalizing anything since we use this for testing purposes - writeField(body, echo.URLField, r.RequestURI) - writeField(body, echo.ClusterField, h.Cluster) - writeField(body, echo.IstioVersionField, h.IstioVersion) - - writeField(body, echo.MethodField, r.Method) - writeField(body, echo.ProtocolField, r.Proto) - ip, _, _ := net.SplitHostPort(r.RemoteAddr) - writeField(body, echo.IPField, ip) - - // Note: since this is the NegotiatedProtocol, it will be set to empty if the client sends an ALPN - // not supported by the server (ie one of h2,http/1.1,http/1.0) - var alpn string - if r.TLS != nil { - alpn = r.TLS.NegotiatedProtocol - } - writeField(body, echo.AlpnField, alpn) - - var keys []string - for k := range r.Header { - keys = append(keys, k) - } - sort.Strings(keys) - for _, key := range keys { - values := r.Header[key] - for _, value := range values { - writeRequestHeader(body, key, value) - } - } - - if hostname, err := os.Hostname(); err == nil { - writeField(body, echo.HostnameField, hostname) - } -} - -func delayResponse(request *http.Request) error { - d := request.FormValue("delay") - if len(d) == 0 { - return nil - } - - t, err := time.ParseDuration(d) - if err != nil { - return err - } - time.Sleep(t) - return nil -} - -func setHeaderResponseFromHeaders(request *http.Request, response http.ResponseWriter) error { - s := request.FormValue("headers") - if len(s) == 0 { - return nil - } - responseHeaders := strings.Split(s, ",") - for _, responseHeader := range responseHeaders { - parts := strings.Split(responseHeader, ":") - // require name:value format - if len(parts) != 2 { - return fmt.Errorf("invalid %q (want name:value)", responseHeader) - } - name := parts[0] - value := parts[1] - // Avoid using .Set() to allow users to pass non-canonical forms - response.Header()[name] = []string{value} - } - return nil -} - -func setResponseFromCodes(request *http.Request, response http.ResponseWriter) (int, error) { - responseCodes := request.FormValue("codes") - - codes, err := validateCodes(responseCodes) - if err != nil { - return 0, err - } - - // Choose a random "slice" from a pie - totalSlices := 0 - for _, flavor := range codes { - totalSlices += flavor.slices - } - slice := rand.Intn(totalSlices) - - // What flavor is that slice? - responseCode := codes[len(codes)-1].httpResponseCode // Assume the last slice - position := 0 - for n, flavor := range codes { - if position > slice { - responseCode = codes[n-1].httpResponseCode // No, use an earlier slice - break - } - position += flavor.slices - } - - response.WriteHeader(responseCode) - return responseCode, nil -} - -// codes must be comma-separated HTTP response code, colon, positive integer -func validateCodes(codestrings string) ([]codeAndSlices, error) { - if codestrings == "" { - // Consider no codes to be "200:1" -- return HTTP 200 100% of the time. - codestrings = strconv.Itoa(http.StatusOK) + ":1" - } - - aCodestrings := strings.Split(codestrings, ",") - codes := make([]codeAndSlices, len(aCodestrings)) - - for i, codestring := range aCodestrings { - codeAndSlice, err := validateCodeAndSlices(codestring) - if err != nil { - return []codeAndSlices{{http.StatusBadRequest, 1}}, err - } - codes[i] = codeAndSlice - } - - return codes, nil -} - -// code must be HTTP response code -func validateCodeAndSlices(codecount string) (codeAndSlices, error) { - flavor := strings.Split(codecount, ":") - - // Demand code or code:number - if len(flavor) == 0 || len(flavor) > 2 { - return codeAndSlices{http.StatusBadRequest, 9999}, - fmt.Errorf("invalid %q (want code or code:count)", codecount) - } - - n, err := strconv.Atoi(flavor[0]) - if err != nil { - return codeAndSlices{http.StatusBadRequest, 9999}, err - } - - if n < http.StatusOK || n >= 600 { - return codeAndSlices{http.StatusBadRequest, 9999}, - fmt.Errorf("invalid HTTP response code %v", n) - } - - count := 1 - if len(flavor) > 1 { - count, err = strconv.Atoi(flavor[1]) - if err != nil { - return codeAndSlices{http.StatusBadRequest, 9999}, err - } - if count < 0 { - return codeAndSlices{http.StatusBadRequest, 9999}, - fmt.Errorf("invalid count %v", count) - } - } - - return codeAndSlices{n, count}, nil -} diff --git a/pkg/test/echo/server/endpoint/instance.go b/pkg/test/echo/server/endpoint/instance.go deleted file mode 100644 index 3d95f4a88..000000000 --- a/pkg/test/echo/server/endpoint/instance.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package endpoint - -import ( - "fmt" - "io" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" -) - -// IsServerReadyFunc is a function that indicates whether the server is currently ready to handle traffic. -type IsServerReadyFunc func() bool - -// OnReadyFunc is a callback function that informs the server that the endpoint is ready. -type OnReadyFunc func() - -// Config for a single endpoint Instance. -type Config struct { - IsServerReady IsServerReadyFunc - Version string - Cluster string - TLSCert string - TLSKey string - UDSServer string - Dialer common.Dialer - Port *common.Port - ListenerIP string - IstioVersion string - DisableALPN bool -} - -// Instance of an endpoint that serves the Echo application on a single port/protocol. -type Instance interface { - io.Closer - Start(onReady OnReadyFunc) error - GetConfig() Config -} - -// New creates a new endpoint Instance. -func New(cfg Config) (Instance, error) { - if cfg.Port != nil { - switch cfg.Port.Protocol { - case protocol.HTTP, protocol.HTTPS: - return newHTTP(cfg), nil - case protocol.HTTP2, protocol.GRPC: - return newGRPC(cfg), nil - case protocol.TCP: - return newTCP(cfg), nil - default: - return nil, fmt.Errorf("unsupported protocol: %s", cfg.Port.Protocol) - } - } - - if len(cfg.UDSServer) > 0 { - return newHTTP(cfg), nil - } - - return nil, fmt.Errorf("either port or UDS must be specified") -} diff --git a/pkg/test/echo/server/endpoint/tcp.go b/pkg/test/echo/server/endpoint/tcp.go deleted file mode 100644 index f1b60d2a2..000000000 --- a/pkg/test/echo/server/endpoint/tcp.go +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package endpoint - -import ( - "crypto/tls" - "fmt" - "io" - "net" - "net/http" - "os" - "strconv" -) - -import ( - "github.com/google/uuid" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -var _ Instance = &tcpInstance{} - -type tcpInstance struct { - Config - l net.Listener -} - -func newTCP(config Config) Instance { - return &tcpInstance{ - Config: config, - } -} - -func (s *tcpInstance) GetConfig() Config { - return s.Config -} - -func (s *tcpInstance) Start(onReady OnReadyFunc) error { - var listener net.Listener - var port int - var err error - if s.Port.TLS { - cert, cerr := tls.LoadX509KeyPair(s.TLSCert, s.TLSKey) - if cerr != nil { - return fmt.Errorf("could not load TLS keys: %v", cerr) - } - config := &tls.Config{Certificates: []tls.Certificate{cert}} - // Listen on the given port and update the port if it changed from what was passed in. - listener, port, err = listenOnAddressTLS(s.ListenerIP, s.Port.Port, config) - // Store the actual listening port back to the argument. - s.Port.Port = port - } else { - // Listen on the given port and update the port if it changed from what was passed in. - listener, port, err = listenOnAddress(s.ListenerIP, s.Port.Port) - // Store the actual listening port back to the argument. - s.Port.Port = port - } - if err != nil { - return err - } - - s.l = listener - if s.Port.TLS { - fmt.Printf("Listening TCP (over TLS) on %v\n", port) - } else { - fmt.Printf("Listening TCP on %v\n", port) - } - - // Start serving TCP traffic. - go func() { - for { - conn, err := listener.Accept() - if err != nil { - epLog.Warn("TCP accept failed: " + err.Error()) - return - } - - go s.echo(conn) - } - }() - - // Notify the WaitGroup once the port has transitioned to ready. - go s.awaitReady(onReady, listener.Addr().String()) - - return nil -} - -// Handles incoming connection. -func (s *tcpInstance) echo(conn net.Conn) { - defer common.Metrics.TCPRequests.With(common.PortLabel.Value(strconv.Itoa(s.Port.Port))).Increment() - defer func() { - _ = conn.Close() - }() - - // If this is server first, client expects a message from server. Send the magic string. - if s.Port.ServerFirst { - _, _ = conn.Write([]byte(common.ServerFirstMagicString)) - } - - id := uuid.New() - epLog.WithLabels("remote", conn.RemoteAddr(), "id", id).Infof("TCP Request") - firstReply := true - buf := make([]byte, 4096) - for { - n, err := conn.Read(buf) - - // important not to start sending any response until we've started reading the message, - // otherwise the response could be read when we expect the magic string - if firstReply { - s.writeResponse(conn) - firstReply = false - } - - if err != nil && err != io.EOF { - epLog.Warnf("TCP read failed: %v", err.Error()) - break - } - - // echo the message from the request - if n > 0 { - out := buf[:n] - if _, err := conn.Write(out); err != nil { - epLog.Warnf("TCP write failed, :%v", err) - break - } - } - - // Read can return n > 0 with EOF, do this last. - if err == io.EOF { - break - } - } - - epLog.WithLabels("id", id).Infof("TCP Response") -} - -func (s *tcpInstance) writeResponse(conn net.Conn) { - ip, _, _ := net.SplitHostPort(conn.RemoteAddr().String()) - // Write non-request fields specific to the instance - respFields := map[echo.Field]string{ - echo.StatusCodeField: strconv.Itoa(http.StatusOK), - echo.ClusterField: s.Cluster, - echo.IstioVersionField: s.IstioVersion, - echo.ServiceVersionField: s.Version, - echo.ServicePortField: strconv.Itoa(s.Port.Port), - echo.IPField: ip, - echo.ProtocolField: "TCP", - } - - if hostname, err := os.Hostname(); err == nil { - respFields[echo.HostnameField] = hostname - } - for field, val := range respFields { - val := fmt.Sprintf("%s=%s\n", string(field), val) - _, err := conn.Write([]byte(val)) - if err != nil { - epLog.Warnf("TCP write failed %q: %v", val, err) - break - } - } -} - -func (s *tcpInstance) Close() error { - if s.l != nil { - s.l.Close() - } - return nil -} - -func (s *tcpInstance) awaitReady(onReady OnReadyFunc, address string) { - defer onReady() - - err := retry.UntilSuccess(func() error { - conn, err := net.Dial("tcp", address) - if err != nil { - return err - } - defer conn.Close() - - // Server is up now, we're ready. - return nil - }, retry.Timeout(readyTimeout), retry.Delay(readyInterval)) - - if err != nil { - epLog.Errorf("readiness failed for endpoint %s: %v", address, err) - } else { - epLog.Infof("ready for TCP endpoint %s", address) - } -} diff --git a/pkg/test/echo/server/endpoint/util.go b/pkg/test/echo/server/endpoint/util.go deleted file mode 100644 index 705650814..000000000 --- a/pkg/test/echo/server/endpoint/util.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package endpoint - -import ( - "bytes" - "crypto/tls" - "net" - "os" - "strconv" -) - -import ( - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/echo" -) - -var epLog = log.RegisterScope("endpoint", "echo serverside", 0) - -func listenOnAddress(ip string, port int) (net.Listener, int, error) { - parsedIP := net.ParseIP(ip) - ipBind := "tcp" - if parsedIP != nil { - if parsedIP.To4() == nil && parsedIP.To16() != nil { - ipBind = "tcp6" - } else if parsedIP.To4() != nil { - ipBind = "tcp4" - } - } - ln, err := net.Listen(ipBind, net.JoinHostPort(ip, strconv.Itoa(port))) - if err != nil { - return nil, 0, err - } - - port = ln.Addr().(*net.TCPAddr).Port - return ln, port, nil -} - -func listenOnAddressTLS(ip string, port int, cfg *tls.Config) (net.Listener, int, error) { - ipBind := "tcp" - parsedIP := net.ParseIP(ip) - if parsedIP != nil { - if parsedIP.To4() == nil && parsedIP.To16() != nil { - ipBind = "tcp6" - } else if parsedIP.To4() != nil { - ipBind = "tcp4" - } - } - ln, err := tls.Listen(ipBind, net.JoinHostPort(ip, strconv.Itoa(port)), cfg) - if err != nil { - return nil, 0, err - } - port = ln.Addr().(*net.TCPAddr).Port - return ln, port, nil -} - -func listenOnUDS(uds string) (net.Listener, error) { - _ = os.Remove(uds) - ln, err := net.Listen("unix", uds) - if err != nil { - return nil, err - } - - return ln, nil -} - -// nolint: interfacer -func writeField(out *bytes.Buffer, field echo.Field, value string) { - _, _ = out.WriteString(string(field) + "=" + value + "\n") -} - -// nolint: interfacer -func writeRequestHeader(out *bytes.Buffer, key, value string) { - writeField(out, echo.RequestHeaderField, key+":"+value) -} diff --git a/pkg/test/echo/server/forwarder/config.go b/pkg/test/echo/server/forwarder/config.go deleted file mode 100644 index bae5a2eca..000000000 --- a/pkg/test/echo/server/forwarder/config.go +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package forwarder - -import ( - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "fmt" - "net/http" - "net/url" - "os" - "strings" - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common/scheme" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/proto" -) - -type Config struct { - Request *proto.ForwardEchoRequest - UDS string - // XDSTestBootstrap, for gRPC forwarders, is used to set the bootstrap without using a global one defined in the env - XDSTestBootstrap []byte - // Http proxy used for connection - Proxy string - - // Filled in values. - scheme scheme.Instance - tlsConfig *tls.Config - getClientCertificate func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) - checkRedirect func(req *http.Request, via []*http.Request) error - proxyURL func(*http.Request) (*url.URL, error) - timeout time.Duration - count int - headers http.Header -} - -func (c *Config) fillDefaults() error { - c.checkRedirect = checkRedirectFunc(c.Request) - c.timeout = common.GetTimeout(c.Request) - c.count = common.GetCount(c.Request) - c.headers = common.GetHeaders(c.Request) - - if i := strings.IndexByte(c.Request.Url, ':'); i > 0 { - c.scheme = scheme.Instance(strings.ToLower(c.Request.Url[0:i])) - } else { - return fmt.Errorf("missing protocol scheme in the request URL: %s", c.Request.Url) - } - - var err error - c.getClientCertificate, err = getClientCertificateFunc(c.Request) - if err != nil { - return err - } - c.tlsConfig, err = newTLSConfig(c.Request, c.getClientCertificate) - if err != nil { - return err - } - - // Parse the proxy if specified. - if len(c.Proxy) > 0 { - proxyURL, err := url.Parse(c.Proxy) - if err != nil { - return err - } - - c.proxyURL = http.ProxyURL(proxyURL) - } - - return nil -} - -func getClientCertificateFunc(r *proto.ForwardEchoRequest) (func(info *tls.CertificateRequestInfo) (*tls.Certificate, error), error) { - if r.KeyFile != "" && r.CertFile != "" { - certData, err := os.ReadFile(r.CertFile) - if err != nil { - return nil, fmt.Errorf("failed to load client certificate: %v", err) - } - r.Cert = string(certData) - keyData, err := os.ReadFile(r.KeyFile) - if err != nil { - return nil, fmt.Errorf("failed to load client certificate key: %v", err) - } - r.Key = string(keyData) - } - - if r.Cert != "" && r.Key != "" { - cert, err := tls.X509KeyPair([]byte(r.Cert), []byte(r.Key)) - if err != nil { - return nil, fmt.Errorf("failed to parse x509 key pair: %v", err) - } - - for _, c := range cert.Certificate { - cert, err := x509.ParseCertificate(c) - if err != nil { - fwLog.Errorf("Failed to parse client certificate: %v", err) - } - fwLog.Debugf("Using client certificate [%s] issued by %s", cert.SerialNumber, cert.Issuer) - for _, uri := range cert.URIs { - fwLog.Debugf(" URI SAN: %s", uri) - } - } - // nolint: unparam - return func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) { - fwLog.Debugf("Peer asking for client certificate") - for i, ca := range info.AcceptableCAs { - x := &pkix.RDNSequence{} - if _, err := asn1.Unmarshal(ca, x); err != nil { - fwLog.Errorf("Failed to decode AcceptableCA[%d]: %v", i, err) - } else { - name := &pkix.Name{} - name.FillFromRDNSequence(x) - fwLog.Debugf(" AcceptableCA[%d]: %s", i, name) - } - } - - return &cert, nil - }, nil - } - - return nil, nil -} - -func newTLSConfig(r *proto.ForwardEchoRequest, getClientCertificate func(info *tls.CertificateRequestInfo) (*tls.Certificate, error)) (*tls.Config, error) { - tlsConfig := &tls.Config{ - GetClientCertificate: getClientCertificate, - NextProtos: r.GetAlpn().GetValue(), - ServerName: r.ServerName, - } - if r.CaCertFile != "" { - certData, err := os.ReadFile(r.CaCertFile) - if err != nil { - return nil, fmt.Errorf("failed to load client certificate: %v", err) - } - r.CaCert = string(certData) - } - if r.InsecureSkipVerify || r.CaCert == "" { - tlsConfig.InsecureSkipVerify = true - } else if r.CaCert != "" { - certPool := x509.NewCertPool() - if !certPool.AppendCertsFromPEM([]byte(r.CaCert)) { - return nil, fmt.Errorf("failed to create cert pool") - } - tlsConfig.RootCAs = certPool - } - return tlsConfig, nil -} - -func checkRedirectFunc(req *proto.ForwardEchoRequest) func(req *http.Request, via []*http.Request) error { - if req.FollowRedirects { - return nil - } - - return func(req *http.Request, via []*http.Request) error { - // Disable redirects - return http.ErrUseLastResponse - } -} diff --git a/pkg/test/echo/server/forwarder/dns.go b/pkg/test/echo/server/forwarder/dns.go deleted file mode 100644 index 3c8f445cc..000000000 --- a/pkg/test/echo/server/forwarder/dns.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package forwarder - -import ( - "bytes" - "context" - "fmt" - "net" - "net/url" - "strings" -) - -var _ protocol = &dnsProtocol{} - -type dnsProtocol struct{} - -type dnsRequest struct { - hostname string - dnsServer string - query string - protocol string -} - -func checkIn(got string, want ...string) error { - for _, w := range want { - if w == got { - return nil - } - } - return fmt.Errorf("got value %q, wanted one of %v", got, want) -} - -func parseRequest(inputURL string) (dnsRequest, error) { - req := dnsRequest{} - u, err := url.Parse(inputURL) - if err != nil { - return req, err - } - qp, err := url.ParseQuery(u.RawQuery) - if err != nil { - return req, err - } - req.protocol = qp.Get("protocol") - if err := checkIn(req.protocol, "", "udp", "tcp"); err != nil { - return req, err - } - req.dnsServer = qp.Get("server") - if req.dnsServer != "" { - if _, _, err := net.SplitHostPort(req.dnsServer); err != nil && strings.Contains(err.Error(), "missing port in address") { - req.dnsServer += ":53" - } - } - req.hostname = u.Host - req.query = qp.Get("query") - if err := checkIn(req.query, "", "A", "AAAA"); err != nil { - return req, err - } - return req, nil -} - -func (c *dnsProtocol) makeRequest(ctx context.Context, rreq *request) (string, error) { - req, err := parseRequest(rreq.URL) - if err != nil { - return "", err - } - r := newResolver(rreq.Timeout, req.protocol, req.dnsServer) - nt := func() string { - switch req.query { - case "A": - return "ip4" - case "AAAA": - return "ip6" - default: - return "ip" - } - }() - ctx, cancel := context.WithTimeout(ctx, rreq.Timeout) - defer cancel() - ips, err := r.LookupIP(ctx, nt, req.hostname) - if err != nil { - return "", err - } - - var outBuffer bytes.Buffer - outBuffer.WriteString(fmt.Sprintf("[%d] Hostname=%s\n", rreq.RequestID, req.hostname)) - outBuffer.WriteString(fmt.Sprintf("[%d] Protocol=%s\n", rreq.RequestID, req.protocol)) - outBuffer.WriteString(fmt.Sprintf("[%d] Query=%s\n", rreq.RequestID, req.query)) - outBuffer.WriteString(fmt.Sprintf("[%d] DnsServer=%s\n", rreq.RequestID, req.dnsServer)) - for n, i := range ips { - outBuffer.WriteString(fmt.Sprintf("[%d body] Response%d=%s\n", rreq.RequestID, n, i.String())) - } - return outBuffer.String(), nil -} - -func (c *dnsProtocol) Close() error { - return nil -} diff --git a/pkg/test/echo/server/forwarder/grpc.go b/pkg/test/echo/server/forwarder/grpc.go deleted file mode 100644 index a64160661..000000000 --- a/pkg/test/echo/server/forwarder/grpc.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package forwarder - -import ( - "bytes" - "context" - "fmt" - "net" - "strconv" - "strings" -) - -import ( - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/metadata" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/proto" -) - -var _ protocol = &grpcProtocol{} - -type grpcProtocol struct { - conn func() (conn *grpc.ClientConn, err error) -} - -func newGRPCProtocol(r *Config) (protocol, error) { - conn := func() (conn *grpc.ClientConn, err error) { - var opts []grpc.DialOption - - // Force DNS lookup each time. - opts = append(opts, grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) { - return newDialer().DialContext(ctx, "tcp", addr) - })) - - security := grpc.WithTransportCredentials(insecure.NewCredentials()) - if r.getClientCertificate != nil { - security = grpc.WithTransportCredentials(credentials.NewTLS(r.tlsConfig)) - } - - // Strip off the scheme from the address (for regular gRPC). - address := r.Request.Url[len(r.scheme+"://"):] - - // Connect to the GRPC server. - ctx, cancel := context.WithTimeout(context.Background(), common.ConnectionTimeout) - defer cancel() - opts = append(opts, security, grpc.WithAuthority(r.headers.Get(hostHeader))) - return grpc.DialContext(ctx, address, opts...) - } - - return &grpcProtocol{ - conn: conn, - }, nil -} - -func (c *grpcProtocol) makeRequest(ctx context.Context, req *request) (string, error) { - conn, err := c.conn() - if err != nil { - return "", err - } - defer func() { _ = conn.Close() }() - - return makeGRPCRequest(ctx, conn, req) -} - -func (c *grpcProtocol) Close() error { - return nil -} - -func makeGRPCRequest(ctx context.Context, conn *grpc.ClientConn, req *request) (string, error) { - // Set the per-request timeout. - ctx, cancel := context.WithTimeout(ctx, req.Timeout) - defer cancel() - - // Add headers to the request context. - outMD := make(metadata.MD) - for k, v := range req.Header { - // Exclude the Host header from the GRPC context. - if !strings.EqualFold(hostHeader, k) { - outMD.Set(k, v...) - } - } - outMD.Set("X-Request-Id", strconv.Itoa(req.RequestID)) - ctx = metadata.NewOutgoingContext(ctx, outMD) - - var outBuffer bytes.Buffer - grpcReq := &proto.EchoRequest{ - Message: req.Message, - } - outBuffer.WriteString(fmt.Sprintf("[%d] grpcecho.Echo(%v)\n", req.RequestID, req)) - - client := proto.NewEchoTestServiceClient(conn) - resp, err := client.Echo(ctx, grpcReq) - if err != nil { - return "", err - } - - // when the underlying HTTP2 request returns status 404, GRPC - // request does not return an error in grpc-go. - // instead it just returns an empty response - for _, line := range strings.Split(resp.GetMessage(), "\n") { - if line != "" { - outBuffer.WriteString(fmt.Sprintf("[%d body] %s\n", req.RequestID, line)) - } - } - return outBuffer.String(), nil -} diff --git a/pkg/test/echo/server/forwarder/http.go b/pkg/test/echo/server/forwarder/http.go deleted file mode 100644 index fcd5f43fd..000000000 --- a/pkg/test/echo/server/forwarder/http.go +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package forwarder - -import ( - "bytes" - "context" - "crypto/tls" - "fmt" - "io" - "net" - "net/http" - "net/url" - "sort" - "strings" -) - -import ( - "github.com/lucas-clemente/quic-go" - "github.com/lucas-clemente/quic-go/http3" - "golang.org/x/net/http2" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common/scheme" -) - -var _ protocol = &httpProtocol{} - -type httpProtocol struct { - *Config -} - -func newHTTPProtocol(r *Config) (*httpProtocol, error) { - // Per-protocol setup. - switch { - case r.Request.Http3: - if r.scheme == scheme.HTTP { - return nil, fmt.Errorf("http3 requires HTTPS") - } - case r.Request.Http2: - if r.Request.Alpn == nil { - r.tlsConfig.NextProtos = []string{"h2"} - } - default: - if r.Request.Alpn == nil { - r.tlsConfig.NextProtos = []string{"http/1.1"} - } - } - - return &httpProtocol{ - Config: r, - }, nil -} - -func splitPath(raw string) (url, path string) { - schemeSep := "://" - schemeBegin := strings.Index(raw, schemeSep) - if schemeBegin == -1 { - return raw, "" - } - schemeEnd := schemeBegin + len(schemeSep) - pathBegin := strings.IndexByte(raw[schemeEnd:], '/') - if pathBegin == -1 { - return raw, "" - } - return raw[:schemeEnd+pathBegin], raw[schemeEnd+pathBegin:] -} - -func (c *httpProtocol) newClient() (*http.Client, error) { - client := &http.Client{ - CheckRedirect: c.checkRedirect, - Timeout: c.timeout, - } - - switch { - case c.Request.Http3: - client.Transport = &http3.RoundTripper{ - TLSClientConfig: c.tlsConfig, - QuicConfig: &quic.Config{}, // nolint - } - case c.Request.Http2: - if c.scheme == scheme.HTTPS { - client.Transport = &http2.Transport{ - TLSClientConfig: c.tlsConfig, - DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) { - return tls.DialWithDialer(newDialer(), network, addr, cfg) - }, - } - } else { - client.Transport = &http2.Transport{ - // Golang doesn't have first class support for h2c, so we provide some workarounds - // See https://www.mailgun.com/blog/http-2-cleartext-h2c-client-example-go/ - // So http2.Transport doesn't complain the URL scheme isn't 'https' - AllowHTTP: true, - // Pretend we are dialing a TLS endpoint. (Note, we ignore the passed tls.Config) - DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) { - return newDialer().Dial(network, addr) - }, - } - } - default: - dialContext := func(ctx context.Context, network, addr string) (net.Conn, error) { - return newDialer().Dial(network, addr) - } - if len(c.UDS) > 0 { - dialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { - return newDialer().Dial("unix", c.UDS) - } - } - transport := &http.Transport{ - // No connection pooling. - DisableKeepAlives: true, - TLSClientConfig: c.tlsConfig, - DialContext: dialContext, - Proxy: http.ProxyFromEnvironment, - } - client.Transport = transport - - // Set the proxy in the transport, if specified. - if len(c.Proxy) > 0 { - proxyURL, err := url.Parse(c.Proxy) - if err != nil { - return nil, err - } - transport.Proxy = http.ProxyURL(proxyURL) - } - } - - return client, nil -} - -func (c *httpProtocol) setHost(client *http.Client, r *http.Request, host string) { - r.Host = host - - if r.URL.Scheme == "https" { - // Set SNI value to be same as the request Host - // For use with SNI routing tests - httpTransport, ok := client.Transport.(*http.Transport) - if ok && httpTransport.TLSClientConfig.ServerName == "" { - httpTransport.TLSClientConfig.ServerName = host - return - } - - http2Transport, ok := client.Transport.(*http2.Transport) - if ok && http2Transport.TLSClientConfig.ServerName == "" { - http2Transport.TLSClientConfig.ServerName = host - return - } - - http3Transport, ok := client.Transport.(*http3.RoundTripper) - if ok && http3Transport.TLSClientConfig.ServerName == "" { - http3Transport.TLSClientConfig.ServerName = host - return - } - } -} - -func (c *httpProtocol) makeRequest(ctx context.Context, req *request) (string, error) { - method := req.Method - if method == "" { - method = "GET" - } - - // Manually split the path from the URL, the http.NewRequest() will fail to parse paths with invalid encoding that we - // intentionally used in the test. - u, p := splitPath(req.URL) - httpReq, err := http.NewRequest(method, u, nil) - if err != nil { - return "", err - } - // Use raw path, we don't want golang normalizing anything since we use this for testing purposes - httpReq.URL.Opaque = p - - // Set the per-request timeout. - ctx, cancel := context.WithTimeout(ctx, req.Timeout) - defer cancel() - httpReq = httpReq.WithContext(ctx) - - var outBuffer bytes.Buffer - outBuffer.WriteString(fmt.Sprintf("[%d] Url=%s\n", req.RequestID, req.URL)) - host := "" - writeHeaders(req.RequestID, req.Header, outBuffer, func(key string, value string) { - if key == hostHeader { - host = value - } else { - // Avoid using .Add() to allow users to pass non-canonical forms - httpReq.Header[key] = append(httpReq.Header[key], value) - } - }) - - // Create a new HTTP client. - client, err := c.newClient() - if err != nil { - return outBuffer.String(), err - } - - c.setHost(client, httpReq, host) - - httpResp, err := client.Do(httpReq) - if err != nil { - return outBuffer.String(), err - } - - outBuffer.WriteString(fmt.Sprintf("[%d] %s=%d\n", req.RequestID, echo.StatusCodeField, httpResp.StatusCode)) - - var keys []string - for k := range httpResp.Header { - keys = append(keys, k) - } - sort.Strings(keys) - for _, key := range keys { - values := httpResp.Header[key] - for _, value := range values { - outBuffer.WriteString(fmt.Sprintf("[%d] %s=%s:%s\n", req.RequestID, echo.ResponseHeaderField, key, value)) - } - } - - data, err := io.ReadAll(httpResp.Body) - defer func() { - if err = httpResp.Body.Close(); err != nil { - outBuffer.WriteString(fmt.Sprintf("[%d error] %s\n", req.RequestID, err)) - } - }() - - if err != nil { - return outBuffer.String(), err - } - - for _, line := range strings.Split(string(data), "\n") { - if line != "" { - outBuffer.WriteString(fmt.Sprintf("[%d body] %s\n", req.RequestID, line)) - } - } - - return outBuffer.String(), nil -} - -func (c *httpProtocol) Close() error { - return nil -} diff --git a/pkg/test/echo/server/forwarder/instance.go b/pkg/test/echo/server/forwarder/instance.go deleted file mode 100644 index 83450efc8..000000000 --- a/pkg/test/echo/server/forwarder/instance.go +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package forwarder - -import ( - "context" - "fmt" - "io" - "net/http" - "sync" - "time" -) - -import ( - "github.com/hashicorp/go-multierror" - "golang.org/x/sync/semaphore" - wrappers "google.golang.org/protobuf/types/known/wrapperspb" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/proto" -) - -var _ io.Closer = &Instance{} - -const maxConcurrency = 20 - -// Instance processes a single proto.ForwardEchoRequest, sending individual echo requests to the destination URL. -type Instance struct { - p protocol - url string - serverFirst bool - timeout time.Duration - count int - qps int - header http.Header - message string - // Method for the request. Only valid for HTTP - method string - expectedResponse *wrappers.StringValue -} - -// New creates a new forwarder Instance. -func New(cfg Config) (*Instance, error) { - if err := cfg.fillDefaults(); err != nil { - return nil, err - } - - p, err := newProtocol(&cfg) - if err != nil { - return nil, err - } - - return &Instance{ - p: p, - url: cfg.Request.Url, - serverFirst: cfg.Request.ServerFirst, - method: cfg.Request.Method, - timeout: cfg.timeout, - count: cfg.count, - qps: int(cfg.Request.Qps), - header: cfg.headers, - message: cfg.Request.Message, - expectedResponse: cfg.Request.ExpectedResponse, - }, nil -} - -// Run the forwarder and collect the responses. -func (i *Instance) Run(ctx context.Context) (*proto.ForwardEchoResponse, error) { - g := multierror.Group{} - responsesMu := sync.RWMutex{} - responses, responseTimes := make([]string, i.count), make([]time.Duration, i.count) - - var throttle *time.Ticker - - if i.qps > 0 { - sleepTime := time.Second / time.Duration(i.qps) - fwLog.Debugf("Sleeping %v between requests", sleepTime) - throttle = time.NewTicker(sleepTime) - } - - // make the timeout apply to the entire set of requests - ctx, cancel := context.WithTimeout(ctx, i.timeout) - var canceled bool - defer func() { - cancel() - canceled = true - }() - - sem := semaphore.NewWeighted(maxConcurrency) - for reqIndex := 0; reqIndex < i.count; reqIndex++ { - r := request{ - RequestID: reqIndex, - URL: i.url, - Message: i.message, - ExpectedResponse: i.expectedResponse, - Header: i.header, - Timeout: i.timeout, - ServerFirst: i.serverFirst, - Method: i.method, - } - - if throttle != nil { - <-throttle.C - } - - if err := sem.Acquire(ctx, 1); err != nil { - // this should only occur for a timeout, fallthrough to the ctx.Done() select case - break - } - g.Go(func() error { - defer sem.Release(1) - if canceled { - return fmt.Errorf("request set timed out") - } - st := time.Now() - resp, err := i.p.makeRequest(ctx, &r) - rt := time.Since(st) - if err != nil { - return err - } - responsesMu.Lock() - responses[r.RequestID] = resp - responseTimes[r.RequestID] = rt - responsesMu.Unlock() - return nil - }) - } - - requestsDone := make(chan *multierror.Error) - go func() { - requestsDone <- g.Wait() - }() - - select { - case err := <-requestsDone: - if err != nil { - return nil, fmt.Errorf("%d/%d requests had errors; first error: %v", err.Len(), i.count, err.Errors[0]) - } - case <-ctx.Done(): - responsesMu.RLock() - defer responsesMu.RUnlock() - var c int - var tt time.Duration - for id, res := range responses { - if res != "" && responseTimes[id] != 0 { - c++ - tt += responseTimes[id] - } - } - var avgTime time.Duration - if c > 0 { - avgTime = tt / time.Duration(c) - } - return nil, fmt.Errorf("request set timed out after %v and only %d/%d requests completed (%v avg)", i.timeout, c, i.count, avgTime) - } - - return &proto.ForwardEchoResponse{ - Output: responses, - }, nil -} - -func (i *Instance) Close() error { - if i != nil && i.p != nil { - return i.p.Close() - } - return nil -} diff --git a/pkg/test/echo/server/forwarder/protocol.go b/pkg/test/echo/server/forwarder/protocol.go deleted file mode 100644 index babbaefd7..000000000 --- a/pkg/test/echo/server/forwarder/protocol.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// An example implementation of a client. - -package forwarder - -import ( - "context" - "fmt" - "net/http" - "time" -) - -import ( - wrappers "google.golang.org/protobuf/types/known/wrapperspb" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common/scheme" -) - -type request struct { - URL string - Header http.Header - RequestID int - Message string - ExpectedResponse *wrappers.StringValue - Timeout time.Duration - ServerFirst bool - Method string -} - -type protocol interface { - makeRequest(ctx context.Context, req *request) (string, error) - Close() error -} - -func newProtocol(cfg *Config) (protocol, error) { - switch cfg.scheme { - case scheme.HTTP, scheme.HTTPS: - return newHTTPProtocol(cfg) - case scheme.GRPC: - return newGRPCProtocol(cfg) - case scheme.XDS: - return newXDSProtocol(cfg) - case scheme.WebSocket: - return newWebsocketProtocol(cfg) - case scheme.DNS: - return &dnsProtocol{}, nil - case scheme.TCP: - return newTCPProtocol(cfg) - case scheme.TLS: - return newTLSProtocol(cfg) - default: - return nil, fmt.Errorf("unrecognized protocol %q", cfg.scheme) - } -} diff --git a/pkg/test/echo/server/forwarder/tcp.go b/pkg/test/echo/server/forwarder/tcp.go deleted file mode 100644 index 84b103b89..000000000 --- a/pkg/test/echo/server/forwarder/tcp.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package forwarder - -import ( - "bufio" - "bytes" - "context" - "crypto/tls" - "fmt" - "io" - "net" - "net/http" - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" -) - -var _ protocol = &tcpProtocol{} - -type tcpProtocol struct { - // conn returns a new connection. This is not just a shared connection as we will - // not re-use the connection for multiple requests with TCP - conn func() (net.Conn, error) -} - -func newTCPProtocol(r *Config) (protocol, error) { - return &tcpProtocol{ - conn: func() (net.Conn, error) { - address := r.Request.Url[len(r.scheme+"://"):] - - if r.getClientCertificate == nil { - ctx, cancel := context.WithTimeout(context.Background(), common.ConnectionTimeout) - defer cancel() - return newDialer().DialContext(ctx, "tcp", address) - } - return tls.DialWithDialer(newDialer(), "tcp", address, r.tlsConfig) - }, - }, nil -} - -func (c *tcpProtocol) makeRequest(ctx context.Context, req *request) (string, error) { - conn, err := c.conn() - if err != nil { - return "", err - } - defer func() { _ = conn.Close() }() - - msgBuilder := strings.Builder{} - msgBuilder.WriteString(fmt.Sprintf("[%d] Url=%s\n", req.RequestID, req.URL)) - - if req.Message != "" { - msgBuilder.WriteString(fmt.Sprintf("[%d] Echo=%s\n", req.RequestID, req.Message)) - } - - // Apply per-request timeout to calculate deadline for reads/writes. - ctx, cancel := context.WithTimeout(ctx, req.Timeout) - defer cancel() - - // Apply the deadline to the connection. - deadline, _ := ctx.Deadline() - if err := conn.SetWriteDeadline(deadline); err != nil { - return msgBuilder.String(), err - } - if err := conn.SetReadDeadline(deadline); err != nil { - return msgBuilder.String(), err - } - - // For server first protocol, we expect the server to send us the magic string first - if req.ServerFirst { - readBytes, err := bufio.NewReader(conn).ReadBytes('\n') - if err != nil { - fwLog.Warnf("server first TCP read failed: %v", err) - return "", err - } - if string(readBytes) != common.ServerFirstMagicString { - return "", fmt.Errorf("did not receive magic sting. Want %q, got %q", common.ServerFirstMagicString, string(readBytes)) - } - } - - // Make sure the client writes something to the buffer - message := "HelloWorld" - if req.Message != "" { - message = req.Message - } - - if _, err := conn.Write([]byte(message + "\n")); err != nil { - fwLog.Warnf("TCP write failed: %v", err) - return msgBuilder.String(), err - } - var resBuffer bytes.Buffer - buf := make([]byte, 1024+len(message)) - for { - n, err := conn.Read(buf) - if err != nil && err != io.EOF { - fwLog.Warnf("TCP read failed (already read %d bytes): %v", len(resBuffer.String()), err) - return msgBuilder.String(), err - } - resBuffer.Write(buf[:n]) - // the message is sent last - when we get the whole message we can stop reading - if err == io.EOF || strings.Contains(resBuffer.String(), message) { - break - } - } - - // format the output for forwarder response - for _, line := range strings.Split(resBuffer.String(), "\n") { - if line != "" { - msgBuilder.WriteString(fmt.Sprintf("[%d body] %s\n", req.RequestID, line)) - } - } - - msg := msgBuilder.String() - expected := fmt.Sprintf("%s=%d", string(echo.StatusCodeField), http.StatusOK) - if req.ExpectedResponse != nil { - expected = req.ExpectedResponse.GetValue() - } - if !strings.Contains(msg, expected) { - return msg, fmt.Errorf("expect to recv message with %s, got %s. Return EOF", expected, msg) - } - return msg, nil -} - -func (c *tcpProtocol) Close() error { - return nil -} diff --git a/pkg/test/echo/server/forwarder/tls.go b/pkg/test/echo/server/forwarder/tls.go deleted file mode 100644 index b50ce9a21..000000000 --- a/pkg/test/echo/server/forwarder/tls.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package forwarder - -import ( - "context" - "crypto/tls" - "encoding/pem" - "fmt" - "strings" -) - -var _ protocol = &tlsProtocol{} - -type tlsProtocol struct { - // conn returns a new connection. This is not just a shared connection as we will - // not re-use the connection for multiple requests with TCP - conn func() (*tls.Conn, error) -} - -func newTLSProtocol(r *Config) (protocol, error) { - return &tlsProtocol{ - conn: func() (*tls.Conn, error) { - address := r.Request.Url[len(r.scheme+"://"):] - - con, err := tls.DialWithDialer(newDialer(), "tcp", address, r.tlsConfig) - if err != nil { - return nil, err - } - return con, nil - }, - }, nil -} - -func (c *tlsProtocol) makeRequest(ctx context.Context, req *request) (string, error) { - conn, err := c.conn() - if err != nil { - return "", err - } - defer func() { _ = conn.Close() }() - msgBuilder := strings.Builder{} - msgBuilder.WriteString(fmt.Sprintf("[%d] Url=%s\n", req.RequestID, req.URL)) - - // Apply per-request timeout to calculate deadline for reads/writes. - ctx, cancel := context.WithTimeout(ctx, req.Timeout) - defer cancel() - - // Apply the deadline to the connection. - deadline, _ := ctx.Deadline() - if err := conn.SetWriteDeadline(deadline); err != nil { - return msgBuilder.String(), err - } - if err := conn.SetReadDeadline(deadline); err != nil { - return msgBuilder.String(), err - } - - if err := conn.HandshakeContext(ctx); err != nil { - return "", err - } - // Make sure the client writes something to the buffer - message := "HelloWorld" - if req.Message != "" { - message = req.Message - } - if _, err := conn.Write([]byte(message + "\n")); err != nil { - fwLog.Warnf("TCP write failed: %v", err) - return msgBuilder.String(), err - } - - cs := conn.ConnectionState() - msgBuilder.WriteString(fmt.Sprintf("[%d] Cipher=%s\n", req.RequestID, tls.CipherSuiteName(cs.CipherSuite))) - msgBuilder.WriteString(fmt.Sprintf("[%d] Version=%s\n", req.RequestID, versionName(cs.Version))) - msgBuilder.WriteString(fmt.Sprintf("[%d] ServerName=%s\n", req.RequestID, cs.ServerName)) - msgBuilder.WriteString(fmt.Sprintf("[%d] Alpn=%s\n", req.RequestID, cs.NegotiatedProtocol)) - for n, i := range cs.PeerCertificates { - pemBlock := pem.Block{ - Type: "CERTIFICATE", - Bytes: i.Raw, - } - msgBuilder.WriteString(fmt.Sprintf("[%d body] Response%d=%q\n", req.RequestID, n, string(pem.EncodeToMemory(&pemBlock)))) - } - - msg := msgBuilder.String() - return msg, nil -} - -func versionName(v uint16) string { - switch v { - case tls.VersionTLS10: - return "1.0" - case tls.VersionTLS11: - return "1.1" - case tls.VersionTLS12: - return "1.2" - case tls.VersionTLS13: - return "1.3" - default: - return fmt.Sprintf("unknown-%v", v) - } -} - -func (c *tlsProtocol) Close() error { - return nil -} diff --git a/pkg/test/echo/server/forwarder/util.go b/pkg/test/echo/server/forwarder/util.go deleted file mode 100644 index 116eafa99..000000000 --- a/pkg/test/echo/server/forwarder/util.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package forwarder - -import ( - "bytes" - "context" - "fmt" - "net" - "net/http" - "time" -) - -import ( - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" -) - -const ( - hostHeader = "Host" -) - -var fwLog = log.RegisterScope("forwarder", "echo clientside", 0) - -func writeHeaders(requestID int, header http.Header, outBuffer bytes.Buffer, addFn func(string, string)) { - for key, values := range header { - for _, v := range values { - addFn(key, v) - if key == hostHeader { - outBuffer.WriteString(fmt.Sprintf("[%d] Host=%s\n", requestID, v)) - } else { - outBuffer.WriteString(fmt.Sprintf("[%d] Header=%s:%s\n", requestID, key, v)) - } - } - } -} - -func newDialer() *net.Dialer { - return &net.Dialer{ - Timeout: common.ConnectionTimeout, - Resolver: newResolver(common.ConnectionTimeout, "", ""), - } -} - -func newResolver(timeout time.Duration, protocol, dnsServer string) *net.Resolver { - return &net.Resolver{ - PreferGo: true, - Dial: func(ctx context.Context, network, address string) (net.Conn, error) { - d := net.Dialer{ - Timeout: timeout, - } - nt := protocol - if nt == "" { - nt = network - } - addr := dnsServer - if addr == "" { - addr = address - } - return d.DialContext(ctx, nt, addr) - }, - } -} diff --git a/pkg/test/echo/server/forwarder/websocket.go b/pkg/test/echo/server/forwarder/websocket.go deleted file mode 100644 index a316cd2b8..000000000 --- a/pkg/test/echo/server/forwarder/websocket.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package forwarder - -import ( - "bytes" - "context" - "fmt" - "net" - "net/http" - "strings" -) - -import ( - "github.com/gorilla/websocket" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" -) - -var _ protocol = &websocketProtocol{} - -type websocketProtocol struct { - *Config -} - -func newWebsocketProtocol(r *Config) (protocol, error) { - return &websocketProtocol{ - Config: r, - }, nil -} - -func (c *websocketProtocol) makeRequest(ctx context.Context, req *request) (string, error) { - wsReq := make(http.Header) - - var outBuffer bytes.Buffer - outBuffer.WriteString(fmt.Sprintf("[%d] Url=%s\n", req.RequestID, req.URL)) - writeHeaders(req.RequestID, req.Header, outBuffer, wsReq.Add) - - // Set the special header to trigger the upgrade to WebSocket. - common.SetWebSocketHeader(wsReq) - - if req.Message != "" { - outBuffer.WriteString(fmt.Sprintf("[%d] Echo=%s\n", req.RequestID, req.Message)) - } - - dialContext := func(network, addr string) (net.Conn, error) { - return newDialer().Dial(network, addr) - } - if len(c.UDS) > 0 { - dialContext = func(network, addr string) (net.Conn, error) { - return newDialer().Dial("unix", c.UDS) - } - } - - dialer := &websocket.Dialer{ - TLSClientConfig: c.tlsConfig, - NetDial: dialContext, - HandshakeTimeout: c.timeout, - } - - conn, _, err := dialer.Dial(req.URL, wsReq) - if err != nil { - // timeout or bad handshake - return outBuffer.String(), err - } - defer func() { - _ = conn.Close() - }() - - // Apply per-request timeout to calculate deadline for reads/writes. - ctx, cancel := context.WithTimeout(ctx, req.Timeout) - defer cancel() - - // Apply the deadline to the connection. - deadline, _ := ctx.Deadline() - if err := conn.SetWriteDeadline(deadline); err != nil { - return outBuffer.String(), err - } - if err := conn.SetReadDeadline(deadline); err != nil { - return outBuffer.String(), err - } - - err = conn.WriteMessage(websocket.TextMessage, []byte(req.Message)) - if err != nil { - return outBuffer.String(), err - } - - _, resp, err := conn.ReadMessage() - if err != nil { - return outBuffer.String(), err - } - - for _, line := range strings.Split(string(resp), "\n") { - if line != "" { - outBuffer.WriteString(fmt.Sprintf("[%d body] %s\n", req.RequestID, line)) - } - } - - return outBuffer.String(), nil -} - -func (c *websocketProtocol) Close() error { - return nil -} diff --git a/pkg/test/echo/server/forwarder/xds.go b/pkg/test/echo/server/forwarder/xds.go deleted file mode 100644 index 087be6147..000000000 --- a/pkg/test/echo/server/forwarder/xds.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package forwarder - -import ( - "context" -) - -import ( - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/credentials/xds" - xdsresolver "google.golang.org/grpc/xds" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" -) - -var _ protocol = &grpcProtocol{} - -type xdsProtocol struct { - conn *grpc.ClientConn -} - -func newXDSProtocol(r *Config) (protocol, error) { - var opts []grpc.DialOption - // grpc-go sets incorrect authority header - - // transport security - creds, err := xds.NewClientCredentials(xds.ClientOptions{FallbackCreds: insecure.NewCredentials()}) - if err != nil { - return nil, err - } - security := grpc.WithTransportCredentials(creds) - if len(r.XDSTestBootstrap) > 0 { - r, err := xdsresolver.NewXDSResolverWithConfigForTesting(r.XDSTestBootstrap) - if err != nil { - return nil, err - } - opts = append(opts, grpc.WithResolvers(r)) - } - - if r.getClientCertificate != nil { - security = grpc.WithTransportCredentials(credentials.NewTLS(r.tlsConfig)) - } - - address := r.Request.Url - - // Connect to the GRPC server. - ctx, cancel := context.WithTimeout(context.Background(), common.ConnectionTimeout) - defer cancel() - opts = append(opts, security, grpc.WithAuthority(r.headers.Get(hostHeader))) - conn, err := grpc.DialContext(ctx, address, opts...) - if err != nil { - return nil, err - } - - return &xdsProtocol{ - conn: conn, - }, nil -} - -func (c *xdsProtocol) makeRequest(ctx context.Context, req *request) (string, error) { - return makeGRPCRequest(ctx, c.conn, req) -} - -func (c *xdsProtocol) Close() error { - return c.conn.Close() -} diff --git a/pkg/test/echo/server/instance.go b/pkg/test/echo/server/instance.go deleted file mode 100644 index 17eaa5b54..000000000 --- a/pkg/test/echo/server/instance.go +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "context" - "fmt" - "io" - "net" - "net/http" - "os" - "strings" - "sync" - "sync/atomic" -) - -import ( - ocprom "contrib.go.opencensus.io/exporter/prometheus" - "github.com/hashicorp/go-multierror" - "github.com/prometheus/client_golang/prometheus" - "go.opencensus.io/stats/view" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/util/network" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/server/endpoint" -) - -// Config for an echo server Instance. -type Config struct { - Ports common.PortList - BindIPPortsMap map[int]struct{} - BindLocalhostPortsMap map[int]struct{} - Metrics int - TLSCert string - TLSKey string - Version string - UDSServer string - Cluster string - Dialer common.Dialer - IstioVersion string - DisableALPN bool -} - -func (c Config) String() string { - var b strings.Builder - b.WriteString(fmt.Sprintf("Ports: %v\n", c.Ports)) - b.WriteString(fmt.Sprintf("BindIPPortsMap: %v\n", c.BindIPPortsMap)) - b.WriteString(fmt.Sprintf("BindLocalhostPortsMap: %v\n", c.BindLocalhostPortsMap)) - b.WriteString(fmt.Sprintf("Metrics: %v\n", c.Metrics)) - b.WriteString(fmt.Sprintf("TLSCert: %v\n", c.TLSCert)) - b.WriteString(fmt.Sprintf("TLSKey: %v\n", c.TLSKey)) - b.WriteString(fmt.Sprintf("Version: %v\n", c.Version)) - b.WriteString(fmt.Sprintf("UDSServer: %v\n", c.UDSServer)) - b.WriteString(fmt.Sprintf("Cluster: %v\n", c.Cluster)) - b.WriteString(fmt.Sprintf("IstioVersion: %v\n", c.IstioVersion)) - - return b.String() -} - -var _ io.Closer = &Instance{} - -// Instance of the Echo server. -type Instance struct { - Config - - endpoints []endpoint.Instance - metricsServer *http.Server - ready uint32 -} - -// New creates a new server instance. -func New(config Config) *Instance { - log.Infof("Creating Server with config:\n%s", config) - config.Dialer = config.Dialer.FillInDefaults() - - return &Instance{ - Config: config, - } -} - -// Start the server. -func (s *Instance) Start() (err error) { - defer func() { - if err != nil { - _ = s.Close() - } - }() - - if err = s.validate(); err != nil { - return err - } - - if s.Metrics > 0 { - go s.startMetricsServer() - } - s.endpoints = make([]endpoint.Instance, 0) - - for _, p := range s.Ports { - ip, err := s.getListenerIP(p) - if err != nil { - return err - } - for _, ip := range getBindAddresses(ip) { - ep, err := s.newEndpoint(p, ip, "") - if err != nil { - return err - } - s.endpoints = append(s.endpoints, ep) - } - } - - if len(s.UDSServer) > 0 { - ep, err := s.newEndpoint(nil, "", s.UDSServer) - if err != nil { - return err - } - s.endpoints = append(s.endpoints, ep) - } - - return s.waitUntilReady() -} - -func getBindAddresses(ip string) []string { - if ip != "" && ip != "localhost" { - return []string{ip} - } - // Binding to "localhost" will only bind to a single address (v4 or v6). We want both, so we need - // to be explicit - v4, v6 := false, false - // Obtain all the IPs from the node - ipAddrs, ok := network.GetPrivateIPs(context.Background()) - if !ok { - return []string{ip} - } - for _, ip := range ipAddrs { - addr := net.ParseIP(ip) - if addr == nil { - // Should not happen - continue - } - if addr.To4() != nil { - v4 = true - } else { - v6 = true - } - } - addrs := []string{} - if v4 { - if ip == "localhost" { - addrs = append(addrs, "127.0.0.1") - } else { - addrs = append(addrs, "0.0.0.0") - } - } - if v6 { - if ip == "localhost" { - addrs = append(addrs, "::1") - } else { - addrs = append(addrs, "::") - } - } - return addrs -} - -// Close implements the application.Application interface -func (s *Instance) Close() (err error) { - for _, s := range s.endpoints { - if s != nil { - err = multierror.Append(err, s.Close()) - } - } - return -} - -func (s *Instance) getListenerIP(port *common.Port) (string, error) { - // Not configured on this port, set to empty which will lead to wildcard bind - // Not 0.0.0.0 in case we want IPv6 - if port == nil { - return "", nil - } - if _, f := s.BindLocalhostPortsMap[port.Port]; f { - return "localhost", nil - } - if _, f := s.BindIPPortsMap[port.Port]; !f { - return "", nil - } - if ip, f := os.LookupEnv("INSTANCE_IP"); f { - return ip, nil - } - return "", fmt.Errorf("--bind-ip set but INSTANCE_IP undefined") -} - -func (s *Instance) newEndpoint(port *common.Port, listenerIP string, udsServer string) (endpoint.Instance, error) { - return endpoint.New(endpoint.Config{ - Port: port, - UDSServer: udsServer, - IsServerReady: s.isReady, - Version: s.Version, - Cluster: s.Cluster, - TLSCert: s.TLSCert, - TLSKey: s.TLSKey, - Dialer: s.Dialer, - ListenerIP: listenerIP, - DisableALPN: s.DisableALPN, - IstioVersion: s.IstioVersion, - }) -} - -func (s *Instance) isReady() bool { - return atomic.LoadUint32(&s.ready) == 1 -} - -func (s *Instance) waitUntilReady() error { - wg := &sync.WaitGroup{} - - onEndpointReady := func() { - wg.Done() - } - - // Start the servers, updating port numbers as necessary. - for _, ep := range s.endpoints { - wg.Add(1) - if err := ep.Start(onEndpointReady); err != nil { - return err - } - } - - // Wait for all the servers to start. - wg.Wait() - - // Indicate that the server is now ready. - atomic.StoreUint32(&s.ready, 1) - - log.Info("Echo server is now ready") - return nil -} - -func (s *Instance) validate() error { - for _, port := range s.Ports { - switch port.Protocol { - case protocol.TCP: - case protocol.HTTP: - case protocol.HTTPS: - case protocol.HTTP2: - case protocol.GRPC: - default: - return fmt.Errorf("protocol %v not currently supported", port.Protocol) - } - } - return nil -} - -func (s *Instance) startMetricsServer() { - mux := http.NewServeMux() - - exporter, err := ocprom.NewExporter(ocprom.Options{Registry: prometheus.DefaultRegisterer.(*prometheus.Registry)}) - if err != nil { - log.Errorf("could not set up prometheus exporter: %v", err) - return - } - view.RegisterExporter(exporter) - mux.Handle("/metrics", exporter) - s.metricsServer = &http.Server{ - Handler: mux, - } - if err := http.ListenAndServe(fmt.Sprintf(":%d", s.Metrics), mux); err != nil { - log.Errorf("metrics terminated with err: %v", err) - } -} diff --git a/pkg/test/env/http_client.go b/pkg/test/env/http_client.go deleted file mode 100644 index 904268a6a..000000000 --- a/pkg/test/env/http_client.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package env - -import ( - "fmt" - "io" - "log" - "net" - "net/http" - "time" -) - -const ( - // HTTP client time out. - httpTimeOut = 5 * time.Second - - // Maximum number of ping the server to wait. - maxAttempts = 30 -) - -// HTTPGet send GET -func HTTPGet(url string) (code int, respBody string, err error) { - log.Println("HTTP GET", url) - client := &http.Client{Timeout: httpTimeOut} - resp, err := client.Get(url) - if err != nil { - log.Println(err) - return 0, "", err - } - defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - if err != nil { - log.Println(err) - return 0, "", err - } - respBody = string(body) - code = resp.StatusCode - return code, respBody, nil -} - -// WaitForHTTPServer waits for a HTTP server -func WaitForHTTPServer(url string) error { - for i := 0; i < maxAttempts; i++ { - log.Println("Pinging URL: ", url) - code, _, err := HTTPGet(url) - if err == nil && code == http.StatusOK { - log.Println("Server is up and running...") - return nil - } - log.Println("Will wait 200ms and try again.") - time.Sleep(200 * time.Millisecond) - } - return fmt.Errorf("timeout waiting for server startup") -} - -// WaitForPort waits for a TCP port -func WaitForPort(port uint16) { - serverPort := fmt.Sprintf("localhost:%v", port) - for i := 0; i < maxAttempts; i++ { - log.Println("Pinging port: ", serverPort) - _, err := net.Dial("tcp", serverPort) - if err == nil { - log.Println("The port is up and running...") - return - } - log.Println("Wait 200ms and try again.") - time.Sleep(200 * time.Millisecond) - } - log.Println("Give up the wait, continue the test...") -} - -// IsPortUsed checks if a port is used -func IsPortUsed(port uint16) bool { - serverPort := fmt.Sprintf("localhost:%v", port) - _, err := net.DialTimeout("tcp", serverPort, 100*time.Millisecond) - return err == nil -} diff --git a/pkg/test/env/http_server.go b/pkg/test/env/http_server.go deleted file mode 100644 index 666046513..000000000 --- a/pkg/test/env/http_server.go +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package env - -import ( - "fmt" - "io" - "log" - "net" - "net/http" - "strconv" - "sync" - "time" -) - -// If HTTP header has non empty FailHeader, -// HTTP server will fail the request with 400 with FailBody in the response body. -const ( - FailHeader = "x-istio-backend-fail" - FailBody = "Bad request from backend." -) - -const publicKey = ` -{ - "keys": [ - { - "alg": "RS256", - "e": "AQAB", - "kid": "62a93512c9ee4c7f8067b5a216dade2763d32a47", - "kty": "RSA", - "n": "` + - "0YWnm_eplO9BFtXszMRQNL5UtZ8HJdTH2jK7vjs4XdLkPW7YBkkm_2xNgcaVpkW0VT2l4mU3KftR-6" + - "s3Oa5Rnz5BrWEUkCTVVolR7VYksfqIB2I_x5yZHdOiomMTcm3DheUUCgbJRv5OKRnNqszA4xHn3tA3" + - "Ry8VO3X7BgKZYAUh9fyZTFLlkeAh0-bLK5zvqCmKW5QgDIXSxUTJxPjZCgfx1vmAfGqaJb-nvmrORX" + - "Q6L284c73DUL7mnt6wj3H6tVqPKA27j56N0TB1Hfx4ja6Slr8S4EB3F1luYhATa1PKUSH8mYDW11Ho" + - "lzZmTQpRoLV8ZoHbHEaTfqX_aYahIw" + - `", - "use": "sig" - }, - { - "alg": "RS256", - "e": "AQAB", - "kid": "b3319a147514df7ee5e4bcdee51350cc890cc89e", - "kty": "RSA", - "n": "` + - "qDi7Tx4DhNvPQsl1ofxxc2ePQFcs-L0mXYo6TGS64CY_2WmOtvYlcLNZjhuddZVV2X88m0MfwaSA16w" + - "E-RiKM9hqo5EY8BPXj57CMiYAyiHuQPp1yayjMgoE1P2jvp4eqF-BTillGJt5W5RuXti9uqfMtCQdag" + - "B8EC3MNRuU_KdeLgBy3lS3oo4LOYd-74kRBVZbk2wnmmb7IhP9OoLc1-7-9qU1uhpDxmE6JwBau0mDS" + - "wMnYDS4G_ML17dC-ZDtLd1i24STUw39KH0pcSdfFbL2NtEZdNeam1DDdk0iUtJSPZliUHJBI_pj8M-2" + - "Mn_oA8jBuI8YKwBqYkZCN1I95Q" + - `", - "use": "sig" - } - ] -} -` - -// HTTPServer stores data for a HTTP server. -type HTTPServer struct { - port uint16 - lis net.Listener - - reqHeaders http.Header - mu sync.Mutex -} - -func pubkeyHandler(w http.ResponseWriter, _ *http.Request) { - _, _ = fmt.Fprintf(w, "%v", publicKey) -} - -// handle handles a request and sends response. If ?delay=n is in request URL, then sleeps for -// n second and sends response. -func (s *HTTPServer) handle(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - // Fail if there is such header. - if r.Header.Get(FailHeader) != "" { - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte(FailBody)) - return - } - - // echo back the Content-Type and Content-Length in the response - for _, k := range []string{"Content-Type", "Content-Length"} { - if v := r.Header.Get(k); v != "" { - w.Header().Set(k, v) - } - } - - if delay := r.URL.Query().Get("delay"); delay != "" { - delaySeconds, err := strconv.ParseInt(delay, 10, 64) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte("Bad request parameter: delay")) - return - } - time.Sleep(time.Duration(delaySeconds) * time.Second) - } - - w.WriteHeader(http.StatusOK) - - reqHeaders := make(http.Header) - reqHeaders[":method"] = []string{r.Method} - reqHeaders[":authority"] = []string{r.Host} - reqHeaders[":path"] = []string{r.URL.String()} - for name, headers := range r.Header { - reqHeaders[name] = append(reqHeaders[name], headers...) - } - - s.mu.Lock() - s.reqHeaders = reqHeaders - s.mu.Unlock() - - _, _ = w.Write(body) -} - -// NewHTTPServer creates a new HTTP server. -func NewHTTPServer(port uint16) (*HTTPServer, error) { - log.Printf("Http server listening on port %v\n", port) - lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) - if err != nil { - log.Fatal(err) - return nil, err - } - return &HTTPServer{ - port: port, - lis: lis, - }, nil -} - -// Start starts the server -func (s *HTTPServer) Start() <-chan error { - errCh := make(chan error, 2) - go func() { - http.HandleFunc("/", s.handle) - http.HandleFunc("/pubkey", pubkeyHandler) - errCh <- http.Serve(s.lis, nil) - }() - go func() { - url := fmt.Sprintf("http://localhost:%v/echo", s.port) - errCh <- WaitForHTTPServer(url) - }() - - return errCh -} - -// Stop shutdown the server -func (s *HTTPServer) Stop() { - log.Printf("Close HTTP server\n") - _ = s.lis.Close() - log.Printf("Close HTTP server -- Done\n") -} - -// LastRequestHeaders returns the headers from the last request and clears the value -func (s *HTTPServer) LastRequestHeaders() http.Header { - s.mu.Lock() - out := s.reqHeaders - s.reqHeaders = nil - s.mu.Unlock() - - return out -} diff --git a/pkg/test/env/istio.go b/pkg/test/env/istio.go deleted file mode 100644 index bee59ba93..000000000 --- a/pkg/test/env/istio.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package env - -import ( - "encoding/json" - "fmt" - "os" - "path" - "path/filepath" - "runtime" -) - -import ( - "istio.io/pkg/log" -) - -var ( - // TARGET_OUT environment variable - // nolint: revive, stylecheck - TARGET_OUT Variable = "TARGET_OUT" - - // LOCAL_OUT environment variable - // nolint: revive, stylecheck - LOCAL_OUT Variable = "LOCAL_OUT" - - // REPO_ROOT environment variable - // nolint: revive, stylecheck - REPO_ROOT Variable = "REPO_ROOT" - - // HUB is the Docker hub to be used for images. - // nolint: revive, stylecheck - HUB Variable = "HUB" - - // TAG is the Docker tag to be used for images. - // nolint: revive, stylecheck - TAG Variable = "TAG" - - // PULL_POLICY is the image pull policy to use when rendering templates. - // nolint: revive, stylecheck - PULL_POLICY Variable = "PULL_POLICY" - - // KUBECONFIG is the list of Kubernetes configuration files. If configuration files are specified on - // the command-line, that takes precedence. - // nolint: revive, stylecheck - KUBECONFIG Variable = "KUBECONFIG" - - // IstioSrc is the location of istio source ($TOP/src/istio.io/istio - IstioSrc = REPO_ROOT.ValueOrDefaultFunc(getDefaultIstioSrc) - - // IstioOut is the location of the output directory ($TOP/out) - IstioOut = verifyFile(TARGET_OUT, TARGET_OUT.ValueOrDefaultFunc(getDefaultIstioOut)) - - // LocalOut is the location of the output directory for the OS we are running in, - // not necessarily the OS we are building for - LocalOut = verifyFile(LOCAL_OUT, LOCAL_OUT.ValueOrDefaultFunc(getDefaultIstioOut)) - - // OtelCollectorInstallFilePath is the OpenTelemetry installation file. - OtelCollectorInstallFilePath = path.Join(IstioSrc, getInstallationFile("opentelemetry/opentelemetry-collector.yaml")) - - // StackdriverInstallFilePath is the stackdriver installation file. - StackdriverInstallFilePath = path.Join(IstioSrc, getInstallationFile("stackdriver/stackdriver.yaml")) - - // GCEMetadataServerInstallFilePath is the GCE Metadata Server installation file. - GCEMetadataServerInstallFilePath = path.Join(IstioSrc, getInstallationFile("gcemetadata/gce_metadata_server.yaml")) - - // ContainerRegistryServerInstallFilePath is the fake container registry installation file. - ContainerRegistryServerInstallFilePath = path.Join(IstioSrc, getInstallationFile("containerregistry/container_registry_server.yaml")) -) - -var ( - _, b, _, _ = runtime.Caller(0) - - // Root folder of this project - // This relies on the fact this file is 3 levels up from the root; if this changes, adjust the path below - Root = filepath.Join(filepath.Dir(b), "../../..") -) - -func getDefaultIstioSrc() string { - return Root -} - -func getInstallationFile(p string) string { - return fmt.Sprintf("pkg/test/framework/components/%s", p) -} - -func getDefaultIstioOut() string { - return fmt.Sprintf("%s/out/%s_%s", IstioSrc, runtime.GOOS, runtime.GOARCH) -} - -func verifyFile(v Variable, f string) string { - if !fileExists(f) { - log.Warnf("unable to resolve %s. Dir %s does not exist", v, f) - return "" - } - return f -} - -func fileExists(f string) bool { - return CheckFileExists(f) == nil -} - -func CheckFileExists(path string) error { - if _, err := os.Stat(path); os.IsNotExist(err) { - return err - } - return nil -} - -func ReadProxySHA() (string, error) { - type DepsFile struct { - Name string `json:"name"` - LastStableSHA string `json:"lastStableSHA"` - } - f := filepath.Join(IstioSrc, "istio.deps") - depJSON, err := os.ReadFile(f) - if err != nil { - return "", err - } - var deps []DepsFile - if err := json.Unmarshal(depJSON, &deps); err != nil { - return "", err - } - for _, d := range deps { - if d.Name == "PROXY_REPO_SHA" { - return d.LastStableSHA, nil - } - } - return "", fmt.Errorf("PROXY_REPO_SHA not found") -} diff --git a/pkg/test/env/variable.go b/pkg/test/env/variable.go deleted file mode 100644 index d7cdaff7e..000000000 --- a/pkg/test/env/variable.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package env - -import ( - "os" -) - -// Variable is a wrapper for an environment variable. -type Variable string - -// Name of the environment variable. -func (e Variable) Name() string { - return string(e) -} - -// Value of the environment variable. -func (e Variable) Value() string { - return os.Getenv(e.Name()) -} - -// ValueOrDefault returns the value of the environment variable if it is non-empty. Otherwise returns the value provided. -func (e Variable) ValueOrDefault(defaultValue string) string { - return e.ValueOrDefaultFunc(func() string { - return defaultValue - }) -} - -// ValueOrDefaultFunc returns the value of the environment variable if it is non-empty. Otherwise returns the value function provided. -func (e Variable) ValueOrDefaultFunc(defaultValueFunc func() string) string { - if value := e.Value(); value != "" { - return value - } - return defaultValueFunc() -} diff --git a/pkg/test/envoy/binary.go b/pkg/test/envoy/binary.go deleted file mode 100644 index 1fb5a9e6d..000000000 --- a/pkg/test/envoy/binary.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package envoy - -import ( - "fmt" - "os" - "path/filepath" - "regexp" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" -) - -var envoyFileNamePattern = regexp.MustCompile("^envoy$|^envoy-[a-f0-9]+$|^envoy-debug-[a-f0-9]+$") - -// FindBinary searches for an Envoy debug binary under TARGET_OUT. If the TARGET_OUT environment variable -// is not set, the default location under GOPATH is assumed. If TARGET_OUT contains multiple debug binaries, -// the most recent file is used. -func FindBinary() (string, error) { - binPaths, err := findBinaries() - if err != nil { - return "", err - } - - if len(binPaths) == 0 { - return "", fmt.Errorf("unable to locate an Envoy binary under dir %s", env.IstioOut) - } - - latestBinPath, err := findMostRecentFile(binPaths) - if err != nil { - return "", err - } - - return latestBinPath, nil -} - -// FindBinaryOrFail calls FindBinary and fails the given test if an error occurs. -func FindBinaryOrFail(t test.Failer) string { - p, err := FindBinary() - if err != nil { - t.Fatal(err) - } - return p -} - -func findBinaries() ([]string, error) { - binPaths := make([]string, 0) - err := filepath.Walk(env.LocalOut, func(path string, f os.FileInfo, err error) error { - if err != nil { - return err - } - if !f.IsDir() && envoyFileNamePattern.MatchString(f.Name()) { - binPaths = append(binPaths, path) - } - return nil - }) - if err != nil { - return nil, err - } - return binPaths, nil -} - -func findMostRecentFile(filePaths []string) (string, error) { - latestFilePath := "" - latestFileTime := int64(0) - for _, filePath := range filePaths { - fileInfo, err := os.Stat(filePath) - if err != nil { - // Should never happen - return "", err - } - fileTime := fileInfo.ModTime().Unix() - if fileTime > latestFileTime { - latestFileTime = fileTime - latestFilePath = filePath - } - } - return latestFilePath, nil -} diff --git a/pkg/test/eventually.go b/pkg/test/eventually.go deleted file mode 100644 index 2a6930174..000000000 --- a/pkg/test/eventually.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "time" -) - -import ( - "github.com/cenkalti/backoff/v4" -) - -// A Condition is a function that returns true when a test condition is satisfied. -type Condition func() bool - -// Eventually polls cond until it completes (returns true) or times out (resulting in a test failure). -func Eventually(t Failer, name string, cond Condition) { - t.Helper() - EventualOpts{backoff.NewExponentialBackOff()}.Eventually(t, name, cond) -} - -// EventualOpts defines a polling strategy for operations that must eventually succeed. A new EventualOpts must be provided -// for each invocation of Eventually (or call Reset on a previously completed set of options). -type EventualOpts struct { - strategy *backoff.ExponentialBackOff -} - -// NewEventualOpts constructs an EventualOpts instance with the provided polling interval and deadline. EventualOpts will -// perform randomized exponential backoff using the starting interval, and will stop polling (and therefore fail) after -// deadline time as elapsed from calling Eventually. -// -// Note: we always backoff with a randomization of 0.5 (50%), a multiplier of 1.5, and a max interval of one minute. -func NewEventualOpts(interval, deadline time.Duration) *EventualOpts { - strategy := backoff.NewExponentialBackOff() - strategy.InitialInterval = interval - strategy.MaxElapsedTime = deadline - return &EventualOpts{strategy} -} - -// Eventually polls cond until it succeeds (returns true) or we exceed the deadline. Eventually performs backoff while -// polling cond. -// -// name is printed as part of the test failure message when we exceed the deadline to help identify the test case failing. -// cond does not need to be thread-safe: it is only called from the current goroutine. cond itself can also fail the test early using t.Fatal. -func (e EventualOpts) Eventually(t Failer, name string, cond Condition) { - t.Helper() - - // Check once before we start polling. - if cond() { - return - } - - // We didn't get a happy fast-path, so set up timers and wait. - // The backoff's ticker will close the channel after MaxElapsedTime, so we don't need to worry about a timeout. - poll := backoff.NewTicker(e.strategy).C - for { - _, cont := <-poll - if cond() { - return - } else if !cont { - t.Fatalf("timed out waiting for condition %q to complete", name) - } - } -} diff --git a/pkg/test/failer.go b/pkg/test/failer.go deleted file mode 100644 index 40cebbe4d..000000000 --- a/pkg/test/failer.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "errors" - "fmt" - "os" - "runtime" - "sync" - "testing" -) - -import ( - "istio.io/pkg/log" -) - -var ( - _ Failer = &testing.T{} - _ Failer = &testing.B{} - _ Failer = &errorWrapper{} -) - -// Failer is an interface to be provided to test functions of the form XXXOrFail. This is a -// substitute for testing.TB, which cannot be implemented outside of the testing -// package. -type Failer interface { - Fail() - FailNow() - Fatal(args ...interface{}) - Fatalf(format string, args ...interface{}) - Log(args ...interface{}) - Logf(format string, args ...interface{}) - TempDir() string - Helper() - Cleanup(func()) -} - -// errorWrapper is a Failer that can be used to just extract an `error`. This allows mixing -// functions that take in a Failer and those that take an error. -// The function must be called within a goroutine, or calls to Fatal will try to terminate the outer -// test context, which will cause the test to panic. The Wrap function handles this automatically -type errorWrapper struct { - mu sync.RWMutex - failed error - cleanup func() -} - -// Wrap executes a function with a fake Failer, and returns an error if the test failed. This allows -// calling functions that take a Failer and using them with functions that expect an error, or -// allowing calling functions that would cause a test to immediately fail to instead return an error. -// Wrap handles Cleanup() and short-circuiting of Fatal() just like the real testing.T. -func Wrap(f func(t Failer)) error { - done := make(chan struct{}) - w := &errorWrapper{} - go func() { - defer close(done) - f(w) - }() - <-done - return w.ToErrorCleanup() -} - -// ToErrorCleanup returns any errors encountered and executes any cleanup actions -func (e *errorWrapper) ToErrorCleanup() error { - e.mu.RLock() - defer e.mu.RUnlock() - if e.cleanup != nil { - e.cleanup() - } - return e.failed -} - -func (e *errorWrapper) Fail() { - e.Fatal("fail called") -} - -func (e *errorWrapper) FailNow() { - e.Fatal("fail now called") -} - -func (e *errorWrapper) Fatal(args ...interface{}) { - e.mu.Lock() - defer e.mu.Unlock() - if e.failed == nil { - e.failed = errors.New(fmt.Sprint(args...)) - } - runtime.Goexit() -} - -func (e *errorWrapper) Fatalf(format string, args ...interface{}) { - e.Fatal(fmt.Sprintf(format, args...)) -} - -func (e *errorWrapper) Helper() { -} - -func (e *errorWrapper) Cleanup(f func()) { - e.mu.Lock() - defer e.mu.Unlock() - oldCleanup := e.cleanup - e.cleanup = func() { - if oldCleanup != nil { - defer func() { - oldCleanup() - }() - } - f() - } -} - -func (e *errorWrapper) Log(args ...interface{}) { - log.Info(args...) -} - -func (e *errorWrapper) Logf(format string, args ...interface{}) { - ag := []interface{}{format} - ag = append(ag, args...) - log.Infof(ag...) -} - -func (e *errorWrapper) TempDir() string { - tempDir, err := os.MkdirTemp("", "test") - if err == nil { - e.Cleanup(func() { - os.RemoveAll(tempDir) - }) - } - return tempDir -} diff --git a/pkg/test/failer_test.go b/pkg/test/failer_test.go deleted file mode 100644 index 3a6161fda..000000000 --- a/pkg/test/failer_test.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "testing" -) - -func TestWrapper(t *testing.T) { - t.Run("fail", func(t *testing.T) { - if err := Wrap(func(t Failer) { - t.Fatalf("failed") - }); err == nil { - t.Fatalf("expected error, got none") - } - }) - t.Run("success", func(t *testing.T) { - if err := Wrap(func(t Failer) {}); err != nil { - t.Fatalf("expected no error, got: %v", err) - } - }) - t.Run("cleanup", func(t *testing.T) { - done := false - if err := Wrap(func(t Failer) { - t.Cleanup(func() { - done = true - }) - }); err != nil { - t.Fatalf("expected no error, got: %v", err) - } - if !done { - t.Fatalf("cleanup not triggered") - } - }) -} diff --git a/pkg/test/fakes/gce_metadata_server/Dockerfile b/pkg/test/fakes/gce_metadata_server/Dockerfile deleted file mode 100644 index dac59c6d8..000000000 --- a/pkg/test/fakes/gce_metadata_server/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -FROM scratch -COPY ./main /gce-metadata-server -EXPOSE 8080 -CMD ["/gce-metadata-server"] diff --git a/pkg/test/fakes/gce_metadata_server/Makefile b/pkg/test/fakes/gce_metadata_server/Makefile deleted file mode 100644 index 5a606d881..000000000 --- a/pkg/test/fakes/gce_metadata_server/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -.PHONY: build_and_push clean all - -MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) -MD_PATH := $(dir $(MKFILE_PATH)) -IMG := gcr.io/istio-testing/fake-gce-metadata - -# NOTE: TAG should be updated whenever changes are made in this directory -# This should also be updated in dependent components -TAG := 1.0 - -all: build_and_push clean - -build_and_push: - cd $(MD_PATH) && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -tags netgo -ldflags '-w -extldflags "-static"' main.go - docker buildx build $(MD_PATH) -t $(IMG):$(TAG) --push - -clean: - rm $(MD_PATH)/main diff --git a/pkg/test/fakes/gce_metadata_server/main.go b/pkg/test/fakes/gce_metadata_server/main.go deleted file mode 100644 index 5d905db8d..000000000 --- a/pkg/test/fakes/gce_metadata_server/main.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "context" - "fmt" - "log" - "net/http" - "os" - "os/signal" - "syscall" -) - -import ( - "github.com/gorilla/mux" -) - -const ( - addr = ":8080" - - projID = "test-project" - projNumber = "123456789" - instance = "test-instance" - instID = "987654321" - zone = "us-west1-c" - - metaPrefix = "/computeMetadata/v1" - projIDPath = metaPrefix + "/project/project-id" - projNumberPath = metaPrefix + "/project/numeric-project-id" - instIDPath = metaPrefix + "/instance/id" - instancePath = metaPrefix + "/instance/name" - zonePath = metaPrefix + "/instance/zone" - attrKey = "attribute" - attrPath = metaPrefix + "/instance/attributes/{" + attrKey + "}" -) - -var instAttrs = map[string]string{ - "instance-template": "some-template", - "created-by": "some-creator", -} - -func checkMetadataHeaders(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - log.Println("request for: " + r.URL.Path) - w.Header().Add("Server", "Metadata Server for VM (Fake)") - w.Header().Add("Metadata-Flavor", "Google") - - flavor := r.Header.Get("Metadata-Flavor") - if flavor == "" && r.RequestURI != "/" { - http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden) - w.Header().Set("Content-Type", "text/html; charset=UTF-8") - log.Println("forbidden: " + r.URL.Path) - return - } - - next.ServeHTTP(w, r) - }) -} - -func handleAttrs(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - - if val, ok := instAttrs[vars[attrKey]]; ok { - fmt.Fprint(w, val) - return - } - - http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) -} - -func main() { - r := mux.NewRouter() - r.Use(checkMetadataHeaders) - r.HandleFunc(projIDPath, func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, projID) }).Methods("GET") - r.HandleFunc(projNumberPath, func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, projNumber) }).Methods("GET") - r.HandleFunc(instIDPath, func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, instID) }).Methods("GET") - r.HandleFunc(instancePath, func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, instance) }).Methods("GET") - r.HandleFunc(zonePath, func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, zone) }).Methods("GET") - r.HandleFunc(attrPath, handleAttrs).Methods("GET") - http.Handle("/", r) - - srv := &http.Server{Addr: addr} - - done := make(chan os.Signal, 1) - signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) - - go func() { - if err := srv.ListenAndServe(); err != http.ErrServerClosed { - log.Fatalf("listen: %v\n", err) - } - }() - - log.Println("GCE metadata server started (" + addr + ")") - <-done - log.Println("GCE metadata server stopped.") - - if err := srv.Shutdown(context.Background()); err != nil { - log.Fatalf("GCE Metadata Shutdown Failed: %+v", err) - } -} diff --git a/pkg/test/fakes/imageregistry/Dockerfile b/pkg/test/fakes/imageregistry/Dockerfile deleted file mode 100644 index 86e155a00..000000000 --- a/pkg/test/fakes/imageregistry/Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM scratch -COPY ./main /registry -CMD ["/registry"] diff --git a/pkg/test/fakes/imageregistry/Makefile b/pkg/test/fakes/imageregistry/Makefile deleted file mode 100644 index c038a3cab..000000000 --- a/pkg/test/fakes/imageregistry/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -.PHONY: build_and_push clean all - -MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) -MD_PATH := $(dir $(MKFILE_PATH)) -IMG := gcr.io/istio-testing/fake-registry - -# NOTE: TAG should be updated whenever changes are made in this directory -# This should also be updated in dependent components -TAG := 1.0 - -all: build_and_push clean - -build_and_push: - cd $(MD_PATH) && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -tags netgo -ldflags '-w -extldflags "-static"' main.go - docker buildx build $(MD_PATH) -t $(IMG):$(TAG) --push - -clean: - rm $(MD_PATH)/main diff --git a/pkg/test/fakes/imageregistry/main b/pkg/test/fakes/imageregistry/main deleted file mode 100755 index a801304d2..000000000 Binary files a/pkg/test/fakes/imageregistry/main and /dev/null differ diff --git a/pkg/test/fakes/imageregistry/main.go b/pkg/test/fakes/imageregistry/main.go deleted file mode 100644 index a3778efa5..000000000 --- a/pkg/test/fakes/imageregistry/main.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "encoding/base64" - "flag" - "fmt" - "net/http" -) - -import ( - "istio.io/pkg/log" -) - -var ( - port = flag.Int("port", 1338, "port to run registry on") - registry = flag.String("registry", "gcr.io", "name of registry to redirect registry request to") -) - -const ( - User = "user" - Passwd = "passwd" -) - -type Handler struct{} - -func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/ready" { - w.WriteHeader(http.StatusOK) - return - } - encoded := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%v:%v", User, Passwd))) - authHdr := r.Header.Get("Authorization") - wantHdr := fmt.Sprintf("Basic %s", encoded) - if authHdr != wantHdr { - log.Infof("Unauthorized: " + r.URL.Path) - log.Infof("Got header %q want header %q", authHdr, wantHdr) - w.Header().Set("WWW-Authenticate", "Basic") - http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) - return - } - rurl := fmt.Sprintf("https://%v%v", *registry, r.URL.Path) - log.Infof("Get %q, send redirect to %q", r.URL, rurl) - http.Redirect(w, r, rurl, http.StatusMovedPermanently) -} - -func main() { - flag.Parse() - s := &http.Server{ - Addr: fmt.Sprintf(":%d", *port), - Handler: &Handler{}, - } - if err := s.ListenAndServe(); err != nil { - log.Error(err) - } -} diff --git a/pkg/test/framework/README.md b/pkg/test/framework/README.md deleted file mode 100644 index 9a1db859c..000000000 --- a/pkg/test/framework/README.md +++ /dev/null @@ -1,2 +0,0 @@ -Please see [this wiki page](https://github.com/istio/istio/wiki/Istio-Test-Framework) for info on using -the test framework. diff --git a/pkg/test/framework/analyzer.go b/pkg/test/framework/analyzer.go deleted file mode 100644 index 55f858144..000000000 --- a/pkg/test/framework/analyzer.go +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package framework - -import ( - "fmt" - "strings" - "testing" -) - -import ( - "github.com/hashicorp/go-multierror" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/features" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/label" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" -) - -type commonAnalyzer struct { - labels label.Set - minCusters, maxClusters int - minIstioVersion, skipMessage string -} - -func newCommonAnalyzer() commonAnalyzer { - return commonAnalyzer{ - labels: label.NewSet(), - skipMessage: "", - minIstioVersion: "", - minCusters: 1, - maxClusters: -1, - } -} - -type suiteAnalyzer struct { - testID string - mRun mRunFn - osExit func(int) - - commonAnalyzer - envFactoryCalls int -} - -func newSuiteAnalyzer(testID string, fn mRunFn, osExit func(int)) Suite { - return &suiteAnalyzer{ - testID: testID, - mRun: fn, - osExit: osExit, - commonAnalyzer: newCommonAnalyzer(), - } -} - -func (s *suiteAnalyzer) EnvironmentFactory(fn resource.EnvironmentFactory) Suite { - if s.envFactoryCalls > 0 { - scopes.Framework.Warn("EnvironmentFactory overridden multiple times for Suite") - } - s.envFactoryCalls++ - return s -} - -func (s *suiteAnalyzer) Label(labels ...label.Instance) Suite { - s.labels = s.labels.Add(labels...) - return s -} - -func (s *suiteAnalyzer) Skip(reason string) Suite { - s.skipMessage = reason - return s -} - -func (s *suiteAnalyzer) SkipIf(reason string, fn resource.ShouldSkipFn) Suite { - s.skipMessage = reason - return s -} - -func (s *suiteAnalyzer) RequireMinClusters(minClusters int) Suite { - if minClusters <= 0 { - minClusters = 1 - } - s.minCusters = minClusters - return s -} - -func (s *suiteAnalyzer) RequireMaxClusters(maxClusters int) Suite { - s.maxClusters = maxClusters - return s -} - -func (s *suiteAnalyzer) RequireSingleCluster() Suite { - return s.RequireMinClusters(1).RequireMaxClusters(1) -} - -func (s *suiteAnalyzer) RequireMultiPrimary() Suite { - return s -} - -func (s *suiteAnalyzer) RequireMinVersion(minorVersion uint) Suite { - return s -} - -func (s *suiteAnalyzer) RequireMaxVersion(minorVersion uint) Suite { - return s -} - -func (s *suiteAnalyzer) Setup(fn resource.SetupFn) Suite { - // TODO track setup fns? - return s -} - -func (s *suiteAnalyzer) Run() { - s.osExit(s.run()) -} - -func (s *suiteAnalyzer) run() int { - initAnalysis(s.track()) - defer finishAnalysis() - scopes.Framework.Infof("=== Begin: Analysis of %s ===", analysis.SuiteID) - - // tests will add their results to the suiteAnalysis during mRun - return s.mRun(nil) -} - -// track generates the final analysis for this suite. track should not be called if more -// modification will happen to this suiteAnalyzer. -func (s *suiteAnalyzer) track() *suiteAnalysis { - return &suiteAnalysis{ - SuiteID: s.testID, - SkipReason: s.skipMessage, - Labels: s.labels.All(), - MultiCluster: s.maxClusters != 1, - MultiClusterOnly: s.minCusters > 1, - Tests: map[string]*testAnalysis{}, - } -} - -func newTestAnalyzer(t *testing.T) Test { - return &testAnalyzer{ - commonAnalyzer: newCommonAnalyzer(), - goTest: t, - featureLabels: map[features.Feature][]string{}, - } -} - -type testAnalyzer struct { - goTest *testing.T - - commonAnalyzer - notImplemented bool - featureLabels map[features.Feature][]string - hasRun bool -} - -func (t *testAnalyzer) Label(labels ...label.Instance) Test { - t.labels = t.labels.Add(labels...) - return t -} - -func (t *testAnalyzer) Features(feats ...features.Feature) Test { - if err := addFeatureLabels(t.featureLabels, feats...); err != nil { - log.Errorf(err) - t.goTest.FailNow() - } - return t -} - -func addFeatureLabels(featureLabels map[features.Feature][]string, feats ...features.Feature) error { - c, err := features.BuildChecker(env.IstioSrc + "/pkg/test/framework/features/features.yaml") - if err != nil { - return fmt.Errorf("unable to build feature checker: %v", err) - } - - err = nil - for _, f := range feats { - check, scenario := c.Check(f) - if !check { - err = multierror.Append(err, fmt.Errorf("feature %s is not a leaf in /pkg/test/framework/features/features.yaml", f)) - continue - } - // feats actually contains feature and scenario. split them here. - onlyFeature := features.Feature(strings.Replace(string(f), scenario, "", 1)) - featureLabels[onlyFeature] = append(featureLabels[onlyFeature], scenario) - } - return err -} - -func (t *testAnalyzer) NotImplementedYet(features ...features.Feature) Test { - t.notImplemented = true - t.Features(features...) - return t -} - -func (t *testAnalyzer) RequiresMinClusters(minClusters int) Test { - t.minCusters = minClusters - return t -} - -func (t *testAnalyzer) RequiresMaxClusters(maxClusters int) Test { - t.maxClusters = maxClusters - return t -} - -func (t *testAnalyzer) RequireIstioVersion(version string) Test { - t.minIstioVersion = version - return t -} - -func (t *testAnalyzer) RequiresSingleCluster() Test { - t.RequiresMinClusters(1) - t.RequiresMaxClusters(1) - return t -} - -func (t *testAnalyzer) RequiresLocalControlPlane() Test { - return t -} - -func (t *testAnalyzer) RequiresSingleNetwork() Test { - return t -} - -func (t *testAnalyzer) Run(_ func(ctx TestContext)) { - defer t.track() - if t.hasRun { - t.goTest.Fatalf("multiple Run calls for %s", t.goTest.Name()) - } - t.hasRun = true - - // don't fail tests that would otherwise be skipped - if analysis.SkipReason != "" || t.skipMessage != "" { - return - } - - // TODO: should we also block new cases? - if len(t.featureLabels) < 1 && !features.GlobalAllowlist.Contains(analysis.SuiteID, t.goTest.Name()) { - t.goTest.Fatalf("Detected new test %s in suite %s with no feature labels. "+ - "See istio/istio/pkg/test/framework/features/README.md", t.goTest.Name(), analysis.SuiteID) - } -} - -func (t *testAnalyzer) RunParallel(fn func(ctx TestContext)) { - t.Run(fn) -} - -func (t *testAnalyzer) track() { - analysis.addTest(t.goTest.Name(), &testAnalysis{ - SkipReason: t.skipMessage, - Labels: t.labels.All(), // TODO should this be merged with suite labels? - Features: t.featureLabels, - Invalid: t.goTest.Failed(), - MultiCluster: t.maxClusters != 1 && analysis.MultiCluster, - MultiClusterOnly: t.minCusters > 1 || analysis.MultiClusterOnly, - NotImplemented: t.notImplemented, - }) -} diff --git a/pkg/test/framework/analyzer_runtime.go b/pkg/test/framework/analyzer_runtime.go deleted file mode 100644 index fcf3ed73c..000000000 --- a/pkg/test/framework/analyzer_runtime.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package framework - -import ( - "flag" - "fmt" - "os" - "path" - "sync" -) - -import ( - "gopkg.in/yaml.v2" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/config" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/features" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/label" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" -) - -var ( - analyzeMode bool - analysis *suiteAnalysis - analysisMu sync.Mutex -) - -func init() { - flag.BoolVar(&analyzeMode, "istio.test.analyze", os.Getenv("ANALYZE_TESTS") != "", "Analyzes tests without actually running them") -} - -func analyze() bool { - if !config.Parsed() { - config.Parse() - } - return analyzeMode -} - -// initAnalysis sets up analysis for a single suite. If an analysis is already running, -// it will block until finishAnalysis is called. -func initAnalysis(a *suiteAnalysis) { - analysisMu.Lock() - analysis = a -} - -// finishAnalysis marks the analysis for a suite as done and dumps the results. -func finishAnalysis() { - scopes.Framework.Infof("=== DONE: Analysis of %s ===", analysis.SuiteID) - - dumpAnalysis() - analysis = nil - analysisMu.Unlock() -} - -func dumpAnalysis() { - s, err := getSettings(analysis.SuiteID) - if err != nil { - scopes.Framework.Errorf("failed to get settings for %s: %v", analysis.SuiteID, err) - } - if err := os.MkdirAll(s.RunDir(), os.ModePerm); err != nil { - scopes.Framework.Errorf("failed to create analysis directory for %s: %v", analysis.SuiteID, err) - return - } - - marshaled, err := yaml.Marshal(analysis) - if err != nil { - scopes.Framework.Errorf("failed to marshaled analysis for %s: %v", analysis.SuiteID, err) - return - } - scopes.Framework.Info("\n" + string(marshaled)) - - outPath := path.Join(s.RunDir(), fmt.Sprintf("%s_analysis.yaml", analysis.SuiteID)) - if err := os.WriteFile(outPath, marshaled, 0o666); err != nil { - scopes.Framework.Errorf("failed writing analysis to file for %s: %v", analysis.SuiteID, err) - return - } - scopes.Framework.Infof("Wrote analysis to %s", outPath) -} - -// suiteAnalysis captures the results of analyzing a Suite -type suiteAnalysis struct { - SuiteID string `yaml:"suiteID"` - SkipReason string `yaml:"skipReason,omitempty"` - Labels []label.Instance `yaml:"labels,omitempty"` - MultiCluster bool `yaml:"multicluster,omitempty"` - MultiClusterOnly bool `yaml:"multiclusterOnly,omitempty"` - Tests map[string]*testAnalysis `yaml:"tests"` -} - -func (s *suiteAnalysis) addTest(id string, test *testAnalysis) { - s.Tests[id] = test -} - -// suiteAnalysis captures the results of analyzing a Test -type testAnalysis struct { - SkipReason string `yaml:"skipReason,omitempty"` - Labels []label.Instance `yaml:"labels,omitempty"` - Features map[features.Feature][]string `yaml:"features,omitempty"` - Invalid bool `yaml:"invalid,omitempty"` - MultiCluster bool `yaml:"multicluster,omitempty"` - MultiClusterOnly bool `yaml:"multiclusterOnly,omitempty"` - NotImplemented bool `yaml:"notImplemented,omitempty"` -} diff --git a/pkg/test/framework/components/cluster/cluster.go b/pkg/test/framework/components/cluster/cluster.go deleted file mode 100644 index 910772af8..000000000 --- a/pkg/test/framework/components/cluster/cluster.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cluster - -import ( - "fmt" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -// Cluster in a multicluster environment. -type Cluster interface { - fmt.Stringer - kube.ExtendedClient - - // Name of this cluster. Use for interacting with the cluster or validation against clusters. - // Use StableName instead of Name when creating subtests. - Name() string - - // StableName gives a deterministic name for the cluster. Use this for test/subtest names to - // allow test grid to compare runs, even when the underlying cluster names are dynamic. - // Use Name for validation/interaction with the actual cluster. - StableName() string - - // Kind of cluster - Kind() Kind - - // NetworkName the cluster is on - NetworkName() string - - // MinKubeVersion returns true if the cluster is at least the version specified, - // false otherwise - MinKubeVersion(minor uint) bool - - // MaxKubeVersion returns true if the cluster is at most the version specified, - // false otherwise - MaxKubeVersion(minor uint) bool - - // IsPrimary returns true if this is a primary cluster, containing an instance - // of the Istio control plane. - IsPrimary() bool - - // IsConfig returns true if this is a config cluster, used as the source of - // Istio config for one or more control planes. - IsConfig() bool - - // IsRemote returns true if this is a remote cluster, which uses a control plane - // residing in another cluster. - IsRemote() bool - - // IsExternalControlPlane returns true if this is a cluster containing an instance - // of the Istio control plane but with its source of config in another cluster. - IsExternalControlPlane() bool - - // Primary returns the primary cluster for this cluster. Will return itself if - // IsPrimary. - Primary() Cluster - - // PrimaryName returns the name of the primary cluster for this cluster. - PrimaryName() string - - // Config returns the config cluster for this cluster. Will return itself if - // IsConfig. - Config() Cluster - - // ConfigName returns the name of the config cluster for this cluster. - ConfigName() string - - // HTTPProxy returns the HTTP proxy config to connect to the cluster - HTTPProxy() string -} diff --git a/pkg/test/framework/components/cluster/clusterboot/factory.go b/pkg/test/framework/components/cluster/clusterboot/factory.go deleted file mode 100644 index b2cb9778c..000000000 --- a/pkg/test/framework/components/cluster/clusterboot/factory.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package clusterboot - -import ( - "fmt" -) - -import ( - "github.com/hashicorp/go-multierror" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - _ "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster/kube" - _ "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster/staticvm" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/config" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" -) - -var _ cluster.Factory = factory{} - -func NewFactory() cluster.Factory { - return factory{} -} - -type factory struct { - configs []cluster.Config -} - -func (a factory) Kind() cluster.Kind { - return cluster.Aggregate -} - -func (a factory) With(configs ...cluster.Config) cluster.Factory { - return factory{configs: append(a.configs, configs...)} -} - -func (a factory) Build() (cluster.Clusters, error) { - scopes.Framework.Infof("=== BEGIN: Building clusters ===") - - // use multierror to give as much detail as possible if the config is bad - var errs error - defer func() { - if errs != nil { - scopes.Framework.Infof("=== FAILED: Building clusters ===") - } - }() - - allClusters := make(cluster.Map) - var clusters cluster.Clusters - for i, cfg := range a.configs { - c, err := buildCluster(cfg, allClusters) - if err != nil { - errs = multierror.Append(errs, fmt.Errorf("failed building cluster from config %d: %v", i, err)) - continue - } - if _, ok := allClusters[c.Name()]; ok { - errs = multierror.Append(errs, fmt.Errorf("more than one cluster named %s", c.Name())) - continue - } - allClusters[c.Name()] = c - clusters = append(clusters, c) - } - if errs != nil { - return nil, errs - } - - // validate the topology has no open edges - for _, c := range allClusters { - if primary, ok := allClusters[c.PrimaryName()]; !ok { - errs = multierror.Append(errs, fmt.Errorf("primary %s for %s is not in the topology", c.PrimaryName(), c.Name())) - continue - } else if !validPrimaryOrConfig(primary) { - errs = multierror.Append(errs, fmt.Errorf("primary %s for %s is of kind %s, primaries must be Kubernetes", primary.Name(), c.Name(), primary.Kind())) - continue - } - if cfg, ok := allClusters[c.ConfigName()]; !ok { - errs = multierror.Append(errs, fmt.Errorf("config %s for %s is not in the topology", c.ConfigName(), c.Name())) - continue - } else if !validPrimaryOrConfig(cfg) { - errs = multierror.Append(errs, fmt.Errorf("config %s for %s is of kind %s, primaries must be Kubernetes", cfg.Name(), c.Name(), cfg.Kind())) - continue - } - } - if errs != nil { - return nil, errs - } - - for _, c := range clusters { - scopes.Framework.Infof("Built Cluster:\n%s", c.String()) - } - - scopes.Framework.Infof("=== DONE: Building clusters ===") - return clusters, errs -} - -func validPrimaryOrConfig(c cluster.Cluster) bool { - return c.Kind() == cluster.Kubernetes || c.Kind() == cluster.Fake -} - -func buildCluster(cfg cluster.Config, allClusters cluster.Map) (cluster.Cluster, error) { - cfg, err := validConfig(cfg) - if err != nil { - return nil, err - } - f, err := cluster.GetFactory(cfg) - if err != nil { - return nil, err - } - return f(cfg, cluster.NewTopology(cfg, allClusters)) -} - -func validConfig(cfg cluster.Config) (cluster.Config, error) { - if cfg.Name == "" { - return cfg, fmt.Errorf("empty cluster name") - } - if cfg.Kind == "" { - return cfg, fmt.Errorf("unspecified Kind for %s", cfg.Name) - } - if cfg.PrimaryClusterName == "" { - cfg.PrimaryClusterName = cfg.Name - } - if cfg.ConfigClusterName == "" { - cfg.ConfigClusterName = cfg.PrimaryClusterName - } - if cfg.Meta == nil { - cfg.Meta = config.Map{} - } - return cfg, nil -} diff --git a/pkg/test/framework/components/cluster/clusterboot/factory_test.go b/pkg/test/framework/components/cluster/clusterboot/factory_test.go deleted file mode 100644 index be5616821..000000000 --- a/pkg/test/framework/components/cluster/clusterboot/factory_test.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package clusterboot - -import ( - "testing" -) - -import ( - "github.com/google/go-cmp/cmp" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/config" -) - -func TestBuild(t *testing.T) { - tests := []struct { - config cluster.Config - cluster cluster.FakeCluster - }{ - { - config: cluster.Config{Kind: cluster.Fake, Name: "auto-fill-primary", Network: "network-0"}, - cluster: cluster.FakeCluster{ - ExtendedClient: kube.MockClient{}, - Topology: cluster.Topology{ - ClusterName: "auto-fill-primary", - ClusterKind: cluster.Fake, - // The primary and config clusters should match the cluster name when not specified - PrimaryClusterName: "auto-fill-primary", - ConfigClusterName: "auto-fill-primary", - Network: "network-0", - }, - }, - }, - { - config: cluster.Config{Kind: cluster.Fake, Name: "auto-fill-remote", PrimaryClusterName: "auto-fill-primary"}, - cluster: cluster.FakeCluster{ - ExtendedClient: kube.MockClient{}, - Topology: cluster.Topology{ - ClusterName: "auto-fill-remote", - ClusterKind: cluster.Fake, - PrimaryClusterName: "auto-fill-primary", - // The config cluster should match the primary cluster when not specified - ConfigClusterName: "auto-fill-primary", - Index: 1, - }, - }, - }, - { - config: cluster.Config{Kind: cluster.Fake, Name: "external-istiod", ConfigClusterName: "remote-config"}, - cluster: cluster.FakeCluster{ - ExtendedClient: kube.MockClient{}, - Topology: cluster.Topology{ - ClusterName: "external-istiod", - ClusterKind: cluster.Fake, - PrimaryClusterName: "external-istiod", - ConfigClusterName: "remote-config", - Index: 2, - }, - }, - }, - { - config: cluster.Config{ - Name: "remote-config", - Kind: cluster.Fake, - PrimaryClusterName: "external-istiod", - ConfigClusterName: "remote-config", - }, - cluster: cluster.FakeCluster{ - ExtendedClient: kube.MockClient{}, - Topology: cluster.Topology{ - ClusterName: "remote-config", - ClusterKind: cluster.Fake, - // Explicitly specified in config, should be copied exactly - PrimaryClusterName: "external-istiod", - ConfigClusterName: "remote-config", - Index: 3, - }, - }, - }, - } - var clusters cluster.Clusters - t.Run("build", func(t *testing.T) { - factory := NewFactory() - for _, tc := range tests { - factory = factory.With(tc.config) - } - var err error - clusters, err = factory.Build() - if err != nil { - t.Fatal(err) - } - if len(clusters) != len(tests) { - t.Errorf("expcted %d clusters but built %d", len(tests), len(clusters)) - } - }) - for _, tc := range tests { - t.Run("built "+tc.config.Name, func(t *testing.T) { - built := *clusters.GetByName(tc.config.Name).(*cluster.FakeCluster) - // don't compare these - built.AllClusters = nil - built.Version = nil - if diff := cmp.Diff(built, tc.cluster); diff != "" { - t.Fatal(diff) - } - }) - } -} - -func TestValidation(t *testing.T) { - tests := map[string][]cluster.Config{ - "empty kind": { - {Name: "no-kind"}, - }, - "empty name": { - {Kind: cluster.Fake}, - }, - "duplicate name": { - {Kind: cluster.Fake, Name: "dupe"}, - {Kind: cluster.Fake, Name: "dupe"}, - }, - "non-existent primary": { - {Kind: cluster.Fake, Name: "no-primary", PrimaryClusterName: "does-not-exist"}, - }, - "non-existent config": { - {Kind: cluster.Fake, Name: "no-primary", ConfigClusterName: "does-not-exist"}, - }, - "vm without kube primary": { - {Kind: cluster.StaticVM, Name: "vm", Meta: config.Map{"deployments": []interface{}{ - config.Map{ - "service": "vm", "namespace": "echo", "instances": []config.Map{{"ip": "1.2.3.4"}}, - }, - }}}, - }, - } - - for name, tc := range tests { - t.Run(name, func(t *testing.T) { - _, err := NewFactory().With(tc...).Build() - if err == nil { - t.Fatal("expected err but got nil") - } - }) - } -} diff --git a/pkg/test/framework/components/cluster/clusters.go b/pkg/test/framework/components/cluster/clusters.go deleted file mode 100644 index 74f52e073..000000000 --- a/pkg/test/framework/components/cluster/clusters.go +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cluster - -import ( - "fmt" - "sort" -) - -// Clusters is an ordered list of Cluster instances. -type Clusters []Cluster - -func (c Clusters) Len() int { - return len(c) -} - -// IsMulticluster is a utility method that indicates whether there are multiple Clusters available. -func (c Clusters) IsMulticluster() bool { - return c.Len() > 1 -} - -// Default returns the first cluster in the list. -func (c Clusters) Default() Cluster { - return c[0] -} - -// GetOrDefault returns the given cluster if non-nil. Otherwise returns the first -// Cluster in the list. -func (c Clusters) GetOrDefault(cluster Cluster) Cluster { - if cluster != nil { - return cluster - } - return c.Default() -} - -// GetByName returns the Cluster with the given name or nil if it is not in the list. -func (c Clusters) GetByName(name string) Cluster { - for _, cc := range c { - if cc.Name() == name { - return cc - } - } - return nil -} - -// Names returns the deduped list of names of the clusters. -func (c Clusters) Names() []string { - dedup := map[string]struct{}{} - for _, cc := range c { - dedup[cc.Name()] = struct{}{} - } - var names []string - for n := range dedup { - names = append(names, n) - } - return names -} - -type ClustersByNetwork map[string]Clusters - -func (c ClustersByNetwork) Networks() []string { - out := make([]string, 0, len(c)) - for n := range c { - out = append(out, n) - } - sort.Strings(out) - return out -} - -// ByNetwork returns a map of network name to a subset of clusters -func (c Clusters) ByNetwork() ClustersByNetwork { - out := make(ClustersByNetwork) - for _, cc := range c { - out[cc.NetworkName()] = append(out[cc.NetworkName()], cc) - } - return out -} - -// Networks returns the list of network names for the clusters. -func (c Clusters) Networks() []string { - return c.ByNetwork().Networks() -} - -// Primaries returns the subset that are primary clusters. -func (c Clusters) Primaries(excluded ...Cluster) Clusters { - return c.filterClusters(func(cc Cluster) bool { - return cc.IsPrimary() - }, exclude(excluded...)) -} - -// Exclude returns all clusters not given as input. -func (c Clusters) Exclude(excluded ...Cluster) Clusters { - return c.filterClusters(func(cc Cluster) bool { - return true - }, exclude(excluded...)) -} - -// Configs returns the subset that are config clusters. -func (c Clusters) Configs(excluded ...Cluster) Clusters { - return c.filterClusters(func(cc Cluster) bool { - return cc.IsConfig() - }, exclude(excluded...)) -} - -// Remotes returns the subset that are remote clusters. -func (c Clusters) Remotes(excluded ...Cluster) Clusters { - return c.filterClusters(func(cc Cluster) bool { - return cc.IsRemote() - }, exclude(excluded...)) -} - -// MeshClusters returns the subset that are not external control plane clusters. -func (c Clusters) MeshClusters(excluded ...Cluster) Clusters { - return c.filterClusters(func(cc Cluster) bool { - return !cc.IsExternalControlPlane() - }, exclude(excluded...)) -} - -// Kube returns OfKind(cluster.Kubernetes) -func (c Clusters) Kube() Clusters { - return c.OfKind(Kubernetes) -} - -// OfKind filters clusters by their Kind. -func (c Clusters) OfKind(kind Kind) Clusters { - return c.filterClusters(func(cc Cluster) bool { - return cc.Kind() == kind - }, none) -} - -func none(Cluster) bool { - return false -} - -func exclude(exclude ...Cluster) func(Cluster) bool { - return func(cc Cluster) bool { - for _, e := range exclude { - if cc.Name() == e.Name() { - return true - } - } - return false - } -} - -func (c Clusters) filterClusters(included func(Cluster) bool, - excluded func(Cluster) bool) Clusters { - var out Clusters - for _, cc := range c { - if !excluded(cc) && included(cc) { - out = append(out, cc) - } - } - return out -} - -func (c Clusters) String() string { - return fmt.Sprintf("%v", c.Names()) -} diff --git a/pkg/test/framework/components/cluster/config.go b/pkg/test/framework/components/cluster/config.go deleted file mode 100644 index a31a2699a..000000000 --- a/pkg/test/framework/components/cluster/config.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cluster - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/config" -) - -type Kind string - -const ( - Kubernetes Kind = "Kubernetes" - Fake Kind = "Fake" - Aggregate Kind = "Aggregate" - StaticVM Kind = "StaticVM" - Unknown Kind = "Unknown" -) - -type Config struct { - Kind Kind `yaml:"kind,omitempty"` - Name string `yaml:"clusterName,omitempty"` - Network string `yaml:"network,omitempty"` - HTTPProxy string `yaml:"httpProxy,omitempty"` - PrimaryClusterName string `yaml:"primaryClusterName,omitempty"` - ConfigClusterName string `yaml:"configClusterName,omitempty"` - Meta config.Map `yaml:"meta,omitempty"` -} diff --git a/pkg/test/framework/components/cluster/factory.go b/pkg/test/framework/components/cluster/factory.go deleted file mode 100644 index c73114402..000000000 --- a/pkg/test/framework/components/cluster/factory.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cluster - -import ( - "fmt" -) - -type Factory interface { - Kind() Kind - With(config ...Config) Factory - Build() (Clusters, error) -} - -// FactoryFunc validates a config and builds a single echo instance. -type FactoryFunc func(cfg Config, topology Topology) (Cluster, error) - -var factoryRegistry = map[Kind]FactoryFunc{} - -// RegisterFactory globally registers a base factory of a given Kind. -// The given factory should be immutable, as it will be used globally. -func RegisterFactory(kind Kind, factory FactoryFunc) { - factoryRegistry[kind] = factory -} - -func GetFactory(config Config) (FactoryFunc, error) { - f, ok := factoryRegistry[config.Kind] - if !ok { - return nil, fmt.Errorf("unsupported cluster kind %s", config.Kind) - } - return f, nil -} diff --git a/pkg/test/framework/components/cluster/fake.go b/pkg/test/framework/components/cluster/fake.go deleted file mode 100644 index 71fb59cab..000000000 --- a/pkg/test/framework/components/cluster/fake.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cluster - -import ( - "k8s.io/apimachinery/pkg/version" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -var _ Cluster = FakeCluster{} - -func init() { - RegisterFactory(Fake, newFakeCluster) -} - -func NewFake(name, major, minor string) Cluster { - c, _ := newFakeCluster( - Config{ - Meta: map[string]interface{}{ - "majorVersion": major, - "minorVersion": minor, - }, - }, - Topology{ - ClusterKind: Fake, - ClusterName: name, - AllClusters: map[string]Cluster{}, - }, - ) - c.(*FakeCluster).Topology.AllClusters[c.Name()] = c - return c -} - -func newFakeCluster(cfg Config, topology Topology) (Cluster, error) { - return &FakeCluster{ - ExtendedClient: kube.MockClient{}, - Topology: topology, - Version: &version.Info{ - Major: cfg.Meta.String("majorVesion"), - Minor: cfg.Meta.String("minorVersion"), - }, - }, nil -} - -// FakeCluster used for testing. -type FakeCluster struct { - kube.ExtendedClient - Version *version.Info - Topology -} - -func (f FakeCluster) GetKubernetesVersion() (*version.Info, error) { - return f.Version, nil -} diff --git a/pkg/test/framework/components/cluster/kube/cluster.go b/pkg/test/framework/components/cluster/kube/cluster.go deleted file mode 100644 index 197938e8c..000000000 --- a/pkg/test/framework/components/cluster/kube/cluster.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "bytes" - "fmt" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" -) - -var _ echo.Cluster = &Cluster{} - -// Cluster for a Kubernetes cluster. Provides access via a kube.Client. -type Cluster struct { - // filename is the path to the kubeconfig file for this cluster. - filename string - - // vmSupport indicates the cluster is being used for fake VMs - vmSupport bool - - // ExtendedClient is embedded to interact with the kube cluster. - kube.ExtendedClient - - // Topology is embedded to include common functionality. - cluster.Topology -} - -// CanDeploy for a kube cluster returns true if the config is a non-vm, or if the cluster supports -// fake pod-based VMs. -func (c *Cluster) CanDeploy(config echo.Config) (echo.Config, bool) { - if config.DeployAsVM && !c.vmSupport { - return echo.Config{}, false - } - return config, true -} - -// OverrideTopology allows customizing the relationship between this and other clusters -// for a single suite. This practice is discouraged, and separate test jobs should be created -// on a per-topology bassis. -// TODO remove this when centralistiod test is isolated as it's own job -func (c *Cluster) OverrideTopology(fn func(cluster.Topology) cluster.Topology) { - c.Topology = fn(c.Topology) -} - -func (c *Cluster) String() string { - buf := &bytes.Buffer{} - - _, _ = fmt.Fprint(buf, c.Topology.String()) - _, _ = fmt.Fprintf(buf, "Filename: %s\n", c.filename) - - return buf.String() -} - -// Filename of the kubeconfig file for this cluster. -// TODO(nmittler): Remove the need for this by changing operator to use provided kube clients directly. -func (c Cluster) Filename() string { - return c.filename -} diff --git a/pkg/test/framework/components/cluster/kube/factory.go b/pkg/test/framework/components/cluster/kube/factory.go deleted file mode 100644 index 16273ed75..000000000 --- a/pkg/test/framework/components/cluster/kube/factory.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "fmt" - "net/http" - "net/url" -) - -import ( - "k8s.io/client-go/rest" -) - -import ( - istioKube "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/file" -) - -const ( - kubeconfigMetaKey = "kubeconfig" - vmSupportMetaKey = "fakeVM" -) - -func init() { - cluster.RegisterFactory(cluster.Kubernetes, buildKube) -} - -func buildKube(origCfg cluster.Config, topology cluster.Topology) (cluster.Cluster, error) { - cfg, err := validConfig(origCfg) - if err != nil { - return nil, err - } - - kubeconfigPath := cfg.Meta.String(kubeconfigMetaKey) - kubeconfigPath, err = file.NormalizePath(kubeconfigPath) - if err != nil { - return nil, err - } - - var client istioKube.ExtendedClient - if len(cfg.HTTPProxy) > 0 { - proxyURL, err := url.Parse(cfg.HTTPProxy) - if err != nil { - return nil, err - } - client, err = buildClientWithProxy(kubeconfigPath, proxyURL) - if err != nil { - return nil, err - } - } else { - client, err = buildClient(kubeconfigPath) - if err != nil { - return nil, err - } - } - - // support fake VMs by default - vmSupport := true - if vmP := cfg.Meta.Bool(vmSupportMetaKey); vmP != nil { - vmSupport = *vmP - } - - return &Cluster{ - filename: kubeconfigPath, - ExtendedClient: client, - vmSupport: vmSupport, - Topology: topology, - }, nil -} - -func validConfig(cfg cluster.Config) (cluster.Config, error) { - // only include kube-specific validation here - if cfg.Meta.String(kubeconfigMetaKey) == "" { - return cfg, fmt.Errorf("missing meta.%s for %s", kubeconfigMetaKey, cfg.Name) - } - return cfg, nil -} - -func buildClient(kubeconfig string) (istioKube.ExtendedClient, error) { - rc, err := istioKube.DefaultRestConfig(kubeconfig, "", func(config *rest.Config) { - config.QPS = 200 - config.Burst = 400 - }) - if err != nil { - return nil, err - } - return istioKube.NewExtendedClient(istioKube.NewClientConfigForRestConfig(rc), "") -} - -func buildClientWithProxy(kubeconfig string, proxyURL *url.URL) (istioKube.ExtendedClient, error) { - rc, err := istioKube.DefaultRestConfig(kubeconfig, "", func(config *rest.Config) { - config.QPS = 200 - config.Burst = 400 - }) - if err != nil { - return nil, err - } - rc.Proxy = http.ProxyURL(proxyURL) - return istioKube.NewExtendedClient(istioKube.NewClientConfigForRestConfig(rc), "") -} diff --git a/pkg/test/framework/components/cluster/staticvm/staticvm.go b/pkg/test/framework/components/cluster/staticvm/staticvm.go deleted file mode 100644 index fa7c27d5d..000000000 --- a/pkg/test/framework/components/cluster/staticvm/staticvm.go +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package staticvm - -import ( - "errors" - "fmt" - "net" - "strings" -) - -import ( - "k8s.io/apimachinery/pkg/version" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/namespace" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/config" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" -) - -func init() { - cluster.RegisterFactory(cluster.StaticVM, build) -} - -var _ echo.Cluster = &vmcluster{} - -type vmcluster struct { - kube.ExtendedClient - cluster.Topology - - vms []echo.Config -} - -func build(cfg cluster.Config, topology cluster.Topology) (cluster.Cluster, error) { - vms, err := readInstances(cfg) - if err != nil { - return nil, err - } - return &vmcluster{ - Topology: topology, - vms: vms, - }, nil -} - -func readInstances(cfg cluster.Config) ([]echo.Config, error) { - var out []echo.Config - deployments := cfg.Meta.Slice("deployments") - for i, deploymentMeta := range deployments { - vm, err := instanceFromMeta(deploymentMeta) - if err != nil { - scopes.Framework.Errorf("failed reading deployment config %d of %s: %v", i, cfg.Name, err) - } - out = append(out, vm) - } - if len(out) == 0 || len(out) != len(deployments) { - return nil, fmt.Errorf("static vm cluster %s has no deployments provided", cfg.Name) - } - return out, nil -} - -func instanceFromMeta(cfg config.Map) (echo.Config, error) { - svc := cfg.String("service") - if svc == "" { - return echo.Config{}, errors.New("service must not be empty") - } - ns := cfg.String("namespace") - if ns == "" { - return echo.Config{}, errors.New("namespace must not be empty") - } - - var ips []string - for _, meta := range cfg.Slice("instances") { - publicIPStr := meta.String("ip") - privateIPStr := meta.String("instanceIP") - ip := net.ParseIP(publicIPStr) - if len(ip) == 0 { - return echo.Config{}, fmt.Errorf("failed parsing %q as IP address", publicIPStr) - } - ip = net.ParseIP(privateIPStr) - if len(ip) == 0 { - return echo.Config{}, fmt.Errorf("failed parsing %q as IP address", privateIPStr) - } - ips = append(ips, publicIPStr+":"+privateIPStr) - } - if len(ips) == 0 { - return echo.Config{}, fmt.Errorf("%s has no IPs", svc) - } - - return echo.Config{ - Namespace: namespace.Static(ns), - Service: svc, - // Will set the version of each subset if not provided - Version: cfg.String("version"), - StaticAddresses: ips, - // TODO support listing subsets - Subsets: nil, - // TODO support TLS for gRPC client - TLSSettings: nil, - }, nil -} - -func (v vmcluster) CanDeploy(config echo.Config) (echo.Config, bool) { - if !config.DeployAsVM { - return echo.Config{}, false - } - for _, vm := range v.vms { - if matchConfig(vm, config) { - vmCfg := config.DeepCopy() - vmCfg.StaticAddresses = vm.StaticAddresses - return vmCfg, true - } - } - return echo.Config{}, false -} - -func (v vmcluster) GetKubernetesVersion() (*version.Info, error) { - return nil, nil -} - -func matchConfig(vm, cfg echo.Config) bool { - return vm.Service == cfg.Service && strings.HasPrefix(cfg.Namespace.Name(), vm.Namespace.Name()) -} diff --git a/pkg/test/framework/components/cluster/staticvm/staticvm_test.go b/pkg/test/framework/components/cluster/staticvm/staticvm_test.go deleted file mode 100644 index fc64e56b2..000000000 --- a/pkg/test/framework/components/cluster/staticvm/staticvm_test.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package staticvm - -import ( - "testing" -) - -import ( - "github.com/google/go-cmp/cmp" - "gopkg.in/yaml.v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/namespace" -) - -func TestBuild(t *testing.T) { - cfg := cluster.Config{} - if err := yaml.Unmarshal([]byte(` -kind: StaticVM -clusterName: static-vms -primaryClusterName: istio-testing -meta: - deployments: - - service: vm - namespace: echo - instances: - - ip: 172.17.0.4 - instanceIP: 10.0.0.1 -`), &cfg); err != nil { - t.Fatal(err) - } - got, err := build(cfg, cluster.Topology{}) - if err != nil { - t.Fatal(err) - } - if diff := cmp.Diff(got.(*vmcluster).vms, []echo.Config{{ - Service: "vm", - Namespace: namespace.Static("echo"), - StaticAddresses: []string{"172.17.0.4:10.0.0.1"}, - }}); diff != "" { - t.Fatal(diff) - } -} - -func TestVmcluster_CanDeploy(t *testing.T) { - aSvc := "a" - echoNS := namespace.Static("echo") - echoGenNS := namespace.Static("echo-1234") - ips := []string{"1.2.3.4"} - vms := vmcluster{vms: []echo.Config{{ - Service: aSvc, Namespace: echoNS, - StaticAddresses: ips, - }}} - - for name, tc := range map[string]struct { - given echo.Config - want echo.Config - wantOk bool - }{ - "match": { - given: echo.Config{DeployAsVM: true, Service: aSvc, Namespace: echoGenNS, Ports: []echo.Port{{Name: "grpc"}}}, - want: echo.Config{DeployAsVM: true, Service: aSvc, Namespace: echoGenNS, Ports: []echo.Port{{Name: "grpc"}}, StaticAddresses: ips}, - wantOk: true, - }, - "non vm": { - given: echo.Config{Service: aSvc, Namespace: echoGenNS, Ports: []echo.Port{{Name: "grpc"}}}, - }, - "namespace mismatch": { - given: echo.Config{DeployAsVM: true, Service: aSvc, Namespace: namespace.Static("other"), Ports: []echo.Port{{Name: "grpc"}}}, - }, - "service mismatch": { - given: echo.Config{DeployAsVM: true, Service: "b", Namespace: echoNS, Ports: []echo.Port{{Name: "grpc"}}}, - }, - } { - t.Run(name, func(t *testing.T) { - got, ok := vms.CanDeploy(tc.given) - if ok != tc.wantOk { - t.Errorf("got %v but wanted %v", ok, tc.wantOk) - } - if diff := cmp.Diff(tc.want, got); diff != "" { - t.Error(diff) - } - }) - } -} diff --git a/pkg/test/framework/components/cluster/topology.go b/pkg/test/framework/components/cluster/topology.go deleted file mode 100644 index 6d2ba7dcf..000000000 --- a/pkg/test/framework/components/cluster/topology.go +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cluster - -import ( - "bytes" - "fmt" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -// Map can be given as a shared reference to multiple Topology/Cluster implementations. -// allowing clusters to find each other for lookups of Primary, ConfigCluster, etc. -type Map = map[string]Cluster - -func NewTopology(config Config, allClusters Map) Topology { - return Topology{ - ClusterName: config.Name, - ClusterKind: config.Kind, - Network: config.Network, - ClusterHTTPProxy: config.HTTPProxy, - PrimaryClusterName: config.PrimaryClusterName, - ConfigClusterName: config.ConfigClusterName, - AllClusters: allClusters, - Index: len(allClusters), - } -} - -// Topology gives information about the relationship between clusters. -// Cluster implementations can embed this struct to include common functionality. -type Topology struct { - ClusterName string - ClusterKind Kind - Network string - ClusterHTTPProxy string - PrimaryClusterName string - ConfigClusterName string - Index int - // AllClusters should contain all AllClusters in the context - AllClusters Map -} - -// NetworkName the cluster is on -func (c Topology) NetworkName() string { - return c.Network -} - -// Name provides the ClusterName this cluster used by Istio. -func (c Topology) Name() string { - return c.ClusterName -} - -// HTTPProxy to connect to the cluster -func (c Topology) HTTPProxy() string { - return c.ClusterHTTPProxy -} - -// knownClusterNames maintains a well-known set of cluster names. These will always be used with -// StableNames if encountered. -var knownClusterNames = map[string]struct{}{ - "primary": {}, - "remote": {}, - "cross-network-primary": {}, -} - -// StableName provides a name used for testcase names. Deterministic, so testgrid -// can be consistent when the underlying cluster names are dynamic. -func (c Topology) StableName() string { - var prefix string - switch c.Kind() { - case Kubernetes: - // If its a known cluster name, use that directly. - // This will not be dynamic, and allows 1:1 correlation of cluster name and test name for simplicity. - if _, f := knownClusterNames[c.Name()]; f { - return c.Name() - } - if c.IsPrimary() { - if c.IsConfig() { - prefix = "primary" - } else { - prefix = "externalistiod" - } - } else if c.IsRemote() { - if c.IsConfig() { - prefix = "config" - } else { - prefix = "remote" - } - } - default: - prefix = string(c.Kind()) - } - - return fmt.Sprintf("%s-%d", prefix, c.Index) -} - -func (c Topology) Kind() Kind { - return c.ClusterKind -} - -func (c Topology) IsPrimary() bool { - return c.Primary().Name() == c.Name() -} - -func (c Topology) IsConfig() bool { - return c.Config().Name() == c.Name() -} - -func (c Topology) IsRemote() bool { - return !c.IsPrimary() -} - -func (c Topology) IsExternalControlPlane() bool { - return c.IsPrimary() && !c.IsConfig() -} - -func (c Topology) Primary() Cluster { - cluster, ok := c.AllClusters[c.PrimaryClusterName] - if !ok || cluster == nil { - panic(fmt.Errorf("cannot find %s, the primary cluster for %s", c.PrimaryClusterName, c.Name())) - } - return cluster -} - -func (c Topology) PrimaryName() string { - return c.PrimaryClusterName -} - -func (c Topology) Config() Cluster { - cluster, ok := c.AllClusters[c.ConfigClusterName] - if !ok || cluster == nil { - panic(fmt.Errorf("cannot find %s, the config cluster for %s", c.ConfigClusterName, c.Name())) - } - return cluster -} - -func (c Topology) ConfigName() string { - return c.ConfigClusterName -} - -func (c Topology) WithPrimary(primaryClusterName string) Topology { - // TODO remove this, should only be provided by external config - c.PrimaryClusterName = primaryClusterName - return c -} - -func (c Topology) WithConfig(configClusterName string) Topology { - // TODO remove this, should only be provided by external config - c.ConfigClusterName = configClusterName - return c -} - -func (c Topology) MinKubeVersion(minor uint) bool { - cluster := c.AllClusters[c.ClusterName] - if cluster.Kind() != Kubernetes && cluster.Kind() != Fake { - return c.Primary().MinKubeVersion(minor) - } - return kube.IsAtLeastVersion(cluster, minor) -} - -func (c Topology) MaxKubeVersion(minor uint) bool { - cluster := c.AllClusters[c.ClusterName] - if cluster.Kind() != Kubernetes && cluster.Kind() != Fake { - return c.Primary().MaxKubeVersion(minor) - } - return kube.IsLessThanVersion(cluster, minor+1) -} - -func (c Topology) String() string { - buf := &bytes.Buffer{} - - _, _ = fmt.Fprintf(buf, "Name: %s\n", c.Name()) - _, _ = fmt.Fprintf(buf, "StableName: %s\n", c.StableName()) - _, _ = fmt.Fprintf(buf, "Kind: %s\n", c.Kind()) - _, _ = fmt.Fprintf(buf, "PrimaryCluster: %s\n", c.Primary().Name()) - _, _ = fmt.Fprintf(buf, "ConfigCluster: %s\n", c.Config().Name()) - _, _ = fmt.Fprintf(buf, "Network: %s\n", c.NetworkName()) - _, _ = fmt.Fprintf(buf, "HTTPProxy: %s\n", c.HTTPProxy()) - - return buf.String() -} diff --git a/pkg/test/framework/components/containerregistry/container_registry_server.yaml b/pkg/test/framework/components/containerregistry/container_registry_server.yaml deleted file mode 100644 index 69a8fcbd6..000000000 --- a/pkg/test/framework/components/containerregistry/container_registry_server.yaml +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -apiVersion: v1 -kind: Service -metadata: - name: container-registry - labels: - app: container-registry -spec: - ports: - - name: http - port: 1338 - selector: - app: container-registry ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: container-registry -spec: - replicas: 1 - selector: - matchLabels: - app: container-registry - template: - metadata: - labels: - app: container-registry - spec: - containers: - - image: gcr.io/istio-testing/fake-registry:1.0 - name: container-registry - ports: - - containerPort: 1338 - readinessProbe: - httpGet: - path: /ready - port: 1338 - initialDelaySeconds: 1 - periodSeconds: 1 - failureThreshold: 10 ---- diff --git a/pkg/test/framework/components/containerregistry/containerregistry.go b/pkg/test/framework/components/containerregistry/containerregistry.go deleted file mode 100644 index 9af424d98..000000000 --- a/pkg/test/framework/components/containerregistry/containerregistry.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package containerregistry provides basic utilities around configuring the fake -// container registry server component for integration testing. -package containerregistry - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -// Instance represents a deployed fake container registry app instance. -type Instance interface { - // Address is the address of the service provided by the - // fake container registry server. - Address() string -} - -// Config defines the options for creating an fake container registry component. -type Config struct { - // Cluster to be used in a multicluster environment - Cluster cluster.Cluster -} - -// New returns a new instance of container registry. -func New(ctx resource.Context, c Config) (i Instance, err error) { - return newKube(ctx, c) -} - -// NewOrFail returns a new container registry instance or fails test. -func NewOrFail(t test.Failer, ctx resource.Context, c Config) Instance { - t.Helper() - i, err := New(ctx, c) - if err != nil { - t.Fatalf("containerregistry.NewOrFail: %v", err) - } - - return i -} diff --git a/pkg/test/framework/components/containerregistry/kube.go b/pkg/test/framework/components/containerregistry/kube.go deleted file mode 100644 index 7398b9b22..000000000 --- a/pkg/test/framework/components/containerregistry/kube.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package containerregistry - -import ( - "fmt" - "io" - "net" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/namespace" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - testKube "github.com/apache/dubbo-go-pixiu/pkg/test/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" -) - -const ( - service = "container-registry" - ns = "container-registry" -) - -var ( - _ Instance = &kubeComponent{} - _ io.Closer = &kubeComponent{} -) - -type kubeComponent struct { - id resource.ID - ns namespace.Instance - cluster cluster.Cluster - address string -} - -func newKube(ctx resource.Context, cfg Config) (Instance, error) { - c := &kubeComponent{ - cluster: ctx.Clusters().GetOrDefault(cfg.Cluster), - } - c.id = ctx.TrackResource(c) - var err error - scopes.Framework.Info("=== BEGIN: Deploy container registry server ===") - defer func() { - if err != nil { - err = fmt.Errorf("container registry deployment failed: %v", err) - scopes.Framework.Infof("=== FAILED: Deploy container registry server ===") - _ = c.Close() - } else { - scopes.Framework.Info("=== SUCCEEDED: Deploy container registry server ===") - } - }() - - c.ns, err = namespace.New(ctx, namespace.Config{ - Prefix: ns, - }) - if err != nil { - return nil, fmt.Errorf("could not create %q namespace for container registry server install; err: %v", ns, err) - } - - // apply YAML - if err := c.cluster.ApplyYAMLFiles(c.ns.Name(), env.ContainerRegistryServerInstallFilePath); err != nil { - return nil, fmt.Errorf("failed to apply rendered %s, err: %v", env.ContainerRegistryServerInstallFilePath, err) - } - - if _, _, err = testKube.WaitUntilServiceEndpointsAreReady(c.cluster, c.ns.Name(), service); err != nil { - scopes.Framework.Infof("Error waiting for container registry service to be available: %v", err) - return nil, err - } - - c.address = net.JoinHostPort(fmt.Sprintf("%s.%s", service, c.ns.Name()), "1338") - scopes.Framework.Infof("container registry server in-cluster address: %s", c.address) - - return c, nil -} - -func (c *kubeComponent) ID() resource.ID { - return c.id -} - -// Close implements io.Closer. -func (c *kubeComponent) Close() error { - return nil -} - -func (c *kubeComponent) Address() string { - return c.address -} diff --git a/pkg/test/framework/components/echo/annotations.go b/pkg/test/framework/components/echo/annotations.go deleted file mode 100644 index 89f3ccdf1..000000000 --- a/pkg/test/framework/components/echo/annotations.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -import ( - "fmt" - "strconv" - "strings" -) - -import ( - "istio.io/api/annotation" -) - -type AnnotationType string - -const ( - WorkloadAnnotation AnnotationType = "workload" -) - -type Annotation struct { - Name string - Type AnnotationType - Default AnnotationValue -} - -var ( - SidecarInject = workloadAnnotation(annotation.SidecarInject.Name, "true") - SidecarRewriteAppHTTPProbers = workloadAnnotation(annotation.SidecarRewriteAppHTTPProbers.Name, "") - SidecarBootstrapOverride = workloadAnnotation(annotation.SidecarBootstrapOverride.Name, "") - SidecarVolumeMount = workloadAnnotation(annotation.SidecarUserVolumeMount.Name, "") - SidecarVolume = workloadAnnotation(annotation.SidecarUserVolume.Name, "") - SidecarConfig = workloadAnnotation(annotation.ProxyConfig.Name, "") - SidecarInterceptionMode = workloadAnnotation(annotation.SidecarInterceptionMode.Name, "REDIRECT") - SidecarIncludeInboundPorts = workloadAnnotation(annotation.SidecarTrafficIncludeInboundPorts.Name, "") - SidecarIncludeOutboundIPRanges = workloadAnnotation(annotation.SidecarTrafficIncludeOutboundIPRanges.Name, "") - SidecarProxyConfig = workloadAnnotation(annotation.ProxyConfig.Name, "") - SidecarInjectTemplates = workloadAnnotation(annotation.InjectTemplates.Name, "") -) - -type AnnotationValue struct { - Value string -} - -func (v *AnnotationValue) Get() string { - return v.Value -} - -func (v *AnnotationValue) AsBool() bool { - return toBool(v.Get()) -} - -func (v *AnnotationValue) AsInt() int { - return toInt(v.Get()) -} - -func (v *AnnotationValue) Set(arg string) *AnnotationValue { - v.Value = arg - return v -} - -func (v *AnnotationValue) SetBool(arg bool) *AnnotationValue { - v.Value = strconv.FormatBool(arg) - return v -} - -func (v *AnnotationValue) SetInt(arg int) *AnnotationValue { - v.Value = strconv.Itoa(arg) - return v -} - -func NewAnnotationValue() *AnnotationValue { - return &AnnotationValue{} -} - -func workloadAnnotation(name string, value string) Annotation { - return Annotation{ - Name: name, - Type: WorkloadAnnotation, - Default: AnnotationValue{ - Value: value, - }, - } -} - -type Annotations map[Annotation]*AnnotationValue - -func NewAnnotations() Annotations { - return make(Annotations) -} - -func (a Annotations) Set(k Annotation, v string) Annotations { - a[k] = &AnnotationValue{v} - return a -} - -func (a Annotations) SetBool(k Annotation, v bool) Annotations { - a[k] = NewAnnotationValue().SetBool(v) - return a -} - -func (a Annotations) SetInt(k Annotation, v int) Annotations { - a[k] = NewAnnotationValue().SetInt(v) - return a -} - -func (a Annotations) getOrDefault(k Annotation) *AnnotationValue { - anno, ok := a[k] - if !ok { - anno = &k.Default - } - return anno -} - -func (a Annotations) GetByName(k string) string { - for keys := range a { - if keys.Name == k { - return a.Get(keys) - } - } - return "" -} - -func (a Annotations) Get(k Annotation) string { - return a.getOrDefault(k).Value -} - -func (a Annotations) GetBool(k Annotation) bool { - return a.getOrDefault(k).AsBool() -} - -func (a Annotations) GetInt(k Annotation) int { - return a.getOrDefault(k).AsInt() -} - -func toBool(v string) bool { - switch strings.ToLower(v) { - // http://yaml.org/type/bool.html - case "y", "yes", "true", "on": - return true - default: - return false - } -} - -func toInt(v string) int { - i, err := strconv.Atoi(v) - if err != nil { - panic(fmt.Sprintf("failed parsing int value: '%s'", v)) - } - return i -} diff --git a/pkg/test/framework/components/echo/caller.go b/pkg/test/framework/components/echo/caller.go deleted file mode 100644 index 25029e981..000000000 --- a/pkg/test/framework/components/echo/caller.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo" -) - -// CallResult the result of a call operation. -type CallResult struct { - From Caller - Opts CallOptions - Responses echo.Responses -} - -type Caller interface { - // Call from this Instance to a target Instance. - Call(options CallOptions) (CallResult, error) - CallOrFail(t test.Failer, options CallOptions) CallResult -} - -type Callers []Caller - -// Instances returns an Instances if all callers are Instance, otherwise returns nil. -func (c Callers) Instances() Instances { - var out Instances - for _, caller := range c { - c, ok := caller.(Instance) - if !ok { - return nil - } - out = append(out, c) - } - return out -} diff --git a/pkg/test/framework/components/echo/calloptions.go b/pkg/test/framework/components/echo/calloptions.go deleted file mode 100644 index 302950dff..000000000 --- a/pkg/test/framework/components/echo/calloptions.go +++ /dev/null @@ -1,352 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -import ( - "errors" - "fmt" - "net/http" - "time" -) - -import ( - wrappers "google.golang.org/protobuf/types/known/wrapperspb" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/http/headers" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common/scheme" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -// HTTP settings -type HTTP struct { - // If true, h2c will be used in HTTP requests - HTTP2 bool - - // If true, HTTP/3 request over QUIC will be used. - // It is mandatory to specify TLS settings - HTTP3 bool - - // Path specifies the URL path for the HTTP(s) request. - Path string - - // Method to send. Defaults to GET. - Method string - - // Headers indicates headers that should be sent in the request. Ignored for WebSocket calls. - // If no Host header is provided, a default will be chosen for the target service endpoint. - Headers http.Header - - // FollowRedirects will instruct the call to follow 301 redirects. Otherwise, the original 301 response - // is returned directly. - FollowRedirects bool - - // HTTProxy used for making ingress echo call via proxy - HTTPProxy string -} - -// TLS settings -type TLS struct { - // Use the custom certificate to make the call. This is mostly used to make mTLS request directly - // (without proxy) from naked client to test certificates issued by custom CA instead of the Istio self-signed CA. - Cert, Key, CaCert string - - // Use the custom certificates file to make the call. - CertFile, KeyFile, CaCertFile string - - // Skip verify peer's certificate. - InsecureSkipVerify bool - - Alpn []string - ServerName string -} - -// Retry settings -type Retry struct { - // NoRetry if true, no retry will be attempted. - NoRetry bool - - // Options to be used when retrying. If not specified, defaults will be used. - Options []retry.Option -} - -// TCP settings -type TCP struct { - // ExpectedResponse asserts this is in the response for TCP requests. - ExpectedResponse *wrappers.StringValue -} - -// Target of a call. -type Target interface { - Configurable - WorkloadContainer - - // Instances in this target. - Instances() Instances -} - -// CallOptions defines options for calling a Endpoint. -type CallOptions struct { - // To is the Target to be called. - To Target - - // ToWorkload will call a specific workload in this instance, rather than the Service. - // If there are multiple workloads in the Instance, the first is used. - // Can be used with `ToWorkload: to.WithWorkloads(someWl)` to send to a specific workload. - // When using the Port field, the ServicePort should be used. - ToWorkload Instance - - // Port to be used for the call. Ignored if Scheme == DNS. If the Port.ServicePort is set, - // either Port.Protocol or Scheme must also be set. If Port.ServicePort is not set, - // the port is looked up in To by either Port.Name or Port.Protocol. - Port Port - - // Scheme to be used when making the call. If not provided, the Scheme will be selected - // based on the Port.Protocol. - Scheme scheme.Instance - - // Address specifies the host name or IP address to be used on the request. If not provided, - // an appropriate default is chosen for To. - Address string - - // Count indicates the number of exchanges that should be made with the service endpoint. - // If Count <= 0, defaults to 1. - Count int - - // Timeout used for each individual request. Must be > 0, otherwise 5 seconds is used. - Timeout time.Duration - - // Retry options for the call. - Retry Retry - - // HTTP settings. - HTTP HTTP - - // TCP settings. - TCP TCP - - // TLS settings. - TLS TLS - - // Message to be sent. - Message string - - // Check the server responses. If none is provided, only the number of responses received - // will be checked. - Check Checker -} - -// GetHost returns the best default host for the call. Returns the first host defined from the following -// sources (in order of precedence): Host header, target's DefaultHostHeader, Address, target's FQDN. -func (o CallOptions) GetHost() string { - // First, use the host header, if specified. - if h := o.HTTP.Headers.Get(headers.Host); len(h) > 0 { - return h - } - - // Next use the target's default, if specified. - if o.To != nil && len(o.To.Config().DefaultHostHeader) > 0 { - return o.To.Config().DefaultHostHeader - } - - // Next, if the Address was manually specified use it as the Host. - if len(o.Address) > 0 { - return o.Address - } - - // Finally, use the target's FQDN. - if o.To != nil { - return o.To.Config().ClusterLocalFQDN() - } - - return "" -} - -func (o CallOptions) DeepCopy() CallOptions { - clone := o - if o.TLS.Alpn != nil { - clone.TLS.Alpn = make([]string, len(o.TLS.Alpn)) - copy(clone.TLS.Alpn, o.TLS.Alpn) - } - return clone -} - -// FillDefaults fills out any defaults that haven't been explicitly specified. -func (o *CallOptions) FillDefaults() error { - // Fill in the address if not set. - if err := o.fillAddress(); err != nil { - return err - } - - // Fill in the port if not set or the service port is missing. - if err := o.fillPort(); err != nil { - return err - } - - // Fill in the scheme if not set, using the port information. - if err := o.fillScheme(); err != nil { - return err - } - - // Fill in HTTP headers - o.fillHeaders() - - if o.Timeout <= 0 { - o.Timeout = common.DefaultRequestTimeout - } - - if o.Count <= 0 { - o.Count = common.DefaultCount - } - - // Add any user-specified options after the default options (last option wins for each type of option). - o.Retry.Options = append(append([]retry.Option{}, DefaultCallRetryOptions()...), o.Retry.Options...) - - // If no Check was specified, assume no error. - if o.Check == nil { - o.Check = NoChecker() - } - return nil -} - -// FillDefaultsOrFail calls FillDefaults and fails if an error occurs. -func (o *CallOptions) FillDefaultsOrFail(t test.Failer) { - t.Helper() - if err := o.FillDefaults(); err != nil { - t.Fatal(err) - } -} - -func (o *CallOptions) fillAddress() error { - if o.Address == "" { - if o.To != nil { - // No host specified, use the fully qualified domain name for the service. - o.Address = o.To.Config().ClusterLocalFQDN() - return nil - } - if o.ToWorkload != nil { - wl, err := o.ToWorkload.Workloads() - if err != nil { - return err - } - o.Address = wl[0].Address() - return nil - } - - return errors.New("if address is not set, then To must be set") - } - return nil -} - -func (o *CallOptions) fillPort() error { - if o.Scheme == scheme.DNS { - // Port is not used for DNS. - return nil - } - - if o.Port.ServicePort > 0 { - if o.Port.Protocol == "" && o.Scheme == "" { - return errors.New("callOptions: servicePort specified, but no protocol or scheme was set") - } - - // The service port was set explicitly. Nothing else to do. - return nil - } - - if o.To != nil { - return o.fillPort2(o.To) - } else if o.ToWorkload != nil { - err := o.fillPort2(o.ToWorkload) - if err != nil { - return err - } - // Set the ServicePort to workload port since we are not reaching it through the Service - p := o.Port - p.ServicePort = p.WorkloadPort - o.Port = p - } - - if o.Port.ServicePort <= 0 || (o.Port.Protocol == "" && o.Scheme == "") || o.Address == "" { - return fmt.Errorf("if target is not set, then port.servicePort, port.protocol or schema, and address must be set") - } - - return nil -} - -func (o *CallOptions) fillPort2(target Target) error { - servicePorts := target.Config().Ports.GetServicePorts() - - if o.Port.Name != "" { - // Look up the port by name. - p, found := servicePorts.ForName(o.Port.Name) - if !found { - return fmt.Errorf("callOptions: no port named %s available in To Instance", o.Port.Name) - } - o.Port = p - return nil - } - - if o.Port.Protocol != "" { - // Look up the port by protocol. - p, found := servicePorts.ForProtocol(o.Port.Protocol) - if !found { - return fmt.Errorf("callOptions: no port for protocol %s available in To Instance", o.Port.Protocol) - } - o.Port = p - return nil - } - - if o.Port.ServicePort != 0 { - // We just have a single port number, populate the rest of the fields - p, found := servicePorts.ForServicePort(o.Port.ServicePort) - if !found { - return fmt.Errorf("callOptions: no port %d available in To Instance", o.Port.ServicePort) - } - o.Port = p - return nil - } - return nil -} - -func (o *CallOptions) fillScheme() error { - if o.Scheme == "" { - // No protocol, fill it in. - var err error - if o.Scheme, err = o.Port.Scheme(); err != nil { - return err - } - } - return nil -} - -func (o *CallOptions) fillHeaders() { - if o.ToWorkload != nil { - return - } - // Initialize the headers and add a default Host header if none provided. - if o.HTTP.Headers == nil { - o.HTTP.Headers = make(http.Header) - } else { - // Avoid mutating input, which can lead to concurrent writes - o.HTTP.Headers = o.HTTP.Headers.Clone() - } - - if h := o.GetHost(); len(h) > 0 { - o.HTTP.Headers.Set(headers.Host, h) - } -} diff --git a/pkg/test/framework/components/echo/check/checkers.go b/pkg/test/framework/components/echo/check/checkers.go deleted file mode 100644 index db8776050..000000000 --- a/pkg/test/framework/components/echo/check/checkers.go +++ /dev/null @@ -1,424 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package check - -import ( - "errors" - "fmt" - "net/http" - "strconv" - "strings" -) - -import ( - "github.com/hashicorp/go-multierror" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - echoClient "github.com/apache/dubbo-go-pixiu/pkg/test/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/istio/ingress" - "github.com/apache/dubbo-go-pixiu/pkg/util/istiomultierror" -) - -// Each applies the given per-response function across all responses. -func Each(c func(r echoClient.Response) error) echo.Checker { - return func(result echo.CallResult, _ error) error { - rs := result.Responses - if rs.IsEmpty() { - return fmt.Errorf("no responses received") - } - outErr := istiomultierror.New() - for i, r := range rs { - if err := c(r); err != nil { - outErr = multierror.Append(outErr, fmt.Errorf("response[%d]: %v", i, err)) - } - } - return outErr.ErrorOrNil() - } -} - -// And is an aggregate Checker that requires all Checkers succeed. Any nil Checkers are ignored. -func And(checkers ...echo.Checker) echo.Checker { - return func(result echo.CallResult, err error) error { - for _, c := range filterNil(checkers) { - if err := c(result, err); err != nil { - return err - } - } - return nil - } -} - -// Or is an aggregate Checker that requires at least one Checker succeeds. -func Or(checkers ...echo.Checker) echo.Checker { - return func(result echo.CallResult, err error) error { - out := istiomultierror.New() - for _, c := range checkers { - err := c(result, err) - if err == nil { - return nil - } - out = multierror.Append(out, err) - } - return out.ErrorOrNil() - } -} - -func filterNil(checkers []echo.Checker) []echo.Checker { - var out []echo.Checker - for _, c := range checkers { - if c != nil { - out = append(out, c) - } - } - return out -} - -// NoError is similar to echo.NoChecker, but provides additional context information. -func NoError() echo.Checker { - return func(_ echo.CallResult, err error) error { - if err != nil { - return fmt.Errorf("expected no error, but encountered %v", err) - } - return nil - } -} - -// Error provides a checker that returns an error if the call succeeds. -func Error() echo.Checker { - return func(_ echo.CallResult, err error) error { - if err == nil { - return errors.New("expected error, but none occurred") - } - return nil - } -} - -// ErrorContains is similar to Error, but checks that the error message contains the given string. -func ErrorContains(expected string) echo.Checker { - return func(_ echo.CallResult, err error) error { - if err == nil { - return errors.New("expected error, but none occurred") - } - if !strings.Contains(err.Error(), expected) { - return fmt.Errorf("expected error to contain %s: %v", expected, err) - } - return nil - } -} - -func ErrorOrStatus(expected int) echo.Checker { - expectedStr := "" - if expected > 0 { - expectedStr = strconv.Itoa(expected) - } - return func(resp echo.CallResult, err error) error { - if err != nil { - return nil - } - for _, r := range resp.Responses { - if r.Code != expectedStr { - return fmt.Errorf("expected response code `%s`, got %q", expectedStr, r.Code) - } - } - return nil - } -} - -// OK is a shorthand for NoErrorAndStatus(200). -func OK() echo.Checker { - return NoErrorAndStatus(http.StatusOK) -} - -// NoErrorAndStatus is checks that no error occurred and htat the returned status code matches the expected -// value. -func NoErrorAndStatus(expected int) echo.Checker { - return And(NoError(), Status(expected)) -} - -// Status checks that the response status code matches the expected value. If the expected value is zero, -// checks that the response code is unset. -func Status(expected int) echo.Checker { - expectedStr := "" - if expected > 0 { - expectedStr = strconv.Itoa(expected) - } - return Each(func(r echoClient.Response) error { - if r.Code != expectedStr { - return fmt.Errorf("expected response code `%s`, got %q. Response: %s", expectedStr, r.Code, r) - } - return nil - }) -} - -// BodyContains checks that the response body contains the given string. -func BodyContains(expected string) echo.Checker { - return Each(func(r echoClient.Response) error { - if !strings.Contains(r.RawContent, expected) { - return fmt.Errorf("want %q in body but not found: %s", expected, r.RawContent) - } - return nil - }) -} - -// Forbidden checks that the response indicates that the request was rejected by RBAC. -func Forbidden(p protocol.Instance) echo.Checker { - switch { - case p.IsGRPC(): - return ErrorContains("rpc error: code = PermissionDenied") - case p.IsTCP(): - return ErrorContains("EOF") - default: - return NoErrorAndStatus(http.StatusForbidden) - } -} - -// TooManyRequests checks that at least one message receives a StatusTooManyRequests status code. -func TooManyRequests() echo.Checker { - codeStr := strconv.Itoa(http.StatusTooManyRequests) - return func(result echo.CallResult, _ error) error { - for _, r := range result.Responses { - if codeStr == r.Code { - // Successfully received too many requests. - return nil - } - } - return errors.New("no request received StatusTooManyRequest error") - } -} - -func Host(expected string) echo.Checker { - return Each(func(r echoClient.Response) error { - if r.Host != expected { - return fmt.Errorf("expected host %s, received %s", expected, r.Host) - } - return nil - }) -} - -func Protocol(expected string) echo.Checker { - return Each(func(r echoClient.Response) error { - if r.Protocol != expected { - return fmt.Errorf("expected protocol %s, received %s", expected, r.Protocol) - } - return nil - }) -} - -func Alpn(expected string) echo.Checker { - return Each(func(r echoClient.Response) error { - if r.Alpn != expected { - return fmt.Errorf("expected alpn %s, received %s", expected, r.Alpn) - } - return nil - }) -} - -func MTLSForHTTP() echo.Checker { - return Each(func(r echoClient.Response) error { - if !strings.HasPrefix(r.RequestURL, "http://") && - !strings.HasPrefix(r.RequestURL, "grpc://") && - !strings.HasPrefix(r.RequestURL, "ws://") { - // Non-HTTP traffic. Fail open, we cannot check mTLS. - return nil - } - _, f1 := r.RequestHeaders["X-Forwarded-Client-Cert"] - // nolint: staticcheck - _, f2 := r.RequestHeaders["x-forwarded-client-cert"] // grpc has different casing - if f1 || f2 { - return nil - } - return fmt.Errorf("expected X-Forwarded-Client-Cert but not found: %v", r) - }) -} - -func Port(expected int) echo.Checker { - return Each(func(r echoClient.Response) error { - expectedStr := strconv.Itoa(expected) - if r.Port != expectedStr { - return fmt.Errorf("expected port %s, received %s", expectedStr, r.Port) - } - return nil - }) -} - -func requestHeader(r echoClient.Response, key, expected string) error { - actual := r.RequestHeaders.Get(key) - if actual != expected { - return fmt.Errorf("request header %s: expected `%s`, received `%s`", key, expected, actual) - } - return nil -} - -func responseHeader(r echoClient.Response, key, expected string) error { - actual := r.ResponseHeaders.Get(key) - if actual != expected { - return fmt.Errorf("response header %s: expected `%s`, received `%s`", key, expected, actual) - } - return nil -} - -func RequestHeader(key, expected string) echo.Checker { - return Each(func(r echoClient.Response) error { - return requestHeader(r, key, expected) - }) -} - -func ResponseHeader(key, expected string) echo.Checker { - return Each(func(r echoClient.Response) error { - return responseHeader(r, key, expected) - }) -} - -func RequestHeaders(expected map[string]string) echo.Checker { - return Each(func(r echoClient.Response) error { - outErr := istiomultierror.New() - for k, v := range expected { - outErr = multierror.Append(outErr, requestHeader(r, k, v)) - } - return outErr.ErrorOrNil() - }) -} - -func ResponseHeaders(expected map[string]string) echo.Checker { - return Each(func(r echoClient.Response) error { - outErr := istiomultierror.New() - for k, v := range expected { - outErr = multierror.Append(outErr, responseHeader(r, k, v)) - } - return outErr.ErrorOrNil() - }) -} - -func Cluster(expected string) echo.Checker { - return Each(func(r echoClient.Response) error { - if r.Cluster != expected { - return fmt.Errorf("expected cluster %s, received %s", expected, r.Cluster) - } - return nil - }) -} - -func URL(expected string) echo.Checker { - return Each(func(r echoClient.Response) error { - if r.URL != expected { - return fmt.Errorf("expected URL %s, received %s", expected, r.URL) - } - return nil - }) -} - -// ReachedTargetClusters is similar to ReachedClusters, except that the set of expected clusters is -// retrieved from the Target of the request. -func ReachedTargetClusters(allClusters cluster.Clusters) echo.Checker { - return func(result echo.CallResult, err error) error { - expectedByNetwork := result.Opts.To.Clusters().ByNetwork() - return checkReachedClusters(result, allClusters, expectedByNetwork) - } -} - -// ReachedClusters returns an error if requests did not load balance as expected. -// -// For cases where all clusters are on the same network, verifies that each of the expected clusters was reached. -// -// For multi-network configurations, verifies the current (limited) Istio load balancing behavior when going through -// a gateway. Ensures that all expected networks were reached, and that all clusters on the same network as the -// client were reached. -func ReachedClusters(allClusters cluster.Clusters, expectedClusters cluster.Clusters) echo.Checker { - expectedByNetwork := expectedClusters.ByNetwork() - return func(result echo.CallResult, err error) error { - return checkReachedClusters(result, allClusters, expectedByNetwork) - } -} - -func checkReachedClusters(result echo.CallResult, allClusters cluster.Clusters, expectedByNetwork cluster.ClustersByNetwork) error { - if err := checkReachedNetworks(result, allClusters, expectedByNetwork); err != nil { - return err - } - return checkReachedClustersInNetwork(result, allClusters, expectedByNetwork) -} - -func checkReachedNetworks(result echo.CallResult, allClusters cluster.Clusters, expectedByNetwork cluster.ClustersByNetwork) error { - // Gather the networks that were reached. - networkHits := make(map[string]int) - for _, rr := range result.Responses { - c := allClusters.GetByName(rr.Cluster) - if c != nil { - networkHits[c.NetworkName()]++ - } - } - - // Verify that all expected networks were reached. - for network := range expectedByNetwork { - if networkHits[network] == 0 { - return fmt.Errorf("did not reach network %v, got %v", network, networkHits) - } - } - - // Verify that no unexpected networks were reached. - for network := range networkHits { - if expectedByNetwork[network] == nil { - return fmt.Errorf("reached network not in %v, got %v", expectedByNetwork.Networks(), networkHits) - } - } - return nil -} - -func checkReachedClustersInNetwork(result echo.CallResult, allClusters cluster.Clusters, expectedByNetwork cluster.ClustersByNetwork) error { - // Determine the source network of the caller. - var sourceNetwork string - switch from := result.From.(type) { - case echo.Instance: - sourceNetwork = from.Config().Cluster.NetworkName() - case ingress.Instance: - sourceNetwork = from.Cluster().NetworkName() - default: - // Unable to determine the source network of the caller. Skip this check. - return nil - } - - // Lookup only the expected clusters in the same network as the caller. - expectedClustersInSourceNetwork := expectedByNetwork[sourceNetwork] - - clusterHits := make(map[string]int) - for _, rr := range result.Responses { - clusterHits[rr.Cluster]++ - } - - for _, c := range expectedClustersInSourceNetwork { - if clusterHits[c.Name()] == 0 { - return fmt.Errorf("did not reach all of %v in source network %v, got %v", - expectedClustersInSourceNetwork, sourceNetwork, clusterHits) - } - } - - // Verify that no unexpected clusters were reached. - for clusterName := range clusterHits { - reachedCluster := allClusters.GetByName(clusterName) - if reachedCluster == nil || reachedCluster.NetworkName() != sourceNetwork { - // Ignore clusters on a different network from the source. - continue - } - - if expectedClustersInSourceNetwork.GetByName(clusterName) == nil { - return fmt.Errorf("reached cluster %v in source network %v not in %v, got %v", - clusterName, sourceNetwork, expectedClustersInSourceNetwork, clusterHits) - } - } - return nil -} diff --git a/pkg/test/framework/components/echo/checker.go b/pkg/test/framework/components/echo/checker.go deleted file mode 100644 index bb5f3ea5f..000000000 --- a/pkg/test/framework/components/echo/checker.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -var noChecker Checker = func(_ CallResult, err error) error { - return err -} - -// Checker inspects echo call results for errors. -type Checker func(CallResult, error) error - -func (c Checker) Check(result CallResult, err error) error { - return c(result, err) -} - -// NoChecker provides a Checker that returns the original raw call error, unaltered. -func NoChecker() Checker { - return noChecker -} diff --git a/pkg/test/framework/components/echo/cmd/echogen/README.md b/pkg/test/framework/components/echo/cmd/echogen/README.md deleted file mode 100644 index 47a6bf213..000000000 --- a/pkg/test/framework/components/echo/cmd/echogen/README.md +++ /dev/null @@ -1,91 +0,0 @@ -# echogen - -`echogen` is a util for generating Kubernetes manifests from echo configurations. - -## Overview - -### Installation - -```bash -go install istio.io/pkg/test/framework/components/echo/echogen -``` - -### Usage - -```text -echogen [opts] config.yaml -``` - -The config file is YAML containing a list of -[echo.Config](https://github.com/istio/istio/blob/master/pkg/test/framework/components/echo/config.go#L52) objects: - -```yaml -- Service: a - Namespace: echo -- Service: headless - Namespace: echo - Headless: true -``` - -### Options - -`echogen` supports all options from the test framework that would affect Echo deployments -such as: `istio.test`, `.imagePullSecret`, `istio.test.hub` and several others. - -In addition to the framework level options: - -```text --out : Write output to the specified file --dir: If specified, each deployment will be written to a separate file, in a directory named by -out. -``` - -### Full Example with gRPC UI - -1. Make sure to install [gRPC UI](https://github.com/fullstorydev/grpcui) if you haven't already - -1. Create an `echogen` config: - -```bash -echo ' -- Service: a - Namespace: echo -- Service: b - Namespace: echo -' > config.yaml -``` - -1. Run `echogen`: - -```bash -echogen -out echo.yaml config.yaml -``` - -1. Apply the manifest to the cluster - -```bash -kubectl apply -f echo.yaml -``` - -1. Port-forward the gRPC port (default container port is 17070) - -```bash -kubectl -n echo port-forward a-v1-fc649d9fc-59rkj 17070 -``` - -1. Start gRPC UI - -```bash -grpcui -plaintext localhost:17070 -``` - -1. Because our echo gRPC service enables reflection, you should be able to open your browser - and get a user interface that shows all of the possible methods and request options. - - Change the "Method name" to `ForwardEcho`, then in "Request Data" set `url` to `grpc://b:7070` then click `Invoke` - -1. (Bonus) If you open the "Raw Request (JSON)" tab, you can re-use that for requests via - [grpcurl](https://github.com/fullstorydev/grpcurl) without constructing JSON by hand: - -```bash -grpcurl -plaintext localhost:17070 EchoTestService/ForwardEcho -d '{"url": "grpc://b:7070"}' -``` diff --git a/pkg/test/framework/components/echo/cmd/echogen/echogen.go b/pkg/test/framework/components/echo/cmd/echogen/echogen.go deleted file mode 100644 index e33a2d612..000000000 --- a/pkg/test/framework/components/echo/cmd/echogen/echogen.go +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "flag" - "fmt" - "io" - "log" - "os" -) - -import ( - "github.com/hashicorp/go-multierror" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/common/ports" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/config" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/yml" -) - -var ( - outputPath string - dirOutput bool -) - -func init() { - flag.StringVar(&outputPath, "out", "", "If specified, all generated output will be written to this file.") - flag.BoolVar(&dirOutput, "dir", false, "If true, all generated output will be written to separate files per-config, in a directory named by -out.") -} - -func main() { - if !config.Parsed() { - config.Parse() - } - if flag.NArg() < 1 { - flag.Usage() - os.Exit(1) - } - generate(flag.Args()[0], outputPath, dirOutput, os.Stdout) -} - -func generate(input, output string, outputDir bool, outstream io.StringWriter) { - if !config.Parsed() { - // for tests - config.Parse() - } - - gen := newGenerator() - if err := gen.load(input); err != nil { - log.Fatalf("failed loading %s: %v", input, err) - } - if err := gen.generate(); err != nil { - log.Fatalf("failed generating manifests: %v", err) - } - - var err error - if output != "" { - err = gen.writeOutputFile(output, outputDir) - } else { - _, err = outstream.WriteString(gen.joinManifests()) - } - if err != nil { - log.Fatalf("failed writing output: %v", err) - } -} - -type generator struct { - // settings - settings *resource.Settings - - // internal - configs []echo.Config - manifests map[string]string -} - -func newGenerator() generator { - // we read resource package settings to respsect --istio.test.versions - settings, err := resource.SettingsFromCommandLine("echogen") - if err != nil { - log.Fatalf("failed reading test framework settings: %v", err) - } - - return generator{ - settings: settings, - } -} - -func (g *generator) load(input string) error { - // deserialize - bytes, err := os.ReadFile(input) - if err != nil { - return fmt.Errorf("failed reading file: %v", err) - } - g.configs, err = echo.ParseConfigs(bytes) - if err != nil { - return fmt.Errorf("failed parsing file: %v", err) - } - // fill in defaults - c := cluster.NewFake("fake", "1", "20") - for i, cfg := range g.configs { - if len(cfg.Ports) == 0 { - cfg.Ports = ports.All() - } - cfg.Cluster = c - if err := cfg.FillDefaults(nil); err != nil { - return fmt.Errorf("failed filling defaults for %s: %v", cfg.ClusterLocalFQDN(), err) - } - g.configs[i] = cfg - } - return nil -} - -func (g *generator) generate() error { - outputByFQDN := map[string]string{} - var errs error - for _, cfg := range g.configs { - id := cfg.ClusterLocalFQDN() - // generate - svc, err := kube.GenerateService(cfg) - if err != nil { - errs = multierror.Append(errs, fmt.Errorf("failed generating service for %s: %v", id, err)) - continue - } - deployment, err := kube.GenerateDeployment(cfg, g.settings) - if err != nil { - errs = multierror.Append(errs, fmt.Errorf("failed generating deployment for %s: %v", id, err)) - continue - } - outputByFQDN[id] = yml.JoinString(svc, deployment) - // add namespace if specified - if cfg.Namespace.Name() != "" { - var err error - outputByFQDN[id], err = yml.ApplyNamespace(outputByFQDN[id], cfg.Namespace.Name()) - if err != nil { - return fmt.Errorf("error applying namespace to %s: %v", id, err) - } - } - - } - g.manifests = outputByFQDN - return errs -} - -func (g *generator) joinManifests() string { - var m []string - for _, yaml := range g.manifests { - m = append(m, yaml) - } - return yml.JoinString(m...) -} - -func (g *generator) writeOutputFile(path string, dir bool) error { - if dir { - // multi file - if err := os.Mkdir(path, 0o644); err != nil { - return fmt.Errorf("failed creating directory %s: %v", path, err) - } - for id, yaml := range g.manifests { - fname := id + ".yaml" - if err := os.WriteFile(fname, []byte(yaml), 0o644); err != nil { - return fmt.Errorf("failed writing %s: %v", fname, err) - } - } - } else if err := os.WriteFile(path, []byte(g.joinManifests()), 0o644); err != nil { - return fmt.Errorf("failed writing %s: %v", path, err) - } - return nil -} diff --git a/pkg/test/framework/components/echo/cmd/echogen/echogen_test.go b/pkg/test/framework/components/echo/cmd/echogen/echogen_test.go deleted file mode 100644 index f25d4284d..000000000 --- a/pkg/test/framework/components/echo/cmd/echogen/echogen_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "bytes" - "flag" - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/test/util" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/config" -) - -const ( - testCfg = "testdata/config.yaml" - goldenFile = "testdata/golden.yaml" -) - -func TestGenerate(t *testing.T) { - if !config.Parsed() { - config.Parse() - } - flag.VisitAll(func(f *flag.Flag) { - // these change often, don't want to regen golden - switch f.Name { - case "istio.test.tag": - _ = f.Value.Set("testtag") - case "istio.test.hub": - _ = f.Value.Set("testhub") - case "istio.test.pullpolicy": - _ = f.Value.Set("IfNotPresent") - } - }) - out := bytes.NewBuffer([]byte{}) - generate(testCfg, "", false, out) - util.CompareContent(t, out.Bytes(), goldenFile) -} diff --git a/pkg/test/framework/components/echo/cmd/echogen/testdata/config.yaml b/pkg/test/framework/components/echo/cmd/echogen/testdata/config.yaml deleted file mode 100644 index fc5cd97f6..000000000 --- a/pkg/test/framework/components/echo/cmd/echogen/testdata/config.yaml +++ /dev/null @@ -1,2 +0,0 @@ -- Service: a - Namespace: echo \ No newline at end of file diff --git a/pkg/test/framework/components/echo/cmd/echogen/testdata/golden.yaml b/pkg/test/framework/components/echo/cmd/echogen/testdata/golden.yaml deleted file mode 100644 index 294f9cb5b..000000000 --- a/pkg/test/framework/components/echo/cmd/echogen/testdata/golden.yaml +++ /dev/null @@ -1,176 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - labels: - app: a - name: a - namespace: echo -spec: - ports: - - name: http - port: 80 - targetPort: 18080 - - name: grpc - port: 7070 - targetPort: 17070 - - name: http2 - port: 85 - targetPort: 18085 - - name: tcp - port: 9090 - targetPort: 19090 - - name: https - port: 443 - targetPort: 18443 - - name: tcp-server - port: 9091 - targetPort: 16060 - - name: auto-tcp - port: 9092 - targetPort: 19091 - - name: auto-tcp-server - port: 9093 - targetPort: 16061 - - name: auto-http - port: 81 - targetPort: 18081 - - name: auto-grpc - port: 7071 - targetPort: 17071 - - name: auto-https - port: 9443 - targetPort: 19443 - - name: http-instance - port: 82 - targetPort: 18082 - - name: http-localhost - port: 84 - targetPort: 18084 - selector: - app: a ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: a-v1 - namespace: echo -spec: - replicas: 1 - selector: - matchLabels: - app: a - version: v1 - template: - metadata: - annotations: - prometheus.io/port: "15014" - prometheus.io/scrape: "true" - labels: - app: a - test.istio.io/class: standard - version: v1 - spec: - containers: - - image: auto - imagePullPolicy: IfNotPresent - name: istio-proxy - securityContext: - readOnlyRootFilesystem: false - - args: - - --metrics=15014 - - --cluster - - fake - - --port - - "18080" - - --grpc - - "17070" - - --port - - "18085" - - --tcp - - "19090" - - --port - - "18443" - - --tls=18443 - - --tcp - - "16060" - - --server-first=16060 - - --tcp - - "19091" - - --tcp - - "16061" - - --server-first=16061 - - --port - - "18081" - - --grpc - - "17071" - - --port - - "19443" - - --tls=19443 - - --port - - "18082" - - --bind-ip=18082 - - --port - - "18084" - - --bind-localhost=18084 - - --tcp - - "19092" - - --port - - "18083" - - --port - - "8080" - - --port - - "3333" - - --version - - v1 - - --istio-version - - "" - - --crt=/cert.crt - - --key=/cert.key - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - image: testhub/app:testtag - imagePullPolicy: IfNotPresent - livenessProbe: - failureThreshold: 10 - initialDelaySeconds: 10 - periodSeconds: 10 - tcpSocket: - port: tcp-health-port - name: app - ports: - - containerPort: 18080 - - containerPort: 17070 - - containerPort: 18085 - - containerPort: 19090 - - containerPort: 18443 - - containerPort: 16060 - - containerPort: 19091 - - containerPort: 16061 - - containerPort: 18081 - - containerPort: 17071 - - containerPort: 19443 - - containerPort: 18082 - - containerPort: 18084 - - containerPort: 19092 - - containerPort: 18083 - - containerPort: 8080 - - containerPort: 3333 - name: tcp-health-port - readinessProbe: - failureThreshold: 10 - httpGet: - path: / - port: 8080 - initialDelaySeconds: 1 - periodSeconds: 2 - securityContext: - runAsGroup: 1338 - runAsUser: 1338 - startupProbe: - failureThreshold: 10 - periodSeconds: 1 - tcpSocket: - port: tcp-health-port \ No newline at end of file diff --git a/pkg/test/framework/components/echo/common/call.go b/pkg/test/framework/components/echo/common/call.go deleted file mode 100644 index 1144d3286..000000000 --- a/pkg/test/framework/components/echo/common/call.go +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package common - -import ( - "context" - "fmt" - "net" - "strconv" - "time" -) - -import ( - echoclient "github.com/apache/dubbo-go-pixiu/pkg/test/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common/scheme" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/proto" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/server/forwarder" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -type sendFunc func(req *proto.ForwardEchoRequest) (echoclient.Responses, error) - -func callInternal(srcName string, from echo.Caller, opts echo.CallOptions, send sendFunc) (echo.CallResult, error) { - // Create the proto request. - req := newForwardRequest(opts) - sendAndValidate := func() (echo.CallResult, error) { - responses, err := send(req) - - // Verify the number of responses matches the expected. - if err == nil && len(responses) != opts.Count { - err = fmt.Errorf("unexpected number of responses: expected %d, received %d", - opts.Count, len(responses)) - } - - // Convert to a CallResult. - result := echo.CallResult{ - From: from, - Opts: opts, - Responses: responses, - } - - // Return the results from the validator. - err = opts.Check(result, err) - if err != nil { - err = fmt.Errorf("call failed from %s to %s (using %s): %v", - srcName, getTargetURL(opts), opts.Scheme, err) - } - - return result, err - } - - if opts.Retry.NoRetry { - // Retry is disabled, just send once. - t0 := time.Now() - defer scopes.Framework.Debugf("echo call complete with duration %v", time.Since(t0)) - return sendAndValidate() - } - - // Retry the call until it succeeds or times out. - var result echo.CallResult - var err error - _, _ = retry.UntilComplete(func() (interface{}, bool, error) { - result, err = sendAndValidate() - if err != nil { - return nil, false, err - } - return nil, true, nil - }, opts.Retry.Options...) - - return result, err -} - -func CallEcho(from echo.Caller, opts echo.CallOptions) (echo.CallResult, error) { - if err := opts.FillDefaults(); err != nil { - return echo.CallResult{}, err - } - - send := func(req *proto.ForwardEchoRequest) (echoclient.Responses, error) { - instance, err := forwarder.New(forwarder.Config{ - Request: req, - Proxy: opts.HTTP.HTTPProxy, - }) - if err != nil { - return nil, err - } - ctx, cancel := context.WithTimeout(context.Background(), opts.Timeout) - defer func() { - cancel() - _ = instance.Close() - }() - ret, err := instance.Run(ctx) - if err != nil { - return nil, err - } - resp := echoclient.ParseResponses(req, ret) - return resp, nil - } - return callInternal("TestRunner", from, opts, send) -} - -func newForwardRequest(opts echo.CallOptions) *proto.ForwardEchoRequest { - return &proto.ForwardEchoRequest{ - Url: getTargetURL(opts), - Count: int32(opts.Count), - Headers: common.HTTPToProtoHeaders(opts.HTTP.Headers), - TimeoutMicros: common.DurationToMicros(opts.Timeout), - Message: opts.Message, - ExpectedResponse: opts.TCP.ExpectedResponse, - Http2: opts.HTTP.HTTP2, - Http3: opts.HTTP.HTTP3, - Method: opts.HTTP.Method, - ServerFirst: opts.Port.ServerFirst, - Cert: opts.TLS.Cert, - Key: opts.TLS.Key, - CaCert: opts.TLS.CaCert, - CertFile: opts.TLS.CertFile, - KeyFile: opts.TLS.KeyFile, - CaCertFile: opts.TLS.CaCertFile, - InsecureSkipVerify: opts.TLS.InsecureSkipVerify, - Alpn: getProtoALPN(opts.TLS.Alpn), - FollowRedirects: opts.HTTP.FollowRedirects, - ServerName: opts.TLS.ServerName, - } -} - -func getProtoALPN(alpn []string) *proto.Alpn { - if alpn != nil { - return &proto.Alpn{ - Value: alpn, - } - } - return nil -} - -// EchoClientProvider provides dynamic creation of Echo clients. This allows retries to potentially make -// use of different (ready) workloads for forward requests. -type EchoClientProvider func() (*echoclient.Client, error) - -func ForwardEcho(srcName string, from echo.Caller, opts echo.CallOptions, clientProvider EchoClientProvider) (echo.CallResult, error) { - if err := opts.FillDefaults(); err != nil { - return echo.CallResult{}, err - } - - res, err := callInternal(srcName, from, opts, func(req *proto.ForwardEchoRequest) (echoclient.Responses, error) { - c, err := clientProvider() - if err != nil { - return nil, err - } - return c.ForwardEcho(context.Background(), req) - }) - if err != nil { - return echo.CallResult{}, fmt.Errorf("failed calling %s->'%s': %v", - srcName, - getTargetURL(opts), - err) - } - return res, nil -} - -func getTargetURL(opts echo.CallOptions) string { - port := opts.Port.ServicePort - addressAndPort := net.JoinHostPort(opts.Address, strconv.Itoa(port)) - // Forward a request from 'this' service to the destination service. - switch opts.Scheme { - case scheme.DNS: - return fmt.Sprintf("%s://%s", string(opts.Scheme), opts.Address) - case scheme.TCP, scheme.GRPC: - return fmt.Sprintf("%s://%s", string(opts.Scheme), addressAndPort) - case scheme.XDS: - return fmt.Sprintf("%s:///%s", string(opts.Scheme), addressAndPort) - default: - return fmt.Sprintf("%s://%s%s", string(opts.Scheme), addressAndPort, opts.HTTP.Path) - } -} diff --git a/pkg/test/framework/components/echo/common/deployment/echos.go b/pkg/test/framework/components/echo/common/deployment/echos.go deleted file mode 100644 index 70821457e..000000000 --- a/pkg/test/framework/components/echo/common/deployment/echos.go +++ /dev/null @@ -1,319 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package deployment - -import ( - "context" - "fmt" - "sort" - "strings" -) - -import ( - "github.com/hashicorp/go-multierror" - "golang.org/x/sync/errgroup" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/common/ports" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/deployment" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/namespace" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -// Config for new echo deployment. -type Config struct { - // Echos is the target Echos for the newly created echo apps. If nil, a new Echos - // instance will be created. - Echos *Echos - - // NamespaceCount indicates the number of echo namespaces to be generated. - // Ignored if Namespaces is non-empty. Defaults to 1. - NamespaceCount int - - // Namespaces is the user-provided list of echo namespaces. If empty, NamespaceCount - // namespaces will be generated. - Namespaces []namespace.Instance - - // NoExternalNamespace if true, no external namespace will be generated and no external echo - // instance will be deployed. Ignored if ExternalNamespace is non-nil. - NoExternalNamespace bool - - // ExternalNamespace the namespace to use for the external deployment. If nil, a namespace - // will be generated unless NoExternalNamespace is specified. - ExternalNamespace namespace.Instance -} - -func (c *Config) fillDefaults(ctx resource.Context) error { - // Create the namespaces concurrently. - g, _ := errgroup.WithContext(context.TODO()) - - if c.Echos == nil { - c.Echos = &Echos{} - } - - if len(c.Namespaces) > 0 { - c.NamespaceCount = len(c.Namespaces) - } else if c.NamespaceCount <= 0 { - c.NamespaceCount = 1 - } - - // Create the echo namespaces. - if len(c.Namespaces) == 0 { - c.Namespaces = make([]namespace.Instance, c.NamespaceCount) - if c.NamespaceCount == 1 { - // If only using a single namespace, preserve the "echo" prefix. - g.Go(func() (err error) { - c.Namespaces[0], err = namespace.New(ctx, namespace.Config{ - Prefix: "echo", - Inject: true, - }) - return - }) - } else { - for i := 0; i < c.NamespaceCount; i++ { - i := i - g.Go(func() (err error) { - c.Namespaces[i], err = namespace.New(ctx, namespace.Config{ - Prefix: fmt.Sprintf("echo%d", i+1), - Inject: true, - }) - return - }) - } - } - } - - // Create the external namespace, if necessary. - if c.ExternalNamespace == nil && !c.NoExternalNamespace { - g.Go(func() (err error) { - c.ExternalNamespace, err = namespace.New(ctx, namespace.Config{ - Prefix: "external", - Inject: false, - }) - return - }) - } - - // Wait for the namespaces to be created. - return g.Wait() -} - -// SingleNamespaceView is a simplified view of Echos for tests that only require a single namespace. -type SingleNamespaceView struct { - // Include the echos at the top-level, so there is no need for accessing sub-structures. - EchoNamespace - - // External (out-of-mesh) deployments - External External - - // All echo instances - All echo.Services -} - -// TwoNamespaceView is a simplified view of Echos for tests that require 2 namespaces. -type TwoNamespaceView struct { - // Ns1 contains the echo deployments in the first namespace - Ns1 EchoNamespace - - // Ns2 contains the echo deployments in the second namespace - Ns2 EchoNamespace - - // Ns1AndNs2 contains just the echo services in Ns1 and Ns2 (excludes External). - Ns1AndNs2 echo.Services - - // External (out-of-mesh) deployments - External External - - // All echo instances - All echo.Services -} - -// Echos is a common set of echo deployments to support integration testing. -type Echos struct { - // NS is the list of echo namespaces. - NS []EchoNamespace - - // External (out-of-mesh) deployments - External External - - // All echo instances. - All echo.Services -} - -// New echo deployment with the given configuration. -func New(ctx resource.Context, cfg Config) (*Echos, error) { - if err := cfg.fillDefaults(ctx); err != nil { - return nil, err - } - - apps := cfg.Echos - apps.NS = make([]EchoNamespace, len(cfg.Namespaces)) - for i, ns := range cfg.Namespaces { - apps.NS[i].Namespace = ns - } - apps.External.Namespace = cfg.ExternalNamespace - - builder := deployment.New(ctx).WithClusters(ctx.Clusters()...) - for _, n := range apps.NS { - builder = n.build(ctx, builder) - } - - if !cfg.NoExternalNamespace { - builder = apps.External.build(builder) - } - - echos, err := builder.Build() - if err != nil { - return nil, err - } - - apps.All = echos.Services() - - g := multierror.Group{} - for i := 0; i < len(apps.NS); i++ { - i := i - g.Go(func() error { - return apps.NS[i].loadValues(ctx, echos, apps) - }) - } - - if !cfg.NoExternalNamespace { - g.Go(func() error { - return apps.External.loadValues(echos) - }) - } - - if err := g.Wait().ErrorOrNil(); err != nil { - return nil, err - } - - return apps, nil -} - -// NewOrFail calls New and fails if an error is returned. -func NewOrFail(t test.Failer, ctx resource.Context, cfg Config) *Echos { - t.Helper() - out, err := New(ctx, cfg) - if err != nil { - t.Fatal(err) - } - return out -} - -// SingleNamespaceView converts this Echos into a SingleNamespaceView. -func (d Echos) SingleNamespaceView() SingleNamespaceView { - return SingleNamespaceView{ - EchoNamespace: d.NS[0], - External: d.External, - All: d.NS[0].All.Append(d.External.All.Services()), - } -} - -// TwoNamespaceView converts this Echos into a TwoNamespaceView. -func (d Echos) TwoNamespaceView() TwoNamespaceView { - ns1AndNs2 := d.NS[0].All.Append(d.NS[1].All) - return TwoNamespaceView{ - Ns1: d.NS[0], - Ns2: d.NS[1], - Ns1AndNs2: ns1AndNs2, - External: d.External, - All: ns1AndNs2.Append(d.External.All.Services()), - } -} - -func (d Echos) namespaces(excludes ...namespace.Instance) []string { - var out []string - for _, n := range d.NS { - include := true - for _, e := range excludes { - if n.Namespace.Name() == e.Name() { - include = false - break - } - } - if include { - out = append(out, n.Namespace.Name()) - } - } - - sort.Strings(out) - return out -} - -func serviceEntryPorts() []echo.Port { - var res []echo.Port - for _, p := range ports.All().GetServicePorts() { - if strings.HasPrefix(p.Name, "auto") { - // The protocol needs to be set in common.EchoPorts to configure the echo deployment - // But for service entry, we want to ensure we set it to "" which will use sniffing - p.Protocol = "" - } - res = append(res, p) - } - return res -} - -// SetupSingleNamespace calls Setup and returns a SingleNamespaceView. -func SetupSingleNamespace(view *SingleNamespaceView) resource.SetupFn { - return func(ctx resource.Context) error { - // Perform a setup with 1 namespace. - var apps Echos - if err := Setup(&apps, Config{NamespaceCount: 1})(ctx); err != nil { - return err - } - - // Store the view. - *view = apps.SingleNamespaceView() - return nil - } -} - -// SetupTwoNamespaces calls Setup and returns a TwoNamespaceView. -func SetupTwoNamespaces(view *TwoNamespaceView) resource.SetupFn { - return func(ctx resource.Context) error { - // Perform a setup with 2 namespaces. - var apps Echos - if err := Setup(&apps, Config{NamespaceCount: 2})(ctx); err != nil { - return err - } - - // Store the view. - *view = apps.TwoNamespaceView() - return nil - } -} - -// Setup function for writing to a global deployment variable. -func Setup(apps *Echos, cfg Config) resource.SetupFn { - return func(ctx resource.Context) error { - // Set the target for the deployments. - cfg.Echos = apps - - _, err := New(ctx, cfg) - if err != nil { - return err - } - - return nil - } -} - -// TODO(nmittler): should ctx.Settings().Skip(echo.Delta) do all of this? -func skipDeltaXDS(ctx resource.Context) bool { - return ctx.Settings().Skip(echo.Delta) || !ctx.Settings().Revisions.AtLeast("1.12") -} diff --git a/pkg/test/framework/components/echo/common/deployment/external.go b/pkg/test/framework/components/echo/common/deployment/external.go deleted file mode 100644 index 12e511fb2..000000000 --- a/pkg/test/framework/components/echo/common/deployment/external.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package deployment - -import ( - "strconv" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/common/ports" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/deployment" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/match" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/namespace" -) - -const ( - ExternalSvc = "external" - externalHostname = "fake.external.com" -) - -type External struct { - // Namespace where external echo app will be deployed - Namespace namespace.Instance - - // All external echo instances with no sidecar injected - All echo.Instances -} - -func (e External) build(b deployment.Builder) deployment.Builder { - return b.WithConfig(echo.Config{ - Service: ExternalSvc, - Namespace: e.Namespace, - DefaultHostHeader: externalHostname, - Ports: ports.All(), - Subsets: []echo.SubsetConfig{ - { - Annotations: map[echo.Annotation]*echo.AnnotationValue{ - echo.SidecarInject: { - Value: strconv.FormatBool(false), - }, - }, - }, - }, - }) -} - -func (e *External) loadValues(echos echo.Instances) error { - e.All = match.ServiceName(echo.NamespacedName{Name: ExternalSvc, Namespace: e.Namespace}).GetMatches(echos) - return nil -} diff --git a/pkg/test/framework/components/echo/common/deployment/namespace.go b/pkg/test/framework/components/echo/common/deployment/namespace.go deleted file mode 100644 index bb1e1c632..000000000 --- a/pkg/test/framework/components/echo/common/deployment/namespace.go +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package deployment - -import ( - "strconv" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/common/ports" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/deployment" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/match" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/namespace" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -const ( - ASvc = "a" - BSvc = "b" - CSvc = "c" - TproxySvc = "tproxy" - VMSvc = "vm" - HeadlessSvc = "headless" - StatefulSetSvc = "statefulset" - ProxylessGRPCSvc = "proxyless-grpc" - NakedSvc = "naked" - DeltaSvc = "delta" -) - -// EchoNamespace contains the echo instances for a single namespace. -type EchoNamespace struct { - // Namespace where the services are deployed. - Namespace namespace.Instance - - // Standard echo app to be used by tests - A echo.Instances - // Standard echo app to be used by tests - B echo.Instances - // Standard echo app to be used by tests - C echo.Instances - // Standard echo app with TPROXY interception mode to be used by tests - Tproxy echo.Instances - // Headless echo app to be used by tests - Headless echo.Instances - // StatefulSet echo app to be used by tests - StatefulSet echo.Instances - // ProxylessGRPC echo app to be used by tests - ProxylessGRPC echo.Instances - // Echo app to be used by tests, with no sidecar injected - Naked echo.Instances - // A virtual machine echo app (only deployed to one cluster) - VM echo.Instances - // DeltaXDS echo app uses the delta XDS protocol. This should be functionally equivalent to A. - DeltaXDS echo.Instances - - // All echo apps in this namespace - All echo.Services -} - -func (n EchoNamespace) build(t resource.Context, b deployment.Builder) deployment.Builder { - b = b.WithConfig(echo.Config{ - Service: ASvc, - Namespace: n.Namespace, - ServiceAccount: true, - Ports: ports.All(), - Subsets: []echo.SubsetConfig{{}}, - Locality: "region.zone.subzone", - }). - WithConfig(echo.Config{ - Service: BSvc, - Namespace: n.Namespace, - ServiceAccount: true, - Ports: ports.All(), - Subsets: []echo.SubsetConfig{{}}, - }). - WithConfig(echo.Config{ - Service: CSvc, - Namespace: n.Namespace, - ServiceAccount: true, - Ports: ports.All(), - Subsets: []echo.SubsetConfig{{}}, - }). - WithConfig(echo.Config{ - Service: HeadlessSvc, - Namespace: n.Namespace, - ServiceAccount: true, - Headless: true, - Ports: ports.Headless(), - Subsets: []echo.SubsetConfig{{}}, - }). - WithConfig(echo.Config{ - Service: StatefulSetSvc, - Namespace: n.Namespace, - ServiceAccount: true, - Headless: true, - StatefulSet: true, - Ports: ports.Headless(), - Subsets: []echo.SubsetConfig{{}}, - }). - WithConfig(echo.Config{ - Service: NakedSvc, - Namespace: n.Namespace, - ServiceAccount: true, - Ports: ports.All(), - Subsets: []echo.SubsetConfig{ - { - Annotations: map[echo.Annotation]*echo.AnnotationValue{ - echo.SidecarInject: { - Value: strconv.FormatBool(false), - }, - }, - }, - }, - }). - WithConfig(echo.Config{ - Service: TproxySvc, - Namespace: n.Namespace, - ServiceAccount: true, - Ports: ports.All(), - Subsets: []echo.SubsetConfig{{ - Annotations: echo.NewAnnotations().Set(echo.SidecarInterceptionMode, "TPROXY"), - }}, - }). - WithConfig(echo.Config{ - Service: VMSvc, - Namespace: n.Namespace, - ServiceAccount: true, - Ports: ports.All(), - DeployAsVM: true, - AutoRegisterVM: true, - Subsets: []echo.SubsetConfig{{}}, - }) - - if !skipDeltaXDS(t) { - b = b. - WithConfig(echo.Config{ - Service: DeltaSvc, - Namespace: n.Namespace, - ServiceAccount: true, - Ports: ports.All(), - Subsets: []echo.SubsetConfig{{ - Annotations: echo.NewAnnotations().Set(echo.SidecarProxyConfig, `proxyMetadata: - ISTIO_DELTA_XDS: "true"`), - }}, - }) - } - - if !t.Clusters().IsMulticluster() { - b = b. - // TODO when agent handles secure control-plane connection for grpc-less, deploy to "remote" clusters - WithConfig(echo.Config{ - Service: ProxylessGRPCSvc, - Namespace: n.Namespace, - ServiceAccount: true, - Ports: ports.All(), - Subsets: []echo.SubsetConfig{ - { - Annotations: map[echo.Annotation]*echo.AnnotationValue{ - echo.SidecarInjectTemplates: { - Value: "grpc-agent", - }, - }, - }, - }, - }) - } - return b -} - -func (n *EchoNamespace) loadValues(t resource.Context, echos echo.Instances, d *Echos) error { - ns := n.Namespace - - all := func(is echo.Instances) echo.Instances { - if len(is) > 0 { - n.All = append(n.All, is) - return is - } - return nil - } - - n.A = all(match.ServiceName(echo.NamespacedName{Name: ASvc, Namespace: ns}).GetMatches(echos)) - n.B = all(match.ServiceName(echo.NamespacedName{Name: BSvc, Namespace: ns}).GetMatches(echos)) - n.C = all(match.ServiceName(echo.NamespacedName{Name: CSvc, Namespace: ns}).GetMatches(echos)) - n.Tproxy = all(match.ServiceName(echo.NamespacedName{Name: TproxySvc, Namespace: ns}).GetMatches(echos)) - n.Headless = all(match.ServiceName(echo.NamespacedName{Name: HeadlessSvc, Namespace: ns}).GetMatches(echos)) - n.StatefulSet = all(match.ServiceName(echo.NamespacedName{Name: StatefulSetSvc, Namespace: ns}).GetMatches(echos)) - n.Naked = all(match.ServiceName(echo.NamespacedName{Name: NakedSvc, Namespace: ns}).GetMatches(echos)) - n.ProxylessGRPC = all(match.ServiceName(echo.NamespacedName{Name: ProxylessGRPCSvc, Namespace: ns}).GetMatches(echos)) - if !t.Settings().Skip(echo.VM) { - n.VM = all(match.ServiceName(echo.NamespacedName{Name: VMSvc, Namespace: ns}).GetMatches(echos)) - } - if !skipDeltaXDS(t) { - n.DeltaXDS = all(match.ServiceName(echo.NamespacedName{Name: DeltaSvc, Namespace: ns}).GetMatches(echos)) - } - - // Restrict egress from this namespace to only those endpoints in the same Echos. - cfg := t.ConfigIstio().New() - cfg.Eval(ns.Name(), map[string]interface{}{ - "otherNS": d.namespaces(n.Namespace), - }, ` -apiVersion: networking.istio.io/v1alpha3 -kind: Sidecar -metadata: - name: restrict-to-namespace -spec: - egress: - - hosts: - - "./*" - - "dubbo-system/*" -{{ range $ns := .otherNS }} - - "{{ $ns }}/*" -{{ end }} -`) - - // Create a ServiceEntry to allow apps in this namespace to talk to the external service. - if d.External.Namespace != nil { - cfg.Eval(ns.Name(), map[string]interface{}{ - "Namespace": d.External.Namespace.Name(), - "Hostname": externalHostname, - "Ports": serviceEntryPorts(), - }, `apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: external-service -spec: - hosts: - - {{.Hostname}} - location: MESH_EXTERNAL - resolution: DNS - endpoints: - - address: external.{{.Namespace}}.svc.cluster.local - ports: - - name: http-tls-origination - number: 8888 - protocol: http - targetPort: 443 - - name: http2-tls-origination - number: 8882 - protocol: http2 - targetPort: 443 -{{- range $i, $p := .Ports }} - - name: {{$p.Name}} - number: {{$p.ServicePort}} - protocol: "{{$p.Protocol}}" -{{- end }} -`) - } - - return cfg.Apply(resource.NoCleanup) -} diff --git a/pkg/test/framework/components/echo/common/ports/ports.go b/pkg/test/framework/components/echo/common/ports/ports.go deleted file mode 100644 index b877030d4..000000000 --- a/pkg/test/framework/components/echo/common/ports/ports.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ports - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" -) - -// Port names. -const ( - HTTP = "http" - GRPC = "grpc" - HTTP2 = "http2" - TCP = "tcp" - HTTPS = "https" - TCPServer = "tcp-server" - AutoTCP = "auto-tcp" - AutoTCPServer = "auto-tcp-server" - AutoHTTP = "auto-http" - AutoGRPC = "auto-grpc" - AutoHTTPS = "auto-https" - HTTPInstance = "http-instance" - HTTPLocalHost = "http-localhost" - TCPWorkloadOnly = "tcp-wl-only" - HTTPWorkloadOnly = "http-wl-only" -) - -// All the common ports. -func All() echo.Ports { - return echo.Ports{ - {Name: HTTP, Protocol: protocol.HTTP, ServicePort: 80, WorkloadPort: 18080}, - {Name: GRPC, Protocol: protocol.GRPC, ServicePort: 7070, WorkloadPort: 17070}, - {Name: HTTP2, Protocol: protocol.HTTP, ServicePort: 85, WorkloadPort: 18085}, - {Name: TCP, Protocol: protocol.TCP, ServicePort: 9090, WorkloadPort: 19090}, - {Name: HTTPS, Protocol: protocol.HTTPS, ServicePort: 443, WorkloadPort: 18443, TLS: true}, - {Name: TCPServer, Protocol: protocol.TCP, ServicePort: 9091, WorkloadPort: 16060, ServerFirst: true}, - {Name: AutoTCP, Protocol: protocol.TCP, ServicePort: 9092, WorkloadPort: 19091}, - {Name: AutoTCPServer, Protocol: protocol.TCP, ServicePort: 9093, WorkloadPort: 16061, ServerFirst: true}, - {Name: AutoHTTP, Protocol: protocol.HTTP, ServicePort: 81, WorkloadPort: 18081}, - {Name: AutoGRPC, Protocol: protocol.GRPC, ServicePort: 7071, WorkloadPort: 17071}, - {Name: AutoHTTPS, Protocol: protocol.HTTPS, ServicePort: 9443, WorkloadPort: 19443, TLS: true}, - {Name: HTTPInstance, Protocol: protocol.HTTP, ServicePort: 82, WorkloadPort: 18082, InstanceIP: true}, - {Name: HTTPLocalHost, Protocol: protocol.HTTP, ServicePort: 84, WorkloadPort: 18084, LocalhostIP: true}, - {Name: TCPWorkloadOnly, Protocol: protocol.TCP, ServicePort: echo.NoServicePort, WorkloadPort: 19092}, - {Name: HTTPWorkloadOnly, Protocol: protocol.HTTP, ServicePort: echo.NoServicePort, WorkloadPort: 18083}, - } -} - -// Headless returns a modified version of All for use with headless services. -func Headless() echo.Ports { - all := All() - headlessPorts := make([]echo.Port, len(all)) - for i, p := range all { - p.ServicePort = p.WorkloadPort - headlessPorts[i] = p - } - return headlessPorts -} diff --git a/pkg/test/framework/components/echo/config.go b/pkg/test/framework/components/echo/config.go deleted file mode 100644 index 2f0f35539..000000000 --- a/pkg/test/framework/components/echo/config.go +++ /dev/null @@ -1,469 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -import ( - "encoding/json" - "fmt" - "strings" - "time" -) - -import ( - "github.com/mitchellh/copystructure" - "gopkg.in/yaml.v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/namespace" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -// Cluster that can deploy echo instances. -// TODO putting this here for now to deal with circular imports, needs to be moved -type Cluster interface { - cluster.Cluster - - CanDeploy(Config) (Config, bool) -} - -// Configurable is and object that has Config. -type Configurable interface { - Config() Config - - // NamespacedName is a short form for Config().NamespacedName(). - NamespacedName() NamespacedName - - // PortForName is a short form for Config().Ports.MustForName(). - PortForName(name string) Port -} - -type VMDistro = string - -const ( - UbuntuXenial VMDistro = "UbuntuXenial" - UbuntuJammy VMDistro = "UbuntuJammy" - Debian11 VMDistro = "Debian9" - Centos7 VMDistro = "Centos7" - Rockylinux8 VMDistro = "Centos8" - - DefaultVMDistro = UbuntuJammy -) - -// Config defines the options for creating an Echo component. -// nolint: maligned -type Config struct { - // Namespace of the echo Instance. If not provided, a default namespace "apps" is used. - Namespace namespace.Instance - - // DefaultHostHeader overrides the default Host header for calls (`service.namespace.svc.cluster.local`) - DefaultHostHeader string - - // Domain of the echo Instance. If not provided, a default will be selected. - Domain string - - // Service indicates the service name of the Echo application. - Service string - - // Version indicates the version path for calls to the Echo application. - Version string - - // Locality (k8s only) indicates the locality of the deployed app. - Locality string - - // Headless (k8s only) indicates that no ClusterIP should be specified. - Headless bool - - // StatefulSet indicates that the pod should be backed by a StatefulSet. This implies Headless=true - // as well. - StatefulSet bool - - // StaticAddress for some echo implementations is an address locally reachable within - // the test framework and from the echo Cluster's network. - StaticAddresses []string - - // ServiceAccount (k8s only) indicates that a service account should be created - // for the deployment. - ServiceAccount bool - - // Ports for this application. Port numbers may or may not be used, depending - // on the implementation. - Ports Ports - - // ServiceAnnotations is annotations on service object. - ServiceAnnotations Annotations - - // ReadinessTimeout specifies the timeout that we wait the application to - // become ready. - ReadinessTimeout time.Duration - - // ReadinessTCPPort if set, use this port for the TCP readiness probe (instead of using a HTTP probe). - ReadinessTCPPort string - - // ReadinessGRPCPort if set, use this port for the GRPC readiness probe (instead of using a HTTP probe). - ReadinessGRPCPort string - - // Subsets contains the list of Subsets config belonging to this echo - // service instance. - Subsets []SubsetConfig - - // Cluster to be used in a multicluster environment - Cluster cluster.Cluster - - // TLS settings for echo server - TLSSettings *common.TLSSettings - - // If enabled, echo will be deployed as a "VM". This means it will run Envoy in the same pod as echo, - // disable sidecar injection, etc. - DeployAsVM bool - - // If enabled, ISTIO_META_AUTO_REGISTER_GROUP will be set on the VM and the WorkloadEntry will be created automatically. - AutoRegisterVM bool - - // The distro to use for a VM. For fake VMs, this maps to docker images. - VMDistro VMDistro - - // The set of environment variables to set for `DeployAsVM` instances. - VMEnvironment map[string]string - - // If enabled, an additional ext-authz container will be included in the deployment. This is mainly used to test - // the CUSTOM authorization policy when the ext-authz server is deployed locally with the application container in - // the same pod. - IncludeExtAuthz bool - - // IPFamily for the service. This is optional field. Mainly is used for dual stack testing - IPFamilies string - - // IPFamilyPolicy. This is optional field. Mainly is used for dual stack testing. - IPFamilyPolicy string -} - -// NamespaceName returns the string name of the namespace. -func (c Config) NamespaceName() string { - if c.Namespace != nil { - return c.Namespace.Name() - } - return "" -} - -// NamespacedName returns the namespaced name for the service. -func (c Config) NamespacedName() NamespacedName { - return NamespacedName{ - Name: c.Service, - Namespace: c.Namespace, - } -} - -// ServiceAccountString returns the service account string for this service. -func (c Config) ServiceAccountString() string { - return "cluster.local/ns/" + c.NamespaceName() + "/sa/" + c.Service -} - -// SubsetConfig is the config for a group of Subsets (e.g. Kubernetes deployment). -type SubsetConfig struct { - // The version of the deployment. - Version string - // Annotations provides metadata hints for deployment of the instance. - Annotations Annotations - // TODO: port more into workload config. -} - -// String implements the Configuration interface (which implements fmt.Stringer) -func (c Config) String() string { - return fmt.Sprint("{service: ", c.Service, ", version: ", c.Version, "}") -} - -// ClusterLocalFQDN returns the fully qualified domain name for cluster-local host. -func (c Config) ClusterLocalFQDN() string { - out := c.Service - if c.Namespace != nil { - out += "." + c.Namespace.Name() + ".svc" - } else { - out += ".default.svc" - } - if c.Domain != "" { - out += "." + c.Domain - } - return out -} - -// ClusterSetLocalFQDN returns the fully qualified domain name for the Kubernetes -// Multi-Cluster Services (MCS) Cluster Set host. -func (c Config) ClusterSetLocalFQDN() string { - out := c.Service - if c.Namespace != nil { - out += "." + c.Namespace.Name() + ".svc" - } else { - out += ".default.svc" - } - out += "." + constants.DefaultClusterSetLocalDomain - return out -} - -// HostHeader returns the Host header that will be used for calls to this service. -func (c Config) HostHeader() string { - if c.DefaultHostHeader != "" { - return c.DefaultHostHeader - } - return c.ClusterLocalFQDN() -} - -func (c Config) IsHeadless() bool { - return c.Headless -} - -func (c Config) IsStatefulSet() bool { - return c.StatefulSet -} - -// IsNaked checks if the config has no sidecar. -// Note: mixed workloads are considered 'naked' -func (c Config) IsNaked() bool { - for _, s := range c.Subsets { - if s.Annotations == nil { - continue - } - if !s.Annotations.GetBool(SidecarInject) { - return true - } - } - return false -} - -func (c Config) IsProxylessGRPC() bool { - // TODO make these check if any subset has a matching annotation - return len(c.Subsets) > 0 && c.Subsets[0].Annotations != nil && strings.HasPrefix(c.Subsets[0].Annotations.Get(SidecarInjectTemplates), "grpc-") -} - -func (c Config) IsTProxy() bool { - // TODO this could be HasCustomInjectionMode - return len(c.Subsets) > 0 && c.Subsets[0].Annotations != nil && c.Subsets[0].Annotations.Get(SidecarInterceptionMode) == "TPROXY" -} - -func (c Config) IsVM() bool { - return c.DeployAsVM -} - -func (c Config) IsDelta() bool { - // TODO this doesn't hold if delta is on by default - return len(c.Subsets) > 0 && c.Subsets[0].Annotations != nil && strings.Contains(c.Subsets[0].Annotations.Get(SidecarProxyConfig), "ISTIO_DELTA_XDS") -} - -// IsRegularPod returns true if the echo pod is not any of the following: -// - VM -// - Naked -// - Headless -// - TProxy -// - Multi-Subset -func (c Config) IsRegularPod() bool { - return len(c.Subsets) == 1 && !c.IsVM() && !c.IsTProxy() && !c.IsNaked() && !c.IsHeadless() && !c.IsStatefulSet() && !c.IsProxylessGRPC() -} - -// DeepCopy creates a clone of IstioEndpoint. -func (c Config) DeepCopy() Config { - newc := c - newc.Cluster = nil - newc = copyInternal(newc).(Config) - newc.Cluster = c.Cluster - newc.Namespace = c.Namespace - return newc -} - -func (c Config) IsExternal() bool { - return c.HostHeader() != c.ClusterLocalFQDN() -} - -const ( - defaultService = "echo" - defaultVersion = "v1" - defaultNamespace = "echo" - defaultDomain = constants.DefaultKubernetesDomain -) - -func (c *Config) FillDefaults(ctx resource.Context) (err error) { - if c.Service == "" { - c.Service = defaultService - } - - if c.Version == "" { - c.Version = defaultVersion - } - - if c.Domain == "" { - c.Domain = defaultDomain - } - - if c.VMDistro == "" { - c.VMDistro = DefaultVMDistro - } - if c.StatefulSet { - // Statefulset requires headless - c.Headless = true - } - - // Convert legacy config to workload oritended. - if c.Subsets == nil { - c.Subsets = []SubsetConfig{ - { - Version: c.Version, - }, - } - } - - for i := range c.Subsets { - if c.Subsets[i].Version == "" { - c.Subsets[i].Version = c.Version - } - } - c.addPortIfMissing(protocol.GRPC) - // If no namespace was provided, use the default. - if c.Namespace == nil && ctx != nil { - nsConfig := namespace.Config{ - Prefix: defaultNamespace, - Inject: true, - } - if c.Namespace, err = namespace.New(ctx, nsConfig); err != nil { - return err - } - } - - // Make a copy of the ports array. This avoids potential corruption if multiple Echo - // Instances share the same underlying ports array. - c.Ports = append([]Port{}, c.Ports...) - - // Mark all user-defined ports as used, so the port generator won't assign them. - portGen := newPortGenerators() - for _, p := range c.Ports { - if p.ServicePort > 0 { - if portGen.Service.IsUsed(p.ServicePort) { - return fmt.Errorf("failed configuring port %s: service port already used %d", p.Name, p.ServicePort) - } - portGen.Service.SetUsed(p.ServicePort) - } - if p.WorkloadPort > 0 { - if portGen.Instance.IsUsed(p.WorkloadPort) { - return fmt.Errorf("failed configuring port %s: instance port already used %d", p.Name, p.WorkloadPort) - } - portGen.Instance.SetUsed(p.WorkloadPort) - } - } - - // Second pass: try to make unassigned instance ports match service port. - for i, p := range c.Ports { - if p.WorkloadPort == 0 && p.ServicePort > 0 && !portGen.Instance.IsUsed(p.ServicePort) { - c.Ports[i].WorkloadPort = p.ServicePort - portGen.Instance.SetUsed(p.ServicePort) - } - } - - // Final pass: assign default values for any ports that haven't been specified. - for i, p := range c.Ports { - if p.ServicePort == 0 { - c.Ports[i].ServicePort = portGen.Service.Next(p.Protocol) - } - if p.WorkloadPort == 0 { - c.Ports[i].WorkloadPort = portGen.Instance.Next(p.Protocol) - } - } - - // If readiness probe is not specified by a test, wait a long time - // Waiting forever would cause the test to timeout and lose logs - if c.ReadinessTimeout == 0 { - c.ReadinessTimeout = DefaultReadinessTimeout() - } - - return nil -} - -// addPortIfMissing adds a port for the given protocol if none was found. -func (c *Config) addPortIfMissing(protocol protocol.Instance) { - if _, found := c.Ports.ForProtocol(protocol); !found { - c.Ports = append([]Port{ - { - Name: strings.ToLower(string(protocol)), - Protocol: protocol, - }, - }, c.Ports...) - } -} - -func copyInternal(v interface{}) interface{} { - copied, err := copystructure.Copy(v) - if err != nil { - // There are 2 locations where errors are generated in copystructure.Copy: - // * The reflection walk over the structure fails, which should never happen - // * A configurable copy function returns an error. This is only used for copying times, which never returns an error. - // Therefore, this should never happen - panic(err) - } - return copied -} - -// ParseConfigs unmarshals the given YAML bytes into []Config, using a namespace.Static rather -// than attempting to Claim the configured namespace. -func ParseConfigs(bytes []byte) ([]Config, error) { - // parse into flexible type, so we can remove Namespace and parse that ourselves - raw := make([]map[string]interface{}, 0) - if err := yaml.Unmarshal(bytes, &raw); err != nil { - return nil, err - } - configs := make([]Config, len(raw)) - - for i, raw := range raw { - if ns, ok := raw["Namespace"]; ok { - configs[i].Namespace = namespace.Static(fmt.Sprint(ns)) - delete(raw, "Namespace") - } - } - - // unmarshal again after Namespace stripped is stripped, to avoid unmarshal error - modifiedBytes, err := json.Marshal(raw) - if err != nil { - return nil, err - } - if err := json.Unmarshal(modifiedBytes, &configs); err != nil { - return nil, nil - } - - return configs, nil -} - -// WorkloadClass returns the type of workload a given config is. -func (c Config) WorkloadClass() WorkloadClass { - if c.IsProxylessGRPC() { - return Proxyless - } else if c.IsVM() { - return VM - } else if c.IsTProxy() { - return TProxy - } else if c.IsNaked() { - return Naked - } else if c.IsExternal() { - return External - } else if c.IsStatefulSet() { - return StatefulSet - } else if c.IsDelta() { - // TODO remove if delta is on by default - return Delta - } - if c.IsHeadless() { - return Headless - } - return Standard -} diff --git a/pkg/test/framework/components/echo/config_test.go b/pkg/test/framework/components/echo/config_test.go deleted file mode 100644 index 13f76618d..000000000 --- a/pkg/test/framework/components/echo/config_test.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -import ( - "testing" -) - -import ( - "github.com/google/go-cmp/cmp" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/namespace" -) - -func TestParseConfigs(t *testing.T) { - cfgs, err := ParseConfigs([]byte(` -- Service: "foo" - Namespace: "bar" - DeployAsVM: true - VMDistro: "Centos8" -`)) - if err != nil { - t.Fatal(err) - } - if diff := cmp.Diff(cfgs, []Config{{ - Service: "foo", - Namespace: namespace.Static("bar"), - DeployAsVM: true, - VMDistro: "Centos8", - }}); diff != "" { - t.Fatal(diff) - } -} diff --git a/pkg/test/framework/components/echo/deployment/builder.go b/pkg/test/framework/components/echo/deployment/builder.go deleted file mode 100644 index 79c016a11..000000000 --- a/pkg/test/framework/components/echo/deployment/builder.go +++ /dev/null @@ -1,413 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package deployment - -import ( - "context" - "fmt" - "strings" - "sync" - "time" -) - -import ( - "github.com/google/go-cmp/cmp" - "github.com/hashicorp/go-multierror" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube/inject" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/kube" - _ "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/staticvm" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/istio" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/namespace" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// Builder for a group of collaborating Echo Instances. Once built, all Instances in the -// group: -// -// 1. Are ready to receive traffic, and -// 2. Can call every other Instance in the group (i.e. have received Envoy config -// from Pilot). -// -// If a test needs to verify that one Instance is NOT reachable from another, there are -// a couple of options: -// -// 1. Build a group while all Instances ARE reachable. Then apply a policy -// disallowing the communication. -// 2. Build the source and destination Instances in separate groups and then -// call `source.WaitUntilCallable(destination)`. -type Builder interface { - // With adds a new Echo configuration to the Builder. Once built, the instance - // pointer will be updated to point at the new Instance. - With(i *echo.Instance, cfg echo.Config) Builder - - // WithConfig mimics the behavior of With, but does not allow passing a reference - // and returns an echoboot builder rather than a generic echo builder. - // TODO rename this to With, and the old method to WithInstance - WithConfig(cfg echo.Config) Builder - - // WithClusters will cause subsequent With or WithConfig calls to be applied to the given clusters. - WithClusters(...cluster.Cluster) Builder - - // Build and initialize all Echo Instances. Upon returning, the Instance pointers - // are assigned and all Instances are ready to communicate with each other. - Build() (echo.Instances, error) - BuildOrFail(t test.Failer) echo.Instances -} - -var _ Builder = builder{} - -// New builder for echo deployments. -func New(ctx resource.Context, clusters ...cluster.Cluster) Builder { - // use all workload clusters unless otherwise specified - if len(clusters) == 0 { - clusters = ctx.Clusters() - } - b := builder{ - ctx: ctx, - configs: map[cluster.Kind][]echo.Config{}, - refs: map[cluster.Kind][]*echo.Instance{}, - namespaces: map[string]namespace.Instance{}, - } - templates, err := b.injectionTemplates() - if err != nil { - // deal with this when we call Build() to avoid making the New signature unwieldy - b.errs = multierror.Append(b.errs, fmt.Errorf("failed finding injection templates on clusters %v", err)) - } - b.templates = templates - - return b.WithClusters(clusters...) -} - -type builder struct { - ctx resource.Context - - // clusters contains the current set of clusters that subsequent With calls will be applied to, - // if the Config passed to With does not explicitly choose a cluster. - clusters cluster.Clusters - - // configs contains configurations to be built, expanded per-cluster and grouped by cluster Kind. - configs map[cluster.Kind][]echo.Config - // refs contains the references to assign built Instances to. - // The length of each refs slice should match the length of the corresponding cluster slice. - // Only the first per-cluster entry for a given config should have a non-nil ref. - refs map[cluster.Kind][]*echo.Instance - // namespaces caches namespaces by their prefix; used for converting Static namespace from configs into actual - // namesapces - namespaces map[string]namespace.Instance - // the set of injection templates for each cluster - templates map[string]sets.Set - // errs contains a multierror for failed validation during With calls - errs error -} - -func (b builder) WithConfig(cfg echo.Config) Builder { - return b.With(nil, cfg).(builder) -} - -// With adds a new Echo configuration to the Builder. When a cluster is provided in the Config, it will only be applied -// to that cluster, otherwise the Config is applied to all WithClusters. Once built, if being built for a single cluster, -// the instance pointer will be updated to point at the new Instance. -func (b builder) With(i *echo.Instance, cfg echo.Config) Builder { - if b.ctx.Settings().SkipWorkloadClassesAsSet().Contains(cfg.WorkloadClass()) { - return b - } - - cfg = cfg.DeepCopy() - if err := cfg.FillDefaults(b.ctx); err != nil { - b.errs = multierror.Append(b.errs, err) - return b - } - - // cache the namespace, so manually added echo.Configs can be a part of it - b.namespaces[cfg.Namespace.Prefix()] = cfg.Namespace - - targetClusters := b.clusters - if cfg.Cluster != nil { - targetClusters = cluster.Clusters{cfg.Cluster} - } - - // If we didn't deploy VMs, but we don't care about VMs, we can ignore this. - shouldSkip := b.ctx.Settings().Skip(echo.VM) && cfg.IsVM() - deployedTo := 0 - for idx, c := range targetClusters { - ec, ok := c.(echo.Cluster) - if !ok { - b.errs = multierror.Append(b.errs, fmt.Errorf("attempted to deploy to %s but it does not implement echo.Cluster", c.Name())) - continue - } - perClusterConfig, ok := ec.CanDeploy(cfg) - if !ok { - continue - } - if !b.validateTemplates(perClusterConfig, c) { - if c.Kind() == cluster.Kubernetes { - scopes.Framework.Warnf("%s does not contain injection templates for %s; skipping deployment", c.Name(), perClusterConfig.ClusterLocalFQDN()) - } - // Don't error out when injection template missing. - shouldSkip = true - continue - } - - var ref *echo.Instance - if idx == 0 { - // ref only applies to the first cluster deployed to - // refs shouldn't be used when deploying to multiple targetClusters - // TODO: should we just panic if a ref is passed in a multi-cluster context? - ref = i - } - perClusterConfig = perClusterConfig.DeepCopy() - k := ec.Kind() - perClusterConfig.Cluster = ec - b.configs[k] = append(b.configs[k], perClusterConfig) - b.refs[k] = append(b.refs[k], ref) - deployedTo++ - } - - if deployedTo == 0 && !shouldSkip { - b.errs = multierror.Append(b.errs, fmt.Errorf("no clusters were eligible for app %s", cfg.Service)) - } - - return b -} - -// WithClusters will cause subsequent With calls to be applied to the given clusters. -func (b builder) WithClusters(clusters ...cluster.Cluster) Builder { - next := b - next.clusters = clusters - return next -} - -func (b builder) Build() (out echo.Instances, err error) { - return build(b) -} - -// injectionTemplates lists the set of templates for each Kube cluster -func (b builder) injectionTemplates() (map[string]sets.Set, error) { - ns := "dubbo-system" - i, err := istio.Get(b.ctx) - if err != nil { - scopes.Framework.Infof("defaulting to dubbo-system namespace for injection template discovery: %v", err) - } else { - ns = i.Settings().SystemNamespace - } - - out := map[string]sets.Set{} - for _, c := range b.ctx.Clusters().Kube() { - out[c.Name()] = sets.New() - // TODO find a place to read revision(s) and avoid listing - cms, err := c.CoreV1().ConfigMaps(ns).List(context.TODO(), metav1.ListOptions{}) - if err != nil { - return nil, err - } - - // take the intersection of the templates available from each revision in this cluster - intersection := sets.New() - for _, item := range cms.Items { - if !strings.HasPrefix(item.Name, "istio-sidecar-injector") { - continue - } - data, err := inject.UnmarshalConfig([]byte(item.Data["config"])) - if err != nil { - return nil, fmt.Errorf("failed parsing injection cm in %s: %v", c.Name(), err) - } - if data.RawTemplates != nil { - t := sets.New() - for name := range data.RawTemplates { - t.Insert(name) - } - // either intersection has not been set or we intersect these templates - // with the current set. - if intersection.IsEmpty() { - intersection = t - } else { - intersection = intersection.Intersection(t) - } - } - } - for name := range intersection { - out[c.Name()].Insert(name) - } - } - - return out, nil -} - -// build inner allows assigning to b (assignment to receiver would be ineffective) -func build(b builder) (out echo.Instances, err error) { - start := time.Now() - scopes.Framework.Info("=== BEGIN: Deploy echo instances ===") - defer func() { - if err != nil { - scopes.Framework.Error("=== FAILED: Deploy echo instances ===") - scopes.Framework.Error(err) - } else { - scopes.Framework.Infof("=== SUCCEEDED: Deploy echo instances in %v ===", time.Since(start)) - } - }() - - // load additional configs - for _, cfg := range *additionalConfigs { - // swap the namespace.Static for a namespace.kube - b, cfg.Namespace = b.getOrCreateNamespace(cfg.Namespace.Prefix()) - // register the extra config - b = b.WithConfig(cfg).(builder) - } - - // bail early if there were issues during the configuration stage - if b.errs != nil { - return nil, b.errs - } - - if err = b.deployServices(); err != nil { - return - } - if out, err = b.deployInstances(); err != nil { - return - } - return -} - -func (b builder) getOrCreateNamespace(prefix string) (builder, namespace.Instance) { - ns, ok := b.namespaces[prefix] - if ok { - return b, ns - } - ns, err := namespace.New(b.ctx, namespace.Config{Prefix: prefix, Inject: true}) - if err != nil { - b.errs = multierror.Append(b.errs, err) - } - b.namespaces[prefix] = ns - return b, ns -} - -// deployServices deploys the kubernetes Service to all clusters. Multicluster meshes should have "sameness" -// per cluster. This avoids concurrent writes later. -func (b builder) deployServices() (err error) { - services := make(map[string]string) - for _, cfgs := range b.configs { - for _, cfg := range cfgs { - svc, err := kube.GenerateService(cfg) - if err != nil { - return err - } - if existing, ok := services[cfg.ClusterLocalFQDN()]; ok { - // we've already run the generation for another echo instance's config, make sure things are the same - if existing != svc { - return fmt.Errorf("inconsistency in %s Service definition:\n%s", cfg.Service, cmp.Diff(existing, svc)) - } - } - services[cfg.ClusterLocalFQDN()] = svc - } - } - - // Deploy the services to all clusters. - cfg := b.ctx.ConfigKube().New() - for svcNs, svcYaml := range services { - ns := strings.Split(svcNs, ".")[1] - cfg.YAML(ns, svcYaml) - } - - return cfg.Apply(resource.NoCleanup) -} - -func (b builder) deployInstances() (instances echo.Instances, err error) { - m := sync.Mutex{} - out := echo.Instances{} - g := multierror.Group{} - // run the builder func for each kind of config in parallel - for kind, configs := range b.configs { - kind := kind - configs := configs - g.Go(func() error { - buildFunc, err := echo.GetBuilder(kind) - if err != nil { - return err - } - instances, err := buildFunc(b.ctx, configs) - if err != nil { - return err - } - - // link reference pointers - if err := assignRefs(b.refs[kind], instances); err != nil { - return err - } - - // safely merge instances from all kinds of cluster into one list - m.Lock() - defer m.Unlock() - out = append(out, instances...) - return nil - }) - } - if err := g.Wait().ErrorOrNil(); err != nil { - return nil, err - } - return out, nil -} - -func assignRefs(refs []*echo.Instance, instances echo.Instances) error { - if len(refs) != len(instances) { - return fmt.Errorf("cannot set %d references, only %d instances were built", len(refs), len(instances)) - } - for i, ref := range refs { - if ref != nil { - *ref = instances[i] - } - } - return nil -} - -func (b builder) BuildOrFail(t test.Failer) echo.Instances { - t.Helper() - out, err := b.Build() - if err != nil { - t.Fatal(err) - } - return out -} - -// validateTemplates returns true if the templates specified by inject.istio.io/templates on the config exist on c -func (b builder) validateTemplates(config echo.Config, c cluster.Cluster) bool { - expected := sets.New() - for _, subset := range config.Subsets { - expected.InsertAll(parseList(subset.Annotations.Get(echo.SidecarInjectTemplates))...) - } - if b.templates == nil || b.templates[c.Name()] == nil { - return len(expected) == 0 - } - - return b.templates[c.Name()].SupersetOf(expected) -} - -func parseList(s string) []string { - if len(strings.TrimSpace(s)) == 0 { - return nil - } - items := strings.Split(s, ",") - for i := range items { - items[i] = strings.TrimSpace(items[i]) - } - return items -} diff --git a/pkg/test/framework/components/echo/deployment/flags.go b/pkg/test/framework/components/echo/deployment/flags.go deleted file mode 100644 index 1235fe140..000000000 --- a/pkg/test/framework/components/echo/deployment/flags.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package deployment - -import ( - "bytes" - "flag" - "fmt" - "os" -) - -import ( - "gopkg.in/yaml.v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/config" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/file" -) - -var additionalConfigs = &configs{} - -func init() { - flag.Var(additionalConfigs, "istio.test.echo.configs", "The path to a file containing a list of "+ - "echo.Config in YAML which will be added to the set of echos for every suite.") -} - -// configs wraps a slice of echo.Config to implement the config.Value interface, allowing -// it to be configured by a flag, or within the test framework config file. -type configs []echo.Config - -var _ config.Value = &configs{} - -func (c *configs) String() string { - buf := &bytes.Buffer{} - for _, cc := range *c { - _, _ = fmt.Fprintf(buf, "FQDN: %s\n", cc.ClusterLocalFQDN()) - _, _ = fmt.Fprintf(buf, "Headless: %v\n", cc.Headless) - _, _ = fmt.Fprintf(buf, "VM: %v\n", cc.DeployAsVM) - if cc.DeployAsVM { - _, _ = fmt.Fprintf(buf, "VMDistro: %s\n", cc.VMDistro) - } - if cc.Cluster.Name() != "" { - _, _ = fmt.Fprintf(buf, "Cluster: %s\n", cc.Cluster.Name()) - } - } - return buf.String() -} - -func (c *configs) Set(path string) error { - path, err := file.NormalizePath(path) - if err != nil { - return err - } - yml, err := os.ReadFile(path) - if err != nil { - return err - } - out, err := echo.ParseConfigs(yml) - if err != nil { - return err - } - *c = out - return nil -} - -func (c *configs) SetConfig(m interface{}) error { - yml, err := yaml.Marshal(m) - if err != nil { - return err - } - out, err := echo.ParseConfigs(yml) - if err != nil { - return err - } - *c = out - return nil -} diff --git a/pkg/test/framework/components/echo/echotest/echotest.go b/pkg/test/framework/components/echo/echotest/echotest.go deleted file mode 100644 index 784104fce..000000000 --- a/pkg/test/framework/components/echo/echotest/echotest.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echotest - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/match" -) - -// T enumerates subtests given a set of workloads as echo.Instances. -type T struct { - // rootCtx is the top level test context to generate subtests from and should only be referenced from RunX methods. - rootCtx framework.TestContext - - sources echo.Instances - destinations echo.Instances - - destinationFilters []CombinationFilter - - sourceDeploymentSetup []srcSetupFn - deploymentPairSetup []svcPairSetupFn - destinationDeploymentSetup []dstSetupFn -} - -// New creates a *T using the given applications as sources and destinations for each subtest. -func New(ctx framework.TestContext, instances echo.Instances) *T { - s, d := make(echo.Instances, len(instances)), make(echo.Instances, len(instances)) - copy(s, instances) - copy(d, instances) - t := &T{rootCtx: ctx, sources: s, destinations: d} - if ctx.Settings().Skip(echo.VM) { - t = t.FromMatch(match.NotVM).ToMatch(match.NotVM) - } - return t -} diff --git a/pkg/test/framework/components/echo/echotest/filters.go b/pkg/test/framework/components/echo/echotest/filters.go deleted file mode 100644 index 1efca6b2e..000000000 --- a/pkg/test/framework/components/echo/echotest/filters.go +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echotest - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/match" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -type ( - Filter func(echo.Instances) echo.Instances - CombinationFilter func(from echo.Instance, to echo.Instances) echo.Instances -) - -// From applies each of the filter functions in order to allow removing workloads from the set of clients. -// Example: -// -// echotest.New(t, apps). -// From(echotest.SingleSimplePodServiceAndAllSpecial, echotest.NoExternalServices). -// Run() -func (t *T) From(filters ...Filter) *T { - for _, filter := range filters { - t.sources = filter(t.sources) - } - return t -} - -func (t *T) FromMatch(m match.Matcher) *T { - return t.From(FilterMatch(m)) -} - -// To applies each of the filter functions in order to allow removing workloads from the set of destinations. -// Example: -// -// echotest.New(t, apps). -// To(echotest.SingleSimplePodServiceAndAllSpecial). -// Run() -func (t *T) To(filters ...Filter) *T { - for _, filter := range filters { - t.destinations = filter(t.destinations) - } - return t -} - -func (t *T) ToMatch(m match.Matcher) *T { - return t.To(FilterMatch(m)) -} - -// ConditionallyTo appends the given filters which are executed per test. Destination filters may need -// to change behavior based on the client. For example, naked services can't be reached cross-network, so -// the client matters. -// Example: -// -// echotest.New(t, apps). -// ConditionallyTo(echotest.ReachableDestinations). -// Run() -func (t *T) ConditionallyTo(filters ...CombinationFilter) *T { - t.destinationFilters = append(t.destinationFilters, filters...) - return t -} - -// WithDefaultFilters applies common filters that work for most tests. -// Example: -// -// The full set of apps is a, b, c, headless, naked, and vm (one simple pod). -// Only a, headless, naked and vm are used as sources. -// Subtests are generated only for reachable destinations. -// Pod a will not be in destinations, but b will (one simpe pod not in sources) -func (t *T) WithDefaultFilters() *T { - return t. - From(SingleSimplePodServiceAndAllSpecial(), FilterMatch(match.NotExternal)). - ConditionallyTo(ReachableDestinations). - To(SingleSimplePodServiceAndAllSpecial(t.sources...)) -} - -func (t *T) applyCombinationFilters(from echo.Instance, to echo.Instances) echo.Instances { - for _, filter := range t.destinationFilters { - to = filter(from, to) - } - return to -} - -// SingleSimplePodServiceAndAllSpecial finds the first Pod deployment that has a sidecar and doesn't use a headless service and removes all -// other "regular" pods that aren't part of the same Service. Pods that are part of the same Service but are in a -// different cluster or revision will still be included. -// Example: -// -// The full set of apps is a, b, c, headless, naked, and vm. -// The plain-pods are a, b and c. -// This filter would result in a, headless, naked and vm. -// -// TODO this name is not good -func SingleSimplePodServiceAndAllSpecial(exclude ...echo.Instance) Filter { - return func(instances echo.Instances) echo.Instances { - return oneRegularPodPerNamespace(exclude)(instances).Append(notRegularPods()(instances)) - } -} - -func oneRegularPodPerNamespace(exclude echo.Instances) Filter { - return func(instances echo.Instances) echo.Instances { - // Apply the filters. - regularPods := match.And( - match.RegularPod, - match.Not(match.AnyServiceName(exclude.NamespacedNames()))).GetMatches(instances) - - if len(regularPods) == 0 { - return regularPods - } - - // Pick a single regular pod per namespace. - namespaces := sets.New() - var outServices echo.Services - for _, svc := range regularPods.Services() { - ns := svc.Config().Namespace.Name() - if !namespaces.Contains(ns) { - namespaces.Insert(ns) - outServices = append(outServices, svc) - } - } - - return outServices.Instances() - } -} - -func notRegularPods() Filter { - return func(instances echo.Instances) echo.Instances { - return match.NotRegularPod.GetMatches(instances) - } -} - -// FilterMatch returns a filter that simply applies the given matcher. -func FilterMatch(matcher match.Matcher) Filter { - return func(instances echo.Instances) echo.Instances { - return matcher.GetMatches(instances) - } -} - -// ReachableDestinations filters out known-unreachable destinations given a source. -// - from a naked pod, we can't reach cross-network endpoints or VMs -// - we can't reach cross-cluster headless endpoints -// - from an injected Pod, only non-naked cross-network endpoints are reachable -var ReachableDestinations CombinationFilter = func(from echo.Instance, to echo.Instances) echo.Instances { - return match.And( - reachableFromNaked(from), - reachableFromVM(from), - reachableFromProxylessGRPC(from), - reachableNakedDestinations(from), - reachableHeadlessDestinations(from)). - GetMatches(to) -} - -// reachableHeadlessDestinations filters out headless services that aren't in the same cluster -// TODO(stevenctl): headless across-networks https://github.com/istio/istio/issues/38327 -func reachableHeadlessDestinations(from echo.Instance) match.Matcher { - excluded := match.And( - match.Headless, - match.Not(match.Network(from.Config().Cluster.NetworkName()))) - return match.Not(excluded) -} - -// reachableNakedDestinations filters out naked instances that aren't on the same network. -// While External services are considered "naked", we won't see 500s due to different loadbalancing. -func reachableNakedDestinations(from echo.Instance) match.Matcher { - srcNw := from.Config().Cluster.NetworkName() - excluded := match.And( - match.Naked, - // TODO we probably don't actually reach all external, but for now maintaining what the tests did - match.NotExternal, - match.Not(match.Network(srcNw))) - return match.Not(excluded) -} - -// reachableFromVM filters out external services due to issues with ServiceEntry resolution -// TODO https://github.com/istio/istio/issues/27154 -func reachableFromVM(from echo.Instance) match.Matcher { - if !from.Config().IsVM() { - return match.Any - } - return match.NotExternal -} - -func reachableFromProxylessGRPC(from echo.Instance) match.Matcher { - if !from.Config().IsProxylessGRPC() { - return match.Any - } - return match.And( - match.NotExternal, - match.NotHeadless) -} - -// reachableFromNaked filters out all virtual machines and any instance that isn't on the same network -func reachableFromNaked(from echo.Instance) match.Matcher { - if !from.Config().IsNaked() { - return match.Any - } - return match.And( - match.Network(from.Config().Cluster.NetworkName()), - match.NotVM) -} diff --git a/pkg/test/framework/components/echo/echotest/filters_test.go b/pkg/test/framework/components/echo/echotest/filters_test.go deleted file mode 100644 index c8f2429ef..000000000 --- a/pkg/test/framework/components/echo/echotest/filters_test.go +++ /dev/null @@ -1,395 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echotest_test - -import ( - "fmt" - "strconv" - "strings" - "testing" -) - -import ( - "github.com/google/go-cmp/cmp" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/echotest" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/match" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/namespace" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -var ( - // TODO set this up with echobuilder/cluster builder in Fake mode - - echo1NS = namespace.Static("echo1") - echo2NS = namespace.Static("echo2") - - // 2 clusters on 2 networks - cls1 = &cluster.FakeCluster{Topology: cluster.Topology{ClusterName: "cls1", Network: "n1", Index: 0, ClusterKind: cluster.Fake}} - cls2 = &cluster.FakeCluster{Topology: cluster.Topology{ClusterName: "cls2", Network: "n2", Index: 1, ClusterKind: cluster.Fake}} - - // simple pod - a1 = &fakeInstance{Cluster: cls1, Namespace: echo1NS, Service: "a"} - a2 = &fakeInstance{Cluster: cls2, Namespace: echo1NS, Service: "a"} - // simple pod with different svc - b1 = &fakeInstance{Cluster: cls1, Namespace: echo1NS, Service: "b"} - b2 = &fakeInstance{Cluster: cls2, Namespace: echo1NS, Service: "b"} - // another simple pod with different svc - c1 = &fakeInstance{Cluster: cls1, Namespace: echo1NS, Service: "c"} - c2 = &fakeInstance{Cluster: cls2, Namespace: echo1NS, Service: "c"} - // simple pod with a different namespace - a1Ns2 = &fakeInstance{Cluster: cls1, Namespace: echo2NS, Service: "a"} - a2Ns2 = &fakeInstance{Cluster: cls2, Namespace: echo2NS, Service: "a"} - // virtual machine - vm1 = &fakeInstance{Cluster: cls1, Namespace: echo1NS, Service: "vm", DeployAsVM: true} - vm2 = &fakeInstance{Cluster: cls2, Namespace: echo1NS, Service: "vm", DeployAsVM: true} - // headless - headless1 = &fakeInstance{Cluster: cls1, Namespace: echo1NS, Service: "headless", Headless: true} - headless2 = &fakeInstance{Cluster: cls2, Namespace: echo1NS, Service: "headless", Headless: true} - // naked pod (uninjected) - naked1 = &fakeInstance{Cluster: cls1, Namespace: echo1NS, Service: "naked", Subsets: []echo.SubsetConfig{{ - Annotations: echo.NewAnnotations().SetBool(echo.SidecarInject, false), - }}} - naked2 = &fakeInstance{Cluster: cls2, Namespace: echo1NS, Service: "naked", Subsets: []echo.SubsetConfig{{ - Annotations: echo.NewAnnotations().SetBool(echo.SidecarInject, false), - }}} - // external svc - external1 = &fakeInstance{ - Cluster: cls1, Namespace: echo1NS, Service: "external", DefaultHostHeader: "external.com", Subsets: []echo.SubsetConfig{{ - Annotations: map[echo.Annotation]*echo.AnnotationValue{echo.SidecarInject: {Value: strconv.FormatBool(false)}}, - }}, - } - external2 = &fakeInstance{ - Cluster: cls2, Namespace: echo1NS, Service: "external", DefaultHostHeader: "external.com", Subsets: []echo.SubsetConfig{{ - Annotations: map[echo.Annotation]*echo.AnnotationValue{echo.SidecarInject: {Value: strconv.FormatBool(false)}}, - }}, - } - - all = echo.Instances{a1, a2, b1, b2, c1, c2, a1Ns2, a2Ns2, vm1, vm2, headless1, headless2, naked1, naked2, external1, external2} -) - -func TestMain(m *testing.M) { - framework.NewSuite(m).EnvironmentFactory(resource.NilEnvironmentFactory).Run() -} - -func TestDeployments(t *testing.T) { - if diff := cmp.Diff( - all.Services().NamespacedNames(), - echo.NamespacedNames{ - { - Name: "a", - Namespace: echo1NS, - }, - { - Name: "a", - Namespace: echo2NS, - }, - { - Name: "b", - Namespace: echo1NS, - }, - { - Name: "c", - Namespace: echo1NS, - }, - { - Name: "external", - Namespace: echo1NS, - }, - { - Name: "headless", - Namespace: echo1NS, - }, - { - Name: "naked", - Namespace: echo1NS, - }, - { - Name: "vm", - Namespace: echo1NS, - }, - }, - ); diff != "" { - t.Fatal(diff) - } -} - -func TestFilters(t *testing.T) { - tests := map[string]struct { - filter func(echo.Instances) echo.Instances - expect echo.Instances - }{ - "SingleSimplePodServiceAndAllSpecial": { - filter: echotest.SingleSimplePodServiceAndAllSpecial(), - expect: echo.Instances{ - // Keep pods for one regular service per namespace. - a1, a2, a1Ns2, a2Ns2, - // keep the special cases - vm1, vm2, - headless1, headless2, - naked1, naked2, - external1, external2, - }, - }, - "ReachableDestinations from pod": { - filter: func(instances echo.Instances) echo.Instances { - return echotest.ReachableDestinations(a1, instances) - }, - expect: echo.Instances{ - // all instances - a1, a2, a1Ns2, a2Ns2, b1, b2, c1, c2, vm1, vm2, external1, external2, - // only same network/cluster - headless1, naked1, - }, - }, - "ReachableDestinations from naked": { - filter: func(instances echo.Instances) echo.Instances { - return echotest.ReachableDestinations(naked1, instances) - }, - expect: echo.Instances{ - // only same network/cluster, no VMs - a1, a1Ns2, b1, c1, headless1, naked1, external1, - }, - }, - "ReachableDestinations from vm": { - filter: func(instances echo.Instances) echo.Instances { - return echotest.ReachableDestinations(vm1, instances) - }, - expect: echo.Instances{ - // all pods/vms, no external - a1, a2, a1Ns2, a2Ns2, b1, b2, c1, c2, vm1, vm2, - // only same network/cluster - headless1, naked1, - }, - }, - } - for n, tc := range tests { - n, tc := n, tc - t.Run(n, func(t *testing.T) { - compare(t, tc.filter(all), tc.expect) - }) - } -} - -func compare(t *testing.T, got echo.Instances, want echo.Instances) { - if len(got) != len(want) { - t.Errorf("got %d instances but expected %d", len(got), len(want)) - } - expected := map[string]struct{}{} - for _, i := range want { - expected[instanceKey(i)] = struct{}{} - } - unexpected := map[string]struct{}{} - for _, i := range all { - k := instanceKey(i) - if _, ok := expected[k]; !ok { - unexpected[k] = struct{}{} - } - } - for _, i := range got { - k := instanceKey(i) - // just remove the items rather than looping over expected, if anythings left we missed it - delete(expected, k) - if _, ok := unexpected[k]; ok { - t.Errorf("expected %s to be filtered out", k) - } - } - if len(expected) > 0 { - t.Errorf("did not include %v", expected) - } -} - -func TestRun(t *testing.T) { - // source svc/cluster -> dest services -> number of subtests (should == num clusters) - framework.NewTest(t).Run(func(t framework.TestContext) { - tests := map[string]struct { - run func(t framework.TestContext, testTopology map[string]map[string]int) - expect map[string]map[string]int - }{ - "Run_WithDefaultFilters": { - run: func(t framework.TestContext, testTopology map[string]map[string]int) { - echotest.New(t, all). - WithDefaultFilters(). - Run(func(ctx framework.TestContext, from echo.Instance, to echo.Target) { - // TODO if the destinations would change based on which cluster then add cluster to srCkey - fromKey := from.Config().ClusterLocalFQDN() - toKey := to.Config().ClusterLocalFQDN() - if testTopology[fromKey] == nil { - testTopology[fromKey] = map[string]int{} - } - testTopology[fromKey][toKey]++ - }) - }, - expect: map[string]map[string]int{ - "a.echo1.svc.cluster.local": { - "b.echo1.svc.cluster.local": 2, - "external.echo1.svc.cluster.local": 2, - "headless.echo1.svc.cluster.local": 2, - "naked.echo1.svc.cluster.local": 2, - "vm.echo1.svc.cluster.local": 2, - }, - "a.echo2.svc.cluster.local": { - "b.echo1.svc.cluster.local": 2, - "external.echo1.svc.cluster.local": 2, - "headless.echo1.svc.cluster.local": 2, - "naked.echo1.svc.cluster.local": 2, - "vm.echo1.svc.cluster.local": 2, - }, - "headless.echo1.svc.cluster.local": { - "b.echo1.svc.cluster.local": 2, - "external.echo1.svc.cluster.local": 2, - "headless.echo1.svc.cluster.local": 2, - "naked.echo1.svc.cluster.local": 2, - "vm.echo1.svc.cluster.local": 2, - }, - "naked.echo1.svc.cluster.local": { - "b.echo1.svc.cluster.local": 2, - "external.echo1.svc.cluster.local": 2, - "headless.echo1.svc.cluster.local": 2, - "naked.echo1.svc.cluster.local": 2, - }, - "vm.echo1.svc.cluster.local": { - "b.echo1.svc.cluster.local": 2, - "headless.echo1.svc.cluster.local": 2, - "naked.echo1.svc.cluster.local": 2, - "vm.echo1.svc.cluster.local": 2, - }, - }, - }, - "RunToN": { - run: func(t framework.TestContext, testTopology map[string]map[string]int) { - echotest.New(t, all). - WithDefaultFilters(). - FromMatch(match.And(match.NotNaked, match.NotHeadless)). - ToMatch(match.NotHeadless). - RunToN(3, func(ctx framework.TestContext, from echo.Instance, dsts echo.Services) { - srcKey := from.Config().ClusterLocalFQDN() - if testTopology[srcKey] == nil { - testTopology[srcKey] = map[string]int{} - } - var dstnames []string - for _, dst := range dsts { - dstnames = append(dstnames, dst.Config().ClusterLocalFQDN()) - } - dstKey := strings.Join(dstnames, "_") - testTopology[srcKey][dstKey]++ - }) - }, - expect: map[string]map[string]int{ - "a.echo1.svc.cluster.local": { - "b.echo1.svc.cluster.local_external.echo1.svc.cluster.local_naked.echo1.svc.cluster.local": 2, - "external.echo1.svc.cluster.local_naked.echo1.svc.cluster.local_vm.echo1.svc.cluster.local": 2, - }, - "a.echo2.svc.cluster.local": { - "b.echo1.svc.cluster.local_external.echo1.svc.cluster.local_naked.echo1.svc.cluster.local": 2, - "external.echo1.svc.cluster.local_naked.echo1.svc.cluster.local_vm.echo1.svc.cluster.local": 2, - }, - "vm.echo1.svc.cluster.local": { - // VM cannot hit external services (https://github.com/istio/istio/issues/27154) - "b.echo1.svc.cluster.local_naked.echo1.svc.cluster.local_vm.echo1.svc.cluster.local": 2, - }, - }, - }, - } - - for name, tt := range tests { - tt := tt - t.NewSubTest(name).Run(func(t framework.TestContext) { - testTopology := map[string]map[string]int{} - tt.run(t, testTopology) - if diff := cmp.Diff(testTopology, tt.expect); diff != "" { - t.Errorf(diff) - } - }) - } - }) -} - -var _ echo.Instance = fakeInstance{} - -func instanceKey(i echo.Instance) string { - return fmt.Sprintf("%s.%s.%s", i.Config().Service, i.Config().Namespace.Name(), i.Config().Cluster.Name()) -} - -// fakeInstance wraps echo.Config for test-framework internals tests where we don't actually make calls -type fakeInstance echo.Config - -func (f fakeInstance) WithWorkloads(wl ...echo.Workload) echo.Instance { - panic("implement me") -} - -func (f fakeInstance) Instances() echo.Instances { - return echo.Instances{f} -} - -func (f fakeInstance) ID() resource.ID { - panic("implement me") -} - -func (f fakeInstance) NamespacedName() echo.NamespacedName { - return f.Config().NamespacedName() -} - -func (f fakeInstance) PortForName(name string) echo.Port { - return f.Config().Ports.MustForName(name) -} - -func (f fakeInstance) Config() echo.Config { - cfg := echo.Config(f) - _ = cfg.FillDefaults(nil) - return cfg -} - -func (f fakeInstance) Address() string { - panic("implement me") -} - -func (f fakeInstance) Addresses() []string { - panic("implement me") -} - -func (f fakeInstance) Workloads() (echo.Workloads, error) { - panic("implement me") -} - -func (f fakeInstance) WorkloadsOrFail(test.Failer) echo.Workloads { - panic("implement me") -} - -func (f fakeInstance) MustWorkloads() echo.Workloads { - panic("implement me") -} - -func (f fakeInstance) Clusters() cluster.Clusters { - panic("implement me") -} - -func (f fakeInstance) Call(echo.CallOptions) (echo.CallResult, error) { - panic("implement me") -} - -func (f fakeInstance) CallOrFail(test.Failer, echo.CallOptions) echo.CallResult { - panic("implement me") -} - -func (f fakeInstance) Restart() error { - panic("implement me") -} diff --git a/pkg/test/framework/components/echo/echotest/run.go b/pkg/test/framework/components/echo/echotest/run.go deleted file mode 100644 index 5c558ae89..000000000 --- a/pkg/test/framework/components/echo/echotest/run.go +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echotest - -import ( - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/istio" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/istio/ingress" -) - -type ( - perDeploymentTest func(t framework.TestContext, deployments echo.Instances) - perNDeploymentTest func(t framework.TestContext, deployments echo.Services) - perInstanceTest func(t framework.TestContext, inst echo.Instance) - perClusterTest func(t framework.TestContext, c cluster.Cluster) - - oneToOneTest func(t framework.TestContext, from echo.Instance, to echo.Target) - oneToNTest func(t framework.TestContext, from echo.Instance, dsts echo.Services) - oneClusterOneTest func(t framework.TestContext, from cluster.Cluster, to echo.Target) - ingressTest func(t framework.TestContext, from ingress.Instance, to echo.Target) -) - -// Run will generate and run one subtest to send traffic between each combination -// of source instance to target deployment. -// -// For example, in a test named `a/to_b/from_cluster-0`, -// `a` is the source deployment, `b` is the destination deployment and -// `cluster-0` marks which instance of the source deployment. -// -// We use a combination of deployment name and cluster name to identify -// a particular source instance, as there should typically be one instance -// per cluster for any deployment. However we do not identify a destination -// cluster, as we expect most tests will cause load-balancing across all possible -// clusters. -func (t *T) Run(testFn oneToOneTest) { - t.rootCtx.Logf("Running tests with: sources %v -> destinations %v", - t.sources.Services().NamespacedNames().NamesWithNamespacePrefix(), - t.destinations.Services().NamespacedNames().NamesWithNamespacePrefix()) - t.fromEachDeployment(t.rootCtx, func(ctx framework.TestContext, from echo.Instances) { - t.setup(ctx, from.Callers()) - t.toEachDeployment(ctx, func(ctx framework.TestContext, to echo.Instances) { - t.setupPair(ctx, from.Callers(), echo.Services{to}) - t.fromEachWorkloadCluster(ctx, from, func(ctx framework.TestContext, from echo.Instance) { - filteredDst := t.applyCombinationFilters(from, to) - if len(filteredDst) == 0 { - // this only happens due to conditional filters and when an entire deployment is filtered we should be noisy - ctx.Skipf("cases from %s in %s with %s as destination are removed by filters ", - from.Config().Service, from.Config().Cluster.StableName(), to[0].Config().Service) - } - testFn(ctx, from, filteredDst) - }) - }) - }) -} - -// RunFromClusters will generate and run one subtest to send traffic to -// destination instance. This is for ingress gateway testing when source instance -// destination instances. This can be used when we're not using echo workloads -// as the source of traffic, such as from the ingress gateway. For example: -// -// RunFromClusters(func(t framework.TestContext, src cluster.Cluster, dst echo.Instances)) { -// ingr := ist.IngressFor(src) -// ingr.CallWithRetryOrFail(...) -// }) -func (t *T) RunFromClusters(testFn oneClusterOneTest) { - t.toEachDeployment(t.rootCtx, func(ctx framework.TestContext, dstInstances echo.Instances) { - t.setupPair(ctx, nil, echo.Services{dstInstances}) - if len(ctx.Clusters()) == 1 { - testFn(ctx, ctx.Clusters()[0], dstInstances) - } else { - t.fromEachCluster(ctx, func(ctx framework.TestContext, c cluster.Cluster) { - testFn(ctx, c, dstInstances) - }) - } - }) -} - -// fromEachCluster runs test from each cluster without requiring source deployment. -func (t *T) fromEachCluster(ctx framework.TestContext, testFn perClusterTest) { - for _, fromCluster := range t.sources.Clusters() { - fromCluster := fromCluster - ctx.NewSubTestf("from %s", fromCluster.StableName()).Run(func(ctx framework.TestContext) { - testFn(ctx, fromCluster) - }) - } -} - -// RunToN will generate nested subtests for every instance in every deployment subsets of the full set of deployments, -// such that every deployment is a destination at least once. To create as few subtests as possible, the same deployment -// may appear as a target in multiple subtests. -// -// Example: Given a as the only source, with a, b, c, d as destinationsand n = 3, we get the following subtests: -// -// a/to_a_b_c/from_cluster_1: -// a/to_a_b_c/from_cluster_2: -// a/to_b_c_d/from_cluster_1: -// a/to_b_c_d/from_cluster_2: -func (t *T) RunToN(n int, testFn oneToNTest) { - t.fromEachDeployment(t.rootCtx, func(ctx framework.TestContext, from echo.Instances) { - t.setup(ctx, from.Callers()) - t.toNDeployments(ctx, n, from, func(ctx framework.TestContext, toServices echo.Services) { - t.setupPair(ctx, from.Callers(), toServices) - t.fromEachWorkloadCluster(ctx, from, func(ctx framework.TestContext, fromInstance echo.Instance) { - // reapply destination filters to only get the reachable instances for this cluster - // this can be done safely since toNDeployments asserts the Services won't change - destDeployments := t.applyCombinationFilters(fromInstance, toServices.Instances()).Services() - testFn(ctx, fromInstance, destDeployments) - }) - }) - }) -} - -func (t *T) RunViaIngress(testFn ingressTest) { - i := istio.GetOrFail(t.rootCtx, t.rootCtx) - t.toEachDeployment(t.rootCtx, func(ctx framework.TestContext, dstInstances echo.Instances) { - t.setupPair(ctx, i.Ingresses().Callers(), echo.Services{dstInstances}) - doTest := func(ctx framework.TestContext, fromCluster cluster.Cluster, dst echo.Instances) { - ingr := i.IngressFor(fromCluster) - if ingr == nil { - ctx.Skipf("no ingress for %s", fromCluster.StableName()) - } - testFn(ctx, ingr, dst) - } - if len(ctx.Clusters()) == 1 { - doTest(ctx, ctx.Clusters()[0], dstInstances) - } else { - t.fromEachCluster(ctx, func(ctx framework.TestContext, c cluster.Cluster) { - doTest(ctx, c, dstInstances) - }) - } - }) -} - -func (t *T) isMultipleNamespaces() bool { - namespaces := map[string]struct{}{} - for _, instances := range []echo.Instances{t.sources, t.destinations} { - for _, i := range instances { - namespaces[i.Config().Namespace.Name()] = struct{}{} - if len(namespaces) > 1 { - return true - } - } - } - return false -} - -// fromEachDeployment enumerates subtests for deployment with the structure -// Intended to be used in combination with other helpers to enumerate subtests for destinations. -func (t *T) fromEachDeployment(ctx framework.TestContext, testFn perDeploymentTest) { - includeNS := t.isMultipleNamespaces() - - for _, from := range t.sources.Services() { - from := from - subtestName := from.Config().Service - if includeNS { - subtestName += "." + from.Config().Namespace.Prefix() - } - ctx.NewSubTest(subtestName).Run(func(ctx framework.TestContext) { - testFn(ctx, from) - }) - } -} - -// toEachDeployment enumerates subtests for every deployment as a destination, adding /to_ to the parent test. -// Intended to be used in combination with other helpers which enumerates the subtests and chooses the source instances. -func (t *T) toEachDeployment(ctx framework.TestContext, testFn perDeploymentTest) { - includeNS := t.isMultipleNamespaces() - - for _, to := range t.destinations.Services() { - to := to - subtestName := to.Config().Service - if includeNS { - subtestName += "." + to.Config().Namespace.Prefix() - } - ctx.NewSubTestf("to %s", subtestName).Run(func(ctx framework.TestContext) { - testFn(ctx, to) - }) - } -} - -func (t *T) fromEachWorkloadCluster(ctx framework.TestContext, from echo.Instances, testFn perInstanceTest) { - for _, fromInstance := range from { - fromInstance := fromInstance - if len(ctx.Clusters()) == 1 && len(from) == 1 { - testFn(ctx, fromInstance) - } else { - ctx.NewSubTestf("from %s", fromInstance.Config().Cluster.StableName()).Run(func(ctx framework.TestContext) { - // assumes we don't change config from cluster to cluster - ctx.SkipDumping() - testFn(ctx, fromInstance) - }) - } - } -} - -func (t *T) toNDeployments(ctx framework.TestContext, n int, from echo.Instances, testFn perNDeploymentTest) { - includeNS := t.isMultipleNamespaces() - - // every eligible target instance from any cluster (map to dedupe) - var commonTargets []string - for _, fromInstance := range from { - // eligible target instances from the source cluster - filteredForSource := t.applyCombinationFilters(fromInstance, t.destinations) - // make sure this targets the same deployments (not necessarily the same instances) - targetNames := filteredForSource.Services().FQDNs() - if len(commonTargets) == 0 { - commonTargets = targetNames - } else if !util.StringSliceEqual(targetNames, commonTargets) { - ctx.Fatalf("%s in each cluster each cluster would not target the same set of deploments", fromInstance.Config().Service) - } - } - - // we take all instances that match the deployments - // combination filters should be run again for individual sources - filteredTargets := t.destinations.Services().MatchFQDNs(commonTargets...) - for _, svc := range nDestinations(ctx, n, filteredTargets) { - svc := svc - - namespacedNames := svc.NamespacedNames() - var toNames []string - if includeNS { - toNames = namespacedNames.NamesWithNamespacePrefix() - } else { - toNames = namespacedNames.Names() - } - - ctx.NewSubTestf("to %s", strings.Join(toNames, " ")).Run(func(ctx framework.TestContext) { - testFn(ctx, svc) - }) - } -} - -// nDestinations splits the given deployments into subsets of size n. A deployment may be present in multiple subsets to -// ensure every deployment is included. -func nDestinations(ctx framework.TestContext, n int, deployments echo.Services) (out []echo.Services) { - nDests := len(deployments) - if nDests < n { - ctx.Fatalf("want to run with %d destinations but there are only %d total", n, nDests) - } - for i := 0; i < nDests; i += n { - start := i - if start+n-1 >= nDests { - // re-use a few destinations to fit the entire slice in - start = nDests - n - } - out = append(out, deployments[start:start+n]) - } - return -} diff --git a/pkg/test/framework/components/echo/echotest/setup.go b/pkg/test/framework/components/echo/echotest/setup.go deleted file mode 100644 index c5491cbc1..000000000 --- a/pkg/test/framework/components/echo/echotest/setup.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echotest - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" -) - -type ( - srcSetupFn func(t framework.TestContext, from echo.Callers) error - svcPairSetupFn func(t framework.TestContext, from echo.Callers, to echo.Services) error - dstSetupFn func(t framework.TestContext, to echo.Target) error -) - -// Setup runs the given function in the source deployment context. -// -// For example, given apps a, b, and c in 2 clusters, -// these tests would all run before the the context is cleaned up: -// -// a/to_b/from_cluster-1 -// a/to_b/from_cluster-2 -// a/to_c/from_cluster-1 -// a/to_c/from_cluster-2 -// cleanup... -// b/to_a/from_cluster-1 -// ... -func (t *T) Setup(setupFn srcSetupFn) *T { - t.sourceDeploymentSetup = append(t.sourceDeploymentSetup, setupFn) - return t -} - -func (t *T) setup(ctx framework.TestContext, from echo.Callers) { - if !t.hasSourceSetup() { - ctx.SkipDumping() - scopes.Framework.Debugf("No echotest setup; skipping test dump at this scope.") - } - for _, setupFn := range t.sourceDeploymentSetup { - if err := setupFn(ctx, from); err != nil { - ctx.Fatal(err) - } - } -} - -func (t *T) hasSourceSetup() bool { - return len(t.sourceDeploymentSetup) > 0 -} - -// SetupForPair runs the given function for every source instance in every cluster in combination with every -// destination service. -// -// Example of how long this setup lasts before the given context is cleaned up: -// -// a/to_b/from_cluster-1 -// a/to_b/from_cluster-2 -// cleanup... -// a/to_b/from_cluster-2 -// ... -func (t *T) SetupForPair(setupFn func(ctx framework.TestContext, from echo.Callers, dsts echo.Instances) error) *T { - return t.SetupForServicePair(func(ctx framework.TestContext, from echo.Callers, dsts echo.Services) error { - return setupFn(ctx, from, dsts.Instances()) - }) -} - -// SetupForServicePair works similarly to SetupForPair, but the setup function accepts echo.Services, which -// contains instances for multiple services and should be used in combination with RunForN. -// The length of dsts services will alyas be N. -func (t *T) SetupForServicePair(setupFn svcPairSetupFn) *T { - t.deploymentPairSetup = append(t.deploymentPairSetup, setupFn) - return t -} - -// SetupForDestination is run each time the destination Service (but not destination cluser) changes. -func (t *T) SetupForDestination(setupFn dstSetupFn) *T { - t.destinationDeploymentSetup = append(t.destinationDeploymentSetup, setupFn) - return t -} - -func (t *T) hasDestinationSetup() bool { - return len(t.deploymentPairSetup)+len(t.destinationDeploymentSetup) > 0 -} - -func (t *T) setupPair(ctx framework.TestContext, from echo.Callers, dsts echo.Services) { - if !t.hasDestinationSetup() { - ctx.SkipDumping() - scopes.Framework.Debugf("No echotest setup; skipping test dump at this scope.") - } - for _, setupFn := range t.deploymentPairSetup { - if err := setupFn(ctx, from, dsts); err != nil { - ctx.Fatal(err) - } - } - for _, setupFn := range t.destinationDeploymentSetup { - if err := setupFn(ctx, dsts.Instances()); err != nil { - ctx.Fatal(err) - } - } -} diff --git a/pkg/test/framework/components/echo/factory.go b/pkg/test/framework/components/echo/factory.go deleted file mode 100644 index 843198c8d..000000000 --- a/pkg/test/framework/components/echo/factory.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -import ( - "fmt" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -// FactoryFunc can be used by a builder to produce instances from configs -type FactoryFunc func(ctx resource.Context, config []Config) (Instances, error) - -var factoryRegistry = map[cluster.Kind]FactoryFunc{} - -// RegisterFactory globally registers a base factory of a given Kind. -// The given factory should be immutable, as it will be used globally. -func RegisterFactory(kind cluster.Kind, factory FactoryFunc) { - factoryRegistry[kind] = factory -} - -func GetBuilder(kind cluster.Kind) (FactoryFunc, error) { - f, ok := factoryRegistry[kind] - if !ok { - return nil, fmt.Errorf("unsupported cluster kind: %q", kind) - } - return f, nil -} diff --git a/pkg/test/framework/components/echo/flags.go b/pkg/test/framework/components/echo/flags.go deleted file mode 100644 index 39656bd71..000000000 --- a/pkg/test/framework/components/echo/flags.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -import ( - "flag" - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -var ( - callTimeout = 20 * time.Second - callDelay = 10 * time.Millisecond - callConverge = 3 - readinessTimeout = 10 * time.Minute -) - -// init registers the command-line flags that we can exposed for "go test". -func init() { - flag.DurationVar(&callTimeout, "istio.test.echo.callTimeout", callTimeout, - "Specifies the default total timeout used when retrying calls to the Echo service") - flag.DurationVar(&callDelay, "istio.test.echo.callDelay", callDelay, - "Specifies the default delay between successive retry attempts when calling the Echo service") - flag.IntVar(&callConverge, "istio.test.echo.callConverge", callConverge, - "Specifies the number of successive retry attempts that must be successful when calling the Echo service") - flag.DurationVar(&readinessTimeout, "istio.test.echo.readinessTimeout", readinessTimeout, - "Specifies the default timeout for echo readiness check") -} - -// DefaultCallRetryOptions returns the default call retry options as specified in command-line flags. -func DefaultCallRetryOptions() []retry.Option { - return []retry.Option{retry.Timeout(callTimeout), retry.BackoffDelay(callDelay), retry.Converge(callConverge)} -} - -// DefaultReadinessTimeout returns the default echo readiness check timeout. -func DefaultReadinessTimeout() time.Duration { - return readinessTimeout -} diff --git a/pkg/test/framework/components/echo/instance.go b/pkg/test/framework/components/echo/instance.go deleted file mode 100644 index 9a7dd8e30..000000000 --- a/pkg/test/framework/components/echo/instance.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -// Instance is a component that provides access to a deployed echo service. -type Instance interface { - Caller - Target - resource.Resource - - // Address of the service (e.g. Kubernetes cluster IP). May be "" if headless. - Address() string - - // Addresses of service in dualmode - Addresses() []string - - // Restart restarts the workloads associated with this echo instance - Restart() error - - // WithWorkloads returns a target with only the specified subset of workloads - WithWorkloads(wl ...Workload) Instance -} diff --git a/pkg/test/framework/components/echo/instances.go b/pkg/test/framework/components/echo/instances.go deleted file mode 100644 index 767ccfac7..000000000 --- a/pkg/test/framework/components/echo/instances.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -import ( - "errors" - "sort" -) - -import ( - "github.com/hashicorp/go-multierror" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" -) - -var _ Target = Instances{} - -// Instances contains the instances created by the builder with methods for filtering -type Instances []Instance - -func (i Instances) NamespacedName() NamespacedName { - return i.Config().NamespacedName() -} - -func (i Instances) PortForName(name string) Port { - return i.Config().Ports.MustForName(name) -} - -func (i Instances) Config() Config { - return i.mustGetFirst().Config() -} - -func (i Instances) Instances() Instances { - return i -} - -func (i Instances) mustGetFirst() Instance { - if i.Len() == 0 { - panic("instances are empty") - } - return i[0] -} - -// Callers is a convenience method to convert Instances into Callers. -func (i Instances) Callers() Callers { - var out Callers - for _, instance := range i { - out = append(out, instance) - } - return out -} - -// Clusters returns a list of cluster names that the instances are deployed in -func (i Instances) Clusters() cluster.Clusters { - clusters := map[string]cluster.Cluster{} - for _, instance := range i { - clusters[instance.Config().Cluster.Name()] = instance.Config().Cluster - } - out := make(cluster.Clusters, 0, len(clusters)) - for _, c := range clusters { - out = append(out, c) - } - return out -} - -func (i Instances) Workloads() (Workloads, error) { - var out Workloads - for _, inst := range i { - ws, err := inst.Workloads() - if err != nil { - return nil, err - } - out = append(out, ws...) - } - - if len(out) == 0 { - return nil, errors.New("got 0 workloads") - } - - return out, nil -} - -func (i Instances) WorkloadsOrFail(t test.Failer) Workloads { - t.Helper() - out, err := i.Workloads() - if err != nil { - t.Fatal(err) - } - return out -} - -func (i Instances) MustWorkloads() Workloads { - out, err := i.Workloads() - if err != nil { - panic(err) - } - return out -} - -// IsDeployment returns true if there is only one deployment contained in the Instances -func (i Instances) IsDeployment() bool { - return len(i.Services()) == 1 -} - -func (i Instances) ContainsTarget(t Target) bool { - return i.Contains(t.Instances()...) -} - -func (i Instances) Contains(instances ...Instance) bool { - for _, thatI := range instances { - found := false - for _, thisI := range i { - if thisI == thatI { - found = true - break - } - } - if !found { - return false - } - } - return true -} - -// Services groups the Instances by FQDN. Each returned element is an Instances -// containing only instances of a single service. -func (i Instances) Services() Services { - grouped := map[string]Instances{} - for _, instance := range i { - k := instance.Config().ClusterLocalFQDN() - grouped[k] = append(grouped[k], instance) - } - var out Services - for _, deployment := range grouped { - out = append(out, deployment) - } - sort.Stable(out) - return out -} - -// Copy this Instances array. -func (i Instances) Copy() Instances { - return append(Instances{}, i...) -} - -// Append returns a new Instances array with the given values appended. -func (i Instances) Append(instances Instances) Instances { - return append(i.Copy(), instances...) -} - -// Restart each Instance -func (i Instances) Restart() error { - g := multierror.Group{} - for _, app := range i { - app := app - g.Go(app.Restart) - } - return g.Wait().ErrorOrNil() -} - -func (i Instances) Len() int { - return len(i) -} - -func (i Instances) NamespacedNames() NamespacedNames { - out := make(NamespacedNames, 0, i.Len()) - for _, ii := range i { - out = append(out, ii.NamespacedName()) - } - - sort.Stable(out) - return out -} diff --git a/pkg/test/framework/components/echo/kube/builder.go b/pkg/test/framework/components/echo/kube/builder.go deleted file mode 100644 index beb23e622..000000000 --- a/pkg/test/framework/components/echo/kube/builder.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "github.com/hashicorp/go-multierror" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -func init() { - echo.RegisterFactory(cluster.Kubernetes, build) -} - -func build(ctx resource.Context, configs []echo.Config) (echo.Instances, error) { - instances := make([]echo.Instance, len(configs)) - - g := multierror.Group{} - for i, cfg := range configs { - i, cfg := i, cfg - g.Go(func() (err error) { - instances[i], err = newInstance(ctx, cfg) - return - }) - } - - err := g.Wait().ErrorOrNil() - return instances, err -} diff --git a/pkg/test/framework/components/echo/kube/deployment.go b/pkg/test/framework/components/echo/kube/deployment.go deleted file mode 100644 index f410c9675..000000000 --- a/pkg/test/framework/components/echo/kube/deployment.go +++ /dev/null @@ -1,1020 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "bufio" - "context" - "fmt" - "net" - "os" - "path" - "strings" - "text/template" - "time" -) - -import ( - "github.com/Masterminds/sprig/v3" - "github.com/hashicorp/go-multierror" - "istio.io/api/label" - meshconfig "istio.io/api/mesh/v1alpha1" - "istio.io/pkg/log" - kubeCore "k8s.io/api/core/v1" - kerrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - echoCommon "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/environment/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/istio" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/istioctl" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" - "github.com/apache/dubbo-go-pixiu/pkg/test/shell" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/tmpl" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -const ( - // for proxyless we add a special gRPC server that doesn't get configured with xDS for test-runner use - grpcMagicPort = 17171 - - serviceYAML = ` -{{- if .ServiceAccount }} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ .Service }} ---- -{{- end }} -apiVersion: v1 -kind: Service -metadata: - name: {{ .Service }} - labels: - app: {{ .Service }} -{{- if .ServiceAnnotations }} - annotations: -{{- range $name, $value := .ServiceAnnotations }} - {{ $name.Name }}: {{ printf "%q" $value.Value }} -{{- end }} -{{- end }} -spec: -{{- if .IPFamilies }} - ipFamilies: [ {{ .IPFamilies }} ] -{{- end }} -{{- if .IPFamilyPolicy }} - ipFamilyPolicy: {{ .IPFamilyPolicy }} -{{- end }} -{{- if .Headless }} - clusterIP: None -{{- end }} - ports: -{{- range $i, $p := .ServicePorts }} - - name: {{ $p.Name }} - port: {{ $p.ServicePort }} - targetPort: {{ $p.WorkloadPort }} -{{- end }} - selector: - app: {{ .Service }} -` - - deploymentYAML = ` -{{- $revVerMap := .Revisions }} -{{- $subsets := .Subsets }} -{{- $cluster := .Cluster }} -{{- range $i, $subset := $subsets }} -{{- range $revision, $version := $revVerMap }} -apiVersion: apps/v1 -{{- if $.StatefulSet }} -kind: StatefulSet -{{- else }} -kind: Deployment -{{- end }} -metadata: -{{- if $.Compatibility }} - name: {{ $.Service }}-{{ $subset.Version }}-{{ $revision }} -{{- else }} - name: {{ $.Service }}-{{ $subset.Version }} -{{- end }} -spec: - {{- if $.StatefulSet }} - serviceName: {{ $.Service }} - {{- end }} - replicas: 1 - selector: - matchLabels: - app: {{ $.Service }} - version: {{ $subset.Version }} -{{- if ne $.Locality "" }} - istio-locality: {{ $.Locality }} -{{- end }} - template: - metadata: - labels: - app: {{ $.Service }} - version: {{ $subset.Version }} - test.istio.io/class: {{ $.WorkloadClass }} -{{- if $.Compatibility }} - istio.io/rev: {{ $revision }} -{{- end }} -{{- if ne $.Locality "" }} - istio-locality: {{ $.Locality }} -{{- end }} - annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "15014" -{{- range $name, $value := $subset.Annotations }} - {{ $name.Name }}: {{ printf "%q" $value.Value }} -{{- end }} - spec: -{{- if $.ServiceAccount }} - serviceAccountName: {{ $.Service }} -{{- end }} -{{- if ne $.ImagePullSecretName "" }} - imagePullSecrets: - - name: {{ $.ImagePullSecretName }} -{{- end }} - containers: -{{- if and - (ne ($subset.Annotations.GetByName "sidecar.istio.io/inject") "false") - (ne ($subset.Annotations.GetByName "inject.istio.io/templates") "grpc") - ($.OverlayIstioProxy) -}} - - name: istio-proxy - image: auto - imagePullPolicy: {{ $.ImagePullPolicy }} - securityContext: # to allow core dumps - readOnlyRootFilesystem: false -{{- end }} -{{- if $.IncludeExtAuthz }} - - name: ext-authz - image: gcr.io/istio-testing/ext-authz:0.7 - imagePullPolicy: {{ $.ImagePullPolicy }} - ports: - - containerPort: 8000 - - containerPort: 9000 -{{- end }} - - name: app - image: {{ $.ImageHub }}/app:{{ $.ImageTag }} - imagePullPolicy: {{ $.ImagePullPolicy }} - securityContext: - runAsUser: 1338 - runAsGroup: 1338 - args: - - --metrics=15014 - - --cluster - - "{{ $cluster }}" -{{- range $i, $p := $.ContainerPorts }} -{{- if eq .Protocol "GRPC" }} -{{- if and $.ProxylessGRPC (ne $p.Port $.GRPCMagicPort) }} - - --xds-grpc-server={{ $p.Port }} -{{- end }} - - --grpc -{{- else if eq .Protocol "TCP" }} - - --tcp -{{- else }} - - --port -{{- end }} - - "{{ $p.Port }}" -{{- if $p.TLS }} - - --tls={{ $p.Port }} -{{- end }} -{{- if $p.ServerFirst }} - - --server-first={{ $p.Port }} -{{- end }} -{{- if $p.InstanceIP }} - - --bind-ip={{ $p.Port }} -{{- end }} -{{- if $p.LocalhostIP }} - - --bind-localhost={{ $p.Port }} -{{- end }} -{{- end }} - - --version - - "{{ $subset.Version }}" - - --istio-version - - "{{ $version }}" -{{- if $.TLSSettings }} - - --crt=/etc/certs/custom/cert-chain.pem - - --key=/etc/certs/custom/key.pem -{{- if $.TLSSettings.AcceptAnyALPN}} - - --disable-alpn -{{- end }} -{{- else }} - - --crt=/cert.crt - - --key=/cert.key -{{- end }} - ports: -{{- range $i, $p := $.ContainerPorts }} - - containerPort: {{ $p.Port }} -{{- if eq .Port 3333 }} - name: tcp-health-port -{{- end }} -{{- end }} - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP -{{- if $.ProxylessGRPC }} - - name: EXPOSE_GRPC_ADMIN - value: "true" -{{- end }} - readinessProbe: -{{- if $.ReadinessTCPPort }} - tcpSocket: - port: {{ $.ReadinessTCPPort }} -{{- else if $.ReadinessGRPCPort }} - grpc: - port: {{ $.ReadinessGRPCPort }} -{{- else }} - httpGet: - path: / - port: 8080 -{{- end }} - initialDelaySeconds: 1 - periodSeconds: 2 - failureThreshold: 10 - livenessProbe: - tcpSocket: - port: tcp-health-port - initialDelaySeconds: 10 - periodSeconds: 10 - failureThreshold: 10 -{{- if $.StartupProbe }} - startupProbe: - tcpSocket: - port: tcp-health-port - periodSeconds: 1 - failureThreshold: 10 -{{- end }} -{{- if $.TLSSettings }} - volumeMounts: - - mountPath: /etc/certs/custom - name: custom-certs - volumes: -{{- if $.TLSSettings.ProxyProvision }} - - emptyDir: - medium: Memory -{{- else }} - - configMap: - name: {{ $.Service }}-certs -{{- end }} - name: custom-certs -{{- end }} ---- -{{- end }} -{{- end }} -{{- if .TLSSettings}}{{if not .TLSSettings.ProxyProvision }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ $.Service }}-certs -data: - root-cert.pem: | -{{ .TLSSettings.RootCert | indent 4 }} - cert-chain.pem: | -{{ .TLSSettings.ClientCert | indent 4 }} - key.pem: | -{{.TLSSettings.Key | indent 4}} ---- -{{- end}}{{- end}} -` - - // vmDeploymentYaml aims to simulate a VM, but instead of managing the complex test setup of spinning up a VM, - // connecting, etc we run it inside a pod. The pod has pretty much all Kubernetes features disabled (DNS and SA token mount) - // such that we can adequately simulate a VM and DIY the bootstrapping. - vmDeploymentYaml = ` -{{- $subsets := .Subsets }} -{{- $cluster := .Cluster }} -{{- range $i, $subset := $subsets }} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ $.Service }}-{{ $subset.Version }} -spec: - replicas: 1 - selector: - matchLabels: - istio.io/test-vm: {{ $.Service }} - istio.io/test-vm-version: {{ $subset.Version }} - template: - metadata: - annotations: - # Sidecar is inside the pod to simulate VMs - do not inject - sidecar.istio.io/inject: "false" - labels: - # Label should not be selected. We will create a workload entry instead - istio.io/test-vm: {{ $.Service }} - istio.io/test-vm-version: {{ $subset.Version }} - spec: - # Disable kube-dns, to mirror VM - # we set policy to none and explicitly provide a set of invalid values - # for nameservers, search namespaces, etc. ndots is set to 1 so that - # the application will first try to resolve the hostname (a, a.ns, etc.) as is - # before attempting to add the search namespaces. - dnsPolicy: None - dnsConfig: - nameservers: - - "8.8.8.8" - searches: - - "com" - options: - - name: "ndots" - value: "1" - # Disable service account mount, to mirror VM - automountServiceAccountToken: false - {{- if $.ImagePullSecretName }} - imagePullSecrets: - - name: {{ $.ImagePullSecretName }} - {{- end }} - containers: - - name: istio-proxy - image: {{ $.ImageHub }}/{{ $.VM.Image }}:{{ $.ImageTag }} - imagePullPolicy: {{ $.ImagePullPolicy }} - securityContext: - capabilities: - add: - - NET_ADMIN - runAsUser: 1338 - runAsGroup: 1338 - command: - - bash - - -c - - |- - # To support image builders which cannot do RUN, do the run commands at startup. - # This exploits the fact the images remove the installer once its installed. - # This is a horrible idea for production images, but these are just for tests. - [[ -f /tmp/istio-sidecar-centos-7.rpm ]] && sudo rpm -vi /tmp/istio-sidecar-centos-7.rpm && sudo rm /tmp/istio-sidecar-centos-7.rpm - [[ -f /tmp/istio-sidecar.rpm ]] && sudo rpm -vi /tmp/istio-sidecar.rpm && sudo rm /tmp/istio-sidecar.rpm - [[ -f /tmp/istio-sidecar.deb ]] && sudo dpkg -i /tmp/istio-sidecar.deb && sudo rm /tmp/istio-sidecar.deb - - # Read root cert from and place signed certs here (can't mount directly or the dir would be unwritable) - sudo mkdir -p /var/run/secrets/istio - - # hack: remove certs that are bundled in the image - sudo rm /var/run/secrets/istio/cert-chain.pem - sudo rm /var/run/secrets/istio/key.pem - sudo chown -R istio-proxy /var/run/secrets - - # place mounted bootstrap files (token is mounted directly to the correct location) - sudo cp /var/run/secrets/istio/bootstrap/root-cert.pem /var/run/secrets/istio/root-cert.pem - sudo cp /var/run/secrets/istio/bootstrap/*.env /var/lib/istio/envoy/ - sudo cp /var/run/secrets/istio/bootstrap/mesh.yaml /etc/istio/config/mesh - sudo sh -c 'cat /var/run/secrets/istio/bootstrap/hosts >> /etc/hosts' - - # read certs from correct directory - sudo sh -c 'echo PROV_CERT=/var/run/secrets/istio >> /var/lib/istio/envoy/cluster.env' - sudo sh -c 'echo OUTPUT_CERTS=/var/run/secrets/istio >> /var/lib/istio/envoy/cluster.env' - - # TODO: run with systemctl? - export ISTIO_AGENT_FLAGS="--concurrency 2 --proxyLogLevel warning,misc:error,rbac:debug,jwt:debug" - sudo -E /usr/local/bin/istio-start.sh& - /usr/local/bin/server --cluster "{{ $cluster }}" --version "{{ $subset.Version }}" \ -{{- range $i, $p := $.ContainerPorts }} -{{- if eq .Protocol "GRPC" }} - --grpc \ -{{- else if eq .Protocol "TCP" }} - --tcp \ -{{- else }} - --port \ -{{- end }} - "{{ $p.Port }}" \ -{{- if $p.ServerFirst }} - --server-first={{ $p.Port }} \ -{{- end }} -{{- if $p.TLS }} - --tls={{ $p.Port }} \ -{{- end }} -{{- if $p.InstanceIP }} - --bind-ip={{ $p.Port }} \ -{{- end }} -{{- if $p.LocalhostIP }} - --bind-localhost={{ $p.Port }} \ -{{- end }} -{{- end }} - --crt=/var/lib/istio/cert.crt \ - --key=/var/lib/istio/cert.key - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - volumeMounts: - - mountPath: /var/run/secrets/tokens - name: {{ $.Service }}-istio-token - - mountPath: /var/run/secrets/istio/bootstrap - name: istio-vm-bootstrap - {{- range $name, $value := $subset.Annotations }} - {{- if eq $name.Name "sidecar.istio.io/bootstrapOverride" }} - - mountPath: /etc/istio-custom-bootstrap - name: custom-bootstrap-volume - {{- end }} - {{- end }} - volumes: - - secret: - secretName: {{ $.Service }}-istio-token - name: {{ $.Service }}-istio-token - - configMap: - name: {{ $.Service }}-{{ $subset.Version }}-vm-bootstrap - name: istio-vm-bootstrap - {{- range $name, $value := $subset.Annotations }} - {{- if eq $name.Name "sidecar.istio.io/bootstrapOverride" }} - - name: custom-bootstrap-volume - configMap: - name: {{ $value.Value }} - {{- end }} - {{- end }} -{{- end}} -` -) - -var ( - serviceTemplate *template.Template - deploymentTemplate *template.Template - vmDeploymentTemplate *template.Template -) - -func init() { - serviceTemplate = template.New("echo_service") - if _, err := serviceTemplate.Funcs(sprig.TxtFuncMap()).Parse(serviceYAML); err != nil { - panic(fmt.Sprintf("unable to parse echo service template: %v", err)) - } - - deploymentTemplate = template.New("echo_deployment") - if _, err := deploymentTemplate.Funcs(sprig.TxtFuncMap()).Parse(deploymentYAML); err != nil { - panic(fmt.Sprintf("unable to parse echo deployment template: %v", err)) - } - - vmDeploymentTemplate = template.New("echo_vm_deployment") - if _, err := vmDeploymentTemplate.Funcs(sprig.TxtFuncMap()).Funcs(template.FuncMap{"Lines": lines}).Parse(vmDeploymentYaml); err != nil { - panic(fmt.Sprintf("unable to parse echo vm deployment template: %v", err)) - } -} - -var _ workloadHandler = &deployment{} - -type deployment struct { - ctx resource.Context - cfg echo.Config - shouldCreateWLE bool -} - -func newDeployment(ctx resource.Context, cfg echo.Config) (*deployment, error) { - if !cfg.Cluster.IsConfig() && cfg.DeployAsVM { - return nil, fmt.Errorf("cannot deploy %s/%s as VM on non-config %s", - cfg.Namespace.Name(), - cfg.Service, - cfg.Cluster.Name()) - } - - if cfg.DeployAsVM { - if err := createVMConfig(ctx, cfg); err != nil { - return nil, fmt.Errorf("failed creating vm config for %s/%s: %v", - cfg.Namespace.Name(), - cfg.Service, - err) - } - } - - deploymentYAML, err := GenerateDeployment(cfg, nil) - if err != nil { - return nil, fmt.Errorf("failed generating echo deployment YAML for %s/%s: %v", - cfg.Namespace.Name(), - cfg.Service, err) - } - - // Apply the deployment to the configured cluster. - if err = ctx.ConfigKube(cfg.Cluster).YAML(cfg.Namespace.Name(), deploymentYAML).Apply(resource.NoCleanup); err != nil { - return nil, fmt.Errorf("failed deploying echo %s to cluster %s: %v", - cfg.ClusterLocalFQDN(), cfg.Cluster.Name(), err) - } - - return &deployment{ - ctx: ctx, - cfg: cfg, - shouldCreateWLE: cfg.DeployAsVM && !cfg.AutoRegisterVM, - }, nil -} - -// Restart performs a `kubectl rollout restart` on the echo deployment and waits for -// `kubectl rollout status` to complete before returning. -func (d *deployment) Restart() error { - var errs error - var deploymentNames []string - for _, s := range d.cfg.Subsets { - // TODO(Monkeyanator) move to common place so doesn't fall out of sync with templates - deploymentNames = append(deploymentNames, fmt.Sprintf("%s-%s", d.cfg.Service, s.Version)) - } - for _, deploymentName := range deploymentNames { - wlType := "deployment" - if d.cfg.IsStatefulSet() { - wlType = "statefulset" - } - rolloutCmd := fmt.Sprintf("kubectl rollout restart %s/%s -n %s", - wlType, deploymentName, d.cfg.Namespace.Name()) - if _, err := shell.Execute(true, rolloutCmd); err != nil { - errs = multierror.Append(errs, fmt.Errorf("failed to rollout restart %v/%v: %v", - d.cfg.Namespace.Name(), deploymentName, err)) - continue - } - waitCmd := fmt.Sprintf("kubectl rollout status %s/%s -n %s", - wlType, deploymentName, d.cfg.Namespace.Name()) - if _, err := shell.Execute(true, waitCmd); err != nil { - errs = multierror.Append(errs, fmt.Errorf("failed to wait rollout status for %v/%v: %v", - d.cfg.Namespace.Name(), deploymentName, err)) - } - } - return errs -} - -func (d *deployment) WorkloadReady(w *workload) { - if !d.shouldCreateWLE { - return - } - - // Deploy the workload entry to the primary cluster. We will read WorkloadEntry across clusters. - wle := d.workloadEntryYAML(w) - if err := d.ctx.ConfigKube(d.cfg.Cluster.Primary()).YAML(d.cfg.Namespace.Name(), wle).Apply(resource.NoCleanup); err != nil { - log.Warnf("failed deploying echo WLE for %s/%s to primary cluster: %v", - d.cfg.Namespace.Name(), - d.cfg.Service, - err) - } -} - -func (d *deployment) WorkloadNotReady(w *workload) { - if !d.shouldCreateWLE { - return - } - - wle := d.workloadEntryYAML(w) - if err := d.ctx.ConfigKube(d.cfg.Cluster.Primary()).YAML(d.cfg.Namespace.Name(), wle).Delete(); err != nil { - log.Warnf("failed deleting echo WLE for %s/%s from primary cluster: %v", - d.cfg.Namespace.Name(), - d.cfg.Service, - err) - } -} - -func (d *deployment) workloadEntryYAML(w *workload) string { - name := w.pod.Name - podIP := w.pod.Status.PodIP - sa := serviceAccount(d.cfg) - network := d.cfg.Cluster.NetworkName() - service := d.cfg.Service - version := w.pod.Labels[constants.TestVMVersionLabel] - - return fmt.Sprintf(` -apiVersion: networking.istio.io/v1alpha3 -kind: WorkloadEntry -metadata: - name: %s -spec: - address: %s - serviceAccount: %s - network: %q - labels: - app: %s - version: %s -`, name, podIP, sa, network, service, version) -} - -func GenerateDeployment(cfg echo.Config, settings *resource.Settings) (string, error) { - params, err := templateParams(cfg, settings) - if err != nil { - return "", err - } - - deploy := deploymentTemplate - if cfg.DeployAsVM { - deploy = vmDeploymentTemplate - } - - return tmpl.Execute(deploy, params) -} - -func GenerateService(cfg echo.Config) (string, error) { - params, err := templateParams(cfg, nil) - if err != nil { - return "", err - } - - return tmpl.Execute(serviceTemplate, params) -} - -var VMImages = map[echo.VMDistro]string{ - echo.UbuntuXenial: "app_sidecar_ubuntu_xenial", - echo.UbuntuJammy: "app_sidecar_ubuntu_jammy", - echo.Debian11: "app_sidecar_debian_11", - echo.Centos7: "app_sidecar_centos_7", - // echo.Rockylinux8: "app_sidecar_rockylinux_8", TODO(https://github.com/istio/istio/issues/38224) -} - -var RevVMImages = func() map[string]echo.VMDistro { - r := map[string]echo.VMDistro{} - for k, v := range VMImages { - r[v] = k - } - return r -}() - -func templateParams(cfg echo.Config, settings *resource.Settings) (map[string]interface{}, error) { - if settings == nil { - var err error - settings, err = resource.SettingsFromCommandLine("template") - if err != nil { - return nil, err - } - } - - supportStartupProbe := cfg.Cluster.MinKubeVersion(0) - - vmImage := VMImages[cfg.VMDistro] - _, knownImage := RevVMImages[cfg.VMDistro] - if vmImage == "" { - if knownImage { - vmImage = cfg.VMDistro - } else { - vmImage = VMImages[echo.DefaultVMDistro] - } - log.Debugf("no image for distro %s, defaulting to %s", cfg.VMDistro, echo.DefaultVMDistro) - } - namespace := "" - if cfg.Namespace != nil { - namespace = cfg.Namespace.Name() - } - imagePullSecretName, err := settings.Image.PullSecretName() - if err != nil { - return nil, err - } - params := map[string]interface{}{ - "ImageHub": settings.Image.Hub, - "ImageTag": strings.TrimSuffix(settings.Image.Tag, "-distroless"), - "ImagePullPolicy": settings.Image.PullPolicy, - "ImagePullSecretName": imagePullSecretName, - "Service": cfg.Service, - "Version": cfg.Version, - "Headless": cfg.Headless, - "StatefulSet": cfg.StatefulSet, - "ProxylessGRPC": cfg.IsProxylessGRPC(), - "GRPCMagicPort": grpcMagicPort, - "Locality": cfg.Locality, - "ServiceAccount": cfg.ServiceAccount, - "ServicePorts": cfg.Ports.GetServicePorts(), - "ContainerPorts": getContainerPorts(cfg), - "ServiceAnnotations": cfg.ServiceAnnotations, - "Subsets": cfg.Subsets, - "TLSSettings": cfg.TLSSettings, - "Cluster": cfg.Cluster.Name(), - "Namespace": namespace, - "ReadinessTCPPort": cfg.ReadinessTCPPort, - "ReadinessGRPCPort": cfg.ReadinessGRPCPort, - "VM": map[string]interface{}{ - "Image": vmImage, - }, - "StartupProbe": supportStartupProbe, - "IncludeExtAuthz": cfg.IncludeExtAuthz, - "Revisions": settings.Revisions.TemplateMap(), - "Compatibility": settings.Compatibility, - "WorkloadClass": cfg.WorkloadClass(), - "OverlayIstioProxy": canCreateIstioProxy(settings.Revisions.Minimum()), - "IPFamilies": cfg.IPFamilies, - "IPFamilyPolicy": cfg.IPFamilyPolicy, - } - return params, nil -} - -func lines(input string) []string { - out := make([]string, 0) - scanner := bufio.NewScanner(strings.NewReader(input)) - for scanner.Scan() { - out = append(out, scanner.Text()) - } - return out -} - -// createVMConfig sets up a Service account, -func createVMConfig(ctx resource.Context, cfg echo.Config) error { - istioCtl, err := istioctl.New(ctx, istioctl.Config{Cluster: cfg.Cluster}) - if err != nil { - return err - } - // generate config files for VM bootstrap - dirname := fmt.Sprintf("%s-vm-config-", cfg.Service) - dir, err := ctx.CreateDirectory(dirname) - if err != nil { - return err - } - - wg := tmpl.MustEvaluate(` -apiVersion: networking.istio.io/v1alpha3 -kind: WorkloadGroup -metadata: - name: {{.name}} - namespace: {{.namespace}} -spec: - metadata: - labels: - app: {{.name}} - test.istio.io/class: {{ .workloadClass }} - template: - serviceAccount: {{.serviceAccount}} - network: "{{.network}}" - probe: - failureThreshold: 5 - httpGet: - path: / - port: 8080 - periodSeconds: 2 - successThreshold: 1 - timeoutSeconds: 2 - -`, map[string]string{ - "name": cfg.Service, - "namespace": cfg.Namespace.Name(), - "serviceAccount": serviceAccount(cfg), - "network": cfg.Cluster.NetworkName(), - "workloadClass": cfg.WorkloadClass(), - }) - - // Push the WorkloadGroup for auto-registration - if cfg.AutoRegisterVM { - if err := ctx.ConfigKube(cfg.Cluster).YAML(cfg.Namespace.Name(), wg).Apply(resource.NoCleanup); err != nil { - return err - } - } - - if cfg.ServiceAccount { - // create service account, the next workload command will use it to generate a token - err = createServiceAccount(cfg.Cluster, cfg.Namespace.Name(), serviceAccount(cfg)) - if err != nil && !kerrors.IsAlreadyExists(err) { - return err - } - } - - if err := os.WriteFile(path.Join(dir, "workloadgroup.yaml"), []byte(wg), 0o600); err != nil { - return err - } - - ist, err := istio.Get(ctx) - if err != nil { - return err - } - // this will wait until the eastwest gateway has an IP before running the next command - istiodAddr, err := ist.RemoteDiscoveryAddressFor(cfg.Cluster) - if err != nil { - return err - } - - var subsetDir string - for _, subset := range cfg.Subsets { - subsetDir, err = os.MkdirTemp(dir, subset.Version+"-") - if err != nil { - return err - } - cmd := []string{ - "x", "workload", "entry", "configure", - "-f", path.Join(dir, "workloadgroup.yaml"), - "-o", subsetDir, - } - if ctx.Clusters().IsMulticluster() { - // When VMs talk about "cluster", they refer to the cluster they connect to for discovery - cmd = append(cmd, "--clusterID", cfg.Cluster.Name()) - } - if cfg.AutoRegisterVM { - cmd = append(cmd, "--autoregister") - } - if !ctx.Environment().(*kube.Environment).Settings().LoadBalancerSupported { - // LoadBalancer may not be supported and the command doesn't have NodePort fallback logic that the tests do - cmd = append(cmd, "--ingressIP", istiodAddr.IP.String()) - } - if nsLabels, err := cfg.Namespace.Labels(); err != nil { - log.Warnf("failed fetching labels for %s; assuming no-revision (can cause failures): %v", cfg.Namespace.Name(), err) - } else if rev := nsLabels[label.IoIstioRev.Name]; rev != "" { - cmd = append(cmd, "--revision", rev) - } - // make sure namespace controller has time to create root-cert ConfigMap - if err := retry.UntilSuccess(func() error { - stdout, stderr, err := istioCtl.Invoke(cmd) - if err != nil { - return fmt.Errorf("%v:\nstdout: %s\nstderr: %s", err, stdout, stderr) - } - return nil - }, retry.Timeout(20*time.Second)); err != nil { - return err - } - - // support proxyConfig customizations on VMs via annotation in the echo API. - for k, v := range subset.Annotations { - if k.Name == "proxy.istio.io/config" { - if err := patchProxyConfigFile(path.Join(subsetDir, "mesh.yaml"), v.Value); err != nil { - return fmt.Errorf("failed patching proxyconfig: %v", err) - } - } - } - - if err := customizeVMEnvironment(ctx, cfg, path.Join(subsetDir, "cluster.env"), istiodAddr); err != nil { - return fmt.Errorf("failed customizing cluster.env: %v", err) - } - - // push boostrap config as a ConfigMap so we can mount it on our "vm" pods - cmData := map[string][]byte{} - generatedFiles, err := os.ReadDir(subsetDir) - if err != nil { - return err - } - for _, file := range generatedFiles { - if file.IsDir() { - continue - } - cmData[file.Name()], err = os.ReadFile(path.Join(subsetDir, file.Name())) - if err != nil { - return err - } - } - cmName := fmt.Sprintf("%s-%s-vm-bootstrap", cfg.Service, subset.Version) - cm := &kubeCore.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: cmName}, BinaryData: cmData} - _, err = cfg.Cluster.CoreV1().ConfigMaps(cfg.Namespace.Name()).Create(context.TODO(), cm, metav1.CreateOptions{}) - if err != nil && !kerrors.IsAlreadyExists(err) { - return fmt.Errorf("failed creating configmap %s: %v", cm.Name, err) - } - } - - // push the generated token as a Secret (only need one, they should be identical) - token, err := os.ReadFile(path.Join(subsetDir, "istio-token")) - if err != nil { - return err - } - secret := &kubeCore.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: cfg.Service + "-istio-token", - Namespace: cfg.Namespace.Name(), - }, - Data: map[string][]byte{ - "istio-token": token, - }, - } - if _, err := cfg.Cluster.CoreV1().Secrets(cfg.Namespace.Name()).Create(context.TODO(), secret, metav1.CreateOptions{}); err != nil { - if kerrors.IsAlreadyExists(err) { - if _, err := cfg.Cluster.CoreV1().Secrets(cfg.Namespace.Name()).Update(context.TODO(), secret, metav1.UpdateOptions{}); err != nil { - return fmt.Errorf("failed updating secret %s: %v", secret.Name, err) - } - } else { - return fmt.Errorf("failed creating secret %s: %v", secret.Name, err) - } - } - - return nil -} - -func patchProxyConfigFile(file string, overrides string) error { - config, err := readMeshConfig(file) - if err != nil { - return err - } - overrideYAML := "defaultConfig:\n" - overrideYAML += istio.Indent(overrides, " ") - if err := protomarshal.ApplyYAML(overrideYAML, config.DefaultConfig); err != nil { - return err - } - outYAML, err := protomarshal.ToYAML(config) - if err != nil { - return err - } - return os.WriteFile(file, []byte(outYAML), 0o744) -} - -func readMeshConfig(file string) (*meshconfig.MeshConfig, error) { - baseYAML, err := os.ReadFile(file) - if err != nil { - return nil, err - } - config := &meshconfig.MeshConfig{} - if err := protomarshal.ApplyYAML(string(baseYAML), config); err != nil { - return nil, err - } - return config, nil -} - -func createServiceAccount(client kubernetes.Interface, ns string, serviceAccount string) error { - scopes.Framework.Debugf("Creating service account for: %s/%s", ns, serviceAccount) - _, err := client.CoreV1().ServiceAccounts(ns).Create(context.TODO(), &kubeCore.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{Name: serviceAccount}, - }, metav1.CreateOptions{}) - return err -} - -// getContainerPorts converts the ports to a port list of container ports. -// Adds ports for health/readiness if necessary. -func getContainerPorts(cfg echo.Config) echoCommon.PortList { - ports := cfg.Ports - containerPorts := make(echoCommon.PortList, 0, len(ports)) - var healthPort *echoCommon.Port - var readyPort *echoCommon.Port - for _, p := range ports { - // Add the port to the set of application ports. - cport := &echoCommon.Port{ - Name: p.Name, - Protocol: p.Protocol, - Port: p.WorkloadPort, - TLS: p.TLS, - ServerFirst: p.ServerFirst, - InstanceIP: p.InstanceIP, - LocalhostIP: p.LocalhostIP, - } - containerPorts = append(containerPorts, cport) - - switch p.Protocol { - case protocol.GRPC: - continue - case protocol.HTTP: - if p.WorkloadPort == httpReadinessPort { - readyPort = cport - } - default: - if p.WorkloadPort == tcpHealthPort { - healthPort = cport - } - } - } - - // If we haven't added the readiness/health ports, do so now. - if readyPort == nil { - containerPorts = append(containerPorts, &echoCommon.Port{ - Name: "http-readiness-port", - Protocol: protocol.HTTP, - Port: httpReadinessPort, - }) - } - if healthPort == nil { - containerPorts = append(containerPorts, &echoCommon.Port{ - Name: "tcp-health-port", - Protocol: protocol.HTTP, - Port: tcpHealthPort, - }) - } - if cfg.IsProxylessGRPC() { - containerPorts = append(containerPorts, &echoCommon.Port{ - Name: "grpc-magic-port", - Protocol: protocol.GRPC, - Port: grpcMagicPort, - LocalhostIP: true, - }) - } - return containerPorts -} - -func customizeVMEnvironment(ctx resource.Context, cfg echo.Config, clusterEnv string, istiodAddr net.TCPAddr) error { - f, err := os.OpenFile(clusterEnv, os.O_APPEND|os.O_WRONLY, os.ModeAppend) - if err != nil { - return fmt.Errorf("failed opening %s: %v", clusterEnv, err) - } - if cfg.VMEnvironment != nil { - for k, v := range cfg.VMEnvironment { - addition := fmt.Sprintf("%s=%s\n", k, v) - _, err = f.Write([]byte(addition)) - if err != nil { - return fmt.Errorf("failed writing %q to %s: %v", addition, clusterEnv, err) - } - } - } - if !ctx.Environment().(*kube.Environment).Settings().LoadBalancerSupported { - // customize cluster.env with NodePort mapping - _, err = f.Write([]byte(fmt.Sprintf("ISTIO_PILOT_PORT=%d\n", istiodAddr.Port))) - if err != nil { - return err - } - } - return err -} - -func canCreateIstioProxy(version resource.IstioVersion) bool { - // if no revision specified create the istio-proxy - if string(version) == "" { - return true - } - if minor := strings.Split(string(version), ".")[1]; minor > "8" || len(minor) > 1 { - return true - } - return false -} diff --git a/pkg/test/framework/components/echo/kube/deployment_test.go b/pkg/test/framework/components/echo/kube/deployment_test.go deleted file mode 100644 index 22cc94b90..000000000 --- a/pkg/test/framework/components/echo/kube/deployment_test.go +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package kube - -import ( - "testing" -) - -import ( - testutil "github.com/apache/dubbo-go-pixiu/pilot/test/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster/clusterboot" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/config" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -func TestDeploymentYAML(t *testing.T) { - testCase := []struct { - name string - wantFilePath string - config echo.Config - revVerMap resource.RevVerMap - compatibility bool - }{ - { - name: "basic", - wantFilePath: "testdata/basic.yaml", - config: echo.Config{ - Service: "foo", - Version: "bar", - Ports: []echo.Port{ - { - Name: "http", - Protocol: protocol.HTTP, - WorkloadPort: 8090, - ServicePort: 8090, - }, - }, - }, - }, - { - name: "two-workloads-one-nosidecar", - wantFilePath: "testdata/two-workloads-one-nosidecar.yaml", - config: echo.Config{ - Service: "foo", - Ports: []echo.Port{ - { - Name: "http", - Protocol: protocol.HTTP, - WorkloadPort: 8090, - ServicePort: 8090, - }, - }, - Subsets: []echo.SubsetConfig{ - { - Version: "v1", - }, - { - Version: "nosidecar", - Annotations: echo.NewAnnotations().SetBool(echo.SidecarInject, false), - }, - }, - }, - }, - { - name: "healthcheck-rewrite", - wantFilePath: "testdata/healthcheck-rewrite.yaml", - config: echo.Config{ - Service: "healthcheck", - Ports: []echo.Port{{ - Name: "http-8080", - Protocol: protocol.HTTP, - ServicePort: 8080, - WorkloadPort: 8080, - }}, - Subsets: []echo.SubsetConfig{ - { - Annotations: echo.NewAnnotations().SetBool(echo.SidecarRewriteAppHTTPProbers, true), - }, - }, - }, - }, - { - name: "multiversion", - wantFilePath: "testdata/multiversion.yaml", - config: echo.Config{ - Service: "multiversion", - Subsets: []echo.SubsetConfig{ - { - Version: "v-istio", - }, - { - Version: "v-legacy", - Annotations: echo.NewAnnotations().SetBool(echo.SidecarInject, false), - }, - }, - Ports: []echo.Port{ - { - Name: "http", - Protocol: protocol.HTTP, - WorkloadPort: 8090, - ServicePort: 8090, - }, - { - Name: "tcp", - Protocol: protocol.TCP, - WorkloadPort: 9000, - ServicePort: 9000, - }, - { - Name: "grpc", - Protocol: protocol.GRPC, - WorkloadPort: 9090, - ServicePort: 9090, - }, - }, - }, - }, - { - name: "multiple-istio-versions", - wantFilePath: "testdata/multiple-istio-versions.yaml", - config: echo.Config{ - Service: "foo", - Version: "bar", - Ports: []echo.Port{ - { - Name: "http", - Protocol: protocol.HTTP, - WorkloadPort: 8090, - ServicePort: 8090, - }, - }, - }, - revVerMap: resource.RevVerMap{ - "rev-a": resource.IstioVersion("1.9.0"), - "rev-b": resource.IstioVersion("1.10.0"), - }, - compatibility: true, - }, - { - name: "multiple-istio-versions-no-proxy", - wantFilePath: "testdata/multiple-istio-versions-no-proxy.yaml", - config: echo.Config{ - Service: "foo", - Version: "bar", - Ports: []echo.Port{ - { - Name: "http", - Protocol: protocol.HTTP, - WorkloadPort: 8090, - ServicePort: 8090, - }, - }, - }, - revVerMap: resource.RevVerMap{ - "rev-a": resource.IstioVersion("1.8.2"), - "rev-b": resource.IstioVersion("1.9.0"), - }, - compatibility: true, - }, - } - for _, tc := range testCase { - t.Run(tc.name, func(t *testing.T) { - clusters, err := clusterboot.NewFactory().With(cluster.Config{ - Kind: cluster.Fake, Name: "cluster-0", - Meta: config.Map{"majorVersion": 1, "minorVersion": 16}, - }).Build() - if err != nil { - t.Fatal(err) - } - tc.config.Cluster = clusters[0] - if err := tc.config.FillDefaults(nil); err != nil { - t.Errorf("failed filling in defaults: %v", err) - } - if !config.Parsed() { - config.Parse() - } - settings := &resource.Settings{ - Revisions: tc.revVerMap, - Compatibility: tc.compatibility, - Image: resource.ImageSettings{ - Hub: "testing.hub", - Tag: "latest", - PullPolicy: "Always", - PullSecret: "testdata/secret.yaml", - }, - } - serviceYAML, err := GenerateService(tc.config) - if err != nil { - t.Errorf("failed to generate service %v", err) - } - deploymentYAML, err := GenerateDeployment(tc.config, settings) - if err != nil { - t.Errorf("failed to generate deployment %v", err) - } - gotBytes := []byte(serviceYAML + "---" + deploymentYAML) - wantedBytes := testutil.ReadGoldenFile(t, gotBytes, tc.wantFilePath) - - wantBytes := testutil.StripVersion(wantedBytes) - gotBytes = testutil.StripVersion(gotBytes) - - testutil.RefreshGoldenFile(t, gotBytes, tc.wantFilePath) - - testutil.CompareBytes(t, gotBytes, wantBytes, tc.wantFilePath) - }) - } -} diff --git a/pkg/test/framework/components/echo/kube/instance.go b/pkg/test/framework/components/echo/kube/instance.go deleted file mode 100644 index 70f137b50..000000000 --- a/pkg/test/framework/components/echo/kube/instance.go +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "context" - "fmt" - "io" - "time" -) - -import ( - "github.com/hashicorp/go-multierror" - kubeCore "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/test" - echoClient "github.com/apache/dubbo-go-pixiu/pkg/test/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common/scheme" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/common" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" - "github.com/apache/dubbo-go-pixiu/pkg/util/istiomultierror" -) - -const ( - tcpHealthPort = 3333 - httpReadinessPort = 8080 -) - -var ( - _ echo.Instance = &instance{} - _ io.Closer = &instance{} - - startDelay = retry.BackoffDelay(time.Millisecond * 100) -) - -type instance struct { - id resource.ID - cfg echo.Config - clusterIP string - clusterIPs []string - ctx resource.Context - cluster cluster.Cluster - workloadMgr *workloadManager - deployment *deployment - workloadFilter []echo.Workload -} - -func newInstance(ctx resource.Context, originalCfg echo.Config) (out *instance, err error) { - cfg := originalCfg.DeepCopy() - - c := &instance{ - cfg: cfg, - ctx: ctx, - cluster: cfg.Cluster, - } - - // Deploy echo to the cluster - c.deployment, err = newDeployment(ctx, cfg) - if err != nil { - return nil, err - } - - // Create the manager for echo workloads for this instance. - c.workloadMgr, err = newWorkloadManager(ctx, cfg, c.deployment) - if err != nil { - return nil, err - } - - // Now that we have the successfully created the workload manager, track this resource so - // that it will be closed when it goes out of scope. - c.id = ctx.TrackResource(c) - - // Now retrieve the service information to find the ClusterIP - s, err := c.cluster.CoreV1().Services(cfg.Namespace.Name()).Get(context.TODO(), cfg.Service, metav1.GetOptions{}) - if err != nil { - return nil, err - } - - c.clusterIP = s.Spec.ClusterIP - c.clusterIPs = s.Spec.ClusterIPs - switch c.clusterIP { - case kubeCore.ClusterIPNone, "": - if !cfg.Headless { - return nil, fmt.Errorf("invalid ClusterIP %s for non-headless service %s/%s", - c.clusterIP, - c.cfg.Namespace.Name(), - c.cfg.Service) - } - c.clusterIP = "" - } - - // Start the workload manager. - if err := c.workloadMgr.Start(); err != nil { - return nil, err - } - - return c, nil -} - -func (c *instance) ID() resource.ID { - return c.id -} - -func (c *instance) Address() string { - return c.clusterIP -} - -func (c *instance) Addresses() []string { - return c.clusterIPs -} - -func (c *instance) Workloads() (echo.Workloads, error) { - wls, err := c.workloadMgr.ReadyWorkloads() - if err != nil { - return nil, err - } - final := []echo.Workload{} - for _, wl := range wls { - filtered := false - for _, filter := range c.workloadFilter { - if wl.Address() != filter.Address() { - filtered = true - break - } - } - if !filtered { - final = append(final, wl) - } - } - return final, nil -} - -func (c *instance) WorkloadsOrFail(t test.Failer) echo.Workloads { - t.Helper() - out, err := c.Workloads() - if err != nil { - t.Fatal(err) - } - return out -} - -func (c *instance) MustWorkloads() echo.Workloads { - out, err := c.Workloads() - if err != nil { - panic(err) - } - return out -} - -func (c *instance) Clusters() cluster.Clusters { - return cluster.Clusters{c.cluster} -} - -func (c *instance) Instances() echo.Instances { - return echo.Instances{c} -} - -func (c *instance) firstClient() (*echoClient.Client, error) { - workloads, err := c.Workloads() - if err != nil { - return nil, err - } - return workloads[0].(*workload).Client() -} - -func (c *instance) Close() (err error) { - return c.workloadMgr.Close() -} - -func (c *instance) NamespacedName() echo.NamespacedName { - return c.cfg.NamespacedName() -} - -func (c *instance) PortForName(name string) echo.Port { - return c.cfg.Ports.MustForName(name) -} - -func (c *instance) Config() echo.Config { - return c.cfg -} - -func (c *instance) WithWorkloads(wls ...echo.Workload) echo.Instance { - n := *c - c.workloadFilter = wls - return &n -} - -func (c *instance) Cluster() cluster.Cluster { - return c.cfg.Cluster -} - -func (c *instance) Call(opts echo.CallOptions) (echo.CallResult, error) { - return c.aggregateResponses(opts) -} - -func (c *instance) CallOrFail(t test.Failer, opts echo.CallOptions) echo.CallResult { - t.Helper() - r, err := c.Call(opts) - if err != nil { - t.Fatal(err) - } - return r -} - -func (c *instance) Restart() error { - // Wait for all current workloads to become ready and preserve the original count. - origWorkloads, err := c.workloadMgr.WaitForReadyWorkloads() - if err != nil { - return fmt.Errorf("restart failed to get initial workloads: %v", err) - } - - // Restart the deployment. - if err := c.deployment.Restart(); err != nil { - return err - } - - // Wait until all pods are ready and match the original count. - return retry.UntilSuccess(func() (err error) { - // Get the currently ready workloads. - workloads, err := c.workloadMgr.WaitForReadyWorkloads() - if err != nil { - return fmt.Errorf("failed waiting for restarted pods for echo %s/%s: %v", - c.cfg.Namespace.Name(), c.cfg.Service, err) - } - - // Make sure the number of pods matches the original. - if len(workloads) != len(origWorkloads) { - return fmt.Errorf("failed restarting echo %s/%s: number of pods %d does not match original %d", - c.cfg.Namespace.Name(), c.cfg.Service, len(workloads), len(origWorkloads)) - } - - return nil - }, retry.Timeout(c.cfg.ReadinessTimeout), startDelay) -} - -// aggregateResponses forwards an echo request from all workloads belonging to this echo instance and aggregates the results. -func (c *instance) aggregateResponses(opts echo.CallOptions) (echo.CallResult, error) { - // TODO put this somewhere else, or require users explicitly set the protocol - quite hacky - if c.Config().IsProxylessGRPC() && (opts.Scheme == scheme.GRPC || opts.Port.Name == "grpc" || opts.Port.Protocol == protocol.GRPC) { - // for gRPC calls, use XDS resolver - opts.Scheme = scheme.XDS - } - - resps := make(echoClient.Responses, 0) - workloads, err := c.Workloads() - if err != nil { - return echo.CallResult{}, err - } - aggErr := istiomultierror.New() - for _, w := range workloads { - clusterName := w.(*workload).cluster.Name() - serviceName := fmt.Sprintf("%s (cluster=%s)", c.cfg.Service, clusterName) - - out, err := common.ForwardEcho(serviceName, c, opts, w.(*workload).Client) - if err != nil { - aggErr = multierror.Append(aggErr, err) - continue - } - resps = append(resps, out.Responses...) - } - if aggErr.ErrorOrNil() != nil { - return echo.CallResult{}, aggErr - } - - return echo.CallResult{ - From: c, - Opts: opts, - Responses: resps, - }, nil -} diff --git a/pkg/test/framework/components/echo/kube/pod_controller.go b/pkg/test/framework/components/echo/kube/pod_controller.go deleted file mode 100644 index 2ef501ec8..000000000 --- a/pkg/test/framework/components/echo/kube/pod_controller.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "time" -) - -import ( - "istio.io/pkg/log" - kubeCore "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/queue" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" -) - -var _ cache.Controller = &podController{} - -type podHandler func(pod *kubeCore.Pod) error - -type podHandlers struct { - added podHandler - updated podHandler - deleted podHandler -} - -type podController struct { - q queue.Instance - informer cache.Controller -} - -func newPodController(cfg echo.Config, handlers podHandlers) *podController { - s := newPodSelector(cfg) - podListWatch := cache.NewFilteredListWatchFromClient(cfg.Cluster.CoreV1().RESTClient(), - "pods", - cfg.Namespace.Name(), - func(options *metav1.ListOptions) { - if len(options.LabelSelector) > 0 { - options.LabelSelector += "," - } - options.LabelSelector += s.String() - }) - q := queue.NewQueue(1 * time.Second) - _, informer := cache.NewInformer(podListWatch, &kubeCore.Pod{}, 0, cache.ResourceEventHandlerFuncs{ - AddFunc: func(newObj interface{}) { - q.Push(func() error { - return handlers.added(newObj.(*kubeCore.Pod)) - }) - }, - UpdateFunc: func(old, cur interface{}) { - q.Push(func() error { - oldObj := old.(metav1.Object) - newObj := cur.(metav1.Object) - - if oldObj.GetResourceVersion() != newObj.GetResourceVersion() { - return handlers.updated(newObj.(*kubeCore.Pod)) - } - return nil - }) - }, - DeleteFunc: func(curr interface{}) { - q.Push(func() error { - pod, ok := curr.(*kubeCore.Pod) - if !ok { - tombstone, ok := curr.(cache.DeletedFinalStateUnknown) - if !ok { - log.Errorf("Couldn't get object from tombstone %#v", curr) - return nil - } - pod, ok = tombstone.Obj.(*kubeCore.Pod) - if !ok { - log.Errorf("Tombstone contained object that is not a pod %#v", curr) - return nil - } - } - return handlers.deleted(pod) - }) - }, - }) - - return &podController{ - q: q, - informer: informer, - } -} - -func (c *podController) Run(stop <-chan struct{}) { - go c.informer.Run(stop) - cache.WaitForCacheSync(stop, c.HasSynced) - go c.q.Run(stop) -} - -func (c *podController) HasSynced() bool { - return c.informer.HasSynced() -} - -func (c *podController) WaitForSync(stopCh <-chan struct{}) bool { - return cache.WaitForNamedCacheSync("echo", stopCh, c.informer.HasSynced) -} - -func (c *podController) LastSyncResourceVersion() string { - return c.informer.LastSyncResourceVersion() -} diff --git a/pkg/test/framework/components/echo/kube/sidecar.go b/pkg/test/framework/components/echo/kube/sidecar.go deleted file mode 100644 index 77add3da3..000000000 --- a/pkg/test/framework/components/echo/kube/sidecar.go +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "context" - "errors" - "fmt" - "strings" - "time" -) - -import ( - envoyAdmin "github.com/envoyproxy/go-control-plane/envoy/admin/v3" - dto "github.com/prometheus/client_model/go" - "github.com/prometheus/common/expfmt" - "google.golang.org/protobuf/proto" - kubeCore "k8s.io/api/core/v1" -) - -import ( - _ "github.com/apache/dubbo-go-pixiu/pkg/config/xds" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -const ( - proxyContainerName = "istio-proxy" - - // DefaultTimeout the default timeout for the entire retry operation - defaultConfigTimeout = time.Second * 30 - - // DefaultDelay the default delay between successive retry attempts - defaultConfigDelay = time.Millisecond * 100 -) - -var _ echo.Sidecar = &sidecar{} - -type sidecar struct { - podNamespace string - podName string - cluster cluster.Cluster -} - -func newSidecar(pod kubeCore.Pod, cluster cluster.Cluster) *sidecar { - sidecar := &sidecar{ - podNamespace: pod.Namespace, - podName: pod.Name, - cluster: cluster, - } - - return sidecar -} - -func (s *sidecar) Info() (*envoyAdmin.ServerInfo, error) { - msg := &envoyAdmin.ServerInfo{} - if err := s.adminRequest("server_info", msg); err != nil { - return nil, err - } - - return msg, nil -} - -func (s *sidecar) InfoOrFail(t test.Failer) *envoyAdmin.ServerInfo { - t.Helper() - info, err := s.Info() - if err != nil { - t.Fatal(err) - } - return info -} - -func (s *sidecar) Config() (*envoyAdmin.ConfigDump, error) { - msg := &envoyAdmin.ConfigDump{} - if err := s.adminRequest("config_dump", msg); err != nil { - return nil, err - } - - return msg, nil -} - -func (s *sidecar) ConfigOrFail(t test.Failer) *envoyAdmin.ConfigDump { - t.Helper() - cfg, err := s.Config() - if err != nil { - t.Fatal(err) - } - return cfg -} - -func (s *sidecar) WaitForConfig(accept func(*envoyAdmin.ConfigDump) (bool, error), options ...retry.Option) error { - options = append([]retry.Option{retry.BackoffDelay(defaultConfigDelay), retry.Timeout(defaultConfigTimeout)}, options...) - - var cfg *envoyAdmin.ConfigDump - _, err := retry.UntilComplete(func() (result interface{}, completed bool, err error) { - cfg, err = s.Config() - if err != nil { - if strings.Contains(err.Error(), "could not resolve Any message type") { - // Unable to parse an Any in the message, likely due to missing imports. - // This is not a recoverable error. - return nil, true, nil - } - if strings.Contains(err.Error(), `Any JSON doesn't have '@type'`) { - // Unable to parse an Any in the message, likely due to an older version. - // This is not a recoverable error. - return nil, true, nil - } - return nil, false, err - } - - accepted, err := accept(cfg) - if err != nil { - // Accept returned an error - retry. - return nil, false, err - } - - if accepted { - // The configuration was accepted. - return nil, true, nil - } - - // The configuration was rejected, don't try again. - return nil, true, errors.New("envoy config rejected") - }, options...) - if err != nil { - configDumpStr := "nil" - if cfg != nil { - b, err := protomarshal.MarshalIndent(cfg, " ") - if err == nil { - configDumpStr = string(b) - } - } - - return fmt.Errorf("failed waiting for Envoy configuration: %v. Last config_dump:\n%s", err, configDumpStr) - } - return nil -} - -func (s *sidecar) WaitForConfigOrFail(t test.Failer, accept func(*envoyAdmin.ConfigDump) (bool, error), options ...retry.Option) { - t.Helper() - if err := s.WaitForConfig(accept, options...); err != nil { - t.Fatal(err) - } -} - -func (s *sidecar) Clusters() (*envoyAdmin.Clusters, error) { - msg := &envoyAdmin.Clusters{} - if err := s.adminRequest("clusters?format=json", msg); err != nil { - return nil, err - } - - return msg, nil -} - -func (s *sidecar) ClustersOrFail(t test.Failer) *envoyAdmin.Clusters { - t.Helper() - clusters, err := s.Clusters() - if err != nil { - t.Fatal(err) - } - return clusters -} - -func (s *sidecar) Listeners() (*envoyAdmin.Listeners, error) { - msg := &envoyAdmin.Listeners{} - if err := s.adminRequest("listeners?format=json", msg); err != nil { - return nil, err - } - - return msg, nil -} - -func (s *sidecar) ListenersOrFail(t test.Failer) *envoyAdmin.Listeners { - t.Helper() - listeners, err := s.Listeners() - if err != nil { - t.Fatal(err) - } - return listeners -} - -func (s *sidecar) Stats() (map[string]*dto.MetricFamily, error) { - return s.proxyStats() -} - -func (s *sidecar) StatsOrFail(t test.Failer) map[string]*dto.MetricFamily { - t.Helper() - stats, err := s.Stats() - if err != nil { - t.Fatal(err) - } - return stats -} - -func (s *sidecar) proxyStats() (map[string]*dto.MetricFamily, error) { - // Exec onto the pod and make a curl request to the admin port, writing - command := "pilot-agent request GET /stats/prometheus" - stdout, stderr, err := s.cluster.PodExec(s.podName, s.podNamespace, proxyContainerName, command) - if err != nil { - return nil, fmt.Errorf("failed exec on pod %s/%s: %v. Command: %s. Output:\n%s", - s.podNamespace, s.podName, err, command, stdout+stderr) - } - - parser := expfmt.TextParser{} - mfMap, err := parser.TextToMetricFamilies(strings.NewReader(stdout)) - if err != nil { - return nil, fmt.Errorf("failed parsing prometheus stats: %v", err) - } - return mfMap, nil -} - -func (s *sidecar) adminRequest(path string, out proto.Message) error { - // Exec onto the pod and make a curl request to the admin port, writing - command := fmt.Sprintf("pilot-agent request GET %s", path) - stdout, stderr, err := s.cluster.PodExec(s.podName, s.podNamespace, proxyContainerName, command) - if err != nil { - return fmt.Errorf("failed exec on pod %s/%s: %v. Command: %s. Output:\n%s", - s.podNamespace, s.podName, err, command, stdout+stderr) - } - - if err := protomarshal.UnmarshalAllowUnknown([]byte(stdout), out); err != nil { - return fmt.Errorf("failed parsing Envoy admin response from '/%s': %v\nResponse JSON: %s", path, err, stdout) - } - return nil -} - -func (s *sidecar) Logs() (string, error) { - return s.cluster.PodLogs(context.TODO(), s.podName, s.podNamespace, proxyContainerName, false) -} - -func (s *sidecar) LogsOrFail(t test.Failer) string { - t.Helper() - logs, err := s.Logs() - if err != nil { - t.Fatal(err) - } - return logs -} diff --git a/pkg/test/framework/components/echo/kube/testdata/basic.yaml b/pkg/test/framework/components/echo/kube/testdata/basic.yaml deleted file mode 100644 index 3045cfaef..000000000 --- a/pkg/test/framework/components/echo/kube/testdata/basic.yaml +++ /dev/null @@ -1,100 +0,0 @@ - -apiVersion: v1 -kind: Service -metadata: - name: foo - labels: - app: foo -spec: - ports: - - name: grpc - port: 7070 - targetPort: 7070 - - name: http - port: 8090 - targetPort: 8090 - selector: - app: foo ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: foo-bar -spec: - replicas: 1 - selector: - matchLabels: - app: foo - version: bar - template: - metadata: - labels: - app: foo - version: bar - test.istio.io/class: standard - annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "15014" - spec: - imagePullSecrets: - - name: myregistrykey - containers: - - name: istio-proxy - image: auto - imagePullPolicy: Always - securityContext: # to allow core dumps - readOnlyRootFilesystem: false - - name: app - image: testing.hub/app:latest - imagePullPolicy: Always - securityContext: - runAsUser: 1338 - runAsGroup: 1338 - args: - - --metrics=15014 - - --cluster - - "cluster-0" - - --grpc - - "7070" - - --port - - "8090" - - --port - - "8080" - - --port - - "3333" - - --version - - "bar" - - --istio-version - - "" - - --crt=/cert.crt - - --key=/cert.key - ports: - - containerPort: 7070 - - containerPort: 8090 - - containerPort: 8080 - - containerPort: 3333 - name: tcp-health-port - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - readinessProbe: - httpGet: - path: / - port: 8080 - initialDelaySeconds: 1 - periodSeconds: 2 - failureThreshold: 10 - livenessProbe: - tcpSocket: - port: tcp-health-port - initialDelaySeconds: 10 - periodSeconds: 10 - failureThreshold: 10 - startupProbe: - tcpSocket: - port: tcp-health-port - periodSeconds: 1 - failureThreshold: 10 ---- diff --git a/pkg/test/framework/components/echo/kube/testdata/healthcheck-rewrite.yaml b/pkg/test/framework/components/echo/kube/testdata/healthcheck-rewrite.yaml deleted file mode 100644 index db4432c70..000000000 --- a/pkg/test/framework/components/echo/kube/testdata/healthcheck-rewrite.yaml +++ /dev/null @@ -1,98 +0,0 @@ - -apiVersion: v1 -kind: Service -metadata: - name: healthcheck - labels: - app: healthcheck -spec: - ports: - - name: grpc - port: 7070 - targetPort: 7070 - - name: http-8080 - port: 8080 - targetPort: 8080 - selector: - app: healthcheck ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: healthcheck-v1 -spec: - replicas: 1 - selector: - matchLabels: - app: healthcheck - version: v1 - template: - metadata: - labels: - app: healthcheck - version: v1 - test.istio.io/class: standard - annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "15014" - sidecar.istio.io/rewriteAppHTTPProbers: "true" - spec: - imagePullSecrets: - - name: myregistrykey - containers: - - name: istio-proxy - image: auto - imagePullPolicy: Always - securityContext: # to allow core dumps - readOnlyRootFilesystem: false - - name: app - image: testing.hub/app:latest - imagePullPolicy: Always - securityContext: - runAsUser: 1338 - runAsGroup: 1338 - args: - - --metrics=15014 - - --cluster - - "cluster-0" - - --grpc - - "7070" - - --port - - "8080" - - --port - - "3333" - - --version - - "v1" - - --istio-version - - "" - - --crt=/cert.crt - - --key=/cert.key - ports: - - containerPort: 7070 - - containerPort: 8080 - - containerPort: 3333 - name: tcp-health-port - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - readinessProbe: - httpGet: - path: / - port: 8080 - initialDelaySeconds: 1 - periodSeconds: 2 - failureThreshold: 10 - livenessProbe: - tcpSocket: - port: tcp-health-port - initialDelaySeconds: 10 - periodSeconds: 10 - failureThreshold: 10 - startupProbe: - tcpSocket: - port: tcp-health-port - periodSeconds: 1 - failureThreshold: 10 ---- diff --git a/pkg/test/framework/components/echo/kube/testdata/multiple-istio-versions-no-proxy.yaml b/pkg/test/framework/components/echo/kube/testdata/multiple-istio-versions-no-proxy.yaml deleted file mode 100644 index dbd89ced6..000000000 --- a/pkg/test/framework/components/echo/kube/testdata/multiple-istio-versions-no-proxy.yaml +++ /dev/null @@ -1,174 +0,0 @@ - -apiVersion: v1 -kind: Service -metadata: - name: foo - labels: - app: foo -spec: - ports: - - name: grpc - port: 7070 - targetPort: 7070 - - name: http - port: 8090 - targetPort: 8090 - selector: - app: foo ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: foo-bar-rev-a -spec: - replicas: 1 - selector: - matchLabels: - app: foo - version: bar - template: - metadata: - labels: - app: foo - version: bar - test.istio.io/class: standard - istio.io/rev: rev-a - annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "15014" - spec: - imagePullSecrets: - - name: myregistrykey - containers: - - name: app - image: testing.hub/app:latest - imagePullPolicy: Always - securityContext: - runAsUser: 1338 - runAsGroup: 1338 - args: - - --metrics=15014 - - --cluster - - "cluster-0" - - --grpc - - "7070" - - --port - - "8090" - - --port - - "8080" - - --port - - "3333" - - --version - - "bar" - - --istio-version - - "1.8.2" - - --crt=/cert.crt - - --key=/cert.key - ports: - - containerPort: 7070 - - containerPort: 8090 - - containerPort: 8080 - - containerPort: 3333 - name: tcp-health-port - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - readinessProbe: - httpGet: - path: / - port: 8080 - initialDelaySeconds: 1 - periodSeconds: 2 - failureThreshold: 10 - livenessProbe: - tcpSocket: - port: tcp-health-port - initialDelaySeconds: 10 - periodSeconds: 10 - failureThreshold: 10 - startupProbe: - tcpSocket: - port: tcp-health-port - periodSeconds: 1 - failureThreshold: 10 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: foo-bar-rev-b -spec: - replicas: 1 - selector: - matchLabels: - app: foo - version: bar - template: - metadata: - labels: - app: foo - version: bar - test.istio.io/class: standard - istio.io/rev: rev-b - annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "15014" - spec: - imagePullSecrets: - - name: myregistrykey - containers: - - name: app - image: testing.hub/app:latest - imagePullPolicy: Always - securityContext: - runAsUser: 1338 - runAsGroup: 1338 - args: - - --metrics=15014 - - --cluster - - "cluster-0" - - --grpc - - "7070" - - --port - - "8090" - - --port - - "8080" - - --port - - "3333" - - --version - - "bar" - - --istio-version - - "1.9.0" - - --crt=/cert.crt - - --key=/cert.key - ports: - - containerPort: 7070 - - containerPort: 8090 - - containerPort: 8080 - - containerPort: 3333 - name: tcp-health-port - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - readinessProbe: - httpGet: - path: / - port: 8080 - initialDelaySeconds: 1 - periodSeconds: 2 - failureThreshold: 10 - livenessProbe: - tcpSocket: - port: tcp-health-port - initialDelaySeconds: 10 - periodSeconds: 10 - failureThreshold: 10 - startupProbe: - tcpSocket: - port: tcp-health-port - periodSeconds: 1 - failureThreshold: 10 ---- diff --git a/pkg/test/framework/components/echo/kube/testdata/multiple-istio-versions.yaml b/pkg/test/framework/components/echo/kube/testdata/multiple-istio-versions.yaml deleted file mode 100644 index ba80bf59b..000000000 --- a/pkg/test/framework/components/echo/kube/testdata/multiple-istio-versions.yaml +++ /dev/null @@ -1,184 +0,0 @@ - -apiVersion: v1 -kind: Service -metadata: - name: foo - labels: - app: foo -spec: - ports: - - name: grpc - port: 7070 - targetPort: 7070 - - name: http - port: 8090 - targetPort: 8090 - selector: - app: foo ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: foo-bar-rev-a -spec: - replicas: 1 - selector: - matchLabels: - app: foo - version: bar - template: - metadata: - labels: - app: foo - version: bar - test.istio.io/class: standard - istio.io/rev: rev-a - annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "15014" - spec: - imagePullSecrets: - - name: myregistrykey - containers: - - name: istio-proxy - image: auto - imagePullPolicy: Always - securityContext: # to allow core dumps - readOnlyRootFilesystem: false - - name: app - image: testing.hub/app:latest - imagePullPolicy: Always - securityContext: - runAsUser: 1338 - runAsGroup: 1338 - args: - - --metrics=15014 - - --cluster - - "cluster-0" - - --grpc - - "7070" - - --port - - "8090" - - --port - - "8080" - - --port - - "3333" - - --version - - "bar" - - --istio-version - - "1.9.0" - - --crt=/cert.crt - - --key=/cert.key - ports: - - containerPort: 7070 - - containerPort: 8090 - - containerPort: 8080 - - containerPort: 3333 - name: tcp-health-port - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - readinessProbe: - httpGet: - path: / - port: 8080 - initialDelaySeconds: 1 - periodSeconds: 2 - failureThreshold: 10 - livenessProbe: - tcpSocket: - port: tcp-health-port - initialDelaySeconds: 10 - periodSeconds: 10 - failureThreshold: 10 - startupProbe: - tcpSocket: - port: tcp-health-port - periodSeconds: 1 - failureThreshold: 10 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: foo-bar-rev-b -spec: - replicas: 1 - selector: - matchLabels: - app: foo - version: bar - template: - metadata: - labels: - app: foo - version: bar - test.istio.io/class: standard - istio.io/rev: rev-b - annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "15014" - spec: - imagePullSecrets: - - name: myregistrykey - containers: - - name: istio-proxy - image: auto - imagePullPolicy: Always - securityContext: # to allow core dumps - readOnlyRootFilesystem: false - - name: app - image: testing.hub/app:latest - imagePullPolicy: Always - securityContext: - runAsUser: 1338 - runAsGroup: 1338 - args: - - --metrics=15014 - - --cluster - - "cluster-0" - - --grpc - - "7070" - - --port - - "8090" - - --port - - "8080" - - --port - - "3333" - - --version - - "bar" - - --istio-version - - "1.10.0" - - --crt=/cert.crt - - --key=/cert.key - ports: - - containerPort: 7070 - - containerPort: 8090 - - containerPort: 8080 - - containerPort: 3333 - name: tcp-health-port - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - readinessProbe: - httpGet: - path: / - port: 8080 - initialDelaySeconds: 1 - periodSeconds: 2 - failureThreshold: 10 - livenessProbe: - tcpSocket: - port: tcp-health-port - initialDelaySeconds: 10 - periodSeconds: 10 - failureThreshold: 10 - startupProbe: - tcpSocket: - port: tcp-health-port - periodSeconds: 1 - failureThreshold: 10 ---- diff --git a/pkg/test/framework/components/echo/kube/testdata/multiversion.yaml b/pkg/test/framework/components/echo/kube/testdata/multiversion.yaml deleted file mode 100644 index 2a8ff7ddf..000000000 --- a/pkg/test/framework/components/echo/kube/testdata/multiversion.yaml +++ /dev/null @@ -1,187 +0,0 @@ - -apiVersion: v1 -kind: Service -metadata: - name: multiversion - labels: - app: multiversion -spec: - ports: - - name: http - port: 8090 - targetPort: 8090 - - name: tcp - port: 9000 - targetPort: 9000 - - name: grpc - port: 9090 - targetPort: 9090 - selector: - app: multiversion ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: multiversion-v-istio -spec: - replicas: 1 - selector: - matchLabels: - app: multiversion - version: v-istio - template: - metadata: - labels: - app: multiversion - version: v-istio - test.istio.io/class: naked - annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "15014" - spec: - imagePullSecrets: - - name: myregistrykey - containers: - - name: istio-proxy - image: auto - imagePullPolicy: Always - securityContext: # to allow core dumps - readOnlyRootFilesystem: false - - name: app - image: testing.hub/app:latest - imagePullPolicy: Always - securityContext: - runAsUser: 1338 - runAsGroup: 1338 - args: - - --metrics=15014 - - --cluster - - "cluster-0" - - --port - - "8090" - - --tcp - - "9000" - - --grpc - - "9090" - - --port - - "8080" - - --port - - "3333" - - --version - - "v-istio" - - --istio-version - - "" - - --crt=/cert.crt - - --key=/cert.key - ports: - - containerPort: 8090 - - containerPort: 9000 - - containerPort: 9090 - - containerPort: 8080 - - containerPort: 3333 - name: tcp-health-port - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - readinessProbe: - httpGet: - path: / - port: 8080 - initialDelaySeconds: 1 - periodSeconds: 2 - failureThreshold: 10 - livenessProbe: - tcpSocket: - port: tcp-health-port - initialDelaySeconds: 10 - periodSeconds: 10 - failureThreshold: 10 - startupProbe: - tcpSocket: - port: tcp-health-port - periodSeconds: 1 - failureThreshold: 10 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: multiversion-v-legacy -spec: - replicas: 1 - selector: - matchLabels: - app: multiversion - version: v-legacy - template: - metadata: - labels: - app: multiversion - version: v-legacy - test.istio.io/class: naked - annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "15014" - sidecar.istio.io/inject: "false" - spec: - imagePullSecrets: - - name: myregistrykey - containers: - - name: app - image: testing.hub/app:latest - imagePullPolicy: Always - securityContext: - runAsUser: 1338 - runAsGroup: 1338 - args: - - --metrics=15014 - - --cluster - - "cluster-0" - - --port - - "8090" - - --tcp - - "9000" - - --grpc - - "9090" - - --port - - "8080" - - --port - - "3333" - - --version - - "v-legacy" - - --istio-version - - "" - - --crt=/cert.crt - - --key=/cert.key - ports: - - containerPort: 8090 - - containerPort: 9000 - - containerPort: 9090 - - containerPort: 8080 - - containerPort: 3333 - name: tcp-health-port - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - readinessProbe: - httpGet: - path: / - port: 8080 - initialDelaySeconds: 1 - periodSeconds: 2 - failureThreshold: 10 - livenessProbe: - tcpSocket: - port: tcp-health-port - initialDelaySeconds: 10 - periodSeconds: 10 - failureThreshold: 10 - startupProbe: - tcpSocket: - port: tcp-health-port - periodSeconds: 1 - failureThreshold: 10 ---- diff --git a/pkg/test/framework/components/echo/kube/testdata/secret.yaml b/pkg/test/framework/components/echo/kube/testdata/secret.yaml deleted file mode 100644 index 3628b7dd5..000000000 --- a/pkg/test/framework/components/echo/kube/testdata/secret.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: myregistrykey \ No newline at end of file diff --git a/pkg/test/framework/components/echo/kube/testdata/two-workloads-one-nosidecar.yaml b/pkg/test/framework/components/echo/kube/testdata/two-workloads-one-nosidecar.yaml deleted file mode 100644 index 0a612d5ed..000000000 --- a/pkg/test/framework/components/echo/kube/testdata/two-workloads-one-nosidecar.yaml +++ /dev/null @@ -1,178 +0,0 @@ - -apiVersion: v1 -kind: Service -metadata: - name: foo - labels: - app: foo -spec: - ports: - - name: grpc - port: 7070 - targetPort: 7070 - - name: http - port: 8090 - targetPort: 8090 - selector: - app: foo ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: foo-v1 -spec: - replicas: 1 - selector: - matchLabels: - app: foo - version: v1 - template: - metadata: - labels: - app: foo - version: v1 - test.istio.io/class: naked - annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "15014" - spec: - imagePullSecrets: - - name: myregistrykey - containers: - - name: istio-proxy - image: auto - imagePullPolicy: Always - securityContext: # to allow core dumps - readOnlyRootFilesystem: false - - name: app - image: testing.hub/app:latest - imagePullPolicy: Always - securityContext: - runAsUser: 1338 - runAsGroup: 1338 - args: - - --metrics=15014 - - --cluster - - "cluster-0" - - --grpc - - "7070" - - --port - - "8090" - - --port - - "8080" - - --port - - "3333" - - --version - - "v1" - - --istio-version - - "" - - --crt=/cert.crt - - --key=/cert.key - ports: - - containerPort: 7070 - - containerPort: 8090 - - containerPort: 8080 - - containerPort: 3333 - name: tcp-health-port - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - readinessProbe: - httpGet: - path: / - port: 8080 - initialDelaySeconds: 1 - periodSeconds: 2 - failureThreshold: 10 - livenessProbe: - tcpSocket: - port: tcp-health-port - initialDelaySeconds: 10 - periodSeconds: 10 - failureThreshold: 10 - startupProbe: - tcpSocket: - port: tcp-health-port - periodSeconds: 1 - failureThreshold: 10 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: foo-nosidecar -spec: - replicas: 1 - selector: - matchLabels: - app: foo - version: nosidecar - template: - metadata: - labels: - app: foo - version: nosidecar - test.istio.io/class: naked - annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "15014" - sidecar.istio.io/inject: "false" - spec: - imagePullSecrets: - - name: myregistrykey - containers: - - name: app - image: testing.hub/app:latest - imagePullPolicy: Always - securityContext: - runAsUser: 1338 - runAsGroup: 1338 - args: - - --metrics=15014 - - --cluster - - "cluster-0" - - --grpc - - "7070" - - --port - - "8090" - - --port - - "8080" - - --port - - "3333" - - --version - - "nosidecar" - - --istio-version - - "" - - --crt=/cert.crt - - --key=/cert.key - ports: - - containerPort: 7070 - - containerPort: 8090 - - containerPort: 8080 - - containerPort: 3333 - name: tcp-health-port - env: - - name: INSTANCE_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - readinessProbe: - httpGet: - path: / - port: 8080 - initialDelaySeconds: 1 - periodSeconds: 2 - failureThreshold: 10 - livenessProbe: - tcpSocket: - port: tcp-health-port - initialDelaySeconds: 10 - periodSeconds: 10 - failureThreshold: 10 - startupProbe: - tcpSocket: - port: tcp-health-port - periodSeconds: 1 - failureThreshold: 10 ---- diff --git a/pkg/test/framework/components/echo/kube/util.go b/pkg/test/framework/components/echo/kube/util.go deleted file mode 100644 index ae9d525f2..000000000 --- a/pkg/test/framework/components/echo/kube/util.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "fmt" - "strings" -) - -import ( - kubeCore "k8s.io/api/core/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/constants" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" -) - -type podSelector struct { - Label string - Value string -} - -func (s podSelector) String() string { - return s.Label + "=" + s.Value -} - -func (s podSelector) MatchesPod(pod *kubeCore.Pod) bool { - return pod.ObjectMeta.Labels[s.Label] == s.Value -} - -func newPodSelector(cfg echo.Config) podSelector { - label := "app" - if cfg.DeployAsVM { - label = constants.TestVMLabel - } - return podSelector{ - Label: label, - Value: cfg.Service, - } -} - -func serviceAccount(cfg echo.Config) string { - if cfg.ServiceAccount { - return cfg.Service - } - if cfg.DeployAsVM { - return "default" - } - return "" -} - -// workloadHasSidecar returns true if the input endpoint is deployed with sidecar injected based on the config. -func workloadHasSidecar(cfg echo.Config, podName string) bool { - // Match workload first. - for _, w := range cfg.Subsets { - if strings.HasPrefix(podName, fmt.Sprintf("%v-%v", cfg.Service, w.Version)) { - return w.Annotations.GetBool(echo.SidecarInject) && - !strings.HasPrefix(w.Annotations.Get(echo.SidecarInjectTemplates), "grpc-") - } - } - return true -} diff --git a/pkg/test/framework/components/echo/kube/workload.go b/pkg/test/framework/components/echo/kube/workload.go deleted file mode 100644 index c3ce43c0f..000000000 --- a/pkg/test/framework/components/echo/kube/workload.go +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "context" - "fmt" - "sync" - "time" -) - -import ( - "github.com/hashicorp/go-multierror" - kubeCore "k8s.io/api/core/v1" -) - -import ( - istioKube "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test" - echoClient "github.com/apache/dubbo-go-pixiu/pkg/test/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/proto" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/errors" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -const ( - appContainerName = "app" -) - -var _ echo.Workload = &workload{} - -type workloadConfig struct { - pod kubeCore.Pod - hasSidecar bool - grpcPort uint16 - cluster cluster.Cluster - tls *common.TLSSettings -} - -type workload struct { - client *echoClient.Client - - workloadConfig - forwarder istioKube.PortForwarder - sidecar *sidecar - ctx resource.Context - mutex sync.Mutex - connectErr error -} - -func newWorkload(cfg workloadConfig, ctx resource.Context) (*workload, error) { - w := &workload{ - workloadConfig: cfg, - ctx: ctx, - } - - // If the pod is ready, connect. - if err := w.Update(cfg.pod); err != nil { - return nil, err - } - - return w, nil -} - -func (w *workload) IsReady() bool { - w.mutex.Lock() - ready := w.isConnected() - w.mutex.Unlock() - return ready -} - -func (w *workload) Client() (c *echoClient.Client, err error) { - w.mutex.Lock() - c = w.client - if c == nil { - err = fmt.Errorf("attempt to use disconnected client for echo %s/%s", - w.pod.Namespace, w.pod.Name) - } - w.mutex.Unlock() - return -} - -func (w *workload) Update(pod kubeCore.Pod) error { - w.mutex.Lock() - defer w.mutex.Unlock() - - if isPodReady(pod) && !w.isConnected() { - if err := w.connect(pod); err != nil { - w.connectErr = err - return err - } - } else if !isPodReady(pod) && w.isConnected() { - w.pod = pod - return w.disconnect() - } - - // Update the pod. - w.pod = pod - return nil -} - -func (w *workload) Close() (err error) { - w.mutex.Lock() - defer w.mutex.Unlock() - - if w.isConnected() { - return w.disconnect() - } - return nil -} - -func (w *workload) PodName() string { - w.mutex.Lock() - n := w.pod.Name - w.mutex.Unlock() - return n -} - -func (w *workload) Address() string { - w.mutex.Lock() - ip := w.pod.Status.PodIP - w.mutex.Unlock() - return ip -} - -func (w *workload) ForwardEcho(ctx context.Context, request *proto.ForwardEchoRequest) (echoClient.Responses, error) { - w.mutex.Lock() - c := w.client - if c == nil { - return nil, fmt.Errorf("failed forwarding echo for disconnected pod %s/%s", - w.pod.Namespace, w.pod.Name) - } - w.mutex.Unlock() - - return c.ForwardEcho(ctx, request) -} - -func (w *workload) Sidecar() echo.Sidecar { - w.mutex.Lock() - s := w.sidecar - w.mutex.Unlock() - return s -} - -func (w *workload) Cluster() cluster.Cluster { - return w.cluster -} - -func (w *workload) Logs() (string, error) { - return w.cluster.PodLogs(context.TODO(), w.pod.Name, w.pod.Namespace, appContainerName, false) -} - -func (w *workload) LogsOrFail(t test.Failer) string { - t.Helper() - logs, err := w.Logs() - if err != nil { - t.Fatal(err) - } - return logs -} - -func isPodReady(pod kubeCore.Pod) bool { - return istioKube.CheckPodReady(&pod) == nil -} - -func (w *workload) isConnected() bool { - return w.forwarder != nil -} - -func (w *workload) connect(pod kubeCore.Pod) (err error) { - defer func() { - if err != nil { - _ = w.disconnect() - } - }() - - // Create a forwarder to the command port of the app. - if err = retry.UntilSuccess(func() error { - w.forwarder, err = w.cluster.NewPortForwarder(pod.Name, pod.Namespace, "", 0, int(w.grpcPort)) - if err != nil { - return fmt.Errorf("failed creating new port forwarder for pod %s/%s: %v", - pod.Namespace, pod.Name, err) - } - if err = w.forwarder.Start(); err != nil { - return fmt.Errorf("failed starting port forwarder for pod %s/%s: %v", - pod.Namespace, pod.Name, err) - } - return nil - }, retry.BackoffDelay(100*time.Millisecond), retry.Timeout(10*time.Second)); err != nil { - return err - } - - // Create a gRPC client to this workload. - w.client, err = echoClient.New(w.forwarder.Address(), w.tls) - if err != nil { - return fmt.Errorf("failed connecting to grpc client to pod %s/%s : %v", - pod.Namespace, pod.Name, err) - } - - if w.hasSidecar { - w.sidecar = newSidecar(pod, w.cluster) - } - - return nil -} - -func (w *workload) disconnect() (err error) { - if w.client != nil { - err = multierror.Append(err, w.client.Close()).ErrorOrNil() - w.client = nil - } - if w.forwarder != nil { - w.forwarder.Close() - w.forwarder = nil - } - if w.ctx.Settings().FailOnDeprecation && w.sidecar != nil { - err = multierror.Append(err, w.checkDeprecation()).ErrorOrNil() - w.sidecar = nil - } - return err -} - -func (w *workload) checkDeprecation() error { - logs, err := w.sidecar.Logs() - if err != nil { - return fmt.Errorf("could not get sidecar logs to inspect for deprecation messages: %v", err) - } - - info := fmt.Sprintf("pod: %s/%s", w.pod.Namespace, w.pod.Name) - return errors.FindDeprecatedMessagesInEnvoyLog(logs, info) -} diff --git a/pkg/test/framework/components/echo/kube/workload_manager.go b/pkg/test/framework/components/echo/kube/workload_manager.go deleted file mode 100644 index 931f55b5c..000000000 --- a/pkg/test/framework/components/echo/kube/workload_manager.go +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "errors" - "fmt" - "io" - "sync" -) - -import ( - "github.com/hashicorp/go-multierror" - kubeCore "k8s.io/api/core/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - echoCommon "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -var ( - _ echo.Instance = &instance{} - _ io.Closer = &instance{} -) - -type workloadHandler interface { - WorkloadReady(w *workload) - WorkloadNotReady(w *workload) -} - -type workloadManager struct { - workloads []*workload - mutex sync.Mutex - podController *podController - cfg echo.Config - ctx resource.Context - grpcPort uint16 - tls *echoCommon.TLSSettings - closing bool - stopCh chan struct{} - handler workloadHandler -} - -func newWorkloadManager(ctx resource.Context, cfg echo.Config, handler workloadHandler) (*workloadManager, error) { - // Get the gRPC port and TLS settings. - var grpcInstancePort int - var tls *echoCommon.TLSSettings - if cfg.IsProxylessGRPC() { - grpcInstancePort = grpcMagicPort - } - if grpcInstancePort == 0 { - if grpcPort, found := cfg.Ports.ForProtocol(protocol.GRPC); found { - if grpcPort.TLS { - tls = cfg.TLSSettings - } - grpcInstancePort = grpcPort.WorkloadPort - } - } - if grpcInstancePort == 0 { - return nil, errors.New("unable fo find GRPC command port") - } - - m := &workloadManager{ - cfg: cfg, - ctx: ctx, - handler: handler, - grpcPort: uint16(grpcInstancePort), - tls: tls, - stopCh: make(chan struct{}, 1), - } - m.podController = newPodController(cfg, podHandlers{ - added: m.onPodAddOrUpdate, - updated: m.onPodAddOrUpdate, - deleted: m.onPodDeleted, - }) - - return m, nil -} - -// WaitForReadyWorkloads waits until all known workloads are ready. -func (m *workloadManager) WaitForReadyWorkloads() (out echo.Workloads, err error) { - err = retry.UntilSuccess(func() error { - m.mutex.Lock() - out, err = m.readyWorkloads() - if err == nil && len(out) != len(m.workloads) { - err = fmt.Errorf("failed waiting for workloads for echo %s/%s to be ready", - m.cfg.Namespace.Name(), - m.cfg.Service) - } - m.mutex.Unlock() - return err - }, retry.Timeout(m.cfg.ReadinessTimeout), startDelay) - return -} - -func (m *workloadManager) readyWorkloads() (echo.Workloads, error) { - out := make(echo.Workloads, 0, len(m.workloads)) - var connErrs error - for _, w := range m.workloads { - if w.IsReady() { - out = append(out, w) - } else if w.connectErr != nil { - connErrs = multierror.Append(connErrs, w.connectErr) - } - } - if len(out) == 0 { - err := fmt.Errorf("no workloads ready for echo %s/%s", m.cfg.Namespace.Name(), m.cfg.Service) - if connErrs != nil { - err = fmt.Errorf("%v: failed connecting: %v", err, connErrs) - } - return nil, err - } - return out, nil -} - -// ReadyWorkloads returns all ready workloads in ascending order by pod name. -func (m *workloadManager) ReadyWorkloads() (echo.Workloads, error) { - m.mutex.Lock() - out, err := m.readyWorkloads() - m.mutex.Unlock() - return out, err -} - -func (m *workloadManager) Start() error { - // Run the pod controller. - go m.podController.Run(m.stopCh) - - // Wait for the cache to sync. - if !m.podController.WaitForSync(m.stopCh) { - return fmt.Errorf( - "failed syncing cache for echo %s/%s: controller stopping", - m.cfg.Namespace.Name(), - m.cfg.Service) - } - - // Wait until all pods are ready. - _, err := m.WaitForReadyWorkloads() - return err -} - -func (m *workloadManager) onPodAddOrUpdate(pod *kubeCore.Pod) error { - m.mutex.Lock() - - // After the method returns, notify the handler the ready state of the workload changed. - var workloadReady *workload - var workloadNotReady *workload - defer func() { - m.mutex.Unlock() - - if workloadReady != nil { - m.handler.WorkloadReady(workloadReady) - } - if workloadNotReady != nil { - m.handler.WorkloadNotReady(workloadNotReady) - } - }() - - // First, check to see if we already have a workload for the pod. If we do, just update it. - for _, w := range m.workloads { - if w.pod.Name == pod.Name { - prevReady := w.IsReady() - if err := w.Update(*pod); err != nil { - return err - } - - // Defer notifying the handler until after we release the mutex. - if !prevReady && w.IsReady() { - workloadReady = w - } else if prevReady && !w.IsReady() { - workloadNotReady = w - } - return nil - } - } - - // Add the pod to the end of the workload list. - newWorkload, err := newWorkload(workloadConfig{ - pod: *pod, - hasSidecar: workloadHasSidecar(m.cfg, pod.Name), - cluster: m.cfg.Cluster, - grpcPort: m.grpcPort, - tls: m.tls, - }, m.ctx) - if err != nil { - return err - } - m.workloads = append(m.workloads, newWorkload) - - if newWorkload.IsReady() { - workloadReady = newWorkload - } - - return nil -} - -func (m *workloadManager) onPodDeleted(pod *kubeCore.Pod) (err error) { - m.mutex.Lock() - - // After the method returns, notify the handler the ready state of the workload changed. - var workloadNotReady *workload - defer func() { - m.mutex.Unlock() - - if workloadNotReady != nil { - m.handler.WorkloadNotReady(workloadNotReady) - } - }() - - newWorkloads := make([]*workload, 0, len(m.workloads)) - for _, w := range m.workloads { - if w.pod.Name == pod.Name { - // Close the workload and remove it from the list. If an - // error occurs, just continue. - if w.IsReady() { - workloadNotReady = w - } - err = w.Close() - } else { - // Just add all other workloads. - newWorkloads = append(newWorkloads, w) - } - } - - m.workloads = newWorkloads - return err -} - -func (m *workloadManager) Close() (err error) { - m.mutex.Lock() - - // Indicate we're closing. - m.closing = true - - // Stop the controller and queue. - close(m.stopCh) - - // Clear out the workloads array - workloads := m.workloads - m.workloads = nil - - m.mutex.Unlock() - - // Close the workloads. - for _, w := range workloads { - err = multierror.Append(err, w.Close()).ErrorOrNil() - } - return -} diff --git a/pkg/test/framework/components/echo/match/matcher.go b/pkg/test/framework/components/echo/match/matcher.go deleted file mode 100644 index fb2a46b34..000000000 --- a/pkg/test/framework/components/echo/match/matcher.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package match - -import ( - "errors" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" -) - -// Matcher is used to filter matching instances -type Matcher func(echo.Instance) bool - -// GetMatches returns the subset of echo.Instances that match this Matcher. -func (m Matcher) GetMatches(i echo.Instances) echo.Instances { - out := make(echo.Instances, 0) - for _, i := range i { - if m(i) { - out = append(out, i) - } - } - return out -} - -// First finds the first Instance that matches the Matcher. -func (m Matcher) First(i echo.Instances) (echo.Instance, error) { - for _, i := range i { - if m(i) { - return i, nil - } - } - - return nil, errors.New("found 0 matching echo instances") -} - -// FirstOrFail calls First and then fails the test if an error occurs. -func (m Matcher) FirstOrFail(t test.Failer, i echo.Instances) echo.Instance { - res, err := m.First(i) - if err != nil { - t.Fatal(err) - } - return res -} - -// Any indicates whether any echo.Instance matches this matcher. -func (m Matcher) Any(i echo.Instances) bool { - for _, i := range i { - if m(i) { - return true - } - } - return false -} diff --git a/pkg/test/framework/components/echo/match/matchers.go b/pkg/test/framework/components/echo/match/matchers.go deleted file mode 100644 index 4ad4bb2bb..000000000 --- a/pkg/test/framework/components/echo/match/matchers.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package match - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/namespace" -) - -// Any doesn't filter out any echos. -var Any Matcher = func(_ echo.Instance) bool { - return true -} - -// And is an aggregate Matcher that requires all matches return true. -func And(ms ...Matcher) Matcher { - return func(i echo.Instance) bool { - for _, m := range ms { - if !m(i) { - return false - } - } - return true - } -} - -// Or is an aggregate Matcher that requires at least one matches return true. -func Or(ms ...Matcher) Matcher { - return func(i echo.Instance) bool { - for _, m := range ms { - if m(i) { - return true - } - } - return false - } -} - -// Not negates the given matcher. Example: -// -// Not(Naked()) -func Not(m Matcher) Matcher { - return func(i echo.Instance) bool { - return !m(i) - } -} - -// ServiceName matches instances with the given namespace and service name. -func ServiceName(n echo.NamespacedName) Matcher { - return func(i echo.Instance) bool { - return n == i.NamespacedName() - } -} - -// AnyServiceName matches instances if they have the same Service and Namespace as any of the provided instances. -func AnyServiceName(expected echo.NamespacedNames) Matcher { - return func(instance echo.Instance) bool { - serviceName := instance.NamespacedName() - for _, expectedName := range expected { - if serviceName == expectedName { - return true - } - } - return false - } -} - -// Namespace matches instances within the given namespace name. -func Namespace(n namespace.Instance) Matcher { - return NamespaceName(n.Name()) -} - -// NamespaceName matches instances within the given namespace name. -func NamespaceName(ns string) Matcher { - return func(i echo.Instance) bool { - return i.Config().Namespace.Name() == ns - } -} - -// Cluster matches instances deployed on the given cluster. -func Cluster(c cluster.Cluster) Matcher { - return func(i echo.Instance) bool { - return c.Name() == i.Config().Cluster.Name() - } -} - -// Network matches instances deployed in the given network. -func Network(n string) Matcher { - return func(i echo.Instance) bool { - return i.Config().Cluster.NetworkName() == n - } -} - -// VM matches instances with DeployAsVM -var VM Matcher = func(i echo.Instance) bool { - return i.Config().IsVM() -} - -// NotVM is matches against instances that are NOT VMs. -var NotVM = Not(VM) - -// External matches instances that have a custom DefaultHostHeader defined -var External Matcher = func(i echo.Instance) bool { - return i.Config().IsExternal() -} - -// NotExternal is equivalent to Not(External) -var NotExternal = Not(External) - -// Naked matches instances that are Pods with a SidecarInject annotation equal to false. -var Naked Matcher = func(i echo.Instance) bool { - return i.Config().IsNaked() -} - -// NotNaked is equivalent to Not(Naked) -var NotNaked = Not(Naked) - -// Headless matches instances that are backed by headless services. -var Headless Matcher = func(i echo.Instance) bool { - return i.Config().Headless -} - -// NotHeadless is equivalent to Not(Headless) -var NotHeadless = Not(Headless) - -// ProxylessGRPC matches instances that are Pods with a SidecarInjectTemplate annotation equal to grpc. -var ProxylessGRPC Matcher = func(i echo.Instance) bool { - return i.Config().IsProxylessGRPC() -} - -// NotProxylessGRPC is equivalent to Not(ProxylessGRPC) -var NotProxylessGRPC = Not(ProxylessGRPC) - -var TProxy Matcher = func(i echo.Instance) bool { - return i.Config().IsTProxy() -} - -var NotTProxy = Not(TProxy) - -// RegularPod matches echos that don't meet any of the following criteria: -// - VM -// - Naked -// - Headless -// - TProxy -// - Multi-Subset -var RegularPod Matcher = func(instance echo.Instance) bool { - return instance.Config().IsRegularPod() -} - -var NotRegularPod = Not(RegularPod) diff --git a/pkg/test/framework/components/echo/match/matchers_test.go b/pkg/test/framework/components/echo/match/matchers_test.go deleted file mode 100644 index 86009ff26..000000000 --- a/pkg/test/framework/components/echo/match/matchers_test.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package match_test - -import ( - "strconv" - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/match" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/namespace" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -var ( - // 2 clusters on 2 networks - cls1 = &cluster.FakeCluster{Topology: cluster.Topology{ClusterName: "cls1", Network: "n1", Index: 0, ClusterKind: cluster.Fake}} - - // simple pod - a1 = &fakeInstance{Cluster: cls1, Namespace: namespace.Static("echo"), Service: "a"} - // simple pod with different svc - b1 = &fakeInstance{Cluster: cls1, Namespace: namespace.Static("echo"), Service: "b"} - // virtual machine - vm1 = &fakeInstance{Cluster: cls1, Namespace: namespace.Static("echo"), Service: "vm", DeployAsVM: true} - // headless - headless1 = &fakeInstance{Cluster: cls1, Namespace: namespace.Static("echo"), Service: "headless", Headless: true} - // naked pod (uninjected) - naked1 = &fakeInstance{Cluster: cls1, Namespace: namespace.Static("echo"), Service: "naked", Subsets: []echo.SubsetConfig{{ - Annotations: echo.NewAnnotations().SetBool(echo.SidecarInject, false), - }}} - // external svc - external1 = &fakeInstance{ - Cluster: cls1, Namespace: namespace.Static("echo"), Service: "external", DefaultHostHeader: "external.com", Subsets: []echo.SubsetConfig{{ - Annotations: map[echo.Annotation]*echo.AnnotationValue{echo.SidecarInject: {Value: strconv.FormatBool(false)}}, - }}, - } -) - -func TestRegularPod(t *testing.T) { - tests := []struct { - app echo.Instance - expect bool - }{ - {app: a1, expect: true}, - {app: b1, expect: true}, - {app: vm1, expect: false}, - {app: naked1, expect: false}, - {app: external1, expect: false}, - {app: headless1, expect: false}, - } - for _, tt := range tests { - t.Run(tt.app.Config().Service, func(t *testing.T) { - if got := match.RegularPod(tt.app); got != tt.expect { - t.Errorf("got %v expected %v", got, tt.expect) - } - }) - } -} - -func TestNaked(t *testing.T) { - tests := []struct { - app echo.Instance - expect bool - }{ - {app: a1, expect: false}, - {app: headless1, expect: false}, - {app: naked1, expect: true}, - {app: external1, expect: true}, - } - for _, tt := range tests { - t.Run(tt.app.Config().Service, func(t *testing.T) { - if got := tt.app.Config().IsNaked(); got != tt.expect { - t.Errorf("got %v expected %v", got, tt.expect) - } - }) - } -} - -var _ echo.Instance = fakeInstance{} - -// fakeInstance wraps echo.Config for test-framework internals tests where we don't actually make calls -type fakeInstance echo.Config - -func (f fakeInstance) WithWorkloads(wl ...echo.Workload) echo.Instance { - // TODO implement me - panic("implement me") -} - -func (f fakeInstance) Instances() echo.Instances { - return echo.Instances{f} -} - -func (f fakeInstance) ID() resource.ID { - panic("implement me") -} - -func (f fakeInstance) NamespacedName() echo.NamespacedName { - return f.Config().NamespacedName() -} - -func (f fakeInstance) PortForName(name string) echo.Port { - return f.Config().Ports.MustForName(name) -} - -func (f fakeInstance) Config() echo.Config { - cfg := echo.Config(f) - _ = cfg.FillDefaults(nil) - return cfg -} - -func (f fakeInstance) Address() string { - panic("implement me") -} - -func (f fakeInstance) Addresses() []string { - panic("implement me") -} - -func (f fakeInstance) Workloads() (echo.Workloads, error) { - panic("implement me") -} - -func (f fakeInstance) WorkloadsOrFail(test.Failer) echo.Workloads { - panic("implement me") -} - -func (f fakeInstance) MustWorkloads() echo.Workloads { - panic("implement me") -} - -func (f fakeInstance) Clusters() cluster.Clusters { - panic("implement me") -} - -func (f fakeInstance) Call(echo.CallOptions) (echo.CallResult, error) { - panic("implement me") -} - -func (f fakeInstance) CallOrFail(test.Failer, echo.CallOptions) echo.CallResult { - panic("implement me") -} - -func (f fakeInstance) Restart() error { - panic("implement me") -} diff --git a/pkg/test/framework/components/echo/namespacedname.go b/pkg/test/framework/components/echo/namespacedname.go deleted file mode 100644 index af81fcb00..000000000 --- a/pkg/test/framework/components/echo/namespacedname.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -import ( - "sort" - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/namespace" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// NamespacedName represents the full name of a service. -type NamespacedName struct { - // Namespace of the echo Instance. If not provided, a default namespace "apps" is used. - Namespace namespace.Instance - - // Name of the service within the Namespace. - Name string -} - -// String returns the Istio-formatted service name in the form of /. -func (n NamespacedName) String() string { - ns := "" - if n.Namespace != nil { - ns = n.Namespace.Name() - } - return ns + "/" + n.Name -} - -// PrefixString returns a string in the form of .. This is helpful for -// providing more stable test names. -func (n NamespacedName) PrefixString() string { - if n.Namespace == nil { - return n.Name - } - return n.Name + "." + n.Namespace.Prefix() -} - -var _ sort.Interface = NamespacedNames{} - -// NamespacedNames is a list of NamespacedName. -type NamespacedNames []NamespacedName - -func (n NamespacedNames) Less(i, j int) bool { - return strings.Compare(n[i].PrefixString(), n[j].PrefixString()) < 0 -} - -func (n NamespacedNames) Swap(i, j int) { - n[i], n[j] = n[j], n[i] -} - -func (n NamespacedNames) Len() int { - return len(n) -} - -// Names returns the list of service names without any namespace appended. -func (n NamespacedNames) Names() []string { - return n.uniqueSortedNames(func(nn NamespacedName) string { - return nn.Name - }) -} - -func (n NamespacedNames) NamesWithNamespacePrefix() []string { - return n.uniqueSortedNames(func(nn NamespacedName) string { - if nn.Namespace == nil { - return nn.Name - } - return nn.Name + "." + nn.Namespace.Prefix() - }) -} - -func (n NamespacedNames) uniqueSortedNames(getName func(NamespacedName) string) []string { - set := sets.NewWithLength(n.Len()) - out := make([]string, 0, n.Len()) - for _, nn := range n { - name := getName(nn) - if !set.Contains(name) { - set.Insert(name) - out = append(out, name) - } - } - sort.Strings(out) - return out -} diff --git a/pkg/test/framework/components/echo/port.go b/pkg/test/framework/components/echo/port.go deleted file mode 100644 index ce0622c9c..000000000 --- a/pkg/test/framework/components/echo/port.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -import ( - "fmt" - "reflect" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common/scheme" -) - -// NoServicePort defines the ServicePort value for a Port that is a workload-only port. -const NoServicePort = -1 - -// Port exposed by an Echo Instance -type Port struct { - // Name of this port - Name string - - // Protocol to be used for the port. - Protocol protocol.Instance - - // ServicePort number where the service can be reached. Does not necessarily - // map to the corresponding port numbers for the instances behind the - // service. If zero (default), a service port will be automatically generated for this port. - // If set to NoServicePort, this port will be assumed to be a workload-only port. - ServicePort int - - // WorkloadPort number where the workload is listening for connections. - // This need not be the same as the ServicePort where the service is accessed. - WorkloadPort int - - // TLS determines whether the connection will be plain text or TLS. By default this is false (plain text). - TLS bool - - // ServerFirst determines whether the port will use server first communication, meaning the client will not send the first byte. - ServerFirst bool - - // InstanceIP determines if echo will listen on the instance IP; otherwise, it will listen on wildcard - InstanceIP bool - - // LocalhostIP determines if echo will listen on the localhost IP; otherwise, it will listen on wildcard - LocalhostIP bool -} - -// IsWorkloadOnly returns true if there is no service port specified for this Port. -func (p Port) IsWorkloadOnly() bool { - return p.ServicePort == NoServicePort -} - -// Scheme infers the scheme to be used based on the Protocol. -func (p Port) Scheme() (scheme.Instance, error) { - switch p.Protocol { - case protocol.GRPC, protocol.GRPCWeb, protocol.HTTP2: - return scheme.GRPC, nil - case protocol.HTTP: - return scheme.HTTP, nil - case protocol.HTTPS: - return scheme.HTTPS, nil - case protocol.TCP: - return scheme.TCP, nil - default: - return "", fmt.Errorf("failed creating call for port %s: unsupported protocol %s", - p.Name, p.Protocol) - } -} - -type Ports []Port - -func (ps Ports) Contains(p Port) bool { - for _, port := range ps { - if reflect.DeepEqual(port, p) { - return true - } - } - return false -} - -// ForName returns the first port found with the given name. -func (ps Ports) ForName(name string) (Port, bool) { - for _, port := range ps { - if name == port.Name { - return port, true - } - } - return Port{}, false -} - -// MustForName calls ForName and panics if not found. -func (ps Ports) MustForName(name string) Port { - p, found := ps.ForName(name) - if !found { - panic("port does not exist for name " + name) - } - return p -} - -// ForProtocol returns the first port found with the given protocol. -func (ps Ports) ForProtocol(protocol protocol.Instance) (Port, bool) { - for _, p := range ps { - if p.Protocol == protocol { - return p, true - } - } - return Port{}, false -} - -// ForServicePort returns the first port found with the given service port. -func (ps Ports) ForServicePort(port int) (Port, bool) { - for _, p := range ps { - if p.ServicePort == port { - return p, true - } - } - return Port{}, false -} - -// MustForProtocol calls ForProtocol and panics if not found. -func (ps Ports) MustForProtocol(protocol protocol.Instance) Port { - p, found := ps.ForProtocol(protocol) - if !found { - panic("port does not exist for protocol " + protocol) - } - return p -} - -// GetServicePorts returns the subset of ports that contain a service port. -func (ps Ports) GetServicePorts() Ports { - out := make(Ports, 0, len(ps)) - for _, p := range ps { - if !p.IsWorkloadOnly() { - out = append(out, p) - } - } - return out -} - -// GetWorkloadOnlyPorts returns the subset of ports that do not contain a service port. -func (ps Ports) GetWorkloadOnlyPorts() Ports { - out := make(Ports, 0, len(ps)) - for _, p := range ps { - if p.IsWorkloadOnly() { - out = append(out, p) - } - } - return out -} diff --git a/pkg/test/framework/components/echo/portgen.go b/pkg/test/framework/components/echo/portgen.go deleted file mode 100644 index d76e6af60..000000000 --- a/pkg/test/framework/components/echo/portgen.go +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -import ( - "math" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" -) - -const ( - httpBase = 80 - httpsBase = 443 - grpcBase = 7070 - tcpBase = 9090 -) - -// portGenerators creates a set of generators for service and instance ports. -type portGenerators struct { - Service *portGenerator - Instance *portGenerator -} - -// newPortGenerators creates a new set of port generators. -func newPortGenerators() *portGenerators { - return &portGenerators{ - Service: newPortGenerator(), - Instance: newPortGenerator(), - } -} - -// portGenerator is a utility that generates reasonable default port values -// for a given protocol. -type portGenerator struct { - next map[protocol.Instance]int - used map[int]struct{} -} - -func newPortGenerator() *portGenerator { - return &portGenerator{ - next: map[protocol.Instance]int{ - protocol.HTTP: httpBase, - protocol.HTTPS: httpsBase, - protocol.TLS: httpsBase, - protocol.TCP: tcpBase, - protocol.GRPCWeb: grpcBase, - protocol.GRPC: grpcBase, - protocol.Mongo: tcpBase, - protocol.MySQL: tcpBase, - protocol.Redis: tcpBase, - protocol.UDP: tcpBase, - }, - used: make(map[int]struct{}), - } -} - -// SetUsed marks the given port as used, so that it will not be assigned by the -// generator. -func (g *portGenerator) SetUsed(port int) *portGenerator { - g.used[port] = struct{}{} - return g -} - -// IsUsed indicates if the given port has already been used. -func (g *portGenerator) IsUsed(port int) bool { - _, ok := g.used[port] - return ok -} - -// Next assigns the next port for the given protocol. -func (g *portGenerator) Next(protocol protocol.Instance) int { - for { - v := g.next[protocol] - - if v == 0 { - panic("echo port generator: unsupported protocol " + protocol) - } - - if v == math.MaxInt16 { - panic("echo port generator: ran out of ports") - } - - g.next[protocol] = v + 1 - - if g.IsUsed(v) { - continue - } - - // Mark this port as used. - g.SetUsed(v) - return v - } -} diff --git a/pkg/test/framework/components/echo/services.go b/pkg/test/framework/components/echo/services.go deleted file mode 100644 index 6408db3f2..000000000 --- a/pkg/test/framework/components/echo/services.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -import ( - "sort" - "strings" -) - -// Services is a set of Instances that share the same FQDN. While an Instance contains -// multiple deployments (a single service in a single cluster), Instances contains multiple -// deployments that may contain multiple Services. -type Services []Instances - -// GetByService finds the first Instances with the given Service name. It is possible to have multiple deployments -// with the same service name but different namespaces (and therefore different FQDNs). Use caution when relying on -// Service. -func (d Services) GetByService(service string) Target { - for _, target := range d { - if target.Config().Service == service { - return target - } - } - return nil -} - -// FQDNs gives the fully-qualified-domain-names each deployment in order. -func (d Services) FQDNs() []string { - var out []string - for _, target := range d { - out = append(out, target.Config().ClusterLocalFQDN()) - } - return out -} - -func (d Services) Instances() Instances { - var out Instances - for _, target := range d { - out = append(out, target.Instances()...) - } - return out -} - -func (d Services) Callers() Callers { - var out Callers - for _, s := range d { - out = append(out, s[0]) - } - return out -} - -func (d Services) MatchFQDNs(fqdns ...string) Services { - match := map[string]bool{} - for _, fqdn := range fqdns { - match[fqdn] = true - } - var out Services - for _, target := range d { - if match[target.Config().ClusterLocalFQDN()] { - out = append(out, target) - } - } - return out -} - -// Services must be sorted to make sure tests have consistent ordering -var _ sort.Interface = Services{} - -// Len returns the number of deployments -func (d Services) Len() int { - return len(d) -} - -// Less returns true if the element at i should appear before the element at j in a sorted Services -func (d Services) Less(i, j int) bool { - return strings.Compare(d[i].Config().ClusterLocalFQDN(), d[j].Config().ClusterLocalFQDN()) < 0 -} - -// Swap switches the positions of elements at i and j (used for sorting). -func (d Services) Swap(i, j int) { - d[i], d[j] = d[j], d[i] -} - -// Copy this services array. -func (d Services) Copy() Services { - return append(Services{}, d...) -} - -// Append returns a new Services array with the given values appended. -func (d Services) Append(others ...Services) Services { - out := d.Copy() - for _, o := range others { - out = append(out, o...) - } - sort.Stable(out) - return out -} - -func (d Services) NamespacedNames() NamespacedNames { - out := make(NamespacedNames, 0, d.Len()) - for _, svc := range d { - out = append(out, svc.NamespacedName()) - } - - sort.Stable(out) - return out -} diff --git a/pkg/test/framework/components/echo/sidecar.go b/pkg/test/framework/components/echo/sidecar.go deleted file mode 100644 index 56c1a101b..000000000 --- a/pkg/test/framework/components/echo/sidecar.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -import ( - envoyAdmin "github.com/envoyproxy/go-control-plane/envoy/admin/v3" - dto "github.com/prometheus/client_model/go" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -// Sidecar provides an interface to execute queries against a single Envoy sidecar. -type Sidecar interface { - // Info about the Envoy instance. - Info() (*envoyAdmin.ServerInfo, error) - InfoOrFail(t test.Failer) *envoyAdmin.ServerInfo - - // Config of the Envoy instance. - Config() (*envoyAdmin.ConfigDump, error) - ConfigOrFail(t test.Failer) *envoyAdmin.ConfigDump - - // WaitForConfig queries the Envoy configuration an executes the given accept handler. If the - // response is not accepted, the request will be retried until either a timeout or a response - // has been accepted. - WaitForConfig(accept func(*envoyAdmin.ConfigDump) (bool, error), options ...retry.Option) error - WaitForConfigOrFail(t test.Failer, accept func(*envoyAdmin.ConfigDump) (bool, error), options ...retry.Option) - - // Clusters for the Envoy instance - Clusters() (*envoyAdmin.Clusters, error) - ClustersOrFail(t test.Failer) *envoyAdmin.Clusters - - // Listeners for the Envoy instance - Listeners() (*envoyAdmin.Listeners, error) - ListenersOrFail(t test.Failer) *envoyAdmin.Listeners - - // Logs returns the logs for the sidecar container - Logs() (string, error) - // LogsOrFail returns the logs for the sidecar container, or aborts if an error is found - LogsOrFail(t test.Failer) string - Stats() (map[string]*dto.MetricFamily, error) - StatsOrFail(t test.Failer) map[string]*dto.MetricFamily -} diff --git a/pkg/test/framework/components/echo/staticvm/instance.go b/pkg/test/framework/components/echo/staticvm/instance.go deleted file mode 100644 index fa2178d43..000000000 --- a/pkg/test/framework/components/echo/staticvm/instance.go +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package staticvm - -import ( - "context" - "errors" - "fmt" - "sync" -) - -import ( - "github.com/hashicorp/go-multierror" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" - "github.com/apache/dubbo-go-pixiu/pkg/test" - echoClient "github.com/apache/dubbo-go-pixiu/pkg/test/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/common" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -var _ echo.Instance = &instance{} - -func init() { - echo.RegisterFactory(cluster.StaticVM, newInstances) -} - -type instance struct { - id resource.ID - config echo.Config - address string - workloads echo.Workloads - workloadFilter []echo.Workload -} - -func newInstances(ctx resource.Context, config []echo.Config) (echo.Instances, error) { - errG := multierror.Group{} - mu := sync.Mutex{} - var out echo.Instances - for _, c := range config { - c := c - errG.Go(func() error { - i, err := newInstance(ctx, c) - if err != nil { - return err - } - mu.Lock() - defer mu.Unlock() - out = append(out, i) - return nil - }) - } - if err := errG.Wait().ErrorOrNil(); err != nil { - return nil, err - } - return out, nil -} - -func newInstance(ctx resource.Context, config echo.Config) (echo.Instance, error) { - // TODO is there a need for static cluster to create workload group/entry? - - grpcPort, found := config.Ports.ForProtocol(protocol.GRPC) - if !found { - return nil, errors.New("unable fo find GRPC command port") - } - workloads, err := newWorkloads(config.StaticAddresses, grpcPort.WorkloadPort, config.TLSSettings, config.Cluster) - if err != nil { - return nil, err - } - if len(workloads) == 0 { - return nil, fmt.Errorf("no workloads for %s", config.Service) - } - svcAddr, err := getClusterIP(config) - if err != nil { - return nil, err - } - i := &instance{ - config: config, - address: svcAddr, - workloads: workloads, - } - i.id = ctx.TrackResource(i) - return i, nil -} - -func getClusterIP(config echo.Config) (string, error) { - svc, err := config.Cluster.Primary().CoreV1(). - Services(config.Namespace.Name()).Get(context.TODO(), config.Service, metav1.GetOptions{}) - if err != nil { - return "", err - } - return svc.Spec.ClusterIP, nil -} - -func (i *instance) ID() resource.ID { - return i.id -} - -func (i *instance) NamespacedName() echo.NamespacedName { - return i.config.NamespacedName() -} - -func (i *instance) PortForName(name string) echo.Port { - return i.Config().Ports.MustForName(name) -} - -func (i *instance) Config() echo.Config { - return i.config -} - -func (i *instance) Address() string { - return i.address -} - -func (i *instance) Addresses() []string { - return []string{i.address} -} - -func (i *instance) WithWorkloads(wls ...echo.Workload) echo.Instance { - n := *i - i.workloadFilter = wls - return &n -} - -func (i *instance) Workloads() (echo.Workloads, error) { - final := []echo.Workload{} - for _, wl := range i.workloads { - filtered := false - for _, filter := range i.workloadFilter { - if wl.Address() != filter.Address() { - filtered = true - break - } - } - if !filtered { - final = append(final, wl) - } - } - return final, nil -} - -func (i *instance) WorkloadsOrFail(t test.Failer) echo.Workloads { - w, err := i.Workloads() - if err != nil { - t.Fatalf("failed getting workloads for %s", i.Config().Service) - } - return w -} - -func (i *instance) MustWorkloads() echo.Workloads { - out, err := i.Workloads() - if err != nil { - panic(err) - } - return out -} - -func (i *instance) Clusters() cluster.Clusters { - var out cluster.Clusters - if i.config.Cluster != nil { - out = append(out, i.config.Cluster) - } - return out -} - -func (i *instance) Instances() echo.Instances { - return echo.Instances{i} -} - -func (i *instance) defaultClient() (*echoClient.Client, error) { - wl, err := i.Workloads() - if err != nil { - return nil, err - } - return wl[0].(*workload).Client, nil -} - -func (i *instance) Call(opts echo.CallOptions) (echo.CallResult, error) { - return common.ForwardEcho(i.Config().Service, i, opts, i.defaultClient) -} - -func (i *instance) CallOrFail(t test.Failer, opts echo.CallOptions) echo.CallResult { - t.Helper() - res, err := i.Call(opts) - if err != nil { - t.Fatal(err) - } - return res -} - -func (i *instance) Restart() error { - panic("cannot trigger restart of a static VM") -} diff --git a/pkg/test/framework/components/echo/staticvm/workload.go b/pkg/test/framework/components/echo/staticvm/workload.go deleted file mode 100644 index 072259eb0..000000000 --- a/pkg/test/framework/components/echo/staticvm/workload.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package staticvm - -import ( - "fmt" - "net" - "strings" -) - -import ( - "github.com/hashicorp/go-multierror" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - echoClient "github.com/apache/dubbo-go-pixiu/pkg/test/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" -) - -var _ echo.Workload = &workload{} - -type workload struct { - *echoClient.Client - cluster cluster.Cluster - address string -} - -func newWorkloads(address []string, grpcPort int, tls *common.TLSSettings, c cluster.Cluster) (echo.Workloads, error) { - var errs error - var out echo.Workloads - for _, ip := range address { - w, err := newWorkload(ip, grpcPort, tls, c) - if err != nil { - errs = multierror.Append(errs, err) - } - out = append(out, w) - } - if errs != nil { - return nil, errs - } - return out, nil -} - -func newWorkload(addresses string, grpcPort int, tls *common.TLSSettings, cl cluster.Cluster) (*workload, error) { - var ( - external string - internal string - ) - parts := strings.Split(addresses, ":") - external = parts[0] - if len(parts) > 1 { - internal = parts[1] - } - - c, err := echoClient.New(net.JoinHostPort(external, fmt.Sprint(grpcPort)), tls) - if err != nil { - return nil, err - } - return &workload{ - Client: c, - cluster: cl, - address: internal, - }, nil -} - -func (w *workload) PodName() string { - return "" -} - -func (w *workload) Address() string { - return w.address -} - -func (w *workload) Cluster() cluster.Cluster { - return w.cluster -} - -func (w *workload) Sidecar() echo.Sidecar { - panic("implement me") -} - -func (w *workload) Logs() (string, error) { - panic("implement me") -} - -func (w *workload) LogsOrFail(_ test.Failer) string { - panic("implement me") -} diff --git a/pkg/test/framework/components/echo/util/traffic/generator.go b/pkg/test/framework/components/echo/util/traffic/generator.go deleted file mode 100644 index a9bfd7948..000000000 --- a/pkg/test/framework/components/echo/util/traffic/generator.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package traffic - -import ( - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/check" -) - -const ( - defaultInterval = 1 * time.Second - defaultTimeout = 15 * time.Second -) - -// Config for a traffic Generator. -type Config struct { - // Source of the traffic. - Source echo.Caller - - // Options for generating traffic from the Source to the target. - Options echo.CallOptions - - // Interval between successive call operations. If not set, defaults to 1 second. - Interval time.Duration - - // Maximum time to wait for traffic to complete after stopping. If not set, defaults to 15 seconds. - StopTimeout time.Duration -} - -// Generator of traffic between echo instances. Every time interval -// (as defined by Config.Interval), a grpc request is sent to the source pod, -// causing it to send a request to the destination echo server. Results are -// captured for each request for later processing. -type Generator interface { - // Start sending traffic. - Start() Generator - - // Stop sending traffic and wait for any in-flight requests to complete. - // Returns the Result - Stop() Result -} - -// NewGenerator returns a new Generator with the given configuration. -func NewGenerator(t test.Failer, cfg Config) Generator { - fillInDefaults(&cfg) - return &generator{ - Config: cfg, - t: t, - stop: make(chan struct{}), - stopped: make(chan struct{}), - } -} - -var _ Generator = &generator{} - -type generator struct { - Config - t test.Failer - result Result - stop chan struct{} - stopped chan struct{} -} - -func (g *generator) Start() Generator { - go func() { - t := time.NewTimer(g.Interval) - for { - select { - case <-g.stop: - t.Stop() - close(g.stopped) - return - case <-t.C: - g.result.add(g.Source.Call(g.Options)) - t.Reset(g.Interval) - } - } - }() - return g -} - -func (g *generator) Stop() Result { - // Trigger the generator to stop. - close(g.stop) - - // Wait for the generator to exit. - t := time.NewTimer(g.StopTimeout) - select { - case <-g.stopped: - t.Stop() - if g.result.TotalRequests == 0 { - g.t.Fatal("no requests completed before stopping the traffic generator") - } - return g.result - case <-t.C: - g.t.Fatal("timed out waiting for result") - } - // Can never happen, but the compiler doesn't know that Fatal terminates - return Result{} -} - -func fillInDefaults(cfg *Config) { - if cfg.Interval == 0 { - cfg.Interval = defaultInterval - } - if cfg.StopTimeout == 0 { - cfg.StopTimeout = defaultTimeout - } - if cfg.Options.Check == nil { - cfg.Options.Check = check.OK() - } -} diff --git a/pkg/test/framework/components/echo/util/traffic/result.go b/pkg/test/framework/components/echo/util/traffic/result.go deleted file mode 100644 index f1d7860c1..000000000 --- a/pkg/test/framework/components/echo/util/traffic/result.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package traffic - -import ( - "bytes" - "fmt" -) - -import ( - "github.com/hashicorp/go-multierror" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" -) - -// Result of a traffic generation operation. -type Result struct { - TotalRequests int - SuccessfulRequests int - Error error -} - -func (r Result) String() string { - buf := &bytes.Buffer{} - - _, _ = fmt.Fprintf(buf, "TotalRequests: %d\n", r.TotalRequests) - _, _ = fmt.Fprintf(buf, "SuccessfulRequests: %d\n", r.SuccessfulRequests) - _, _ = fmt.Fprintf(buf, "PercentSuccess: %f\n", r.PercentSuccess()) - _, _ = fmt.Fprintf(buf, "Errors: %v\n", r.Error) - - return buf.String() -} - -func (r *Result) add(result echo.CallResult, err error) { - count := result.Responses.Len() - if count == 0 { - count = 1 - } - - r.TotalRequests += count - if err != nil { - r.Error = multierror.Append(r.Error, fmt.Errorf("request %d: %v", r.TotalRequests, err)) - } else { - r.SuccessfulRequests += count - } -} - -func (r Result) PercentSuccess() float64 { - return float64(r.SuccessfulRequests) / float64(r.TotalRequests) -} - -// CheckSuccessRate asserts that a minimum success threshold was met. -func (r Result) CheckSuccessRate(t test.Failer, minimumPercent float64) { - if r.PercentSuccess() < minimumPercent { - t.Fatalf("Minimum success threshold, %f, was not met. %d/%d (%f) requests failed: %v", - minimumPercent, r.SuccessfulRequests, r.TotalRequests, r.PercentSuccess(), r.Error) - } - if r.SuccessfulRequests == r.TotalRequests { - t.Log("traffic checker succeeded with all successful requests") - } else { - t.Logf("traffic checker met minimum threshold, with %d/%d successes, but encountered some failures: %v", r.SuccessfulRequests, r.TotalRequests, r.Error) - } -} diff --git a/pkg/test/framework/components/echo/workload.go b/pkg/test/framework/components/echo/workload.go deleted file mode 100644 index 49b731ab2..000000000 --- a/pkg/test/framework/components/echo/workload.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -import ( - "context" - "sort" - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/proto" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" -) - -// WorkloadContainer is container for a number of Workload objects. -type WorkloadContainer interface { - // Workloads retrieves the list of all deployed workloads for this Echo service. - // Guarantees at least one workload, if error == nil. - Workloads() (Workloads, error) - WorkloadsOrFail(t test.Failer) Workloads - MustWorkloads() Workloads - - // Clusters where the workloads are deployed. - Clusters() cluster.Clusters -} - -// Workload provides an interface for a single deployed echo server. -type Workload interface { - // PodName gets the original pod name for the workload. - PodName() string - - // Address returns the network address of the endpoint. - Address() string - - // Sidecar if one was specified. - Sidecar() Sidecar - - // Cluster where this Workload resides. - Cluster() cluster.Cluster - - // ForwardEcho executes specific call from this workload. - // TODO(nmittler): Instead of this, we should just make Workload implement Caller. - ForwardEcho(context.Context, *proto.ForwardEchoRequest) (echo.Responses, error) - - // Logs returns the logs for the app container - Logs() (string, error) - LogsOrFail(t test.Failer) string -} - -type Workloads []Workload - -func (ws Workloads) Len() int { - return len(ws) -} - -// Addresses returns the list of addresses for all workloads. -func (ws Workloads) Addresses() []string { - out := make([]string, 0, len(ws)) - for _, w := range ws { - out = append(out, w.Address()) - } - return out -} - -func (ws Workloads) Clusters() cluster.Clusters { - clusters := make(map[string]cluster.Cluster) - for _, w := range ws { - if c := w.Cluster(); c != nil { - clusters[c.Name()] = c - } - } - out := make(cluster.Clusters, 0, len(clusters)) - for _, c := range clusters { - out = append(out, c) - } - - // Sort the clusters by name. - sort.SliceStable(out, func(i, j int) bool { - return strings.Compare(out[i].Name(), out[j].Name()) < 0 - }) - - return out -} diff --git a/pkg/test/framework/components/echo/workloadclass.go b/pkg/test/framework/components/echo/workloadclass.go deleted file mode 100644 index 14dbec5ab..000000000 --- a/pkg/test/framework/components/echo/workloadclass.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package echo - -// WorkloadClass is the class of workload in the echo instance -type WorkloadClass = string - -const ( - Proxyless WorkloadClass = "proxyless" - VM WorkloadClass = "vm" - Delta WorkloadClass = "delta" - TProxy WorkloadClass = "tproxy" - Naked WorkloadClass = "naked" - External WorkloadClass = "external" - StatefulSet WorkloadClass = "statefulset" - Headless WorkloadClass = "headless" - Standard WorkloadClass = "standard" -) diff --git a/pkg/test/framework/components/environment/kube/fake.go b/pkg/test/framework/components/environment/kube/fake.go deleted file mode 100644 index 7461d51bd..000000000 --- a/pkg/test/framework/components/environment/kube/fake.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "fmt" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster/clusterboot" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -var _ resource.Environment = FakeEnvironment{} - -// FakeEnvironment for testing. -type FakeEnvironment struct { - Name string - NumClusters int - IDValue string -} - -func (f FakeEnvironment) ID() resource.ID { - return resource.FakeID(f.IDValue) -} - -func (f FakeEnvironment) IsMultinetwork() bool { - return false -} - -func (f FakeEnvironment) EnvironmentName() string { - if len(f.Name) == 0 { - return "fake" - } - return f.Name -} - -func (f FakeEnvironment) AllClusters() cluster.Clusters { - factory := clusterboot.NewFactory() - for i := 0; i < f.NumClusters; i++ { - factory = factory.With(cluster.Config{Kind: cluster.Fake, Name: fmt.Sprintf("cluster-%d", i)}) - } - out, err := factory.Build() - if err != nil { - panic(err) - } - return out -} - -func (f FakeEnvironment) Clusters() cluster.Clusters { - return f.AllClusters().MeshClusters() -} diff --git a/pkg/test/framework/components/environment/kube/flags.go b/pkg/test/framework/components/environment/kube/flags.go deleted file mode 100644 index 123f496b4..000000000 --- a/pkg/test/framework/components/environment/kube/flags.go +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "flag" - "fmt" - "os" - "path/filepath" - "strconv" - "strings" -) - -import ( - "gopkg.in/yaml.v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/config" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/file" -) - -const ( - defaultKubeConfig = "~/.kube/config" -) - -var ( - // Settings we will collect from the command-line. - settingsFromCommandLine = &Settings{ - LoadBalancerSupported: true, - } - // hold kubeconfigs from command line to split later - kubeConfigs string - // hold controlPlaneTopology from command line to parse later - controlPlaneTopology string - // hold networkTopology from command line to parse later - networkTopology string - // hold configTopology from command line to parse later - configTopology string - // file defining all types of topology - clusterConfigs configsVal -) - -// NewSettingsFromCommandLine returns Settings obtained from command-line flags. -// config.Parse must be called before calling this function. -func NewSettingsFromCommandLine() (*Settings, error) { - if !config.Parsed() { - panic("config.Parse must be called before this function") - } - - s := settingsFromCommandLine.clone() - - // Process the kube clusterConfigs. - var err error - s.KubeConfig, err = parseKubeConfigs(kubeConfigs, ",") - if err != nil { - return nil, fmt.Errorf("error parsing KubeConfigs from command-line: %v", err) - } - - s.controlPlaneTopology, err = newControlPlaneTopology() - if err != nil { - return nil, err - } - - s.networkTopology, err = parseNetworkTopology() - if err != nil { - return nil, err - } - - s.configTopology, err = newConfigTopology() - if err != nil { - return nil, err - } - - return s, nil -} - -func getKubeConfigsFromEnvironment() ([]string, error) { - // Normalize KUBECONFIG so that it is separated by the OS path list separator. - // The framework currently supports comma as a separator, but that violates the - // KUBECONFIG spec. - value := env.KUBECONFIG.Value() - if strings.Contains(value, ",") { - updatedValue := strings.ReplaceAll(value, ",", string(filepath.ListSeparator)) - _ = os.Setenv(env.KUBECONFIG.Name(), updatedValue) - scopes.Framework.Warnf("KUBECONFIG contains commas: %s.\nReplacing with %s: %s", value, - filepath.ListSeparator, updatedValue) - value = updatedValue - } - out, err := parseKubeConfigs(value, string(filepath.ListSeparator)) - if err != nil { - return nil, err - } - if len(out) == 0 { - scopes.Framework.Info("Environment variable KUBECONFIG unspecified, defaultiing to ~/.kube/config.") - normalizedDefaultKubeConfig, err := file.NormalizePath(defaultKubeConfig) - if err != nil { - return nil, fmt.Errorf("error normalizing default kube config file %s: %v", - defaultKubeConfig, err) - } - out = []string{normalizedDefaultKubeConfig} - } - return out, nil -} - -func parseKubeConfigs(value, separator string) ([]string, error) { - if len(value) == 0 { - return make([]string, 0), nil - } - - parts := strings.Split(value, separator) - out := make([]string, 0, len(parts)) - for _, f := range parts { - f := strings.TrimSpace(f) - if len(f) != 0 { - var err error - if f, err = file.NormalizePath(f); err != nil { - return nil, err - } - out = append(out, f) - } - } - return out, nil -} - -func newControlPlaneTopology() (clusterTopology, error) { - topology, err := parseClusterTopology(controlPlaneTopology) - if err != nil { - return nil, err - } - if len(topology) == 0 { - return nil, nil - } - return topology, nil -} - -func newConfigTopology() (clusterTopology, error) { - topology, err := parseClusterTopology(configTopology) - if err != nil { - return nil, err - } - if len(topology) == 0 { - return nil, nil - } - return topology, nil -} - -func parseClusterTopology(topology string) (clusterTopology, error) { - if topology == "" { - return nil, nil - } - out := make(clusterTopology) - - values := strings.Split(topology, ",") - for _, v := range values { - parts := strings.Split(v, ":") - if len(parts) != 2 { - return nil, fmt.Errorf("failed parsing topology mapping entry %s", v) - } - sourceCluster, err := parseClusterIndex(parts[0]) - if err != nil { - return nil, err - } - targetCluster, err := parseClusterIndex(parts[1]) - if err != nil { - return nil, err - } - if _, ok := out[sourceCluster]; ok { - return nil, fmt.Errorf("multiple mappings for source cluster %d", sourceCluster) - } - out[sourceCluster] = targetCluster - } - return out, nil -} - -func parseNetworkTopology() (map[clusterIndex]string, error) { - if networkTopology == "" { - return nil, nil - } - out := make(map[clusterIndex]string) - values := strings.Split(networkTopology, ",") - for _, v := range values { - parts := strings.Split(v, ":") - if len(parts) != 2 { - return nil, fmt.Errorf("failed parsing network mapping mapping entry %s", v) - } - cluster, err := parseClusterIndex(parts[0]) - if err != nil { - return nil, err - } - if len(parts[1]) == 0 { - return nil, fmt.Errorf("failed parsing network mapping entry %s: failed parsing network name", v) - } - out[cluster] = parts[1] - } - return out, nil -} - -func parseClusterIndex(index string) (clusterIndex, error) { - ci, err := strconv.Atoi(index) - if err != nil || ci < 0 { - return 0, fmt.Errorf("failed parsing cluster index: %s", index) - } - return clusterIndex(ci), nil -} - -// configsVal implements config.Value to allow setting the path as a flag or embedding the topology content -// in the overal test framework config -type configsVal []cluster.Config - -func (c *configsVal) String() string { - return fmt.Sprint(*c) -} - -func (c *configsVal) Set(s string) error { - filename, err := file.NormalizePath(s) - if err != nil { - return err - } - topologyBytes, err := os.ReadFile(filename) - if err != nil { - return err - } - configs := []cluster.Config{} - if err := yaml.Unmarshal(topologyBytes, &configs); err != nil { - return fmt.Errorf("failed to parse %s: %v", s, err) - } - *c = configs - scopes.Framework.Infof("Using clusterConfigs file: %v.", s) - return nil -} - -func (c *configsVal) SetConfig(m interface{}) error { - bytes, err := yaml.Marshal(m) - if err != nil { - return err - } - configs := []cluster.Config{} - if err := yaml.Unmarshal(bytes, &configs); err != nil { - return fmt.Errorf("failed to reparse: %v", err) - } - *c = configs - scopes.Framework.Infof("Using topology from test framework config file.") - return nil -} - -var _ config.Value = &configsVal{} - -// init registers the command-line flags that we can exposed for "go test". -func init() { - flag.StringVar(&kubeConfigs, "istio.test.kube.config", "", - "A comma-separated list of paths to kube config files for cluster environments.") - flag.BoolVar(&settingsFromCommandLine.LoadBalancerSupported, "istio.test.kube.loadbalancer", settingsFromCommandLine.LoadBalancerSupported, - "Indicates whether or not clusters in the environment support external IPs for LoadBalaner services. Used "+ - "to obtain the right IP address for the Ingress Gateway. Set --istio.test.kube.loadbalancer=false for local KinD tests."+ - "without MetalLB installed.") - flag.StringVar(&controlPlaneTopology, "istio.test.kube.controlPlaneTopology", - "", "Specifies the mapping for each cluster to the cluster hosting its control plane. The value is a "+ - "comma-separated list of the form :, where the indexes refer to the order in which "+ - "a given cluster appears in the 'istio.test.kube.config' flag. This topology also determines where control planes should "+ - "be deployed. If not specified, the default is to deploy a control plane per cluster (i.e. `replicated control "+ - "planes') and map every cluster to itself (e.g. 0:0,1:1,...).") - flag.StringVar(&networkTopology, "istio.test.kube.networkTopology", - "", "Specifies the mapping for each cluster to it's network name, for multi-network scenarios. The value is a "+ - "comma-separated list of the form :, where the indexes refer to the order in which "+ - "a given cluster appears in the 'istio.test.kube.config' flag. If not specified, network name will be left unset") - flag.StringVar(&configTopology, "istio.test.kube.configTopology", - "", "Specifies the mapping for each cluster to the cluster hosting its config. The value is a "+ - "comma-separated list of the form :, where the indexes refer to the order in which "+ - "a given cluster appears in the 'istio.test.kube.config' flag. If not specified, the default is every cluster maps to itself(e.g. 0:0,1:1,...).") - flag.Var(&clusterConfigs, "istio.test.kube.topology", "The path to a JSON file that defines control plane,"+ - " network, and config cluster topology. The JSON document should be an array of objects that contain the keys \"control_plane_index\","+ - " \"network_id\" and \"config_index\" with all integer values. If control_plane_index is omitted, the index of the array item is used."+ - "If network_id is omitted, 0 will be used. If config_index is omitted, control_plane_index will be used.") - flag.BoolVar(&settingsFromCommandLine.MCSControllerEnabled, "istio.test.kube.mcs.controllerEnabled", settingsFromCommandLine.MCSControllerEnabled, - "Indicates whether the Kubernetes environment has a Multi-Cluster Services (MCS) controller running.") - flag.StringVar(&settingsFromCommandLine.MCSAPIGroup, "istio.test.kube.mcs.apiGroup", "multicluster.x-k8s.io", - "The group to be used for the Kubernetes Multi-Cluster Services (MCS) API.") - flag.StringVar(&settingsFromCommandLine.MCSAPIVersion, "istio.test.kube.mcs.apiVersion", "v1alpha1", - "The version to be used for the Kubernets Multi-Cluster Services (MCS) API.") -} diff --git a/pkg/test/framework/components/environment/kube/kube.go b/pkg/test/framework/components/environment/kube/kube.go deleted file mode 100644 index dcc46bb1f..000000000 --- a/pkg/test/framework/components/environment/kube/kube.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster/clusterboot" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" -) - -// Environment is the implementation of a kubernetes environment. It implements environment.Environment, -// and also hosts publicly accessible methods that are specific to cluster environment. -type Environment struct { - id resource.ID - ctx resource.Context - clusters []cluster.Cluster - s *Settings -} - -var _ resource.Environment = &Environment{} - -// New returns a new Kubernetes environment -func New(ctx resource.Context, s *Settings) (env resource.Environment, err error) { - defer func() { - if err != nil && !ctx.Settings().CIMode { - scopes.Framework.Infof(` -There was an error while setting up the Kubernetes test environment. -Check the test framework wiki for details on running the tests locally: - https://github.com/istio/istio/wiki/Istio-Test-Framework -`) - } - }() - scopes.Framework.Infof("Test Framework Kubernetes environment Settings:\n%s", s) - e := &Environment{ - ctx: ctx, - s: s, - } - e.id = ctx.TrackResource(e) - - configs, err := s.clusterConfigs() - if err != nil { - return nil, err - } - clusters, err := clusterboot.NewFactory().With(configs...).Build() - if err != nil { - return nil, err - } - e.clusters = clusters - - return e, nil -} - -func (e *Environment) EnvironmentName() string { - return "Kube" -} - -func (e *Environment) IsMulticluster() bool { - return len(e.clusters) > 1 -} - -// IsMultinetwork returns true if there is more than one network name in networkTopology. -func (e *Environment) IsMultinetwork() bool { - return len(e.ClustersByNetwork()) > 1 -} - -func (e *Environment) AllClusters() cluster.Clusters { - out := make([]cluster.Cluster, 0, len(e.clusters)) - out = append(out, e.clusters...) - return out -} - -func (e *Environment) Clusters() cluster.Clusters { - return e.AllClusters().MeshClusters() -} - -// ClustersByNetwork returns an inverse mapping of the network topolgoy to a slice of clusters in a given network. -func (e *Environment) ClustersByNetwork() map[string][]cluster.Cluster { - out := make(map[string][]cluster.Cluster) - for _, c := range e.Clusters() { - out[c.NetworkName()] = append(out[c.NetworkName()], c) - } - return out -} - -// ID implements resource.Instance -func (e *Environment) ID() resource.ID { - return e.id -} - -func (e *Environment) Settings() *Settings { - return e.s.clone() -} diff --git a/pkg/test/framework/components/environment/kube/settings.go b/pkg/test/framework/components/environment/kube/settings.go deleted file mode 100644 index 27c164af9..000000000 --- a/pkg/test/framework/components/environment/kube/settings.go +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "fmt" -) - -import ( - "k8s.io/apimachinery/pkg/runtime/schema" -) - -import ( - istioKube "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/config" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" -) - -// clusterIndex is the index of a cluster within the KubeConfig or topology file entries -type clusterIndex int - -// clusterTopology defines the associations between multiple clusters in a topology. -type clusterTopology = map[clusterIndex]clusterIndex - -// ClientFactoryFunc is a transformation function that creates k8s clients -// from the provided k8s config files. -type ClientFactoryFunc func(kubeConfigs []string) ([]istioKube.ExtendedClient, error) - -// Settings provide kube-specific Settings from flags. -type Settings struct { - // An array of paths to kube config files. Required if the environment is kubernetes. - KubeConfig []string - - // Indicates that the LoadBalancer services can obtain a public IP. If not, NodePort be used as a workaround - // for ingress gateway. KinD will not support LoadBalancer out of the box and requires a workaround such as - // MetalLB. - LoadBalancerSupported bool - - // MCSControllerEnabled indicates that the Kubernetes environment has a Multi-Cluster Services (MCS) - // controller up and running. - MCSControllerEnabled bool - - // MCSAPIGroup the group to use for the MCS API - MCSAPIGroup string - - // MCSAPIVersion the version to use for the MCS API - MCSAPIVersion string - - // controlPlaneTopology maps each cluster to the cluster that runs its control plane. For replicated control - // plane cases (where each cluster has its own control plane), the cluster will map to itself (e.g. 0->0). - controlPlaneTopology clusterTopology - - // networkTopology is used for the initial assignment of networks to each cluster. - // The source of truth clusters' networks is the Cluster instances themselves, rather than this field. - networkTopology map[clusterIndex]string - - // configTopology maps each cluster to the cluster that runs it's config. - // If the cluster runs its own config, the cluster will map to itself (e.g. 0->0) - // By default, we use the controlPlaneTopology as the config topology. - configTopology clusterTopology -} - -func (s *Settings) clone() *Settings { - c := *s - return &c -} - -func (s *Settings) clusterConfigs() ([]cluster.Config, error) { - if len(clusterConfigs) == 0 { - // not loaded from file file, build directly from provided kubeconfigs and topology flag maps - return s.clusterConfigsFromFlags() - } - - return s.clusterConfigsFromFile() -} - -func (s *Settings) clusterConfigsFromFlags() ([]cluster.Config, error) { - if len(s.KubeConfig) == 0 { - // flag-based, but no kubeconfigs, get kubeconfigs from environment - scopes.Framework.Info("Flags istio.test.kube.config and istio.test.kube.topology not specified.") - var err error - s.KubeConfig, err = getKubeConfigsFromEnvironment() - if err != nil { - return nil, fmt.Errorf("error parsing KubeConfigs from environment: %v", err) - } - } - scopes.Framework.Infof("Using KubeConfigs: %v.", s.KubeConfig) - if err := s.validateTopologyFlags(len(s.KubeConfig)); err != nil { - return nil, err - } - var configs []cluster.Config - for i, kc := range s.KubeConfig { - ci := clusterIndex(i) - cfg := cluster.Config{ - Name: fmt.Sprintf("cluster-%d", i), - Kind: cluster.Kubernetes, - Network: s.networkTopology[ci], - Meta: config.Map{"kubeconfig": kc}, - } - if idx, ok := s.controlPlaneTopology[ci]; ok { - cfg.PrimaryClusterName = fmt.Sprintf("cluster-%d", idx) - } - if idx, ok := s.configTopology[ci]; ok { - cfg.ConfigClusterName = fmt.Sprintf("cluster-%d", idx) - } - configs = append(configs, cfg) - } - return configs, nil -} - -func (s *Settings) clusterConfigsFromFile() ([]cluster.Config, error) { - // Allow kubeconfig flag to override file - var err error - clusterConfigs, err = replaceKubeconfigs(clusterConfigs, s.KubeConfig) - if err != nil { - return nil, err - } - - if err := s.validateTopologyFlags(len(clusterConfigs)); err != nil { - return nil, err - } - - // Apply clusterConfigs overrides from flags, if specified. - if s.controlPlaneTopology != nil && len(s.controlPlaneTopology) > 0 { - if len(s.controlPlaneTopology) != len(clusterConfigs) { - return nil, fmt.Errorf("istio.test.kube.controlPlaneTopology has %d entries but there are %d clusters", len(controlPlaneTopology), len(clusterConfigs)) - } - for src, dst := range s.controlPlaneTopology { - clusterConfigs[src].PrimaryClusterName = clusterConfigs[dst].Name - } - } - if s.configTopology != nil { - if len(s.configTopology) != len(clusterConfigs) { - return nil, fmt.Errorf("istio.test.kube.configTopology has %d entries but there are %d clusters", len(controlPlaneTopology), len(clusterConfigs)) - } - for src, dst := range s.controlPlaneTopology { - clusterConfigs[src].ConfigClusterName = clusterConfigs[dst].Name - } - } - if s.networkTopology != nil { - if len(s.networkTopology) != len(clusterConfigs) { - return nil, fmt.Errorf("istio.test.kube.networkTopology has %d entries but there are %d clusters", len(controlPlaneTopology), len(clusterConfigs)) - } - for src, network := range s.networkTopology { - clusterConfigs[src].ConfigClusterName = network - } - } - - return clusterConfigs, nil -} - -func (s *Settings) validateTopologyFlags(nClusters int) error { - makeErr := func(idx clusterIndex, flag string) error { - return fmt.Errorf("invalid cluster index %d in %s exceeds %d, the number of configured clusters", idx, flag, nClusters) - } - for flag, m := range map[string]clusterTopology{ - "istio.test.kube.controlPlaneTopology": s.controlPlaneTopology, - "istio.test.kube.configTopology": s.configTopology, - } { - for src, dst := range m { - if int(src) >= nClusters { - return makeErr(src, flag) - } - if int(dst) >= nClusters { - return makeErr(dst, flag) - } - } - } - for idx := range s.networkTopology { - if int(idx) >= nClusters { - return makeErr(idx, "istio.test.kube.networkTopology") - } - } - - return nil -} - -// replaceKubeconfigs allows using flags to specify the kubeconfigs for each cluster instead of the topology flags. -// This capability is needed for backwards compatibility and will likely be removed. -func replaceKubeconfigs(configs []cluster.Config, kubeconfigs []string) ([]cluster.Config, error) { - if len(kubeconfigs) == 0 { - return configs, nil - } - kube := 0 - out := []cluster.Config{} - for _, cfg := range configs { - if cfg.Kind == cluster.Kubernetes { - if kube >= len(kubeconfigs) { - // not enough to cover all clusters in file - return nil, fmt.Errorf("istio.test.kube.cfg should have a kubeconfig for each kube cluster") - } - if cfg.Meta == nil { - cfg.Meta = config.Map{} - } - cfg.Meta["kubeconfig"] = kubeconfigs[kube] - } - kube++ - out = append(out, cfg) - } - if kube < len(kubeconfigs) { - return nil, fmt.Errorf("%d kubeconfigs were provided but topolgy has %d kube clusters", len(kubeconfigs), kube) - } - - return out, nil -} - -func (s *Settings) MCSAPIGroupVersion() schema.GroupVersion { - return schema.GroupVersion{ - Group: s.MCSAPIGroup, - Version: s.MCSAPIVersion, - } -} - -func (s *Settings) ServiceExportGVR() schema.GroupVersionResource { - return s.MCSAPIGroupVersion().WithResource("serviceexports") -} - -func (s *Settings) ServiceImportGVR() schema.GroupVersionResource { - return s.MCSAPIGroupVersion().WithResource("serviceimports") -} - -// String implements fmt.Stringer -func (s *Settings) String() string { - result := "" - - result += fmt.Sprintf("Kubeconfigs: %s\n", s.KubeConfig) - result += fmt.Sprintf("LoadBalancerSupported: %v\n", s.LoadBalancerSupported) - result += fmt.Sprintf("MCSControllerEnabled: %v\n", s.MCSControllerEnabled) - result += fmt.Sprintf("ControlPlaneTopology: %v\n", s.controlPlaneTopology) - result += fmt.Sprintf("NetworkTopology: %v\n", s.networkTopology) - result += fmt.Sprintf("ConfigTopology: %v\n", s.configTopology) - return result -} diff --git a/pkg/test/framework/components/gcemetadata/gce_metadata_server.yaml b/pkg/test/framework/components/gcemetadata/gce_metadata_server.yaml deleted file mode 100644 index 28fc4fe98..000000000 --- a/pkg/test/framework/components/gcemetadata/gce_metadata_server.yaml +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -apiVersion: v1 -kind: Service -metadata: - name: gce-metadata-server - labels: - app: gce-metadata -spec: - ports: - - name: http - port: 8080 - selector: - app: gce-metadata ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: gce-metadata-server -spec: - replicas: 1 - selector: - matchLabels: - app: gce-metadata - template: - metadata: - labels: - app: gce-metadata - spec: - containers: - - image: gcr.io/istio-testing/fake-gce-metadata:1.0 - imagePullPolicy: Always - name: gce-metadata - ports: - - containerPort: 8080 diff --git a/pkg/test/framework/components/gcemetadata/gcemetadata.go b/pkg/test/framework/components/gcemetadata/gcemetadata.go deleted file mode 100644 index 2f84f331e..000000000 --- a/pkg/test/framework/components/gcemetadata/gcemetadata.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package gcemetadata provides basic utilities around configuring the fake -// GCE Metadata Server component for integration testing. -package gcemetadata - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -// Instance represents a deployed GCE Metadata Server app instance. -type Instance interface { - // Address is the IP Address of the service provided by the fake GCE - // Metadata Server. - Address() string -} - -// Config defines the options for creating an fake GCE Metadata Server component. -type Config struct { - // Cluster to be used in a multicluster environment - Cluster cluster.Cluster -} - -// New returns a new instance of stackdriver. -func New(ctx resource.Context, c Config) (i Instance, err error) { - return newKube(ctx, c) -} - -// NewOrFail returns a new GCE Metadata Server instance or fails test. -func NewOrFail(t test.Failer, ctx resource.Context, c Config) Instance { - t.Helper() - i, err := New(ctx, c) - if err != nil { - t.Fatalf("gcemetadata.NewOrFail: %v", err) - } - - return i -} diff --git a/pkg/test/framework/components/gcemetadata/kube.go b/pkg/test/framework/components/gcemetadata/kube.go deleted file mode 100644 index 0f54bcc0d..000000000 --- a/pkg/test/framework/components/gcemetadata/kube.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gcemetadata - -import ( - "fmt" - "io" - "net" -) - -import ( - kubeApiCore "k8s.io/api/core/v1" -) - -import ( - environ "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/namespace" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - testKube "github.com/apache/dubbo-go-pixiu/pkg/test/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" -) - -const ( - ns = "gce-metadata" -) - -var ( - _ Instance = &kubeComponent{} - _ io.Closer = &kubeComponent{} -) - -type kubeComponent struct { - id resource.ID - ns namespace.Instance - cluster cluster.Cluster - address string -} - -func newKube(ctx resource.Context, cfg Config) (Instance, error) { - c := &kubeComponent{ - cluster: ctx.Clusters().GetOrDefault(cfg.Cluster), - } - c.id = ctx.TrackResource(c) - var err error - scopes.Framework.Info("=== BEGIN: Deploy GCE Metadata Server ===") - defer func() { - if err != nil { - err = fmt.Errorf("gcemetadata deployment failed: %v", err) - scopes.Framework.Infof("=== FAILED: Deploy GCE Metadata Server ===") - _ = c.Close() - } else { - scopes.Framework.Info("=== SUCCEEDED: Deploy GCE Metadata Server ===") - } - }() - - c.ns, err = namespace.New(ctx, namespace.Config{ - Prefix: ns, - }) - if err != nil { - return nil, fmt.Errorf("could not create %q namespace for GCE Metadata Server install; err: %v", ns, err) - } - - // apply YAML - if err := c.cluster.ApplyYAMLFiles(c.ns.Name(), environ.GCEMetadataServerInstallFilePath); err != nil { - return nil, fmt.Errorf("failed to apply rendered %s, err: %v", environ.GCEMetadataServerInstallFilePath, err) - } - - var svc *kubeApiCore.Service - if svc, _, err = testKube.WaitUntilServiceEndpointsAreReady(c.cluster, c.ns.Name(), "gce-metadata-server"); err != nil { - scopes.Framework.Infof("Error waiting for GCE Metadata service to be available: %v", err) - return nil, err - } - - c.address = net.JoinHostPort(svc.Spec.ClusterIP, fmt.Sprint(svc.Spec.Ports[0].TargetPort.IntVal)) - scopes.Framework.Infof("GCE Metadata Server in-cluster address: %s", c.address) - - return c, nil -} - -func (c *kubeComponent) ID() resource.ID { - return c.id -} - -// Close implements io.Closer. -func (c *kubeComponent) Close() error { - return nil -} - -func (c *kubeComponent) Address() string { - return c.address -} diff --git a/pkg/test/framework/components/istio/config.go b/pkg/test/framework/components/istio/config.go deleted file mode 100644 index 94261f525..000000000 --- a/pkg/test/framework/components/istio/config.go +++ /dev/null @@ -1,338 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istio - -import ( - "fmt" - "os" - "path" - "path/filepath" - "strings" -) - -import ( - kubeCore "k8s.io/api/core/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/namespace" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" -) - -const ( - // DefaultSystemNamespace default value for SystemNamespace - DefaultSystemNamespace = "dubbo-system" - - // IntegrationTestDefaultsIOP is the path of the default IstioOperator spec to use - // for integration tests - IntegrationTestDefaultsIOP = "tests/integration/iop-integration-test-defaults.yaml" - - // IntegrationTestDefaultsIOPWithQUIC is the path of the default IstioOperator spec to - // use for integration tests involving QUIC - IntegrationTestDefaultsIOPWithQUIC = "tests/integration/iop-integration-test-defaults-with-quic.yaml" - - // IntegrationTestRemoteDefaultsIOP is the path of the default IstioOperator spec to use - // on remote clusters for integration tests - IntegrationTestRemoteDefaultsIOP = "tests/integration/iop-remote-integration-test-defaults.yaml" - - // BaseIOP is the path of the base IstioOperator spec - BaseIOP = "tests/integration/base.yaml" - - // IntegrationTestRemoteGatewaysIOP is the path of the default IstioOperator spec to use - // to install gateways on remote clusters for integration tests - IntegrationTestRemoteGatewaysIOP = "tests/integration/iop-remote-integration-test-gateways.yaml" - - // IntegrationTestExternalIstiodPrimaryDefaultsIOP is the path of the default IstioOperator spec to use - // on external istiod primary clusters for integration tests - IntegrationTestExternalIstiodPrimaryDefaultsIOP = "tests/integration/iop-externalistiod-primary-integration-test-defaults.yaml" - - // IntegrationTestExternalIstiodConfigDefaultsIOP is the path of the default IstioOperator spec to use - // on external istiod config clusters for integration tests - IntegrationTestExternalIstiodConfigDefaultsIOP = "tests/integration/iop-externalistiod-config-integration-test-defaults.yaml" - - // hubValuesKey values key for the Docker image hub. - hubValuesKey = "global.hub" - - // tagValuesKey values key for the Docker image tag. - tagValuesKey = "global.tag" - - // imagePullPolicyValuesKey values key for the Docker image pull policy. - imagePullPolicyValuesKey = "global.imagePullPolicy" -) - -var ( - helmValues string - operatorOptions string - - settingsFromCommandline = &Config{ - SystemNamespace: DefaultSystemNamespace, - TelemetryNamespace: DefaultSystemNamespace, - DeployIstio: true, - PrimaryClusterIOPFile: IntegrationTestDefaultsIOP, - ConfigClusterIOPFile: IntegrationTestDefaultsIOP, - RemoteClusterIOPFile: IntegrationTestRemoteDefaultsIOP, - BaseIOPFile: BaseIOP, - DeployEastWestGW: true, - DumpKubernetesManifests: false, - IstiodlessRemotes: false, - EnableCNI: false, - } -) - -// Config provide kube-specific Config from flags. -type Config struct { - // The namespace where the Istio components (<=1.1) reside in a typical deployment (default: "dubbo-system"). - SystemNamespace string - - // The namespace in which kiali, tracing providers, graphana, prometheus are deployed. - TelemetryNamespace string - - // The IstioOperator spec file to be used for Control plane cluster by default - PrimaryClusterIOPFile string - - // The IstioOperator spec file to be used for Config cluster by default - ConfigClusterIOPFile string - - // The IstioOperator spec file to be used for Remote cluster by default - RemoteClusterIOPFile string - - // The IstioOperator spec file used as the base for all installs - BaseIOPFile string - - // Override values specifically for the ICP crd - // This is mostly required for cases where --set cannot be used - // These values are applied to non-remote clusters - ControlPlaneValues string - - // Override values specifically for the ICP crd - // This is mostly required for cases where --set cannot be used - // These values are only applied to remote clusters - // Default value will be ControlPlaneValues if no remote values provided - RemoteClusterValues string - - // Override values specifically for the ICP crd - // This is mostly required for cases where --set cannot be used - // These values are only applied to remote config clusters - // Default value will be ControlPlaneValues if no remote values provided - ConfigClusterValues string - - // Overrides for the Helm values file. - Values map[string]string - - // Indicates that the test should deploy Istio into the target Kubernetes cluster before running tests. - DeployIstio bool - - // Do not wait for the validation webhook before completing the deployment. This is useful for - // doing deployments without Galley. - SkipWaitForValidationWebhook bool - - // Indicates that the test should deploy Istio's east west gateway into the target Kubernetes cluster - // before running tests. - DeployEastWestGW bool - - // DumpKubernetesManifests will cause Kubernetes YAML generated by istioctl install/generate to be dumped to artifacts. - DumpKubernetesManifests bool - - // IstiodlessRemotes makes remote clusters run without istiod, using webhooks/ca from the primary cluster. - // TODO we could set this per-cluster if istiod was smarter about patching remotes. - IstiodlessRemotes bool - - // OperatorOptions overrides default operator configuration. - OperatorOptions map[string]string - - // EnableCNI indicates the test should have CNI enabled. - EnableCNI bool -} - -func (c *Config) OverridesYAML(s *resource.Settings) string { - return fmt.Sprintf(` -global: - hub: %s - tag: %s -`, s.Image.Hub, s.Image.Tag) -} - -func (c *Config) IstioOperatorConfigYAML(s *resource.Settings, iopYaml string) string { - data := "" - if iopYaml != "" { - data = Indent(iopYaml, " ") - } - - return fmt.Sprintf(` -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: - hub: %s - tag: %s -%s -`, s.Image.Hub, s.Image.Tag, data) -} - -// Indent indents a block of text with an indent string -func Indent(text, indent string) string { - if text[len(text)-1:] == "\n" { - result := "" - for _, j := range strings.Split(text[:len(text)-1], "\n") { - result += indent + j + "\n" - } - return result - } - result := "" - for _, j := range strings.Split(strings.TrimRight(text, "\n"), "\n") { - result += indent + j + "\n" - } - return result[:len(result)-1] -} - -// DefaultConfig creates a new Config from defaults, environments variables, and command-line parameters. -func DefaultConfig(ctx resource.Context) (Config, error) { - // Make a local copy. - s := *settingsFromCommandline - - iopFile := s.PrimaryClusterIOPFile - if iopFile != "" && !path.IsAbs(s.PrimaryClusterIOPFile) { - iopFile = filepath.Join(env.IstioSrc, s.PrimaryClusterIOPFile) - } - - if err := checkFileExists(iopFile); err != nil { - scopes.Framework.Warnf("Default IOPFile missing: %v", err) - } - - var err error - if s.Values, err = newHelmValues(ctx); err != nil { - return Config{}, err - } - - if s.OperatorOptions, err = parseConfigOptions(operatorOptions); err != nil { - return Config{}, err - } - - return s, nil -} - -// DefaultConfigOrFail calls DefaultConfig and fails t if an error occurs. -func DefaultConfigOrFail(t test.Failer, ctx resource.Context) Config { - cfg, err := DefaultConfig(ctx) - if err != nil { - t.Fatalf("Get istio config: %v", err) - } - return cfg -} - -func checkFileExists(path string) error { - if _, err := os.Stat(path); os.IsNotExist(err) { - return err - } - return nil -} - -func newHelmValues(ctx resource.Context) (map[string]string, error) { - userValues, err := parseConfigOptions(helmValues) - if err != nil { - return nil, err - } - - // Copy the defaults first. - values := make(map[string]string) - - // Common values - s := ctx.Settings() - values[hubValuesKey] = s.Image.Hub - values[tagValuesKey] = s.Image.Tag - values[imagePullPolicyValuesKey] = s.Image.PullPolicy - - // Copy the user values. - for k, v := range userValues { - values[k] = v - } - - // Always pull Docker images if using the "latest". - if values[tagValuesKey] == "latest" { - values[imagePullPolicyValuesKey] = string(kubeCore.PullAlways) - } - - // We need more information on Envoy logs to detect usage of any deprecated feature - if ctx.Settings().FailOnDeprecation { - values["global.proxy.logLevel"] = "debug" - values["global.proxy.componentLogLevel"] = "misc:debug" - } - - return values, nil -} - -func parseConfigOptions(options string) (map[string]string, error) { - out := make(map[string]string) - if options == "" { - return out, nil - } - - values := strings.Split(options, ",") - for _, v := range values { - parts := strings.Split(v, "=") - if len(parts) != 2 { - return nil, fmt.Errorf("failed parsing config options: %s", options) - } - out[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1]) - } - return out, nil -} - -// String implements fmt.Stringer -func (c *Config) String() string { - result := "" - - result += fmt.Sprintf("SystemNamespace: %s\n", c.SystemNamespace) - result += fmt.Sprintf("TelemetryNamespace: %s\n", c.TelemetryNamespace) - result += fmt.Sprintf("DeployIstio: %v\n", c.DeployIstio) - result += fmt.Sprintf("DeployEastWestGW: %v\n", c.DeployEastWestGW) - result += fmt.Sprintf("Values: %v\n", c.Values) - result += fmt.Sprintf("PrimaryClusterIOPFile: %s\n", c.PrimaryClusterIOPFile) - result += fmt.Sprintf("ConfigClusterIOPFile: %s\n", c.ConfigClusterIOPFile) - result += fmt.Sprintf("RemoteClusterIOPFile: %s\n", c.RemoteClusterIOPFile) - result += fmt.Sprintf("BaseIOPFile: %s\n", c.BaseIOPFile) - result += fmt.Sprintf("SkipWaitForValidationWebhook: %v\n", c.SkipWaitForValidationWebhook) - result += fmt.Sprintf("DumpKubernetesManifests: %v\n", c.DumpKubernetesManifests) - result += fmt.Sprintf("IstiodlessRemotes: %v\n", c.IstiodlessRemotes) - result += fmt.Sprintf("OperatorOptions: %v\n", c.OperatorOptions) - result += fmt.Sprintf("EnableCNI: %v\n", c.EnableCNI) - - return result -} - -// ClaimSystemNamespace retrieves the namespace for the Istio system components from the environment. -func ClaimSystemNamespace(ctx resource.Context) (namespace.Instance, error) { - istioCfg, err := DefaultConfig(ctx) - if err != nil { - return nil, err - } - nsCfg := namespace.Config{ - Prefix: istioCfg.SystemNamespace, - Inject: false, - } - return namespace.Claim(ctx, nsCfg) -} - -// ClaimSystemNamespaceOrFail calls ClaimSystemNamespace, failing the test if an error occurs. -func ClaimSystemNamespaceOrFail(t test.Failer, ctx resource.Context) namespace.Instance { - t.Helper() - i, err := ClaimSystemNamespace(ctx) - if err != nil { - t.Fatal(err) - } - return i -} diff --git a/pkg/test/framework/components/istio/eastwest.go b/pkg/test/framework/components/istio/eastwest.go deleted file mode 100644 index d84d2588f..000000000 --- a/pkg/test/framework/components/istio/eastwest.go +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istio - -import ( - "bytes" - "context" - "fmt" - "os" - "os/exec" - "path" - "path/filepath" -) - -import ( - corev1 "k8s.io/api/core/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/cmd/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/tmpl" -) - -var ( - mcSamples = path.Join(env.IstioSrc, "samples", "multicluster") - exposeIstiodGateway = path.Join(mcSamples, "expose-istiod.yaml") - exposeIstiodGatewayRev = path.Join(mcSamples, "expose-istiod-rev.yaml.tmpl") - exposeServicesGateway = path.Join(mcSamples, "expose-services.yaml") - genGatewayScript = path.Join(mcSamples, "gen-eastwest-gateway.sh") -) - -// deployEastWestGateway will create a separate gateway deployment for cross-cluster discovery or cross-network services. -func (i *operatorComponent) deployEastWestGateway(cluster cluster.Cluster, revision string) error { - // generate istio operator yaml - args := []string{ - "--cluster", cluster.Name(), - "--network", cluster.NetworkName(), - "--revision", revision, - "--mesh", meshID, - } - if !i.environment.IsMulticluster() { - args = []string{"--single-cluster"} - } - cmd := exec.Command(genGatewayScript, args...) - gwIOP, err := cmd.CombinedOutput() - if err != nil { - return fmt.Errorf("failed generating eastwestgateway operator yaml: %v: %v", err, string(gwIOP)) - } - iopFile := path.Join(i.workDir, fmt.Sprintf("eastwest-%s.yaml", cluster.Name())) - if err := os.WriteFile(iopFile, gwIOP, os.ModePerm); err != nil { - return err - } - - // Save the manifest generate output so we can later cleanup - s := i.ctx.Settings() - manifestGenArgs := &mesh.ManifestGenerateArgs{ - InFilenames: []string{iopFile}, - Set: []string{ - "hub=" + s.Image.Hub, - "tag=" + s.Image.Tag, - "values.global.imagePullPolicy=" + s.Image.PullPolicy, - "values.gateways.istio-ingressgateway.autoscaleEnabled=false", - }, - ManifestsPath: filepath.Join(env.IstioSrc, "manifests"), - Revision: revision, - } - - var stdOut, stdErr bytes.Buffer - if err := mesh.ManifestGenerate(&mesh.RootArgs{}, manifestGenArgs, cmdLogOptions(), cmdLogger(&stdOut, &stdErr)); err != nil { - return err - } - i.saveManifestForCleanup(cluster.Name(), stdOut.String()) - - kubeConfigFile, err := kubeConfigFileForCluster(cluster) - if err != nil { - return err - } - - installArgs := &mesh.InstallArgs{ - InFilenames: []string{iopFile}, - KubeConfigPath: kubeConfigFile, - Set: manifestGenArgs.Set, - ManifestsPath: manifestGenArgs.ManifestsPath, - Revision: manifestGenArgs.Revision, - } - - scopes.Framework.Infof("Deploying eastwestgateway in %s: %v", cluster.Name(), installArgs) - err = install(i, installArgs, cluster.Name()) - if err != nil { - scopes.Framework.Error(err) - return fmt.Errorf("failed installing eastwestgateway via IstioOperator: %v", err) - } - - // wait for a ready pod - if err := retry.UntilSuccess(func() error { - pods, err := cluster.CoreV1().Pods(i.settings.SystemNamespace).List(context.TODO(), v1.ListOptions{ - LabelSelector: "istio=" + eastWestIngressIstioLabel, - }) - if err != nil { - return err - } - for _, p := range pods.Items { - if p.Status.Phase == corev1.PodRunning { - return nil - } - } - return fmt.Errorf("no ready pods for istio=" + eastWestIngressIstioLabel) - }, componentDeployTimeout, componentDeployDelay); err != nil { - return fmt.Errorf("failed waiting for %s to become ready: %v", eastWestIngressServiceName, err) - } - - return nil -} - -func (i *operatorComponent) exposeUserServices(cluster cluster.Cluster) error { - scopes.Framework.Infof("Exposing services via eastwestgateway in %v", cluster.Name()) - return cluster.ApplyYAMLFiles(i.settings.SystemNamespace, exposeServicesGateway) -} - -func (i *operatorComponent) applyIstiodGateway(cluster cluster.Cluster, revision string) error { - scopes.Framework.Infof("Exposing istiod via eastwestgateway in %v", cluster.Name()) - if revision == "" { - return cluster.ApplyYAMLFiles(i.settings.SystemNamespace, exposeIstiodGateway) - } - gwTmpl, err := os.ReadFile(exposeIstiodGatewayRev) - if err != nil { - return fmt.Errorf("failed loading template %s: %v", exposeIstiodGatewayRev, err) - } - out, err := tmpl.Evaluate(string(gwTmpl), map[string]string{"Revision": revision}) - if err != nil { - return fmt.Errorf("failed running template %s: %v", exposeIstiodGatewayRev, err) - } - return i.ctx.ConfigKube(cluster).YAML(i.settings.SystemNamespace, out).Apply() -} diff --git a/pkg/test/framework/components/istio/flags.go b/pkg/test/framework/components/istio/flags.go deleted file mode 100644 index 18a104e26..000000000 --- a/pkg/test/framework/components/istio/flags.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istio - -import ( - "flag" -) - -// init registers the command-line flags that we can exposed for "go test". -func init() { - flag.StringVar(&settingsFromCommandline.SystemNamespace, "istio.test.kube.systemNamespace", settingsFromCommandline.SystemNamespace, - "Deprecated, specifies the namespace where the Istio components (<=1.1) reside in a typical deployment.") - flag.StringVar(&settingsFromCommandline.TelemetryNamespace, "istio.test.kube.telemetryNamespace", settingsFromCommandline.TelemetryNamespace, - "Specifies the namespace in which kiali, tracing providers, graphana, prometheus are deployed.") - flag.BoolVar(&settingsFromCommandline.DeployIstio, "istio.test.kube.deploy", settingsFromCommandline.DeployIstio, - "Deploy Istio into the target Kubernetes environment.") - flag.StringVar(&settingsFromCommandline.PrimaryClusterIOPFile, "istio.test.kube.helm.iopFile", settingsFromCommandline.PrimaryClusterIOPFile, - "IstioOperator spec file. This can be an absolute path or relative to repository root.") - flag.StringVar(&helmValues, "istio.test.kube.helm.values", helmValues, - "Manual overrides for Helm values file. Only valid when deploying Istio.") - flag.BoolVar(&settingsFromCommandline.DeployEastWestGW, "istio.test.kube.deployEastWestGW", settingsFromCommandline.DeployEastWestGW, - "Deploy Istio east west gateway into the target Kubernetes environment.") - flag.BoolVar(&settingsFromCommandline.DumpKubernetesManifests, "istio.test.istio.dumpManifests", settingsFromCommandline.DumpKubernetesManifests, - "Dump generated Istio install manifests in the artifacts directory.") - flag.BoolVar(&settingsFromCommandline.IstiodlessRemotes, "istio.test.istio.istiodlessRemotes", settingsFromCommandline.IstiodlessRemotes, - "Remote clusters run without istiod, using webhooks/ca from the primary cluster.") - flag.StringVar(&operatorOptions, "istio.test.istio.operatorOptions", operatorOptions, - `Comma separated operator configuration in addition to the default operator configuration. - e.g. components.cni.enabled=true,components.cni.namespace=kube-system`) - flag.BoolVar(&settingsFromCommandline.EnableCNI, "istio.test.istio.enableCNI", settingsFromCommandline.EnableCNI, - "Deploy Istio with CNI enabled.") -} diff --git a/pkg/test/framework/components/istio/ingress.go b/pkg/test/framework/components/istio/ingress.go deleted file mode 100644 index dfb5c4ec2..000000000 --- a/pkg/test/framework/components/istio/ingress.go +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istio - -import ( - "context" - "encoding/json" - "fmt" - "net" - "strconv" - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/http/headers" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo/common/scheme" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/common" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/environment/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/istio/ingress" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -const ( - defaultIngressIstioLabel = "ingressgateway" - defaultIngressServiceName = "istio-" + defaultIngressIstioLabel - - eastWestIngressIstioLabel = "eastwestgateway" - eastWestIngressServiceName = "istio-" + eastWestIngressIstioLabel - - proxyContainerName = "istio-proxy" - proxyAdminPort = 15000 - discoveryPort = 15012 -) - -var ( - getAddressTimeout = retry.Timeout(3 * time.Minute) - getAddressDelay = retry.BackoffDelay(500 * time.Millisecond) - - _ ingress.Instance = &ingressImpl{} -) - -type ingressConfig struct { - // ServiceName is the kubernetes Service name for the cluster - ServiceName string - // Namespace the ingress can be found in - Namespace string - // IstioLabel is the value for the "istio" label on the ingress kubernetes objects - IstioLabel string - - // Cluster to be used in a multicluster environment - Cluster cluster.Cluster -} - -func newIngress(ctx resource.Context, cfg ingressConfig) (i ingress.Instance) { - if cfg.ServiceName == "" { - cfg.ServiceName = defaultIngressServiceName - } - if cfg.IstioLabel == "" { - cfg.IstioLabel = defaultIngressIstioLabel - } - c := &ingressImpl{ - serviceName: cfg.ServiceName, - istioLabel: cfg.IstioLabel, - namespace: cfg.Namespace, - env: ctx.Environment().(*kube.Environment), - cluster: ctx.Clusters().GetOrDefault(cfg.Cluster), - } - return c -} - -type ingressImpl struct { - serviceName string - istioLabel string - namespace string - - env *kube.Environment - cluster cluster.Cluster -} - -// getAddressInner returns the external address for the given port. When we don't have support for LoadBalancer, -// the returned net.Addr will have the externally reachable NodePort address and port. -func (c *ingressImpl) getAddressInner(port int) (string, int, error) { - attempts := 0 - addr, err := retry.UntilComplete(func() (result interface{}, completed bool, err error) { - attempts++ - result, completed, err = getRemoteServiceAddress(c.env.Settings(), c.cluster, c.namespace, c.istioLabel, c.serviceName, port) - if err != nil && attempts > 1 { - // Log if we fail more than once to avoid test appearing to hang - // LB provision be slow, so timeout here needs to be long we should give context - scopes.Framework.Warnf("failed to get address for port %v: %v", port, err) - } - return - }, getAddressTimeout, getAddressDelay) - if err != nil { - return "", 0, err - } - - switch v := addr.(type) { - case string: - host, portStr, err := net.SplitHostPort(v) - if err != nil { - return "", 0, err - } - mappedPort, err := strconv.Atoi(portStr) - if err != nil { - return "", 0, err - } - return host, mappedPort, nil - case net.TCPAddr: - return v.IP.String(), v.Port, nil - } - - return "", 0, fmt.Errorf("failed to get address for port %v", port) -} - -// AddressForPort returns the externally reachable host and port of the component for the given port. -func (c *ingressImpl) AddressForPort(port int) (string, int) { - host, port, err := c.getAddressInner(port) - if err != nil { - scopes.Framework.Error(err) - return "", 0 - } - return host, port -} - -func (c *ingressImpl) Cluster() cluster.Cluster { - return c.cluster -} - -// HTTPAddress returns the externally reachable HTTP host and port (80) of the component. -func (c *ingressImpl) HTTPAddress() (string, int) { - return c.AddressForPort(80) -} - -// TCPAddress returns the externally reachable TCP host and port (31400) of the component. -func (c *ingressImpl) TCPAddress() (string, int) { - return c.AddressForPort(31400) -} - -// HTTPSAddress returns the externally reachable TCP host and port (443) of the component. -func (c *ingressImpl) HTTPSAddress() (string, int) { - return c.AddressForPort(443) -} - -// DiscoveryAddress returns the externally reachable discovery address (15012) of the component. -func (c *ingressImpl) DiscoveryAddress() net.TCPAddr { - host, port := c.AddressForPort(discoveryPort) - ip := net.ParseIP(host) - if ip.String() == "" { - // TODO support hostname based discovery address - return net.TCPAddr{} - } - return net.TCPAddr{IP: ip, Port: port} -} - -func (c *ingressImpl) Call(options echo.CallOptions) (echo.CallResult, error) { - return c.callEcho(options) -} - -func (c *ingressImpl) CallOrFail(t test.Failer, options echo.CallOptions) echo.CallResult { - t.Helper() - resp, err := c.Call(options) - if err != nil { - t.Fatal(err) - } - return resp -} - -func (c *ingressImpl) callEcho(opts echo.CallOptions) (echo.CallResult, error) { - var ( - addr string - port int - ) - opts = opts.DeepCopy() - - if opts.Port.ServicePort == 0 { - s, err := c.schemeFor(opts) - if err != nil { - return echo.CallResult{}, err - } - opts.Scheme = s - - // Default port based on protocol - switch s { - case scheme.HTTP: - addr, port = c.HTTPAddress() - case scheme.HTTPS: - addr, port = c.HTTPSAddress() - case scheme.TCP: - addr, port = c.TCPAddress() - default: - return echo.CallResult{}, fmt.Errorf("ingress: scheme %v not supported. Options: %v+", s, opts) - } - } else { - addr, port = c.AddressForPort(opts.Port.ServicePort) - } - - if addr == "" || port == 0 { - scopes.Framework.Warnf("failed to get host and port for %s/%d", opts.Port.Protocol, opts.Port.ServicePort) - } - - // Even if they set ServicePort, when load balancer is disabled, we may need to switch to NodePort, so replace it. - opts.Port.ServicePort = port - if len(opts.Address) == 0 { - // Default address based on port - opts.Address = addr - } - if opts.HTTP.Headers == nil { - opts.HTTP.Headers = map[string][]string{} - } - if host := opts.GetHost(); len(host) > 0 { - opts.HTTP.Headers.Set(headers.Host, host) - } - if len(c.cluster.HTTPProxy()) > 0 { - opts.HTTP.HTTPProxy = c.cluster.HTTPProxy() - } - return common.CallEcho(c, opts) -} - -func (c *ingressImpl) schemeFor(opts echo.CallOptions) (scheme.Instance, error) { - if opts.Scheme == "" && opts.Port.Protocol == "" { - return "", fmt.Errorf("must provide either protocol or scheme") - } - - if opts.Scheme != "" { - return opts.Scheme, nil - } - - return opts.Port.Scheme() -} - -func (c *ingressImpl) ProxyStats() (map[string]int, error) { - var stats map[string]int - statsJSON, err := c.adminRequest("stats?format=json") - if err != nil { - return stats, fmt.Errorf("failed to get response from admin port: %v", err) - } - return c.unmarshalStats(statsJSON) -} - -func (c *ingressImpl) PodID(i int) (string, error) { - pods, err := c.env.Clusters().Default().PodsForSelector(context.TODO(), c.namespace, "istio=ingressgateway") - if err != nil { - return "", fmt.Errorf("unable to get ingressImpl gateway stats: %v", err) - } - if i < 0 || i >= len(pods.Items) { - return "", fmt.Errorf("pod index out of boundary (%d): %d", len(pods.Items), i) - } - return pods.Items[i].Name, nil -} - -// adminRequest makes a call to admin port at ingress gateway proxy and returns error on request failure. -func (c *ingressImpl) adminRequest(path string) (string, error) { - pods, err := c.env.Clusters().Default().PodsForSelector(context.TODO(), c.namespace, "istio=ingressgateway") - if err != nil { - return "", fmt.Errorf("unable to get ingressImpl gateway stats: %v", err) - } - podNs, podName := pods.Items[0].Namespace, pods.Items[0].Name - // Exec onto the pod and make a curl request to the admin port - command := fmt.Sprintf("curl http://127.0.0.1:%d/%s", proxyAdminPort, path) - stdout, stderr, err := c.env.Clusters().Default().PodExec(podName, podNs, proxyContainerName, command) - return stdout + stderr, err -} - -type statEntry struct { - Name string `json:"name"` - Value json.Number `json:"value"` -} - -type stats struct { - StatList []statEntry `json:"stats"` -} - -// unmarshalStats unmarshals Envoy stats from JSON format into a map, where stats name is -// key, and stats value is value. -func (c *ingressImpl) unmarshalStats(statsJSON string) (map[string]int, error) { - statsMap := make(map[string]int) - - var statsArray stats - if err := json.Unmarshal([]byte(statsJSON), &statsArray); err != nil { - return statsMap, fmt.Errorf("unable to unmarshal stats from json: %v", err) - } - - for _, v := range statsArray.StatList { - if v.Value == "" { - continue - } - tmp, _ := v.Value.Float64() - statsMap[v.Name] = int(tmp) - } - return statsMap, nil -} - -func (c *ingressImpl) Namespace() string { - return c.namespace -} diff --git a/pkg/test/framework/components/istio/ingress/interface.go b/pkg/test/framework/components/istio/ingress/interface.go deleted file mode 100644 index 8876f275a..000000000 --- a/pkg/test/framework/components/istio/ingress/interface.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ingress - -import ( - "net" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo" -) - -type Instances []Instance - -func (i Instances) Callers() echo.Callers { - var out echo.Callers - for _, instance := range i { - out = append(out, instance) - } - return out -} - -// Instance represents a deployed Ingress Gateway instance. -type Instance interface { - echo.Caller - // HTTPAddress returns the external HTTP (80) address of the ingress gateway ((or the NodePort address, - // // when in an environment that doesn't support LoadBalancer). - HTTPAddress() (string, int) - // HTTPSAddress returns the external HTTPS (443) address of the ingress gateway (or the NodePort address, - // // when in an environment that doesn't support LoadBalancer). - HTTPSAddress() (string, int) - // TCPAddress returns the external TCP (31400) address of the ingress gateway (or the NodePort address, - // when in an environment that doesn't support LoadBalancer). - TCPAddress() (string, int) - // DiscoveryAddress returns the external XDS (!5012) address on the ingress gateway (or the NodePort address, - // when in an evnironment that doesn't support LoadBalancer). - DiscoveryAddress() net.TCPAddr - // AddressForPort returns the external address of the ingress gateway (or the NodePort address, - // when in an environment that doesn't support LoadBalancer) for the given port. - AddressForPort(port int) (string, int) - - // ProxyStats returns proxy stats, or error if failure happens. - ProxyStats() (map[string]int, error) - - // PodID returns the name of the ingress gateway pod of index i. Returns error if failed to get the pod - // or the index is out of boundary. - PodID(i int) (string, error) - - // Cluster the ingress is deployed to - Cluster() cluster.Cluster - - // Namespace of the ingress - Namespace() string -} diff --git a/pkg/test/framework/components/istio/istio.go b/pkg/test/framework/components/istio/istio.go deleted file mode 100644 index ad2396330..000000000 --- a/pkg/test/framework/components/istio/istio.go +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istio - -import ( - "net" - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/environment/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/istio/ingress" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" -) - -// Instance represents a deployed Istio instance -type Instance interface { - resource.Resource - - // Ingresses returns all ingresses for "istio-ingressgateway" in each cluster. - Ingresses() ingress.Instances - // IngressFor returns an ingress used for reaching workloads in the given cluster. - // The ingress's service name will be "istio-ingressgateway" and the istio label will be "ingressgateway". - IngressFor(cluster cluster.Cluster) ingress.Instance - // CustomIngressFor returns an ingress with a specific service name and "istio" label used for reaching workloads - // in the given cluster. - CustomIngressFor(cluster cluster.Cluster, serviceName, istioLabel string) ingress.Instance - - // RemoteDiscoveryAddressFor returns the external address of the discovery server that controls - // the given cluster. This allows access to the discovery server from - // outside its cluster. - RemoteDiscoveryAddressFor(cluster cluster.Cluster) (net.TCPAddr, error) - Settings() Config -} - -// SetupConfigFn is a setup function that specifies the overrides of the configuration to deploy Istio. -type SetupConfigFn func(ctx resource.Context, cfg *Config) - -// SetupContextFn is a setup function that uses Context for configuration. -type SetupContextFn func(ctx resource.Context) error - -// Get returns the Istio component from the context. If there is none an error is returned. -func Get(ctx resource.Context) (Instance, error) { - var i Instance - if err := ctx.GetResource(&i); err != nil { - return nil, err - } - return i, nil -} - -// GetOrFail returns the Istio component from the context. If there is none the test is failed. -func GetOrFail(t test.Failer, ctx resource.Context) Instance { - t.Helper() - i, err := Get(ctx) - if err != nil { - t.Fatal(err) - } - return i -} - -// DefaultIngress returns the ingress installed in the default cluster. The ingress's service name -// will be "istio-ingressgateway" and the istio label will be "ingressgateway". -func DefaultIngress(ctx resource.Context) (ingress.Instance, error) { - i, err := Get(ctx) - if err != nil { - return nil, err - } - return i.IngressFor(ctx.Clusters().Default()), nil -} - -// DefaultIngressOrFail calls DefaultIngress and fails if an error is encountered. -func DefaultIngressOrFail(t test.Failer, ctx resource.Context) ingress.Instance { - t.Helper() - i, err := DefaultIngress(ctx) - if err != nil { - t.Fatal(err) - } - return i -} - -// Ingresses returns all ingresses for "istio-ingressgateway" in each cluster. -func Ingresses(ctx resource.Context) (ingress.Instances, error) { - i, err := Get(ctx) - if err != nil { - return nil, err - } - return i.Ingresses(), nil -} - -// IngressesOrFail calls Ingresses and fails if an error is encountered. -func IngressesOrFail(t test.Failer, ctx resource.Context) ingress.Instances { - t.Helper() - i, err := Ingresses(ctx) - if err != nil { - t.Fatal(err) - } - return i -} - -// Setup is a setup function that will deploy Istio on Kubernetes environment -func Setup(i *Instance, cfn SetupConfigFn, ctxFns ...SetupContextFn) resource.SetupFn { - return func(ctx resource.Context) error { - cfg, err := DefaultConfig(ctx) - if err != nil { - return err - } - if cfn != nil { - cfn(ctx, &cfg) - } - for _, ctxFn := range ctxFns { - if ctxFn != nil { - err := ctxFn(ctx) - if err != nil { - scopes.Framework.Infof("=== FAILED: context setup function [err=%v] ===", err) - return err - } - scopes.Framework.Info("=== SUCCESS: context setup function ===") - } - } - - ins, err := Deploy(ctx, &cfg) - if err != nil { - return err - } - if i != nil { - *i = ins - } - - return nil - } -} - -// Deploy deploys (or attaches to) an Istio deployment and returns a handle. If cfg is nil, then DefaultConfig is used. -func Deploy(ctx resource.Context, cfg *Config) (Instance, error) { - if cfg == nil { - c, err := DefaultConfig(ctx) - if err != nil { - return nil, err - } - cfg = &c - } - - t0 := time.Now() - scopes.Framework.Infof("=== BEGIN: Deploy Istio [Suite=%s] ===", ctx.Settings().TestID) - - i, err := deploy(ctx, ctx.Environment().(*kube.Environment), *cfg) - if err != nil { - scopes.Framework.Infof("=== FAILED: Deploy Istio in %v [Suite=%s] ===", time.Since(t0), ctx.Settings().TestID) - } else { - scopes.Framework.Infof("=== SUCCEEDED: Deploy Istio in %v [Suite=%s]===", time.Since(t0), ctx.Settings().TestID) - } - return i, err -} diff --git a/pkg/test/framework/components/istio/operator.go b/pkg/test/framework/components/istio/operator.go deleted file mode 100644 index 60fe2b31c..000000000 --- a/pkg/test/framework/components/istio/operator.go +++ /dev/null @@ -1,1117 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istio - -import ( - "bytes" - "context" - "fmt" - "io" - "net" - "os" - "path" - "path/filepath" - "regexp" - "strconv" - "sync" - "time" -) - -import ( - "github.com/hashicorp/go-multierror" - "istio.io/api/label" - opAPI "istio.io/api/operator/v1alpha1" - "istio.io/pkg/log" - kubeApiCore "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - kubeApiMeta "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/cmd" - "github.com/apache/dubbo-go-pixiu/operator/cmd/mesh" - pkgAPI "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" - "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" - "github.com/apache/dubbo-go-pixiu/pkg/test/cert/ca" - testenv "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - kubecluster "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/environment/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/istio/ingress" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/istioctl" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - kube2 "github.com/apache/dubbo-go-pixiu/pkg/test/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/file" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/yml" -) - -// TODO: dynamically generate meshID to support multi-tenancy tests -const ( - meshID = "testmesh0" - istiodSvcName = "istiod" -) - -var ( - // the retry options for waiting for an individual component to be ready - componentDeployTimeout = retry.Timeout(1 * time.Minute) - componentDeployDelay = retry.BackoffDelay(200 * time.Millisecond) -) - -type operatorComponent struct { - id resource.ID - settings Config - ctx resource.Context - environment *kube.Environment - - mu sync.Mutex - // installManifest includes the yamls use to install Istio. These can be deleted on cleanup - // The key is the cluster name - installManifest map[string][]string - // ingress components, indexed first by cluster name and then by gateway name. - ingress map[string]map[string]ingress.Instance - workDir string -} - -var ( - _ io.Closer = &operatorComponent{} - _ Instance = &operatorComponent{} - _ resource.Dumper = &operatorComponent{} -) - -// ID implements resource.Instance -func (i *operatorComponent) ID() resource.ID { - return i.id -} - -func (i *operatorComponent) Settings() Config { - return i.settings -} - -func removeCRDsSlice(raw []string) string { - res := make([]string, 0) - for _, r := range raw { - res = append(res, removeCRDs(r)) - } - return yml.JoinString(res...) -} - -// When we cleanup, we should not delete CRDs. This will filter out all the crds -func removeCRDs(istioYaml string) string { - allParts := yml.SplitString(istioYaml) - nonCrds := make([]string, 0, len(allParts)) - - // Make the regular expression multi-line and anchor to the beginning of the line. - r := regexp.MustCompile(`(?m)^kind: CustomResourceDefinition$`) - - for _, p := range allParts { - if r.MatchString(p) { - continue - } - nonCrds = append(nonCrds, p) - } - - return yml.JoinString(nonCrds...) -} - -type istioctlConfigFiles struct { - iopFile string - operatorSpec *opAPI.IstioOperatorSpec - configIopFile string - configOperatorSpec *opAPI.IstioOperatorSpec - remoteIopFile string - remoteOperatorSpec *opAPI.IstioOperatorSpec -} - -func (i *operatorComponent) Ingresses() ingress.Instances { - var out ingress.Instances - for _, c := range i.ctx.Clusters().Kube() { - // call IngressFor in-case initialization is needed. - out = append(out, i.IngressFor(c)) - } - return out -} - -func (i *operatorComponent) IngressFor(c cluster.Cluster) ingress.Instance { - return i.CustomIngressFor(c, defaultIngressServiceName, defaultIngressIstioLabel) -} - -func (i *operatorComponent) CustomIngressFor(c cluster.Cluster, serviceName, istioLabel string) ingress.Instance { - i.mu.Lock() - defer i.mu.Unlock() - if c.Kind() != cluster.Kubernetes { - c = c.Primary() - } - - if i.ingress[c.Name()] == nil { - i.ingress[c.Name()] = map[string]ingress.Instance{} - } - if _, ok := i.ingress[c.Name()][istioLabel]; !ok { - i.ingress[c.Name()][istioLabel] = newIngress(i.ctx, ingressConfig{ - Namespace: i.settings.SystemNamespace, - Cluster: c, - ServiceName: serviceName, - IstioLabel: istioLabel, - }) - } - return i.ingress[c.Name()][istioLabel] -} - -func (i *operatorComponent) Close() error { - t0 := time.Now() - scopes.Framework.Infof("=== BEGIN: Cleanup Istio [Suite=%s] ===", i.ctx.Settings().TestID) - - // Write time spent for cleanup and deploy to ARTIFACTS/trace.yaml and logs to allow analyzing test times - defer func() { - delta := time.Since(t0) - i.ctx.RecordTraceEvent("istio-cleanup", delta.Seconds()) - scopes.Framework.Infof("=== SUCCEEDED: Cleanup Istio in %v [Suite=%s] ===", delta, i.ctx.Settings().TestID) - }() - - if i.settings.DumpKubernetesManifests { - i.dumpGeneratedManifests() - } - - if i.settings.DeployIstio { - errG := multierror.Group{} - // Make sure to clean up primary clusters before remotes, or istiod will recreate some of the CMs that we delete - // in the remote clusters before it's deleted. - for _, c := range i.ctx.AllClusters().Primaries().Kube() { - i.cleanupCluster(c, &errG) - } - for _, c := range i.ctx.Clusters().Remotes().Kube() { - i.cleanupCluster(c, &errG) - } - return errG.Wait().ErrorOrNil() - } - return nil -} - -func (i *operatorComponent) cleanupCluster(c cluster.Cluster, errG *multierror.Group) { - scopes.Framework.Infof("clean up cluster %s", c.Name()) - errG.Go(func() (err error) { - if e := i.ctx.ConfigKube(c).YAML("", removeCRDsSlice(i.installManifest[c.Name()])).Delete(); e != nil { - err = multierror.Append(err, e) - } - // Cleanup all secrets and configmaps - these are dynamically created by tests and/or istiod so they are not captured above - // This includes things like leader election locks (allowing next test to start without 30s delay), - // custom cacerts, custom kubeconfigs, etc. - // We avoid deleting the whole namespace since its extremely slow in Kubernetes (30-60s+) - if e := c.CoreV1().Secrets(i.settings.SystemNamespace).DeleteCollection( - context.Background(), kubeApiMeta.DeleteOptions{}, kubeApiMeta.ListOptions{}); e != nil { - err = multierror.Append(err, e) - } - if e := c.CoreV1().ConfigMaps(i.settings.SystemNamespace).DeleteCollection( - context.Background(), kubeApiMeta.DeleteOptions{}, kubeApiMeta.ListOptions{}); e != nil { - err = multierror.Append(err, e) - } - // Delete validating and mutating webhook configurations. These can be created outside of generated manifests - // when installing with istioctl and must be deleted separately. - if e := c.AdmissionregistrationV1().ValidatingWebhookConfigurations().DeleteCollection( - context.Background(), kubeApiMeta.DeleteOptions{}, kubeApiMeta.ListOptions{}); e != nil { - err = multierror.Append(err, e) - } - if e := c.AdmissionregistrationV1().MutatingWebhookConfigurations().DeleteCollection( - context.Background(), kubeApiMeta.DeleteOptions{}, kubeApiMeta.ListOptions{}); e != nil { - err = multierror.Append(err, e) - } - return - }) -} - -func (i *operatorComponent) dumpGeneratedManifests() { - manifestsDir := path.Join(i.workDir, "manifests") - if err := os.Mkdir(manifestsDir, 0o700); err != nil { - scopes.Framework.Errorf("Unable to create directory for dumping install manifests: %v", err) - return - } - for clusterName, manifests := range i.installManifest { - clusterDir := path.Join(manifestsDir, clusterName) - if err := os.Mkdir(manifestsDir, 0o700); err != nil { - scopes.Framework.Errorf("Unable to create directory for dumping %s install manifests: %v", clusterName, err) - return - } - for i, manifest := range manifests { - err := os.WriteFile(path.Join(clusterDir, "manifest-"+strconv.Itoa(i)+".yaml"), []byte(manifest), 0o644) - if err != nil { - scopes.Framework.Errorf("Failed writing manifest %d/%d in %s: %v", i, len(manifests)-1, clusterName, err) - } - } - } -} - -func (i *operatorComponent) Dump(ctx resource.Context) { - scopes.Framework.Errorf("=== Dumping Istio Deployment State...") - ns := i.settings.SystemNamespace - d, err := ctx.CreateTmpDirectory("istio-state") - if err != nil { - scopes.Framework.Errorf("Unable to create directory for dumping Istio contents: %v", err) - return - } - kube2.DumpPods(ctx, d, ns, []string{}) - kube2.DumpWebhooks(ctx, d) - for _, c := range ctx.Clusters().Kube() { - kube2.DumpDebug(ctx, c, d, "configz") - kube2.DumpDebug(ctx, c, d, "mcsz") - kube2.DumpDebug(ctx, c, d, "clusterz") - } - // Dump istio-cni. - kube2.DumpPods(ctx, d, "kube-system", []string{"k8s-app=istio-cni-node"}) -} - -// saveManifestForCleanup will ensure we delete the given yaml from the given cluster during cleanup. -func (i *operatorComponent) saveManifestForCleanup(clusterName string, yaml string) { - i.mu.Lock() - defer i.mu.Unlock() - i.installManifest[clusterName] = append(i.installManifest[clusterName], yaml) -} - -func deploy(ctx resource.Context, env *kube.Environment, cfg Config) (Instance, error) { - i := &operatorComponent{ - environment: env, - settings: cfg, - ctx: ctx, - installManifest: map[string][]string{}, - ingress: map[string]map[string]ingress.Instance{}, - } - if i.isExternalControlPlane() { - cfg.PrimaryClusterIOPFile = IntegrationTestExternalIstiodPrimaryDefaultsIOP - cfg.ConfigClusterIOPFile = IntegrationTestExternalIstiodConfigDefaultsIOP - i.settings = cfg - } else if !cfg.IstiodlessRemotes { - cfg.RemoteClusterIOPFile = IntegrationTestDefaultsIOP - i.settings = cfg - } - - scopes.Framework.Infof("=== Istio Component Config ===") - scopes.Framework.Infof("\n%s", cfg.String()) - scopes.Framework.Infof("================================") - - t0 := time.Now() - defer func() { - ctx.RecordTraceEvent("istio-deploy", time.Since(t0).Seconds()) - }() - i.id = ctx.TrackResource(i) - - if !cfg.DeployIstio { - scopes.Framework.Info("skipping deployment as specified in the config") - return i, nil - } - - // Top-level work dir for Istio deployment. - workDir, err := ctx.CreateTmpDirectory("istio-deployment") - if err != nil { - return nil, err - } - i.workDir = workDir - - // generate common IstioOperator yamls for different cluster types (primary, remote, remote-config) - istioctlConfigFiles, err := createIstioctlConfigFile(ctx.Settings(), workDir, cfg) - if err != nil { - return nil, err - } - - // For multicluster, create and push the CA certs to all clusters to establish a shared root of trust. - if env.IsMulticluster() { - if err := deployCACerts(workDir, env, cfg); err != nil { - return nil, err - } - } - - // First install remote-config clusters. - // We do this first because the external istiod needs to read the config cluster at startup. - s := ctx.Settings() - for _, c := range ctx.Clusters().Kube().Configs().Remotes() { - if err = installConfigCluster(s, i, cfg, c, istioctlConfigFiles.configIopFile); err != nil { - return i, err - } - } - - // Install control plane clusters (can be external or primary). - errG := multierror.Group{} - for _, c := range ctx.AllClusters().Kube().Primaries() { - c := c - errG.Go(func() error { - return installControlPlaneCluster(s, i, cfg, c, istioctlConfigFiles.iopFile, istioctlConfigFiles.operatorSpec) - }) - } - if err := errG.Wait().ErrorOrNil(); err != nil { - scopes.Framework.Errorf("one or more errors occurred installing control-plane clusters: %v", err) - return i, err - } - - // For multicluster, configure direct access so each control plane can get endpoints from all API servers. - // This needs to be done before installing remote clusters to accommodate non-istiodless remote cluster - // that use the default profile, which installs gateways right away and will fail if the control plane - // isn't responding. - if ctx.Clusters().IsMulticluster() { - if err := i.configureDirectAPIServerAccess(ctx, cfg); err != nil { - return nil, err - } - } - - // Install (non-config) remote clusters. - errG = multierror.Group{} - for _, c := range ctx.Clusters().Kube().Remotes(ctx.Clusters().Configs()...) { - c := c - errG.Go(func() error { - if err := installRemoteCluster(s, i, cfg, c, istioctlConfigFiles.remoteIopFile); err != nil { - return fmt.Errorf("failed installing remote cluster %s: %v", c.Name(), err) - } - return nil - }) - } - if errs := errG.Wait(); errs != nil { - return nil, fmt.Errorf("%d errors occurred deploying remote clusters: %v", errs.Len(), errs.ErrorOrNil()) - } - - // Configure gateways for remote clusters. - for _, c := range ctx.Clusters().Kube().Remotes() { - c := c - if i.isExternalControlPlane() || cfg.IstiodlessRemotes { - if err = configureRemoteClusterDiscovery(i, cfg, c); err != nil { - return i, err - } - - // Install ingress and egress gateways - // These need to be installed as a separate step for external control planes because config clusters are installed - // before the external control plane cluster. Since remote clusters use gateway injection, we can't install the gateways - // until after the control plane is running, so we install them here. This is not really necessary for pure (non-config) - // remote clusters, but it's cleaner to just install gateways as a separate step for all remote clusters. - if err = installRemoteClusterGateways(s, i, c); err != nil { - return i, err - } - } - - // remote clusters only need east-west gateway for multi-network purposes - if ctx.Environment().IsMultinetwork() { - spec := istioctlConfigFiles.remoteOperatorSpec - if c.IsConfig() { - spec = istioctlConfigFiles.configOperatorSpec - } - if err := i.deployEastWestGateway(c, spec.Revision); err != nil { - return i, err - } - - // Wait for the eastwestgateway to have a public IP. - _ = i.CustomIngressFor(c, eastWestIngressServiceName, eastWestIngressIstioLabel).DiscoveryAddress() - } - } - - if env.IsMultinetwork() { - // enable cross network traffic - for _, c := range ctx.Clusters().Kube().Configs() { - if err := i.exposeUserServices(c); err != nil { - return nil, err - } - } - } - - return i, nil -} - -// patchIstiodCustomHost sets the ISTIOD_CUSTOM_HOST to the given address, -// to allow webhook connections to succeed when reaching webhook by IP. -func patchIstiodCustomHost(istiodAddress net.TCPAddr, cfg Config, c cluster.Cluster) error { - scopes.Framework.Infof("patching custom host for cluster %s as %s", c.Name(), istiodAddress.IP.String()) - patchOptions := kubeApiMeta.PatchOptions{ - FieldManager: "istio-ci", - TypeMeta: kubeApiMeta.TypeMeta{ - Kind: "Deployment", - APIVersion: "apps/v1", - }, - } - contents := fmt.Sprintf(` -apiVersion: apps/v1 -kind: Deployment -spec: - template: - spec: - containers: - - name: discovery - env: - - name: ISTIOD_CUSTOM_HOST - value: %s -`, istiodAddress.IP.String()) - if _, err := c.AppsV1().Deployments(cfg.SystemNamespace).Patch(context.TODO(), "istiod", types.ApplyPatchType, - []byte(contents), patchOptions); err != nil { - return fmt.Errorf("failed to patch istiod with ISTIOD_CUSTOM_HOST: %v", err) - } - - if err := retry.UntilSuccess(func() error { - pods, err := c.CoreV1().Pods(cfg.SystemNamespace).List(context.TODO(), kubeApiMeta.ListOptions{LabelSelector: "app=istiod"}) - if err != nil { - return err - } - if len(pods.Items) == 0 { - return fmt.Errorf("no istiod pods") - } - for _, p := range pods.Items { - for _, c := range p.Spec.Containers { - if c.Name != "discovery" { - continue - } - found := false - for _, envVar := range c.Env { - if envVar.Name == "ISTIOD_CUSTOM_HOST" { - found = true - break - } - } - if !found { - return fmt.Errorf("%v does not have ISTIOD_CUSTOM_HOST set", p.Name) - } - } - } - return nil - }, componentDeployTimeout, componentDeployDelay); err != nil { - return fmt.Errorf("failed waiting for patched istiod pod to come up in %s: %v", c.Name(), err) - } - - return nil -} - -func initIOPFile(s *resource.Settings, cfg Config, iopFile string, valuesYaml string) (*opAPI.IstioOperatorSpec, error) { - operatorYaml := cfg.IstioOperatorConfigYAML(s, valuesYaml) - - operatorCfg := &pkgAPI.IstioOperator{} - if err := yaml.Unmarshal([]byte(operatorYaml), operatorCfg); err != nil { - return nil, fmt.Errorf("failed to unmarshal base iop: %v, %v", err, operatorYaml) - } - - // marshaling entire operatorCfg causes panic because of *time.Time in ObjectMeta - outb, err := yaml.Marshal(operatorCfg.Spec) - if err != nil { - return nil, fmt.Errorf("failed marshaling iop spec: %v", err) - } - - out := fmt.Sprintf(` -apiVersion: install.istio.io/v1alpha1 -kind: IstioOperator -spec: -%s`, Indent(string(outb), " ")) - - if err := os.WriteFile(iopFile, []byte(out), os.ModePerm); err != nil { - return nil, fmt.Errorf("failed to write iop: %v", err) - } - - return operatorCfg.Spec, nil -} - -// installControlPlaneCluster installs the istiod control plane to the given cluster. -// The cluster is considered a "primary" cluster if it is also a "config cluster", in which case components -// like ingress will be installed. -func installControlPlaneCluster(s *resource.Settings, i *operatorComponent, cfg Config, c cluster.Cluster, iopFile string, - spec *opAPI.IstioOperatorSpec) error { - scopes.Framework.Infof("setting up %s as control-plane cluster", c.Name()) - - if !c.IsConfig() { - if err := i.configureRemoteConfigForControlPlane(c); err != nil { - return err - } - } - installArgs, err := i.generateCommonInstallArgs(s, cfg, c, cfg.PrimaryClusterIOPFile, iopFile) - if err != nil { - return err - } - - if i.environment.IsMulticluster() { - if i.isExternalControlPlane() || cfg.IstiodlessRemotes { - // Enable namespace controller writing to remote clusters - installArgs.Set = append(installArgs.Set, "values.pilot.env.EXTERNAL_ISTIOD=true") - } - - // Set the clusterName for the local cluster. - // This MUST match the clusterName in the remote secret for this cluster. - clusterName := c.Name() - if !c.IsConfig() { - clusterName = c.ConfigName() - } - installArgs.Set = append(installArgs.Set, "values.global.multiCluster.clusterName="+clusterName) - } - - err = install(i, installArgs, c.Name()) - if err != nil { - return err - } - - if c.IsConfig() { - // this is a traditional primary cluster, install the eastwest gateway - - // there are a few tests that require special gateway setup which will cause eastwest gateway fail to start - // exclude these tests from installing eastwest gw for now - if !cfg.DeployEastWestGW { - return nil - } - - if err := i.deployEastWestGateway(c, spec.Revision); err != nil { - return err - } - // Other clusters should only use this for discovery if its a config cluster. - if err := i.applyIstiodGateway(c, spec.Revision); err != nil { - return fmt.Errorf("failed applying istiod gateway for cluster %s: %v", c.Name(), err) - } - if err := waitForIstioReady(i.ctx, c, cfg); err != nil { - return err - } - } - - if !c.IsConfig() || settingsFromCommandline.IstiodlessRemotes { - // patch the ISTIOD_CUSTOM_HOST to allow using the webhook by IP (this is something we only do in tests) - - // fetch after installing eastwest (for external istiod, this will be loadbalancer address of istiod directly) - istiodAddress, err := i.RemoteDiscoveryAddressFor(c) - if err != nil { - return err - } - - // TODO generate & install a valid cert in CI - if err := patchIstiodCustomHost(istiodAddress, cfg, c); err != nil { - return err - } - - // configure istioctl to run with an external control plane topology. - if !c.IsConfig() { - _ = os.Setenv("ISTIOCTL_XDS_ADDRESS", istiodAddress.String()) - _ = os.Setenv("ISTIOCTL_PREFER_EXPERIMENTAL", "true") - if err := cmd.ConfigAndEnvProcessing(); err != nil { - return err - } - } - } - - return nil -} - -// installConfigCluster installs istio to a cluster that runs workloads and provides Istio configuration. -// The installed components include CRDs, Roles, etc. but not istiod. -func installConfigCluster(s *resource.Settings, i *operatorComponent, cfg Config, c cluster.Cluster, configIopFile string) error { - scopes.Framework.Infof("setting up %s as config cluster", c.Name()) - return installRemoteCommon(s, i, cfg, c, cfg.ConfigClusterIOPFile, configIopFile) -} - -// installRemoteCluster installs istio to a remote cluster that does not also serve as a config cluster. -func installRemoteCluster(s *resource.Settings, i *operatorComponent, cfg Config, c cluster.Cluster, remoteIopFile string) error { - scopes.Framework.Infof("setting up %s as remote cluster", c.Name()) - return installRemoteCommon(s, i, cfg, c, cfg.RemoteClusterIOPFile, remoteIopFile) -} - -// Common install on a either a remote-config or pure remote cluster. -func installRemoteCommon(s *resource.Settings, i *operatorComponent, cfg Config, c cluster.Cluster, defaultsIOPFile, iopFile string) error { - installArgs, err := i.generateCommonInstallArgs(s, cfg, c, defaultsIOPFile, iopFile) - if err != nil { - return err - } - if i.environment.IsMulticluster() { - // Set the clusterName for the local cluster. - // This MUST match the clusterName in the remote secret for this cluster. - installArgs.Set = append(installArgs.Set, "values.global.multiCluster.clusterName="+c.Name()) - } - - // Configure the cluster and network arguments to pass through the injector webhook. - if i.isExternalControlPlane() { - installArgs.Set = append(installArgs.Set, - fmt.Sprintf("values.istiodRemote.injectionPath=/inject/net/%s/cluster/%s", c.NetworkName(), c.Name())) - } else { - remoteIstiodAddress, err := i.RemoteDiscoveryAddressFor(c) - if err != nil { - return err - } - installArgs.Set = append(installArgs.Set, "values.global.remotePilotAddress="+remoteIstiodAddress.IP.String()) - if cfg.IstiodlessRemotes { - installArgs.Set = append(installArgs.Set, - fmt.Sprintf("values.istiodRemote.injectionURL=https://%s/inject/net/%s/cluster/%s", - net.JoinHostPort(remoteIstiodAddress.IP.String(), "15017"), c.NetworkName(), c.Name())) - } - } - - if err := install(i, installArgs, c.Name()); err != nil { - return err - } - - return nil -} - -func installRemoteClusterGateways(s *resource.Settings, i *operatorComponent, c cluster.Cluster) error { - kubeConfigFile, err := kubeConfigFileForCluster(c) - if err != nil { - return err - } - - installArgs := &mesh.InstallArgs{ - KubeConfigPath: kubeConfigFile, - ManifestsPath: filepath.Join(testenv.IstioSrc, "manifests"), - InFilenames: []string{ - filepath.Join(testenv.IstioSrc, IntegrationTestRemoteGatewaysIOP), - }, - Set: []string{ - "values.global.imagePullPolicy=" + s.Image.PullPolicy, - }, - } - - scopes.Framework.Infof("Deploying ingress and egress gateways in %s: %v", c.Name(), installArgs) - if err = install(i, installArgs, c.Name()); err != nil { - return err - } - - return nil -} - -func kubeConfigFileForCluster(c cluster.Cluster) (string, error) { - type Filenamer interface { - Filename() string - } - fn, ok := c.(Filenamer) - if !ok { - return "", fmt.Errorf("cluster does not support fetching kube config") - } - return fn.Filename(), nil -} - -func (i *operatorComponent) generateCommonInstallArgs(s *resource.Settings, cfg Config, c cluster.Cluster, defaultsIOPFile, - iopFile string) (*mesh.InstallArgs, error) { - kubeConfigFile, err := kubeConfigFileForCluster(c) - if err != nil { - return nil, err - } - - if !path.IsAbs(defaultsIOPFile) { - defaultsIOPFile = filepath.Join(testenv.IstioSrc, defaultsIOPFile) - } - baseIOP := cfg.BaseIOPFile - if !path.IsAbs(baseIOP) { - baseIOP = filepath.Join(testenv.IstioSrc, baseIOP) - } - - installArgs := &mesh.InstallArgs{ - KubeConfigPath: kubeConfigFile, - ManifestsPath: filepath.Join(testenv.IstioSrc, "manifests"), - InFilenames: []string{ - baseIOP, - defaultsIOPFile, - iopFile, - }, - Set: []string{ - "values.global.imagePullPolicy=" + s.Image.PullPolicy, - }, - } - - if i.environment.IsMultinetwork() && c.NetworkName() != "" { - installArgs.Set = append(installArgs.Set, - "values.global.meshID="+meshID, - "values.global.network="+c.NetworkName()) - } - - // Include all user-specified values and configuration options. - if cfg.EnableCNI { - installArgs.Set = append(installArgs.Set, - "components.cni.namespace=kube-system", - "components.cni.enabled=true") - } - - // Include all user-specified values. - for k, v := range cfg.Values { - installArgs.Set = append(installArgs.Set, fmt.Sprintf("values.%s=%s", k, v)) - } - - for k, v := range cfg.OperatorOptions { - installArgs.Set = append(installArgs.Set, fmt.Sprintf("%s=%s", k, v)) - } - return installArgs, nil -} - -// install will replace and reconcile the installation based on the given install settings -func install(c *operatorComponent, installArgs *mesh.InstallArgs, clusterName string) error { - var stdOut, stdErr bytes.Buffer - if err := mesh.ManifestGenerate(&mesh.RootArgs{}, &mesh.ManifestGenerateArgs{ - InFilenames: installArgs.InFilenames, - Set: installArgs.Set, - Force: installArgs.Force, - ManifestsPath: installArgs.ManifestsPath, - Revision: installArgs.Revision, - }, cmdLogOptions(), cmdLogger(&stdOut, &stdErr)); err != nil { - return err - } - c.saveManifestForCleanup(clusterName, stdOut.String()) - - // Actually run the install command - installArgs.SkipConfirmation = true - - scopes.Framework.Infof("Installing Istio components on cluster %s %s", clusterName, installArgs) - stdOut.Reset() - stdErr.Reset() - if err := mesh.Install(&mesh.RootArgs{}, installArgs, cmdLogOptions(), &stdOut, - cmdLogger(&stdOut, &stdErr), - mesh.NewPrinterForWriter(&stdOut)); err != nil { - return fmt.Errorf("install failed: %v: %s", err, &stdErr) - } - return nil -} - -func cmdLogOptions() *log.Options { - o := log.DefaultOptions() - - // These scopes are, at the default "INFO" level, too chatty for command line use - o.SetOutputLevel("validation", log.ErrorLevel) - o.SetOutputLevel("processing", log.ErrorLevel) - o.SetOutputLevel("analysis", log.WarnLevel) - o.SetOutputLevel("installer", log.WarnLevel) - o.SetOutputLevel("translator", log.WarnLevel) - o.SetOutputLevel("adsc", log.WarnLevel) - o.SetOutputLevel("default", log.WarnLevel) - o.SetOutputLevel("klog", log.WarnLevel) - o.SetOutputLevel("kube", log.ErrorLevel) - - return o -} - -func cmdLogger(stdOut, stdErr io.Writer) clog.Logger { - return clog.NewConsoleLogger(stdOut, stdErr, scopes.Framework) -} - -func waitForIstioReady(ctx resource.Context, c cluster.Cluster, cfg Config) error { - if !cfg.SkipWaitForValidationWebhook { - // Wait for webhook to come online. The only reliable way to do that is to see if we can submit invalid config. - if err := waitForValidationWebhook(ctx, c, cfg); err != nil { - return err - } - } - return nil -} - -func (i *operatorComponent) configureDirectAPIServerAccess(ctx resource.Context, cfg Config) error { - // Configure direct access for each control plane to each APIServer. This allows each control plane to - // automatically discover endpoints in remote clusters. - for _, c := range ctx.Clusters().Kube() { - if err := i.configureDirectAPIServiceAccessForCluster(ctx, cfg, c); err != nil { - return err - } - } - return nil -} - -func (i *operatorComponent) configureDirectAPIServiceAccessForCluster(ctx resource.Context, cfg Config, - c cluster.Cluster) error { - clusters := ctx.Clusters().Configs(c) - if len(clusters) == 0 { - // giving 0 clusters to ctx.ConfigKube() means using all clusters - return nil - } - // Create a secret. - secret, err := CreateRemoteSecret(ctx, c, cfg) - if err != nil { - return fmt.Errorf("failed creating remote secret for cluster %s: %v", c.Name(), err) - } - if err := ctx.ConfigKube(clusters...).YAML(cfg.SystemNamespace, secret).Apply(resource.NoCleanup); err != nil { - return fmt.Errorf("failed applying remote secret to clusters: %v", err) - } - return nil -} - -func CreateRemoteSecret(ctx resource.Context, c cluster.Cluster, cfg Config, opts ...string) (string, error) { - istioCtl, err := istioctl.New(ctx, istioctl.Config{ - Cluster: c, - }) - if err != nil { - return "", err - } - cmd := []string{ - "create-remote-secret", - "--name", c.Name(), - "--namespace", cfg.SystemNamespace, - "--manifests", filepath.Join(testenv.IstioSrc, "manifests"), - } - cmd = append(cmd, opts...) - - scopes.Framework.Infof("Creating remote secret for cluster %s %v", c.Name(), cmd) - out, _, err := istioCtl.Invoke(cmd) - if err != nil { - return "", fmt.Errorf("create remote secret failed for cluster %s: %v", c.Name(), err) - } - return out, nil -} - -func deployCACerts(workDir string, env *kube.Environment, cfg Config) error { - certsDir := filepath.Join(workDir, "cacerts") - if err := os.Mkdir(certsDir, 0o700); err != nil { - return err - } - - root, err := ca.NewRoot(certsDir) - if err != nil { - return fmt.Errorf("failed creating the root CA: %v", err) - } - - for _, c := range env.Clusters() { - // Create a subdir for the cluster certs. - clusterDir := filepath.Join(certsDir, c.Name()) - if err := os.Mkdir(clusterDir, 0o700); err != nil { - return err - } - - // Create the new extensions config for the CA - caConfig, err := ca.NewIstioConfig(cfg.SystemNamespace) - if err != nil { - return err - } - - // Create the certs for the cluster. - clusterCA, err := ca.NewIntermediate(clusterDir, caConfig, root) - if err != nil { - return fmt.Errorf("failed creating intermediate CA for cluster %s: %v", c.Name(), err) - } - - // Create the CA secret for this cluster. Istio will use these certs for its CA rather - // than its autogenerated self-signed root. - secret, err := clusterCA.NewIstioCASecret() - if err != nil { - return fmt.Errorf("failed creating intermediate CA secret for cluster %s: %v", c.Name(), err) - } - - // Create the system namespace. - var nsLabels map[string]string - if env.IsMultinetwork() { - nsLabels = map[string]string{label.TopologyNetwork.Name: c.NetworkName()} - } - if _, err := c.CoreV1().Namespaces().Create(context.TODO(), &kubeApiCore.Namespace{ - ObjectMeta: kubeApiMeta.ObjectMeta{ - Labels: nsLabels, - Name: cfg.SystemNamespace, - }, - }, kubeApiMeta.CreateOptions{}); err != nil { - if errors.IsAlreadyExists(err) { - if _, err := c.CoreV1().Namespaces().Update(context.TODO(), &kubeApiCore.Namespace{ - ObjectMeta: kubeApiMeta.ObjectMeta{ - Labels: nsLabels, - Name: cfg.SystemNamespace, - }, - }, kubeApiMeta.UpdateOptions{}); err != nil { - scopes.Framework.Errorf("failed updating namespace %s on cluster %s. This can happen when deploying "+ - "multiple control planes. Error: %v", cfg.SystemNamespace, c.Name(), err) - } - } else { - scopes.Framework.Errorf("failed creating namespace %s on cluster %s. This can happen when deploying "+ - "multiple control planes. Error: %v", cfg.SystemNamespace, c.Name(), err) - } - } - - // Create the secret for the cacerts. - if _, err := c.CoreV1().Secrets(cfg.SystemNamespace).Create(context.TODO(), secret, - kubeApiMeta.CreateOptions{}); err != nil { - if errors.IsAlreadyExists(err) { - if _, err := c.CoreV1().Secrets(cfg.SystemNamespace).Update(context.TODO(), secret, - kubeApiMeta.UpdateOptions{}); err != nil { - scopes.Framework.Errorf("failed to create CA secrets on cluster %s. This can happen when deploying "+ - "multiple control planes. Error: %v", c.Name(), err) - } - } else { - scopes.Framework.Errorf("failed to create CA secrets on cluster %s. This can happen when deploying "+ - "multiple control planes. Error: %v", c.Name(), err) - } - } - } - return nil -} - -// configureRemoteClusterDiscovery creates a local istiod Service and Endpoints pointing to the external control plane. -// This is used to configure the remote cluster webhooks in the test environment. -// In a production deployment, the external istiod would be configured using proper DNS+certs instead. -func configureRemoteClusterDiscovery(i *operatorComponent, cfg Config, c cluster.Cluster) error { - discoveryAddress, err := i.RemoteDiscoveryAddressFor(c) - if err != nil { - return err - } - - discoveryIP := discoveryAddress.IP.String() - - scopes.Framework.Infof("creating endpoints and service in %s to get discovery from %s", c.Name(), discoveryIP) - svc := &kubeApiCore.Service{ - ObjectMeta: kubeApiMeta.ObjectMeta{ - Name: istiodSvcName, - Namespace: cfg.SystemNamespace, - }, - Spec: kubeApiCore.ServiceSpec{ - Ports: []kubeApiCore.ServicePort{ - { - Port: 15012, - Name: "tls-istiod", - Protocol: kubeApiCore.ProtocolTCP, - }, - { - Name: "tls-webhook", - Protocol: kubeApiCore.ProtocolTCP, - Port: 443, - }, - { - Name: "tls", - Protocol: kubeApiCore.ProtocolTCP, - Port: 15443, - }, - }, - }, - } - if _, err = c.CoreV1().Services(cfg.SystemNamespace).Create(context.TODO(), svc, kubeApiMeta.CreateOptions{}); err != nil { - // Ignore if service already exists. An update requires additional metadata. - if !errors.IsAlreadyExists(err) { - scopes.Framework.Errorf("failed to create services: %v", err) - return err - } - } - - eps := &kubeApiCore.Endpoints{ - ObjectMeta: kubeApiMeta.ObjectMeta{ - Name: istiodSvcName, - Namespace: cfg.SystemNamespace, - }, - Subsets: []kubeApiCore.EndpointSubset{ - { - Addresses: []kubeApiCore.EndpointAddress{ - { - IP: discoveryIP, - }, - }, - Ports: []kubeApiCore.EndpointPort{ - { - Name: "tls-istiod", - Protocol: kubeApiCore.ProtocolTCP, - Port: 15012, - }, - { - Name: "tls-webhook", - Protocol: kubeApiCore.ProtocolTCP, - Port: 443, - }, - }, - }, - }, - } - - if _, err = c.CoreV1().Endpoints(cfg.SystemNamespace).Create(context.TODO(), eps, kubeApiMeta.CreateOptions{}); err != nil { - if errors.IsAlreadyExists(err) { - if _, err = c.CoreV1().Endpoints(cfg.SystemNamespace).Update(context.TODO(), eps, kubeApiMeta.UpdateOptions{}); err != nil { - scopes.Framework.Errorf("failed to update endpoints: %v", err) - return err - } - } else { - scopes.Framework.Errorf("failed to create endpoints: %v", err) - return err - } - } - - err = retry.UntilSuccess(func() error { - _, err := c.CoreV1().Services(cfg.SystemNamespace).Get(context.TODO(), istiodSvcName, kubeApiMeta.GetOptions{}) - if err != nil { - return err - } - _, err = c.CoreV1().Endpoints(cfg.SystemNamespace).Get(context.TODO(), istiodSvcName, kubeApiMeta.GetOptions{}) - if err != nil { - return err - } - return nil - }, componentDeployTimeout, componentDeployDelay) - return err -} - -// configureRemoteConfigForControlPlane allows istiod in the given external control plane to read resources -// in its remote config cluster by creating the kubeconfig secret pointing to the remote kubeconfig, and the -// service account required to read the secret. -func (i *operatorComponent) configureRemoteConfigForControlPlane(c cluster.Cluster) error { - cfg := i.settings - configCluster := c.Config() - istioKubeConfig, err := file.AsString(configCluster.(*kubecluster.Cluster).Filename()) - if err != nil { - scopes.Framework.Infof("error in parsing kubeconfig for %s", configCluster.Name()) - return err - } - - scopes.Framework.Infof("configuring external control plane in %s to use config cluster %s", c.Name(), configCluster.Name()) - // ensure system namespace exists - if _, err = c.CoreV1().Namespaces(). - Create(context.TODO(), &kubeApiCore.Namespace{ - ObjectMeta: kubeApiMeta.ObjectMeta{ - Name: cfg.SystemNamespace, - }, - }, kubeApiMeta.CreateOptions{}); err != nil && !errors.IsAlreadyExists(err) { - return err - } - // create kubeconfig secret - if _, err = c.CoreV1().Secrets(cfg.SystemNamespace). - Create(context.TODO(), &kubeApiCore.Secret{ - ObjectMeta: kubeApiMeta.ObjectMeta{ - Name: "istio-kubeconfig", - Namespace: cfg.SystemNamespace, - }, - Data: map[string][]byte{ - "config": []byte(istioKubeConfig), - }, - }, kubeApiMeta.CreateOptions{}); err != nil { - if errors.IsAlreadyExists(err) { // Allow easier running locally when we run multiple tests in a row - if _, err := c.CoreV1().Secrets(cfg.SystemNamespace).Update(context.TODO(), &kubeApiCore.Secret{ - ObjectMeta: kubeApiMeta.ObjectMeta{ - Name: "istio-kubeconfig", - Namespace: cfg.SystemNamespace, - }, - Data: map[string][]byte{ - "config": []byte(istioKubeConfig), - }, - }, kubeApiMeta.UpdateOptions{}); err != nil { - scopes.Framework.Infof("error updating istio-kubeconfig secret: %v", err) - return err - } - } else { - scopes.Framework.Infof("error creating istio-kubeconfig secret %v", err) - return err - } - } - return nil -} - -func createIstioctlConfigFile(s *resource.Settings, workDir string, cfg Config) (istioctlConfigFiles, error) { - var err error - configFiles := istioctlConfigFiles{ - iopFile: "", - configIopFile: "", - remoteIopFile: "", - } - // Generate the istioctl config file for control plane(primary) cluster - configFiles.iopFile = filepath.Join(workDir, "iop.yaml") - if configFiles.operatorSpec, err = initIOPFile(s, cfg, configFiles.iopFile, cfg.ControlPlaneValues); err != nil { - return configFiles, err - } - - // Generate the istioctl config file for remote cluster - if cfg.RemoteClusterValues == "" { - cfg.RemoteClusterValues = cfg.ControlPlaneValues - } - - configFiles.remoteIopFile = filepath.Join(workDir, "remote.yaml") - if configFiles.remoteOperatorSpec, err = initIOPFile(s, cfg, configFiles.remoteIopFile, cfg.RemoteClusterValues); err != nil { - return configFiles, err - } - - // Generate the istioctl config file for config cluster - configFiles.configIopFile = configFiles.iopFile - configFiles.configOperatorSpec = configFiles.operatorSpec - if cfg.ConfigClusterValues != "" { - configFiles.configIopFile = filepath.Join(workDir, "config.yaml") - if configFiles.configOperatorSpec, err = initIOPFile(s, cfg, configFiles.configIopFile, cfg.ConfigClusterValues); err != nil { - return configFiles, err - } - } - return configFiles, nil -} diff --git a/pkg/test/framework/components/istio/util.go b/pkg/test/framework/components/istio/util.go deleted file mode 100644 index 9a9b4b106..000000000 --- a/pkg/test/framework/components/istio/util.go +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istio - -import ( - "context" - "fmt" - "net" - "strconv" - "sync" - "time" -) - -import ( - "github.com/hashicorp/go-multierror" - meshconfig "istio.io/api/mesh/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/environment/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -const ( - istiodLabel = "pilot" -) - -var dummyValidationVirtualServiceTemplate = ` -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: validation-readiness-dummy-virtual-service - namespace: %s -spec: - hosts: - - non-existent-host - http: - - route: - - destination: - host: non-existent-host - subset: v1 - weight: 75 - - destination: - host: non-existent-host - subset: v2 - weight: 25 -` - -func waitForValidationWebhook(ctx resource.Context, cluster cluster.Cluster, cfg Config) error { - dummyValidationVirtualService := fmt.Sprintf(dummyValidationVirtualServiceTemplate, cfg.SystemNamespace) - defer func() { - e := ctx.ConfigKube(cluster).YAML("", dummyValidationVirtualService).Delete() - if e != nil { - scopes.Framework.Warnf("error deleting dummy virtual service for waiting the validation webhook: %v", e) - } - }() - - scopes.Framework.Info("Creating dummy virtual service to check for validation webhook readiness") - return retry.UntilSuccess(func() error { - err := ctx.ConfigKube(cluster).YAML("", dummyValidationVirtualService).Apply() - if err == nil { - return nil - } - - return fmt.Errorf("validation webhook not ready yet: %v", err) - }, retry.Timeout(time.Minute)) -} - -func (i *operatorComponent) RemoteDiscoveryAddressFor(cluster cluster.Cluster) (net.TCPAddr, error) { - var addr net.TCPAddr - primary := cluster.Primary() - if !primary.IsConfig() { - // istiod is exposed via LoadBalancer since we won't have ingress outside of a cluster;a cluster that is; - // a control cluster, but not config cluster is supposed to simulate istiod outside of k8s or "external" - address, err := retry.UntilComplete(func() (interface{}, bool, error) { - return getRemoteServiceAddress(i.environment.Settings(), primary, i.settings.SystemNamespace, istiodLabel, - istiodSvcName, discoveryPort) - }, getAddressTimeout, getAddressDelay) - if err != nil { - return net.TCPAddr{}, err - } - addr = address.(net.TCPAddr) - } else { - addr = i.CustomIngressFor(primary, eastWestIngressServiceName, eastWestIngressIstioLabel).DiscoveryAddress() - } - if addr.IP.String() == "" { - return net.TCPAddr{}, fmt.Errorf("failed to get ingress IP for %s", primary.Name()) - } - return addr, nil -} - -func getRemoteServiceAddress(s *kube.Settings, cluster cluster.Cluster, ns, label, svcName string, - port int) (interface{}, bool, error) { - if !s.LoadBalancerSupported { - pods, err := cluster.PodsForSelector(context.TODO(), ns, fmt.Sprintf("istio=%s", label)) - if err != nil { - return nil, false, err - } - - names := make([]string, 0, len(pods.Items)) - for _, p := range pods.Items { - names = append(names, p.Name) - } - scopes.Framework.Debugf("Querying remote service %s, pods:%v", svcName, names) - if len(pods.Items) == 0 { - return nil, false, fmt.Errorf("no remote service pod found") - } - - scopes.Framework.Debugf("Found pod: %v", pods.Items[0].Name) - ip := pods.Items[0].Status.HostIP - if ip == "" { - return nil, false, fmt.Errorf("no Host IP available on the remote service node yet") - } - - svc, err := cluster.CoreV1().Services(ns).Get(context.TODO(), svcName, v1.GetOptions{}) - if err != nil { - return nil, false, err - } - - if len(svc.Spec.Ports) == 0 { - return nil, false, fmt.Errorf("no ports found in service: %s/%s", ns, svcName) - } - - var nodePort int32 - for _, svcPort := range svc.Spec.Ports { - if svcPort.Protocol == "TCP" && svcPort.Port == int32(port) { - nodePort = svcPort.NodePort - break - } - } - if nodePort == 0 { - return nil, false, fmt.Errorf("no port %d found in service: %s/%s", port, ns, svcName) - } - - return net.TCPAddr{IP: net.ParseIP(ip), Port: int(nodePort)}, true, nil - } - - // Otherwise, get the load balancer IP. - svc, err := cluster.CoreV1().Services(ns).Get(context.TODO(), svcName, v1.GetOptions{}) - if err != nil { - return nil, false, err - } - - if len(svc.Status.LoadBalancer.Ingress) == 0 { - return nil, false, fmt.Errorf("service %s/%s is not available yet: no ingress", svc.Namespace, svc.Name) - } - ingr := svc.Status.LoadBalancer.Ingress[0] - if ingr.IP == "" && ingr.Hostname == "" { - return nil, false, fmt.Errorf("service %s/%s is not available yet: no ingress", svc.Namespace, svc.Name) - } - if ingr.IP != "" { - return net.TCPAddr{IP: net.ParseIP(ingr.IP), Port: port}, true, nil - } - return net.JoinHostPort(ingr.Hostname, strconv.Itoa(port)), true, nil -} - -func (i *operatorComponent) isExternalControlPlane() bool { - for _, cluster := range i.ctx.AllClusters() { - if cluster.IsPrimary() && !cluster.IsConfig() { - return true - } - } - return false -} - -func PatchMeshConfig(t resource.Context, ns string, clusters cluster.Clusters, patch string) error { - errG := multierror.Group{} - origCfg := map[string]string{} - mu := sync.RWMutex{} - - cmName := "istio" - if rev := t.Settings().Revisions.Default(); rev != "default" && rev != "" { - cmName += "-" + rev - } - for _, c := range clusters.Kube() { - c := c - errG.Go(func() error { - cm, err := c.CoreV1().ConfigMaps(ns).Get(context.TODO(), cmName, v1.GetOptions{}) - if err != nil { - return err - } - mcYaml, ok := cm.Data["mesh"] - if !ok { - return fmt.Errorf("mesh config was missing in istio config map for %s", c.Name()) - } - mu.Lock() - origCfg[c.Name()] = cm.Data["mesh"] - mu.Unlock() - mc := &meshconfig.MeshConfig{} - if err := protomarshal.ApplyYAML(mcYaml, mc); err != nil { - return err - } - if err := protomarshal.ApplyYAML(patch, mc); err != nil { - return err - } - cm.Data["mesh"], err = protomarshal.ToYAML(mc) - if err != nil { - return err - } - _, err = c.CoreV1().ConfigMaps(ns).Update(context.TODO(), cm, v1.UpdateOptions{}) - if err != nil { - return err - } - scopes.Framework.Infof("patched %s meshconfig:\n%s", c.Name(), cm.Data["mesh"]) - return nil - }) - } - t.Cleanup(func() { - errG := multierror.Group{} - mu.RLock() - defer mu.RUnlock() - for cn, mcYaml := range origCfg { - cn, mcYaml := cn, mcYaml - c := clusters.GetByName(cn) - errG.Go(func() error { - cm, err := c.CoreV1().ConfigMaps(ns).Get(context.TODO(), cmName, v1.GetOptions{}) - if err != nil { - return err - } - cm.Data["mesh"] = mcYaml - _, err = c.CoreV1().ConfigMaps(ns).Update(context.TODO(), cm, v1.UpdateOptions{}) - return err - }) - } - if err := errG.Wait().ErrorOrNil(); err != nil { - scopes.Framework.Errorf("failed cleaning up cluster-local config: %v", err) - } - }) - return errG.Wait().ErrorOrNil() -} - -func PatchMeshConfigOrFail(t framework.TestContext, ns string, clusters cluster.Clusters, patch string) { - t.Helper() - if err := PatchMeshConfig(t, ns, clusters, patch); err != nil { - t.Fatal(err) - } -} diff --git a/pkg/test/framework/components/istioctl/istioctl.go b/pkg/test/framework/components/istioctl/istioctl.go deleted file mode 100644 index 64d6f0b35..000000000 --- a/pkg/test/framework/components/istioctl/istioctl.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istioctl - -import ( - "fmt" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -// Instance represents "istioctl" -type Instance interface { - // WaitForConfig will wait until all passed in config has been distributed - WaitForConfig(defaultNamespace string, configs string) error - - // Invoke invokes an istioctl command and returns the output and exception. - // stdout and stderr will be returned as different strings - Invoke(args []string) (string, string, error) - - // InvokeOrFail calls Invoke and fails tests if it returns en err - InvokeOrFail(t test.Failer, args []string) (string, string) -} - -// Config is structured config for the istioctl component -type Config struct { - // Cluster to be used in a multicluster environment - Cluster cluster.Cluster -} - -// New returns a new instance of "istioctl". -func New(ctx resource.Context, cfg Config) (i Instance, err error) { - return newKube(ctx, cfg) -} - -// NewOrFail returns a new instance of "istioctl". -func NewOrFail(t test.Failer, c resource.Context, config Config) Instance { - i, err := New(c, config) - if err != nil { - t.Fatalf("istioctl.NewOrFail:: %v", err) - } - - return i -} - -func (c *Config) String() string { - result := "" - result += fmt.Sprintf("Cluster: %s\n", c.Cluster) - return result -} diff --git a/pkg/test/framework/components/istioctl/kube.go b/pkg/test/framework/components/istioctl/kube.go deleted file mode 100644 index dc3a9454c..000000000 --- a/pkg/test/framework/components/istioctl/kube.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istioctl - -import ( - "bytes" - "fmt" - "strings" - "sync" - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/istioctl/cmd" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/crd" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" -) - -// We cannot invoke the istioctl library concurrently due to the number of global variables -// https://github.com/istio/istio/issues/37324 -var invokeMutex sync.Mutex - -type kubeComponent struct { - config Config - kubeconfig string -} - -// Filenamer is an interface to avoid importing kubecluster package, instead build our own interface -// to extract kube context -type Filenamer interface { - Filename() string -} - -func newKube(ctx resource.Context, config Config) (Instance, error) { - fn, ok := ctx.Clusters().GetOrDefault(config.Cluster).(Filenamer) - if !ok { - return nil, fmt.Errorf("cluster does not support fetching kube config") - } - n := &kubeComponent{ - config: config, - kubeconfig: fn.Filename(), - } - - return n, nil -} - -// Invoke implements WaitForConfigs -func (c *kubeComponent) WaitForConfig(defaultNamespace string, configs string) error { - cfgs, _, err := crd.ParseInputs(configs) - if err != nil { - return fmt.Errorf("failed to parse input: %v", err) - } - for _, cfg := range cfgs { - ns := cfg.Namespace - if ns == "" { - ns = defaultNamespace - } - // TODO(https://github.com/istio/istio/issues/37148) increase timeout. Right now it fails often, so - // set it to low timeout to reduce impact - if out, stderr, err := c.Invoke([]string{"x", "wait", "-v", "--timeout=5s", cfg.GroupVersionKind.Kind, cfg.Name + "." + ns}); err != nil { - return fmt.Errorf("wait: %v\nout: %v\nerr: %v", err, out, stderr) - } - } - return nil -} - -// Invoke implements Instance -func (c *kubeComponent) Invoke(args []string) (string, string, error) { - cmdArgs := append([]string{ - "--kubeconfig", - c.kubeconfig, - }, args...) - - var out bytes.Buffer - var err bytes.Buffer - - start := time.Now() - - invokeMutex.Lock() - rootCmd := cmd.GetRootCmd(cmdArgs) - rootCmd.SetOut(&out) - rootCmd.SetErr(&err) - fErr := rootCmd.Execute() - invokeMutex.Unlock() - - scopes.Framework.Infof("istioctl (%v): completed after %.4fs", args, time.Since(start).Seconds()) - - if err.String() != "" { - scopes.Framework.Infof("istioctl error: %v", strings.TrimSpace(err.String())) - } - return out.String(), err.String(), fErr -} - -// InvokeOrFail implements Instance -func (c *kubeComponent) InvokeOrFail(t test.Failer, args []string) (string, string) { - output, stderr, err := c.Invoke(args) - if err != nil { - t.Logf("Unwanted exception for 'istioctl %s': %v", strings.Join(args, " "), err) - t.Logf("Output:\n%v", output) - t.Logf("Error:\n%v", stderr) - t.FailNow() - } - return output, stderr -} diff --git a/pkg/test/framework/components/namespace/kube.go b/pkg/test/framework/components/namespace/kube.go deleted file mode 100644 index 391307a34..000000000 --- a/pkg/test/framework/components/namespace/kube.go +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package namespace - -import ( - "context" - "fmt" - "io" - "math/rand" - "strings" - "sync" - "time" -) - -import ( - "github.com/google/go-cmp/cmp" - "github.com/hashicorp/go-multierror" - "istio.io/api/label" - "istio.io/pkg/log" - kubeApiCore "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - kube2 "github.com/apache/dubbo-go-pixiu/pkg/test/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" -) - -var ( - idctr int64 - rnd = rand.New(rand.NewSource(time.Now().UnixNano())) - mu sync.Mutex -) - -// kubeNamespace represents a Kubernetes namespace. It is tracked as a resource. -type kubeNamespace struct { - id resource.ID - name string - prefix string - ctx resource.Context -} - -func (n *kubeNamespace) Dump(ctx resource.Context) { - scopes.Framework.Errorf("=== Dumping Namespace %s State...", n.name) - - d, err := ctx.CreateTmpDirectory(n.name + "-state") - if err != nil { - scopes.Framework.Errorf("Unable to create directory for dumping %s contents: %v", n.name, err) - return - } - - kube2.DumpPods(n.ctx, d, n.name, []string{}) - kube2.DumpDeployments(n.ctx, d, n.name) -} - -var ( - _ Instance = &kubeNamespace{} - _ io.Closer = &kubeNamespace{} - _ resource.Resource = &kubeNamespace{} - _ resource.Dumper = &kubeNamespace{} -) - -func (n *kubeNamespace) Name() string { - return n.name -} - -func (n *kubeNamespace) Prefix() string { - return n.prefix -} - -func (n *kubeNamespace) Labels() (map[string]string, error) { - perCluster := make([]map[string]string, len(n.ctx.Clusters())) - errG := multierror.Group{} - for i, cluster := range n.ctx.Clusters() { - i, cluster := i, cluster - errG.Go(func() error { - ns, err := cluster.CoreV1().Namespaces().Get(context.TODO(), n.Name(), metav1.GetOptions{}) - if err != nil { - return err - } - perCluster[i] = ns.Labels - return nil - }) - } - if err := errG.Wait().ErrorOrNil(); err != nil { - return nil, err - } - for i, clusterLabels := range perCluster { - if i == 0 { - continue - } - if diff := cmp.Diff(perCluster[0], clusterLabels); diff != "" { - log.Warnf("namespace labels are different across clusters:\n%s", diff) - } - } - return perCluster[0], nil -} - -func (n *kubeNamespace) SetLabel(key, value string) error { - return n.setNamespaceLabel(key, value) -} - -func (n *kubeNamespace) RemoveLabel(key string) error { - return n.removeNamespaceLabel(key) -} - -func (n *kubeNamespace) ID() resource.ID { - return n.id -} - -// Close implements io.Closer -func (n *kubeNamespace) Close() (err error) { - if n.name != "" { - scopes.Framework.Debugf("%s deleting namespace", n.id) - ns := n.name - n.name = "" - - for _, c := range n.ctx.Clusters().Kube() { - err = c.CoreV1().Namespaces().Delete(context.TODO(), ns, kube2.DeleteOptionsForeground()) - } - } - - scopes.Framework.Debugf("%s close complete (err:%v)", n.id, err) - return -} - -func claimKube(ctx resource.Context, nsConfig *Config) (Instance, error) { - for _, cluster := range ctx.Clusters().Kube() { - if !kube2.NamespaceExists(cluster, nsConfig.Prefix) { - if _, err := cluster.CoreV1().Namespaces().Create(context.TODO(), &kubeApiCore.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: nsConfig.Prefix, - Labels: createNamespaceLabels(ctx, nsConfig), - }, - }, metav1.CreateOptions{}); err != nil { - return nil, err - } - } - } - return &kubeNamespace{prefix: nsConfig.Prefix, name: nsConfig.Prefix, ctx: ctx}, nil -} - -// setNamespaceLabel labels a namespace with the given key, value pair -func (n *kubeNamespace) setNamespaceLabel(key, value string) error { - // need to convert '/' to '~1' as per the JSON patch spec http://jsonpatch.com/#operations - jsonPatchEscapedKey := strings.ReplaceAll(key, "/", "~1") - for _, cluster := range n.ctx.Clusters().Kube() { - nsLabelPatch := fmt.Sprintf(`[{"op":"replace","path":"/metadata/labels/%s","value":"%s"}]`, jsonPatchEscapedKey, value) - if _, err := cluster.CoreV1().Namespaces().Patch(context.TODO(), n.name, types.JSONPatchType, []byte(nsLabelPatch), metav1.PatchOptions{}); err != nil { - return err - } - } - - return nil -} - -// removeNamespaceLabel removes namespace label with the given key -func (n *kubeNamespace) removeNamespaceLabel(key string) error { - // need to convert '/' to '~1' as per the JSON patch spec http://jsonpatch.com/#operations - jsonPatchEscapedKey := strings.ReplaceAll(key, "/", "~1") - for _, cluster := range n.ctx.Clusters().Kube() { - nsLabelPatch := fmt.Sprintf(`[{"op":"remove","path":"/metadata/labels/%s"}]`, jsonPatchEscapedKey) - if _, err := cluster.CoreV1().Namespaces().Patch(context.TODO(), n.name, types.JSONPatchType, []byte(nsLabelPatch), metav1.PatchOptions{}); err != nil { - return err - } - } - - return nil -} - -// NewNamespace allocates a new testing namespace. -func newKube(ctx resource.Context, nsConfig *Config) (Instance, error) { - mu.Lock() - idctr++ - nsid := idctr - r := rnd.Intn(99999) - mu.Unlock() - - ns := fmt.Sprintf("%s-%d-%d", nsConfig.Prefix, nsid, r) - n := &kubeNamespace{ - name: ns, - prefix: nsConfig.Prefix, - ctx: ctx, - } - id := ctx.TrackResource(n) - n.id = id - - s := ctx.Settings() - for _, cluster := range n.ctx.Clusters().Kube() { - if _, err := cluster.CoreV1().Namespaces().Create(context.TODO(), &kubeApiCore.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: ns, - Labels: createNamespaceLabels(ctx, nsConfig), - }, - }, metav1.CreateOptions{}); err != nil { - return nil, err - } - if s.Image.PullSecret != "" { - if err := cluster.ApplyYAMLFiles(n.name, s.Image.PullSecret); err != nil { - return nil, err - } - } - } - - return n, nil -} - -// createNamespaceLabels will take a namespace config and generate the proper k8s labels -func createNamespaceLabels(ctx resource.Context, cfg *Config) map[string]string { - l := make(map[string]string) - l["istio-testing"] = "istio-test" - if cfg.Inject { - // do not add namespace labels when running compatibility tests since - // this disables the necessary object selectors - if !ctx.Settings().Compatibility { - if cfg.Revision != "" { - l[label.IoIstioRev.Name] = cfg.Revision - } else { - l["istio-injection"] = "enabled" - } - } - } else { - // if we're running compatibility tests, disable injection in the namespace - // explicitly so that object selectors are ignored - if ctx.Settings().Compatibility { - l["istio-injection"] = "disabled" - } - } - - // bring over supplied labels - for k, v := range cfg.Labels { - l[k] = v - } - return l -} diff --git a/pkg/test/framework/components/namespace/namespace.go b/pkg/test/framework/components/namespace/namespace.go deleted file mode 100644 index 30e79da6e..000000000 --- a/pkg/test/framework/components/namespace/namespace.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package namespace - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -// Config contains configuration information about the namespace instance -type Config struct { - // Prefix to use for autogenerated namespace name - Prefix string - // Inject indicates whether to add sidecar injection label to this namespace - Inject bool - // Revision is the namespace of custom injector instance - Revision string - // Labels to be applied to namespace - Labels map[string]string -} - -// Instance represents an allocated namespace that can be used to create config, or deploy components in. -type Instance interface { - Name() string - SetLabel(key, value string) error - RemoveLabel(key string) error - Prefix() string - Labels() (map[string]string, error) -} - -// Claim an existing namespace in all clusters, or create a new one if doesn't exist. -func Claim(ctx resource.Context, nsConfig Config) (i Instance, err error) { - overwriteRevisionIfEmpty(&nsConfig, ctx.Settings().Revisions.Default()) - return claimKube(ctx, &nsConfig) -} - -// ClaimOrFail calls Claim and fails test if it returns error -func ClaimOrFail(t test.Failer, ctx resource.Context, name string) Instance { - t.Helper() - nsCfg := Config{ - Prefix: name, - Inject: true, - } - i, err := Claim(ctx, nsCfg) - if err != nil { - t.Fatalf("namespace.ClaimOrFail:: %v", err) - } - return i -} - -// New creates a new Namespace in all clusters. -func New(ctx resource.Context, nsConfig Config) (i Instance, err error) { - if ctx.Settings().StableNamespaces { - return Claim(ctx, nsConfig) - } - overwriteRevisionIfEmpty(&nsConfig, ctx.Settings().Revisions.Default()) - return newKube(ctx, &nsConfig) -} - -// NewOrFail calls New and fails test if it returns error -func NewOrFail(t test.Failer, ctx resource.Context, nsConfig Config) Instance { - t.Helper() - i, err := New(ctx, nsConfig) - if err != nil { - t.Fatalf("namespace.NewOrFail: %v", err) - } - return i -} - -func overwriteRevisionIfEmpty(nsConfig *Config, revision string) { - // Overwrite the default namespace label (istio-injection=enabled) - // with istio.io/rev=XXX. If a revision label is already provided, - // the label will remain as is. - if nsConfig.Revision == "" { - nsConfig.Revision = revision - } - // Allow setting revision explicitly to `default` to avoid configuration overwrite - if nsConfig.Revision == "default" { - nsConfig.Revision = "" - } -} diff --git a/pkg/test/framework/components/namespace/namespace_test.go b/pkg/test/framework/components/namespace/namespace_test.go deleted file mode 100644 index 287709938..000000000 --- a/pkg/test/framework/components/namespace/namespace_test.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package namespace - -import ( - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -func TestConfigRevisionOverwrite(t *testing.T) { - testCases := []struct { - name string - - // test inputs. - revision string - cfg Config - - // expected results. - wantRevision string - }{ - { - name: "NoOverwriteEmptyInput", - revision: "", - cfg: Config{ - Prefix: "default", - Revision: "istio.io/rev=XXX", - }, - wantRevision: "istio.io/rev=XXX", - }, - { - name: "NoOverwriteNonEmptyInput", - revision: "BadRevision", - cfg: Config{ - Prefix: "default", - Revision: "istio.io/rev=XXX", - }, - wantRevision: "istio.io/rev=XXX", - }, - { - name: "OverwriteNonEmptyInput", - revision: "istio.io/rev=canary", - cfg: Config{ - Prefix: "default", - Revision: "", - }, - wantRevision: "istio.io/rev=canary", - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - overwriteRevisionIfEmpty(&tc.cfg, tc.revision) - g := NewWithT(t) - g.Expect(tc.cfg.Revision).Should(Equal(tc.wantRevision)) - }) - } -} diff --git a/pkg/test/framework/components/namespace/static.go b/pkg/test/framework/components/namespace/static.go deleted file mode 100644 index 29900cd0e..000000000 --- a/pkg/test/framework/components/namespace/static.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package namespace - -var ( - chck Static - _ Instance = &chck -) - -// Static is a namespace that may or may not exist. It is used for configuration purposes only -type Static string - -func (s Static) Name() string { - return string(s) -} - -func (s Static) Prefix() string { - return string(s) -} - -func (s Static) Labels() (map[string]string, error) { - panic("implement me") -} - -func (s Static) SetLabel(key, value string) error { - panic("implement me") -} - -func (s Static) RemoveLabel(key string) error { - panic("implement me") -} - -func (s *Static) UnmarshalJSON(bytes []byte) error { - *s = Static(bytes) - return nil -} diff --git a/pkg/test/framework/components/opentelemetry/kube.go b/pkg/test/framework/components/opentelemetry/kube.go deleted file mode 100644 index c65254123..000000000 --- a/pkg/test/framework/components/opentelemetry/kube.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package opentelemetry - -import ( - "fmt" - "net" - "os" - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/istio" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - testKube "github.com/apache/dubbo-go-pixiu/pkg/test/kube" -) - -type otel struct { - id resource.ID - cluster cluster.Cluster -} - -const ( - appName = "opentelemetry-collector" - remoteOtelEntry = ` -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: otel-gateway - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 55678 - name: http-tracing-span - protocol: HTTP - hosts: - - "opentelemetry-collector.{INGRESS_DOMAIN}" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: opentelemetry-collector - namespace: dubbo-system -spec: - hosts: - - "opentelemetry-collector.{INGRESS_DOMAIN}" - gateways: - - otel-gateway - http: - - match: - - port: 55678 - route: - - destination: - host: opentelemetry-collector - port: - number: 55678 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: opentelemetry-collector - namespace: dubbo-system -spec: - host: opentelemetry-collector - trafficPolicy: - tls: - mode: DISABLE ----` - - extServiceEntry = ` -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: opentelemetry-collector -spec: - hosts: - # must be of form name.namespace.global - - opentelemetry-collector.dubbo-system.global - # Treat remote cluster services as part of the service mesh - # as all clusters in the service mesh share the same root of trust. - location: MESH_INTERNAL - ports: - - name: http-tracing-span - number: 55678 - protocol: http - resolution: DNS - addresses: - # the IP address to which opentelemetry-collector.dubbo-system.global will resolve to - # must be unique for each remote service, within a given cluster. - # This address need not be routable. Traffic for this IP will be captured - # by the sidecar and routed appropriately. - - 240.0.0.2 - endpoints: - # This is the routable address of the ingress gateway in cluster1 that - # sits in front of otel service. Traffic from the sidecar will be - # routed to this address. - - address: {INGRESS_DOMAIN} - ports: - http-tracing-span: 15443 # Do not change this port value -` -) - -func getYaml() (string, error) { - b, err := os.ReadFile(env.OtelCollectorInstallFilePath) - if err != nil { - return "", err - } - return string(b), nil -} - -func install(ctx resource.Context, ns string) error { - y, err := getYaml() - if err != nil { - return err - } - return ctx.ConfigKube().YAML(ns, y).Apply() -} - -func installServiceEntry(ctx resource.Context, ns, ingressAddr string) error { - // Setup remote access to zipkin in cluster - yaml := strings.ReplaceAll(remoteOtelEntry, "{INGRESS_DOMAIN}", ingressAddr) - if err := ctx.ConfigIstio().YAML(ns, yaml).Apply(); err != nil { - return err - } - // For all other clusters, add a service entry so that can access - // zipkin in cluster installed. - yaml = strings.ReplaceAll(extServiceEntry, "{INGRESS_DOMAIN}", ingressAddr) - if err := ctx.ConfigIstio().YAML(ns, yaml).Apply(); err != nil { - return err - } - return nil -} - -func newCollector(ctx resource.Context, c Config) (*otel, error) { - o := &otel{ - cluster: ctx.Clusters().GetOrDefault(c.Cluster), - } - ctx.TrackResource(o) - - istioCfg, err := istio.DefaultConfig(ctx) - if err != nil { - return nil, err - } - - ns := istioCfg.TelemetryNamespace - if err := install(ctx, ns); err != nil { - return nil, err - } - - f := testKube.NewSinglePodFetch(o.cluster, ns, fmt.Sprintf("app=%s", appName)) - _, err = testKube.WaitUntilPodsAreReady(f) - if err != nil { - return nil, err - } - - isIP := net.ParseIP(c.IngressAddr).String() != "" - ingressDomain := c.IngressAddr - if isIP { - ingressDomain = fmt.Sprintf("%s.sslip.io", strings.ReplaceAll(c.IngressAddr, ":", "-")) - } - - err = installServiceEntry(ctx, istioCfg.TelemetryNamespace, ingressDomain) - if err != nil { - return nil, err - } - return o, nil -} - -func (o *otel) ID() resource.ID { - return o.id -} diff --git a/pkg/test/framework/components/opentelemetry/opentelemetry-collector.go b/pkg/test/framework/components/opentelemetry/opentelemetry-collector.go deleted file mode 100644 index aed9a485e..000000000 --- a/pkg/test/framework/components/opentelemetry/opentelemetry-collector.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package opentelemetry - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -// Config represents the configuration for setting up an opentelemetry -// collector. -type Config struct { - // Cluster to be used in a multicluster environment - Cluster cluster.Cluster - - // HTTP Address of ingress gateway of the cluster to be used to install open telemetry collector in. - IngressAddr string -} - -// Instance represents a opencensus collector deployment on kubernetes. -type Instance interface { - resource.Resource -} - -// New creates and returns a new instance of otel. -func New(ctx resource.Context, c Config) (Instance, error) { - return newCollector(ctx, c) -} - -// NewOrFail returns a new otel instance or fails the test. -func NewOrFail(t *testing.T, ctx resource.Context, c Config) Instance { - t.Helper() - i, err := New(ctx, c) - if err != nil { - t.Fatalf("opentelemetry.NewOrFail: %v", err) - } - return i -} diff --git a/pkg/test/framework/components/opentelemetry/opentelemetry-collector.yaml b/pkg/test/framework/components/opentelemetry/opentelemetry-collector.yaml deleted file mode 100644 index e1da04aad..000000000 --- a/pkg/test/framework/components/opentelemetry/opentelemetry-collector.yaml +++ /dev/null @@ -1,119 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: opentelemetry-collector - labels: - app: opentelemetry-collector -data: - config: | - receivers: - opencensus: - endpoint: 0.0.0.0:55678 - processors: - memory_limiter: - # Must be same as --mem-ballast-size-mib CLI argument - ballast_size_mib: 20 - limit_mib: 100 - spike_limit_mib: 10 - check_interval: 5s - - exporters: - zipkin: - # Export to zipkin for easy querying - endpoint: http://zipkin.dubbo-system.svc:9411/api/v2/spans - logging: - loglevel: debug - - extensions: - health_check: - port: 13133 - - service: - extensions: - - health_check - pipelines: - traces: - receivers: - - opencensus - processors: - - memory_limiter - exporters: - - zipkin - - logging ---- -apiVersion: v1 -kind: Service -metadata: - name: opentelemetry-collector - labels: - app: opentelemetry-collector -spec: - type: ClusterIP - selector: - app: opentelemetry-collector - ports: - - name: grpc-opencensus - port: 55678 - protocol: TCP - targetPort: 55678 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: opentelemetry-collector - labels: - app: opentelemetry-collector -spec: - replicas: 1 - selector: - matchLabels: - app: opentelemetry-collector - template: - metadata: - labels: - app: opentelemetry-collector - spec: - containers: - - name: opentelemetry-collector - image: "otel/opentelemetry-collector:0.9.0" - imagePullPolicy: IfNotPresent - command: - - "/otelcol" - - "--config=/conf/config.yaml" - - "--mem-ballast-size-mib=20" - ports: - - name: grpc-opencensus - containerPort: 55678 - protocol: TCP - volumeMounts: - - name: opentelemetry-collector-config - mountPath: /conf - readinessProbe: - httpGet: - path: / - port: 13133 - resources: - requests: - cpu: 40m - memory: 100Mi - volumes: - - name: opentelemetry-collector-config - configMap: - name: opentelemetry-collector - items: - - key: config - path: config.yaml diff --git a/pkg/test/framework/components/prometheus/kube.go b/pkg/test/framework/components/prometheus/kube.go deleted file mode 100644 index 193d3f9ef..000000000 --- a/pkg/test/framework/components/prometheus/kube.go +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package prometheus - -import ( - "context" - "fmt" - "io" - "os" - "path/filepath" - "sort" - "strings" - "time" -) - -import ( - prometheusApi "github.com/prometheus/client_golang/api" - prometheusApiV1 "github.com/prometheus/client_golang/api/prometheus/v1" - "github.com/prometheus/common/model" - kubeApiMeta "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - istioKube "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/istio" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - testKube "github.com/apache/dubbo-go-pixiu/pkg/test/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" -) - -const ( - serviceName = "prometheus" - appName = "prometheus" -) - -var ( - _ Instance = &kubeComponent{} - _ io.Closer = &kubeComponent{} -) - -type kubeComponent struct { - id resource.ID - - api map[string]prometheusApiV1.API - forwarder map[string]istioKube.PortForwarder - clusters cluster.Clusters -} - -func getPrometheusYaml() (string, error) { - yamlBytes, err := os.ReadFile(filepath.Join(env.IstioSrc, "samples/addons/prometheus.yaml")) - if err != nil { - return "", err - } - yaml := string(yamlBytes) - // For faster tests, drop scrape interval - yaml = strings.ReplaceAll(yaml, "scrape_interval: 15s", "scrape_interval: 5s") - yaml = strings.ReplaceAll(yaml, "scrape_timeout: 10s", "scrape_timeout: 5s") - return yaml, nil -} - -func installPrometheus(ctx resource.Context, ns string) error { - yaml, err := getPrometheusYaml() - if err != nil { - return err - } - if err := ctx.ConfigKube().YAML(ns, yaml).Apply(resource.NoCleanup); err != nil { - return err - } - ctx.ConditionalCleanup(func() { - _ = ctx.ConfigKube().YAML(ns, yaml).Delete() - }) - return nil -} - -func newKube(ctx resource.Context, cfgIn Config) (Instance, error) { - c := &kubeComponent{ - clusters: ctx.Clusters(), - } - c.id = ctx.TrackResource(c) - c.api = make(map[string]prometheusApiV1.API) - c.forwarder = make(map[string]istioKube.PortForwarder) - cfg, err := istio.DefaultConfig(ctx) - if err != nil { - return nil, err - } - - if !cfgIn.SkipDeploy { - if err := installPrometheus(ctx, cfg.TelemetryNamespace); err != nil { - return nil, err - } - } - for _, cls := range ctx.Clusters().Kube() { - scopes.Framework.Debugf("Installing Prometheus on cluster: %s", cls.Name()) - // Find the Prometheus pod and service, and start forwarding a local port. - fetchFn := testKube.NewSinglePodFetch(cls, cfg.TelemetryNamespace, fmt.Sprintf("app=%s", appName)) - pods, err := testKube.WaitUntilPodsAreReady(fetchFn) - if err != nil { - return nil, err - } - pod := pods[0] - - svc, err := cls.CoreV1().Services(cfg.TelemetryNamespace).Get(context.TODO(), serviceName, kubeApiMeta.GetOptions{}) - if err != nil { - return nil, err - } - port := uint16(svc.Spec.Ports[0].Port) - - forwarder, err := cls.NewPortForwarder(pod.Name, pod.Namespace, "", 0, int(port)) - if err != nil { - return nil, err - } - - if err := forwarder.Start(); err != nil { - return nil, err - } - c.forwarder[cls.Name()] = forwarder - scopes.Framework.Debugf("initialized Prometheus port forwarder: %v", forwarder.Address()) - - address := fmt.Sprintf("http://%s", forwarder.Address()) - var client prometheusApi.Client - client, err = prometheusApi.NewClient(prometheusApi.Config{Address: address}) - if err != nil { - return nil, err - } - - c.api[cls.Name()] = prometheusApiV1.NewAPI(client) - } - return c, nil -} - -func (c *kubeComponent) ID() resource.ID { - return c.id -} - -// API implements environment.DeployedPrometheus. -func (c *kubeComponent) API() prometheusApiV1.API { - return c.api[c.clusters.Default().Name()] -} - -func (c *kubeComponent) APIForCluster(cluster cluster.Cluster) prometheusApiV1.API { - return c.api[cluster.Name()] -} - -func (c *kubeComponent) Query(cluster cluster.Cluster, query Query) (model.Value, error) { - scopes.Framework.Debugf("Query running: %q", query) - - v, _, err := c.api[cluster.Name()].Query(context.Background(), query.String(), time.Now()) - if err != nil { - return nil, fmt.Errorf("error querying Prometheus: %v", err) - } - scopes.Framework.Debugf("Query received: %v", v) - - switch v.Type() { - case model.ValScalar, model.ValString: - return v, nil - - case model.ValVector: - value := v.(model.Vector) - if len(value) == 0 { - return nil, fmt.Errorf("value not found (query: %v)", query) - } - return v, nil - - default: - return nil, fmt.Errorf("unhandled value type: %v", v.Type()) - } -} - -func (c *kubeComponent) QuerySum(cluster cluster.Cluster, query Query) (float64, error) { - val, err := c.Query(cluster, query) - if err != nil { - return 0, err - } - got, err := Sum(val) - if err != nil { - return 0, fmt.Errorf("could not find metric value: %v", err) - } - return got, nil -} - -func Sum(val model.Value) (float64, error) { - if val.Type() != model.ValVector { - return 0, fmt.Errorf("value not a model.Vector; was %s", val.Type().String()) - } - - value := val.(model.Vector) - - valueCount := 0.0 - for _, sample := range value { - valueCount += float64(sample.Value) - } - - if valueCount > 0.0 { - return valueCount, nil - } - return 0, fmt.Errorf("value not found") -} - -// Close implements io.Closer. -func (c *kubeComponent) Close() error { - for _, forwarder := range c.forwarder { - forwarder.Close() - } - return nil -} - -type Query struct { - Metric string - Aggregation string - Labels map[string]string -} - -func (q Query) String() string { - query := q.Metric + `{` - - keys := []string{} - for k := range q.Labels { - keys = append(keys, k) - } - sort.Strings(keys) - for _, k := range keys { - v := q.Labels[k] - query += fmt.Sprintf(`%s=%q,`, k, v) - } - query += "}" - if q.Aggregation != "" { - query = fmt.Sprintf(`%s(%s)`, q.Aggregation, query) - } - return query -} diff --git a/pkg/test/framework/components/prometheus/prometheus.go b/pkg/test/framework/components/prometheus/prometheus.go deleted file mode 100644 index 185225858..000000000 --- a/pkg/test/framework/components/prometheus/prometheus.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package prometheus - -import ( - v1 "github.com/prometheus/client_golang/api/prometheus/v1" - prom "github.com/prometheus/common/model" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -type Instance interface { - resource.Resource - - // API Returns the core Prometheus APIs. - API() v1.API - APIForCluster(cluster cluster.Cluster) v1.API - - // Query Run the provided query against the given cluster - Query(cluster cluster.Cluster, query Query) (prom.Value, error) - - // QuerySum is a help around Query to compute the sum - QuerySum(cluster cluster.Cluster, query Query) (float64, error) -} - -type Config struct { - // If true, connect to an existing prometheus rather than creating a new one - SkipDeploy bool -} - -// New returns a new instance of prometheus. -func New(ctx resource.Context, c Config) (i Instance, err error) { - return newKube(ctx, c) -} - -// NewOrFail returns a new Prometheus instance or fails test. -func NewOrFail(t test.Failer, ctx resource.Context, c Config) Instance { - t.Helper() - i, err := New(ctx, c) - if err != nil { - t.Fatalf("prometheus.NewOrFail: %v", err) - } - - return i -} diff --git a/pkg/test/framework/components/stackdriver/google.go b/pkg/test/framework/components/stackdriver/google.go deleted file mode 100644 index ab174f31f..000000000 --- a/pkg/test/framework/components/stackdriver/google.go +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package stackdriver - -import ( - "context" - "fmt" - "net/http" - "strings" - "time" -) - -import ( - cloudtrace "google.golang.org/api/cloudtrace/v1" - logging "google.golang.org/api/logging/v2" - monitoring "google.golang.org/api/monitoring/v3" - "google.golang.org/genproto/googleapis/api/metric" - "google.golang.org/genproto/googleapis/api/monitoredres" - cloudtracepb "google.golang.org/genproto/googleapis/devtools/cloudtrace/v1" - ltype "google.golang.org/genproto/googleapis/logging/type" - loggingpb "google.golang.org/genproto/googleapis/logging/v2" - monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" -) - -import ( - md "github.com/apache/dubbo-go-pixiu/pkg/bootstrap/platform" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -type realStackdriver struct { - monitoringService *monitoring.Service - loggingService *logging.Service - traceService *cloudtrace.Service - gcpEnv md.Environment - projectID string -} - -type timeseriesQuery struct { - metricName string - resourceType string -} - -var ( - _ Instance = &realStackdriver{} - timeseriesQueries = []timeseriesQuery{ - { - metricName: "istio.io/service/server/request_count", - resourceType: "k8s_container", - }, - { - metricName: "istio.io/service/server/request_count", - resourceType: "gce_instance", - }, - { - metricName: "istio.io/service/client/request_count", - resourceType: "k8s_pod", - }, - { - metricName: "istio.io/service/server/connection_open_count", - resourceType: "k8s_container", - }, - { - metricName: "istio.io/service/server/connection_open_count", - resourceType: "gce_instance", - }, - { - metricName: "istio.io/service/client/connection_open_count", - resourceType: "k8s_pod", - }, - } - queryInterval = -90 * time.Second -) - -func newRealStackdriver(_ resource.Context, _ Config) (Instance, error) { - monitoringService, err := monitoring.NewService(context.Background()) - if err != nil { - return nil, fmt.Errorf("failed to get monitoring service: %v", err) - } - loggingService, err := logging.NewService(context.Background()) - if err != nil { - return nil, fmt.Errorf("failed to get logging service: %v", err) - } - traceService, err := cloudtrace.NewService(context.Background()) - if err != nil { - return nil, fmt.Errorf("failed to get tracing service: %v", err) - } - rsd := &realStackdriver{ - monitoringService: monitoringService, - loggingService: loggingService, - traceService: traceService, - gcpEnv: md.NewGCP(), - } - rsd.projectID = rsd.gcpEnv.Metadata()[md.GCPProject] - return rsd, nil -} - -func (s *realStackdriver) ListTimeSeries(namespace string) ([]*monitoringpb.TimeSeries, error) { - endTime := time.Now() - startTime := endTime.Add(queryInterval) - ret := &monitoringpb.ListTimeSeriesResponse{ - TimeSeries: make([]*monitoringpb.TimeSeries, 0), - } - for _, q := range timeseriesQueries { - filter := fmt.Sprintf("metric.type = %q AND resource.type = %q", q.metricName, q.resourceType) - if strings.HasPrefix(q.resourceType, "k8s") { - filter = fmt.Sprintf("%s AND resource.labels.namespace_name = %q", filter, namespace) - } - lr := s.monitoringService.Projects.TimeSeries.List(fmt.Sprintf("projects/%v", s.projectID)). - IntervalStartTime(startTime.Format(time.RFC3339)). - IntervalEndTime(endTime.Format(time.RFC3339)). - AggregationCrossSeriesReducer("REDUCE_NONE"). - AggregationAlignmentPeriod("60s"). - AggregationPerSeriesAligner("ALIGN_RATE"). - Filter(filter). - Context(context.Background()) - resp, err := lr.Do() - if err != nil { - return nil, err - } - if resp.HTTPStatusCode != http.StatusOK { - return nil, fmt.Errorf("failed to get expected status code from monitoring service, got: %d", resp.HTTPStatusCode) - } - for _, ts := range resp.TimeSeries { - newTS := &monitoringpb.TimeSeries{} - if ts.Metric == nil { - continue - } - newTS.Metric = &metric.Metric{} - newTS.Metric.Labels = ts.Metric.Labels - newTS.Metric.Type = ts.Metric.Type - if ts.Resource == nil { - continue - } - newTS.Resource = &monitoredres.MonitoredResource{} - newTS.Resource.Type = ts.Resource.Type - newTS.Resource.Labels = ts.Resource.Labels - ret.TimeSeries = append(ret.TimeSeries, newTS) - } - } - - return trimMetricLabels(ret), nil -} - -func (s *realStackdriver) ListLogEntries(filter LogType, namespace string) ([]*loggingpb.LogEntry, error) { - logName := logNameSuffix(filter) - resp, err := s.loggingService.Entries.List(&logging.ListLogEntriesRequest{ - ResourceNames: []string{fmt.Sprintf("projects/%v", s.projectID)}, - PageSize: 1000, - Filter: fmt.Sprintf("timestamp > %q AND logName:%q AND resource.labels.namespace_name=%q", - time.Now().Add(queryInterval).Format(time.RFC3339), logName, namespace), - }).Context(context.Background()).Do() - if err != nil { - return nil, fmt.Errorf("unexpected error from the logging backend: %v", err) - } - if resp.HTTPStatusCode != http.StatusOK { - return nil, fmt.Errorf("unexpected status code from logging service, got: %d", resp.HTTPStatusCode) - } - - resppb := loggingpb.ListLogEntriesResponse{ - Entries: make([]*loggingpb.LogEntry, len(resp.Entries)), - } - for i, le := range resp.Entries { - resppb.Entries[i] = &loggingpb.LogEntry{} - resppb.Entries[i].LogName = le.LogName - if le.TextPayload != "" { - resppb.Entries[i].Payload = &loggingpb.LogEntry_TextPayload{ - TextPayload: le.TextPayload, - } - } - if le.HttpRequest != nil { - resppb.Entries[i].HttpRequest = <ype.HttpRequest{} - resppb.Entries[i].HttpRequest.RequestMethod = le.HttpRequest.RequestMethod - resppb.Entries[i].HttpRequest.RequestUrl = le.HttpRequest.RequestUrl - resppb.Entries[i].HttpRequest.Status = int32(le.HttpRequest.Status) - resppb.Entries[i].HttpRequest.Protocol = le.HttpRequest.Protocol - } - resppb.Entries[i].Labels = le.Labels - resppb.Entries[i].TraceSampled = le.TraceSampled - } - return trimLogLabels(&resppb, filter), nil -} - -func (s *realStackdriver) ListTraces(namespace string) ([]*cloudtracepb.Trace, error) { - startTime := time.Now().Add(queryInterval) - listTracesResponse, err := s.traceService.Projects.Traces.List(s.projectID). - StartTime(startTime.Format(time.RFC3339)). - View("COMPLETE"). - Filter(fmt.Sprintf("istio.namespace:%q", namespace)). - Context(context.Background()). - PageSize(200). - Do() - if err != nil { - return nil, fmt.Errorf("unexpected error from the tracing backend: %v", err) - } - - ret := make([]*cloudtracepb.Trace, len(listTracesResponse.Traces)) - for i, t := range listTracesResponse.Traces { - ret[i] = &cloudtracepb.Trace{} - ret[i].ProjectId = t.ProjectId - ret[i].TraceId = t.TraceId - ret[i].Spans = make([]*cloudtracepb.TraceSpan, len(t.Spans)) - for j, s := range t.Spans { - ret[i].Spans[j] = &cloudtracepb.TraceSpan{} - ret[i].Spans[j].SpanId = s.SpanId - ret[i].Spans[j].Name = s.Name - ret[i].Spans[j].Labels = s.Labels - } - } - return ret, nil -} - -func (s *realStackdriver) GetStackdriverNamespace() string { - return "" -} - -func (s *realStackdriver) Address() string { - return "" -} diff --git a/pkg/test/framework/components/stackdriver/kube.go b/pkg/test/framework/components/stackdriver/kube.go deleted file mode 100644 index 64e59c91e..000000000 --- a/pkg/test/framework/components/stackdriver/kube.go +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package stackdriver - -import ( - "fmt" - "io" - "net" - "net/http" - "strings" - "time" -) - -import ( - "cloud.google.com/go/compute/metadata" - cloudtracepb "google.golang.org/genproto/googleapis/devtools/cloudtrace/v1" - ltype "google.golang.org/genproto/googleapis/logging/type" - loggingpb "google.golang.org/genproto/googleapis/logging/v2" - monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" - kubeApiCore "k8s.io/api/core/v1" -) - -import ( - istioKube "github.com/apache/dubbo-go-pixiu/pkg/kube" - environ "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/namespace" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - testKube "github.com/apache/dubbo-go-pixiu/pkg/test/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -type LogType int - -const ( - stackdriverNamespace = "istio-stackdriver" - stackdriverPort = 8091 -) - -const ( - ServerAccessLog LogType = iota - ServerAuditLog -) - -var ( - _ Instance = &kubeComponent{} - _ io.Closer = &kubeComponent{} -) - -type kubeComponent struct { - id resource.ID - ns namespace.Instance - forwarder istioKube.PortForwarder - cluster cluster.Cluster - address string -} - -func newKube(ctx resource.Context, cfg Config) (Instance, error) { - c := &kubeComponent{ - cluster: ctx.Clusters().GetOrDefault(cfg.Cluster), - } - c.id = ctx.TrackResource(c) - var err error - scopes.Framework.Info("=== BEGIN: Deploy Stackdriver ===") - defer func() { - if err != nil { - err = fmt.Errorf("stackdriver deployment failed: %v", err) - scopes.Framework.Infof("=== FAILED: Deploy Stackdriver ===") - _ = c.Close() - } else { - scopes.Framework.Info("=== SUCCEEDED: Deploy Stackdriver ===") - } - }() - - c.ns, err = namespace.New(ctx, namespace.Config{ - Prefix: stackdriverNamespace, - }) - if err != nil { - return nil, fmt.Errorf("could not create %s Namespace for Stackdriver install; err:%v", stackdriverNamespace, err) - } - - // apply stackdriver YAML - if err := c.cluster.ApplyYAMLFiles(c.ns.Name(), environ.StackdriverInstallFilePath); err != nil { - return nil, fmt.Errorf("failed to apply rendered %s, err: %v", environ.StackdriverInstallFilePath, err) - } - - fetchFn := testKube.NewSinglePodFetch(c.cluster, c.ns.Name(), "app=stackdriver") - pods, err := testKube.WaitUntilPodsAreReady(fetchFn) - if err != nil { - return nil, err - } - pod := pods[0] - - forwarder, err := c.cluster.NewPortForwarder(pod.Name, pod.Namespace, "", 0, stackdriverPort) - if err != nil { - return nil, err - } - - if err := forwarder.Start(); err != nil { - return nil, err - } - c.forwarder = forwarder - scopes.Framework.Debugf("initialized stackdriver port forwarder: %v", forwarder.Address()) - - var svc *kubeApiCore.Service - if svc, _, err = testKube.WaitUntilServiceEndpointsAreReady(c.cluster, c.ns.Name(), "stackdriver"); err != nil { - scopes.Framework.Infof("Error waiting for Stackdriver service to be available: %v", err) - return nil, err - } - - c.address = net.JoinHostPort(pod.Status.HostIP, fmt.Sprint(svc.Spec.Ports[0].NodePort)) - scopes.Framework.Infof("Stackdriver address: %s NodeName %s", c.address, pod.Spec.NodeName) - - return c, nil -} - -func (c *kubeComponent) ListTimeSeries(_ string) ([]*monitoringpb.TimeSeries, error) { - client := http.Client{ - Timeout: 5 * time.Second, - } - resp, err := client.Get("http://" + c.forwarder.Address() + "/timeseries") - if err != nil { - return []*monitoringpb.TimeSeries{}, err - } - defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - if err != nil { - return []*monitoringpb.TimeSeries{}, err - } - var r monitoringpb.ListTimeSeriesResponse - err = protomarshal.Unmarshal(body, &r) - if err != nil { - return []*monitoringpb.TimeSeries{}, err - } - return trimMetricLabels(&r), nil -} - -func (c *kubeComponent) ListLogEntries(lt LogType, _ string) ([]*loggingpb.LogEntry, error) { - client := http.Client{ - Timeout: 5 * time.Second, - } - - resp, err := client.Get("http://" + c.forwarder.Address() + "/logentries") - if err != nil { - return []*loggingpb.LogEntry{}, err - } - - defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - if err != nil { - return []*loggingpb.LogEntry{}, err - } - var r loggingpb.ListLogEntriesResponse - err = protomarshal.Unmarshal(body, &r) - if err != nil { - return []*loggingpb.LogEntry{}, err - } - return trimLogLabels(&r, lt), nil -} - -func (c *kubeComponent) ListTraces(_ string) ([]*cloudtracepb.Trace, error) { - client := http.Client{ - Timeout: 5 * time.Second, - } - resp, err := client.Get("http://" + c.forwarder.Address() + "/traces") - if err != nil { - return []*cloudtracepb.Trace{}, err - } - defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - if err != nil { - return []*cloudtracepb.Trace{}, err - } - var traceResp cloudtracepb.ListTracesResponse - err = protomarshal.Unmarshal(body, &traceResp) - if err != nil { - return []*cloudtracepb.Trace{}, err - } - - return traceResp.Traces, nil -} - -func (c *kubeComponent) ID() resource.ID { - return c.id -} - -// Close implements io.Closer. -func (c *kubeComponent) Close() error { - return nil -} - -func (c *kubeComponent) GetStackdriverNamespace() string { - return c.ns.Name() -} - -func (c *kubeComponent) Address() string { - return c.address -} - -func trimMetricLabels(r *monitoringpb.ListTimeSeriesResponse) []*monitoringpb.TimeSeries { - var ret []*monitoringpb.TimeSeries - for _, t := range r.TimeSeries { - if t == nil { - continue - } - t.Points = nil - if metadata.OnGCE() { - // If the test runs on GCE, only remove MR fields that do not need verification - delete(t.Resource.Labels, "cluster_name") - delete(t.Resource.Labels, "location") - delete(t.Resource.Labels, "project_id") - delete(t.Resource.Labels, "pod_name") - } else { - // Otherwise remove the whole MR since it is not correctly filled on other platform yet. - t.Resource = nil - } - ret = append(ret, t) - t.Metadata = nil - } - return ret -} - -func trimLogLabels(r *loggingpb.ListLogEntriesResponse, filter LogType) []*loggingpb.LogEntry { - logNameFilter := logNameSuffix(filter) - - var ret []*loggingpb.LogEntry - for _, l := range r.Entries { - if l == nil { - continue - } - if !strings.HasSuffix(l.LogName, logNameFilter) { - continue - } - // Remove fields that do not need verification - l.Timestamp = nil - l.Trace = "" - l.SpanId = "" - l.LogName = "" - l.Severity = ltype.LogSeverity_DEFAULT - if l.HttpRequest != nil { - l.HttpRequest.ResponseSize = 0 - l.HttpRequest.RequestSize = 0 - l.HttpRequest.ServerIp = "" - l.HttpRequest.RemoteIp = "" - l.HttpRequest.UserAgent = "" - l.HttpRequest.Latency = nil - } - delete(l.Labels, "request_id") - delete(l.Labels, "source_name") - delete(l.Labels, "destination_ip") - delete(l.Labels, "destination_name") - delete(l.Labels, "connection_id") - delete(l.Labels, "upstream_host") - delete(l.Labels, "connection_state") - delete(l.Labels, "source_ip") - delete(l.Labels, "source_port") - delete(l.Labels, "total_sent_bytes") - delete(l.Labels, "total_received_bytes") - ret = append(ret, l) - } - return ret -} - -func logNameSuffix(filter LogType) string { - switch filter { - case ServerAuditLog: - return "server-istio-audit-log" - case ServerAccessLog: - return "server-accesslog-stackdriver" - } - return "" -} diff --git a/pkg/test/framework/components/stackdriver/stackdriver.go b/pkg/test/framework/components/stackdriver/stackdriver.go deleted file mode 100644 index e8f5b9099..000000000 --- a/pkg/test/framework/components/stackdriver/stackdriver.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package stackdriver - -import ( - cloudtracepb "google.golang.org/genproto/googleapis/devtools/cloudtrace/v1" - loggingpb "google.golang.org/genproto/googleapis/logging/v2" - monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" -) - -import ( - md "github.com/apache/dubbo-go-pixiu/pkg/bootstrap/platform" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -// Instance represents a deployed Stackdriver app instance in a Kubernetes cluster. -type Instance interface { - Address() string - // Gets the namespace in which stackdriver is deployed. - GetStackdriverNamespace() string - ListTimeSeries(namespace string) ([]*monitoringpb.TimeSeries, error) - ListLogEntries(lt LogType, namespace string) ([]*loggingpb.LogEntry, error) - ListTraces(namespace string) ([]*cloudtracepb.Trace, error) -} - -type Config struct { - // Cluster to be used in a multicluster environment - Cluster cluster.Cluster -} - -// New returns a new instance of stackdriver. -func New(ctx resource.Context, c Config) (i Instance, err error) { - if UseRealStackdriver() { - return newRealStackdriver(ctx, c) - } - return newKube(ctx, c) -} - -// NewOrFail returns a new Stackdriver instance or fails test. -func NewOrFail(t test.Failer, ctx resource.Context, c Config, realSD bool) Instance { - t.Helper() - i, err := New(ctx, c) - if err != nil { - t.Fatalf("stackdriver.NewOrFail: %v", err) - } - - return i -} - -func UseRealStackdriver() bool { - // Use real stackdriver only if the test intends to AND the test is running on GCP. - // Currently real stackdriver only works if the test runs on GCP. - return framework.UseRealStackdriver && md.IsGCP() -} diff --git a/pkg/test/framework/components/stackdriver/stackdriver.yaml b/pkg/test/framework/components/stackdriver/stackdriver.yaml deleted file mode 100644 index 52894df9b..000000000 --- a/pkg/test/framework/components/stackdriver/stackdriver.yaml +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright 2020 Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -apiVersion: v1 -kind: Service -metadata: - name: stackdriver - labels: - app: stackdriver -spec: - # Setting to NodePort, so that it's accessible from all clusters. - type: NodePort - ports: - - name: grpc - port: 8090 - - name: http - port: 8091 - selector: - app: stackdriver ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: stackdriver -spec: - replicas: 1 - selector: - matchLabels: - app: stackdriver - template: - metadata: - labels: - app: stackdriver - spec: - containers: - # Image from https://github.com/istio/proxy/tree/master/test/envoye2e/stackdriver_plugin - - image: gcr.io/istio-testing/fake-stackdriver:7.0 - name: stackdriver - ports: - - containerPort: 8090 - - containerPort: 8091 - readinessProbe: - initialDelaySeconds: 2 - periodSeconds: 5 - httpGet: - port: 8091 - path: /timeseries diff --git a/pkg/test/framework/components/zipkin/kube.go b/pkg/test/framework/components/zipkin/kube.go deleted file mode 100644 index 0cfdda04e..000000000 --- a/pkg/test/framework/components/zipkin/kube.go +++ /dev/null @@ -1,326 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package zipkin - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "net" - "net/http" - "os" - "path/filepath" - "sort" - "strings" - "time" -) - -import ( - istioKube "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/istio" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - testKube "github.com/apache/dubbo-go-pixiu/pkg/test/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" -) - -const ( - appName = "zipkin" - tracesAPI = "/api/v2/traces?limit=%d&spanName=%s&annotationQuery=%s" - zipkinPort = 9411 - - remoteZipkinEntry = ` -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: tracing-gateway - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 80 - name: http-tracing - protocol: HTTP - hosts: - - "tracing.{INGRESS_DOMAIN}" - - port: - number: 9411 - name: http-tracing-span - protocol: HTTP - hosts: - - "tracing.{INGRESS_DOMAIN}" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: tracing-vs - namespace: dubbo-system -spec: - hosts: - - "tracing.{INGRESS_DOMAIN}" - gateways: - - tracing-gateway - http: - - match: - - port: 80 - route: - - destination: - host: tracing - port: - number: 80 - - match: - - port: 9411 - route: - - destination: - host: tracing - port: - number: 9411 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: tracing - namespace: dubbo-system -spec: - host: tracing - trafficPolicy: - tls: - mode: DISABLE ----` - - extServiceEntry = ` -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: zipkin -spec: - hosts: - # must be of form name.namespace.global - - zipkin.dubbo-system.global - # Treat remote cluster services as part of the service mesh - # as all clusters in the service mesh share the same root of trust. - location: MESH_INTERNAL - ports: - - name: http-tracing-span - number: 9411 - protocol: http - resolution: DNS - addresses: - # the IP address to which zipkin.dubbo-system.global will resolve to - # must be unique for each remote service, within a given cluster. - # This address need not be routable. Traffic for this IP will be captured - # by the sidecar and routed appropriately. - - 240.0.0.2 - endpoints: - # This is the routable address of the ingress gateway in cluster1 that - # sits in front of zipkin service. Traffic from the sidecar will be - # routed to this address. - - address: {INGRESS_DOMAIN} - ports: - http-tracing-span: 15443 # Do not change this port value -` -) - -var ( - _ Instance = &kubeComponent{} - _ io.Closer = &kubeComponent{} -) - -type kubeComponent struct { - id resource.ID - address string - forwarder istioKube.PortForwarder - cluster cluster.Cluster -} - -func getZipkinYaml() (string, error) { - yamlBytes, err := os.ReadFile(filepath.Join(env.IstioSrc, "samples/addons/extras/zipkin.yaml")) - if err != nil { - return "", err - } - yaml := string(yamlBytes) - return yaml, nil -} - -func installZipkin(ctx resource.Context, ns string) error { - yaml, err := getZipkinYaml() - if err != nil { - return err - } - return ctx.ConfigKube().YAML(ns, yaml).Apply() -} - -func installServiceEntry(ctx resource.Context, ns, ingressAddr string) error { - // Setup remote access to zipkin in cluster - yaml := strings.ReplaceAll(remoteZipkinEntry, "{INGRESS_DOMAIN}", ingressAddr) - err := ctx.ConfigIstio().YAML(ns, yaml).Apply() - if err != nil { - return err - } - yaml = strings.ReplaceAll(extServiceEntry, "{INGRESS_DOMAIN}", ingressAddr) - err = ctx.ConfigIstio().YAML(ns, yaml).Apply() - if err != nil { - return err - } - return nil -} - -func newKube(ctx resource.Context, cfgIn Config) (Instance, error) { - c := &kubeComponent{ - cluster: ctx.Clusters().GetOrDefault(cfgIn.Cluster), - } - c.id = ctx.TrackResource(c) - - // Find the zipkin pod and service, and start forwarding a local port. - cfg, err := istio.DefaultConfig(ctx) - if err != nil { - return nil, err - } - - if err := installZipkin(ctx, cfg.TelemetryNamespace); err != nil { - return nil, err - } - - fetchFn := testKube.NewSinglePodFetch(c.cluster, cfg.SystemNamespace, fmt.Sprintf("app=%s", appName)) - pods, err := testKube.WaitUntilPodsAreReady(fetchFn) - if err != nil { - return nil, err - } - pod := pods[0] - - forwarder, err := c.cluster.NewPortForwarder(pod.Name, pod.Namespace, "", 0, zipkinPort) - if err != nil { - return nil, err - } - - if err := forwarder.Start(); err != nil { - return nil, err - } - c.forwarder = forwarder - scopes.Framework.Debugf("initialized zipkin port forwarder: %v", forwarder.Address()) - - isIP := net.ParseIP(cfgIn.IngressAddr).String() != "" - ingressDomain := cfgIn.IngressAddr - if isIP { - ingressDomain = fmt.Sprintf("%s.sslip.io", strings.ReplaceAll(cfgIn.IngressAddr, ":", "-")) - } - - c.address = fmt.Sprintf("http://tracing.%s", ingressDomain) - scopes.Framework.Debugf("Zipkin address: %s ", c.address) - err = installServiceEntry(ctx, cfg.TelemetryNamespace, ingressDomain) - if err != nil { - return nil, err - } - return c, nil -} - -func (c *kubeComponent) ID() resource.ID { - return c.id -} - -func (c *kubeComponent) QueryTraces(limit int, spanName, annotationQuery string) ([]Trace, error) { - // Get 100 most recent traces - client := http.Client{ - Timeout: 5 * time.Second, - } - scopes.Framework.Debugf("make get call to zipkin api %v", c.address+fmt.Sprintf(tracesAPI, limit, spanName, annotationQuery)) - resp, err := client.Get(c.address + fmt.Sprintf(tracesAPI, limit, spanName, annotationQuery)) - if err != nil { - scopes.Framework.Debugf("zipking err %v", err) - return nil, err - } - if resp.StatusCode != http.StatusOK { - scopes.Framework.Debugf("response err %v", resp.StatusCode) - return nil, fmt.Errorf("zipkin api returns non-ok: %v", resp.StatusCode) - } - defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - traces, err := extractTraces(body) - if err != nil { - return nil, err - } - return traces, nil -} - -// Close implements io.Closer. -func (c *kubeComponent) Close() error { - if c.forwarder != nil { - c.forwarder.Close() - } - return nil -} - -func extractTraces(resp []byte) ([]Trace, error) { - var traceObjs []interface{} - if err := json.Unmarshal(resp, &traceObjs); err != nil { - return []Trace{}, err - } - var ret []Trace - for _, t := range traceObjs { - spanObjs, ok := t.([]interface{}) - if !ok || len(spanObjs) == 0 { - scopes.Framework.Debugf("cannot parse or cannot find spans in trace object %+v", t) - continue - } - var spans []Span - for _, obj := range spanObjs { - newSpan := buildSpan(obj) - spans = append(spans, newSpan) - } - for p := range spans { - for c := range spans { - if spans[c].ParentSpanID == spans[p].SpanID { - spans[p].ChildSpans = append(spans[p].ChildSpans, &spans[c]) - } - } - // make order of child spans deterministic - sort.Slice(spans[p].ChildSpans, func(i, j int) bool { - return spans[p].ChildSpans[i].Name < spans[p].ChildSpans[j].Name - }) - } - ret = append(ret, Trace{Spans: spans}) - } - if len(ret) > 0 { - return ret, nil - } - return []Trace{}, errors.New("cannot find any traces") -} - -func buildSpan(obj interface{}) Span { - var s Span - spanSpec := obj.(map[string]interface{}) - if spanID, ok := spanSpec["id"]; ok { - s.SpanID = spanID.(string) - } - if parentSpanID, ok := spanSpec["parentId"]; ok { - s.ParentSpanID = parentSpanID.(string) - } - if endpointObj, ok := spanSpec["localEndpoint"]; ok { - if em, ok := endpointObj.(map[string]interface{}); ok { - s.ServiceName = em["serviceName"].(string) - } - } - if name, ok := spanSpec["name"]; ok { - s.Name = name.(string) - } - return s -} diff --git a/pkg/test/framework/components/zipkin/zipkin.go b/pkg/test/framework/components/zipkin/zipkin.go deleted file mode 100644 index 96b928a52..000000000 --- a/pkg/test/framework/components/zipkin/zipkin.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package zipkin - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -// Instance represents a zipkin deployment on kube -type Instance interface { - resource.Resource - - // QueryTraces gets at most number of limit most recent available traces from zipkin. - // spanName filters that only trace with the given span name will be included. - QueryTraces(limit int, spanName, annotationQuery string) ([]Trace, error) -} - -type Config struct { - // Cluster to be used in a multicluster environment - Cluster cluster.Cluster - - // HTTP Address of ingress gateway of the cluster to be used to install zipkin in. - IngressAddr string -} - -// Span represents a single span, which includes span attributes for verification -// TODO(bianpengyuan) consider using zipkin proto api https://github.com/istio/istio/issues/13926 -type Span struct { - SpanID string - ParentSpanID string - ServiceName string - Name string - ChildSpans []*Span -} - -// Trace represents a trace by a collection of spans which all belong to that trace -type Trace struct { - Spans []Span -} - -// New returns a new instance of zipkin. -func New(ctx resource.Context, c Config) (i Instance, err error) { - return newKube(ctx, c) -} - -// NewOrFail returns a new zipkin instance or fails test. -func NewOrFail(t *testing.T, ctx resource.Context, c Config) Instance { - t.Helper() - i, err := New(ctx, c) - if err != nil { - t.Fatalf("zipkin.NewOrFail: %v", err) - } - - return i -} diff --git a/pkg/test/framework/config.go b/pkg/test/framework/config.go deleted file mode 100644 index 0f723af03..000000000 --- a/pkg/test/framework/config.go +++ /dev/null @@ -1,298 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package framework - -import ( - "context" - "fmt" - "strings" -) - -import ( - "github.com/hashicorp/go-multierror" - "go.uber.org/atomic" - "golang.org/x/sync/errgroup" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/istioctl" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/file" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/tmpl" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/yml" -) - -var _ resource.ConfigManager = &configManager{} - -type configManager struct { - ctx resource.Context - clusters []cluster.Cluster - prefix string -} - -func newConfigManager(ctx resource.Context, clusters cluster.Clusters) resource.ConfigManager { - if len(clusters) == 0 { - clusters = ctx.Clusters() - } - return &configManager{ - ctx: ctx, - clusters: clusters.Kube(), - } -} - -// GlobalYAMLWrites records how many YAMLs we have applied from all sources. -// Note: go tests are distinct binaries per test suite, so this is the suite level number of calls -var GlobalYAMLWrites = atomic.NewUint64(0) - -func (c *configManager) New() resource.Config { - return &configImpl{ - configManager: c, - yamlText: make(map[string][]string), - } -} - -func (c *configManager) YAML(ns string, yamlText ...string) resource.Config { - return c.New().YAML(ns, yamlText...) -} - -func (c *configManager) Eval(ns string, args interface{}, yamlTemplates ...string) resource.Config { - return c.New().Eval(ns, args, yamlTemplates...) -} - -func (c *configManager) File(ns string, filePaths ...string) resource.Config { - return c.New().File(ns, filePaths...) -} - -func (c *configManager) EvalFile(ns string, args interface{}, filePaths ...string) resource.Config { - return c.New().EvalFile(ns, args, filePaths...) -} - -func (c *configManager) applyYAML(cleanup bool, ns string, yamlText ...string) error { - if len(c.prefix) == 0 { - return c.WithFilePrefix("apply").(*configManager).applyYAML(cleanup, ns, yamlText...) - } - GlobalYAMLWrites.Add(uint64(len(yamlText))) - - // Convert the content to files. - yamlFiles, err := c.ctx.WriteYAML(c.prefix, yamlText...) - if err != nil { - return err - } - - g, _ := errgroup.WithContext(context.TODO()) - for _, cl := range c.clusters { - cl := cl - g.Go(func() error { - scopes.Framework.Debugf("Applying to %s to namespace %v: %s", cl.StableName(), ns, strings.Join(yamlFiles, ", ")) - if err := cl.ApplyYAMLFiles(ns, yamlFiles...); err != nil { - return fmt.Errorf("failed applying YAML files %v to ns %s in cluster %s: %v", yamlFiles, ns, cl.Name(), err) - } - if cleanup { - c.ctx.Cleanup(func() { - scopes.Framework.Debugf("Deleting from %s: %s", cl.StableName(), strings.Join(yamlFiles, ", ")) - if err := cl.DeleteYAMLFiles(ns, yamlFiles...); err != nil { - scopes.Framework.Errorf("failed deleting YAML files %v from ns %s in cluster %s: %v", yamlFiles, ns, cl.Name(), err) - } - }) - } - return nil - }) - } - return g.Wait() -} - -func (c *configManager) deleteYAML(ns string, yamlText ...string) error { - if len(c.prefix) == 0 { - return c.WithFilePrefix("delete").(*configManager).deleteYAML(ns, yamlText...) - } - - // Convert the content to files. - yamlFiles, err := c.ctx.WriteYAML(c.prefix, yamlText...) - if err != nil { - return err - } - - g, _ := errgroup.WithContext(context.TODO()) - for _, c := range c.clusters { - c := c - g.Go(func() error { - if err := c.DeleteYAMLFiles(ns, yamlFiles...); err != nil { - return fmt.Errorf("failed deleting YAML from cluster %s: %v", c.Name(), err) - } - return nil - }) - } - return g.Wait() -} - -func (c *configManager) WaitForConfig(ctx resource.Context, ns string, yamlText ...string) error { - var outErr error - for _, c := range c.ctx.Clusters() { - ik, err := istioctl.New(ctx, istioctl.Config{Cluster: c}) - if err != nil { - return err - } - - for _, config := range yamlText { - config := config - - // TODO(https://github.com/istio/istio/issues/37324): It's currently unsafe - // to call istioctl concurrently since it relies on the istioctl library - // (rather than calling the binary from the command line) which uses a number - // of global variables, which will be overwritten for each call. - if err := ik.WaitForConfig(ns, config); err != nil { - // Get proxy status for additional debugging - s, _, _ := ik.Invoke([]string{"ps"}) - outErr = multierror.Append(err, fmt.Errorf("failed waiting for config for cluster %s: err=%v. Proxy status: %v", - c.StableName(), err, s)) - } - } - } - return outErr -} - -func (c *configManager) WaitForConfigOrFail(ctx resource.Context, t test.Failer, ns string, yamlText ...string) { - err := c.WaitForConfig(ctx, ns, yamlText...) - if err != nil { - // TODO(https://github.com/istio/istio/issues/37148) fail hard in this case - t.Log(err) - } -} - -func (c *configManager) WithFilePrefix(prefix string) resource.ConfigManager { - return &configManager{ - ctx: c.ctx, - prefix: prefix, - clusters: c.clusters, - } -} - -var _ resource.Config = &configImpl{} - -type configImpl struct { - *configManager - yamlText map[string][]string -} - -func (c *configImpl) Copy() resource.Config { - yamlText := make(map[string][]string, len(c.yamlText)) - for k, v := range c.yamlText { - yamlText[k] = append([]string{}, v...) - } - return &configImpl{ - configManager: c.configManager, - yamlText: yamlText, - } -} - -func (c *configImpl) YAML(ns string, yamlText ...string) resource.Config { - c.yamlText[ns] = append(c.yamlText[ns], splitYAML(yamlText...)...) - return c -} - -func splitYAML(yamlText ...string) []string { - var out []string - for _, doc := range yamlText { - out = append(out, yml.SplitString(doc)...) - } - return out -} - -func (c *configImpl) File(ns string, paths ...string) resource.Config { - yamlText, err := file.AsStringArray(paths...) - if err != nil { - panic(err) - } - - return c.YAML(ns, yamlText...) -} - -func (c *configImpl) Eval(ns string, args interface{}, templates ...string) resource.Config { - return c.YAML(ns, tmpl.MustEvaluateAll(args, templates...)...) -} - -func (c *configImpl) EvalFile(ns string, args interface{}, paths ...string) resource.Config { - templates, err := file.AsStringArray(paths...) - if err != nil { - panic(err) - } - - return c.Eval(ns, args, templates...) -} - -func (c *configImpl) Apply(opts ...resource.ConfigOption) error { - // Apply the options. - options := resource.ConfigOptions{} - for _, o := range opts { - o(&options) - } - - // Apply for each namespace concurrently. - g, _ := errgroup.WithContext(context.TODO()) - for ns, y := range c.yamlText { - ns, y := ns, y - g.Go(func() error { - return c.applyYAML(!options.NoCleanup, ns, y...) - }) - } - - // Wait for all each apply to complete. - if err := g.Wait(); err != nil { - return err - } - - if options.Wait { - // TODO: wait for each namespace concurrently once WaitForConfig supports concurrency. - for ns, y := range c.yamlText { - if err := c.WaitForConfig(c.ctx, ns, y...); err != nil { - // TODO(https://github.com/istio/istio/issues/37148) fail hard in this case - scopes.Framework.Warnf("(Ignored until https://github.com/istio/istio/issues/37148 is fixed) "+ - "failed waiting for YAML %v: %v", y, err) - } - } - } - return nil -} - -func (c *configImpl) ApplyOrFail(t test.Failer, opts ...resource.ConfigOption) { - t.Helper() - if err := c.Apply(opts...); err != nil { - t.Fatal(err) - } -} - -func (c *configImpl) Delete() error { - // Delete for each namespace concurrently. - g, _ := errgroup.WithContext(context.TODO()) - for ns, y := range c.yamlText { - ns, y := ns, y - g.Go(func() error { - return c.deleteYAML(ns, y...) - }) - } - - // Wait for all each delete to complete. - return g.Wait() -} - -func (c *configImpl) DeleteOrFail(t test.Failer) { - t.Helper() - if err := c.Delete(); err != nil { - t.Fatal(err) - } -} diff --git a/pkg/test/framework/config/config.go b/pkg/test/framework/config/config.go deleted file mode 100644 index 04d94e2bc..000000000 --- a/pkg/test/framework/config/config.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package config - -import ( - "flag" - "fmt" - "os" - "strings" -) - -import ( - "go.uber.org/atomic" - "gopkg.in/yaml.v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/file" -) - -const prefix = "istio.test" - -var ( - configFilePath string - parsed atomic.Bool -) - -func init() { - flag.StringVar(&configFilePath, "istio.test.config", "", "Path to test framework config file") -} - -type Value interface { - flag.Value - // SetConfig will receive either a Map or a []Map - SetConfig(interface{}) error -} - -func Parsed() bool { - return flag.Parsed() && parsed.Load() -} - -// Parse overrides any unset command line flags beginning with "istio.test" with values provided -// from a YAML file. -func Parse() { - defer func() { - parsed.Store(true) - }() - if !flag.Parsed() { - flag.Parse() - } - if configFilePath == "" { - return - } - - cfg, err := readConfig() - if err != nil { - scopes.Framework.Error(err) - return - } - set := map[string]struct{}{} - flag.Visit(func(f *flag.Flag) { - set[f.Name] = struct{}{} - }) - - flag.VisitAll(func(f *flag.Flag) { - var err error - defer func() { - if err != nil { - scopes.Framework.Errorf("failed getting %s from config file: %v", f.Name, err) - } - }() - - // exclude non-istio flags and flags that were set via command line - if !strings.HasPrefix(f.Name, prefix) { - return - } - if _, ok := set[f.Name]; ok { - return - } - - // grab the map containing the last "." separated key - keys := strings.Split(f.Name, ".") - parentPath, key := keys[:len(keys)-1], keys[len(keys)-1] - parent := cfg - for _, k := range parentPath { - parent = parent.Map(k) - if parent == nil { - return - } - } - - // if the registered flag implements config.Value, and is a non-string type, we can do fancy custom parsing - cfgValue, isCfgVal := f.Value.(Value) - if cfgMap := parent.Map(key); isCfgVal && len(cfgMap) > 0 { - err = cfgValue.SetConfig(cfgMap) - } else if cfgSlice := parent.Slice(key); isCfgVal && len(cfgSlice) > 0 { - err = cfgValue.SetConfig(cfgSlice) - } else if v := parent.String(key); v != "" { - // otherwise parse via string (if-set) - err = f.Value.Set(v) - } - }) -} - -func readConfig() (Map, error) { - path, err := file.NormalizePath(configFilePath) - if err != nil { - return nil, fmt.Errorf("failed normalizing config file path %q: %v", configFilePath, err) - } - bytes, err := os.ReadFile(path) - if err != nil { - return nil, fmt.Errorf("failed reading %s: %v", path, err) - } - cfg := Map{} - if err := yaml.Unmarshal(bytes, cfg); err != nil { - return nil, fmt.Errorf("failed unmarshalling %s: %v", path, err) - } - return cfg, nil -} diff --git a/pkg/test/framework/config/map.go b/pkg/test/framework/config/map.go deleted file mode 100644 index 71714ab6d..000000000 --- a/pkg/test/framework/config/map.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package config - -import ( - "fmt" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" -) - -type Map map[string]interface{} - -func (m Map) Map(key string) Map { - nested, ok := m[key] - if !ok { - return nil - } - out, ok := nested.(Map) - if !ok { - return nil - } - return out -} - -func (m Map) String(key string) string { - v, ok := m[key] - if !ok || v == nil { - return "" - } - str, ok := v.(string) - if !ok { - return fmt.Sprint(m[key]) - } - return str -} - -func (m Map) Slice(key string) []Map { - v, ok := m[key].([]interface{}) - if !ok { - return nil - } - var out []Map - for i, imeta := range v { - meta, ok := toMap(imeta) - if !ok { - scopes.Framework.Warnf("failed to parse item %d of %s, defaulting to empty: %v", i, key, imeta) - return nil - } - out = append(out, meta) - } - return out -} - -func toMap(orig interface{}) (Map, bool) { - // keys are strings, easily cast - if cfgMeta, ok := orig.(Map); ok { - return cfgMeta, true - } - // keys are interface{}, manually change to string keys - mapInterface, ok := orig.(map[interface{}]interface{}) - if !ok { - // not a map at all - return nil, false - } - mapString := make(map[string]interface{}) - for key, value := range mapInterface { - mapString[fmt.Sprintf("%v", key)] = value - } - return mapString, true -} - -func (m Map) Bool(key string) *bool { - if m[key] == nil { - return nil - } - v, ok := m[key].(bool) - if !ok { - scopes.Framework.Warnf("failed to parse key of type %T value %q as bool, defaulting to false", m[key], key) - return nil - } - return &v -} - -func (m Map) Int(key string) int { - if m[key] == nil { - return 0 - } - v, ok := m[key].(int) - if !ok { - scopes.Framework.Warnf("failed to parse key of type %T value %q as int, defaulting to 0", m[key], key) - return 0 - } - return v -} diff --git a/pkg/test/framework/errors/deprecations.go b/pkg/test/framework/errors/deprecations.go deleted file mode 100644 index 0175a10ca..000000000 --- a/pkg/test/framework/errors/deprecations.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package errors - -import ( - "bufio" - "fmt" - "strings" -) - -import ( - "github.com/hashicorp/go-multierror" -) - -type DeprecatedError struct { - msg string -} - -func NewDeprecatedError(format string, args ...interface{}) error { - return &DeprecatedError{fmt.Sprintf(format, args...)} -} - -func IsDeprecatedError(err error) bool { - _, ok := err.(*DeprecatedError) - return ok -} - -func IsOrContainsDeprecatedError(err error) bool { - if IsDeprecatedError(err) { - return true - } - - if m, ok := err.(*multierror.Error); ok { - for _, e := range m.Errors { - if IsDeprecatedError(e) { - return true - } - } - } - - return false -} - -func (de *DeprecatedError) Error() string { - return de.msg -} - -// FindDeprecatedMessagesInEnvoyLog looks for deprecated messages in the `logs` parameter. If found, it will return -// a DeprecatedError. Use `extraInfo` to pass additional info, like pod namespace/name, etc. -func FindDeprecatedMessagesInEnvoyLog(logs, extraInfo string) error { - scanner := bufio.NewScanner(strings.NewReader(logs)) - for scanner.Scan() { - line := scanner.Text() - if strings.Contains(strings.ToLower(line), "deprecated") { - if len(extraInfo) > 0 { - extraInfo = fmt.Sprintf(" (%s)", extraInfo) - } - return NewDeprecatedError("usage of deprecated stuff in Envoy%s: %s", extraInfo, line) - } - } - - return nil -} diff --git a/pkg/test/framework/features/README.md b/pkg/test/framework/features/README.md deleted file mode 100644 index bd630dbd2..000000000 --- a/pkg/test/framework/features/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# Feature Coverage Reporting - -## Overview - -The features package defines values that are used to track feature coverage at eng.istio.io - -## Labeling a Test - -Labeling a test with a feature and scenario allow release managers and others to understand what value this test case is adding. To label a test, call the Feature() function on the Test Object, with a string identifying the feature and scenario you are testing. These parameters should be formatted like featurepath.scenariopath, where both feature path and scenario path are dot delimited heirarchies, and the featurepath is a leaf node in features.yaml. If you are testing a feature whose path does not yet exist in features,yaml, see the next section for how to define one. - -## Adding New Feature Constants - -For consistency, features must be registered in [features.yaml](features.yaml), or your test will fail. Each entry in this file will be equivalent to a dot delimited feature label. For instance: - -```yaml - usability: - observability: - status: - analyzers: - virtualservice: -``` - -will allow you to label a test with: - -```go - "usability.observability.status.exist-by-default" - "usability.analyzers.virtualservice" -``` - -where "usability.observability.status" is the feature, and "exist-by-default" is the scenario in the first case, and the second case has no scenario. - -The heirarchical nature of feature labels allows us to aggregate data about related feature sets. To provide for consistent reporting and aggregation, we have defined the top two layers of hierarchy, and ask that you place your feature under these headings. For more detail on the purpose of each heading, see Top-Level Feature Headings below. If you feel that none of the existing headings fit your feature, please check with the Test and Release Working Group before adding a new heading. - -## Writing a Test Stub - -Test stubs are a valuable way to indicate that a feature should be tested, but isn't yet. When writing a design doc, a set of test stubs can represent your Test Plan. You can also use stubs to document features whose lack of testing needs to be visible in our feature coverage dashboard. - -To write a stub, simply create a test object and call NotImplementedYet, passing the label of any features this test should cover. This test gap will now appear in our feature coverage dashboards, which give release managers a better understanding of what is and isn't tested in a given release. If you are implementing a test stub with no immediate plans to implement the test, it's a best practice to create a tracking issue as well. If your test stub uses a new feature constant, be sure to follow the instructions above to update our list of features. - -```go - func TestExample(t *testing.T) { - framework.NewTest(t).NotImplementedYet("my.feature.string") - } -``` \ No newline at end of file diff --git a/pkg/test/framework/features/allowlist.txt b/pkg/test/framework/features/allowlist.txt deleted file mode 100644 index acdf32e89..000000000 --- a/pkg/test/framework/features/allowlist.txt +++ /dev/null @@ -1,1538 +0,0 @@ -// This file contains all test cases known to exist before 6/8/2020 -// It will be used to allow old test cases to lack feature labels, -// while failing on new tests that don't use labels. -authn_policy,TestAuthnPolicy -authz_deny,TestAuthzDeny -authz_ingress,TestAuthzIngress -authz_jwt,TestAuthzJWT -authz_tcp,TestAuthzTCP -cert_mtls_test,TestCertMtls -cert_provision_prometheus,TestPrometheusCert -client_tracing_test,TestClientTracing -cni,TestCNIReachability -cni,TestCNIReachability/global-mtls-on -cni,TestCNIReachability/global-mtls-on/a->grpc://a:grpc -cni,TestCNIReachability/global-mtls-on/a->grpc://b:grpc -cni,TestCNIReachability/global-mtls-on/a->grpc://headless:grpc -cni,TestCNIReachability/global-mtls-on/a->grpc://multiversion:grpc -cni,TestCNIReachability/global-mtls-on/a->grpc://naked:grpc -cni,TestCNIReachability/global-mtls-on/a->http://a:http -cni,TestCNIReachability/global-mtls-on/a->http://b:http -cni,TestCNIReachability/global-mtls-on/a->http://headless:http -cni,TestCNIReachability/global-mtls-on/a->http://multiversion:http -cni,TestCNIReachability/global-mtls-on/a->http://naked:http -cni,TestCNIReachability/global-mtls-on/a->tcp://a:tcp -cni,TestCNIReachability/global-mtls-on/a->tcp://b:tcp -cni,TestCNIReachability/global-mtls-on/a->tcp://multiversion:tcp -cni,TestCNIReachability/global-mtls-on/a->tcp://naked:tcp -cni,TestCNIReachability/global-mtls-on/a->ws://a:http -cni,TestCNIReachability/global-mtls-on/a->ws://b:http -cni,TestCNIReachability/global-mtls-on/a->ws://headless:http -cni,TestCNIReachability/global-mtls-on/a->ws://multiversion:http -cni,TestCNIReachability/global-mtls-on/a->ws://naked:http -cni,TestCNIReachability/global-mtls-on/b->grpc://a:grpc -cni,TestCNIReachability/global-mtls-on/b->grpc://b:grpc -cni,TestCNIReachability/global-mtls-on/b->grpc://headless:grpc -cni,TestCNIReachability/global-mtls-on/b->grpc://multiversion:grpc -cni,TestCNIReachability/global-mtls-on/b->grpc://naked:grpc -cni,TestCNIReachability/global-mtls-on/b->http://a:http -cni,TestCNIReachability/global-mtls-on/b->http://b:http -cni,TestCNIReachability/global-mtls-on/b->http://headless:http -cni,TestCNIReachability/global-mtls-on/b->http://multiversion:http -cni,TestCNIReachability/global-mtls-on/b->http://naked:http -cni,TestCNIReachability/global-mtls-on/b->tcp://a:tcp -cni,TestCNIReachability/global-mtls-on/b->tcp://b:tcp -cni,TestCNIReachability/global-mtls-on/b->tcp://multiversion:tcp -cni,TestCNIReachability/global-mtls-on/b->tcp://naked:tcp -cni,TestCNIReachability/global-mtls-on/b->ws://a:http -cni,TestCNIReachability/global-mtls-on/b->ws://b:http -cni,TestCNIReachability/global-mtls-on/b->ws://headless:http -cni,TestCNIReachability/global-mtls-on/b->ws://multiversion:http -cni,TestCNIReachability/global-mtls-on/b->ws://naked:http -cni,TestCNIReachability/global-mtls-on/headless->grpc://a:grpc -cni,TestCNIReachability/global-mtls-on/headless->grpc://b:grpc -cni,TestCNIReachability/global-mtls-on/headless->grpc://headless:grpc -cni,TestCNIReachability/global-mtls-on/headless->grpc://multiversion:grpc -cni,TestCNIReachability/global-mtls-on/headless->grpc://naked:grpc -cni,TestCNIReachability/global-mtls-on/headless->http://a:http -cni,TestCNIReachability/global-mtls-on/headless->http://b:http -cni,TestCNIReachability/global-mtls-on/headless->http://headless:http -cni,TestCNIReachability/global-mtls-on/headless->http://multiversion:http -cni,TestCNIReachability/global-mtls-on/headless->http://naked:http -cni,TestCNIReachability/global-mtls-on/headless->tcp://a:tcp -cni,TestCNIReachability/global-mtls-on/headless->tcp://b:tcp -cni,TestCNIReachability/global-mtls-on/headless->tcp://multiversion:tcp -cni,TestCNIReachability/global-mtls-on/headless->tcp://naked:tcp -cni,TestCNIReachability/global-mtls-on/headless->ws://a:http -cni,TestCNIReachability/global-mtls-on/headless->ws://b:http -cni,TestCNIReachability/global-mtls-on/headless->ws://headless:http -cni,TestCNIReachability/global-mtls-on/headless->ws://multiversion:http -cni,TestCNIReachability/global-mtls-on/headless->ws://naked:http -cni,TestCNIReachability/global-mtls-on/naked->grpc://a:grpc -cni,TestCNIReachability/global-mtls-on/naked->grpc://b:grpc -cni,TestCNIReachability/global-mtls-on/naked->grpc://headless:grpc -cni,TestCNIReachability/global-mtls-on/naked->grpc://multiversion:grpc -cni,TestCNIReachability/global-mtls-on/naked->grpc://naked:grpc -cni,TestCNIReachability/global-mtls-on/naked->http://a:http -cni,TestCNIReachability/global-mtls-on/naked->http://b:http -cni,TestCNIReachability/global-mtls-on/naked->http://headless:http -cni,TestCNIReachability/global-mtls-on/naked->http://multiversion:http -cni,TestCNIReachability/global-mtls-on/naked->http://naked:http -cni,TestCNIReachability/global-mtls-on/naked->tcp://a:tcp -cni,TestCNIReachability/global-mtls-on/naked->tcp://b:tcp -cni,TestCNIReachability/global-mtls-on/naked->tcp://multiversion:tcp -cni,TestCNIReachability/global-mtls-on/naked->tcp://naked:tcp -cni,TestCNIReachability/global-mtls-on/naked->ws://a:http -cni,TestCNIReachability/global-mtls-on/naked->ws://b:http -cni,TestCNIReachability/global-mtls-on/naked->ws://headless:http -cni,TestCNIReachability/global-mtls-on/naked->ws://multiversion:http -cni,TestCNIReachability/global-mtls-on/naked->ws://naked:http -configuration,TestHealthCheck -conformance_test,TestConformance -conformance_test,TestConformance/config/adapter/basic -conformance_test,TestConformance/config/quotaspec/basic -conformance_test,TestConformance/config/quotaspecbinding/basic -conformance_test,TestConformance/config/template/basic -conformance_test,TestConformance/k8s/ingress/basic -conformance_test,TestConformance/k8s/ingress/merge -conformance_test,TestConformance/k8s/ingress/merge/0 -conformance_test,TestConformance/k8s/ingress/merge/1 -conformance_test,TestConformance/networking/destinationRule/basic -conformance_test,TestConformance/networking/envoyFilter/basic -conformance_test,TestConformance/networking/gateway/basic -conformance_test,TestConformance/networking/serviceEntry/basic -conformance_test,TestConformance/networking/sidecar/basic -conformance_test,TestConformance/networking/traffic -conformance_test,TestConformance/networking/traffic/0 -conformance_test,TestConformance/networking/traffic/1 -conformance_test,TestConformance/networking/traffic/2 -conformance_test,TestConformance/networking/traffic/3 -conformance_test,TestConformance/networking/traffic/4 -conformance_test,TestConformance/networking/traffic/5 -conformance_test,TestConformance/networking/traffic/6 -conformance_test,TestConformance/networking/virtualService/basic -conformance_test,TestConformance/policy/attributemanifests/basic -conformance_test,TestConformance/policy/handlers/basic -conformance_test,TestConformance/policy/httpapispecbindings/basic -conformance_test,TestConformance/policy/httpapispecs/basic -conformance_test,TestConformance/policy/instance/basic -conformance_test,TestConformance/policy/rules/basic -conformance_test,TestConformance/rbac/clusterRbacConfig/basic -conformance_test,TestConformance/rbac/rbacConfig/basic -conformance_test,TestConformance/rbac/serviceRole/basic -conformance_test,TestConformance/rbac/serviceRoleBinding/basic -conformance_test,TestConformance/security/authorizationPolicy/basic -conformance_test,TestMissingMCPTests -dns_cert,TestDNSCert -dns_certificate_test,TestDNSCertificate -dns_certificate_test,TestDNSCertificate/generateDNSCertificates -dns_certificate_test,TestDNSCertificate/regenerateDNSCertificates -dns_certificate_test,TestDNSCertificate/rotateDNSCertificatesWhenCAUpdated -dns_certificate_test,TestDNSCertificate/rotateDNSCertificatesWhenCertExpired -envoyfilter_test,TestEnvoyFilterHTTPFilterInsertBefore -examples,TestBookinfo -framework,TestStyle1 -framework,TestStyle2 -framework_test,TestParallel -framework_test,TestParallel/top -framework_test,TestParallel/top/l1a -framework_test,TestParallel/top/l1a/l2a -framework_test,TestParallel/top/l1a/l2b -framework_test,TestParallel/top/l1b -framework_test,TestParallel/top/l1b/l2c -framework_test,TestParallel/top/l1b/l2d -framework_test,TestStyle1 -framework_test,TestStyle2 -pilot,TestAllNamespaces -pilot,TestDirectoryWithoutRecursion -pilot,TestDirectoryWithRecursion -pilot,TestEmptyCluster -pilot,TestEnsureNoMissingCRDs -pilot,TestFileAndKubeCombined -pilot,TestFileOnly -pilot,TestInvalidFileError -pilot,TestJsonInputFile -pilot,TestJsonOutput -pilot,TestJsonOutput/invalid_file_does_not_output_error_in_stdout -pilot,TestJsonOutput/no_other_output_except_analysis_json_output -pilot,TestKubeOnly -pilot,TestNamespace -pilot,TestTimeout -pilot,TestValidation -pilot,TestWebhook -galley_test,TestAllNamespaces -galley_test,TestConversion/config.istio.io_v1alpha2_rule -galley_test,TestConversion/core_v1_namespace -galley_test,TestConversion/core_v1_service -galley_test,TestConversion/extensions_v1beta1_ingress_basic -galley_test,TestConversion/extensions_v1beta1_ingress_merge -galley_test,TestConversion/extensions_v1beta1_ingress_multihost -galley_test,TestConversion/mesh.istio.io_v1alpha1_meshconfig -galley_test,TestConversion/networking.istio.io_v1alpha3_destinationRule -galley_test,TestConversion/networking.istio.io_v1alpha3_gateway -galley_test,TestConversion/networking.istio.io_v1alpha3_virtualService -galley_test,TestConversion/networking.istio.io_v1alpha3_virtualServiceWithUnsupported -galley_test,TestDialout_Basic -galley_test,TestDirectoryWithoutRecursion -galley_test,TestDirectoryWithRecursion -galley_test,TestEmptyCluster -galley_test,TestEnsureNoMissingCRDs -galley_test,TestFileAndKubeCombined -galley_test,TestFileOnly -galley_test,TestInvalidFileError -galley_test,TestJsonInputFile -galley_test,TestKubeOnly -galley_test,TestNamespace -galley_test,TestTimeout -galley_test,TestValidation -galley_test,TestWebhook -istioctl_webhook_test,TestWebhookManagement -locality_prioritized_failover_loadbalancing,TestDistribute -locality_prioritized_failover_loadbalancing,TestFailover -locality_prioritized_failover_loadbalancing,TestFailover/CDS -locality_prioritized_failover_loadbalancing,TestFailover/EDS -locality_prioritized_failover_loadbalancing,TestPrioritized -locality_prioritized_failover_loadbalancing,TestPrioritized/CDS -locality_prioritized_failover_loadbalancing,TestPrioritized/EDS -meshnetwork_test,TestAsymmetricMeshNetworkWithGatewayIP -mtls_first_party_jwt,TestMtlsStrictK8sCA -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->grpc://a:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->grpc://b:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->grpc://multiversion:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->grpc://naked:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->http://a:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->http://b:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->http://multiversion:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->http://naked:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->tcp://a:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->tcp://b:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->tcp://multiversion:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->tcp://naked:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->ws://a:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->ws://b:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->ws://multiversion:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->ws://naked:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->grpc://a:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->grpc://b:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->grpc://multiversion:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->grpc://naked:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->http://a:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->http://b:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->http://multiversion:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->http://naked:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->tcp://a:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->tcp://b:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->tcp://multiversion:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->tcp://naked:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->ws://a:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->ws://b:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->ws://multiversion:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->ws://naked:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->grpc://a:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->grpc://b:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->grpc://multiversion:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->grpc://naked:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->http://a:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->http://b:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->http://multiversion:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->http://naked:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->tcp://a:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->tcp://b:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->tcp://multiversion:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->tcp://naked:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->ws://a:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->ws://b:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->ws://multiversion:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->ws://naked:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->grpc://a:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->grpc://b:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->grpc://multiversion:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->grpc://naked:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->http://a:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->http://b:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->http://multiversion:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->http://naked:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->tcp://a:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->tcp://b:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->tcp://multiversion:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->tcp://naked:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->ws://a:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->ws://b:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->ws://multiversion:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->ws://naked:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->grpc://a:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->grpc://b:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->grpc://headless:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->grpc://multiversion:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->grpc://naked:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->http://a:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->http://b:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->http://headless:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->http://multiversion:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->http://naked:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->tcp://a:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->tcp://b:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->tcp://multiversion:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->tcp://naked:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->ws://a:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->ws://b:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->ws://headless:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->ws://multiversion:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->ws://naked:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->grpc://a:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->grpc://b:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->grpc://headless:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->grpc://multiversion:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->grpc://naked:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->http://a:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->http://b:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->http://headless:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->http://multiversion:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->http://naked:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->tcp://a:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->tcp://b:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->tcp://multiversion:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->tcp://naked:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->ws://a:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->ws://b:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->ws://headless:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->ws://multiversion:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->ws://naked:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->grpc://a:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->grpc://b:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->grpc://headless:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->grpc://multiversion:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->grpc://naked:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->http://a:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->http://b:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->http://headless:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->http://multiversion:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->http://naked:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->tcp://a:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->tcp://b:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->tcp://multiversion:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->tcp://naked:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->ws://a:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->ws://b:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->ws://headless:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->ws://multiversion:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->ws://naked:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->grpc://a:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->grpc://b:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->grpc://headless:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->grpc://multiversion:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->grpc://naked:grpc -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->http://a:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->http://b:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->http://headless:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->http://multiversion:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->http://naked:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->tcp://a:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->tcp://b:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->tcp://multiversion:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->tcp://naked:tcp -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->ws://a:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->ws://b:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->ws://headless:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->ws://multiversion:http -mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->ws://naked:http -mtls_k8s_ca,TestMtlsStrictK8sCA -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->grpc://a:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->grpc://b:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->grpc://multiversion:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->grpc://naked:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->http://a:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->http://b:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->http://multiversion:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->http://naked:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->tcp://a:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->tcp://b:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->tcp://multiversion:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->tcp://naked:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->ws://a:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->ws://b:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->ws://multiversion:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->ws://naked:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->grpc://a:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->grpc://b:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->grpc://multiversion:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->grpc://naked:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->http://a:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->http://b:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->http://multiversion:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->http://naked:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->tcp://a:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->tcp://b:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->tcp://multiversion:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->tcp://naked:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->ws://a:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->ws://b:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->ws://multiversion:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->ws://naked:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->grpc://a:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->grpc://b:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->grpc://multiversion:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->grpc://naked:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->http://a:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->http://b:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->http://multiversion:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->http://naked:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->tcp://a:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->tcp://b:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->tcp://multiversion:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->tcp://naked:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->ws://a:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->ws://b:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->ws://multiversion:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->ws://naked:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->grpc://a:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->grpc://b:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->grpc://multiversion:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->grpc://naked:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->http://a:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->http://b:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->http://multiversion:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->http://naked:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->tcp://a:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->tcp://b:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->tcp://multiversion:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->tcp://naked:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->ws://a:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->ws://b:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->ws://multiversion:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->ws://naked:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/a->grpc://a:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/a->grpc://b:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/a->grpc://headless:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/a->grpc://multiversion:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/a->grpc://naked:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/a->http://a:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/a->http://b:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/a->http://headless:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/a->http://multiversion:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/a->http://naked:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/a->tcp://a:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/a->tcp://b:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/a->tcp://multiversion:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/a->tcp://naked:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/a->ws://a:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/a->ws://b:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/a->ws://headless:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/a->ws://multiversion:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/a->ws://naked:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/b->grpc://a:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/b->grpc://b:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/b->grpc://headless:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/b->grpc://multiversion:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/b->grpc://naked:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/b->http://a:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/b->http://b:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/b->http://headless:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/b->http://multiversion:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/b->http://naked:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/b->tcp://a:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/b->tcp://b:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/b->tcp://multiversion:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/b->tcp://naked:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/b->ws://a:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/b->ws://b:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/b->ws://headless:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/b->ws://multiversion:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/b->ws://naked:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/headless->grpc://a:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/headless->grpc://b:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/headless->grpc://headless:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/headless->grpc://multiversion:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/headless->grpc://naked:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/headless->http://a:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/headless->http://b:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/headless->http://headless:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/headless->http://multiversion:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/headless->http://naked:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/headless->tcp://a:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/headless->tcp://b:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/headless->tcp://multiversion:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/headless->tcp://naked:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/headless->ws://a:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/headless->ws://b:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/headless->ws://headless:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/headless->ws://multiversion:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/headless->ws://naked:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/naked->grpc://a:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/naked->grpc://b:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/naked->grpc://headless:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/naked->grpc://multiversion:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/naked->grpc://naked:grpc -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/naked->http://a:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/naked->http://b:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/naked->http://headless:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/naked->http://multiversion:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/naked->http://naked:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/naked->tcp://a:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/naked->tcp://b:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/naked->tcp://multiversion:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/naked->tcp://naked:tcp -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/naked->ws://a:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/naked->ws://b:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/naked->ws://headless:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/naked->ws://multiversion:http -mtls_k8s_ca,TestMtlsStrictK8sCA/global-plaintext/naked->ws://naked:http -observability,TestAccessLog -operator,TestController -operator,TestOperatorRemove -operator_controller,TestController -outbound_traffic_policy,TestOutboundTrafficPolicy_AllowAny -outbound_traffic_policy,TestOutboundTrafficPolicy_RegistryOnly -outbound_traffic_policy_egressproxy_test,TestSidecarConfig -outbound_traffic_policy_telemetry_v2,TestOutboundTrafficPolicy_AllowAny_TelemetryV2 -outbound_traffic_policy_telemetry_v2,TestOutboundTrafficPolicy_RegistryOnly_TelemetryV2 -pilot,TestDestinationRuleTLS -pilot,TestDestinationRuleTLS/grpc -pilot,TestDestinationRuleTLS/http -pilot,TestDestinationRuleTLS/tcp -pilot,TestReachability -pilot,TestServiceEntryDNS -pilot,TestServiceEntryDNSNoSelfImport -pilot,TestServiceEntryStatic -pilot,TestSidecarListeners -pilot,TestSidecarScopeIngressListener -pilot,TestSniffing -pilot,TestVersion -pilot_analysis,TestAnalysisWritesStatus -pilot_analysis,TestStatusExistsByDefault -pilot_analysis_test,TestAnalysisWritesStatus -pilot_analysis_test,TestStatusExistsByDefault -pilot_cni,TestCNIReachability -pilot_cni,TestCNIReachability/global-mtls-on -pilot_cni,TestCNIReachability/global-mtls-on/a->grpc://a:grpc -pilot_cni,TestCNIReachability/global-mtls-on/a->grpc://b:grpc -pilot_cni,TestCNIReachability/global-mtls-on/a->grpc://headless:grpc -pilot_cni,TestCNIReachability/global-mtls-on/a->grpc://multiversion:grpc -pilot_cni,TestCNIReachability/global-mtls-on/a->grpc://naked:grpc -pilot_cni,TestCNIReachability/global-mtls-on/a->http://a:http -pilot_cni,TestCNIReachability/global-mtls-on/a->http://b:http -pilot_cni,TestCNIReachability/global-mtls-on/a->http://headless:http -pilot_cni,TestCNIReachability/global-mtls-on/a->http://multiversion:http -pilot_cni,TestCNIReachability/global-mtls-on/a->http://naked:http -pilot_cni,TestCNIReachability/global-mtls-on/a->tcp://a:tcp -pilot_cni,TestCNIReachability/global-mtls-on/a->tcp://b:tcp -pilot_cni,TestCNIReachability/global-mtls-on/a->tcp://multiversion:tcp -pilot_cni,TestCNIReachability/global-mtls-on/a->tcp://naked:tcp -pilot_cni,TestCNIReachability/global-mtls-on/a->ws://a:http -pilot_cni,TestCNIReachability/global-mtls-on/a->ws://b:http -pilot_cni,TestCNIReachability/global-mtls-on/a->ws://headless:http -pilot_cni,TestCNIReachability/global-mtls-on/a->ws://multiversion:http -pilot_cni,TestCNIReachability/global-mtls-on/a->ws://naked:http -pilot_cni,TestCNIReachability/global-mtls-on/b->grpc://a:grpc -pilot_cni,TestCNIReachability/global-mtls-on/b->grpc://b:grpc -pilot_cni,TestCNIReachability/global-mtls-on/b->grpc://headless:grpc -pilot_cni,TestCNIReachability/global-mtls-on/b->grpc://multiversion:grpc -pilot_cni,TestCNIReachability/global-mtls-on/b->grpc://naked:grpc -pilot_cni,TestCNIReachability/global-mtls-on/b->http://a:http -pilot_cni,TestCNIReachability/global-mtls-on/b->http://b:http -pilot_cni,TestCNIReachability/global-mtls-on/b->http://headless:http -pilot_cni,TestCNIReachability/global-mtls-on/b->http://multiversion:http -pilot_cni,TestCNIReachability/global-mtls-on/b->http://naked:http -pilot_cni,TestCNIReachability/global-mtls-on/b->tcp://a:tcp -pilot_cni,TestCNIReachability/global-mtls-on/b->tcp://b:tcp -pilot_cni,TestCNIReachability/global-mtls-on/b->tcp://multiversion:tcp -pilot_cni,TestCNIReachability/global-mtls-on/b->tcp://naked:tcp -pilot_cni,TestCNIReachability/global-mtls-on/b->ws://a:http -pilot_cni,TestCNIReachability/global-mtls-on/b->ws://b:http -pilot_cni,TestCNIReachability/global-mtls-on/b->ws://headless:http -pilot_cni,TestCNIReachability/global-mtls-on/b->ws://multiversion:http -pilot_cni,TestCNIReachability/global-mtls-on/b->ws://naked:http -pilot_cni,TestCNIReachability/global-mtls-on/headless->grpc://a:grpc -pilot_cni,TestCNIReachability/global-mtls-on/headless->grpc://b:grpc -pilot_cni,TestCNIReachability/global-mtls-on/headless->grpc://headless:grpc -pilot_cni,TestCNIReachability/global-mtls-on/headless->grpc://multiversion:grpc -pilot_cni,TestCNIReachability/global-mtls-on/headless->grpc://naked:grpc -pilot_cni,TestCNIReachability/global-mtls-on/headless->http://a:http -pilot_cni,TestCNIReachability/global-mtls-on/headless->http://b:http -pilot_cni,TestCNIReachability/global-mtls-on/headless->http://headless:http -pilot_cni,TestCNIReachability/global-mtls-on/headless->http://multiversion:http -pilot_cni,TestCNIReachability/global-mtls-on/headless->http://naked:http -pilot_cni,TestCNIReachability/global-mtls-on/headless->tcp://a:tcp -pilot_cni,TestCNIReachability/global-mtls-on/headless->tcp://b:tcp -pilot_cni,TestCNIReachability/global-mtls-on/headless->tcp://multiversion:tcp -pilot_cni,TestCNIReachability/global-mtls-on/headless->tcp://naked:tcp -pilot_cni,TestCNIReachability/global-mtls-on/headless->ws://a:http -pilot_cni,TestCNIReachability/global-mtls-on/headless->ws://b:http -pilot_cni,TestCNIReachability/global-mtls-on/headless->ws://headless:http -pilot_cni,TestCNIReachability/global-mtls-on/headless->ws://multiversion:http -pilot_cni,TestCNIReachability/global-mtls-on/headless->ws://naked:http -pilot_cni,TestCNIReachability/global-mtls-on/naked->grpc://a:grpc -pilot_cni,TestCNIReachability/global-mtls-on/naked->grpc://b:grpc -pilot_cni,TestCNIReachability/global-mtls-on/naked->grpc://headless:grpc -pilot_cni,TestCNIReachability/global-mtls-on/naked->grpc://multiversion:grpc -pilot_cni,TestCNIReachability/global-mtls-on/naked->grpc://naked:grpc -pilot_cni,TestCNIReachability/global-mtls-on/naked->http://a:http -pilot_cni,TestCNIReachability/global-mtls-on/naked->http://b:http -pilot_cni,TestCNIReachability/global-mtls-on/naked->http://headless:http -pilot_cni,TestCNIReachability/global-mtls-on/naked->http://multiversion:http -pilot_cni,TestCNIReachability/global-mtls-on/naked->http://naked:http -pilot_cni,TestCNIReachability/global-mtls-on/naked->tcp://a:tcp -pilot_cni,TestCNIReachability/global-mtls-on/naked->tcp://b:tcp -pilot_cni,TestCNIReachability/global-mtls-on/naked->tcp://multiversion:tcp -pilot_cni,TestCNIReachability/global-mtls-on/naked->tcp://naked:tcp -pilot_cni,TestCNIReachability/global-mtls-on/naked->ws://a:http -pilot_cni,TestCNIReachability/global-mtls-on/naked->ws://b:http -pilot_cni,TestCNIReachability/global-mtls-on/naked->ws://headless:http -pilot_cni,TestCNIReachability/global-mtls-on/naked->ws://multiversion:http -pilot_cni,TestCNIReachability/global-mtls-on/naked->ws://naked:http -pilot_envoyfilter,TestEnvoyFilterHTTPFilterInsertBefore -pilot,TestGateway -pilot,TestIngress -pilot_meshnetwork,TestAsymmetricMeshNetworkWithGatewayIP -pilot_revisions,TestMultiRevision -pilot_sidecarscope,TestServiceEntryDNS -pilot_sidecarscope,TestServiceEntryDNSNoSelfImport -pilot_sidecarscope,TestServiceEntryStatic -pilot_sidecarscope,TestSidecarScopeIngressListener -pilot_test,TestMultiRevision -pilot_test,TestReachability -pilot_test,TestSidecarListeners -pilot_test,TestSniffing -pilot_vm,TestVmTraffic -pkg_test_framework_integration,TestParallel -pkg_test_framework_integration,TestParallel/top -pkg_test_framework_integration,TestParallel/top/l1a -pkg_test_framework_integration,TestParallel/top/l1a/l2a -pkg_test_framework_integration,TestParallel/top/l1a/l2b -pkg_test_framework_integration,TestParallel/top/l1b -pkg_test_framework_integration,TestParallel/top/l1b/l2c -pkg_test_framework_integration,TestParallel/top/l1b/l2d -plugin_ca_cert,TestPluginCACert -profile_default,TestDocs/tasks/traffic-management/fault-injection/test.sh -profile_default,TestDocs/tasks/traffic-management/request-routing/test.sh -profile_default,TestDocs/tasks/traffic-management/tcp-traffic-shifting/test.sh -profile_default,TestDocs/tasks/traffic-management/traffic-shifting/test.sh -sds_ingress,TestMtlsGateways -sds_ingress,TestMtlsGateways/runtestmultimtlsgateways1.example.com -sds_ingress,TestMtlsGateways/runtestmultimtlsgateways2.example.com -sds_ingress,TestMtlsGateways/runtestmultimtlsgateways3.example.com -sds_ingress,TestMtlsGateways/runtestmultimtlsgateways4.example.com -sds_ingress,TestMtlsGateways/runtestmultimtlsgateways5.example.com -sds_ingress,TestMultiMtlsGateway_InvalidSecret -sds_ingress,TestMultiMtlsGateway_InvalidSecret/mtls_ingress_gateway_invalid_CA_cert -sds_ingress,TestMultiMtlsGateway_InvalidSecret/mtls_ingress_gateway_mismatched_CA_cert -sds_ingress,TestMultiMtlsGateway_InvalidSecret/mtls_ingress_gateway_no_CA_cert -sds_ingress,TestMultiTlsGateway_InvalidSecret -sds_ingress,TestMultiTlsGateway_InvalidSecret/tls_ingress_gateway_invalid_private_key -sds_ingress,TestMultiTlsGateway_InvalidSecret/tls_ingress_gateway_invalid_server_cert -sds_ingress,TestMultiTlsGateway_InvalidSecret/tls_ingress_gateway_mis-matched_key_and_cert -sds_ingress,TestMultiTlsGateway_InvalidSecret/tls_ingress_gateway_no_private_key -sds_ingress,TestMultiTlsGateway_InvalidSecret/tls_ingress_gateway_no_server_cert -sds_ingress,TestSingleMTLSGateway_CompoundSecretRotation -sds_ingress,TestSingleMTLSGateway_ServerKeyCertRotation -sds_ingress,TestSingleTlsGateway_SecretRotation -sds_ingress,TestTlsGateways -sds_ingress,TestTlsGateways/runtestmultitlsgateways1.example.com -sds_ingress,TestTlsGateways/runtestmultitlsgateways2.example.com -sds_ingress,TestTlsGateways/runtestmultitlsgateways3.example.com -sds_ingress,TestTlsGateways/runtestmultitlsgateways4.example.com -sds_ingress,TestTlsGateways/runtestmultitlsgateways5.example.com -sds_ingress_k8sca,TestMtlsGatewaysK8sca -sds_ingress_k8sca,TestMtlsGatewaysK8sca/runtestmultimtlsgateways1.example.com -sds_ingress_k8sca,TestMtlsGatewaysK8sca/runtestmultimtlsgateways2.example.com -sds_ingress_k8sca,TestMtlsGatewaysK8sca/runtestmultimtlsgateways3.example.com -sds_ingress_k8sca,TestMtlsGatewaysK8sca/runtestmultimtlsgateways4.example.com -sds_ingress_k8sca,TestMtlsGatewaysK8sca/runtestmultimtlsgateways5.example.com -sds_ingress_k8sca,TestTlsGatewaysK8sca -sds_ingress_k8sca,TestTlsGatewaysK8sca/runtestmultitlsgateways1.example.com -sds_ingress_k8sca,TestTlsGatewaysK8sca/runtestmultitlsgateways2.example.com -sds_ingress_k8sca,TestTlsGatewaysK8sca/runtestmultitlsgateways3.example.com -sds_ingress_k8sca,TestTlsGatewaysK8sca/runtestmultitlsgateways4.example.com -sds_ingress_k8sca,TestTlsGatewaysK8sca/runtestmultitlsgateways5.example.com -security,TestAuthorization_Conditions -security,TestAuthorization_Deny -security,TestAuthorization_EgressGateway -security,TestAuthorization_GRPC -security,TestAuthorization_IngressGateway -security,TestAuthorization_JWT -security,TestAuthorization_mTLS -security,TestAuthorization_NegativeMatch -security,TestAuthorization_Path -security,TestAuthorization_TCP -security,TestAuthorization_WorkloadSelector -security,TestAuthorization_Audit -security,TestAuthorizationForHTTPServices -security,TestIngressRequestAuthentication -security,TestMtlsHealthCheck -security,TestMutualTLSMigration -security,TestPassThroughFilterChain -security,TestReachability -security,TestReachability/automtls-partial-sidecar-dr-disable -security,TestReachability/automtls-partial-sidecar-dr-disable/a->http://multiversion:http/vistio -security,TestReachability/automtls-partial-sidecar-dr-disable/a->http://multiversion:http/vlegacy -security,TestReachability/automtls-partial-sidecar-dr-mutual -security,TestReachability/automtls-partial-sidecar-dr-mutual/a->http://multiversion:http/vistio -security,TestReachability/automtls-partial-sidecar-dr-mutual/a->http://multiversion:http/vlegacy -security,TestReachability/automtls-partial-sidecar-dr-no-tls -security,TestReachability/automtls-partial-sidecar-dr-no-tls/a->http://multiversion:http/vistio -security,TestReachability/automtls-partial-sidecar-dr-no-tls/a->http://multiversion:http/vlegacy -security,TestReachability/beta-mtls-automtls -security,TestReachability/beta-mtls-automtls/a->grpc://a:grpc -security,TestReachability/beta-mtls-automtls/a->grpc://b:grpc -security,TestReachability/beta-mtls-automtls/a->grpc://headless:grpc -security,TestReachability/beta-mtls-automtls/a->grpc://multiversion:grpc -security,TestReachability/beta-mtls-automtls/a->grpc://naked:grpc -security,TestReachability/beta-mtls-automtls/a->http://a:http -security,TestReachability/beta-mtls-automtls/a->http://b:http -security,TestReachability/beta-mtls-automtls/a->http://headless:http -security,TestReachability/beta-mtls-automtls/a->http://multiversion:http -security,TestReachability/beta-mtls-automtls/a->http://naked:http -security,TestReachability/beta-mtls-automtls/a->tcp://a:tcp -security,TestReachability/beta-mtls-automtls/a->tcp://b:tcp -security,TestReachability/beta-mtls-automtls/a->tcp://headless:tcp -security,TestReachability/beta-mtls-automtls/a->tcp://multiversion:tcp -security,TestReachability/beta-mtls-automtls/a->tcp://naked:tcp -security,TestReachability/beta-mtls-automtls/a->ws://a:http -security,TestReachability/beta-mtls-automtls/a->ws://b:http -security,TestReachability/beta-mtls-automtls/a->ws://headless:http -security,TestReachability/beta-mtls-automtls/a->ws://multiversion:http -security,TestReachability/beta-mtls-automtls/a->ws://naked:http -security,TestReachability/beta-mtls-automtls/b->grpc://a:grpc -security,TestReachability/beta-mtls-automtls/b->grpc://b:grpc -security,TestReachability/beta-mtls-automtls/b->grpc://headless:grpc -security,TestReachability/beta-mtls-automtls/b->grpc://multiversion:grpc -security,TestReachability/beta-mtls-automtls/b->grpc://naked:grpc -security,TestReachability/beta-mtls-automtls/b->http://a:http -security,TestReachability/beta-mtls-automtls/b->http://b:http -security,TestReachability/beta-mtls-automtls/b->http://headless:http -security,TestReachability/beta-mtls-automtls/b->http://multiversion:http -security,TestReachability/beta-mtls-automtls/b->http://naked:http -security,TestReachability/beta-mtls-automtls/b->tcp://a:tcp -security,TestReachability/beta-mtls-automtls/b->tcp://b:tcp -security,TestReachability/beta-mtls-automtls/b->tcp://headless:tcp -security,TestReachability/beta-mtls-automtls/b->tcp://multiversion:tcp -security,TestReachability/beta-mtls-automtls/b->tcp://naked:tcp -security,TestReachability/beta-mtls-automtls/b->ws://a:http -security,TestReachability/beta-mtls-automtls/b->ws://b:http -security,TestReachability/beta-mtls-automtls/b->ws://headless:http -security,TestReachability/beta-mtls-automtls/b->ws://multiversion:http -security,TestReachability/beta-mtls-automtls/b->ws://naked:http -security,TestReachability/beta-mtls-automtls/headless->grpc://a:grpc -security,TestReachability/beta-mtls-automtls/headless->grpc://b:grpc -security,TestReachability/beta-mtls-automtls/headless->grpc://headless:grpc -security,TestReachability/beta-mtls-automtls/headless->grpc://multiversion:grpc -security,TestReachability/beta-mtls-automtls/headless->grpc://naked:grpc -security,TestReachability/beta-mtls-automtls/headless->http://a:http -security,TestReachability/beta-mtls-automtls/headless->http://b:http -security,TestReachability/beta-mtls-automtls/headless->http://headless:http -security,TestReachability/beta-mtls-automtls/headless->http://multiversion:http -security,TestReachability/beta-mtls-automtls/headless->http://naked:http -security,TestReachability/beta-mtls-automtls/headless->tcp://a:tcp -security,TestReachability/beta-mtls-automtls/headless->tcp://b:tcp -security,TestReachability/beta-mtls-automtls/headless->tcp://headless:tcp -security,TestReachability/beta-mtls-automtls/headless->tcp://multiversion:tcp -security,TestReachability/beta-mtls-automtls/headless->tcp://naked:tcp -security,TestReachability/beta-mtls-automtls/headless->ws://a:http -security,TestReachability/beta-mtls-automtls/headless->ws://b:http -security,TestReachability/beta-mtls-automtls/headless->ws://headless:http -security,TestReachability/beta-mtls-automtls/headless->ws://multiversion:http -security,TestReachability/beta-mtls-automtls/headless->ws://naked:http -security,TestReachability/beta-mtls-automtls/naked->grpc://a:grpc -security,TestReachability/beta-mtls-automtls/naked->grpc://b:grpc -security,TestReachability/beta-mtls-automtls/naked->grpc://headless:grpc -security,TestReachability/beta-mtls-automtls/naked->grpc://multiversion:grpc -security,TestReachability/beta-mtls-automtls/naked->grpc://naked:grpc -security,TestReachability/beta-mtls-automtls/naked->http://a:http -security,TestReachability/beta-mtls-automtls/naked->http://b:http -security,TestReachability/beta-mtls-automtls/naked->http://headless:http -security,TestReachability/beta-mtls-automtls/naked->http://multiversion:http -security,TestReachability/beta-mtls-automtls/naked->http://naked:http -security,TestReachability/beta-mtls-automtls/naked->tcp://a:tcp -security,TestReachability/beta-mtls-automtls/naked->tcp://b:tcp -security,TestReachability/beta-mtls-automtls/naked->tcp://headless:tcp -security,TestReachability/beta-mtls-automtls/naked->tcp://multiversion:tcp -security,TestReachability/beta-mtls-automtls/naked->tcp://naked:tcp -security,TestReachability/beta-mtls-automtls/naked->ws://a:http -security,TestReachability/beta-mtls-automtls/naked->ws://b:http -security,TestReachability/beta-mtls-automtls/naked->ws://headless:http -security,TestReachability/beta-mtls-automtls/naked->ws://multiversion:http -security,TestReachability/beta-mtls-automtls/naked->ws://naked:http -security,TestReachability/beta-mtls-off -security,TestReachability/beta-mtls-off/a->grpc://a:grpc -security,TestReachability/beta-mtls-off/a->grpc://b:grpc -security,TestReachability/beta-mtls-off/a->grpc://headless:grpc -security,TestReachability/beta-mtls-off/a->grpc://multiversion:grpc -security,TestReachability/beta-mtls-off/a->grpc://naked:grpc -security,TestReachability/beta-mtls-off/a->http://a:http -security,TestReachability/beta-mtls-off/a->http://b:http -security,TestReachability/beta-mtls-off/a->http://headless:http -security,TestReachability/beta-mtls-off/a->http://multiversion:http -security,TestReachability/beta-mtls-off/a->http://naked:http -security,TestReachability/beta-mtls-off/a->tcp://a:tcp -security,TestReachability/beta-mtls-off/a->tcp://b:tcp -security,TestReachability/beta-mtls-off/a->tcp://headless:tcp -security,TestReachability/beta-mtls-off/a->tcp://multiversion:tcp -security,TestReachability/beta-mtls-off/a->tcp://naked:tcp -security,TestReachability/beta-mtls-off/a->ws://a:http -security,TestReachability/beta-mtls-off/a->ws://b:http -security,TestReachability/beta-mtls-off/a->ws://headless:http -security,TestReachability/beta-mtls-off/a->ws://multiversion:http -security,TestReachability/beta-mtls-off/a->ws://naked:http -security,TestReachability/beta-mtls-off/b->grpc://a:grpc -security,TestReachability/beta-mtls-off/b->grpc://b:grpc -security,TestReachability/beta-mtls-off/b->grpc://headless:grpc -security,TestReachability/beta-mtls-off/b->grpc://multiversion:grpc -security,TestReachability/beta-mtls-off/b->grpc://naked:grpc -security,TestReachability/beta-mtls-off/b->http://a:http -security,TestReachability/beta-mtls-off/b->http://b:http -security,TestReachability/beta-mtls-off/b->http://headless:http -security,TestReachability/beta-mtls-off/b->http://multiversion:http -security,TestReachability/beta-mtls-off/b->http://naked:http -security,TestReachability/beta-mtls-off/b->tcp://a:tcp -security,TestReachability/beta-mtls-off/b->tcp://b:tcp -security,TestReachability/beta-mtls-off/b->tcp://headless:tcp -security,TestReachability/beta-mtls-off/b->tcp://multiversion:tcp -security,TestReachability/beta-mtls-off/b->tcp://naked:tcp -security,TestReachability/beta-mtls-off/b->ws://a:http -security,TestReachability/beta-mtls-off/b->ws://b:http -security,TestReachability/beta-mtls-off/b->ws://headless:http -security,TestReachability/beta-mtls-off/b->ws://multiversion:http -security,TestReachability/beta-mtls-off/b->ws://naked:http -security,TestReachability/beta-mtls-off/headless->grpc://a:grpc -security,TestReachability/beta-mtls-off/headless->grpc://b:grpc -security,TestReachability/beta-mtls-off/headless->grpc://headless:grpc -security,TestReachability/beta-mtls-off/headless->grpc://multiversion:grpc -security,TestReachability/beta-mtls-off/headless->grpc://naked:grpc -security,TestReachability/beta-mtls-off/headless->http://a:http -security,TestReachability/beta-mtls-off/headless->http://b:http -security,TestReachability/beta-mtls-off/headless->http://headless:http -security,TestReachability/beta-mtls-off/headless->http://multiversion:http -security,TestReachability/beta-mtls-off/headless->http://naked:http -security,TestReachability/beta-mtls-off/headless->tcp://a:tcp -security,TestReachability/beta-mtls-off/headless->tcp://b:tcp -security,TestReachability/beta-mtls-off/headless->tcp://headless:tcp -security,TestReachability/beta-mtls-off/headless->tcp://multiversion:tcp -security,TestReachability/beta-mtls-off/headless->tcp://naked:tcp -security,TestReachability/beta-mtls-off/headless->ws://a:http -security,TestReachability/beta-mtls-off/headless->ws://b:http -security,TestReachability/beta-mtls-off/headless->ws://headless:http -security,TestReachability/beta-mtls-off/headless->ws://multiversion:http -security,TestReachability/beta-mtls-off/headless->ws://naked:http -security,TestReachability/beta-mtls-off/naked->grpc://a:grpc -security,TestReachability/beta-mtls-off/naked->grpc://b:grpc -security,TestReachability/beta-mtls-off/naked->grpc://headless:grpc -security,TestReachability/beta-mtls-off/naked->grpc://multiversion:grpc -security,TestReachability/beta-mtls-off/naked->grpc://naked:grpc -security,TestReachability/beta-mtls-off/naked->http://a:http -security,TestReachability/beta-mtls-off/naked->http://b:http -security,TestReachability/beta-mtls-off/naked->http://headless:http -security,TestReachability/beta-mtls-off/naked->http://multiversion:http -security,TestReachability/beta-mtls-off/naked->http://naked:http -security,TestReachability/beta-mtls-off/naked->tcp://a:tcp -security,TestReachability/beta-mtls-off/naked->tcp://b:tcp -security,TestReachability/beta-mtls-off/naked->tcp://headless:tcp -security,TestReachability/beta-mtls-off/naked->tcp://multiversion:tcp -security,TestReachability/beta-mtls-off/naked->tcp://naked:tcp -security,TestReachability/beta-mtls-off/naked->ws://a:http -security,TestReachability/beta-mtls-off/naked->ws://b:http -security,TestReachability/beta-mtls-off/naked->ws://headless:http -security,TestReachability/beta-mtls-off/naked->ws://multiversion:http -security,TestReachability/beta-mtls-off/naked->ws://naked:http -security,TestReachability/beta-mtls-on -security,TestReachability/beta-mtls-on/a->grpc://a:grpc -security,TestReachability/beta-mtls-on/a->grpc://b:grpc -security,TestReachability/beta-mtls-on/a->grpc://headless:grpc -security,TestReachability/beta-mtls-on/a->grpc://multiversion:grpc -security,TestReachability/beta-mtls-on/a->grpc://naked:grpc -security,TestReachability/beta-mtls-on/a->http://a:http -security,TestReachability/beta-mtls-on/a->http://b:http -security,TestReachability/beta-mtls-on/a->http://headless:http -security,TestReachability/beta-mtls-on/a->http://multiversion:http -security,TestReachability/beta-mtls-on/a->http://naked:http -security,TestReachability/beta-mtls-on/a->tcp://a:tcp -security,TestReachability/beta-mtls-on/a->tcp://b:tcp -security,TestReachability/beta-mtls-on/a->tcp://headless:tcp -security,TestReachability/beta-mtls-on/a->tcp://multiversion:tcp -security,TestReachability/beta-mtls-on/a->tcp://naked:tcp -security,TestReachability/beta-mtls-on/a->ws://a:http -security,TestReachability/beta-mtls-on/a->ws://b:http -security,TestReachability/beta-mtls-on/a->ws://headless:http -security,TestReachability/beta-mtls-on/a->ws://multiversion:http -security,TestReachability/beta-mtls-on/a->ws://naked:http -security,TestReachability/beta-mtls-on/b->grpc://a:grpc -security,TestReachability/beta-mtls-on/b->grpc://b:grpc -security,TestReachability/beta-mtls-on/b->grpc://headless:grpc -security,TestReachability/beta-mtls-on/b->grpc://multiversion:grpc -security,TestReachability/beta-mtls-on/b->grpc://naked:grpc -security,TestReachability/beta-mtls-on/b->http://a:http -security,TestReachability/beta-mtls-on/b->http://b:http -security,TestReachability/beta-mtls-on/b->http://headless:http -security,TestReachability/beta-mtls-on/b->http://multiversion:http -security,TestReachability/beta-mtls-on/b->http://naked:http -security,TestReachability/beta-mtls-on/b->tcp://a:tcp -security,TestReachability/beta-mtls-on/b->tcp://b:tcp -security,TestReachability/beta-mtls-on/b->tcp://headless:tcp -security,TestReachability/beta-mtls-on/b->tcp://multiversion:tcp -security,TestReachability/beta-mtls-on/b->tcp://naked:tcp -security,TestReachability/beta-mtls-on/b->ws://a:http -security,TestReachability/beta-mtls-on/b->ws://b:http -security,TestReachability/beta-mtls-on/b->ws://headless:http -security,TestReachability/beta-mtls-on/b->ws://multiversion:http -security,TestReachability/beta-mtls-on/b->ws://naked:http -security,TestReachability/beta-mtls-on/headless->grpc://a:grpc -security,TestReachability/beta-mtls-on/headless->grpc://b:grpc -security,TestReachability/beta-mtls-on/headless->grpc://headless:grpc -security,TestReachability/beta-mtls-on/headless->grpc://multiversion:grpc -security,TestReachability/beta-mtls-on/headless->grpc://naked:grpc -security,TestReachability/beta-mtls-on/headless->http://a:http -security,TestReachability/beta-mtls-on/headless->http://b:http -security,TestReachability/beta-mtls-on/headless->http://headless:http -security,TestReachability/beta-mtls-on/headless->http://multiversion:http -security,TestReachability/beta-mtls-on/headless->http://naked:http -security,TestReachability/beta-mtls-on/headless->tcp://a:tcp -security,TestReachability/beta-mtls-on/headless->tcp://b:tcp -security,TestReachability/beta-mtls-on/headless->tcp://headless:tcp -security,TestReachability/beta-mtls-on/headless->tcp://multiversion:tcp -security,TestReachability/beta-mtls-on/headless->tcp://naked:tcp -security,TestReachability/beta-mtls-on/headless->ws://a:http -security,TestReachability/beta-mtls-on/headless->ws://b:http -security,TestReachability/beta-mtls-on/headless->ws://headless:http -security,TestReachability/beta-mtls-on/headless->ws://multiversion:http -security,TestReachability/beta-mtls-on/headless->ws://naked:http -security,TestReachability/beta-mtls-on/naked->grpc://a:grpc -security,TestReachability/beta-mtls-on/naked->grpc://b:grpc -security,TestReachability/beta-mtls-on/naked->grpc://headless:grpc -security,TestReachability/beta-mtls-on/naked->grpc://multiversion:grpc -security,TestReachability/beta-mtls-on/naked->grpc://naked:grpc -security,TestReachability/beta-mtls-on/naked->http://a:http -security,TestReachability/beta-mtls-on/naked->http://b:http -security,TestReachability/beta-mtls-on/naked->http://headless:http -security,TestReachability/beta-mtls-on/naked->http://multiversion:http -security,TestReachability/beta-mtls-on/naked->http://naked:http -security,TestReachability/beta-mtls-on/naked->tcp://a:tcp -security,TestReachability/beta-mtls-on/naked->tcp://b:tcp -security,TestReachability/beta-mtls-on/naked->tcp://headless:tcp -security,TestReachability/beta-mtls-on/naked->tcp://multiversion:tcp -security,TestReachability/beta-mtls-on/naked->tcp://naked:tcp -security,TestReachability/beta-mtls-on/naked->ws://a:http -security,TestReachability/beta-mtls-on/naked->ws://b:http -security,TestReachability/beta-mtls-on/naked->ws://headless:http -security,TestReachability/beta-mtls-on/naked->ws://multiversion:http -security,TestReachability/beta-mtls-on/naked->ws://naked:http -security,TestReachability/beta-mtls-partial-automtls -security,TestReachability/beta-mtls-partial-automtls/a->grpc://a:grpc -security,TestReachability/beta-mtls-partial-automtls/a->grpc://b:grpc -security,TestReachability/beta-mtls-partial-automtls/a->grpc://headless:grpc -security,TestReachability/beta-mtls-partial-automtls/a->grpc://multiversion:grpc -security,TestReachability/beta-mtls-partial-automtls/a->grpc://naked:grpc -security,TestReachability/beta-mtls-partial-automtls/a->http://a:http -security,TestReachability/beta-mtls-partial-automtls/a->http://b:http -security,TestReachability/beta-mtls-partial-automtls/a->http://headless:http -security,TestReachability/beta-mtls-partial-automtls/a->http://multiversion:http -security,TestReachability/beta-mtls-partial-automtls/a->http://naked:http -security,TestReachability/beta-mtls-partial-automtls/a->tcp://a:tcp -security,TestReachability/beta-mtls-partial-automtls/a->tcp://b:tcp -security,TestReachability/beta-mtls-partial-automtls/a->tcp://headless:tcp -security,TestReachability/beta-mtls-partial-automtls/a->tcp://multiversion:tcp -security,TestReachability/beta-mtls-partial-automtls/a->tcp://naked:tcp -security,TestReachability/beta-mtls-partial-automtls/a->ws://a:http -security,TestReachability/beta-mtls-partial-automtls/a->ws://b:http -security,TestReachability/beta-mtls-partial-automtls/a->ws://headless:http -security,TestReachability/beta-mtls-partial-automtls/a->ws://multiversion:http -security,TestReachability/beta-mtls-partial-automtls/a->ws://naked:http -security,TestReachability/beta-mtls-partial-automtls/b->grpc://a:grpc -security,TestReachability/beta-mtls-partial-automtls/b->grpc://b:grpc -security,TestReachability/beta-mtls-partial-automtls/b->grpc://headless:grpc -security,TestReachability/beta-mtls-partial-automtls/b->grpc://multiversion:grpc -security,TestReachability/beta-mtls-partial-automtls/b->grpc://naked:grpc -security,TestReachability/beta-mtls-partial-automtls/b->http://a:http -security,TestReachability/beta-mtls-partial-automtls/b->http://b:http -security,TestReachability/beta-mtls-partial-automtls/b->http://headless:http -security,TestReachability/beta-mtls-partial-automtls/b->http://multiversion:http -security,TestReachability/beta-mtls-partial-automtls/b->http://naked:http -security,TestReachability/beta-mtls-partial-automtls/b->tcp://a:tcp -security,TestReachability/beta-mtls-partial-automtls/b->tcp://b:tcp -security,TestReachability/beta-mtls-partial-automtls/b->tcp://headless:tcp -security,TestReachability/beta-mtls-partial-automtls/b->tcp://multiversion:tcp -security,TestReachability/beta-mtls-partial-automtls/b->tcp://naked:tcp -security,TestReachability/beta-mtls-partial-automtls/b->ws://a:http -security,TestReachability/beta-mtls-partial-automtls/b->ws://b:http -security,TestReachability/beta-mtls-partial-automtls/b->ws://headless:http -security,TestReachability/beta-mtls-partial-automtls/b->ws://multiversion:http -security,TestReachability/beta-mtls-partial-automtls/b->ws://naked:http -security,TestReachability/beta-mtls-partial-automtls/headless->grpc://a:grpc -security,TestReachability/beta-mtls-partial-automtls/headless->grpc://b:grpc -security,TestReachability/beta-mtls-partial-automtls/headless->grpc://headless:grpc -security,TestReachability/beta-mtls-partial-automtls/headless->grpc://multiversion:grpc -security,TestReachability/beta-mtls-partial-automtls/headless->grpc://naked:grpc -security,TestReachability/beta-mtls-partial-automtls/headless->http://a:http -security,TestReachability/beta-mtls-partial-automtls/headless->http://b:http -security,TestReachability/beta-mtls-partial-automtls/headless->http://headless:http -security,TestReachability/beta-mtls-partial-automtls/headless->http://multiversion:http -security,TestReachability/beta-mtls-partial-automtls/headless->http://naked:http -security,TestReachability/beta-mtls-partial-automtls/headless->tcp://a:tcp -security,TestReachability/beta-mtls-partial-automtls/headless->tcp://b:tcp -security,TestReachability/beta-mtls-partial-automtls/headless->tcp://headless:tcp -security,TestReachability/beta-mtls-partial-automtls/headless->tcp://multiversion:tcp -security,TestReachability/beta-mtls-partial-automtls/headless->tcp://naked:tcp -security,TestReachability/beta-mtls-partial-automtls/headless->ws://a:http -security,TestReachability/beta-mtls-partial-automtls/headless->ws://b:http -security,TestReachability/beta-mtls-partial-automtls/headless->ws://headless:http -security,TestReachability/beta-mtls-partial-automtls/headless->ws://multiversion:http -security,TestReachability/beta-mtls-partial-automtls/headless->ws://naked:http -security,TestReachability/beta-mtls-partial-automtls/naked->grpc://a:grpc -security,TestReachability/beta-mtls-partial-automtls/naked->grpc://b:grpc -security,TestReachability/beta-mtls-partial-automtls/naked->grpc://headless:grpc -security,TestReachability/beta-mtls-partial-automtls/naked->grpc://multiversion:grpc -security,TestReachability/beta-mtls-partial-automtls/naked->grpc://naked:grpc -security,TestReachability/beta-mtls-partial-automtls/naked->http://a:http -security,TestReachability/beta-mtls-partial-automtls/naked->http://b:http -security,TestReachability/beta-mtls-partial-automtls/naked->http://headless:http -security,TestReachability/beta-mtls-partial-automtls/naked->http://multiversion:http -security,TestReachability/beta-mtls-partial-automtls/naked->http://naked:http -security,TestReachability/beta-mtls-partial-automtls/naked->tcp://a:tcp -security,TestReachability/beta-mtls-partial-automtls/naked->tcp://b:tcp -security,TestReachability/beta-mtls-partial-automtls/naked->tcp://headless:tcp -security,TestReachability/beta-mtls-partial-automtls/naked->tcp://multiversion:tcp -security,TestReachability/beta-mtls-partial-automtls/naked->tcp://naked:tcp -security,TestReachability/beta-mtls-partial-automtls/naked->ws://a:http -security,TestReachability/beta-mtls-partial-automtls/naked->ws://b:http -security,TestReachability/beta-mtls-partial-automtls/naked->ws://headless:http -security,TestReachability/beta-mtls-partial-automtls/naked->ws://multiversion:http -security,TestReachability/beta-mtls-partial-automtls/naked->ws://naked:http -security,TestReachability/beta-mtls-permissive -security,TestReachability/beta-mtls-permissive/a->grpc://a:grpc -security,TestReachability/beta-mtls-permissive/a->grpc://b:grpc -security,TestReachability/beta-mtls-permissive/a->grpc://headless:grpc -security,TestReachability/beta-mtls-permissive/a->grpc://multiversion:grpc -security,TestReachability/beta-mtls-permissive/a->http://a:http -security,TestReachability/beta-mtls-permissive/a->http://b:http -security,TestReachability/beta-mtls-permissive/a->http://headless:http -security,TestReachability/beta-mtls-permissive/a->http://multiversion:http -security,TestReachability/beta-mtls-permissive/a->tcp://a:tcp -security,TestReachability/beta-mtls-permissive/a->tcp://b:tcp -security,TestReachability/beta-mtls-permissive/a->tcp://headless:tcp -security,TestReachability/beta-mtls-permissive/a->tcp://multiversion:tcp -security,TestReachability/beta-mtls-permissive/a->ws://a:http -security,TestReachability/beta-mtls-permissive/a->ws://b:http -security,TestReachability/beta-mtls-permissive/a->ws://headless:http -security,TestReachability/beta-mtls-permissive/a->ws://multiversion:http -security,TestReachability/beta-mtls-permissive/b->grpc://a:grpc -security,TestReachability/beta-mtls-permissive/b->grpc://b:grpc -security,TestReachability/beta-mtls-permissive/b->grpc://headless:grpc -security,TestReachability/beta-mtls-permissive/b->grpc://multiversion:grpc -security,TestReachability/beta-mtls-permissive/b->http://a:http -security,TestReachability/beta-mtls-permissive/b->http://b:http -security,TestReachability/beta-mtls-permissive/b->http://headless:http -security,TestReachability/beta-mtls-permissive/b->http://multiversion:http -security,TestReachability/beta-mtls-permissive/b->tcp://a:tcp -security,TestReachability/beta-mtls-permissive/b->tcp://b:tcp -security,TestReachability/beta-mtls-permissive/b->tcp://headless:tcp -security,TestReachability/beta-mtls-permissive/b->tcp://multiversion:tcp -security,TestReachability/beta-mtls-permissive/b->ws://a:http -security,TestReachability/beta-mtls-permissive/b->ws://b:http -security,TestReachability/beta-mtls-permissive/b->ws://headless:http -security,TestReachability/beta-mtls-permissive/b->ws://multiversion:http -security,TestReachability/beta-mtls-permissive/headless->grpc://a:grpc -security,TestReachability/beta-mtls-permissive/headless->grpc://b:grpc -security,TestReachability/beta-mtls-permissive/headless->grpc://headless:grpc -security,TestReachability/beta-mtls-permissive/headless->grpc://multiversion:grpc -security,TestReachability/beta-mtls-permissive/headless->http://a:http -security,TestReachability/beta-mtls-permissive/headless->http://b:http -security,TestReachability/beta-mtls-permissive/headless->http://headless:http -security,TestReachability/beta-mtls-permissive/headless->http://multiversion:http -security,TestReachability/beta-mtls-permissive/headless->tcp://a:tcp -security,TestReachability/beta-mtls-permissive/headless->tcp://b:tcp -security,TestReachability/beta-mtls-permissive/headless->tcp://headless:tcp -security,TestReachability/beta-mtls-permissive/headless->tcp://multiversion:tcp -security,TestReachability/beta-mtls-permissive/headless->ws://a:http -security,TestReachability/beta-mtls-permissive/headless->ws://b:http -security,TestReachability/beta-mtls-permissive/headless->ws://headless:http -security,TestReachability/beta-mtls-permissive/headless->ws://multiversion:http -security,TestReachability/beta-mtls-permissive/naked->grpc://a:grpc -security,TestReachability/beta-mtls-permissive/naked->grpc://b:grpc -security,TestReachability/beta-mtls-permissive/naked->grpc://headless:grpc -security,TestReachability/beta-mtls-permissive/naked->grpc://multiversion:grpc -security,TestReachability/beta-mtls-permissive/naked->http://a:http -security,TestReachability/beta-mtls-permissive/naked->http://b:http -security,TestReachability/beta-mtls-permissive/naked->http://headless:http -security,TestReachability/beta-mtls-permissive/naked->http://multiversion:http -security,TestReachability/beta-mtls-permissive/naked->tcp://a:tcp -security,TestReachability/beta-mtls-permissive/naked->tcp://b:tcp -security,TestReachability/beta-mtls-permissive/naked->tcp://headless:tcp -security,TestReachability/beta-mtls-permissive/naked->tcp://multiversion:tcp -security,TestReachability/beta-mtls-permissive/naked->ws://a:http -security,TestReachability/beta-mtls-permissive/naked->ws://b:http -security,TestReachability/beta-mtls-permissive/naked->ws://headless:http -security,TestReachability/beta-mtls-permissive/naked->ws://multiversion:http -security,TestReachability/beta-per-port-mtls -security,TestReachability/beta-per-port-mtls/a->grpc://b:grpc -security,TestReachability/beta-per-port-mtls/a->http://b:http -security,TestReachability/beta-per-port-mtls/a->tcp://b:tcp -security,TestReachability/beta-per-port-mtls/a->ws://b:http -security,TestReachability/beta-per-port-mtls/b->grpc://b:grpc -security,TestReachability/beta-per-port-mtls/b->http://b:http -security,TestReachability/beta-per-port-mtls/b->tcp://b:tcp -security,TestReachability/beta-per-port-mtls/b->ws://b:http -security,TestReachability/beta-per-port-mtls/headless->grpc://b:grpc -security,TestReachability/beta-per-port-mtls/headless->http://b:http -security,TestReachability/beta-per-port-mtls/headless->tcp://b:tcp -security,TestReachability/beta-per-port-mtls/headless->ws://b:http -security,TestReachability/beta-per-port-mtls/naked->grpc://b:grpc -security,TestReachability/beta-per-port-mtls/naked->http://b:http -security,TestReachability/beta-per-port-mtls/naked->tcp://b:tcp -security,TestReachability/beta-per-port-mtls/naked->ws://b:http -security,TestReachability/global-plaintext -security,TestReachability/global-plaintext/a->grpc://a:grpc -security,TestReachability/global-plaintext/a->grpc://b:grpc -security,TestReachability/global-plaintext/a->grpc://headless:grpc -security,TestReachability/global-plaintext/a->grpc://multiversion:grpc -security,TestReachability/global-plaintext/a->grpc://naked:grpc -security,TestReachability/global-plaintext/a->http://a:http -security,TestReachability/global-plaintext/a->http://b:http -security,TestReachability/global-plaintext/a->http://headless:http -security,TestReachability/global-plaintext/a->http://multiversion:http -security,TestReachability/global-plaintext/a->http://naked:http -security,TestReachability/global-plaintext/a->tcp://a:tcp -security,TestReachability/global-plaintext/a->tcp://b:tcp -security,TestReachability/global-plaintext/a->tcp://multiversion:tcp -security,TestReachability/global-plaintext/a->tcp://naked:tcp -security,TestReachability/global-plaintext/a->ws://a:http -security,TestReachability/global-plaintext/a->ws://b:http -security,TestReachability/global-plaintext/a->ws://headless:http -security,TestReachability/global-plaintext/a->ws://multiversion:http -security,TestReachability/global-plaintext/a->ws://naked:http -security,TestReachability/global-plaintext/b->grpc://a:grpc -security,TestReachability/global-plaintext/b->grpc://b:grpc -security,TestReachability/global-plaintext/b->grpc://headless:grpc -security,TestReachability/global-plaintext/b->grpc://multiversion:grpc -security,TestReachability/global-plaintext/b->grpc://naked:grpc -security,TestReachability/global-plaintext/b->http://a:http -security,TestReachability/global-plaintext/b->http://b:http -security,TestReachability/global-plaintext/b->http://headless:http -security,TestReachability/global-plaintext/b->http://multiversion:http -security,TestReachability/global-plaintext/b->http://naked:http -security,TestReachability/global-plaintext/b->tcp://a:tcp -security,TestReachability/global-plaintext/b->tcp://b:tcp -security,TestReachability/global-plaintext/b->tcp://multiversion:tcp -security,TestReachability/global-plaintext/b->tcp://naked:tcp -security,TestReachability/global-plaintext/b->ws://a:http -security,TestReachability/global-plaintext/b->ws://b:http -security,TestReachability/global-plaintext/b->ws://headless:http -security,TestReachability/global-plaintext/b->ws://multiversion:http -security,TestReachability/global-plaintext/b->ws://naked:http -security,TestReachability/global-plaintext/headless->grpc://a:grpc -security,TestReachability/global-plaintext/headless->grpc://b:grpc -security,TestReachability/global-plaintext/headless->grpc://headless:grpc -security,TestReachability/global-plaintext/headless->grpc://multiversion:grpc -security,TestReachability/global-plaintext/headless->grpc://naked:grpc -security,TestReachability/global-plaintext/headless->http://a:http -security,TestReachability/global-plaintext/headless->http://b:http -security,TestReachability/global-plaintext/headless->http://headless:http -security,TestReachability/global-plaintext/headless->http://multiversion:http -security,TestReachability/global-plaintext/headless->http://naked:http -security,TestReachability/global-plaintext/headless->tcp://a:tcp -security,TestReachability/global-plaintext/headless->tcp://b:tcp -security,TestReachability/global-plaintext/headless->tcp://multiversion:tcp -security,TestReachability/global-plaintext/headless->tcp://naked:tcp -security,TestReachability/global-plaintext/headless->ws://a:http -security,TestReachability/global-plaintext/headless->ws://b:http -security,TestReachability/global-plaintext/headless->ws://headless:http -security,TestReachability/global-plaintext/headless->ws://multiversion:http -security,TestReachability/global-plaintext/headless->ws://naked:http -security,TestReachability/global-plaintext/naked->grpc://a:grpc -security,TestReachability/global-plaintext/naked->grpc://b:grpc -security,TestReachability/global-plaintext/naked->grpc://headless:grpc -security,TestReachability/global-plaintext/naked->grpc://multiversion:grpc -security,TestReachability/global-plaintext/naked->grpc://naked:grpc -security,TestReachability/global-plaintext/naked->http://a:http -security,TestReachability/global-plaintext/naked->http://b:http -security,TestReachability/global-plaintext/naked->http://headless:http -security,TestReachability/global-plaintext/naked->http://multiversion:http -security,TestReachability/global-plaintext/naked->http://naked:http -security,TestReachability/global-plaintext/naked->tcp://a:tcp -security,TestReachability/global-plaintext/naked->tcp://b:tcp -security,TestReachability/global-plaintext/naked->tcp://multiversion:tcp -security,TestReachability/global-plaintext/naked->tcp://naked:tcp -security,TestReachability/global-plaintext/naked->ws://a:http -security,TestReachability/global-plaintext/naked->ws://b:http -security,TestReachability/global-plaintext/naked->ws://headless:http -security,TestReachability/global-plaintext/naked->ws://multiversion:http -security,TestReachability/global-plaintext/naked->ws://naked:http -security,TestRequestAuthentication -security_cert_provision_prometheus,TestPrometheusCert -security_chiron,TestDNSCertificate -security_chiron,TestDNSCertificate/generateDNSCertificates -security_chiron,TestDNSCertificate/regenerateDNSCertificates -security_chiron,TestDNSCertificate/rotateDNSCertificatesWhenCAUpdated -security_chiron,TestDNSCertificate/rotateDNSCertificatesWhenCertExpired -security_mtls_first_party_jwt,TestMtlsStrictK8sCA -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->grpc://a:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->grpc://b:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->grpc://multiversion:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->grpc://naked:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->http://a:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->http://b:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->http://multiversion:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->http://naked:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->tcp://a:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->tcp://b:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->tcp://multiversion:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->tcp://naked:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->ws://a:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->ws://b:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->ws://multiversion:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->ws://naked:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->grpc://a:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->grpc://b:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->grpc://multiversion:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->grpc://naked:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->http://a:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->http://b:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->http://multiversion:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->http://naked:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->tcp://a:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->tcp://b:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->tcp://multiversion:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->tcp://naked:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->ws://a:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->ws://b:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->ws://multiversion:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->ws://naked:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->grpc://a:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->grpc://b:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->grpc://multiversion:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->grpc://naked:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->http://a:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->http://b:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->http://multiversion:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->http://naked:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->tcp://a:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->tcp://b:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->tcp://multiversion:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->tcp://naked:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->ws://a:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->ws://b:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->ws://multiversion:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->ws://naked:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->grpc://a:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->grpc://b:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->grpc://multiversion:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->grpc://naked:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->http://a:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->http://b:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->http://multiversion:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->http://naked:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->tcp://a:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->tcp://b:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->tcp://multiversion:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->tcp://naked:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->ws://a:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->ws://b:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->ws://multiversion:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->ws://naked:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->grpc://a:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->grpc://b:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->grpc://headless:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->grpc://multiversion:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->grpc://naked:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->http://a:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->http://b:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->http://headless:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->http://multiversion:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->http://naked:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->tcp://a:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->tcp://b:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->tcp://multiversion:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->tcp://naked:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->ws://a:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->ws://b:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->ws://headless:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->ws://multiversion:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/a->ws://naked:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->grpc://a:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->grpc://b:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->grpc://headless:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->grpc://multiversion:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->grpc://naked:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->http://a:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->http://b:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->http://headless:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->http://multiversion:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->http://naked:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->tcp://a:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->tcp://b:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->tcp://multiversion:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->tcp://naked:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->ws://a:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->ws://b:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->ws://headless:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->ws://multiversion:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/b->ws://naked:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->grpc://a:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->grpc://b:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->grpc://headless:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->grpc://multiversion:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->grpc://naked:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->http://a:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->http://b:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->http://headless:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->http://multiversion:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->http://naked:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->tcp://a:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->tcp://b:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->tcp://multiversion:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->tcp://naked:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->ws://a:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->ws://b:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->ws://headless:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->ws://multiversion:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/headless->ws://naked:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->grpc://a:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->grpc://b:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->grpc://headless:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->grpc://multiversion:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->grpc://naked:grpc -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->http://a:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->http://b:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->http://headless:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->http://multiversion:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->http://naked:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->tcp://a:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->tcp://b:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->tcp://multiversion:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->tcp://naked:tcp -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->ws://a:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->ws://b:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->ws://headless:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->ws://multiversion:http -security_mtls_first_party_jwt,TestMtlsStrictK8sCA/global-plaintext/naked->ws://naked:http -security_mtlscert_pluginca_securenaming,TestMTLSCertPluginCASecureNaming -security_mtlscert_pluginca_securenaming,TestMTLSCertPluginCASecureNaming/connection_fails_when_DR_contains_non-matching,_non-existing_SA -security_mtlscert_pluginca_securenaming,TestMTLSCertPluginCASecureNaming/connection_fails_when_DR_doesn't_match_SA -security_mtlscert_pluginca_securenaming,TestMTLSCertPluginCASecureNaming/connection_succeeds_when_DR_matches_SA -security_mtlscert_pluginca_securenaming,TestMTLSCertPluginCASecureNaming/connection_succeeds_when_SA_is_in_the_list_of_SANs -security_mtlscert_pluginca_securenaming,TestMTLSCertPluginCASecureNaming/mTLS_cert_validation_with_plugin_CA -security_mtlsk8sca,TestMtlsStrictK8sCA -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->grpc://a:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->grpc://b:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->grpc://multiversion:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->grpc://naked:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->http://a:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->http://b:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->http://multiversion:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->http://naked:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->tcp://a:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->tcp://b:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->tcp://multiversion:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->tcp://naked:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->ws://a:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->ws://b:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->ws://multiversion:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/a->ws://naked:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->grpc://a:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->grpc://b:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->grpc://multiversion:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->grpc://naked:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->http://a:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->http://b:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->http://multiversion:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->http://naked:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->tcp://a:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->tcp://b:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->tcp://multiversion:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->tcp://naked:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->ws://a:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->ws://b:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->ws://multiversion:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/b->ws://naked:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->grpc://a:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->grpc://b:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->grpc://multiversion:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->grpc://naked:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->http://a:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->http://b:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->http://multiversion:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->http://naked:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->tcp://a:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->tcp://b:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->tcp://multiversion:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->tcp://naked:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->ws://a:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->ws://b:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->ws://multiversion:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/headless->ws://naked:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->grpc://a:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->grpc://b:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->grpc://multiversion:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->grpc://naked:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->http://a:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->http://b:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->http://multiversion:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->http://naked:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->tcp://a:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->tcp://b:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->tcp://multiversion:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->tcp://naked:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->ws://a:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->ws://b:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->ws://multiversion:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-mtls-on-no-dr/naked->ws://naked:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/a->grpc://a:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/a->grpc://b:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/a->grpc://headless:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/a->grpc://multiversion:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/a->grpc://naked:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/a->http://a:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/a->http://b:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/a->http://headless:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/a->http://multiversion:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/a->http://naked:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/a->tcp://a:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/a->tcp://b:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/a->tcp://multiversion:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/a->tcp://naked:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/a->ws://a:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/a->ws://b:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/a->ws://headless:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/a->ws://multiversion:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/a->ws://naked:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/b->grpc://a:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/b->grpc://b:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/b->grpc://headless:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/b->grpc://multiversion:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/b->grpc://naked:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/b->http://a:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/b->http://b:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/b->http://headless:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/b->http://multiversion:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/b->http://naked:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/b->tcp://a:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/b->tcp://b:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/b->tcp://multiversion:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/b->tcp://naked:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/b->ws://a:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/b->ws://b:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/b->ws://headless:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/b->ws://multiversion:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/b->ws://naked:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/headless->grpc://a:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/headless->grpc://b:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/headless->grpc://headless:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/headless->grpc://multiversion:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/headless->grpc://naked:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/headless->http://a:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/headless->http://b:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/headless->http://headless:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/headless->http://multiversion:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/headless->http://naked:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/headless->tcp://a:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/headless->tcp://b:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/headless->tcp://multiversion:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/headless->tcp://naked:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/headless->ws://a:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/headless->ws://b:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/headless->ws://headless:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/headless->ws://multiversion:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/headless->ws://naked:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/naked->grpc://a:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/naked->grpc://b:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/naked->grpc://headless:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/naked->grpc://multiversion:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/naked->grpc://naked:grpc -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/naked->http://a:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/naked->http://b:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/naked->http://headless:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/naked->http://multiversion:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/naked->http://naked:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/naked->tcp://a:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/naked->tcp://b:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/naked->tcp://multiversion:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/naked->tcp://naked:tcp -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/naked->ws://a:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/naked->ws://b:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/naked->ws://headless:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/naked->ws://multiversion:http -security_mtlsk8sca,TestMtlsStrictK8sCA/global-plaintext/naked->ws://naked:http -security_sds_ingress,TestMtlsGateways -security_sds_ingress,TestMtlsGateways/runtestmultimtlsgateways1.example.com -security_sds_ingress,TestMtlsGateways/runtestmultimtlsgateways2.example.com -security_sds_ingress,TestMtlsGateways/runtestmultimtlsgateways3.example.com -security_sds_ingress,TestMtlsGateways/runtestmultimtlsgateways4.example.com -security_sds_ingress,TestMtlsGateways/runtestmultimtlsgateways5.example.com -security_sds_ingress,TestMultiMtlsGateway_InvalidSecret -security_sds_ingress,TestMultiMtlsGateway_InvalidSecret/mtls_ingress_gateway_invalid_CA_cert -security_sds_ingress,TestMultiMtlsGateway_InvalidSecret/mtls_ingress_gateway_mismatched_CA_cert -security_sds_ingress,TestMultiMtlsGateway_InvalidSecret/mtls_ingress_gateway_no_CA_cert -security_sds_ingress,TestMultiTlsGateway_InvalidSecret -security_sds_ingress,TestMultiTlsGateway_InvalidSecret/tls_ingress_gateway_invalid_private_key -security_sds_ingress,TestMultiTlsGateway_InvalidSecret/tls_ingress_gateway_invalid_server_cert -security_sds_ingress,TestMultiTlsGateway_InvalidSecret/tls_ingress_gateway_mis-matched_key_and_cert -security_sds_ingress,TestMultiTlsGateway_InvalidSecret/tls_ingress_gateway_no_private_key -security_sds_ingress,TestMultiTlsGateway_InvalidSecret/tls_ingress_gateway_no_server_cert -security_sds_ingress,TestSingleMTLSGateway_CompoundSecretRotation -security_sds_ingress,TestSingleMTLSGateway_ServerKeyCertRotation -security_sds_ingress,TestSingleMTLSGatewayAndNotGeneric_CompoundSecretRotation -security_sds_ingress,TestSingleTlsGateway_SecretRotation -security_sds_ingress,TestTlsGateways -security_sds_ingress,TestTlsGateways/runtestmultitlsgateways1.example.com -security_sds_ingress,TestTlsGateways/runtestmultitlsgateways2.example.com -security_sds_ingress,TestTlsGateways/runtestmultitlsgateways3.example.com -security_sds_ingress,TestTlsGateways/runtestmultitlsgateways4.example.com -security_sds_ingress,TestTlsGateways/runtestmultitlsgateways5.example.com -security_sds_ingress_k8sca,TestMtlsGatewaysK8sca -security_sds_ingress_k8sca,TestMtlsGatewaysK8sca/runtestmultimtlsgateways1.example.com -security_sds_ingress_k8sca,TestMtlsGatewaysK8sca/runtestmultimtlsgateways2.example.com -security_sds_ingress_k8sca,TestMtlsGatewaysK8sca/runtestmultimtlsgateways3.example.com -security_sds_ingress_k8sca,TestMtlsGatewaysK8sca/runtestmultimtlsgateways4.example.com -security_sds_ingress_k8sca,TestMtlsGatewaysK8sca/runtestmultimtlsgateways5.example.com -security_sds_ingress_k8sca,TestTlsGatewaysK8sca -security_sds_ingress_k8sca,TestTlsGatewaysK8sca/runtestmultitlsgateways1.example.com -security_sds_ingress_k8sca,TestTlsGatewaysK8sca/runtestmultitlsgateways2.example.com -security_sds_ingress_k8sca,TestTlsGatewaysK8sca/runtestmultitlsgateways3.example.com -security_sds_ingress_k8sca,TestTlsGatewaysK8sca/runtestmultitlsgateways4.example.com -security_sds_ingress_k8sca,TestTlsGatewaysK8sca/runtestmultitlsgateways5.example.com -security_webhook,TestWebhookManagement -sidecar_scope_test,TestServiceEntryDNS -sidecar_scope_test,TestServiceEntryDNSNoSelfImport -sidecar_scope_test,TestServiceEntryStatic -sidecar_scope_test,TestSidecarScopeIngressListener -stats_filter_test,TestStatsFilter -stats_filter_wasm_test,TestWasmStatsFilter -stats_tcp_filter,TestTcpMetric -telemetry,TestDashboard -telemetry,TestDashboard/istio-mesh-dashboard.json -telemetry,TestDashboard/istio-performance-dashboard.json -telemetry,TestDashboard/istio-service-dashboard.json -telemetry,TestDashboard/istio-workload-dashboard.json -telemetry,TestDashboard/pilot-dashboard.json -telemetry_outboundtrafficpolicy,TestOutboundTrafficPolicy_AllowAny -telemetry_outboundtrafficpolicy,TestOutboundTrafficPolicy_RegistryOnly -telemetry_requestclassification,TestRequestClassification -telemetry_stats_prometheus_http_nullvm,TestStatsFilter -telemetry_stats_prometheus_http_wasm,TestWasmStatsFilter -telemetry_stats_prometheus_tcp,TestTcpMetric -telemetry_test,TestDashboard -telemetry_test,TestDashboard/istio-mesh-dashboard.json -telemetry_test,TestDashboard/istio-performance-dashboard.json -telemetry_test,TestDashboard/istio-service-dashboard.json -telemetry_test,TestDashboard/istio-workload-dashboard.json -telemetry_test,TestDashboard/pilot-dashboard.json -telemetry_tracing_clienttracing,TestClientTracing -telemetry_tracing_servertracing,TestProxyTracing -tracing_test,TestProxyTracing -trafficmanagement,TestCircuitBreaking -trafficmanagement,TestFaultInjection -trafficmanagement,TestMirror -trafficmanagement,TestMirroring -trafficmanagement,TestRequestRouting -trafficmanagement,TestRequestTimeouts -trafficmanagement,TestTCPTrafficShifting -trafficmanagement,TestTrafficShifting -trafficmanagement_egress,TestTlsOrigination -trafficmanagement_ingress,TestIngressControl -trafficmanagement_ingress,TestIngressSNIPassthrough -trafficmanagement_ingress,TestSecureIngress diff --git a/pkg/test/framework/features/features.gen.go b/pkg/test/framework/features/features.gen.go deleted file mode 100644 index 632569720..000000000 --- a/pkg/test/framework/features/features.gen.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//WARNING: THIS IS AN AUTO-GENERATED FILE, DO NOT EDIT. - -package features - -const ( - Observability Feature = "observability" - Security_Certificates_Citadel Feature = "security.certificates.citadel" - Security_Certificates_LetsEncrypt Feature = "security.certificates.lets-encrypt" - Security_Certificates_Spire Feature = "security.certificates.spire" - Security_MTLS_OnByDefault Feature = "security.mTLS.on-by-default" - Traffic_CircuitBreakers_FailGracefully Feature = "traffic.circuit-breakers.fail-gracefully" - Usability_Introspection_Status_Analysis_ContainsMessageWhenFalse Feature = "usability.introspection.status.analysis.contains-message-when-false" - Usability_Introspection_Status_Analysis_TrueWhenWarnOnly Feature = "usability.introspection.status.analysis.true-when-warn-only" - Usability_Introspection_Status_Distribution_EventuallyTrue Feature = "usability.introspection.status.distribution.eventually-true" - Usability_Introspection_Status_Distribution_ImmediatelyFalse Feature = "usability.introspection.status.distribution.immediately-false" - Usability_Observability_Status Feature = "usability.observability.status" - Usability_Observability_Status_DefaultExists Feature = "usability.observability.status.default-exists" -) diff --git a/pkg/test/framework/features/features.go b/pkg/test/framework/features/features.go deleted file mode 100644 index ff26455e7..000000000 --- a/pkg/test/framework/features/features.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package features - -import ( - "fmt" - "os" - "strings" -) - -import ( - "istio.io/pkg/log" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/env" -) - -type Feature string - -// Checker ensures that the values passed to Features() are in the features.yaml file -type Checker interface { - Check(feature Feature) (check bool, scenario string) -} - -type checkerImpl struct { - m map[string]interface{} -} - -func BuildChecker(yamlPath string) (Checker, error) { - data, err := os.ReadFile(yamlPath) - if err != nil { - log.Errorf("Error reading feature file: %s", yamlPath) - return nil, err - } - m := make(map[string]interface{}) - - err = yaml.Unmarshal(data, &m) - if err != nil { - log.Errorf("Error parsing features file: %s", err) - return nil, err - } - return &checkerImpl{m["features"].(map[string]interface{})}, nil -} - -// returns true if the feature is defined in features.yaml, -// false if not -func (c *checkerImpl) Check(feature Feature) (check bool, scenario string) { - return checkPathSegment(c.m, strings.Split(string(feature), ".")) -} - -func checkPathSegment(m map[string]interface{}, path []string) (check bool, scenario string) { - if len(path) < 1 { - return false, "" - } - segment := path[0] - if val, ok := m[segment]; ok { - if valmap, ok := val.(map[string]interface{}); ok { - return checkPathSegment(valmap, path[1:]) - } else if val == nil { - return true, strings.Join(path[1:], ".") - } - } - return false, "" -} - -var GlobalAllowlist = fromFile(env.IstioSrc + "/pkg/test/framework/features/allowlist.txt") - -type Allowlist struct { - hashset map[string]bool -} - -func fromFile(path string) *Allowlist { - result := &Allowlist{hashset: map[string]bool{}} - data, err := os.ReadFile(path) - if err != nil { - log.Errorf("Error reading allowlist file: %s", path) - return nil - } - for _, i := range strings.Split(string(data), "\n") { - if strings.HasPrefix(i, "//") { - continue - } - result.hashset[i] = true - } - return result -} - -func (w *Allowlist) Contains(suite, test string) bool { - _, ok := w.hashset[fmt.Sprintf("%s,%s", suite, test)] - return ok -} diff --git a/pkg/test/framework/features/features.yaml b/pkg/test/framework/features/features.yaml deleted file mode 100644 index 348e7c6c9..000000000 --- a/pkg/test/framework/features/features.yaml +++ /dev/null @@ -1,176 +0,0 @@ -# Copyright 2020 Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http:#www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Edit this file to add more feature labels for the testing dashboard -# Each element must be a map key in order to be valid yaml. -features: - # features that relate to measuring or exploring your service mesh or the services which comprise it. - observability: - telemetry: - stackdriver: - logging: - stats: - prometheus: - customize-metric: - merge: - http: - nullvm: - wasm: - tcp: - request-classification: - tracing: - client: - server: - dashboard: - istioctl: - # features relating to controlling the traffic of the service mesh. - traffic: - locality: - reachability: - shifting: - routing: - short-circuiting: - mirroring: - ingress: - loadbalancing: - custom: - topology: - ratelimit: - envoy: - original-source-ip: - mcs: - autoexport: - servicediscovery: - cni: - race-condition-repair: - upgrade: - gateway: - usability: - observability: - describe: - version: - wait: - status: - proxy-config: - proxy-status: - authz-check: - remote-clusters: - analysis: - line-numbers: - tcp-probe: - grpc-probe: - helpers: - add-to-mesh: - remove-from-mesh: - kube-inject: - # features that allow users to secure their services and service mesh. - security: - externalca: - reachability: - peer: - secure-naming: - trust-domain-validation: - trust-domain-alias-secure-naming: - file-mounted-certs: - ecc-signature-algorithm: - multiple-root: - user: - ingress: - mtls: - secretrotation: - generic-compoundrotation: - nongeneric-compoundrotation: - gateway: - tls: - secretrotation: - gateway: - invalid-secret: - K8sca: - valid-secret: - quic: - sds: - tls: - mtls: - sds-k8sca: - tls: - mtls: - egress: - mtls: - sds: - tls: - filebased: - sds: - authorization: - mtls-local: - jwt-token: - workload-selector: - deny-action: - negative-match: - ingress-gateway: - egress-gateway: - tcp: - conditions: - grpc-protocol: - path-normalization: - custom: - authentication: - jwt: - ingressjwt: - reachability: - healthcheck: - filterchain: - control-plane: - k8s-certs: - dns-certificate: - k8sca: - jwt: - webhook: - plugin-cert: - normalization: - fuzz: - authorization: - jwt: - # features which allow extending or deep integrations with istio and other products - extensibility: - wasm: - remote-load: - # features releated to the lifecycle of istio installations - installation: - istioctl: - install: - uninstall_revision: - uninstall_manifest: - uninstall_purge: - postinstall_verify: - revision_centric_view: - revision_tags: - multicluster: - cluster_local: - multimaster: - remote: - centralremotekubeconfig: - istiodremote: - helm: - default: - install: - upgrade: - firstpartyjwt: - install: - upgrade: - # describes internal build and testing infrastrcuture - infrastructure: - # the testing framework - framework: - vm: - autoregistration: diff --git a/pkg/test/framework/integration/component.go b/pkg/test/framework/integration/component.go deleted file mode 100644 index 3bc9a15f4..000000000 --- a/pkg/test/framework/integration/component.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package integration - -import ( - "io" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -var _ io.Closer = &component{} - -type component struct { - name string - id resource.ID - handleClose func(*component) -} - -func (c *component) ID() resource.ID { - return c.id -} - -func (c *component) Close() error { - if c.handleClose != nil { - c.handleClose(c) - } - return nil -} - -func newComponent(ctx resource.Context, name string, handleClose func(*component)) *component { - c := &component{ - name: name, - handleClose: handleClose, - } - c.id = ctx.TrackResource(c) - return c -} diff --git a/pkg/test/framework/integration/framework_test.go b/pkg/test/framework/integration/framework_test.go deleted file mode 100644 index 7399c967f..000000000 --- a/pkg/test/framework/integration/framework_test.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package integration - -import ( - "fmt" - "sync" - "testing" - "time" -) - -import ( - "go.uber.org/atomic" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework" -) - -func TestParallel(t *testing.T) { - var top, l1a, l1b, l2a, l2b, l2c, l2d *component - - closeTimes := make(map[string]time.Time) - mutex := &sync.Mutex{} - closeHandler := func(c *component) { - mutex.Lock() - defer mutex.Unlock() - closeTimes[c.name] = time.Now() - - // Sleep briefly to force time separation between close events. - time.Sleep(100 * time.Millisecond) - } - - assertClosedBefore := func(t *testing.T, c1, c2 *component) { - t.Helper() - if !closeTimes[c1.name].Before(closeTimes[c2.name]) { - t.Fatalf("%s closed after %s", c1.name, c2.name) - } - } - - framework.NewTest(t). - Run(func(ctx framework.TestContext) { - ctx.NewSubTest("top"). - Run(func(ctx framework.TestContext) { - // NOTE: top can't be parallel for this test since it will exit before the children run, - // which means we won't be able to verify the results here. - top = newComponent(ctx, ctx.Name(), closeHandler) - - ctx.NewSubTest("l1a"). - RunParallel(func(ctx framework.TestContext) { - l1a = newComponent(ctx, ctx.Name(), closeHandler) - - ctx.NewSubTest("l2a"). - RunParallel(func(ctx framework.TestContext) { - l2a = newComponent(ctx, ctx.Name(), closeHandler) - }) - - ctx.NewSubTest("l2b"). - RunParallel(func(ctx framework.TestContext) { - l2b = newComponent(ctx, ctx.Name(), closeHandler) - }) - }) - - ctx.NewSubTest("l1b"). - RunParallel(func(ctx framework.TestContext) { - l1b = newComponent(ctx, ctx.Name(), closeHandler) - - ctx.NewSubTest("l2c"). - RunParallel(func(ctx framework.TestContext) { - l2c = newComponent(ctx, ctx.Name(), closeHandler) - }) - - ctx.NewSubTest("l2d"). - RunParallel(func(ctx framework.TestContext) { - l2d = newComponent(ctx, ctx.Name(), closeHandler) - }) - }) - }) - }) - - assertClosedBefore(t, l2a, l1a) - assertClosedBefore(t, l2b, l1a) - assertClosedBefore(t, l2c, l1b) - assertClosedBefore(t, l2d, l1b) - assertClosedBefore(t, l1a, top) - assertClosedBefore(t, l1b, top) -} - -// Validate that cleanup is done synchronously for asynchronous tests -func TestParallelWhenDone(t *testing.T) { - errors := make(chan error, 10) - framework.NewTest(t).Features("infrastructure.framework"). - Run(func(ctx framework.TestContext) { - runCheck(ctx, errors, "root") - ctx.NewSubTest("nested-serial").Run(func(ctx framework.TestContext) { - runCheck(ctx, errors, "nested-serial") - }) - ctx.NewSubTest("nested-parallel").RunParallel(func(ctx framework.TestContext) { - runCheck(ctx, errors, "nested-parallel") - }) - }) - - for { - select { - case e := <-errors: - t.Error(e) - default: - return - } - } -} - -func runCheck(ctx framework.TestContext, errors chan error, name string) { - currentTest := atomic.NewString("") - for _, c := range []string{"a", "b", "c"} { - c := c - ctx.NewSubTest(c).Run(func(ctx framework.TestContext) { - // Store the test name. We will check this in ConditionalCleanup to ensure we call finish cleanup before the next test runs - currentTest.Store(c) - ctx.ConditionalCleanup(func() { - time.Sleep(time.Millisecond * 100) - if ct := currentTest.Load(); ct != c { - errors <- fmt.Errorf("expected current test for %s to be %s but was %s", name, c, ct) - } - }) - for _, st := range []string{"p1", "p2"} { - ctx.NewSubTest(st). - RunParallel(func(ctx framework.TestContext) {}) - } - }) - } -} diff --git a/pkg/test/framework/integration/main_test.go b/pkg/test/framework/integration/main_test.go deleted file mode 100644 index 2b832adef..000000000 --- a/pkg/test/framework/integration/main_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package integration - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -func TestMain(m *testing.M) { - framework.NewSuite(m).EnvironmentFactory(resource.NilEnvironmentFactory).Run() -} diff --git a/pkg/test/framework/label/filter.go b/pkg/test/framework/label/filter.go deleted file mode 100644 index c9f715472..000000000 --- a/pkg/test/framework/label/filter.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package label - -import ( - "fmt" - "regexp" - "strings" -) - -import ( - "istio.io/pkg/log" -) - -// Selector is a Set of label filter expressions that get applied together to decide whether tests should be selected -// for execution or not. -type Selector struct { - // The constraints are and'ed together. - present Set - absent Set -} - -var _ fmt.Stringer = Selector{} - -// NewSelector returns a new selector based on the given presence/absence predicates. -func NewSelector(present []Instance, absent []Instance) Selector { - return Selector{ - present: NewSet(present...), - absent: NewSet(absent...), - } -} - -var userLabelRegex = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z0-9_]+)*$`) - -// ParseSelector parses and returns a new instance of Selector. -func ParseSelector(s string) (Selector, error) { - var present, absent []Instance - - parts := strings.Split(s, ",") - for _, p := range parts { - if len(p) == 0 { - continue - } - - var negative bool - switch p[0] { - case '-': - negative = true - p = p[1:] - case '+': - p = p[1:] - } - - if !userLabelRegex.MatchString(p) { - return Selector{}, fmt.Errorf("invalid label name: %q", p) - } - - l := Instance(p) - if !all.contains(l) { - log.Warnf("unknown label name: %q", p) - continue - } - - if negative { - absent = append(absent, l) - } else { - present = append(present, l) - } - } - - pSet := NewSet(present...) - aSet := NewSet(absent...) - - if pSet.containsAny(aSet) || aSet.containsAny(pSet) { - return Selector{}, fmt.Errorf("conflicting selector specification: %q", s) - } - - return NewSelector(present, absent), nil -} - -// Selects returns true, if the given label set satisfies the Selector. -func (f *Selector) Selects(inputs Set) bool { - return !inputs.containsAny(f.absent) && inputs.containsAll(f.present) -} - -// Excludes returns false, if the given set of labels, even combined with new ones, could end up satisfying the Selector. -// It returns false, if Matches would never return true, even if new labels are added to the input set. -func (f *Selector) Excludes(inputs Set) bool { - return inputs.containsAny(f.absent) -} - -func (f Selector) String() string { - var result string - - for _, p := range f.present.All() { - if result != "" { - result += "," - } - result += "+" + string(p) - } - - for _, p := range f.absent.All() { - if result != "" { - result += "," - } - result += "-" + string(p) - } - - return result -} diff --git a/pkg/test/framework/label/instance.go b/pkg/test/framework/label/instance.go deleted file mode 100644 index 04548ed38..000000000 --- a/pkg/test/framework/label/instance.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package label - -import ( - "sort" - "strings" -) - -// Instance is a label instance. -type Instance string - -// Set is a set of labels -type Set map[Instance]struct{} - -// NewSet returns a new label set. -func NewSet(labels ...Instance) Set { - s := make(map[Instance]struct{}) - for _, l := range labels { - s[l] = struct{}{} - } - - return s -} - -// Add adds the given labels and returns a new, combined set -func (l Set) Add(labels ...Instance) Set { - c := l.Clone() - for _, label := range labels { - c[label] = struct{}{} - } - - return c -} - -// Merge returns a set that is merging of l and s. -func (l Set) Merge(s Set) Set { - c := l.Clone() - for k, v := range s { - c[k] = v - } - - return c -} - -// Clone this set of labels -func (l Set) Clone() Set { - s := make(map[Instance]struct{}) - for k, v := range l { - s[k] = v - } - - return s -} - -// All returns all labels in this set. -func (l Set) All() []Instance { - r := make([]Instance, 0, len(l)) - for label := range l { - r = append(r, label) - } - - sort.Slice(r, func(i, j int) bool { - return strings.Compare(string(r[i]), string(r[j])) < 0 - }) - return r -} - -func (l Set) contains(label Instance) bool { - _, found := l[label] - return found -} - -func (l Set) containsAny(other Set) bool { - for l2 := range other { - if l.contains(l2) { - return true - } - } - - return false -} - -func (l Set) containsAll(other Set) bool { - for l2 := range other { - if l.contains(l2) { - continue - } - return false - } - - return true -} diff --git a/pkg/test/framework/label/labels.go b/pkg/test/framework/label/labels.go deleted file mode 100644 index ca72bd8db..000000000 --- a/pkg/test/framework/label/labels.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package label - -const ( - // Postsubmit indicates that the test should be run as part of a postsubmit run only. - Postsubmit Instance = "postsubmit" - - // CustomSetup indicates that the test requires a custom Istio installation. - CustomSetup Instance = "customsetup" - - // IPv4 indicates a test is only compatible with IPv4 clusters. - // Any usage of this should have an associated GitHub issue to make it compatible with IPv6 - IPv4 Instance = "ipv4" -) - -var all = NewSet( - Postsubmit, - CustomSetup, - IPv4) - -// Find the label with the given name -func Find(name string) (Instance, bool) { - candidate := Instance(name) - if _, ok := all[candidate]; ok { - return candidate, true - } - - return "", false -} diff --git a/pkg/test/framework/label/labels_test.go b/pkg/test/framework/label/labels_test.go deleted file mode 100644 index e900bc65a..000000000 --- a/pkg/test/framework/label/labels_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package label - -import ( - "strconv" - "testing" -) - -func TestLabels(t *testing.T) { - tests := []struct { - filter string - labels Set - expected bool - err bool - }{ - {filter: "", labels: nil, expected: true}, - {filter: "", labels: NewSet(Postsubmit), expected: true}, - {filter: "", labels: NewSet(Postsubmit, CustomSetup), expected: true}, - {filter: "$requires.kube", labels: NewSet(Postsubmit, CustomSetup), err: true}, - {filter: "zoo", labels: NewSet(Postsubmit, CustomSetup), expected: true}, - {filter: "postsubmit", labels: NewSet(Postsubmit), expected: true}, - {filter: "postsubmit", labels: NewSet(CustomSetup), expected: false}, - {filter: "postsubmit", labels: NewSet(CustomSetup, Postsubmit), expected: true}, - {filter: "postsubmit,customsetup", labels: NewSet(Postsubmit, CustomSetup), expected: true}, - {filter: "postsubmit,customsetup", labels: NewSet(Postsubmit), expected: false}, - {filter: "+postsubmit,+customsetup", labels: NewSet(Postsubmit), expected: false}, - {filter: "postsubmit,+customsetup", labels: NewSet(Postsubmit, CustomSetup), expected: true}, - {filter: "-postsubmit", labels: NewSet(), expected: true}, - {filter: "-postsubmit", labels: NewSet(Postsubmit), expected: false}, - {filter: "-postsubmit,-customsetup", labels: NewSet(), expected: true}, - {filter: "-postsubmit,customsetup", labels: NewSet(Postsubmit, CustomSetup), expected: false}, - {filter: "-postsubmit,customsetup", labels: NewSet(CustomSetup), expected: true}, - {filter: "-postsubmit,customsetup", labels: NewSet(Postsubmit), expected: false}, - {filter: "-postsubmit,customsetup", labels: NewSet(), expected: false}, - {filter: "-postsubmit,-postsubmit", labels: NewSet(), expected: true}, - {filter: "-postsubmit,-postsubmit", labels: NewSet(Postsubmit), expected: false}, - {filter: "postsubmit,postsubmit", labels: NewSet(), expected: false}, - {filter: "postsubmit,postsubmit", labels: NewSet(Postsubmit), expected: true}, - {filter: "postsubmit,postsubmit", labels: NewSet(), expected: false}, - {filter: "postsubmit,-postsubmit", labels: NewSet(), err: true}, - } - - for i, te := range tests { - t.Run(strconv.FormatInt(int64(i), 10), func(t *testing.T) { - f, err := ParseSelector(te.filter) - if err != nil { - if te.err { - return - } - t.Fatalf("Unexpected error: %v, filter:%q, labels:%v", err, te.filter, te.labels) - } else if te.err { - t.Fatalf("Expected error not found: filter:%q, labels:%v", te.filter, te.labels) - } - - actual := f.Selects(te.labels) - if actual != te.expected { - t.Fatalf("Mismatch: got:%v, wanted: %v, filter:%q, labels:%v", actual, te.expected, te.filter, te.labels) - } - }) - } -} diff --git a/pkg/test/framework/logging.go b/pkg/test/framework/logging.go deleted file mode 100644 index db4164713..000000000 --- a/pkg/test/framework/logging.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package framework - -import ( - "flag" - "io" -) - -import ( - "google.golang.org/grpc/grpclog" - "istio.io/pkg/log" -) - -var logOptionsFromCommandline = log.DefaultOptions() - -func init() { - logOptionsFromCommandline.AttachFlags( - func(p *[]string, name string, value []string, usage string) { - // TODO(ozben): Implement string array method for capturing the complete set of log settings. - }, - flag.StringVar, - flag.IntVar, - flag.BoolVar) -} - -func configureLogging() error { - o := *logOptionsFromCommandline - - o.LogGrpc = false - grpclog.SetLoggerV2(grpclog.NewLoggerV2(io.Discard, io.Discard, io.Discard)) - - return log.Configure(&o) -} diff --git a/pkg/test/framework/operations.go b/pkg/test/framework/operations.go deleted file mode 100644 index 280dfa767..000000000 --- a/pkg/test/framework/operations.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package framework - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/label" -) - -// Run runs the given test. -func Run(t *testing.T, fn func(ctx TestContext)) { - NewTest(t).Run(fn) -} - -// NewContext creates a new test context and returns. It is up to the caller to close to context by calling -// .Done() at the end of the test run. -func NewContext(goTest *testing.T, labels ...label.Instance) TestContext { - return newRootContext(nil, goTest, labels...) -} - -// newRootContext creates a new TestContext that has no parent. Delegates to the global runtime. -func newRootContext(test *testImpl, goTest *testing.T, labels ...label.Instance) *testContext { - rtMu.Lock() - defer rtMu.Unlock() - - if rt == nil { - panic("call to scope without running the test framework") - } - - return rt.newRootContext(test, goTest, label.NewSet(labels...)) -} diff --git a/pkg/test/framework/resource/context.go b/pkg/test/framework/resource/context.go deleted file mode 100644 index c75ee45e1..000000000 --- a/pkg/test/framework/resource/context.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/yml" -) - -type ConfigOptions struct { - NoCleanup bool - Wait bool -} - -type ConfigOption func(o *ConfigOptions) - -// NoCleanup does not delete the applied Config once it goes out of scope. -var NoCleanup ConfigOption = func(o *ConfigOptions) { - o.NoCleanup = true -} - -// Wait for the Config to be applied everywhere. -var Wait ConfigOption = func(o *ConfigOptions) { - o.Wait = true -} - -// ConfigFactory is a factory for creating new Config resources. -type ConfigFactory interface { - // YAML adds YAML content to this config for the given namespace. - YAML(ns string, yamlText ...string) Config - - // File reads the given YAML files and adds their content to this config - // for the given namespace. - File(ns string, paths ...string) Config - - // Eval the same as YAML, but it evaluates the template parameters. - Eval(ns string, args interface{}, yamlText ...string) Config - - // EvalFile the same as File, but it evaluates the template parameters. - EvalFile(ns string, args interface{}, paths ...string) Config -} - -// Config builds a configuration that can be applied or deleted as a single -type Config interface { - // ConfigFactory for appending to this Config - ConfigFactory - - // Copy this Config - Copy() Config - - // Apply this config to all clusters within the ConfigManager - Apply(opts ...ConfigOption) error - ApplyOrFail(t test.Failer, opts ...ConfigOption) - - // Delete this config from all clusters within the ConfigManager - Delete() error - DeleteOrFail(t test.Failer) -} - -// ConfigManager is an interface for applying/deleting yaml resources. -type ConfigManager interface { - ConfigFactory - - // New empty Config. - New() Config - - // WithFilePrefix sets the prefix used for intermediate files. - WithFilePrefix(prefix string) ConfigManager -} - -// Context is the core context interface that is used by resources. -type Context interface { - yml.FileWriter - - // TrackResource tracks a resource in this context. If the context is closed, then the resource will be - // cleaned up. - TrackResource(r Resource) ID - - // GetResource accepts either a *T or *[]*T where T implements Resource. - // For a non-slice pointer, the value will be assigned to the first matching resource. - // For a slice pointer, the matching resources from this scope and its parent(s) will be appended. - // If ref is not a pointer, an error will be returned. - // If there is no match for a non-slice pointer, an error will be returned. - GetResource(ref interface{}) error - - // The Environment in which the tests run - Environment() Environment - - // Clusters in this Environment. There will always be at least one. - Clusters() cluster.Clusters - - // AllClusters in this Environment, including external control planes. - AllClusters() cluster.Clusters - - // Settings returns common settings - Settings() *Settings - - // ConditionalCleanup runs the given function when the test context completes. - // If -istio.test.nocleanup is set, this function will not be executed. To unconditionally cleanup, use Cleanup. - // This function may not (safely) access the test context. - ConditionalCleanup(fn func()) - - // Cleanup runs the given function when the test context completes. - // This function will always run, regardless of -istio.test.nocleanup. To run only when cleanup is enabled, - // use WhenDone. - // This function may not (safely) access the test context. - Cleanup(fn func()) - - // CreateDirectory creates a new subdirectory within this context. - CreateDirectory(name string) (string, error) - - // CreateTmpDirectory creates a new temporary directory within this context. - CreateTmpDirectory(prefix string) (string, error) - - // ConfigKube returns a ConfigManager that writes config to the provided clusers. If - // no clusters are provided, writes to all clusters in the mesh. - ConfigKube(clusters ...cluster.Cluster) ConfigManager - - // ConfigIstio returns a ConfigManager that writes config to all Istio config clusters. - ConfigIstio() ConfigManager - - // RecordTraceEvent records an event. This is later saved to trace.yaml for analysis - RecordTraceEvent(key string, value interface{}) -} diff --git a/pkg/test/framework/resource/dumper.go b/pkg/test/framework/resource/dumper.go deleted file mode 100644 index 398f8aa99..000000000 --- a/pkg/test/framework/resource/dumper.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -// Dumper is an interface that is implemented by all components that can dump their state. In CI, it is -// useful to get as much context as possible when a test fails. Dumper allows dumping of state from a test. -type Dumper interface { - Dump(ctx Context) -} diff --git a/pkg/test/framework/resource/environment.go b/pkg/test/framework/resource/environment.go deleted file mode 100644 index 8434b690b..000000000 --- a/pkg/test/framework/resource/environment.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" -) - -// EnvironmentFactory creates an Environment. -type EnvironmentFactory func(ctx Context) (Environment, error) - -var _ EnvironmentFactory = NilEnvironmentFactory - -// NilEnvironmentFactory is an EnvironmentFactory that returns nil. -func NilEnvironmentFactory(Context) (Environment, error) { - return nil, nil -} - -// Environment is the ambient environment that the test runs in. -type Environment interface { - Resource - - EnvironmentName() string - - // Mesh clusters in this Environment. There will always be at least one. - Clusters() cluster.Clusters - - // All clusters in this Environment, including external control planes. - AllClusters() cluster.Clusters - IsMultinetwork() bool -} diff --git a/pkg/test/framework/resource/flags.go b/pkg/test/framework/resource/flags.go deleted file mode 100644 index 6a4d2d216..000000000 --- a/pkg/test/framework/resource/flags.go +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -import ( - "flag" - "fmt" - "os" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/env" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/config" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/label" -) - -var settingsFromCommandLine = DefaultSettings() - -// SettingsFromCommandLine returns settings obtained from command-line flags. config.Parse must be called before -// calling this function. -func SettingsFromCommandLine(testID string) (*Settings, error) { - if !config.Parsed() { - panic("config.Parse must be called before this function") - } - - s := settingsFromCommandLine.Clone() - s.TestID = testID - - f, err := label.ParseSelector(s.SelectorString) - if err != nil { - return nil, err - } - s.Selector = f - - s.SkipMatcher, err = NewMatcher(s.SkipString) - if err != nil { - return nil, err - } - - // NOTE: not using echo.VM, etc. here to avoid circular dependency. - if s.SkipVM { - s.SkipWorkloadClasses = append(s.SkipWorkloadClasses, "vm") - } - if s.SkipTProxy { - s.SkipWorkloadClasses = append(s.SkipWorkloadClasses, "tproxy") - } - if s.SkipDelta { - // TODO we may also want to trigger this if we have an old verion - s.SkipWorkloadClasses = append(s.SkipWorkloadClasses, "delta") - } - - if s.Image.Hub == "" { - s.Image.Hub = env.HUB.ValueOrDefault("gcr.io/istio-testing") - } - - if s.Image.Tag == "" { - s.Image.Tag = env.TAG.ValueOrDefault("latest") - } - - if s.Image.PullPolicy == "" { - s.Image.PullPolicy = env.PULL_POLICY.ValueOrDefault("Always") - } - - if err = validate(s); err != nil { - return nil, err - } - - return s, nil -} - -// validate checks that user has not passed invalid flag combinations to test framework. -func validate(s *Settings) error { - if s.FailOnDeprecation && s.NoCleanup { - return fmt.Errorf("checking for deprecation occurs at cleanup level, thus flags -istio.test.nocleanup and" + - " -istio.test.deprecation_failure must not be used at the same time") - } - - if s.Revision != "" { - if s.Revisions != nil { - return fmt.Errorf("cannot use --istio.test.revision and --istio.test.revisions at the same time," + - " --istio.test.revisions will take precedence and --istio.test.revision will be ignored") - } - // use Revision as the sole revision in RevVerMap - s.Revisions = RevVerMap{ - s.Revision: "", - } - } else if s.Revisions != nil { - // TODO(Monkeyanator) remove once existing jobs are migrated to use compatibility flag. - s.Compatibility = true - } - - if s.Revisions == nil && s.Compatibility { - return fmt.Errorf("cannot use --istio.test.compatibility without setting --istio.test.revisions") - } - - if s.Image.Hub == "" || s.Image.Tag == "" { - return fmt.Errorf("values for Hub & Tag are not detected. Please supply them through command-line or via environment") - } - - return nil -} - -// init registers the command-line flags that we can exposed for "go test". -func init() { - flag.StringVar(&settingsFromCommandLine.BaseDir, "istio.test.work_dir", os.TempDir(), - "Local working directory for creating logs/temp files. If left empty, os.TempDir() is used.") - - var env string - flag.StringVar(&env, "istio.test.env", "", "Deprecated. This flag does nothing") - - flag.BoolVar(&settingsFromCommandLine.NoCleanup, "istio.test.nocleanup", settingsFromCommandLine.NoCleanup, - "Do not cleanup resources after test completion") - - flag.BoolVar(&settingsFromCommandLine.CIMode, "istio.test.ci", settingsFromCommandLine.CIMode, - "Enable CI Mode. Additional logging and state dumping will be enabled.") - - flag.StringVar(&settingsFromCommandLine.SelectorString, "istio.test.select", settingsFromCommandLine.SelectorString, - "Comma separated list of labels for selecting tests to run (e.g. 'foo,+bar-baz').") - - flag.Var(&settingsFromCommandLine.SkipString, "istio.test.skip", - "Skip tests matching the regular expression. This follows the semantics of -test.run.") - - flag.Var(&settingsFromCommandLine.SkipWorkloadClasses, "istio.test.skipWorkloads", - "Skips deploying and using workloads of the given comma-separated classes (e.g. vm, proxyless, etc.)") - - flag.IntVar(&settingsFromCommandLine.Retries, "istio.test.retries", settingsFromCommandLine.Retries, - "Number of times to retry tests") - - flag.BoolVar(&settingsFromCommandLine.StableNamespaces, "istio.test.stableNamespaces", settingsFromCommandLine.StableNamespaces, - "If set, will use consistent namespace rather than randomly generated. Useful with nocleanup to develop tests.") - - flag.BoolVar(&settingsFromCommandLine.FailOnDeprecation, "istio.test.deprecation_failure", settingsFromCommandLine.FailOnDeprecation, - "Make tests fail if any usage of deprecated stuff (e.g. Envoy flags) is detected.") - - flag.StringVar(&settingsFromCommandLine.Revision, "istio.test.revision", settingsFromCommandLine.Revision, - "If set to XXX, overwrite the default namespace label (istio-injection=enabled) with istio.io/rev=XXX.") - - flag.BoolVar(&settingsFromCommandLine.SkipVM, "istio.test.skipVM", settingsFromCommandLine.SkipVM, - "Skip VM related parts in all tests.") - - flag.BoolVar(&settingsFromCommandLine.SkipDelta, "istio.test.skipDelta", settingsFromCommandLine.SkipDelta, - "Skip Delta XDS related parts in all tests.") - - flag.BoolVar(&settingsFromCommandLine.SkipTProxy, "istio.test.skipTProxy", settingsFromCommandLine.SkipTProxy, - "Skip TProxy related parts in all tests.") - - flag.BoolVar(&settingsFromCommandLine.Compatibility, "istio.test.compatibility", settingsFromCommandLine.Compatibility, - "Transparently deploy echo instances pointing to each revision set in `Revisions`") - - flag.Var(&settingsFromCommandLine.Revisions, "istio.test.revisions", "Istio CP revisions available to the test framework and their corresponding versions.") - - flag.StringVar(&settingsFromCommandLine.Image.Hub, "istio.test.hub", settingsFromCommandLine.Image.Hub, - "Container registry hub to use") - flag.StringVar(&settingsFromCommandLine.Image.Tag, "istio.test.tag", settingsFromCommandLine.Image.Tag, - "Common Container tag to use when deploying container images") - flag.StringVar(&settingsFromCommandLine.Image.PullPolicy, "istio.test.pullpolicy", settingsFromCommandLine.Image.PullPolicy, - "Common image pull policy to use when deploying container images") - flag.StringVar(&settingsFromCommandLine.Image.PullSecret, "istio.test.imagePullSecret", settingsFromCommandLine.Image.PullSecret, - "Path to a file containing a DockerConfig secret use for test apps. This will be pushed to all created namespaces."+ - "Secret should already exist when used with istio.test.stableNamespaces.") -} diff --git a/pkg/test/framework/resource/flags_test.go b/pkg/test/framework/resource/flags_test.go deleted file mode 100644 index 8719fc7fd..000000000 --- a/pkg/test/framework/resource/flags_test.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package resource - -import ( - "testing" -) - -import ( - "github.com/google/go-cmp/cmp" -) - -func TestValidate(t *testing.T) { - tcs := []struct { - name string - settings *Settings - expectErr bool - expectedRevs RevVerMap - }{ - { - name: "fail on deprecation and nocleanup", - settings: &Settings{ - FailOnDeprecation: true, - NoCleanup: true, - }, - expectErr: true, - }, - { - name: "fail on both revision and revisions flag", - settings: &Settings{ - Revision: "a", - Compatibility: false, - Revisions: RevVerMap{ - "b": "", - }, - }, - expectErr: true, - }, - { - name: "fail when compatibility mode but no revisions", - settings: &Settings{ - Compatibility: true, - }, - expectErr: true, - }, - { - name: "revision flag converted to revvermap", - settings: &Settings{ - Revision: "a", - Compatibility: false, - }, - expectedRevs: RevVerMap{ - "a": "", - }, - }, - } - - for _, tc := range tcs { - t.Run(tc.name, func(t *testing.T) { - err := validate(tc.settings) - if tc.expectErr { - if err == nil { - t.Error("expected error but got none") - } - return - } - if tc.expectedRevs != nil { - if diff := cmp.Diff(tc.expectedRevs, tc.settings.Revisions); diff != "" { - t.Errorf("unexpected revisions, got: %v, want: %v, diff: %v", - tc.settings.Revisions, tc.expectedRevs, diff) - } - } - }) - } -} diff --git a/pkg/test/framework/resource/matcher.go b/pkg/test/framework/resource/matcher.go deleted file mode 100644 index ebd5b0aa2..000000000 --- a/pkg/test/framework/resource/matcher.go +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -import ( - "regexp" - "strconv" - "strings" -) - -// testFilter is a regex matcher on a test. It is split by / following go subtest format -type testFilter []*regexp.Regexp - -type Matcher struct { - filters []testFilter -} - -// NewMatcher reimplements the logic of Go's -test.run. The code is mostly directly copied from Go's source. -func NewMatcher(regexs []string) (*Matcher, error) { - filters := []testFilter{} - for _, regex := range regexs { - filter := splitRegexp(regex) - for i, s := range filter { - filter[i] = rewrite(s) - } - rxs := []*regexp.Regexp{} - for _, f := range filter { - r, err := regexp.Compile(f) - if err != nil { - return nil, err - } - rxs = append(rxs, r) - } - filters = append(filters, rxs) - } - return &Matcher{filters: filters}, nil -} - -func (m *Matcher) MatchTest(testName string) bool { - for _, f := range m.filters { - if matchSingle(f, testName) { - return true - } - } - return false -} - -func matchSingle(filter testFilter, testName string) bool { - if len(filter) == 0 { - // No regex defined, we default to NOT matching. This ensures our default skips nothing - return false - } - elem := strings.Split(testName, "/") - if len(filter) > len(elem) { - return false - } - for i, s := range elem { - if i >= len(filter) { - break - } - if !filter[i].MatchString(s) { - return false - } - } - return true -} - -// From go/src/testing/match.go -// nolint -func splitRegexp(s string) []string { - a := make([]string, 0, strings.Count(s, "/")) - cs := 0 - cp := 0 - for i := 0; i < len(s); { - switch s[i] { - case '[': - cs++ - case ']': - if cs--; cs < 0 { // An unmatched ']' is legal. - cs = 0 - } - case '(': - if cs == 0 { - cp++ - } - case ')': - if cs == 0 { - cp-- - } - case '\\': - i++ - case '/': - if cs == 0 && cp == 0 { - a = append(a, s[:i]) - s = s[i+1:] - i = 0 - continue - } - } - i++ - } - return append(a, s) -} - -// rewrite rewrites a subname to having only printable characters and no white -// space. -// From go/src/testing/match.go -func rewrite(s string) string { - b := []byte{} - for _, r := range s { - switch { - case isSpace(r): - b = append(b, '_') - case !strconv.IsPrint(r): - s := strconv.QuoteRune(r) - b = append(b, s[1:len(s)-1]...) - default: - b = append(b, string(r)...) - } - } - return string(b) -} - -// From go/src/testing/match.go -func isSpace(r rune) bool { - if r < 0x2000 { - switch r { - // Note: not the same as Unicode Z class. - case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0, 0x1680: - return true - } - } else { - if r <= 0x200a { - return true - } - switch r { - case 0x2028, 0x2029, 0x202f, 0x205f, 0x3000: - return true - } - } - return false -} diff --git a/pkg/test/framework/resource/matcher_test.go b/pkg/test/framework/resource/matcher_test.go deleted file mode 100644 index 6016e517e..000000000 --- a/pkg/test/framework/resource/matcher_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -import ( - "testing" -) - -func TestMatcher(t *testing.T) { - cases := []struct { - name string - input []string - matches []string - nomatches []string - }{ - { - name: "empty", - input: []string{}, - matches: []string{}, - nomatches: []string{"", "foo", "foo/bar", ".*"}, - }, - { - name: "single", - input: []string{"Foo"}, - matches: []string{"TestFoo", "TestMyFooBar", "TestFoo/TestBar"}, - nomatches: []string{"baz", "baz/foo", "TestBar/TestFoo"}, - }, - { - name: "double", - input: []string{"Foo/Bar"}, - matches: []string{"TestFoo/TestBar", "TestFoo/TestBar/TestBaz"}, - nomatches: []string{"TestFoo", "TestBar", "TestMyFooBar"}, - }, - { - name: "space", - input: []string{"TestFoo/with space"}, - matches: []string{"TestFoo/with_space"}, - }, - { - name: "regex", - input: []string{"Foo/.*/Baz"}, - matches: []string{"TestFoo/something/TestBaz"}, - nomatches: []string{"TestFoo", "TestFoo/TestBaz", "TestFoo/something/TestBar"}, - }, - { - name: "multiple", - input: []string{"TestFoo/subset", "TestBar"}, - matches: []string{"TestBar", "TestBar/subtest", "TestFoo/subset"}, - nomatches: []string{"TestFoo/something/subset", "TestFoo/something", "TestFoo", "TestFoo/TestBar"}, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - matcher, err := NewMatcher(tt.input) - if err != nil { - t.Fatal(err) - } - for _, m := range tt.matches { - got := matcher.MatchTest(m) - if !got { - t.Errorf("expected match for %q", m) - } - } - for _, m := range tt.nomatches { - got := matcher.MatchTest(m) - if got { - t.Errorf("expected no match for %q", m) - } - } - }) - } -} diff --git a/pkg/test/framework/resource/resource.go b/pkg/test/framework/resource/resource.go deleted file mode 100644 index 17302f958..000000000 --- a/pkg/test/framework/resource/resource.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -import ( - "fmt" -) - -// Resource of a resource. -type Resource interface { - // ID used for debugging the resource instance. - ID() ID -} - -// ID for the resource instance. This is allocated by the framework and passed here. -type ID interface { - fmt.Stringer -} - -var _ ID = FakeID("") - -// FakeID used for testing. -type FakeID string - -func (id FakeID) String() string { - return string(id) -} - -var _ Resource = &FakeResource{} - -// FakeResource used for testing. -type FakeResource struct { - IDValue string - OtherValue string -} - -func (f *FakeResource) ID() ID { - return FakeID(f.IDValue) -} - -// GetOtherValue is an additional method used to distinguish this resource API from others. -func (f *FakeResource) GetOtherValue() string { - return f.OtherValue -} diff --git a/pkg/test/framework/resource/settings.go b/pkg/test/framework/resource/settings.go deleted file mode 100644 index c0804a4c5..000000000 --- a/pkg/test/framework/resource/settings.go +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -import ( - "fmt" - "os" - "path" - "strings" -) - -import ( - "github.com/google/uuid" - "gopkg.in/yaml.v3" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/label" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -const ( - // maxTestIDLength is the maximum length allowed for testID. - maxTestIDLength = 30 -) - -// ImageSettings for container images. -type ImageSettings struct { - // Hub value to use in Helm templates - Hub string - - // Tag value to use in Helm templates - Tag string - - // Image pull policy to use for deployments. If not specified, the defaults of each deployment will be used. - PullPolicy string - - // PullSecret path to a file containing a k8s secret in yaml so test pods can pull from protected registries. - PullSecret string -} - -func (s *ImageSettings) PullSecretName() (string, error) { - if s.PullSecret == "" { - return "", nil - } - data, err := os.ReadFile(s.PullSecret) - if err != nil { - return "", err - } - secret := unstructured.Unstructured{Object: map[string]interface{}{}} - if err := yaml.Unmarshal(data, secret.Object); err != nil { - return "", err - } - return secret.GetName(), nil -} - -func (s *ImageSettings) PullSecretNameOrFail(t test.Failer) string { - t.Helper() - out, err := s.PullSecretName() - if err != nil { - t.Fatal(err) - } - return out -} - -// Settings is the set of arguments to the test driver. -type Settings struct { - // Name of the test - TestID string - - RunID uuid.UUID - - // Do not cleanup the resources after the test run. - NoCleanup bool - - // Indicates that the tests are running in CI Mode - CIMode bool - - // Should the tests fail if usage of deprecated stuff (e.g. Envoy flags) is detected - FailOnDeprecation bool - - // Local working directory root for creating temporary directories / files in. If left empty, - // os.TempDir() will be used. - BaseDir string - - // The number of times to retry failed tests. - // This should not be depended on as a primary means for reducing test flakes. - Retries int - - // If enabled, namespaces will be reused rather than created with dynamic names each time. - // This is useful when combined with NoCleanup, to allow quickly iterating on tests. - StableNamespaces bool - - // The label selector that the user has specified. - SelectorString string - - // The regex specifying which tests to skip. This follows inverted semantics of golang's - // -test.run flag, which only supports positive match. If an entire package is meant to be - // excluded, it can be filtered with `go list` and explicitly passing the list of desired - // packages. For example: `go test $(go list ./... | grep -v bad-package)`. - SkipString ArrayFlags - SkipMatcher *Matcher - - // SkipWorkloadClasses can be used to skip deploying special workload types like TPROXY, VMs, etc. - SkipWorkloadClasses ArrayFlags - - // The label selector, in parsed form. - Selector label.Selector - - // EnvironmentFactory allows caller to override the environment creation. If nil, a default is used based - // on the known environment names. - EnvironmentFactory EnvironmentFactory - - // Deprecated: prefer to use `--istio.test.revisions=`. - // The revision label on a namespace for injection webhook. - // If set to XXX, all the namespaces created with istio-injection=enabled will be replaced with istio.io/rev=XXX. - Revision string - - // Skip VM related parts for all the tests. - SkipVM bool - - // Skip Delta XDS related parts for all the tests. - SkipDelta bool - - // Skip TProxy related parts for all the tests. - SkipTProxy bool - - // Compatibility determines whether we should transparently deploy echo workloads attached to each revision - // specified in `Revisions` when creating echo instances. Used primarily for compatibility testing between revisions - // on different control plane versions. - Compatibility bool - - // Revisions maps the Istio revisions that are available to each cluster to their corresponding versions. - // This flag must be used with --istio.test.kube.deploy=false with the versions pre-installed. - // This flag should be passed in as comma-separated values, such as "rev-a=1.7.3,rev-b=1.8.2,rev-c=1.9.0", and the test framework will - // spin up pods pointing to these revisions for each echo instance and skip tests accordingly. - // To configure it so that an Istio revision is on the latest version simply list the revision name without the version (i.e. "rev-a,rev-b") - // If using this flag with --istio.test.revision, this flag will take precedence. - Revisions RevVerMap - - // Image settings - Image ImageSettings -} - -func (s Settings) Skip(class string) bool { - return s.SkipWorkloadClassesAsSet().Contains(class) -} - -func (s *Settings) SkipWorkloadClassesAsSet() sets.Set { - return sets.New(s.SkipWorkloadClasses...) -} - -// RunDir is the name of the dir to output, for this particular run. -func (s *Settings) RunDir() string { - u := strings.Replace(s.RunID.String(), "-", "", -1) - t := strings.Replace(s.TestID, "_", "-", -1) - // We want at least 6 characters of uuid padding - padding := maxTestIDLength - len(t) - if padding < 0 { - padding = 0 - } - n := fmt.Sprintf("%s-%s", t, u[0:padding]) - - return path.Join(s.BaseDir, n) -} - -// Clone settings -func (s *Settings) Clone() *Settings { - cl := *s - return &cl -} - -// DefaultSettings returns a default settings instance. -func DefaultSettings() *Settings { - return &Settings{ - RunID: uuid.New(), - } -} - -// String implements fmt.Stringer -func (s *Settings) String() string { - result := "" - - result += fmt.Sprintf("TestID: %s\n", s.TestID) - result += fmt.Sprintf("RunID: %s\n", s.RunID.String()) - result += fmt.Sprintf("NoCleanup: %v\n", s.NoCleanup) - result += fmt.Sprintf("BaseDir: %s\n", s.BaseDir) - result += fmt.Sprintf("Selector: %v\n", s.Selector) - result += fmt.Sprintf("FailOnDeprecation: %v\n", s.FailOnDeprecation) - result += fmt.Sprintf("CIMode: %v\n", s.CIMode) - result += fmt.Sprintf("Retries: %v\n", s.Retries) - result += fmt.Sprintf("StableNamespaces: %v\n", s.StableNamespaces) - result += fmt.Sprintf("Revision: %v\n", s.Revision) - result += fmt.Sprintf("SkipWorkloads %v\n", s.SkipWorkloadClasses) - result += fmt.Sprintf("Compatibility: %v\n", s.Compatibility) - result += fmt.Sprintf("Revisions: %v\n", s.Revisions.String()) - result += fmt.Sprintf("Hub: %s\n", s.Image.Hub) - result += fmt.Sprintf("Tag: %s\n", s.Image.Tag) - result += fmt.Sprintf("PullPolicy: %s\n", s.Image.PullPolicy) - result += fmt.Sprintf("PullSecret: %s\n", s.Image.PullSecret) - return result -} - -type ArrayFlags []string - -func (i *ArrayFlags) String() string { - return fmt.Sprint([]string(*i)) -} - -func (i *ArrayFlags) Set(value string) error { - *i = append(*i, value) - return nil -} diff --git a/pkg/test/framework/resource/setup.go b/pkg/test/framework/resource/setup.go deleted file mode 100644 index ee6cf0f09..000000000 --- a/pkg/test/framework/resource/setup.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -// SetupFn is a function used for performing setup actions. -type SetupFn func(ctx Context) error - -// ShouldSkipFn is a function used for performing skip actions; if it returns true a job is skipped -// Note: function may be called multiple times during the setup process. -type ShouldSkipFn func(ctx Context) bool diff --git a/pkg/test/framework/resource/version.go b/pkg/test/framework/resource/version.go deleted file mode 100644 index 8636a63a0..000000000 --- a/pkg/test/framework/resource/version.go +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -import ( - "fmt" - "sort" - "strconv" - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/config" -) - -var _ config.Value = &RevVerMap{} - -// RevVerMap maps installed revisions to their Istio versions. -type RevVerMap map[string]IstioVersion - -func (rv *RevVerMap) SetConfig(mi interface{}) error { - m, ok := mi.(config.Map) - if !ok { - return fmt.Errorf("revisions map: expected map but got slice") - } - out := make(RevVerMap) - for k := range m { - version := m.String(k) - v, err := NewIstioVersion(version) - if err != nil { - return fmt.Errorf("could not parse %s as version: %w", - version, err) - } - out[k] = v - } - *rv = out - return nil -} - -// Set parses IstioVersions from a string flag in the form "a=1.5.6,b,c=1.4". -// If no version is specified for a revision assume latest, represented as "" -func (rv *RevVerMap) Set(value string) error { - m := make(map[string]IstioVersion) - rvPairs := strings.Split(value, ",") - for _, rv := range rvPairs { - s := strings.Split(rv, "=") - rev := s[0] - if len(s) == 1 { - m[rev] = "" - } else if len(s) == 2 { - ver := s[1] - v, err := NewIstioVersion(ver) - if err != nil { - return fmt.Errorf("could not parse %s as version: %w", - ver, err) - } - m[rev] = v - } else { - return fmt.Errorf("invalid revision<->version pairing specified: %q", rv) - } - } - *rv = m - return nil -} - -func (rv *RevVerMap) String() string { - if rv == nil { - return "" - } - var rvPairs []string - for rev, ver := range *rv { - if ver == "" { - ver = "latest" - } - rvPairs = append(rvPairs, - fmt.Sprintf("%s=%s", rev, ver)) - } - return strings.Join(rvPairs, ",") -} - -// Versions returns the Istio versions present in the given RevVerMap. -func (rv *RevVerMap) Versions() IstioVersions { - if rv == nil { - return nil - } - var vers []IstioVersion - for _, v := range *rv { - vers = append(vers, v) - } - return vers -} - -// Default returns the revision with the newest `IstioVersion`, or in the case of a tie, the first -// alphabetically. -func (rv *RevVerMap) Default() string { - if rv == nil || len(*rv) == 0 { - return "" - } - max := rv.Maximum() - var candidates []string - for rev, ver := range *rv { - if ver.Compare(max) == 0 { - candidates = append(candidates, rev) - } - } - if len(candidates) == 0 { - panic("could not find revision with max IstioVersion") - } - sort.Strings(candidates) - if candidates[0] == "default" { - return "" - } - return candidates[0] -} - -// Minimum returns the minimum version from the revision-version mapping. -func (rv *RevVerMap) Minimum() IstioVersion { - return rv.Versions().Minimum() -} - -// Maximum returns the maximum version from the revision-version mapping. -func (rv *RevVerMap) Maximum() IstioVersion { - return rv.Versions().Maximum() -} - -// AtLeast returns true if the minimum Istio version under test is at least the given version. -func (rv *RevVerMap) AtLeast(v IstioVersion) bool { - return rv.Versions().Minimum().Compare(v) >= 0 -} - -// IsMultiVersion returns whether the associated IstioVersions have multiple specified versions. -func (rv *RevVerMap) IsMultiVersion() bool { - return rv != nil && len(*rv) > 0 -} - -// TemplateMap creates a map of revisions and versions suitable for templating. -func (rv *RevVerMap) TemplateMap() map[string]string { - if rv == nil { - return nil - } - templateMap := make(map[string]string) - if len(*rv) == 0 { - // if there are no entries, generate a dummy value so that we don't render - // deployment template with empty loop - templateMap[""] = "" - return templateMap - } - for r, v := range *rv { - templateMap[r] = string(v) - } - return templateMap -} - -// IstioVersion is an Istio version running within a cluster. -type IstioVersion string - -// IstioVersions represents a collection of Istio versions running in a cluster. -type IstioVersions []IstioVersion - -// NewIstioVersion creates an IstioVersion with validation. -func NewIstioVersion(s string) (IstioVersion, error) { - // empty version string sentinel value for latest - if s == "" { - return "", nil - } - parts := strings.Split(s, ".") - if len(parts) < 2 || len(parts) > 3 { - return "", fmt.Errorf("cannot parse version from %s", s) - } - for _, part := range parts { - if _, err := strconv.Atoi(part); err != nil { - return "", fmt.Errorf("cannot use %s as version part", part) - } - } - return IstioVersion(s), nil -} - -// Compare compares two Istio versions. Returns -1 if version "v" is less than "other", 0 if the same, -// and 1 if "v" is greater than "other". -func (v IstioVersion) Compare(other IstioVersion) int { - ver := model.ParseIstioVersion(string(v)) - otherVer := model.ParseIstioVersion(string(other)) - return ver.Compare(otherVer) -} - -// Minimum returns the minimum from a set of IstioVersions -// returns empty value if no versions. -func (v IstioVersions) Minimum() IstioVersion { - if len(v) == 0 { - return "" - } - min := v[0] - for i := 1; i < len(v); i++ { - ver := v[i] - if ver.Compare(min) < 0 { - min = ver - } - } - return min -} - -// Maximum returns the maximum from a set of IstioVersions -// returns empty value if no versions. -func (v IstioVersions) Maximum() IstioVersion { - if len(v) == 0 { - return "" - } - max := v[0] - for i := 1; i < len(v); i++ { - ver := v[i] - if ver.Compare(max) > 0 { - max = ver - } - } - return max -} diff --git a/pkg/test/framework/resource/version_test.go b/pkg/test/framework/resource/version_test.go deleted file mode 100644 index df2124fb3..000000000 --- a/pkg/test/framework/resource/version_test.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resource - -import ( - "fmt" - "strconv" - "testing" -) - -func TestCompareIstioVersion(t *testing.T) { - tcs := []struct { - a, b IstioVersion - result int - }{ - { - IstioVersion("1.4"), - IstioVersion("1.5"), - -1, - }, - { - IstioVersion("1.9.0"), - IstioVersion("1.10"), - -1, - }, - { - IstioVersion("1.8.0"), - IstioVersion("1.8.1"), - -1, - }, - { - IstioVersion("1.9.1"), - IstioVersion("1.9.1"), - 0, - }, - { - IstioVersion("1.12"), - IstioVersion("1.3"), - 1, - }, - { - IstioVersion(""), - IstioVersion(""), - 0, - }, - { - IstioVersion(""), - IstioVersion("1.9"), - 1, - }, - } - - for _, tc := range tcs { - t.Run(fmt.Sprintf("compare version %s->%s", tc.a, tc.b), func(t *testing.T) { - r := tc.a.Compare(tc.b) - if r != tc.result { - t.Errorf("expected %d, got %d", tc.result, r) - } - }) - } -} - -func TestMinimumIstioVersion(t *testing.T) { - tcs := []struct { - name string - versions IstioVersions - result IstioVersion - }{ - { - "two versions", - IstioVersions([]IstioVersion{ - "1.4", "1.5", - }), - IstioVersion("1.4"), - }, - { - "three versions", - IstioVersions([]IstioVersion{ - "1.9", "1.14", "1.10", - }), - IstioVersion("1.9"), - }, - { - "single version", - IstioVersions([]IstioVersion{ - "1.9", - }), - IstioVersion("1.9"), - }, - } - - for _, tc := range tcs { - t.Run(tc.name, func(t *testing.T) { - min := tc.versions.Minimum() - if min != tc.result { - t.Errorf("expected %v, got %v", tc.result, min) - } - }) - } -} - -func TestAtLeast(t *testing.T) { - tcs := []struct { - name string - versions RevVerMap - version IstioVersion - result bool - }{ - { - "not at least", - makeRevVerMap("1.4", "1.5"), - IstioVersion("1.8"), - false, - }, - { - "tied with minimum", - makeRevVerMap("1.4", "1.5"), - IstioVersion("1.4"), - true, - }, - { - "lower than minimum", - makeRevVerMap("1.4", "1.5"), - IstioVersion("1.3"), - true, - }, - { - "no versions", - makeRevVerMap(), - IstioVersion("1.8"), - true, - }, - } - - for _, tc := range tcs { - t.Run(tc.name, func(t *testing.T) { - min := tc.versions.AtLeast(tc.version) - if min != tc.result { - t.Errorf("expected %v, got %v", tc.result, min) - } - }) - } -} - -func makeRevVerMap(versions ...string) RevVerMap { - m := make(map[string]IstioVersion) - for i, v := range versions { - m[strconv.Itoa(i)] = IstioVersion(v) - } - return m -} diff --git a/pkg/test/framework/resourceid.go b/pkg/test/framework/resourceid.go deleted file mode 100644 index 93f5503a1..000000000 --- a/pkg/test/framework/resourceid.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package framework - -type resourceID struct { - id string -} - -func (r *resourceID) String() string { - return r.id -} diff --git a/pkg/test/framework/runtime.go b/pkg/test/framework/runtime.go deleted file mode 100644 index 11ff3b153..000000000 --- a/pkg/test/framework/runtime.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package framework - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/label" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -var _ resource.Dumper = &runtime{} - -// runtime for the test environment. -type runtime struct { - context *suiteContext -} - -// newRuntime returns a new runtime instance. -func newRuntime(s *resource.Settings, fn resource.EnvironmentFactory, labels label.Set) (*runtime, error) { - ctx, err := newSuiteContext(s, fn, labels) - if err != nil { - return nil, err - } - return &runtime{ - context: ctx, - }, nil -} - -// Dump state for all allocated resources. -func (i *runtime) Dump(ctx resource.Context) { - i.context.globalScope.dump(ctx, true) -} - -func (i *runtime) DumpShallow(ctx resource.Context) { - i.context.globalScope.dump(ctx, false) -} - -// suiteContext returns the suiteContext. -func (i *runtime) suiteContext() *suiteContext { - return i.context -} - -// newRootContext creates and returns a new testContext with no parent. -func (i *runtime) newRootContext(test *testImpl, goTest *testing.T, labels label.Set) *testContext { - return newTestContext(test, goTest, i.context, nil, labels) -} - -// Close implements io.Closer -func (i *runtime) Close() error { - return i.context.globalScope.done(i.context.settings.NoCleanup) -} diff --git a/pkg/test/framework/scope.go b/pkg/test/framework/scope.go deleted file mode 100644 index cf5f2d511..000000000 --- a/pkg/test/framework/scope.go +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package framework - -import ( - "fmt" - "io" - "reflect" - "sync" - "time" -) - -import ( - "github.com/hashicorp/go-multierror" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" -) - -// scope hold resources in a particular scope. -type scope struct { - // friendly name for the scope for debugging purposes. - id string - - parent *scope - - resources []resource.Resource - - closers []io.Closer - - children []*scope - - closeChan chan struct{} - - skipDump bool - - // Mutex to lock changes to resources, children, and closers that can be done concurrently - mu sync.Mutex -} - -func newScope(id string, p *scope) *scope { - s := &scope{ - id: id, - parent: p, - closeChan: make(chan struct{}, 1), - } - - if p != nil { - p.children = append(p.children, s) - } - - return s -} - -func (s *scope) add(r resource.Resource, id *resourceID) { - scopes.Framework.Debugf("Adding resource for tracking: %v", id) - s.mu.Lock() - defer s.mu.Unlock() - s.resources = append(s.resources, r) - - if c, ok := r.(io.Closer); ok { - s.closers = append(s.closers, c) - } -} - -func (s *scope) get(ref interface{}) error { - s.mu.Lock() - defer s.mu.Unlock() - - refVal := reflect.ValueOf(ref) - if refVal.Kind() != reflect.Ptr { - return fmt.Errorf("ref must be a pointer instead got: %T", ref) - } - // work with the underlying value rather than the pointer - refVal = refVal.Elem() - - targetT := refVal.Type() - if refVal.Kind() == reflect.Slice { - // for slices look at the element type - targetT = targetT.Elem() - } - - for _, res := range s.resources { - if res == nil { - continue - } - resVal := reflect.ValueOf(res) - if resVal.Type().AssignableTo(targetT) { - if refVal.Kind() == reflect.Slice { - refVal.Set(reflect.Append(refVal, resVal)) - } else { - refVal.Set(resVal) - return nil - } - } - } - - if s.parent != nil { - // either didn't find the value or need to continue filling the slice - return s.parent.get(ref) - } - - if refVal.Kind() != reflect.Slice { - // didn't find the non-slice value - return fmt.Errorf("no %v in context", targetT) - } - - return nil -} - -func (s *scope) addCloser(c io.Closer) { - s.mu.Lock() - defer s.mu.Unlock() - s.closers = append(s.closers, c) -} - -func (s *scope) done(nocleanup bool) error { - scopes.Framework.Debugf("Begin cleaning up scope: %v", s.id) - - // First, wait for all of the children to be done. - for _, c := range s.children { - c.waitForDone() - } - - // Upon returning, notify the parent that we're done. - defer func() { - close(s.closeChan) - }() - - var err error - // Do reverse walk for cleanup. - for i := len(s.closers) - 1; i >= 0; i-- { - c := s.closers[i] - - if nocleanup { - if cc, ok := c.(*closer); ok && cc.noskip { - continue - } else if !ok { - continue - } - } - - name := "lambda" - if r, ok := c.(resource.Resource); ok { - name = fmt.Sprintf("resource %v", r.ID()) - } - - scopes.Framework.Debugf("Begin cleaning up %s", name) - if e := c.Close(); e != nil { - scopes.Framework.Debugf("Error cleaning up %s: %v", name, e) - err = multierror.Append(err, e).ErrorOrNil() - } - scopes.Framework.Debugf("Cleanup complete for %s", name) - } - - s.mu.Lock() - s.resources = nil - s.closers = nil - s.mu.Unlock() - - scopes.Framework.Debugf("Done cleaning up scope: %v", s.id) - return err -} - -func (s *scope) waitForDone() { - <-s.closeChan -} - -func (s *scope) skipDumping() { - s.mu.Lock() - defer s.mu.Unlock() - s.skipDump = true -} - -func (s *scope) dump(ctx resource.Context, recursive bool) { - s.mu.Lock() - skip := s.skipDump - s.mu.Unlock() - if skip { - return - } - st := time.Now() - defer func() { - scopes.Framework.Debugf("Done dumping scope: %s (%v)", s.id, time.Since(st)) - }() - s.mu.Lock() - defer s.mu.Unlock() - if recursive { - for _, c := range s.children { - c.dump(ctx, recursive) - } - } - wg := sync.WaitGroup{} - for _, c := range s.resources { - if d, ok := c.(resource.Dumper); ok { - d := d - wg.Add(1) - go func() { - d.Dump(ctx) - wg.Done() - }() - } - } - wg.Wait() -} diff --git a/pkg/test/framework/scope_test.go b/pkg/test/framework/scope_test.go deleted file mode 100644 index 843163539..000000000 --- a/pkg/test/framework/scope_test.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package framework - -import ( - "fmt" - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -func TestGet_Struct(t *testing.T) { - res := &resource.FakeResource{ - IDValue: "my-fake-resource", - } - - tests := map[string]struct { - setup func() *scope - expError error - }{ - "exists": { - setup: func() *scope { - scope := newScope("s", nil) - scope.add(res, &resourceID{id: res.IDValue}) - return scope - }, - }, - "parent": { - setup: func() *scope { - p := newScope("p", nil) - p.add(res, &resourceID{id: res.IDValue}) - scope := newScope("s", p) - return scope - }, - }, - "missing": { - setup: func() *scope { return newScope("s", nil) }, - expError: fmt.Errorf("no framework.OtherInterface in context"), - }, - } - - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - g := NewWithT(t) - scope := tt.setup() - var got OtherInterface - err := scope.get(&got) - if tt.expError == nil { - g.Expect(err).To(BeNil()) - g.Expect(got).To(Equal(res)) - } else { - g.Expect(err).To(Equal(tt.expError)) - } - }) - } -} - -func TestGet_Slice(t *testing.T) { - exp := []*resource.FakeResource{ - { - IDValue: "child-resource", - OtherValue: "child", - }, - { - IDValue: "parent-resource", - OtherValue: "parent", - }, - } - - g := NewWithT(t) - parent := newScope("parent", nil) - parent.add(exp[1], &resourceID{id: exp[1].IDValue}) - child := newScope("child", parent) - child.add(exp[0], &resourceID{id: exp[0].IDValue}) - var got []OtherInterface - err := child.get(&got) - g.Expect(err).To(BeNil()) - g.Expect(got).To(HaveLen(len(exp))) - for i, res := range exp { - g.Expect(got[i]).To(Equal(res)) - } -} diff --git a/pkg/test/framework/suite.go b/pkg/test/framework/suite.go deleted file mode 100644 index abcdffcfa..000000000 --- a/pkg/test/framework/suite.go +++ /dev/null @@ -1,589 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package framework - -import ( - "errors" - "fmt" - "os" - "path" - "path/filepath" - "regexp" - goruntime "runtime" - "strings" - "sync" - "testing" - "time" -) - -import ( - "gopkg.in/yaml.v2" - "istio.io/pkg/log" -) - -import ( - kubelib "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/echo" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/environment/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/config" - ferrors "github.com/apache/dubbo-go-pixiu/pkg/test/framework/errors" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/label" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/file" -) - -// test.Run uses 0, 1, 2 exit codes. Use different exit codes for our framework. -const ( - // Indicates a framework-level init error - exitCodeInitError = -1 - - // Indicates an error due to the setup function supplied by the user - exitCodeSetupError = -2 -) - -var ( - rt *runtime - rtMu sync.Mutex - - // Well-known paths which are stripped when generating test IDs. - // Note: Order matters! Always specify the most specific directory first. - wellKnownPaths = mustCompileAll( - // This allows us to trim test IDs on the istio.io/istio.io repo. - ".*/istio.io/istio.io/", - - // These allow us to trim test IDs on istio.io/istio repo. - ".*/istio.io/istio/tests/integration/", - ".*/istio.io/istio/", - - // These are also used for istio.io/istio, but make help to satisfy - // the feature label enforcement when running with BUILD_WITH_CONTAINER=1. - "^/work/tests/integration/", - "^/work/", - - // Outside of standard Istio GOPATH - ".*/istio/tests/integration/", - ) -) - -// getSettingsFunc is a function used to extract the default settings for the Suite. -type getSettingsFunc func(string) (*resource.Settings, error) - -// mRunFn abstracts testing.M.run, so that the framework itself can be tested. -type mRunFn func(ctx *suiteContext) int - -// Suite allows the test author to specify suite-related metadata and do setup in a fluent-style, before commencing execution. -type Suite interface { - // EnvironmentFactory sets a custom function used for creating the resource.Environment for this Suite. - EnvironmentFactory(fn resource.EnvironmentFactory) Suite - // Label all the tests in suite with the given labels - Label(labels ...label.Instance) Suite - // SkipIf skips the suite if the function returns true - SkipIf(reason string, fn resource.ShouldSkipFn) Suite - // Skip marks a suite as skipped with the given reason. This will prevent any setup functions from occurring. - Skip(reason string) Suite - // RequireMinClusters ensures that the current environment contains at least the given number of clusters. - // Otherwise it stops test execution. - RequireMinClusters(minClusters int) Suite - // RequireMaxClusters ensures that the current environment contains at least the given number of clusters. - // Otherwise it stops test execution. - RequireMaxClusters(maxClusters int) Suite - // RequireSingleCluster is a utility method that requires that there be exactly 1 cluster in the environment. - // - // Deprecated: All new tests should support multiple clusters. - RequireSingleCluster() Suite - // RequireMultiPrimary ensures that each cluster is running a control plane. - // - // Deprecated: All new tests should work for any control plane topology. - RequireMultiPrimary() Suite - // RequireMinVersion validates the environment meets a minimum version - RequireMinVersion(minorVersion uint) Suite - // RequireMaxVersion validates the environment meets a maximum version - RequireMaxVersion(minorVersion uint) Suite - // Setup runs enqueues the given setup function to run before test execution. - Setup(fn resource.SetupFn) Suite - // Run the suite. This method calls os.Exit and does not return. - Run() -} - -// suiteImpl will actually run the test suite -type suiteImpl struct { - testID string - skipMessage string - skipFn resource.ShouldSkipFn - mRun mRunFn - osExit func(int) - labels label.Set - - requireFns []resource.SetupFn - setupFns []resource.SetupFn - - getSettings getSettingsFunc - envFactory resource.EnvironmentFactory -} - -// Given the filename of a test, derive its suite name -func deriveSuiteName(caller string) string { - d := filepath.Dir(caller) - // We will trim out paths preceding some well known paths. This should handle anything in istio or docs repo, - // as well as special case tests/integration. The end result is a test under ./tests/integration/pilot/ingress - // will become pilot_ingress - // Note: if this fails to trim, we end up with "ugly" suite names but otherwise no real impact. - for _, wellKnownPath := range wellKnownPaths { - // Try removing this path from the directory name. - result := wellKnownPath.ReplaceAllString(d, "") - if len(result) < len(d) { - // Successfully found and removed this path from the directory. - d = result - break - } - } - return strings.ReplaceAll(d, "/", "_") -} - -// NewSuite returns a new suite instance. -func NewSuite(m *testing.M) Suite { - _, f, _, _ := goruntime.Caller(1) - suiteName := deriveSuiteName(f) - - if analyze() { - return newSuiteAnalyzer( - suiteName, - func(_ *suiteContext) int { - return m.Run() - }, - os.Exit) - } - - return newSuite(suiteName, - func(_ *suiteContext) int { - return m.Run() - }, - os.Exit, - getSettings) -} - -func newSuite(testID string, fn mRunFn, osExit func(int), getSettingsFn getSettingsFunc) *suiteImpl { - s := &suiteImpl{ - testID: testID, - mRun: fn, - osExit: osExit, - getSettings: getSettingsFn, - labels: label.NewSet(), - } - - return s -} - -func (s *suiteImpl) EnvironmentFactory(fn resource.EnvironmentFactory) Suite { - if fn != nil && s.envFactory != nil { - scopes.Framework.Warn("EnvironmentFactory overridden multiple times for Suite") - } - s.envFactory = fn - return s -} - -func (s *suiteImpl) Label(labels ...label.Instance) Suite { - s.labels = s.labels.Add(labels...) - return s -} - -func (s *suiteImpl) Skip(reason string) Suite { - s.skipMessage = reason - s.skipFn = func(ctx resource.Context) bool { - return true - } - return s -} - -func (s *suiteImpl) SkipIf(reason string, fn resource.ShouldSkipFn) Suite { - s.skipMessage = reason - s.skipFn = fn - return s -} - -func (s *suiteImpl) RequireMinClusters(minClusters int) Suite { - if minClusters <= 0 { - minClusters = 1 - } - - fn := func(ctx resource.Context) error { - if len(clusters(ctx)) < minClusters { - s.Skip(fmt.Sprintf("Number of clusters %d does not exceed minimum %d", - len(clusters(ctx)), minClusters)) - } - return nil - } - - s.requireFns = append(s.requireFns, fn) - return s -} - -func (s *suiteImpl) RequireMaxClusters(maxClusters int) Suite { - if maxClusters <= 0 { - maxClusters = 1 - } - - fn := func(ctx resource.Context) error { - if len(clusters(ctx)) > maxClusters { - s.Skip(fmt.Sprintf("Number of clusters %d exceeds maximum %d", - len(clusters(ctx)), maxClusters)) - } - return nil - } - - s.requireFns = append(s.requireFns, fn) - return s -} - -func (s *suiteImpl) RequireSingleCluster() Suite { - return s.RequireMinClusters(1).RequireMaxClusters(1) -} - -func (s *suiteImpl) RequireMultiPrimary() Suite { - fn := func(ctx resource.Context) error { - for _, c := range ctx.Clusters() { - if !c.IsPrimary() { - s.Skip(fmt.Sprintf("Cluster %s is not using a local control plane", - c.Name())) - } - } - return nil - } - - s.requireFns = append(s.requireFns, fn) - return s -} - -func (s *suiteImpl) RequireMinVersion(minorVersion uint) Suite { - fn := func(ctx resource.Context) error { - for _, c := range ctx.Clusters().Kube() { - ver, err := c.GetKubernetesVersion() - if err != nil { - return fmt.Errorf("failed to get Kubernetes version: %v", err) - } - if !kubelib.IsAtLeastVersion(c, minorVersion) { - s.Skip(fmt.Sprintf("Required Kubernetes version (1.%v) is greater than current: %v", - minorVersion, ver.String())) - } - } - return nil - } - - s.requireFns = append(s.requireFns, fn) - return s -} - -func (s *suiteImpl) RequireMaxVersion(minorVersion uint) Suite { - fn := func(ctx resource.Context) error { - for _, c := range ctx.Clusters().Kube() { - ver, err := c.GetKubernetesVersion() - if err != nil { - return fmt.Errorf("failed to get Kubernetes version: %v", err) - } - if !kubelib.IsLessThanVersion(c, minorVersion+1) { - s.Skip(fmt.Sprintf("Maximum Kubernetes version (1.%v) is less than current: %v", - minorVersion, ver.String())) - } - } - return nil - } - - s.requireFns = append(s.requireFns, fn) - return s -} - -func (s *suiteImpl) Setup(fn resource.SetupFn) Suite { - s.setupFns = append(s.setupFns, fn) - return s -} - -func (s *suiteImpl) runSetupFn(fn resource.SetupFn, ctx SuiteContext) (err error) { - defer func() { - // Dump if the setup function fails - if err != nil && ctx.Settings().CIMode { - rt.Dump(ctx) - } - }() - err = fn(ctx) - return -} - -func (s *suiteImpl) Run() { - s.osExit(s.run()) -} - -func (s *suiteImpl) isSkipped(ctx SuiteContext) bool { - if s.skipFn != nil && s.skipFn(ctx) { - return true - } - return false -} - -func (s *suiteImpl) doSkip(ctx *suiteContext) int { - scopes.Framework.Infof("Skipping suite %q: %s", ctx.Settings().TestID, s.skipMessage) - - // Mark this suite as skipped in the context. - ctx.skipped = true - - // Run the tests so that the golang test framework exits normally. The tests will not run because - // they see that this suite has been skipped. - _ = s.mRun(ctx) - - // Return success. - return 0 -} - -func (s *suiteImpl) run() (errLevel int) { - if err := initRuntime(s); err != nil { - scopes.Framework.Errorf("Error during test framework init: %v", err) - return exitCodeInitError - } - - ctx := rt.suiteContext() - // Skip the test if its explicitly skipped - if s.isSkipped(ctx) { - return s.doSkip(ctx) - } - - // Before starting, check whether the current set of labels & label selectors will ever allow us to run tests. - // if not, simply exit now. - if ctx.Settings().Selector.Excludes(s.labels) { - s.Skip(fmt.Sprintf("Label mismatch: labels=%v, selector=%v", - s.labels, - ctx.Settings().Selector)) - return s.doSkip(ctx) - } - - start := time.Now() - - defer func() { - if errLevel != 0 && ctx.Settings().CIMode { - rt.Dump(ctx) - } - - if err := rt.Close(); err != nil { - scopes.Framework.Errorf("Error during close: %v", err) - if rt.context.settings.FailOnDeprecation { - if ferrors.IsOrContainsDeprecatedError(err) { - errLevel = 1 - } - } - } - rt = nil - }() - - if err := s.runSetupFns(ctx); err != nil { - scopes.Framework.Errorf("Exiting due to setup failure: %v", err) - return exitCodeSetupError - } - - // Check if one of the setup functions ended up skipping the suite. - if s.isSkipped(ctx) { - return s.doSkip(ctx) - } - - defer func() { - end := time.Now() - scopes.Framework.Infof("=== Suite %q run time: %v ===", ctx.Settings().TestID, end.Sub(start)) - - ctx.RecordTraceEvent("suite-runtime", end.Sub(start).Seconds()) - ctx.RecordTraceEvent("echo-calls", echo.GlobalEchoRequests.Load()) - ctx.RecordTraceEvent("yaml-apply", GlobalYAMLWrites.Load()) - _ = appendToFile(ctx.marshalTraceEvent(), filepath.Join(ctx.Settings().BaseDir, "trace.yaml")) - }() - - attempt := 0 - for attempt <= ctx.settings.Retries { - attempt++ - scopes.Framework.Infof("=== BEGIN: Test Run: '%s' ===", ctx.Settings().TestID) - errLevel = s.mRun(ctx) - if errLevel == 0 { - scopes.Framework.Infof("=== DONE: Test Run: '%s' ===", ctx.Settings().TestID) - break - } - scopes.Framework.Infof("=== FAILED: Test Run: '%s' (exitCode: %v) ===", - ctx.Settings().TestID, errLevel) - if attempt <= ctx.settings.Retries { - scopes.Framework.Warnf("=== RETRY: Test Run: '%s' ===", ctx.Settings().TestID) - } - } - s.writeOutput() - - return -} - -type SuiteOutcome struct { - Name string - Environment string - Multicluster bool - TestOutcomes []TestOutcome -} - -func environmentName(ctx resource.Context) string { - if ctx.Environment() != nil { - return ctx.Environment().EnvironmentName() - } - return "" -} - -func isMulticluster(ctx resource.Context) bool { - if ctx.Environment() != nil { - return ctx.Clusters().IsMulticluster() - } - return false -} - -func clusters(ctx resource.Context) []cluster.Cluster { - if ctx.Environment() != nil { - return ctx.Environment().Clusters() - } - return nil -} - -func (s *suiteImpl) writeOutput() { - // the ARTIFACTS env var is set by prow, and uploaded to GCS as part of the job artifact - artifactsPath, err := file.NormalizePath(os.Getenv("ARTIFACTS")) - if err != nil { - artifactsPath = os.Getenv("ARTIFACTS") - log.Warnf("failed normalizing %s: %v", artifactsPath, err) - } - if artifactsPath != "" { - ctx := rt.suiteContext() - ctx.outcomeMu.RLock() - out := SuiteOutcome{ - Name: ctx.Settings().TestID, - Environment: environmentName(ctx), - Multicluster: isMulticluster(ctx), - TestOutcomes: ctx.testOutcomes, - } - ctx.outcomeMu.RUnlock() - outbytes, err := yaml.Marshal(out) - if err != nil { - log.Errorf("failed writing test suite outcome to yaml: %s", err) - } - err = os.WriteFile(path.Join(artifactsPath, out.Name+".yaml"), outbytes, 0o644) - if err != nil { - log.Errorf("failed writing test suite outcome to file: %s", err) - } - } -} - -func (s *suiteImpl) runSetupFns(ctx SuiteContext) (err error) { - scopes.Framework.Infof("=== BEGIN: Setup: '%s' ===", ctx.Settings().TestID) - - // Run all the require functions first, then the setup functions. - setupFns := append(append([]resource.SetupFn{}, s.requireFns...), s.setupFns...) - - start := time.Now() - for _, fn := range setupFns { - err := s.runSetupFn(fn, ctx) - if err != nil { - scopes.Framework.Errorf("Test setup error: %v", err) - scopes.Framework.Infof("=== FAILED: Setup: '%s' (%v) ===", ctx.Settings().TestID, err) - return err - } - - if s.isSkipped(ctx) { - return nil - } - } - elapsed := time.Since(start) - scopes.Framework.Infof("=== DONE: Setup: '%s' (%v) ===", ctx.Settings().TestID, elapsed) - return nil -} - -func initRuntime(s *suiteImpl) error { - rtMu.Lock() - defer rtMu.Unlock() - - if rt != nil { - return errors.New("framework is already initialized") - } - - settings, err := s.getSettings(s.testID) - if err != nil { - return err - } - - // Get the EnvironmentFactory. - environmentFactory := s.envFactory - if environmentFactory == nil { - environmentFactory = settings.EnvironmentFactory - } - if environmentFactory == nil { - environmentFactory = newEnvironment - } - - if err := configureLogging(); err != nil { - return err - } - - scopes.Framework.Infof("=== Test Framework Settings ===") - scopes.Framework.Info(settings.String()) - scopes.Framework.Infof("===============================") - - // Ensure that the work dir is set. - if err := os.MkdirAll(settings.RunDir(), os.ModePerm); err != nil { - return fmt.Errorf("error creating rundir %q: %v", settings.RunDir(), err) - } - scopes.Framework.Infof("Test run dir: %v", settings.RunDir()) - - rt, err = newRuntime(settings, environmentFactory, s.labels) - return err -} - -func newEnvironment(ctx resource.Context) (resource.Environment, error) { - s, err := kube.NewSettingsFromCommandLine() - if err != nil { - return nil, err - } - return kube.New(ctx, s) -} - -func getSettings(testID string) (*resource.Settings, error) { - // Parse flags and init logging. - if !config.Parsed() { - config.Parse() - } - - return resource.SettingsFromCommandLine(testID) -} - -func mustCompileAll(patterns ...string) []*regexp.Regexp { - out := make([]*regexp.Regexp, 0, len(patterns)) - for _, pattern := range patterns { - out = append(out, regexp.MustCompile(pattern)) - } - - return out -} - -func appendToFile(contents []byte, file string) error { - f, err := os.OpenFile(file, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o600) - if err != nil { - return err - } - - defer func() { - _ = f.Close() - }() - - if _, err = f.Write(contents); err != nil { - return err - } - return nil -} diff --git a/pkg/test/framework/suite_test.go b/pkg/test/framework/suite_test.go deleted file mode 100644 index 195c21657..000000000 --- a/pkg/test/framework/suite_test.go +++ /dev/null @@ -1,530 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package framework - -import ( - "fmt" - "os" - "sync" - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/environment/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/label" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" -) - -func defaultExitFn(_ int) {} - -func settingsFn(s *resource.Settings) func(string) (*resource.Settings, error) { - return func(testID string) (*resource.Settings, error) { - s.TestID = testID - s.BaseDir = os.TempDir() - return s, nil - } -} - -func defaultSettingsFn(testID string) (*resource.Settings, error) { - s := resource.DefaultSettings() - s.TestID = testID - s.BaseDir = os.TempDir() - - return s, nil -} - -func cleanupRT() { - rtMu.Lock() - defer rtMu.Unlock() - rt = nil -} - -// Create a bogus environment for testing. This can be removed when "environments" are removed -func newTestSuite(testID string, fn mRunFn, osExit func(int), getSettingsFn getSettingsFunc) *suiteImpl { - s := newSuite(testID, fn, osExit, getSettingsFn) - s.envFactory = func(ctx resource.Context) (resource.Environment, error) { - return kube.FakeEnvironment{}, nil - } - return s -} - -func TestSuite_Basic(t *testing.T) { - defer cleanupRT() - g := NewWithT(t) - - var runCalled bool - var runSkipped bool - var exitCode int - runFn := func(ctx *suiteContext) int { - runCalled = true - runSkipped = ctx.skipped - return -1 - } - exitFn := func(code int) { - exitCode = code - } - - s := newTestSuite("tid", runFn, exitFn, defaultSettingsFn) - s.Run() - - g.Expect(runCalled).To(BeTrue()) - g.Expect(runSkipped).To(BeFalse()) - g.Expect(exitCode).To(Equal(-1)) -} - -func TestSuite_Label_SuiteFilter(t *testing.T) { - defer cleanupRT() - g := NewWithT(t) - - var runSkipped bool - runFn := func(ctx *suiteContext) int { - runSkipped = ctx.skipped - return 0 - } - - sel, err := label.ParseSelector("-customsetup") - g.Expect(err).To(BeNil()) - settings := resource.DefaultSettings() - settings.Selector = sel - - s := newTestSuite("tid", runFn, defaultExitFn, settingsFn(settings)) - s.Label(label.CustomSetup) - s.Run() - - g.Expect(runSkipped).To(BeTrue()) -} - -func TestSuite_Label_SuiteAllow(t *testing.T) { - defer cleanupRT() - g := NewWithT(t) - - var runCalled bool - var runSkipped bool - runFn := func(ctx *suiteContext) int { - runCalled = true - runSkipped = ctx.skipped - return 0 - } - - sel, err := label.ParseSelector("+postsubmit") - g.Expect(err).To(BeNil()) - settings := resource.DefaultSettings() - settings.Selector = sel - - s := newTestSuite("tid", runFn, defaultExitFn, settingsFn(settings)) - s.Label(label.CustomSetup) - s.Run() - - g.Expect(runCalled).To(BeTrue()) - g.Expect(runSkipped).To(BeFalse()) -} - -func TestSuite_RequireMinMaxClusters(t *testing.T) { - cases := []struct { - name string - min int - max int - actual int - expectSkip bool - }{ - { - name: "min less than zero", - min: 0, - max: 100, - actual: 1, - expectSkip: false, - }, - { - name: "less than min", - min: 2, - max: 100, - actual: 1, - expectSkip: true, - }, - { - name: "equal to min", - min: 1, - max: 100, - actual: 1, - expectSkip: false, - }, - { - name: "greater than min", - min: 1, - max: 100, - actual: 2, - expectSkip: false, - }, - { - name: "max less than zero", - min: 1, - max: 0, - actual: 1, - expectSkip: false, - }, - { - name: "greater than max", - min: 1, - max: 1, - actual: 2, - expectSkip: true, - }, - { - name: "equal to max", - min: 1, - max: 2, - actual: 2, - expectSkip: false, - }, - { - name: "less than max", - min: 1, - max: 2, - actual: 1, - expectSkip: false, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - defer cleanupRT() - g := NewWithT(t) - - var runCalled bool - var runSkipped bool - runFn := func(ctx *suiteContext) int { - runCalled = true - runSkipped = ctx.skipped - return 0 - } - - // Set the kube config flag. - kubeConfigs := make([]string, c.actual) - for i := 0; i < c.actual; i++ { - kubeConfigs[i] = "~/.kube/config" - } - - settings := resource.DefaultSettings() - - s := newTestSuite("tid", runFn, defaultExitFn, settingsFn(settings)) - s.envFactory = newFakeEnvironmentFactory(c.actual) - s.RequireMinClusters(c.min) - s.RequireMaxClusters(c.max) - s.Run() - - g.Expect(runCalled).To(BeTrue()) - if c.expectSkip { - g.Expect(runSkipped).To(BeTrue()) - } else { - g.Expect(runSkipped).To(BeFalse()) - } - }) - } -} - -func TestSuite_Setup(t *testing.T) { - defer cleanupRT() - g := NewWithT(t) - - var runCalled bool - var runSkipped bool - runFn := func(ctx *suiteContext) int { - runCalled = true - runSkipped = ctx.skipped - return 0 - } - - s := newTestSuite("tid", runFn, defaultExitFn, defaultSettingsFn) - - var setupCalled bool - s.Setup(func(c resource.Context) error { - setupCalled = true - return nil - }) - s.Run() - - g.Expect(setupCalled).To(BeTrue()) - g.Expect(runCalled).To(BeTrue()) - g.Expect(runSkipped).To(BeFalse()) -} - -func TestSuite_SetupFail(t *testing.T) { - defer cleanupRT() - g := NewWithT(t) - - var runCalled bool - runFn := func(ctx *suiteContext) int { - runCalled = true - return 0 - } - - s := newTestSuite("tid", runFn, defaultExitFn, defaultSettingsFn) - - var setupCalled bool - s.Setup(func(c resource.Context) error { - setupCalled = true - return fmt.Errorf("can't run this") - }) - s.Run() - - g.Expect(setupCalled).To(BeTrue()) - g.Expect(runCalled).To(BeFalse()) -} - -func TestSuite_SetupFail_Dump(t *testing.T) { - defer cleanupRT() - g := NewWithT(t) - - var runCalled bool - runFn := func(_ *suiteContext) int { - runCalled = true - return 0 - } - - settings := resource.DefaultSettings() - settings.CIMode = true - - s := newTestSuite("tid", runFn, defaultExitFn, settingsFn(settings)) - - var setupCalled bool - s.Setup(func(c resource.Context) error { - setupCalled = true - return fmt.Errorf("can't run this") - }) - s.Run() - - g.Expect(setupCalled).To(BeTrue()) - g.Expect(runCalled).To(BeFalse()) -} - -func TestSuite_Cleanup(t *testing.T) { - t.Run("cleanup", func(t *testing.T) { - defer cleanupRT() - g := NewWithT(t) - - var cleanupCalled bool - var conditionalCleanupCalled bool - var waitForRun1 sync.WaitGroup - waitForRun1.Add(1) - runFn := func(ctx *suiteContext) int { - waitForRun1.Done() - return 0 - } - settings := resource.DefaultSettings() - settings.NoCleanup = false - - s := newTestSuite("tid", runFn, defaultExitFn, settingsFn(settings)) - s.Setup(func(ctx resource.Context) error { - ctx.Cleanup(func() { - cleanupCalled = true - }) - ctx.ConditionalCleanup(func() { - conditionalCleanupCalled = true - }) - return nil - }) - s.Run() - waitForRun1.Wait() - - g.Expect(cleanupCalled).To(BeTrue()) - g.Expect(conditionalCleanupCalled).To(BeTrue()) - }) - t.Run("nocleanup", func(t *testing.T) { - defer cleanupRT() - g := NewWithT(t) - - var cleanupCalled bool - var conditionalCleanupCalled bool - var waitForRun1 sync.WaitGroup - waitForRun1.Add(1) - runFn := func(ctx *suiteContext) int { - waitForRun1.Done() - return 0 - } - settings := resource.DefaultSettings() - settings.NoCleanup = true - - s := newTestSuite("tid", runFn, defaultExitFn, settingsFn(settings)) - s.Setup(func(ctx resource.Context) error { - ctx.Cleanup(func() { - cleanupCalled = true - }) - ctx.ConditionalCleanup(func() { - conditionalCleanupCalled = true - }) - return nil - }) - s.Run() - waitForRun1.Wait() - - g.Expect(cleanupCalled).To(BeTrue()) - g.Expect(conditionalCleanupCalled).To(BeFalse()) - }) -} - -func TestSuite_DoubleInit_Error(t *testing.T) { - defer cleanupRT() - g := NewWithT(t) - - var waitForRun1 sync.WaitGroup - waitForRun1.Add(1) - var waitForTestCompletion sync.WaitGroup - waitForTestCompletion.Add(1) - runFn1 := func(ctx *suiteContext) int { - waitForRun1.Done() - waitForTestCompletion.Wait() - return 0 - } - - runFn2 := func(ctx *suiteContext) int { - return 0 - } - - var waitForExit1Call sync.WaitGroup - waitForExit1Call.Add(1) - var errCode1 int - exitFn1 := func(errCode int) { - errCode1 = errCode - waitForExit1Call.Done() - } - - var exit2Called bool - var errCode2 int - exitFn2 := func(errCode int) { - exit2Called = true - errCode2 = errCode - } - - s := newTestSuite("tid1", runFn1, exitFn1, defaultSettingsFn) - - s2 := newTestSuite("tid2", runFn2, exitFn2, defaultSettingsFn) - - go s.Run() - waitForRun1.Wait() - - s2.Run() - waitForTestCompletion.Done() - waitForExit1Call.Wait() - - g.Expect(exit2Called).To(Equal(true)) - g.Expect(errCode1).To(Equal(0)) - g.Expect(errCode2).NotTo(Equal(0)) -} - -func TestSuite_GetResource(t *testing.T) { - defer cleanupRT() - - act := func(refPtr interface{}, trackedResource resource.Resource) error { - var err error - runFn := func(ctx *suiteContext) int { - err = ctx.GetResource(refPtr) - return 0 - } - s := newTestSuite("tid", runFn, defaultExitFn, defaultSettingsFn) - s.Setup(func(c resource.Context) error { - c.TrackResource(trackedResource) - return nil - }) - s.Run() - return err - } - - t.Run("struct reference", func(t *testing.T) { - g := NewWithT(t) - var ref *resource.FakeResource - tracked := &resource.FakeResource{IDValue: "1"} - // notice that we pass **fakeCluster: - // GetResource requires *T where T implements resource.Resource. - // *fakeCluster implements it but fakeCluster does not. - err := act(&ref, tracked) - g.Expect(err).To(BeNil()) - g.Expect(tracked).To(Equal(ref)) - }) - t.Run("interface reference", func(t *testing.T) { - g := NewWithT(t) - var ref OtherInterface - tracked := &resource.FakeResource{IDValue: "1"} - err := act(&ref, tracked) - g.Expect(err).To(BeNil()) - g.Expect(tracked).To(Equal(ref)) - }) - t.Run("slice reference", func(t *testing.T) { - g := NewWithT(t) - existing := &resource.FakeResource{IDValue: "1"} - tracked := &resource.FakeResource{IDValue: "2"} - ref := []OtherInterface{existing} - err := act(&ref, tracked) - g.Expect(err).To(BeNil()) - g.Expect(ref).To(HaveLen(2)) - g.Expect(existing).To(Equal(ref[0])) - g.Expect(tracked).To(Equal(ref[1])) - }) - t.Run("non pointer ref", func(t *testing.T) { - g := NewWithT(t) - err := act(resource.FakeResource{}, &resource.FakeResource{}) - g.Expect(err).NotTo(BeNil()) - }) -} - -func TestDeriveSuiteName(t *testing.T) { - cases := []struct { - caller string - expected string - }{ - { - caller: "/home/me/go/src/istio.io/istio/some/path/mytest.go", - expected: "some_path", - }, - { - caller: "/home/me/go/src/istio.io/istio.io/some/path/mytest.go", - expected: "some_path", - }, - { - caller: "/home/me/go/src/istio.io/istio/tests/integration/some/path/mytest.go", - expected: "some_path", - }, - { - caller: "/work/some/path/mytest.go", - expected: "some_path", - }, - { - caller: "/work/tests/integration/some/path/mytest.go", - expected: "some_path", - }, - } - - for _, c := range cases { - t.Run(c.caller, func(t *testing.T) { - g := NewWithT(t) - actual := deriveSuiteName(c.caller) - g.Expect(actual).To(Equal(c.expected)) - }) - } -} - -func newFakeEnvironmentFactory(numClusters int) resource.EnvironmentFactory { - e := kube.FakeEnvironment{NumClusters: numClusters} - return func(ctx resource.Context) (resource.Environment, error) { - return e, nil - } -} - -type OtherInterface interface { - GetOtherValue() string -} diff --git a/pkg/test/framework/suitecontext.go b/pkg/test/framework/suitecontext.go deleted file mode 100644 index 71ea11c7b..000000000 --- a/pkg/test/framework/suitecontext.go +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package framework - -import ( - "fmt" - "os" - "path" - "reflect" - "strings" - "sync" -) - -import ( - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/features" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/label" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/yml" - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// suiteContext contains suite-level items used during runtime. -type SuiteContext interface { - resource.Context -} - -var _ SuiteContext = &suiteContext{} - -// suiteContext contains suite-level items used during runtime. -type suiteContext struct { - settings *resource.Settings - environment resource.Environment - - skipped bool - - workDir string - yml.FileWriter - - // context-level resources - globalScope *scope - - contextMu sync.Mutex - contextNames sets.Set - - suiteLabels label.Set - - outcomeMu sync.RWMutex - testOutcomes []TestOutcome - - traces sync.Map -} - -func newSuiteContext(s *resource.Settings, envFn resource.EnvironmentFactory, labels label.Set) (*suiteContext, error) { - scopeID := fmt.Sprintf("[suite(%s)]", s.TestID) - - workDir := path.Join(s.RunDir(), "_suite_context") - if err := os.MkdirAll(workDir, os.ModePerm); err != nil { - return nil, err - } - c := &suiteContext{ - settings: s, - globalScope: newScope(scopeID, nil), - workDir: workDir, - FileWriter: yml.NewFileWriter(workDir), - suiteLabels: labels, - contextNames: sets.New(), - } - - env, err := envFn(c) - if err != nil { - return nil, err - } - c.environment = env - c.globalScope.add(env, &resourceID{id: scopeID}) - - return c, nil -} - -// allocateContextID allocates a unique context id for TestContexts. Useful for creating unique names to help with -// debugging -func (s *suiteContext) allocateContextID(prefix string) string { - s.contextMu.Lock() - defer s.contextMu.Unlock() - - candidate := prefix - discriminator := 0 - for { - if !s.contextNames.Contains(candidate) { - s.contextNames.Insert(candidate) - return candidate - } - - candidate = fmt.Sprintf("%s-%d", prefix, discriminator) - discriminator++ - } -} - -func (s *suiteContext) allocateResourceID(contextID string, r resource.Resource) string { - s.contextMu.Lock() - defer s.contextMu.Unlock() - - t := reflect.TypeOf(r) - candidate := fmt.Sprintf("%s/[%s]", contextID, t.String()) - discriminator := 0 - for { - if !s.contextNames.Contains(candidate) { - s.contextNames.Insert(candidate) - return candidate - } - - candidate = fmt.Sprintf("%s/[%s-%d]", contextID, t.String(), discriminator) - discriminator++ - } -} - -func (s *suiteContext) ConditionalCleanup(fn func()) { - s.globalScope.addCloser(&closer{fn: func() error { - fn() - return nil - }, noskip: true}) -} - -func (s *suiteContext) Cleanup(fn func()) { - s.globalScope.addCloser(&closer{fn: func() error { - fn() - return nil - }}) -} - -// TrackResource adds a new resource to track to the context at this level. -func (s *suiteContext) TrackResource(r resource.Resource) resource.ID { - id := s.allocateResourceID(s.globalScope.id, r) - rid := &resourceID{id: id} - s.globalScope.add(r, rid) - return rid -} - -func (s *suiteContext) GetResource(ref interface{}) error { - return s.globalScope.get(ref) -} - -func (s *suiteContext) Environment() resource.Environment { - return s.environment -} - -func (s *suiteContext) Clusters() cluster.Clusters { - return s.Environment().Clusters() -} - -func (s *suiteContext) AllClusters() cluster.Clusters { - return s.Environment().AllClusters() -} - -// Settings returns the current runtime.Settings. -func (s *suiteContext) Settings() *resource.Settings { - return s.settings -} - -// CreateDirectory creates a new subdirectory within this context. -func (s *suiteContext) CreateDirectory(name string) (string, error) { - dir, err := os.MkdirTemp(s.workDir, name) - if err != nil { - scopes.Framework.Errorf("Error creating temp dir: runID='%s', prefix='%s', workDir='%v', err='%v'", - s.settings.RunID, name, s.workDir, err) - } else { - scopes.Framework.Debugf("Created a temp dir: runID='%s', name='%s'", s.settings.RunID, dir) - } - return dir, err -} - -// CreateTmpDirectory creates a new temporary directory with the given prefix. -func (s *suiteContext) CreateTmpDirectory(prefix string) (string, error) { - if len(prefix) != 0 && !strings.HasSuffix(prefix, "-") { - prefix += "-" - } - - dir, err := os.MkdirTemp(s.workDir, prefix) - if err != nil { - scopes.Framework.Errorf("Error creating temp dir: runID='%s', prefix='%s', workDir='%v', err='%v'", - s.settings.RunID, prefix, s.workDir, err) - } else { - scopes.Framework.Debugf("Created a temp dir: runID='%s', Name='%s'", s.settings.RunID, dir) - } - - return dir, err -} - -func (s *suiteContext) ConfigKube(clusters ...cluster.Cluster) resource.ConfigManager { - return newConfigManager(s, clusters) -} - -func (s *suiteContext) ConfigIstio() resource.ConfigManager { - return newConfigManager(s, s.Clusters().Configs()) -} - -type Outcome string - -const ( - Passed Outcome = "Passed" - Failed Outcome = "Failed" - Skipped Outcome = "Skipped" - NotImplemented Outcome = "NotImplemented" -) - -type TestOutcome struct { - Name string - Type string - Outcome Outcome - FeatureLabels map[features.Feature][]string -} - -func (s *suiteContext) registerOutcome(test *testImpl) { - s.outcomeMu.Lock() - defer s.outcomeMu.Unlock() - o := Passed - if test.notImplemented { - o = NotImplemented - } else if test.goTest.Failed() { - o = Failed - } else if test.goTest.Skipped() { - o = Skipped - } - newOutcome := TestOutcome{ - Name: test.goTest.Name(), - Type: "integration", - Outcome: o, - FeatureLabels: test.featureLabels, - } - s.contextMu.Lock() - defer s.contextMu.Unlock() - s.testOutcomes = append(s.testOutcomes, newOutcome) -} - -func (s *suiteContext) RecordTraceEvent(key string, value interface{}) { - s.traces.Store(key, value) -} - -func (s *suiteContext) marshalTraceEvent() []byte { - kvs := map[string]interface{}{} - s.traces.Range(func(key, value interface{}) bool { - kvs[key.(string)] = value - return true - }) - outer := map[string]interface{}{ - fmt.Sprintf("suite/%s", s.settings.TestID): kvs, - } - d, _ := yaml.Marshal(outer) - return d -} diff --git a/pkg/test/framework/telemetry.go b/pkg/test/framework/telemetry.go deleted file mode 100644 index b936e4e02..000000000 --- a/pkg/test/framework/telemetry.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package framework - -import ( - "flag" - "time" -) - -var ( - // TelemetryRetryDelay is the retry delay used in tests. - TelemetryRetryDelay time.Duration - // TelemetryRetryTimeout is the retry timeout used in tests. - TelemetryRetryTimeout time.Duration - // UseRealStackdriver controls whether to use real stackdriver backend for testing or not. - UseRealStackdriver bool -) - -func init() { - flag.DurationVar(&TelemetryRetryDelay, "istio.test.telemetry.retryDelay", time.Second*2, "Default retry delay used in tests") - flag.DurationVar(&TelemetryRetryTimeout, "istio.test.telemetry.retryTimeout", time.Second*80, "Default retry timeout used in tests") - flag.BoolVar(&UseRealStackdriver, "istio.test.telemetry.useRealStackdriver", false, - "controls whether to use real Stackdriver backend or not for Stackdriver integration test.") -} diff --git a/pkg/test/framework/test.go b/pkg/test/framework/test.go deleted file mode 100644 index 1b3d261fd..000000000 --- a/pkg/test/framework/test.go +++ /dev/null @@ -1,340 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package framework - -import ( - "fmt" - "sync" - "testing" - "time" -) - -import ( - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/features" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/label" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" -) - -type Test interface { - // Label applies the given labels to this test. - Label(labels ...label.Instance) Test - // Label applies the given labels to this test. - Features(feats ...features.Feature) Test - NotImplementedYet(features ...features.Feature) Test - // RequireIstioVersion ensures that all installed versions of Istio are at least the - // required version for the annotated test to pass - RequireIstioVersion(version string) Test - // RequiresMinClusters ensures that the current environment contains at least the expected number of clusters. - // Otherwise it stops test execution and skips the test. - RequiresMinClusters(minClusters int) Test - // RequiresMaxClusters ensures that the current environment contains at most the expected number of clusters. - // Otherwise it stops test execution and skips the test. - RequiresMaxClusters(maxClusters int) Test - // RequiresSingleCluster this a utility that requires the min/max clusters to both = 1. - RequiresSingleCluster() Test - // RequiresLocalControlPlane ensures that clusters are using locally-deployed control planes. - RequiresLocalControlPlane() Test - // RequiresSingleNetwork ensures that clusters are in the same network - RequiresSingleNetwork() Test - // Run the test, supplied as a lambda. - Run(fn func(t TestContext)) - // RunParallel runs this test in parallel with other children of the same parent test/suite. Under the hood, - // this relies on Go's t.Parallel() and will, therefore, have the same behavior. - // - // A parallel test will run in parallel with siblings that share the same parent test. The parent test function - // will exit before the parallel children are executed. It should be noted that if the parent test is prevented - // from exiting (e.g. parent test is waiting for something to occur within the child test), the test will - // deadlock. - // - // Example: - // - // func TestParallel(t *testing.T) { - // framework.NewTest(t). - // Run(func(ctx framework.TestContext) { - // ctx.NewSubTest("T1"). - // Run(func(ctx framework.TestContext) { - // ctx.NewSubTest("T1a"). - // RunParallel(func(ctx framework.TestContext) { - // // Run in parallel with T1b - // }) - // ctx.NewSubTest("T1b"). - // RunParallel(func(ctx framework.TestContext) { - // // Run in parallel with T1a - // }) - // // Exits before T1a and T1b are run. - // }) - // - // ctx.NewSubTest("T2"). - // Run(func(ctx framework.TestContext) { - // ctx.NewSubTest("T2a"). - // RunParallel(func(ctx framework.TestContext) { - // // Run in parallel with T2b - // }) - // ctx.NewSubTest("T2b"). - // RunParallel(func(ctx framework.TestContext) { - // // Run in parallel with T2a - // }) - // // Exits before T2a and T2b are run. - // }) - // }) - // } - // - // In the example above, non-parallel parents T1 and T2 contain parallel children T1a, T1b, T2a, T2b. - // - // Since both T1 and T2 are non-parallel, they are run synchronously: T1 followed by T2. After T1 exits, - // T1a and T1b are run asynchronously with each other. After T1a and T1b complete, T2 is then run in the - // same way: T2 exits, then T2a and T2b are run asynchronously to completion. - RunParallel(fn func(t TestContext)) -} - -// Test allows the test author to specify test-related metadata in a fluent-style, before commencing execution. -type testImpl struct { - // name to be used when creating a Golang test. Only used for subtests. - name string - parent *testImpl - goTest *testing.T - labels []label.Instance - // featureLabels maps features to the scenarios they cover. - featureLabels map[features.Feature][]string - notImplemented bool - s *suiteContext - requiredMinClusters int - requiredMaxClusters int - requireLocalIstiod bool - requireSingleNetwork bool - minIstioVersion string - - ctx *testContext - - // Indicates that at least one child test is being run in parallel. In Go, when - // t.Parallel() is called on a test, execution is halted until the parent test exits. - // Only after that point, are the Parallel children are resumed. Because the parent test - // must exit before the Parallel children do, we have to defer closing the parent's - // testcontext until after the children have completed. - hasParallelChildren bool -} - -// globalCleanupLock defines a global wait group to synchronize cleanup of test suites -var globalParentLock = new(sync.Map) - -// NewTest returns a new test wrapper for running a single test. -func NewTest(t *testing.T) Test { - if analyze() { - return newTestAnalyzer(t) - } - rtMu.Lock() - defer rtMu.Unlock() - - if rt == nil { - panic("call to scope without running the test framework") - } - - runner := &testImpl{ - s: rt.suiteContext(), - goTest: t, - featureLabels: make(map[features.Feature][]string), - } - - return runner -} - -func (t *testImpl) Label(labels ...label.Instance) Test { - t.labels = append(t.labels, labels...) - return t -} - -func (t *testImpl) Features(feats ...features.Feature) Test { - if err := addFeatureLabels(t.featureLabels, feats...); err != nil { - // test runs shouldn't fail - log.Errorf(err) - } - return t -} - -func (t *testImpl) NotImplementedYet(features ...features.Feature) Test { - t.notImplemented = true - t.Features(features...). - Run(func(_ TestContext) { t.goTest.Skip("Test Not Yet Implemented") }) - return t -} - -func (t *testImpl) RequiresMinClusters(minClusters int) Test { - t.requiredMinClusters = minClusters - return t -} - -func (t *testImpl) RequiresMaxClusters(maxClusters int) Test { - t.requiredMaxClusters = maxClusters - return t -} - -func (t *testImpl) RequiresSingleCluster() Test { - return t.RequiresMaxClusters(1).RequiresMinClusters(1) -} - -func (t *testImpl) RequiresLocalControlPlane() Test { - t.requireLocalIstiod = true - return t -} - -func (t *testImpl) RequiresSingleNetwork() Test { - t.requireSingleNetwork = true - return t -} - -func (t *testImpl) RequireIstioVersion(version string) Test { - t.minIstioVersion = version - return t -} - -func (t *testImpl) Run(fn func(ctx TestContext)) { - t.runInternal(fn, false) -} - -func (t *testImpl) RunParallel(fn func(ctx TestContext)) { - t.runInternal(fn, true) -} - -func (t *testImpl) runInternal(fn func(ctx TestContext), parallel bool) { - // Disallow running the same test more than once. - if t.ctx != nil { - testName := t.name - if testName == "" && t.goTest != nil { - testName = t.goTest.Name() - } - panic(fmt.Sprintf("Attempting to run test `%s` more than once", testName)) - } - - if t.s.skipped { - t.goTest.Skip("Skipped because parent Suite was skipped.") - return - } - - if t.parent != nil { - // Create a new subtest under the parent's test. - parentGoTest := t.parent.goTest - parentCtx := t.parent.ctx - parentGoTest.Run(t.name, func(goTest *testing.T) { - t.goTest = goTest - t.doRun(parentCtx.newChildContext(t), fn, parallel) - }) - } else { - // Not a child context. Running with the test provided during construction. - t.doRun(newRootContext(t, t.goTest, t.labels...), fn, parallel) - } -} - -func (t *testImpl) doRun(ctx *testContext, fn func(ctx TestContext), parallel bool) { - if fn == nil { - panic("attempting to run test with nil function") - } - - t.ctx = ctx - - // we check kube for min clusters, these assume we're talking about real multicluster. - // it's possible to have 1 kube cluster then 1 non-kube cluster (vm for example) - if t.requiredMinClusters > 0 && len(t.s.Environment().Clusters().Kube()) < t.requiredMinClusters { - ctx.Done() - t.goTest.Skipf("Skipping %q: number of clusters %d is below required min %d", - t.goTest.Name(), len(t.s.Environment().Clusters()), t.requiredMinClusters) - return - } - - // max clusters doesn't check kube only, the test may be written in a way that doesn't loop over all of Clusters() - if t.requiredMaxClusters > 0 && len(t.s.Environment().Clusters()) > t.requiredMaxClusters { - ctx.Done() - t.goTest.Skipf("Skipping %q: number of clusters %d is above required max %d", - t.goTest.Name(), len(t.s.Environment().Clusters()), t.requiredMaxClusters) - return - } - - if t.requireLocalIstiod { - for _, c := range ctx.Clusters() { - if !c.IsPrimary() { - ctx.Done() - t.goTest.Skipf(fmt.Sprintf("Skipping %q: cluster %s is not using a local control plane", - t.goTest.Name(), c.Name())) - return - } - } - } - - if t.requireSingleNetwork && t.s.Environment().IsMultinetwork() { - ctx.Done() - t.goTest.Skipf(fmt.Sprintf("Skipping %q: only single network allowed", - t.goTest.Name())) - return - } - - if t.minIstioVersion != "" { - if !t.ctx.Settings().Revisions.AtLeast(resource.IstioVersion(t.minIstioVersion)) { - ctx.Done() - t.goTest.Skipf("Skipping %q: running with min Istio version %q, test requires at least %s", - t.goTest.Name(), t.ctx.Settings().Revisions.Minimum(), t.minIstioVersion) - } - } - - start := time.Now() - - scopes.Framework.Infof("=== BEGIN: Test: '%s[%s]' ===", rt.suiteContext().Settings().TestID, t.goTest.Name()) - - // Initial setup if we're running in Parallel. - if parallel { - // Inform the parent, who will need to call ctx.Done asynchronously. - if t.parent != nil { - t.parent.hasParallelChildren = true - } - - // Run the underlying Go test in parallel. This will not return until the parent - // test (if there is one) exits. - t.goTest.Parallel() - } - - defer func() { - doneFn := func() { - message := "passed" - if t.goTest.Failed() { - message = "failed" - } - end := time.Now() - scopes.Framework.Infof("=== DONE (%s): Test: '%s[%s] (%v)' ===", - message, - rt.suiteContext().Settings().TestID, - t.goTest.Name(), - end.Sub(start)) - rt.suiteContext().registerOutcome(t) - ctx.Done() - if t.hasParallelChildren { - globalParentLock.Delete(t) - } - } - if t.hasParallelChildren { - // If a child is running in parallel, it won't continue until this test returns. - // Since ctx.Done() will block until the child test is complete, we run ctx.Done() - // asynchronously. - globalParentLock.Store(t, struct{}{}) - go doneFn() - } else { - doneFn() - } - }() - - fn(ctx) -} diff --git a/pkg/test/framework/testcontext.go b/pkg/test/framework/testcontext.go deleted file mode 100644 index 7b99ef2b1..000000000 --- a/pkg/test/framework/testcontext.go +++ /dev/null @@ -1,422 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package framework - -import ( - "fmt" - "io" - "os" - "path" - "testing" - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/errors" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/label" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/yml" -) - -// TestContext is a test-level context that can be created as part of test executing tests. -type TestContext interface { - resource.Context - test.Failer - - // NewSubTest creates a new sub-test under the current running Test. The lifecycle of a sub-Test is scoped to the - // parent. Calls to Done() will block until all children are also Done(). When Run, sub-Tests will automatically - // create their own Golang *testing.T with the name provided. - // - // If this TestContext was not created by a Test or if that Test is not running, this method will panic. - NewSubTest(name string) Test - NewSubTestf(format string, a ...interface{}) Test - - // WorkDir allocated for this test. - WorkDir() string - - // CreateDirectoryOrFail creates a new sub directory with the given name in the workdir, or fails the test. - CreateDirectoryOrFail(name string) string - - // CreateTmpDirectoryOrFail creates a new temporary directory with the given prefix in the workdir, or fails the test. - CreateTmpDirectoryOrFail(prefix string) string - - // SkipDumping will skip dumping debug logs/configs/etc for this scope only (child scopes are not skipped). - SkipDumping() - - // Done should be called when this context is no longer needed. It triggers the asynchronous cleanup of any - // allocated resources. - Done() - - // Methods for interacting with the underlying *testing.T. - Error(args ...interface{}) - Errorf(format string, args ...interface{}) - Failed() bool - Name() string - Skip(args ...interface{}) - SkipNow() - Skipf(format string, args ...interface{}) - Skipped() bool -} - -var ( - _ TestContext = &testContext{} - _ test.Failer = &testContext{} -) - -// testContext for the currently executing test. -type testContext struct { - yml.FileWriter - - // The id of the test context. useful for debugging the test framework itself. - id string - - // The currently running Test. Non-nil if this context was created by a Test - test *testImpl - - // The underlying Go testing.T for this context. - *testing.T - - // suite-level context - suite *suiteContext - - // resource scope for this context. - scope *scope - - // The workDir for this particular context - workDir string -} - -// Before executing a new context, we should wait for existing contexts to terminate if they are NOT parents of this context. -// This is to workaround termination of functions run with RunParallel. When this is used, child tests will not run until the parent -// has terminated. This means that the parent cannot synchronously cleanup, or it would block its children. However, if we do async cleanup, -// then new tests can unexpectedly start during the cleanup of another. This may lead to odd results, like a test cleanup undoing the setup of a future test. -// To workaround this, we maintain a set of all contexts currently terminating. Before starting the context, we will search this set; -// if any non-parent contexts are found, we will wait. -func waitForParents(test *testImpl) { - iterations := 0 - for { - iterations++ - done := true - globalParentLock.Range(func(key, value interface{}) bool { - k := key.(*testImpl) - current := test - for current != nil { - if current == k { - return true - } - current = current.parent - - } - // We found an item in the list, and we are *not* a child of it. This means another test hierarchy has exclusive access right now - // Wait until they are finished before proceeding - done = false - return true - }) - if done { - return - } - time.Sleep(time.Millisecond * 50) - // Add some logging in case something locks up so we can debug - if iterations%10 == 0 { - globalParentLock.Range(func(key, value interface{}) bool { - scopes.Framework.Warnf("Stuck waiting for parent test suites to terminate... %v is blocking", key.(*testImpl).goTest.Name()) - return true - }) - } - } -} - -func newTestContext(test *testImpl, goTest *testing.T, s *suiteContext, parentScope *scope, labels label.Set) *testContext { - waitForParents(test) - id := s.allocateContextID(goTest.Name()) - - allLabels := s.suiteLabels.Merge(labels) - if !s.settings.Selector.Selects(allLabels) { - goTest.Skipf("Skipping: label mismatch: labels=%v, filter=%v", allLabels, s.settings.Selector) - } - - if s.settings.SkipMatcher.MatchTest(goTest.Name()) { - goTest.Skipf("Skipping: test %v matched -istio.test.skip regex", goTest.Name()) - } - - scopes.Framework.Debugf("Creating New test context") - workDir := path.Join(s.settings.RunDir(), goTest.Name(), "_test_context") - if _, err := os.Stat(path.Join(s.settings.RunDir(), goTest.Name())); !os.IsNotExist(err) { - // Folder already exist. This can happen due to --istio.test.retries. Switch to using "id", which - // is globally unique. We do not due this all the time since it breaks the structure of subtests - // a bit. When we use id we end up with "Parent-0". However, subtests end up with - // "Parent/Child-0", which is not in the same folder. As a compromise, we only append the id in - // the rare case of retry. This ensures we always have all data, and in the common cases the data - // is more readable. - workDir = path.Join(s.settings.RunDir(), id, "_test_context") - } - if err := os.MkdirAll(workDir, os.ModePerm); err != nil { - goTest.Fatalf("Error creating work dir %q: %v", workDir, err) - } - - scopes.Framework.Debugf("Creating new testContext: %q", id) - - if parentScope == nil { - parentScope = s.globalScope - } - - scopeID := fmt.Sprintf("[%s]", id) - return &testContext{ - id: id, - test: test, - T: goTest, - suite: s, - scope: newScope(scopeID, parentScope), - workDir: workDir, - FileWriter: yml.NewFileWriter(workDir), - } -} - -func (c *testContext) Settings() *resource.Settings { - return c.suite.settings -} - -func (c *testContext) TrackResource(r resource.Resource) resource.ID { - id := c.suite.allocateResourceID(c.id, r) - rid := &resourceID{id: id} - c.scope.add(r, rid) - return rid -} - -func (c *testContext) GetResource(ref interface{}) error { - return c.scope.get(ref) -} - -func (c *testContext) WorkDir() string { - return c.workDir -} - -func (c *testContext) Environment() resource.Environment { - return c.suite.environment -} - -func (c *testContext) Clusters() cluster.Clusters { - if c == nil || c.Environment() == nil { - return nil - } - return c.Environment().Clusters() -} - -func (c *testContext) AllClusters() cluster.Clusters { - if c == nil || c.Environment() == nil { - return nil - } - return c.Environment().AllClusters() -} - -func (c *testContext) CreateDirectory(name string) (string, error) { - dir, err := os.MkdirTemp(c.workDir, name) - if err != nil { - scopes.Framework.Errorf("Error creating dir: runID='%v', prefix='%s', workDir='%v', err='%v'", - c.suite.settings.RunID, name, c.workDir, err) - } else { - scopes.Framework.Debugf("Created a dir: runID='%v', name='%s'", c.suite.settings.RunID, dir) - } - return dir, err -} - -func (c *testContext) CreateDirectoryOrFail(name string) string { - tmp, err := c.CreateDirectory(name) - if err != nil { - c.Fatalf("Error creating directory with name %q: %v", name, err) - } - return tmp -} - -func (c *testContext) CreateTmpDirectory(prefix string) (string, error) { - dir, err := os.MkdirTemp(c.workDir, prefix) - if err != nil { - scopes.Framework.Errorf("Error creating temp dir: runID='%v', prefix='%s', workDir='%v', err='%v'", - c.suite.settings.RunID, prefix, c.workDir, err) - } else { - scopes.Framework.Debugf("Created a temp dir: runID='%v', name='%s'", c.suite.settings.RunID, dir) - } - - return dir, err -} - -func (c *testContext) SkipDumping() { - c.scope.skipDumping() -} - -func (c *testContext) ConfigKube(clusters ...cluster.Cluster) resource.ConfigManager { - return newConfigManager(c, clusters) -} - -func (c *testContext) ConfigIstio() resource.ConfigManager { - return newConfigManager(c, c.Clusters().Configs()) -} - -func (c *testContext) CreateTmpDirectoryOrFail(prefix string) string { - tmp, err := c.CreateTmpDirectory(prefix) - if err != nil { - c.Fatalf("Error creating temp directory with prefix %q: %v", prefix, err) - } - return tmp -} - -func (c *testContext) newChildContext(test *testImpl) *testContext { - return newTestContext(test, test.goTest, c.suite, c.scope, label.NewSet(test.labels...)) -} - -func (c *testContext) NewSubTest(name string) Test { - if c.test == nil { - panic(fmt.Sprintf("Attempting to create subtest %s from a TestContext with no associated Test", name)) - } - - if c.test.goTest == nil || c.test.ctx == nil { - panic(fmt.Sprintf("Attempting to create subtest %s before running parent", name)) - } - - return &testImpl{ - name: name, - parent: c.test, - s: c.test.s, - featureLabels: c.test.featureLabels, - } -} - -func (c *testContext) NewSubTestf(format string, a ...interface{}) Test { - return c.NewSubTest(fmt.Sprintf(format, a...)) -} - -func (c *testContext) ConditionalCleanup(fn func()) { - c.scope.addCloser(&closer{fn: func() error { - fn() - return nil - }, noskip: true}) -} - -func (c *testContext) Cleanup(fn func()) { - c.scope.addCloser(&closer{fn: func() error { - fn() - return nil - }}) -} - -func (c *testContext) Done() { - if c.Failed() && c.Settings().CIMode { - scopes.Framework.Debugf("Begin dumping testContext: %q", c.id) - // make sure we dump suite-level resources, but don't dump sibling tests or their children - rt.DumpShallow(c) - c.scope.dump(c, true) - scopes.Framework.Debugf("Completed dumping testContext: %q", c.id) - } - - scopes.Framework.Debugf("Begin cleaning up testContext: %q", c.id) - if err := c.scope.done(c.suite.settings.NoCleanup); err != nil { - c.Logf("error scope cleanup: %v", err) - if c.Settings().FailOnDeprecation { - if errors.IsOrContainsDeprecatedError(err) { - c.Error("Using deprecated Envoy features. Failing due to -istio.test.deprecation_failure flag.") - } - } - } - scopes.Framework.Debugf("Completed cleaning up testContext: %q", c.id) -} - -func (c *testContext) Error(args ...interface{}) { - c.Helper() - c.T.Error(args...) -} - -func (c *testContext) Errorf(format string, args ...interface{}) { - c.Helper() - c.T.Errorf(format, args...) -} - -func (c *testContext) Fail() { - c.Helper() - c.T.Fail() -} - -func (c *testContext) FailNow() { - c.Helper() - c.T.FailNow() -} - -func (c *testContext) Failed() bool { - c.Helper() - return c.T.Failed() -} - -func (c *testContext) Fatal(args ...interface{}) { - c.Helper() - c.T.Fatal(args...) -} - -func (c *testContext) Fatalf(format string, args ...interface{}) { - c.Helper() - c.T.Fatalf(format, args...) -} - -func (c *testContext) Log(args ...interface{}) { - c.Helper() - c.T.Log(args...) -} - -func (c *testContext) Logf(format string, args ...interface{}) { - c.Helper() - c.T.Logf(format, args...) -} - -func (c *testContext) Name() string { - c.Helper() - return c.T.Name() -} - -func (c *testContext) Skip(args ...interface{}) { - c.Helper() - c.T.Skip(args...) -} - -func (c *testContext) SkipNow() { - c.Helper() - c.T.SkipNow() -} - -func (c *testContext) Skipf(format string, args ...interface{}) { - c.Helper() - c.T.Skipf(format, args...) -} - -func (c *testContext) Skipped() bool { - c.Helper() - return c.T.Skipped() -} - -var _ io.Closer = &closer{} - -type closer struct { - fn func() error - noskip bool -} - -func (c *closer) Close() error { - return c.fn() -} - -func (c *testContext) RecordTraceEvent(key string, value interface{}) { - // Currently, only supported at suite level. - panic("TODO: implement tracing in test context") -} diff --git a/pkg/test/framework/tools/featuresgen/cmd/root.go b/pkg/test/framework/tools/featuresgen/cmd/root.go deleted file mode 100644 index de30571a3..000000000 --- a/pkg/test/framework/tools/featuresgen/cmd/root.go +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "fmt" - "os" - "reflect" - "regexp" - "sort" - "strings" -) - -import ( - "github.com/spf13/cobra" - "gopkg.in/yaml.v2" -) - -const ( - Copyright = `// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -` - - GeneratedWarning = ` -//WARNING: THIS IS AN AUTO-GENERATED FILE, DO NOT EDIT. -` - - TypeDefFeature = ` -type Feature string -` - - Package = ` -package features -` - - ConstOpen = ` -const ( -` - - ConstClose = ` -) -` -) - -var ( - input string - output string - - rootCmd = &cobra.Command{ - Use: "featuresgen [OPTIONS]", - Short: "FeaturesGen generates valid test labels from a yaml file", - Run: func(cmd *cobra.Command, args []string) { - createLabelsFile() - }, - } - - alphanumericRegex = regexp.MustCompile(`[^a-zA-Z0-9-]`) - dotsRegex = regexp.MustCompile(`[\.]`) - replaceDashRegex = regexp.MustCompile(`-(.)`) -) - -func Execute() { - if err := rootCmd.Execute(); err != nil { - fmt.Println("Error running featuresgen:") - panic(err) - } -} - -func init() { - rootCmd.Flags().StringVarP(&input, "inputFile", "i", "features.yaml", "the YAML file to use as input") - rootCmd.Flags().StringVarP(&output, "outputFile", "o", "features.gen.go", "output Go file with labels as string consts") -} - -// Parses a map in the yaml file -func readMap(m map[interface{}]interface{}, path []string) []string { - var labels []string - for k, v := range m { - // If we see "values," then the element is a root and we shouldn't put it in our label name - if k == "values" { - labels = append(labels, readVal(v, path)...) - } else { - if len(path) > 0 || k.(string) != "features" { - path = append(path, k.(string)) - } - labels = append(labels, readVal(v, path)...) - if len(path) > 0 { - path = path[:len(path)-1] - } - } - } - return labels -} - -// Parses a slice in the yaml file -func readSlice(slc []interface{}, path []string) []string { - labels := make([]string, 0) - for _, v := range slc { - labels = append(labels, readVal(v, path)...) - } - return labels -} - -// Determines the type of a node in the yaml file and parses it accordingly -func readVal(v interface{}, path []string) []string { - typ := reflect.TypeOf(v).Kind() - if typ == reflect.Int || typ == reflect.String { - path = append(path, v.(string)) - return []string{createConstantString(path)} - } else if typ == reflect.Slice { - return readSlice(v.([]interface{}), path) - } else if typ == reflect.Map { - return readMap(v.(map[interface{}]interface{}), path) - } - panic("Found invalid type in YAML file") -} - -func removeDashAndTitle(s string) string { - // nolint: staticcheck - return strings.Title(s[1:]) -} - -// Writes a label to the constants file -func createConstantString(path []string) string { - name := "" - value := "" - for i := 0; i < len(path); i++ { - namePart := alphanumericRegex.ReplaceAllString(path[i], "") - namePart = replaceDashRegex.ReplaceAllStringFunc(namePart, removeDashAndTitle) - // nolint: staticcheck - namePart = strings.Title(namePart) - name += namePart - name += "_" - - value += dotsRegex.ReplaceAllString(path[i], "") - value += "." - } - name = strings.TrimSuffix(name, "_") - value = strings.TrimSuffix(value, ".") - return fmt.Sprintf("\t%s\tFeature = \"%s\"", name, value) -} - -// Reads the yaml file and generates a string constant for each leaf node -func createLabelsFromYaml() string { - data, err := os.ReadFile(input) - if err != nil { - pwd, _ := os.Getwd() - fmt.Println("Error running featuresgen on file: ", pwd, "/", input) - panic(err) - } - m := make(map[interface{}]interface{}) - - err = yaml.Unmarshal(data, &m) - if err != nil { - pwd, _ := os.Getwd() - fmt.Println("Error running featuresgen on file: ", pwd, "/", input) - panic(err) - } - - constants := readVal(m, make([]string, 0)) - // The parsing of the yaml file doesn't seem to happen in a consistent order. To avoid a different file every time 'make gen' is run, we sort the list. - sort.Strings(constants) - return strings.Join(constants, "\n") -} - -func check(err error) { - if err != nil { - panic(err) - } -} - -// Main function that writes the new generated labels file -func createLabelsFile() { - f, err := os.Create("./" + output) - if err != nil { - fmt.Println("Error running featuresgen:") - panic(err) - } - - defer f.Close() - - _, err = f.WriteString(Copyright) - check(err) - _, err = f.WriteString(GeneratedWarning) - check(err) - _, err = f.WriteString(Package) - check(err) - _, err = f.WriteString(TypeDefFeature) - check(err) - _, err = f.WriteString(ConstOpen) - check(err) - _, err = f.WriteString(createLabelsFromYaml()) - check(err) - _, err = f.WriteString(ConstClose) - check(err) - err = f.Sync() - check(err) -} diff --git a/pkg/test/framework/tools/featuresgen/cmd/root_test.go b/pkg/test/framework/tools/featuresgen/cmd/root_test.go deleted file mode 100644 index 633c1759b..000000000 --- a/pkg/test/framework/tools/featuresgen/cmd/root_test.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "sort" - "strings" - "testing" -) - -import ( - "gopkg.in/yaml.v2" -) - -func TestCreateConstantString(t *testing.T) { - s1 := createConstantString([]string{"lab-el1", "label2"}) - if s1 != "\tLabEl1_Label2\tFeature = \"lab-el1.label2\"" { - t.Errorf("Expected '\tLabEl1_Label2\tFeature = \"lab-el1.label2\"' got '%s'", s1) - } - - s2 := createConstantString([]string{"lab.*)($)#el1", "lab.el2"}) - if s2 != "\tLabel1_Label2\tFeature = \"lab*)($)#el1.label2\"" { - t.Errorf("Expected '\tLabel1_Label2\tFeature = \"lab*)($)#el1.label2\"' got '%s'", s2) - } -} - -var testYaml = ` -features: - values: [hello1, hello2] - key2: - values: [val1, val2] -` - -const expectedResult = ` Hello1 Feature = "hello1" - Hello2 Feature = "hello2" - Key2_Val1 Feature = "key2.val1" - Key2_Val2 Feature = "key2.val2"` - -func TestReadVal(t *testing.T) { - m := make(map[interface{}]interface{}) - - err := yaml.Unmarshal([]byte(testYaml), &m) - if err != nil { - t.Errorf(err.Error()) - } - - s1 := readVal(m, make([]string, 0)) - - sort.Strings(s1) - - if strings.Join(s1, "\n") != expectedResult { - t.Errorf("Expected '%s' got '%s'", expectedResult, strings.Join(s1, "\n")) - } -} diff --git a/pkg/test/framework/tools/featuresgen/main.go b/pkg/test/framework/tools/featuresgen/main.go deleted file mode 100644 index a6ad0e123..000000000 --- a/pkg/test/framework/tools/featuresgen/main.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/tools/featuresgen/cmd" -) - -func main() { - cmd.Execute() -} diff --git a/pkg/test/helm/helm.go b/pkg/test/helm/helm.go deleted file mode 100644 index d47670c8c..000000000 --- a/pkg/test/helm/helm.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package helm - -import ( - "fmt" - "strings" - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" - "github.com/apache/dubbo-go-pixiu/pkg/test/shell" -) - -// Helm allows clients to interact with helm commands in their cluster -type Helm struct { - kubeConfig string -} - -// New returns a new instance of a helm object. -func New(kubeConfig string) *Helm { - return &Helm{ - kubeConfig: kubeConfig, - } -} - -// InstallChartWithValues installs the specified chart with its given name to the given namespace -func (h *Helm) InstallChartWithValues(name, chartPath, namespace string, values []string, timeout time.Duration) error { - command := fmt.Sprintf("helm install %s %s --namespace %s --kubeconfig %s --timeout %s %s", - name, chartPath, namespace, h.kubeConfig, timeout, strings.Join(values, " ")) - _, err := execCommand(command) - return err -} - -// InstallChart installs the specified chart with its given name to the given namespace -func (h *Helm) InstallChart(name, chartPath, namespace, overridesFile string, timeout time.Duration) error { - command := fmt.Sprintf("helm install %s %s --namespace %s -f %s --kubeconfig %s --timeout %s", - name, chartPath, namespace, overridesFile, h.kubeConfig, timeout) - _, err := execCommand(command) - return err -} - -// UpgradeChart upgrades the specified chart with its given name to the given namespace; does not use baseWorkDir -// but the full path passed -func (h *Helm) UpgradeChart(name, chartPath, namespace, overridesFile string, timeout time.Duration, args ...string) error { - command := fmt.Sprintf("helm upgrade %s %s --namespace %s -f %s --kubeconfig %s --timeout %s %v", - name, chartPath, namespace, overridesFile, h.kubeConfig, timeout, strings.Join(args, " ")) - _, err := execCommand(command) - return err -} - -// DeleteChart deletes the specified chart with its given name in the given namespace -func (h *Helm) DeleteChart(name, namespace string) error { - command := fmt.Sprintf("helm delete %s --namespace %s --kubeconfig %s", name, namespace, h.kubeConfig) - _, err := execCommand(command) - return err -} - -// Template runs the template command and applies the generated file with kubectl -func (h *Helm) Template(name, chartPath, namespace, templateFile string, timeout time.Duration, args ...string) (string, error) { - command := fmt.Sprintf("helm template %s %s --namespace %s -s %s --kubeconfig %s --timeout %s %s ", - name, chartPath, namespace, templateFile, h.kubeConfig, timeout, strings.Join(args, " ")) - - return execCommand(command) -} - -func execCommand(cmd string) (string, error) { - scopes.Framework.Infof("Applying helm command: %s", cmd) - - s, err := shell.Execute(true, cmd) - if err != nil { - scopes.Framework.Infof("(FAILED) Executing helm: %s (err: %v): %s", cmd, err, s) - return "", fmt.Errorf("%v: %s", err, s) - } - - return s, nil -} diff --git a/pkg/test/json.go b/pkg/test/json.go deleted file mode 100644 index b2c989bb2..000000000 --- a/pkg/test/json.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "bytes" - "encoding/json" -) - -// JSONEquals compares two json strings. We cannot compare JSON strings from protobuf because of -// design decisions https://github.com/golang/protobuf/issues/1373 Instead, use this function to -// normalize the formatting -func JSONEquals(t Failer, a, b string) { - t.Helper() - ba := bytes.Buffer{} - if err := json.Compact(&ba, []byte(a)); err != nil { - t.Fatal(err) - } - bb := bytes.Buffer{} - if err := json.Compact(&bb, []byte(b)); err != nil { - t.Fatal(err) - } - if ba.String() != bb.String() { - t.Fatalf("got %v, want %v", ba.String(), bb.String()) - } -} diff --git a/pkg/test/kube/dump.go b/pkg/test/kube/dump.go deleted file mode 100644 index 00aa37b30..000000000 --- a/pkg/test/kube/dump.go +++ /dev/null @@ -1,464 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "context" - "encoding/json" - "fmt" - "os" - "path" - "path/filepath" - "strings" - "sync" -) - -import ( - "github.com/hashicorp/go-multierror" - "go.uber.org/atomic" - "istio.io/api/annotation" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/istioctl" - "github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" -) - -const maxCoreDumpedPods = 5 - -var coreDumpedPods = atomic.NewInt32(0) - -// PodDumper will dump information from all the pods into the given workDir. -// If no pods are provided, client will be used to fetch all the pods in a namespace. -type PodDumper func(ctx resource.Context, cluster cluster.Cluster, workDir string, namespace string, pods ...corev1.Pod) - -func podOutputPath(workDir string, cluster cluster.Cluster, pod corev1.Pod, dumpName string) string { - return outputPath(workDir, cluster, pod.Name, dumpName) -} - -// outputPath gives a path in the form of workDir/cluster/_ -func outputPath(workDir string, cluster cluster.Cluster, prefix, suffix string) string { - dir := path.Join(workDir, cluster.StableName()) - if err := os.MkdirAll(dir, os.ModeDir|0o700); err != nil { - scopes.Framework.Warnf("failed creating directory: %s", dir) - } - return path.Join(dir, fmt.Sprintf("%s_%s", prefix, suffix)) -} - -func DumpDeployments(ctx resource.Context, workDir, namespace string) { - errG := multierror.Group{} - for _, cluster := range ctx.AllClusters().Kube() { - deps, err := cluster.AppsV1().Deployments(namespace).List(context.TODO(), metav1.ListOptions{}) - if err != nil { - scopes.Framework.Warnf("Error getting deployments: %v", err) - return - } - for _, deployment := range deps.Items { - deployment := deployment - errG.Go(func() error { - out, err := yaml.Marshal(deployment) - if err != nil { - return err - } - return os.WriteFile(outputPath(workDir, cluster, deployment.Name, "deployment.yaml"), out, os.ModePerm) - }) - } - } - _ = errG.Wait() -} - -func DumpWebhooks(ctx resource.Context, workDir string) { - errG := multierror.Group{} - for _, cluster := range ctx.AllClusters().Kube() { - mwhs, err := cluster.AdmissionregistrationV1().MutatingWebhookConfigurations().List(context.TODO(), metav1.ListOptions{}) - if err != nil { - scopes.Framework.Warnf("Error getting mutating webhook configurations: %v", err) - return - } - for _, mwh := range mwhs.Items { - mwh := mwh - errG.Go(func() error { - out, err := yaml.Marshal(mwh) - if err != nil { - return err - } - return os.WriteFile(outputPath(workDir, cluster, mwh.Name, "mutatingwebhook.yaml"), out, os.ModePerm) - }) - } - vwhs, err := cluster.AdmissionregistrationV1().ValidatingWebhookConfigurations().List(context.TODO(), metav1.ListOptions{}) - if err != nil { - scopes.Framework.Warnf("Error getting validating webhook configurations: %v", err) - return - } - for _, vwh := range vwhs.Items { - vwh := vwh - errG.Go(func() error { - out, err := yaml.Marshal(vwh) - if err != nil { - return err - } - return os.WriteFile(outputPath(workDir, cluster, vwh.Name, "validatingwebhook.yaml"), out, os.ModePerm) - }) - } - } - _ = errG.Wait() -} - -// DumpPods runs each dumper with the selected pods in the given namespace. -// If selectors is empty, all pods in the namespace will be dumpped. -// If no dumpers are provided, their resource state, events, container logs and Envoy information will be dumped. -func DumpPods(ctx resource.Context, workDir, namespace string, selectors []string, dumpers ...PodDumper) { - if len(dumpers) == 0 { - dumpers = []PodDumper{ - DumpPodState, - DumpPodEvents, - DumpPodLogs, - DumpPodProxies, - DumpNdsz, - DumpCoreDumps, - } - } - - wg := sync.WaitGroup{} - for _, cluster := range ctx.AllClusters().Kube() { - pods, err := cluster.PodsForSelector(context.TODO(), namespace, selectors...) - if err != nil { - scopes.Framework.Warnf("Error getting pods list via kubectl: %v", err) - return - } - if len(pods.Items) == 0 { - continue - } - for _, dump := range dumpers { - cluster, dump := cluster, dump - wg.Add(1) - go func() { - dump(ctx, cluster, workDir, namespace, pods.Items...) - wg.Done() - }() - } - } - wg.Wait() -} - -const coredumpDir = "/var/lib/istio" - -func DumpCoreDumps(ctx resource.Context, c cluster.Cluster, workDir string, namespace string, pods ...corev1.Pod) { - if coreDumpedPods.Load() >= maxCoreDumpedPods { - return - } - pods = podsOrFetch(c, pods, namespace) - for _, pod := range pods { - if coreDumpedPods.Load() >= maxCoreDumpedPods { - return - } - wroteDumpsForPod := false - containers := append(pod.Spec.Containers, pod.Spec.InitContainers...) - for _, container := range containers { - if container.Name != "istio-proxy" { - continue - } - restarts := containerRestarts(pod, "istio-proxy") - crashed, _ := containerCrashed(pod, "istio-proxy") - if !crashed || restarts == 0 { - // no need to store this dump - continue - } - - findDumps := fmt.Sprintf("find %s -name core.*", coredumpDir) - stdout, _, err := c.PodExec(pod.Name, pod.Namespace, container.Name, findDumps) - if err != nil { - scopes.Framework.Warnf("Unable to get core dumps for pod: %s/%s: %v", pod.Namespace, pod.Name, err) - continue - } - for _, cd := range strings.Split(stdout, "\n") { - if strings.TrimSpace(cd) == "" { - continue - } - stdout, _, err := c.PodExec(pod.Name, pod.Namespace, container.Name, "cat "+cd) - if err != nil { - scopes.Framework.Warnf("Unable to get core dumps %v for pod: %s/%s: %v", cd, pod.Namespace, pod.Name, err) - continue - } - fname := podOutputPath(workDir, c, pod, filepath.Base(cd)) - if err = os.WriteFile(fname, []byte(stdout), os.ModePerm); err != nil { - scopes.Framework.Warnf("Unable to write envoy core dump log for pod: %s/%s: %v", pod.Namespace, pod.Name, err) - } else { - wroteDumpsForPod = true - } - } - } - if wroteDumpsForPod { - coreDumpedPods.Inc() - } - } -} - -func podsOrFetch(a cluster.Cluster, pods []corev1.Pod, namespace string) []corev1.Pod { - if len(pods) == 0 { - podList, err := a.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{}) - if err != nil { - scopes.Framework.Warnf("Error getting pods list via kubectl: %v", err) - return nil - } - pods = podList.Items - } - return pods -} - -// DumpPodState dumps the pod state for either the provided pods or all pods in the namespace if none are provided. -func DumpPodState(_ resource.Context, c cluster.Cluster, workDir string, namespace string, pods ...corev1.Pod) { - pods = podsOrFetch(c, pods, namespace) - - for _, pod := range pods { - out, err := yaml.Marshal(&pod) - if err != nil { - scopes.Framework.Warnf("Error marshaling pod state for output: %v", err) - continue - } - - outPath := podOutputPath(workDir, c, pod, "pod-state.yaml") - if err := os.WriteFile(outPath, out, os.ModePerm); err != nil { - scopes.Framework.Infof("Error writing out pod state to file: %v", err) - } - } -} - -// DumpPodEvents dumps the pod events for either the provided pods or all pods in the namespace if none are provided. -func DumpPodEvents(_ resource.Context, c cluster.Cluster, workDir, namespace string, pods ...corev1.Pod) { - pods = podsOrFetch(c, pods, namespace) - - for _, pod := range pods { - list, err := c.CoreV1().Events(namespace).List(context.TODO(), - metav1.ListOptions{ - FieldSelector: "involvedObject.name=" + pod.Name, - }) - if err != nil { - scopes.Framework.Warnf("Error getting events list for pod %s/%s via kubectl: %v", namespace, pod.Name, err) - return - } - - out, err := yaml.Marshal(list.Items) - if err != nil { - scopes.Framework.Warnf("Error marshaling pod event for output: %v", err) - continue - } - - outPath := podOutputPath(workDir, c, pod, "pod-events.yaml") - if err := os.WriteFile(outPath, out, os.ModePerm); err != nil { - scopes.Framework.Infof("Error writing out pod events to file: %v", err) - } - } -} - -// containerRestarts checks how many times container has ever restarted -func containerRestarts(pod corev1.Pod, container string) int { - for _, cs := range pod.Status.ContainerStatuses { - if cs.Name == container { - return int(cs.RestartCount) - } - } - // No match - assume that means no restart - return 0 -} - -func containerCrashed(pod corev1.Pod, container string) (bool, *corev1.ContainerStateTerminated) { - for _, cs := range pod.Status.ContainerStatuses { - if cs.Name == container && cs.State.Terminated != nil && cs.State.Terminated.ExitCode != 0 { - return true, cs.State.Terminated - } - } - return false, nil -} - -// DumpPodLogs will dump logs from each container in each of the provided pods -// or all pods in the namespace if none are provided. -func DumpPodLogs(_ resource.Context, c cluster.Cluster, workDir, namespace string, pods ...corev1.Pod) { - pods = podsOrFetch(c, pods, namespace) - - for _, pod := range pods { - isVM := checkIfVM(pod) - containers := append(pod.Spec.Containers, pod.Spec.InitContainers...) - for _, container := range containers { - l, err := c.PodLogs(context.TODO(), pod.Name, pod.Namespace, container.Name, false /* previousLog */) - if err != nil { - scopes.Framework.Warnf("Unable to get logs for pod/container: %s/%s/%s for: %v", pod.Namespace, pod.Name, container.Name, err) - } - - fname := podOutputPath(workDir, c, pod, fmt.Sprintf("%s.log", container.Name)) - if err = os.WriteFile(fname, []byte(l), os.ModePerm); err != nil { - scopes.Framework.Warnf("Unable to write logs for pod/container: %s/%s/%s", pod.Namespace, pod.Name, container.Name) - } - - // Get previous container logs, if applicable - if restarts := containerRestarts(pod, container.Name); restarts > 0 { - // only care about istio components restart - if container.Name == "istio-proxy" || container.Name == "discovery" || container.Name == "istio-init" || - container.Name == "istio-validation" || strings.HasPrefix(pod.Name, "istio-cni-node") { - // This is only called if the test failed, so we cannot mark it as "failed" again. Instead, output - // a log which will get highlighted in the test logs - // TODO proper analysis of restarts to ensure we do not miss crashes when tests still pass. - scopes.Framework.Errorf("FAIL: pod %v/%v container %v restarted %d times", pod.Name, pod.Namespace, container.Name, restarts) - } - l, err := c.PodLogs(context.TODO(), pod.Name, pod.Namespace, container.Name, true /* previousLog */) - if err != nil { - scopes.Framework.Warnf("Unable to get previous logs for pod/container: %s/%s/%s", pod.Namespace, pod.Name, container.Name) - } - - fname := podOutputPath(workDir, c, pod, fmt.Sprintf("%s.previous.log", container.Name)) - if err = os.WriteFile(fname, []byte(l), os.ModePerm); err != nil { - scopes.Framework.Warnf("Unable to write previous logs for pod/container: %s/%s/%s", pod.Namespace, pod.Name, container.Name) - } - } - - if crashed, terminateState := containerCrashed(pod, container.Name); crashed { - scopes.Framework.Errorf("FAIL: pod %v/%v crashed with status: %+v", pod.Name, container.Name, terminateState) - } - - // Get envoy logs if the pod is a VM, since kubectl logs only shows the logs from iptables for VMs - if isVM && container.Name == "istio-proxy" { - if stdout, stderr, err := c.PodExec(pod.Name, pod.Namespace, container.Name, "cat /var/log/istio/istio.err.log"); err == nil { - fname := podOutputPath(workDir, c, pod, fmt.Sprintf("%s.envoy.err.log", container.Name)) - if err = os.WriteFile(fname, []byte(stdout+stderr), os.ModePerm); err != nil { - scopes.Framework.Warnf("Unable to write envoy err log for pod/container: %s/%s/%s", pod.Namespace, pod.Name, container.Name) - } - if strings.Contains(stdout, "envoy backtrace") { - scopes.Framework.Errorf("FAIL: VM %v/%v crashed", pod.Name, container.Name) - } - } else { - scopes.Framework.Warnf("Unable to get envoy err log for pod: %s/%s", pod.Namespace, pod.Name) - } - - if stdout, stderr, err := c.PodExec(pod.Name, pod.Namespace, container.Name, "cat /var/log/istio/istio.log"); err == nil { - fname := podOutputPath(workDir, c, pod, fmt.Sprintf("%s.envoy.log", container.Name)) - if err = os.WriteFile(fname, []byte(stdout+stderr), os.ModePerm); err != nil { - scopes.Framework.Warnf("Unable to write envoy log for pod/container: %s/%s/%s", pod.Namespace, pod.Name, container.Name) - } - } else { - scopes.Framework.Warnf("Unable to get envoy log for pod: %s/%s", pod.Namespace, pod.Name) - } - } - } - } -} - -// DumpPodProxies will dump Envoy proxy config and clusters in each of the provided pods -// or all pods in the namespace if none are provided. -func DumpPodProxies(_ resource.Context, c cluster.Cluster, workDir, namespace string, pods ...corev1.Pod) { - pods = podsOrFetch(c, pods, namespace) - for _, pod := range pods { - if !hasEnvoy(pod) { - continue - } - dumpProxyCommand(c, pod, workDir, "proxy-config.json", "pilot-agent request GET config_dump?include_eds=true") - dumpProxyCommand(c, pod, workDir, "proxy-clusters.txt", "pilot-agent request GET clusters") - dumpProxyCommand(c, pod, workDir, "proxy-stats.txt", "pilot-agent request GET stats/prometheus") - } -} - -func dumpProxyCommand(c cluster.Cluster, pod corev1.Pod, workDir, filename, command string) { - isVM := checkIfVM(pod) - containers := append(pod.Spec.Containers, pod.Spec.InitContainers...) - for _, container := range containers { - if container.Name != "istio-proxy" && !isVM { - // if we don't have istio-proxy container, and we're not running as a VM, agent isn't running - continue - } - - if cfgDump, _, err := c.PodExec(pod.Name, pod.Namespace, container.Name, command); err == nil { - fname := podOutputPath(workDir, c, pod, filename) - if err = os.WriteFile(fname, []byte(cfgDump), os.ModePerm); err != nil { - scopes.Framework.Errorf("Unable to write output for command %q on pod/container: %s/%s/%s", command, pod.Namespace, pod.Name, container.Name) - } - } else { - scopes.Framework.Errorf("Unable to get execute command %q on pod: %s/%s for: %v", command, pod.Namespace, pod.Name, err) - } - } -} - -func hasEnvoy(pod corev1.Pod) bool { - if checkIfVM(pod) { - // assume VMs run Envoy - return true - } - f := false - for _, c := range pod.Spec.Containers { - if c.Name == "istio-proxy" { - f = true - break - } - } - if !f { - // no proxy container - return false - } - for k, v := range pod.ObjectMeta.Annotations { - if k == annotation.InjectTemplates.Name && strings.HasPrefix(v, "grpc-") { - // proxy container may run only agent for proxyless gRPC - return false - } - } - return true -} - -func checkIfVM(pod corev1.Pod) bool { - for k := range pod.ObjectMeta.Labels { - if strings.Contains(k, "test-vm") { - return true - } - } - return false -} - -func DumpDebug(ctx resource.Context, c cluster.Cluster, workDir string, endpoint string) { - ik, err := istioctl.New(ctx, istioctl.Config{Cluster: c}) - if err != nil { - scopes.Framework.Warnf("failed dumping %q: %v", endpoint, err) - return - } - args := []string{"x", "internal-debug", "--all", endpoint} - if ctx.Settings().Revisions.Default() != "" { - args = append(args, "--revision", ctx.Settings().Revisions.Default()) - } - scopes.Framework.Debugf("dump %v: %v", endpoint, args) - stdout, _, err := ik.Invoke(args) - if err != nil { - scopes.Framework.Warnf("failed dumping %q: %v", endpoint, err) - return - } - outputs := map[string]string{} - if err := json.Unmarshal([]byte(stdout), &outputs); err != nil { - scopes.Framework.Warnf("failed dumping %q: %v", endpoint, err) - return - } - for istiod, out := range outputs { - outPath := outputPath(workDir, c, istiod, endpoint) - if err := os.WriteFile(outPath, []byte(out), 0o644); err != nil { - scopes.Framework.Warnf("failed dumping %q: %v", endpoint, err) - return - } - } -} - -func DumpNdsz(_ resource.Context, c cluster.Cluster, workDir string, _ string, pods ...corev1.Pod) { - for _, pod := range pods { - dumpProxyCommand(c, pod, workDir, "ndsz.json", "pilot-agent request --debug-port 15020 GET /debug/ndsz") - } -} diff --git a/pkg/test/kube/util.go b/pkg/test/kube/util.go deleted file mode 100644 index a3ef98204..000000000 --- a/pkg/test/kube/util.go +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package kube - -import ( - "context" - "fmt" - "sort" - "time" -) - -import ( - "github.com/hashicorp/go-multierror" - kubeApiCore "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - kubeApiMeta "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" -) - -import ( - istioKube "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/scopes" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -var ( - defaultRetryTimeout = retry.Timeout(time.Minute * 10) - defaultRetryDelay = retry.BackoffDelay(time.Millisecond * 200) -) - -// PodFetchFunc fetches pods from a k8s Client. -type PodFetchFunc func() ([]kubeApiCore.Pod, error) - -// NewPodFetch creates a new PodFetchFunction that fetches all pods matching the namespace and label selectors. -func NewPodFetch(a istioKube.ExtendedClient, namespace string, selectors ...string) PodFetchFunc { - return func() ([]kubeApiCore.Pod, error) { - pods, err := a.PodsForSelector(context.TODO(), namespace, selectors...) - if err != nil { - return nil, err - } - return pods.Items, nil - } -} - -// NewSinglePodFetch creates a new PodFetchFunction that fetches a single pod matching the given label selectors. -func NewSinglePodFetch(a istioKube.ExtendedClient, namespace string, selectors ...string) PodFetchFunc { - return func() ([]kubeApiCore.Pod, error) { - pods, err := a.PodsForSelector(context.TODO(), namespace, selectors...) - if err != nil { - return nil, err - } - - if len(pods.Items) == 0 { - return nil, fmt.Errorf("no matching pod found for selectors: %v", selectors) - } - - if len(pods.Items) > 1 { - scopes.Framework.Warnf("More than one pod found matching selectors: %v", selectors) - } - - return []kubeApiCore.Pod{pods.Items[0]}, nil - } -} - -// NewPodMustFetch creates a new PodFetchFunction that fetches all pods matching the namespace and label selectors. -// If no pods are found, an error is returned -func NewPodMustFetch(a istioKube.ExtendedClient, namespace string, selectors ...string) PodFetchFunc { - return func() ([]kubeApiCore.Pod, error) { - pods, err := a.PodsForSelector(context.TODO(), namespace, selectors...) - if err != nil { - return nil, err - } - if len(pods.Items) == 0 { - return nil, fmt.Errorf("no pods found for %v", selectors) - } - return pods.Items, nil - } -} - -// CheckPodsAreReady checks whether the pods that are selected by the given function is in ready state or not. -func CheckPodsAreReady(fetchFunc PodFetchFunc) ([]kubeApiCore.Pod, error) { - scopes.Framework.Infof("Checking pods ready...") - - fetched, err := fetchFunc() - if err != nil { - scopes.Framework.Infof("Failed retrieving pods: %v", err) - return nil, err - } - - if len(fetched) == 0 { - scopes.Framework.Infof("No pods found...") - return nil, fmt.Errorf("no pods fetched") - } - - for i, p := range fetched { - msg := "Ready" - if e := istioKube.CheckPodReadyOrComplete(&p); e != nil { - msg = e.Error() - err = multierror.Append(err, fmt.Errorf("%s/%s: %s", p.Namespace, p.Name, msg)) - } - scopes.Framework.Infof(" [%2d] %45s %15s (%v)", i, p.Name, p.Status.Phase, msg) - } - - if err != nil { - return nil, err - } - - return fetched, nil -} - -// DeleteOptionsForeground creates new delete options that will block until the operation completes. -func DeleteOptionsForeground() kubeApiMeta.DeleteOptions { - propagationPolicy := kubeApiMeta.DeletePropagationForeground - gracePeriod := int64(0) - return kubeApiMeta.DeleteOptions{ - PropagationPolicy: &propagationPolicy, - GracePeriodSeconds: &gracePeriod, - } -} - -// WaitUntilPodsAreReady waits until the pod with the name/namespace is in ready state. -func WaitUntilPodsAreReady(fetchFunc PodFetchFunc, opts ...retry.Option) ([]kubeApiCore.Pod, error) { - var pods []kubeApiCore.Pod - err := retry.UntilSuccess(func() error { - scopes.Framework.Infof("Checking pods ready...") - - fetched, err := CheckPodsAreReady(fetchFunc) - if err != nil { - return err - } - pods = fetched - return nil - }, newRetryOptions(opts...)...) - - return pods, err -} - -// WaitUntilServiceEndpointsAreReady will wait until the service with the given name/namespace is present, and have at least -// one usable endpoint. -func WaitUntilServiceEndpointsAreReady(a kubernetes.Interface, ns string, name string, - opts ...retry.Option) (*kubeApiCore.Service, *kubeApiCore.Endpoints, error) { - var service *kubeApiCore.Service - var endpoints *kubeApiCore.Endpoints - err := retry.UntilSuccess(func() error { - s, err := a.CoreV1().Services(ns).Get(context.TODO(), name, kubeApiMeta.GetOptions{}) - if err != nil { - return err - } - - eps, err := a.CoreV1().Endpoints(ns).Get(context.TODO(), name, kubeApiMeta.GetOptions{}) - if err != nil { - return err - } - if len(eps.Subsets) == 0 { - return fmt.Errorf("%s/%v endpoint not ready: no subsets", ns, name) - } - - for _, subset := range eps.Subsets { - if len(subset.Addresses) > 0 && len(subset.NotReadyAddresses) == 0 { - service = s - endpoints = eps - return nil - } - } - return fmt.Errorf("%s/%v endpoint not ready: no ready addresses", ns, name) - }, newRetryOptions(opts...)...) - if err != nil { - return nil, nil, err - } - - return service, endpoints, nil -} - -// WaitForSecretToExist waits for the given secret up to the given waitTime. -func WaitForSecretToExist(a kubernetes.Interface, namespace, name string, waitTime time.Duration) (*kubeApiCore.Secret, error) { - secret := a.CoreV1().Secrets(namespace) - - watch, err := secret.Watch(context.TODO(), kubeApiMeta.ListOptions{}) - if err != nil { - return nil, fmt.Errorf("failed to set up watch for secret (error: %v)", err) - } - events := watch.ResultChan() - - startTime := time.Now() - for { - select { - case event := <-events: - secret := event.Object.(*kubeApiCore.Secret) - if secret.GetName() == name { - return secret, nil - } - case <-time.After(waitTime - time.Since(startTime)): - return nil, fmt.Errorf("secret %v did not become existent within %v", - name, waitTime) - } - } -} - -// WaitForSecretToExistOrFail calls WaitForSecretToExist and fails the given test.Failer if an error occurs. -func WaitForSecretToExistOrFail(t test.Failer, a kubernetes.Interface, namespace, name string, - waitTime time.Duration) *kubeApiCore.Secret { - t.Helper() - s, err := WaitForSecretToExist(a, namespace, name, waitTime) - if err != nil { - t.Fatal(err) - } - return s -} - -// WaitForNamespaceDeletion waits until a namespace is deleted. -func WaitForNamespaceDeletion(a kubernetes.Interface, ns string, opts ...retry.Option) error { - return retry.UntilSuccess(func() error { - _, err := a.CoreV1().Namespaces().Get(context.TODO(), ns, kubeApiMeta.GetOptions{}) - if err == nil { - return fmt.Errorf("namespace %v still exists", ns) - } - - if errors.IsNotFound(err) { - return nil - } - - return err - }, newRetryOptions(opts...)...) -} - -// NamespaceExists returns true if the given namespace exists. -func NamespaceExists(a kubernetes.Interface, ns string) bool { - allNs, err := a.CoreV1().Namespaces().List(context.TODO(), kubeApiMeta.ListOptions{}) - if err != nil { - return false - } - for _, n := range allNs.Items { - if n.Name == ns { - return true - } - } - return false -} - -func newRetryOptions(opts ...retry.Option) []retry.Option { - out := make([]retry.Option, 0, 2+len(opts)) - out = append(out, defaultRetryTimeout, defaultRetryDelay) - out = append(out, opts...) - return out -} - -// MutatingWebhookConfigurationsExists returns true if all the given mutating webhook configs exist. -func MutatingWebhookConfigurationsExists(a kubernetes.Interface, names []string) bool { - cfgs, err := a.AdmissionregistrationV1().MutatingWebhookConfigurations().List(context.TODO(), kubeApiMeta.ListOptions{}) - if err != nil { - return false - } - - if len(cfgs.Items) != len(names) { - return false - } - - sort.Strings(names) - for _, cfg := range cfgs.Items { - if idx := sort.SearchStrings(names, cfg.Name); idx == len(names) { - return false - } - } - - return true -} - -// ValidatingWebhookConfigurationsExists returns true if all the given validating webhook configs exist. -func ValidatingWebhookConfigurationsExists(a kubernetes.Interface, names []string) bool { - cfgs, err := a.AdmissionregistrationV1().ValidatingWebhookConfigurations().List(context.TODO(), kubeApiMeta.ListOptions{}) - if err != nil { - return false - } - - if len(cfgs.Items) != len(names) { - return false - } - - sort.Strings(names) - for _, cfg := range cfgs.Items { - if idx := sort.SearchStrings(names, cfg.Name); idx == len(names) { - return false - } - } - - return true -} diff --git a/pkg/test/loadbalancersim/lb_test.go b/pkg/test/loadbalancersim/lb_test.go deleted file mode 100644 index ff22663be..000000000 --- a/pkg/test/loadbalancersim/lb_test.go +++ /dev/null @@ -1,392 +0,0 @@ -//go:build lbsim -// +build lbsim - -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package loadbalancersim - -import ( - "fmt" - "os" - "sort" - "strings" - "sync" - "testing" - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/loadbalancer" - "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/locality" - "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/mesh" - "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/network" - "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/timeseries" -) - -func TestLoadBalancing(t *testing.T) { - serviceTime := 20 * time.Millisecond - numClients := 1 - clientRPS := 1500 - clientRequests := 1500 - activeRequestBias := 1.0 - sameZone := locality.Parse("us-east/ny") - sameRegion := locality.Parse("us-east/boston") - otherRegion := locality.Parse("asia-east/hongkong") - priorityWeights := map[uint32]uint32{ - 0: 30, - 1: 20, - 2: 1, - } - networkLatencies := map[mesh.RouteKey]time.Duration{ - mesh.RouteKey{ - Src: sameZone, - Dest: sameZone, - }: 1 * time.Millisecond, - mesh.RouteKey{ - Src: sameZone, - Dest: sameRegion, - }: 10 * time.Millisecond, - mesh.RouteKey{ - Src: sameZone, - Dest: otherRegion, - }: 100 * time.Millisecond, - } - - networkLatencyCases := []struct { - enable bool - latencies map[mesh.RouteKey]time.Duration - }{ - { - enable: false, - latencies: make(map[mesh.RouteKey]time.Duration), - }, - { - enable: true, - latencies: networkLatencies, - }, - } - - weightCases := []struct { - enableWeighting bool - newWeightedConnection loadbalancer.WeightedConnectionFactory - }{ - { - enableWeighting: false, - newWeightedConnection: loadbalancer.EquallyWeightedConnectionFactory(), - }, - { - enableWeighting: true, - newWeightedConnection: loadbalancer.PriorityWeightedConnectionFactory(loadbalancer.LocalityPrioritySelector, priorityWeights), - }, - } - - algorithmCases := []struct { - name string - newLB func(conns []*loadbalancer.WeightedConnection) network.Connection - }{ - { - name: "round robin", - newLB: loadbalancer.NewRoundRobin, - }, - { - name: "least request", - newLB: func(conns []*loadbalancer.WeightedConnection) network.Connection { - return loadbalancer.NewLeastRequest(loadbalancer.LeastRequestSettings{ - Connections: conns, - ActiveRequestBias: activeRequestBias, - }) - }, - }, - } - - topologyCases := []struct { - name string - countSameZone int - countSameRegion int - countOtherRegion int - }{ - { - name: "all local", - countSameZone: 6, - countSameRegion: 0, - countOtherRegion: 0, - }, - { - name: "even", - countSameZone: 2, - countSameRegion: 2, - countOtherRegion: 2, - }, - { - name: "one remote", - countSameZone: 4, - countSameRegion: 1, - countOtherRegion: 1, - }, - { - name: "one local", - countSameZone: 1, - countSameRegion: 3, - countOtherRegion: 3, - }, - } - - var sm suiteMetrics - for _, enableQueueLatency := range []bool{false, true} { - t.Run("queue latency "+toggleStr(enableQueueLatency), func(t *testing.T) { - for _, networkLatencyCase := range networkLatencyCases { - t.Run("network latency "+toggleStr(networkLatencyCase.enable), func(t *testing.T) { - for _, weightCase := range weightCases { - weightCase := weightCase - t.Run("weighting "+toggleStr(weightCase.enableWeighting), func(t *testing.T) { - for _, algorithmCase := range algorithmCases { - algorithmCase := algorithmCase - t.Run(algorithmCase.name, func(t *testing.T) { - for _, topologyCase := range topologyCases { - topologyCase := topologyCase - t.Run(topologyCase.name, func(t *testing.T) { - m := mesh.New(mesh.Settings{ - NetworkLatencies: networkLatencyCase.latencies, - }) - defer m.ShutDown() - - // Create the new test output. - tm := &testMetrics{ - hasNetworkLatency: networkLatencyCase.enable, - hasQueueLatency: enableQueueLatency, - weighted: weightCase.enableWeighting, - algorithm: algorithmCase.name, - topology: topologyCase.name, - } - sm = append(sm, tm) - - // Create the clients. - for i := 0; i < numClients; i++ { - _ = m.NewClient(mesh.ClientSettings{ - RPS: clientRPS, - Locality: sameZone, - }) - } - - // Allocate the nodes in the configured topology. - m.NewNodes(topologyCase.countSameZone, serviceTime, enableQueueLatency, sameZone) - m.NewNodes(topologyCase.countSameRegion, serviceTime, enableQueueLatency, sameRegion) - m.NewNodes(topologyCase.countOtherRegion, serviceTime, enableQueueLatency, otherRegion) - - runTest(t, testSettings{ - mesh: m, - clientRequests: clientRequests, - activeRequestBias: activeRequestBias, - newWeightedConnection: weightCase.newWeightedConnection, - newLB: algorithmCase.newLB, - }, tm) - }) - } - }) - } - }) - } - }) - } - }) - } - - outputFile := os.Getenv("LB_SIM_OUTPUT_FILE") - if len(outputFile) == 0 { - homeDir, err := os.UserHomeDir() - if err != nil { - t.Fatal(err) - } - outputFile = fmt.Sprintf("%s/lb_output.csv", homeDir) - } - - err := os.WriteFile(outputFile, []byte(sm.toCSV()), 0644) - if err != nil { - t.Fatal(err) - } -} - -func toggleStrUpper(on bool) string { - return strings.ToUpper(toggleStr(on)) -} - -func toggleStr(on bool) string { - if on { - return "on" - } - return "off" -} - -type testSettings struct { - mesh *mesh.Instance - clientRequests int - newLB func(conns []*loadbalancer.WeightedConnection) network.Connection - newWeightedConnection loadbalancer.WeightedConnectionFactory - activeRequestBias float64 -} - -type testMetrics struct { - hasQueueLatency bool - hasNetworkLatency bool - weighted bool - algorithm string - topology string - qLatencyMin float64 - qLatencyAvg float64 - qLatencyMax float64 - latencyMin float64 - latencyAvg float64 - latencyMax float64 - nodesSameZone int - nodesSameRegion int - nodesOtherRegion int - requestsSameZone uint64 - requestsSameRegion uint64 - requestsOtherRegion uint64 -} - -func (tm testMetrics) totalRequests() uint64 { - return tm.requestsSameZone + tm.requestsSameRegion + tm.requestsOtherRegion -} - -func (tm testMetrics) sameZonePercent() float64 { - return (float64(tm.requestsSameZone) / float64(tm.totalRequests())) * 100 -} - -func (tm testMetrics) sameRegionPercent() float64 { - return (float64(tm.requestsSameRegion) / float64(tm.totalRequests())) * 100 -} - -func (tm testMetrics) otherRegionPercent() float64 { - return (float64(tm.requestsOtherRegion) / float64(tm.totalRequests())) * 100 -} - -func (tm testMetrics) String() string { - out := "" - out += fmt.Sprintf(" Requests: %d\n", tm.totalRequests()) - out += fmt.Sprintf(" Topology: Same Zone=%d, Same Region=%d, Other Region=%d\n", tm.nodesSameZone, tm.nodesSameRegion, tm.nodesOtherRegion) - out += fmt.Sprintf("Latency (min): %8.3fs\n", tm.latencyMin) - out += fmt.Sprintf("Latency (avg): %8.3fs\n", tm.latencyAvg) - out += fmt.Sprintf("Latency (max): %8.3fs\n", tm.latencyMax) - out += fmt.Sprintf("QLatency (min): %8.3fs\n", tm.qLatencyMin) - out += fmt.Sprintf("QLatency (avg): %8.3fs\n", tm.qLatencyAvg) - out += fmt.Sprintf("QLatency (max): %8.3fs\n", tm.qLatencyMax) - out += fmt.Sprintf(" Same Zone: %8.3f%%\n", tm.sameZonePercent()) - out += fmt.Sprintf(" Same Region: %8.3f%%\n", tm.sameRegionPercent()) - out += fmt.Sprintf(" Other Region: %8.3f%%\n", tm.otherRegionPercent()) - return out -} - -func (tm testMetrics) toCSV() string { - return fmt.Sprintf("%s,%s,%s,%s,%s,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f", tm.topology, - toggleStrUpper(tm.weighted), toggleStrUpper(tm.hasNetworkLatency), toggleStrUpper(tm.hasQueueLatency), - tm.algorithm, tm.latencyMin, tm.latencyAvg, tm.latencyMax, tm.qLatencyMin, tm.qLatencyAvg, tm.qLatencyMax, - tm.sameZonePercent(), tm.sameRegionPercent(), tm.otherRegionPercent()) -} - -type suiteMetrics []*testMetrics - -func cmpBool(b1, b2 bool) int { - if b1 == b2 { - return 0 - } - if !b1 { - return -1 - } - return 1 -} - -func (sm suiteMetrics) toCSV() string { - sort.SliceStable(sm, func(i, j int) bool { - a := sm[i] - b := sm[j] - - if cmp := cmpBool(a.hasQueueLatency, b.hasQueueLatency); cmp != 0 { - return cmp < 0 - } - - if cmp := cmpBool(a.hasNetworkLatency, b.hasNetworkLatency); cmp != 0 { - return cmp < 0 - } - - if cmp := strings.Compare(a.topology, b.topology); cmp != 0 { - return cmp < 0 - } - - // Sort algorithm in descending order so "round robin" is first - return strings.Compare(a.algorithm, b.algorithm) > 0 - }) - out := "TOPOLOGY,WEIGHTING,NW LATENCY,Q LATENCY,ALG,LATENCY (MIN),LATENCY (AVG),LATENCY (MAX),QLATENCY (MIN),QLATENCY (AVG),QLATENCY (MAX),IN-ZONE,IN-REGION,OUT-REGION\n" - for _, tm := range sm { - out += tm.toCSV() + "\n" - } - return out -} - -func runTest(t *testing.T, s testSettings, tm *testMetrics) { - t.Helper() - - wg := sync.WaitGroup{} - - clientLatencies := make([]timeseries.Data, len(s.mesh.Clients())) - for i, client := range s.mesh.Clients() { - i := i - client := client - wg.Add(1) - go func() { - // Assign weights to the endpoints. - var conns []*loadbalancer.WeightedConnection - for _, n := range s.mesh.Nodes() { - conns = append(conns, s.newWeightedConnection(client, n)) - } - - // Create a load balancer - lb := s.newLB(conns) - - // Send the requests. - client.SendRequests(lb, s.clientRequests, func() { - clientLatencies[i] = lb.Latency().Data() - wg.Done() - }) - }() - } - - wg.Wait() - - c := s.mesh.Clients()[0] - clientLocality := c.Locality() - clientLatency := clientLatencies[0] - - nodesSameZone := s.mesh.Nodes().Select(locality.MatchZone(clientLocality)) - nodesSameRegion := s.mesh.Nodes().Select(locality.MatchOtherZoneInSameRegion(clientLocality)) - nodesOtherRegion := s.mesh.Nodes().Select(locality.Not(locality.MatchRegion(clientLocality))) - - // Store in the output. - qLatency := s.mesh.Nodes().QueueLatency().Data() - tm.qLatencyMin = qLatency.Min() - tm.qLatencyAvg = qLatency.Mean() - tm.qLatencyMax = qLatency.Max() - tm.latencyMin = clientLatency.Min() - tm.latencyAvg = clientLatency.Mean() - tm.latencyMax = clientLatency.Max() - tm.nodesSameZone = len(nodesSameZone) - tm.nodesSameRegion = len(nodesSameRegion) - tm.nodesOtherRegion = len(nodesOtherRegion) - tm.requestsSameZone = nodesSameZone.TotalRequests() - tm.requestsSameRegion = nodesSameRegion.TotalRequests() - tm.requestsOtherRegion = nodesOtherRegion.TotalRequests() - - t.Log("Test Results:\n" + tm.String()) -} diff --git a/pkg/test/loadbalancersim/loadbalancer/edf.go b/pkg/test/loadbalancersim/loadbalancer/edf.go deleted file mode 100644 index 6a7517367..000000000 --- a/pkg/test/loadbalancersim/loadbalancer/edf.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package loadbalancer - -import ( - "container/heap" -) - -// Entry is an item for load balance -type Entry struct { - deadline float64 - index int64 - value interface{} - weight float64 -} - -// priorityQueue is a queue that always pop the highest priority item -type priorityQueue []*Entry - -// Len implements heap.Interface/sort.Interface -func (pq priorityQueue) Len() int { return len(pq) } - -// Less implements heap.Interface/sort.Interface -func (pq priorityQueue) Less(i, j int) bool { - // Flip logic to make this a min queue. - if pq[i].deadline == pq[j].deadline { - return pq[i].index < pq[j].index - } - return pq[i].deadline < pq[j].deadline -} - -// Swap implements heap.Interface/sort.Interface -func (pq priorityQueue) Swap(i, j int) { - pq[i], pq[j] = pq[j], pq[i] -} - -// Push implements heap.Interface for pushing an item into the heap -func (pq *priorityQueue) Push(x interface{}) { - entry := x.(*Entry) - *pq = append(*pq, entry) -} - -// Pop implements heap.Interface for poping an item from the heap -func (pq *priorityQueue) Pop() interface{} { - old := *pq - n := len(old) - entry := old[n-1] - *pq = old[0 : n-1] - return entry -} - -// EDF implements the Earliest Deadline First scheduling algorithm -type EDF struct { - pq *priorityQueue - currentIndex int64 - currentDeadline float64 -} - -// Add a new entry for load balance -func (e *EDF) Add(weight float64, value interface{}) { - e.currentIndex++ - heap.Push(e.pq, &Entry{ - value: value, - weight: weight, - deadline: e.currentDeadline + 1/weight, - index: e.currentIndex, - }) -} - -// PickAndAdd picks an available entry and re-adds it with the given weight calculation -func (e *EDF) PickAndAdd(calcWeight func(prevWeight float64, value interface{}) float64) interface{} { - // if no available entry, return nil - if len(*e.pq) == 0 { - return nil - } - entry := heap.Pop(e.pq).(*Entry) - // currentDeadline should be entry's deadline so that new added entry would have a fair - // competition environment with the old ones - e.currentDeadline = entry.deadline - - // Re-add it with the updated weight. - e.Add(calcWeight(entry.weight, entry.value), entry.value) - return entry.value -} - -// NewEDF create a new edf scheduler -func NewEDF() *EDF { - pq := make(priorityQueue, 0) - return &EDF{ - pq: &pq, - currentIndex: 0, - } -} diff --git a/pkg/test/loadbalancersim/loadbalancer/leastrequest.go b/pkg/test/loadbalancersim/loadbalancer/leastrequest.go deleted file mode 100644 index 2ef686442..000000000 --- a/pkg/test/loadbalancersim/loadbalancer/leastrequest.go +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package loadbalancer - -import ( - "math" - "math/rand" - "sync" - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/network" -) - -type LeastRequestSettings struct { - Connections []*WeightedConnection - ActiveRequestBias float64 -} - -func NewLeastRequest(s LeastRequestSettings) network.Connection { - if len(s.Connections) == 0 { - panic("attempting to create load balancer with zero connections") - } - - conn := newLBConnection("LeastRequestLB", s.Connections) - - if conn.AllWeightsEqual() { - return newUnweightedLeastRequest(conn) - } - - return newWeightedLeastRequest(conn, s.ActiveRequestBias) -} - -type unweightedLeastRequest struct { - *weightedConnections - r *rand.Rand -} - -func newUnweightedLeastRequest(conn *weightedConnections) network.Connection { - return &unweightedLeastRequest{ - weightedConnections: conn, - r: rand.New(rand.NewSource(time.Now().UnixNano())), - } -} - -func (lb *unweightedLeastRequest) pick2() (*WeightedConnection, *WeightedConnection) { - numConnections := len(lb.conns) - index1 := lb.r.Intn(numConnections) - index2 := lb.r.Intn(numConnections) - if index2 == index1 { - index2 = (index2 + 1) % numConnections - } - - return lb.get(index1), lb.get(index2) -} - -func (lb *unweightedLeastRequest) Request(onDone func()) { - if len(lb.conns) == 1 { - lb.doRequest(lb.get(0), onDone) - return - } - - // Pick 2 endpoints at random. - c1, c2 := lb.pick2() - - // Choose the endpoint with fewer active requests. - selected := c1 - if c2.ActiveRequests() < c1.ActiveRequests() { - selected = c2 - } - - // Apply the selected endpoint to the metrics decorator and send the request. - lb.doRequest(selected, onDone) -} - -type weightedLeastRequest struct { - *weightedConnections - activeRequestBias float64 - edf *EDF - edfMutex sync.Mutex -} - -func newWeightedLeastRequest(conn *weightedConnections, activeRequestBias float64) network.Connection { - lb := &weightedLeastRequest{ - weightedConnections: conn, - activeRequestBias: activeRequestBias, - edf: NewEDF(), - } - - // Add all endpoints to the EDF scheduler. - for _, c := range conn.conns { - lb.edf.Add(lb.calcEDFWeight(0, c), c) - } - - return lb -} - -func (lb *weightedLeastRequest) Request(onDone func()) { - // Pick the next endpoint and re-add it with the updated weight. - lb.edfMutex.Lock() - selected := lb.edf.PickAndAdd(lb.calcEDFWeight).(*WeightedConnection) - lb.edfMutex.Unlock() - - // Make the request. - lb.doRequest(selected, onDone) -} - -func (lb *weightedLeastRequest) calcEDFWeight(_ float64, value interface{}) float64 { - conn := value.(*WeightedConnection) - - weight := float64(conn.Weight) - if lb.activeRequestBias >= 1.0 { - weight /= float64(conn.ActiveRequests() + 1) - } else if lb.activeRequestBias > 0.0 { - weight /= math.Pow(float64(conn.ActiveRequests()+1), lb.activeRequestBias) - } - return weight -} diff --git a/pkg/test/loadbalancersim/loadbalancer/priority.go b/pkg/test/loadbalancersim/loadbalancer/priority.go deleted file mode 100644 index 066f0cf93..000000000 --- a/pkg/test/loadbalancersim/loadbalancer/priority.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package loadbalancer - -import ( - mesh2 "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/mesh" -) - -type PrioritySelector func(src *mesh2.Client, dest *mesh2.Node) uint32 - -func LocalityPrioritySelector(src *mesh2.Client, dest *mesh2.Node) uint32 { - priority := uint32(2) - if src.Locality().Region == dest.Locality().Region { - priority = 1 - if src.Locality().Zone == dest.Locality().Zone { - priority = 0 - } - } - return priority -} diff --git a/pkg/test/loadbalancersim/loadbalancer/roundrobin.go b/pkg/test/loadbalancersim/loadbalancer/roundrobin.go deleted file mode 100644 index c794966c7..000000000 --- a/pkg/test/loadbalancersim/loadbalancer/roundrobin.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package loadbalancer - -import ( - "math/rand" - "sync" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/network" -) - -func NewRoundRobin(conns []*WeightedConnection) network.Connection { - // Add instances for each connection based on the weight. - var lbConns []*WeightedConnection - for _, conn := range conns { - for i := uint32(0); i < conn.Weight; i++ { - lbConns = append(lbConns, conn) - } - } - - // Shuffle the connections. - rand.Shuffle(len(lbConns), func(i, j int) { - lbConns[i], lbConns[j] = lbConns[j], lbConns[i] - }) - - return &roundRobin{ - weightedConnections: newLBConnection("RoundRobinLB", lbConns), - } -} - -type roundRobin struct { - *weightedConnections - - next int - nextMutex sync.Mutex -} - -func (lb *roundRobin) Request(onDone func()) { - // Select the connection to use for this request. - lb.nextMutex.Lock() - selected := lb.get(lb.next) - lb.next = (lb.next + 1) % len(lb.conns) - lb.nextMutex.Unlock() - - lb.doRequest(selected, onDone) -} diff --git a/pkg/test/loadbalancersim/loadbalancer/weight.go b/pkg/test/loadbalancersim/loadbalancer/weight.go deleted file mode 100644 index ab3f1efc5..000000000 --- a/pkg/test/loadbalancersim/loadbalancer/weight.go +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package loadbalancer - -import ( - mesh2 "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/mesh" - network2 "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/network" - "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/timeseries" -) - -type WeightedConnection struct { - network2.Connection - Weight uint32 -} - -type weightedConnections struct { - conns []*WeightedConnection - helper *network2.ConnectionHelper -} - -func newLBConnection(name string, conns []*WeightedConnection) *weightedConnections { - return &weightedConnections{ - conns: conns, - helper: network2.NewConnectionHelper(name), - } -} - -func (lb *weightedConnections) AllWeightsEqual() bool { - if len(lb.conns) == 0 { - return true - } - - weight := lb.conns[0].Weight - for _, conn := range lb.conns { - if conn.Weight != weight { - return false - } - } - return true -} - -func (lb *weightedConnections) get(index int) *WeightedConnection { - return lb.conns[index] -} - -func (lb *weightedConnections) doRequest(c *WeightedConnection, onDone func()) { - lb.helper.Request(c.Request, onDone) -} - -func (lb *weightedConnections) Name() string { - return lb.helper.Name() -} - -func (lb *weightedConnections) TotalRequests() uint64 { - return lb.helper.TotalRequests() -} - -func (lb *weightedConnections) ActiveRequests() uint64 { - return lb.helper.ActiveRequests() -} - -func (lb *weightedConnections) Latency() *timeseries.Instance { - return lb.helper.Latency() -} - -type WeightedConnectionFactory func(src *mesh2.Client, n *mesh2.Node) *WeightedConnection - -func EquallyWeightedConnectionFactory() WeightedConnectionFactory { - return func(src *mesh2.Client, dest *mesh2.Node) *WeightedConnection { - return &WeightedConnection{ - Connection: src.Mesh().NewConnection(src, dest), - Weight: 1, - } - } -} - -func PriorityWeightedConnectionFactory(selectPriority PrioritySelector, priorityWeightMap map[uint32]uint32) WeightedConnectionFactory { - return func(src *mesh2.Client, dest *mesh2.Node) *WeightedConnection { - // Select the priority for this node. - priority := selectPriority(src, dest) - - // Get the weight for the priority. - weight := uint32(1) - if priorityWeightMap != nil { - if w := priorityWeightMap[priority]; w > 0 { - weight = w - } - } - - return &WeightedConnection{ - Connection: src.Mesh().NewConnection(src, dest), - Weight: weight, - } - } -} diff --git a/pkg/test/loadbalancersim/locality/locality.go b/pkg/test/loadbalancersim/locality/locality.go deleted file mode 100644 index 5eca0767b..000000000 --- a/pkg/test/loadbalancersim/locality/locality.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package locality - -import ( - "fmt" - "strings" -) - -type Instance struct { - Zone string - Region string -} - -func (l Instance) String() string { - return fmt.Sprintf("%s/%s", l.Region, l.Zone) -} - -func Parse(s string) Instance { - parts := strings.Split(s, "/") - if len(parts) != 2 { - panic("invalid locality string: " + s) - } - return Instance{ - Region: parts[0], - Zone: parts[1], - } -} diff --git a/pkg/test/loadbalancersim/locality/match.go b/pkg/test/loadbalancersim/locality/match.go deleted file mode 100644 index d9ca19990..000000000 --- a/pkg/test/loadbalancersim/locality/match.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package locality - -type Match func(Instance) bool - -func MatchZone(l Instance) Match { - return func(o Instance) bool { - return l.Zone == o.Zone && - l.Region == o.Region - } -} - -func MatchRegion(l Instance) Match { - return func(o Instance) bool { - return l.Region == o.Region - } -} - -func MatchOtherZoneInSameRegion(l Instance) Match { - return And(MatchRegion(l), Not(MatchZone(l))) -} - -func And(m1 Match, m2 Match) Match { - return func(o Instance) bool { - return m1(o) && m2(o) - } -} - -func Not(match Match) Match { - return func(o Instance) bool { - return !match(o) - } -} diff --git a/pkg/test/loadbalancersim/mesh/client.go b/pkg/test/loadbalancersim/mesh/client.go deleted file mode 100644 index 008e86b83..000000000 --- a/pkg/test/loadbalancersim/mesh/client.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "sync" - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/locality" - "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/network" -) - -type ClientSettings struct { - RPS int - Locality locality.Instance -} - -type Client struct { - mesh *Instance - s ClientSettings -} - -func (c *Client) Mesh() *Instance { - return c.mesh -} - -func (c *Client) Locality() locality.Instance { - return c.s.Locality -} - -func (c *Client) SendRequests(conn network.Connection, numRequests int, done func()) { - go func() { - wg := sync.WaitGroup{} - - interval := time.Duration((1.0 / float64(c.s.RPS)) * float64(time.Second)) - - ticker := time.NewTicker(interval) - for { - // Wait for to send the next request. - <-ticker.C - - // Send a request - wg.Add(1) - conn.Request(wg.Done) - numRequests-- - - if numRequests <= 0 { - ticker.Stop() - - // Wait for all pending requests to complete. - wg.Wait() - done() - return - } - } - }() -} diff --git a/pkg/test/loadbalancersim/mesh/mesh.go b/pkg/test/loadbalancersim/mesh/mesh.go deleted file mode 100644 index 441f83567..000000000 --- a/pkg/test/loadbalancersim/mesh/mesh.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "fmt" - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/locality" - "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/network" - "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/timer" -) - -type RouteKey struct { - Src locality.Instance - Dest locality.Instance -} - -type Settings struct { - NetworkLatencies map[RouteKey]time.Duration -} - -type Instance struct { - nodes Nodes - clients []*Client - s Settings - networkQ *timer.Queue -} - -func New(s Settings) *Instance { - return &Instance{ - s: s, - networkQ: timer.NewQueue(), - } -} - -func (m *Instance) Nodes() Nodes { - return m.nodes -} - -func (m *Instance) Clients() []*Client { - return m.clients -} - -func (m *Instance) NewConnection(src *Client, dest *Node) network.Connection { - // Lookup the route between the source and destination - networkLatency := m.s.NetworkLatencies[RouteKey{ - Src: src.Locality(), - Dest: dest.Locality(), - }] - - request := dest.Request - if networkLatency > time.Duration(0) { - request = func(onDone func()) { - m.networkQ.Schedule(func() { - dest.Request(onDone) - }, time.Now().Add(networkLatency)) - } - } - - return network.NewConnection(dest.Name(), request) -} - -func (m *Instance) ShutDown() { - m.networkQ.ShutDown() - m.nodes.ShutDown() -} - -func (m *Instance) NewNodes(count int, serviceTime time.Duration, enableQueueLatency bool, locality locality.Instance) Nodes { - out := make(Nodes, 0, count) - for i := 0; i < count; i++ { - name := fmt.Sprintf("%s_%d", locality, i) - out = append(out, newNode(name, serviceTime, enableQueueLatency, locality)) - } - - m.nodes = append(m.nodes, out...) - - return out -} - -func (m *Instance) NewClient(s ClientSettings) *Client { - c := &Client{ - mesh: m, - s: s, - } - - m.clients = append(m.clients, c) - return c -} diff --git a/pkg/test/loadbalancersim/mesh/node.go b/pkg/test/loadbalancersim/mesh/node.go deleted file mode 100644 index dab760ded..000000000 --- a/pkg/test/loadbalancersim/mesh/node.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package mesh - -import ( - "math" - "time" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/locality" - "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/network" - "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/timer" - "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/timeseries" -) - -var _ network.Connection = &Node{} - -const maxQLatency = 30 * time.Second - -type Node struct { - locality locality.Instance - helper *network.ConnectionHelper - q *timer.Queue - serviceTime time.Duration - qLatencyEnabled bool - qLength timeseries.Instance - qLatency timeseries.Instance -} - -func newNode(name string, serviceTime time.Duration, enableQueueLatency bool, l locality.Instance) *Node { - return &Node{ - locality: l, - helper: network.NewConnectionHelper(name), - q: timer.NewQueue(), - serviceTime: serviceTime, - qLatencyEnabled: enableQueueLatency, - } -} - -func (n *Node) Name() string { - return n.helper.Name() -} - -func (n *Node) QueueLength() *timeseries.Instance { - return &n.qLength -} - -func (n *Node) QueueLatency() *timeseries.Instance { - return &n.qLatency -} - -func (n *Node) calcRequestDuration() time.Duration { - // Get the current queue length. - qLen := n.q.Len() - qLatency := n.calcQLatency(qLen) - - // Add the observations - tnow := time.Now() - n.qLength.AddObservation(float64(qLen), tnow) - n.qLatency.AddObservation(qLatency.Seconds(), tnow) - - return n.serviceTime + qLatency -} - -func (n *Node) calcQLatency(qlen int) time.Duration { - if !n.qLatencyEnabled { - return 0 - } - - // Compute the queue latency in milliseconds. - latency := math.Pow(1.2, float64(qlen+1)) - - // Clip the latency at the maximum value. - clippedLatency := math.Min(latency, float64(maxQLatency.Milliseconds())) - - // Return the latency as milliseconds. - return time.Duration(clippedLatency) * time.Millisecond -} - -func (n *Node) TotalRequests() uint64 { - return n.helper.TotalRequests() -} - -func (n *Node) ActiveRequests() uint64 { - return n.helper.ActiveRequests() -} - -func (n *Node) Latency() *timeseries.Instance { - return n.helper.Latency() -} - -func (n *Node) Request(onDone func()) { - n.helper.Request(func(wrappedOnDone func()) { - deadline := time.Now().Add(n.calcRequestDuration()) - - // Schedule the done function to be called after the deadline. - n.q.Schedule(wrappedOnDone, deadline) - }, onDone) -} - -func (n *Node) Locality() locality.Instance { - return n.locality -} - -func (n *Node) ShutDown() { - n.q.ShutDown() -} - -type Nodes []*Node - -func (nodes Nodes) Select(match locality.Match) Nodes { - var out Nodes - for _, n := range nodes { - if match(n.locality) { - out = append(out, n) - } - } - return out -} - -func (nodes Nodes) Latency() *timeseries.Instance { - var out timeseries.Instance - for _, n := range nodes { - out.AddAll(n.Latency()) - } - return &out -} - -func (nodes Nodes) QueueLatency() *timeseries.Instance { - var out timeseries.Instance - for _, n := range nodes { - out.AddAll(n.QueueLatency()) - } - return &out -} - -func (nodes Nodes) TotalRequests() uint64 { - var out uint64 - for _, n := range nodes { - out += n.TotalRequests() - } - return out -} - -func (nodes Nodes) ShutDown() { - for _, n := range nodes { - n.ShutDown() - } -} diff --git a/pkg/test/loadbalancersim/network/connection.go b/pkg/test/loadbalancersim/network/connection.go deleted file mode 100644 index 91cb04e55..000000000 --- a/pkg/test/loadbalancersim/network/connection.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package network - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/timeseries" -) - -type Connection interface { - Name() string - Request(onDone func()) - TotalRequests() uint64 - ActiveRequests() uint64 - Latency() *timeseries.Instance -} - -func NewConnection(name string, request func(onDone func())) Connection { - return &connection{ - request: request, - helper: NewConnectionHelper(name), - } -} - -type connection struct { - request func(onDone func()) - helper *ConnectionHelper -} - -func (c *connection) Name() string { - return c.helper.Name() -} - -func (c *connection) TotalRequests() uint64 { - return c.helper.TotalRequests() -} - -func (c *connection) ActiveRequests() uint64 { - return c.helper.ActiveRequests() -} - -func (c *connection) Latency() *timeseries.Instance { - return c.helper.Latency() -} - -func (c *connection) Request(onDone func()) { - c.helper.Request(c.request, onDone) -} diff --git a/pkg/test/loadbalancersim/network/helper.go b/pkg/test/loadbalancersim/network/helper.go deleted file mode 100644 index 6bf8cf258..000000000 --- a/pkg/test/loadbalancersim/network/helper.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package network - -import ( - "time" -) - -import ( - "go.uber.org/atomic" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/loadbalancersim/timeseries" -) - -type ConnectionHelper struct { - name string - hist timeseries.Instance - active *atomic.Uint64 - total *atomic.Uint64 -} - -func NewConnectionHelper(name string) *ConnectionHelper { - return &ConnectionHelper{ - active: atomic.NewUint64(0), - total: atomic.NewUint64(0), - name: name, - } -} - -func (c *ConnectionHelper) Name() string { - return c.name -} - -func (c *ConnectionHelper) TotalRequests() uint64 { - return c.total.Load() -} - -func (c *ConnectionHelper) ActiveRequests() uint64 { - return c.active.Load() -} - -func (c *ConnectionHelper) Latency() *timeseries.Instance { - return &c.hist -} - -func (c *ConnectionHelper) Request(request func(onDone func()), onDone func()) { - start := time.Now() - c.total.Inc() - c.active.Inc() - - wrappedDone := func() { - // Calculate the latency for this request. - latency := time.Since(start) - - // Add the latency observation. - c.hist.AddObservation(latency.Seconds(), time.Now()) - - c.active.Dec() - - // Invoke the caller's handler. - onDone() - } - - request(wrappedDone) -} diff --git a/pkg/test/loadbalancersim/timer/queue.go b/pkg/test/loadbalancersim/timer/queue.go deleted file mode 100644 index 99412bdbd..000000000 --- a/pkg/test/loadbalancersim/timer/queue.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package timer - -import ( - "container/heap" - "sync" - "time" -) - -type Queue struct { - heap timerHeap - mutex sync.Mutex - stopCh chan struct{} - resetTimerCh chan struct{} - stopping bool - timer *time.Timer - currentDeadline time.Time -} - -func NewQueue() *Queue { - q := &Queue{ - heap: make(timerHeap, 0), - timer: time.NewTimer(1 * time.Minute), - stopCh: make(chan struct{}), - resetTimerCh: make(chan struct{}), - } - - // Start the worker thread. - go func() { - for { - select { - case <-q.stopCh: - q.stopTimer() - return - case <-q.resetTimerCh: - q.resetTimer() - case <-q.timer.C: - q.onTimerExpired() - } - } - }() - - return q -} - -func (q *Queue) Len() int { - q.mutex.Lock() - defer q.mutex.Unlock() - return q.heap.Len() -} - -func (q *Queue) Schedule(handler func(), deadline time.Time) { - // Add the timer to the heap. - q.mutex.Lock() - heap.Push(&q.heap, &entry{ - handler: handler, - deadline: deadline, - index: 0, - }) - q.mutex.Unlock() - - // Request that the timer be reset. - q.resetTimerCh <- struct{}{} -} - -func (q *Queue) ShutDown() { - close(q.stopCh) -} - -func (q *Queue) stopTimer() { - q.mutex.Lock() - q.stopping = true - q.mutex.Unlock() - - q.timer.Stop() -} - -func (q *Queue) resetTimer() { - // Below is a separate function to limit the scope of the lock. - // We don't want to lock when we modify the timer in case it causes - // an immediate callback, which would reaquire the lock. - needReset, resetDuration := func() (bool, time.Duration) { - q.mutex.Lock() - defer q.mutex.Unlock() - - if q.stopping { - // Ignore the event, since we're already shutting down. - return false, 0 - } - - e := q.heap.peek() - if e == nil || e.deadline.Equal(q.currentDeadline) { - // nothing to do. - return false, 0 - } - - q.currentDeadline = e.deadline - return true, time.Until(e.deadline) - }() - - // Reset the timer. - if needReset { - q.timer.Reset(resetDuration) - } -} - -func (q *Queue) onTimerExpired() { - // Collect all expired timers. - q.mutex.Lock() - handlers := q.heap.advanceTo(time.Now()) - q.mutex.Unlock() - - // Call the expired timer handlers. - for _, h := range handlers { - h() - } - - // Reset the timer based on the earliest deadline. - q.resetTimer() -} - -type entry struct { - deadline time.Time - handler func() - index int -} - -type timerHeap []*entry - -func (h timerHeap) peek() *entry { - if h.Len() > 0 { - return h[0] - } - return nil -} - -func (h *timerHeap) advanceTo(tnow time.Time) (out []func()) { - for { - if top := h.peek(); top != nil && !top.deadline.After(tnow) { - heap.Remove(h, top.index) - out = append(out, top.handler) - } else { - // There are no further expired timers. - return - } - } -} - -func (h timerHeap) Len() int { - return len(h) -} - -func (h timerHeap) Less(i, j int) bool { - return h[i].deadline.Before(h[j].deadline) -} - -func (h timerHeap) Swap(i, j int) { - h[i], h[j] = h[j], h[i] - h[i].index = i - h[j].index = j -} - -func (h *timerHeap) Push(x interface{}) { - e := x.(*entry) - *h = append(*h, e) - e.index = len(*h) - 1 -} - -func (h *timerHeap) Pop() interface{} { - n := h.Len() - e := (*h)[n-1] - *h = (*h)[:n-1] - e.index = -1 - return e -} diff --git a/pkg/test/loadbalancersim/timeseries/data.go b/pkg/test/loadbalancersim/timeseries/data.go deleted file mode 100644 index 29e4b2076..000000000 --- a/pkg/test/loadbalancersim/timeseries/data.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package timeseries - -import ( - "math" - "sort" -) - -var ( - negativeInfinity = math.Inf(-1) - infinity = math.Inf(1) - nan = math.NaN() -) - -type Data []float64 - -func (d Data) Min() float64 { - return d.sorted().min() -} - -func (d Data) Max() float64 { - return d.sorted().max() -} - -func (d Data) Median() float64 { - return d.Quantile(0.5) -} - -func (d Data) Mean() float64 { - total := float64(0) - for _, v := range d { - total += v - } - return total / float64(len(d)) -} - -func (d Data) Quantile(phi float64) float64 { - return d.sorted().quantile(phi) -} - -func (d Data) Quantiles(phis ...float64) []float64 { - return d.sorted().quantiles(phis...) -} - -func (d Data) Copy() Data { - out := make(Data, 0, len(d)) - out = append(out, d...) - return out -} - -func (d Data) sorted() sorted { - out := make(sorted, 0, len(d)) - out = append(out, d...) - - sort.Float64s(out) - return out -} - -type sorted []float64 - -func (s sorted) min() float64 { - if len(s) == 0 { - return negativeInfinity - } - - return s[0] -} - -func (s sorted) max() float64 { - if len(s) == 0 { - return infinity - } - - return s[len(s)-1] -} - -func (s sorted) quantile(phi float64) float64 { - if len(s) == 0 || math.IsNaN(phi) { - return nan - } - if phi <= 0 { - return s.min() - } - if phi >= 1 { - return s.max() - } - idx := uint(phi*float64(len(s)-1) + 0.5) - if idx >= uint(len(s)) { - idx = uint(len(s) - 1) - } - return s[idx] -} - -func (s sorted) quantiles(phis ...float64) []float64 { - out := make([]float64, 0, len(phis)) - for _, phi := range phis { - out = append(out, s.quantile(phi)) - } - return out -} diff --git a/pkg/test/loadbalancersim/timeseries/instance.go b/pkg/test/loadbalancersim/timeseries/instance.go deleted file mode 100644 index 53a194636..000000000 --- a/pkg/test/loadbalancersim/timeseries/instance.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package timeseries - -import ( - "sync" - "time" -) - -type Instance struct { - data Data - times times - mutex sync.Mutex -} - -func (ts *Instance) AddObservation(val float64, t time.Time) { - ts.mutex.Lock() - defer ts.mutex.Unlock() - ts.data = append(ts.data, val) - ts.times = append(ts.times, t) -} - -func (ts *Instance) AddAll(o *Instance) { - ts.mutex.Lock() - defer ts.mutex.Unlock() - - oData, oTimes := o.Series() - ts.data = append(ts.data, oData...) - ts.times = append(ts.times, oTimes...) -} - -func (ts *Instance) Data() Data { - ts.mutex.Lock() - defer ts.mutex.Unlock() - return ts.data.Copy() -} - -func (ts *Instance) Series() (Data, []time.Time) { - ts.mutex.Lock() - defer ts.mutex.Unlock() - return ts.data.Copy(), ts.times.copy() -} - -func (ts *Instance) SeriesAsDurationSinceEpoch(epoch time.Time) (Data, []time.Duration) { - ts.mutex.Lock() - defer ts.mutex.Unlock() - return ts.data.Copy(), ts.times.asDurationSinceEpoch(epoch) -} - -type times []time.Time - -func (t times) copy() times { - out := make(times, 0, len(t)) - out = append(out, t...) - return out -} - -func (t times) asDurationSinceEpoch(epoch time.Time) []time.Duration { - out := make([]time.Duration, 0, len(t)) - for _, tm := range t { - out = append(out, tm.Sub(epoch)) - } - return out -} diff --git a/pkg/test/profile/fgprof.go b/pkg/test/profile/fgprof.go deleted file mode 100644 index 25f1db9e1..000000000 --- a/pkg/test/profile/fgprof.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package profile - -import ( - "flag" - "os" -) - -import ( - "github.com/felixge/fgprof" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -var fprof string - -// init initializes additional profiling flags -// Go comes with some, like -cpuprofile and -memprofile by default, so those are elided. -func init() { - flag.StringVar(&fprof, "fullprofile", "", "enable full profile. Path will be relative to test directory") -} - -// FullProfile runs a "Full" profile (https://github.com/felixge/fgprof). This differs from standard -// CPU profile, as it includes both IO blocking and CPU usage in one profile, giving a full view of -// the application. -func FullProfile(t test.Failer) { - if fprof == "" { - return - } - f, err := os.Create(fprof) - if err != nil { - t.Fatalf("%v", err) - } - stop := fgprof.Start(f, fgprof.FormatPprof) - - t.Cleanup(func() { - if err := stop(); err != nil { - t.Fatal(err) - } - if err := f.Close(); err != nil { - t.Fatal(err) - } - }) -} diff --git a/pkg/test/scopes/scopes.go b/pkg/test/scopes/scopes.go deleted file mode 100644 index 1ba97528e..000000000 --- a/pkg/test/scopes/scopes.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package scopes - -import ( - "istio.io/pkg/log" -) - -// Framework is the general logging scope for the framework. -var Framework = log.RegisterScope("tf", "General scope for the test framework", 0) diff --git a/pkg/test/shell/shell.go b/pkg/test/shell/shell.go deleted file mode 100644 index 4f17072d4..000000000 --- a/pkg/test/shell/shell.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package shell - -import ( - "fmt" - "os/exec" - "strings" -) - -import ( - "github.com/google/shlex" - "istio.io/pkg/log" -) - -var scope = log.RegisterScope("shell", "Shell execution scope", 0) - -// Execute the given command. -func Execute(combinedOutput bool, format string, args ...interface{}) (string, error) { - s := fmt.Sprintf(format, args...) - - parts, err := shlex.Split(s) - if err != nil { - return "", fmt.Errorf("fail to parse cmd: %q, err: %v", s, err) - } - - var p []string - for i := 0; i < len(parts); i++ { - if parts[i] != "" { - p = append(p, parts[i]) - } - } - - var argStrings []string - if len(p) > 0 { - argStrings = p[1:] - } - return ExecuteArgs(nil, combinedOutput, parts[0], argStrings...) -} - -func ExecuteArgs(env []string, combinedOutput bool, name string, args ...string) (string, error) { - if scope.DebugEnabled() { - cmd := strings.Join(args, " ") - cmd = name + " " + cmd - scope.Debugf("Executing command: %s", cmd) - } - - c := exec.Command(name, args...) - c.Env = env - - var b []byte - var err error - if combinedOutput { - // Combine stderr and stdout in b. - b, err = c.CombinedOutput() - } else { - // Just return stdout in b. - b, err = c.Output() - } - - if err != nil || !c.ProcessState.Success() { - scope.Debugf("Command[%s] => (FAILED) %s", name, string(b)) - } else { - scope.Debugf("Command[%s] => %s", name, string(b)) - } - - return string(b), err -} diff --git a/pkg/test/util.go b/pkg/test/util.go deleted file mode 100644 index e5f72a609..000000000 --- a/pkg/test/util.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "os" - "time" -) - -// SetEnvForTest sets an environment variable for the duration of a test, then resets it once the test is complete. -func SetEnvForTest(t Failer, k, v string) { - old := os.Getenv(k) - if err := os.Setenv(k, v); err != nil { - t.Fatal(err) - } - t.Cleanup(func() { - if err := os.Setenv(k, old); err != nil { - t.Fatal(err) - } - }) -} - -// SetStringForTest sets a variable for the duration of a test, then resets it once the test is complete. -func SetStringForTest(t Failer, vv *string, v string) { - old := *vv - *vv = v - t.Cleanup(func() { - *vv = old - }) -} - -// SetBoolForTest sets a variable for the duration of a test, then resets it once the test is complete. -func SetBoolForTest(t Failer, vv *bool, v bool) { - old := *vv - *vv = v - t.Cleanup(func() { - *vv = old - }) -} - -// SetFloatForTest sets a variable for the duration of a test, then resets it once the test is complete. -func SetFloatForTest(t Failer, vv *float64, v float64) { - old := *vv - *vv = v - t.Cleanup(func() { - *vv = old - }) -} - -// SetDurationForTest sets a variable for the duration of a test, then resets it once the test is complete. -func SetDurationForTest(t Failer, vv *time.Duration, v time.Duration) { - old := *vv - *vv = v - t.Cleanup(func() { - *vv = old - }) -} diff --git a/pkg/test/util/assert/assert.go b/pkg/test/util/assert/assert.go deleted file mode 100644 index f49a5221d..000000000 --- a/pkg/test/util/assert/assert.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package assert - -import ( - "strings" -) - -import ( - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "google.golang.org/protobuf/testing/protocmp" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -// Equal -func Equal(t test.Failer, a, b interface{}, context ...string) { - t.Helper() - if !cmp.Equal(a, b, protocmp.Transform(), cmpopts.EquateEmpty()) { - cs := "" - if len(context) > 0 { - cs = " " + strings.Join(context, ", ") + ":" - } - t.Fatalf("found diff:%s %v", cs, cmp.Diff(a, b, protocmp.Transform())) - } -} - -func Error(t test.Failer, err error) { - t.Helper() - if err == nil { - t.Fatal("expected error but got nil") - } -} - -func NoError(t test.Failer, err error) { - t.Helper() - if err != nil { - t.Fatal("expected no error but got: %v", err) - } -} diff --git a/pkg/test/util/file/file.go b/pkg/test/util/file/file.go deleted file mode 100644 index 743a41de4..000000000 --- a/pkg/test/util/file/file.go +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package file - -import ( - "archive/tar" - "bytes" - "fmt" - "io" - "os" - "path/filepath" - "strings" -) - -import ( - "github.com/mitchellh/go-homedir" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -// AsBytes is a simple wrapper around os.ReadFile provided for completeness. -func AsBytes(filename string) ([]byte, error) { - return os.ReadFile(filename) -} - -// AsBytesOrFail calls AsBytes and fails the test if any errors occurred. -func AsBytesOrFail(t test.Failer, filename string) []byte { - t.Helper() - content, err := AsBytes(filename) - if err != nil { - t.Fatal(err) - } - return content -} - -// AsString is a convenience wrapper around os.ReadFile that converts the content to a string. -func AsStringArray(files ...string) ([]string, error) { - out := make([]string, 0, len(files)) - for _, f := range files { - b, err := AsBytes(f) - if err != nil { - return nil, err - } - out = append(out, string(b)) - } - return out, nil -} - -// AsStringArrayOrFail calls AsStringOrFail and then converts to string. -func AsStringArrayOrFail(t test.Failer, files ...string) []string { - t.Helper() - out, err := AsStringArray(files...) - if err != nil { - t.Fatal(err) - } - return out -} - -// AsString is a convenience wrapper around os.ReadFile that converts the content to a string. -func AsString(filename string) (string, error) { - b, err := AsBytes(filename) - if err != nil { - return "", err - } - return string(b), nil -} - -// AsStringOrFail calls AsBytesOrFail and then converts to string. -func AsStringOrFail(t test.Failer, filename string) string { - t.Helper() - return string(AsBytesOrFail(t, filename)) -} - -// NormalizePath expands the homedir (~) and returns an error if the file doesn't exist. -func NormalizePath(originalPath string) (string, error) { - if originalPath == "" { - return "", nil - } - // trim leading/trailing spaces from the path and if it uses the homedir ~, expand it. - var err error - out := strings.TrimSpace(originalPath) - out, err = homedir.Expand(out) - if err != nil { - return "", err - } - - // Verify that the file exists. - if _, err := os.Stat(out); os.IsNotExist(err) { - return "", fmt.Errorf("failed normalizing file %s: %v", originalPath, err) - } - - return out, nil -} - -// ReadTarFile reads a tar compress file from the embedded -func ReadTarFile(filePath string) (string, error) { - b, err := os.ReadFile(filePath) - if err != nil { - return "", err - } - tr := tar.NewReader(bytes.NewBuffer(b)) - for { - hdr, err := tr.Next() - if err == io.EOF { - break // End of archive - } - if err != nil { - return "", err - } - if hdr.Name != strings.TrimSuffix(filepath.Base(filePath), filepath.Ext(filePath)) { - continue - } - contents, err := io.ReadAll(tr) - if err != nil { - return "", err - } - return string(contents), nil - } - return "", fmt.Errorf("file not found %v", filePath) -} - -// ReadDir returns the names of all files in the given directory. This is not recursive. -// The base path is appended; for example, ReadDir("dir") -> ["dir/file1", "dir/folder1"] -func ReadDir(filePath string, extensions ...string) ([]string, error) { - dir, err := os.ReadDir(filePath) - if err != nil { - return nil, err - } - res := []string{} - for _, d := range dir { - matched := len(extensions) == 0 // If none are set, match anything - for _, ext := range extensions { - if filepath.Ext(d.Name()) == ext { - matched = true - break - } - } - if matched { - res = append(res, filepath.Join(filePath, d.Name())) - } - } - return res, nil -} - -func ReadDirOrFail(t test.Failer, filePath string, extensions ...string) []string { - res, err := ReadDir(filePath, extensions...) - if err != nil { - t.Fatal(err) - } - return res -} diff --git a/pkg/test/util/reserveport/api.go b/pkg/test/util/reserveport/api.go deleted file mode 100644 index 856fa00a2..000000000 --- a/pkg/test/util/reserveport/api.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package reserveport - -import ( - "testing" -) - -const ( - poolSize = 50 -) - -// ReservedPort a port reserved by a PortManager -type ReservedPort interface { - // GetPort returns the bound port number. - GetPort() uint16 - // Close unbinds this port. - Close() error - CloseSilently() -} - -// PortManager is responsible for reserving ports for an application. -type PortManager interface { - // ReservePort reserves a new port. The lifecycle of the returned port is transferred to the caller. - ReservePort() (ReservedPort, error) - ReservePortOrFail(t *testing.T) ReservedPort - ReservePortNumber() (uint16, error) - ReservePortNumberOrFail(t *testing.T) uint16 - // Close shuts down this manager and frees any associated resources. - Close() error - CloseSilently() -} - -// NewPortManager allocates a new PortManager -func NewPortManager() (mgr PortManager, err error) { - return &managerImpl{}, nil -} - -// NewPortManagerOrFail calls NewPortManager and fails the test if unsuccessful. -func NewPortManagerOrFail(t *testing.T) PortManager { - t.Helper() - mgr, err := NewPortManager() - if err != nil { - t.Fatal(err) - } - return mgr -} diff --git a/pkg/test/util/reserveport/manager.go b/pkg/test/util/reserveport/manager.go deleted file mode 100644 index 5e58f5256..000000000 --- a/pkg/test/util/reserveport/manager.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package reserveport - -import ( - "fmt" - "sync" - "testing" -) - -type managerImpl struct { - pool []ReservedPort - index int - mutex sync.Mutex -} - -func (m *managerImpl) ReservePort() (ReservedPort, error) { - m.mutex.Lock() - defer m.mutex.Unlock() - - if m.index >= len(m.pool) { - // Re-create the pool. - var err error - m.pool, err = allocatePool(poolSize) - if err != nil { - return nil, err - } - m.index = 0 - // Don't need to free the pool, since all ports have been reserved. - } - - p := m.pool[m.index] - if p == nil { - return nil, fmt.Errorf("attempting to reserve port after manager closed") - } - m.pool[m.index] = nil - m.index++ - return p, nil -} - -func (m *managerImpl) ReservePortOrFail(t *testing.T) ReservedPort { - t.Helper() - p, err := m.ReservePort() - if err != nil { - t.Fatal(err) - } - return p -} - -func (m *managerImpl) ReservePortNumber() (uint16, error) { - p, err := m.ReservePort() - if err != nil { - return 0, err - } - n := p.GetPort() - if err := p.Close(); err != nil { - return 0, err - } - return n, nil -} - -func (m *managerImpl) ReservePortNumberOrFail(t *testing.T) uint16 { - t.Helper() - p, err := m.ReservePortNumber() - if err != nil { - t.Fatal(err) - } - return p -} - -func (m *managerImpl) Close() (err error) { - m.mutex.Lock() - defer m.mutex.Unlock() - - pool := m.pool - m.pool = nil - m.index = 0 - return freePool(pool) -} - -func (m *managerImpl) CloseSilently() { - _ = m.Close() -} diff --git a/pkg/test/util/reserveport/pool.go b/pkg/test/util/reserveport/pool.go deleted file mode 100644 index 772f14ab1..000000000 --- a/pkg/test/util/reserveport/pool.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package reserveport - -import ( - multierror "github.com/hashicorp/go-multierror" -) - -func allocatePool(size int) (pool []ReservedPort, err error) { - tempPool := make([]ReservedPort, size) - defer func() { - if err != nil { - _ = freePool(tempPool) - } - }() - - for i := 0; i < size; i++ { - var err error - tempPool[i], err = newReservedPort() - if err != nil { - return nil, err - } - } - return tempPool, nil -} - -func freePool(pool []ReservedPort) (err error) { - // Close any ports still in the pool. - for i := 0; i < len(pool); i++ { - p := pool[i] - if p == nil { - continue - } - - if e := p.Close(); e != nil { - err = multierror.Append(err, e) - } - } - return err -} diff --git a/pkg/test/util/reserveport/port.go b/pkg/test/util/reserveport/port.go deleted file mode 100644 index 8de42c236..000000000 --- a/pkg/test/util/reserveport/port.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package reserveport - -import ( - "net" -) - -type portImpl struct { - listener net.Listener - port uint16 -} - -func (p *portImpl) GetPort() uint16 { - return p.port -} - -func (p *portImpl) Close() error { - if p.listener != nil { - return p.listener.Close() - } - return nil -} - -func (p *portImpl) CloseSilently() { - _ = p.Close() -} - -func newReservedPort() (port ReservedPort, err error) { - addr, err := net.ResolveTCPAddr("tcp", "localhost:0") - if err != nil { - return nil, err - } - - l, err := net.ListenTCP("tcp", addr) - if err != nil { - return nil, err - } - // If any errors occur later in this method, close the listener. - defer func() { - if err != nil { - _ = l.Close() - } - }() - - p := uint16(l.Addr().(*net.TCPAddr).Port) - return &portImpl{ - listener: l, - port: p, - }, nil -} diff --git a/pkg/test/util/retry/retry.go b/pkg/test/util/retry/retry.go deleted file mode 100644 index 2408a660b..000000000 --- a/pkg/test/util/retry/retry.go +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package retry - -import ( - "errors" - "fmt" - "time" -) - -import ( - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -var scope = log.RegisterScope("retry", "logs for retries", 0) - -const ( - // DefaultTimeout the default timeout for the entire retry operation - DefaultTimeout = time.Second * 30 - - // DefaultDelay the default delay between successive retry attempts - DefaultDelay = time.Millisecond * 10 - - // DefaultConverge the default converge, requiring something to succeed one time - DefaultConverge = 1 -) - -var defaultConfig = config{ - timeout: DefaultTimeout, - delay: DefaultDelay, - delayMax: DefaultDelay * 16, - converge: DefaultConverge, -} - -type config struct { - error string - timeout time.Duration - delay time.Duration - delayMax time.Duration - converge int -} - -// Option for a retry operation. -type Option func(cfg *config) - -// Timeout sets the timeout for the entire retry operation. -func Timeout(timeout time.Duration) Option { - return func(cfg *config) { - cfg.timeout = timeout - } -} - -// Delay sets the delay between successive retry attempts. -func Delay(delay time.Duration) Option { - return func(cfg *config) { - cfg.delay = delay - cfg.delayMax = delay - } -} - -func BackoffDelay(delay time.Duration) Option { - return func(cfg *config) { - cfg.delay = delay - // Currently, hardcode to 16 backoffs. We can make it configurable if needed - cfg.delayMax = delay * 16 - } -} - -// Converge sets the number of successes in a row needed to count a success. -// This is useful to avoid the case where tests like `coin.Flip() == HEADS` will always -// return success due to random variance. -func Converge(successes int) Option { - return func(cfg *config) { - cfg.converge = successes - } -} - -// Message defines a more detailed error message to use when failing -func Message(errorMessage string) Option { - return func(cfg *config) { - cfg.error = errorMessage - } -} - -// RetriableFunc a function that can be retried. -type RetriableFunc func() (result interface{}, completed bool, err error) - -// UntilSuccess retries the given function until success, timeout, or until the passed-in function returns nil. -func UntilSuccess(fn func() error, options ...Option) error { - _, e := UntilComplete(func() (interface{}, bool, error) { - err := fn() - if err != nil { - return nil, false, err - } - - return nil, true, nil - }, options...) - - return e -} - -// UntilSuccessOrFail calls UntilSuccess, and fails t with Fatalf if it ends up returning an error -func UntilSuccessOrFail(t test.Failer, fn func() error, options ...Option) { - t.Helper() - err := UntilSuccess(fn, options...) - if err != nil { - t.Fatalf("retry.UntilSuccessOrFail: %v", err) - } -} - -var ErrConditionNotMet = errors.New("expected condition not met") - -// Until retries the given function until it returns true or hits the timeout timeout -func Until(fn func() bool, options ...Option) error { - return UntilSuccess(func() error { - if !fn() { - return getErrorMessage(options) - } - return nil - }, options...) -} - -// UntilOrFail calls Until, and fails t with Fatalf if it ends up returning an error -func UntilOrFail(t test.Failer, fn func() bool, options ...Option) { - t.Helper() - err := Until(fn, options...) - if err != nil { - t.Fatalf("retry.UntilOrFail: %v", err) - } -} - -func getErrorMessage(options []Option) error { - cfg := defaultConfig - for _, option := range options { - option(&cfg) - } - if cfg.error == "" { - return ErrConditionNotMet - } - return errors.New(cfg.error) -} - -// UntilComplete retries the given function, until there is a timeout, or until the function indicates that it has completed. -// Once complete, the returned value and error are returned. -func UntilComplete(fn RetriableFunc, options ...Option) (interface{}, error) { - cfg := defaultConfig - for _, option := range options { - option(&cfg) - } - - successes := 0 - attempts := 0 - var lasterr error - to := time.After(cfg.timeout) - delay := cfg.delay - for { - select { - case <-to: - return nil, fmt.Errorf("timeout while waiting after %d attempts (last error: %v)", attempts, lasterr) - default: - } - - result, completed, err := fn() - attempts++ - if completed { - if err == nil { - successes++ - } else { - successes = 0 - } - if successes >= cfg.converge { - return result, err - } - - // Skip delay if we have a success - continue - } - successes = 0 - if err != nil { - scope.Debugf("encountered an error on attempt %d: %v", attempts, err) - lasterr = err - } - - select { - case <-to: - convergeStr := "" - if cfg.converge > 1 { - convergeStr = fmt.Sprintf(", %d/%d successes", successes, cfg.converge) - } - return nil, fmt.Errorf("timeout while waiting after %d attempts%s (last error: %v)", attempts, convergeStr, lasterr) - case <-time.After(delay): - delay *= 2 - if delay > cfg.delayMax { - delay = cfg.delayMax - } - } - } -} diff --git a/pkg/test/util/retry/retry_test.go b/pkg/test/util/retry/retry_test.go deleted file mode 100644 index 45b42509f..000000000 --- a/pkg/test/util/retry/retry_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package retry - -import ( - "fmt" - "testing" - "time" -) - -func TestConverge(t *testing.T) { - t.Run("no converge", func(t *testing.T) { - flipFlop := true - err := UntilSuccess(func() error { - flipFlop = !flipFlop - if flipFlop { - return fmt.Errorf("flipFlop was true") - } - return nil - }, Converge(2), Timeout(time.Millisecond*10), Delay(time.Millisecond)) - if err == nil { - t.Fatal("expected no convergence, but test passed") - } - }) - - t.Run("converge", func(t *testing.T) { - n := 0 - err := UntilSuccess(func() error { - n++ - if n < 10 { - return fmt.Errorf("%v is too low, try again", n) - } - return nil - }, Converge(2), Timeout(time.Second*10000), Delay(time.Millisecond)) - if err != nil { - t.Fatalf("expected convergance, but test failed: %v", err) - } - }) -} diff --git a/pkg/test/util/structpath/instance.go b/pkg/test/util/structpath/instance.go deleted file mode 100644 index c5fa34c81..000000000 --- a/pkg/test/util/structpath/instance.go +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package structpath - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "reflect" - "regexp" - "strings" -) - -import ( - "github.com/google/go-cmp/cmp" - "google.golang.org/protobuf/proto" - "k8s.io/client-go/util/jsonpath" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" -) - -var ( - fixupNumericJSONComparison = regexp.MustCompile(`([=<>]+)\s*([0-9]+)\s*\)`) - fixupAttributeReference = regexp.MustCompile(`\[\s*'[^']+\s*'\s*]`) -) - -type Instance struct { - structure interface{} - isJSON bool - constraints []constraint - creationError error -} - -type constraint func() error - -// ForProto creates a structpath Instance by marshaling the proto to JSON and then evaluating over that -// structure. This is the most generally useful form as serialization to JSON also automatically -// converts proto.Any and proto.Struct to the serialized JSON forms which can then be evaluated -// over. The downside is the loss of type fidelity for numeric types as JSON can only represent -// floats. -func ForProto(proto proto.Message) *Instance { - if proto == nil { - return newErrorInstance(errors.New("expected non-nil proto")) - } - - parsed, err := protoToParsedJSON(proto) - if err != nil { - return newErrorInstance(err) - } - - i := &Instance{ - isJSON: true, - structure: parsed, - } - i.structure = parsed - return i -} - -func newErrorInstance(err error) *Instance { - return &Instance{ - isJSON: true, - creationError: err, - } -} - -func protoToParsedJSON(message proto.Message) (interface{}, error) { - // Convert proto to json and then parse into struct - jsonText, err := protomarshal.MarshalIndent(message, " ") - if err != nil { - return nil, fmt.Errorf("failed to convert proto to JSON: %v", err) - } - var parsed interface{} - err = json.Unmarshal(jsonText, &parsed) - if err != nil { - return nil, fmt.Errorf("failed to parse into JSON struct: %v", err) - } - return parsed, nil -} - -func (i *Instance) Select(path string, args ...interface{}) *Instance { - if i.creationError != nil { - // There was an error during the creation of this Instance. Just return the - // same instance since it will error on Check anyway. - return i - } - - path = fmt.Sprintf(path, args...) - value, err := i.findValue(path) - if err != nil { - return newErrorInstance(err) - } - if value == nil { - return newErrorInstance(fmt.Errorf("cannot select non-existent path: %v", path)) - } - - // Success. - return &Instance{ - isJSON: i.isJSON, - structure: value, - } -} - -func (i *Instance) appendConstraint(fn func() error) *Instance { - i.constraints = append(i.constraints, fn) - return i -} - -func (i *Instance) Equals(expected interface{}, path string, args ...interface{}) *Instance { - path = fmt.Sprintf(path, args...) - return i.appendConstraint(func() error { - typeOf := reflect.TypeOf(expected) - protoMessageType := reflect.TypeOf((*proto.Message)(nil)).Elem() - if typeOf.Implements(protoMessageType) { - return i.equalsStruct(expected.(proto.Message), path) - } - switch kind := typeOf.Kind(); kind { - case reflect.String: - return i.equalsString(reflect.ValueOf(expected).String(), path) - case reflect.Bool: - return i.equalsBool(reflect.ValueOf(expected).Bool(), path) - case reflect.Float32, reflect.Float64: - return i.equalsNumber(reflect.ValueOf(expected).Float(), path) - case reflect.Int, reflect.Int8, reflect.Int32, reflect.Int64: - return i.equalsNumber(float64(reflect.ValueOf(expected).Int()), path) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return i.equalsNumber(float64(reflect.ValueOf(expected).Uint()), path) - case protoMessageType.Kind(): - } - // TODO: Add struct support - return fmt.Errorf("attempt to call Equals for unsupported type: %v", expected) - }) -} - -func (i *Instance) ContainSubstring(substr, path string) *Instance { - return i.appendConstraint(func() error { - value, err := i.execute(path) - if err != nil { - return err - } - if found := strings.Contains(value, substr); !found { - return fmt.Errorf("substring %v did not match: %v", substr, value) - } - return nil - }) -} - -func (i *Instance) equalsString(expected string, path string) error { - value, err := i.execute(path) - if err != nil { - return err - } - if value != expected { - return fmt.Errorf("expected %v but got %v for path %v", expected, value, path) - } - return nil -} - -func (i *Instance) equalsNumber(expected float64, path string) error { - v, err := i.findValue(path) - if err != nil { - return err - } - result := reflect.ValueOf(v).Float() - if result != expected { - return fmt.Errorf("expected %v but got %v for path %v", expected, result, path) - } - return nil -} - -func (i *Instance) equalsBool(expected bool, path string) error { - v, err := i.findValue(path) - if err != nil { - return err - } - result := reflect.ValueOf(v).Bool() - if result != expected { - return fmt.Errorf("expected %v but got %v for path %v", expected, result, path) - } - return nil -} - -func (i *Instance) equalsStruct(proto proto.Message, path string) error { - jsonStruct, err := protoToParsedJSON(proto) - if err != nil { - return err - } - v, err := i.findValue(path) - if err != nil { - return err - } - diff := cmp.Diff(reflect.ValueOf(v).Interface(), jsonStruct) - if diff != "" { - return fmt.Errorf("structs did not match: %v", diff) - } - return nil -} - -func (i *Instance) Exists(path string, args ...interface{}) *Instance { - path = fmt.Sprintf(path, args...) - return i.appendConstraint(func() error { - v, err := i.findValue(path) - if err != nil { - return err - } - if v == nil { - return fmt.Errorf("no entry exists at path: %v", path) - } - return nil - }) -} - -func (i *Instance) NotExists(path string, args ...interface{}) *Instance { - path = fmt.Sprintf(path, args...) - return i.appendConstraint(func() error { - parser := jsonpath.New("path") - err := parser.Parse(i.fixPath(path)) - if err != nil { - return fmt.Errorf("invalid path: %v - %v", path, err) - } - values, err := parser.AllowMissingKeys(true).FindResults(i.structure) - if err != nil { - return fmt.Errorf("err finding results for path: %v - %v", path, err) - } - if len(values) == 0 { - return nil - } - if len(values[0]) > 0 { - return fmt.Errorf("expected no result but got: %v for path: %v", values[0], path) - } - return nil - }) -} - -// Check executes the set of constraints for this selection -// and returns the first error encountered, or nil if all constraints -// have been successfully met. All constraints are removed after them -// check is performed. -func (i *Instance) Check() error { - // After the check completes, clear out the constraints. - defer func() { - i.constraints = i.constraints[:0] - }() - - // If there was a creation error, just return that immediately. - if i.creationError != nil { - return i.creationError - } - - for _, c := range i.constraints { - if err := c(); err != nil { - return err - } - } - return nil -} - -// CheckOrFail calls Check on this selection and fails the given test if an -// error is encountered. -func (i *Instance) CheckOrFail(t test.Failer) *Instance { - t.Helper() - if err := i.Check(); err != nil { - t.Fatal(err) - } - return i -} - -func (i *Instance) execute(path string) (string, error) { - parser := jsonpath.New("path") - err := parser.Parse(i.fixPath(path)) - if err != nil { - return "", fmt.Errorf("invalid path: %v - %v", path, err) - } - buf := new(bytes.Buffer) - err = parser.Execute(buf, i.structure) - if err != nil { - return "", fmt.Errorf("err finding results for path: %v - %v", path, err) - } - return buf.String(), nil -} - -func (i *Instance) findValue(path string) (interface{}, error) { - parser := jsonpath.New("path") - err := parser.Parse(i.fixPath(path)) - if err != nil { - return nil, fmt.Errorf("invalid path: %v - %v", path, err) - } - values, err := parser.FindResults(i.structure) - if err != nil { - return nil, fmt.Errorf("err finding results for path: %v: %v. Structure: %v", path, err, i.structure) - } - if len(values) == 0 || len(values[0]) == 0 { - return nil, fmt.Errorf("no value for path: %v", path) - } - return values[0][0].Interface(), nil -} - -// Fixes up some quirks in jsonpath handling. -// See https://github.com/kubernetes/client-go/issues/553 -func (i *Instance) fixPath(path string) string { - // jsonpath doesn't handle numeric comparisons in a tolerant way. All json numbers are floats - // and filter expressions on the form {.x[?(@.some.value==123]} won't work but - // {.x[?(@.some.value==123.0]} will. - result := path - if i.isJSON { - template := "$1$2.0)" - result = fixupNumericJSONComparison.ReplaceAllString(path, template) - } - // jsonpath doesn't like map literal references that contain periods. I.e - // you can't do x['user.map'] but x.user\.map works so we just translate to that - result = string(fixupAttributeReference.ReplaceAllFunc([]byte(result), func(i []byte) []byte { - input := string(i) - input = strings.Replace(input, "[", "", 1) - input = strings.Replace(input, "]", "", 1) - input = strings.Replace(input, "'", "", 2) - parts := strings.Split(input, ".") - output := "." - for i := 0; i < len(parts)-1; i++ { - output += parts[i] - output += "\\." - } - output += parts[len(parts)-1] - return []byte(output) - })) - - return result -} diff --git a/pkg/test/util/structpath/instance_test.go b/pkg/test/util/structpath/instance_test.go deleted file mode 100644 index 5eb29485c..000000000 --- a/pkg/test/util/structpath/instance_test.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package structpath_test - -import ( - "testing" -) - -import ( - discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/util/structpath" -) - -func TestContainSubstring(t *testing.T) { - testResponse := &discovery.DiscoveryResponse{ - VersionInfo: "2019-07-16T10:54:41-07:00/1", - TypeUrl: "some.Random.Type.URL", - } - validator := structpath.ForProto(testResponse) - - tests := []struct { - name string - substrs []string - err bool - }{ - { - name: "Substring exist", - substrs: []string{"Random", "Type", "URL", "some"}, - err: false, - }, - { - name: "Substring does not exist", - substrs: []string{"RaNdOm"}, - err: true, - }, - { - name: "Substring empty", - substrs: []string{" "}, - err: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var instance *structpath.Instance - for _, s := range tt.substrs { - instance = validator.ContainSubstring(s, "{.typeUrl}") - } - err := instance.Check() - if tt.err && err == nil { - t.Errorf("expected err but got none for %s", tt.name) - } - }) - } -} diff --git a/pkg/test/util/tmpl/evaluate.go b/pkg/test/util/tmpl/evaluate.go deleted file mode 100644 index e5353422d..000000000 --- a/pkg/test/util/tmpl/evaluate.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package tmpl - -import ( - "fmt" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/file" -) - -// Evaluate parses the template and then executes it with the given parameters. -func Evaluate(tpl string, data interface{}) (string, error) { - t, err := Parse(tpl) - if err != nil { - return "", err - } - - return Execute(t, data) -} - -func EvaluateFile(filePath string, data interface{}) (string, error) { - tpl, err := file.AsString(filePath) - if err != nil { - return "", err - } - return Evaluate(tpl, data) -} - -// EvaluateOrFail calls Evaluate and fails tests if it returns error. -func EvaluateOrFail(t test.Failer, tpl string, data interface{}) string { - t.Helper() - s, err := Evaluate(tpl, data) - if err != nil { - t.Fatalf("tmpl.EvaluateOrFail: %v", err) - } - return s -} - -func EvaluateFileOrFail(t test.Failer, filePath string, data interface{}) string { - t.Helper() - s, err := EvaluateFile(filePath, data) - if err != nil { - t.Fatalf("tmpl.EvaluateFileOrFail: %v", err) - } - return s -} - -// MustEvaluate calls Evaluate and panics if there is an error. -func MustEvaluate(tpl string, data interface{}) string { - s, err := Evaluate(tpl, data) - if err != nil { - panic(fmt.Sprintf("tmpl.MustEvaluate: %v", err)) - } - return s -} - -func MustEvaluateFile(filePath string, data interface{}) string { - s, err := EvaluateFile(filePath, data) - if err != nil { - panic(fmt.Sprintf("tmpl.MustEvaluate: %v", err)) - } - return s -} - -// EvaluateAll calls Evaluate the same data args against each of the given templates. -func EvaluateAll(data interface{}, templates ...string) ([]string, error) { - out := make([]string, 0, len(templates)) - for _, t := range templates { - content, err := Evaluate(t, data) - if err != nil { - return nil, err - } - out = append(out, content) - } - return out, nil -} - -func EvaluateAllFiles(data interface{}, filePaths ...string) ([]string, error) { - templates, err := file.AsStringArray(filePaths...) - if err != nil { - return nil, err - } - return EvaluateAll(data, templates...) -} - -func MustEvaluateAll(data interface{}, templates ...string) []string { - out, err := EvaluateAll(data, templates...) - if err != nil { - panic(fmt.Sprintf("tmpl.MustEvaluateAll: %v", err)) - } - return out -} - -// EvaluateAllOrFail calls Evaluate and fails t if an error occurs. -func EvaluateAllOrFail(t test.Failer, data interface{}, templates ...string) []string { - t.Helper() - out, err := EvaluateAll(data, templates...) - if err != nil { - t.Fatal(err) - } - return out -} - -func EvaluateAllFilesOrFail(t test.Failer, data interface{}, filePaths ...string) []string { - t.Helper() - out, err := EvaluateAllFiles(data, filePaths...) - if err != nil { - t.Fatal(err) - } - return out -} diff --git a/pkg/test/util/tmpl/execute.go b/pkg/test/util/tmpl/execute.go deleted file mode 100644 index d580b1ce0..000000000 --- a/pkg/test/util/tmpl/execute.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package tmpl - -import ( - "bytes" - "text/template" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -// Execute the template with the given parameters. -func Execute(t *template.Template, data interface{}) (string, error) { - var b bytes.Buffer - if err := t.Execute(&b, data); err != nil { - return "", err - } - - return b.String(), nil -} - -// ExecuteOrFail calls Execute and fails the test if it returns an error. -func ExecuteOrFail(t test.Failer, t2 *template.Template, data interface{}) string { - t.Helper() - s, err := Execute(t2, data) - if err != nil { - t.Fatal(err) - } - return s -} diff --git a/pkg/test/util/tmpl/parse.go b/pkg/test/util/tmpl/parse.go deleted file mode 100644 index 0755de081..000000000 --- a/pkg/test/util/tmpl/parse.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package tmpl - -import ( - "text/template" -) - -import ( - "github.com/Masterminds/sprig/v3" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -// Parse the given template content. -func Parse(tpl string) (*template.Template, error) { - t := template.New("test template") - return t.Funcs(sprig.TxtFuncMap()).Parse(tpl) -} - -// ParseOrFail calls Parse and fails tests if it returns error. -func ParseOrFail(t test.Failer, tpl string) *template.Template { - t.Helper() - tpl2, err := Parse(tpl) - if err != nil { - t.Fatalf("tmpl.ParseOrFail: %v", err) - } - return tpl2 -} diff --git a/pkg/test/util/yml/apply.go b/pkg/test/util/yml/apply.go deleted file mode 100644 index 0e5e190a5..000000000 --- a/pkg/test/util/yml/apply.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package yml - -import ( - "fmt" - "reflect" -) - -import ( - "sigs.k8s.io/yaml" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -// ApplyNamespace applies the given namespaces to the resources in the yamlText if not set. -func ApplyNamespace(yamlText, ns string) (string, error) { - chunks := SplitString(yamlText) - - toJoin := make([]string, 0, len(chunks)) - for _, chunk := range chunks { - chunk, err := applyNamespace(chunk, ns) - if err != nil { - return "", err - } - toJoin = append(toJoin, chunk) - } - - result := JoinString(toJoin...) - return result, nil -} - -// MustApplyNamespace applies the given namespaces to the resources in the yamlText if not set. -func MustApplyNamespace(t test.Failer, yamlText, ns string) string { - y, err := ApplyNamespace(yamlText, ns) - if err != nil { - t.Fatalf("ApplyNamespace: %v for text %v", err, yamlText) - } - return y -} - -func applyNamespace(yamlText, ns string) (string, error) { - m := make(map[string]interface{}) - if err := yaml.Unmarshal([]byte(yamlText), &m); err != nil { - return "", err - } - - meta, err := ensureChildMap(m, "metadata") - if err != nil { - return "", err - } - if meta["namespace"] != nil && meta["namespace"] != "" { - return yamlText, nil - } - meta["namespace"] = ns - - by, err := yaml.Marshal(m) - if err != nil { - return "", err - } - - return string(by), nil -} - -func ensureChildMap(m map[string]interface{}, name string) (map[string]interface{}, error) { - c, ok := m[name] - if !ok { - c = make(map[string]interface{}) - } - - cm, ok := c.(map[string]interface{}) - if !ok { - return nil, fmt.Errorf("child %q field is not a map: %v", name, reflect.TypeOf(c)) - } - - return cm, nil -} diff --git a/pkg/test/util/yml/apply_test.go b/pkg/test/util/yml/apply_test.go deleted file mode 100644 index 05c5a03de..000000000 --- a/pkg/test/util/yml/apply_test.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package yml - -import ( - "testing" -) - -func TestApplyNamespace(t *testing.T) { - cases := []struct { - name string - yml string - namespace string - expected string - }{ - { - "no namespace", - `metadata: - name: foo`, - "default", - `metadata: - name: foo - namespace: default`, - }, - { - "empty namespace", - `metadata: - name: foo - namespace: ""`, - "default", - `metadata: - name: foo - namespace: default`, - }, - { - "existing namespace", - `metadata: - name: foo - namespace: bar`, - "default", - `metadata: - name: foo - namespace: bar`, - }, - { - "multipart", - `apiVersion: v1 -metadata: - name: foo ---- -apiVersion: v1 -metadata: - name: bar -`, - "default", - `apiVersion: v1 -metadata: - name: foo - namespace: default ---- -apiVersion: v1 -metadata: - name: bar - namespace: default`, - }, - } - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - got, err := ApplyNamespace(tt.yml, tt.namespace) - if err != nil { - t.Fatal(err) - } - if got != tt.expected { - t.Fatalf("expected '%s', got '%s'", tt.expected, got) - } - }) - } -} diff --git a/pkg/test/util/yml/cache.go b/pkg/test/util/yml/cache.go deleted file mode 100644 index 3fa3c6494..000000000 --- a/pkg/test/util/yml/cache.go +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package yml - -import ( - "fmt" - "os" - "path" - "strings" - "sync" -) - -// Cache tracks the life-cycle of Yaml based resources. It stores single-part Yaml files on disk and updates the -// files as needed, as the resources change. -type Cache struct { - mu sync.Mutex - - discriminator int64 - resources map[CacheKey]*resourceState - dir string -} - -// CacheKey is a key representing a tracked Yaml based resource. -type CacheKey struct { - group string - kind string - namespace string - name string -} - -type resourceState struct { - part Part - file string -} - -// NewCache returns a new Cache instance -func NewCache(dir string) *Cache { - return &Cache{ - resources: make(map[CacheKey]*resourceState), - dir: dir, - } -} - -// Apply adds the given yamlText contents as part of a given context name. If there is an existing context -// with the given name, then a diffgram will be generated. -func (c *Cache) Apply(yamlText string) ([]CacheKey, error) { - c.mu.Lock() - defer c.mu.Unlock() - - parts, err := Parse(yamlText) - if err != nil { - return nil, err - } - - var result []CacheKey - newKeys := make(map[CacheKey]struct{}) - - for _, p := range parts { - key := toKey(p.Descriptor) - result = append(result, key) - newKeys[key] = struct{}{} - - state, found := c.resources[key] - if found { - if err = c.deleteFile(state.file); err != nil { - return nil, err - } - } else { - state = &resourceState{} - c.resources[key] = state - } - state.file = c.generateFileName(key) - state.part = p - - if err = c.writeFile(state.file, p.Contents); err != nil { - return nil, err - } - } - - return result, nil -} - -// Delete the resources from the given yamlText -func (c *Cache) Delete(yamlText string) error { - c.mu.Lock() - defer c.mu.Unlock() - - parts, err := Parse(yamlText) - if err != nil { - return err - } - - for _, p := range parts { - key := toKey(p.Descriptor) - - state, found := c.resources[key] - if found { - if err = c.deleteFile(state.file); err != nil { - return err - } - - delete(c.resources, key) - } - } - - return nil -} - -// AllKeys returns all resource keys in the tracker. -func (c *Cache) AllKeys() []CacheKey { - c.mu.Lock() - defer c.mu.Unlock() - - var result []CacheKey - - for k := range c.resources { - result = append(result, k) - } - - return result -} - -// Clear all tracked yaml content. -func (c *Cache) Clear() error { - c.mu.Lock() - defer c.mu.Unlock() - - for _, s := range c.resources { - if err := c.deleteFile(s.file); err != nil { - return err - } - } - - c.resources = make(map[CacheKey]*resourceState) - - return nil -} - -// GetFileFor returns the file that keeps the on-disk state for the given key. -func (c *Cache) GetFileFor(k CacheKey) string { - c.mu.Lock() - defer c.mu.Unlock() - - state, found := c.resources[k] - if !found { - return "" - } - return state.file -} - -func (c *Cache) writeFile(file string, contents string) error { - return os.WriteFile(file, []byte(contents), os.ModePerm) -} - -func (c *Cache) deleteFile(file string) error { - return os.Remove(file) -} - -func (c *Cache) generateFileName(key CacheKey) string { - c.discriminator++ - d := c.discriminator - - name := fmt.Sprintf("%s_%s_%s_%s-%d.yaml", - sanitize(key.group), sanitize(key.kind), sanitize(key.namespace), sanitize(key.name), d) - - return path.Join(c.dir, name) -} - -func sanitize(c string) string { - return strings.Replace( - strings.Replace(c, "/", "", -1), - ".", "_", -1) -} - -func toKey(d Descriptor) CacheKey { - return CacheKey{ - group: d.Group, - kind: d.Kind, - namespace: d.Metadata.Namespace, - name: d.Metadata.Name, - } -} diff --git a/pkg/test/util/yml/cache_test.go b/pkg/test/util/yml/cache_test.go deleted file mode 100644 index 09a156e04..000000000 --- a/pkg/test/util/yml/cache_test.go +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package yml - -import ( - "os" - "path" - "strings" - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -var ( - gateway = ` -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: some-ingress -spec: - servers: - - port: - number: 80 - name: http - protocol: http - hosts: - - "*.example.com" -` - - updatedGateway = ` -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: some-ingress -spec: - servers: - - port: - number: 8080 - name: https - protocol: https - hosts: - - "*.example.com" -` - - virtualService = ` -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: route-for-myapp -spec: - hosts: - - some.example.com - gateways: - - some-ingress - http: - - route: - - destination: - host: some.example.internal -` -) - -func TestCache_Apply_Basic(t *testing.T) { - g := NewWithT(t) - d := t.TempDir() - t.Logf("Test Dir: %q", d) - - c := NewCache(d) - - keys, err := c.Apply(gateway) - g.Expect(err).To(BeNil()) - g.Expect(keys).To(HaveLen(1)) - expected := CacheKey{ - group: "networking.istio.io", - kind: "Gateway", - namespace: "", - name: "some-ingress", - } - g.Expect(keys[0]).To(Equal(expected)) - key1 := keys[0] - - file := c.GetFileFor(keys[0]) - by, err := os.ReadFile(file) - g.Expect(err).To(BeNil()) - g.Expect(strings.TrimSpace(string(by))).To(Equal(strings.TrimSpace(gateway))) - - keys, err = c.Apply(virtualService) - g.Expect(err).To(BeNil()) - g.Expect(keys).To(HaveLen(1)) - expected = CacheKey{ - group: "networking.istio.io", - kind: "VirtualService", - namespace: "", - name: "route-for-myapp", - } - g.Expect(keys[0]).To(Equal(expected)) - key2 := keys[0] - - file = c.GetFileFor(keys[0]) - by, err = os.ReadFile(file) - g.Expect(err).To(BeNil()) - g.Expect(strings.TrimSpace(string(by))).To(Equal(strings.TrimSpace(virtualService))) - - keys = c.AllKeys() - g.Expect(keys).To(HaveLen(2)) - g.Expect(keys).To(ContainElement(key1)) - g.Expect(keys).To(ContainElement(key2)) - - items, err := os.ReadDir(d) - g.Expect(err).To(BeNil()) - g.Expect(items).To(HaveLen(2)) -} - -func TestCache_Apply_MultiPart(t *testing.T) { - g := NewWithT(t) - d := t.TempDir() - t.Logf("Test Dir: %q", d) - - c := NewCache(d) - - keys, err := c.Apply(JoinString(gateway, virtualService)) - g.Expect(err).To(BeNil()) - g.Expect(keys).To(HaveLen(2)) - expected := CacheKey{ - group: "networking.istio.io", - kind: "Gateway", - namespace: "", - name: "some-ingress", - } - g.Expect(keys[0]).To(Equal(expected)) - - file := c.GetFileFor(keys[0]) - by, err := os.ReadFile(file) - g.Expect(err).To(BeNil()) - g.Expect(strings.TrimSpace(string(by))).To(Equal(strings.TrimSpace(gateway))) - - expected = CacheKey{ - group: "networking.istio.io", - kind: "VirtualService", - namespace: "", - name: "route-for-myapp", - } - g.Expect(keys[1]).To(Equal(expected)) - - file = c.GetFileFor(keys[1]) - by, err = os.ReadFile(file) - g.Expect(err).To(BeNil()) - g.Expect(strings.TrimSpace(string(by))).To(Equal(strings.TrimSpace(virtualService))) - - applyKeys := keys - keys = c.AllKeys() - g.Expect(keys).To(HaveLen(2)) - g.Expect(keys).To(ContainElement(applyKeys[0])) - g.Expect(keys).To(ContainElement(applyKeys[1])) - - items, err := os.ReadDir(d) - g.Expect(err).To(BeNil()) - g.Expect(items).To(HaveLen(2)) -} - -func TestCache_Apply_Add_Update(t *testing.T) { - g := NewWithT(t) - d := t.TempDir() - t.Logf("Test Dir: %q", d) - - c := NewCache(d) - - keys, err := c.Apply(gateway) - g.Expect(err).To(BeNil()) - - file := c.GetFileFor(keys[0]) - by, err := os.ReadFile(file) - g.Expect(err).To(BeNil()) - g.Expect(strings.TrimSpace(string(by))).To(Equal(strings.TrimSpace(gateway))) - - keys, err = c.Apply(updatedGateway) - g.Expect(err).To(BeNil()) - file = c.GetFileFor(keys[0]) - by, err = os.ReadFile(file) - g.Expect(err).To(BeNil()) - g.Expect(strings.TrimSpace(string(by))).To(Equal(strings.TrimSpace(updatedGateway))) - - applyKeys := keys - keys = c.AllKeys() - g.Expect(keys).To(HaveLen(1)) - g.Expect(keys).To(ContainElement(applyKeys[0])) - - items, err := os.ReadDir(d) - g.Expect(err).To(BeNil()) - g.Expect(items).To(HaveLen(1)) -} - -func TestCache_Apply_SameContent(t *testing.T) { - g := NewWithT(t) - d := t.TempDir() - t.Logf("Test Dir: %q", d) - - c := NewCache(d) - - keys1, err := c.Apply(gateway) - g.Expect(err).To(BeNil()) - - keys2, err := c.Apply(gateway) - g.Expect(err).To(BeNil()) - - keys := c.AllKeys() - g.Expect(keys).To(HaveLen(1)) - g.Expect(keys1).To(HaveLen(1)) - g.Expect(keys2).To(HaveLen(1)) - g.Expect(keys).To(ContainElement(keys1[0])) - g.Expect(keys).To(ContainElement(keys2[0])) - - items, err := os.ReadDir(d) - g.Expect(err).To(BeNil()) - g.Expect(items).To(HaveLen(1)) -} - -func TestCache_Clear(t *testing.T) { - g := NewWithT(t) - d := t.TempDir() - t.Logf("Test Dir: %q", d) - - c := NewCache(d) - - _, err := c.Apply(gateway) - g.Expect(err).To(BeNil()) - - _, err = c.Apply(virtualService) - g.Expect(err).To(BeNil()) - - err = c.Clear() - g.Expect(err).To(BeNil()) - - keys := c.AllKeys() - g.Expect(keys).To(HaveLen(0)) - - items, err := os.ReadDir(d) - g.Expect(err).To(BeNil()) - g.Expect(items).To(HaveLen(0)) -} - -func TestCache_GetFileFor_Empty(t *testing.T) { - g := NewWithT(t) - d := t.TempDir() - t.Logf("Test Dir: %q", d) - - c := NewCache(d) - - f := c.GetFileFor(CacheKey{}) - g.Expect(f).To(BeEmpty()) -} - -func TestCache_Delete(t *testing.T) { - g := NewWithT(t) - d := t.TempDir() - t.Logf("Test Dir: %q", d) - - c := NewCache(d) - - _, err := c.Apply(gateway) - g.Expect(err).To(BeNil()) - - keys1, err := c.Apply(virtualService) - g.Expect(err).To(BeNil()) - - err = c.Delete(gateway) - g.Expect(err).To(BeNil()) - - keys := c.AllKeys() - g.Expect(keys).To(HaveLen(1)) - g.Expect(keys1).To(HaveLen(1)) - g.Expect(keys).To(ContainElement(keys1[0])) - - items, err := os.ReadDir(d) - g.Expect(err).To(BeNil()) - g.Expect(items).To(HaveLen(1)) - - by, err := os.ReadFile(path.Join(d, items[0].Name())) - g.Expect(err).To(BeNil()) - g.Expect(strings.TrimSpace(string(by))).To(Equal(strings.TrimSpace(virtualService))) -} - -func TestCache_Delete_Missing(t *testing.T) { - g := NewWithT(t) - d := t.TempDir() - t.Logf("Test Dir: %q", d) - - c := NewCache(d) - - _, err := c.Apply(gateway) - g.Expect(err).To(BeNil()) - - err = c.Delete(virtualService) - g.Expect(err).To(BeNil()) - - items, err := os.ReadDir(d) - g.Expect(err).To(BeNil()) - g.Expect(items).To(HaveLen(1)) - - by, err := os.ReadFile(path.Join(d, items[0].Name())) - g.Expect(err).To(BeNil()) - g.Expect(strings.TrimSpace(string(by))).To(Equal(strings.TrimSpace(gateway))) -} diff --git a/pkg/test/util/yml/file.go b/pkg/test/util/yml/file.go deleted file mode 100644 index c9757fbc5..000000000 --- a/pkg/test/util/yml/file.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package yml - -import ( - "fmt" - "os" - "strings" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test" -) - -type docType string - -const ( - namespacesAndCRDs docType = "namespaces_and_crds" - misc docType = "misc" -) - -// FileWriter write YAML content to files. -type FileWriter interface { - // WriteYAML writes the given YAML content to one or more YAML files. - WriteYAML(filenamePrefix string, contents ...string) ([]string, error) - - // WriteYAMLOrFail calls WriteYAML and fails the test if an error occurs. - WriteYAMLOrFail(t test.Failer, filenamePrefix string, contents ...string) []string -} - -type writerImpl struct { - workDir string -} - -// NewFileWriter creates a new FileWriter that stores files under workDir. -func NewFileWriter(workDir string) FileWriter { - return &writerImpl{ - workDir: workDir, - } -} - -// WriteYAML writes the given YAML content to one or more YAML files. -func (w *writerImpl) WriteYAML(filenamePrefix string, contents ...string) ([]string, error) { - out := make([]string, 0, len(contents)) - content := JoinString(contents...) - files, err := splitContentsToFiles(w.workDir, content, filenamePrefix) - if err != nil { - return nil, err - } - - if len(files) == 0 { - f, err := writeContentsToTempFile(w.workDir, content) - if err != nil { - return nil, err - } - files = append(files, f) - } - out = append(out, files...) - return out, nil -} - -// WriteYAMLOrFial calls WriteYAML and fails the test if an error occurs. -func (w *writerImpl) WriteYAMLOrFail(t test.Failer, filenamePrefix string, contents ...string) []string { - t.Helper() - out, err := w.WriteYAML(filenamePrefix, contents...) - if err != nil { - t.Fatal(err) - } - return out -} - -func writeContentsToTempFile(workDir, contents string) (filename string, err error) { - defer func() { - if err != nil && filename != "" { - _ = os.Remove(filename) - filename = "" - } - }() - - var f *os.File - f, err = os.CreateTemp(workDir, yamlToFilename(contents)+".*.yaml") - if err != nil { - return - } - filename = f.Name() - - _, err = f.WriteString(contents) - return -} - -func yamlToFilename(contents string) string { - spl := SplitYamlByKind(contents) - delete(spl, "") - types := []string{} - for k := range spl { - types = append(types, k) - } - switch len(types) { - case 0: - return "empty" - case 1: - m := GetMetadata(contents) - if len(m) == 0 { - return fmt.Sprintf("%s.%s", types[0], m[0].Name) - } - return types[0] - case 2, 3, 4: - return strings.Join(types, "-") - default: - return strings.Join(types[:4], "-") + "-more" - } -} - -func splitContentsToFiles(workDir, content, filenamePrefix string) ([]string, error) { - split := SplitYamlByKind(content) - namespacesAndCrds := &yamlDoc{ - docType: namespacesAndCRDs, - content: split["Namespace"], - } - misc := &yamlDoc{ - docType: misc, - content: split["CustomResourceDefinition"], - } - - // If all elements were put into a single doc just return an empty list, indicating that the original - // content should be used. - docs := []*yamlDoc{namespacesAndCrds, misc} - for _, doc := range docs { - if len(doc.content) == 0 { - return make([]string, 0), nil - } - } - - filesToApply := make([]string, 0, len(docs)) - for _, doc := range docs { - tfile, err := doc.toTempFile(workDir, filenamePrefix) - if err != nil { - return nil, err - } - filesToApply = append(filesToApply, tfile) - } - return filesToApply, nil -} - -type yamlDoc struct { - content string - docType docType -} - -func (d *yamlDoc) toTempFile(workDir, fileNamePrefix string) (string, error) { - f, err := os.CreateTemp(workDir, fmt.Sprintf("%s_%s.yaml", fileNamePrefix, d.docType)) - if err != nil { - return "", err - } - defer func() { _ = f.Close() }() - - name := f.Name() - - _, err = f.WriteString(d.content) - if err != nil { - return "", err - } - return name, nil -} diff --git a/pkg/test/util/yml/parse.go b/pkg/test/util/yml/parse.go deleted file mode 100644 index aa8de0dd2..000000000 --- a/pkg/test/util/yml/parse.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package yml - -import ( - "encoding/json" - "fmt" - "strings" -) - -import ( - "sigs.k8s.io/yaml" -) - -// Metadata metadata for a kubernetes resource. -type Metadata struct { - Name string `json:"name"` - Namespace string `json:"namespace"` -} - -// Descriptor a descriptor for a kubernetes resource. -type Descriptor struct { - Kind string `json:"kind"` - Group string `json:"group"` - APIVersion string `json:"apiVersion"` - Metadata Metadata `json:"metadata"` -} - -// Part is a single-part yaml source, along with its descriptor. -type Part struct { - Contents string - Descriptor Descriptor -} - -// Parse parses the given multi-part yaml text, and returns as Parts. -func Parse(yamlText string) ([]Part, error) { - splitContent := SplitString(yamlText) - parts := make([]Part, 0, len(splitContent)) - for _, part := range splitContent { - if len(part) > 0 { - descriptor, err := ParseDescriptor(part) - if err != nil { - return nil, err - } - - parts = append(parts, Part{ - Contents: part, - Descriptor: descriptor, - }) - } - } - return parts, nil -} - -// ParseDescriptor parses the given single-part yaml and generates the descriptor. -func ParseDescriptor(yamlText string) (Descriptor, error) { - d := Descriptor{} - jsonText, err := yaml.YAMLToJSON([]byte(yamlText)) - if err != nil { - return Descriptor{}, fmt.Errorf("failed converting YAML to JSON: %v", err) - } - - if err := json.Unmarshal(jsonText, &d); err != nil { - return Descriptor{}, fmt.Errorf("failed parsing descriptor: %v", err) - } - - parts := strings.Split(d.APIVersion, "/") - switch len(parts) { - case 1: - d.APIVersion = parts[0] - case 2: - d.Group = parts[0] - d.APIVersion = parts[1] - default: - return Descriptor{}, fmt.Errorf("unexpected apiGroup: %q", d.APIVersion) - } - - return d, nil -} diff --git a/pkg/test/util/yml/parts.go b/pkg/test/util/yml/parts.go deleted file mode 100644 index 046e95a37..000000000 --- a/pkg/test/util/yml/parts.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package yml - -import ( - "regexp" - "strings" -) - -import ( - kubeApiMeta "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/yaml" -) - -const ( - joinSeparator = "\n---\n" -) - -// Split where the '---' appears at the very beginning of a line. This will avoid -// accidentally splitting in cases where yaml resources contain nested yaml (which -// is indented). -var splitRegex = regexp.MustCompile(`(^|\n)---`) - -// SplitYamlByKind splits the given YAML into parts indexed by kind. -func SplitYamlByKind(content string) map[string]string { - cfgs := SplitString(content) - result := map[string]string{} - for _, cfg := range cfgs { - var typeMeta kubeApiMeta.TypeMeta - if e := yaml.Unmarshal([]byte(cfg), &typeMeta); e != nil { - // Ignore invalid parts. This most commonly happens when it's empty or contains only comments. - continue - } - result[typeMeta.Kind] = JoinString(result[typeMeta.Kind], cfg) - } - return result -} - -// SplitYamlByKind splits the given YAML into parts indexed by kind. -func GetMetadata(content string) []kubeApiMeta.ObjectMeta { - cfgs := SplitString(content) - result := []kubeApiMeta.ObjectMeta{} - for _, cfg := range cfgs { - var m kubeApiMeta.ObjectMeta - if e := yaml.Unmarshal([]byte(cfg), &m); e != nil { - // Ignore invalid parts. This most commonly happens when it's empty or contains only comments. - continue - } - result = append(result, m) - } - return result -} - -// SplitString splits the given yaml doc if it's multipart document. -func SplitString(yamlText string) []string { - out := make([]string, 0) - parts := splitRegex.Split(yamlText, -1) - for _, part := range parts { - part := strings.TrimSpace(part) - if len(part) > 0 { - out = append(out, part) - } - } - return out -} - -// JoinString joins the given yaml parts into a single multipart document. -func JoinString(parts ...string) string { - // Assume that each part is already a multi-document. Split and trim each part, - // if necessary. - toJoin := make([]string, 0, len(parts)) - for _, part := range parts { - toJoin = append(toJoin, SplitString(part)...) - } - - return strings.Join(toJoin, joinSeparator) -} diff --git a/pkg/test/util/yml/parts_test.go b/pkg/test/util/yml/parts_test.go deleted file mode 100644 index d8227b2a0..000000000 --- a/pkg/test/util/yml/parts_test.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package yml_test - -import ( - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/util/yml" -) - -func TestEmptyDoc(t *testing.T) { - g := NewWithT(t) - - yaml := ` -` - parts := yml.SplitString(yaml) - g.Expect(len(parts)).To(Equal(0)) - - yaml = yml.JoinString(parts...) - g.Expect(yaml).To(Equal("")) -} - -func TestSplitWithEmptyPart(t *testing.T) { - expected := []string{ - "a", - "b", - } - - cases := []struct { - name string - doc string - }{ - { - name: "beginningNoCR", - doc: `--- -a ---- -b -`, - }, - { - name: "beginningWithCR", - doc: ` ---- -a ---- -b -`, - }, - { - name: "middle", - doc: `a ---- ---- -b -`, - }, - { - name: "end", - doc: ` -a ---- -b ---- -`, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - parts := yml.SplitString(c.doc) - g := NewWithT(t) - g.Expect(parts).To(Equal(expected)) - }) - } -} - -func TestSplitWithNestedDocument(t *testing.T) { - doc := ` -b - b1 - --- - b2 -` - expected := []string{ - `b - b1 - --- - b2`, - } - - g := NewWithT(t) - parts := yml.SplitString(doc) - g.Expect(parts).To(Equal(expected)) -} - -func TestJoinRemovesEmptyParts(t *testing.T) { - parts := []string{ - `--- ---- ---- -`, - ` ---- -a ---- -`, - ` -b ---- -`, - } - - expected := `a ---- -b` - - g := NewWithT(t) - doc := yml.JoinString(parts...) - g.Expect(doc).To(Equal(expected)) -} diff --git a/pkg/testcerts/generate-certs.sh b/pkg/testcerts/generate-certs.sh deleted file mode 100755 index d8c1495f2..000000000 --- a/pkg/testcerts/generate-certs.sh +++ /dev/null @@ -1,109 +0,0 @@ -#!/bin/bash - -# Copyright 2018 Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http:#www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Generates the a CA cert, a server key/cert, client key/cert signed by -# the CA. -# -# reference: https://github.com/kubernetes/kubernetes/blob/master/plugin/pkg/admission/webhook/gencerts.sh - -set -e - -cat > client.conf < server.conf < $outfile << EOF -/* -Copyright Istio Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -EOF - -{ - echo "// This file was generated using openssl by the gencerts.sh script" - echo "// and holds raw certificates for the webhook tests." - echo "" - echo "package testcerts" -} >> $outfile - -for file in CACert ServerKey ServerCert ClientKey ClientCert; do - data=$(cat ${file}.pem) - { - echo "" - echo "// ${file} is a test cert for dynamic admission controller." - echo "var $file = []byte(\`$data\`)" - } >> $outfile -done - -# Clean up after we're done. -rm ./*.pem -rm ./*.csr -rm ./*.srl -rm ./*.conf diff --git a/pkg/testcerts/testcerts.go b/pkg/testcerts/testcerts.go deleted file mode 100644 index 56f128a62..000000000 --- a/pkg/testcerts/testcerts.go +++ /dev/null @@ -1,161 +0,0 @@ -/* -Copyright Istio Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// This file was generated using openssl by the gencerts.sh script -// and holds raw certificates for the webhook tests. - -package testcerts - -// CACert is a test cert for dynamic admission controller. -var CACert = []byte(`-----BEGIN CERTIFICATE----- -MIIC9DCCAdygAwIBAgIJAIFe3lWPaalKMA0GCSqGSIb3DQEBCwUAMA4xDDAKBgNV -BAMMA19jYTAgFw0xNzEyMjIxODA0MjRaGA8yMjkxMTAwNzE4MDQyNFowDjEMMAoG -A1UEAwwDX2NhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuBdxj+Hi -8h0TkId1f64TprLydwgzzLwXAs3wpmXz+BfnW1oMQPNyN7vojW6VzqJGGYLsc1OB -MgwObU/VeFNc6YUCmu6mfFJwoPfXMPnhmGuSwf/kjXomlejAYjxClU3UFVWQht54 -xNLjTi2M1ZOnwNbECOhXC3Tw3G8mCtfanMAO0UXM5yObbPa8yauUpJKkpoxWA7Ed -qiuUD9qRxluFPqqw/z86V8ikmvnyjQE9960j+8StlAbRs82ArtnrhRgkDO0Smtf7 -4QZsb/hA1KNMm73bOGS6+SVU+eH8FgVOzcTQYFRpRT3Mhi6dKZe9twIO8mpZK4wk -uygRxBM32Ag9QQIDAQABo1MwUTAdBgNVHQ4EFgQUc8tvoNNBHyIkoVV8XCXy63Ya -BEQwHwYDVR0jBBgwFoAUc8tvoNNBHyIkoVV8XCXy63YaBEQwDwYDVR0TAQH/BAUw -AwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAVmaUkkYESfcfgnuPeZ4sTNs2nk2Y+Xpd -lxkMJhChb8YQtlCe4uiLvVe7er1sXcBLNCm/+2K9AT71gnxBSeS5mEOzWmCPErhy -RmYtSxeRyXAaUWVYLs/zMlBQ0Iz4dpY+FVVbMjIurelVwHF0NBk3VtU5U3lHyKdZ -j4C2rMjvTxmkyIcR1uBEeVvuGU8R70nZ1yfo3vDwmNGMcLwW+4QK+WcfwfjLXhLs -5550arfEYdTzYFMxY60HJT/LvbGrjxY0PQUWWDbPiRfsdRjOFduAbM0/EVRda/Oo -Fg72WnHeojDUhqEz4UyFZbnRJ4x6leQhnrIcVjWX4FFFktiO9rqqfw== ------END CERTIFICATE-----`) - -// BadCert is a abd x509 cert. Copied from crypto/x509/x509_test.go:1628 -var BadCert = []byte(` ------BEGIN CERTIFICATE----- -MIIC1jCCAb6gAwIBAgICEjQwDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UEAxMdRW1w -dHkgbmFtZSBjb25zdHJhaW50cyBpc3N1ZXIwHhcNMTMwMjAxMDAwMDAwWhcNMjAw -NTMwMTA0ODM4WjAhMR8wHQYDVQQDExZFbXB0eSBuYW1lIGNvbnN0cmFpbnRzMIIB -IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwriElUIt3LCqmJObs+yDoWPD -F5IqgWk6moIobYjPfextZiYU6I3EfvAwoNxPDkN2WowcocUZMJbEeEq5ebBksFnx -f12gBxlIViIYwZAzu7aFvhDMyPKQI3C8CG0ZSC9ABZ1E3umdA3CEueNOmP/TChNq -Cl23+BG1Qb/PJkpAO+GfpWSVhTcV53Mf/cKvFHcjGNrxzdSoq9fyW7a6gfcGEQY0 -LVkmwFWUfJ0wT8kaeLr0E0tozkIfo01KNWNzv6NcYP80QOBRDlApWu9ODmEVJHPD -blx4jzTQ3JLa+4DvBNOjVUOp+mgRmjiW0rLdrxwOxIqIOwNjweMCp/hgxX/hTQID -AQABoxEwDzANBgNVHR4EBjAEoAChADANBgkqhkiG9w0BAQsFAAOCAQEAWG+/zUMH -QhP8uNCtgSHyim/vh7wminwAvWgMKxlkLBFns6nZeQqsOV1lABY7U0Zuoqa1Z5nb -6L+iJa4ElREJOi/erLc9uLwBdDCAR0hUTKD7a6i4ooS39DTle87cUnj0MW1CUa6H -v5SsvpYW+1XleYJk/axQOOTcy4Es53dvnZsjXH0EA/QHnn7UV+JmlE3rtVxcYp6M -LYPmRhTioROA/drghicRkiu9hxdPyxkYS16M5g3Zj30jdm+k/6C6PeNtN9YmOOga -nCOSyFYfGhqOANYzpmuV+oIedAsPpIbfIzN8njYUs1zio+1IoI4o8ddM9sCbtPU8 -o+WoY6IsCKXV/g== ------END CERTIFICATE-----`) - -// ServerKey is a test cert for dynamic admission controller. -var ServerKey = []byte(`-----BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAqZ9WyVOdHCM3ToIKnYeo8EvbuZKglfhjee7yZ5ZQ4atboHmb -n9q0GhkE780ezNuyMVllr/5tha1iUjSGdCPip/l3GdfLVt719Iw5gsCPxRKHMnoJ -O5PcejMxzXFa4vRpIKF/fbJ/ZYDKpIJtOGlaIR4M4ZQb3661SftEO7DnqElfOo7D -kQ+9n1mg1FPUCo0OUZydJ55ezzDYhrPD6ry12qu+FQnrJt8WGNghEv9qwZ8cz8Wv -Xcj9RUgossCID1n4vsWKL3nks9LHhGcVnIoCUdqWjxkk2YvmcOIt9Ny5TKOx8VoK -wGpToMuZXxgzbyFdnzDit7tX3SteR2QFONq5DwIDAQABAoIBABmW50HiMl6PVYWr -iqxvTeZKm3BolX9qhJ9dlAZaoAMblewkzHyWQvt48My4lj/zmPNm+DdP2/gBy0Z5 -lBsrWsNamEQ20P9fDZ4CFZ8LK+VgQTM1Q/VP/kAVPxsuUbbRhpacpp4w8pU+k9Oz -tYSAKE+8t9bEQFxDgCgUFxwmORyjDhyMxGO4hXq4vHR7SA8XGx4VRbr/KS4vbAR5 -uvSX54MzU8UvP/y3G/8sCFRHp3+rWbwjAKXWCDO1paWxLMRM5vOs3wX1iowiaoWb -5rMtwyJsrEFm7HXIyGvdZnEHug9U5uMs86pC7Z8XDtFp5MA1CqD4IO5o8kzcbYbs -nJgUafECgYEA22HW2NPYQ5M3W8pXzYbuabfCtU0yYJPmwX652LO9mH1lM4KNxWsP -tpj2jcAMMPovBp6JapClVHFpAYk71ZmlkU2tV3F+fDJoqf9m5rU8iBuhQaBgwZaU -UqqQJDTkH+NeE3veRq73gR03qHwUa8lo5JQsVnmkxYdcmArsMSMPCCcCgYEAxe9G -JYCuoRZN16Meu8CrjtioETj8AMk3i64OzY83WcTgPY3tZanP7JtaONJDrgCjcnX8 -rqDlq/EBN+fpQIUlL4ZUvi2XI5Z99TrimxtIcjTHaaUGe7zWU2ozAsYNLnkrxcMJ -MgCnns5WjVFMe2QdOaG+qpMfYfJpweCLufeusNkCgYEAmjGfX5EubPiZLUQACK4w -/k8xZFrY8LajtxaKK3zR4s8oBVdarAp+5dmHWcRFDVubF+zwKt11xu9bXcAGNTCk -BYfyMQbNXx/THsErozZ5UDUTV1wRBZ//qkbFvx0Jxjv50Hn8lfO+dJqDl0F23PeY -aSiYLUOcg1WLyDXNIxBALXsCgYBHkQw74xtBA1+B6GjkWfWt4IhkMcZsQlTjHDwd -9vp8asLpfrenWo7jbghhIyV1dKWkbSS/v01LrghSvneH7JxVYqyhVrqfE3rXgEMO -8f5vzMWNXS3K76xO3Mc06Yc6lnVNPAfHHJV+xfxlfE+7DafDfsgBxNBECfJTN21O -AFAZgQKBgQDRRC9Coel3Sm3jHwug3Pf1ziZg6BEwaNS6pSRqbcD9b66R298m/BHM -DYyKQ0uRkAvNVAT2WD87ZiK8IGgn+U7qWc+LlmvVVPJRiKbuxdjtGeH+2PFc9uFD -5QyMqlncVzkf3N4rNB6p83Yy5gEJLlfVXFKngu0MiH2J2NM7gqlonw== ------END RSA PRIVATE KEY-----`) - -// ServerCert is a test cert for dynamic admission controller. -var ServerCert = []byte(`-----BEGIN CERTIFICATE----- -MIIDATCCAemgAwIBAgIJAIaY2+s9cKkgMA0GCSqGSIb3DQEBCwUAMA4xDDAKBgNV -BAMMA19jYTAgFw0xNzEyMjIxODA0MjRaGA8yMjkxMTAwNzE4MDQyNFowEjEQMA4G -A1UEAwwHX3NlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKmf -VslTnRwjN06CCp2HqPBL27mSoJX4Y3nu8meWUOGrW6B5m5/atBoZBO/NHszbsjFZ -Za/+bYWtYlI0hnQj4qf5dxnXy1be9fSMOYLAj8UShzJ6CTuT3HozMc1xWuL0aSCh -f32yf2WAyqSCbThpWiEeDOGUG9+utUn7RDuw56hJXzqOw5EPvZ9ZoNRT1AqNDlGc -nSeeXs8w2Iazw+q8tdqrvhUJ6ybfFhjYIRL/asGfHM/Fr13I/UVIKLLAiA9Z+L7F -ii955LPSx4RnFZyKAlHalo8ZJNmL5nDiLfTcuUyjsfFaCsBqU6DLmV8YM28hXZ8w -4re7V90rXkdkBTjauQ8CAwEAAaNcMFowCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAw -HQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMCEGA1UdEQQaMBiHBH8AAAGH -EAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQELBQADggEBAD7aRhLfs4HlqE3E -AaV3WSGFq/m97PsoqQpKTYJ8kAXPE7cd0Pgh1A6Vm/NXIMNw3vSpLDOLy0Y6Ggbb -78bw3YeZuRkY5fEXmFf70248oxv+2MbIuJ0n1cU0hHOtHw6+BCC1q4yR5/iJ7YEX -jRbUacqhn9IxH9O8Bs7ntv6NaoHjUfskEiGyl1UZSAsFsd/Cp2Qu4Jmm0DHsvd+S -9oIO+EILiCvnGwcYfH4UFmNCx6S7JVOuNaFdgHUuo5dNDMj0hlt9krk3XfcCQ/YQ -K9pLL7WMDV5tvu437+UWUn0yZv6LkxcE33smqcHumrhwRtEfqAUbM1FHVteFZDCp -p3vBDAM= ------END CERTIFICATE-----`) - -// RotatedKey is a test cert for dynamic admission controller. -var RotatedKey = []byte(`-----BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEA3Tr24CaBegyfkdDGWckqMHEWvpJBThjXlMz/FKcg1bgq57OD -oNHXN4dcyPCHWWEY3Eo3YG1es4pqTkvzK0+1JoY6/K88Lu1ePj5PeSFuWfPWi1BW -9oyWJW+AAzqqGkZmSo4z26N+E7N8ht5bTBMNVD3jqz9+MaqCTVmQ6dAgdFKH07wd -XWh6kKoh2g9bgBKB+qWrezmVRb31i93sM1pJos35cUmIbWgiSQYuSXEInitejcGZ -cBjqRy61SiZB7nbmMoew0G0aGXe20Wx+QiyJjbt9XNUm0IvjAJ1SSiPqfFQ4F+tx -K4q3xAwp1smyiMv57RNC2ny8YMntZYgQDDkhBQIDAQABAoIBAQDZHK396yw0WEEd -vFN8+CRUaBfXLPe0KkMgAFLxtNdPhy9sNsueP3HESC7x8MQUHmtkfd1837kJ4HRV -pMnfnpj8Vs17AIrCzycnVMVv7jQ7SUcrb8v4qJ4N3TA3exJHOQHYd1hDXF81/Hbg -cUYOEcCKBTby8BvrqBe6y4ShQiUnoaeeM5j9x32+QB652/9PMuZJ9xfwyoEBjoVA -cccp+u3oBX864ztaG9Gn0wbgRVeafsPfuAOUmShykohV1mVJddiA0wayxGi0TmoK -dwrltdToI7BmpmmTLc59O44JFGwO67aJQHsrHBjEnpWlxFDwbfZuf93FgdFUFFjr -tVx2dPF9AoGBAPkIaUYxMSW78MD9862eJlGS6F/SBfPLTve3l9M+UFnEsWapNz+V -anlupaBtlfRJxLDzjheMVZTv/TaFIrrMdN/xc/NoYj6lw5RV8PEfKPB0FjSAqSEl -iVOA5q4kuI1xEeV7xLE4uJUF3wdoHz9cSmjrXDVZXq/KsaInABlAl1xjAoGBAONr -bgFUlQ+fbsGNjeefrIXBSU93cDo5tkd4+lefrmUVTwCo5V/AvMMQi88gV/sz6qCJ -gR0hXOqdQyn++cuqfMTDj4lediDGFEPho+n4ToIvkA020NQ05cKxcmn/6Ei+P9pk -v+zoT9RiVnkBje2n/KU2d/PEL9Nl4gvvAgPLt8V3AoGAZ6JZdQ15n3Nj0FyecKz0 -01Oogl+7fGYqGap8cztmYsUY8lkPFdXPNnOWV3njQoMEaIMiqagL4Wwx2uNyvXvi -U2N+1lelMt720h8loqJN/irBJt44BARD7s0gsm2zo6DfSrnD8+Bf6BxGYSWyg0Kb -8KepesYTQmK+o3VJdDjOBHMCgYAIxbwYkQqu75d2H9+5b49YGXyadCEAHfnKCACg -IKi5fXjurZUrfGPLonfCJZ0/M2F5j9RLK15KLobIt+0qzgjCDkkbI2mrGfjuJWYN -QGbG3s7Ps62age/a8r1XGWf8ZlpQMlK08MEjkCeFw2mWIUS9mrxFyuuNXAC8NRv+ -yXztQQKBgQDWTFFQdeYfuiKHrNmgOmLVuk1WhAaDgsdK8RrnNZgJX9bd7n7bm7No -GheN946AYsFge4DX7o0UXXJ3h5hTFn/hSWASI54cO6WyWNEiaP5HRlZqK7Jfej7L -mz+dlU3j/BY19RLmYeg4jFV4W66CnkDqpneOJs5WdmFFoWnHn7gRBw== ------END RSA PRIVATE KEY-----`) - -// RotatedCert is a test cert for dynamic admission controller. -var RotatedCert = []byte(`-----BEGIN CERTIFICATE----- -MIIDATCCAemgAwIBAgIJAJwGb32Zn8sDMA0GCSqGSIb3DQEBCwUAMA4xDDAKBgNV -BAMMA19jYTAgFw0xODAzMTYxNzI0NDJaGA8yMjkxMTIzMDE3MjQ0MlowEjEQMA4G -A1UEAwwHX3NlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN06 -9uAmgXoMn5HQxlnJKjBxFr6SQU4Y15TM/xSnINW4Kuezg6DR1zeHXMjwh1lhGNxK -N2BtXrOKak5L8ytPtSaGOvyvPC7tXj4+T3khblnz1otQVvaMliVvgAM6qhpGZkqO -M9ujfhOzfIbeW0wTDVQ946s/fjGqgk1ZkOnQIHRSh9O8HV1oepCqIdoPW4ASgfql -q3s5lUW99Yvd7DNaSaLN+XFJiG1oIkkGLklxCJ4rXo3BmXAY6kcutUomQe525jKH -sNBtGhl3ttFsfkIsiY27fVzVJtCL4wCdUkoj6nxUOBfrcSuKt8QMKdbJsojL+e0T -Qtp8vGDJ7WWIEAw5IQUCAwEAAaNcMFowCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAw -HQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMCEGA1UdEQQaMBiHBH8AAAGH -EAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQELBQADggEBACbBlWo/pY/OIJaW -RwkfSRVzEIWpHt5OF6p93xfyy4/zVwwhH1AQB7Euji8vOaVNOpMfGYLNH3KIRReC -CIvGEH4yZDbpiH2cOshqMCuV1CMRUTdl4mq6M0PtGm6b8OG3uIFTLIR973LBWOl5 -wCR1yrefT1NHuIScGaBXUGAV4JAx37pfg84hDD73T2j1TDD3Lrmsb9WCP+L26TG6 -ICN61cIhgz8wChQpF8/fFAI5Fjbjrz5C1Xw/EUHLf/TTn/7Yfp2BHsGm126Et+k+ -+MLBzBfrHKwPaGqDvNHUDrI6c3GI0Qp7jW93FbL5ul8JQ+AowoMF2dIEbN9qQEVP -ZOQ5UvU= ------END CERTIFICATE-----`) diff --git a/pixiu/pkg/tracing/api.go b/pkg/tracing/api.go similarity index 100% rename from pixiu/pkg/tracing/api.go rename to pkg/tracing/api.go diff --git a/pixiu/pkg/tracing/driver.go b/pkg/tracing/driver.go similarity index 94% rename from pixiu/pkg/tracing/driver.go rename to pkg/tracing/driver.go index 5705182f2..83263bfbd 100644 --- a/pixiu/pkg/tracing/driver.go +++ b/pkg/tracing/driver.go @@ -33,10 +33,10 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/tracing/jaeger" - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/tracing/otlp" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/tracing/jaeger" + "github.com/apache/dubbo-go-pixiu/pkg/tracing/otlp" ) // Different Listeners listen to different protocol requests, and create the corresponding tracer diff --git a/pixiu/pkg/tracing/http.go b/pkg/tracing/http.go similarity index 100% rename from pixiu/pkg/tracing/http.go rename to pkg/tracing/http.go diff --git a/pixiu/pkg/tracing/jaeger/jaeger.go b/pkg/tracing/jaeger/jaeger.go similarity index 96% rename from pixiu/pkg/tracing/jaeger/jaeger.go rename to pkg/tracing/jaeger/jaeger.go index 9d4424226..171b94625 100644 --- a/pixiu/pkg/tracing/jaeger/jaeger.go +++ b/pkg/tracing/jaeger/jaeger.go @@ -25,7 +25,7 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) type jaegerConfig struct { diff --git a/pixiu/pkg/tracing/otlp/otlp.go b/pkg/tracing/otlp/otlp.go similarity index 95% rename from pixiu/pkg/tracing/otlp/otlp.go rename to pkg/tracing/otlp/otlp.go index d952fb6e8..31d61771d 100644 --- a/pixiu/pkg/tracing/otlp/otlp.go +++ b/pkg/tracing/otlp/otlp.go @@ -28,7 +28,7 @@ import ( ) import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) func NewOTLPExporter(ctx context.Context, cfg *model.TracerConfig) (sdktrace.SpanExporter, error) { diff --git a/pixiu/pkg/tracing/trace_manager.go b/pkg/tracing/trace_manager.go similarity index 96% rename from pixiu/pkg/tracing/trace_manager.go rename to pkg/tracing/trace_manager.go index fa37e0012..d83a91dca 100644 --- a/pixiu/pkg/tracing/trace_manager.go +++ b/pkg/tracing/trace_manager.go @@ -18,7 +18,7 @@ package tracing import ( - "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" + "github.com/apache/dubbo-go-pixiu/pkg/model" ) // TraceDriverManager Driver maintains all tracers and the provider. diff --git a/pkg/uds/listener.go b/pkg/uds/listener.go deleted file mode 100644 index fad85a24e..000000000 --- a/pkg/uds/listener.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package uds - -import ( - "fmt" - "net" - "os" - "path/filepath" -) - -import ( - "istio.io/pkg/log" -) - -func NewListener(path string) (net.Listener, error) { - // Remove unix socket before use. - if err := os.Remove(path); err != nil && !os.IsNotExist(err) { - // Anything other than "file not found" is an error. - return nil, fmt.Errorf("failed to remove unix://%s: %v", path, err) - } - - // Attempt to create the folder in case it doesn't exist - if err := os.MkdirAll(filepath.Dir(path), 0o750); err != nil { - // If we cannot create it, just warn here - we will fail later if there is a real error - log.Warnf("Failed to create directory for %v: %v", path, err) - } - - var err error - listener, err := net.Listen("unix", path) - if err != nil { - return nil, fmt.Errorf("failed to listen on unix socket %q: %v", path, err) - } - - // Update file permission so that istio-proxy has permission to access it. - if _, err := os.Stat(path); err != nil { - return nil, fmt.Errorf("uds file %q doesn't exist", path) - } - if err := os.Chmod(path, 0o666); err != nil { - return nil, fmt.Errorf("failed to update %q permission", path) - } - - return listener, nil -} diff --git a/pkg/uds/listener_test.go b/pkg/uds/listener_test.go deleted file mode 100644 index a5e963fd7..000000000 --- a/pkg/uds/listener_test.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package uds - -import ( - "context" - "net" - "testing" -) - -import ( - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" -) - -func TestUdsListener(t *testing.T) { - l, err := NewListener("./etc/istio/proxy/test") - if err != nil { - t.Fatalf("unexpected error %v", err) - } - defer l.Close() - conn, err := connect("./etc/istio/proxy/test") - if err != nil { - t.Fatalf("failed to connect %v", err) - } - conn.Close() -} - -func connect(socket string) (*grpc.ClientConn, error) { - var opts []grpc.DialOption - - opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) { - var d net.Dialer - return d.DialContext(ctx, "unix", socket) - })) - - conn, err := grpc.Dial(socket, opts...) - if err != nil { - return nil, err - } - - return conn, nil -} diff --git a/pkg/url/url.go b/pkg/url/url.go deleted file mode 100644 index 3d37bf9e7..000000000 --- a/pkg/url/url.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package url - -import ( - "fmt" -) - -import ( - "github.com/apache/dubbo-go-pixiu/operator/version" -) - -var ( - v = version.OperatorBinaryVersion - baseVersion = v.MinorVersion.String() - patchVersion = v.PatchVersion.String() - - // ReleaseTar is a URL to download latest istio version from Github release - ReleaseTar = `https://github.com/istio/istio/releases/download/` + patchVersion + `/istio-` + patchVersion + `-linux-amd64.tar.gz` -) - -// istio.io related URLs -var ( - // BaseURL for istio.io - BaseURL = "https://istio.io/" - - // DocsVersion is a documentation version for istio.io - // This will build version as v1.6, v1.7, v1.8 - DocsVersion = fmt.Sprintf("%s%s", "v", baseVersion) - - // DocsURL is a base docs URL for istio.io - DocsURL = fmt.Sprintf("%s%s%s", BaseURL, DocsVersion, "/docs/") - - // ##################################### - // Setup related URLs for istio.io - // ##################################### - - // SetupURL is a base URL for setup related docs - SetupURL = fmt.Sprintf("%s%s", DocsURL, "setup/") - - // SidecarInjection should generate - // https://istio.io/v1.7/docs/setup/additional-setup/sidecar-injection/#automatic-sidecar-injection - SidecarInjection = fmt.Sprintf("%s%s", SetupURL, "additional-setup/sidecar-injection/#automatic-sidecar-injection") - - // SidecarDeployingApp should generate - // https://istio.io/v1.7/docs/setup/additional-setup/sidecar-injection/#deploying-an-app - SidecarDeployingApp = fmt.Sprintf("%s%s", SetupURL, "additional-setup/sidecar-injection/#deploying-an-app") - - // ##################################### - // Tasks related URLs for istio.io - // ##################################### - - // TasksURL is a base URL for tasks related docs - TasksURL = fmt.Sprintf("%s%s", DocsURL, "tasks/") - - // ##################################### - // Examples related URLs for istio.io - // ##################################### - - // ExamplesURL is a base URL for examples related docs - ExamplesURL = fmt.Sprintf("%s%s", DocsURL, "examples/") - - // ##################################### - // Operations related URLs for istio.io - // ##################################### - - // OpsURL is a base URL for operations related docs - OpsURL = fmt.Sprintf("%s%s", DocsURL, "ops/") - - // DeploymentRequirements should generate - // https://istio.io/v1.7/docs/ops/deployment/requirements/ - DeploymentRequirements = fmt.Sprintf("%s%s", OpsURL, "deployment/requirements/") - - // ConfigureSAToken should generate - // https://istio.io/v1.7/docs/ops/best-practices/security/#configure-third-party-service-account-tokens - ConfigureSAToken = fmt.Sprintf("%s%s", OpsURL, "best-practices/security/#configure-third-party-service-account-tokens") - - // ##################################### - // Reference related URLs for istio.io - // ##################################### - - // ReferenceURL is a base URL for reference related docs - ReferenceURL = fmt.Sprintf("%s%s", DocsURL, "reference/") - - // IstioOperatorSpec should generate - // https://istio.io/v1.7/docs/reference/config/istio.operator.v1alpha1/#IstioOperatorSpec - IstioOperatorSpec = fmt.Sprintf("%s%s", ReferenceURL, "config/istio.operator.v1alpha1/#IstioOperatorSpec") - - // ConfigAnalysis should generate - // https://istio.io/v1.7/docs/reference/config/analysis - ConfigAnalysis = fmt.Sprintf("%s%s", ReferenceURL, "config/analysis") -) - -// Kubernetes related URLs -var ( - - // K8TLSBootstrapping is a link for Kubelet TLS Bootstrapping - K8TLSBootstrapping = "https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping" -) diff --git a/pkg/url/url_test.go b/pkg/url/url_test.go deleted file mode 100644 index 87cb3904f..000000000 --- a/pkg/url/url_test.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package url - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -func TestURL(t *testing.T) { - assert.Equal(t, ReleaseTar, - `https://github.com/istio/istio/releases/download/`+patchVersion+`/istio-`+patchVersion+`-linux-amd64.tar.gz`, - "base url should be equal") - - assert.Equal(t, BaseURL, "https://istio.io/", "base url should be equal") - assert.Equal(t, DocsURL, "https://istio.io/"+DocsVersion+"/docs/", "docs url should be equal") - - assert.Equal(t, SetupURL, "https://istio.io/"+DocsVersion+"/docs/setup/", "setup url should be equal") - assert.Equal(t, SidecarInjection, - "https://istio.io/"+DocsVersion+"/docs/setup/additional-setup/sidecar-injection/#automatic-sidecar-injection", - "SidecarInjection url should be equal") - assert.Equal(t, SidecarDeployingApp, - "https://istio.io/"+DocsVersion+"/docs/setup/additional-setup/sidecar-injection/#deploying-an-app", - "SidecarDeployingApp url should be equal") - - assert.Equal(t, TasksURL, "https://istio.io/"+DocsVersion+"/docs/tasks/", "tasks url should be equal") - assert.Equal(t, ExamplesURL, "https://istio.io/"+DocsVersion+"/docs/examples/", "examples url should be equal") - - assert.Equal(t, OpsURL, "https://istio.io/"+DocsVersion+"/docs/ops/", "ops url should be equal") - assert.Equal(t, DeploymentRequirements, - "https://istio.io/"+DocsVersion+"/docs/ops/deployment/requirements/", - "DeploymentRequirements url should be equal") - assert.Equal(t, ConfigureSAToken, - "https://istio.io/"+DocsVersion+"/docs/ops/best-practices/security/#configure-third-party-service-account-tokens", - "ConfigureSAToken url should be equal") - - assert.Equal(t, ReferenceURL, "https://istio.io/"+DocsVersion+"/docs/reference/", "reference url should be equal") - assert.Equal(t, IstioOperatorSpec, - "https://istio.io/"+DocsVersion+"/docs/reference/config/istio.operator.v1alpha1/#IstioOperatorSpec", - "IstioOperatorSpec url should be equal") - assert.Equal(t, ConfigAnalysis, - "https://istio.io/"+DocsVersion+"/docs/reference/config/analysis", - "ConfigAnalysis url should be equal") - - assert.Equal(t, K8TLSBootstrapping, - "https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping", - "K8TLSBootstrapping url should be equal") -} diff --git a/pkg/util/gogoprotomarshal/protomarshal.go b/pkg/util/gogoprotomarshal/protomarshal.go deleted file mode 100644 index 61cb673d5..000000000 --- a/pkg/util/gogoprotomarshal/protomarshal.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gogoprotomarshal - -import ( - "strings" -) - -import ( - "github.com/gogo/protobuf/jsonpb" - "github.com/gogo/protobuf/proto" - "istio.io/pkg/log" -) - -// ApplyJSON unmarshals a JSON string into a proto message. Unknown fields are allowed -func ApplyJSON(js string, pb proto.Message) error { - reader := strings.NewReader(js) - m := jsonpb.Unmarshaler{} - if err := m.Unmarshal(reader, pb); err != nil { - log.Debugf("Failed to decode proto: %q. Trying decode with AllowUnknownFields=true", err) - m.AllowUnknownFields = true - reader.Reset(js) - return m.Unmarshal(reader, pb) - } - return nil -} - -// ApplyJSONStrict unmarshals a JSON string into a proto message. -func ApplyJSONStrict(js string, pb proto.Message) error { - reader := strings.NewReader(js) - m := jsonpb.Unmarshaler{} - return m.Unmarshal(reader, pb) -} diff --git a/pkg/util/identifier/util.go b/pkg/util/identifier/util.go deleted file mode 100644 index 7d30555c5..000000000 --- a/pkg/util/identifier/util.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package identifier - -const Undefined = "" - -func IsSameOrEmpty(a, b string) bool { - return a == Undefined || b == Undefined || a == b -} diff --git a/pkg/util/istiomultierror/util.go b/pkg/util/istiomultierror/util.go deleted file mode 100644 index 0438b772d..000000000 --- a/pkg/util/istiomultierror/util.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istiomultierror - -import ( - "fmt" - "strings" -) - -import ( - "github.com/hashicorp/go-multierror" -) - -// MultiErrorFormat provides a format for multierrors. This matches the default format, but if there -// is only one error we will not expand to multiple lines. -func MultiErrorFormat() multierror.ErrorFormatFunc { - return func(es []error) string { - if len(es) == 1 { - return es[0].Error() - } - - points := make([]string, len(es)) - for i, err := range es { - points[i] = fmt.Sprintf("* %s", err) - } - - return fmt.Sprintf( - "%d errors occurred:\n\t%s\n\n", - len(es), strings.Join(points, "\n\t")) - } -} - -func New() *multierror.Error { - return &multierror.Error{ - ErrorFormat: MultiErrorFormat(), - } -} diff --git a/pkg/util/protomarshal/protomarshal.go b/pkg/util/protomarshal/protomarshal.go deleted file mode 100644 index 12e0871d0..000000000 --- a/pkg/util/protomarshal/protomarshal.go +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package protomarshal provides operations to marshal and unmarshal protobuf objects. -// Unlike the rest of this repo, which uses the new google.golang.org/protobuf API, this package -// explicitly uses the legacy jsonpb package. This is due to a number of compatibility concerns with the new API: -// * https://github.com/golang/protobuf/issues/1374 -// * https://github.com/golang/protobuf/issues/1373 -package protomarshal - -import ( - "bytes" - "encoding/json" - "errors" - "strings" -) - -import ( - "github.com/golang/protobuf/jsonpb" // nolint: staticcheck - legacyproto "github.com/golang/protobuf/proto" // nolint: staticcheck - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" - "istio.io/pkg/log" - "sigs.k8s.io/yaml" -) - -var ( - unmarshaler = jsonpb.Unmarshaler{AllowUnknownFields: true} - strictUnmarshaler = jsonpb.Unmarshaler{} -) - -func Unmarshal(b []byte, m proto.Message) error { - return strictUnmarshaler.Unmarshal(bytes.NewReader(b), legacyproto.MessageV1(m)) -} - -func UnmarshalAllowUnknown(b []byte, m proto.Message) error { - return unmarshaler.Unmarshal(bytes.NewReader(b), legacyproto.MessageV1(m)) -} - -// ToJSON marshals a proto to canonical JSON -func ToJSON(msg proto.Message) (string, error) { - return ToJSONWithIndent(msg, "") -} - -// Marshal marshals a proto to canonical JSON -func Marshal(msg proto.Message) ([]byte, error) { - res, err := ToJSONWithIndent(msg, "") - if err != nil { - return nil, err - } - return []byte(res), err -} - -// MarshalIndent marshals a proto to canonical JSON with indentation -func MarshalIndent(msg proto.Message, indent string) ([]byte, error) { - res, err := ToJSONWithIndent(msg, indent) - if err != nil { - return nil, err - } - return []byte(res), err -} - -// MarshalProtoNames marshals a proto to canonical JSON original protobuf names -func MarshalProtoNames(msg proto.Message) ([]byte, error) { - if msg == nil { - return nil, errors.New("unexpected nil message") - } - - // Marshal from proto to json bytes - m := jsonpb.Marshaler{OrigName: true} - buf := &bytes.Buffer{} - err := m.Marshal(buf, legacyproto.MessageV1(msg)) - if err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -// ToJSONWithIndent marshals a proto to canonical JSON with pretty printed string -func ToJSONWithIndent(msg proto.Message, indent string) (string, error) { - if msg == nil { - return "", errors.New("unexpected nil message") - } - - // Marshal from proto to json bytes - m := jsonpb.Marshaler{Indent: indent} - return m.MarshalToString(legacyproto.MessageV1(msg)) -} - -// ToYAML marshals a proto to canonical YAML -func ToYAML(msg proto.Message) (string, error) { - js, err := ToJSON(msg) - if err != nil { - return "", err - } - yml, err := yaml.JSONToYAML([]byte(js)) - return string(yml), err -} - -// ToJSONMap converts a proto message to a generic map using canonical JSON encoding -// JSON encoding is specified here: https://developers.google.com/protocol-buffers/docs/proto3#json -func ToJSONMap(msg proto.Message) (map[string]interface{}, error) { - js, err := ToJSON(msg) - if err != nil { - return nil, err - } - - // Unmarshal from json bytes to go map - var data map[string]interface{} - err = json.Unmarshal([]byte(js), &data) - if err != nil { - return nil, err - } - - return data, nil -} - -// ApplyJSON unmarshals a JSON string into a proto message. -func ApplyJSON(js string, pb proto.Message) error { - reader := strings.NewReader(js) - m := jsonpb.Unmarshaler{} - if err := m.Unmarshal(reader, legacyproto.MessageV1(pb)); err != nil { - log.Debugf("Failed to decode proto: %q. Trying decode with AllowUnknownFields=true", err) - m.AllowUnknownFields = true - reader.Reset(js) - return m.Unmarshal(reader, legacyproto.MessageV1(pb)) - } - return nil -} - -// ApplyJSONStrict unmarshals a JSON string into a proto message. -func ApplyJSONStrict(js string, pb proto.Message) error { - reader := strings.NewReader(js) - m := jsonpb.Unmarshaler{} - return m.Unmarshal(reader, legacyproto.MessageV1(pb)) -} - -// ApplyYAML unmarshals a YAML string into a proto message. -// Unknown fields are allowed. -func ApplyYAML(yml string, pb proto.Message) error { - js, err := yaml.YAMLToJSON([]byte(yml)) - if err != nil { - return err - } - return ApplyJSON(string(js), pb) -} - -// ApplyYAMLStrict unmarshals a YAML string into a proto message. -// Unknown fields are not allowed. -func ApplyYAMLStrict(yml string, pb proto.Message) error { - js, err := yaml.YAMLToJSON([]byte(yml)) - if err != nil { - return err - } - return ApplyJSONStrict(string(js), pb) -} - -func ShallowCopy(dst, src proto.Message) { - dm := dst.ProtoReflect() - sm := src.ProtoReflect() - if dm.Type() != sm.Type() { - panic("mismatching type") - } - proto.Reset(dst) - sm.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { - dm.Set(fd, v) - return true - }) -} diff --git a/pkg/util/sets/string.go b/pkg/util/sets/string.go deleted file mode 100644 index 50e4dbea0..000000000 --- a/pkg/util/sets/string.go +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sets - -import ( - "sort" -) - -type Set map[string]struct{} - -// NewWithLength returns an empty Set with the given capacity. -// It's only a hint, not a limitation. -func NewWithLength(l int) Set { - return make(Set, l) -} - -// New creates a new Set with the given items. -func New(items ...string) Set { - s := NewWithLength(len(items)) - return s.InsertAll(items...) -} - -// Insert a single item to this Set. -func (s Set) Insert(item string) Set { - s[item] = struct{}{} - return s -} - -// InsertAll adds the items to this Set. -func (s Set) InsertAll(items ...string) Set { - for _, item := range items { - s[item] = struct{}{} - } - return s -} - -// Delete removes an item from the set. -func (s Set) Delete(item string) Set { - delete(s, item) - return s -} - -// Delete removes items from the set. -func (s Set) DeleteAll(items ...string) Set { - for _, item := range items { - delete(s, item) - } - return s -} - -// Merge a set of objects that are in s2 into s -// For example: -// s = {a1, a2, a3} -// s2 = {a3, a4, a5} -// s.Merge(s2) = {a1, a2, a3, a4, a5} -func (s Set) Merge(s2 Set) Set { - for item := range s2 { - s[item] = struct{}{} - } - - return s -} - -// Copy this set. -func (s Set) Copy() Set { - result := New() - for key := range s { - result.Insert(key) - } - return result -} - -// Union returns a set of objects that are in s or s2 -// For example: -// s = {a1, a2, a3} -// s2 = {a1, a2, a4, a5} -// s.Union(s2) = s2.Union(s) = {a1, a2, a3, a4, a5} -func (s Set) Union(s2 Set) Set { - result := s.Copy() - for key := range s2 { - result.Insert(key) - } - return result -} - -// Difference returns a set of objects that are not in s2 -// For example: -// s = {a1, a2, a3} -// s2 = {a1, a2, a4, a5} -// s.Difference(s2) = {a3} -// s2.Difference(s) = {a4, a5} -func (s Set) Difference(s2 Set) Set { - result := New() - for key := range s { - if !s2.Contains(key) { - result.Insert(key) - } - } - return result -} - -// Intersection returns a set of objects that are common between s and s2 -// For example: -// s = {a1, a2, a3} -// s2 = {a1, a2, a4, a5} -// s.Intersection(s2) = {a1, a2} -func (s Set) Intersection(s2 Set) Set { - result := New() - for key := range s { - if s2.Contains(key) { - result.Insert(key) - } - } - return result -} - -// SupersetOf returns true if s contains all elements of s2 -// For example: -// s = {a1, a2, a3} -// s2 = {a1, a2, a3, a4, a5} -// s.SupersetOf(s2) = false -// s2.SupersetOf(s) = true -func (s Set) SupersetOf(s2 Set) bool { - return s2.Difference(s).IsEmpty() -} - -// UnsortedList returns the slice with contents in random order. -func (s Set) UnsortedList() []string { - res := make([]string, 0, s.Len()) - for key := range s { - res = append(res, key) - } - return res -} - -// SortedList returns the slice with contents sorted. -func (s Set) SortedList() []string { - res := s.UnsortedList() - sort.Strings(res) - return res -} - -// Contains returns whether the given item is in the set. -func (s Set) Contains(item string) bool { - _, ok := s[item] - return ok -} - -// Equals checks whether the given set is equal to the current set. -func (s Set) Equals(other Set) bool { - if s.Len() != other.Len() { - return false - } - - for key := range s { - if !other.Contains(key) { - return false - } - } - - return true -} - -// Len returns the number of elements in this Set. -func (s Set) Len() int { - return len(s) -} - -// IsEmpty indicates whether the set is the empty set. -func (s Set) IsEmpty() bool { - return len(s) == 0 -} - -// Diff takes a pair of Sets, and returns the elements that occur only on the left and right set. -func (s Set) Diff(other Set) (left []string, right []string) { - for k := range s { - if _, f := other[k]; !f { - left = append(left, k) - } - } - for k := range other { - if _, f := s[k]; !f { - right = append(right, k) - } - } - return -} diff --git a/pkg/util/sets/string_test.go b/pkg/util/sets/string_test.go deleted file mode 100644 index 3d60cf3ed..000000000 --- a/pkg/util/sets/string_test.go +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sets - -import ( - "fmt" - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/test/util/assert" -) - -func TestNewSet(t *testing.T) { - elements := []string{"a", "b", "c"} - set := New(elements...) - - if len(set) != len(elements) { - t.Errorf("Expected length %d != %d", len(set), len(elements)) - } - - for _, e := range elements { - if _, exist := set[e]; !exist { - t.Errorf("%s is not in set %v", e, set) - } - } -} - -func TestUnion(t *testing.T) { - elements := []string{"a", "b", "c", "d"} - elements2 := []string{"a", "b", "e"} - want := New("a", "b", "c", "d", "e") - for _, sets := range [][]Set{ - {New(elements...), New(elements2...)}, - {New(elements2...), New(elements...)}, - } { - s1, s2 := sets[0], sets[1] - if got := s1.Union(s2); !got.Equals(want) { - t.Errorf("expected %v; got %v", want, got) - } - } -} - -func TestDifference(t *testing.T) { - elements := []string{"a", "b", "c", "d"} - s1 := New(elements...) - - elements2 := []string{"a", "b", "e"} - s2 := New(elements2...) - - d := s1.Difference(s2) - - if len(d) != 2 { - t.Errorf("Expected len=2: %d", len(d)) - } - - if _, exist := d["c"]; !exist { - t.Errorf("c is not in %v", d) - } - if _, exist := d["d"]; !exist { - t.Errorf("d is not in %v", d) - } -} - -func TestIntersection(t *testing.T) { - elements := []string{"a", "b", "d"} - s1 := New(elements...) - - elements2 := []string{"a", "b", "c"} - s2 := New(elements2...) - - d := s1.Intersection(s2) - - if len(d) != 2 { - t.Errorf("Expected len=2: %d", len(d)) - } - - if _, exist := d["a"]; !exist { - t.Errorf("a is not in %v", d) - } - if _, exist := d["b"]; !exist { - t.Errorf("b is not in %v", d) - } -} - -func TestSupersetOf(t *testing.T) { - elements := []string{"a", "b", "c", "d"} - s1 := New(elements...) - - elements2 := []string{"a", "b"} - s2 := New(elements2...) - - if !s1.SupersetOf(s2) { - t.Errorf("%v should be superset of %v", s1.SortedList(), s2.SortedList()) - } - - s3 := New() - if !New().SupersetOf(s3) { - fmt.Printf("%q\n", s3.SortedList()[0]) - t.Errorf("%v should be superset of empty set", s1.SortedList()) - } -} - -func TestEquals(t *testing.T) { - tests := []struct { - name string - first Set - second Set - want bool - }{ - { - "both nil", - nil, - nil, - true, - }, - { - "unequal length", - New("test"), - New("test", "test1"), - false, - }, - { - "equal sets", - New("test", "test1"), - New("test", "test1"), - true, - }, - { - "unequal sets", - New("test", "test1"), - New("test", "test2"), - false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := tt.first.Equals(tt.second); got != tt.want { - t.Errorf("Unexpected Equal. got %v, want %v", got, tt.want) - } - }) - } -} - -func TestMerge(t *testing.T) { - cases := []struct { - s1, s2 Set - expected []string - }{ - { - s1: New("a1", "a2"), - s2: New("a1", "a2"), - expected: []string{"a1", "a2"}, - }, - { - s1: New("a1", "a2", "a3"), - s2: New("a1", "a2"), - expected: []string{"a1", "a2", "a3"}, - }, - { - s1: New("a1", "a2"), - s2: New("a3", "a4"), - expected: []string{"a1", "a2", "a3", "a4"}, - }, - } - - for _, tc := range cases { - got := tc.s1.Merge(tc.s2) - assert.Equal(t, tc.expected, got.SortedList()) - } -} diff --git a/pkg/util/shellescape/quote.go b/pkg/util/shellescape/quote.go deleted file mode 100644 index 78d5688c7..000000000 --- a/pkg/util/shellescape/quote.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package shellescape - -import ( - "regexp" - "strings" -) - -var unsafeValue = regexp.MustCompile(`[^\\w@%+=:,./-]`) - -func Quote(s string) string { - // ported from https://github.com/chrissimpkins/shellescape/blob/master/lib/shellescape/main.py - if len(s) == 0 { - return "''" - } - - if unsafeValue.MatchString(s) { - return "'" + strings.ReplaceAll(s, "'", "'\"'\"'") + "'" - } - - return s -} diff --git a/pkg/util/shellescape/quote_test.go b/pkg/util/shellescape/quote_test.go deleted file mode 100644 index 9211512e3..000000000 --- a/pkg/util/shellescape/quote_test.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright Istio Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package shellescape - -import ( - "os/exec" - "strings" - "testing" -) - -func TestQuote(t *testing.T) { - testCases := []string{ - `{"key": "value"}`, - `it's going to need single quotes`, - "", - "no issues here", - } - for _, original := range testCases { - out, err := exec.Command("sh", "-c", "echo "+Quote(original)).CombinedOutput() - if err != nil { - t.Fatal(err) - } - got := strings.TrimRight(string(out), "\n") - if got != original { - t.Errorf("got: %s; want: %s", got, original) - } - } -} diff --git a/pkg/util/strcase/camelcase.go b/pkg/util/strcase/camelcase.go deleted file mode 100644 index 564b56760..000000000 --- a/pkg/util/strcase/camelcase.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package strcase - -import ( - "bytes" - "strings" -) - -// CamelCase converts the string into camel case string -func CamelCase(s string) string { - if s == "" { - return "" - } - t := make([]byte, 0, 32) - i := 0 - if isWordSeparator(s[0]) { - // Need a capital letter; drop the '_'. - t = append(t, 'X') - i++ - } - // Invariant: if the next letter is lower case, it must be converted - // to upper case. - // That is, we process a word at a time, where words are marked by _, - or - // upper case letter. Digits are treated as words. - for ; i < len(s); i++ { - c := s[i] - if isWordSeparator(c) { - // Skip the separate and capitalize the next letter. - continue - } - if isASCIIDigit(c) { - t = append(t, c) - continue - } - // Assume we have a letter now - if not, it's a bogus identifier. - // The next word is a sequence of characters that must start upper case. - if isASCIILower(c) { - c ^= ' ' // Make it a capital letter. - } - t = append(t, c) // Guaranteed not lower case. - // Accept lower case sequence that follows. - for i+1 < len(s) && isASCIILower(s[i+1]) { - i++ - t = append(t, s[i]) - } - } - return string(t) -} - -// CamelCaseWithSeparator splits the given string by the separator, converts the parts to CamelCase and then re-joins them. -func CamelCaseWithSeparator(n string, sep string) string { - p := strings.Split(n, sep) - for i := 0; i < len(p); i++ { - p[i] = CamelCase(p[i]) - } - return strings.Join(p, "") -} - -// CamelCaseToKebabCase converts "MyName" to "my-name" -func CamelCaseToKebabCase(s string) string { - switch s { - case "HTTPAPISpec": - return "http-api-spec" - case "HTTPRoute": - return "http-route" - case "HTTPAPISpecBinding": - return "http-api-spec-binding" - default: - var out bytes.Buffer - for i := range s { - if 'A' <= s[i] && s[i] <= 'Z' { - if i > 0 { - out.WriteByte('-') - } - out.WriteByte(s[i] - 'A' + 'a') - } else { - out.WriteByte(s[i]) - } - } - return out.String() - } -} - -func isWordSeparator(c byte) bool { - return c == '_' || c == '-' -} - -// Is c an ASCII lower-case letter? -func isASCIILower(c byte) bool { - return 'a' <= c && c <= 'z' -} - -// Is c an ASCII digit? -func isASCIIDigit(c byte) bool { - return '0' <= c && c <= '9' -} diff --git a/pkg/util/strcase/camelcase_test.go b/pkg/util/strcase/camelcase_test.go deleted file mode 100644 index 3aba379ce..000000000 --- a/pkg/util/strcase/camelcase_test.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package strcase_test - -import ( - "testing" -) - -import ( - . "github.com/onsi/gomega" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/util/strcase" -) - -func TestCamelCase(t *testing.T) { - cases := map[string]string{ - "": "", - "foo": "Foo", - "foobar": "Foobar", - "fooBar": "FooBar", - "foo_bar": "FooBar", - "foo-bar": "FooBar", - "foo_Bar": "FooBar", - "foo9bar": "Foo9Bar", - "HTTP-API-Spec": "HTTPAPISpec", - "http-api-spec": "HttpApiSpec", - "_foo": "XFoo", - "-foo": "XFoo", - "_Foo": "XFoo", - "-Foo": "XFoo", - } - - for k, v := range cases { - t.Run(k, func(t *testing.T) { - g := NewWithT(t) - - a := strcase.CamelCase(k) - g.Expect(a).To(Equal(v)) - }) - } -} - -func TestCamelCaseToKebabCase(t *testing.T) { - cases := map[string]string{ - "": "", - "Foo": "foo", - "FooBar": "foo-bar", - "foo9bar": "foo9bar", - "HTTPAPISpec": "http-api-spec", - "HTTPAPISpecBinding": "http-api-spec-binding", - } - - for k, v := range cases { - t.Run(k, func(t *testing.T) { - g := NewWithT(t) - - a := strcase.CamelCaseToKebabCase(k) - g.Expect(a).To(Equal(v)) - }) - } -} diff --git a/pkg/wasm/cache.go b/pkg/wasm/cache.go deleted file mode 100644 index 6bac72b21..000000000 --- a/pkg/wasm/cache.go +++ /dev/null @@ -1,437 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package wasm - -import ( - "bytes" - "context" - "crypto/sha256" - "encoding/hex" - "fmt" - "net/url" - "os" - "path/filepath" - "strconv" - "strings" - "sync" - "time" -) - -import ( - "github.com/google/go-containerregistry/pkg/name" - extensions "istio.io/api/extensions/v1alpha1" - "istio.io/pkg/log" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -var wasmLog = log.RegisterScope("wasm", "", 0) - -const ( - // DefaultWasmModulePurgeInterval is the default interval for periodic stale Wasm module clean up. - DefaultWasmModulePurgeInterval = 10 * time.Minute - - // DefaultWasmModuleExpiry is the default duration for least recently touched Wasm module to become stale. - DefaultWasmModuleExpiry = 24 * time.Hour - - // oci URL prefix - ociURLPrefix = "oci://" - - // sha256 scheme prefix - sha256SchemePrefix = "sha256:" -) - -// Cache models a Wasm module cache. -type Cache interface { - Get(url, checksum, resourceName, resourceVersion string, timeout time.Duration, pullSecret []byte, pullPolicy extensions.PullPolicy) (string, error) - Cleanup() -} - -// LocalFileCache for downloaded Wasm modules. Currently it stores the Wasm module as local file. -type LocalFileCache struct { - // Map from Wasm module checksum to cache entry. - modules map[moduleKey]*cacheEntry - // Map from tagged URL to checksum - checksums map[string]*checksumEntry - - // http fetcher fetches Wasm module with HTTP get. - httpFetcher *HTTPFetcher - - // directory path used to store Wasm module. - dir string - - // mux is needed because stale Wasm module files will be purged periodically. - mux sync.Mutex - - // Duration for stale Wasm module purging. - purgeInterval time.Duration - wasmModuleExpiry time.Duration - insecureRegistries sets.Set - allowAllInsecureRegistries bool - - // stopChan currently is only used by test - stopChan chan struct{} -} - -var _ Cache = &LocalFileCache{} - -type checksumEntry struct { - checksum string - // Keeps the resource version per each resource for dealing with multiple resources which pointing the same image. - resourceVersionByResource map[string]string -} - -type moduleKey struct { - // Identifier for the module. It should be neutral for the checksum. - // e.g.) oci://docker.io/test@sha256:0123456789 is not allowed. - // oci://docker.io/test:latest (tagged form) is allowed. - name string - checksum string -} - -type cacheKey struct { - moduleKey - downloadURL string - // Resource name of WasmPlugin resource. This should be a fully-qualified name. - resourceName string - // Resource version of WasmPlugin resource. Even though PullPolicy is Always, - // if there is no change of resource state, a cached entry is used instead of pulling newly. - resourceVersion string -} - -// cacheEntry contains information about a Wasm module cache entry. -type cacheEntry struct { - // File path to the downloaded wasm modules. - modulePath string - // Last time that this local Wasm module is referenced. - last time.Time - // set of URLs referencing this entry - referencingURLs sets.Set -} - -// NewLocalFileCache create a new Wasm module cache which downloads and stores Wasm module files locally. -func NewLocalFileCache(dir string, purgeInterval, moduleExpiry time.Duration, insecureRegistries []string) *LocalFileCache { - ir := sets.New(insecureRegistries...) - cache := &LocalFileCache{ - httpFetcher: NewHTTPFetcher(), - modules: make(map[moduleKey]*cacheEntry), - checksums: make(map[string]*checksumEntry), - dir: dir, - purgeInterval: purgeInterval, - wasmModuleExpiry: moduleExpiry, - stopChan: make(chan struct{}), - insecureRegistries: ir, - // If the set of the given insecure registries contains "*", then allow all the insecure registries. - allowAllInsecureRegistries: ir.Contains("*"), - } - - go func() { - cache.purge() - }() - return cache -} - -func urlAsResourceName(fullURLStr string) string { - if strings.HasPrefix(fullURLStr, ociURLPrefix) { - if tag, err := name.ParseReference(fullURLStr[len(ociURLPrefix):]); err == nil { - // remove tag or sha - return ociURLPrefix + tag.Context().Name() - } - } - return fullURLStr -} - -func pullIfNotPresent(pullPolicy extensions.PullPolicy, u *url.URL) bool { - if u.Scheme == "oci" { - switch pullPolicy { - case extensions.PullPolicy_Always: - return false - case extensions.PullPolicy_IfNotPresent: - return true - default: - return !strings.HasSuffix(u.Path, ":latest") - } - } - // If http/https is used, it has `always` semantics at this time. - return false -} - -// Get returns path the local Wasm module file. -func (c *LocalFileCache) Get( - downloadURL, checksum, resourceName, resourceVersion string, - timeout time.Duration, pullSecret []byte, pullPolicy extensions.PullPolicy) (string, error) { - // Construct Wasm cache key with downloading URL and provided checksum of the module. - key := cacheKey{ - downloadURL: downloadURL, - moduleKey: moduleKey{ - name: urlAsResourceName(downloadURL), - checksum: checksum, - }, - resourceName: resourceName, - resourceVersion: resourceVersion, - } - - u, err := url.Parse(downloadURL) - if err != nil { - return "", fmt.Errorf("fail to parse Wasm module fetch url: %s", downloadURL) - } - - // First check if the cache entry is already downloaded and policy does not require to pull always. - var modulePath string - modulePath, key.checksum = c.getEntry(key, pullIfNotPresent(pullPolicy, u)) - if modulePath != "" { - c.touchEntry(key) - return modulePath, nil - } - - // If not, fetch images. - - // Byte array of Wasm binary. - var b []byte - // Hex-Encoded sha256 checksum of binary. - var dChecksum string - var binaryFetcher func() ([]byte, error) - switch u.Scheme { - case "http", "https": - // Download the Wasm module with http fetcher. - b, err = c.httpFetcher.Fetch(downloadURL, timeout) - if err != nil { - wasmRemoteFetchCount.With(resultTag.Value(downloadFailure)).Increment() - return "", err - } - - // Get sha256 checksum and check if it is the same as provided one. - sha := sha256.Sum256(b) - dChecksum = hex.EncodeToString(sha[:]) - case "oci": - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - insecure := false - if c.allowAllInsecureRegistries || c.insecureRegistries.Contains(u.Host) { - insecure = true - } - // TODO: support imagePullSecret and pass it to ImageFetcherOption. - imgFetcherOps := ImageFetcherOption{ - Insecure: insecure, - } - if pullSecret != nil { - imgFetcherOps.PullSecret = pullSecret - } - wasmLog.Debugf("wasm oci fetch %s with options: %v", downloadURL, imgFetcherOps) - fetcher := NewImageFetcher(ctx, imgFetcherOps) - binaryFetcher, dChecksum, err = fetcher.PrepareFetch(u.Host + u.Path) - if err != nil { - wasmRemoteFetchCount.With(resultTag.Value(downloadFailure)).Increment() - return "", fmt.Errorf("could not fetch Wasm OCI image: %v", err) - } - default: - return "", fmt.Errorf("unsupported Wasm module downloading URL scheme: %v", u.Scheme) - } - - if key.checksum == "" { - key.checksum = dChecksum - // check again if the cache is having the checksum. - if modulePath, _ := c.getEntry(key, true); modulePath != "" { - c.touchEntry(key) - return modulePath, nil - } - } else if dChecksum != key.checksum { - wasmRemoteFetchCount.With(resultTag.Value(checksumMismatch)).Increment() - return "", fmt.Errorf("module downloaded from %v has checksum %v, which does not match: %v", downloadURL, dChecksum, key.checksum) - } - - if binaryFetcher != nil { - b, err = binaryFetcher() - if err != nil { - wasmRemoteFetchCount.With(resultTag.Value(downloadFailure)).Increment() - return "", fmt.Errorf("could not fetch Wasm binary: %v", err) - } - } - - if !isValidWasmBinary(b) { - wasmRemoteFetchCount.With(resultTag.Value(fetchFailure)).Increment() - return "", fmt.Errorf("fetched Wasm binary from %s is invalid", downloadURL) - } - - wasmRemoteFetchCount.With(resultTag.Value(fetchSuccess)).Increment() - - key.checksum = dChecksum - f := filepath.Join(c.dir, fmt.Sprintf("%s.wasm", dChecksum)) - - if err := c.addEntry(key, b, f); err != nil { - return "", err - } - return f, nil -} - -// Cleanup closes background Wasm module purge routine. -func (c *LocalFileCache) Cleanup() { - close(c.stopChan) -} - -func (c *LocalFileCache) updateChecksum(key cacheKey) bool { - // If OCI URL having a tag, we need to update checksum. - needChecksumUpdate := strings.HasPrefix(key.downloadURL, ociURLPrefix) && !strings.Contains(key.downloadURL, "@") - if needChecksumUpdate { - ce := c.checksums[key.downloadURL] - if ce == nil { - ce = new(checksumEntry) - ce.resourceVersionByResource = make(map[string]string) - c.checksums[key.downloadURL] = ce - } - ce.checksum = key.checksum - ce.resourceVersionByResource[key.resourceName] = key.resourceVersion - } - return needChecksumUpdate -} - -func (c *LocalFileCache) touchEntry(key cacheKey) { - c.mux.Lock() - defer c.mux.Unlock() - c.updateChecksum(key) -} - -func (c *LocalFileCache) addEntry(key cacheKey, wasmModule []byte, f string) error { - c.mux.Lock() - defer c.mux.Unlock() - needChecksumUpdate := c.updateChecksum(key) - if needChecksumUpdate { - ce := c.checksums[key.downloadURL] - if ce == nil { - ce = new(checksumEntry) - ce.resourceVersionByResource = make(map[string]string) - c.checksums[key.downloadURL] = ce - } - ce.checksum = key.checksum - ce.resourceVersionByResource[key.resourceName] = key.resourceVersion - } - - // Check if the module has already been added. If so, avoid writing the file again. - if ce, ok := c.modules[key.moduleKey]; ok { - // Update last touched time. - ce.last = time.Now() - if needChecksumUpdate { - ce.referencingURLs.Insert(key.downloadURL) - } - return nil - } - - // Materialize the Wasm module into a local file. Use checksum as name of the module. - if err := os.WriteFile(f, wasmModule, 0o644); err != nil { - return err - } - - ce := cacheEntry{ - modulePath: f, - last: time.Now(), - referencingURLs: sets.New(), - } - if needChecksumUpdate { - ce.referencingURLs.Insert(key.downloadURL) - } - c.modules[key.moduleKey] = &ce - wasmCacheEntries.Record(float64(len(c.modules))) - return nil -} - -// getEntry finds a cached module, and returns the path of the module and its checksum. -func (c *LocalFileCache) getEntry(key cacheKey, ignoreResourceVersion bool) (string, string) { - modulePath := "" - cacheHit := false - - c.mux.Lock() - defer c.mux.Unlock() - - // Only apply this for OCI image, not http/https because OCI image has ImagePullPolicy - // to control the pull policy, but http/https currently rely on existence of checksum. - // At this point, we don't need to break the current behavior for http/https. - if len(key.checksum) == 0 && strings.HasPrefix(key.downloadURL, ociURLPrefix) { - if d, err := name.NewDigest(key.downloadURL[len(ociURLPrefix):]); err == nil { - // If there is no checksum and the digest is suffixed in URL, use the digest. - dstr := d.DigestStr() - if strings.HasPrefix(dstr, sha256SchemePrefix) { - key.checksum = dstr[len(sha256SchemePrefix):] - } - // For other digest scheme, give up to use cache. - } else { - // If no checksum, try the checksum cache. - // If the image was pulled before, there should be a checksum of the most recently pulled image. - if ce, found := c.checksums[key.downloadURL]; found { - if ignoreResourceVersion || key.resourceVersion == ce.resourceVersionByResource[key.resourceName] { - key.checksum = ce.checksum - } - // update resource version here - ce.resourceVersionByResource[key.resourceName] = key.resourceVersion - } - } - } - - if ce, ok := c.modules[key.moduleKey]; ok { - // Update last touched time. - ce.last = time.Now() - modulePath = ce.modulePath - cacheHit = true - } - wasmCacheLookupCount.With(hitTag.Value(strconv.FormatBool(cacheHit))).Increment() - return modulePath, key.checksum -} - -// Purge periodically clean up the stale Wasm modules local file and the cache map. -func (c *LocalFileCache) purge() { - ticker := time.NewTicker(c.purgeInterval) - defer ticker.Stop() - for { - select { - case <-ticker.C: - c.mux.Lock() - for k, m := range c.modules { - if !m.expired(c.wasmModuleExpiry) { - continue - } - // The module has not be touched for expiry duration, delete it from the map as well as the local dir. - if err := os.Remove(m.modulePath); err != nil { - wasmLog.Errorf("failed to purge Wasm module %v: %v", m.modulePath, err) - } else { - for downloadURL := range m.referencingURLs { - delete(c.checksums, downloadURL) - } - delete(c.modules, k) - wasmLog.Debugf("successfully removed stale Wasm module %v", m.modulePath) - } - } - wasmCacheEntries.Record(float64(len(c.modules))) - c.mux.Unlock() - case <-c.stopChan: - // Currently this will only happen in test. - return - } - } -} - -// Expired returns true if the module has not been touched for Wasm module Expiry. -func (ce *cacheEntry) expired(expiry time.Duration) bool { - now := time.Now() - return now.Sub(ce.last) > expiry -} - -var wasmMagicNumber = []byte{0x00, 0x61, 0x73, 0x6d} - -func isValidWasmBinary(in []byte) bool { - // Wasm file header is 8 bytes (magic number + version). - return len(in) >= 8 && bytes.Equal(in[:4], wasmMagicNumber) -} diff --git a/pkg/wasm/cache_test.go b/pkg/wasm/cache_test.go deleted file mode 100644 index 611543606..000000000 --- a/pkg/wasm/cache_test.go +++ /dev/null @@ -1,910 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package wasm - -import ( - "crypto/sha256" - "crypto/tls" - "encoding/hex" - "fmt" - "net/http" - "net/http/httptest" - "net/url" - "os" - "path/filepath" - "strings" - "sync/atomic" - "testing" - "time" -) - -import ( - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/google/go-containerregistry/pkg/crane" - "github.com/google/go-containerregistry/pkg/registry" - "github.com/google/go-containerregistry/pkg/v1/empty" - "github.com/google/go-containerregistry/pkg/v1/mutate" - "github.com/google/go-containerregistry/pkg/v1/remote" - "github.com/google/go-containerregistry/pkg/v1/types" - extensions "istio.io/api/extensions/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/util/sets" -) - -// Wasm header = magic number (4 bytes) + Wasm spec version (4 bytes). -var wasmHeader = append(wasmMagicNumber, []byte{0x1, 0x00, 0x00, 0x00}...) - -func TestWasmCache(t *testing.T) { - // Setup http server. - tsNumRequest := int32(0) - - httpData := append(wasmHeader, []byte("data")...) - invalidHTTPData := []byte("invalid binary") - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - atomic.AddInt32(&tsNumRequest, 1) - - if r.URL.Path == "/different-url" { - w.Write(append(httpData, []byte("different data")...)) - } else if r.URL.Path == "/invalid-wasm-header" { - w.Write(invalidHTTPData) - } else { - w.Write(httpData) - } - })) - defer ts.Close() - httpDataSha := sha256.Sum256(httpData) - httpDataCheckSum := hex.EncodeToString(httpDataSha[:]) - invalidHTTPDataSha := sha256.Sum256(invalidHTTPData) - invalidHTTPDataCheckSum := hex.EncodeToString(invalidHTTPDataSha[:]) - - reg := registry.New() - // Set up a fake registry for OCI images. - tos := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - atomic.AddInt32(&tsNumRequest, 1) - reg.ServeHTTP(w, r) - })) - defer tos.Close() - ou, err := url.Parse(tos.URL) - if err != nil { - t.Fatal(err) - } - - dockerImageDigest, invalidOCIImageDigest := setupOCIRegistry(t, ou.Host) - - ociWasmFile := fmt.Sprintf("%s.wasm", dockerImageDigest) - ociURLWithTag := fmt.Sprintf("oci://%s/test/valid/docker:v0.1.0", ou.Host) - ociURLWithLatestTag := fmt.Sprintf("oci://%s/test/valid/docker:latest", ou.Host) - ociURLWithDigest := fmt.Sprintf("oci://%s/test/valid/docker@sha256:%s", ou.Host, dockerImageDigest) - - // Calculate cachehit sum. - cacheHitSha := sha256.Sum256([]byte("cachehit")) - cacheHitSum := hex.EncodeToString(cacheHitSha[:]) - - cases := []struct { - name string - initialCachedModules map[moduleKey]cacheEntry - initialCachedChecksums map[string]*checksumEntry - fetchURL string - purgeInterval time.Duration - wasmModuleExpiry time.Duration - checkPurgeTimeout time.Duration - checksum string // Hex-encoded string. - resourceName string - resourceVersion string - requestTimeout time.Duration - pullPolicy extensions.PullPolicy - wantCachedModules map[moduleKey]*cacheEntry - wantCachedChecksums map[string]*checksumEntry - wantFileName string - wantErrorMsgPrefix string - wantVisitServer bool - }{ - { - name: "cache miss", - initialCachedModules: map[moduleKey]cacheEntry{}, - initialCachedChecksums: map[string]*checksumEntry{}, - fetchURL: ts.URL, - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - checksum: httpDataCheckSum, - requestTimeout: time.Second * 10, - wantCachedModules: map[moduleKey]*cacheEntry{ - {name: ts.URL, checksum: httpDataCheckSum}: {modulePath: httpDataCheckSum + ".wasm"}, - }, - wantCachedChecksums: map[string]*checksumEntry{}, - wantFileName: fmt.Sprintf("%s.wasm", httpDataCheckSum), - wantVisitServer: true, - }, - { - name: "cache hit", - initialCachedModules: map[moduleKey]cacheEntry{ - {name: urlAsResourceName(ts.URL), checksum: cacheHitSum}: {modulePath: "test.wasm"}, - }, - initialCachedChecksums: map[string]*checksumEntry{}, - fetchURL: ts.URL, - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - checksum: cacheHitSum, - requestTimeout: time.Second * 10, - wantCachedModules: map[moduleKey]*cacheEntry{ - {name: ts.URL, checksum: cacheHitSum}: {modulePath: "test.wasm"}, - }, - wantCachedChecksums: map[string]*checksumEntry{}, - wantFileName: "test.wasm", - wantVisitServer: false, - }, - { - name: "invalid scheme", - initialCachedModules: map[moduleKey]cacheEntry{}, - initialCachedChecksums: map[string]*checksumEntry{}, - fetchURL: "foo://abc", - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - checksum: httpDataCheckSum, - requestTimeout: time.Second * 10, - wantCachedModules: map[moduleKey]*cacheEntry{}, - wantCachedChecksums: map[string]*checksumEntry{}, - wantFileName: fmt.Sprintf("%s.wasm", httpDataCheckSum), - wantErrorMsgPrefix: "unsupported Wasm module downloading URL scheme: foo", - wantVisitServer: false, - }, - { - name: "download failure", - initialCachedModules: map[moduleKey]cacheEntry{}, - initialCachedChecksums: map[string]*checksumEntry{}, - fetchURL: "https://dummyurl", - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - requestTimeout: time.Second * 10, - wantCachedModules: map[moduleKey]*cacheEntry{}, - wantCachedChecksums: map[string]*checksumEntry{}, - wantErrorMsgPrefix: "wasm module download failed, last error: Get \"https://dummyurl\"", - wantVisitServer: false, - }, - { - name: "wrong checksum", - initialCachedModules: map[moduleKey]cacheEntry{}, - initialCachedChecksums: map[string]*checksumEntry{}, - fetchURL: ts.URL, - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - checksum: "wrongchecksum\n", - requestTimeout: time.Second * 10, - wantCachedModules: map[moduleKey]*cacheEntry{}, - wantCachedChecksums: map[string]*checksumEntry{}, - wantErrorMsgPrefix: fmt.Sprintf("module downloaded from %v has checksum %s, which does not match", ts.URL, httpDataCheckSum), - wantVisitServer: true, - }, - { - // this might be common error in user configuration, that url was updated, but not checksum. - // Test that downloading still proceeds and error returns. - name: "different url same checksum", - initialCachedModules: map[moduleKey]cacheEntry{ - {name: urlAsResourceName(ts.URL), checksum: httpDataCheckSum}: {modulePath: fmt.Sprintf("%s.wasm", httpDataCheckSum)}, - }, - initialCachedChecksums: map[string]*checksumEntry{}, - fetchURL: ts.URL + "/different-url", - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - checksum: httpDataCheckSum, - requestTimeout: time.Second * 10, - wantCachedModules: map[moduleKey]*cacheEntry{ - {name: ts.URL, checksum: httpDataCheckSum}: {modulePath: httpDataCheckSum + ".wasm"}, - }, - wantCachedChecksums: map[string]*checksumEntry{}, - wantErrorMsgPrefix: fmt.Sprintf("module downloaded from %v/different-url has checksum", ts.URL), - wantVisitServer: true, - }, - { - name: "invalid wasm header", - initialCachedModules: map[moduleKey]cacheEntry{ - {name: urlAsResourceName(ts.URL), checksum: httpDataCheckSum}: {modulePath: fmt.Sprintf("%s.wasm", httpDataCheckSum)}, - }, - initialCachedChecksums: map[string]*checksumEntry{}, - fetchURL: ts.URL + "/invalid-wasm-header", - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - checksum: invalidHTTPDataCheckSum, - requestTimeout: time.Second * 10, - wantCachedModules: map[moduleKey]*cacheEntry{ - {name: ts.URL, checksum: httpDataCheckSum}: {modulePath: httpDataCheckSum + ".wasm"}, - }, - wantCachedChecksums: map[string]*checksumEntry{}, - wantErrorMsgPrefix: fmt.Sprintf("fetched Wasm binary from %s is invalid", ts.URL+"/invalid-wasm-header"), - wantVisitServer: true, - }, - { - name: "purge on expiry", - initialCachedModules: map[moduleKey]cacheEntry{}, - initialCachedChecksums: map[string]*checksumEntry{}, - fetchURL: ts.URL, - purgeInterval: 1 * time.Millisecond, - wasmModuleExpiry: 1 * time.Millisecond, - checkPurgeTimeout: 5 * time.Second, - checksum: httpDataCheckSum, - requestTimeout: time.Second * 10, - wantCachedModules: map[moduleKey]*cacheEntry{}, - wantCachedChecksums: map[string]*checksumEntry{}, - wantFileName: fmt.Sprintf("%s.wasm", httpDataCheckSum), - wantVisitServer: true, - }, - { - name: "fetch oci without digest", - initialCachedModules: map[moduleKey]cacheEntry{}, - initialCachedChecksums: map[string]*checksumEntry{}, - resourceName: "namespace.resource", - resourceVersion: "0", - fetchURL: ociURLWithTag, - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - requestTimeout: time.Second * 10, - wantCachedModules: map[moduleKey]*cacheEntry{ - {name: urlAsResourceName(ociURLWithTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - wantCachedChecksums: map[string]*checksumEntry{ - ociURLWithTag: {checksum: dockerImageDigest, resourceVersionByResource: map[string]string{"namespace.resource": "0"}}, - }, - wantFileName: ociWasmFile, - wantVisitServer: true, - }, - { - name: "fetch oci with digest", - initialCachedModules: map[moduleKey]cacheEntry{}, - initialCachedChecksums: map[string]*checksumEntry{}, - resourceName: "namespace.resource", - resourceVersion: "0", - fetchURL: ociURLWithTag, - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - requestTimeout: time.Second * 10, - checksum: dockerImageDigest, - wantCachedModules: map[moduleKey]*cacheEntry{ - {name: urlAsResourceName(ociURLWithTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - wantCachedChecksums: map[string]*checksumEntry{ - ociURLWithTag: {checksum: dockerImageDigest, resourceVersionByResource: map[string]string{"namespace.resource": "0"}}, - }, - wantFileName: ociWasmFile, - wantVisitServer: true, - }, - { - name: "cache hit for tagged oci url with digest", - initialCachedModules: map[moduleKey]cacheEntry{ - {name: urlAsResourceName(ociURLWithTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - initialCachedChecksums: map[string]*checksumEntry{}, - resourceName: "namespace.resource", - resourceVersion: "0", - fetchURL: ociURLWithTag, - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - requestTimeout: time.Second * 10, - checksum: dockerImageDigest, - wantCachedModules: map[moduleKey]*cacheEntry{ - {name: urlAsResourceName(ociURLWithTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - wantCachedChecksums: map[string]*checksumEntry{ - ociURLWithTag: {checksum: dockerImageDigest, resourceVersionByResource: map[string]string{"namespace.resource": "0"}}, - }, - wantFileName: ociWasmFile, - wantVisitServer: false, - }, - { - name: "cache hit for tagged oci url without digest", - initialCachedModules: map[moduleKey]cacheEntry{ - {name: urlAsResourceName(ociURLWithTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - initialCachedChecksums: map[string]*checksumEntry{ - ociURLWithTag: { - checksum: dockerImageDigest, - resourceVersionByResource: map[string]string{ - "namespace.resource": "123456", - }, - }, - }, - resourceName: "namespace.resource", - resourceVersion: "0", - fetchURL: ociURLWithTag, - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - requestTimeout: time.Second * 10, - wantCachedModules: map[moduleKey]*cacheEntry{ - {name: urlAsResourceName(ociURLWithTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - wantCachedChecksums: map[string]*checksumEntry{ - ociURLWithTag: {checksum: dockerImageDigest, resourceVersionByResource: map[string]string{"namespace.resource": "0"}}, - }, - wantFileName: ociWasmFile, - wantVisitServer: false, - }, - { - name: "cache miss for tagged oci url without digest", - initialCachedModules: map[moduleKey]cacheEntry{ - {name: urlAsResourceName(ociURLWithTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - initialCachedChecksums: map[string]*checksumEntry{}, - resourceName: "namespace.resource", - resourceVersion: "0", - fetchURL: ociURLWithTag, - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - requestTimeout: time.Second * 10, - wantCachedModules: map[moduleKey]*cacheEntry{ - {name: urlAsResourceName(ociURLWithTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - wantCachedChecksums: map[string]*checksumEntry{ - ociURLWithTag: {checksum: dockerImageDigest, resourceVersionByResource: map[string]string{"namespace.resource": "0"}}, - }, - wantFileName: ociWasmFile, - wantVisitServer: true, - }, - { - name: "cache hit for oci url suffixed by digest", - initialCachedModules: map[moduleKey]cacheEntry{ - {name: urlAsResourceName(ociURLWithTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - initialCachedChecksums: map[string]*checksumEntry{}, - resourceName: "namespace.resource", - resourceVersion: "0", - fetchURL: ociURLWithDigest, - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - requestTimeout: time.Second * 10, - wantCachedModules: map[moduleKey]*cacheEntry{ - {name: urlAsResourceName(ociURLWithTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - wantCachedChecksums: map[string]*checksumEntry{}, - wantFileName: ociWasmFile, - wantVisitServer: false, - }, - { - name: "pull due to pull-always policy when cache hit", - initialCachedModules: map[moduleKey]cacheEntry{ - {name: urlAsResourceName(ociURLWithTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - initialCachedChecksums: map[string]*checksumEntry{ - ociURLWithTag: { - checksum: dockerImageDigest, - resourceVersionByResource: map[string]string{ - "namespace.resource": "123456", - }, - }, - }, - resourceName: "namespace.resource", - resourceVersion: "0", - fetchURL: ociURLWithTag, - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - requestTimeout: time.Second * 10, - pullPolicy: extensions.PullPolicy_Always, - wantCachedModules: map[moduleKey]*cacheEntry{ - {name: urlAsResourceName(ociURLWithTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - wantCachedChecksums: map[string]*checksumEntry{ - ociURLWithTag: {checksum: dockerImageDigest, resourceVersionByResource: map[string]string{"namespace.resource": "0"}}, - }, - wantFileName: ociWasmFile, - wantVisitServer: true, - }, - { - name: "do not pull due to resourceVersion is the same", - initialCachedModules: map[moduleKey]cacheEntry{ - {name: urlAsResourceName(ociURLWithTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - initialCachedChecksums: map[string]*checksumEntry{ - ociURLWithTag: { - checksum: dockerImageDigest, - resourceVersionByResource: map[string]string{ - "namespace.resource": "123456", - }, - }, - }, - resourceName: "namespace.resource", - resourceVersion: "123456", - fetchURL: ociURLWithTag, - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - requestTimeout: time.Second * 10, - pullPolicy: extensions.PullPolicy_Always, - wantCachedModules: map[moduleKey]*cacheEntry{ - {name: urlAsResourceName(ociURLWithTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - wantCachedChecksums: map[string]*checksumEntry{ - ociURLWithTag: {checksum: dockerImageDigest, resourceVersionByResource: map[string]string{"namespace.resource": "123456"}}, - }, - wantFileName: ociWasmFile, - wantVisitServer: false, - }, - { - name: "pull due to if-not-present policy when cache hit", - initialCachedModules: map[moduleKey]cacheEntry{ - {name: urlAsResourceName(ociURLWithTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - initialCachedChecksums: map[string]*checksumEntry{ - ociURLWithTag: { - checksum: dockerImageDigest, - resourceVersionByResource: map[string]string{ - "namespace.resource": "123456", - }, - }, - }, - resourceName: "namespace.resource", - resourceVersion: "0", - fetchURL: ociURLWithTag, - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - requestTimeout: time.Second * 10, - pullPolicy: extensions.PullPolicy_IfNotPresent, - wantCachedModules: map[moduleKey]*cacheEntry{ - {name: urlAsResourceName(ociURLWithTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - wantCachedChecksums: map[string]*checksumEntry{ - ociURLWithTag: {checksum: dockerImageDigest, resourceVersionByResource: map[string]string{"namespace.resource": "0"}}, - }, - wantFileName: ociWasmFile, - wantVisitServer: false, - }, - { - name: "do not pull in spite of pull-always policy due to checksum", - initialCachedModules: map[moduleKey]cacheEntry{ - {name: urlAsResourceName(ociURLWithTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - resourceName: "namespace.resource", - resourceVersion: "0", - fetchURL: ociURLWithTag, - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - requestTimeout: time.Second * 10, - checksum: dockerImageDigest, - pullPolicy: extensions.PullPolicy_Always, - wantCachedModules: map[moduleKey]*cacheEntry{ - {name: urlAsResourceName(ociURLWithTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - wantCachedChecksums: map[string]*checksumEntry{ - ociURLWithTag: {checksum: dockerImageDigest, resourceVersionByResource: map[string]string{"namespace.resource": "0"}}, - }, - wantFileName: ociWasmFile, - wantVisitServer: false, - }, - { - name: "pull due to latest tag", - initialCachedModules: map[moduleKey]cacheEntry{ - {name: urlAsResourceName(ociURLWithLatestTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - resourceName: "namespace.resource", - resourceVersion: "0", - initialCachedChecksums: map[string]*checksumEntry{ - ociURLWithLatestTag: { - checksum: dockerImageDigest, - resourceVersionByResource: map[string]string{ - "namespace.resource": "123456", - }, - }, - }, - fetchURL: ociURLWithLatestTag, - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - requestTimeout: time.Second * 10, - pullPolicy: extensions.PullPolicy_UNSPECIFIED_POLICY, // Default policy - wantCachedModules: map[moduleKey]*cacheEntry{ - {name: urlAsResourceName(ociURLWithLatestTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - wantCachedChecksums: map[string]*checksumEntry{ - ociURLWithLatestTag: {checksum: dockerImageDigest, resourceVersionByResource: map[string]string{"namespace.resource": "0"}}, - }, - wantFileName: ociWasmFile, - wantVisitServer: true, - }, - { - name: "do not pull in spite of latest tag due to checksum", - initialCachedModules: map[moduleKey]cacheEntry{ - {name: urlAsResourceName(ociURLWithLatestTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - initialCachedChecksums: map[string]*checksumEntry{ - ociURLWithLatestTag: { - checksum: dockerImageDigest, - resourceVersionByResource: map[string]string{ - "namespace.resource": "123456", - }, - }, - }, - resourceName: "namespace.resource", - resourceVersion: "0", - fetchURL: ociURLWithLatestTag, - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - requestTimeout: time.Second * 10, - checksum: dockerImageDigest, - pullPolicy: extensions.PullPolicy_UNSPECIFIED_POLICY, // Default policy - wantCachedModules: map[moduleKey]*cacheEntry{ - {name: urlAsResourceName(ociURLWithLatestTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - wantCachedChecksums: map[string]*checksumEntry{ - ociURLWithLatestTag: {checksum: dockerImageDigest, resourceVersionByResource: map[string]string{"namespace.resource": "0"}}, - }, - wantFileName: ociWasmFile, - wantVisitServer: false, - }, - { - name: "do not pull in spite of latest tag due to IfNotPresent policy", - initialCachedModules: map[moduleKey]cacheEntry{ - {name: urlAsResourceName(ociURLWithLatestTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - initialCachedChecksums: map[string]*checksumEntry{ - ociURLWithLatestTag: { - checksum: dockerImageDigest, - resourceVersionByResource: map[string]string{ - "namespace.resource": "123456", - }, - }, - }, - resourceName: "namespace.resource", - resourceVersion: "0", - fetchURL: ociURLWithLatestTag, - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - requestTimeout: time.Second * 10, - pullPolicy: extensions.PullPolicy_IfNotPresent, - wantCachedModules: map[moduleKey]*cacheEntry{ - {name: urlAsResourceName(ociURLWithLatestTag), checksum: dockerImageDigest}: {modulePath: ociWasmFile}, - }, - wantCachedChecksums: map[string]*checksumEntry{ - ociURLWithLatestTag: {checksum: dockerImageDigest, resourceVersionByResource: map[string]string{"namespace.resource": "0"}}, - }, - wantFileName: ociWasmFile, - wantVisitServer: false, - }, - { - name: "purge OCI image on expiry", - initialCachedModules: map[moduleKey]cacheEntry{ - {name: urlAsResourceName(ociURLWithTag) + "-purged", checksum: dockerImageDigest}: {modulePath: ociWasmFile, referencingURLs: sets.New(ociURLWithTag)}, - }, - initialCachedChecksums: map[string]*checksumEntry{ - ociURLWithTag: { - checksum: dockerImageDigest, - resourceVersionByResource: map[string]string{ - "namespace.resource": "123456", - }, - }, - "test-url": { - checksum: "test-checksum", - resourceVersionByResource: map[string]string{ - "namespace.resource2": "123456", - }, - }, - }, - resourceName: "namespace.resource", - resourceVersion: "0", - fetchURL: ociURLWithDigest, - purgeInterval: 1 * time.Millisecond, - wasmModuleExpiry: 1 * time.Millisecond, - requestTimeout: time.Second * 10, - checkPurgeTimeout: 5 * time.Second, - wantCachedModules: map[moduleKey]*cacheEntry{}, - wantCachedChecksums: map[string]*checksumEntry{ - "test-url": {checksum: "test-checksum", resourceVersionByResource: map[string]string{"namespace.resource2": "123456"}}, - }, - wantFileName: ociWasmFile, - wantVisitServer: true, - }, - { - name: "fetch oci timed out", - initialCachedModules: map[moduleKey]cacheEntry{}, - resourceName: "namespace.resource", - resourceVersion: "0", - fetchURL: ociURLWithTag, - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - requestTimeout: 0, // Cause timeout immediately. - wantCachedModules: map[moduleKey]*cacheEntry{}, - wantCachedChecksums: map[string]*checksumEntry{}, - wantErrorMsgPrefix: fmt.Sprintf("could not fetch Wasm OCI image: could not fetch manifest: Get \"https://%s/v2/\"", ou.Host), - wantVisitServer: false, - }, - { - name: "fetch oci with wrong digest", - initialCachedModules: map[moduleKey]cacheEntry{}, - resourceName: "namespace.resource", - resourceVersion: "0", - fetchURL: ociURLWithTag, - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - requestTimeout: time.Second * 10, - checksum: "wrongdigest", - wantCachedModules: map[moduleKey]*cacheEntry{}, - wantCachedChecksums: map[string]*checksumEntry{}, - wantErrorMsgPrefix: fmt.Sprintf( - "module downloaded from %v has checksum %v, which does not match:", fmt.Sprintf("oci://%s/test/valid/docker:v0.1.0", ou.Host), dockerImageDigest, - ), - wantVisitServer: true, - }, - { - name: "fetch invalid oci", - initialCachedModules: map[moduleKey]cacheEntry{}, - resourceName: "namespace.resource", - resourceVersion: "0", - fetchURL: fmt.Sprintf("oci://%s/test/invalid", ou.Host), - purgeInterval: DefaultWasmModulePurgeInterval, - wasmModuleExpiry: DefaultWasmModuleExpiry, - checksum: invalidOCIImageDigest, - requestTimeout: time.Second * 10, - wantCachedModules: map[moduleKey]*cacheEntry{}, - wantCachedChecksums: map[string]*checksumEntry{}, - wantErrorMsgPrefix: `could not fetch Wasm binary: the given image is in invalid format as an OCI image: 2 errors occurred: - * could not parse as compat variant: invalid media type application/vnd.oci.image.layer.v1.tar (expect application/vnd.oci.image.layer.v1.tar+gzip) - * could not parse as oci variant: number of layers must be 2 but got 1`, - wantVisitServer: true, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - tmpDir := t.TempDir() - cache := NewLocalFileCache(tmpDir, c.purgeInterval, c.wasmModuleExpiry, nil) - cache.httpFetcher.initialBackoff = time.Microsecond - defer close(cache.stopChan) - - var cacheHitKey *moduleKey - initTime := time.Now() - cache.mux.Lock() - for k, m := range c.initialCachedModules { - filePath := filepath.Join(tmpDir, m.modulePath) - err := os.WriteFile(filePath, []byte("data/\n"), 0o644) - if err != nil { - t.Fatalf("failed to write initial wasm module file %v", err) - } - // wait flush - time.Sleep(10 * time.Millisecond) - mkey := moduleKey{name: k.name, checksum: k.checksum} - - cache.modules[mkey] = &cacheEntry{modulePath: filePath, last: initTime} - if m.referencingURLs != nil { - cache.modules[mkey].referencingURLs = m.referencingURLs.Copy() - } else { - cache.modules[mkey].referencingURLs = sets.New() - } - - if urlAsResourceName(c.fetchURL) == k.name && c.checksum == k.checksum { - cacheHitKey = &mkey - } - } - - for k, m := range c.initialCachedChecksums { - cache.checksums[k] = m - } - - // put the tmp dir into the module path. - for k, m := range c.wantCachedModules { - c.wantCachedModules[k].modulePath = filepath.Join(tmpDir, m.modulePath) - } - cache.mux.Unlock() - - atomic.StoreInt32(&tsNumRequest, 0) - gotFilePath, gotErr := cache.Get(c.fetchURL, c.checksum, c.resourceName, c.resourceVersion, c.requestTimeout, []byte{}, c.pullPolicy) - serverVisited := atomic.LoadInt32(&tsNumRequest) > 0 - - if c.checkPurgeTimeout > 0 { - moduleDeleted := false - for start := time.Now(); time.Since(start) < c.checkPurgeTimeout; { - // Check existence of module files. files should be deleted before timing out. - if files, err := os.ReadDir(tmpDir); err == nil && len(files) == 0 { - moduleDeleted = true - break - } - } - - if !moduleDeleted { - t.Fatalf("Wasm modules are not purged before purge timeout") - } - } - - cache.mux.Lock() - if cacheHitKey != nil { - if entry, ok := cache.modules[*cacheHitKey]; ok && entry.last == initTime { - t.Errorf("Wasm module cache entry's last access time not updated after get operation, key: %v", *cacheHitKey) - } - } - - if diff := cmp.Diff(cache.modules, c.wantCachedModules, - cmpopts.IgnoreFields(cacheEntry{}, "last", "referencingURLs"), - cmp.AllowUnexported(cacheEntry{}), - ); diff != "" { - t.Errorf("unexpected module cache: (+want, -got)\n%v", diff) - } - - if diff := cmp.Diff(cache.checksums, c.wantCachedChecksums, - cmp.AllowUnexported(checksumEntry{}), - ); diff != "" { - t.Errorf("unexpected checksums: (+want, -got)\n%v", diff) - } - - cache.mux.Unlock() - - wantFilePath := filepath.Join(tmpDir, c.wantFileName) - if c.wantErrorMsgPrefix != "" { - if gotErr == nil { - t.Errorf("Wasm module cache lookup got no error, want error prefix `%v`", c.wantErrorMsgPrefix) - } else if !strings.HasPrefix(gotErr.Error(), c.wantErrorMsgPrefix) { - t.Errorf("Wasm module cache lookup got error `%v`, want error prefix `%v`", gotErr, c.wantErrorMsgPrefix) - } - } else if gotFilePath != wantFilePath { - t.Errorf("Wasm module local file path got %v, want %v", gotFilePath, wantFilePath) - if gotErr != nil { - t.Errorf("got unexpected error %v", gotErr) - } - } - if c.wantVisitServer != serverVisited { - t.Errorf("test wasm binary server encountered the unexpected visiting status got %v, want %v", serverVisited, c.wantVisitServer) - } - }) - } -} - -func setupOCIRegistry(t *testing.T, host string) (dockerImageDigest, invalidOCIImageDigest string) { - // Push *compat* variant docker image (others are well tested in imagefetcher's test and the behavior is consistent). - ref := fmt.Sprintf("%s/test/valid/docker:v0.1.0", host) - binary := append(wasmHeader, []byte("this is wasm plugin")...) - transport := remote.DefaultTransport.Clone() - transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} - fetchOpt := crane.WithTransport(transport) - - // Create docker layer. - l, err := newMockLayer(types.DockerLayer, - map[string][]byte{"plugin.wasm": binary}) - if err != nil { - t.Fatal(err) - } - img, err := mutate.Append(empty.Image, mutate.Addendum{Layer: l}) - if err != nil { - t.Fatal(err) - } - - // Set manifest type. - manifest, err := img.Manifest() - if err != nil { - t.Fatal(err) - } - manifest.MediaType = types.DockerManifestSchema2 - - // Push image to the registry. - err = crane.Push(img, ref, fetchOpt) - if err != nil { - t.Fatal(err) - } - - // Push image to the registry with latest tag as well - ref = fmt.Sprintf("%s/test/valid/docker:latest", host) - err = crane.Push(img, ref, fetchOpt) - if err != nil { - t.Fatal(err) - } - - // Calculate sum - d, _ := img.Digest() - dockerImageDigest = d.Hex - - // Finally push the invalid image. - ref = fmt.Sprintf("%s/test/invalid", host) - l, err = newMockLayer(types.OCIUncompressedLayer, map[string][]byte{"not-wasm.txt": []byte("a")}) - if err != nil { - t.Fatal(err) - } - img2, err := mutate.Append(empty.Image, mutate.Addendum{Layer: l}) - if err != nil { - t.Fatal(err) - } - - // Set manifest type so it will pass the docker parsing branch. - img2 = mutate.MediaType(img2, types.OCIManifestSchema1) - - d, _ = img2.Digest() - invalidOCIImageDigest = d.Hex - - // Push image to the registry. - err = crane.Push(img2, ref, fetchOpt) - if err != nil { - t.Fatal(err) - } - return -} - -func TestWasmCacheMissChecksum(t *testing.T) { - tmpDir := t.TempDir() - cache := NewLocalFileCache(tmpDir, DefaultWasmModulePurgeInterval, DefaultWasmModuleExpiry, nil) - defer close(cache.stopChan) - - gotNumRequest := 0 - binary1 := append(wasmHeader, 1) - binary2 := append(wasmHeader, 2) - - // Create a test server which returns 0 for the first two calls, and returns 1 for the following calls. - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if gotNumRequest <= 1 { - w.Write(binary1) - } else { - w.Write(binary2) - } - gotNumRequest++ - })) - defer ts.Close() - wantFilePath1 := filepath.Join(tmpDir, fmt.Sprintf("%x.wasm", sha256.Sum256(binary1))) - wantFilePath2 := filepath.Join(tmpDir, fmt.Sprintf("%x.wasm", sha256.Sum256(binary2))) - var defaultPullPolicy extensions.PullPolicy - - // Get wasm module three times, since checksum is not specified, it will be fetched from module server every time. - // 1st time - gotFilePath, err := cache.Get(ts.URL, "", "namespace.resource", "123456", 0, []byte{}, defaultPullPolicy) - if err != nil { - t.Fatalf("failed to download Wasm module: %v", err) - } - if gotFilePath != wantFilePath1 { - t.Errorf("wasm download path got %v want %v", gotFilePath, wantFilePath1) - } - - // 2nd time - gotFilePath, err = cache.Get(ts.URL, "", "namespace.resource", "123456", 0, []byte{}, defaultPullPolicy) - if err != nil { - t.Fatalf("failed to download Wasm module: %v", err) - } - if gotFilePath != wantFilePath1 { - t.Errorf("wasm download path got %v want %v", gotFilePath, wantFilePath1) - } - - // 3rd time - gotFilePath, err = cache.Get(ts.URL, "", "namespace.resource", "123456", 0, []byte{}, defaultPullPolicy) - if err != nil { - t.Fatalf("failed to download Wasm module: %v", err) - } - if gotFilePath != wantFilePath2 { - t.Errorf("wasm download path got %v want %v", gotFilePath, wantFilePath2) - } - - wantNumRequest := 3 - if gotNumRequest != wantNumRequest { - t.Errorf("wasm download call got %v want %v", gotNumRequest, wantNumRequest) - } -} - -func TestAllInsecureServer(t *testing.T) { - tmpDir := t.TempDir() - cache := NewLocalFileCache(tmpDir, DefaultWasmModulePurgeInterval, DefaultWasmModuleExpiry, []string{"*"}) - defer close(cache.stopChan) - - // Set up a fake registry for OCI images with TLS Server - // Without "insecure" option, this should cause an error. - tos := httptest.NewTLSServer(registry.New()) - defer tos.Close() - ou, err := url.Parse(tos.URL) - if err != nil { - t.Fatal(err) - } - - dockerImageDigest, _ := setupOCIRegistry(t, ou.Host) - ociURLWithTag := fmt.Sprintf("oci://%s/test/valid/docker:v0.1.0", ou.Host) - var defaultPullPolicy extensions.PullPolicy - - gotFilePath, err := cache.Get(ociURLWithTag, "", "namespace.resource", "123456", time.Second*10, []byte{}, defaultPullPolicy) - if err != nil { - t.Fatalf("failed to download Wasm module: %v", err) - } - - wantFilePath := filepath.Join(tmpDir, fmt.Sprintf("%s.wasm", dockerImageDigest)) - if gotFilePath != wantFilePath { - t.Errorf("Wasm module local file path got %v, want %v", gotFilePath, wantFilePath) - } -} diff --git a/pkg/wasm/convert.go b/pkg/wasm/convert.go deleted file mode 100644 index fa9ce470f..000000000 --- a/pkg/wasm/convert.go +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package wasm - -import ( - "sync" - "time" -) - -import ( - udpa "github.com/cncf/xds/go/udpa/type/v1" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - wasm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/wasm/v3" - "github.com/envoyproxy/go-control-plane/pkg/conversion" - "go.uber.org/atomic" - any "google.golang.org/protobuf/types/known/anypb" - extensions "istio.io/api/extensions/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pkg/config/xds" -) - -// MaybeConvertWasmExtensionConfig converts any presence of module remote download to local file. -// It downloads the Wasm module and stores the module locally in the file system. -func MaybeConvertWasmExtensionConfig(resources []*any.Any, cache Cache) bool { - var wg sync.WaitGroup - numResources := len(resources) - wg.Add(numResources) - sendNack := atomic.NewBool(false) - startTime := time.Now() - defer func() { - wasmConfigConversionDuration.Record(float64(time.Since(startTime).Milliseconds())) - }() - - for i := 0; i < numResources; i++ { - go func(i int) { - defer wg.Done() - - newExtensionConfig, nack := convert(resources[i], cache) - if nack { - sendNack.Store(true) - return - } - resources[i] = newExtensionConfig - }(i) - } - - wg.Wait() - return sendNack.Load() -} - -func convert(resource *any.Any, cache Cache) (newExtensionConfig *any.Any, sendNack bool) { - ec := &core.TypedExtensionConfig{} - newExtensionConfig = resource - sendNack = false - status := noRemoteLoad - defer func() { - wasmConfigConversionCount. - With(resultTag.Value(status)). - Increment() - }() - if err := resource.UnmarshalTo(ec); err != nil { - wasmLog.Debugf("failed to unmarshal extension config resource: %v", err) - return - } - - wasmHTTPFilterConfig := &wasm.Wasm{} - // Wasm filter can be configured using typed struct and Wasm filter type - if ec.GetTypedConfig() != nil && ec.GetTypedConfig().TypeUrl == xds.WasmHTTPFilterType { - err := ec.GetTypedConfig().UnmarshalTo(wasmHTTPFilterConfig) - if err != nil { - wasmLog.Debugf("failed to unmarshal extension config resource into Wasm HTTP filter: %v", err) - return - } - } else if ec.GetTypedConfig() == nil || ec.GetTypedConfig().TypeUrl != xds.TypedStructType { - wasmLog.Debugf("cannot find typed struct in %+v", ec) - return - } else { - wasmStruct := &udpa.TypedStruct{} - wasmTypedConfig := ec.GetTypedConfig() - if err := wasmTypedConfig.UnmarshalTo(wasmStruct); err != nil { - wasmLog.Debugf("failed to unmarshal typed config for wasm filter: %v", err) - return - } - - if wasmStruct.TypeUrl != xds.WasmHTTPFilterType { - wasmLog.Debugf("typed extension config %+v does not contain wasm http filter", wasmStruct) - return - } - - if err := conversion.StructToMessage(wasmStruct.Value, wasmHTTPFilterConfig); err != nil { - wasmLog.Debugf("failed to convert extension config struct %+v to Wasm HTTP filter", wasmStruct) - return - } - } - - if wasmHTTPFilterConfig.Config.GetVmConfig().GetCode().GetRemote() == nil { - wasmLog.Debugf("no remote load found in Wasm HTTP filter %+v", wasmHTTPFilterConfig) - return - } - - // Wasm plugin configuration has remote load. From this point, any failure should result as a Nack, - // unless the plugin is marked as fail open. - failOpen := wasmHTTPFilterConfig.Config.GetFailOpen() - sendNack = !failOpen - status = conversionSuccess - - vm := wasmHTTPFilterConfig.Config.GetVmConfig() - envs := vm.GetEnvironmentVariables() - var pullSecret []byte - pullPolicy := extensions.PullPolicy_UNSPECIFIED_POLICY - resourceVersion := "" - if envs != nil { - if sec, found := envs.KeyValues[model.WasmSecretEnv]; found { - if sec == "" { - status = fetchFailure - wasmLog.Errorf("cannot fetch Wasm module %v: missing image pulling secret", wasmHTTPFilterConfig.Config.Name) - return - } - pullSecret = []byte(sec) - } - // Strip all internal env variables from VM env variable. - // These env variables are added by Istio control plane and meant to be consumed by the agent for image pulling control, - // thus should not be leaked to Envoy or the Wasm extension runtime. - delete(envs.KeyValues, model.WasmSecretEnv) - if len(envs.KeyValues) == 0 { - if len(envs.HostEnvKeys) == 0 { - vm.EnvironmentVariables = nil - } else { - envs.KeyValues = nil - } - } - - if ps, found := envs.KeyValues[model.WasmPolicyEnv]; found { - if p, found := extensions.PullPolicy_value[ps]; found { - pullPolicy = extensions.PullPolicy(p) - } - } - - resourceVersion = envs.KeyValues[model.WasmResourceVersionEnv] - } - remote := vm.GetCode().GetRemote() - httpURI := remote.GetHttpUri() - if httpURI == nil { - status = missRemoteFetchHint - wasmLog.Errorf("wasm remote fetch %+v does not have httpUri specified", remote) - return - } - // checksum sent by istiod can be "nil" if not set by user - magic value used to avoid unmarshaling errors - if remote.Sha256 == "nil" { - remote.Sha256 = "" - } - timeout := time.Duration(0) - if remote.GetHttpUri().Timeout != nil { - timeout = remote.GetHttpUri().Timeout.AsDuration() - } - f, err := cache.Get(httpURI.GetUri(), remote.Sha256, wasmHTTPFilterConfig.Config.Name, resourceVersion, timeout, pullSecret, pullPolicy) - if err != nil { - status = fetchFailure - wasmLog.Errorf("cannot fetch Wasm module %v: %v", remote.GetHttpUri().GetUri(), err) - return - } - - // Rewrite remote fetch to local file. - vm.Code = &core.AsyncDataSource{ - Specifier: &core.AsyncDataSource_Local{ - Local: &core.DataSource{ - Specifier: &core.DataSource_Filename{ - Filename: f, - }, - }, - }, - } - - wasmTypedConfig, err := any.New(wasmHTTPFilterConfig) - if err != nil { - status = marshalFailure - wasmLog.Errorf("failed to marshal new wasm HTTP filter %+v to protobuf Any: %v", wasmHTTPFilterConfig, err) - return - } - ec.TypedConfig = wasmTypedConfig - wasmLog.Debugf("new extension config resource %+v", ec) - - nec, err := any.New(ec) - if err != nil { - status = marshalFailure - wasmLog.Errorf("failed to marshal new extension config resource: %v", err) - return - } - - // At this point, we are certain that wasm module has been downloaded and config is rewritten. - // ECDS has been rewritten successfully and should not nack. - newExtensionConfig = nec - sendNack = false - return -} diff --git a/pkg/wasm/convert_test.go b/pkg/wasm/convert_test.go deleted file mode 100644 index 4b0c226fc..000000000 --- a/pkg/wasm/convert_test.go +++ /dev/null @@ -1,343 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package wasm - -import ( - "errors" - "fmt" - "net/url" - "reflect" - "testing" - "time" -) - -import ( - udpa "github.com/cncf/xds/go/udpa/type/v1" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - wasm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/wasm/v3" - v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/wasm/v3" - "github.com/envoyproxy/go-control-plane/pkg/conversion" - resource "github.com/envoyproxy/go-control-plane/pkg/resource/v3" - "google.golang.org/protobuf/proto" - any "google.golang.org/protobuf/types/known/anypb" - "google.golang.org/protobuf/types/known/structpb" - extensions "istio.io/api/extensions/v1alpha1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" - "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" - "github.com/apache/dubbo-go-pixiu/pkg/config/xds" -) - -type mockCache struct { - wantSecret []byte - wantPolicy extensions.PullPolicy -} - -func (c *mockCache) Get( - downloadURL, checksum, resourceName, resourceVersion string, - timeout time.Duration, pullSecret []byte, pullPolicy extensions.PullPolicy) (string, error) { - url, _ := url.Parse(downloadURL) - query := url.Query() - - module := query.Get("module") - errMsg := query.Get("error") - var err error - if errMsg != "" { - err = errors.New(errMsg) - } - if c.wantSecret != nil && !reflect.DeepEqual(c.wantSecret, pullSecret) { - return "", fmt.Errorf("wrong secret for %v, got %q want %q", downloadURL, string(pullSecret), c.wantSecret) - } - if c.wantPolicy != pullPolicy { - return "", fmt.Errorf("wrong pull policy for %v, got %v want %v", downloadURL, pullPolicy, c.wantPolicy) - } - - return module, err -} -func (c *mockCache) Cleanup() {} - -func TestWasmConvert(t *testing.T) { - cases := []struct { - name string - input []*core.TypedExtensionConfig - wantOutput []*core.TypedExtensionConfig - wantNack bool - }{ - { - name: "remote load success", - input: []*core.TypedExtensionConfig{ - extensionConfigMap["remote-load-success"], - }, - wantOutput: []*core.TypedExtensionConfig{ - extensionConfigMap["remote-load-success-local-file"], - }, - wantNack: false, - }, - { - name: "remote load fail", - input: []*core.TypedExtensionConfig{ - extensionConfigMap["remote-load-fail"], - }, - wantOutput: []*core.TypedExtensionConfig{ - extensionConfigMap["remote-load-fail"], - }, - wantNack: true, - }, - { - name: "mix", - input: []*core.TypedExtensionConfig{ - extensionConfigMap["remote-load-fail"], - extensionConfigMap["remote-load-success"], - }, - wantOutput: []*core.TypedExtensionConfig{ - extensionConfigMap["remote-load-fail"], - extensionConfigMap["remote-load-success-local-file"], - }, - wantNack: true, - }, - { - name: "remote load fail open", - input: []*core.TypedExtensionConfig{ - extensionConfigMap["remote-load-fail-open"], - }, - wantOutput: []*core.TypedExtensionConfig{ - extensionConfigMap["remote-load-fail-open"], - }, - wantNack: false, - }, - { - name: "no typed struct", - input: []*core.TypedExtensionConfig{ - extensionConfigMap["empty"], - }, - wantOutput: []*core.TypedExtensionConfig{ - extensionConfigMap["empty"], - }, - wantNack: false, - }, - { - name: "no wasm", - input: []*core.TypedExtensionConfig{ - extensionConfigMap["no-wasm"], - }, - wantOutput: []*core.TypedExtensionConfig{ - extensionConfigMap["no-wasm"], - }, - wantNack: false, - }, - { - name: "no remote load", - input: []*core.TypedExtensionConfig{ - extensionConfigMap["no-remote-load"], - }, - wantOutput: []*core.TypedExtensionConfig{ - extensionConfigMap["no-remote-load"], - }, - wantNack: false, - }, - { - name: "no uri", - input: []*core.TypedExtensionConfig{ - extensionConfigMap["no-http-uri"], - }, - wantOutput: []*core.TypedExtensionConfig{ - extensionConfigMap["no-http-uri"], - }, - wantNack: true, - }, - { - name: "secret", - input: []*core.TypedExtensionConfig{ - extensionConfigMap["remote-load-secret"], - }, - wantOutput: []*core.TypedExtensionConfig{ - extensionConfigMap["remote-load-success-local-file"], - }, - wantNack: false, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - resources := make([]*any.Any, 0, len(c.input)) - for _, i := range c.input { - resources = append(resources, util.MessageToAny(i)) - } - mc := &mockCache{} - gotNack := MaybeConvertWasmExtensionConfig(resources, mc) - if len(resources) != len(c.wantOutput) { - t.Fatalf("wasm config conversion number of configuration got %v want %v", len(resources), len(c.wantOutput)) - } - for i, output := range resources { - ec := &core.TypedExtensionConfig{} - if err := output.UnmarshalTo(ec); err != nil { - t.Errorf("wasm config conversion output %v failed to unmarshal", output) - continue - } - if !proto.Equal(ec, c.wantOutput[i]) { - t.Errorf("wasm config conversion output index %d got %v want %v", i, ec, c.wantOutput[i]) - } - } - if gotNack != c.wantNack { - t.Errorf("wasm config conversion send nack got %v want %v", gotNack, c.wantNack) - } - }) - } -} - -func buildTypedStructExtensionConfig(name string, wasm *wasm.Wasm) *core.TypedExtensionConfig { - ws, _ := conversion.MessageToStruct(wasm) - return &core.TypedExtensionConfig{ - Name: name, - TypedConfig: util.MessageToAny( - &udpa.TypedStruct{ - TypeUrl: xds.WasmHTTPFilterType, - Value: ws, - }, - ), - } -} - -func buildWasmExtensionConfig(name string, wasm *wasm.Wasm) *core.TypedExtensionConfig { - return &core.TypedExtensionConfig{ - Name: name, - TypedConfig: util.MessageToAny(wasm), - } -} - -var extensionConfigMap = map[string]*core.TypedExtensionConfig{ - "empty": { - Name: "empty", - TypedConfig: util.MessageToAny( - &structpb.Struct{}, - ), - }, - "no-wasm": { - Name: "no-wasm", - TypedConfig: util.MessageToAny( - &udpa.TypedStruct{TypeUrl: resource.APITypePrefix + "sometype"}, - ), - }, - "no-remote-load": buildTypedStructExtensionConfig("no-remote-load", &wasm.Wasm{ - Config: &v3.PluginConfig{ - Vm: &v3.PluginConfig_VmConfig{ - VmConfig: &v3.VmConfig{ - Runtime: "envoy.wasm.runtime.null", - Code: &core.AsyncDataSource{Specifier: &core.AsyncDataSource_Local{ - Local: &core.DataSource{ - Specifier: &core.DataSource_InlineString{ - InlineString: "envoy.wasm.metadata_exchange", - }, - }, - }}, - }, - }, - }, - }), - "no-http-uri": buildTypedStructExtensionConfig("no-remote-load", &wasm.Wasm{ - Config: &v3.PluginConfig{ - Vm: &v3.PluginConfig_VmConfig{ - VmConfig: &v3.VmConfig{ - Code: &core.AsyncDataSource{Specifier: &core.AsyncDataSource_Remote{ - Remote: &core.RemoteDataSource{}, - }}, - }, - }, - }, - }), - "remote-load-success": buildTypedStructExtensionConfig("remote-load-success", &wasm.Wasm{ - Config: &v3.PluginConfig{ - Vm: &v3.PluginConfig_VmConfig{ - VmConfig: &v3.VmConfig{ - Code: &core.AsyncDataSource{Specifier: &core.AsyncDataSource_Remote{ - Remote: &core.RemoteDataSource{ - HttpUri: &core.HttpUri{ - Uri: "http://test?module=test.wasm", - }, - }, - }}, - }, - }, - }, - }), - "remote-load-success-local-file": buildWasmExtensionConfig("remote-load-success", &wasm.Wasm{ - Config: &v3.PluginConfig{ - Vm: &v3.PluginConfig_VmConfig{ - VmConfig: &v3.VmConfig{ - Code: &core.AsyncDataSource{Specifier: &core.AsyncDataSource_Local{ - Local: &core.DataSource{ - Specifier: &core.DataSource_Filename{ - Filename: "test.wasm", - }, - }, - }}, - }, - }, - }, - }), - "remote-load-fail": buildTypedStructExtensionConfig("remote-load-fail", &wasm.Wasm{ - Config: &v3.PluginConfig{ - Vm: &v3.PluginConfig_VmConfig{ - VmConfig: &v3.VmConfig{ - Code: &core.AsyncDataSource{Specifier: &core.AsyncDataSource_Remote{ - Remote: &core.RemoteDataSource{ - HttpUri: &core.HttpUri{ - Uri: "http://test?module=test.wasm&error=download-error", - }, - }, - }}, - }, - }, - }, - }), - "remote-load-fail-open": buildTypedStructExtensionConfig("remote-load-fail", &wasm.Wasm{ - Config: &v3.PluginConfig{ - Vm: &v3.PluginConfig_VmConfig{ - VmConfig: &v3.VmConfig{ - Code: &core.AsyncDataSource{Specifier: &core.AsyncDataSource_Remote{ - Remote: &core.RemoteDataSource{ - HttpUri: &core.HttpUri{ - Uri: "http://test?module=test.wasm&error=download-error", - }, - }, - }}, - }, - }, - FailOpen: true, - }, - }), - "remote-load-secret": buildTypedStructExtensionConfig("remote-load-success", &wasm.Wasm{ - Config: &v3.PluginConfig{ - Vm: &v3.PluginConfig_VmConfig{ - VmConfig: &v3.VmConfig{ - Code: &core.AsyncDataSource{Specifier: &core.AsyncDataSource_Remote{ - Remote: &core.RemoteDataSource{ - HttpUri: &core.HttpUri{ - Uri: "http://test?module=test.wasm", - }, - }, - }}, - EnvironmentVariables: &v3.EnvironmentVariables{ - KeyValues: map[string]string{ - model.WasmSecretEnv: "secret", - }, - }, - }, - }, - }, - }), -} diff --git a/pkg/wasm/httpfetcher.go b/pkg/wasm/httpfetcher.go deleted file mode 100644 index f43330688..000000000 --- a/pkg/wasm/httpfetcher.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package wasm - -import ( - "fmt" - "io" - "net/http" - "time" -) - -import ( - "github.com/cenkalti/backoff/v4" -) - -// HTTPFetcher fetches remote wasm module with HTTP get. -type HTTPFetcher struct { - defaultClient *http.Client - initialBackoff time.Duration -} - -// NewHTTPFetcher create a new HTTP remote wasm module fetcher. -func NewHTTPFetcher() *HTTPFetcher { - return &HTTPFetcher{ - defaultClient: &http.Client{ - Timeout: 5 * time.Second, - }, - initialBackoff: time.Millisecond * 500, - } -} - -// Fetch downloads a wasm module with HTTP get. -func (f *HTTPFetcher) Fetch(url string, timeout time.Duration) ([]byte, error) { - c := f.defaultClient - if timeout != 0 { - c = &http.Client{ - Timeout: timeout, - } - } - attempts := 0 - - b := backoff.NewExponentialBackOff() - b.InitialInterval = f.initialBackoff - b.Reset() - var lastError error - for attempts < 5 { - attempts++ - resp, err := c.Get(url) - if err != nil { - lastError = err - wasmLog.Debugf("wasm module download request failed: %v", err) - time.Sleep(b.NextBackOff()) - continue - } - if resp.StatusCode == http.StatusOK { - body, err := io.ReadAll(resp.Body) - resp.Body.Close() - return body, err - } - lastError = fmt.Errorf("wasm module download request failed: status code %v", resp.StatusCode) - if retryable(resp.StatusCode) { - body, _ := io.ReadAll(resp.Body) - wasmLog.Debugf("wasm module download failed: status code %v, body %v", resp.StatusCode, string(body)) - resp.Body.Close() - time.Sleep(b.NextBackOff()) - continue - } - resp.Body.Close() - break - } - return nil, fmt.Errorf("wasm module download failed, last error: %v", lastError) -} - -func retryable(code int) bool { - return code >= 500 && - !(code == http.StatusNotImplemented || - code == http.StatusHTTPVersionNotSupported || - code == http.StatusNetworkAuthenticationRequired) -} diff --git a/pkg/wasm/httpfetcher_test.go b/pkg/wasm/httpfetcher_test.go deleted file mode 100644 index d9f16dafe..000000000 --- a/pkg/wasm/httpfetcher_test.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package wasm - -import ( - "fmt" - "net/http" - "net/http/httptest" - "testing" - "time" -) - -func TestWasmHTTPFetch(t *testing.T) { - var ts *httptest.Server - - cases := []struct { - name string - handler func(http.ResponseWriter, *http.Request, int) - wantNumRequest int - wantError string - }{ - { - name: "download ok", - handler: func(w http.ResponseWriter, r *http.Request, num int) { - fmt.Fprintln(w, "wasm") - }, - wantNumRequest: 1, - }, - { - name: "download retry", - handler: func(w http.ResponseWriter, r *http.Request, num int) { - if num <= 2 { - w.WriteHeader(500) - } else { - fmt.Fprintln(w, "wasm") - } - }, - wantNumRequest: 4, - }, - { - name: "download max retry", - handler: func(w http.ResponseWriter, r *http.Request, num int) { - w.WriteHeader(500) - }, - wantNumRequest: 5, - wantError: "wasm module download failed, last error: wasm module download request failed: status code 500", - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - gotNumRequest := 0 - wantWasmModule := "wasm\n" - ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - c.handler(w, r, gotNumRequest) - gotNumRequest++ - })) - defer ts.Close() - fetcher := NewHTTPFetcher() - fetcher.initialBackoff = time.Microsecond - b, err := fetcher.Fetch(ts.URL, 0) - if c.wantNumRequest != gotNumRequest { - t.Errorf("Wasm download request got %v, want %v", gotNumRequest, c.wantNumRequest) - } - if c.wantError != "" { - if err == nil { - t.Errorf("Wasm download got no error, want error `%v`", c.wantError) - } else if c.wantError != err.Error() { - t.Errorf("Wasm download got error `%v`, want error `%v`", err, c.wantError) - } - } else if string(b) != wantWasmModule { - t.Errorf("downloaded wasm module got %v, want wasm", string(b)) - } - }) - } -} diff --git a/pkg/wasm/imagefetcher.go b/pkg/wasm/imagefetcher.go deleted file mode 100644 index c2c2df213..000000000 --- a/pkg/wasm/imagefetcher.go +++ /dev/null @@ -1,365 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package wasm - -import ( - "archive/tar" - "bytes" - "compress/gzip" - "context" - "crypto/tls" - "fmt" - "io" - "path/filepath" - "reflect" - "strings" -) - -import ( - "github.com/docker/cli/cli/config/configfile" - dtypes "github.com/docker/cli/cli/config/types" - "github.com/google/go-containerregistry/pkg/authn" - "github.com/google/go-containerregistry/pkg/name" - v1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/google/go-containerregistry/pkg/v1/remote" - "github.com/google/go-containerregistry/pkg/v1/types" - "github.com/hashicorp/go-multierror" -) - -// This file implements the fetcher of "Wasm Image Specification" compatible container images. -// The spec is here https://github.com/solo-io/wasm/blob/master/spec/README.md. -// Basically, this supports fetching and unpackaging three types of container images containing a Wasm binary. -type ImageFetcherOption struct { - // TODO(mathetake) Add signature verification stuff. - PullSecret []byte - Insecure bool -} - -func (o *ImageFetcherOption) useDefaultKeyChain() bool { - return o.PullSecret == nil -} - -func (o ImageFetcherOption) String() string { - if o.PullSecret == nil { - return fmt.Sprintf("{Insecure: %v}", o.Insecure) - } - return fmt.Sprintf("{Insecure: %v, PullSecret: }", o.Insecure) -} - -type ImageFetcher struct { - fetchOpts []remote.Option -} - -func NewImageFetcher(ctx context.Context, opt ImageFetcherOption) *ImageFetcher { - fetchOpts := make([]remote.Option, 0, 2) - // TODO(mathetake): have "Anonymous" option? - if opt.useDefaultKeyChain() { - // Note that default key chain reads the docker config from DOCKER_CONFIG - // so must set the envvar when reaching this branch is expected. - fetchOpts = append(fetchOpts, remote.WithAuthFromKeychain(authn.DefaultKeychain)) - } else { - fetchOpts = append(fetchOpts, remote.WithAuthFromKeychain(&wasmKeyChain{data: opt.PullSecret})) - } - - if opt.Insecure { - t := remote.DefaultTransport.Clone() - t.TLSClientConfig = &tls.Config{ - InsecureSkipVerify: opt.Insecure, //nolint: gosec - } - fetchOpts = append(fetchOpts, remote.WithTransport(t)) - } - - return &ImageFetcher{ - fetchOpts: append(fetchOpts, remote.WithContext(ctx)), - } -} - -// PrepareFetch is the entrypoint for fetching Wasm binary from Wasm Image Specification compatible images. -// Wasm binary is not fetched immediately, but returned by `binaryFetcher` function, which is returned by PrepareFetch. -// By this way, we can have another chance to check cache with `actualDigest` without downloading the OCI image. -func (o *ImageFetcher) PrepareFetch(url string) (binaryFetcher func() ([]byte, error), actualDigest string, err error) { - ref, err := name.ParseReference(url) - if err != nil { - err = fmt.Errorf("could not parse url in image reference: %v", err) - return - } - - // fallback to http based request, inspired by [helm](https://github.com/helm/helm/blob/12f1bc0acdeb675a8c50a78462ed3917fb7b2e37/pkg/registry/client.go#L594) - // only deal with https fallback instead of attributing all other type of errors to URL parsing error - desc, err := remote.Get(ref, o.fetchOpts...) - if err != nil && strings.Contains(err.Error(), "server gave HTTP response") { - wasmLog.Infof("fetch with plain text from %s", url) - ref, err = name.ParseReference(url, name.Insecure) - if err == nil { - desc, err = remote.Get(ref, o.fetchOpts...) - } - } - - if err != nil { - err = fmt.Errorf("could not fetch manifest: %v", err) - return - } - - // Fetch image. - img, err := desc.Image() - if err != nil { - err = fmt.Errorf("could not fetch image: %v", err) - return - } - - // Check Manifest's digest if expManifestDigest is not empty. - d, _ := img.Digest() - actualDigest = d.Hex - binaryFetcher = func() ([]byte, error) { - manifest, err := img.Manifest() - if err != nil { - return nil, fmt.Errorf("could not retrieve manifest: %v", err) - } - - if manifest.MediaType == types.DockerManifestSchema2 { - // This case, assume we have docker images with "application/vnd.docker.distribution.manifest.v2+json" - // as the manifest media type. Note that the media type of manifest is Docker specific and - // all OCI images would have an empty string in .MediaType field. - ret, err := extractDockerImage(img) - if err != nil { - return nil, fmt.Errorf("could not extract Wasm file from the image as Docker container %v", err) - } - return ret, nil - } - - // We try to parse it as the "compat" variant image with a single "application/vnd.oci.image.layer.v1.tar+gzip" layer. - ret, errCompat := extractOCIStandardImage(img) - if errCompat == nil { - return ret, nil - } - - // Otherwise, we try to parse it as the *oci* variant image with custom artifact media types. - ret, errOCI := extractOCIArtifactImage(img) - if errOCI == nil { - return ret, nil - } - - // We failed to parse the image in any format, so wrap the errors and return. - return nil, fmt.Errorf("the given image is in invalid format as an OCI image: %v", - multierror.Append(err, - fmt.Errorf("could not parse as compat variant: %v", errCompat), - fmt.Errorf("could not parse as oci variant: %v", errOCI), - ), - ) - } - return -} - -// extractDockerImage extracts the Wasm binary from the -// *compat* variant Wasm image with the standard Docker media type: application/vnd.docker.image.rootfs.diff.tar.gzip. -// https://github.com/solo-io/wasm/blob/master/spec/spec-compat.md#specification -func extractDockerImage(img v1.Image) ([]byte, error) { - layers, err := img.Layers() - if err != nil { - return nil, fmt.Errorf("could not fetch layers: %v", err) - } - - // The image must be single-layered. - if len(layers) != 1 { - return nil, fmt.Errorf("number of layers must be 1 but got %d", len(layers)) - } - - layer := layers[0] - mt, err := layer.MediaType() - if err != nil { - return nil, fmt.Errorf("could not get media type: %v", err) - } - - // Media type must be application/vnd.docker.image.rootfs.diff.tar.gzip. - if mt != types.DockerLayer { - return nil, fmt.Errorf("invalid media type %s (expect %s)", mt, types.DockerLayer) - } - - r, err := layer.Compressed() - if err != nil { - return nil, fmt.Errorf("could not get layer content: %v", err) - } - defer r.Close() - - ret, err := extractWasmPluginBinary(r) - if err != nil { - return nil, fmt.Errorf("could not extract wasm binary: %v", err) - } - return ret, nil -} - -// extractOCIStandardImage extracts the Wasm binary from the -// *compat* variant Wasm image with the standard OCI media type: application/vnd.oci.image.layer.v1.tar+gzip. -// https://github.com/solo-io/wasm/blob/master/spec/spec-compat.md#specification -func extractOCIStandardImage(img v1.Image) ([]byte, error) { - layers, err := img.Layers() - if err != nil { - return nil, fmt.Errorf("could not fetch layers: %v", err) - } - - // The image must be single-layered. - if len(layers) != 1 { - return nil, fmt.Errorf("number of layers must be 1 but got %d", len(layers)) - } - - layer := layers[0] - mt, err := layer.MediaType() - if err != nil { - return nil, fmt.Errorf("could not get media type: %v", err) - } - - // Check if the layer is "application/vnd.oci.image.layer.v1.tar+gzip". - if types.OCILayer != mt { - return nil, fmt.Errorf("invalid media type %s (expect %s)", mt, types.OCILayer) - } - - r, err := layer.Compressed() - if err != nil { - return nil, fmt.Errorf("could not get layer content: %v", err) - } - defer r.Close() - - ret, err := extractWasmPluginBinary(r) - if err != nil { - return nil, fmt.Errorf("could not extract wasm binary: %v", err) - } - return ret, nil -} - -// Extracts the Wasm plugin binary named "plugin.wasm" in a given reader for tar.gz. -// This is only used for *compat* variant. -func extractWasmPluginBinary(r io.Reader) ([]byte, error) { - gr, err := gzip.NewReader(r) - if err != nil { - return nil, fmt.Errorf("failed to parse layer as tar.gz: %v", err) - } - - // The target file name for Wasm binary. - // https://github.com/solo-io/wasm/blob/master/spec/spec-compat.md#specification - const wasmPluginFileName = "plugin.wasm" - - // Search for the file walking through the archive. - tr := tar.NewReader(gr) - for { - h, err := tr.Next() - if err == io.EOF { - break - } else if err != nil { - return nil, err - } - - ret := make([]byte, h.Size) - if filepath.Base(h.Name) == wasmPluginFileName { - _, err := io.ReadFull(tr, ret) - if err != nil { - return nil, fmt.Errorf("failed to read %s: %v", wasmPluginFileName, err) - } - return ret, nil - } - } - return nil, fmt.Errorf("%s not found in the archive", wasmPluginFileName) -} - -// extractOCIArtifactImage extracts the Wasm binary from the -// *oci* variant Wasm image: https://github.com/solo-io/wasm/blob/master/spec/spec.md#format -func extractOCIArtifactImage(img v1.Image) ([]byte, error) { - layers, err := img.Layers() - if err != nil { - return nil, fmt.Errorf("could not fetch layers: %v", err) - } - - // The image must be two-layered. - if len(layers) != 2 { - return nil, fmt.Errorf("number of layers must be 2 but got %d", len(layers)) - } - - // The layer type of the Wasm binary itself in *oci* variant. - const wasmLayerMediaType = "application/vnd.module.wasm.content.layer.v1+wasm" - - // Find the target layer walking through the layers. - var layer v1.Layer - for _, l := range layers { - mt, err := l.MediaType() - if err != nil { - return nil, fmt.Errorf("could not retrieve the media type: %v", err) - } - if mt == wasmLayerMediaType { - layer = l - break - } - } - - if layer == nil { - return nil, fmt.Errorf("could not find the layer of type %s", wasmLayerMediaType) - } - - // Somehow go-containerregistry recognizes custom artifact layers as compressed ones, - // while the Solo's Wasm layer is actually uncompressed and therefore - // the content itself is a raw Wasm binary. So using "Uncompressed()" here result in errors - // since internally it tries to umcompress it as gzipped blob. - r, err := layer.Compressed() - if err != nil { - return nil, fmt.Errorf("could not get layer content: %v", err) - } - defer r.Close() - - // Just read it since the content is already a raw Wasm binary as mentioned above. - ret, err := io.ReadAll(r) - if err != nil { - return nil, fmt.Errorf("could not extract wasm binary: %v", err) - } - return ret, nil -} - -type wasmKeyChain struct { - data []byte -} - -// Resolve an image reference to a credential. -// The function code is borrowed from https://github.com/google/go-containerregistry/blob/v0.8.0/pkg/authn/keychain.go#L65, -// by making it take dockerconfigjson directly as bytes instead of reading from files. -func (k *wasmKeyChain) Resolve(target authn.Resource) (authn.Authenticator, error) { - if reflect.DeepEqual(k.data, []byte("null")) { - // Filter out key chain with content "null" to prevent crash at underlying docker library. - // Remove this check when https://github.com/docker/cli/pull/3434 is merged. - return nil, fmt.Errorf("") - } - reader := bytes.NewReader(k.data) - cf := configfile.ConfigFile{} - if err := cf.LoadFromReader(reader); err != nil { - return nil, err - } - key := target.RegistryStr() - if key == name.DefaultRegistry { - key = authn.DefaultAuthKey - } - cfg, err := cf.GetAuthConfig(key) - if err != nil { - return nil, err - } - - empty := dtypes.AuthConfig{} - if cfg == empty { - return authn.Anonymous, nil - } - authConfig := authn.AuthConfig{ - Username: cfg.Username, - Password: cfg.Password, - Auth: cfg.Auth, - IdentityToken: cfg.IdentityToken, - RegistryToken: cfg.RegistryToken, - } - return authn.FromConfig(authConfig), nil -} diff --git a/pkg/wasm/imagefetcher_test.go b/pkg/wasm/imagefetcher_test.go deleted file mode 100644 index 83e978350..000000000 --- a/pkg/wasm/imagefetcher_test.go +++ /dev/null @@ -1,609 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package wasm - -import ( - "archive/tar" - "bytes" - "compress/gzip" - "crypto/sha256" - "encoding/base64" - "encoding/hex" - "fmt" - "io" - "net/http/httptest" - "net/url" - "reflect" - "strings" - "testing" -) - -import ( - "github.com/google/go-containerregistry/pkg/authn" - "github.com/google/go-containerregistry/pkg/crane" - "github.com/google/go-containerregistry/pkg/name" - "github.com/google/go-containerregistry/pkg/registry" - v1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/google/go-containerregistry/pkg/v1/empty" - "github.com/google/go-containerregistry/pkg/v1/mutate" - "github.com/google/go-containerregistry/pkg/v1/partial" - "github.com/google/go-containerregistry/pkg/v1/random" - "github.com/google/go-containerregistry/pkg/v1/remote" - "github.com/google/go-containerregistry/pkg/v1/types" -) - -func TestImageFetcherOption_useDefaultKeyChain(t *testing.T) { - cases := []struct { - name string - opt ImageFetcherOption - exp bool - }{ - {name: "default key chain", exp: true}, - {name: "use secret config", opt: ImageFetcherOption{PullSecret: []byte("secret")}}, - {name: "missing secret", opt: ImageFetcherOption{}, exp: true}, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - actual := c.opt.useDefaultKeyChain() - if actual != c.exp { - t.Errorf("useDefaultKeyChain got %v want %v", actual, c.exp) - } - }) - } -} - -func TestImageFetcher_Fetch(t *testing.T) { - // Fetcher with anonymous auth. - fetcher := ImageFetcher{fetchOpts: []remote.Option{remote.WithAuth(authn.Anonymous)}} - - // Set up a fake registry. - s := httptest.NewServer(registry.New()) - defer s.Close() - u, err := url.Parse(s.URL) - if err != nil { - t.Fatal(err) - } - - t.Run("docker image", func(t *testing.T) { - ref := fmt.Sprintf("%s/test/valid/docker", u.Host) - exp := "this is wasm plugin" - - // Create docker layer. - l, err := newMockLayer(types.DockerLayer, - map[string][]byte{"plugin.wasm": []byte(exp)}) - if err != nil { - t.Fatal(err) - } - img, err := mutate.Append(empty.Image, mutate.Addendum{Layer: l}) - if err != nil { - t.Fatal(err) - } - - // Set manifest type. - manifest, err := img.Manifest() - if err != nil { - t.Fatal(err) - } - manifest.MediaType = types.DockerManifestSchema2 - - // Push image to the registry. - err = crane.Push(img, ref) - if err != nil { - t.Fatal(err) - } - - // Fetch docker image with digest - d, err := img.Digest() - if err != nil { - t.Fatal(err) - } - - // Fetch OCI image. - binaryFetcher, actualDiget, err := fetcher.PrepareFetch(ref) - if err != nil { - t.Fatal(err) - } - actual, err := binaryFetcher() - if err != nil { - t.Fatal(err) - } - if string(actual) != exp { - t.Errorf("ImageFetcher.binaryFetcher got %s, but want '%s'", string(actual), exp) - } - if actualDiget != d.Hex { - t.Errorf("ImageFetcher.binaryFetcher got digest %s, but want '%s'", actualDiget, d.Hex) - } - }) - - t.Run("OCI standard", func(t *testing.T) { - ref := fmt.Sprintf("%s/test/valid/oci_standard", u.Host) - exp := "this is wasm plugin" - - // Create OCI compressed layer. - l, err := newMockLayer(types.OCILayer, - map[string][]byte{"plugin.wasm": []byte(exp)}) - if err != nil { - t.Fatal(err) - } - img, err := mutate.Append(empty.Image, mutate.Addendum{Layer: l}) - if err != nil { - t.Fatal(err) - } - img = mutate.MediaType(img, types.OCIManifestSchema1) - - // Push image to the registry. - err = crane.Push(img, ref) - if err != nil { - t.Fatal(err) - } - - // Fetch OCI image with digest - d, err := img.Digest() - if err != nil { - t.Fatal(err) - } - - // Fetch OCI image. - binaryFetcher, actualDiget, err := fetcher.PrepareFetch(ref) - if err != nil { - t.Fatal(err) - } - actual, err := binaryFetcher() - if err != nil { - t.Fatal(err) - } - if string(actual) != exp { - t.Errorf("ImageFetcher.binaryFetcher got %s, but want '%s'", string(actual), exp) - } - if actualDiget != d.Hex { - t.Errorf("ImageFetcher.binaryFetcher got digest %s, but want '%s'", actualDiget, d.Hex) - } - }) - - t.Run("OCI artifact", func(t *testing.T) { - ref := fmt.Sprintf("%s/test/valid/oci_artifact", u.Host) - - // Create the image with custom media types. - wasmLayer, err := random.Layer(1000, "application/vnd.module.wasm.content.layer.v1+wasm") - if err != nil { - t.Fatal(err) - } - configLayer, err := random.Layer(1000, "application/vnd.module.wasm.config.v1+json") - if err != nil { - t.Fatal(err) - } - img, err := mutate.Append(empty.Image, mutate.Addendum{Layer: wasmLayer}, mutate.Addendum{Layer: configLayer}) - if err != nil { - t.Fatal(err) - } - img = mutate.MediaType(img, types.OCIManifestSchema1) - - // Push image to the registry. - err = crane.Push(img, ref) - if err != nil { - t.Fatal(err) - } - - // Retrieve the wanted image content. - wantReader, err := wasmLayer.Compressed() - if err != nil { - t.Fatal(err) - } - defer wantReader.Close() - - want, err := io.ReadAll(wantReader) - if err != nil { - t.Fatal(err) - } - - // Fetch OCI image with digest - d, err := img.Digest() - if err != nil { - t.Fatal(err) - } - - // Fetch OCI image. - binaryFetcher, actualDiget, err := fetcher.PrepareFetch(ref) - if err != nil { - t.Fatal(err) - } - actual, err := binaryFetcher() - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(actual, want) { - t.Errorf("ImageFetcher.binaryFetcher got %s, but want '%s'", string(actual), string(want)) - } - if actualDiget != d.Hex { - t.Errorf("ImageFetcher.binaryFetcher got digest %s, but want '%s'", actualDiget, d.Hex) - } - }) - - t.Run("invalid image", func(t *testing.T) { - ref := fmt.Sprintf("%s/test/invalid", u.Host) - - l, err := newMockLayer(types.OCIUncompressedLayer, map[string][]byte{"not-wasm.txt": []byte("a")}) - if err != nil { - t.Fatal(err) - } - img, err := mutate.Append(empty.Image, mutate.Addendum{Layer: l}) - if err != nil { - t.Fatal(err) - } - img = mutate.MediaType(img, types.OCIManifestSchema1) - - // Push image to the registry. - err = crane.Push(img, ref) - if err != nil { - t.Fatal(err) - } - - // Try to fetch. - binaryFetcher, _, err := fetcher.PrepareFetch(ref) - if err != nil { - t.Fatal(err) - } - actual, err := binaryFetcher() - if actual != nil { - t.Errorf("ImageFetcher.binaryFetcher got %s, but want nil", string(actual)) - } - - expErr := `the given image is in invalid format as an OCI image: 2 errors occurred: - * could not parse as compat variant: invalid media type application/vnd.oci.image.layer.v1.tar (expect application/vnd.oci.image.layer.v1.tar+gzip) - * could not parse as oci variant: number of layers must be 2 but got 1` - if actual := strings.TrimSpace(err.Error()); actual != expErr { - t.Errorf("ImageFetcher.binaryFetcher get unexpected error '%v', but want '%v'", actual, expErr) - } - }) -} - -func TestExtractDockerImage(t *testing.T) { - t.Run("valid", func(t *testing.T) { - exp := "this is wasm binary" - l, err := newMockLayer(types.DockerLayer, map[string][]byte{ - "plugin.wasm": []byte(exp), - }) - if err != nil { - t.Fatal(err) - } - img, err := mutate.Append(empty.Image, mutate.Addendum{Layer: l}) - if err != nil { - t.Fatal(err) - } - actual, err := extractDockerImage(img) - if err != nil { - t.Fatalf("extractDockerImage failed: %v", err) - } - - if string(actual) != exp { - t.Fatalf("got %s, but want %s", string(actual), exp) - } - }) - - t.Run("multiple layers", func(t *testing.T) { - l, err := newMockLayer(types.DockerLayer, nil) - if err != nil { - t.Fatal(err) - } - img := empty.Image - for i := 0; i < 2; i++ { - img, err = mutate.Append(img, mutate.Addendum{Layer: l}) - if err != nil { - t.Fatal(err) - } - } - _, err = extractDockerImage(img) - if err == nil || !strings.Contains(err.Error(), "number of layers must be") { - t.Fatal("extractDockerImage should fail due to invalid number of layers") - } - }) - - t.Run("invalid media type", func(t *testing.T) { - l, err := newMockLayer(types.DockerPluginConfig, nil) - if err != nil { - t.Fatal(err) - } - img, err := mutate.Append(empty.Image, mutate.Addendum{Layer: l}) - if err != nil { - t.Fatal(err) - } - _, err = extractDockerImage(img) - if err == nil || !strings.Contains(err.Error(), "invalid media type") { - t.Fatal("extractDockerImage should fail due to invalid media type") - } - }) -} - -func TestExtractOCIStandardImage(t *testing.T) { - t.Run("valid", func(t *testing.T) { - exp := "this is wasm binary" - l, err := newMockLayer(types.OCILayer, map[string][]byte{ - "plugin.wasm": []byte(exp), - }) - if err != nil { - t.Fatal(err) - } - img, err := mutate.Append(empty.Image, mutate.Addendum{Layer: l}) - if err != nil { - t.Fatal(err) - } - actual, err := extractOCIStandardImage(img) - if err != nil { - t.Fatalf("extractOCIStandardImage failed: %v", err) - } - - if string(actual) != exp { - t.Fatalf("got %s, but want %s", string(actual), exp) - } - }) - - t.Run("multiple layers", func(t *testing.T) { - l, err := newMockLayer(types.OCILayer, nil) - if err != nil { - t.Fatal(err) - } - img := empty.Image - for i := 0; i < 2; i++ { - img, err = mutate.Append(img, mutate.Addendum{Layer: l}) - if err != nil { - t.Fatal(err) - } - } - _, err = extractOCIStandardImage(img) - if err == nil || !strings.Contains(err.Error(), "number of layers must be") { - t.Fatal("extractOCIStandardImage should fail due to invalid number of layers") - } - }) - - t.Run("invalid media type", func(t *testing.T) { - l, err := newMockLayer(types.DockerLayer, nil) - if err != nil { - t.Fatal(err) - } - img, err := mutate.Append(empty.Image, mutate.Addendum{Layer: l}) - if err != nil { - t.Fatal(err) - } - _, err = extractOCIStandardImage(img) - if err == nil || !strings.Contains(err.Error(), "invalid media type") { - t.Fatal("extractOCIStandardImage should fail due to invalid media type") - } - }) -} - -func newMockLayer(mediaType types.MediaType, contents map[string][]byte) (v1.Layer, error) { - var b bytes.Buffer - hasher := sha256.New() - mw := io.MultiWriter(&b, hasher) - tw := tar.NewWriter(mw) - defer tw.Close() - - for filename, content := range contents { - if err := tw.WriteHeader(&tar.Header{ - Name: filename, - Size: int64(len(content)), - Typeflag: tar.TypeRegA, - }); err != nil { - return nil, err - } - if _, err := io.CopyN(tw, bytes.NewReader(content), int64(len(content))); err != nil { - return nil, err - } - } - return partial.UncompressedToLayer( - &mockLayer{ - raw: b.Bytes(), - diffID: v1.Hash{ - Algorithm: "sha256", - Hex: hex.EncodeToString(hasher.Sum(make([]byte, 0, hasher.Size()))), - }, - mediaType: mediaType, - }, - ) -} - -type mockLayer struct { - raw []byte - diffID v1.Hash - mediaType types.MediaType -} - -func (r *mockLayer) DiffID() (v1.Hash, error) { return v1.Hash{}, nil } -func (r *mockLayer) Uncompressed() (io.ReadCloser, error) { - return io.NopCloser(bytes.NewBuffer(r.raw)), nil -} -func (r *mockLayer) MediaType() (types.MediaType, error) { return r.mediaType, nil } - -func TestExtractOCIArtifactImage(t *testing.T) { - t.Run("valid", func(t *testing.T) { - // Create the image with custom media types. - wasmLayer, err := random.Layer(1000, "application/vnd.module.wasm.content.layer.v1+wasm") - if err != nil { - t.Fatal(err) - } - configLayer, err := random.Layer(1000, "application/vnd.module.wasm.config.v1+json") - if err != nil { - t.Fatal(err) - } - img, err := mutate.Append(empty.Image, mutate.Addendum{Layer: wasmLayer}, mutate.Addendum{Layer: configLayer}) - if err != nil { - t.Fatal(err) - } - - // Extract the binary. - actual, err := extractOCIArtifactImage(img) - if err != nil { - t.Fatalf("extractOCIArtifactImage failed: %v", err) - } - - // Retrieve the wanted image content. - wantReader, err := wasmLayer.Compressed() - if err != nil { - t.Fatal(err) - } - defer wantReader.Close() - want, err := io.ReadAll(wantReader) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(actual, want) { - t.Errorf("extractOCIArtifactImage got %s, but want '%s'", string(actual), string(want)) - } - }) - - t.Run("invalid number of layers", func(t *testing.T) { - l, err := random.Layer(1000, "application/vnd.module.wasm.content.layer.v1+wasm") - if err != nil { - t.Fatal(err) - } - img, err := mutate.Append(empty.Image, mutate.Addendum{Layer: l}) - if err != nil { - t.Fatal(err) - } - _, err = extractOCIArtifactImage(img) - if err == nil || !strings.Contains(err.Error(), "number of layers must be") { - t.Fatal("extractOCIArtifactImage should fail due to invalid number of layers") - } - }) - - t.Run("invalid media types", func(t *testing.T) { - // Create the image with invalid media types. - layer, err := random.Layer(1000, "aaa") - if err != nil { - t.Fatal(err) - } - img, err := mutate.Append(empty.Image, mutate.Addendum{Layer: layer}, mutate.Addendum{Layer: layer}) - if err != nil { - t.Fatal(err) - } - - _, err = extractOCIArtifactImage(img) - if err == nil || !strings.Contains(err.Error(), - "could not find the layer of type application/vnd.module.wasm.content.layer.v1+wasm") { - t.Fatal("extractOCIArtifactImage should fail due to invalid number of layers") - } - }) -} - -func TestExtractWasmPluginBinary(t *testing.T) { - t.Run("ok", func(t *testing.T) { - buf := bytes.NewBuffer(nil) - gz := gzip.NewWriter(buf) - tw := tar.NewWriter(gz) - - exp := "hello" - if err := tw.WriteHeader(&tar.Header{ - Name: "plugin.wasm", - Size: int64(len(exp)), - }); err != nil { - t.Fatal(err) - } - - if _, err := io.WriteString(tw, exp); err != nil { - t.Fatal(err) - } - - tw.Close() - gz.Close() - - actual, err := extractWasmPluginBinary(buf) - if err != nil { - t.Errorf("extractWasmPluginBinary failed: %v", err) - } - - if string(actual) != exp { - t.Errorf("extractWasmPluginBinary got %v, but want %v", string(actual), exp) - } - }) - - t.Run("ok with relative path prefix", func(t *testing.T) { - buf := bytes.NewBuffer(nil) - gz := gzip.NewWriter(buf) - tw := tar.NewWriter(gz) - - exp := "hello" - if err := tw.WriteHeader(&tar.Header{ - Name: "./plugin.wasm", - Size: int64(len(exp)), - }); err != nil { - t.Fatal(err) - } - - if _, err := io.WriteString(tw, exp); err != nil { - t.Fatal(err) - } - - tw.Close() - gz.Close() - - actual, err := extractWasmPluginBinary(buf) - if err != nil { - t.Errorf("extractWasmPluginBinary failed: %v", err) - } - - if string(actual) != exp { - t.Errorf("extractWasmPluginBinary got %v, but want %v", string(actual), exp) - } - }) - - t.Run("not found", func(t *testing.T) { - buf := bytes.NewBuffer(nil) - gz := gzip.NewWriter(buf) - tw := tar.NewWriter(gz) - if err := tw.WriteHeader(&tar.Header{ - Name: "non-wasm.txt", - Size: int64(1), - }); err != nil { - t.Fatal(err) - } - if _, err := tw.Write([]byte{1}); err != nil { - t.Fatal(err) - } - tw.Close() - gz.Close() - _, err := extractWasmPluginBinary(buf) - if err == nil || !strings.Contains(err.Error(), "not found") { - t.Errorf("extractWasmPluginBinary must fail with not found") - } - }) -} - -func TestWasmKeyChain(t *testing.T) { - dockerjson := fmt.Sprintf(`{"auths": {"test.io": {"auth": %q}}}`, encode("foo", "bar")) - keyChain := wasmKeyChain{data: []byte(dockerjson)} - testRegistry, _ := name.NewRegistry("test.io", name.WeakValidation) - keyChain.Resolve(testRegistry) - auth, err := keyChain.Resolve(testRegistry) - if err != nil { - t.Fatalf("Resolve() = %v", err) - } - got, err := auth.Authorization() - if err != nil { - t.Fatal(err) - } - want := &authn.AuthConfig{ - Username: "foo", - Password: "bar", - } - if !reflect.DeepEqual(got, want) { - t.Errorf("got %+v, want %+v", got, want) - } -} - -func encode(user, pass string) string { - delimited := fmt.Sprintf("%s:%s", user, pass) - return base64.StdEncoding.EncodeToString([]byte(delimited)) -} diff --git a/pixiu/pkg/wasm/key.go b/pkg/wasm/key.go similarity index 100% rename from pixiu/pkg/wasm/key.go rename to pkg/wasm/key.go diff --git a/pkg/wasm/leak_test.go b/pkg/wasm/leak_test.go deleted file mode 100644 index 2d5d45f2e..000000000 --- a/pkg/wasm/leak_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package wasm - -import ( - "testing" -) - -import ( - "github.com/apache/dubbo-go-pixiu/tests/util/leak" -) - -func TestMain(m *testing.M) { - // CheckMain asserts that no goroutines are leaked after a test package exits. - leak.CheckMain(m) -} diff --git a/pkg/wasm/manager.go b/pkg/wasm/manager.go new file mode 100644 index 000000000..fe83af8d2 --- /dev/null +++ b/pkg/wasm/manager.go @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 wasm + +import ( + "sync" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/config" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +type WasmManager struct { + model *model.WasmConfig + serviceMap map[string]*WasmService +} + +var manager *WasmManager +var once sync.Once + +// InitWasmManager loads config in conf.yaml +func InitWasmManager(model *model.WasmConfig) { + manager = &WasmManager{ + model: model, + serviceMap: make(map[string]*WasmService), + } + + for _, service := range model.Services { + if !contains(service.Name) { + logger.Warnf("[dubbo-go-pixiu] wasm config error: service name isn't in pixiu keys.") + continue + } + wasmService, err := createWasmService(service) + if err != nil { + logger.Warnf("[dubbo-go-pixiu] wasm config error: %v", err) + continue + } + manager.serviceMap[service.Name] = wasmService + } +} + +// GetWasmServiceByName get WasmService By Name +func getWasmServiceByName(name string) *WasmService { + once.Do(func() { + bs := config.GetBootstrap() + InitWasmManager(bs.Wasm) + }) + if wasmService, exist := manager.serviceMap[name]; exist { + return wasmService + } + logger.Warnf("[dubbo-go-pixiu] no wasmService named %v, check pixiu conf.yaml", name) + + return nil +} + +func GetServiceRootID(name string) int32 { + wasmService := getWasmServiceByName(name) + return wasmService.rootContextID +} + +// CreateAbIContextByName create abiContext according to every request +func CreateABIContextByName(name string, ctx *http.HttpContext) *ABIContextWrapper { + wasmService := getWasmServiceByName(name) + + if wasmService != nil { + return wasmService.createABIContextWrapper(ctx) + } + return nil +} + +// ContextDone put wasmInstance back to Pool. +func ContextDone(wrapper *ABIContextWrapper) error { + wasmService := getWasmServiceByName(wrapper.name) + return wasmService.putWasmInstance(wrapper.Context.Instance) +} diff --git a/pkg/wasm/monitoring.go b/pkg/wasm/monitoring.go deleted file mode 100644 index 6328587f1..000000000 --- a/pkg/wasm/monitoring.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package wasm - -import ( - "istio.io/pkg/monitoring" -) - -// Const strings for label value. -const ( - // For remote fetch metric. - fetchSuccess = "success" - downloadFailure = "download_failure" - checksumMismatch = "checksum_mismatched" - - // For Wasm conversion metric. - conversionSuccess = "success" - noRemoteLoad = "no_remote_load" - marshalFailure = "marshal_failure" - fetchFailure = "fetch_failure" - missRemoteFetchHint = "miss_remote_fetch_hint" -) - -var ( - hitTag = monitoring.MustCreateLabel("hit") - resultTag = monitoring.MustCreateLabel("result") - - wasmCacheEntries = monitoring.NewGauge( - "wasm_cache_entries", - "number of Wasm remote fetch cache entries.", - ) - - wasmCacheLookupCount = monitoring.NewSum( - "wasm_cache_lookup_count", - "number of Wasm remote fetch cache lookups.", - monitoring.WithLabels(hitTag), - ) - - wasmRemoteFetchCount = monitoring.NewSum( - "wasm_remote_fetch_count", - "number of Wasm remote fetches and results, including success, download failure, and checksum mismatch.", - monitoring.WithLabels(resultTag), - ) - - wasmConfigConversionCount = monitoring.NewSum( - "wasm_config_conversion_count", - "number of Wasm config conversion count and results, including success, no remote load, marshal failure, remote fetch failure, miss remote fetch hint.", - monitoring.WithLabels(resultTag), - ) - - wasmConfigConversionDuration = monitoring.NewDistribution( - "wasm_config_conversion_duration", - "Total time in milliseconds istio-agent spends on converting remote load in Wasm config.", - []float64{1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384}, - ) -) - -func init() { - monitoring.MustRegister( - wasmCacheEntries, - wasmCacheLookupCount, - wasmRemoteFetchCount, - wasmConfigConversionCount, - wasmConfigConversionDuration, - ) -} diff --git a/pkg/wasm/service.go b/pkg/wasm/service.go new file mode 100644 index 000000000..1590cf450 --- /dev/null +++ b/pkg/wasm/service.go @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 wasm + +import ( + "math" + "os" + "path/filepath" + "sync" + "sync/atomic" +) + +import ( + "github.com/mitchellh/mapstructure" + "mosn.io/proxy-wasm-go-host/common" + "mosn.io/proxy-wasm-go-host/proxywasm" + "mosn.io/proxy-wasm-go-host/wasmer" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/model" +) + +// WasmService manages a collection of WasmInstances of the same type. +type WasmService struct { + rootContextID int32 + contextIDGenerator int32 + name string + path string + mutex sync.Mutex + model model.WasmService + instancePool sync.Pool +} + +// WasmConfig contains config about every wasmFile. +type WasmConfig struct { + Path string `yaml:"path" json:"path,omitempty"` +} + +// ABIContextWrapper is a request wrapper which indicates request was handled in the wasmInstance. +type ABIContextWrapper struct { + ContextID int32 + name string + Context *proxywasm.ABIContext +} + +func createWasmService(service model.WasmService) (*WasmService, error) { + var cfg WasmConfig + if err := mapstructure.Decode(service.Config, &cfg); err != nil { + return nil, err + } + + wasmService := &WasmService{ + contextIDGenerator: 0, + name: service.Name, + path: cfg.Path, + mutex: sync.Mutex{}, + model: service, + } + wasmService.rootContextID = atomic.AddInt32(&wasmService.contextIDGenerator, 1) + + wasmService.instancePool = sync.Pool{ + New: func() interface{} { + pwd, _ := os.Getwd() + path := filepath.Join(pwd, cfg.Path) + if _, err := os.Stat(path); err != nil { + logger.Warnf("[dubbo-go-pixiu] wasmFile path:%v error: %v", path, err) + return nil + } + instance := wasmer.NewWasmerInstanceFromFile(path) + proxywasm.RegisterImports(instance) + _ = instance.Start() + return instance + }, + } + return wasmService, nil +} + +func (ws *WasmService) getWasmInstance() common.WasmInstance { + return ws.instancePool.Get().(common.WasmInstance) +} + +func (ws *WasmService) putWasmInstance(instance common.WasmInstance) error { + ws.instancePool.Put(instance) + return nil +} + +func (ws *WasmService) createABIContextWrapper(ctx *http.HttpContext) *ABIContextWrapper { + + contextWrapper := new(ABIContextWrapper) + contextWrapper.ContextID = atomic.AddInt32(&ws.contextIDGenerator, 1) + atomic.CompareAndSwapInt32(&ws.contextIDGenerator, math.MaxInt32, 1) + + contextWrapper.name = ws.name + contextWrapper.Context = &proxywasm.ABIContext{ + Imports: &importHandler{ + ctx: ctx, + reqHeader: &headerWrapper{ + header: ctx.Request.Header, + }, + }, + Instance: ws.getWasmInstance(), + } + _ = contextWrapper.Context.GetExports().ProxyOnContextCreate(ws.rootContextID, 0) + + return contextWrapper +} diff --git a/pkg/wasm/utils.go b/pkg/wasm/utils.go new file mode 100644 index 000000000..7ce3883d8 --- /dev/null +++ b/pkg/wasm/utils.go @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 wasm + +import ( + "net/http" +) + +import ( + "mosn.io/proxy-wasm-go-host/common" + "mosn.io/proxy-wasm-go-host/proxywasm" +) + +import ( + contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +type ( + headerWrapper struct { + header http.Header + } + + importHandler struct { + ctx *contexthttp.HttpContext + reqHeader common.HeaderMap + proxywasm.DefaultImportsHandler + } +) + +func (h *headerWrapper) Get(key string) (string, bool) { + value := h.header.Get(key) + if value == "" { + return "", false + } + return value, true +} + +func (h *headerWrapper) Set(key, value string) { + h.header.Set(key, value) +} + +func (h *headerWrapper) Add(key, value string) { + h.header.Add(key, value) +} + +func (h *headerWrapper) Del(key string) { + h.header.Del(key) +} + +func (h *headerWrapper) Range(f func(key string, value string) bool) { + for k := range h.header { + v := h.header.Get(k) + f(k, v) + } +} + +func (h *headerWrapper) Clone() common.HeaderMap { + copy := &headerWrapper{ + header: h.header.Clone(), + } + return copy +} + +func (h *headerWrapper) ByteSize() uint64 { + var size uint64 + + for k := range h.header { + v := h.header.Get(k) + size += uint64(len(k) + len(v)) + } + return size +} + +func (im *importHandler) GetHttpRequestHeader() common.HeaderMap { + return im.reqHeader +} + +func (im *importHandler) Log(level proxywasm.LogLevel, msg string) proxywasm.WasmResult { + switch level { + case proxywasm.LogLevelDebug: + logger.Debugf(msg) + case proxywasm.LogLevelInfo: + logger.Infof(msg) + case proxywasm.LogLevelWarn: + logger.Warnf(msg) + case proxywasm.LogLevelError: + logger.Errorf(msg) + default: + logger.Infof(msg) + } + return proxywasm.WasmResultOk +} diff --git a/pkg/webhooks/monitoring.go b/pkg/webhooks/monitoring.go deleted file mode 100644 index 5d3e6f5c2..000000000 --- a/pkg/webhooks/monitoring.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package webhooks - -import ( - "istio.io/pkg/monitoring" -) - -var ( - // webhookConfigNameTag holds the target webhook config name for the context. - webhookConfigNameTag = monitoring.MustCreateLabel("name") - - // reasonTag holds the error reason for the context. - reasonTag = monitoring.MustCreateLabel("reason") -) - -var ( - metricWebhookPatchAttempts = monitoring.NewSum( - "webhook_patch_attempts_total", - "Webhook patching attempts", - monitoring.WithLabels(webhookConfigNameTag), - ) - - metricWebhookPatchRetries = monitoring.NewSum( - "webhook_patch_retries_total", - "Webhook patching retries", - monitoring.WithLabels(webhookConfigNameTag), - ) - - metricWebhookPatchFailures = monitoring.NewSum( - "webhook_patch_failures_total", - "Webhook patching total failures", - monitoring.WithLabels(webhookConfigNameTag, reasonTag), - ) -) - -const ( - // webhook patching failure reasons - reasonWrongRevision = "wrong_revision" - reasonLoadCABundleFailure = "load_ca_bundle_failure" - reasonWebhookConfigNotFound = "webhook_config_not_found" - reasonWebhookEntryNotFound = "webhook_entry_not_found" - reasonWebhookUpdateFailure = "webhook_update_failure" -) - -func init() { - monitoring.MustRegister( - metricWebhookPatchAttempts, - metricWebhookPatchRetries, - metricWebhookPatchFailures, - ) -} - -func reportWebhookPatchAttempts(webhookConfigName string) { - metricWebhookPatchAttempts. - With(webhookConfigNameTag.Value(webhookConfigName)). - Increment() -} - -func reportWebhookPatchRetry(webhookConfigName string) { - metricWebhookPatchRetries. - With(webhookConfigNameTag.Value(webhookConfigName)). - Increment() -} - -func reportWebhookPatchFailure(webhookConfigName string, reason string) { - metricWebhookPatchFailures. - With(webhookConfigNameTag.Value(webhookConfigName)). - With(reasonTag.Value(reason)). - Increment() -} diff --git a/pkg/webhooks/util/util.go b/pkg/webhooks/util/util.go deleted file mode 100644 index 884ad03b1..000000000 --- a/pkg/webhooks/util/util.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "crypto/x509" - "encoding/pem" - "errors" - "fmt" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/keycertbundle" -) - -type ConfigError struct { - err error - reason string -} - -func (e ConfigError) Error() string { - return e.err.Error() -} - -func (e ConfigError) Reason() string { - return e.reason -} - -func LoadCABundle(caBundleWatcher *keycertbundle.Watcher) ([]byte, error) { - caBundle := caBundleWatcher.GetCABundle() - if err := VerifyCABundle(caBundle); err != nil { - return nil, &ConfigError{err, "could not verify caBundle"} - } - - return caBundle, nil -} - -func VerifyCABundle(caBundle []byte) error { - block, _ := pem.Decode(caBundle) - if block == nil { - return errors.New("could not decode pem") - } - if block.Type != "CERTIFICATE" { - return fmt.Errorf("cert contains wrong pem type: %q", block.Type) - } - if _, err := x509.ParseCertificate(block.Bytes); err != nil { - return fmt.Errorf("cert contains invalid x509 certificate: %v", err) - } - return nil -} diff --git a/pkg/webhooks/validation/controller/controller.go b/pkg/webhooks/validation/controller/controller.go deleted file mode 100644 index 14876d1ec..000000000 --- a/pkg/webhooks/validation/controller/controller.go +++ /dev/null @@ -1,464 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package controller implements a k8s controller for managing the lifecycle of a validating webhook. -package controller - -import ( - "bytes" - "context" - "errors" - "fmt" - "reflect" - "strings" - "time" -) - -import ( - "github.com/hashicorp/go-multierror" - "istio.io/api/label" - networking "istio.io/api/networking/v1alpha3" - "istio.io/client-go/pkg/apis/networking/v1alpha3" - "istio.io/pkg/log" - kubeApiAdmission "k8s.io/api/admissionregistration/v1" - kubeErrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/runtime/serializer/json" - "k8s.io/apimachinery/pkg/runtime/serializer/versioning" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/util/workqueue" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/keycertbundle" - "github.com/apache/dubbo-go-pixiu/pkg/config/labels" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/webhooks/util" -) - -var scope = log.RegisterScope("validationController", "validation webhook controller", 0) - -type Options struct { - // Istio system namespace where istiod resides. - WatchedNamespace string - - // File path to the x509 certificate bundle used by the webhook server - // and patched into the webhook config. - CABundleWatcher *keycertbundle.Watcher - - // Revision for control plane performing patching on the validating webhook. - Revision string - - // Name of the service running the webhook server. - ServiceName string -} - -// Validate the options that exposed to end users -func (o Options) Validate() error { - var errs *multierror.Error - if o.WatchedNamespace == "" || !labels.IsDNS1123Label(o.WatchedNamespace) { - errs = multierror.Append(errs, fmt.Errorf("invalid namespace: %q", o.WatchedNamespace)) - } - if o.ServiceName == "" || !labels.IsDNS1123Label(o.ServiceName) { - errs = multierror.Append(errs, fmt.Errorf("invalid service name: %q", o.ServiceName)) - } - if o.CABundleWatcher == nil { - errs = multierror.Append(errs, errors.New("CA bundle watcher not specified")) - } - return errs.ErrorOrNil() -} - -// String produces a string field version of the arguments for debugging. -func (o Options) String() string { - buf := &bytes.Buffer{} - _, _ = fmt.Fprintf(buf, "WatchedNamespace: %v\n", o.WatchedNamespace) - _, _ = fmt.Fprintf(buf, "Revision: %v\n", o.Revision) - _, _ = fmt.Fprintf(buf, "ServiceName: %v\n", o.ServiceName) - return buf.String() -} - -type Controller struct { - o Options - client kube.Client - - webhookInformer cache.SharedInformer - queue workqueue.RateLimitingInterface - dryRunOfInvalidConfigRejected bool -} - -// NewValidatingWebhookController creates a new Controller. -func NewValidatingWebhookController(client kube.Client, - revision, ns string, caBundleWatcher *keycertbundle.Watcher) *Controller { - o := Options{ - WatchedNamespace: ns, - CABundleWatcher: caBundleWatcher, - Revision: revision, - ServiceName: "istiod", - } - return newController(o, client) -} - -type eventType string - -const ( - retryEvent eventType = "retryEvent" - updateEvent eventType = "updateEvent" -) - -type reconcileRequest struct { - event eventType - description string - webhookName string // if empty, ALL webhooks should be updated in response -} - -func (rr reconcileRequest) String() string { - return fmt.Sprintf("[description] %s, [eventType] %s, [name] %s", rr.description, rr.event, rr.webhookName) -} - -func filterWatchedObject(obj metav1.Object) (skip bool, key string) { - key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) - if err != nil { - return true, "" - } - return false, key -} - -func makeHandler(queue workqueue.Interface, gvk schema.GroupVersionKind) *cache.ResourceEventHandlerFuncs { - return &cache.ResourceEventHandlerFuncs{ - AddFunc: func(curr interface{}) { - obj, err := meta.Accessor(curr) - if err != nil { - return - } - skip, key := filterWatchedObject(obj) - scope.Debugf("HandlerAdd: key=%v skip=%v", key, skip) - if skip { - return - } - req := reconcileRequest{ - event: updateEvent, - webhookName: obj.GetName(), - description: fmt.Sprintf("add event (%v, Kind=%v) %v", gvk.GroupVersion(), gvk.Kind, key), - } - queue.Add(req) - }, - UpdateFunc: func(prev, curr interface{}) { - currObj, err := meta.Accessor(curr) - if err != nil { - return - } - prevObj, err := meta.Accessor(prev) - if err != nil { - return - } - if currObj.GetResourceVersion() == prevObj.GetResourceVersion() { - return - } - skip, key := filterWatchedObject(currObj) - scope.Debugf("HandlerUpdate: key=%v skip=%v", key, skip) - if skip { - return - } - req := reconcileRequest{ - event: updateEvent, - webhookName: currObj.GetName(), - description: fmt.Sprintf("update event (%v, Kind=%v) %v", gvk.GroupVersion(), gvk.Kind, key), - } - queue.Add(req) - }, - } -} - -// precompute GVK for known types. -var ( - configGVK = kubeApiAdmission.SchemeGroupVersion.WithKind(reflect.TypeOf(kubeApiAdmission.ValidatingWebhookConfiguration{}).Name()) -) - -func newController( - o Options, - client kube.Client, -) *Controller { - c := &Controller{ - o: o, - client: client, - queue: workqueue.NewRateLimitingQueue(workqueue.NewItemExponentialFailureRateLimiter(1*time.Second, 5*time.Minute)), - } - - webhookInformer := cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) { - opts.LabelSelector = fmt.Sprintf("%s=%s", label.IoIstioRev.Name, o.Revision) - return client.AdmissionregistrationV1().ValidatingWebhookConfigurations().List(context.TODO(), opts) - }, - WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) { - opts.LabelSelector = fmt.Sprintf("%s=%s", label.IoIstioRev.Name, o.Revision) - return client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Watch(context.TODO(), opts) - }, - }, - &kubeApiAdmission.ValidatingWebhookConfiguration{}, 0, cache.Indexers{}, - ) - webhookInformer.AddEventHandler(makeHandler(c.queue, configGVK)) - c.webhookInformer = webhookInformer - - return c -} - -func (c *Controller) Run(stop <-chan struct{}) { - defer c.queue.ShutDown() - go c.webhookInformer.Run(stop) - if !cache.WaitForCacheSync(stop, c.webhookInformer.HasSynced) { - return - } - go c.startCaBundleWatcher(stop) - go c.runWorker() - <-stop -} - -// startCaBundleWatcher listens for updates to the CA bundle and patches the webhooks. -// shouldn't we be doing this for both validating and mutating webhooks...? -func (c *Controller) startCaBundleWatcher(stop <-chan struct{}) { - if c.o.CABundleWatcher == nil { - return - } - id, watchCh := c.o.CABundleWatcher.AddWatcher() - defer c.o.CABundleWatcher.RemoveWatcher(id) - // trigger initial update - watchCh <- struct{}{} - for { - select { - case <-watchCh: - c.queue.AddRateLimited(reconcileRequest{ - updateEvent, - "CA bundle update", - "", - }) - case <-stop: - return - } - } -} - -func (c *Controller) runWorker() { - for c.processNextWorkItem() { - } -} - -func (c *Controller) processNextWorkItem() (cont bool) { - obj, shutdown := c.queue.Get() - if shutdown { - return false - } - defer c.queue.Done(obj) - - req, ok := obj.(reconcileRequest) - if !ok { - // don't retry an invalid reconcileRequest item - c.queue.Forget(req) - return true - } - - // empty webhook name means we must patch for each webhook - if req.webhookName == "" { - c.updateAll() - return true - } - - if retry, err := c.reconcileRequest(req); retry || err != nil { - c.queue.AddRateLimited(reconcileRequest{ - event: retryEvent, - description: "retry reconcile request", - webhookName: req.webhookName, - }) - utilruntime.HandleError(err) - } else { - c.queue.Forget(obj) - } - return true -} - -// updateAll updates all webhooks matching the controller's revision, generally in response -// to a CA bundle update. updateAll reports an error only when there's an issue listing webhooks, -// not when it has an issue updating a single webhook so that we can retry separately for the -// two cases. -func (c *Controller) updateAll() { - whs := c.webhookInformer.GetStore().List() - for _, item := range whs { - wh := item.(*kubeApiAdmission.ValidatingWebhookConfiguration) - if retry, err := c.reconcileRequest(reconcileRequest{ - event: updateEvent, - description: "CA bundle update", - webhookName: wh.Name, - }); retry || err != nil { - c.queue.AddRateLimited(reconcileRequest{ - event: retryEvent, - description: "retry reconcile request", - webhookName: wh.Name, - }) - } - } -} - -// reconcile the desired state with the kube-apiserver. -// the returned results indicate if the reconciliation should be retried and/or -// if there was an error. -func (c *Controller) reconcileRequest(req reconcileRequest) (bool, error) { - // Stop early if webhook is not present, rather than attempting (and failing) to reconcile permanently - // If the webhook is later added a new reconciliation request will trigger it to update - configuration, err := c.client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Get(context.Background(), req.webhookName, metav1.GetOptions{}) - if err != nil { - if kubeErrors.IsNotFound(err) { - scope.Infof("Skip patching webhook, webhook %q not found", req.webhookName) - return false, nil - } - return false, err - } - - scope.Debugf("Reconcile(enter): %v", req) - defer func() { scope.Debugf("Reconcile(exit)") }() - - caBundle, err := util.LoadCABundle(c.o.CABundleWatcher) - if err != nil { - scope.Errorf("Failed to load CA bundle: %v", err) - reportValidationConfigLoadError(err.(*util.ConfigError).Reason()) - // no point in retrying unless cert file changes. - return false, nil - } - failurePolicy := kubeApiAdmission.Ignore - ready := c.readyForFailClose() - if ready { - failurePolicy = kubeApiAdmission.Fail - } - return !ready, c.updateValidatingWebhookConfiguration(configuration, caBundle, failurePolicy) -} - -func (c *Controller) readyForFailClose() bool { - if !c.dryRunOfInvalidConfigRejected { - if rejected, reason := c.isDryRunOfInvalidConfigRejected(); !rejected { - scope.Infof("Not ready to switch validation to fail-closed: %v", reason) - return false - } - scope.Info("Endpoint successfully rejected invalid config. Switching to fail-close.") - c.dryRunOfInvalidConfigRejected = true - } - return true -} - -const ( - deniedRequestMessageFragment = `denied the request` - missingResourceMessageFragment = `the server could not find the requested resource` - unsupportedDryRunMessageFragment = `does not support dry run` -) - -// Confirm invalid configuration is successfully rejected before switching to FAIL-CLOSE. -func (c *Controller) isDryRunOfInvalidConfigRejected() (rejected bool, reason string) { - invalidGateway := &v1alpha3.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "invalid-gateway", - Namespace: c.o.WatchedNamespace, - // Must ensure that this is the revision validating the known-bad config - Labels: map[string]string{ - label.IoIstioRev.Name: c.o.Revision, - }, - }, - Spec: networking.Gateway{}, - } - - createOptions := metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}} - istioClient := c.client.Istio().NetworkingV1alpha3() - _, err := istioClient.Gateways(c.o.WatchedNamespace).Create(context.TODO(), invalidGateway, createOptions) - if kubeErrors.IsAlreadyExists(err) { - updateOptions := metav1.UpdateOptions{DryRun: []string{metav1.DryRunAll}} - _, err = istioClient.Gateways(c.o.WatchedNamespace).Update(context.TODO(), invalidGateway, updateOptions) - } - if err == nil { - return false, "dummy invalid config not rejected" - } - // We expect to get deniedRequestMessageFragment (the config was rejected, as expected) - if strings.Contains(err.Error(), deniedRequestMessageFragment) { - return true, "" - } - // If the CRD does not exist, we will get this error. This is to handle when Pilot is run - // without CRDs - in this case, this check will not be possible. - if strings.Contains(err.Error(), missingResourceMessageFragment) { - scope.Warnf("Missing Gateway CRD, cannot perform validation check. Assuming validation is ready") - return true, "" - } - // If some validating webhooks does not support dryRun(sideEffects=Unknown or Some), we will get this error. - // We should assume valdiation is ready because there is no point in retrying this request. - if strings.Contains(err.Error(), unsupportedDryRunMessageFragment) { - scope.Warnf("One of the validating webhooks does not support DryRun, cannot perform validation check. Assuming validation is ready. Details: %v", err) - return true, "" - } - return false, fmt.Sprintf("dummy invalid rejected for the wrong reason: %v", err) -} - -func (c *Controller) updateValidatingWebhookConfiguration(current *kubeApiAdmission.ValidatingWebhookConfiguration, - caBundle []byte, failurePolicy kubeApiAdmission.FailurePolicyType) error { - dirty := false - for i := range current.Webhooks { - if !bytes.Equal(current.Webhooks[i].ClientConfig.CABundle, caBundle) || - (current.Webhooks[i].FailurePolicy != nil && *current.Webhooks[i].FailurePolicy != failurePolicy) { - dirty = true - break - } - } - if !dirty { - scope.Infof("validatingwebhookconfiguration %v (failurePolicy=%v, resourceVersion=%v) is up-to-date. No change required.", - current.Name, failurePolicy, current.ResourceVersion) - return nil - } - updated := current.DeepCopy() - for i := range updated.Webhooks { - updated.Webhooks[i].ClientConfig.CABundle = caBundle - updated.Webhooks[i].FailurePolicy = &failurePolicy - } - - latest, err := c.client.AdmissionregistrationV1(). - ValidatingWebhookConfigurations().Update(context.TODO(), updated, metav1.UpdateOptions{}) - if err != nil { - scope.Errorf("Failed to update validatingwebhookconfiguration %v (failurePolicy=%v, resourceVersion=%v): %v", - updated.Name, failurePolicy, updated.ResourceVersion, err) - reportValidationConfigUpdateError(kubeErrors.ReasonForError(err)) - return err - } - - scope.Infof("Successfully updated validatingwebhookconfiguration %v (failurePolicy=%v,resourceVersion=%v)", - updated.Name, failurePolicy, latest.ResourceVersion) - reportValidationConfigUpdate() - return nil -} - -var ( - codec runtime.Codec - scheme *runtime.Scheme -) - -func init() { - scheme = runtime.NewScheme() - utilruntime.Must(kubeApiAdmission.AddToScheme(scheme)) - opt := json.SerializerOptions{Yaml: true} - yamlSerializer := json.NewSerializerWithOptions(json.DefaultMetaFactory, scheme, scheme, opt) - codec = versioning.NewDefaultingCodecForScheme( - scheme, - yamlSerializer, - yamlSerializer, - kubeApiAdmission.SchemeGroupVersion, - runtime.InternalGroupVersioner, - ) -} diff --git a/pkg/webhooks/validation/controller/controller_test.go b/pkg/webhooks/validation/controller/controller_test.go deleted file mode 100644 index cd686cbb1..000000000 --- a/pkg/webhooks/validation/controller/controller_test.go +++ /dev/null @@ -1,403 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "context" - "errors" - "fmt" - "sync" - "testing" - "time" -) - -import ( - . "github.com/onsi/gomega" - "istio.io/api/label" - "istio.io/client-go/pkg/apis/networking/v1alpha3" - istiofake "istio.io/client-go/pkg/clientset/versioned/fake" - kubeApiAdmission "k8s.io/api/admissionregistration/v1" - kubeErrors "k8s.io/apimachinery/pkg/api/errors" - kubeApiMeta "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes/fake" - kubeTypedAdmission "k8s.io/client-go/kubernetes/typed/admissionregistration/v1" - ktesting "k8s.io/client-go/testing" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/util/workqueue" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/keycertbundle" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" - "github.com/apache/dubbo-go-pixiu/pkg/testcerts" - "github.com/apache/dubbo-go-pixiu/pkg/webhooks/util" -) - -var ( - failurePolicyFail = kubeApiAdmission.Fail - failurePolicyIgnore = kubeApiAdmission.Ignore - - unpatchedWebhookConfig = &kubeApiAdmission.ValidatingWebhookConfiguration{ - TypeMeta: kubeApiMeta.TypeMeta{ - APIVersion: kubeApiAdmission.SchemeGroupVersion.String(), - Kind: "ValidatingWebhookConfiguration", - }, - ObjectMeta: kubeApiMeta.ObjectMeta{ - Name: webhookName, - Labels: map[string]string{ - label.IoIstioRev.Name: revision, - }, - }, - Webhooks: []kubeApiAdmission.ValidatingWebhook{{ - Name: "hook0", - ClientConfig: kubeApiAdmission.WebhookClientConfig{Service: &kubeApiAdmission.ServiceReference{ - Namespace: namespace, - Name: istiod, - Path: &[]string{"/hook0"}[0], - }}, - Rules: []kubeApiAdmission.RuleWithOperations{{ - Operations: []kubeApiAdmission.OperationType{kubeApiAdmission.Create, kubeApiAdmission.Update}, - Rule: kubeApiAdmission.Rule{ - APIGroups: []string{"group0"}, - APIVersions: []string{"*"}, - Resources: []string{"*"}, - }, - }}, - FailurePolicy: &failurePolicyIgnore, - }, { - Name: "hook1", - ClientConfig: kubeApiAdmission.WebhookClientConfig{Service: &kubeApiAdmission.ServiceReference{ - Namespace: namespace, - Name: istiod, - Path: &[]string{"/hook1"}[0], - }}, - Rules: []kubeApiAdmission.RuleWithOperations{{ - Operations: []kubeApiAdmission.OperationType{kubeApiAdmission.Create, kubeApiAdmission.Update}, - Rule: kubeApiAdmission.Rule{ - APIGroups: []string{"group1"}, - APIVersions: []string{"*"}, - Resources: []string{"*"}, - }, - }}, - FailurePolicy: &failurePolicyIgnore, - }}, - } - - webhookConfigEncoded string - webhookConfigWithCABundleFail *kubeApiAdmission.ValidatingWebhookConfiguration - webhookConfigWithCABundleIgnore *kubeApiAdmission.ValidatingWebhookConfiguration - - caBundle0 = []byte(`-----BEGIN CERTIFICATE----- -MIIC9DCCAdygAwIBAgIJAIFe3lWPaalKMA0GCSqGSIb3DQEBCwUAMA4xDDAKBgNV -BAMMA19jYTAgFw0xNzEyMjIxODA0MjRaGA8yMjkxMTAwNzE4MDQyNFowDjEMMAoG -A1UEAwwDX2NhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuBdxj+Hi -8h0TkId1f64TprLydwgzzLwXAs3wpmXz+BfnW1oMQPNyN7vojW6VzqJGGYLsc1OB -MgwObU/VeFNc6YUCmu6mfFJwoPfXMPnhmGuSwf/kjXomlejAYjxClU3UFVWQht54 -xNLjTi2M1ZOnwNbECOhXC3Tw3G8mCtfanMAO0UXM5yObbPa8yauUpJKkpoxWA7Ed -qiuUD9qRxluFPqqw/z86V8ikmvnyjQE9960j+8StlAbRs82ArtnrhRgkDO0Smtf7 -4QZsb/hA1KNMm73bOGS6+SVU+eH8FgVOzcTQYFRpRT3Mhi6dKZe9twIO8mpZK4wk -uygRxBM32Ag9QQIDAQABo1MwUTAdBgNVHQ4EFgQUc8tvoNNBHyIkoVV8XCXy63Ya -BEQwHwYDVR0jBBgwFoAUc8tvoNNBHyIkoVV8XCXy63YaBEQwDwYDVR0TAQH/BAUw -AwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAVmaUkkYESfcfgnuPeZ4sTNs2nk2Y+Xpd -lxkMJhChb8YQtlCe4uiLvVe7er1sXcBLNCm/+2K9AT71gnxBSeS5mEOzWmCPErhy -RmYtSxeRyXAaUWVYLs/zMlBQ0Iz4dpY+FVVbMjIurelVwHF0NBk3VtU5U3lHyKdZ -j4C2rMjvTxmkyIcR1uBEeVvuGU8R70nZ1yfo3vDwmNGMcLwW+4QK+WcfwfjLXhLs -5550arfEYdTzYFMxY60HJT/LvbGrjxY0PQUWWDbPiRfsdRjOFduAbM0/EVRda/Oo -Fg72WnHeojDUhqEz4UyFZbnRJ4x6leQhnrIcVjWX4FFFktiO9rqqfw== ------END CERTIFICATE-----`) - - caBundle1 = []byte(`-----BEGIN CERTIFICATE----- -MIIDCzCCAfOgAwIBAgIQbfOzhcKTldFipQ1X2WXpHDANBgkqhkiG9w0BAQsFADAv -MS0wKwYDVQQDEyRhNzU5YzcyZC1lNjcyLTQwMzYtYWMzYy1kYzAxMDBmMTVkNWUw -HhcNMTkwNTE2MjIxMTI2WhcNMjQwNTE0MjMxMTI2WjAvMS0wKwYDVQQDEyRhNzU5 -YzcyZC1lNjcyLTQwMzYtYWMzYy1kYzAxMDBmMTVkNWUwggEiMA0GCSqGSIb3DQEB -AQUAA4IBDwAwggEKAoIBAQC6sSAN80Ci0DYFpNDumGYoejMQai42g6nSKYS+ekvs -E7uT+eepO74wj8o6nFMNDu58+XgIsvPbWnn+3WtUjJfyiQXxmmTg8om4uY1C7R1H -gMsrL26pUaXZ/lTE8ZV5CnQJ9XilagY4iZKeptuZkxrWgkFBD7tr652EA3hmj+3h -4sTCQ+pBJKG8BJZDNRrCoiABYBMcFLJsaKuGZkJ6KtxhQEO9QxJVaDoSvlCRGa8R -fcVyYQyXOZ+0VHZJQgaLtqGpiQmlFttpCwDiLfMkk3UAd79ovkhN1MCq+O5N7YVt -eVQWaTUqUV2tKUFvVq21Zdl4dRaq+CF5U8uOqLY/4Kg9AgMBAAGjIzAhMA4GA1Ud -DwEB/wQEAwICBDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCg -oF71Ey2b1QY22C6BXcANF1+wPzxJovFeKYAnUqwh3rF7pIYCS/adZXOKlgDBsbcS -MxAGnCRi1s+A7hMYj3sQAbBXttc31557lRoJrx58IeN5DyshT53t7q4VwCzuCXFT -3zRHVRHQnO6LHgZx1FuKfwtkhfSXDyYU2fQYw2Hcb9krYU/alViVZdE0rENXCClq -xO7AQk5MJcGg6cfE5wWAKU1ATjpK4CN+RTn8v8ODLoI2SW3pfsnXxm93O+pp9HN4 -+O+1PQtNUWhCfh+g6BN2mYo2OEZ8qGSxDlMZej4YOdVkW8PHmFZTK0w9iJKqM5o1 -V6g5gZlqSoRhICK09tpc ------END CERTIFICATE-----`) -) - -// patch the caBundle into the final istiod and galley configs. -func init() { - webhookConfigEncoded = runtime.EncodeOrDie(codec, unpatchedWebhookConfig) - - webhookConfigWithCABundleIgnore = unpatchedWebhookConfig.DeepCopyObject().(*kubeApiAdmission.ValidatingWebhookConfiguration) - webhookConfigWithCABundleIgnore.Webhooks[0].ClientConfig.CABundle = caBundle0 - webhookConfigWithCABundleIgnore.Webhooks[1].ClientConfig.CABundle = caBundle0 - - webhookConfigWithCABundleFail = webhookConfigWithCABundleIgnore.DeepCopyObject().(*kubeApiAdmission.ValidatingWebhookConfiguration) - webhookConfigWithCABundleFail.Webhooks[0].FailurePolicy = &failurePolicyFail - webhookConfigWithCABundleFail.Webhooks[1].FailurePolicy = &failurePolicyFail -} - -type fakeController struct { - *Controller - - configStore cache.Store - - injectedMu sync.Mutex - - *fake.Clientset - istioFakeClient *istiofake.Clientset - client kube.Client -} - -const ( - istiod = "istio-revision" - namespace = "dubbo-system" - revision = "revision" -) - -var webhookName = fmt.Sprintf("istio-validator-revision-%s", namespace) - -func createTestController(t *testing.T) *fakeController { - fakeClient := kube.NewFakeClient() - watcher := keycertbundle.NewWatcher() - o := Options{ - WatchedNamespace: namespace, - ServiceName: istiod, - CABundleWatcher: watcher, - Revision: revision, - } - watcher.SetAndNotify(nil, nil, caBundle0) - - fc := &fakeController{ - client: fakeClient, - Clientset: fakeClient.Kube().(*fake.Clientset), - istioFakeClient: fakeClient.Istio().(*istiofake.Clientset), - } - - var err error - fc.Controller = newController(o, fakeClient) - if err != nil { - t.Fatalf("failed to create test controller: %v", err) - } - - fakeClient.RunAndWait(make(chan struct{})) - - fc.configStore = fakeClient.KubeInformer().Admissionregistration().V1().ValidatingWebhookConfigurations().Informer().GetStore() - - return fc -} - -func copyWithName(vwh *kubeApiAdmission.ValidatingWebhookConfiguration, newName string) *kubeApiAdmission.ValidatingWebhookConfiguration { - n := vwh.DeepCopyObject().(*kubeApiAdmission.ValidatingWebhookConfiguration) - n.Name = newName - return n -} - -func (fc *fakeController) ValidatingWebhookConfigurations() kubeTypedAdmission.ValidatingWebhookConfigurationInterface { - return fc.client.AdmissionregistrationV1().ValidatingWebhookConfigurations() -} - -func reconcileHelper(t *testing.T, c *fakeController, whName string) { - t.Helper() - - c.ClearActions() - if _, err := c.reconcileRequest(reconcileRequest{ - event: updateEvent, - webhookName: whName, - }); err != nil { - t.Fatalf("unexpected reconciliation error: %v", err) - } -} - -func TestGreenfield(t *testing.T) { - g := NewWithT(t) - c := createTestController(t) - - // install adds the webhook config with fail open policy - _, _ = c.ValidatingWebhookConfigurations().Create(context.TODO(), unpatchedWebhookConfig, kubeApiMeta.CreateOptions{}) - _ = c.configStore.Add(unpatchedWebhookConfig) - - reconcileHelper(t, c, unpatchedWebhookConfig.Name) - g.Expect(c.ValidatingWebhookConfigurations().Get(context.TODO(), webhookName, kubeApiMeta.GetOptions{})). - Should(Equal(webhookConfigWithCABundleIgnore), "no config update when endpoint not present") - - // verify the webhook isn't updated if invalid config is accepted. - c.istioFakeClient.PrependReactor("create", "gateways", func(action ktesting.Action) (bool, runtime.Object, error) { - return true, &v1alpha3.Gateway{}, nil - }) - reconcileHelper(t, c, unpatchedWebhookConfig.Name) - g.Expect(c.ValidatingWebhookConfigurations().Get(context.TODO(), webhookName, kubeApiMeta.GetOptions{})). - Should(Equal(webhookConfigWithCABundleIgnore), "no config update when endpoint invalid config is accepted") - - // verify the webhook is updated after the controller can confirm invalid config is rejected. - c.istioFakeClient.PrependReactor("create", "gateways", func(action ktesting.Action) (bool, runtime.Object, error) { - return true, &v1alpha3.Gateway{}, kubeErrors.NewInternalError(errors.New("unknown error")) - }) - reconcileHelper(t, c, unpatchedWebhookConfig.Name) - g.Expect(c.ValidatingWebhookConfigurations().Get(context.TODO(), webhookName, kubeApiMeta.GetOptions{})). - Should(Equal(webhookConfigWithCABundleIgnore), - "no config update when endpoint invalid config is rejected for an unknown reason") - - // verify the webhook is updated after the controller can confirm invalid config is rejected. - c.istioFakeClient.PrependReactor("create", "gateways", func(action ktesting.Action) (bool, runtime.Object, error) { - return true, &v1alpha3.Gateway{}, kubeErrors.NewInternalError(errors.New(deniedRequestMessageFragment)) - }) - reconcileHelper(t, c, unpatchedWebhookConfig.Name) - g.Expect(c.Actions()[1].Matches("update", "validatingwebhookconfigurations")).Should(BeTrue()) - g.Expect(c.ValidatingWebhookConfigurations().Get(context.TODO(), webhookName, kubeApiMeta.GetOptions{})). - Should(Equal(webhookConfigWithCABundleFail), - "istiod config created when endpoint is ready and invalid config is denied") -} - -// TestBackoff ensures when we fail to update the webhook, we are doing backoff -func TestBackoff(t *testing.T) { - c := createTestController(t) - maxAttempts := 5 - c.queue = workqueue.NewRateLimitingQueue(workqueue.NewItemFastSlowRateLimiter(1*time.Millisecond, 180*time.Minute, maxAttempts)) - - // install adds the webhook config with fail open policy - _, _ = c.ValidatingWebhookConfigurations().Create(context.TODO(), unpatchedWebhookConfig, kubeApiMeta.CreateOptions{}) - _ = c.configStore.Add(unpatchedWebhookConfig) - - stop := make(chan struct{}) - t.Cleanup(func() { - close(stop) - }) - go c.Run(stop) - // This is fairly difficult to properly test. Basically what we do is setup the queue to retry 5x quickly, then extremely slowly. - // This ensures that we are actually retrying using the provided rate limiter. - retry.UntilOrFail(t, func() bool { - fmt.Println("len(c.istioFakeClient.Actions()) ", len(c.istioFakeClient.Actions()), " != ", (2*maxAttempts + 1)) - return len(c.istioFakeClient.Actions()) == (2*maxAttempts + 1) - }, retry.Timeout(time.Second*5)) -} - -// TestUpdateAll ensures that we create request to update all webhooks when CA bundle changes. -func TestUpdateAll(t *testing.T) { - c := createTestController(t) - c.queue = workqueue.NewRateLimitingQueue(workqueue.NewItemExponentialFailureRateLimiter(1*time.Second, 1*time.Minute)) - - // add two validating webhook configurations - const secondName = "second" - _, _ = c.ValidatingWebhookConfigurations().Create(context.TODO(), unpatchedWebhookConfig, kubeApiMeta.CreateOptions{}) - _, _ = c.ValidatingWebhookConfigurations().Create(context.TODO(), copyWithName(unpatchedWebhookConfig, secondName), kubeApiMeta.CreateOptions{}) - _ = c.configStore.Add(unpatchedWebhookConfig) - _ = c.configStore.Add(copyWithName(unpatchedWebhookConfig, secondName)) - - // Run to populate the webhook informer. - stop := make(chan struct{}) - t.Cleanup(func() { - close(stop) - }) - go c.webhookInformer.Run(stop) - - retry.UntilOrFail(t, func() bool { - c.updateAll() - if c.queue.Len() != 2 { - return false - } - for c.queue.Len() > 0 { - item, _ := c.queue.Get() - rr := item.(reconcileRequest) - - // both "updateEvent" reconcile requests should have failed - // creating two "retryEvent" reconcile requests - if rr.event != retryEvent { - return false - } - } - return true - }, retry.Delay(time.Second), retry.Timeout(time.Second*5)) -} - -func TestCABundleChange(t *testing.T) { - g := NewWithT(t) - c := createTestController(t) - - _, _ = c.ValidatingWebhookConfigurations().Create(context.TODO(), unpatchedWebhookConfig, kubeApiMeta.CreateOptions{}) - _ = c.configStore.Add(unpatchedWebhookConfig) - c.istioFakeClient.PrependReactor("create", "gateways", func(action ktesting.Action) (bool, runtime.Object, error) { - return true, &v1alpha3.Gateway{}, kubeErrors.NewInternalError(errors.New(deniedRequestMessageFragment)) - }) - reconcileHelper(t, c, unpatchedWebhookConfig.Name) - g.Expect(c.ValidatingWebhookConfigurations().Get(context.TODO(), unpatchedWebhookConfig.Name, kubeApiMeta.GetOptions{})). - Should(Equal(webhookConfigWithCABundleFail), "istiod config created when endpoint is ready") - - // keep test store and tracker in-sync - _ = c.configStore.Add(webhookConfigWithCABundleFail) - - // verify the config updates after injecting a cafile change - c.injectedMu.Lock() - c.o.CABundleWatcher.SetAndNotify(nil, nil, caBundle1) - c.injectedMu.Unlock() - - webhookConfigAfterCAUpdate := webhookConfigWithCABundleFail.DeepCopyObject().(*kubeApiAdmission.ValidatingWebhookConfiguration) - webhookConfigAfterCAUpdate.Webhooks[0].ClientConfig.CABundle = caBundle1 - webhookConfigAfterCAUpdate.Webhooks[1].ClientConfig.CABundle = caBundle1 - - reconcileHelper(t, c, unpatchedWebhookConfig.Name) - g.Expect(c.ValidatingWebhookConfigurations().Get(context.TODO(), webhookName, kubeApiMeta.GetOptions{})). - Should(Equal(webhookConfigAfterCAUpdate), "webhook should change after cert change") - // keep test store and tracker in-sync - _ = c.configStore.Update(webhookConfigAfterCAUpdate) -} - -func TestLoadCaCertPem(t *testing.T) { - cases := []struct { - name string - cert []byte - wantError bool - }{ - { - name: "valid pem", - cert: testcerts.CACert, - wantError: false, - }, - { - name: "pem decode error", - cert: append([]byte("-----codec"), testcerts.CACert...), - wantError: true, - }, - { - name: "pem wrong type", - wantError: true, - }, - { - name: "invalid x509", - cert: testcerts.BadCert, - wantError: true, - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("[%v] %s", i, c.name), func(tt *testing.T) { - err := util.VerifyCABundle(c.cert) - if err != nil { - if !c.wantError { - tt.Fatalf("unexpected error: got error %q", err) - } - } else { - if c.wantError { - tt.Fatal("expected error") - } - } - }) - } -} diff --git a/pkg/webhooks/validation/controller/monitoring.go b/pkg/webhooks/validation/controller/monitoring.go deleted file mode 100644 index 267e55455..000000000 --- a/pkg/webhooks/validation/controller/monitoring.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controller - -import ( - "context" -) - -import ( - "go.opencensus.io/stats" - "go.opencensus.io/stats/view" - "go.opencensus.io/tag" - kubeMeta "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const ( - reason = "reason" -) - -// reasonTag holds the error reason for the context. -var reasonTag tag.Key - -var ( - metricWebhookConfigurationUpdateError = stats.Int64( - "galley/validation/config_update_error", - "k8s webhook configuration update error", - stats.UnitDimensionless) - metricWebhookConfigurationUpdates = stats.Int64( - "galley/validation_config_updates", - "k8s webhook configuration updates", - stats.UnitDimensionless) - metricWebhookConfigurationDeleteError = stats.Int64( - "galley/validation/config_delete_error", - "k8s webhook configuration delete error", - stats.UnitDimensionless) - metricWebhookConfigurationLoadError = stats.Int64( - "galley/validation/config_load_error", - "k8s webhook configuration (re)load error", - stats.UnitDimensionless) - metricWebhookConfigurationLoad = stats.Int64( - "galley/validation/config_load", - "k8s webhook configuration (re)loads", - stats.UnitDimensionless) -) - -func newView(measure stats.Measure, keys []tag.Key, aggregation *view.Aggregation) *view.View { - return &view.View{ - Name: measure.Name(), - Description: measure.Description(), - Measure: measure, - TagKeys: keys, - Aggregation: aggregation, - } -} - -func init() { - var err error - if reasonTag, err = tag.NewKey(reason); err != nil { - panic(err) - } - - var noKeys []tag.Key - reasonKey := []tag.Key{reasonTag} - - err = view.Register( - newView(metricWebhookConfigurationUpdateError, reasonKey, view.Count()), - newView(metricWebhookConfigurationUpdates, noKeys, view.Count()), - newView(metricWebhookConfigurationDeleteError, reasonKey, view.Count()), - newView(metricWebhookConfigurationLoadError, reasonKey, view.Count()), - newView(metricWebhookConfigurationLoad, noKeys, view.Count()), - ) - - if err != nil { - panic(err) - } -} - -func reportValidationConfigUpdateError(reason kubeMeta.StatusReason) { - ctx, err := tag.New(context.Background(), tag.Insert(reasonTag, string(reason))) - if err != nil { - scope.Errorf("Error creating monitoring context for reportValidationConfigUpdateError: %v", err) - } else { - stats.Record(ctx, metricWebhookConfigurationUpdateError.M(1)) - } -} - -func reportValidationConfigLoadError(reason string) { - ctx, err := tag.New(context.Background(), tag.Insert(reasonTag, reason)) - if err != nil { - scope.Errorf("Error creating monitoring context for reportValidationConfigLoadError: %v", err) - } else { - stats.Record(ctx, metricWebhookConfigurationLoadError.M(1)) - } -} - -func reportValidationConfigUpdate() { - stats.Record(context.Background(), metricWebhookConfigurationUpdates.M(1)) -} diff --git a/pkg/webhooks/validation/server/monitoring.go b/pkg/webhooks/validation/server/monitoring.go deleted file mode 100644 index f1fff8b2b..000000000 --- a/pkg/webhooks/validation/server/monitoring.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "strconv" -) - -import ( - "istio.io/pkg/monitoring" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -const ( - group = "group" - version = "version" - resourceTag = "resource" - reason = "reason" - status = "status" -) - -var ( - // GroupTag holds the resource group for the context. - GroupTag = monitoring.MustCreateLabel(group) - - // VersionTag holds the resource version for the context. - VersionTag = monitoring.MustCreateLabel(version) - - // ResourceTag holds the resource name for the context. - ResourceTag = monitoring.MustCreateLabel(resourceTag) - - // ReasonTag holds the error reason for the context. - ReasonTag = monitoring.MustCreateLabel(reason) - - // StatusTag holds the error code for the context. - StatusTag = monitoring.MustCreateLabel(status) -) - -var ( - metricValidationPassed = monitoring.NewSum( - "galley/validation/passed", - "Resource is valid", - monitoring.WithLabels(GroupTag, VersionTag, ResourceTag), - ) - metricValidationFailed = monitoring.NewSum( - "galley/validation/failed", - "Resource validation failed", - monitoring.WithLabels(GroupTag, VersionTag, ResourceTag, ReasonTag), - ) - metricValidationHTTPError = monitoring.NewSum( - "galley/validation/http_error", - "Resource validation http serve errors", - monitoring.WithLabels(StatusTag), - ) -) - -func init() { - monitoring.MustRegister( - metricValidationPassed, - metricValidationFailed, - metricValidationHTTPError, - ) -} - -func reportValidationFailed(request *kube.AdmissionRequest, reason string) { - metricValidationFailed. - With(GroupTag.Value(request.Resource.Group)). - With(VersionTag.Value(request.Resource.Version)). - With(ResourceTag.Value(request.Resource.Resource)). - With(ReasonTag.Value(reason)). - Increment() -} - -func reportValidationPass(request *kube.AdmissionRequest) { - metricValidationPassed. - With(GroupTag.Value(request.Resource.Group)). - With(VersionTag.Value(request.Resource.Version)). - With(ResourceTag.Value(request.Resource.Resource)). - Increment() -} - -func reportValidationHTTPError(status int) { - metricValidationHTTPError. - With(StatusTag.Value(strconv.Itoa(status))). - Increment() -} - -const ( - reasonUnsupportedOperation = "unsupported_operation" - reasonYamlDecodeError = "yaml_decode_error" - reasonUnknownType = "unknown_type" - reasonCRDConversionError = "crd_conversion_error" - reasonInvalidConfig = "invalid_resource" -) diff --git a/pkg/webhooks/validation/server/server.go b/pkg/webhooks/validation/server/server.go deleted file mode 100644 index a3e21ced1..000000000 --- a/pkg/webhooks/validation/server/server.go +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "net/http" -) - -import ( - multierror "github.com/hashicorp/go-multierror" - "istio.io/pkg/log" - kubeApiAdmissionv1 "k8s.io/api/admission/v1" - kubeApiAdmissionv1beta1 "k8s.io/api/admission/v1beta1" - kubeApiApps "k8s.io/api/apps/v1beta1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/serializer" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/config/kube/crd" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/resource" - "github.com/apache/dubbo-go-pixiu/pkg/config/validation" - "github.com/apache/dubbo-go-pixiu/pkg/kube" -) - -var scope = log.RegisterScope("validationServer", "validation webhook server", 0) - -var ( - runtimeScheme = runtime.NewScheme() - codecs = serializer.NewCodecFactory(runtimeScheme) - deserializer = codecs.UniversalDeserializer() - - // Expect AdmissionRequest to only include these top-level field names - validFields = map[string]bool{ - "apiVersion": true, - "kind": true, - "metadata": true, - "spec": true, - "status": true, - } -) - -func init() { - _ = kubeApiApps.AddToScheme(runtimeScheme) - _ = kubeApiAdmissionv1.AddToScheme(runtimeScheme) - _ = kubeApiAdmissionv1beta1.AddToScheme(runtimeScheme) -} - -// Options contains the configuration for the Istio Pilot validation -// admission controller. -type Options struct { - // Schemas provides a description of all configuration resources. - Schemas collection.Schemas - - // DomainSuffix is the DNS domain suffix for Pilot CRD resources, - // e.g. cluster.local. - DomainSuffix string - - // Port where the webhook is served. the number should be greater than 1024 for non-root - // user, because non-root user cannot bind port number less than 1024 - // Mainly used for testing. Webhook server is started by Istiod. - Port uint - - // Use an existing mux instead of creating our own. - Mux *http.ServeMux -} - -// String produces a stringified version of the arguments for debugging. -func (o Options) String() string { - buf := &bytes.Buffer{} - - _, _ = fmt.Fprintf(buf, "DomainSuffix: %s\n", o.DomainSuffix) - _, _ = fmt.Fprintf(buf, "Port: %d\n", o.Port) - - return buf.String() -} - -// DefaultArgs allocates an Options struct initialized with Webhook's default configuration. -func DefaultArgs() Options { - return Options{ - Port: 9443, - } -} - -// Webhook implements the validating admission webhook for validating Istio configuration. -type Webhook struct { - // pilot - schemas collection.Schemas - domainSuffix string -} - -// New creates a new instance of the admission webhook server. -func New(o Options) (*Webhook, error) { - if o.Mux == nil { - scope.Error("mux not set correctly") - return nil, errors.New("expected mux to be passed, but was not passed") - } - wh := &Webhook{ - schemas: o.Schemas, - domainSuffix: o.DomainSuffix, - } - - o.Mux.HandleFunc("/validate", wh.serveValidate) - o.Mux.HandleFunc("/validate/", wh.serveValidate) - - return wh, nil -} - -func toAdmissionResponse(err error) *kube.AdmissionResponse { - return &kube.AdmissionResponse{Result: &metav1.Status{Message: err.Error()}} -} - -type admitFunc func(*kube.AdmissionRequest) *kube.AdmissionResponse - -func serve(w http.ResponseWriter, r *http.Request, admit admitFunc) { - var body []byte - if r.Body != nil { - if data, err := kube.HTTPConfigReader(r); err == nil { - body = data - } else { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - } - if len(body) == 0 { - reportValidationHTTPError(http.StatusBadRequest) - http.Error(w, "no body found", http.StatusBadRequest) - return - } - - // verify the content type is accurate - contentType := r.Header.Get("Content-Type") - if contentType != "application/json" { - reportValidationHTTPError(http.StatusUnsupportedMediaType) - http.Error(w, "invalid Content-Type, want `application/json`", http.StatusUnsupportedMediaType) - return - } - - var reviewResponse *kube.AdmissionResponse - var obj runtime.Object - var ar *kube.AdmissionReview - if out, _, err := deserializer.Decode(body, nil, obj); err != nil { - reviewResponse = toAdmissionResponse(fmt.Errorf("could not decode body: %v", err)) - } else { - ar, err = kube.AdmissionReviewKubeToAdapter(out) - if err != nil { - reviewResponse = toAdmissionResponse(fmt.Errorf("could not decode object: %v", err)) - } else { - reviewResponse = admit(ar.Request) - } - } - - response := kube.AdmissionReview{} - response.Response = reviewResponse - var responseKube runtime.Object - var apiVersion string - if ar != nil { - apiVersion = ar.APIVersion - response.TypeMeta = ar.TypeMeta - if response.Response != nil { - if ar.Request != nil { - response.Response.UID = ar.Request.UID - } - } - } - responseKube = kube.AdmissionReviewAdapterToKube(&response, apiVersion) - resp, err := json.Marshal(responseKube) - if err != nil { - reportValidationHTTPError(http.StatusInternalServerError) - http.Error(w, fmt.Sprintf("could encode response: %v", err), http.StatusInternalServerError) - return - } - if _, err := w.Write(resp); err != nil { - reportValidationHTTPError(http.StatusInternalServerError) - http.Error(w, fmt.Sprintf("could write response: %v", err), http.StatusInternalServerError) - } -} - -func (wh *Webhook) serveValidate(w http.ResponseWriter, r *http.Request) { - serve(w, r, wh.validate) -} - -func (wh *Webhook) validate(request *kube.AdmissionRequest) *kube.AdmissionResponse { - switch request.Operation { - case kube.Create, kube.Update: - default: - scope.Warnf("Unsupported webhook operation %v", request.Operation) - reportValidationFailed(request, reasonUnsupportedOperation) - return &kube.AdmissionResponse{Allowed: true} - } - - var obj crd.IstioKind - if err := json.Unmarshal(request.Object.Raw, &obj); err != nil { - scope.Infof("cannot decode configuration: %v", err) - reportValidationFailed(request, reasonYamlDecodeError) - return toAdmissionResponse(fmt.Errorf("cannot decode configuration: %v", err)) - } - - gvk := obj.GroupVersionKind() - - // TODO(jasonwzm) remove this when multi-version is supported. v1beta1 shares the same - // schema as v1lalpha3. Fake conversion and validate against v1alpha3. - if gvk.Group == "networking.istio.io" && gvk.Version == "v1beta1" && - // ProxyConfig CR is stored as v1beta1 since it was introduced as v1beta1 - gvk.Kind != collections.IstioNetworkingV1Beta1Proxyconfigs.Resource().Kind() { - gvk.Version = "v1alpha3" - } - s, exists := wh.schemas.FindByGroupVersionKind(resource.FromKubernetesGVK(&gvk)) - if !exists { - scope.Infof("unrecognized type %v", obj.GroupVersionKind()) - reportValidationFailed(request, reasonUnknownType) - return toAdmissionResponse(fmt.Errorf("unrecognized type %v", obj.GroupVersionKind())) - } - - out, err := crd.ConvertObject(s, &obj, wh.domainSuffix) - if err != nil { - scope.Infof("error decoding configuration: %v", err) - reportValidationFailed(request, reasonCRDConversionError) - return toAdmissionResponse(fmt.Errorf("error decoding configuration: %v", err)) - } - - warnings, err := s.Resource().ValidateConfig(*out) - if err != nil { - scope.Infof("configuration is invalid: %v", err) - reportValidationFailed(request, reasonInvalidConfig) - return toAdmissionResponse(fmt.Errorf("configuration is invalid: %v", err)) - } - - if reason, err := checkFields(request.Object.Raw, request.Kind.Kind, request.Namespace, obj.Name); err != nil { - reportValidationFailed(request, reason) - return toAdmissionResponse(err) - } - - reportValidationPass(request) - return &kube.AdmissionResponse{Allowed: true, Warnings: toKubeWarnings(warnings)} -} - -func toKubeWarnings(warn validation.Warning) []string { - if warn == nil { - return nil - } - me, ok := warn.(*multierror.Error) - if ok { - res := []string{} - for _, e := range me.Errors { - res = append(res, e.Error()) - } - return res - } - return []string{warn.Error()} -} - -func checkFields(raw []byte, kind string, namespace string, name string) (string, error) { - trial := make(map[string]json.RawMessage) - if err := json.Unmarshal(raw, &trial); err != nil { - scope.Infof("cannot decode configuration fields: %v", err) - return reasonYamlDecodeError, fmt.Errorf("cannot decode configuration fields: %v", err) - } - - for key := range trial { - if _, ok := validFields[key]; !ok { - scope.Infof("unknown field %q on %s resource %s/%s", - key, kind, namespace, name) - return reasonInvalidConfig, fmt.Errorf("unknown field %q on %s resource %s/%s", - key, kind, namespace, name) - } - } - - return "", nil -} - -// validatePort checks that the network port is in range -func validatePort(port int) error { - if 1 <= port && port <= 65535 { - return nil - } - return fmt.Errorf("port number %d must be in the range 1..65535", port) -} - -// Validate tests if the Options has valid params. -func (o Options) Validate() error { - var errs *multierror.Error - if err := validatePort(int(o.Port)); err != nil { - errs = multierror.Append(errs, err) - } - return errs.ErrorOrNil() -} diff --git a/pkg/webhooks/validation/server/server_test.go b/pkg/webhooks/validation/server/server_test.go deleted file mode 100644 index d1bf7d34d..000000000 --- a/pkg/webhooks/validation/server/server_test.go +++ /dev/null @@ -1,388 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "net/http" - "net/http/httptest" - "os" - "path/filepath" - "strconv" - "strings" - "testing" -) - -import ( - kubeApiAdmission "k8s.io/api/admission/v1beta1" - kubeApisMeta "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/config" - "github.com/apache/dubbo-go-pixiu/pkg/testcerts" -) - -const ( - // testDomainSuffix is the default DNS domain suffix for Istio - // CRD resources. - testDomainSuffix = "local.cluster" -) - -func TestArgs_String(t *testing.T) { - p := DefaultArgs() - // Should not crash - _ = p.String() -} - -func createTestWebhook(t testing.TB) *Webhook { - t.Helper() - dir := t.TempDir() - - var ( - certFile = filepath.Join(dir, "cert-file.yaml") - keyFile = filepath.Join(dir, "key-file.yaml") - port = uint(0) - ) - - // cert - if err := os.WriteFile(certFile, testcerts.ServerCert, 0o644); err != nil { // nolint: vetshadow - t.Fatalf("WriteFile(%v) failed: %v", certFile, err) - } - // key - if err := os.WriteFile(keyFile, testcerts.ServerKey, 0o644); err != nil { // nolint: vetshadow - t.Fatalf("WriteFile(%v) failed: %v", keyFile, err) - } - - options := Options{ - Port: port, - DomainSuffix: testDomainSuffix, - Schemas: collections.Mocks, - Mux: http.NewServeMux(), - } - wh, err := New(options) - if err != nil { - t.Fatalf("New() failed: %v", err) - } - - return wh -} - -func makePilotConfig(t *testing.T, i int, validConfig bool, includeBogusKey bool) []byte { // nolint: unparam - t.Helper() - - var key string - if validConfig { - key = "key" - } - - name := fmt.Sprintf("%s%d", "mock-config", i) - - r := collections.Mock.Resource() - var un unstructured.Unstructured - un.SetGroupVersionKind(schema.GroupVersionKind{ - Group: r.Group(), - Version: r.Version(), - Kind: r.Kind(), - }) - un.SetName(name) - un.SetLabels(map[string]string{"key": name}) - un.SetAnnotations(map[string]string{"annotationKey": name}) - un.Object["spec"] = &config.MockConfig{ - Key: key, - Pairs: []*config.ConfigPair{{ - Key: key, - Value: strconv.Itoa(i), - }}, - } - raw, err := json.Marshal(&un) - if err != nil { - t.Fatalf("Marshal(%v) failed: %v", name, err) - } - if includeBogusKey { - trial := make(map[string]interface{}) - if err := json.Unmarshal(raw, &trial); err != nil { - t.Fatalf("Unmarshal(%v) failed: %v", name, err) - } - trial["unexpected_key"] = "any value" - if raw, err = json.Marshal(&trial); err != nil { - t.Fatalf("re-Marshal(%v) failed: %v", name, err) - } - } - return raw -} - -func TestAdmitPilot(t *testing.T) { - valid := makePilotConfig(t, 0, true, false) - invalidConfig := makePilotConfig(t, 0, false, false) - extraKeyConfig := makePilotConfig(t, 0, true, true) - - wh := createTestWebhook(t) - - cases := []struct { - name string - admit admitFunc - in *kube.AdmissionRequest - allowed bool - }{ - { - name: "valid create", - admit: wh.validate, - in: &kube.AdmissionRequest{ - Kind: kubeApisMeta.GroupVersionKind{Kind: collections.Mock.Resource().Kind()}, - Object: runtime.RawExtension{Raw: valid}, - Operation: kube.Create, - }, - allowed: true, - }, - { - name: "valid update", - admit: wh.validate, - in: &kube.AdmissionRequest{ - Kind: kubeApisMeta.GroupVersionKind{Kind: collections.Mock.Resource().Kind()}, - Object: runtime.RawExtension{Raw: valid}, - Operation: kube.Update, - }, - allowed: true, - }, - { - name: "unsupported operation", - admit: wh.validate, - in: &kube.AdmissionRequest{ - Kind: kubeApisMeta.GroupVersionKind{Kind: collections.Mock.Resource().Kind()}, - Object: runtime.RawExtension{Raw: valid}, - Operation: kube.Delete, - }, - allowed: true, - }, - { - name: "invalid spec", - admit: wh.validate, - in: &kube.AdmissionRequest{ - Kind: kubeApisMeta.GroupVersionKind{Kind: collections.Mock.Resource().Kind()}, - Object: runtime.RawExtension{Raw: invalidConfig}, - Operation: kube.Create, - }, - allowed: false, - }, - { - name: "corrupt object", - admit: wh.validate, - in: &kube.AdmissionRequest{ - Kind: kubeApisMeta.GroupVersionKind{Kind: collections.Mock.Resource().Kind()}, - Object: runtime.RawExtension{Raw: append([]byte("---"), valid...)}, - Operation: kube.Create, - }, - allowed: false, - }, - { - name: "invalid extra key create", - admit: wh.validate, - in: &kube.AdmissionRequest{ - Kind: kubeApisMeta.GroupVersionKind{Kind: collections.Mock.Resource().Kind()}, - Object: runtime.RawExtension{Raw: extraKeyConfig}, - Operation: kube.Create, - }, - allowed: false, - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("[%d] %s", i, c.name), func(t *testing.T) { - got := wh.validate(c.in) - if got.Allowed != c.allowed { - t.Fatalf("got %v want %v", got.Allowed, c.allowed) - } - }) - } -} - -func makeTestReview(t *testing.T, valid bool, apiVersion string) []byte { - t.Helper() - review := kubeApiAdmission.AdmissionReview{ - TypeMeta: kubeApisMeta.TypeMeta{ - Kind: "AdmissionReview", - APIVersion: fmt.Sprintf("admission.k8s.io/%s", apiVersion), - }, - Request: &kubeApiAdmission.AdmissionRequest{ - Kind: kubeApisMeta.GroupVersionKind{ - Group: kubeApiAdmission.GroupName, - Version: apiVersion, - Kind: "AdmissionRequest", - }, - Object: runtime.RawExtension{ - Raw: makePilotConfig(t, 0, valid, false), - }, - Operation: kubeApiAdmission.Create, - }, - } - reviewJSON, err := json.Marshal(review) - if err != nil { - t.Fatalf("Failed to create AdmissionReview: %v", err) - } - return reviewJSON -} - -func TestServe(t *testing.T) { - _ = createTestWebhook(t) - stop := make(chan struct{}) - defer func() { - close(stop) - }() - - validReview := makeTestReview(t, true, "v1beta1") - validReviewV1 := makeTestReview(t, true, "v1") - invalidReview := makeTestReview(t, false, "v1beta1") - - cases := []struct { - name string - body []byte - contentType string - wantStatusCode int - wantAllowed bool - allowedResponse bool - }{ - { - name: "valid", - body: validReview, - contentType: "application/json", - wantAllowed: true, - wantStatusCode: http.StatusOK, - allowedResponse: true, - }, - { - name: "valid(v1 version)", - body: validReviewV1, - contentType: "application/json", - wantAllowed: true, - wantStatusCode: http.StatusOK, - allowedResponse: true, - }, - { - name: "invalid", - body: invalidReview, - contentType: "application/json", - wantAllowed: false, - wantStatusCode: http.StatusOK, - }, - { - name: "wrong content-type", - body: validReview, - contentType: "application/yaml", - wantAllowed: false, - wantStatusCode: http.StatusUnsupportedMediaType, - }, - { - name: "bad content", - body: []byte{0, 1, 2, 3, 4, 5}, // random data - contentType: "application/json", - wantAllowed: false, - wantStatusCode: http.StatusOK, - }, - { - name: "no content", - body: []byte{}, - contentType: "application/json", - wantAllowed: false, - wantStatusCode: http.StatusBadRequest, - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("[%d] %s", i, c.name), func(t *testing.T) { - req := httptest.NewRequest("POST", "http://validator", bytes.NewReader(c.body)) - req.Header.Add("Content-Type", c.contentType) - w := httptest.NewRecorder() - - serve(w, req, func(*kube.AdmissionRequest) *kube.AdmissionResponse { - return &kube.AdmissionResponse{Allowed: c.allowedResponse} - }) - - res := w.Result() - - if res.StatusCode != c.wantStatusCode { - t.Fatalf("%v: wrong status code: \ngot %v \nwant %v", c.name, res.StatusCode, c.wantStatusCode) - } - - if res.StatusCode != http.StatusOK { - return - } - - gotBody, err := io.ReadAll(res.Body) - if err != nil { - t.Fatalf("%v: could not read body: %v", c.name, err) - } - var gotReview kubeApiAdmission.AdmissionReview - if err := json.Unmarshal(gotBody, &gotReview); err != nil { - t.Fatalf("%v: could not decode response body: %v", c.name, err) - } - if gotReview.Response.Allowed != c.wantAllowed { - t.Fatalf("%v: AdmissionReview.Response.Allowed is wrong : got %v want %v", - c.name, gotReview.Response.Allowed, c.wantAllowed) - } - }) - } -} - -// scenario is a common struct used by many tests in this context. -type scenario struct { - wrapFunc func(*Options) - expectedError string -} - -func TestValidate(t *testing.T) { - scenarios := map[string]scenario{ - "valid": { - wrapFunc: func(args *Options) {}, - expectedError: "", - }, - "invalid port": { - wrapFunc: func(args *Options) { args.Port = 100000 }, - expectedError: "port number 100000 must be in the range 1..65535", - }, - } - - for name, scenario := range scenarios { - t.Run(name, func(tt *testing.T) { - runTestCode(name, tt, scenario) - }) - } -} - -func runTestCode(name string, t *testing.T, test scenario) { - args := DefaultArgs() - - test.wrapFunc(&args) - err := args.Validate() - if err == nil && test.expectedError != "" { - t.Errorf("Test %q failed: expected error: %q, got nil", name, test.expectedError) - } - if err != nil { - if test.expectedError == "" { - t.Errorf("Test %q failed: expected nil error, got %v", name, err) - } - if !strings.Contains(err.Error(), test.expectedError) { - t.Errorf("Test %q failed: expected error: %q, got %q", name, test.expectedError, err.Error()) - } - } -} diff --git a/pkg/webhooks/webhookpatch.go b/pkg/webhooks/webhookpatch.go deleted file mode 100644 index 336f17e64..000000000 --- a/pkg/webhooks/webhookpatch.go +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package webhooks - -import ( - "bytes" - "context" - "errors" - "fmt" - "strings" -) - -import ( - "istio.io/api/label" - "istio.io/pkg/log" - v1 "k8s.io/api/admissionregistration/v1" - kubeErrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - admissioninformer "k8s.io/client-go/informers/admissionregistration/v1" - "k8s.io/client-go/kubernetes" - admissionregistrationv1client "k8s.io/client-go/kubernetes/typed/admissionregistration/v1" - "k8s.io/client-go/tools/cache" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/keycertbundle" - kubelib "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/kube/controllers" - "github.com/apache/dubbo-go-pixiu/pkg/webhooks/util" -) - -var ( - errWrongRevision = errors.New("webhook does not belong to target revision") - errNotFound = errors.New("webhook not found") - errNoWebhookWithName = errors.New("webhook configuration did not contain webhook with target name") -) - -// WebhookCertPatcher listens for webhooks on specified revision and patches their CA bundles -type WebhookCertPatcher struct { - client kubernetes.Interface - - // revision to patch webhooks for - revision string - webhookName string - - queue controllers.Queue - - // File path to the x509 certificate bundle used by the webhook server - // and patched into the webhook config. - CABundleWatcher *keycertbundle.Watcher - - informer cache.SharedIndexInformer -} - -// NewWebhookCertPatcher creates a WebhookCertPatcher -func NewWebhookCertPatcher( - client kubelib.Client, - revision, webhookName string, caBundleWatcher *keycertbundle.Watcher) (*WebhookCertPatcher, error) { - p := &WebhookCertPatcher{ - client: client, - revision: revision, - webhookName: webhookName, - CABundleWatcher: caBundleWatcher, - } - p.queue = controllers.NewQueue("webhook patcher", - controllers.WithReconciler(p.webhookPatchTask), - controllers.WithMaxAttempts(5)) - informer := admissioninformer.NewFilteredMutatingWebhookConfigurationInformer(client, 0, cache.Indexers{}, func(options *metav1.ListOptions) { - options.LabelSelector = fmt.Sprintf("%s=%s", label.IoIstioRev.Name, revision) - }) - p.informer = informer - informer.AddEventHandler(controllers.ObjectHandler(p.queue.AddObject)) - - return p, nil -} - -// Run runs the WebhookCertPatcher -func (w *WebhookCertPatcher) Run(stopChan <-chan struct{}) { - go w.informer.Run(stopChan) - go w.startCaBundleWatcher(stopChan) - w.queue.Run(stopChan) -} - -func (w *WebhookCertPatcher) HasSynced() bool { - return w.informer.HasSynced() && w.queue.HasSynced() -} - -// webhookPatchTask takes the result of patchMutatingWebhookConfig and modifies the result for use in task queue -func (w *WebhookCertPatcher) webhookPatchTask(o types.NamespacedName) error { - reportWebhookPatchAttempts(o.Name) - err := w.patchMutatingWebhookConfig( - w.client.AdmissionregistrationV1().MutatingWebhookConfigurations(), - o.Name) - - // do not want to retry the task if these errors occur, they indicate that - // we should no longer be patching the given webhook - if kubeErrors.IsNotFound(err) || errors.Is(err, errWrongRevision) || errors.Is(err, errNoWebhookWithName) || errors.Is(err, errNotFound) { - return nil - } - - if err != nil { - log.Errorf("patching webhook %s failed: %v", o.Name, err) - reportWebhookPatchRetry(o.Name) - } - - return err -} - -// patchMutatingWebhookConfig takes a webhookConfigName and patches the CA bundle for that webhook configuration -func (w *WebhookCertPatcher) patchMutatingWebhookConfig( - client admissionregistrationv1client.MutatingWebhookConfigurationInterface, - webhookConfigName string) error { - raw, _, err := w.informer.GetIndexer().GetByKey(webhookConfigName) - if raw == nil || err != nil { - reportWebhookPatchFailure(webhookConfigName, reasonWebhookConfigNotFound) - return errNotFound - } - config := raw.(*v1.MutatingWebhookConfiguration) - // prevents a race condition between multiple istiods when the revision is changed or modified - v, ok := config.Labels[label.IoIstioRev.Name] - if v != w.revision || !ok { - reportWebhookPatchFailure(webhookConfigName, reasonWrongRevision) - return errWrongRevision - } - - found := false - updated := false - caCertPem, err := util.LoadCABundle(w.CABundleWatcher) - if err != nil { - log.Errorf("Failed to load CA bundle: %v", err) - reportWebhookPatchFailure(webhookConfigName, reasonLoadCABundleFailure) - return err - } - for i, wh := range config.Webhooks { - if strings.HasSuffix(wh.Name, w.webhookName) { - if !bytes.Equal(caCertPem, config.Webhooks[i].ClientConfig.CABundle) { - updated = true - } - config.Webhooks[i].ClientConfig.CABundle = caCertPem - found = true - } - } - if !found { - reportWebhookPatchFailure(webhookConfigName, reasonWebhookEntryNotFound) - return errNoWebhookWithName - } - - if updated { - _, err = client.Update(context.Background(), config, metav1.UpdateOptions{}) - if err != nil { - reportWebhookPatchFailure(webhookConfigName, reasonWebhookUpdateFailure) - } - } - - return err -} - -// startCaBundleWatcher listens for updates to the CA bundle and patches the webhooks. -func (w *WebhookCertPatcher) startCaBundleWatcher(stop <-chan struct{}) { - id, watchCh := w.CABundleWatcher.AddWatcher() - defer w.CABundleWatcher.RemoveWatcher(id) - for { - select { - case <-watchCh: - lists := w.informer.GetStore().List() - for _, list := range lists { - mutatingWebhookConfig := list.(*v1.MutatingWebhookConfiguration) - if mutatingWebhookConfig == nil { - continue - } - log.Debugf("updating caBundle for webhook %q", mutatingWebhookConfig.Name) - w.queue.Add(types.NamespacedName{Name: mutatingWebhookConfig.Name}) - } - case <-stop: - return - } - } -} diff --git a/pkg/webhooks/webhookpatch_test.go b/pkg/webhooks/webhookpatch_test.go deleted file mode 100644 index dd8f836d3..000000000 --- a/pkg/webhooks/webhookpatch_test.go +++ /dev/null @@ -1,277 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package webhooks - -import ( - "bytes" - "context" - "strings" - "testing" -) - -import ( - "istio.io/api/label" - admissionregistrationv1 "k8s.io/api/admissionregistration/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pilot/pkg/keycertbundle" - "github.com/apache/dubbo-go-pixiu/pkg/kube" - "github.com/apache/dubbo-go-pixiu/pkg/test/util/retry" -) - -var caBundle0 = []byte(`-----BEGIN CERTIFICATE----- -MIIC9DCCAdygAwIBAgIJAIFe3lWPaalKMA0GCSqGSIb3DQEBCwUAMA4xDDAKBgNV -BAMMA19jYTAgFw0xNzEyMjIxODA0MjRaGA8yMjkxMTAwNzE4MDQyNFowDjEMMAoG -A1UEAwwDX2NhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuBdxj+Hi -8h0TkId1f64TprLydwgzzLwXAs3wpmXz+BfnW1oMQPNyN7vojW6VzqJGGYLsc1OB -MgwObU/VeFNc6YUCmu6mfFJwoPfXMPnhmGuSwf/kjXomlejAYjxClU3UFVWQht54 -xNLjTi2M1ZOnwNbECOhXC3Tw3G8mCtfanMAO0UXM5yObbPa8yauUpJKkpoxWA7Ed -qiuUD9qRxluFPqqw/z86V8ikmvnyjQE9960j+8StlAbRs82ArtnrhRgkDO0Smtf7 -4QZsb/hA1KNMm73bOGS6+SVU+eH8FgVOzcTQYFRpRT3Mhi6dKZe9twIO8mpZK4wk -uygRxBM32Ag9QQIDAQABo1MwUTAdBgNVHQ4EFgQUc8tvoNNBHyIkoVV8XCXy63Ya -BEQwHwYDVR0jBBgwFoAUc8tvoNNBHyIkoVV8XCXy63YaBEQwDwYDVR0TAQH/BAUw -AwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAVmaUkkYESfcfgnuPeZ4sTNs2nk2Y+Xpd -lxkMJhChb8YQtlCe4uiLvVe7er1sXcBLNCm/+2K9AT71gnxBSeS5mEOzWmCPErhy -RmYtSxeRyXAaUWVYLs/zMlBQ0Iz4dpY+FVVbMjIurelVwHF0NBk3VtU5U3lHyKdZ -j4C2rMjvTxmkyIcR1uBEeVvuGU8R70nZ1yfo3vDwmNGMcLwW+4QK+WcfwfjLXhLs -5550arfEYdTzYFMxY60HJT/LvbGrjxY0PQUWWDbPiRfsdRjOFduAbM0/EVRda/Oo -Fg72WnHeojDUhqEz4UyFZbnRJ4x6leQhnrIcVjWX4FFFktiO9rqqfw== ------END CERTIFICATE-----`) - -func TestMutatingWebhookPatch(t *testing.T) { - testRevision := "test-revision" - wrongRevision := "wrong-revision" - testRevisionLabel := map[string]string{label.IoIstioRev.Name: testRevision} - wrongRevisionLabel := map[string]string{label.IoIstioRev.Name: wrongRevision} - watcher := &keycertbundle.Watcher{} - watcher.SetAndNotify(nil, nil, caBundle0) - ts := []struct { - name string - configs admissionregistrationv1.MutatingWebhookConfigurationList - revision string - configName string - webhookName string - pemData []byte - err string - }{ - { - "WebhookConfigNotFound", - admissionregistrationv1.MutatingWebhookConfigurationList{}, - testRevision, - "config1", - "webhook1", - caBundle0, - errNotFound.Error(), - }, - { - "WebhookEntryNotFound", - admissionregistrationv1.MutatingWebhookConfigurationList{ - Items: []admissionregistrationv1.MutatingWebhookConfiguration{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "config1", - Labels: testRevisionLabel, - }, - }, - }, - }, - testRevision, - "config1", - "webhook1", - caBundle0, - errNoWebhookWithName.Error(), - }, - { - "SuccessfullyPatched", - admissionregistrationv1.MutatingWebhookConfigurationList{ - Items: []admissionregistrationv1.MutatingWebhookConfiguration{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "config1", - Labels: testRevisionLabel, - }, - Webhooks: []admissionregistrationv1.MutatingWebhook{ - { - Name: "webhook1", - ClientConfig: admissionregistrationv1.WebhookClientConfig{}, - }, - }, - }, - }, - }, - testRevision, - "config1", - "webhook1", - caBundle0, - "", - }, - { - "Prefix", - admissionregistrationv1.MutatingWebhookConfigurationList{ - Items: []admissionregistrationv1.MutatingWebhookConfiguration{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "config1", - Labels: testRevisionLabel, - }, - Webhooks: []admissionregistrationv1.MutatingWebhook{ - { - Name: "prefix.webhook1", - ClientConfig: admissionregistrationv1.WebhookClientConfig{}, - }, - }, - }, - }, - }, - testRevision, - "config1", - "webhook1", - caBundle0, - "", - }, - { - "NoRevisionWebhookNotUpdated", - admissionregistrationv1.MutatingWebhookConfigurationList{ - Items: []admissionregistrationv1.MutatingWebhookConfiguration{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "config1", - }, - Webhooks: []admissionregistrationv1.MutatingWebhook{ - { - Name: "webhook1", - ClientConfig: admissionregistrationv1.WebhookClientConfig{}, - }, - }, - }, - }, - }, - testRevision, - "config1", - "webhook1", - caBundle0, - errNotFound.Error(), - }, - { - "WrongRevisionWebhookNotUpdated", - admissionregistrationv1.MutatingWebhookConfigurationList{ - Items: []admissionregistrationv1.MutatingWebhookConfiguration{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "config1", - Labels: wrongRevisionLabel, - }, - Webhooks: []admissionregistrationv1.MutatingWebhook{ - { - Name: "webhook1", - ClientConfig: admissionregistrationv1.WebhookClientConfig{}, - }, - }, - }, - }, - }, - testRevision, - "config1", - "webhook1", - caBundle0, - errNotFound.Error(), - }, - { - "MultipleWebhooks", - admissionregistrationv1.MutatingWebhookConfigurationList{ - Items: []admissionregistrationv1.MutatingWebhookConfiguration{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "config1", - Labels: testRevisionLabel, - }, - Webhooks: []admissionregistrationv1.MutatingWebhook{ - { - Name: "webhook1", - ClientConfig: admissionregistrationv1.WebhookClientConfig{}, - }, - { - Name: "should not be changed", - ClientConfig: admissionregistrationv1.WebhookClientConfig{}, - }, - }, - }, - }, - }, - testRevision, - "config1", - "webhook1", - caBundle0, - "", - }, - } - for _, tc := range ts { - t.Run(tc.name, func(t *testing.T) { - client := kube.NewFakeClient() - for _, wh := range tc.configs.Items { - if _, err := client.AdmissionregistrationV1(). - MutatingWebhookConfigurations().Create(context.Background(), wh.DeepCopy(), metav1.CreateOptions{}); err != nil { - t.Fatal(err) - } - } - - watcher := keycertbundle.NewWatcher() - watcher.SetAndNotify(nil, nil, tc.pemData) - whPatcher, err := NewWebhookCertPatcher(client, tc.revision, tc.webhookName, watcher) - if err != nil { - t.Fatal(err) - } - - stop := make(chan struct{}) - t.Cleanup(func() { - close(stop) - }) - go whPatcher.informer.Run(stop) - client.RunAndWait(stop) - retry.UntilOrFail(t, whPatcher.informer.HasSynced) - - err = whPatcher.patchMutatingWebhookConfig( - client.AdmissionregistrationV1().MutatingWebhookConfigurations(), - tc.configName) - if (err != nil) != (tc.err != "") { - t.Fatalf("Wrong error: got %v want %v", err, tc.err) - } - if err != nil { - if !strings.Contains(err.Error(), tc.err) { - t.Fatalf("Got %q, want %q", err, tc.err) - } - } else { - obj, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().Get(context.Background(), tc.configName, metav1.GetOptions{}) - if err != nil { - t.Fatal(err) - } - for _, w := range obj.Webhooks { - if strings.HasSuffix(w.Name, tc.webhookName) { - if !bytes.Equal(w.ClientConfig.CABundle, tc.pemData) { - t.Fatalf("Incorrect CA bundle: expect %s got %s", tc.pemData, w.ClientConfig.CABundle) - } - } - if !strings.HasSuffix(w.Name, tc.webhookName) { - if bytes.Equal(w.ClientConfig.CABundle, tc.pemData) { - t.Fatalf("Non-matching webhook \"%s\" CA bundle updated to %v", w.Name, w.ClientConfig.CABundle) - } - } - } - } - }) - } -} diff --git a/samples/README.md b/samples/README.md deleted file mode 100644 index a1a140f8c..000000000 --- a/samples/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Istio Samples - -This directory contains sample applications highlighting various Istio features. diff --git a/samples/addons/README.md b/samples/addons/README.md deleted file mode 100644 index 9f8f32e67..000000000 --- a/samples/addons/README.md +++ /dev/null @@ -1,92 +0,0 @@ -# Telemetry Addons - -This directory contains sample deployments of various addons that integrate with Istio. While these applications -are not a part of Istio, they are essential to making the most of Istio's observability features. - -The deployments here are meant to quickly get up and running, and are optimized for this case. As a result, -they may not be suitable for production. See below for more info on integrating a production grade version of each -addon. - -## Getting started - -To quickly deploy all addons: - -```shell script -kubectl apply -f samples/addons -``` - -Alternatively, you can deploy individual addons: - -```shell script -kubectl apply -f samples/addons/prometheus.yaml -``` - -## Addons - -### Prometheus - -[Prometheus](https://prometheus.io/) is an open source monitoring system and time series database. -You can use Prometheus with Istio to record metrics that track the health of Istio and of applications within the service mesh. -You can visualize metrics using tools like [Grafana](#grafana) and [Kiali](#kiali). - -For more information about integrating with Prometheus, please see the [Prometheus integration page](https://istio.io/docs/ops/integrations/prometheus/). - -### Grafana - -[Grafana](http://grafana.com/) is an open source monitoring solution that can be used to configure dashboards for Istio. -You can use Grafana to monitor the health of Istio and of applications within the service mesh. - -This sample provides the following dashboards: - -* [Mesh Dashboard](https://grafana.com/grafana/dashboards/7639) provides an overview of all services in the mesh. -* [Service Dashboard](https://grafana.com/grafana/dashboards/7636) provides a detailed breakdown of metrics for a service. -* [Workload Dashboard](https://grafana.com/grafana/dashboards/7630) provides a detailed breakdown of metrics for a workload. -* [Performance Dashboard](https://grafana.com/grafana/dashboards/11829) monitors the resource usage of the mesh. -* [Control Plane Dashboard](https://grafana.com/grafana/dashboards/7645) monitors the health and performance of the control plane. -* [WASM Extension Dashboard](https://grafana.com/grafana/dashboards/13277) provides an overview of mesh wide WebAssembly extension runtime and loading state. - -For more information about integrating with Grafana, please see the [Grafana integration page](https://istio.io/docs/ops/integrations/grafana/). - -### Kiali - -[Kiali](https://kiali.io/) is an observability console for Istio with service mesh configuration capabilities. -It helps you to understand the structure of your service mesh by inferring the topology, and also provides the health of your mesh. -Kiali provides detailed metrics, and a basic [Grafana](#grafana) integration is available for advanced queries. -Distributed tracing is provided by integrating [Jaeger](#jaeger). - -For more information about using Kiali, see the [Visualizing Your Mesh](https://istio.io/docs/tasks/observability/kiali/) task. - -### Jaeger - -[Jaeger](https://www.jaegertracing.io/) is an open source end to end distributed tracing system, allowing users to monitor and troubleshoot transactions in complex distributed systems. - -Jaeger helps in a variety of tasks including: - -* Distributed context propagation -* Distributed transaction monitoring -* Root cause analysis -* Service dependency analysis -* Performance / latency optimization - -For more information about integrating with Jaeger, please see the [Jaeger integration page](https://istio.io/docs/tasks/observability/distributed-tracing/jaeger/). - -### Zipkin - -[Zipkin](https://zipkin.io/) is a distributed tracing system. It helps gather timing data needed to troubleshoot latency problems in service architectures. Features include both the collection and lookup of this data. - -Zipkin is an alternative to Jaeger and is not deployed by default. To replace Jaeger with Zipkin, run `kubectl apply -f samples/addons/extras/zipkin.yaml`. -You may also want to remove the Jaeger deployment, which will not be used, with `kubectl delete deployment jaeger`, or avoid installing it -to begin with by following the selective install steps in [Getting Started](#getting-started). - -For more information about integrating with Zipkin, please see the [Zipkin integration page](https://istio.io/docs/tasks/observability/distributed-tracing/zipkin/). - -### Prometheus Operator - -The [Prometheus Operator](https://github.com/coreos/prometheus-operator) manages and operators a Prometheus instance. - -As an alternative to the standard Prometheus deployment, we provide a `ServiceMonitor` to monitor the Istio control plane and `PodMonitor` -Envoy proxies. To use these, make sure you have the Prometheus operator deployed, then run `kubectl apply -f samples/addons/extras/prometheus-operator.yaml`. - -Note: The example `PodMonitor` requires [metrics merging](https://istio.io/latest/docs/ops/integrations/prometheus/#option-1-metrics-merging) to be enabled. This is enabled by default. - -Note: The configurations here are only for Istio deployments, and do not scrape metrics from the Kubernetes components. See the [Cluster Monitoring](https://coreos.com/operators/prometheus/docs/latest/user-guides/cluster-monitoring.html) documentation for configuring this. diff --git a/samples/addons/extras/prometheus-operator.yaml b/samples/addons/extras/prometheus-operator.yaml deleted file mode 100644 index 545270b33..000000000 --- a/samples/addons/extras/prometheus-operator.yaml +++ /dev/null @@ -1,57 +0,0 @@ -apiVersion: monitoring.coreos.com/v1 -kind: PodMonitor -metadata: - name: envoy-stats-monitor - namespace: dubbo-system - labels: - monitoring: istio-proxies - release: istio -spec: - selector: - matchExpressions: - - {key: istio-prometheus-ignore, operator: DoesNotExist} - namespaceSelector: - any: true - jobLabel: envoy-stats - podMetricsEndpoints: - - path: /stats/prometheus - interval: 15s - relabelings: - - action: keep - sourceLabels: [__meta_kubernetes_pod_container_name] - regex: "istio-proxy" - - action: keep - sourceLabels: [__meta_kubernetes_pod_annotationpresent_prometheus_io_scrape] - - sourceLabels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port] - action: replace - regex: ([^:]+)(?::\d+)?;(\d+) - replacement: $1:$2 - targetLabel: __address__ - - action: labeldrop - regex: "__meta_kubernetes_pod_label_(.+)" - - sourceLabels: [__meta_kubernetes_namespace] - action: replace - targetLabel: namespace - - sourceLabels: [__meta_kubernetes_pod_name] - action: replace - targetLabel: pod_name ---- -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - name: istio-component-monitor - namespace: dubbo-system - labels: - monitoring: istio-components - release: istio -spec: - jobLabel: istio - targetLabels: [app] - selector: - matchExpressions: - - {key: istio, operator: In, values: [pilot]} - namespaceSelector: - any: true - endpoints: - - port: http-monitoring - interval: 15s diff --git a/samples/addons/extras/prometheus_vm.yaml b/samples/addons/extras/prometheus_vm.yaml deleted file mode 100644 index 66329d709..000000000 --- a/samples/addons/extras/prometheus_vm.yaml +++ /dev/null @@ -1,531 +0,0 @@ ---- -# Source: prometheus/templates/server/serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - component: "server" - app: prometheus - release: prometheus - chart: prometheus-13.6.0 - heritage: Helm - name: prometheus - namespace: dubbo-system - annotations: - {} ---- -# Source: prometheus/templates/server/cm.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - labels: - component: "server" - app: prometheus - release: prometheus - chart: prometheus-13.6.0 - heritage: Helm - name: prometheus - namespace: dubbo-system -data: - alerting_rules.yml: | - {} - alerts: | - {} - prometheus.yml: | - global: - evaluation_interval: 1m - scrape_interval: 15s - scrape_timeout: 10s - rule_files: - - /etc/config/recording_rules.yml - - /etc/config/alerting_rules.yml - - /etc/config/rules - - /etc/config/alerts - scrape_configs: - - job_name: prometheus - static_configs: - - targets: - - localhost:9090 - - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - job_name: kubernetes-apiservers - kubernetes_sd_configs: - - role: endpoints - relabel_configs: - - action: keep - regex: default;kubernetes;https - source_labels: - - __meta_kubernetes_namespace - - __meta_kubernetes_service_name - - __meta_kubernetes_endpoint_port_name - scheme: https - tls_config: - ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - insecure_skip_verify: true - - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - job_name: kubernetes-nodes - kubernetes_sd_configs: - - role: node - relabel_configs: - - action: labelmap - regex: __meta_kubernetes_node_label_(.+) - - replacement: kubernetes.default.svc:443 - target_label: __address__ - - regex: (.+) - replacement: /api/v1/nodes/$1/proxy/metrics - source_labels: - - __meta_kubernetes_node_name - target_label: __metrics_path__ - scheme: https - tls_config: - ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - insecure_skip_verify: true - - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - job_name: kubernetes-nodes-cadvisor - kubernetes_sd_configs: - - role: node - relabel_configs: - - action: labelmap - regex: __meta_kubernetes_node_label_(.+) - - replacement: kubernetes.default.svc:443 - target_label: __address__ - - regex: (.+) - replacement: /api/v1/nodes/$1/proxy/metrics/cadvisor - source_labels: - - __meta_kubernetes_node_name - target_label: __metrics_path__ - scheme: https - tls_config: - ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - insecure_skip_verify: true - - job_name: kubernetes-service-endpoints - kubernetes_sd_configs: - - role: endpoints - relabel_configs: - - action: keep - regex: true - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_scrape - - action: replace - regex: (https?) - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_scheme - target_label: __scheme__ - - action: replace - regex: (.+) - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_path - target_label: __metrics_path__ - - action: replace - regex: ([^:]+)(?::\d+)?;(\d+) - replacement: $1:$2 - source_labels: - - __address__ - - __meta_kubernetes_service_annotation_prometheus_io_port - target_label: __address__ - - action: labelmap - regex: __meta_kubernetes_service_label_(.+) - - action: replace - source_labels: - - __meta_kubernetes_namespace - target_label: kubernetes_namespace - - action: replace - source_labels: - - __meta_kubernetes_service_name - target_label: kubernetes_name - - action: replace - source_labels: - - __meta_kubernetes_pod_node_name - target_label: kubernetes_node - - job_name: kubernetes-service-endpoints-slow - kubernetes_sd_configs: - - role: endpoints - relabel_configs: - - action: keep - regex: true - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_scrape_slow - - action: replace - regex: (https?) - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_scheme - target_label: __scheme__ - - action: replace - regex: (.+) - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_path - target_label: __metrics_path__ - - action: replace - regex: ([^:]+)(?::\d+)?;(\d+) - replacement: $1:$2 - source_labels: - - __address__ - - __meta_kubernetes_service_annotation_prometheus_io_port - target_label: __address__ - - action: labelmap - regex: __meta_kubernetes_service_label_(.+) - - action: replace - source_labels: - - __meta_kubernetes_namespace - target_label: kubernetes_namespace - - action: replace - source_labels: - - __meta_kubernetes_service_name - target_label: kubernetes_name - - action: replace - source_labels: - - __meta_kubernetes_pod_node_name - target_label: kubernetes_node - scrape_interval: 5m - scrape_timeout: 30s - - honor_labels: true - job_name: prometheus-pushgateway - kubernetes_sd_configs: - - role: service - relabel_configs: - - action: keep - regex: pushgateway - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_probe - - job_name: kubernetes-services - kubernetes_sd_configs: - - role: service - metrics_path: /probe - params: - module: - - http_2xx - relabel_configs: - - action: keep - regex: true - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_probe - - source_labels: - - __address__ - target_label: __param_target - - replacement: blackbox - target_label: __address__ - - source_labels: - - __param_target - target_label: instance - - action: labelmap - regex: __meta_kubernetes_service_label_(.+) - - source_labels: - - __meta_kubernetes_namespace - target_label: kubernetes_namespace - - source_labels: - - __meta_kubernetes_service_name - target_label: kubernetes_name - - job_name: kubernetes-pods - kubernetes_sd_configs: - - role: pod - relabel_configs: - - action: keep - regex: true - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_scrape - - action: replace - regex: (https?) - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_scheme - target_label: __scheme__ - - action: replace - regex: (.+) - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_path - target_label: __metrics_path__ - - action: replace - regex: ([^:]+)(?::\d+)?;(\d+) - replacement: $1:$2 - source_labels: - - __address__ - - __meta_kubernetes_pod_annotation_prometheus_io_port - target_label: __address__ - - action: labelmap - regex: __meta_kubernetes_pod_label_(.+) - - action: replace - source_labels: - - __meta_kubernetes_namespace - target_label: kubernetes_namespace - - action: replace - source_labels: - - __meta_kubernetes_pod_name - target_label: kubernetes_pod_name - - action: drop - regex: Pending|Succeeded|Failed - source_labels: - - __meta_kubernetes_pod_phase - - job_name: kubernetes-pods-slow - kubernetes_sd_configs: - - role: pod - relabel_configs: - - action: keep - regex: true - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_scrape_slow - - action: replace - regex: (https?) - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_scheme - target_label: __scheme__ - - action: replace - regex: (.+) - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_path - target_label: __metrics_path__ - - action: replace - regex: ([^:]+)(?::\d+)?;(\d+) - replacement: $1:$2 - source_labels: - - __address__ - - __meta_kubernetes_pod_annotation_prometheus_io_port - target_label: __address__ - - action: labelmap - regex: __meta_kubernetes_pod_label_(.+) - - action: replace - source_labels: - - __meta_kubernetes_namespace - target_label: kubernetes_namespace - - action: replace - source_labels: - - __meta_kubernetes_pod_name - target_label: kubernetes_pod_name - - action: drop - regex: Pending|Succeeded|Failed - source_labels: - - __meta_kubernetes_pod_phase - scrape_interval: 5m - scrape_timeout: 30s - - job_name: kubernetes-file-sd-endpoints - kubernetes_sd_configs: - - role: endpoints - file_sd_configs: - - files: - - /etc/file_sd/*.json - relabel_configs: - - action: keep - regex: (.+) - source_labels: - - __meta_filepath - - replacement: /stats/prometheus - target_label: __metrics_path__ - - recording_rules.yml: | - {} - rules: | - {} ---- -# Source: prometheus/templates/server/clusterrole.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - component: "server" - app: prometheus - release: prometheus - chart: prometheus-13.6.0 - heritage: Helm - name: prometheus -rules: - - apiGroups: - - "" - resources: - - nodes - - nodes/proxy - - nodes/metrics - - services - - endpoints - - pods - - ingresses - verbs: - - get - - list - - watch - - apiGroups: - - "extensions" - - "networking.k8s.io" - resources: - - ingresses/status - - ingresses - verbs: - - get - - list - - watch - - nonResourceURLs: - - "/metrics" - verbs: - - get - - apiGroups: - - "networking.istio.io" - verbs: - - get - - watch - - list - resources: - - workloadentries - - apiGroups: - - "" - verbs: - - get - - watch - - list - - create - - update - - patch - - delete - resources: - - configmaps ---- -# Source: prometheus/templates/server/clusterrolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - component: "server" - app: prometheus - release: prometheus - chart: prometheus-13.6.0 - heritage: Helm - name: prometheus -subjects: - - kind: ServiceAccount - name: prometheus - namespace: dubbo-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: prometheus ---- -# Source: prometheus/templates/server/service.yaml -apiVersion: v1 -kind: Service -metadata: - labels: - component: "server" - app: prometheus - release: prometheus - chart: prometheus-13.6.0 - heritage: Helm - name: prometheus - namespace: dubbo-system -spec: - ports: - - name: http - port: 9090 - protocol: TCP - targetPort: 9090 - selector: - component: "server" - app: prometheus - release: prometheus - sessionAffinity: None - type: "ClusterIP" ---- -# Source: prometheus/templates/server/deploy.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - component: "server" - app: prometheus - release: prometheus - chart: prometheus-13.6.0 - heritage: Helm - name: prometheus - namespace: dubbo-system -spec: - selector: - matchLabels: - component: "server" - app: prometheus - release: prometheus - replicas: 1 - template: - metadata: - annotations: - sidecar.istio.io/inject: "false" - labels: - component: "server" - app: prometheus - release: prometheus - chart: prometheus-13.6.0 - heritage: Helm - spec: - serviceAccountName: prometheus - containers: - - name: prometheus-server-configmap-reload - image: "jimmidyson/configmap-reload:v0.5.0" - imagePullPolicy: "IfNotPresent" - args: - - --volume-dir=/etc/config - - --webhook-url=http://127.0.0.1:9090/-/reload - resources: - {} - volumeMounts: - - name: config-volume - mountPath: /etc/config - readOnly: true - - name: file-sd-volume - mountPath: /etc/file_sd - readOnly: true - - - name: prometheus-server - image: "prom/prometheus:v2.24.0" - imagePullPolicy: "IfNotPresent" - args: - - --storage.tsdb.retention.time=15d - - --config.file=/etc/config/prometheus.yml - - --storage.tsdb.path=/data - - --web.console.libraries=/etc/prometheus/console_libraries - - --web.console.templates=/etc/prometheus/consoles - - --web.enable-lifecycle - ports: - - containerPort: 9090 - readinessProbe: - httpGet: - path: /-/ready - port: 9090 - initialDelaySeconds: 0 - periodSeconds: 5 - timeoutSeconds: 4 - failureThreshold: 3 - successThreshold: 1 - livenessProbe: - httpGet: - path: /-/healthy - port: 9090 - initialDelaySeconds: 30 - periodSeconds: 15 - timeoutSeconds: 10 - failureThreshold: 3 - successThreshold: 1 - resources: - {} - volumeMounts: - - name: config-volume - mountPath: /etc/config - - name: storage-volume - mountPath: /data - subPath: "" - - name: file-sd-volume - mountPath: /etc/file_sd - - name: vm-discovery - image: "istioecosystem/vm-discovery:latest" - imagePullPolicy: "IfNotPresent" - hostNetwork: false - dnsPolicy: ClusterFirst - securityContext: - fsGroup: 65534 - runAsGroup: 65534 - runAsNonRoot: true - runAsUser: 65534 - terminationGracePeriodSeconds: 300 - volumes: - - name: config-volume - configMap: - name: prometheus - - name: file-sd-volume - configMap: - name: file-sd-config - optional: true - - name: storage-volume - emptyDir: - {} \ No newline at end of file diff --git a/samples/addons/extras/prometheus_vm_tls.yaml b/samples/addons/extras/prometheus_vm_tls.yaml deleted file mode 100644 index 2b66db2a5..000000000 --- a/samples/addons/extras/prometheus_vm_tls.yaml +++ /dev/null @@ -1,548 +0,0 @@ ---- -# Source: prometheus/templates/server/serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - component: "server" - app: prometheus - release: prometheus - chart: prometheus-13.6.0 - heritage: Helm - name: prometheus - namespace: dubbo-system - annotations: - {} ---- -# Source: prometheus/templates/server/cm.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - labels: - component: "server" - app: prometheus - release: prometheus - chart: prometheus-13.6.0 - heritage: Helm - name: prometheus - namespace: dubbo-system -data: - alerting_rules.yml: | - {} - alerts: | - {} - prometheus.yml: | - global: - evaluation_interval: 1m - scrape_interval: 15s - scrape_timeout: 10s - rule_files: - - /etc/config/recording_rules.yml - - /etc/config/alerting_rules.yml - - /etc/config/rules - - /etc/config/alerts - scrape_configs: - - job_name: prometheus - static_configs: - - targets: - - localhost:9090 - - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - job_name: kubernetes-apiservers - kubernetes_sd_configs: - - role: endpoints - relabel_configs: - - action: keep - regex: default;kubernetes;https - source_labels: - - __meta_kubernetes_namespace - - __meta_kubernetes_service_name - - __meta_kubernetes_endpoint_port_name - scheme: https - tls_config: - ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - insecure_skip_verify: true - - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - job_name: kubernetes-nodes - kubernetes_sd_configs: - - role: node - relabel_configs: - - action: labelmap - regex: __meta_kubernetes_node_label_(.+) - - replacement: kubernetes.default.svc:443 - target_label: __address__ - - regex: (.+) - replacement: /api/v1/nodes/$1/proxy/metrics - source_labels: - - __meta_kubernetes_node_name - target_label: __metrics_path__ - scheme: https - tls_config: - ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - insecure_skip_verify: true - - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - job_name: kubernetes-nodes-cadvisor - kubernetes_sd_configs: - - role: node - relabel_configs: - - action: labelmap - regex: __meta_kubernetes_node_label_(.+) - - replacement: kubernetes.default.svc:443 - target_label: __address__ - - regex: (.+) - replacement: /api/v1/nodes/$1/proxy/metrics/cadvisor - source_labels: - - __meta_kubernetes_node_name - target_label: __metrics_path__ - scheme: https - tls_config: - ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - insecure_skip_verify: true - - job_name: kubernetes-service-endpoints - kubernetes_sd_configs: - - role: endpoints - relabel_configs: - - action: keep - regex: true - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_scrape - - action: replace - regex: (https?) - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_scheme - target_label: __scheme__ - - action: replace - regex: (.+) - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_path - target_label: __metrics_path__ - - action: replace - regex: ([^:]+)(?::\d+)?;(\d+) - replacement: $1:$2 - source_labels: - - __address__ - - __meta_kubernetes_service_annotation_prometheus_io_port - target_label: __address__ - - action: labelmap - regex: __meta_kubernetes_service_label_(.+) - - action: replace - source_labels: - - __meta_kubernetes_namespace - target_label: kubernetes_namespace - - action: replace - source_labels: - - __meta_kubernetes_service_name - target_label: kubernetes_name - - action: replace - source_labels: - - __meta_kubernetes_pod_node_name - target_label: kubernetes_node - - job_name: kubernetes-service-endpoints-slow - kubernetes_sd_configs: - - role: endpoints - relabel_configs: - - action: keep - regex: true - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_scrape_slow - - action: replace - regex: (https?) - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_scheme - target_label: __scheme__ - - action: replace - regex: (.+) - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_path - target_label: __metrics_path__ - - action: replace - regex: ([^:]+)(?::\d+)?;(\d+) - replacement: $1:$2 - source_labels: - - __address__ - - __meta_kubernetes_service_annotation_prometheus_io_port - target_label: __address__ - - action: labelmap - regex: __meta_kubernetes_service_label_(.+) - - action: replace - source_labels: - - __meta_kubernetes_namespace - target_label: kubernetes_namespace - - action: replace - source_labels: - - __meta_kubernetes_service_name - target_label: kubernetes_name - - action: replace - source_labels: - - __meta_kubernetes_pod_node_name - target_label: kubernetes_node - scrape_interval: 5m - scrape_timeout: 30s - - honor_labels: true - job_name: prometheus-pushgateway - kubernetes_sd_configs: - - role: service - relabel_configs: - - action: keep - regex: pushgateway - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_probe - - job_name: kubernetes-services - kubernetes_sd_configs: - - role: service - metrics_path: /probe - params: - module: - - http_2xx - relabel_configs: - - action: keep - regex: true - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_probe - - source_labels: - - __address__ - target_label: __param_target - - replacement: blackbox - target_label: __address__ - - source_labels: - - __param_target - target_label: instance - - action: labelmap - regex: __meta_kubernetes_service_label_(.+) - - source_labels: - - __meta_kubernetes_namespace - target_label: kubernetes_namespace - - source_labels: - - __meta_kubernetes_service_name - target_label: kubernetes_name - - job_name: kubernetes-pods - kubernetes_sd_configs: - - role: pod - relabel_configs: - - action: keep - regex: true - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_scrape - - action: replace - regex: (https?) - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_scheme - target_label: __scheme__ - - action: replace - regex: (.+) - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_path - target_label: __metrics_path__ - - action: replace - regex: ([^:]+)(?::\d+)?;(\d+) - replacement: $1:$2 - source_labels: - - __address__ - - __meta_kubernetes_pod_annotation_prometheus_io_port - target_label: __address__ - - action: labelmap - regex: __meta_kubernetes_pod_label_(.+) - - action: replace - source_labels: - - __meta_kubernetes_namespace - target_label: kubernetes_namespace - - action: replace - source_labels: - - __meta_kubernetes_pod_name - target_label: kubernetes_pod_name - - action: drop - regex: Pending|Succeeded|Failed - source_labels: - - __meta_kubernetes_pod_phase - - job_name: kubernetes-pods-slow - kubernetes_sd_configs: - - role: pod - relabel_configs: - - action: keep - regex: true - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_scrape_slow - - action: replace - regex: (https?) - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_scheme - target_label: __scheme__ - - action: replace - regex: (.+) - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_path - target_label: __metrics_path__ - - action: replace - regex: ([^:]+)(?::\d+)?;(\d+) - replacement: $1:$2 - source_labels: - - __address__ - - __meta_kubernetes_pod_annotation_prometheus_io_port - target_label: __address__ - - action: labelmap - regex: __meta_kubernetes_pod_label_(.+) - - action: replace - source_labels: - - __meta_kubernetes_namespace - target_label: kubernetes_namespace - - action: replace - source_labels: - - __meta_kubernetes_pod_name - target_label: kubernetes_pod_name - - action: drop - regex: Pending|Succeeded|Failed - source_labels: - - __meta_kubernetes_pod_phase - scrape_interval: 5m - scrape_timeout: 30s - - job_name: kubernetes-file-sd-endpoints - kubernetes_sd_configs: - - role: endpoints - file_sd_configs: - - files: - - /etc/file_sd/*.json - scheme: https - tls_config: - ca_file: /etc/prom-certs/root-cert.pem - cert_file: /etc/prom-certs/cert-chain.pem - key_file: /etc/prom-certs/key.pem - insecure_skip_verify: true # Prometheus does not support Istio security naming, thus skip verifying target pod ceritifcate - relabel_configs: - - action: keep - regex: (.+) - source_labels: - - __meta_filepath - - replacement: /stats/prometheus - target_label: __metrics_path__ - - recording_rules.yml: | - {} - rules: | - {} ---- -# Source: prometheus/templates/server/clusterrole.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - component: "server" - app: prometheus - release: prometheus - chart: prometheus-13.6.0 - heritage: Helm - name: prometheus -rules: - - apiGroups: - - "" - resources: - - nodes - - nodes/proxy - - nodes/metrics - - services - - endpoints - - pods - - ingresses - verbs: - - get - - list - - watch - - apiGroups: - - "extensions" - - "networking.k8s.io" - resources: - - ingresses/status - - ingresses - verbs: - - get - - list - - watch - - nonResourceURLs: - - "/metrics" - verbs: - - get - - apiGroups: - - "networking.istio.io" - verbs: - - get - - watch - - list - resources: - - workloadentries - - apiGroups: - - "" - verbs: - - get - - watch - - list - - create - - update - - patch - - delete - resources: - - configmaps ---- -# Source: prometheus/templates/server/clusterrolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - component: "server" - app: prometheus - release: prometheus - chart: prometheus-13.6.0 - heritage: Helm - name: prometheus -subjects: - - kind: ServiceAccount - name: prometheus - namespace: dubbo-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: prometheus ---- -# Source: prometheus/templates/server/service.yaml -apiVersion: v1 -kind: Service -metadata: - labels: - component: "server" - app: prometheus - release: prometheus - chart: prometheus-13.6.0 - heritage: Helm - name: prometheus - namespace: dubbo-system -spec: - ports: - - name: http - port: 9090 - protocol: TCP - targetPort: 9090 - selector: - component: "server" - app: prometheus - release: prometheus - sessionAffinity: None - type: "ClusterIP" ---- -# Source: prometheus/templates/server/deploy.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - component: "server" - app: prometheus - release: prometheus - chart: prometheus-13.6.0 - heritage: Helm - name: prometheus - namespace: dubbo-system -spec: - selector: - matchLabels: - component: "server" - app: prometheus - release: prometheus - replicas: 1 - template: - metadata: - annotations: - sidecar.istio.io/inject: "true" - traffic.sidecar.istio.io/includeInboundPorts: "" # do not intercept any inbound ports - traffic.sidecar.istio.io/includeOutboundIPRanges: "" # do not intercept any outbound traffic - proxy.istio.io/config: | # configure an env variable `OUTPUT_CERTS` to write certificates to the given folder - proxyMetadata: - OUTPUT_CERTS: /etc/istio-output-certs - sidecar.istio.io/userVolumeMount: '[{"name": "istio-certs", "mountPath": "/etc/istio-output-certs"}]' # mount the shared volume at sidecar proxy - labels: - component: "server" - app: prometheus - release: prometheus - chart: prometheus-13.6.0 - heritage: Helm - spec: - serviceAccountName: prometheus - containers: - - name: prometheus-server-configmap-reload - image: "jimmidyson/configmap-reload:v0.5.0" - imagePullPolicy: "IfNotPresent" - args: - - --volume-dir=/etc/config - - --webhook-url=http://127.0.0.1:9090/-/reload - resources: - {} - volumeMounts: - - name: config-volume - mountPath: /etc/config - readOnly: true - - name: file-sd-volume - mountPath: /etc/file_sd - readOnly: true - - - name: prometheus-server - image: "prom/prometheus:v2.24.0" - imagePullPolicy: "IfNotPresent" - args: - - --storage.tsdb.retention.time=15d - - --config.file=/etc/config/prometheus.yml - - --storage.tsdb.path=/data - - --web.console.libraries=/etc/prometheus/console_libraries - - --web.console.templates=/etc/prometheus/consoles - - --web.enable-lifecycle - ports: - - containerPort: 9090 - readinessProbe: - httpGet: - path: /-/ready - port: 9090 - initialDelaySeconds: 0 - periodSeconds: 5 - timeoutSeconds: 4 - failureThreshold: 3 - successThreshold: 1 - livenessProbe: - httpGet: - path: /-/healthy - port: 9090 - initialDelaySeconds: 30 - periodSeconds: 15 - timeoutSeconds: 10 - failureThreshold: 3 - successThreshold: 1 - resources: - {} - volumeMounts: - - name: config-volume - mountPath: /etc/config - - name: storage-volume - mountPath: /data - subPath: "" - - name: file-sd-volume - mountPath: /etc/file_sd - - name: istio-certs - mountPath: /etc/prom-certs/ - - name: vm-discovery - image: "istioecosystem/vm-discovery:latest" - imagePullPolicy: "IfNotPresent" - hostNetwork: false - dnsPolicy: ClusterFirst - securityContext: - fsGroup: 65534 - runAsGroup: 65534 - runAsNonRoot: true - runAsUser: 65534 - terminationGracePeriodSeconds: 300 - volumes: - - name: config-volume - configMap: - name: prometheus - - name: file-sd-volume - configMap: - name: file-sd-config - optional: true - - name: istio-certs - emptyDir: - medium: Memory - - name: storage-volume - emptyDir: - {} \ No newline at end of file diff --git a/samples/addons/extras/zipkin.yaml b/samples/addons/extras/zipkin.yaml deleted file mode 100644 index 0f0fca912..000000000 --- a/samples/addons/extras/zipkin.yaml +++ /dev/null @@ -1,62 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: zipkin - namespace: dubbo-system - labels: - app: zipkin -spec: - selector: - matchLabels: - app: zipkin - template: - metadata: - labels: - app: zipkin - annotations: - sidecar.istio.io/inject: "false" - spec: - containers: - - name: zipkin - image: openzipkin/zipkin-slim:2.23.14 - env: - - name: STORAGE_METHOD - value: "mem" - readinessProbe: - httpGet: - path: /health - port: 9411 - initialDelaySeconds: 5 - periodSeconds: 5 ---- -apiVersion: v1 -kind: Service -metadata: - name: tracing - namespace: dubbo-system - labels: - app: zipkin -spec: - type: ClusterIP - ports: - - name: http-query - port: 80 - protocol: TCP - targetPort: 9411 - selector: - app: zipkin ---- -apiVersion: v1 -kind: Service -metadata: - labels: - name: zipkin - name: zipkin - namespace: dubbo-system -spec: - ports: - - port: 9411 - targetPort: 9411 - name: http-query - selector: - app: zipkin diff --git a/samples/addons/grafana.yaml b/samples/addons/grafana.yaml deleted file mode 100644 index 6d37f2945..000000000 --- a/samples/addons/grafana.yaml +++ /dev/null @@ -1,1112 +0,0 @@ ---- -# Source: grafana/templates/serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - helm.sh/chart: grafana-6.18.2 - app.kubernetes.io/name: grafana - app.kubernetes.io/instance: grafana - app.kubernetes.io/version: "8.3.1" - app.kubernetes.io/managed-by: Helm - name: grafana - namespace: dubbo-system ---- -# Source: grafana/templates/configmap.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: grafana - namespace: dubbo-system - labels: - helm.sh/chart: grafana-6.18.2 - app.kubernetes.io/name: grafana - app.kubernetes.io/instance: grafana - app.kubernetes.io/version: "8.3.1" - app.kubernetes.io/managed-by: Helm -data: - grafana.ini: | - [analytics] - check_for_updates = true - [grafana_net] - url = https://grafana.net - [log] - mode = console - [paths] - data = /var/lib/grafana/ - logs = /var/log/grafana - plugins = /var/lib/grafana/plugins - provisioning = /etc/grafana/provisioning - - datasources.yaml: | - apiVersion: 1 - datasources: - - access: proxy - editable: true - isDefault: true - jsonData: - timeInterval: 5s - name: Prometheus - orgId: 1 - type: prometheus - url: http://prometheus:9090 - dashboardproviders.yaml: | - apiVersion: 1 - providers: - - disableDeletion: false - folder: istio - name: istio - options: - path: /var/lib/grafana/dashboards/istio - orgId: 1 - type: file - - disableDeletion: false - folder: istio - name: istio-services - options: - path: /var/lib/grafana/dashboards/istio-services - orgId: 1 - type: file ---- -# Source: grafana/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: grafana - namespace: dubbo-system - labels: - helm.sh/chart: grafana-6.18.2 - app.kubernetes.io/name: grafana - app.kubernetes.io/instance: grafana - app.kubernetes.io/version: "8.3.1" - app.kubernetes.io/managed-by: Helm -spec: - type: ClusterIP - ports: - - name: service - port: 3000 - protocol: TCP - targetPort: 3000 - - selector: - app.kubernetes.io/name: grafana - app.kubernetes.io/instance: grafana ---- -# Source: grafana/templates/deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: grafana - namespace: dubbo-system - labels: - helm.sh/chart: grafana-6.18.2 - app.kubernetes.io/name: grafana - app.kubernetes.io/instance: grafana - app.kubernetes.io/version: "8.3.1" - app.kubernetes.io/managed-by: Helm -spec: - replicas: 1 - revisionHistoryLimit: 10 - selector: - matchLabels: - app.kubernetes.io/name: grafana - app.kubernetes.io/instance: grafana - strategy: - type: RollingUpdate - template: - metadata: - labels: - app.kubernetes.io/name: grafana - app.kubernetes.io/instance: grafana - app: grafana - sidecar.istio.io/inject: "false" - annotations: - checksum/config: b09dc3addb625f8b5744b94f7ab624a3b02fdfd16aad7e4294a5fbf3608cb129 - checksum/dashboards-json-config: 01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b - checksum/sc-dashboard-provider-config: 01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b - spec: - - serviceAccountName: grafana - automountServiceAccountToken: true - securityContext: - fsGroup: 472 - runAsGroup: 472 - runAsUser: 472 - enableServiceLinks: true - containers: - - name: grafana - image: "grafana/grafana:8.3.1" - imagePullPolicy: IfNotPresent - volumeMounts: - - name: config - mountPath: "/etc/grafana/grafana.ini" - subPath: grafana.ini - - name: storage - mountPath: "/var/lib/grafana" - - name: dashboards-istio - mountPath: "/var/lib/grafana/dashboards/istio" - - name: dashboards-istio-services - mountPath: "/var/lib/grafana/dashboards/istio-services" - - name: config - mountPath: "/etc/grafana/provisioning/datasources/datasources.yaml" - subPath: "datasources.yaml" - - name: config - mountPath: "/etc/grafana/provisioning/dashboards/dashboardproviders.yaml" - subPath: "dashboardproviders.yaml" - ports: - - name: service - containerPort: 3000 - protocol: TCP - - name: grafana - containerPort: 3000 - protocol: TCP - env: - - - name: GF_PATHS_DATA - value: /var/lib/grafana/ - - name: GF_PATHS_LOGS - value: /var/log/grafana - - name: GF_PATHS_PLUGINS - value: /var/lib/grafana/plugins - - name: GF_PATHS_PROVISIONING - value: /etc/grafana/provisioning - - name: "GF_AUTH_ANONYMOUS_ENABLED" - value: "true" - - name: "GF_AUTH_ANONYMOUS_ORG_ROLE" - value: "Admin" - - name: "GF_AUTH_BASIC_ENABLED" - value: "false" - - name: "GF_SECURITY_ADMIN_PASSWORD" - value: "-" - - name: "GF_SECURITY_ADMIN_USER" - value: "-" - livenessProbe: - failureThreshold: 10 - httpGet: - path: /api/health - port: 3000 - initialDelaySeconds: 60 - timeoutSeconds: 30 - readinessProbe: - httpGet: - path: /api/health - port: 3000 - resources: - {} - volumes: - - name: config - configMap: - name: grafana - - - name: dashboards-istio - configMap: - name: istio-grafana-dashboards - - name: dashboards-istio-services - configMap: - name: istio-services-grafana-dashboards - - name: storage - emptyDir: {} - ---- - -apiVersion: v1 -data: - istio-performance-dashboard.json: | - {"annotations":{"list":[{"builtIn":1,"datasource":"-- Grafana --","enable":true,"hide":true,"iconColor":"rgba(0, 211, 255, 1)","name":"Annotations & Alerts","type":"dashboard"}]},"editable":false,"gnetId":null,"graphTooltip":0,"links":[],"panels":[{"collapsed":true,"gridPos":{"h":1,"w":24,"x":0,"y":0},"id":21,"panels":[{"content":"The charts on this dashboard are intended to show Istio main components cost in terms of resources utilization under steady load.\n\n- **vCPU / 1k rps:** shows vCPU utilization by the main Istio components normalized by 1000 requests/second. When idle or low traffic, this chart will be blank. The curve for istio-proxy refers to the services sidecars only.\n- **vCPU:** vCPU utilization by Istio components, not normalized.\n- **Memory:** memory footprint for the components. Telemetry and policy are normalized by 1k rps, and no data is shown when there is no traffic. For ingress and istio-proxy, the data is per instance.\n- **Bytes transferred / sec:** shows the number of bytes flowing through each Istio component.\n\n\n","gridPos":{"h":6,"w":24,"x":0,"y":1},"id":19,"links":[],"mode":"markdown","timeFrom":null,"timeShift":null,"title":"Performance Dashboard README","transparent":true,"type":"text"}],"title":"Performance Dashboard Notes","type":"row"},{"collapsed":false,"gridPos":{"h":1,"w":24,"x":0,"y":1},"id":6,"panels":[],"title":"vCPU Usage","type":"row"},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fill":1,"gridPos":{"h":8,"w":12,"x":0,"y":2},"id":4,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":2,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"(sum(irate(container_cpu_usage_seconds_total{pod=~\"istio-ingressgateway-.*\",container=\"istio-proxy\"}[1m])) / (round(sum(irate(istio_requests_total{source_workload=\"istio-ingressgateway\", reporter=\"source\"}[1m])), 0.001)/1000))","format":"time_series","hide":false,"intervalFactor":1,"legendFormat":"istio-ingressgateway","refId":"A"},{"expr":"(sum(irate(container_cpu_usage_seconds_total{namespace!=\"dubbo-system\",container=\"istio-proxy\"}[1m]))/ (round(sum(irate(istio_requests_total[1m])), 0.001)/1000))/ (sum(irate(istio_requests_total{source_workload=\"istio-ingressgateway\"}[1m])) >bool 10)","format":"time_series","intervalFactor":1,"legendFormat":"istio-proxy","refId":"B"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"vCPU / 1k rps","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fill":1,"gridPos":{"h":8,"w":12,"x":12,"y":2},"id":7,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":2,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"sum(rate(container_cpu_usage_seconds_total{pod=~\"istio-ingressgateway-.*\",container=\"istio-proxy\"}[1m]))","format":"time_series","intervalFactor":1,"legendFormat":"istio-ingressgateway","refId":"A"},{"expr":"sum(rate(container_cpu_usage_seconds_total{namespace!=\"dubbo-system\",container=\"istio-proxy\"}[1m]))","format":"time_series","intervalFactor":1,"legendFormat":"istio-proxy","refId":"B"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"vCPU","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}},{"collapsed":false,"gridPos":{"h":1,"w":24,"x":0,"y":10},"id":13,"panels":[],"title":"Memory and Data Rates","type":"row"},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fill":1,"gridPos":{"h":8,"w":12,"x":0,"y":11},"id":902,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":2,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"sum(container_memory_working_set_bytes{pod=~\"istio-ingressgateway-.*\"}) / count(container_memory_working_set_bytes{pod=~\"istio-ingressgateway-.*\",container!=\"POD\"})","format":"time_series","intervalFactor":1,"legendFormat":"per istio-ingressgateway","refId":"A"},{"expr":"sum(container_memory_working_set_bytes{namespace!=\"dubbo-system\",container=\"istio-proxy\"}) / count(container_memory_working_set_bytes{namespace!=\"dubbo-system\",container=\"istio-proxy\"})","format":"time_series","intervalFactor":1,"legendFormat":"per istio proxy","refId":"B"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Memory Usage","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"bytes","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fill":1,"gridPos":{"h":8,"w":12,"x":12,"y":11},"id":11,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":2,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"sum(irate(istio_response_bytes_sum{source_workload=\"istio-ingressgateway\", reporter=\"source\"}[1m]))","format":"time_series","intervalFactor":1,"legendFormat":"istio-ingressgateway","refId":"A"},{"expr":"sum(irate(istio_response_bytes_sum{source_workload_namespace!=\"dubbo-system\", reporter=\"source\"}[1m])) + sum(irate(istio_request_bytes_sum{source_workload_namespace!=\"dubbo-system\", reporter=\"source\"}[1m]))","format":"time_series","intervalFactor":1,"legendFormat":"istio-proxy","refId":"B"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Bytes transferred / sec","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"Bps","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}},{"collapsed":false,"gridPos":{"h":1,"w":24,"x":0,"y":19},"id":17,"panels":[],"title":"Istio Component Versions","type":"row"},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fill":1,"gridPos":{"h":8,"w":24,"x":0,"y":20},"id":15,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":2,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"sum(istio_build) by (component, tag)","format":"time_series","intervalFactor":1,"legendFormat":"{{ component }}: {{ tag }}","refId":"A"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Istio Components by Version","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}},{"collapsed":false,"gridPos":{"h":1,"w":24,"x":0,"y":31},"id":71,"panels":[],"title":"Proxy Resource Usage","type":"row"},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fill":1,"gridPos":{"h":7,"w":6,"x":0,"y":32},"id":72,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"sum(container_memory_working_set_bytes{container=\"istio-proxy\"})","format":"time_series","hide":false,"intervalFactor":2,"legendFormat":"Total (k8s)","refId":"A","step":2}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Memory","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"bytes","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":false}],"yaxis":{"align":false,"alignLevel":null}},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fill":1,"gridPos":{"h":7,"w":6,"x":6,"y":32},"id":73,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"sum(rate(container_cpu_usage_seconds_total{container=\"istio-proxy\"}[1m]))","format":"time_series","hide":false,"intervalFactor":2,"legendFormat":"Total (k8s)","refId":"A","step":2}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"vCPU","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fill":1,"gridPos":{"h":7,"w":6,"x":12,"y":32},"id":702,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"sum(container_fs_usage_bytes{container=\"istio-proxy\"})","format":"time_series","intervalFactor":2,"legendFormat":"Total (k8s)","refId":"A","step":2}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Disk","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"bytes","label":"","logBase":1,"max":null,"min":null,"show":true},{"decimals":null,"format":"none","label":"","logBase":1024,"max":null,"min":null,"show":false}],"yaxis":{"align":false,"alignLevel":null}},{"collapsed":false,"gridPos":{"h":1,"w":24,"x":0,"y":39},"id":69,"panels":[],"title":"Istiod Resource Usage","type":"row"},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fill":1,"gridPos":{"h":7,"w":6,"x":0,"y":40},"id":5,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"process_virtual_memory_bytes{app=\"istiod\"}","format":"time_series","instant":false,"intervalFactor":2,"legendFormat":"Virtual Memory","refId":"I","step":2},{"expr":"process_resident_memory_bytes{app=\"istiod\"}","format":"time_series","intervalFactor":2,"legendFormat":"Resident Memory","refId":"H","step":2},{"expr":"go_memstats_heap_sys_bytes{app=\"istiod\"}","format":"time_series","hide":true,"intervalFactor":2,"legendFormat":"heap sys","refId":"A"},{"expr":"go_memstats_heap_alloc_bytes{app=\"istiod\"}","format":"time_series","hide":true,"intervalFactor":2,"legendFormat":"heap alloc","refId":"D"},{"expr":"go_memstats_alloc_bytes{app=\"istiod\"}","format":"time_series","intervalFactor":2,"legendFormat":"Alloc","refId":"F","step":2},{"expr":"go_memstats_heap_inuse_bytes{app=\"istiod\"}","format":"time_series","hide":false,"intervalFactor":2,"legendFormat":"Heap in-use","refId":"E","step":2},{"expr":"go_memstats_stack_inuse_bytes{app=\"istiod\"}","format":"time_series","intervalFactor":2,"legendFormat":"Stack in-use","refId":"G","step":2},{"expr":"sum(container_memory_working_set_bytes{container=~\"discovery|istio-proxy\", pod=~\"istiod-.*\"})","format":"time_series","hide":false,"intervalFactor":2,"legendFormat":"Total (k8s)","refId":"C","step":2},{"expr":"container_memory_working_set_bytes{container=~\"discovery|istio-proxy\", pod=~\"istiod-.*\"}","format":"time_series","hide":false,"intervalFactor":2,"legendFormat":"{{ container }} (k8s)","refId":"B","step":2}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Memory","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"bytes","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":false}],"yaxis":{"align":false,"alignLevel":null}},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fill":1,"gridPos":{"h":7,"w":6,"x":6,"y":40},"id":602,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"sum(rate(container_cpu_usage_seconds_total{container=~\"discovery|istio-proxy\", pod=~\"istiod-.*\"}[1m]))","format":"time_series","hide":false,"intervalFactor":2,"legendFormat":"Total (k8s)","refId":"A","step":2},{"expr":"sum(rate(container_cpu_usage_seconds_total{container=~\"discovery|istio-proxy\", pod=~\"istiod-.*\"}[1m])) by (container)","format":"time_series","hide":false,"intervalFactor":2,"legendFormat":"{{ container }} (k8s)","refId":"B","step":2},{"expr":"irate(process_cpu_seconds_total{app=\"istiod\"}[1m])","format":"time_series","hide":false,"intervalFactor":2,"legendFormat":"pilot (self-reported)","refId":"C","step":2}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"vCPU","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fill":1,"gridPos":{"h":7,"w":6,"x":12,"y":40},"id":74,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"process_open_fds{app=\"istiod\"}","format":"time_series","hide":true,"instant":false,"interval":"","intervalFactor":2,"legendFormat":"Open FDs (pilot)","refId":"A"},{"expr":"container_fs_usage_bytes{ container=~\"discovery|istio-proxy\", pod=~\"istiod-.*\"}","format":"time_series","intervalFactor":2,"legendFormat":"{{ container }}","refId":"B","step":2}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Disk","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"bytes","label":"","logBase":1,"max":null,"min":null,"show":true},{"decimals":null,"format":"none","label":"","logBase":1024,"max":null,"min":null,"show":false}],"yaxis":{"align":false,"alignLevel":null}},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fill":1,"gridPos":{"h":7,"w":6,"x":18,"y":40},"id":402,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":false,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"go_goroutines{app=\"istiod\"}","format":"time_series","intervalFactor":2,"legendFormat":"Number of Goroutines","refId":"A","step":2}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Goroutines","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"short","label":"","logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}}],"refresh":"10s","schemaVersion":18,"style":"dark","tags":[],"templating":{"list":[]},"time":{"from":"now-5m","to":"now"},"timepicker":{"refresh_intervals":["5s","10s","30s","1m","5m","15m","30m","1h","2h","1d"],"time_options":["5m","15m","1h","6h","12h","24h","2d","7d","30d"]},"timezone":"","title":"Istio Performance Dashboard","uid":"vu8e0VWZk","version":22} - pilot-dashboard.json: | - {"annotations":{"list":[{"builtIn":1,"datasource":"-- Grafana --","enable":true,"hide":true,"iconColor":"rgba(0, 211, 255, 1)","name":"Annotations & Alerts","type":"dashboard"}]},"editable":false,"gnetId":null,"graphTooltip":1,"links":[],"panels":[{"collapsed":false,"gridPos":{"h":1,"w":24,"x":0,"y":0},"id":60,"panels":[],"title":"Deployed Versions","type":"row"},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fill":1,"gridPos":{"h":5,"w":24,"x":0,"y":1},"id":56,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"sum(istio_build{component=\"pilot\"}) by (tag)","format":"time_series","intervalFactor":1,"legendFormat":"{{ tag }}","refId":"A"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Pilot Versions","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":false}],"yaxis":{"align":false,"alignLevel":null}},{"collapsed":false,"gridPos":{"h":1,"w":24,"x":0,"y":6},"id":62,"panels":[],"title":"Resource Usage","type":"row"},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fill":1,"gridPos":{"h":7,"w":6,"x":0,"y":7},"id":5,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"process_virtual_memory_bytes{app=\"istiod\"}","format":"time_series","instant":false,"intervalFactor":2,"legendFormat":"Virtual Memory","refId":"I","step":2},{"expr":"process_resident_memory_bytes{app=\"istiod\"}","format":"time_series","intervalFactor":2,"legendFormat":"Resident Memory","refId":"H","step":2},{"expr":"go_memstats_heap_sys_bytes{app=\"istiod\"}","format":"time_series","hide":true,"intervalFactor":2,"legendFormat":"heap sys","refId":"A"},{"expr":"go_memstats_heap_alloc_bytes{app=\"istiod\"}","format":"time_series","hide":true,"intervalFactor":2,"legendFormat":"heap alloc","refId":"D"},{"expr":"go_memstats_alloc_bytes{app=\"istiod\"}","format":"time_series","intervalFactor":2,"legendFormat":"Alloc","refId":"F","step":2},{"expr":"go_memstats_heap_inuse_bytes{app=\"istiod\"}","format":"time_series","hide":false,"intervalFactor":2,"legendFormat":"Heap in-use","refId":"E","step":2},{"expr":"go_memstats_stack_inuse_bytes{app=\"istiod\"}","format":"time_series","intervalFactor":2,"legendFormat":"Stack in-use","refId":"G","step":2},{"expr":"container_memory_working_set_bytes{container=~\"discovery\", pod=~\"istiod-.*|istio-pilot-.*\"}","format":"time_series","hide":false,"intervalFactor":2,"legendFormat":"Discovery (container)","refId":"B","step":2},{"expr":"container_memory_working_set_bytes{container=~\"istio-proxy\", pod=~\"istiod-.*|istio-pilot-.*\"}","format":"time_series","intervalFactor":1,"legendFormat":"Sidecar (container)","refId":"C"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Memory","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"bytes","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":false}],"yaxis":{"align":false,"alignLevel":null}},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fill":1,"gridPos":{"h":7,"w":6,"x":6,"y":7},"id":6,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"sum(irate(container_cpu_usage_seconds_total{container=\"discovery\", pod=~\"istiod-.*|istio-pilot-.*\"}[1m]))","format":"time_series","intervalFactor":1,"legendFormat":"Discovery (container)","refId":"A"},{"expr":"irate(process_cpu_seconds_total{app=\"istiod\"}[1m])","format":"time_series","hide":false,"intervalFactor":2,"legendFormat":"Discovery (process)","refId":"C","step":2},{"expr":"sum(irate(container_cpu_usage_seconds_total{container=\"istio-proxy\", pod=~\"istiod-.*|istio-pilot-.*\"}[1m]))","format":"time_series","hide":false,"intervalFactor":2,"legendFormat":"Sidecar (container)","refId":"B","step":2}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"CPU","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fill":1,"gridPos":{"h":7,"w":6,"x":12,"y":7},"id":7,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"container_fs_usage_bytes{container=\"discovery\", pod=~\"istiod-.*|istio-pilot-.*\"}","format":"time_series","intervalFactor":2,"legendFormat":"Discovery","refId":"B","step":2},{"expr":"container_fs_usage_bytes{container=\"istio-proxy\", pod=~\"istiod-.*|istio-pilot-.*\"}","format":"time_series","intervalFactor":1,"legendFormat":"Sidecar","refId":"A"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Disk","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"bytes","label":"","logBase":1,"max":null,"min":null,"show":true},{"decimals":null,"format":"none","label":"","logBase":1024,"max":null,"min":null,"show":false}],"yaxis":{"align":false,"alignLevel":null}},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fill":1,"gridPos":{"h":7,"w":6,"x":18,"y":7},"id":4,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":false,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"go_goroutines{app=\"istiod\"}","format":"time_series","intervalFactor":2,"legendFormat":"Number of Goroutines","refId":"A","step":2}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Goroutines","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"short","label":"","logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}},{"collapsed":false,"gridPos":{"h":1,"w":24,"x":0,"y":14},"id":58,"panels":[],"title":"Pilot Push Information","type":"row"},{"aliasColors":{},"bars":true,"dashLength":10,"dashes":false,"datasource":"Prometheus","description":"Shows the rate of pilot pushes","fill":1,"gridPos":{"h":8,"w":8,"x":0,"y":15},"id":622,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":false,"linewidth":1,"links":[],"nullPointMode":"null as zero","paceLength":10,"percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":true,"steppedLine":false,"targets":[{"expr":"sum(irate(pilot_xds_pushes{type=\"cds\"}[1m]))","format":"time_series","intervalFactor":1,"legendFormat":"Cluster","refId":"C"},{"expr":"sum(irate(pilot_xds_pushes{type=\"eds\"}[1m]))","format":"time_series","intervalFactor":1,"legendFormat":"Endpoints","refId":"D"},{"expr":"sum(irate(pilot_xds_pushes{type=\"lds\"}[1m]))","format":"time_series","intervalFactor":1,"legendFormat":"Listeners","refId":"A"},{"expr":"sum(irate(pilot_xds_pushes{type=\"rds\"}[1m]))","format":"time_series","intervalFactor":1,"legendFormat":"Routes","refId":"E"},{"expr":"sum(irate(pilot_xds_pushes{type=\"sds\"}[1m]))","interval":"","legendFormat":"Secrets","refId":"B"},{"expr":"sum(irate(pilot_xds_pushes{type=\"nds\"}[1m]))","interval":"","legendFormat":"Nametables","refId":"F"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Pilot Pushes","tooltip":{"shared":false,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":["total"]},"yaxes":[{"format":"ops","label":null,"logBase":1,"max":null,"min":"0","show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":false}],"yaxis":{"align":false,"alignLevel":null}},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","description":"Captures a variety of pilot errors","fill":1,"gridPos":{"h":8,"w":8,"x":8,"y":15},"id":67,"legend":{"avg":false,"current":false,"hideEmpty":true,"hideZero":true,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"sum(pilot_xds_cds_reject{app=\"istiod\"}) or (absent(pilot_xds_cds_reject{app=\"istiod\"}) - 1)","format":"time_series","hide":false,"intervalFactor":1,"legendFormat":"Rejected CDS Configs","refId":"C"},{"expr":"sum(pilot_xds_eds_reject{app=\"istiod\"}) or (absent(pilot_xds_eds_reject{app=\"istiod\"}) - 1)","format":"time_series","hide":false,"intervalFactor":1,"legendFormat":"Rejected EDS Configs","refId":"D"},{"expr":"sum(pilot_xds_rds_reject{app=\"istiod\"}) or (absent(pilot_xds_rds_reject{app=\"istiod\"}) - 1)","format":"time_series","hide":false,"intervalFactor":1,"legendFormat":"Rejected RDS Configs","refId":"A"},{"expr":"sum(pilot_xds_lds_reject{app=\"istiod\"}) or (absent(pilot_xds_lds_reject{app=\"istiod\"}) - 1)","format":"time_series","hide":false,"intervalFactor":1,"legendFormat":"Rejected LDS Configs","refId":"B"},{"expr":"sum(rate(pilot_xds_write_timeout{app=\"istiod\"}[1m]))","format":"time_series","intervalFactor":1,"legendFormat":"Write Timeouts","refId":"F"},{"expr":"sum(rate(pilot_total_xds_internal_errors{app=\"istiod\"}[1m]))","format":"time_series","hide":false,"intervalFactor":1,"legendFormat":"Internal Errors","refId":"H"},{"expr":"sum(rate(pilot_total_xds_rejects{app=\"istiod\"}[1m]))","format":"time_series","hide":false,"intervalFactor":1,"legendFormat":"Config Rejection Rate","refId":"E"},{"expr":"sum(rate(pilot_xds_push_context_errors{app=\"istiod\"}[1m]))","format":"time_series","hide":false,"intervalFactor":1,"legendFormat":"Push Context Errors","refId":"K"},{"expr":"sum(rate(pilot_xds_write_timeout{app=\"istiod\"}[1m]))","format":"time_series","intervalFactor":1,"legendFormat":"Push Timeouts","refId":"G"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Pilot Errors","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","description":"Shows the total time it takes to push a config update to a proxy","fill":1,"gridPos":{"h":8,"w":8,"x":16,"y":15},"id":624,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":2,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"histogram_quantile(0.5, sum(rate(pilot_proxy_convergence_time_bucket[1m])) by (le))","format":"time_series","intervalFactor":1,"legendFormat":"p50 ","refId":"A"},{"expr":"histogram_quantile(0.9, sum(rate(pilot_proxy_convergence_time_bucket[1m])) by (le))","format":"time_series","intervalFactor":1,"legendFormat":"p90","refId":"B"},{"expr":"histogram_quantile(0.99, sum(rate(pilot_proxy_convergence_time_bucket[1m])) by (le))","format":"time_series","intervalFactor":1,"legendFormat":"p99","refId":"C"},{"expr":"histogram_quantile(0.999, sum(rate(pilot_proxy_convergence_time_bucket[1m])) by (le))","format":"time_series","intervalFactor":1,"legendFormat":"p99.9","refId":"D"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Proxy Push Time","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"s","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fill":1,"gridPos":{"h":8,"w":12,"x":0,"y":23},"id":45,"legend":{"avg":false,"current":false,"hideEmpty":true,"hideZero":true,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null as zero","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"pilot_conflict_inbound_listener{app=\"istiod\"}","format":"time_series","hide":false,"intervalFactor":1,"legendFormat":"Inbound Listeners","refId":"B"},{"expr":"pilot_conflict_outbound_listener_http_over_current_tcp{app=\"istiod\"}","format":"time_series","hide":false,"intervalFactor":1,"legendFormat":"Outbound Listeners (http over current tcp)","refId":"A"},{"expr":"pilot_conflict_outbound_listener_tcp_over_current_tcp{app=\"istiod\"}","format":"time_series","hide":false,"intervalFactor":1,"legendFormat":"Outbound Listeners (tcp over current tcp)","refId":"C"},{"expr":"pilot_conflict_outbound_listener_tcp_over_current_http{app=\"istiod\"}","format":"time_series","hide":false,"intervalFactor":1,"legendFormat":"Outbound Listeners (tcp over current http)","refId":"D"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Conflicts","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":false}],"yaxis":{"align":false,"alignLevel":null}},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fill":1,"gridPos":{"h":8,"w":12,"x":12,"y":23},"id":47,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"avg(pilot_virt_services{app=\"istiod\"})","format":"time_series","intervalFactor":1,"legendFormat":"Virtual Services","refId":"A"},{"expr":"avg(pilot_services{app=\"istiod\"})","format":"time_series","intervalFactor":1,"legendFormat":"Services","refId":"B"},{"expr":"sum(pilot_xds{app=\"istiod\"}) by (pod)","format":"time_series","intervalFactor":1,"legendFormat":"Connected Endpoints {{pod}}","refId":"E"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"ADS Monitoring","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}},{"collapsed":false,"gridPos":{"h":1,"w":24,"x":0,"y":31},"id":64,"panels":[],"title":"Envoy Information","type":"row"},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","description":"Shows details about Envoy proxies in the mesh","fill":1,"gridPos":{"h":8,"w":8,"x":0,"y":32},"id":40,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"sum(irate(envoy_cluster_upstream_cx_total{cluster_name=\"xds-grpc\"}[1m]))","format":"time_series","hide":false,"intervalFactor":1,"legendFormat":"XDS Connections","refId":"C"},{"expr":"sum(irate(envoy_cluster_upstream_cx_connect_fail{cluster_name=\"xds-grpc\"}[1m]))","format":"time_series","hide":false,"intervalFactor":1,"legendFormat":"XDS Connection Failures","refId":"A"},{"expr":"sum(increase(envoy_server_hot_restart_epoch[1m]))","format":"time_series","intervalFactor":1,"legendFormat":"Envoy Restarts","refId":"B"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Envoy Details","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"ops","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"ops","label":null,"logBase":1,"max":null,"min":null,"show":false}],"yaxis":{"align":false,"alignLevel":null}},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fill":1,"gridPos":{"h":8,"w":8,"x":8,"y":32},"id":41,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"sum(envoy_cluster_upstream_cx_active{cluster_name=\"xds-grpc\"})","format":"time_series","intervalFactor":2,"legendFormat":"XDS Active Connections","refId":"C","step":2}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"XDS Active Connections","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","description":"Shows the size of XDS requests and responses","fill":1,"gridPos":{"h":8,"w":8,"x":16,"y":32},"id":42,"legend":{"avg":false,"current":false,"hideEmpty":false,"hideZero":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"max(rate(envoy_cluster_upstream_cx_rx_bytes_total{cluster_name=\"xds-grpc\"}[1m]))","format":"time_series","hide":false,"intervalFactor":1,"legendFormat":"XDS Response Bytes Max","refId":"D"},{"expr":"quantile(0.5, rate(envoy_cluster_upstream_cx_rx_bytes_total{cluster_name=\"xds-grpc\"}[1m]))","format":"time_series","hide":false,"intervalFactor":1,"legendFormat":"XDS Response Bytes Average","refId":"B"},{"expr":"max(rate(envoy_cluster_upstream_cx_tx_bytes_total{cluster_name=\"xds-grpc\"}[1m]))","format":"time_series","intervalFactor":1,"legendFormat":"XDS Request Bytes Max","refId":"A"},{"expr":"quantile(.5, rate(envoy_cluster_upstream_cx_tx_bytes_total{cluster_name=\"xds-grpc\"}[1m]))","format":"time_series","intervalFactor":1,"legendFormat":"XDS Request Bytes Average","refId":"C"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"XDS Requests Size","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"Bps","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"ops","label":null,"logBase":1,"max":null,"min":null,"show":false}],"yaxis":{"align":false,"alignLevel":null}},{"collapsed":false,"datasource":null,"gridPos":{"h":1,"w":24,"x":0,"y":40},"id":626,"panels":[],"title":"Webhooks","type":"row"},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":null,"fill":1,"fillGradient":0,"gridPos":{"h":8,"w":12,"x":0,"y":41},"hiddenSeries":false,"id":629,"legend":{"avg":false,"current":false,"hideEmpty":false,"hideZero":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"nullPointMode":"null","options":{"dataLinks":[]},"percentage":false,"pointradius":2,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"sum(rate(galley_validation_passed[1m]))","interval":"","legendFormat":"Validations (Success)","refId":"A"},{"expr":"sum(rate(galley_validation_failed[1m]))","interval":"","legendFormat":"Validation (Failure)","refId":"B"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Configuration Validation","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":null,"description":"","fill":1,"fillGradient":0,"gridPos":{"h":8,"w":12,"x":12,"y":41},"hiddenSeries":false,"id":630,"legend":{"avg":false,"current":false,"hideZero":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"nullPointMode":"null","options":{"dataLinks":[]},"percentage":false,"pointradius":2,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"sum(rate(sidecar_injection_success_total[1m]))","interval":"","legendFormat":"Injections (Success)","refId":"A"},{"expr":"sum(rate(sidecar_injection_failure_total[1m]))","interval":"","legendFormat":"Injections (Failure)","refId":"B"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Sidecar Injection","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}}],"refresh":"5s","schemaVersion":18,"style":"dark","tags":[],"templating":{"list":[{"current":{"selected":true,"text":"default","value":"default"},"hide":0,"includeAll":false,"label":null,"multi":false,"name":"datasource","options":[],"query":"prometheus","queryValue":"","refresh":1,"regex":"","skipUrlSync":false,"type":"datasource"}]},"time":{"from":"now-5m","to":"now"},"timepicker":{"refresh_intervals":["5s","10s","30s","1m","5m","15m","30m","1h","2h","1d"],"time_options":["5m","15m","1h","6h","12h","24h","2d","7d","30d"]},"timezone":"browser","title":"Istio Control Plane Dashboard","uid":"3--MLVZZk","version":11} -kind: ConfigMap -metadata: - creationTimestamp: null - name: istio-grafana-dashboards - namespace: dubbo-system - ---- - -apiVersion: v1 -data: - istio-extension-dashboard.json: | - {"annotations":{"list":[{"builtIn":1,"datasource":"-- Grafana --","enable":true,"hide":true,"iconColor":"rgba(0, 211, 255, 1)","name":"Annotations & Alerts","type":"dashboard"}]},"editable":false,"gnetId":null,"graphTooltip":0,"links":[],"panels":[{"collapsed":false,"datasource":"Prometheus","gridPos":{"h":1,"w":24,"x":0,"y":0},"id":3,"panels":[],"title":"Wasm VMs","type":"row"},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","description":"","fieldConfig":{"defaults":{"custom":{"align":null},"links":[],"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"red","value":80}]}},"overrides":[]},"fill":1,"fillGradient":0,"gridPos":{"h":8,"w":12,"x":0,"y":1},"hiddenSeries":false,"id":2,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"nullPointMode":"null","options":{"alertThreshold":true},"percentage":false,"pluginVersion":"7.2.1","pointradius":2,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"avg(envoy_wasm_envoy_wasm_runtime_null_active)","interval":"","legendFormat":"native","refId":"A"},{"expr":"avg(envoy_wasm_envoy_wasm_runtime_v8_active)","interval":"","legendFormat":"v8","refId":"B"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Active","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"$$hashKey":"object:123","format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true},{"$$hashKey":"object:124","format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fieldConfig":{"defaults":{"custom":{},"links":[]},"overrides":[]},"fill":1,"fillGradient":0,"gridPos":{"h":8,"w":12,"x":12,"y":1},"hiddenSeries":false,"id":6,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"nullPointMode":"null","options":{"alertThreshold":true},"percentage":false,"pluginVersion":"7.2.1","pointradius":2,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"avg(envoy_wasm_envoy_wasm_runtime_null_created)","interval":"","legendFormat":"native","refId":"A"},{"expr":"avg(envoy_wasm_envoy_wasm_runtime_v8_created)","interval":"","legendFormat":"v8","refId":"B"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Created","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"$$hashKey":"object:68","format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true},{"$$hashKey":"object:69","format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}},{"collapsed":false,"datasource":"Prometheus","gridPos":{"h":1,"w":24,"x":0,"y":9},"id":7,"panels":[],"title":"Wasm Module Remote Load","type":"row"},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fieldConfig":{"defaults":{"custom":{},"links":[]},"overrides":[]},"fill":1,"fillGradient":0,"gridPos":{"h":8,"w":8,"x":0,"y":10},"hiddenSeries":false,"id":11,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"nullPointMode":"null","options":{"alertThreshold":true},"percentage":false,"pluginVersion":"7.2.1","pointradius":2,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"avg(envoy_wasm_remote_load_cache_entries)","interval":"","legendFormat":"entries","refId":"A"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Cache Entry","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"$$hashKey":"object:178","format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true},{"$$hashKey":"object:179","format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fieldConfig":{"defaults":{"custom":{},"links":[]},"overrides":[]},"fill":1,"fillGradient":0,"gridPos":{"h":8,"w":8,"x":8,"y":10},"hiddenSeries":false,"id":8,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"nullPointMode":"null","options":{"alertThreshold":true},"percentage":false,"pluginVersion":"7.2.1","pointradius":2,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"avg(envoy_wasm_remote_load_cache_hits)","interval":"","legendFormat":"hits","refId":"A"},{"expr":"avg(envoy_wasm_remote_load_cache_misses)","interval":"","legendFormat":"misses","refId":"B"},{"expr":"avg(envoy_wasm_remote_load_cache_negative_hits)","interval":"","legendFormat":"negative hits","refId":"C"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Cache Visit","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"$$hashKey":"object:233","format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true},{"$$hashKey":"object:234","format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fieldConfig":{"defaults":{"custom":{},"links":[]},"overrides":[]},"fill":1,"fillGradient":0,"gridPos":{"h":8,"w":8,"x":16,"y":10},"hiddenSeries":false,"id":10,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"nullPointMode":"null","options":{"alertThreshold":true},"percentage":false,"pluginVersion":"7.2.1","pointradius":2,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"avg(envoy_wasm_remote_load_fetch_failures)","interval":"","legendFormat":"failures","refId":"A"},{"expr":"avg(envoy_wasm_remote_load_fetch_successes)","interval":"","legendFormat":"successes","refId":"B"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Remote Fetch","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"$$hashKey":"object:288","format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true},{"$$hashKey":"object:289","format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}},{"collapsed":false,"datasource":"Prometheus","gridPos":{"h":1,"w":24,"x":0,"y":18},"id":71,"panels":[],"title":"Proxy Resource Usage","type":"row"},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fieldConfig":{"defaults":{"custom":{}},"overrides":[]},"fill":1,"fillGradient":0,"gridPos":{"h":8,"w":12,"x":0,"y":19},"hiddenSeries":false,"id":72,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","options":{"alertThreshold":true},"percentage":false,"pluginVersion":"7.2.1","pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"sum(container_memory_working_set_bytes{container=\"istio-proxy\"})","format":"time_series","hide":false,"intervalFactor":2,"legendFormat":"Total (k8s)","refId":"A","step":2}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Memory","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"$$hashKey":"object:396","format":"bytes","label":null,"logBase":1,"max":null,"min":null,"show":true},{"$$hashKey":"object:397","format":"short","label":null,"logBase":1,"max":null,"min":null,"show":false}],"yaxis":{"align":false,"alignLevel":null}},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fieldConfig":{"defaults":{"custom":{}},"overrides":[]},"fill":1,"fillGradient":0,"gridPos":{"h":8,"w":12,"x":12,"y":19},"hiddenSeries":false,"id":73,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","options":{"alertThreshold":true},"percentage":false,"pluginVersion":"7.2.1","pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"sum(rate(container_cpu_usage_seconds_total{container=\"istio-proxy\"}[1m]))","format":"time_series","hide":false,"intervalFactor":2,"legendFormat":"Total (k8s)","refId":"A","step":2}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"vCPU","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"$$hashKey":"object:447","format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true},{"$$hashKey":"object:448","format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}],"yaxis":{"align":false,"alignLevel":null}}],"refresh":false,"schemaVersion":26,"style":"dark","tags":[],"templating":{"list":[]},"time":{"from":"2020-10-22T23:11:45.783Z","to":"2020-10-23T00:04:19.481Z"},"timepicker":{"refresh_intervals":["10s","30s","1m","5m","15m","30m","1h","2h","1d"]},"timezone":"","title":"Istio Wasm Extension Dashboard","uid":"7PAV7ctGz","version":17} - istio-mesh-dashboard.json: | - {"annotations":{"list":[{"builtIn":1,"datasource":"-- Grafana --","enable":true,"hide":true,"iconColor":"rgba(0, 211, 255, 1)","name":"Annotations & Alerts","type":"dashboard"}]},"editable":false,"gnetId":null,"graphTooltip":0,"id":null,"links":[],"panels":[{"content":"
\n
\n Istio\n
\n
\n Istio is an open platform that provides a uniform way to secure,\n connect, and \n monitor microservices.\n
\n Need help? Join the Istio community.\n
\n
","gridPos":{"h":3,"w":24,"x":0,"y":0},"height":"50px","id":13,"links":[],"mode":"html","style":{"font-size":"18pt"},"title":"","transparent":true,"type":"text"},{"cacheTimeout":null,"colorBackground":false,"colorValue":false,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"datasource":"Prometheus","format":"ops","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"gridPos":{"h":3,"w":6,"x":0,"y":3},"id":20,"interval":null,"links":[],"options":{"colorMode":"value","graphMode":"area","justifyMode":"auto","orientation":"horizontal","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"textMode":"auto"},"mappingType":1,"mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","rangeMaps":[{"from":"null","text":"N/A","to":"null"}],"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":true},"tableColumn":"","targets":[{"expr":"round(sum(irate(istio_requests_total{reporter=\"source\"}[1m])), 0.001)","intervalFactor":1,"refId":"A","step":4}],"thresholds":"","title":"Global Request Volume","type":"singlestat","valueFontSize":"80%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg"},{"cacheTimeout":null,"colorBackground":false,"colorValue":false,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"datasource":"Prometheus","format":"percentunit","gauge":{"maxValue":100,"minValue":80,"show":false,"thresholdLabels":false,"thresholdMarkers":false},"gridPos":{"h":3,"w":6,"x":6,"y":3},"id":21,"interval":null,"links":[],"options":{"colorMode":"value","graphMode":"area","justifyMode":"auto","orientation":"horizontal","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"textMode":"auto"},"mappingType":1,"mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","rangeMaps":[{"from":"null","text":"N/A","to":"null"}],"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":true},"tableColumn":"","targets":[{"expr":"sum(rate(istio_requests_total{reporter=\"source\", response_code!~\"5.*\"}[1m])) / sum(rate(istio_requests_total{reporter=\"source\"}[1m]))","format":"time_series","intervalFactor":1,"refId":"A","step":4}],"thresholds":"95, 99, 99.5","title":"Global Success Rate (non-5xx responses)","type":"singlestat","valueFontSize":"80%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg"},{"cacheTimeout":null,"colorBackground":false,"colorValue":false,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"datasource":"Prometheus","format":"ops","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"gridPos":{"h":3,"w":6,"x":12,"y":3},"id":22,"interval":null,"links":[],"options":{"colorMode":"value","graphMode":"area","justifyMode":"auto","orientation":"horizontal","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"textMode":"auto"},"mappingType":1,"mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","rangeMaps":[{"from":"null","text":"N/A","to":"null"}],"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":true},"tableColumn":"","targets":[{"expr":"sum(irate(istio_requests_total{reporter=\"source\", response_code=~\"4.*\"}[1m]))","format":"time_series","intervalFactor":1,"refId":"A","step":4}],"thresholds":"","title":"4xxs","type":"singlestat","valueFontSize":"80%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg"},{"cacheTimeout":null,"colorBackground":false,"colorValue":false,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"datasource":"Prometheus","format":"ops","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"gridPos":{"h":3,"w":6,"x":18,"y":3},"id":23,"interval":null,"links":[],"options":{"colorMode":"value","graphMode":"area","justifyMode":"auto","orientation":"horizontal","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"textMode":"auto"},"mappingType":1,"mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","rangeMaps":[{"from":"null","text":"N/A","to":"null"}],"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":true},"tableColumn":"","targets":[{"expr":"sum(irate(istio_requests_total{reporter=\"source\", response_code=~\"5.*\"}[1m]))","format":"time_series","intervalFactor":1,"refId":"A","step":4}],"thresholds":"","title":"5xxs","type":"singlestat","valueFontSize":"80%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg"},{"cacheTimeout":null,"colorBackground":false,"colorValue":false,"colors":["#299c46","rgba(237, 129, 40, 0.89)","#d44a3a"],"datasource":"Prometheus","format":"none","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"gridPos":{"h":3,"w":6,"x":0,"y":6},"id":113,"interval":null,"links":[],"options":{"colorMode":"value","graphMode":"area","justifyMode":"auto","orientation":"horizontal","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"textMode":"auto"},"mappingType":1,"mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","rangeMaps":[{"from":"null","text":"N/A","to":"null"}],"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":false,"lineColor":"rgb(31, 120, 193)","show":true},"tableColumn":"","targets":[{"expr":"max(pilot_k8s_cfg_events{type=\"VirtualService\", event=\"add\"}) - (max(pilot_k8s_cfg_events{type=\"VirtualService\", event=\"delete\"}) or max(up * 0))","format":"time_series","intervalFactor":1,"refId":"A"}],"thresholds":"","timeFrom":null,"timeShift":null,"title":"Virtual Services","type":"singlestat","valueFontSize":"80%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"current"},{"cacheTimeout":null,"colorBackground":false,"colorValue":false,"colors":["#299c46","rgba(237, 129, 40, 0.89)","#d44a3a"],"datasource":"Prometheus","format":"none","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"gridPos":{"h":3,"w":6,"x":6,"y":6},"id":114,"interval":null,"links":[],"options":{"colorMode":"value","graphMode":"area","justifyMode":"auto","orientation":"horizontal","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"textMode":"auto"},"mappingType":1,"mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","rangeMaps":[{"from":"null","text":"N/A","to":"null"}],"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":false,"lineColor":"rgb(31, 120, 193)","show":true},"tableColumn":"","targets":[{"expr":"max(pilot_k8s_cfg_events{type=\"DestinationRule\", event=\"add\"}) - (max(pilot_k8s_cfg_events{type=\"DestinationRule\", event=\"delete\"}) or max(up * 0))","format":"time_series","intervalFactor":1,"refId":"A"}],"thresholds":"","timeFrom":null,"timeShift":null,"title":"Destination Rules","type":"singlestat","valueFontSize":"80%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"current"},{"cacheTimeout":null,"colorBackground":false,"colorValue":false,"colors":["#299c46","rgba(237, 129, 40, 0.89)","#d44a3a"],"datasource":"Prometheus","format":"none","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"gridPos":{"h":3,"w":6,"x":12,"y":6},"id":115,"interval":null,"links":[],"options":{"colorMode":"value","graphMode":"area","justifyMode":"auto","orientation":"horizontal","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"textMode":"auto"},"mappingType":1,"mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","rangeMaps":[{"from":"null","text":"N/A","to":"null"}],"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":false,"lineColor":"rgb(31, 120, 193)","show":true},"tableColumn":"","targets":[{"expr":"max(pilot_k8s_cfg_events{type=\"Gateway\", event=\"add\"}) - (max(pilot_k8s_cfg_events{type=\"Gateway\", event=\"delete\"}) or max(up * 0))","format":"time_series","intervalFactor":1,"refId":"A"}],"thresholds":"","timeFrom":null,"timeShift":null,"title":"Gateways","type":"singlestat","valueFontSize":"80%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"current"},{"cacheTimeout":null,"colorBackground":false,"colorValue":false,"colors":["#299c46","rgba(237, 129, 40, 0.89)","#d44a3a"],"datasource":"Prometheus","format":"none","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"gridPos":{"h":3,"w":6,"x":18,"y":6},"id":116,"interval":null,"links":[],"options":{"colorMode":"value","graphMode":"area","justifyMode":"auto","orientation":"horizontal","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"textMode":"auto"},"mappingType":1,"mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","rangeMaps":[{"from":"null","text":"N/A","to":"null"}],"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":false,"lineColor":"rgb(31, 120, 193)","show":true},"tableColumn":"","targets":[{"expr":"max(pilot_k8s_cfg_events{type=\"WorkloadEntry\", event=\"add\"}) - (max(pilot_k8s_cfg_events{type=\"WorkloadEntry\", event=\"delete\"}) or max(up * 0))","format":"time_series","intervalFactor":1,"refId":"A"}],"thresholds":"","timeFrom":null,"timeShift":null,"title":"Workload Entries","type":"singlestat","valueFontSize":"80%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"current"},{"cacheTimeout":null,"colorBackground":false,"colorValue":false,"colors":["#299c46","rgba(237, 129, 40, 0.89)","#d44a3a"],"datasource":"Prometheus","format":"none","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"gridPos":{"h":3,"w":6,"x":0,"y":6},"id":117,"interval":null,"links":[],"options":{"colorMode":"value","graphMode":"area","justifyMode":"auto","orientation":"horizontal","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"textMode":"auto"},"mappingType":1,"mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","rangeMaps":[{"from":"null","text":"N/A","to":"null"}],"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":false,"lineColor":"rgb(31, 120, 193)","show":true},"tableColumn":"","targets":[{"expr":"max(pilot_k8s_cfg_events{type=\"ServiceEntry\", event=\"add\"}) - (max(pilot_k8s_cfg_events{type=\"ServiceEntry\", event=\"delete\"}) or max(up * 0))","format":"time_series","intervalFactor":1,"refId":"A"}],"thresholds":"","timeFrom":null,"timeShift":null,"title":"Service Entries","type":"singlestat","valueFontSize":"80%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"current"},{"cacheTimeout":null,"colorBackground":false,"colorValue":false,"colors":["#299c46","rgba(237, 129, 40, 0.89)","#d44a3a"],"datasource":"Prometheus","format":"none","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"gridPos":{"h":3,"w":6,"x":6,"y":6},"id":90,"interval":null,"links":[],"options":{"colorMode":"value","graphMode":"area","justifyMode":"auto","orientation":"horizontal","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"textMode":"auto"},"mappingType":1,"mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","rangeMaps":[{"from":"null","text":"N/A","to":"null"}],"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":false,"lineColor":"rgb(31, 120, 193)","show":true},"tableColumn":"","targets":[{"expr":"max(pilot_k8s_cfg_events{type=\"PeerAuthentication\", event=\"add\"}) - (max(pilot_k8s_cfg_events{type=\"PeerAuthentication\", event=\"delete\"}) or max(up * 0))","format":"time_series","intervalFactor":1,"refId":"A"}],"thresholds":"","timeFrom":null,"timeShift":null,"title":"PeerAuthentication Policies","type":"singlestat","valueFontSize":"80%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"current"},{"cacheTimeout":null,"colorBackground":false,"colorValue":false,"colors":["#299c46","rgba(237, 129, 40, 0.89)","#d44a3a"],"datasource":"Prometheus","format":"none","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"gridPos":{"h":3,"w":6,"x":12,"y":6},"id":91,"interval":null,"links":[],"options":{"colorMode":"value","graphMode":"area","justifyMode":"auto","orientation":"horizontal","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"textMode":"auto"},"mappingType":1,"mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","rangeMaps":[{"from":"null","text":"N/A","to":"null"}],"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":false,"lineColor":"rgb(31, 120, 193)","show":true},"tableColumn":"","targets":[{"expr":"max(pilot_k8s_cfg_events{type=\"RequestAuthentication\", event=\"add\"}) - (max(pilot_k8s_cfg_events{type=\"RequestAuthentication\", event=\"delete\"}) or max(up * 0))","format":"time_series","intervalFactor":1,"refId":"A"}],"thresholds":"","timeFrom":null,"timeShift":null,"title":"RequestAuthentication Policies","type":"singlestat","valueFontSize":"80%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"current"},{"cacheTimeout":null,"colorBackground":false,"colorValue":false,"colors":["#299c46","rgba(237, 129, 40, 0.89)","#d44a3a"],"datasource":"Prometheus","format":"none","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"gridPos":{"h":3,"w":6,"x":18,"y":6},"id":92,"interval":null,"links":[],"options":{"colorMode":"value","graphMode":"area","justifyMode":"auto","orientation":"horizontal","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"textMode":"auto"},"mappingType":1,"mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","rangeMaps":[{"from":"null","text":"N/A","to":"null"}],"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":false,"lineColor":"rgb(31, 120, 193)","show":true},"tableColumn":"","targets":[{"expr":"max(pilot_k8s_cfg_events{type=\"AuthorizationPolicy\", event=\"add\"}) - (max(pilot_k8s_cfg_events{type=\"AuthorizationPolicy\", event=\"delete\"}) or max(up * 0))","format":"time_series","intervalFactor":1,"refId":"A"}],"thresholds":"","timeFrom":null,"timeShift":null,"title":"Authorization Policies","type":"singlestat","valueFontSize":"80%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"current"},{"columns":[],"datasource":"Prometheus","fontSize":"100%","gridPos":{"h":21,"w":24,"x":0,"y":9},"hideTimeOverride":false,"id":73,"links":[],"pageSize":null,"repeat":null,"repeatDirection":"v","scroll":true,"showHeader":true,"sort":{"col":5,"desc":true},"styles":[{"alias":"Workload","colorMode":null,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"dateFormat":"YYYY-MM-DD HH:mm:ss","decimals":2,"link":false,"linkTargetBlank":false,"linkTooltip":"Workload dashboard","linkUrl":"/dashboard/db/istio-workload-dashboard?var-namespace=${__cell_3:raw}&var-workload=${__cell_2:raw}","pattern":"destination_workload","preserveFormat":false,"sanitize":false,"thresholds":[],"type":"hidden","unit":"short"},{"alias":"","colorMode":null,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"dateFormat":"YYYY-MM-DD HH:mm:ss","decimals":2,"pattern":"Time","thresholds":[],"type":"hidden","unit":"short"},{"alias":"Requests","colorMode":null,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"dateFormat":"YYYY-MM-DD HH:mm:ss","decimals":2,"pattern":"Value #A","thresholds":[],"type":"number","unit":"ops"},{"alias":"P50 Latency","colorMode":null,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"dateFormat":"YYYY-MM-DD HH:mm:ss","decimals":2,"pattern":"Value #B","thresholds":[],"type":"number","unit":"s"},{"alias":"P90 Latency","colorMode":null,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"dateFormat":"YYYY-MM-DD HH:mm:ss","decimals":2,"pattern":"Value #C","thresholds":[],"type":"number","unit":"s"},{"alias":"P99 Latency","colorMode":null,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"dateFormat":"YYYY-MM-DD HH:mm:ss","decimals":2,"pattern":"Value #D","thresholds":[],"type":"number","unit":"s"},{"alias":"Success Rate","colorMode":"cell","colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"dateFormat":"YYYY-MM-DD HH:mm:ss","decimals":2,"pattern":"Value #E","thresholds":[".95"," 1.00"],"type":"number","unit":"percentunit"},{"alias":"Workload","colorMode":null,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"dateFormat":"YYYY-MM-DD HH:mm:ss","decimals":2,"link":true,"linkTooltip":"$__cell dashboard","linkUrl":"/dashboard/db/istio-workload-dashboard?var-workload=${__cell_2:raw}&var-namespace=${__cell_3:raw}","pattern":"destination_workload_var","thresholds":[],"type":"number","unit":"short"},{"alias":"Service","colorMode":null,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"dateFormat":"YYYY-MM-DD HH:mm:ss","decimals":2,"link":true,"linkTooltip":"$__cell dashboard","linkUrl":"/dashboard/db/istio-service-dashboard?var-service=${__cell_1:raw}","pattern":"destination_service","thresholds":[],"type":"string","unit":"short"},{"alias":"","colorMode":null,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"dateFormat":"YYYY-MM-DD HH:mm:ss","decimals":2,"pattern":"destination_workload_namespace","thresholds":[],"type":"hidden","unit":"short"}],"targets":[{"expr":"label_join(sum(rate(istio_requests_total{reporter=\"source\", response_code=\"200\"}[1m])) by (destination_workload, destination_workload_namespace, destination_service), \"destination_workload_var\", \".\", \"destination_workload\", \"destination_workload_namespace\")","format":"table","hide":false,"instant":true,"intervalFactor":1,"legendFormat":"{{ destination_workload}}.{{ destination_workload_namespace }}","refId":"A"},{"expr":"label_join((histogram_quantile(0.50, sum(rate(istio_request_duration_milliseconds_bucket{reporter=\"source\"}[1m])) by (le, destination_workload, destination_workload_namespace)) / 1000) or histogram_quantile(0.50, sum(rate(istio_request_duration_seconds_bucket{reporter=\"source\"}[1m])) by (le, destination_workload, destination_workload_namespace)), \"destination_workload_var\", \".\", \"destination_workload\", \"destination_workload_namespace\")","format":"table","hide":false,"instant":true,"intervalFactor":1,"legendFormat":"{{ destination_workload}}.{{ destination_workload_namespace }}","refId":"B"},{"expr":"label_join((histogram_quantile(0.90, sum(rate(istio_request_duration_milliseconds_bucket{reporter=\"source\"}[1m])) by (le, destination_workload, destination_workload_namespace)) / 1000) or histogram_quantile(0.90, sum(rate(istio_request_duration_seconds_bucket{reporter=\"source\"}[1m])) by (le, destination_workload, destination_workload_namespace)), \"destination_workload_var\", \".\", \"destination_workload\", \"destination_workload_namespace\")","format":"table","hide":false,"instant":true,"intervalFactor":1,"legendFormat":"{{ destination_workload }}.{{ destination_workload_namespace }}","refId":"C"},{"expr":"label_join((histogram_quantile(0.99, sum(rate(istio_request_duration_milliseconds_bucket{reporter=\"source\"}[1m])) by (le, destination_workload, destination_workload_namespace)) / 1000) or histogram_quantile(0.99, sum(rate(istio_request_duration_seconds_bucket{reporter=\"source\"}[1m])) by (le, destination_workload, destination_workload_namespace)), \"destination_workload_var\", \".\", \"destination_workload\", \"destination_workload_namespace\")","format":"table","hide":false,"instant":true,"intervalFactor":1,"legendFormat":"{{ destination_workload }}.{{ destination_workload_namespace }}","refId":"D"},{"expr":"label_join((sum(rate(istio_requests_total{reporter=\"source\", response_code!~\"5.*\"}[1m])) by (destination_workload, destination_workload_namespace) / sum(rate(istio_requests_total{reporter=\"source\"}[1m])) by (destination_workload, destination_workload_namespace)), \"destination_workload_var\", \".\", \"destination_workload\", \"destination_workload_namespace\")","format":"table","hide":false,"instant":true,"interval":"","intervalFactor":1,"legendFormat":"{{ destination_workload }}.{{ destination_workload_namespace }}","refId":"E"}],"timeFrom":null,"title":"HTTP/GRPC Workloads","transform":"table","type":"table"},{"columns":[],"datasource":"Prometheus","fontSize":"100%","gridPos":{"h":18,"w":24,"x":0,"y":30},"hideTimeOverride":false,"id":109,"links":[],"pageSize":null,"repeatDirection":"v","scroll":true,"showHeader":true,"sort":{"col":5,"desc":true},"styles":[{"alias":"Workload","colorMode":null,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"dateFormat":"YYYY-MM-DD HH:mm:ss","decimals":2,"link":false,"linkTargetBlank":false,"linkTooltip":"$__cell dashboard","linkUrl":"/dashboard/db/istio-workload-dashboard?var-namespace=${__cell_3:raw}&var-workload=${__cell_2:raw}","pattern":"destination_workload","preserveFormat":false,"sanitize":false,"thresholds":[],"type":"hidden","unit":"short"},{"alias":"Bytes Sent","colorMode":null,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"dateFormat":"YYYY-MM-DD HH:mm:ss","decimals":2,"pattern":"Value #A","thresholds":[""],"type":"number","unit":"Bps"},{"alias":"Bytes Received","colorMode":null,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"dateFormat":"YYYY-MM-DD HH:mm:ss","decimals":2,"pattern":"Value #B","thresholds":[],"type":"number","unit":"Bps"},{"alias":"","colorMode":null,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"dateFormat":"YYYY-MM-DD HH:mm:ss","decimals":2,"pattern":"Time","thresholds":[],"type":"hidden","unit":"short"},{"alias":"Workload","colorMode":null,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"dateFormat":"YYYY-MM-DD HH:mm:ss","decimals":2,"link":true,"linkTooltip":"$__cell dashboard","linkUrl":"/dashboard/db/istio-workload-dashboard?var-namespace=${__cell_3:raw}&var-workload=${__cell_2:raw}","pattern":"destination_workload_var","thresholds":[],"type":"string","unit":"short"},{"alias":"","colorMode":null,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"dateFormat":"YYYY-MM-DD HH:mm:ss","decimals":2,"pattern":"destination_workload_namespace","thresholds":[],"type":"hidden","unit":"short"},{"alias":"Service","colorMode":null,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"dateFormat":"YYYY-MM-DD HH:mm:ss","decimals":2,"link":true,"linkTooltip":"$__cell dashboard","linkUrl":"/dashboard/db/istio-service-dashboard?var-service=${__cell_1:raw}","pattern":"destination_service","thresholds":[],"type":"number","unit":"short"}],"targets":[{"expr":"label_join(sum(rate(istio_tcp_received_bytes_total{reporter=\"source\"}[1m])) by (destination_workload, destination_workload_namespace, destination_service), \"destination_workload_var\", \".\", \"destination_workload\", \"destination_workload_namespace\")","format":"table","hide":false,"instant":true,"intervalFactor":1,"legendFormat":"{{ destination_workload }}","refId":"A"},{"expr":"label_join(sum(rate(istio_tcp_sent_bytes_total{reporter=\"source\"}[1m])) by (destination_workload, destination_workload_namespace, destination_service), \"destination_workload_var\", \".\", \"destination_workload\", \"destination_workload_namespace\")","format":"table","hide":false,"instant":true,"intervalFactor":1,"legendFormat":"{{ destination_workload }}","refId":"B"}],"timeFrom":null,"title":"TCP Workloads","transform":"table","type":"table"},{"aliasColors":{},"bars":false,"dashLength":10,"dashes":false,"datasource":"Prometheus","fill":1,"gridPos":{"h":9,"w":24,"x":0,"y":48},"id":111,"legend":{"alignAsTable":false,"avg":false,"current":false,"max":false,"min":false,"rightSide":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":1,"links":[],"nullPointMode":"null","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"spaceLength":10,"stack":false,"steppedLine":false,"targets":[{"expr":"sum(istio_build) by (component, tag)","format":"time_series","intervalFactor":1,"legendFormat":"{{ component }}: {{ tag }}","refId":"A"}],"thresholds":[],"timeFrom":null,"timeRegions":[],"timeShift":null,"title":"Istio Components by Version","tooltip":{"shared":true,"sort":0,"value_type":"individual"},"type":"graph","xaxis":{"buckets":null,"mode":"time","name":null,"show":true,"values":[]},"yaxes":[{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":false}],"yaxis":{"align":false,"alignLevel":null}}],"refresh":"5s","schemaVersion":18,"style":"dark","tags":[],"templating":{"list":[{"current":{"selected":true,"text":"default","value":"default"},"hide":0,"includeAll":false,"label":null,"multi":false,"name":"datasource","options":[],"query":"prometheus","queryValue":"","refresh":1,"regex":"","skipUrlSync":false,"type":"datasource"}]},"time":{"from":"now-5m","to":"now"},"timepicker":{"refresh_intervals":["5s","10s","30s","1m","5m","15m","30m","1h","2h","1d"],"time_options":["5m","15m","1h","6h","12h","24h","2d","7d","30d"]},"timezone":"browser","title":"Istio Mesh Dashboard","uid":"G8wLrJIZk","version":5} - istio-service-dashboard.json: "{\"annotations\":{\"list\":[{\"builtIn\":1,\"datasource\":\"-- - Grafana --\",\"enable\":true,\"hide\":true,\"iconColor\":\"rgba(0, 211, 255, 1)\",\"name\":\"Annotations - & Alerts\",\"type\":\"dashboard\"}]},\"editable\":false,\"gnetId\":null,\"graphTooltip\":0,\"iteration\":1595591291797,\"links\":[],\"panels\":[{\"collapsed\":true,\"gridPos\":{\"h\":1,\"w\":24,\"x\":0,\"y\":0},\"id\":106,\"panels\":[{\"content\":\"
\\nSERVICE: $service\\n
\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"gridPos\":{\"h\":3,\"w\":24,\"x\":0,\"y\":1},\"id\":89,\"links\":[],\"mode\":\"html\",\"options\":{\"content\":\"
\\nSERVICE: $service\\n
\",\"mode\":\"html\"},\"pluginVersion\":\"7.1.0\",\"title\":\"\",\"transparent\":true,\"type\":\"text\"},{\"cacheTimeout\":null,\"colorBackground\":false,\"colorValue\":false,\"colors\":[\"rgba(245, - 54, 54, 0.9)\",\"rgba(237, 129, 40, 0.89)\",\"rgba(50, 172, 45, 0.97)\"],\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"format\":\"ops\",\"gauge\":{\"maxValue\":100,\"minValue\":0,\"show\":false,\"thresholdLabels\":false,\"thresholdMarkers\":true},\"gridPos\":{\"h\":4,\"w\":6,\"x\":0,\"y\":4},\"id\":12,\"interval\":null,\"links\":[],\"options\":{\"colorMode\":\"value\",\"graphMode\":\"area\",\"justifyMode\":\"auto\",\"orientation\":\"horizontal\",\"reduceOptions\":{\"calcs\":[\"lastNotNull\"],\"fields\":\"\",\"values\":false},\"textMode\":\"auto\"},\"mappingType\":1,\"mappingTypes\":[{\"name\":\"value - to text\",\"value\":1},{\"name\":\"range to text\",\"value\":2}],\"maxDataPoints\":100,\"nullPointMode\":\"connected\",\"nullText\":null,\"postfix\":\"\",\"postfixFontSize\":\"50%\",\"prefix\":\"\",\"prefixFontSize\":\"50%\",\"rangeMaps\":[{\"from\":\"null\",\"text\":\"N/A\",\"to\":\"null\"}],\"sparkline\":{\"fillColor\":\"rgba(31, - 118, 189, 0.18)\",\"full\":true,\"lineColor\":\"rgb(31, 120, 193)\",\"show\":true},\"tableColumn\":\"\",\"targets\":[{\"expr\":\"round(sum(irate(istio_requests_total{reporter=~\\\"$qrep\\\",destination_service=~\\\"$service\\\"}[5m])), - 0.001)\",\"format\":\"time_series\",\"intervalFactor\":1,\"refId\":\"A\",\"step\":4}],\"thresholds\":\"\",\"title\":\"Client - Request Volume\",\"type\":\"singlestat\",\"valueFontSize\":\"80%\",\"valueMaps\":[{\"op\":\"=\",\"text\":\"N/A\",\"value\":\"null\"}],\"valueName\":\"current\"},{\"cacheTimeout\":null,\"colorBackground\":false,\"colorValue\":false,\"colors\":[\"rgba(50, - 172, 45, 0.97)\",\"rgba(237, 129, 40, 0.89)\",\"rgba(245, 54, 54, 0.9)\"],\"datasource\":\"Prometheus\",\"decimals\":null,\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"format\":\"percentunit\",\"gauge\":{\"maxValue\":100,\"minValue\":80,\"show\":false,\"thresholdLabels\":false,\"thresholdMarkers\":false},\"gridPos\":{\"h\":4,\"w\":6,\"x\":6,\"y\":4},\"id\":14,\"interval\":null,\"links\":[],\"options\":{\"colorMode\":\"value\",\"graphMode\":\"area\",\"justifyMode\":\"auto\",\"orientation\":\"horizontal\",\"reduceOptions\":{\"calcs\":[\"lastNotNull\"],\"fields\":\"\",\"values\":false},\"textMode\":\"auto\"},\"mappingType\":1,\"mappingTypes\":[{\"name\":\"value - to text\",\"value\":1},{\"name\":\"range to text\",\"value\":2}],\"maxDataPoints\":100,\"nullPointMode\":\"connected\",\"nullText\":null,\"postfix\":\"\",\"postfixFontSize\":\"50%\",\"prefix\":\"\",\"prefixFontSize\":\"50%\",\"rangeMaps\":[{\"from\":\"null\",\"text\":\"N/A\",\"to\":\"null\"}],\"sparkline\":{\"fillColor\":\"rgba(31, - 118, 189, 0.18)\",\"full\":true,\"lineColor\":\"rgb(31, 120, 193)\",\"show\":true},\"tableColumn\":\"\",\"targets\":[{\"expr\":\"sum(irate(istio_requests_total{reporter=~\\\"$qrep\\\",destination_service=~\\\"$service\\\",response_code!~\\\"5.*\\\"}[5m])) - / sum(irate(istio_requests_total{reporter=~\\\"$qrep\\\",destination_service=~\\\"$service\\\"}[5m]))\",\"format\":\"time_series\",\"intervalFactor\":1,\"refId\":\"A\"}],\"thresholds\":\"95, - 99, 99.5\",\"title\":\"Client Success Rate (non-5xx responses)\",\"type\":\"singlestat\",\"valueFontSize\":\"80%\",\"valueMaps\":[{\"op\":\"=\",\"text\":\"N/A\",\"value\":\"null\"}],\"valueName\":\"avg\"},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":4,\"w\":6,\"x\":12,\"y\":4},\"hiddenSeries\":false,\"id\":87,\"legend\":{\"alignAsTable\":false,\"avg\":false,\"current\":false,\"hideEmpty\":false,\"hideZero\":false,\"max\":false,\"min\":false,\"rightSide\":true,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"(histogram_quantile(0.50, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\",destination_service=~\\\"$service\\\"}[1m])) - by (le)) / 1000) or histogram_quantile(0.50, sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\",destination_service=~\\\"$service\\\"}[1m])) - by (le))\",\"format\":\"time_series\",\"interval\":\"\",\"intervalFactor\":1,\"legendFormat\":\"P50\",\"refId\":\"A\"},{\"expr\":\"(histogram_quantile(0.90, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\",destination_service=~\\\"$service\\\"}[1m])) - by (le)) / 1000) or histogram_quantile(0.90, sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\",destination_service=~\\\"$service\\\"}[1m])) - by (le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"P90\",\"refId\":\"B\"},{\"expr\":\"(histogram_quantile(0.99, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\",destination_service=~\\\"$service\\\"}[1m])) - by (le)) / 1000) or histogram_quantile(0.99, sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\",destination_service=~\\\"$service\\\"}[1m])) - by (le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"P99\",\"refId\":\"C\"}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Client - Request Duration\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"s\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"cacheTimeout\":null,\"colorBackground\":false,\"colorValue\":false,\"colors\":[\"#299c46\",\"rgba(237, - 129, 40, 0.89)\",\"#d44a3a\"],\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"format\":\"Bps\",\"gauge\":{\"maxValue\":100,\"minValue\":0,\"show\":false,\"thresholdLabels\":false,\"thresholdMarkers\":true},\"gridPos\":{\"h\":4,\"w\":6,\"x\":18,\"y\":4},\"id\":84,\"interval\":null,\"links\":[],\"options\":{\"colorMode\":\"value\",\"graphMode\":\"area\",\"justifyMode\":\"auto\",\"orientation\":\"horizontal\",\"reduceOptions\":{\"calcs\":[\"lastNotNull\"],\"fields\":\"\",\"values\":false},\"textMode\":\"auto\"},\"mappingType\":1,\"mappingTypes\":[{\"name\":\"value - to text\",\"value\":1},{\"name\":\"range to text\",\"value\":2}],\"maxDataPoints\":100,\"nullPointMode\":\"connected\",\"nullText\":null,\"postfix\":\"\",\"postfixFontSize\":\"50%\",\"prefix\":\"\",\"prefixFontSize\":\"50%\",\"rangeMaps\":[{\"from\":\"null\",\"text\":\"N/A\",\"to\":\"null\"}],\"sparkline\":{\"fillColor\":\"rgba(31, - 118, 189, 0.18)\",\"full\":true,\"lineColor\":\"rgb(31, 120, 193)\",\"show\":true},\"tableColumn\":\"\",\"targets\":[{\"expr\":\"sum(irate(istio_tcp_received_bytes_total{reporter=~\\\"$qrep\\\", - destination_service=~\\\"$service\\\"}[1m]))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"\",\"refId\":\"A\"}],\"thresholds\":\"\",\"title\":\"TCP - Received Bytes\",\"type\":\"singlestat\",\"valueFontSize\":\"80%\",\"valueMaps\":[{\"op\":\"=\",\"text\":\"N/A\",\"value\":\"null\"}],\"valueName\":\"avg\"},{\"cacheTimeout\":null,\"colorBackground\":false,\"colorValue\":false,\"colors\":[\"rgba(245, - 54, 54, 0.9)\",\"rgba(237, 129, 40, 0.89)\",\"rgba(50, 172, 45, 0.97)\"],\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"format\":\"ops\",\"gauge\":{\"maxValue\":100,\"minValue\":0,\"show\":false,\"thresholdLabels\":false,\"thresholdMarkers\":true},\"gridPos\":{\"h\":4,\"w\":6,\"x\":0,\"y\":8},\"id\":97,\"interval\":null,\"links\":[],\"options\":{\"colorMode\":\"value\",\"graphMode\":\"area\",\"justifyMode\":\"auto\",\"orientation\":\"horizontal\",\"reduceOptions\":{\"calcs\":[\"lastNotNull\"],\"fields\":\"\",\"values\":false},\"textMode\":\"auto\"},\"mappingType\":1,\"mappingTypes\":[{\"name\":\"value - to text\",\"value\":1},{\"name\":\"range to text\",\"value\":2}],\"maxDataPoints\":100,\"nullPointMode\":\"connected\",\"nullText\":null,\"postfix\":\"\",\"postfixFontSize\":\"50%\",\"prefix\":\"\",\"prefixFontSize\":\"50%\",\"rangeMaps\":[{\"from\":\"null\",\"text\":\"N/A\",\"to\":\"null\"}],\"sparkline\":{\"fillColor\":\"rgba(31, - 118, 189, 0.18)\",\"full\":true,\"lineColor\":\"rgb(31, 120, 193)\",\"show\":true},\"tableColumn\":\"\",\"targets\":[{\"expr\":\"round(sum(irate(istio_requests_total{reporter=\\\"destination\\\",destination_service=~\\\"$service\\\"}[5m])), - 0.001)\",\"format\":\"time_series\",\"intervalFactor\":1,\"refId\":\"A\",\"step\":4}],\"thresholds\":\"\",\"title\":\"Server - Request Volume\",\"type\":\"singlestat\",\"valueFontSize\":\"80%\",\"valueMaps\":[{\"op\":\"=\",\"text\":\"N/A\",\"value\":\"null\"}],\"valueName\":\"current\"},{\"cacheTimeout\":null,\"colorBackground\":false,\"colorValue\":false,\"colors\":[\"rgba(50, - 172, 45, 0.97)\",\"rgba(237, 129, 40, 0.89)\",\"rgba(245, 54, 54, 0.9)\"],\"datasource\":\"Prometheus\",\"decimals\":null,\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"format\":\"percentunit\",\"gauge\":{\"maxValue\":100,\"minValue\":80,\"show\":false,\"thresholdLabels\":false,\"thresholdMarkers\":false},\"gridPos\":{\"h\":4,\"w\":6,\"x\":6,\"y\":8},\"id\":98,\"interval\":null,\"links\":[],\"options\":{\"colorMode\":\"value\",\"graphMode\":\"area\",\"justifyMode\":\"auto\",\"orientation\":\"horizontal\",\"reduceOptions\":{\"calcs\":[\"lastNotNull\"],\"fields\":\"\",\"values\":false},\"textMode\":\"auto\"},\"mappingType\":1,\"mappingTypes\":[{\"name\":\"value - to text\",\"value\":1},{\"name\":\"range to text\",\"value\":2}],\"maxDataPoints\":100,\"nullPointMode\":\"connected\",\"nullText\":null,\"postfix\":\"\",\"postfixFontSize\":\"50%\",\"prefix\":\"\",\"prefixFontSize\":\"50%\",\"rangeMaps\":[{\"from\":\"null\",\"text\":\"N/A\",\"to\":\"null\"}],\"sparkline\":{\"fillColor\":\"rgba(31, - 118, 189, 0.18)\",\"full\":true,\"lineColor\":\"rgb(31, 120, 193)\",\"show\":true},\"tableColumn\":\"\",\"targets\":[{\"expr\":\"sum(irate(istio_requests_total{reporter=\\\"destination\\\",destination_service=~\\\"$service\\\",response_code!~\\\"5.*\\\"}[5m])) - / sum(irate(istio_requests_total{reporter=\\\"destination\\\",destination_service=~\\\"$service\\\"}[5m]))\",\"format\":\"time_series\",\"intervalFactor\":1,\"refId\":\"A\"}],\"thresholds\":\"95, - 99, 99.5\",\"title\":\"Server Success Rate (non-5xx responses)\",\"type\":\"singlestat\",\"valueFontSize\":\"80%\",\"valueMaps\":[{\"op\":\"=\",\"text\":\"N/A\",\"value\":\"null\"}],\"valueName\":\"avg\"},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":4,\"w\":6,\"x\":12,\"y\":8},\"hiddenSeries\":false,\"id\":99,\"legend\":{\"alignAsTable\":false,\"avg\":false,\"current\":false,\"hideEmpty\":false,\"hideZero\":false,\"max\":false,\"min\":false,\"rightSide\":true,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"(histogram_quantile(0.50, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=\\\"destination\\\",destination_service=~\\\"$service\\\"}[1m])) - by (le)) / 1000) or histogram_quantile(0.50, sum(irate(istio_request_duration_seconds_bucket{reporter=\\\"destination\\\",destination_service=~\\\"$service\\\"}[1m])) - by (le))\",\"format\":\"time_series\",\"interval\":\"\",\"intervalFactor\":1,\"legendFormat\":\"P50\",\"refId\":\"A\"},{\"expr\":\"(histogram_quantile(0.90, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=\\\"destination\\\",destination_service=~\\\"$service\\\"}[1m])) - by (le)) / 1000) or histogram_quantile(0.90, sum(irate(istio_request_duration_seconds_bucket{reporter=\\\"destination\\\",destination_service=~\\\"$service\\\"}[1m])) - by (le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"P90\",\"refId\":\"B\"},{\"expr\":\"(histogram_quantile(0.99, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=\\\"destination\\\",destination_service=~\\\"$service\\\"}[1m])) - by (le)) / 1000) or histogram_quantile(0.99, sum(irate(istio_request_duration_seconds_bucket{reporter=\\\"destination\\\",destination_service=~\\\"$service\\\"}[1m])) - by (le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"P99\",\"refId\":\"C\"}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Server - Request Duration\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"s\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"cacheTimeout\":null,\"colorBackground\":false,\"colorValue\":false,\"colors\":[\"#299c46\",\"rgba(237, - 129, 40, 0.89)\",\"#d44a3a\"],\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"format\":\"Bps\",\"gauge\":{\"maxValue\":100,\"minValue\":0,\"show\":false,\"thresholdLabels\":false,\"thresholdMarkers\":true},\"gridPos\":{\"h\":4,\"w\":6,\"x\":18,\"y\":8},\"id\":100,\"interval\":null,\"links\":[],\"options\":{\"colorMode\":\"value\",\"graphMode\":\"area\",\"justifyMode\":\"auto\",\"orientation\":\"horizontal\",\"reduceOptions\":{\"calcs\":[\"lastNotNull\"],\"fields\":\"\",\"values\":false},\"textMode\":\"auto\"},\"mappingType\":1,\"mappingTypes\":[{\"name\":\"value - to text\",\"value\":1},{\"name\":\"range to text\",\"value\":2}],\"maxDataPoints\":100,\"nullPointMode\":\"connected\",\"nullText\":null,\"postfix\":\"\",\"postfixFontSize\":\"50%\",\"prefix\":\"\",\"prefixFontSize\":\"50%\",\"rangeMaps\":[{\"from\":\"null\",\"text\":\"N/A\",\"to\":\"null\"}],\"sparkline\":{\"fillColor\":\"rgba(31, - 118, 189, 0.18)\",\"full\":true,\"lineColor\":\"rgb(31, 120, 193)\",\"show\":true},\"tableColumn\":\"\",\"targets\":[{\"expr\":\"sum(irate(istio_tcp_sent_bytes_total{reporter=~\\\"$qrep\\\", - destination_service=~\\\"$service\\\"}[1m]))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"\",\"refId\":\"A\"}],\"thresholds\":\"\",\"title\":\"TCP - Sent Bytes\",\"type\":\"singlestat\",\"valueFontSize\":\"80%\",\"valueMaps\":[{\"op\":\"=\",\"text\":\"N/A\",\"value\":\"null\"}],\"valueName\":\"avg\"}],\"title\":\"General\",\"type\":\"row\"},{\"collapsed\":true,\"gridPos\":{\"h\":1,\"w\":24,\"x\":0,\"y\":1},\"id\":104,\"panels\":[{\"content\":\"
\\nCLIENT WORKLOADS\\n
\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"gridPos\":{\"h\":3,\"w\":24,\"x\":0,\"y\":2},\"id\":45,\"links\":[],\"mode\":\"html\",\"options\":{\"content\":\"
\\nCLIENT WORKLOADS\\n
\",\"mode\":\"html\"},\"pluginVersion\":\"7.1.0\",\"title\":\"\",\"transparent\":true,\"type\":\"text\"},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":0,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":12,\"x\":0,\"y\":5},\"hiddenSeries\":false,\"id\":25,\"legend\":{\"avg\":false,\"current\":false,\"hideEmpty\":true,\"max\":false,\"min\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null - as zero\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"round(sum(irate(istio_requests_total{connection_security_policy=\\\"mutual_tls\\\",destination_service=~\\\"$service\\\",reporter=~\\\"$qrep\\\",source_workload=~\\\"$srcwl\\\",source_workload_namespace=~\\\"$srcns\\\"}[5m])) - by (source_workload, source_workload_namespace, response_code), 0.001)\",\"format\":\"time_series\",\"intervalFactor\":1,\"legendFormat\":\"{{ - source_workload }}.{{ source_workload_namespace }} : {{ response_code }} (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"round(sum(irate(istio_requests_total{connection_security_policy!=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", reporter=~\\\"$qrep\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[5m])) by (source_workload, source_workload_namespace, - response_code), 0.001)\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - source_workload }}.{{ source_workload_namespace }} : {{ response_code }}\",\"refId\":\"B\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Incoming - Requests By Source And Response Code\",\"tooltip\":{\"shared\":false,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[\"total\"]},\"yaxes\":[{\"format\":\"ops\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":12,\"x\":12,\"y\":5},\"hiddenSeries\":false,\"id\":26,\"legend\":{\"avg\":false,\"current\":false,\"hideEmpty\":true,\"hideZero\":false,\"max\":false,\"min\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"sum(irate(istio_requests_total{reporter=~\\\"$qrep\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\",response_code!~\\\"5.*\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[5m])) - by (source_workload, source_workload_namespace) / sum(irate(istio_requests_total{reporter=~\\\"$qrep\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[5m])) - by (source_workload, source_workload_namespace)\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - source_workload }}.{{ source_workload_namespace }} (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"sum(irate(istio_requests_total{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\",response_code!~\\\"5.*\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[5m])) - by (source_workload, source_workload_namespace) / sum(irate(istio_requests_total{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[5m])) - by (source_workload, source_workload_namespace)\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - source_workload }}.{{ source_workload_namespace }}\",\"refId\":\"B\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Incoming - Success Rate (non-5xx responses) By Source\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"percentunit\",\"label\":null,\"logBase\":1,\"max\":\"1.01\",\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"description\":\"\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":8,\"x\":0,\"y\":11},\"hiddenSeries\":false,\"id\":27,\"legend\":{\"alignAsTable\":false,\"avg\":false,\"current\":false,\"hideEmpty\":true,\"hideZero\":false,\"max\":false,\"min\":false,\"rightSide\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"(histogram_quantile(0.50, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.50, - sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P50 (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"(histogram_quantile(0.90, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.90, - sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P90 (\U0001F510mTLS)\",\"refId\":\"B\",\"step\":2},{\"expr\":\"(histogram_quantile(0.95, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.95, - sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P95 (\U0001F510mTLS)\",\"refId\":\"C\",\"step\":2},{\"expr\":\"(histogram_quantile(0.99, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.99, - sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P99 (\U0001F510mTLS)\",\"refId\":\"D\",\"step\":2},{\"expr\":\"(histogram_quantile(0.50, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.50, - sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\", connection_security_policy!=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P50\",\"refId\":\"E\",\"step\":2},{\"expr\":\"(histogram_quantile(0.90, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.90, - sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\", connection_security_policy!=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P90\",\"refId\":\"F\",\"step\":2},{\"expr\":\"(histogram_quantile(0.95, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.95, - sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\", connection_security_policy!=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P95\",\"refId\":\"G\",\"step\":2},{\"expr\":\"(histogram_quantile(0.99, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le)) / 1000) or histogram_quantile(0.99, - sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\", connection_security_policy!=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P99\",\"refId\":\"H\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Incoming - Request Duration By Source\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"s\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":8,\"x\":8,\"y\":11},\"hiddenSeries\":false,\"id\":28,\"legend\":{\"alignAsTable\":false,\"avg\":false,\"current\":false,\"hideEmpty\":true,\"max\":false,\"min\":false,\"rightSide\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"histogram_quantile(0.50, - sum(irate(istio_request_bytes_bucket{reporter=~\\\"$qrep\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P50 (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"histogram_quantile(0.90, - sum(irate(istio_request_bytes_bucket{reporter=~\\\"$qrep\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - \ P90 (\U0001F510mTLS)\",\"refId\":\"B\",\"step\":2},{\"expr\":\"histogram_quantile(0.95, - sum(irate(istio_request_bytes_bucket{reporter=~\\\"$qrep\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P95 (\U0001F510mTLS)\",\"refId\":\"C\",\"step\":2},{\"expr\":\"histogram_quantile(0.99, - sum(irate(istio_request_bytes_bucket{reporter=~\\\"$qrep\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - \ P99 (\U0001F510mTLS)\",\"refId\":\"D\",\"step\":2},{\"expr\":\"histogram_quantile(0.50, - sum(irate(istio_request_bytes_bucket{reporter=~\\\"$qrep\\\", connection_security_policy!=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P50\",\"refId\":\"E\",\"step\":2},{\"expr\":\"histogram_quantile(0.90, sum(irate(istio_request_bytes_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P90\",\"refId\":\"F\",\"step\":2},{\"expr\":\"histogram_quantile(0.95, sum(irate(istio_request_bytes_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P95\",\"refId\":\"G\",\"step\":2},{\"expr\":\"histogram_quantile(0.99, sum(irate(istio_request_bytes_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P99\",\"refId\":\"H\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Incoming - Request Size By Source\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"decbytes\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":8,\"x\":16,\"y\":11},\"hiddenSeries\":false,\"id\":68,\"legend\":{\"alignAsTable\":false,\"avg\":false,\"current\":false,\"hideEmpty\":true,\"max\":false,\"min\":false,\"rightSide\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"histogram_quantile(0.50, - sum(irate(istio_response_bytes_bucket{reporter=~\\\"$qrep\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P50 (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"histogram_quantile(0.90, - sum(irate(istio_response_bytes_bucket{reporter=~\\\"$qrep\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - \ P90 (\U0001F510mTLS)\",\"refId\":\"B\",\"step\":2},{\"expr\":\"histogram_quantile(0.95, - sum(irate(istio_response_bytes_bucket{reporter=~\\\"$qrep\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P95 (\U0001F510mTLS)\",\"refId\":\"C\",\"step\":2},{\"expr\":\"histogram_quantile(0.99, - sum(irate(istio_response_bytes_bucket{reporter=~\\\"$qrep\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - \ P99 (\U0001F510mTLS)\",\"refId\":\"D\",\"step\":2},{\"expr\":\"histogram_quantile(0.50, - sum(irate(istio_response_bytes_bucket{reporter=~\\\"$qrep\\\", connection_security_policy!=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P50\",\"refId\":\"E\",\"step\":2},{\"expr\":\"histogram_quantile(0.90, sum(irate(istio_response_bytes_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P90\",\"refId\":\"F\",\"step\":2},{\"expr\":\"histogram_quantile(0.95, sum(irate(istio_response_bytes_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P95\",\"refId\":\"G\",\"step\":2},{\"expr\":\"histogram_quantile(0.99, sum(irate(istio_response_bytes_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P99\",\"refId\":\"H\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Response - Size By Source\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"decbytes\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":12,\"x\":0,\"y\":17},\"hiddenSeries\":false,\"id\":80,\"legend\":{\"avg\":false,\"current\":false,\"max\":false,\"min\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"round(sum(irate(istio_tcp_received_bytes_total{reporter=~\\\"$qrep\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace), 0.001)\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - source_workload }}.{{ source_workload_namespace}} (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"round(sum(irate(istio_tcp_received_bytes_total{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace), 0.001)\",\"format\":\"time_series\",\"intervalFactor\":1,\"legendFormat\":\"{{ - source_workload }}.{{ source_workload_namespace}}\",\"refId\":\"B\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Bytes - Received from Incoming TCP Connection\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"Bps\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":true}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":12,\"x\":12,\"y\":17},\"hiddenSeries\":false,\"id\":82,\"legend\":{\"avg\":false,\"current\":false,\"max\":false,\"min\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"round(sum(irate(istio_tcp_sent_bytes_total{connection_security_policy=\\\"mutual_tls\\\", - reporter=~\\\"$qrep\\\", destination_service=~\\\"$service\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace), - 0.001)\",\"format\":\"time_series\",\"intervalFactor\":1,\"legendFormat\":\"{{ - source_workload }}.{{ source_workload_namespace}} (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"round(sum(irate(istio_tcp_sent_bytes_total{connection_security_policy!=\\\"mutual_tls\\\", - reporter=~\\\"$qrep\\\", destination_service=~\\\"$service\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace), - 0.001)\",\"format\":\"time_series\",\"intervalFactor\":1,\"legendFormat\":\"{{ - source_workload }}.{{ source_workload_namespace}}\",\"refId\":\"B\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Bytes - Sent to Incoming TCP Connection\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"Bps\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":true}],\"yaxis\":{\"align\":false,\"alignLevel\":null}}],\"title\":\"Client - Workloads\",\"type\":\"row\"},{\"collapsed\":true,\"gridPos\":{\"h\":1,\"w\":24,\"x\":0,\"y\":2},\"id\":102,\"panels\":[{\"content\":\"
\\nSERVICE WORKLOADS\\n
\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"gridPos\":{\"h\":3,\"w\":24,\"x\":0,\"y\":3},\"id\":69,\"links\":[],\"mode\":\"html\",\"options\":{\"content\":\"
\\nSERVICE WORKLOADS\\n
\",\"mode\":\"html\"},\"pluginVersion\":\"7.1.0\",\"title\":\"\",\"transparent\":true,\"type\":\"text\"},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":0,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":12,\"x\":0,\"y\":6},\"hiddenSeries\":false,\"id\":90,\"legend\":{\"avg\":false,\"current\":false,\"hideEmpty\":true,\"max\":false,\"min\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null - as zero\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"round(sum(irate(istio_requests_total{connection_security_policy=\\\"mutual_tls\\\",destination_service=~\\\"$service\\\",reporter=\\\"destination\\\",destination_workload=~\\\"$dstwl\\\",destination_workload_namespace=~\\\"$dstns\\\"}[5m])) - by (destination_workload, destination_workload_namespace, response_code), 0.001)\",\"format\":\"time_series\",\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} : {{ response_code - }} (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"round(sum(irate(istio_requests_total{connection_security_policy!=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", reporter=\\\"destination\\\", destination_workload=~\\\"$dstwl\\\", - destination_workload_namespace=~\\\"$dstns\\\"}[5m])) by (destination_workload, - destination_workload_namespace, response_code), 0.001)\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} : {{ response_code - }}\",\"refId\":\"B\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Incoming - Requests By Destination Workload And Response Code\",\"tooltip\":{\"shared\":false,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[\"total\"]},\"yaxes\":[{\"format\":\"ops\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":12,\"x\":12,\"y\":6},\"hiddenSeries\":false,\"id\":91,\"legend\":{\"avg\":false,\"current\":false,\"hideEmpty\":true,\"hideZero\":false,\"max\":false,\"min\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"sum(irate(istio_requests_total{reporter=\\\"destination\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\",response_code!~\\\"5.*\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[5m])) - by (destination_workload, destination_workload_namespace) / sum(irate(istio_requests_total{reporter=\\\"destination\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[5m])) - by (destination_workload, destination_workload_namespace)\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"sum(irate(istio_requests_total{reporter=\\\"destination\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\",response_code!~\\\"5.*\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[5m])) - by (destination_workload, destination_workload_namespace) / sum(irate(istio_requests_total{reporter=\\\"destination\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[5m])) - by (destination_workload, destination_workload_namespace)\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }}\",\"refId\":\"B\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Incoming - Success Rate (non-5xx responses) By Destination Workload\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"percentunit\",\"label\":null,\"logBase\":1,\"max\":\"1.01\",\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"description\":\"\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":8,\"x\":0,\"y\":12},\"hiddenSeries\":false,\"id\":94,\"legend\":{\"alignAsTable\":false,\"avg\":false,\"current\":false,\"hideEmpty\":true,\"hideZero\":false,\"max\":false,\"min\":false,\"rightSide\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"(histogram_quantile(0.50, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=\\\"destination\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le)) / 1000) or histogram_quantile(0.50, - sum(irate(istio_request_duration_seconds_bucket{reporter=\\\"destination\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P50 (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"(histogram_quantile(0.90, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=\\\"destination\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le)) / 1000) or histogram_quantile(0.90, - sum(irate(istio_request_duration_seconds_bucket{reporter=\\\"destination\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P90 (\U0001F510mTLS)\",\"refId\":\"B\",\"step\":2},{\"expr\":\"(histogram_quantile(0.95, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=\\\"destination\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le)) / 1000) or histogram_quantile(0.95, - sum(irate(istio_request_duration_seconds_bucket{reporter=\\\"destination\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P95 (\U0001F510mTLS)\",\"refId\":\"C\",\"step\":2},{\"expr\":\"(histogram_quantile(0.99, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=\\\"destination\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le)) / 1000) or histogram_quantile(0.99, - sum(irate(istio_request_duration_seconds_bucket{reporter=\\\"destination\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P99 (\U0001F510mTLS)\",\"refId\":\"D\",\"step\":2},{\"expr\":\"(histogram_quantile(0.50, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=\\\"destination\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le)) / 1000) or histogram_quantile(0.50, - sum(irate(istio_request_duration_seconds_bucket{reporter=\\\"destination\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P50\",\"refId\":\"E\",\"step\":2},{\"expr\":\"(histogram_quantile(0.90, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=\\\"destination\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le)) / 1000) or histogram_quantile(0.90, - sum(irate(istio_request_duration_seconds_bucket{reporter=\\\"destination\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P90\",\"refId\":\"F\",\"step\":2},{\"expr\":\"(histogram_quantile(0.95, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=\\\"destination\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le)) / 1000) or histogram_quantile(0.95, - sum(irate(istio_request_duration_seconds_bucket{reporter=\\\"destination\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P95\",\"refId\":\"G\",\"step\":2},{\"expr\":\"(histogram_quantile(0.99, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=\\\"destination\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le)) / 1000) or histogram_quantile(0.99, - sum(irate(istio_request_duration_seconds_bucket{reporter=\\\"destination\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P99\",\"refId\":\"H\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Incoming - Request Duration By Service Workload\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"s\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":8,\"x\":8,\"y\":12},\"hiddenSeries\":false,\"id\":95,\"legend\":{\"alignAsTable\":false,\"avg\":false,\"current\":false,\"hideEmpty\":true,\"max\":false,\"min\":false,\"rightSide\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"histogram_quantile(0.50, - sum(irate(istio_request_bytes_bucket{reporter=\\\"destination\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P50 (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"histogram_quantile(0.90, - sum(irate(istio_request_bytes_bucket{reporter=\\\"destination\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P90 (\U0001F510mTLS)\",\"refId\":\"B\",\"step\":2},{\"expr\":\"histogram_quantile(0.95, - sum(irate(istio_request_bytes_bucket{reporter=\\\"destination\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P95 (\U0001F510mTLS)\",\"refId\":\"C\",\"step\":2},{\"expr\":\"histogram_quantile(0.99, - sum(irate(istio_request_bytes_bucket{reporter=\\\"destination\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P99 (\U0001F510mTLS)\",\"refId\":\"D\",\"step\":2},{\"expr\":\"histogram_quantile(0.50, - sum(irate(istio_request_bytes_bucket{reporter=\\\"destination\\\", connection_security_policy!=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P50\",\"refId\":\"E\",\"step\":2},{\"expr\":\"histogram_quantile(0.90, - sum(irate(istio_request_bytes_bucket{reporter=\\\"destination\\\", connection_security_policy!=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P90\",\"refId\":\"F\",\"step\":2},{\"expr\":\"histogram_quantile(0.95, - sum(irate(istio_request_bytes_bucket{reporter=\\\"destination\\\", connection_security_policy!=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P95\",\"refId\":\"G\",\"step\":2},{\"expr\":\"histogram_quantile(0.99, - sum(irate(istio_request_bytes_bucket{reporter=\\\"destination\\\", connection_security_policy!=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P99\",\"refId\":\"H\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Incoming - Request Size By Service Workload\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"decbytes\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":8,\"x\":16,\"y\":12},\"hiddenSeries\":false,\"id\":96,\"legend\":{\"alignAsTable\":false,\"avg\":false,\"current\":false,\"hideEmpty\":true,\"max\":false,\"min\":false,\"rightSide\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"histogram_quantile(0.50, - sum(irate(istio_response_bytes_bucket{reporter=\\\"destination\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P50 (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"histogram_quantile(0.90, - sum(irate(istio_response_bytes_bucket{reporter=\\\"destination\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P90 (\U0001F510mTLS)\",\"refId\":\"B\",\"step\":2},{\"expr\":\"histogram_quantile(0.95, - sum(irate(istio_response_bytes_bucket{reporter=\\\"destination\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P95 (\U0001F510mTLS)\",\"refId\":\"C\",\"step\":2},{\"expr\":\"histogram_quantile(0.99, - sum(irate(istio_response_bytes_bucket{reporter=\\\"destination\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P99 (\U0001F510mTLS)\",\"refId\":\"D\",\"step\":2},{\"expr\":\"histogram_quantile(0.50, - sum(irate(istio_response_bytes_bucket{reporter=\\\"destination\\\", connection_security_policy!=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P50\",\"refId\":\"E\",\"step\":2},{\"expr\":\"histogram_quantile(0.90, - sum(irate(istio_response_bytes_bucket{reporter=\\\"destination\\\", connection_security_policy!=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P90\",\"refId\":\"F\",\"step\":2},{\"expr\":\"histogram_quantile(0.95, - sum(irate(istio_response_bytes_bucket{reporter=\\\"destination\\\", connection_security_policy!=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P95\",\"refId\":\"G\",\"step\":2},{\"expr\":\"histogram_quantile(0.99, - sum(irate(istio_response_bytes_bucket{reporter=\\\"destination\\\", connection_security_policy!=\\\"mutual_tls\\\", - destination_service=~\\\"$service\\\", destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace }} P99\",\"refId\":\"H\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Response - Size By Service Workload\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"decbytes\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":12,\"x\":0,\"y\":18},\"hiddenSeries\":false,\"id\":92,\"legend\":{\"avg\":false,\"current\":false,\"max\":false,\"min\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"round(sum(irate(istio_tcp_received_bytes_total{reporter=\\\"destination\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace), 0.001)\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace}} (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"round(sum(irate(istio_tcp_received_bytes_total{reporter=\\\"destination\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_service=~\\\"$service\\\", - destination_workload=~\\\"$dstwl\\\", destination_workload_namespace=~\\\"$dstns\\\"}[1m])) - by (destination_workload, destination_workload_namespace), 0.001)\",\"format\":\"time_series\",\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{ destination_workload_namespace}}\",\"refId\":\"B\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Bytes - Received from Incoming TCP Connection\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"Bps\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":true}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":12,\"x\":12,\"y\":18},\"hiddenSeries\":false,\"id\":93,\"legend\":{\"avg\":false,\"current\":false,\"max\":false,\"min\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"round(sum(irate(istio_tcp_sent_bytes_total{connection_security_policy=\\\"mutual_tls\\\", - reporter=\\\"destination\\\", destination_service=~\\\"$service\\\", destination_workload=~\\\"$dstwl\\\", - destination_workload_namespace=~\\\"$dstns\\\"}[1m])) by (destination_workload, - destination_workload_namespace), 0.001)\",\"format\":\"time_series\",\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{destination_workload_namespace }} (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"round(sum(irate(istio_tcp_sent_bytes_total{connection_security_policy!=\\\"mutual_tls\\\", - reporter=\\\"destination\\\", destination_service=~\\\"$service\\\", destination_workload=~\\\"$dstwl\\\", - destination_workload_namespace=~\\\"$dstns\\\"}[1m])) by (destination_workload, - destination_workload_namespace), 0.001)\",\"format\":\"time_series\",\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_workload }}.{{destination_workload_namespace }}\",\"refId\":\"B\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Bytes - Sent to Incoming TCP Connection\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"Bps\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":true}],\"yaxis\":{\"align\":false,\"alignLevel\":null}}],\"title\":\"Service - Workloads\",\"type\":\"row\"}],\"refresh\":\"1m\",\"schemaVersion\":26,\"style\":\"dark\",\"tags\":[],\"templating\":{\"list\":[{\"current\":{\"selected\":true,\"text\":\"default\",\"value\":\"default\"},\"hide\":0,\"includeAll\":false,\"label\":null,\"multi\":false,\"name\":\"datasource\",\"options\":[],\"query\":\"prometheus\",\"queryValue\":\"\",\"refresh\":1,\"regex\":\"\",\"skipUrlSync\":false,\"type\":\"datasource\"},{\"allValue\":null,\"current\":{},\"datasource\":\"Prometheus\",\"definition\":\"\",\"hide\":0,\"includeAll\":false,\"label\":\"Service\",\"multi\":false,\"name\":\"service\",\"options\":[],\"query\":\"label_values(destination_service)\",\"refresh\":1,\"regex\":\"\",\"skipUrlSync\":false,\"sort\":0,\"tagValuesQuery\":\"\",\"tags\":[],\"tagsQuery\":\"\",\"type\":\"query\",\"useTags\":false},{\"allValue\":null,\"current\":{\"selected\":true,\"text\":\"destination\",\"value\":\"destination\"},\"datasource\":\"Prometheus\",\"definition\":\"\",\"hide\":0,\"includeAll\":false,\"label\":\"Reporter\",\"multi\":true,\"name\":\"qrep\",\"options\":[],\"query\":\"label_values(reporter)\",\"refresh\":1,\"regex\":\"\",\"skipUrlSync\":false,\"sort\":2,\"tagValuesQuery\":\"\",\"tags\":[],\"tagsQuery\":\"\",\"type\":\"query\",\"useTags\":false},{\"allValue\":null,\"current\":{},\"datasource\":\"Prometheus\",\"definition\":\"\",\"hide\":0,\"includeAll\":true,\"label\":\"Client - Workload Namespace\",\"multi\":true,\"name\":\"srcns\",\"options\":[],\"query\":\"query_result(sum(istio_requests_total{reporter=~\\\"$qrep\\\", - destination_service=\\\"$service\\\"}) by (source_workload_namespace) or sum(istio_tcp_sent_bytes_total{reporter=~\\\"$qrep\\\", - destination_service=~\\\"$service\\\"}) by (source_workload_namespace))\",\"refresh\":1,\"regex\":\"/.*namespace=\\\"([^\\\"]*).*/\",\"skipUrlSync\":false,\"sort\":2,\"tagValuesQuery\":\"\",\"tags\":[],\"tagsQuery\":\"\",\"type\":\"query\",\"useTags\":false},{\"allValue\":null,\"current\":{},\"datasource\":\"Prometheus\",\"definition\":\"\",\"hide\":0,\"includeAll\":true,\"label\":\"Client - Workload\",\"multi\":true,\"name\":\"srcwl\",\"options\":[],\"query\":\"query_result(sum(istio_requests_total{reporter=~\\\"$qrep\\\", - destination_service=~\\\"$service\\\", source_workload_namespace=~\\\"$srcns\\\"}) - by (source_workload) or sum(istio_tcp_sent_bytes_total{reporter=~\\\"$qrep\\\", - destination_service=~\\\"$service\\\", source_workload_namespace=~\\\"$srcns\\\"}) - by (source_workload))\",\"refresh\":1,\"regex\":\"/.*workload=\\\"([^\\\"]*).*/\",\"skipUrlSync\":false,\"sort\":3,\"tagValuesQuery\":\"\",\"tags\":[],\"tagsQuery\":\"\",\"type\":\"query\",\"useTags\":false},{\"allValue\":null,\"current\":{},\"datasource\":\"Prometheus\",\"definition\":\"\",\"hide\":0,\"includeAll\":true,\"label\":\"Service - Workload Namespace\",\"multi\":true,\"name\":\"dstns\",\"options\":[],\"query\":\"query_result(sum(istio_requests_total{reporter=\\\"destination\\\", - destination_service=\\\"$service\\\"}) by (destination_workload_namespace) or - sum(istio_tcp_sent_bytes_total{reporter=\\\"destination\\\", destination_service=~\\\"$service\\\"}) - by (destination_workload_namespace))\",\"refresh\":1,\"regex\":\"/.*namespace=\\\"([^\\\"]*).*/\",\"skipUrlSync\":false,\"sort\":2,\"tagValuesQuery\":\"\",\"tags\":[],\"tagsQuery\":\"\",\"type\":\"query\",\"useTags\":false},{\"allValue\":null,\"current\":{},\"datasource\":\"Prometheus\",\"definition\":\"\",\"hide\":0,\"includeAll\":true,\"label\":\"Service - Workload\",\"multi\":true,\"name\":\"dstwl\",\"options\":[],\"query\":\"query_result( - sum(istio_requests_total{reporter=\\\"destination\\\", destination_service=~\\\"$service\\\", - destination_workload_namespace=~\\\"$dstns\\\"}) by (destination_workload) or - sum(istio_tcp_sent_bytes_total{reporter=\\\"destination\\\", destination_service=~\\\"$service\\\", - destination_workload_namespace=~\\\"$dstns\\\"}) by (destination_workload))\",\"refresh\":1,\"regex\":\"/.*workload=\\\"([^\\\"]*).*/\",\"skipUrlSync\":false,\"sort\":3,\"tagValuesQuery\":\"\",\"tags\":[],\"tagsQuery\":\"\",\"type\":\"query\",\"useTags\":false}]},\"time\":{\"from\":\"now-5m\",\"to\":\"now\"},\"timepicker\":{\"refresh_intervals\":[\"5m\",\"15m\",\"30m\",\"1h\",\"2h\",\"1d\"],\"time_options\":[\"5m\",\"15m\",\"1h\",\"6h\",\"12h\",\"24h\",\"2d\",\"7d\",\"30d\"]},\"timezone\":\"\",\"title\":\"Istio - Service Dashboard\",\"uid\":\"LJ_uJAvmk\",\"version\":1}\n" - istio-workload-dashboard.json: "{\"annotations\":{\"list\":[{\"builtIn\":1,\"datasource\":\"-- - Grafana --\",\"enable\":true,\"hide\":true,\"iconColor\":\"rgba(0, 211, 255, 1)\",\"name\":\"Annotations - & Alerts\",\"type\":\"dashboard\"}]},\"editable\":false,\"gnetId\":null,\"graphTooltip\":0,\"iteration\":1531345461465,\"links\":[],\"panels\":[{\"collapsed\":true,\"gridPos\":{\"h\":1,\"w\":24,\"x\":0,\"y\":0},\"id\":95,\"panels\":[{\"content\":\"
\\nWORKLOAD: $workload.$namespace\\n
\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"gridPos\":{\"h\":3,\"w\":24,\"x\":0,\"y\":1},\"id\":89,\"links\":[],\"mode\":\"html\",\"options\":{\"content\":\"
\\nWORKLOAD: $workload.$namespace\\n
\",\"mode\":\"html\"},\"pluginVersion\":\"7.1.0\",\"title\":\"\",\"transparent\":true,\"type\":\"text\"},{\"cacheTimeout\":null,\"colorBackground\":false,\"colorValue\":false,\"colors\":[\"rgba(245, - 54, 54, 0.9)\",\"rgba(237, 129, 40, 0.89)\",\"rgba(50, 172, 45, 0.97)\"],\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"format\":\"ops\",\"gauge\":{\"maxValue\":100,\"minValue\":0,\"show\":false,\"thresholdLabels\":false,\"thresholdMarkers\":true},\"gridPos\":{\"h\":4,\"w\":8,\"x\":0,\"y\":4},\"id\":12,\"interval\":null,\"links\":[],\"options\":{\"colorMode\":\"value\",\"graphMode\":\"area\",\"justifyMode\":\"auto\",\"orientation\":\"horizontal\",\"reduceOptions\":{\"calcs\":[\"lastNotNull\"],\"fields\":\"\",\"values\":false},\"textMode\":\"auto\"},\"mappingType\":1,\"mappingTypes\":[{\"name\":\"value - to text\",\"value\":1},{\"name\":\"range to text\",\"value\":2}],\"maxDataPoints\":100,\"nullPointMode\":\"connected\",\"nullText\":null,\"postfix\":\"\",\"postfixFontSize\":\"50%\",\"prefix\":\"\",\"prefixFontSize\":\"50%\",\"rangeMaps\":[{\"from\":\"null\",\"text\":\"N/A\",\"to\":\"null\"}],\"sparkline\":{\"fillColor\":\"rgba(31, - 118, 189, 0.18)\",\"full\":true,\"lineColor\":\"rgb(31, 120, 193)\",\"show\":true},\"tableColumn\":\"\",\"targets\":[{\"expr\":\"round(sum(irate(istio_requests_total{reporter=~\\\"$qrep\\\",destination_workload_namespace=~\\\"$namespace\\\",destination_workload=~\\\"$workload\\\"}[5m])), - 0.001)\",\"format\":\"time_series\",\"intervalFactor\":1,\"refId\":\"A\",\"step\":4}],\"thresholds\":\"\",\"title\":\"Incoming - Request Volume\",\"type\":\"singlestat\",\"valueFontSize\":\"80%\",\"valueMaps\":[{\"op\":\"=\",\"text\":\"N/A\",\"value\":\"null\"}],\"valueName\":\"current\"},{\"cacheTimeout\":null,\"colorBackground\":false,\"colorValue\":false,\"colors\":[\"rgba(50, - 172, 45, 0.97)\",\"rgba(237, 129, 40, 0.89)\",\"rgba(245, 54, 54, 0.9)\"],\"datasource\":\"Prometheus\",\"decimals\":null,\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"format\":\"percentunit\",\"gauge\":{\"maxValue\":100,\"minValue\":80,\"show\":false,\"thresholdLabels\":false,\"thresholdMarkers\":false},\"gridPos\":{\"h\":4,\"w\":8,\"x\":8,\"y\":4},\"id\":14,\"interval\":null,\"links\":[],\"options\":{\"colorMode\":\"value\",\"graphMode\":\"area\",\"justifyMode\":\"auto\",\"orientation\":\"horizontal\",\"reduceOptions\":{\"calcs\":[\"lastNotNull\"],\"fields\":\"\",\"values\":false},\"textMode\":\"auto\"},\"mappingType\":1,\"mappingTypes\":[{\"name\":\"value - to text\",\"value\":1},{\"name\":\"range to text\",\"value\":2}],\"maxDataPoints\":100,\"nullPointMode\":\"connected\",\"nullText\":null,\"postfix\":\"\",\"postfixFontSize\":\"50%\",\"prefix\":\"\",\"prefixFontSize\":\"50%\",\"rangeMaps\":[{\"from\":\"null\",\"text\":\"N/A\",\"to\":\"null\"}],\"sparkline\":{\"fillColor\":\"rgba(31, - 118, 189, 0.18)\",\"full\":true,\"lineColor\":\"rgb(31, 120, 193)\",\"show\":true},\"tableColumn\":\"\",\"targets\":[{\"expr\":\"sum(irate(istio_requests_total{reporter=~\\\"$qrep\\\",destination_workload_namespace=~\\\"$namespace\\\",destination_workload=~\\\"$workload\\\",response_code!~\\\"5.*\\\"}[5m])) - / sum(irate(istio_requests_total{reporter=~\\\"$qrep\\\",destination_workload_namespace=~\\\"$namespace\\\",destination_workload=~\\\"$workload\\\"}[5m]))\",\"format\":\"time_series\",\"intervalFactor\":1,\"refId\":\"A\"}],\"thresholds\":\"95, - 99, 99.5\",\"title\":\"Incoming Success Rate (non-5xx responses)\",\"type\":\"singlestat\",\"valueFontSize\":\"80%\",\"valueMaps\":[{\"op\":\"=\",\"text\":\"N/A\",\"value\":\"null\"}],\"valueName\":\"avg\"},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":4,\"w\":8,\"x\":16,\"y\":4},\"hiddenSeries\":false,\"id\":87,\"legend\":{\"alignAsTable\":false,\"avg\":false,\"current\":false,\"hideEmpty\":false,\"hideZero\":false,\"max\":false,\"min\":false,\"rightSide\":true,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"(histogram_quantile(0.50, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\",destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\"}[1m])) by (le)) / 1000) or - histogram_quantile(0.50, sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\",destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\"}[1m])) by (le))\",\"format\":\"time_series\",\"interval\":\"\",\"intervalFactor\":1,\"legendFormat\":\"P50\",\"refId\":\"A\"},{\"expr\":\"(histogram_quantile(0.90, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\",destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\"}[1m])) by (le)) / 1000) or - histogram_quantile(0.90, sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\",destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\"}[1m])) by (le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"P90\",\"refId\":\"B\"},{\"expr\":\"(histogram_quantile(0.99, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\",destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\"}[1m])) by (le)) / 1000) or - histogram_quantile(0.99, sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\",destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\"}[1m])) by (le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"P99\",\"refId\":\"C\"}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Request - Duration\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"s\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"cacheTimeout\":null,\"colorBackground\":false,\"colorValue\":false,\"colors\":[\"#299c46\",\"rgba(237, - 129, 40, 0.89)\",\"#d44a3a\"],\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"format\":\"Bps\",\"gauge\":{\"maxValue\":100,\"minValue\":0,\"show\":false,\"thresholdLabels\":false,\"thresholdMarkers\":true},\"gridPos\":{\"h\":4,\"w\":12,\"x\":0,\"y\":8},\"id\":84,\"interval\":null,\"links\":[],\"options\":{\"colorMode\":\"value\",\"graphMode\":\"area\",\"justifyMode\":\"auto\",\"orientation\":\"horizontal\",\"reduceOptions\":{\"calcs\":[\"lastNotNull\"],\"fields\":\"\",\"values\":false},\"textMode\":\"auto\"},\"mappingType\":1,\"mappingTypes\":[{\"name\":\"value - to text\",\"value\":1},{\"name\":\"range to text\",\"value\":2}],\"maxDataPoints\":100,\"nullPointMode\":\"connected\",\"nullText\":null,\"postfix\":\"\",\"postfixFontSize\":\"50%\",\"prefix\":\"\",\"prefixFontSize\":\"50%\",\"rangeMaps\":[{\"from\":\"null\",\"text\":\"N/A\",\"to\":\"null\"}],\"sparkline\":{\"fillColor\":\"rgba(31, - 118, 189, 0.18)\",\"full\":true,\"lineColor\":\"rgb(31, 120, 193)\",\"show\":true},\"tableColumn\":\"\",\"targets\":[{\"expr\":\"sum(irate(istio_tcp_sent_bytes_total{reporter=~\\\"$qrep\\\", - destination_workload_namespace=~\\\"$namespace\\\", destination_workload=~\\\"$workload\\\"}[1m])) - + sum(irate(istio_tcp_received_bytes_total{reporter=~\\\"$qrep\\\", destination_workload_namespace=~\\\"$namespace\\\", - destination_workload=~\\\"$workload\\\"}[1m]))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"\",\"refId\":\"A\"}],\"thresholds\":\"\",\"title\":\"TCP - Server Traffic\",\"type\":\"singlestat\",\"valueFontSize\":\"80%\",\"valueMaps\":[{\"op\":\"=\",\"text\":\"N/A\",\"value\":\"null\"}],\"valueName\":\"avg\"},{\"cacheTimeout\":null,\"colorBackground\":false,\"colorValue\":false,\"colors\":[\"#299c46\",\"rgba(237, - 129, 40, 0.89)\",\"#d44a3a\"],\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"format\":\"Bps\",\"gauge\":{\"maxValue\":100,\"minValue\":0,\"show\":false,\"thresholdLabels\":false,\"thresholdMarkers\":true},\"gridPos\":{\"h\":4,\"w\":12,\"x\":12,\"y\":8},\"id\":85,\"interval\":null,\"links\":[],\"options\":{\"colorMode\":\"value\",\"graphMode\":\"area\",\"justifyMode\":\"auto\",\"orientation\":\"horizontal\",\"reduceOptions\":{\"calcs\":[\"lastNotNull\"],\"fields\":\"\",\"values\":false},\"textMode\":\"auto\"},\"mappingType\":1,\"mappingTypes\":[{\"name\":\"value - to text\",\"value\":1},{\"name\":\"range to text\",\"value\":2}],\"maxDataPoints\":100,\"nullPointMode\":\"connected\",\"nullText\":null,\"postfix\":\"\",\"postfixFontSize\":\"50%\",\"prefix\":\"\",\"prefixFontSize\":\"50%\",\"rangeMaps\":[{\"from\":\"null\",\"text\":\"N/A\",\"to\":\"null\"}],\"sparkline\":{\"fillColor\":\"rgba(31, - 118, 189, 0.18)\",\"full\":true,\"lineColor\":\"rgb(31, 120, 193)\",\"show\":true},\"tableColumn\":\"\",\"targets\":[{\"expr\":\"sum(irate(istio_tcp_sent_bytes_total{reporter=~\\\"$qrep\\\", - source_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$workload\\\"}[1m])) - + sum(irate(istio_tcp_received_bytes_total{reporter=~\\\"$qrep\\\", source_workload_namespace=~\\\"$namespace\\\", - source_workload=~\\\"$workload\\\"}[1m]))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"\",\"refId\":\"A\"}],\"thresholds\":\"\",\"title\":\"TCP - Client Traffic\",\"type\":\"singlestat\",\"valueFontSize\":\"80%\",\"valueMaps\":[{\"op\":\"=\",\"text\":\"N/A\",\"value\":\"null\"}],\"valueName\":\"avg\"}],\"title\":\"General\",\"type\":\"row\"},{\"collapsed\":true,\"gridPos\":{\"h\":1,\"w\":24,\"x\":0,\"y\":1},\"id\":93,\"panels\":[{\"content\":\"
\\nINBOUND WORKLOADS\\n
\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"gridPos\":{\"h\":3,\"w\":24,\"x\":0,\"y\":13},\"id\":45,\"links\":[],\"mode\":\"html\",\"options\":{\"content\":\"
\\nINBOUND WORKLOADS\\n
\",\"mode\":\"html\"},\"pluginVersion\":\"7.1.0\",\"title\":\"\",\"transparent\":true,\"type\":\"text\"},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":0,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":12,\"x\":0,\"y\":16},\"hiddenSeries\":false,\"id\":25,\"legend\":{\"avg\":false,\"current\":false,\"hideEmpty\":true,\"max\":false,\"min\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null - as zero\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"round(sum(irate(istio_requests_total{connection_security_policy=\\\"mutual_tls\\\", - destination_workload_namespace=~\\\"$namespace\\\", destination_workload=~\\\"$workload\\\", - reporter=~\\\"$qrep\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[5m])) - by (source_workload, source_workload_namespace, response_code), 0.001)\",\"format\":\"time_series\",\"intervalFactor\":1,\"legendFormat\":\"{{ - source_workload }}.{{ source_workload_namespace }} : {{ response_code }} (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"round(sum(irate(istio_requests_total{connection_security_policy!=\\\"mutual_tls\\\", - destination_workload_namespace=~\\\"$namespace\\\", destination_workload=~\\\"$workload\\\", - reporter=~\\\"$qrep\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[5m])) - by (source_workload, source_workload_namespace, response_code), 0.001)\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - source_workload }}.{{ source_workload_namespace }} : {{ response_code }}\",\"refId\":\"B\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Incoming - Requests By Source And Response Code\",\"tooltip\":{\"shared\":false,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[\"total\"]},\"yaxes\":[{\"format\":\"ops\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":12,\"x\":12,\"y\":16},\"hiddenSeries\":false,\"id\":26,\"legend\":{\"avg\":false,\"current\":false,\"hideEmpty\":true,\"hideZero\":false,\"max\":false,\"min\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"sum(irate(istio_requests_total{reporter=~\\\"$qrep\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_workload_namespace=~\\\"$namespace\\\", - destination_workload=~\\\"$workload\\\",response_code!~\\\"5.*\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[5m])) by (source_workload, source_workload_namespace) - / sum(irate(istio_requests_total{reporter=~\\\"$qrep\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_workload_namespace=~\\\"$namespace\\\", destination_workload=~\\\"$workload\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[5m])) - by (source_workload, source_workload_namespace)\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - source_workload }}.{{ source_workload_namespace }} (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"sum(irate(istio_requests_total{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_workload_namespace=~\\\"$namespace\\\", - destination_workload=~\\\"$workload\\\",response_code!~\\\"5.*\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[5m])) by (source_workload, source_workload_namespace) - / sum(irate(istio_requests_total{reporter=~\\\"$qrep\\\", connection_security_policy!=\\\"mutual_tls\\\", - destination_workload_namespace=~\\\"$namespace\\\", destination_workload=~\\\"$workload\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[5m])) - by (source_workload, source_workload_namespace)\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - source_workload }}.{{ source_workload_namespace }}\",\"refId\":\"B\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Incoming - Success Rate (non-5xx responses) By Source\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"percentunit\",\"label\":null,\"logBase\":1,\"max\":\"1.01\",\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"description\":\"\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":8,\"x\":0,\"y\":22},\"hiddenSeries\":false,\"id\":27,\"legend\":{\"alignAsTable\":false,\"avg\":false,\"current\":false,\"hideEmpty\":true,\"hideZero\":false,\"max\":false,\"min\":false,\"rightSide\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"(histogram_quantile(0.50, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le)) / 1000) or histogram_quantile(0.50, sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P50 (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"(histogram_quantile(0.90, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le)) / 1000) or histogram_quantile(0.90, sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P90 (\U0001F510mTLS)\",\"refId\":\"B\",\"step\":2},{\"expr\":\"(histogram_quantile(0.95, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le)) / 1000) or histogram_quantile(0.95, sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P95 (\U0001F510mTLS)\",\"refId\":\"C\",\"step\":2},{\"expr\":\"(histogram_quantile(0.99, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le)) / 1000) or histogram_quantile(0.99, sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P99 (\U0001F510mTLS)\",\"refId\":\"D\",\"step\":2},{\"expr\":\"(histogram_quantile(0.50, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le)) / 1000) or histogram_quantile(0.50, sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P50\",\"refId\":\"E\",\"step\":2},{\"expr\":\"(histogram_quantile(0.90, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le)) / 1000) or histogram_quantile(0.90, sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P90\",\"refId\":\"F\",\"step\":2},{\"expr\":\"(histogram_quantile(0.95, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le)) / 1000) or histogram_quantile(0.95, sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P95\",\"refId\":\"G\",\"step\":2},{\"expr\":\"(histogram_quantile(0.99, sum(irate(istio_request_duration_milliseconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le)) / 1000) or histogram_quantile(0.99, sum(irate(istio_request_duration_seconds_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P99\",\"refId\":\"H\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Incoming - Request Duration By Source\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"s\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":8,\"x\":8,\"y\":22},\"hiddenSeries\":false,\"id\":28,\"legend\":{\"alignAsTable\":false,\"avg\":false,\"current\":false,\"hideEmpty\":true,\"max\":false,\"min\":false,\"rightSide\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"histogram_quantile(0.50, - sum(irate(istio_request_bytes_bucket{reporter=~\\\"$qrep\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_workload=~\\\"$workload\\\", destination_workload_namespace=~\\\"$namespace\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P50 (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"histogram_quantile(0.90, - sum(irate(istio_request_bytes_bucket{reporter=~\\\"$qrep\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_workload=~\\\"$workload\\\", destination_workload_namespace=~\\\"$namespace\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - \ P90 (\U0001F510mTLS)\",\"refId\":\"B\",\"step\":2},{\"expr\":\"histogram_quantile(0.95, - sum(irate(istio_request_bytes_bucket{reporter=~\\\"$qrep\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_workload=~\\\"$workload\\\", destination_workload_namespace=~\\\"$namespace\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P95 (\U0001F510mTLS)\",\"refId\":\"C\",\"step\":2},{\"expr\":\"histogram_quantile(0.99, - sum(irate(istio_request_bytes_bucket{reporter=~\\\"$qrep\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_workload=~\\\"$workload\\\", destination_workload_namespace=~\\\"$namespace\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - \ P99 (\U0001F510mTLS)\",\"refId\":\"D\",\"step\":2},{\"expr\":\"histogram_quantile(0.50, - sum(irate(istio_request_bytes_bucket{reporter=~\\\"$qrep\\\", connection_security_policy!=\\\"mutual_tls\\\", - destination_workload=~\\\"$workload\\\", destination_workload_namespace=~\\\"$namespace\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P50\",\"refId\":\"E\",\"step\":2},{\"expr\":\"histogram_quantile(0.90, sum(irate(istio_request_bytes_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P90\",\"refId\":\"F\",\"step\":2},{\"expr\":\"histogram_quantile(0.95, sum(irate(istio_request_bytes_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P95\",\"refId\":\"G\",\"step\":2},{\"expr\":\"histogram_quantile(0.99, sum(irate(istio_request_bytes_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P99\",\"refId\":\"H\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Incoming - Request Size By Source\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"decbytes\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":8,\"x\":16,\"y\":22},\"hiddenSeries\":false,\"id\":68,\"legend\":{\"alignAsTable\":false,\"avg\":false,\"current\":false,\"hideEmpty\":true,\"max\":false,\"min\":false,\"rightSide\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"histogram_quantile(0.50, - sum(irate(istio_response_bytes_bucket{reporter=~\\\"$qrep\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_workload=~\\\"$workload\\\", destination_workload_namespace=~\\\"$namespace\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P50 (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"histogram_quantile(0.90, - sum(irate(istio_response_bytes_bucket{reporter=~\\\"$qrep\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_workload=~\\\"$workload\\\", destination_workload_namespace=~\\\"$namespace\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - \ P90 (\U0001F510mTLS)\",\"refId\":\"B\",\"step\":2},{\"expr\":\"histogram_quantile(0.95, - sum(irate(istio_response_bytes_bucket{reporter=~\\\"$qrep\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_workload=~\\\"$workload\\\", destination_workload_namespace=~\\\"$namespace\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P95 (\U0001F510mTLS)\",\"refId\":\"C\",\"step\":2},{\"expr\":\"histogram_quantile(0.99, - sum(irate(istio_response_bytes_bucket{reporter=~\\\"$qrep\\\", connection_security_policy=\\\"mutual_tls\\\", - destination_workload=~\\\"$workload\\\", destination_workload_namespace=~\\\"$namespace\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - \ P99 (\U0001F510mTLS)\",\"refId\":\"D\",\"step\":2},{\"expr\":\"histogram_quantile(0.50, - sum(irate(istio_response_bytes_bucket{reporter=~\\\"$qrep\\\", connection_security_policy!=\\\"mutual_tls\\\", - destination_workload=~\\\"$workload\\\", destination_workload_namespace=~\\\"$namespace\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P50\",\"refId\":\"E\",\"step\":2},{\"expr\":\"histogram_quantile(0.90, sum(irate(istio_response_bytes_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P90\",\"refId\":\"F\",\"step\":2},{\"expr\":\"histogram_quantile(0.95, sum(irate(istio_response_bytes_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P95\",\"refId\":\"G\",\"step\":2},{\"expr\":\"histogram_quantile(0.99, sum(irate(istio_response_bytes_bucket{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_workload=~\\\"$workload\\\", - destination_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$srcwl\\\", - source_workload_namespace=~\\\"$srcns\\\"}[1m])) by (source_workload, source_workload_namespace, - le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{source_workload}}.{{source_workload_namespace}} - P99\",\"refId\":\"H\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Response - Size By Source\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"decbytes\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":12,\"x\":0,\"y\":28},\"hiddenSeries\":false,\"id\":80,\"legend\":{\"avg\":false,\"current\":false,\"max\":false,\"min\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"round(sum(irate(istio_tcp_received_bytes_total{reporter=~\\\"$qrep\\\", - connection_security_policy=\\\"mutual_tls\\\", destination_workload_namespace=~\\\"$namespace\\\", - destination_workload=~\\\"$workload\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace), 0.001)\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - source_workload }}.{{ source_workload_namespace}} (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"round(sum(irate(istio_tcp_received_bytes_total{reporter=~\\\"$qrep\\\", - connection_security_policy!=\\\"mutual_tls\\\", destination_workload_namespace=~\\\"$namespace\\\", - destination_workload=~\\\"$workload\\\", source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace), 0.001)\",\"format\":\"time_series\",\"intervalFactor\":1,\"legendFormat\":\"{{ - source_workload }}.{{ source_workload_namespace}}\",\"refId\":\"B\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Bytes - Received from Incoming TCP Connection\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"Bps\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":true}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":12,\"x\":12,\"y\":28},\"hiddenSeries\":false,\"id\":82,\"legend\":{\"avg\":false,\"current\":false,\"max\":false,\"min\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"round(sum(irate(istio_tcp_sent_bytes_total{connection_security_policy=\\\"mutual_tls\\\", - reporter=~\\\"$qrep\\\", destination_workload_namespace=~\\\"$namespace\\\", destination_workload=~\\\"$workload\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace), 0.001)\",\"format\":\"time_series\",\"intervalFactor\":1,\"legendFormat\":\"{{ - source_workload }}.{{ source_workload_namespace}} (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"round(sum(irate(istio_tcp_sent_bytes_total{connection_security_policy!=\\\"mutual_tls\\\", - reporter=~\\\"$qrep\\\", destination_workload_namespace=~\\\"$namespace\\\", destination_workload=~\\\"$workload\\\", - source_workload=~\\\"$srcwl\\\", source_workload_namespace=~\\\"$srcns\\\"}[1m])) - by (source_workload, source_workload_namespace), 0.001)\",\"format\":\"time_series\",\"intervalFactor\":1,\"legendFormat\":\"{{ - source_workload }}.{{ source_workload_namespace}}\",\"refId\":\"B\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Bytes - Sent to Incoming TCP Connection\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"Bps\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":true}],\"yaxis\":{\"align\":false,\"alignLevel\":null}}],\"title\":\"Inbound - Workloads\",\"type\":\"row\"},{\"collapsed\":true,\"gridPos\":{\"h\":1,\"w\":24,\"x\":0,\"y\":2},\"id\":91,\"panels\":[{\"content\":\"
\\nOUTBOUND SERVICES\\n
\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"gridPos\":{\"h\":3,\"w\":24,\"x\":0,\"y\":14},\"id\":69,\"links\":[],\"mode\":\"html\",\"options\":{\"content\":\"
\\nOUTBOUND SERVICES\\n
\",\"mode\":\"html\"},\"pluginVersion\":\"7.1.0\",\"title\":\"\",\"transparent\":true,\"type\":\"text\"},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":0,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":12,\"x\":0,\"y\":17},\"hiddenSeries\":false,\"id\":70,\"legend\":{\"avg\":false,\"current\":false,\"hideEmpty\":true,\"max\":false,\"min\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null - as zero\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"round(sum(irate(istio_requests_total{destination_principal=~\\\"spiffe.*\\\", - source_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$workload\\\", - reporter=\\\"source\\\", destination_service=~\\\"$dstsvc\\\"}[5m])) by (destination_service, - response_code), 0.001)\",\"format\":\"time_series\",\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} : {{ response_code }} (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"round(sum(irate(istio_requests_total{destination_principal!~\\\"spiffe.*\\\", - source_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$workload\\\", - reporter=\\\"source\\\", destination_service=~\\\"$dstsvc\\\"}[5m])) by (destination_service, - response_code), 0.001)\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} : {{ response_code }}\",\"refId\":\"B\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Outgoing - Requests By Destination And Response Code\",\"tooltip\":{\"shared\":false,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[\"total\"]},\"yaxes\":[{\"format\":\"ops\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":12,\"x\":12,\"y\":17},\"hiddenSeries\":false,\"id\":71,\"legend\":{\"avg\":false,\"current\":false,\"hideEmpty\":true,\"hideZero\":false,\"max\":false,\"min\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"sum(irate(istio_requests_total{reporter=\\\"source\\\", - connection_security_policy=\\\"mutual_tls\\\", source_workload_namespace=~\\\"$namespace\\\", - source_workload=~\\\"$workload\\\",response_code!~\\\"5.*\\\", destination_service=~\\\"$dstsvc\\\"}[5m])) - by (destination_service) / sum(irate(istio_requests_total{reporter=\\\"source\\\", - connection_security_policy=\\\"mutual_tls\\\", source_workload_namespace=~\\\"$namespace\\\", - source_workload=~\\\"$workload\\\", destination_service=~\\\"$dstsvc\\\"}[5m])) - by (destination_service)\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"sum(irate(istio_requests_total{reporter=\\\"source\\\", - connection_security_policy!=\\\"mutual_tls\\\", source_workload_namespace=~\\\"$namespace\\\", - source_workload=~\\\"$workload\\\",response_code!~\\\"5.*\\\", destination_service=~\\\"$dstsvc\\\"}[5m])) - by (destination_service) / sum(irate(istio_requests_total{reporter=\\\"source\\\", - connection_security_policy!=\\\"mutual_tls\\\", source_workload_namespace=~\\\"$namespace\\\", - source_workload=~\\\"$workload\\\", destination_service=~\\\"$dstsvc\\\"}[5m])) - by (destination_service)\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }}\",\"refId\":\"B\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Outgoing - Success Rate (non-5xx responses) By Destination\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"percentunit\",\"label\":null,\"logBase\":1,\"max\":\"1.01\",\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"description\":\"\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":8,\"x\":0,\"y\":23},\"hiddenSeries\":false,\"id\":72,\"legend\":{\"alignAsTable\":false,\"avg\":false,\"current\":false,\"hideEmpty\":true,\"hideZero\":false,\"max\":false,\"min\":false,\"rightSide\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"(histogram_quantile(0.50, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=\\\"source\\\", - connection_security_policy=\\\"mutual_tls\\\", source_workload=~\\\"$workload\\\", - source_workload_namespace=~\\\"$namespace\\\", destination_service=~\\\"$dstsvc\\\"}[1m])) - by (destination_service, le)) / 1000) or histogram_quantile(0.50, sum(irate(istio_request_duration_seconds_bucket{reporter=\\\"source\\\", - connection_security_policy=\\\"mutual_tls\\\", source_workload=~\\\"$workload\\\", - source_workload_namespace=~\\\"$namespace\\\", destination_service=~\\\"$dstsvc\\\"}[1m])) - by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P50 (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"(histogram_quantile(0.90, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=\\\"source\\\", - connection_security_policy=\\\"mutual_tls\\\", source_workload=~\\\"$workload\\\", - source_workload_namespace=~\\\"$namespace\\\", destination_service=~\\\"$dstsvc\\\"}[1m])) - by (destination_service, le)) / 1000) or histogram_quantile(0.90, sum(irate(istio_request_duration_seconds_bucket{reporter=\\\"source\\\", - connection_security_policy=\\\"mutual_tls\\\", source_workload=~\\\"$workload\\\", - source_workload_namespace=~\\\"$namespace\\\", destination_service=~\\\"$dstsvc\\\"}[1m])) - by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P90 (\U0001F510mTLS)\",\"refId\":\"B\",\"step\":2},{\"expr\":\"(histogram_quantile(0.95, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=\\\"source\\\", - connection_security_policy=\\\"mutual_tls\\\", source_workload=~\\\"$workload\\\", - source_workload_namespace=~\\\"$namespace\\\", destination_service=~\\\"$dstsvc\\\"}[1m])) - by (destination_service, le)) / 1000) or histogram_quantile(0.95, sum(irate(istio_request_duration_seconds_bucket{reporter=\\\"source\\\", - connection_security_policy=\\\"mutual_tls\\\", source_workload=~\\\"$workload\\\", - source_workload_namespace=~\\\"$namespace\\\", destination_service=~\\\"$dstsvc\\\"}[1m])) - by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P95 (\U0001F510mTLS)\",\"refId\":\"C\",\"step\":2},{\"expr\":\"(histogram_quantile(0.99, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=\\\"source\\\", - connection_security_policy=\\\"mutual_tls\\\", source_workload=~\\\"$workload\\\", - source_workload_namespace=~\\\"$namespace\\\", destination_service=~\\\"$dstsvc\\\"}[1m])) - by (destination_service, le)) / 1000) or histogram_quantile(0.99, sum(irate(istio_request_duration_seconds_bucket{reporter=\\\"source\\\", - connection_security_policy=\\\"mutual_tls\\\", source_workload=~\\\"$workload\\\", - source_workload_namespace=~\\\"$namespace\\\", destination_service=~\\\"$dstsvc\\\"}[1m])) - by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P99 (\U0001F510mTLS)\",\"refId\":\"D\",\"step\":2},{\"expr\":\"(histogram_quantile(0.50, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=\\\"source\\\", - connection_security_policy!=\\\"mutual_tls\\\", source_workload=~\\\"$workload\\\", - source_workload_namespace=~\\\"$namespace\\\", destination_service=~\\\"$dstsvc\\\"}[1m])) - by (destination_service, le)) / 1000) or histogram_quantile(0.50, sum(irate(istio_request_duration_seconds_bucket{reporter=\\\"source\\\", - connection_security_policy!=\\\"mutual_tls\\\", source_workload=~\\\"$workload\\\", - source_workload_namespace=~\\\"$namespace\\\", destination_service=~\\\"$dstsvc\\\"}[1m])) - by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P50\",\"refId\":\"E\",\"step\":2},{\"expr\":\"(histogram_quantile(0.90, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=\\\"source\\\", - connection_security_policy!=\\\"mutual_tls\\\", source_workload=~\\\"$workload\\\", - source_workload_namespace=~\\\"$namespace\\\", destination_service=~\\\"$dstsvc\\\"}[1m])) - by (destination_service, le)) / 1000) or histogram_quantile(0.90, sum(irate(istio_request_duration_seconds_bucket{reporter=\\\"source\\\", - connection_security_policy!=\\\"mutual_tls\\\", source_workload=~\\\"$workload\\\", - source_workload_namespace=~\\\"$namespace\\\", destination_service=~\\\"$dstsvc\\\"}[1m])) - by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P90\",\"refId\":\"F\",\"step\":2},{\"expr\":\"(histogram_quantile(0.95, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=\\\"source\\\", - connection_security_policy!=\\\"mutual_tls\\\", source_workload=~\\\"$workload\\\", - source_workload_namespace=~\\\"$namespace\\\", destination_service=~\\\"$dstsvc\\\"}[1m])) - by (destination_service, le)) / 1000) or histogram_quantile(0.95, sum(irate(istio_request_duration_seconds_bucket{reporter=\\\"source\\\", - connection_security_policy!=\\\"mutual_tls\\\", source_workload=~\\\"$workload\\\", - source_workload_namespace=~\\\"$namespace\\\", destination_service=~\\\"$dstsvc\\\"}[1m])) - by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P95\",\"refId\":\"G\",\"step\":2},{\"expr\":\"(histogram_quantile(0.99, - sum(irate(istio_request_duration_milliseconds_bucket{reporter=\\\"source\\\", - connection_security_policy!=\\\"mutual_tls\\\", source_workload=~\\\"$workload\\\", - source_workload_namespace=~\\\"$namespace\\\", destination_service=~\\\"$dstsvc\\\"}[1m])) - by (destination_service, le)) / 1000) or histogram_quantile(0.99, sum(irate(istio_request_duration_seconds_bucket{reporter=\\\"source\\\", - connection_security_policy!=\\\"mutual_tls\\\", source_workload=~\\\"$workload\\\", - source_workload_namespace=~\\\"$namespace\\\", destination_service=~\\\"$dstsvc\\\"}[1m])) - by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P99\",\"refId\":\"H\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Outgoing - Request Duration By Destination\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"s\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":8,\"x\":8,\"y\":23},\"hiddenSeries\":false,\"id\":73,\"legend\":{\"alignAsTable\":false,\"avg\":false,\"current\":false,\"hideEmpty\":true,\"max\":false,\"min\":false,\"rightSide\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"histogram_quantile(0.50, - sum(irate(istio_request_bytes_bucket{reporter=\\\"source\\\", connection_security_policy=\\\"mutual_tls\\\", - source_workload=~\\\"$workload\\\", source_workload_namespace=~\\\"$namespace\\\", - destination_service=~\\\"$dstsvc\\\"}[1m])) by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P50 (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"histogram_quantile(0.90, - sum(irate(istio_request_bytes_bucket{reporter=\\\"source\\\", connection_security_policy=\\\"mutual_tls\\\", - source_workload=~\\\"$workload\\\", source_workload_namespace=~\\\"$namespace\\\", - destination_service=~\\\"$dstsvc\\\"}[1m])) by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P90 (\U0001F510mTLS)\",\"refId\":\"B\",\"step\":2},{\"expr\":\"histogram_quantile(0.95, - sum(irate(istio_request_bytes_bucket{reporter=\\\"source\\\", connection_security_policy=\\\"mutual_tls\\\", - source_workload=~\\\"$workload\\\", source_workload_namespace=~\\\"$namespace\\\", - destination_service=~\\\"$dstsvc\\\"}[1m])) by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P95 (\U0001F510mTLS)\",\"refId\":\"C\",\"step\":2},{\"expr\":\"histogram_quantile(0.99, - sum(irate(istio_request_bytes_bucket{reporter=\\\"source\\\", connection_security_policy=\\\"mutual_tls\\\", - source_workload=~\\\"$workload\\\", source_workload_namespace=~\\\"$namespace\\\", - destination_service=~\\\"$dstsvc\\\"}[1m])) by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P99 (\U0001F510mTLS)\",\"refId\":\"D\",\"step\":2},{\"expr\":\"histogram_quantile(0.50, - sum(irate(istio_request_bytes_bucket{reporter=\\\"source\\\", connection_security_policy!=\\\"mutual_tls\\\", - source_workload=~\\\"$workload\\\", source_workload_namespace=~\\\"$namespace\\\", - destination_service=~\\\"$dstsvc\\\"}[1m])) by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P50\",\"refId\":\"E\",\"step\":2},{\"expr\":\"histogram_quantile(0.90, - sum(irate(istio_request_bytes_bucket{reporter=\\\"source\\\", connection_security_policy!=\\\"mutual_tls\\\", - source_workload=~\\\"$workload\\\", source_workload_namespace=~\\\"$namespace\\\", - destination_service=~\\\"$dstsvc\\\"}[1m])) by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P90\",\"refId\":\"F\",\"step\":2},{\"expr\":\"histogram_quantile(0.95, - sum(irate(istio_request_bytes_bucket{reporter=\\\"source\\\", connection_security_policy!=\\\"mutual_tls\\\", - source_workload=~\\\"$workload\\\", source_workload_namespace=~\\\"$namespace\\\", - destination_service=~\\\"$dstsvc\\\"}[1m])) by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P95\",\"refId\":\"G\",\"step\":2},{\"expr\":\"histogram_quantile(0.99, - sum(irate(istio_request_bytes_bucket{reporter=\\\"source\\\", connection_security_policy!=\\\"mutual_tls\\\", - source_workload=~\\\"$workload\\\", source_workload_namespace=~\\\"$namespace\\\", - destination_service=~\\\"$dstsvc\\\"}[1m])) by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P99\",\"refId\":\"H\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Outgoing - Request Size By Destination\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"decbytes\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":8,\"x\":16,\"y\":23},\"hiddenSeries\":false,\"id\":74,\"legend\":{\"alignAsTable\":false,\"avg\":false,\"current\":false,\"hideEmpty\":true,\"max\":false,\"min\":false,\"rightSide\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"histogram_quantile(0.50, - sum(irate(istio_response_bytes_bucket{reporter=\\\"source\\\", connection_security_policy=\\\"mutual_tls\\\", - source_workload=~\\\"$workload\\\", source_workload_namespace=~\\\"$namespace\\\", - destination_service=~\\\"$dstsvc\\\"}[1m])) by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P50 (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"histogram_quantile(0.90, - sum(irate(istio_response_bytes_bucket{reporter=\\\"source\\\", connection_security_policy=\\\"mutual_tls\\\", - source_workload=~\\\"$workload\\\", source_workload_namespace=~\\\"$namespace\\\", - destination_service=~\\\"$dstsvc\\\"}[1m])) by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P90 (\U0001F510mTLS)\",\"refId\":\"B\",\"step\":2},{\"expr\":\"histogram_quantile(0.95, - sum(irate(istio_response_bytes_bucket{reporter=\\\"source\\\", connection_security_policy=\\\"mutual_tls\\\", - source_workload=~\\\"$workload\\\", source_workload_namespace=~\\\"$namespace\\\", - destination_service=~\\\"$dstsvc\\\"}[1m])) by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P95 (\U0001F510mTLS)\",\"refId\":\"C\",\"step\":2},{\"expr\":\"histogram_quantile(0.99, - sum(irate(istio_response_bytes_bucket{reporter=\\\"source\\\", connection_security_policy=\\\"mutual_tls\\\", - source_workload=~\\\"$workload\\\", source_workload_namespace=~\\\"$namespace\\\", - destination_service=~\\\"$dstsvc\\\"}[1m])) by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P99 (\U0001F510mTLS)\",\"refId\":\"D\",\"step\":2},{\"expr\":\"histogram_quantile(0.50, - sum(irate(istio_response_bytes_bucket{reporter=\\\"source\\\", connection_security_policy!=\\\"mutual_tls\\\", - source_workload=~\\\"$workload\\\", source_workload_namespace=~\\\"$namespace\\\", - destination_service=~\\\"$dstsvc\\\"}[1m])) by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P50\",\"refId\":\"E\",\"step\":2},{\"expr\":\"histogram_quantile(0.90, - sum(irate(istio_response_bytes_bucket{reporter=\\\"source\\\", connection_security_policy!=\\\"mutual_tls\\\", - source_workload=~\\\"$workload\\\", source_workload_namespace=~\\\"$namespace\\\", - destination_service=~\\\"$dstsvc\\\"}[1m])) by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P90\",\"refId\":\"F\",\"step\":2},{\"expr\":\"histogram_quantile(0.95, - sum(irate(istio_response_bytes_bucket{reporter=\\\"source\\\", connection_security_policy!=\\\"mutual_tls\\\", - source_workload=~\\\"$workload\\\", source_workload_namespace=~\\\"$namespace\\\", - destination_service=~\\\"$dstsvc\\\"}[1m])) by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P95\",\"refId\":\"G\",\"step\":2},{\"expr\":\"histogram_quantile(0.99, - sum(irate(istio_response_bytes_bucket{reporter=\\\"source\\\", connection_security_policy!=\\\"mutual_tls\\\", - source_workload=~\\\"$workload\\\", source_workload_namespace=~\\\"$namespace\\\", - destination_service=~\\\"$dstsvc\\\"}[1m])) by (destination_service, le))\",\"format\":\"time_series\",\"hide\":false,\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} P99\",\"refId\":\"H\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Response - Size By Destination\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"decbytes\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":false}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":12,\"x\":0,\"y\":29},\"hiddenSeries\":false,\"id\":76,\"legend\":{\"avg\":false,\"current\":false,\"max\":false,\"min\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"round(sum(irate(istio_tcp_received_bytes_total{connection_security_policy=\\\"mutual_tls\\\", - reporter=\\\"source\\\", source_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$workload\\\", - destination_service=~\\\"$dstsvc\\\"}[1m])) by (destination_service), 0.001)\",\"format\":\"time_series\",\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"round(sum(irate(istio_tcp_received_bytes_total{connection_security_policy!=\\\"mutual_tls\\\", - reporter=\\\"source\\\", source_workload_namespace=~\\\"$namespace\\\", source_workload=~\\\"$workload\\\", - destination_service=~\\\"$dstsvc\\\"}[1m])) by (destination_service), 0.001)\",\"format\":\"time_series\",\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }}\",\"refId\":\"B\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Bytes - Sent on Outgoing TCP Connection\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"Bps\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":true}],\"yaxis\":{\"align\":false,\"alignLevel\":null}},{\"aliasColors\":{},\"bars\":false,\"dashLength\":10,\"dashes\":false,\"datasource\":\"Prometheus\",\"fieldConfig\":{\"defaults\":{\"custom\":{}},\"overrides\":[]},\"fill\":1,\"fillGradient\":0,\"gridPos\":{\"h\":6,\"w\":12,\"x\":12,\"y\":29},\"hiddenSeries\":false,\"id\":78,\"legend\":{\"avg\":false,\"current\":false,\"max\":false,\"min\":false,\"show\":true,\"total\":false,\"values\":false},\"lines\":true,\"linewidth\":1,\"links\":[],\"nullPointMode\":\"null\",\"percentage\":false,\"pluginVersion\":\"7.1.0\",\"pointradius\":5,\"points\":false,\"renderer\":\"flot\",\"seriesOverrides\":[],\"spaceLength\":10,\"stack\":false,\"steppedLine\":false,\"targets\":[{\"expr\":\"round(sum(irate(istio_tcp_sent_bytes_total{reporter=\\\"source\\\", - connection_security_policy=\\\"mutual_tls\\\", source_workload_namespace=~\\\"$namespace\\\", - source_workload=~\\\"$workload\\\", destination_service=~\\\"$dstsvc\\\"}[1m])) - by (destination_service), 0.001)\",\"format\":\"time_series\",\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }} (\U0001F510mTLS)\",\"refId\":\"A\",\"step\":2},{\"expr\":\"round(sum(irate(istio_tcp_sent_bytes_total{reporter=\\\"source\\\", - connection_security_policy!=\\\"mutual_tls\\\", source_workload_namespace=~\\\"$namespace\\\", - source_workload=~\\\"$workload\\\", destination_service=~\\\"$dstsvc\\\"}[1m])) - by (destination_service), 0.001)\",\"format\":\"time_series\",\"intervalFactor\":1,\"legendFormat\":\"{{ - destination_service }}\",\"refId\":\"B\",\"step\":2}],\"thresholds\":[],\"timeFrom\":null,\"timeRegions\":[],\"timeShift\":null,\"title\":\"Bytes - Received from Outgoing TCP Connection\",\"tooltip\":{\"shared\":true,\"sort\":0,\"value_type\":\"individual\"},\"type\":\"graph\",\"xaxis\":{\"buckets\":null,\"mode\":\"time\",\"name\":null,\"show\":true,\"values\":[]},\"yaxes\":[{\"format\":\"Bps\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":\"0\",\"show\":true},{\"format\":\"short\",\"label\":null,\"logBase\":1,\"max\":null,\"min\":null,\"show\":true}],\"yaxis\":{\"align\":false,\"alignLevel\":null}}],\"title\":\"Outbound - Services\",\"type\":\"row\"}],\"refresh\":\"1m\",\"schemaVersion\":26,\"style\":\"dark\",\"tags\":[],\"templating\":{\"list\":[{\"current\":{\"selected\":true,\"text\":\"default\",\"value\":\"default\"},\"hide\":0,\"includeAll\":false,\"label\":null,\"multi\":false,\"name\":\"datasource\",\"options\":[],\"query\":\"prometheus\",\"queryValue\":\"\",\"refresh\":1,\"regex\":\"\",\"skipUrlSync\":false,\"type\":\"datasource\"},{\"allValue\":null,\"current\":{},\"datasource\":\"Prometheus\",\"definition\":\"\",\"hide\":0,\"includeAll\":false,\"label\":\"Namespace\",\"multi\":false,\"name\":\"namespace\",\"options\":[],\"query\":\"query_result(sum(istio_requests_total) - by (destination_workload_namespace) or sum(istio_tcp_sent_bytes_total) by (destination_workload_namespace))\",\"refresh\":1,\"regex\":\"/.*_namespace=\\\"([^\\\"]*).*/\",\"skipUrlSync\":false,\"sort\":0,\"tagValuesQuery\":\"\",\"tags\":[],\"tagsQuery\":\"\",\"type\":\"query\",\"useTags\":false},{\"allValue\":null,\"current\":{},\"datasource\":\"Prometheus\",\"definition\":\"\",\"hide\":0,\"includeAll\":false,\"label\":\"Workload\",\"multi\":false,\"name\":\"workload\",\"options\":[],\"query\":\"query_result((sum(istio_requests_total{destination_workload_namespace=~\\\"$namespace\\\"}) - by (destination_workload) or sum(istio_requests_total{source_workload_namespace=~\\\"$namespace\\\"}) - by (source_workload)) or (sum(istio_tcp_sent_bytes_total{destination_workload_namespace=~\\\"$namespace\\\"}) - by (destination_workload) or sum(istio_tcp_sent_bytes_total{source_workload_namespace=~\\\"$namespace\\\"}) - by (source_workload)))\",\"refresh\":1,\"regex\":\"/.*workload=\\\"([^\\\"]*).*/\",\"skipUrlSync\":false,\"sort\":1,\"tagValuesQuery\":\"\",\"tags\":[],\"tagsQuery\":\"\",\"type\":\"query\",\"useTags\":false},{\"allValue\":null,\"current\":{\"selected\":true,\"text\":\"destination\",\"value\":\"destination\"},\"datasource\":\"Prometheus\",\"definition\":\"\",\"hide\":0,\"includeAll\":false,\"label\":\"Reporter\",\"multi\":true,\"name\":\"qrep\",\"options\":[],\"query\":\"label_values(reporter)\",\"refresh\":1,\"regex\":\"\",\"skipUrlSync\":false,\"sort\":2,\"tagValuesQuery\":\"\",\"tags\":[],\"tagsQuery\":\"\",\"type\":\"query\",\"useTags\":false},{\"allValue\":null,\"current\":{},\"datasource\":\"Prometheus\",\"definition\":\"\",\"hide\":0,\"includeAll\":true,\"label\":\"Inbound - Workload Namespace\",\"multi\":true,\"name\":\"srcns\",\"options\":[],\"query\":\"query_result(sum(istio_requests_total{reporter=~\\\"$qrep\\\", - destination_workload=\\\"$workload\\\", destination_workload_namespace=~\\\"$namespace\\\"}) - by (source_workload_namespace) or sum(istio_tcp_sent_bytes_total{reporter=~\\\"$qrep\\\", - destination_workload=\\\"$workload\\\", destination_workload_namespace=~\\\"$namespace\\\"}) - by (source_workload_namespace))\",\"refresh\":1,\"regex\":\"/.*namespace=\\\"([^\\\"]*).*/\",\"skipUrlSync\":false,\"sort\":2,\"tagValuesQuery\":\"\",\"tags\":[],\"tagsQuery\":\"\",\"type\":\"query\",\"useTags\":false},{\"allValue\":null,\"current\":{},\"datasource\":\"Prometheus\",\"definition\":\"\",\"hide\":0,\"includeAll\":true,\"label\":\"Inbound - Workload\",\"multi\":true,\"name\":\"srcwl\",\"options\":[],\"query\":\"query_result(sum(istio_requests_total{reporter=~\\\"$qrep\\\", - destination_workload=\\\"$workload\\\", destination_workload_namespace=~\\\"$namespace\\\", - source_workload_namespace=~\\\"$srcns\\\"}) by (source_workload) or sum(istio_tcp_sent_bytes_total{reporter=~\\\"$qrep\\\", - destination_workload=\\\"$workload\\\", destination_workload_namespace=~\\\"$namespace\\\", - source_workload_namespace=~\\\"$srcns\\\"}) by (source_workload))\",\"refresh\":1,\"regex\":\"/.*workload=\\\"([^\\\"]*).*/\",\"skipUrlSync\":false,\"sort\":3,\"tagValuesQuery\":\"\",\"tags\":[],\"tagsQuery\":\"\",\"type\":\"query\",\"useTags\":false},{\"allValue\":null,\"current\":{},\"datasource\":\"Prometheus\",\"definition\":\"\",\"hide\":0,\"includeAll\":true,\"label\":\"Destination - Service\",\"multi\":true,\"name\":\"dstsvc\",\"options\":[],\"query\":\"query_result(sum(istio_requests_total{reporter=\\\"source\\\", - source_workload=~\\\"$workload\\\", source_workload_namespace=~\\\"$namespace\\\"}) - by (destination_service) or sum(istio_tcp_sent_bytes_total{reporter=\\\"source\\\", - source_workload=~\\\"$workload\\\", source_workload_namespace=~\\\"$namespace\\\"}) - by (destination_service))\",\"refresh\":1,\"regex\":\"/.*destination_service=\\\"([^\\\"]*).*/\",\"skipUrlSync\":false,\"sort\":4,\"tagValuesQuery\":\"\",\"tags\":[],\"tagsQuery\":\"\",\"type\":\"query\",\"useTags\":false}]},\"time\":{\"from\":\"now-5m\",\"to\":\"now\"},\"timepicker\":{\"refresh_intervals\":[\"5m\",\"15m\",\"30m\",\"1h\",\"2h\",\"1d\"],\"time_options\":[\"5m\",\"15m\",\"1h\",\"6h\",\"12h\",\"24h\",\"2d\",\"7d\",\"30d\"]},\"timezone\":\"\",\"title\":\"Istio - Workload Dashboard\",\"uid\":\"UbsSZTDik\",\"version\":1}\n" -kind: ConfigMap -metadata: - creationTimestamp: null - name: istio-services-grafana-dashboards - namespace: dubbo-system diff --git a/samples/addons/jaeger.yaml b/samples/addons/jaeger.yaml deleted file mode 100644 index 1e29bb0af..000000000 --- a/samples/addons/jaeger.yaml +++ /dev/null @@ -1,117 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: jaeger - namespace: dubbo-system - labels: - app: jaeger -spec: - selector: - matchLabels: - app: jaeger - template: - metadata: - labels: - app: jaeger - annotations: - sidecar.istio.io/inject: "false" - prometheus.io/scrape: "true" - prometheus.io/port: "14269" - spec: - containers: - - name: jaeger - image: "docker.io/jaegertracing/all-in-one:1.29" - env: - - name: BADGER_EPHEMERAL - value: "false" - - name: SPAN_STORAGE_TYPE - value: "badger" - - name: BADGER_DIRECTORY_VALUE - value: "/badger/data" - - name: BADGER_DIRECTORY_KEY - value: "/badger/key" - - name: COLLECTOR_ZIPKIN_HOST_PORT - value: ":9411" - - name: MEMORY_MAX_TRACES - value: "50000" - - name: QUERY_BASE_PATH - value: /jaeger - livenessProbe: - httpGet: - path: / - port: 14269 - readinessProbe: - httpGet: - path: / - port: 14269 - volumeMounts: - - name: data - mountPath: /badger - resources: - requests: - cpu: 10m - volumes: - - name: data - emptyDir: {} ---- -apiVersion: v1 -kind: Service -metadata: - name: tracing - namespace: dubbo-system - labels: - app: jaeger -spec: - type: ClusterIP - ports: - - name: http-query - port: 80 - protocol: TCP - targetPort: 16686 - # Note: Change port name if you add '--query.grpc.tls.enabled=true' - - name: grpc-query - port: 16685 - protocol: TCP - targetPort: 16685 - selector: - app: jaeger ---- -# Jaeger implements the Zipkin API. To support swapping out the tracing backend, we use a Service named Zipkin. -apiVersion: v1 -kind: Service -metadata: - labels: - name: zipkin - name: zipkin - namespace: dubbo-system -spec: - ports: - - port: 9411 - targetPort: 9411 - name: http-query - selector: - app: jaeger ---- -apiVersion: v1 -kind: Service -metadata: - name: jaeger-collector - namespace: dubbo-system - labels: - app: jaeger -spec: - type: ClusterIP - ports: - - name: jaeger-collector-http - port: 14268 - targetPort: 14268 - protocol: TCP - - name: jaeger-collector-grpc - port: 14250 - targetPort: 14250 - protocol: TCP - - port: 9411 - targetPort: 9411 - name: http-zipkin - selector: - app: jaeger diff --git a/samples/addons/kiali.yaml b/samples/addons/kiali.yaml deleted file mode 100644 index 1c25cf65b..000000000 --- a/samples/addons/kiali.yaml +++ /dev/null @@ -1,541 +0,0 @@ ---- -# Source: kiali-server/templates/serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: kiali - namespace: dubbo-system - labels: - helm.sh/chart: kiali-server-1.50.0 - app: kiali - app.kubernetes.io/name: kiali - app.kubernetes.io/instance: kiali - version: "v1.50.0" - app.kubernetes.io/version: "v1.50.0" - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/part-of: "kiali" -... ---- -# Source: kiali-server/templates/configmap.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: kiali - namespace: dubbo-system - labels: - helm.sh/chart: kiali-server-1.50.0 - app: kiali - app.kubernetes.io/name: kiali - app.kubernetes.io/instance: kiali - version: "v1.50.0" - app.kubernetes.io/version: "v1.50.0" - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/part-of: "kiali" -data: - config.yaml: | - auth: - openid: {} - openshift: - client_id_prefix: kiali - strategy: anonymous - deployment: - accessible_namespaces: - - '**' - additional_service_yaml: {} - affinity: - node: {} - pod: {} - pod_anti: {} - configmap_annotations: {} - custom_secrets: [] - host_aliases: [] - hpa: - api_version: autoscaling/v2beta2 - spec: {} - image_digest: "" - image_name: quay.io/kiali/kiali - image_pull_policy: Always - image_pull_secrets: [] - image_version: v1.50 - ingress: - additional_labels: {} - class_name: nginx - override_yaml: - metadata: {} - ingress_enabled: false - instance_name: kiali - logger: - log_format: text - log_level: info - sampler_rate: "1" - time_field_format: 2006-01-02T15:04:05Z07:00 - namespace: dubbo-system - node_selector: {} - pod_annotations: {} - pod_labels: - sidecar.istio.io/inject: "false" - priority_class_name: "" - replicas: 1 - resources: - limits: - memory: 1Gi - requests: - cpu: 10m - memory: 64Mi - secret_name: kiali - service_annotations: {} - service_type: "" - tolerations: [] - version_label: v1.50.0 - view_only_mode: false - external_services: - custom_dashboards: - enabled: true - istio: - root_namespace: dubbo-system - identity: - cert_file: "" - private_key_file: "" - istio_namespace: dubbo-system - kiali_feature_flags: - certificates_information_indicators: - enabled: true - secrets: - - cacerts - - istio-ca-secret - clustering: - enabled: true - disabled_features: [] - validations: - ignore: - - KIA1201 - login_token: - signing_key: CHANGEME00000000 - server: - metrics_enabled: true - metrics_port: 9090 - port: 20001 - web_root: /kiali -... ---- -# Source: kiali-server/templates/role-viewer.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: kiali-viewer - labels: - helm.sh/chart: kiali-server-1.50.0 - app: kiali - app.kubernetes.io/name: kiali - app.kubernetes.io/instance: kiali - version: "v1.50.0" - app.kubernetes.io/version: "v1.50.0" - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/part-of: "kiali" -rules: -- apiGroups: [""] - resources: - - configmaps - - endpoints - - pods/log - verbs: - - get - - list - - watch -- apiGroups: [""] - resources: - - namespaces - - pods - - replicationcontrollers - - services - verbs: - - get - - list - - watch -- apiGroups: [""] - resources: - - pods/portforward - verbs: - - create - - post -- apiGroups: ["extensions", "apps"] - resources: - - daemonsets - - deployments - - replicasets - - statefulsets - verbs: - - get - - list - - watch -- apiGroups: ["batch"] - resources: - - cronjobs - - jobs - verbs: - - get - - list - - watch -- apiGroups: - - networking.istio.io - - security.istio.io - resources: ["*"] - verbs: - - get - - list - - watch -- apiGroups: ["apps.openshift.io"] - resources: - - deploymentconfigs - verbs: - - get - - list - - watch -- apiGroups: ["project.openshift.io"] - resources: - - projects - verbs: - - get -- apiGroups: ["route.openshift.io"] - resources: - - routes - verbs: - - get -- apiGroups: ["authentication.k8s.io"] - resources: - - tokenreviews - verbs: - - create -... ---- -# Source: kiali-server/templates/role.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: kiali - labels: - helm.sh/chart: kiali-server-1.50.0 - app: kiali - app.kubernetes.io/name: kiali - app.kubernetes.io/instance: kiali - version: "v1.50.0" - app.kubernetes.io/version: "v1.50.0" - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/part-of: "kiali" -rules: -- apiGroups: [""] - resources: - - configmaps - - endpoints - - pods/log - verbs: - - get - - list - - watch -- apiGroups: [""] - resources: - - namespaces - - pods - - replicationcontrollers - - services - verbs: - - get - - list - - watch - - patch -- apiGroups: [""] - resources: - - pods/portforward - verbs: - - create - - post -- apiGroups: ["extensions", "apps"] - resources: - - daemonsets - - deployments - - replicasets - - statefulsets - verbs: - - get - - list - - watch - - patch -- apiGroups: ["batch"] - resources: - - cronjobs - - jobs - verbs: - - get - - list - - watch - - patch -- apiGroups: - - networking.istio.io - - security.istio.io - resources: ["*"] - verbs: - - get - - list - - watch - - create - - delete - - patch -- apiGroups: ["apps.openshift.io"] - resources: - - deploymentconfigs - verbs: - - get - - list - - watch - - patch -- apiGroups: ["project.openshift.io"] - resources: - - projects - verbs: - - get -- apiGroups: ["route.openshift.io"] - resources: - - routes - verbs: - - get -- apiGroups: ["authentication.k8s.io"] - resources: - - tokenreviews - verbs: - - create -... ---- -# Source: kiali-server/templates/rolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: kiali - labels: - helm.sh/chart: kiali-server-1.50.0 - app: kiali - app.kubernetes.io/name: kiali - app.kubernetes.io/instance: kiali - version: "v1.50.0" - app.kubernetes.io/version: "v1.50.0" - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/part-of: "kiali" -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: kiali -subjects: -- kind: ServiceAccount - name: kiali - namespace: dubbo-system -... ---- -# Source: kiali-server/templates/role-controlplane.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: kiali-controlplane - namespace: dubbo-system - labels: - helm.sh/chart: kiali-server-1.50.0 - app: kiali - app.kubernetes.io/name: kiali - app.kubernetes.io/instance: kiali - version: "v1.50.0" - app.kubernetes.io/version: "v1.50.0" - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/part-of: "kiali" -rules: -- apiGroups: [""] - resources: - - secrets - verbs: - - list -- apiGroups: [""] - resourceNames: - - cacerts - - istio-ca-secret - resources: - - secrets - verbs: - - get - - list - - watch -... ---- -# Source: kiali-server/templates/rolebinding-controlplane.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: kiali-controlplane - namespace: dubbo-system - labels: - helm.sh/chart: kiali-server-1.50.0 - app: kiali - app.kubernetes.io/name: kiali - app.kubernetes.io/instance: kiali - version: "v1.50.0" - app.kubernetes.io/version: "v1.50.0" - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/part-of: "kiali" -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: kiali-controlplane -subjects: -- kind: ServiceAccount - name: kiali - namespace: dubbo-system -... ---- -# Source: kiali-server/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: kiali - namespace: dubbo-system - labels: - helm.sh/chart: kiali-server-1.50.0 - app: kiali - app.kubernetes.io/name: kiali - app.kubernetes.io/instance: kiali - version: "v1.50.0" - app.kubernetes.io/version: "v1.50.0" - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/part-of: "kiali" - annotations: -spec: - ports: - - name: http - protocol: TCP - port: 20001 - - name: http-metrics - protocol: TCP - port: 9090 - selector: - app.kubernetes.io/name: kiali - app.kubernetes.io/instance: kiali -... ---- -# Source: kiali-server/templates/deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: kiali - namespace: dubbo-system - labels: - helm.sh/chart: kiali-server-1.50.0 - app: kiali - app.kubernetes.io/name: kiali - app.kubernetes.io/instance: kiali - version: "v1.50.0" - app.kubernetes.io/version: "v1.50.0" - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/part-of: "kiali" -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: kiali - app.kubernetes.io/instance: kiali - strategy: - rollingUpdate: - maxSurge: 1 - maxUnavailable: 1 - type: RollingUpdate - template: - metadata: - name: kiali - labels: - helm.sh/chart: kiali-server-1.50.0 - app: kiali - app.kubernetes.io/name: kiali - app.kubernetes.io/instance: kiali - version: "v1.50.0" - app.kubernetes.io/version: "v1.50.0" - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/part-of: "kiali" - sidecar.istio.io/inject: "false" - annotations: - checksum/config: 03a5118048208a4375d255f2e6119911359cbdf0bbbea7a66012cf2648b41d52 - prometheus.io/scrape: "true" - prometheus.io/port: "9090" - kiali.io/dashboards: go,kiali - spec: - serviceAccountName: kiali - containers: - - image: "quay.io/kiali/kiali:v1.50" - imagePullPolicy: Always - name: kiali - command: - - "/opt/kiali/kiali" - - "-config" - - "/kiali-configuration/config.yaml" - securityContext: - allowPrivilegeEscalation: false - privileged: false - readOnlyRootFilesystem: true - runAsNonRoot: true - ports: - - name: api-port - containerPort: 20001 - - name: http-metrics - containerPort: 9090 - readinessProbe: - httpGet: - path: /kiali/healthz - port: api-port - scheme: HTTP - initialDelaySeconds: 5 - periodSeconds: 30 - livenessProbe: - httpGet: - path: /kiali/healthz - port: api-port - scheme: HTTP - initialDelaySeconds: 5 - periodSeconds: 30 - env: - - name: ACTIVE_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: LOG_LEVEL - value: "info" - - name: LOG_FORMAT - value: "text" - - name: LOG_TIME_FIELD_FORMAT - value: "2006-01-02T15:04:05Z07:00" - - name: LOG_SAMPLER_RATE - value: "1" - volumeMounts: - - name: kiali-configuration - mountPath: "/kiali-configuration" - - name: kiali-cert - mountPath: "/kiali-cert" - - name: kiali-secret - mountPath: "/kiali-secret" - - name: kiali-cabundle - mountPath: "/kiali-cabundle" - resources: - limits: - memory: 1Gi - requests: - cpu: 10m - memory: 64Mi - volumes: - - name: kiali-configuration - configMap: - name: kiali - - name: kiali-cert - secret: - secretName: istio.kiali-service-account - optional: true - - name: kiali-secret - secret: - secretName: kiali - optional: true - - name: kiali-cabundle - configMap: - name: kiali-cabundle - optional: true -... diff --git a/samples/addons/prometheus.yaml b/samples/addons/prometheus.yaml deleted file mode 100644 index 449db1cff..000000000 --- a/samples/addons/prometheus.yaml +++ /dev/null @@ -1,501 +0,0 @@ ---- -# Source: prometheus/templates/server/serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - component: "server" - app: prometheus - release: prometheus - chart: prometheus-15.0.1 - heritage: Helm - name: prometheus - namespace: dubbo-system - annotations: - {} ---- -# Source: prometheus/templates/server/cm.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - labels: - component: "server" - app: prometheus - release: prometheus - chart: prometheus-15.0.1 - heritage: Helm - name: prometheus - namespace: dubbo-system -data: - alerting_rules.yml: | - {} - alerts: | - {} - prometheus.yml: | - global: - evaluation_interval: 1m - scrape_interval: 15s - scrape_timeout: 10s - rule_files: - - /etc/config/recording_rules.yml - - /etc/config/alerting_rules.yml - - /etc/config/rules - - /etc/config/alerts - scrape_configs: - - job_name: prometheus - static_configs: - - targets: - - localhost:9090 - - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - job_name: kubernetes-apiservers - kubernetes_sd_configs: - - role: endpoints - relabel_configs: - - action: keep - regex: default;kubernetes;https - source_labels: - - __meta_kubernetes_namespace - - __meta_kubernetes_service_name - - __meta_kubernetes_endpoint_port_name - scheme: https - tls_config: - ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - insecure_skip_verify: true - - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - job_name: kubernetes-nodes - kubernetes_sd_configs: - - role: node - relabel_configs: - - action: labelmap - regex: __meta_kubernetes_node_label_(.+) - - replacement: kubernetes.default.svc:443 - target_label: __address__ - - regex: (.+) - replacement: /api/v1/nodes/$1/proxy/metrics - source_labels: - - __meta_kubernetes_node_name - target_label: __metrics_path__ - scheme: https - tls_config: - ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - insecure_skip_verify: true - - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - job_name: kubernetes-nodes-cadvisor - kubernetes_sd_configs: - - role: node - relabel_configs: - - action: labelmap - regex: __meta_kubernetes_node_label_(.+) - - replacement: kubernetes.default.svc:443 - target_label: __address__ - - regex: (.+) - replacement: /api/v1/nodes/$1/proxy/metrics/cadvisor - source_labels: - - __meta_kubernetes_node_name - target_label: __metrics_path__ - scheme: https - tls_config: - ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - insecure_skip_verify: true - - job_name: kubernetes-service-endpoints - kubernetes_sd_configs: - - role: endpoints - relabel_configs: - - action: keep - regex: true - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_scrape - - action: replace - regex: (https?) - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_scheme - target_label: __scheme__ - - action: replace - regex: (.+) - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_path - target_label: __metrics_path__ - - action: replace - regex: ([^:]+)(?::\d+)?;(\d+) - replacement: $1:$2 - source_labels: - - __address__ - - __meta_kubernetes_service_annotation_prometheus_io_port - target_label: __address__ - - action: labelmap - regex: __meta_kubernetes_service_annotation_prometheus_io_param_(.+) - replacement: __param_$1 - - action: labelmap - regex: __meta_kubernetes_service_label_(.+) - - action: replace - source_labels: - - __meta_kubernetes_namespace - target_label: namespace - - action: replace - source_labels: - - __meta_kubernetes_service_name - target_label: service - - action: replace - source_labels: - - __meta_kubernetes_pod_node_name - target_label: node - - job_name: kubernetes-service-endpoints-slow - kubernetes_sd_configs: - - role: endpoints - relabel_configs: - - action: keep - regex: true - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_scrape_slow - - action: replace - regex: (https?) - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_scheme - target_label: __scheme__ - - action: replace - regex: (.+) - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_path - target_label: __metrics_path__ - - action: replace - regex: ([^:]+)(?::\d+)?;(\d+) - replacement: $1:$2 - source_labels: - - __address__ - - __meta_kubernetes_service_annotation_prometheus_io_port - target_label: __address__ - - action: labelmap - regex: __meta_kubernetes_service_annotation_prometheus_io_param_(.+) - replacement: __param_$1 - - action: labelmap - regex: __meta_kubernetes_service_label_(.+) - - action: replace - source_labels: - - __meta_kubernetes_namespace - target_label: namespace - - action: replace - source_labels: - - __meta_kubernetes_service_name - target_label: service - - action: replace - source_labels: - - __meta_kubernetes_pod_node_name - target_label: node - scrape_interval: 5m - scrape_timeout: 30s - - honor_labels: true - job_name: prometheus-pushgateway - kubernetes_sd_configs: - - role: service - relabel_configs: - - action: keep - regex: pushgateway - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_probe - - job_name: kubernetes-services - kubernetes_sd_configs: - - role: service - metrics_path: /probe - params: - module: - - http_2xx - relabel_configs: - - action: keep - regex: true - source_labels: - - __meta_kubernetes_service_annotation_prometheus_io_probe - - source_labels: - - __address__ - target_label: __param_target - - replacement: blackbox - target_label: __address__ - - source_labels: - - __param_target - target_label: instance - - action: labelmap - regex: __meta_kubernetes_service_label_(.+) - - source_labels: - - __meta_kubernetes_namespace - target_label: namespace - - source_labels: - - __meta_kubernetes_service_name - target_label: service - - job_name: kubernetes-pods - kubernetes_sd_configs: - - role: pod - relabel_configs: - - action: keep - regex: true - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_scrape - - action: replace - regex: (https?) - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_scheme - target_label: __scheme__ - - action: replace - regex: (.+) - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_path - target_label: __metrics_path__ - - action: replace - regex: ([^:]+)(?::\d+)?;(\d+) - replacement: $1:$2 - source_labels: - - __address__ - - __meta_kubernetes_pod_annotation_prometheus_io_port - target_label: __address__ - - action: labelmap - regex: __meta_kubernetes_pod_annotation_prometheus_io_param_(.+) - replacement: __param_$1 - - action: labelmap - regex: __meta_kubernetes_pod_label_(.+) - - action: replace - source_labels: - - __meta_kubernetes_namespace - target_label: namespace - - action: replace - source_labels: - - __meta_kubernetes_pod_name - target_label: pod - - action: drop - regex: Pending|Succeeded|Failed|Completed - source_labels: - - __meta_kubernetes_pod_phase - - job_name: kubernetes-pods-slow - kubernetes_sd_configs: - - role: pod - relabel_configs: - - action: keep - regex: true - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_scrape_slow - - action: replace - regex: (https?) - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_scheme - target_label: __scheme__ - - action: replace - regex: (.+) - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_path - target_label: __metrics_path__ - - action: replace - regex: ([^:]+)(?::\d+)?;(\d+) - replacement: $1:$2 - source_labels: - - __address__ - - __meta_kubernetes_pod_annotation_prometheus_io_port - target_label: __address__ - - action: labelmap - regex: __meta_kubernetes_pod_annotation_prometheus_io_param_(.+) - replacement: __param_$1 - - action: labelmap - regex: __meta_kubernetes_pod_label_(.+) - - action: replace - source_labels: - - __meta_kubernetes_namespace - target_label: namespace - - action: replace - source_labels: - - __meta_kubernetes_pod_name - target_label: pod - - action: drop - regex: Pending|Succeeded|Failed|Completed - source_labels: - - __meta_kubernetes_pod_phase - scrape_interval: 5m - scrape_timeout: 30s - recording_rules.yml: | - {} - rules: | - {} ---- -# Source: prometheus/templates/server/clusterrole.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - component: "server" - app: prometheus - release: prometheus - chart: prometheus-15.0.1 - heritage: Helm - name: prometheus -rules: - - apiGroups: - - "" - resources: - - nodes - - nodes/proxy - - nodes/metrics - - services - - endpoints - - pods - - ingresses - - configmaps - verbs: - - get - - list - - watch - - apiGroups: - - "extensions" - - "networking.k8s.io" - resources: - - ingresses/status - - ingresses - verbs: - - get - - list - - watch - - nonResourceURLs: - - "/metrics" - verbs: - - get ---- -# Source: prometheus/templates/server/clusterrolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - component: "server" - app: prometheus - release: prometheus - chart: prometheus-15.0.1 - heritage: Helm - name: prometheus -subjects: - - kind: ServiceAccount - name: prometheus - namespace: dubbo-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: prometheus ---- -# Source: prometheus/templates/server/service.yaml -apiVersion: v1 -kind: Service -metadata: - labels: - component: "server" - app: prometheus - release: prometheus - chart: prometheus-15.0.1 - heritage: Helm - name: prometheus - namespace: dubbo-system -spec: - ports: - - name: http - port: 9090 - protocol: TCP - targetPort: 9090 - selector: - component: "server" - app: prometheus - release: prometheus - sessionAffinity: None - type: "ClusterIP" ---- -# Source: prometheus/templates/server/deploy.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - component: "server" - app: prometheus - release: prometheus - chart: prometheus-15.0.1 - heritage: Helm - name: prometheus - namespace: dubbo-system -spec: - selector: - matchLabels: - component: "server" - app: prometheus - release: prometheus - replicas: 1 - template: - metadata: - labels: - component: "server" - app: prometheus - release: prometheus - chart: prometheus-15.0.1 - heritage: Helm - - sidecar.istio.io/inject: "false" - spec: - enableServiceLinks: true - serviceAccountName: prometheus - containers: - - name: prometheus-server-configmap-reload - image: "jimmidyson/configmap-reload:v0.5.0" - imagePullPolicy: "IfNotPresent" - args: - - --volume-dir=/etc/config - - --webhook-url=http://127.0.0.1:9090/-/reload - resources: - {} - volumeMounts: - - name: config-volume - mountPath: /etc/config - readOnly: true - - - name: prometheus-server - image: "prom/prometheus:v2.31.1" - imagePullPolicy: "IfNotPresent" - args: - - --storage.tsdb.retention.time=15d - - --config.file=/etc/config/prometheus.yml - - --storage.tsdb.path=/data - - --web.console.libraries=/etc/prometheus/console_libraries - - --web.console.templates=/etc/prometheus/consoles - - --web.enable-lifecycle - ports: - - containerPort: 9090 - readinessProbe: - httpGet: - path: /-/ready - port: 9090 - scheme: HTTP - initialDelaySeconds: 0 - periodSeconds: 5 - timeoutSeconds: 4 - failureThreshold: 3 - successThreshold: 1 - livenessProbe: - httpGet: - path: /-/healthy - port: 9090 - scheme: HTTP - initialDelaySeconds: 30 - periodSeconds: 15 - timeoutSeconds: 10 - failureThreshold: 3 - successThreshold: 1 - resources: - {} - volumeMounts: - - name: config-volume - mountPath: /etc/config - - name: storage-volume - mountPath: /data - subPath: "" - hostNetwork: false - dnsPolicy: ClusterFirst - securityContext: - fsGroup: 65534 - runAsGroup: 65534 - runAsNonRoot: true - runAsUser: 65534 - terminationGracePeriodSeconds: 300 - volumes: - - name: config-volume - configMap: - name: prometheus - - name: storage-volume - emptyDir: - {} diff --git a/samples/bookinfo/README.md b/samples/bookinfo/README.md deleted file mode 100644 index 7683b21b0..000000000 --- a/samples/bookinfo/README.md +++ /dev/null @@ -1,100 +0,0 @@ -# Bookinfo Sample - -See . - -**Note**: We need the owner of the PR to perform the appropriate testing with built/pushed images to their own docker repository before we would build/push images to the official Istio repository. - -## Build docker images - -```bash -cd samples/bookinfo -src/build-services.sh -``` - -Where `` is the tag and `` is the docker registry to tag the images. - -For example: - -```bash -$ src/build-services.sh 1.16.3 docker.io/shamsher31 -Sending build context to Docker daemon 1.218MB -Step 1/16 : FROM python:3.7.7-slim -3.7.7-slim: Pulling from library/python -8559a31e96f4: Pull complete -... -Successfully built 1b293582cc2e -Successfully tagged shamsher31/examples-bookinfo-ratings-v2:1.16.3 -Successfully tagged shamsher31/examples-bookinfo-ratings-v2:latest -``` - -The bookinfo versions are different from Istio versions since the sample should work with any version of Istio. - -## Push docker images to docker hub - -After the local build is successful, you need to update the YAML file with the latest tag that you used during the build eg: `1.16.3`. - -Run the following script to build the docker images, push them to docker hub, and to update the YAML files in one step. - -```bash -./build_push_update_images.sh -``` - -For example: - -```bash -$ ./build_push_update_images.sh 1.16.3 --prefix=shamsher31 -... -1.16.3: digest: sha256:70634d3847a190b9826975c8 size: 3883 -Pushing: shamsher31/examples-bookinfo-reviews-v2:1.16.3 -The push refers to a repository [docker.io/shamsher31/examples-bookinfo-reviews-v2] -... -``` - -Verify that expected tag eg: `1.16.3` is updated in `platform/kube/bookinfo*.yaml` files. - -## Tests - -Test that the bookinfo samples work with the latest tag eg: `1.16.3` that you pushed. - -```bash -$ cd ../../ -$ kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -serviceaccount/bookinfo-details created -deployment.apps/details-v1 created -serviceaccount/bookinfo-ratings created -... -``` - -Wait for all the pods to be in `Running` start. - -```bash -$ kubectl get pods -NAME READY STATUS RESTARTS AGE -details-v1-7f556f5c6b-485l2 2/2 Running 0 10m -productpage-v1-84c8f95c8d-tlml2 2/2 Running 0 10m -ratings-v1-66777f856b-2ls78 2/2 Running 0 10m -reviews-v1-64c47f4f44-rx642 2/2 Running 0 10m -reviews-v2-66b6b95f44-s5nt6 2/2 Running 0 10m -reviews-v3-7f69dd7fd4-zjvc8 2/2 Running 0 10m -``` - -Once all the pods are in the `Running` state. Test if the bookinfo works through cli. - -```bash -$ kubectl exec -it "$(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}')" -c ratings -- curl productpage:9080/productpage | grep -o ".*" -Simple Bookstore App -``` - -You can also test it by hitting productpage in the browser. - -```bash -http://192.168.39.116:31395/productpage -``` - -You should see the following in the browser. - -![star](https://user-images.githubusercontent.com/2920003/86032538-212ff900-ba55-11ea-9492-d4bc90656a02.png) - -**Note**: If everything works as mentioned above, request a new official set of images be built and pushed from the reviewer, and add another commit to the original PR with the version changes. - -Bookinfo is tested by istio.io integration tests. You can find them under [tests](https://github.com/istio/istio.io/tree/master/tests) in the [istio/istio.io](https://github.com/istio/istio.io) repository. diff --git a/samples/bookinfo/build_push_update_images.sh b/samples/bookinfo/build_push_update_images.sh deleted file mode 100755 index 1d4fdcf95..000000000 --- a/samples/bookinfo/build_push_update_images.sh +++ /dev/null @@ -1,111 +0,0 @@ -#!/bin/bash -# -# Copyright 2018 Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -o errexit - -display_usage() { - echo - echo "USAGE: ./build_push_update_images.sh [-h|--help] [--prefix=value] [--scan-images]" - echo " version : Version of the sample app images (Required)" - echo " -h|--help : Prints usage information" - echo " --prefix: Use the value as the prefix for image names. By default, 'istio' is used" - echo -e " --scan-images : Enable security vulnerability scans for docker images \n\t\t\trelated to bookinfo sample apps. By default, this feature \n\t\t\tis disabled." - exit 1 -} - -# Check if there is atleast one input argument -if [[ -z "$1" ]] ; then - echo "Missing version parameter" - display_usage -else - VERSION="$1" - shift -fi - -# Process the input arguments. By default, image scanning is disabled. -PREFIX=istio -ENABLE_IMAGE_SCAN=false -echo "$@" -for i in "$@" -do - case "$i" in - --prefix=* ) - PREFIX="${i#--prefix=}" ;; - --scan-images ) - ENABLE_IMAGE_SCAN=true ;; - -h|--help ) - echo - echo "Build the docker images for bookinfo sample apps, push them to docker hub and update the yaml files." - display_usage ;; - * ) - echo "Unknown argument: $i" - display_usage ;; - esac -done - -#Build docker images -src/build-services.sh "${VERSION}" "${PREFIX}" - -#get all the new image names and tags -for v in ${VERSION} "latest" -do - IMAGES+=$(docker images -f reference="${PREFIX}/examples-bookinfo*:$v" --format "{{.Repository}}:$v") - IMAGES+=" " -done - -# check that $IMAGES contains the images we've just built -if [[ "${IMAGES}" =~ ^\ +$ ]] ; then - echo "Found no images matching prefix \"${PREFIX}/examples-bookinfo\"." - echo "Try running the script without specifying the image registry in --prefix (e.g. --prefix=/foo instead of --prefix=docker.io/foo)." - exit 1 -fi - -# -# Run security vulnerability scanning on bookinfo sample app images using -# trivy. If the image has vulnerabilities, the file will have a .failed -# suffix. A successult scan will have a .passed suffix. -function run_vulnerability_scanning() { - RESULT_DIR="vulnerability_scan_results" - mkdir -p "$RESULT_DIR" - # skip-dir added to prevent timeout of review images - set +e - trivy image --ignore-unfixed --no-progress --exit-code 2 --skip-dirs /opt/ibm/wlp --output "$RESULT_DIR/$1_$VERSION.failed" "$2" - test $? -ne 0 || mv "$RESULT_DIR/$1_$VERSION.failed" "$RESULT_DIR/$1_$VERSION.passed" - set -e -} - -# Push images. Scan images if ENABLE_IMAGE_SCAN is true. -for IMAGE in ${IMAGES}; -do - echo "Pushing: ${IMAGE}" - docker push "${IMAGE}"; - - # $IMAGE has the following format: istio/examples-bookinfo*:"$v". - # We want to get the sample app name from $IMAGE (the examples-bookinfo* portion) - # to create the file to store the results of the scan for that image. The first - # part of the $IMAGE_NAME gets examples-bookinfo*:"$v", and the second part gets - # 'examples-bookinfo*'. - if [[ "$ENABLE_IMAGE_SCAN" == "true" ]]; then - echo "Scanning ${IMAGE} for security vulnerabilities" - IMAGE_NAME=${IMAGE#*/} - IMAGE_NAME=${IMAGE_NAME%:*} - run_vulnerability_scanning "${IMAGE_NAME}" "${IMAGE}" - fi -done - -#Update image references in the yaml files -find . -name "*bookinfo*.yaml" -exec sed -i.bak "s/image:.*\\(\\/examples-bookinfo-.*\\):.*/image: ${PREFIX//\//\\\/}\\1:$VERSION/g" {} + - diff --git a/samples/bookinfo/networking/bookinfo-gateway.yaml b/samples/bookinfo/networking/bookinfo-gateway.yaml deleted file mode 100644 index 951f069f3..000000000 --- a/samples/bookinfo/networking/bookinfo-gateway.yaml +++ /dev/null @@ -1,41 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: bookinfo-gateway -spec: - selector: - istio: ingressgateway # use istio default controller - servers: - - port: - number: 80 - name: http - protocol: HTTP - hosts: - - "*" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: bookinfo -spec: - hosts: - - "*" - gateways: - - bookinfo-gateway - http: - - match: - - uri: - exact: /productpage - - uri: - prefix: /static - - uri: - exact: /login - - uri: - exact: /logout - - uri: - prefix: /api/v1/products - route: - - destination: - host: productpage - port: - number: 9080 diff --git a/samples/bookinfo/networking/certmanager-gateway.yaml b/samples/bookinfo/networking/certmanager-gateway.yaml deleted file mode 100644 index 48d623ff7..000000000 --- a/samples/bookinfo/networking/certmanager-gateway.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: cert-manager-gateway - namespace: dubbo-system -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 80 - name: http - protocol: HTTP - hosts: - - "*" ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: cert-manager - namespace: dubbo-system -spec: - hosts: - - "*" - gateways: - - cert-manager-gateway - http: - - match: - - uri: - prefix: /.well-known/acme-challenge/ - route: - - destination: - host: cert-manager-resolver - port: - number: 8089 diff --git a/samples/bookinfo/networking/destination-rule-all-mtls.yaml b/samples/bookinfo/networking/destination-rule-all-mtls.yaml deleted file mode 100644 index 2a19c3fb4..000000000 --- a/samples/bookinfo/networking/destination-rule-all-mtls.yaml +++ /dev/null @@ -1,74 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: productpage -spec: - host: productpage - trafficPolicy: - tls: - mode: ISTIO_MUTUAL - subsets: - - name: v1 - labels: - version: v1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: reviews -spec: - host: reviews - trafficPolicy: - tls: - mode: ISTIO_MUTUAL - subsets: - - name: v1 - labels: - version: v1 - - name: v2 - labels: - version: v2 - - name: v3 - labels: - version: v3 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: ratings -spec: - host: ratings - trafficPolicy: - tls: - mode: ISTIO_MUTUAL - subsets: - - name: v1 - labels: - version: v1 - - name: v2 - labels: - version: v2 - - name: v2-mysql - labels: - version: v2-mysql - - name: v2-mysql-vm - labels: - version: v2-mysql-vm ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: details -spec: - host: details - trafficPolicy: - tls: - mode: ISTIO_MUTUAL - subsets: - - name: v1 - labels: - version: v1 - - name: v2 - labels: - version: v2 ---- diff --git a/samples/bookinfo/networking/destination-rule-all.yaml b/samples/bookinfo/networking/destination-rule-all.yaml deleted file mode 100644 index 96be6993a..000000000 --- a/samples/bookinfo/networking/destination-rule-all.yaml +++ /dev/null @@ -1,62 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: productpage -spec: - host: productpage - subsets: - - name: v1 - labels: - version: v1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: reviews -spec: - host: reviews - subsets: - - name: v1 - labels: - version: v1 - - name: v2 - labels: - version: v2 - - name: v3 - labels: - version: v3 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: ratings -spec: - host: ratings - subsets: - - name: v1 - labels: - version: v1 - - name: v2 - labels: - version: v2 - - name: v2-mysql - labels: - version: v2-mysql - - name: v2-mysql-vm - labels: - version: v2-mysql-vm ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: details -spec: - host: details - subsets: - - name: v1 - labels: - version: v1 - - name: v2 - labels: - version: v2 ---- diff --git a/samples/bookinfo/networking/destination-rule-reviews.yaml b/samples/bookinfo/networking/destination-rule-reviews.yaml deleted file mode 100644 index 69f30f1d9..000000000 --- a/samples/bookinfo/networking/destination-rule-reviews.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: reviews -spec: - host: reviews - trafficPolicy: - loadBalancer: - simple: RANDOM - subsets: - - name: v1 - labels: - version: v1 - - name: v2 - labels: - version: v2 - - name: v3 - labels: - version: v3 diff --git a/samples/bookinfo/networking/egress-rule-google-apis.yaml b/samples/bookinfo/networking/egress-rule-google-apis.yaml deleted file mode 100644 index d35e3ac1d..000000000 --- a/samples/bookinfo/networking/egress-rule-google-apis.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: googleapis -spec: - hosts: - - www.googleapis.com - ports: - - number: 80 - name: http - protocol: HTTP - - number: 443 - name: https - protocol: HTTPS - resolution: DNS ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: rewrite-port-for-googleapis -spec: - hosts: - - www.googleapis.com - http: - - match: - - port: 80 - route: - - destination: - host: www.googleapis.com - port: - number: 443 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: originate-tls-for-googleapis -spec: - host: www.googleapis.com - trafficPolicy: - loadBalancer: - simple: ROUND_ROBIN - portLevelSettings: - - port: - number: 443 - tls: - mode: SIMPLE # initiates HTTPS when accessing www.googleapis.com diff --git a/samples/bookinfo/networking/fault-injection-details-v1.yaml b/samples/bookinfo/networking/fault-injection-details-v1.yaml deleted file mode 100644 index c45509256..000000000 --- a/samples/bookinfo/networking/fault-injection-details-v1.yaml +++ /dev/null @@ -1,32 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: details -spec: - hosts: - - details - http: - - fault: - abort: - httpStatus: 555 - percentage: - value: 100 - route: - - destination: - host: details - subset: v1 - - route: - - destination: - host: details - subset: v1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: details -spec: - host: details - subsets: - - name: v1 - labels: - version: v1 \ No newline at end of file diff --git a/samples/bookinfo/networking/virtual-service-all-v1.yaml b/samples/bookinfo/networking/virtual-service-all-v1.yaml deleted file mode 100644 index 6811e31d9..000000000 --- a/samples/bookinfo/networking/virtual-service-all-v1.yaml +++ /dev/null @@ -1,52 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: productpage -spec: - hosts: - - productpage - http: - - route: - - destination: - host: productpage - subset: v1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews -spec: - hosts: - - reviews - http: - - route: - - destination: - host: reviews - subset: v1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: ratings -spec: - hosts: - - ratings - http: - - route: - - destination: - host: ratings - subset: v1 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: details -spec: - hosts: - - details - http: - - route: - - destination: - host: details - subset: v1 ---- diff --git a/samples/bookinfo/networking/virtual-service-details-v2.yaml b/samples/bookinfo/networking/virtual-service-details-v2.yaml deleted file mode 100644 index 5f21fa530..000000000 --- a/samples/bookinfo/networking/virtual-service-details-v2.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: details -spec: - hosts: - - details - http: - - route: - - destination: - host: details - subset: v2 diff --git a/samples/bookinfo/networking/virtual-service-ratings-db.yaml b/samples/bookinfo/networking/virtual-service-ratings-db.yaml deleted file mode 100644 index 1698ec247..000000000 --- a/samples/bookinfo/networking/virtual-service-ratings-db.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews -spec: - hosts: - - reviews - http: - - route: - - destination: - host: reviews - subset: v3 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: ratings -spec: - hosts: - - ratings - http: - - route: - - destination: - host: ratings - subset: v2 ---- diff --git a/samples/bookinfo/networking/virtual-service-ratings-mysql-vm.yaml b/samples/bookinfo/networking/virtual-service-ratings-mysql-vm.yaml deleted file mode 100644 index fdf882702..000000000 --- a/samples/bookinfo/networking/virtual-service-ratings-mysql-vm.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews -spec: - hosts: - - reviews - http: - - route: - - destination: - host: reviews - subset: v3 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: ratings -spec: - hosts: - - ratings - http: - - route: - - destination: - host: ratings - subset: v2-mysql-vm ---- diff --git a/samples/bookinfo/networking/virtual-service-ratings-mysql.yaml b/samples/bookinfo/networking/virtual-service-ratings-mysql.yaml deleted file mode 100644 index 03a700ead..000000000 --- a/samples/bookinfo/networking/virtual-service-ratings-mysql.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews -spec: - hosts: - - reviews - http: - - route: - - destination: - host: reviews - subset: v3 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: ratings -spec: - hosts: - - ratings - http: - - route: - - destination: - host: ratings - subset: v2-mysql ---- diff --git a/samples/bookinfo/networking/virtual-service-ratings-test-abort.yaml b/samples/bookinfo/networking/virtual-service-ratings-test-abort.yaml deleted file mode 100644 index 51c6fe9c6..000000000 --- a/samples/bookinfo/networking/virtual-service-ratings-test-abort.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: ratings -spec: - hosts: - - ratings - http: - - match: - - headers: - end-user: - exact: jason - fault: - abort: - percentage: - value: 100.0 - httpStatus: 500 - route: - - destination: - host: ratings - subset: v1 - - route: - - destination: - host: ratings - subset: v1 diff --git a/samples/bookinfo/networking/virtual-service-ratings-test-delay.yaml b/samples/bookinfo/networking/virtual-service-ratings-test-delay.yaml deleted file mode 100644 index 6c4e19dad..000000000 --- a/samples/bookinfo/networking/virtual-service-ratings-test-delay.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: ratings -spec: - hosts: - - ratings - http: - - match: - - headers: - end-user: - exact: jason - fault: - delay: - percentage: - value: 100.0 - fixedDelay: 7s - route: - - destination: - host: ratings - subset: v1 - - route: - - destination: - host: ratings - subset: v1 diff --git a/samples/bookinfo/networking/virtual-service-reviews-50-v3.yaml b/samples/bookinfo/networking/virtual-service-reviews-50-v3.yaml deleted file mode 100644 index aad8c3175..000000000 --- a/samples/bookinfo/networking/virtual-service-reviews-50-v3.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews -spec: - hosts: - - reviews - http: - - route: - - destination: - host: reviews - subset: v1 - weight: 50 - - destination: - host: reviews - subset: v3 - weight: 50 diff --git a/samples/bookinfo/networking/virtual-service-reviews-80-20.yaml b/samples/bookinfo/networking/virtual-service-reviews-80-20.yaml deleted file mode 100644 index 7304d867d..000000000 --- a/samples/bookinfo/networking/virtual-service-reviews-80-20.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews -spec: - hosts: - - reviews - http: - - route: - - destination: - host: reviews - subset: v1 - weight: 80 - - destination: - host: reviews - subset: v2 - weight: 20 diff --git a/samples/bookinfo/networking/virtual-service-reviews-90-10.yaml b/samples/bookinfo/networking/virtual-service-reviews-90-10.yaml deleted file mode 100644 index d211dd16a..000000000 --- a/samples/bookinfo/networking/virtual-service-reviews-90-10.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews -spec: - hosts: - - reviews - http: - - route: - - destination: - host: reviews - subset: v1 - weight: 90 - - destination: - host: reviews - subset: v2 - weight: 10 diff --git a/samples/bookinfo/networking/virtual-service-reviews-jason-v2-v3.yaml b/samples/bookinfo/networking/virtual-service-reviews-jason-v2-v3.yaml deleted file mode 100644 index fb3571368..000000000 --- a/samples/bookinfo/networking/virtual-service-reviews-jason-v2-v3.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews -spec: - hosts: - - reviews - http: - - match: - - headers: - end-user: - exact: jason - route: - - destination: - host: reviews - subset: v2 - - route: - - destination: - host: reviews - subset: v3 diff --git a/samples/bookinfo/networking/virtual-service-reviews-test-v2.yaml b/samples/bookinfo/networking/virtual-service-reviews-test-v2.yaml deleted file mode 100644 index ea07efb29..000000000 --- a/samples/bookinfo/networking/virtual-service-reviews-test-v2.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews -spec: - hosts: - - reviews - http: - - match: - - headers: - end-user: - exact: jason - route: - - destination: - host: reviews - subset: v2 - - route: - - destination: - host: reviews - subset: v1 diff --git a/samples/bookinfo/networking/virtual-service-reviews-v2-v3.yaml b/samples/bookinfo/networking/virtual-service-reviews-v2-v3.yaml deleted file mode 100644 index 7ae7b8042..000000000 --- a/samples/bookinfo/networking/virtual-service-reviews-v2-v3.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews -spec: - hosts: - - reviews - http: - - route: - - destination: - host: reviews - subset: v2 - weight: 50 - - destination: - host: reviews - subset: v3 - weight: 50 diff --git a/samples/bookinfo/networking/virtual-service-reviews-v3.yaml b/samples/bookinfo/networking/virtual-service-reviews-v3.yaml deleted file mode 100644 index 5da999d4f..000000000 --- a/samples/bookinfo/networking/virtual-service-reviews-v3.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: reviews -spec: - hosts: - - reviews - http: - - route: - - destination: - host: reviews - subset: v3 diff --git a/samples/bookinfo/platform/kube/README.md b/samples/bookinfo/platform/kube/README.md deleted file mode 100644 index d1189bec3..000000000 --- a/samples/bookinfo/platform/kube/README.md +++ /dev/null @@ -1,2 +0,0 @@ -See the [Bookinfo guide](https://istio.io/docs/guides/bookinfo.html) in Istio -docs for instructions on how to run this demo application. diff --git a/samples/bookinfo/platform/kube/bookinfo-certificate.yaml b/samples/bookinfo/platform/kube/bookinfo-certificate.yaml deleted file mode 100644 index e549b30e2..000000000 --- a/samples/bookinfo/platform/kube/bookinfo-certificate.yaml +++ /dev/null @@ -1,37 +0,0 @@ ---- -apiVersion: certmanager.k8s.io/v1alpha1 -kind: ClusterIssuer -metadata: - name: letsencrypt-staging - namespace: dubbo-system -spec: - acme: - # The ACME server URL - server: https://acme-staging-v02.api.letsencrypt.org/directory - # Email address used for ACME registration - email: stage@istio.io - # Name of a secret used to store the ACME account private key - privateKeySecretRef: - name: letsencrypt-staging - # Enable the HTTP-01 challenge provider - http01: {} ---- -apiVersion: certmanager.k8s.io/v1alpha1 -kind: Certificate -metadata: - name: istio-ingressgateway-certs - namespace: dubbo-system -spec: - secretName: istio-ingressgateway-certs - issuerRef: - name: letsencrypt-staging - kind: ClusterIssuer - commonName: bookinfo.example.com - dnsNames: - - bookinfo.example.com - acme: - config: - - http01: - ingressClass: none - domains: - - bookinfo.example.com diff --git a/samples/bookinfo/platform/kube/bookinfo-db.yaml b/samples/bookinfo/platform/kube/bookinfo-db.yaml deleted file mode 100644 index 7ddf314ca..000000000 --- a/samples/bookinfo/platform/kube/bookinfo-db.yaml +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: v1 -kind: Service -metadata: - name: mongodb - labels: - app: mongodb - service: mongodb -spec: - ports: - - port: 27017 - name: mongo - selector: - app: mongodb ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: mongodb-v1 - labels: - app: mongodb - version: v1 -spec: - replicas: 1 - selector: - matchLabels: - app: mongodb - version: v1 - template: - metadata: - labels: - app: mongodb - version: v1 - spec: - containers: - - name: mongodb - image: docker.io/istio/examples-bookinfo-mongodb:1.16.4 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 27017 - volumeMounts: - - name: data-db - mountPath: /data/db - volumes: - - name: data-db - emptyDir: {} ---- diff --git a/samples/bookinfo/platform/kube/bookinfo-details-v2.yaml b/samples/bookinfo/platform/kube/bookinfo-details-v2.yaml deleted file mode 100644 index 32005bd34..000000000 --- a/samples/bookinfo/platform/kube/bookinfo-details-v2.yaml +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -################################################################################################## -# Details service v2 -################################################################################################## -apiVersion: apps/v1 -kind: Deployment -metadata: - name: details-v2 - labels: - app: details - version: v2 -spec: - replicas: 1 - selector: - matchLabels: - app: details - version: v2 - template: - metadata: - labels: - app: details - version: v2 - spec: - containers: - - name: details - image: docker.io/istio/examples-bookinfo-details-v2:1.16.4 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9080 - env: - - name: DO_NOT_ENCRYPT - value: "true" - securityContext: - runAsUser: 1000 ---- diff --git a/samples/bookinfo/platform/kube/bookinfo-details.yaml b/samples/bookinfo/platform/kube/bookinfo-details.yaml deleted file mode 100644 index c39eefc12..000000000 --- a/samples/bookinfo/platform/kube/bookinfo-details.yaml +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -################################################################################################## -# Details service -################################################################################################## -apiVersion: v1 -kind: Service -metadata: - name: details - labels: - app: details - service: details -spec: - ports: - - port: 9080 - name: http - selector: - app: details ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: details-v1 - labels: - app: details - version: v1 -spec: - replicas: 1 - selector: - matchLabels: - app: details - version: v1 - template: - metadata: - labels: - app: details - version: v1 - spec: - containers: - - name: details - image: docker.io/istio/examples-bookinfo-details-v1:1.16.4 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9080 - securityContext: - runAsUser: 1000 ---- diff --git a/samples/bookinfo/platform/kube/bookinfo-ingress.yaml b/samples/bookinfo/platform/kube/bookinfo-ingress.yaml deleted file mode 100644 index e2143399f..000000000 --- a/samples/bookinfo/platform/kube/bookinfo-ingress.yaml +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -########################################################################### -# Ingress resource (gateway) -########################################################################## -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: gateway - annotations: - kubernetes.io/ingress.class: "istio" -spec: - rules: - - http: - paths: - - path: /productpage - pathType: Exact - backend: - service: - name: productpage - port: - number: 9080 - - path: /static/ - pathType: Prefix - backend: - service: - name: productpage - port: - number: 9080 - - path: /login - pathType: Exact - backend: - service: - name: productpage - port: - number: 9080 - - path: /logout - pathType: Exact - backend: - service: - name: productpage - port: - number: 9080 - - path: /api/v1/products - pathType: Prefix - backend: - service: - name: productpage - port: - number: 9080 ---- diff --git a/samples/bookinfo/platform/kube/bookinfo-mysql.yaml b/samples/bookinfo/platform/kube/bookinfo-mysql.yaml deleted file mode 100644 index df5e1533b..000000000 --- a/samples/bookinfo/platform/kube/bookinfo-mysql.yaml +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -################################################################################################## -# Mysql db services -# credentials: root/password -################################################################################################## -apiVersion: v1 -kind: Secret -metadata: - name: mysql-credentials -type: Opaque -data: - rootpasswd: cGFzc3dvcmQ= ---- -apiVersion: v1 -kind: Service -metadata: - name: mysqldb - labels: - app: mysqldb - service: mysqldb -spec: - ports: - - port: 3306 - name: tcp - selector: - app: mysqldb ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: mysqldb-v1 - labels: - app: mysqldb - version: v1 -spec: - replicas: 1 - selector: - matchLabels: - app: mysqldb - version: v1 - template: - metadata: - labels: - app: mysqldb - version: v1 - spec: - containers: - - name: mysqldb - image: docker.io/istio/examples-bookinfo-mysqldb:1.16.4 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 3306 - env: - - name: MYSQL_ROOT_PASSWORD - valueFrom: - secretKeyRef: - name: mysql-credentials - key: rootpasswd - args: ["--default-authentication-plugin","mysql_native_password"] - volumeMounts: - - name: var-lib-mysql - mountPath: /var/lib/mysql - volumes: - - name: var-lib-mysql - emptyDir: {} ---- diff --git a/samples/bookinfo/platform/kube/bookinfo-ratings-discovery.yaml b/samples/bookinfo/platform/kube/bookinfo-ratings-discovery.yaml deleted file mode 100644 index 61c4b7f10..000000000 --- a/samples/bookinfo/platform/kube/bookinfo-ratings-discovery.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -################################################################################################## -# Ratings service -################################################################################################## -apiVersion: v1 -kind: Service -metadata: - name: ratings - labels: - app: ratings - service: ratings -spec: - ports: - - port: 9080 - name: http - selector: - app: ratings ---- diff --git a/samples/bookinfo/platform/kube/bookinfo-ratings-v2-mysql-vm.yaml b/samples/bookinfo/platform/kube/bookinfo-ratings-v2-mysql-vm.yaml deleted file mode 100644 index db2605f96..000000000 --- a/samples/bookinfo/platform/kube/bookinfo-ratings-v2-mysql-vm.yaml +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: ratings-v2-mysql-vm - labels: - app: ratings - version: v2-mysql-vm -spec: - replicas: 1 - selector: - matchLabels: - app: ratings - version: v2-mysql-vm - template: - metadata: - labels: - app: ratings - version: v2-mysql-vm - spec: - containers: - - name: ratings - image: docker.io/istio/examples-bookinfo-ratings-v2:1.16.4 - imagePullPolicy: IfNotPresent - env: - # This assumes you registered your mysql vm as - # istioctl register -n vm mysqldb 1.2.3.4 3306 - - name: DB_TYPE - value: "mysql" - - name: MYSQL_DB_HOST - value: mysqldb.vm.svc.cluster.local - - name: MYSQL_DB_PORT - value: "3306" - - name: MYSQL_DB_USER - value: root - - name: MYSQL_DB_PASSWORD - value: password - ports: - - containerPort: 9080 - securityContext: - runAsUser: 1000 ---- diff --git a/samples/bookinfo/platform/kube/bookinfo-ratings-v2-mysql.yaml b/samples/bookinfo/platform/kube/bookinfo-ratings-v2-mysql.yaml deleted file mode 100644 index b13d166a0..000000000 --- a/samples/bookinfo/platform/kube/bookinfo-ratings-v2-mysql.yaml +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: ratings-v2-mysql - labels: - app: ratings - version: v2-mysql -spec: - replicas: 1 - selector: - matchLabels: - app: ratings - version: v2-mysql - template: - metadata: - labels: - app: ratings - version: v2-mysql - spec: - containers: - - name: ratings - image: docker.io/istio/examples-bookinfo-ratings-v2:1.16.4 - imagePullPolicy: IfNotPresent - env: - # ratings-v2 will use mongodb as the default db backend. - # if you would like to use mysqldb then you can use this file - # which sets DB_TYPE = 'mysql' and the rest of the parameters shown - # here and also create the # mysqldb service using bookinfo-mysql.yaml - # NOTE: This file is mutually exclusive to bookinfo-ratings-v2.yaml - - name: DB_TYPE - value: "mysql" - - name: MYSQL_DB_HOST - value: mysqldb - - name: MYSQL_DB_PORT - value: "3306" - - name: MYSQL_DB_USER - value: root - - name: MYSQL_DB_PASSWORD - value: password - ports: - - containerPort: 9080 - securityContext: - runAsUser: 1000 ---- diff --git a/samples/bookinfo/platform/kube/bookinfo-ratings-v2.yaml b/samples/bookinfo/platform/kube/bookinfo-ratings-v2.yaml deleted file mode 100644 index b23a0122c..000000000 --- a/samples/bookinfo/platform/kube/bookinfo-ratings-v2.yaml +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: v1 -kind: ServiceAccount -metadata: - name: bookinfo-ratings-v2 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: ratings-v2 - labels: - app: ratings - version: v2 -spec: - replicas: 1 - selector: - matchLabels: - app: ratings - version: v2 - template: - metadata: - labels: - app: ratings - version: v2 - spec: - serviceAccountName: bookinfo-ratings-v2 - containers: - - name: ratings - image: docker.io/istio/examples-bookinfo-ratings-v2:1.16.4 - imagePullPolicy: IfNotPresent - env: - # ratings-v2 will use mongodb as the default db backend. - # if you would like to use mysqldb then set DB_TYPE = 'mysql', set - # the rest of the parameters shown here and also create the - # mysqldb service using bookinfo-mysql.yaml - # - name: DB_TYPE #default to - # value: "mysql" - # - name: MYSQL_DB_HOST - # value: mysqldb - # - name: MYSQL_DB_PORT - # value: "3306" - # - name: MYSQL_DB_USER - # value: root - # - name: MYSQL_DB_PASSWORD - # value: password - - name: MONGO_DB_URL - value: mongodb://mongodb:27017/test - ports: - - containerPort: 9080 - securityContext: - runAsUser: 1000 ---- diff --git a/samples/bookinfo/platform/kube/bookinfo-ratings.yaml b/samples/bookinfo/platform/kube/bookinfo-ratings.yaml deleted file mode 100644 index 13c557e65..000000000 --- a/samples/bookinfo/platform/kube/bookinfo-ratings.yaml +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -################################################################################################## -# Ratings service -################################################################################################## -apiVersion: v1 -kind: Service -metadata: - name: ratings - labels: - app: ratings - service: ratings -spec: - ports: - - port: 9080 - name: http - selector: - app: ratings ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: ratings-v1 - labels: - app: ratings - version: v1 -spec: - replicas: 1 - selector: - matchLabels: - app: ratings - version: v1 - template: - metadata: - labels: - app: ratings - version: v1 - spec: - containers: - - name: ratings - image: docker.io/istio/examples-bookinfo-ratings-v1:1.16.4 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9080 - securityContext: - runAsUser: 1000 ---- diff --git a/samples/bookinfo/platform/kube/bookinfo-reviews-v2.yaml b/samples/bookinfo/platform/kube/bookinfo-reviews-v2.yaml deleted file mode 100644 index 8a1615145..000000000 --- a/samples/bookinfo/platform/kube/bookinfo-reviews-v2.yaml +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -################################################################################################## -# Reviews service v2 -################################################################################################## -apiVersion: apps/v1 -kind: Deployment -metadata: - name: reviews-v2 - labels: - app: reviews - version: v2 -spec: - replicas: 1 - selector: - matchLabels: - app: reviews - version: v2 - template: - metadata: - labels: - app: reviews - version: v2 - spec: - containers: - - name: reviews - image: docker.io/istio/examples-bookinfo-reviews-v2:1.16.4 - imagePullPolicy: IfNotPresent - env: - - name: LOG_DIR - value: "/tmp/logs" - ports: - - containerPort: 9080 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: wlp-output - mountPath: /opt/ibm/wlp/output - securityContext: - runAsUser: 1000 - volumes: - - name: wlp-output - emptyDir: {} - - name: tmp - emptyDir: {} ---- diff --git a/samples/bookinfo/platform/kube/bookinfo.yaml b/samples/bookinfo/platform/kube/bookinfo.yaml deleted file mode 100644 index c6efcb34c..000000000 --- a/samples/bookinfo/platform/kube/bookinfo.yaml +++ /dev/null @@ -1,343 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -################################################################################################## -# This file defines the services, service accounts, and deployments for the Bookinfo sample. -# -# To apply all 4 Bookinfo services, their corresponding service accounts, and deployments: -# -# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -# -# Alternatively, you can deploy any resource separately: -# -# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -l service=reviews # reviews Service -# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -l account=reviews # reviews ServiceAccount -# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -l app=reviews,version=v3 # reviews-v3 Deployment -################################################################################################## - -################################################################################################## -# Details service -################################################################################################## -apiVersion: v1 -kind: Service -metadata: - name: details - labels: - app: details - service: details -spec: - ports: - - port: 9080 - name: http - selector: - app: details ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: bookinfo-details - labels: - account: details ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: details-v1 - labels: - app: details - version: v1 -spec: - replicas: 1 - selector: - matchLabels: - app: details - version: v1 - template: - metadata: - labels: - app: details - version: v1 - spec: - serviceAccountName: bookinfo-details - containers: - - name: details - image: docker.io/istio/examples-bookinfo-details-v1:1.16.4 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9080 - securityContext: - runAsUser: 1000 ---- -################################################################################################## -# Ratings service -################################################################################################## -apiVersion: v1 -kind: Service -metadata: - name: ratings - labels: - app: ratings - service: ratings -spec: - ports: - - port: 9080 - name: http - selector: - app: ratings ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: bookinfo-ratings - labels: - account: ratings ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: ratings-v1 - labels: - app: ratings - version: v1 -spec: - replicas: 1 - selector: - matchLabels: - app: ratings - version: v1 - template: - metadata: - labels: - app: ratings - version: v1 - spec: - serviceAccountName: bookinfo-ratings - containers: - - name: ratings - image: docker.io/istio/examples-bookinfo-ratings-v1:1.16.4 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9080 - securityContext: - runAsUser: 1000 ---- -################################################################################################## -# Reviews service -################################################################################################## -apiVersion: v1 -kind: Service -metadata: - name: reviews - labels: - app: reviews - service: reviews -spec: - ports: - - port: 9080 - name: http - selector: - app: reviews ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: bookinfo-reviews - labels: - account: reviews ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: reviews-v1 - labels: - app: reviews - version: v1 -spec: - replicas: 1 - selector: - matchLabels: - app: reviews - version: v1 - template: - metadata: - labels: - app: reviews - version: v1 - spec: - serviceAccountName: bookinfo-reviews - containers: - - name: reviews - image: docker.io/istio/examples-bookinfo-reviews-v1:1.16.4 - imagePullPolicy: IfNotPresent - env: - - name: LOG_DIR - value: "/tmp/logs" - ports: - - containerPort: 9080 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: wlp-output - mountPath: /opt/ibm/wlp/output - securityContext: - runAsUser: 1000 - volumes: - - name: wlp-output - emptyDir: {} - - name: tmp - emptyDir: {} ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: reviews-v2 - labels: - app: reviews - version: v2 -spec: - replicas: 1 - selector: - matchLabels: - app: reviews - version: v2 - template: - metadata: - labels: - app: reviews - version: v2 - spec: - serviceAccountName: bookinfo-reviews - containers: - - name: reviews - image: docker.io/istio/examples-bookinfo-reviews-v2:1.16.4 - imagePullPolicy: IfNotPresent - env: - - name: LOG_DIR - value: "/tmp/logs" - ports: - - containerPort: 9080 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: wlp-output - mountPath: /opt/ibm/wlp/output - securityContext: - runAsUser: 1000 - volumes: - - name: wlp-output - emptyDir: {} - - name: tmp - emptyDir: {} ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: reviews-v3 - labels: - app: reviews - version: v3 -spec: - replicas: 1 - selector: - matchLabels: - app: reviews - version: v3 - template: - metadata: - labels: - app: reviews - version: v3 - spec: - serviceAccountName: bookinfo-reviews - containers: - - name: reviews - image: docker.io/istio/examples-bookinfo-reviews-v3:1.16.4 - imagePullPolicy: IfNotPresent - env: - - name: LOG_DIR - value: "/tmp/logs" - ports: - - containerPort: 9080 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: wlp-output - mountPath: /opt/ibm/wlp/output - securityContext: - runAsUser: 1000 - volumes: - - name: wlp-output - emptyDir: {} - - name: tmp - emptyDir: {} ---- -################################################################################################## -# Productpage services -################################################################################################## -apiVersion: v1 -kind: Service -metadata: - name: productpage - labels: - app: productpage - service: productpage -spec: - ports: - - port: 9080 - name: http - selector: - app: productpage ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: bookinfo-productpage - labels: - account: productpage ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: productpage-v1 - labels: - app: productpage - version: v1 -spec: - replicas: 1 - selector: - matchLabels: - app: productpage - version: v1 - template: - metadata: - labels: - app: productpage - version: v1 - spec: - serviceAccountName: bookinfo-productpage - containers: - - name: productpage - image: docker.io/istio/examples-bookinfo-productpage-v1:1.16.4 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9080 - volumeMounts: - - name: tmp - mountPath: /tmp - securityContext: - runAsUser: 1000 - volumes: - - name: tmp - emptyDir: {} ---- diff --git a/samples/bookinfo/platform/kube/cleanup.sh b/samples/bookinfo/platform/kube/cleanup.sh deleted file mode 100755 index 18aaa8559..000000000 --- a/samples/bookinfo/platform/kube/cleanup.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/bin/bash -# -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -SCRIPTDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) - -# only ask if in interactive mode -if [[ -t 0 && -z ${NAMESPACE} ]];then - echo -n "namespace ? [default] " - read -r NAMESPACE -fi - -# verify if the namespace exists, otherwise use default namespace -if [[ -n ${NAMESPACE} ]];then - ns=$(kubectl get namespace "${NAMESPACE}" --no-headers --output=go-template="{{.metadata.name}}" 2>/dev/null) - if [[ -z ${ns} ]];then - echo "NAMESPACE ${NAMESPACE} not found." - NAMESPACE=default - fi -fi - -# if no namespace is provided, use default namespace -if [[ -z ${NAMESPACE} ]];then - NAMESPACE=default -fi - -echo "using NAMESPACE=${NAMESPACE}" - -protos=( destinationrules virtualservices gateways ) -for proto in "${protos[@]}"; do - for resource in $(kubectl get -n ${NAMESPACE} "$proto" -o name); do - kubectl delete -n ${NAMESPACE} "$resource"; - done -done - -OUTPUT=$(mktemp) -export OUTPUT -echo "Application cleanup may take up to one minute" -kubectl delete -n ${NAMESPACE} -f "$SCRIPTDIR/bookinfo.yaml" > "${OUTPUT}" 2>&1 -ret=$? -function cleanup() { - rm -f "${OUTPUT}" -} - -trap cleanup EXIT - -if [[ ${ret} -eq 0 ]];then - cat "${OUTPUT}" -else - # ignore NotFound errors - OUT2=$(grep -v NotFound "${OUTPUT}") - if [[ -n ${OUT2} ]];then - cat "${OUTPUT}" - exit ${ret} - fi -fi - -# wait for 30 sec for bookinfo to clean up -sleep 30 - -echo "Application cleanup successful" diff --git a/samples/bookinfo/platform/kube/productpage-nodeport.yaml b/samples/bookinfo/platform/kube/productpage-nodeport.yaml deleted file mode 100644 index aadba2e0c..000000000 --- a/samples/bookinfo/platform/kube/productpage-nodeport.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -################################################################################################## -# Productpage services -################################################################################################## -apiVersion: v1 -kind: Service -metadata: - name: productpage - labels: - app: productpage - service: productpage -spec: - type: NodePort - ports: - - port: 9080 - name: http - selector: - app: productpage ---- diff --git a/samples/bookinfo/policy/productpage_envoy_ratelimit.yaml b/samples/bookinfo/policy/productpage_envoy_ratelimit.yaml deleted file mode 100644 index eb1dfeb56..000000000 --- a/samples/bookinfo/policy/productpage_envoy_ratelimit.yaml +++ /dev/null @@ -1,88 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: filter-ratelimit - namespace: dubbo-system -spec: - workloadSelector: - # select by label in the same namespace - labels: - istio: ingressgateway - configPatches: - # The Envoy config you want to modify - - applyTo: HTTP_FILTER - match: - context: GATEWAY - listener: - filterChain: - filter: - name: "envoy.filters.network.http_connection_manager" - subFilter: - name: "envoy.filters.http.router" - patch: - operation: INSERT_BEFORE - value: - name: envoy.ratelimit - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit - # domain can be anything! Match it to the ratelimter service config - domain: productpage-ratelimit - failure_mode_deny: true - rate_limit_service: - grpc_service: - envoy_grpc: - cluster_name: rate_limit_cluster - timeout: 10s - - applyTo: CLUSTER - match: - cluster: - service: ratelimit.default.svc.cluster.local - patch: - operation: ADD - value: - name: rate_limit_cluster - type: STRICT_DNS - connect_timeout: 10s - lb_policy: ROUND_ROBIN - http2_protocol_options: {} - load_assignment: - cluster_name: rate_limit_cluster - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: ratelimit.default.svc.cluster.local - port_value: 8081 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: EnvoyFilter -metadata: - name: filter-ratelimit-svc - namespace: dubbo-system -spec: - workloadSelector: - labels: - istio: ingressgateway - configPatches: - - applyTo: VIRTUAL_HOST - match: - context: GATEWAY - routeConfiguration: - vhost: - name: "" - route: - action: ANY - patch: - operation: MERGE - value: - rate_limits: - - actions: # any actions in here - # Multiple actions nest the descriptors - # - generic_key: - # descriptor_value: "test" - - request_headers: - header_name: ":path" - descriptor_key: "PATH" - # - remote_address: {} - # - destination_cluster: {} \ No newline at end of file diff --git a/samples/bookinfo/src/build-services.sh b/samples/bookinfo/src/build-services.sh deleted file mode 100755 index e9f595546..000000000 --- a/samples/bookinfo/src/build-services.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/bin/bash -# -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -o errexit - -if [ "$#" -ne 2 ]; then - echo "Incorrect parameters" - echo "Usage: build-services.sh " - exit 1 -fi - -VERSION=$1 -PREFIX=$2 -SCRIPTDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) - -pushd "$SCRIPTDIR/productpage" - docker build --pull -t "${PREFIX}/examples-bookinfo-productpage-v1:${VERSION}" -t "${PREFIX}/examples-bookinfo-productpage-v1:latest" . - #flooding - docker build --pull -t "${PREFIX}/examples-bookinfo-productpage-v-flooding:${VERSION}" -t "${PREFIX}/examples-bookinfo-productpage-v-flooding:latest" --build-arg flood_factor=100 . -popd - -pushd "$SCRIPTDIR/details" - #plain build -- no calling external book service to fetch topics - docker build --pull -t "${PREFIX}/examples-bookinfo-details-v1:${VERSION}" -t "${PREFIX}/examples-bookinfo-details-v1:latest" --build-arg service_version=v1 . - #with calling external book service to fetch topic for the book - docker build --pull -t "${PREFIX}/examples-bookinfo-details-v2:${VERSION}" -t "${PREFIX}/examples-bookinfo-details-v2:latest" --build-arg service_version=v2 \ - --build-arg enable_external_book_service=true . -popd - -pushd "$SCRIPTDIR/reviews" - #java build the app. - docker run --rm -u root -v "$(pwd)":/home/gradle/project -w /home/gradle/project gradle:4.8.1 gradle clean build - pushd reviews-wlpcfg - #plain build -- no ratings - docker build --pull -t "${PREFIX}/examples-bookinfo-reviews-v1:${VERSION}" -t "${PREFIX}/examples-bookinfo-reviews-v1:latest" --build-arg service_version=v1 . - #with ratings black stars - docker build --pull -t "${PREFIX}/examples-bookinfo-reviews-v2:${VERSION}" -t "${PREFIX}/examples-bookinfo-reviews-v2:latest" --build-arg service_version=v2 \ - --build-arg enable_ratings=true . - #with ratings red stars - docker build --pull -t "${PREFIX}/examples-bookinfo-reviews-v3:${VERSION}" -t "${PREFIX}/examples-bookinfo-reviews-v3:latest" --build-arg service_version=v3 \ - --build-arg enable_ratings=true --build-arg star_color=red . - popd -popd - -pushd "$SCRIPTDIR/ratings" - docker build --pull -t "${PREFIX}/examples-bookinfo-ratings-v1:${VERSION}" -t "${PREFIX}/examples-bookinfo-ratings-v1:latest" --build-arg service_version=v1 . - docker build --pull -t "${PREFIX}/examples-bookinfo-ratings-v2:${VERSION}" -t "${PREFIX}/examples-bookinfo-ratings-v2:latest" --build-arg service_version=v2 . - docker build --pull -t "${PREFIX}/examples-bookinfo-ratings-v-faulty:${VERSION}" -t "${PREFIX}/examples-bookinfo-ratings-v-faulty:latest" --build-arg service_version=v-faulty . - docker build --pull -t "${PREFIX}/examples-bookinfo-ratings-v-delayed:${VERSION}" -t "${PREFIX}/examples-bookinfo-ratings-v-delayed:latest" --build-arg service_version=v-delayed . - docker build --pull -t "${PREFIX}/examples-bookinfo-ratings-v-unavailable:${VERSION}" -t "${PREFIX}/examples-bookinfo-ratings-v-unavailable:latest" --build-arg service_version=v-unavailable . - docker build --pull -t "${PREFIX}/examples-bookinfo-ratings-v-unhealthy:${VERSION}" -t "${PREFIX}/examples-bookinfo-ratings-v-unhealthy:latest" --build-arg service_version=v-unhealthy . -popd - -pushd "$SCRIPTDIR/mysql" - docker build --pull -t "${PREFIX}/examples-bookinfo-mysqldb:${VERSION}" -t "${PREFIX}/examples-bookinfo-mysqldb:latest" . -popd - -pushd "$SCRIPTDIR/mongodb" - docker build --pull -t "${PREFIX}/examples-bookinfo-mongodb:${VERSION}" -t "${PREFIX}/examples-bookinfo-mongodb:latest" . -popd diff --git a/samples/bookinfo/src/details/Dockerfile b/samples/bookinfo/src/details/Dockerfile deleted file mode 100644 index 510802594..000000000 --- a/samples/bookinfo/src/details/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM ruby:2.7.1-slim - -COPY details.rb /opt/microservices/ - -ARG service_version -ENV SERVICE_VERSION ${service_version:-v1} -ARG enable_external_book_service -ENV ENABLE_EXTERNAL_BOOK_SERVICE ${enable_external_book_service:-false} - -EXPOSE 9080 -WORKDIR /opt/microservices - -CMD ["ruby", "details.rb", "9080"] diff --git a/samples/bookinfo/src/details/details.rb b/samples/bookinfo/src/details/details.rb deleted file mode 100755 index 641a85510..000000000 --- a/samples/bookinfo/src/details/details.rb +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/ruby -# -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'webrick' -require 'json' -require 'net/http' - -if ARGV.length < 1 then - puts "usage: #{$PROGRAM_NAME} port" - exit(-1) -end - -port = Integer(ARGV[0]) - -server = WEBrick::HTTPServer.new :BindAddress => '*', :Port => port - -trap 'INT' do server.shutdown end - -server.mount_proc '/health' do |req, res| - res.status = 200 - res.body = {'status' => 'Details is healthy'}.to_json - res['Content-Type'] = 'application/json' -end - -server.mount_proc '/details' do |req, res| - pathParts = req.path.split('/') - headers = get_forward_headers(req) - - begin - begin - id = Integer(pathParts[-1]) - rescue - raise 'please provide numeric product id' - end - details = get_book_details(id, headers) - res.body = details.to_json - res['Content-Type'] = 'application/json' - rescue => error - res.body = {'error' => error}.to_json - res['Content-Type'] = 'application/json' - res.status = 400 - end -end - -# TODO: provide details on different books. -def get_book_details(id, headers) - if ENV['ENABLE_EXTERNAL_BOOK_SERVICE'] === 'true' then - # the ISBN of one of Comedy of Errors on the Amazon - # that has Shakespeare as the single author - isbn = '0486424618' - return fetch_details_from_external_service(isbn, id, headers) - end - - return { - 'id' => id, - 'author': 'William Shakespeare', - 'year': 1595, - 'type' => 'paperback', - 'pages' => 200, - 'publisher' => 'PublisherA', - 'language' => 'English', - 'ISBN-10' => '1234567890', - 'ISBN-13' => '123-1234567890' - } -end - -def fetch_details_from_external_service(isbn, id, headers) - uri = URI.parse('https://www.googleapis.com/books/v1/volumes?q=isbn:' + isbn) - http = Net::HTTP.new(uri.host, ENV['DO_NOT_ENCRYPT'] === 'true' ? 80:443) - http.read_timeout = 5 # seconds - - # DO_NOT_ENCRYPT is used to configure the details service to use either - # HTTP (true) or HTTPS (false, default) when calling the external service to - # retrieve the book information. - # - # Unless this environment variable is set to true, the app will use TLS (HTTPS) - # to access external services. - unless ENV['DO_NOT_ENCRYPT'] === 'true' then - http.use_ssl = true - end - - request = Net::HTTP::Get.new(uri.request_uri) - headers.each { |header, value| request[header] = value } - - response = http.request(request) - - json = JSON.parse(response.body) - book = json['items'][0]['volumeInfo'] - - language = book['language'] === 'en'? 'English' : 'unknown' - type = book['printType'] === 'BOOK'? 'paperback' : 'unknown' - isbn10 = get_isbn(book, 'ISBN_10') - isbn13 = get_isbn(book, 'ISBN_13') - - return { - 'id' => id, - 'author': book['authors'][0], - 'year': book['publishedDate'], - 'type' => type, - 'pages' => book['pageCount'], - 'publisher' => book['publisher'], - 'language' => language, - 'ISBN-10' => isbn10, - 'ISBN-13' => isbn13 - } - -end - -def get_isbn(book, isbn_type) - isbn_dentifiers = book['industryIdentifiers'].select do |identifier| - identifier['type'] === isbn_type - end - - return isbn_dentifiers[0]['identifier'] -end - -def get_forward_headers(request) - headers = {} - - # Keep this in sync with the headers in productpage and reviews. - incoming_headers = [ - # All applications should propagate x-request-id. This header is - # included in access log statements and is used for consistent trace - # sampling and log sampling decisions in Istio. - 'x-request-id', - - # Lightstep tracing header. Propagate this if you use lightstep tracing - # in Istio (see - # https://istio.io/latest/docs/tasks/observability/distributed-tracing/lightstep/) - # Note: this should probably be changed to use B3 or W3C TRACE_CONTEXT. - # Lightstep recommends using B3 or TRACE_CONTEXT and most application - # libraries from lightstep do not support x-ot-span-context. - 'x-ot-span-context', - - # Datadog tracing header. Propagate these headers if you use Datadog - # tracing. - 'x-datadog-trace-id', - 'x-datadog-parent-id', - 'x-datadog-sampling-priority', - - # W3C Trace Context. Compatible with OpenCensusAgent and Stackdriver Istio - # configurations. - 'traceparent', - 'tracestate', - - # Cloud trace context. Compatible with OpenCensusAgent and Stackdriver Istio - # configurations. - 'x-cloud-trace-context', - - # Grpc binary trace context. Compatible with OpenCensusAgent nad - # Stackdriver Istio configurations. - 'grpc-trace-bin', - - # b3 trace headers. Compatible with Zipkin, OpenCensusAgent, and - # Stackdriver Istio configurations. - 'x-b3-traceid', - 'x-b3-spanid', - 'x-b3-parentspanid', - 'x-b3-sampled', - 'x-b3-flags', - - # Application-specific headers to forward. - 'end-user', - 'user-agent', - ] - - request.each do |header, value| - if incoming_headers.include? header then - headers[header] = value - end - end - - return headers -end - -server.start diff --git a/samples/bookinfo/src/mongodb/Dockerfile b/samples/bookinfo/src/mongodb/Dockerfile deleted file mode 100644 index 19c05d7c1..000000000 --- a/samples/bookinfo/src/mongodb/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM mongo:4.0.19-xenial -WORKDIR /app/data/ -COPY ratings_data.json /app/data/ -COPY script.sh /docker-entrypoint-initdb.d/ -RUN chmod +x /docker-entrypoint-initdb.d/script.sh diff --git a/samples/bookinfo/src/mongodb/ratings_data.json b/samples/bookinfo/src/mongodb/ratings_data.json deleted file mode 100644 index b4563b50c..000000000 --- a/samples/bookinfo/src/mongodb/ratings_data.json +++ /dev/null @@ -1,2 +0,0 @@ -{rating: 5} -{rating: 4} diff --git a/samples/bookinfo/src/mongodb/script.sh b/samples/bookinfo/src/mongodb/script.sh deleted file mode 100644 index 7e230ee5a..000000000 --- a/samples/bookinfo/src/mongodb/script.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh - -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -e -mongoimport --host localhost --db test --collection ratings --drop --file /app/data/ratings_data.json diff --git a/samples/bookinfo/src/mysql/Dockerfile b/samples/bookinfo/src/mysql/Dockerfile deleted file mode 100644 index 7d1c5453d..000000000 --- a/samples/bookinfo/src/mysql/Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM mysql:8.0.20 -# MYSQL_ROOT_PASSWORD must be supplied as an env var - -COPY ./mysqldb-init.sql /docker-entrypoint-initdb.d diff --git a/samples/bookinfo/src/mysql/mysqldb-init.sql b/samples/bookinfo/src/mysql/mysqldb-init.sql deleted file mode 100644 index dea37e6f8..000000000 --- a/samples/bookinfo/src/mysql/mysqldb-init.sql +++ /dev/null @@ -1,13 +0,0 @@ -# Initialize a mysql db with a 'test' db and be able test productpage with it. -# mysql -h 127.0.0.1 -ppassword < mysqldb-init.sql - -CREATE DATABASE test; -USE test; - -CREATE TABLE `ratings` ( - `ReviewID` INT NOT NULL, - `Rating` INT, - PRIMARY KEY (`ReviewID`) -); -INSERT INTO ratings (ReviewID, Rating) VALUES (1, 5); -INSERT INTO ratings (ReviewID, Rating) VALUES (2, 4); diff --git a/samples/bookinfo/src/productpage/Dockerfile b/samples/bookinfo/src/productpage/Dockerfile deleted file mode 100644 index a6c97402c..000000000 --- a/samples/bookinfo/src/productpage/Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM python:3.7.7-slim - -WORKDIR / -COPY requirements.txt ./ -RUN pip install --no-cache-dir -r requirements.txt - -COPY test-requirements.txt ./ -RUN pip install --no-cache-dir -r test-requirements.txt - -COPY productpage.py /opt/microservices/ -COPY tests/unit/* /opt/microservices/ -COPY templates /opt/microservices/templates -COPY static /opt/microservices/static -COPY requirements.txt /opt/microservices/ - -ARG flood_factor -ENV FLOOD_FACTOR ${flood_factor:-0} - -EXPOSE 9080 -WORKDIR /opt/microservices -RUN python -m unittest discover - -CMD ["python", "productpage.py", "9080"] diff --git a/samples/bookinfo/src/productpage/productpage.py b/samples/bookinfo/src/productpage/productpage.py deleted file mode 100755 index 851540911..000000000 --- a/samples/bookinfo/src/productpage/productpage.py +++ /dev/null @@ -1,431 +0,0 @@ -#!/usr/bin/python -# -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -from __future__ import print_function -from flask_bootstrap import Bootstrap -from flask import Flask, request, session, render_template, redirect, url_for -from flask import _request_ctx_stack as stack -from jaeger_client import Tracer, ConstSampler -from jaeger_client.reporter import NullReporter -from jaeger_client.codecs import B3Codec -from opentracing.ext import tags -from opentracing.propagation import Format -from opentracing_instrumentation.request_context import get_current_span, span_in_context -import simplejson as json -import requests -import sys -from json2html import * -import logging -import os -import asyncio - -# These two lines enable debugging at httplib level (requests->urllib3->http.client) -# You will see the REQUEST, including HEADERS and DATA, and RESPONSE with HEADERS but without DATA. -# The only thing missing will be the response.body which is not logged. -try: - import http.client as http_client -except ImportError: - # Python 2 - import httplib as http_client -http_client.HTTPConnection.debuglevel = 1 - -app = Flask(__name__) -logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) -requests_log = logging.getLogger("requests.packages.urllib3") -requests_log.setLevel(logging.DEBUG) -requests_log.propagate = True -app.logger.addHandler(logging.StreamHandler(sys.stdout)) -app.logger.setLevel(logging.DEBUG) - -# Set the secret key to some random bytes. Keep this really secret! -app.secret_key = b'_5#y2L"F4Q8z\n\xec]/' - -Bootstrap(app) - -servicesDomain = "" if (os.environ.get("SERVICES_DOMAIN") is None) else "." + os.environ.get("SERVICES_DOMAIN") -detailsHostname = "details" if (os.environ.get("DETAILS_HOSTNAME") is None) else os.environ.get("DETAILS_HOSTNAME") -ratingsHostname = "ratings" if (os.environ.get("RATINGS_HOSTNAME") is None) else os.environ.get("RATINGS_HOSTNAME") -reviewsHostname = "reviews" if (os.environ.get("REVIEWS_HOSTNAME") is None) else os.environ.get("REVIEWS_HOSTNAME") - -flood_factor = 0 if (os.environ.get("FLOOD_FACTOR") is None) else int(os.environ.get("FLOOD_FACTOR")) - -details = { - "name": "http://{0}{1}:9080".format(detailsHostname, servicesDomain), - "endpoint": "details", - "children": [] -} - -ratings = { - "name": "http://{0}{1}:9080".format(ratingsHostname, servicesDomain), - "endpoint": "ratings", - "children": [] -} - -reviews = { - "name": "http://{0}{1}:9080".format(reviewsHostname, servicesDomain), - "endpoint": "reviews", - "children": [ratings] -} - -productpage = { - "name": "http://{0}{1}:9080".format(detailsHostname, servicesDomain), - "endpoint": "details", - "children": [details, reviews] -} - -service_dict = { - "productpage": productpage, - "details": details, - "reviews": reviews, -} - -# A note on distributed tracing: -# -# Although Istio proxies are able to automatically send spans, they need some -# hints to tie together the entire trace. Applications need to propagate the -# appropriate HTTP headers so that when the proxies send span information, the -# spans can be correlated correctly into a single trace. -# -# To do this, an application needs to collect and propagate headers from the -# incoming request to any outgoing requests. The choice of headers to propagate -# is determined by the trace configuration used. See getForwardHeaders for -# the different header options. -# -# This example code uses OpenTracing (http://opentracing.io/) to propagate -# the 'b3' (zipkin) headers. Using OpenTracing for this is not a requirement. -# Using OpenTracing allows you to add application-specific tracing later on, -# but you can just manually forward the headers if you prefer. -# -# The OpenTracing example here is very basic. It only forwards headers. It is -# intended as a reference to help people get started, eg how to create spans, -# extract/inject context, etc. - -# A very basic OpenTracing tracer (with null reporter) -tracer = Tracer( - one_span_per_rpc=True, - service_name='productpage', - reporter=NullReporter(), - sampler=ConstSampler(decision=True), - extra_codecs={Format.HTTP_HEADERS: B3Codec()} -) - - -def trace(): - ''' - Function decorator that creates opentracing span from incoming b3 headers - ''' - def decorator(f): - def wrapper(*args, **kwargs): - request = stack.top.request - try: - # Create a new span context, reading in values (traceid, - # spanid, etc) from the incoming x-b3-*** headers. - span_ctx = tracer.extract( - Format.HTTP_HEADERS, - dict(request.headers) - ) - # Note: this tag means that the span will *not* be - # a child span. It will use the incoming traceid and - # spanid. We do this to propagate the headers verbatim. - rpc_tag = {tags.SPAN_KIND: tags.SPAN_KIND_RPC_SERVER} - span = tracer.start_span( - operation_name='op', child_of=span_ctx, tags=rpc_tag - ) - except Exception as e: - # We failed to create a context, possibly due to no - # incoming x-b3-*** headers. Start a fresh span. - # Note: This is a fallback only, and will create fresh headers, - # not propagate headers. - span = tracer.start_span('op') - with span_in_context(span): - r = f(*args, **kwargs) - return r - wrapper.__name__ = f.__name__ - return wrapper - return decorator - - -def getForwardHeaders(request): - headers = {} - - # x-b3-*** headers can be populated using the opentracing span - span = get_current_span() - carrier = {} - tracer.inject( - span_context=span.context, - format=Format.HTTP_HEADERS, - carrier=carrier) - - headers.update(carrier) - - # We handle other (non x-b3-***) headers manually - if 'user' in session: - headers['end-user'] = session['user'] - - # Keep this in sync with the headers in details and reviews. - incoming_headers = [ - # All applications should propagate x-request-id. This header is - # included in access log statements and is used for consistent trace - # sampling and log sampling decisions in Istio. - 'x-request-id', - - # Lightstep tracing header. Propagate this if you use lightstep tracing - # in Istio (see - # https://istio.io/latest/docs/tasks/observability/distributed-tracing/lightstep/) - # Note: this should probably be changed to use B3 or W3C TRACE_CONTEXT. - # Lightstep recommends using B3 or TRACE_CONTEXT and most application - # libraries from lightstep do not support x-ot-span-context. - 'x-ot-span-context', - - # Datadog tracing header. Propagate these headers if you use Datadog - # tracing. - 'x-datadog-trace-id', - 'x-datadog-parent-id', - 'x-datadog-sampling-priority', - - # W3C Trace Context. Compatible with OpenCensusAgent and Stackdriver Istio - # configurations. - 'traceparent', - 'tracestate', - - # Cloud trace context. Compatible with OpenCensusAgent and Stackdriver Istio - # configurations. - 'x-cloud-trace-context', - - # Grpc binary trace context. Compatible with OpenCensusAgent nad - # Stackdriver Istio configurations. - 'grpc-trace-bin', - - # b3 trace headers. Compatible with Zipkin, OpenCensusAgent, and - # Stackdriver Istio configurations. Commented out since they are - # propagated by the OpenTracing tracer above. - # 'x-b3-traceid', - # 'x-b3-spanid', - # 'x-b3-parentspanid', - # 'x-b3-sampled', - # 'x-b3-flags', - - # Application-specific headers to forward. - 'user-agent', - ] - # For Zipkin, always propagate b3 headers. - # For Lightstep, always propagate the x-ot-span-context header. - # For Datadog, propagate the corresponding datadog headers. - # For OpenCensusAgent and Stackdriver configurations, you can choose any - # set of compatible headers to propagate within your application. For - # example, you can propagate b3 headers or W3C trace context headers with - # the same result. This can also allow you to translate between context - # propagation mechanisms between different applications. - - for ihdr in incoming_headers: - val = request.headers.get(ihdr) - if val is not None: - headers[ihdr] = val - - return headers - - -# The UI: -@app.route('/') -@app.route('/index.html') -def index(): - """ Display productpage with normal user and test user buttons""" - global productpage - - table = json2html.convert(json=json.dumps(productpage), - table_attributes="class=\"table table-condensed table-bordered table-hover\"") - - return render_template('index.html', serviceTable=table) - - -@app.route('/health') -def health(): - return 'Product page is healthy' - - -@app.route('/login', methods=['POST']) -def login(): - user = request.values.get('username') - response = app.make_response(redirect(request.referrer)) - session['user'] = user - return response - - -@app.route('/logout', methods=['GET']) -def logout(): - response = app.make_response(redirect(request.referrer)) - session.pop('user', None) - return response - -# a helper function for asyncio.gather, does not return a value - - -async def getProductReviewsIgnoreResponse(product_id, headers): - getProductReviews(product_id, headers) - -# flood reviews with unnecessary requests to demonstrate Istio rate limiting, asynchoronously - - -async def floodReviewsAsynchronously(product_id, headers): - # the response is disregarded - await asyncio.gather(*(getProductReviewsIgnoreResponse(product_id, headers) for _ in range(flood_factor))) - -# flood reviews with unnecessary requests to demonstrate Istio rate limiting - - -def floodReviews(product_id, headers): - loop = asyncio.new_event_loop() - loop.run_until_complete(floodReviewsAsynchronously(product_id, headers)) - loop.close() - - -@app.route('/productpage') -@trace() -def front(): - product_id = 0 # TODO: replace default value - headers = getForwardHeaders(request) - user = session.get('user', '') - product = getProduct(product_id) - detailsStatus, details = getProductDetails(product_id, headers) - - if flood_factor > 0: - floodReviews(product_id, headers) - - reviewsStatus, reviews = getProductReviews(product_id, headers) - return render_template( - 'productpage.html', - detailsStatus=detailsStatus, - reviewsStatus=reviewsStatus, - product=product, - details=details, - reviews=reviews, - user=user) - - -# The API: -@app.route('/api/v1/products') -def productsRoute(): - return json.dumps(getProducts()), 200, {'Content-Type': 'application/json'} - - -@app.route('/api/v1/products/') -@trace() -def productRoute(product_id): - headers = getForwardHeaders(request) - status, details = getProductDetails(product_id, headers) - return json.dumps(details), status, {'Content-Type': 'application/json'} - - -@app.route('/api/v1/products//reviews') -@trace() -def reviewsRoute(product_id): - headers = getForwardHeaders(request) - status, reviews = getProductReviews(product_id, headers) - return json.dumps(reviews), status, {'Content-Type': 'application/json'} - - -@app.route('/api/v1/products//ratings') -@trace() -def ratingsRoute(product_id): - headers = getForwardHeaders(request) - status, ratings = getProductRatings(product_id, headers) - return json.dumps(ratings), status, {'Content-Type': 'application/json'} - - -# Data providers: -def getProducts(): - return [ - { - 'id': 0, - 'title': 'The Comedy of Errors', - 'descriptionHtml': 'Wikipedia Summary: The Comedy of Errors is one of William Shakespeare\'s early plays. It is his shortest and one of his most farcical comedies, with a major part of the humour coming from slapstick and mistaken identity, in addition to puns and word play.' - } - ] - - -def getProduct(product_id): - products = getProducts() - if product_id + 1 > len(products): - return None - else: - return products[product_id] - - -def getProductDetails(product_id, headers): - try: - url = details['name'] + "/" + details['endpoint'] + "/" + str(product_id) - res = requests.get(url, headers=headers, timeout=3.0) - except BaseException: - res = None - if res and res.status_code == 200: - return 200, res.json() - else: - status = res.status_code if res is not None and res.status_code else 500 - return status, {'error': 'Sorry, product details are currently unavailable for this book.'} - - -def getProductReviews(product_id, headers): - # Do not remove. Bug introduced explicitly for illustration in fault injection task - # TODO: Figure out how to achieve the same effect using Envoy retries/timeouts - for _ in range(2): - try: - url = reviews['name'] + "/" + reviews['endpoint'] + "/" + str(product_id) - res = requests.get(url, headers=headers, timeout=3.0) - except BaseException: - res = None - if res and res.status_code == 200: - return 200, res.json() - status = res.status_code if res is not None and res.status_code else 500 - return status, {'error': 'Sorry, product reviews are currently unavailable for this book.'} - - -def getProductRatings(product_id, headers): - try: - url = ratings['name'] + "/" + ratings['endpoint'] + "/" + str(product_id) - res = requests.get(url, headers=headers, timeout=3.0) - except BaseException: - res = None - if res and res.status_code == 200: - return 200, res.json() - else: - status = res.status_code if res is not None and res.status_code else 500 - return status, {'error': 'Sorry, product ratings are currently unavailable for this book.'} - - -class Writer(object): - def __init__(self, filename): - self.file = open(filename, 'w') - - def write(self, data): - self.file.write(data) - - def flush(self): - self.file.flush() - - -if __name__ == '__main__': - if len(sys.argv) < 2: - logging.error("usage: %s port" % (sys.argv[0])) - sys.exit(-1) - - p = int(sys.argv[1]) - logging.info("start at port %s" % (p)) - # Make it compatible with IPv6 if Linux - if sys.platform == "linux": - app.run(host='::', port=p, debug=True, threaded=True) - else: - app.run(host='0.0.0.0', port=p, debug=True, threaded=True) diff --git a/samples/bookinfo/src/productpage/requirements.txt b/samples/bookinfo/src/productpage/requirements.txt deleted file mode 100644 index dbccbd2c5..000000000 --- a/samples/bookinfo/src/productpage/requirements.txt +++ /dev/null @@ -1,31 +0,0 @@ -certifi==2023.7.22 -chardet==3.0.4 -Click==7.0 -contextlib2==0.5.5 -dominate==2.3.5 -Flask==2.3.2 -Flask-Bootstrap==3.3.7.1 -Flask-JSON==0.3.3 -future==0.17.1 -futures==3.1.1 -gevent==23.9.1 -greenlet==0.4.15 -idna==3.7 -itsdangerous==1.1.0 -jaeger-client==3.13.0 -Jinja2==3.1.3 -json2html==1.2.1 -MarkupSafe==0.23 -nose==1.3.7 -opentracing==1.2.2 -opentracing-instrumentation==2.4.3 -requests==2.31.0 -simplejson==3.16.0 -six==1.12.0 -threadloop==1.0.2 -thrift==0.11.0 -tornado==6.3.3 -urllib3==1.26.18 -visitor==0.1.3 -Werkzeug==3.0.1 -wrapt==1.11.1 diff --git a/samples/bookinfo/src/productpage/static/bootstrap/css/bootstrap-theme.css b/samples/bookinfo/src/productpage/static/bootstrap/css/bootstrap-theme.css deleted file mode 100644 index c19cd5c4b..000000000 --- a/samples/bookinfo/src/productpage/static/bootstrap/css/bootstrap-theme.css +++ /dev/null @@ -1,587 +0,0 @@ -/*! - * Bootstrap v3.3.5 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -.btn-default, -.btn-primary, -.btn-success, -.btn-info, -.btn-warning, -.btn-danger { - text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); -} -.btn-default:active, -.btn-primary:active, -.btn-success:active, -.btn-info:active, -.btn-warning:active, -.btn-danger:active, -.btn-default.active, -.btn-primary.active, -.btn-success.active, -.btn-info.active, -.btn-warning.active, -.btn-danger.active { - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn-default.disabled, -.btn-primary.disabled, -.btn-success.disabled, -.btn-info.disabled, -.btn-warning.disabled, -.btn-danger.disabled, -.btn-default[disabled], -.btn-primary[disabled], -.btn-success[disabled], -.btn-info[disabled], -.btn-warning[disabled], -.btn-danger[disabled], -fieldset[disabled] .btn-default, -fieldset[disabled] .btn-primary, -fieldset[disabled] .btn-success, -fieldset[disabled] .btn-info, -fieldset[disabled] .btn-warning, -fieldset[disabled] .btn-danger { - -webkit-box-shadow: none; - box-shadow: none; -} -.btn-default .badge, -.btn-primary .badge, -.btn-success .badge, -.btn-info .badge, -.btn-warning .badge, -.btn-danger .badge { - text-shadow: none; -} -.btn:active, -.btn.active { - background-image: none; -} -.btn-default { - text-shadow: 0 1px 0 #fff; - background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); - background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); - background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - background-repeat: repeat-x; - border-color: #dbdbdb; - border-color: #ccc; -} -.btn-default:hover, -.btn-default:focus { - background-color: #e0e0e0; - background-position: 0 -15px; -} -.btn-default:active, -.btn-default.active { - background-color: #e0e0e0; - border-color: #dbdbdb; -} -.btn-default.disabled, -.btn-default[disabled], -fieldset[disabled] .btn-default, -.btn-default.disabled:hover, -.btn-default[disabled]:hover, -fieldset[disabled] .btn-default:hover, -.btn-default.disabled:focus, -.btn-default[disabled]:focus, -fieldset[disabled] .btn-default:focus, -.btn-default.disabled.focus, -.btn-default[disabled].focus, -fieldset[disabled] .btn-default.focus, -.btn-default.disabled:active, -.btn-default[disabled]:active, -fieldset[disabled] .btn-default:active, -.btn-default.disabled.active, -.btn-default[disabled].active, -fieldset[disabled] .btn-default.active { - background-color: #e0e0e0; - background-image: none; -} -.btn-primary { - background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); - background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); - background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - background-repeat: repeat-x; - border-color: #245580; -} -.btn-primary:hover, -.btn-primary:focus { - background-color: #265a88; - background-position: 0 -15px; -} -.btn-primary:active, -.btn-primary.active { - background-color: #265a88; - border-color: #245580; -} -.btn-primary.disabled, -.btn-primary[disabled], -fieldset[disabled] .btn-primary, -.btn-primary.disabled:hover, -.btn-primary[disabled]:hover, -fieldset[disabled] .btn-primary:hover, -.btn-primary.disabled:focus, -.btn-primary[disabled]:focus, -fieldset[disabled] .btn-primary:focus, -.btn-primary.disabled.focus, -.btn-primary[disabled].focus, -fieldset[disabled] .btn-primary.focus, -.btn-primary.disabled:active, -.btn-primary[disabled]:active, -fieldset[disabled] .btn-primary:active, -.btn-primary.disabled.active, -.btn-primary[disabled].active, -fieldset[disabled] .btn-primary.active { - background-color: #265a88; - background-image: none; -} -.btn-success { - background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); - background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); - background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - background-repeat: repeat-x; - border-color: #3e8f3e; -} -.btn-success:hover, -.btn-success:focus { - background-color: #419641; - background-position: 0 -15px; -} -.btn-success:active, -.btn-success.active { - background-color: #419641; - border-color: #3e8f3e; -} -.btn-success.disabled, -.btn-success[disabled], -fieldset[disabled] .btn-success, -.btn-success.disabled:hover, -.btn-success[disabled]:hover, -fieldset[disabled] .btn-success:hover, -.btn-success.disabled:focus, -.btn-success[disabled]:focus, -fieldset[disabled] .btn-success:focus, -.btn-success.disabled.focus, -.btn-success[disabled].focus, -fieldset[disabled] .btn-success.focus, -.btn-success.disabled:active, -.btn-success[disabled]:active, -fieldset[disabled] .btn-success:active, -.btn-success.disabled.active, -.btn-success[disabled].active, -fieldset[disabled] .btn-success.active { - background-color: #419641; - background-image: none; -} -.btn-info { - background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); - background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); - background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - background-repeat: repeat-x; - border-color: #28a4c9; -} -.btn-info:hover, -.btn-info:focus { - background-color: #2aabd2; - background-position: 0 -15px; -} -.btn-info:active, -.btn-info.active { - background-color: #2aabd2; - border-color: #28a4c9; -} -.btn-info.disabled, -.btn-info[disabled], -fieldset[disabled] .btn-info, -.btn-info.disabled:hover, -.btn-info[disabled]:hover, -fieldset[disabled] .btn-info:hover, -.btn-info.disabled:focus, -.btn-info[disabled]:focus, -fieldset[disabled] .btn-info:focus, -.btn-info.disabled.focus, -.btn-info[disabled].focus, -fieldset[disabled] .btn-info.focus, -.btn-info.disabled:active, -.btn-info[disabled]:active, -fieldset[disabled] .btn-info:active, -.btn-info.disabled.active, -.btn-info[disabled].active, -fieldset[disabled] .btn-info.active { - background-color: #2aabd2; - background-image: none; -} -.btn-warning { - background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); - background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); - background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - background-repeat: repeat-x; - border-color: #e38d13; -} -.btn-warning:hover, -.btn-warning:focus { - background-color: #eb9316; - background-position: 0 -15px; -} -.btn-warning:active, -.btn-warning.active { - background-color: #eb9316; - border-color: #e38d13; -} -.btn-warning.disabled, -.btn-warning[disabled], -fieldset[disabled] .btn-warning, -.btn-warning.disabled:hover, -.btn-warning[disabled]:hover, -fieldset[disabled] .btn-warning:hover, -.btn-warning.disabled:focus, -.btn-warning[disabled]:focus, -fieldset[disabled] .btn-warning:focus, -.btn-warning.disabled.focus, -.btn-warning[disabled].focus, -fieldset[disabled] .btn-warning.focus, -.btn-warning.disabled:active, -.btn-warning[disabled]:active, -fieldset[disabled] .btn-warning:active, -.btn-warning.disabled.active, -.btn-warning[disabled].active, -fieldset[disabled] .btn-warning.active { - background-color: #eb9316; - background-image: none; -} -.btn-danger { - background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); - background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); - background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - background-repeat: repeat-x; - border-color: #b92c28; -} -.btn-danger:hover, -.btn-danger:focus { - background-color: #c12e2a; - background-position: 0 -15px; -} -.btn-danger:active, -.btn-danger.active { - background-color: #c12e2a; - border-color: #b92c28; -} -.btn-danger.disabled, -.btn-danger[disabled], -fieldset[disabled] .btn-danger, -.btn-danger.disabled:hover, -.btn-danger[disabled]:hover, -fieldset[disabled] .btn-danger:hover, -.btn-danger.disabled:focus, -.btn-danger[disabled]:focus, -fieldset[disabled] .btn-danger:focus, -.btn-danger.disabled.focus, -.btn-danger[disabled].focus, -fieldset[disabled] .btn-danger.focus, -.btn-danger.disabled:active, -.btn-danger[disabled]:active, -fieldset[disabled] .btn-danger:active, -.btn-danger.disabled.active, -.btn-danger[disabled].active, -fieldset[disabled] .btn-danger.active { - background-color: #c12e2a; - background-image: none; -} -.thumbnail, -.img-thumbnail { - -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); - box-shadow: 0 1px 2px rgba(0, 0, 0, .075); -} -.dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus { - background-color: #e8e8e8; - background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); - background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); - background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); - background-repeat: repeat-x; -} -.dropdown-menu > .active > a, -.dropdown-menu > .active > a:hover, -.dropdown-menu > .active > a:focus { - background-color: #2e6da4; - background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); - background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); - background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); - background-repeat: repeat-x; -} -.navbar-default { - background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); - background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); - background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - background-repeat: repeat-x; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); -} -.navbar-default .navbar-nav > .open > a, -.navbar-default .navbar-nav > .active > a { - background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); - background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); - background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); - background-repeat: repeat-x; - -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); - box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); -} -.navbar-brand, -.navbar-nav > li > a { - text-shadow: 0 1px 0 rgba(255, 255, 255, .25); -} -.navbar-inverse { - background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); - background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); - background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - background-repeat: repeat-x; - border-radius: 4px; -} -.navbar-inverse .navbar-nav > .open > a, -.navbar-inverse .navbar-nav > .active > a { - background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); - background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); - background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); - background-repeat: repeat-x; - -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); - box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); -} -.navbar-inverse .navbar-brand, -.navbar-inverse .navbar-nav > li > a { - text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); -} -.navbar-static-top, -.navbar-fixed-top, -.navbar-fixed-bottom { - border-radius: 0; -} -@media (max-width: 767px) { - .navbar .navbar-nav .open .dropdown-menu > .active > a, - .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #fff; - background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); - background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); - background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); - background-repeat: repeat-x; - } -} -.alert { - text-shadow: 0 1px 0 rgba(255, 255, 255, .2); - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); -} -.alert-success { - background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); - background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); - background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); - background-repeat: repeat-x; - border-color: #b2dba1; -} -.alert-info { - background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); - background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); - background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); - background-repeat: repeat-x; - border-color: #9acfea; -} -.alert-warning { - background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); - background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); - background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); - background-repeat: repeat-x; - border-color: #f5e79e; -} -.alert-danger { - background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); - background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); - background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); - background-repeat: repeat-x; - border-color: #dca7a7; -} -.progress { - background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); - background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); - background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); - background-repeat: repeat-x; -} -.progress-bar { - background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); - background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); - background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); - background-repeat: repeat-x; -} -.progress-bar-success { - background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); - background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); - background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); - background-repeat: repeat-x; -} -.progress-bar-info { - background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); - background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); - background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); - background-repeat: repeat-x; -} -.progress-bar-warning { - background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); - background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); - background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); - background-repeat: repeat-x; -} -.progress-bar-danger { - background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); - background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); - background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); - background-repeat: repeat-x; -} -.progress-bar-striped { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.list-group { - border-radius: 4px; - -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); - box-shadow: 0 1px 2px rgba(0, 0, 0, .075); -} -.list-group-item.active, -.list-group-item.active:hover, -.list-group-item.active:focus { - text-shadow: 0 -1px 0 #286090; - background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); - background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); - background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); - background-repeat: repeat-x; - border-color: #2b669a; -} -.list-group-item.active .badge, -.list-group-item.active:hover .badge, -.list-group-item.active:focus .badge { - text-shadow: none; -} -.panel { - -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); - box-shadow: 0 1px 2px rgba(0, 0, 0, .05); -} -.panel-default > .panel-heading { - background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); - background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); - background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); - background-repeat: repeat-x; -} -.panel-primary > .panel-heading { - background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); - background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); - background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); - background-repeat: repeat-x; -} -.panel-success > .panel-heading { - background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); - background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); - background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); - background-repeat: repeat-x; -} -.panel-info > .panel-heading { - background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); - background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); - background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); - background-repeat: repeat-x; -} -.panel-warning > .panel-heading { - background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); - background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); - background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); - background-repeat: repeat-x; -} -.panel-danger > .panel-heading { - background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); - background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); - background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); - background-repeat: repeat-x; -} -.well { - background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); - background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); - background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); - background-repeat: repeat-x; - border-color: #dcdcdc; - -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); -} -/*# sourceMappingURL=bootstrap-theme.css.map */ diff --git a/samples/bookinfo/src/productpage/static/bootstrap/css/bootstrap-theme.css.map b/samples/bookinfo/src/productpage/static/bootstrap/css/bootstrap-theme.css.map deleted file mode 100644 index 753531147..000000000 --- a/samples/bookinfo/src/productpage/static/bootstrap/css/bootstrap-theme.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["bootstrap-theme.css","less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":"AAAA;;;;GAIG;ACeH;;;;;;EAME,yCAAA;EC2CA,4FAAA;EACQ,oFAAA;CFvDT;ACgBC;;;;;;;;;;;;ECsCA,yDAAA;EACQ,iDAAA;CFxCT;ACMC;;;;;;;;;;;;;;;;;;ECiCA,yBAAA;EACQ,iBAAA;CFnBT;AC/BD;;;;;;EAuBI,kBAAA;CDgBH;ACyBC;;EAEE,uBAAA;CDvBH;AC4BD;EErEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;EAuC2C,0BAAA;EAA2B,mBAAA;CDjBvE;ACpBC;;EAEE,0BAAA;EACA,6BAAA;CDsBH;ACnBC;;EAEE,0BAAA;EACA,sBAAA;CDqBH;ACfG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6BL;ACbD;EEtEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8DD;AC5DC;;EAEE,0BAAA;EACA,6BAAA;CD8DH;AC3DC;;EAEE,0BAAA;EACA,sBAAA;CD6DH;ACvDG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqEL;ACpDD;EEvEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CDsGD;ACpGC;;EAEE,0BAAA;EACA,6BAAA;CDsGH;ACnGC;;EAEE,0BAAA;EACA,sBAAA;CDqGH;AC/FG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6GL;AC3FD;EExEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8ID;AC5IC;;EAEE,0BAAA;EACA,6BAAA;CD8IH;AC3IC;;EAEE,0BAAA;EACA,sBAAA;CD6IH;ACvIG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqJL;AClID;EEzEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CDsLD;ACpLC;;EAEE,0BAAA;EACA,6BAAA;CDsLH;ACnLC;;EAEE,0BAAA;EACA,sBAAA;CDqLH;AC/KG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6LL;ACzKD;EE1EI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8ND;AC5NC;;EAEE,0BAAA;EACA,6BAAA;CD8NH;AC3NC;;EAEE,0BAAA;EACA,sBAAA;CD6NH;ACvNG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqOL;AC1MD;;EClCE,mDAAA;EACQ,2CAAA;CFgPT;ACrMD;;EE3FI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF0FF,0BAAA;CD2MD;ACzMD;;;EEhGI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFgGF,0BAAA;CD+MD;ACtMD;EE7GI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ECnBF,oEAAA;EH+HA,mBAAA;ECjEA,4FAAA;EACQ,oFAAA;CF8QT;ACjND;;EE7GI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ED2CF,yDAAA;EACQ,iDAAA;CFwRT;AC9MD;;EAEE,+CAAA;CDgND;AC5MD;EEhII,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ECnBF,oEAAA;EHkJA,mBAAA;CDkND;ACrND;;EEhII,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ED2CF,wDAAA;EACQ,gDAAA;CF+ST;AC/ND;;EAYI,0CAAA;CDuNH;AClND;;;EAGE,iBAAA;CDoND;AC/LD;EAfI;;;IAGE,YAAA;IE7JF,yEAAA;IACA,oEAAA;IACA,8FAAA;IAAA,uEAAA;IACA,4BAAA;IACA,uHAAA;GH+WD;CACF;AC3MD;EACE,8CAAA;EC3HA,2FAAA;EACQ,mFAAA;CFyUT;ACnMD;EEtLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CD+MD;AC1MD;EEvLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CDuND;ACjND;EExLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CD+ND;ACxND;EEzLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CDuOD;ACxND;EEjMI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH4ZH;ACrND;EE3MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHmaH;AC3ND;EE5MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH0aH;ACjOD;EE7MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHibH;ACvOD;EE9MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHwbH;AC7OD;EE/MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH+bH;AChPD;EElLI,8MAAA;EACA,yMAAA;EACA,sMAAA;CHqaH;AC5OD;EACE,mBAAA;EC9KA,mDAAA;EACQ,2CAAA;CF6ZT;AC7OD;;;EAGE,8BAAA;EEnOE,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFiOF,sBAAA;CDmPD;ACxPD;;;EAQI,kBAAA;CDqPH;AC3OD;ECnME,kDAAA;EACQ,0CAAA;CFibT;ACrOD;EE5PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHoeH;AC3OD;EE7PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH2eH;ACjPD;EE9PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHkfH;ACvPD;EE/PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHyfH;AC7PD;EEhQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHggBH;ACnQD;EEjQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHugBH;ACnQD;EExQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFsQF,sBAAA;EC3NA,0FAAA;EACQ,kFAAA;CFqeT","file":"bootstrap-theme.css","sourcesContent":["/*!\n * Bootstrap v3.3.5 (http://getbootstrap.com)\n * Copyright 2011-2015 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.btn-default:active,\n.btn-primary:active,\n.btn-success:active,\n.btn-info:active,\n.btn-warning:active,\n.btn-danger:active,\n.btn-default.active,\n.btn-primary.active,\n.btn-success.active,\n.btn-info.active,\n.btn-warning.active,\n.btn-danger.active {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-default.disabled,\n.btn-primary.disabled,\n.btn-success.disabled,\n.btn-info.disabled,\n.btn-warning.disabled,\n.btn-danger.disabled,\n.btn-default[disabled],\n.btn-primary[disabled],\n.btn-success[disabled],\n.btn-info[disabled],\n.btn-warning[disabled],\n.btn-danger[disabled],\nfieldset[disabled] .btn-default,\nfieldset[disabled] .btn-primary,\nfieldset[disabled] .btn-success,\nfieldset[disabled] .btn-info,\nfieldset[disabled] .btn-warning,\nfieldset[disabled] .btn-danger {\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn-default .badge,\n.btn-primary .badge,\n.btn-success .badge,\n.btn-info .badge,\n.btn-warning .badge,\n.btn-danger .badge {\n text-shadow: none;\n}\n.btn:active,\n.btn.active {\n background-image: none;\n}\n.btn-default {\n background-image: -webkit-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);\n background-image: -o-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);\n background-image: linear-gradient(to bottom, #ffffff 0%, #e0e0e0 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #dbdbdb;\n text-shadow: 0 1px 0 #fff;\n border-color: #ccc;\n}\n.btn-default:hover,\n.btn-default:focus {\n background-color: #e0e0e0;\n background-position: 0 -15px;\n}\n.btn-default:active,\n.btn-default.active {\n background-color: #e0e0e0;\n border-color: #dbdbdb;\n}\n.btn-default.disabled,\n.btn-default[disabled],\nfieldset[disabled] .btn-default,\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus,\n.btn-default.disabled:active,\n.btn-default[disabled]:active,\nfieldset[disabled] .btn-default:active,\n.btn-default.disabled.active,\n.btn-default[disabled].active,\nfieldset[disabled] .btn-default.active {\n background-color: #e0e0e0;\n background-image: none;\n}\n.btn-primary {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #245580;\n}\n.btn-primary:hover,\n.btn-primary:focus {\n background-color: #265a88;\n background-position: 0 -15px;\n}\n.btn-primary:active,\n.btn-primary.active {\n background-color: #265a88;\n border-color: #245580;\n}\n.btn-primary.disabled,\n.btn-primary[disabled],\nfieldset[disabled] .btn-primary,\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus,\n.btn-primary.disabled:active,\n.btn-primary[disabled]:active,\nfieldset[disabled] .btn-primary:active,\n.btn-primary.disabled.active,\n.btn-primary[disabled].active,\nfieldset[disabled] .btn-primary.active {\n background-color: #265a88;\n background-image: none;\n}\n.btn-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #3e8f3e;\n}\n.btn-success:hover,\n.btn-success:focus {\n background-color: #419641;\n background-position: 0 -15px;\n}\n.btn-success:active,\n.btn-success.active {\n background-color: #419641;\n border-color: #3e8f3e;\n}\n.btn-success.disabled,\n.btn-success[disabled],\nfieldset[disabled] .btn-success,\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus,\n.btn-success.disabled:active,\n.btn-success[disabled]:active,\nfieldset[disabled] .btn-success:active,\n.btn-success.disabled.active,\n.btn-success[disabled].active,\nfieldset[disabled] .btn-success.active {\n background-color: #419641;\n background-image: none;\n}\n.btn-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #28a4c9;\n}\n.btn-info:hover,\n.btn-info:focus {\n background-color: #2aabd2;\n background-position: 0 -15px;\n}\n.btn-info:active,\n.btn-info.active {\n background-color: #2aabd2;\n border-color: #28a4c9;\n}\n.btn-info.disabled,\n.btn-info[disabled],\nfieldset[disabled] .btn-info,\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus,\n.btn-info.disabled:active,\n.btn-info[disabled]:active,\nfieldset[disabled] .btn-info:active,\n.btn-info.disabled.active,\n.btn-info[disabled].active,\nfieldset[disabled] .btn-info.active {\n background-color: #2aabd2;\n background-image: none;\n}\n.btn-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #e38d13;\n}\n.btn-warning:hover,\n.btn-warning:focus {\n background-color: #eb9316;\n background-position: 0 -15px;\n}\n.btn-warning:active,\n.btn-warning.active {\n background-color: #eb9316;\n border-color: #e38d13;\n}\n.btn-warning.disabled,\n.btn-warning[disabled],\nfieldset[disabled] .btn-warning,\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus,\n.btn-warning.disabled:active,\n.btn-warning[disabled]:active,\nfieldset[disabled] .btn-warning:active,\n.btn-warning.disabled.active,\n.btn-warning[disabled].active,\nfieldset[disabled] .btn-warning.active {\n background-color: #eb9316;\n background-image: none;\n}\n.btn-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #b92c28;\n}\n.btn-danger:hover,\n.btn-danger:focus {\n background-color: #c12e2a;\n background-position: 0 -15px;\n}\n.btn-danger:active,\n.btn-danger.active {\n background-color: #c12e2a;\n border-color: #b92c28;\n}\n.btn-danger.disabled,\n.btn-danger[disabled],\nfieldset[disabled] .btn-danger,\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus,\n.btn-danger.disabled:active,\n.btn-danger[disabled]:active,\nfieldset[disabled] .btn-danger:active,\n.btn-danger.disabled.active,\n.btn-danger[disabled].active,\nfieldset[disabled] .btn-danger.active {\n background-color: #c12e2a;\n background-image: none;\n}\n.thumbnail,\n.img-thumbnail {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n background-color: #e8e8e8;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n background-color: #2e6da4;\n}\n.navbar-default {\n background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);\n}\n.navbar-inverse {\n background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222222 100%);\n background-image: -o-linear-gradient(top, #3c3c3c 0%, #222222 100%);\n background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n}\n.navbar-inverse .navbar-brand,\n.navbar-inverse .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n@media (max-width: 767px) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #fff;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n }\n}\n.alert {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.alert-success {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);\n border-color: #b2dba1;\n}\n.alert-info {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);\n border-color: #9acfea;\n}\n.alert-warning {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);\n border-color: #f5e79e;\n}\n.alert-danger {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);\n border-color: #dca7a7;\n}\n.progress {\n background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);\n}\n.progress-bar {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);\n}\n.progress-bar-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);\n}\n.progress-bar-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);\n}\n.progress-bar-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);\n}\n.progress-bar-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);\n}\n.progress-bar-striped {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.list-group {\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 #286090;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);\n border-color: #2b669a;\n}\n.list-group-item.active .badge,\n.list-group-item.active:hover .badge,\n.list-group-item.active:focus .badge {\n text-shadow: none;\n}\n.panel {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.panel-default > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n}\n.panel-primary > .panel-heading {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n}\n.panel-success > .panel-heading {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);\n}\n.panel-info > .panel-heading {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);\n}\n.panel-warning > .panel-heading {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);\n}\n.panel-danger > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);\n}\n.well {\n background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);\n border-color: #dcdcdc;\n -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n}\n/*# sourceMappingURL=bootstrap-theme.css.map */","/*!\n * Bootstrap v3.3.5 (http://getbootstrap.com)\n * Copyright 2011-2015 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n .box-shadow(none);\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &,\n &:hover,\n &:focus,\n &.focus,\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n border-radius: @navbar-border-radius;\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]} \ No newline at end of file diff --git a/samples/bookinfo/src/productpage/static/bootstrap/css/bootstrap-theme.min.css b/samples/bookinfo/src/productpage/static/bootstrap/css/bootstrap-theme.min.css deleted file mode 100644 index 61358b13d..000000000 --- a/samples/bookinfo/src/productpage/static/bootstrap/css/bootstrap-theme.min.css +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * Bootstrap v3.3.5 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} \ No newline at end of file diff --git a/samples/bookinfo/src/productpage/static/bootstrap/css/bootstrap.css b/samples/bookinfo/src/productpage/static/bootstrap/css/bootstrap.css deleted file mode 100644 index 680e76878..000000000 --- a/samples/bookinfo/src/productpage/static/bootstrap/css/bootstrap.css +++ /dev/null @@ -1,6800 +0,0 @@ -/*! - * Bootstrap v3.3.5 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ -html { - font-family: sans-serif; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; -} -body { - margin: 0; -} -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -main, -menu, -nav, -section, -summary { - display: block; -} -audio, -canvas, -progress, -video { - display: inline-block; - vertical-align: baseline; -} -audio:not([controls]) { - display: none; - height: 0; -} -[hidden], -template { - display: none; -} -a { - background-color: transparent; -} -a:active, -a:hover { - outline: 0; -} -abbr[title] { - border-bottom: 1px dotted; -} -b, -strong { - font-weight: bold; -} -dfn { - font-style: italic; -} -h1 { - margin: .67em 0; - font-size: 2em; -} -mark { - color: #000; - background: #ff0; -} -small { - font-size: 80%; -} -sub, -sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; -} -sup { - top: -.5em; -} -sub { - bottom: -.25em; -} -img { - border: 0; -} -svg:not(:root) { - overflow: hidden; -} -figure { - margin: 1em 40px; -} -hr { - height: 0; - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; -} -pre { - overflow: auto; -} -code, -kbd, -pre, -samp { - font-family: monospace, monospace; - font-size: 1em; -} -button, -input, -optgroup, -select, -textarea { - margin: 0; - font: inherit; - color: inherit; -} -button { - overflow: visible; -} -button, -select { - text-transform: none; -} -button, -html input[type="button"], -input[type="reset"], -input[type="submit"] { - -webkit-appearance: button; - cursor: pointer; -} -button[disabled], -html input[disabled] { - cursor: default; -} -button::-moz-focus-inner, -input::-moz-focus-inner { - padding: 0; - border: 0; -} -input { - line-height: normal; -} -input[type="checkbox"], -input[type="radio"] { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - padding: 0; -} -input[type="number"]::-webkit-inner-spin-button, -input[type="number"]::-webkit-outer-spin-button { - height: auto; -} -input[type="search"] { - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; - -webkit-appearance: textfield; -} -input[type="search"]::-webkit-search-cancel-button, -input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} -fieldset { - padding: .35em .625em .75em; - margin: 0 2px; - border: 1px solid #c0c0c0; -} -legend { - padding: 0; - border: 0; -} -textarea { - overflow: auto; -} -optgroup { - font-weight: bold; -} -table { - border-spacing: 0; - border-collapse: collapse; -} -td, -th { - padding: 0; -} -/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ -@media print { - *, - *:before, - *:after { - color: #000 !important; - text-shadow: none !important; - background: transparent !important; - -webkit-box-shadow: none !important; - box-shadow: none !important; - } - a, - a:visited { - text-decoration: underline; - } - a[href]:after { - content: " (" attr(href) ")"; - } - abbr[title]:after { - content: " (" attr(title) ")"; - } - a[href^="#"]:after, - a[href^="javascript:"]:after { - content: ""; - } - pre, - blockquote { - border: 1px solid #999; - - page-break-inside: avoid; - } - thead { - display: table-header-group; - } - tr, - img { - page-break-inside: avoid; - } - img { - max-width: 100% !important; - } - p, - h2, - h3 { - orphans: 3; - widows: 3; - } - h2, - h3 { - page-break-after: avoid; - } - .navbar { - display: none; - } - .btn > .caret, - .dropup > .btn > .caret { - border-top-color: #000 !important; - } - .label { - border: 1px solid #000; - } - .table { - border-collapse: collapse !important; - } - .table td, - .table th { - background-color: #fff !important; - } - .table-bordered th, - .table-bordered td { - border: 1px solid #ddd !important; - } -} -@font-face { - font-family: 'Glyphicons Halflings'; - - src: url('../fonts/glyphicons-halflings-regular.eot'); - src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); -} -.glyphicon { - position: relative; - top: 1px; - display: inline-block; - font-family: 'Glyphicons Halflings'; - font-style: normal; - font-weight: normal; - line-height: 1; - - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -.glyphicon-asterisk:before { - content: "\2a"; -} -.glyphicon-plus:before { - content: "\2b"; -} -.glyphicon-euro:before, -.glyphicon-eur:before { - content: "\20ac"; -} -.glyphicon-minus:before { - content: "\2212"; -} -.glyphicon-cloud:before { - content: "\2601"; -} -.glyphicon-envelope:before { - content: "\2709"; -} -.glyphicon-pencil:before { - content: "\270f"; -} -.glyphicon-glass:before { - content: "\e001"; -} -.glyphicon-music:before { - content: "\e002"; -} -.glyphicon-search:before { - content: "\e003"; -} -.glyphicon-heart:before { - content: "\e005"; -} -.glyphicon-star:before { - content: "\e006"; -} -.glyphicon-star-empty:before { - content: "\e007"; -} -.glyphicon-user:before { - content: "\e008"; -} -.glyphicon-film:before { - content: "\e009"; -} -.glyphicon-th-large:before { - content: "\e010"; -} -.glyphicon-th:before { - content: "\e011"; -} -.glyphicon-th-list:before { - content: "\e012"; -} -.glyphicon-ok:before { - content: "\e013"; -} -.glyphicon-remove:before { - content: "\e014"; -} -.glyphicon-zoom-in:before { - content: "\e015"; -} -.glyphicon-zoom-out:before { - content: "\e016"; -} -.glyphicon-off:before { - content: "\e017"; -} -.glyphicon-signal:before { - content: "\e018"; -} -.glyphicon-cog:before { - content: "\e019"; -} -.glyphicon-trash:before { - content: "\e020"; -} -.glyphicon-home:before { - content: "\e021"; -} -.glyphicon-file:before { - content: "\e022"; -} -.glyphicon-time:before { - content: "\e023"; -} -.glyphicon-road:before { - content: "\e024"; -} -.glyphicon-download-alt:before { - content: "\e025"; -} -.glyphicon-download:before { - content: "\e026"; -} -.glyphicon-upload:before { - content: "\e027"; -} -.glyphicon-inbox:before { - content: "\e028"; -} -.glyphicon-play-circle:before { - content: "\e029"; -} -.glyphicon-repeat:before { - content: "\e030"; -} -.glyphicon-refresh:before { - content: "\e031"; -} -.glyphicon-list-alt:before { - content: "\e032"; -} -.glyphicon-lock:before { - content: "\e033"; -} -.glyphicon-flag:before { - content: "\e034"; -} -.glyphicon-headphones:before { - content: "\e035"; -} -.glyphicon-volume-off:before { - content: "\e036"; -} -.glyphicon-volume-down:before { - content: "\e037"; -} -.glyphicon-volume-up:before { - content: "\e038"; -} -.glyphicon-qrcode:before { - content: "\e039"; -} -.glyphicon-barcode:before { - content: "\e040"; -} -.glyphicon-tag:before { - content: "\e041"; -} -.glyphicon-tags:before { - content: "\e042"; -} -.glyphicon-book:before { - content: "\e043"; -} -.glyphicon-bookmark:before { - content: "\e044"; -} -.glyphicon-print:before { - content: "\e045"; -} -.glyphicon-camera:before { - content: "\e046"; -} -.glyphicon-font:before { - content: "\e047"; -} -.glyphicon-bold:before { - content: "\e048"; -} -.glyphicon-italic:before { - content: "\e049"; -} -.glyphicon-text-height:before { - content: "\e050"; -} -.glyphicon-text-width:before { - content: "\e051"; -} -.glyphicon-align-left:before { - content: "\e052"; -} -.glyphicon-align-center:before { - content: "\e053"; -} -.glyphicon-align-right:before { - content: "\e054"; -} -.glyphicon-align-justify:before { - content: "\e055"; -} -.glyphicon-list:before { - content: "\e056"; -} -.glyphicon-indent-left:before { - content: "\e057"; -} -.glyphicon-indent-right:before { - content: "\e058"; -} -.glyphicon-facetime-video:before { - content: "\e059"; -} -.glyphicon-picture:before { - content: "\e060"; -} -.glyphicon-map-marker:before { - content: "\e062"; -} -.glyphicon-adjust:before { - content: "\e063"; -} -.glyphicon-tint:before { - content: "\e064"; -} -.glyphicon-edit:before { - content: "\e065"; -} -.glyphicon-share:before { - content: "\e066"; -} -.glyphicon-check:before { - content: "\e067"; -} -.glyphicon-move:before { - content: "\e068"; -} -.glyphicon-step-backward:before { - content: "\e069"; -} -.glyphicon-fast-backward:before { - content: "\e070"; -} -.glyphicon-backward:before { - content: "\e071"; -} -.glyphicon-play:before { - content: "\e072"; -} -.glyphicon-pause:before { - content: "\e073"; -} -.glyphicon-stop:before { - content: "\e074"; -} -.glyphicon-forward:before { - content: "\e075"; -} -.glyphicon-fast-forward:before { - content: "\e076"; -} -.glyphicon-step-forward:before { - content: "\e077"; -} -.glyphicon-eject:before { - content: "\e078"; -} -.glyphicon-chevron-left:before { - content: "\e079"; -} -.glyphicon-chevron-right:before { - content: "\e080"; -} -.glyphicon-plus-sign:before { - content: "\e081"; -} -.glyphicon-minus-sign:before { - content: "\e082"; -} -.glyphicon-remove-sign:before { - content: "\e083"; -} -.glyphicon-ok-sign:before { - content: "\e084"; -} -.glyphicon-question-sign:before { - content: "\e085"; -} -.glyphicon-info-sign:before { - content: "\e086"; -} -.glyphicon-screenshot:before { - content: "\e087"; -} -.glyphicon-remove-circle:before { - content: "\e088"; -} -.glyphicon-ok-circle:before { - content: "\e089"; -} -.glyphicon-ban-circle:before { - content: "\e090"; -} -.glyphicon-arrow-left:before { - content: "\e091"; -} -.glyphicon-arrow-right:before { - content: "\e092"; -} -.glyphicon-arrow-up:before { - content: "\e093"; -} -.glyphicon-arrow-down:before { - content: "\e094"; -} -.glyphicon-share-alt:before { - content: "\e095"; -} -.glyphicon-resize-full:before { - content: "\e096"; -} -.glyphicon-resize-small:before { - content: "\e097"; -} -.glyphicon-exclamation-sign:before { - content: "\e101"; -} -.glyphicon-gift:before { - content: "\e102"; -} -.glyphicon-leaf:before { - content: "\e103"; -} -.glyphicon-fire:before { - content: "\e104"; -} -.glyphicon-eye-open:before { - content: "\e105"; -} -.glyphicon-eye-close:before { - content: "\e106"; -} -.glyphicon-warning-sign:before { - content: "\e107"; -} -.glyphicon-plane:before { - content: "\e108"; -} -.glyphicon-calendar:before { - content: "\e109"; -} -.glyphicon-random:before { - content: "\e110"; -} -.glyphicon-comment:before { - content: "\e111"; -} -.glyphicon-magnet:before { - content: "\e112"; -} -.glyphicon-chevron-up:before { - content: "\e113"; -} -.glyphicon-chevron-down:before { - content: "\e114"; -} -.glyphicon-retweet:before { - content: "\e115"; -} -.glyphicon-shopping-cart:before { - content: "\e116"; -} -.glyphicon-folder-close:before { - content: "\e117"; -} -.glyphicon-folder-open:before { - content: "\e118"; -} -.glyphicon-resize-vertical:before { - content: "\e119"; -} -.glyphicon-resize-horizontal:before { - content: "\e120"; -} -.glyphicon-hdd:before { - content: "\e121"; -} -.glyphicon-bullhorn:before { - content: "\e122"; -} -.glyphicon-bell:before { - content: "\e123"; -} -.glyphicon-certificate:before { - content: "\e124"; -} -.glyphicon-thumbs-up:before { - content: "\e125"; -} -.glyphicon-thumbs-down:before { - content: "\e126"; -} -.glyphicon-hand-right:before { - content: "\e127"; -} -.glyphicon-hand-left:before { - content: "\e128"; -} -.glyphicon-hand-up:before { - content: "\e129"; -} -.glyphicon-hand-down:before { - content: "\e130"; -} -.glyphicon-circle-arrow-right:before { - content: "\e131"; -} -.glyphicon-circle-arrow-left:before { - content: "\e132"; -} -.glyphicon-circle-arrow-up:before { - content: "\e133"; -} -.glyphicon-circle-arrow-down:before { - content: "\e134"; -} -.glyphicon-globe:before { - content: "\e135"; -} -.glyphicon-wrench:before { - content: "\e136"; -} -.glyphicon-tasks:before { - content: "\e137"; -} -.glyphicon-filter:before { - content: "\e138"; -} -.glyphicon-briefcase:before { - content: "\e139"; -} -.glyphicon-fullscreen:before { - content: "\e140"; -} -.glyphicon-dashboard:before { - content: "\e141"; -} -.glyphicon-paperclip:before { - content: "\e142"; -} -.glyphicon-heart-empty:before { - content: "\e143"; -} -.glyphicon-link:before { - content: "\e144"; -} -.glyphicon-phone:before { - content: "\e145"; -} -.glyphicon-pushpin:before { - content: "\e146"; -} -.glyphicon-usd:before { - content: "\e148"; -} -.glyphicon-gbp:before { - content: "\e149"; -} -.glyphicon-sort:before { - content: "\e150"; -} -.glyphicon-sort-by-alphabet:before { - content: "\e151"; -} -.glyphicon-sort-by-alphabet-alt:before { - content: "\e152"; -} -.glyphicon-sort-by-order:before { - content: "\e153"; -} -.glyphicon-sort-by-order-alt:before { - content: "\e154"; -} -.glyphicon-sort-by-attributes:before { - content: "\e155"; -} -.glyphicon-sort-by-attributes-alt:before { - content: "\e156"; -} -.glyphicon-unchecked:before { - content: "\e157"; -} -.glyphicon-expand:before { - content: "\e158"; -} -.glyphicon-collapse-down:before { - content: "\e159"; -} -.glyphicon-collapse-up:before { - content: "\e160"; -} -.glyphicon-log-in:before { - content: "\e161"; -} -.glyphicon-flash:before { - content: "\e162"; -} -.glyphicon-log-out:before { - content: "\e163"; -} -.glyphicon-new-window:before { - content: "\e164"; -} -.glyphicon-record:before { - content: "\e165"; -} -.glyphicon-save:before { - content: "\e166"; -} -.glyphicon-open:before { - content: "\e167"; -} -.glyphicon-saved:before { - content: "\e168"; -} -.glyphicon-import:before { - content: "\e169"; -} -.glyphicon-export:before { - content: "\e170"; -} -.glyphicon-send:before { - content: "\e171"; -} -.glyphicon-floppy-disk:before { - content: "\e172"; -} -.glyphicon-floppy-saved:before { - content: "\e173"; -} -.glyphicon-floppy-remove:before { - content: "\e174"; -} -.glyphicon-floppy-save:before { - content: "\e175"; -} -.glyphicon-floppy-open:before { - content: "\e176"; -} -.glyphicon-credit-card:before { - content: "\e177"; -} -.glyphicon-transfer:before { - content: "\e178"; -} -.glyphicon-cutlery:before { - content: "\e179"; -} -.glyphicon-header:before { - content: "\e180"; -} -.glyphicon-compressed:before { - content: "\e181"; -} -.glyphicon-earphone:before { - content: "\e182"; -} -.glyphicon-phone-alt:before { - content: "\e183"; -} -.glyphicon-tower:before { - content: "\e184"; -} -.glyphicon-stats:before { - content: "\e185"; -} -.glyphicon-sd-video:before { - content: "\e186"; -} -.glyphicon-hd-video:before { - content: "\e187"; -} -.glyphicon-subtitles:before { - content: "\e188"; -} -.glyphicon-sound-stereo:before { - content: "\e189"; -} -.glyphicon-sound-dolby:before { - content: "\e190"; -} -.glyphicon-sound-5-1:before { - content: "\e191"; -} -.glyphicon-sound-6-1:before { - content: "\e192"; -} -.glyphicon-sound-7-1:before { - content: "\e193"; -} -.glyphicon-copyright-mark:before { - content: "\e194"; -} -.glyphicon-registration-mark:before { - content: "\e195"; -} -.glyphicon-cloud-download:before { - content: "\e197"; -} -.glyphicon-cloud-upload:before { - content: "\e198"; -} -.glyphicon-tree-conifer:before { - content: "\e199"; -} -.glyphicon-tree-deciduous:before { - content: "\e200"; -} -.glyphicon-cd:before { - content: "\e201"; -} -.glyphicon-save-file:before { - content: "\e202"; -} -.glyphicon-open-file:before { - content: "\e203"; -} -.glyphicon-level-up:before { - content: "\e204"; -} -.glyphicon-copy:before { - content: "\e205"; -} -.glyphicon-paste:before { - content: "\e206"; -} -.glyphicon-alert:before { - content: "\e209"; -} -.glyphicon-equalizer:before { - content: "\e210"; -} -.glyphicon-king:before { - content: "\e211"; -} -.glyphicon-queen:before { - content: "\e212"; -} -.glyphicon-pawn:before { - content: "\e213"; -} -.glyphicon-bishop:before { - content: "\e214"; -} -.glyphicon-knight:before { - content: "\e215"; -} -.glyphicon-baby-formula:before { - content: "\e216"; -} -.glyphicon-tent:before { - content: "\26fa"; -} -.glyphicon-blackboard:before { - content: "\e218"; -} -.glyphicon-bed:before { - content: "\e219"; -} -.glyphicon-apple:before { - content: "\f8ff"; -} -.glyphicon-erase:before { - content: "\e221"; -} -.glyphicon-hourglass:before { - content: "\231b"; -} -.glyphicon-lamp:before { - content: "\e223"; -} -.glyphicon-duplicate:before { - content: "\e224"; -} -.glyphicon-piggy-bank:before { - content: "\e225"; -} -.glyphicon-scissors:before { - content: "\e226"; -} -.glyphicon-bitcoin:before { - content: "\e227"; -} -.glyphicon-btc:before { - content: "\e227"; -} -.glyphicon-xbt:before { - content: "\e227"; -} -.glyphicon-yen:before { - content: "\00a5"; -} -.glyphicon-jpy:before { - content: "\00a5"; -} -.glyphicon-ruble:before { - content: "\20bd"; -} -.glyphicon-rub:before { - content: "\20bd"; -} -.glyphicon-scale:before { - content: "\e230"; -} -.glyphicon-ice-lolly:before { - content: "\e231"; -} -.glyphicon-ice-lolly-tasted:before { - content: "\e232"; -} -.glyphicon-education:before { - content: "\e233"; -} -.glyphicon-option-horizontal:before { - content: "\e234"; -} -.glyphicon-option-vertical:before { - content: "\e235"; -} -.glyphicon-menu-hamburger:before { - content: "\e236"; -} -.glyphicon-modal-window:before { - content: "\e237"; -} -.glyphicon-oil:before { - content: "\e238"; -} -.glyphicon-grain:before { - content: "\e239"; -} -.glyphicon-sunglasses:before { - content: "\e240"; -} -.glyphicon-text-size:before { - content: "\e241"; -} -.glyphicon-text-color:before { - content: "\e242"; -} -.glyphicon-text-background:before { - content: "\e243"; -} -.glyphicon-object-align-top:before { - content: "\e244"; -} -.glyphicon-object-align-bottom:before { - content: "\e245"; -} -.glyphicon-object-align-horizontal:before { - content: "\e246"; -} -.glyphicon-object-align-left:before { - content: "\e247"; -} -.glyphicon-object-align-vertical:before { - content: "\e248"; -} -.glyphicon-object-align-right:before { - content: "\e249"; -} -.glyphicon-triangle-right:before { - content: "\e250"; -} -.glyphicon-triangle-left:before { - content: "\e251"; -} -.glyphicon-triangle-bottom:before { - content: "\e252"; -} -.glyphicon-triangle-top:before { - content: "\e253"; -} -.glyphicon-console:before { - content: "\e254"; -} -.glyphicon-superscript:before { - content: "\e255"; -} -.glyphicon-subscript:before { - content: "\e256"; -} -.glyphicon-menu-left:before { - content: "\e257"; -} -.glyphicon-menu-right:before { - content: "\e258"; -} -.glyphicon-menu-down:before { - content: "\e259"; -} -.glyphicon-menu-up:before { - content: "\e260"; -} -* { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -*:before, -*:after { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -html { - font-size: 10px; - - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} -body { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 1.42857143; - color: #333; - background-color: #fff; -} -input, -button, -select, -textarea { - font-family: inherit; - font-size: inherit; - line-height: inherit; -} -a { - color: #337ab7; - text-decoration: none; -} -a:hover, -a:focus { - color: #23527c; - text-decoration: underline; -} -a:focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -figure { - margin: 0; -} -img { - vertical-align: middle; -} -.img-responsive, -.thumbnail > img, -.thumbnail a > img, -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - display: block; - max-width: 100%; - height: auto; -} -.img-rounded { - border-radius: 6px; -} -.img-thumbnail { - display: inline-block; - max-width: 100%; - height: auto; - padding: 4px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: all .2s ease-in-out; - -o-transition: all .2s ease-in-out; - transition: all .2s ease-in-out; -} -.img-circle { - border-radius: 50%; -} -hr { - margin-top: 20px; - margin-bottom: 20px; - border: 0; - border-top: 1px solid #eee; -} -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - border: 0; -} -.sr-only-focusable:active, -.sr-only-focusable:focus { - position: static; - width: auto; - height: auto; - margin: 0; - overflow: visible; - clip: auto; -} -[role="button"] { - cursor: pointer; -} -h1, -h2, -h3, -h4, -h5, -h6, -.h1, -.h2, -.h3, -.h4, -.h5, -.h6 { - font-family: inherit; - font-weight: 500; - line-height: 1.1; - color: inherit; -} -h1 small, -h2 small, -h3 small, -h4 small, -h5 small, -h6 small, -.h1 small, -.h2 small, -.h3 small, -.h4 small, -.h5 small, -.h6 small, -h1 .small, -h2 .small, -h3 .small, -h4 .small, -h5 .small, -h6 .small, -.h1 .small, -.h2 .small, -.h3 .small, -.h4 .small, -.h5 .small, -.h6 .small { - font-weight: normal; - line-height: 1; - color: #777; -} -h1, -.h1, -h2, -.h2, -h3, -.h3 { - margin-top: 20px; - margin-bottom: 10px; -} -h1 small, -.h1 small, -h2 small, -.h2 small, -h3 small, -.h3 small, -h1 .small, -.h1 .small, -h2 .small, -.h2 .small, -h3 .small, -.h3 .small { - font-size: 65%; -} -h4, -.h4, -h5, -.h5, -h6, -.h6 { - margin-top: 10px; - margin-bottom: 10px; -} -h4 small, -.h4 small, -h5 small, -.h5 small, -h6 small, -.h6 small, -h4 .small, -.h4 .small, -h5 .small, -.h5 .small, -h6 .small, -.h6 .small { - font-size: 75%; -} -h1, -.h1 { - font-size: 36px; -} -h2, -.h2 { - font-size: 30px; -} -h3, -.h3 { - font-size: 24px; -} -h4, -.h4 { - font-size: 18px; -} -h5, -.h5 { - font-size: 14px; -} -h6, -.h6 { - font-size: 12px; -} -p { - margin: 0 0 10px; -} -.lead { - margin-bottom: 20px; - font-size: 16px; - font-weight: 300; - line-height: 1.4; -} -@media (min-width: 768px) { - .lead { - font-size: 21px; - } -} -small, -.small { - font-size: 85%; -} -mark, -.mark { - padding: .2em; - background-color: #fcf8e3; -} -.text-left { - text-align: left; -} -.text-right { - text-align: right; -} -.text-center { - text-align: center; -} -.text-justify { - text-align: justify; -} -.text-nowrap { - white-space: nowrap; -} -.text-lowercase { - text-transform: lowercase; -} -.text-uppercase { - text-transform: uppercase; -} -.text-capitalize { - text-transform: capitalize; -} -.text-muted { - color: #777; -} -.text-primary { - color: #337ab7; -} -a.text-primary:hover, -a.text-primary:focus { - color: #286090; -} -.text-success { - color: #3c763d; -} -a.text-success:hover, -a.text-success:focus { - color: #2b542c; -} -.text-info { - color: #31708f; -} -a.text-info:hover, -a.text-info:focus { - color: #245269; -} -.text-warning { - color: #8a6d3b; -} -a.text-warning:hover, -a.text-warning:focus { - color: #66512c; -} -.text-danger { - color: #a94442; -} -a.text-danger:hover, -a.text-danger:focus { - color: #843534; -} -.bg-primary { - color: #fff; - background-color: #337ab7; -} -a.bg-primary:hover, -a.bg-primary:focus { - background-color: #286090; -} -.bg-success { - background-color: #dff0d8; -} -a.bg-success:hover, -a.bg-success:focus { - background-color: #c1e2b3; -} -.bg-info { - background-color: #d9edf7; -} -a.bg-info:hover, -a.bg-info:focus { - background-color: #afd9ee; -} -.bg-warning { - background-color: #fcf8e3; -} -a.bg-warning:hover, -a.bg-warning:focus { - background-color: #f7ecb5; -} -.bg-danger { - background-color: #f2dede; -} -a.bg-danger:hover, -a.bg-danger:focus { - background-color: #e4b9b9; -} -.page-header { - padding-bottom: 9px; - margin: 40px 0 20px; - border-bottom: 1px solid #eee; -} -ul, -ol { - margin-top: 0; - margin-bottom: 10px; -} -ul ul, -ol ul, -ul ol, -ol ol { - margin-bottom: 0; -} -.list-unstyled { - padding-left: 0; - list-style: none; -} -.list-inline { - padding-left: 0; - margin-left: -5px; - list-style: none; -} -.list-inline > li { - display: inline-block; - padding-right: 5px; - padding-left: 5px; -} -dl { - margin-top: 0; - margin-bottom: 20px; -} -dt, -dd { - line-height: 1.42857143; -} -dt { - font-weight: bold; -} -dd { - margin-left: 0; -} -@media (min-width: 768px) { - .dl-horizontal dt { - float: left; - width: 160px; - overflow: hidden; - clear: left; - text-align: right; - text-overflow: ellipsis; - white-space: nowrap; - } - .dl-horizontal dd { - margin-left: 180px; - } -} -abbr[title], -abbr[data-original-title] { - cursor: help; - border-bottom: 1px dotted #777; -} -.initialism { - font-size: 90%; - text-transform: uppercase; -} -blockquote { - padding: 10px 20px; - margin: 0 0 20px; - font-size: 17.5px; - border-left: 5px solid #eee; -} -blockquote p:last-child, -blockquote ul:last-child, -blockquote ol:last-child { - margin-bottom: 0; -} -blockquote footer, -blockquote small, -blockquote .small { - display: block; - font-size: 80%; - line-height: 1.42857143; - color: #777; -} -blockquote footer:before, -blockquote small:before, -blockquote .small:before { - content: '\2014 \00A0'; -} -.blockquote-reverse, -blockquote.pull-right { - padding-right: 15px; - padding-left: 0; - text-align: right; - border-right: 5px solid #eee; - border-left: 0; -} -.blockquote-reverse footer:before, -blockquote.pull-right footer:before, -.blockquote-reverse small:before, -blockquote.pull-right small:before, -.blockquote-reverse .small:before, -blockquote.pull-right .small:before { - content: ''; -} -.blockquote-reverse footer:after, -blockquote.pull-right footer:after, -.blockquote-reverse small:after, -blockquote.pull-right small:after, -.blockquote-reverse .small:after, -blockquote.pull-right .small:after { - content: '\00A0 \2014'; -} -address { - margin-bottom: 20px; - font-style: normal; - line-height: 1.42857143; -} -code, -kbd, -pre, -samp { - font-family: Menlo, Monaco, Consolas, "Courier New", monospace; -} -code { - padding: 2px 4px; - font-size: 90%; - color: #c7254e; - background-color: #f9f2f4; - border-radius: 4px; -} -kbd { - padding: 2px 4px; - font-size: 90%; - color: #fff; - background-color: #333; - border-radius: 3px; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); -} -kbd kbd { - padding: 0; - font-size: 100%; - font-weight: bold; - -webkit-box-shadow: none; - box-shadow: none; -} -pre { - display: block; - padding: 9.5px; - margin: 0 0 10px; - font-size: 13px; - line-height: 1.42857143; - color: #333; - word-break: break-all; - word-wrap: break-word; - background-color: #f5f5f5; - border: 1px solid #ccc; - border-radius: 4px; -} -pre code { - padding: 0; - font-size: inherit; - color: inherit; - white-space: pre-wrap; - background-color: transparent; - border-radius: 0; -} -.pre-scrollable { - max-height: 340px; - overflow-y: scroll; -} -.container { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} -@media (min-width: 768px) { - .container { - width: 750px; - } -} -@media (min-width: 992px) { - .container { - width: 970px; - } -} -@media (min-width: 1200px) { - .container { - width: 1170px; - } -} -.container-fluid { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} -.row { - margin-right: -15px; - margin-left: -15px; -} -.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { - position: relative; - min-height: 1px; - padding-right: 15px; - padding-left: 15px; -} -.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { - float: left; -} -.col-xs-12 { - width: 100%; -} -.col-xs-11 { - width: 91.66666667%; -} -.col-xs-10 { - width: 83.33333333%; -} -.col-xs-9 { - width: 75%; -} -.col-xs-8 { - width: 66.66666667%; -} -.col-xs-7 { - width: 58.33333333%; -} -.col-xs-6 { - width: 50%; -} -.col-xs-5 { - width: 41.66666667%; -} -.col-xs-4 { - width: 33.33333333%; -} -.col-xs-3 { - width: 25%; -} -.col-xs-2 { - width: 16.66666667%; -} -.col-xs-1 { - width: 8.33333333%; -} -.col-xs-pull-12 { - right: 100%; -} -.col-xs-pull-11 { - right: 91.66666667%; -} -.col-xs-pull-10 { - right: 83.33333333%; -} -.col-xs-pull-9 { - right: 75%; -} -.col-xs-pull-8 { - right: 66.66666667%; -} -.col-xs-pull-7 { - right: 58.33333333%; -} -.col-xs-pull-6 { - right: 50%; -} -.col-xs-pull-5 { - right: 41.66666667%; -} -.col-xs-pull-4 { - right: 33.33333333%; -} -.col-xs-pull-3 { - right: 25%; -} -.col-xs-pull-2 { - right: 16.66666667%; -} -.col-xs-pull-1 { - right: 8.33333333%; -} -.col-xs-pull-0 { - right: auto; -} -.col-xs-push-12 { - left: 100%; -} -.col-xs-push-11 { - left: 91.66666667%; -} -.col-xs-push-10 { - left: 83.33333333%; -} -.col-xs-push-9 { - left: 75%; -} -.col-xs-push-8 { - left: 66.66666667%; -} -.col-xs-push-7 { - left: 58.33333333%; -} -.col-xs-push-6 { - left: 50%; -} -.col-xs-push-5 { - left: 41.66666667%; -} -.col-xs-push-4 { - left: 33.33333333%; -} -.col-xs-push-3 { - left: 25%; -} -.col-xs-push-2 { - left: 16.66666667%; -} -.col-xs-push-1 { - left: 8.33333333%; -} -.col-xs-push-0 { - left: auto; -} -.col-xs-offset-12 { - margin-left: 100%; -} -.col-xs-offset-11 { - margin-left: 91.66666667%; -} -.col-xs-offset-10 { - margin-left: 83.33333333%; -} -.col-xs-offset-9 { - margin-left: 75%; -} -.col-xs-offset-8 { - margin-left: 66.66666667%; -} -.col-xs-offset-7 { - margin-left: 58.33333333%; -} -.col-xs-offset-6 { - margin-left: 50%; -} -.col-xs-offset-5 { - margin-left: 41.66666667%; -} -.col-xs-offset-4 { - margin-left: 33.33333333%; -} -.col-xs-offset-3 { - margin-left: 25%; -} -.col-xs-offset-2 { - margin-left: 16.66666667%; -} -.col-xs-offset-1 { - margin-left: 8.33333333%; -} -.col-xs-offset-0 { - margin-left: 0; -} -@media (min-width: 768px) { - .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { - float: left; - } - .col-sm-12 { - width: 100%; - } - .col-sm-11 { - width: 91.66666667%; - } - .col-sm-10 { - width: 83.33333333%; - } - .col-sm-9 { - width: 75%; - } - .col-sm-8 { - width: 66.66666667%; - } - .col-sm-7 { - width: 58.33333333%; - } - .col-sm-6 { - width: 50%; - } - .col-sm-5 { - width: 41.66666667%; - } - .col-sm-4 { - width: 33.33333333%; - } - .col-sm-3 { - width: 25%; - } - .col-sm-2 { - width: 16.66666667%; - } - .col-sm-1 { - width: 8.33333333%; - } - .col-sm-pull-12 { - right: 100%; - } - .col-sm-pull-11 { - right: 91.66666667%; - } - .col-sm-pull-10 { - right: 83.33333333%; - } - .col-sm-pull-9 { - right: 75%; - } - .col-sm-pull-8 { - right: 66.66666667%; - } - .col-sm-pull-7 { - right: 58.33333333%; - } - .col-sm-pull-6 { - right: 50%; - } - .col-sm-pull-5 { - right: 41.66666667%; - } - .col-sm-pull-4 { - right: 33.33333333%; - } - .col-sm-pull-3 { - right: 25%; - } - .col-sm-pull-2 { - right: 16.66666667%; - } - .col-sm-pull-1 { - right: 8.33333333%; - } - .col-sm-pull-0 { - right: auto; - } - .col-sm-push-12 { - left: 100%; - } - .col-sm-push-11 { - left: 91.66666667%; - } - .col-sm-push-10 { - left: 83.33333333%; - } - .col-sm-push-9 { - left: 75%; - } - .col-sm-push-8 { - left: 66.66666667%; - } - .col-sm-push-7 { - left: 58.33333333%; - } - .col-sm-push-6 { - left: 50%; - } - .col-sm-push-5 { - left: 41.66666667%; - } - .col-sm-push-4 { - left: 33.33333333%; - } - .col-sm-push-3 { - left: 25%; - } - .col-sm-push-2 { - left: 16.66666667%; - } - .col-sm-push-1 { - left: 8.33333333%; - } - .col-sm-push-0 { - left: auto; - } - .col-sm-offset-12 { - margin-left: 100%; - } - .col-sm-offset-11 { - margin-left: 91.66666667%; - } - .col-sm-offset-10 { - margin-left: 83.33333333%; - } - .col-sm-offset-9 { - margin-left: 75%; - } - .col-sm-offset-8 { - margin-left: 66.66666667%; - } - .col-sm-offset-7 { - margin-left: 58.33333333%; - } - .col-sm-offset-6 { - margin-left: 50%; - } - .col-sm-offset-5 { - margin-left: 41.66666667%; - } - .col-sm-offset-4 { - margin-left: 33.33333333%; - } - .col-sm-offset-3 { - margin-left: 25%; - } - .col-sm-offset-2 { - margin-left: 16.66666667%; - } - .col-sm-offset-1 { - margin-left: 8.33333333%; - } - .col-sm-offset-0 { - margin-left: 0; - } -} -@media (min-width: 992px) { - .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { - float: left; - } - .col-md-12 { - width: 100%; - } - .col-md-11 { - width: 91.66666667%; - } - .col-md-10 { - width: 83.33333333%; - } - .col-md-9 { - width: 75%; - } - .col-md-8 { - width: 66.66666667%; - } - .col-md-7 { - width: 58.33333333%; - } - .col-md-6 { - width: 50%; - } - .col-md-5 { - width: 41.66666667%; - } - .col-md-4 { - width: 33.33333333%; - } - .col-md-3 { - width: 25%; - } - .col-md-2 { - width: 16.66666667%; - } - .col-md-1 { - width: 8.33333333%; - } - .col-md-pull-12 { - right: 100%; - } - .col-md-pull-11 { - right: 91.66666667%; - } - .col-md-pull-10 { - right: 83.33333333%; - } - .col-md-pull-9 { - right: 75%; - } - .col-md-pull-8 { - right: 66.66666667%; - } - .col-md-pull-7 { - right: 58.33333333%; - } - .col-md-pull-6 { - right: 50%; - } - .col-md-pull-5 { - right: 41.66666667%; - } - .col-md-pull-4 { - right: 33.33333333%; - } - .col-md-pull-3 { - right: 25%; - } - .col-md-pull-2 { - right: 16.66666667%; - } - .col-md-pull-1 { - right: 8.33333333%; - } - .col-md-pull-0 { - right: auto; - } - .col-md-push-12 { - left: 100%; - } - .col-md-push-11 { - left: 91.66666667%; - } - .col-md-push-10 { - left: 83.33333333%; - } - .col-md-push-9 { - left: 75%; - } - .col-md-push-8 { - left: 66.66666667%; - } - .col-md-push-7 { - left: 58.33333333%; - } - .col-md-push-6 { - left: 50%; - } - .col-md-push-5 { - left: 41.66666667%; - } - .col-md-push-4 { - left: 33.33333333%; - } - .col-md-push-3 { - left: 25%; - } - .col-md-push-2 { - left: 16.66666667%; - } - .col-md-push-1 { - left: 8.33333333%; - } - .col-md-push-0 { - left: auto; - } - .col-md-offset-12 { - margin-left: 100%; - } - .col-md-offset-11 { - margin-left: 91.66666667%; - } - .col-md-offset-10 { - margin-left: 83.33333333%; - } - .col-md-offset-9 { - margin-left: 75%; - } - .col-md-offset-8 { - margin-left: 66.66666667%; - } - .col-md-offset-7 { - margin-left: 58.33333333%; - } - .col-md-offset-6 { - margin-left: 50%; - } - .col-md-offset-5 { - margin-left: 41.66666667%; - } - .col-md-offset-4 { - margin-left: 33.33333333%; - } - .col-md-offset-3 { - margin-left: 25%; - } - .col-md-offset-2 { - margin-left: 16.66666667%; - } - .col-md-offset-1 { - margin-left: 8.33333333%; - } - .col-md-offset-0 { - margin-left: 0; - } -} -@media (min-width: 1200px) { - .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { - float: left; - } - .col-lg-12 { - width: 100%; - } - .col-lg-11 { - width: 91.66666667%; - } - .col-lg-10 { - width: 83.33333333%; - } - .col-lg-9 { - width: 75%; - } - .col-lg-8 { - width: 66.66666667%; - } - .col-lg-7 { - width: 58.33333333%; - } - .col-lg-6 { - width: 50%; - } - .col-lg-5 { - width: 41.66666667%; - } - .col-lg-4 { - width: 33.33333333%; - } - .col-lg-3 { - width: 25%; - } - .col-lg-2 { - width: 16.66666667%; - } - .col-lg-1 { - width: 8.33333333%; - } - .col-lg-pull-12 { - right: 100%; - } - .col-lg-pull-11 { - right: 91.66666667%; - } - .col-lg-pull-10 { - right: 83.33333333%; - } - .col-lg-pull-9 { - right: 75%; - } - .col-lg-pull-8 { - right: 66.66666667%; - } - .col-lg-pull-7 { - right: 58.33333333%; - } - .col-lg-pull-6 { - right: 50%; - } - .col-lg-pull-5 { - right: 41.66666667%; - } - .col-lg-pull-4 { - right: 33.33333333%; - } - .col-lg-pull-3 { - right: 25%; - } - .col-lg-pull-2 { - right: 16.66666667%; - } - .col-lg-pull-1 { - right: 8.33333333%; - } - .col-lg-pull-0 { - right: auto; - } - .col-lg-push-12 { - left: 100%; - } - .col-lg-push-11 { - left: 91.66666667%; - } - .col-lg-push-10 { - left: 83.33333333%; - } - .col-lg-push-9 { - left: 75%; - } - .col-lg-push-8 { - left: 66.66666667%; - } - .col-lg-push-7 { - left: 58.33333333%; - } - .col-lg-push-6 { - left: 50%; - } - .col-lg-push-5 { - left: 41.66666667%; - } - .col-lg-push-4 { - left: 33.33333333%; - } - .col-lg-push-3 { - left: 25%; - } - .col-lg-push-2 { - left: 16.66666667%; - } - .col-lg-push-1 { - left: 8.33333333%; - } - .col-lg-push-0 { - left: auto; - } - .col-lg-offset-12 { - margin-left: 100%; - } - .col-lg-offset-11 { - margin-left: 91.66666667%; - } - .col-lg-offset-10 { - margin-left: 83.33333333%; - } - .col-lg-offset-9 { - margin-left: 75%; - } - .col-lg-offset-8 { - margin-left: 66.66666667%; - } - .col-lg-offset-7 { - margin-left: 58.33333333%; - } - .col-lg-offset-6 { - margin-left: 50%; - } - .col-lg-offset-5 { - margin-left: 41.66666667%; - } - .col-lg-offset-4 { - margin-left: 33.33333333%; - } - .col-lg-offset-3 { - margin-left: 25%; - } - .col-lg-offset-2 { - margin-left: 16.66666667%; - } - .col-lg-offset-1 { - margin-left: 8.33333333%; - } - .col-lg-offset-0 { - margin-left: 0; - } -} -table { - background-color: transparent; -} -caption { - padding-top: 8px; - padding-bottom: 8px; - color: #777; - text-align: left; -} -th { - text-align: left; -} -.table { - width: 100%; - max-width: 100%; - margin-bottom: 20px; -} -.table > thead > tr > th, -.table > tbody > tr > th, -.table > tfoot > tr > th, -.table > thead > tr > td, -.table > tbody > tr > td, -.table > tfoot > tr > td { - padding: 8px; - line-height: 1.42857143; - vertical-align: top; - border-top: 1px solid #ddd; -} -.table > thead > tr > th { - vertical-align: bottom; - border-bottom: 2px solid #ddd; -} -.table > caption + thead > tr:first-child > th, -.table > colgroup + thead > tr:first-child > th, -.table > thead:first-child > tr:first-child > th, -.table > caption + thead > tr:first-child > td, -.table > colgroup + thead > tr:first-child > td, -.table > thead:first-child > tr:first-child > td { - border-top: 0; -} -.table > tbody + tbody { - border-top: 2px solid #ddd; -} -.table .table { - background-color: #fff; -} -.table-condensed > thead > tr > th, -.table-condensed > tbody > tr > th, -.table-condensed > tfoot > tr > th, -.table-condensed > thead > tr > td, -.table-condensed > tbody > tr > td, -.table-condensed > tfoot > tr > td { - padding: 5px; -} -.table-bordered { - border: 1px solid #ddd; -} -.table-bordered > thead > tr > th, -.table-bordered > tbody > tr > th, -.table-bordered > tfoot > tr > th, -.table-bordered > thead > tr > td, -.table-bordered > tbody > tr > td, -.table-bordered > tfoot > tr > td { - border: 1px solid #ddd; -} -.table-bordered > thead > tr > th, -.table-bordered > thead > tr > td { - border-bottom-width: 2px; -} -.table-striped > tbody > tr:nth-of-type(odd) { - background-color: #f9f9f9; -} -.table-hover > tbody > tr:hover { - background-color: #f5f5f5; -} -table col[class*="col-"] { - position: static; - display: table-column; - float: none; -} -table td[class*="col-"], -table th[class*="col-"] { - position: static; - display: table-cell; - float: none; -} -.table > thead > tr > td.active, -.table > tbody > tr > td.active, -.table > tfoot > tr > td.active, -.table > thead > tr > th.active, -.table > tbody > tr > th.active, -.table > tfoot > tr > th.active, -.table > thead > tr.active > td, -.table > tbody > tr.active > td, -.table > tfoot > tr.active > td, -.table > thead > tr.active > th, -.table > tbody > tr.active > th, -.table > tfoot > tr.active > th { - background-color: #f5f5f5; -} -.table-hover > tbody > tr > td.active:hover, -.table-hover > tbody > tr > th.active:hover, -.table-hover > tbody > tr.active:hover > td, -.table-hover > tbody > tr:hover > .active, -.table-hover > tbody > tr.active:hover > th { - background-color: #e8e8e8; -} -.table > thead > tr > td.success, -.table > tbody > tr > td.success, -.table > tfoot > tr > td.success, -.table > thead > tr > th.success, -.table > tbody > tr > th.success, -.table > tfoot > tr > th.success, -.table > thead > tr.success > td, -.table > tbody > tr.success > td, -.table > tfoot > tr.success > td, -.table > thead > tr.success > th, -.table > tbody > tr.success > th, -.table > tfoot > tr.success > th { - background-color: #dff0d8; -} -.table-hover > tbody > tr > td.success:hover, -.table-hover > tbody > tr > th.success:hover, -.table-hover > tbody > tr.success:hover > td, -.table-hover > tbody > tr:hover > .success, -.table-hover > tbody > tr.success:hover > th { - background-color: #d0e9c6; -} -.table > thead > tr > td.info, -.table > tbody > tr > td.info, -.table > tfoot > tr > td.info, -.table > thead > tr > th.info, -.table > tbody > tr > th.info, -.table > tfoot > tr > th.info, -.table > thead > tr.info > td, -.table > tbody > tr.info > td, -.table > tfoot > tr.info > td, -.table > thead > tr.info > th, -.table > tbody > tr.info > th, -.table > tfoot > tr.info > th { - background-color: #d9edf7; -} -.table-hover > tbody > tr > td.info:hover, -.table-hover > tbody > tr > th.info:hover, -.table-hover > tbody > tr.info:hover > td, -.table-hover > tbody > tr:hover > .info, -.table-hover > tbody > tr.info:hover > th { - background-color: #c4e3f3; -} -.table > thead > tr > td.warning, -.table > tbody > tr > td.warning, -.table > tfoot > tr > td.warning, -.table > thead > tr > th.warning, -.table > tbody > tr > th.warning, -.table > tfoot > tr > th.warning, -.table > thead > tr.warning > td, -.table > tbody > tr.warning > td, -.table > tfoot > tr.warning > td, -.table > thead > tr.warning > th, -.table > tbody > tr.warning > th, -.table > tfoot > tr.warning > th { - background-color: #fcf8e3; -} -.table-hover > tbody > tr > td.warning:hover, -.table-hover > tbody > tr > th.warning:hover, -.table-hover > tbody > tr.warning:hover > td, -.table-hover > tbody > tr:hover > .warning, -.table-hover > tbody > tr.warning:hover > th { - background-color: #faf2cc; -} -.table > thead > tr > td.danger, -.table > tbody > tr > td.danger, -.table > tfoot > tr > td.danger, -.table > thead > tr > th.danger, -.table > tbody > tr > th.danger, -.table > tfoot > tr > th.danger, -.table > thead > tr.danger > td, -.table > tbody > tr.danger > td, -.table > tfoot > tr.danger > td, -.table > thead > tr.danger > th, -.table > tbody > tr.danger > th, -.table > tfoot > tr.danger > th { - background-color: #f2dede; -} -.table-hover > tbody > tr > td.danger:hover, -.table-hover > tbody > tr > th.danger:hover, -.table-hover > tbody > tr.danger:hover > td, -.table-hover > tbody > tr:hover > .danger, -.table-hover > tbody > tr.danger:hover > th { - background-color: #ebcccc; -} -.table-responsive { - min-height: .01%; - overflow-x: auto; -} -@media screen and (max-width: 767px) { - .table-responsive { - width: 100%; - margin-bottom: 15px; - overflow-y: hidden; - -ms-overflow-style: -ms-autohiding-scrollbar; - border: 1px solid #ddd; - } - .table-responsive > .table { - margin-bottom: 0; - } - .table-responsive > .table > thead > tr > th, - .table-responsive > .table > tbody > tr > th, - .table-responsive > .table > tfoot > tr > th, - .table-responsive > .table > thead > tr > td, - .table-responsive > .table > tbody > tr > td, - .table-responsive > .table > tfoot > tr > td { - white-space: nowrap; - } - .table-responsive > .table-bordered { - border: 0; - } - .table-responsive > .table-bordered > thead > tr > th:first-child, - .table-responsive > .table-bordered > tbody > tr > th:first-child, - .table-responsive > .table-bordered > tfoot > tr > th:first-child, - .table-responsive > .table-bordered > thead > tr > td:first-child, - .table-responsive > .table-bordered > tbody > tr > td:first-child, - .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-left: 0; - } - .table-responsive > .table-bordered > thead > tr > th:last-child, - .table-responsive > .table-bordered > tbody > tr > th:last-child, - .table-responsive > .table-bordered > tfoot > tr > th:last-child, - .table-responsive > .table-bordered > thead > tr > td:last-child, - .table-responsive > .table-bordered > tbody > tr > td:last-child, - .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-right: 0; - } - .table-responsive > .table-bordered > tbody > tr:last-child > th, - .table-responsive > .table-bordered > tfoot > tr:last-child > th, - .table-responsive > .table-bordered > tbody > tr:last-child > td, - .table-responsive > .table-bordered > tfoot > tr:last-child > td { - border-bottom: 0; - } -} -fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; -} -legend { - display: block; - width: 100%; - padding: 0; - margin-bottom: 20px; - font-size: 21px; - line-height: inherit; - color: #333; - border: 0; - border-bottom: 1px solid #e5e5e5; -} -label { - display: inline-block; - max-width: 100%; - margin-bottom: 5px; - font-weight: bold; -} -input[type="search"] { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -input[type="radio"], -input[type="checkbox"] { - margin: 4px 0 0; - margin-top: 1px \9; - line-height: normal; -} -input[type="file"] { - display: block; -} -input[type="range"] { - display: block; - width: 100%; -} -select[multiple], -select[size] { - height: auto; -} -input[type="file"]:focus, -input[type="radio"]:focus, -input[type="checkbox"]:focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -output { - display: block; - padding-top: 7px; - font-size: 14px; - line-height: 1.42857143; - color: #555; -} -.form-control { - display: block; - width: 100%; - height: 34px; - padding: 6px 12px; - font-size: 14px; - line-height: 1.42857143; - color: #555; - background-color: #fff; - background-image: none; - border: 1px solid #ccc; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; - -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; -} -.form-control:focus { - border-color: #66afe9; - outline: 0; - -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); -} -.form-control::-moz-placeholder { - color: #999; - opacity: 1; -} -.form-control:-ms-input-placeholder { - color: #999; -} -.form-control::-webkit-input-placeholder { - color: #999; -} -.form-control[disabled], -.form-control[readonly], -fieldset[disabled] .form-control { - background-color: #eee; - opacity: 1; -} -.form-control[disabled], -fieldset[disabled] .form-control { - cursor: not-allowed; -} -textarea.form-control { - height: auto; -} -input[type="search"] { - -webkit-appearance: none; -} -@media screen and (-webkit-min-device-pixel-ratio: 0) { - input[type="date"].form-control, - input[type="time"].form-control, - input[type="datetime-local"].form-control, - input[type="month"].form-control { - line-height: 34px; - } - input[type="date"].input-sm, - input[type="time"].input-sm, - input[type="datetime-local"].input-sm, - input[type="month"].input-sm, - .input-group-sm input[type="date"], - .input-group-sm input[type="time"], - .input-group-sm input[type="datetime-local"], - .input-group-sm input[type="month"] { - line-height: 30px; - } - input[type="date"].input-lg, - input[type="time"].input-lg, - input[type="datetime-local"].input-lg, - input[type="month"].input-lg, - .input-group-lg input[type="date"], - .input-group-lg input[type="time"], - .input-group-lg input[type="datetime-local"], - .input-group-lg input[type="month"] { - line-height: 46px; - } -} -.form-group { - margin-bottom: 15px; -} -.radio, -.checkbox { - position: relative; - display: block; - margin-top: 10px; - margin-bottom: 10px; -} -.radio label, -.checkbox label { - min-height: 20px; - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - cursor: pointer; -} -.radio input[type="radio"], -.radio-inline input[type="radio"], -.checkbox input[type="checkbox"], -.checkbox-inline input[type="checkbox"] { - position: absolute; - margin-top: 4px \9; - margin-left: -20px; -} -.radio + .radio, -.checkbox + .checkbox { - margin-top: -5px; -} -.radio-inline, -.checkbox-inline { - position: relative; - display: inline-block; - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - vertical-align: middle; - cursor: pointer; -} -.radio-inline + .radio-inline, -.checkbox-inline + .checkbox-inline { - margin-top: 0; - margin-left: 10px; -} -input[type="radio"][disabled], -input[type="checkbox"][disabled], -input[type="radio"].disabled, -input[type="checkbox"].disabled, -fieldset[disabled] input[type="radio"], -fieldset[disabled] input[type="checkbox"] { - cursor: not-allowed; -} -.radio-inline.disabled, -.checkbox-inline.disabled, -fieldset[disabled] .radio-inline, -fieldset[disabled] .checkbox-inline { - cursor: not-allowed; -} -.radio.disabled label, -.checkbox.disabled label, -fieldset[disabled] .radio label, -fieldset[disabled] .checkbox label { - cursor: not-allowed; -} -.form-control-static { - min-height: 34px; - padding-top: 7px; - padding-bottom: 7px; - margin-bottom: 0; -} -.form-control-static.input-lg, -.form-control-static.input-sm { - padding-right: 0; - padding-left: 0; -} -.input-sm { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -select.input-sm { - height: 30px; - line-height: 30px; -} -textarea.input-sm, -select[multiple].input-sm { - height: auto; -} -.form-group-sm .form-control { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.form-group-sm select.form-control { - height: 30px; - line-height: 30px; -} -.form-group-sm textarea.form-control, -.form-group-sm select[multiple].form-control { - height: auto; -} -.form-group-sm .form-control-static { - height: 30px; - min-height: 32px; - padding: 6px 10px; - font-size: 12px; - line-height: 1.5; -} -.input-lg { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -select.input-lg { - height: 46px; - line-height: 46px; -} -textarea.input-lg, -select[multiple].input-lg { - height: auto; -} -.form-group-lg .form-control { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -.form-group-lg select.form-control { - height: 46px; - line-height: 46px; -} -.form-group-lg textarea.form-control, -.form-group-lg select[multiple].form-control { - height: auto; -} -.form-group-lg .form-control-static { - height: 46px; - min-height: 38px; - padding: 11px 16px; - font-size: 18px; - line-height: 1.3333333; -} -.has-feedback { - position: relative; -} -.has-feedback .form-control { - padding-right: 42.5px; -} -.form-control-feedback { - position: absolute; - top: 0; - right: 0; - z-index: 2; - display: block; - width: 34px; - height: 34px; - line-height: 34px; - text-align: center; - pointer-events: none; -} -.input-lg + .form-control-feedback, -.input-group-lg + .form-control-feedback, -.form-group-lg .form-control + .form-control-feedback { - width: 46px; - height: 46px; - line-height: 46px; -} -.input-sm + .form-control-feedback, -.input-group-sm + .form-control-feedback, -.form-group-sm .form-control + .form-control-feedback { - width: 30px; - height: 30px; - line-height: 30px; -} -.has-success .help-block, -.has-success .control-label, -.has-success .radio, -.has-success .checkbox, -.has-success .radio-inline, -.has-success .checkbox-inline, -.has-success.radio label, -.has-success.checkbox label, -.has-success.radio-inline label, -.has-success.checkbox-inline label { - color: #3c763d; -} -.has-success .form-control { - border-color: #3c763d; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-success .form-control:focus { - border-color: #2b542c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; -} -.has-success .input-group-addon { - color: #3c763d; - background-color: #dff0d8; - border-color: #3c763d; -} -.has-success .form-control-feedback { - color: #3c763d; -} -.has-warning .help-block, -.has-warning .control-label, -.has-warning .radio, -.has-warning .checkbox, -.has-warning .radio-inline, -.has-warning .checkbox-inline, -.has-warning.radio label, -.has-warning.checkbox label, -.has-warning.radio-inline label, -.has-warning.checkbox-inline label { - color: #8a6d3b; -} -.has-warning .form-control { - border-color: #8a6d3b; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-warning .form-control:focus { - border-color: #66512c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; -} -.has-warning .input-group-addon { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #8a6d3b; -} -.has-warning .form-control-feedback { - color: #8a6d3b; -} -.has-error .help-block, -.has-error .control-label, -.has-error .radio, -.has-error .checkbox, -.has-error .radio-inline, -.has-error .checkbox-inline, -.has-error.radio label, -.has-error.checkbox label, -.has-error.radio-inline label, -.has-error.checkbox-inline label { - color: #a94442; -} -.has-error .form-control { - border-color: #a94442; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-error .form-control:focus { - border-color: #843534; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; -} -.has-error .input-group-addon { - color: #a94442; - background-color: #f2dede; - border-color: #a94442; -} -.has-error .form-control-feedback { - color: #a94442; -} -.has-feedback label ~ .form-control-feedback { - top: 25px; -} -.has-feedback label.sr-only ~ .form-control-feedback { - top: 0; -} -.help-block { - display: block; - margin-top: 5px; - margin-bottom: 10px; - color: #737373; -} -@media (min-width: 768px) { - .form-inline .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .form-inline .form-control-static { - display: inline-block; - } - .form-inline .input-group { - display: inline-table; - vertical-align: middle; - } - .form-inline .input-group .input-group-addon, - .form-inline .input-group .input-group-btn, - .form-inline .input-group .form-control { - width: auto; - } - .form-inline .input-group > .form-control { - width: 100%; - } - .form-inline .control-label { - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .radio, - .form-inline .checkbox { - display: inline-block; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .radio label, - .form-inline .checkbox label { - padding-left: 0; - } - .form-inline .radio input[type="radio"], - .form-inline .checkbox input[type="checkbox"] { - position: relative; - margin-left: 0; - } - .form-inline .has-feedback .form-control-feedback { - top: 0; - } -} -.form-horizontal .radio, -.form-horizontal .checkbox, -.form-horizontal .radio-inline, -.form-horizontal .checkbox-inline { - padding-top: 7px; - margin-top: 0; - margin-bottom: 0; -} -.form-horizontal .radio, -.form-horizontal .checkbox { - min-height: 27px; -} -.form-horizontal .form-group { - margin-right: -15px; - margin-left: -15px; -} -@media (min-width: 768px) { - .form-horizontal .control-label { - padding-top: 7px; - margin-bottom: 0; - text-align: right; - } -} -.form-horizontal .has-feedback .form-control-feedback { - right: 15px; -} -@media (min-width: 768px) { - .form-horizontal .form-group-lg .control-label { - padding-top: 14.333333px; - font-size: 18px; - } -} -@media (min-width: 768px) { - .form-horizontal .form-group-sm .control-label { - padding-top: 6px; - font-size: 12px; - } -} -.btn { - display: inline-block; - padding: 6px 12px; - margin-bottom: 0; - font-size: 14px; - font-weight: normal; - line-height: 1.42857143; - text-align: center; - white-space: nowrap; - vertical-align: middle; - -ms-touch-action: manipulation; - touch-action: manipulation; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - background-image: none; - border: 1px solid transparent; - border-radius: 4px; -} -.btn:focus, -.btn:active:focus, -.btn.active:focus, -.btn.focus, -.btn:active.focus, -.btn.active.focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -.btn:hover, -.btn:focus, -.btn.focus { - color: #333; - text-decoration: none; -} -.btn:active, -.btn.active { - background-image: none; - outline: 0; - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn.disabled, -.btn[disabled], -fieldset[disabled] .btn { - cursor: not-allowed; - filter: alpha(opacity=65); - -webkit-box-shadow: none; - box-shadow: none; - opacity: .65; -} -a.btn.disabled, -fieldset[disabled] a.btn { - pointer-events: none; -} -.btn-default { - color: #333; - background-color: #fff; - border-color: #ccc; -} -.btn-default:focus, -.btn-default.focus { - color: #333; - background-color: #e6e6e6; - border-color: #8c8c8c; -} -.btn-default:hover { - color: #333; - background-color: #e6e6e6; - border-color: #adadad; -} -.btn-default:active, -.btn-default.active, -.open > .dropdown-toggle.btn-default { - color: #333; - background-color: #e6e6e6; - border-color: #adadad; -} -.btn-default:active:hover, -.btn-default.active:hover, -.open > .dropdown-toggle.btn-default:hover, -.btn-default:active:focus, -.btn-default.active:focus, -.open > .dropdown-toggle.btn-default:focus, -.btn-default:active.focus, -.btn-default.active.focus, -.open > .dropdown-toggle.btn-default.focus { - color: #333; - background-color: #d4d4d4; - border-color: #8c8c8c; -} -.btn-default:active, -.btn-default.active, -.open > .dropdown-toggle.btn-default { - background-image: none; -} -.btn-default.disabled, -.btn-default[disabled], -fieldset[disabled] .btn-default, -.btn-default.disabled:hover, -.btn-default[disabled]:hover, -fieldset[disabled] .btn-default:hover, -.btn-default.disabled:focus, -.btn-default[disabled]:focus, -fieldset[disabled] .btn-default:focus, -.btn-default.disabled.focus, -.btn-default[disabled].focus, -fieldset[disabled] .btn-default.focus, -.btn-default.disabled:active, -.btn-default[disabled]:active, -fieldset[disabled] .btn-default:active, -.btn-default.disabled.active, -.btn-default[disabled].active, -fieldset[disabled] .btn-default.active { - background-color: #fff; - border-color: #ccc; -} -.btn-default .badge { - color: #fff; - background-color: #333; -} -.btn-primary { - color: #fff; - background-color: #337ab7; - border-color: #2e6da4; -} -.btn-primary:focus, -.btn-primary.focus { - color: #fff; - background-color: #286090; - border-color: #122b40; -} -.btn-primary:hover { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.btn-primary:active, -.btn-primary.active, -.open > .dropdown-toggle.btn-primary { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.btn-primary:active:hover, -.btn-primary.active:hover, -.open > .dropdown-toggle.btn-primary:hover, -.btn-primary:active:focus, -.btn-primary.active:focus, -.open > .dropdown-toggle.btn-primary:focus, -.btn-primary:active.focus, -.btn-primary.active.focus, -.open > .dropdown-toggle.btn-primary.focus { - color: #fff; - background-color: #204d74; - border-color: #122b40; -} -.btn-primary:active, -.btn-primary.active, -.open > .dropdown-toggle.btn-primary { - background-image: none; -} -.btn-primary.disabled, -.btn-primary[disabled], -fieldset[disabled] .btn-primary, -.btn-primary.disabled:hover, -.btn-primary[disabled]:hover, -fieldset[disabled] .btn-primary:hover, -.btn-primary.disabled:focus, -.btn-primary[disabled]:focus, -fieldset[disabled] .btn-primary:focus, -.btn-primary.disabled.focus, -.btn-primary[disabled].focus, -fieldset[disabled] .btn-primary.focus, -.btn-primary.disabled:active, -.btn-primary[disabled]:active, -fieldset[disabled] .btn-primary:active, -.btn-primary.disabled.active, -.btn-primary[disabled].active, -fieldset[disabled] .btn-primary.active { - background-color: #337ab7; - border-color: #2e6da4; -} -.btn-primary .badge { - color: #337ab7; - background-color: #fff; -} -.btn-success { - color: #fff; - background-color: #5cb85c; - border-color: #4cae4c; -} -.btn-success:focus, -.btn-success.focus { - color: #fff; - background-color: #449d44; - border-color: #255625; -} -.btn-success:hover { - color: #fff; - background-color: #449d44; - border-color: #398439; -} -.btn-success:active, -.btn-success.active, -.open > .dropdown-toggle.btn-success { - color: #fff; - background-color: #449d44; - border-color: #398439; -} -.btn-success:active:hover, -.btn-success.active:hover, -.open > .dropdown-toggle.btn-success:hover, -.btn-success:active:focus, -.btn-success.active:focus, -.open > .dropdown-toggle.btn-success:focus, -.btn-success:active.focus, -.btn-success.active.focus, -.open > .dropdown-toggle.btn-success.focus { - color: #fff; - background-color: #398439; - border-color: #255625; -} -.btn-success:active, -.btn-success.active, -.open > .dropdown-toggle.btn-success { - background-image: none; -} -.btn-success.disabled, -.btn-success[disabled], -fieldset[disabled] .btn-success, -.btn-success.disabled:hover, -.btn-success[disabled]:hover, -fieldset[disabled] .btn-success:hover, -.btn-success.disabled:focus, -.btn-success[disabled]:focus, -fieldset[disabled] .btn-success:focus, -.btn-success.disabled.focus, -.btn-success[disabled].focus, -fieldset[disabled] .btn-success.focus, -.btn-success.disabled:active, -.btn-success[disabled]:active, -fieldset[disabled] .btn-success:active, -.btn-success.disabled.active, -.btn-success[disabled].active, -fieldset[disabled] .btn-success.active { - background-color: #5cb85c; - border-color: #4cae4c; -} -.btn-success .badge { - color: #5cb85c; - background-color: #fff; -} -.btn-info { - color: #fff; - background-color: #5bc0de; - border-color: #46b8da; -} -.btn-info:focus, -.btn-info.focus { - color: #fff; - background-color: #31b0d5; - border-color: #1b6d85; -} -.btn-info:hover { - color: #fff; - background-color: #31b0d5; - border-color: #269abc; -} -.btn-info:active, -.btn-info.active, -.open > .dropdown-toggle.btn-info { - color: #fff; - background-color: #31b0d5; - border-color: #269abc; -} -.btn-info:active:hover, -.btn-info.active:hover, -.open > .dropdown-toggle.btn-info:hover, -.btn-info:active:focus, -.btn-info.active:focus, -.open > .dropdown-toggle.btn-info:focus, -.btn-info:active.focus, -.btn-info.active.focus, -.open > .dropdown-toggle.btn-info.focus { - color: #fff; - background-color: #269abc; - border-color: #1b6d85; -} -.btn-info:active, -.btn-info.active, -.open > .dropdown-toggle.btn-info { - background-image: none; -} -.btn-info.disabled, -.btn-info[disabled], -fieldset[disabled] .btn-info, -.btn-info.disabled:hover, -.btn-info[disabled]:hover, -fieldset[disabled] .btn-info:hover, -.btn-info.disabled:focus, -.btn-info[disabled]:focus, -fieldset[disabled] .btn-info:focus, -.btn-info.disabled.focus, -.btn-info[disabled].focus, -fieldset[disabled] .btn-info.focus, -.btn-info.disabled:active, -.btn-info[disabled]:active, -fieldset[disabled] .btn-info:active, -.btn-info.disabled.active, -.btn-info[disabled].active, -fieldset[disabled] .btn-info.active { - background-color: #5bc0de; - border-color: #46b8da; -} -.btn-info .badge { - color: #5bc0de; - background-color: #fff; -} -.btn-warning { - color: #fff; - background-color: #f0ad4e; - border-color: #eea236; -} -.btn-warning:focus, -.btn-warning.focus { - color: #fff; - background-color: #ec971f; - border-color: #985f0d; -} -.btn-warning:hover { - color: #fff; - background-color: #ec971f; - border-color: #d58512; -} -.btn-warning:active, -.btn-warning.active, -.open > .dropdown-toggle.btn-warning { - color: #fff; - background-color: #ec971f; - border-color: #d58512; -} -.btn-warning:active:hover, -.btn-warning.active:hover, -.open > .dropdown-toggle.btn-warning:hover, -.btn-warning:active:focus, -.btn-warning.active:focus, -.open > .dropdown-toggle.btn-warning:focus, -.btn-warning:active.focus, -.btn-warning.active.focus, -.open > .dropdown-toggle.btn-warning.focus { - color: #fff; - background-color: #d58512; - border-color: #985f0d; -} -.btn-warning:active, -.btn-warning.active, -.open > .dropdown-toggle.btn-warning { - background-image: none; -} -.btn-warning.disabled, -.btn-warning[disabled], -fieldset[disabled] .btn-warning, -.btn-warning.disabled:hover, -.btn-warning[disabled]:hover, -fieldset[disabled] .btn-warning:hover, -.btn-warning.disabled:focus, -.btn-warning[disabled]:focus, -fieldset[disabled] .btn-warning:focus, -.btn-warning.disabled.focus, -.btn-warning[disabled].focus, -fieldset[disabled] .btn-warning.focus, -.btn-warning.disabled:active, -.btn-warning[disabled]:active, -fieldset[disabled] .btn-warning:active, -.btn-warning.disabled.active, -.btn-warning[disabled].active, -fieldset[disabled] .btn-warning.active { - background-color: #f0ad4e; - border-color: #eea236; -} -.btn-warning .badge { - color: #f0ad4e; - background-color: #fff; -} -.btn-danger { - color: #fff; - background-color: #d9534f; - border-color: #d43f3a; -} -.btn-danger:focus, -.btn-danger.focus { - color: #fff; - background-color: #c9302c; - border-color: #761c19; -} -.btn-danger:hover { - color: #fff; - background-color: #c9302c; - border-color: #ac2925; -} -.btn-danger:active, -.btn-danger.active, -.open > .dropdown-toggle.btn-danger { - color: #fff; - background-color: #c9302c; - border-color: #ac2925; -} -.btn-danger:active:hover, -.btn-danger.active:hover, -.open > .dropdown-toggle.btn-danger:hover, -.btn-danger:active:focus, -.btn-danger.active:focus, -.open > .dropdown-toggle.btn-danger:focus, -.btn-danger:active.focus, -.btn-danger.active.focus, -.open > .dropdown-toggle.btn-danger.focus { - color: #fff; - background-color: #ac2925; - border-color: #761c19; -} -.btn-danger:active, -.btn-danger.active, -.open > .dropdown-toggle.btn-danger { - background-image: none; -} -.btn-danger.disabled, -.btn-danger[disabled], -fieldset[disabled] .btn-danger, -.btn-danger.disabled:hover, -.btn-danger[disabled]:hover, -fieldset[disabled] .btn-danger:hover, -.btn-danger.disabled:focus, -.btn-danger[disabled]:focus, -fieldset[disabled] .btn-danger:focus, -.btn-danger.disabled.focus, -.btn-danger[disabled].focus, -fieldset[disabled] .btn-danger.focus, -.btn-danger.disabled:active, -.btn-danger[disabled]:active, -fieldset[disabled] .btn-danger:active, -.btn-danger.disabled.active, -.btn-danger[disabled].active, -fieldset[disabled] .btn-danger.active { - background-color: #d9534f; - border-color: #d43f3a; -} -.btn-danger .badge { - color: #d9534f; - background-color: #fff; -} -.btn-link { - font-weight: normal; - color: #337ab7; - border-radius: 0; -} -.btn-link, -.btn-link:active, -.btn-link.active, -.btn-link[disabled], -fieldset[disabled] .btn-link { - background-color: transparent; - -webkit-box-shadow: none; - box-shadow: none; -} -.btn-link, -.btn-link:hover, -.btn-link:focus, -.btn-link:active { - border-color: transparent; -} -.btn-link:hover, -.btn-link:focus { - color: #23527c; - text-decoration: underline; - background-color: transparent; -} -.btn-link[disabled]:hover, -fieldset[disabled] .btn-link:hover, -.btn-link[disabled]:focus, -fieldset[disabled] .btn-link:focus { - color: #777; - text-decoration: none; -} -.btn-lg, -.btn-group-lg > .btn { - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -.btn-sm, -.btn-group-sm > .btn { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.btn-xs, -.btn-group-xs > .btn { - padding: 1px 5px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.btn-block { - display: block; - width: 100%; -} -.btn-block + .btn-block { - margin-top: 5px; -} -input[type="submit"].btn-block, -input[type="reset"].btn-block, -input[type="button"].btn-block { - width: 100%; -} -.fade { - opacity: 0; - -webkit-transition: opacity .15s linear; - -o-transition: opacity .15s linear; - transition: opacity .15s linear; -} -.fade.in { - opacity: 1; -} -.collapse { - display: none; -} -.collapse.in { - display: block; -} -tr.collapse.in { - display: table-row; -} -tbody.collapse.in { - display: table-row-group; -} -.collapsing { - position: relative; - height: 0; - overflow: hidden; - -webkit-transition-timing-function: ease; - -o-transition-timing-function: ease; - transition-timing-function: ease; - -webkit-transition-duration: .35s; - -o-transition-duration: .35s; - transition-duration: .35s; - -webkit-transition-property: height, visibility; - -o-transition-property: height, visibility; - transition-property: height, visibility; -} -.caret { - display: inline-block; - width: 0; - height: 0; - margin-left: 2px; - vertical-align: middle; - border-top: 4px dashed; - border-top: 4px solid \9; - border-right: 4px solid transparent; - border-left: 4px solid transparent; -} -.dropup, -.dropdown { - position: relative; -} -.dropdown-toggle:focus { - outline: 0; -} -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - display: none; - float: left; - min-width: 160px; - padding: 5px 0; - margin: 2px 0 0; - font-size: 14px; - text-align: left; - list-style: none; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .15); - border-radius: 4px; - -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); - box-shadow: 0 6px 12px rgba(0, 0, 0, .175); -} -.dropdown-menu.pull-right { - right: 0; - left: auto; -} -.dropdown-menu .divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5; -} -.dropdown-menu > li > a { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - line-height: 1.42857143; - color: #333; - white-space: nowrap; -} -.dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus { - color: #262626; - text-decoration: none; - background-color: #f5f5f5; -} -.dropdown-menu > .active > a, -.dropdown-menu > .active > a:hover, -.dropdown-menu > .active > a:focus { - color: #fff; - text-decoration: none; - background-color: #337ab7; - outline: 0; -} -.dropdown-menu > .disabled > a, -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - color: #777; -} -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - text-decoration: none; - cursor: not-allowed; - background-color: transparent; - background-image: none; - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.open > .dropdown-menu { - display: block; -} -.open > a { - outline: 0; -} -.dropdown-menu-right { - right: 0; - left: auto; -} -.dropdown-menu-left { - right: auto; - left: 0; -} -.dropdown-header { - display: block; - padding: 3px 20px; - font-size: 12px; - line-height: 1.42857143; - color: #777; - white-space: nowrap; -} -.dropdown-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 990; -} -.pull-right > .dropdown-menu { - right: 0; - left: auto; -} -.dropup .caret, -.navbar-fixed-bottom .dropdown .caret { - content: ""; - border-top: 0; - border-bottom: 4px dashed; - border-bottom: 4px solid \9; -} -.dropup .dropdown-menu, -.navbar-fixed-bottom .dropdown .dropdown-menu { - top: auto; - bottom: 100%; - margin-bottom: 2px; -} -@media (min-width: 768px) { - .navbar-right .dropdown-menu { - right: 0; - left: auto; - } - .navbar-right .dropdown-menu-left { - right: auto; - left: 0; - } -} -.btn-group, -.btn-group-vertical { - position: relative; - display: inline-block; - vertical-align: middle; -} -.btn-group > .btn, -.btn-group-vertical > .btn { - position: relative; - float: left; -} -.btn-group > .btn:hover, -.btn-group-vertical > .btn:hover, -.btn-group > .btn:focus, -.btn-group-vertical > .btn:focus, -.btn-group > .btn:active, -.btn-group-vertical > .btn:active, -.btn-group > .btn.active, -.btn-group-vertical > .btn.active { - z-index: 2; -} -.btn-group .btn + .btn, -.btn-group .btn + .btn-group, -.btn-group .btn-group + .btn, -.btn-group .btn-group + .btn-group { - margin-left: -1px; -} -.btn-toolbar { - margin-left: -5px; -} -.btn-toolbar .btn, -.btn-toolbar .btn-group, -.btn-toolbar .input-group { - float: left; -} -.btn-toolbar > .btn, -.btn-toolbar > .btn-group, -.btn-toolbar > .input-group { - margin-left: 5px; -} -.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { - border-radius: 0; -} -.btn-group > .btn:first-child { - margin-left: 0; -} -.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.btn-group > .btn:last-child:not(:first-child), -.btn-group > .dropdown-toggle:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group > .btn-group { - float: left; -} -.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, -.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group .dropdown-toggle:active, -.btn-group.open .dropdown-toggle { - outline: 0; -} -.btn-group > .btn + .dropdown-toggle { - padding-right: 8px; - padding-left: 8px; -} -.btn-group > .btn-lg + .dropdown-toggle { - padding-right: 12px; - padding-left: 12px; -} -.btn-group.open .dropdown-toggle { - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn-group.open .dropdown-toggle.btn-link { - -webkit-box-shadow: none; - box-shadow: none; -} -.btn .caret { - margin-left: 0; -} -.btn-lg .caret { - border-width: 5px 5px 0; - border-bottom-width: 0; -} -.dropup .btn-lg .caret { - border-width: 0 5px 5px; -} -.btn-group-vertical > .btn, -.btn-group-vertical > .btn-group, -.btn-group-vertical > .btn-group > .btn { - display: block; - float: none; - width: 100%; - max-width: 100%; -} -.btn-group-vertical > .btn-group > .btn { - float: none; -} -.btn-group-vertical > .btn + .btn, -.btn-group-vertical > .btn + .btn-group, -.btn-group-vertical > .btn-group + .btn, -.btn-group-vertical > .btn-group + .btn-group { - margin-top: -1px; - margin-left: 0; -} -.btn-group-vertical > .btn:not(:first-child):not(:last-child) { - border-radius: 0; -} -.btn-group-vertical > .btn:first-child:not(:last-child) { - border-top-right-radius: 4px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group-vertical > .btn:last-child:not(:first-child) { - border-top-left-radius: 0; - border-top-right-radius: 0; - border-bottom-left-radius: 4px; -} -.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, -.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.btn-group-justified { - display: table; - width: 100%; - table-layout: fixed; - border-collapse: separate; -} -.btn-group-justified > .btn, -.btn-group-justified > .btn-group { - display: table-cell; - float: none; - width: 1%; -} -.btn-group-justified > .btn-group .btn { - width: 100%; -} -.btn-group-justified > .btn-group .dropdown-menu { - left: auto; -} -[data-toggle="buttons"] > .btn input[type="radio"], -[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], -[data-toggle="buttons"] > .btn input[type="checkbox"], -[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { - position: absolute; - clip: rect(0, 0, 0, 0); - pointer-events: none; -} -.input-group { - position: relative; - display: table; - border-collapse: separate; -} -.input-group[class*="col-"] { - float: none; - padding-right: 0; - padding-left: 0; -} -.input-group .form-control { - position: relative; - z-index: 2; - float: left; - width: 100%; - margin-bottom: 0; -} -.input-group-lg > .form-control, -.input-group-lg > .input-group-addon, -.input-group-lg > .input-group-btn > .btn { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -select.input-group-lg > .form-control, -select.input-group-lg > .input-group-addon, -select.input-group-lg > .input-group-btn > .btn { - height: 46px; - line-height: 46px; -} -textarea.input-group-lg > .form-control, -textarea.input-group-lg > .input-group-addon, -textarea.input-group-lg > .input-group-btn > .btn, -select[multiple].input-group-lg > .form-control, -select[multiple].input-group-lg > .input-group-addon, -select[multiple].input-group-lg > .input-group-btn > .btn { - height: auto; -} -.input-group-sm > .form-control, -.input-group-sm > .input-group-addon, -.input-group-sm > .input-group-btn > .btn { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -select.input-group-sm > .form-control, -select.input-group-sm > .input-group-addon, -select.input-group-sm > .input-group-btn > .btn { - height: 30px; - line-height: 30px; -} -textarea.input-group-sm > .form-control, -textarea.input-group-sm > .input-group-addon, -textarea.input-group-sm > .input-group-btn > .btn, -select[multiple].input-group-sm > .form-control, -select[multiple].input-group-sm > .input-group-addon, -select[multiple].input-group-sm > .input-group-btn > .btn { - height: auto; -} -.input-group-addon, -.input-group-btn, -.input-group .form-control { - display: table-cell; -} -.input-group-addon:not(:first-child):not(:last-child), -.input-group-btn:not(:first-child):not(:last-child), -.input-group .form-control:not(:first-child):not(:last-child) { - border-radius: 0; -} -.input-group-addon, -.input-group-btn { - width: 1%; - white-space: nowrap; - vertical-align: middle; -} -.input-group-addon { - padding: 6px 12px; - font-size: 14px; - font-weight: normal; - line-height: 1; - color: #555; - text-align: center; - background-color: #eee; - border: 1px solid #ccc; - border-radius: 4px; -} -.input-group-addon.input-sm { - padding: 5px 10px; - font-size: 12px; - border-radius: 3px; -} -.input-group-addon.input-lg { - padding: 10px 16px; - font-size: 18px; - border-radius: 6px; -} -.input-group-addon input[type="radio"], -.input-group-addon input[type="checkbox"] { - margin-top: 0; -} -.input-group .form-control:first-child, -.input-group-addon:first-child, -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group > .btn, -.input-group-btn:first-child > .dropdown-toggle, -.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), -.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.input-group-addon:first-child { - border-right: 0; -} -.input-group .form-control:last-child, -.input-group-addon:last-child, -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group > .btn, -.input-group-btn:last-child > .dropdown-toggle, -.input-group-btn:first-child > .btn:not(:first-child), -.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.input-group-addon:last-child { - border-left: 0; -} -.input-group-btn { - position: relative; - font-size: 0; - white-space: nowrap; -} -.input-group-btn > .btn { - position: relative; -} -.input-group-btn > .btn + .btn { - margin-left: -1px; -} -.input-group-btn > .btn:hover, -.input-group-btn > .btn:focus, -.input-group-btn > .btn:active { - z-index: 2; -} -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group { - margin-right: -1px; -} -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group { - z-index: 2; - margin-left: -1px; -} -.nav { - padding-left: 0; - margin-bottom: 0; - list-style: none; -} -.nav > li { - position: relative; - display: block; -} -.nav > li > a { - position: relative; - display: block; - padding: 10px 15px; -} -.nav > li > a:hover, -.nav > li > a:focus { - text-decoration: none; - background-color: #eee; -} -.nav > li.disabled > a { - color: #777; -} -.nav > li.disabled > a:hover, -.nav > li.disabled > a:focus { - color: #777; - text-decoration: none; - cursor: not-allowed; - background-color: transparent; -} -.nav .open > a, -.nav .open > a:hover, -.nav .open > a:focus { - background-color: #eee; - border-color: #337ab7; -} -.nav .nav-divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5; -} -.nav > li > a > img { - max-width: none; -} -.nav-tabs { - border-bottom: 1px solid #ddd; -} -.nav-tabs > li { - float: left; - margin-bottom: -1px; -} -.nav-tabs > li > a { - margin-right: 2px; - line-height: 1.42857143; - border: 1px solid transparent; - border-radius: 4px 4px 0 0; -} -.nav-tabs > li > a:hover { - border-color: #eee #eee #ddd; -} -.nav-tabs > li.active > a, -.nav-tabs > li.active > a:hover, -.nav-tabs > li.active > a:focus { - color: #555; - cursor: default; - background-color: #fff; - border: 1px solid #ddd; - border-bottom-color: transparent; -} -.nav-tabs.nav-justified { - width: 100%; - border-bottom: 0; -} -.nav-tabs.nav-justified > li { - float: none; -} -.nav-tabs.nav-justified > li > a { - margin-bottom: 5px; - text-align: center; -} -.nav-tabs.nav-justified > .dropdown .dropdown-menu { - top: auto; - left: auto; -} -@media (min-width: 768px) { - .nav-tabs.nav-justified > li { - display: table-cell; - width: 1%; - } - .nav-tabs.nav-justified > li > a { - margin-bottom: 0; - } -} -.nav-tabs.nav-justified > li > a { - margin-right: 0; - border-radius: 4px; -} -.nav-tabs.nav-justified > .active > a, -.nav-tabs.nav-justified > .active > a:hover, -.nav-tabs.nav-justified > .active > a:focus { - border: 1px solid #ddd; -} -@media (min-width: 768px) { - .nav-tabs.nav-justified > li > a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0; - } - .nav-tabs.nav-justified > .active > a, - .nav-tabs.nav-justified > .active > a:hover, - .nav-tabs.nav-justified > .active > a:focus { - border-bottom-color: #fff; - } -} -.nav-pills > li { - float: left; -} -.nav-pills > li > a { - border-radius: 4px; -} -.nav-pills > li + li { - margin-left: 2px; -} -.nav-pills > li.active > a, -.nav-pills > li.active > a:hover, -.nav-pills > li.active > a:focus { - color: #fff; - background-color: #337ab7; -} -.nav-stacked > li { - float: none; -} -.nav-stacked > li + li { - margin-top: 2px; - margin-left: 0; -} -.nav-justified { - width: 100%; -} -.nav-justified > li { - float: none; -} -.nav-justified > li > a { - margin-bottom: 5px; - text-align: center; -} -.nav-justified > .dropdown .dropdown-menu { - top: auto; - left: auto; -} -@media (min-width: 768px) { - .nav-justified > li { - display: table-cell; - width: 1%; - } - .nav-justified > li > a { - margin-bottom: 0; - } -} -.nav-tabs-justified { - border-bottom: 0; -} -.nav-tabs-justified > li > a { - margin-right: 0; - border-radius: 4px; -} -.nav-tabs-justified > .active > a, -.nav-tabs-justified > .active > a:hover, -.nav-tabs-justified > .active > a:focus { - border: 1px solid #ddd; -} -@media (min-width: 768px) { - .nav-tabs-justified > li > a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0; - } - .nav-tabs-justified > .active > a, - .nav-tabs-justified > .active > a:hover, - .nav-tabs-justified > .active > a:focus { - border-bottom-color: #fff; - } -} -.tab-content > .tab-pane { - display: none; -} -.tab-content > .active { - display: block; -} -.nav-tabs .dropdown-menu { - margin-top: -1px; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.navbar { - position: relative; - min-height: 50px; - margin-bottom: 20px; - border: 1px solid transparent; -} -@media (min-width: 768px) { - .navbar { - border-radius: 4px; - } -} -@media (min-width: 768px) { - .navbar-header { - float: left; - } -} -.navbar-collapse { - padding-right: 15px; - padding-left: 15px; - overflow-x: visible; - -webkit-overflow-scrolling: touch; - border-top: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); -} -.navbar-collapse.in { - overflow-y: auto; -} -@media (min-width: 768px) { - .navbar-collapse { - width: auto; - border-top: 0; - -webkit-box-shadow: none; - box-shadow: none; - } - .navbar-collapse.collapse { - display: block !important; - height: auto !important; - padding-bottom: 0; - overflow: visible !important; - } - .navbar-collapse.in { - overflow-y: visible; - } - .navbar-fixed-top .navbar-collapse, - .navbar-static-top .navbar-collapse, - .navbar-fixed-bottom .navbar-collapse { - padding-right: 0; - padding-left: 0; - } -} -.navbar-fixed-top .navbar-collapse, -.navbar-fixed-bottom .navbar-collapse { - max-height: 340px; -} -@media (max-device-width: 480px) and (orientation: landscape) { - .navbar-fixed-top .navbar-collapse, - .navbar-fixed-bottom .navbar-collapse { - max-height: 200px; - } -} -.container > .navbar-header, -.container-fluid > .navbar-header, -.container > .navbar-collapse, -.container-fluid > .navbar-collapse { - margin-right: -15px; - margin-left: -15px; -} -@media (min-width: 768px) { - .container > .navbar-header, - .container-fluid > .navbar-header, - .container > .navbar-collapse, - .container-fluid > .navbar-collapse { - margin-right: 0; - margin-left: 0; - } -} -.navbar-static-top { - z-index: 1000; - border-width: 0 0 1px; -} -@media (min-width: 768px) { - .navbar-static-top { - border-radius: 0; - } -} -.navbar-fixed-top, -.navbar-fixed-bottom { - position: fixed; - right: 0; - left: 0; - z-index: 1030; -} -@media (min-width: 768px) { - .navbar-fixed-top, - .navbar-fixed-bottom { - border-radius: 0; - } -} -.navbar-fixed-top { - top: 0; - border-width: 0 0 1px; -} -.navbar-fixed-bottom { - bottom: 0; - margin-bottom: 0; - border-width: 1px 0 0; -} -.navbar-brand { - float: left; - height: 50px; - padding: 15px 15px; - font-size: 18px; - line-height: 20px; -} -.navbar-brand:hover, -.navbar-brand:focus { - text-decoration: none; -} -.navbar-brand > img { - display: block; -} -@media (min-width: 768px) { - .navbar > .container .navbar-brand, - .navbar > .container-fluid .navbar-brand { - margin-left: -15px; - } -} -.navbar-toggle { - position: relative; - float: right; - padding: 9px 10px; - margin-top: 8px; - margin-right: 15px; - margin-bottom: 8px; - background-color: transparent; - background-image: none; - border: 1px solid transparent; - border-radius: 4px; -} -.navbar-toggle:focus { - outline: 0; -} -.navbar-toggle .icon-bar { - display: block; - width: 22px; - height: 2px; - border-radius: 1px; -} -.navbar-toggle .icon-bar + .icon-bar { - margin-top: 4px; -} -@media (min-width: 768px) { - .navbar-toggle { - display: none; - } -} -.navbar-nav { - margin: 7.5px -15px; -} -.navbar-nav > li > a { - padding-top: 10px; - padding-bottom: 10px; - line-height: 20px; -} -@media (max-width: 767px) { - .navbar-nav .open .dropdown-menu { - position: static; - float: none; - width: auto; - margin-top: 0; - background-color: transparent; - border: 0; - -webkit-box-shadow: none; - box-shadow: none; - } - .navbar-nav .open .dropdown-menu > li > a, - .navbar-nav .open .dropdown-menu .dropdown-header { - padding: 5px 15px 5px 25px; - } - .navbar-nav .open .dropdown-menu > li > a { - line-height: 20px; - } - .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-nav .open .dropdown-menu > li > a:focus { - background-image: none; - } -} -@media (min-width: 768px) { - .navbar-nav { - float: left; - margin: 0; - } - .navbar-nav > li { - float: left; - } - .navbar-nav > li > a { - padding-top: 15px; - padding-bottom: 15px; - } -} -.navbar-form { - padding: 10px 15px; - margin-top: 8px; - margin-right: -15px; - margin-bottom: 8px; - margin-left: -15px; - border-top: 1px solid transparent; - border-bottom: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); -} -@media (min-width: 768px) { - .navbar-form .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .navbar-form .form-control-static { - display: inline-block; - } - .navbar-form .input-group { - display: inline-table; - vertical-align: middle; - } - .navbar-form .input-group .input-group-addon, - .navbar-form .input-group .input-group-btn, - .navbar-form .input-group .form-control { - width: auto; - } - .navbar-form .input-group > .form-control { - width: 100%; - } - .navbar-form .control-label { - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .radio, - .navbar-form .checkbox { - display: inline-block; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .radio label, - .navbar-form .checkbox label { - padding-left: 0; - } - .navbar-form .radio input[type="radio"], - .navbar-form .checkbox input[type="checkbox"] { - position: relative; - margin-left: 0; - } - .navbar-form .has-feedback .form-control-feedback { - top: 0; - } -} -@media (max-width: 767px) { - .navbar-form .form-group { - margin-bottom: 5px; - } - .navbar-form .form-group:last-child { - margin-bottom: 0; - } -} -@media (min-width: 768px) { - .navbar-form { - width: auto; - padding-top: 0; - padding-bottom: 0; - margin-right: 0; - margin-left: 0; - border: 0; - -webkit-box-shadow: none; - box-shadow: none; - } -} -.navbar-nav > li > .dropdown-menu { - margin-top: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { - margin-bottom: 0; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.navbar-btn { - margin-top: 8px; - margin-bottom: 8px; -} -.navbar-btn.btn-sm { - margin-top: 10px; - margin-bottom: 10px; -} -.navbar-btn.btn-xs { - margin-top: 14px; - margin-bottom: 14px; -} -.navbar-text { - margin-top: 15px; - margin-bottom: 15px; -} -@media (min-width: 768px) { - .navbar-text { - float: left; - margin-right: 15px; - margin-left: 15px; - } -} -@media (min-width: 768px) { - .navbar-left { - float: left !important; - } - .navbar-right { - float: right !important; - margin-right: -15px; - } - .navbar-right ~ .navbar-right { - margin-right: 0; - } -} -.navbar-default { - background-color: #f8f8f8; - border-color: #e7e7e7; -} -.navbar-default .navbar-brand { - color: #777; -} -.navbar-default .navbar-brand:hover, -.navbar-default .navbar-brand:focus { - color: #5e5e5e; - background-color: transparent; -} -.navbar-default .navbar-text { - color: #777; -} -.navbar-default .navbar-nav > li > a { - color: #777; -} -.navbar-default .navbar-nav > li > a:hover, -.navbar-default .navbar-nav > li > a:focus { - color: #333; - background-color: transparent; -} -.navbar-default .navbar-nav > .active > a, -.navbar-default .navbar-nav > .active > a:hover, -.navbar-default .navbar-nav > .active > a:focus { - color: #555; - background-color: #e7e7e7; -} -.navbar-default .navbar-nav > .disabled > a, -.navbar-default .navbar-nav > .disabled > a:hover, -.navbar-default .navbar-nav > .disabled > a:focus { - color: #ccc; - background-color: transparent; -} -.navbar-default .navbar-toggle { - border-color: #ddd; -} -.navbar-default .navbar-toggle:hover, -.navbar-default .navbar-toggle:focus { - background-color: #ddd; -} -.navbar-default .navbar-toggle .icon-bar { - background-color: #888; -} -.navbar-default .navbar-collapse, -.navbar-default .navbar-form { - border-color: #e7e7e7; -} -.navbar-default .navbar-nav > .open > a, -.navbar-default .navbar-nav > .open > a:hover, -.navbar-default .navbar-nav > .open > a:focus { - color: #555; - background-color: #e7e7e7; -} -@media (max-width: 767px) { - .navbar-default .navbar-nav .open .dropdown-menu > li > a { - color: #777; - } - .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { - color: #333; - background-color: transparent; - } - .navbar-default .navbar-nav .open .dropdown-menu > .active > a, - .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #555; - background-color: #e7e7e7; - } - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { - color: #ccc; - background-color: transparent; - } -} -.navbar-default .navbar-link { - color: #777; -} -.navbar-default .navbar-link:hover { - color: #333; -} -.navbar-default .btn-link { - color: #777; -} -.navbar-default .btn-link:hover, -.navbar-default .btn-link:focus { - color: #333; -} -.navbar-default .btn-link[disabled]:hover, -fieldset[disabled] .navbar-default .btn-link:hover, -.navbar-default .btn-link[disabled]:focus, -fieldset[disabled] .navbar-default .btn-link:focus { - color: #ccc; -} -.navbar-inverse { - background-color: #222; - border-color: #080808; -} -.navbar-inverse .navbar-brand { - color: #9d9d9d; -} -.navbar-inverse .navbar-brand:hover, -.navbar-inverse .navbar-brand:focus { - color: #fff; - background-color: transparent; -} -.navbar-inverse .navbar-text { - color: #9d9d9d; -} -.navbar-inverse .navbar-nav > li > a { - color: #9d9d9d; -} -.navbar-inverse .navbar-nav > li > a:hover, -.navbar-inverse .navbar-nav > li > a:focus { - color: #fff; - background-color: transparent; -} -.navbar-inverse .navbar-nav > .active > a, -.navbar-inverse .navbar-nav > .active > a:hover, -.navbar-inverse .navbar-nav > .active > a:focus { - color: #fff; - background-color: #080808; -} -.navbar-inverse .navbar-nav > .disabled > a, -.navbar-inverse .navbar-nav > .disabled > a:hover, -.navbar-inverse .navbar-nav > .disabled > a:focus { - color: #444; - background-color: transparent; -} -.navbar-inverse .navbar-toggle { - border-color: #333; -} -.navbar-inverse .navbar-toggle:hover, -.navbar-inverse .navbar-toggle:focus { - background-color: #333; -} -.navbar-inverse .navbar-toggle .icon-bar { - background-color: #fff; -} -.navbar-inverse .navbar-collapse, -.navbar-inverse .navbar-form { - border-color: #101010; -} -.navbar-inverse .navbar-nav > .open > a, -.navbar-inverse .navbar-nav > .open > a:hover, -.navbar-inverse .navbar-nav > .open > a:focus { - color: #fff; - background-color: #080808; -} -@media (max-width: 767px) { - .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { - border-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu .divider { - background-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { - color: #9d9d9d; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { - color: #fff; - background-color: transparent; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #fff; - background-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { - color: #444; - background-color: transparent; - } -} -.navbar-inverse .navbar-link { - color: #9d9d9d; -} -.navbar-inverse .navbar-link:hover { - color: #fff; -} -.navbar-inverse .btn-link { - color: #9d9d9d; -} -.navbar-inverse .btn-link:hover, -.navbar-inverse .btn-link:focus { - color: #fff; -} -.navbar-inverse .btn-link[disabled]:hover, -fieldset[disabled] .navbar-inverse .btn-link:hover, -.navbar-inverse .btn-link[disabled]:focus, -fieldset[disabled] .navbar-inverse .btn-link:focus { - color: #444; -} -.breadcrumb { - padding: 8px 15px; - margin-bottom: 20px; - list-style: none; - background-color: #f5f5f5; - border-radius: 4px; -} -.breadcrumb > li { - display: inline-block; -} -.breadcrumb > li + li:before { - padding: 0 5px; - color: #ccc; - content: "/\00a0"; -} -.breadcrumb > .active { - color: #777; -} -.pagination { - display: inline-block; - padding-left: 0; - margin: 20px 0; - border-radius: 4px; -} -.pagination > li { - display: inline; -} -.pagination > li > a, -.pagination > li > span { - position: relative; - float: left; - padding: 6px 12px; - margin-left: -1px; - line-height: 1.42857143; - color: #337ab7; - text-decoration: none; - background-color: #fff; - border: 1px solid #ddd; -} -.pagination > li:first-child > a, -.pagination > li:first-child > span { - margin-left: 0; - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; -} -.pagination > li:last-child > a, -.pagination > li:last-child > span { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; -} -.pagination > li > a:hover, -.pagination > li > span:hover, -.pagination > li > a:focus, -.pagination > li > span:focus { - z-index: 3; - color: #23527c; - background-color: #eee; - border-color: #ddd; -} -.pagination > .active > a, -.pagination > .active > span, -.pagination > .active > a:hover, -.pagination > .active > span:hover, -.pagination > .active > a:focus, -.pagination > .active > span:focus { - z-index: 2; - color: #fff; - cursor: default; - background-color: #337ab7; - border-color: #337ab7; -} -.pagination > .disabled > span, -.pagination > .disabled > span:hover, -.pagination > .disabled > span:focus, -.pagination > .disabled > a, -.pagination > .disabled > a:hover, -.pagination > .disabled > a:focus { - color: #777; - cursor: not-allowed; - background-color: #fff; - border-color: #ddd; -} -.pagination-lg > li > a, -.pagination-lg > li > span { - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; -} -.pagination-lg > li:first-child > a, -.pagination-lg > li:first-child > span { - border-top-left-radius: 6px; - border-bottom-left-radius: 6px; -} -.pagination-lg > li:last-child > a, -.pagination-lg > li:last-child > span { - border-top-right-radius: 6px; - border-bottom-right-radius: 6px; -} -.pagination-sm > li > a, -.pagination-sm > li > span { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; -} -.pagination-sm > li:first-child > a, -.pagination-sm > li:first-child > span { - border-top-left-radius: 3px; - border-bottom-left-radius: 3px; -} -.pagination-sm > li:last-child > a, -.pagination-sm > li:last-child > span { - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; -} -.pager { - padding-left: 0; - margin: 20px 0; - text-align: center; - list-style: none; -} -.pager li { - display: inline; -} -.pager li > a, -.pager li > span { - display: inline-block; - padding: 5px 14px; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 15px; -} -.pager li > a:hover, -.pager li > a:focus { - text-decoration: none; - background-color: #eee; -} -.pager .next > a, -.pager .next > span { - float: right; -} -.pager .previous > a, -.pager .previous > span { - float: left; -} -.pager .disabled > a, -.pager .disabled > a:hover, -.pager .disabled > a:focus, -.pager .disabled > span { - color: #777; - cursor: not-allowed; - background-color: #fff; -} -.label { - display: inline; - padding: .2em .6em .3em; - font-size: 75%; - font-weight: bold; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: .25em; -} -a.label:hover, -a.label:focus { - color: #fff; - text-decoration: none; - cursor: pointer; -} -.label:empty { - display: none; -} -.btn .label { - position: relative; - top: -1px; -} -.label-default { - background-color: #777; -} -.label-default[href]:hover, -.label-default[href]:focus { - background-color: #5e5e5e; -} -.label-primary { - background-color: #337ab7; -} -.label-primary[href]:hover, -.label-primary[href]:focus { - background-color: #286090; -} -.label-success { - background-color: #5cb85c; -} -.label-success[href]:hover, -.label-success[href]:focus { - background-color: #449d44; -} -.label-info { - background-color: #5bc0de; -} -.label-info[href]:hover, -.label-info[href]:focus { - background-color: #31b0d5; -} -.label-warning { - background-color: #f0ad4e; -} -.label-warning[href]:hover, -.label-warning[href]:focus { - background-color: #ec971f; -} -.label-danger { - background-color: #d9534f; -} -.label-danger[href]:hover, -.label-danger[href]:focus { - background-color: #c9302c; -} -.badge { - display: inline-block; - min-width: 10px; - padding: 3px 7px; - font-size: 12px; - font-weight: bold; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: middle; - background-color: #777; - border-radius: 10px; -} -.badge:empty { - display: none; -} -.btn .badge { - position: relative; - top: -1px; -} -.btn-xs .badge, -.btn-group-xs > .btn .badge { - top: 0; - padding: 1px 5px; -} -a.badge:hover, -a.badge:focus { - color: #fff; - text-decoration: none; - cursor: pointer; -} -.list-group-item.active > .badge, -.nav-pills > .active > a > .badge { - color: #337ab7; - background-color: #fff; -} -.list-group-item > .badge { - float: right; -} -.list-group-item > .badge + .badge { - margin-right: 5px; -} -.nav-pills > li > a > .badge { - margin-left: 3px; -} -.jumbotron { - padding-top: 30px; - padding-bottom: 30px; - margin-bottom: 30px; - color: inherit; - background-color: #eee; -} -.jumbotron h1, -.jumbotron .h1 { - color: inherit; -} -.jumbotron p { - margin-bottom: 15px; - font-size: 21px; - font-weight: 200; -} -.jumbotron > hr { - border-top-color: #d5d5d5; -} -.container .jumbotron, -.container-fluid .jumbotron { - border-radius: 6px; -} -.jumbotron .container { - max-width: 100%; -} -@media screen and (min-width: 768px) { - .jumbotron { - padding-top: 48px; - padding-bottom: 48px; - } - .container .jumbotron, - .container-fluid .jumbotron { - padding-right: 60px; - padding-left: 60px; - } - .jumbotron h1, - .jumbotron .h1 { - font-size: 63px; - } -} -.thumbnail { - display: block; - padding: 4px; - margin-bottom: 20px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: border .2s ease-in-out; - -o-transition: border .2s ease-in-out; - transition: border .2s ease-in-out; -} -.thumbnail > img, -.thumbnail a > img { - margin-right: auto; - margin-left: auto; -} -a.thumbnail:hover, -a.thumbnail:focus, -a.thumbnail.active { - border-color: #337ab7; -} -.thumbnail .caption { - padding: 9px; - color: #333; -} -.alert { - padding: 15px; - margin-bottom: 20px; - border: 1px solid transparent; - border-radius: 4px; -} -.alert h4 { - margin-top: 0; - color: inherit; -} -.alert .alert-link { - font-weight: bold; -} -.alert > p, -.alert > ul { - margin-bottom: 0; -} -.alert > p + p { - margin-top: 5px; -} -.alert-dismissable, -.alert-dismissible { - padding-right: 35px; -} -.alert-dismissable .close, -.alert-dismissible .close { - position: relative; - top: -2px; - right: -21px; - color: inherit; -} -.alert-success { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6; -} -.alert-success hr { - border-top-color: #c9e2b3; -} -.alert-success .alert-link { - color: #2b542c; -} -.alert-info { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; -} -.alert-info hr { - border-top-color: #a6e1ec; -} -.alert-info .alert-link { - color: #245269; -} -.alert-warning { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; -} -.alert-warning hr { - border-top-color: #f7e1b5; -} -.alert-warning .alert-link { - color: #66512c; -} -.alert-danger { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} -.alert-danger hr { - border-top-color: #e4b9c0; -} -.alert-danger .alert-link { - color: #843534; -} -@-webkit-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -@-o-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -@keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -.progress { - height: 20px; - margin-bottom: 20px; - overflow: hidden; - background-color: #f5f5f5; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); -} -.progress-bar { - float: left; - width: 0; - height: 100%; - font-size: 12px; - line-height: 20px; - color: #fff; - text-align: center; - background-color: #337ab7; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - -webkit-transition: width .6s ease; - -o-transition: width .6s ease; - transition: width .6s ease; -} -.progress-striped .progress-bar, -.progress-bar-striped { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - -webkit-background-size: 40px 40px; - background-size: 40px 40px; -} -.progress.active .progress-bar, -.progress-bar.active { - -webkit-animation: progress-bar-stripes 2s linear infinite; - -o-animation: progress-bar-stripes 2s linear infinite; - animation: progress-bar-stripes 2s linear infinite; -} -.progress-bar-success { - background-color: #5cb85c; -} -.progress-striped .progress-bar-success { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-info { - background-color: #5bc0de; -} -.progress-striped .progress-bar-info { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-warning { - background-color: #f0ad4e; -} -.progress-striped .progress-bar-warning { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-danger { - background-color: #d9534f; -} -.progress-striped .progress-bar-danger { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.media { - margin-top: 15px; -} -.media:first-child { - margin-top: 0; -} -.media, -.media-body { - overflow: hidden; - zoom: 1; -} -.media-body { - width: 10000px; -} -.media-object { - display: block; -} -.media-object.img-thumbnail { - max-width: none; -} -.media-right, -.media > .pull-right { - padding-left: 10px; -} -.media-left, -.media > .pull-left { - padding-right: 10px; -} -.media-left, -.media-right, -.media-body { - display: table-cell; - vertical-align: top; -} -.media-middle { - vertical-align: middle; -} -.media-bottom { - vertical-align: bottom; -} -.media-heading { - margin-top: 0; - margin-bottom: 5px; -} -.media-list { - padding-left: 0; - list-style: none; -} -.list-group { - padding-left: 0; - margin-bottom: 20px; -} -.list-group-item { - position: relative; - display: block; - padding: 10px 15px; - margin-bottom: -1px; - background-color: #fff; - border: 1px solid #ddd; -} -.list-group-item:first-child { - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} -.list-group-item:last-child { - margin-bottom: 0; - border-bottom-right-radius: 4px; - border-bottom-left-radius: 4px; -} -a.list-group-item, -button.list-group-item { - color: #555; -} -a.list-group-item .list-group-item-heading, -button.list-group-item .list-group-item-heading { - color: #333; -} -a.list-group-item:hover, -button.list-group-item:hover, -a.list-group-item:focus, -button.list-group-item:focus { - color: #555; - text-decoration: none; - background-color: #f5f5f5; -} -button.list-group-item { - width: 100%; - text-align: left; -} -.list-group-item.disabled, -.list-group-item.disabled:hover, -.list-group-item.disabled:focus { - color: #777; - cursor: not-allowed; - background-color: #eee; -} -.list-group-item.disabled .list-group-item-heading, -.list-group-item.disabled:hover .list-group-item-heading, -.list-group-item.disabled:focus .list-group-item-heading { - color: inherit; -} -.list-group-item.disabled .list-group-item-text, -.list-group-item.disabled:hover .list-group-item-text, -.list-group-item.disabled:focus .list-group-item-text { - color: #777; -} -.list-group-item.active, -.list-group-item.active:hover, -.list-group-item.active:focus { - z-index: 2; - color: #fff; - background-color: #337ab7; - border-color: #337ab7; -} -.list-group-item.active .list-group-item-heading, -.list-group-item.active:hover .list-group-item-heading, -.list-group-item.active:focus .list-group-item-heading, -.list-group-item.active .list-group-item-heading > small, -.list-group-item.active:hover .list-group-item-heading > small, -.list-group-item.active:focus .list-group-item-heading > small, -.list-group-item.active .list-group-item-heading > .small, -.list-group-item.active:hover .list-group-item-heading > .small, -.list-group-item.active:focus .list-group-item-heading > .small { - color: inherit; -} -.list-group-item.active .list-group-item-text, -.list-group-item.active:hover .list-group-item-text, -.list-group-item.active:focus .list-group-item-text { - color: #c7ddef; -} -.list-group-item-success { - color: #3c763d; - background-color: #dff0d8; -} -a.list-group-item-success, -button.list-group-item-success { - color: #3c763d; -} -a.list-group-item-success .list-group-item-heading, -button.list-group-item-success .list-group-item-heading { - color: inherit; -} -a.list-group-item-success:hover, -button.list-group-item-success:hover, -a.list-group-item-success:focus, -button.list-group-item-success:focus { - color: #3c763d; - background-color: #d0e9c6; -} -a.list-group-item-success.active, -button.list-group-item-success.active, -a.list-group-item-success.active:hover, -button.list-group-item-success.active:hover, -a.list-group-item-success.active:focus, -button.list-group-item-success.active:focus { - color: #fff; - background-color: #3c763d; - border-color: #3c763d; -} -.list-group-item-info { - color: #31708f; - background-color: #d9edf7; -} -a.list-group-item-info, -button.list-group-item-info { - color: #31708f; -} -a.list-group-item-info .list-group-item-heading, -button.list-group-item-info .list-group-item-heading { - color: inherit; -} -a.list-group-item-info:hover, -button.list-group-item-info:hover, -a.list-group-item-info:focus, -button.list-group-item-info:focus { - color: #31708f; - background-color: #c4e3f3; -} -a.list-group-item-info.active, -button.list-group-item-info.active, -a.list-group-item-info.active:hover, -button.list-group-item-info.active:hover, -a.list-group-item-info.active:focus, -button.list-group-item-info.active:focus { - color: #fff; - background-color: #31708f; - border-color: #31708f; -} -.list-group-item-warning { - color: #8a6d3b; - background-color: #fcf8e3; -} -a.list-group-item-warning, -button.list-group-item-warning { - color: #8a6d3b; -} -a.list-group-item-warning .list-group-item-heading, -button.list-group-item-warning .list-group-item-heading { - color: inherit; -} -a.list-group-item-warning:hover, -button.list-group-item-warning:hover, -a.list-group-item-warning:focus, -button.list-group-item-warning:focus { - color: #8a6d3b; - background-color: #faf2cc; -} -a.list-group-item-warning.active, -button.list-group-item-warning.active, -a.list-group-item-warning.active:hover, -button.list-group-item-warning.active:hover, -a.list-group-item-warning.active:focus, -button.list-group-item-warning.active:focus { - color: #fff; - background-color: #8a6d3b; - border-color: #8a6d3b; -} -.list-group-item-danger { - color: #a94442; - background-color: #f2dede; -} -a.list-group-item-danger, -button.list-group-item-danger { - color: #a94442; -} -a.list-group-item-danger .list-group-item-heading, -button.list-group-item-danger .list-group-item-heading { - color: inherit; -} -a.list-group-item-danger:hover, -button.list-group-item-danger:hover, -a.list-group-item-danger:focus, -button.list-group-item-danger:focus { - color: #a94442; - background-color: #ebcccc; -} -a.list-group-item-danger.active, -button.list-group-item-danger.active, -a.list-group-item-danger.active:hover, -button.list-group-item-danger.active:hover, -a.list-group-item-danger.active:focus, -button.list-group-item-danger.active:focus { - color: #fff; - background-color: #a94442; - border-color: #a94442; -} -.list-group-item-heading { - margin-top: 0; - margin-bottom: 5px; -} -.list-group-item-text { - margin-bottom: 0; - line-height: 1.3; -} -.panel { - margin-bottom: 20px; - background-color: #fff; - border: 1px solid transparent; - border-radius: 4px; - -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: 0 1px 1px rgba(0, 0, 0, .05); -} -.panel-body { - padding: 15px; -} -.panel-heading { - padding: 10px 15px; - border-bottom: 1px solid transparent; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel-heading > .dropdown .dropdown-toggle { - color: inherit; -} -.panel-title { - margin-top: 0; - margin-bottom: 0; - font-size: 16px; - color: inherit; -} -.panel-title > a, -.panel-title > small, -.panel-title > .small, -.panel-title > small > a, -.panel-title > .small > a { - color: inherit; -} -.panel-footer { - padding: 10px 15px; - background-color: #f5f5f5; - border-top: 1px solid #ddd; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .list-group, -.panel > .panel-collapse > .list-group { - margin-bottom: 0; -} -.panel > .list-group .list-group-item, -.panel > .panel-collapse > .list-group .list-group-item { - border-width: 1px 0; - border-radius: 0; -} -.panel > .list-group:first-child .list-group-item:first-child, -.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { - border-top: 0; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .list-group:last-child .list-group-item:last-child, -.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { - border-bottom: 0; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.panel-heading + .list-group .list-group-item:first-child { - border-top-width: 0; -} -.list-group + .panel-footer { - border-top-width: 0; -} -.panel > .table, -.panel > .table-responsive > .table, -.panel > .panel-collapse > .table { - margin-bottom: 0; -} -.panel > .table caption, -.panel > .table-responsive > .table caption, -.panel > .panel-collapse > .table caption { - padding-right: 15px; - padding-left: 15px; -} -.panel > .table:first-child, -.panel > .table-responsive:first-child > .table:first-child { - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { - border-top-left-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { - border-top-right-radius: 3px; -} -.panel > .table:last-child, -.panel > .table-responsive:last-child > .table:last-child { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { - border-bottom-right-radius: 3px; -} -.panel > .panel-body + .table, -.panel > .panel-body + .table-responsive, -.panel > .table + .panel-body, -.panel > .table-responsive + .panel-body { - border-top: 1px solid #ddd; -} -.panel > .table > tbody:first-child > tr:first-child th, -.panel > .table > tbody:first-child > tr:first-child td { - border-top: 0; -} -.panel > .table-bordered, -.panel > .table-responsive > .table-bordered { - border: 0; -} -.panel > .table-bordered > thead > tr > th:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, -.panel > .table-bordered > tbody > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, -.panel > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-bordered > thead > tr > td:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, -.panel > .table-bordered > tbody > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, -.panel > .table-bordered > tfoot > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-left: 0; -} -.panel > .table-bordered > thead > tr > th:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, -.panel > .table-bordered > tbody > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, -.panel > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-bordered > thead > tr > td:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, -.panel > .table-bordered > tbody > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, -.panel > .table-bordered > tfoot > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-right: 0; -} -.panel > .table-bordered > thead > tr:first-child > td, -.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, -.panel > .table-bordered > tbody > tr:first-child > td, -.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, -.panel > .table-bordered > thead > tr:first-child > th, -.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, -.panel > .table-bordered > tbody > tr:first-child > th, -.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { - border-bottom: 0; -} -.panel > .table-bordered > tbody > tr:last-child > td, -.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, -.panel > .table-bordered > tfoot > tr:last-child > td, -.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, -.panel > .table-bordered > tbody > tr:last-child > th, -.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, -.panel > .table-bordered > tfoot > tr:last-child > th, -.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { - border-bottom: 0; -} -.panel > .table-responsive { - margin-bottom: 0; - border: 0; -} -.panel-group { - margin-bottom: 20px; -} -.panel-group .panel { - margin-bottom: 0; - border-radius: 4px; -} -.panel-group .panel + .panel { - margin-top: 5px; -} -.panel-group .panel-heading { - border-bottom: 0; -} -.panel-group .panel-heading + .panel-collapse > .panel-body, -.panel-group .panel-heading + .panel-collapse > .list-group { - border-top: 1px solid #ddd; -} -.panel-group .panel-footer { - border-top: 0; -} -.panel-group .panel-footer + .panel-collapse .panel-body { - border-bottom: 1px solid #ddd; -} -.panel-default { - border-color: #ddd; -} -.panel-default > .panel-heading { - color: #333; - background-color: #f5f5f5; - border-color: #ddd; -} -.panel-default > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #ddd; -} -.panel-default > .panel-heading .badge { - color: #f5f5f5; - background-color: #333; -} -.panel-default > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #ddd; -} -.panel-primary { - border-color: #337ab7; -} -.panel-primary > .panel-heading { - color: #fff; - background-color: #337ab7; - border-color: #337ab7; -} -.panel-primary > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #337ab7; -} -.panel-primary > .panel-heading .badge { - color: #337ab7; - background-color: #fff; -} -.panel-primary > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #337ab7; -} -.panel-success { - border-color: #d6e9c6; -} -.panel-success > .panel-heading { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6; -} -.panel-success > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #d6e9c6; -} -.panel-success > .panel-heading .badge { - color: #dff0d8; - background-color: #3c763d; -} -.panel-success > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #d6e9c6; -} -.panel-info { - border-color: #bce8f1; -} -.panel-info > .panel-heading { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; -} -.panel-info > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #bce8f1; -} -.panel-info > .panel-heading .badge { - color: #d9edf7; - background-color: #31708f; -} -.panel-info > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #bce8f1; -} -.panel-warning { - border-color: #faebcc; -} -.panel-warning > .panel-heading { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; -} -.panel-warning > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #faebcc; -} -.panel-warning > .panel-heading .badge { - color: #fcf8e3; - background-color: #8a6d3b; -} -.panel-warning > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #faebcc; -} -.panel-danger { - border-color: #ebccd1; -} -.panel-danger > .panel-heading { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} -.panel-danger > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #ebccd1; -} -.panel-danger > .panel-heading .badge { - color: #f2dede; - background-color: #a94442; -} -.panel-danger > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #ebccd1; -} -.embed-responsive { - position: relative; - display: block; - height: 0; - padding: 0; - overflow: hidden; -} -.embed-responsive .embed-responsive-item, -.embed-responsive iframe, -.embed-responsive embed, -.embed-responsive object, -.embed-responsive video { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 100%; - height: 100%; - border: 0; -} -.embed-responsive-16by9 { - padding-bottom: 56.25%; -} -.embed-responsive-4by3 { - padding-bottom: 75%; -} -.well { - min-height: 20px; - padding: 19px; - margin-bottom: 20px; - background-color: #f5f5f5; - border: 1px solid #e3e3e3; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); -} -.well blockquote { - border-color: #ddd; - border-color: rgba(0, 0, 0, .15); -} -.well-lg { - padding: 24px; - border-radius: 6px; -} -.well-sm { - padding: 9px; - border-radius: 3px; -} -.close { - float: right; - font-size: 21px; - font-weight: bold; - line-height: 1; - color: #000; - text-shadow: 0 1px 0 #fff; - filter: alpha(opacity=20); - opacity: .2; -} -.close:hover, -.close:focus { - color: #000; - text-decoration: none; - cursor: pointer; - filter: alpha(opacity=50); - opacity: .5; -} -button.close { - -webkit-appearance: none; - padding: 0; - cursor: pointer; - background: transparent; - border: 0; -} -.modal-open { - overflow: hidden; -} -.modal { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1050; - display: none; - overflow: hidden; - -webkit-overflow-scrolling: touch; - outline: 0; -} -.modal.fade .modal-dialog { - -webkit-transition: -webkit-transform .3s ease-out; - -o-transition: -o-transform .3s ease-out; - transition: transform .3s ease-out; - -webkit-transform: translate(0, -25%); - -ms-transform: translate(0, -25%); - -o-transform: translate(0, -25%); - transform: translate(0, -25%); -} -.modal.in .modal-dialog { - -webkit-transform: translate(0, 0); - -ms-transform: translate(0, 0); - -o-transform: translate(0, 0); - transform: translate(0, 0); -} -.modal-open .modal { - overflow-x: hidden; - overflow-y: auto; -} -.modal-dialog { - position: relative; - width: auto; - margin: 10px; -} -.modal-content { - position: relative; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #999; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - outline: 0; - -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); - box-shadow: 0 3px 9px rgba(0, 0, 0, .5); -} -.modal-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1040; - background-color: #000; -} -.modal-backdrop.fade { - filter: alpha(opacity=0); - opacity: 0; -} -.modal-backdrop.in { - filter: alpha(opacity=50); - opacity: .5; -} -.modal-header { - min-height: 16.42857143px; - padding: 15px; - border-bottom: 1px solid #e5e5e5; -} -.modal-header .close { - margin-top: -2px; -} -.modal-title { - margin: 0; - line-height: 1.42857143; -} -.modal-body { - position: relative; - padding: 15px; -} -.modal-footer { - padding: 15px; - text-align: right; - border-top: 1px solid #e5e5e5; -} -.modal-footer .btn + .btn { - margin-bottom: 0; - margin-left: 5px; -} -.modal-footer .btn-group .btn + .btn { - margin-left: -1px; -} -.modal-footer .btn-block + .btn-block { - margin-left: 0; -} -.modal-scrollbar-measure { - position: absolute; - top: -9999px; - width: 50px; - height: 50px; - overflow: scroll; -} -@media (min-width: 768px) { - .modal-dialog { - width: 600px; - margin: 30px auto; - } - .modal-content { - -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); - box-shadow: 0 5px 15px rgba(0, 0, 0, .5); - } - .modal-sm { - width: 300px; - } -} -@media (min-width: 992px) { - .modal-lg { - width: 900px; - } -} -.tooltip { - position: absolute; - z-index: 1070; - display: block; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 12px; - font-style: normal; - font-weight: normal; - line-height: 1.42857143; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - word-wrap: normal; - white-space: normal; - filter: alpha(opacity=0); - opacity: 0; - - line-break: auto; -} -.tooltip.in { - filter: alpha(opacity=90); - opacity: .9; -} -.tooltip.top { - padding: 5px 0; - margin-top: -3px; -} -.tooltip.right { - padding: 0 5px; - margin-left: 3px; -} -.tooltip.bottom { - padding: 5px 0; - margin-top: 3px; -} -.tooltip.left { - padding: 0 5px; - margin-left: -3px; -} -.tooltip-inner { - max-width: 200px; - padding: 3px 8px; - color: #fff; - text-align: center; - background-color: #000; - border-radius: 4px; -} -.tooltip-arrow { - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.tooltip.top .tooltip-arrow { - bottom: 0; - left: 50%; - margin-left: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.top-left .tooltip-arrow { - right: 5px; - bottom: 0; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.top-right .tooltip-arrow { - bottom: 0; - left: 5px; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.right .tooltip-arrow { - top: 50%; - left: 0; - margin-top: -5px; - border-width: 5px 5px 5px 0; - border-right-color: #000; -} -.tooltip.left .tooltip-arrow { - top: 50%; - right: 0; - margin-top: -5px; - border-width: 5px 0 5px 5px; - border-left-color: #000; -} -.tooltip.bottom .tooltip-arrow { - top: 0; - left: 50%; - margin-left: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.tooltip.bottom-left .tooltip-arrow { - top: 0; - right: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.tooltip.bottom-right .tooltip-arrow { - top: 0; - left: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.popover { - position: absolute; - top: 0; - left: 0; - z-index: 1060; - display: none; - max-width: 276px; - padding: 1px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - font-style: normal; - font-weight: normal; - line-height: 1.42857143; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - word-wrap: normal; - white-space: normal; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); - box-shadow: 0 5px 10px rgba(0, 0, 0, .2); - - line-break: auto; -} -.popover.top { - margin-top: -10px; -} -.popover.right { - margin-left: 10px; -} -.popover.bottom { - margin-top: 10px; -} -.popover.left { - margin-left: -10px; -} -.popover-title { - padding: 8px 14px; - margin: 0; - font-size: 14px; - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - border-radius: 5px 5px 0 0; -} -.popover-content { - padding: 9px 14px; -} -.popover > .arrow, -.popover > .arrow:after { - position: absolute; - display: block; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.popover > .arrow { - border-width: 11px; -} -.popover > .arrow:after { - content: ""; - border-width: 10px; -} -.popover.top > .arrow { - bottom: -11px; - left: 50%; - margin-left: -11px; - border-top-color: #999; - border-top-color: rgba(0, 0, 0, .25); - border-bottom-width: 0; -} -.popover.top > .arrow:after { - bottom: 1px; - margin-left: -10px; - content: " "; - border-top-color: #fff; - border-bottom-width: 0; -} -.popover.right > .arrow { - top: 50%; - left: -11px; - margin-top: -11px; - border-right-color: #999; - border-right-color: rgba(0, 0, 0, .25); - border-left-width: 0; -} -.popover.right > .arrow:after { - bottom: -10px; - left: 1px; - content: " "; - border-right-color: #fff; - border-left-width: 0; -} -.popover.bottom > .arrow { - top: -11px; - left: 50%; - margin-left: -11px; - border-top-width: 0; - border-bottom-color: #999; - border-bottom-color: rgba(0, 0, 0, .25); -} -.popover.bottom > .arrow:after { - top: 1px; - margin-left: -10px; - content: " "; - border-top-width: 0; - border-bottom-color: #fff; -} -.popover.left > .arrow { - top: 50%; - right: -11px; - margin-top: -11px; - border-right-width: 0; - border-left-color: #999; - border-left-color: rgba(0, 0, 0, .25); -} -.popover.left > .arrow:after { - right: 1px; - bottom: -10px; - content: " "; - border-right-width: 0; - border-left-color: #fff; -} -.carousel { - position: relative; -} -.carousel-inner { - position: relative; - width: 100%; - overflow: hidden; -} -.carousel-inner > .item { - position: relative; - display: none; - -webkit-transition: .6s ease-in-out left; - -o-transition: .6s ease-in-out left; - transition: .6s ease-in-out left; -} -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - line-height: 1; -} -@media all and (transform-3d), (-webkit-transform-3d) { - .carousel-inner > .item { - -webkit-transition: -webkit-transform .6s ease-in-out; - -o-transition: -o-transform .6s ease-in-out; - transition: transform .6s ease-in-out; - - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - -webkit-perspective: 1000px; - perspective: 1000px; - } - .carousel-inner > .item.next, - .carousel-inner > .item.active.right { - left: 0; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } - .carousel-inner > .item.prev, - .carousel-inner > .item.active.left { - left: 0; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } - .carousel-inner > .item.next.left, - .carousel-inner > .item.prev.right, - .carousel-inner > .item.active { - left: 0; - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} -.carousel-inner > .active, -.carousel-inner > .next, -.carousel-inner > .prev { - display: block; -} -.carousel-inner > .active { - left: 0; -} -.carousel-inner > .next, -.carousel-inner > .prev { - position: absolute; - top: 0; - width: 100%; -} -.carousel-inner > .next { - left: 100%; -} -.carousel-inner > .prev { - left: -100%; -} -.carousel-inner > .next.left, -.carousel-inner > .prev.right { - left: 0; -} -.carousel-inner > .active.left { - left: -100%; -} -.carousel-inner > .active.right { - left: 100%; -} -.carousel-control { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 15%; - font-size: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6); - filter: alpha(opacity=50); - opacity: .5; -} -.carousel-control.left { - background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); - background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); - background-repeat: repeat-x; -} -.carousel-control.right { - right: 0; - left: auto; - background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); - background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); - background-repeat: repeat-x; -} -.carousel-control:hover, -.carousel-control:focus { - color: #fff; - text-decoration: none; - filter: alpha(opacity=90); - outline: 0; - opacity: .9; -} -.carousel-control .icon-prev, -.carousel-control .icon-next, -.carousel-control .glyphicon-chevron-left, -.carousel-control .glyphicon-chevron-right { - position: absolute; - top: 50%; - z-index: 5; - display: inline-block; - margin-top: -10px; -} -.carousel-control .icon-prev, -.carousel-control .glyphicon-chevron-left { - left: 50%; - margin-left: -10px; -} -.carousel-control .icon-next, -.carousel-control .glyphicon-chevron-right { - right: 50%; - margin-right: -10px; -} -.carousel-control .icon-prev, -.carousel-control .icon-next { - width: 20px; - height: 20px; - font-family: serif; - line-height: 1; -} -.carousel-control .icon-prev:before { - content: '\2039'; -} -.carousel-control .icon-next:before { - content: '\203a'; -} -.carousel-indicators { - position: absolute; - bottom: 10px; - left: 50%; - z-index: 15; - width: 60%; - padding-left: 0; - margin-left: -30%; - text-align: center; - list-style: none; -} -.carousel-indicators li { - display: inline-block; - width: 10px; - height: 10px; - margin: 1px; - text-indent: -999px; - cursor: pointer; - background-color: #000 \9; - background-color: rgba(0, 0, 0, 0); - border: 1px solid #fff; - border-radius: 10px; -} -.carousel-indicators .active { - width: 12px; - height: 12px; - margin: 0; - background-color: #fff; -} -.carousel-caption { - position: absolute; - right: 15%; - bottom: 20px; - left: 15%; - z-index: 10; - padding-top: 20px; - padding-bottom: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6); -} -.carousel-caption .btn { - text-shadow: none; -} -@media screen and (min-width: 768px) { - .carousel-control .glyphicon-chevron-left, - .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-prev, - .carousel-control .icon-next { - width: 30px; - height: 30px; - margin-top: -15px; - font-size: 30px; - } - .carousel-control .glyphicon-chevron-left, - .carousel-control .icon-prev { - margin-left: -15px; - } - .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-next { - margin-right: -15px; - } - .carousel-caption { - right: 20%; - left: 20%; - padding-bottom: 30px; - } - .carousel-indicators { - bottom: 20px; - } -} -.clearfix:before, -.clearfix:after, -.dl-horizontal dd:before, -.dl-horizontal dd:after, -.container:before, -.container:after, -.container-fluid:before, -.container-fluid:after, -.row:before, -.row:after, -.form-horizontal .form-group:before, -.form-horizontal .form-group:after, -.btn-toolbar:before, -.btn-toolbar:after, -.btn-group-vertical > .btn-group:before, -.btn-group-vertical > .btn-group:after, -.nav:before, -.nav:after, -.navbar:before, -.navbar:after, -.navbar-header:before, -.navbar-header:after, -.navbar-collapse:before, -.navbar-collapse:after, -.pager:before, -.pager:after, -.panel-body:before, -.panel-body:after, -.modal-footer:before, -.modal-footer:after { - display: table; - content: " "; -} -.clearfix:after, -.dl-horizontal dd:after, -.container:after, -.container-fluid:after, -.row:after, -.form-horizontal .form-group:after, -.btn-toolbar:after, -.btn-group-vertical > .btn-group:after, -.nav:after, -.navbar:after, -.navbar-header:after, -.navbar-collapse:after, -.pager:after, -.panel-body:after, -.modal-footer:after { - clear: both; -} -.center-block { - display: block; - margin-right: auto; - margin-left: auto; -} -.pull-right { - float: right !important; -} -.pull-left { - float: left !important; -} -.hide { - display: none !important; -} -.show { - display: block !important; -} -.invisible { - visibility: hidden; -} -.text-hide { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} -.hidden { - display: none !important; -} -.affix { - position: fixed; -} -@-ms-viewport { - width: device-width; -} -.visible-xs, -.visible-sm, -.visible-md, -.visible-lg { - display: none !important; -} -.visible-xs-block, -.visible-xs-inline, -.visible-xs-inline-block, -.visible-sm-block, -.visible-sm-inline, -.visible-sm-inline-block, -.visible-md-block, -.visible-md-inline, -.visible-md-inline-block, -.visible-lg-block, -.visible-lg-inline, -.visible-lg-inline-block { - display: none !important; -} -@media (max-width: 767px) { - .visible-xs { - display: block !important; - } - table.visible-xs { - display: table !important; - } - tr.visible-xs { - display: table-row !important; - } - th.visible-xs, - td.visible-xs { - display: table-cell !important; - } -} -@media (max-width: 767px) { - .visible-xs-block { - display: block !important; - } -} -@media (max-width: 767px) { - .visible-xs-inline { - display: inline !important; - } -} -@media (max-width: 767px) { - .visible-xs-inline-block { - display: inline-block !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm { - display: block !important; - } - table.visible-sm { - display: table !important; - } - tr.visible-sm { - display: table-row !important; - } - th.visible-sm, - td.visible-sm { - display: table-cell !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-block { - display: block !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-inline { - display: inline !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-inline-block { - display: inline-block !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md { - display: block !important; - } - table.visible-md { - display: table !important; - } - tr.visible-md { - display: table-row !important; - } - th.visible-md, - td.visible-md { - display: table-cell !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-block { - display: block !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-inline { - display: inline !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-inline-block { - display: inline-block !important; - } -} -@media (min-width: 1200px) { - .visible-lg { - display: block !important; - } - table.visible-lg { - display: table !important; - } - tr.visible-lg { - display: table-row !important; - } - th.visible-lg, - td.visible-lg { - display: table-cell !important; - } -} -@media (min-width: 1200px) { - .visible-lg-block { - display: block !important; - } -} -@media (min-width: 1200px) { - .visible-lg-inline { - display: inline !important; - } -} -@media (min-width: 1200px) { - .visible-lg-inline-block { - display: inline-block !important; - } -} -@media (max-width: 767px) { - .hidden-xs { - display: none !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .hidden-sm { - display: none !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .hidden-md { - display: none !important; - } -} -@media (min-width: 1200px) { - .hidden-lg { - display: none !important; - } -} -.visible-print { - display: none !important; -} -@media print { - .visible-print { - display: block !important; - } - table.visible-print { - display: table !important; - } - tr.visible-print { - display: table-row !important; - } - th.visible-print, - td.visible-print { - display: table-cell !important; - } -} -.visible-print-block { - display: none !important; -} -@media print { - .visible-print-block { - display: block !important; - } -} -.visible-print-inline { - display: none !important; -} -@media print { - .visible-print-inline { - display: inline !important; - } -} -.visible-print-inline-block { - display: none !important; -} -@media print { - .visible-print-inline-block { - display: inline-block !important; - } -} -@media print { - .hidden-print { - display: none !important; - } -} -/*# sourceMappingURL=bootstrap.css.map */ diff --git a/samples/bookinfo/src/productpage/static/bootstrap/css/bootstrap.css.map b/samples/bookinfo/src/productpage/static/bootstrap/css/bootstrap.css.map deleted file mode 100644 index 9f60ed2b1..000000000 --- a/samples/bookinfo/src/productpage/static/bootstrap/css/bootstrap.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["bootstrap.css","less/normalize.less","less/print.less","less/glyphicons.less","less/scaffolding.less","less/mixins/vendor-prefixes.less","less/mixins/tab-focus.less","less/mixins/image.less","less/type.less","less/mixins/text-emphasis.less","less/mixins/background-variant.less","less/mixins/text-overflow.less","less/code.less","less/grid.less","less/mixins/grid.less","less/mixins/grid-framework.less","less/tables.less","less/mixins/table-row.less","less/forms.less","less/mixins/forms.less","less/buttons.less","less/mixins/buttons.less","less/mixins/opacity.less","less/component-animations.less","less/dropdowns.less","less/mixins/nav-divider.less","less/mixins/reset-filter.less","less/button-groups.less","less/mixins/border-radius.less","less/input-groups.less","less/navs.less","less/navbar.less","less/mixins/nav-vertical-align.less","less/utilities.less","less/breadcrumbs.less","less/pagination.less","less/mixins/pagination.less","less/pager.less","less/labels.less","less/mixins/labels.less","less/badges.less","less/jumbotron.less","less/thumbnails.less","less/alerts.less","less/mixins/alerts.less","less/progress-bars.less","less/mixins/gradients.less","less/mixins/progress-bar.less","less/media.less","less/list-group.less","less/mixins/list-group.less","less/panels.less","less/mixins/panels.less","less/responsive-embed.less","less/wells.less","less/close.less","less/modals.less","less/tooltip.less","less/mixins/reset-text.less","less/popovers.less","less/carousel.less","less/mixins/clearfix.less","less/mixins/center-block.less","less/mixins/hide-text.less","less/responsive-utilities.less","less/mixins/responsive-visibility.less"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,4EAA4E;ACG5E;EACE,wBAAA;EACA,2BAAA;EACA,+BAAA;CDDD;ACQD;EACE,UAAA;CDND;ACmBD;;;;;;;;;;;;;EAaE,eAAA;CDjBD;ACyBD;;;;EAIE,sBAAA;EACA,yBAAA;CDvBD;AC+BD;EACE,cAAA;EACA,UAAA;CD7BD;ACqCD;;EAEE,cAAA;CDnCD;AC6CD;EACE,8BAAA;CD3CD;ACmDD;;EAEE,WAAA;CDjDD;AC2DD;EACE,0BAAA;CDzDD;ACgED;;EAEE,kBAAA;CD9DD;ACqED;EACE,mBAAA;CDnED;AC2ED;EACE,eAAA;EACA,iBAAA;CDzED;ACgFD;EACE,iBAAA;EACA,YAAA;CD9ED;ACqFD;EACE,eAAA;CDnFD;AC0FD;;EAEE,eAAA;EACA,eAAA;EACA,mBAAA;EACA,yBAAA;CDxFD;AC2FD;EACE,YAAA;CDzFD;AC4FD;EACE,gBAAA;CD1FD;ACoGD;EACE,UAAA;CDlGD;ACyGD;EACE,iBAAA;CDvGD;ACiHD;EACE,iBAAA;CD/GD;ACsHD;EACE,gCAAA;KAAA,6BAAA;UAAA,wBAAA;EACA,UAAA;CDpHD;AC2HD;EACE,eAAA;CDzHD;ACgID;;;;EAIE,kCAAA;EACA,eAAA;CD9HD;ACgJD;;;;;EAKE,eAAA;EACA,cAAA;EACA,UAAA;CD9ID;ACqJD;EACE,kBAAA;CDnJD;AC6JD;;EAEE,qBAAA;CD3JD;ACsKD;;;;EAIE,2BAAA;EACA,gBAAA;CDpKD;AC2KD;;EAEE,gBAAA;CDzKD;ACgLD;;EAEE,UAAA;EACA,WAAA;CD9KD;ACsLD;EACE,oBAAA;CDpLD;AC+LD;;EAEE,+BAAA;KAAA,4BAAA;UAAA,uBAAA;EACA,WAAA;CD7LD;ACsMD;;EAEE,aAAA;CDpMD;AC4MD;EACE,8BAAA;EACA,gCAAA;KAAA,6BAAA;UAAA,wBAAA;CD1MD;ACmND;;EAEE,yBAAA;CDjND;ACwND;EACE,0BAAA;EACA,cAAA;EACA,+BAAA;CDtND;AC8ND;EACE,UAAA;EACA,WAAA;CD5ND;ACmOD;EACE,eAAA;CDjOD;ACyOD;EACE,kBAAA;CDvOD;ACiPD;EACE,0BAAA;EACA,kBAAA;CD/OD;ACkPD;;EAEE,WAAA;CDhPD;AACD,qFAAqF;AElFrF;EA7FI;;;IAGI,mCAAA;IACA,uBAAA;IACA,oCAAA;YAAA,4BAAA;IACA,6BAAA;GFkLL;EE/KC;;IAEI,2BAAA;GFiLL;EE9KC;IACI,6BAAA;GFgLL;EE7KC;IACI,8BAAA;GF+KL;EE1KC;;IAEI,YAAA;GF4KL;EEzKC;;IAEI,uBAAA;IACA,yBAAA;GF2KL;EExKC;IACI,4BAAA;GF0KL;EEvKC;;IAEI,yBAAA;GFyKL;EEtKC;IACI,2BAAA;GFwKL;EErKC;;;IAGI,WAAA;IACA,UAAA;GFuKL;EEpKC;;IAEI,wBAAA;GFsKL;EEhKC;IACI,cAAA;GFkKL;EEhKC;;IAGQ,kCAAA;GFiKT;EE9JC;IACI,uBAAA;GFgKL;EE7JC;IACI,qCAAA;GF+JL;EEhKC;;IAKQ,kCAAA;GF+JT;EE5JC;;IAGQ,kCAAA;GF6JT;CACF;AGnPD;EACE,oCAAA;EACA,sDAAA;EACA,gYAAA;CHqPD;AG7OD;EACE,mBAAA;EACA,SAAA;EACA,sBAAA;EACA,oCAAA;EACA,mBAAA;EACA,oBAAA;EACA,eAAA;EACA,oCAAA;EACA,mCAAA;CH+OD;AG3OmC;EAAW,eAAA;CH8O9C;AG7OmC;EAAW,eAAA;CHgP9C;AG9OmC;;EAAW,iBAAA;CHkP9C;AGjPmC;EAAW,iBAAA;CHoP9C;AGnPmC;EAAW,iBAAA;CHsP9C;AGrPmC;EAAW,iBAAA;CHwP9C;AGvPmC;EAAW,iBAAA;CH0P9C;AGzPmC;EAAW,iBAAA;CH4P9C;AG3PmC;EAAW,iBAAA;CH8P9C;AG7PmC;EAAW,iBAAA;CHgQ9C;AG/PmC;EAAW,iBAAA;CHkQ9C;AGjQmC;EAAW,iBAAA;CHoQ9C;AGnQmC;EAAW,iBAAA;CHsQ9C;AGrQmC;EAAW,iBAAA;CHwQ9C;AGvQmC;EAAW,iBAAA;CH0Q9C;AGzQmC;EAAW,iBAAA;CH4Q9C;AG3QmC;EAAW,iBAAA;CH8Q9C;AG7QmC;EAAW,iBAAA;CHgR9C;AG/QmC;EAAW,iBAAA;CHkR9C;AGjRmC;EAAW,iBAAA;CHoR9C;AGnRmC;EAAW,iBAAA;CHsR9C;AGrRmC;EAAW,iBAAA;CHwR9C;AGvRmC;EAAW,iBAAA;CH0R9C;AGzRmC;EAAW,iBAAA;CH4R9C;AG3RmC;EAAW,iBAAA;CH8R9C;AG7RmC;EAAW,iBAAA;CHgS9C;AG/RmC;EAAW,iBAAA;CHkS9C;AGjSmC;EAAW,iBAAA;CHoS9C;AGnSmC;EAAW,iBAAA;CHsS9C;AGrSmC;EAAW,iBAAA;CHwS9C;AGvSmC;EAAW,iBAAA;CH0S9C;AGzSmC;EAAW,iBAAA;CH4S9C;AG3SmC;EAAW,iBAAA;CH8S9C;AG7SmC;EAAW,iBAAA;CHgT9C;AG/SmC;EAAW,iBAAA;CHkT9C;AGjTmC;EAAW,iBAAA;CHoT9C;AGnTmC;EAAW,iBAAA;CHsT9C;AGrTmC;EAAW,iBAAA;CHwT9C;AGvTmC;EAAW,iBAAA;CH0T9C;AGzTmC;EAAW,iBAAA;CH4T9C;AG3TmC;EAAW,iBAAA;CH8T9C;AG7TmC;EAAW,iBAAA;CHgU9C;AG/TmC;EAAW,iBAAA;CHkU9C;AGjUmC;EAAW,iBAAA;CHoU9C;AGnUmC;EAAW,iBAAA;CHsU9C;AGrUmC;EAAW,iBAAA;CHwU9C;AGvUmC;EAAW,iBAAA;CH0U9C;AGzUmC;EAAW,iBAAA;CH4U9C;AG3UmC;EAAW,iBAAA;CH8U9C;AG7UmC;EAAW,iBAAA;CHgV9C;AG/UmC;EAAW,iBAAA;CHkV9C;AGjVmC;EAAW,iBAAA;CHoV9C;AGnVmC;EAAW,iBAAA;CHsV9C;AGrVmC;EAAW,iBAAA;CHwV9C;AGvVmC;EAAW,iBAAA;CH0V9C;AGzVmC;EAAW,iBAAA;CH4V9C;AG3VmC;EAAW,iBAAA;CH8V9C;AG7VmC;EAAW,iBAAA;CHgW9C;AG/VmC;EAAW,iBAAA;CHkW9C;AGjWmC;EAAW,iBAAA;CHoW9C;AGnWmC;EAAW,iBAAA;CHsW9C;AGrWmC;EAAW,iBAAA;CHwW9C;AGvWmC;EAAW,iBAAA;CH0W9C;AGzWmC;EAAW,iBAAA;CH4W9C;AG3WmC;EAAW,iBAAA;CH8W9C;AG7WmC;EAAW,iBAAA;CHgX9C;AG/WmC;EAAW,iBAAA;CHkX9C;AGjXmC;EAAW,iBAAA;CHoX9C;AGnXmC;EAAW,iBAAA;CHsX9C;AGrXmC;EAAW,iBAAA;CHwX9C;AGvXmC;EAAW,iBAAA;CH0X9C;AGzXmC;EAAW,iBAAA;CH4X9C;AG3XmC;EAAW,iBAAA;CH8X9C;AG7XmC;EAAW,iBAAA;CHgY9C;AG/XmC;EAAW,iBAAA;CHkY9C;AGjYmC;EAAW,iBAAA;CHoY9C;AGnYmC;EAAW,iBAAA;CHsY9C;AGrYmC;EAAW,iBAAA;CHwY9C;AGvYmC;EAAW,iBAAA;CH0Y9C;AGzYmC;EAAW,iBAAA;CH4Y9C;AG3YmC;EAAW,iBAAA;CH8Y9C;AG7YmC;EAAW,iBAAA;CHgZ9C;AG/YmC;EAAW,iBAAA;CHkZ9C;AGjZmC;EAAW,iBAAA;CHoZ9C;AGnZmC;EAAW,iBAAA;CHsZ9C;AGrZmC;EAAW,iBAAA;CHwZ9C;AGvZmC;EAAW,iBAAA;CH0Z9C;AGzZmC;EAAW,iBAAA;CH4Z9C;AG3ZmC;EAAW,iBAAA;CH8Z9C;AG7ZmC;EAAW,iBAAA;CHga9C;AG/ZmC;EAAW,iBAAA;CHka9C;AGjamC;EAAW,iBAAA;CHoa9C;AGnamC;EAAW,iBAAA;CHsa9C;AGramC;EAAW,iBAAA;CHwa9C;AGvamC;EAAW,iBAAA;CH0a9C;AGzamC;EAAW,iBAAA;CH4a9C;AG3amC;EAAW,iBAAA;CH8a9C;AG7amC;EAAW,iBAAA;CHgb9C;AG/amC;EAAW,iBAAA;CHkb9C;AGjbmC;EAAW,iBAAA;CHob9C;AGnbmC;EAAW,iBAAA;CHsb9C;AGrbmC;EAAW,iBAAA;CHwb9C;AGvbmC;EAAW,iBAAA;CH0b9C;AGzbmC;EAAW,iBAAA;CH4b9C;AG3bmC;EAAW,iBAAA;CH8b9C;AG7bmC;EAAW,iBAAA;CHgc9C;AG/bmC;EAAW,iBAAA;CHkc9C;AGjcmC;EAAW,iBAAA;CHoc9C;AGncmC;EAAW,iBAAA;CHsc9C;AGrcmC;EAAW,iBAAA;CHwc9C;AGvcmC;EAAW,iBAAA;CH0c9C;AGzcmC;EAAW,iBAAA;CH4c9C;AG3cmC;EAAW,iBAAA;CH8c9C;AG7cmC;EAAW,iBAAA;CHgd9C;AG/cmC;EAAW,iBAAA;CHkd9C;AGjdmC;EAAW,iBAAA;CHod9C;AGndmC;EAAW,iBAAA;CHsd9C;AGrdmC;EAAW,iBAAA;CHwd9C;AGvdmC;EAAW,iBAAA;CH0d9C;AGzdmC;EAAW,iBAAA;CH4d9C;AG3dmC;EAAW,iBAAA;CH8d9C;AG7dmC;EAAW,iBAAA;CHge9C;AG/dmC;EAAW,iBAAA;CHke9C;AGjemC;EAAW,iBAAA;CHoe9C;AGnemC;EAAW,iBAAA;CHse9C;AGremC;EAAW,iBAAA;CHwe9C;AGvemC;EAAW,iBAAA;CH0e9C;AGzemC;EAAW,iBAAA;CH4e9C;AG3emC;EAAW,iBAAA;CH8e9C;AG7emC;EAAW,iBAAA;CHgf9C;AG/emC;EAAW,iBAAA;CHkf9C;AGjfmC;EAAW,iBAAA;CHof9C;AGnfmC;EAAW,iBAAA;CHsf9C;AGrfmC;EAAW,iBAAA;CHwf9C;AGvfmC;EAAW,iBAAA;CH0f9C;AGzfmC;EAAW,iBAAA;CH4f9C;AG3fmC;EAAW,iBAAA;CH8f9C;AG7fmC;EAAW,iBAAA;CHggB9C;AG/fmC;EAAW,iBAAA;CHkgB9C;AGjgBmC;EAAW,iBAAA;CHogB9C;AGngBmC;EAAW,iBAAA;CHsgB9C;AGrgBmC;EAAW,iBAAA;CHwgB9C;AGvgBmC;EAAW,iBAAA;CH0gB9C;AGzgBmC;EAAW,iBAAA;CH4gB9C;AG3gBmC;EAAW,iBAAA;CH8gB9C;AG7gBmC;EAAW,iBAAA;CHghB9C;AG/gBmC;EAAW,iBAAA;CHkhB9C;AGjhBmC;EAAW,iBAAA;CHohB9C;AGnhBmC;EAAW,iBAAA;CHshB9C;AGrhBmC;EAAW,iBAAA;CHwhB9C;AGvhBmC;EAAW,iBAAA;CH0hB9C;AGzhBmC;EAAW,iBAAA;CH4hB9C;AG3hBmC;EAAW,iBAAA;CH8hB9C;AG7hBmC;EAAW,iBAAA;CHgiB9C;AG/hBmC;EAAW,iBAAA;CHkiB9C;AGjiBmC;EAAW,iBAAA;CHoiB9C;AGniBmC;EAAW,iBAAA;CHsiB9C;AGriBmC;EAAW,iBAAA;CHwiB9C;AGviBmC;EAAW,iBAAA;CH0iB9C;AGziBmC;EAAW,iBAAA;CH4iB9C;AG3iBmC;EAAW,iBAAA;CH8iB9C;AG7iBmC;EAAW,iBAAA;CHgjB9C;AG/iBmC;EAAW,iBAAA;CHkjB9C;AGjjBmC;EAAW,iBAAA;CHojB9C;AGnjBmC;EAAW,iBAAA;CHsjB9C;AGrjBmC;EAAW,iBAAA;CHwjB9C;AGvjBmC;EAAW,iBAAA;CH0jB9C;AGzjBmC;EAAW,iBAAA;CH4jB9C;AG3jBmC;EAAW,iBAAA;CH8jB9C;AG7jBmC;EAAW,iBAAA;CHgkB9C;AG/jBmC;EAAW,iBAAA;CHkkB9C;AGjkBmC;EAAW,iBAAA;CHokB9C;AGnkBmC;EAAW,iBAAA;CHskB9C;AGrkBmC;EAAW,iBAAA;CHwkB9C;AGvkBmC;EAAW,iBAAA;CH0kB9C;AGzkBmC;EAAW,iBAAA;CH4kB9C;AG3kBmC;EAAW,iBAAA;CH8kB9C;AG7kBmC;EAAW,iBAAA;CHglB9C;AG/kBmC;EAAW,iBAAA;CHklB9C;AGjlBmC;EAAW,iBAAA;CHolB9C;AGnlBmC;EAAW,iBAAA;CHslB9C;AGrlBmC;EAAW,iBAAA;CHwlB9C;AGvlBmC;EAAW,iBAAA;CH0lB9C;AGzlBmC;EAAW,iBAAA;CH4lB9C;AG3lBmC;EAAW,iBAAA;CH8lB9C;AG7lBmC;EAAW,iBAAA;CHgmB9C;AG/lBmC;EAAW,iBAAA;CHkmB9C;AGjmBmC;EAAW,iBAAA;CHomB9C;AGnmBmC;EAAW,iBAAA;CHsmB9C;AGrmBmC;EAAW,iBAAA;CHwmB9C;AGvmBmC;EAAW,iBAAA;CH0mB9C;AGzmBmC;EAAW,iBAAA;CH4mB9C;AG3mBmC;EAAW,iBAAA;CH8mB9C;AG7mBmC;EAAW,iBAAA;CHgnB9C;AG/mBmC;EAAW,iBAAA;CHknB9C;AGjnBmC;EAAW,iBAAA;CHonB9C;AGnnBmC;EAAW,iBAAA;CHsnB9C;AGrnBmC;EAAW,iBAAA;CHwnB9C;AGvnBmC;EAAW,iBAAA;CH0nB9C;AGznBmC;EAAW,iBAAA;CH4nB9C;AG3nBmC;EAAW,iBAAA;CH8nB9C;AG7nBmC;EAAW,iBAAA;CHgoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AGvoBmC;EAAW,iBAAA;CH0oB9C;AGzoBmC;EAAW,iBAAA;CH4oB9C;AG3oBmC;EAAW,iBAAA;CH8oB9C;AG7oBmC;EAAW,iBAAA;CHgpB9C;AG/oBmC;EAAW,iBAAA;CHkpB9C;AGjpBmC;EAAW,iBAAA;CHopB9C;AGnpBmC;EAAW,iBAAA;CHspB9C;AGrpBmC;EAAW,iBAAA;CHwpB9C;AGvpBmC;EAAW,iBAAA;CH0pB9C;AGzpBmC;EAAW,iBAAA;CH4pB9C;AG3pBmC;EAAW,iBAAA;CH8pB9C;AG7pBmC;EAAW,iBAAA;CHgqB9C;AG/pBmC;EAAW,iBAAA;CHkqB9C;AGjqBmC;EAAW,iBAAA;CHoqB9C;AGnqBmC;EAAW,iBAAA;CHsqB9C;AGrqBmC;EAAW,iBAAA;CHwqB9C;AGvqBmC;EAAW,iBAAA;CH0qB9C;AGzqBmC;EAAW,iBAAA;CH4qB9C;AG3qBmC;EAAW,iBAAA;CH8qB9C;AG7qBmC;EAAW,iBAAA;CHgrB9C;AG/qBmC;EAAW,iBAAA;CHkrB9C;AGjrBmC;EAAW,iBAAA;CHorB9C;AGnrBmC;EAAW,iBAAA;CHsrB9C;AGrrBmC;EAAW,iBAAA;CHwrB9C;AGvrBmC;EAAW,iBAAA;CH0rB9C;AGzrBmC;EAAW,iBAAA;CH4rB9C;AG3rBmC;EAAW,iBAAA;CH8rB9C;AG7rBmC;EAAW,iBAAA;CHgsB9C;AG/rBmC;EAAW,iBAAA;CHksB9C;AGjsBmC;EAAW,iBAAA;CHosB9C;AGnsBmC;EAAW,iBAAA;CHssB9C;AGrsBmC;EAAW,iBAAA;CHwsB9C;AGvsBmC;EAAW,iBAAA;CH0sB9C;AGzsBmC;EAAW,iBAAA;CH4sB9C;AG3sBmC;EAAW,iBAAA;CH8sB9C;AG7sBmC;EAAW,iBAAA;CHgtB9C;AG/sBmC;EAAW,iBAAA;CHktB9C;AGjtBmC;EAAW,iBAAA;CHotB9C;AGntBmC;EAAW,iBAAA;CHstB9C;AGrtBmC;EAAW,iBAAA;CHwtB9C;AGvtBmC;EAAW,iBAAA;CH0tB9C;AGztBmC;EAAW,iBAAA;CH4tB9C;AG3tBmC;EAAW,iBAAA;CH8tB9C;AG7tBmC;EAAW,iBAAA;CHguB9C;AG/tBmC;EAAW,iBAAA;CHkuB9C;AGjuBmC;EAAW,iBAAA;CHouB9C;AGnuBmC;EAAW,iBAAA;CHsuB9C;AGruBmC;EAAW,iBAAA;CHwuB9C;AGvuBmC;EAAW,iBAAA;CH0uB9C;AGzuBmC;EAAW,iBAAA;CH4uB9C;AG3uBmC;EAAW,iBAAA;CH8uB9C;AG7uBmC;EAAW,iBAAA;CHgvB9C;AIthCD;ECgEE,+BAAA;EACG,4BAAA;EACK,uBAAA;CLy9BT;AIxhCD;;EC6DE,+BAAA;EACG,4BAAA;EACK,uBAAA;CL+9BT;AIthCD;EACE,gBAAA;EACA,8CAAA;CJwhCD;AIrhCD;EACE,4DAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,0BAAA;CJuhCD;AInhCD;;;;EAIE,qBAAA;EACA,mBAAA;EACA,qBAAA;CJqhCD;AI/gCD;EACE,eAAA;EACA,sBAAA;CJihCD;AI/gCC;;EAEE,eAAA;EACA,2BAAA;CJihCH;AI9gCC;EErDA,qBAAA;EAEA,2CAAA;EACA,qBAAA;CNqkCD;AIxgCD;EACE,UAAA;CJ0gCD;AIpgCD;EACE,uBAAA;CJsgCD;AIlgCD;;;;;EGvEE,eAAA;EACA,gBAAA;EACA,aAAA;CPglCD;AItgCD;EACE,mBAAA;CJwgCD;AIlgCD;EACE,aAAA;EACA,wBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;EC6FA,yCAAA;EACK,oCAAA;EACG,iCAAA;EEvLR,sBAAA;EACA,gBAAA;EACA,aAAA;CPgmCD;AIlgCD;EACE,mBAAA;CJogCD;AI9/BD;EACE,iBAAA;EACA,oBAAA;EACA,UAAA;EACA,8BAAA;CJggCD;AIx/BD;EACE,mBAAA;EACA,WAAA;EACA,YAAA;EACA,aAAA;EACA,WAAA;EACA,iBAAA;EACA,uBAAA;EACA,UAAA;CJ0/BD;AIl/BC;;EAEE,iBAAA;EACA,YAAA;EACA,aAAA;EACA,UAAA;EACA,kBAAA;EACA,WAAA;CJo/BH;AIz+BD;EACE,gBAAA;CJ2+BD;AQloCD;;;;;;;;;;;;EAEE,qBAAA;EACA,iBAAA;EACA,iBAAA;EACA,eAAA;CR8oCD;AQnpCD;;;;;;;;;;;;;;;;;;;;;;;;EASI,oBAAA;EACA,eAAA;EACA,eAAA;CRoqCH;AQhqCD;;;;;;EAGE,iBAAA;EACA,oBAAA;CRqqCD;AQzqCD;;;;;;;;;;;;EAQI,eAAA;CR+qCH;AQ5qCD;;;;;;EAGE,iBAAA;EACA,oBAAA;CRirCD;AQrrCD;;;;;;;;;;;;EAQI,eAAA;CR2rCH;AQvrCD;;EAAU,gBAAA;CR2rCT;AQ1rCD;;EAAU,gBAAA;CR8rCT;AQ7rCD;;EAAU,gBAAA;CRisCT;AQhsCD;;EAAU,gBAAA;CRosCT;AQnsCD;;EAAU,gBAAA;CRusCT;AQtsCD;;EAAU,gBAAA;CR0sCT;AQpsCD;EACE,iBAAA;CRssCD;AQnsCD;EACE,oBAAA;EACA,gBAAA;EACA,iBAAA;EACA,iBAAA;CRqsCD;AQhsCD;EAAA;IAFI,gBAAA;GRssCD;CACF;AQ9rCD;;EAEE,eAAA;CRgsCD;AQ7rCD;;EAEE,0BAAA;EACA,cAAA;CR+rCD;AQ3rCD;EAAuB,iBAAA;CR8rCtB;AQ7rCD;EAAuB,kBAAA;CRgsCtB;AQ/rCD;EAAuB,mBAAA;CRksCtB;AQjsCD;EAAuB,oBAAA;CRosCtB;AQnsCD;EAAuB,oBAAA;CRssCtB;AQnsCD;EAAuB,0BAAA;CRssCtB;AQrsCD;EAAuB,0BAAA;CRwsCtB;AQvsCD;EAAuB,2BAAA;CR0sCtB;AQvsCD;EACE,eAAA;CRysCD;AQvsCD;ECrGE,eAAA;CT+yCD;AS9yCC;;EAEE,eAAA;CTgzCH;AQ3sCD;ECxGE,eAAA;CTszCD;ASrzCC;;EAEE,eAAA;CTuzCH;AQ/sCD;EC3GE,eAAA;CT6zCD;AS5zCC;;EAEE,eAAA;CT8zCH;AQntCD;EC9GE,eAAA;CTo0CD;ASn0CC;;EAEE,eAAA;CTq0CH;AQvtCD;ECjHE,eAAA;CT20CD;AS10CC;;EAEE,eAAA;CT40CH;AQvtCD;EAGE,YAAA;EE3HA,0BAAA;CVm1CD;AUl1CC;;EAEE,0BAAA;CVo1CH;AQztCD;EE9HE,0BAAA;CV01CD;AUz1CC;;EAEE,0BAAA;CV21CH;AQ7tCD;EEjIE,0BAAA;CVi2CD;AUh2CC;;EAEE,0BAAA;CVk2CH;AQjuCD;EEpIE,0BAAA;CVw2CD;AUv2CC;;EAEE,0BAAA;CVy2CH;AQruCD;EEvIE,0BAAA;CV+2CD;AU92CC;;EAEE,0BAAA;CVg3CH;AQpuCD;EACE,oBAAA;EACA,oBAAA;EACA,iCAAA;CRsuCD;AQ9tCD;;EAEE,cAAA;EACA,oBAAA;CRguCD;AQnuCD;;;;EAMI,iBAAA;CRmuCH;AQ5tCD;EACE,gBAAA;EACA,iBAAA;CR8tCD;AQ1tCD;EALE,gBAAA;EACA,iBAAA;EAMA,kBAAA;CR6tCD;AQ/tCD;EAKI,sBAAA;EACA,kBAAA;EACA,mBAAA;CR6tCH;AQxtCD;EACE,cAAA;EACA,oBAAA;CR0tCD;AQxtCD;;EAEE,wBAAA;CR0tCD;AQxtCD;EACE,kBAAA;CR0tCD;AQxtCD;EACE,eAAA;CR0tCD;AQjsCD;EAAA;IAVM,YAAA;IACA,aAAA;IACA,YAAA;IACA,kBAAA;IGtNJ,iBAAA;IACA,wBAAA;IACA,oBAAA;GXs6CC;EQ3sCH;IAHM,mBAAA;GRitCH;CACF;AQxsCD;;EAGE,aAAA;EACA,kCAAA;CRysCD;AQvsCD;EACE,eAAA;EA9IqB,0BAAA;CRw1CtB;AQrsCD;EACE,mBAAA;EACA,iBAAA;EACA,kBAAA;EACA,+BAAA;CRusCD;AQlsCG;;;EACE,iBAAA;CRssCL;AQhtCD;;;EAmBI,eAAA;EACA,eAAA;EACA,wBAAA;EACA,eAAA;CRksCH;AQhsCG;;;EACE,uBAAA;CRosCL;AQ5rCD;;EAEE,oBAAA;EACA,gBAAA;EACA,gCAAA;EACA,eAAA;EACA,kBAAA;CR8rCD;AQxrCG;;;;;;EAAW,YAAA;CRgsCd;AQ/rCG;;;;;;EACE,uBAAA;CRssCL;AQhsCD;EACE,oBAAA;EACA,mBAAA;EACA,wBAAA;CRksCD;AYx+CD;;;;EAIE,+DAAA;CZ0+CD;AYt+CD;EACE,iBAAA;EACA,eAAA;EACA,eAAA;EACA,0BAAA;EACA,mBAAA;CZw+CD;AYp+CD;EACE,iBAAA;EACA,eAAA;EACA,eAAA;EACA,0BAAA;EACA,mBAAA;EACA,uDAAA;UAAA,+CAAA;CZs+CD;AY5+CD;EASI,WAAA;EACA,gBAAA;EACA,kBAAA;EACA,yBAAA;UAAA,iBAAA;CZs+CH;AYj+CD;EACE,eAAA;EACA,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,wBAAA;EACA,sBAAA;EACA,sBAAA;EACA,eAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;CZm+CD;AY9+CD;EAeI,WAAA;EACA,mBAAA;EACA,eAAA;EACA,sBAAA;EACA,8BAAA;EACA,iBAAA;CZk+CH;AY79CD;EACE,kBAAA;EACA,mBAAA;CZ+9CD;AazhDD;ECHE,mBAAA;EACA,kBAAA;EACA,mBAAA;EACA,oBAAA;Cd+hDD;AazhDC;EAAA;IAFE,aAAA;Gb+hDD;CACF;Aa3hDC;EAAA;IAFE,aAAA;GbiiDD;CACF;Aa7hDD;EAAA;IAFI,cAAA;GbmiDD;CACF;Aa1hDD;ECvBE,mBAAA;EACA,kBAAA;EACA,mBAAA;EACA,oBAAA;CdojDD;AavhDD;ECvBE,mBAAA;EACA,oBAAA;CdijDD;AejjDG;EACE,mBAAA;EAEA,gBAAA;EAEA,mBAAA;EACA,oBAAA;CfijDL;AejiDG;EACE,YAAA;CfmiDL;Ae5hDC;EACE,YAAA;Cf8hDH;Ae/hDC;EACE,oBAAA;CfiiDH;AeliDC;EACE,oBAAA;CfoiDH;AeriDC;EACE,WAAA;CfuiDH;AexiDC;EACE,oBAAA;Cf0iDH;Ae3iDC;EACE,oBAAA;Cf6iDH;Ae9iDC;EACE,WAAA;CfgjDH;AejjDC;EACE,oBAAA;CfmjDH;AepjDC;EACE,oBAAA;CfsjDH;AevjDC;EACE,WAAA;CfyjDH;Ae1jDC;EACE,oBAAA;Cf4jDH;Ae7jDC;EACE,mBAAA;Cf+jDH;AejjDC;EACE,YAAA;CfmjDH;AepjDC;EACE,oBAAA;CfsjDH;AevjDC;EACE,oBAAA;CfyjDH;Ae1jDC;EACE,WAAA;Cf4jDH;Ae7jDC;EACE,oBAAA;Cf+jDH;AehkDC;EACE,oBAAA;CfkkDH;AenkDC;EACE,WAAA;CfqkDH;AetkDC;EACE,oBAAA;CfwkDH;AezkDC;EACE,oBAAA;Cf2kDH;Ae5kDC;EACE,WAAA;Cf8kDH;Ae/kDC;EACE,oBAAA;CfilDH;AellDC;EACE,mBAAA;CfolDH;AehlDC;EACE,YAAA;CfklDH;AelmDC;EACE,WAAA;CfomDH;AermDC;EACE,mBAAA;CfumDH;AexmDC;EACE,mBAAA;Cf0mDH;Ae3mDC;EACE,UAAA;Cf6mDH;Ae9mDC;EACE,mBAAA;CfgnDH;AejnDC;EACE,mBAAA;CfmnDH;AepnDC;EACE,UAAA;CfsnDH;AevnDC;EACE,mBAAA;CfynDH;Ae1nDC;EACE,mBAAA;Cf4nDH;Ae7nDC;EACE,UAAA;Cf+nDH;AehoDC;EACE,mBAAA;CfkoDH;AenoDC;EACE,kBAAA;CfqoDH;AejoDC;EACE,WAAA;CfmoDH;AernDC;EACE,kBAAA;CfunDH;AexnDC;EACE,0BAAA;Cf0nDH;Ae3nDC;EACE,0BAAA;Cf6nDH;Ae9nDC;EACE,iBAAA;CfgoDH;AejoDC;EACE,0BAAA;CfmoDH;AepoDC;EACE,0BAAA;CfsoDH;AevoDC;EACE,iBAAA;CfyoDH;Ae1oDC;EACE,0BAAA;Cf4oDH;Ae7oDC;EACE,0BAAA;Cf+oDH;AehpDC;EACE,iBAAA;CfkpDH;AenpDC;EACE,0BAAA;CfqpDH;AetpDC;EACE,yBAAA;CfwpDH;AezpDC;EACE,gBAAA;Cf2pDH;Aa3pDD;EElCI;IACE,YAAA;GfgsDH;EezrDD;IACE,YAAA;Gf2rDD;Ee5rDD;IACE,oBAAA;Gf8rDD;Ee/rDD;IACE,oBAAA;GfisDD;EelsDD;IACE,WAAA;GfosDD;EersDD;IACE,oBAAA;GfusDD;EexsDD;IACE,oBAAA;Gf0sDD;Ee3sDD;IACE,WAAA;Gf6sDD;Ee9sDD;IACE,oBAAA;GfgtDD;EejtDD;IACE,oBAAA;GfmtDD;EeptDD;IACE,WAAA;GfstDD;EevtDD;IACE,oBAAA;GfytDD;Ee1tDD;IACE,mBAAA;Gf4tDD;Ee9sDD;IACE,YAAA;GfgtDD;EejtDD;IACE,oBAAA;GfmtDD;EeptDD;IACE,oBAAA;GfstDD;EevtDD;IACE,WAAA;GfytDD;Ee1tDD;IACE,oBAAA;Gf4tDD;Ee7tDD;IACE,oBAAA;Gf+tDD;EehuDD;IACE,WAAA;GfkuDD;EenuDD;IACE,oBAAA;GfquDD;EetuDD;IACE,oBAAA;GfwuDD;EezuDD;IACE,WAAA;Gf2uDD;Ee5uDD;IACE,oBAAA;Gf8uDD;Ee/uDD;IACE,mBAAA;GfivDD;Ee7uDD;IACE,YAAA;Gf+uDD;Ee/vDD;IACE,WAAA;GfiwDD;EelwDD;IACE,mBAAA;GfowDD;EerwDD;IACE,mBAAA;GfuwDD;EexwDD;IACE,UAAA;Gf0wDD;Ee3wDD;IACE,mBAAA;Gf6wDD;Ee9wDD;IACE,mBAAA;GfgxDD;EejxDD;IACE,UAAA;GfmxDD;EepxDD;IACE,mBAAA;GfsxDD;EevxDD;IACE,mBAAA;GfyxDD;Ee1xDD;IACE,UAAA;Gf4xDD;Ee7xDD;IACE,mBAAA;Gf+xDD;EehyDD;IACE,kBAAA;GfkyDD;Ee9xDD;IACE,WAAA;GfgyDD;EelxDD;IACE,kBAAA;GfoxDD;EerxDD;IACE,0BAAA;GfuxDD;EexxDD;IACE,0BAAA;Gf0xDD;Ee3xDD;IACE,iBAAA;Gf6xDD;Ee9xDD;IACE,0BAAA;GfgyDD;EejyDD;IACE,0BAAA;GfmyDD;EepyDD;IACE,iBAAA;GfsyDD;EevyDD;IACE,0BAAA;GfyyDD;Ee1yDD;IACE,0BAAA;Gf4yDD;Ee7yDD;IACE,iBAAA;Gf+yDD;EehzDD;IACE,0BAAA;GfkzDD;EenzDD;IACE,yBAAA;GfqzDD;EetzDD;IACE,gBAAA;GfwzDD;CACF;AahzDD;EE3CI;IACE,YAAA;Gf81DH;Eev1DD;IACE,YAAA;Gfy1DD;Ee11DD;IACE,oBAAA;Gf41DD;Ee71DD;IACE,oBAAA;Gf+1DD;Eeh2DD;IACE,WAAA;Gfk2DD;Een2DD;IACE,oBAAA;Gfq2DD;Eet2DD;IACE,oBAAA;Gfw2DD;Eez2DD;IACE,WAAA;Gf22DD;Ee52DD;IACE,oBAAA;Gf82DD;Ee/2DD;IACE,oBAAA;Gfi3DD;Eel3DD;IACE,WAAA;Gfo3DD;Eer3DD;IACE,oBAAA;Gfu3DD;Eex3DD;IACE,mBAAA;Gf03DD;Ee52DD;IACE,YAAA;Gf82DD;Ee/2DD;IACE,oBAAA;Gfi3DD;Eel3DD;IACE,oBAAA;Gfo3DD;Eer3DD;IACE,WAAA;Gfu3DD;Eex3DD;IACE,oBAAA;Gf03DD;Ee33DD;IACE,oBAAA;Gf63DD;Ee93DD;IACE,WAAA;Gfg4DD;Eej4DD;IACE,oBAAA;Gfm4DD;Eep4DD;IACE,oBAAA;Gfs4DD;Eev4DD;IACE,WAAA;Gfy4DD;Ee14DD;IACE,oBAAA;Gf44DD;Ee74DD;IACE,mBAAA;Gf+4DD;Ee34DD;IACE,YAAA;Gf64DD;Ee75DD;IACE,WAAA;Gf+5DD;Eeh6DD;IACE,mBAAA;Gfk6DD;Een6DD;IACE,mBAAA;Gfq6DD;Eet6DD;IACE,UAAA;Gfw6DD;Eez6DD;IACE,mBAAA;Gf26DD;Ee56DD;IACE,mBAAA;Gf86DD;Ee/6DD;IACE,UAAA;Gfi7DD;Eel7DD;IACE,mBAAA;Gfo7DD;Eer7DD;IACE,mBAAA;Gfu7DD;Eex7DD;IACE,UAAA;Gf07DD;Ee37DD;IACE,mBAAA;Gf67DD;Ee97DD;IACE,kBAAA;Gfg8DD;Ee57DD;IACE,WAAA;Gf87DD;Eeh7DD;IACE,kBAAA;Gfk7DD;Een7DD;IACE,0BAAA;Gfq7DD;Eet7DD;IACE,0BAAA;Gfw7DD;Eez7DD;IACE,iBAAA;Gf27DD;Ee57DD;IACE,0BAAA;Gf87DD;Ee/7DD;IACE,0BAAA;Gfi8DD;Eel8DD;IACE,iBAAA;Gfo8DD;Eer8DD;IACE,0BAAA;Gfu8DD;Eex8DD;IACE,0BAAA;Gf08DD;Ee38DD;IACE,iBAAA;Gf68DD;Ee98DD;IACE,0BAAA;Gfg9DD;Eej9DD;IACE,yBAAA;Gfm9DD;Eep9DD;IACE,gBAAA;Gfs9DD;CACF;Aa38DD;EE9CI;IACE,YAAA;Gf4/DH;Eer/DD;IACE,YAAA;Gfu/DD;Eex/DD;IACE,oBAAA;Gf0/DD;Ee3/DD;IACE,oBAAA;Gf6/DD;Ee9/DD;IACE,WAAA;GfggED;EejgED;IACE,oBAAA;GfmgED;EepgED;IACE,oBAAA;GfsgED;EevgED;IACE,WAAA;GfygED;Ee1gED;IACE,oBAAA;Gf4gED;Ee7gED;IACE,oBAAA;Gf+gED;EehhED;IACE,WAAA;GfkhED;EenhED;IACE,oBAAA;GfqhED;EethED;IACE,mBAAA;GfwhED;Ee1gED;IACE,YAAA;Gf4gED;Ee7gED;IACE,oBAAA;Gf+gED;EehhED;IACE,oBAAA;GfkhED;EenhED;IACE,WAAA;GfqhED;EethED;IACE,oBAAA;GfwhED;EezhED;IACE,oBAAA;Gf2hED;Ee5hED;IACE,WAAA;Gf8hED;Ee/hED;IACE,oBAAA;GfiiED;EeliED;IACE,oBAAA;GfoiED;EeriED;IACE,WAAA;GfuiED;EexiED;IACE,oBAAA;Gf0iED;Ee3iED;IACE,mBAAA;Gf6iED;EeziED;IACE,YAAA;Gf2iED;Ee3jED;IACE,WAAA;Gf6jED;Ee9jED;IACE,mBAAA;GfgkED;EejkED;IACE,mBAAA;GfmkED;EepkED;IACE,UAAA;GfskED;EevkED;IACE,mBAAA;GfykED;Ee1kED;IACE,mBAAA;Gf4kED;Ee7kED;IACE,UAAA;Gf+kED;EehlED;IACE,mBAAA;GfklED;EenlED;IACE,mBAAA;GfqlED;EetlED;IACE,UAAA;GfwlED;EezlED;IACE,mBAAA;Gf2lED;Ee5lED;IACE,kBAAA;Gf8lED;Ee1lED;IACE,WAAA;Gf4lED;Ee9kED;IACE,kBAAA;GfglED;EejlED;IACE,0BAAA;GfmlED;EeplED;IACE,0BAAA;GfslED;EevlED;IACE,iBAAA;GfylED;Ee1lED;IACE,0BAAA;Gf4lED;Ee7lED;IACE,0BAAA;Gf+lED;EehmED;IACE,iBAAA;GfkmED;EenmED;IACE,0BAAA;GfqmED;EetmED;IACE,0BAAA;GfwmED;EezmED;IACE,iBAAA;Gf2mED;Ee5mED;IACE,0BAAA;Gf8mED;Ee/mED;IACE,yBAAA;GfinED;EelnED;IACE,gBAAA;GfonED;CACF;AgBxrED;EACE,8BAAA;ChB0rED;AgBxrED;EACE,iBAAA;EACA,oBAAA;EACA,eAAA;EACA,iBAAA;ChB0rED;AgBxrED;EACE,iBAAA;ChB0rED;AgBprED;EACE,YAAA;EACA,gBAAA;EACA,oBAAA;ChBsrED;AgBzrED;;;;;;EAWQ,aAAA;EACA,wBAAA;EACA,oBAAA;EACA,8BAAA;ChBsrEP;AgBpsED;EAoBI,uBAAA;EACA,iCAAA;ChBmrEH;AgBxsED;;;;;;EA8BQ,cAAA;ChBkrEP;AgBhtED;EAoCI,8BAAA;ChB+qEH;AgBntED;EAyCI,0BAAA;ChB6qEH;AgBtqED;;;;;;EAOQ,aAAA;ChBuqEP;AgB5pED;EACE,0BAAA;ChB8pED;AgB/pED;;;;;;EAQQ,0BAAA;ChB+pEP;AgBvqED;;EAeM,yBAAA;ChB4pEL;AgBlpED;EAEI,0BAAA;ChBmpEH;AgB1oED;EAEI,0BAAA;ChB2oEH;AgBloED;EACE,iBAAA;EACA,YAAA;EACA,sBAAA;ChBooED;AgB/nEG;;EACE,iBAAA;EACA,YAAA;EACA,oBAAA;ChBkoEL;AiB9wEC;;;;;;;;;;;;EAOI,0BAAA;CjBqxEL;AiB/wEC;;;;;EAMI,0BAAA;CjBgxEL;AiBnyEC;;;;;;;;;;;;EAOI,0BAAA;CjB0yEL;AiBpyEC;;;;;EAMI,0BAAA;CjBqyEL;AiBxzEC;;;;;;;;;;;;EAOI,0BAAA;CjB+zEL;AiBzzEC;;;;;EAMI,0BAAA;CjB0zEL;AiB70EC;;;;;;;;;;;;EAOI,0BAAA;CjBo1EL;AiB90EC;;;;;EAMI,0BAAA;CjB+0EL;AiBl2EC;;;;;;;;;;;;EAOI,0BAAA;CjBy2EL;AiBn2EC;;;;;EAMI,0BAAA;CjBo2EL;AgBltED;EACE,iBAAA;EACA,kBAAA;ChBotED;AgBvpED;EAAA;IA1DI,YAAA;IACA,oBAAA;IACA,mBAAA;IACA,6CAAA;IACA,0BAAA;GhBqtED;EgB/pEH;IAlDM,iBAAA;GhBotEH;EgBlqEH;;;;;;IAzCY,oBAAA;GhBmtET;EgB1qEH;IAjCM,UAAA;GhB8sEH;EgB7qEH;;;;;;IAxBY,eAAA;GhB6sET;EgBrrEH;;;;;;IApBY,gBAAA;GhBitET;EgB7rEH;;;;IAPY,iBAAA;GhB0sET;CACF;AkBp6ED;EACE,WAAA;EACA,UAAA;EACA,UAAA;EAIA,aAAA;ClBm6ED;AkBh6ED;EACE,eAAA;EACA,YAAA;EACA,WAAA;EACA,oBAAA;EACA,gBAAA;EACA,qBAAA;EACA,eAAA;EACA,UAAA;EACA,iCAAA;ClBk6ED;AkB/5ED;EACE,sBAAA;EACA,gBAAA;EACA,mBAAA;EACA,kBAAA;ClBi6ED;AkBt5ED;Eb4BE,+BAAA;EACG,4BAAA;EACK,uBAAA;CL63ET;AkBt5ED;;EAEE,gBAAA;EACA,mBAAA;EACA,oBAAA;ClBw5ED;AkBr5ED;EACE,eAAA;ClBu5ED;AkBn5ED;EACE,eAAA;EACA,YAAA;ClBq5ED;AkBj5ED;;EAEE,aAAA;ClBm5ED;AkB/4ED;;;EZvEE,qBAAA;EAEA,2CAAA;EACA,qBAAA;CN09ED;AkB/4ED;EACE,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;ClBi5ED;AkBv3ED;EACE,eAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,0BAAA;EACA,uBAAA;EACA,0BAAA;EACA,mBAAA;EbxDA,yDAAA;EACQ,iDAAA;EAyHR,uFAAA;EACK,0EAAA;EACG,uEAAA;CL0zET;AmBl8EC;EACE,sBAAA;EACA,WAAA;EdUF,uFAAA;EACQ,+EAAA;CL27ET;AK15EC;EACE,eAAA;EACA,WAAA;CL45EH;AK15EC;EAA0B,eAAA;CL65E3B;AK55EC;EAAgC,eAAA;CL+5EjC;AkB/3EC;;;EAGE,0BAAA;EACA,WAAA;ClBi4EH;AkB93EC;;EAEE,oBAAA;ClBg4EH;AkB53EC;EACE,aAAA;ClB83EH;AkBl3ED;EACE,yBAAA;ClBo3ED;AkB50ED;EAtBI;;;;IACE,kBAAA;GlBw2EH;EkBr2EC;;;;;;;;IAEE,kBAAA;GlB62EH;EkB12EC;;;;;;;;IAEE,kBAAA;GlBk3EH;CACF;AkBx2ED;EACE,oBAAA;ClB02ED;AkBl2ED;;EAEE,mBAAA;EACA,eAAA;EACA,iBAAA;EACA,oBAAA;ClBo2ED;AkBz2ED;;EAQI,iBAAA;EACA,mBAAA;EACA,iBAAA;EACA,oBAAA;EACA,gBAAA;ClBq2EH;AkBl2ED;;;;EAIE,mBAAA;EACA,mBAAA;EACA,mBAAA;ClBo2ED;AkBj2ED;;EAEE,iBAAA;ClBm2ED;AkB/1ED;;EAEE,mBAAA;EACA,sBAAA;EACA,mBAAA;EACA,iBAAA;EACA,uBAAA;EACA,oBAAA;EACA,gBAAA;ClBi2ED;AkB/1ED;;EAEE,cAAA;EACA,kBAAA;ClBi2ED;AkBx1EC;;;;;;EAGE,oBAAA;ClB61EH;AkBv1EC;;;;EAEE,oBAAA;ClB21EH;AkBr1EC;;;;EAGI,oBAAA;ClBw1EL;AkB70ED;EAEE,iBAAA;EACA,oBAAA;EAEA,iBAAA;EACA,iBAAA;ClB60ED;AkB30EC;;EAEE,gBAAA;EACA,iBAAA;ClB60EH;AkBh0ED;EC7PE,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CnBgkFD;AmB9jFC;EACE,aAAA;EACA,kBAAA;CnBgkFH;AmB7jFC;;EAEE,aAAA;CnB+jFH;AkB50ED;EAEI,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;ClB60EH;AkBn1ED;EASI,aAAA;EACA,kBAAA;ClB60EH;AkBv1ED;;EAcI,aAAA;ClB60EH;AkB31ED;EAiBI,aAAA;EACA,iBAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;ClB60EH;AkBz0ED;ECzRE,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CnBqmFD;AmBnmFC;EACE,aAAA;EACA,kBAAA;CnBqmFH;AmBlmFC;;EAEE,aAAA;CnBomFH;AkBr1ED;EAEI,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;ClBs1EH;AkB51ED;EASI,aAAA;EACA,kBAAA;ClBs1EH;AkBh2ED;;EAcI,aAAA;ClBs1EH;AkBp2ED;EAiBI,aAAA;EACA,iBAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;ClBs1EH;AkB70ED;EAEE,mBAAA;ClB80ED;AkBh1ED;EAMI,sBAAA;ClB60EH;AkBz0ED;EACE,mBAAA;EACA,OAAA;EACA,SAAA;EACA,WAAA;EACA,eAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,mBAAA;EACA,qBAAA;ClB20ED;AkBz0ED;;;EAGE,YAAA;EACA,aAAA;EACA,kBAAA;ClB20ED;AkBz0ED;;;EAGE,YAAA;EACA,aAAA;EACA,kBAAA;ClB20ED;AkBv0ED;;;;;;;;;;ECpZI,eAAA;CnBuuFH;AkBn1ED;EChZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLwrFT;AmBtuFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL6rFT;AkB71ED;ECtYI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBsuFH;AkBl2ED;EChYI,eAAA;CnBquFH;AkBl2ED;;;;;;;;;;ECvZI,eAAA;CnBqwFH;AkB92ED;ECnZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLstFT;AmBpwFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL2tFT;AkBx3ED;ECzYI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBowFH;AkB73ED;ECnYI,eAAA;CnBmwFH;AkB73ED;;;;;;;;;;EC1ZI,eAAA;CnBmyFH;AkBz4ED;ECtZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLovFT;AmBlyFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CLyvFT;AkBn5ED;EC5YI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBkyFH;AkBx5ED;ECtYI,eAAA;CnBiyFH;AkBp5EC;EACG,UAAA;ClBs5EJ;AkBp5EC;EACG,OAAA;ClBs5EJ;AkB54ED;EACE,eAAA;EACA,gBAAA;EACA,oBAAA;EACA,eAAA;ClB84ED;AkB3zED;EAAA;IA9DM,sBAAA;IACA,iBAAA;IACA,uBAAA;GlB63EH;EkBj0EH;IAvDM,sBAAA;IACA,YAAA;IACA,uBAAA;GlB23EH;EkBt0EH;IAhDM,sBAAA;GlBy3EH;EkBz0EH;IA5CM,sBAAA;IACA,uBAAA;GlBw3EH;EkB70EH;;;IAtCQ,YAAA;GlBw3EL;EkBl1EH;IAhCM,YAAA;GlBq3EH;EkBr1EH;IA5BM,iBAAA;IACA,uBAAA;GlBo3EH;EkBz1EH;;IApBM,sBAAA;IACA,cAAA;IACA,iBAAA;IACA,uBAAA;GlBi3EH;EkBh2EH;;IAdQ,gBAAA;GlBk3EL;EkBp2EH;;IATM,mBAAA;IACA,eAAA;GlBi3EH;EkBz2EH;IAHM,OAAA;GlB+2EH;CACF;AkBr2ED;;;;EASI,cAAA;EACA,iBAAA;EACA,iBAAA;ClBk2EH;AkB72ED;;EAiBI,iBAAA;ClBg2EH;AkBj3ED;EJhhBE,mBAAA;EACA,oBAAA;Cdo4FD;AkB90EC;EAAA;IAVI,kBAAA;IACA,iBAAA;IACA,iBAAA;GlB41EH;CACF;AkB53ED;EAwCI,YAAA;ClBu1EH;AkBz0EC;EAAA;IAJM,yBAAA;IACA,gBAAA;GlBi1EL;CACF;AkBv0EC;EAAA;IAJM,iBAAA;IACA,gBAAA;GlB+0EL;CACF;AoBl6FD;EACE,sBAAA;EACA,iBAAA;EACA,oBAAA;EACA,mBAAA;EACA,uBAAA;EACA,+BAAA;MAAA,2BAAA;EACA,gBAAA;EACA,uBAAA;EACA,8BAAA;EACA,oBAAA;EC6CA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,mBAAA;EhB4JA,0BAAA;EACG,uBAAA;EACC,sBAAA;EACI,kBAAA;CL6tFT;AoBr6FG;;;;;;EdrBF,qBAAA;EAEA,2CAAA;EACA,qBAAA;CNi8FD;AoBz6FC;;;EAGE,eAAA;EACA,sBAAA;CpB26FH;AoBx6FC;;EAEE,WAAA;EACA,uBAAA;Ef2BF,yDAAA;EACQ,iDAAA;CLg5FT;AoBx6FC;;;EAGE,oBAAA;EE7CF,cAAA;EAGA,0BAAA;EjB8DA,yBAAA;EACQ,iBAAA;CLy5FT;AoBx6FG;;EAEE,qBAAA;CpB06FL;AoBj6FD;EC3DE,eAAA;EACA,0BAAA;EACA,sBAAA;CrB+9FD;AqB79FC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB+9FP;AqB79FC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB+9FP;AqB79FC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB+9FP;AqB79FG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBq+FT;AqBl+FC;;;EAGE,uBAAA;CrBo+FH;AqB/9FG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrB6+FT;AoB/9FD;ECTI,eAAA;EACA,0BAAA;CrB2+FH;AoBh+FD;EC9DE,eAAA;EACA,0BAAA;EACA,sBAAA;CrBiiGD;AqB/hGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBiiGP;AqB/hGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBiiGP;AqB/hGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBiiGP;AqB/hGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBuiGT;AqBpiGC;;;EAGE,uBAAA;CrBsiGH;AqBjiGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrB+iGT;AoB9hGD;ECZI,eAAA;EACA,0BAAA;CrB6iGH;AoB9hGD;EClEE,eAAA;EACA,0BAAA;EACA,sBAAA;CrBmmGD;AqBjmGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBmmGP;AqBjmGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBmmGP;AqBjmGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBmmGP;AqBjmGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBymGT;AqBtmGC;;;EAGE,uBAAA;CrBwmGH;AqBnmGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrBinGT;AoB5lGD;EChBI,eAAA;EACA,0BAAA;CrB+mGH;AoB5lGD;ECtEE,eAAA;EACA,0BAAA;EACA,sBAAA;CrBqqGD;AqBnqGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBqqGP;AqBnqGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBqqGP;AqBnqGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBqqGP;AqBnqGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB2qGT;AqBxqGC;;;EAGE,uBAAA;CrB0qGH;AqBrqGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrBmrGT;AoB1pGD;ECpBI,eAAA;EACA,0BAAA;CrBirGH;AoB1pGD;EC1EE,eAAA;EACA,0BAAA;EACA,sBAAA;CrBuuGD;AqBruGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBuuGP;AqBruGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBuuGP;AqBruGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBuuGP;AqBruGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB6uGT;AqB1uGC;;;EAGE,uBAAA;CrB4uGH;AqBvuGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrBqvGT;AoBxtGD;ECxBI,eAAA;EACA,0BAAA;CrBmvGH;AoBxtGD;EC9EE,eAAA;EACA,0BAAA;EACA,sBAAA;CrByyGD;AqBvyGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrByyGP;AqBvyGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrByyGP;AqBvyGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrByyGP;AqBvyGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB+yGT;AqB5yGC;;;EAGE,uBAAA;CrB8yGH;AqBzyGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrBuzGT;AoBtxGD;EC5BI,eAAA;EACA,0BAAA;CrBqzGH;AoBjxGD;EACE,eAAA;EACA,oBAAA;EACA,iBAAA;CpBmxGD;AoBjxGC;;;;;EAKE,8BAAA;EfnCF,yBAAA;EACQ,iBAAA;CLuzGT;AoBlxGC;;;;EAIE,0BAAA;CpBoxGH;AoBlxGC;;EAEE,eAAA;EACA,2BAAA;EACA,8BAAA;CpBoxGH;AoBhxGG;;;;EAEE,eAAA;EACA,sBAAA;CpBoxGL;AoB3wGD;;ECrEE,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CrBo1GD;AoB9wGD;;ECzEE,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CrB21GD;AoBjxGD;;EC7EE,iBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CrBk2GD;AoBhxGD;EACE,eAAA;EACA,YAAA;CpBkxGD;AoB9wGD;EACE,gBAAA;CpBgxGD;AoBzwGC;;;EACE,YAAA;CpB6wGH;AuBv6GD;EACE,WAAA;ElBoLA,yCAAA;EACK,oCAAA;EACG,iCAAA;CLsvGT;AuB16GC;EACE,WAAA;CvB46GH;AuBx6GD;EACE,cAAA;CvB06GD;AuBx6GC;EAAY,eAAA;CvB26Gb;AuB16GC;EAAY,mBAAA;CvB66Gb;AuB56GC;EAAY,yBAAA;CvB+6Gb;AuB56GD;EACE,mBAAA;EACA,UAAA;EACA,iBAAA;ElBuKA,gDAAA;EACQ,2CAAA;KAAA,wCAAA;EAOR,mCAAA;EACQ,8BAAA;KAAA,2BAAA;EAGR,yCAAA;EACQ,oCAAA;KAAA,iCAAA;CLgwGT;AwB18GD;EACE,sBAAA;EACA,SAAA;EACA,UAAA;EACA,iBAAA;EACA,uBAAA;EACA,uBAAA;EACA,yBAAA;EACA,oCAAA;EACA,mCAAA;CxB48GD;AwBx8GD;;EAEE,mBAAA;CxB08GD;AwBt8GD;EACE,WAAA;CxBw8GD;AwBp8GD;EACE,mBAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,cAAA;EACA,YAAA;EACA,iBAAA;EACA,eAAA;EACA,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,iBAAA;EACA,0BAAA;EACA,0BAAA;EACA,sCAAA;EACA,mBAAA;EnBsBA,oDAAA;EACQ,4CAAA;EmBrBR,qCAAA;UAAA,6BAAA;CxBu8GD;AwBl8GC;EACE,SAAA;EACA,WAAA;CxBo8GH;AwB79GD;ECzBE,YAAA;EACA,cAAA;EACA,iBAAA;EACA,0BAAA;CzBy/GD;AwBn+GD;EAmCI,eAAA;EACA,kBAAA;EACA,YAAA;EACA,oBAAA;EACA,wBAAA;EACA,eAAA;EACA,oBAAA;CxBm8GH;AwB77GC;;EAEE,sBAAA;EACA,eAAA;EACA,0BAAA;CxB+7GH;AwBz7GC;;;EAGE,eAAA;EACA,sBAAA;EACA,WAAA;EACA,0BAAA;CxB27GH;AwBl7GC;;;EAGE,eAAA;CxBo7GH;AwBh7GC;;EAEE,sBAAA;EACA,8BAAA;EACA,uBAAA;EE3GF,oEAAA;EF6GE,oBAAA;CxBk7GH;AwB76GD;EAGI,eAAA;CxB66GH;AwBh7GD;EAQI,WAAA;CxB26GH;AwBn6GD;EACE,WAAA;EACA,SAAA;CxBq6GD;AwB75GD;EACE,QAAA;EACA,YAAA;CxB+5GD;AwB35GD;EACE,eAAA;EACA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,oBAAA;CxB65GD;AwBz5GD;EACE,gBAAA;EACA,QAAA;EACA,SAAA;EACA,UAAA;EACA,OAAA;EACA,aAAA;CxB25GD;AwBv5GD;EACE,SAAA;EACA,WAAA;CxBy5GD;AwBj5GD;;EAII,cAAA;EACA,0BAAA;EACA,4BAAA;EACA,YAAA;CxBi5GH;AwBx5GD;;EAWI,UAAA;EACA,aAAA;EACA,mBAAA;CxBi5GH;AwB53GD;EAXE;IApEA,WAAA;IACA,SAAA;GxB+8GC;EwB54GD;IA1DA,QAAA;IACA,YAAA;GxBy8GC;CACF;A2BzlHD;;EAEE,mBAAA;EACA,sBAAA;EACA,uBAAA;C3B2lHD;A2B/lHD;;EAMI,mBAAA;EACA,YAAA;C3B6lHH;A2B3lHG;;;;;;;;EAIE,WAAA;C3BimHL;A2B3lHD;;;;EAKI,kBAAA;C3B4lHH;A2BvlHD;EACE,kBAAA;C3BylHD;A2B1lHD;;;EAOI,YAAA;C3BwlHH;A2B/lHD;;;EAYI,iBAAA;C3BwlHH;A2BplHD;EACE,iBAAA;C3BslHD;A2BllHD;EACE,eAAA;C3BolHD;A2BnlHC;EClDA,8BAAA;EACG,2BAAA;C5BwoHJ;A2BllHD;;EC/CE,6BAAA;EACG,0BAAA;C5BqoHJ;A2BjlHD;EACE,YAAA;C3BmlHD;A2BjlHD;EACE,iBAAA;C3BmlHD;A2BjlHD;;ECnEE,8BAAA;EACG,2BAAA;C5BwpHJ;A2BhlHD;ECjEE,6BAAA;EACG,0BAAA;C5BopHJ;A2B/kHD;;EAEE,WAAA;C3BilHD;A2BhkHD;EACE,kBAAA;EACA,mBAAA;C3BkkHD;A2BhkHD;EACE,mBAAA;EACA,oBAAA;C3BkkHD;A2B7jHD;EtB/CE,yDAAA;EACQ,iDAAA;CL+mHT;A2B7jHC;EtBnDA,yBAAA;EACQ,iBAAA;CLmnHT;A2B1jHD;EACE,eAAA;C3B4jHD;A2BzjHD;EACE,wBAAA;EACA,uBAAA;C3B2jHD;A2BxjHD;EACE,wBAAA;C3B0jHD;A2BnjHD;;;EAII,eAAA;EACA,YAAA;EACA,YAAA;EACA,gBAAA;C3BojHH;A2B3jHD;EAcM,YAAA;C3BgjHL;A2B9jHD;;;;EAsBI,iBAAA;EACA,eAAA;C3B8iHH;A2BziHC;EACE,iBAAA;C3B2iHH;A2BziHC;EACE,6BAAA;ECpKF,8BAAA;EACC,6BAAA;C5BgtHF;A2B1iHC;EACE,+BAAA;EChLF,2BAAA;EACC,0BAAA;C5B6tHF;A2B1iHD;EACE,iBAAA;C3B4iHD;A2B1iHD;;EC/KE,8BAAA;EACC,6BAAA;C5B6tHF;A2BziHD;EC7LE,2BAAA;EACC,0BAAA;C5ByuHF;A2BriHD;EACE,eAAA;EACA,YAAA;EACA,oBAAA;EACA,0BAAA;C3BuiHD;A2B3iHD;;EAOI,YAAA;EACA,oBAAA;EACA,UAAA;C3BwiHH;A2BjjHD;EAYI,YAAA;C3BwiHH;A2BpjHD;EAgBI,WAAA;C3BuiHH;A2BthHD;;;;EAKM,mBAAA;EACA,uBAAA;EACA,qBAAA;C3BuhHL;A6BjwHD;EACE,mBAAA;EACA,eAAA;EACA,0BAAA;C7BmwHD;A6BhwHC;EACE,YAAA;EACA,gBAAA;EACA,iBAAA;C7BkwHH;A6B3wHD;EAeI,mBAAA;EACA,WAAA;EAKA,YAAA;EAEA,YAAA;EACA,iBAAA;C7B0vHH;A6BjvHD;;;EV8BE,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CnBwtHD;AmBttHC;;;EACE,aAAA;EACA,kBAAA;CnB0tHH;AmBvtHC;;;;;;EAEE,aAAA;CnB6tHH;A6BnwHD;;;EVyBE,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CnB+uHD;AmB7uHC;;;EACE,aAAA;EACA,kBAAA;CnBivHH;AmB9uHC;;;;;;EAEE,aAAA;CnBovHH;A6BjxHD;;;EAGE,oBAAA;C7BmxHD;A6BjxHC;;;EACE,iBAAA;C7BqxHH;A6BjxHD;;EAEE,UAAA;EACA,oBAAA;EACA,uBAAA;C7BmxHD;A6B9wHD;EACE,kBAAA;EACA,gBAAA;EACA,oBAAA;EACA,eAAA;EACA,eAAA;EACA,mBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;C7BgxHD;A6B7wHC;EACE,kBAAA;EACA,gBAAA;EACA,mBAAA;C7B+wHH;A6B7wHC;EACE,mBAAA;EACA,gBAAA;EACA,mBAAA;C7B+wHH;A6BnyHD;;EA0BI,cAAA;C7B6wHH;A6BxwHD;;;;;;;EDhGE,8BAAA;EACG,2BAAA;C5Bi3HJ;A6BzwHD;EACE,gBAAA;C7B2wHD;A6BzwHD;;;;;;;EDpGE,6BAAA;EACG,0BAAA;C5Bs3HJ;A6B1wHD;EACE,eAAA;C7B4wHD;A6BvwHD;EACE,mBAAA;EAGA,aAAA;EACA,oBAAA;C7BuwHD;A6B5wHD;EAUI,mBAAA;C7BqwHH;A6B/wHD;EAYM,kBAAA;C7BswHL;A6BnwHG;;;EAGE,WAAA;C7BqwHL;A6BhwHC;;EAGI,mBAAA;C7BiwHL;A6B9vHC;;EAGI,WAAA;EACA,kBAAA;C7B+vHL;A8B15HD;EACE,iBAAA;EACA,gBAAA;EACA,iBAAA;C9B45HD;A8B/5HD;EAOI,mBAAA;EACA,eAAA;C9B25HH;A8Bn6HD;EAWM,mBAAA;EACA,eAAA;EACA,mBAAA;C9B25HL;A8B15HK;;EAEE,sBAAA;EACA,0BAAA;C9B45HP;A8Bv5HG;EACE,eAAA;C9By5HL;A8Bv5HK;;EAEE,eAAA;EACA,sBAAA;EACA,8BAAA;EACA,oBAAA;C9By5HP;A8Bl5HG;;;EAGE,0BAAA;EACA,sBAAA;C9Bo5HL;A8B77HD;ELHE,YAAA;EACA,cAAA;EACA,iBAAA;EACA,0BAAA;CzBm8HD;A8Bn8HD;EA0DI,gBAAA;C9B44HH;A8Bn4HD;EACE,iCAAA;C9Bq4HD;A8Bt4HD;EAGI,YAAA;EAEA,oBAAA;C9Bq4HH;A8B14HD;EASM,kBAAA;EACA,wBAAA;EACA,8BAAA;EACA,2BAAA;C9Bo4HL;A8Bn4HK;EACE,sCAAA;C9Bq4HP;A8B/3HK;;;EAGE,eAAA;EACA,0BAAA;EACA,0BAAA;EACA,iCAAA;EACA,gBAAA;C9Bi4HP;A8B53HC;EAqDA,YAAA;EA8BA,iBAAA;C9B6yHD;A8Bh4HC;EAwDE,YAAA;C9B20HH;A8Bn4HC;EA0DI,mBAAA;EACA,mBAAA;C9B40HL;A8Bv4HC;EAgEE,UAAA;EACA,WAAA;C9B00HH;A8B9zHD;EAAA;IAPM,oBAAA;IACA,UAAA;G9By0HH;E8Bn0HH;IAJQ,iBAAA;G9B00HL;CACF;A8Bp5HC;EAuFE,gBAAA;EACA,mBAAA;C9Bg0HH;A8Bx5HC;;;EA8FE,0BAAA;C9B+zHH;A8BjzHD;EAAA;IATM,iCAAA;IACA,2BAAA;G9B8zHH;E8BtzHH;;;IAHM,6BAAA;G9B8zHH;CACF;A8B/5HD;EAEI,YAAA;C9Bg6HH;A8Bl6HD;EAMM,mBAAA;C9B+5HL;A8Br6HD;EASM,iBAAA;C9B+5HL;A8B15HK;;;EAGE,eAAA;EACA,0BAAA;C9B45HP;A8Bp5HD;EAEI,YAAA;C9Bq5HH;A8Bv5HD;EAIM,gBAAA;EACA,eAAA;C9Bs5HL;A8B14HD;EACE,YAAA;C9B44HD;A8B74HD;EAII,YAAA;C9B44HH;A8Bh5HD;EAMM,mBAAA;EACA,mBAAA;C9B64HL;A8Bp5HD;EAYI,UAAA;EACA,WAAA;C9B24HH;A8B/3HD;EAAA;IAPM,oBAAA;IACA,UAAA;G9B04HH;E8Bp4HH;IAJQ,iBAAA;G9B24HL;CACF;A8Bn4HD;EACE,iBAAA;C9Bq4HD;A8Bt4HD;EAKI,gBAAA;EACA,mBAAA;C9Bo4HH;A8B14HD;;;EAYI,0BAAA;C9Bm4HH;A8Br3HD;EAAA;IATM,iCAAA;IACA,2BAAA;G9Bk4HH;E8B13HH;;;IAHM,6BAAA;G9Bk4HH;CACF;A8Bz3HD;EAEI,cAAA;C9B03HH;A8B53HD;EAKI,eAAA;C9B03HH;A8Bj3HD;EAEE,iBAAA;EF3OA,2BAAA;EACC,0BAAA;C5B8lIF;A+BxlID;EACE,mBAAA;EACA,iBAAA;EACA,oBAAA;EACA,8BAAA;C/B0lID;A+BllID;EAAA;IAFI,mBAAA;G/BwlID;CACF;A+BzkID;EAAA;IAFI,YAAA;G/B+kID;CACF;A+BjkID;EACE,oBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kCAAA;EACA,2DAAA;UAAA,mDAAA;EAEA,kCAAA;C/BkkID;A+BhkIC;EACE,iBAAA;C/BkkIH;A+BtiID;EAAA;IAxBI,YAAA;IACA,cAAA;IACA,yBAAA;YAAA,iBAAA;G/BkkID;E+BhkIC;IACE,0BAAA;IACA,wBAAA;IACA,kBAAA;IACA,6BAAA;G/BkkIH;E+B/jIC;IACE,oBAAA;G/BikIH;E+B5jIC;;;IAGE,gBAAA;IACA,iBAAA;G/B8jIH;CACF;A+B1jID;;EAGI,kBAAA;C/B2jIH;A+BtjIC;EAAA;;IAFI,kBAAA;G/B6jIH;CACF;A+BpjID;;;;EAII,oBAAA;EACA,mBAAA;C/BsjIH;A+BhjIC;EAAA;;;;IAHI,gBAAA;IACA,eAAA;G/B0jIH;CACF;A+B9iID;EACE,cAAA;EACA,sBAAA;C/BgjID;A+B3iID;EAAA;IAFI,iBAAA;G/BijID;CACF;A+B7iID;;EAEE,gBAAA;EACA,SAAA;EACA,QAAA;EACA,cAAA;C/B+iID;A+BziID;EAAA;;IAFI,iBAAA;G/BgjID;CACF;A+B9iID;EACE,OAAA;EACA,sBAAA;C/BgjID;A+B9iID;EACE,UAAA;EACA,iBAAA;EACA,sBAAA;C/BgjID;A+B1iID;EACE,YAAA;EACA,mBAAA;EACA,gBAAA;EACA,kBAAA;EACA,aAAA;C/B4iID;A+B1iIC;;EAEE,sBAAA;C/B4iIH;A+BrjID;EAaI,eAAA;C/B2iIH;A+BliID;EALI;;IAEE,mBAAA;G/B0iIH;CACF;A+BhiID;EACE,mBAAA;EACA,aAAA;EACA,mBAAA;EACA,kBAAA;EC9LA,gBAAA;EACA,mBAAA;ED+LA,8BAAA;EACA,uBAAA;EACA,8BAAA;EACA,mBAAA;C/BmiID;A+B/hIC;EACE,WAAA;C/BiiIH;A+B/iID;EAmBI,eAAA;EACA,YAAA;EACA,YAAA;EACA,mBAAA;C/B+hIH;A+BrjID;EAyBI,gBAAA;C/B+hIH;A+BzhID;EAAA;IAFI,cAAA;G/B+hID;CACF;A+BthID;EACE,oBAAA;C/BwhID;A+BzhID;EAII,kBAAA;EACA,qBAAA;EACA,kBAAA;C/BwhIH;A+B5/HC;EAAA;IAtBI,iBAAA;IACA,YAAA;IACA,YAAA;IACA,cAAA;IACA,8BAAA;IACA,UAAA;IACA,yBAAA;YAAA,iBAAA;G/BshIH;E+BtgID;;IAbM,2BAAA;G/BuhIL;E+B1gID;IAVM,kBAAA;G/BuhIL;E+BthIK;;IAEE,uBAAA;G/BwhIP;CACF;A+BtgID;EAAA;IAXI,YAAA;IACA,UAAA;G/BqhID;E+B3gIH;IAPM,YAAA;G/BqhIH;E+B9gIH;IALQ,kBAAA;IACA,qBAAA;G/BshIL;CACF;A+B3gID;EACE,mBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kCAAA;EACA,qCAAA;E1B9NA,6FAAA;EACQ,qFAAA;E2B/DR,gBAAA;EACA,mBAAA;ChC4yID;AkB5xHD;EAAA;IA9DM,sBAAA;IACA,iBAAA;IACA,uBAAA;GlB81HH;EkBlyHH;IAvDM,sBAAA;IACA,YAAA;IACA,uBAAA;GlB41HH;EkBvyHH;IAhDM,sBAAA;GlB01HH;EkB1yHH;IA5CM,sBAAA;IACA,uBAAA;GlBy1HH;EkB9yHH;;;IAtCQ,YAAA;GlBy1HL;EkBnzHH;IAhCM,YAAA;GlBs1HH;EkBtzHH;IA5BM,iBAAA;IACA,uBAAA;GlBq1HH;EkB1zHH;;IApBM,sBAAA;IACA,cAAA;IACA,iBAAA;IACA,uBAAA;GlBk1HH;EkBj0HH;;IAdQ,gBAAA;GlBm1HL;EkBr0HH;;IATM,mBAAA;IACA,eAAA;GlBk1HH;EkB10HH;IAHM,OAAA;GlBg1HH;CACF;A+BpjIC;EAAA;IANI,mBAAA;G/B8jIH;E+B5jIG;IACE,iBAAA;G/B8jIL;CACF;A+B7iID;EAAA;IARI,YAAA;IACA,UAAA;IACA,eAAA;IACA,gBAAA;IACA,eAAA;IACA,kBAAA;I1BzPF,yBAAA;IACQ,iBAAA;GLmzIP;CACF;A+BnjID;EACE,cAAA;EHpUA,2BAAA;EACC,0BAAA;C5B03IF;A+BnjID;EACE,iBAAA;EHzUA,6BAAA;EACC,4BAAA;EAOD,8BAAA;EACC,6BAAA;C5By3IF;A+B/iID;EChVE,gBAAA;EACA,mBAAA;ChCk4ID;A+BhjIC;ECnVA,iBAAA;EACA,oBAAA;ChCs4ID;A+BjjIC;ECtVA,iBAAA;EACA,oBAAA;ChC04ID;A+B3iID;EChWE,iBAAA;EACA,oBAAA;ChC84ID;A+BviID;EAAA;IAJI,YAAA;IACA,kBAAA;IACA,mBAAA;G/B+iID;CACF;A+BlhID;EAhBE;IExWA,uBAAA;GjC84IC;E+BriID;IE5WA,wBAAA;IF8WE,oBAAA;G/BuiID;E+BziID;IAKI,gBAAA;G/BuiIH;CACF;A+B9hID;EACE,0BAAA;EACA,sBAAA;C/BgiID;A+BliID;EAKI,eAAA;C/BgiIH;A+B/hIG;;EAEE,eAAA;EACA,8BAAA;C/BiiIL;A+B1iID;EAcI,eAAA;C/B+hIH;A+B7iID;EAmBM,eAAA;C/B6hIL;A+B3hIK;;EAEE,eAAA;EACA,8BAAA;C/B6hIP;A+BzhIK;;;EAGE,eAAA;EACA,0BAAA;C/B2hIP;A+BvhIK;;;EAGE,eAAA;EACA,8BAAA;C/ByhIP;A+BjkID;EA8CI,sBAAA;C/BshIH;A+BrhIG;;EAEE,0BAAA;C/BuhIL;A+BxkID;EAoDM,0BAAA;C/BuhIL;A+B3kID;;EA0DI,sBAAA;C/BqhIH;A+B9gIK;;;EAGE,0BAAA;EACA,eAAA;C/BghIP;A+B/+HC;EAAA;IAzBQ,eAAA;G/B4gIP;E+B3gIO;;IAEE,eAAA;IACA,8BAAA;G/B6gIT;E+BzgIO;;;IAGE,eAAA;IACA,0BAAA;G/B2gIT;E+BvgIO;;;IAGE,eAAA;IACA,8BAAA;G/BygIT;CACF;A+B3mID;EA8GI,eAAA;C/BggIH;A+B//HG;EACE,eAAA;C/BigIL;A+BjnID;EAqHI,eAAA;C/B+/HH;A+B9/HG;;EAEE,eAAA;C/BggIL;A+B5/HK;;;;EAEE,eAAA;C/BggIP;A+Bx/HD;EACE,0BAAA;EACA,sBAAA;C/B0/HD;A+B5/HD;EAKI,eAAA;C/B0/HH;A+Bz/HG;;EAEE,eAAA;EACA,8BAAA;C/B2/HL;A+BpgID;EAcI,eAAA;C/By/HH;A+BvgID;EAmBM,eAAA;C/Bu/HL;A+Br/HK;;EAEE,eAAA;EACA,8BAAA;C/Bu/HP;A+Bn/HK;;;EAGE,eAAA;EACA,0BAAA;C/Bq/HP;A+Bj/HK;;;EAGE,eAAA;EACA,8BAAA;C/Bm/HP;A+B3hID;EA+CI,sBAAA;C/B++HH;A+B9+HG;;EAEE,0BAAA;C/Bg/HL;A+BliID;EAqDM,0BAAA;C/Bg/HL;A+BriID;;EA2DI,sBAAA;C/B8+HH;A+Bx+HK;;;EAGE,0BAAA;EACA,eAAA;C/B0+HP;A+Bn8HC;EAAA;IA/BQ,sBAAA;G/Bs+HP;E+Bv8HD;IA5BQ,0BAAA;G/Bs+HP;E+B18HD;IAzBQ,eAAA;G/Bs+HP;E+Br+HO;;IAEE,eAAA;IACA,8BAAA;G/Bu+HT;E+Bn+HO;;;IAGE,eAAA;IACA,0BAAA;G/Bq+HT;E+Bj+HO;;;IAGE,eAAA;IACA,8BAAA;G/Bm+HT;CACF;A+B3kID;EA+GI,eAAA;C/B+9HH;A+B99HG;EACE,eAAA;C/Bg+HL;A+BjlID;EAsHI,eAAA;C/B89HH;A+B79HG;;EAEE,eAAA;C/B+9HL;A+B39HK;;;;EAEE,eAAA;C/B+9HP;AkCzmJD;EACE,kBAAA;EACA,oBAAA;EACA,iBAAA;EACA,0BAAA;EACA,mBAAA;ClC2mJD;AkChnJD;EAQI,sBAAA;ClC2mJH;AkCnnJD;EAWM,kBAAA;EACA,eAAA;EACA,eAAA;ClC2mJL;AkCxnJD;EAkBI,eAAA;ClCymJH;AmC7nJD;EACE,sBAAA;EACA,gBAAA;EACA,eAAA;EACA,mBAAA;CnC+nJD;AmCnoJD;EAOI,gBAAA;CnC+nJH;AmCtoJD;;EAUM,mBAAA;EACA,YAAA;EACA,kBAAA;EACA,wBAAA;EACA,sBAAA;EACA,eAAA;EACA,0BAAA;EACA,0BAAA;EACA,kBAAA;CnCgoJL;AmC9nJG;;EAGI,eAAA;EPXN,+BAAA;EACG,4BAAA;C5B2oJJ;AmC7nJG;;EPvBF,gCAAA;EACG,6BAAA;C5BwpJJ;AmCxnJG;;;;EAEE,WAAA;EACA,eAAA;EACA,0BAAA;EACA,sBAAA;CnC4nJL;AmCtnJG;;;;;;EAGE,WAAA;EACA,eAAA;EACA,0BAAA;EACA,sBAAA;EACA,gBAAA;CnC2nJL;AmClrJD;;;;;;EAkEM,eAAA;EACA,0BAAA;EACA,sBAAA;EACA,oBAAA;CnCwnJL;AmC/mJD;;EC3EM,mBAAA;EACA,gBAAA;EACA,uBAAA;CpC8rJL;AoC5rJG;;ERKF,+BAAA;EACG,4BAAA;C5B2rJJ;AoC3rJG;;ERTF,gCAAA;EACG,6BAAA;C5BwsJJ;AmC1nJD;;EChFM,kBAAA;EACA,gBAAA;EACA,iBAAA;CpC8sJL;AoC5sJG;;ERKF,+BAAA;EACG,4BAAA;C5B2sJJ;AoC3sJG;;ERTF,gCAAA;EACG,6BAAA;C5BwtJJ;AqC3tJD;EACE,gBAAA;EACA,eAAA;EACA,iBAAA;EACA,mBAAA;CrC6tJD;AqCjuJD;EAOI,gBAAA;CrC6tJH;AqCpuJD;;EAUM,sBAAA;EACA,kBAAA;EACA,0BAAA;EACA,0BAAA;EACA,oBAAA;CrC8tJL;AqC5uJD;;EAmBM,sBAAA;EACA,0BAAA;CrC6tJL;AqCjvJD;;EA2BM,aAAA;CrC0tJL;AqCrvJD;;EAkCM,YAAA;CrCutJL;AqCzvJD;;;;EA2CM,eAAA;EACA,0BAAA;EACA,oBAAA;CrCotJL;AsClwJD;EACE,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,kBAAA;EACA,eAAA;EACA,eAAA;EACA,mBAAA;EACA,oBAAA;EACA,yBAAA;EACA,qBAAA;CtCowJD;AsChwJG;;EAEE,eAAA;EACA,sBAAA;EACA,gBAAA;CtCkwJL;AsC7vJC;EACE,cAAA;CtC+vJH;AsC3vJC;EACE,mBAAA;EACA,UAAA;CtC6vJH;AsCtvJD;ECtCE,0BAAA;CvC+xJD;AuC5xJG;;EAEE,0BAAA;CvC8xJL;AsCzvJD;EC1CE,0BAAA;CvCsyJD;AuCnyJG;;EAEE,0BAAA;CvCqyJL;AsC5vJD;EC9CE,0BAAA;CvC6yJD;AuC1yJG;;EAEE,0BAAA;CvC4yJL;AsC/vJD;EClDE,0BAAA;CvCozJD;AuCjzJG;;EAEE,0BAAA;CvCmzJL;AsClwJD;ECtDE,0BAAA;CvC2zJD;AuCxzJG;;EAEE,0BAAA;CvC0zJL;AsCrwJD;EC1DE,0BAAA;CvCk0JD;AuC/zJG;;EAEE,0BAAA;CvCi0JL;AwCn0JD;EACE,sBAAA;EACA,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,kBAAA;EACA,eAAA;EACA,eAAA;EACA,uBAAA;EACA,oBAAA;EACA,mBAAA;EACA,0BAAA;EACA,oBAAA;CxCq0JD;AwCl0JC;EACE,cAAA;CxCo0JH;AwCh0JC;EACE,mBAAA;EACA,UAAA;CxCk0JH;AwC/zJC;;EAEE,OAAA;EACA,iBAAA;CxCi0JH;AwC5zJG;;EAEE,eAAA;EACA,sBAAA;EACA,gBAAA;CxC8zJL;AwCzzJC;;EAEE,eAAA;EACA,0BAAA;CxC2zJH;AwCxzJC;EACE,aAAA;CxC0zJH;AwCvzJC;EACE,kBAAA;CxCyzJH;AwCtzJC;EACE,iBAAA;CxCwzJH;AyCl3JD;EACE,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,eAAA;EACA,0BAAA;CzCo3JD;AyCz3JD;;EASI,eAAA;CzCo3JH;AyC73JD;EAaI,oBAAA;EACA,gBAAA;EACA,iBAAA;CzCm3JH;AyCl4JD;EAmBI,0BAAA;CzCk3JH;AyC/2JC;;EAEE,mBAAA;CzCi3JH;AyCz4JD;EA4BI,gBAAA;CzCg3JH;AyC91JD;EAAA;IAdI,kBAAA;IACA,qBAAA;GzCg3JD;EyC92JC;;IAEE,mBAAA;IACA,oBAAA;GzCg3JH;EyCx2JH;;IAHM,gBAAA;GzC+2JH;CACF;A0C15JD;EACE,eAAA;EACA,aAAA;EACA,oBAAA;EACA,wBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;ErCiLA,4CAAA;EACK,uCAAA;EACG,oCAAA;CL4uJT;A0Ct6JD;;EAaI,kBAAA;EACA,mBAAA;C1C65JH;A0Cz5JC;;;EAGE,sBAAA;C1C25JH;A0Ch7JD;EA0BI,aAAA;EACA,eAAA;C1Cy5JH;A2Cl7JD;EACE,cAAA;EACA,oBAAA;EACA,8BAAA;EACA,mBAAA;C3Co7JD;A2Cx7JD;EAQI,cAAA;EAEA,eAAA;C3Ck7JH;A2C57JD;EAeI,kBAAA;C3Cg7JH;A2C/7JD;;EAqBI,iBAAA;C3C86JH;A2Cn8JD;EAyBI,gBAAA;C3C66JH;A2Cr6JD;;EAEE,oBAAA;C3Cu6JD;A2Cz6JD;;EAMI,mBAAA;EACA,UAAA;EACA,aAAA;EACA,eAAA;C3Cu6JH;A2C/5JD;ECvDE,0BAAA;EACA,sBAAA;EACA,eAAA;C5Cy9JD;A2Cp6JD;EClDI,0BAAA;C5Cy9JH;A2Cv6JD;EC/CI,eAAA;C5Cy9JH;A2Ct6JD;EC3DE,0BAAA;EACA,sBAAA;EACA,eAAA;C5Co+JD;A2C36JD;ECtDI,0BAAA;C5Co+JH;A2C96JD;ECnDI,eAAA;C5Co+JH;A2C76JD;EC/DE,0BAAA;EACA,sBAAA;EACA,eAAA;C5C++JD;A2Cl7JD;EC1DI,0BAAA;C5C++JH;A2Cr7JD;ECvDI,eAAA;C5C++JH;A2Cp7JD;ECnEE,0BAAA;EACA,sBAAA;EACA,eAAA;C5C0/JD;A2Cz7JD;EC9DI,0BAAA;C5C0/JH;A2C57JD;EC3DI,eAAA;C5C0/JH;A6C5/JD;EACE;IAAQ,4BAAA;G7C+/JP;E6C9/JD;IAAQ,yBAAA;G7CigKP;CACF;A6C9/JD;EACE;IAAQ,4BAAA;G7CigKP;E6ChgKD;IAAQ,yBAAA;G7CmgKP;CACF;A6CtgKD;EACE;IAAQ,4BAAA;G7CigKP;E6ChgKD;IAAQ,yBAAA;G7CmgKP;CACF;A6C5/JD;EACE,iBAAA;EACA,aAAA;EACA,oBAAA;EACA,0BAAA;EACA,mBAAA;ExCsCA,uDAAA;EACQ,+CAAA;CLy9JT;A6C3/JD;EACE,YAAA;EACA,UAAA;EACA,aAAA;EACA,gBAAA;EACA,kBAAA;EACA,eAAA;EACA,mBAAA;EACA,0BAAA;ExCyBA,uDAAA;EACQ,+CAAA;EAyHR,oCAAA;EACK,+BAAA;EACG,4BAAA;CL62JT;A6Cx/JD;;ECCI,8MAAA;EACA,yMAAA;EACA,sMAAA;EDAF,mCAAA;UAAA,2BAAA;C7C4/JD;A6Cr/JD;;ExC5CE,2DAAA;EACK,sDAAA;EACG,mDAAA;CLqiKT;A6Cl/JD;EErEE,0BAAA;C/C0jKD;A+CvjKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9C0gKH;A6Ct/JD;EEzEE,0BAAA;C/CkkKD;A+C/jKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9CkhKH;A6C1/JD;EE7EE,0BAAA;C/C0kKD;A+CvkKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9C0hKH;A6C9/JD;EEjFE,0BAAA;C/CklKD;A+C/kKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9CkiKH;AgD1lKD;EAEE,iBAAA;ChD2lKD;AgDzlKC;EACE,cAAA;ChD2lKH;AgDvlKD;;EAEE,QAAA;EACA,iBAAA;ChDylKD;AgDtlKD;EACE,eAAA;ChDwlKD;AgDrlKD;EACE,eAAA;ChDulKD;AgDplKC;EACE,gBAAA;ChDslKH;AgDllKD;;EAEE,mBAAA;ChDolKD;AgDjlKD;;EAEE,oBAAA;ChDmlKD;AgDhlKD;;;EAGE,oBAAA;EACA,oBAAA;ChDklKD;AgD/kKD;EACE,uBAAA;ChDilKD;AgD9kKD;EACE,uBAAA;ChDglKD;AgD5kKD;EACE,cAAA;EACA,mBAAA;ChD8kKD;AgDxkKD;EACE,gBAAA;EACA,iBAAA;ChD0kKD;AiDjoKD;EAEE,oBAAA;EACA,gBAAA;CjDkoKD;AiD1nKD;EACE,mBAAA;EACA,eAAA;EACA,mBAAA;EAEA,oBAAA;EACA,0BAAA;EACA,0BAAA;CjD2nKD;AiDxnKC;ErB3BA,6BAAA;EACC,4BAAA;C5BspKF;AiDznKC;EACE,iBAAA;ErBvBF,gCAAA;EACC,+BAAA;C5BmpKF;AiDlnKD;;EAEE,eAAA;CjDonKD;AiDtnKD;;EAKI,eAAA;CjDqnKH;AiDjnKC;;;;EAEE,sBAAA;EACA,eAAA;EACA,0BAAA;CjDqnKH;AiDjnKD;EACE,YAAA;EACA,iBAAA;CjDmnKD;AiD9mKC;;;EAGE,0BAAA;EACA,eAAA;EACA,oBAAA;CjDgnKH;AiDrnKC;;;EASI,eAAA;CjDinKL;AiD1nKC;;;EAYI,eAAA;CjDmnKL;AiD9mKC;;;EAGE,WAAA;EACA,eAAA;EACA,0BAAA;EACA,sBAAA;CjDgnKH;AiDtnKC;;;;;;;;;EAYI,eAAA;CjDqnKL;AiDjoKC;;;EAeI,eAAA;CjDunKL;AkDztKC;EACE,eAAA;EACA,0BAAA;ClD2tKH;AkDztKG;;EAEE,eAAA;ClD2tKL;AkD7tKG;;EAKI,eAAA;ClD4tKP;AkDztKK;;;;EAEE,eAAA;EACA,0BAAA;ClD6tKP;AkD3tKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClDguKP;AkDtvKC;EACE,eAAA;EACA,0BAAA;ClDwvKH;AkDtvKG;;EAEE,eAAA;ClDwvKL;AkD1vKG;;EAKI,eAAA;ClDyvKP;AkDtvKK;;;;EAEE,eAAA;EACA,0BAAA;ClD0vKP;AkDxvKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClD6vKP;AkDnxKC;EACE,eAAA;EACA,0BAAA;ClDqxKH;AkDnxKG;;EAEE,eAAA;ClDqxKL;AkDvxKG;;EAKI,eAAA;ClDsxKP;AkDnxKK;;;;EAEE,eAAA;EACA,0BAAA;ClDuxKP;AkDrxKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClD0xKP;AkDhzKC;EACE,eAAA;EACA,0BAAA;ClDkzKH;AkDhzKG;;EAEE,eAAA;ClDkzKL;AkDpzKG;;EAKI,eAAA;ClDmzKP;AkDhzKK;;;;EAEE,eAAA;EACA,0BAAA;ClDozKP;AkDlzKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClDuzKP;AiDttKD;EACE,cAAA;EACA,mBAAA;CjDwtKD;AiDttKD;EACE,iBAAA;EACA,iBAAA;CjDwtKD;AmDl1KD;EACE,oBAAA;EACA,0BAAA;EACA,8BAAA;EACA,mBAAA;E9C0DA,kDAAA;EACQ,0CAAA;CL2xKT;AmDj1KD;EACE,cAAA;CnDm1KD;AmD90KD;EACE,mBAAA;EACA,qCAAA;EvBpBA,6BAAA;EACC,4BAAA;C5Bq2KF;AmDp1KD;EAMI,eAAA;CnDi1KH;AmD50KD;EACE,cAAA;EACA,iBAAA;EACA,gBAAA;EACA,eAAA;CnD80KD;AmDl1KD;;;;;EAWI,eAAA;CnD80KH;AmDz0KD;EACE,mBAAA;EACA,0BAAA;EACA,8BAAA;EvBxCA,gCAAA;EACC,+BAAA;C5Bo3KF;AmDn0KD;;EAGI,iBAAA;CnDo0KH;AmDv0KD;;EAMM,oBAAA;EACA,iBAAA;CnDq0KL;AmDj0KG;;EAEI,cAAA;EvBvEN,6BAAA;EACC,4BAAA;C5B24KF;AmD/zKG;;EAEI,iBAAA;EvBvEN,gCAAA;EACC,+BAAA;C5By4KF;AmDx1KD;EvB1DE,2BAAA;EACC,0BAAA;C5Bq5KF;AmD3zKD;EAEI,oBAAA;CnD4zKH;AmDzzKD;EACE,oBAAA;CnD2zKD;AmDnzKD;;;EAII,iBAAA;CnDozKH;AmDxzKD;;;EAOM,mBAAA;EACA,oBAAA;CnDszKL;AmD9zKD;;EvBzGE,6BAAA;EACC,4BAAA;C5B26KF;AmDn0KD;;;;EAmBQ,4BAAA;EACA,6BAAA;CnDszKP;AmD10KD;;;;;;;;EAwBU,4BAAA;CnD4zKT;AmDp1KD;;;;;;;;EA4BU,6BAAA;CnDk0KT;AmD91KD;;EvBjGE,gCAAA;EACC,+BAAA;C5Bm8KF;AmDn2KD;;;;EAyCQ,+BAAA;EACA,gCAAA;CnDg0KP;AmD12KD;;;;;;;;EA8CU,+BAAA;CnDs0KT;AmDp3KD;;;;;;;;EAkDU,gCAAA;CnD40KT;AmD93KD;;;;EA2DI,8BAAA;CnDy0KH;AmDp4KD;;EA+DI,cAAA;CnDy0KH;AmDx4KD;;EAmEI,UAAA;CnDy0KH;AmD54KD;;;;;;;;;;;;EA0EU,eAAA;CnDg1KT;AmD15KD;;;;;;;;;;;;EA8EU,gBAAA;CnD01KT;AmDx6KD;;;;;;;;EAuFU,iBAAA;CnD21KT;AmDl7KD;;;;;;;;EAgGU,iBAAA;CnD41KT;AmD57KD;EAsGI,UAAA;EACA,iBAAA;CnDy1KH;AmD/0KD;EACE,oBAAA;CnDi1KD;AmDl1KD;EAKI,iBAAA;EACA,mBAAA;CnDg1KH;AmDt1KD;EASM,gBAAA;CnDg1KL;AmDz1KD;EAcI,iBAAA;CnD80KH;AmD51KD;;EAkBM,8BAAA;CnD80KL;AmDh2KD;EAuBI,cAAA;CnD40KH;AmDn2KD;EAyBM,iCAAA;CnD60KL;AmDt0KD;EC1PE,sBAAA;CpDmkLD;AoDjkLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDmkLH;AoDtkLC;EAMI,0BAAA;CpDmkLL;AoDzkLC;EASI,eAAA;EACA,0BAAA;CpDmkLL;AoDhkLC;EAEI,6BAAA;CpDikLL;AmDr1KD;EC7PE,sBAAA;CpDqlLD;AoDnlLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDqlLH;AoDxlLC;EAMI,0BAAA;CpDqlLL;AoD3lLC;EASI,eAAA;EACA,0BAAA;CpDqlLL;AoDllLC;EAEI,6BAAA;CpDmlLL;AmDp2KD;EChQE,sBAAA;CpDumLD;AoDrmLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDumLH;AoD1mLC;EAMI,0BAAA;CpDumLL;AoD7mLC;EASI,eAAA;EACA,0BAAA;CpDumLL;AoDpmLC;EAEI,6BAAA;CpDqmLL;AmDn3KD;ECnQE,sBAAA;CpDynLD;AoDvnLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDynLH;AoD5nLC;EAMI,0BAAA;CpDynLL;AoD/nLC;EASI,eAAA;EACA,0BAAA;CpDynLL;AoDtnLC;EAEI,6BAAA;CpDunLL;AmDl4KD;ECtQE,sBAAA;CpD2oLD;AoDzoLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD2oLH;AoD9oLC;EAMI,0BAAA;CpD2oLL;AoDjpLC;EASI,eAAA;EACA,0BAAA;CpD2oLL;AoDxoLC;EAEI,6BAAA;CpDyoLL;AmDj5KD;ECzQE,sBAAA;CpD6pLD;AoD3pLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD6pLH;AoDhqLC;EAMI,0BAAA;CpD6pLL;AoDnqLC;EASI,eAAA;EACA,0BAAA;CpD6pLL;AoD1pLC;EAEI,6BAAA;CpD2pLL;AqD3qLD;EACE,mBAAA;EACA,eAAA;EACA,UAAA;EACA,WAAA;EACA,iBAAA;CrD6qLD;AqDlrLD;;;;;EAYI,mBAAA;EACA,OAAA;EACA,QAAA;EACA,UAAA;EACA,aAAA;EACA,YAAA;EACA,UAAA;CrD6qLH;AqDxqLD;EACE,uBAAA;CrD0qLD;AqDtqLD;EACE,oBAAA;CrDwqLD;AsDnsLD;EACE,iBAAA;EACA,cAAA;EACA,oBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;EjDwDA,wDAAA;EACQ,gDAAA;CL8oLT;AsD7sLD;EASI,mBAAA;EACA,kCAAA;CtDusLH;AsDlsLD;EACE,cAAA;EACA,mBAAA;CtDosLD;AsDlsLD;EACE,aAAA;EACA,mBAAA;CtDosLD;AuD1tLD;EACE,aAAA;EACA,gBAAA;EACA,kBAAA;EACA,eAAA;EACA,eAAA;EACA,6BAAA;EjCRA,aAAA;EAGA,0BAAA;CtBmuLD;AuD3tLC;;EAEE,eAAA;EACA,sBAAA;EACA,gBAAA;EjCfF,aAAA;EAGA,0BAAA;CtB2uLD;AuDvtLC;EACE,WAAA;EACA,gBAAA;EACA,wBAAA;EACA,UAAA;EACA,yBAAA;CvDytLH;AwD9uLD;EACE,iBAAA;CxDgvLD;AwD5uLD;EACE,cAAA;EACA,iBAAA;EACA,gBAAA;EACA,OAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,kCAAA;EAIA,WAAA;CxD2uLD;AwDxuLC;EnD+GA,sCAAA;EACI,kCAAA;EACC,iCAAA;EACG,8BAAA;EAkER,oDAAA;EAEK,0CAAA;EACG,oCAAA;CL2jLT;AwD9uLC;EnD2GA,mCAAA;EACI,+BAAA;EACC,8BAAA;EACG,2BAAA;CLsoLT;AwDlvLD;EACE,mBAAA;EACA,iBAAA;CxDovLD;AwDhvLD;EACE,mBAAA;EACA,YAAA;EACA,aAAA;CxDkvLD;AwD9uLD;EACE,mBAAA;EACA,0BAAA;EACA,0BAAA;EACA,qCAAA;EACA,mBAAA;EnDaA,iDAAA;EACQ,yCAAA;EmDZR,qCAAA;UAAA,6BAAA;EAEA,WAAA;CxDgvLD;AwD5uLD;EACE,gBAAA;EACA,OAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,0BAAA;CxD8uLD;AwD5uLC;ElCrEA,WAAA;EAGA,yBAAA;CtBkzLD;AwD/uLC;ElCtEA,aAAA;EAGA,0BAAA;CtBszLD;AwD9uLD;EACE,cAAA;EACA,iCAAA;EACA,0BAAA;CxDgvLD;AwD7uLD;EACE,iBAAA;CxD+uLD;AwD3uLD;EACE,UAAA;EACA,wBAAA;CxD6uLD;AwDxuLD;EACE,mBAAA;EACA,cAAA;CxD0uLD;AwDtuLD;EACE,cAAA;EACA,kBAAA;EACA,8BAAA;CxDwuLD;AwD3uLD;EAQI,iBAAA;EACA,iBAAA;CxDsuLH;AwD/uLD;EAaI,kBAAA;CxDquLH;AwDlvLD;EAiBI,eAAA;CxDouLH;AwD/tLD;EACE,mBAAA;EACA,aAAA;EACA,YAAA;EACA,aAAA;EACA,iBAAA;CxDiuLD;AwD/sLD;EAZE;IACE,aAAA;IACA,kBAAA;GxD8tLD;EwD5tLD;InDvEA,kDAAA;IACQ,0CAAA;GLsyLP;EwD3tLD;IAAY,aAAA;GxD8tLX;CACF;AwDztLD;EAFE;IAAY,aAAA;GxD+tLX;CACF;AyD92LD;EACE,mBAAA;EACA,cAAA;EACA,eAAA;ECRA,4DAAA;EAEA,mBAAA;EACA,oBAAA;EACA,uBAAA;EACA,iBAAA;EACA,wBAAA;EACA,iBAAA;EACA,kBAAA;EACA,sBAAA;EACA,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mBAAA;EACA,qBAAA;EACA,kBAAA;EDHA,gBAAA;EnCVA,WAAA;EAGA,yBAAA;CtBq4LD;AyD13LC;EnCdA,aAAA;EAGA,0BAAA;CtBy4LD;AyD73LC;EAAW,iBAAA;EAAmB,eAAA;CzDi4L/B;AyDh4LC;EAAW,iBAAA;EAAmB,eAAA;CzDo4L/B;AyDn4LC;EAAW,gBAAA;EAAmB,eAAA;CzDu4L/B;AyDt4LC;EAAW,kBAAA;EAAmB,eAAA;CzD04L/B;AyDt4LD;EACE,iBAAA;EACA,iBAAA;EACA,eAAA;EACA,mBAAA;EACA,0BAAA;EACA,mBAAA;CzDw4LD;AyDp4LD;EACE,mBAAA;EACA,SAAA;EACA,UAAA;EACA,0BAAA;EACA,oBAAA;CzDs4LD;AyDl4LC;EACE,UAAA;EACA,UAAA;EACA,kBAAA;EACA,wBAAA;EACA,0BAAA;CzDo4LH;AyDl4LC;EACE,UAAA;EACA,WAAA;EACA,oBAAA;EACA,wBAAA;EACA,0BAAA;CzDo4LH;AyDl4LC;EACE,UAAA;EACA,UAAA;EACA,oBAAA;EACA,wBAAA;EACA,0BAAA;CzDo4LH;AyDl4LC;EACE,SAAA;EACA,QAAA;EACA,iBAAA;EACA,4BAAA;EACA,4BAAA;CzDo4LH;AyDl4LC;EACE,SAAA;EACA,SAAA;EACA,iBAAA;EACA,4BAAA;EACA,2BAAA;CzDo4LH;AyDl4LC;EACE,OAAA;EACA,UAAA;EACA,kBAAA;EACA,wBAAA;EACA,6BAAA;CzDo4LH;AyDl4LC;EACE,OAAA;EACA,WAAA;EACA,iBAAA;EACA,wBAAA;EACA,6BAAA;CzDo4LH;AyDl4LC;EACE,OAAA;EACA,UAAA;EACA,iBAAA;EACA,wBAAA;EACA,6BAAA;CzDo4LH;A2Dj+LD;EACE,mBAAA;EACA,OAAA;EACA,QAAA;EACA,cAAA;EACA,cAAA;EACA,iBAAA;EACA,aAAA;EDXA,4DAAA;EAEA,mBAAA;EACA,oBAAA;EACA,uBAAA;EACA,iBAAA;EACA,wBAAA;EACA,iBAAA;EACA,kBAAA;EACA,sBAAA;EACA,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mBAAA;EACA,qBAAA;EACA,kBAAA;ECAA,gBAAA;EAEA,0BAAA;EACA,qCAAA;UAAA,6BAAA;EACA,0BAAA;EACA,qCAAA;EACA,mBAAA;EtD8CA,kDAAA;EACQ,0CAAA;CLi8LT;A2D5+LC;EAAY,kBAAA;C3D++Lb;A2D9+LC;EAAY,kBAAA;C3Di/Lb;A2Dh/LC;EAAY,iBAAA;C3Dm/Lb;A2Dl/LC;EAAY,mBAAA;C3Dq/Lb;A2Dl/LD;EACE,UAAA;EACA,kBAAA;EACA,gBAAA;EACA,0BAAA;EACA,iCAAA;EACA,2BAAA;C3Do/LD;A2Dj/LD;EACE,kBAAA;C3Dm/LD;A2D3+LC;;EAEE,mBAAA;EACA,eAAA;EACA,SAAA;EACA,UAAA;EACA,0BAAA;EACA,oBAAA;C3D6+LH;A2D1+LD;EACE,mBAAA;C3D4+LD;A2D1+LD;EACE,mBAAA;EACA,YAAA;C3D4+LD;A2Dx+LC;EACE,UAAA;EACA,mBAAA;EACA,uBAAA;EACA,0BAAA;EACA,sCAAA;EACA,cAAA;C3D0+LH;A2Dz+LG;EACE,aAAA;EACA,YAAA;EACA,mBAAA;EACA,uBAAA;EACA,0BAAA;C3D2+LL;A2Dx+LC;EACE,SAAA;EACA,YAAA;EACA,kBAAA;EACA,qBAAA;EACA,4BAAA;EACA,wCAAA;C3D0+LH;A2Dz+LG;EACE,aAAA;EACA,UAAA;EACA,cAAA;EACA,qBAAA;EACA,4BAAA;C3D2+LL;A2Dx+LC;EACE,UAAA;EACA,mBAAA;EACA,oBAAA;EACA,6BAAA;EACA,yCAAA;EACA,WAAA;C3D0+LH;A2Dz+LG;EACE,aAAA;EACA,SAAA;EACA,mBAAA;EACA,oBAAA;EACA,6BAAA;C3D2+LL;A2Dv+LC;EACE,SAAA;EACA,aAAA;EACA,kBAAA;EACA,sBAAA;EACA,2BAAA;EACA,uCAAA;C3Dy+LH;A2Dx+LG;EACE,aAAA;EACA,WAAA;EACA,sBAAA;EACA,2BAAA;EACA,cAAA;C3D0+LL;A4DnmMD;EACE,mBAAA;C5DqmMD;A4DlmMD;EACE,mBAAA;EACA,iBAAA;EACA,YAAA;C5DomMD;A4DvmMD;EAMI,cAAA;EACA,mBAAA;EvD6KF,0CAAA;EACK,qCAAA;EACG,kCAAA;CLw7LT;A4D9mMD;;EAcM,eAAA;C5DomML;A4D1kMC;EAAA;IvDiKA,uDAAA;IAEK,6CAAA;IACG,uCAAA;IA7JR,oCAAA;IAEQ,4BAAA;IA+GR,4BAAA;IAEQ,oBAAA;GL69LP;E4DxmMG;;IvDmHJ,2CAAA;IACQ,mCAAA;IuDjHF,QAAA;G5D2mML;E4DzmMG;;IvD8GJ,4CAAA;IACQ,oCAAA;IuD5GF,QAAA;G5D4mML;E4D1mMG;;;IvDyGJ,wCAAA;IACQ,gCAAA;IuDtGF,QAAA;G5D6mML;CACF;A4DnpMD;;;EA6CI,eAAA;C5D2mMH;A4DxpMD;EAiDI,QAAA;C5D0mMH;A4D3pMD;;EAsDI,mBAAA;EACA,OAAA;EACA,YAAA;C5DymMH;A4DjqMD;EA4DI,WAAA;C5DwmMH;A4DpqMD;EA+DI,YAAA;C5DwmMH;A4DvqMD;;EAmEI,QAAA;C5DwmMH;A4D3qMD;EAuEI,YAAA;C5DumMH;A4D9qMD;EA0EI,WAAA;C5DumMH;A4D/lMD;EACE,mBAAA;EACA,OAAA;EACA,QAAA;EACA,UAAA;EACA,WAAA;EtC9FA,aAAA;EAGA,0BAAA;EsC6FA,gBAAA;EACA,eAAA;EACA,mBAAA;EACA,0CAAA;C5DkmMD;A4D7lMC;EdlGE,mGAAA;EACA,8FAAA;EACA,qHAAA;EAAA,+FAAA;EACA,4BAAA;EACA,uHAAA;C9CksMH;A4DjmMC;EACE,WAAA;EACA,SAAA;EdvGA,mGAAA;EACA,8FAAA;EACA,qHAAA;EAAA,+FAAA;EACA,4BAAA;EACA,uHAAA;C9C2sMH;A4DnmMC;;EAEE,WAAA;EACA,eAAA;EACA,sBAAA;EtCtHF,aAAA;EAGA,0BAAA;CtB0tMD;A4DpoMD;;;;EAsCI,mBAAA;EACA,SAAA;EACA,kBAAA;EACA,WAAA;EACA,sBAAA;C5DomMH;A4D9oMD;;EA8CI,UAAA;EACA,mBAAA;C5DomMH;A4DnpMD;;EAmDI,WAAA;EACA,oBAAA;C5DomMH;A4DxpMD;;EAwDI,YAAA;EACA,aAAA;EACA,eAAA;EACA,mBAAA;C5DomMH;A4D/lMG;EACE,iBAAA;C5DimML;A4D7lMG;EACE,iBAAA;C5D+lML;A4DrlMD;EACE,mBAAA;EACA,aAAA;EACA,UAAA;EACA,YAAA;EACA,WAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;C5DulMD;A4DhmMD;EAYI,sBAAA;EACA,YAAA;EACA,aAAA;EACA,YAAA;EACA,oBAAA;EACA,0BAAA;EACA,oBAAA;EACA,gBAAA;EAWA,0BAAA;EACA,mCAAA;C5D6kMH;A4D5mMD;EAkCI,UAAA;EACA,YAAA;EACA,aAAA;EACA,0BAAA;C5D6kMH;A4DtkMD;EACE,mBAAA;EACA,UAAA;EACA,WAAA;EACA,aAAA;EACA,YAAA;EACA,kBAAA;EACA,qBAAA;EACA,eAAA;EACA,mBAAA;EACA,0CAAA;C5DwkMD;A4DvkMC;EACE,kBAAA;C5DykMH;A4DhiMD;EAhCE;;;;IAKI,YAAA;IACA,aAAA;IACA,kBAAA;IACA,gBAAA;G5DkkMH;E4D1kMD;;IAYI,mBAAA;G5DkkMH;E4D9kMD;;IAgBI,oBAAA;G5DkkMH;E4D7jMD;IACE,UAAA;IACA,WAAA;IACA,qBAAA;G5D+jMD;E4D3jMD;IACE,aAAA;G5D6jMD;CACF;A6D3zMC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAEE,aAAA;EACA,eAAA;C7Dy1MH;A6Dv1MC;;;;;;;;;;;;;;;EACE,YAAA;C7Du2MH;AiC/2MD;E6BRE,eAAA;EACA,kBAAA;EACA,mBAAA;C9D03MD;AiCj3MD;EACE,wBAAA;CjCm3MD;AiCj3MD;EACE,uBAAA;CjCm3MD;AiC32MD;EACE,yBAAA;CjC62MD;AiC32MD;EACE,0BAAA;CjC62MD;AiC32MD;EACE,mBAAA;CjC62MD;AiC32MD;E8BzBE,YAAA;EACA,mBAAA;EACA,kBAAA;EACA,8BAAA;EACA,UAAA;C/Du4MD;AiCz2MD;EACE,yBAAA;CjC22MD;AiCp2MD;EACE,gBAAA;CjCs2MD;AgEv4MD;EACE,oBAAA;ChEy4MD;AgEn4MD;;;;ECdE,yBAAA;CjEu5MD;AgEl4MD;;;;;;;;;;;;EAYE,yBAAA;ChEo4MD;AgE73MD;EAAA;IChDE,0BAAA;GjEi7MC;EiEh7MD;IAAU,0BAAA;GjEm7MT;EiEl7MD;IAAU,8BAAA;GjEq7MT;EiEp7MD;;IACU,+BAAA;GjEu7MT;CACF;AgEv4MD;EAAA;IAFI,0BAAA;GhE64MD;CACF;AgEv4MD;EAAA;IAFI,2BAAA;GhE64MD;CACF;AgEv4MD;EAAA;IAFI,iCAAA;GhE64MD;CACF;AgEt4MD;EAAA;ICrEE,0BAAA;GjE+8MC;EiE98MD;IAAU,0BAAA;GjEi9MT;EiEh9MD;IAAU,8BAAA;GjEm9MT;EiEl9MD;;IACU,+BAAA;GjEq9MT;CACF;AgEh5MD;EAAA;IAFI,0BAAA;GhEs5MD;CACF;AgEh5MD;EAAA;IAFI,2BAAA;GhEs5MD;CACF;AgEh5MD;EAAA;IAFI,iCAAA;GhEs5MD;CACF;AgE/4MD;EAAA;IC1FE,0BAAA;GjE6+MC;EiE5+MD;IAAU,0BAAA;GjE++MT;EiE9+MD;IAAU,8BAAA;GjEi/MT;EiEh/MD;;IACU,+BAAA;GjEm/MT;CACF;AgEz5MD;EAAA;IAFI,0BAAA;GhE+5MD;CACF;AgEz5MD;EAAA;IAFI,2BAAA;GhE+5MD;CACF;AgEz5MD;EAAA;IAFI,iCAAA;GhE+5MD;CACF;AgEx5MD;EAAA;IC/GE,0BAAA;GjE2gNC;EiE1gND;IAAU,0BAAA;GjE6gNT;EiE5gND;IAAU,8BAAA;GjE+gNT;EiE9gND;;IACU,+BAAA;GjEihNT;CACF;AgEl6MD;EAAA;IAFI,0BAAA;GhEw6MD;CACF;AgEl6MD;EAAA;IAFI,2BAAA;GhEw6MD;CACF;AgEl6MD;EAAA;IAFI,iCAAA;GhEw6MD;CACF;AgEj6MD;EAAA;IC5HE,yBAAA;GjEiiNC;CACF;AgEj6MD;EAAA;ICjIE,yBAAA;GjEsiNC;CACF;AgEj6MD;EAAA;ICtIE,yBAAA;GjE2iNC;CACF;AgEj6MD;EAAA;IC3IE,yBAAA;GjEgjNC;CACF;AgE95MD;ECnJE,yBAAA;CjEojND;AgE35MD;EAAA;ICjKE,0BAAA;GjEgkNC;EiE/jND;IAAU,0BAAA;GjEkkNT;EiEjkND;IAAU,8BAAA;GjEokNT;EiEnkND;;IACU,+BAAA;GjEskNT;CACF;AgEz6MD;EACE,yBAAA;ChE26MD;AgEt6MD;EAAA;IAFI,0BAAA;GhE46MD;CACF;AgE16MD;EACE,yBAAA;ChE46MD;AgEv6MD;EAAA;IAFI,2BAAA;GhE66MD;CACF;AgE36MD;EACE,yBAAA;ChE66MD;AgEx6MD;EAAA;IAFI,iCAAA;GhE86MD;CACF;AgEv6MD;EAAA;ICpLE,yBAAA;GjE+lNC;CACF","file":"bootstrap.css","sourcesContent":["/*!\n * Bootstrap v3.3.5 (http://getbootstrap.com)\n * Copyright 2011-2015 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\nhtml {\n font-family: sans-serif;\n -ms-text-size-adjust: 100%;\n -webkit-text-size-adjust: 100%;\n}\nbody {\n margin: 0;\n}\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block;\n vertical-align: baseline;\n}\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n[hidden],\ntemplate {\n display: none;\n}\na {\n background-color: transparent;\n}\na:active,\na:hover {\n outline: 0;\n}\nabbr[title] {\n border-bottom: 1px dotted;\n}\nb,\nstrong {\n font-weight: bold;\n}\ndfn {\n font-style: italic;\n}\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\nmark {\n background: #ff0;\n color: #000;\n}\nsmall {\n font-size: 80%;\n}\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\nsup {\n top: -0.5em;\n}\nsub {\n bottom: -0.25em;\n}\nimg {\n border: 0;\n}\nsvg:not(:root) {\n overflow: hidden;\n}\nfigure {\n margin: 1em 40px;\n}\nhr {\n box-sizing: content-box;\n height: 0;\n}\npre {\n overflow: auto;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit;\n font: inherit;\n margin: 0;\n}\nbutton {\n overflow: visible;\n}\nbutton,\nselect {\n text-transform: none;\n}\nbutton,\nhtml input[type=\"button\"],\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button;\n cursor: pointer;\n}\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\ninput {\n line-height: normal;\n}\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box;\n padding: 0;\n}\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\ninput[type=\"search\"] {\n -webkit-appearance: textfield;\n box-sizing: content-box;\n}\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\nlegend {\n border: 0;\n padding: 0;\n}\ntextarea {\n overflow: auto;\n}\noptgroup {\n font-weight: bold;\n}\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\ntd,\nth {\n padding: 0;\n}\n/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n@media print {\n *,\n *:before,\n *:after {\n background: transparent !important;\n color: #000 !important;\n box-shadow: none !important;\n text-shadow: none !important;\n }\n a,\n a:visited {\n text-decoration: underline;\n }\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\";\n }\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n thead {\n display: table-header-group;\n }\n tr,\n img {\n page-break-inside: avoid;\n }\n img {\n max-width: 100% !important;\n }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n h2,\n h3 {\n page-break-after: avoid;\n }\n .navbar {\n display: none;\n }\n .btn > .caret,\n .dropup > .btn > .caret {\n border-top-color: #000 !important;\n }\n .label {\n border: 1px solid #000;\n }\n .table {\n border-collapse: collapse !important;\n }\n .table td,\n .table th {\n background-color: #fff !important;\n }\n .table-bordered th,\n .table-bordered td {\n border: 1px solid #ddd !important;\n }\n}\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('../fonts/glyphicons-halflings-regular.eot');\n src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');\n}\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n.glyphicon-asterisk:before {\n content: \"\\2a\";\n}\n.glyphicon-plus:before {\n content: \"\\2b\";\n}\n.glyphicon-euro:before,\n.glyphicon-eur:before {\n content: \"\\20ac\";\n}\n.glyphicon-minus:before {\n content: \"\\2212\";\n}\n.glyphicon-cloud:before {\n content: \"\\2601\";\n}\n.glyphicon-envelope:before {\n content: \"\\2709\";\n}\n.glyphicon-pencil:before {\n content: \"\\270f\";\n}\n.glyphicon-glass:before {\n content: \"\\e001\";\n}\n.glyphicon-music:before {\n content: \"\\e002\";\n}\n.glyphicon-search:before {\n content: \"\\e003\";\n}\n.glyphicon-heart:before {\n content: \"\\e005\";\n}\n.glyphicon-star:before {\n content: \"\\e006\";\n}\n.glyphicon-star-empty:before {\n content: \"\\e007\";\n}\n.glyphicon-user:before {\n content: \"\\e008\";\n}\n.glyphicon-film:before {\n content: \"\\e009\";\n}\n.glyphicon-th-large:before {\n content: \"\\e010\";\n}\n.glyphicon-th:before {\n content: \"\\e011\";\n}\n.glyphicon-th-list:before {\n content: \"\\e012\";\n}\n.glyphicon-ok:before {\n content: \"\\e013\";\n}\n.glyphicon-remove:before {\n content: \"\\e014\";\n}\n.glyphicon-zoom-in:before {\n content: \"\\e015\";\n}\n.glyphicon-zoom-out:before {\n content: \"\\e016\";\n}\n.glyphicon-off:before {\n content: \"\\e017\";\n}\n.glyphicon-signal:before {\n content: \"\\e018\";\n}\n.glyphicon-cog:before {\n content: \"\\e019\";\n}\n.glyphicon-trash:before {\n content: \"\\e020\";\n}\n.glyphicon-home:before {\n content: \"\\e021\";\n}\n.glyphicon-file:before {\n content: \"\\e022\";\n}\n.glyphicon-time:before {\n content: \"\\e023\";\n}\n.glyphicon-road:before {\n content: \"\\e024\";\n}\n.glyphicon-download-alt:before {\n content: \"\\e025\";\n}\n.glyphicon-download:before {\n content: \"\\e026\";\n}\n.glyphicon-upload:before {\n content: \"\\e027\";\n}\n.glyphicon-inbox:before {\n content: \"\\e028\";\n}\n.glyphicon-play-circle:before {\n content: \"\\e029\";\n}\n.glyphicon-repeat:before {\n content: \"\\e030\";\n}\n.glyphicon-refresh:before {\n content: \"\\e031\";\n}\n.glyphicon-list-alt:before {\n content: \"\\e032\";\n}\n.glyphicon-lock:before {\n content: \"\\e033\";\n}\n.glyphicon-flag:before {\n content: \"\\e034\";\n}\n.glyphicon-headphones:before {\n content: \"\\e035\";\n}\n.glyphicon-volume-off:before {\n content: \"\\e036\";\n}\n.glyphicon-volume-down:before {\n content: \"\\e037\";\n}\n.glyphicon-volume-up:before {\n content: \"\\e038\";\n}\n.glyphicon-qrcode:before {\n content: \"\\e039\";\n}\n.glyphicon-barcode:before {\n content: \"\\e040\";\n}\n.glyphicon-tag:before {\n content: \"\\e041\";\n}\n.glyphicon-tags:before {\n content: \"\\e042\";\n}\n.glyphicon-book:before {\n content: \"\\e043\";\n}\n.glyphicon-bookmark:before {\n content: \"\\e044\";\n}\n.glyphicon-print:before {\n content: \"\\e045\";\n}\n.glyphicon-camera:before {\n content: \"\\e046\";\n}\n.glyphicon-font:before {\n content: \"\\e047\";\n}\n.glyphicon-bold:before {\n content: \"\\e048\";\n}\n.glyphicon-italic:before {\n content: \"\\e049\";\n}\n.glyphicon-text-height:before {\n content: \"\\e050\";\n}\n.glyphicon-text-width:before {\n content: \"\\e051\";\n}\n.glyphicon-align-left:before {\n content: \"\\e052\";\n}\n.glyphicon-align-center:before {\n content: \"\\e053\";\n}\n.glyphicon-align-right:before {\n content: \"\\e054\";\n}\n.glyphicon-align-justify:before {\n content: \"\\e055\";\n}\n.glyphicon-list:before {\n content: \"\\e056\";\n}\n.glyphicon-indent-left:before {\n content: \"\\e057\";\n}\n.glyphicon-indent-right:before {\n content: \"\\e058\";\n}\n.glyphicon-facetime-video:before {\n content: \"\\e059\";\n}\n.glyphicon-picture:before {\n content: \"\\e060\";\n}\n.glyphicon-map-marker:before {\n content: \"\\e062\";\n}\n.glyphicon-adjust:before {\n content: \"\\e063\";\n}\n.glyphicon-tint:before {\n content: \"\\e064\";\n}\n.glyphicon-edit:before {\n content: \"\\e065\";\n}\n.glyphicon-share:before {\n content: \"\\e066\";\n}\n.glyphicon-check:before {\n content: \"\\e067\";\n}\n.glyphicon-move:before {\n content: \"\\e068\";\n}\n.glyphicon-step-backward:before {\n content: \"\\e069\";\n}\n.glyphicon-fast-backward:before {\n content: \"\\e070\";\n}\n.glyphicon-backward:before {\n content: \"\\e071\";\n}\n.glyphicon-play:before {\n content: \"\\e072\";\n}\n.glyphicon-pause:before {\n content: \"\\e073\";\n}\n.glyphicon-stop:before {\n content: \"\\e074\";\n}\n.glyphicon-forward:before {\n content: \"\\e075\";\n}\n.glyphicon-fast-forward:before {\n content: \"\\e076\";\n}\n.glyphicon-step-forward:before {\n content: \"\\e077\";\n}\n.glyphicon-eject:before {\n content: \"\\e078\";\n}\n.glyphicon-chevron-left:before {\n content: \"\\e079\";\n}\n.glyphicon-chevron-right:before {\n content: \"\\e080\";\n}\n.glyphicon-plus-sign:before {\n content: \"\\e081\";\n}\n.glyphicon-minus-sign:before {\n content: \"\\e082\";\n}\n.glyphicon-remove-sign:before {\n content: \"\\e083\";\n}\n.glyphicon-ok-sign:before {\n content: \"\\e084\";\n}\n.glyphicon-question-sign:before {\n content: \"\\e085\";\n}\n.glyphicon-info-sign:before {\n content: \"\\e086\";\n}\n.glyphicon-screenshot:before {\n content: \"\\e087\";\n}\n.glyphicon-remove-circle:before {\n content: \"\\e088\";\n}\n.glyphicon-ok-circle:before {\n content: \"\\e089\";\n}\n.glyphicon-ban-circle:before {\n content: \"\\e090\";\n}\n.glyphicon-arrow-left:before {\n content: \"\\e091\";\n}\n.glyphicon-arrow-right:before {\n content: \"\\e092\";\n}\n.glyphicon-arrow-up:before {\n content: \"\\e093\";\n}\n.glyphicon-arrow-down:before {\n content: \"\\e094\";\n}\n.glyphicon-share-alt:before {\n content: \"\\e095\";\n}\n.glyphicon-resize-full:before {\n content: \"\\e096\";\n}\n.glyphicon-resize-small:before {\n content: \"\\e097\";\n}\n.glyphicon-exclamation-sign:before {\n content: \"\\e101\";\n}\n.glyphicon-gift:before {\n content: \"\\e102\";\n}\n.glyphicon-leaf:before {\n content: \"\\e103\";\n}\n.glyphicon-fire:before {\n content: \"\\e104\";\n}\n.glyphicon-eye-open:before {\n content: \"\\e105\";\n}\n.glyphicon-eye-close:before {\n content: \"\\e106\";\n}\n.glyphicon-warning-sign:before {\n content: \"\\e107\";\n}\n.glyphicon-plane:before {\n content: \"\\e108\";\n}\n.glyphicon-calendar:before {\n content: \"\\e109\";\n}\n.glyphicon-random:before {\n content: \"\\e110\";\n}\n.glyphicon-comment:before {\n content: \"\\e111\";\n}\n.glyphicon-magnet:before {\n content: \"\\e112\";\n}\n.glyphicon-chevron-up:before {\n content: \"\\e113\";\n}\n.glyphicon-chevron-down:before {\n content: \"\\e114\";\n}\n.glyphicon-retweet:before {\n content: \"\\e115\";\n}\n.glyphicon-shopping-cart:before {\n content: \"\\e116\";\n}\n.glyphicon-folder-close:before {\n content: \"\\e117\";\n}\n.glyphicon-folder-open:before {\n content: \"\\e118\";\n}\n.glyphicon-resize-vertical:before {\n content: \"\\e119\";\n}\n.glyphicon-resize-horizontal:before {\n content: \"\\e120\";\n}\n.glyphicon-hdd:before {\n content: \"\\e121\";\n}\n.glyphicon-bullhorn:before {\n content: \"\\e122\";\n}\n.glyphicon-bell:before {\n content: \"\\e123\";\n}\n.glyphicon-certificate:before {\n content: \"\\e124\";\n}\n.glyphicon-thumbs-up:before {\n content: \"\\e125\";\n}\n.glyphicon-thumbs-down:before {\n content: \"\\e126\";\n}\n.glyphicon-hand-right:before {\n content: \"\\e127\";\n}\n.glyphicon-hand-left:before {\n content: \"\\e128\";\n}\n.glyphicon-hand-up:before {\n content: \"\\e129\";\n}\n.glyphicon-hand-down:before {\n content: \"\\e130\";\n}\n.glyphicon-circle-arrow-right:before {\n content: \"\\e131\";\n}\n.glyphicon-circle-arrow-left:before {\n content: \"\\e132\";\n}\n.glyphicon-circle-arrow-up:before {\n content: \"\\e133\";\n}\n.glyphicon-circle-arrow-down:before {\n content: \"\\e134\";\n}\n.glyphicon-globe:before {\n content: \"\\e135\";\n}\n.glyphicon-wrench:before {\n content: \"\\e136\";\n}\n.glyphicon-tasks:before {\n content: \"\\e137\";\n}\n.glyphicon-filter:before {\n content: \"\\e138\";\n}\n.glyphicon-briefcase:before {\n content: \"\\e139\";\n}\n.glyphicon-fullscreen:before {\n content: \"\\e140\";\n}\n.glyphicon-dashboard:before {\n content: \"\\e141\";\n}\n.glyphicon-paperclip:before {\n content: \"\\e142\";\n}\n.glyphicon-heart-empty:before {\n content: \"\\e143\";\n}\n.glyphicon-link:before {\n content: \"\\e144\";\n}\n.glyphicon-phone:before {\n content: \"\\e145\";\n}\n.glyphicon-pushpin:before {\n content: \"\\e146\";\n}\n.glyphicon-usd:before {\n content: \"\\e148\";\n}\n.glyphicon-gbp:before {\n content: \"\\e149\";\n}\n.glyphicon-sort:before {\n content: \"\\e150\";\n}\n.glyphicon-sort-by-alphabet:before {\n content: \"\\e151\";\n}\n.glyphicon-sort-by-alphabet-alt:before {\n content: \"\\e152\";\n}\n.glyphicon-sort-by-order:before {\n content: \"\\e153\";\n}\n.glyphicon-sort-by-order-alt:before {\n content: \"\\e154\";\n}\n.glyphicon-sort-by-attributes:before {\n content: \"\\e155\";\n}\n.glyphicon-sort-by-attributes-alt:before {\n content: \"\\e156\";\n}\n.glyphicon-unchecked:before {\n content: \"\\e157\";\n}\n.glyphicon-expand:before {\n content: \"\\e158\";\n}\n.glyphicon-collapse-down:before {\n content: \"\\e159\";\n}\n.glyphicon-collapse-up:before {\n content: \"\\e160\";\n}\n.glyphicon-log-in:before {\n content: \"\\e161\";\n}\n.glyphicon-flash:before {\n content: \"\\e162\";\n}\n.glyphicon-log-out:before {\n content: \"\\e163\";\n}\n.glyphicon-new-window:before {\n content: \"\\e164\";\n}\n.glyphicon-record:before {\n content: \"\\e165\";\n}\n.glyphicon-save:before {\n content: \"\\e166\";\n}\n.glyphicon-open:before {\n content: \"\\e167\";\n}\n.glyphicon-saved:before {\n content: \"\\e168\";\n}\n.glyphicon-import:before {\n content: \"\\e169\";\n}\n.glyphicon-export:before {\n content: \"\\e170\";\n}\n.glyphicon-send:before {\n content: \"\\e171\";\n}\n.glyphicon-floppy-disk:before {\n content: \"\\e172\";\n}\n.glyphicon-floppy-saved:before {\n content: \"\\e173\";\n}\n.glyphicon-floppy-remove:before {\n content: \"\\e174\";\n}\n.glyphicon-floppy-save:before {\n content: \"\\e175\";\n}\n.glyphicon-floppy-open:before {\n content: \"\\e176\";\n}\n.glyphicon-credit-card:before {\n content: \"\\e177\";\n}\n.glyphicon-transfer:before {\n content: \"\\e178\";\n}\n.glyphicon-cutlery:before {\n content: \"\\e179\";\n}\n.glyphicon-header:before {\n content: \"\\e180\";\n}\n.glyphicon-compressed:before {\n content: \"\\e181\";\n}\n.glyphicon-earphone:before {\n content: \"\\e182\";\n}\n.glyphicon-phone-alt:before {\n content: \"\\e183\";\n}\n.glyphicon-tower:before {\n content: \"\\e184\";\n}\n.glyphicon-stats:before {\n content: \"\\e185\";\n}\n.glyphicon-sd-video:before {\n content: \"\\e186\";\n}\n.glyphicon-hd-video:before {\n content: \"\\e187\";\n}\n.glyphicon-subtitles:before {\n content: \"\\e188\";\n}\n.glyphicon-sound-stereo:before {\n content: \"\\e189\";\n}\n.glyphicon-sound-dolby:before {\n content: \"\\e190\";\n}\n.glyphicon-sound-5-1:before {\n content: \"\\e191\";\n}\n.glyphicon-sound-6-1:before {\n content: \"\\e192\";\n}\n.glyphicon-sound-7-1:before {\n content: \"\\e193\";\n}\n.glyphicon-copyright-mark:before {\n content: \"\\e194\";\n}\n.glyphicon-registration-mark:before {\n content: \"\\e195\";\n}\n.glyphicon-cloud-download:before {\n content: \"\\e197\";\n}\n.glyphicon-cloud-upload:before {\n content: \"\\e198\";\n}\n.glyphicon-tree-conifer:before {\n content: \"\\e199\";\n}\n.glyphicon-tree-deciduous:before {\n content: \"\\e200\";\n}\n.glyphicon-cd:before {\n content: \"\\e201\";\n}\n.glyphicon-save-file:before {\n content: \"\\e202\";\n}\n.glyphicon-open-file:before {\n content: \"\\e203\";\n}\n.glyphicon-level-up:before {\n content: \"\\e204\";\n}\n.glyphicon-copy:before {\n content: \"\\e205\";\n}\n.glyphicon-paste:before {\n content: \"\\e206\";\n}\n.glyphicon-alert:before {\n content: \"\\e209\";\n}\n.glyphicon-equalizer:before {\n content: \"\\e210\";\n}\n.glyphicon-king:before {\n content: \"\\e211\";\n}\n.glyphicon-queen:before {\n content: \"\\e212\";\n}\n.glyphicon-pawn:before {\n content: \"\\e213\";\n}\n.glyphicon-bishop:before {\n content: \"\\e214\";\n}\n.glyphicon-knight:before {\n content: \"\\e215\";\n}\n.glyphicon-baby-formula:before {\n content: \"\\e216\";\n}\n.glyphicon-tent:before {\n content: \"\\26fa\";\n}\n.glyphicon-blackboard:before {\n content: \"\\e218\";\n}\n.glyphicon-bed:before {\n content: \"\\e219\";\n}\n.glyphicon-apple:before {\n content: \"\\f8ff\";\n}\n.glyphicon-erase:before {\n content: \"\\e221\";\n}\n.glyphicon-hourglass:before {\n content: \"\\231b\";\n}\n.glyphicon-lamp:before {\n content: \"\\e223\";\n}\n.glyphicon-duplicate:before {\n content: \"\\e224\";\n}\n.glyphicon-piggy-bank:before {\n content: \"\\e225\";\n}\n.glyphicon-scissors:before {\n content: \"\\e226\";\n}\n.glyphicon-bitcoin:before {\n content: \"\\e227\";\n}\n.glyphicon-btc:before {\n content: \"\\e227\";\n}\n.glyphicon-xbt:before {\n content: \"\\e227\";\n}\n.glyphicon-yen:before {\n content: \"\\00a5\";\n}\n.glyphicon-jpy:before {\n content: \"\\00a5\";\n}\n.glyphicon-ruble:before {\n content: \"\\20bd\";\n}\n.glyphicon-rub:before {\n content: \"\\20bd\";\n}\n.glyphicon-scale:before {\n content: \"\\e230\";\n}\n.glyphicon-ice-lolly:before {\n content: \"\\e231\";\n}\n.glyphicon-ice-lolly-tasted:before {\n content: \"\\e232\";\n}\n.glyphicon-education:before {\n content: \"\\e233\";\n}\n.glyphicon-option-horizontal:before {\n content: \"\\e234\";\n}\n.glyphicon-option-vertical:before {\n content: \"\\e235\";\n}\n.glyphicon-menu-hamburger:before {\n content: \"\\e236\";\n}\n.glyphicon-modal-window:before {\n content: \"\\e237\";\n}\n.glyphicon-oil:before {\n content: \"\\e238\";\n}\n.glyphicon-grain:before {\n content: \"\\e239\";\n}\n.glyphicon-sunglasses:before {\n content: \"\\e240\";\n}\n.glyphicon-text-size:before {\n content: \"\\e241\";\n}\n.glyphicon-text-color:before {\n content: \"\\e242\";\n}\n.glyphicon-text-background:before {\n content: \"\\e243\";\n}\n.glyphicon-object-align-top:before {\n content: \"\\e244\";\n}\n.glyphicon-object-align-bottom:before {\n content: \"\\e245\";\n}\n.glyphicon-object-align-horizontal:before {\n content: \"\\e246\";\n}\n.glyphicon-object-align-left:before {\n content: \"\\e247\";\n}\n.glyphicon-object-align-vertical:before {\n content: \"\\e248\";\n}\n.glyphicon-object-align-right:before {\n content: \"\\e249\";\n}\n.glyphicon-triangle-right:before {\n content: \"\\e250\";\n}\n.glyphicon-triangle-left:before {\n content: \"\\e251\";\n}\n.glyphicon-triangle-bottom:before {\n content: \"\\e252\";\n}\n.glyphicon-triangle-top:before {\n content: \"\\e253\";\n}\n.glyphicon-console:before {\n content: \"\\e254\";\n}\n.glyphicon-superscript:before {\n content: \"\\e255\";\n}\n.glyphicon-subscript:before {\n content: \"\\e256\";\n}\n.glyphicon-menu-left:before {\n content: \"\\e257\";\n}\n.glyphicon-menu-right:before {\n content: \"\\e258\";\n}\n.glyphicon-menu-down:before {\n content: \"\\e259\";\n}\n.glyphicon-menu-up:before {\n content: \"\\e260\";\n}\n* {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n*:before,\n*:after {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\nbody {\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-size: 14px;\n line-height: 1.42857143;\n color: #333333;\n background-color: #ffffff;\n}\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\na {\n color: #337ab7;\n text-decoration: none;\n}\na:hover,\na:focus {\n color: #23527c;\n text-decoration: underline;\n}\na:focus {\n outline: thin dotted;\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\nfigure {\n margin: 0;\n}\nimg {\n vertical-align: middle;\n}\n.img-responsive,\n.thumbnail > img,\n.thumbnail a > img,\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n display: block;\n max-width: 100%;\n height: auto;\n}\n.img-rounded {\n border-radius: 6px;\n}\n.img-thumbnail {\n padding: 4px;\n line-height: 1.42857143;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n border-radius: 4px;\n -webkit-transition: all 0.2s ease-in-out;\n -o-transition: all 0.2s ease-in-out;\n transition: all 0.2s ease-in-out;\n display: inline-block;\n max-width: 100%;\n height: auto;\n}\n.img-circle {\n border-radius: 50%;\n}\nhr {\n margin-top: 20px;\n margin-bottom: 20px;\n border: 0;\n border-top: 1px solid #eeeeee;\n}\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n border: 0;\n}\n.sr-only-focusable:active,\n.sr-only-focusable:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n}\n[role=\"button\"] {\n cursor: pointer;\n}\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n font-family: inherit;\n font-weight: 500;\n line-height: 1.1;\n color: inherit;\n}\nh1 small,\nh2 small,\nh3 small,\nh4 small,\nh5 small,\nh6 small,\n.h1 small,\n.h2 small,\n.h3 small,\n.h4 small,\n.h5 small,\n.h6 small,\nh1 .small,\nh2 .small,\nh3 .small,\nh4 .small,\nh5 .small,\nh6 .small,\n.h1 .small,\n.h2 .small,\n.h3 .small,\n.h4 .small,\n.h5 .small,\n.h6 .small {\n font-weight: normal;\n line-height: 1;\n color: #777777;\n}\nh1,\n.h1,\nh2,\n.h2,\nh3,\n.h3 {\n margin-top: 20px;\n margin-bottom: 10px;\n}\nh1 small,\n.h1 small,\nh2 small,\n.h2 small,\nh3 small,\n.h3 small,\nh1 .small,\n.h1 .small,\nh2 .small,\n.h2 .small,\nh3 .small,\n.h3 .small {\n font-size: 65%;\n}\nh4,\n.h4,\nh5,\n.h5,\nh6,\n.h6 {\n margin-top: 10px;\n margin-bottom: 10px;\n}\nh4 small,\n.h4 small,\nh5 small,\n.h5 small,\nh6 small,\n.h6 small,\nh4 .small,\n.h4 .small,\nh5 .small,\n.h5 .small,\nh6 .small,\n.h6 .small {\n font-size: 75%;\n}\nh1,\n.h1 {\n font-size: 36px;\n}\nh2,\n.h2 {\n font-size: 30px;\n}\nh3,\n.h3 {\n font-size: 24px;\n}\nh4,\n.h4 {\n font-size: 18px;\n}\nh5,\n.h5 {\n font-size: 14px;\n}\nh6,\n.h6 {\n font-size: 12px;\n}\np {\n margin: 0 0 10px;\n}\n.lead {\n margin-bottom: 20px;\n font-size: 16px;\n font-weight: 300;\n line-height: 1.4;\n}\n@media (min-width: 768px) {\n .lead {\n font-size: 21px;\n }\n}\nsmall,\n.small {\n font-size: 85%;\n}\nmark,\n.mark {\n background-color: #fcf8e3;\n padding: .2em;\n}\n.text-left {\n text-align: left;\n}\n.text-right {\n text-align: right;\n}\n.text-center {\n text-align: center;\n}\n.text-justify {\n text-align: justify;\n}\n.text-nowrap {\n white-space: nowrap;\n}\n.text-lowercase {\n text-transform: lowercase;\n}\n.text-uppercase {\n text-transform: uppercase;\n}\n.text-capitalize {\n text-transform: capitalize;\n}\n.text-muted {\n color: #777777;\n}\n.text-primary {\n color: #337ab7;\n}\na.text-primary:hover,\na.text-primary:focus {\n color: #286090;\n}\n.text-success {\n color: #3c763d;\n}\na.text-success:hover,\na.text-success:focus {\n color: #2b542c;\n}\n.text-info {\n color: #31708f;\n}\na.text-info:hover,\na.text-info:focus {\n color: #245269;\n}\n.text-warning {\n color: #8a6d3b;\n}\na.text-warning:hover,\na.text-warning:focus {\n color: #66512c;\n}\n.text-danger {\n color: #a94442;\n}\na.text-danger:hover,\na.text-danger:focus {\n color: #843534;\n}\n.bg-primary {\n color: #fff;\n background-color: #337ab7;\n}\na.bg-primary:hover,\na.bg-primary:focus {\n background-color: #286090;\n}\n.bg-success {\n background-color: #dff0d8;\n}\na.bg-success:hover,\na.bg-success:focus {\n background-color: #c1e2b3;\n}\n.bg-info {\n background-color: #d9edf7;\n}\na.bg-info:hover,\na.bg-info:focus {\n background-color: #afd9ee;\n}\n.bg-warning {\n background-color: #fcf8e3;\n}\na.bg-warning:hover,\na.bg-warning:focus {\n background-color: #f7ecb5;\n}\n.bg-danger {\n background-color: #f2dede;\n}\na.bg-danger:hover,\na.bg-danger:focus {\n background-color: #e4b9b9;\n}\n.page-header {\n padding-bottom: 9px;\n margin: 40px 0 20px;\n border-bottom: 1px solid #eeeeee;\n}\nul,\nol {\n margin-top: 0;\n margin-bottom: 10px;\n}\nul ul,\nol ul,\nul ol,\nol ol {\n margin-bottom: 0;\n}\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n.list-inline {\n padding-left: 0;\n list-style: none;\n margin-left: -5px;\n}\n.list-inline > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n}\ndl {\n margin-top: 0;\n margin-bottom: 20px;\n}\ndt,\ndd {\n line-height: 1.42857143;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0;\n}\n@media (min-width: 768px) {\n .dl-horizontal dt {\n float: left;\n width: 160px;\n clear: left;\n text-align: right;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .dl-horizontal dd {\n margin-left: 180px;\n }\n}\nabbr[title],\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted #777777;\n}\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\nblockquote {\n padding: 10px 20px;\n margin: 0 0 20px;\n font-size: 17.5px;\n border-left: 5px solid #eeeeee;\n}\nblockquote p:last-child,\nblockquote ul:last-child,\nblockquote ol:last-child {\n margin-bottom: 0;\n}\nblockquote footer,\nblockquote small,\nblockquote .small {\n display: block;\n font-size: 80%;\n line-height: 1.42857143;\n color: #777777;\n}\nblockquote footer:before,\nblockquote small:before,\nblockquote .small:before {\n content: '\\2014 \\00A0';\n}\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid #eeeeee;\n border-left: 0;\n text-align: right;\n}\n.blockquote-reverse footer:before,\nblockquote.pull-right footer:before,\n.blockquote-reverse small:before,\nblockquote.pull-right small:before,\n.blockquote-reverse .small:before,\nblockquote.pull-right .small:before {\n content: '';\n}\n.blockquote-reverse footer:after,\nblockquote.pull-right footer:after,\n.blockquote-reverse small:after,\nblockquote.pull-right small:after,\n.blockquote-reverse .small:after,\nblockquote.pull-right .small:after {\n content: '\\00A0 \\2014';\n}\naddress {\n margin-bottom: 20px;\n font-style: normal;\n line-height: 1.42857143;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: Menlo, Monaco, Consolas, \"Courier New\", monospace;\n}\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: #c7254e;\n background-color: #f9f2f4;\n border-radius: 4px;\n}\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: #ffffff;\n background-color: #333333;\n border-radius: 3px;\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\nkbd kbd {\n padding: 0;\n font-size: 100%;\n font-weight: bold;\n box-shadow: none;\n}\npre {\n display: block;\n padding: 9.5px;\n margin: 0 0 10px;\n font-size: 13px;\n line-height: 1.42857143;\n word-break: break-all;\n word-wrap: break-word;\n color: #333333;\n background-color: #f5f5f5;\n border: 1px solid #cccccc;\n border-radius: 4px;\n}\npre code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n}\n.pre-scrollable {\n max-height: 340px;\n overflow-y: scroll;\n}\n.container {\n margin-right: auto;\n margin-left: auto;\n padding-left: 15px;\n padding-right: 15px;\n}\n@media (min-width: 768px) {\n .container {\n width: 750px;\n }\n}\n@media (min-width: 992px) {\n .container {\n width: 970px;\n }\n}\n@media (min-width: 1200px) {\n .container {\n width: 1170px;\n }\n}\n.container-fluid {\n margin-right: auto;\n margin-left: auto;\n padding-left: 15px;\n padding-right: 15px;\n}\n.row {\n margin-left: -15px;\n margin-right: -15px;\n}\n.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {\n position: relative;\n min-height: 1px;\n padding-left: 15px;\n padding-right: 15px;\n}\n.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {\n float: left;\n}\n.col-xs-12 {\n width: 100%;\n}\n.col-xs-11 {\n width: 91.66666667%;\n}\n.col-xs-10 {\n width: 83.33333333%;\n}\n.col-xs-9 {\n width: 75%;\n}\n.col-xs-8 {\n width: 66.66666667%;\n}\n.col-xs-7 {\n width: 58.33333333%;\n}\n.col-xs-6 {\n width: 50%;\n}\n.col-xs-5 {\n width: 41.66666667%;\n}\n.col-xs-4 {\n width: 33.33333333%;\n}\n.col-xs-3 {\n width: 25%;\n}\n.col-xs-2 {\n width: 16.66666667%;\n}\n.col-xs-1 {\n width: 8.33333333%;\n}\n.col-xs-pull-12 {\n right: 100%;\n}\n.col-xs-pull-11 {\n right: 91.66666667%;\n}\n.col-xs-pull-10 {\n right: 83.33333333%;\n}\n.col-xs-pull-9 {\n right: 75%;\n}\n.col-xs-pull-8 {\n right: 66.66666667%;\n}\n.col-xs-pull-7 {\n right: 58.33333333%;\n}\n.col-xs-pull-6 {\n right: 50%;\n}\n.col-xs-pull-5 {\n right: 41.66666667%;\n}\n.col-xs-pull-4 {\n right: 33.33333333%;\n}\n.col-xs-pull-3 {\n right: 25%;\n}\n.col-xs-pull-2 {\n right: 16.66666667%;\n}\n.col-xs-pull-1 {\n right: 8.33333333%;\n}\n.col-xs-pull-0 {\n right: auto;\n}\n.col-xs-push-12 {\n left: 100%;\n}\n.col-xs-push-11 {\n left: 91.66666667%;\n}\n.col-xs-push-10 {\n left: 83.33333333%;\n}\n.col-xs-push-9 {\n left: 75%;\n}\n.col-xs-push-8 {\n left: 66.66666667%;\n}\n.col-xs-push-7 {\n left: 58.33333333%;\n}\n.col-xs-push-6 {\n left: 50%;\n}\n.col-xs-push-5 {\n left: 41.66666667%;\n}\n.col-xs-push-4 {\n left: 33.33333333%;\n}\n.col-xs-push-3 {\n left: 25%;\n}\n.col-xs-push-2 {\n left: 16.66666667%;\n}\n.col-xs-push-1 {\n left: 8.33333333%;\n}\n.col-xs-push-0 {\n left: auto;\n}\n.col-xs-offset-12 {\n margin-left: 100%;\n}\n.col-xs-offset-11 {\n margin-left: 91.66666667%;\n}\n.col-xs-offset-10 {\n margin-left: 83.33333333%;\n}\n.col-xs-offset-9 {\n margin-left: 75%;\n}\n.col-xs-offset-8 {\n margin-left: 66.66666667%;\n}\n.col-xs-offset-7 {\n margin-left: 58.33333333%;\n}\n.col-xs-offset-6 {\n margin-left: 50%;\n}\n.col-xs-offset-5 {\n margin-left: 41.66666667%;\n}\n.col-xs-offset-4 {\n margin-left: 33.33333333%;\n}\n.col-xs-offset-3 {\n margin-left: 25%;\n}\n.col-xs-offset-2 {\n margin-left: 16.66666667%;\n}\n.col-xs-offset-1 {\n margin-left: 8.33333333%;\n}\n.col-xs-offset-0 {\n margin-left: 0%;\n}\n@media (min-width: 768px) {\n .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {\n float: left;\n }\n .col-sm-12 {\n width: 100%;\n }\n .col-sm-11 {\n width: 91.66666667%;\n }\n .col-sm-10 {\n width: 83.33333333%;\n }\n .col-sm-9 {\n width: 75%;\n }\n .col-sm-8 {\n width: 66.66666667%;\n }\n .col-sm-7 {\n width: 58.33333333%;\n }\n .col-sm-6 {\n width: 50%;\n }\n .col-sm-5 {\n width: 41.66666667%;\n }\n .col-sm-4 {\n width: 33.33333333%;\n }\n .col-sm-3 {\n width: 25%;\n }\n .col-sm-2 {\n width: 16.66666667%;\n }\n .col-sm-1 {\n width: 8.33333333%;\n }\n .col-sm-pull-12 {\n right: 100%;\n }\n .col-sm-pull-11 {\n right: 91.66666667%;\n }\n .col-sm-pull-10 {\n right: 83.33333333%;\n }\n .col-sm-pull-9 {\n right: 75%;\n }\n .col-sm-pull-8 {\n right: 66.66666667%;\n }\n .col-sm-pull-7 {\n right: 58.33333333%;\n }\n .col-sm-pull-6 {\n right: 50%;\n }\n .col-sm-pull-5 {\n right: 41.66666667%;\n }\n .col-sm-pull-4 {\n right: 33.33333333%;\n }\n .col-sm-pull-3 {\n right: 25%;\n }\n .col-sm-pull-2 {\n right: 16.66666667%;\n }\n .col-sm-pull-1 {\n right: 8.33333333%;\n }\n .col-sm-pull-0 {\n right: auto;\n }\n .col-sm-push-12 {\n left: 100%;\n }\n .col-sm-push-11 {\n left: 91.66666667%;\n }\n .col-sm-push-10 {\n left: 83.33333333%;\n }\n .col-sm-push-9 {\n left: 75%;\n }\n .col-sm-push-8 {\n left: 66.66666667%;\n }\n .col-sm-push-7 {\n left: 58.33333333%;\n }\n .col-sm-push-6 {\n left: 50%;\n }\n .col-sm-push-5 {\n left: 41.66666667%;\n }\n .col-sm-push-4 {\n left: 33.33333333%;\n }\n .col-sm-push-3 {\n left: 25%;\n }\n .col-sm-push-2 {\n left: 16.66666667%;\n }\n .col-sm-push-1 {\n left: 8.33333333%;\n }\n .col-sm-push-0 {\n left: auto;\n }\n .col-sm-offset-12 {\n margin-left: 100%;\n }\n .col-sm-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-sm-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-sm-offset-9 {\n margin-left: 75%;\n }\n .col-sm-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-sm-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-sm-offset-6 {\n margin-left: 50%;\n }\n .col-sm-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-sm-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-sm-offset-3 {\n margin-left: 25%;\n }\n .col-sm-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-sm-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-sm-offset-0 {\n margin-left: 0%;\n }\n}\n@media (min-width: 992px) {\n .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {\n float: left;\n }\n .col-md-12 {\n width: 100%;\n }\n .col-md-11 {\n width: 91.66666667%;\n }\n .col-md-10 {\n width: 83.33333333%;\n }\n .col-md-9 {\n width: 75%;\n }\n .col-md-8 {\n width: 66.66666667%;\n }\n .col-md-7 {\n width: 58.33333333%;\n }\n .col-md-6 {\n width: 50%;\n }\n .col-md-5 {\n width: 41.66666667%;\n }\n .col-md-4 {\n width: 33.33333333%;\n }\n .col-md-3 {\n width: 25%;\n }\n .col-md-2 {\n width: 16.66666667%;\n }\n .col-md-1 {\n width: 8.33333333%;\n }\n .col-md-pull-12 {\n right: 100%;\n }\n .col-md-pull-11 {\n right: 91.66666667%;\n }\n .col-md-pull-10 {\n right: 83.33333333%;\n }\n .col-md-pull-9 {\n right: 75%;\n }\n .col-md-pull-8 {\n right: 66.66666667%;\n }\n .col-md-pull-7 {\n right: 58.33333333%;\n }\n .col-md-pull-6 {\n right: 50%;\n }\n .col-md-pull-5 {\n right: 41.66666667%;\n }\n .col-md-pull-4 {\n right: 33.33333333%;\n }\n .col-md-pull-3 {\n right: 25%;\n }\n .col-md-pull-2 {\n right: 16.66666667%;\n }\n .col-md-pull-1 {\n right: 8.33333333%;\n }\n .col-md-pull-0 {\n right: auto;\n }\n .col-md-push-12 {\n left: 100%;\n }\n .col-md-push-11 {\n left: 91.66666667%;\n }\n .col-md-push-10 {\n left: 83.33333333%;\n }\n .col-md-push-9 {\n left: 75%;\n }\n .col-md-push-8 {\n left: 66.66666667%;\n }\n .col-md-push-7 {\n left: 58.33333333%;\n }\n .col-md-push-6 {\n left: 50%;\n }\n .col-md-push-5 {\n left: 41.66666667%;\n }\n .col-md-push-4 {\n left: 33.33333333%;\n }\n .col-md-push-3 {\n left: 25%;\n }\n .col-md-push-2 {\n left: 16.66666667%;\n }\n .col-md-push-1 {\n left: 8.33333333%;\n }\n .col-md-push-0 {\n left: auto;\n }\n .col-md-offset-12 {\n margin-left: 100%;\n }\n .col-md-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-md-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-md-offset-9 {\n margin-left: 75%;\n }\n .col-md-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-md-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-md-offset-6 {\n margin-left: 50%;\n }\n .col-md-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-md-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-md-offset-3 {\n margin-left: 25%;\n }\n .col-md-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-md-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-md-offset-0 {\n margin-left: 0%;\n }\n}\n@media (min-width: 1200px) {\n .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {\n float: left;\n }\n .col-lg-12 {\n width: 100%;\n }\n .col-lg-11 {\n width: 91.66666667%;\n }\n .col-lg-10 {\n width: 83.33333333%;\n }\n .col-lg-9 {\n width: 75%;\n }\n .col-lg-8 {\n width: 66.66666667%;\n }\n .col-lg-7 {\n width: 58.33333333%;\n }\n .col-lg-6 {\n width: 50%;\n }\n .col-lg-5 {\n width: 41.66666667%;\n }\n .col-lg-4 {\n width: 33.33333333%;\n }\n .col-lg-3 {\n width: 25%;\n }\n .col-lg-2 {\n width: 16.66666667%;\n }\n .col-lg-1 {\n width: 8.33333333%;\n }\n .col-lg-pull-12 {\n right: 100%;\n }\n .col-lg-pull-11 {\n right: 91.66666667%;\n }\n .col-lg-pull-10 {\n right: 83.33333333%;\n }\n .col-lg-pull-9 {\n right: 75%;\n }\n .col-lg-pull-8 {\n right: 66.66666667%;\n }\n .col-lg-pull-7 {\n right: 58.33333333%;\n }\n .col-lg-pull-6 {\n right: 50%;\n }\n .col-lg-pull-5 {\n right: 41.66666667%;\n }\n .col-lg-pull-4 {\n right: 33.33333333%;\n }\n .col-lg-pull-3 {\n right: 25%;\n }\n .col-lg-pull-2 {\n right: 16.66666667%;\n }\n .col-lg-pull-1 {\n right: 8.33333333%;\n }\n .col-lg-pull-0 {\n right: auto;\n }\n .col-lg-push-12 {\n left: 100%;\n }\n .col-lg-push-11 {\n left: 91.66666667%;\n }\n .col-lg-push-10 {\n left: 83.33333333%;\n }\n .col-lg-push-9 {\n left: 75%;\n }\n .col-lg-push-8 {\n left: 66.66666667%;\n }\n .col-lg-push-7 {\n left: 58.33333333%;\n }\n .col-lg-push-6 {\n left: 50%;\n }\n .col-lg-push-5 {\n left: 41.66666667%;\n }\n .col-lg-push-4 {\n left: 33.33333333%;\n }\n .col-lg-push-3 {\n left: 25%;\n }\n .col-lg-push-2 {\n left: 16.66666667%;\n }\n .col-lg-push-1 {\n left: 8.33333333%;\n }\n .col-lg-push-0 {\n left: auto;\n }\n .col-lg-offset-12 {\n margin-left: 100%;\n }\n .col-lg-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-lg-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-lg-offset-9 {\n margin-left: 75%;\n }\n .col-lg-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-lg-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-lg-offset-6 {\n margin-left: 50%;\n }\n .col-lg-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-lg-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-lg-offset-3 {\n margin-left: 25%;\n }\n .col-lg-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-lg-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-lg-offset-0 {\n margin-left: 0%;\n }\n}\ntable {\n background-color: transparent;\n}\ncaption {\n padding-top: 8px;\n padding-bottom: 8px;\n color: #777777;\n text-align: left;\n}\nth {\n text-align: left;\n}\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: 20px;\n}\n.table > thead > tr > th,\n.table > tbody > tr > th,\n.table > tfoot > tr > th,\n.table > thead > tr > td,\n.table > tbody > tr > td,\n.table > tfoot > tr > td {\n padding: 8px;\n line-height: 1.42857143;\n vertical-align: top;\n border-top: 1px solid #dddddd;\n}\n.table > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid #dddddd;\n}\n.table > caption + thead > tr:first-child > th,\n.table > colgroup + thead > tr:first-child > th,\n.table > thead:first-child > tr:first-child > th,\n.table > caption + thead > tr:first-child > td,\n.table > colgroup + thead > tr:first-child > td,\n.table > thead:first-child > tr:first-child > td {\n border-top: 0;\n}\n.table > tbody + tbody {\n border-top: 2px solid #dddddd;\n}\n.table .table {\n background-color: #ffffff;\n}\n.table-condensed > thead > tr > th,\n.table-condensed > tbody > tr > th,\n.table-condensed > tfoot > tr > th,\n.table-condensed > thead > tr > td,\n.table-condensed > tbody > tr > td,\n.table-condensed > tfoot > tr > td {\n padding: 5px;\n}\n.table-bordered {\n border: 1px solid #dddddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > tbody > tr > th,\n.table-bordered > tfoot > tr > th,\n.table-bordered > thead > tr > td,\n.table-bordered > tbody > tr > td,\n.table-bordered > tfoot > tr > td {\n border: 1px solid #dddddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > thead > tr > td {\n border-bottom-width: 2px;\n}\n.table-striped > tbody > tr:nth-of-type(odd) {\n background-color: #f9f9f9;\n}\n.table-hover > tbody > tr:hover {\n background-color: #f5f5f5;\n}\ntable col[class*=\"col-\"] {\n position: static;\n float: none;\n display: table-column;\n}\ntable td[class*=\"col-\"],\ntable th[class*=\"col-\"] {\n position: static;\n float: none;\n display: table-cell;\n}\n.table > thead > tr > td.active,\n.table > tbody > tr > td.active,\n.table > tfoot > tr > td.active,\n.table > thead > tr > th.active,\n.table > tbody > tr > th.active,\n.table > tfoot > tr > th.active,\n.table > thead > tr.active > td,\n.table > tbody > tr.active > td,\n.table > tfoot > tr.active > td,\n.table > thead > tr.active > th,\n.table > tbody > tr.active > th,\n.table > tfoot > tr.active > th {\n background-color: #f5f5f5;\n}\n.table-hover > tbody > tr > td.active:hover,\n.table-hover > tbody > tr > th.active:hover,\n.table-hover > tbody > tr.active:hover > td,\n.table-hover > tbody > tr:hover > .active,\n.table-hover > tbody > tr.active:hover > th {\n background-color: #e8e8e8;\n}\n.table > thead > tr > td.success,\n.table > tbody > tr > td.success,\n.table > tfoot > tr > td.success,\n.table > thead > tr > th.success,\n.table > tbody > tr > th.success,\n.table > tfoot > tr > th.success,\n.table > thead > tr.success > td,\n.table > tbody > tr.success > td,\n.table > tfoot > tr.success > td,\n.table > thead > tr.success > th,\n.table > tbody > tr.success > th,\n.table > tfoot > tr.success > th {\n background-color: #dff0d8;\n}\n.table-hover > tbody > tr > td.success:hover,\n.table-hover > tbody > tr > th.success:hover,\n.table-hover > tbody > tr.success:hover > td,\n.table-hover > tbody > tr:hover > .success,\n.table-hover > tbody > tr.success:hover > th {\n background-color: #d0e9c6;\n}\n.table > thead > tr > td.info,\n.table > tbody > tr > td.info,\n.table > tfoot > tr > td.info,\n.table > thead > tr > th.info,\n.table > tbody > tr > th.info,\n.table > tfoot > tr > th.info,\n.table > thead > tr.info > td,\n.table > tbody > tr.info > td,\n.table > tfoot > tr.info > td,\n.table > thead > tr.info > th,\n.table > tbody > tr.info > th,\n.table > tfoot > tr.info > th {\n background-color: #d9edf7;\n}\n.table-hover > tbody > tr > td.info:hover,\n.table-hover > tbody > tr > th.info:hover,\n.table-hover > tbody > tr.info:hover > td,\n.table-hover > tbody > tr:hover > .info,\n.table-hover > tbody > tr.info:hover > th {\n background-color: #c4e3f3;\n}\n.table > thead > tr > td.warning,\n.table > tbody > tr > td.warning,\n.table > tfoot > tr > td.warning,\n.table > thead > tr > th.warning,\n.table > tbody > tr > th.warning,\n.table > tfoot > tr > th.warning,\n.table > thead > tr.warning > td,\n.table > tbody > tr.warning > td,\n.table > tfoot > tr.warning > td,\n.table > thead > tr.warning > th,\n.table > tbody > tr.warning > th,\n.table > tfoot > tr.warning > th {\n background-color: #fcf8e3;\n}\n.table-hover > tbody > tr > td.warning:hover,\n.table-hover > tbody > tr > th.warning:hover,\n.table-hover > tbody > tr.warning:hover > td,\n.table-hover > tbody > tr:hover > .warning,\n.table-hover > tbody > tr.warning:hover > th {\n background-color: #faf2cc;\n}\n.table > thead > tr > td.danger,\n.table > tbody > tr > td.danger,\n.table > tfoot > tr > td.danger,\n.table > thead > tr > th.danger,\n.table > tbody > tr > th.danger,\n.table > tfoot > tr > th.danger,\n.table > thead > tr.danger > td,\n.table > tbody > tr.danger > td,\n.table > tfoot > tr.danger > td,\n.table > thead > tr.danger > th,\n.table > tbody > tr.danger > th,\n.table > tfoot > tr.danger > th {\n background-color: #f2dede;\n}\n.table-hover > tbody > tr > td.danger:hover,\n.table-hover > tbody > tr > th.danger:hover,\n.table-hover > tbody > tr.danger:hover > td,\n.table-hover > tbody > tr:hover > .danger,\n.table-hover > tbody > tr.danger:hover > th {\n background-color: #ebcccc;\n}\n.table-responsive {\n overflow-x: auto;\n min-height: 0.01%;\n}\n@media screen and (max-width: 767px) {\n .table-responsive {\n width: 100%;\n margin-bottom: 15px;\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid #dddddd;\n }\n .table-responsive > .table {\n margin-bottom: 0;\n }\n .table-responsive > .table > thead > tr > th,\n .table-responsive > .table > tbody > tr > th,\n .table-responsive > .table > tfoot > tr > th,\n .table-responsive > .table > thead > tr > td,\n .table-responsive > .table > tbody > tr > td,\n .table-responsive > .table > tfoot > tr > td {\n white-space: nowrap;\n }\n .table-responsive > .table-bordered {\n border: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:first-child,\n .table-responsive > .table-bordered > tbody > tr > th:first-child,\n .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n .table-responsive > .table-bordered > thead > tr > td:first-child,\n .table-responsive > .table-bordered > tbody > tr > td:first-child,\n .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:last-child,\n .table-responsive > .table-bordered > tbody > tr > th:last-child,\n .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n .table-responsive > .table-bordered > thead > tr > td:last-child,\n .table-responsive > .table-bordered > tbody > tr > td:last-child,\n .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n }\n .table-responsive > .table-bordered > tbody > tr:last-child > th,\n .table-responsive > .table-bordered > tfoot > tr:last-child > th,\n .table-responsive > .table-bordered > tbody > tr:last-child > td,\n .table-responsive > .table-bordered > tfoot > tr:last-child > td {\n border-bottom: 0;\n }\n}\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n min-width: 0;\n}\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: 20px;\n font-size: 21px;\n line-height: inherit;\n color: #333333;\n border: 0;\n border-bottom: 1px solid #e5e5e5;\n}\nlabel {\n display: inline-block;\n max-width: 100%;\n margin-bottom: 5px;\n font-weight: bold;\n}\ninput[type=\"search\"] {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9;\n line-height: normal;\n}\ninput[type=\"file\"] {\n display: block;\n}\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\nselect[multiple],\nselect[size] {\n height: auto;\n}\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n outline: thin dotted;\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\noutput {\n display: block;\n padding-top: 7px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555555;\n}\n.form-control {\n display: block;\n width: 100%;\n height: 34px;\n padding: 6px 12px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555555;\n background-color: #ffffff;\n background-image: none;\n border: 1px solid #cccccc;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n}\n.form-control:focus {\n border-color: #66afe9;\n outline: 0;\n -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n}\n.form-control::-moz-placeholder {\n color: #999999;\n opacity: 1;\n}\n.form-control:-ms-input-placeholder {\n color: #999999;\n}\n.form-control::-webkit-input-placeholder {\n color: #999999;\n}\n.form-control[disabled],\n.form-control[readonly],\nfieldset[disabled] .form-control {\n background-color: #eeeeee;\n opacity: 1;\n}\n.form-control[disabled],\nfieldset[disabled] .form-control {\n cursor: not-allowed;\n}\ntextarea.form-control {\n height: auto;\n}\ninput[type=\"search\"] {\n -webkit-appearance: none;\n}\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n input[type=\"date\"].form-control,\n input[type=\"time\"].form-control,\n input[type=\"datetime-local\"].form-control,\n input[type=\"month\"].form-control {\n line-height: 34px;\n }\n input[type=\"date\"].input-sm,\n input[type=\"time\"].input-sm,\n input[type=\"datetime-local\"].input-sm,\n input[type=\"month\"].input-sm,\n .input-group-sm input[type=\"date\"],\n .input-group-sm input[type=\"time\"],\n .input-group-sm input[type=\"datetime-local\"],\n .input-group-sm input[type=\"month\"] {\n line-height: 30px;\n }\n input[type=\"date\"].input-lg,\n input[type=\"time\"].input-lg,\n input[type=\"datetime-local\"].input-lg,\n input[type=\"month\"].input-lg,\n .input-group-lg input[type=\"date\"],\n .input-group-lg input[type=\"time\"],\n .input-group-lg input[type=\"datetime-local\"],\n .input-group-lg input[type=\"month\"] {\n line-height: 46px;\n }\n}\n.form-group {\n margin-bottom: 15px;\n}\n.radio,\n.checkbox {\n position: relative;\n display: block;\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.radio label,\n.checkbox label {\n min-height: 20px;\n padding-left: 20px;\n margin-bottom: 0;\n font-weight: normal;\n cursor: pointer;\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n position: absolute;\n margin-left: -20px;\n margin-top: 4px \\9;\n}\n.radio + .radio,\n.checkbox + .checkbox {\n margin-top: -5px;\n}\n.radio-inline,\n.checkbox-inline {\n position: relative;\n display: inline-block;\n padding-left: 20px;\n margin-bottom: 0;\n vertical-align: middle;\n font-weight: normal;\n cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n margin-top: 0;\n margin-left: 10px;\n}\ninput[type=\"radio\"][disabled],\ninput[type=\"checkbox\"][disabled],\ninput[type=\"radio\"].disabled,\ninput[type=\"checkbox\"].disabled,\nfieldset[disabled] input[type=\"radio\"],\nfieldset[disabled] input[type=\"checkbox\"] {\n cursor: not-allowed;\n}\n.radio-inline.disabled,\n.checkbox-inline.disabled,\nfieldset[disabled] .radio-inline,\nfieldset[disabled] .checkbox-inline {\n cursor: not-allowed;\n}\n.radio.disabled label,\n.checkbox.disabled label,\nfieldset[disabled] .radio label,\nfieldset[disabled] .checkbox label {\n cursor: not-allowed;\n}\n.form-control-static {\n padding-top: 7px;\n padding-bottom: 7px;\n margin-bottom: 0;\n min-height: 34px;\n}\n.form-control-static.input-lg,\n.form-control-static.input-sm {\n padding-left: 0;\n padding-right: 0;\n}\n.input-sm {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-sm {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-sm,\nselect[multiple].input-sm {\n height: auto;\n}\n.form-group-sm .form-control {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.form-group-sm select.form-control {\n height: 30px;\n line-height: 30px;\n}\n.form-group-sm textarea.form-control,\n.form-group-sm select[multiple].form-control {\n height: auto;\n}\n.form-group-sm .form-control-static {\n height: 30px;\n min-height: 32px;\n padding: 6px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.input-lg {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-lg {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-lg,\nselect[multiple].input-lg {\n height: auto;\n}\n.form-group-lg .form-control {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.form-group-lg select.form-control {\n height: 46px;\n line-height: 46px;\n}\n.form-group-lg textarea.form-control,\n.form-group-lg select[multiple].form-control {\n height: auto;\n}\n.form-group-lg .form-control-static {\n height: 46px;\n min-height: 38px;\n padding: 11px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.has-feedback {\n position: relative;\n}\n.has-feedback .form-control {\n padding-right: 42.5px;\n}\n.form-control-feedback {\n position: absolute;\n top: 0;\n right: 0;\n z-index: 2;\n display: block;\n width: 34px;\n height: 34px;\n line-height: 34px;\n text-align: center;\n pointer-events: none;\n}\n.input-lg + .form-control-feedback,\n.input-group-lg + .form-control-feedback,\n.form-group-lg .form-control + .form-control-feedback {\n width: 46px;\n height: 46px;\n line-height: 46px;\n}\n.input-sm + .form-control-feedback,\n.input-group-sm + .form-control-feedback,\n.form-group-sm .form-control + .form-control-feedback {\n width: 30px;\n height: 30px;\n line-height: 30px;\n}\n.has-success .help-block,\n.has-success .control-label,\n.has-success .radio,\n.has-success .checkbox,\n.has-success .radio-inline,\n.has-success .checkbox-inline,\n.has-success.radio label,\n.has-success.checkbox label,\n.has-success.radio-inline label,\n.has-success.checkbox-inline label {\n color: #3c763d;\n}\n.has-success .form-control {\n border-color: #3c763d;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-success .form-control:focus {\n border-color: #2b542c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n}\n.has-success .input-group-addon {\n color: #3c763d;\n border-color: #3c763d;\n background-color: #dff0d8;\n}\n.has-success .form-control-feedback {\n color: #3c763d;\n}\n.has-warning .help-block,\n.has-warning .control-label,\n.has-warning .radio,\n.has-warning .checkbox,\n.has-warning .radio-inline,\n.has-warning .checkbox-inline,\n.has-warning.radio label,\n.has-warning.checkbox label,\n.has-warning.radio-inline label,\n.has-warning.checkbox-inline label {\n color: #8a6d3b;\n}\n.has-warning .form-control {\n border-color: #8a6d3b;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-warning .form-control:focus {\n border-color: #66512c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n}\n.has-warning .input-group-addon {\n color: #8a6d3b;\n border-color: #8a6d3b;\n background-color: #fcf8e3;\n}\n.has-warning .form-control-feedback {\n color: #8a6d3b;\n}\n.has-error .help-block,\n.has-error .control-label,\n.has-error .radio,\n.has-error .checkbox,\n.has-error .radio-inline,\n.has-error .checkbox-inline,\n.has-error.radio label,\n.has-error.checkbox label,\n.has-error.radio-inline label,\n.has-error.checkbox-inline label {\n color: #a94442;\n}\n.has-error .form-control {\n border-color: #a94442;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-error .form-control:focus {\n border-color: #843534;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n}\n.has-error .input-group-addon {\n color: #a94442;\n border-color: #a94442;\n background-color: #f2dede;\n}\n.has-error .form-control-feedback {\n color: #a94442;\n}\n.has-feedback label ~ .form-control-feedback {\n top: 25px;\n}\n.has-feedback label.sr-only ~ .form-control-feedback {\n top: 0;\n}\n.help-block {\n display: block;\n margin-top: 5px;\n margin-bottom: 10px;\n color: #737373;\n}\n@media (min-width: 768px) {\n .form-inline .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .form-inline .form-control-static {\n display: inline-block;\n }\n .form-inline .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .form-inline .input-group .input-group-addon,\n .form-inline .input-group .input-group-btn,\n .form-inline .input-group .form-control {\n width: auto;\n }\n .form-inline .input-group > .form-control {\n width: 100%;\n }\n .form-inline .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio,\n .form-inline .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio label,\n .form-inline .checkbox label {\n padding-left: 0;\n }\n .form-inline .radio input[type=\"radio\"],\n .form-inline .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .form-inline .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox,\n.form-horizontal .radio-inline,\n.form-horizontal .checkbox-inline {\n margin-top: 0;\n margin-bottom: 0;\n padding-top: 7px;\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox {\n min-height: 27px;\n}\n.form-horizontal .form-group {\n margin-left: -15px;\n margin-right: -15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .control-label {\n text-align: right;\n margin-bottom: 0;\n padding-top: 7px;\n }\n}\n.form-horizontal .has-feedback .form-control-feedback {\n right: 15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-lg .control-label {\n padding-top: 14.333333px;\n font-size: 18px;\n }\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-sm .control-label {\n padding-top: 6px;\n font-size: 12px;\n }\n}\n.btn {\n display: inline-block;\n margin-bottom: 0;\n font-weight: normal;\n text-align: center;\n vertical-align: middle;\n touch-action: manipulation;\n cursor: pointer;\n background-image: none;\n border: 1px solid transparent;\n white-space: nowrap;\n padding: 6px 12px;\n font-size: 14px;\n line-height: 1.42857143;\n border-radius: 4px;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n}\n.btn:focus,\n.btn:active:focus,\n.btn.active:focus,\n.btn.focus,\n.btn:active.focus,\n.btn.active.focus {\n outline: thin dotted;\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n.btn:hover,\n.btn:focus,\n.btn.focus {\n color: #333333;\n text-decoration: none;\n}\n.btn:active,\n.btn.active {\n outline: 0;\n background-image: none;\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn.disabled,\n.btn[disabled],\nfieldset[disabled] .btn {\n cursor: not-allowed;\n opacity: 0.65;\n filter: alpha(opacity=65);\n -webkit-box-shadow: none;\n box-shadow: none;\n}\na.btn.disabled,\nfieldset[disabled] a.btn {\n pointer-events: none;\n}\n.btn-default {\n color: #333333;\n background-color: #ffffff;\n border-color: #cccccc;\n}\n.btn-default:focus,\n.btn-default.focus {\n color: #333333;\n background-color: #e6e6e6;\n border-color: #8c8c8c;\n}\n.btn-default:hover {\n color: #333333;\n background-color: #e6e6e6;\n border-color: #adadad;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n color: #333333;\n background-color: #e6e6e6;\n border-color: #adadad;\n}\n.btn-default:active:hover,\n.btn-default.active:hover,\n.open > .dropdown-toggle.btn-default:hover,\n.btn-default:active:focus,\n.btn-default.active:focus,\n.open > .dropdown-toggle.btn-default:focus,\n.btn-default:active.focus,\n.btn-default.active.focus,\n.open > .dropdown-toggle.btn-default.focus {\n color: #333333;\n background-color: #d4d4d4;\n border-color: #8c8c8c;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n background-image: none;\n}\n.btn-default.disabled,\n.btn-default[disabled],\nfieldset[disabled] .btn-default,\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus,\n.btn-default.disabled:active,\n.btn-default[disabled]:active,\nfieldset[disabled] .btn-default:active,\n.btn-default.disabled.active,\n.btn-default[disabled].active,\nfieldset[disabled] .btn-default.active {\n background-color: #ffffff;\n border-color: #cccccc;\n}\n.btn-default .badge {\n color: #ffffff;\n background-color: #333333;\n}\n.btn-primary {\n color: #ffffff;\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary:focus,\n.btn-primary.focus {\n color: #ffffff;\n background-color: #286090;\n border-color: #122b40;\n}\n.btn-primary:hover {\n color: #ffffff;\n background-color: #286090;\n border-color: #204d74;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n color: #ffffff;\n background-color: #286090;\n border-color: #204d74;\n}\n.btn-primary:active:hover,\n.btn-primary.active:hover,\n.open > .dropdown-toggle.btn-primary:hover,\n.btn-primary:active:focus,\n.btn-primary.active:focus,\n.open > .dropdown-toggle.btn-primary:focus,\n.btn-primary:active.focus,\n.btn-primary.active.focus,\n.open > .dropdown-toggle.btn-primary.focus {\n color: #ffffff;\n background-color: #204d74;\n border-color: #122b40;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n background-image: none;\n}\n.btn-primary.disabled,\n.btn-primary[disabled],\nfieldset[disabled] .btn-primary,\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus,\n.btn-primary.disabled:active,\n.btn-primary[disabled]:active,\nfieldset[disabled] .btn-primary:active,\n.btn-primary.disabled.active,\n.btn-primary[disabled].active,\nfieldset[disabled] .btn-primary.active {\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary .badge {\n color: #337ab7;\n background-color: #ffffff;\n}\n.btn-success {\n color: #ffffff;\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success:focus,\n.btn-success.focus {\n color: #ffffff;\n background-color: #449d44;\n border-color: #255625;\n}\n.btn-success:hover {\n color: #ffffff;\n background-color: #449d44;\n border-color: #398439;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n color: #ffffff;\n background-color: #449d44;\n border-color: #398439;\n}\n.btn-success:active:hover,\n.btn-success.active:hover,\n.open > .dropdown-toggle.btn-success:hover,\n.btn-success:active:focus,\n.btn-success.active:focus,\n.open > .dropdown-toggle.btn-success:focus,\n.btn-success:active.focus,\n.btn-success.active.focus,\n.open > .dropdown-toggle.btn-success.focus {\n color: #ffffff;\n background-color: #398439;\n border-color: #255625;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n background-image: none;\n}\n.btn-success.disabled,\n.btn-success[disabled],\nfieldset[disabled] .btn-success,\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus,\n.btn-success.disabled:active,\n.btn-success[disabled]:active,\nfieldset[disabled] .btn-success:active,\n.btn-success.disabled.active,\n.btn-success[disabled].active,\nfieldset[disabled] .btn-success.active {\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success .badge {\n color: #5cb85c;\n background-color: #ffffff;\n}\n.btn-info {\n color: #ffffff;\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info:focus,\n.btn-info.focus {\n color: #ffffff;\n background-color: #31b0d5;\n border-color: #1b6d85;\n}\n.btn-info:hover {\n color: #ffffff;\n background-color: #31b0d5;\n border-color: #269abc;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n color: #ffffff;\n background-color: #31b0d5;\n border-color: #269abc;\n}\n.btn-info:active:hover,\n.btn-info.active:hover,\n.open > .dropdown-toggle.btn-info:hover,\n.btn-info:active:focus,\n.btn-info.active:focus,\n.open > .dropdown-toggle.btn-info:focus,\n.btn-info:active.focus,\n.btn-info.active.focus,\n.open > .dropdown-toggle.btn-info.focus {\n color: #ffffff;\n background-color: #269abc;\n border-color: #1b6d85;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n background-image: none;\n}\n.btn-info.disabled,\n.btn-info[disabled],\nfieldset[disabled] .btn-info,\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus,\n.btn-info.disabled:active,\n.btn-info[disabled]:active,\nfieldset[disabled] .btn-info:active,\n.btn-info.disabled.active,\n.btn-info[disabled].active,\nfieldset[disabled] .btn-info.active {\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info .badge {\n color: #5bc0de;\n background-color: #ffffff;\n}\n.btn-warning {\n color: #ffffff;\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning:focus,\n.btn-warning.focus {\n color: #ffffff;\n background-color: #ec971f;\n border-color: #985f0d;\n}\n.btn-warning:hover {\n color: #ffffff;\n background-color: #ec971f;\n border-color: #d58512;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n color: #ffffff;\n background-color: #ec971f;\n border-color: #d58512;\n}\n.btn-warning:active:hover,\n.btn-warning.active:hover,\n.open > .dropdown-toggle.btn-warning:hover,\n.btn-warning:active:focus,\n.btn-warning.active:focus,\n.open > .dropdown-toggle.btn-warning:focus,\n.btn-warning:active.focus,\n.btn-warning.active.focus,\n.open > .dropdown-toggle.btn-warning.focus {\n color: #ffffff;\n background-color: #d58512;\n border-color: #985f0d;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n background-image: none;\n}\n.btn-warning.disabled,\n.btn-warning[disabled],\nfieldset[disabled] .btn-warning,\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus,\n.btn-warning.disabled:active,\n.btn-warning[disabled]:active,\nfieldset[disabled] .btn-warning:active,\n.btn-warning.disabled.active,\n.btn-warning[disabled].active,\nfieldset[disabled] .btn-warning.active {\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning .badge {\n color: #f0ad4e;\n background-color: #ffffff;\n}\n.btn-danger {\n color: #ffffff;\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger:focus,\n.btn-danger.focus {\n color: #ffffff;\n background-color: #c9302c;\n border-color: #761c19;\n}\n.btn-danger:hover {\n color: #ffffff;\n background-color: #c9302c;\n border-color: #ac2925;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n color: #ffffff;\n background-color: #c9302c;\n border-color: #ac2925;\n}\n.btn-danger:active:hover,\n.btn-danger.active:hover,\n.open > .dropdown-toggle.btn-danger:hover,\n.btn-danger:active:focus,\n.btn-danger.active:focus,\n.open > .dropdown-toggle.btn-danger:focus,\n.btn-danger:active.focus,\n.btn-danger.active.focus,\n.open > .dropdown-toggle.btn-danger.focus {\n color: #ffffff;\n background-color: #ac2925;\n border-color: #761c19;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n background-image: none;\n}\n.btn-danger.disabled,\n.btn-danger[disabled],\nfieldset[disabled] .btn-danger,\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus,\n.btn-danger.disabled:active,\n.btn-danger[disabled]:active,\nfieldset[disabled] .btn-danger:active,\n.btn-danger.disabled.active,\n.btn-danger[disabled].active,\nfieldset[disabled] .btn-danger.active {\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger .badge {\n color: #d9534f;\n background-color: #ffffff;\n}\n.btn-link {\n color: #337ab7;\n font-weight: normal;\n border-radius: 0;\n}\n.btn-link,\n.btn-link:active,\n.btn-link.active,\n.btn-link[disabled],\nfieldset[disabled] .btn-link {\n background-color: transparent;\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn-link,\n.btn-link:hover,\n.btn-link:focus,\n.btn-link:active {\n border-color: transparent;\n}\n.btn-link:hover,\n.btn-link:focus {\n color: #23527c;\n text-decoration: underline;\n background-color: transparent;\n}\n.btn-link[disabled]:hover,\nfieldset[disabled] .btn-link:hover,\n.btn-link[disabled]:focus,\nfieldset[disabled] .btn-link:focus {\n color: #777777;\n text-decoration: none;\n}\n.btn-lg,\n.btn-group-lg > .btn {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.btn-sm,\n.btn-group-sm > .btn {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-xs,\n.btn-group-xs > .btn {\n padding: 1px 5px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-block {\n display: block;\n width: 100%;\n}\n.btn-block + .btn-block {\n margin-top: 5px;\n}\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n width: 100%;\n}\n.fade {\n opacity: 0;\n -webkit-transition: opacity 0.15s linear;\n -o-transition: opacity 0.15s linear;\n transition: opacity 0.15s linear;\n}\n.fade.in {\n opacity: 1;\n}\n.collapse {\n display: none;\n}\n.collapse.in {\n display: block;\n}\ntr.collapse.in {\n display: table-row;\n}\ntbody.collapse.in {\n display: table-row-group;\n}\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n -webkit-transition-property: height, visibility;\n transition-property: height, visibility;\n -webkit-transition-duration: 0.35s;\n transition-duration: 0.35s;\n -webkit-transition-timing-function: ease;\n transition-timing-function: ease;\n}\n.caret {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 2px;\n vertical-align: middle;\n border-top: 4px dashed;\n border-top: 4px solid \\9;\n border-right: 4px solid transparent;\n border-left: 4px solid transparent;\n}\n.dropup,\n.dropdown {\n position: relative;\n}\n.dropdown-toggle:focus {\n outline: 0;\n}\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1000;\n display: none;\n float: left;\n min-width: 160px;\n padding: 5px 0;\n margin: 2px 0 0;\n list-style: none;\n font-size: 14px;\n text-align: left;\n background-color: #ffffff;\n border: 1px solid #cccccc;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 4px;\n -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n background-clip: padding-box;\n}\n.dropdown-menu.pull-right {\n right: 0;\n left: auto;\n}\n.dropdown-menu .divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.dropdown-menu > li > a {\n display: block;\n padding: 3px 20px;\n clear: both;\n font-weight: normal;\n line-height: 1.42857143;\n color: #333333;\n white-space: nowrap;\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n text-decoration: none;\n color: #262626;\n background-color: #f5f5f5;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n color: #ffffff;\n text-decoration: none;\n outline: 0;\n background-color: #337ab7;\n}\n.dropdown-menu > .disabled > a,\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n color: #777777;\n}\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n text-decoration: none;\n background-color: transparent;\n background-image: none;\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n cursor: not-allowed;\n}\n.open > .dropdown-menu {\n display: block;\n}\n.open > a {\n outline: 0;\n}\n.dropdown-menu-right {\n left: auto;\n right: 0;\n}\n.dropdown-menu-left {\n left: 0;\n right: auto;\n}\n.dropdown-header {\n display: block;\n padding: 3px 20px;\n font-size: 12px;\n line-height: 1.42857143;\n color: #777777;\n white-space: nowrap;\n}\n.dropdown-backdrop {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n top: 0;\n z-index: 990;\n}\n.pull-right > .dropdown-menu {\n right: 0;\n left: auto;\n}\n.dropup .caret,\n.navbar-fixed-bottom .dropdown .caret {\n border-top: 0;\n border-bottom: 4px dashed;\n border-bottom: 4px solid \\9;\n content: \"\";\n}\n.dropup .dropdown-menu,\n.navbar-fixed-bottom .dropdown .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-bottom: 2px;\n}\n@media (min-width: 768px) {\n .navbar-right .dropdown-menu {\n left: auto;\n right: 0;\n }\n .navbar-right .dropdown-menu-left {\n left: 0;\n right: auto;\n }\n}\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-block;\n vertical-align: middle;\n}\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n float: left;\n}\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group-vertical > .btn:focus,\n.btn-group > .btn:active,\n.btn-group-vertical > .btn:active,\n.btn-group > .btn.active,\n.btn-group-vertical > .btn.active {\n z-index: 2;\n}\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group {\n margin-left: -1px;\n}\n.btn-toolbar {\n margin-left: -5px;\n}\n.btn-toolbar .btn,\n.btn-toolbar .btn-group,\n.btn-toolbar .input-group {\n float: left;\n}\n.btn-toolbar > .btn,\n.btn-toolbar > .btn-group,\n.btn-toolbar > .input-group {\n margin-left: 5px;\n}\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n border-radius: 0;\n}\n.btn-group > .btn:first-child {\n margin-left: 0;\n}\n.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group > .btn-group {\n float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n outline: 0;\n}\n.btn-group > .btn + .dropdown-toggle {\n padding-left: 8px;\n padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n padding-left: 12px;\n padding-right: 12px;\n}\n.btn-group.open .dropdown-toggle {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-group.open .dropdown-toggle.btn-link {\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn .caret {\n margin-left: 0;\n}\n.btn-lg .caret {\n border-width: 5px 5px 0;\n border-bottom-width: 0;\n}\n.dropup .btn-lg .caret {\n border-width: 0 5px 5px;\n}\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group,\n.btn-group-vertical > .btn-group > .btn {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n}\n.btn-group-vertical > .btn-group > .btn {\n float: none;\n}\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n}\n.btn-group-vertical > .btn:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.btn-group-vertical > .btn:first-child:not(:last-child) {\n border-top-right-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn:last-child:not(:first-child) {\n border-bottom-left-radius: 4px;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group-justified {\n display: table;\n width: 100%;\n table-layout: fixed;\n border-collapse: separate;\n}\n.btn-group-justified > .btn,\n.btn-group-justified > .btn-group {\n float: none;\n display: table-cell;\n width: 1%;\n}\n.btn-group-justified > .btn-group .btn {\n width: 100%;\n}\n.btn-group-justified > .btn-group .dropdown-menu {\n left: auto;\n}\n[data-toggle=\"buttons\"] > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn input[type=\"checkbox\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n.input-group {\n position: relative;\n display: table;\n border-collapse: separate;\n}\n.input-group[class*=\"col-\"] {\n float: none;\n padding-left: 0;\n padding-right: 0;\n}\n.input-group .form-control {\n position: relative;\n z-index: 2;\n float: left;\n width: 100%;\n margin-bottom: 0;\n}\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-group-lg > .form-control,\nselect.input-group-lg > .input-group-addon,\nselect.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-group-lg > .form-control,\ntextarea.input-group-lg > .input-group-addon,\ntextarea.input-group-lg > .input-group-btn > .btn,\nselect[multiple].input-group-lg > .form-control,\nselect[multiple].input-group-lg > .input-group-addon,\nselect[multiple].input-group-lg > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-group-sm > .form-control,\nselect.input-group-sm > .input-group-addon,\nselect.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-group-sm > .form-control,\ntextarea.input-group-sm > .input-group-addon,\ntextarea.input-group-sm > .input-group-btn > .btn,\nselect[multiple].input-group-sm > .form-control,\nselect[multiple].input-group-sm > .input-group-addon,\nselect[multiple].input-group-sm > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n display: table-cell;\n}\n.input-group-addon:not(:first-child):not(:last-child),\n.input-group-btn:not(:first-child):not(:last-child),\n.input-group .form-control:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.input-group-addon,\n.input-group-btn {\n width: 1%;\n white-space: nowrap;\n vertical-align: middle;\n}\n.input-group-addon {\n padding: 6px 12px;\n font-size: 14px;\n font-weight: normal;\n line-height: 1;\n color: #555555;\n text-align: center;\n background-color: #eeeeee;\n border: 1px solid #cccccc;\n border-radius: 4px;\n}\n.input-group-addon.input-sm {\n padding: 5px 10px;\n font-size: 12px;\n border-radius: 3px;\n}\n.input-group-addon.input-lg {\n padding: 10px 16px;\n font-size: 18px;\n border-radius: 6px;\n}\n.input-group-addon input[type=\"radio\"],\n.input-group-addon input[type=\"checkbox\"] {\n margin-top: 0;\n}\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.input-group-addon:first-child {\n border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.input-group-addon:last-child {\n border-left: 0;\n}\n.input-group-btn {\n position: relative;\n font-size: 0;\n white-space: nowrap;\n}\n.input-group-btn > .btn {\n position: relative;\n}\n.input-group-btn > .btn + .btn {\n margin-left: -1px;\n}\n.input-group-btn > .btn:hover,\n.input-group-btn > .btn:focus,\n.input-group-btn > .btn:active {\n z-index: 2;\n}\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group {\n margin-right: -1px;\n}\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group {\n z-index: 2;\n margin-left: -1px;\n}\n.nav {\n margin-bottom: 0;\n padding-left: 0;\n list-style: none;\n}\n.nav > li {\n position: relative;\n display: block;\n}\n.nav > li > a {\n position: relative;\n display: block;\n padding: 10px 15px;\n}\n.nav > li > a:hover,\n.nav > li > a:focus {\n text-decoration: none;\n background-color: #eeeeee;\n}\n.nav > li.disabled > a {\n color: #777777;\n}\n.nav > li.disabled > a:hover,\n.nav > li.disabled > a:focus {\n color: #777777;\n text-decoration: none;\n background-color: transparent;\n cursor: not-allowed;\n}\n.nav .open > a,\n.nav .open > a:hover,\n.nav .open > a:focus {\n background-color: #eeeeee;\n border-color: #337ab7;\n}\n.nav .nav-divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.nav > li > a > img {\n max-width: none;\n}\n.nav-tabs {\n border-bottom: 1px solid #dddddd;\n}\n.nav-tabs > li {\n float: left;\n margin-bottom: -1px;\n}\n.nav-tabs > li > a {\n margin-right: 2px;\n line-height: 1.42857143;\n border: 1px solid transparent;\n border-radius: 4px 4px 0 0;\n}\n.nav-tabs > li > a:hover {\n border-color: #eeeeee #eeeeee #dddddd;\n}\n.nav-tabs > li.active > a,\n.nav-tabs > li.active > a:hover,\n.nav-tabs > li.active > a:focus {\n color: #555555;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n border-bottom-color: transparent;\n cursor: default;\n}\n.nav-tabs.nav-justified {\n width: 100%;\n border-bottom: 0;\n}\n.nav-tabs.nav-justified > li {\n float: none;\n}\n.nav-tabs.nav-justified > li > a {\n text-align: center;\n margin-bottom: 5px;\n}\n.nav-tabs.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-tabs.nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs.nav-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs.nav-justified > .active > a,\n.nav-tabs.nav-justified > .active > a:hover,\n.nav-tabs.nav-justified > .active > a:focus {\n border: 1px solid #dddddd;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li > a {\n border-bottom: 1px solid #dddddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs.nav-justified > .active > a,\n .nav-tabs.nav-justified > .active > a:hover,\n .nav-tabs.nav-justified > .active > a:focus {\n border-bottom-color: #ffffff;\n }\n}\n.nav-pills > li {\n float: left;\n}\n.nav-pills > li > a {\n border-radius: 4px;\n}\n.nav-pills > li + li {\n margin-left: 2px;\n}\n.nav-pills > li.active > a,\n.nav-pills > li.active > a:hover,\n.nav-pills > li.active > a:focus {\n color: #ffffff;\n background-color: #337ab7;\n}\n.nav-stacked > li {\n float: none;\n}\n.nav-stacked > li + li {\n margin-top: 2px;\n margin-left: 0;\n}\n.nav-justified {\n width: 100%;\n}\n.nav-justified > li {\n float: none;\n}\n.nav-justified > li > a {\n text-align: center;\n margin-bottom: 5px;\n}\n.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs-justified {\n border-bottom: 0;\n}\n.nav-tabs-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs-justified > .active > a,\n.nav-tabs-justified > .active > a:hover,\n.nav-tabs-justified > .active > a:focus {\n border: 1px solid #dddddd;\n}\n@media (min-width: 768px) {\n .nav-tabs-justified > li > a {\n border-bottom: 1px solid #dddddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs-justified > .active > a,\n .nav-tabs-justified > .active > a:hover,\n .nav-tabs-justified > .active > a:focus {\n border-bottom-color: #ffffff;\n }\n}\n.tab-content > .tab-pane {\n display: none;\n}\n.tab-content > .active {\n display: block;\n}\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.navbar {\n position: relative;\n min-height: 50px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n}\n@media (min-width: 768px) {\n .navbar {\n border-radius: 4px;\n }\n}\n@media (min-width: 768px) {\n .navbar-header {\n float: left;\n }\n}\n.navbar-collapse {\n overflow-x: visible;\n padding-right: 15px;\n padding-left: 15px;\n border-top: 1px solid transparent;\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);\n -webkit-overflow-scrolling: touch;\n}\n.navbar-collapse.in {\n overflow-y: auto;\n}\n@media (min-width: 768px) {\n .navbar-collapse {\n width: auto;\n border-top: 0;\n box-shadow: none;\n }\n .navbar-collapse.collapse {\n display: block !important;\n height: auto !important;\n padding-bottom: 0;\n overflow: visible !important;\n }\n .navbar-collapse.in {\n overflow-y: visible;\n }\n .navbar-fixed-top .navbar-collapse,\n .navbar-static-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n padding-left: 0;\n padding-right: 0;\n }\n}\n.navbar-fixed-top .navbar-collapse,\n.navbar-fixed-bottom .navbar-collapse {\n max-height: 340px;\n}\n@media (max-device-width: 480px) and (orientation: landscape) {\n .navbar-fixed-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n max-height: 200px;\n }\n}\n.container > .navbar-header,\n.container-fluid > .navbar-header,\n.container > .navbar-collapse,\n.container-fluid > .navbar-collapse {\n margin-right: -15px;\n margin-left: -15px;\n}\n@media (min-width: 768px) {\n .container > .navbar-header,\n .container-fluid > .navbar-header,\n .container > .navbar-collapse,\n .container-fluid > .navbar-collapse {\n margin-right: 0;\n margin-left: 0;\n }\n}\n.navbar-static-top {\n z-index: 1000;\n border-width: 0 0 1px;\n}\n@media (min-width: 768px) {\n .navbar-static-top {\n border-radius: 0;\n }\n}\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n position: fixed;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n@media (min-width: 768px) {\n .navbar-fixed-top,\n .navbar-fixed-bottom {\n border-radius: 0;\n }\n}\n.navbar-fixed-top {\n top: 0;\n border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n bottom: 0;\n margin-bottom: 0;\n border-width: 1px 0 0;\n}\n.navbar-brand {\n float: left;\n padding: 15px 15px;\n font-size: 18px;\n line-height: 20px;\n height: 50px;\n}\n.navbar-brand:hover,\n.navbar-brand:focus {\n text-decoration: none;\n}\n.navbar-brand > img {\n display: block;\n}\n@media (min-width: 768px) {\n .navbar > .container .navbar-brand,\n .navbar > .container-fluid .navbar-brand {\n margin-left: -15px;\n }\n}\n.navbar-toggle {\n position: relative;\n float: right;\n margin-right: 15px;\n padding: 9px 10px;\n margin-top: 8px;\n margin-bottom: 8px;\n background-color: transparent;\n background-image: none;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.navbar-toggle:focus {\n outline: 0;\n}\n.navbar-toggle .icon-bar {\n display: block;\n width: 22px;\n height: 2px;\n border-radius: 1px;\n}\n.navbar-toggle .icon-bar + .icon-bar {\n margin-top: 4px;\n}\n@media (min-width: 768px) {\n .navbar-toggle {\n display: none;\n }\n}\n.navbar-nav {\n margin: 7.5px -15px;\n}\n.navbar-nav > li > a {\n padding-top: 10px;\n padding-bottom: 10px;\n line-height: 20px;\n}\n@media (max-width: 767px) {\n .navbar-nav .open .dropdown-menu {\n position: static;\n float: none;\n width: auto;\n margin-top: 0;\n background-color: transparent;\n border: 0;\n box-shadow: none;\n }\n .navbar-nav .open .dropdown-menu > li > a,\n .navbar-nav .open .dropdown-menu .dropdown-header {\n padding: 5px 15px 5px 25px;\n }\n .navbar-nav .open .dropdown-menu > li > a {\n line-height: 20px;\n }\n .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-nav .open .dropdown-menu > li > a:focus {\n background-image: none;\n }\n}\n@media (min-width: 768px) {\n .navbar-nav {\n float: left;\n margin: 0;\n }\n .navbar-nav > li {\n float: left;\n }\n .navbar-nav > li > a {\n padding-top: 15px;\n padding-bottom: 15px;\n }\n}\n.navbar-form {\n margin-left: -15px;\n margin-right: -15px;\n padding: 10px 15px;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n margin-top: 8px;\n margin-bottom: 8px;\n}\n@media (min-width: 768px) {\n .navbar-form .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .navbar-form .form-control-static {\n display: inline-block;\n }\n .navbar-form .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .navbar-form .input-group .input-group-addon,\n .navbar-form .input-group .input-group-btn,\n .navbar-form .input-group .form-control {\n width: auto;\n }\n .navbar-form .input-group > .form-control {\n width: 100%;\n }\n .navbar-form .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio,\n .navbar-form .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio label,\n .navbar-form .checkbox label {\n padding-left: 0;\n }\n .navbar-form .radio input[type=\"radio\"],\n .navbar-form .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .navbar-form .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n@media (max-width: 767px) {\n .navbar-form .form-group {\n margin-bottom: 5px;\n }\n .navbar-form .form-group:last-child {\n margin-bottom: 0;\n }\n}\n@media (min-width: 768px) {\n .navbar-form {\n width: auto;\n border: 0;\n margin-left: 0;\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n -webkit-box-shadow: none;\n box-shadow: none;\n }\n}\n.navbar-nav > li > .dropdown-menu {\n margin-top: 0;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n margin-bottom: 0;\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.navbar-btn {\n margin-top: 8px;\n margin-bottom: 8px;\n}\n.navbar-btn.btn-sm {\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.navbar-btn.btn-xs {\n margin-top: 14px;\n margin-bottom: 14px;\n}\n.navbar-text {\n margin-top: 15px;\n margin-bottom: 15px;\n}\n@media (min-width: 768px) {\n .navbar-text {\n float: left;\n margin-left: 15px;\n margin-right: 15px;\n }\n}\n@media (min-width: 768px) {\n .navbar-left {\n float: left !important;\n }\n .navbar-right {\n float: right !important;\n margin-right: -15px;\n }\n .navbar-right ~ .navbar-right {\n margin-right: 0;\n }\n}\n.navbar-default {\n background-color: #f8f8f8;\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-brand {\n color: #777777;\n}\n.navbar-default .navbar-brand:hover,\n.navbar-default .navbar-brand:focus {\n color: #5e5e5e;\n background-color: transparent;\n}\n.navbar-default .navbar-text {\n color: #777777;\n}\n.navbar-default .navbar-nav > li > a {\n color: #777777;\n}\n.navbar-default .navbar-nav > li > a:hover,\n.navbar-default .navbar-nav > li > a:focus {\n color: #333333;\n background-color: transparent;\n}\n.navbar-default .navbar-nav > .active > a,\n.navbar-default .navbar-nav > .active > a:hover,\n.navbar-default .navbar-nav > .active > a:focus {\n color: #555555;\n background-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .disabled > a,\n.navbar-default .navbar-nav > .disabled > a:hover,\n.navbar-default .navbar-nav > .disabled > a:focus {\n color: #cccccc;\n background-color: transparent;\n}\n.navbar-default .navbar-toggle {\n border-color: #dddddd;\n}\n.navbar-default .navbar-toggle:hover,\n.navbar-default .navbar-toggle:focus {\n background-color: #dddddd;\n}\n.navbar-default .navbar-toggle .icon-bar {\n background-color: #888888;\n}\n.navbar-default .navbar-collapse,\n.navbar-default .navbar-form {\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .open > a:hover,\n.navbar-default .navbar-nav > .open > a:focus {\n background-color: #e7e7e7;\n color: #555555;\n}\n@media (max-width: 767px) {\n .navbar-default .navbar-nav .open .dropdown-menu > li > a {\n color: #777777;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #333333;\n background-color: transparent;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #555555;\n background-color: #e7e7e7;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #cccccc;\n background-color: transparent;\n }\n}\n.navbar-default .navbar-link {\n color: #777777;\n}\n.navbar-default .navbar-link:hover {\n color: #333333;\n}\n.navbar-default .btn-link {\n color: #777777;\n}\n.navbar-default .btn-link:hover,\n.navbar-default .btn-link:focus {\n color: #333333;\n}\n.navbar-default .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-default .btn-link:hover,\n.navbar-default .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-default .btn-link:focus {\n color: #cccccc;\n}\n.navbar-inverse {\n background-color: #222222;\n border-color: #080808;\n}\n.navbar-inverse .navbar-brand {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-brand:hover,\n.navbar-inverse .navbar-brand:focus {\n color: #ffffff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-text {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a:hover,\n.navbar-inverse .navbar-nav > li > a:focus {\n color: #ffffff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-nav > .active > a,\n.navbar-inverse .navbar-nav > .active > a:hover,\n.navbar-inverse .navbar-nav > .active > a:focus {\n color: #ffffff;\n background-color: #080808;\n}\n.navbar-inverse .navbar-nav > .disabled > a,\n.navbar-inverse .navbar-nav > .disabled > a:hover,\n.navbar-inverse .navbar-nav > .disabled > a:focus {\n color: #444444;\n background-color: transparent;\n}\n.navbar-inverse .navbar-toggle {\n border-color: #333333;\n}\n.navbar-inverse .navbar-toggle:hover,\n.navbar-inverse .navbar-toggle:focus {\n background-color: #333333;\n}\n.navbar-inverse .navbar-toggle .icon-bar {\n background-color: #ffffff;\n}\n.navbar-inverse .navbar-collapse,\n.navbar-inverse .navbar-form {\n border-color: #101010;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .open > a:hover,\n.navbar-inverse .navbar-nav > .open > a:focus {\n background-color: #080808;\n color: #ffffff;\n}\n@media (max-width: 767px) {\n .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {\n border-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu .divider {\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {\n color: #9d9d9d;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #ffffff;\n background-color: transparent;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #ffffff;\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #444444;\n background-color: transparent;\n }\n}\n.navbar-inverse .navbar-link {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-link:hover {\n color: #ffffff;\n}\n.navbar-inverse .btn-link {\n color: #9d9d9d;\n}\n.navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link:focus {\n color: #ffffff;\n}\n.navbar-inverse .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-inverse .btn-link:focus {\n color: #444444;\n}\n.breadcrumb {\n padding: 8px 15px;\n margin-bottom: 20px;\n list-style: none;\n background-color: #f5f5f5;\n border-radius: 4px;\n}\n.breadcrumb > li {\n display: inline-block;\n}\n.breadcrumb > li + li:before {\n content: \"/\\00a0\";\n padding: 0 5px;\n color: #cccccc;\n}\n.breadcrumb > .active {\n color: #777777;\n}\n.pagination {\n display: inline-block;\n padding-left: 0;\n margin: 20px 0;\n border-radius: 4px;\n}\n.pagination > li {\n display: inline;\n}\n.pagination > li > a,\n.pagination > li > span {\n position: relative;\n float: left;\n padding: 6px 12px;\n line-height: 1.42857143;\n text-decoration: none;\n color: #337ab7;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n margin-left: -1px;\n}\n.pagination > li:first-child > a,\n.pagination > li:first-child > span {\n margin-left: 0;\n border-bottom-left-radius: 4px;\n border-top-left-radius: 4px;\n}\n.pagination > li:last-child > a,\n.pagination > li:last-child > span {\n border-bottom-right-radius: 4px;\n border-top-right-radius: 4px;\n}\n.pagination > li > a:hover,\n.pagination > li > span:hover,\n.pagination > li > a:focus,\n.pagination > li > span:focus {\n z-index: 3;\n color: #23527c;\n background-color: #eeeeee;\n border-color: #dddddd;\n}\n.pagination > .active > a,\n.pagination > .active > span,\n.pagination > .active > a:hover,\n.pagination > .active > span:hover,\n.pagination > .active > a:focus,\n.pagination > .active > span:focus {\n z-index: 2;\n color: #ffffff;\n background-color: #337ab7;\n border-color: #337ab7;\n cursor: default;\n}\n.pagination > .disabled > span,\n.pagination > .disabled > span:hover,\n.pagination > .disabled > span:focus,\n.pagination > .disabled > a,\n.pagination > .disabled > a:hover,\n.pagination > .disabled > a:focus {\n color: #777777;\n background-color: #ffffff;\n border-color: #dddddd;\n cursor: not-allowed;\n}\n.pagination-lg > li > a,\n.pagination-lg > li > span {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.pagination-lg > li:first-child > a,\n.pagination-lg > li:first-child > span {\n border-bottom-left-radius: 6px;\n border-top-left-radius: 6px;\n}\n.pagination-lg > li:last-child > a,\n.pagination-lg > li:last-child > span {\n border-bottom-right-radius: 6px;\n border-top-right-radius: 6px;\n}\n.pagination-sm > li > a,\n.pagination-sm > li > span {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.pagination-sm > li:first-child > a,\n.pagination-sm > li:first-child > span {\n border-bottom-left-radius: 3px;\n border-top-left-radius: 3px;\n}\n.pagination-sm > li:last-child > a,\n.pagination-sm > li:last-child > span {\n border-bottom-right-radius: 3px;\n border-top-right-radius: 3px;\n}\n.pager {\n padding-left: 0;\n margin: 20px 0;\n list-style: none;\n text-align: center;\n}\n.pager li {\n display: inline;\n}\n.pager li > a,\n.pager li > span {\n display: inline-block;\n padding: 5px 14px;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n border-radius: 15px;\n}\n.pager li > a:hover,\n.pager li > a:focus {\n text-decoration: none;\n background-color: #eeeeee;\n}\n.pager .next > a,\n.pager .next > span {\n float: right;\n}\n.pager .previous > a,\n.pager .previous > span {\n float: left;\n}\n.pager .disabled > a,\n.pager .disabled > a:hover,\n.pager .disabled > a:focus,\n.pager .disabled > span {\n color: #777777;\n background-color: #ffffff;\n cursor: not-allowed;\n}\n.label {\n display: inline;\n padding: .2em .6em .3em;\n font-size: 75%;\n font-weight: bold;\n line-height: 1;\n color: #ffffff;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: .25em;\n}\na.label:hover,\na.label:focus {\n color: #ffffff;\n text-decoration: none;\n cursor: pointer;\n}\n.label:empty {\n display: none;\n}\n.btn .label {\n position: relative;\n top: -1px;\n}\n.label-default {\n background-color: #777777;\n}\n.label-default[href]:hover,\n.label-default[href]:focus {\n background-color: #5e5e5e;\n}\n.label-primary {\n background-color: #337ab7;\n}\n.label-primary[href]:hover,\n.label-primary[href]:focus {\n background-color: #286090;\n}\n.label-success {\n background-color: #5cb85c;\n}\n.label-success[href]:hover,\n.label-success[href]:focus {\n background-color: #449d44;\n}\n.label-info {\n background-color: #5bc0de;\n}\n.label-info[href]:hover,\n.label-info[href]:focus {\n background-color: #31b0d5;\n}\n.label-warning {\n background-color: #f0ad4e;\n}\n.label-warning[href]:hover,\n.label-warning[href]:focus {\n background-color: #ec971f;\n}\n.label-danger {\n background-color: #d9534f;\n}\n.label-danger[href]:hover,\n.label-danger[href]:focus {\n background-color: #c9302c;\n}\n.badge {\n display: inline-block;\n min-width: 10px;\n padding: 3px 7px;\n font-size: 12px;\n font-weight: bold;\n color: #ffffff;\n line-height: 1;\n vertical-align: middle;\n white-space: nowrap;\n text-align: center;\n background-color: #777777;\n border-radius: 10px;\n}\n.badge:empty {\n display: none;\n}\n.btn .badge {\n position: relative;\n top: -1px;\n}\n.btn-xs .badge,\n.btn-group-xs > .btn .badge {\n top: 0;\n padding: 1px 5px;\n}\na.badge:hover,\na.badge:focus {\n color: #ffffff;\n text-decoration: none;\n cursor: pointer;\n}\n.list-group-item.active > .badge,\n.nav-pills > .active > a > .badge {\n color: #337ab7;\n background-color: #ffffff;\n}\n.list-group-item > .badge {\n float: right;\n}\n.list-group-item > .badge + .badge {\n margin-right: 5px;\n}\n.nav-pills > li > a > .badge {\n margin-left: 3px;\n}\n.jumbotron {\n padding-top: 30px;\n padding-bottom: 30px;\n margin-bottom: 30px;\n color: inherit;\n background-color: #eeeeee;\n}\n.jumbotron h1,\n.jumbotron .h1 {\n color: inherit;\n}\n.jumbotron p {\n margin-bottom: 15px;\n font-size: 21px;\n font-weight: 200;\n}\n.jumbotron > hr {\n border-top-color: #d5d5d5;\n}\n.container .jumbotron,\n.container-fluid .jumbotron {\n border-radius: 6px;\n}\n.jumbotron .container {\n max-width: 100%;\n}\n@media screen and (min-width: 768px) {\n .jumbotron {\n padding-top: 48px;\n padding-bottom: 48px;\n }\n .container .jumbotron,\n .container-fluid .jumbotron {\n padding-left: 60px;\n padding-right: 60px;\n }\n .jumbotron h1,\n .jumbotron .h1 {\n font-size: 63px;\n }\n}\n.thumbnail {\n display: block;\n padding: 4px;\n margin-bottom: 20px;\n line-height: 1.42857143;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n border-radius: 4px;\n -webkit-transition: border 0.2s ease-in-out;\n -o-transition: border 0.2s ease-in-out;\n transition: border 0.2s ease-in-out;\n}\n.thumbnail > img,\n.thumbnail a > img {\n margin-left: auto;\n margin-right: auto;\n}\na.thumbnail:hover,\na.thumbnail:focus,\na.thumbnail.active {\n border-color: #337ab7;\n}\n.thumbnail .caption {\n padding: 9px;\n color: #333333;\n}\n.alert {\n padding: 15px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.alert h4 {\n margin-top: 0;\n color: inherit;\n}\n.alert .alert-link {\n font-weight: bold;\n}\n.alert > p,\n.alert > ul {\n margin-bottom: 0;\n}\n.alert > p + p {\n margin-top: 5px;\n}\n.alert-dismissable,\n.alert-dismissible {\n padding-right: 35px;\n}\n.alert-dismissable .close,\n.alert-dismissible .close {\n position: relative;\n top: -2px;\n right: -21px;\n color: inherit;\n}\n.alert-success {\n background-color: #dff0d8;\n border-color: #d6e9c6;\n color: #3c763d;\n}\n.alert-success hr {\n border-top-color: #c9e2b3;\n}\n.alert-success .alert-link {\n color: #2b542c;\n}\n.alert-info {\n background-color: #d9edf7;\n border-color: #bce8f1;\n color: #31708f;\n}\n.alert-info hr {\n border-top-color: #a6e1ec;\n}\n.alert-info .alert-link {\n color: #245269;\n}\n.alert-warning {\n background-color: #fcf8e3;\n border-color: #faebcc;\n color: #8a6d3b;\n}\n.alert-warning hr {\n border-top-color: #f7e1b5;\n}\n.alert-warning .alert-link {\n color: #66512c;\n}\n.alert-danger {\n background-color: #f2dede;\n border-color: #ebccd1;\n color: #a94442;\n}\n.alert-danger hr {\n border-top-color: #e4b9c0;\n}\n.alert-danger .alert-link {\n color: #843534;\n}\n@-webkit-keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n@keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n.progress {\n overflow: hidden;\n height: 20px;\n margin-bottom: 20px;\n background-color: #f5f5f5;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n.progress-bar {\n float: left;\n width: 0%;\n height: 100%;\n font-size: 12px;\n line-height: 20px;\n color: #ffffff;\n text-align: center;\n background-color: #337ab7;\n -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n -webkit-transition: width 0.6s ease;\n -o-transition: width 0.6s ease;\n transition: width 0.6s ease;\n}\n.progress-striped .progress-bar,\n.progress-bar-striped {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-size: 40px 40px;\n}\n.progress.active .progress-bar,\n.progress-bar.active {\n -webkit-animation: progress-bar-stripes 2s linear infinite;\n -o-animation: progress-bar-stripes 2s linear infinite;\n animation: progress-bar-stripes 2s linear infinite;\n}\n.progress-bar-success {\n background-color: #5cb85c;\n}\n.progress-striped .progress-bar-success {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-info {\n background-color: #5bc0de;\n}\n.progress-striped .progress-bar-info {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-warning {\n background-color: #f0ad4e;\n}\n.progress-striped .progress-bar-warning {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-danger {\n background-color: #d9534f;\n}\n.progress-striped .progress-bar-danger {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.media {\n margin-top: 15px;\n}\n.media:first-child {\n margin-top: 0;\n}\n.media,\n.media-body {\n zoom: 1;\n overflow: hidden;\n}\n.media-body {\n width: 10000px;\n}\n.media-object {\n display: block;\n}\n.media-object.img-thumbnail {\n max-width: none;\n}\n.media-right,\n.media > .pull-right {\n padding-left: 10px;\n}\n.media-left,\n.media > .pull-left {\n padding-right: 10px;\n}\n.media-left,\n.media-right,\n.media-body {\n display: table-cell;\n vertical-align: top;\n}\n.media-middle {\n vertical-align: middle;\n}\n.media-bottom {\n vertical-align: bottom;\n}\n.media-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.media-list {\n padding-left: 0;\n list-style: none;\n}\n.list-group {\n margin-bottom: 20px;\n padding-left: 0;\n}\n.list-group-item {\n position: relative;\n display: block;\n padding: 10px 15px;\n margin-bottom: -1px;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n}\n.list-group-item:first-child {\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n}\n.list-group-item:last-child {\n margin-bottom: 0;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n}\na.list-group-item,\nbutton.list-group-item {\n color: #555555;\n}\na.list-group-item .list-group-item-heading,\nbutton.list-group-item .list-group-item-heading {\n color: #333333;\n}\na.list-group-item:hover,\nbutton.list-group-item:hover,\na.list-group-item:focus,\nbutton.list-group-item:focus {\n text-decoration: none;\n color: #555555;\n background-color: #f5f5f5;\n}\nbutton.list-group-item {\n width: 100%;\n text-align: left;\n}\n.list-group-item.disabled,\n.list-group-item.disabled:hover,\n.list-group-item.disabled:focus {\n background-color: #eeeeee;\n color: #777777;\n cursor: not-allowed;\n}\n.list-group-item.disabled .list-group-item-heading,\n.list-group-item.disabled:hover .list-group-item-heading,\n.list-group-item.disabled:focus .list-group-item-heading {\n color: inherit;\n}\n.list-group-item.disabled .list-group-item-text,\n.list-group-item.disabled:hover .list-group-item-text,\n.list-group-item.disabled:focus .list-group-item-text {\n color: #777777;\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n z-index: 2;\n color: #ffffff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.list-group-item.active .list-group-item-heading,\n.list-group-item.active:hover .list-group-item-heading,\n.list-group-item.active:focus .list-group-item-heading,\n.list-group-item.active .list-group-item-heading > small,\n.list-group-item.active:hover .list-group-item-heading > small,\n.list-group-item.active:focus .list-group-item-heading > small,\n.list-group-item.active .list-group-item-heading > .small,\n.list-group-item.active:hover .list-group-item-heading > .small,\n.list-group-item.active:focus .list-group-item-heading > .small {\n color: inherit;\n}\n.list-group-item.active .list-group-item-text,\n.list-group-item.active:hover .list-group-item-text,\n.list-group-item.active:focus .list-group-item-text {\n color: #c7ddef;\n}\n.list-group-item-success {\n color: #3c763d;\n background-color: #dff0d8;\n}\na.list-group-item-success,\nbutton.list-group-item-success {\n color: #3c763d;\n}\na.list-group-item-success .list-group-item-heading,\nbutton.list-group-item-success .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-success:hover,\nbutton.list-group-item-success:hover,\na.list-group-item-success:focus,\nbutton.list-group-item-success:focus {\n color: #3c763d;\n background-color: #d0e9c6;\n}\na.list-group-item-success.active,\nbutton.list-group-item-success.active,\na.list-group-item-success.active:hover,\nbutton.list-group-item-success.active:hover,\na.list-group-item-success.active:focus,\nbutton.list-group-item-success.active:focus {\n color: #fff;\n background-color: #3c763d;\n border-color: #3c763d;\n}\n.list-group-item-info {\n color: #31708f;\n background-color: #d9edf7;\n}\na.list-group-item-info,\nbutton.list-group-item-info {\n color: #31708f;\n}\na.list-group-item-info .list-group-item-heading,\nbutton.list-group-item-info .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-info:hover,\nbutton.list-group-item-info:hover,\na.list-group-item-info:focus,\nbutton.list-group-item-info:focus {\n color: #31708f;\n background-color: #c4e3f3;\n}\na.list-group-item-info.active,\nbutton.list-group-item-info.active,\na.list-group-item-info.active:hover,\nbutton.list-group-item-info.active:hover,\na.list-group-item-info.active:focus,\nbutton.list-group-item-info.active:focus {\n color: #fff;\n background-color: #31708f;\n border-color: #31708f;\n}\n.list-group-item-warning {\n color: #8a6d3b;\n background-color: #fcf8e3;\n}\na.list-group-item-warning,\nbutton.list-group-item-warning {\n color: #8a6d3b;\n}\na.list-group-item-warning .list-group-item-heading,\nbutton.list-group-item-warning .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-warning:hover,\nbutton.list-group-item-warning:hover,\na.list-group-item-warning:focus,\nbutton.list-group-item-warning:focus {\n color: #8a6d3b;\n background-color: #faf2cc;\n}\na.list-group-item-warning.active,\nbutton.list-group-item-warning.active,\na.list-group-item-warning.active:hover,\nbutton.list-group-item-warning.active:hover,\na.list-group-item-warning.active:focus,\nbutton.list-group-item-warning.active:focus {\n color: #fff;\n background-color: #8a6d3b;\n border-color: #8a6d3b;\n}\n.list-group-item-danger {\n color: #a94442;\n background-color: #f2dede;\n}\na.list-group-item-danger,\nbutton.list-group-item-danger {\n color: #a94442;\n}\na.list-group-item-danger .list-group-item-heading,\nbutton.list-group-item-danger .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-danger:hover,\nbutton.list-group-item-danger:hover,\na.list-group-item-danger:focus,\nbutton.list-group-item-danger:focus {\n color: #a94442;\n background-color: #ebcccc;\n}\na.list-group-item-danger.active,\nbutton.list-group-item-danger.active,\na.list-group-item-danger.active:hover,\nbutton.list-group-item-danger.active:hover,\na.list-group-item-danger.active:focus,\nbutton.list-group-item-danger.active:focus {\n color: #fff;\n background-color: #a94442;\n border-color: #a94442;\n}\n.list-group-item-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.list-group-item-text {\n margin-bottom: 0;\n line-height: 1.3;\n}\n.panel {\n margin-bottom: 20px;\n background-color: #ffffff;\n border: 1px solid transparent;\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.panel-body {\n padding: 15px;\n}\n.panel-heading {\n padding: 10px 15px;\n border-bottom: 1px solid transparent;\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel-heading > .dropdown .dropdown-toggle {\n color: inherit;\n}\n.panel-title {\n margin-top: 0;\n margin-bottom: 0;\n font-size: 16px;\n color: inherit;\n}\n.panel-title > a,\n.panel-title > small,\n.panel-title > .small,\n.panel-title > small > a,\n.panel-title > .small > a {\n color: inherit;\n}\n.panel-footer {\n padding: 10px 15px;\n background-color: #f5f5f5;\n border-top: 1px solid #dddddd;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .list-group,\n.panel > .panel-collapse > .list-group {\n margin-bottom: 0;\n}\n.panel > .list-group .list-group-item,\n.panel > .panel-collapse > .list-group .list-group-item {\n border-width: 1px 0;\n border-radius: 0;\n}\n.panel > .list-group:first-child .list-group-item:first-child,\n.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child {\n border-top: 0;\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel > .list-group:last-child .list-group-item:last-child,\n.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child {\n border-bottom: 0;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.panel-heading + .list-group .list-group-item:first-child {\n border-top-width: 0;\n}\n.list-group + .panel-footer {\n border-top-width: 0;\n}\n.panel > .table,\n.panel > .table-responsive > .table,\n.panel > .panel-collapse > .table {\n margin-bottom: 0;\n}\n.panel > .table caption,\n.panel > .table-responsive > .table caption,\n.panel > .panel-collapse > .table caption {\n padding-left: 15px;\n padding-right: 15px;\n}\n.panel > .table:first-child,\n.panel > .table-responsive:first-child > .table:first-child {\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child {\n border-top-left-radius: 3px;\n border-top-right-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {\n border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {\n border-top-right-radius: 3px;\n}\n.panel > .table:last-child,\n.panel > .table-responsive:last-child > .table:last-child {\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child {\n border-bottom-left-radius: 3px;\n border-bottom-right-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {\n border-bottom-right-radius: 3px;\n}\n.panel > .panel-body + .table,\n.panel > .panel-body + .table-responsive,\n.panel > .table + .panel-body,\n.panel > .table-responsive + .panel-body {\n border-top: 1px solid #dddddd;\n}\n.panel > .table > tbody:first-child > tr:first-child th,\n.panel > .table > tbody:first-child > tr:first-child td {\n border-top: 0;\n}\n.panel > .table-bordered,\n.panel > .table-responsive > .table-bordered {\n border: 0;\n}\n.panel > .table-bordered > thead > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,\n.panel > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-bordered > thead > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,\n.panel > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-bordered > tfoot > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n}\n.panel > .table-bordered > thead > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,\n.panel > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-bordered > thead > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,\n.panel > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-bordered > tfoot > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n}\n.panel > .table-bordered > thead > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,\n.panel > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-bordered > thead > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,\n.panel > .table-bordered > tbody > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {\n border-bottom: 0;\n}\n.panel > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-bordered > tfoot > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {\n border-bottom: 0;\n}\n.panel > .table-responsive {\n border: 0;\n margin-bottom: 0;\n}\n.panel-group {\n margin-bottom: 20px;\n}\n.panel-group .panel {\n margin-bottom: 0;\n border-radius: 4px;\n}\n.panel-group .panel + .panel {\n margin-top: 5px;\n}\n.panel-group .panel-heading {\n border-bottom: 0;\n}\n.panel-group .panel-heading + .panel-collapse > .panel-body,\n.panel-group .panel-heading + .panel-collapse > .list-group {\n border-top: 1px solid #dddddd;\n}\n.panel-group .panel-footer {\n border-top: 0;\n}\n.panel-group .panel-footer + .panel-collapse .panel-body {\n border-bottom: 1px solid #dddddd;\n}\n.panel-default {\n border-color: #dddddd;\n}\n.panel-default > .panel-heading {\n color: #333333;\n background-color: #f5f5f5;\n border-color: #dddddd;\n}\n.panel-default > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #dddddd;\n}\n.panel-default > .panel-heading .badge {\n color: #f5f5f5;\n background-color: #333333;\n}\n.panel-default > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #dddddd;\n}\n.panel-primary {\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading {\n color: #ffffff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #337ab7;\n}\n.panel-primary > .panel-heading .badge {\n color: #337ab7;\n background-color: #ffffff;\n}\n.panel-primary > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #337ab7;\n}\n.panel-success {\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading {\n color: #3c763d;\n background-color: #dff0d8;\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #d6e9c6;\n}\n.panel-success > .panel-heading .badge {\n color: #dff0d8;\n background-color: #3c763d;\n}\n.panel-success > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #d6e9c6;\n}\n.panel-info {\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading {\n color: #31708f;\n background-color: #d9edf7;\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #bce8f1;\n}\n.panel-info > .panel-heading .badge {\n color: #d9edf7;\n background-color: #31708f;\n}\n.panel-info > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #bce8f1;\n}\n.panel-warning {\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading {\n color: #8a6d3b;\n background-color: #fcf8e3;\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #faebcc;\n}\n.panel-warning > .panel-heading .badge {\n color: #fcf8e3;\n background-color: #8a6d3b;\n}\n.panel-warning > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #faebcc;\n}\n.panel-danger {\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading {\n color: #a94442;\n background-color: #f2dede;\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #ebccd1;\n}\n.panel-danger > .panel-heading .badge {\n color: #f2dede;\n background-color: #a94442;\n}\n.panel-danger > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #ebccd1;\n}\n.embed-responsive {\n position: relative;\n display: block;\n height: 0;\n padding: 0;\n overflow: hidden;\n}\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n height: 100%;\n width: 100%;\n border: 0;\n}\n.embed-responsive-16by9 {\n padding-bottom: 56.25%;\n}\n.embed-responsive-4by3 {\n padding-bottom: 75%;\n}\n.well {\n min-height: 20px;\n padding: 19px;\n margin-bottom: 20px;\n background-color: #f5f5f5;\n border: 1px solid #e3e3e3;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.well blockquote {\n border-color: #ddd;\n border-color: rgba(0, 0, 0, 0.15);\n}\n.well-lg {\n padding: 24px;\n border-radius: 6px;\n}\n.well-sm {\n padding: 9px;\n border-radius: 3px;\n}\n.close {\n float: right;\n font-size: 21px;\n font-weight: bold;\n line-height: 1;\n color: #000000;\n text-shadow: 0 1px 0 #ffffff;\n opacity: 0.2;\n filter: alpha(opacity=20);\n}\n.close:hover,\n.close:focus {\n color: #000000;\n text-decoration: none;\n cursor: pointer;\n opacity: 0.5;\n filter: alpha(opacity=50);\n}\nbutton.close {\n padding: 0;\n cursor: pointer;\n background: transparent;\n border: 0;\n -webkit-appearance: none;\n}\n.modal-open {\n overflow: hidden;\n}\n.modal {\n display: none;\n overflow: hidden;\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1050;\n -webkit-overflow-scrolling: touch;\n outline: 0;\n}\n.modal.fade .modal-dialog {\n -webkit-transform: translate(0, -25%);\n -ms-transform: translate(0, -25%);\n -o-transform: translate(0, -25%);\n transform: translate(0, -25%);\n -webkit-transition: -webkit-transform 0.3s ease-out;\n -moz-transition: -moz-transform 0.3s ease-out;\n -o-transition: -o-transform 0.3s ease-out;\n transition: transform 0.3s ease-out;\n}\n.modal.in .modal-dialog {\n -webkit-transform: translate(0, 0);\n -ms-transform: translate(0, 0);\n -o-transform: translate(0, 0);\n transform: translate(0, 0);\n}\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 10px;\n}\n.modal-content {\n position: relative;\n background-color: #ffffff;\n border: 1px solid #999999;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 6px;\n -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n background-clip: padding-box;\n outline: 0;\n}\n.modal-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1040;\n background-color: #000000;\n}\n.modal-backdrop.fade {\n opacity: 0;\n filter: alpha(opacity=0);\n}\n.modal-backdrop.in {\n opacity: 0.5;\n filter: alpha(opacity=50);\n}\n.modal-header {\n padding: 15px;\n border-bottom: 1px solid #e5e5e5;\n min-height: 16.42857143px;\n}\n.modal-header .close {\n margin-top: -2px;\n}\n.modal-title {\n margin: 0;\n line-height: 1.42857143;\n}\n.modal-body {\n position: relative;\n padding: 15px;\n}\n.modal-footer {\n padding: 15px;\n text-align: right;\n border-top: 1px solid #e5e5e5;\n}\n.modal-footer .btn + .btn {\n margin-left: 5px;\n margin-bottom: 0;\n}\n.modal-footer .btn-group .btn + .btn {\n margin-left: -1px;\n}\n.modal-footer .btn-block + .btn-block {\n margin-left: 0;\n}\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n@media (min-width: 768px) {\n .modal-dialog {\n width: 600px;\n margin: 30px auto;\n }\n .modal-content {\n -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n }\n .modal-sm {\n width: 300px;\n }\n}\n@media (min-width: 992px) {\n .modal-lg {\n width: 900px;\n }\n}\n.tooltip {\n position: absolute;\n z-index: 1070;\n display: block;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n line-break: auto;\n line-height: 1.42857143;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n white-space: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n font-size: 12px;\n opacity: 0;\n filter: alpha(opacity=0);\n}\n.tooltip.in {\n opacity: 0.9;\n filter: alpha(opacity=90);\n}\n.tooltip.top {\n margin-top: -3px;\n padding: 5px 0;\n}\n.tooltip.right {\n margin-left: 3px;\n padding: 0 5px;\n}\n.tooltip.bottom {\n margin-top: 3px;\n padding: 5px 0;\n}\n.tooltip.left {\n margin-left: -3px;\n padding: 0 5px;\n}\n.tooltip-inner {\n max-width: 200px;\n padding: 3px 8px;\n color: #ffffff;\n text-align: center;\n background-color: #000000;\n border-radius: 4px;\n}\n.tooltip-arrow {\n position: absolute;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.tooltip.top .tooltip-arrow {\n bottom: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000000;\n}\n.tooltip.top-left .tooltip-arrow {\n bottom: 0;\n right: 5px;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000000;\n}\n.tooltip.top-right .tooltip-arrow {\n bottom: 0;\n left: 5px;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000000;\n}\n.tooltip.right .tooltip-arrow {\n top: 50%;\n left: 0;\n margin-top: -5px;\n border-width: 5px 5px 5px 0;\n border-right-color: #000000;\n}\n.tooltip.left .tooltip-arrow {\n top: 50%;\n right: 0;\n margin-top: -5px;\n border-width: 5px 0 5px 5px;\n border-left-color: #000000;\n}\n.tooltip.bottom .tooltip-arrow {\n top: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000000;\n}\n.tooltip.bottom-left .tooltip-arrow {\n top: 0;\n right: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000000;\n}\n.tooltip.bottom-right .tooltip-arrow {\n top: 0;\n left: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000000;\n}\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1060;\n display: none;\n max-width: 276px;\n padding: 1px;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n line-break: auto;\n line-height: 1.42857143;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n white-space: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n font-size: 14px;\n background-color: #ffffff;\n background-clip: padding-box;\n border: 1px solid #cccccc;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 6px;\n -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n}\n.popover.top {\n margin-top: -10px;\n}\n.popover.right {\n margin-left: 10px;\n}\n.popover.bottom {\n margin-top: 10px;\n}\n.popover.left {\n margin-left: -10px;\n}\n.popover-title {\n margin: 0;\n padding: 8px 14px;\n font-size: 14px;\n background-color: #f7f7f7;\n border-bottom: 1px solid #ebebeb;\n border-radius: 5px 5px 0 0;\n}\n.popover-content {\n padding: 9px 14px;\n}\n.popover > .arrow,\n.popover > .arrow:after {\n position: absolute;\n display: block;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.popover > .arrow {\n border-width: 11px;\n}\n.popover > .arrow:after {\n border-width: 10px;\n content: \"\";\n}\n.popover.top > .arrow {\n left: 50%;\n margin-left: -11px;\n border-bottom-width: 0;\n border-top-color: #999999;\n border-top-color: rgba(0, 0, 0, 0.25);\n bottom: -11px;\n}\n.popover.top > .arrow:after {\n content: \" \";\n bottom: 1px;\n margin-left: -10px;\n border-bottom-width: 0;\n border-top-color: #ffffff;\n}\n.popover.right > .arrow {\n top: 50%;\n left: -11px;\n margin-top: -11px;\n border-left-width: 0;\n border-right-color: #999999;\n border-right-color: rgba(0, 0, 0, 0.25);\n}\n.popover.right > .arrow:after {\n content: \" \";\n left: 1px;\n bottom: -10px;\n border-left-width: 0;\n border-right-color: #ffffff;\n}\n.popover.bottom > .arrow {\n left: 50%;\n margin-left: -11px;\n border-top-width: 0;\n border-bottom-color: #999999;\n border-bottom-color: rgba(0, 0, 0, 0.25);\n top: -11px;\n}\n.popover.bottom > .arrow:after {\n content: \" \";\n top: 1px;\n margin-left: -10px;\n border-top-width: 0;\n border-bottom-color: #ffffff;\n}\n.popover.left > .arrow {\n top: 50%;\n right: -11px;\n margin-top: -11px;\n border-right-width: 0;\n border-left-color: #999999;\n border-left-color: rgba(0, 0, 0, 0.25);\n}\n.popover.left > .arrow:after {\n content: \" \";\n right: 1px;\n border-right-width: 0;\n border-left-color: #ffffff;\n bottom: -10px;\n}\n.carousel {\n position: relative;\n}\n.carousel-inner {\n position: relative;\n overflow: hidden;\n width: 100%;\n}\n.carousel-inner > .item {\n display: none;\n position: relative;\n -webkit-transition: 0.6s ease-in-out left;\n -o-transition: 0.6s ease-in-out left;\n transition: 0.6s ease-in-out left;\n}\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n line-height: 1;\n}\n@media all and (transform-3d), (-webkit-transform-3d) {\n .carousel-inner > .item {\n -webkit-transition: -webkit-transform 0.6s ease-in-out;\n -moz-transition: -moz-transform 0.6s ease-in-out;\n -o-transition: -o-transform 0.6s ease-in-out;\n transition: transform 0.6s ease-in-out;\n -webkit-backface-visibility: hidden;\n -moz-backface-visibility: hidden;\n backface-visibility: hidden;\n -webkit-perspective: 1000px;\n -moz-perspective: 1000px;\n perspective: 1000px;\n }\n .carousel-inner > .item.next,\n .carousel-inner > .item.active.right {\n -webkit-transform: translate3d(100%, 0, 0);\n transform: translate3d(100%, 0, 0);\n left: 0;\n }\n .carousel-inner > .item.prev,\n .carousel-inner > .item.active.left {\n -webkit-transform: translate3d(-100%, 0, 0);\n transform: translate3d(-100%, 0, 0);\n left: 0;\n }\n .carousel-inner > .item.next.left,\n .carousel-inner > .item.prev.right,\n .carousel-inner > .item.active {\n -webkit-transform: translate3d(0, 0, 0);\n transform: translate3d(0, 0, 0);\n left: 0;\n }\n}\n.carousel-inner > .active,\n.carousel-inner > .next,\n.carousel-inner > .prev {\n display: block;\n}\n.carousel-inner > .active {\n left: 0;\n}\n.carousel-inner > .next,\n.carousel-inner > .prev {\n position: absolute;\n top: 0;\n width: 100%;\n}\n.carousel-inner > .next {\n left: 100%;\n}\n.carousel-inner > .prev {\n left: -100%;\n}\n.carousel-inner > .next.left,\n.carousel-inner > .prev.right {\n left: 0;\n}\n.carousel-inner > .active.left {\n left: -100%;\n}\n.carousel-inner > .active.right {\n left: 100%;\n}\n.carousel-control {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n width: 15%;\n opacity: 0.5;\n filter: alpha(opacity=50);\n font-size: 20px;\n color: #ffffff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n.carousel-control.left {\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);\n}\n.carousel-control.right {\n left: auto;\n right: 0;\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);\n}\n.carousel-control:hover,\n.carousel-control:focus {\n outline: 0;\n color: #ffffff;\n text-decoration: none;\n opacity: 0.9;\n filter: alpha(opacity=90);\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-left,\n.carousel-control .glyphicon-chevron-right {\n position: absolute;\n top: 50%;\n margin-top: -10px;\n z-index: 5;\n display: inline-block;\n}\n.carousel-control .icon-prev,\n.carousel-control .glyphicon-chevron-left {\n left: 50%;\n margin-left: -10px;\n}\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-right {\n right: 50%;\n margin-right: -10px;\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next {\n width: 20px;\n height: 20px;\n line-height: 1;\n font-family: serif;\n}\n.carousel-control .icon-prev:before {\n content: '\\2039';\n}\n.carousel-control .icon-next:before {\n content: '\\203a';\n}\n.carousel-indicators {\n position: absolute;\n bottom: 10px;\n left: 50%;\n z-index: 15;\n width: 60%;\n margin-left: -30%;\n padding-left: 0;\n list-style: none;\n text-align: center;\n}\n.carousel-indicators li {\n display: inline-block;\n width: 10px;\n height: 10px;\n margin: 1px;\n text-indent: -999px;\n border: 1px solid #ffffff;\n border-radius: 10px;\n cursor: pointer;\n background-color: #000 \\9;\n background-color: rgba(0, 0, 0, 0);\n}\n.carousel-indicators .active {\n margin: 0;\n width: 12px;\n height: 12px;\n background-color: #ffffff;\n}\n.carousel-caption {\n position: absolute;\n left: 15%;\n right: 15%;\n bottom: 20px;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: #ffffff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n.carousel-caption .btn {\n text-shadow: none;\n}\n@media screen and (min-width: 768px) {\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-prev,\n .carousel-control .icon-next {\n width: 30px;\n height: 30px;\n margin-top: -15px;\n font-size: 30px;\n }\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .icon-prev {\n margin-left: -15px;\n }\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-next {\n margin-right: -15px;\n }\n .carousel-caption {\n left: 20%;\n right: 20%;\n padding-bottom: 30px;\n }\n .carousel-indicators {\n bottom: 20px;\n }\n}\n.clearfix:before,\n.clearfix:after,\n.dl-horizontal dd:before,\n.dl-horizontal dd:after,\n.container:before,\n.container:after,\n.container-fluid:before,\n.container-fluid:after,\n.row:before,\n.row:after,\n.form-horizontal .form-group:before,\n.form-horizontal .form-group:after,\n.btn-toolbar:before,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:before,\n.btn-group-vertical > .btn-group:after,\n.nav:before,\n.nav:after,\n.navbar:before,\n.navbar:after,\n.navbar-header:before,\n.navbar-header:after,\n.navbar-collapse:before,\n.navbar-collapse:after,\n.pager:before,\n.pager:after,\n.panel-body:before,\n.panel-body:after,\n.modal-footer:before,\n.modal-footer:after {\n content: \" \";\n display: table;\n}\n.clearfix:after,\n.dl-horizontal dd:after,\n.container:after,\n.container-fluid:after,\n.row:after,\n.form-horizontal .form-group:after,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:after,\n.nav:after,\n.navbar:after,\n.navbar-header:after,\n.navbar-collapse:after,\n.pager:after,\n.panel-body:after,\n.modal-footer:after {\n clear: both;\n}\n.center-block {\n display: block;\n margin-left: auto;\n margin-right: auto;\n}\n.pull-right {\n float: right !important;\n}\n.pull-left {\n float: left !important;\n}\n.hide {\n display: none !important;\n}\n.show {\n display: block !important;\n}\n.invisible {\n visibility: hidden;\n}\n.text-hide {\n font: 0/0 a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n.hidden {\n display: none !important;\n}\n.affix {\n position: fixed;\n}\n@-ms-viewport {\n width: device-width;\n}\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n display: none !important;\n}\n.visible-xs-block,\n.visible-xs-inline,\n.visible-xs-inline-block,\n.visible-sm-block,\n.visible-sm-inline,\n.visible-sm-inline-block,\n.visible-md-block,\n.visible-md-inline,\n.visible-md-inline-block,\n.visible-lg-block,\n.visible-lg-inline,\n.visible-lg-inline-block {\n display: none !important;\n}\n@media (max-width: 767px) {\n .visible-xs {\n display: block !important;\n }\n table.visible-xs {\n display: table !important;\n }\n tr.visible-xs {\n display: table-row !important;\n }\n th.visible-xs,\n td.visible-xs {\n display: table-cell !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-block {\n display: block !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline {\n display: inline !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm {\n display: block !important;\n }\n table.visible-sm {\n display: table !important;\n }\n tr.visible-sm {\n display: table-row !important;\n }\n th.visible-sm,\n td.visible-sm {\n display: table-cell !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-block {\n display: block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline {\n display: inline !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md {\n display: block !important;\n }\n table.visible-md {\n display: table !important;\n }\n tr.visible-md {\n display: table-row !important;\n }\n th.visible-md,\n td.visible-md {\n display: table-cell !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-block {\n display: block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline {\n display: inline !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg {\n display: block !important;\n }\n table.visible-lg {\n display: table !important;\n }\n tr.visible-lg {\n display: table-row !important;\n }\n th.visible-lg,\n td.visible-lg {\n display: table-cell !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-block {\n display: block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline {\n display: inline !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline-block {\n display: inline-block !important;\n }\n}\n@media (max-width: 767px) {\n .hidden-xs {\n display: none !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .hidden-sm {\n display: none !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .hidden-md {\n display: none !important;\n }\n}\n@media (min-width: 1200px) {\n .hidden-lg {\n display: none !important;\n }\n}\n.visible-print {\n display: none !important;\n}\n@media print {\n .visible-print {\n display: block !important;\n }\n table.visible-print {\n display: table !important;\n }\n tr.visible-print {\n display: table-row !important;\n }\n th.visible-print,\n td.visible-print {\n display: table-cell !important;\n }\n}\n.visible-print-block {\n display: none !important;\n}\n@media print {\n .visible-print-block {\n display: block !important;\n }\n}\n.visible-print-inline {\n display: none !important;\n}\n@media print {\n .visible-print-inline {\n display: inline !important;\n }\n}\n.visible-print-inline-block {\n display: none !important;\n}\n@media print {\n .visible-print-inline-block {\n display: inline-block !important;\n }\n}\n@media print {\n .hidden-print {\n display: none !important;\n }\n}\n/*# sourceMappingURL=bootstrap.css.map */","/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\n\n//\n// 1. Set default font family to sans-serif.\n// 2. Prevent iOS and IE text size adjust after device orientation change,\n// without disabling user zoom.\n//\n\nhtml {\n font-family: sans-serif; // 1\n -ms-text-size-adjust: 100%; // 2\n -webkit-text-size-adjust: 100%; // 2\n}\n\n//\n// Remove default margin.\n//\n\nbody {\n margin: 0;\n}\n\n// HTML5 display definitions\n// ==========================================================================\n\n//\n// Correct `block` display not defined for any HTML5 element in IE 8/9.\n// Correct `block` display not defined for `details` or `summary` in IE 10/11\n// and Firefox.\n// Correct `block` display not defined for `main` in IE 11.\n//\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\n\n//\n// 1. Correct `inline-block` display not defined in IE 8/9.\n// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n//\n\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block; // 1\n vertical-align: baseline; // 2\n}\n\n//\n// Prevent modern browsers from displaying `audio` without controls.\n// Remove excess height in iOS 5 devices.\n//\n\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n\n//\n// Address `[hidden]` styling not present in IE 8/9/10.\n// Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.\n//\n\n[hidden],\ntemplate {\n display: none;\n}\n\n// Links\n// ==========================================================================\n\n//\n// Remove the gray background color from active links in IE 10.\n//\n\na {\n background-color: transparent;\n}\n\n//\n// Improve readability of focused elements when they are also in an\n// active/hover state.\n//\n\na:active,\na:hover {\n outline: 0;\n}\n\n// Text-level semantics\n// ==========================================================================\n\n//\n// Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n//\n\nabbr[title] {\n border-bottom: 1px dotted;\n}\n\n//\n// Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n//\n\nb,\nstrong {\n font-weight: bold;\n}\n\n//\n// Address styling not present in Safari and Chrome.\n//\n\ndfn {\n font-style: italic;\n}\n\n//\n// Address variable `h1` font-size and margin within `section` and `article`\n// contexts in Firefox 4+, Safari, and Chrome.\n//\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n//\n// Address styling not present in IE 8/9.\n//\n\nmark {\n background: #ff0;\n color: #000;\n}\n\n//\n// Address inconsistent and variable font size in all browsers.\n//\n\nsmall {\n font-size: 80%;\n}\n\n//\n// Prevent `sub` and `sup` affecting `line-height` in all browsers.\n//\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsup {\n top: -0.5em;\n}\n\nsub {\n bottom: -0.25em;\n}\n\n// Embedded content\n// ==========================================================================\n\n//\n// Remove border when inside `a` element in IE 8/9/10.\n//\n\nimg {\n border: 0;\n}\n\n//\n// Correct overflow not hidden in IE 9/10/11.\n//\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\n// Grouping content\n// ==========================================================================\n\n//\n// Address margin not present in IE 8/9 and Safari.\n//\n\nfigure {\n margin: 1em 40px;\n}\n\n//\n// Address differences between Firefox and other browsers.\n//\n\nhr {\n box-sizing: content-box;\n height: 0;\n}\n\n//\n// Contain overflow in all browsers.\n//\n\npre {\n overflow: auto;\n}\n\n//\n// Address odd `em`-unit font size rendering in all browsers.\n//\n\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\n\n// Forms\n// ==========================================================================\n\n//\n// Known limitation: by default, Chrome and Safari on OS X allow very limited\n// styling of `select`, unless a `border` property is set.\n//\n\n//\n// 1. Correct color not being inherited.\n// Known issue: affects color of disabled elements.\n// 2. Correct font properties not being inherited.\n// 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n//\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit; // 1\n font: inherit; // 2\n margin: 0; // 3\n}\n\n//\n// Address `overflow` set to `hidden` in IE 8/9/10/11.\n//\n\nbutton {\n overflow: visible;\n}\n\n//\n// Address inconsistent `text-transform` inheritance for `button` and `select`.\n// All other form control elements do not inherit `text-transform` values.\n// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n// Correct `select` style inheritance in Firefox.\n//\n\nbutton,\nselect {\n text-transform: none;\n}\n\n//\n// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n// and `video` controls.\n// 2. Correct inability to style clickable `input` types in iOS.\n// 3. Improve usability and consistency of cursor style between image-type\n// `input` and others.\n//\n\nbutton,\nhtml input[type=\"button\"], // 1\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button; // 2\n cursor: pointer; // 3\n}\n\n//\n// Re-set default cursor for disabled elements.\n//\n\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\n\n//\n// Remove inner padding and border in Firefox 4+.\n//\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\n\n//\n// Address Firefox 4+ setting `line-height` on `input` using `!important` in\n// the UA stylesheet.\n//\n\ninput {\n line-height: normal;\n}\n\n//\n// It's recommended that you don't attempt to style these elements.\n// Firefox's implementation doesn't respect box-sizing, padding, or width.\n//\n// 1. Address box sizing set to `content-box` in IE 8/9/10.\n// 2. Remove excess padding in IE 8/9/10.\n//\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box; // 1\n padding: 0; // 2\n}\n\n//\n// Fix the cursor style for Chrome's increment/decrement buttons. For certain\n// `font-size` values of the `input`, it causes the cursor style of the\n// decrement button to change from `default` to `text`.\n//\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n//\n// 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n// 2. Address `box-sizing` set to `border-box` in Safari and Chrome.\n//\n\ninput[type=\"search\"] {\n -webkit-appearance: textfield; // 1\n box-sizing: content-box; //2\n}\n\n//\n// Remove inner padding and search cancel button in Safari and Chrome on OS X.\n// Safari (but not Chrome) clips the cancel button when the search input has\n// padding (and `textfield` appearance).\n//\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// Define consistent border, margin, and padding.\n//\n\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\n\n//\n// 1. Correct `color` not being inherited in IE 8/9/10/11.\n// 2. Remove padding so people aren't caught out if they zero out fieldsets.\n//\n\nlegend {\n border: 0; // 1\n padding: 0; // 2\n}\n\n//\n// Remove default vertical scrollbar in IE 8/9/10/11.\n//\n\ntextarea {\n overflow: auto;\n}\n\n//\n// Don't inherit the `font-weight` (applied by a rule above).\n// NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n//\n\noptgroup {\n font-weight: bold;\n}\n\n// Tables\n// ==========================================================================\n\n//\n// Remove most spacing between table cells.\n//\n\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\n\ntd,\nth {\n padding: 0;\n}\n","/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n\n// ==========================================================================\n// Print styles.\n// Inlined to avoid the additional HTTP request: h5bp.com/r\n// ==========================================================================\n\n@media print {\n *,\n *:before,\n *:after {\n background: transparent !important;\n color: #000 !important; // Black prints faster: h5bp.com/s\n box-shadow: none !important;\n text-shadow: none !important;\n }\n\n a,\n a:visited {\n text-decoration: underline;\n }\n\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n\n // Don't show links that are fragment identifiers,\n // or use the `javascript:` pseudo protocol\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\";\n }\n\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n\n thead {\n display: table-header-group; // h5bp.com/t\n }\n\n tr,\n img {\n page-break-inside: avoid;\n }\n\n img {\n max-width: 100% !important;\n }\n\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n\n h2,\n h3 {\n page-break-after: avoid;\n }\n\n // Bootstrap specific changes start\n\n // Bootstrap components\n .navbar {\n display: none;\n }\n .btn,\n .dropup > .btn {\n > .caret {\n border-top-color: #000 !important;\n }\n }\n .label {\n border: 1px solid #000;\n }\n\n .table {\n border-collapse: collapse !important;\n\n td,\n th {\n background-color: #fff !important;\n }\n }\n .table-bordered {\n th,\n td {\n border: 1px solid #ddd !important;\n }\n }\n\n // Bootstrap specific changes end\n}\n","//\n// Glyphicons for Bootstrap\n//\n// Since icons are fonts, they can be placed anywhere text is placed and are\n// thus automatically sized to match the surrounding child. To use, create an\n// inline element with the appropriate classes, like so:\n//\n// Star\n\n// Import the fonts\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('@{icon-font-path}@{icon-font-name}.eot');\n src: url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype'),\n url('@{icon-font-path}@{icon-font-name}.woff2') format('woff2'),\n url('@{icon-font-path}@{icon-font-name}.woff') format('woff'),\n url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype'),\n url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg');\n}\n\n// Catchall baseclass\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n// Individual icons\n.glyphicon-asterisk { &:before { content: \"\\2a\"; } }\n.glyphicon-plus { &:before { content: \"\\2b\"; } }\n.glyphicon-euro,\n.glyphicon-eur { &:before { content: \"\\20ac\"; } }\n.glyphicon-minus { &:before { content: \"\\2212\"; } }\n.glyphicon-cloud { &:before { content: \"\\2601\"; } }\n.glyphicon-envelope { &:before { content: \"\\2709\"; } }\n.glyphicon-pencil { &:before { content: \"\\270f\"; } }\n.glyphicon-glass { &:before { content: \"\\e001\"; } }\n.glyphicon-music { &:before { content: \"\\e002\"; } }\n.glyphicon-search { &:before { content: \"\\e003\"; } }\n.glyphicon-heart { &:before { content: \"\\e005\"; } }\n.glyphicon-star { &:before { content: \"\\e006\"; } }\n.glyphicon-star-empty { &:before { content: \"\\e007\"; } }\n.glyphicon-user { &:before { content: \"\\e008\"; } }\n.glyphicon-film { &:before { content: \"\\e009\"; } }\n.glyphicon-th-large { &:before { content: \"\\e010\"; } }\n.glyphicon-th { &:before { content: \"\\e011\"; } }\n.glyphicon-th-list { &:before { content: \"\\e012\"; } }\n.glyphicon-ok { &:before { content: \"\\e013\"; } }\n.glyphicon-remove { &:before { content: \"\\e014\"; } }\n.glyphicon-zoom-in { &:before { content: \"\\e015\"; } }\n.glyphicon-zoom-out { &:before { content: \"\\e016\"; } }\n.glyphicon-off { &:before { content: \"\\e017\"; } }\n.glyphicon-signal { &:before { content: \"\\e018\"; } }\n.glyphicon-cog { &:before { content: \"\\e019\"; } }\n.glyphicon-trash { &:before { content: \"\\e020\"; } }\n.glyphicon-home { &:before { content: \"\\e021\"; } }\n.glyphicon-file { &:before { content: \"\\e022\"; } }\n.glyphicon-time { &:before { content: \"\\e023\"; } }\n.glyphicon-road { &:before { content: \"\\e024\"; } }\n.glyphicon-download-alt { &:before { content: \"\\e025\"; } }\n.glyphicon-download { &:before { content: \"\\e026\"; } }\n.glyphicon-upload { &:before { content: \"\\e027\"; } }\n.glyphicon-inbox { &:before { content: \"\\e028\"; } }\n.glyphicon-play-circle { &:before { content: \"\\e029\"; } }\n.glyphicon-repeat { &:before { content: \"\\e030\"; } }\n.glyphicon-refresh { &:before { content: \"\\e031\"; } }\n.glyphicon-list-alt { &:before { content: \"\\e032\"; } }\n.glyphicon-lock { &:before { content: \"\\e033\"; } }\n.glyphicon-flag { &:before { content: \"\\e034\"; } }\n.glyphicon-headphones { &:before { content: \"\\e035\"; } }\n.glyphicon-volume-off { &:before { content: \"\\e036\"; } }\n.glyphicon-volume-down { &:before { content: \"\\e037\"; } }\n.glyphicon-volume-up { &:before { content: \"\\e038\"; } }\n.glyphicon-qrcode { &:before { content: \"\\e039\"; } }\n.glyphicon-barcode { &:before { content: \"\\e040\"; } }\n.glyphicon-tag { &:before { content: \"\\e041\"; } }\n.glyphicon-tags { &:before { content: \"\\e042\"; } }\n.glyphicon-book { &:before { content: \"\\e043\"; } }\n.glyphicon-bookmark { &:before { content: \"\\e044\"; } }\n.glyphicon-print { &:before { content: \"\\e045\"; } }\n.glyphicon-camera { &:before { content: \"\\e046\"; } }\n.glyphicon-font { &:before { content: \"\\e047\"; } }\n.glyphicon-bold { &:before { content: \"\\e048\"; } }\n.glyphicon-italic { &:before { content: \"\\e049\"; } }\n.glyphicon-text-height { &:before { content: \"\\e050\"; } }\n.glyphicon-text-width { &:before { content: \"\\e051\"; } }\n.glyphicon-align-left { &:before { content: \"\\e052\"; } }\n.glyphicon-align-center { &:before { content: \"\\e053\"; } }\n.glyphicon-align-right { &:before { content: \"\\e054\"; } }\n.glyphicon-align-justify { &:before { content: \"\\e055\"; } }\n.glyphicon-list { &:before { content: \"\\e056\"; } }\n.glyphicon-indent-left { &:before { content: \"\\e057\"; } }\n.glyphicon-indent-right { &:before { content: \"\\e058\"; } }\n.glyphicon-facetime-video { &:before { content: \"\\e059\"; } }\n.glyphicon-picture { &:before { content: \"\\e060\"; } }\n.glyphicon-map-marker { &:before { content: \"\\e062\"; } }\n.glyphicon-adjust { &:before { content: \"\\e063\"; } }\n.glyphicon-tint { &:before { content: \"\\e064\"; } }\n.glyphicon-edit { &:before { content: \"\\e065\"; } }\n.glyphicon-share { &:before { content: \"\\e066\"; } }\n.glyphicon-check { &:before { content: \"\\e067\"; } }\n.glyphicon-move { &:before { content: \"\\e068\"; } }\n.glyphicon-step-backward { &:before { content: \"\\e069\"; } }\n.glyphicon-fast-backward { &:before { content: \"\\e070\"; } }\n.glyphicon-backward { &:before { content: \"\\e071\"; } }\n.glyphicon-play { &:before { content: \"\\e072\"; } }\n.glyphicon-pause { &:before { content: \"\\e073\"; } }\n.glyphicon-stop { &:before { content: \"\\e074\"; } }\n.glyphicon-forward { &:before { content: \"\\e075\"; } }\n.glyphicon-fast-forward { &:before { content: \"\\e076\"; } }\n.glyphicon-step-forward { &:before { content: \"\\e077\"; } }\n.glyphicon-eject { &:before { content: \"\\e078\"; } }\n.glyphicon-chevron-left { &:before { content: \"\\e079\"; } }\n.glyphicon-chevron-right { &:before { content: \"\\e080\"; } }\n.glyphicon-plus-sign { &:before { content: \"\\e081\"; } }\n.glyphicon-minus-sign { &:before { content: \"\\e082\"; } }\n.glyphicon-remove-sign { &:before { content: \"\\e083\"; } }\n.glyphicon-ok-sign { &:before { content: \"\\e084\"; } }\n.glyphicon-question-sign { &:before { content: \"\\e085\"; } }\n.glyphicon-info-sign { &:before { content: \"\\e086\"; } }\n.glyphicon-screenshot { &:before { content: \"\\e087\"; } }\n.glyphicon-remove-circle { &:before { content: \"\\e088\"; } }\n.glyphicon-ok-circle { &:before { content: \"\\e089\"; } }\n.glyphicon-ban-circle { &:before { content: \"\\e090\"; } }\n.glyphicon-arrow-left { &:before { content: \"\\e091\"; } }\n.glyphicon-arrow-right { &:before { content: \"\\e092\"; } }\n.glyphicon-arrow-up { &:before { content: \"\\e093\"; } }\n.glyphicon-arrow-down { &:before { content: \"\\e094\"; } }\n.glyphicon-share-alt { &:before { content: \"\\e095\"; } }\n.glyphicon-resize-full { &:before { content: \"\\e096\"; } }\n.glyphicon-resize-small { &:before { content: \"\\e097\"; } }\n.glyphicon-exclamation-sign { &:before { content: \"\\e101\"; } }\n.glyphicon-gift { &:before { content: \"\\e102\"; } }\n.glyphicon-leaf { &:before { content: \"\\e103\"; } }\n.glyphicon-fire { &:before { content: \"\\e104\"; } }\n.glyphicon-eye-open { &:before { content: \"\\e105\"; } }\n.glyphicon-eye-close { &:before { content: \"\\e106\"; } }\n.glyphicon-warning-sign { &:before { content: \"\\e107\"; } }\n.glyphicon-plane { &:before { content: \"\\e108\"; } }\n.glyphicon-calendar { &:before { content: \"\\e109\"; } }\n.glyphicon-random { &:before { content: \"\\e110\"; } }\n.glyphicon-comment { &:before { content: \"\\e111\"; } }\n.glyphicon-magnet { &:before { content: \"\\e112\"; } }\n.glyphicon-chevron-up { &:before { content: \"\\e113\"; } }\n.glyphicon-chevron-down { &:before { content: \"\\e114\"; } }\n.glyphicon-retweet { &:before { content: \"\\e115\"; } }\n.glyphicon-shopping-cart { &:before { content: \"\\e116\"; } }\n.glyphicon-folder-close { &:before { content: \"\\e117\"; } }\n.glyphicon-folder-open { &:before { content: \"\\e118\"; } }\n.glyphicon-resize-vertical { &:before { content: \"\\e119\"; } }\n.glyphicon-resize-horizontal { &:before { content: \"\\e120\"; } }\n.glyphicon-hdd { &:before { content: \"\\e121\"; } }\n.glyphicon-bullhorn { &:before { content: \"\\e122\"; } }\n.glyphicon-bell { &:before { content: \"\\e123\"; } }\n.glyphicon-certificate { &:before { content: \"\\e124\"; } }\n.glyphicon-thumbs-up { &:before { content: \"\\e125\"; } }\n.glyphicon-thumbs-down { &:before { content: \"\\e126\"; } }\n.glyphicon-hand-right { &:before { content: \"\\e127\"; } }\n.glyphicon-hand-left { &:before { content: \"\\e128\"; } }\n.glyphicon-hand-up { &:before { content: \"\\e129\"; } }\n.glyphicon-hand-down { &:before { content: \"\\e130\"; } }\n.glyphicon-circle-arrow-right { &:before { content: \"\\e131\"; } }\n.glyphicon-circle-arrow-left { &:before { content: \"\\e132\"; } }\n.glyphicon-circle-arrow-up { &:before { content: \"\\e133\"; } }\n.glyphicon-circle-arrow-down { &:before { content: \"\\e134\"; } }\n.glyphicon-globe { &:before { content: \"\\e135\"; } }\n.glyphicon-wrench { &:before { content: \"\\e136\"; } }\n.glyphicon-tasks { &:before { content: \"\\e137\"; } }\n.glyphicon-filter { &:before { content: \"\\e138\"; } }\n.glyphicon-briefcase { &:before { content: \"\\e139\"; } }\n.glyphicon-fullscreen { &:before { content: \"\\e140\"; } }\n.glyphicon-dashboard { &:before { content: \"\\e141\"; } }\n.glyphicon-paperclip { &:before { content: \"\\e142\"; } }\n.glyphicon-heart-empty { &:before { content: \"\\e143\"; } }\n.glyphicon-link { &:before { content: \"\\e144\"; } }\n.glyphicon-phone { &:before { content: \"\\e145\"; } }\n.glyphicon-pushpin { &:before { content: \"\\e146\"; } }\n.glyphicon-usd { &:before { content: \"\\e148\"; } }\n.glyphicon-gbp { &:before { content: \"\\e149\"; } }\n.glyphicon-sort { &:before { content: \"\\e150\"; } }\n.glyphicon-sort-by-alphabet { &:before { content: \"\\e151\"; } }\n.glyphicon-sort-by-alphabet-alt { &:before { content: \"\\e152\"; } }\n.glyphicon-sort-by-order { &:before { content: \"\\e153\"; } }\n.glyphicon-sort-by-order-alt { &:before { content: \"\\e154\"; } }\n.glyphicon-sort-by-attributes { &:before { content: \"\\e155\"; } }\n.glyphicon-sort-by-attributes-alt { &:before { content: \"\\e156\"; } }\n.glyphicon-unchecked { &:before { content: \"\\e157\"; } }\n.glyphicon-expand { &:before { content: \"\\e158\"; } }\n.glyphicon-collapse-down { &:before { content: \"\\e159\"; } }\n.glyphicon-collapse-up { &:before { content: \"\\e160\"; } }\n.glyphicon-log-in { &:before { content: \"\\e161\"; } }\n.glyphicon-flash { &:before { content: \"\\e162\"; } }\n.glyphicon-log-out { &:before { content: \"\\e163\"; } }\n.glyphicon-new-window { &:before { content: \"\\e164\"; } }\n.glyphicon-record { &:before { content: \"\\e165\"; } }\n.glyphicon-save { &:before { content: \"\\e166\"; } }\n.glyphicon-open { &:before { content: \"\\e167\"; } }\n.glyphicon-saved { &:before { content: \"\\e168\"; } }\n.glyphicon-import { &:before { content: \"\\e169\"; } }\n.glyphicon-export { &:before { content: \"\\e170\"; } }\n.glyphicon-send { &:before { content: \"\\e171\"; } }\n.glyphicon-floppy-disk { &:before { content: \"\\e172\"; } }\n.glyphicon-floppy-saved { &:before { content: \"\\e173\"; } }\n.glyphicon-floppy-remove { &:before { content: \"\\e174\"; } }\n.glyphicon-floppy-save { &:before { content: \"\\e175\"; } }\n.glyphicon-floppy-open { &:before { content: \"\\e176\"; } }\n.glyphicon-credit-card { &:before { content: \"\\e177\"; } }\n.glyphicon-transfer { &:before { content: \"\\e178\"; } }\n.glyphicon-cutlery { &:before { content: \"\\e179\"; } }\n.glyphicon-header { &:before { content: \"\\e180\"; } }\n.glyphicon-compressed { &:before { content: \"\\e181\"; } }\n.glyphicon-earphone { &:before { content: \"\\e182\"; } }\n.glyphicon-phone-alt { &:before { content: \"\\e183\"; } }\n.glyphicon-tower { &:before { content: \"\\e184\"; } }\n.glyphicon-stats { &:before { content: \"\\e185\"; } }\n.glyphicon-sd-video { &:before { content: \"\\e186\"; } }\n.glyphicon-hd-video { &:before { content: \"\\e187\"; } }\n.glyphicon-subtitles { &:before { content: \"\\e188\"; } }\n.glyphicon-sound-stereo { &:before { content: \"\\e189\"; } }\n.glyphicon-sound-dolby { &:before { content: \"\\e190\"; } }\n.glyphicon-sound-5-1 { &:before { content: \"\\e191\"; } }\n.glyphicon-sound-6-1 { &:before { content: \"\\e192\"; } }\n.glyphicon-sound-7-1 { &:before { content: \"\\e193\"; } }\n.glyphicon-copyright-mark { &:before { content: \"\\e194\"; } }\n.glyphicon-registration-mark { &:before { content: \"\\e195\"; } }\n.glyphicon-cloud-download { &:before { content: \"\\e197\"; } }\n.glyphicon-cloud-upload { &:before { content: \"\\e198\"; } }\n.glyphicon-tree-conifer { &:before { content: \"\\e199\"; } }\n.glyphicon-tree-deciduous { &:before { content: \"\\e200\"; } }\n.glyphicon-cd { &:before { content: \"\\e201\"; } }\n.glyphicon-save-file { &:before { content: \"\\e202\"; } }\n.glyphicon-open-file { &:before { content: \"\\e203\"; } }\n.glyphicon-level-up { &:before { content: \"\\e204\"; } }\n.glyphicon-copy { &:before { content: \"\\e205\"; } }\n.glyphicon-paste { &:before { content: \"\\e206\"; } }\n// The following 2 Glyphicons are omitted for the time being because\n// they currently use Unicode codepoints that are outside the\n// Basic Multilingual Plane (BMP). Older buggy versions of WebKit can't handle\n// non-BMP codepoints in CSS string escapes, and thus can't display these two icons.\n// Notably, the bug affects some older versions of the Android Browser.\n// More info: https://github.com/twbs/bootstrap/issues/10106\n// .glyphicon-door { &:before { content: \"\\1f6aa\"; } }\n// .glyphicon-key { &:before { content: \"\\1f511\"; } }\n.glyphicon-alert { &:before { content: \"\\e209\"; } }\n.glyphicon-equalizer { &:before { content: \"\\e210\"; } }\n.glyphicon-king { &:before { content: \"\\e211\"; } }\n.glyphicon-queen { &:before { content: \"\\e212\"; } }\n.glyphicon-pawn { &:before { content: \"\\e213\"; } }\n.glyphicon-bishop { &:before { content: \"\\e214\"; } }\n.glyphicon-knight { &:before { content: \"\\e215\"; } }\n.glyphicon-baby-formula { &:before { content: \"\\e216\"; } }\n.glyphicon-tent { &:before { content: \"\\26fa\"; } }\n.glyphicon-blackboard { &:before { content: \"\\e218\"; } }\n.glyphicon-bed { &:before { content: \"\\e219\"; } }\n.glyphicon-apple { &:before { content: \"\\f8ff\"; } }\n.glyphicon-erase { &:before { content: \"\\e221\"; } }\n.glyphicon-hourglass { &:before { content: \"\\231b\"; } }\n.glyphicon-lamp { &:before { content: \"\\e223\"; } }\n.glyphicon-duplicate { &:before { content: \"\\e224\"; } }\n.glyphicon-piggy-bank { &:before { content: \"\\e225\"; } }\n.glyphicon-scissors { &:before { content: \"\\e226\"; } }\n.glyphicon-bitcoin { &:before { content: \"\\e227\"; } }\n.glyphicon-btc { &:before { content: \"\\e227\"; } }\n.glyphicon-xbt { &:before { content: \"\\e227\"; } }\n.glyphicon-yen { &:before { content: \"\\00a5\"; } }\n.glyphicon-jpy { &:before { content: \"\\00a5\"; } }\n.glyphicon-ruble { &:before { content: \"\\20bd\"; } }\n.glyphicon-rub { &:before { content: \"\\20bd\"; } }\n.glyphicon-scale { &:before { content: \"\\e230\"; } }\n.glyphicon-ice-lolly { &:before { content: \"\\e231\"; } }\n.glyphicon-ice-lolly-tasted { &:before { content: \"\\e232\"; } }\n.glyphicon-education { &:before { content: \"\\e233\"; } }\n.glyphicon-option-horizontal { &:before { content: \"\\e234\"; } }\n.glyphicon-option-vertical { &:before { content: \"\\e235\"; } }\n.glyphicon-menu-hamburger { &:before { content: \"\\e236\"; } }\n.glyphicon-modal-window { &:before { content: \"\\e237\"; } }\n.glyphicon-oil { &:before { content: \"\\e238\"; } }\n.glyphicon-grain { &:before { content: \"\\e239\"; } }\n.glyphicon-sunglasses { &:before { content: \"\\e240\"; } }\n.glyphicon-text-size { &:before { content: \"\\e241\"; } }\n.glyphicon-text-color { &:before { content: \"\\e242\"; } }\n.glyphicon-text-background { &:before { content: \"\\e243\"; } }\n.glyphicon-object-align-top { &:before { content: \"\\e244\"; } }\n.glyphicon-object-align-bottom { &:before { content: \"\\e245\"; } }\n.glyphicon-object-align-horizontal{ &:before { content: \"\\e246\"; } }\n.glyphicon-object-align-left { &:before { content: \"\\e247\"; } }\n.glyphicon-object-align-vertical { &:before { content: \"\\e248\"; } }\n.glyphicon-object-align-right { &:before { content: \"\\e249\"; } }\n.glyphicon-triangle-right { &:before { content: \"\\e250\"; } }\n.glyphicon-triangle-left { &:before { content: \"\\e251\"; } }\n.glyphicon-triangle-bottom { &:before { content: \"\\e252\"; } }\n.glyphicon-triangle-top { &:before { content: \"\\e253\"; } }\n.glyphicon-console { &:before { content: \"\\e254\"; } }\n.glyphicon-superscript { &:before { content: \"\\e255\"; } }\n.glyphicon-subscript { &:before { content: \"\\e256\"; } }\n.glyphicon-menu-left { &:before { content: \"\\e257\"; } }\n.glyphicon-menu-right { &:before { content: \"\\e258\"; } }\n.glyphicon-menu-down { &:before { content: \"\\e259\"; } }\n.glyphicon-menu-up { &:before { content: \"\\e260\"; } }\n","//\n// Scaffolding\n// --------------------------------------------------\n\n\n// Reset the box-sizing\n//\n// Heads up! This reset may cause conflicts with some third-party widgets.\n// For recommendations on resolving such conflicts, see\n// http://getbootstrap.com/getting-started/#third-box-sizing\n* {\n .box-sizing(border-box);\n}\n*:before,\n*:after {\n .box-sizing(border-box);\n}\n\n\n// Body reset\n\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0,0,0,0);\n}\n\nbody {\n font-family: @font-family-base;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @text-color;\n background-color: @body-bg;\n}\n\n// Reset fonts for relevant elements\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\n\n// Links\n\na {\n color: @link-color;\n text-decoration: none;\n\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: @link-hover-decoration;\n }\n\n &:focus {\n .tab-focus();\n }\n}\n\n\n// Figures\n//\n// We reset this here because previously Normalize had no `figure` margins. This\n// ensures we don't break anyone's use of the element.\n\nfigure {\n margin: 0;\n}\n\n\n// Images\n\nimg {\n vertical-align: middle;\n}\n\n// Responsive images (ensure images don't scale beyond their parents)\n.img-responsive {\n .img-responsive();\n}\n\n// Rounded corners\n.img-rounded {\n border-radius: @border-radius-large;\n}\n\n// Image thumbnails\n//\n// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`.\n.img-thumbnail {\n padding: @thumbnail-padding;\n line-height: @line-height-base;\n background-color: @thumbnail-bg;\n border: 1px solid @thumbnail-border;\n border-radius: @thumbnail-border-radius;\n .transition(all .2s ease-in-out);\n\n // Keep them at most 100% wide\n .img-responsive(inline-block);\n}\n\n// Perfect circle\n.img-circle {\n border-radius: 50%; // set radius in percents\n}\n\n\n// Horizontal rules\n\nhr {\n margin-top: @line-height-computed;\n margin-bottom: @line-height-computed;\n border: 0;\n border-top: 1px solid @hr-border;\n}\n\n\n// Only display content to screen readers\n//\n// See: http://a11yproject.com/posts/how-to-hide-content/\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0,0,0,0);\n border: 0;\n}\n\n// Use in conjunction with .sr-only to only display content when it's focused.\n// Useful for \"Skip to main content\" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1\n// Credit: HTML5 Boilerplate\n\n.sr-only-focusable {\n &:active,\n &:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n }\n}\n\n\n// iOS \"clickable elements\" fix for role=\"button\"\n//\n// Fixes \"clickability\" issue (and more generally, the firing of events such as focus as well)\n// for traditionally non-focusable elements with role=\"button\"\n// see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile\n\n[role=\"button\"] {\n cursor: pointer;\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// WebKit-style focus\n\n.tab-focus() {\n // Default\n outline: thin dotted;\n // WebKit\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n.img-responsive(@display: block) {\n display: @display;\n max-width: 100%; // Part 1: Set a maximum relative to the parent\n height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size. Note that the\n// spelling of `min--moz-device-pixel-ratio` is intentional.\n.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {\n background-image: url(\"@{file-1x}\");\n\n @media\n only screen and (-webkit-min-device-pixel-ratio: 2),\n only screen and ( min--moz-device-pixel-ratio: 2),\n only screen and ( -o-min-device-pixel-ratio: 2/1),\n only screen and ( min-device-pixel-ratio: 2),\n only screen and ( min-resolution: 192dpi),\n only screen and ( min-resolution: 2dppx) {\n background-image: url(\"@{file-2x}\");\n background-size: @width-1x @height-1x;\n }\n}\n","//\n// Typography\n// --------------------------------------------------\n\n\n// Headings\n// -------------------------\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n font-family: @headings-font-family;\n font-weight: @headings-font-weight;\n line-height: @headings-line-height;\n color: @headings-color;\n\n small,\n .small {\n font-weight: normal;\n line-height: 1;\n color: @headings-small-color;\n }\n}\n\nh1, .h1,\nh2, .h2,\nh3, .h3 {\n margin-top: @line-height-computed;\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 65%;\n }\n}\nh4, .h4,\nh5, .h5,\nh6, .h6 {\n margin-top: (@line-height-computed / 2);\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 75%;\n }\n}\n\nh1, .h1 { font-size: @font-size-h1; }\nh2, .h2 { font-size: @font-size-h2; }\nh3, .h3 { font-size: @font-size-h3; }\nh4, .h4 { font-size: @font-size-h4; }\nh5, .h5 { font-size: @font-size-h5; }\nh6, .h6 { font-size: @font-size-h6; }\n\n\n// Body text\n// -------------------------\n\np {\n margin: 0 0 (@line-height-computed / 2);\n}\n\n.lead {\n margin-bottom: @line-height-computed;\n font-size: floor((@font-size-base * 1.15));\n font-weight: 300;\n line-height: 1.4;\n\n @media (min-width: @screen-sm-min) {\n font-size: (@font-size-base * 1.5);\n }\n}\n\n\n// Emphasis & misc\n// -------------------------\n\n// Ex: (12px small font / 14px base font) * 100% = about 85%\nsmall,\n.small {\n font-size: floor((100% * @font-size-small / @font-size-base));\n}\n\nmark,\n.mark {\n background-color: @state-warning-bg;\n padding: .2em;\n}\n\n// Alignment\n.text-left { text-align: left; }\n.text-right { text-align: right; }\n.text-center { text-align: center; }\n.text-justify { text-align: justify; }\n.text-nowrap { white-space: nowrap; }\n\n// Transformation\n.text-lowercase { text-transform: lowercase; }\n.text-uppercase { text-transform: uppercase; }\n.text-capitalize { text-transform: capitalize; }\n\n// Contextual colors\n.text-muted {\n color: @text-muted;\n}\n.text-primary {\n .text-emphasis-variant(@brand-primary);\n}\n.text-success {\n .text-emphasis-variant(@state-success-text);\n}\n.text-info {\n .text-emphasis-variant(@state-info-text);\n}\n.text-warning {\n .text-emphasis-variant(@state-warning-text);\n}\n.text-danger {\n .text-emphasis-variant(@state-danger-text);\n}\n\n// Contextual backgrounds\n// For now we'll leave these alongside the text classes until v4 when we can\n// safely shift things around (per SemVer rules).\n.bg-primary {\n // Given the contrast here, this is the only class to have its color inverted\n // automatically.\n color: #fff;\n .bg-variant(@brand-primary);\n}\n.bg-success {\n .bg-variant(@state-success-bg);\n}\n.bg-info {\n .bg-variant(@state-info-bg);\n}\n.bg-warning {\n .bg-variant(@state-warning-bg);\n}\n.bg-danger {\n .bg-variant(@state-danger-bg);\n}\n\n\n// Page header\n// -------------------------\n\n.page-header {\n padding-bottom: ((@line-height-computed / 2) - 1);\n margin: (@line-height-computed * 2) 0 @line-height-computed;\n border-bottom: 1px solid @page-header-border-color;\n}\n\n\n// Lists\n// -------------------------\n\n// Unordered and Ordered lists\nul,\nol {\n margin-top: 0;\n margin-bottom: (@line-height-computed / 2);\n ul,\n ol {\n margin-bottom: 0;\n }\n}\n\n// List options\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n .list-unstyled();\n margin-left: -5px;\n\n > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n }\n}\n\n// Description Lists\ndl {\n margin-top: 0; // Remove browser default\n margin-bottom: @line-height-computed;\n}\ndt,\ndd {\n line-height: @line-height-base;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0; // Undo browser default\n}\n\n// Horizontal description lists\n//\n// Defaults to being stacked without any of the below styles applied, until the\n// grid breakpoint is reached (default of ~768px).\n\n.dl-horizontal {\n dd {\n &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present\n }\n\n @media (min-width: @grid-float-breakpoint) {\n dt {\n float: left;\n width: (@dl-horizontal-offset - 20);\n clear: left;\n text-align: right;\n .text-overflow();\n }\n dd {\n margin-left: @dl-horizontal-offset;\n }\n }\n}\n\n\n// Misc\n// -------------------------\n\n// Abbreviations and acronyms\nabbr[title],\n// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted @abbr-border-color;\n}\n.initialism {\n font-size: 90%;\n .text-uppercase();\n}\n\n// Blockquotes\nblockquote {\n padding: (@line-height-computed / 2) @line-height-computed;\n margin: 0 0 @line-height-computed;\n font-size: @blockquote-font-size;\n border-left: 5px solid @blockquote-border-color;\n\n p,\n ul,\n ol {\n &:last-child {\n margin-bottom: 0;\n }\n }\n\n // Note: Deprecated small and .small as of v3.1.0\n // Context: https://github.com/twbs/bootstrap/issues/11660\n footer,\n small,\n .small {\n display: block;\n font-size: 80%; // back to default font-size\n line-height: @line-height-base;\n color: @blockquote-small-color;\n\n &:before {\n content: '\\2014 \\00A0'; // em dash, nbsp\n }\n }\n}\n\n// Opposite alignment of blockquote\n//\n// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0.\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid @blockquote-border-color;\n border-left: 0;\n text-align: right;\n\n // Account for citation\n footer,\n small,\n .small {\n &:before { content: ''; }\n &:after {\n content: '\\00A0 \\2014'; // nbsp, em dash\n }\n }\n}\n\n// Addresses\naddress {\n margin-bottom: @line-height-computed;\n font-style: normal;\n line-height: @line-height-base;\n}\n","// Typography\n\n.text-emphasis-variant(@color) {\n color: @color;\n a&:hover,\n a&:focus {\n color: darken(@color, 10%);\n }\n}\n","// Contextual backgrounds\n\n.bg-variant(@color) {\n background-color: @color;\n a&:hover,\n a&:focus {\n background-color: darken(@color, 10%);\n }\n}\n","// Text overflow\n// Requires inline-block or block for proper styling\n\n.text-overflow() {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n","//\n// Code (inline and block)\n// --------------------------------------------------\n\n\n// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n font-family: @font-family-monospace;\n}\n\n// Inline code\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: @code-color;\n background-color: @code-bg;\n border-radius: @border-radius-base;\n}\n\n// User input typically entered via keyboard\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: @kbd-color;\n background-color: @kbd-bg;\n border-radius: @border-radius-small;\n box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);\n\n kbd {\n padding: 0;\n font-size: 100%;\n font-weight: bold;\n box-shadow: none;\n }\n}\n\n// Blocks of code\npre {\n display: block;\n padding: ((@line-height-computed - 1) / 2);\n margin: 0 0 (@line-height-computed / 2);\n font-size: (@font-size-base - 1); // 14px to 13px\n line-height: @line-height-base;\n word-break: break-all;\n word-wrap: break-word;\n color: @pre-color;\n background-color: @pre-bg;\n border: 1px solid @pre-border-color;\n border-radius: @border-radius-base;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: @pre-scrollable-max-height;\n overflow-y: scroll;\n}\n","//\n// Grid system\n// --------------------------------------------------\n\n\n// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n.container {\n .container-fixed();\n\n @media (min-width: @screen-sm-min) {\n width: @container-sm;\n }\n @media (min-width: @screen-md-min) {\n width: @container-md;\n }\n @media (min-width: @screen-lg-min) {\n width: @container-lg;\n }\n}\n\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but without any defined\n// width for fluid, full width layouts.\n\n.container-fluid {\n .container-fixed();\n}\n\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n.row {\n .make-row();\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n.make-grid-columns();\n\n\n// Extra small grid\n//\n// Columns, offsets, pushes, and pulls for extra small devices like\n// smartphones.\n\n.make-grid(xs);\n\n\n// Small grid\n//\n// Columns, offsets, pushes, and pulls for the small device range, from phones\n// to tablets.\n\n@media (min-width: @screen-sm-min) {\n .make-grid(sm);\n}\n\n\n// Medium grid\n//\n// Columns, offsets, pushes, and pulls for the desktop device range.\n\n@media (min-width: @screen-md-min) {\n .make-grid(md);\n}\n\n\n// Large grid\n//\n// Columns, offsets, pushes, and pulls for the large desktop device range.\n\n@media (min-width: @screen-lg-min) {\n .make-grid(lg);\n}\n","// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n// Centered container element\n.container-fixed(@gutter: @grid-gutter-width) {\n margin-right: auto;\n margin-left: auto;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n &:extend(.clearfix all);\n}\n\n// Creates a wrapper for a series of columns\n.make-row(@gutter: @grid-gutter-width) {\n margin-left: ceil((@gutter / -2));\n margin-right: floor((@gutter / -2));\n &:extend(.clearfix all);\n}\n\n// Generate the extra small columns\n.make-xs-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n float: left;\n width: percentage((@columns / @grid-columns));\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n}\n.make-xs-column-offset(@columns) {\n margin-left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-push(@columns) {\n left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-pull(@columns) {\n right: percentage((@columns / @grid-columns));\n}\n\n// Generate the small columns\n.make-sm-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-sm-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-offset(@columns) {\n @media (min-width: @screen-sm-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-push(@columns) {\n @media (min-width: @screen-sm-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-pull(@columns) {\n @media (min-width: @screen-sm-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the medium columns\n.make-md-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-md-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-offset(@columns) {\n @media (min-width: @screen-md-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-push(@columns) {\n @media (min-width: @screen-md-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-pull(@columns) {\n @media (min-width: @screen-md-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the large columns\n.make-lg-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-lg-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-offset(@columns) {\n @media (min-width: @screen-lg-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-push(@columns) {\n @media (min-width: @screen-lg-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-pull(@columns) {\n @media (min-width: @screen-lg-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `@grid-columns`.\n\n.make-grid-columns() {\n // Common styles for all sizes of grid columns, widths 1-12\n .col(@index) { // initial\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general; \"=<\" isn't a typo\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n position: relative;\n // Prevent columns from collapsing when empty\n min-height: 1px;\n // Inner gutter via padding\n padding-left: ceil((@grid-gutter-width / 2));\n padding-right: floor((@grid-gutter-width / 2));\n }\n }\n .col(1); // kickstart it\n}\n\n.float-grid-columns(@class) {\n .col(@index) { // initial\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n float: left;\n }\n }\n .col(1); // kickstart it\n}\n\n.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {\n .col-@{class}-@{index} {\n width: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) {\n .col-@{class}-push-@{index} {\n left: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) {\n .col-@{class}-push-0 {\n left: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) {\n .col-@{class}-pull-@{index} {\n right: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) {\n .col-@{class}-pull-0 {\n right: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = offset) {\n .col-@{class}-offset-@{index} {\n margin-left: percentage((@index / @grid-columns));\n }\n}\n\n// Basic looping in LESS\n.loop-grid-columns(@index, @class, @type) when (@index >= 0) {\n .calc-grid-column(@index, @class, @type);\n // next iteration\n .loop-grid-columns((@index - 1), @class, @type);\n}\n\n// Create grid for specific class\n.make-grid(@class) {\n .float-grid-columns(@class);\n .loop-grid-columns(@grid-columns, @class, width);\n .loop-grid-columns(@grid-columns, @class, pull);\n .loop-grid-columns(@grid-columns, @class, push);\n .loop-grid-columns(@grid-columns, @class, offset);\n}\n","//\n// Tables\n// --------------------------------------------------\n\n\ntable {\n background-color: @table-bg;\n}\ncaption {\n padding-top: @table-cell-padding;\n padding-bottom: @table-cell-padding;\n color: @text-muted;\n text-align: left;\n}\nth {\n text-align: left;\n}\n\n\n// Baseline styles\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: @line-height-computed;\n // Cells\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-cell-padding;\n line-height: @line-height-base;\n vertical-align: top;\n border-top: 1px solid @table-border-color;\n }\n }\n }\n // Bottom align for column headings\n > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid @table-border-color;\n }\n // Remove top border from thead by default\n > caption + thead,\n > colgroup + thead,\n > thead:first-child {\n > tr:first-child {\n > th,\n > td {\n border-top: 0;\n }\n }\n }\n // Account for multiple tbody instances\n > tbody + tbody {\n border-top: 2px solid @table-border-color;\n }\n\n // Nesting\n .table {\n background-color: @body-bg;\n }\n}\n\n\n// Condensed table w/ half padding\n\n.table-condensed {\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-condensed-cell-padding;\n }\n }\n }\n}\n\n\n// Bordered version\n//\n// Add borders all around the table and between all the columns.\n\n.table-bordered {\n border: 1px solid @table-border-color;\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n border: 1px solid @table-border-color;\n }\n }\n }\n > thead > tr {\n > th,\n > td {\n border-bottom-width: 2px;\n }\n }\n}\n\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n > tbody > tr:nth-of-type(odd) {\n background-color: @table-bg-accent;\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n > tbody > tr:hover {\n background-color: @table-bg-hover;\n }\n}\n\n\n// Table cell sizing\n//\n// Reset default table behavior\n\ntable col[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-column;\n}\ntable {\n td,\n th {\n &[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-cell;\n }\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n// Generate the contextual variants\n.table-row-variant(active; @table-bg-active);\n.table-row-variant(success; @state-success-bg);\n.table-row-variant(info; @state-info-bg);\n.table-row-variant(warning; @state-warning-bg);\n.table-row-variant(danger; @state-danger-bg);\n\n\n// Responsive tables\n//\n// Wrap your tables in `.table-responsive` and we'll make them mobile friendly\n// by enabling horizontal scrolling. Only applies <768px. Everything above that\n// will display normally.\n\n.table-responsive {\n overflow-x: auto;\n min-height: 0.01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837)\n\n @media screen and (max-width: @screen-xs-max) {\n width: 100%;\n margin-bottom: (@line-height-computed * 0.75);\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid @table-border-color;\n\n // Tighten up spacing\n > .table {\n margin-bottom: 0;\n\n // Ensure the content doesn't wrap\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n white-space: nowrap;\n }\n }\n }\n }\n\n // Special overrides for the bordered tables\n > .table-bordered {\n border: 0;\n\n // Nuke the appropriate borders so that the parent can handle them\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th:first-child,\n > td:first-child {\n border-left: 0;\n }\n > th:last-child,\n > td:last-child {\n border-right: 0;\n }\n }\n }\n\n // Only nuke the last row's bottom-border in `tbody` and `tfoot` since\n // chances are there will be only one `tr` in a `thead` and that would\n // remove the border altogether.\n > tbody,\n > tfoot {\n > tr:last-child {\n > th,\n > td {\n border-bottom: 0;\n }\n }\n }\n\n }\n }\n}\n","// Tables\n\n.table-row-variant(@state; @background) {\n // Exact selectors below required to override `.table-striped` and prevent\n // inheritance to nested tables.\n .table > thead > tr,\n .table > tbody > tr,\n .table > tfoot > tr {\n > td.@{state},\n > th.@{state},\n &.@{state} > td,\n &.@{state} > th {\n background-color: @background;\n }\n }\n\n // Hover states for `.table-hover`\n // Note: this is not available for cells or rows within `thead` or `tfoot`.\n .table-hover > tbody > tr {\n > td.@{state}:hover,\n > th.@{state}:hover,\n &.@{state}:hover > td,\n &:hover > .@{state},\n &.@{state}:hover > th {\n background-color: darken(@background, 5%);\n }\n }\n}\n","//\n// Forms\n// --------------------------------------------------\n\n\n// Normalize non-controls\n//\n// Restyle and baseline non-control form elements.\n\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n // Chrome and Firefox set a `min-width: min-content;` on fieldsets,\n // so we reset that to ensure it behaves more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359.\n min-width: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: @line-height-computed;\n font-size: (@font-size-base * 1.5);\n line-height: inherit;\n color: @legend-color;\n border: 0;\n border-bottom: 1px solid @legend-border-color;\n}\n\nlabel {\n display: inline-block;\n max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141)\n margin-bottom: 5px;\n font-weight: bold;\n}\n\n\n// Normalize form controls\n//\n// While most of our form styles require extra classes, some basic normalization\n// is required to ensure optimum display with or without those classes to better\n// address browser inconsistencies.\n\n// Override content-box in Normalize (* isn't specific enough)\ninput[type=\"search\"] {\n .box-sizing(border-box);\n}\n\n// Position radios and checkboxes better\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9; // IE8-9\n line-height: normal;\n}\n\ninput[type=\"file\"] {\n display: block;\n}\n\n// Make range inputs behave like textual form controls\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\n\n// Make multiple select elements height not fixed\nselect[multiple],\nselect[size] {\n height: auto;\n}\n\n// Focus for file, radio, and checkbox\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n .tab-focus();\n}\n\n// Adjust output element\noutput {\n display: block;\n padding-top: (@padding-base-vertical + 1);\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n}\n\n\n// Common form controls\n//\n// Shared size and type resets for form controls. Apply `.form-control` to any\n// of the following form controls:\n//\n// select\n// textarea\n// input[type=\"text\"]\n// input[type=\"password\"]\n// input[type=\"datetime\"]\n// input[type=\"datetime-local\"]\n// input[type=\"date\"]\n// input[type=\"month\"]\n// input[type=\"time\"]\n// input[type=\"week\"]\n// input[type=\"number\"]\n// input[type=\"email\"]\n// input[type=\"url\"]\n// input[type=\"search\"]\n// input[type=\"tel\"]\n// input[type=\"color\"]\n\n.form-control {\n display: block;\n width: 100%;\n height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border)\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n background-color: @input-bg;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid @input-border;\n border-radius: @input-border-radius; // Note: This has no effect on s in CSS.\n .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));\n .transition(~\"border-color ease-in-out .15s, box-shadow ease-in-out .15s\");\n\n // Customize the `:focus` state to imitate native WebKit styles.\n .form-control-focus();\n\n // Placeholder\n .placeholder();\n\n // Disabled and read-only inputs\n //\n // HTML5 says that controls under a fieldset > legend:first-child won't be\n // disabled if the fieldset is disabled. Due to implementation difficulty, we\n // don't honor that edge case; we style them as disabled anyway.\n &[disabled],\n &[readonly],\n fieldset[disabled] & {\n background-color: @input-bg-disabled;\n opacity: 1; // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655\n }\n\n &[disabled],\n fieldset[disabled] & {\n cursor: @cursor-disabled;\n }\n\n // Reset height for `textarea`s\n textarea& {\n height: auto;\n }\n}\n\n\n// Search inputs in iOS\n//\n// This overrides the extra rounded corners on search inputs in iOS so that our\n// `.form-control` class can properly style them. Note that this cannot simply\n// be added to `.form-control` as it's not specific enough. For details, see\n// https://github.com/twbs/bootstrap/issues/11586.\n\ninput[type=\"search\"] {\n -webkit-appearance: none;\n}\n\n\n// Special styles for iOS temporal inputs\n//\n// In Mobile Safari, setting `display: block` on temporal inputs causes the\n// text within the input to become vertically misaligned. As a workaround, we\n// set a pixel line-height that matches the given height of the input, but only\n// for Safari. See https://bugs.webkit.org/show_bug.cgi?id=139848\n//\n// Note that as of 8.3, iOS doesn't support `datetime` or `week`.\n\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n input[type=\"date\"],\n input[type=\"time\"],\n input[type=\"datetime-local\"],\n input[type=\"month\"] {\n &.form-control {\n line-height: @input-height-base;\n }\n\n &.input-sm,\n .input-group-sm & {\n line-height: @input-height-small;\n }\n\n &.input-lg,\n .input-group-lg & {\n line-height: @input-height-large;\n }\n }\n}\n\n\n// Form groups\n//\n// Designed to help with the organization and spacing of vertical forms. For\n// horizontal forms, use the predefined grid classes.\n\n.form-group {\n margin-bottom: @form-group-margin-bottom;\n}\n\n\n// Checkboxes and radios\n//\n// Indent the labels to position radios/checkboxes as hanging controls.\n\n.radio,\n.checkbox {\n position: relative;\n display: block;\n margin-top: 10px;\n margin-bottom: 10px;\n\n label {\n min-height: @line-height-computed; // Ensure the input doesn't jump when there is no text\n padding-left: 20px;\n margin-bottom: 0;\n font-weight: normal;\n cursor: pointer;\n }\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n position: absolute;\n margin-left: -20px;\n margin-top: 4px \\9;\n}\n\n.radio + .radio,\n.checkbox + .checkbox {\n margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing\n}\n\n// Radios and checkboxes on same line\n.radio-inline,\n.checkbox-inline {\n position: relative;\n display: inline-block;\n padding-left: 20px;\n margin-bottom: 0;\n vertical-align: middle;\n font-weight: normal;\n cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n margin-top: 0;\n margin-left: 10px; // space out consecutive inline controls\n}\n\n// Apply same disabled cursor tweak as for inputs\n// Some special care is needed because